diff --git a/.githooks/pre-commit b/.githooks/pre-commit deleted file mode 100755 index a902e9de6..000000000 --- a/.githooks/pre-commit +++ /dev/null @@ -1,26 +0,0 @@ -#!/bin/sh -set -eu - -version_num="30300" -version_base="3.3.0-dev" -version_file="include/gmssl/version.h" -cmake_file="CMakeLists.txt" - -count=$(git rev-list --count HEAD) -next=$((count + 1)) -version="${version_base}.${next}" - -tmp="${version_file}.tmp" -sed \ - -e "s/^#define GMSSL_VERSION_NUM.*/#define GMSSL_VERSION_NUM ${version_num}/" \ - -e "s/^#define GMSSL_VERSION_STR.*/#define GMSSL_VERSION_STR \"GmSSL ${version}\"/" \ - "${version_file}" > "${tmp}" -mv "${tmp}" "${version_file}" - -tmp="${cmake_file}.tmp" -sed \ - -e "s/^set(CPACK_PACKAGE_VERSION .*/set(CPACK_PACKAGE_VERSION \"${version}\")/" \ - "${cmake_file}" > "${tmp}" -mv "${tmp}" "${cmake_file}" - -git add "${version_file}" "${cmake_file}" diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 000000000..b2209d2ee --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,21 @@ +--- +name: Bug report +about: Create a report to help us improve + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Environments** +- OS: [e.g. ubuntu16.04LTS x64] +- Compiler: [e.g. gcc (Ubuntu 5.4.0-6ubuntu1~16.04.11) 5.4.0 20160609] + +**Additional context** +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/custom.md b/.github/ISSUE_TEMPLATE/custom.md new file mode 100644 index 000000000..99bb9a009 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/custom.md @@ -0,0 +1,7 @@ +--- +name: Custom issue template +about: Describe this issue template's purpose here. + +--- + + diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 000000000..066b2d920 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,17 @@ +--- +name: Feature request +about: Suggest an idea for this project + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. diff --git a/.github/workflows/android-ci.yml b/.github/workflows/android-ci.yml deleted file mode 100644 index 1f69f5b04..000000000 --- a/.github/workflows/android-ci.yml +++ /dev/null @@ -1,48 +0,0 @@ -name: CMake-Android - -on: - push: - branches: [ master ] - pull_request: - branches: [ master ] - -permissions: - contents: read - -env: - # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) - BUILD_TYPE: Release - -jobs: - build: - # The CMake configure and build commands are platform agnostic and should work equally well on Windows or Mac. - # You can convert this to a matrix build if you need cross-platform coverage. - # See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix - strategy: - matrix: - os: [ ubuntu-latest ] - runs-on: ${{ matrix.os }} - - steps: - - uses: actions/checkout@v3 - - - uses: nttld/setup-ndk@v1 - id: setup-ndk - with: - ndk-version: r25 - add-to-path: false - - run: echo ${{env.ANDROID_NDK_HOME}} - env: - ANDROID_NDK_HOME: ${{ steps.setup-ndk.outputs.ndk-path }} - - - # - name: Configure CMake - # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. - # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type - # run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} - - - name: Build - # Build your program with the given configuration - run: mkdir ${{github.workspace}}/build; cd ${{github.workspace}}/build; cmake .. -DCMAKE_TOOLCHAIN_FILE=${{env.ANDROID_NDK_HOME}}/build/cmake/android.toolchain.cmake -DANDROID_ABI=arm64-v8a -DANDROID_PLATFORM=android-23;make - env: - ANDROID_NDK_HOME: ${{ steps.setup-ndk.outputs.ndk-path }} diff --git a/.github/workflows/cmake-windows.yml b/.github/workflows/cmake-windows.yml deleted file mode 100644 index cfedb97b7..000000000 --- a/.github/workflows/cmake-windows.yml +++ /dev/null @@ -1,43 +0,0 @@ -name: CMake-windows - -on: - push: - branches: [ master ] - pull_request: - branches: [ master ] - -permissions: - contents: read - -env: - # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) - BUILD_TYPE: Release - -jobs: - build: - - runs-on: windows-latest - - steps: - - uses: actions/checkout@v3 - - - name: Configure build for x86 - uses: ilammy/msvc-dev-cmd@v1 - with: - arch: amd64_x86 - - - name: Configure CMake - # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. - # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type - run: cmake -B ${{github.workspace}}\build -G "NMake Makefiles" -DWIN32=ON; - - - name: Build - working-directory: ${{github.workspace}}\build - # Build your program with the given configuration - run: nmake - - - name: Test - working-directory: ${{github.workspace}}\build - # Execute tests defined by the CMake configuration. - # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail - run: ctest --rerun-failed --output-on-failure -C ${{env.BUILD_TYPE}} diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml deleted file mode 100644 index 1c155f8df..000000000 --- a/.github/workflows/cmake.yml +++ /dev/null @@ -1,43 +0,0 @@ -name: CMake - -on: - push: - branches: [ master ] - pull_request: - branches: [ master ] - -permissions: - contents: read - -env: - # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) - BUILD_TYPE: Release - -jobs: - build: - # The CMake configure and build commands are platform agnostic and should work equally well on Windows or Mac. - # You can convert this to a matrix build if you need cross-platform coverage. - # See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix - strategy: - matrix: - os: [ ubuntu-latest, macos-latest ] - runs-on: ${{ matrix.os }} - - steps: - - uses: actions/checkout@v3 - - - name: Configure CMake - # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. - # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type - run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} - - - name: Build - # Build your program with the given configuration - run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} - - - name: Test - working-directory: ${{github.workspace}}/build - # Execute tests defined by the CMake configuration. - # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail - run: ctest --rerun-failed --output-on-failure -C ${{env.BUILD_TYPE}} - diff --git a/.github/workflows/ios.yml b/.github/workflows/ios.yml deleted file mode 100644 index 9fcf8fc0b..000000000 --- a/.github/workflows/ios.yml +++ /dev/null @@ -1,42 +0,0 @@ -name: CMake-iOS - -on: - push: - branches: [ "master" ] - pull_request: - branches: [ "master" ] - -permissions: - contents: read - -env: - # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) - BUILD_TYPE: Release - -jobs: - build: - - runs-on: macos-latest - - steps: - - uses: actions/checkout@v3 - - name: Get ios-cmake - uses: sudosubin/git-clone-action@v1.0.1 - with: - # Repository owner and name. Ex: sudosubin/git-clone-action - repository: leetal/ios-cmake - # Git host platform. Ex: github, gitlab, bitbucket, gitee, or else (git.suckless.org, ...) - platform: github - # Relative path from current directory, where to clone. - path: ios-cmake # optional - - - - name: Configure CMake - # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. - # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type - run: cmake -B ${{github.workspace}}/build -G Xcode -DCMAKE_TOOLCHAIN_FILE=ios-cmake/ios.toolchain.cmake -DPLATFORM=OS64 -DBUILD_SHARED_LIBS=OFF - - - name: Build - # Build your program with the given configuration - run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} - diff --git a/.github/workflows/msvc.yml b/.github/workflows/msvc.yml deleted file mode 100644 index 06498d471..000000000 --- a/.github/workflows/msvc.yml +++ /dev/null @@ -1,33 +0,0 @@ -name: MSVC CMake - -on: - push: - branches: [ "master", "v2.5" ] - pull_request: - branches: [ "master" ] - schedule: - - cron: '41 2 * * 4' - -env: - # Path to the CMake build directory. - build: '${{ github.workspace }}/build' - -permissions: - contents: read - -jobs: - build: - permissions: - contents: read - name: Build - runs-on: windows-latest - - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Configure CMake - run: cmake -B ${{ env.build }} - - - name: Build CMake - run: cmake --build ${{ env.build }} diff --git a/.github/workflows/ohos.yml b/.github/workflows/ohos.yml deleted file mode 100644 index 4f2b5ba25..000000000 --- a/.github/workflows/ohos.yml +++ /dev/null @@ -1,44 +0,0 @@ -name: CMake-OpenHarmony - -on: - push: - branches: [master] - pull_request: - branches: [master] - -permissions: - contents: read - -env: - # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) - BUILD_TYPE: Release - -jobs: - build: - # The CMake configure and build commands are platform agnostic and should work equally well on Windows or Mac. - # You can convert this to a matrix build if you need cross-platform coverage. - # See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix - strategy: - matrix: - os: [ubuntu-latest] - target: [arm64-v8a, armeabi-v7a, x86_64] - runs-on: ${{ matrix.os }} - - steps: - - uses: actions/checkout@v3 - - - name: Setup OpenHarmony SDK - uses: openharmony-rs/setup-ohos-sdk@v0.2.4 - id: setup-ohos - with: - version: "5.0.0" - components: "native" - cache: "true" - - - name: Build - # Build your program with the given configuration - run: | - mkdir ${{github.workspace}}/build - cd ${{github.workspace}}/build - cmake .. -DCMAKE_TOOLCHAIN_FILE=${{steps.setup-ohos.outputs.ohos_sdk_native}}/build/cmake/ohos.toolchain.cmake -DOHOS_ARCH=${{matrix.target}} - make diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml deleted file mode 100644 index 1853162b1..000000000 --- a/.github/workflows/release.yml +++ /dev/null @@ -1,239 +0,0 @@ -name: Release packages - -on: - workflow_dispatch: - inputs: - tag: - description: Release tag, for example v3.2.0 - required: true - type: string - -permissions: - contents: write - -env: - BUILD_TYPE: Release - -jobs: - linux: - runs-on: ubuntu-22.04 - - steps: - - uses: actions/checkout@v3 - with: - ref: ${{ inputs.tag }} - - - name: Build, test, and package - env: - TAG: ${{ inputs.tag }} - run: | - set -eux - VERSION="${TAG#v}" - PACKAGE="GmSSL-${VERSION}-Linux-x86_64" - PREFIX="${PWD}/${PACKAGE}" - STATIC_PREFIX="${PWD}/${PACKAGE}-static" - - rm -rf build-shared build-static "${PREFIX}" "${STATIC_PREFIX}" "${PACKAGE}.tar.gz" - - cmake -S . -B build-shared \ - -DCMAKE_BUILD_TYPE="${BUILD_TYPE}" \ - -DBUILD_SHARED_LIBS=ON \ - -DCMAKE_INSTALL_PREFIX="${PREFIX}" - cmake --build build-shared --config "${BUILD_TYPE}" - cmake --install build-shared --prefix "${PREFIX}" - "${PREFIX}/bin/gmssl" version | grep "GmSSL ${VERSION}" - - cmake -S . -B build-static \ - -DCMAKE_BUILD_TYPE="${BUILD_TYPE}" \ - -DBUILD_SHARED_LIBS=OFF \ - -DCMAKE_INSTALL_PREFIX="${STATIC_PREFIX}" - cmake --build build-static --config "${BUILD_TYPE}" - cmake --install build-static --prefix "${STATIC_PREFIX}" - cp "${STATIC_PREFIX}/bin/gmssl" "${PREFIX}/bin/gmssl" - cp "${STATIC_PREFIX}/lib/libgmssl.a" "${PREFIX}/lib/libgmssl.a" - if ldd "${PREFIX}/bin/gmssl" | grep -q libgmssl; then - echo "gmssl must not depend on libgmssl.so" >&2 - exit 1 - fi - - cat > smoke.c <<'EOF' - #include - #include - - int main(void) - { - puts(gmssl_version_str()); - return 0; - } - EOF - cc smoke.c -I"${PREFIX}/include" "${PREFIX}/lib/libgmssl.a" -o smoke-static - ./smoke-static | grep "GmSSL ${VERSION}" - - tar czf "${PACKAGE}.tar.gz" "${PACKAGE}" - - - name: Upload Linux package - env: - GH_TOKEN: ${{ github.token }} - TAG: ${{ inputs.tag }} - run: | - VERSION="${TAG#v}" - gh release upload "${TAG}" "GmSSL-${VERSION}-Linux-x86_64.tar.gz" --clobber - - macos: - strategy: - matrix: - include: - - arch: arm64 - runner: macos-15 - - arch: x86_64 - runner: macos-15-intel - runs-on: ${{ matrix.runner }} - - steps: - - uses: actions/checkout@v3 - with: - ref: ${{ inputs.tag }} - - - name: Build, test, and package - env: - TAG: ${{ inputs.tag }} - ARCH: ${{ matrix.arch }} - run: | - set -eux - VERSION="${TAG#v}" - PACKAGE="GmSSL-${VERSION}-macOS-${ARCH}" - PREFIX="${PWD}/${PACKAGE}" - STATIC_PREFIX="${PWD}/${PACKAGE}-static" - - rm -rf build-shared build-static "${PREFIX}" "${STATIC_PREFIX}" "${PACKAGE}.tar.gz" - - cmake -S . -B build-shared \ - -DCMAKE_BUILD_TYPE="${BUILD_TYPE}" \ - -DCMAKE_OSX_ARCHITECTURES="${ARCH}" \ - -DBUILD_SHARED_LIBS=ON \ - -DCMAKE_INSTALL_PREFIX="${PREFIX}" - cmake --build build-shared --config "${BUILD_TYPE}" - cmake --install build-shared --prefix "${PREFIX}" - "${PREFIX}/bin/gmssl" version | grep "GmSSL ${VERSION}" - - cmake -S . -B build-static \ - -DCMAKE_BUILD_TYPE="${BUILD_TYPE}" \ - -DCMAKE_OSX_ARCHITECTURES="${ARCH}" \ - -DBUILD_SHARED_LIBS=OFF \ - -DCMAKE_INSTALL_PREFIX="${STATIC_PREFIX}" - cmake --build build-static --config "${BUILD_TYPE}" - cmake --install build-static --prefix "${STATIC_PREFIX}" - cp "${STATIC_PREFIX}/bin/gmssl" "${PREFIX}/bin/gmssl" - cp "${STATIC_PREFIX}/lib/libgmssl.a" "${PREFIX}/lib/libgmssl.a" - - file "${PREFIX}/bin/gmssl" | grep "${ARCH}" - if otool -L "${PREFIX}/bin/gmssl" | grep -q libgmssl; then - echo "gmssl must not depend on libgmssl.dylib" >&2 - exit 1 - fi - "${PREFIX}/bin/gmssl" version | grep "GmSSL ${VERSION}" - - cat > smoke.c <<'EOF' - #include - #include - - int main(void) - { - puts(gmssl_version_str()); - return 0; - } - EOF - cc smoke.c -I"${PREFIX}/include" "${PREFIX}/lib/libgmssl.a" -o smoke-static - ./smoke-static | grep "GmSSL ${VERSION}" - - tar czf "${PACKAGE}.tar.gz" "${PACKAGE}" - - - name: Upload macOS package - env: - GH_TOKEN: ${{ github.token }} - TAG: ${{ inputs.tag }} - ARCH: ${{ matrix.arch }} - run: | - VERSION="${TAG#v}" - gh release upload "${TAG}" "GmSSL-${VERSION}-macOS-${ARCH}.tar.gz" --clobber - - windows: - runs-on: windows-latest - - steps: - - uses: actions/checkout@v3 - with: - ref: ${{ inputs.tag }} - - - name: Configure Visual Studio environment - uses: ilammy/msvc-dev-cmd@v1 - with: - arch: x64 - - - name: Build, test, and package - shell: pwsh - env: - TAG: ${{ inputs.tag }} - run: | - $ErrorActionPreference = "Stop" - $Version = $env:TAG.TrimStart("v") - $Package = "GmSSL-$Version-win64" - $Prefix = Join-Path $PWD $Package - $StaticPrefix = Join-Path $PWD "$Package-static" - - Remove-Item -Recurse -Force build-shared, build-static, $Prefix, $StaticPrefix, "$Package.zip" -ErrorAction SilentlyContinue - - cmake -S . -B build-shared -G "NMake Makefiles" ` - -DCMAKE_BUILD_TYPE="$env:BUILD_TYPE" ` - -DBUILD_SHARED_LIBS=ON ` - -DCMAKE_INSTALL_PREFIX="$Prefix" - cmake --build build-shared --config "$env:BUILD_TYPE" - cmake --install build-shared --prefix "$Prefix" - - $SharedImportLib = Join-Path $Prefix "lib\gmssl.lib" - if (Test-Path $SharedImportLib) { - Copy-Item $SharedImportLib (Join-Path $Prefix "lib\gmssl_shared.lib") -Force - } - - cmake -S . -B build-static -G "NMake Makefiles" ` - -DCMAKE_BUILD_TYPE="$env:BUILD_TYPE" ` - -DBUILD_SHARED_LIBS=OFF ` - -DCMAKE_INSTALL_PREFIX="$StaticPrefix" - cmake --build build-static --config "$env:BUILD_TYPE" - cmake --install build-static --prefix "$StaticPrefix" - - Copy-Item (Join-Path $StaticPrefix "bin\gmssl.exe") (Join-Path $Prefix "bin\gmssl.exe") -Force - $StaticLib = Join-Path $StaticPrefix "lib\gmssl.lib" - if (Test-Path $StaticLib) { - Copy-Item $StaticLib (Join-Path $Prefix "lib\gmssl_static.lib") -Force - } - - & (Join-Path $Prefix "bin\gmssl.exe") version | Select-String "GmSSL $Version" - $Dependents = & dumpbin /dependents (Join-Path $Prefix "bin\gmssl.exe") - $Dependents | Out-String | Write-Host - if ($Dependents -match "gmssl.dll") { - throw "gmssl.exe must not depend on gmssl.dll" - } - @" - #include - #include - - int main(void) - { - puts(gmssl_version_str()); - return 0; - } - "@ | Set-Content -Encoding ASCII smoke.c - cl /nologo smoke.c /I "$Prefix\include" "$Prefix\lib\gmssl_static.lib" /Fe:smoke-static.exe - .\smoke-static.exe | Select-String "GmSSL $Version" - - Compress-Archive -Path $Package -DestinationPath "$Package.zip" -Force - - - name: Upload Windows package - shell: pwsh - env: - GH_TOKEN: ${{ github.token }} - TAG: ${{ inputs.tag }} - run: | - $Version = $env:TAG.TrimStart("v") - gh release upload $env:TAG "GmSSL-$Version-win64.zip" --clobber diff --git a/.gitignore b/.gitignore index 2d180346e..d582b4163 100644 --- a/.gitignore +++ b/.gitignore @@ -1,20 +1,163 @@ +# Ignore editor artefacts +/.dir-locals.el -# generated file -/build* -/demos/*.pem -/demos/.private -/demos/scripts/*.pem -/demos/scripts/*.der -/demos/scripts/*.txt -/demos/scripts/*.bin -/demos/scripts/*.sig +# Top level excludes +/Makefile.orig +/MINFO +/TABLE +/*.a +/*.pc +/rehash.time +/inc.* +/makefile.* +/out.* +/tmp.* +/configdata.pm +/build +/Makefile -/examples/* +# *all* Makefiles +#Makefile +*.tmp + +# Java +/java/*.class +/java/Makefile* + +# Links under apps +/apps/CA.pl +/apps/tsget +/apps/tsget.pl +/apps/md4.c + +# Auto generated headers +/crypto/buildinf.h +/crypto/include/internal/*_conf.h +/openssl/include/opensslconf.h +/util/domd + +# Executables +/apps/openssl +/test/sha256t +/test/sha512t +/test/gost2814789t +/test/ssltest_old +/test/*test +/test/fips_aesavs +/test/fips_desmovs +/test/fips_dhvs +/test/fips_drbgvs +/test/fips_dssvs +/test/fips_ecdhvs +/test/fips_ecdsavs +/test/fips_rngvs +/test/fips_test_suite +/test/ssltest_old +/test/x509aux +/test/v3ext + +# Certain files that get created by tests on the fly +/test/*.ss +/test/*.srl +/test/.rnd +/test/test*.pem +/test/newkey.pem +/test/*.log +/test/buildtest_* +/test/*.p +/test/*.dd +/test/*.dp +/test/*.pd +/test/*.p + +/util/shlib_wrap.sh + +# Fuzz stuff. +# Anything without an extension is an executable on Unix, so we keep files +# with extensions. And we keep the corpora subddir versioned as well. +# Anything more generic with extensions that should be ignored will be taken +# care of by general ignores for those extensions (*.o, *.obj, *.exe, ...) +/fuzz/* +!/fuzz/README* +!/fuzz/corpora +!/fuzz/*.* + +# Misc auto generated files +/include/openssl/opensslconf.h +/tools/c_rehash +/tools/c_rehash.pl +/tags +/TAGS +/crypto.map +/ssl.map + +# Windows (legacy) +/tmp32 +/tmp32.dbg +/tmp32dll +/tmp32dll.dbg +/out32 +/out32.dbg +/out32dll +/out32dll.dbg +/inc32 +/MINFO +/ms/.rnd +/ms/bcb.mak +/ms/libeay32.def +/ms/nt.mak +/ms/ntdll.mak +/ms/ssleay32.def +/ms/version32.rc + +# Files created on other branches that are not held in git, and are not +# needed on this branch +/include/openssl/asn1_mac.h +/include/openssl/des_old.h +/include/openssl/fips.h +/include/openssl/fips_rand.h +/include/openssl/krb5_asn.h +/include/openssl/kssl.h +/include/openssl/pq_compat.h +/include/openssl/ssl23.h +/include/openssl/tmdiff.h +/include/openssl/ui_compat.h +/test/fips_aesavs.c +/test/fips_desmovs.c +/test/fips_dsatest.c +/test/fips_dssvs.c +/test/fips_hmactest.c +/test/fips_randtest.c +/test/fips_rngvs.c +/test/fips_rsagtest.c +/test/fips_rsastest.c +/test/fips_rsavtest.c +/test/fips_shatest.c +/test/fips_test_suite.c +/test/shatest.c + +##### Generic patterns +# Auto generated assembly language source files +*.s +!/crypto/*/asm/*.s +/crypto/arm*.S +/crypto/*/*.S +*.asm +!/crypto/*/asm/*.asm # Object files *.o *.obj +# editor artefacts +*.swp +.#* +\#*# +*~ + +# Certificate symbolic links +*.0 + # All kinds of executables *.so *.so.* @@ -32,21 +175,46 @@ *.rc *.res +# Misc generated stuff +Makefile.save +/crypto/**/lib +/engines/**/lib +/ssl/**/lib +*.bak +cscope.* +*.d + # macOS .DS_Store *.tar.gz -# editor artefacts -.vscode/* -*.swp -.#* -\#*# -*~ +# add by LiTianjue for GmSSL +# auto create by Configure +crypto/opensslconf.h +tool/c_rehash +# exec file +apps/gmssl +apps/gmca/.ca + +# gmtls +/ssl/ssl_load.c + +# demos +/demos +/demos/kdf +/demos/ssl +/demos/otp +/demos/sm9 + +# engines +/engines/e_skf* +/engines/e_sdf* +/engines/sdf +/engines/skf -/docs/* +include/openssl/srp.h -/.vs/* -/out/* +/*.sh -*.plist -TODO.md +/rust +/python diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 000000000..2d4aa9068 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,48 @@ +dist: xenial +sudo: required + +language: c +cache: ccache +git: + submodules: false + +before_install: + - if [ -n "$COVERALLS" ]; then + pip install --user cpp-coveralls; + fi; + - if expr "$CONFIG_OPTS" ":" ".*enable-external-tests" > /dev/null; then + git submodule update --init --recursive; + fi; + +addons: + apt: + packages: + - ccache + - golang-1.6 + +os: + - linux + +compiler: + - clang + - gcc + +env: + - CONFIG_OPTS=" --prefix=/home/travis/dist/" DESTDIR="_install" + +before_script: + - env + - pwd + - ls -l + - ./config -v $CONFIG_OPTS; + +script: + - if [ -z "$BUILDONLY" ]; then + make="make -s"; + else + make="make"; + fi + - mkdir ~/dist + - $make update + - $make + - $make install diff --git a/CMakeLists.txt b/CMakeLists.txt deleted file mode 100644 index 2224f7066..000000000 --- a/CMakeLists.txt +++ /dev/null @@ -1,1017 +0,0 @@ -if (CMAKE_VERSION VERSION_LESS "3.0") - cmake_minimum_required(VERSION 2.8) -else() - cmake_minimum_required(VERSION 3.10) -endif() -project(GmSSL C) - -SET(CMAKE_PROJECT_HOMEPAGE_URL "http://gmssl.org") - -set(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin) -set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin) -SET(CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH};${CMAKE_CURRENT_SOURCE_DIR}/cmake") - -include_directories(include) - -if(MSVC) - #add_compile_options(/O2) # conflict with CI /0d (not optimized) -else() - add_compile_options(-O3) -endif() - -set(GMSSL_TARGET_PROCESSOR "${CMAKE_SYSTEM_PROCESSOR}") -if (APPLE AND CMAKE_OSX_ARCHITECTURES) - list(LENGTH CMAKE_OSX_ARCHITECTURES GMSSL_OSX_ARCH_COUNT) - if (GMSSL_OSX_ARCH_COUNT EQUAL 1) - list(GET CMAKE_OSX_ARCHITECTURES 0 GMSSL_TARGET_PROCESSOR) - else() - set(GMSSL_TARGET_PROCESSOR "") - endif() -endif() -string(TOLOWER "${GMSSL_TARGET_PROCESSOR}" GMSSL_TARGET_PROCESSOR) - -set(GMSSL_DEFAULT_ENABLE_SM2_ARM64 OFF) -set(GMSSL_DEFAULT_ENABLE_SM3_ARM64 OFF) -set(GMSSL_DEFAULT_ENABLE_SM4_ARM64 OFF) -set(GMSSL_DEFAULT_ENABLE_SM4_CE OFF) -set(GMSSL_DEFAULT_ENABLE_SM2_NEON OFF) -set(GMSSL_DEFAULT_ENABLE_SM4_AVX2 OFF) -set(GMSSL_DEFAULT_ENABLE_SM4_AESNI OFF) -set(GMSSL_DEFAULT_ENABLE_SM2_AMD64 OFF) -set(GMSSL_DEFAULT_ENABLE_SM3_SSE OFF) -set(GMSSL_DEFAULT_ENABLE_LMS_CL OFF) -set(GMSSL_DEFAULT_ENABLE_XMSS_CL OFF) - -if (NOT MSVC) - if (GMSSL_TARGET_PROCESSOR MATCHES "^(aarch64|arm64)$") - set(GMSSL_DEFAULT_ENABLE_SM2_ARM64 ON) - set(GMSSL_DEFAULT_ENABLE_SM3_ARM64 ON) - set(GMSSL_DEFAULT_ENABLE_SM4_ARM64 ON) - elseif (GMSSL_TARGET_PROCESSOR MATCHES "^(x86_64|amd64|x64)$") - set(GMSSL_DEFAULT_ENABLE_SM2_AMD64 ON) - if (CMAKE_C_COMPILER_ID MATCHES "GNU|Clang|AppleClang" AND NOT CMAKE_CROSSCOMPILING) - include(CheckCSourceCompiles) - set(GMSSL_CMAKE_REQUIRED_FLAGS_SAVE "${CMAKE_REQUIRED_FLAGS}") - set(CMAKE_REQUIRED_FLAGS "-march=native") - check_c_source_compiles( - "#include - int main(void) { __m128i x = _mm_setzero_si128(); x = _mm_shuffle_epi8(x, x); return _mm_cvtsi128_si32(x); }" - GMSSL_HAVE_NATIVE_SSSE3) - check_c_source_compiles( - "#include - int main(void) { __m128i x = _mm_setzero_si128(); x = _mm_aesenclast_si128(x, x); x = _mm_shuffle_epi8(x, x); return _mm_cvtsi128_si32(x); }" - GMSSL_HAVE_NATIVE_AESNI) - check_c_source_compiles( - "#include - int main(void) { __m256i x = _mm256_setzero_si256(); x = _mm256_shuffle_epi8(x, x); return _mm256_extract_epi32(x, 0); }" - GMSSL_HAVE_NATIVE_AVX2) - set(CMAKE_REQUIRED_FLAGS "${GMSSL_CMAKE_REQUIRED_FLAGS_SAVE}") - if (GMSSL_HAVE_NATIVE_SSSE3) - set(GMSSL_DEFAULT_ENABLE_SM3_SSE ON) - endif() - if (DEFINED ENABLE_SM4_AVX2 AND ENABLE_SM4_AVX2) - set(GMSSL_DEFAULT_ENABLE_SM4_AESNI OFF) - elseif (DEFINED ENABLE_SM4_AESNI AND ENABLE_SM4_AESNI) - set(GMSSL_DEFAULT_ENABLE_SM4_AVX2 OFF) - elseif (GMSSL_HAVE_NATIVE_AVX2) - set(GMSSL_DEFAULT_ENABLE_SM4_AVX2 ON) - elseif (GMSSL_HAVE_NATIVE_AESNI) - set(GMSSL_DEFAULT_ENABLE_SM4_AESNI ON) - endif() - endif() - elseif (GMSSL_TARGET_PROCESSOR MATCHES "^arm") - include(CheckCSourceCompiles) - check_c_source_compiles( - "#include - int main(void) { uint64x2_t x = vmovq_n_u64(0); return (int)vgetq_lane_u64(x, 0); }" - GMSSL_HAVE_ARM_NEON) - if (GMSSL_HAVE_ARM_NEON) - set(GMSSL_DEFAULT_ENABLE_SM2_NEON ON) - endif() - endif() -endif() - -option(ENABLE_TEST_SPEED "Enable test speed" OFF) -option(ENABLE_SLOW_TEST "Enable slow tests" OFF) - - - -option(ENABLE_SM2_ARM64 "Enable SM2_Z256 ARMv8 assembly" ${GMSSL_DEFAULT_ENABLE_SM2_ARM64}) -option(ENABLE_SM3_ARM64 "Enable SM3 Arm Neon implementation (10% faster on Apple M2)" ${GMSSL_DEFAULT_ENABLE_SM3_ARM64}) -option(ENABLE_SM4_ARM64 "Enable SM4 AARCH64 assembly implementation" ${GMSSL_DEFAULT_ENABLE_SM4_ARM64}) -option(ENABLE_SM4_CE "Enable SM4 ARM CE assembly implementation" ${GMSSL_DEFAULT_ENABLE_SM4_CE}) -option(ENABLE_SM2_NEON "Enable SM2_Z256 Arm Neon implementation" ${GMSSL_DEFAULT_ENABLE_SM2_NEON}) -option(ENABLE_SM9_ARM64 "Enable SM9_Z256 ARMv8 assembly" OFF) -option(ENABLE_GMUL_ARM64 "Enable GF(2^128) Multiplication AArch64 assembly" OFF) - - -option(ENABLE_SM4_AVX2 "Enable SM4 AVX2 8x implementation" ${GMSSL_DEFAULT_ENABLE_SM4_AVX2}) -option(ENABLE_SM4_AESNI "Enable SM4 AES-NI (4x) implementation" ${GMSSL_DEFAULT_ENABLE_SM4_AESNI}) -option(ENABLE_SM2_AMD64 "Enable SM2_Z256 X86_64 assembly" ${GMSSL_DEFAULT_ENABLE_SM2_AMD64}) - - -option(ENABLE_SM3_SSE "Enable SM3 SSE assembly implementation" ${GMSSL_DEFAULT_ENABLE_SM3_SSE}) - -option(ENABLE_SM4_CTR_AESNI_AVX "Enable SM4 CTR AESNI+AVX assembly implementation" OFF) -option(ENABLE_SM4_CL "Enable SM4 OpenCL" OFF) -option(ENABLE_LMS_CL "Enable LMS OpenCL" ${GMSSL_DEFAULT_ENABLE_LMS_CL}) -option(ENABLE_XMSS_CL "Enable XMSS OpenCL" ${GMSSL_DEFAULT_ENABLE_XMSS_CL}) - - -option(ENABLE_INTEL_RDRAND "Enable Intel RDRAND instructions" OFF) -option(ENABLE_INTEL_RDSEED "Enable Intel RDSEED instructions" OFF) - -option(ENABLE_SM4_ECB "Enable SM4 ECB mode" ON) -option(ENABLE_SM4_OFB "Enable SM4 OFB mode" ON) -option(ENABLE_SM4_CFB "Enable SM4 CFB mode" ON) -option(ENABLE_SM4_CCM "Enable SM4 CCM mode" ON) -option(ENABLE_SM4_XTS "Enable SM4 XTS mode" ON) -option(ENABLE_SM4_CBC_MAC "Enable SM4-CBC-MAC" ON) -option(ENABLE_SM4_FF1 "Enable SM4 FF1 format-preserving encryption" ON) - -option(ENABLE_SM9 "Enable SM9" ON) -option(ENABLE_CMS "Enable CMS" ON) - -option(ENABLE_SECP256R1 "Enable ECDH/ECDSA on curve secp256r1" ON) -option(ENABLE_SECP384R1 "Enable curve secp384r1" ON) - -option(ENABLE_LMS "Enable LMS/HSS signature" ON) -option(ENABLE_XMSS "Enable XMSS/XMSS^MT signature" ON) -option(ENABLE_SPHINCS "Enable SPHINCS+ signature" ON) -option(ENABLE_KYBER "Enable Kyber" ON) - -option(ENABLE_SHA1 "Enable SHA1" ON) -option(ENABLE_SHA2 "Enable SHA2" ON) -option(ENABLE_AES "Enable AES" ON) -option(ENABLE_AES_CCM "Enable AES CCM mode" ON) -option(ENABLE_ZUC "Enable ZUC" ON) -option(ENABLE_GHASH "Enable standalone GHASH command and test" ON) - -option(ENABLE_SKF "Enable SKF module" ON) -option(ENABLE_SDF "Enable SDF module" ON) - -if (APPLE) - option(ENABLE_ASM_UNDERSCORE_PREFIX "Add prefix `_` to assembly symbols" ON) -else() - option(ENABLE_ASM_UNDERSCORE_PREFIX "Add prefix `_` to assembly symbols" OFF) -endif() - -option(ENABLE_TLS "Enable TLS and TLCP protocol support" ON) -option(ENABLE_QUIC "Enable QUIC support" ON) -option(ENABLE_TLS_DEBUG "Enable TLS and TLCP print debug message" OFF) - -option (ENABLE_SM2_ENC_PRE_COMPUTE "Enable SM2 encryption precomputing" ON) - -set(src - src/version.c - src/debug.c - src/sm4.c - src/sm4_cbc.c - src/sm4_ctr.c - src/sm4_gcm.c - src/sm3.c - src/sm3_hmac.c - src/sm3_kdf.c - src/sm3_pbkdf2.c - src/sm2_z256.c - src/sm2_z256_table.c - src/sm2_key.c - src/sm2_sign.c - src/sm2_enc.c - src/sm2_exch.c - src/block_cipher.c - src/digest.c - src/hmac.c - src/hkdf.c - src/pbkdf2.c - src/gf128.c - src/ghash.c - src/pkcs8.c - src/ec.c - src/asn1.c - src/hex.c - src/base64.c - src/pem.c - src/x509_alg.c - src/x509_cer.c - src/x509_ext.c - src/x509_vrf.c - src/x509_req.c - src/x509_crl.c - src/x509_new.c - src/ocsp.c - src/x509_key.c - src/rsa.c - src/socket.c - src/file.c - src/passwd.c -) - -set(tools - tools/gmssl.c - tools/passwd.c - tools/version.c -# tools/sm4.c - tools/sm4_cbc.c - tools/sm4_ctr.c - tools/sm4_gcm.c - tools/sm3.c - tools/sm3_hmac.c - tools/sm3_pbkdf2.c - tools/sm2keygen.c - tools/sm2sign.c - tools/sm2verify.c - tools/sm2encrypt.c - tools/sm2decrypt.c - tools/sm2exch.c - tools/rand.c - tools/certgen.c - tools/certparse.c - tools/certverify.c - tools/certrevoke.c - tools/ocspreq.c - tools/ocspget.c - tools/ocspsign.c - tools/ocspverify.c - tools/reqgen.c - tools/reqparse.c - tools/reqsign.c - tools/crlgen.c - tools/crlget.c - tools/crlparse.c - tools/crlverify.c -) - -set(tests - sm4 - sm4_cbc - sm4_ctr - sm4_gcm - sm3 - sm3_hmac - sm3_hkdf - sm2_z256 - sm2_key - sm2_sign - sm2_enc - sm2_exch - block_cipher - digest - hkdf - pbkdf2 - gf128 - pkcs8 - ec - asn1 - hex - base64 - pem - x509 - x509_oid - x509_alg - x509_str - x509_ext - x509_vrf - x509_req - x509_crl - x509_key - ocsp -) - - - - -include(CheckSymbolExists) - - -option(ENABLE_SMALL_FOOTPRINT "Enable small code size" OFF) -if (ENABLE_SMALL_FOOTPRINT) - message(STATUS "ENABLE_SMALL_FOOTPRINT is ON") - add_definitions(-DENABLE_SMALL_FOOTPRINT) -endif() - - - -if (ENABLE_TEST_SPEED) - message(STATUS "ENABLE_TEST_SPEED is ON") - add_definitions(-DENABLE_TEST_SPEED) -endif() - -if (ENABLE_SLOW_TEST) - message(STATUS "ENABLE_SLOW_TEST is ON") - add_definitions(-DENABLE_SLOW_TEST) -endif() - - -option(ENABLE_SM2_ALGOR_ID_ENCODE_NULL "Enable AlgorithmIdenifier with algorithm sm2sign_with_sm3 encode a NULL object as parameters" OFF) -if (ENABLE_SM2_ALGOR_ID_ENCODE_NULL) - message(STATUS "ENABLE_SM2_ALGOR_ID_ENCODE_NULL is ON") - add_definitions(-DENABLE_SM2_ALGOR_ID_ENCODE_NULL) -endif() - -if (ENABLE_ASM_UNDERSCORE_PREFIX) - message(STATUS "ENABLE_ASM_UNDERSCORE_PREFIX is ON") - add_definitions(-DENABLE_ASM_UNDERSCORE_PREFIX) -endif() - -if (ENABLE_GMUL_ARM64) - message(STATUS "ENABLE_GMUL_ARM64 is ON") - add_definitions(-DENABLE_GMUL_ARM64) - enable_language(ASM) - list(APPEND src src/gf128_arm64.c) -endif() - - - -if (ENABLE_SM2_ARM64) - message(STATUS "ENABLE_SM2_ARM64 is ON") - add_definitions(-DENABLE_SM2_ARM64) - enable_language(ASM) - list(APPEND src src/sm2_z256_arm64.S) -endif() - -if (ENABLE_SM2_AMD64) - message(STATUS "ENABLE_SM2_AMD64 is ON") - add_definitions(-DENABLE_SM2_AMD64) - enable_language(ASM) - list(APPEND src src/sm2_z256_amd64.S) -endif() - -if (ENABLE_SM2_NEON) - message(STATUS "ENABLE_SM2_NEON is ON") - add_definitions(-DENABLE_SM2_Z256_NEON) -endif() - -if (ENABLE_SM9_ARM64) - if (NOT ENABLE_SM9) - message(FATAL_ERROR "ENABLE_SM9_ARM64 requires ENABLE_SM9") - endif() - message(STATUS "ENABLE_SM9_ARM64 is ON") - add_definitions(-DENABLE_SM9_ARM64) - enable_language(ASM) - list(APPEND src src/sm9_z256_arm64.S) -endif() - - -if (ENABLE_TLS_DEBUG) - message(STATUS "ENABLE_TLS_DEBUG is ON") - add_definitions(-DENABLE_TLS_DEBUG) -endif() - -if (ENABLE_SM3_SSE) - message(STATUS "ENABLE_SM3_SSE is ON") - list(FIND src src/sm3.c sm3_index) - list(REMOVE_AT src ${sm3_index}) - list(INSERT src ${sm3_index} src/sm3_sse.c) - if (CMAKE_C_COMPILER_ID MATCHES "GNU|Clang|AppleClang") - set_source_files_properties(src/sm3_sse.c PROPERTIES COMPILE_OPTIONS "-march=native") - endif() -endif() - -if (ENABLE_SM3_ARM64) - message(STATUS "ENABLE_SM3_ARM64 is ON") - list(FIND src src/sm3.c index) - list(REMOVE_AT src ${index}) - list(INSERT src ${index} src/sm3_arm64.c) -endif() - -if (ENABLE_SM4_ARM64) - message(STATUS "ENABLE_SM4_ARM64 is ON") - list(FIND src src/sm4.c sm4_index) - list(REMOVE_AT src ${sm4_index}) - list(INSERT src ${sm4_index} src/sm4_arm64.c) - enable_language(ASM) -endif() - -if (ENABLE_SM4_AVX2) - message(STATUS "ENABLE_SM4_AVX2 is ON") - list(FIND src src/sm4.c sm4_index) - list(REMOVE_AT src ${sm4_index}) - list(INSERT src ${sm4_index} src/sm4_avx2.c) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -march=native") -endif() - -if (ENABLE_SM4_AESNI) - message(STATUS "ENABLE_SM4_AESNI is ON") - list(FIND src src/sm4.c sm4_index) - list(REMOVE_AT src ${sm4_index}) - list(INSERT src ${sm4_index} src/sm4_aesni.c) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -march=native") -endif() - - - - -if (ENABLE_SM4_CE) - message(STATUS "ENABLE_SM4_CE is ON") - list(FIND src src/sm4.c sm4_index) - list(REMOVE_AT src ${sm4_index}) - list(INSERT src ${sm4_index} src/sm4_ce.c) - set_source_files_properties(src/sm4_ce.c PROPERTIES COMPILE_OPTIONS "-march=armv8.2-a+sm4") -endif() - - -if (ENABLE_SM4_CTR_AESNI_AVX) - message(STATUS "ENABLE_SM4_CTR_AESNI_AVX is ON") - list(FIND src src/sm4_ctr.c sm4_ctr_index) - list(REMOVE_AT src ${sm4_ctr_index}) - list(INSERT src ${sm4_ctr_index} src/sm4_ctr_aesni_avx.c) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -march=native") -endif() - - - -if (ENABLE_SM4_CL) - message(STATUS "ENABLE_SM4_CL is ON") - add_definitions(-DENABLE_SM4_CL) - if (CMAKE_SYSTEM_NAME STREQUAL "Darwin") - add_definitions(-DMACOS) # to include - endif() - list(APPEND src src/sm4_cl.c) - list(APPEND tests sm4_cl) -endif() - -if (ENABLE_SM4_ECB) - message(STATUS "ENABLE_SM4_ECB is ON") - add_definitions(-DENABLE_SM4_ECB) - list(APPEND src src/sm4_ecb.c) - list(APPEND tools tools/sm4_ecb.c) - list(APPEND tests sm4_ecb) -endif() - -if (ENABLE_SM4_OFB) - message(STATUS "ENABLE_SM4_OFB is ON") - add_definitions(-DENABLE_SM4_OFB) - list(APPEND src src/sm4_ofb.c) - list(APPEND tools tools/sm4_ofb.c) - list(APPEND tests sm4_ofb) -endif() - -if (ENABLE_SM4_CFB) - message(STATUS "ENABLE_SM4_CFB is ON") - add_definitions(-DENABLE_SM4_CFB) - list(APPEND src src/sm4_cfb.c) - list(APPEND tools tools/sm4_cfb.c) - list(APPEND tests sm4_cfb) -endif() - -if (ENABLE_SM4_CCM) - message(STATUS "ENABLE_SM4_CCM is ON") - set(ENABLE_SM4_CBC_MAC ON) - add_definitions(-DENABLE_SM4_CCM) - list(APPEND src src/sm4_ccm.c) - list(APPEND tools tools/sm4_ccm.c) - list(APPEND tests sm4_ccm) -endif() - -if (ENABLE_SM4_XTS) - message(STATUS "ENABLE_SM4_XTS is ON") - add_definitions(-DENABLE_SM4_XTS) - list(APPEND src src/sm4_xts.c) - list(APPEND tools tools/sm4_xts.c) - list(APPEND tests sm4_xts) -endif() - -if (ENABLE_SM4_FF1) - message(STATUS "ENABLE_SM4_FF1 is ON") - add_definitions(-DENABLE_SM4_FF1) - list(APPEND src src/sm4_ff1.c) - list(APPEND tools tools/sm4_ff1.c) - list(APPEND tests sm4_ff1) -endif() - - -if (ENABLE_SM9) - message(STATUS "ENABLE_SM9 is ON") - add_definitions(-DENABLE_SM9) - list(APPEND src - src/sm9_z256.c - src/sm9_z256_table.c - src/sm9_key.c - src/sm9_sign.c - src/sm9_enc.c - src/sm9_exch.c - src/sm9_cms.c) - list(APPEND tools - tools/sm9setup.c - tools/sm9keygen.c - tools/sm9sign.c - tools/sm9verify.c - tools/sm9encrypt.c - tools/sm9decrypt.c - tools/sm9exch.c) - list(APPEND tests sm9) -endif() - - -if (ENABLE_CMS) - message(STATUS "ENABLE_CMS is ON") - add_definitions(-DENABLE_CMS) - list(APPEND src src/cms.c) - list(APPEND tools - tools/cmssign.c - tools/cmsverify.c - tools/cmsencrypt.c - tools/cmsdecrypt.c - tools/cmsparse.c) - list(APPEND tests cms) -endif() - - -if (ENABLE_SECP256R1) - message(STATUS "ENABLE_SECP256R1 is ON") - if (NOT ENABLE_SHA2) - message(STATUS "ENABLE_SECP256R1 requires ENABLE_SHA2; enabling ENABLE_SHA2") - set(ENABLE_SHA2 ON) - endif() - add_definitions(-DENABLE_SECP256R1) - list(APPEND src src/bn.c src/secp256r1.c) - list(APPEND tests bn secp256r1 secp256r1_key ecdsa) -endif() - -if (ENABLE_SECP384R1) - message(STATUS "ENABLE_SECP384R1 is ON") - if (NOT ENABLE_SHA2) - message(STATUS "ENABLE_SECP384R1 requires ENABLE_SHA2; enabling ENABLE_SHA2") - set(ENABLE_SHA2 ON) - endif() - add_definitions(-DENABLE_SECP384R1) - if (NOT ENABLE_SECP256R1) - list(APPEND src src/bn.c) - endif() - list(APPEND src src/secp384r1.c) - list(APPEND tests secp384r1 secp384r1_key secp384r1_ecdsa) -endif() - -if (ENABLE_SECP256R1 OR ENABLE_SECP384R1) - list(APPEND tools tools/eckeygen.c) -endif() - -if (ENABLE_LMS) - message(STATUS "ENABLE_LMS is ON") - add_definitions(-DENABLE_LMS) - list(APPEND src src/lms.c) - list(APPEND tools tools/lmskeygen.c tools/lmssign.c tools/lmsverify.c) - list(APPEND tools tools/hsskeygen.c tools/hsssign.c tools/hssverify.c) - list(APPEND tests lms) -endif() - -if (ENABLE_LMS_CL) - if (NOT ENABLE_LMS) - message(FATAL_ERROR "ENABLE_LMS_CL requires ENABLE_LMS") - endif() - message(STATUS "ENABLE_LMS_CL is ON") - add_definitions(-DENABLE_LMS_CL) - if (CMAKE_SYSTEM_NAME STREQUAL "Darwin") - add_definitions(-DMACOS) # to include - endif() - list(APPEND src src/lms_cl.c) - list(APPEND tests lms_cl) -endif() - - -if (ENABLE_XMSS) - message(STATUS "ENABLE_XMSS is ON") - add_definitions(-DENABLE_XMSS) - list(APPEND src src/xmss.c) - list(APPEND tools tools/xmsskeygen.c tools/xmsssign.c tools/xmssverify.c) - list(APPEND tools tools/xmssmtkeygen.c tools/xmssmtsign.c tools/xmssmtverify.c) - list(APPEND tests xmss) -endif() - -if (ENABLE_XMSS_CL) - if (NOT ENABLE_XMSS) - message(FATAL_ERROR "ENABLE_XMSS_CL requires ENABLE_XMSS") - endif() - message(STATUS "ENABLE_XMSS_CL is ON") - add_definitions(-DENABLE_XMSS_CL) - if (CMAKE_SYSTEM_NAME STREQUAL "Darwin") - add_definitions(-DMACOS) # to include - endif() - list(APPEND src src/xmss_cl.c) - list(APPEND tests xmss_cl) -endif() - - -if (ENABLE_SPHINCS) - message(STATUS "ENABLE_SPHINCS is ON") - add_definitions(-DENABLE_SPHINCS) - list(APPEND src src/sphincs.c) - list(APPEND tools tools/sphincskeygen.c tools/sphincssign.c tools/sphincsverify.c) - list(APPEND tests sphincs) -endif() - -if (ENABLE_SM4_CL OR ENABLE_LMS_CL OR ENABLE_XMSS_CL) - list(APPEND src src/cl.c) -endif() - -if (ENABLE_LMS_CL OR ENABLE_XMSS_CL) - list(APPEND src src/sm3_cl.c) -endif() - - -if (ENABLE_KYBER) - message(STATUS "ENABLE_KYBER is ON") - add_definitions(-DENABLE_KYBER) - list(APPEND src src/sha3.c src/kyber.c) - list(APPEND tools tools/kyberkeygen.c tools/kyberencap.c tools/kyberdecap.c) - list(APPEND tests kyber) -endif() - - -if (ENABLE_SHA1) - message(STATUS "ENABLE_SHA1 is ON") - add_definitions(-DENABLE_SHA1) - list(APPEND src src/sha1.c) - list(APPEND tests sha1) -endif() - - -if (ENABLE_SHA2) - message(STATUS "ENABLE_SHA2 is ON") - add_definitions(-DENABLE_SHA2) - list(APPEND src src/sha2.c) - list(APPEND tests sha224 sha256 sha384 sha512 hmac) -endif() - -if (ENABLE_AES) - message(STATUS "ENABLE_AES is ON") - add_definitions(-DENABLE_AES) - list(APPEND src src/aes.c) - list(APPEND tests aes) -endif() - -if (ENABLE_AES_CCM) - if (NOT ENABLE_AES) - message(FATAL_ERROR "ENABLE_AES_CCM requires ENABLE_AES") - endif() - message(STATUS "ENABLE_AES_CCM is ON") - add_definitions(-DENABLE_AES_CCM) -endif() - - -if (ENABLE_ZUC) - message(STATUS "ENABLE_ZUC is ON") - add_definitions(-DENABLE_ZUC) - list(APPEND src src/zuc.c src/zuc_modes.c) - list(APPEND tools - tools/zuc.c - tools/zuc256.c - tools/zuc_128_eea3.c - tools/zuc_128_eia3.c) - list(APPEND tests zuc) -endif() - -if (ENABLE_GHASH) - message(STATUS "ENABLE_GHASH is ON") - add_definitions(-DENABLE_GHASH) - list(APPEND tools tools/ghash.c) - list(APPEND tests ghash) -endif() - -if (ENABLE_TLS) - message(STATUS "ENABLE_TLS is ON") - message(STATUS "TLS ECDHE-ECDSA cipher suites require ENABLE_AES, ENABLE_SHA2 and ENABLE_SECP256R1") - add_definitions(-DENABLE_TLS) - list(APPEND src - src/tls.c - src/tls_cert.c - src/tls_alpn.c - src/tls_ext.c - src/tls_psk.c - src/tls_sni.c - src/sct.c - src/tls_sct.c - src/tls_ocsp.c - src/tls_cookie.c - src/tls_trace.c - src/tls_vrf.c - src/tlcp.c - src/tls12.c - src/tls13.c) - list(APPEND tools - tools/tlcp_client.c - tools/tlcp_server.c - tools/tls12_client.c - tools/tls12_server.c - tools/tls13_client.c - tools/tls13_server.c - tools/sctverify.c) - list(APPEND tests tls tls13 tls_ocsp) -endif() - -if (ENABLE_QUIC) - if (ENABLE_TLS AND ENABLE_SHA2) - message(STATUS "ENABLE_QUIC is ON") - add_definitions(-DENABLE_QUIC) - list(APPEND src src/quic.c) - list(APPEND tools - tools/quic_client.c - tools/quic_server.c) - list(APPEND tests quic) - else() - message(STATUS "ENABLE_QUIC requires ENABLE_TLS and ENABLE_SHA2; disabled") - endif() -endif() - - -if (ENABLE_INTEL_RDRAND) - include(CheckSourceCompiles) - set(CMAKE_REQUIRED_FLAGS "-rdrand") - check_source_compiles(C - "#include int main(void) { unsigned long long val; _rdrand64_step(&val); return 0; }" - HAVE_INTEL_RDRAND) - if (HAVE_INTEL_RDRAND) - message(STATUS "ENABLE_INTEL_RDRAND") - add_definitions(-DENABLE_INTEL_RDRAND) - list(APPEND src src/rdrand.c) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mrdrnd") - endif() - if (ENABLE_INTEL_RDSEED) - set(CMAKE_REQUIRED_FLAGS "-rdseed") - check_source_compiles(C - "#include int main(void) { unsigned long long val; _rdseed64_step(&val); return 0; }" - HAVE_INTEL_RDSEED) - if (HAVE_INTEL_RDSEED) - message(STATUS "ENABLE_INTEL_RDSEED") - add_definitions(-DENABLE_INTEL_RDSEED) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mrdseed") - endif() - endif() -endif() - - -if (ENABLE_SM4_CBC_MAC) - message(STATUS "ENABLE_SM4_CBC_MAC is ON") - add_definitions(-DENABLE_SM4_CBC_MAC) - list(APPEND src src/sm4_cbc_mac.c) - list(APPEND tools tools/sm4_cbc_mac.c) - list(APPEND tests sm4_cbc_mac) -endif() - - -check_symbol_exists(getentropy "unistd.h" HAVE_GETENTROPY) -if (WIN32) - add_definitions(-D_WINSOCK_DEPRECATED_NO_WARNINGS) - list(APPEND src src/rand_win.c src/http_win.c) -elseif (APPLE) - list(APPEND src src/rand_apple.c src/http.c) -elseif (HAVE_GETENTROPY) - list(APPEND src src/rand_unix.c src/http.c) - message(STATUS "have getentropy") -else() - list(APPEND src src/rand.c src/http.c) -endif() - - -if (ENABLE_SKF) - message(STATUS "ENABLE_SKF is ON") - add_definitions(-DENABLE_SKF) - list(APPEND src - src/skf/skf.c - src/skf/skf_lib.c - src/skf/skf_meth.c - src/skf/skf_ext.c - src/skf/skf_prn.c - src/skf/skf_wisec.c) - list(APPEND tools tools/skfutil.c) - add_library(skf_dummy SHARED src/skf/skf_dummy.c) - set_target_properties(skf_dummy PROPERTIES VERSION 3.3 SOVERSION 3) -endif() - -if (ENABLE_SDF) - message(STATUS "ENABLE_SDF is ON") - add_definitions(-DENABLE_SDF) - list(APPEND src - src/sdf/sdf.c - src/sdf/sdf_lib.c - src/sdf/sdf_meth.c - src/sdf/sdf_ext.c - src/sdf/sdf_sansec.c) - list(APPEND tools tools/sdfinfo.c tools/sdfdigest.c tools/sdfexport.c tools/sdfsign.c tools/sdfencrypt.c tools/sdfdecrypt.c tools/sdftest.c) -endif() - - -option(ENABLE_HTTP_TESTS "Enable HTTP GET/POST related tests" OFF) -if (ENABLE_HTTP_TESTS) - message(STATUS "ENABLE_HTTP_TESTS") - list(APPEND tests http http_crl) -endif() - - -option(BUILD_SHARED_LIBS "Build using shared libraries" ON) - -if (CMAKE_C_COMPILER_ID MATCHES "MSVC") - message(STATUS "CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS") - set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) # set before add_library -endif() - -add_library(gmssl ${src}) - - - - - -if (WIN32) - target_link_libraries(gmssl PRIVATE Ws2_32) -elseif (APPLE) - if (ENABLE_SDF) - target_link_libraries(gmssl dl) - endif() - target_link_libraries(gmssl "-framework Security") - #target_link_libraries(gmssl "-framework CoreFoundation") # rand_apple.c CFRelease() -elseif (MINGW) - target_link_libraries(gmssl PRIVATE wsock32) -else() - if (ENABLE_SDF) - target_link_libraries(gmssl dl) - endif() -endif() - -if (ENABLE_SM4_CL OR ENABLE_LMS_CL OR ENABLE_XMSS_CL) - if (APPLE) - target_link_libraries(gmssl "-framework OpenCL") - else() - find_package(OpenCL REQUIRED) - target_include_directories(gmssl PRIVATE ${OpenCL_INCLUDE_DIRS}) - target_link_libraries(gmssl ${OpenCL_LIBRARIES}) - endif() -endif() - - -set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib") - -SET_TARGET_PROPERTIES(gmssl PROPERTIES VERSION 3.3 SOVERSION 3) - - -install(TARGETS gmssl ARCHIVE DESTINATION lib LIBRARY DESTINATION lib RUNTIME DESTINATION bin) -install(DIRECTORY ${CMAKE_SOURCE_DIR}/include/gmssl DESTINATION include) - -if(UNIX AND NOT APPLE AND BUILD_SHARED_LIBS) - install(CODE [[ - message(STATUS "GmSSL shared library installed to: ${CMAKE_INSTALL_PREFIX}/lib") - message(STATUS "If gmssl or external applications cannot find libgmssl.so, configure the runtime library path manually.") - message(STATUS "For a temporary shell: export LD_LIBRARY_PATH=${CMAKE_INSTALL_PREFIX}/lib:$LD_LIBRARY_PATH") - message(STATUS "For a system-wide setup: echo ${CMAKE_INSTALL_PREFIX}/lib | sudo tee /etc/ld.so.conf.d/gmssl.conf && sudo ldconfig") - ]]) -endif() - - -if (NOT ${CMAKE_SYSTEM_NAME} STREQUAL "iOS") - add_executable(gmssl-bin ${tools}) - target_link_libraries(gmssl-bin LINK_PUBLIC gmssl) - set_target_properties(gmssl-bin PROPERTIES RUNTIME_OUTPUT_NAME gmssl) - if (WIN32) - target_link_libraries(gmssl-bin PRIVATE Ws2_32) - endif() - - enable_testing() - foreach(name ${tests}) - add_test(NAME ${name} COMMAND ${name}test) - add_executable(${name}test tests/${name}test.c) - target_link_libraries (${name}test LINK_PUBLIC gmssl) - endforeach() - - install(TARGETS gmssl-bin RUNTIME DESTINATION bin) -endif() - -if (CMAKE_C_COMPILER_ID MATCHES "MSVC") - set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT gmssl-bin) - set(CMAKE_INSTALL_PREFIX "C:/Program Files/GmSSL") # change by `cmake -DCMAKE_INSTALL_PREFIX=C:\path\to\install` - # run `set path=%path%;C:\Program Files\GmSSL\bin` - add_compile_definitions(_CRT_SECURE_NO_WARNINGS) - - target_compile_options(gmssl PRIVATE /utf-8) - target_compile_options(gmssl-bin PRIVATE /utf-8) - -# target_compile_options(gmssl PRIVATE /wd4996) -# target_compile_options(gmssl-bin PRIVATE /wd4996) -endif() - - -add_test(NAME tool_sm3 COMMAND ${CMAKE_COMMAND} -P "${CMAKE_SOURCE_DIR}/cmake/tool_sm3.cmake") -add_test(NAME tool_sm2 COMMAND ${CMAKE_COMMAND} -P "${CMAKE_SOURCE_DIR}/cmake/tool_sm2.cmake") -add_test(NAME tool_rand COMMAND ${CMAKE_COMMAND} -P "${CMAKE_SOURCE_DIR}/cmake/tool_rand.cmake") -add_test(NAME tool_sm4 COMMAND ${CMAKE_COMMAND} - -DENABLE_SM4_ECB=${ENABLE_SM4_ECB} - -DENABLE_SM4_CFB=${ENABLE_SM4_CFB} - -DENABLE_SM4_OFB=${ENABLE_SM4_OFB} - -DENABLE_SM4_CCM=${ENABLE_SM4_CCM} - -DENABLE_SM4_XTS=${ENABLE_SM4_XTS} - -DENABLE_SM4_CBC_MAC=${ENABLE_SM4_CBC_MAC} - -DENABLE_SM4_FF1=${ENABLE_SM4_FF1} - -P "${CMAKE_SOURCE_DIR}/cmake/tool_sm4.cmake") -if(ENABLE_ZUC) - add_test(NAME tool_zuc COMMAND ${CMAKE_COMMAND} -P "${CMAKE_SOURCE_DIR}/cmake/tool_zuc.cmake") -endif() -if(ENABLE_GHASH) - add_test(NAME tool_ghash COMMAND ${CMAKE_COMMAND} -P "${CMAKE_SOURCE_DIR}/cmake/tool_ghash.cmake") -endif() -if(ENABLE_SM9) - add_test(NAME tool_sm9 COMMAND ${CMAKE_COMMAND} -P "${CMAKE_SOURCE_DIR}/cmake/tool_sm9.cmake") -endif() -if(ENABLE_LMS OR ENABLE_XMSS OR ENABLE_SPHINCS OR ENABLE_KYBER) - add_test(NAME tool_pqc COMMAND ${CMAKE_COMMAND} - -DENABLE_LMS=${ENABLE_LMS} - -DENABLE_XMSS=${ENABLE_XMSS} - -DENABLE_SPHINCS=${ENABLE_SPHINCS} - -DENABLE_KYBER=${ENABLE_KYBER} - -P "${CMAKE_SOURCE_DIR}/cmake/tool_pqc.cmake") -endif() -add_test(NAME tool_cert COMMAND ${CMAKE_COMMAND} -P "${CMAKE_SOURCE_DIR}/cmake/tool_cert.cmake") -set_tests_properties(tool_cert PROPERTIES FIXTURES_SETUP gmssl_cert_files) -add_test(NAME tool_crl COMMAND ${CMAKE_COMMAND} -P "${CMAKE_SOURCE_DIR}/cmake/tool_crl.cmake") -add_test(NAME tool_ocsp COMMAND ${CMAKE_COMMAND} -P "${CMAKE_SOURCE_DIR}/cmake/tool_ocsp.cmake") -set_tests_properties(tool_crl tool_ocsp PROPERTIES FIXTURES_REQUIRED gmssl_cert_files) -if(ENABLE_CMS) - add_test(NAME tool_cms COMMAND ${CMAKE_COMMAND} -P "${CMAKE_SOURCE_DIR}/cmake/tool_cms.cmake") - set_tests_properties(tool_cms PROPERTIES FIXTURES_REQUIRED gmssl_cert_files) -endif() -if(ENABLE_TLS AND NOT WIN32) - find_program(OPENSSL_EXECUTABLE openssl) - set(GMSSL_OPENSSL_INTEROP_ENABLED OFF) - if(ENABLE_AES AND ENABLE_SHA2 AND ENABLE_SECP256R1) - set(GMSSL_OPENSSL_INTEROP_ENABLED ON) - else() - message(STATUS "OpenSSL TLS interop tests require ENABLE_AES=ON, ENABLE_SHA2=ON and ENABLE_SECP256R1=ON; skipping") - endif() - add_test(NAME tool_tlcp_sm4_gcm_sni COMMAND ${CMAKE_COMMAND} -DTEST_CASE=tlcp_sm4_gcm_sni -P "${CMAKE_SOURCE_DIR}/cmake/tlcp_commands.cmake") - add_test(NAME tool_tlcp_sm4_cbc_sni COMMAND ${CMAKE_COMMAND} -DTEST_CASE=tlcp_sm4_cbc_sni -P "${CMAKE_SOURCE_DIR}/cmake/tlcp_commands.cmake") - add_test(NAME tool_tlcp_sm4_gcm_client_cert COMMAND ${CMAKE_COMMAND} -DTEST_CASE=tlcp_sm4_gcm_client_cert -P "${CMAKE_SOURCE_DIR}/cmake/tlcp_commands.cmake") - add_test(NAME tool_tlcp_ecdhe_sm4_cbc_client_cert COMMAND ${CMAKE_COMMAND} -DTEST_CASE=tlcp_ecdhe_sm4_cbc_client_cert -P "${CMAKE_SOURCE_DIR}/cmake/tlcp_commands.cmake") - add_test(NAME tool_tlcp_ecdhe_sm4_gcm_client_cert COMMAND ${CMAKE_COMMAND} -DTEST_CASE=tlcp_ecdhe_sm4_gcm_client_cert -P "${CMAKE_SOURCE_DIR}/cmake/tlcp_commands.cmake") - add_test(NAME tool_tls12_sm4_gcm_sni COMMAND ${CMAKE_COMMAND} -DTEST_CASE=tls12_sm4_gcm_sni -P "${CMAKE_SOURCE_DIR}/cmake/tls12_commands.cmake") - add_test(NAME tool_tls12_sm4_cbc_sni COMMAND ${CMAKE_COMMAND} -DTEST_CASE=tls12_sm4_cbc_sni -P "${CMAKE_SOURCE_DIR}/cmake/tls12_commands.cmake") - add_test(NAME tool_tls12_sm4_gcm_client_cert COMMAND ${CMAKE_COMMAND} -DTEST_CASE=tls12_sm4_gcm_client_cert -P "${CMAKE_SOURCE_DIR}/cmake/tls12_commands.cmake") - add_test(NAME tool_tls12_sm4_gcm_renegotiation_info COMMAND ${CMAKE_COMMAND} -DTEST_CASE=tls12_sm4_gcm_renegotiation_info -P "${CMAKE_SOURCE_DIR}/cmake/tls12_commands.cmake") - add_test(NAME tool_tls12_sm4_gcm_renegotiation_info_scsv COMMAND ${CMAKE_COMMAND} -DTEST_CASE=tls12_sm4_gcm_renegotiation_info_scsv -P "${CMAKE_SOURCE_DIR}/cmake/tls12_commands.cmake") - add_test(NAME tool_tls12_client_reject_renegotiation_info_both - COMMAND bash -c "bin/gmssl tls12_client -host 127.0.0.1 -renegotiation_info -renegotiation_info_scsv > tls12_client_reject_renegotiation_info_both.log 2>&1; test $? -ne 0 && grep -q 'should not be used together' tls12_client_reject_renegotiation_info_both.log") - add_test(NAME tool_tls13_sm4_gcm_sni COMMAND ${CMAKE_COMMAND} -DTEST_CASE=tls13_sm4_gcm_sni -P "${CMAKE_SOURCE_DIR}/cmake/tls13_commands.cmake") - add_test(NAME tool_tls13_sm4_gcm_client_cert COMMAND ${CMAKE_COMMAND} -DTEST_CASE=tls13_sm4_gcm_client_cert -P "${CMAKE_SOURCE_DIR}/cmake/tls13_commands.cmake") - add_test(NAME tool_tls13_hrr_sm4_gcm COMMAND ${CMAKE_COMMAND} -DTEST_CASE=tls13_hrr_sm4_gcm -P "${CMAKE_SOURCE_DIR}/cmake/tls13_commands.cmake") - add_test(NAME tool_tls13_psk_dhe_sm4_gcm COMMAND ${CMAKE_COMMAND} -DTEST_CASE=tls13_psk_dhe_sm4_gcm -P "${CMAKE_SOURCE_DIR}/cmake/tls13_commands.cmake") - add_test(NAME tool_tls13_psk_only_sm4_gcm COMMAND ${CMAKE_COMMAND} -DTEST_CASE=tls13_psk_only_sm4_gcm -P "${CMAKE_SOURCE_DIR}/cmake/tls13_commands.cmake") - add_test(NAME tool_tls13_early_data_sm4_gcm COMMAND ${CMAKE_COMMAND} -DTEST_CASE=tls13_early_data_sm4_gcm -P "${CMAKE_SOURCE_DIR}/cmake/tls13_commands.cmake") - set_tests_properties( - tool_tlcp_sm4_gcm_sni - tool_tlcp_sm4_cbc_sni - tool_tlcp_sm4_gcm_client_cert - tool_tlcp_ecdhe_sm4_cbc_client_cert - tool_tlcp_ecdhe_sm4_gcm_client_cert - tool_tls12_sm4_gcm_sni - tool_tls12_sm4_cbc_sni - tool_tls12_sm4_gcm_client_cert - tool_tls12_sm4_gcm_renegotiation_info - tool_tls12_sm4_gcm_renegotiation_info_scsv - tool_tls13_sm4_gcm_sni - tool_tls13_sm4_gcm_client_cert - tool_tls13_hrr_sm4_gcm - tool_tls13_psk_dhe_sm4_gcm - tool_tls13_psk_only_sm4_gcm - tool_tls13_early_data_sm4_gcm - PROPERTIES FIXTURES_REQUIRED gmssl_cert_files) - if(OPENSSL_EXECUTABLE AND GMSSL_OPENSSL_INTEROP_ENABLED) - add_test(NAME tool_tls12_openssl_server COMMAND ${CMAKE_COMMAND} -DOPENSSL_EXECUTABLE=${OPENSSL_EXECUTABLE} -DTEST_CASE=tls12_openssl_server -P "${CMAKE_SOURCE_DIR}/cmake/openssl_interop_commands.cmake") - add_test(NAME tool_tls12_openssl_server_renegotiation_info COMMAND ${CMAKE_COMMAND} -DOPENSSL_EXECUTABLE=${OPENSSL_EXECUTABLE} -DTEST_CASE=tls12_openssl_server_renegotiation_info -P "${CMAKE_SOURCE_DIR}/cmake/openssl_interop_commands.cmake") - add_test(NAME tool_tls12_openssl_server_renegotiation_info_scsv COMMAND ${CMAKE_COMMAND} -DOPENSSL_EXECUTABLE=${OPENSSL_EXECUTABLE} -DTEST_CASE=tls12_openssl_server_renegotiation_info_scsv -P "${CMAKE_SOURCE_DIR}/cmake/openssl_interop_commands.cmake") - add_test(NAME tool_tls12_openssl_client COMMAND ${CMAKE_COMMAND} -DOPENSSL_EXECUTABLE=${OPENSSL_EXECUTABLE} -DTEST_CASE=tls12_openssl_client -P "${CMAKE_SOURCE_DIR}/cmake/openssl_interop_commands.cmake") - add_test(NAME tool_tls13_openssl_server COMMAND ${CMAKE_COMMAND} -DOPENSSL_EXECUTABLE=${OPENSSL_EXECUTABLE} -DTEST_CASE=tls13_openssl_server -P "${CMAKE_SOURCE_DIR}/cmake/openssl_interop_commands.cmake") - add_test(NAME tool_tls13_openssl_client COMMAND ${CMAKE_COMMAND} -DOPENSSL_EXECUTABLE=${OPENSSL_EXECUTABLE} -DTEST_CASE=tls13_openssl_client -P "${CMAKE_SOURCE_DIR}/cmake/openssl_interop_commands.cmake") - add_test(NAME tool_tls13_hrr_openssl_client COMMAND ${CMAKE_COMMAND} -DOPENSSL_EXECUTABLE=${OPENSSL_EXECUTABLE} -DTEST_CASE=tls13_hrr_openssl_client -P "${CMAKE_SOURCE_DIR}/cmake/openssl_interop_commands.cmake") - add_test(NAME tool_tls13_psk_dhe_openssl_server COMMAND ${CMAKE_COMMAND} -DOPENSSL_EXECUTABLE=${OPENSSL_EXECUTABLE} -DTEST_CASE=tls13_psk_dhe_openssl_server -P "${CMAKE_SOURCE_DIR}/cmake/openssl_interop_commands.cmake") - add_test(NAME tool_tls13_psk_dhe_openssl_client COMMAND ${CMAKE_COMMAND} -DOPENSSL_EXECUTABLE=${OPENSSL_EXECUTABLE} -DTEST_CASE=tls13_psk_dhe_openssl_client -P "${CMAKE_SOURCE_DIR}/cmake/openssl_interop_commands.cmake") - add_test(NAME tool_tls13_psk_only_openssl_server COMMAND ${CMAKE_COMMAND} -DOPENSSL_EXECUTABLE=${OPENSSL_EXECUTABLE} -DTEST_CASE=tls13_psk_only_openssl_server -P "${CMAKE_SOURCE_DIR}/cmake/openssl_interop_commands.cmake") - add_test(NAME tool_tls13_psk_only_openssl_client COMMAND ${CMAKE_COMMAND} -DOPENSSL_EXECUTABLE=${OPENSSL_EXECUTABLE} -DTEST_CASE=tls13_psk_only_openssl_client -P "${CMAKE_SOURCE_DIR}/cmake/openssl_interop_commands.cmake") - set_tests_properties( - tool_tls12_openssl_server - tool_tls12_openssl_server_renegotiation_info - tool_tls12_openssl_server_renegotiation_info_scsv - tool_tls12_openssl_client - tool_tls13_openssl_server - tool_tls13_openssl_client - tool_tls13_hrr_openssl_client - tool_tls13_psk_dhe_openssl_server - tool_tls13_psk_dhe_openssl_client - tool_tls13_psk_only_openssl_server - tool_tls13_psk_only_openssl_client - PROPERTIES FIXTURES_REQUIRED gmssl_cert_files) - elseif(NOT OPENSSL_EXECUTABLE) - message(STATUS "openssl executable not found; skipping OpenSSL TLS interop tests") - endif() -endif() - -# Generate install package with cpack -# cpack -G TGZ -# cpack -G STGZ -# cpack -G NSIS64 # Windows only -# -# Install the STGZ package -# ./GmSSL--Linux.sh --prefix=/usr/local -# -set(CPACK_PACKAGE_NAME "GmSSL") -set(CPACK_PACKAGE_VENDOR "GmSSL develop team") -set(CPACK_PACKAGE_VERSION "3.3.0-dev.1183") -set(CPACK_PACKAGE_DESCRIPTION_FILE ${PROJECT_SOURCE_DIR}/README.md) -set(CPACK_NSIS_MODIFY_PATH ON) -include(CPack) diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 000000000..a8988d6a3 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,76 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, sex characteristics, gender identity and expression, +level of experience, education, socio-economic status, nationality, personal +appearance, race, religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or + advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at guan@pku.edu.cn. All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see +https://www.contributor-covenant.org/faq diff --git a/Configurations/00-base-templates.conf b/Configurations/00-base-templates.conf new file mode 100644 index 000000000..bf3dd5f02 --- /dev/null +++ b/Configurations/00-base-templates.conf @@ -0,0 +1,296 @@ +# -*- Mode: perl -*- +%targets=( + DEFAULTS => { + template => 1, + + cflags => "", + defines => [], + thread_scheme => "(unknown)", # Assume we don't know + thread_defines => [], + + apps_aux_src => "", + cpuid_asm_src => "mem_clr.c", + uplink_aux_src => "", + bn_asm_src => "bn_asm.c", + ec_asm_src => "", + des_asm_src => "des_enc.c fcrypt_b.c", + aes_asm_src => "aes_core.c aes_cbc.c", + bf_asm_src => "bf_enc.c", + md5_asm_src => "", + cast_asm_src => "c_enc.c", + rc4_asm_src => "rc4_enc.c rc4_skey.c", + rmd160_asm_src => "", + rc5_asm_src => "rc5_enc.c", + wp_asm_src => "wp_block.c", + cmll_asm_src => "camellia.c cmll_misc.c cmll_cbc.c", + modes_asm_src => "", + padlock_asm_src => "", + gmi_asm_src => "", + chacha_asm_src => "chacha_enc.c", + poly1305_asm_src => "", + + unistd => "", + shared_target => "", + shared_cflag => "", + shared_defines => [], + shared_ldflag => "", + shared_rcflag => "", + shared_extension => "", + + build_scheme => [ "unified", "unix" ], + build_file => "Makefile", + }, + + BASE_common => { + template => 1, + defines => + sub { + my @defs = (); + push @defs, "ZLIB" unless $disabled{zlib}; + push @defs, "ZLIB_SHARED" unless $disabled{"zlib-dynamic"}; + return [ @defs ]; + }, + }, + + BASE_unix => { + inherit_from => [ "BASE_common" ], + template => 1, + + ex_libs => + sub { + unless ($disabled{zlib}) { + if (defined($disabled{"zlib-dynamic"})) { + if (defined($withargs{zlib_lib})) { + return "-L".$withargs{zlib_lib}." -lz"; + } else { + return "-lz"; + } + } + } + return (); }, + + build_scheme => [ "unified", "unix" ], + build_file => "Makefile", + }, + + BASE_Windows => { + inherit_from => [ "BASE_common" ], + template => 1, + + ex_libs => + sub { + unless ($disabled{zlib}) { + if (defined($disabled{"zlib-dynamic"})) { + return $withargs{zlib_lib} // "ZLIB1"; + } + } + return (); + }, + + ld => "link", + lflags => "/nologo", + loutflag => "/out:", + ar => "lib", + arflags => "/nologo", + aroutflag => "/out:", + rc => "rc", + rcoutflag => "/fo", + mt => "mt", + mtflags => "-nologo", + mtinflag => "-manifest ", + mtoutflag => "-outputresource:", + + build_file => "makefile", + build_scheme => [ "unified", "windows" ], + }, + + BASE_VMS => { + inherit_from => [ "BASE_common" ], + template => 1, + + build_file => "descrip.mms", + build_scheme => [ "unified", "VMS" ], + }, + + uplink_common => { + template => 1, + apps_aux_src => add("../ms/applink.c"), + uplink_aux_src => add("../ms/uplink.c"), + defines => add("OPENSSL_USE_APPLINK"), + }, + x86_uplink => { + inherit_from => [ "uplink_common" ], + template => 1, + uplink_aux_src => add("uplink-x86.s"), + }, + x86_64_uplink => { + inherit_from => [ "uplink_common" ], + template => 1, + uplink_aux_src => add("uplink-x86_64.s"), + }, + ia64_uplink => { + inherit_from => [ "uplink_common" ], + template => 1, + uplink_aux_src => add("uplink-ia64.s"), + }, + + x86_asm => { + template => 1, + cpuid_asm_src => "x86cpuid.s", + bn_asm_src => "bn-586.s co-586.s x86-mont.s x86-gf2m.s", + ec_asm_src => "ecp_nistz256.c ecp_nistz256-x86.s", + des_asm_src => "des-586.s crypt586.s", + aes_asm_src => "aes-586.s vpaes-x86.s aesni-x86.s", + bf_asm_src => "bf-586.s", + md5_asm_src => "md5-586.s", + cast_asm_src => "cast-586.s", + sha1_asm_src => "sha1-586.s sha256-586.s sha512-586.s", + rc4_asm_src => "rc4-586.s", + rmd160_asm_src => "rmd-586.s", + rc5_asm_src => "rc5-586.s", + wp_asm_src => "wp_block.c wp-mmx.s", + cmll_asm_src => "cmll-x86.s", + modes_asm_src => "ghash-x86.s", + padlock_asm_src => "e_padlock-x86.s", + gmi_asm_src => "e_gmi-x86.s", + chacha_asm_src => "chacha-x86.s", + poly1305_asm_src=> "poly1305-x86.s", + }, + x86_elf_asm => { + template => 1, + inherit_from => [ "x86_asm" ], + perlasm_scheme => "elf" + }, + x86_64_asm => { + template => 1, + cpuid_asm_src => "x86_64cpuid.s", + bn_asm_src => "asm/x86_64-gcc.c x86_64-mont.s x86_64-mont5.s x86_64-gf2m.s rsaz_exp.c rsaz-x86_64.s rsaz-avx2.s", + ec_asm_src => "ecp_nistz256.c ecp_nistz256-x86_64.s ecp_sm2z256.c ecp_sm2z256-x86_64.s", + aes_asm_src => "aes-x86_64.s vpaes-x86_64.s bsaes-x86_64.s aesni-x86_64.s aesni-sha1-x86_64.s aesni-sha256-x86_64.s aesni-mb-x86_64.s", + md5_asm_src => "md5-x86_64.s", + sha1_asm_src => "sha1-x86_64.s sha256-x86_64.s sha512-x86_64.s sha1-mb-x86_64.s sha256-mb-x86_64.s", + rc4_asm_src => "rc4-x86_64.s rc4-md5-x86_64.s", + wp_asm_src => "wp-x86_64.s", + cmll_asm_src => "cmll-x86_64.s cmll_misc.c", + modes_asm_src => "ghash-x86_64.s aesni-gcm-x86_64.s", + padlock_asm_src => "e_padlock-x86_64.s", + gmi_asm_src => "e_gmi-x86_64.s", + chacha_asm_src => "chacha-x86_64.s", + poly1305_asm_src=> "poly1305-x86_64.s", + }, + ia64_asm => { + template => 1, + cpuid_asm_src => "ia64cpuid.s", + bn_asm_src => "bn-ia64.s ia64-mont.s", + aes_asm_src => "aes_core.c aes_cbc.c aes-ia64.s", + md5_asm_src => "md5-ia64.s", + sha1_asm_src => "sha1-ia64.s sha256-ia64.s sha512-ia64.s", + rc4_asm_src => "rc4-ia64.s rc4_skey.c", + modes_asm_src => "ghash-ia64.s", + perlasm_scheme => "void" + }, + sparcv9_asm => { + template => 1, + cpuid_asm_src => "sparcv9cap.c sparccpuid.S", + bn_asm_src => "asm/sparcv8plus.S sparcv9-mont.S sparcv9a-mont.S vis3-mont.S sparct4-mont.S sparcv9-gf2m.S", + ec_asm_src => "ecp_nistz256.c ecp_nistz256-sparcv9.S", + des_asm_src => "des_enc-sparc.S fcrypt_b.c dest4-sparcv9.S", + aes_asm_src => "aes_core.c aes_cbc.c aes-sparcv9.S aest4-sparcv9.S aesfx-sparcv9.S", + md5_asm_src => "md5-sparcv9.S", + sha1_asm_src => "sha1-sparcv9.S sha256-sparcv9.S sha512-sparcv9.S", + cmll_asm_src => "camellia.c cmll_misc.c cmll_cbc.c cmllt4-sparcv9.S", + modes_asm_src => "ghash-sparcv9.S", + poly1305_asm_src=> "poly1305-sparcv9.S", + perlasm_scheme => "void" + }, + sparcv8_asm => { + template => 1, + cpuid_asm_src => "", + bn_asm_src => "asm/sparcv8.S", + des_asm_src => "des_enc-sparc.S fcrypt_b.c", + perlasm_scheme => "void" + }, + alpha_asm => { + template => 1, + cpuid_asm_src => "alphacpuid.s", + bn_asm_src => "bn_asm.c alpha-mont.S", + sha1_asm_src => "sha1-alpha.S", + modes_asm_src => "ghash-alpha.S", + perlasm_scheme => "void" + }, + mips32_asm => { + template => 1, + bn_asm_src => "bn-mips.s mips-mont.s", + aes_asm_src => "aes_cbc.c aes-mips.S", + sha1_asm_src => "sha1-mips.S sha256-mips.S", + }, + mips64_asm => { + inherit_from => [ "mips32_asm" ], + template => 1, + sha1_asm_src => add("sha512-mips.S"), + poly1305_asm_src=> "poly1305-mips.S", + }, + s390x_asm => { + template => 1, + cpuid_asm_src => "s390xcap.c s390xcpuid.S", + bn_asm_src => "asm/s390x.S s390x-mont.S s390x-gf2m.s", + aes_asm_src => "aes-s390x.S aes-ctr.fake aes-xts.fake", + sha1_asm_src => "sha1-s390x.S sha256-s390x.S sha512-s390x.S", + rc4_asm_src => "rc4-s390x.s", + modes_asm_src => "ghash-s390x.S", + chacha_asm_src => "chacha-s390x.S", + poly1305_asm_src=> "poly1305-s390x.S", + }, + armv4_asm => { + template => 1, + cpuid_asm_src => "armcap.c armv4cpuid.S", + bn_asm_src => "bn_asm.c armv4-mont.S armv4-gf2m.S", + ec_asm_src => "ecp_nistz256.c ecp_nistz256-armv4.S", + aes_asm_src => "aes_cbc.c aes-armv4.S bsaes-armv7.S aesv8-armx.S", + sha1_asm_src => "sha1-armv4-large.S sha256-armv4.S sha512-armv4.S", + modes_asm_src => "ghash-armv4.S ghashv8-armx.S", + chacha_asm_src => "chacha-armv4.S", + poly1305_asm_src=> "poly1305-armv4.S", + perlasm_scheme => "void" + }, + aarch64_asm => { + template => 1, + cpuid_asm_src => "armcap.c arm64cpuid.S", + ec_asm_src => "ecp_nistz256.c ecp_nistz256-armv8.S", + bn_asm_src => "bn_asm.c armv8-mont.S", + aes_asm_src => "aes_core.c aes_cbc.c aesv8-armx.S vpaes-armv8.S", + sha1_asm_src => "sha1-armv8.S sha256-armv8.S sha512-armv8.S", + modes_asm_src => "ghashv8-armx.S", + chacha_asm_src => "chacha-armv8.S", + poly1305_asm_src=> "poly1305-armv8.S", + }, + parisc11_asm => { + template => 1, + cpuid_asm_src => "pariscid.s", + bn_asm_src => "bn_asm.c parisc-mont.s", + aes_asm_src => "aes_core.c aes_cbc.c aes-parisc.s", + sha1_asm_src => "sha1-parisc.s sha256-parisc.s sha512-parisc.s", + rc4_asm_src => "rc4-parisc.s", + modes_asm_src => "ghash-parisc.s", + perlasm_scheme => "32" + }, + parisc20_64_asm => { + template => 1, + inherit_from => [ "parisc11_asm" ], + perlasm_scheme => "64", + }, + ppc64_asm => { + template => 1, + cpuid_asm_src => "ppccpuid.s ppccap.c", + bn_asm_src => "bn-ppc.s ppc-mont.s ppc64-mont.s", + aes_asm_src => "aes_core.c aes_cbc.c aes-ppc.s vpaes-ppc.s aesp8-ppc.s", + sha1_asm_src => "sha1-ppc.s sha256-ppc.s sha512-ppc.s sha256p8-ppc.s sha512p8-ppc.s", + modes_asm_src => "ghashp8-ppc.s", + chacha_asm_src => "chacha-ppc.s", + poly1305_asm_src=> "poly1305-ppc.s poly1305-ppcfp.s", + }, + ppc32_asm => { + inherit_from => [ "ppc64_asm" ], + template => 1 + }, +); diff --git a/Configurations/10-main.conf b/Configurations/10-main.conf new file mode 100644 index 000000000..becda7902 --- /dev/null +++ b/Configurations/10-main.conf @@ -0,0 +1,1894 @@ +## -*- mode: perl; -*- +## Standard openssl configuration targets. + +# Helper functions for the Windows configs +my $vc_win64a_info = {}; +sub vc_win64a_info { + unless (%$vc_win64a_info) { + if (`nasm -v 2>NUL` =~ /NASM version ([0-9]+\.[0-9]+)/ && $1 >= 2.0) { + $vc_win64a_info = { as => "nasm", + asflags => "-f win64 -DNEAR -Ox -g", + asoutflag => "-o" }; + } elsif ($disabled{asm}) { + $vc_win64a_info = { as => "ml64", + asflags => "/c /Cp /Cx /Zi", + asoutflag => "/Fo" }; + } else { + $die->("NASM not found - please read INSTALL and NOTES.WIN for further details\n"); + $vc_win64a_info = { as => "{unknown}", + asflags => "", + asoutflag => "" }; + } + } + return $vc_win64a_info; +} + +my $vc_win32_info = {}; +sub vc_win32_info { + unless (%$vc_win32_info) { + my $ver=`nasm -v 2>NUL`; + my $vew=`nasmw -v 2>NUL`; + if ($ver ne "" || $vew ne "") { + $vc_win32_info = { as => $ver ge $vew ? "nasm" : "nasmw", + asflags => "-f win32", + asoutflag => "-o", + perlasm_scheme => "win32n" }; + } elsif ($disabled{asm}) { + $vc_win32_info = { as => "ml", + asflags => "/nologo /Cp /coff /c /Cx /Zi", + asoutflag => "/Fo", + perlasm_scheme => "win32" }; + } else { + $die->("NASM not found - please read INSTALL and NOTES.WIN for further details\n"); + $vc_win32_info = { as => "{unknown}", + asflags => "", + asoutflag => "", + perlasm_scheme => "win32" }; + } + } + return $vc_win32_info; +} + +my $vc_wince_info = {}; +sub vc_wince_info { + unless (%$vc_wince_info) { + # sanity check + $die->('%OSVERSION% is not defined') if (!defined($ENV{'OSVERSION'})); + $die->('%PLATFORM% is not defined') if (!defined($ENV{'PLATFORM'})); + $die->('%TARGETCPU% is not defined') if (!defined($ENV{'TARGETCPU'})); + + # + # Idea behind this is to mimic flags set by eVC++ IDE... + # + my $wcevers = $ENV{'OSVERSION'}; # WCENNN + my $wcevernum; + my $wceverdotnum; + if ($wcevers =~ /^WCE([1-9])([0-9]{2})$/) { + $wcevernum = "$1$2"; + $wceverdotnum = "$1.$2"; + } else { + $die->('%OSVERSION% value is insane'); + $wcevernum = "{unknown}"; + $wceverdotnum = "{unknown}"; + } + my $wcecdefs = "-D_WIN32_WCE=$wcevernum -DUNDER_CE=$wcevernum"; # -D_WIN32_WCE=NNN + my $wcelflag = "/subsystem:windowsce,$wceverdotnum"; # ...,N.NN + + my $wceplatf = $ENV{'PLATFORM'}; + + $wceplatf =~ tr/a-z0-9 /A-Z0-9_/; + $wcecdefs .= " -DWCE_PLATFORM_$wceplatf"; + + my $wcetgt = $ENV{'TARGETCPU'}; # just shorter name... + SWITCH: for($wcetgt) { + /^X86/ && do { $wcecdefs.=" -Dx86 -D_X86_ -D_i386_ -Di_386_"; + $wcelflag.=" /machine:X86"; last; }; + /^ARMV4[IT]/ && do { $wcecdefs.=" -DARM -D_ARM_ -D$wcetgt"; + $wcecdefs.=" -DTHUMB -D_THUMB_" if($wcetgt=~/T$/); + $wcecdefs.=" -QRarch4T -QRinterwork-return"; + $wcelflag.=" /machine:THUMB"; last; }; + /^ARM/ && do { $wcecdefs.=" -DARM -D_ARM_ -D$wcetgt"; + $wcelflag.=" /machine:ARM"; last; }; + /^MIPSIV/ && do { $wcecdefs.=" -DMIPS -D_MIPS_ -DR4000 -D$wcetgt"; + $wcecdefs.=" -D_MIPS64 -QMmips4 -QMn32"; + $wcelflag.=" /machine:MIPSFPU"; last; }; + /^MIPS16/ && do { $wcecdefs.=" -DMIPS -D_MIPS_ -DR4000 -D$wcetgt"; + $wcecdefs.=" -DMIPSII -QMmips16"; + $wcelflag.=" /machine:MIPS16"; last; }; + /^MIPSII/ && do { $wcecdefs.=" -DMIPS -D_MIPS_ -DR4000 -D$wcetgt"; + $wcecdefs.=" -QMmips2"; + $wcelflag.=" /machine:MIPS"; last; }; + /^R4[0-9]{3}/ && do { $wcecdefs.=" -DMIPS -D_MIPS_ -DR4000"; + $wcelflag.=" /machine:MIPS"; last; }; + /^SH[0-9]/ && do { $wcecdefs.=" -D$wcetgt -D_${wcetgt}_ -DSHx"; + $wcecdefs.=" -Qsh4" if ($wcetgt =~ /^SH4/); + $wcelflag.=" /machine:$wcetgt"; last; }; + { $wcecdefs.=" -D$wcetgt -D_${wcetgt}_"; + $wcelflag.=" /machine:$wcetgt"; last; }; + } + + $vc_wince_info = { cflags => $wcecdefs, + lflags => $wcelflag }; + } + return $vc_wince_info; +} + +# Helper functions for the VMS configs +my $vms_info = {}; +sub vms_info { + unless (%$vms_info) { + my $pointer_size = shift; + my $pointer_size_str = $pointer_size == 0 ? "" : "$pointer_size"; + + $vms_info->{disable_warns} = [ ]; + $vms_info->{pointer_size} = $pointer_size_str; + if ($pointer_size == 64) { + `PIPE CC /NOCROSS_REFERENCE /NOLIST /NOOBJECT /WARNINGS = DISABLE = ( MAYLOSEDATA3, EMPTYFILE ) NL: 2> NL:`; + if ($? == 0) { + push @{$vms_info->{disable_warns}}, "MAYLOSEDATA3"; + } + } + + unless ($disabled{zlib}) { + my $default_zlib = 'GNV$LIBZSHR' . $pointer_size_str; + if (defined($disabled{"zlib-dynamic"})) { + $vms_info->{zlib} = $withargs{zlib_lib} || "$default_zlib/SHARE"; + } else { + $vms_info->{def_zlib} = $withargs{zlib_lib} || $default_zlib; + # In case the --with-zlib-lib value contains something like + # /SHARE or /LIB or so at the end, remove it. + $vms_info->{def_zlib} =~ s|/.*$||g; + } + } + } + return $vms_info; +} + +%targets = ( + +#### Basic configs that should work on any 32-bit box + "gcc" => { + cc => "gcc", + cflags => picker(debug => "-O0 -g", + release => "-O3"), + thread_scheme => "(unknown)", + bn_ops => "BN_LLONG", + }, + "cc" => { + cc => "cc", + cflags => "-O", + thread_scheme => "(unknown)", + }, + +#### VOS Configurations + "vos-gcc" => { + inherit_from => [ "BASE_unix" ], + cc => "gcc", + cflags => picker(default => "-Wall -DOPENSSL_SYS_VOS -D_POSIX_C_SOURCE=200112L -D_BSD -D_VOS_EXTENDED_NAMES -DB_ENDIAN", + debug => "-O0 -g", + release => "-O3"), + thread_scheme => "(unknown)", + sys_id => "VOS", + lflags => "-Wl,-map", + bn_ops => "BN_LLONG", + shared_extension => ".so", + }, + +#### Solaris configurations + "solaris-common" => { + inherit_from => [ "BASE_unix" ], + template => 1, + cflags => "-DFILIO_H", + ex_libs => add("-lresolv -lsocket -lnsl -ldl"), + dso_scheme => "dlfcn", + thread_scheme => "pthreads", + shared_target => "solaris-shared", + shared_extension => ".so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", + }, +#### Solaris x86 with GNU C setups + "solaris-x86-gcc" => { + # NB. GNU C has to be configured to use GNU assembler, and not + # /usr/ccs/bin/as. Failure to comply will result in compile + # failures [at least] in 32-bit build. + # [Above statement is in direct contradition with one below. + # Latter is kept, because it's formally inappropriate to + # modify compile flags in letter release.] + # -DOPENSSL_NO_INLINE_ASM switches off inline assembler. We have + # to do it here because whenever GNU C instantiates an assembler + # template it surrounds it with #APP #NO_APP comment pair which + # (at least Solaris 7_x86) /usr/ccs/bin/as fails to assemble + # with "Illegal mnemonic" error message. + inherit_from => [ "solaris-common", asm("x86_elf_asm") ], + cc => "gcc", + cflags => add_before(picker(default => "-Wall -DL_ENDIAN -DOPENSSL_NO_INLINE_ASM", + debug => "-O0 -g", + release => "-O3 -fomit-frame-pointer"), + threads("-pthread")), + bn_ops => "BN_LLONG", + shared_cflag => "-fPIC", + shared_ldflag => "-shared -static-libgcc", + }, + "solaris64-x86_64-gcc" => { + # -shared -static-libgcc might appear controversial, but modules + # taken from static libgcc do not have relocations and linking + # them into our shared objects doesn't have any negative side + # effects. On the contrary, doing so makes it possible to use + # gcc shared build with Sun C. Given that gcc generates faster + # code [thanks to inline assembler], I would actually recommend + # to consider using gcc shared build even with vendor compiler:-) + # + inherit_from => [ "solaris-common", asm("x86_64_asm") ], + cc => "gcc", + cflags => add_before(picker(default => "-m64 -Wall -DL_ENDIAN", + debug => "-O0 -g", + release => "-O3"), + threads("-pthread")), + bn_ops => "SIXTY_FOUR_BIT_LONG", + perlasm_scheme => "elf", + shared_cflag => "-fPIC", + shared_ldflag => "-m64 -shared -static-libgcc", + multilib => "/64", + }, + +#### Solaris x86 with Sun C setups + # There used to be solaris-x86-cc target, but it was removed, + # primarily because vendor assembler can't assemble our modules + # with -KPIC flag. As result it, assembly support, was not even + # available as option. But its lack means lack of side-channel + # resistant code, which is incompatible with security by todays + # standards. Fortunately gcc is readily available prepackaged + # option, which we can firmly point at... + # + # On related note, solaris64-x86_64-cc target won't compile code + # paths utilizing AVX and post-Haswell instruction extensions. + # Consider switching to solaris64-x86_64-gcc even here... + # + "solaris64-x86_64-cc" => { + inherit_from => [ "solaris-common", asm("x86_64_asm") ], + cc => "cc", + cflags => add_before(picker(default => "-xarch=generic64 -xstrconst -Xa -DL_ENDIAN", + debug => "-g", + release => "-xO5 -xdepend -xbuiltin"), + threads("-D_REENTRANT")), + thread_scheme => "pthreads", + lflags => add("-xarch=generic64",threads("-mt")), + ex_libs => add(threads("-lpthread")), + bn_ops => "SIXTY_FOUR_BIT_LONG", + perlasm_scheme => "elf", + shared_cflag => "-KPIC", + shared_ldflag => "-xarch=generic64 -G -dy -z text", + multilib => "/64", + }, + +#### SPARC Solaris with GNU C setups + "solaris-sparcv7-gcc" => { + inherit_from => [ "solaris-common" ], + cc => "gcc", + cflags => add_before(picker(default => "-Wall -DB_ENDIAN -DBN_DIV2W", + debug => "-O0 -g", + release => "-O3"), + threads("-pthread")), + bn_ops => "BN_LLONG RC4_CHAR", + shared_cflag => "-fPIC", + shared_ldflag => "-shared", + }, + "solaris-sparcv8-gcc" => { + inherit_from => [ "solaris-sparcv7-gcc", asm("sparcv8_asm") ], + cflags => add_before("-mcpu=v8"), + }, + "solaris-sparcv9-gcc" => { + # -m32 should be safe to add as long as driver recognizes + # -mcpu=ultrasparc + inherit_from => [ "solaris-sparcv7-gcc", asm("sparcv9_asm") ], + cflags => add_before("-m32 -mcpu=ultrasparc"), + }, + "solaris64-sparcv9-gcc" => { + inherit_from => [ "solaris-sparcv9-gcc" ], + cflags => sub { my $f=join(" ",@_); $f =~ s/\-m32/-m64/; $f; }, + bn_ops => "BN_LLONG RC4_CHAR", + shared_ldflag => "-m64 -shared", + multilib => "/64", + }, + +#### SPARC Solaris with Sun C setups +# SC4.0 doesn't pass 'make test', upgrade to SC5.0 or SC4.2. +# SC4.2 is ok, better than gcc even on bn as long as you tell it -xarch=v8 +# SC5.0 note: Compiler common patch 107357-01 or later is required! + "solaris-sparcv7-cc" => { + inherit_from => [ "solaris-common" ], + cc => "cc", + cflags => add_before(picker(default => "-xstrconst -Xa -DB_ENDIAN -DBN_DIV2W", + debug => "-g", + release => "-xO5 -xdepend"), + threads("-D_REENTRANT")), + lflags => add(threads("-mt")), + ex_libs => add(threads("-lpthread")), + bn_ops => "BN_LLONG RC4_CHAR", + shared_cflag => "-KPIC", + shared_ldflag => "-G -dy -z text", + }, +#### + "solaris-sparcv8-cc" => { + inherit_from => [ "solaris-sparcv7-cc", asm("sparcv8_asm") ], + cflags => add_before("-xarch=v8"), + }, + "solaris-sparcv9-cc" => { + inherit_from => [ "solaris-sparcv7-cc", asm("sparcv9_asm") ], + cflags => add_before("-xarch=v8plus"), + }, + "solaris64-sparcv9-cc" => { + inherit_from => [ "solaris-sparcv7-cc", asm("sparcv9_asm") ], + cflags => add_before("-xarch=v9"), + lflags => add_before("-xarch=v9"), + bn_ops => "BN_LLONG RC4_CHAR", + shared_ldflag => "-xarch=v9 -G -dy -z text", + multilib => "/64", + }, + +#### IRIX 6.x configs +# Only N32 and N64 ABIs are supported. + "irix-mips3-gcc" => { + inherit_from => [ "BASE_unix", asm("mips64_asm") ], + cc => "gcc", + cflags => combine(picker(default => "-mabi=n32 -DB_ENDIAN -DBN_DIV3W", + debug => "-g -O0", + release => "-O3"), + threads("-D_SGI_MP_SOURCE")), + ex_libs => add(threads("-lpthread")), + bn_ops => "RC4_CHAR SIXTY_FOUR_BIT", + thread_scheme => "pthreads", + perlasm_scheme => "n32", + dso_scheme => "dlfcn", + shared_target => "irix-shared", + shared_ldflag => "-mabi=n32", + shared_extension => ".so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", + multilib => "32", + }, + "irix-mips3-cc" => { + inherit_from => [ "BASE_unix", asm("mips64_asm") ], + cc => "cc", + cflags => combine(picker(default => "-n32 -mips3 -use_readonly_const -G0 -rdata_shared -DB_ENDIAN -DBN_DIV3W", + debug => "-g -O0", + release => "-O2"), + threads("-D_SGI_MP_SOURCE")), + ex_libs => add(threads("-lpthread")), + bn_ops => "RC4_CHAR SIXTY_FOUR_BIT", + thread_scheme => "pthreads", + perlasm_scheme => "n32", + dso_scheme => "dlfcn", + shared_target => "irix-shared", + shared_ldflag => "-n32", + shared_extension => ".so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", + multilib => "32", + }, + # N64 ABI builds. + "irix64-mips4-gcc" => { + inherit_from => [ "BASE_unix", asm("mips64_asm") ], + cc => "gcc", + cflags => combine(picker(default => "-mabi=64 -mips4 -DB_ENDIAN -DBN_DIV3W", + debug => "-g -O0", + release => "-O3"), + threads("-D_SGI_MP_SOURCE")), + ex_libs => add(threads("-lpthread")), + bn_ops => "RC4_CHAR SIXTY_FOUR_BIT_LONG", + thread_scheme => "pthreads", + perlasm_scheme => "64", + dso_scheme => "dlfcn", + shared_target => "irix-shared", + shared_ldflag => "-mabi=64", + shared_extension => ".so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", + multilib => "64", + }, + "irix64-mips4-cc" => { + inherit_from => [ "BASE_unix", asm("mips64_asm") ], + cc => "cc", + cflags => combine(picker(default => "-64 -mips4 -use_readonly_const -G0 -rdata_shared -DB_ENDIAN -DBN_DIV3W", + debug => "-g -O0", + release => "-O2"), + threads("-D_SGI_MP_SOURCE")), + ex_libs => add(threads("-lpthread")), + bn_ops => "RC4_CHAR SIXTY_FOUR_BIT_LONG", + thread_scheme => "pthreads", + perlasm_scheme => "64", + dso_scheme => "dlfcn", + shared_target => "irix-shared", + shared_ldflag => "-64", + shared_extension => ".so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", + multilib => "64", + }, + +#### Unified HP-UX ANSI C configs. +# Special notes: +# - Originally we were optimizing at +O4 level. It should be noted +# that the only difference between +O3 and +O4 is global inter- +# procedural analysis. As it has to be performed during the link +# stage the compiler leaves behind certain pseudo-code in lib*.a +# which might be release or even patch level specific. Generating +# the machine code for and analyzing the *whole* program appears +# to be *extremely* memory demanding while the performance gain is +# actually questionable. The situation is intensified by the default +# HP-UX data set size limit (infamous 'maxdsiz' tunable) of 64MB +# which is way too low for +O4. In other words, doesn't +O3 make +# more sense? +# - Keep in mind that the HP compiler by default generates code +# suitable for execution on the host you're currently compiling at. +# If the toolkit is meant to be used on various PA-RISC processors +# consider './Configure hpux-parisc-[g]cc +DAportable'. +# - -DMD32_XARRAY triggers workaround for compiler bug we ran into in +# 32-bit message digests. (For the moment of this writing) HP C +# doesn't seem to "digest" too many local variables (they make "him" +# chew forever:-). For more details look-up MD32_XARRAY comment in +# crypto/sha/sha_lcl.h. +# - originally there were 32-bit hpux-parisc2-* targets. They were +# scrapped, because a) they were not interchangeable with other 32-bit +# targets; b) performance-critical 32-bit assembly modules implement +# even PA-RISC 2.0-specific code paths, which are chosen at run-time, +# thus adequate performance is provided even with PA-RISC 1.1 build. +# + "hpux-parisc-gcc" => { + inherit_from => [ "BASE_unix" ], + cc => "gcc", + cflags => combine(picker(default => "-DB_ENDIAN -DBN_DIV2W", + debug => "-O0 -g", + release => "-O3"), + threads("-pthread")), + ex_libs => add("-Wl,+s -ldld"), + bn_ops => "BN_LLONG", + thread_scheme => "pthreads", + dso_scheme => "dl", + shared_target => "hpux-shared", + shared_cflag => "-fPIC", + shared_ldflag => "-shared", + shared_extension => ".sl.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", + }, + "hpux-parisc1_1-gcc" => { + inherit_from => [ "hpux-parisc-gcc", asm("parisc11_asm") ], + multilib => "/pa1.1", + }, + "hpux64-parisc2-gcc" => { + inherit_from => [ "BASE_unix", asm("parisc20_64_asm") ], + cc => "gcc", + cflags => combine(picker(default => "-DB_ENDIAN", + debug => "-O0 -g", + release => "-O3"), + threads("-D_REENTRANT")), + ex_libs => add("-ldl"), + bn_ops => "SIXTY_FOUR_BIT_LONG RC4_CHAR", + thread_scheme => "pthreads", + dso_scheme => "dlfcn", + shared_target => "hpux-shared", + shared_cflag => "-fpic", + shared_ldflag => "-shared", + shared_extension => ".sl.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", + multilib => "/pa20_64", + }, + + # More attempts at unified 10.X and 11.X targets for HP C compiler. + # + # Chris Ruemmler + # Kevin Steves + "hpux-parisc-cc" => { + inherit_from => [ "BASE_unix" ], + cc => "cc", + cflags => combine(picker(default => "+Optrs_strongly_typed -Ae +ESlit -DB_ENDIAN -DBN_DIV2W -DMD32_XARRAY", + debug => "+O0 +d -g", + release => "+O3"), + threads("-D_REENTRANT")), + ex_libs => add("-Wl,+s -ldld",threads("-lpthread")), + bn_ops => "RC4_CHAR", + thread_scheme => "pthreads", + dso_scheme => "dl", + shared_target => "hpux-shared", + shared_cflag => "+Z", + shared_ldflag => "-b", + shared_extension => ".sl.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", + }, + "hpux-parisc1_1-cc" => { + inherit_from => [ "hpux-parisc-cc", asm("parisc11_asm") ], + cflags => add_before("+DA1.1"), + multilib => "/pa1.1", + }, + "hpux64-parisc2-cc" => { + inherit_from => [ "BASE_unix", asm("parisc20_64_asm") ], + cc => "cc", + cflags => combine(picker(default => "+DD64 +Optrs_strongly_typed -Ae +ESlit -DB_ENDIAN -DMD32_XARRAY", + debug => "+O0 +d -g", + release => "+O3"), + threads("-D_REENTRANT")), + ex_libs => add("-ldl",threads("-lpthread")), + bn_ops => "SIXTY_FOUR_BIT_LONG RC4_CHAR", + thread_scheme => "pthreads", + dso_scheme => "dlfcn", + shared_target => "hpux-shared", + shared_cflag => "+Z", + shared_ldflag => "+DD64 -b", + shared_extension => ".sl.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", + multilib => "/pa20_64", + }, + + # HP/UX IA-64 targets + "hpux-ia64-cc" => { + inherit_from => [ "BASE_unix", asm("ia64_asm") ], + cc => "cc", + cflags => combine(picker(default => "-Ae +DD32 +Olit=all -z -DB_ENDIAN", + debug => "+O0 +d -g", + release => "+O2"), + threads("-D_REENTRANT")), + ex_libs => add("-ldl",threads("-lpthread")), + bn_ops => "SIXTY_FOUR_BIT", + thread_scheme => "pthreads", + dso_scheme => "dlfcn", + shared_target => "hpux-shared", + shared_cflag => "+Z", + shared_ldflag => "+DD32 -b", + shared_extension => ".so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", + multilib => "/hpux32", + }, + # Frank Geurts has patiently assisted + # with debugging of the following config. + "hpux64-ia64-cc" => { + inherit_from => [ "BASE_unix", asm("ia64_asm") ], + cc => "cc", + cflags => combine(picker(default => "-Ae +DD64 +Olit=all -z -DB_ENDIAN", + debug => "+O0 +d -g", + release => "+O3"), + threads("-D_REENTRANT")), + ex_libs => add("-ldl", threads("-lpthread")), + bn_ops => "SIXTY_FOUR_BIT_LONG", + thread_scheme => "pthreads", + dso_scheme => "dlfcn", + shared_target => "hpux-shared", + shared_cflag => "+Z", + shared_ldflag => "+DD64 -b", + shared_extension => ".so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", + multilib => "/hpux64", + }, + # GCC builds... + "hpux-ia64-gcc" => { + inherit_from => [ "BASE_unix", asm("ia64_asm") ], + cc => "gcc", + cflags => combine(picker(default => "-DB_ENDIAN", + debug => "-O0 -g", + release => "-O3"), + threads("-pthread")), + ex_libs => add("-ldl"), + bn_ops => "SIXTY_FOUR_BIT", + thread_scheme => "pthreads", + dso_scheme => "dlfcn", + shared_target => "hpux-shared", + shared_cflag => "-fpic", + shared_ldflag => "-shared", + shared_extension => ".so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", + multilib => "/hpux32", + }, + "hpux64-ia64-gcc" => { + inherit_from => [ "BASE_unix", asm("ia64_asm") ], + cc => "gcc", + cflags => combine(picker(default => "-mlp64 -DB_ENDIAN", + debug => "-O0 -g", + release => "-O3"), + threads("-pthread")), + ex_libs => add("-ldl"), + bn_ops => "SIXTY_FOUR_BIT_LONG", + thread_scheme => "pthreads", + dso_scheme => "dlfcn", + shared_target => "hpux-shared", + shared_cflag => "-fpic", + shared_ldflag => "-mlp64 -shared", + shared_extension => ".so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", + multilib => "/hpux64", + }, + +#### HP MPE/iX http://jazz.external.hp.com/src/openssl/ + "MPE/iX-gcc" => { + inherit_from => [ "BASE_unix" ], + cc => "gcc", + cflags => "-D_ENDIAN -DBN_DIV2W -O3 -D_POSIX_SOURCE -D_SOCKET_SOURCE -I/SYSLOG/PUB", + sys_id => "MPE", + ex_libs => add("-L/SYSLOG/PUB -lsyslog -lsocket -lcurses"), + thread_scheme => "(unknown)", + bn_ops => "BN_LLONG", + }, + +#### DEC Alpha Tru64 targets. Tru64 is marketing name for OSF/1 version 4 +#### and forward. In reality 'uname -s' still returns "OSF1". Originally +#### there were even osf1-* configs targeting prior versions provided, +#### but not anymore... + "tru64-alpha-gcc" => { + inherit_from => [ "BASE_unix", asm("alpha_asm") ], + cc => "gcc", + cflags => combine("-std=c9x -D_XOPEN_SOURCE=500 -D_OSF_SOURCE -O3", + threads("-pthread")), + ex_libs => "-lrt", # for mlock(2) + bn_ops => "SIXTY_FOUR_BIT_LONG", + thread_scheme => "pthreads", + dso_scheme => "dlfcn", + shared_target => "alpha-osf1-shared", + shared_extension => ".so", + }, + "tru64-alpha-cc" => { + inherit_from => [ "BASE_unix", asm("alpha_asm") ], + cc => "cc", + cflags => combine("-std1 -D_XOPEN_SOURCE=500 -D_OSF_SOURCE -tune host -fast -readonly_strings", + threads("-pthread")), + ex_libs => "-lrt", # for mlock(2) + bn_ops => "SIXTY_FOUR_BIT_LONG", + thread_scheme => "pthreads", + dso_scheme => "dlfcn", + shared_target => "alpha-osf1-shared", + shared_ldflag => "-msym", + shared_extension => ".so", + }, + +#### +#### Variety of LINUX:-) +#### +# *-generic* is endian-neutral target, but ./config is free to +# throw in -D[BL]_ENDIAN, whichever appropriate... + "linux-generic32" => { + inherit_from => [ "BASE_unix" ], + cc => "gcc", + cflags => combine(picker(default => "-Wall", + debug => "-O0 -g", + release => "-O3"), + threads("-pthread")), + ex_libs => add("-ldl"), + bn_ops => "BN_LLONG RC4_CHAR", + thread_scheme => "pthreads", + dso_scheme => "dlfcn", + shared_target => "linux-shared", + shared_cflag => "-fPIC -DOPENSSL_USE_NODELETE", + shared_ldflag => "-Wl,-znodelete", + shared_extension => ".so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", + }, + "linux-generic64" => { + inherit_from => [ "linux-generic32" ], + bn_ops => "SIXTY_FOUR_BIT_LONG RC4_CHAR", + }, + + "linux-ppc" => { + inherit_from => [ "linux-generic32", asm("ppc32_asm") ], + perlasm_scheme => "linux32", + }, + "linux-ppc64" => { + inherit_from => [ "linux-generic64", asm("ppc64_asm") ], + cflags => add("-m64 -DB_ENDIAN"), + perlasm_scheme => "linux64", + shared_ldflag => add("-m64"), + multilib => "64", + }, + "linux-ppc64le" => { + inherit_from => [ "linux-generic64", asm("ppc64_asm") ], + cflags => add("-m64 -DL_ENDIAN"), + perlasm_scheme => "linux64le", + shared_ldflag => add("-m64"), + }, + + "linux-armv4" => { + ################################################################ + # Note that -march is not among compiler options in linux-armv4 + # target description. Not specifying one is intentional to give + # you choice to: + # + # a) rely on your compiler default by not specifying one; + # b) specify your target platform explicitly for optimal + # performance, e.g. -march=armv6 or -march=armv7-a; + # c) build "universal" binary that targets *range* of platforms + # by specifying minimum and maximum supported architecture; + # + # As for c) option. It actually makes no sense to specify + # maximum to be less than ARMv7, because it's the least + # requirement for run-time switch between platform-specific + # code paths. And without run-time switch performance would be + # equivalent to one for minimum. Secondly, there are some + # natural limitations that you'd have to accept and respect. + # Most notably you can *not* build "universal" binary for + # big-endian platform. This is because ARMv7 processor always + # picks instructions in little-endian order. Another similar + # limitation is that -mthumb can't "cross" -march=armv6t2 + # boundary, because that's where it became Thumb-2. Well, this + # limitation is a bit artificial, because it's not really + # impossible, but it's deemed too tricky to support. And of + # course you have to be sure that your binutils are actually + # up to the task of handling maximum target platform. With all + # this in mind here is an example of how to configure + # "universal" build: + # + # ./Configure linux-armv4 -march=armv6 -D__ARM_MAX_ARCH__=8 + # + inherit_from => [ "linux-generic32", asm("armv4_asm") ], + perlasm_scheme => "linux32", + }, + "linux-aarch64" => { + inherit_from => [ "linux-generic64", asm("aarch64_asm") ], + perlasm_scheme => "linux64", + }, + "linux-arm64ilp32" => { # https://wiki.linaro.org/Platform/arm64-ilp32 + inherit_from => [ "linux-generic32", asm("aarch64_asm") ], + cflags => add("-mabi=ilp32"), + bn_ops => "SIXTY_FOUR_BIT RC4_CHAR", + perlasm_scheme => "linux64", + shared_ldflag => add("-mabi=ilp32"), + }, + + "linux-mips32" => { + # Configure script adds minimally required -march for assembly + # support, if no -march was specified at command line. + inherit_from => [ "linux-generic32", asm("mips32_asm") ], + cflags => add("-mabi=32 -DBN_DIV3W"), + perlasm_scheme => "o32", + shared_ldflag => add("-mabi=32"), + }, + # mips32 and mips64 below refer to contemporary MIPS Architecture + # specifications, MIPS32 and MIPS64, rather than to kernel bitness. + "linux-mips64" => { + inherit_from => [ "linux-generic32", asm("mips64_asm") ], + cflags => add("-mabi=n32 -DBN_DIV3W"), + bn_ops => "SIXTY_FOUR_BIT RC4_CHAR", + perlasm_scheme => "n32", + shared_ldflag => add("-mabi=n32"), + multilib => "32", + }, + "linux64-mips64" => { + inherit_from => [ "linux-generic64", asm("mips64_asm") ], + cflags => add("-mabi=64 -DBN_DIV3W"), + perlasm_scheme => "64", + shared_ldflag => add("-mabi=64"), + multilib => "64", + }, + + #### IA-32 targets... + #### These two targets are a bit aged and are to be used on older Linux + #### machines where gcc doesn't understand -m32 and -m64 + "linux-elf" => { + inherit_from => [ "linux-generic32", asm("x86_elf_asm") ], + cflags => add(picker(default => "-DL_ENDIAN", + release => "-fomit-frame-pointer")), + bn_ops => "BN_LLONG", + }, + "linux-aout" => { + inherit_from => [ "BASE_unix", asm("x86_asm") ], + cc => "gcc", + cflags => add(picker(default => "-DL_ENDIAN -Wall", + debug => "-O0 -g", + release => "-O3 -fomit-frame-pointer")), + bn_ops => "BN_LLONG", + thread_scheme => "(unknown)", + perlasm_scheme => "a.out", + }, + + #### X86 / X86_64 targets + "linux-x86" => { + inherit_from => [ "linux-generic32", asm("x86_asm") ], + cflags => add(picker(default => "-m32 -DL_ENDIAN", + release => "-fomit-frame-pointer")), + bn_ops => "BN_LLONG", + perlasm_scheme => "elf", + shared_ldflag => add("-m32"), + }, + "linux-x86-clang" => { + inherit_from => [ "linux-x86" ], + cc => "clang", + cxx => "clang++", + cflags => add("-Wextra -Qunused-arguments"), + }, + "linux-x86_64" => { + inherit_from => [ "linux-generic64", asm("x86_64_asm") ], + cflags => add("-m64 -DL_ENDIAN"), + bn_ops => "SIXTY_FOUR_BIT_LONG", + perlasm_scheme => "elf", + shared_ldflag => add("-m64"), + multilib => "64", + }, + "linux-x86_64-clang" => { + inherit_from => [ "linux-x86_64" ], + cc => "clang", + cflags => add("-Wextra -Qunused-arguments"), + }, + "linux-x32" => { + inherit_from => [ "linux-generic32", asm("x86_64_asm") ], + cflags => add("-mx32 -DL_ENDIAN"), + bn_ops => "SIXTY_FOUR_BIT", + perlasm_scheme => "elf32", + shared_ldflag => add("-mx32"), + multilib => "x32", + }, + + "linux-ia64" => { + inherit_from => [ "linux-generic64", asm("ia64_asm") ], + bn_ops => "SIXTY_FOUR_BIT_LONG", + }, + + "linux64-s390x" => { + inherit_from => [ "linux-generic64", asm("s390x_asm") ], + cflags => add("-m64 -DB_ENDIAN"), + perlasm_scheme => "64", + shared_ldflag => add("-m64"), + multilib => "64", + }, + "linux32-s390x" => { + #### So called "highgprs" target for z/Architecture CPUs + # "Highgprs" is kernel feature first implemented in Linux + # 2.6.32, see /proc/cpuinfo. The idea is to preserve most + # significant bits of general purpose registers not only + # upon 32-bit process context switch, but even on + # asynchronous signal delivery to such process. This makes + # it possible to deploy 64-bit instructions even in legacy + # application context and achieve better [or should we say + # adequate] performance. The build is binary compatible with + # linux-generic32, and the idea is to be able to install the + # resulting libcrypto.so alongside generic one, e.g. as + # /lib/highgprs/libcrypto.so.x.y, for ldconfig and run-time + # linker to autodiscover. Unfortunately it doesn't work just + # yet, because of couple of bugs in glibc + # sysdeps/s390/dl-procinfo.c affecting ldconfig and ld.so.1... + # + inherit_from => [ "linux-generic32", asm("s390x_asm") ], + cflags => add("-m31 -Wa,-mzarch -DB_ENDIAN"), + bn_asm_src => sub { my $r=join(" ",@_); $r=~s|asm/s390x\.S|bn_asm.c|; $r; }, + perlasm_scheme => "31", + shared_ldflag => add("-m31"), + multilib => "/highgprs", + }, + + #### SPARC Linux setups + # Ray Miller has + # patiently assisted with debugging of following two configs. + "linux-sparcv8" => { + inherit_from => [ "linux-generic32", asm("sparcv8_asm") ], + cflags => add("-mcpu=v8 -DB_ENDIAN -DBN_DIV2W"), + }, + "linux-sparcv9" => { + # it's a real mess with -mcpu=ultrasparc option under Linux, + # but -Wa,-Av8plus should do the trick no matter what. + inherit_from => [ "linux-generic32", asm("sparcv9_asm") ], + cflags => add("-m32 -mcpu=ultrasparc -Wa,-Av8plus -DB_ENDIAN -DBN_DIV2W"), + shared_ldflag => add("-m32"), + }, + "linux64-sparcv9" => { + # GCC 3.1 is a requirement + inherit_from => [ "linux-generic64", asm("sparcv9_asm") ], + cflags => add("-m64 -mcpu=ultrasparc -DB_ENDIAN"), + bn_ops => "BN_LLONG RC4_CHAR", + shared_ldflag => add("-m64"), + multilib => "64", + }, + + "linux-alpha-gcc" => { + inherit_from => [ "linux-generic64", asm("alpha_asm") ], + cflags => add("-DL_ENDIAN"), + bn_ops => "SIXTY_FOUR_BIT_LONG", + }, + "linux-c64xplus" => { + inherit_from => [ "BASE_unix" ], + # TI_CGT_C6000_7.3.x is a requirement + cc => "cl6x", + cflags => combine("--linux -ea=.s -eo=.o -mv6400+ -o2 -ox -ms -pden -DOPENSSL_SMALL_FOOTPRINT", + threads("-D_REENTRANT")), + bn_ops => "BN_LLONG", + cpuid_asm_src => "c64xpluscpuid.s", + bn_asm_src => "asm/bn-c64xplus.asm c64xplus-gf2m.s", + aes_asm_src => "aes-c64xplus.s aes_cbc.c aes-ctr.fake", + sha1_asm_src => "sha1-c64xplus.s sha256-c64xplus.s sha512-c64xplus.s", + rc4_asm_src => "rc4-c64xplus.s", + modes_asm_src => "ghash-c64xplus.s", + chacha_asm_src => "chacha-c64xplus.s", + poly1305_asm_src => "poly1305-c64xplus.s", + thread_scheme => "pthreads", + perlasm_scheme => "void", + dso_scheme => "dlfcn", + shared_target => "linux-shared", + shared_cflag => "--pic", + shared_ldflag => add("-z --sysv --shared"), + shared_extension => ".so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", + ranlib => "true", + }, + +#### Android: linux-* but without pointers to headers and libs. + # + # It takes pair of prior-set environment variables to make it work: + # + # CROSS_SYSROOT=/some/where/android-ndk-/platforms/android-/arch- + # CROSS_COMPILE= + # + # As well as PATH adjusted to cover ${CROSS_COMPILE}gcc and company. + # For example to compile for ICS and ARM with NDK 10d, you'd: + # + # ANDROID_NDK=/some/where/android-ndk-10d + # CROSS_SYSROOT=$ANDROID_NDK/platforms/android-14/arch-arm + # CROSS_COMPILE=arm-linux-adroideabi- + # PATH=$ANDROID_NDK/toolchains/arm-linux-androideabi-4.8/prebuild/linux-x86_64/bin + # + "android" => { + inherit_from => [ "linux-generic32" ], + # Special note about unconditional -fPIC and -pie. The underlying + # reason is that Lollipop refuses to run non-PIE. But what about + # older systems and NDKs? -fPIC was never problem, so the only + # concern is -pie. Older toolchains, e.g. r4, appear to handle it + # and binaries turn mostly functional. "Mostly" means that oldest + # Androids, such as Froyo, fail to handle executable, but newer + # systems are perfectly capable of executing binaries targeting + # Froyo. Keep in mind that in the nutshell Android builds are + # about JNI, i.e. shared libraries, not applications. + cflags => add(picker(default => "-fPIC --sysroot=\$(CROSS_SYSROOT) -Wa,--noexecstack")), + bin_cflags => "-pie", + }, + "android-x86" => { + inherit_from => [ "android", asm("x86_asm") ], + cflags => add(picker(release => "-fomit-frame-pointer")), + bn_ops => "BN_LLONG", + perlasm_scheme => "android", + }, + ################################################################ + # Contemporary Android applications can provide multiple JNI + # providers in .apk, targeting multiple architectures. Among + # them there is "place" for two ARM flavours: generic eabi and + # armv7-a/hard-float. However, it should be noted that OpenSSL's + # ability to engage NEON is not constrained by ABI choice, nor + # is your ability to call OpenSSL from your application code + # compiled with floating-point ABI other than default 'soft'. + # [Latter thanks to __attribute__((pcs("aapcs"))) declaration.] + # This means that choice of ARM libraries you provide in .apk + # is driven by application needs. For example if application + # itself benefits from NEON or is floating-point intensive, then + # it might be appropriate to provide both libraries. Otherwise + # just generic eabi would do. But in latter case it would be + # appropriate to + # + # ./Configure android-armeabi -D__ARM_MAX_ARCH__=8 + # + # in order to build "universal" binary and allow OpenSSL take + # advantage of NEON when it's available. + # + "android-armeabi" => { + inherit_from => [ "android", asm("armv4_asm") ], + }, + "android-mips" => { + inherit_from => [ "android", asm("mips32_asm") ], + perlasm_scheme => "o32", + }, + + "android64" => { + inherit_from => [ "linux-generic64" ], + cflags => add(picker(default => "-fPIC --sysroot=\$(CROSS_SYSROOT) -Wa,--noexecstack")), + bin_cflags => "-pie", + }, + "android64-aarch64" => { + inherit_from => [ "android64", asm("aarch64_asm") ], + perlasm_scheme => "linux64", + }, + +#### *BSD + "BSD-generic32" => { + # As for thread cflag. Idea is to maintain "collective" set of + # flags, which would cover all BSD flavors. -pthread applies + # to them all, but is treated differently. OpenBSD expands is + # as -D_POSIX_THREAD -lc_r, which is sufficient. FreeBSD 4.x + # expands it as -lc_r, which has to be accompanied by explicit + # -D_THREAD_SAFE and sometimes -D_REENTRANT. FreeBSD 5.x + # expands it as -lc_r, which seems to be sufficient? + inherit_from => [ "BASE_unix" ], + cc => "cc", + cflags => combine(picker(default => "-Wall", + debug => "-O0 -g", + release => "-O3"), + threads("-pthread -D_THREAD_SAFE -D_REENTRANT")), + bn_ops => "BN_LLONG", + thread_scheme => "pthreads", + dso_scheme => "dlfcn", + shared_target => "bsd-gcc-shared", + shared_cflag => "-fPIC", + shared_extension => ".so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", + }, + "BSD-generic64" => { + inherit_from => [ "BSD-generic32" ], + bn_ops => "SIXTY_FOUR_BIT_LONG", + }, + + "BSD-x86" => { + inherit_from => [ "BSD-generic32", asm("x86_asm") ], + cflags => add(picker(default => "-DL_ENDIAN", + release => "-fomit-frame-pointer")), + bn_ops => "BN_LLONG", + shared_target => "bsd-shared", + perlasm_scheme => "a.out", + }, + "BSD-x86-elf" => { + inherit_from => [ "BSD-x86" ], + perlasm_scheme => "elf", + }, + + "BSD-sparcv8" => { + inherit_from => [ "BSD-generic32", asm("sparcv8_asm") ], + cflags => add("-mcpu=v8 -DB_ENDIAN"), + }, + "BSD-sparc64" => { + # -DMD32_REG_T=int doesn't actually belong in sparc64 target, it + # simply *happens* to work around a compiler bug in gcc 3.3.3, + # triggered by RIPEMD160 code. + inherit_from => [ "BSD-generic64", asm("sparcv9_asm") ], + cflags => add("-DB_ENDIAN -DMD32_REG_T=int"), + bn_ops => "BN_LLONG", + }, + + "BSD-ia64" => { + inherit_from => [ "BSD-generic64", asm("ia64_asm") ], + cflags => add_before("-DL_ENDIAN"), + bn_ops => "SIXTY_FOUR_BIT_LONG", + }, + + "BSD-x86_64" => { + inherit_from => [ "BSD-generic64", asm("x86_64_asm") ], + cflags => add_before("-DL_ENDIAN"), + bn_ops => "SIXTY_FOUR_BIT_LONG", + perlasm_scheme => "elf", + }, + + "bsdi-elf-gcc" => { + inherit_from => [ "BASE_unix", asm("x86_elf_asm") ], + cc => "gcc", + cflags => "-DPERL5 -DL_ENDIAN -fomit-frame-pointer -O3 -Wall", + ex_libs => add("-ldl"), + bn_ops => "BN_LLONG", + thread_scheme => "(unknown)", + dso_scheme => "dlfcn", + shared_target => "bsd-gcc-shared", + shared_cflag => "-fPIC", + shared_extension => ".so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", + }, + + "nextstep" => { + inherit_from => [ "BASE_unix" ], + cc => "cc", + cflags => "-O -Wall", + unistd => "", + bn_ops => "BN_LLONG", + thread_scheme => "(unknown)", + }, + "nextstep3.3" => { + inherit_from => [ "BASE_unix" ], + cc => "cc", + cflags => "-O3 -Wall", + unistd => "", + bn_ops => "BN_LLONG", + thread_scheme => "(unknown)", + }, + +# QNX + "qnx4" => { + inherit_from => [ "BASE_unix" ], + cc => "cc", + cflags => "-DL_ENDIAN -DTERMIO", + thread_scheme => "(unknown)", + }, + "QNX6" => { + inherit_from => [ "BASE_unix" ], + cc => "gcc", + ex_libs => add("-lsocket"), + dso_scheme => "dlfcn", + shared_target => "bsd-gcc-shared", + shared_cflag => "-fPIC", + shared_extension => ".so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", + }, + "QNX6-i386" => { + inherit_from => [ "BASE_unix", asm("x86_elf_asm") ], + cc => "gcc", + cflags => "-DL_ENDIAN -O2 -Wall", + ex_libs => add("-lsocket"), + dso_scheme => "dlfcn", + shared_target => "bsd-gcc-shared", + shared_cflag => "-fPIC", + shared_extension => ".so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", + }, + +#### SCO/Caldera targets. +# +# Originally we had like unixware-*, unixware-*-pentium, unixware-*-p6, etc. +# Now we only have blended unixware-* as it's the only one used by ./config. +# If you want to optimize for particular microarchitecture, bypass ./config +# and './Configure unixware-7 -Kpentium_pro' or whatever appropriate. +# Note that not all targets include assembler support. Mostly because of +# lack of motivation to support out-of-date platforms with out-of-date +# compiler drivers and assemblers. Tim Rice has +# patiently assisted to debug most of it. +# +# UnixWare 2.0x fails destest with -O. + "unixware-2.0" => { + inherit_from => [ "BASE_unix" ], + cc => "cc", + cflags => combine("-DFILIO_H -DNO_STRINGS_H", + threads("-Kthread")), + ex_libs => add("-lsocket -lnsl -lresolv -lx"), + thread_scheme => "uithreads", + }, + "unixware-2.1" => { + inherit_from => [ "BASE_unix" ], + cc => "cc", + cflags => combine("-O -DFILIO_H", + threads("-Kthread")), + ex_libs => add("-lsocket -lnsl -lresolv -lx"), + thread_scheme => "uithreads", + }, + "unixware-7" => { + inherit_from => [ "BASE_unix", asm("x86_elf_asm") ], + cc => "cc", + cflags => combine("-O -DFILIO_H -Kalloca", + threads("-Kthread")), + ex_libs => add("-lsocket -lnsl"), + thread_scheme => "uithreads", + bn_ops => "BN_LLONG", + perlasm_scheme => "elf-1", + dso_scheme => "dlfcn", + shared_target => "svr5-shared", + shared_cflag => "-Kpic", + shared_extension => ".so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", + }, + "unixware-7-gcc" => { + inherit_from => [ "BASE_unix", asm("x86_elf_asm") ], + cc => "gcc", + cflags => combine("-DL_ENDIAN -DFILIO_H -O3 -fomit-frame-pointer -Wall", + threads("-D_REENTRANT")), + ex_libs => add("-lsocket -lnsl"), + bn_ops => "BN_LLONG", + thread_scheme => "pthreads", + perlasm_scheme => "elf-1", + dso_scheme => "dlfcn", + shared_target => "gnu-shared", + shared_cflag => "-fPIC", + shared_extension => ".so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", + }, +# SCO 5 - Ben Laurie says the -O breaks the SCO cc. + "sco5-cc" => { + inherit_from => [ "BASE_unix", asm("x86_elf_asm") ], + cc => "cc", + cflags => "-belf", + ex_libs => add("-lsocket -lnsl"), + thread_scheme => "(unknown)", + perlasm_scheme => "elf-1", + dso_scheme => "dlfcn", + shared_target => "svr3-shared", + shared_cflag => "-Kpic", + shared_extension => ".so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", + }, + "sco5-gcc" => { + inherit_from => [ "BASE_unix", asm("x86_elf_asm") ], + cc => "gcc", + cflags => "-O3 -fomit-frame-pointer", + ex_libs => add("-lsocket -lnsl"), + bn_ops => "BN_LLONG", + thread_scheme => "(unknown)", + perlasm_scheme => "elf-1", + dso_scheme => "dlfcn", + shared_target => "svr3-shared", + shared_cflag => "-fPIC", + shared_extension => ".so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", + }, + +#### IBM's AIX. + # Below targets assume AIX >=5. Caveat lector. If you are accustomed + # to control compilation "bitness" by setting $OBJECT_MODE environment + # variable, then you should know that in OpenSSL case it's considered + # only in ./config. Once configured, build procedure remains "deaf" to + # current value of $OBJECT_MODE. + "aix-gcc" => { + inherit_from => [ "BASE_unix", asm("ppc32_asm") ], + cc => "gcc", + cflags => combine(picker(default => "-DB_ENDIAN", + debug => "-O0 -g", + release => "-O"), + threads("-pthread")), + sys_id => "AIX", + bn_ops => "BN_LLONG RC4_CHAR", + thread_scheme => "pthreads", + perlasm_scheme => "aix32", + dso_scheme => "dlfcn", + shared_target => "aix-shared", + shared_ldflag => "-shared -static-libgcc -Wl,-G", + shared_extension => ".so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", + arflags => "-X32", + }, + "aix64-gcc" => { + inherit_from => [ "BASE_unix", asm("ppc64_asm") ], + cc => "gcc", + cflags => combine(picker(default => "-maix64 -DB_ENDIAN", + debug => "-O0 -g", + release => "-O"), + threads("-pthread")), + sys_id => "AIX", + bn_ops => "SIXTY_FOUR_BIT_LONG RC4_CHAR", + thread_scheme => "pthreads", + perlasm_scheme => "aix64", + dso_scheme => "dlfcn", + shared_target => "aix-shared", + shared_ldflag => "-maix64 -shared -static-libgcc -Wl,-G", + shared_extension => ".so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", + arflags => "-X64", + }, + "aix-cc" => { + inherit_from => [ "BASE_unix", asm("ppc32_asm") ], + cc => "cc", + cflags => combine(picker(default => "-q32 -DB_ENDIAN -qmaxmem=16384 -qro -qroconst", + debug => "-O0 -g", + release => "-O"), + threads("-qthreaded -D_THREAD_SAFE")), + sys_id => "AIX", + bn_ops => "BN_LLONG RC4_CHAR", + thread_scheme => "pthreads", + ex_libs => threads("-lpthreads"), + perlasm_scheme => "aix32", + dso_scheme => "dlfcn", + shared_target => "aix-shared", + shared_ldflag => "-q32 -G", + shared_extension => ".so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", + arflags => "-X 32", + }, + "aix64-cc" => { + inherit_from => [ "BASE_unix", asm("ppc64_asm") ], + cc => "cc", + cflags => combine(picker(default => "-q64 -DB_ENDIAN -qmaxmem=16384 -qro -qroconst", + debug => "-O0 -g", + release => "-O"), + threads("-qthreaded -D_THREAD_SAFE")), + sys_id => "AIX", + bn_ops => "SIXTY_FOUR_BIT_LONG RC4_CHAR", + thread_scheme => "pthreads", + ex_libs => threads("-lpthreads"), + perlasm_scheme => "aix64", + dso_scheme => "dlfcn", + shared_target => "aix-shared", + shared_ldflag => "-q64 -G", + shared_extension => ".so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", + arflags => "-X 64", + }, + +# SIEMENS BS2000/OSD: an EBCDIC-based mainframe + "BS2000-OSD" => { + inherit_from => [ "BASE_unix" ], + cc => "c89", + cflags => "-O -XLLML -XLLMK -XL -DB_ENDIAN -DCHARSET_EBCDIC", + ex_libs => add("-lsocket -lnsl"), + bn_ops => "THIRTY_TWO_BIT RC4_CHAR", + thread_scheme => "(unknown)", + }, + +# OS/390 Unix an EBCDIC-based Unix system on IBM mainframe +# You need to compile using the c89.sh wrapper in the tools directory, because the +# IBM compiler does not like the -L switch after any object modules. +# + "OS390-Unix" => { + inherit_from => [ "BASE_unix" ], + cc => "c89.sh", + cflags => "-O -DB_ENDIAN -DCHARSET_EBCDIC -DNO_SYS_PARAM_H -D_ALL_SOURCE", + bn_ops => "THIRTY_TWO_BIT RC4_CHAR", + thread_scheme => "(unknown)", + }, + +#### Visual C targets +# +# Win64 targets, WIN64I denotes IA-64 and WIN64A - AMD64 +# +# Note about -wd4090, disable warning C4090. This warning returns false +# positives in some situations. Disabling it altogether masks both +# legitimate and false cases, but as we compile on multiple platforms, +# we rely on other compilers to catch legitimate cases. +# +# Also note that we force threads no matter what. Configuring "no-threads" +# is ignored. + "VC-common" => { + inherit_from => [ "BASE_Windows" ], + template => 1, + cc => "cl", + cflags => "-W3 -wd4090 -Gs0 -GF -Gy -nologo -DOPENSSL_SYS_WIN32 -DWIN32_LEAN_AND_MEAN -DL_ENDIAN -D_CRT_SECURE_NO_DEPRECATE", + defines => add(sub { my @defs = (); + unless ($disabled{"zlib-dynamic"}) { + my $zlib = + $withargs{zlib_lib} // "ZLIB1"; + push @defs, + quotify("perl", + 'LIBZ="' . $zlib . '"'); + } + return [ @defs ]; + }), + coutflag => "/Fo", + lib_cflags => add("/Zi /Fdossl_static"), + dso_cflags => "/Zi /Fddso", + bin_cflags => "/Zi /Fdapp", + lflags => add("/debug"), + shared_ldflag => "/dll", + shared_target => "win-shared", # meaningless except it gives Configure a hint + thread_scheme => "winthreads", + dso_scheme => "win32", + apps_aux_src => add("win32_init.c"), + }, + "VC-noCE-common" => { + inherit_from => [ "VC-common" ], + template => 1, + cflags => add(picker(default => "-DUNICODE -D_UNICODE", + debug => + sub { + ($disabled{shared} ? "" : "/MDd") + ." /Od -DDEBUG -D_DEBUG"; + }, + release => + sub { + ($disabled{shared} ? "" : "/MD") + ." /O2"; + })), + lib_cflags => add(sub { $disabled{shared} ? "/MT /Zl" : () }), + # Following might/should appears controversial, i.e. defining + # /MDd without evaluating $disabled{shared}. It works in + # non-shared build because static library is compiled with /Zl + # and bares no reference to specific RTL. And it works in + # shared build because multiple /MDd options are not prohibited. + # But why /MDd in static build? Well, basically this is just a + # reference point, which allows to catch eventual errors that + # would prevent those who want to wrap OpenSSL into own .DLL. + # Why not /MD in release build then? Well, some are likely to + # prefer [non-debug] openssl.exe to be free from Micorosoft RTL + # redistributable. + bin_cflags => add(picker(debug => "/MDd", + release => sub { $disabled{shared} ? "/MT" : () }, + )), + bin_lflags => add("/subsystem:console /opt:ref"), + ex_libs => add(sub { + my @ex_libs = (); + push @ex_libs, 'ws2_32.lib' unless $disabled{sock}; + push @ex_libs, 'gdi32.lib advapi32.lib crypt32.lib user32.lib'; + return join(" ", @ex_libs); + }), + }, + "VC-WIN64-common" => { + inherit_from => [ "VC-noCE-common" ], + template => 1, + ex_libs => add(sub { + my @ex_libs = (); + push @ex_libs, 'bufferoverflowu.lib' if (`cl 2>&1` =~ /14\.00\.4[0-9]{4}\./); + return join(" ", @_, @ex_libs); + }), + bn_ops => "SIXTY_FOUR_BIT EXPORT_VAR_AS_FN", + build_scheme => add("VC-W64", { separator => undef }), + }, + "VC-WIN64I" => { + inherit_from => [ "VC-WIN64-common", asm("ia64_asm"), + sub { $disabled{shared} ? () : "ia64_uplink" } ], + as => "ias", + asflags => "-d debug", + asoutflag => "-o", + sys_id => "WIN64I", + bn_asm_src => sub { return undef unless @_; + my $r=join(" ",@_); $r=~s|bn-ia64.s|bn_asm.c|; $r; }, + perlasm_scheme => "ias", + multilib => "-ia64", + }, + "VC-WIN64A" => { + inherit_from => [ "VC-WIN64-common", asm("x86_64_asm"), + sub { $disabled{shared} ? () : "x86_64_uplink" } ], + as => sub { vc_win64a_info()->{as} }, + asflags => sub { vc_win64a_info()->{asflags} }, + asoutflag => sub { vc_win64a_info()->{asoutflag} }, + sys_id => "WIN64A", + bn_asm_src => sub { return undef unless @_; + my $r=join(" ",@_); $r=~s|asm/x86_64-gcc|bn_asm|; $r; }, + perlasm_scheme => "auto", + multilib => "-x64", + }, + "VC-WIN32" => { + # x86 Win32 target defaults to ANSI API, if you want UNICODE, + # configure with 'perl Configure VC-WIN32 -DUNICODE -D_UNICODE' + inherit_from => [ "VC-noCE-common", asm("x86_asm"), + sub { $disabled{shared} ? () : "uplink_common" } ], + as => sub { vc_win32_info()->{as} }, + asflags => sub { vc_win32_info()->{asflags} }, + asoutflag => sub { vc_win32_info()->{asoutflag} }, + ex_libs => add(sub { + my @ex_libs = (); + # WIN32 UNICODE build gets linked with unicows.lib for + # backward compatibility with Win9x. + push @ex_libs, 'unicows.lib' + if (grep { $_ eq "UNICODE" } @user_defines); + return join(" ", @ex_libs, @_); + }), + sys_id => "WIN32", + bn_ops => "BN_LLONG EXPORT_VAR_AS_FN", + perlasm_scheme => sub { vc_win32_info()->{perlasm_scheme} }, + build_scheme => add("VC-W32", { separator => undef }), + }, + "VC-CE" => { + inherit_from => [ "VC-common" ], + as => "ml", + asflags => "/nologo /Cp /coff /c /Cx /Zi", + asoutflag => "/Fo", + cc => "cl", + cflags => + picker(default => + combine('/W3 /WX /GF /Gy /nologo -DUNICODE -D_UNICODE -DOPENSSL_SYS_WINCE -DWIN32_LEAN_AND_MEAN -DL_ENDIAN -DDSO_WIN32 -DNO_CHMOD -DOPENSSL_SMALL_FOOTPRINT', + sub { vc_wince_info()->{cflags}; }, + sub { defined($ENV{'WCECOMPAT'}) + ? '-I$(WCECOMPAT)/include' : (); }, + sub { defined($ENV{'PORTSDK_LIBPATH'}) + ? '-I$(PORTSDK_LIBPATH)/../../include' : (); }, + sub { `cl 2>&1` =~ /Version ([0-9]+)\./ && $1>=14 + ? ($disabled{shared} ? " /MT" : " /MD") + : " /MC"; }), + debug => "/Od -DDEBUG -D_DEBUG", + release => "/O1i"), + lflags => combine("/nologo /opt:ref", + sub { vc_wince_info()->{lflags}; }, + sub { defined($ENV{PORTSDK_LIBPATH}) + ? "/entry:mainCRTstartup" : (); }), + sys_id => "WINCE", + bn_ops => "BN_LLONG EXPORT_VAR_AS_FN", + ex_libs => add(sub { + my @ex_libs = (); + push @ex_libs, 'ws2.lib' unless $disabled{sock}; + push @ex_libs, 'crypt32.lib'; + if (defined($ENV{WCECOMPAT})) { + my $x = '$(WCECOMPAT)/lib'; + if (-f "$x/$ENV{TARGETCPU}/wcecompatex.lib") { + $x .= '/$(TARGETCPU)/wcecompatex.lib'; + } else { + $x .= '/wcecompatex.lib'; + } + push @ex_libs, $x; + } + push @ex_libs, '$(PORTSDK_LIBPATH)/portlib.lib' + if (defined($ENV{'PORTSDK_LIBPATH'})); + push @ex_libs, ' /nodefaultlib coredll.lib corelibc.lib' + if ($ENV{'TARGETCPU'} eq "X86"); + return @ex_libs; + }), + build_scheme => add("VC-WCE", { separator => undef }), + }, + +#### MinGW + "mingw" => { + inherit_from => [ "BASE_unix", asm("x86_asm"), + sub { $disabled{shared} ? () : "x86_uplink" } ], + cc => "gcc", + cflags => combine(picker(default => "-DL_ENDIAN -DWIN32_LEAN_AND_MEAN -DUNICODE -D_UNICODE -m32 -Wall", + debug => "-g -O0", + release => "-O3 -fomit-frame-pointer"), + threads("-D_MT")), + sys_id => "MINGW32", + ex_libs => add("-lws2_32 -lgdi32 -lcrypt32"), + bn_ops => "BN_LLONG EXPORT_VAR_AS_FN", + thread_scheme => "winthreads", + perlasm_scheme => "coff", + dso_scheme => "win32", + shared_target => "mingw-shared", + shared_cflag => add("-D_WINDLL"), + shared_ldflag => "-static-libgcc", + shared_rcflag => "--target=pe-i386", + shared_extension => ".dll", + multilib => "", + apps_aux_src => add("win32_init.c"), + }, + "mingw64" => { + # As for OPENSSL_USE_APPLINK. Applink makes it possible to use + # .dll compiled with one compiler with application compiled with + # another compiler. It's possible to engage Applink support in + # mingw64 build, but it's not done, because till mingw64 + # supports structured exception handling, one can't seriously + # consider its binaries for using with non-mingw64 run-time + # environment. And as mingw64 is always consistent with itself, + # Applink is never engaged and can as well be omitted. + inherit_from => [ "BASE_unix", asm("x86_64_asm") ], + cc => "gcc", + cflags => combine(picker(default => "-DL_ENDIAN -DWIN32_LEAN_AND_MEAN -DUNICODE -D_UNICODE -m64 -Wall", + debug => "-g -O0", + release => "-O3"), + threads("-D_MT")), + sys_id => "MINGW64", + ex_libs => add("-lws2_32 -lgdi32 -lcrypt32"), + bn_ops => "SIXTY_FOUR_BIT EXPORT_VAR_AS_FN", + thread_scheme => "winthreads", + perlasm_scheme => "mingw64", + dso_scheme => "win32", + shared_target => "mingw-shared", + shared_cflag => add("-D_WINDLL"), + shared_ldflag => "-static-libgcc", + shared_rcflag => "--target=pe-x86-64", + shared_extension => ".dll", + multilib => "64", + apps_aux_src => add("win32_init.c"), + }, + +#### UEFI + "UEFI" => { + inherit_from => [ "BASE_unix" ], + cc => "cc", + cflags => "-DL_ENDIAN -O", + sys_id => "UEFI", + }, + +#### UWIN + "UWIN" => { + inherit_from => [ "BASE_unix" ], + cc => "cc", + cflags => "-DTERMIOS -DL_ENDIAN -O -Wall", + sys_id => "UWIN", + bn_ops => "BN_LLONG", + dso_scheme => "win32", + }, + +#### Cygwin + "Cygwin-x86" => { + inherit_from => [ "BASE_unix", asm("x86_asm") ], + cc => "gcc", + cflags => picker(default => "-DTERMIOS -DL_ENDIAN -Wall", + debug => "-g -O0", + release => "-O3 -fomit-frame-pointer"), + sys_id => "CYGWIN", + bn_ops => "BN_LLONG", + thread_scheme => "pthread", + perlasm_scheme => "coff", + dso_scheme => "dlfcn", + shared_target => "cygwin-shared", + shared_cflag => "-D_WINDLL", + shared_ldflag => "-shared", + shared_extension => ".dll", + }, + "Cygwin-x86_64" => { + inherit_from => [ "BASE_unix", asm("x86_64_asm") ], + cc => "gcc", + cflags => picker(default => "-DTERMIOS -DL_ENDIAN -Wall", + debug => "-g -O0", + release => "-O3"), + sys_id => "CYGWIN", + bn_ops => "SIXTY_FOUR_BIT_LONG", + thread_scheme => "pthread", + perlasm_scheme => "mingw64", + dso_scheme => "dlfcn", + shared_target => "cygwin-shared", + shared_cflag => "-D_WINDLL", + shared_ldflag => "-shared", + shared_extension => ".dll", + }, + # Backward compatibility for those using this target + "Cygwin" => { + inherit_from => [ "Cygwin-x86" ] + }, + # In case someone constructs the Cygwin target name themself + "Cygwin-i386" => { + inherit_from => [ "Cygwin-x86" ] + }, + "Cygwin-i486" => { + inherit_from => [ "Cygwin-x86" ] + }, + "Cygwin-i586" => { + inherit_from => [ "Cygwin-x86" ] + }, + "Cygwin-i686" => { + inherit_from => [ "Cygwin-x86" ] + }, + +##### MacOS X (a.k.a. Darwin) setup + "darwin-common" => { + inherit_from => [ "BASE_unix" ], + template => 1, + cc => "cc", + cflags => combine(picker(default => "", + debug => "-g -O0", + release => "-O3"), + threads("-D_REENTRANT")), + sys_id => "MACOSX", + plib_lflags => "-Wl,-search_paths_first", + bn_ops => "BN_LLONG RC4_CHAR", + thread_scheme => "pthreads", + perlasm_scheme => "osx32", + dso_scheme => "dlfcn", + ranlib => "ranlib -c", + shared_target => "darwin-shared", + shared_cflag => "-fPIC", + shared_ldflag => "-dynamiclib", + shared_extension => ".\$(SHLIB_MAJOR).\$(SHLIB_MINOR).dylib", + }, + # Option "freeze" such as -std=gnu9x can't negatively interfere + # with future defaults for below two targets, because MacOS X + # for PPC has no future, it was discontinued by vendor in 2009. + "darwin-ppc-cc" => { + inherit_from => [ "darwin-common", asm("ppc32_asm") ], + cflags => add("-arch ppc -std=gnu9x -DB_ENDIAN -Wa,-force_cpusubtype_ALL"), + perlasm_scheme => "osx32", + shared_ldflag => "-arch ppc -dynamiclib", + }, + "darwin64-ppc-cc" => { + inherit_from => [ "darwin-common", asm("ppc64_asm") ], + cflags => add("-arch ppc64 -std=gnu9x -DB_ENDIAN"), + bn_ops => "SIXTY_FOUR_BIT_LONG RC4_CHAR", + perlasm_scheme => "osx64", + shared_ldflag => "-arch ppc64 -dynamiclib", + }, + "darwin-i386-cc" => { + inherit_from => [ "darwin-common", asm("x86_asm") ], + cflags => add(picker(default => "-arch i386 -DL_ENDIAN", + release => "-fomit-frame-pointer")), + bn_ops => "BN_LLONG RC4_INT", + perlasm_scheme => "macosx", + shared_ldflag => "-arch i386 -dynamiclib", + }, + "darwin64-x86_64-cc" => { + inherit_from => [ "darwin-common", asm("x86_64_asm") ], + cflags => add("-arch x86_64 -DL_ENDIAN -Wall"), + bn_ops => "SIXTY_FOUR_BIT_LONG", + perlasm_scheme => "macosx", + shared_ldflag => "-arch x86_64 -dynamiclib", + }, + +#### iPhoneOS/iOS +# +# It takes three prior-set environment variables to make it work: +# +# CROSS_COMPILE=/where/toolchain/is/usr/bin/ [note ending slash] +# CROSS_TOP=/where/SDKs/are +# CROSS_SDK=iPhoneOSx.y.sdk +# +# Exact paths vary with Xcode releases, but for couple of last ones +# they would look like this: +# +# CROSS_COMPILE=`xcode-select --print-path`/Toolchains/XcodeDefault.xctoolchain/usr/bin/ +# CROSS_TOP=`xcode-select --print-path`/Platforms/iPhoneOS.platform/Developer +# CROSS_SDK=iPhoneOS.sdk +# + "iphoneos-cross" => { + inherit_from => [ "darwin-common" ], + cflags => add("-isysroot \$(CROSS_TOP)/SDKs/\$(CROSS_SDK) -fno-common"), + sys_id => "iOS", + }, + "ios-cross" => { + inherit_from => [ "darwin-common", asm("armv4_asm") ], + # It should be possible to go below iOS 6 and even add -arch armv6, + # thus targeting iPhone pre-3GS, but it's assumed to be irrelevant + # at this point. + cflags => add("-arch armv7 -mios-version-min=6.0.0 -isysroot \$(CROSS_TOP)/SDKs/\$(CROSS_SDK) -fno-common"), + sys_id => "iOS", + perlasm_scheme => "ios32", + }, + "ios64-cross" => { + inherit_from => [ "darwin-common", asm("aarch64_asm") ], + cflags => add("-arch arm64 -mios-version-min=7.0.0 -isysroot \$(CROSS_TOP)/SDKs/\$(CROSS_SDK) -fno-common"), + sys_id => "iOS", + bn_ops => "SIXTY_FOUR_BIT_LONG RC4_CHAR", + perlasm_scheme => "ios64", + }, + +##### GNU Hurd + "hurd-x86" => { + inherit_from => [ "BASE_unix" ], + inherit_from => [ asm("x86_elf_asm") ], + cc => "gcc", + cflags => combine("-DL_ENDIAN -O3 -fomit-frame-pointer -Wall", + threads("-pthread")), + ex_libs => add("-ldl"), + bn_ops => "BN_LLONG", + thread_scheme => "pthreads", + dso_scheme => "dlfcn", + shared_target => "linux-shared", + shared_cflag => "-fPIC", + shared_extension => ".so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", + }, + +##### VxWorks for various targets + "vxworks-ppc60x" => { + inherit_from => [ "BASE_unix" ], + cc => "ccppc", + cflags => "-D_REENTRANT -mrtp -mhard-float -mstrict-align -fno-implicit-fp -DPPC32_fp60x -O2 -fstrength-reduce -fno-builtin -fno-strict-aliasing -Wall -DCPU=PPC32 -DTOOL_FAMILY=gnu -DTOOL=gnu -I\$(WIND_BASE)/target/usr/h -I\$(WIND_BASE)/target/usr/h/wrn/coreip", + sys_id => "VXWORKS", + ex_libs => add("-Wl,--defsym,__wrs_rtp_base=0xe0000000 -L \$(WIND_BASE)/target/usr/lib/ppc/PPC32/common"), + }, + "vxworks-ppcgen" => { + inherit_from => [ "BASE_unix" ], + cc => "ccppc", + cflags => "-D_REENTRANT -mrtp -msoft-float -mstrict-align -O1 -fno-builtin -fno-strict-aliasing -Wall -DCPU=PPC32 -DTOOL_FAMILY=gnu -DTOOL=gnu -I\$(WIND_BASE)/target/usr/h -I\$(WIND_BASE)/target/usr/h/wrn/coreip", + sys_id => "VXWORKS", + ex_libs => add("-Wl,--defsym,__wrs_rtp_base=0xe0000000 -L \$(WIND_BASE)/target/usr/lib/ppc/PPC32/sfcommon"), + }, + "vxworks-ppc405" => { + inherit_from => [ "BASE_unix" ], + cc => "ccppc", + cflags => "-g -msoft-float -mlongcall -DCPU=PPC405 -I\$(WIND_BASE)/target/h", + sys_id => "VXWORKS", + lflags => "-r", + }, + "vxworks-ppc750" => { + inherit_from => [ "BASE_unix" ], + cc => "ccppc", + cflags => "-ansi -nostdinc -DPPC750 -D_REENTRANT -fvolatile -fno-builtin -fno-for-scope -fsigned-char -Wall -msoft-float -mlongcall -DCPU=PPC604 -I\$(WIND_BASE)/target/h \$(DEBUG_FLAG)", + sys_id => "VXWORKS", + lflags => "-r", + }, + "vxworks-ppc750-debug" => { + inherit_from => [ "BASE_unix" ], + cc => "ccppc", + cflags => "-ansi -nostdinc -DPPC750 -D_REENTRANT -fvolatile -fno-builtin -fno-for-scope -fsigned-char -Wall -msoft-float -mlongcall -DCPU=PPC604 -I\$(WIND_BASE)/target/h -DPEDANTIC -DDEBUG -g", + sys_id => "VXWORKS", + lflags => "-r", + }, + "vxworks-ppc860" => { + inherit_from => [ "BASE_unix" ], + cc => "ccppc", + cflags => "-nostdinc -msoft-float -DCPU=PPC860 -DNO_STRINGS_H -I\$(WIND_BASE)/target/h", + sys_id => "VXWORKS", + lflags => "-r", + }, + "vxworks-simlinux" => { + inherit_from => [ "BASE_unix" ], + cc => "ccpentium", + cflags => "-B\$(WIND_BASE)/host/\$(WIND_HOST_TYPE)/lib/gcc-lib/ -D_VSB_CONFIG_FILE=\"\$(WIND_BASE)/target/lib/h/config/vsbConfig.h\" -DL_ENDIAN -DCPU=SIMLINUX -DTOOL_FAMILY=gnu -DTOOL=gnu -fno-builtin -fno-defer-pop -DNO_STRINGS_H -I\$(WIND_BASE)/target/h -I\$(WIND_BASE)/target/h/wrn/coreip -DOPENSSL_NO_HW_PADLOCK", + sys_id => "VXWORKS", + lflags => "-r", + ranlib => "ranlibpentium", + }, + "vxworks-mips" => { + inherit_from => [ "BASE_unix", asm("mips32_asm") ], + cc => "ccmips", + cflags => combine("-mrtp -mips2 -O -G 0 -B\$(WIND_BASE)/host/\$(WIND_HOST_TYPE)/lib/gcc-lib/ -D_VSB_CONFIG_FILE=\"\$(WIND_BASE)/target/lib/h/config/vsbConfig.h\" -DCPU=MIPS32 -msoft-float -mno-branch-likely -DTOOL_FAMILY=gnu -DTOOL=gnu -fno-builtin -fno-defer-pop -DNO_STRINGS_H -I\$(WIND_BASE)/target/usr/h -I\$(WIND_BASE)/target/h/wrn/coreip", + threads("-D_REENTRANT")), + sys_id => "VXWORKS", + ex_libs => add("-Wl,--defsym,__wrs_rtp_base=0xe0000000 -L \$(WIND_BASE)/target/usr/lib/mips/MIPSI32/sfcommon"), + thread_scheme => "pthreads", + perlasm_scheme => "o32", + ranlib => "ranlibmips", + }, + +#### uClinux + "uClinux-dist" => { + inherit_from => [ "BASE_unix" ], + cc => "$ENV{'CC'}", + cflags => combine("\$(CFLAGS)", + threads("-D_REENTRANT")), + plib_lflags => "\$(LDFLAGS)", + ex_libs => add("\$(LDLIBS)"), + bn_ops => "BN_LLONG", + thread_scheme => "pthreads", + dso_scheme => "$ENV{'LIBSSL_dlfcn'}", + shared_target => "linux-shared", + shared_cflag => "-fPIC", + shared_ldflag => "-shared", + shared_extension => ".so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", + ranlib => "$ENV{'RANLIB'}", + }, + "uClinux-dist64" => { + inherit_from => [ "BASE_unix" ], + cc => "$ENV{'CC'}", + cflags => combine("\$(CFLAGS)", + threads("-D_REENTRANT")), + plib_lflags => "\$(LDFLAGS)", + ex_libs => add("\$(LDLIBS)"), + bn_ops => "SIXTY_FOUR_BIT_LONG", + thread_scheme => "pthreads", + dso_scheme => "$ENV{'LIBSSL_dlfcn'}", + shared_target => "linux-shared", + shared_cflag => "-fPIC", + shared_ldflag => "-shared", + shared_extension => ".so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", + ranlib => "$ENV{'RANLIB'}", + }, + + ##### VMS + "vms-generic" => { + inherit_from => [ "BASE_VMS" ], + template => 1, + cc => "CC/DECC", + cflags => picker(default => "/STANDARD=(ISOC94,RELAXED)/NOLIST/PREFIX=ALL", + debug => "/NOOPTIMIZE/DEBUG", + release => "/OPTIMIZE/NODEBUG"), + defines => add("OPENSSL_USE_NODELETE"), + lflags => picker(default => "/MAP", + debug => "/DEBUG/TRACEBACK", + release => "/NODEBUG/NOTRACEBACK"), + lib_cflags => add("/NAMES=(AS_IS,SHORTENED)/EXTERN_MODEL=STRICT_REFDEF"), + dso_cflags => add("/NAMES=(AS_IS,SHORTENED)"), + shared_target => "vms-shared", + dso_scheme => "vms", + thread_scheme => "pthreads", + + apps_aux_src => "vms_decc_init.c vms_term_sock.c", + }, + + "vms-alpha" => { + inherit_from => [ "vms-generic" ], + cflags => add(sub { my @warnings = + @{vms_info(0)->{disable_warns}}; + @warnings + ? "/WARNINGS=DISABLE=(".join(",",@warnings).")" : (); }), + defines => + add(sub { + return vms_info(0)->{def_zlib} + ? "LIBZ=\"\"\"".vms_info(0)->{def_zlib}."\"\"\"" : (); + }), + ex_libs => add(sub { return vms_info(0)->{zlib} || (); }), + pointer_size => sub { return vms_info(0)->{pointer_size} }, + #as => "???", + #debug_aflags => "/NOOPTIMIZE/DEBUG", + #release_aflags => "/OPTIMIZE/NODEBUG", + bn_opts => "SIXTY_FOUR_BIT RC4_INT", + }, + "vms-alpha-p32" => { + inherit_from => [ "vms-generic" ], + cflags => + add("/POINTER_SIZE=32", + sub { my @warnings = + @{vms_info(32)->{disable_warns}}; + @warnings + ? "/WARNINGS=DISABLE=(".join(",",@warnings).")" : (); + } ), + defines => + add(sub { + return vms_info(32)->{def_zlib} + ? "LIBZ=\"\"\"".vms_info(32)->{def_zlib}."\"\"\"" : (); + }), + ex_libs => add(sub { return vms_info(32)->{zlib} || (); }), + pointer_size => sub { return vms_info(32)->{pointer_size} }, + }, + "vms-alpha-p64" => { + inherit_from => [ "vms-generic" ], + cflags => + add("/POINTER_SIZE=64=ARGV", + sub { my @warnings = + @{vms_info(64)->{disable_warns}}; + @warnings + ? "/WARNINGS=DISABLE=(".join(",",@warnings).")" : (); + } ), + defines => + add(sub { + return vms_info(64)->{def_zlib} + ? "LIBZ=\"\"\"".vms_info(64)->{def_zlib}."\"\"\"" : (); + }), + ex_libs => add(sub { return vms_info(64)->{zlib} || (); }), + pointer_size => sub { return vms_info(64)->{pointer_size} }, + }, + "vms-ia64" => { + inherit_from => [ "vms-generic" ], + cflags => add(sub { my @warnings = + @{vms_info(0)->{disable_warns}}; + @warnings + ? "/WARNINGS=DISABLE=(".join(",",@warnings).")" : (); }), + defines => + add(sub { + return vms_info(0)->{def_zlib} + ? "LIBZ=\"\"\"".vms_info(0)->{def_zlib}."\"\"\"" : (); + }), + ex_libs => add(sub { return vms_info(0)->{zlib} || (); }), + pointer_size => sub { return vms_info(0)->{pointer_size} }, + #as => "I4S", + #debug_aflags => "/NOOPTIMIZE/DEBUG", + #release_aflags => "/OPTIMIZE/NODEBUG", + bn_opts => "SIXTY_FOUR_BIT RC4_INT", + }, + "vms-ia64-p32" => { + inherit_from => [ "vms-generic" ], + cflags => + add("/POINTER_SIZE=32", + sub { my @warnings = + @{vms_info(32)->{disable_warns}}; + @warnings + ? "/WARNINGS=DISABLE=(".join(",",@warnings).")" : (); + } ), + defines => + add(sub { + return vms_info(32)->{def_zlib} + ? "LIBZ=\"\"\"".vms_info(32)->{def_zlib}."\"\"\"" : (); + }), + ex_libs => add(sub { return vms_info(32)->{zlib} || (); }), + pointer_size => sub { return vms_info(32)->{pointer_size} }, + }, + "vms-ia64-p64" => { + inherit_from => [ "vms-generic" ], + cflags => + add("/POINTER_SIZE=64=ARGV", + sub { my @warnings = + @{vms_info(64)->{disable_warns}}; + @warnings + ? "/WARNINGS=DISABLE=(".join(",",@warnings).")" : (); + } ), + defines => + add(sub { + return vms_info(64)->{def_zlib} + ? "LIBZ=\"\"\"".vms_info(64)->{def_zlib}."\"\"\"" : (); + }), + ex_libs => add(sub { return vms_info(64)->{zlib} || (); }), + pointer_size => sub { return vms_info(64)->{pointer_size} }, + }, + +); diff --git a/Configurations/50-djgpp.conf b/Configurations/50-djgpp.conf new file mode 100644 index 000000000..f532bd16f --- /dev/null +++ b/Configurations/50-djgpp.conf @@ -0,0 +1,15 @@ +# We can't make any commitment to support the DJGPP platform, +# and rely entirely on the OpenSSL community to help is fine +# tune and test. + +%targets = ( + "DJGPP" => { + inherit_from => [ asm("x86_asm") ], + cc => "gcc", + cflags => "-I/dev/env/WATT_ROOT/inc -DTERMIOS -DL_ENDIAN -fomit-frame-pointer -O2 -Wall", + sys_id => "MSDOS", + ex_libs => add("-L/dev/env/WATT_ROOT/lib -lwatt"), + bn_ops => "BN_LLONG", + perlasm_scheme => "a.out", + }, +); diff --git a/Configurations/50-haiku.conf b/Configurations/50-haiku.conf new file mode 100644 index 000000000..f11466660 --- /dev/null +++ b/Configurations/50-haiku.conf @@ -0,0 +1,29 @@ +%targets = ( + "haiku-common" => { + template => 1, + cc => "cc", + cflags => add_before(picker(default => "-DL_ENDIAN -Wall -include \$(SRCDIR)/os-dep/haiku.h", + debug => "-g -O0", + release => "-O2"), + threads("-D_REENTRANT")), + sys_id => "HAIKU", + ex_libs => "-lnetwork", + perlasm_scheme => "elf", + thread_scheme => "pthreads", + dso_scheme => "dlfcn", + shared_target => "gnu-shared", + shared_cflag => "-fPIC", + shared_ldflag => "-shared", + shared_extension => ".so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", + }, + "haiku-x86" => { + inherit_from => [ "haiku-common", asm("x86_elf_asm") ], + cflags => add(picker(release => "-fomit-frame-pointer")), + bn_ops => "BN_LLONG", + }, + "haiku-x86_64" => { + inherit_from => [ "haiku-common" ], + cflags => add("-m64"), + bn_ops => "SIXTY_FOUR_BIT_LONG", + }, +); diff --git a/Configurations/50-masm.conf b/Configurations/50-masm.conf new file mode 100644 index 000000000..60a55072f --- /dev/null +++ b/Configurations/50-masm.conf @@ -0,0 +1,17 @@ +# We can't make commitment to supporting Microsoft assembler, +# because it would mean supporting all masm versions. This in +# in turn is because masm is not really an interchangeable option, +# while users tend to have reasons to stick with specific Visual +# Studio versions. It's usually lesser hassle to make it work +# with latest assembler, but tweaking for older versions had +# proven to be daunting task. This is experimental target, for +# production builds stick with [up-to-date version of] nasm. + +%targets = ( + "VC-WIN64A-masm" => { + inherit_from => [ "VC-WIN64A" ], + as => "ml64", + asflags => "/c /Cp /Cx /Zi", + asoutflag => "/Fo", + }, +); diff --git a/Configurations/90-team.conf b/Configurations/90-team.conf new file mode 100644 index 000000000..6ff299b18 --- /dev/null +++ b/Configurations/90-team.conf @@ -0,0 +1,113 @@ +## -*- mode: perl; -*- +## Build configuration targets for openssl-team members + +%targets = ( + "purify" => { + cc => "purify gcc", + cflags => "-g -Wall", + thread_scheme => "(unknown)", + ex_libs => add(" ","-lsocket -lnsl"), + }, + "debug" => { + cc => "gcc", + cflags => "-DBN_DEBUG -DREF_DEBUG -DCONF_DEBUG -DBN_CTX_DEBUG -DOPENSSL_NO_ASM -ggdb -g2 -Wformat -Wshadow -Wmissing-prototypes -Wmissing-declarations -Werror", + thread_scheme => "(unknown)", + }, + "debug-erbridge" => { + inherit_from => [ "x86_64_asm" ], + cc => "gcc", + cflags => combine("$gcc_devteam_warn -DBN_DEBUG -DCONF_DEBUG -m64 -DL_ENDIAN -DTERMIO -g", + threads("-D_REENTRANT")), + ex_libs => add(" ","-ldl"), + bn_ops => "SIXTY_FOUR_BIT_LONG", + thread_scheme => "pthreads", + perlasm_scheme => "elf", + dso_scheme => "dlfcn", + shared_target => "linux-shared", + shared_cflag => "-fPIC", + shared_ldflag => "-m64", + shared_extension => ".so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", + multilib => "64", + }, + "debug-linux-pentium" => { + inherit_from => [ "x86_elf_asm" ], + cc => "gcc", + cflags => combine("-DBN_DEBUG -DREF_DEBUG -DCONF_DEBUG -DBN_CTX_DEBUG -DL_ENDIAN -g -mcpu=pentium -Wall", + threads("-D_REENTRANT")), + ex_libs => add(" ","-ldl"), + bn_ops => "BN_LLONG", + thread_scheme => "pthreads", + dso_scheme => "dlfcn", + }, + "debug-linux-ppro" => { + inherit_from => [ "x86_elf_asm" ], + cc => "gcc", + cflags => combine("-DBN_DEBUG -DREF_DEBUG -DCONF_DEBUG -DBN_CTX_DEBUG -DL_ENDIAN -g -mcpu=pentiumpro -Wall", + threads("-D_REENTRANT")), + ex_libs => add(" ","-ldl"), + bn_ops => "BN_LLONG", + thread_scheme => "pthreads", + dso_scheme => "dlfcn", + }, + "debug-linux-ia32-aes" => { + cc => "gcc", + cflags => combine("-DL_ENDIAN -O3 -fomit-frame-pointer -Wall", + threads("-D_REENTRANT")), + ex_libs => add(" ","-ldl"), + bn_ops => "BN_LLONG", + cpuid_asm_src => "x86cpuid.s", + bn_asm_src => "bn-586.s co-586.s x86-mont.s", + des_asm_src => "des-586.s crypt586.s", + aes_asm_src => "aes_x86core.s aes_cbc.s aesni-x86.s", + bf_asm_src => "bf-586.s", + md5_asm_src => "md5-586.s", + sha1_asm_src => "sha1-586.s sha256-586.s sha512-586.s", + cast_asm_src => "cast-586.s", + rc4_asm_src => "rc4-586.s", + rmd160_asm_src => "rmd-586.s", + rc5_asm_src => "rc5-586.s", + wp_asm_src => "wp_block.s wp-mmx.s", + modes_asm_src => "ghash-x86.s", + padlock_asm_src => "e_padlock-x86.s", + gmi_asm_src => "e_gmi-x86.s", + thread_scheme => "pthreads", + perlasm_scheme => "elf", + dso_scheme => "dlfcn", + shared_target => "linux-shared", + shared_cflag => "-fPIC", + shared_extension => ".so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", + }, + "dist" => { + cc => "cc", + cflags => "-O", + thread_scheme => "(unknown)", + }, + "debug-test-64-clang" => { + inherit_from => [ "x86_64_asm" ], + cc => "clang", + cflags => combine("$gcc_devteam_warn -Wno-error=overlength-strings -Wno-error=extended-offsetof -Wno-error=language-extension-token -Wno-error=unused-const-variable -Wstrict-overflow -Qunused-arguments -DBN_DEBUG -DCONF_DEBUG -DDEBUG_SAFESTACK -DDEBUG_UNUSED -g3 -O3 -pipe", + threads("${BSDthreads}")), + bn_ops => "SIXTY_FOUR_BIT_LONG", + thread_scheme => "pthreads", + perlasm_scheme => "elf", + dso_scheme => "dlfcn", + shared_target => "bsd-gcc-shared", + shared_cflag => "-fPIC", + shared_extension => ".so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", + }, + "darwin64-debug-test-64-clang" => { + inherit_from => [ "x86_64_asm" ], + cc => "clang", + cflags => combine("-arch x86_64 -DL_ENDIAN $gcc_devteam_warn -Wno-error=overlength-strings -Wno-error=extended-offsetof -Wno-error=language-extension-token -Wno-error=unused-const-variable -Wstrict-overflow -Qunused-arguments -DBN_DEBUG -DCONF_DEBUG -DDEBUG_SAFESTACK -DDEBUG_UNUSED -g3 -O3 -pipe", + threads("${BSDthreads}")), + sys_id => "MACOSX", + bn_ops => "SIXTY_FOUR_BIT_LONG", + thread_scheme => "pthreads", + perlasm_scheme => "macosx", + dso_scheme => "dlfcn", + shared_target => "darwin-shared", + shared_cflag => "-fPIC -fno-common", + shared_ldflag => "-arch x86_64 -dynamiclib", + shared_extension => ".\$(SHLIB_MAJOR).\$(SHLIB_MINOR).dylib", + }, +); diff --git a/Configurations/INTERNALS.Configure b/Configurations/INTERNALS.Configure new file mode 100644 index 000000000..b28305dec --- /dev/null +++ b/Configurations/INTERNALS.Configure @@ -0,0 +1,136 @@ +Configure Internals +=================== + +[ note: this file uses markdown for formatting ] + +Intro +----- + +This is a collection of notes that are hopefully of interest to those +who decide to dive into Configure and what it does. This is a living +document and anyone is encouraged to add to it and submit changes. +There's no claim for this document to be complete at any time, but it +will hopefully reach such a point in time. + + +---------------------------------------------------------------------- + +Parsing build.info files, processing conditions +----------------------------------------------- + +Processing conditions in build.info files is done with the help of a +condition stack that tell if a build.info should be processed or if it +should just be skipped over. The possible states of the stack top are +expressed in the following comment from Configure: + + # The top item of this stack has the following values + # -2 positive already run and we found ELSE (following ELSIF should fail) + # -1 positive already run (skip until ENDIF) + # 0 negatives so far (if we're at a condition, check it) + # 1 last was positive (don't skip lines until next ELSE, ELSIF or ENDIF) + # 2 positive ELSE (following ELSIF should fail) + +Ground rule is that non-condition lines are skipped over if the +stack top is > 0. Condition lines (IF, ELSIF, ELSE and ENDIF +statements) need to be processed either way to keep track of the skip +stack states, so they are a little more intricate. + +Instead of trying to describe in words, here are some example of what +the skip stack should look like after each line is processed: + +Example 1: + +| IF[1] | 1 | | +| ... whatever ... | | this line is processed | +| IF[1] | 1 1 | | +| ... whatever ... | | this line is processed | +| ELSIF[1] | 1 -1 | | +| ... whatever ... | | this line is skipped over | +| ELSE | 1 -2 | | +| ... whatever ... | | this line is skipped over | +| ENDIF | 1 | | +| ... whatever ... | | this line is processed | +| ELSIF[1] | -1 | | +| ... whatever ... | | this line is skipped over | +| IF[1] | -1 -1 | | +| ... whatever ... | | this line is skipped over | +| ELSIF[1] | -1 -1 | | +| ... whatever ... | | this line is skipped over | +| ELSE | -1 -2 | | +| ... whatever ... | | this line is skipped over | +| ENDIF | -1 | | +| ... whatever ... | | this line is skipped over | +| ENDIF | | | + +Example 2: + +| IF[0] | 0 | | +| ... whatever ... | | this line is skipped over | +| IF[1] | 0 -1 | | +| ... whatever ... | | this line is skipped over | +| ELSIF[1] | 0 -1 | | +| ... whatever ... | | this line is skipped over | +| ELSE | 0 -2 | | +| ... whatever ... | | this line is skipped over | +| ENDIF | 0 | | +| ... whatever ... | | this line is skipped over | +| ELSIF[1] | 1 | | +| ... whatever ... | | this line is processed | +| IF[1] | 1 1 | | +| ... whatever ... | | this line is processed | +| ELSIF[1] | 1 -1 | | +| ... whatever ... | | this line is skipped over | +| ELSE | 1 -2 | | +| ... whatever ... | | this line is skipped over | +| ENDIF | 1 | | +| ... whatever ... | | this line is processed | +| ENDIF | | | + +Example 3: + +| IF[0] | 0 | | +| ... whatever ... | | this line is skipped over | +| IF[0] | 0 -1 | | +| ... whatever ... | | this line is skipped over | +| ELSIF[1] | 0 -1 | | +| ... whatever ... | | this line is skipped over | +| ELSE | 0 -2 | | +| ... whatever ... | | this line is skipped over | +| ENDIF | 0 | | +| ... whatever ... | | this line is skipped over | +| ELSIF[1] | 1 | | +| ... whatever ... | | this line is processed | +| IF[0] | 1 0 | | +| ... whatever ... | | this line is skipped over | +| ELSIF[1] | 1 1 | | +| ... whatever ... | | this line is processed | +| ELSE | 1 -2 | | +| ... whatever ... | | this line is skipped over | +| ENDIF | 1 | | +| ... whatever ... | | this line is processed | +| ENDIF | | | + +Example 4: + +| IF[0] | 0 | | +| ... whatever ... | | this line is skipped over | +| IF[0] | 0 -1 | | +| ... whatever ... | | this line is skipped over | +| ELSIF[0] | 0 -1 | | +| ... whatever ... | | this line is skipped over | +| ELSE | 0 -2 | | +| ... whatever ... | | this line is skipped over | +| ENDIF | 0 | | +| ... whatever ... | | this line is skipped over | +| ELSIF[1] | 1 | | +| ... whatever ... | | this line is processed | +| IF[0] | 1 0 | | +| ... whatever ... | | this line is skipped over | +| ELSIF[0] | 1 0 | | +| ... whatever ... | | this line is skipped over | +| ELSE | 1 2 | | +| ... whatever ... | | this line is processed | +| ENDIF | 1 | | +| ... whatever ... | | this line is processed | +| ENDIF | | | + diff --git a/Configurations/README b/Configurations/README new file mode 100644 index 000000000..da64e8c79 --- /dev/null +++ b/Configurations/README @@ -0,0 +1,655 @@ +Configurations of OpenSSL target platforms +========================================== + +Target configurations are a collection of facts that we know about +different platforms and their capabilities. We organise them in a +hash table, where each entry represent a specific target. + +In each table entry, the following keys are significant: + + inherit_from => Other targets to inherit values from. + Explained further below. [1] + template => Set to 1 if this isn't really a platform + target. Instead, this target is a template + upon which other targets can be built. + Explained further below. [1] + + sys_id => System identity for systems where that + is difficult to determine automatically. + + cc => The compiler command, usually one of "cc", + "gcc" or "clang". This command is normally + also used to link object files and + libraries into the final program. + cflags => Flags that are used at all times when + compiling. + defines => As an alternative, macro definitions may be + present here instead of in `cflags'. If + given here, they MUST be as an array of the + string such as "MACRO=value", or just + "MACRO" for definitions without value. + shared_cflag => Extra compilation flags used when + compiling for shared libraries, typically + something like "-fPIC". + + (linking is a complex thing, see [3] below) + ld => Linker command, usually not defined + (meaning the compiler command is used + instead). + (NOTE: this is here for future use, it's + not implemented yet) + lflags => Flags that are used when linking apps. + shared_ldflag => Flags that are used when linking shared + or dynamic libraries. + plib_lflags => Extra linking flags to appear just before + the libraries on the command line. + ex_libs => Extra libraries that are needed when + linking. + + ar => The library archive command, the default is + "ar". + (NOTE: this is here for future use, it's + not implemented yet) + arflags => Flags to be used with the library archive + command. + + ranlib => The library archive indexing command, the + default is 'ranlib' it it exists. + + unistd => An alternative header to the typical + ''. This is very rarely needed. + + shared_extension => File name extension used for shared + libraries. + obj_extension => File name extension used for object files. + On unix, this defaults to ".o" (NOTE: this + is here for future use, it's not + implemented yet) + exe_extension => File name extension used for executable + files. On unix, this defaults to "" (NOTE: + this is here for future use, it's not + implemented yet) + + thread_scheme => The type of threads is used on the + configured platform. Currently known + values are "(unknown)", "pthreads", + "uithreads" (a.k.a solaris threads) and + "winthreads". Except for "(unknown)", the + actual value is currently ignored but may + be used in the future. See further notes + below [2]. + dso_scheme => The type of dynamic shared objects to build + for. This mostly comes into play with + engines, but can be used for other purposes + as well. Valid values are "DLFCN" + (dlopen() et al), "DLFCN_NO_H" (for systems + that use dlopen() et al but do not have + fcntl.h), "DL" (shl_load() et al), "WIN32" + and "VMS". + perlasm_scheme => The perlasm method used to created the + assembler files used when compiling with + assembler implementations. + shared_target => The shared library building method used. + This is a target found in Makefile.shared. + build_scheme => The scheme used to build up a Makefile. + In its simplest form, the value is a string + with the name of the build scheme. + The value may also take the form of a list + of strings, if the build_scheme is to have + some options. In this case, the first + string in the list is the name of the build + scheme. + Currently recognised build scheme is "unified". + For the "unified" build scheme, this item + *must* be an array with the first being the + word "unified" and the second being a word + to identify the platform family. + + multilib => On systems that support having multiple + implementations of a library (typically a + 32-bit and a 64-bit variant), this is used + to have the different variants in different + directories. + + bn_ops => Building options (was just bignum options + in the earlier history of this option, + hence the name). This a string of words + that describe properties on the designated + target platform, such as the type of + integers used to build up the bitnum, + different ways to implement certain ciphers + and so on. To fully comprehend the + meaning, the best is to read the affected + source. + The valid words are: + + BN_LLONG use 'unsigned long long' in + some bignum calculations. + This has no value when + SIXTY_FOUR_BIT or + SIXTY_FOUR_BIT_LONG is given. + RC4_CHAR makes the basic RC4 unit of + calculation an unsigned char. + SIXTY_FOUR_BIT processor registers + are 64 bits, long is + 32 bits, long long is + 64 bits. + SIXTY_FOUR_BIT_LONG processor registers + are 64 bits, long is + 64 bits. + THIRTY_TWO_BIT processor registers + are 32 bits. + EXPORT_VAR_AS_FN for shared libraries, + export vars as + accessor functions. + + apps_extra_src => Extra source to build apps/openssl, as + needed by the target. + cpuid_asm_src => assembler implementation of cpuid code as + well as OPENSSL_cleanse(). + Default to mem_clr.c + bn_asm_src => Assembler implementation of core bignum + functions. + Defaults to bn_asm.c + ec_asm_src => Assembler implementation of core EC + functions. + des_asm_src => Assembler implementation of core DES + encryption functions. + Defaults to 'des_enc.c fcrypt_b.c' + aes_asm_src => Assembler implementation of core AES + functions. + Defaults to 'aes_core.c aes_cbc.c' + bf_asm_src => Assembler implementation of core BlowFish + functions. + Defaults to 'bf_enc.c' + md5_asm_src => Assembler implementation of core MD5 + functions. + sha1_asm_src => Assembler implementation of core SHA1, + functions, and also possibly SHA256 and + SHA512 ones. + cast_asm_src => Assembler implementation of core CAST + functions. + Defaults to 'c_enc.c' + rc4_asm_src => Assembler implementation of core RC4 + functions. + Defaults to 'rc4_enc.c rc4_skey.c' + rmd160_asm_src => Assembler implementation of core RMD160 + functions. + rc5_asm_src => Assembler implementation of core RC5 + functions. + Defaults to 'rc5_enc.c' + wp_asm_src => Assembler implementation of core WHIRLPOOL + functions. + cmll_asm_src => Assembler implementation of core CAMELLIA + functions. + Defaults to 'camellia.c cmll_misc.c cmll_cbc.c' + modes_asm_src => Assembler implementation of cipher modes, + currently the functions gcm_gmult_4bit and + gcm_ghash_4bit. + padlock_asm_src => Assembler implementation of core parts of + the padlock engine. This is mandatory on + any platform where the padlock engine might + actually be built. + + +[1] as part of the target configuration, one can have a key called + 'inherit_from' that indicate what other configurations to inherit + data from. These are resolved recursively. + + Inheritance works as a set of default values that can be overridden + by corresponding key values in the inheriting configuration. + + Note 1: any configuration table can be used as a template. + Note 2: pure templates have the attribute 'template => 1' and + cannot be used as build targets. + + If several configurations are given in the 'inherit_from' array, + the values of same attribute are concatenated with space + separation. With this, it's possible to have several smaller + templates for different configuration aspects that can be combined + into a complete configuration. + + instead of a scalar value or an array, a value can be a code block + of the form 'sub { /* your code here */ }'. This code block will + be called with the list of inherited values for that key as + arguments. In fact, the concatenation of strings is really done + by using 'sub { join(" ",@_) }' on the list of inherited values. + + An example: + + "foo" => { + template => 1, + haha => "ha ha", + hoho => "ho", + ignored => "This should not appear in the end result", + }, + "bar" => { + template => 1, + haha => "ah", + hoho => "haho", + hehe => "hehe" + }, + "laughter" => { + inherit_from => [ "foo", "bar" ], + hehe => sub { join(" ",(@_,"!!!")) }, + ignored => "", + } + + The entry for "laughter" will become as follows after processing: + + "laughter" => { + haha => "ha ha ah", + hoho => "ho haho", + hehe => "hehe !!!", + ignored => "" + } + +[2] OpenSSL is built with threading capabilities unless the user + specifies 'no-threads'. The value of the key 'thread_scheme' may + be "(unknown)", in which case the user MUST give some compilation + flags to Configure. + +[3] OpenSSL has three types of things to link from object files or + static libraries: + + - shared libraries; that would be libcrypto and libssl. + - shared objects (sometimes called dynamic libraries); that would + be the engines. + - applications; those are apps/openssl and all the test apps. + + Very roughly speaking, linking is done like this (words in braces + represent the configuration settings documented at the beginning + of this file): + + shared libraries: + {ld} $(CFLAGS) {shared_ldflag} -shared -o libfoo.so \ + -Wl,--whole-archive libfoo.a -Wl,--no-whole-archive \ + {plib_lflags} -lcrypto {ex_libs} + + shared objects: + {ld} $(CFLAGS) {shared_ldflag} -shared -o libeng.so \ + blah1.o blah2.o {plib_lflags} -lcrypto {ex_libs} + + applications: + {ld} $(CFLAGS) {lflags} -o app \ + app1.o utils.o {plib_lflags} -lssl -lcrypto {ex_libs} + + +Historically, the target configurations came in form of a string with +values separated by colons. This use is deprecated. The string form +looked like this: + + "target" => "{cc}:{cflags}:{unistd}:{thread_cflag}:{sys_id}:{lflags}:{bn_ops}:{cpuid_obj}:{bn_obj}:{ec_obj}:{des_obj}:{aes_obj}:{bf_obj}:{md5_obj}:{sha1_obj}:{cast_obj}:{rc4_obj}:{rmd160_obj}:{rc5_obj}:{wp_obj}:{cmll_obj}:{modes_obj}:{padlock_obj}:{perlasm_scheme}:{dso_scheme}:{shared_target}:{shared_cflag}:{shared_ldflag}:{shared_extension}:{ranlib}:{arflags}:{multilib}" + + +Build info files +================ + +The build.info files that are spread over the source tree contain the +minimum information needed to build and distribute OpenSSL. It uses a +simple and yet fairly powerful language to determine what needs to be +built, from what sources, and other relationships between files. + +For every build.info file, all file references are relative to the +directory of the build.info file for source files, and the +corresponding build directory for built files if the build tree +differs from the source tree. + +When processed, every line is processed with the perl module +Text::Template, using the delimiters "{-" and "-}". The hashes +%config and %target are passed to the perl fragments, along with +$sourcedir and $builddir, which are the locations of the source +directory for the current build.info file and the corresponding build +directory, all relative to the top of the build tree. + +To begin with, things to be built are declared by setting specific +variables: + + PROGRAMS=foo bar + LIBS=libsomething + ENGINES=libeng + SCRIPTS=myhack + EXTRA=file1 file2 + +Note that the files mentioned for PROGRAMS, LIBS and ENGINES *must* be +without extensions. The build file templates will figure them out. + +For each thing to be built, it is then possible to say what sources +they are built from: + + PROGRAMS=foo bar + SOURCE[foo]=foo.c common.c + SOURCE[bar]=bar.c extra.c common.c + +It's also possible to tell some other dependencies: + + DEPEND[foo]=libsomething + DEPEND[libbar]=libsomethingelse + +(it could be argued that 'libsomething' and 'libsomethingelse' are +source as well. However, the files given through SOURCE are expected +to be located in the source tree while files given through DEPEND are +expected to be located in the build tree) + +For some libraries, we maintain files with public symbols and their +slot in a transfer vector (important on some platforms). It can be +declared like this: + + ORDINALS[libcrypto]=crypto + +The value is not the name of the file in question, but rather the +argument to util/mkdef.pl that indicates which file to use. + +One some platforms, shared libraries come with a name that's different +from their static counterpart. That's declared as follows: + + SHARED_NAME[libfoo]=cygfoo-{- $config{shlibver} -} + +The example is from Cygwin, which has a required naming convention. + +Sometimes, it makes sense to rename an output file, for example a +library: + + RENAME[libfoo]=libbar + +That lines has "libfoo" get renamed to "libbar". While it makes no +sense at all to just have a rename like that (why not just use +"libbar" everywhere?), it does make sense when it can be used +conditionally. See a little further below for an example. + +In some cases, it's desirable to include some source files in the +shared form of a library only: + + SHARED_SOURCE[libfoo]=dllmain.c + +For any file to be built, it's also possible to tell what extra +include paths the build of their source files should use: + + INCLUDE[foo]=include + +In some cases, one might want to generate some source files from +others, that's done as follows: + + GENERATE[foo.s]=asm/something.pl $(CFLAGS) + GENERATE[bar.s]=asm/bar.S + +The value of each GENERATE line is a command line or part of it. +Configure places no rules on the command line, except the the first +item muct be the generator file. It is, however, entirely up to the +build file template to define exactly how those command lines should +be handled, how the output is captured and so on. + +Sometimes, the generator file itself depends on other files, for +example if it is a perl script that depends on other perl modules. +This can be expressed using DEPEND like this: + + DEPEND[asm/something.pl]=../perlasm/Foo.pm + +There may also be cases where the exact file isn't easily specified, +but an inclusion directory still needs to be specified. INCLUDE can +be used in that case: + + INCLUDE[asm/something.pl]=../perlasm + +NOTE: GENERATE lines are limited to one command only per GENERATE. + +As a last resort, it's possible to have raw build file lines, between +BEGINRAW and ENDRAW lines as follows: + + BEGINRAW[Makefile(unix)] + haha.h: {- $builddir -}/Makefile + echo "/* haha */" > haha.h + ENDRAW[Makefile(unix)] + +The word within square brackets is the build_file configuration item +or the build_file configuration item followed by the second word in the +build_scheme configuration item for the configured target within +parenthesis as shown above. For example, with the following relevant +configuration items: + + build_file => "build.ninja" + build_scheme => [ "unified", "unix" ] + +... these lines will be considered: + + BEGINRAW[build.ninja] + build haha.h: echo "/* haha */" > haha.h + ENDRAW[build.ninja] + + BEGINRAW[build.ninja(unix)] + build hoho.h: echo "/* hoho */" > hoho.h + ENDRAW[build.ninja(unix)] + +Should it be needed because the recipes within a RAW section might +clash with those generated by Configure, it's possible to tell it +not to generate them with the use of OVERRIDES, for example: + + SOURCE[libfoo]=foo.c bar.c + + OVERRIDES=bar.o + BEGINRAW[Makefile(unix)] + bar.o: bar.c + $(CC) $(CFLAGS) -DSPECIAL -c -o $@ $< + ENDRAW[Makefile(unix)] + +See the documentation further up for more information on configuration +items. + +Finally, you can have some simple conditional use of the build.info +information, looking like this: + + IF[1] + something + ELSIF[2] + something other + ELSE + something else + ENDIF + +The expression in square brackets is interpreted as a string in perl, +and will be seen as true if perl thinks it is, otherwise false. For +example, the above would have "something" used, since 1 is true. + +Together with the use of Text::Template, this can be used as +conditions based on something in the passed variables, for example: + + IF[{- $disabled{shared} -}] + LIBS=libcrypto + SOURCE[libcrypto]=... + ELSE + LIBS=libfoo + SOURCE[libfoo]=... + ENDIF + +or: + + # VMS has a cultural standard where all libraries are prefixed. + # For OpenSSL, the choice is 'ossl_' + IF[{- $config{target} =~ /^vms/ -}] + RENAME[libcrypto]=ossl_libcrypto + RENAME[libssl]=ossl_libssl + ENDIF + + +Build-file programming with the "unified" build system +====================================================== + +"Build files" are called "Makefile" on Unix-like operating systems, +"descrip.mms" for MMS on VMS, "makefile" for nmake on Windows, etc. + +To use the "unified" build system, the target configuration needs to +set the three items 'build_scheme', 'build_file' and 'build_command'. +In the rest of this section, we will assume that 'build_scheme' is set +to "unified" (see the configurations documentation above for the +details). + +For any name given by 'build_file', the "unified" system expects a +template file in Configurations/ named like the build file, with +".tmpl" appended, or in case of possible ambiguity, a combination of +the second 'build_scheme' list item and the 'build_file' name. For +example, if 'build_file' is set to "Makefile", the template could be +Configurations/Makefile.tmpl or Configurations/unix-Makefile.tmpl. +In case both Configurations/unix-Makefile.tmpl and +Configurations/Makefile.tmpl are present, the former takes +precedence. + +The build-file template is processed with the perl module +Text::Template, using "{-" and "-}" as delimiters that enclose the +perl code fragments that generate configuration-dependent content. +Those perl fragments have access to all the hash variables from +configdata.pem. + +The build-file template is expected to define at least the following +perl functions in a perl code fragment enclosed with "{-" and "-}". +They are all expected to return a string with the lines they produce. + + generatesrc - function that produces build file lines to generate + a source file from some input. + + It's called like this: + + generatesrc(src => "PATH/TO/tobegenerated", + generator => [ "generatingfile", ... ] + generator_incs => [ "INCL/PATH", ... ] + generator_deps => [ "dep1", ... ] + generator => [ "generatingfile", ... ] + incs => [ "INCL/PATH", ... ], + deps => [ "dep1", ... ], + intent => one of "libs", "dso", "bin" ); + + 'src' has the name of the file to be generated. + 'generator' is the command or part of command to + generate the file, of which the first item is + expected to be the file to generate from. + generatesrc() is expected to analyse and figure out + exactly how to apply that file and how to capture + the result. 'generator_incs' and 'generator_deps' + are include directories and files that the generator + file itself depends on. 'incs' and 'deps' are + include directories and files that are used if $(CC) + is used as an intermediary step when generating the + end product (the file indicated by 'src'). 'intent' + indicates what the generated file is going to be + used for. + + src2obj - function that produces build file lines to build an + object file from source files and associated data. + + It's called like this: + + src2obj(obj => "PATH/TO/objectfile", + srcs => [ "PATH/TO/sourcefile", ... ], + deps => [ "dep1", ... ], + incs => [ "INCL/PATH", ... ] + intent => one of "lib", "dso", "bin" ); + + 'obj' has the intended object file *without* + extension, src2obj() is expected to add that. + 'srcs' has the list of source files to build the + object file, with the first item being the source + file that directly corresponds to the object file. + 'deps' is a list of explicit dependencies. 'incs' + is a list of include file directories. Finally, + 'intent' indicates what this object file is going + to be used for. + + obj2lib - function that produces build file lines to build a + static library file ("libfoo.a" in Unix terms) from + object files. + + called like this: + + obj2lib(lib => "PATH/TO/libfile", + objs => [ "PATH/TO/objectfile", ... ]); + + 'lib' has the intended library file name *without* + extension, obj2lib is expected to add that. 'objs' + has the list of object files (also *without* + extension) to build this library. + + libobj2shlib - function that produces build file lines to build a + shareable object library file ("libfoo.so" in Unix + terms) from the corresponding static library file + or object files. + + called like this: + + libobj2shlib(shlib => "PATH/TO/shlibfile", + lib => "PATH/TO/libfile", + objs => [ "PATH/TO/objectfile", ... ], + deps => [ "PATH/TO/otherlibfile", ... ], + ordinals => [ "word", "/PATH/TO/ordfile" ]); + + 'lib' has the intended library file name *without* + extension, libobj2shlib is expected to add that. + 'shlib' has the corresponding shared library name + *without* extension. 'deps' has the list of other + libraries (also *without* extension) this library + needs to be linked with. 'objs' has the list of + object files (also *without* extension) to build + this library. 'ordinals' MAY be present, and when + it is, its value is an array where the word is + "crypto" or "ssl" and the file is one of the ordinal + files util/libeay.num or util/ssleay.num in the + source directory. + + This function has a choice; it can use the + corresponding static library as input to make the + shared library, or the list of object files. + + obj2dso - function that produces build file lines to build a + dynamic shared object file from object files. + + called like this: + + obj2dso(lib => "PATH/TO/libfile", + objs => [ "PATH/TO/objectfile", ... ], + deps => [ "PATH/TO/otherlibfile", + ... ]); + + This is almost the same as libobj2shlib, but the + intent is to build a shareable library that can be + loaded in runtime (a "plugin"...). The differences + are subtle, one of the most visible ones is that the + resulting shareable library is produced from object + files only. + + obj2bin - function that produces build file lines to build an + executable file from object files. + + called like this: + + obj2bin(bin => "PATH/TO/binfile", + objs => [ "PATH/TO/objectfile", ... ], + deps => [ "PATH/TO/libfile", ... ]); + + 'bin' has the intended executable file name + *without* extension, obj2bin is expected to add + that. 'objs' has the list of object files (also + *without* extension) to build this library. 'deps' + has the list of library files (also *without* + extension) that the programs needs to be linked + with. + + in2script - function that produces build file lines to build a + script file from some input. + + called like this: + + in2script(script => "PATH/TO/scriptfile", + sources => [ "PATH/TO/infile", ... ]); + + 'script' has the intended script file name. + 'sources' has the list of source files to build the + resulting script from. + +In all cases, file file paths are relative to the build tree top, and +the build file actions run with the build tree top as current working +directory. + +Make sure to end the section with these functions with a string that +you thing is appropriate for the resulting build file. If nothing +else, end it like this: + + ""; # Make sure no lingering values end up in the Makefile + -} diff --git a/Configurations/README.design b/Configurations/README.design new file mode 100644 index 000000000..bea9790af --- /dev/null +++ b/Configurations/README.design @@ -0,0 +1,641 @@ +Design document for the unified scheme data +=========================================== + +How are things connected? +------------------------- + +The unified scheme takes all its data from the build.info files seen +throughout the source tree. These files hold the minimum information +needed to build end product files from diverse sources. See the +section on build.info files below. + +From the information in build.info files, Configure builds up an +information database as a hash table called %unified_info, which is +stored in configdata.pm, found at the top of the build tree (which may +or may not be the same as the source tree). + +Configurations/common.tmpl uses the data from %unified_info to +generate the rules for building end product files as well as +intermediary files with the help of a few functions found in the +build-file templates. See the section on build-file templates further +down for more information. + +build.info files +---------------- + +As mentioned earlier, build.info files are meant to hold the minimum +information needed to build output files, and therefore only (with a +few possible exceptions [1]) have information about end products (such +as scripts, library files and programs) and source files (such as C +files, C header files, assembler files, etc). Intermediate files such +as object files are rarely directly referred to in build.info files (and +when they are, it's always with the file name extension .o), they are +inferred by Configure. By the same rule of minimalism, end product +file name extensions (such as .so, .a, .exe, etc) are never mentioned +in build.info. Their file name extensions will be inferred by the +build-file templates, adapted for the platform they are meant for (see +sections on %unified_info and build-file templates further down). + +The variables PROGRAMS, LIBS, ENGINES and SCRIPTS are used to declare +end products. There are variants for them with '_NO_INST' as suffix +(PROGRAM_NO_INST etc) to specify end products that shouldn't get +installed. + +The variables SOURCE, DEPEND, INCLUDE and ORDINALS are indexed by a +produced file, and their values are the source used to produce that +particular produced file, extra dependencies, include directories +needed, and ordinal files (explained further below. + +All their values in all the build.info throughout the source tree are +collected together and form a set of programs, libraries, engines and +scripts to be produced, source files, dependencies, etc etc etc. + +Let's have a pretend example, a very limited contraption of OpenSSL, +composed of the program 'apps/openssl', the libraries 'libssl' and +'libcrypto', an engine 'engines/ossltest' and their sources and +dependencies. + + # build.info + LIBS=libcrypto libssl + ORDINALS[libcrypto]=crypto + ORDINALS[libssl]=ssl + INCLUDE[libcrypto]=include + INCLUDE[libssl]=include + DEPEND[libssl]=libcrypto + +This is the top directory build.info file, and it tells us that two +libraries are to be built, there are some ordinals to be used to +declare what symbols in those libraries are seen as public, the +include directory 'include/' shall be used throughout when building +anything that will end up in each library, and that the library +'libssl' depend on the library 'libcrypto' to function properly. + + # apps/build.info + PROGRAMS=openssl + SOURCE[openssl]=openssl.c + INCLUDE[openssl]=.. ../include + DEPEND[openssl]=../libssl + +This is the build.info file in 'apps/', one may notice that all file +paths mentioned are relative to the directory the build.info file is +located in. This one tells us that there's a program to be built +called 'apps/openssl' (the file name extension will depend on the +platform and is therefore not mentioned in the build.info file). It's +built from one source file, 'apps/openssl.c', and building it requires +the use of '.' and 'include' include directories (both are declared +from the point of view of the 'apps/' directory), and that the program +depends on the library 'libssl' to function properly. + + # crypto/build.info + LIBS=../libcrypto + SOURCE[../libcrypto]=aes.c evp.c cversion.c + DEPEND[cversion.o]=buildinf.h + + GENERATE[buildinf.h]=../util/mkbuildinf.pl "$(CC) $(CFLAGS)" "$(PLATFORM)" + DEPEND[buildinf.h]=../Makefile + DEPEND[../util/mkbuildinf.pl]=../util/Foo.pm + +This is the build.info file in 'crypto', and it tells us a little more +about what's needed to produce 'libcrypto'. LIBS is used again to +declare that 'libcrypto' is to be produced. This declaration is +really unnecessary as it's already mentioned in the top build.info +file, but can make the info file easier to understand. This is to +show that duplicate information isn't an issue. + +This build.info file informs us that 'libcrypto' is built from a few +source files, 'crypto/aes.c', 'crypto/evp.c' and 'crypto/cversion.c'. +It also shows us that building the object file inferred from +'crypto/cversion.c' depends on 'crypto/buildinf.h'. Finally, it +also shows the possibility to declare how some files are generated +using some script, in this case a perl script, and how such scripts +can be declared to depend on other files, in this case a perl module. + +Two things are worth an extra note: + +'DEPEND[cversion.o]' mentions an object file. DEPEND indexes is the +only location where it's valid to mention them + +Lines in 'BEGINRAW'..'ENDRAW' sections must always mention files as +seen from the top directory, no exception. + + # ssl/build.info + LIBS=../libssl + SOURCE[../libssl]=tls.c + +This is the build.info file in 'ssl/', and it tells us that the +library 'libssl' is built from the source file 'ssl/tls.c'. + + # engines/build.info + ENGINES=dasync + SOURCE[dasync]=e_dasync.c + DEPEND[dasync]=../libcrypto + INCLUDE[dasync]=../include + + ENGINES_NO_INST=ossltest + SOURCE[ossltest]=e_ossltest.c + DEPEND[ossltest]=../libcrypto + INCLUDE[ossltest]=../include + +This is the build.info file in 'engines/', telling us that two engines +called 'engines/dasync' and 'engines/ossltest' shall be built, that +dasync's source is 'engines/e_dasync.c' and ossltest's source is +'engines/e_ossltest.c' and that the include directory 'include/' may +be used when building anything that will be part of these engines. +Also, both engines depend on the library 'libcrypto' to function +properly. Finally, only dasync is being installed, as ossltest is +only for internal testing. + +When Configure digests these build.info files, the accumulated +information comes down to this: + + LIBS=libcrypto libssl + ORDINALS[libcrypto]=crypto + SOURCE[libcrypto]=crypto/aes.c crypto/evp.c crypto/cversion.c + DEPEND[crypto/cversion.o]=crypto/buildinf.h + INCLUDE[libcrypto]=include + ORDINALS[libssl]=ssl + SOURCE[libssl]=ssl/tls.c + INCLUDE[libssl]=include + DEPEND[libssl]=libcrypto + + PROGRAMS=apps/openssl + SOURCE[apps/openssl]=apps/openssl.c + INCLUDE[apps/openssl]=. include + DEPEND[apps/openssl]=libssl + + ENGINES=engines/dasync + SOURCE[engines/dasync]=engines/e_dasync.c + DEPEND[engines/dasync]=libcrypto + INCLUDE[engines/dasync]=include + + ENGINES_NO_INST=engines/ossltest + SOURCE[engines/ossltest]=engines/e_ossltest.c + DEPEND[engines/ossltest]=libcrypto + INCLUDE[engines/ossltest]=include + + GENERATE[crypto/buildinf.h]=util/mkbuildinf.pl "$(CC) $(CFLAGS)" "$(PLATFORM)" + DEPEND[crypto/buildinf.h]=Makefile + DEPEND[util/mkbuildinf.pl]=util/Foo.pm + + +A few notes worth mentioning: + +LIBS may be used to declare routine libraries only. + +PROGRAMS may be used to declare programs only. + +ENGINES may be used to declare engines only. + +The indexes for SOURCE and ORDINALS must only be end product files, +such as libraries, programs or engines. The values of SOURCE +variables must only be source files (possibly generated) + +INCLUDE and DEPEND shows a relationship between different files +(usually produced files) or between files and directories, such as a +program depending on a library, or between an object file and some +extra source file. + +When Configure processes the build.info files, it will take it as +truth without question, and will therefore perform very few checks. +If the build tree is separate from the source tree, it will assume +that all built files and up in the build directory and that all source +files are to be found in the source tree, if they can be found there. +Configure will assume that source files that can't be found in the +source tree (such as 'crypto/bildinf.h' in the example above) are +generated and will be found in the build tree. + + +The %unified_info database +-------------------------- + +The information in all the build.info get digested by Configure and +collected into the %unified_info database, divided into the following +indexes: + + depends => a hash table containing 'file' => [ 'dependency' ... ] + pairs. These are directly inferred from the DEPEND + variables in build.info files. + + engines => a list of engines. These are directly inferred from + the ENGINES variable in build.info files. + + generate => a hash table containing 'file' => [ 'generator' ... ] + pairs. These are directly inferred from the GENERATE + variables in build.info files. + + includes => a hash table containing 'file' => [ 'include' ... ] + pairs. These are directly inferred from the INCLUDE + variables in build.info files. + + install => a hash table containing 'type' => [ 'file' ... ] pairs. + The types are 'programs', 'libraries', 'engines' and + 'scripts', and the array of files list the files of + that type that should be installed. + + libraries => a list of libraries. These are directly inferred from + the LIBS variable in build.info files. + + ordinals => a hash table containing 'file' => [ 'word', 'ordfile' ] + pairs. 'file' and 'word' are directly inferred from + the ORDINALS variables in build.info files, while the + file 'ofile' comes from internal knowledge in + Configure. + + programs => a list of programs. These are directly inferred from + the PROGRAMS variable in build.info files. + + rawlines => a list of build-file lines. These are a direct copy of + the BEGINRAW..ENDRAW lines in build.info files. Note: + only the BEGINRAW..ENDRAW section for the current + platform are copied, the rest are ignored. + + scripts => a list of scripts. There are directly inferred from + the SCRIPTS variable in build.info files. + + sources => a hash table containing 'file' => [ 'sourcefile' ... ] + pairs. These are indirectly inferred from the SOURCE + variables in build.info files. Object files are + mentioned in this hash table, with source files from + SOURCE variables, and AS source files for programs and + libraries. + + shared_sources => + a hash table just like 'sources', but only as source + files (object files) for building shared libraries. + +As an example, here is how the build.info files example from the +section above would be digested into a %unified_info table: + + our %unified_info = ( + "depends" => + { + "apps/openssl" => + [ + "libssl", + ], + "crypto/buildinf.h" => + [ + "Makefile", + ], + "crypto/cversion.o" => + [ + "crypto/buildinf.h", + ], + "engines/ossltest" => + [ + "libcrypto", + ], + "libssl" => + [ + "libcrypto", + ], + "util/mkbuildinf.pl" => + [ + "util/Foo.pm", + ], + }, + "engines" => + [ + "engines/dasync", + "engines/ossltest", + ], + "generate" => + { + "crypto/buildinf.h" => + [ + "util/mkbuildinf.pl", + "\"\$(CC)", + "\$(CFLAGS)\"", + "\"$(PLATFORM)\"", + ], + }, + "includes" => + { + "apps/openssl" => + [ + ".", + "include", + ], + "engines/ossltest" => + [ + "include" + ], + "libcrypto" => + [ + "include", + ], + "libssl" => + [ + "include", + ], + "util/mkbuildinf.pl" => + [ + "util", + ], + } + "install" => + { + "engines" => + [ + "engines/dasync", + ], + "libraries" => + [ + "libcrypto", + "libssl", + ], + "programs" => + [ + "apps/openssl", + ], + }, + "libraries" => + [ + "libcrypto", + "libssl", + ], + "ordinals" => + { + "libcrypto" => + [ + "crypto", + "util/libcrypto.num", + ], + "libssl" => + [ + "ssl", + "util/libssl.num", + ], + }, + "programs" => + [ + "apps/openssl", + ], + "rawlines" => + [ + ], + "sources" => + { + "apps/openssl" => + [ + "apps/openssl.o", + ], + "apps/openssl.o" => + [ + "apps/openssl.c", + ], + "crypto/aes.o" => + [ + "crypto/aes.c", + ], + "crypto/cversion.o" => + [ + "crypto/cversion.c", + ], + "crypto/evp.o" => + [ + "crypto/evp.c", + ], + "engines/e_ossltest.o" => + [ + "engines/e_ossltest.c", + ], + "engines/ossltest" => + [ + "engines/e_ossltest.o", + ], + "libcrypto" => + [ + "crypto/aes.c", + "crypto/cversion.c", + "crypto/evp.c", + ], + "libssl" => + [ + "ssl/tls.c", + ], + "ssl/tls.o" => + [ + "ssl/tls.c", + ], + }, + ); + +As can be seen, everything in %unified_info is fairly simple suggest +of information. Still, it tells us that to build all programs, we +must build 'apps/openssl', and to build the latter, we will need to +build all its sources ('apps/openssl.o' in this case) and all the +other things it depends on (such as 'libssl'). All those dependencies +need to be built as well, using the same logic, so to build 'libssl', +we need to build 'ssl/tls.o' as well as 'libcrypto', and to build the +latter... + + +Build-file templates +-------------------- + +Build-file templates are essentially build-files (such as Makefile on +Unix) with perl code fragments mixed in. Those perl code fragment +will generate all the configuration dependent data, including all the +rules needed to build end product files and intermediary files alike. +At a minimum, there must be a perl code fragment that defines a set of +functions that are used to generates specific build-file rules, to +build static libraries from object files, to build shared libraries +from static libraries, to programs from object files and libraries, +etc. + + generatesrc - function that produces build file lines to generate + a source file from some input. + + It's called like this: + + generatesrc(src => "PATH/TO/tobegenerated", + generator => [ "generatingfile", ... ] + generator_incs => [ "INCL/PATH", ... ] + generator_deps => [ "dep1", ... ] + incs => [ "INCL/PATH", ... ], + deps => [ "dep1", ... ], + intent => one of "libs", "dso", "bin" ); + + 'src' has the name of the file to be generated. + 'generator' is the command or part of command to + generate the file, of which the first item is + expected to be the file to generate from. + generatesrc() is expected to analyse and figure out + exactly how to apply that file and how to capture + the result. 'generator_incs' and 'generator_deps' + are include directories and files that the generator + file itself depends on. 'incs' and 'deps' are + include directories and files that are used if $(CC) + is used as an intermediary step when generating the + end product (the file indicated by 'src'). 'intent' + indicates what the generated file is going to be + used for. + + src2obj - function that produces build file lines to build an + object file from source files and associated data. + + It's called like this: + + src2obj(obj => "PATH/TO/objectfile", + srcs => [ "PATH/TO/sourcefile", ... ], + deps => [ "dep1", ... ], + incs => [ "INCL/PATH", ... ] + intent => one of "lib", "dso", "bin" ); + + 'obj' has the intended object file *without* + extension, src2obj() is expected to add that. + 'srcs' has the list of source files to build the + object file, with the first item being the source + file that directly corresponds to the object file. + 'deps' is a list of explicit dependencies. 'incs' + is a list of include file directories. Finally, + 'intent' indicates what this object file is going + to be used for. + + obj2lib - function that produces build file lines to build a + static library file ("libfoo.a" in Unix terms) from + object files. + + called like this: + + obj2lib(lib => "PATH/TO/libfile", + objs => [ "PATH/TO/objectfile", ... ]); + + 'lib' has the intended library file name *without* + extension, obj2lib is expected to add that. 'objs' + has the list of object files (also *without* + extension) to build this library. + + libobj2shlib - function that produces build file lines to build a + shareable object library file ("libfoo.so" in Unix + terms) from the corresponding static library file + or object files. + + called like this: + + libobj2shlib(shlib => "PATH/TO/shlibfile", + lib => "PATH/TO/libfile", + objs => [ "PATH/TO/objectfile", ... ], + deps => [ "PATH/TO/otherlibfile", ... ], + ordinals => [ "word", "/PATH/TO/ordfile" ]); + + 'lib' has the intended library file name *without* + extension, libobj2shlib is expected to add that. + 'shlib' has the corresponding shared library name + *without* extension. 'deps' has the list of other + libraries (also *without* extension) this library + needs to be linked with. 'objs' has the list of + object files (also *without* extension) to build + this library. 'ordinals' MAY be present, and when + it is, its value is an array where the word is + "crypto" or "ssl" and the file is one of the ordinal + files util/libcrypto.num or util/libssl.num in the + source directory. + + This function has a choice; it can use the + corresponding static library as input to make the + shared library, or the list of object files. + + obj2dynlib - function that produces build file lines to build a + dynamically loadable library file ("libfoo.so" on + Unix) from object files. + + called like this: + + obj2dynlib(lib => "PATH/TO/libfile", + objs => [ "PATH/TO/objectfile", ... ], + deps => [ "PATH/TO/otherlibfile", + ... ]); + + This is almost the same as libobj2shlib, but the + intent is to build a shareable library that can be + loaded in runtime (a "plugin"...). The differences + are subtle, one of the most visible ones is that the + resulting shareable library is produced from object + files only. + + obj2bin - function that produces build file lines to build an + executable file from object files. + + called like this: + + obj2bin(bin => "PATH/TO/binfile", + objs => [ "PATH/TO/objectfile", ... ], + deps => [ "PATH/TO/libfile", ... ]); + + 'bin' has the intended executable file name + *without* extension, obj2bin is expected to add + that. 'objs' has the list of object files (also + *without* extension) to build this library. 'deps' + has the list of library files (also *without* + extension) that the programs needs to be linked + with. + + in2script - function that produces build file lines to build a + script file from some input. + + called like this: + + in2script(script => "PATH/TO/scriptfile", + sources => [ "PATH/TO/infile", ... ]); + + 'script' has the intended script file name. + 'sources' has the list of source files to build the + resulting script from. + +Along with the build-file templates is the driving engine +Configurations/common.tmpl, which looks through all the information in +%unified_info and generates all the rulesets to build libraries, +programs and all intermediate files, using the rule generating +functions defined in the build-file template. + +As an example with the smaller build.info set we've seen as an +example, producing the rules to build 'libcrypto' would result in the +following calls: + + # Note: libobj2shlib will only be called if shared libraries are + # to be produced. + # Note 2: libobj2shlib gets both the name of the static library + # and the names of all the object files that go into it. It's up + # to the implementation to decide which to use as input. + # Note 3: common.tmpl peals off the ".o" extension from all object + # files, as the platform at hand may have a different one. + libobj2shlib(shlib => "libcrypto", + lib => "libcrypto", + objs => [ "crypto/aes", "crypto/evp", "crypto/cversion" ], + deps => [ ] + ordinals => [ "crypto", "util/libcrypto.num" ]); + + obj2lib(lib => "libcrypto" + objs => [ "crypto/aes", "crypto/evp", "crypto/cversion" ]); + + src2obj(obj => "crypto/aes" + srcs => [ "crypto/aes.c" ], + deps => [ ], + incs => [ "include" ], + intent => "lib"); + + src2obj(obj => "crypto/evp" + srcs => [ "crypto/evp.c" ], + deps => [ ], + incs => [ "include" ], + intent => "lib"); + + src2obj(obj => "crypto/cversion" + srcs => [ "crypto/cversion.c" ], + deps => [ "crypto/buildinf.h" ], + incs => [ "include" ], + intent => "lib"); + + generatesrc(src => "crypto/buildinf.h", + generator => [ "util/mkbuildinf.pl", "\"$(CC)", + "$(CFLAGS)\"", "\"$(PLATFORM)\"" ], + generator_incs => [ "util" ], + generator_deps => [ "util/Foo.pm" ], + incs => [ ], + deps => [ ], + intent => "lib"); + +The returned strings from all those calls are then concatenated +together and written to the resulting build-file. diff --git a/Configurations/common.tmpl b/Configurations/common.tmpl new file mode 100644 index 000000000..9d7fbf2e7 --- /dev/null +++ b/Configurations/common.tmpl @@ -0,0 +1,231 @@ +{- # -*- Mode: perl -*- + + use File::Basename; + + # A cache of objects for which a recipe has already been generated + my %cache; + + # resolvedepends and reducedepends work in tandem to make sure + # there are no duplicate dependencies and that they are in the + # right order. This is especially used to sort the list of + # libraries that a build depends on. + sub resolvedepends { + my $thing = shift; + my @listsofar = @_; # to check if we're looping + my @list = @{$unified_info{depends}->{$thing}}; + my @newlist = (); + if (scalar @list) { + foreach my $item (@list) { + # It's time to break off when the dependency list starts looping + next if grep { $_ eq $item } @listsofar; + push @newlist, $item, resolvedepends($item, @listsofar, $item); + } + } + @newlist; + } + sub reducedepends { + my @list = @_; + my @newlist = (); + while (@list) { + my $item = shift @list; + push @newlist, $item + unless grep { $item eq $_ } @list; + } + @newlist; + } + + # dogenerate is responsible for producing all the recipes that build + # generated source files. It recurses in case a dependency is also a + # generated source file. + sub dogenerate { + my $src = shift; + return "" if $cache{$src}; + my $obj = shift; + my $bin = shift; + my %opts = @_; + if ($unified_info{generate}->{$src}) { + die "$src is generated by Configure, should not appear in build file\n" + if ref $unified_info{generate}->{$src} eq ""; + my $script = $unified_info{generate}->{$src}->[0]; + $OUT .= generatesrc(src => $src, + generator => $unified_info{generate}->{$src}, + generator_incs => $unified_info{includes}->{$script}, + generator_deps => $unified_info{depends}->{$script}, + deps => $unified_info{depends}->{$src}, + incs => [ @{$unified_info{includes}->{$bin}}, + @{$unified_info{includes}->{$obj}} ], + %opts); + foreach (@{$unified_info{depends}->{$src}}) { + dogenerate($_, $obj, $bin, %opts); + } + } + $cache{$src} = 1; + } + + # doobj is responsible for producing all the recipes that build + # object files as well as dependency files. + sub doobj { + my $obj = shift; + return "" if $cache{$obj}; + (my $obj_no_o = $obj) =~ s|\.o$||; + my $bin = shift; + my %opts = @_; + if (@{$unified_info{sources}->{$obj}}) { + $OUT .= src2obj(obj => $obj_no_o, + srcs => $unified_info{sources}->{$obj}, + deps => $unified_info{depends}->{$obj}, + incs => [ @{$unified_info{includes}->{$bin}}, + @{$unified_info{includes}->{$obj}} ], + %opts); + foreach ((@{$unified_info{sources}->{$obj}}, + @{$unified_info{depends}->{$obj}})) { + dogenerate($_, $obj, $bin, %opts); + } + } + $cache{$obj} = 1; + } + + # dolib is responsible for building libraries. It will call + # libobj2shlib is shared libraries are produced, and obj2lib in all + # cases. It also makes sure all object files for the library are + # built. + sub dolib { + my $lib = shift; + return "" if $cache{$lib}; + unless ($disabled{shared}) { + my %ordinals = + $unified_info{ordinals}->{$lib} + ? (ordinals => $unified_info{ordinals}->{$lib}) : (); + $OUT .= libobj2shlib(shlib => $unified_info{sharednames}->{$lib}, + lib => $lib, + objs => [ map { (my $x = $_) =~ s|\.o$||; $x } + (@{$unified_info{sources}->{$lib}}, + @{$unified_info{shared_sources}->{$lib}}) ], + deps => [ reducedepends(resolvedepends($lib)) ], + %ordinals); + foreach (@{$unified_info{shared_sources}->{$lib}}) { + doobj($_, $lib, intent => "lib"); + } + } + $OUT .= obj2lib(lib => $lib, + objs => [ map { (my $x = $_) =~ s|\.o$||; $x } + @{$unified_info{sources}->{$lib}} ]); + foreach (@{$unified_info{sources}->{$lib}}) { + doobj($_, $lib, intent => "lib"); + } + $cache{$lib} = 1; + } + + # doengine is responsible for building engines. It will call + # obj2dso, and also makes sure all object files for the library + # are built. + sub doengine { + my $lib = shift; + return "" if $cache{$lib}; + $OUT .= obj2dso(lib => $lib, + objs => [ map { (my $x = $_) =~ s|\.o$||; $x } + (@{$unified_info{sources}->{$lib}}, + @{$unified_info{shared_sources}->{$lib}}) ], + deps => [ resolvedepends($lib) ]); + foreach ((@{$unified_info{sources}->{$lib}}, + @{$unified_info{shared_sources}->{$lib}})) { + doobj($_, $lib, intent => "dso"); + } + $cache{$lib} = 1; + } + + # dobin is responsible for building programs. It will call obj2bin, + # and also makes sure all object files for the library are built. + sub dobin { + my $bin = shift; + return "" if $cache{$bin}; + my $deps = [ reducedepends(resolvedepends($bin)) ]; + $OUT .= obj2bin(bin => $bin, + objs => [ map { (my $x = $_) =~ s|\.o$||; $x } + @{$unified_info{sources}->{$bin}} ], + deps => $deps); + foreach (@{$unified_info{sources}->{$bin}}) { + doobj($_, $bin, intent => "bin"); + } + $cache{$bin} = 1; + } + + # dobin is responsible for building scripts from templates. It will + # call in2script. + sub doscript { + my $script = shift; + return "" if $cache{$script}; + $OUT .= in2script(script => $script, + sources => $unified_info{sources}->{$script}); + $cache{$script} = 1; + } + + sub dodir { + my $dir = shift; + return "" if !exists(&generatedir) or $cache{$dir}; + $OUT .= generatedir(dir => $dir, + deps => $unified_info{dirinfo}->{$dir}->{deps}, + %{$unified_info{dirinfo}->{$_}->{products}}); + $cache{$dir} = 1; + } + + # Start with populating the cache with all the overrides + %cache = map { $_ => 1 } @{$unified_info{overrides}}; + + # For convenience collect information regarding directories where + # files are generated, those generated files and the end product + # they end up in where applicable. Then, add build rules for those + # directories + if (exists &generatedir) { + my %loopinfo = ( "dso" => [ @{$unified_info{engines}} ], + "lib" => [ @{$unified_info{libraries}} ], + "bin" => [ @{$unified_info{programs}} ], + "script" => [ @{$unified_info{scripts}} ] ); + foreach my $type (keys %loopinfo) { + foreach my $product (@{$loopinfo{$type}}) { + my %dirs = (); + my $pd = dirname($product); + + # We already have a "test" target, and the current directory + # is just silly to make a target for + $dirs{$pd} = 1 unless $pd eq "test" || $pd eq "."; + + foreach (@{$unified_info{sources}->{$product}}) { + my $d = dirname($_); + + # We don't want to create targets for source directories + # when building out of source + next if ($config{sourcedir} ne $config{builddir} + && $d =~ m|^\Q$config{sourcedir}\E|); + # We already have a "test" target, and the current directory + # is just silly to make a target for + next if $d eq "test" || $d eq "."; + + $dirs{$d} = 1; + push @{$unified_info{dirinfo}->{$d}->{deps}}, $_ + if $d ne $pd; + } + foreach (keys %dirs) { + push @{$unified_info{dirinfo}->{$_}->{products}->{$type}}, + $product; + } + } + } + } + + # Build mandatory generated headers + foreach (@{$unified_info{depends}->{""}}) { dogenerate($_); } + + # Build all known libraries, engines, programs and scripts. + # Everything else will be handled as a consequence. + foreach (@{$unified_info{libraries}}) { dolib($_); } + foreach (@{$unified_info{engines}}) { doengine($_); } + foreach (@{$unified_info{programs}}) { dobin($_); } + foreach (@{$unified_info{scripts}}) { doscript($_); } + + foreach (sort keys %{$unified_info{dirinfo}}) { dodir($_); } + + # Finally, should there be any applicable BEGINRAW/ENDRAW sections, + # they are added here. + $OUT .= $_."\n" foreach @{$unified_info{rawlines}}; +-} diff --git a/Configurations/descrip.mms.tmpl b/Configurations/descrip.mms.tmpl new file mode 100644 index 000000000..da57049c2 --- /dev/null +++ b/Configurations/descrip.mms.tmpl @@ -0,0 +1,767 @@ +## descrip.mms to build OpenSSL on OpenVMS +## +## {- join("\n## ", @autowarntext) -} +{- + use File::Spec::Functions qw/:DEFAULT abs2rel rel2abs/; + + # Our prefix, claimed when speaking with the VSI folks Tuesday + # January 26th 2016 + our $osslprefix = 'OSSL$'; + (our $osslprefix_q = $osslprefix) =~ s/\$/\\\$/; + + our $sover = sprintf "%02d%02d", $config{shlib_major}, $config{shlib_minor}; + our $osslver = sprintf "%02d%02d", split(/\./, $config{version}); + + our $sourcedir = $config{sourcedir}; + our $builddir = $config{builddir}; + sub sourcefile { + catfile($sourcedir, @_); + } + sub buildfile { + catfile($builddir, @_); + } + sub sourcedir { + catdir($sourcedir, @_); + } + sub builddir { + catdir($builddir, @_); + } + sub tree { + (my $x = shift) =~ s|\]$|...]|; + $x + } + sub move { + my $f = catdir(@_); + my $b = abs2rel(rel2abs("."),rel2abs($f)); + $sourcedir = catdir($b,$sourcedir) + if !file_name_is_absolute($sourcedir); + $builddir = catdir($b,$builddir) + if !file_name_is_absolute($builddir); + ""; + } + + # Because we need to make two computations of these data, + # we store them in arrays for reuse + our @shlibs = map { $unified_info{sharednames}->{$_} || () } @{$unified_info{libraries}}; + our @install_shlibs = map { $unified_info{sharednames}->{$_} || () } @{$unified_info{install}->{libraries}}; + our @generated = ( ( map { (my $x = $_) =~ s|\.S$|\.s|; $x } + grep { defined $unified_info{generate}->{$_} } + map { @{$unified_info{sources}->{$_}} } + grep { /\.o$/ } keys %{$unified_info{sources}} ), + ( grep { /\.h$/ } keys %{$unified_info{generate}} ) ); + + # This is a horrible hack, but is needed because recursive inclusion of files + # in different directories does not work well with HP C. + my $sd = sourcedir("crypto", "async", "arch"); + foreach (grep /\[\.crypto\.async\.arch\].*\.o$/, keys %{$unified_info{sources}}) { + (my $x = $_) =~ s|\.o$|.OBJ|; + $unified_info{before}->{$x} + = qq(arch_include = F\$PARSE("$sd","A.;",,,"SYNTAX_ONLY") - "A.;" + define arch 'arch_include'); + $unified_info{after}->{$x} + = qq(deassign arch); + } + my $sd1 = sourcedir("ssl","record"); + my $sd2 = sourcedir("ssl","statem"); + $unified_info{before}->{"[.test]heartbeat_test.OBJ"} + = $unified_info{before}->{"[.test]ssltest_old.OBJ"} + = qq(record_include = F\$PARSE("$sd1","A.;",,,"SYNTAX_ONLY") - "A.;" + define record 'record_include' + statem_include = F\$PARSE("$sd2","A.;",,,"SYNTAX_ONLY") - "A.;" + define statem 'statem_include'); + $unified_info{after}->{"[.test]heartbeat_test.OBJ"} + = $unified_info{after}->{"[.test]ssltest.OBJ"} + = qq(deassign statem + deassign record); + foreach (grep /^\[\.ssl\.(?:record|statem)\].*\.o$/, keys %{$unified_info{sources}}) { + (my $x = $_) =~ s|\.o$|.OBJ|; + $unified_info{before}->{$x} + = qq(record_include = F\$PARSE("$sd1","A.;",,,"SYNTAX_ONLY") - "A.;" + define record 'record_include' + statem_include = F\$PARSE("$sd2","A.;",,,"SYNTAX_ONLY") - "A.;" + define statem 'statem_include'); + $unified_info{after}->{$x} + = qq(deassign statem + deassign record); + } + #use Data::Dumper; + #print STDERR "DEBUG: before:\n", Dumper($unified_info{before}); + #print STDERR "DEBUG: after:\n", Dumper($unified_info{after}); + ""; +-} +PLATFORM={- $config{target} -} +OPTIONS={- $config{options} -} +CONFIGURE_ARGS=({- join(", ",quotify_l(@{$config{perlargv}})) -}) +SRCDIR={- $config{sourcedir} -} +BLDDIR={- $config{builddir} -} + +# Allow both V and VERBOSE to indicate verbosity. This only applies +# to testing. +VERBOSE=$(V) + +VERSION={- $config{version} -} +MAJOR={- $config{major} -} +MINOR={- $config{minor} -} +SHLIB_VERSION_NUMBER={- $config{shlib_version_number} -} +SHLIB_VERSION_HISTORY={- $config{shlib_version_history} -} +SHLIB_MAJOR={- $config{shlib_major} -} +SHLIB_MINOR={- $config{shlib_minor} -} +SHLIB_TARGET={- $target{shared_target} -} + +EXE_EXT=.EXE +LIB_EXT=.OLB +SHLIB_EXT=.EXE +OBJ_EXT=.OBJ +DEP_EXT=.D + +LIBS={- join(", ", map { "-\n\t".$_.".OLB" } @{$unified_info{libraries}}) -} +SHLIBS={- join(", ", map { "-\n\t".$_.".EXE" } @shlibs) -} +ENGINES={- join(", ", map { "-\n\t".$_.".EXE" } @{$unified_info{engines}}) -} +PROGRAMS={- join(", ", map { "-\n\t".$_.".EXE" } @{$unified_info{programs}}) -} +SCRIPTS={- join(", ", map { "-\n\t".$_ } @{$unified_info{scripts}}) -} +{- output_off() if $disabled{makedepend}; "" -} +DEPS={- our @deps = map { (my $x = $_) =~ s|\.o$|\$(DEP_EXT)|; $x; } + grep { $unified_info{sources}->{$_}->[0] =~ /\.c$/ } + keys %{$unified_info{sources}}; + join(", ", map { "-\n\t".$_ } @deps); -} +{- output_on() if $disabled{makedepend}; "" -} +GENERATED_MANDATORY={- join(", ", map { "-\n\t".$_ } @{$unified_info{depends}->{""}} ) -} +GENERATED={- join(", ", map { "-\n\t".$_ } @generated) -} + +INSTALL_LIBS={- join(", ", map { "-\n\t".$_.".OLB" } @{$unified_info{install}->{libraries}}) -} +INSTALL_SHLIBS={- join(", ", map { "-\n\t".$_.".EXE" } @install_shlibs) -} +INSTALL_ENGINES={- join(", ", map { "-\n\t".$_.".EXE" } @{$unified_info{install}->{engines}}) -} +INSTALL_PROGRAMS={- join(", ", map { "-\n\t".$_.".EXE" } @{$unified_info{install}->{programs}}) -} +{- output_off() if $disabled{apps}; "" -} +BIN_SCRIPTS=[.tools]c_rehash.pl +MISC_SCRIPTS=[.apps]CA.pl, [.apps]tsget.pl +{- output_on() if $disabled{apps}; "" -} + +# DESTDIR is for package builders so that they can configure for, say, +# SYS$COMMON:[OPENSSL] and yet have everything installed in STAGING:[USER]. +# In that case, configure with --prefix=SYS$COMMON:[OPENSSL] and then run +# MMS with /MACROS=(DESTDIR=STAGING:[USER]). The result will end up in +# STAGING:[USER.OPENSSL]. +# Normally it is left empty. +DESTDIR= + +# Do not edit this manually. Use Configure --prefix=DIR to change this! +INSTALLTOP={- our $installtop = + catdir($config{prefix}) || "SYS\$COMMON:[OPENSSL]"; + $installtop -} +SYSTARTUP={- catdir($installtop, '[.SYS$STARTUP]'); -} +# This is the standard central area to store certificates, private keys... +OPENSSLDIR={- catdir($config{openssldir}) or + $config{prefix} ? catdir($config{prefix},"COMMON") + : "SYS\$COMMON:[OPENSSL-COMMON]" -} +# The same, but for C +OPENSSLDIR_C={- $osslprefix -}DATAROOT:[000000] +# Where installed engines reside, for C +ENGINESDIR_C={- $osslprefix -}ENGINES{- $sover.$target{pointer_size} -}: + +CC= {- $target{cc} -} +CFLAGS= /DEFINE=({- join(",", @{$target{defines}}, @{$config{defines}},"OPENSSLDIR=\"\"\"\$(OPENSSLDIR_C)\"\"\"","ENGINESDIR=\"\"\"\$(ENGINESDIR_C)\"\"\"") -}) {- $target{cflags} -} {- $config{cflags} -} +CFLAGS_Q=$(CFLAGS) +DEPFLAG= /DEFINE=({- join(",", @{$config{depdefines}}) -}) +LDFLAGS= {- $target{lflags} -} +EX_LIBS= {- $target{ex_libs} ? ",".$target{ex_libs} : "" -}{- $config{ex_libs} ? ",".$config{ex_libs} : "" -} +LIB_CFLAGS={- $target{lib_cflags} || "" -} +DSO_CFLAGS={- $target{dso_cflags} || "" -} +BIN_CFLAGS={- $target{bin_cflags} || "" -} + +PERL={- $config{perl} -} + +# We let the C compiler driver to take care of .s files. This is done in +# order to be excused from maintaining a separate set of architecture +# dependent assembler flags. E.g. if you throw -mcpu=ultrasparc at SPARC +# gcc, then the driver will automatically translate it to -xarch=v8plus +# and pass it down to assembler. +AS={- $target{as} -} +ASFLAG={- $target{asflags} -} + +# .FIRST and .LAST are special targets with MMS and MMK. +# The defines in there are for C. includes that look like +# this: +# +# #include +# #include "internal/bar.h" +# +# will use the logical names to find the files. Expecting +# DECompHP C to find files in subdirectories of whatever was +# given with /INCLUDE is a fantasy, unfortunately. +NODEBUG=@ +.FIRST : + $(NODEBUG) openssl_inc1 = F$PARSE("[.include.openssl]","A.;",,,"syntax_only") - "A.;" + $(NODEBUG) openssl_inc2 = F$PARSE("{- catdir($config{sourcedir},"[.include.openssl]") -}","A.;",,,"SYNTAX_ONLY") - "A.;" + $(NODEBUG) internal_inc1 = F$PARSE("[.crypto.include.internal]","A.;",,,"SYNTAX_ONLY") - "A.;" + $(NODEBUG) internal_inc2 = F$PARSE("{- catdir($config{sourcedir},"[.include.internal]") -}","A.;",,,"SYNTAX_ONLY") - "A.;" + $(NODEBUG) internal_inc3 = F$PARSE("{- catdir($config{sourcedir},"[.crypto.include.internal]") -}","A.;",,,"SYNTAX_ONLY") - "A.;" + $(NODEBUG) DEFINE openssl 'openssl_inc1','openssl_inc2' + $(NODEBUG) DEFINE internal 'internal_inc1','internal_inc2','internal_inc3' + $(NODEBUG) staging_dir = "$(DESTDIR)" + $(NODEBUG) staging_instdir = "" + $(NODEBUG) staging_datadir = "" + $(NODEBUG) IF staging_dir .NES. "" THEN - + staging_instdir = F$PARSE("A.;",staging_dir,"[]",,"SYNTAX_ONLY") + $(NODEBUG) IF staging_instdir - "]A.;" .NES. staging_instdir THEN - + staging_instdir = staging_instdir - "]A.;" + ".OPENSSL-INSTALL]" + $(NODEBUG) IF staging_instdir - "A.;" .NES. staging_instdir THEN - + staging_instdir = staging_instdir - "A.;" + "[OPENSSL-INSTALL]" + $(NODEBUG) IF staging_dir .NES. "" THEN - + staging_datadir = F$PARSE("A.;",staging_dir,"[]",,"SYNTAX_ONLY") + $(NODEBUG) IF staging_datadir - "]A.;" .NES. staging_datadir THEN - + staging_datadir = staging_datadir - "]A.;" + ".OPENSSL-COMMON]" + $(NODEBUG) IF staging_datadir - "A.;" .NES. staging_datadir THEN - + staging_datadir = staging_datadir - "A.;" + "[OPENSSL-COMMON]" + $(NODEBUG) ! + $(NODEBUG) ! Installation logical names + $(NODEBUG) ! + $(NODEBUG) installtop = F$PARSE(staging_instdir,"$(INSTALLTOP)","[]A.;",,"SYNTAX_ONLY,NO_CONCEAL") - ".][000000" - "[000000." - "][" - "]A.;" + ".]" + $(NODEBUG) datatop = F$PARSE(staging_datadir,"$(OPENSSLDIR)","[]A.;",,"SYNTAX_ONLY,NO_CONCEAL") - ".][000000" - "[000000." - "][" - "]A.;" + ".]" + $(NODEBUG) DEFINE ossl_installroot 'installtop' + $(NODEBUG) DEFINE ossl_dataroot 'datatop' + $(NODEBUG) ! + $(NODEBUG) ! Figure out the architecture + $(NODEBUG) ! + $(NODEBUG) arch = f$edit( f$getsyi( "arch_name"), "upcase") + $(NODEBUG) ! + $(NODEBUG) ! Set up logical names for the libraries, so LINK and + $(NODEBUG) ! running programs can use them. + $(NODEBUG) ! + $(NODEBUG) {- join("\n\t\$(NODEBUG) ", map { "DEFINE ".uc($_)." 'F\$ENV(\"DEFAULT\")'".uc($_)."\$(SHLIB_EXT)" } map { $unified_info{sharednames}->{$_} || () } @{$unified_info{libraries}}) || "!" -} + +.LAST : + $(NODEBUG) {- join("\n\t\$(NODEBUG) ", map { "DEASSIGN ".uc($_) } map { $unified_info{sharednames}->{$_} || () } @{$unified_info{libraries}}) || "!" -} + $(NODEBUG) DEASSIGN ossl_dataroot + $(NODEBUG) DEASSIGN ossl_installroot + $(NODEBUG) DEASSIGN internal + $(NODEBUG) DEASSIGN openssl +.DEFAULT : + @ ! MMS cannot handle no actions... + +# The main targets ################################################### + +all : build_generated, - + build_libs_nodep, build_engines_nodep, build_programs_nodep, - + depend + +build_libs : build_generated, build_libs_nodep, depend +build_libs_nodep : $(LIBS), $(SHLIBS) +build_engines : build_generated, build_engines_nodep, depend +build_engines_nodep : $(ENGINES) +build_programs : build_generated, build_programs_nodep, depend +build_programs_nodep : $(PROGRAMS), $(SCRIPTS) + +build_generated : $(GENERATED_MANDATORY) + +# Kept around for backward compatibility +build_apps build_tests : build_programs + +test tests : build_generated, build_programs_nodep, build_engines_nodep, - + depend + @ ! {- output_off() if $disabled{tests}; "" -} + SET DEFAULT [.test]{- move("test") -} + DEFINE SRCTOP {- sourcedir() -} + DEFINE BLDTOP {- builddir() -} + DEFINE OPENSSL_ENGINES {- builddir("engines") -} + DEFINE OPENSSL_DEBUG_MEMORY "on" + IF "$(VERBOSE)" .NES. "" THEN DEFINE VERBOSE "$(VERBOSE)" + $(PERL) {- sourcefile("test", "run_tests.pl") -} $(TESTS) + DEASSIGN OPENSSL_DEBUG_MEMORY + DEASSIGN OPENSSL_ENGINES + DEASSIGN BLDTOP + DEASSIGN SRCTOP + SET DEFAULT [-]{- move("..") -} + @ ! {- if ($disabled{tests}) { output_on(); } else { output_off(); } "" -} + @ WRITE SYS$OUTPUT "Tests are not supported with your chosen Configure options" + @ ! {- output_on() if !$disabled{tests}; "" -} + +list-tests : + @ ! {- output_off() if $disabled{tests}; "" -} + @ DEFINE SRCTOP {- sourcedir() -} + @ $(PERL) {- sourcefile("test", "run_tests.pl") -} list + @ DEASSIGN SRCTOP + @ ! {- if ($disabled{tests}) { output_on(); } else { output_off(); } "" -} + @ WRITE SYS$OUTPUT "Tests are not supported with your chosen Configure options" + @ ! {- output_on() if !$disabled{tests}; "" -} + +install : install_sw install_ssldirs install_docs + @ WRITE SYS$OUTPUT "" + @ WRITE SYS$OUTPUT "######################################################################" + @ WRITE SYS$OUTPUT "" + @ IF "$(DESTDIR)" .EQS. "" THEN - + PIPE ( WRITE SYS$OUTPUT "Installation complete" ; - + WRITE SYS$OUTPUT "" ; - + WRITE SYS$OUTPUT "Run @$(SYSTARTUP)openssl_startup{- $osslver -} to set up logical names" ; - + WRITE SYS$OUTPUT "then run @$(SYSTARTUP)openssl_utils{- $osslver -} to define commands" ; - + WRITE SYS$OUTPUT "" ) + @ IF "$(DESTDIR)" .NES. "" THEN - + PIPE ( WRITE SYS$OUTPUT "Staging installation complete" ; - + WRITE SYS$OUTPUT "" ; - + WRITE SYS$OUTPUT "Finish or package in such a way that the contents of the directory tree" ; - + WRITE SYS$OUTPUT staging_instdir ; - + WRITE SYS$OUTPUT "ends up in $(INSTALLTOP)," ; - + WRITE SYS$OUTPUT "and that the contents of the contents of the directory tree" ; - + WRITE SYS$OUTPUT staging_datadir ; - + WRITE SYS$OUTPUT "ends up in $(OPENSSLDIR)" ; - + WRITE SYS$OUTPUT "" ; - + WRITE SYS$OUTPUT "When in its final destination," ; - + WRITE SYS$OUTPUT "Run @$(SYSTARTUP)openssl_startup{- $osslver -} to set up logical names" ; - + WRITE SYS$OUTPUT "then run @$(SYSTARTUP)openssl_utils{- $osslver -} to define commands" ; - + WRITE SYS$OUTPUT "" ) + +check_install : + spawn/nolog @ossl_installroot:[SYSTEST]openssl_ivp{- $osslver -}.com + +uninstall : uninstall_docs uninstall_sw + +# Because VMS wants the generation number (or *) to delete files, we can't +# use $(LIBS), $(PROGRAMS), $(GENERATED) and $(ENGINES)directly. +libclean : + {- join("\n\t", map { "- DELETE $_.OLB;*" } @{$unified_info{libraries}}) || "@ !" -} + {- join("\n\t", map { "- DELETE $_.EXE;*,$_.MAP;*,$_.OPT;*" } @shlibs) || "@ !" -} + +clean : libclean + {- join("\n\t", map { "- DELETE $_.EXE;*,$_.OPT;*" } @{$unified_info{programs}}) || "@ !" -} + {- join("\n\t", map { "- DELETE $_.EXE;*,$_.OPT;*" } @{$unified_info{engines}}) || "@ !" -} + {- join("\n\t", map { "- DELETE $_;*" } @{$unified_info{scripts}}) || "@ !" -} + {- join("\n\t", map { "- DELETE $_;*" } @generated) || "@ !" -} + - DELETE [...]*.MAP;* + - DELETE [...]*.D;* + - DELETE [...]*.OBJ;*,*.LIS;* + - DELETE []CXX$DEMANGLER_DB.;* + - DELETE [.VMS]openssl_startup.com;* + - DELETE [.VMS]openssl_shutdown.com;* + - DELETE []vmsconfig.pm;* + +distclean : clean + - DELETE configdata.pm;* + - DELETE descrip.mms;* + +depend : descrip.mms +descrip.mms : FORCE + @ ! {- output_off() if $disabled{makedepend}; "" -} + @ $(PERL) -pe "if (/^# DO NOT DELETE.*/) { exit(0); }" - + < descrip.mms > descrip.mms-new + @ OPEN/APPEND DESCRIP descrip.mms-new + @ WRITE DESCRIP "# DO NOT DELETE THIS LINE -- make depend depends on it." + {- join("\n\t", map { "\@ IF F\$SEARCH(\"$_\") .NES. \"\" THEN TYPE $_ /OUTPUT=DESCRIP:" } @deps); -} + @ CLOSE DESCRIP + @ PIPE ( $(PERL) -e "use File::Compare qw/compare_text/; my $x = compare_text(""descrip.mms"",""descrip.mms-new""); exit(0x10000000 + ($x == 0));" || - + RENAME descrip.mms-new descrip.mms ) + @ IF F$SEARCH("descrip.mms-new") .NES. "" THEN DELETE descrip.mms-new;* + -@ SPAWN/OUTPUT=NLA0: PURGE/NOLOG descrip.mms + @ ! {- output_on() if $disabled{makedepend}; "" -} + +# Install helper targets ############################################# + +install_sw : all install_shared _install_dev_ns - + install_engines _install_runtime_ns - + install_startup install_ivp + +uninstall_sw : uninstall_shared _uninstall_dev_ns - + uninstall_engines _uninstall_runtime_ns - + uninstall_startup uninstall_ivp + +install_docs : install_html_docs + +uninstall_docs : uninstall_html_docs + +install_ssldirs : check_INSTALLTOP + - CREATE/DIR/PROT=(S:RWED,O:RWE,G:RE,W:RE) OSSL_DATAROOT:[000000] + IF F$SEARCH("OSSL_DATAROOT:[000000]CERTS.DIR;1") .EQS. "" THEN - + CREATE/DIR/PROT=(S:RWED,O:RWE,G:RE,W:RE) OSSL_DATAROOT:[CERTS] + IF F$SEARCH("OSSL_DATAROOT:[000000]PRIVATE.DIR;1") .EQS. "" THEN - + CREATE/DIR/PROT=(S:RWED,O:RWE,G,W) OSSL_DATAROOT:[PRIVATE] + IF F$SEARCH("OSSL_DATAROOT:[000000]MISC.DIR;1") .EQS. "" THEN - + CREATE/DIR/PROT=(S:RWED,O:RWE,G,W) OSSL_DATAROOT:[MISC] + COPY/PROT=W:RE $(MISC_SCRIPTS) OSSL_DATAROOT:[MISC] + @ ! Install configuration file + COPY/PROT=W:R {- sourcefile("apps", "openssl-vms.cnf") -} - + ossl_dataroot:[000000]openssl.cnf-dist + IF F$SEARCH("OSSL_DATAROOT:[000000]openssl.cnf") .EQS. "" THEN - + COPY/PROT=W:R {- sourcefile("apps", "openssl-vms.cnf") -} - + ossl_dataroot:[000000]openssl.cnf + +install_shared : check_INSTALLTOP + @ {- output_off() if $disabled{shared}; "" -} ! + @ WRITE SYS$OUTPUT "*** Installing shareable images" + @ ! Install shared (runtime) libraries + - CREATE/DIR ossl_installroot:[LIB.'arch'] + {- join("\n ", + map { "COPY/PROT=W:R $_.EXE ossl_installroot:[LIB.'arch']" } + @install_shlibs) -} + @ {- output_on() if $disabled{shared}; "" -} ! + +_install_dev_ns : check_INSTALLTOP + @ WRITE SYS$OUTPUT "*** Installing development files" + @ ! Install header files + - CREATE/DIR ossl_installroot:[include.openssl] + COPY/PROT=W:R openssl:*.h ossl_installroot:[include.openssl] + @ ! Install static (development) libraries + - CREATE/DIR ossl_installroot:[LIB.'arch'] + {- join("\n ", + map { "COPY/PROT=W:R $_.OLB ossl_installroot:[LIB.'arch']" } + @{$unified_info{install}->{libraries}}) -} + +install_dev : install_shared _install_dev_ns + +_install_runtime_ns : check_INSTALLTOP + @ ! Install the main program + - CREATE/DIR ossl_installroot:[EXE.'arch'] + COPY/PROT=W:RE [.APPS]openssl.EXE - + ossl_installroot:[EXE.'arch']openssl{- $osslver -}.EXE + @ ! Install scripts + COPY/PROT=W:RE $(BIN_SCRIPTS) ossl_installroot:[EXE] + @ ! {- output_on() if $disabled{apps}; "" -} + +install_runtime : install_shared _install_runtime_ns + +install_engines : check_INSTALLTOP + @ {- output_off() unless scalar @{$unified_info{engines}}; "" -} ! + @ WRITE SYS$OUTPUT "*** Installing engines" + - CREATE/DIR ossl_installroot:[ENGINES{- $sover.$target{pointer_size} -}.'arch'] + {- join("\n ", + map { "COPY/PROT=W:RE $_.EXE ossl_installroot:[ENGINES$sover$target{pointer_size}.'arch']" } + @{$unified_info{install}->{engines}}) -} + @ {- output_on() unless scalar @{$unified_info{engines}}; "" -} ! + +install_startup : [.VMS]openssl_startup.com [.VMS]openssl_shutdown.com - + [.VMS]openssl_utils.com, check_INSTALLTOP + - CREATE/DIR ossl_installroot:[SYS$STARTUP] + COPY/PROT=W:RE [.VMS]openssl_startup.com - + ossl_installroot:[SYS$STARTUP]openssl_startup{- $osslver -}.com + COPY/PROT=W:RE [.VMS]openssl_shutdown.com - + ossl_installroot:[SYS$STARTUP]openssl_shutdown{- $osslver -}.com + COPY/PROT=W:RE [.VMS]openssl_utils.com - + ossl_installroot:[SYS$STARTUP]openssl_utils{- $osslver -}.com + +install_ivp : [.VMS]openssl_ivp.com check_INSTALLTOP + - CREATE/DIR ossl_installroot:[SYSTEST] + COPY/PROT=W:RE [.VMS]openssl_ivp.com - + ossl_installroot:[SYSTEST]openssl_ivp{- $osslver -}.com + +[.VMS]openssl_startup.com : vmsconfig.pm {- sourcefile("VMS", "openssl_startup.com.in") -} + - CREATE/DIR [.VMS] + $(PERL) "-I." "-Mvmsconfig" {- sourcefile("util", "dofile.pl") -} - + {- sourcefile("VMS", "openssl_startup.com.in") -} - + > [.VMS]openssl_startup.com + +[.VMS]openssl_utils.com : vmsconfig.pm {- sourcefile("VMS", "openssl_utils.com.in") -} + - CREATE/DIR [.VMS] + $(PERL) "-I." "-Mvmsconfig" {- sourcefile("util", "dofile.pl") -} - + {- sourcefile("VMS", "openssl_utils.com.in") -} - + > [.VMS]openssl_utils.com + +[.VMS]openssl_shutdown.com : vmsconfig.pm {- sourcefile("VMS", "openssl_shutdown.com.in") -} + - CREATE/DIR [.VMS] + $(PERL) "-I." "-Mvmsconfig" {- sourcefile("util", "dofile.pl") -} - + {- sourcefile("VMS", "openssl_shutdown.com.in") -} - + > [.VMS]openssl_shutdown.com + +[.VMS]openssl_ivp.com : vmsconfig.pm {- sourcefile("VMS", "openssl_ivp.com.in") -} + - CREATE/DIR [.VMS] + $(PERL) "-I." "-Mvmsconfig" {- sourcefile("util", "dofile.pl") -} - + {- sourcefile("VMS", "openssl_ivp.com.in") -} - + > [.VMS]openssl_ivp.com + +vmsconfig.pm : configdata.pm + OPEN/WRITE/SHARE=READ CONFIG []vmsconfig.pm + WRITE CONFIG "package vmsconfig;" + WRITE CONFIG "use strict; use warnings;" + WRITE CONFIG "use Exporter;" + WRITE CONFIG "our @ISA = qw(Exporter);" + WRITE CONFIG "our @EXPORT = qw(%config %target %withargs %unified_info %disabled);" + WRITE CONFIG "our %config = (" + WRITE CONFIG " target => '","{- $config{target} -}","'," + WRITE CONFIG " version => '","{- $config{version} -}","'," + WRITE CONFIG " shlib_major => '","{- $config{shlib_major} -}","'," + WRITE CONFIG " shlib_minor => '","{- $config{shlib_minor} -}","'," + WRITE CONFIG " no_shared => '","{- $disabled{shared} -}","'," + WRITE CONFIG " INSTALLTOP => '$(INSTALLTOP)'," + WRITE CONFIG " OPENSSLDIR => '$(OPENSSLDIR)'," + WRITE CONFIG " pointer_size => '","{- $target{pointer_size} -}","'," + WRITE CONFIG ");" + WRITE CONFIG "our %target = ();" + WRITE CONFIG "our %disabled = ();" + WRITE CONFIG "our %withargs = ();" + WRITE CONFIG "our %unified_info = ();" + WRITE CONFIG "1;" + CLOSE CONFIG + +install_html_docs : check_INSTALLTOP + sourcedir = F$PARSE("{- $sourcedir -}A.;","[]") - "]A.;" + ".DOC]" + $(PERL) {- sourcefile("util", "process_docs.pl") -} - + --sourcedir='sourcedir' --destdir=ossl_installroot:[HTML] - + --type=html + +check_INSTALLTOP : + @ IF "$(INSTALLTOP)" .EQS. "" THEN - + WRITE SYS$ERROR "INSTALLTOP should not be empty" + @ IF "$(INSTALLTOP)" .EQS. "" THEN - + EXIT %x10000002 + +# Helper targets ##################################################### + +# Developer targets ################################################## + +debug_logicals : + SH LOGICAL/PROC openssl,internal,ossl_installroot,ossl_dataroot + +# Building targets ################################################### + +configdata.pm : $(SRCDIR)Configure $(SRCDIR)config.com {- join(" ", @{$config{build_file_templates}}, @{$config{build_infos}}, @{$config{conf_files}}) -} + @ WRITE SYS$OUTPUT "Reconfiguring..." + perl $(SRCDIR)Configure reconf + @ WRITE SYS$OUTPUT "*************************************************" + @ WRITE SYS$OUTPUT "*** ***" + @ WRITE SYS$OUTPUT "*** Please run the same mms command again ***" + @ WRITE SYS$OUTPUT "*** ***" + @ WRITE SYS$OUTPUT "*************************************************" + @ PIPE ( EXIT %X10000000 ) + +{- + use File::Basename; + use File::Spec::Functions qw/abs2rel rel2abs catfile catdir/; + + sub generatesrc { + my %args = @_; + my $generator = join(" ", @{$args{generator}}); + my $generator_incs = join("", map { ' "-I'.$_.'"' } @{$args{generator_incs}}); + my $deps = join(", -\n\t\t", @{$args{generator_deps}}, @{$args{deps}}); + + if ($args{src} !~ /\.[sS]$/) { + if ($args{generator}->[0] =~ m|^.*\.in$|) { + my $dofile = abs2rel(rel2abs(catfile($config{sourcedir}, + "util", "dofile.pl")), + rel2abs($config{builddir})); + return <<"EOF"; +$args{src} : $args{generator}->[0] $deps + \$(PERL) "-I\$(BLDDIR)" "-Mconfigdata" $dofile \\ + "-o$target{build_file}" $generator > \$@ +EOF + } else { + return <<"EOF"; +$args{src} : $args{generator}->[0] $deps + \$(PERL)$generator_incs $generator > \$@ +EOF + } + } else { + die "No method to generate assembler source present.\n"; + } + } + + sub src2obj { + my %args = @_; + my $obj = $args{obj}; + my $deps = join(", -\n\t\t", @{$args{srcs}}, @{$args{deps}}); + + # Because VMS C isn't very good at combining a /INCLUDE path with + # #includes having a relative directory (like '#include "../foo.h"), + # the best choice is to move to the first source file's intended + # directory before compiling, and make sure to write the object file + # in the correct position (important when the object tree is other + # than the source tree). + my $forward = dirname($args{srcs}->[0]); + my $backward = abs2rel(rel2abs("."), rel2abs($forward)); + my $objd = abs2rel(rel2abs(dirname($obj)), rel2abs($forward)); + my $objn = basename($obj); + my $srcs = + join(", ", + map { abs2rel(rel2abs($_), rel2abs($forward)) } @{$args{srcs}}); + my $ecflags = { lib => '$(LIB_CFLAGS)', + dso => '$(DSO_CFLAGS)', + bin => '$(BIN_CFLAGS)' } -> {$args{intent}}; + my $incs_on = "\@ !"; + my $incs_off = "\@ !"; + my $incs = ""; + my @incs = (); + push @incs, @{$args{incs}} if @{$args{incs}}; + unless ($disabled{zlib}) { + # GNV$ZLIB_INCLUDE is the standard logical name for later zlib + # incarnations. + push @incs, ($withargs{zlib_include} || 'GNV$ZLIB_INCLUDE:'); + } + if (@incs) { + $incs_on = + "DEFINE tmp_includes " + .join(",-\n\t\t\t", map { + file_name_is_absolute($_) + ? $_ : catdir($backward,$_) + } @incs); + $incs_off = "DEASSIGN tmp_includes"; + $incs = " /INCLUDE=(tmp_includes:)"; + } + my $before = $unified_info{before}->{$obj.".OBJ"} || "\@ !"; + my $after = $unified_info{after}->{$obj.".OBJ"} || "\@ !"; + my $depbuild = $disabled{makedepend} ? "" + : " /MMS=(FILE=${objd}${objn}.tmp-D,TARGET=$obj.OBJ)"; + + return <<"EOF"; +$obj.OBJ : $deps + ${before} + SET DEFAULT $forward + $incs_on + \$(CC) \$(CFLAGS)${ecflags}${incs}${depbuild} /OBJECT=${objd}${objn}.OBJ /REPOSITORY=$backward $srcs + $incs_off + SET DEFAULT $backward + ${after} + \@ PIPE ( \$(PERL) -e "use File::Compare qw/compare_text/; my \$x = compare_text(""$obj.D"",""$obj.tmp-D""); exit(0x10000000 + (\$x == 0));" || - + RENAME $obj.tmp-D $obj.d ) + \@ IF F\$SEARCH("$obj.tmp-D") .NES. "" THEN DELETE $obj.tmp-D;* + - PURGE $obj.OBJ +EOF + } + sub libobj2shlib { + my %args = @_; + my $lib = $args{lib}; + my $shlib = $args{shlib}; + my $libd = dirname($lib); + my $libn = basename($lib); + (my $mkdef_key = $libn) =~ s/^${osslprefix_q}lib([^0-9]*)\d*/$1/i; + my @deps = map { + $disabled{shared} ? $_.".OLB" + : $unified_info{sharednames}->{$_}.".EXE"; } @{$args{deps}}; + my $deps = join(", -\n\t\t", @deps); + my $shlib_target = $disabled{shared} ? "" : $target{shared_target}; + my $ordinalsfile = defined($args{ordinals}) ? $args{ordinals}->[1] : ""; + my $engine_opt = abs2rel(rel2abs(catfile($config{sourcedir}, + "VMS", "engine.opt")), + rel2abs($config{builddir})); + my $mkdef_pl = abs2rel(rel2abs(catfile($config{sourcedir}, + "util", "mkdef.pl")), + rel2abs($config{builddir})); + my $translatesyms_pl = abs2rel(rel2abs(catfile($config{sourcedir}, + "VMS", "translatesyms.pl")), + rel2abs($config{builddir})); + # The "[]" hack is because in .OPT files, each line inherits the + # previous line's file spec as default, so if no directory spec + # is present in the current line and the previous line has one that + # doesn't apply, you're in for a surprise. + my $write_opt = + join("\n\t", map { my $x = $_ =~ /\[/ ? $_ : "[]".$_; + $x =~ s|(\.EXE)|$1/SHARE|; + $x =~ s|(\.OLB)|$1/LIB|; + "WRITE OPT_FILE \"$x\"" } @deps) + || "\@ !"; + return <<"EOF"; +$shlib.EXE : $lib.OLB $deps $ordinalsfile + \$(PERL) $mkdef_pl "$mkdef_key" "VMS" > $shlib.SYMVEC-tmp + \$(PERL) $translatesyms_pl \$(BLDDIR)CXX\$DEMANGLER_DB. < $shlib.SYMVEC-tmp > $shlib.SYMVEC + DELETE $shlib.SYMVEC-tmp;* + OPEN/WRITE/SHARE=READ OPT_FILE $shlib.OPT + WRITE OPT_FILE "IDENTIFICATION=""V$config{version}""" + TYPE $shlib.SYMVEC /OUTPUT=OPT_FILE: + WRITE OPT_FILE "$lib.OLB/LIBRARY" + $write_opt + CLOSE OPT_FILE + LINK /MAP=$shlib.MAP /FULL/SHARE=$shlib.EXE $shlib.OPT/OPT \$(EX_LIBS) + DELETE $shlib.SYMVEC;* + PURGE $shlib.EXE,$shlib.OPT,$shlib.MAP +EOF + } + sub obj2dso { + my %args = @_; + my $lib = $args{lib}; + my $libd = dirname($lib); + my $libn = basename($lib); + (my $libn_nolib = $libn) =~ s/^lib//; + my @objs = map { "$_.OBJ" } @{$args{objs}}; + my @deps = map { + $disabled{shared} ? $_.".OLB" + : $unified_info{sharednames}->{$_}.".EXE"; } @{$args{deps}}; + my $deps = join(", -\n\t\t", @objs, @deps); + my $shlib_target = $disabled{shared} ? "" : $target{shared_target}; + my $engine_opt = abs2rel(rel2abs(catfile($config{sourcedir}, + "VMS", "engine.opt")), + rel2abs($config{builddir})); + # The "[]" hack is because in .OPT files, each line inherits the + # previous line's file spec as default, so if no directory spec + # is present in the current line and the previous line has one that + # doesn't apply, you're in for a surprise. + my $write_opt1 = + join(",-\"\n\t", map { my $x = $_ =~ /\[/ ? $_ : "[]".$_; + "WRITE OPT_FILE \"$x" } @objs). + "\""; + my $write_opt2 = + join("\n\t", map { my $x = $_ =~ /\[/ ? $_ : "[]".$_; + $x =~ s|(\.EXE)|$1/SHARE|; + $x =~ s|(\.OLB)|$1/LIB|; + "WRITE OPT_FILE \"$x\"" } @deps) + || "\@ !"; + return <<"EOF"; +$lib.EXE : $deps + OPEN/WRITE/SHARE=READ OPT_FILE $lib.OPT + TYPE $engine_opt /OUTPUT=OPT_FILE: + $write_opt1 + $write_opt2 + CLOSE OPT_FILE + LINK /MAP=$lib.MAP /FULL/SHARE=$lib.EXE $lib.OPT/OPT \$(EX_LIBS) + - PURGE $lib.EXE,$lib.OPT,$lib.MAP +EOF + } + sub obj2lib { + my %args = @_; + my $lib = $args{lib}; + my $objs = join(", -\n\t\t", map { $_.".OBJ" } (@{$args{objs}})); + my $fill_lib = join("\n\t", (map { "LIBRARY/REPLACE $lib.OLB $_.OBJ" } + @{$args{objs}})); + return <<"EOF"; +$lib.OLB : $objs + LIBRARY/CREATE/OBJECT $lib.OLB + $fill_lib + - PURGE $lib.OLB +EOF + } + sub obj2bin { + my %args = @_; + my $bin = $args{bin}; + my $bind = dirname($bin); + my $binn = basename($bin); + my @objs = map { "$_.OBJ" } @{$args{objs}}; + my @deps = map { + $disabled{shared} ? $_.".OLB" + : $unified_info{sharednames}->{$_}.".EXE"; } @{$args{deps}}; + my $deps = join(", -\n\t\t", @objs, @deps); + # The "[]" hack is because in .OPT files, each line inherits the + # previous line's file spec as default, so if no directory spec + # is present in the current line and the previous line has one that + # doesn't apply, you're in for a surprise. + my $write_opt1 = + join(",-\"\n\t", map { my $x = $_ =~ /\[/ ? $_ : "[]".$_; + "WRITE OPT_FILE \"$x" } @objs). + "\""; + my $write_opt2 = + join("\n\t", map { my $x = $_ =~ /\[/ ? $_ : "[]".$_; + $x =~ s|(\.EXE)|$1/SHARE|; + $x =~ s|(\.OLB)|$1/LIB|; + "WRITE OPT_FILE \"$x\"" } @deps) + || "\@ !"; + return <<"EOF"; +$bin.EXE : $deps + OPEN/WRITE/SHARE=READ OPT_FILE $bin.OPT + $write_opt1 + $write_opt2 + CLOSE OPT_FILE + LINK/EXEC=$bin.EXE \$(LDFLAGS) $bin.OPT/OPT \$(EX_LIBS) + - PURGE $bin.EXE,$bin.OPT +EOF + } + sub in2script { + my %args = @_; + my $script = $args{script}; + return "" if grep { $_ eq $script } @{$args{sources}}; # No overwrite! + my $sources = join(" ", @{$args{sources}}); + my $dofile = abs2rel(rel2abs(catfile($config{sourcedir}, + "util", "dofile.pl")), + rel2abs($config{builddir})); + return <<"EOF"; +$script : $sources + \$(PERL) "-I\$(BLDDIR)" "-Mconfigdata" $dofile - + "-o$target{build_file}" $sources > $script + SET FILE/PROT=(S:RWED,O:RWED,G:RE,W:RE) $script + PURGE $script +EOF + } + "" # Important! This becomes part of the template result. +-} diff --git a/Configurations/unix-Makefile.tmpl b/Configurations/unix-Makefile.tmpl new file mode 100644 index 000000000..16cc6ec7c --- /dev/null +++ b/Configurations/unix-Makefile.tmpl @@ -0,0 +1,1125 @@ +## +## Makefile for OpenSSL +## +## {- join("\n## ", @autowarntext) -} +{- + our $objext = $target{obj_extension} || ".o"; + our $depext = $target{dep_extension} || ".d"; + our $exeext = $target{exe_extension} || ""; + our $libext = $target{lib_extension} || ".a"; + our $shlibext = $target{shared_extension} || ".so"; + our $shlibextsimple = $target{shared_extension_simple} || ".so"; + our $shlibextimport = $target{shared_import_extension} || ""; + our $dsoext = $target{dso_extension} || ".so"; + + sub windowsdll { $config{target} =~ /^(?:Cygwin|mingw)/ } + + our $sover = $config{target} =~ /^mingw/ + ? $config{shlib_major}."_".$config{shlib_minor} + : $config{shlib_major}.".".$config{shlib_minor}; + + # shlib and shlib_simple both take a static library name and figure + # out what the shlib name should be. + # + # When OpenSSL is configured "no-shared", these functions will just + # return empty lists, making them suitable to join(). + # + # With Windows DLL producers, shlib($libname) will return the shared + # library name (which usually is different from the static library + # name) with the default shared extension appended to it, while + # shlib_simple($libname) will return the static library name with + # the shared extension followed by ".a" appended to it. The former + # result is used as the runtime shared library while the latter is + # used as the DLL import library. + # + # On all Unix systems, shlib($libname) will return the library name + # with the default shared extension, while shlib_simple($libname) + # will return the name from shlib($libname) with any SO version number + # removed. On some systems, they may therefore return the exact same + # string. + sub shlib { + return () if $disabled{shared}; + my $lib = shift; + return $unified_info{sharednames}->{$lib} . $shlibext; + } + sub shlib_simple { + return () if $disabled{shared}; + + my $lib = shift; + if (windowsdll()) { + return $lib . $shlibextimport; + } + return $lib . $shlibextsimple; + } + + # dso is a complement to shlib / shlib_simple that returns the + # given libname with the simple shared extension (possible SO version + # removed). This differs from shlib_simple() by being unconditional. + sub dso { + my $engine = shift; + + return $engine . $dsoext; + } + # This makes sure things get built in the order they need + # to. You're welcome. + sub dependmagic { + my $target = shift; + + return "$target: build_generated\n\t\$(MAKE) depend && \$(MAKE) _$target\n_$target"; + } + ''; +-} +PLATFORM={- $config{target} -} +OPTIONS={- $config{options} -} +CONFIGURE_ARGS=({- join(", ",quotify_l(@{$config{perlargv}})) -}) +SRCDIR={- $config{sourcedir} -} +BLDDIR={- $config{builddir} -} + +VERSION={- $config{version} -} +MAJOR={- $config{major} -} +MINOR={- $config{minor} -} +SHLIB_VERSION_NUMBER={- $config{shlib_version_number} -} +SHLIB_VERSION_HISTORY={- $config{shlib_version_history} -} +SHLIB_MAJOR={- $config{shlib_major} -} +SHLIB_MINOR={- $config{shlib_minor} -} +SHLIB_TARGET={- $target{shared_target} -} + +LIBS={- join(" ", map { $_.$libext } @{$unified_info{libraries}}) -} +SHLIBS={- join(" ", map { shlib($_) } @{$unified_info{libraries}}) -} +SHLIB_INFO={- join(" ", map { "\"".shlib($_).";".shlib_simple($_)."\"" } @{$unified_info{libraries}}) -} +ENGINES={- join(" ", map { dso($_) } @{$unified_info{engines}}) -} +PROGRAMS={- join(" ", map { $_.$exeext } @{$unified_info{programs}}) -} +SCRIPTS={- join(" ", @{$unified_info{scripts}}) -} +{- output_off() if $disabled{makedepend}; "" -} +DEPS={- join(" ", map { (my $x = $_) =~ s|\.o$|$depext|; $x; } + grep { $unified_info{sources}->{$_}->[0] =~ /\.c$/ } + keys %{$unified_info{sources}}); -} +{- output_on() if $disabled{makedepend}; "" -} +GENERATED_MANDATORY={- join(" ", @{$unified_info{depends}->{""}} ) -} +GENERATED={- join(" ", + ( map { (my $x = $_) =~ s|\.S$|\.s|; $x } + grep { defined $unified_info{generate}->{$_} } + map { @{$unified_info{sources}->{$_}} } + grep { /\.o$/ } keys %{$unified_info{sources}} ), + ( grep { /\.h$/ } keys %{$unified_info{generate}} )) -} + +INSTALL_LIBS={- join(" ", map { $_.$libext } @{$unified_info{install}->{libraries}}) -} +INSTALL_SHLIBS={- join(" ", map { shlib($_) } @{$unified_info{install}->{libraries}}) -} +INSTALL_SHLIB_INFO={- join(" ", map { "\"".shlib($_).";".shlib_simple($_)."\"" } @{$unified_info{install}->{libraries}}) -} +INSTALL_ENGINES={- join(" ", map { dso($_) } @{$unified_info{install}->{engines}}) -} +INSTALL_PROGRAMS={- join(" ", map { $_.$exeext } @{$unified_info{install}->{programs}}) -} +{- output_off() if $disabled{apps}; "" -} +BIN_SCRIPTS=$(BLDDIR)/tools/c_rehash +MISC_SCRIPTS=$(BLDDIR)/apps/CA.pl $(BLDDIR)/apps/tsget +{- output_on() if $disabled{apps}; "" -} + +# DESTDIR is for package builders so that they can configure for, say, +# /usr/ and yet have everything installed to /tmp/somedir/usr/. +# Normally it is left empty. +DESTDIR= + +# Do not edit these manually. Use Configure with --prefix or --openssldir +# to change this! Short explanation in the top comment in Configure +INSTALLTOP={- # $prefix is used in the OPENSSLDIR perl snippet + # + our $prefix = $config{prefix} || "/usr/local"; + $prefix -} +OPENSSLDIR={- # + # The logic here is that if no --openssldir was given, + # OPENSSLDIR will get the value from $prefix plus "/ssl". + # If --openssldir was given and the value is an absolute + # path, OPENSSLDIR will get its value without change. + # If the value from --openssldir is a relative path, + # OPENSSLDIR will get $prefix with the --openssldir + # value appended as a subdirectory. + # + use File::Spec::Functions; + our $openssldir = + $config{openssldir} ? + (file_name_is_absolute($config{openssldir}) ? + $config{openssldir} + : catdir($prefix, $config{openssldir})) + : catdir($prefix, "ssl"); + $openssldir -} +LIBDIR={- # + # if $prefix/lib$target{multilib} is not an existing + # directory, then assume that it's not searched by linker + # automatically, in which case adding $target{multilib} suffix + # causes more grief than we're ready to tolerate, so don't... + our $multilib = + -d "$prefix/lib$target{multilib}" ? $target{multilib} : ""; + our $libdir = $config{libdir} || "lib$multilib"; + $libdir -} +ENGINESDIR={- use File::Spec::Functions; + catdir($prefix,$libdir,"engines-$sover") -} + +# Convenience variable for those who want to set the rpath in shared +# libraries and applications +LIBRPATH=$(INSTALLTOP)/$(LIBDIR) + +MANDIR=$(INSTALLTOP)/share/man +DOCDIR=$(INSTALLTOP)/share/doc/$(BASENAME) +HTMLDIR=$(DOCDIR)/html + +# MANSUFFIX is for the benefit of anyone who may want to have a suffix +# appended after the manpage file section number. "ssl" is popular, +# resulting in files such as config.5ssl rather than config.5. +MANSUFFIX= +HTMLSUFFIX=html + + + +CROSS_COMPILE= {- $config{cross_compile_prefix} -} +CC= $(CROSS_COMPILE){- $target{cc} -} +CFLAGS={- our $cflags2 = join(" ",(map { "-D".$_} @{$target{defines}}, @{$config{defines}}),"-DOPENSSLDIR=\"\\\"\$(OPENSSLDIR)\\\"\"","-DENGINESDIR=\"\\\"\$(ENGINESDIR)\\\"\"") -} {- $target{cflags} -} {- $config{cflags} -} +CFLAGS_Q={- $cflags2 =~ s|([\\"])|\\$1|g; $cflags2 -} {- $config{cflags} -} +LDFLAGS= {- $target{lflags} -} +PLIB_LDFLAGS= {- $target{plib_lflags} -} +EX_LIBS= {- $target{ex_libs} -} {- $config{ex_libs} -} +LIB_CFLAGS={- $target{shared_cflag} || "" -} +LIB_LDFLAGS={- $target{shared_ldflag}." ".$config{shared_ldflag} -} +DSO_CFLAGS={- $target{shared_cflag} || "" -} +DSO_LDFLAGS=$(LIB_LDFLAGS) +BIN_CFLAGS={- $target{bin_cflags} -} + +PERL={- $config{perl} -} + +ARFLAGS= {- $target{arflags} -} +AR=$(CROSS_COMPILE){- $target{ar} || "ar" -} $(ARFLAGS) r +RANLIB= {- $target{ranlib} -} +NM= $(CROSS_COMPILE){- $target{nm} || "nm" -} +RCFLAGS={- $target{shared_rcflag} -} +RC= $(CROSS_COMPILE){- $target{rc} || "windres" -} +RM= rm -f +RMDIR= rmdir +TAR= {- $target{tar} || "tar" -} +TARFLAGS= {- $target{tarflags} -} +MAKEDEPEND={- $config{makedepprog} -} + +BASENAME= openssl +NAME= $(BASENAME)-$(VERSION) +TARFILE= ../$(NAME).tar + +# We let the C compiler driver to take care of .s files. This is done in +# order to be excused from maintaining a separate set of architecture +# dependent assembler flags. E.g. if you throw -mcpu=ultrasparc at SPARC +# gcc, then the driver will automatically translate it to -xarch=v8plus +# and pass it down to assembler. +AS=$(CC) -c +ASFLAG=$(CFLAGS) +PERLASM_SCHEME= {- $target{perlasm_scheme} -} + +# For x86 assembler: Set PROCESSOR to 386 if you want to support +# the 80386. +PROCESSOR= {- $config{processor} -} + +# We want error [and other] messages in English. Trouble is that make(1) +# doesn't pass macros down as environment variables unless there already +# was corresponding variable originally set. In other words we can only +# reassign environment variables, but not set new ones, not in portable +# manner that is. That's why we reassign several, just to be sure... +LC_ALL=C +LC_MESSAGES=C +LANG=C + +# The main targets ################################################### + +{- dependmagic('all'); -}: build_libs_nodep build_engines_nodep build_programs_nodep link-utils +{- dependmagic('build_libs'); -}: build_libs_nodep +{- dependmagic('build_engines'); -}: build_engines_nodep +{- dependmagic('build_programs'); -}: build_programs_nodep + +build_generated: $(GENERATED_MANDATORY) +build_libs_nodep: libcrypto.pc libssl.pc openssl.pc +build_engines_nodep: $(ENGINES) +build_programs_nodep: $(PROGRAMS) $(SCRIPTS) + +# Kept around for backward compatibility +build_apps build_tests: build_programs + +test: tests +{- dependmagic('tests'); -}: build_programs_nodep build_engines_nodep link-utils + @ : {- output_off() if $disabled{tests}; "" -} + ( cd test; \ + SRCTOP=../$(SRCDIR) \ + BLDTOP=../$(BLDDIR) \ + PERL="$(PERL)" \ + EXE_EXT={- $exeext -} \ + OPENSSL_ENGINES=../$(BLDDIR)/engines \ + OPENSSL_DEBUG_MEMORY=on \ + $(PERL) ../$(SRCDIR)/test/run_tests.pl $(TESTS) ) + @ : {- if ($disabled{tests}) { output_on(); } else { output_off(); } "" -} + @echo "Tests are not supported with your chosen Configure options" + @ : {- output_on() if !$disabled{tests}; "" -} + +list-tests: + @ : {- output_off() if $disabled{tests}; "" -} + @SRCTOP="$(SRCDIR)" \ + $(PERL) $(SRCDIR)/test/run_tests.pl list + @ : {- if ($disabled{tests}) { output_on(); } else { output_off(); } "" -} + @echo "Tests are not supported with your chosen Configure options" + @ : {- output_on() if !$disabled{tests}; "" -} + +install: install_sw install_ssldirs install_docs + +uninstall: uninstall_docs uninstall_sw + +libclean: + @set -e; for s in $(SHLIB_INFO); do \ + s1=`echo "$$s" | cut -f1 -d";"`; \ + s2=`echo "$$s" | cut -f2 -d";"`; \ + echo $(RM) $$s1; \ + $(RM) $$s1; \ + if [ "$$s1" != "$$s2" ]; then \ + echo $(RM) $$s2; \ + $(RM) $$s2; \ + fi; \ + done + $(RM) $(LIBS) + $(RM) *.map + +clean: libclean + $(RM) $(PROGRAMS) $(TESTPROGS) $(ENGINES) $(SCRIPTS) + $(RM) $(GENERATED) + -$(RM) `find . -name '*{- $depext -}' -a \! -path "./.git/*"` + -$(RM) `find . -name '*{- $objext -}' -a \! -path "./.git/*"` + $(RM) core + $(RM) tags TAGS + $(RM) openssl.pc libcrypto.pc libssl.pc + -$(RM) `find . -type l -a \! -path "./.git/*"` + $(RM) $(TARFILE) + +distclean: clean + $(RM) configdata.pm + $(RM) Makefile + +# We check if any depfile is newer than Makefile and decide to +# concatenate only if that is true. +depend: + @: {- output_off() if $disabled{makedepend}; "" -} + @if egrep "^# DO NOT DELETE THIS LINE" Makefile >/dev/null && [ -z "`find $(DEPS) -newer Makefile 2>/dev/null; exit 0`" ]; then :; else \ + ( $(PERL) -pe 'exit 0 if /^# DO NOT DELETE THIS LINE.*/' < Makefile; \ + echo '# DO NOT DELETE THIS LINE -- make depend depends on it.'; \ + echo; \ + for f in $(DEPS); do \ + if [ -f $$f ]; then cat $$f; fi; \ + done ) > Makefile.new; \ + if cmp Makefile.new Makefile >/dev/null 2>&1; then \ + rm -f Makefile.new; \ + else \ + mv -f Makefile.new Makefile; \ + fi; \ + fi + @: {- output_on() if $disabled{makedepend}; "" -} + +# Install helper targets ############################################# + +install_sw: all install_dev install_engines install_runtime + +uninstall_sw: uninstall_runtime uninstall_engines uninstall_dev + +install_docs: install_man_docs install_html_docs + +uninstall_docs: uninstall_man_docs uninstall_html_docs + $(RM) -r -v $(DESTDIR)$(DOCDIR) + +install_ssldirs: + @$(PERL) $(SRCDIR)/util/mkdir-p.pl $(DESTDIR)$(OPENSSLDIR)/certs + @$(PERL) $(SRCDIR)/util/mkdir-p.pl $(DESTDIR)$(OPENSSLDIR)/private + @$(PERL) $(SRCDIR)/util/mkdir-p.pl $(DESTDIR)$(OPENSSLDIR)/misc + @set -e; for x in dummy $(MISC_SCRIPTS); do \ + if [ "$$x" = "dummy" ]; then continue; fi; \ + fn=`basename $$x`; \ + echo "install $$x -> $(DESTDIR)$(OPENSSLDIR)/misc/$$fn"; \ + cp $$x $(DESTDIR)$(OPENSSLDIR)/misc/$$fn.new; \ + chmod 755 $(DESTDIR)$(OPENSSLDIR)/misc/$$fn.new; \ + mv -f $(DESTDIR)$(OPENSSLDIR)/misc/$$fn.new \ + $(DESTDIR)$(OPENSSLDIR)/misc/$$fn; \ + done + @echo "install $(SRCDIR)/apps/openssl.cnf -> $(DESTDIR)$(OPENSSLDIR)/openssl.cnf.dist" + @cp $(SRCDIR)/apps/openssl.cnf $(DESTDIR)$(OPENSSLDIR)/openssl.cnf.new + @chmod 644 $(DESTDIR)$(OPENSSLDIR)/openssl.cnf.new + @mv -f $(DESTDIR)$(OPENSSLDIR)/openssl.cnf.new $(DESTDIR)$(OPENSSLDIR)/openssl.cnf.dist + @if ! [ -f "$(DESTDIR)$(OPENSSLDIR)/openssl.cnf" ]; then \ + echo "install $(SRCDIR)/apps/openssl.cnf -> $(DESTDIR)$(OPENSSLDIR)/openssl.cnf"; \ + cp $(SRCDIR)/apps/openssl.cnf $(DESTDIR)$(OPENSSLDIR)/openssl.cnf; \ + chmod 644 $(DESTDIR)$(OPENSSLDIR)/openssl.cnf; \ + fi + +install_dev: + @[ -n "$(INSTALLTOP)" ] || (echo INSTALLTOP should not be empty; exit 1) + @echo "*** Installing development files" + @$(PERL) $(SRCDIR)/util/mkdir-p.pl $(DESTDIR)$(INSTALLTOP)/include/openssl + @ : {- output_off() unless grep { $_ eq "OPENSSL_USE_APPLINK" } @{$target{defines}}; "" -} + @echo "install $(SRCDIR)/ms/applink.c -> $(DESTDIR)$(INSTALLTOP)/include/openssl/applink.c" + @cp $(SRCDIR)/ms/applink.c $(DESTDIR)$(INSTALLTOP)/include/openssl/applink.c + @chmod 644 $(DESTDIR)$(INSTALLTOP)/include/openssl/applink.c + @ : {- output_on() unless grep { $_ eq "OPENSSL_USE_APPLINK" } @{$target{defines}}; "" -} + @set -e; for i in $(SRCDIR)/include/openssl/*.h \ + $(BLDDIR)/include/openssl/*.h; do \ + fn=`basename $$i`; \ + echo "install $$i -> $(DESTDIR)$(INSTALLTOP)/include/openssl/$$fn"; \ + cp $$i $(DESTDIR)$(INSTALLTOP)/include/openssl/$$fn; \ + chmod 644 $(DESTDIR)$(INSTALLTOP)/include/openssl/$$fn; \ + done + @$(PERL) $(SRCDIR)/util/mkdir-p.pl $(DESTDIR)$(INSTALLTOP)/$(LIBDIR) + @set -e; for l in $(INSTALL_LIBS); do \ + fn=`basename $$l`; \ + echo "install $$l -> $(DESTDIR)$(INSTALLTOP)/$(LIBDIR)/$$fn"; \ + cp $$l $(DESTDIR)$(INSTALLTOP)/$(LIBDIR)/$$fn.new; \ + $(RANLIB) $(DESTDIR)$(INSTALLTOP)/$(LIBDIR)/$$fn.new; \ + chmod 644 $(DESTDIR)$(INSTALLTOP)/$(LIBDIR)/$$fn.new; \ + mv -f $(DESTDIR)$(INSTALLTOP)/$(LIBDIR)/$$fn.new \ + $(DESTDIR)$(INSTALLTOP)/$(LIBDIR)/$$fn; \ + done + @ : {- output_off() if $disabled{shared}; "" -} + @set -e; for s in $(INSTALL_SHLIB_INFO); do \ + s1=`echo "$$s" | cut -f1 -d";"`; \ + s2=`echo "$$s" | cut -f2 -d";"`; \ + fn1=`basename $$s1`; \ + fn2=`basename $$s2`; \ + : {- output_off() if windowsdll(); "" -}; \ + echo "install $$s1 -> $(DESTDIR)$(INSTALLTOP)/$(LIBDIR)/$$fn1"; \ + cp $$s1 $(DESTDIR)$(INSTALLTOP)/$(LIBDIR)/$$fn1.new; \ + chmod 755 $(DESTDIR)$(INSTALLTOP)/$(LIBDIR)/$$fn1.new; \ + mv -f $(DESTDIR)$(INSTALLTOP)/$(LIBDIR)/$$fn1.new \ + $(DESTDIR)$(INSTALLTOP)/$(LIBDIR)/$$fn1; \ + if [ "$$fn1" != "$$fn2" ]; then \ + echo "link $(DESTDIR)$(INSTALLTOP)/$(LIBDIR)/$$fn2 -> $(DESTDIR)$(INSTALLTOP)/$(LIBDIR)/$$fn1"; \ + ln -sf $$fn1 $(DESTDIR)$(INSTALLTOP)/$(LIBDIR)/$$fn2; \ + fi; \ + : {- output_on() if windowsdll(); "" -}{- output_off() unless windowsdll(); "" -}; \ + echo "install $$s2 -> $(DESTDIR)$(INSTALLTOP)/$(LIBDIR)/$$fn2"; \ + cp $$s2 $(DESTDIR)$(INSTALLTOP)/$(LIBDIR)/$$fn2.new; \ + chmod 755 $(DESTDIR)$(INSTALLTOP)/$(LIBDIR)/$$fn2.new; \ + mv -f $(DESTDIR)$(INSTALLTOP)/$(LIBDIR)/$$fn2.new \ + $(DESTDIR)$(INSTALLTOP)/$(LIBDIR)/$$fn2; \ + : {- output_on() unless windowsdll(); "" -}; \ + done + @ : {- output_on() if $disabled{shared}; "" -} + @$(PERL) $(SRCDIR)/util/mkdir-p.pl $(DESTDIR)$(INSTALLTOP)/$(LIBDIR)/pkgconfig + @echo "install libcrypto.pc -> $(DESTDIR)$(INSTALLTOP)/$(LIBDIR)/pkgconfig/libcrypto.pc" + @cp libcrypto.pc $(DESTDIR)$(INSTALLTOP)/$(LIBDIR)/pkgconfig + @chmod 644 $(DESTDIR)$(INSTALLTOP)/$(LIBDIR)/pkgconfig/libcrypto.pc + @echo "install libssl.pc -> $(DESTDIR)$(INSTALLTOP)/$(LIBDIR)/pkgconfig/libssl.pc" + @cp libssl.pc $(DESTDIR)$(INSTALLTOP)/$(LIBDIR)/pkgconfig + @chmod 644 $(DESTDIR)$(INSTALLTOP)/$(LIBDIR)/pkgconfig/libssl.pc + @echo "install openssl.pc -> $(DESTDIR)$(INSTALLTOP)/$(LIBDIR)/pkgconfig/openssl.pc" + @cp openssl.pc $(DESTDIR)$(INSTALLTOP)/$(LIBDIR)/pkgconfig + @chmod 644 $(DESTDIR)$(INSTALLTOP)/$(LIBDIR)/pkgconfig/openssl.pc + +uninstall_dev: + @echo "*** Uninstalling development files" + @ : {- output_off() unless grep { $_ eq "OPENSSL_USE_APPLINK" } @{$target{defines}}; "" -} + @echo "$(RM) $(DESTDIR)$(INSTALLTOP)/include/openssl/applink.c" + @$(RM) $(DESTDIR)$(INSTALLTOP)/include/openssl/applink.c + @ : {- output_on() unless grep { $_ eq "OPENSSL_USE_APPLINK" } @{$target{defines}}; "" -} + @set -e; for i in $(SRCDIR)/include/openssl/*.h \ + $(BLDDIR)/include/openssl/*.h; do \ + fn=`basename $$i`; \ + echo "$(RM) $(DESTDIR)$(INSTALLTOP)/include/openssl/$$fn"; \ + $(RM) $(DESTDIR)$(INSTALLTOP)/include/openssl/$$fn; \ + done + -$(RMDIR) $(DESTDIR)$(INSTALLTOP)/include/openssl + -$(RMDIR) $(DESTDIR)$(INSTALLTOP)/include + @set -e; for l in $(INSTALL_LIBS); do \ + fn=`basename $$l`; \ + echo "$(RM) $(DESTDIR)$(INSTALLTOP)/$(LIBDIR)/$$fn"; \ + $(RM) $(DESTDIR)$(INSTALLTOP)/$(LIBDIR)/$$fn; \ + done + @ : {- output_off() if $disabled{shared}; "" -} + @set -e; for s in $(INSTALL_SHLIB_INFO); do \ + s1=`echo "$$s" | cut -f1 -d";"`; \ + s2=`echo "$$s" | cut -f2 -d";"`; \ + fn1=`basename $$s1`; \ + fn2=`basename $$s2`; \ + : {- output_off() if windowsdll(); "" -}; \ + echo "$(RM) $(DESTDIR)$(INSTALLTOP)/$(LIBDIR)/$$fn1"; \ + $(RM) $(DESTDIR)$(INSTALLTOP)/$(LIBDIR)/$$fn1; \ + if [ "$$fn1" != "$$fn2" ]; then \ + echo "$(RM) $(DESTDIR)$(INSTALLTOP)/$(LIBDIR)/$$fn2"; \ + $(RM) $(DESTDIR)$(INSTALLTOP)/$(LIBDIR)/$$fn2; \ + fi; \ + : {- output_on() if windowsdll(); "" -}{- output_off() unless windowsdll(); "" -}; \ + echo "$(RM) $(DESTDIR)$(INSTALLTOP)/$(LIBDIR)/$$fn2"; \ + $(RM) $(DESTDIR)$(INSTALLTOP)/$(LIBDIR)/$$fn2; \ + : {- output_on() unless windowsdll(); "" -}; \ + done + @ : {- output_on() if $disabled{shared}; "" -} + $(RM) $(DESTDIR)$(INSTALLTOP)/$(LIBDIR)/pkgconfig/libcrypto.pc + $(RM) $(DESTDIR)$(INSTALLTOP)/$(LIBDIR)/pkgconfig/libssl.pc + $(RM) $(DESTDIR)$(INSTALLTOP)/$(LIBDIR)/pkgconfig/openssl.pc + -$(RMDIR) $(DESTDIR)$(INSTALLTOP)/$(LIBDIR)/pkgconfig + -$(RMDIR) $(DESTDIR)$(INSTALLTOP)/$(LIBDIR) + +install_engines: + @[ -n "$(INSTALLTOP)" ] || (echo INSTALLTOP should not be empty; exit 1) + @$(PERL) $(SRCDIR)/util/mkdir-p.pl $(DESTDIR)$(ENGINESDIR)/ + @echo "*** Installing engines" + @set -e; for e in dummy $(INSTALL_ENGINES); do \ + if [ "$$e" = "dummy" ]; then continue; fi; \ + fn=`basename $$e`; \ + echo "install $$e -> $(DESTDIR)$(ENGINESDIR)/$$fn"; \ + cp $$e $(DESTDIR)$(ENGINESDIR)/$$fn.new; \ + chmod 755 $(DESTDIR)$(ENGINESDIR)/$$fn.new; \ + mv -f $(DESTDIR)$(ENGINESDIR)/$$fn.new \ + $(DESTDIR)$(ENGINESDIR)/$$fn; \ + done + +uninstall_engines: + @echo "*** Uninstalling engines" + @set -e; for e in dummy $(INSTALL_ENGINES); do \ + if [ "$$e" = "dummy" ]; then continue; fi; \ + fn=`basename $$e`; \ + if [ "$$fn" = '{- dso("ossltest") -}' ]; then \ + continue; \ + fi; \ + echo "$(RM) $(DESTDIR)$(ENGINESDIR)/$$fn"; \ + $(RM) $(DESTDIR)$(ENGINESDIR)/$$fn; \ + done + -$(RMDIR) $(DESTDIR)$(ENGINESDIR) + +install_runtime: + @[ -n "$(INSTALLTOP)" ] || (echo INSTALLTOP should not be empty; exit 1) + @$(PERL) $(SRCDIR)/util/mkdir-p.pl $(DESTDIR)$(INSTALLTOP)/bin + @ : {- output_off() if windowsdll(); "" -} + @$(PERL) $(SRCDIR)/util/mkdir-p.pl $(DESTDIR)$(INSTALLTOP)/$(LIBDIR) + @ : {- output_on() if windowsdll(); "" -} + @echo "*** Installing runtime files" + @set -e; for s in dummy $(INSTALL_SHLIBS); do \ + if [ "$$s" = "dummy" ]; then continue; fi; \ + fn=`basename $$s`; \ + : {- output_off() unless windowsdll(); "" -}; \ + echo "install $$s -> $(DESTDIR)$(INSTALLTOP)/bin/$$fn"; \ + cp $$s $(DESTDIR)$(INSTALLTOP)/bin/$$fn.new; \ + chmod 644 $(DESTDIR)$(INSTALLTOP)/bin/$$fn.new; \ + mv -f $(DESTDIR)$(INSTALLTOP)/bin/$$fn.new \ + $(DESTDIR)$(INSTALLTOP)/bin/$$fn; \ + : {- output_on() unless windowsdll(); "" -}{- output_off() if windowsdll(); "" -}; \ + echo "install $$s -> $(DESTDIR)$(INSTALLTOP)/$(LIBDIR)/$$fn"; \ + cp $$s $(DESTDIR)$(INSTALLTOP)/$(LIBDIR)/$$fn.new; \ + chmod 755 $(DESTDIR)$(INSTALLTOP)/$(LIBDIR)/$$fn.new; \ + mv -f $(DESTDIR)$(INSTALLTOP)/$(LIBDIR)/$$fn.new \ + $(DESTDIR)$(INSTALLTOP)/$(LIBDIR)/$$fn; \ + : {- output_on() if windowsdll(); "" -}; \ + done + @set -e; for x in dummy $(INSTALL_PROGRAMS); do \ + if [ "$$x" = "dummy" ]; then continue; fi; \ + fn=`basename $$x`; \ + echo "install $$x -> $(DESTDIR)$(INSTALLTOP)/bin/$$fn"; \ + cp $$x $(DESTDIR)$(INSTALLTOP)/bin/$$fn.new; \ + chmod 755 $(DESTDIR)$(INSTALLTOP)/bin/$$fn.new; \ + mv -f $(DESTDIR)$(INSTALLTOP)/bin/$$fn.new \ + $(DESTDIR)$(INSTALLTOP)/bin/$$fn; \ + echo "link $(DESTDIR)$(INSTALLTOP)/bin/$$fn -> $(DESTDIR)$(INSTALLTOP)/bin/openssl"; \ + ln -sf $$fn $(DESTDIR)$(INSTALLTOP)/bin/openssl; \ + done + @set -e; for x in dummy $(BIN_SCRIPTS); do \ + if [ "$$x" = "dummy" ]; then continue; fi; \ + fn=`basename $$x`; \ + echo "install $$x -> $(DESTDIR)$(INSTALLTOP)/bin/$$fn"; \ + cp $$x $(DESTDIR)$(INSTALLTOP)/bin/$$fn.new; \ + chmod 755 $(DESTDIR)$(INSTALLTOP)/bin/$$fn.new; \ + mv -f $(DESTDIR)$(INSTALLTOP)/bin/$$fn.new \ + $(DESTDIR)$(INSTALLTOP)/bin/$$fn; \ + done + +uninstall_runtime: + @echo "*** Uninstalling runtime files" + @set -e; for x in dummy $(INSTALL_PROGRAMS); \ + do \ + if [ "$$x" = "dummy" ]; then continue; fi; \ + fn=`basename $$x`; \ + echo "$(RM) $(DESTDIR)$(INSTALLTOP)/bin/$$fn"; \ + $(RM) $(DESTDIR)$(INSTALLTOP)/bin/$$fn; \ + done; + @set -e; for x in dummy $(BIN_SCRIPTS); \ + do \ + if [ "$$x" = "dummy" ]; then continue; fi; \ + fn=`basename $$x`; \ + echo "$(RM) $(DESTDIR)$(INSTALLTOP)/bin/$$fn"; \ + $(RM) $(DESTDIR)$(INSTALLTOP)/bin/$$fn; \ + done + @ : {- output_off() unless windowsdll(); "" -} + @set -e; for s in dummy $(INSTALL_SHLIBS); do \ + if [ "$$s" = "dummy" ]; then continue; fi; \ + fn=`basename $$s`; \ + echo "$(RM) $(DESTDIR)$(INSTALLTOP)/bin/$$fn"; \ + $(RM) $(DESTDIR)$(INSTALLTOP)/bin/$$fn; \ + done + @ : {- output_on() unless windowsdll(); "" -} + -$(RMDIR) $(DESTDIR)$(INSTALLTOP)/bin + +# A method to extract all names from a .pod file +# The first sed extracts everything between "=head1 NAME" and the next =head1 +# The perl command joins all the lines into one +# The second sed removes the description and turns all commas into spaces +# Voilà, you have a space separated list of names! +EXTRACT_NAMES=sed -e '1,/^=head1 *NAME *$$/d;/^=head1/,$$d' | \ + $(PERL) -p -0 -e 's/\n/ /g; END {print "\n"}' | \ + sed -e 's/ - .*$$//;s/,/ /g' +PROCESS_PODS=\ + set -e; \ + here=`cd $(SRCDIR); pwd`; \ + point=$$here/util/point.sh; \ + for ds in apps:1 crypto:3 ssl:3; do \ + defdir=`echo $$ds | cut -f1 -d:`; \ + defsec=`echo $$ds | cut -f2 -d:`; \ + for p in $(SRCDIR)/doc/$$defdir/*.pod; do \ + SEC=`sed -ne 's/^=for *comment *openssl_manual_section: *\([0-9]\) *$$/\1/p' $$p`; \ + [ -z "$$SEC" ] && SEC=$$defsec; \ + fn=`basename $$p .pod`; \ + Name=$$fn; \ + NAME=`echo $$fn | tr '[a-z]' '[A-Z]'`; \ + suf=`eval "echo $$OUTSUFFIX"`; \ + top=`eval "echo $$OUTTOP"`; \ + $(PERL) $(SRCDIR)/util/mkdir-p.pl $$top/man$$SEC; \ + echo "install $$p -> $$top/man$$SEC/$$fn$$suf"; \ + cat $$p | eval "$$GENERATE" \ + > $$top/man$$SEC/$$fn$$suf; \ + names=`cat $$p | $(EXTRACT_NAMES)`; \ + ( cd $$top/man$$SEC; \ + for n in $$names; do \ + comp_n="$$n"; \ + comp_fn="$$fn"; \ + case "$(PLATFORM)" in DJGPP|Cygwin*|mingw*|darwin*-*-cc) \ + comp_n=`echo "$$n" | tr '[A-Z]' '[a-z]'`; \ + comp_fn=`echo "$$fn" | tr '[A-Z]' '[a-z]'`; \ + ;; \ + esac; \ + if [ "$$comp_n" != "$$comp_fn" ]; then \ + echo "link $$top/man$$SEC/$$n$$suf -> $$top/man$$SEC/$$fn$$suf"; \ + PLATFORM=$(PLATFORM) $$point $$fn$$suf $$n$$suf; \ + fi; \ + done ); \ + done; \ + done +UNINSTALL_DOCS=\ + set -e; \ + here=`cd $(SRCDIR); pwd`; \ + for ds in apps:1 crypto:3 ssl:3; do \ + defdir=`echo $$ds | cut -f1 -d:`; \ + defsec=`echo $$ds | cut -f2 -d:`; \ + for p in $(SRCDIR)/doc/$$defdir/*.pod; do \ + SEC=`sed -ne 's/^=for *comment *openssl_manual_section: *\([0-9]\) *$$/\1/p' $$p`; \ + [ -z "$$SEC" ] && SEC=$$defsec; \ + fn=`basename $$p .pod`; \ + suf=`eval "echo $$OUTSUFFIX"`; \ + top=`eval "echo $$OUTTOP"`; \ + echo "$(RM) $$top/man$$SEC/$$fn$$suf"; \ + $(RM) $$top/man$$SEC/$$fn$$suf; \ + names=`cat $$p | $(EXTRACT_NAMES)`; \ + for n in $$names; do \ + comp_n="$$n"; \ + comp_fn="$$fn"; \ + case "$(PLATFORM)" in DJGPP|Cygwin*|mingw*|darwin*-*-cc) \ + comp_n=`echo "$$n" | tr '[A-Z]' '[a-z]'`; \ + comp_fn=`echo "$$fn" | tr '[A-Z]' '[a-z]'`; \ + ;; \ + esac; \ + if [ "$$comp_n" != "$$comp_fn" ]; then \ + echo "$(RM) $$top/man$$SEC/$$n$$suf"; \ + $(RM) $$top/man$$SEC/$$n$$suf; \ + fi; \ + done; \ + ( $(RMDIR) $$top/man$$SEC 2>/dev/null || exit 0 ); \ + done; \ + done + +install_man_docs: + @[ -n "$(INSTALLTOP)" ] || (echo INSTALLTOP should not be empty; exit 1) + @echo "*** Installing manpages" + @\ + OUTSUFFIX='.$${SEC}$(MANSUFFIX)'; \ + OUTTOP="$(DESTDIR)$(MANDIR)"; \ + GENERATE='pod2man --name=$$NAME --section=$$SEC --center=GmSSL --release=$(VERSION)'; \ + $(PROCESS_PODS) + +uninstall_man_docs: + @echo "*** Uninstalling manpages" + @\ + OUTSUFFIX='.$${SEC}$(MANSUFFIX)'; \ + OUTTOP="$(DESTDIR)$(MANDIR)"; \ + $(UNINSTALL_DOCS) + +install_html_docs: + @[ -n "$(INSTALLTOP)" ] || (echo INSTALLTOP should not be empty; exit 1) + @echo "*** Installing HTML manpages" + @\ + OUTSUFFIX='.$(HTMLSUFFIX)'; \ + OUTTOP="$(DESTDIR)$(HTMLDIR)"; \ + GENERATE="pod2html --podroot=$(SRCDIR)/doc --htmldir=.. \ + --podpath=apps:crypto:ssl --title=\$$Name \ + | perl -pe 's|href=\"http://man.he.net/man|href=\"../man|g; s|href=\"(.*/man.*)(?|href=\"\$$1.html\">|g;'"; \ + $(PROCESS_PODS) + +uninstall_html_docs: + @echo "*** Uninstalling manpages" + @\ + OUTSUFFIX='.$(HTMLSUFFIX)'; \ + OUTTOP="$(DESTDIR)$(HTMLDIR)"; \ + $(UNINSTALL_DOCS) + + +# Developer targets (note: these are only available on Unix) ######### + +update: generate errors ordinals + +generate: generate_apps generate_crypto_bn generate_crypto_objects \ + generate_crypto_conf generate_crypto_asn1 + +# Test coverage is a good idea for the future +#coverage: $(PROGRAMS) $(TESTPROGRAMS) +# ... + +lint: + lint -DLINT $(INCLUDES) $(SRCS) + +{- # because the program apps/openssl has object files as sources, and + # they then have the corresponding C files as source, we need to chain + # the lookups in %unified_info + my $apps_openssl = catfile("apps","gmssl"); + our @openssl_source = map { @{$unified_info{sources}->{$_}} } + @{$unified_info{sources}->{$apps_openssl}}; + ""; -} +generate_apps: + ( cd $(SRCDIR); $(PERL) VMS/VMSify-conf.pl \ + < apps/openssl.cnf > apps/openssl-vms.cnf ) + ( b=`pwd`; cd $(SRCDIR); $(PERL) -I$$b apps/progs.pl \ + {- join(" ", @openssl_source) -} \ + > apps/progs.h ) + +generate_crypto_bn: + ( cd $(SRCDIR); $(PERL) crypto/bn/bn_prime.pl > crypto/bn/bn_prime.h ) + +generate_crypto_objects: + ( cd $(SRCDIR); $(PERL) crypto/objects/objects.pl \ + crypto/objects/objects.txt \ + crypto/objects/obj_mac.num \ + include/openssl/obj_mac.h ) + ( cd $(SRCDIR); $(PERL) crypto/objects/obj_dat.pl \ + include/openssl/obj_mac.h \ + crypto/objects/obj_dat.h ) + ( cd $(SRCDIR); $(PERL) crypto/objects/objxref.pl \ + crypto/objects/obj_mac.num \ + crypto/objects/obj_xref.txt \ + > crypto/objects/obj_xref.h ) + +generate_crypto_conf: + ( cd $(SRCDIR); $(PERL) crypto/conf/keysets.pl \ + > crypto/conf/conf_def.h ) + +generate_crypto_asn1: + ( cd $(SRCDIR); $(PERL) crypto/asn1/charmap.pl \ + > crypto/asn1/charmap.h ) + +errors: + ( cd $(SRCDIR); $(PERL) util/ck_errf.pl -strict */*.c */*/*.c ) + ( cd $(SRCDIR); $(PERL) util/mkerr.pl -recurse -write ) + ( cd $(SRCDIR)/engines; \ + for e in *.ec; do \ + $(PERL) ../util/mkerr.pl -conf $$e \ + -nostatic -staticloader -write *.c; \ + done ) + +ordinals: + ( b=`pwd`; cd $(SRCDIR); $(PERL) -I$$b util/mkdef.pl crypto update ) + ( b=`pwd`; cd $(SRCDIR); $(PERL) -I$$b util/mkdef.pl ssl update ) + +test_ordinals: + ( cd test; \ + SRCTOP=../$(SRCDIR) \ + BLDTOP=../$(BLDDIR) \ + $(PERL) ../$(SRCDIR)/test/run_tests.pl test_ordinals ) + +tags TAGS: FORCE + rm -f TAGS tags + -ctags -R . + -etags `find . -name '*.[ch]' -o -name '*.pm'` + +# Release targets (note: only available on Unix) ##################### + +TAR_COMMAND=$(TAR) $(TARFLAGS) --owner 0 --group 0 -cvf - +PREPARE_CMD=: +tar: + TMPDIR=/var/tmp/openssl-copy.$$$$; \ + DISTDIR=$(NAME); \ + mkdir -p $$TMPDIR/$$DISTDIR; \ + (cd $(SRCDIR); \ + git ls-tree -r --name-only --full-tree HEAD \ + | grep -v '^fuzz/corpora' \ + | while read F; do \ + mkdir -p $$TMPDIR/$$DISTDIR/`dirname $$F`; \ + cp $$F $$TMPDIR/$$DISTDIR/$$F; \ + done); \ + (cd $$TMPDIR; \ + $(PREPARE_CMD); \ + find $$TMPDIR/$$DISTDIR -type d -print | xargs chmod 755; \ + find $$TMPDIR/$$DISTDIR -type f -print | xargs chmod a+r; \ + find $$TMPDIR/$$DISTDIR -type f -perm -0100 -print | xargs chmod a+x; \ + $(TAR_COMMAND) $$DISTDIR) \ + | (cd $(SRCDIR); gzip --best > $(TARFILE).gz); \ + rm -rf $$TMPDIR + cd $(SRCDIR); ls -l $(TARFILE).gz + +dist: + @$(MAKE) PREPARE_CMD='$(PERL) ./Configure dist' tar + +# Helper targets ##################################################### + +link-utils: $(BLDDIR)/util/opensslwrap.sh + +$(BLDDIR)/util/opensslwrap.sh: configdata.pm + @if [ "$(SRCDIR)" != "$(BLDDIR)" ]; then \ + mkdir -p "$(BLDDIR)/util"; \ + ln -sf "../$(SRCDIR)/util/opensslwrap.sh" "$(BLDDIR)/util"; \ + fi + +FORCE: + +# Building targets ################################################### + +libcrypto.pc libssl.pc openssl.pc: configdata.pm $(LIBS) {- join(" ",map { shlib_simple($_) } @{$unified_info{libraries}}) -} +libcrypto.pc: + @ ( echo 'prefix=$(INSTALLTOP)'; \ + echo 'exec_prefix=$${prefix}'; \ + echo 'libdir=$${exec_prefix}/$(LIBDIR)'; \ + echo 'includedir=$${prefix}/include'; \ + echo 'enginesdir=$${libdir}/engines-{- $sover -}'; \ + echo ''; \ + echo 'Name: GmSSL-libcrypto'; \ + echo 'Description: GmSSL cryptography library'; \ + echo 'Version: '$(VERSION); \ + echo 'Libs: -L$${libdir} -lcrypto'; \ + echo 'Libs.private: $(EX_LIBS)'; \ + echo 'Cflags: -I$${includedir}' ) > libcrypto.pc + +libssl.pc: + @ ( echo 'prefix=$(INSTALLTOP)'; \ + echo 'exec_prefix=$${prefix}'; \ + echo 'libdir=$${exec_prefix}/$(LIBDIR)'; \ + echo 'includedir=$${prefix}/include'; \ + echo ''; \ + echo 'Name: GmSSL-libssl'; \ + echo 'Description: Secure Sockets Layer and cryptography libraries'; \ + echo 'Version: '$(VERSION); \ + echo 'Requires.private: libcrypto'; \ + echo 'Libs: -L$${libdir} -lssl'; \ + echo 'Libs.private: $(EX_LIBS)'; \ + echo 'Cflags: -I$${includedir}' ) > libssl.pc + +openssl.pc: + @ ( echo 'prefix=$(INSTALLTOP)'; \ + echo 'exec_prefix=$${prefix}'; \ + echo 'libdir=$${exec_prefix}/$(LIBDIR)'; \ + echo 'includedir=$${prefix}/include'; \ + echo ''; \ + echo 'Name: GmSSL'; \ + echo 'Description: Secure Sockets Layer and cryptography libraries and tools'; \ + echo 'Version: '$(VERSION); \ + echo 'Requires: libssl libcrypto' ) > openssl.pc + +configdata.pm: $(SRCDIR)/Configure $(SRCDIR)/config {- join(" ", @{$config{build_file_templates}}, @{$config{build_infos}}, @{$config{conf_files}}) -} + @echo "Detected changed: $?" + @echo "Reconfiguring..." + $(PERL) $(SRCDIR)/Configure reconf + @echo "**************************************************" + @echo "*** ***" + @echo "*** Please run the same make command again ***" + @echo "*** ***" + @echo "**************************************************" + @false + +{- + use File::Basename; + use File::Spec::Functions qw/:DEFAULT abs2rel rel2abs/; + + # Helper function to figure out dependencies on libraries + # It takes a list of library names and outputs a list of dependencies + sub compute_lib_depends { + if ($disabled{shared}) { + return map { $_.$libext } @_; + } + + # Depending on shared libraries: + # On Windows POSIX layers, we depend on {libname}.dll.a + # On Unix platforms, we depend on {shlibname}.so + return map { shlib_simple($_) } @_; + } + + sub generatesrc { + my %args = @_; + my $generator = join(" ", @{$args{generator}}); + my $generator_incs = join("", map { " -I".$_ } @{$args{generator_incs}}); + my $incs = join("", map { " -I".$_ } @{$args{incs}}); + my $deps = join(" ", @{$args{generator_deps}}, @{$args{deps}}); + + if ($args{src} !~ /\.[sS]$/) { + if ($args{generator}->[0] =~ m|^.*\.in$|) { + my $dofile = abs2rel(rel2abs(catfile($config{sourcedir}, + "util", "dofile.pl")), + rel2abs($config{builddir})); + return <<"EOF"; +$args{src}: $args{generator}->[0] $deps + \$(PERL) "-I\$(BLDDIR)" -Mconfigdata "$dofile" \\ + "-o$target{build_file}" $generator > \$@ +EOF + } else { + return <<"EOF"; +$args{src}: $args{generator}->[0] $deps + \$(PERL)$generator_incs $generator > \$@ +EOF + } + } else { + if ($args{generator}->[0] =~ /\.pl$/) { + $generator = 'CC="$(CC)" $(PERL)'.$generator_incs.' '.$generator; + } elsif ($args{generator}->[0] =~ /\.m4$/) { + $generator = 'm4 -B 8192'.$generator_incs.' '.$generator.' >' + } elsif ($args{generator}->[0] =~ /\.S$/) { + $generator = undef; + } else { + die "Generator type for $args{src} unknown: $generator\n"; + } + + if (defined($generator)) { + # If the target is named foo.S in build.info, we want to + # end up generating foo.s in two steps. + if ($args{src} =~ /\.S$/) { + (my $target = $args{src}) =~ s|\.S$|.s|; + return <<"EOF"; +$target: $args{generator}->[0] $deps + ( trap "rm -f \$@.*" INT 0; \\ + $generator \$@.S; \\ + \$(CC) $incs \$(CFLAGS) -E \$@.S | \\ + \$(PERL) -ne '/^#(line)?\\s*[0-9]+/ or print' > \$@.i && \\ + mv -f \$@.i \$@ ) +EOF + } + # Otherwise.... + return <<"EOF"; +$args{src}: $args{generator}->[0] $deps + $generator \$@ +EOF + } + return <<"EOF"; +$args{src}: $args{generator}->[0] $deps + \$(CC) $incs \$(CFLAGS) -E \$< | \\ + \$(PERL) -ne '/^#(line)?\\s*[0-9]+/ or print' > \$@ +EOF + } + } + + # Should one wonder about the end of the Perl snippet, it's because this + # second regexp eats up line endings as well, if the removed path is the + # last in the line. We may therefore need to put back a line ending. + sub src2obj { + my %args = @_; + my $obj = $args{obj}; + my @srcs = map { if ($unified_info{generate}->{$_}) { + (my $x = $_) =~ s/\.S$/.s/; $x + } else { + $_ + } + } ( @{$args{srcs}} ); + my $srcs = join(" ", @srcs); + my $deps = join(" ", @srcs, @{$args{deps}}); + my $incs = join("", map { " -I".$_ } @{$args{incs}}); + unless ($disabled{zlib}) { + if ($withargs{zlib_include}) { + $incs .= " -I".$withargs{zlib_include}; + } + } + my $ecflags = { lib => '$(LIB_CFLAGS)', + dso => '$(DSO_CFLAGS)', + bin => '$(BIN_CFLAGS)' } -> {$args{intent}}; + my $makedepprog = $config{makedepprog}; + my $recipe = <<"EOF"; +$obj$objext: $deps +EOF + if (!$disabled{makedepend} && $makedepprog !~ /\/makedepend/) { + $recipe .= <<"EOF"; + \$(CC) $incs \$(CFLAGS) $ecflags -MMD -MF $obj$depext.tmp -MT \$\@ -c -o \$\@ $srcs + \@touch $obj$depext.tmp + \@if cmp $obj$depext.tmp $obj$depext > /dev/null 2> /dev/null; then \\ + rm -f $obj$depext.tmp; \\ + else \\ + mv $obj$depext.tmp $obj$depext; \\ + fi +EOF + } else { + $recipe .= <<"EOF"; + \$(CC) $incs \$(CFLAGS) $ecflags -c -o \$\@ $srcs +EOF + if (!$disabled{makedepend} && $makedepprog =~ /\/makedepend/) { + $recipe .= <<"EOF"; + -\$(MAKEDEPEND) -f- -o"|\$\@" -- $incs \$(CFLAGS) $ecflags -- $srcs \\ + >$obj$depext.tmp 2>/dev/null + -\$(PERL) -i -pe 's/^.*\\|//; s/ \\/(\\\\.|[^ ])*//; \$\$_ = undef if (/: *\$\$/ || /^(#.*| *)\$\$/); \$\$_.="\\n" unless !defined(\$\$_) or /\\R\$\$/g;' $obj$depext.tmp + \@if cmp $obj$depext.tmp $obj$depext > /dev/null 2> /dev/null; then \\ + rm -f $obj$depext.tmp; \\ + else \\ + mv $obj$depext.tmp $obj$depext; \\ + fi +EOF + } + } + return $recipe; + } + # On Unix, we build shlibs from static libs, so we're ignoring the + # object file array. We *know* this routine is only called when we've + # configure 'shared'. + sub libobj2shlib { + my %args = @_; + my $lib = $args{lib}; + my $shlib = $args{shlib}; + my $libd = dirname($lib); + my $libn = basename($lib); + (my $libname = $libn) =~ s/^lib//; + my $linklibs = join("", map { my $d = dirname($_); + my $f = basename($_); + (my $l = $f) =~ s/^lib//; + " -L$d -l$l" } @{$args{deps}}); + my $deps = join(" ",compute_lib_depends(@{$args{deps}})); + my $shlib_target = $target{shared_target}; + my $ordinalsfile = defined($args{ordinals}) ? $args{ordinals}->[1] : ""; + my $target = shlib_simple($lib); + return <<"EOF" +# With a build on a Windows POSIX layer (Cygwin or Mingw), we know for a fact +# that two files get produced, {shlibname}.dll and {libname}.dll.a. +# With all other Unix platforms, we often build a shared library with the +# SO version built into the file name and a symlink without the SO version +# It's not necessary to have both as targets. The choice falls on the +# simplest, {libname}$shlibextimport for Windows POSIX layers and +# {libname}$shlibextsimple for the Unix platforms. +$target: $lib$libext $deps $ordinalsfile + \$(MAKE) -f \$(SRCDIR)/Makefile.shared -e \\ + PLATFORM=\$(PLATFORM) \\ + PERL="\$(PERL)" SRCDIR='\$(SRCDIR)' DSTDIR="$libd" \\ + INSTALLTOP='\$(INSTALLTOP)' LIBDIR='\$(LIBDIR)' \\ + LIBDEPS='\$(PLIB_LDFLAGS) '"$linklibs"' \$(EX_LIBS)' \\ + LIBNAME=$libname LIBVERSION=\$(SHLIB_MAJOR).\$(SHLIB_MINOR) \\ + LIBCOMPATVERSIONS=';\$(SHLIB_VERSION_HISTORY)' \\ + CC='\$(CC)' CFLAGS='\$(CFLAGS) \$(LIB_CFLAGS)' \\ + LDFLAGS='\$(LDFLAGS)' \\ + SHARED_LDFLAGS='\$(LIB_LDFLAGS)' SHLIB_EXT=$shlibext \\ + RC='\$(RC)' SHARED_RCFLAGS='\$(RCFLAGS)' \\ + link_shlib.$shlib_target +EOF + . (windowsdll() ? <<"EOF" : ""); + rm -f apps/$shlib$shlibext + rm -f test/$shlib$shlibext + cp -p $shlib$shlibext apps/ + cp -p $shlib$shlibext test/ +EOF + } + sub obj2dso { + my %args = @_; + my $lib = $args{lib}; + my $libd = dirname($lib); + my $libn = basename($lib); + (my $libname = $libn) =~ s/^lib//; + my $shlibdeps = join("", map { my $d = dirname($_); + my $f = basename($_); + (my $l = $f) =~ s/^lib//; + " -L$d -l$l" } @{$args{deps}}); + my $deps = join(" ",compute_lib_depends(@{$args{deps}})); + my $shlib_target = $target{shared_target}; + my $objs = join(" ", map { $_.$objext } @{$args{objs}}); + my $target = dso($lib); + return <<"EOF"; +$target: $objs $deps + \$(MAKE) -f \$(SRCDIR)/Makefile.shared -e \\ + PLATFORM=\$(PLATFORM) \\ + PERL="\$(PERL)" SRCDIR='\$(SRCDIR)' DSTDIR="$libd" \\ + LIBDEPS='\$(PLIB_LDFLAGS) '"$shlibdeps"' \$(EX_LIBS)' \\ + LIBNAME=$libname LDFLAGS='\$(LDFLAGS)' \\ + CC='\$(CC)' CFLAGS='\$(CFLAGS) \$(DSO_CFLAGS)' \\ + SHARED_LDFLAGS='\$(DSO_LDFLAGS)' \\ + SHLIB_EXT=$dsoext \\ + LIBEXTRAS="$objs" \\ + link_dso.$shlib_target +EOF + } + sub obj2lib { + my %args = @_; + my $lib = $args{lib}; + my $objs = join(" ", map { $_.$objext } @{$args{objs}}); + return <<"EOF"; +$lib$libext: $objs + \$(AR) \$\@ \$\? + \$(RANLIB) \$\@ || echo Never mind. +EOF + } + sub obj2bin { + my %args = @_; + my $bin = $args{bin}; + my $bind = dirname($bin); + my $binn = basename($bin); + my $objs = join(" ", map { $_.$objext } @{$args{objs}}); + my $deps = join(" ",compute_lib_depends(@{$args{deps}})); + my $linklibs = join("", map { my $d = dirname($_); + my $f = basename($_); + $d = "." if $d eq $f; + (my $l = $f) =~ s/^lib//; + " -L$d -l$l" } @{$args{deps}}); + my $shlib_target = $disabled{shared} ? "" : $target{shared_target}; + return <<"EOF"; +$bin$exeext: $objs $deps + \$(RM) $bin$exeext + \$(MAKE) -f \$(SRCDIR)/Makefile.shared -e \\ + PERL="\$(PERL)" SRCDIR=\$(SRCDIR) \\ + APPNAME=$bin$exeext OBJECTS="$objs" \\ + LIBDEPS='\$(PLIB_LDFLAGS) '"$linklibs"' \$(EX_LIBS)' \\ + CC='\$(CC)' CFLAGS='\$(CFLAGS) \$(BIN_CFLAGS)' \\ + LDFLAGS='\$(LDFLAGS)' \\ + link_app.$shlib_target +EOF + } + sub in2script { + my %args = @_; + my $script = $args{script}; + my $sources = join(" ", @{$args{sources}}); + my $dofile = abs2rel(rel2abs(catfile($config{sourcedir}, + "util", "dofile.pl")), + rel2abs($config{builddir})); + return <<"EOF"; +$script: $sources + \$(PERL) "-I\$(BLDDIR)" -Mconfigdata "$dofile" \\ + "-o$target{build_file}" $sources > "$script" + chmod a+x $script +EOF + } + sub generatedir { + my %args = @_; + my $dir = $args{dir}; + my @deps = map { s|\.o$|$objext|; $_ } @{$args{deps}}; + my @actions = (); + my %extinfo = ( dso => $dsoext, + lib => $libext, + bin => $exeext ); + + foreach my $type (("dso", "lib", "bin", "script")) { + next unless defined($unified_info{dirinfo}->{$dir}->{products}->{$type}); + # For lib object files, we could update the library. However, it + # was decided that it's enough to build the directory local object + # files, so we don't need to add any actions, and the dependencies + # are already taken care of. + if ($type ne "lib") { + foreach my $prod (@{$unified_info{dirinfo}->{$dir}->{products}->{$type}}) { + if (dirname($prod) eq $dir) { + push @deps, $prod.$extinfo{$type}; + } else { + push @actions, "\t@ : No support to produce $type ".join(", ", @{$unified_info{dirinfo}->{$dir}->{products}->{$type}}); + } + } + } + } + + my $deps = join(" ", @deps); + my $actions = join("\n", "", @actions); + return <<"EOF"; +$args{dir} $args{dir}/: $deps$actions +EOF + } + "" # Important! This becomes part of the template result. +-} diff --git a/Configurations/windows-makefile.tmpl b/Configurations/windows-makefile.tmpl new file mode 100644 index 000000000..ffa6d5f67 --- /dev/null +++ b/Configurations/windows-makefile.tmpl @@ -0,0 +1,603 @@ +## +## Makefile for GmSSL +## +## {- join("\n## ", @autowarntext) -} +{- + our $objext = $target{obj_extension} || ".obj"; + our $depext = $target{dep_extension} || ".d"; + our $exeext = $target{exe_extension} || ".exe"; + our $libext = $target{lib_extension} || ".lib"; + our $shlibext = $target{shared_extension} || ".dll"; + our $shlibextimport = $target{shared_import_extension} || ".lib"; + our $dsoext = $target{dso_extension} || ".dll"; + + our $sover = $config{shlib_major}."_".$config{shlib_minor}; + + my $win_installenv = + $target{build_scheme}->[2] eq "VC-W32" ? + "ProgramFiles(x86)" : "ProgramW6432"; + my $win_commonenv = + $target{build_scheme}->[2] eq "VC-W32" + ? "CommonProgramFiles(x86)" : "CommonProgramW6432"; + our $win_installroot = + defined($ENV{$win_installenv}) + ? $win_installenv : 'ProgramFiles'; + our $win_commonroot = + defined($ENV{$win_commonenv}) + ? $win_commonenv : 'CommonProgramFiles'; + + # expand variables early + $win_installroot = $ENV{$win_installroot}; + $win_commonroot = $ENV{$win_commonroot}; + + sub shlib { + return () if $disabled{shared}; + my $lib = shift; + return $unified_info{sharednames}->{$lib} . $shlibext; + } + + sub shlib_import { + return () if $disabled{shared}; + my $lib = shift; + return $lib . $shlibextimport; + } + + sub dso { + my $dso = shift; + + return $dso . $dsoext; + } + ''; +-} + +PLATFORM={- $config{target} -} +SRCDIR={- $config{sourcedir} -} +BLDDIR={- $config{builddir} -} + +VERSION={- $config{version} -} +MAJOR={- $config{major} -} +MINOR={- $config{minor} -} + +SHLIB_VERSION_NUMBER={- $config{shlib_version_number} -} + +LIBS={- join(" ", map { $_.$libext } @{$unified_info{libraries}}) -} +SHLIBS={- join(" ", map { shlib($_) } @{$unified_info{libraries}}) -} +SHLIBPDBS={- join(" ", map { local $shlibext = ".pdb"; shlib($_) } @{$unified_info{libraries}}) -} +ENGINES={- join(" ", map { dso($_) } @{$unified_info{engines}}) -} +ENGINEPDBS={- join(" ", map { local $dsoext = ".pdb"; dso($_) } @{$unified_info{engines}}) -} +## FIXME: PROGRAMS might be too long, seperate it into 2 strings +PROGRAMS={- our @PROGRAMS = map { $_.$exeext } @{$unified_info{programs}}; join(" ", @PROGRAMS) -} +PROGRAMPDBS={- join(" ", map { $_.".pdb" } @{$unified_info{programs}}) -} +SCRIPTS={- join(" ", @{$unified_info{scripts}}) -} +{- output_off() if $disabled{makedepend}; "" -} +DEPS={- join(" ", map { (my $x = $_) =~ s|\.o$|$depext|; $x; } + grep { $unified_info{sources}->{$_}->[0] =~ /\.c$/ } + keys %{$unified_info{sources}}); -} +{- output_on() if $disabled{makedepend}; "" -} +GENERATED_MANDATORY={- join(" ", @{$unified_info{depends}->{""}} ) -} +GENERATED={- join(" ", + ( map { (my $x = $_) =~ s|\.[sS]$|\.asm|; $x } + grep { defined $unified_info{generate}->{$_} } + map { @{$unified_info{sources}->{$_}} } + grep { /\.o$/ } keys %{$unified_info{sources}} ), + ( grep { /\.h$/ } keys %{$unified_info{generate}} )) -} + +INSTALL_LIBS={- join(" ", map { $_.$libext } @{$unified_info{install}->{libraries}}) -} +INSTALL_SHLIBS={- join(" ", map { shlib($_) } @{$unified_info{install}->{libraries}}) -} +INSTALL_SHLIBPDBS={- join(" ", map { local $shlibext = ".pdb"; shlib($_) } @{$unified_info{install}->{libraries}}) -} +INSTALL_ENGINES={- join(" ", map { dso($_) } @{$unified_info{install}->{engines}}) -} +INSTALL_ENGINEPDBS={- join(" ", map { local $dsoext = ".pdb"; dso($_) } @{$unified_info{install}->{engines}}) -} +INSTALL_PROGRAMS={- join(" ", map { $_.$exeext } grep { !m|^test\\| } @{$unified_info{install}->{programs}}) -} +INSTALL_PROGRAMPDBS={- join(" ", map { $_.".pdb" } grep { !m|^test\\| } @{$unified_info{install}->{programs}}) -} +{- output_off() if $disabled{apps}; "" -} +BIN_SCRIPTS=$(BLDDIR)\tools\c_rehash.pl +MISC_SCRIPTS=$(BLDDIR)\apps\CA.pl $(BLDDIR)\apps\tsget.pl +{- output_on() if $disabled{apps}; "" -} + +# Do not edit these manually. Use Configure with --prefix or --openssldir +# to change this! Short explanation in the top comment in Configure +INSTALLTOP_dev={- # $prefix is used in the OPENSSLDIR perl snippet + # + use File::Spec::Functions qw(:DEFAULT splitpath); + our $prefix = $config{prefix} || "$win_installroot\\GmSSL"; + our ($prefix_dev, $prefix_dir, $prefix_file) = + splitpath($prefix, 1); + $prefix_dev -} +INSTALLTOP_dir={- $prefix_dir -} +OPENSSLDIR_dev={- # + # The logic here is that if no --openssldir was given, + # OPENSSLDIR will get the value from $prefix plus "/ssl". + # If --openssldir was given and the value is an absolute + # path, OPENSSLDIR will get its value without change. + # If the value from --openssldir is a relative path, + # OPENSSLDIR will get $prefix with the --openssldir + # value appended as a subdirectory. + # + use File::Spec::Functions qw(:DEFAULT splitpath); + our $openssldir = + $config{openssldir} ? + (file_name_is_absolute($config{openssldir}) ? + $config{openssldir} + : catdir($prefix, $config{openssldir})) + : "$win_commonroot\\SSL"; + our ($openssldir_dev, $openssldir_dir, $openssldir_file) = + splitpath($openssldir, 1); + $openssldir_dev -} +OPENSSLDIR_dir={- $openssldir_dir -} +LIBDIR={- our $libdir = $config{libdir} || "lib"; + $libdir -} +ENGINESDIR_dev={- use File::Spec::Functions qw(:DEFAULT splitpath); + our $enginesdir = catdir($prefix,$libdir,"engines-$sover"); + our ($enginesdir_dev, $enginesdir_dir, $enginesdir_file) = + splitpath($enginesdir, 1); + $enginesdir_dev -} +ENGINESDIR_dir={- $enginesdir_dir -} +!IF "$(DESTDIR)" != "" +INSTALLTOP=$(DESTDIR)$(INSTALLTOP_dir) +OPENSSLDIR=$(DESTDIR)$(OPENSSLDIR_dir) +ENGINESDIR=$(DESTDIR)$(ENGINESDIR_dir) +!ELSE +INSTALLTOP=$(INSTALLTOP_dev)$(INSTALLTOP_dir) +OPENSSLDIR=$(OPENSSLDIR_dev)$(OPENSSLDIR_dir) +ENGINESDIR=$(ENGINESDIR_dev)$(ENGINESDIR_dir) +!ENDIF + +CC={- $target{cc} -} +CFLAGS={- join(" ",(map { "-D".$_} @{$target{defines}}, @{$config{defines}})) -} {- join(" ", quotify_l("-DENGINESDIR=\"$enginesdir\"", "-DOPENSSLDIR=\"$openssldir\"")) -} {- $target{cflags} -} {- $config{cflags} -} +COUTFLAG={- $target{coutflag} || "/Fo" -}$(OSSL_EMPTY) +RC={- $target{rc} || "rc" -} +RCOUTFLAG={- $target{rcoutflag} || "/fo" -}$(OSSL_EMPTY) +LD={- $target{ld} || "link" -} +LDFLAGS={- $target{lflags} -} +LDOUTFLAG={- $target{loutflag} || "/out:" -}$(OSSL_EMPTY) +EX_LIBS={- $target{ex_libs} -} +LIB_CFLAGS={- join(" ", $target{lib_cflags}, $target{shared_cflag}) || "" -} +LIB_LDFLAGS={- $target{shared_ldflag} || "" -} +DSO_CFLAGS={- join(" ", $target{dso_cflags}, $target{shared_cflag}) || "" -} +DSO_LDFLAGS={- join(" ", $target{dso_lflags}, $target{shared_ldflag}) || "" -} +BIN_CFLAGS={- $target{bin_cflags} -} +BIN_LDFLAGS={- $target{bin_lflags} -} + +PERL={- $config{perl} -} + +AR={- $target{ar} -} +ARFLAGS= {- $target{arflags} -} +AROUTFLAG={- $target{aroutflag} || "/out:" -}$(OSSL_EMPTY) + +MT={- $target{mt} -} +MTFLAGS= {- $target{mtflags} -} +MTINFLAG={- $target{mtinflag} || "-manifest " -}$(OSSL_EMPTY) +MTOUTFLAG={- $target{mtoutflag} || "-outputresource:" -}$(OSSL_EMPTY) + +AS={- $target{as} -} +ASFLAGS={- $target{asflags} -} +ASOUTFLAG={- $target{asoutflag} -}$(OSSL_EMPTY) +PERLASM_SCHEME= {- $target{perlasm_scheme} -} + +PROCESSOR= {- $config{processor} -} + +# The main targets ################################################### + +all: build_generated \ + build_libs_nodep build_engines_nodep build_programs_nodep depend + +build_libs: build_generated build_libs_nodep depend +build_libs_nodep: $(LIBS) {- join(" ",map { shlib_import($_) } @{$unified_info{libraries}}) -} +build_engines: build_generated build_engines_nodep depend +build_engines_nodep: $(ENGINES) +build_programs: build_generated build_programs_nodep depend +build_programs_nodep: $(PROGRAMS) $(SCRIPTS) + +build_generated: $(GENERATED_MANDATORY) + +# Kept around for backward compatibility +build_apps build_tests: build_programs + +test: tests +tests: build_generated build_programs_nodep build_engines_nodep depend + @rem {- output_off() if $disabled{tests}; "" -} + set SRCTOP=$(SRCDIR) + set BLDTOP=$(BLDDIR) + set PERL=$(PERL) + set OPENSSL_DEBUG_MEMORY=on + "$(PERL)" "$(SRCDIR)\test\run_tests.pl" $(TESTS) + @rem {- if ($disabled{tests}) { output_on(); } else { output_off(); } "" -} + @echo "Tests are not supported with your chosen Configure options" + @rem {- output_on() if !$disabled{tests}; "" -} + +list-tests: + @rem {- output_off() if $disabled{tests}; "" -} + @set SRCTOP=$(SRCDIR) + @"$(PERL)" "$(SRCDIR)\test\run_tests.pl" list + @rem {- if ($disabled{tests}) { output_on(); } else { output_off(); } "" -} + @echo "Tests are not supported with your chosen Configure options" + @rem {- output_on() if !$disabled{tests}; "" -} + +install: install_sw install_ssldirs install_docs + +uninstall: uninstall_docs uninstall_sw + +libclean: + "$(PERL)" -e "map { m/(.*)\.dll$$/; unlink glob """$$1.*"""; } @ARGV" $(SHLIBS) + "$(PERL)" -e "map { m/(.*)\.dll$$/; unlink glob """apps/$$1.*"""; } @ARGV" $(SHLIBS) + "$(PERL)" -e "map { m/(.*)\.dll$$/; unlink glob """test/$$1.*"""; } @ARGV" $(SHLIBS) + -del /Q /F $(LIBS) + -del /Q ossl_static.pdb + +clean: libclean + {- join("\n\t", map { "-del /Q /F $_" } @PROGRAMS); -} + -del /Q /F $(ENGINES) $(SCRIPTS) + -del /Q /F $(GENERATED) + -del /Q /S /F *.d + -del /Q /S /F *.obj + -del /Q /S /F *.pdb + -del /Q /S /F *.exp + -del /Q /S /F engines\*.ilk + -del /Q /S /F engines\*.lib + -del /Q /S /F apps\*.lib + -del /Q /S /F engines\*.manifest + -del /Q /S /F apps\*.manifest + -del /Q /S /F test\*.manifest + +distclean: clean + -del /Q /F configdata.pm + -del /Q /F makefile + +depend: + +# Install helper targets ############################################# + +install_sw: all install_dev install_engines install_runtime + +uninstall_sw: uninstall_runtime uninstall_engines uninstall_dev + +install_docs: install_html_docs + +uninstall_docs: uninstall_html_docs + +install_ssldirs: + @"$(PERL)" "$(SRCDIR)\util\mkdir-p.pl" "$(OPENSSLDIR)\certs" + @"$(PERL)" "$(SRCDIR)\util\mkdir-p.pl" "$(OPENSSLDIR)\private" + @"$(PERL)" "$(SRCDIR)\util\mkdir-p.pl" "$(OPENSSLDIR)\misc" + @"$(PERL)" "$(SRCDIR)\util\copy.pl" "$(SRCDIR)\apps\openssl.cnf" \ + "$(OPENSSLDIR)\openssl.cnf.dist" + @IF NOT EXIST "$(OPENSSLDIR)\openssl.cnf" \ + "$(PERL)" "$(SRCDIR)\util\copy.pl" "$(SRCDIR)\apps\openssl.cnf" \ + "$(OPENSSLDIR)\openssl.cnf" + @"$(PERL)" "$(SRCDIR)\util\copy.pl" $(MISC_SCRIPTS) \ + "$(OPENSSLDIR)\misc" + +install_dev: + @if "$(INSTALLTOP)"=="" ( echo INSTALLTOP should not be empty & exit 1 ) + @echo *** Installing development files + @"$(PERL)" "$(SRCDIR)\util\mkdir-p.pl" "$(INSTALLTOP)\include\openssl" + @rem {- output_off() unless grep { $_ eq "OPENSSL_USE_APPLINK" } @{$target{defines}}; "" -} + @"$(PERL)" "$(SRCDIR)\util\copy.pl" "$(SRCDIR)\ms\applink.c" \ + "$(INSTALLTOP)\include\openssl" + @rem {- output_on() unless grep { $_ eq "OPENSSL_USE_APPLINK" } @{$target{defines}}; "" -} + @"$(PERL)" "$(SRCDIR)\util\copy.pl" "$(SRCDIR)\include\openssl\*.h" \ + "$(INSTALLTOP)\include\openssl" + @"$(PERL)" "$(SRCDIR)\util\copy.pl" $(BLDDIR)\include\openssl\*.h \ + "$(INSTALLTOP)\include\openssl" + @"$(PERL)" "$(SRCDIR)\util\mkdir-p.pl" "$(INSTALLTOP)\$(LIBDIR)" + @"$(PERL)" "$(SRCDIR)\util\copy.pl" $(INSTALL_LIBS) \ + "$(INSTALLTOP)\$(LIBDIR)" + @if "$(SHLIBS)"=="" \ + "$(PERL)" "$(SRCDIR)\util\copy.pl" ossl_static.pdb \ + "$(INSTALLTOP)\$(LIBDIR)" + +uninstall_dev: + +install_engines: + @if "$(INSTALLTOP)"=="" ( echo INSTALLTOP should not be empty & exit 1 ) + @echo *** Installing engines + @"$(PERL)" "$(SRCDIR)\util\mkdir-p.pl" "$(ENGINESDIR)" + @if not "$(ENGINES)"=="" \ + "$(PERL)" "$(SRCDIR)\util\copy.pl" $(INSTALL_ENGINES) "$(ENGINESDIR)" + @if not "$(ENGINES)"=="" \ + "$(PERL)" "$(SRCDIR)\util\copy.pl" $(INSTALL_ENGINEPDBS) "$(ENGINESDIR)" + +uninstall_engines: + +install_runtime: + @if "$(INSTALLTOP)"=="" ( echo INSTALLTOP should not be empty & exit 1 ) + @echo *** Installing runtime files + @"$(PERL)" "$(SRCDIR)\util\mkdir-p.pl" "$(INSTALLTOP)\bin" + @if not "$(SHLIBS)"=="" \ + "$(PERL)" "$(SRCDIR)\util\copy.pl" $(INSTALL_SHLIBS) "$(INSTALLTOP)\bin" + @if not "$(SHLIBS)"=="" \ + "$(PERL)" "$(SRCDIR)\util\copy.pl" $(INSTALL_SHLIBPDBS) \ + "$(INSTALLTOP)\bin" + @"$(PERL)" "$(SRCDIR)\util\copy.pl" $(INSTALL_PROGRAMS) \ + "$(INSTALLTOP)\bin" + @"$(PERL)" "$(SRCDIR)\util\copy.pl" $(INSTALL_PROGRAMPDBS) \ + "$(INSTALLTOP)\bin" + @"$(PERL)" "$(SRCDIR)\util\copy.pl" $(BIN_SCRIPTS) \ + "$(INSTALLTOP)\bin" + +uninstall_runtime: + +install_html_docs: + "$(PERL)" "$(SRCDIR)\util\process_docs.pl" \ + "--destdir=$(INSTALLTOP)\html" --type=html + +uninstall_html_docs: + +# Building targets ################################################### + +configdata.pm: "$(SRCDIR)\Configure" {- join(" ", map { '"'.$_.'"' } @{$config{build_file_templates}}, @{$config{build_infos}}, @{$config{conf_files}}) -} + @echo "Detected changed: $?" + @echo "Reconfiguring..." + "$(PERL)" "$(SRCDIR)\Configure" reconf + @echo "**************************************************" + @echo "*** ***" + @echo "*** Please run the same make command again ***" + @echo "*** ***" + @echo "**************************************************" + @exit 1 + +{- + use File::Basename; + use File::Spec::Functions qw/:DEFAULT abs2rel rel2abs/; + + # Helper function to figure out dependencies on libraries + # It takes a list of library names and outputs a list of dependencies + sub compute_lib_depends { + if ($disabled{shared}) { + return map { $_.$libext } @_; + } + return map { shlib_import($_) } @_; + } + + sub generatesrc { + my %args = @_; + (my $target = $args{src}) =~ s/\.[sS]$/.asm/; + my $generator = '"'.join('" "', @{$args{generator}}).'"'; + my $generator_incs = join("", map { " -I \"$_\"" } @{$args{generator_incs}}); + my $incs = join("", map { " /I \"$_\"" } @{$args{incs}}); + my $deps = @{$args{deps}} ? + '"'.join('" "', @{$args{generator_deps}}, @{$args{deps}}).'"' : ''; + + if ($target !~ /\.asm$/) { + if ($args{generator}->[0] =~ m|^.*\.in$|) { + my $dofile = abs2rel(rel2abs(catfile($config{sourcedir}, + "util", "dofile.pl")), + rel2abs($config{builddir})); + return <<"EOF"; +$target: "$args{generator}->[0]" $deps + "\$(PERL)" "-I\$(BLDDIR)" -Mconfigdata "$dofile" \\ + "-o$target{build_file}" $generator > \$@ +EOF + } else { + return <<"EOF"; +$target: "$args{generator}->[0]" $deps + "\$(PERL)"$generator_incs $generator > \$@ +EOF + } + } else { + if ($args{generator}->[0] =~ /\.pl$/) { + $generator = '"$(PERL)"'.$generator_incs.' '.$generator; + } elsif ($args{generator}->[0] =~ /\.S$/) { + $generator = undef; + } else { + die "Generator type for $src unknown: $generator\n"; + } + + if (defined($generator)) { + # If the target is named foo.S in build.info, we want to + # end up generating foo.s in two steps. + if ($args{src} =~ /\.S$/) { + return <<"EOF"; +$target: "$args{generator}->[0]" $deps + set ASM=\$(AS) + $generator \$@.S + \$(CC) $incs \$(CFLAGS) /EP /C \$@.S > \$@.i && move /Y \$@.i \$@ + del /Q \$@.S +EOF + } + # Otherwise.... + return <<"EOF"; +$target: "$args{generator}->[0]" $deps + set ASM=\$(AS) + $generator \$@ +EOF + } + return <<"EOF"; +$target: "$args{generator}->[0]" $deps + \$(CC) $incs \$(CFLAGS) /EP /C "$args{generator}->[0]" > \$@.i && move /Y \$@.i \$@ +EOF + } + } + + sub src2obj { + my %args = @_; + my $obj = $args{obj}; + my @srcs = map { (my $x = $_) =~ s/\.s$/.asm/; $x + } ( @{$args{srcs}} ); + my $srcs = '"'.join('" "', @srcs).'"'; + my $deps = '"'.join('" "', @srcs, @{$args{deps}}).'"'; + my $incs = join("", map { ' /I "'.$_.'"' } @{$args{incs}}); + unless ($disabled{zlib}) { + if ($withargs{zlib_include}) { + $incs .= ' /I "'.$withargs{zlib_include}.'"'; + } + } + my $ecflags = { lib => '$(LIB_CFLAGS)', + dso => '$(DSO_CFLAGS)', + bin => '$(BIN_CFLAGS)' } -> {$args{intent}}; + my $makedepprog = $config{makedepprog}; + if ($srcs[0] =~ /\.asm$/) { + return <<"EOF"; +$obj$objext: $deps + \$(AS) \$(ASFLAGS) \$(ASOUTFLAG)\$\@ $srcs +EOF + } + return <<"EOF" if (!$disabled{makedepend}); +$obj$depext: $deps + \$(CC) \$(CFLAGS) $ecflags$inc /Zs /showIncludes $srcs 2>&1 | \\ + "\$(PERL)" -n << > $obj$depext +chomp; +s/^Note: including file: *//; +\$\$collect{\$\$_} = 1; +END { print '$obj$objext: ',join(" ", sort keys \%collect),"\\n" } +<< +$obj$objext: $obj$depext + \$(CC) $incs \$(CFLAGS) $ecflags -c \$(COUTFLAG)\$\@ @<< +$srcs +<< +EOF + return <<"EOF" if ($disabled{makedepend}); +$obj$objext: $deps + \$(CC) $incs \$(CFLAGS) $ecflags -c \$(COUTFLAG)\$\@ $srcs +EOF + } + + # On Unix, we build shlibs from static libs, so we're ignoring the + # object file array. We *know* this routine is only called when we've + # configure 'shared'. + sub libobj2shlib { + my %args = @_; + my $lib = $args{lib}; + my $shlib = $args{shlib}; + (my $mkdef_key = $lib) =~ s/^lib//i; + my $objs = join("\n", map { $_.$objext } @{$args{objs}}); + my $linklibs = join("", + map { "\n$_" } compute_lib_depends(@{$args{deps}})); + my $deps = join(" ", + (map { $_.$objext } @{$args{objs}}), + compute_lib_depends(@{$args{deps}})); + my $ordinalsfile = defined($args{ordinals}) ? $args{ordinals}->[1] : ""; + my $mkdef_pl = abs2rel(rel2abs(catfile($config{sourcedir}, + "util", "mkdef.pl")), + rel2abs($config{builddir})); + my $mkrc_pl = abs2rel(rel2abs(catfile($config{sourcedir}, + "util", "mkrc.pl")), + rel2abs($config{builddir})); + my $target = shlib_import($lib); + return <<"EOF" +$target: $deps "$ordinalsfile" "$mkdef_pl" + "\$(PERL)" "$mkdef_pl" "$mkdef_key" 32 > $shlib.def + "\$(PERL)" -i.tmp -pe "s|^LIBRARY\\s+${mkdef_key}32|LIBRARY $shlib|;" $shlib.def + DEL $shlib.def.tmp + "\$(PERL)" "$mkrc_pl" $shlib$shlibext > $shlib.rc + \$(RC) \$(RCOUTFLAG)$shlib.res $shlib.rc + IF EXIST $shlib$shlibext.manifest DEL /F /Q $shlib$shlibext.manifest + \$(LD) \$(LDFLAGS) \$(LIB_LDFLAGS) \\ + /implib:\$@ \$(LDOUTFLAG)$shlib$shlibext /def:$shlib.def @<< || (DEL /Q \$(\@B).* $shlib.* && EXIT 1) +$objs $shlib.res$linklibs \$(EX_LIBS) +<< + IF EXIST $shlib$shlibext.manifest \\ + \$(MT) \$(MTFLAGS) \$(MTINFLAG)$shlib$shlibext.manifest \$(MTOUTFLAG)$shlib$shlibext + IF EXIST apps\\$shlib$shlibext DEL /Q /F apps\\$shlib$shlibext + IF EXIST test\\$shlib$shlibext DEL /Q /F test\\$shlib$shlibext + COPY $shlib$shlibext apps + COPY $shlib$shlibext test +EOF + } + sub obj2dso { + my %args = @_; + my $dso = $args{lib}; + my $dso_n = basename($dso); + my $objs = join("\n", map { $_.$objext } @{$args{objs}}); + my $linklibs = join("", + map { "\n$_" } compute_lib_depends(@{$args{deps}})); + my $deps = join(" ", + (map { $_.$objext } @{$args{objs}}), + compute_lib_depends(@{$args{deps}})); + return <<"EOF"; +$dso$dsoext: $deps + IF EXIST $dso$dsoext.manifest DEL /F /Q $dso$dsoext.manifest + \$(LD) \$(LDFLAGS) \$(DSO_LDFLAGS) \$(LDOUTFLAG)$dso$dsoext /def:<< @<< +LIBRARY $dso_n +EXPORTS + bind_engine @1 + v_check @2 +<< +$objs$linklibs \$(EX_LIBS) +<< + IF EXIST $dso$dsoext.manifest \\ + \$(MT) \$(MTFLAGS) \$(MTINFLAG)$dso$dsoext.manifest \$(MTOUTFLAG)$dso$dsoext +EOF + } + sub obj2lib { + # Because static libs and import libs are both named the same in native + # Windows, we can't have both. We skip the static lib in that case, + # as the shared libs are what we use anyway. + return "" unless $disabled{"shared"}; + + my %args = @_; + my $lib = $args{lib}; + my $objs = join("\n", map { $_.$objext } @{$args{objs}}); + my $deps = join(" ", map { $_.$objext } @{$args{objs}}); + return <<"EOF"; +$lib$libext: $deps + \$(AR) \$(ARFLAGS) \$(AROUTFLAG)$lib$libext @<< +\$** +<< +EOF + } + sub obj2bin { + my %args = @_; + my $bin = $args{bin}; + my $objs = join("\n", map { $_.$objext } @{$args{objs}}); + my $linklibs = join("", + map { "\n$_" } compute_lib_depends(@{$args{deps}})); + my $deps = join(" ", + (map { $_.$objext } @{$args{objs}}), + compute_lib_depends(@{$args{deps}})); + return <<"EOF"; +$bin$exeext: $deps + IF EXIST $bin$exeext.manifest DEL /F /Q $bin$exeext.manifest + \$(LD) \$(LDFLAGS) \$(BIN_LDFLAGS) \$(LDOUTFLAG)$bin$exeext @<< +$objs setargv.obj$linklibs \$(EX_LIBS) +<< + IF EXIST $bin$exeext.manifest \\ + \$(MT) \$(MTFLAGS) \$(MTINFLAG)$bin$exeext.manifest \$(MTOUTFLAG)$bin$exeext +EOF + } + sub in2script { + my %args = @_; + my $script = $args{script}; + my $sources = '"'.join('" "', @{$args{sources}}).'"'; + my $dofile = abs2rel(rel2abs(catfile($config{sourcedir}, + "util", "dofile.pl")), + rel2abs($config{builddir})); + return <<"EOF"; +$script: $sources + "\$(PERL)" "-I\$(BLDDIR)" -Mconfigdata "$dofile" \\ + "-o$target{build_file}" $sources > "$script" +EOF + } + sub generatedir { + my %args = @_; + my $dir = $args{dir}; + my @deps = map { s|\.o$|$objext|; $_ } @{$args{deps}}; + my @actions = (); + my %extinfo = ( dso => $dsoext, + lib => $libext, + bin => $exeext ); + + foreach my $type (("dso", "lib", "bin", "script")) { + next unless defined($unified_info{dirinfo}->{$dir}->{products}->{$type}); + # For lib object files, we could update the library. However, + # LIB on Windows doesn't work that way, so we won't create any + # actions for it, and the dependencies are already taken care of. + if ($type ne "lib") { + foreach my $prod (@{$unified_info{dirinfo}->{$dir}->{products}->{$type}}) { + if (dirname($prod) eq $dir) { + push @deps, $prod.$extinfo{$type}; + } else { + push @actions, "\t@rem No support to produce $type ".join(", ", @{$unified_info{dirinfo}->{$dir}->{products}->{$type}}); + } + } + } + } + + my $deps = join(" ", @deps); + my $actions = join("\n", "", @actions); + return <<"EOF"; +$args{dir} $args{dir}\\ : $deps$actions +EOF + } + "" # Important! This becomes part of the template result. +-} diff --git a/Configure b/Configure new file mode 100755 index 000000000..813cb77ab --- /dev/null +++ b/Configure @@ -0,0 +1,2822 @@ +#! /usr/bin/env perl +# -*- mode: perl; -*- +# Copyright 2016 - 2017 The GmSSL Project. All Rights Reserved. +# Copyright 2016 The OpenSSL Project Authors. All Rights Reserved. +# +# Licensed under the OpenSSL license (the "License"). You may not use +# this file except in compliance with the License. You can obtain a copy +# in the file LICENSE in the source distribution or at +# https://www.openssl.org/source/license.html + +## Configure -- OpenSSL source tree configuration script + +require 5.10.0; +use strict; +use File::Basename; +use File::Spec::Functions qw/:DEFAULT abs2rel rel2abs/; +use File::Path qw/mkpath/; +use if $^O ne "VMS", 'File::Glob' => qw/glob/; + +# see INSTALL for instructions. + +my $usage="Usage: Configure [no- ...] [enable- ...] [-Dxxx] [-lxxx] [-Lxxx] [-fxxx] [-Kxxx] [no-hw-xxx|no-hw] [[no-]threads] [[no-]shared] [[no-]zlib|zlib-dynamic] [no-asm] [no-dso] [no-egd] [sctp] [386] [--prefix=DIR] [--openssldir=OPENSSLDIR] [--with-xxx[=vvv]] [--config=FILE] os/compiler[:flags]\n"; + +# Options: +# +# --config add the given configuration file, which will be read after +# any "Configurations*" files that are found in the same +# directory as this script. +# --prefix prefix for the OpenSSL installation, which includes the +# directories bin, lib, include, share/man, share/doc/openssl +# This becomes the value of INSTALLTOP in Makefile +# (Default: /usr/local) +# --openssldir OpenSSL data area, such as openssl.cnf, certificates and keys. +# If it's a relative directory, it will be added on the directory +# given with --prefix. +# This becomes the value of OPENSSLDIR in Makefile and in C. +# (Default: PREFIX/ssl) +# +# --cross-compile-prefix Add specified prefix to binutils components. +# +# --api One of 0.9.8, 1.0.0 or 1.1.0. Do not compile support for +# interfaces deprecated as of the specified OpenSSL version. +# +# no-hw-xxx do not compile support for specific crypto hardware. +# Generic OpenSSL-style methods relating to this support +# are always compiled but return NULL if the hardware +# support isn't compiled. +# no-hw do not compile support for any crypto hardware. +# [no-]threads [don't] try to create a library that is suitable for +# multithreaded applications (default is "threads" if we +# know how to do it) +# [no-]shared [don't] try to create shared libraries when supported. +# [no-]pic [don't] try to build position independent code when supported. +# If disabled, it also disables shared and dynamic-engine. +# no-asm do not use assembler +# no-dso do not compile in any native shared-library methods. This +# will ensure that all methods just return NULL. +# no-egd do not compile support for the entropy-gathering daemon APIs +# [no-]zlib [don't] compile support for zlib compression. +# zlib-dynamic Like "zlib", but the zlib library is expected to be a shared +# library and will be loaded in run-time by the OpenSSL library. +# sctp include SCTP support +# enable-weak-ssl-ciphers +# Enable weak ciphers that are disabled by default. This currently +# only includes RC4 based ciphers. +# 386 generate 80386 code in assembly modules +# no-sse2 disables IA-32 SSE2 code in assembly modules, the above +# mentioned '386' option implies this one +# no- build without specified algorithm (rsa, idea, rc5, ...) +# - + compiler options are passed through +# -static while -static is also a pass-through compiler option (and +# as such is limited to environments where it's actually +# meaningful), it triggers a number configuration options, +# namely no-dso, no-pic, no-shared and no-threads. It is +# argued that the only reason to produce statically linked +# binaries (and in context it means executables linked with +# -static flag, and not just executables linked with static +# libcrypto.a) is to eliminate dependency on specific run-time, +# a.k.a. libc version. The mentioned config options are meant +# to achieve just that. Unfortunately on Linux it's impossible +# to eliminate the dependency completely for openssl executable +# because of getaddrinfo and gethostbyname calls, which can +# invoke dynamically loadable library facility anyway to meet +# the lookup requests. For this reason on Linux statically +# linked openssl executable has rather debugging value than +# production quality. +# +# DEBUG_SAFESTACK use type-safe stacks to enforce type-safety on stack items +# provided to stack calls. Generates unique stack functions for +# each possible stack type. +# BN_LLONG use the type 'long long' in crypto/bn/bn.h +# RC4_CHAR use 'char' instead of 'int' for RC4_INT in crypto/rc4/rc4.h +# Following are set automatically by this script +# +# MD5_ASM use some extra md5 assembler, +# SHA1_ASM use some extra sha1 assembler, must define L_ENDIAN for x86 +# RMD160_ASM use some extra ripemd160 assembler, +# SHA256_ASM sha256_block is implemented in assembler +# SHA512_ASM sha512_block is implemented in assembler +# AES_ASM AES_[en|de]crypt is implemented in assembler + +# Minimum warning options... any contributions to OpenSSL should at least get +# past these. + +# DEBUG_UNUSED enables __owur (warn unused result) checks. +my $gcc_devteam_warn = "-DDEBUG_UNUSED" + # -DPEDANTIC complements -pedantic and is meant to mask code that + # is not strictly standard-compliant and/or implementation-specific, + # e.g. inline assembly, disregards to alignment requirements, such + # that -pedantic would complain about. Incidentally -DPEDANTIC has + # to be used even in sanitized builds, because sanitizer too is + # supposed to and does take notice of non-standard behaviour. Then + # -pedantic with pre-C9x compiler would also complain about 'long + # long' not being supported. As 64-bit algorithms are common now, + # it grew impossible to resolve this without sizeable additional + # code, so we just tell compiler to be pedantic about everything + # but 'long long' type. + . " -DPEDANTIC -pedantic -Wno-long-long" + . " -Wall" + . " -Wsign-compare" + . " -Wmissing-prototypes" + . " -Wshadow" + . " -Wformat" + . " -Wtype-limits" + . " -Werror" + ; + +# These are used in addition to $gcc_devteam_warn when the compiler is clang. +# TODO(openssl-team): fix problems and investigate if (at least) the +# following warnings can also be enabled: +# -Wswitch-enum +# -Wcast-align +# -Wunreachable-code +# -Wlanguage-extension-token -- no, we use asm() +# -Wunused-macros -- no, too tricky for BN and _XOPEN_SOURCE etc +# -Wextended-offsetof -- no, needed in CMS ASN1 code +my $clang_devteam_warn = "" + . " -Qunused-arguments" + . " -Wextra" + . " -Wno-unused-parameter" + . " -Wno-missing-field-initializers" + . " -Wno-language-extension-token" + . " -Wno-extended-offsetof" + . " -Wconditional-uninitialized" + . " -Wincompatible-pointer-types-discards-qualifiers" + . " -Wmissing-variable-declarations" + ; + +# This adds backtrace information to the memory leak info. Is only used +# when crypto-mdebug-backtrace is enabled. +my $memleak_devteam_backtrace = "-rdynamic"; + +my $strict_warnings = 0; + +# As for $BSDthreads. Idea is to maintain "collective" set of flags, +# which would cover all BSD flavors. -pthread applies to them all, +# but is treated differently. OpenBSD expands is as -D_POSIX_THREAD +# -lc_r, which is sufficient. FreeBSD 4.x expands it as -lc_r, +# which has to be accompanied by explicit -D_THREAD_SAFE and +# sometimes -D_REENTRANT. FreeBSD 5.x expands it as -lc_r, which +# seems to be sufficient? +our $BSDthreads="-pthread -D_THREAD_SAFE -D_REENTRANT"; + +# +# API compatibility name to version number mapping. +# +my $maxapi = "1.1.0"; # API for "no-deprecated" builds +my $apitable = { + "1.1.0" => "0x10100000L", + "1.0.0" => "0x10000000L", + "0.9.8" => "0x00908000L", +}; + +our %table = (); +our %config = (); +our %withargs = (); + +# Forward declarations ############################################### + +# read_config(filename) +# +# Reads a configuration file and populates %table with the contents +# (which the configuration file places in %targets). +sub read_config; + +# resolve_config(target) +# +# Resolves all the late evaluations, inheritances and so on for the +# chosen target and any target it inherits from. +sub resolve_config; + + +# Information collection ############################################# + +# Unified build supports separate build dir +my $srcdir = catdir(absolutedir(dirname($0))); # catdir ensures local syntax +my $blddir = catdir(absolutedir(".")); # catdir ensures local syntax +my $dofile = abs2rel(catfile($srcdir, "util/dofile.pl")); + +my $local_config_envname = 'OPENSSL_LOCAL_CONFIG_DIR'; + +$config{sourcedir} = abs2rel($srcdir); +$config{builddir} = abs2rel($blddir); + +# Collect reconfiguration information if needed +my @argvcopy=@ARGV; + +if (grep /^reconf(igure)?$/, @argvcopy) { + if (-f "./configdata.pm") { + my $file = "./configdata.pm"; + unless (my $return = do $file) { + die "couldn't parse $file: $@" if $@; + die "couldn't do $file: $!" unless defined $return; + die "couldn't run $file" unless $return; + } + + @argvcopy = defined($configdata::config{perlargv}) ? + @{$configdata::config{perlargv}} : (); + die "Incorrect data to reconfigure, please do a normal configuration\n" + if (grep(/^reconf/,@argvcopy)); + $ENV{CROSS_COMPILE} = $configdata::config{cross_compile_prefix} + if defined($configdata::config{cross_compile_prefix}); + $ENV{CC} = $configdata::config{cc} + if defined($configdata::config{cc}); + $ENV{BUILDFILE} = $configdata::config{build_file} + if defined($configdata::config{build_file}); + $ENV{$local_config_envname} = $configdata::config{local_config_dir} + if defined($configdata::config{local_config_dir}); + + print "Reconfiguring with: ", join(" ",@argvcopy), "\n"; + print " CROSS_COMPILE = ",$ENV{CROSS_COMPILE},"\n" + if $ENV{CROSS_COMPILE}; + print " CC = ",$ENV{CC},"\n" if $ENV{CC}; + print " BUILDFILE = ",$ENV{BUILDFILE},"\n" if $ENV{BUILDFILE}; + print " $local_config_envname = ",$ENV{$local_config_envname},"\n" + if $ENV{$local_config_envname}; + } else { + die "Insufficient data to reconfigure, please do a normal configuration\n"; + } +} + +$config{perlargv} = [ @argvcopy ]; + +# Collect version numbers +$config{version} = "unknown"; +$config{version_num} = "unknown"; +$config{shlib_version_number} = "unknown"; +$config{shlib_version_history} = "unknown"; + +collect_information( + collect_from_file(catfile($srcdir,'include/openssl/opensslv.h')), + qr/OPENSSL.VERSION.TEXT.*GmSSL (\S+) / => sub { $config{version} = $1; }, + qr/OPENSSL.VERSION.NUMBER.*(0x\S+)/ => sub { $config{version_num}=$1 }, + qr/SHLIB_VERSION_NUMBER *"([^"]+)"/ => sub { $config{shlib_version_number}=$1 }, + qr/SHLIB_VERSION_HISTORY *"([^"]*)"/ => sub { $config{shlib_version_history}=$1 } + ); +if ($config{shlib_version_history} ne "") { $config{shlib_version_history} .= ":"; } + +($config{major}, $config{minor}) + = ($config{version} =~ /^([0-9]+)\.([0-9\.]+)/); +($config{shlib_major}, $config{shlib_minor}) + = ($config{shlib_version_number} =~ /^([0-9]+)\.([0-9\.]+)/); +die "erroneous version information in opensslv.h: ", + "$config{major}, $config{minor}, $config{shlib_major}, $config{shlib_minor}\n" + if ($config{major} eq "" || $config{minor} eq "" + || $config{shlib_major} eq "" || $config{shlib_minor} eq ""); + +# Collect target configurations + +my $pattern = catfile(dirname($0), "Configurations", "*.conf"); +foreach (sort glob($pattern)) { + &read_config($_); +} + +if (defined $ENV{$local_config_envname}) { + if ($^O eq 'VMS') { + # VMS environment variables are logical names, + # which can be used as is + $pattern = $local_config_envname . ':' . '*.conf'; + } else { + $pattern = catfile($ENV{$local_config_envname}, '*.conf'); + } + + foreach (sort glob($pattern)) { + &read_config($_); + } +} + + +print "Configuring GmSSL version $config{version} ($config{version_num})\n"; + +$config{prefix}=""; +$config{openssldir}=""; +$config{processor}=""; +$config{libdir}=""; +$config{cross_compile_prefix}=""; +$config{fipslibdir}="/usr/local/ssl/fips-2.0/lib/"; +my $nofipscanistercheck=0; +$config{baseaddr}="0xFB00000"; +my $auto_threads=1; # enable threads automatically? true by default +my $default_ranlib; +$config{fips}=0; + +# Top level directories to build +$config{dirs} = [ "crypto", "ssl", "engines", "apps", "util", "tools", "fuzz", "test" ]; +# crypto/ subdirectories to build +$config{sdirs} = [ + "objects", + "md2", "md4", "md5", "sha", "mdc2", "hmac", "ripemd", "whrlpool", "poly1305", "blake2", + "des", "aes", "rc2", "rc4", "rc5", "idea", "bf", "cast", "camellia", "seed", "chacha", "modes", + "bn", "ec", "rsa", "dsa", "dh", "dso", "engine", + "buffer", "bio", "stack", "lhash", "rand", "err", + "evp", "asn1", "pem", "x509", "x509v3", "conf", "txt_db", "pkcs7", "pkcs12", "comp", "ocsp", "ui", + "cms", "ts", "srp", "cmac", "ct", "async", "kdf", + "sm3", "sms4", "zuc", "kdf2", "otp", + "ecies", "sm2", "paillier", "sm9", + "gmapi", "skf", "sdf" + ]; + +# Known TLS and DTLS protocols +my @tls = qw(ssl3 tls1 tls1_1 tls1_2); +my @dtls = qw(dtls1 dtls1_2); + +# Explicitly known options that are possible to disable. They can +# be regexps, and will be used like this: /^no-${option}$/ +# For developers: keep it sorted alphabetically + +my @disablables = ( + "afalgeng", + "asan", + "asm", + "async", + "autoalginit", + "autoerrinit", + "bf", + "blake2", + "camellia", + "capieng", + "cast", + "chacha", + "cmac", + "cms", + "comp", + "crypto-mdebug", + "crypto-mdebug-backtrace", + "ct", + "deprecated", + "des", + "dgram", + "dh", + "dsa", + "dso", + "dtls", + "dynamic-engine", + "ec", + "ec2m", + "ecdh", + "ecdsa", + "ec_nistp_64_gcc_128", + "egd", + "engine", + "err", + "filenames", + "fuzz-libfuzzer", + "fuzz-afl", + "gost", + "heartbeats", + "hw(-.+)?", + "idea", + "makedepend", + "md2", + "md4", + "mdc2", + "msan", + "multiblock", + "nextprotoneg", + "ocb", + "ocsp", + "pic", + "poly1305", + "posix-io", + "psk", + "rc2", + "rc4", + "rc5", + "rdrand", + "rfc3779", + "rmd160", + "scrypt", + "sctp", + "seed", + "shared", + "sock", + "srp", + "srtp", + "sse2", + "ssl", + "ssl-trace", + "static-engine", + "stdio", + "threads", + "tls", + "ts", + "ubsan", + "ui", + "unit-test", + "whirlpool", + "weak-ssl-ciphers", + "zlib", + "zlib-dynamic", + "skfeng", + "sdfeng", + "gmieng", + "sm3", + "sms4", + "kdf2", + "ecies", + "sm2", + "paillier", + "otp", + "gmapi", + "sm9", + "sdf", + "skf", + "zuc", + "aes", + "sha", + "md5", + "rsa", + "pem", + "pkcs7", + "java", + "ca", + "gmtls", + "fuzz", + "test", + ); +foreach my $proto ((@tls, @dtls)) + { + push(@disablables, $proto); + push(@disablables, "$proto-method"); + } + +my %deprecated_disablables = ( + "ssl2" => undef, + "buf-freelists" => undef, + "ripemd" => "rmd160" + ); + +# All of the following is disabled by default (RC5 was enabled before 0.9.8): + +our %disabled = ( # "what" => "comment" + "asan" => "default", + "crypto-mdebug" => "default", + "crypto-mdebug-backtrace" => "default", + "ec_nistp_64_gcc_128" => "default", + "egd" => "default", + "fuzz-libfuzzer" => "default", + "fuzz-afl" => "default", + "heartbeats" => "default", + "md2" => "default", + "msan" => "default", + "rc5" => "default", + "sctp" => "default", + "ssl-trace" => "default", + "ssl3" => "default", + "ssl3-method" => "default", + "ubsan" => "default", + "unit-test" => "default", + "weak-ssl-ciphers" => "default", + "zlib" => "default", + "zlib-dynamic" => "default", + "skfeng" => "default", + "sdfeng" => "default", + "gmieng" => "default", + ); + +# Note: => pair form used for aesthetics, not to truly make a hash table +my @disable_cascades = ( + # "what" => [ "cascade", ... ] + sub { $config{processor} eq "386" } + => [ "sse2" ], + "ssl" => [ "ssl3" ], + "ssl3-method" => [ "ssl3" ], + "zlib" => [ "zlib-dynamic" ], + "des" => [ "mdc2" ], + "ec" => [ "ecdsa", "ecdh" ], + + "dgram" => [ "dtls", "sctp" ], + "sock" => [ "dgram" ], + "dtls" => [ @dtls ], + + "sm3" => [ "sm2" ], + + # SSL 3.0, (D)TLS 1.0 and TLS 1.1 require MD5 and SHA + "md5" => [ "ssl", "tls1", "tls1_1", "dtls1", "engine" ], + # current pkcs12 rfc only define sha1/des bag + "sha" => [ "ssl", "tls1", "tls1_1", "dtls1", "engine", "rsa", "dh", "dsa", "ocsp", "ct", "srp", "ts"], + "aes" => [ "engine" ], + + # Additionally, SSL 3.0 requires either RSA or DSA+DH + sub { $disabled{rsa} + && ($disabled{dsa} || $disabled{dh}); } + => [ "ssl" ], + + # (D)TLS 1.0 and TLS 1.1 also require either RSA or DSA+DH + # or ECDSA + ECDH. (D)TLS 1.2 has this requirement as well. + # (XXX: We don't support PSK-only builds). + sub { $disabled{rsa} + && ($disabled{dsa} || $disabled{dh}) + && ($disabled{ecdsa} || $disabled{ecdh}); } + => [ "tls1", "tls1_1", "tls1_2", + "dtls1", "dtls1_2" ], + + "tls" => [ @tls ], + + # SRP and HEARTBEATS require TLSEXT + "tlsext" => [ "srp", "heartbeats" ], + + "crypto-mdebug" => [ "crypto-mdebug-backtrace" ], + + # Without DSO, we can't load dynamic engines, so don't build them dynamic + "dso" => [ "dynamic-engine" ], + + # Without position independent code, there can be no shared libraries or DSOs + "pic" => [ "shared" ], + "shared" => [ "dynamic-engine" ], + "engine" => [ "afalgeng", "saf" ], + + # no-autoalginit is only useful when building non-shared + "autoalginit" => [ "shared", "apps" ], + + "stdio" => [ "apps", "capieng" ], + "apps" => [ "tests" ], + "comp" => [ "zlib" ], + sub { !$disabled{"unit-test"} } => [ "heartbeats" ], + + sub { !$disabled{"msan"} } => [ "asm" ], + ); + +# Avoid protocol support holes. Also disable all versions below N, if version +# N is disabled while N+1 is enabled. +# +my @list = (reverse @tls); +while ((my $first, my $second) = (shift @list, shift @list)) { + last unless @list; + push @disable_cascades, ( sub { !$disabled{$first} && $disabled{$second} } + => [ @list ] ); + unshift @list, $second; +} +my @list = (reverse @dtls); +while ((my $first, my $second) = (shift @list, shift @list)) { + last unless @list; + push @disable_cascades, ( sub { !$disabled{$first} && $disabled{$second} } + => [ @list ] ); + unshift @list, $second; +} + +# Explicit "no-..." options will be collected in %disabled along with the defaults. +# To remove something from %disabled, use "enable-foo". +# For symmetry, "disable-foo" is a synonym for "no-foo". + +my $no_sse2=0; + +&usage if ($#ARGV < 0); + +my $user_cflags=""; +my @user_defines=(); +$config{openssl_api_defines}=[]; +$config{openssl_algorithm_defines}=[]; +$config{openssl_thread_defines}=[]; +$config{openssl_sys_defines}=[]; +$config{openssl_other_defines}=[]; +my $libs=""; +my $target=""; +$config{options}=""; +$config{build_type} = "release"; + +my %unsupported_options = (); +my %deprecated_options = (); +while (@argvcopy) + { + $_ = shift @argvcopy; + # VMS is a case insensitive environment, and depending on settings + # out of our control, we may receive options uppercased. Let's + # downcase at least the part before any equal sign. + if ($^O eq "VMS") + { + s/^([^=]*)/lc($1)/e; + } + s /^-no-/no-/; # some people just can't read the instructions + + # rewrite some options in "enable-..." form + s /^-?-?shared$/enable-shared/; + s /^sctp$/enable-sctp/; + s /^threads$/enable-threads/; + s /^zlib$/enable-zlib/; + s /^zlib-dynamic$/enable-zlib-dynamic/; + + if (/^(no|disable|enable)-(.+)$/) + { + my $word = $2; + if (!exists $deprecated_disablables{$word} + && !grep { $word =~ /^${_}$/ } @disablables) + { + $unsupported_options{$_} = 1; + next; + } + } + if (/^no-(.+)$/ || /^disable-(.+)$/) + { + foreach my $proto ((@tls, @dtls)) + { + if ($1 eq "$proto-method") + { + $disabled{"$proto"} = "option($proto-method)"; + last; + } + } + if ($1 eq "dtls") + { + foreach my $proto (@dtls) + { + $disabled{$proto} = "option(dtls)"; + } + $disabled{"dtls"} = "option(dtls)"; + } + elsif ($1 eq "ssl") + { + # Last one of its kind + $disabled{"ssl3"} = "option(ssl)"; + } + elsif ($1 eq "tls") + { + # XXX: Tests will fail if all SSL/TLS + # protocols are disabled. + foreach my $proto (@tls) + { + $disabled{$proto} = "option(tls)"; + } + } + elsif ($1 eq "static-engine") + { + delete $disabled{"dynamic-engine"}; + } + elsif ($1 eq "dynamic-engine") + { + $disabled{"dynamic-engine"} = "option"; + } + elsif (exists $deprecated_disablables{$1}) + { + $deprecated_options{$_} = 1; + if (defined $deprecated_disablables{$1}) + { + $disabled{$deprecated_disablables{$1}} = "option"; + } + } + else + { + $disabled{$1} = "option"; + } + # No longer an automatic choice + $auto_threads = 0 if ($1 eq "threads"); + } + elsif (/^enable-(.+)$/) + { + if ($1 eq "static-engine") + { + $disabled{"dynamic-engine"} = "option"; + } + elsif ($1 eq "dynamic-engine") + { + delete $disabled{"dynamic-engine"}; + } + elsif ($1 eq "zlib-dynamic") + { + delete $disabled{"zlib"}; + } + my $algo = $1; + delete $disabled{$algo}; + + # No longer an automatic choice + $auto_threads = 0 if ($1 eq "threads"); + } + elsif (/^--strict-warnings$/) + { + $strict_warnings = 1; + } + elsif (/^--debug$/) + { + $config{build_type} = "debug"; + } + elsif (/^--release$/) + { + $config{build_type} = "release"; + } + elsif (/^386$/) + { $config{processor}=386; } + elsif (/^fips$/) + { + $config{fips}=1; + } + elsif (/^rsaref$/) + { + # No RSAref support any more since it's not needed. + # The check for the option is there so scripts aren't + # broken + } + elsif (/^nofipscanistercheck$/) + { + $config{fips} = 1; + $nofipscanistercheck = 1; + } + elsif (/^[-+]/) + { + if (/^--prefix=(.*)$/) + { + $config{prefix}=$1; + die "Directory given with --prefix MUST be absolute\n" + unless file_name_is_absolute($config{prefix}); + } + elsif (/^--api=(.*)$/) + { + $config{api}=$1; + } + elsif (/^--libdir=(.*)$/) + { + $config{libdir}=$1; + } + elsif (/^--openssldir=(.*)$/) + { + $config{openssldir}=$1; + } + elsif (/^--with-zlib-lib=(.*)$/) + { + $withargs{zlib_lib}=$1; + } + elsif (/^--with-zlib-include=(.*)$/) + { + $withargs{zlib_include}=$1; + } + elsif (/^--with-fuzzer-lib=(.*)$/) + { + $withargs{fuzzer_lib}=$1; + } + elsif (/^--with-fuzzer-include=(.*)$/) + { + $withargs{fuzzer_include}=$1; + } + elsif (/^--with-fipslibdir=(.*)$/) + { + $config{fipslibdir}="$1/"; + } + elsif (/^--with-baseaddr=(.*)$/) + { + $config{baseaddr}="$1"; + } + elsif (/^--cross-compile-prefix=(.*)$/) + { + $config{cross_compile_prefix}=$1; + } + elsif (/^--config=(.*)$/) + { + read_config $1; + } + elsif (/^-[lL](.*)$/ or /^-Wl,/) + { + $libs.=$_." "; + } + elsif (/^-rpath$/ or /^-R$/) + # -rpath is the OSF1 rpath flag + # -R is the old Solaris rpath flag + { + my $rpath = shift(@argvcopy) || ""; + $rpath .= " " if $rpath ne ""; + $libs.=$_." ".$rpath; + } + elsif (/^-static$/) + { + $libs.=$_." "; + $disabled{"dso"} = "forced"; + $disabled{"pic"} = "forced"; + $disabled{"shared"} = "forced"; + $disabled{"threads"} = "forced"; + } + elsif (/^-D(.*)$/) + { + push @user_defines, $1; + } + else # common if (/^[-+]/), just pass down... + { + $_ =~ s/%([0-9a-f]{1,2})/chr(hex($1))/gei; + $user_cflags.=" ".$_; + } + } + else + { + die "target already defined - $target (offending arg: $_)\n" if ($target ne ""); + $target=$_; + } + unless ($_ eq $target || /^no-/ || /^disable-/) + { + # "no-..." follows later after implied disactivations + # have been derived. (Don't take this too seriously, + # we really only write OPTIONS to the Makefile out of + # nostalgia.) + + if ($config{options} eq "") + { $config{options} = $_; } + else + { $config{options} .= " ".$_; } + } + + if (defined($config{api}) && !exists $apitable->{$config{api}}) { + die "***** Unsupported api compatibility level: $config{api}\n", + } + + if (keys %deprecated_options) + { + warn "***** Deprecated options: ", + join(", ", keys %deprecated_options), "\n"; + } + if (keys %unsupported_options) + { + die "***** Unsupported options: ", + join(", ", keys %unsupported_options), "\n"; + } + } + +if ($libs =~ /(^|\s)-Wl,-rpath,/ + && !$disabled{shared} + && !($disabled{asan} && $disabled{msan} && $disabled{ubsan})) { + die "***** Cannot simultaneously use -rpath, shared libraries, and\n", + "***** any of asan, msan or ubsan\n"; +} + +if ($config{fips}) + { + delete $disabled{"shared"} if ($disabled{"shared"} =~ /^default/); + } +else + { + @{$config{dirs}} = grep !/^fips$/, @{$config{dirs}}; + } + +my @tocheckfor = (keys %disabled); +while (@tocheckfor) { + my %new_tocheckfor = (); + my @cascade_copy = (@disable_cascades); + while (@cascade_copy) { + my ($test, $descendents) = (shift @cascade_copy, shift @cascade_copy); + if (ref($test) eq "CODE" ? $test->() : defined($disabled{$test})) { + foreach(grep { !defined($disabled{$_}) } @$descendents) { + $new_tocheckfor{$_} = 1; $disabled{$_} = "forced"; + } + } + } + @tocheckfor = (keys %new_tocheckfor); +} + +our $die = sub { die @_; }; +if ($target eq "TABLE") { + local $die = sub { warn @_; }; + foreach (sort keys %table) { + print_table_entry($_, "TABLE"); + } + exit 0; +} + +if ($target eq "LIST") { + foreach (sort keys %table) { + print $_,"\n" unless $table{$_}->{template}; + } + exit 0; +} + +if ($target eq "HASH") { + local $die = sub { warn @_; }; + print "%table = (\n"; + foreach (sort keys %table) { + print_table_entry($_, "HASH"); + } + exit 0; +} + +# Backward compatibility? +if ($target =~ m/^CygWin32(-.*)$/) { + $target = "Cygwin".$1; +} + +foreach (sort (keys %disabled)) + { + $config{options} .= " no-$_"; + + printf " no-%-12s %-10s", $_, "[$disabled{$_}]"; + + if (/^dso$/) + { } + elsif (/^threads$/) + { } + elsif (/^shared$/) + { } + elsif (/^pic$/) + { } + elsif (/^zlib$/) + { } + elsif (/^dynamic-engine$/) + { } + elsif (/^makedepend$/) + { } + elsif (/^zlib-dynamic$/) + { } + elsif (/^sse2$/) + { $no_sse2 = 1; } + elsif (/^engine$/) + { + @{$config{dirs}} = grep !/^engines$/, @{$config{dirs}}; + @{$config{sdirs}} = grep !/^engine$/, @{$config{sdirs}}; + push @{$config{openssl_other_defines}}, "OPENSSL_NO_ENGINE"; + print " OPENSSL_NO_ENGINE (skip engines)"; + } + elsif (/^test$/) + { + @{$config{dirs}} = grep !/^test$/, @{$config{dirs}}; + #@{$config{sdirs}} = grep !/^engine$/, @{$config{sdirs}}; + #push @{$config{openssl_other_defines}}, "OPENSSL_NO_ENGINE"; + print " OPENSSL_NO_TEST (skip test)"; + } + elsif (/^fuzz$/) + { + @{$config{dirs}} = grep !/^fuzz$/, @{$config{dirs}}; + #@{$config{sdirs}} = grep !/^engine$/, @{$config{sdirs}}; + #push @{$config{openssl_other_defines}}, "OPENSSL_NO_ENGINE"; + print " OPENSSL_NO_FUZZ (skip fuzz)"; + } + else + { + my ($WHAT, $what); + + ($WHAT = $what = $_) =~ tr/[\-a-z]/[_A-Z]/; + + # Fix up C macro end names + $WHAT = "RMD160" if $what eq "ripemd"; + + # fix-up crypto/directory name(s) + $what = "ripemd" if $what eq "rmd160"; + $what = "whrlpool" if $what eq "whirlpool"; + + if ($what ne "async" && $what ne "err" + && grep { $_ eq $what } @{$config{sdirs}}) + { + push @{$config{openssl_algorithm_defines}}, "OPENSSL_NO_$WHAT"; + @{$config{sdirs}} = grep { $_ ne $what} @{$config{sdirs}}; + + print " OPENSSL_NO_$WHAT (skip dir)"; + } + else + { + push @{$config{openssl_other_defines}}, "OPENSSL_NO_$WHAT"; + print " OPENSSL_NO_$WHAT"; + + if (/^err$/) { push @user_defines, "OPENSSL_NO_ERR"; } + } + } + + print "\n"; + } + +print "Configuring for $target\n"; + +# Support for legacy targets having a name starting with 'debug-' +my ($d, $t) = $target =~ m/^(debug-)?(.*)$/; +if ($d) { + $config{build_type} = "debug"; + + # If we do not find debug-foo in the table, the target is set to foo. + if (!$table{$target}) { + $target = $t; + } +} +$config{target} = $target; +my %target = resolve_config($target); + +&usage if (!%target || $target{template}); + +my %conf_files = map { $_ => 1 } (@{$target{_conf_fname_int}}); +$config{conf_files} = [ sort keys %conf_files ]; +%target = ( %{$table{DEFAULTS}}, %target ); + +$target{exe_extension}=""; +$target{exe_extension}=".exe" if ($config{target} eq "DJGPP" + || $config{target} =~ /^(?:Cygwin|mingw)/); +$target{exe_extension}=".pm" if ($config{target} =~ /vos/); + +($target{shared_extension_simple}=$target{shared_extension}) + =~ s|\.\$\(SHLIB_MAJOR\)\.\$\(SHLIB_MINOR\)||; +$target{dso_extension}=$target{shared_extension_simple}; +($target{shared_import_extension}=$target{shared_extension_simple}.".a") + if ($config{target} =~ /^(?:Cygwin|mingw)/); + + +$config{cross_compile_prefix} = $ENV{'CROSS_COMPILE'} + if $config{cross_compile_prefix} eq ""; + +# Allow overriding the names of some tools. USE WITH CARE +# Note: only Unix cares about HASHBANGPERL... that explains +# the default string. +$config{perl} = $ENV{'PERL'} || ($^O ne "VMS" ? $^X : "perl"); +$config{hashbangperl} = + $ENV{'HASHBANGPERL'} || $ENV{'PERL'} || "/usr/bin/env perl"; +$target{cc} = $ENV{'CC'} || $target{cc} || "cc"; +$target{ranlib} = $ENV{'RANLIB'} || $target{ranlib} || + (which("$config{cross_compile_prefix}ranlib") ? + "\$(CROSS_COMPILE)ranlib" : "true"); +$target{ar} = $ENV{'AR'} || $target{ar} || "ar"; +$target{nm} = $ENV{'NM'} || $target{nm} || "nm"; +$target{rc} = + $ENV{'RC'} || $ENV{'WINDRES'} || $target{rc} || "windres"; + +# Allow overriding the build file name +$target{build_file} = $ENV{BUILDFILE} || $target{build_file} || "Makefile"; + +# Cache information necessary for reconfiguration +$config{cc} = $target{cc}; +$config{build_file} = $target{build_file}; + +# For cflags, lflags, plib_lflags, ex_libs and defines, add the debug_ +# or release_ attributes. +# Do it in such a way that no spurious space is appended (hence the grep). +$config{defines} = []; +$config{cflags} = ""; +$config{ex_libs} = ""; +$config{shared_ldflag} = ""; + +# Make sure build_scheme is consistent. +$target{build_scheme} = [ $target{build_scheme} ] + if ref($target{build_scheme}) ne "ARRAY"; + +my ($builder, $builder_platform, @builder_opts) = + @{$target{build_scheme}}; + +push @{$config{defines}}, "NDEBUG" if $config{build_type} eq "release"; + +if ($target =~ /^mingw/ && `$target{cc} --target-help 2>&1` =~ m/-mno-cygwin/m) + { + $config{cflags} .= " -mno-cygwin"; + $config{shared_ldflag} .= " -mno-cygwin"; + } + +if ($target =~ /linux.*-mips/ && !$disabled{asm} && $user_cflags !~ /-m(ips|arch=)/) { + # minimally required architecture flags for assembly modules + $config{cflags}="-mips2 $config{cflags}" if ($target =~ /mips32/); + $config{cflags}="-mips64r2 $config{cflags}" if ($target =~ /mips64/); +} + +my $no_shared_warn=0; +my $no_user_cflags=0; +my $no_user_defines=0; + +# The DSO code currently always implements all functions so that no +# applications will have to worry about that from a compilation point +# of view. However, the "method"s may return zero unless that platform +# has support compiled in for them. Currently each method is enabled +# by a define "DSO_" ... we translate the "dso_scheme" config +# string entry into using the following logic; +if (!$disabled{dso} && $target{dso_scheme} ne "") + { + $target{dso_scheme} =~ tr/[a-z]/[A-Z]/; + if ($target{dso_scheme} eq "DLFCN") + { + unshift @{$config{defines}}, "DSO_DLFCN", "HAVE_DLFCN_H"; + } + elsif ($target{dso_scheme} eq "DLFCN_NO_H") + { + unshift @{$config{defines}}, "DSO_DLFCN"; + } + else + { + unshift @{$config{defines}}, "DSO_$target{dso_scheme}"; + } + } + +$config{ex_libs}="$libs$config{ex_libs}" if ($libs ne ""); + +if ($disabled{asm}) + { + if ($config{fips}) + { + @{$config{defines}} = grep !/^[BL]_ENDIAN$/, @{$config{defines}}; + @{$target{defines}} = grep !/^[BL]_ENDIAN$/, @{$target{defines}}; + } + } + +# If threads aren't disabled, check how possible they are +unless ($disabled{threads}) { + if ($auto_threads) { + # Enabled by default, disable it forcibly if unavailable + if ($target{thread_scheme} eq "(unknown)") { + $disabled{threads} = "unavailable"; + } + } else { + # The user chose to enable threads explicitly, let's see + # if there's a chance that's possible + if ($target{thread_scheme} eq "(unknown)") { + # If the user asked for "threads" and we don't have internal + # knowledge how to do it, [s]he is expected to provide any + # system-dependent compiler options that are necessary. We + # can't truly check that the given options are correct, but + # we expect the user to know what [s]He is doing. + if ($no_user_cflags && $no_user_defines) { + die "You asked for multi-threading support, but didn't\n" + ,"provide any system-specific compiler options\n"; + } + } + } +} + +# If threads still aren't disabled, add a C macro to ensure the source +# code knows about it. Any other flag is taken care of by the configs. +unless($disabled{threads}) { + foreach (("defines", "openssl_thread_defines")) { + push @{$config{$_}}, "OPENSSL_THREADS"; + } +} + +# With "deprecated" disable all deprecated features. +if (defined($disabled{"deprecated"})) { + $config{api} = $maxapi; +} + +if ($target{shared_target} eq "") + { + $no_shared_warn = 1 + if ((!$disabled{shared} || !$disabled{"dynamic-engine"}) + && !$config{fips}); + $disabled{shared} = "no-shared-target"; + $disabled{pic} = $disabled{shared} = $disabled{"dynamic-engine"} = + "no-shared-target"; + } + +if ($disabled{"dynamic-engine"}) { + push @{$config{defines}}, "OPENSSL_NO_DYNAMIC_ENGINE"; + $config{dynamic_engines} = 0; +} else { + push @{$config{defines}}, "OPENSSL_NO_STATIC_ENGINE"; + $config{dynamic_engines} = 1; +} + +unless ($disabled{"fuzz-libfuzzer"}) { + $config{cflags} .= "-fsanitize-coverage=edge,indirect-calls "; +} + +unless ($disabled{asan}) { + $config{cflags} .= "-fsanitize=address "; +} + +unless ($disabled{ubsan}) { + # -DPEDANTIC or -fnosanitize=alignment may also be required on some + # platforms. + $config{cflags} .= "-fsanitize=undefined -fno-sanitize-recover=all "; +} + +unless ($disabled{msan}) { + $config{cflags} .= "-fsanitize=memory "; +} + +unless ($disabled{"fuzz-libfuzzer"} && $disabled{"fuzz-afl"} + && $disabled{asan} && $disabled{ubsan} && $disabled{msan}) { + $config{cflags} .= "-fno-omit-frame-pointer -g "; +} +# +# Platform fix-ups +# + +# This saves the build files from having to check +if ($disabled{pic}) + { + $target{shared_cflag} = $target{shared_ldflag} = + $target{shared_rcflag} = ""; + } +else + { + push @{$config{defines}}, "OPENSSL_PIC"; + } + +if ($target{sys_id} ne "") + { + push @{$config{openssl_sys_defines}}, "OPENSSL_SYS_$target{sys_id}"; + } + +unless ($disabled{asm}) { + $target{cpuid_asm_src}=$table{DEFAULTS}->{cpuid_asm_src} if ($config{processor} eq "386"); + $target{bn_asm_src} =~ s/\w+-gf2m.c// if (defined($disabled{ec2m})); + + # bn-586 is the only one implementing bn_*_part_words + push @{$config{defines}}, "OPENSSL_BN_ASM_PART_WORDS" if ($target{bn_asm_src} =~ /bn-586/); + push @{$config{defines}}, "OPENSSL_IA32_SSE2" if (!$no_sse2 && $target{bn_asm_src} =~ /86/); + + push @{$config{defines}}, "OPENSSL_BN_ASM_MONT" if ($target{bn_asm_src} =~ /-mont/); + push @{$config{defines}}, "OPENSSL_BN_ASM_MONT5" if ($target{bn_asm_src} =~ /-mont5/); + push @{$config{defines}}, "OPENSSL_BN_ASM_GF2m" if ($target{bn_asm_src} =~ /-gf2m/); + + if ($config{fips}) { + push @{$config{openssl_other_defines}}, "OPENSSL_FIPS"; + } + + if ($target{sha1_asm_src}) { + push @{$config{defines}}, "SHA1_ASM" if ($target{sha1_asm_src} =~ /sx86/ || $target{sha1_asm_src} =~ /sha1/); + push @{$config{defines}}, "SHA256_ASM" if ($target{sha1_asm_src} =~ /sha256/); + push @{$config{defines}}, "SHA512_ASM" if ($target{sha1_asm_src} =~ /sha512/); + } + if ($target{rc4_asm_src} ne $table{DEFAULTS}->{rc4_asm_src}) { + push @{$config{defines}}, "RC4_ASM"; + } + if ($target{md5_asm_src}) { + push @{$config{defines}}, "MD5_ASM"; + } + $target{cast_asm_src}=$table{DEFAULTS}->{cast_asm_src} unless $disabled{pic}; # CAST assembler is not PIC + if ($target{rmd160_asm_src}) { + push @{$config{defines}}, "RMD160_ASM"; + } + if ($target{aes_asm_src}) { + push @{$config{defines}}, "AES_ASM" if ($target{aes_asm_src} =~ m/\baes-/);; + # aes-ctr.fake is not a real file, only indication that assembler + # module implements AES_ctr32_encrypt... + push @{$config{defines}}, "AES_CTR_ASM" if ($target{aes_asm_src} =~ s/\s*aes-ctr\.fake//); + # aes-xts.fake indicates presence of AES_xts_[en|de]crypt... + push @{$config{defines}}, "AES_XTS_ASM" if ($target{aes_asm_src} =~ s/\s*aes-xts\.fake//); + $target{aes_asm_src} =~ s/\s*(vpaes|aesni)-x86\.s//g if ($no_sse2); + push @{$config{defines}}, "VPAES_ASM" if ($target{aes_asm_src} =~ m/vpaes/); + push @{$config{defines}}, "BSAES_ASM" if ($target{aes_asm_src} =~ m/bsaes/); + } + if ($target{wp_asm_src} =~ /mmx/) { + if ($config{processor} eq "386") { + $target{wp_asm_src}=$table{DEFAULTS}->{wp_asm_src}; + } elsif (!$disabled{"whirlpool"}) { + push @{$config{defines}}, "WHIRLPOOL_ASM"; + } + } + if ($target{modes_asm_src} =~ /ghash-/) { + push @{$config{defines}}, "GHASH_ASM"; + } + if ($target{ec_asm_src} =~ /ecp_nistz256/) { + push @{$config{defines}}, "ECP_NISTZ256_ASM"; + } + if ($target{padlock_asm_src} ne $table{DEFAULTS}->{padlock_asm_src}) { + push @{$config{defines}}, "PADLOCK_ASM"; + } + if ($target{gmi_asm_src} ne $table{DEFAULTS}->{gmi_asm_src}) { + push @{$config{defines}}, "GMI_ASM"; + } + if ($target{poly1305_asm_src} ne "") { + push @{$config{defines}}, "POLY1305_ASM"; + } +} + +my $ecc = $target{cc}; +if ($^O ne "VMS" && !$disabled{makedepend}) { + # Is the compiler gcc or clang? $ecc is used below to see if + # error-checking can be turned on. + my $ccpcc = "$config{cross_compile_prefix}$target{cc}"; + open(PIPE, "$ccpcc --version 2>&1 |"); + my $lines = 2; + while ( ) { + # Find the version number and save the major. + m|(?:.*)\b(\d+)\.\d+\.\d+\b(?:.*)|; + my $compiler_major = $1; + # We know that GNU C version 3 and up as well as all clang + # versions support dependency generation + $config{makedepprog} = $ccpcc + if (/clang/ || (/gcc/ && $compiler_major >= 3)); + $ecc = "clang" if /clang/; + $ecc = "gcc" if /gcc/; + last if ($config{makedepprog} || !$lines--); + } + close(PIPE); + + $config{makedepprog} = which('makedepend') unless $config{makedepprog}; + $disabled{makedepend} = "unavailable" unless $config{makedepprog}; +} + + + +# Deal with bn_ops ################################################### + +$config{bn_ll} =0; +$config{export_var_as_fn} =0; +my $def_int="unsigned int"; +$config{rc4_int} =$def_int; +($config{b64l},$config{b64},$config{b32})=(0,0,1); + +my $count = 0; +foreach (sort split(/\s+/,$target{bn_ops})) { + $count++ if /SIXTY_FOUR_BIT|SIXTY_FOUR_BIT_LONG|THIRTY_TWO_BIT/; + $config{export_var_as_fn}=1 if $_ eq 'EXPORT_VAR_AS_FN'; + $config{bn_ll}=1 if $_ eq 'BN_LLONG'; + $config{rc4_int}="unsigned char" if $_ eq 'RC4_CHAR'; + ($config{b64l},$config{b64},$config{b32}) + =(0,1,0) if $_ eq 'SIXTY_FOUR_BIT'; + ($config{b64l},$config{b64},$config{b32}) + =(1,0,0) if $_ eq 'SIXTY_FOUR_BIT_LONG'; + ($config{b64l},$config{b64},$config{b32}) + =(0,0,1) if $_ eq 'THIRTY_TWO_BIT'; +} +die "Exactly one of SIXTY_FOUR_BIT|SIXTY_FOUR_BIT_LONG|THIRTY_TWO_BIT can be set in bn_ops\n" + if $count > 1; + + +# Hack cflags for better warnings (dev option) ####################### + +# "Stringify" the C flags string. This permits it to be made part of a string +# and works as well on command lines. +$config{cflags} =~ s/([\\\"])/\\$1/g; + +if (defined($config{api})) { + $config{openssl_api_defines} = [ "OPENSSL_MIN_API=".$apitable->{$config{api}} ]; + my $apiflag = sprintf("OPENSSL_API_COMPAT=%s", $apitable->{$config{api}}); + push @{$config{defines}}, $apiflag; +} + +if ($strict_warnings) + { + my $wopt; + die "ERROR --strict-warnings requires gcc or clang" + unless $ecc eq 'gcc' || $ecc eq 'clang'; + foreach $wopt (split /\s+/, $gcc_devteam_warn) + { + $config{cflags} .= " $wopt" unless ($config{cflags} =~ /(?:^|\s)$wopt(?:\s|$)/) + } + if ($ecc eq "clang") + { + foreach $wopt (split /\s+/, $clang_devteam_warn) + { + $config{cflags} .= " $wopt" unless ($config{cflags} =~ /(?:^|\s)$wopt(?:\s|$)/) + } + } + } + +unless ($disabled{"crypto-mdebug-backtrace"}) + { + foreach my $wopt (split /\s+/, $memleak_devteam_backtrace) + { + $config{cflags} .= " $wopt" unless ($config{cflags} =~ /(?:^|\s)$wopt(?:\s|$)/) + } + if ($target =~ /^BSD-/) + { + $config{ex_libs} .= " -lexecinfo"; + } + } + +if ($user_cflags ne "") { $config{cflags}="$config{cflags}$user_cflags"; } +else { $no_user_cflags=1; } +if (@user_defines) { $config{defines}=[ @{$config{defines}}, @user_defines ]; } +else { $no_user_defines=1; } + +# ALL MODIFICATIONS TO %config and %target MUST BE DONE FROM HERE ON + +unless ($disabled{afalgeng}) { + $config{afalgeng}=""; + if ($target =~ m/^linux/) { + my $minver = 4*10000 + 1*100 + 0; + if ($config{cross_compile_prefix} eq "") { + my $verstr = `uname -r`; + my ($ma, $mi1, $mi2) = split("\\.", $verstr); + ($mi2) = $mi2 =~ /(\d+)/; + my $ver = $ma*10000 + $mi1*100 + $mi2; + if ($ver < $minver) { + $disabled{afalgeng} = "too-old-kernel"; + } else { + push @{$config{engdirs}}, "afalg"; + } + } else { + $disabled{afalgeng} = "cross-compiling"; + } + } else { + $disabled{afalgeng} = "not-linux"; + } +} + +push @{$config{openssl_other_defines}}, "OPENSSL_NO_AFALGENG" if ($disabled{afalgeng}); + +# If we use the unified build, collect information from build.info files +my %unified_info = (); + +my $buildinfo_debug = defined($ENV{CONFIGURE_DEBUG_BUILDINFO}); +if ($builder eq "unified") { + use lib catdir(dirname(__FILE__),"util"); + use with_fallback qw(Text::Template); + + sub cleandir { + my $base = shift; + my $dir = shift; + my $relativeto = shift || "."; + + $dir = catdir($base,$dir) unless isabsolute($dir); + + # Make sure the directories we're building in exists + mkpath($dir); + + my $res = abs2rel(absolutedir($dir), rel2abs($relativeto)); + #print STDERR "DEBUG[cleandir]: $dir , $base => $res\n"; + return $res; + } + + sub cleanfile { + my $base = shift; + my $file = shift; + my $relativeto = shift || "."; + + $file = catfile($base,$file) unless isabsolute($file); + + my $d = dirname($file); + my $f = basename($file); + + # Make sure the directories we're building in exists + mkpath($d); + + my $res = abs2rel(catfile(absolutedir($d), $f), rel2abs($relativeto)); + #print STDERR "DEBUG[cleanfile]: $d , $f => $res\n"; + return $res; + } + + # Store the name of the template file we will build the build file from + # in %config. This may be useful for the build file itself. + my @build_file_template_names = + ( $builder_platform."-".$target{build_file}.".tmpl", + $target{build_file}.".tmpl" ); + my @build_file_templates = (); + + # First, look in the user provided directory, if given + if (defined $ENV{$local_config_envname}) { + @build_file_templates = + map { + if ($^O eq 'VMS') { + # VMS environment variables are logical names, + # which can be used as is + $local_config_envname . ':' . $_; + } else { + catfile($ENV{$local_config_envname}, $_); + } + } + @build_file_template_names; + } + # Then, look in our standard directory + push @build_file_templates, + ( map { cleanfile($srcdir, catfile("Configurations", $_), $blddir) } + @build_file_template_names ); + + my $build_file_template; + for $_ (@build_file_templates) { + $build_file_template = $_; + last if -f $build_file_template; + + $build_file_template = undef; + } + if (!defined $build_file_template) { + die "*** Couldn't find any of:\n", join("\n", @build_file_templates), "\n"; + } + $config{build_file_templates} + = [ $build_file_template, + cleanfile($srcdir, catfile("Configurations", "common.tmpl"), + $blddir) ]; + + my @build_infos = ( [ ".", "build.info" ] ); + foreach (@{$config{dirs}}) { + push @build_infos, [ $_, "build.info" ] + if (-f catfile($srcdir, $_, "build.info")); + } + foreach (@{$config{sdirs}}) { + push @build_infos, [ catdir("crypto", $_), "build.info" ] + if (-f catfile($srcdir, "crypto", $_, "build.info")); + } + foreach (@{$config{engdirs}}) { + push @build_infos, [ catdir("engines", $_), "build.info" ] + if (-f catfile($srcdir, "engines", $_, "build.info")); + } + + $config{build_infos} = [ ]; + + foreach (@build_infos) { + my $sourced = catdir($srcdir, $_->[0]); + my $buildd = catdir($blddir, $_->[0]); + + mkpath($buildd); + + my $f = $_->[1]; + # The basic things we're trying to build + my @programs = (); + my @programs_install = (); + my @libraries = (); + my @libraries_install = (); + my @engines = (); + my @engines_install = (); + my @scripts = (); + my @scripts_install = (); + my @extra = (); + my @overrides = (); + my @intermediates = (); + my @rawlines = (); + + my %ordinals = (); + my %sources = (); + my %shared_sources = (); + my %includes = (); + my %depends = (); + my %renames = (); + my %sharednames = (); + my %generate = (); + + push @{$config{build_infos}}, catfile(abs2rel($sourced, $blddir), $f); + my $template = Text::Template->new(TYPE => 'FILE', + SOURCE => catfile($sourced, $f)); + die "Something went wrong with $sourced/$f: $!\n" unless $template; + my @text = + split /^/m, + $template->fill_in(HASH => { config => \%config, + target => \%target, + disabled => \%disabled, + withargs => \%withargs, + builddir => abs2rel($buildd, $blddir), + sourcedir => abs2rel($sourced, $blddir), + buildtop => abs2rel($blddir, $blddir), + sourcetop => abs2rel($srcdir, $blddir) }, + DELIMITERS => [ "{-", "-}" ]); + + # The top item of this stack has the following values + # -2 positive already run and we found ELSE (following ELSIF should fail) + # -1 positive already run (skip until ENDIF) + # 0 negatives so far (if we're at a condition, check it) + # 1 last was positive (don't skip lines until next ELSE, ELSIF or ENDIF) + # 2 positive ELSE (following ELSIF should fail) + my @skip = (); + collect_information( + collect_from_array([ @text ], + qr/\\$/ => sub { my $l1 = shift; my $l2 = shift; + $l1 =~ s/\\$//; $l1.$l2 }), + # Info we're looking for + qr/^\s*IF\[((?:\\.|[^\\\]])*)\]\s*$/ + => sub { + if (! @skip || $skip[$#skip] > 0) { + push @skip, !! $1; + } else { + push @skip, -1; + } + }, + qr/^\s*ELSIF\[((?:\\.|[^\\\]])*)\]\s*$/ + => sub { die "ELSIF out of scope" if ! @skip; + die "ELSIF following ELSE" if abs($skip[$#skip]) == 2; + $skip[$#skip] = -1 if $skip[$#skip] != 0; + $skip[$#skip] = !! $1 + if $skip[$#skip] == 0; }, + qr/^\s*ELSE\s*$/ + => sub { die "ELSE out of scope" if ! @skip; + $skip[$#skip] = -2 if $skip[$#skip] != 0; + $skip[$#skip] = 2 if $skip[$#skip] == 0; }, + qr/^\s*ENDIF\s*$/ + => sub { die "ENDIF out of scope" if ! @skip; + pop @skip; }, + qr/^\s*PROGRAMS(_NO_INST)?\s*=\s*(.*)\s*$/ + => sub { + if (!@skip || $skip[$#skip] > 0) { + my $install = $1; + my @x = tokenize($2); + push @programs, @x; + push @programs_install, @x unless $install; + } + }, + qr/^\s*LIBS(_NO_INST)?\s*=\s*(.*)\s*$/ + => sub { + if (!@skip || $skip[$#skip] > 0) { + my $install = $1; + my @x = tokenize($2); + push @libraries, @x; + push @libraries_install, @x unless $install; + } + }, + qr/^\s*ENGINES(_NO_INST)?\s*=\s*(.*)\s*$/ + => sub { + if (!@skip || $skip[$#skip] > 0) { + my $install = $1; + my @x = tokenize($2); + push @engines, @x; + push @engines_install, @x unless $install; + } + }, + qr/^\s*SCRIPTS(_NO_INST)?\s*=\s*(.*)\s*$/ + => sub { + if (!@skip || $skip[$#skip] > 0) { + my $install = $1; + my @x = tokenize($2); + push @scripts, @x; + push @scripts_install, @x unless $install; + } + }, + qr/^\s*EXTRA\s*=\s*(.*)\s*$/ + => sub { push @extra, tokenize($1) + if !@skip || $skip[$#skip] > 0 }, + qr/^\s*OVERRIDES\s*=\s*(.*)\s*$/ + => sub { push @overrides, tokenize($1) + if !@skip || $skip[$#skip] > 0 }, + + qr/^\s*ORDINALS\[((?:\\.|[^\\\]])+)\]\s*=\s*(.*)\s*$/, + => sub { push @{$ordinals{$1}}, tokenize($2) + if !@skip || $skip[$#skip] > 0 }, + qr/^\s*SOURCE\[((?:\\.|[^\\\]])+)\]\s*=\s*(.*)\s*$/ + => sub { push @{$sources{$1}}, tokenize($2) + if !@skip || $skip[$#skip] > 0 }, + qr/^\s*SHARED_SOURCE\[((?:\\.|[^\\\]])+)\]\s*=\s*(.*)\s*$/ + => sub { push @{$shared_sources{$1}}, tokenize($2) + if !@skip || $skip[$#skip] > 0 }, + qr/^\s*INCLUDE\[((?:\\.|[^\\\]])+)\]\s*=\s*(.*)\s*$/ + => sub { push @{$includes{$1}}, tokenize($2) + if !@skip || $skip[$#skip] > 0 }, + qr/^\s*DEPEND\[((?:\\.|[^\\\]])*)\]\s*=\s*(.*)\s*$/ + => sub { push @{$depends{$1}}, tokenize($2) + if !@skip || $skip[$#skip] > 0 }, + qr/^\s*GENERATE\[((?:\\.|[^\\\]])+)\]\s*=\s*(.*)\s*$/ + => sub { push @{$generate{$1}}, $2 + if !@skip || $skip[$#skip] > 0 }, + qr/^\s*RENAME\[((?:\\.|[^\\\]])+)\]\s*=\s*(.*)\s*$/ + => sub { push @{$renames{$1}}, tokenize($2) + if !@skip || $skip[$#skip] > 0 }, + qr/^\s*SHARED_NAME\[((?:\\.|[^\\\]])+)\]\s*=\s*(.*)\s*$/ + => sub { push @{$sharednames{$1}}, tokenize($2) + if !@skip || $skip[$#skip] > 0 }, + qr/^\s*BEGINRAW\[((?:\\.|[^\\\]])+)\]\s*$/ + => sub { + my $lineiterator = shift; + my $target_kind = $1; + while (defined $lineiterator->()) { + s|\R$||; + if (/^\s*ENDRAW\[((?:\\.|[^\\\]])+)\]\s*$/) { + die "ENDRAW doesn't match BEGINRAW" + if $1 ne $target_kind; + last; + } + next if @skip && $skip[$#skip] <= 0; + push @rawlines, $_ + if ($target_kind eq $target{build_file} + || $target_kind eq $target{build_file}."(".$builder_platform.")"); + } + }, + qr/^(?:#.*|\s*)$/ => sub { }, + "OTHERWISE" => sub { die "Something wrong with this line:\n$_\nat $sourced/$f" }, + "BEFORE" => sub { + if ($buildinfo_debug) { + print STDERR "DEBUG: Parsing ",join(" ", @_),"\n"; + print STDERR "DEBUG: ... before parsing, skip stack is ",join(" ", map { int($_) } @skip),"\n"; + } + }, + "AFTER" => sub { + if ($buildinfo_debug) { + print STDERR "DEBUG: .... after parsing, skip stack is ",join(" ", map { int($_) } @skip),"\n"; + } + }, + ); + die "runaway IF?" if (@skip); + + foreach (keys %renames) { + die "$_ renamed to more than one thing: " + ,join(" ", @{$renames{$_}}),"\n" + if scalar @{$renames{$_}} > 1; + my $dest = cleanfile($buildd, $_, $blddir); + my $to = cleanfile($buildd, $renames{$_}->[0], $blddir); + die "$dest renamed to more than one thing: " + ,$unified_info{rename}->{$dest}, $to + unless !defined($unified_info{rename}->{$dest}) + or $unified_info{rename}->{$dest} eq $to; + $unified_info{rename}->{$dest} = $to; + } + + foreach (@programs) { + my $program = cleanfile($buildd, $_, $blddir); + if ($unified_info{rename}->{$program}) { + $program = $unified_info{rename}->{$program}; + } + $unified_info{programs}->{$program} = 1; + } + + foreach (@programs_install) { + my $program = cleanfile($buildd, $_, $blddir); + if ($unified_info{rename}->{$program}) { + $program = $unified_info{rename}->{$program}; + } + $unified_info{install}->{programs}->{$program} = 1; + } + + foreach (@libraries) { + my $library = cleanfile($buildd, $_, $blddir); + if ($unified_info{rename}->{$library}) { + $library = $unified_info{rename}->{$library}; + } + $unified_info{libraries}->{$library} = 1; + } + + foreach (@libraries_install) { + my $library = cleanfile($buildd, $_, $blddir); + if ($unified_info{rename}->{$library}) { + $library = $unified_info{rename}->{$library}; + } + $unified_info{install}->{libraries}->{$library} = 1; + } + + die <<"EOF" if scalar @engines and !$config{dynamic_engines}; +ENGINES can only be used if configured with 'dynamic-engine'. +This is usually a fault in a build.info file. +EOF + foreach (@engines) { + my $library = cleanfile($buildd, $_, $blddir); + if ($unified_info{rename}->{$library}) { + $library = $unified_info{rename}->{$library}; + } + $unified_info{engines}->{$library} = 1; + } + + foreach (@engines_install) { + my $library = cleanfile($buildd, $_, $blddir); + if ($unified_info{rename}->{$library}) { + $library = $unified_info{rename}->{$library}; + } + $unified_info{install}->{engines}->{$library} = 1; + } + + foreach (@scripts) { + my $script = cleanfile($buildd, $_, $blddir); + if ($unified_info{rename}->{$script}) { + $script = $unified_info{rename}->{$script}; + } + $unified_info{scripts}->{$script} = 1; + } + + foreach (@scripts_install) { + my $script = cleanfile($buildd, $_, $blddir); + if ($unified_info{rename}->{$script}) { + $script = $unified_info{rename}->{$script}; + } + $unified_info{install}->{scripts}->{$script} = 1; + } + + foreach (@extra) { + my $extra = cleanfile($buildd, $_, $blddir); + $unified_info{extra}->{$extra} = 1; + } + + foreach (@overrides) { + my $override = cleanfile($buildd, $_, $blddir); + $unified_info{overrides}->{$override} = 1; + } + + push @{$unified_info{rawlines}}, @rawlines; + + unless ($disabled{shared}) { + # Check sharednames. + foreach (keys %sharednames) { + my $dest = cleanfile($buildd, $_, $blddir); + if ($unified_info{rename}->{$dest}) { + $dest = $unified_info{rename}->{$dest}; + } + die "shared_name for $dest with multiple values: " + ,join(" ", @{$sharednames{$_}}),"\n" + if scalar @{$sharednames{$_}} > 1; + my $to = cleanfile($buildd, $sharednames{$_}->[0], $blddir); + die "shared_name found for a library $dest that isn't defined\n" + unless $unified_info{libraries}->{$dest}; + die "shared_name for $dest with multiple values: " + ,$unified_info{sharednames}->{$dest}, ", ", $to + unless !defined($unified_info{sharednames}->{$dest}) + or $unified_info{sharednames}->{$dest} eq $to; + $unified_info{sharednames}->{$dest} = $to; + } + + # Additionally, we set up sharednames for libraries that don't + # have any, as themselves. + foreach (keys %{$unified_info{libraries}}) { + if (!defined $unified_info{sharednames}->{$_}) { + $unified_info{sharednames}->{$_} = $_ + } + } + } + + foreach (keys %ordinals) { + my $dest = $_; + my $ddest = cleanfile($buildd, $_, $blddir); + if ($unified_info{rename}->{$ddest}) { + $ddest = $unified_info{rename}->{$ddest}; + } + foreach (@{$ordinals{$dest}}) { + my %known_ordinals = + ( + crypto => + cleanfile($sourced, catfile("util", "libcrypto.num"), $blddir), + ssl => + cleanfile($sourced, catfile("util", "libssl.num"), $blddir) + ); + my $o = $known_ordinals{$_}; + die "Ordinals for $ddest defined more than once\n" + if $unified_info{ordinals}->{$ddest}; + $unified_info{ordinals}->{$ddest} = [ $_, $o ]; + } + } + + foreach (keys %sources) { + my $dest = $_; + my $ddest = cleanfile($buildd, $_, $blddir); + if ($unified_info{rename}->{$ddest}) { + $ddest = $unified_info{rename}->{$ddest}; + } + foreach (@{$sources{$dest}}) { + my $s = cleanfile($sourced, $_, $blddir); + + # If it isn't in the source tree, we assume it's generated + # in the build tree + if (! -f $s) { + $s = cleanfile($buildd, $_, $blddir); + } + # We recognise C and asm files + if ($s =~ /\.[csS]\b$/) { + (my $o = $_) =~ s/\.[csS]\b$/.o/; + $o = cleanfile($buildd, $o, $blddir); + $unified_info{sources}->{$ddest}->{$o} = 1; + $unified_info{sources}->{$o}->{$s} = 1; + } else { + $unified_info{sources}->{$ddest}->{$s} = 1; + } + } + } + + foreach (keys %shared_sources) { + my $dest = $_; + my $ddest = cleanfile($buildd, $_, $blddir); + if ($unified_info{rename}->{$ddest}) { + $ddest = $unified_info{rename}->{$ddest}; + } + foreach (@{$shared_sources{$dest}}) { + my $s = cleanfile($sourced, $_, $blddir); + + # If it isn't in the source tree, we assume it's generated + # in the build tree + if (! -f $s) { + $s = cleanfile($buildd, $_, $blddir); + } + # We recognise C and asm files + if ($s =~ /\.[csS]\b$/) { + (my $o = $_) =~ s/\.[csS]\b$/.o/; + $o = cleanfile($buildd, $o, $blddir); + $unified_info{shared_sources}->{$ddest}->{$o} = 1; + $unified_info{sources}->{$o}->{$s} = 1; + } else { + die "unrecognised source file type for shared library: $s\n"; + } + } + } + + foreach (keys %generate) { + my $dest = $_; + my $ddest = cleanfile($buildd, $_, $blddir); + if ($unified_info{rename}->{$ddest}) { + $ddest = $unified_info{rename}->{$ddest}; + } + die "more than one generator for $dest: " + ,join(" ", @{$generate{$_}}),"\n" + if scalar @{$generate{$_}} > 1; + my @generator = split /\s+/, $generate{$dest}->[0]; + $generator[0] = cleanfile($sourced, $generator[0], $blddir), + $unified_info{generate}->{$ddest} = [ @generator ]; + } + + foreach (keys %depends) { + my $dest = $_; + my $ddest = $dest eq "" ? "" : cleanfile($sourced, $_, $blddir); + + # If the destination doesn't exist in source, it can only be + # a generated file in the build tree. + if ($ddest ne "" && ! -f $ddest) { + $ddest = cleanfile($buildd, $_, $blddir); + if ($unified_info{rename}->{$ddest}) { + $ddest = $unified_info{rename}->{$ddest}; + } + } + foreach (@{$depends{$dest}}) { + my $d = cleanfile($sourced, $_, $blddir); + + # If we know it's generated, or assume it is because we can't + # find it in the source tree, we set file we depend on to be + # in the build tree rather than the source tree, and assume + # and that there are lines to build it in a BEGINRAW..ENDRAW + # section or in the Makefile template. + if (! -f $d + || (grep { $d eq $_ } + map { cleanfile($srcdir, $_, $blddir) } + grep { /\.h$/ } keys %{$unified_info{generate}})) { + $d = cleanfile($buildd, $_, $blddir); + } + # Take note if the file to depend on is being renamed + if ($unified_info{rename}->{$d}) { + $d = $unified_info{rename}->{$d}; + } + $unified_info{depends}->{$ddest}->{$d} = 1; + # If we depend on a header file or a perl module, let's make + # sure it can get included + if ($dest ne "" && $d =~ /\.(h|pm)$/) { + my $i = dirname($d); + push @{$unified_info{includes}->{$ddest}->{source}}, $i + unless grep { $_ eq $i } @{$unified_info{includes}->{$ddest}->{source}}; + } + } + } + + foreach (keys %includes) { + my $dest = $_; + my $ddest = cleanfile($sourced, $_, $blddir); + + # If the destination doesn't exist in source, it can only be + # a generated file in the build tree. + if (! -f $ddest) { + $ddest = cleanfile($buildd, $_, $blddir); + if ($unified_info{rename}->{$ddest}) { + $ddest = $unified_info{rename}->{$ddest}; + } + } + foreach (@{$includes{$dest}}) { + my $is = cleandir($sourced, $_, $blddir); + my $ib = cleandir($buildd, $_, $blddir); + push @{$unified_info{includes}->{$ddest}->{source}}, $is + unless grep { $_ eq $is } @{$unified_info{includes}->{$ddest}->{source}}; + push @{$unified_info{includes}->{$ddest}->{build}}, $ib + unless grep { $_ eq $ib } @{$unified_info{includes}->{$ddest}->{build}}; + } + } + } + + ### Make unified_info a bit more efficient + # One level structures + foreach (("programs", "libraries", "engines", "scripts", "extra", "overrides")) { + $unified_info{$_} = [ sort keys %{$unified_info{$_}} ]; + } + # Two level structures + foreach my $l1 (("install", "sources", "shared_sources", "ldadd", "depends")) { + foreach my $l2 (sort keys %{$unified_info{$l1}}) { + $unified_info{$l1}->{$l2} = + [ sort keys %{$unified_info{$l1}->{$l2}} ]; + } + } + # Includes + foreach my $dest (sort keys %{$unified_info{includes}}) { + if (defined($unified_info{includes}->{$dest}->{build})) { + my @source_includes = + ( @{$unified_info{includes}->{$dest}->{source}} ); + $unified_info{includes}->{$dest} = + [ @{$unified_info{includes}->{$dest}->{build}} ]; + foreach my $inc (@source_includes) { + push @{$unified_info{includes}->{$dest}}, $inc + unless grep { $_ eq $inc } @{$unified_info{includes}->{$dest}}; + } + } else { + $unified_info{includes}->{$dest} = + [ @{$unified_info{includes}->{$dest}->{source}} ]; + } + } +} + +# For the schemes that need it, we provide the old *_obj configs +# from the *_asm_obj ones +foreach (grep /_(asm|aux)_src$/, keys %target) { + my $src = $_; + (my $obj = $_) =~ s/_(asm|aux)_src$/_obj/; + ($target{$obj} = $target{$src}) =~ s/\.[csS]\b/.o/g; +} + +# Write down our configuration where it fits ######################### + +open(OUT,">configdata.pm") || die "unable to create configdata.pm: $!\n"; +print OUT <<"EOF"; +package configdata; + +use strict; +use warnings; + +use Exporter; +#use vars qw(\@ISA \@EXPORT); +our \@ISA = qw(Exporter); +our \@EXPORT = qw(\%config \%target \%disabled \%withargs \%unified_info \@disablables); + +EOF +print OUT "our %config = (\n"; +foreach (sort keys %config) { + if (ref($config{$_}) eq "ARRAY") { + print OUT " ", $_, " => [ ", join(", ", + map { quotify("perl", $_) } + @{$config{$_}}), " ],\n"; + } else { + print OUT " ", $_, " => ", quotify("perl", $config{$_}), ",\n" + } +} +print OUT <<"EOF"; +); + +EOF +print OUT "our %target = (\n"; +foreach (sort keys %target) { + if (ref($target{$_}) eq "ARRAY") { + print OUT " ", $_, " => [ ", join(", ", + map { quotify("perl", $_) } + @{$target{$_}}), " ],\n"; + } else { + print OUT " ", $_, " => ", quotify("perl", $target{$_}), ",\n" + } +} +print OUT <<"EOF"; +); + +EOF +print OUT "our \%available_protocols = (\n"; +print OUT " tls => [ ", join(", ", map { quotify("perl", $_) } @tls), " ],\n"; +print OUT " dtls => [ ", join(", ", map { quotify("perl", $_) } @dtls), " ],\n"; +print OUT <<"EOF"; +); + +EOF +print OUT "our \@disablables = (\n"; +foreach (@disablables) { + print OUT " ", quotify("perl", $_), ",\n"; +} +print OUT <<"EOF"; +); + +EOF +print OUT "our \%disabled = (\n"; +foreach (sort keys %disabled) { + print OUT " ", quotify("perl", $_), " => ", quotify("perl", $disabled{$_}), ",\n"; +} +print OUT <<"EOF"; +); + +EOF +print OUT "our %withargs = (\n"; +foreach (sort keys %withargs) { + if (ref($withargs{$_}) eq "ARRAY") { + print OUT " ", $_, " => [ ", join(", ", + map { quotify("perl", $_) } + @{$withargs{$_}}), " ],\n"; + } else { + print OUT " ", $_, " => ", quotify("perl", $withargs{$_}), ",\n" + } +} +print OUT <<"EOF"; +); + +EOF +if ($builder eq "unified") { + my $recurse; + $recurse = sub { + my $indent = shift; + foreach (@_) { + if (ref $_ eq "ARRAY") { + print OUT " "x$indent, "[\n"; + foreach (@$_) { + $recurse->($indent + 4, $_); + } + print OUT " "x$indent, "],\n"; + } elsif (ref $_ eq "HASH") { + my %h = %$_; + print OUT " "x$indent, "{\n"; + foreach (sort keys %h) { + if (ref $h{$_} eq "") { + print OUT " "x($indent + 4), quotify("perl", $_), " => ", quotify("perl", $h{$_}), ",\n"; + } else { + print OUT " "x($indent + 4), quotify("perl", $_), " =>\n"; + $recurse->($indent + 8, $h{$_}); + } + } + print OUT " "x$indent, "},\n"; + } else { + print OUT " "x$indent, quotify("perl", $_), ",\n"; + } + } + }; + print OUT "our %unified_info = (\n"; + foreach (sort keys %unified_info) { + if (ref $unified_info{$_} eq "") { + print OUT " "x4, quotify("perl", $_), " => ", quotify("perl", $unified_info{$_}), ",\n"; + } else { + print OUT " "x4, quotify("perl", $_), " =>\n"; + $recurse->(8, $unified_info{$_}); + } + } + print OUT <<"EOF"; +); + +EOF +} +print OUT "1;\n"; +close(OUT); + + +print "CC =$config{cross_compile_prefix}$target{cc}\n"; +print "CFLAG =$target{cflags} $config{cflags}\n"; +print "SHARED_CFLAG =$target{shared_cflag}\n"; +print "DEFINES =",join(" ", @{$target{defines}}, @{$config{defines}}),"\n"; +print "LFLAG =$target{lflags}\n"; +print "PLIB_LFLAG =$target{plib_lflags}\n"; +print "EX_LIBS =$target{ex_libs} $config{ex_libs}\n"; +print "APPS_OBJ =$target{apps_obj}\n"; +print "CPUID_OBJ =$target{cpuid_obj}\n"; +print "UPLINK_OBJ =$target{uplink_obj}\n"; +print "BN_ASM =$target{bn_obj}\n"; +print "EC_ASM =$target{ec_obj}\n"; +print "DES_ENC =$target{des_obj}\n"; +print "AES_ENC =$target{aes_obj}\n"; +print "BF_ENC =$target{bf_obj}\n"; +print "CAST_ENC =$target{cast_obj}\n"; +print "RC4_ENC =$target{rc4_obj}\n"; +print "RC5_ENC =$target{rc5_obj}\n"; +print "MD5_OBJ_ASM =$target{md5_obj}\n"; +print "SHA1_OBJ_ASM =$target{sha1_obj}\n"; +print "RMD160_OBJ_ASM=$target{rmd160_obj}\n"; +print "CMLL_ENC =$target{cmll_obj}\n"; +print "MODES_OBJ =$target{modes_obj}\n"; +print "PADLOCK_OBJ =$target{padlock_obj}\n"; +print "GMI_OBJ =$target{gmi_obj}\n"; +print "CHACHA_ENC =$target{chacha_obj}\n"; +print "POLY1305_OBJ =$target{poly1305_obj}\n"; +print "BLAKE2_OBJ =$target{blake2_obj}\n"; +print "PROCESSOR =$config{processor}\n"; +print "RANLIB =", $target{ranlib} eq '$(CROSS_COMPILE)ranlib' ? + "$config{cross_compile_prefix}ranlib" : + "$target{ranlib}", "\n"; +print "ARFLAGS =$target{arflags}\n"; +print "PERL =$config{perl}\n"; +print "\n"; +print "SIXTY_FOUR_BIT_LONG mode\n" if $config{b64l}; +print "SIXTY_FOUR_BIT mode\n" if $config{b64}; +print "THIRTY_TWO_BIT mode\n" if $config{b32}; +print "BN_LLONG mode\n" if $config{bn_ll}; +print "RC4 uses $config{rc4_int}\n" if $config{rc4_int} ne $def_int; + +my %builders = ( + unified => sub { + run_dofile(catfile($blddir, $target{build_file}), + @{$config{build_file_templates}}); + }, + ); + +$builders{$builder}->($builder_platform, @builder_opts); + +print <<"EOF"; + +Configured for $target. +EOF + +print <<"EOF" if ($disabled{threads} eq "unavailable"); + +The library could not be configured for supporting multi-threaded +applications as the compiler options required on this system are not known. +See file INSTALL for details if you need multi-threading. +EOF + +print <<"EOF" if ($no_shared_warn); + +The options 'shared', 'pic' and 'dynamic-engine' aren't supported on this +platform, so we will pretend you gave the option 'no-pic', which also disables +'shared' and 'dynamic-engine'. If you know how to implement shared libraries +or position independent code, please let us know (but please first make sure +you have tried with a current version of OpenSSL). +EOF + +print <<"EOF" if (-f catfile($srcdir, "configdata.pm") && $srcdir ne $blddir); + +WARNING: there are indications that another build was made in the source +directory. This build may have picked up artifacts from that build, the +safest course of action is to clean the source directory and redo this +configuration. +EOF + +exit(0); + +###################################################################### +# +# Helpers and utility functions +# + +# Configuration file reading ######################################### + +# Note: All of the helper functions are for lazy evaluation. They all +# return a CODE ref, which will return the intended value when evaluated. +# Thus, whenever there's mention of a returned value, it's about that +# intended value. + +# Helper function to implement conditional inheritance depending on the +# value of $disabled{asm}. Used in inherit_from values as follows: +# +# inherit_from => [ "template", asm("asm_tmpl") ] +# +sub asm { + my @x = @_; + sub { + $disabled{asm} ? () : @x; + } +} + +# Helper function to implement conditional value variants, with a default +# plus additional values based on the value of $config{build_type}. +# Arguments are given in hash table form: +# +# picker(default => "Basic string: ", +# debug => "debug", +# release => "release") +# +# When configuring with --debug, the resulting string will be +# "Basic string: debug", and when not, it will be "Basic string: release" +# +# This can be used to create variants of sets of flags according to the +# build type: +# +# cflags => picker(default => "-Wall", +# debug => "-g -O0", +# release => "-O3") +# +sub picker { + my %opts = @_; + return sub { add($opts{default} || (), + $opts{$config{build_type}} || ())->(); } +} + +# Helper function to combine several values of different types into one. +# This is useful if you want to combine a string with the result of a +# lazy function, such as: +# +# cflags => combine("-Wall", sub { $disabled{zlib} ? () : "-DZLIB" }) +# +sub combine { + my @stuff = @_; + return sub { add(@stuff)->(); } +} + +# Helper function to implement conditional values depending on the value +# of $disabled{threads}. Can be used as follows: +# +# cflags => combine("-Wall", threads("-pthread")) +# +sub threads { + my @flags = @_; + return sub { add($disabled{threads} ? () : @flags)->(); } +} + + + +our $add_called = 0; +# Helper function to implement adding values to already existing configuration +# values. It handles elements that are ARRAYs, CODEs and scalars +sub _add { + my $separator = shift; + + # If there's any ARRAY in the collection of values OR the separator + # is undef, we will return an ARRAY of combined values, otherwise a + # string of joined values with $separator as the separator. + my $found_array = !defined($separator); + + my @values = + map { + my $res = $_; + while (ref($res) eq "CODE") { + $res = $res->(); + } + if (defined($res)) { + if (ref($res) eq "ARRAY") { + $found_array = 1; + @$res; + } else { + $res; + } + } else { + (); + } + } (@_); + + $add_called = 1; + + if ($found_array) { + [ @values ]; + } else { + join($separator, grep { defined($_) && $_ ne "" } @values); + } +} +sub add_before { + my $separator = " "; + if (ref($_[$#_]) eq "HASH") { + my $opts = pop; + $separator = $opts->{separator}; + } + my @x = @_; + sub { _add($separator, @x, @_) }; +} +sub add { + my $separator = " "; + if (ref($_[$#_]) eq "HASH") { + my $opts = pop; + $separator = $opts->{separator}; + } + my @x = @_; + sub { _add($separator, @_, @x) }; +} + +# configuration reader, evaluates the input file as a perl script and expects +# it to fill %targets with target configurations. Those are then added to +# %table. +sub read_config { + my $fname = shift; + open(CONFFILE, "< $fname") + or die "Can't open configuration file '$fname'!\n"; + my $x = $/; + undef $/; + my $content = ; + $/ = $x; + close(CONFFILE); + my %targets = (); + { + # Protect certain tables from tampering + local %table = %::table; + + eval $content; + warn $@ if $@; + } + + # For each target, check that it's configured with a hash table. + foreach (keys %targets) { + if (ref($targets{$_}) ne "HASH") { + if (ref($targets{$_}) eq "") { + warn "Deprecated target configuration for $_, ignoring...\n"; + } else { + warn "Misconfigured target configuration for $_ (should be a hash table), ignoring...\n"; + } + delete $targets{$_}; + } else { + $targets{$_}->{_conf_fname_int} = add([ $fname ]); + } + } + + %table = (%table, %targets); + +} + +# configuration resolver. Will only resolve all the lazy evaluation +# codeblocks for the chosen target and all those it inherits from, +# recursively +sub resolve_config { + my $target = shift; + my @breadcrumbs = @_; + +# my $extra_checks = defined($ENV{CONFIGURE_EXTRA_CHECKS}); + + if (grep { $_ eq $target } @breadcrumbs) { + die "inherit_from loop! target backtrace:\n " + ,$target,"\n ",join("\n ", @breadcrumbs),"\n"; + } + + if (!defined($table{$target})) { + warn "Warning! target $target doesn't exist!\n"; + return (); + } + # Recurse through all inheritances. They will be resolved on the + # fly, so when this operation is done, they will all just be a + # bunch of attributes with string values. + # What we get here, though, are keys with references to lists of + # the combined values of them all. We will deal with lists after + # this stage is done. + my %combined_inheritance = (); + if ($table{$target}->{inherit_from}) { + my @inherit_from = + map { ref($_) eq "CODE" ? $_->() : $_ } @{$table{$target}->{inherit_from}}; + foreach (@inherit_from) { + my %inherited_config = resolve_config($_, $target, @breadcrumbs); + + # 'template' is a marker that's considered private to + # the config that had it. + delete $inherited_config{template}; + + foreach (keys %inherited_config) { + if (!$combined_inheritance{$_}) { + $combined_inheritance{$_} = []; + } + push @{$combined_inheritance{$_}}, $inherited_config{$_}; + } + } + } + + # We won't need inherit_from in this target any more, since we've + # resolved all the inheritances that lead to this + delete $table{$target}->{inherit_from}; + + # Now is the time to deal with those lists. Here's the place to + # decide what shall be done with those lists, all based on the + # values of the target we're currently dealing with. + # - If a value is a coderef, it will be executed with the list of + # inherited values as arguments. + # - If the corresponding key doesn't have a value at all or is the + # empty string, the inherited value list will be run through the + # default combiner (below), and the result becomes this target's + # value. + # - Otherwise, this target's value is assumed to be a string that + # will simply override the inherited list of values. + my $default_combiner = add(); + + my %all_keys = + map { $_ => 1 } (keys %combined_inheritance, + keys %{$table{$target}}); + + sub process_values { + my $object = shift; + my $inherited = shift; # Always a [ list ] + my $target = shift; + my $entry = shift; + + $add_called = 0; + + while(ref($object) eq "CODE") { + $object = $object->(@$inherited); + } + if (!defined($object)) { + return (); + } + elsif (ref($object) eq "ARRAY") { + local $add_called; # To make sure recursive calls don't affect it + return [ map { process_values($_, $inherited, $target, $entry) } + @$object ]; + } elsif (ref($object) eq "") { + return $object; + } else { + die "cannot handle reference type ",ref($object) + ," found in target ",$target," -> ",$entry,"\n"; + } + } + + foreach (sort keys %all_keys) { + my $previous = $combined_inheritance{$_}; + + # Current target doesn't have a value for the current key? + # Assign it the default combiner, the rest of this loop body + # will handle it just like any other coderef. + if (!exists $table{$target}->{$_}) { + $table{$target}->{$_} = $default_combiner; + } + + $table{$target}->{$_} = process_values($table{$target}->{$_}, + $combined_inheritance{$_}, + $target, $_); + unless(defined($table{$target}->{$_})) { + delete $table{$target}->{$_}; + } +# if ($extra_checks && +# $previous && !($add_called || $previous ~~ $table{$target}->{$_})) { +# warn "$_ got replaced in $target\n"; +# } + } + + # Finally done, return the result. + return %{$table{$target}}; +} + +sub usage + { + print STDERR $usage; + print STDERR "\npick os/compiler from:\n"; + my $j=0; + my $i; + my $k=0; + foreach $i (sort keys %table) + { + next if $table{$i}->{template}; + next if $i =~ /^debug/; + $k += length($i) + 1; + if ($k > 78) + { + print STDERR "\n"; + $k=length($i); + } + print STDERR $i . " "; + } + foreach $i (sort keys %table) + { + next if $table{$i}->{template}; + next if $i !~ /^debug/; + $k += length($i) + 1; + if ($k > 78) + { + print STDERR "\n"; + $k=length($i); + } + print STDERR $i . " "; + } + print STDERR "\n\nNOTE: If in doubt, on Unix-ish systems use './config'.\n"; + exit(1); + } + +sub run_dofile +{ + my $out = shift; + my @templates = @_; + + unlink $out || warn "Can't remove $out, $!" + if -f $out; + foreach (@templates) { + die "Can't open $_, $!" unless -f $_; + } + my $perlcmd = (quotify("maybeshell", $config{perl}))[0]; + my $cmd = "$perlcmd \"-I.\" \"-Mconfigdata\" \"$dofile\" -o\"Configure\" \"".join("\" \"",@templates)."\" > \"$out.new\""; + #print STDERR "DEBUG[run_dofile]: \$cmd = $cmd\n"; + system($cmd); + exit 1 if $? != 0; + rename("$out.new", $out) || die "Can't rename $out.new, $!"; +} + +sub which +{ + my ($name)=@_; + + if (eval { require IPC::Cmd; 1; }) { + IPC::Cmd->import(); + return scalar IPC::Cmd::can_run($name); + } else { + # if there is $directories component in splitpath, + # then it's not something to test with $PATH... + return $name if (File::Spec->splitpath($name))[1]; + + foreach (File::Spec->path()) { + my $fullpath = catfile($_, "$name$target{exe_extension}"); + if (-f $fullpath and -x $fullpath) { + return $fullpath; + } + } + } +} + +# Configuration printer ############################################## + +sub print_table_entry +{ + my $target = shift; + my %target = resolve_config($target); + my $type = shift; + + # Don't print the templates + return if $target{template}; + + my @sequence = ( + "sys_id", + "cc", + "cflags", + "defines", + "unistd", + "ld", + "lflags", + "loutflag", + "plib_lflags", + "ex_libs", + "bn_ops", + "apps_aux_src", + "cpuid_asm_src", + "uplink_aux_src", + "bn_asm_src", + "ec_asm_src", + "des_asm_src", + "aes_asm_src", + "bf_asm_src", + "md5_asm_src", + "cast_asm_src", + "sha1_asm_src", + "rc4_asm_src", + "rmd160_asm_src", + "rc5_asm_src", + "wp_asm_src", + "cmll_asm_src", + "modes_asm_src", + "padlock_asm_src", + "gmi_asm_src", + "chacha_asm_src", + "poly1035_asm_src", + "thread_scheme", + "perlasm_scheme", + "dso_scheme", + "shared_target", + "shared_cflag", + "shared_defines", + "shared_ldflag", + "shared_rcflag", + "shared_extension", + "dso_extension", + "obj_extension", + "exe_extension", + "ranlib", + "ar", + "arflags", + "aroutflag", + "rc", + "rcflags", + "rcoutflag", + "mt", + "mtflags", + "mtinflag", + "mtoutflag", + "multilib", + "build_scheme", + ); + + if ($type eq "TABLE") { + print "\n"; + print "*** $target\n"; + foreach (@sequence) { + if (ref($target{$_}) eq "ARRAY") { + printf "\$%-12s = %s\n", $_, join(" ", @{$target{$_}}); + } else { + printf "\$%-12s = %s\n", $_, $target{$_}; + } + } + } elsif ($type eq "HASH") { + my $largest = + length((sort { length($a) <=> length($b) } @sequence)[-1]); + print " '$target' => {\n"; + foreach (@sequence) { + if ($target{$_}) { + if (ref($target{$_}) eq "ARRAY") { + print " '",$_,"'"," " x ($largest - length($_))," => [ ",join(", ", map { "'$_'" } @{$target{$_}})," ],\n"; + } else { + print " '",$_,"'"," " x ($largest - length($_))," => '",$target{$_},"',\n"; + } + } + } + print " },\n"; + } +} + +# Utility routines ################################################### + +# On VMS, if the given file is a logical name, File::Spec::Functions +# will consider it an absolute path. There are cases when we want a +# purely syntactic check without checking the environment. +sub isabsolute { + my $file = shift; + + # On non-platforms, we just use file_name_is_absolute(). + return file_name_is_absolute($file) unless $^O eq "VMS"; + + # If the file spec includes a device or a directpry spec, + # file_name_is_absolute() is perfectly safe. + return file_name_is_absolute($file) if $file =~ m|[:\[]|; + + # Here, we know the given file spec isn't absolute + return 0; +} + +# Makes a directory absolute and cleans out /../ in paths like foo/../bar +# On some platforms, this uses rel2abs(), while on others, realpath() is used. +# realpath() requires that at least all path components except the last is an +# existing directory. On VMS, the last component of the directory spec must +# exist. +sub absolutedir { + my $dir = shift; + + # realpath() is quite buggy on VMS. It uses LIB$FID_TO_NAME, which + # will return the volume name for the device, no matter what. Also, + # it will return an incorrect directory spec if the argument is a + # directory that doesn't exist. + if ($^O eq "VMS") { + return rel2abs($dir); + } + + # We use realpath() on Unix, since no other will properly clean out + # a directory spec. + use Cwd qw/realpath/; + + return realpath($dir); +} + +sub quotify { + my %processors = ( + perl => sub { my $x = shift; + $x =~ s/([\\\$\@"])/\\$1/g; + return '"'.$x.'"'; }, + maybeshell => sub { my $x = shift; + (my $y = $x) =~ s/([\\\"])/\\$1/g; + if ($x ne $y || $x =~ m|\s|) { + return '"'.$y.'"'; + } else { + return $x; + } + }, + ); + my $for = shift; + my $processor = + defined($processors{$for}) ? $processors{$for} : sub { shift; }; + + return map { $processor->($_); } @_; +} + +# collect_from_file($filename, $line_concat_cond_re, $line_concat) +# $filename is a file name to read from +# $line_concat_cond_re is a regexp detecting a line continuation ending +# $line_concat is a CODEref that takes care of concatenating two lines +sub collect_from_file { + my $filename = shift; + my $line_concat_cond_re = shift; + my $line_concat = shift; + + open my $fh, $filename || die "unable to read $filename: $!\n"; + return sub { + my $saved_line = ""; + $_ = ""; + while (<$fh>) { + s|\R$||; + if (defined $line_concat) { + $_ = $line_concat->($saved_line, $_); + $saved_line = ""; + } + if (defined $line_concat_cond_re && /$line_concat_cond_re/) { + $saved_line = $_; + next; + } + return $_; + } + die "$filename ending with continuation line\n" if $_; + close $fh; + return undef; + } +} + +# collect_from_array($array, $line_concat_cond_re, $line_concat) +# $array is an ARRAYref of lines +# $line_concat_cond_re is a regexp detecting a line continuation ending +# $line_concat is a CODEref that takes care of concatenating two lines +sub collect_from_array { + my $array = shift; + my $line_concat_cond_re = shift; + my $line_concat = shift; + my @array = (@$array); + + return sub { + my $saved_line = ""; + $_ = ""; + while (defined($_ = shift @array)) { + s|\R$||; + if (defined $line_concat) { + $_ = $line_concat->($saved_line, $_); + $saved_line = ""; + } + if (defined $line_concat_cond_re && /$line_concat_cond_re/) { + $saved_line = $_; + next; + } + return $_; + } + die "input text ending with continuation line\n" if $_; + return undef; + } +} + +# collect_information($lineiterator, $line_continue, $regexp => $CODEref, ...) +# $lineiterator is a CODEref that delivers one line at a time. +# All following arguments are regex/CODEref pairs, where the regexp detects a +# line and the CODEref does something with the result of the regexp. +sub collect_information { + my $lineiterator = shift; + my %collectors = @_; + + while(defined($_ = $lineiterator->())) { + s|\R$||; + my $found = 0; + if ($collectors{"BEFORE"}) { + $collectors{"BEFORE"}->($_); + } + foreach my $re (keys %collectors) { + if ($re !~ /^OTHERWISE|BEFORE|AFTER$/ && /$re/) { + $collectors{$re}->($lineiterator); + $found = 1; + }; + } + if ($collectors{"OTHERWISE"}) { + $collectors{"OTHERWISE"}->($lineiterator, $_) + unless $found || !defined $collectors{"OTHERWISE"}; + } + if ($collectors{"AFTER"}) { + $collectors{"AFTER"}->($_); + } + } +} + +# tokenize($line) +# $line is a line of text to split up into tokens +# returns a list of tokens +# +# Tokens are divided by spaces. If the tokens include spaces, they +# have to be quoted with single or double quotes. Double quotes +# inside a double quoted token must be escaped. Escaping is done +# with backslash. +# Basically, the same quoting rules apply for " and ' as in any +# Unix shell. +sub tokenize { + my $line = my $debug_line = shift; + my @result = (); + + while ($line =~ s|^\s+||, $line ne "") { + my $token = ""; + while ($line ne "" && $line !~ m|^\s|) { + if ($line =~ m/^"((?:[^"\\]+|\\.)*)"/) { + $token .= $1; + $line = $'; + } elsif ($line =~ m/^'([^']*)'/) { + $token .= $1; + $line = $'; + } elsif ($line =~ m/^(\S+)/) { + $token .= $1; + $line = $'; + } + } + push @result, $token; + } + + if ($ENV{CONFIGURE_DEBUG_TOKENIZE}) { + print STDERR "DEBUG[tokenize]: Parsed '$debug_line' into:\n"; + print STDERR "DEBUG[tokenize]: ('", join("', '", @result), "')\n"; + } + return @result; +} diff --git a/INSTALL.md b/INSTALL.md deleted file mode 100644 index fdc286f6b..000000000 --- a/INSTALL.md +++ /dev/null @@ -1,373 +0,0 @@ -# 编译与安装 - -[TOC] - -## 概述 - -GmSSL当å‰ç‰ˆæœ¬é‡‡ç”¨CMake构建系统。由于CMake是一个跨平å°çš„编译ã€å®‰è£…工具,因此GmSSLå¯ä»¥åœ¨å¤§å¤šæ•°ä¸»æµæ“作系统上编译ã€å®‰è£…å’Œè¿è¡Œã€‚GmSSL项目官方测试了Windows (包括Visual Studioå’ŒCygwin)ã€Linuxã€macOSã€Androidå’ŒiOSè¿™å‡ ä¸ªä¸»æµæ“作系统上的编译,并通过GitHubçš„CI工作æµå¯¹æäº¤çš„æœ€æ–°ä»£ç è¿›è¡Œè‡ªåŠ¨åŒ–çš„ç¼–è¯‘æµ‹è¯•ã€‚ - -和其他基于CMake的开æºé¡¹ç›®ç±»ä¼¼ï¼ŒGmSSL的构建过程主è¦åŒ…å«é…ç½®ã€ç¼–è¯‘ã€æµ‹è¯•ã€å®‰è£…这几个步骤。默认é…置会生æˆåЍæ€åº“;如果需è¦é™æ€åº“,å¯ä»¥åœ¨é…置阶段设置`-DBUILD_SHARED_LIBS=OFF`。 - -### Linux/macOSæºç å®‰è£… - -在Linux或macOS环境下,下载并解压GmSSLæºä»£ç åŽï¼Œè¿›å…¥æºç ç›®å½•,执行如下命令: - -```bash -mkdir build -cd build -cmake .. -DCMAKE_BUILD_TYPE=Release -cmake --build . -ctest --output-on-failure -sudo cmake --install . -``` - -å°±å¯ä»¥å®Œæˆé…ç½®ã€ç¼–è¯‘ã€æµ‹è¯•和安装。 - -在执行`cmake --build .`编译æˆåŠŸåŽï¼Œåœ¨`build/bin`目录下会生æˆé¡¹ç›®çš„坿‰§è¡Œæ–‡ä»¶å’Œåº“文件。对于密ç å·¥å…·æ¥è¯´ï¼Œåœ¨å®‰è£…使用之å‰é€šè¿‡`ctest --output-on-failure`进行测试是é‡è¦çš„一步,如果测试失败,那么ä¸åº”该使用这个软件。在å‘生æŸä¸ªæµ‹è¯•错误åŽï¼Œå¯ä»¥æ‰§è¡Œ`build/bin`下的具体æŸä¸ªæµ‹è¯•命令行,如`sm4test`,这样å¯ä»¥çœ‹åˆ°å…·ä½“的错误打å°ä¿¡æ¯ã€‚ - -执行`sudo cmake --install .`,安装完æˆåŽï¼Œå¯ä»¥å‘½ä»¤è¡Œä¸­è°ƒç”¨`gmssl`命令行工具。在Linuxå’ŒmacOS环境下,头文件通常被安装在`/usr/local/include/gmssl`目录下,库文件被安装在`/usr/local/lib`目录下。Linux默认动æ€åº“å称为`libgmssl.so`,macOS默认动æ€åº“å称为`libgmssl.dylib`。 - -### macOS二进制包的Gatekeeperæç¤º - -GmSSLå‘布的macOSäºŒè¿›åˆ¶åŒ…å½“å‰æœªè¿›è¡ŒApple Developer IDç­¾åå’Œnotarization。从æµè§ˆå™¨ä¸‹è½½å¹¶è§£åŽ‹åŽï¼ŒmacOSå¯èƒ½ä¼šä¸ºæ–‡ä»¶æ·»åŠ `com.apple.quarantine`属性,è¿è¡Œ`gmssl`æ—¶æç¤ºApple无法验è¯è¯¥ç¨‹åºæ˜¯å¦ä¸å«æ¶æ„软件。 - -如果确认二进制包æ¥è‡ªGmSSL GitHub Release页é¢ï¼Œå¯ä»¥åœ¨è§£åŽ‹åŽçš„目录上执行: - -```bash -xattr -dr com.apple.quarantine GmSSL-3.2.0-macOS-arm64 -``` - -Intel x86_64版本对应执行: - -```bash -xattr -dr com.apple.quarantine GmSSL-3.2.0-macOS-x86_64 -``` - -ç„¶åŽå†è¿è¡Œï¼š - -```bash -./GmSSL-3.2.0-macOS-arm64/bin/gmssl version -``` - -或者将对应`bin`目录加入`PATH`ã€‚è¿™ä¸ªå¤„ç†æ–¹å¼åªç§»é™¤ä¸‹è½½éš”离标记,ä¸ä¿®æ”¹`gmssl`二进制内容。 - -在Linux上安装动æ€åº“时,安装程åºä¸ä¼šè‡ªåŠ¨ä¿®æ”¹`/etc/ld.so.conf.d`或执行`ldconfig`。如果安装åŽç³»ç»Ÿæ‰¾ä¸åˆ°`libgmssl.so`,å¯ä»¥ä¸´æ—¶è®¾ç½®åЍæ€åº“æœç´¢è·¯å¾„: - -```bash -export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH -``` - -也å¯ä»¥é…置系统级动æ€åº“æœç´¢è·¯å¾„: - -```bash -echo /usr/local/lib | sudo tee /etc/ld.so.conf.d/gmssl.conf -sudo ldconfig -``` - -如果ä¸å¸Œæœ›å®‰è£…到系统目录,å¯ä»¥æŒ‡å®šå®‰è£…å‰ç¼€ï¼š - -```bash -cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=$HOME/local/gmssl -cmake --build . -ctest --output-on-failure -cmake --install . -``` - -### Windowsæºç å®‰è£… - -在Windows上建议使用Visual Studio 2022或更新版本。在"Developer Command Prompt for VS 2022"中执行: - -```bat -mkdir build -cd build -cmake .. -G "NMake Makefiles" -DCMAKE_BUILD_TYPE=Release -nmake -ctest --output-on-failure -nmake install -``` - -默认安装目录通常为`C:\Program Files\GmSSL`,命令行工具ã€å¤´æ–‡ä»¶å’Œåº“文件分别安装到`bin`ã€`include`å’Œ`lib`目录。普通用户执行安装å¯èƒ½ä¼šå› ä¸ºæƒé™ä¸è¶³å¤±è´¥ï¼Œéœ€è¦ä»¥ç®¡ç†å‘˜èº«ä»½æ‰“开开å‘者命令æç¤ºç¬¦ï¼Œæˆ–者通过`-DCMAKE_INSTALL_PREFIX=C:\path\to\gmssl`指定用户å¯å†™çš„目录。 - -## 项目æºä»£ç  - -GmSSL项目的æºä»£ç åœ¨GitHub中å‘布和维护。 - -项目在GitHub的主页为:https://github.com/guanzhi/GmSSL - -æºä»£ç åŒ…å«ä¸»åˆ†æ”¯çš„æœ€æ–°ä»£ç å’Œå®šæœŸå‘布的Release版本,建议优先采用主分支最新版。 - -### 通过CI判断当å‰ä»£ç çŠ¶æ€ - -有时候最新æäº¤çš„代ç å¯èƒ½å­˜åœ¨ç¼–译错误,通常这些错误会在1-2天内被新的æäº¤ä¿®å¤ã€‚å¦‚æžœå½“å‰æœ€æ–°ä»£ç è¿˜æ²¡æœ‰ä¿®å¤ï¼Œé‚£ä¹ˆå¯ä»¥é€šè¿‡GitHubçš„CIçŠ¶æ€æ¥é€‰æ‹©æ²¡æœ‰é”™è¯¯çš„代ç ã€‚ - -通过GitHubçš„CI工作æµçжæ€å¯ä»¥åˆ¤æ–­æŸæ¬¡æäº¤æ˜¯å¦å­˜åœ¨ç¼–译错误,目å‰GmSSL项目中é…置了如下编译环境: - -* CMake ubuntu-latest -* CMake windows-latest -* CMake macos-latest -* CMake-Android -* CMake-iOS -* CMake-OpenHarmony -* MSVC CMake - -通过查看这些CI的状æ€ï¼Œå¯ä»¥åˆ¤æ–­å½“å‰ä»£ç æ˜¯å¦å¯ä»¥åœ¨å¯¹åº”æ“作系统上æˆåŠŸç¼–è¯‘ã€‚å¦‚æžœå½“å‰æœ€æ–°ä»£ç æ— æ³•在æŸä¸ªå¹³å°ä¸Šç¼–译,那么å¯ä»¥é€‰æ‹©ä¹‹å‰æŸä¸ªé€šè¿‡æµ‹è¯•çš„Commit版本。 - -## é…置编译选项 - -在执行`cmake`阶段å¯ä»¥å¯¹é¡¹ç›®çš„默认编译é…置进行修改,修改是通过设置CMakeå˜é‡æ¥å®Œæˆçš„,å¯ä»¥æŸ¥çœ‹é¡¹ç›®æºä»£ç ä¸­çš„`CMakeLists.txt`中所有的`option`æŒ‡ä»¤æ¥æŸ¥çœ‹å¯é€‰çš„é…置。例如: - -```cmake -option(BUILD_SHARED_LIBS "Build using shared libraries" ON) -``` - -表明项目默认生æˆåЍæ€åº“。 - -### 设置生æˆåЍæ€åº“æˆ–é™æ€åº“ - -GmSSLçš„CMake默认生æˆåЍæ€åº“,å¯ä»¥é€šè¿‡è®¾å®šCMakeå˜é‡`BUILD_SHARED_LIBS`为`ON`或者`OFF`æ¥æŒ‡å®šç”ŸæˆåЍæ€åº“æˆ–é™æ€åº“。 - -``` -cmake .. -DBUILD_SHARED_LIBS=ON -cmake .. -DBUILD_SHARED_LIBS=OFF -``` - -### 设置优化的密ç ç®—法实现 - -GmSSL包å«äº†é’ˆå¯¹ç‰¹å®šç¡¬ä»¶å’Œå¤„ç†æŒ‡ä»¤é›†çš„密ç ç®—法优化实现,如针对Intel AVX2ã€AES-NIã€ARMv8/AArch64å’ŒOpenCL等平å°çš„优化。这些优化实现在匹é…的处ç†å™¨ä¸Šçš„实现速度或安全性会大大超过默认的C语言实现。 - -在é…置阶段å¯ä»¥æ˜¾å¼åœ°æŒ‡å®šé‡‡ç”¨ä¼˜åŒ–实现,å¯é€‰çš„CMakeé…ç½®å˜é‡åŒ…括: - -* `ENABLE_SM2_ARM64` SM2算法的ARMv8汇编实现。 -* `ENABLE_SM2_AMD64` SM2算法的x86_64汇编实现。 -* `ENABLE_SM3_ARM64` SM3算法的ARM NEON实现。 -* `ENABLE_SM3_SSE` SM3算法的SSE实现。 -* `ENABLE_SM4_ARM64` SM4算法的AArch64汇编实现。 -* `ENABLE_SM4_CE` SM4算法的ARMv8 Cryptography Extensions实现。 -* `ENABLE_SM4_AVX2` SM4算法的AVX2并行实现。 -* `ENABLE_SM4_AESNI` SM4算法的AES-NI实现。 -* `ENABLE_SM4_CTR_AESNI_AVX` SM4 CTR模å¼çš„AES-NI + AVX实现。 -* `ENABLE_GMUL_ARM64` GF(2^128)乘法的AArch64实现。 -* `ENABLE_INTEL_RDRAND` 基于Intel RDRANDæŒ‡ä»¤çš„ç¡¬ä»¶éšæœºæ•°ç”Ÿæˆå™¨ã€‚ -* `ENABLE_INTEL_RDSEED` 基于Intel RDSEEDæŒ‡ä»¤çš„ç¡¬ä»¶éšæœºæ•°ç”Ÿæˆå™¨ã€‚ -* `ENABLE_SM4_CL` SM4算法的OpenCL实现。 - -### è£å‰ªå¯é€‰ç®—æ³•å’Œæ¨¡å— - -GmSSL的部分算法ã€å议和硬件接å£å¯ä»¥åœ¨é…置阶段关闭,以å‡å°‘äºŒè¿›åˆ¶ä½“ç§¯æˆ–æ»¡è¶³ç‰¹å®šéƒ¨ç½²è¦æ±‚。例如: - -```bash -cmake .. -DENABLE_SHA1=OFF -DENABLE_AES=OFF -DENABLE_SKF=OFF -DENABLE_SDF=OFF -cmake --build . -``` - -å¯é€‰å¼€å…³ä»¥`CMakeLists.txt`中的`option(...)`定义为准。关闭底层算法å¯èƒ½ä¼šå½±å“ä¾èµ–它的åè®®ã€å·¥å…·å’Œæµ‹è¯•用例。 - -## 在Visual Studio环境中编译 - -CMake支æŒé€šè¿‡æŒ‡å®šä¸åŒçš„æž„建系统生æˆå™¨ï¼ˆGenerator),生æˆä¸åŒç±»åž‹çš„Makefile。在Windowså’ŒVisual Studio环境下,CMakeå³å¯ä»¥ç”Ÿæˆå¸¸è§„çš„Visual Studio解决方案(.sln)文件,在Visual Studio图形界é¢ä¸­å®Œæˆç¼–译,也å¯ä»¥ç”Ÿæˆç±»ä¼¼äºŽLinux环境下的Makefile文件,在命令行环境下完æˆç¼–译和测试。 - -### 生æˆMakefile编译 - -在安装完Visual Studio之åŽï¼Œåœ¨å¯åЍèœå•æ ä¸­ä¼šå‡ºçްVisual Studioèœå•目录,其中包å«x64 Native Tools Command Prompt for VS 2022等多个终端命令行环境èœå•项。 - -```bash -C:\Program Files\Microsoft Visual Studio\2022\Community>cd /path/to/gmssl -mkdir build -cd build -cmake .. -G "NMake Makefiles" -nmake -ctest --output-on-failure -``` - -在编译完æˆåŽç›´æŽ¥æ‰§è¡Œå®‰è£…会报æƒé™é”™è¯¯ï¼Œè¿™æ˜¯å› ä¸ºå®‰è£…过程需è¦å‘ç³»ç»Ÿç›®å½•ä¸­å†™å…¥æ–‡ä»¶ï¼Œè€Œå½“å‰æ‰“开命令行环境的用户ä¸å…·å¤‡è¯¥æƒé™ã€‚å¯ä»¥é€šè¿‡å³é”®é€‰æ‹©â€œæ›´å¤š-以管ç†å‘˜èº«ä»½è¿è¡Œâ€æ‰“å¼€x64 Native Tools Command Prompt for VS 2022终端,执行 - -``` -nmake install -``` - -那么`gmssl`命令行程åºã€å¤´æ–‡ä»¶å’Œåº“文件分别被写入`C:/Program Files/GmSSL/bin`ã€`C:/Program Files/GmSSL/include`ã€`C:/Program Files/GmSSL/lib`这几个系统目录中。为了能够直接在命令行环境任æ„目录下执行`gmssl`命令行程åºï¼Œéœ€è¦å°†å…¶å®‰è£…目录加入到系统路径中,å¯ä»¥æ‰§è¡Œï¼š - -```bash -set path=%path%;C:\Program Files\GmSSL\bin -``` - -设置完毕åŽå¯ä»¥åœ¨å‘½ä»¤è¡Œä¸­æ‰§è¡Œ`path`,查看新的路径是å¦å·²ç»æˆåŠŸåŠ å…¥ã€‚ - -### 在Visual Studio图形界é¢ä¸­ç¼–译 - -在安装完Visual Studio之åŽï¼Œåœ¨å¯åЍèœå•æ ä¸­ä¼šå‡ºçްVisual Studioèœå•目录,其中包å«x64 Native Tools Command Prompt for VS 2022等多个终端命令行环境èœå•项。 - -```bash -C:\Program Files\Microsoft Visual Studio\2022\Community>cd /path/to/gmssl -mkdir build -cd build -cmake .. -``` - -完æˆåŽå¯ä»¥çœ‹åˆ°CMake在`build`目录下生æˆäº†ä¸€ä¸ª`GmSSL.sln`文件和大é‡çš„`.vcxproj`文件。 - -点击`GmSSL.sln`就打开Visual Studio,点击Visual Studio工具æ ä¸Šçš„"本地Windows调试器"按钮,å¯ä»¥å¯åŠ¨ç¼–è¯‘ã€‚ - -在Visual Studio界é¢ä¸­å¯ä»¥é€‰æ‹©Debugã€Releaseã€MinSizeRelç­‰ä¸åŒé…置。 - -### 在Visual Studio中è¿è¡Œæµ‹è¯• - -在解决方案资æºç®¡ç†å™¨ä¸­æ‰¾åˆ°`RUN_TESTS`项目,å³é”®èœå•选择"调试-å¯åŠ¨æ–°å®žä¾‹",å³å¯è¿è¡Œæµ‹è¯•,并且在â€è¾“出“窗å£ä¸­çœ‹åˆ°æµ‹è¯•结果。测试完æˆåŽä¼šå‡ºçްRUN_TESTSæ‹’ç»è®¿é—®çš„å¯¹è¯æ¡†ã€‚ - -### 选择生æˆ32使ˆ–64ä½ç¨‹åº - -通过在Visual Studioä¸åŒçš„命令行环境中编译GmSSL,å¯ä»¥ç”Ÿæˆ32ä½çš„X86或者64ä½çš„X86_64程åºï¼Œåœ¨x64 Native Tools Command Prompt for VS 2022命令行环境下,生æˆçš„æ˜¯64ä½çš„程åºï¼Œåœ¨x86 Native Tools Command Prompt for VS 2022命令行环境下,生æˆçš„æ˜¯32ä½çš„程åºã€‚ - -å¯ä»¥é€šè¿‡Windowsæ“作系统内置的资æºç®¡ç†å™¨æ¥æ£€æŸ¥ç¼–译生æˆçš„坿‰§è¡Œç¨‹åºæ˜¯32ä½è¿˜æ˜¯64ä½ï¼Œåœ¨èµ„æºç®¡ç†å™¨çš„CPU页é¢ä¸­ï¼Œé€šè¿‡â€œé€‰æ‹©åˆ—â€å¢žåŠ â€œå¹³å°â€åˆ—,这样就å¯ä»¥æ˜¾ç¤ºæ¯ä¸ªè¿›ç¨‹çš„æ˜¯32使ˆ–64ä½ã€‚å¯ä»¥è¿è¡Œ`gmssl tlcp_client`或者在æŸä¸ªæµ‹è¯•文件中增加循环时间æ¥ä¿æŒå‘½ä»¤è¡Œè¿è¡Œä¸€æ®µæ—¶é—´ã€‚ - -## 在Cygwin环境中编译 - -Cygwin是Windows上的Linux模拟è¿è¡ŒçŽ¯å¢ƒã€‚Cygwinæä¾›äº†Linux Shell和大é‡Linux命令行工具,也æä¾›äº†åº”用程åºå¼€å‘必须的编译工具ã€å¤´æ–‡ä»¶å’Œåº“文件。é¢å‘Linuxå¼€å‘的应用通常ä¾èµ–`unistd.h`ã€`sys/socket.h`等头文件åŠå‡½æ•°ï¼Œä½†æ˜¯Visual Studioçš„C库并没有æä¾›è¿™äº›POSIX函数实现,因此这些Linux应用没有办法直接在Windows环境下编译。Cygwin通过å°è£…Windowsæ“作系统原生功能,æä¾›äº†ä¸€ä¸ªPOSIX接å£å±‚,以åŠå°è£…这些功能的动æ€åº“(`cygwin1.dll`),并且æä¾›äº†GCCã€CMake等完整的Linux编译工具链,这æ„å‘³ç€æ ‡å‡†Linux环境下的头文件都存在,并且代ç ä¸­ä¾èµ–GCC编译器的特殊语法都å¯ä»¥è¢«ç¼–译器识别(Visual Studioçš„`cl`编译器ä¸èƒ½å®Œæ•´æ”¯æŒC99语法),因此标准的Linux应用都å¯ä»¥é€šè¿‡Cygwinç§»æ¤åˆ°Windows环境,编译为Windows本地应用。Cygwinæä¾›çš„Linux Shell环境æ„味ç€Shell脚本也是å¯ä»¥ä½¿ç”¨çš„。 - -在Cygwin环境下编译生æˆçš„坿‰§è¡Œç¨‹åºæ˜¯åŽŸç”Ÿçš„Windows程åºï¼Œå’ŒVisual Studio编译的程åºçš„主è¦åŒºåˆ«åœ¨äºŽï¼ŒCygwin下编译的程åºéƒ½å¿…é¡»ä¾èµ–`cygwin1.dll`这个动æ€åº“,因为应用所有的POSIX函数调用都需è¦é€šè¿‡è¿™ä¸ªåЍæ€åº“翻译为Windows本地的系统调用(如WinSock2),因此å‘布Cygwin的程åºä¸å¤ªæ–¹ä¾¿ï¼Œå¿…é¡»è¦åŒ…å«ä¸€ä¸ªè¾ƒå¤§çš„`cygwin1.dll`库文件。å¦å¤–如果应用涉åŠå¤§é‡çš„系统调用,那么通过Cygwin中间层会引入一定的开销,ç†è®ºä¸Šä¼šæ¯”Visual Studio编译的应用效率略低。 - -总的æ¥è¯´ï¼Œå¦‚果你想在Windows环境下快速å°è¯•一下GmSSL的命令行功能,并且å¯èƒ½éœ€è¦åˆ©ç”¨Linux Shell环境下的一些常用工具åšå®žéªŒå’Œæµ‹è¯•,或者ä¸å¤ªç†Ÿæ‚‰Visual Studioå¼€å‘环境,那么采用Cygwin环境是一个éžå¸¸æ–¹ä¾¿çš„选择。 - -### 准备Cygwin环境 - -Cygwin的安装ã€é…置都是通过一个å•一的`setup-x86_64.exe`应用程åºå®Œæˆçš„。在Cygwin的官网 https://www.cygwin.com/ å¯ä»¥ä¸‹è½½è¿™ä¸ªåº”用程åºã€‚ - -注æ„,在首次安装的时候å¯èƒ½æ²¡æœ‰é€‰æ‹©æ‰€æœ‰éœ€è¦çš„程åºï¼Œå†æ¬¡è¿è¡Œ`setup-x86_64.exe`程åºå¯ä»¥å¯¹çŽ¯å¢ƒè¿›è¡Œé…置和更新。有些工具,例如CMake,官方æä¾›äº†ç‹¬ç«‹çš„Windows安装包,在Cygwin环境下没有必è¦ç‹¬ç«‹å®‰è£…这些工具,也ä¸å»ºè®®å®‰è£…,所有ä¾èµ–çš„Linux工具都应该通过Cygwin环境æ¥é…置管ç†ã€‚ - -在安装ã€é…置完æˆä¹‹åŽï¼Œå¯ä»¥é€šè¿‡è¿è¡Œ`Cygwin64 Terminal`应用,打开一个命令行环境。 - -### 在Cygwin环境中编译GmSSL - -Cygwin环境相对标准的Linux环境有一些细微的差别。首先,在Cygwin命令行环境中,文件系统是一个类似Linux文件系统结构的独立目录,如果æºä»£ç å·²ç»ä¸‹è½½åˆ°Windowsæ“作系统中(比如,下载到用户的Download目录),那么需è¦é¦–先将æºä»£ç æ‹·è´åˆ°Cygwin文件系统的用户目录中(例如当å‰ç”¨æˆ·é»˜è®¤ç›®å½•`~`)。在Cygwin文件系统中,Windows文件系统被映射到`/cygdrive`目录中,Windows当å‰ç”¨æˆ·Guan Zhi的下载目录中的`GmSSL-master.zip`文件就被映射到`/cygdrive/c/Users/Guan Zhi/Downloads/GmSSL-master.zip`中。 - -```bash -cp "/cygdrive/c/Users/Guan Zhi/Downloads/GmSSL-master.zip" ~/ -``` - -ç„¶åŽå¯ä»¥æŒ‰ç…§Linux环境下相似的过程编译ã€å®‰è£… - -```bash -unzip GmSSL-master.zip -cd GmSSL-master -mkdir build -cd build -cmake .. -cmake --build . -ctest --output-on-failure -cmake --install . -``` - -注æ„,由于在Cygwin环境中用户本身具有系统æƒé™ï¼Œå› æ­¤åœ¨æ‰§è¡Œ`cmake --install .`æ—¶ä¸éœ€è¦`sudo`。 - -在安装完æˆä¹‹åŽï¼Œå¯ä»¥åœ¨Cygwin的命令行环境下执行`gmssl`命令行,或者è¿è¡Œæºä»£ç `demo`目录下的演示脚本。 - -注æ„,将`gmssl`ç­‰å¯æ‰§è¡Œç¨‹åºç›´æŽ¥ä»ŽCygwin目录拷è´åˆ°Windows文件系统下,在执行时会æç¤ºæ‰¾ä¸åˆ°`cygwin1.dll`的错误,è¿è¡Œæˆ–者å‘å¸ƒå¯æ‰§è¡Œç¨‹åºæ—¶ï¼Œåº”处ç†å¥½å¯¹è¿™ä¸ªåЍæ€åº“çš„ä¾èµ–问题。 - -### 存在的问题 - -Cygwin的动æ€åº“å称比较特殊,是以`cyg`开头的。使用Cygwin生æˆçš„坿‰§è¡Œç¨‹åºå’ŒåЍæ€åº“通常ä¾èµ–`cygwin1.dll`,å‘布给éžCygwin环境使用时需è¦é¢å¤–处ç†è¿™ä¸ªè¿è¡Œæ—¶ä¾èµ–。 - -## é¢å‘iOS/iPhoneOS的交å‰ç¼–译 - -下载 https://github.com/leetal/ios-cmake ,将`ios.toolchain.cmake`文件å¤åˆ¶åˆ°`build`目录。 - -```bash -mkdir build; cd build -cmake .. -G Xcode -DCMAKE_TOOLCHAIN_FILE=../ios.toolchain.cmake -DPLATFORM=OS64 -cmake --build . --config Release -``` - -如果出现“error: Signing for "gmssl" requires a development team.â€é”™è¯¯ï¼Œå¯ä»¥ç”¨Xcode打开工程文件,在Signingé…置中设置Development Team。 - -## é¢å‘Android的交å‰ç¼–译 - -下载Android NDK,执行 - -```bash -mkdir build; cd build -cmake .. -DCMAKE_TOOLCHAIN_FILE=$NDK/build/cmake/android.toolchain.cmake -DANDROID_ABI=arm64-v8a -DANDROID_PLATFORM=android-23 -cmake --build . -``` - -## 安装包构建 - -ä¾èµ–cmake工具包中的cpack工具,生æˆå¯å‘布的安装包。 - -生æˆçš„安装包在`build`目录下。 - -### 构建DEB安装包 - -``` -mkdir build; cd build -cmake .. -DCMAKE_BUILD_TYPE=Release -cmake --build . -cpack -G DEB -``` - -### 构建RPM安装包 - -``` -mkdir build; cd build -cmake .. -DCMAKE_BUILD_TYPE=Release -cmake --build . -cpack -G RPM -``` - -### 构建`.sh`安装脚本 - -``` -mkdir build; cd build -cmake .. -DCMAKE_BUILD_TYPE=Release -cmake --build . -cpack -G STGZ -``` - -## 生æˆäºŒè¿›åˆ¶åŒ… - -为了ä¿è¯å…¼å®¹æ€§ï¼Œå‘布的二进制包ä¸åŒ…å«é’ˆå¯¹ç‰¹å®šæŒ‡ä»¤é›†çš„优化代ç ï¼Œå¹¶ä¸”ä¸å¯ç”¨ç¼–译器的`-O3`优化。 - -在正å¼å‘布之å‰ï¼Œéœ€è¦åœ¨æµ‹è¯•å¹³å°ä¸Šç¼–è¯‘ã€æµ‹è¯•ã€å®‰è£…。验è¯`gmssl`命令行å¯ä»¥æ­£ç¡®ä½¿ç”¨ï¼ŒéªŒè¯`sm3_demo.c`å¯ä»¥æ­£ç¡®å’Œ`-lgmssl`编译,并且å¯ä»¥æ­£ç¡®è¾“出哈希值。 - -完æˆç¼–译和测试åŽï¼Œåœ¨æºç ç›®å½•下执行如下æ“作。二进制包需è¦åŒæ—¶æä¾›åЍæ€åº“å’Œé™æ€åº“,因此使用两个构建目录:先安装动æ€åº“,å†å®‰è£…陿€åº“到åŒä¸€ä¸ªæ‰“包目录。第二次安装会覆盖`bin/gmssl`,使å‘å¸ƒåŒ…ä¸­çš„å‘½ä»¤è¡Œå·¥å…·é“¾æŽ¥é™æ€`libgmssl`ï¼ŒåŒæ—¶`lib`目录中ä¿ç•™åЍæ€åº“å’Œé™æ€åº“。 - -```bash -#!/bin/bash -x -VERSION=3.2.0 -OS=macos -ARCH=arm64 - -PREFIX="$PWD/gmssl-$VERSION" -rm -rf "$PREFIX" build-shared build-static - -cmake -S . -B build-shared -DCMAKE_BUILD_TYPE=Release \ - -DBUILD_SHARED_LIBS=ON \ - -DCMAKE_INSTALL_PREFIX="$PREFIX" -cmake --build build-shared -ctest --test-dir build-shared --output-on-failure -cmake --install build-shared - -cmake -S . -B build-static -DCMAKE_BUILD_TYPE=Release \ - -DBUILD_SHARED_LIBS=OFF \ - -DCMAKE_INSTALL_PREFIX="$PREFIX" -cmake --build build-static -ctest --test-dir build-static --output-on-failure -cmake --install build-static - -tar czvf gmssl-$VERSION-$OS-$ARCH.tar.gz gmssl-$VERSION -``` - -å‘布包目录结构如下: - -```bash -gmssl-$VERSION/ - bin/ - gmssl - include/ - gmssl/ - lib/ - libgmssl.a - libgmssl.so # Linux动æ€åº“ - libgmssl.dylib # macOS动æ€åº“ -``` - -其䏭陿€åº“和动æ€åº“的具体文件åä¼šéšæ“作系统å˜åŒ–。例如Linux通常生æˆ`libgmssl.so`åŠå…¶ç‰ˆæœ¬ç¬¦å·é“¾æŽ¥ï¼ŒmacOS通常生æˆ`libgmssl.dylib`åŠå…¶ç‰ˆæœ¬ç¬¦å·é“¾æŽ¥ï¼ŒWindows通常生æˆ`.dll`和导入库。打包å‰éœ€è¦åœ¨ç›®æ ‡å¹³å°ä¸Šç¡®è®¤`bin/gmssl version`å¯ä»¥è¿è¡Œï¼Œå¹¶ç¡®è®¤ä¸€ä¸ªå¤–部C程åºå¯ä»¥é€šè¿‡`-I/include`å’Œ`-L/lib -lgmssl`完æˆç¼–译和链接。 diff --git a/LICENSE b/LICENSE index d9a10c0d8..464cbd41f 100644 --- a/LICENSE +++ b/LICENSE @@ -1,176 +1,113 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS + LICENSE ISSUES + ============== + + The GmSSL toolkit stays under a double license, i.e. both the conditions of + the GmSSL License and the original OpenSSL license apply to the toolkit. + See below for the actual license texts. + +/* ==================================================================== + * Copyright (c) 2014 - 2018 The GmSSL Project. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the GmSSL Project. + * (http://gmssl.org/)" + * + * 4. The name "GmSSL Project" must not be used to endorse or promote + * products derived from this software without prior written + * permission. For written permission, please contact + * guanzhi1980@gmail.com. + * + * 5. Products derived from this software may not be called "GmSSL" + * nor may "GmSSL" appear in their names without prior written + * permission of the GmSSL Project. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the GmSSL Project + * (http://gmssl.org/)" + * + * THIS SOFTWARE IS PROVIDED BY THE GmSSL PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE GmSSL PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + */ + + OpenSSL License + --------------- + +/* ==================================================================== + * Copyright (c) 1998-2017 The OpenSSL Project. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit. (http://www.openssl.org/)" + * + * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * openssl-core@openssl.org. + * + * 5. Products derived from this software may not be called "OpenSSL" + * nor may "OpenSSL" appear in their names without prior written + * permission of the OpenSSL Project. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit (http://www.openssl.org/)" + * + * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This product includes cryptographic software written by Eric Young + * (eay@cryptsoft.com). This product includes software written by Tim + * Hudson (tjh@cryptsoft.com). + * + */ diff --git a/Makefile.shared b/Makefile.shared new file mode 100644 index 000000000..098e1ec6f --- /dev/null +++ b/Makefile.shared @@ -0,0 +1,622 @@ +# +# Helper makefile to link shared libraries in a portable way. +# This is much simpler than libtool, and hopefully not too error-prone. +# +# The following variables need to be set on the command line to build +# properly + +# CC contains the current compiler. This one MUST be defined +CC=cc +CFLAGS=$(CFLAG) +# LDFLAGS contains flags to be used when temporary object files (when building +# shared libraries) are created, or when an application is linked. +# SHARED_LDFLAGS contains flags to be used when the shared library is created. +LDFLAGS=$(LDFLAG) +SHARED_LDFLAGS=$(SHARED_LDFLAG) + +RC=windres +# SHARED_RCFLAGS are flags used with windres, i.e. when build for Cygwin +# or Mingw. +SHARED_RCFLAGS=$(SHARED_RCFLAG) + +NM=nm + +# LIBNAME contains just the name of the library, without prefix ("lib" +# on Unix, "cyg" for certain forms under Cygwin...) or suffix (.a, .so, +# .dll, ...). This one MUST have a value when using this makefile to +# build shared libraries. +# For example, to build libfoo.so, you need to do the following: +#LIBNAME=foo +LIBNAME= + +# APPNAME contains just the name of the application, without suffix ("" +# on Unix, ".exe" on Windows, ...). This one MUST have a value when using +# this makefile to build applications. +# For example, to build foo, you need to do the following: +#APPNAME=foo +APPNAME= + +# DSTDIR is the directory where the built file should end up in. +DSTDIR=. + +# SRCDIR is the top directory of the source tree. +SRCDIR=. + +# OBJECTS contains all the object files to link together into the application. +# This must contain at least one object file. +#OBJECTS=foo.o +OBJECTS= + +# LIBEXTRAS contains extra modules to link together with the library. +# For example, if a second library, say libbar.a needs to be linked into +# libfoo.so, you need to do the following: +#LIBEXTRAS=libbar.a +# Note that this MUST be used when using the link_dso targets, to hold the +# names of all object files that go into the target shared object. +LIBEXTRAS= + +# LIBVERSION contains the current version of the library. +# For example, to build libfoo.so.1.2, you need to do the following: +#LIBVERSION=1.2 +LIBVERSION= + +# LIBCOMPATVERSIONS contains the compatibility versions (a list) of +# the library. They MUST be in decreasing order. +# For example, if libfoo.so.1.2.1 is backward compatible with libfoo.so.1.2 +# and libfoo.so.1, you need to do the following: +#LIBCOMPATVERSIONS=1.2 1 +# Note that on systems that use sonames, the last number will appear as +# part of it. +# It's also possible, for systems that support it (Tru64, for example), +# to add extra compatibility info with more precision, by adding a second +# list of versions, separated from the first with a semicolon, like this: +#LIBCOMPATVERSIONS=1.2 1;1.2.0 1.1.2 1.1.1 1.1.0 1.0.0 +LIBCOMPATVERSIONS= + +# LIBDEPS contains all the flags necessary to cover all necessary +# dependencies to other libraries. +LIBDEPS= + +#------------------------------------------------------------------------------ +# The rest is private to this makefile. + +SET_X=: +#SET_X=set -x + +top: + echo "Trying to use this makefile interactively? Don't." + +CALC_VERSIONS= \ + SHLIB_COMPAT=; SHLIB_SOVER=; \ + if [ -n "$(LIBVERSION)$(LIBCOMPATVERSIONS)" ]; then \ + prev=""; \ + for v in `echo "$(LIBVERSION) $(LIBCOMPATVERSIONS)" | cut -d';' -f1`; do \ + SHLIB_SOVER_NODOT=$$v; \ + SHLIB_SOVER=.$$v; \ + if [ -n "$$prev" ]; then \ + SHLIB_COMPAT="$$SHLIB_COMPAT .$$prev"; \ + fi; \ + prev=$$v; \ + done; \ + fi + +LINK_APP= \ + ( $(SET_X); \ + LIBDEPS="$${LIBDEPS:-$(LIBDEPS)}"; \ + LDCMD="$${LDCMD:-$(CC)}"; LDFLAGS="$${LDFLAGS:-$(CFLAGS) $(LDFLAGS)}"; \ + LIBPATH=`for x in $$LIBDEPS; do echo $$x; done | sed -e 's/^ *-L//;t' -e d | uniq`; \ + LIBPATH=`echo $$LIBPATH | sed -e 's/ /:/g'`; \ + echo LD_LIBRARY_PATH=$$LIBPATH:$$LD_LIBRARY_PATH \ + $${LDCMD} $${LDFLAGS} -o $${APPNAME:=$(APPNAME)} $(OBJECTS) $${LIBDEPS}; \ + LD_LIBRARY_PATH=$$LIBPATH:$$LD_LIBRARY_PATH \ + $${LDCMD} $${LDFLAGS} -o $${APPNAME:=$(APPNAME)} $(OBJECTS) $${LIBDEPS} ) + +LINK_SO= \ + ( $(SET_X); \ + LIBDEPS="$${LIBDEPS:-$(LIBDEPS)}"; \ + SHAREDCMD="$${SHAREDCMD:-$(CC)}"; \ + SHAREDFLAGS="$${SHAREDFLAGS:-$(CFLAGS) $(SHARED_LDFLAGS)}"; \ + LIBPATH=`for x in $$LIBDEPS; do echo $$x; done | sed -e 's/^ *-L//;t' -e d | uniq`; \ + LIBPATH=`echo $$LIBPATH | sed -e 's/ /:/g'`; \ + echo LD_LIBRARY_PATH=$$LIBPATH:$$LD_LIBRARY_PATH \ + $${SHAREDCMD} $${SHAREDFLAGS} \ + -o $(DSTDIR)/$$SHLIB$$SHLIB_SOVER$$SHLIB_SUFFIX \ + $$ALLSYMSFLAGS $$SHOBJECTS $$NOALLSYMSFLAGS $$LIBDEPS; \ + LD_LIBRARY_PATH=$$LIBPATH:$$LD_LIBRARY_PATH \ + $${SHAREDCMD} $${SHAREDFLAGS} \ + -o $(DSTDIR)/$$SHLIB$$SHLIB_SOVER$$SHLIB_SUFFIX \ + $$ALLSYMSFLAGS $$SHOBJECTS $$NOALLSYMSFLAGS $$LIBDEPS \ + ) && $(SYMLINK_SO) + +SYMLINK_SO= \ + if [ -n "$$INHIBIT_SYMLINKS" ]; then :; else \ + prev=$$SHLIB$$SHLIB_SOVER$$SHLIB_SUFFIX; \ + if [ -n "$$SHLIB_COMPAT" ]; then \ + for x in $$SHLIB_COMPAT; do \ + ( $(SET_X); rm -f $(DSTDIR)/$$SHLIB$$x$$SHLIB_SUFFIX; \ + ln -s $$prev $(DSTDIR)/$$SHLIB$$x$$SHLIB_SUFFIX ); \ + prev=$$SHLIB$$x$$SHLIB_SUFFIX; \ + done; \ + fi; \ + if [ -n "$$SHLIB_SOVER" ]; then \ + ( $(SET_X); rm -f $(DSTDIR)/$$SHLIB$$SHLIB_SUFFIX; \ + ln -s $$prev $(DSTDIR)/$$SHLIB$$SHLIB_SUFFIX ); \ + fi; \ + fi + +LINK_SO_SHLIB= SHOBJECTS="$(DSTDIR)/lib$(LIBNAME).a $(LIBEXTRAS)"; $(LINK_SO) +LINK_SO_DSO= INHIBIT_SYMLINKS=yes; SHOBJECTS="$(LIBEXTRAS)"; $(LINK_SO) + +LINK_SO_SHLIB_VIA_O= \ + SHOBJECTS=$(DSTDIR)/lib$(LIBNAME).o; \ + ALL=$$ALLSYMSFLAGS; ALLSYMSFLAGS=; NOALLSYMSFLAGS=; \ + ( echo ld $(LDFLAGS) -r -o $$SHOBJECTS $$ALL lib$(LIBNAME).a $(LIBEXTRAS); \ + ld $(LDFLAGS) -r -o $$SHOBJECTS $$ALL $(DSTDIR)/lib$(LIBNAME).a $(LIBEXTRAS) ); \ + $(LINK_SO) && ( echo rm -f $$SHOBJECTS; rm -f $$SHOBJECTS ) + +LINK_SO_SHLIB_UNPACKED= \ + UNPACKDIR=link_tmp.$$$$; rm -rf $$UNPACKDIR; mkdir $$UNPACKDIR; \ + (cd $$UNPACKDIR; ar x ../$(DSTDIR)/lib$(LIBNAME).a) && \ + ([ -z "$(LIBEXTRAS)" ] || cp $(LIBEXTRAS) $$UNPACKDIR) && \ + SHOBJECTS=$$UNPACKDIR/*.o; \ + $(LINK_SO) && rm -rf $$UNPACKDIR + +DETECT_GNU_LD=($(CC) -Wl,-V /dev/null 2>&1 | grep '^GNU ld' )>/dev/null + +DO_GNU_SO_COMMON=\ + SHAREDFLAGS="$(CFLAGS) $(SHARED_LDFLAGS) -shared -Wl,-Bsymbolic -Wl,-soname=$$SHLIB$$SHLIB_SOVER$$SHLIB_SUFFIX" +DO_GNU_DSO=\ + SHLIB=$(LIBNAME).so; \ + SHLIB_SOVER=; \ + SHLIB_SUFFIX=; \ + $(DO_GNU_SO_COMMON) +DO_GNU_SO=\ + $(CALC_VERSIONS); \ + SHLIB=lib$(LIBNAME).so; \ + ALLSYMSFLAGS='-Wl,--whole-archive'; \ + NOALLSYMSFLAGS='-Wl,--no-whole-archive'; \ + $(DO_GNU_SO_COMMON) +DO_GNU_APP=LDFLAGS="$(CFLAGS) $(LDFLAGS)" + +#This is rather special. It's a special target with which one can link +#applications without bothering with any features that have anything to +#do with shared libraries, for example when linking against static +#libraries. It's mostly here to avoid a lot of conditionals everywhere +#else... +link_app.: + $(LINK_APP) + +link_dso.gnu: + @ $(DO_GNU_DSO); $(LINK_SO_DSO) +link_shlib.gnu: + @ $(DO_GNU_SO); $(LINK_SO_SHLIB) +link_app.gnu: + @ $(DO_GNU_APP); $(LINK_APP) + +link_shlib.linux-shared: + @$(PERL) $(SRCDIR)/util/mkdef.pl $(LIBNAME) linux >$(LIBNAME).map; \ + $(DO_GNU_SO); \ + ALLSYMSFLAGS='-Wl,--whole-archive,--version-script=$(LIBNAME).map'; \ + $(LINK_SO_SHLIB) + +link_dso.bsd: + @if $(DETECT_GNU_LD); then $(DO_GNU_DSO); else \ + SHLIB=$(LIBNAME).so; \ + SHLIB_SUFFIX=; \ + LIBDEPS=" "; \ + ALLSYMSFLAGS=; \ + NOALLSYMSFLAGS=; \ + SHAREDFLAGS="$(CFLAGS) $(SHARED_LDFLAGS) -shared -nostdlib"; \ + fi; $(LINK_SO_DSO) +link_shlib.bsd: + @if $(DETECT_GNU_LD); then $(DO_GNU_SO); else \ + $(CALC_VERSIONS); \ + SHLIB=lib$(LIBNAME).so; \ + SHLIB_SUFFIX=; \ + LIBDEPS=" "; \ + ALLSYMSFLAGS="-Wl,-Bforcearchive"; \ + NOALLSYMSFLAGS=; \ + SHAREDFLAGS="$(CFLAGS) $(SHARED_LDFLAGS) -shared -nostdlib"; \ + fi; $(LINK_SO_SHLIB) +link_app.bsd: + @if $(DETECT_GNU_LD); then $(DO_GNU_APP); else \ + LDFLAGS="$(CFLAGS) $(LDFLAGS)"; \ + fi; $(LINK_APP) + +# For Darwin AKA Mac OS/X (dyld) +# Originally link_dso.darwin produced .so, because it was hard-coded +# in dso_dlfcn module. At later point dso_dlfcn switched to .dylib +# extension in order to allow for run-time linking with vendor- +# supplied shared libraries such as libz, so that link_dso.darwin had +# to be harmonized with it. This caused minor controversy, because +# it was believed that dlopen can't be used to dynamically load +# .dylib-s, only so called bundle modules (ones linked with -bundle +# flag). The belief seems to be originating from pre-10.4 release, +# where dlfcn functionality was emulated by dlcompat add-on. In +# 10.4 dlopen was rewritten as native part of dyld and is documented +# to be capable of loading both dynamic libraries and bundles. In +# order to provide compatibility with pre-10.4 dlopen, modules are +# linked with -bundle flag, which makes .dylib extension misleading. +# It works, because dlopen is [and always was] extension-agnostic. +# Alternative to this heuristic approach is to develop specific +# MacOS X dso module relying on whichever "native" dyld interface. +link_dso.darwin: + @ SHLIB=$(LIBNAME); \ + SHLIB_SUFFIX=.dylib; \ + ALLSYMSFLAGS=''; \ + NOALLSYMSFLAGS=''; \ + SHAREDFLAGS="$(CFLAGS) `echo $(SHARED_LDFLAGS) | sed s/dynamiclib/bundle/`"; \ + $(LINK_SO_DSO) +link_shlib.darwin: + @ $(CALC_VERSIONS); \ + SHLIB=lib$(LIBNAME); \ + SHLIB_SUFFIX=.dylib; \ + ALLSYMSFLAGS='-all_load'; \ + NOALLSYMSFLAGS=''; \ + SHAREDFLAGS="$(CFLAGS) $(SHARED_LDFLAGS)"; \ + if [ -n "$(LIBVERSION)" ]; then \ + SHAREDFLAGS="$$SHAREDFLAGS -current_version $(LIBVERSION)"; \ + fi; \ + if [ -n "$$SHLIB_SOVER_NODOT" ]; then \ + SHAREDFLAGS="$$SHAREDFLAGS -compatibility_version $$SHLIB_SOVER_NODOT"; \ + fi; \ + SHAREDFLAGS="$$SHAREDFLAGS -install_name $(INSTALLTOP)/$(LIBDIR)/$$SHLIB$(SHLIB_EXT)"; \ + $(LINK_SO_SHLIB) +link_app.darwin: # is there run-path on darwin? + $(LINK_APP) + +link_dso.cygwin: + @SHLIB=$(LIBNAME); \ + SHLIB_SUFFIX=.dll; \ + ALLSYMSFLAGS=''; \ + NOALLSYMSFLAGS=''; \ + base=-Wl,--enable-auto-image-base; \ + SHAREDFLAGS="$(CFLAGS) $(SHARED_LDFLAGS) -shared $$base -Wl,-Bsymbolic"; \ + $(LINK_SO_DSO) +link_shlib.cygwin: + @ $(CALC_VERSIONS); \ + INHIBIT_SYMLINKS=yes; \ + SHLIB=cyg$(LIBNAME); SHLIB_SOVER=-$(LIBVERSION); SHLIB_SUFFIX=.dll; \ + dll_name=$$SHLIB$$SHLIB_SOVER$$SHLIB_SUFFIX; \ + echo "$(PERL) $(SRCDIR)/util/mkrc.pl $$dll_name |" \ + "$(RC) $(SHARED_RCFLAGS) -o rc.o"; \ + $(PERL) $(SRCDIR)/util/mkrc.pl $$dll_name | \ + $(RC) $(SHARED_RCFLAGS) -o rc.o; \ + ALLSYMSFLAGS='-Wl,--whole-archive'; \ + NOALLSYMSFLAGS='-Wl,--no-whole-archive'; \ + SHAREDFLAGS="$(CFLAGS) $(SHARED_LDFLAGS) -shared -Wl,--enable-auto-image-base -Wl,-Bsymbolic -Wl,--out-implib,lib$(LIBNAME).dll.a rc.o"; \ + $(LINK_SO_SHLIB) || exit 1; \ + rm rc.o +link_app.cygwin: + $(LINK_APP) + +# link_dso.mingw-shared and link_app.mingw-shared are mapped to the +# corresponding cygwin targets, as they do the exact same thing. +link_shlib.mingw: + @ $(CALC_VERSIONS); \ + INHIBIT_SYMLINKS=yes; \ + arch=; \ + if expr $(PLATFORM) : mingw64 > /dev/null; then arch=-x64; fi; \ + sover=`echo $(LIBVERSION) | sed -e 's/\./_/g'` ; \ + SHLIB=lib$(LIBNAME); \ + SHLIB_SOVER=-$$sover$$arch; \ + SHLIB_SUFFIX=.dll; \ + dll_name=$$SHLIB$$SHLIB_SOVER$$SHLIB_SUFFIX; \ + base=; [ $(LIBNAME) = "crypto" -a -n "$(FIPSCANLIB)" ] && base=-Wl,--image-base,0x63000000; \ + $(PERL) $(SRCDIR)/util/mkdef.pl 32 $(LIBNAME) \ + | sed -e 's|^\(LIBRARY *\)$(LIBNAME)32|\1'"$$dll_name"'|' \ + > $(LIBNAME).def; \ + echo "$(PERL) $(SRCDIR)/util/mkrc.pl $$dll_name |" \ + "$(RC) $(SHARED_RCFLAGS) -o rc.o"; \ + $(PERL) $(SRCDIR)/util/mkrc.pl $$dll_name | \ + $(RC) $(SHARED_RCFLAGS) -o rc.o; \ + ALLSYMSFLAGS='-Wl,--whole-archive'; \ + NOALLSYMSFLAGS='-Wl,--no-whole-archive'; \ + SHAREDFLAGS="$(CFLAGS) $(SHARED_LDFLAGS) -shared $$base -Wl,-Bsymbolic -Wl,--out-implib,lib$(LIBNAME).dll.a $(LIBNAME).def rc.o"; \ + $(LINK_SO_SHLIB) || exit 1; \ + rm $(LIBNAME).def rc.o + +link_dso.alpha-osf1: + @ if $(DETECT_GNU_LD); then \ + $(DO_GNU_DSO); \ + else \ + SHLIB=$(LIBNAME).so; \ + SHLIB_SUFFIX=; \ + ALLSYMSFLAGS=''; \ + NOALLSYMSFLAGS=''; \ + SHAREDFLAGS="$(CFLAGS) $(SHARED_LDFLAGS) -shared -Wl,-B,symbolic"; \ + fi; \ + $(LINK_SO_DSO) +link_shlib.alpha-osf1: + @ if $(DETECT_GNU_LD); then \ + $(DO_GNU_SO); \ + else \ + SHLIB=lib$(LIBNAME).so; \ + SHLIB_SUFFIX=; \ + SHLIB_HIST=`echo "$(LIBCOMPATVERSIONS)" | cut -d';' -f2 | sed -e 's/ */:/'`; \ + if [ -n "$$SHLIB_HIST" ]; then \ + SHLIB_HIST="$${SHLIB_HIST}:$(LIBVERSION)"; \ + else \ + SHLIB_HIST="$(LIBVERSION)"; \ + fi; \ + SHLIB_SOVER=; \ + ALLSYMSFLAGS='-all'; \ + NOALLSYMSFLAGS='-none'; \ + SHAREDFLAGS="$(CFLAGS) $(SHARED_LDFLAGS) -shared -Wl,-B,symbolic"; \ + if [ -n "$$SHLIB_HIST" ]; then \ + SHAREDFLAGS="$$SHAREDFLAGS -set_version $$SHLIB_HIST"; \ + fi; \ + fi; \ + $(LINK_SO_SHLIB) +link_app.alpha-osf1: + @if $(DETECT_GNU_LD); then \ + $(DO_GNU_APP); \ + else \ + LDFLAGS="$(CFLAGS) $(LDFLAGS)"; \ + fi; \ + $(LINK_APP) + +link_dso.solaris: + @ if $(DETECT_GNU_LD); then \ + $(DO_GNU_DSO); \ + else \ + $(CALC_VERSIONS); \ + SHLIB=$(LIBNAME).so; \ + SHLIB_SUFFIX=; \ + ALLSYMSFLAGS=""; \ + NOALLSYMSFLAGS=""; \ + SHAREDFLAGS="$(CFLAGS) $(SHARED_LDFLAGS) -h $$SHLIB$$SHLIB_SOVER$$SHLIB_SUFFIX -Wl,-Bsymbolic"; \ + fi; \ + $(LINK_SO_DSO) +link_shlib.solaris: + @ if $(DETECT_GNU_LD); then \ + $(DO_GNU_SO); \ + else \ + $(CALC_VERSIONS); \ + SHLIB=lib$(LIBNAME).so; \ + SHLIB_SUFFIX=;\ + $(PERL) $(SRCDIR)/util/mkdef.pl $(LIBNAME) linux >$(LIBNAME).map; \ + ALLSYMSFLAGS="-Wl,-z,allextract,-M,$(LIBNAME).map"; \ + NOALLSYMSFLAGS="-Wl,-z,defaultextract"; \ + SHAREDFLAGS="$(CFLAGS) $(SHARED_LDFLAGS) -h $$SHLIB$$SHLIB_SOVER$$SHLIB_SUFFIX -Wl,-Bsymbolic"; \ + fi; \ + $(LINK_SO_SHLIB) +link_app.solaris: + @ if $(DETECT_GNU_LD); then \ + $(DO_GNU_APP); \ + else \ + LDFLAGS="$(CFLAGS) $(LDFLAGS)"; \ + fi; \ + $(LINK_APP) + +# OpenServer 5 native compilers used +link_dso.svr3: + @ if $(DETECT_GNU_LD); then \ + $(DO_GNU_DSO); \ + else \ + $(CALC_VERSIONS); \ + SHLIB=$(LIBNAME).so; \ + SHLIB_SUFFIX=; \ + ALLSYMSFLAGS=''; \ + NOALLSYMSFLAGS=''; \ + SHAREDFLAGS="$(CFLAGS) -G -h $$SHLIB$$SHLIB_SUFFIX"; \ + fi; \ + $(LINK_SO_DSO) +link_shlib.svr3: + @ if $(DETECT_GNU_LD); then \ + $(DO_GNU_SO); \ + else \ + $(CALC_VERSIONS); \ + SHLIB=lib$(LIBNAME).so; \ + SHLIB_SUFFIX=; \ + ALLSYMSFLAGS=''; \ + NOALLSYMSFLAGS=''; \ + SHAREDFLAGS="$(CFLAGS) -G -h $$SHLIB$$SHLIB_SOVER$$SHLIB_SUFFIX"; \ + fi; \ + $(LINK_SO_SHLIB_UNPACKED) +link_app.svr3: + @$(DETECT_GNU_LD) && $(DO_GNU_APP); \ + $(LINK_APP) + +# UnixWare 7 and OpenUNIX 8 native compilers used +link_dso.svr5: + @ if $(DETECT_GNU_LD); then \ + $(DO_GNU_DSO); \ + else \ + SHARE_FLAG='-G'; \ + ($(CC) -v 2>&1 | grep gcc) > /dev/null && SHARE_FLAG='-shared'; \ + SHLIB=$(LIBNAME).so; \ + SHLIB_SUFFIX=; \ + ALLSYMSFLAGS=''; \ + NOALLSYMSFLAGS=''; \ + SHAREDFLAGS="$(CFLAGS) $${SHARE_FLAG} -h $$SHLIB$$SHLIB_SUFFIX"; \ + fi; \ + $(LINK_SO_DSO) +link_shlib.svr5: + @ if $(DETECT_GNU_LD); then \ + $(DO_GNU_SO); \ + else \ + $(CALC_VERSIONS); \ + SHARE_FLAG='-G'; \ + ($(CC) -v 2>&1 | grep gcc) > /dev/null && SHARE_FLAG='-shared'; \ + SHLIB=lib$(LIBNAME).so; \ + SHLIB_SUFFIX=; \ + ALLSYMSFLAGS=''; \ + NOALLSYMSFLAGS=''; \ + SHAREDFLAGS="$(CFLAGS) $${SHARE_FLAG} -h $$SHLIB$$SHLIB_SOVER$$SHLIB_SUFFIX"; \ + fi; \ + $(LINK_SO_SHLIB_UNPACKED) +link_app.svr5: + @$(DETECT_GNU_LD) && $(DO_GNU_APP); \ + $(LINK_APP) + +link_dso.irix: + @ if $(DETECT_GNU_LD); then \ + $(DO_GNU_DSO); \ + else \ + SHLIB=$(LIBNAME).so; \ + SHLIB_SUFFIX=; \ + ALLSYMSFLAGS=""; \ + NOALLSYMSFLAGS=""; \ + SHAREDFLAGS="$(CFLAGS) $(SHARED_LDFLAGS) -shared -Wl,-soname,$$SHLIB$$SHLIB_SUFFIX,-B,symbolic"; \ + fi; \ + $(LINK_SO_DSO) +link_shlib.irix: + @ if $(DETECT_GNU_LD); then \ + $(DO_GNU_SO); \ + else \ + $(CALC_VERSIONS); \ + SHLIB=lib$(LIBNAME).so; \ + SHLIB_SUFFIX=; \ + MINUSWL=""; \ + ($(CC) -v 2>&1 | grep gcc) > /dev/null && MINUSWL="-Wl,"; \ + ALLSYMSFLAGS="$${MINUSWL}-all"; \ + NOALLSYMSFLAGS="$${MINUSWL}-none"; \ + SHAREDFLAGS="$(CFLAGS) $(SHARED_LDFLAGS) -shared -Wl,-soname,$$SHLIB$$SHLIB_SOVER$$SHLIB_SUFFIX,-B,symbolic"; \ + fi; \ + $(LINK_SO_SHLIB) +link_app.irix: + @LDFLAGS="$(CFLAGS) $(LDFLAGS)"; \ + $(LINK_APP) + +# 32-bit PA-RISC HP-UX embeds the -L pathname of libs we link with, so +# we compensate for it with +cdp ../: and +cdp ./:. Yes, these rewrite +# rules imply that we can only link one level down in catalog structure, +# but that's what takes place for the moment of this writing. +cdp option +# was introduced in HP-UX 11.x and applies in 32-bit PA-RISC link +# editor context only [it's simply ignored in other cases, which are all +# ELFs by the way]. +# +link_dso.hpux: + @if $(DETECT_GNU_LD); then $(DO_GNU_DSO); else \ + SHLIB=$(LIBNAME).sl; \ + expr "$(CFLAGS)" : '.*DSO_DLFCN' > /dev/null && SHLIB=$(LIBNAME).so; \ + SHLIB_SUFFIX=; \ + ALLSYMSFLAGS=''; \ + NOALLSYMSFLAGS=''; \ + expr $(PLATFORM) : 'hpux64' > /dev/null && ALLSYMSFLAGS='-Wl,+forceload'; \ + SHAREDFLAGS="$(CFLAGS) $(SHARED_LDFLAGS) -Wl,-B,symbolic,+vnocompatwarnings,-z,+s,+h,$$SHLIB$$SHLIB_SUFFIX,+cdp,../:,+cdp,./:"; \ + fi; \ + rm -f $(DSTDIR)/$$SHLIB$$SHLIB_SUFFIX || :; \ + $(LINK_SO_DSO) && chmod a=rx $(DSTDIR)/$$SHLIB$$SHLIB_SUFFIX +link_shlib.hpux: + @if $(DETECT_GNU_LD); then $(DO_GNU_SO); else \ + $(CALC_VERSIONS); \ + SHLIB=lib$(LIBNAME).sl; \ + expr $(PLATFORM) : '.*ia64' > /dev/null && SHLIB=lib$(LIBNAME).so; \ + SHLIB_SUFFIX=; \ + ALLSYMSFLAGS='-Wl,-Fl'; \ + NOALLSYMSFLAGS=''; \ + expr $(PLATFORM) : 'hpux64' > /dev/null && ALLSYMSFLAGS='-Wl,+forceload'; \ + SHAREDFLAGS="$(CFLAGS) $(SHARED_LDFLAGS) -Wl,-B,symbolic,+vnocompatwarnings,-z,+s,+h,$$SHLIB$$SHLIB_SOVER$$SHLIB_SUFFIX,+cdp,../:,+cdp,./:"; \ + fi; \ + rm -f $(DSTDIR)/$$SHLIB$$SHLIB_SOVER$$SHLIB_SUFFIX || :; \ + $(LINK_SO_SHLIB) && chmod a=rx $(DSTDIR)/$$SHLIB$$SHLIB_SOVER$$SHLIB_SUFFIX +link_app.hpux: + @if $(DETECT_GNU_LD); then $(DO_GNU_APP); else \ + LDFLAGS="$(CFLAGS) $(LDFLAGS) -Wl,+s,+cdp,../:,+cdp,./:"; \ + fi; \ + $(LINK_APP) + +link_dso.aix: + @OBJECT_MODE=`expr "x$(SHARED_LDFLAGS)" : 'x\-[a-z]*\(64\)'` || :; \ + OBJECT_MODE=$${OBJECT_MODE:-32}; export OBJECT_MODE; \ + SHLIB=$(LIBNAME).so; \ + SHLIB_SUFFIX=; \ + ALLSYMSFLAGS=''; \ + NOALLSYMSFLAGS=''; \ + SHAREDFLAGS='$(CFLAGS) $(SHARED_LDFLAGS) -Wl,-bexpall,-bnolibpath,-bM:SRE'; \ + rm -f $(DSTDIR)/$$SHLIB$$SHLIB_SOVER 2>&1 > /dev/null ; \ + $(LINK_SO_DSO); +link_shlib.aix: + @ $(CALC_VERSIONS); \ + OBJECT_MODE=`expr "x$(SHARED_LDFLAGS)" : 'x\-[a-z]*\(64\)'` || : ; \ + OBJECT_MODE=$${OBJECT_MODE:-32}; export OBJECT_MODE; \ + SHLIB=lib$(LIBNAME).so; \ + SHLIB_SUFFIX=; \ + ALLSYMSFLAGS='-bnogc'; \ + NOALLSYMSFLAGS=''; \ + SHAREDFLAGS='$(CFLAGS) $(SHARED_LDFLAGS) -Wl,-bexpall,-bnolibpath,-bM:SRE'; \ + rm -f $(DSTDIR)/$$SHLIB$$SHLIB_SOVER 2>&1 > /dev/null ; \ + $(LINK_SO_SHLIB_VIA_O) +link_app.aix: + LDFLAGS="$(CFLAGS) -Wl,-bsvr4 $(LDFLAGS)"; \ + $(LINK_APP) + + +# Targets to build symbolic links when needed +symlink.gnu symlink.solaris symlink.svr3 symlink.svr5 symlink.irix \ +symlink.aix: + @ $(CALC_VERSIONS); \ + SHLIB=lib$(LIBNAME).so; \ + $(SYMLINK_SO) +symlink.darwin: + @ $(CALC_VERSIONS); \ + SHLIB=lib$(LIBNAME); \ + SHLIB_SUFFIX=.dylib; \ + $(SYMLINK_SO) +symlink.hpux: + @ $(CALC_VERSIONS); \ + SHLIB=lib$(LIBNAME).sl; \ + expr $(PLATFORM) : '.*ia64' > /dev/null && SHLIB=lib$(LIBNAME).so; \ + $(SYMLINK_SO) +# The following lines means those specific architectures do no symlinks +symlink.cygwin symlink.alpha-osf1 symlink.tru64 symlink.tru64-rpath: + +# Compatibility targets +link_dso.bsd-gcc-shared link_dso.linux-shared link_dso.gnu-shared: link_dso.gnu +link_shlib.bsd-gcc-shared: link_shlib.linux-shared +link_shlib.gnu-shared: link_shlib.gnu +link_app.bsd-gcc-shared link_app.linux-shared link_app.gnu-shared: link_app.gnu +symlink.bsd-gcc-shared symlink.bsd-shared symlink.linux-shared symlink.gnu-shared: symlink.gnu +link_dso.bsd-shared: link_dso.bsd +link_shlib.bsd-shared: link_shlib.bsd +link_app.bsd-shared: link_app.bsd +link_dso.darwin-shared: link_dso.darwin +link_shlib.darwin-shared: link_shlib.darwin +link_app.darwin-shared: link_app.darwin +symlink.darwin-shared: symlink.darwin +link_dso.cygwin-shared: link_dso.cygwin +link_shlib.cygwin-shared: link_shlib.cygwin +link_app.cygwin-shared: link_app.cygwin +symlink.cygwin-shared: symlink.cygwin +link_dso.mingw-shared: link_dso.cygwin +link_shlib.mingw-shared: link_shlib.mingw +link_app.mingw-shared: link_app.cygwin +symlink.mingw-shared: symlink.cygwin +link_dso.alpha-osf1-shared: link_dso.alpha-osf1 +link_shlib.alpha-osf1-shared: link_shlib.alpha-osf1 +link_app.alpha-osf1-shared: link_app.alpha-osf1 +symlink.alpha-osf1-shared: symlink.alpha-osf1 +link_dso.tru64-shared: link_dso.tru64 +link_shlib.tru64-shared: link_shlib.tru64 +link_app.tru64-shared: link_app.tru64 +symlink.tru64-shared: symlink.tru64 +link_dso.tru64-shared-rpath: link_dso.tru64-rpath +link_shlib.tru64-shared-rpath: link_shlib.tru64-rpath +link_app.tru64-shared-rpath: link_app.tru64-rpath +symlink.tru64-shared-rpath: symlink.tru64-rpath +link_dso.solaris-shared: link_dso.solaris +link_shlib.solaris-shared: link_shlib.solaris +link_app.solaris-shared: link_app.solaris +symlink.solaris-shared: symlink.solaris +link_dso.svr3-shared: link_dso.svr3 +link_shlib.svr3-shared: link_shlib.svr3 +link_app.svr3-shared: link_app.svr3 +symlink.svr3-shared: symlink.svr3 +link_dso.svr5-shared: link_dso.svr5 +link_shlib.svr5-shared: link_shlib.svr5 +link_app.svr5-shared: link_app.svr5 +symlink.svr5-shared: symlink.svr5 +link_dso.irix-shared: link_dso.irix +link_shlib.irix-shared: link_shlib.irix +link_app.irix-shared: link_app.irix +symlink.irix-shared: symlink.irix +link_dso.hpux-shared: link_dso.hpux +link_shlib.hpux-shared: link_shlib.hpux +link_app.hpux-shared: link_app.hpux +symlink.hpux-shared: symlink.hpux +link_dso.aix-shared: link_dso.aix +link_shlib.aix-shared: link_shlib.aix +link_app.aix-shared: link_app.aix +symlink.aix-shared: symlink.aix diff --git a/README.md b/README.md index 1cf824ef5..69cb89467 100644 --- a/README.md +++ b/README.md @@ -1,175 +1,181 @@ -# GmSSL +## About GmSSL -[![CMake-Ubuntu/macOS](https://github.com/guanzhi/GmSSL/workflows/CMake/badge.svg)](https://github.com/guanzhi/GmSSL/actions/workflows/cmake.yml) -[![CMake-Windows](https://github.com/guanzhi/GmSSL/workflows/CMake-windows/badge.svg)](https://github.com/guanzhi/GmSSL/actions/workflows/cmake-windows.yml) -[![CMake-Android](https://github.com/guanzhi/GmSSL/actions/workflows/android-ci.yml/badge.svg)](https://github.com/guanzhi/GmSSL/actions/workflows/android-ci.yml) -[![CMake-OpenHarmony](https://github.com/guanzhi/GmSSL/actions/workflows/ohos.yml/badge.svg)](https://github.com/guanzhi/GmSSL/actions/workflows/ohos.yml) -[![CMake-iOS](https://github.com/guanzhi/GmSSL/actions/workflows/ios.yml/badge.svg)](https://github.com/guanzhi/GmSSL/actions/workflows/ios.yml) +[![Build Status](https://travis-ci.org/guanzhi/GmSSL.svg?branch=master)](https://travis-ci.org/guanzhi/GmSSL) +[![Build status](https://ci.appveyor.com/api/projects/status/8frwxuwaj4695grq/branch/master?svg=true)](https://ci.appveyor.com/project/zhaoxiaomeng/gmssl/branch/master) -GmSSL是由北京大学自主开å‘的国产商用密ç å¼€æºåº“ï¼Œå®žçŽ°äº†å¯¹å›½å¯†ç®—æ³•ã€æ ‡å‡†å’Œå®‰å…¨é€šä¿¡å议的全é¢åŠŸèƒ½è¦†ç›–ï¼Œæ”¯æŒåŒ…æ‹¬ç§»åŠ¨ç«¯åœ¨å†…çš„ä¸»æµæ“作系统和处ç†å™¨ï¼Œæ”¯æŒå¯†ç é’¥åŒ™ã€å¯†ç å¡ç­‰å…¸åž‹å›½äº§å¯†ç ç¡¬ä»¶ï¼Œæä¾›åŠŸèƒ½ä¸°å¯Œçš„å‘½ä»¤è¡Œå·¥å…·åŠå¤šç§ç¼–译语言编程接å£ã€‚ +GmSSL is an open source cryptographic toolkit that provide first level support of Chinese national cryptographic algorithms and protocols which are specified in the GM/T serial standards. As a branch of the OpenSSL project, GmSSL provides API level compatibility with OpenSSL and maintains all the functionalities. Existing projects such as Apache web server can be easily ported to GmSSL with minor modification and a simple rebuild. Since the first release in late 2014, GmSSL has been selected as one of the six recommended cryptographic projects by Open Source China and the winner of the 2015 Chinese Linux Software Award. -## 主è¦ç‰¹æ€§ +## Features -* è¶…è½»é‡ï¼šGmSSL 3 大幅度é™ä½Žäº†å†…存需求和二进制代ç ä½“积,ä¸ä¾èµ–动æ€å†…存,å¯ä»¥ç”¨äºŽæ— æ“作系统的低功耗嵌入å¼çŽ¯å¢ƒ(MCUã€SOCç­‰),开å‘者也å¯ä»¥æ›´å®¹æ˜“地将国密算法和SSLå议嵌入到现有的项目中。 -* æ›´åˆè§„:GmSSL 3 å¯ä»¥é…置为仅包å«å›½å¯†ç®—法和国密åè®®(TLCPåè®®),ä¾èµ–GmSSL 的密ç åº”用更容易满足密ç äº§å“åž‹å·æ£€æµ‹çš„è¦æ±‚,é¿å…由于混æ‚éžå›½å¯†ç®—法ã€ä¸å®‰å…¨ç®—法等导致的安全问题和åˆè§„问题。 -* 更安全:TLS 1.3在安全性和通信延迟上相对之å‰çš„TLSå议有巨大的æå‡ï¼ŒGmSSL 3 支æŒTLS 1.3å议和RFC 8998的国密套件。GmSSL 3 默认支æŒå¯†é’¥çš„åŠ å¯†ä¿æŠ¤ï¼Œæå‡äº†å¯†ç ç®—æ³•çš„æŠ—ä¾§ä¿¡é“æ”»å‡»èƒ½åŠ›ã€‚ -* 跨平å°ï¼šGmSSL 3 更容易跨平å°ï¼Œæž„建系统ä¸å†ä¾èµ–Perl,默认的CMake构建系统å¯ä»¥å®¹æ˜“地和Visual Studioã€Android NDK等默认编译工具é…åˆä½¿ç”¨ï¼Œå¼€å‘者也å¯ä»¥æ‰‹å·¥ç¼–写Makefile在特殊环境中编译ã€å‰ªè£ã€‚ + - Support [Chinese GM/T cryptographic standards](http://gmssl.org/docs/standards.html). + - Support [hardware cryptographic modules from Chinese vendors](http://www.sca.gov.cn/sca/zxfw/cpxx.shtml). + - With commercial friendly open source [license](http://gmssl.org/docs/licenses.html). + - Maintained by the [crypto research group of Peking University](http://infosec.pku.edu.cn). -## 下载 +## Supported Algorithms -* GmSSL的当å‰ç¨³å®šå‘布版本为 [GmSSL-3.2.0](https://github.com/guanzhi/GmSSL/releases/tag/v3.2.0),主è¦å¢žåŠ TLS 1.3ã€TLCPã€åŽé‡å­å¯†ç ç®—æ³•å’Œè·¨å¹³å°æž„建支æŒï¼ŒWindowsã€Androidã€iOSã€OpenHarmony等平å°çš„å¼€å‘者需è¦ä½¿ç”¨è¯¥ç‰ˆæœ¬ã€‚ +GmSSL will support all the following GM/T cryptographic algorithms: -## 编译与安装 + - SM3 (GM/T 0004-2012): cryptographic hash function with 256-bit digest length. + - SM4 (GM/T 0002-2012): block cipher with 128-bit key length and 128-bit block size, also named SMS4. + - SM2 (GM/T 0003-2012): elliptic curve cryptographic schemes including digital signature scheme, public key encryption, (authenticated) key exchange protocol and one recommended 256-bit prime field curve `sm2p256v1`. + - SM9 (GM/T 0044-2016): pairing-based cryptographic schemes including identity-based digital signature, encryption, (authenticated) key exchange protocol and one 256-bit recommended BN curve. + - ZUC (GM/T 0001-2012): stream cipher, with 128-EEA3 encryption algorithm and 128-EIA3 integrity algorithm. + - SM1 and SSF33: block ciphers with 128-bit key length and 128-bit block size without public specification, only provided with chip. -GmSSL 3 采用了cmake构建系统。下载æºä»£ç åŽå°†å…¶è§£åŽ‹ç¼©ï¼Œè¿›å…¥æºç ç›®å½•,执行: +GmSSL supports many useful cryptographic algorithms and schemes: -```bash -mkdir build -cd build -cmake .. -make -make test -sudo make install -``` + - Public-key schemes: Paillier, ECIES (Elliptic Curve Integrated Encryption Scheme) + - Pairing-based cryptography: BF-IBE, BB1-IBE + - Block ciphers and modes: Serpent, Speck + - Block cipher modes: FPE (Format-Preserver Encryption) + - OTP (One-Time Password) based on SM3/SM4 (GM/T 0021-2012) + - Encoding: Base58 + +OpenSSL algorithms such as ECDSA, RSA, AES, SHA-1 are all still available in GmSSL. + +## GM/T Protocols -在`make install`完æˆåŽï¼ŒGmSSL会在默认安装目录中安装`gmssl`命令行工具,在头文件目录中创建`gmssl`目录,并且在库目录中安装`libgmssl.so`或`libgmssl.dylib`等动æ€åº“文件。需è¦é™æ€åº“时,å¯ä»¥åœ¨é…置阶段指定`-DBUILD_SHARED_LIBS=OFF`生æˆ`libgmssl.a`。 +The GM/T standards cover 2 protocols: -### Visual Studio环境编译 + - SSL VPN Protocol (GM/T 0024-2014) + - IPSec VPN Protocol (GM/T 0022-2014) -在Visual Studio命令æç¤ºç¬¦ä¸‹æ‰§è¡Œï¼š +The GM/T 0024-2014 SSL VPN protocol is different from IETF TLS in the follows aspects: -```bash -mkdir build -cd build -cmake .. -G "NMake Makefiles" -DWIN32=ON -nmake + - Current version of TLS is 1.3 (0x0304) while GM/T SSL version is 1.1 (0x0102). + - The handshake protocol of GM/T SSL is different from TLS handshake. + - There is an optional different record protocol in GM/T SSL designed for VPN applications. + - GM/T SSL has 12 ciphersuites, some of these ciphers do not provide forward secrecy. + +GM/T 0024-2014 Ciphersuites: + +``` + 1. {0xe0,0x01} GMTLS_SM2DHE_SM2SIGN_WITH_SM1_SM3 + 2. {0xe0,0x03} GMTLS_SM2ENC_WITH_SM1_SM3 + 3. {0xe0,0x05} GMTLS_SM9DHE_SM9SIGN_WITH_SM1_SM3 + 4. {0xe0,0x07} GMTLS_SM9ENC_WITH_SM1_SM3 + 5. {0xe0,0x09} GMTLS_RSA_WITH_SM1_SM3 + 6. {0xe0,0x0a} GMTLS_RSA_WITH_SM1_SHA1 + 7. {0xe0,0x11} GMTLS_SM2DHE_SM2SIGN_WITH_SMS4_SM3 + 8. {0xe0,0x13} GMTLS_SM2ENC_WITH_SMS4_SM3 + 9. {0xe0,0x15} GMTLS_SM9DHE_SM9SIGN_WITH_SMS4_SM3 +10. {0xe0,0x17} GMTLS_SM9ENC_WITH_SMS4_SM3 +11. {0xe0,0x19} GMTLS_RSA_WITH_SMS4_SM3 +12. {0xe0,0x1a} GMTLS_RSA_WITH_SMS4_SHA1 ``` -## 主è¦åŠŸèƒ½ +GmSSL supports the standard TLS 1.2 protocol with SM2/SM3/SM4 ciphersuites and the GM/T SSL VPN protocol and ciphersuites. Currently the following ciphersuites are supported: -### 密ç ç®—法 +``` +ECDHE-SM2-WITH-SMS4-SM3 +ECDHE-SM2-WITH-SMS4-SHA256 +``` -* 分组密ç ï¼šSM4 (CBC/CTR/GCM/ECB/CFB/OFB/CCM/XTS), AES (CBC/CTR/GCM) -* åºåˆ—密ç ï¼šZUC/ZUC-256 -* 哈希函数: SM3, SHA-1, SHA-224/256/384/512 -* 公钥密ç ï¼šSM2加密/ç­¾å, SM9加密/ç­¾å -* åŽé‡å­å¯†ç ï¼šLMS/HSSç­¾å -* MAC算法:HMAC, GHASH, CBC-MAC -* 密钥导出函数:PBKDF2ã€HKDF -* éšæœºæ•°ç”Ÿæˆå™¨ï¼šIntel RDRAND, HASH_DRBG (NIST.SP.800-90A) +## APIs -### è¯ä¹¦å’Œæ•°å­—ä¿¡å° +Except for the native C interface and the `gmssl` command line, GmSSL also provide the following interfaces: -* æ•°å­—è¯ä¹¦ï¼šX.509è¯ä¹¦, CRLè¯ä¹¦æ³¨é”€åˆ—表, CSR (PKCS #10) è¯ä¹¦ç­¾å请求 -* ç§é’¥åŠ å¯†ï¼šåŸºäºŽSM4/SM3å£ä»¤åŠ å¯†çš„PEMæ ¼å¼ç§é’¥ (PKCS #8) -* æ•°å­—ä¿¡å°ï¼šSM2å¯†ç æ¶ˆæ¯ (GM/T 0010-2012) + - Java: crypto, X.509 and SSL API through JNI (Java Native Interface). + - Go: crypto, X.509 and SSL API through CGO. + - SKF C API: GM/T 0016-2012 Smart token cryptography application interface specification. + - SDF C API: GM/T 0018-2012 Interface specifications of cryptography device application. + - SAF C API: GM/T 0019-2012 Universal cryptography service interface specification. + - SOF C/Java API: GM/T 0020-2012 Certificate application integrated service interface specification. -### SSLåè®® +## Supported Cryptographic Hardwares -* TLCP 1.1,支æŒå¯†ç å¥—`TLS_ECC_SM4_CBC_SM3 {0xE0,0x13}` (GB/T 38636-2020ã€GM/T 0024-2014) -* TLS 1.2,支æŒå¯†ç å¥—ä»¶`TLS_ECDHE_SM4_CBC_SM3 {0xE0,0x11}` (GB/T 38636-2020ã€GM/T 0024-2014) -* TLS 1.3,支æŒå¯†ç å¥—ä»¶`TLS_SM4_GCM_SM3 {0x00,0xC6}` (RFC 8998) + - USB-Key through the SKF ENGINE and the SKF API. + - PCI-E card through the SDF ENGINE and the SDF API. + - GM Instruction sets (SM3/SM4) through the GMI ENGINE. -### å¤šè¯­è¨€æŽ¥å£ +## Quick Start -GmSSL通过å­é¡¹ç›®æä¾›å¤šç§ç¼–程语言绑定 +This short guide describes the build, install and typical usage of the `gmssl` command line tool. Visit http://gmssl.org for more documents. - * [GmSSL-Java](https://github.com/GmSSL/GmSSL-Java) 以JNIæ–¹å¼å®žçŽ°çš„Java语言绑定 - * [GmSSL-PHP](https://github.com/GmSSL/GmSSL-PHP) 以PHP扩展方å¼å®žçŽ°çš„PHP语言绑定 - * [GmSSL-Go](https://github.com/GmSSL/GmSSL-Go) 以CGOæ–¹å¼å®žçŽ°çš„Go语言绑定 - * [GmSSL-Python](https://github.com/GmSSL/GmSSL-Python) 以ctypesæ–¹å¼å®žçŽ°çš„Python语言绑定 - * [gmssl-rs](https://github.com/GmSSL/gmssl-rs) GmSSL密ç åº“çš„Rust语言å°è£… - * [GmSSL-Nodejs](https://github.com/GmSSL/GmSSL-Nodejs) GmSSL密ç åº“çš„Node.js语言绑定 +Download ([GmSSL-master.zip](https://github.com/guanzhi/GmSSL/archive/master.zip)), uncompress it and go to the source code folder. On Linux and OS X, run the following commands: -### 支æŒå›½å¯†ç¡¬ä»¶ + ```sh + $ ./config + $ make + $ sudo make install + ``` -GmSSL内置支æŒå›½å¯†SDF密ç ç¡¬ä»¶ï¼ˆé€šå¸¸ä¸ºPCI-E接å£çš„密ç å¡æˆ–者æœåŠ¡å™¨å¯†ç æœºï¼‰å’ŒSKF密ç ç¡¬ä»¶ï¼ˆé€šå¸¸ä¸ºå°åž‹USB密ç é’¥åŒ™ï¼‰ã€‚ç»è¿‡æµ‹è¯•的密ç äº§å“åž‹å·åŒ…括: +After installation you can run `gmssl version -a` to print detailed information. -* to be added. +The `gmssl` command line tool supports SM2 key generation through `ecparam` or `genpkey` option, supports SM2 signing and encryption through `pkeyutl` option, supports SM3 through `sm3` or `dgst` option, and supports SM4 through `sms4` or `enc` option. -å¼€å‘者也å¯ä»¥ç”¨GmSSLçš„å­é¡¹ç›®SoftSDF(https://github.com/GmSSL/SoftSDF) ,用功能等效(但是ä¸å…·å¤‡å¯†ç ç¡¬ä»¶å¯†é’¥ä¿æŠ¤ç­‰ä»·çš„安全性)的软件SDFæ¨¡å—æ¥è¿›è¡Œå¼€å‘和测试,正å¼éƒ¨ç½²çš„æ—¶å€™å†æ›¿æ¢ä¸ºç¡¬ä»¶SDF。 +The following are some examples. -### OpenSSL兼容性 +SM3 digest generation: -GmSSL 3.0版本é‡å†™äº†æ‰€æœ‰çš„代ç å¹¶æ”¹å˜äº†åŽŸæœ‰API,因此当å‰GmSSL版本和OpenSSLä¸å…¼å®¹ï¼Œæ— æ³•直接用GmSSL替æ¢OpenSSL进行编译。GmSSLæä¾›äº†å­é¡¹ç›® OpenSSL-Compatibility-Layer (https://github.com/GmSSL/OpenSSL-Compatibility-Layer) æä¾›äº†OpenSSL的兼容层,Nginx等应用å¯ä»¥é€šè¿‡OpenSSL-Compatibility-Layer调用GmSSL功能。ç»è¿‡æµ‹è¯•ç›®å‰å…¼å®¹å±‚å¯ä»¥å…¼å®¹Nginx 1.16 ~ 1.25 之间的版本。 +``` +$ echo -n "abc" | gmssl sm3 +(stdin)= 66c7f0f462eeedd9d1f2d46bdc10e4e24167c4875cf2f7a2297da02b8f4ba8e0 +``` -## Benchmark +SM4 encryption and decryption: -æ€§èƒ½æµ‹è¯•ç»“æžœæ˜¯åœ¨å•æ ¸å•线程且未修改处ç†å™¨é»˜è®¤é…置下5æ¬¡æµ‹è¯•ä¸­å–æœ€å¥½æ•ˆæžœã€‚由于未关闭ç¿é¢‘æˆ–è¿›è¡Œå¤§å°æ ¸è®¾ç½®ï¼Œè¿™ä¸ªæˆç»©é€šå¸¸ä¼šç•¥é«˜äºŽå¤šæ ¸å¤šçº¿ç¨‹ä¸­æ¯æ ¸å¿ƒçš„平凿ˆç»©ã€‚ +```sh +$ gmssl sms4 -in README.md -out README.sms4 +$ gmssl sms4 -d -in README.sms4 +``` +ZUC/ZUC256 encryption and decryption: + +```sh +$ gmssl zuc -in README.md -out README.zuc +$ gmssl zuc -d -in README.zuc +$ gmssl zuc256 -in README.md -out README.zuc256 +$ gmssl zuc256 -d -in README.zuc256 ``` -cmake .. -DENABLE_TEST_SPEED=ON -make -./bin/sm4_cltest; ./bin/sm4test; ./bin/sm3test; ./bin/sm2_signtest; ./bin/sm2_enctest; ./bin/sm9test; ./bin/zuctest + +SM2 private key generation: + +```sh +$ gmssl sm2 -genkey -out skey.pem ``` -MacBook Air M2 2022. Apple M2. 16 GB. Tahoe 26.1. +Derive the public key from the generated SM2 private key: + +```sh +$ gmssl sm2 -pubout -in skey.pem -out vkey.pem ``` -speed_sm4_cl_ctr32_encrypt_blocks: 15340.364334-MiB per seconds + +SM2 signature generation and verification: + +```sh +$ gmssl sm2utl -sign -in README.md -inkey skey.pem -out README.md.sig +$ gmssl sm2utl -verify -in README.md -pubin -inkey vkey.pem -sigfile README.md.sig ``` -MacBook Pro 13-inch 2018: 2.7 GHz Quad-Core Intel Core i7, Intel Iris Plus Graphics 655. 8 GB 2133 HMz LPDDR3. macOS Sonoma 14.3. +Generate SM2 encryption key pair and do SM2 public key encyption/decryption. It should be noted `pkeyutl -encrypt` should only be used to encrypt short messages such as session key and passphrase. +```sh +$ gmssl sm2 -genkey -out dkey.pem +$ gmssl sm2 -pubout -in dkey.pem -out ekey.pem +$ echo "Top Secret" | gmssl sm2utl -encrypt -pubin -inkey ekey.pem -out ciphertext.sm2 +$ gmssl sm2utl -decrypt -inkey dkey.pem -in ciphertext.sm2 ``` -speed_sm4_encrypt: 164.826108 MiB per second -speed_sm4_encrypt_blocks: 163.120495 MiB per second -speed_sm4_cbc_encrypt_blocks: 156.870435 MiB per second -speed_sm4_cbc_decrypt_blocks: 174.701097 MiB per second -speed_sm4_ctr_encrypt_blocks: 178.457901 MiB per second -speed_sm4_ctr32_encrypt_blocks: 185.007458 MiB per second -speed_zuc_generate_keystream: 337.403260-MiB per second -speed_zuc_encrypt: 356.315696-MiB per second -test_sm3_speed: 273.551034 MiB per second -sm2_sign_ctx speed (ENABLE_SM2_AMD64): 4096 signs time 0.036916 seconds, 110954.599632 signs per second -sm2_sign_ctx speed : 4096 signs time 0.236611 seconds, 17311.114023 signs per second -test_sm2_encrypt_ctx_speed (ENABLE_SM2_AMD64): 17879.592122 encryptions per second -test_sm2_encrypt_ctx_speed: 1869.314131 encryptions per second -test_sm9_z256_pairing_speed: 173 pairings per second + +Identity-based encryption with SM9 + +```sh +$ echo "Message" | gmssl pkeyutl -encrypt -pubin -inkey params.pem -pkeyopt id:Alice -out ciphertext.der +$ gmssl pkeyutl -decrypt -inkey sm9key.pem -in ciphertext.der ``` -MacBook Air M2 2022. Apple M2. 16 GB. Sonoma 14.5. +Self-signed SM2 certificate generation: +```sh +$ gmssl req -new -x509 -key skey.pem -out cert.pem ``` -speed_sm4_encrypt: 143.180578 MiB per second -speed_sm4_encrypt_blocks: 190.928509 MiB per second -speed_sm4_cbc_encrypt_blocks: 148.519447 MiB per second -speed_sm4_cbc_decrypt_blocks: 187.305378 MiB per second -speed_sm4_ctr_encrypt_blocks: 191.195450 MiB per second -speed_sm4_ctr32_encrypt_blocks: 191.522725 MiB per second -speed_zuc_generate_keystream: 344.604781-MiB per second -speed_zuc_encrypt: 344.723575-MiB per second -test_sm3_speed: 327.298762 MiB per second -sm2_sign_ctx speed : 4096 signs time 0.290709 seconds, 14089.691066 signs per second -test_sm2_encrypt_ctx_speed: 1518.575861 encryptions per second -test_sm9_z256_pairing_speed: 141 pairings per seconds + +TLS/DTLS with SM2 ciphersuites: + +```sh +$ gmssl s_server [-tls1_2|-dtls1_2] -port 443 -cipher SM2 -key sm2key.pem -cert sm2cert.pem & +$ gmssl s_client [-tls1_2|-dtls1_2] -connect localhost:443 -cipher SM2 -CAfile cacert.pem ``` -## ChangeLog - -自从3.1.1ç‰ˆæœ¬ä»¥æ¥ - -* 增加了TLS 1.3çš„PSK-DHEå’ŒPSK-onlyæ¡æ‰‹æ¨¡å¼ï¼Œæ”¯æŒ0-RTT -* 增加了TLS 1.3 HelloRetryRequestå商机制 -* 增加了TLS 1.3çš„SNI扩展 -* 增加了TLS 1.3çš„CTã€OCSP Stapling扩展 -* 增加了P-256 + AES128 + SHA256密ç å¥—ä»¶ -* ä»¥çŠ¶æ€æœºæ–¹å¼å®žçްTLSå议栈,支æŒéžé˜»å¡žæ¨¡å¼ -* 增加了CRYSTALS-KyberåŽé‡å­å¯†é’¥å°è£…算法åŠå‘½ä»¤è¡Œé€‰é¡¹ -* 增加了基于SM3çš„SPHINCS+åŽé‡å­å“ˆå¸Œç­¾å算法(无状æ€)åŠå‘½ä»¤è¡Œé€‰é¡¹ -* 增加了基于SM3çš„XMSSåŽé‡å­å“ˆå¸Œç­¾å算法(有状æ€)åŠå‘½ä»¤è¡Œé€‰é¡¹ -* 增加了基于SM3çš„LMS/HSSåŽé‡å­å“ˆå¸Œç­¾å算法(有状æ€)åŠå‘½ä»¤è¡Œé€‰é¡¹ -* æå‡äº†å…¨éƒ¨å›½å¯†ç®—法的性能,并在`tests`测试程åºä¸­å¢žåŠ äº†å›½å¯†ç®—æ³•çš„æ€§èƒ½æµ‹è¯• -* 增加了SM4 ECB/CFB/OFB/CCM/XTS加密模å¼ï¼Œå¸¦SM3-HMACçš„SM4 CBC/CTR模å¼ï¼Œå¹¶ä¸”在`gmssl`命令行工具中增加了所有SM4加密模å¼çš„选项 -* 在`gmssl`命令行中增加了GHASH计算的选项 -* 增加了`sdftest`正确性和兼容性测试命令,以独立å­é¡¹ç›®çš„å½¢å¼æä¾›äº†SDF的软件实现SoftSDF -* 移除了RC4, MD5等已被攻破的密ç ç®—法 - -## å¼€å‘者们 - - - - -## Stargazers over time - -[![Stargazers over time](https://starchart.cc/guanzhi/GmSSL.svg)](https://starchart.cc/guanzhi/GmSSL) diff --git a/VMS/VMSify-conf.pl b/VMS/VMSify-conf.pl new file mode 100644 index 000000000..21eff113f --- /dev/null +++ b/VMS/VMSify-conf.pl @@ -0,0 +1,41 @@ +#! /usr/bin/env perl +# Copyright 2004-2016 The OpenSSL Project Authors. All Rights Reserved. +# +# Licensed under the OpenSSL license (the "License"). You may not use +# this file except in compliance with the License. You can obtain a copy +# in the file LICENSE in the source distribution or at +# https://www.openssl.org/source/license.html + + +use strict; +use warnings; + +my @directory_vars = ( "dir", "certs", "crl_dir", "new_certs_dir" ); +my @file_vars = ( "database", "certificate", "serial", "crlnumber", + "crl", "private_key", "RANDFILE" ); +while() { + s|\R$||; + foreach my $d (@directory_vars) { + if (/^(\s*\#?\s*${d}\s*=\s*)\.\/([^\s\#]*)([\s\#].*)$/) { + $_ = "$1sys\\\$disk:\[.$2$3"; + } elsif (/^(\s*\#?\s*${d}\s*=\s*)(\w[^\s\#]*)([\s\#].*)$/) { + $_ = "$1sys\\\$disk:\[.$2$3"; + } + s/^(\s*\#?\s*${d}\s*=\s*\$\w+)\/([^\s\#]*)([\s\#].*)$/$1.$2\]$3/; + while(/^(\s*\#?\s*${d}\s*=\s*(\$\w+\.|sys\\\$disk:\[\.)[\w\.]+)\/([^\]]*)\](.*)$/) { + $_ = "$1.$3]$4"; + } + } + foreach my $f (@file_vars) { + s/^(\s*\#?\s*${f}\s*=\s*)\.\/(.*)$/$1sys\\\$disk:\[\/$2/; + while(/^(\s*\#?\s*${f}\s*=\s*(\$\w+|sys\\\$disk:\[)[^\/]*)\/(\w+\/[^\s\#]*)([\s\#].*)$/) { + $_ = "$1.$3$4"; + } + if (/^(\s*\#?\s*${f}\s*=\s*(\$\w+|sys\\\$disk:\[)[^\/]*)\/(\w+)([\s\#].*)$/) { + $_ = "$1]$3.$4"; + } elsif (/^(\s*\#?\s*${f}\s*=\s*(\$\w+|sys\\\$disk:\[)[^\/]*)\/([^\s\#]*)([\s\#].*)$/) { + $_ = "$1]$3$4"; + } + } + print $_,"\n"; +} diff --git a/VMS/engine.opt b/VMS/engine.opt new file mode 100644 index 000000000..1c73c8005 --- /dev/null +++ b/VMS/engine.opt @@ -0,0 +1,2 @@ +CASE_SENSITIVE=YES +SYMBOL_VECTOR=(bind_engine=PROCEDURE,v_check=PROCEDURE) diff --git a/VMS/openssl_ivp.com.in b/VMS/openssl_ivp.com.in new file mode 100644 index 000000000..825a699c4 --- /dev/null +++ b/VMS/openssl_ivp.com.in @@ -0,0 +1,50 @@ +$ ! OpenSSL Internal Verification Procedure +$ ! +$ ! This script checks the consistency of a OpenSSL installation +$ ! It had better be spawned, as it creates process logicals +$ +$ ! Generated information +$ INSTALLTOP := {- $config{INSTALLTOP} -} +$ OPENSSLDIR := {- $config{OPENSSLDIR} -} +$ +$ ! Make sure that INSTALLTOP and OPENSSLDIR become something one +$ ! can use to call the startup procedure +$ INSTALLTOP_ = F$PARSE("A.;",INSTALLTOP,,,"NO_CONCEAL") - + - ".][000000" - "[000000." - "][" - "]A.;" + "." +$ OPENSSLDIR_ = F$PARSE("A.;",OPENSSLDIR,,,"NO_CONCEAL") - + - ".][000000" - "[000000." - "][" - "]A.;" + "." +$ +$ v := {- sprintf "%02d%02d", split(/\./, $config{version}) -} +$ pz := {- $config{pointer_size} -} +$ +$ @'INSTALLTOP_'SYS$STARTUP]openssl_startup'v' +$ @'INSTALLTOP_'SYS$STARTUP]openssl_utils'v' +$ +$ IF F$SEARCH("OSSL$LIBCRYPTO''pz'") .EQS. "" - + .OR. F$SEARCH("OSSL$LIBSSL''pz'") .EQS. "" {- output_off() if $config{no_shared}; "" -}- + .OR. F$SEARCH("OSSL$LIBCRYPTO_SHR''pz'") .EQS. "" - + .OR. F$SEARCH("OSSL$LIBSSL_SHR''pz'") .EQS. "" {- output_on() if $config{no_shared}; "" -}- + .OR. F$SEARCH("OSSL$INCLUDE:[OPENSSL]crypto.h") .EQS. "" - + .OR. F$SEARCH("OPENSSL:crypto.h") .EQS. "" - + .OR. F$SEARCH("OSSL$EXE:OPENSSL''v'.EXE") .EQS. "" +$ THEN +$ WRITE SYS$ERROR "Installation inconsistent" +$ EXIT %x00018292 ! RMS$_FNF, file not found +$ ENDIF +$ +$ ON ERROR THEN GOTO error +$ +$ ! If something else is wrong with the installation, we're likely +$ ! to get an image activation error here +$ openssl version -a +$ +$ ! FUTURE ENHANCEMENT: Verify that engines are where they should be. +$ ! openssl engine -c -t checker +$ +$ WRITE SYS$ERROR "OpenSSL IVP passed" +$ EXIT %x10000001 +$ +$ error: +$ save_status = $STATUS +$ WRITE SYS$ERROR "OpenSSL IVP failed" +$ EXIT 'save_status' diff --git a/VMS/openssl_shutdown.com.in b/VMS/openssl_shutdown.com.in new file mode 100644 index 000000000..f0df1c1c3 --- /dev/null +++ b/VMS/openssl_shutdown.com.in @@ -0,0 +1,56 @@ +$ ! OpenSSL shutdown script +$ ! +$ ! This script deassigns the logical names used by the installation +$ ! of OpenSSL. It can do so at any level, defined by P1. +$ ! +$ ! P1 Qualifier(s) for DEASSIGN. +$ ! Default: /PROCESS +$ ! +$ ! P2 If the value is "NOALIASES", no alias logical names are +$ ! deassigned. +$ +$ status = %x10000001 ! Generic success +$ +$ ! In case there's a problem +$ ON CONTROL_Y THEN GOTO bailout +$ ON ERROR THEN GOTO bailout +$ +$ ! Find the architecture +$ IF F$GETSYI("CPU") .LT. 128 +$ THEN +$ arch := VAX +$ ELSE +$ arch := F$EDIT(F$GETSYI("ARCH_NAME"),"UPCASE") +$ IF arch .EQS. "" THEN GOTO unknown_arch +$ ENDIF +$ +$ ! Abbrevs +$ DEAS := DEASSIGN /NOLOG 'P1' +$ sv := {- sprintf "%02d%02d", $config{shlib_major}, $config{shlib_minor} -} +$ pz := {- $config{pointer_size} -} +$ +$ DEAS OSSL$DATAROOT +$ DEAS OSSL$INSTROOT +$ DEAS OSSL$INCLUDE +$ DEAS OSSL$LIB +$ DEAS OSSL$SHARE +$ DEAS OSSL$ENGINES'sv' +$ DEAS OSSL$EXE +$ DEAS OSSL$LIBCRYPTO'pz' +$ DEAS OSSL$LIBSSL'pz' +${- output_off() if $config{no_shared}; "" -} +$ DEAS OSSL$LIBCRYPTO'sv'_SHR'pz' +$ DEAS OSSL$LIBSSL'sv'_SHR'pz' +${- output_on() if $config{no_shared}; "" -} +$ DEAS OPENSSL +$ +$ IF P2 .NES. "NOALIASES" +$ THEN +$ DEAS OSSL$ENGINES +${- output_off() if $config{no_shared}; "" -} +$ DEAS OSSL$LIBCRYPTO_SHR'pz' +$ DEAS OSSL$LIBSSL_SHR'pz' +${- output_on() if $config{no_shared}; "" -} +$ ENDIF +$ +$ EXIT 'status' diff --git a/VMS/openssl_startup.com.in b/VMS/openssl_startup.com.in new file mode 100644 index 000000000..9c8c09ac9 --- /dev/null +++ b/VMS/openssl_startup.com.in @@ -0,0 +1,123 @@ +$ ! OpenSSL startup script +$ ! +$ ! This script defines the logical names used by the installation +$ ! of OpenSSL. It can provide those logical names at any level, +$ ! defined by P1. +$ ! +$ ! The logical names created are: +$ ! +$ ! OSSL$INSTROOT Installation root +$ ! OSSL$DATAROOT Data root (common directory +$ ! for certs etc) +$ ! OSSL$INCLUDE Include directory root +$ ! OSSL$LIB Where the static library files +$ ! are located +$ ! OSSL$SHARE Where the shareable image files +$ ! are located +$ ! OSSL$EXE Where the executables are located +$ ! OSSL$ENGINESnnn Where the shareable images are located +$ ! OSSL$LIBCRYPTO The static crypto library +$ ! OSSL$LIBSSL The static ssl library +$ ! OSSL$LIBCRYPTOnnn_SHR The shareable crypto image +$ ! OSSL$LIBSSLnnn_SHR The shareable ssl image +$ ! OPENSSL is OSSL$INCLUDE:[OPENSSL] +$ ! +$ ! In all these, nnn is the OpenSSL version number. This allows +$ ! several OpenSSL versions to be installed simultaneously, which +$ ! matters for applications that are linked to the shareable images +$ ! or that depend on engines. +$ ! +$ ! In addition, unless P2 is "NOALIASES", these logical names are +$ ! created: +$ ! +$ ! OSSL$ENGINES Alias for OSSL$ENGINESnnn +$ ! OSSL$LIBCRYPTO_SHR Alias for OSSL$LIBCRYPTOnnn_SHR +$ ! OSSL$LIBSSL_SHR Alias for OSSL$LIBSSLnnn_SHR +$ ! +$ ! P1 Qualifier(s) for DEFINE. "/SYSTEM" would be typical when +$ ! calling this script from SYS$STARTUP:SYSTARTUP_VMS.COM, +$ ! while "/PROCESS" would be typical for a personal install. +$ ! Default: /PROCESS +$ ! +$ ! P2 If the value is "NOALIASES", no alias logical names are +$ ! created. +$ +$ status = %x10000001 ! Generic success +$ +$ ! In case there's a problem +$ ON CONTROL_Y THEN GOTO bailout +$ ON ERROR THEN GOTO bailout +$ +$ ! Find the architecture +$ IF F$GETSYI("CPU") .LT. 128 +$ THEN +$ arch := VAX +$ ELSE +$ arch = F$EDIT(F$GETSYI("ARCH_NAME"),"UPCASE") +$ IF arch .EQS. "" THEN GOTO unknown_arch +$ ENDIF +$ +$ ! Generated information +$ INSTALLTOP := {- $config{INSTALLTOP} -} +$ OPENSSLDIR := {- $config{OPENSSLDIR} -} +$ +$ ! Make sure that INSTALLTOP and OPENSSLDIR become something one +$ ! can build concealed logical names on +$ INSTALLTOP_ = F$PARSE("A.;",INSTALLTOP,,,"NO_CONCEAL") - + - ".][000000" - "[000000." - "][" - "]A.;" + "." +$ OPENSSLDIR_ = F$PARSE("A.;",OPENSSLDIR,,,"NO_CONCEAL") - + - ".][000000" - "[000000." - "][" - "]A.;" + "." +$ +$ DEFINE /TRANSLATION=CONCEALED /NOLOG WRK_INSTALLTOP 'INSTALLTOP_'] +$ DEFINE /TRANSLATION=CONCEALED /NOLOG WRK_OPENSSLDIR 'OPENSSLDIR_'] +$ +$ ! Check that things are in place, and specifically, the stuff +$ ! belonging to this architecture +$ IF F$SEARCH("WRK_INSTALLTOP:[000000]INCLUDE.DIR;1") .EQS. "" - + .OR. F$SEARCH("WRK_INSTALLTOP:[000000]LIB.DIR;1") .EQS. "" - + .OR. F$SEARCH("WRK_INSTALLTOP:[000000]EXE.DIR;1") .EQS. "" - + .OR. F$SEARCH("WRK_INSTALLTOP:[LIB]''arch'.DIR;1") .EQS. "" - + .OR. F$SEARCH("WRK_INSTALLTOP:[EXE]''arch'.DIR;1") .EQS. "" - + .OR. F$SEARCH("WRK_OPENSSLDIR:[000000]openssl.cnf") .EQS. "" +$ THEN +$ WRITE SYS$ERROR "''INSTALLTOP' doesn't look like an OpenSSL installation for ''arch'" +$ status = %x00018292 ! RMS$_FNF, file not found +$ GOTO bailout +$ ENDIF +$ +$ ! Abbrevs +$ DEFT := DEFINE /TRANSLATION=CONCEALED /NOLOG 'P1' +$ DEF := DEFINE /NOLOG 'P1' +$ sv := {- sprintf "%02d%02d", $config{shlib_major}, $config{shlib_minor} -} +$ pz := {- $config{pointer_size} -} +$ +$ DEFT OSSL$DATAROOT 'OPENSSLDIR_'] +$ DEFT OSSL$INSTROOT 'INSTALLTOP_'] +$ DEFT OSSL$INCLUDE 'INSTALLTOP_'INCLUDE.] +$ DEF OSSL$LIB OSSL$INSTROOT:[LIB.'arch'] +$ DEF OSSL$SHARE OSSL$INSTROOT:[LIB.'arch'] +$ DEF OSSL$ENGINES'sv''pz' OSSL$INSTROOT:[ENGINES'sv''pz'.'arch'] +$ DEF OSSL$EXE OSSL$INSTROOT:[EXE.'arch'],- + OSSL$INSTROOT:[EXE] +$ DEF OSSL$LIBCRYPTO'pz' OSSL$LIB:OSSL$LIBCRYPTO'pz'.OLB +$ DEF OSSL$LIBSSL'pz' OSSL$LIB:OSSL$LIBSSL'pz'.OLB +${- output_off() if $config{no_shared}; "" -} +$ DEF OSSL$LIBCRYPTO'sv'_SHR'pz' OSSL$SHARE:OSSL$LIBCRYPTO'sv'_SHR'pz'.EXE +$ DEF OSSL$LIBSSL'sv'_SHR'pz' OSSL$SHARE:OSSL$LIBSSL'sv'_SHR'pz'.EXE +${- output_on() if $config{no_shared}; "" -} +$ DEF OPENSSL OSSL$INCLUDE:[OPENSSL] +$ +$ IF P2 .NES. "NOALIASES" +$ THEN +$ DEF OSSL$ENGINES'pz' OSSL$ENGINES'sv''pz' +${- output_off() if $config{no_shared}; "" -} +$ DEF OSSL$LIBCRYPTO_SHR'pz' OSSL$LIBCRYPTO'sv'_SHR'pz' +$ DEF OSSL$LIBSSL_SHR'pz' OSSL$LIBSSL'sv'_SHR'pz' +${- output_on() if $config{no_shared}; "" -} +$ ENDIF +$ +$ bailout: +$ DEASSIGN WRK_INSTALLTOP +$ DEASSIGN WRK_OPENSSLDIR +$ +$ EXIT 'status' diff --git a/VMS/openssl_utils.com.in b/VMS/openssl_utils.com.in new file mode 100644 index 000000000..edd733d7b --- /dev/null +++ b/VMS/openssl_utils.com.in @@ -0,0 +1,14 @@ +$ ! OpenSSL utilities +$ ! +$ +$ v := {- sprintf "%02d%02d", split(/\./, $config{version}) -} +$ +$ OPENSSL'v' :== $OSSL$EXE:OPENSSL'v' +$ OPENSSL :== $OSSL$EXE:OPENSSL'v' +$ +$ IF F$TYPE(PERL) .EQS. "STRING" +$ THEN +$ C_REHASH :== 'PERL' OSSL$EXE:c_rehash.pl +$ ELSE +$ WRITE SYS$ERROR "NOTE: no perl => no C_REHASH" +$ ENDIF diff --git a/VMS/test-includes.com b/VMS/test-includes.com new file mode 100644 index 000000000..c1d7ccd0e --- /dev/null +++ b/VMS/test-includes.com @@ -0,0 +1,28 @@ +$! Quick script to check how well including individual header files works +$! on VMS, even when the VMS macro isn't defined. +$ +$ sav_def = f$env("DEFAULT") +$ here = f$parse("A.;0",f$ENV("PROCEDURE")) - "A.;0" +$ set default 'here' +$ set default [-.include.openssl] +$ define openssl 'f$env("DEFAULT")' +$ set default [--] +$ +$ loop: +$ f = f$search("openssl:*.h") +$ if f .eqs. "" then goto loop_end +$ write sys$output "Checking ",f +$ open/write foo foo.c +$ write foo "#undef VMS" +$ write foo "#include " +$ write foo "#include " +$ write foo "main()" +$ write foo "{printf(""foo\n"");}" +$ close foo +$ cc/STANDARD=ANSI89/NOLIST/PREFIX=ALL foo.c +$ delete foo.c; +$ goto loop +$ loop_end: +$ set default 'save_def' +$ exit + diff --git a/VMS/translatesyms.pl b/VMS/translatesyms.pl new file mode 100644 index 000000000..f61d954ec --- /dev/null +++ b/VMS/translatesyms.pl @@ -0,0 +1,62 @@ +#! /usr/bin/env perl +# Copyright 2016 The OpenSSL Project Authors. All Rights Reserved. +# +# Licensed under the OpenSSL license (the "License"). You may not use +# this file except in compliance with the License. You can obtain a copy +# in the file LICENSE in the source distribution or at +# https://www.openssl.org/source/license.html + + +# This script will translate any SYMBOL_VECTOR item that has a translation +# in CXX$DEMANGLER_DB. The latter is generated by and CC/DECC command that +# uses the qualifier /REPOSITORY with the build directory as value. When +# /NAMES=SHORTENED has been used, this file will hold the translations from +# the original symbols to the shortened variants. +# +# CXX$DEMAGLER_DB. is an ISAM file, but with the magic of RMS, it can be +# read as a text file, with each record as one line. +# +# The lines will have the following syntax for any symbol found that's longer +# than 31 characters: +# +# LONG_symbol_34567890123{cksum}$LONG_symbol_34567890123_more_than_31_chars +# +# $ is present at the end of the shortened symbol name, and is preceded by a +# 7 character checksum. The $ makes it easy to separate the shortened name +# from the original one. + +use strict; +use warnings; + +usage() if scalar @ARGV < 1; + +my %translations = (); + +open DEMANGLER_DATA, $ARGV[0] + or die "Couldn't open $ARGV[0]: $!\n"; +while() { + s|\R$||; + (my $translated, my $original) = split /\$/; + $translations{$original} = $translated.'$'; +} +close DEMANGLER_DATA; + +$| = 1; # Autoflush +while() { + s@ + ((?:[A-Za-z0-9_]+)\/)?([A-Za-z0-9_]+)=(PROCEDURE|DATA) + @ + if (defined($translations{$2})) { + my $trans = $translations{$2}; + my $trans_uc = uc $trans; + if (defined($1) && $trans ne $trans_uc) { + "$trans_uc/$trans=$3" + } else { + "$trans=$3" + } + } else { + $& + } + @gxe; + print $_; +} diff --git a/apps/CA.pl.in b/apps/CA.pl.in new file mode 100644 index 000000000..3187e473d --- /dev/null +++ b/apps/CA.pl.in @@ -0,0 +1,196 @@ +#!{- $config{hashbangperl} -} +# Copyright 2000-2016 The OpenSSL Project Authors. All Rights Reserved. +# +# Licensed under the OpenSSL license (the "License"). You may not use +# this file except in compliance with the License. You can obtain a copy +# in the file LICENSE in the source distribution or at +# https://www.openssl.org/source/license.html + +# +# Wrapper around the ca to make it easier to use +# +# {- join("\n# ", @autowarntext) -} + +use strict; +use warnings; + +my $openssl = "openssl"; +if(defined $ENV{'OPENSSL'}) { + $openssl = $ENV{'OPENSSL'}; +} else { + $ENV{'OPENSSL'} = $openssl; +} + +my $verbose = 1; + +my $OPENSSL_CONFIG = $ENV{"OPENSSL_CONFIG"} || ""; +my $DAYS = "-days 365"; +my $CADAYS = "-days 1095"; # 3 years +my $REQ = "$openssl req $OPENSSL_CONFIG"; +my $CA = "$openssl ca $OPENSSL_CONFIG"; +my $VERIFY = "$openssl verify"; +my $X509 = "$openssl x509"; +my $PKCS12 = "$openssl pkcs12"; + +# default openssl.cnf file has setup as per the following +my $CATOP = "./demoCA"; +my $CAKEY = "cakey.pem"; +my $CAREQ = "careq.pem"; +my $CACERT = "cacert.pem"; +my $CACRL = "crl.pem"; +my $DIRMODE = 0777; + +my $NEWKEY = "newkey.pem"; +my $NEWREQ = "newreq.pem"; +my $NEWCERT = "newcert.pem"; +my $NEWP12 = "newcert.p12"; +my $RET = 0; +my $WHAT = shift @ARGV || ""; +my $FILE; + +# See if reason for a CRL entry is valid; exit if not. +sub crl_reason_ok +{ + my $r = shift; + + if ($r eq 'unspecified' || $r eq 'keyCompromise' + || $r eq 'CACompromise' || $r eq 'affiliationChanged' + || $r eq 'superseded' || $r eq 'cessationOfOperation' + || $r eq 'certificateHold' || $r eq 'removeFromCRL') { + return 1; + } + print STDERR "Invalid CRL reason; must be one of:\n"; + print STDERR " unspecified, keyCompromise, CACompromise,\n"; + print STDERR " affiliationChanged, superseded, cessationOfOperation\n"; + print STDERR " certificateHold, removeFromCRL"; + exit 1; +} + +# Copy a PEM-format file; return like exit status (zero means ok) +sub copy_pemfile +{ + my ($infile, $outfile, $bound) = @_; + my $found = 0; + + open IN, $infile || die "Cannot open $infile, $!"; + open OUT, ">$outfile" || die "Cannot write to $outfile, $!"; + while () { + $found = 1 if /^-----BEGIN.*$bound/; + print OUT $_ if $found; + $found = 2, last if /^-----END.*$bound/; + } + close IN; + close OUT; + return $found == 2 ? 0 : 1; +} + +# Wrapper around system; useful for debugging. Returns just the exit status +sub run +{ + my $cmd = shift; + print "====\n$cmd\n" if $verbose; + my $status = system($cmd); + print "==> $status\n====\n" if $verbose; + return $status >> 8; +} + + +if ( $WHAT =~ /^(-\?|-h|-help)$/ ) { + print STDERR "usage: CA -newcert|-newreq|-newreq-nodes|-newca|-sign|-signcert|-verify\n"; + print STDERR " CA -pkcs12 [certname]\n"; + print STDERR " CA -crl|-revoke cert-filename [reason]\n"; + exit 0; +} +if ($WHAT eq '-newcert' ) { + # create a certificate + $RET = run("$REQ -new -x509 -keyout $NEWKEY -out $NEWCERT $DAYS"); + print "Cert is in $NEWCERT, private key is in $NEWKEY\n" if $RET == 0; +} elsif ($WHAT eq '-newreq' ) { + # create a certificate request + $RET = run("$REQ -new -keyout $NEWKEY -out $NEWREQ $DAYS"); + print "Request is in $NEWREQ, private key is in $NEWKEY\n" if $RET == 0; +} elsif ($WHAT eq '-newreq-nodes' ) { + # create a certificate request + $RET = run("$REQ -new -nodes -keyout $NEWKEY -out $NEWREQ $DAYS"); + print "Request is in $NEWREQ, private key is in $NEWKEY\n" if $RET == 0; +} elsif ($WHAT eq '-newca' ) { + # create the directory hierarchy + mkdir ${CATOP}, $DIRMODE; + mkdir "${CATOP}/certs", $DIRMODE; + mkdir "${CATOP}/crl", $DIRMODE ; + mkdir "${CATOP}/newcerts", $DIRMODE; + mkdir "${CATOP}/private", $DIRMODE; + open OUT, ">${CATOP}/index.txt"; + close OUT; + open OUT, ">${CATOP}/crlnumber"; + print OUT "01\n"; + close OUT; + # ask user for existing CA certificate + print "CA certificate filename (or enter to create)\n"; + $FILE = "" unless defined($FILE = ); + $FILE =~ s{\R$}{}; + if ($FILE ne "") { + copy_pemfile($FILE,"${CATOP}/private/$CAKEY", "PRIVATE"); + copy_pemfile($FILE,"${CATOP}/$CACERT", "CERTIFICATE"); + } else { + print "Making CA certificate ...\n"; + $RET = run("$REQ -new -keyout" + . " ${CATOP}/private/$CAKEY" + . " -out ${CATOP}/$CAREQ"); + $RET = run("$CA -create_serial" + . " -out ${CATOP}/$CACERT $CADAYS -batch" + . " -keyfile ${CATOP}/private/$CAKEY -selfsign" + . " -extensions v3_ca" + . " -infiles ${CATOP}/$CAREQ") if $RET == 0; + print "CA certificate is in ${CATOP}/$CACERT\n" if $RET == 0; + } +} elsif ($WHAT eq '-pkcs12' ) { + my $cname = $ARGV[1]; + $cname = "My Certificate" unless defined $cname; + $RET = run("$PKCS12 -in $NEWCERT -inkey $NEWKEY" + . " -certfile ${CATOP}/$CACERT" + . " -out $NEWP12" + . " -export -name \"$cname\""); + print "PKCS #12 file is in $NEWP12\n" if $RET == 0; +} elsif ($WHAT eq '-xsign' ) { + $RET = run("$CA -policy policy_anything -infiles $NEWREQ"); +} elsif ($WHAT eq '-sign' ) { + $RET = run("$CA -policy policy_anything -out $NEWCERT -infiles $NEWREQ"); + print "Signed certificate is in $NEWCERT\n" if $RET == 0; +} elsif ($WHAT eq '-signCA' ) { + $RET = run("$CA -policy policy_anything -out $NEWCERT" + . " -extensions v3_ca -infiles $NEWREQ"); + print "Signed CA certificate is in $NEWCERT\n" if $RET == 0; +} elsif ($WHAT eq '-signcert' ) { + $RET = run("$X509 -x509toreq -in $NEWREQ -signkey $NEWREQ" + . " -out tmp.pem"); + $RET = run("$CA -policy policy_anything -out $NEWCERT" + . " -infiles tmp.pem") if $RET == 0; + print "Signed certificate is in $NEWCERT\n" if $RET == 0; +} elsif ($WHAT eq '-verify' ) { + my @files = @ARGV ? @ARGV : ( $NEWCERT ); + my $file; + foreach $file (@files) { + my $status = run("$VERIFY \"-CAfile\" ${CATOP}/$CACERT $file"); + $RET = $status if $status != 0; + } +} elsif ($WHAT eq '-crl' ) { + $RET = run("$CA -gencrl -out ${CATOP}/crl/$CACRL"); + print "Generated CRL is in ${CATOP}/crl/$CACRL\n" if $RET == 0; +} elsif ($WHAT eq '-revoke' ) { + my $cname = $ARGV[1]; + if (!defined $cname) { + print "Certificate filename is required; reason optional.\n"; + exit 1; + } + my $reason = $ARGV[2]; + $reason = " -crl_reason $reason" + if defined $reason && crl_reason_ok($reason); + $RET = run("$CA -revoke \"$cname\"" . $reason); +} else { + print STDERR "Unknown arg \"$WHAT\"\n"; + print STDERR "Use -help for help.\n"; + exit 1; +} + +exit $RET; diff --git a/apps/app_rand.c b/apps/app_rand.c new file mode 100644 index 000000000..0d44af903 --- /dev/null +++ b/apps/app_rand.c @@ -0,0 +1,115 @@ +/* + * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include "apps.h" +#include +#include + +static int seeded = 0; +static int egdsocket = 0; + +int app_RAND_load_file(const char *file, int dont_warn) +{ + int consider_randfile = (file == NULL); + char buffer[200]; + + if (file == NULL) + file = RAND_file_name(buffer, sizeof buffer); +#ifndef OPENSSL_NO_EGD + else if (RAND_egd(file) > 0) { + /* + * we try if the given filename is an EGD socket. if it is, we don't + * write anything back to the file. + */ + egdsocket = 1; + return 1; + } +#endif + if (file == NULL || !RAND_load_file(file, -1)) { + if (RAND_status() == 0) { + if (!dont_warn) { + BIO_printf(bio_err, "unable to load 'random state'\n"); + BIO_printf(bio_err, + "This means that the random number generator has not been seeded\n"); + BIO_printf(bio_err, "with much random data.\n"); + if (consider_randfile) { /* explanation does not apply when a + * file is explicitly named */ + BIO_printf(bio_err, + "Consider setting the RANDFILE environment variable to point at a file that\n"); + BIO_printf(bio_err, + "'random' data can be kept in (the file will be overwritten).\n"); + } + } + return 0; + } + } + seeded = 1; + return 1; +} + +long app_RAND_load_files(char *name) +{ + char *p, *n; + int last; + long tot = 0; +#ifndef OPENSSL_NO_EGD + int egd; +#endif + + for (;;) { + last = 0; + for (p = name; ((*p != '\0') && (*p != LIST_SEPARATOR_CHAR)); p++) ; + if (*p == '\0') + last = 1; + *p = '\0'; + n = name; + name = p + 1; + if (*n == '\0') + break; + +#ifndef OPENSSL_NO_EGD + egd = RAND_egd(n); + if (egd > 0) + tot += egd; + else +#endif + tot += RAND_load_file(n, -1); + if (last) + break; + } + if (tot > 512) + app_RAND_allow_write_file(); + return (tot); +} + +int app_RAND_write_file(const char *file) +{ + char buffer[200]; + + if (egdsocket || !seeded) + /* + * If we did not manage to read the seed file, we should not write a + * low-entropy seed file back -- it would suppress a crucial warning + * the next time we want to use it. + */ + return 0; + + if (file == NULL) + file = RAND_file_name(buffer, sizeof buffer); + if (file == NULL || !RAND_write_file(file)) { + BIO_printf(bio_err, "unable to write 'random state'\n"); + return 0; + } + return 1; +} + +void app_RAND_allow_write_file(void) +{ + seeded = 1; +} diff --git a/apps/apps.c b/apps/apps.c new file mode 100644 index 000000000..f3079bbf8 --- /dev/null +++ b/apps/apps.c @@ -0,0 +1,2655 @@ +/* + * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#if !defined(_POSIX_C_SOURCE) && defined(OPENSSL_SYS_VMS) +/* + * On VMS, you need to define this to get the declaration of fileno(). The + * value 2 is to make sure no function defined in POSIX-2 is left undefined. + */ +# define _POSIX_C_SOURCE 2 +#endif + +#include +#include +#include +#ifndef NO_SYS_TYPES_H +# include +#endif +#ifndef OPENSSL_NO_POSIX_IO +# include +# include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifndef OPENSSL_NO_ENGINE +# include +#endif +#ifndef OPENSSL_NO_RSA +# include +#endif +#include +#include +#include "s_apps.h" +#include "apps.h" + +#ifdef _WIN32 +static int WIN32_rename(const char *from, const char *to); +# define rename(from,to) WIN32_rename((from),(to)) +#endif + +typedef struct { + const char *name; + unsigned long flag; + unsigned long mask; +} NAME_EX_TBL; + +#if !defined(OPENSSL_NO_UI) || !defined(OPENSSL_NO_ENGINE) +static UI_METHOD *ui_method = NULL; +#endif + +static int set_table_opts(unsigned long *flags, const char *arg, + const NAME_EX_TBL * in_tbl); +static int set_multi_opts(unsigned long *flags, const char *arg, + const NAME_EX_TBL * in_tbl); + +int app_init(long mesgwin); + +int chopup_args(ARGS *arg, char *buf) +{ + int quoted; + char c = '\0', *p = NULL; + + arg->argc = 0; + if (arg->size == 0) { + arg->size = 20; + arg->argv = app_malloc(sizeof(*arg->argv) * arg->size, "argv space"); + } + + for (p = buf;;) { + /* Skip whitespace. */ + while (*p && isspace(_UC(*p))) + p++; + if (!*p) + break; + + /* The start of something good :-) */ + if (arg->argc >= arg->size) { + char **tmp; + arg->size += 20; + tmp = OPENSSL_realloc(arg->argv, sizeof(*arg->argv) * arg->size); + if (tmp == NULL) + return 0; + arg->argv = tmp; + } + quoted = *p == '\'' || *p == '"'; + if (quoted) + c = *p++; + arg->argv[arg->argc++] = p; + + /* now look for the end of this */ + if (quoted) { + while (*p && *p != c) + p++; + *p++ = '\0'; + } else { + while (*p && !isspace(_UC(*p))) + p++; + if (*p) + *p++ = '\0'; + } + } + arg->argv[arg->argc] = NULL; + return (1); +} + +#ifndef APP_INIT +int app_init(long mesgwin) +{ + return (1); +} +#endif + +int ctx_set_verify_locations(SSL_CTX *ctx, const char *CAfile, + const char *CApath, int noCAfile, int noCApath) +{ + if (CAfile == NULL && CApath == NULL) { + if (!noCAfile && SSL_CTX_set_default_verify_file(ctx) <= 0) + return 0; + if (!noCApath && SSL_CTX_set_default_verify_dir(ctx) <= 0) + return 0; + + return 1; + } + return SSL_CTX_load_verify_locations(ctx, CAfile, CApath); +} + +#ifndef OPENSSL_NO_CT + +int ctx_set_ctlog_list_file(SSL_CTX *ctx, const char *path) +{ + if (path == NULL) { + return SSL_CTX_set_default_ctlog_list_file(ctx); + } + + return SSL_CTX_set_ctlog_list_file(ctx, path); +} + +#endif + +int dump_cert_text(BIO *out, X509 *x) +{ + char *p; + + p = X509_NAME_oneline(X509_get_subject_name(x), NULL, 0); + BIO_puts(out, "subject="); + BIO_puts(out, p); + OPENSSL_free(p); + + p = X509_NAME_oneline(X509_get_issuer_name(x), NULL, 0); + BIO_puts(out, "\nissuer="); + BIO_puts(out, p); + BIO_puts(out, "\n"); + OPENSSL_free(p); + + return 0; +} + +#ifndef OPENSSL_NO_UI +static int ui_open(UI *ui) +{ + return UI_method_get_opener(UI_OpenSSL())(ui); +} + +static int ui_read(UI *ui, UI_STRING *uis) +{ + if (UI_get_input_flags(uis) & UI_INPUT_FLAG_DEFAULT_PWD + && UI_get0_user_data(ui)) { + switch (UI_get_string_type(uis)) { + case UIT_PROMPT: + case UIT_VERIFY: + { + const char *password = + ((PW_CB_DATA *)UI_get0_user_data(ui))->password; + if (password && password[0] != '\0') { + UI_set_result(ui, uis, password); + return 1; + } + } + default: + break; + } + } + return UI_method_get_reader(UI_OpenSSL())(ui, uis); +} + +static int ui_write(UI *ui, UI_STRING *uis) +{ + if (UI_get_input_flags(uis) & UI_INPUT_FLAG_DEFAULT_PWD + && UI_get0_user_data(ui)) { + switch (UI_get_string_type(uis)) { + case UIT_PROMPT: + case UIT_VERIFY: + { + const char *password = + ((PW_CB_DATA *)UI_get0_user_data(ui))->password; + if (password && password[0] != '\0') + return 1; + } + default: + break; + } + } + return UI_method_get_writer(UI_OpenSSL())(ui, uis); +} + +static int ui_close(UI *ui) +{ + return UI_method_get_closer(UI_OpenSSL())(ui); +} + +int setup_ui_method(void) +{ + ui_method = UI_create_method("OpenSSL application user interface"); + UI_method_set_opener(ui_method, ui_open); + UI_method_set_reader(ui_method, ui_read); + UI_method_set_writer(ui_method, ui_write); + UI_method_set_closer(ui_method, ui_close); + return 0; +} + +void destroy_ui_method(void) +{ + if (ui_method) { + UI_destroy_method(ui_method); + ui_method = NULL; + } +} +#endif + +int password_callback(char *buf, int bufsiz, int verify, PW_CB_DATA *cb_tmp) +{ + int res = 0; +#ifndef OPENSSL_NO_UI + UI *ui = NULL; +#endif + PW_CB_DATA *cb_data = (PW_CB_DATA *)cb_tmp; + +#ifdef OPENSSL_NO_UI + if (cb_data != NULL && cb_data->password != NULL) { + res = strlen(cb_data->password); + if (res > bufsiz) + res = bufsiz; + memcpy(buf, cb_data->password, res); + } +#else + ui = UI_new_method(ui_method); + if (ui) { + int ok = 0; + char *buff = NULL; + int ui_flags = 0; + const char *prompt_info = NULL; + char *prompt; + + if (cb_data != NULL && cb_data->prompt_info != NULL) + prompt_info = cb_data->prompt_info; + prompt = UI_construct_prompt(ui, "pass phrase", prompt_info); + if (!prompt) { + BIO_printf(bio_err, "Out of memory\n"); + UI_free(ui); + return 0; + } + + ui_flags |= UI_INPUT_FLAG_DEFAULT_PWD; + UI_ctrl(ui, UI_CTRL_PRINT_ERRORS, 1, 0, 0); + + /* We know that there is no previous user data to return to us */ + (void)UI_add_user_data(ui, cb_data); + + if (ok >= 0) + ok = UI_add_input_string(ui, prompt, ui_flags, buf, + PW_MIN_LENGTH, bufsiz - 1); + if (ok >= 0 && verify) { + buff = app_malloc(bufsiz, "password buffer"); + ok = UI_add_verify_string(ui, prompt, ui_flags, buff, + PW_MIN_LENGTH, bufsiz - 1, buf); + } + if (ok >= 0) + do { + ok = UI_process(ui); + } + while (ok < 0 && UI_ctrl(ui, UI_CTRL_IS_REDOABLE, 0, 0, 0)); + + OPENSSL_clear_free(buff, (unsigned int)bufsiz); + + if (ok >= 0) + res = strlen(buf); + if (ok == -1) { + BIO_printf(bio_err, "User interface error\n"); + ERR_print_errors(bio_err); + OPENSSL_cleanse(buf, (unsigned int)bufsiz); + res = 0; + } + if (ok == -2) { + BIO_printf(bio_err, "aborted!\n"); + OPENSSL_cleanse(buf, (unsigned int)bufsiz); + res = 0; + } + UI_free(ui); + OPENSSL_free(prompt); + } +#endif + return res; +} + +static char *app_get_pass(const char *arg, int keepbio); + +int app_passwd(const char *arg1, const char *arg2, char **pass1, char **pass2) +{ + int same; + if (!arg2 || !arg1 || strcmp(arg1, arg2)) + same = 0; + else + same = 1; + if (arg1) { + *pass1 = app_get_pass(arg1, same); + if (!*pass1) + return 0; + } else if (pass1) + *pass1 = NULL; + if (arg2) { + *pass2 = app_get_pass(arg2, same ? 2 : 0); + if (!*pass2) + return 0; + } else if (pass2) + *pass2 = NULL; + return 1; +} + +static char *app_get_pass(const char *arg, int keepbio) +{ + char *tmp, tpass[APP_PASS_LEN]; + static BIO *pwdbio = NULL; + int i; + + if (strncmp(arg, "pass:", 5) == 0) + return OPENSSL_strdup(arg + 5); + if (strncmp(arg, "env:", 4) == 0) { + tmp = getenv(arg + 4); + if (!tmp) { + BIO_printf(bio_err, "Can't read environment variable %s\n", arg + 4); + return NULL; + } + return OPENSSL_strdup(tmp); + } + if (!keepbio || !pwdbio) { + if (strncmp(arg, "file:", 5) == 0) { + pwdbio = BIO_new_file(arg + 5, "r"); + if (!pwdbio) { + BIO_printf(bio_err, "Can't open file %s\n", arg + 5); + return NULL; + } +#if !defined(_WIN32) + /* + * Under _WIN32, which covers even Win64 and CE, file + * descriptors referenced by BIO_s_fd are not inherited + * by child process and therefore below is not an option. + * It could have been an option if bss_fd.c was operating + * on real Windows descriptors, such as those obtained + * with CreateFile. + */ + } else if (strncmp(arg, "fd:", 3) == 0) { + BIO *btmp; + i = atoi(arg + 3); + if (i >= 0) + pwdbio = BIO_new_fd(i, BIO_NOCLOSE); + if ((i < 0) || !pwdbio) { + BIO_printf(bio_err, "Can't access file descriptor %s\n", arg + 3); + return NULL; + } + /* + * Can't do BIO_gets on an fd BIO so add a buffering BIO + */ + btmp = BIO_new(BIO_f_buffer()); + pwdbio = BIO_push(btmp, pwdbio); +#endif + } else if (strcmp(arg, "stdin") == 0) { + pwdbio = dup_bio_in(FORMAT_TEXT); + if (!pwdbio) { + BIO_printf(bio_err, "Can't open BIO for stdin\n"); + return NULL; + } + } else { + BIO_printf(bio_err, "Invalid password argument \"%s\"\n", arg); + return NULL; + } + } + i = BIO_gets(pwdbio, tpass, APP_PASS_LEN); + if (keepbio != 1) { + BIO_free_all(pwdbio); + pwdbio = NULL; + } + if (i <= 0) { + BIO_printf(bio_err, "Error reading password from BIO\n"); + return NULL; + } + tmp = strchr(tpass, '\n'); + if (tmp) + *tmp = 0; + return OPENSSL_strdup(tpass); +} + +static CONF *app_load_config_(BIO *in, const char *filename) +{ + long errorline = -1; + CONF *conf; + int i; + + conf = NCONF_new(NULL); + i = NCONF_load_bio(conf, in, &errorline); + if (i > 0) + return conf; + + if (errorline <= 0) + BIO_printf(bio_err, "%s: Can't load config file \"%s\"\n", + opt_getprog(), filename); + else + BIO_printf(bio_err, "%s: Error on line %ld of config file \"%s\"\n", + opt_getprog(), errorline, filename); + NCONF_free(conf); + return NULL; +} +CONF *app_load_config(const char *filename) +{ + BIO *in; + CONF *conf; + + in = bio_open_default(filename, 'r', FORMAT_TEXT); + if (in == NULL) + return NULL; + + conf = app_load_config_(in, filename); + BIO_free(in); + return conf; +} +CONF *app_load_config_quiet(const char *filename) +{ + BIO *in; + CONF *conf; + + in = bio_open_default_quiet(filename, 'r', FORMAT_TEXT); + if (in == NULL) + return NULL; + + conf = app_load_config_(in, filename); + BIO_free(in); + return conf; +} + +int app_load_modules(const CONF *config) +{ + CONF *to_free = NULL; + + if (config == NULL) + config = to_free = app_load_config_quiet(default_config_file); + if (config == NULL) + return 1; + + if (CONF_modules_load(config, NULL, 0) <= 0) { + BIO_printf(bio_err, "Error configuring OpenSSL modules\n"); + ERR_print_errors(bio_err); + NCONF_free(to_free); + return 0; + } + NCONF_free(to_free); + return 1; +} + +int add_oid_section(CONF *conf) +{ + char *p; + STACK_OF(CONF_VALUE) *sktmp; + CONF_VALUE *cnf; + int i; + + if ((p = NCONF_get_string(conf, NULL, "oid_section")) == NULL) { + ERR_clear_error(); + return 1; + } + if ((sktmp = NCONF_get_section(conf, p)) == NULL) { + BIO_printf(bio_err, "problem loading oid section %s\n", p); + return 0; + } + for (i = 0; i < sk_CONF_VALUE_num(sktmp); i++) { + cnf = sk_CONF_VALUE_value(sktmp, i); + if (OBJ_create(cnf->value, cnf->name, cnf->name) == NID_undef) { + BIO_printf(bio_err, "problem creating object %s=%s\n", + cnf->name, cnf->value); + return 0; + } + } + return 1; +} + +static int load_pkcs12(BIO *in, const char *desc, + pem_password_cb *pem_cb, void *cb_data, + EVP_PKEY **pkey, X509 **cert, STACK_OF(X509) **ca) +{ + const char *pass; + char tpass[PEM_BUFSIZE]; + int len, ret = 0; + PKCS12 *p12; + p12 = d2i_PKCS12_bio(in, NULL); + if (p12 == NULL) { + BIO_printf(bio_err, "Error loading PKCS12 file for %s\n", desc); + goto die; + } + /* See if an empty password will do */ + if (PKCS12_verify_mac(p12, "", 0) || PKCS12_verify_mac(p12, NULL, 0)) + pass = ""; + else { + if (!pem_cb) + pem_cb = (pem_password_cb *)password_callback; + len = pem_cb(tpass, PEM_BUFSIZE, 0, cb_data); + if (len < 0) { + BIO_printf(bio_err, "Passphrase callback error for %s\n", desc); + goto die; + } + if (len < PEM_BUFSIZE) + tpass[len] = 0; + if (!PKCS12_verify_mac(p12, tpass, len)) { + BIO_printf(bio_err, + "Mac verify error (wrong password?) in PKCS12 file for %s\n", + desc); + goto die; + } + pass = tpass; + } + ret = PKCS12_parse(p12, pass, pkey, cert, ca); + die: + PKCS12_free(p12); + return ret; +} + +#if !defined(OPENSSL_NO_OCSP) && !defined(OPENSSL_NO_SOCK) +static int load_cert_crl_http(const char *url, X509 **pcert, X509_CRL **pcrl) +{ + char *host = NULL, *port = NULL, *path = NULL; + BIO *bio = NULL; + OCSP_REQ_CTX *rctx = NULL; + int use_ssl, rv = 0; + if (!OCSP_parse_url(url, &host, &port, &path, &use_ssl)) + goto err; + if (use_ssl) { + BIO_puts(bio_err, "https not supported\n"); + goto err; + } + bio = BIO_new_connect(host); + if (!bio || !BIO_set_conn_port(bio, port)) + goto err; + rctx = OCSP_REQ_CTX_new(bio, 1024); + if (rctx == NULL) + goto err; + if (!OCSP_REQ_CTX_http(rctx, "GET", path)) + goto err; + if (!OCSP_REQ_CTX_add1_header(rctx, "Host", host)) + goto err; + if (pcert) { + do { + rv = X509_http_nbio(rctx, pcert); + } while (rv == -1); + } else { + do { + rv = X509_CRL_http_nbio(rctx, pcrl); + } while (rv == -1); + } + + err: + OPENSSL_free(host); + OPENSSL_free(path); + OPENSSL_free(port); + if (bio) + BIO_free_all(bio); + OCSP_REQ_CTX_free(rctx); + if (rv != 1) { + BIO_printf(bio_err, "Error loading %s from %s\n", + pcert ? "certificate" : "CRL", url); + ERR_print_errors(bio_err); + } + return rv; +} +#endif + +X509 *load_cert(const char *file, int format, const char *cert_descrip) +{ + X509 *x = NULL; + BIO *cert; + + if (format == FORMAT_HTTP) { +#if !defined(OPENSSL_NO_OCSP) && !defined(OPENSSL_NO_SOCK) + load_cert_crl_http(file, &x, NULL); +#endif + return x; + } + + if (file == NULL) { + unbuffer(stdin); + cert = dup_bio_in(format); + } else + cert = bio_open_default(file, 'r', format); + if (cert == NULL) + goto end; + + if (format == FORMAT_ASN1) + x = d2i_X509_bio(cert, NULL); + else if (format == FORMAT_PEM) + x = PEM_read_bio_X509_AUX(cert, NULL, + (pem_password_cb *)password_callback, NULL); + else if (format == FORMAT_PKCS12) { + if (!load_pkcs12(cert, cert_descrip, NULL, NULL, NULL, &x, NULL)) + goto end; + } else { + BIO_printf(bio_err, "bad input format specified for %s\n", cert_descrip); + goto end; + } + end: + if (x == NULL) { + BIO_printf(bio_err, "unable to load certificate\n"); + ERR_print_errors(bio_err); + } + BIO_free(cert); + return (x); +} + +X509_CRL *load_crl(const char *infile, int format) +{ + X509_CRL *x = NULL; + BIO *in = NULL; + + if (format == FORMAT_HTTP) { +#if !defined(OPENSSL_NO_OCSP) && !defined(OPENSSL_NO_SOCK) + load_cert_crl_http(infile, NULL, &x); +#endif + return x; + } + + in = bio_open_default(infile, 'r', format); + if (in == NULL) + goto end; + if (format == FORMAT_ASN1) + x = d2i_X509_CRL_bio(in, NULL); + else if (format == FORMAT_PEM) + x = PEM_read_bio_X509_CRL(in, NULL, NULL, NULL); + else { + BIO_printf(bio_err, "bad input format specified for input crl\n"); + goto end; + } + if (x == NULL) { + BIO_printf(bio_err, "unable to load CRL\n"); + ERR_print_errors(bio_err); + goto end; + } + + end: + BIO_free(in); + return (x); +} + +EVP_PKEY *load_key(const char *file, int format, int maybe_stdin, + const char *pass, ENGINE *e, const char *key_descrip) +{ + BIO *key = NULL; + EVP_PKEY *pkey = NULL; + PW_CB_DATA cb_data; + + cb_data.password = pass; + cb_data.prompt_info = file; + + if (file == NULL && (!maybe_stdin || format == FORMAT_ENGINE)) { + BIO_printf(bio_err, "no keyfile specified\n"); + goto end; + } + if (format == FORMAT_ENGINE) { + if (e == NULL) + BIO_printf(bio_err, "no engine specified\n"); + else { +#ifndef OPENSSL_NO_ENGINE + if (ENGINE_init(e)) { + pkey = ENGINE_load_private_key(e, file, ui_method, &cb_data); + ENGINE_finish(e); + } + if (pkey == NULL) { + BIO_printf(bio_err, "cannot load %s from engine\n", key_descrip); + ERR_print_errors(bio_err); + } +#else + BIO_printf(bio_err, "engines not supported\n"); +#endif + } + goto end; + } + if (file == NULL && maybe_stdin) { + unbuffer(stdin); + key = dup_bio_in(format); + } else + key = bio_open_default(file, 'r', format); + if (key == NULL) + goto end; + if (format == FORMAT_ASN1) { + pkey = d2i_PrivateKey_bio(key, NULL); + } else if (format == FORMAT_PEM) { + pkey = PEM_read_bio_PrivateKey(key, NULL, + (pem_password_cb *)password_callback, + &cb_data); + } + else if (format == FORMAT_PKCS12) { + if (!load_pkcs12(key, key_descrip, + (pem_password_cb *)password_callback, &cb_data, + &pkey, NULL, NULL)) + goto end; + } +#if !defined(OPENSSL_NO_RSA) && !defined(OPENSSL_NO_DSA) && !defined (OPENSSL_NO_RC4) + else if (format == FORMAT_MSBLOB) + pkey = b2i_PrivateKey_bio(key); + else if (format == FORMAT_PVK) + pkey = b2i_PVK_bio(key, (pem_password_cb *)password_callback, + &cb_data); +#endif + else { + BIO_printf(bio_err, "bad input format specified for key file\n"); + goto end; + } + end: + BIO_free(key); + if (pkey == NULL) { + BIO_printf(bio_err, "unable to load %s\n", key_descrip); + ERR_print_errors(bio_err); + } + return (pkey); +} + +EVP_PKEY *load_pubkey(const char *file, int format, int maybe_stdin, + const char *pass, ENGINE *e, const char *key_descrip) +{ + BIO *key = NULL; + EVP_PKEY *pkey = NULL; + PW_CB_DATA cb_data; + + cb_data.password = pass; + cb_data.prompt_info = file; + + if (file == NULL && (!maybe_stdin || format == FORMAT_ENGINE)) { + BIO_printf(bio_err, "no keyfile specified\n"); + goto end; + } + if (format == FORMAT_ENGINE) { + if (e == NULL) + BIO_printf(bio_err, "no engine specified\n"); + else { +#ifndef OPENSSL_NO_ENGINE + pkey = ENGINE_load_public_key(e, file, ui_method, &cb_data); + if (pkey == NULL) { + BIO_printf(bio_err, "cannot load %s from engine\n", key_descrip); + ERR_print_errors(bio_err); + } +#else + BIO_printf(bio_err, "engines not supported\n"); +#endif + } + goto end; + } + if (file == NULL && maybe_stdin) { + unbuffer(stdin); + key = dup_bio_in(format); + } else + key = bio_open_default(file, 'r', format); + if (key == NULL) + goto end; + if (format == FORMAT_ASN1) { + pkey = d2i_PUBKEY_bio(key, NULL); + } + else if (format == FORMAT_ASN1RSA) { +#ifndef OPENSSL_NO_RSA + RSA *rsa; + rsa = d2i_RSAPublicKey_bio(key, NULL); + if (rsa) { + pkey = EVP_PKEY_new(); + if (pkey != NULL) + EVP_PKEY_set1_RSA(pkey, rsa); + RSA_free(rsa); + } else +#else + BIO_printf(bio_err, "RSA keys not supported\n"); +#endif + pkey = NULL; + } else if (format == FORMAT_PEMRSA) { +#ifndef OPENSSL_NO_RSA + RSA *rsa; + rsa = PEM_read_bio_RSAPublicKey(key, NULL, + (pem_password_cb *)password_callback, + &cb_data); + if (rsa != NULL) { + pkey = EVP_PKEY_new(); + if (pkey != NULL) + EVP_PKEY_set1_RSA(pkey, rsa); + RSA_free(rsa); + } else +#else + BIO_printf(bio_err, "RSA keys not supported\n"); +#endif + pkey = NULL; + } + else if (format == FORMAT_PEM) { + pkey = PEM_read_bio_PUBKEY(key, NULL, + (pem_password_cb *)password_callback, + &cb_data); + } +#if !defined(OPENSSL_NO_RSA) && !defined(OPENSSL_NO_DSA) + else if (format == FORMAT_MSBLOB) + pkey = b2i_PublicKey_bio(key); +#endif + end: + BIO_free(key); + if (pkey == NULL) + BIO_printf(bio_err, "unable to load %s\n", key_descrip); + return (pkey); +} + +static int load_certs_crls(const char *file, int format, + const char *pass, const char *desc, + STACK_OF(X509) **pcerts, + STACK_OF(X509_CRL) **pcrls) +{ + int i; + BIO *bio; + STACK_OF(X509_INFO) *xis = NULL; + X509_INFO *xi; + PW_CB_DATA cb_data; + int rv = 0; + + cb_data.password = pass; + cb_data.prompt_info = file; + + if (format != FORMAT_PEM) { + BIO_printf(bio_err, "bad input format specified for %s\n", desc); + return 0; + } + + bio = bio_open_default(file, 'r', FORMAT_PEM); + if (bio == NULL) + return 0; + + xis = PEM_X509_INFO_read_bio(bio, NULL, + (pem_password_cb *)password_callback, + &cb_data); + + BIO_free(bio); + + if (pcerts && *pcerts == NULL) { + *pcerts = sk_X509_new_null(); + if (!*pcerts) + goto end; + } + + if (pcrls && *pcrls == NULL) { + *pcrls = sk_X509_CRL_new_null(); + if (!*pcrls) + goto end; + } + + for (i = 0; i < sk_X509_INFO_num(xis); i++) { + xi = sk_X509_INFO_value(xis, i); + if (xi->x509 && pcerts) { + if (!sk_X509_push(*pcerts, xi->x509)) + goto end; + xi->x509 = NULL; + } + if (xi->crl && pcrls) { + if (!sk_X509_CRL_push(*pcrls, xi->crl)) + goto end; + xi->crl = NULL; + } + } + + if (pcerts && sk_X509_num(*pcerts) > 0) + rv = 1; + + if (pcrls && sk_X509_CRL_num(*pcrls) > 0) + rv = 1; + + end: + + sk_X509_INFO_pop_free(xis, X509_INFO_free); + + if (rv == 0) { + if (pcerts) { + sk_X509_pop_free(*pcerts, X509_free); + *pcerts = NULL; + } + if (pcrls) { + sk_X509_CRL_pop_free(*pcrls, X509_CRL_free); + *pcrls = NULL; + } + BIO_printf(bio_err, "unable to load %s\n", + pcerts ? "certificates" : "CRLs"); + ERR_print_errors(bio_err); + } + return rv; +} + +void* app_malloc(int sz, const char *what) +{ + void *vp = OPENSSL_malloc(sz); + + if (vp == NULL) { + BIO_printf(bio_err, "%s: Could not allocate %d bytes for %s\n", + opt_getprog(), sz, what); + ERR_print_errors(bio_err); + exit(1); + } + return vp; +} + +/* + * Initialize or extend, if *certs != NULL, a certificate stack. + */ +int load_certs(const char *file, STACK_OF(X509) **certs, int format, + const char *pass, const char *desc) +{ + return load_certs_crls(file, format, pass, desc, certs, NULL); +} + +/* + * Initialize or extend, if *crls != NULL, a certificate stack. + */ +int load_crls(const char *file, STACK_OF(X509_CRL) **crls, int format, + const char *pass, const char *desc) +{ + return load_certs_crls(file, format, pass, desc, NULL, crls); +} + +#define X509V3_EXT_UNKNOWN_MASK (0xfL << 16) +/* Return error for unknown extensions */ +#define X509V3_EXT_DEFAULT 0 +/* Print error for unknown extensions */ +#define X509V3_EXT_ERROR_UNKNOWN (1L << 16) +/* ASN1 parse unknown extensions */ +#define X509V3_EXT_PARSE_UNKNOWN (2L << 16) +/* BIO_dump unknown extensions */ +#define X509V3_EXT_DUMP_UNKNOWN (3L << 16) + +#define X509_FLAG_CA (X509_FLAG_NO_ISSUER | X509_FLAG_NO_PUBKEY | \ + X509_FLAG_NO_HEADER | X509_FLAG_NO_VERSION) + +int set_cert_ex(unsigned long *flags, const char *arg) +{ + static const NAME_EX_TBL cert_tbl[] = { + {"compatible", X509_FLAG_COMPAT, 0xffffffffl}, + {"ca_default", X509_FLAG_CA, 0xffffffffl}, + {"no_header", X509_FLAG_NO_HEADER, 0}, + {"no_version", X509_FLAG_NO_VERSION, 0}, + {"no_serial", X509_FLAG_NO_SERIAL, 0}, + {"no_signame", X509_FLAG_NO_SIGNAME, 0}, + {"no_validity", X509_FLAG_NO_VALIDITY, 0}, + {"no_subject", X509_FLAG_NO_SUBJECT, 0}, + {"no_issuer", X509_FLAG_NO_ISSUER, 0}, + {"no_pubkey", X509_FLAG_NO_PUBKEY, 0}, + {"no_extensions", X509_FLAG_NO_EXTENSIONS, 0}, + {"no_sigdump", X509_FLAG_NO_SIGDUMP, 0}, + {"no_aux", X509_FLAG_NO_AUX, 0}, + {"no_attributes", X509_FLAG_NO_ATTRIBUTES, 0}, + {"ext_default", X509V3_EXT_DEFAULT, X509V3_EXT_UNKNOWN_MASK}, + {"ext_error", X509V3_EXT_ERROR_UNKNOWN, X509V3_EXT_UNKNOWN_MASK}, + {"ext_parse", X509V3_EXT_PARSE_UNKNOWN, X509V3_EXT_UNKNOWN_MASK}, + {"ext_dump", X509V3_EXT_DUMP_UNKNOWN, X509V3_EXT_UNKNOWN_MASK}, + {NULL, 0, 0} + }; + return set_multi_opts(flags, arg, cert_tbl); +} + +int set_name_ex(unsigned long *flags, const char *arg) +{ + static const NAME_EX_TBL ex_tbl[] = { + {"esc_2253", ASN1_STRFLGS_ESC_2253, 0}, + {"esc_2254", ASN1_STRFLGS_ESC_2254, 0}, + {"esc_ctrl", ASN1_STRFLGS_ESC_CTRL, 0}, + {"esc_msb", ASN1_STRFLGS_ESC_MSB, 0}, + {"use_quote", ASN1_STRFLGS_ESC_QUOTE, 0}, + {"utf8", ASN1_STRFLGS_UTF8_CONVERT, 0}, + {"ignore_type", ASN1_STRFLGS_IGNORE_TYPE, 0}, + {"show_type", ASN1_STRFLGS_SHOW_TYPE, 0}, + {"dump_all", ASN1_STRFLGS_DUMP_ALL, 0}, + {"dump_nostr", ASN1_STRFLGS_DUMP_UNKNOWN, 0}, + {"dump_der", ASN1_STRFLGS_DUMP_DER, 0}, + {"compat", XN_FLAG_COMPAT, 0xffffffffL}, + {"sep_comma_plus", XN_FLAG_SEP_COMMA_PLUS, XN_FLAG_SEP_MASK}, + {"sep_comma_plus_space", XN_FLAG_SEP_CPLUS_SPC, XN_FLAG_SEP_MASK}, + {"sep_semi_plus_space", XN_FLAG_SEP_SPLUS_SPC, XN_FLAG_SEP_MASK}, + {"sep_multiline", XN_FLAG_SEP_MULTILINE, XN_FLAG_SEP_MASK}, + {"dn_rev", XN_FLAG_DN_REV, 0}, + {"nofname", XN_FLAG_FN_NONE, XN_FLAG_FN_MASK}, + {"sname", XN_FLAG_FN_SN, XN_FLAG_FN_MASK}, + {"lname", XN_FLAG_FN_LN, XN_FLAG_FN_MASK}, + {"align", XN_FLAG_FN_ALIGN, 0}, + {"oid", XN_FLAG_FN_OID, XN_FLAG_FN_MASK}, + {"space_eq", XN_FLAG_SPC_EQ, 0}, + {"dump_unknown", XN_FLAG_DUMP_UNKNOWN_FIELDS, 0}, + {"RFC2253", XN_FLAG_RFC2253, 0xffffffffL}, + {"oneline", XN_FLAG_ONELINE, 0xffffffffL}, + {"multiline", XN_FLAG_MULTILINE, 0xffffffffL}, + {"ca_default", XN_FLAG_MULTILINE, 0xffffffffL}, + {NULL, 0, 0} + }; + if (set_multi_opts(flags, arg, ex_tbl) == 0) + return 0; + if ((*flags & XN_FLAG_SEP_MASK) == 0) + *flags |= XN_FLAG_SEP_CPLUS_SPC; + return 1; +} + +int set_ext_copy(int *copy_type, const char *arg) +{ + if (strcasecmp(arg, "none") == 0) + *copy_type = EXT_COPY_NONE; + else if (strcasecmp(arg, "copy") == 0) + *copy_type = EXT_COPY_ADD; + else if (strcasecmp(arg, "copyall") == 0) + *copy_type = EXT_COPY_ALL; + else + return 0; + return 1; +} + +int copy_extensions(X509 *x, X509_REQ *req, int copy_type) +{ + STACK_OF(X509_EXTENSION) *exts = NULL; + X509_EXTENSION *ext, *tmpext; + ASN1_OBJECT *obj; + int i, idx, ret = 0; + if (!x || !req || (copy_type == EXT_COPY_NONE)) + return 1; + exts = X509_REQ_get_extensions(req); + + for (i = 0; i < sk_X509_EXTENSION_num(exts); i++) { + ext = sk_X509_EXTENSION_value(exts, i); + obj = X509_EXTENSION_get_object(ext); + idx = X509_get_ext_by_OBJ(x, obj, -1); + /* Does extension exist? */ + if (idx != -1) { + /* If normal copy don't override existing extension */ + if (copy_type == EXT_COPY_ADD) + continue; + /* Delete all extensions of same type */ + do { + tmpext = X509_get_ext(x, idx); + X509_delete_ext(x, idx); + X509_EXTENSION_free(tmpext); + idx = X509_get_ext_by_OBJ(x, obj, -1); + } while (idx != -1); + } + if (!X509_add_ext(x, ext, -1)) + goto end; + } + + ret = 1; + + end: + + sk_X509_EXTENSION_pop_free(exts, X509_EXTENSION_free); + + return ret; +} + +static int set_multi_opts(unsigned long *flags, const char *arg, + const NAME_EX_TBL * in_tbl) +{ + STACK_OF(CONF_VALUE) *vals; + CONF_VALUE *val; + int i, ret = 1; + if (!arg) + return 0; + vals = X509V3_parse_list(arg); + for (i = 0; i < sk_CONF_VALUE_num(vals); i++) { + val = sk_CONF_VALUE_value(vals, i); + if (!set_table_opts(flags, val->name, in_tbl)) + ret = 0; + } + sk_CONF_VALUE_pop_free(vals, X509V3_conf_free); + return ret; +} + +static int set_table_opts(unsigned long *flags, const char *arg, + const NAME_EX_TBL * in_tbl) +{ + char c; + const NAME_EX_TBL *ptbl; + c = arg[0]; + + if (c == '-') { + c = 0; + arg++; + } else if (c == '+') { + c = 1; + arg++; + } else + c = 1; + + for (ptbl = in_tbl; ptbl->name; ptbl++) { + if (strcasecmp(arg, ptbl->name) == 0) { + *flags &= ~ptbl->mask; + if (c) + *flags |= ptbl->flag; + else + *flags &= ~ptbl->flag; + return 1; + } + } + return 0; +} + +void print_name(BIO *out, const char *title, X509_NAME *nm, + unsigned long lflags) +{ + char *buf; + char mline = 0; + int indent = 0; + + if (title) + BIO_puts(out, title); + if ((lflags & XN_FLAG_SEP_MASK) == XN_FLAG_SEP_MULTILINE) { + mline = 1; + indent = 4; + } + if (lflags == XN_FLAG_COMPAT) { + buf = X509_NAME_oneline(nm, 0, 0); + BIO_puts(out, buf); + BIO_puts(out, "\n"); + OPENSSL_free(buf); + } else { + if (mline) + BIO_puts(out, "\n"); + X509_NAME_print_ex(out, nm, indent, lflags); + BIO_puts(out, "\n"); + } +} + +void print_bignum_var(BIO *out, const BIGNUM *in, const char *var, + int len, unsigned char *buffer) +{ + BIO_printf(out, " static unsigned char %s_%d[] = {", var, len); + if (BN_is_zero(in)) + BIO_printf(out, "\n\t0x00"); + else { + int i, l; + + l = BN_bn2bin(in, buffer); + for (i = 0; i < l; i++) { + if ((i % 10) == 0) + BIO_printf(out, "\n\t"); + if (i < l - 1) + BIO_printf(out, "0x%02X, ", buffer[i]); + else + BIO_printf(out, "0x%02X", buffer[i]); + } + } + BIO_printf(out, "\n };\n"); +} +void print_array(BIO *out, const char* title, int len, const unsigned char* d) +{ + int i; + + BIO_printf(out, "unsigned char %s[%d] = {", title, len); + for (i = 0; i < len; i++) { + if ((i % 10) == 0) + BIO_printf(out, "\n "); + if (i < len - 1) + BIO_printf(out, "0x%02X, ", d[i]); + else + BIO_printf(out, "0x%02X", d[i]); + } + BIO_printf(out, "\n};\n"); +} + +X509_STORE *setup_verify(const char *CAfile, const char *CApath, int noCAfile, int noCApath) +{ + X509_STORE *store = X509_STORE_new(); + X509_LOOKUP *lookup; + + if (store == NULL) + goto end; + + if (CAfile != NULL || !noCAfile) { + lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file()); + if (lookup == NULL) + goto end; + if (CAfile) { + if (!X509_LOOKUP_load_file(lookup, CAfile, X509_FILETYPE_PEM)) { + BIO_printf(bio_err, "Error loading file %s\n", CAfile); + goto end; + } + } else + X509_LOOKUP_load_file(lookup, NULL, X509_FILETYPE_DEFAULT); + } + + if (CApath != NULL || !noCApath) { + lookup = X509_STORE_add_lookup(store, X509_LOOKUP_hash_dir()); + if (lookup == NULL) + goto end; + if (CApath) { + if (!X509_LOOKUP_add_dir(lookup, CApath, X509_FILETYPE_PEM)) { + BIO_printf(bio_err, "Error loading directory %s\n", CApath); + goto end; + } + } else + X509_LOOKUP_add_dir(lookup, NULL, X509_FILETYPE_DEFAULT); + } + + ERR_clear_error(); + return store; + end: + X509_STORE_free(store); + return NULL; +} + +#ifndef OPENSSL_NO_ENGINE +/* Try to load an engine in a shareable library */ +static ENGINE *try_load_engine(const char *engine) +{ + ENGINE *e = ENGINE_by_id("dynamic"); + if (e) { + if (!ENGINE_ctrl_cmd_string(e, "SO_PATH", engine, 0) + || !ENGINE_ctrl_cmd_string(e, "LOAD", NULL, 0)) { + ENGINE_free(e); + e = NULL; + } + } + return e; +} +#endif + +ENGINE *setup_engine(const char *engine, int debug) +{ + ENGINE *e = NULL; + +#ifndef OPENSSL_NO_ENGINE + if (engine) { + if (strcmp(engine, "auto") == 0) { + BIO_printf(bio_err, "enabling auto ENGINE support\n"); + ENGINE_register_all_complete(); + return NULL; + } + if ((e = ENGINE_by_id(engine)) == NULL + && (e = try_load_engine(engine)) == NULL) { + BIO_printf(bio_err, "invalid engine \"%s\"\n", engine); + ERR_print_errors(bio_err); + return NULL; + } + if (debug) { + ENGINE_ctrl(e, ENGINE_CTRL_SET_LOGSTREAM, 0, bio_err, 0); + } + ENGINE_ctrl_cmd(e, "SET_USER_INTERFACE", 0, ui_method, 0, 1); + if (!ENGINE_set_default(e, ENGINE_METHOD_ALL)) { + BIO_printf(bio_err, "can't use that engine\n"); + ERR_print_errors(bio_err); + ENGINE_free(e); + return NULL; + } + + // GmSSL add here! + ENGINE_add_conf_module(); + BIO_printf(bio_err, "engine \"%s\" set.\n", ENGINE_get_id(e)); + } +#endif + return e; +} + +void release_engine(ENGINE *e) +{ +#ifndef OPENSSL_NO_ENGINE + if (e != NULL) + /* Free our "structural" reference. */ + ENGINE_free(e); +#endif +} + +static unsigned long index_serial_hash(const OPENSSL_CSTRING *a) +{ + const char *n; + + n = a[DB_serial]; + while (*n == '0') + n++; + return OPENSSL_LH_strhash(n); +} + +static int index_serial_cmp(const OPENSSL_CSTRING *a, + const OPENSSL_CSTRING *b) +{ + const char *aa, *bb; + + for (aa = a[DB_serial]; *aa == '0'; aa++) ; + for (bb = b[DB_serial]; *bb == '0'; bb++) ; + return (strcmp(aa, bb)); +} + +static int index_name_qual(char **a) +{ + return (a[0][0] == 'V'); +} + +static unsigned long index_name_hash(const OPENSSL_CSTRING *a) +{ + return OPENSSL_LH_strhash(a[DB_name]); +} + +int index_name_cmp(const OPENSSL_CSTRING *a, const OPENSSL_CSTRING *b) +{ + return (strcmp(a[DB_name], b[DB_name])); +} + +static IMPLEMENT_LHASH_HASH_FN(index_serial, OPENSSL_CSTRING) +static IMPLEMENT_LHASH_COMP_FN(index_serial, OPENSSL_CSTRING) +static IMPLEMENT_LHASH_HASH_FN(index_name, OPENSSL_CSTRING) +static IMPLEMENT_LHASH_COMP_FN(index_name, OPENSSL_CSTRING) +#undef BSIZE +#define BSIZE 256 +BIGNUM *load_serial(const char *serialfile, int create, ASN1_INTEGER **retai) +{ + BIO *in = NULL; + BIGNUM *ret = NULL; + char buf[1024]; + ASN1_INTEGER *ai = NULL; + + ai = ASN1_INTEGER_new(); + if (ai == NULL) + goto err; + + in = BIO_new_file(serialfile, "r"); + if (in == NULL) { + if (!create) { + perror(serialfile); + goto err; + } + ERR_clear_error(); + ret = BN_new(); + if (ret == NULL || !rand_serial(ret, ai)) + BIO_printf(bio_err, "Out of memory\n"); + } else { + if (!a2i_ASN1_INTEGER(in, ai, buf, 1024)) { + BIO_printf(bio_err, "unable to load number from %s\n", + serialfile); + goto err; + } + ret = ASN1_INTEGER_to_BN(ai, NULL); + if (ret == NULL) { + BIO_printf(bio_err, + "error converting number from bin to BIGNUM\n"); + goto err; + } + } + + if (ret && retai) { + *retai = ai; + ai = NULL; + } + err: + BIO_free(in); + ASN1_INTEGER_free(ai); + return (ret); +} + +int save_serial(const char *serialfile, const char *suffix, const BIGNUM *serial, + ASN1_INTEGER **retai) +{ + char buf[1][BSIZE]; + BIO *out = NULL; + int ret = 0; + ASN1_INTEGER *ai = NULL; + int j; + + if (suffix == NULL) + j = strlen(serialfile); + else + j = strlen(serialfile) + strlen(suffix) + 1; + if (j >= BSIZE) { + BIO_printf(bio_err, "file name too long\n"); + goto err; + } + + if (suffix == NULL) + OPENSSL_strlcpy(buf[0], serialfile, BSIZE); + else { +#ifndef OPENSSL_SYS_VMS + j = BIO_snprintf(buf[0], sizeof buf[0], "%s.%s", serialfile, suffix); +#else + j = BIO_snprintf(buf[0], sizeof buf[0], "%s-%s", serialfile, suffix); +#endif + } + out = BIO_new_file(buf[0], "w"); + if (out == NULL) { + ERR_print_errors(bio_err); + goto err; + } + + if ((ai = BN_to_ASN1_INTEGER(serial, NULL)) == NULL) { + BIO_printf(bio_err, "error converting serial to ASN.1 format\n"); + goto err; + } + i2a_ASN1_INTEGER(out, ai); + BIO_puts(out, "\n"); + ret = 1; + if (retai) { + *retai = ai; + ai = NULL; + } + err: + BIO_free_all(out); + ASN1_INTEGER_free(ai); + return (ret); +} + +int rotate_serial(const char *serialfile, const char *new_suffix, + const char *old_suffix) +{ + char buf[2][BSIZE]; + int i, j; + + i = strlen(serialfile) + strlen(old_suffix); + j = strlen(serialfile) + strlen(new_suffix); + if (i > j) + j = i; + if (j + 1 >= BSIZE) { + BIO_printf(bio_err, "file name too long\n"); + goto err; + } +#ifndef OPENSSL_SYS_VMS + j = BIO_snprintf(buf[0], sizeof buf[0], "%s.%s", serialfile, new_suffix); + j = BIO_snprintf(buf[1], sizeof buf[1], "%s.%s", serialfile, old_suffix); +#else + j = BIO_snprintf(buf[0], sizeof buf[0], "%s-%s", serialfile, new_suffix); + j = BIO_snprintf(buf[1], sizeof buf[1], "%s-%s", serialfile, old_suffix); +#endif + if (rename(serialfile, buf[1]) < 0 && errno != ENOENT +#ifdef ENOTDIR + && errno != ENOTDIR +#endif + ) { + BIO_printf(bio_err, + "unable to rename %s to %s\n", serialfile, buf[1]); + perror("reason"); + goto err; + } + if (rename(buf[0], serialfile) < 0) { + BIO_printf(bio_err, + "unable to rename %s to %s\n", buf[0], serialfile); + perror("reason"); + rename(buf[1], serialfile); + goto err; + } + return 1; + err: + return 0; +} + +int rand_serial(BIGNUM *b, ASN1_INTEGER *ai) +{ + BIGNUM *btmp; + int ret = 0; + + if (b) + btmp = b; + else + btmp = BN_new(); + + if (btmp == NULL) + return 0; + + if (!BN_pseudo_rand(btmp, SERIAL_RAND_BITS, 0, 0)) + goto error; + if (ai && !BN_to_ASN1_INTEGER(btmp, ai)) + goto error; + + ret = 1; + + error: + + if (btmp != b) + BN_free(btmp); + + return ret; +} + +CA_DB *load_index(const char *dbfile, DB_ATTR *db_attr) +{ + CA_DB *retdb = NULL; + TXT_DB *tmpdb = NULL; + BIO *in; + CONF *dbattr_conf = NULL; + char buf[BSIZE]; + + in = BIO_new_file(dbfile, "r"); + if (in == NULL) { + ERR_print_errors(bio_err); + goto err; + } + if ((tmpdb = TXT_DB_read(in, DB_NUMBER)) == NULL) + goto err; + +#ifndef OPENSSL_SYS_VMS + BIO_snprintf(buf, sizeof buf, "%s.attr", dbfile); +#else + BIO_snprintf(buf, sizeof buf, "%s-attr", dbfile); +#endif + dbattr_conf = app_load_config(buf); + + retdb = app_malloc(sizeof(*retdb), "new DB"); + retdb->db = tmpdb; + tmpdb = NULL; + if (db_attr) + retdb->attributes = *db_attr; + else { + retdb->attributes.unique_subject = 1; + } + + if (dbattr_conf) { + char *p = NCONF_get_string(dbattr_conf, NULL, "unique_subject"); + if (p) { + retdb->attributes.unique_subject = parse_yesno(p, 1); + } + } + + err: + NCONF_free(dbattr_conf); + TXT_DB_free(tmpdb); + BIO_free_all(in); + return retdb; +} + +int index_index(CA_DB *db) +{ + if (!TXT_DB_create_index(db->db, DB_serial, NULL, + LHASH_HASH_FN(index_serial), + LHASH_COMP_FN(index_serial))) { + BIO_printf(bio_err, + "error creating serial number index:(%ld,%ld,%ld)\n", + db->db->error, db->db->arg1, db->db->arg2); + return 0; + } + + if (db->attributes.unique_subject + && !TXT_DB_create_index(db->db, DB_name, index_name_qual, + LHASH_HASH_FN(index_name), + LHASH_COMP_FN(index_name))) { + BIO_printf(bio_err, "error creating name index:(%ld,%ld,%ld)\n", + db->db->error, db->db->arg1, db->db->arg2); + return 0; + } + return 1; +} + +int save_index(const char *dbfile, const char *suffix, CA_DB *db) +{ + char buf[3][BSIZE]; + BIO *out; + int j; + + j = strlen(dbfile) + strlen(suffix); + if (j + 6 >= BSIZE) { + BIO_printf(bio_err, "file name too long\n"); + goto err; + } +#ifndef OPENSSL_SYS_VMS + j = BIO_snprintf(buf[2], sizeof buf[2], "%s.attr", dbfile); + j = BIO_snprintf(buf[1], sizeof buf[1], "%s.attr.%s", dbfile, suffix); + j = BIO_snprintf(buf[0], sizeof buf[0], "%s.%s", dbfile, suffix); +#else + j = BIO_snprintf(buf[2], sizeof buf[2], "%s-attr", dbfile); + j = BIO_snprintf(buf[1], sizeof buf[1], "%s-attr-%s", dbfile, suffix); + j = BIO_snprintf(buf[0], sizeof buf[0], "%s-%s", dbfile, suffix); +#endif + out = BIO_new_file(buf[0], "w"); + if (out == NULL) { + perror(dbfile); + BIO_printf(bio_err, "unable to open '%s'\n", dbfile); + goto err; + } + j = TXT_DB_write(out, db->db); + BIO_free(out); + if (j <= 0) + goto err; + + out = BIO_new_file(buf[1], "w"); + if (out == NULL) { + perror(buf[2]); + BIO_printf(bio_err, "unable to open '%s'\n", buf[2]); + goto err; + } + BIO_printf(out, "unique_subject = %s\n", + db->attributes.unique_subject ? "yes" : "no"); + BIO_free(out); + + return 1; + err: + return 0; +} + +int rotate_index(const char *dbfile, const char *new_suffix, + const char *old_suffix) +{ + char buf[5][BSIZE]; + int i, j; + + i = strlen(dbfile) + strlen(old_suffix); + j = strlen(dbfile) + strlen(new_suffix); + if (i > j) + j = i; + if (j + 6 >= BSIZE) { + BIO_printf(bio_err, "file name too long\n"); + goto err; + } +#ifndef OPENSSL_SYS_VMS + j = BIO_snprintf(buf[4], sizeof buf[4], "%s.attr", dbfile); + j = BIO_snprintf(buf[3], sizeof buf[3], "%s.attr.%s", dbfile, old_suffix); + j = BIO_snprintf(buf[2], sizeof buf[2], "%s.attr.%s", dbfile, new_suffix); + j = BIO_snprintf(buf[1], sizeof buf[1], "%s.%s", dbfile, old_suffix); + j = BIO_snprintf(buf[0], sizeof buf[0], "%s.%s", dbfile, new_suffix); +#else + j = BIO_snprintf(buf[4], sizeof buf[4], "%s-attr", dbfile); + j = BIO_snprintf(buf[3], sizeof buf[3], "%s-attr-%s", dbfile, old_suffix); + j = BIO_snprintf(buf[2], sizeof buf[2], "%s-attr-%s", dbfile, new_suffix); + j = BIO_snprintf(buf[1], sizeof buf[1], "%s-%s", dbfile, old_suffix); + j = BIO_snprintf(buf[0], sizeof buf[0], "%s-%s", dbfile, new_suffix); +#endif + if (rename(dbfile, buf[1]) < 0 && errno != ENOENT +#ifdef ENOTDIR + && errno != ENOTDIR +#endif + ) { + BIO_printf(bio_err, "unable to rename %s to %s\n", dbfile, buf[1]); + perror("reason"); + goto err; + } + if (rename(buf[0], dbfile) < 0) { + BIO_printf(bio_err, "unable to rename %s to %s\n", buf[0], dbfile); + perror("reason"); + rename(buf[1], dbfile); + goto err; + } + if (rename(buf[4], buf[3]) < 0 && errno != ENOENT +#ifdef ENOTDIR + && errno != ENOTDIR +#endif + ) { + BIO_printf(bio_err, "unable to rename %s to %s\n", buf[4], buf[3]); + perror("reason"); + rename(dbfile, buf[0]); + rename(buf[1], dbfile); + goto err; + } + if (rename(buf[2], buf[4]) < 0) { + BIO_printf(bio_err, "unable to rename %s to %s\n", buf[2], buf[4]); + perror("reason"); + rename(buf[3], buf[4]); + rename(dbfile, buf[0]); + rename(buf[1], dbfile); + goto err; + } + return 1; + err: + return 0; +} + +void free_index(CA_DB *db) +{ + if (db) { + TXT_DB_free(db->db); + OPENSSL_free(db); + } +} + +int parse_yesno(const char *str, int def) +{ + if (str) { + switch (*str) { + case 'f': /* false */ + case 'F': /* FALSE */ + case 'n': /* no */ + case 'N': /* NO */ + case '0': /* 0 */ + return 0; + case 't': /* true */ + case 'T': /* TRUE */ + case 'y': /* yes */ + case 'Y': /* YES */ + case '1': /* 1 */ + return 1; + } + } + return def; +} + +/* + * name is expected to be in the format /type0=value0/type1=value1/type2=... + * where characters may be escaped by \ + */ +X509_NAME *parse_name(const char *cp, long chtype, int canmulti) +{ + int nextismulti = 0; + char *work; + X509_NAME *n; + + if (*cp++ != '/') + return NULL; + + n = X509_NAME_new(); + if (n == NULL) + return NULL; + work = OPENSSL_strdup(cp); + if (work == NULL) + goto err; + + while (*cp) { + char *bp = work; + char *typestr = bp; + unsigned char *valstr; + int nid; + int ismulti = nextismulti; + nextismulti = 0; + + /* Collect the type */ + while (*cp && *cp != '=') + *bp++ = *cp++; + if (*cp == '\0') { + BIO_printf(bio_err, + "%s: Hit end of string before finding the equals.\n", + opt_getprog()); + goto err; + } + *bp++ = '\0'; + ++cp; + + /* Collect the value. */ + valstr = (unsigned char *)bp; + for (; *cp && *cp != '/'; *bp++ = *cp++) { + if (canmulti && *cp == '+') { + nextismulti = 1; + break; + } + if (*cp == '\\' && *++cp == '\0') { + BIO_printf(bio_err, + "%s: escape character at end of string\n", + opt_getprog()); + goto err; + } + } + *bp++ = '\0'; + + /* If not at EOS (must be + or /), move forward. */ + if (*cp) + ++cp; + + /* Parse */ + nid = OBJ_txt2nid(typestr); + if (nid == NID_undef) { + BIO_printf(bio_err, "%s: Skipping unknown attribute \"%s\"\n", + opt_getprog(), typestr); + continue; + } + if (!X509_NAME_add_entry_by_NID(n, nid, chtype, + valstr, strlen((char *)valstr), + -1, ismulti ? -1 : 0)) + goto err; + } + + OPENSSL_free(work); + return n; + + err: + X509_NAME_free(n); + OPENSSL_free(work); + return NULL; +} + +/* + * Read whole contents of a BIO into an allocated memory buffer and return + * it. + */ + +int bio_to_mem(unsigned char **out, int maxlen, BIO *in) +{ + BIO *mem; + int len, ret; + unsigned char tbuf[1024]; + + mem = BIO_new(BIO_s_mem()); + if (mem == NULL) + return -1; + for (;;) { + if ((maxlen != -1) && maxlen < 1024) + len = maxlen; + else + len = 1024; + len = BIO_read(in, tbuf, len); + if (len < 0) { + BIO_free(mem); + return -1; + } + if (len == 0) + break; + if (BIO_write(mem, tbuf, len) != len) { + BIO_free(mem); + return -1; + } + maxlen -= len; + + if (maxlen == 0) + break; + } + ret = BIO_get_mem_data(mem, (char **)out); + BIO_set_flags(mem, BIO_FLAGS_MEM_RDONLY); + BIO_free(mem); + return ret; +} + +int pkey_ctrl_string(EVP_PKEY_CTX *ctx, const char *value) +{ + int rv; + char *stmp, *vtmp = NULL; + stmp = OPENSSL_strdup(value); + if (!stmp) + return -1; + vtmp = strchr(stmp, ':'); + if (vtmp) { + *vtmp = 0; + vtmp++; + } + rv = EVP_PKEY_CTX_ctrl_str(ctx, stmp, vtmp); + OPENSSL_free(stmp); + return rv; +} + +static void nodes_print(const char *name, STACK_OF(X509_POLICY_NODE) *nodes) +{ + X509_POLICY_NODE *node; + int i; + + BIO_printf(bio_err, "%s Policies:", name); + if (nodes) { + BIO_puts(bio_err, "\n"); + for (i = 0; i < sk_X509_POLICY_NODE_num(nodes); i++) { + node = sk_X509_POLICY_NODE_value(nodes, i); + X509_POLICY_NODE_print(bio_err, node, 2); + } + } else + BIO_puts(bio_err, " \n"); +} + +void policies_print(X509_STORE_CTX *ctx) +{ + X509_POLICY_TREE *tree; + int explicit_policy; + tree = X509_STORE_CTX_get0_policy_tree(ctx); + explicit_policy = X509_STORE_CTX_get_explicit_policy(ctx); + + BIO_printf(bio_err, "Require explicit Policy: %s\n", + explicit_policy ? "True" : "False"); + + nodes_print("Authority", X509_policy_tree_get0_policies(tree)); + nodes_print("User", X509_policy_tree_get0_user_policies(tree)); +} + +/*- + * next_protos_parse parses a comma separated list of strings into a string + * in a format suitable for passing to SSL_CTX_set_next_protos_advertised. + * outlen: (output) set to the length of the resulting buffer on success. + * err: (maybe NULL) on failure, an error message line is written to this BIO. + * in: a NUL terminated string like "abc,def,ghi" + * + * returns: a malloc'd buffer or NULL on failure. + */ +unsigned char *next_protos_parse(size_t *outlen, const char *in) +{ + size_t len; + unsigned char *out; + size_t i, start = 0; + + len = strlen(in); + if (len >= 65535) + return NULL; + + out = app_malloc(strlen(in) + 1, "NPN buffer"); + for (i = 0; i <= len; ++i) { + if (i == len || in[i] == ',') { + if (i - start > 255) { + OPENSSL_free(out); + return NULL; + } + out[start] = i - start; + start = i + 1; + } else + out[i + 1] = in[i]; + } + + *outlen = len + 1; + return out; +} + +void print_cert_checks(BIO *bio, X509 *x, + const char *checkhost, + const char *checkemail, const char *checkip) +{ + if (x == NULL) + return; + if (checkhost) { + BIO_printf(bio, "Hostname %s does%s match certificate\n", + checkhost, + X509_check_host(x, checkhost, 0, 0, NULL) == 1 + ? "" : " NOT"); + } + + if (checkemail) { + BIO_printf(bio, "Email %s does%s match certificate\n", + checkemail, X509_check_email(x, checkemail, 0, 0) + ? "" : " NOT"); + } + + if (checkip) { + BIO_printf(bio, "IP %s does%s match certificate\n", + checkip, X509_check_ip_asc(x, checkip, 0) ? "" : " NOT"); + } +} + +/* Get first http URL from a DIST_POINT structure */ + +static const char *get_dp_url(DIST_POINT *dp) +{ + GENERAL_NAMES *gens; + GENERAL_NAME *gen; + int i, gtype; + ASN1_STRING *uri; + if (!dp->distpoint || dp->distpoint->type != 0) + return NULL; + gens = dp->distpoint->name.fullname; + for (i = 0; i < sk_GENERAL_NAME_num(gens); i++) { + gen = sk_GENERAL_NAME_value(gens, i); + uri = GENERAL_NAME_get0_value(gen, >ype); + if (gtype == GEN_URI && ASN1_STRING_length(uri) > 6) { + const char *uptr = (const char *)ASN1_STRING_get0_data(uri); + if (strncmp(uptr, "http://", 7) == 0) + return uptr; + } + } + return NULL; +} + +/* + * Look through a CRLDP structure and attempt to find an http URL to + * downloads a CRL from. + */ + +static X509_CRL *load_crl_crldp(STACK_OF(DIST_POINT) *crldp) +{ + int i; + const char *urlptr = NULL; + for (i = 0; i < sk_DIST_POINT_num(crldp); i++) { + DIST_POINT *dp = sk_DIST_POINT_value(crldp, i); + urlptr = get_dp_url(dp); + if (urlptr) + return load_crl(urlptr, FORMAT_HTTP); + } + return NULL; +} + +/* + * Example of downloading CRLs from CRLDP: not usable for real world as it + * always downloads, doesn't support non-blocking I/O and doesn't cache + * anything. + */ + +static STACK_OF(X509_CRL) *crls_http_cb(X509_STORE_CTX *ctx, X509_NAME *nm) +{ + X509 *x; + STACK_OF(X509_CRL) *crls = NULL; + X509_CRL *crl; + STACK_OF(DIST_POINT) *crldp; + + crls = sk_X509_CRL_new_null(); + if (!crls) + return NULL; + x = X509_STORE_CTX_get_current_cert(ctx); + crldp = X509_get_ext_d2i(x, NID_crl_distribution_points, NULL, NULL); + crl = load_crl_crldp(crldp); + sk_DIST_POINT_pop_free(crldp, DIST_POINT_free); + if (!crl) { + sk_X509_CRL_free(crls); + return NULL; + } + sk_X509_CRL_push(crls, crl); + /* Try to download delta CRL */ + crldp = X509_get_ext_d2i(x, NID_freshest_crl, NULL, NULL); + crl = load_crl_crldp(crldp); + sk_DIST_POINT_pop_free(crldp, DIST_POINT_free); + if (crl) + sk_X509_CRL_push(crls, crl); + return crls; +} + +void store_setup_crl_download(X509_STORE *st) +{ + X509_STORE_set_lookup_crls_cb(st, crls_http_cb); +} + +/* + * Platform-specific sections + */ +#if defined(_WIN32) +# ifdef fileno +# undef fileno +# define fileno(a) (int)_fileno(a) +# endif + +# include +# include + +static int WIN32_rename(const char *from, const char *to) +{ + TCHAR *tfrom = NULL, *tto; + DWORD err; + int ret = 0; + + if (sizeof(TCHAR) == 1) { + tfrom = (TCHAR *)from; + tto = (TCHAR *)to; + } else { /* UNICODE path */ + + size_t i, flen = strlen(from) + 1, tlen = strlen(to) + 1; + tfrom = malloc(sizeof(*tfrom) * (flen + tlen)); + if (tfrom == NULL) + goto err; + tto = tfrom + flen; +# if !defined(_WIN32_WCE) || _WIN32_WCE>=101 + if (!MultiByteToWideChar(CP_ACP, 0, from, flen, (WCHAR *)tfrom, flen)) +# endif + for (i = 0; i < flen; i++) + tfrom[i] = (TCHAR)from[i]; +# if !defined(_WIN32_WCE) || _WIN32_WCE>=101 + if (!MultiByteToWideChar(CP_ACP, 0, to, tlen, (WCHAR *)tto, tlen)) +# endif + for (i = 0; i < tlen; i++) + tto[i] = (TCHAR)to[i]; + } + + if (MoveFile(tfrom, tto)) + goto ok; + err = GetLastError(); + if (err == ERROR_ALREADY_EXISTS || err == ERROR_FILE_EXISTS) { + if (DeleteFile(tto) && MoveFile(tfrom, tto)) + goto ok; + err = GetLastError(); + } + if (err == ERROR_FILE_NOT_FOUND || err == ERROR_PATH_NOT_FOUND) + errno = ENOENT; + else if (err == ERROR_ACCESS_DENIED) + errno = EACCES; + else + errno = EINVAL; /* we could map more codes... */ + err: + ret = -1; + ok: + if (tfrom != NULL && tfrom != (TCHAR *)from) + free(tfrom); + return ret; +} +#endif + +/* app_tminterval section */ +#if defined(_WIN32) +double app_tminterval(int stop, int usertime) +{ + FILETIME now; + double ret = 0; + static ULARGE_INTEGER tmstart; + static int warning = 1; +# ifdef _WIN32_WINNT + static HANDLE proc = NULL; + + if (proc == NULL) { + if (check_winnt()) + proc = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, + GetCurrentProcessId()); + if (proc == NULL) + proc = (HANDLE) - 1; + } + + if (usertime && proc != (HANDLE) - 1) { + FILETIME junk; + GetProcessTimes(proc, &junk, &junk, &junk, &now); + } else +# endif + { + SYSTEMTIME systime; + + if (usertime && warning) { + BIO_printf(bio_err, "To get meaningful results, run " + "this program on idle system.\n"); + warning = 0; + } + GetSystemTime(&systime); + SystemTimeToFileTime(&systime, &now); + } + + if (stop == TM_START) { + tmstart.u.LowPart = now.dwLowDateTime; + tmstart.u.HighPart = now.dwHighDateTime; + } else { + ULARGE_INTEGER tmstop; + + tmstop.u.LowPart = now.dwLowDateTime; + tmstop.u.HighPart = now.dwHighDateTime; + + ret = (__int64)(tmstop.QuadPart - tmstart.QuadPart) * 1e-7; + } + + return (ret); +} +#elif defined(OPENSSL_SYSTEM_VXWORKS) +# include + +double app_tminterval(int stop, int usertime) +{ + double ret = 0; +# ifdef CLOCK_REALTIME + static struct timespec tmstart; + struct timespec now; +# else + static unsigned long tmstart; + unsigned long now; +# endif + static int warning = 1; + + if (usertime && warning) { + BIO_printf(bio_err, "To get meaningful results, run " + "this program on idle system.\n"); + warning = 0; + } +# ifdef CLOCK_REALTIME + clock_gettime(CLOCK_REALTIME, &now); + if (stop == TM_START) + tmstart = now; + else + ret = ((now.tv_sec + now.tv_nsec * 1e-9) + - (tmstart.tv_sec + tmstart.tv_nsec * 1e-9)); +# else + now = tickGet(); + if (stop == TM_START) + tmstart = now; + else + ret = (now - tmstart) / (double)sysClkRateGet(); +# endif + return (ret); +} + +#elif defined(OPENSSL_SYSTEM_VMS) +# include +# include + +double app_tminterval(int stop, int usertime) +{ + static clock_t tmstart; + double ret = 0; + clock_t now; +# ifdef __TMS + struct tms rus; + + now = times(&rus); + if (usertime) + now = rus.tms_utime; +# else + if (usertime) + now = clock(); /* sum of user and kernel times */ + else { + struct timeval tv; + gettimeofday(&tv, NULL); + now = (clock_t)((unsigned long long)tv.tv_sec * CLK_TCK + + (unsigned long long)tv.tv_usec * (1000000 / CLK_TCK) + ); + } +# endif + if (stop == TM_START) + tmstart = now; + else + ret = (now - tmstart) / (double)(CLK_TCK); + + return (ret); +} + +#elif defined(_SC_CLK_TCK) /* by means of unistd.h */ +# include + +double app_tminterval(int stop, int usertime) +{ + double ret = 0; + struct tms rus; + clock_t now = times(&rus); + static clock_t tmstart; + + if (usertime) + now = rus.tms_utime; + + if (stop == TM_START) + tmstart = now; + else { + long int tck = sysconf(_SC_CLK_TCK); + ret = (now - tmstart) / (double)tck; + } + + return (ret); +} + +#else +# include +# include + +double app_tminterval(int stop, int usertime) +{ + double ret = 0; + struct rusage rus; + struct timeval now; + static struct timeval tmstart; + + if (usertime) + getrusage(RUSAGE_SELF, &rus), now = rus.ru_utime; + else + gettimeofday(&now, NULL); + + if (stop == TM_START) + tmstart = now; + else + ret = ((now.tv_sec + now.tv_usec * 1e-6) + - (tmstart.tv_sec + tmstart.tv_usec * 1e-6)); + + return ret; +} +#endif + +int app_access(const char* name, int flag) +{ +#ifdef _WIN32 + return _access(name, flag); +#else + return access(name, flag); +#endif +} + +/* app_isdir section */ +#ifdef _WIN32 +int app_isdir(const char *name) +{ + HANDLE hList; + WIN32_FIND_DATA FileData; +# if defined(UNICODE) || defined(_UNICODE) + size_t i, len_0 = strlen(name) + 1; + + if (len_0 > OSSL_NELEM(FileData.cFileName)) + return -1; + +# if !defined(_WIN32_WCE) || _WIN32_WCE>=101 + if (!MultiByteToWideChar + (CP_ACP, 0, name, len_0, FileData.cFileName, len_0)) +# endif + for (i = 0; i < len_0; i++) + FileData.cFileName[i] = (WCHAR)name[i]; + + hList = FindFirstFile(FileData.cFileName, &FileData); +# else + hList = FindFirstFile(name, &FileData); +# endif + if (hList == INVALID_HANDLE_VALUE) + return -1; + FindClose(hList); + return ((FileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0); +} +#else +# include +# ifndef S_ISDIR +# if defined(_S_IFMT) && defined(_S_IFDIR) +# define S_ISDIR(a) (((a) & _S_IFMT) == _S_IFDIR) +# else +# define S_ISDIR(a) (((a) & S_IFMT) == S_IFDIR) +# endif +# endif + +int app_isdir(const char *name) +{ +# if defined(S_ISDIR) + struct stat st; + + if (stat(name, &st) == 0) + return S_ISDIR(st.st_mode); + else + return -1; +# else + return -1; +# endif +} +#endif + +/* raw_read|write section */ +#if defined(__VMS) +# include "vms_term_sock.h" +static int stdin_sock = -1; + +static void close_stdin_sock(void) +{ + TerminalSocket (TERM_SOCK_DELETE, &stdin_sock); +} + +int fileno_stdin(void) +{ + if (stdin_sock == -1) { + TerminalSocket(TERM_SOCK_CREATE, &stdin_sock); + atexit(close_stdin_sock); + } + + return stdin_sock; +} +#else +int fileno_stdin(void) +{ + return fileno(stdin); +} +#endif + +int fileno_stdout(void) +{ + return fileno(stdout); +} + +#if defined(_WIN32) && defined(STD_INPUT_HANDLE) +int raw_read_stdin(void *buf, int siz) +{ + DWORD n; + if (ReadFile(GetStdHandle(STD_INPUT_HANDLE), buf, siz, &n, NULL)) + return (n); + else + return (-1); +} +#elif defined(__VMS) +#include + +int raw_read_stdin(void *buf, int siz) +{ + return recv(fileno_stdin(), buf, siz, 0); +} +#else +int raw_read_stdin(void *buf, int siz) +{ + return read(fileno_stdin(), buf, siz); +} +#endif + +#if defined(_WIN32) && defined(STD_OUTPUT_HANDLE) +int raw_write_stdout(const void *buf, int siz) +{ + DWORD n; + if (WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), buf, siz, &n, NULL)) + return (n); + else + return (-1); +} +#else +int raw_write_stdout(const void *buf, int siz) +{ + return write(fileno_stdout(), buf, siz); +} +#endif + +/* + * Centralized handling if input and output files with format specification + * The format is meant to show what the input and output is supposed to be, + * and is therefore a show of intent more than anything else. However, it + * does impact behavior on some platform, such as differentiating between + * text and binary input/output on non-Unix platforms + */ +static int istext(int format) +{ + return (format & B_FORMAT_TEXT) == B_FORMAT_TEXT; +} + +BIO *dup_bio_in(int format) +{ + return BIO_new_fp(stdin, + BIO_NOCLOSE | (istext(format) ? BIO_FP_TEXT : 0)); +} + +BIO *dup_bio_out(int format) +{ + BIO *b = BIO_new_fp(stdout, + BIO_NOCLOSE | (istext(format) ? BIO_FP_TEXT : 0)); +#ifdef OPENSSL_SYS_VMS + if (istext(format)) + b = BIO_push(BIO_new(BIO_f_linebuffer()), b); +#endif + return b; +} + +BIO *dup_bio_err(int format) +{ + BIO *b = BIO_new_fp(stderr, + BIO_NOCLOSE | (istext(format) ? BIO_FP_TEXT : 0)); +#ifdef OPENSSL_SYS_VMS + if (istext(format)) + b = BIO_push(BIO_new(BIO_f_linebuffer()), b); +#endif + return b; +} + +void unbuffer(FILE *fp) +{ +/* + * On VMS, setbuf() will only take 32-bit pointers, and a compilation + * with /POINTER_SIZE=64 will give off a MAYLOSEDATA2 warning here. + * However, we trust that the C RTL will never give us a FILE pointer + * above the first 4 GB of memory, so we simply turn off the warning + * temporarily. + */ +#if defined(OPENSSL_SYS_VMS) && defined(__DECC) +# pragma environment save +# pragma message disable maylosedata2 +#endif + setbuf(fp, NULL); +#if defined(OPENSSL_SYS_VMS) && defined(__DECC) +# pragma environment restore +#endif +} + +static const char *modestr(char mode, int format) +{ + OPENSSL_assert(mode == 'a' || mode == 'r' || mode == 'w'); + + switch (mode) { + case 'a': + return istext(format) ? "a" : "ab"; + case 'r': + return istext(format) ? "r" : "rb"; + case 'w': + return istext(format) ? "w" : "wb"; + } + /* The assert above should make sure we never reach this point */ + return NULL; +} + +static const char *modeverb(char mode) +{ + switch (mode) { + case 'a': + return "appending"; + case 'r': + return "reading"; + case 'w': + return "writing"; + } + return "(doing something)"; +} + +/* + * Open a file for writing, owner-read-only. + */ +BIO *bio_open_owner(const char *filename, int format, int private) +{ + FILE *fp = NULL; + BIO *b = NULL; + int fd = -1, bflags, mode, textmode; + + if (!private || filename == NULL || strcmp(filename, "-") == 0) + return bio_open_default(filename, 'w', format); + + mode = O_WRONLY; +#ifdef O_CREAT + mode |= O_CREAT; +#endif +#ifdef O_TRUNC + mode |= O_TRUNC; +#endif + textmode = istext(format); + if (!textmode) { +#ifdef O_BINARY + mode |= O_BINARY; +#elif defined(_O_BINARY) + mode |= _O_BINARY; +#endif + } + +#ifdef OPENSSL_SYS_VMS + /* VMS doesn't have O_BINARY, it just doesn't make sense. But, + * it still needs to know that we're going binary, or fdopen() + * will fail with "invalid argument"... so we tell VMS what the + * context is. + */ + if (!textmode) + fd = open(filename, mode, 0600, "ctx=bin"); + else +#endif + fd = open(filename, mode, 0600); + if (fd < 0) + goto err; + fp = fdopen(fd, modestr('w', format)); + if (fp == NULL) + goto err; + bflags = BIO_CLOSE; + if (textmode) + bflags |= BIO_FP_TEXT; + b = BIO_new_fp(fp, bflags); + if (b) + return b; + + err: + BIO_printf(bio_err, "%s: Can't open \"%s\" for writing, %s\n", + opt_getprog(), filename, strerror(errno)); + ERR_print_errors(bio_err); + /* If we have fp, then fdopen took over fd, so don't close both. */ + if (fp) + fclose(fp); + else if (fd >= 0) + close(fd); + return NULL; +} + +static BIO *bio_open_default_(const char *filename, char mode, int format, + int quiet) +{ + BIO *ret; + + if (filename == NULL || strcmp(filename, "-") == 0) { + ret = mode == 'r' ? dup_bio_in(format) : dup_bio_out(format); + if (quiet) { + ERR_clear_error(); + return ret; + } + if (ret != NULL) + return ret; + BIO_printf(bio_err, + "Can't open %s, %s\n", + mode == 'r' ? "stdin" : "stdout", strerror(errno)); + } else { + ret = BIO_new_file(filename, modestr(mode, format)); + if (quiet) { + ERR_clear_error(); + return ret; + } + if (ret != NULL) + return ret; + BIO_printf(bio_err, + "Can't open %s for %s, %s\n", + filename, modeverb(mode), strerror(errno)); + } + ERR_print_errors(bio_err); + return NULL; +} + +BIO *bio_open_default(const char *filename, char mode, int format) +{ + return bio_open_default_(filename, mode, format, 0); +} + +BIO *bio_open_default_quiet(const char *filename, char mode, int format) +{ + return bio_open_default_(filename, mode, format, 1); +} + +void wait_for_async(SSL *s) +{ + /* On Windows select only works for sockets, so we simply don't wait */ +#ifndef OPENSSL_SYS_WINDOWS + int width = 0; + fd_set asyncfds; + OSSL_ASYNC_FD *fds; + size_t numfds; + + if (!SSL_get_all_async_fds(s, NULL, &numfds)) + return; + if (numfds == 0) + return; + fds = app_malloc(sizeof(OSSL_ASYNC_FD) * numfds, "allocate async fds"); + if (!SSL_get_all_async_fds(s, fds, &numfds)) { + OPENSSL_free(fds); + } + + FD_ZERO(&asyncfds); + while (numfds > 0) { + if (width <= (int)*fds) + width = (int)*fds + 1; + openssl_fdset((int)*fds, &asyncfds); + numfds--; + fds++; + } + select(width, (void *)&asyncfds, NULL, NULL, NULL); +#endif +} + +/* if OPENSSL_SYS_WINDOWS is defined then so is OPENSSL_SYS_MSDOS */ +#if defined(OPENSSL_SYS_MSDOS) +int has_stdin_waiting(void) +{ +# if defined(OPENSSL_SYS_WINDOWS) + HANDLE inhand = GetStdHandle(STD_INPUT_HANDLE); + DWORD events = 0; + INPUT_RECORD inputrec; + DWORD insize = 1; + BOOL peeked; + + if (inhand == INVALID_HANDLE_VALUE) { + return 0; + } + + peeked = PeekConsoleInput(inhand, &inputrec, insize, &events); + if (!peeked) { + /* Probably redirected input? _kbhit() does not work in this case */ + if (!feof(stdin)) { + return 1; + } + return 0; + } +# endif + return _kbhit(); +} +#endif + +/* Corrupt a signature by modifying final byte */ +void corrupt_signature(const ASN1_STRING *signature) +{ + unsigned char *s = signature->data; + s[signature->length - 1] ^= 0x1; +} + +int set_cert_times(X509 *x, const char *startdate, const char *enddate, + int days) +{ + if (startdate == NULL || strcmp(startdate, "today") == 0) { + if (X509_gmtime_adj(X509_getm_notBefore(x), 0) == NULL) + return 0; + } else { + if (!ASN1_TIME_set_string(X509_getm_notBefore(x), startdate)) + return 0; + } + if (enddate == NULL) { + if (X509_time_adj_ex(X509_getm_notAfter(x), days, 0, NULL) + == NULL) + return 0; + } else if (!ASN1_TIME_set_string(X509_getm_notAfter(x), enddate)) { + return 0; + } + return 1; +} diff --git a/apps/apps.h b/apps/apps.h new file mode 100644 index 000000000..654cf6262 --- /dev/null +++ b/apps/apps.h @@ -0,0 +1,574 @@ +/* + * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#ifndef HEADER_APPS_H +# define HEADER_APPS_H + +# include "e_os.h" +# if defined(__unix) || defined(__unix__) +# include /* struct timeval for DTLS */ +# endif +# include + +# include +# include +# include +# include +# include +# include +# include +# ifndef OPENSSL_NO_ENGINE +# include +# endif +# ifndef OPENSSL_NO_OCSP +# include +# endif +# include + +# if defined(OPENSSL_SYS_WIN32) || defined(OPENSSL_SYS_WINCE) +# define openssl_fdset(a,b) FD_SET((unsigned int)a, b) +# else +# define openssl_fdset(a,b) FD_SET(a, b) +# endif + +/* + * quick macro when you need to pass an unsigned char instead of a char. + * this is true for some implementations of the is*() functions, for + * example. + */ +#define _UC(c) ((unsigned char)(c)) + +int app_RAND_load_file(const char *file, int dont_warn); +int app_RAND_write_file(const char *file); +/* + * When `file' is NULL, use defaults. `bio_e' is for error messages. + */ +void app_RAND_allow_write_file(void); +long app_RAND_load_files(char *file); /* `file' is a list of files to read, + * separated by LIST_SEPARATOR_CHAR + * (see e_os.h). The string is + * destroyed! */ + +extern char *default_config_file; +extern BIO *bio_in; +extern BIO *bio_out; +extern BIO *bio_err; +BIO *dup_bio_in(int format); +BIO *dup_bio_out(int format); +BIO *dup_bio_err(int format); +BIO *bio_open_owner(const char *filename, int format, int private); +BIO *bio_open_default(const char *filename, char mode, int format); +BIO *bio_open_default_quiet(const char *filename, char mode, int format); +CONF *app_load_config(const char *filename); +CONF *app_load_config_quiet(const char *filename); +int app_load_modules(const CONF *config); +void unbuffer(FILE *fp); +void wait_for_async(SSL *s); +# if defined(OPENSSL_SYS_MSDOS) +int has_stdin_waiting(void); +# endif + +void corrupt_signature(const ASN1_STRING *signature); +int set_cert_times(X509 *x, const char *startdate, const char *enddate, + int days); + +/* + * Common verification options. + */ +# define OPT_V_ENUM \ + OPT_V__FIRST=2000, \ + OPT_V_POLICY, OPT_V_PURPOSE, OPT_V_VERIFY_NAME, OPT_V_VERIFY_DEPTH, \ + OPT_V_ATTIME, OPT_V_VERIFY_HOSTNAME, OPT_V_VERIFY_EMAIL, \ + OPT_V_VERIFY_IP, OPT_V_IGNORE_CRITICAL, OPT_V_ISSUER_CHECKS, \ + OPT_V_CRL_CHECK, OPT_V_CRL_CHECK_ALL, OPT_V_POLICY_CHECK, \ + OPT_V_EXPLICIT_POLICY, OPT_V_INHIBIT_ANY, OPT_V_INHIBIT_MAP, \ + OPT_V_X509_STRICT, OPT_V_EXTENDED_CRL, OPT_V_USE_DELTAS, \ + OPT_V_POLICY_PRINT, OPT_V_CHECK_SS_SIG, OPT_V_TRUSTED_FIRST, \ + OPT_V_SUITEB_128_ONLY, OPT_V_SUITEB_128, OPT_V_SUITEB_192, \ + OPT_V_PARTIAL_CHAIN, OPT_V_NO_ALT_CHAINS, OPT_V_NO_CHECK_TIME, \ + OPT_V_VERIFY_AUTH_LEVEL, OPT_V_ALLOW_PROXY_CERTS, \ + OPT_V__LAST + +# define OPT_V_OPTIONS \ + { "policy", OPT_V_POLICY, 's', "adds policy to the acceptable policy set"}, \ + { "purpose", OPT_V_PURPOSE, 's', \ + "certificate chain purpose"}, \ + { "verify_name", OPT_V_VERIFY_NAME, 's', "verification policy name"}, \ + { "verify_depth", OPT_V_VERIFY_DEPTH, 'n', \ + "chain depth limit" }, \ + { "auth_level", OPT_V_VERIFY_AUTH_LEVEL, 'n', \ + "chain authentication security level" }, \ + { "attime", OPT_V_ATTIME, 'M', "verification epoch time" }, \ + { "verify_hostname", OPT_V_VERIFY_HOSTNAME, 's', \ + "expected peer hostname" }, \ + { "verify_email", OPT_V_VERIFY_EMAIL, 's', \ + "expected peer email" }, \ + { "verify_ip", OPT_V_VERIFY_IP, 's', \ + "expected peer IP address" }, \ + { "ignore_critical", OPT_V_IGNORE_CRITICAL, '-', \ + "permit unhandled critical extensions"}, \ + { "issuer_checks", OPT_V_ISSUER_CHECKS, '-', "(deprecated)"}, \ + { "crl_check", OPT_V_CRL_CHECK, '-', "check leaf certificate revocation" }, \ + { "crl_check_all", OPT_V_CRL_CHECK_ALL, '-', "check full chain revocation" }, \ + { "policy_check", OPT_V_POLICY_CHECK, '-', "perform rfc5280 policy checks"}, \ + { "explicit_policy", OPT_V_EXPLICIT_POLICY, '-', \ + "set policy variable require-explicit-policy"}, \ + { "inhibit_any", OPT_V_INHIBIT_ANY, '-', \ + "set policy variable inhibit-any-policy"}, \ + { "inhibit_map", OPT_V_INHIBIT_MAP, '-', \ + "set policy variable inhibit-policy-mapping"}, \ + { "x509_strict", OPT_V_X509_STRICT, '-', \ + "disable certificate compatibility work-arounds"}, \ + { "extended_crl", OPT_V_EXTENDED_CRL, '-', \ + "enable extended CRL features"}, \ + { "use_deltas", OPT_V_USE_DELTAS, '-', \ + "use delta CRLs"}, \ + { "policy_print", OPT_V_POLICY_PRINT, '-', \ + "print policy processing diagnostics"}, \ + { "check_ss_sig", OPT_V_CHECK_SS_SIG, '-', \ + "check root CA self-signatures"}, \ + { "trusted_first", OPT_V_TRUSTED_FIRST, '-', \ + "search trust store first (default)" }, \ + { "suiteB_128_only", OPT_V_SUITEB_128_ONLY, '-', "Suite B 128-bit-only mode"}, \ + { "suiteB_128", OPT_V_SUITEB_128, '-', \ + "Suite B 128-bit mode allowing 192-bit algorithms"}, \ + { "suiteB_192", OPT_V_SUITEB_192, '-', "Suite B 192-bit-only mode" }, \ + { "partial_chain", OPT_V_PARTIAL_CHAIN, '-', \ + "accept chains anchored by intermediate trust-store CAs"}, \ + { "no_alt_chains", OPT_V_NO_ALT_CHAINS, '-', "(deprecated)" }, \ + { "no_check_time", OPT_V_NO_CHECK_TIME, '-', "ignore certificate validity time" }, \ + { "allow_proxy_certs", OPT_V_ALLOW_PROXY_CERTS, '-', "allow the use of proxy certificates" } + +# define OPT_V_CASES \ + OPT_V__FIRST: case OPT_V__LAST: break; \ + case OPT_V_POLICY: \ + case OPT_V_PURPOSE: \ + case OPT_V_VERIFY_NAME: \ + case OPT_V_VERIFY_DEPTH: \ + case OPT_V_VERIFY_AUTH_LEVEL: \ + case OPT_V_ATTIME: \ + case OPT_V_VERIFY_HOSTNAME: \ + case OPT_V_VERIFY_EMAIL: \ + case OPT_V_VERIFY_IP: \ + case OPT_V_IGNORE_CRITICAL: \ + case OPT_V_ISSUER_CHECKS: \ + case OPT_V_CRL_CHECK: \ + case OPT_V_CRL_CHECK_ALL: \ + case OPT_V_POLICY_CHECK: \ + case OPT_V_EXPLICIT_POLICY: \ + case OPT_V_INHIBIT_ANY: \ + case OPT_V_INHIBIT_MAP: \ + case OPT_V_X509_STRICT: \ + case OPT_V_EXTENDED_CRL: \ + case OPT_V_USE_DELTAS: \ + case OPT_V_POLICY_PRINT: \ + case OPT_V_CHECK_SS_SIG: \ + case OPT_V_TRUSTED_FIRST: \ + case OPT_V_SUITEB_128_ONLY: \ + case OPT_V_SUITEB_128: \ + case OPT_V_SUITEB_192: \ + case OPT_V_PARTIAL_CHAIN: \ + case OPT_V_NO_ALT_CHAINS: \ + case OPT_V_NO_CHECK_TIME: \ + case OPT_V_ALLOW_PROXY_CERTS + +/* + * Common "extended"? options. + */ +# define OPT_X_ENUM \ + OPT_X__FIRST=1000, \ + OPT_X_KEY, OPT_X_CERT, OPT_X_CHAIN, OPT_X_CHAIN_BUILD, \ + OPT_X_CERTFORM, OPT_X_KEYFORM, \ + OPT_X__LAST + +# define OPT_X_OPTIONS \ + { "xkey", OPT_X_KEY, '<', "key for Extended certificates"}, \ + { "xcert", OPT_X_CERT, '<', "cert for Extended certificates"}, \ + { "xchain", OPT_X_CHAIN, '<', "chain for Extended certificates"}, \ + { "xchain_build", OPT_X_CHAIN_BUILD, '-', \ + "build certificate chain for the extended certificates"}, \ + { "xcertform", OPT_X_CERTFORM, 'F', \ + "format of Extended certificate (PEM or DER) PEM default " }, \ + { "xkeyform", OPT_X_KEYFORM, 'F', \ + "format of Extended certificate's key (PEM or DER) PEM default"} + +# define OPT_X_CASES \ + OPT_X__FIRST: case OPT_X__LAST: break; \ + case OPT_X_KEY: \ + case OPT_X_CERT: \ + case OPT_X_CHAIN: \ + case OPT_X_CHAIN_BUILD: \ + case OPT_X_CERTFORM: \ + case OPT_X_KEYFORM + +/* + * Common SSL options. + * Any changes here must be coordinated with ../ssl/ssl_conf.c + */ +# define OPT_S_ENUM \ + OPT_S__FIRST=3000, \ + OPT_S_NOSSL3, OPT_S_NOTLS1, OPT_S_NOTLS1_1, OPT_S_NOTLS1_2, \ + OPT_S_BUGS, OPT_S_NO_COMP, OPT_S_NOTICKET, \ + OPT_S_SERVERPREF, OPT_S_LEGACYRENEG, OPT_S_LEGACYCONN, \ + OPT_S_ONRESUMP, OPT_S_NOLEGACYCONN, OPT_S_STRICT, OPT_S_SIGALGS, \ + OPT_S_CLIENTSIGALGS, OPT_S_CURVES, OPT_S_NAMEDCURVE, OPT_S_CIPHER, \ + OPT_S_DHPARAM, OPT_S_DEBUGBROKE, OPT_S_COMP, \ + OPT_S__LAST + +# define OPT_S_OPTIONS \ + {"no_ssl3", OPT_S_NOSSL3, '-',"Just disable SSLv3" }, \ + {"no_tls1", OPT_S_NOTLS1, '-', "Just disable TLSv1"}, \ + {"no_tls1_1", OPT_S_NOTLS1_1, '-', "Just disable TLSv1.1" }, \ + {"no_tls1_2", OPT_S_NOTLS1_2, '-', "Just disable TLSv1.2"}, \ + {"bugs", OPT_S_BUGS, '-', "Turn on SSL bug compatibility"}, \ + {"no_comp", OPT_S_NO_COMP, '-', "Disable SSL/TLS compression (default)" }, \ + {"comp", OPT_S_COMP, '-', "Use SSL/TLS-level compression" }, \ + {"no_ticket", OPT_S_NOTICKET, '-', \ + "Disable use of TLS session tickets"}, \ + {"serverpref", OPT_S_SERVERPREF, '-', "Use server's cipher preferences"}, \ + {"legacy_renegotiation", OPT_S_LEGACYRENEG, '-', \ + "Enable use of legacy renegotiation (dangerous)"}, \ + {"legacy_server_connect", OPT_S_LEGACYCONN, '-', \ + "Allow initial connection to servers that don't support RI"}, \ + {"no_resumption_on_reneg", OPT_S_ONRESUMP, '-', \ + "Disallow session resumption on renegotiation"}, \ + {"no_legacy_server_connect", OPT_S_NOLEGACYCONN, '-', \ + "Disallow initial connection to servers that don't support RI"}, \ + {"strict", OPT_S_STRICT, '-', \ + "Enforce strict certificate checks as per TLS standard"}, \ + {"sigalgs", OPT_S_SIGALGS, 's', \ + "Signature algorithms to support (colon-separated list)" }, \ + {"client_sigalgs", OPT_S_CLIENTSIGALGS, 's', \ + "Signature algorithms to support for client certificate" \ + " authentication (colon-separated list)" }, \ + {"curves", OPT_S_CURVES, 's', \ + "Elliptic curves to advertise (colon-separated list)" }, \ + {"named_curve", OPT_S_NAMEDCURVE, 's', \ + "Elliptic curve used for ECDHE (server-side only)" }, \ + {"cipher", OPT_S_CIPHER, 's', "Specify cipher list to be used"}, \ + {"dhparam", OPT_S_DHPARAM, '<', \ + "DH parameter file to use, in cert file if not specified"}, \ + {"debug_broken_protocol", OPT_S_DEBUGBROKE, '-', \ + "Perform all sorts of protocol violations for testing purposes"} + +# define OPT_S_CASES \ + OPT_S__FIRST: case OPT_S__LAST: break; \ + case OPT_S_NOSSL3: \ + case OPT_S_NOTLS1: \ + case OPT_S_NOTLS1_1: \ + case OPT_S_NOTLS1_2: \ + case OPT_S_BUGS: \ + case OPT_S_NO_COMP: \ + case OPT_S_COMP: \ + case OPT_S_NOTICKET: \ + case OPT_S_SERVERPREF: \ + case OPT_S_LEGACYRENEG: \ + case OPT_S_LEGACYCONN: \ + case OPT_S_ONRESUMP: \ + case OPT_S_NOLEGACYCONN: \ + case OPT_S_STRICT: \ + case OPT_S_SIGALGS: \ + case OPT_S_CLIENTSIGALGS: \ + case OPT_S_CURVES: \ + case OPT_S_NAMEDCURVE: \ + case OPT_S_CIPHER: \ + case OPT_S_DHPARAM: \ + case OPT_S_DEBUGBROKE + +#define IS_NO_PROT_FLAG(o) \ + (o == OPT_S_NOSSL3 || o == OPT_S_NOTLS1 || o == OPT_S_NOTLS1_1 \ + || o == OPT_S_NOTLS1_2) + +/* + * Option parsing. + */ +extern const char OPT_HELP_STR[]; +extern const char OPT_MORE_STR[]; +typedef struct options_st { + const char *name; + int retval; + /* + * value type: - no value (also the value zero), n number, p positive + * number, u unsigned, l long, s string, < input file, > output file, + * f any format, F der/pem format , E der/pem/engine format identifier. + * l, n and u include zero; p does not. + */ + int valtype; + const char *helpstr; +} OPTIONS; + +/* + * A string/int pairing; widely use for option value lookup, hence the + * name OPT_PAIR. But that name is misleading in s_cb.c, so we also use + * the "generic" name STRINT_PAIR. + */ +typedef struct string_int_pair_st { + const char *name; + int retval; +} OPT_PAIR, STRINT_PAIR; + +/* Flags to pass into opt_format; see FORMAT_xxx, below. */ +# define OPT_FMT_PEMDER (1L << 1) +# define OPT_FMT_PKCS12 (1L << 2) +# define OPT_FMT_SMIME (1L << 3) +# define OPT_FMT_ENGINE (1L << 4) +# define OPT_FMT_MSBLOB (1L << 5) +# define OPT_FMT_NETSCAPE (1L << 6) +# define OPT_FMT_NSS (1L << 7) +# define OPT_FMT_TEXT (1L << 8) +# define OPT_FMT_HTTP (1L << 9) +# define OPT_FMT_PVK (1L << 10) +# define OPT_FMT_BINARY (1L << 11) +# define OPT_FMT_PDE (OPT_FMT_PEMDER | OPT_FMT_ENGINE) +# define OPT_FMT_PDS (OPT_FMT_PEMDER | OPT_FMT_SMIME) +# define OPT_FMT_ANY ( \ + OPT_FMT_PEMDER | OPT_FMT_PKCS12 | OPT_FMT_SMIME | \ + OPT_FMT_ENGINE | OPT_FMT_MSBLOB | OPT_FMT_NETSCAPE | \ + OPT_FMT_NSS | OPT_FMT_TEXT | OPT_FMT_HTTP | OPT_FMT_PVK) + +char *opt_progname(const char *argv0); +char *opt_getprog(void); +char *opt_init(int ac, char **av, const OPTIONS * o); +int opt_next(void); +int opt_format(const char *s, unsigned long flags, int *result); +int opt_int(const char *arg, int *result); +int opt_ulong(const char *arg, unsigned long *result); +int opt_long(const char *arg, long *result); +#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L && \ + defined(INTMAX_MAX) && defined(UINTMAX_MAX) +int opt_imax(const char *arg, intmax_t *result); +int opt_umax(const char *arg, uintmax_t *result); +#else +# define opt_imax opt_long +# define opt_umax opt_ulong +# define intmax_t long +# define uintmax_t unsigned long +#endif +int opt_pair(const char *arg, const OPT_PAIR * pairs, int *result); +int opt_cipher(const char *name, const EVP_CIPHER **cipherp); +int opt_md(const char *name, const EVP_MD **mdp); +char *opt_arg(void); +char *opt_flag(void); +char *opt_unknown(void); +char *opt_reset(void); +char **opt_rest(void); +int opt_num_rest(void); +int opt_verify(int i, X509_VERIFY_PARAM *vpm); +void opt_help(const OPTIONS * list); +int opt_format_error(const char *s, unsigned long flags); + +typedef struct args_st { + int size; + int argc; + char **argv; +} ARGS; + +/* + * VMS C only for now, implemented in vms_decc_init.c + * If other C compilers forget to terminate argv with NULL, this function + * can be re-used. + */ +char **copy_argv(int *argc, char *argv[]); +/* + * Win32-specific argv initialization that splits OS-supplied UNICODE + * command line string to array of UTF8-encoded strings. + */ +void win32_utf8argv(int *argc, char **argv[]); + + +# define PW_MIN_LENGTH 4 +typedef struct pw_cb_data { + const void *password; + const char *prompt_info; +} PW_CB_DATA; + +int password_callback(char *buf, int bufsiz, int verify, PW_CB_DATA *cb_data); + +int setup_ui_method(void); +void destroy_ui_method(void); + +int chopup_args(ARGS *arg, char *buf); +# ifdef HEADER_X509_H +int dump_cert_text(BIO *out, X509 *x); +void print_name(BIO *out, const char *title, X509_NAME *nm, + unsigned long lflags); +# endif +void print_bignum_var(BIO *, const BIGNUM *, const char*, + int, unsigned char *); +void print_array(BIO *, const char *, int, const unsigned char *); +int set_cert_ex(unsigned long *flags, const char *arg); +int set_name_ex(unsigned long *flags, const char *arg); +int set_ext_copy(int *copy_type, const char *arg); +int copy_extensions(X509 *x, X509_REQ *req, int copy_type); +int app_passwd(const char *arg1, const char *arg2, char **pass1, char **pass2); +int add_oid_section(CONF *conf); +X509 *load_cert(const char *file, int format, const char *cert_descrip); +X509_CRL *load_crl(const char *infile, int format); +EVP_PKEY *load_key(const char *file, int format, int maybe_stdin, + const char *pass, ENGINE *e, const char *key_descrip); +EVP_PKEY *load_pubkey(const char *file, int format, int maybe_stdin, + const char *pass, ENGINE *e, const char *key_descrip); +int load_certs(const char *file, STACK_OF(X509) **certs, int format, + const char *pass, const char *cert_descrip); +int load_crls(const char *file, STACK_OF(X509_CRL) **crls, int format, + const char *pass, const char *cert_descrip); +X509_STORE *setup_verify(const char *CAfile, const char *CApath, + int noCAfile, int noCApath); +__owur int ctx_set_verify_locations(SSL_CTX *ctx, const char *CAfile, + const char *CApath, int noCAfile, + int noCApath); + +#ifndef OPENSSL_NO_CT + +/* + * Sets the file to load the Certificate Transparency log list from. + * If path is NULL, loads from the default file path. + * Returns 1 on success, 0 otherwise. + */ +__owur int ctx_set_ctlog_list_file(SSL_CTX *ctx, const char *path); + +#endif + +ENGINE *setup_engine(const char *engine, int debug); +void release_engine(ENGINE *e); + +# ifndef OPENSSL_NO_OCSP +OCSP_RESPONSE *process_responder(OCSP_REQUEST *req, + const char *host, const char *path, + const char *port, int use_ssl, + STACK_OF(CONF_VALUE) *headers, + int req_timeout); +# endif + +/* Functions defined in ca.c and also used in ocsp.c */ +int unpack_revinfo(ASN1_TIME **prevtm, int *preason, ASN1_OBJECT **phold, + ASN1_GENERALIZEDTIME **pinvtm, const char *str); + +# define DB_type 0 +# define DB_exp_date 1 +# define DB_rev_date 2 +# define DB_serial 3 /* index - unique */ +# define DB_file 4 +# define DB_name 5 /* index - unique when active and not + * disabled */ +# define DB_NUMBER 6 + +# define DB_TYPE_REV 'R' +# define DB_TYPE_EXP 'E' +# define DB_TYPE_VAL 'V' + +typedef struct db_attr_st { + int unique_subject; +} DB_ATTR; +typedef struct ca_db_st { + DB_ATTR attributes; + TXT_DB *db; +} CA_DB; + +void* app_malloc(int sz, const char *what); +BIGNUM *load_serial(const char *serialfile, int create, ASN1_INTEGER **retai); +int save_serial(const char *serialfile, const char *suffix, const BIGNUM *serial, + ASN1_INTEGER **retai); +int rotate_serial(const char *serialfile, const char *new_suffix, + const char *old_suffix); +int rand_serial(BIGNUM *b, ASN1_INTEGER *ai); +CA_DB *load_index(const char *dbfile, DB_ATTR *dbattr); +int index_index(CA_DB *db); +int save_index(const char *dbfile, const char *suffix, CA_DB *db); +int rotate_index(const char *dbfile, const char *new_suffix, + const char *old_suffix); +void free_index(CA_DB *db); +# define index_name_cmp_noconst(a, b) \ + index_name_cmp((const OPENSSL_CSTRING *)CHECKED_PTR_OF(OPENSSL_STRING, a), \ + (const OPENSSL_CSTRING *)CHECKED_PTR_OF(OPENSSL_STRING, b)) +int index_name_cmp(const OPENSSL_CSTRING *a, const OPENSSL_CSTRING *b); +int parse_yesno(const char *str, int def); + +X509_NAME *parse_name(const char *str, long chtype, int multirdn); +int args_verify(char ***pargs, int *pargc, + int *badarg, X509_VERIFY_PARAM **pm); +void policies_print(X509_STORE_CTX *ctx); +int bio_to_mem(unsigned char **out, int maxlen, BIO *in); +int pkey_ctrl_string(EVP_PKEY_CTX *ctx, const char *value); +int init_gen_str(EVP_PKEY_CTX **pctx, + const char *algname, ENGINE *e, int do_param); +int do_X509_sign(X509 *x, EVP_PKEY *pkey, const EVP_MD *md, + STACK_OF(OPENSSL_STRING) *sigopts); +int do_X509_REQ_sign(X509_REQ *x, EVP_PKEY *pkey, const EVP_MD *md, + STACK_OF(OPENSSL_STRING) *sigopts); +int do_X509_CRL_sign(X509_CRL *x, EVP_PKEY *pkey, const EVP_MD *md, + STACK_OF(OPENSSL_STRING) *sigopts); +# ifndef OPENSSL_NO_PSK +extern char *psk_key; +# endif + +unsigned char *next_protos_parse(size_t *outlen, const char *in); + +void print_cert_checks(BIO *bio, X509 *x, + const char *checkhost, + const char *checkemail, const char *checkip); + +void store_setup_crl_download(X509_STORE *st); + +/* See OPT_FMT_xxx, above. */ +/* On some platforms, it's important to distinguish between text and binary + * files. On some, there might even be specific file formats for different + * contents. The FORMAT_xxx macros are meant to express an intent with the + * file being read or created. + */ +# define B_FORMAT_TEXT 0x8000 +# define FORMAT_UNDEF 0 +# define FORMAT_TEXT (1 | B_FORMAT_TEXT) /* Generic text */ +# define FORMAT_BINARY 2 /* Generic binary */ +# define FORMAT_BASE64 (3 | B_FORMAT_TEXT) /* Base64 */ +# define FORMAT_ASN1 4 /* ASN.1/DER */ +# define FORMAT_PEM (5 | B_FORMAT_TEXT) +# define FORMAT_PKCS12 6 +# define FORMAT_SMIME (7 | B_FORMAT_TEXT) +# define FORMAT_ENGINE 8 /* Not really a file format */ +# define FORMAT_PEMRSA (9 | B_FORMAT_TEXT) /* PEM RSAPubicKey format */ +# define FORMAT_ASN1RSA 10 /* DER RSAPubicKey format */ +# define FORMAT_MSBLOB 11 /* MS Key blob format */ +# define FORMAT_PVK 12 /* MS PVK file format */ +# define FORMAT_HTTP 13 /* Download using HTTP */ +# define FORMAT_NSS 14 /* NSS keylog format */ + +# define EXT_COPY_NONE 0 +# define EXT_COPY_ADD 1 +# define EXT_COPY_ALL 2 + +# define NETSCAPE_CERT_HDR "certificate" + +# define APP_PASS_LEN 1024 + +# define SERIAL_RAND_BITS 64 + +int app_isdir(const char *); +int app_access(const char *, int flag); +int fileno_stdin(void); +int fileno_stdout(void); +int raw_read_stdin(void *, int); +int raw_write_stdout(const void *, int); + +# define TM_START 0 +# define TM_STOP 1 +double app_tminterval(int stop, int usertime); + +typedef struct verify_options_st { + int depth; + int quiet; + int error; + int return_error; +} VERIFY_CB_ARGS; + +extern VERIFY_CB_ARGS verify_args; + +# include "progs.h" + +#endif diff --git a/apps/asn1pars.c b/apps/asn1pars.c new file mode 100644 index 000000000..1ac261c76 --- /dev/null +++ b/apps/asn1pars.c @@ -0,0 +1,330 @@ +/* + * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +/* + * A nice addition from Dr Stephen Henson to add the + * -strparse option which parses nested binary structures + */ + +#include +#include +#include +#include "apps.h" +#include +#include +#include +#include + +typedef enum OPTION_choice { + OPT_ERR = -1, OPT_EOF = 0, OPT_HELP, + OPT_INFORM, OPT_IN, OPT_OUT, OPT_INDENT, OPT_NOOUT, + OPT_OID, OPT_OFFSET, OPT_LENGTH, OPT_DUMP, OPT_DLIMIT, + OPT_STRPARSE, OPT_GENSTR, OPT_GENCONF, OPT_STRICTPEM +} OPTION_CHOICE; + +OPTIONS asn1parse_options[] = { + {"help", OPT_HELP, '-', "Display this summary"}, + {"inform", OPT_INFORM, 'F', "input format - one of DER PEM"}, + {"in", OPT_IN, '<', "input file"}, + {"out", OPT_OUT, '>', "output file (output format is always DER)"}, + {"i", OPT_INDENT, 0, "indents the output"}, + {"noout", OPT_NOOUT, 0, "do not produce any output"}, + {"offset", OPT_OFFSET, 'p', "offset into file"}, + {"length", OPT_LENGTH, 'p', "length of section in file"}, + {"oid", OPT_OID, '<', "file of extra oid definitions"}, + {"dump", OPT_DUMP, 0, "unknown data in hex form"}, + {"dlimit", OPT_DLIMIT, 'p', + "dump the first arg bytes of unknown data in hex form"}, + {"strparse", OPT_STRPARSE, 's', + "offset; a series of these can be used to 'dig'"}, + {OPT_MORE_STR, 0, 0, "into multiple ASN1 blob wrappings"}, + {"genstr", OPT_GENSTR, 's', "string to generate ASN1 structure from"}, + {"genconf", OPT_GENCONF, 's', "file to generate ASN1 structure from"}, + {OPT_MORE_STR, 0, 0, "(-inform will be ignored)"}, + {"strictpem", OPT_STRICTPEM, 0, + "do not attempt base64 decode outside PEM markers"}, + {NULL} +}; + +static int do_generate(char *genstr, const char *genconf, BUF_MEM *buf); + +int asn1parse_main(int argc, char **argv) +{ + ASN1_TYPE *at = NULL; + BIO *in = NULL, *b64 = NULL, *derout = NULL; + BUF_MEM *buf = NULL; + STACK_OF(OPENSSL_STRING) *osk = NULL; + char *genstr = NULL, *genconf = NULL; + char *infile = NULL, *oidfile = NULL, *derfile = NULL; + unsigned char *str = NULL; + char *name = NULL, *header = NULL, *prog; + const unsigned char *ctmpbuf; + int indent = 0, noout = 0, dump = 0, strictpem = 0, informat = FORMAT_PEM; + int offset = 0, ret = 1, i, j; + long num, tmplen; + unsigned char *tmpbuf; + unsigned int length = 0; + OPTION_CHOICE o; + + prog = opt_init(argc, argv, asn1parse_options); + + if ((osk = sk_OPENSSL_STRING_new_null()) == NULL) { + BIO_printf(bio_err, "%s: Memory allocation failure\n", prog); + goto end; + } + + while ((o = opt_next()) != OPT_EOF) { + switch (o) { + case OPT_EOF: + case OPT_ERR: + opthelp: + BIO_printf(bio_err, "%s: Use -help for summary.\n", prog); + goto end; + case OPT_HELP: + opt_help(asn1parse_options); + ret = 0; + goto end; + case OPT_INFORM: + if (!opt_format(opt_arg(), OPT_FMT_PEMDER, &informat)) + goto opthelp; + break; + case OPT_IN: + infile = opt_arg(); + break; + case OPT_OUT: + derfile = opt_arg(); + break; + case OPT_INDENT: + indent = 1; + break; + case OPT_NOOUT: + noout = 1; + break; + case OPT_OID: + oidfile = opt_arg(); + break; + case OPT_OFFSET: + offset = strtol(opt_arg(), NULL, 0); + break; + case OPT_LENGTH: + length = atoi(opt_arg()); + break; + case OPT_DUMP: + dump = -1; + break; + case OPT_DLIMIT: + dump = atoi(opt_arg()); + break; + case OPT_STRPARSE: + sk_OPENSSL_STRING_push(osk, opt_arg()); + break; + case OPT_GENSTR: + genstr = opt_arg(); + break; + case OPT_GENCONF: + genconf = opt_arg(); + break; + case OPT_STRICTPEM: + strictpem = 1; + informat = FORMAT_PEM; + break; + } + } + argc = opt_num_rest(); + if (argc != 0) + goto opthelp; + + if (oidfile != NULL) { + in = bio_open_default(oidfile, 'r', FORMAT_TEXT); + if (in == NULL) + goto end; + OBJ_create_objects(in); + BIO_free(in); + } + + if ((in = bio_open_default(infile, 'r', informat)) == NULL) + goto end; + + if (derfile && (derout = bio_open_default(derfile, 'w', FORMAT_ASN1)) == NULL) + goto end; + + if (strictpem) { + if (PEM_read_bio(in, &name, &header, &str, &num) != + 1) { + BIO_printf(bio_err, "Error reading PEM file\n"); + ERR_print_errors(bio_err); + goto end; + } + } else { + + if ((buf = BUF_MEM_new()) == NULL) + goto end; + if (!BUF_MEM_grow(buf, BUFSIZ * 8)) + goto end; /* Pre-allocate :-) */ + + if (genstr || genconf) { + num = do_generate(genstr, genconf, buf); + if (num < 0) { + ERR_print_errors(bio_err); + goto end; + } + } + + else { + + if (informat == FORMAT_PEM) { + BIO *tmp; + + if ((b64 = BIO_new(BIO_f_base64())) == NULL) + goto end; + BIO_push(b64, in); + tmp = in; + in = b64; + b64 = tmp; + } + + num = 0; + for (;;) { + if (!BUF_MEM_grow(buf, (int)num + BUFSIZ)) + goto end; + i = BIO_read(in, &(buf->data[num]), BUFSIZ); + if (i <= 0) + break; + num += i; + } + } + str = (unsigned char *)buf->data; + + } + + /* If any structs to parse go through in sequence */ + + if (sk_OPENSSL_STRING_num(osk)) { + tmpbuf = str; + tmplen = num; + for (i = 0; i < sk_OPENSSL_STRING_num(osk); i++) { + ASN1_TYPE *atmp; + int typ; + j = atoi(sk_OPENSSL_STRING_value(osk, i)); + if (j == 0) { + BIO_printf(bio_err, "'%s' is an invalid number\n", + sk_OPENSSL_STRING_value(osk, i)); + continue; + } + tmpbuf += j; + tmplen -= j; + atmp = at; + ctmpbuf = tmpbuf; + at = d2i_ASN1_TYPE(NULL, &ctmpbuf, tmplen); + ASN1_TYPE_free(atmp); + if (!at) { + BIO_printf(bio_err, "Error parsing structure\n"); + ERR_print_errors(bio_err); + goto end; + } + typ = ASN1_TYPE_get(at); + if ((typ == V_ASN1_OBJECT) + || (typ == V_ASN1_BOOLEAN) + || (typ == V_ASN1_NULL)) { + BIO_printf(bio_err, "Can't parse %s type\n", ASN1_tag2str(typ)); + ERR_print_errors(bio_err); + goto end; + } + /* hmm... this is a little evil but it works */ + tmpbuf = at->value.asn1_string->data; + tmplen = at->value.asn1_string->length; + } + str = tmpbuf; + num = tmplen; + } + + if (offset >= num) { + BIO_printf(bio_err, "Error: offset too large\n"); + goto end; + } + + num -= offset; + + if ((length == 0) || ((long)length > num)) + length = (unsigned int)num; + if (derout) { + if (BIO_write(derout, str + offset, length) != (int)length) { + BIO_printf(bio_err, "Error writing output\n"); + ERR_print_errors(bio_err); + goto end; + } + } + if (!noout && + !ASN1_parse_dump(bio_out, &(str[offset]), length, + indent, dump)) { + ERR_print_errors(bio_err); + goto end; + } + ret = 0; + end: + BIO_free(derout); + BIO_free(in); + BIO_free(b64); + if (ret != 0) + ERR_print_errors(bio_err); + BUF_MEM_free(buf); + OPENSSL_free(name); + OPENSSL_free(header); + if (strictpem) + OPENSSL_free(str); + ASN1_TYPE_free(at); + sk_OPENSSL_STRING_free(osk); + return (ret); +} + +static int do_generate(char *genstr, const char *genconf, BUF_MEM *buf) +{ + CONF *cnf = NULL; + int len; + unsigned char *p; + ASN1_TYPE *atyp = NULL; + + if (genconf) { + if ((cnf = app_load_config(genconf)) == NULL) + goto err; + if (!genstr) + genstr = NCONF_get_string(cnf, "default", "asn1"); + if (!genstr) { + BIO_printf(bio_err, "Can't find 'asn1' in '%s'\n", genconf); + goto err; + } + } + + atyp = ASN1_generate_nconf(genstr, cnf); + NCONF_free(cnf); + cnf = NULL; + + if (!atyp) + return -1; + + len = i2d_ASN1_TYPE(atyp, NULL); + + if (len <= 0) + goto err; + + if (!BUF_MEM_grow(buf, len)) + goto err; + + p = (unsigned char *)buf->data; + + i2d_ASN1_TYPE(atyp, &p); + + ASN1_TYPE_free(atyp); + return len; + + err: + NCONF_free(cnf); + ASN1_TYPE_free(atyp); + return -1; +} diff --git a/apps/build.info b/apps/build.info new file mode 100644 index 000000000..37f8241f0 --- /dev/null +++ b/apps/build.info @@ -0,0 +1,24 @@ +{- our $tsget_name = $config{target} =~ /^(VC|vms)-/ ? "tsget.pl" : "tsget"; + "" -} +IF[{- !$disabled{apps} -}] + PROGRAMS=gmssl + SOURCE[gmssl]=\ + gmssl.c \ + asn1pars.c ca.c ciphers.c cms.c crl.c crl2p7.c dgst.c dhparam.c \ + dsa.c dsaparam.c ec.c ecparam.c enc.c engine.c errstr.c gendsa.c \ + genpkey.c genrsa.c nseq.c ocsp.c passwd.c pkcs12.c pkcs7.c pkcs8.c \ + pkey.c pkeyparam.c pkeyutl.c prime.c rand.c req.c rsa.c rsautl.c \ + s_client.c s_server.c s_time.c sess_id.c smime.c speed.c spkac.c \ + srp.c ts.c verify.c version.c x509.c rehash.c \ + apps.c opt.c s_cb.c s_socket.c \ + app_rand.c otp.c paiutl.c \ + sm2.c sm2utl.c sdf.c skf.c \ + sm9param.c gensm9.c sm9.c sm9utl.c \ + {- $target{apps_aux_src} -} + INCLUDE[gmssl]=.. ../include + DEPEND[gmssl]=../libssl + + SCRIPTS=CA.pl {- $tsget_name -} + SOURCE[CA.pl]=CA.pl.in + SOURCE[{- $tsget_name -}]=tsget.in +ENDIF diff --git a/apps/ca-cert.srl b/apps/ca-cert.srl new file mode 100644 index 000000000..2c7456e3e --- /dev/null +++ b/apps/ca-cert.srl @@ -0,0 +1 @@ +07 diff --git a/apps/ca-key.pem b/apps/ca-key.pem new file mode 100644 index 000000000..4e7424906 --- /dev/null +++ b/apps/ca-key.pem @@ -0,0 +1,16 @@ +-----BEGIN PRIVATE KEY----- +MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAL4tQNyKy4U2zX6l +IZvORB1edmwMwIgSB4cgoFECrG5pixzYxKauZkAwKG9/+L4DB8qXRjfXWcvafcOU +DlYpRROykJ7wGkiqmqbZyrxY8DWjk5ZZQXiSuhYOAJB+Fyfb11JZV6+CvBQX/1g+ +vhJr39Gmp6oAesoYrj90ecozClmnAgMBAAECgYA3j6sSg+5f9hnldUMzbPjTh8Sb +XsJlPrc6UFrmMBzGiUleXSpe9Dbla+x0XvQCN4pwMvAN4nnWp/f0Su5BV/9Y93nb +im5ijGNrfN9i6QrnqGCr+MMute+4E8HR2pCScX0mBLDDf40SmDvMzCaxtd21keyr +9DqHgInQZNEi6NKlkQJBAPCbUTFg6iQ6VTCQ8CsEf5q2xHhuTK23fJ999lvWVxN7 +QsvWb9RP9Ng34HVtvB7Pl6P7FyHLQYiDJhhvYR0L0+kCQQDKV/09Kt6Wjf5Omp1I +wd3A+tFnipdqnPw+qNHGjevv0hYiEIWQOYbx00zXgaX+WN/pzV9eeNN2XAxlNJ++ +dxcPAkBrzeuPKFFAcjKBVC+H1rgl5gYZv7Hzk+buv02G0H6rZ+sB0c7BXiHiTwbv +Fn/XfkP/YR14Ms3mEH0dLaphjU8hAkEAh3Ar/rRiN04mCcEuRFQXtaNtZSv8PA2G +Pf7MI2Y9pdHupLCAZlBLRjTUO2/5hu1AO4QPMPIZQSFN3rRBtMCL+wJAMp/m2hvI +TmtbMp/IrKGfma09e3yFiCmoNn7cHLJ7jLvXcacV2XNzpr9YHfBxiZo0g9FqZKvv +PZoQ5B2XJ7bhTQ== +-----END PRIVATE KEY----- diff --git a/apps/ca-req.pem b/apps/ca-req.pem new file mode 100644 index 000000000..84c6dbb68 --- /dev/null +++ b/apps/ca-req.pem @@ -0,0 +1,11 @@ +-----BEGIN CERTIFICATE REQUEST----- +MIIBmzCCAQQCAQAwWzELMAkGA1UEBhMCQVUxEzARBgNVBAgMClF1ZWVuc2xhbmQx +GjAYBgNVBAoMEUNyeXB0U29mdCBQdHkgTHRkMRswGQYDVQQDDBJUZXN0IENBICgx +MDI0IGJpdCkwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAL4tQNyKy4U2zX6l +IZvORB1edmwMwIgSB4cgoFECrG5pixzYxKauZkAwKG9/+L4DB8qXRjfXWcvafcOU +DlYpRROykJ7wGkiqmqbZyrxY8DWjk5ZZQXiSuhYOAJB+Fyfb11JZV6+CvBQX/1g+ +vhJr39Gmp6oAesoYrj90ecozClmnAgMBAAGgADANBgkqhkiG9w0BAQsFAAOBgQCo +2jE7J1SNV7kyRm9m8CoPw8xYsuVcVFxPheBymYp8BlO0/rSdYygRjobpYnLVRUPZ +pV792wzT1Rp4sXfZWO10lkFY4yi0pH2cdK2RX7qedibV1Xu9vt/yYANFBKVpA4dy +PRyTQwi3In1N8hdfddpYR8f5MIUYRe5poFMIJcf8JA== +-----END CERTIFICATE REQUEST----- diff --git a/apps/ca.c b/apps/ca.c new file mode 100644 index 000000000..1fb7b08f8 --- /dev/null +++ b/apps/ca.c @@ -0,0 +1,2565 @@ +/* + * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +/* The PPKI stuff has been donated by Jeff Barber */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef W_OK +# ifdef OPENSSL_SYS_VMS +# if defined(__DECC) +# include +# else +# include +# endif +# elif !defined(OPENSSL_SYS_VXWORKS) && !defined(OPENSSL_SYS_WINDOWS) +# include +# endif +#endif + +#include "apps.h" + +#ifndef W_OK +# define F_OK 0 +# define X_OK 1 +# define W_OK 2 +# define R_OK 4 +#endif + +#undef BSIZE +#define BSIZE 256 + +#define BASE_SECTION "ca" + +#define ENV_DEFAULT_CA "default_ca" + +#define STRING_MASK "string_mask" +#define UTF8_IN "utf8" + +#define ENV_NEW_CERTS_DIR "new_certs_dir" +#define ENV_CERTIFICATE "certificate" +#define ENV_SERIAL "serial" +#define ENV_CRLNUMBER "crlnumber" +#define ENV_PRIVATE_KEY "private_key" +#define ENV_DEFAULT_DAYS "default_days" +#define ENV_DEFAULT_STARTDATE "default_startdate" +#define ENV_DEFAULT_ENDDATE "default_enddate" +#define ENV_DEFAULT_CRL_DAYS "default_crl_days" +#define ENV_DEFAULT_CRL_HOURS "default_crl_hours" +#define ENV_DEFAULT_MD "default_md" +#define ENV_DEFAULT_EMAIL_DN "email_in_dn" +#define ENV_PRESERVE "preserve" +#define ENV_POLICY "policy" +#define ENV_EXTENSIONS "x509_extensions" +#define ENV_CRLEXT "crl_extensions" +#define ENV_MSIE_HACK "msie_hack" +#define ENV_NAMEOPT "name_opt" +#define ENV_CERTOPT "cert_opt" +#define ENV_EXTCOPY "copy_extensions" +#define ENV_UNIQUE_SUBJECT "unique_subject" + +#define ENV_DATABASE "database" + +/* Additional revocation information types */ + +#define REV_NONE 0 /* No additional information */ +#define REV_CRL_REASON 1 /* Value is CRL reason code */ +#define REV_HOLD 2 /* Value is hold instruction */ +#define REV_KEY_COMPROMISE 3 /* Value is cert key compromise time */ +#define REV_CA_COMPROMISE 4 /* Value is CA key compromise time */ + +static char *lookup_conf(const CONF *conf, const char *group, const char *tag); + +static int certify(X509 **xret, const char *infile, EVP_PKEY *pkey, X509 *x509, + const EVP_MD *dgst, STACK_OF(OPENSSL_STRING) *sigopts, + STACK_OF(CONF_VALUE) *policy, CA_DB *db, + BIGNUM *serial, const char *subj, unsigned long chtype, + int multirdn, int email_dn, const char *startdate, + const char *enddate, + long days, int batch, const char *ext_sect, CONF *conf, + int verbose, unsigned long certopt, unsigned long nameopt, + int default_op, int ext_copy, int selfsign); +static int certify_cert(X509 **xret, const char *infile, EVP_PKEY *pkey, X509 *x509, + const EVP_MD *dgst, STACK_OF(OPENSSL_STRING) *sigopts, + STACK_OF(CONF_VALUE) *policy, CA_DB *db, + BIGNUM *serial, const char *subj, unsigned long chtype, + int multirdn, int email_dn, const char *startdate, + const char *enddate, long days, int batch, const char *ext_sect, + CONF *conf, int verbose, unsigned long certopt, + unsigned long nameopt, int default_op, int ext_copy); +static int certify_spkac(X509 **xret, const char *infile, EVP_PKEY *pkey, + X509 *x509, const EVP_MD *dgst, + STACK_OF(OPENSSL_STRING) *sigopts, + STACK_OF(CONF_VALUE) *policy, CA_DB *db, + BIGNUM *serial, const char *subj, unsigned long chtype, + int multirdn, int email_dn, const char *startdate, + const char *enddate, long days, const char *ext_sect, CONF *conf, + int verbose, unsigned long certopt, + unsigned long nameopt, int default_op, int ext_copy); +static void write_new_certificate(BIO *bp, X509 *x, int output_der, int notext); +static int do_body(X509 **xret, EVP_PKEY *pkey, X509 *x509, + const EVP_MD *dgst, STACK_OF(OPENSSL_STRING) *sigopts, + STACK_OF(CONF_VALUE) *policy, CA_DB *db, BIGNUM *serial, + const char *subj, unsigned long chtype, int multirdn, + int email_dn, const char *startdate, const char *enddate, long days, + int batch, int verbose, X509_REQ *req, const char *ext_sect, + CONF *conf, unsigned long certopt, unsigned long nameopt, + int default_op, int ext_copy, int selfsign); +static int do_revoke(X509 *x509, CA_DB *db, int ext, char *extval); +static int get_certificate_status(const char *ser_status, CA_DB *db); +static int do_updatedb(CA_DB *db); +static int check_time_format(const char *str); +char *make_revocation_str(int rev_type, char *rev_arg); +int make_revoked(X509_REVOKED *rev, const char *str); +static int old_entry_print(const ASN1_OBJECT *obj, const ASN1_STRING *str); + +static CONF *extconf = NULL; +static int preserve = 0; +static int msie_hack = 0; + +typedef enum OPTION_choice { + OPT_ERR = -1, OPT_EOF = 0, OPT_HELP, + OPT_ENGINE, OPT_VERBOSE, OPT_CONFIG, OPT_NAME, OPT_SUBJ, OPT_UTF8, + OPT_CREATE_SERIAL, OPT_MULTIVALUE_RDN, OPT_STARTDATE, OPT_ENDDATE, + OPT_DAYS, OPT_MD, OPT_POLICY, OPT_KEYFILE, OPT_KEYFORM, OPT_PASSIN, + OPT_KEY, OPT_CERT, OPT_SELFSIGN, OPT_IN, OPT_OUT, OPT_OUTDIR, + OPT_SIGOPT, OPT_NOTEXT, OPT_BATCH, OPT_PRESERVEDN, OPT_NOEMAILDN, + OPT_GENCRL, OPT_MSIE_HACK, OPT_CRLDAYS, OPT_CRLHOURS, OPT_CRLSEC, + OPT_INFILES, OPT_SS_CERT, OPT_SPKAC, OPT_REVOKE, OPT_VALID, + OPT_EXTENSIONS, OPT_EXTFILE, OPT_STATUS, OPT_UPDATEDB, OPT_CRLEXTS, + OPT_CRL_REASON, OPT_CRL_HOLD, OPT_CRL_COMPROMISE, + OPT_CRL_CA_COMPROMISE +} OPTION_CHOICE; + +OPTIONS ca_options[] = { + {"help", OPT_HELP, '-', "Display this summary"}, + {"verbose", OPT_VERBOSE, '-', "Verbose output during processing"}, + {"config", OPT_CONFIG, 's', "A config file"}, + {"name", OPT_NAME, 's', "The particular CA definition to use"}, + {"subj", OPT_SUBJ, 's', "Use arg instead of request's subject"}, + {"utf8", OPT_UTF8, '-', "Input characters are UTF8 (default ASCII)"}, + {"create_serial", OPT_CREATE_SERIAL, '-', + "If reading serial fails, create a new random serial"}, + {"multivalue-rdn", OPT_MULTIVALUE_RDN, '-', + "Enable support for multivalued RDNs"}, + {"startdate", OPT_STARTDATE, 's', "Cert notBefore, YYMMDDHHMMSSZ"}, + {"enddate", OPT_ENDDATE, 's', + "YYMMDDHHMMSSZ cert notAfter (overrides -days)"}, + {"days", OPT_DAYS, 'p', "Number of days to certify the cert for"}, + {"md", OPT_MD, 's', "md to use; one of md2, md5, sha or sha1"}, + {"policy", OPT_POLICY, 's', "The CA 'policy' to support"}, + {"keyfile", OPT_KEYFILE, 's', "Private key"}, + {"keyform", OPT_KEYFORM, 'f', "Private key file format (PEM or ENGINE)"}, + {"passin", OPT_PASSIN, 's', "Input file pass phrase source"}, + {"key", OPT_KEY, 's', "Key to decode the private key if it is encrypted"}, + {"cert", OPT_CERT, '<', "The CA cert"}, + {"selfsign", OPT_SELFSIGN, '-', + "Sign a cert with the key associated with it"}, + {"in", OPT_IN, '<', "The input PEM encoded cert request(s)"}, + {"out", OPT_OUT, '>', "Where to put the output file(s)"}, + {"outdir", OPT_OUTDIR, '/', "Where to put output cert"}, + {"sigopt", OPT_SIGOPT, 's', "Signature parameter in n:v form"}, + {"notext", OPT_NOTEXT, '-', "Do not print the generated certificate"}, + {"batch", OPT_BATCH, '-', "Don't ask questions"}, + {"preserveDN", OPT_PRESERVEDN, '-', "Don't re-order the DN"}, + {"noemailDN", OPT_NOEMAILDN, '-', "Don't add the EMAIL field to the DN"}, + {"gencrl", OPT_GENCRL, '-', "Generate a new CRL"}, + {"msie_hack", OPT_MSIE_HACK, '-', + "msie modifications to handle all those universal strings"}, + {"crldays", OPT_CRLDAYS, 'p', "Days until the next CRL is due"}, + {"crlhours", OPT_CRLHOURS, 'p', "Hours until the next CRL is due"}, + {"crlsec", OPT_CRLSEC, 'p', "Seconds until the next CRL is due"}, + {"infiles", OPT_INFILES, '-', "The last argument, requests to process"}, + {"ss_cert", OPT_SS_CERT, '<', "File contains a self signed cert to sign"}, + {"spkac", OPT_SPKAC, '<', + "File contains DN and signed public key and challenge"}, + {"revoke", OPT_REVOKE, '<', "Revoke a cert (given in file)"}, + {"valid", OPT_VALID, 's', + "Add a Valid(not-revoked) DB entry about a cert (given in file)"}, + {"extensions", OPT_EXTENSIONS, 's', + "Extension section (override value in config file)"}, + {"extfile", OPT_EXTFILE, '<', + "Configuration file with X509v3 extensions to add"}, + {"status", OPT_STATUS, 's', "Shows cert status given the serial number"}, + {"updatedb", OPT_UPDATEDB, '-', "Updates db for expired cert"}, + {"crlexts", OPT_CRLEXTS, 's', + "CRL extension section (override value in config file)"}, + {"crl_reason", OPT_CRL_REASON, 's', "revocation reason"}, + {"crl_hold", OPT_CRL_HOLD, 's', + "the hold instruction, an OID. Sets revocation reason to certificateHold"}, + {"crl_compromise", OPT_CRL_COMPROMISE, 's', + "sets compromise time to val and the revocation reason to keyCompromise"}, + {"crl_CA_compromise", OPT_CRL_CA_COMPROMISE, 's', + "sets compromise time to val and the revocation reason to CACompromise"}, +#ifndef OPENSSL_NO_ENGINE + {"engine", OPT_ENGINE, 's', "Use engine, possibly a hardware device"}, +#endif + {NULL} +}; + +int ca_main(int argc, char **argv) +{ + CONF *conf = NULL; + ENGINE *e = NULL; + BIGNUM *crlnumber = NULL, *serial = NULL; + EVP_PKEY *pkey = NULL; + BIO *in = NULL, *out = NULL, *Sout = NULL; + ASN1_INTEGER *tmpser; + ASN1_TIME *tmptm; + CA_DB *db = NULL; + DB_ATTR db_attr; + STACK_OF(CONF_VALUE) *attribs = NULL; + STACK_OF(OPENSSL_STRING) *sigopts = NULL; + STACK_OF(X509) *cert_sk = NULL; + X509_CRL *crl = NULL; + const EVP_MD *dgst = NULL; + char *configfile = default_config_file, *section = NULL; + char *md = NULL, *policy = NULL, *keyfile = NULL; + char *certfile = NULL, *crl_ext = NULL, *crlnumberfile = NULL, *key = NULL; + const char *infile = NULL, *spkac_file = NULL, *ss_cert_file = NULL; + const char *extensions = NULL, *extfile = NULL, *passinarg = NULL; + char *outdir = NULL, *outfile = NULL, *rev_arg = NULL, *ser_status = NULL; + const char *serialfile = NULL, *subj = NULL; + char *prog, *startdate = NULL, *enddate = NULL; + char *dbfile = NULL, *f, *randfile = NULL; + char buf[3][BSIZE]; + char *const *pp; + const char *p; + int create_ser = 0, free_key = 0, total = 0, total_done = 0; + int batch = 0, default_op = 1, doupdatedb = 0, ext_copy = EXT_COPY_NONE; + int keyformat = FORMAT_PEM, multirdn = 0, notext = 0, output_der = 0; + int ret = 1, email_dn = 1, req = 0, verbose = 0, gencrl = 0, dorevoke = 0; + int i, j, rev_type = REV_NONE, selfsign = 0; + long crldays = 0, crlhours = 0, crlsec = 0, days = 0; + unsigned long chtype = MBSTRING_ASC, nameopt = 0, certopt = 0; + X509 *x509 = NULL, *x509p = NULL, *x = NULL; + X509_REVOKED *r = NULL; + OPTION_CHOICE o; + + prog = opt_init(argc, argv, ca_options); + while ((o = opt_next()) != OPT_EOF) { + switch (o) { + case OPT_EOF: + case OPT_ERR: +opthelp: + BIO_printf(bio_err, "%s: Use -help for summary.\n", prog); + goto end; + case OPT_HELP: + opt_help(ca_options); + ret = 0; + goto end; + case OPT_IN: + req = 1; + infile = opt_arg(); + break; + case OPT_OUT: + outfile = opt_arg(); + break; + case OPT_VERBOSE: + verbose = 1; + break; + case OPT_CONFIG: + configfile = opt_arg(); + break; + case OPT_NAME: + section = opt_arg(); + break; + case OPT_SUBJ: + subj = opt_arg(); + /* preserve=1; */ + break; + case OPT_UTF8: + chtype = MBSTRING_UTF8; + break; + case OPT_CREATE_SERIAL: + create_ser = 1; + break; + case OPT_MULTIVALUE_RDN: + multirdn = 1; + break; + case OPT_STARTDATE: + startdate = opt_arg(); + break; + case OPT_ENDDATE: + enddate = opt_arg(); + break; + case OPT_DAYS: + days = atoi(opt_arg()); + break; + case OPT_MD: + md = opt_arg(); + break; + case OPT_POLICY: + policy = opt_arg(); + break; + case OPT_KEYFILE: + keyfile = opt_arg(); + break; + case OPT_KEYFORM: + if (!opt_format(opt_arg(), OPT_FMT_ANY, &keyformat)) + goto opthelp; + break; + case OPT_PASSIN: + passinarg = opt_arg(); + break; + case OPT_KEY: + key = opt_arg(); + break; + case OPT_CERT: + certfile = opt_arg(); + break; + case OPT_SELFSIGN: + selfsign = 1; + break; + case OPT_OUTDIR: + outdir = opt_arg(); + break; + case OPT_SIGOPT: + if (sigopts == NULL) + sigopts = sk_OPENSSL_STRING_new_null(); + if (sigopts == NULL + || !sk_OPENSSL_STRING_push(sigopts, opt_arg())) + goto end; + break; + case OPT_NOTEXT: + notext = 1; + break; + case OPT_BATCH: + batch = 1; + break; + case OPT_PRESERVEDN: + preserve = 1; + break; + case OPT_NOEMAILDN: + email_dn = 0; + break; + case OPT_GENCRL: + gencrl = 1; + break; + case OPT_MSIE_HACK: + msie_hack = 1; + break; + case OPT_CRLDAYS: + crldays = atol(opt_arg()); + break; + case OPT_CRLHOURS: + crlhours = atol(opt_arg()); + break; + case OPT_CRLSEC: + crlsec = atol(opt_arg()); + break; + case OPT_INFILES: + req = 1; + goto end_of_options; + case OPT_SS_CERT: + ss_cert_file = opt_arg(); + req = 1; + break; + case OPT_SPKAC: + spkac_file = opt_arg(); + req = 1; + break; + case OPT_REVOKE: + infile = opt_arg(); + dorevoke = 1; + break; + case OPT_VALID: + infile = opt_arg(); + dorevoke = 2; + break; + case OPT_EXTENSIONS: + extensions = opt_arg(); + break; + case OPT_EXTFILE: + extfile = opt_arg(); + break; + case OPT_STATUS: + ser_status = opt_arg(); + break; + case OPT_UPDATEDB: + doupdatedb = 1; + break; + case OPT_CRLEXTS: + crl_ext = opt_arg(); + break; + case OPT_CRL_REASON: + rev_arg = opt_arg(); + rev_type = REV_CRL_REASON; + break; + case OPT_CRL_HOLD: + rev_arg = opt_arg(); + rev_type = REV_HOLD; + break; + case OPT_CRL_COMPROMISE: + rev_arg = opt_arg(); + rev_type = REV_KEY_COMPROMISE; + break; + case OPT_CRL_CA_COMPROMISE: + rev_arg = opt_arg(); + rev_type = REV_CA_COMPROMISE; + break; + case OPT_ENGINE: + e = setup_engine(opt_arg(), 0); + break; + } + } +end_of_options: + argc = opt_num_rest(); + argv = opt_rest(); + + BIO_printf(bio_err, "Using configuration from %s\n", configfile); + + if ((conf = app_load_config(configfile)) == NULL) + goto end; + if (configfile != default_config_file && !app_load_modules(conf)) + goto end; + + /* Lets get the config section we are using */ + if (section == NULL + && (section = lookup_conf(conf, BASE_SECTION, ENV_DEFAULT_CA)) == NULL) + goto end; + + if (conf != NULL) { + p = NCONF_get_string(conf, NULL, "oid_file"); + if (p == NULL) + ERR_clear_error(); + if (p != NULL) { + BIO *oid_bio; + + oid_bio = BIO_new_file(p, "r"); + if (oid_bio == NULL) { + /*- + BIO_printf(bio_err,"problems opening %s for extra oid's\n",p); + ERR_print_errors(bio_err); + */ + ERR_clear_error(); + } else { + OBJ_create_objects(oid_bio); + BIO_free(oid_bio); + } + } + if (!add_oid_section(conf)) { + ERR_print_errors(bio_err); + goto end; + } + } + + randfile = NCONF_get_string(conf, BASE_SECTION, "RANDFILE"); + if (randfile == NULL) + ERR_clear_error(); + app_RAND_load_file(randfile, 0); + + f = NCONF_get_string(conf, section, STRING_MASK); + if (!f) + ERR_clear_error(); + + if (f && !ASN1_STRING_set_default_mask_asc(f)) { + BIO_printf(bio_err, "Invalid global string mask setting %s\n", f); + goto end; + } + + if (chtype != MBSTRING_UTF8) { + f = NCONF_get_string(conf, section, UTF8_IN); + if (!f) + ERR_clear_error(); + else if (strcmp(f, "yes") == 0) + chtype = MBSTRING_UTF8; + } + + db_attr.unique_subject = 1; + p = NCONF_get_string(conf, section, ENV_UNIQUE_SUBJECT); + if (p) { + db_attr.unique_subject = parse_yesno(p, 1); + } else + ERR_clear_error(); + + /*****************************************************************/ + /* report status of cert with serial number given on command line */ + if (ser_status) { + dbfile = lookup_conf(conf, section, ENV_DATABASE); + if (dbfile == NULL) + goto end; + + db = load_index(dbfile, &db_attr); + if (db == NULL) + goto end; + + if (!index_index(db)) + goto end; + + if (get_certificate_status(ser_status, db) != 1) + BIO_printf(bio_err, "Error verifying serial %s!\n", ser_status); + goto end; + } + + /*****************************************************************/ + /* we definitely need a private key, so let's get it */ + + if (keyfile == NULL + && (keyfile = lookup_conf(conf, section, ENV_PRIVATE_KEY)) == NULL) + goto end; + + if (!key) { + free_key = 1; + if (!app_passwd(passinarg, NULL, &key, NULL)) { + BIO_printf(bio_err, "Error getting password\n"); + goto end; + } + } + pkey = load_key(keyfile, keyformat, 0, key, e, "CA private key"); + if (key) + OPENSSL_cleanse(key, strlen(key)); + if (pkey == NULL) { + /* load_key() has already printed an appropriate message */ + goto end; + } + + /*****************************************************************/ + /* we need a certificate */ + if (!selfsign || spkac_file || ss_cert_file || gencrl) { + if (certfile == NULL + && (certfile = lookup_conf(conf, section, ENV_CERTIFICATE)) == NULL) + goto end; + + x509 = load_cert(certfile, FORMAT_PEM, "CA certificate"); + if (x509 == NULL) + goto end; + + if (!X509_check_private_key(x509, pkey)) { + BIO_printf(bio_err, + "CA certificate and CA private key do not match\n"); + goto end; + } + } + if (!selfsign) + x509p = x509; + + f = NCONF_get_string(conf, BASE_SECTION, ENV_PRESERVE); + if (f == NULL) + ERR_clear_error(); + if ((f != NULL) && ((*f == 'y') || (*f == 'Y'))) + preserve = 1; + f = NCONF_get_string(conf, BASE_SECTION, ENV_MSIE_HACK); + if (f == NULL) + ERR_clear_error(); + if ((f != NULL) && ((*f == 'y') || (*f == 'Y'))) + msie_hack = 1; + + f = NCONF_get_string(conf, section, ENV_NAMEOPT); + + if (f) { + if (!set_name_ex(&nameopt, f)) { + BIO_printf(bio_err, "Invalid name options: \"%s\"\n", f); + goto end; + } + default_op = 0; + } else { + nameopt = XN_FLAG_ONELINE; + ERR_clear_error(); + } + + f = NCONF_get_string(conf, section, ENV_CERTOPT); + + if (f) { + if (!set_cert_ex(&certopt, f)) { + BIO_printf(bio_err, "Invalid certificate options: \"%s\"\n", f); + goto end; + } + default_op = 0; + } else + ERR_clear_error(); + + f = NCONF_get_string(conf, section, ENV_EXTCOPY); + + if (f) { + if (!set_ext_copy(&ext_copy, f)) { + BIO_printf(bio_err, "Invalid extension copy option: \"%s\"\n", f); + goto end; + } + } else + ERR_clear_error(); + + /*****************************************************************/ + /* lookup where to write new certificates */ + if ((outdir == NULL) && (req)) { + + outdir = NCONF_get_string(conf, section, ENV_NEW_CERTS_DIR); + if (outdir == NULL) { + BIO_printf(bio_err, + "there needs to be defined a directory for new certificate to be placed in\n"); + goto end; + } +#ifndef OPENSSL_SYS_VMS + /* + * outdir is a directory spec, but access() for VMS demands a + * filename. We could use the DEC C routine to convert the + * directory syntax to Unixly, and give that to app_isdir, + * but for now the fopen will catch the error if it's not a + * directory + */ + if (app_isdir(outdir) <= 0) { + BIO_printf(bio_err, "%s: %s is not a directory\n", prog, outdir); + perror(outdir); + goto end; + } +#endif + } + + /*****************************************************************/ + /* we need to load the database file */ + dbfile = lookup_conf(conf, section, ENV_DATABASE); + if (dbfile == NULL) + goto end; + + db = load_index(dbfile, &db_attr); + if (db == NULL) + goto end; + + /* Lets check some fields */ + for (i = 0; i < sk_OPENSSL_PSTRING_num(db->db->data); i++) { + pp = sk_OPENSSL_PSTRING_value(db->db->data, i); + if ((pp[DB_type][0] != DB_TYPE_REV) && (pp[DB_rev_date][0] != '\0')) { + BIO_printf(bio_err, + "entry %d: not revoked yet, but has a revocation date\n", + i + 1); + goto end; + } + if ((pp[DB_type][0] == DB_TYPE_REV) && + !make_revoked(NULL, pp[DB_rev_date])) { + BIO_printf(bio_err, " in entry %d\n", i + 1); + goto end; + } + if (!check_time_format((char *)pp[DB_exp_date])) { + BIO_printf(bio_err, "entry %d: invalid expiry date\n", i + 1); + goto end; + } + p = pp[DB_serial]; + j = strlen(p); + if (*p == '-') { + p++; + j--; + } + if ((j & 1) || (j < 2)) { + BIO_printf(bio_err, "entry %d: bad serial number length (%d)\n", + i + 1, j); + goto end; + } + for ( ; *p; p++) { + if (!isxdigit(_UC(*p))) { + BIO_printf(bio_err, + "entry %d: bad char 0%o '%c' in serial number\n", + i + 1, *p, *p); + goto end; + } + } + } + if (verbose) { + TXT_DB_write(bio_out, db->db); + BIO_printf(bio_err, "%d entries loaded from the database\n", + sk_OPENSSL_PSTRING_num(db->db->data)); + BIO_printf(bio_err, "generating index\n"); + } + + if (!index_index(db)) + goto end; + + /*****************************************************************/ + /* Update the db file for expired certificates */ + if (doupdatedb) { + if (verbose) + BIO_printf(bio_err, "Updating %s ...\n", dbfile); + + i = do_updatedb(db); + if (i == -1) { + BIO_printf(bio_err, "Malloc failure\n"); + goto end; + } else if (i == 0) { + if (verbose) + BIO_printf(bio_err, "No entries found to mark expired\n"); + } else { + if (!save_index(dbfile, "new", db)) + goto end; + + if (!rotate_index(dbfile, "new", "old")) + goto end; + + if (verbose) + BIO_printf(bio_err, + "Done. %d entries marked as expired\n", i); + } + } + + /*****************************************************************/ + /* Read extensions config file */ + if (extfile) { + if ((extconf = app_load_config(extfile)) == NULL) { + ret = 1; + goto end; + } + + if (verbose) + BIO_printf(bio_err, "Successfully loaded extensions file %s\n", + extfile); + + /* We can have sections in the ext file */ + if (extensions == NULL) { + extensions = NCONF_get_string(extconf, "default", "extensions"); + if (extensions == NULL) + extensions = "default"; + } + } + + /*****************************************************************/ + if (req || gencrl) { + /* FIXME: Is it really always text? */ + Sout = bio_open_default(outfile, 'w', FORMAT_TEXT); + if (Sout == NULL) + goto end; + } + + if (md == NULL + && (md = lookup_conf(conf, section, ENV_DEFAULT_MD)) == NULL) + goto end; + + if (strcmp(md, "default") == 0) { + int def_nid; + if (EVP_PKEY_get_default_digest_nid(pkey, &def_nid) <= 0) { + BIO_puts(bio_err, "no default digest\n"); + goto end; + } + md = (char *)OBJ_nid2sn(def_nid); + } + + if (!opt_md(md, &dgst)) { + goto end; + } + + if (req) { + if (email_dn == 1) { + char *tmp_email_dn = NULL; + + tmp_email_dn = NCONF_get_string(conf, section, ENV_DEFAULT_EMAIL_DN); + if (tmp_email_dn != NULL && strcmp(tmp_email_dn, "no") == 0) + email_dn = 0; + } + if (verbose) + BIO_printf(bio_err, "message digest is %s\n", + OBJ_nid2ln(EVP_MD_type(dgst))); + if (policy == NULL + && (policy = lookup_conf(conf, section, ENV_POLICY)) == NULL) + goto end; + + if (verbose) + BIO_printf(bio_err, "policy is %s\n", policy); + + serialfile = lookup_conf(conf, section, ENV_SERIAL); + if (serialfile == NULL) + goto end; + + if (!extconf) { + /* + * no '-extfile' option, so we look for extensions in the main + * configuration file + */ + if (!extensions) { + extensions = NCONF_get_string(conf, section, ENV_EXTENSIONS); + if (!extensions) + ERR_clear_error(); + } + if (extensions) { + /* Check syntax of file */ + X509V3_CTX ctx; + X509V3_set_ctx_test(&ctx); + X509V3_set_nconf(&ctx, conf); + if (!X509V3_EXT_add_nconf(conf, &ctx, extensions, NULL)) { + BIO_printf(bio_err, + "Error Loading extension section %s\n", + extensions); + ret = 1; + goto end; + } + } + } + + if (startdate == NULL) { + startdate = NCONF_get_string(conf, section, + ENV_DEFAULT_STARTDATE); + if (startdate == NULL) + ERR_clear_error(); + } + if (startdate && !ASN1_TIME_set_string(NULL, startdate)) { + BIO_printf(bio_err, + "start date is invalid, it should be YYMMDDHHMMSSZ or YYYYMMDDHHMMSSZ\n"); + goto end; + } + if (startdate == NULL) + startdate = "today"; + + if (enddate == NULL) { + enddate = NCONF_get_string(conf, section, ENV_DEFAULT_ENDDATE); + if (enddate == NULL) + ERR_clear_error(); + } + if (enddate && !ASN1_TIME_set_string(NULL, enddate)) { + BIO_printf(bio_err, + "end date is invalid, it should be YYMMDDHHMMSSZ or YYYYMMDDHHMMSSZ\n"); + goto end; + } + + if (days == 0) { + if (!NCONF_get_number(conf, section, ENV_DEFAULT_DAYS, &days)) + days = 0; + } + if (!enddate && (days == 0)) { + BIO_printf(bio_err, + "cannot lookup how many days to certify for\n"); + goto end; + } + + if ((serial = load_serial(serialfile, create_ser, NULL)) == NULL) { + BIO_printf(bio_err, "error while loading serial number\n"); + goto end; + } + if (verbose) { + if (BN_is_zero(serial)) + BIO_printf(bio_err, "next serial number is 00\n"); + else { + if ((f = BN_bn2hex(serial)) == NULL) + goto end; + BIO_printf(bio_err, "next serial number is %s\n", f); + OPENSSL_free(f); + } + } + + if ((attribs = NCONF_get_section(conf, policy)) == NULL) { + BIO_printf(bio_err, "unable to find 'section' for %s\n", policy); + goto end; + } + + if ((cert_sk = sk_X509_new_null()) == NULL) { + BIO_printf(bio_err, "Memory allocation failure\n"); + goto end; + } + if (spkac_file != NULL) { + total++; + j = certify_spkac(&x, spkac_file, pkey, x509, dgst, sigopts, + attribs, db, serial, subj, chtype, multirdn, + email_dn, startdate, enddate, days, extensions, + conf, verbose, certopt, nameopt, default_op, + ext_copy); + if (j < 0) + goto end; + if (j > 0) { + total_done++; + BIO_printf(bio_err, "\n"); + if (!BN_add_word(serial, 1)) + goto end; + if (!sk_X509_push(cert_sk, x)) { + BIO_printf(bio_err, "Memory allocation failure\n"); + goto end; + } + if (outfile) { + output_der = 1; + batch = 1; + } + } + } + if (ss_cert_file != NULL) { + total++; + j = certify_cert(&x, ss_cert_file, pkey, x509, dgst, sigopts, + attribs, + db, serial, subj, chtype, multirdn, email_dn, + startdate, enddate, days, batch, extensions, + conf, verbose, certopt, nameopt, default_op, + ext_copy); + if (j < 0) + goto end; + if (j > 0) { + total_done++; + BIO_printf(bio_err, "\n"); + if (!BN_add_word(serial, 1)) + goto end; + if (!sk_X509_push(cert_sk, x)) { + BIO_printf(bio_err, "Memory allocation failure\n"); + goto end; + } + } + } + if (infile != NULL) { + total++; + j = certify(&x, infile, pkey, x509p, dgst, sigopts, attribs, db, + serial, subj, chtype, multirdn, email_dn, startdate, + enddate, days, batch, extensions, conf, verbose, + certopt, nameopt, default_op, ext_copy, selfsign); + if (j < 0) + goto end; + if (j > 0) { + total_done++; + BIO_printf(bio_err, "\n"); + if (!BN_add_word(serial, 1)) + goto end; + if (!sk_X509_push(cert_sk, x)) { + BIO_printf(bio_err, "Memory allocation failure\n"); + goto end; + } + } + } + for (i = 0; i < argc; i++) { + total++; + j = certify(&x, argv[i], pkey, x509p, dgst, sigopts, attribs, db, + serial, subj, chtype, multirdn, email_dn, startdate, + enddate, days, batch, extensions, conf, verbose, + certopt, nameopt, default_op, ext_copy, selfsign); + if (j < 0) + goto end; + if (j > 0) { + total_done++; + BIO_printf(bio_err, "\n"); + if (!BN_add_word(serial, 1)) + goto end; + if (!sk_X509_push(cert_sk, x)) { + BIO_printf(bio_err, "Memory allocation failure\n"); + goto end; + } + } + } + /* + * we have a stack of newly certified certificates and a data base + * and serial number that need updating + */ + + if (sk_X509_num(cert_sk) > 0) { + if (!batch) { + BIO_printf(bio_err, + "\n%d out of %d certificate requests certified, commit? [y/n]", + total_done, total); + (void)BIO_flush(bio_err); + buf[0][0] = '\0'; + if (!fgets(buf[0], 10, stdin)) { + BIO_printf(bio_err, + "CERTIFICATION CANCELED: I/O error\n"); + ret = 0; + goto end; + } + if ((buf[0][0] != 'y') && (buf[0][0] != 'Y')) { + BIO_printf(bio_err, "CERTIFICATION CANCELED\n"); + ret = 0; + goto end; + } + } + + BIO_printf(bio_err, "Write out database with %d new entries\n", + sk_X509_num(cert_sk)); + + if (!save_serial(serialfile, "new", serial, NULL)) + goto end; + + if (!save_index(dbfile, "new", db)) + goto end; + } + + if (verbose) + BIO_printf(bio_err, "writing new certificates\n"); + for (i = 0; i < sk_X509_num(cert_sk); i++) { + BIO *Cout = NULL; + X509 *xi = sk_X509_value(cert_sk, i); + ASN1_INTEGER *serialNumber = X509_get_serialNumber(xi); + int k; + char *n; + + j = ASN1_STRING_length(serialNumber); + p = (const char *)ASN1_STRING_get0_data(serialNumber); + + if (strlen(outdir) >= (size_t)(j ? BSIZE - j * 2 - 6 : BSIZE - 8)) { + BIO_printf(bio_err, "certificate file name too long\n"); + goto end; + } + + strcpy(buf[2], outdir); + +#ifndef OPENSSL_SYS_VMS + OPENSSL_strlcat(buf[2], "/", sizeof(buf[2])); +#endif + + n = (char *)&(buf[2][strlen(buf[2])]); + if (j > 0) { + for (k = 0; k < j; k++) { + if (n >= &(buf[2][sizeof(buf[2])])) + break; + BIO_snprintf(n, + &buf[2][0] + sizeof(buf[2]) - n, + "%02X", (unsigned char)*(p++)); + n += 2; + } + } else { + *(n++) = '0'; + *(n++) = '0'; + } + *(n++) = '.'; + *(n++) = 'p'; + *(n++) = 'e'; + *(n++) = 'm'; + *n = '\0'; + if (verbose) + BIO_printf(bio_err, "writing %s\n", buf[2]); + + Cout = BIO_new_file(buf[2], "w"); + if (Cout == NULL) { + perror(buf[2]); + goto end; + } + write_new_certificate(Cout, xi, 0, notext); + write_new_certificate(Sout, xi, output_der, notext); + BIO_free_all(Cout); + } + + if (sk_X509_num(cert_sk)) { + /* Rename the database and the serial file */ + if (!rotate_serial(serialfile, "new", "old")) + goto end; + + if (!rotate_index(dbfile, "new", "old")) + goto end; + + BIO_printf(bio_err, "Data Base Updated\n"); + } + } + + /*****************************************************************/ + if (gencrl) { + int crl_v2 = 0; + if (!crl_ext) { + crl_ext = NCONF_get_string(conf, section, ENV_CRLEXT); + if (!crl_ext) + ERR_clear_error(); + } + if (crl_ext) { + /* Check syntax of file */ + X509V3_CTX ctx; + X509V3_set_ctx_test(&ctx); + X509V3_set_nconf(&ctx, conf); + if (!X509V3_EXT_add_nconf(conf, &ctx, crl_ext, NULL)) { + BIO_printf(bio_err, + "Error Loading CRL extension section %s\n", + crl_ext); + ret = 1; + goto end; + } + } + + if ((crlnumberfile = NCONF_get_string(conf, section, ENV_CRLNUMBER)) + != NULL) + if ((crlnumber = load_serial(crlnumberfile, 0, NULL)) == NULL) { + BIO_printf(bio_err, "error while loading CRL number\n"); + goto end; + } + + if (!crldays && !crlhours && !crlsec) { + if (!NCONF_get_number(conf, section, + ENV_DEFAULT_CRL_DAYS, &crldays)) + crldays = 0; + if (!NCONF_get_number(conf, section, + ENV_DEFAULT_CRL_HOURS, &crlhours)) + crlhours = 0; + ERR_clear_error(); + } + if ((crldays == 0) && (crlhours == 0) && (crlsec == 0)) { + BIO_printf(bio_err, + "cannot lookup how long until the next CRL is issued\n"); + goto end; + } + + if (verbose) + BIO_printf(bio_err, "making CRL\n"); + if ((crl = X509_CRL_new()) == NULL) + goto end; + if (!X509_CRL_set_issuer_name(crl, X509_get_subject_name(x509))) + goto end; + + tmptm = ASN1_TIME_new(); + if (tmptm == NULL) + goto end; + X509_gmtime_adj(tmptm, 0); + X509_CRL_set1_lastUpdate(crl, tmptm); + if (!X509_time_adj_ex(tmptm, crldays, crlhours * 60 * 60 + crlsec, + NULL)) { + BIO_puts(bio_err, "error setting CRL nextUpdate\n"); + goto end; + } + X509_CRL_set1_nextUpdate(crl, tmptm); + + ASN1_TIME_free(tmptm); + + for (i = 0; i < sk_OPENSSL_PSTRING_num(db->db->data); i++) { + pp = sk_OPENSSL_PSTRING_value(db->db->data, i); + if (pp[DB_type][0] == DB_TYPE_REV) { + if ((r = X509_REVOKED_new()) == NULL) + goto end; + j = make_revoked(r, pp[DB_rev_date]); + if (!j) + goto end; + if (j == 2) + crl_v2 = 1; + if (!BN_hex2bn(&serial, pp[DB_serial])) + goto end; + tmpser = BN_to_ASN1_INTEGER(serial, NULL); + BN_free(serial); + serial = NULL; + if (!tmpser) + goto end; + X509_REVOKED_set_serialNumber(r, tmpser); + ASN1_INTEGER_free(tmpser); + X509_CRL_add0_revoked(crl, r); + } + } + + /* + * sort the data so it will be written in serial number order + */ + X509_CRL_sort(crl); + + /* we now have a CRL */ + if (verbose) + BIO_printf(bio_err, "signing CRL\n"); + + /* Add any extensions asked for */ + + if (crl_ext || crlnumberfile != NULL) { + X509V3_CTX crlctx; + X509V3_set_ctx(&crlctx, x509, NULL, NULL, crl, 0); + X509V3_set_nconf(&crlctx, conf); + + if (crl_ext) + if (!X509V3_EXT_CRL_add_nconf(conf, &crlctx, crl_ext, crl)) + goto end; + if (crlnumberfile != NULL) { + tmpser = BN_to_ASN1_INTEGER(crlnumber, NULL); + if (!tmpser) + goto end; + X509_CRL_add1_ext_i2d(crl, NID_crl_number, tmpser, 0, 0); + ASN1_INTEGER_free(tmpser); + crl_v2 = 1; + if (!BN_add_word(crlnumber, 1)) + goto end; + } + } + if (crl_ext || crl_v2) { + if (!X509_CRL_set_version(crl, 1)) + goto end; /* version 2 CRL */ + } + + /* we have a CRL number that need updating */ + if (crlnumberfile != NULL) + if (!save_serial(crlnumberfile, "new", crlnumber, NULL)) + goto end; + + BN_free(crlnumber); + crlnumber = NULL; + + if (!do_X509_CRL_sign(crl, pkey, dgst, sigopts)) + goto end; + + PEM_write_bio_X509_CRL(Sout, crl); + + if (crlnumberfile != NULL) /* Rename the crlnumber file */ + if (!rotate_serial(crlnumberfile, "new", "old")) + goto end; + + } + /*****************************************************************/ + if (dorevoke) { + if (infile == NULL) { + BIO_printf(bio_err, "no input files\n"); + goto end; + } else { + X509 *revcert; + revcert = load_cert(infile, FORMAT_PEM, infile); + if (revcert == NULL) + goto end; + if (dorevoke == 2) + rev_type = -1; + j = do_revoke(revcert, db, rev_type, rev_arg); + if (j <= 0) + goto end; + X509_free(revcert); + + if (!save_index(dbfile, "new", db)) + goto end; + + if (!rotate_index(dbfile, "new", "old")) + goto end; + + BIO_printf(bio_err, "Data Base Updated\n"); + } + } + /*****************************************************************/ + ret = 0; + end: + BIO_free_all(Sout); + BIO_free_all(out); + BIO_free_all(in); + sk_X509_pop_free(cert_sk, X509_free); + + if (ret) + ERR_print_errors(bio_err); + app_RAND_write_file(randfile); + if (free_key) + OPENSSL_free(key); + BN_free(serial); + BN_free(crlnumber); + free_index(db); + sk_OPENSSL_STRING_free(sigopts); + EVP_PKEY_free(pkey); + X509_free(x509); + X509_CRL_free(crl); + NCONF_free(conf); + NCONF_free(extconf); + release_engine(e); + return (ret); +} + +static char *lookup_conf(const CONF *conf, const char *section, const char *tag) +{ + char *entry = NCONF_get_string(conf, section, tag); + if (entry == NULL) + BIO_printf(bio_err, "variable lookup failed for %s::%s\n", section, tag); + return entry; +} + +static int certify(X509 **xret, const char *infile, EVP_PKEY *pkey, X509 *x509, + const EVP_MD *dgst, STACK_OF(OPENSSL_STRING) *sigopts, + STACK_OF(CONF_VALUE) *policy, CA_DB *db, + BIGNUM *serial, const char *subj, unsigned long chtype, + int multirdn, int email_dn, const char *startdate, + const char *enddate, + long days, int batch, const char *ext_sect, CONF *lconf, + int verbose, unsigned long certopt, unsigned long nameopt, + int default_op, int ext_copy, int selfsign) +{ + X509_REQ *req = NULL; + BIO *in = NULL; + EVP_PKEY *pktmp = NULL; + int ok = -1, i; + + in = BIO_new_file(infile, "r"); + if (in == NULL) { + ERR_print_errors(bio_err); + goto end; + } + if ((req = PEM_read_bio_X509_REQ(in, NULL, NULL, NULL)) == NULL) { + BIO_printf(bio_err, "Error reading certificate request in %s\n", + infile); + goto end; + } + if (verbose) + X509_REQ_print(bio_err, req); + + BIO_printf(bio_err, "Check that the request matches the signature\n"); + + if (selfsign && !X509_REQ_check_private_key(req, pkey)) { + BIO_printf(bio_err, + "Certificate request and CA private key do not match\n"); + ok = 0; + goto end; + } + if ((pktmp = X509_REQ_get0_pubkey(req)) == NULL) { + BIO_printf(bio_err, "error unpacking public key\n"); + goto end; + } + i = X509_REQ_verify(req, pktmp); + pktmp = NULL; + if (i < 0) { + ok = 0; + BIO_printf(bio_err, "Signature verification problems....\n"); + ERR_print_errors(bio_err); + goto end; + } + if (i == 0) { + ok = 0; + BIO_printf(bio_err, + "Signature did not match the certificate request\n"); + ERR_print_errors(bio_err); + goto end; + } else + BIO_printf(bio_err, "Signature ok\n"); + + ok = do_body(xret, pkey, x509, dgst, sigopts, policy, db, serial, subj, + chtype, multirdn, email_dn, startdate, enddate, days, batch, + verbose, req, ext_sect, lconf, certopt, nameopt, default_op, + ext_copy, selfsign); + + end: + X509_REQ_free(req); + BIO_free(in); + return (ok); +} + +static int certify_cert(X509 **xret, const char *infile, EVP_PKEY *pkey, X509 *x509, + const EVP_MD *dgst, STACK_OF(OPENSSL_STRING) *sigopts, + STACK_OF(CONF_VALUE) *policy, CA_DB *db, + BIGNUM *serial, const char *subj, unsigned long chtype, + int multirdn, int email_dn, const char *startdate, + const char *enddate, long days, int batch, const char *ext_sect, + CONF *lconf, int verbose, unsigned long certopt, + unsigned long nameopt, int default_op, int ext_copy) +{ + X509 *req = NULL; + X509_REQ *rreq = NULL; + EVP_PKEY *pktmp = NULL; + int ok = -1, i; + + if ((req = load_cert(infile, FORMAT_PEM, infile)) == NULL) + goto end; + if (verbose) + X509_print(bio_err, req); + + BIO_printf(bio_err, "Check that the request matches the signature\n"); + + if ((pktmp = X509_get0_pubkey(req)) == NULL) { + BIO_printf(bio_err, "error unpacking public key\n"); + goto end; + } + i = X509_verify(req, pktmp); + if (i < 0) { + ok = 0; + BIO_printf(bio_err, "Signature verification problems....\n"); + goto end; + } + if (i == 0) { + ok = 0; + BIO_printf(bio_err, "Signature did not match the certificate\n"); + goto end; + } else + BIO_printf(bio_err, "Signature ok\n"); + + if ((rreq = X509_to_X509_REQ(req, NULL, NULL)) == NULL) + goto end; + + ok = do_body(xret, pkey, x509, dgst, sigopts, policy, db, serial, subj, + chtype, multirdn, email_dn, startdate, enddate, days, batch, + verbose, rreq, ext_sect, lconf, certopt, nameopt, default_op, + ext_copy, 0); + + end: + X509_REQ_free(rreq); + X509_free(req); + return (ok); +} + +static int do_body(X509 **xret, EVP_PKEY *pkey, X509 *x509, + const EVP_MD *dgst, STACK_OF(OPENSSL_STRING) *sigopts, + STACK_OF(CONF_VALUE) *policy, CA_DB *db, BIGNUM *serial, + const char *subj, unsigned long chtype, int multirdn, + int email_dn, const char *startdate, const char *enddate, long days, + int batch, int verbose, X509_REQ *req, const char *ext_sect, + CONF *lconf, unsigned long certopt, unsigned long nameopt, + int default_op, int ext_copy, int selfsign) +{ + X509_NAME *name = NULL, *CAname = NULL, *subject = NULL, *dn_subject = + NULL; + const ASN1_TIME *tm; + ASN1_STRING *str, *str2; + ASN1_OBJECT *obj; + X509 *ret = NULL; + X509_NAME_ENTRY *ne; + X509_NAME_ENTRY *tne, *push; + EVP_PKEY *pktmp; + int ok = -1, i, j, last, nid; + const char *p; + CONF_VALUE *cv; + OPENSSL_STRING row[DB_NUMBER]; + OPENSSL_STRING *irow = NULL; + OPENSSL_STRING *rrow = NULL; + char buf[25]; + + for (i = 0; i < DB_NUMBER; i++) + row[i] = NULL; + + if (subj) { + X509_NAME *n = parse_name(subj, chtype, multirdn); + + if (!n) { + ERR_print_errors(bio_err); + goto end; + } + X509_REQ_set_subject_name(req, n); + X509_NAME_free(n); + } + + if (default_op) + BIO_printf(bio_err, + "The Subject's Distinguished Name is as follows\n"); + + name = X509_REQ_get_subject_name(req); + for (i = 0; i < X509_NAME_entry_count(name); i++) { + ne = X509_NAME_get_entry(name, i); + str = X509_NAME_ENTRY_get_data(ne); + obj = X509_NAME_ENTRY_get_object(ne); + + if (msie_hack) { + /* assume all type should be strings */ + nid = OBJ_obj2nid(X509_NAME_ENTRY_get_object(ne)); + + if (str->type == V_ASN1_UNIVERSALSTRING) + ASN1_UNIVERSALSTRING_to_string(str); + + if ((str->type == V_ASN1_IA5STRING) && + (nid != NID_pkcs9_emailAddress)) + str->type = V_ASN1_T61STRING; + + if ((nid == NID_pkcs9_emailAddress) && + (str->type == V_ASN1_PRINTABLESTRING)) + str->type = V_ASN1_IA5STRING; + } + + /* If no EMAIL is wanted in the subject */ + if ((OBJ_obj2nid(obj) == NID_pkcs9_emailAddress) && (!email_dn)) + continue; + + /* check some things */ + if ((OBJ_obj2nid(obj) == NID_pkcs9_emailAddress) && + (str->type != V_ASN1_IA5STRING)) { + BIO_printf(bio_err, + "\nemailAddress type needs to be of type IA5STRING\n"); + goto end; + } + if ((str->type != V_ASN1_BMPSTRING) + && (str->type != V_ASN1_UTF8STRING)) { + j = ASN1_PRINTABLE_type(str->data, str->length); + if (((j == V_ASN1_T61STRING) && + (str->type != V_ASN1_T61STRING)) || + ((j == V_ASN1_IA5STRING) && + (str->type == V_ASN1_PRINTABLESTRING))) { + BIO_printf(bio_err, + "\nThe string contains characters that are illegal for the ASN.1 type\n"); + goto end; + } + } + + if (default_op) + old_entry_print(obj, str); + } + + /* Ok, now we check the 'policy' stuff. */ + if ((subject = X509_NAME_new()) == NULL) { + BIO_printf(bio_err, "Memory allocation failure\n"); + goto end; + } + + /* take a copy of the issuer name before we mess with it. */ + if (selfsign) + CAname = X509_NAME_dup(name); + else + CAname = X509_NAME_dup(X509_get_subject_name(x509)); + if (CAname == NULL) + goto end; + str = str2 = NULL; + + for (i = 0; i < sk_CONF_VALUE_num(policy); i++) { + cv = sk_CONF_VALUE_value(policy, i); /* get the object id */ + if ((j = OBJ_txt2nid(cv->name)) == NID_undef) { + BIO_printf(bio_err, + "%s:unknown object type in 'policy' configuration\n", + cv->name); + goto end; + } + obj = OBJ_nid2obj(j); + + last = -1; + for (;;) { + /* lookup the object in the supplied name list */ + j = X509_NAME_get_index_by_OBJ(name, obj, last); + if (j < 0) { + if (last != -1) + break; + tne = NULL; + } else { + tne = X509_NAME_get_entry(name, j); + } + last = j; + + /* depending on the 'policy', decide what to do. */ + push = NULL; + if (strcmp(cv->value, "optional") == 0) { + if (tne != NULL) + push = tne; + } else if (strcmp(cv->value, "supplied") == 0) { + if (tne == NULL) { + BIO_printf(bio_err, + "The %s field needed to be supplied and was missing\n", + cv->name); + goto end; + } else + push = tne; + } else if (strcmp(cv->value, "match") == 0) { + int last2; + + if (tne == NULL) { + BIO_printf(bio_err, + "The mandatory %s field was missing\n", + cv->name); + goto end; + } + + last2 = -1; + + again2: + j = X509_NAME_get_index_by_OBJ(CAname, obj, last2); + if ((j < 0) && (last2 == -1)) { + BIO_printf(bio_err, + "The %s field does not exist in the CA certificate,\n" + "the 'policy' is misconfigured\n", + cv->name); + goto end; + } + if (j >= 0) { + push = X509_NAME_get_entry(CAname, j); + str = X509_NAME_ENTRY_get_data(tne); + str2 = X509_NAME_ENTRY_get_data(push); + last2 = j; + if (ASN1_STRING_cmp(str, str2) != 0) + goto again2; + } + if (j < 0) { + BIO_printf(bio_err, + "The %s field is different between\n" + "CA certificate (%s) and the request (%s)\n", + cv->name, + ((str2 == NULL) ? "NULL" : (char *)str2->data), + ((str == NULL) ? "NULL" : (char *)str->data)); + goto end; + } + } else { + BIO_printf(bio_err, + "%s:invalid type in 'policy' configuration\n", + cv->value); + goto end; + } + + if (push != NULL) { + if (!X509_NAME_add_entry(subject, push, -1, 0)) { + X509_NAME_ENTRY_free(push); + BIO_printf(bio_err, "Memory allocation failure\n"); + goto end; + } + } + if (j < 0) + break; + } + } + + if (preserve) { + X509_NAME_free(subject); + /* subject=X509_NAME_dup(X509_REQ_get_subject_name(req)); */ + subject = X509_NAME_dup(name); + if (subject == NULL) + goto end; + } + + if (verbose) + BIO_printf(bio_err, + "The subject name appears to be ok, checking data base for clashes\n"); + + /* Build the correct Subject if no e-mail is wanted in the subject */ + /* + * and add it later on because of the method extensions are added + * (altName) + */ + + if (email_dn) + dn_subject = subject; + else { + X509_NAME_ENTRY *tmpne; + /* + * Its best to dup the subject DN and then delete any email addresses + * because this retains its structure. + */ + if ((dn_subject = X509_NAME_dup(subject)) == NULL) { + BIO_printf(bio_err, "Memory allocation failure\n"); + goto end; + } + while ((i = X509_NAME_get_index_by_NID(dn_subject, + NID_pkcs9_emailAddress, + -1)) >= 0) { + tmpne = X509_NAME_get_entry(dn_subject, i); + X509_NAME_delete_entry(dn_subject, i); + X509_NAME_ENTRY_free(tmpne); + } + } + + if (BN_is_zero(serial)) + row[DB_serial] = OPENSSL_strdup("00"); + else + row[DB_serial] = BN_bn2hex(serial); + if (row[DB_serial] == NULL) { + BIO_printf(bio_err, "Memory allocation failure\n"); + goto end; + } + + if (db->attributes.unique_subject) { + OPENSSL_STRING *crow = row; + + rrow = TXT_DB_get_by_index(db->db, DB_name, crow); + if (rrow != NULL) { + BIO_printf(bio_err, + "ERROR:There is already a certificate for %s\n", + row[DB_name]); + } + } + if (rrow == NULL) { + rrow = TXT_DB_get_by_index(db->db, DB_serial, row); + if (rrow != NULL) { + BIO_printf(bio_err, + "ERROR:Serial number %s has already been issued,\n", + row[DB_serial]); + BIO_printf(bio_err, + " check the database/serial_file for corruption\n"); + } + } + + if (rrow != NULL) { + BIO_printf(bio_err, "The matching entry has the following details\n"); + if (rrow[DB_type][0] == 'E') + p = "Expired"; + else if (rrow[DB_type][0] == 'R') + p = "Revoked"; + else if (rrow[DB_type][0] == 'V') + p = "Valid"; + else + p = "\ninvalid type, Data base error\n"; + BIO_printf(bio_err, "Type :%s\n", p);; + if (rrow[DB_type][0] == 'R') { + p = rrow[DB_exp_date]; + if (p == NULL) + p = "undef"; + BIO_printf(bio_err, "Was revoked on:%s\n", p); + } + p = rrow[DB_exp_date]; + if (p == NULL) + p = "undef"; + BIO_printf(bio_err, "Expires on :%s\n", p); + p = rrow[DB_serial]; + if (p == NULL) + p = "undef"; + BIO_printf(bio_err, "Serial Number :%s\n", p); + p = rrow[DB_file]; + if (p == NULL) + p = "undef"; + BIO_printf(bio_err, "File name :%s\n", p); + p = rrow[DB_name]; + if (p == NULL) + p = "undef"; + BIO_printf(bio_err, "Subject Name :%s\n", p); + ok = -1; /* This is now a 'bad' error. */ + goto end; + } + + /* We are now totally happy, lets make and sign the certificate */ + if (verbose) + BIO_printf(bio_err, + "Everything appears to be ok, creating and signing the certificate\n"); + + if ((ret = X509_new()) == NULL) + goto end; + +#ifdef X509_V3 + /* Make it an X509 v3 certificate. */ + if (!X509_set_version(ret, 2)) + goto end; +#endif + + if (BN_to_ASN1_INTEGER(serial, X509_get_serialNumber(ret)) == NULL) + goto end; + if (selfsign) { + if (!X509_set_issuer_name(ret, subject)) + goto end; + } else { + if (!X509_set_issuer_name(ret, X509_get_subject_name(x509))) + goto end; + } + + if (!set_cert_times(ret, startdate, enddate, days)) + goto end; + + if (enddate != NULL) { + int tdays; + ASN1_TIME_diff(&tdays, NULL, NULL, X509_get0_notAfter(ret)); + days = tdays; + } + + if (!X509_set_subject_name(ret, subject)) + goto end; + + pktmp = X509_REQ_get0_pubkey(req); + i = X509_set_pubkey(ret, pktmp); + if (!i) + goto end; + + /* Lets add the extensions, if there are any */ + if (ext_sect) { + X509V3_CTX ctx; + X509_set_version(ret, 2); + + /* Initialize the context structure */ + if (selfsign) + X509V3_set_ctx(&ctx, ret, ret, req, NULL, 0); + else + X509V3_set_ctx(&ctx, x509, ret, req, NULL, 0); + + if (extconf) { + if (verbose) + BIO_printf(bio_err, "Extra configuration file found\n"); + + /* Use the extconf configuration db LHASH */ + X509V3_set_nconf(&ctx, extconf); + + /* Test the structure (needed?) */ + /* X509V3_set_ctx_test(&ctx); */ + + /* Adds exts contained in the configuration file */ + if (!X509V3_EXT_add_nconf(extconf, &ctx, ext_sect, ret)) { + BIO_printf(bio_err, + "ERROR: adding extensions in section %s\n", + ext_sect); + ERR_print_errors(bio_err); + goto end; + } + if (verbose) + BIO_printf(bio_err, + "Successfully added extensions from file.\n"); + } else if (ext_sect) { + /* We found extensions to be set from config file */ + X509V3_set_nconf(&ctx, lconf); + + if (!X509V3_EXT_add_nconf(lconf, &ctx, ext_sect, ret)) { + BIO_printf(bio_err, + "ERROR: adding extensions in section %s\n", + ext_sect); + ERR_print_errors(bio_err); + goto end; + } + + if (verbose) + BIO_printf(bio_err, + "Successfully added extensions from config\n"); + } + } + + /* Copy extensions from request (if any) */ + + if (!copy_extensions(ret, req, ext_copy)) { + BIO_printf(bio_err, "ERROR: adding extensions from request\n"); + ERR_print_errors(bio_err); + goto end; + } + + /* Set the right value for the noemailDN option */ + if (email_dn == 0) { + if (!X509_set_subject_name(ret, dn_subject)) + goto end; + } + + if (!default_op) { + BIO_printf(bio_err, "Certificate Details:\n"); + /* + * Never print signature details because signature not present + */ + certopt |= X509_FLAG_NO_SIGDUMP | X509_FLAG_NO_SIGNAME; + X509_print_ex(bio_err, ret, nameopt, certopt); + } + + BIO_printf(bio_err, "Certificate is to be certified until "); + ASN1_TIME_print(bio_err, X509_get0_notAfter(ret)); + if (days) + BIO_printf(bio_err, " (%ld days)", days); + BIO_printf(bio_err, "\n"); + + if (!batch) { + + BIO_printf(bio_err, "Sign the certificate? [y/n]:"); + (void)BIO_flush(bio_err); + buf[0] = '\0'; + if (!fgets(buf, sizeof(buf) - 1, stdin)) { + BIO_printf(bio_err, + "CERTIFICATE WILL NOT BE CERTIFIED: I/O error\n"); + ok = 0; + goto end; + } + if (!((buf[0] == 'y') || (buf[0] == 'Y'))) { + BIO_printf(bio_err, "CERTIFICATE WILL NOT BE CERTIFIED\n"); + ok = 0; + goto end; + } + } + + pktmp = X509_get0_pubkey(ret); + if (EVP_PKEY_missing_parameters(pktmp) && + !EVP_PKEY_missing_parameters(pkey)) + EVP_PKEY_copy_parameters(pktmp, pkey); + + if (!do_X509_sign(ret, pkey, dgst, sigopts)) + goto end; + + /* We now just add it to the database */ + row[DB_type] = OPENSSL_strdup("V"); + tm = X509_get0_notAfter(ret); + row[DB_exp_date] = app_malloc(tm->length + 1, "row expdate"); + memcpy(row[DB_exp_date], tm->data, tm->length); + row[DB_exp_date][tm->length] = '\0'; + row[DB_rev_date] = NULL; + row[DB_file] = OPENSSL_strdup("unknown"); + row[DB_name] = X509_NAME_oneline(X509_get_subject_name(ret), NULL, 0); + + if ((row[DB_type] == NULL) || (row[DB_exp_date] == NULL) || + (row[DB_file] == NULL) || (row[DB_name] == NULL)) { + BIO_printf(bio_err, "Memory allocation failure\n"); + goto end; + } + + irow = app_malloc(sizeof(*irow) * (DB_NUMBER + 1), "row space"); + for (i = 0; i < DB_NUMBER; i++) { + irow[i] = row[i]; + row[i] = NULL; + } + irow[DB_NUMBER] = NULL; + + if (!TXT_DB_insert(db->db, irow)) { + BIO_printf(bio_err, "failed to update database\n"); + BIO_printf(bio_err, "TXT_DB error number %ld\n", db->db->error); + goto end; + } + ok = 1; + end: + for (i = 0; i < DB_NUMBER; i++) + OPENSSL_free(row[i]); + + X509_NAME_free(CAname); + X509_NAME_free(subject); + if (dn_subject != subject) + X509_NAME_free(dn_subject); + if (ok <= 0) + X509_free(ret); + else + *xret = ret; + return (ok); +} + +static void write_new_certificate(BIO *bp, X509 *x, int output_der, + int notext) +{ + + if (output_der) { + (void)i2d_X509_bio(bp, x); + return; + } + if (!notext) + X509_print(bp, x); + PEM_write_bio_X509(bp, x); +} + +static int certify_spkac(X509 **xret, const char *infile, EVP_PKEY *pkey, + X509 *x509, const EVP_MD *dgst, + STACK_OF(OPENSSL_STRING) *sigopts, + STACK_OF(CONF_VALUE) *policy, CA_DB *db, + BIGNUM *serial, const char *subj, unsigned long chtype, + int multirdn, int email_dn, const char *startdate, + const char *enddate, long days, const char *ext_sect, + CONF *lconf, int verbose, unsigned long certopt, + unsigned long nameopt, int default_op, int ext_copy) +{ + STACK_OF(CONF_VALUE) *sk = NULL; + LHASH_OF(CONF_VALUE) *parms = NULL; + X509_REQ *req = NULL; + CONF_VALUE *cv = NULL; + NETSCAPE_SPKI *spki = NULL; + char *type, *buf; + EVP_PKEY *pktmp = NULL; + X509_NAME *n = NULL; + X509_NAME_ENTRY *ne = NULL; + int ok = -1, i, j; + long errline; + int nid; + + /* + * Load input file into a hash table. (This is just an easy + * way to read and parse the file, then put it into a convenient + * STACK format). + */ + parms = CONF_load(NULL, infile, &errline); + if (parms == NULL) { + BIO_printf(bio_err, "error on line %ld of %s\n", errline, infile); + ERR_print_errors(bio_err); + goto end; + } + + sk = CONF_get_section(parms, "default"); + if (sk_CONF_VALUE_num(sk) == 0) { + BIO_printf(bio_err, "no name/value pairs found in %s\n", infile); + goto end; + } + + /* + * Now create a dummy X509 request structure. We don't actually + * have an X509 request, but we have many of the components + * (a public key, various DN components). The idea is that we + * put these components into the right X509 request structure + * and we can use the same code as if you had a real X509 request. + */ + req = X509_REQ_new(); + if (req == NULL) { + ERR_print_errors(bio_err); + goto end; + } + + /* + * Build up the subject name set. + */ + n = X509_REQ_get_subject_name(req); + + for (i = 0;; i++) { + if (sk_CONF_VALUE_num(sk) <= i) + break; + + cv = sk_CONF_VALUE_value(sk, i); + type = cv->name; + /* + * Skip past any leading X. X: X, etc to allow for multiple instances + */ + for (buf = cv->name; *buf; buf++) + if ((*buf == ':') || (*buf == ',') || (*buf == '.')) { + buf++; + if (*buf) + type = buf; + break; + } + + buf = cv->value; + if ((nid = OBJ_txt2nid(type)) == NID_undef) { + if (strcmp(type, "SPKAC") == 0) { + spki = NETSCAPE_SPKI_b64_decode(cv->value, -1); + if (spki == NULL) { + BIO_printf(bio_err, + "unable to load Netscape SPKAC structure\n"); + ERR_print_errors(bio_err); + goto end; + } + } + continue; + } + + if (!X509_NAME_add_entry_by_NID(n, nid, chtype, + (unsigned char *)buf, -1, -1, 0)) + goto end; + } + if (spki == NULL) { + BIO_printf(bio_err, "Netscape SPKAC structure not found in %s\n", + infile); + goto end; + } + + /* + * Now extract the key from the SPKI structure. + */ + + BIO_printf(bio_err, + "Check that the SPKAC request matches the signature\n"); + + if ((pktmp = NETSCAPE_SPKI_get_pubkey(spki)) == NULL) { + BIO_printf(bio_err, "error unpacking SPKAC public key\n"); + goto end; + } + + j = NETSCAPE_SPKI_verify(spki, pktmp); + if (j <= 0) { + EVP_PKEY_free(pktmp); + BIO_printf(bio_err, + "signature verification failed on SPKAC public key\n"); + goto end; + } + BIO_printf(bio_err, "Signature ok\n"); + + X509_REQ_set_pubkey(req, pktmp); + EVP_PKEY_free(pktmp); + ok = do_body(xret, pkey, x509, dgst, sigopts, policy, db, serial, subj, + chtype, multirdn, email_dn, startdate, enddate, days, 1, + verbose, req, ext_sect, lconf, certopt, nameopt, default_op, + ext_copy, 0); + end: + X509_REQ_free(req); + CONF_free(parms); + NETSCAPE_SPKI_free(spki); + X509_NAME_ENTRY_free(ne); + + return (ok); +} + +static int check_time_format(const char *str) +{ + return ASN1_TIME_set_string(NULL, str); +} + +static int do_revoke(X509 *x509, CA_DB *db, int type, char *value) +{ + const ASN1_TIME *tm = NULL; + char *row[DB_NUMBER], **rrow, **irow; + char *rev_str = NULL; + BIGNUM *bn = NULL; + int ok = -1, i; + + for (i = 0; i < DB_NUMBER; i++) + row[i] = NULL; + row[DB_name] = X509_NAME_oneline(X509_get_subject_name(x509), NULL, 0); + bn = ASN1_INTEGER_to_BN(X509_get_serialNumber(x509), NULL); + if (!bn) + goto end; + if (BN_is_zero(bn)) + row[DB_serial] = OPENSSL_strdup("00"); + else + row[DB_serial] = BN_bn2hex(bn); + BN_free(bn); + if ((row[DB_name] == NULL) || (row[DB_serial] == NULL)) { + BIO_printf(bio_err, "Memory allocation failure\n"); + goto end; + } + /* + * We have to lookup by serial number because name lookup skips revoked + * certs + */ + rrow = TXT_DB_get_by_index(db->db, DB_serial, row); + if (rrow == NULL) { + BIO_printf(bio_err, + "Adding Entry with serial number %s to DB for %s\n", + row[DB_serial], row[DB_name]); + + /* We now just add it to the database */ + row[DB_type] = OPENSSL_strdup("V"); + tm = X509_get0_notAfter(x509); + row[DB_exp_date] = app_malloc(tm->length + 1, "row exp_data"); + memcpy(row[DB_exp_date], tm->data, tm->length); + row[DB_exp_date][tm->length] = '\0'; + row[DB_rev_date] = NULL; + row[DB_file] = OPENSSL_strdup("unknown"); + + irow = app_malloc(sizeof(*irow) * (DB_NUMBER + 1), "row ptr"); + for (i = 0; i < DB_NUMBER; i++) { + irow[i] = row[i]; + row[i] = NULL; + } + irow[DB_NUMBER] = NULL; + + if (!TXT_DB_insert(db->db, irow)) { + BIO_printf(bio_err, "failed to update database\n"); + BIO_printf(bio_err, "TXT_DB error number %ld\n", db->db->error); + goto end; + } + + /* Revoke Certificate */ + if (type == -1) + ok = 1; + else + ok = do_revoke(x509, db, type, value); + + goto end; + + } else if (index_name_cmp_noconst(row, rrow)) { + BIO_printf(bio_err, "ERROR:name does not match %s\n", row[DB_name]); + goto end; + } else if (type == -1) { + BIO_printf(bio_err, "ERROR:Already present, serial number %s\n", + row[DB_serial]); + goto end; + } else if (rrow[DB_type][0] == 'R') { + BIO_printf(bio_err, "ERROR:Already revoked, serial number %s\n", + row[DB_serial]); + goto end; + } else { + BIO_printf(bio_err, "Revoking Certificate %s.\n", rrow[DB_serial]); + rev_str = make_revocation_str(type, value); + if (!rev_str) { + BIO_printf(bio_err, "Error in revocation arguments\n"); + goto end; + } + rrow[DB_type][0] = 'R'; + rrow[DB_type][1] = '\0'; + rrow[DB_rev_date] = rev_str; + } + ok = 1; + end: + for (i = 0; i < DB_NUMBER; i++) { + OPENSSL_free(row[i]); + } + return (ok); +} + +static int get_certificate_status(const char *serial, CA_DB *db) +{ + char *row[DB_NUMBER], **rrow; + int ok = -1, i; + size_t serial_len = strlen(serial); + + /* Free Resources */ + for (i = 0; i < DB_NUMBER; i++) + row[i] = NULL; + + /* Malloc needed char spaces */ + row[DB_serial] = app_malloc(serial_len + 2, "row serial#"); + + if (serial_len % 2) { + /* + * Set the first char to 0 + */ ; + row[DB_serial][0] = '0'; + + /* Copy String from serial to row[DB_serial] */ + memcpy(row[DB_serial] + 1, serial, serial_len); + row[DB_serial][serial_len + 1] = '\0'; + } else { + /* Copy String from serial to row[DB_serial] */ + memcpy(row[DB_serial], serial, serial_len); + row[DB_serial][serial_len] = '\0'; + } + + /* Make it Upper Case */ + for (i = 0; row[DB_serial][i] != '\0'; i++) + row[DB_serial][i] = toupper((unsigned char)row[DB_serial][i]); + + ok = 1; + + /* Search for the certificate */ + rrow = TXT_DB_get_by_index(db->db, DB_serial, row); + if (rrow == NULL) { + BIO_printf(bio_err, "Serial %s not present in db.\n", row[DB_serial]); + ok = -1; + goto end; + } else if (rrow[DB_type][0] == 'V') { + BIO_printf(bio_err, "%s=Valid (%c)\n", + row[DB_serial], rrow[DB_type][0]); + goto end; + } else if (rrow[DB_type][0] == 'R') { + BIO_printf(bio_err, "%s=Revoked (%c)\n", + row[DB_serial], rrow[DB_type][0]); + goto end; + } else if (rrow[DB_type][0] == 'E') { + BIO_printf(bio_err, "%s=Expired (%c)\n", + row[DB_serial], rrow[DB_type][0]); + goto end; + } else if (rrow[DB_type][0] == 'S') { + BIO_printf(bio_err, "%s=Suspended (%c)\n", + row[DB_serial], rrow[DB_type][0]); + goto end; + } else { + BIO_printf(bio_err, "%s=Unknown (%c).\n", + row[DB_serial], rrow[DB_type][0]); + ok = -1; + } + end: + for (i = 0; i < DB_NUMBER; i++) { + OPENSSL_free(row[i]); + } + return (ok); +} + +static int do_updatedb(CA_DB *db) +{ + ASN1_UTCTIME *a_tm = NULL; + int i, cnt = 0; + int db_y2k, a_y2k; /* flags = 1 if y >= 2000 */ + char **rrow, *a_tm_s; + + a_tm = ASN1_UTCTIME_new(); + if (a_tm == NULL) + return -1; + + /* get actual time and make a string */ + a_tm = X509_gmtime_adj(a_tm, 0); + a_tm_s = app_malloc(a_tm->length + 1, "time string"); + + memcpy(a_tm_s, a_tm->data, a_tm->length); + a_tm_s[a_tm->length] = '\0'; + + if (strncmp(a_tm_s, "49", 2) <= 0) + a_y2k = 1; + else + a_y2k = 0; + + for (i = 0; i < sk_OPENSSL_PSTRING_num(db->db->data); i++) { + rrow = sk_OPENSSL_PSTRING_value(db->db->data, i); + + if (rrow[DB_type][0] == 'V') { + /* ignore entries that are not valid */ + if (strncmp(rrow[DB_exp_date], "49", 2) <= 0) + db_y2k = 1; + else + db_y2k = 0; + + if (db_y2k == a_y2k) { + /* all on the same y2k side */ + if (strcmp(rrow[DB_exp_date], a_tm_s) <= 0) { + rrow[DB_type][0] = 'E'; + rrow[DB_type][1] = '\0'; + cnt++; + + BIO_printf(bio_err, "%s=Expired\n", rrow[DB_serial]); + } + } else if (db_y2k < a_y2k) { + rrow[DB_type][0] = 'E'; + rrow[DB_type][1] = '\0'; + cnt++; + + BIO_printf(bio_err, "%s=Expired\n", rrow[DB_serial]); + } + + } + } + + ASN1_UTCTIME_free(a_tm); + OPENSSL_free(a_tm_s); + return (cnt); +} + +static const char *crl_reasons[] = { + /* CRL reason strings */ + "unspecified", + "keyCompromise", + "CACompromise", + "affiliationChanged", + "superseded", + "cessationOfOperation", + "certificateHold", + "removeFromCRL", + /* Additional pseudo reasons */ + "holdInstruction", + "keyTime", + "CAkeyTime" +}; + +#define NUM_REASONS OSSL_NELEM(crl_reasons) + +/* + * Given revocation information convert to a DB string. The format of the + * string is: revtime[,reason,extra]. Where 'revtime' is the revocation time + * (the current time). 'reason' is the optional CRL reason and 'extra' is any + * additional argument + */ + +char *make_revocation_str(int rev_type, char *rev_arg) +{ + char *str; + const char *other = NULL; + const char *reason = NULL; + ASN1_OBJECT *otmp; + ASN1_UTCTIME *revtm = NULL; + int i; + switch (rev_type) { + case REV_NONE: + break; + + case REV_CRL_REASON: + for (i = 0; i < 8; i++) { + if (strcasecmp(rev_arg, crl_reasons[i]) == 0) { + reason = crl_reasons[i]; + break; + } + } + if (reason == NULL) { + BIO_printf(bio_err, "Unknown CRL reason %s\n", rev_arg); + return NULL; + } + break; + + case REV_HOLD: + /* Argument is an OID */ + + otmp = OBJ_txt2obj(rev_arg, 0); + ASN1_OBJECT_free(otmp); + + if (otmp == NULL) { + BIO_printf(bio_err, "Invalid object identifier %s\n", rev_arg); + return NULL; + } + + reason = "holdInstruction"; + other = rev_arg; + break; + + case REV_KEY_COMPROMISE: + case REV_CA_COMPROMISE: + + /* Argument is the key compromise time */ + if (!ASN1_GENERALIZEDTIME_set_string(NULL, rev_arg)) { + BIO_printf(bio_err, + "Invalid time format %s. Need YYYYMMDDHHMMSSZ\n", + rev_arg); + return NULL; + } + other = rev_arg; + if (rev_type == REV_KEY_COMPROMISE) + reason = "keyTime"; + else + reason = "CAkeyTime"; + + break; + + } + + revtm = X509_gmtime_adj(NULL, 0); + + if (!revtm) + return NULL; + + i = revtm->length + 1; + + if (reason) + i += strlen(reason) + 1; + if (other) + i += strlen(other) + 1; + + str = app_malloc(i, "revocation reason"); + OPENSSL_strlcpy(str, (char *)revtm->data, i); + if (reason) { + OPENSSL_strlcat(str, ",", i); + OPENSSL_strlcat(str, reason, i); + } + if (other) { + OPENSSL_strlcat(str, ",", i); + OPENSSL_strlcat(str, other, i); + } + ASN1_UTCTIME_free(revtm); + return str; +} + +/*- + * Convert revocation field to X509_REVOKED entry + * return code: + * 0 error + * 1 OK + * 2 OK and some extensions added (i.e. V2 CRL) + */ + +int make_revoked(X509_REVOKED *rev, const char *str) +{ + char *tmp = NULL; + int reason_code = -1; + int i, ret = 0; + ASN1_OBJECT *hold = NULL; + ASN1_GENERALIZEDTIME *comp_time = NULL; + ASN1_ENUMERATED *rtmp = NULL; + + ASN1_TIME *revDate = NULL; + + i = unpack_revinfo(&revDate, &reason_code, &hold, &comp_time, str); + + if (i == 0) + goto end; + + if (rev && !X509_REVOKED_set_revocationDate(rev, revDate)) + goto end; + + if (rev && (reason_code != OCSP_REVOKED_STATUS_NOSTATUS)) { + rtmp = ASN1_ENUMERATED_new(); + if (rtmp == NULL || !ASN1_ENUMERATED_set(rtmp, reason_code)) + goto end; + if (!X509_REVOKED_add1_ext_i2d(rev, NID_crl_reason, rtmp, 0, 0)) + goto end; + } + + if (rev && comp_time) { + if (!X509_REVOKED_add1_ext_i2d + (rev, NID_invalidity_date, comp_time, 0, 0)) + goto end; + } + if (rev && hold) { + if (!X509_REVOKED_add1_ext_i2d + (rev, NID_hold_instruction_code, hold, 0, 0)) + goto end; + } + + if (reason_code != OCSP_REVOKED_STATUS_NOSTATUS) + ret = 2; + else + ret = 1; + + end: + + OPENSSL_free(tmp); + ASN1_OBJECT_free(hold); + ASN1_GENERALIZEDTIME_free(comp_time); + ASN1_ENUMERATED_free(rtmp); + ASN1_TIME_free(revDate); + + return ret; +} + +static int old_entry_print(const ASN1_OBJECT *obj, const ASN1_STRING *str) +{ + char buf[25], *pbuf; + const char *p; + int j; + + j = i2a_ASN1_OBJECT(bio_err, obj); + pbuf = buf; + for (j = 22 - j; j > 0; j--) + *(pbuf++) = ' '; + *(pbuf++) = ':'; + *(pbuf++) = '\0'; + BIO_puts(bio_err, buf); + + if (str->type == V_ASN1_PRINTABLESTRING) + BIO_printf(bio_err, "PRINTABLE:'"); + else if (str->type == V_ASN1_T61STRING) + BIO_printf(bio_err, "T61STRING:'"); + else if (str->type == V_ASN1_IA5STRING) + BIO_printf(bio_err, "IA5STRING:'"); + else if (str->type == V_ASN1_UNIVERSALSTRING) + BIO_printf(bio_err, "UNIVERSALSTRING:'"); + else + BIO_printf(bio_err, "ASN.1 %2d:'", str->type); + + p = (const char *)str->data; + for (j = str->length; j > 0; j--) { + if ((*p >= ' ') && (*p <= '~')) + BIO_printf(bio_err, "%c", *p); + else if (*p & 0x80) + BIO_printf(bio_err, "\\0x%02X", *p); + else if ((unsigned char)*p == 0xf7) + BIO_printf(bio_err, "^?"); + else + BIO_printf(bio_err, "^%c", *p + '@'); + p++; + } + BIO_printf(bio_err, "'\n"); + return 1; +} + +int unpack_revinfo(ASN1_TIME **prevtm, int *preason, ASN1_OBJECT **phold, + ASN1_GENERALIZEDTIME **pinvtm, const char *str) +{ + char *tmp; + char *rtime_str, *reason_str = NULL, *arg_str = NULL, *p; + int reason_code = -1; + int ret = 0; + unsigned int i; + ASN1_OBJECT *hold = NULL; + ASN1_GENERALIZEDTIME *comp_time = NULL; + + tmp = OPENSSL_strdup(str); + if (!tmp) { + BIO_printf(bio_err, "memory allocation failure\n"); + goto end; + } + + p = strchr(tmp, ','); + + rtime_str = tmp; + + if (p) { + *p = '\0'; + p++; + reason_str = p; + p = strchr(p, ','); + if (p) { + *p = '\0'; + arg_str = p + 1; + } + } + + if (prevtm) { + *prevtm = ASN1_UTCTIME_new(); + if (*prevtm == NULL) { + BIO_printf(bio_err, "memory allocation failure\n"); + goto end; + } + if (!ASN1_UTCTIME_set_string(*prevtm, rtime_str)) { + BIO_printf(bio_err, "invalid revocation date %s\n", rtime_str); + goto end; + } + } + if (reason_str) { + for (i = 0; i < NUM_REASONS; i++) { + if (strcasecmp(reason_str, crl_reasons[i]) == 0) { + reason_code = i; + break; + } + } + if (reason_code == OCSP_REVOKED_STATUS_NOSTATUS) { + BIO_printf(bio_err, "invalid reason code %s\n", reason_str); + goto end; + } + + if (reason_code == 7) + reason_code = OCSP_REVOKED_STATUS_REMOVEFROMCRL; + else if (reason_code == 8) { /* Hold instruction */ + if (!arg_str) { + BIO_printf(bio_err, "missing hold instruction\n"); + goto end; + } + reason_code = OCSP_REVOKED_STATUS_CERTIFICATEHOLD; + hold = OBJ_txt2obj(arg_str, 0); + + if (!hold) { + BIO_printf(bio_err, "invalid object identifier %s\n", + arg_str); + goto end; + } + if (phold) + *phold = hold; + else + ASN1_OBJECT_free(hold); + } else if ((reason_code == 9) || (reason_code == 10)) { + if (!arg_str) { + BIO_printf(bio_err, "missing compromised time\n"); + goto end; + } + comp_time = ASN1_GENERALIZEDTIME_new(); + if (comp_time == NULL) { + BIO_printf(bio_err, "memory allocation failure\n"); + goto end; + } + if (!ASN1_GENERALIZEDTIME_set_string(comp_time, arg_str)) { + BIO_printf(bio_err, "invalid compromised time %s\n", arg_str); + goto end; + } + if (reason_code == 9) + reason_code = OCSP_REVOKED_STATUS_KEYCOMPROMISE; + else + reason_code = OCSP_REVOKED_STATUS_CACOMPROMISE; + } + } + + if (preason) + *preason = reason_code; + if (pinvtm) { + *pinvtm = comp_time; + comp_time = NULL; + } + + ret = 1; + + end: + + OPENSSL_free(tmp); + ASN1_GENERALIZEDTIME_free(comp_time); + + return ret; +} diff --git a/apps/cert.pem b/apps/cert.pem new file mode 100644 index 000000000..de4a77ac6 --- /dev/null +++ b/apps/cert.pem @@ -0,0 +1,11 @@ +-----BEGIN CERTIFICATE----- +MIIBoDCCAUoCAQAwDQYJKoZIhvcNAQEEBQAwYzELMAkGA1UEBhMCQVUxEzARBgNV +BAgTClF1ZWVuc2xhbmQxGjAYBgNVBAoTEUNyeXB0U29mdCBQdHkgTHRkMSMwIQYD +VQQDExpTZXJ2ZXIgdGVzdCBjZXJ0ICg1MTIgYml0KTAeFw05NzA5MDkwMzQxMjZa +Fw05NzEwMDkwMzQxMjZaMF4xCzAJBgNVBAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0 +YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxFzAVBgNVBAMT +DkVyaWMgdGhlIFlvdW5nMFEwCQYFKw4DAgwFAANEAAJBALVEqPODnpI4rShlY8S7 +tB713JNvabvn6Gned7zylwLLiXQAo/PAT6mfdWPTyCX9RlId/Aroh1ou893BA32Q +sggwDQYJKoZIhvcNAQEEBQADQQCU5SSgapJSdRXJoX+CpCvFy+JVh9HpSjCpSNKO +19raHv98hKAUJuP9HyM+SUsffO6mAIgitUaqW8/wDMePhEC3 +-----END CERTIFICATE----- diff --git a/apps/ciphers.c b/apps/ciphers.c new file mode 100644 index 000000000..8f49fd163 --- /dev/null +++ b/apps/ciphers.c @@ -0,0 +1,252 @@ +/* + * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#include +#include +#include "apps.h" +#include +#include + +typedef enum OPTION_choice { + OPT_ERR = -1, OPT_EOF = 0, OPT_HELP, + OPT_STDNAME, + OPT_SSL3, + OPT_TLS1, + OPT_TLS1_1, + OPT_TLS1_2, + OPT_GMTLS, + OPT_PSK, + OPT_SRP, + OPT_V, OPT_UPPER_V, OPT_S +} OPTION_CHOICE; + +OPTIONS ciphers_options[] = { + {"help", OPT_HELP, '-', "Display this summary"}, + {"v", OPT_V, '-', "Verbose listing of the SSL/TLS ciphers"}, + {"V", OPT_UPPER_V, '-', "Even more verbose"}, + {"s", OPT_S, '-', "Only supported ciphers"}, +#ifndef OPENSSL_NO_SSL3 + {"ssl3", OPT_SSL3, '-', "SSL3 mode"}, +#endif +#ifndef OPENSSL_NO_TLS1 + {"tls1", OPT_TLS1, '-', "TLS1 mode"}, +#endif +#ifndef OPENSSL_NO_TLS1_1 + {"tls1_1", OPT_TLS1_1, '-', "TLS1.1 mode"}, +#endif +#ifndef OPENSSL_NO_TLS1_2 + {"tls1_2", OPT_TLS1_2, '-', "TLS1.2 mode"}, +#endif +#ifndef OPENSSL_NO_GMTLS_METHOD + {"gmtls", OPT_GMTLS, '-', "GMTLS1.1 mode"}, +#endif +#ifndef OPENSSL_NO_SSL_TRACE + {"stdname", OPT_STDNAME, '-', "Show standard cipher names"}, +#endif +#ifndef OPENSSL_NO_PSK + {"psk", OPT_PSK, '-', "include ciphersuites requiring PSK"}, +#endif +#ifndef OPENSSL_NO_SRP + {"srp", OPT_SRP, '-', "include ciphersuites requiring SRP"}, +#endif + {NULL} +}; + +#ifndef OPENSSL_NO_PSK +static unsigned int dummy_psk(SSL *ssl, const char *hint, char *identity, + unsigned int max_identity_len, + unsigned char *psk, + unsigned int max_psk_len) +{ + return 0; +} +#endif +#ifndef OPENSSL_NO_SRP +static char *dummy_srp(SSL *ssl, void *arg) +{ + return ""; +} +#endif + +int ciphers_main(int argc, char **argv) +{ + SSL_CTX *ctx = NULL; + SSL *ssl = NULL; + STACK_OF(SSL_CIPHER) *sk = NULL; + const SSL_METHOD *meth = TLS_server_method(); + int ret = 1, i, verbose = 0, Verbose = 0, use_supported = 0; +#ifndef OPENSSL_NO_SSL_TRACE + int stdname = 0; +#endif +#ifndef OPENSSL_NO_PSK + int psk = 0; +#endif +#ifndef OPENSSL_NO_SRP + int srp = 0; +#endif + const char *p; + char *ciphers = NULL, *prog; + char buf[512]; + OPTION_CHOICE o; + int min_version = 0, max_version = 0; + + prog = opt_init(argc, argv, ciphers_options); + while ((o = opt_next()) != OPT_EOF) { + switch (o) { + case OPT_EOF: + case OPT_ERR: + opthelp: + BIO_printf(bio_err, "%s: Use -help for summary.\n", prog); + goto end; + case OPT_HELP: + opt_help(ciphers_options); + ret = 0; + goto end; + case OPT_V: + verbose = 1; + break; + case OPT_UPPER_V: + verbose = Verbose = 1; + break; + case OPT_S: + use_supported = 1; + break; + case OPT_STDNAME: +#ifndef OPENSSL_NO_SSL_TRACE + stdname = verbose = 1; +#endif + break; + case OPT_SSL3: + min_version = SSL3_VERSION; + max_version = SSL3_VERSION; + break; + case OPT_TLS1: + min_version = TLS1_VERSION; + max_version = TLS1_VERSION; + break; + case OPT_TLS1_1: + min_version = TLS1_1_VERSION; + max_version = TLS1_1_VERSION; + break; + case OPT_TLS1_2: + min_version = TLS1_2_VERSION; + max_version = TLS1_2_VERSION; + break; +#ifndef OPENSSL_NO_GMTLS_METHOD + case OPT_GMTLS: + min_version = GMTLS_VERSION; + max_version = GMTLS_VERSION; + break; +#endif + case OPT_PSK: +#ifndef OPENSSL_NO_PSK + psk = 1; +#endif + break; + case OPT_SRP: +#ifndef OPENSSL_NO_SRP + srp = 1; +#endif + break; + } + } + argv = opt_rest(); + argc = opt_num_rest(); + + if (argc == 1) + ciphers = *argv; + else if (argc != 0) + goto opthelp; + + ctx = SSL_CTX_new(meth); + if (ctx == NULL) + goto err; + if (SSL_CTX_set_min_proto_version(ctx, min_version) == 0) + goto err; + if (SSL_CTX_set_max_proto_version(ctx, max_version) == 0) + goto err; + +#ifndef OPENSSL_NO_PSK + if (psk) + SSL_CTX_set_psk_client_callback(ctx, dummy_psk); +#endif +#ifndef OPENSSL_NO_SRP + if (srp) + SSL_CTX_set_srp_client_pwd_callback(ctx, dummy_srp); +#endif + if (ciphers != NULL) { + if (!SSL_CTX_set_cipher_list(ctx, ciphers)) { + BIO_printf(bio_err, "Error in cipher list\n"); + goto err; + } + } + ssl = SSL_new(ctx); + if (ssl == NULL) + goto err; + + if (use_supported) + sk = SSL_get1_supported_ciphers(ssl); + else + sk = SSL_get_ciphers(ssl); + + if (!verbose) { + for (i = 0; i < sk_SSL_CIPHER_num(sk); i++) { + const SSL_CIPHER *c = sk_SSL_CIPHER_value(sk, i); + p = SSL_CIPHER_get_name(c); + if (p == NULL) + break; + if (i != 0) + BIO_printf(bio_out, ":"); + BIO_printf(bio_out, "%s", p); + } + BIO_printf(bio_out, "\n"); + } else { + + for (i = 0; i < sk_SSL_CIPHER_num(sk); i++) { + const SSL_CIPHER *c; + + c = sk_SSL_CIPHER_value(sk, i); + + if (Verbose) { + unsigned long id = SSL_CIPHER_get_id(c); + int id0 = (int)(id >> 24); + int id1 = (int)((id >> 16) & 0xffL); + int id2 = (int)((id >> 8) & 0xffL); + int id3 = (int)(id & 0xffL); + + if ((id & 0xff000000L) == 0x03000000L) + BIO_printf(bio_out, " 0x%02X,0x%02X - ", id2, id3); /* SSL3 + * cipher */ + else + BIO_printf(bio_out, "0x%02X,0x%02X,0x%02X,0x%02X - ", id0, id1, id2, id3); /* whatever */ + } +#ifndef OPENSSL_NO_SSL_TRACE + if (stdname) { + const char *nm = SSL_CIPHER_standard_name(c); + if (nm == NULL) + nm = "UNKNOWN"; + BIO_printf(bio_out, "%-40s - ", nm); + } +#endif + BIO_puts(bio_out, SSL_CIPHER_description(c, buf, sizeof buf)); + } + } + + ret = 0; + goto end; + err: + ERR_print_errors(bio_err); + end: + if (use_supported) + sk_SSL_CIPHER_free(sk); + SSL_CTX_free(ctx); + SSL_free(ssl); + return (ret); +} diff --git a/apps/client.pem b/apps/client.pem new file mode 100644 index 000000000..e7a47a73f --- /dev/null +++ b/apps/client.pem @@ -0,0 +1,52 @@ +subject= C = UK, O = OpenSSL Group, OU = FOR TESTING PURPOSES ONLY, CN = Test Client Cert +issuer= C = UK, O = OpenSSL Group, OU = FOR TESTING PURPOSES ONLY, CN = OpenSSL Test Intermediate CA +-----BEGIN CERTIFICATE----- +MIID5zCCAs+gAwIBAgIJALnu1NlVpZ6yMA0GCSqGSIb3DQEBBQUAMHAxCzAJBgNV +BAYTAlVLMRYwFAYDVQQKDA1PcGVuU1NMIEdyb3VwMSIwIAYDVQQLDBlGT1IgVEVT +VElORyBQVVJQT1NFUyBPTkxZMSUwIwYDVQQDDBxPcGVuU1NMIFRlc3QgSW50ZXJt +ZWRpYXRlIENBMB4XDTExMTIwODE0MDE0OFoXDTIxMTAxNjE0MDE0OFowZDELMAkG +A1UEBhMCVUsxFjAUBgNVBAoMDU9wZW5TU0wgR3JvdXAxIjAgBgNVBAsMGUZPUiBU +RVNUSU5HIFBVUlBPU0VTIE9OTFkxGTAXBgNVBAMMEFRlc3QgQ2xpZW50IENlcnQw +ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC0ranbHRLcLVqN+0BzcZpY ++yOLqxzDWT1LD9eW1stC4NzXX9/DCtSIVyN7YIHdGLrIPr64IDdXXaMRzgZ2rOKs +lmHCAiFpO/ja99gGCJRxH0xwQatqAULfJVHeUhs7OEGOZc2nWifjqKvGfNTilP7D +nwi69ipQFq9oS19FmhwVHk2wg7KZGHI1qDyG04UrfCZMRitvS9+UVhPpIPjuiBi2 +x3/FZIpL5gXJvvFK6xHY63oq2asyzBATntBgnP4qJFWWcvRx24wF1PnZabxuVoL2 +bPnQ/KvONDrw3IdqkKhYNTul7jEcu3OlcZIMw+7DiaKJLAzKb/bBF5gm/pwW6As9 +AgMBAAGjgY8wgYwwDAYDVR0TAQH/BAIwADAOBgNVHQ8BAf8EBAMCBeAwLAYJYIZI +AYb4QgENBB8WHU9wZW5TU0wgR2VuZXJhdGVkIENlcnRpZmljYXRlMB0GA1UdDgQW +BBSZHKyLoTh7Mb409Zn/mK1ceSDAjDAfBgNVHSMEGDAWgBQ2w2yI55X+sL3szj49 +hqshgYfa2jANBgkqhkiG9w0BAQUFAAOCAQEAD0mL7PtPYgCEuDyOQSbLpeND5hVS +curxQdGnrJ6Acrhodb7E9ccATokeb0PLx6HBLQUicxhTZIQ9FbO43YkQcOU6C3BB +IlwskqmtN6+VmrQzNolHCDzvxNZs9lYL2VbGPGqVRyjZeHpoAlf9cQr8PgDb4d4b +vUx2KAhHQvV2nkmYvKyXcgnRuHggumF87mkxidriGAEFwH4qfOqetUg64WyxP7P2 +QLipm04SyQa7ONtIApfVXgHcE42Py4/f4arzCzMjKe3VyhGkS7nsT55X/fWgTaRm +CQPkO+H94P958WTvQDt77bQ+D3IvYaVvfil8n6HJMOJfFT0LJuSUbpSXJg== +-----END CERTIFICATE----- +-----BEGIN RSA PRIVATE KEY----- +MIIEpQIBAAKCAQEAtK2p2x0S3C1ajftAc3GaWPsji6scw1k9Sw/XltbLQuDc11/f +wwrUiFcje2CB3Ri6yD6+uCA3V12jEc4GdqzirJZhwgIhaTv42vfYBgiUcR9McEGr +agFC3yVR3lIbOzhBjmXNp1on46irxnzU4pT+w58IuvYqUBavaEtfRZocFR5NsIOy +mRhyNag8htOFK3wmTEYrb0vflFYT6SD47ogYtsd/xWSKS+YFyb7xSusR2Ot6Ktmr +MswQE57QYJz+KiRVlnL0cduMBdT52Wm8blaC9mz50PyrzjQ68NyHapCoWDU7pe4x +HLtzpXGSDMPuw4miiSwMym/2wReYJv6cFugLPQIDAQABAoIBAAZOyc9MhIwLSU4L +p4RgQvM4UVVe8/Id+3XTZ8NsXExJbWxXfIhiqGjaIfL8u4vsgRjcl+v1s/jo2/iT +KMab4o4D8gXD7UavQVDjtjb/ta79WL3SjRl2Uc9YjjMkyq6WmDNQeo2NKDdafCTB +1uzSJtLNipB8Z53ELPuHJhxX9QMHrMnuha49riQgXZ7buP9iQrHJFhImBjSzbxJx +L+TI6rkyLSf9Wi0Pd3L27Ob3QWNfNRYNSeTE+08eSRChkur5W0RuXAcuAICdQlCl +LBvWO/LmmvbzCqiDcgy/TliSb6CGGwgiNG7LJZmlkYNj8laGwalNlYZs3UrVv6NO +Br2loAECgYEA2kvCvPGj0Dg/6g7WhXDvAkEbcaL1tSeCxBbNH+6HS2UWMWvyTtCn +/bbD519QIdkvayy1QjEf32GV/UjUVmlULMLBcDy0DGjtL3+XpIhLKWDNxN1v1/ai +1oz23ZJCOgnk6K4qtFtlRS1XtynjA+rBetvYvLP9SKeFrnpzCgaA2r0CgYEA0+KX +1ACXDTNH5ySX3kMjSS9xdINf+OOw4CvPHFwbtc9aqk2HePlEsBTz5I/W3rKwXva3 +NqZ/bRqVVeZB/hHKFywgdUQk2Uc5z/S7Lw70/w1HubNTXGU06Ngb6zOFAo/o/TwZ +zTP1BMIKSOB6PAZPS3l+aLO4FRIRotfFhgRHOoECgYEAmiZbqt8cJaJDB/5YYDzC +mp3tSk6gIb936Q6M5VqkMYp9pIKsxhk0N8aDCnTU+kIK6SzWBpr3/d9Ecmqmfyq7 +5SvWO3KyVf0WWK9KH0abhOm2BKm2HBQvI0DB5u8sUx2/hsvOnjPYDISbZ11t0MtK +u35Zy89yMYcSsIYJjG/ROCUCgYEAgI2P9G5PNxEP5OtMwOsW84Y3Xat/hPAQFlI+ +HES+AzbFGWJkeT8zL2nm95tVkFP1sggZ7Kxjz3w7cpx7GX0NkbWSE9O+T51pNASV +tN1sQ3p5M+/a+cnlqgfEGJVvc7iAcXQPa3LEi5h2yPR49QYXAgG6cifn3dDSpmwn +SUI7PQECgYEApGCIIpSRPLAEHTGmP87RBL1smurhwmy2s/pghkvUkWehtxg0sGHh +kuaqDWcskogv+QC0sVdytiLSz8G0DwcEcsHK1Fkyb8A+ayiw6jWJDo2m9+IF4Fww +1Te6jFPYDESnbhq7+TLGgHGhtwcu5cnb4vSuYXGXKupZGzoLOBbv1Zw= +-----END RSA PRIVATE KEY----- diff --git a/apps/cms.c b/apps/cms.c new file mode 100644 index 000000000..7af2757a7 --- /dev/null +++ b/apps/cms.c @@ -0,0 +1,1306 @@ +/* + * Copyright 2008-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +/* CMS utility function */ + +#include +#include +#include "apps.h" + +#ifndef OPENSSL_NO_CMS + +# include +# include +# include +# include +# include +# include + +static int save_certs(char *signerfile, STACK_OF(X509) *signers); +static int cms_cb(int ok, X509_STORE_CTX *ctx); +static void receipt_request_print(CMS_ContentInfo *cms); +static CMS_ReceiptRequest *make_receipt_request(STACK_OF(OPENSSL_STRING) + *rr_to, int rr_allorfirst, STACK_OF(OPENSSL_STRING) + *rr_from); +static int cms_set_pkey_param(EVP_PKEY_CTX *pctx, + STACK_OF(OPENSSL_STRING) *param); + +# define SMIME_OP 0x10 +# define SMIME_IP 0x20 +# define SMIME_SIGNERS 0x40 +# define SMIME_ENCRYPT (1 | SMIME_OP) +# define SMIME_DECRYPT (2 | SMIME_IP) +# define SMIME_SIGN (3 | SMIME_OP | SMIME_SIGNERS) +# define SMIME_VERIFY (4 | SMIME_IP) +# define SMIME_CMSOUT (5 | SMIME_IP | SMIME_OP) +# define SMIME_RESIGN (6 | SMIME_IP | SMIME_OP | SMIME_SIGNERS) +# define SMIME_DATAOUT (7 | SMIME_IP) +# define SMIME_DATA_CREATE (8 | SMIME_OP) +# define SMIME_DIGEST_VERIFY (9 | SMIME_IP) +# define SMIME_DIGEST_CREATE (10 | SMIME_OP) +# define SMIME_UNCOMPRESS (11 | SMIME_IP) +# define SMIME_COMPRESS (12 | SMIME_OP) +# define SMIME_ENCRYPTED_DECRYPT (13 | SMIME_IP) +# define SMIME_ENCRYPTED_ENCRYPT (14 | SMIME_OP) +# define SMIME_SIGN_RECEIPT (15 | SMIME_IP | SMIME_OP) +# define SMIME_VERIFY_RECEIPT (16 | SMIME_IP) + +static int verify_err = 0; + +typedef struct cms_key_param_st cms_key_param; + +struct cms_key_param_st { + int idx; + STACK_OF(OPENSSL_STRING) *param; + cms_key_param *next; +}; + +typedef enum OPTION_choice { + OPT_ERR = -1, OPT_EOF = 0, OPT_HELP, + OPT_INFORM, OPT_OUTFORM, OPT_IN, OPT_OUT, OPT_ENCRYPT, + OPT_DECRYPT, OPT_SIGN, OPT_SIGN_RECEIPT, OPT_RESIGN, + OPT_VERIFY, OPT_VERIFY_RETCODE, OPT_VERIFY_RECEIPT, + OPT_CMSOUT, OPT_DATA_OUT, OPT_DATA_CREATE, OPT_DIGEST_VERIFY, + OPT_DIGEST_CREATE, OPT_COMPRESS, OPT_UNCOMPRESS, + OPT_ED_DECRYPT, OPT_ED_ENCRYPT, OPT_DEBUG_DECRYPT, OPT_TEXT, + OPT_ASCIICRLF, OPT_NOINTERN, OPT_NOVERIFY, OPT_NOCERTS, + OPT_NOATTR, OPT_NODETACH, OPT_NOSMIMECAP, OPT_BINARY, OPT_KEYID, + OPT_NOSIGS, OPT_NO_CONTENT_VERIFY, OPT_NO_ATTR_VERIFY, OPT_INDEF, + OPT_NOINDEF, OPT_CRLFEOL, OPT_NOOUT, OPT_RR_PRINT, + OPT_RR_ALL, OPT_RR_FIRST, OPT_RCTFORM, OPT_CERTFILE, OPT_CAFILE, + OPT_CAPATH, OPT_NOCAPATH, OPT_NOCAFILE,OPT_CONTENT, OPT_PRINT, + OPT_SECRETKEY, OPT_SECRETKEYID, OPT_PWRI_PASSWORD, OPT_ECONTENT_TYPE, + OPT_RAND, OPT_PASSIN, OPT_TO, OPT_FROM, OPT_SUBJECT, OPT_SIGNER, OPT_RECIP, + OPT_CERTSOUT, OPT_MD, OPT_INKEY, OPT_KEYFORM, OPT_KEYOPT, OPT_RR_FROM, + OPT_RR_TO, OPT_AES128_WRAP, OPT_AES192_WRAP, OPT_AES256_WRAP, + OPT_SMS4_WRAP, OPT_3DES_WRAP, OPT_ENGINE, + OPT_V_ENUM, + OPT_CIPHER +} OPTION_CHOICE; + +OPTIONS cms_options[] = { + {OPT_HELP_STR, 1, '-', "Usage: %s [options] cert.pem...\n"}, + {OPT_HELP_STR, 1, '-', + " cert.pem... recipient certs for encryption\n"}, + {OPT_HELP_STR, 1, '-', "Valid options are:\n"}, + {"help", OPT_HELP, '-', "Display this summary"}, + {"inform", OPT_INFORM, 'c', "Input format SMIME (default), PEM or DER"}, + {"outform", OPT_OUTFORM, 'c', + "Output format SMIME (default), PEM or DER"}, + {"in", OPT_IN, '<', "Input file"}, + {"out", OPT_OUT, '>', "Output file"}, + {"encrypt", OPT_ENCRYPT, '-', "Encrypt message"}, + {"decrypt", OPT_DECRYPT, '-', "Decrypt encrypted message"}, + {"sign", OPT_SIGN, '-', "Sign message"}, + {"sign_receipt", OPT_SIGN_RECEIPT, '-', "Generate a signed receipt for the message"}, + {"resign", OPT_RESIGN, '-', "Resign a signed message"}, + {"verify", OPT_VERIFY, '-', "Verify signed message"}, + {"verify_retcode", OPT_VERIFY_RETCODE, '-'}, + {"verify_receipt", OPT_VERIFY_RECEIPT, '<'}, + {"cmsout", OPT_CMSOUT, '-', "Output CMS structure"}, + {"data_out", OPT_DATA_OUT, '-'}, + {"data_create", OPT_DATA_CREATE, '-'}, + {"digest_verify", OPT_DIGEST_VERIFY, '-'}, + {"digest_create", OPT_DIGEST_CREATE, '-'}, + {"compress", OPT_COMPRESS, '-'}, + {"uncompress", OPT_UNCOMPRESS, '-'}, + {"EncryptedData_decrypt", OPT_ED_DECRYPT, '-'}, + {"EncryptedData_encrypt", OPT_ED_ENCRYPT, '-'}, + {"debug_decrypt", OPT_DEBUG_DECRYPT, '-'}, + {"text", OPT_TEXT, '-', "Include or delete text MIME headers"}, + {"asciicrlf", OPT_ASCIICRLF, '-'}, + {"nointern", OPT_NOINTERN, '-', + "Don't search certificates in message for signer"}, + {"noverify", OPT_NOVERIFY, '-', "Don't verify signers certificate"}, + {"nocerts", OPT_NOCERTS, '-', + "Don't include signers certificate when signing"}, + {"noattr", OPT_NOATTR, '-', "Don't include any signed attributes"}, + {"nodetach", OPT_NODETACH, '-', "Use opaque signing"}, + {"nosmimecap", OPT_NOSMIMECAP, '-', "Omit the SMIMECapabilities attribute"}, + {"binary", OPT_BINARY, '-', "Don't translate message to text"}, + {"keyid", OPT_KEYID, '-', "Use subject key identifier"}, + {"nosigs", OPT_NOSIGS, '-', "Don't verify message signature"}, + {"no_content_verify", OPT_NO_CONTENT_VERIFY, '-'}, + {"no_attr_verify", OPT_NO_ATTR_VERIFY, '-'}, + {"stream", OPT_INDEF, '-', "Enable CMS streaming"}, + {"indef", OPT_INDEF, '-', "Same as -stream"}, + {"noindef", OPT_NOINDEF, '-', "Disable CMS streaming"}, + {"crlfeol", OPT_CRLFEOL, '-', "Use CRLF as EOL termination instead of CR only" }, + {"noout", OPT_NOOUT, '-', "For the -cmsout operation do not output the parsed CMS structure"}, + {"receipt_request_print", OPT_RR_PRINT, '-', "Print CMS Receipt Request" }, + {"receipt_request_all", OPT_RR_ALL, '-'}, + {"receipt_request_first", OPT_RR_FIRST, '-'}, + {"rctform", OPT_RCTFORM, 'F', "Receipt file format"}, + {"certfile", OPT_CERTFILE, '<', "Other certificates file"}, + {"CAfile", OPT_CAFILE, '<', "Trusted certificates file"}, + {"CApath", OPT_CAPATH, '/', "trusted certificates directory"}, + {"no-CAfile", OPT_NOCAFILE, '-', + "Do not load the default certificates file"}, + {"no-CApath", OPT_NOCAPATH, '-', + "Do not load certificates from the default certificates directory"}, + {"content", OPT_CONTENT, '<', + "Supply or override content for detached signature"}, + {"print", OPT_PRINT, '-', + "For the -cmsout operation print out all fields of the CMS structure"}, + {"secretkey", OPT_SECRETKEY, 's'}, + {"secretkeyid", OPT_SECRETKEYID, 's'}, + {"pwri_password", OPT_PWRI_PASSWORD, 's'}, + {"econtent_type", OPT_ECONTENT_TYPE, 's'}, + {"rand", OPT_RAND, 's', + "Load the file(s) into the random number generator"}, + {"passin", OPT_PASSIN, 's', "Input file pass phrase source"}, + {"to", OPT_TO, 's', "To address"}, + {"from", OPT_FROM, 's', "From address"}, + {"subject", OPT_SUBJECT, 's', "Subject"}, + {"signer", OPT_SIGNER, 's', "Signer certificate file"}, + {"recip", OPT_RECIP, '<', "Recipient cert file for decryption"}, + {"certsout", OPT_CERTSOUT, '>', "Certificate output file"}, + {"md", OPT_MD, 's', "Digest algorithm to use when signing or resigning"}, + {"inkey", OPT_INKEY, 's', + "Input private key (if not signer or recipient)"}, + {"keyform", OPT_KEYFORM, 'f', "Input private key format (PEM or ENGINE)"}, + {"keyopt", OPT_KEYOPT, 's', "Set public key parameters as n:v pairs"}, + {"receipt_request_from", OPT_RR_FROM, 's'}, + {"receipt_request_to", OPT_RR_TO, 's'}, + {"", OPT_CIPHER, '-', "Any supported cipher"}, + OPT_V_OPTIONS, +# ifndef OPENSSL_NO_AES + {"aes128-wrap", OPT_AES128_WRAP, '-', "Use AES128 to wrap key"}, + {"aes192-wrap", OPT_AES192_WRAP, '-', "Use AES192 to wrap key"}, + {"aes256-wrap", OPT_AES256_WRAP, '-', "Use AES256 to wrap key"}, +# endif +# ifndef OPENSSL_NO_SMS4 + {"sms4-wrap", OPT_SMS4_WRAP, '-', "Use SMS4 to wrap key"}, +# endif +# ifndef OPENSSL_NO_DES + {"des3-wrap", OPT_3DES_WRAP, '-', "Use 3DES-EDE to wrap key"}, +# endif +# ifndef OPENSSL_NO_ENGINE + {"engine", OPT_ENGINE, 's', "Use engine e, possibly a hardware device"}, +# endif + {NULL} +}; + +int cms_main(int argc, char **argv) +{ + ASN1_OBJECT *econtent_type = NULL; + BIO *in = NULL, *out = NULL, *indata = NULL, *rctin = NULL; + CMS_ContentInfo *cms = NULL, *rcms = NULL; + CMS_ReceiptRequest *rr = NULL; + ENGINE *e = NULL; + EVP_PKEY *key = NULL; + const EVP_CIPHER *cipher = NULL, *wrap_cipher = NULL; + const EVP_MD *sign_md = NULL; + STACK_OF(OPENSSL_STRING) *rr_to = NULL, *rr_from = NULL; + STACK_OF(OPENSSL_STRING) *sksigners = NULL, *skkeys = NULL; + STACK_OF(X509) *encerts = NULL, *other = NULL; + X509 *cert = NULL, *recip = NULL, *signer = NULL; + X509_STORE *store = NULL; + X509_VERIFY_PARAM *vpm = NULL; + char *certfile = NULL, *keyfile = NULL, *contfile = NULL; + const char *CAfile = NULL, *CApath = NULL; + char *certsoutfile = NULL; + int noCAfile = 0, noCApath = 0; + char *infile = NULL, *outfile = NULL, *rctfile = NULL, *inrand = NULL; + char *passinarg = NULL, *passin = NULL, *signerfile = NULL, *recipfile = + NULL; + char *to = NULL, *from = NULL, *subject = NULL, *prog; + cms_key_param *key_first = NULL, *key_param = NULL; + int flags = CMS_DETACHED, noout = 0, print = 0, keyidx = -1, vpmtouched = + 0; + int informat = FORMAT_SMIME, outformat = FORMAT_SMIME; + int need_rand = 0, operation = 0, ret = 1, rr_print = 0, rr_allorfirst = + -1; + int verify_retcode = 0, rctformat = FORMAT_SMIME, keyform = FORMAT_PEM; + size_t secret_keylen = 0, secret_keyidlen = 0; + unsigned char *pwri_pass = NULL, *pwri_tmp = NULL; + unsigned char *secret_key = NULL, *secret_keyid = NULL; + long ltmp; + const char *mime_eol = "\n"; + OPTION_CHOICE o; + + if ((vpm = X509_VERIFY_PARAM_new()) == NULL) + return 1; + + prog = opt_init(argc, argv, cms_options); + while ((o = opt_next()) != OPT_EOF) { + switch (o) { + case OPT_EOF: + case OPT_ERR: + opthelp: + BIO_printf(bio_err, "%s: Use -help for summary.\n", prog); + goto end; + case OPT_HELP: + opt_help(cms_options); + ret = 0; + goto end; + case OPT_INFORM: + if (!opt_format(opt_arg(), OPT_FMT_PDS, &informat)) + goto opthelp; + break; + case OPT_OUTFORM: + if (!opt_format(opt_arg(), OPT_FMT_PDS, &outformat)) + goto opthelp; + break; + case OPT_OUT: + outfile = opt_arg(); + break; + case OPT_ENCRYPT: + operation = SMIME_ENCRYPT; + break; + case OPT_DECRYPT: + operation = SMIME_DECRYPT; + break; + case OPT_SIGN: + operation = SMIME_SIGN; + break; + case OPT_SIGN_RECEIPT: + operation = SMIME_SIGN_RECEIPT; + break; + case OPT_RESIGN: + operation = SMIME_RESIGN; + break; + case OPT_VERIFY: + operation = SMIME_VERIFY; + break; + case OPT_VERIFY_RETCODE: + verify_retcode = 1; + break; + case OPT_VERIFY_RECEIPT: + operation = SMIME_VERIFY_RECEIPT; + rctfile = opt_arg(); + break; + case OPT_CMSOUT: + operation = SMIME_CMSOUT; + break; + case OPT_DATA_OUT: + operation = SMIME_DATAOUT; + break; + case OPT_DATA_CREATE: + operation = SMIME_DATA_CREATE; + break; + case OPT_DIGEST_VERIFY: + operation = SMIME_DIGEST_VERIFY; + break; + case OPT_DIGEST_CREATE: + operation = SMIME_DIGEST_CREATE; + break; + case OPT_COMPRESS: + operation = SMIME_COMPRESS; + break; + case OPT_UNCOMPRESS: + operation = SMIME_UNCOMPRESS; + break; + case OPT_ED_DECRYPT: + operation = SMIME_ENCRYPTED_DECRYPT; + break; + case OPT_ED_ENCRYPT: + operation = SMIME_ENCRYPTED_ENCRYPT; + break; + case OPT_DEBUG_DECRYPT: + flags |= CMS_DEBUG_DECRYPT; + break; + case OPT_TEXT: + flags |= CMS_TEXT; + break; + case OPT_ASCIICRLF: + flags |= CMS_ASCIICRLF; + break; + case OPT_NOINTERN: + flags |= CMS_NOINTERN; + break; + case OPT_NOVERIFY: + flags |= CMS_NO_SIGNER_CERT_VERIFY; + break; + case OPT_NOCERTS: + flags |= CMS_NOCERTS; + break; + case OPT_NOATTR: + flags |= CMS_NOATTR; + break; + case OPT_NODETACH: + flags &= ~CMS_DETACHED; + break; + case OPT_NOSMIMECAP: + flags |= CMS_NOSMIMECAP; + break; + case OPT_BINARY: + flags |= CMS_BINARY; + break; + case OPT_KEYID: + flags |= CMS_USE_KEYID; + break; + case OPT_NOSIGS: + flags |= CMS_NOSIGS; + break; + case OPT_NO_CONTENT_VERIFY: + flags |= CMS_NO_CONTENT_VERIFY; + break; + case OPT_NO_ATTR_VERIFY: + flags |= CMS_NO_ATTR_VERIFY; + break; + case OPT_INDEF: + flags |= CMS_STREAM; + break; + case OPT_NOINDEF: + flags &= ~CMS_STREAM; + break; + case OPT_CRLFEOL: + mime_eol = "\r\n"; + flags |= CMS_CRLFEOL; + break; + case OPT_NOOUT: + noout = 1; + break; + case OPT_RR_PRINT: + rr_print = 1; + break; + case OPT_RR_ALL: + rr_allorfirst = 0; + break; + case OPT_RR_FIRST: + rr_allorfirst = 1; + break; + case OPT_RCTFORM: + if (rctformat == FORMAT_SMIME) + rcms = SMIME_read_CMS(rctin, NULL); + else if (rctformat == FORMAT_PEM) + rcms = PEM_read_bio_CMS(rctin, NULL, NULL, NULL); + else if (rctformat == FORMAT_ASN1) + if (!opt_format(opt_arg(), + OPT_FMT_PEMDER | OPT_FMT_SMIME, &rctformat)) + goto opthelp; + break; + case OPT_CERTFILE: + certfile = opt_arg(); + break; + case OPT_CAFILE: + CAfile = opt_arg(); + break; + case OPT_CAPATH: + CApath = opt_arg(); + break; + case OPT_NOCAFILE: + noCAfile = 1; + break; + case OPT_NOCAPATH: + noCApath = 1; + break; + case OPT_IN: + infile = opt_arg(); + break; + case OPT_CONTENT: + contfile = opt_arg(); + break; + case OPT_RR_FROM: + if (rr_from == NULL + && (rr_from = sk_OPENSSL_STRING_new_null()) == NULL) + goto end; + sk_OPENSSL_STRING_push(rr_from, opt_arg()); + break; + case OPT_RR_TO: + if (rr_to == NULL + && (rr_to = sk_OPENSSL_STRING_new_null()) == NULL) + goto end; + sk_OPENSSL_STRING_push(rr_to, opt_arg()); + break; + case OPT_PRINT: + noout = print = 1; + break; + case OPT_SECRETKEY: + if (secret_key != NULL) { + BIO_printf(bio_err, "Invalid key (supplied twice) %s\n", + opt_arg()); + goto opthelp; + } + secret_key = OPENSSL_hexstr2buf(opt_arg(), <mp); + if (secret_key == NULL) { + BIO_printf(bio_err, "Invalid key %s\n", opt_arg()); + goto end; + } + secret_keylen = (size_t)ltmp; + break; + case OPT_SECRETKEYID: + if (secret_keyid != NULL) { + BIO_printf(bio_err, "Invalid id (supplied twice) %s\n", + opt_arg()); + goto opthelp; + } + secret_keyid = OPENSSL_hexstr2buf(opt_arg(), <mp); + if (secret_keyid == NULL) { + BIO_printf(bio_err, "Invalid id %s\n", opt_arg()); + goto opthelp; + } + secret_keyidlen = (size_t)ltmp; + break; + case OPT_PWRI_PASSWORD: + pwri_pass = (unsigned char *)opt_arg(); + break; + case OPT_ECONTENT_TYPE: + if (econtent_type != NULL) { + BIO_printf(bio_err, "Invalid OID (supplied twice) %s\n", + opt_arg()); + goto opthelp; + } + econtent_type = OBJ_txt2obj(opt_arg(), 0); + if (econtent_type == NULL) { + BIO_printf(bio_err, "Invalid OID %s\n", opt_arg()); + goto opthelp; + } + break; + case OPT_RAND: + inrand = opt_arg(); + need_rand = 1; + break; + case OPT_ENGINE: + e = setup_engine(opt_arg(), 0); + break; + case OPT_PASSIN: + passinarg = opt_arg(); + break; + case OPT_TO: + to = opt_arg(); + break; + case OPT_FROM: + from = opt_arg(); + break; + case OPT_SUBJECT: + subject = opt_arg(); + break; + case OPT_CERTSOUT: + certsoutfile = opt_arg(); + break; + case OPT_MD: + if (!opt_md(opt_arg(), &sign_md)) + goto end; + break; + case OPT_SIGNER: + /* If previous -signer argument add signer to list */ + if (signerfile) { + if (sksigners == NULL + && (sksigners = sk_OPENSSL_STRING_new_null()) == NULL) + goto end; + sk_OPENSSL_STRING_push(sksigners, signerfile); + if (keyfile == NULL) + keyfile = signerfile; + if (skkeys == NULL + && (skkeys = sk_OPENSSL_STRING_new_null()) == NULL) + goto end; + sk_OPENSSL_STRING_push(skkeys, keyfile); + keyfile = NULL; + } + signerfile = opt_arg(); + break; + case OPT_INKEY: + /* If previous -inkey argument add signer to list */ + if (keyfile) { + if (signerfile == NULL) { + BIO_puts(bio_err, "Illegal -inkey without -signer\n"); + goto end; + } + if (sksigners == NULL + && (sksigners = sk_OPENSSL_STRING_new_null()) == NULL) + goto end; + sk_OPENSSL_STRING_push(sksigners, signerfile); + signerfile = NULL; + if (skkeys == NULL + && (skkeys = sk_OPENSSL_STRING_new_null()) == NULL) + goto end; + sk_OPENSSL_STRING_push(skkeys, keyfile); + } + keyfile = opt_arg(); + break; + case OPT_KEYFORM: + if (!opt_format(opt_arg(), OPT_FMT_ANY, &keyform)) + goto opthelp; + break; + case OPT_RECIP: + if (operation == SMIME_ENCRYPT) { + if (encerts == NULL && (encerts = sk_X509_new_null()) == NULL) + goto end; + cert = load_cert(opt_arg(), FORMAT_PEM, + "recipient certificate file"); + if (cert == NULL) + goto end; + sk_X509_push(encerts, cert); + cert = NULL; + } else + recipfile = opt_arg(); + break; + case OPT_CIPHER: + if (!opt_cipher(opt_unknown(), &cipher)) + goto end; + break; + case OPT_KEYOPT: + keyidx = -1; + if (operation == SMIME_ENCRYPT) { + if (encerts) + keyidx += sk_X509_num(encerts); + } else { + if (keyfile || signerfile) + keyidx++; + if (skkeys) + keyidx += sk_OPENSSL_STRING_num(skkeys); + } + if (keyidx < 0) { + BIO_printf(bio_err, "No key specified\n"); + goto opthelp; + } + if (key_param == NULL || key_param->idx != keyidx) { + cms_key_param *nparam; + nparam = app_malloc(sizeof(*nparam), "key param buffer"); + nparam->idx = keyidx; + if ((nparam->param = sk_OPENSSL_STRING_new_null()) == NULL) + goto end; + nparam->next = NULL; + if (key_first == NULL) + key_first = nparam; + else + key_param->next = nparam; + key_param = nparam; + } + sk_OPENSSL_STRING_push(key_param->param, opt_arg()); + break; + case OPT_V_CASES: + if (!opt_verify(o, vpm)) + goto end; + vpmtouched++; + break; +# if !defined(OPENSSL_NO_DES) && !defined(OPENSSL_NO_SHA) + case OPT_3DES_WRAP: + wrap_cipher = EVP_des_ede3_wrap(); + break; +# endif +# ifndef OPENSSL_NO_AES + case OPT_AES128_WRAP: + wrap_cipher = EVP_aes_128_wrap(); + break; + case OPT_AES192_WRAP: + wrap_cipher = EVP_aes_192_wrap(); + break; + case OPT_AES256_WRAP: + wrap_cipher = EVP_aes_256_wrap(); + break; +# endif +# ifndef OPENSSL_NO_SMS4 + case OPT_SMS4_WRAP: + wrap_cipher = EVP_sms4_wrap(); + break; +# endif + } + } + argc = opt_num_rest(); + argv = opt_rest(); + + if (((rr_allorfirst != -1) || rr_from) && !rr_to) { + BIO_puts(bio_err, "No Signed Receipts Recipients\n"); + goto opthelp; + } + + if (!(operation & SMIME_SIGNERS) && (rr_to || rr_from)) { + BIO_puts(bio_err, "Signed receipts only allowed with -sign\n"); + goto opthelp; + } + if (!(operation & SMIME_SIGNERS) && (skkeys || sksigners)) { + BIO_puts(bio_err, "Multiple signers or keys not allowed\n"); + goto opthelp; + } + + if (operation & SMIME_SIGNERS) { + if (keyfile && !signerfile) { + BIO_puts(bio_err, "Illegal -inkey without -signer\n"); + goto opthelp; + } + /* Check to see if any final signer needs to be appended */ + if (signerfile) { + if (!sksigners + && (sksigners = sk_OPENSSL_STRING_new_null()) == NULL) + goto end; + sk_OPENSSL_STRING_push(sksigners, signerfile); + if (!skkeys && (skkeys = sk_OPENSSL_STRING_new_null()) == NULL) + goto end; + if (!keyfile) + keyfile = signerfile; + sk_OPENSSL_STRING_push(skkeys, keyfile); + } + if (!sksigners) { + BIO_printf(bio_err, "No signer certificate specified\n"); + goto opthelp; + } + signerfile = NULL; + keyfile = NULL; + need_rand = 1; + } + + else if (operation == SMIME_DECRYPT) { + if (!recipfile && !keyfile && !secret_key && !pwri_pass) { + BIO_printf(bio_err, + "No recipient certificate or key specified\n"); + goto opthelp; + } + } else if (operation == SMIME_ENCRYPT) { + if (*argv == NULL && !secret_key && !pwri_pass && !encerts) { + BIO_printf(bio_err, "No recipient(s) certificate(s) specified\n"); + goto opthelp; + } + need_rand = 1; + } else if (!operation) + goto opthelp; + + if (!app_passwd(passinarg, NULL, &passin, NULL)) { + BIO_printf(bio_err, "Error getting password\n"); + goto end; + } + + if (need_rand) { + app_RAND_load_file(NULL, (inrand != NULL)); + if (inrand != NULL) + BIO_printf(bio_err, "%ld semi-random bytes loaded\n", + app_RAND_load_files(inrand)); + } + + ret = 2; + + if (!(operation & SMIME_SIGNERS)) + flags &= ~CMS_DETACHED; + + if (!(operation & SMIME_OP)) { + if (flags & CMS_BINARY) + outformat = FORMAT_BINARY; + } + + if (!(operation & SMIME_IP)) { + if (flags & CMS_BINARY) + informat = FORMAT_BINARY; + } + + if (operation == SMIME_ENCRYPT) { + if (!cipher) { +# ifndef OPENSSL_NO_DES + cipher = EVP_des_ede3_cbc(); +# else + BIO_printf(bio_err, "No cipher selected\n"); + goto end; +# endif + } + + if (secret_key && !secret_keyid) { + BIO_printf(bio_err, "No secret key id\n"); + goto end; + } + + if (*argv && !encerts) + if ((encerts = sk_X509_new_null()) == NULL) + goto end; + while (*argv) { + if ((cert = load_cert(*argv, FORMAT_PEM, + "recipient certificate file")) == NULL) + goto end; + sk_X509_push(encerts, cert); + cert = NULL; + argv++; + } + } + + if (certfile) { + if (!load_certs(certfile, &other, FORMAT_PEM, NULL, + "certificate file")) { + ERR_print_errors(bio_err); + goto end; + } + } + + if (recipfile && (operation == SMIME_DECRYPT)) { + if ((recip = load_cert(recipfile, FORMAT_PEM, + "recipient certificate file")) == NULL) { + ERR_print_errors(bio_err); + goto end; + } + } + + if (operation == SMIME_SIGN_RECEIPT) { + if ((signer = load_cert(signerfile, FORMAT_PEM, + "receipt signer certificate file")) == NULL) { + ERR_print_errors(bio_err); + goto end; + } + } + + if (operation == SMIME_DECRYPT) { + if (!keyfile) + keyfile = recipfile; + } else if ((operation == SMIME_SIGN) || (operation == SMIME_SIGN_RECEIPT)) { + if (!keyfile) + keyfile = signerfile; + } else + keyfile = NULL; + + if (keyfile) { + key = load_key(keyfile, keyform, 0, passin, e, "signing key file"); + if (!key) + goto end; + } + + in = bio_open_default(infile, 'r', informat); + if (in == NULL) + goto end; + + if (operation & SMIME_IP) { + if (informat == FORMAT_SMIME) + cms = SMIME_read_CMS(in, &indata); + else if (informat == FORMAT_PEM) + cms = PEM_read_bio_CMS(in, NULL, NULL, NULL); + else if (informat == FORMAT_ASN1) + cms = d2i_CMS_bio(in, NULL); + else { + BIO_printf(bio_err, "Bad input format for CMS file\n"); + goto end; + } + + if (!cms) { + BIO_printf(bio_err, "Error reading S/MIME message\n"); + goto end; + } + if (contfile) { + BIO_free(indata); + if ((indata = BIO_new_file(contfile, "rb")) == NULL) { + BIO_printf(bio_err, "Can't read content file %s\n", contfile); + goto end; + } + } + if (certsoutfile) { + STACK_OF(X509) *allcerts; + allcerts = CMS_get1_certs(cms); + if (!save_certs(certsoutfile, allcerts)) { + BIO_printf(bio_err, + "Error writing certs to %s\n", certsoutfile); + ret = 5; + goto end; + } + sk_X509_pop_free(allcerts, X509_free); + } + } + + if (rctfile) { + char *rctmode = (rctformat == FORMAT_ASN1) ? "rb" : "r"; + if ((rctin = BIO_new_file(rctfile, rctmode)) == NULL) { + BIO_printf(bio_err, "Can't open receipt file %s\n", rctfile); + goto end; + } + + if (rctformat == FORMAT_SMIME) + rcms = SMIME_read_CMS(rctin, NULL); + else if (rctformat == FORMAT_PEM) + rcms = PEM_read_bio_CMS(rctin, NULL, NULL, NULL); + else if (rctformat == FORMAT_ASN1) + rcms = d2i_CMS_bio(rctin, NULL); + else { + BIO_printf(bio_err, "Bad input format for receipt\n"); + goto end; + } + + if (!rcms) { + BIO_printf(bio_err, "Error reading receipt\n"); + goto end; + } + } + + out = bio_open_default(outfile, 'w', outformat); + if (out == NULL) + goto end; + + if ((operation == SMIME_VERIFY) || (operation == SMIME_VERIFY_RECEIPT)) { + if ((store = setup_verify(CAfile, CApath, noCAfile, noCApath)) == NULL) + goto end; + X509_STORE_set_verify_cb(store, cms_cb); + if (vpmtouched) + X509_STORE_set1_param(store, vpm); + } + + ret = 3; + + if (operation == SMIME_DATA_CREATE) { + cms = CMS_data_create(in, flags); + } else if (operation == SMIME_DIGEST_CREATE) { + cms = CMS_digest_create(in, sign_md, flags); + } else if (operation == SMIME_COMPRESS) { + cms = CMS_compress(in, -1, flags); + } else if (operation == SMIME_ENCRYPT) { + int i; + flags |= CMS_PARTIAL; + cms = CMS_encrypt(NULL, in, cipher, flags); + if (!cms) + goto end; + for (i = 0; i < sk_X509_num(encerts); i++) { + CMS_RecipientInfo *ri; + cms_key_param *kparam; + int tflags = flags; + X509 *x = sk_X509_value(encerts, i); + for (kparam = key_first; kparam; kparam = kparam->next) { + if (kparam->idx == i) { + tflags |= CMS_KEY_PARAM; + break; + } + } + ri = CMS_add1_recipient_cert(cms, x, tflags); + if (!ri) + goto end; + if (kparam) { + EVP_PKEY_CTX *pctx; + pctx = CMS_RecipientInfo_get0_pkey_ctx(ri); + if (!cms_set_pkey_param(pctx, kparam->param)) + goto end; + } + if (CMS_RecipientInfo_type(ri) == CMS_RECIPINFO_AGREE + && wrap_cipher) { + EVP_CIPHER_CTX *wctx; + wctx = CMS_RecipientInfo_kari_get0_ctx(ri); + EVP_EncryptInit_ex(wctx, wrap_cipher, NULL, NULL, NULL); + } + } + + if (secret_key) { + if (!CMS_add0_recipient_key(cms, NID_undef, + secret_key, secret_keylen, + secret_keyid, secret_keyidlen, + NULL, NULL, NULL)) + goto end; + /* NULL these because call absorbs them */ + secret_key = NULL; + secret_keyid = NULL; + } + if (pwri_pass) { + pwri_tmp = (unsigned char *)OPENSSL_strdup((char *)pwri_pass); + if (!pwri_tmp) + goto end; + if (!CMS_add0_recipient_password(cms, + -1, NID_undef, NID_undef, + pwri_tmp, -1, NULL)) + goto end; + pwri_tmp = NULL; + } + if (!(flags & CMS_STREAM)) { + if (!CMS_final(cms, in, NULL, flags)) + goto end; + } + } else if (operation == SMIME_ENCRYPTED_ENCRYPT) { + cms = CMS_EncryptedData_encrypt(in, cipher, + secret_key, secret_keylen, flags); + + } else if (operation == SMIME_SIGN_RECEIPT) { + CMS_ContentInfo *srcms = NULL; + STACK_OF(CMS_SignerInfo) *sis; + CMS_SignerInfo *si; + sis = CMS_get0_SignerInfos(cms); + if (!sis) + goto end; + si = sk_CMS_SignerInfo_value(sis, 0); + srcms = CMS_sign_receipt(si, signer, key, other, flags); + if (!srcms) + goto end; + CMS_ContentInfo_free(cms); + cms = srcms; + } else if (operation & SMIME_SIGNERS) { + int i; + /* + * If detached data content we enable streaming if S/MIME output + * format. + */ + if (operation == SMIME_SIGN) { + + if (flags & CMS_DETACHED) { + if (outformat == FORMAT_SMIME) + flags |= CMS_STREAM; + } + flags |= CMS_PARTIAL; + cms = CMS_sign(NULL, NULL, other, in, flags); + if (!cms) + goto end; + if (econtent_type) + CMS_set1_eContentType(cms, econtent_type); + + if (rr_to) { + rr = make_receipt_request(rr_to, rr_allorfirst, rr_from); + if (!rr) { + BIO_puts(bio_err, + "Signed Receipt Request Creation Error\n"); + goto end; + } + } + } else + flags |= CMS_REUSE_DIGEST; + for (i = 0; i < sk_OPENSSL_STRING_num(sksigners); i++) { + CMS_SignerInfo *si; + cms_key_param *kparam; + int tflags = flags; + signerfile = sk_OPENSSL_STRING_value(sksigners, i); + keyfile = sk_OPENSSL_STRING_value(skkeys, i); + + signer = load_cert(signerfile, FORMAT_PEM, "signer certificate"); + if (!signer) + goto end; + key = load_key(keyfile, keyform, 0, passin, e, "signing key file"); + if (!key) + goto end; + for (kparam = key_first; kparam; kparam = kparam->next) { + if (kparam->idx == i) { + tflags |= CMS_KEY_PARAM; + break; + } + } + si = CMS_add1_signer(cms, signer, key, sign_md, tflags); + if (!si) + goto end; + if (kparam) { + EVP_PKEY_CTX *pctx; + pctx = CMS_SignerInfo_get0_pkey_ctx(si); + if (!cms_set_pkey_param(pctx, kparam->param)) + goto end; + } + if (rr && !CMS_add1_ReceiptRequest(si, rr)) + goto end; + X509_free(signer); + signer = NULL; + EVP_PKEY_free(key); + key = NULL; + } + /* If not streaming or resigning finalize structure */ + if ((operation == SMIME_SIGN) && !(flags & CMS_STREAM)) { + if (!CMS_final(cms, in, NULL, flags)) + goto end; + } + } + + if (!cms) { + BIO_printf(bio_err, "Error creating CMS structure\n"); + goto end; + } + + ret = 4; + if (operation == SMIME_DECRYPT) { + if (flags & CMS_DEBUG_DECRYPT) + CMS_decrypt(cms, NULL, NULL, NULL, NULL, flags); + + if (secret_key) { + if (!CMS_decrypt_set1_key(cms, + secret_key, secret_keylen, + secret_keyid, secret_keyidlen)) { + BIO_puts(bio_err, "Error decrypting CMS using secret key\n"); + goto end; + } + } + + if (key) { + if (!CMS_decrypt_set1_pkey(cms, key, recip)) { + BIO_puts(bio_err, "Error decrypting CMS using private key\n"); + goto end; + } + } + + if (pwri_pass) { + if (!CMS_decrypt_set1_password(cms, pwri_pass, -1)) { + BIO_puts(bio_err, "Error decrypting CMS using password\n"); + goto end; + } + } + + if (!CMS_decrypt(cms, NULL, NULL, indata, out, flags)) { + BIO_printf(bio_err, "Error decrypting CMS structure\n"); + goto end; + } + } else if (operation == SMIME_DATAOUT) { + if (!CMS_data(cms, out, flags)) + goto end; + } else if (operation == SMIME_UNCOMPRESS) { + if (!CMS_uncompress(cms, indata, out, flags)) + goto end; + } else if (operation == SMIME_DIGEST_VERIFY) { + if (CMS_digest_verify(cms, indata, out, flags) > 0) + BIO_printf(bio_err, "Verification successful\n"); + else { + BIO_printf(bio_err, "Verification failure\n"); + goto end; + } + } else if (operation == SMIME_ENCRYPTED_DECRYPT) { + if (!CMS_EncryptedData_decrypt(cms, secret_key, secret_keylen, + indata, out, flags)) + goto end; + } else if (operation == SMIME_VERIFY) { + if (CMS_verify(cms, other, store, indata, out, flags) > 0) + BIO_printf(bio_err, "Verification successful\n"); + else { + BIO_printf(bio_err, "Verification failure\n"); + if (verify_retcode) + ret = verify_err + 32; + goto end; + } + if (signerfile) { + STACK_OF(X509) *signers; + signers = CMS_get0_signers(cms); + if (!save_certs(signerfile, signers)) { + BIO_printf(bio_err, + "Error writing signers to %s\n", signerfile); + ret = 5; + goto end; + } + sk_X509_free(signers); + } + if (rr_print) + receipt_request_print(cms); + + } else if (operation == SMIME_VERIFY_RECEIPT) { + if (CMS_verify_receipt(rcms, cms, other, store, flags) > 0) + BIO_printf(bio_err, "Verification successful\n"); + else { + BIO_printf(bio_err, "Verification failure\n"); + goto end; + } + } else { + if (noout) { + if (print) + CMS_ContentInfo_print_ctx(out, cms, 0, NULL); + } else if (outformat == FORMAT_SMIME) { + if (to) + BIO_printf(out, "To: %s%s", to, mime_eol); + if (from) + BIO_printf(out, "From: %s%s", from, mime_eol); + if (subject) + BIO_printf(out, "Subject: %s%s", subject, mime_eol); + if (operation == SMIME_RESIGN) + ret = SMIME_write_CMS(out, cms, indata, flags); + else + ret = SMIME_write_CMS(out, cms, in, flags); + } else if (outformat == FORMAT_PEM) + ret = PEM_write_bio_CMS_stream(out, cms, in, flags); + else if (outformat == FORMAT_ASN1) + ret = i2d_CMS_bio_stream(out, cms, in, flags); + else { + BIO_printf(bio_err, "Bad output format for CMS file\n"); + goto end; + } + if (ret <= 0) { + ret = 6; + goto end; + } + } + ret = 0; + end: + if (ret) + ERR_print_errors(bio_err); + if (need_rand) + app_RAND_write_file(NULL); + sk_X509_pop_free(encerts, X509_free); + sk_X509_pop_free(other, X509_free); + X509_VERIFY_PARAM_free(vpm); + sk_OPENSSL_STRING_free(sksigners); + sk_OPENSSL_STRING_free(skkeys); + OPENSSL_free(secret_key); + OPENSSL_free(secret_keyid); + OPENSSL_free(pwri_tmp); + ASN1_OBJECT_free(econtent_type); + CMS_ReceiptRequest_free(rr); + sk_OPENSSL_STRING_free(rr_to); + sk_OPENSSL_STRING_free(rr_from); + for (key_param = key_first; key_param;) { + cms_key_param *tparam; + sk_OPENSSL_STRING_free(key_param->param); + tparam = key_param->next; + OPENSSL_free(key_param); + key_param = tparam; + } + X509_STORE_free(store); + X509_free(cert); + X509_free(recip); + X509_free(signer); + EVP_PKEY_free(key); + CMS_ContentInfo_free(cms); + CMS_ContentInfo_free(rcms); + release_engine(e); + BIO_free(rctin); + BIO_free(in); + BIO_free(indata); + BIO_free_all(out); + OPENSSL_free(passin); + return (ret); +} + +static int save_certs(char *signerfile, STACK_OF(X509) *signers) +{ + int i; + BIO *tmp; + if (!signerfile) + return 1; + tmp = BIO_new_file(signerfile, "w"); + if (!tmp) + return 0; + for (i = 0; i < sk_X509_num(signers); i++) + PEM_write_bio_X509(tmp, sk_X509_value(signers, i)); + BIO_free(tmp); + return 1; +} + +/* Minimal callback just to output policy info (if any) */ + +static int cms_cb(int ok, X509_STORE_CTX *ctx) +{ + int error; + + error = X509_STORE_CTX_get_error(ctx); + + verify_err = error; + + if ((error != X509_V_ERR_NO_EXPLICIT_POLICY) + && ((error != X509_V_OK) || (ok != 2))) + return ok; + + policies_print(ctx); + + return ok; + +} + +static void gnames_stack_print(STACK_OF(GENERAL_NAMES) *gns) +{ + STACK_OF(GENERAL_NAME) *gens; + GENERAL_NAME *gen; + int i, j; + + for (i = 0; i < sk_GENERAL_NAMES_num(gns); i++) { + gens = sk_GENERAL_NAMES_value(gns, i); + for (j = 0; j < sk_GENERAL_NAME_num(gens); j++) { + gen = sk_GENERAL_NAME_value(gens, j); + BIO_puts(bio_err, " "); + GENERAL_NAME_print(bio_err, gen); + BIO_puts(bio_err, "\n"); + } + } + return; +} + +static void receipt_request_print(CMS_ContentInfo *cms) +{ + STACK_OF(CMS_SignerInfo) *sis; + CMS_SignerInfo *si; + CMS_ReceiptRequest *rr; + int allorfirst; + STACK_OF(GENERAL_NAMES) *rto, *rlist; + ASN1_STRING *scid; + int i, rv; + sis = CMS_get0_SignerInfos(cms); + for (i = 0; i < sk_CMS_SignerInfo_num(sis); i++) { + si = sk_CMS_SignerInfo_value(sis, i); + rv = CMS_get1_ReceiptRequest(si, &rr); + BIO_printf(bio_err, "Signer %d:\n", i + 1); + if (rv == 0) + BIO_puts(bio_err, " No Receipt Request\n"); + else if (rv < 0) { + BIO_puts(bio_err, " Receipt Request Parse Error\n"); + ERR_print_errors(bio_err); + } else { + const char *id; + int idlen; + CMS_ReceiptRequest_get0_values(rr, &scid, &allorfirst, + &rlist, &rto); + BIO_puts(bio_err, " Signed Content ID:\n"); + idlen = ASN1_STRING_length(scid); + id = (const char *)ASN1_STRING_get0_data(scid); + BIO_dump_indent(bio_err, id, idlen, 4); + BIO_puts(bio_err, " Receipts From"); + if (rlist) { + BIO_puts(bio_err, " List:\n"); + gnames_stack_print(rlist); + } else if (allorfirst == 1) + BIO_puts(bio_err, ": First Tier\n"); + else if (allorfirst == 0) + BIO_puts(bio_err, ": All\n"); + else + BIO_printf(bio_err, " Unknown (%d)\n", allorfirst); + BIO_puts(bio_err, " Receipts To:\n"); + gnames_stack_print(rto); + } + CMS_ReceiptRequest_free(rr); + } +} + +static STACK_OF(GENERAL_NAMES) *make_names_stack(STACK_OF(OPENSSL_STRING) *ns) +{ + int i; + STACK_OF(GENERAL_NAMES) *ret; + GENERAL_NAMES *gens = NULL; + GENERAL_NAME *gen = NULL; + ret = sk_GENERAL_NAMES_new_null(); + if (!ret) + goto err; + for (i = 0; i < sk_OPENSSL_STRING_num(ns); i++) { + char *str = sk_OPENSSL_STRING_value(ns, i); + gen = a2i_GENERAL_NAME(NULL, NULL, NULL, GEN_EMAIL, str, 0); + if (!gen) + goto err; + gens = GENERAL_NAMES_new(); + if (gens == NULL) + goto err; + if (!sk_GENERAL_NAME_push(gens, gen)) + goto err; + gen = NULL; + if (!sk_GENERAL_NAMES_push(ret, gens)) + goto err; + gens = NULL; + } + + return ret; + + err: + sk_GENERAL_NAMES_pop_free(ret, GENERAL_NAMES_free); + GENERAL_NAMES_free(gens); + GENERAL_NAME_free(gen); + return NULL; +} + +static CMS_ReceiptRequest *make_receipt_request(STACK_OF(OPENSSL_STRING) + *rr_to, int rr_allorfirst, STACK_OF(OPENSSL_STRING) + *rr_from) +{ + STACK_OF(GENERAL_NAMES) *rct_to = NULL, *rct_from = NULL; + CMS_ReceiptRequest *rr; + rct_to = make_names_stack(rr_to); + if (!rct_to) + goto err; + if (rr_from) { + rct_from = make_names_stack(rr_from); + if (!rct_from) + goto err; + } else + rct_from = NULL; + rr = CMS_ReceiptRequest_create0(NULL, -1, rr_allorfirst, rct_from, + rct_to); + return rr; + err: + sk_GENERAL_NAMES_pop_free(rct_to, GENERAL_NAMES_free); + return NULL; +} + +static int cms_set_pkey_param(EVP_PKEY_CTX *pctx, + STACK_OF(OPENSSL_STRING) *param) +{ + char *keyopt; + int i; + if (sk_OPENSSL_STRING_num(param) <= 0) + return 1; + for (i = 0; i < sk_OPENSSL_STRING_num(param); i++) { + keyopt = sk_OPENSSL_STRING_value(param, i); + if (pkey_ctrl_string(pctx, keyopt) <= 0) { + BIO_printf(bio_err, "parameter error \"%s\"\n", keyopt); + ERR_print_errors(bio_err); + return 0; + } + } + return 1; +} + +#endif diff --git a/apps/crl.c b/apps/crl.c new file mode 100644 index 000000000..bd294d7b1 --- /dev/null +++ b/apps/crl.c @@ -0,0 +1,347 @@ +/* + * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#include +#include +#include "apps.h" +#include +#include +#include +#include +#include + +typedef enum OPTION_choice { + OPT_ERR = -1, OPT_EOF = 0, OPT_HELP, + OPT_INFORM, OPT_IN, OPT_OUTFORM, OPT_OUT, OPT_KEYFORM, OPT_KEY, + OPT_ISSUER, OPT_LASTUPDATE, OPT_NEXTUPDATE, OPT_FINGERPRINT, + OPT_CRLNUMBER, OPT_BADSIG, OPT_GENDELTA, OPT_CAPATH, OPT_CAFILE, + OPT_NOCAPATH, OPT_NOCAFILE, OPT_VERIFY, OPT_TEXT, OPT_HASH, OPT_HASH_OLD, + OPT_NOOUT, OPT_NAMEOPT, OPT_MD +} OPTION_CHOICE; + +OPTIONS crl_options[] = { + {"help", OPT_HELP, '-', "Display this summary"}, + {"inform", OPT_INFORM, 'F', "Input format; default PEM"}, + {"in", OPT_IN, '<', "Input file - default stdin"}, + {"outform", OPT_OUTFORM, 'F', "Output format - default PEM"}, + {"out", OPT_OUT, '>', "output file - default stdout"}, + {"keyform", OPT_KEYFORM, 'F', "Private key file format (PEM or ENGINE)"}, + {"key", OPT_KEY, '<', "CRL signing Private key to use"}, + {"issuer", OPT_ISSUER, '-', "Print issuer DN"}, + {"lastupdate", OPT_LASTUPDATE, '-', "Set lastUpdate field"}, + {"nextupdate", OPT_NEXTUPDATE, '-', "Set nextUpdate field"}, + {"noout", OPT_NOOUT, '-', "No CRL output"}, + {"fingerprint", OPT_FINGERPRINT, '-', "Print the crl fingerprint"}, + {"crlnumber", OPT_CRLNUMBER, '-', "Print CRL number"}, + {"badsig", OPT_BADSIG, '-', "Corrupt last byte of loaded CRL signature (for test)" }, + {"gendelta", OPT_GENDELTA, '<', "Other CRL to compare/diff to the Input one"}, + {"CApath", OPT_CAPATH, '/', "Verify CRL using certificates in dir"}, + {"CAfile", OPT_CAFILE, '<', "Verify CRL using certificates in file name"}, + {"no-CAfile", OPT_NOCAFILE, '-', + "Do not load the default certificates file"}, + {"no-CApath", OPT_NOCAPATH, '-', + "Do not load certificates from the default certificates directory"}, + {"verify", OPT_VERIFY, '-', "Verify CRL signature"}, + {"text", OPT_TEXT, '-', "Print out a text format version"}, + {"hash", OPT_HASH, '-', "Print hash value"}, + {"nameopt", OPT_NAMEOPT, 's', "Various certificate name options"}, + {"", OPT_MD, '-', "Any supported digest"}, +#ifndef OPENSSL_NO_MD5 + {"hash_old", OPT_HASH_OLD, '-', "Print old-style (MD5) hash value"}, +#endif + {NULL} +}; + +int crl_main(int argc, char **argv) +{ + X509_CRL *x = NULL; + BIO *out = NULL; + X509_STORE *store = NULL; + X509_STORE_CTX *ctx = NULL; + X509_LOOKUP *lookup = NULL; + X509_OBJECT *xobj = NULL; + EVP_PKEY *pkey; + const EVP_MD *digest = EVP_get_digestbynid(NID_sha1); + unsigned long nmflag = 0; + char nmflag_set = 0; + char *infile = NULL, *outfile = NULL, *crldiff = NULL, *keyfile = NULL; + const char *CAfile = NULL, *CApath = NULL, *prog; + OPTION_CHOICE o; + int hash = 0, issuer = 0, lastupdate = 0, nextupdate = 0, noout = 0; + int informat = FORMAT_PEM, outformat = FORMAT_PEM, keyformat = FORMAT_PEM; + int ret = 1, num = 0, badsig = 0, fingerprint = 0, crlnumber = 0; + int text = 0, do_ver = 0, noCAfile = 0, noCApath = 0; + int i; +#ifndef OPENSSL_NO_MD5 + int hash_old = 0; +#endif + + prog = opt_init(argc, argv, crl_options); + while ((o = opt_next()) != OPT_EOF) { + switch (o) { + case OPT_EOF: + case OPT_ERR: + opthelp: + BIO_printf(bio_err, "%s: Use -help for summary.\n", prog); + goto end; + case OPT_HELP: + opt_help(crl_options); + ret = 0; + goto end; + case OPT_INFORM: + if (!opt_format(opt_arg(), OPT_FMT_PEMDER, &informat)) + goto opthelp; + break; + case OPT_IN: + infile = opt_arg(); + break; + case OPT_OUTFORM: + if (!opt_format(opt_arg(), OPT_FMT_PEMDER, &outformat)) + goto opthelp; + break; + case OPT_OUT: + outfile = opt_arg(); + break; + case OPT_KEYFORM: + if (!opt_format(opt_arg(), OPT_FMT_PEMDER, &keyformat)) + goto opthelp; + break; + case OPT_KEY: + keyfile = opt_arg(); + break; + case OPT_GENDELTA: + crldiff = opt_arg(); + break; + case OPT_CAPATH: + CApath = opt_arg(); + do_ver = 1; + break; + case OPT_CAFILE: + CAfile = opt_arg(); + do_ver = 1; + break; + case OPT_NOCAPATH: + noCApath = 1; + break; + case OPT_NOCAFILE: + noCAfile = 1; + break; + case OPT_HASH_OLD: +#ifndef OPENSSL_NO_MD5 + hash_old = ++num; +#endif + break; + case OPT_VERIFY: + do_ver = 1; + break; + case OPT_TEXT: + text = 1; + break; + case OPT_HASH: + hash = ++num; + break; + case OPT_ISSUER: + issuer = ++num; + break; + case OPT_LASTUPDATE: + lastupdate = ++num; + break; + case OPT_NEXTUPDATE: + nextupdate = ++num; + break; + case OPT_NOOUT: + noout = ++num; + break; + case OPT_FINGERPRINT: + fingerprint = ++num; + break; + case OPT_CRLNUMBER: + crlnumber = ++num; + break; + case OPT_BADSIG: + badsig = 1; + break; + case OPT_NAMEOPT: + nmflag_set = 1; + if (!set_name_ex(&nmflag, opt_arg())) + goto opthelp; + break; + case OPT_MD: + if (!opt_md(opt_unknown(), &digest)) + goto opthelp; + } + } + argc = opt_num_rest(); + if (argc != 0) + goto opthelp; + + if (!nmflag_set) + nmflag = XN_FLAG_ONELINE; + + x = load_crl(infile, informat); + if (x == NULL) + goto end; + + if (do_ver) { + if ((store = setup_verify(CAfile, CApath, noCAfile, noCApath)) == NULL) + goto end; + lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file()); + if (lookup == NULL) + goto end; + ctx = X509_STORE_CTX_new(); + if (ctx == NULL || !X509_STORE_CTX_init(ctx, store, NULL, NULL)) { + BIO_printf(bio_err, "Error initialising X509 store\n"); + goto end; + } + + xobj = X509_STORE_CTX_get_obj_by_subject(ctx, X509_LU_X509, + X509_CRL_get_issuer(x)); + if (xobj == NULL) { + BIO_printf(bio_err, "Error getting CRL issuer certificate\n"); + goto end; + } + pkey = X509_get_pubkey(X509_OBJECT_get0_X509(xobj)); + X509_OBJECT_free(xobj); + if (!pkey) { + BIO_printf(bio_err, "Error getting CRL issuer public key\n"); + goto end; + } + i = X509_CRL_verify(x, pkey); + EVP_PKEY_free(pkey); + if (i < 0) + goto end; + if (i == 0) + BIO_printf(bio_err, "verify failure\n"); + else + BIO_printf(bio_err, "verify OK\n"); + } + + if (crldiff) { + X509_CRL *newcrl, *delta; + if (!keyfile) { + BIO_puts(bio_err, "Missing CRL signing key\n"); + goto end; + } + newcrl = load_crl(crldiff, informat); + if (!newcrl) + goto end; + pkey = load_key(keyfile, keyformat, 0, NULL, NULL, "CRL signing key"); + if (!pkey) { + X509_CRL_free(newcrl); + goto end; + } + delta = X509_CRL_diff(x, newcrl, pkey, digest, 0); + X509_CRL_free(newcrl); + EVP_PKEY_free(pkey); + if (delta) { + X509_CRL_free(x); + x = delta; + } else { + BIO_puts(bio_err, "Error creating delta CRL\n"); + goto end; + } + } + + if (badsig) { + const ASN1_BIT_STRING *sig; + + X509_CRL_get0_signature(x, &sig, NULL); + corrupt_signature(sig); + } + + if (num) { + for (i = 1; i <= num; i++) { + if (issuer == i) { + print_name(bio_out, "issuer=", X509_CRL_get_issuer(x), + nmflag); + } + if (crlnumber == i) { + ASN1_INTEGER *crlnum; + crlnum = X509_CRL_get_ext_d2i(x, NID_crl_number, NULL, NULL); + BIO_printf(bio_out, "crlNumber="); + if (crlnum) { + i2a_ASN1_INTEGER(bio_out, crlnum); + ASN1_INTEGER_free(crlnum); + } else + BIO_puts(bio_out, ""); + BIO_printf(bio_out, "\n"); + } + if (hash == i) { + BIO_printf(bio_out, "%08lx\n", + X509_NAME_hash(X509_CRL_get_issuer(x))); + } +#ifndef OPENSSL_NO_MD5 + if (hash_old == i) { + BIO_printf(bio_out, "%08lx\n", + X509_NAME_hash_old(X509_CRL_get_issuer(x))); + } +#endif + if (lastupdate == i) { + BIO_printf(bio_out, "lastUpdate="); + ASN1_TIME_print(bio_out, X509_CRL_get0_lastUpdate(x)); + BIO_printf(bio_out, "\n"); + } + if (nextupdate == i) { + BIO_printf(bio_out, "nextUpdate="); + if (X509_CRL_get0_nextUpdate(x)) + ASN1_TIME_print(bio_out, X509_CRL_get0_nextUpdate(x)); + else + BIO_printf(bio_out, "NONE"); + BIO_printf(bio_out, "\n"); + } + if (fingerprint == i) { + int j; + unsigned int n; + unsigned char md[EVP_MAX_MD_SIZE]; + + if (!X509_CRL_digest(x, digest, md, &n)) { + BIO_printf(bio_err, "out of memory\n"); + goto end; + } + BIO_printf(bio_out, "%s Fingerprint=", + OBJ_nid2sn(EVP_MD_type(digest))); + for (j = 0; j < (int)n; j++) { + BIO_printf(bio_out, "%02X%c", md[j], (j + 1 == (int)n) + ? '\n' : ':'); + } + } + } + } + out = bio_open_default(outfile, 'w', outformat); + if (out == NULL) + goto end; + + if (text) + X509_CRL_print(out, x); + + if (noout) { + ret = 0; + goto end; + } + + if (outformat == FORMAT_ASN1) + i = (int)i2d_X509_CRL_bio(out, x); + else + i = PEM_write_bio_X509_CRL(out, x); + if (!i) { + BIO_printf(bio_err, "unable to write CRL\n"); + goto end; + } + ret = 0; + + end: + if (ret != 0) + ERR_print_errors(bio_err); + BIO_free_all(out); + X509_CRL_free(x); + X509_STORE_CTX_free(ctx); + X509_STORE_free(store); + return (ret); +} diff --git a/apps/crl2p7.c b/apps/crl2p7.c new file mode 100644 index 000000000..9c5f79f9f --- /dev/null +++ b/apps/crl2p7.c @@ -0,0 +1,216 @@ +/* + * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#include +#include +#include "apps.h" +#include +#include +#include +#include +#include +#include + +static int add_certs_from_file(STACK_OF(X509) *stack, char *certfile); + +typedef enum OPTION_choice { + OPT_ERR = -1, OPT_EOF = 0, OPT_HELP, + OPT_INFORM, OPT_OUTFORM, OPT_IN, OPT_OUT, OPT_NOCRL, OPT_CERTFILE +} OPTION_CHOICE; + +OPTIONS crl2pkcs7_options[] = { + {"help", OPT_HELP, '-', "Display this summary"}, + {"inform", OPT_INFORM, 'F', "Input format - DER or PEM"}, + {"outform", OPT_OUTFORM, 'F', "Output format - DER or PEM"}, + {"in", OPT_IN, '<', "Input file"}, + {"out", OPT_OUT, '>', "Output file"}, + {"nocrl", OPT_NOCRL, '-', "No crl to load, just certs from '-certfile'"}, + {"certfile", OPT_CERTFILE, '<', + "File of chain of certs to a trusted CA; can be repeated"}, + {NULL} +}; + +int crl2pkcs7_main(int argc, char **argv) +{ + BIO *in = NULL, *out = NULL; + PKCS7 *p7 = NULL; + PKCS7_SIGNED *p7s = NULL; + STACK_OF(OPENSSL_STRING) *certflst = NULL; + STACK_OF(X509) *cert_stack = NULL; + STACK_OF(X509_CRL) *crl_stack = NULL; + X509_CRL *crl = NULL; + char *infile = NULL, *outfile = NULL, *prog, *certfile; + int i = 0, informat = FORMAT_PEM, outformat = FORMAT_PEM, ret = 1, nocrl = + 0; + OPTION_CHOICE o; + + prog = opt_init(argc, argv, crl2pkcs7_options); + while ((o = opt_next()) != OPT_EOF) { + switch (o) { + case OPT_EOF: + case OPT_ERR: + opthelp: + BIO_printf(bio_err, "%s: Use -help for summary.\n", prog); + goto end; + case OPT_HELP: + opt_help(crl2pkcs7_options); + ret = 0; + goto end; + case OPT_INFORM: + if (!opt_format(opt_arg(), OPT_FMT_PEMDER, &informat)) + goto opthelp; + break; + case OPT_OUTFORM: + if (!opt_format(opt_arg(), OPT_FMT_PEMDER, &outformat)) + goto opthelp; + break; + case OPT_IN: + infile = opt_arg(); + break; + case OPT_OUT: + outfile = opt_arg(); + break; + case OPT_NOCRL: + nocrl = 1; + break; + case OPT_CERTFILE: + if ((certflst == NULL) + && (certflst = sk_OPENSSL_STRING_new_null()) == NULL) + goto end; + if (!sk_OPENSSL_STRING_push(certflst, opt_arg())) + goto end; + break; + } + } + argc = opt_num_rest(); + if (argc != 0) + goto opthelp; + + if (!nocrl) { + in = bio_open_default(infile, 'r', informat); + if (in == NULL) + goto end; + + if (informat == FORMAT_ASN1) + crl = d2i_X509_CRL_bio(in, NULL); + else if (informat == FORMAT_PEM) + crl = PEM_read_bio_X509_CRL(in, NULL, NULL, NULL); + if (crl == NULL) { + BIO_printf(bio_err, "unable to load CRL\n"); + ERR_print_errors(bio_err); + goto end; + } + } + + if ((p7 = PKCS7_new()) == NULL) + goto end; + if ((p7s = PKCS7_SIGNED_new()) == NULL) + goto end; + p7->type = OBJ_nid2obj(NID_pkcs7_signed); + p7->d.sign = p7s; + p7s->contents->type = OBJ_nid2obj(NID_pkcs7_data); + + if (!ASN1_INTEGER_set(p7s->version, 1)) + goto end; + if ((crl_stack = sk_X509_CRL_new_null()) == NULL) + goto end; + p7s->crl = crl_stack; + if (crl != NULL) { + sk_X509_CRL_push(crl_stack, crl); + crl = NULL; /* now part of p7 for OPENSSL_freeing */ + } + + if ((cert_stack = sk_X509_new_null()) == NULL) + goto end; + p7s->cert = cert_stack; + + if (certflst) + for (i = 0; i < sk_OPENSSL_STRING_num(certflst); i++) { + certfile = sk_OPENSSL_STRING_value(certflst, i); + if (add_certs_from_file(cert_stack, certfile) < 0) { + BIO_printf(bio_err, "error loading certificates\n"); + ERR_print_errors(bio_err); + goto end; + } + } + + out = bio_open_default(outfile, 'w', outformat); + if (out == NULL) + goto end; + + if (outformat == FORMAT_ASN1) + i = i2d_PKCS7_bio(out, p7); + else if (outformat == FORMAT_PEM) + i = PEM_write_bio_PKCS7(out, p7); + if (!i) { + BIO_printf(bio_err, "unable to write pkcs7 object\n"); + ERR_print_errors(bio_err); + goto end; + } + ret = 0; + end: + sk_OPENSSL_STRING_free(certflst); + BIO_free(in); + BIO_free_all(out); + PKCS7_free(p7); + X509_CRL_free(crl); + + return (ret); +} + +/*- + *---------------------------------------------------------------------- + * int add_certs_from_file + * + * Read a list of certificates to be checked from a file. + * + * Results: + * number of certs added if successful, -1 if not. + *---------------------------------------------------------------------- + */ +static int add_certs_from_file(STACK_OF(X509) *stack, char *certfile) +{ + BIO *in = NULL; + int count = 0; + int ret = -1; + STACK_OF(X509_INFO) *sk = NULL; + X509_INFO *xi; + + in = BIO_new_file(certfile, "r"); + if (in == NULL) { + BIO_printf(bio_err, "error opening the file, %s\n", certfile); + goto end; + } + + /* This loads from a file, a stack of x509/crl/pkey sets */ + sk = PEM_X509_INFO_read_bio(in, NULL, NULL, NULL); + if (sk == NULL) { + BIO_printf(bio_err, "error reading the file, %s\n", certfile); + goto end; + } + + /* scan over it and pull out the CRL's */ + while (sk_X509_INFO_num(sk)) { + xi = sk_X509_INFO_shift(sk); + if (xi->x509 != NULL) { + sk_X509_push(stack, xi->x509); + xi->x509 = NULL; + count++; + } + X509_INFO_free(xi); + } + + ret = count; + end: + /* never need to OPENSSL_free x */ + BIO_free(in); + sk_X509_INFO_free(sk); + return (ret); +} diff --git a/apps/ct_log_list.cnf b/apps/ct_log_list.cnf new file mode 100644 index 000000000..243487453 --- /dev/null +++ b/apps/ct_log_list.cnf @@ -0,0 +1,34 @@ +enabled_logs=pilot,aviator,rocketeer,digicert,certly,izempe,symantec,venafi + +[pilot] +description = Google Pilot Log +key = MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEfahLEimAoz2t01p3uMziiLOl/fHTDM0YDOhBRuiBARsV4UvxG2LdNgoIGLrtCzWE0J5APC2em4JlvR8EEEFMoA== + +[aviator] +description = Google Aviator log +key = MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE1/TMabLkDpCjiupacAlP7xNi0I1JYP8bQFAHDG1xhtolSY1l4QgNRzRrvSe8liE+NPWHdjGxfx3JhTsN9x8/6Q== + +[rocketeer] +description = Google Rocketeer log +key = MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIFsYyDzBi7MxCAC/oJBXK7dHjG+1aLCOkHjpoHPqTyghLpzA9BYbqvnV16mAw04vUjyYASVGJCUoI3ctBcJAeg== + +[digicert] +description = DigiCert Log Server +key = MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEAkbFvhu7gkAW6MHSrBlpE1n4+HCFRkC5OLAjgqhkTH+/uzSfSl8ois8ZxAD2NgaTZe1M9akhYlrYkes4JECs6A== + +[certly] +description = Certly.IO log +key = MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAECyPLhWKYYUgEc+tUXfPQB4wtGS2MNvXrjwFCCnyYJifBtd2Sk7Cu+Js9DNhMTh35FftHaHu6ZrclnNBKwmbbSA== + +[izempe] +description = Izempe log +key = MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEJ2Q5DC3cUBj4IQCiDu0s6j51up+TZAkAEcQRF6tczw90rLWXkJMAW7jr9yc92bIKgV8vDXU4lDeZHvYHduDuvg== + +[symantec] +description = Symantec log +key = MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEluqsHEYMG1XcDfy1lCdGV0JwOmkY4r87xNuroPS2bMBTP01CEDPwWJePa75y9CrsHEKqAy8afig1dpkIPSEUhg== + +[venafi] +description = Venafi log +key = MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAolpIHxdSlTXLo1s6H1OCdpSj/4DyHDc8wLG9wVmLqy1lk9fz4ATVmm+/1iN2Nk8jmctUKK2MFUtlWXZBSpym97M7frGlSaQXUWyA3CqQUEuIJOmlEjKTBEiQAvpfDjCHjlV2Be4qTM6jamkJbiWtgnYPhJL6ONaGTiSPm7Byy57iaz/hbckldSOIoRhYBiMzeNoA0DiRZ9KmfSeXZ1rB8y8X5urSW+iBzf2SaOfzBvDpcoTuAaWx2DPazoOl28fP1hZ+kHUYvxbcMjttjauCFx+JII0dmuZNIwjfeG/GBb9frpSX219k1O4Wi6OEbHEr8at/XQ0y7gTikOxBn/s5wQIDAQAB + diff --git a/apps/demoCA/cacert.pem b/apps/demoCA/cacert.pem new file mode 100644 index 000000000..affbce3bc --- /dev/null +++ b/apps/demoCA/cacert.pem @@ -0,0 +1,14 @@ +subject=/C=AU/SOP=QLD/O=Mincom Pty. Ltd./OU=CS/CN=SSLeay demo server +issuer= /C=AU/SOP=QLD/O=Mincom Pty. Ltd./OU=CS/CN=CA +-----BEGIN X509 CERTIFICATE----- + +MIIBgjCCASwCAQQwDQYJKoZIhvcNAQEEBQAwODELMAkGA1UEBhMCQVUxDDAKBgNV +BAgTA1FMRDEbMBkGA1UEAxMSU1NMZWF5L3JzYSB0ZXN0IENBMB4XDTk1MTAwOTIz +MzIwNVoXDTk4MDcwNTIzMzIwNVowYDELMAkGA1UEBhMCQVUxDDAKBgNVBAgTA1FM +RDEZMBcGA1UEChMQTWluY29tIFB0eS4gTHRkLjELMAkGA1UECxMCQ1MxGzAZBgNV +BAMTElNTTGVheSBkZW1vIHNlcnZlcjBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQC3 +LCXcScWua0PFLkHBLm2VejqpA1F4RQ8q0VjRiPafjx/Z/aWH3ipdMVvuJGa/wFXb +/nDFLDlfWp+oCPwhBtVPAgMBAAEwDQYJKoZIhvcNAQEEBQADQQArNFsihWIjBzb0 +DCsU0BvL2bvSwJrPEqFlkDq3F4M6EGutL9axEcANWgbbEdAvNJD1dmEmoWny27Pn +IMs6ZOZB +-----END X509 CERTIFICATE----- diff --git a/apps/demoCA/index.txt b/apps/demoCA/index.txt new file mode 100644 index 000000000..2cdd252d6 --- /dev/null +++ b/apps/demoCA/index.txt @@ -0,0 +1,39 @@ +R 980705233205Z 951009233205Z 01 certs/00000001 /CN=Eric Young +E 951009233205Z 02 certs/00000002 /CN=Duncan Young +R 980705233205Z 951201010000Z 03 certs/00000003 /CN=Tim Hudson +V 980705233205Z 04 certs/00000004 /CN=Eric Young4 +V 980705233205Z 05 certs/00000004 /CN=Eric Young5 +V 980705233205Z 06 certs/00000004 /CN=Eric Young6 +V 980705233205Z 07 certs/00000004 /CN=Eric Young7 +V 980705233205Z 08 certs/00000004 /CN=Eric Young8 +V 980705233205Z 09 certs/00000004 /CN=Eric Young9 +V 980705233205Z 0A certs/00000004 /CN=Eric YoungA +V 980705233205Z 0B certs/00000004 /CN=Eric YoungB +V 980705233205Z 0C certs/00000004 /CN=Eric YoungC +V 980705233205Z 0D certs/00000004 /CN=Eric YoungD +V 980705233205Z 0E certs/00000004 /CN=Eric YoungE +V 980705233205Z 0F certs/00000004 /CN=Eric YoungF +V 980705233205Z 10 certs/00000004 /CN=Eric Young10 +V 980705233205Z 11 certs/00000004 /CN=Eric Young11 +V 980705233205Z 12 certs/00000004 /CN=Eric Young12 +V 980705233205Z 13 certs/00000004 /CN=Eric Young13 +V 980705233205Z 14 certs/00000004 /CN=Eric Young14 +V 980705233205Z 15 certs/00000004 /CN=Eric Young15 +V 980705233205Z 16 certs/00000004 /CN=Eric Young16 +V 980705233205Z 17 certs/00000004 /CN=Eric Young17 +V 961206150305Z 010C unknown /C=AU/SP=QLD/O=Mincom Pty. Ltd./OU=MTR/CN=Eric Young/Email=eay@mincom.oz.au +V 961206153245Z 010D unknown /C=AU/SP=Queensland/O=Mincom Pty Ltd/OU=MTR/CN=Eric Young/Email=eay@mincom.oz.au +V 970322074816Z 010E unknown /CN=Eric Young/Email=eay@mincom.oz.au +V 970322075152Z 010F unknown /CN=Eric Young +V 970322075906Z 0110 unknown /CN=Eric Youngg +V 970324092238Z 0111 unknown /C=AU/SP=Queensland/CN=Eric Young +V 970324221931Z 0112 unknown /CN=Fred +V 970324224934Z 0113 unknown /C=AU/CN=eay +V 971001005237Z 0114 unknown /C=AU/SP=QLD/O=Mincom Pty Ltd/OU=MTR/CN=x509v3 test +V 971001010331Z 0115 unknown /C=AU/SP=Queensland/O=Mincom Pty Ltd/OU=MTR/CN=test again - x509v3 +V 971001013945Z 0117 unknown /C=AU/SP=Queensland/O=Mincom Pty Ltd/OU=MTR/CN=x509v3 test +V 971014225415Z 0118 unknown /C=AU/SP=Queensland/CN=test +V 971015004448Z 0119 unknown /C=AU/SP=Queensland/O=Mincom Pty Ltd/OU=MTR/CN=test2 +V 971016035001Z 011A unknown /C=AU/SP=Queensland/O=Mincom Pty Ltd/OU=MTR/CN=test64 +V 971016080129Z 011B unknown /C=FR/O=ALCATEL/OU=Alcatel Mobile Phones/CN=bourque/Email=bourque@art.alcatel.fr +V 971016224000Z 011D unknown /L=Bedford/O=Cranfield University/OU=Computer Centre/CN=Peter R Lister/Email=P.Lister@cranfield.ac.uk diff --git a/apps/demoCA/private/cakey.pem b/apps/demoCA/private/cakey.pem new file mode 100644 index 000000000..48fb18c7d --- /dev/null +++ b/apps/demoCA/private/cakey.pem @@ -0,0 +1,24 @@ +issuer= /C=AU/SOP=QLD/O=Mincom Pty. Ltd./OU=CS/CN=CA +subject=/C=AU/SOP=QLD/O=Mincom Pty. Ltd./OU=CS/CN=SSLeay demo server +-----BEGIN X509 CERTIFICATE----- + +MIIBgjCCASwCAQQwDQYJKoZIhvcNAQEEBQAwODELMAkGA1UEBhMCQVUxDDAKBgNV +BAgTA1FMRDEbMBkGA1UEAxMSU1NMZWF5L3JzYSB0ZXN0IENBMB4XDTk1MTAwOTIz +MzIwNVoXDTk4MDcwNTIzMzIwNVowYDELMAkGA1UEBhMCQVUxDDAKBgNVBAgTA1FM +RDEZMBcGA1UEChMQTWluY29tIFB0eS4gTHRkLjELMAkGA1UECxMCQ1MxGzAZBgNV +BAMTElNTTGVheSBkZW1vIHNlcnZlcjBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQC3 +LCXcScWua0PFLkHBLm2VejqpA1F4RQ8q0VjRiPafjx/Z/aWH3ipdMVvuJGa/wFXb +/nDFLDlfWp+oCPwhBtVPAgMBAAEwDQYJKoZIhvcNAQEEBQADQQArNFsihWIjBzb0 +DCsU0BvL2bvSwJrPEqFlkDq3F4M6EGutL9axEcANWgbbEdAvNJD1dmEmoWny27Pn +IMs6ZOZB +-----END X509 CERTIFICATE----- +-----BEGIN RSA PRIVATE KEY----- + +MIIBPAIBAAJBALcsJdxJxa5rQ8UuQcEubZV6OqkDUXhFDyrRWNGI9p+PH9n9pYfe +Kl0xW+4kZr/AVdv+cMUsOV9an6gI/CEG1U8CAwEAAQJAXJMBZ34ZXHd1vtgL/3hZ +hexKbVTx/djZO4imXO/dxPGRzG2ylYZpHmG32/T1kaHpZlCHoEPgHoSzmxYXfxjG +sQIhAPmZ/bQOjmRUHM/VM2X5zrjjM6z18R1P6l3ObFwt9FGdAiEAu943Yh9SqMRw +tL0xHGxKmM/YJueUw1gB6sLkETN71NsCIQCeT3RhoqXfrpXDoEcEU+gwzjI1bpxq +agiNTOLfqGoA5QIhAIQFYjgzONxex7FLrsKBm16N2SFl5pXsN9SpRqqL2n63AiEA +g9VNIQ3xwpw7og3IbONifeku+J9qGMGQJMKwSTwrFtI= +-----END RSA PRIVATE KEY----- diff --git a/apps/demoCA/serial b/apps/demoCA/serial new file mode 100644 index 000000000..69fa0ffe2 --- /dev/null +++ b/apps/demoCA/serial @@ -0,0 +1 @@ +011E diff --git a/apps/demoSRP/srp_verifier.txt b/apps/demoSRP/srp_verifier.txt new file mode 100644 index 000000000..ccae62924 --- /dev/null +++ b/apps/demoSRP/srp_verifier.txt @@ -0,0 +1,6 @@ +# This is a file that will be filled by the openssl srp routine. +# You can initialize the file with additional groups, these are +# records starting with a I followed by the g and N values and the id. +# The exact values ... you have to dig this out from the source of srp.c +# or srp_vfy.c +# The last value of an I is used as the default group for new users. diff --git a/apps/demoSRP/srp_verifier.txt.attr b/apps/demoSRP/srp_verifier.txt.attr new file mode 100644 index 000000000..8f7e63a34 --- /dev/null +++ b/apps/demoSRP/srp_verifier.txt.attr @@ -0,0 +1 @@ +unique_subject = yes diff --git a/apps/dgst.c b/apps/dgst.c new file mode 100644 index 000000000..98807cb98 --- /dev/null +++ b/apps/dgst.c @@ -0,0 +1,494 @@ +/* + * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#include +#include +#include "apps.h" +#include +#include +#include +#include +#include +#include +#include + +#undef BUFSIZE +#define BUFSIZE 1024*8 + +int do_fp(BIO *out, unsigned char *buf, BIO *bp, int sep, int binout, + EVP_PKEY *key, unsigned char *sigin, int siglen, + const char *sig_name, const char *md_name, + const char *file); + +typedef enum OPTION_choice { + OPT_ERR = -1, OPT_EOF = 0, OPT_HELP, + OPT_C, OPT_R, OPT_RAND, OPT_OUT, OPT_SIGN, OPT_PASSIN, OPT_VERIFY, + OPT_PRVERIFY, OPT_SIGNATURE, OPT_KEYFORM, OPT_ENGINE, OPT_ENGINE_IMPL, + OPT_HEX, OPT_BINARY, OPT_DEBUG, OPT_FIPS_FINGERPRINT, + OPT_HMAC, OPT_MAC, OPT_SIGOPT, OPT_MACOPT, + OPT_DIGEST, OPT_CONFIG +} OPTION_CHOICE; + +OPTIONS dgst_options[] = { + {OPT_HELP_STR, 1, '-', "Usage: %s [options] [file...]\n"}, + {OPT_HELP_STR, 1, '-', + " file... files to digest (default is stdin)\n"}, + {"help", OPT_HELP, '-', "Display this summary"}, + {"c", OPT_C, '-', "Print the digest with separating colons"}, + {"r", OPT_R, '-', "Print the digest in coreutils format"}, + {"rand", OPT_RAND, 's', + "Use file(s) containing random data to seed RNG or an EGD sock"}, + {"out", OPT_OUT, '>', "Output to filename rather than stdout"}, + {"passin", OPT_PASSIN, 's', "Input file pass phrase source"}, + {"sign", OPT_SIGN, 's', "Sign digest using private key"}, + {"verify", OPT_VERIFY, 's', + "Verify a signature using public key"}, + {"prverify", OPT_PRVERIFY, 's', + "Verify a signature using private key"}, + {"signature", OPT_SIGNATURE, '<', "File with signature to verify"}, + {"keyform", OPT_KEYFORM, 'f', "Key file format (PEM or ENGINE)"}, + {"hex", OPT_HEX, '-', "Print as hex dump"}, + {"binary", OPT_BINARY, '-', "Print in binary form"}, + {"d", OPT_DEBUG, '-', "Print debug info"}, + {"debug", OPT_DEBUG, '-', "Print debug info"}, + {"fips-fingerprint", OPT_FIPS_FINGERPRINT, '-', + "Compute HMAC with the key used in OpenSSL-FIPS fingerprint"}, + {"hmac", OPT_HMAC, 's', "Create hashed MAC with key"}, + {"mac", OPT_MAC, 's', "Create MAC (not necessarily HMAC)"}, + {"sigopt", OPT_SIGOPT, 's', "Signature parameter in n:v form"}, + {"macopt", OPT_MACOPT, 's', "MAC algorithm parameters in n:v form or key"}, + {"", OPT_DIGEST, '-', "Any supported digest"}, +#ifndef OPENSSL_NO_ENGINE + {"engine", OPT_ENGINE, 's', "Use engine e, possibly a hardware device"}, + {"engine_impl", OPT_ENGINE_IMPL, '-', + "Also use engine given by -engine for digest operations"}, + {"config", OPT_CONFIG, 's', "A config file"}, +#endif + {NULL} +}; + +int dgst_main(int argc, char **argv) +{ + BIO *in = NULL, *inp, *bmd = NULL, *out = NULL; + ENGINE *e = NULL, *impl = NULL; + EVP_PKEY *sigkey = NULL; + STACK_OF(OPENSSL_STRING) *sigopts = NULL, *macopts = NULL; + char *hmac_key = NULL; + char *mac_name = NULL; + char *passinarg = NULL, *passin = NULL; + const EVP_MD *md = NULL, *m; + const char *outfile = NULL, *keyfile = NULL, *prog = NULL; + const char *sigfile = NULL, *randfile = NULL; + OPTION_CHOICE o; + int separator = 0, debug = 0, keyform = FORMAT_PEM, siglen = 0; + int i, ret = 1, out_bin = -1, want_pub = 0, do_verify = 0; + unsigned char *buf = NULL, *sigbuf = NULL; + int engine_impl = 0; + CONF *conf = NULL; + char *configfile = default_config_file; + + prog = opt_progname(argv[0]); + buf = app_malloc(BUFSIZE, "I/O buffer"); + md = EVP_get_digestbyname(prog); + + prog = opt_init(argc, argv, dgst_options); + while ((o = opt_next()) != OPT_EOF) { + switch (o) { + case OPT_EOF: + case OPT_ERR: + opthelp: + BIO_printf(bio_err, "%s: Use -help for summary.\n", prog); + goto end; + case OPT_HELP: + opt_help(dgst_options); + ret = 0; + goto end; + case OPT_C: + separator = 1; + break; + case OPT_R: + separator = 2; + break; + case OPT_RAND: + randfile = opt_arg(); + break; + case OPT_OUT: + outfile = opt_arg(); + break; + case OPT_SIGN: + keyfile = opt_arg(); + break; + case OPT_PASSIN: + passinarg = opt_arg(); + break; + case OPT_VERIFY: + keyfile = opt_arg(); + want_pub = do_verify = 1; + break; + case OPT_PRVERIFY: + keyfile = opt_arg(); + do_verify = 1; + break; + case OPT_SIGNATURE: + sigfile = opt_arg(); + break; + case OPT_KEYFORM: + if (!opt_format(opt_arg(), OPT_FMT_ANY, &keyform)) + goto opthelp; + break; + case OPT_ENGINE: + e = setup_engine(opt_arg(), 0); + break; + case OPT_ENGINE_IMPL: + engine_impl = 1; + break; + case OPT_HEX: + out_bin = 0; + break; + case OPT_BINARY: + out_bin = 1; + break; + case OPT_DEBUG: + debug = 1; + break; + case OPT_FIPS_FINGERPRINT: + hmac_key = "etaonrishdlcupfm"; + break; + case OPT_HMAC: + hmac_key = opt_arg(); + break; + case OPT_MAC: + mac_name = opt_arg(); + break; + case OPT_SIGOPT: + if (!sigopts) + sigopts = sk_OPENSSL_STRING_new_null(); + if (!sigopts || !sk_OPENSSL_STRING_push(sigopts, opt_arg())) + goto opthelp; + break; + case OPT_MACOPT: + if (!macopts) + macopts = sk_OPENSSL_STRING_new_null(); + if (!macopts || !sk_OPENSSL_STRING_push(macopts, opt_arg())) + goto opthelp; + break; + case OPT_DIGEST: + if (!opt_md(opt_unknown(), &m)) + goto opthelp; + md = m; + break; + case OPT_CONFIG: + configfile = opt_arg(); + break; + } + } + argc = opt_num_rest(); + argv = opt_rest(); + if (keyfile != NULL && argc > 1) { + BIO_printf(bio_err, "%s: Can only sign or verify one file.\n", prog); + goto end; + } + + if (do_verify && !sigfile) { + BIO_printf(bio_err, + "No signature to verify: use the -signature option\n"); + goto end; + } + if (engine_impl) + impl = e; + + in = BIO_new(BIO_s_file()); + bmd = BIO_new(BIO_f_md()); + if ((in == NULL) || (bmd == NULL)) { + ERR_print_errors(bio_err); + goto end; + } + + if (debug) { + BIO_set_callback(in, BIO_debug_callback); + /* needed for windows 3.1 */ + BIO_set_callback_arg(in, (char *)bio_err); + } + + if (!app_passwd(passinarg, NULL, &passin, NULL)) { + BIO_printf(bio_err, "Error getting password\n"); + goto end; + } + + if (out_bin == -1) { + if (keyfile) + out_bin = 1; + else + out_bin = 0; + } + + if (e) + BIO_printf(bio_err, "Using configuration from %s\n", configfile); + if ((conf = app_load_config(configfile)) == NULL) + goto end; + if (configfile != default_config_file && !app_load_modules(conf)) + goto end; + + if (randfile) + app_RAND_load_file(randfile, 0); + + out = bio_open_default(outfile, 'w', out_bin ? FORMAT_BINARY : FORMAT_TEXT); + if (out == NULL) + goto end; + + if ((! !mac_name + ! !keyfile + ! !hmac_key) > 1) { + BIO_printf(bio_err, "MAC and Signing key cannot both be specified\n"); + goto end; + } + + if (keyfile) { + if (want_pub) + sigkey = load_pubkey(keyfile, keyform, 0, NULL, e, "key file"); + else + sigkey = load_key(keyfile, keyform, 0, passin, e, "key file"); + if (!sigkey) { + /* + * load_[pub]key() has already printed an appropriate message + */ + goto end; + } + } + + if (mac_name) { + EVP_PKEY_CTX *mac_ctx = NULL; + int r = 0; + if (!init_gen_str(&mac_ctx, mac_name, impl, 0)) + goto mac_end; + if (macopts) { + char *macopt; + for (i = 0; i < sk_OPENSSL_STRING_num(macopts); i++) { + macopt = sk_OPENSSL_STRING_value(macopts, i); + if (pkey_ctrl_string(mac_ctx, macopt) <= 0) { + BIO_printf(bio_err, + "MAC parameter error \"%s\"\n", macopt); + ERR_print_errors(bio_err); + goto mac_end; + } + } + } + if (EVP_PKEY_keygen(mac_ctx, &sigkey) <= 0) { + BIO_puts(bio_err, "Error generating key\n"); + ERR_print_errors(bio_err); + goto mac_end; + } + r = 1; + mac_end: + EVP_PKEY_CTX_free(mac_ctx); + if (r == 0) + goto end; + } + + if (hmac_key) { + sigkey = EVP_PKEY_new_mac_key(EVP_PKEY_HMAC, impl, + (unsigned char *)hmac_key, -1); + if (!sigkey) + goto end; + } + + if (sigkey) { + EVP_MD_CTX *mctx = NULL; + EVP_PKEY_CTX *pctx = NULL; + int r; + if (!BIO_get_md_ctx(bmd, &mctx)) { + BIO_printf(bio_err, "Error getting context\n"); + ERR_print_errors(bio_err); + goto end; + } + if (do_verify) + r = EVP_DigestVerifyInit(mctx, &pctx, md, impl, sigkey); + else + r = EVP_DigestSignInit(mctx, &pctx, md, impl, sigkey); + if (!r) { + BIO_printf(bio_err, "Error setting context\n"); + ERR_print_errors(bio_err); + goto end; + } + if (sigopts) { + char *sigopt; + for (i = 0; i < sk_OPENSSL_STRING_num(sigopts); i++) { + sigopt = sk_OPENSSL_STRING_value(sigopts, i); + if (pkey_ctrl_string(pctx, sigopt) <= 0) { + BIO_printf(bio_err, "parameter error \"%s\"\n", sigopt); + ERR_print_errors(bio_err); + goto end; + } + } + } + } + /* we use md as a filter, reading from 'in' */ + else { + EVP_MD_CTX *mctx = NULL; + if (!BIO_get_md_ctx(bmd, &mctx)) { + BIO_printf(bio_err, "Error getting context\n"); + ERR_print_errors(bio_err); + goto end; + } + if (md == NULL) + md = EVP_get_digestbynid(NID_sha256); + if (!EVP_DigestInit_ex(mctx, md, impl)) { + BIO_printf(bio_err, "Error setting digest\n"); + ERR_print_errors(bio_err); + goto end; + } + } + + if (sigfile && sigkey) { + BIO *sigbio = BIO_new_file(sigfile, "rb"); + if (!sigbio) { + BIO_printf(bio_err, "Error opening signature file %s\n", sigfile); + ERR_print_errors(bio_err); + goto end; + } + siglen = EVP_PKEY_size(sigkey); + sigbuf = app_malloc(siglen, "signature buffer"); + siglen = BIO_read(sigbio, sigbuf, siglen); + BIO_free(sigbio); + if (siglen <= 0) { + BIO_printf(bio_err, "Error reading signature file %s\n", sigfile); + ERR_print_errors(bio_err); + goto end; + } + } + inp = BIO_push(bmd, in); + + if (md == NULL) { + EVP_MD_CTX *tctx; + BIO_get_md_ctx(bmd, &tctx); + md = EVP_MD_CTX_md(tctx); + } + + if (argc == 0) { + BIO_set_fp(in, stdin, BIO_NOCLOSE); + ret = do_fp(out, buf, inp, separator, out_bin, sigkey, sigbuf, + siglen, NULL, NULL, "stdin"); + } else { + const char *md_name = NULL, *sig_name = NULL; + if (!out_bin) { + if (sigkey) { + const EVP_PKEY_ASN1_METHOD *ameth; + ameth = EVP_PKEY_get0_asn1(sigkey); + if (ameth) + EVP_PKEY_asn1_get0_info(NULL, NULL, + NULL, NULL, &sig_name, ameth); + } + if (md) + md_name = EVP_MD_name(md); + } + ret = 0; + for (i = 0; i < argc; i++) { + int r; + if (BIO_read_filename(in, argv[i]) <= 0) { + perror(argv[i]); + ret++; + continue; + } else + r = do_fp(out, buf, inp, separator, out_bin, sigkey, sigbuf, + siglen, sig_name, md_name, argv[i]); + if (r) + ret = r; + (void)BIO_reset(bmd); + } + } + end: + OPENSSL_clear_free(buf, BUFSIZE); + BIO_free(in); + OPENSSL_free(passin); + BIO_free_all(out); + EVP_PKEY_free(sigkey); + sk_OPENSSL_STRING_free(sigopts); + sk_OPENSSL_STRING_free(macopts); + OPENSSL_free(sigbuf); + BIO_free(bmd); + release_engine(e); + NCONF_free(conf); + return (ret); +} + +int do_fp(BIO *out, unsigned char *buf, BIO *bp, int sep, int binout, + EVP_PKEY *key, unsigned char *sigin, int siglen, + const char *sig_name, const char *md_name, + const char *file) +{ + size_t len; + int i; + + for (;;) { + i = BIO_read(bp, (char *)buf, BUFSIZE); + if (i < 0) { + BIO_printf(bio_err, "Read Error in %s\n", file); + ERR_print_errors(bio_err); + return 1; + } + if (i == 0) + break; + } + if (sigin) { + EVP_MD_CTX *ctx; + BIO_get_md_ctx(bp, &ctx); + i = EVP_DigestVerifyFinal(ctx, sigin, (unsigned int)siglen); + if (i > 0) + BIO_printf(out, "Verified OK\n"); + else if (i == 0) { + BIO_printf(out, "Verification Failure\n"); + return 1; + } else { + BIO_printf(bio_err, "Error Verifying Data\n"); + ERR_print_errors(bio_err); + return 1; + } + return 0; + } + if (key) { + EVP_MD_CTX *ctx; + BIO_get_md_ctx(bp, &ctx); + len = BUFSIZE; + if (!EVP_DigestSignFinal(ctx, buf, &len)) { + BIO_printf(bio_err, "Error Signing Data\n"); + ERR_print_errors(bio_err); + return 1; + } + } else { + len = BIO_gets(bp, (char *)buf, BUFSIZE); + if ((int)len < 0) { + ERR_print_errors(bio_err); + return 1; + } + } + + if (binout) + BIO_write(out, buf, len); + else if (sep == 2) { + for (i = 0; i < (int)len; i++) + BIO_printf(out, "%02x", buf[i]); + BIO_printf(out, " *%s\n", file); + } else { + if (sig_name) { + BIO_puts(out, sig_name); + if (md_name) + BIO_printf(out, "-%s", md_name); + BIO_printf(out, "(%s)= ", file); + } else if (md_name) + BIO_printf(out, "%s(%s)= ", md_name, file); + else + BIO_printf(out, "(%s)= ", file); + for (i = 0; i < (int)len; i++) { + if (sep && (i != 0)) + BIO_printf(out, ":"); + BIO_printf(out, "%02x", buf[i]); + } + BIO_printf(out, "\n"); + } + return 0; +} diff --git a/apps/dh1024.pem b/apps/dh1024.pem new file mode 100644 index 000000000..f1a5e180a --- /dev/null +++ b/apps/dh1024.pem @@ -0,0 +1,10 @@ +-----BEGIN DH PARAMETERS----- +MIGHAoGBAP//////////yQ/aoiFowjTExmKLgNwc0SkCTgiKZ8x0Agu+pjsTmyJR +Sgh5jjQE3e+VGbPNOkMbMCsKbfJfFDdP4TVtbVHCReSFtXZiXn7G9ExC6aY37WsL +/1y29Aa37e44a/taiZ+lrp8kEXxLH+ZJKGZR7OZTgf//////////AgEC +-----END DH PARAMETERS----- + +These are the 1024-bit DH parameters from "Internet Key Exchange +Protocol Version 2 (IKEv2)": https://tools.ietf.org/html/rfc5996 + +See https://tools.ietf.org/html/rfc2412 for how they were generated. diff --git a/apps/dh2048.pem b/apps/dh2048.pem new file mode 100644 index 000000000..e899f2e02 --- /dev/null +++ b/apps/dh2048.pem @@ -0,0 +1,14 @@ +-----BEGIN DH PARAMETERS----- +MIIBCAKCAQEA///////////JD9qiIWjCNMTGYouA3BzRKQJOCIpnzHQCC76mOxOb +IlFKCHmONATd75UZs806QxswKwpt8l8UN0/hNW1tUcJF5IW1dmJefsb0TELppjft +awv/XLb0Brft7jhr+1qJn6WunyQRfEsf5kkoZlHs5Fs9wgB8uKFjvwWY2kg2HFXT +mmkWP6j9JM9fg2VdI9yjrZYcYvNWIIVSu57VKQdwlpZtZww1Tkq8mATxdGwIyhgh +fDKQXkYuNs474553LBgOhgObJ4Oi7Aeij7XFXfBvTFLJ3ivL9pVYFxg5lUl86pVq +5RXSJhiY+gUQFXKOWoqsqmj//////////wIBAg== +-----END DH PARAMETERS----- + +These are the 2048-bit DH parameters from "More Modular Exponential +(MODP) Diffie-Hellman groups for Internet Key Exchange (IKE)": +https://tools.ietf.org/html/rfc3526 + +See https://tools.ietf.org/html/rfc2412 for how they were generated. diff --git a/apps/dh4096.pem b/apps/dh4096.pem new file mode 100644 index 000000000..adada2b55 --- /dev/null +++ b/apps/dh4096.pem @@ -0,0 +1,19 @@ +-----BEGIN DH PARAMETERS----- +MIICCAKCAgEA///////////JD9qiIWjCNMTGYouA3BzRKQJOCIpnzHQCC76mOxOb +IlFKCHmONATd75UZs806QxswKwpt8l8UN0/hNW1tUcJF5IW1dmJefsb0TELppjft +awv/XLb0Brft7jhr+1qJn6WunyQRfEsf5kkoZlHs5Fs9wgB8uKFjvwWY2kg2HFXT +mmkWP6j9JM9fg2VdI9yjrZYcYvNWIIVSu57VKQdwlpZtZww1Tkq8mATxdGwIyhgh +fDKQXkYuNs474553LBgOhgObJ4Oi7Aeij7XFXfBvTFLJ3ivL9pVYFxg5lUl86pVq +5RXSJhiY+gUQFXKOWoqqxC2tMxcNBFB6M6hVIavfHLpk7PuFBFjb7wqK6nFXXQYM +fbOXD4Wm4eTHq/WujNsJM9cejJTgSiVhnc7j0iYa0u5r8S/6BtmKCGTYdgJzPshq +ZFIfKxgXeyAMu+EXV3phXWx3CYjAutlG4gjiT6B05asxQ9tb/OD9EI5LgtEgqSEI +ARpyPBKnh+bXiHGaEL26WyaZwycYavTiPBqUaDS2FQvaJYPpyirUTOjbu8LbBN6O ++S6O/BQfvsqmKHxZR05rwF2ZspZPoJDDoiM7oYZRW+ftH2EpcM7i16+4G912IXBI +HNAGkSfVsFqpk7TqmI2P3cGG/7fckKbAj030Nck0BjGZ//////////8CAQI= +-----END DH PARAMETERS----- + +These are the 4096-bit DH parameters from "More Modular Exponential +(MODP) Diffie-Hellman groups for Internet Key Exchange (IKE)": +https://tools.ietf.org/html/rfc3526 + +See https://tools.ietf.org/html/rfc2412 for how they were generated. diff --git a/apps/dhparam.c b/apps/dhparam.c new file mode 100644 index 000000000..2223e1aac --- /dev/null +++ b/apps/dhparam.c @@ -0,0 +1,380 @@ +/* + * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#ifdef OPENSSL_NO_DH +NON_EMPTY_TRANSLATION_UNIT +#else + +# include +# include +# include +# include +# include "apps.h" +# include +# include +# include +# include +# include +# include + +# ifndef OPENSSL_NO_DSA +# include +# endif + +# define DEFBITS 2048 + +static int dh_cb(int p, int n, BN_GENCB *cb); + +typedef enum OPTION_choice { + OPT_ERR = -1, OPT_EOF = 0, OPT_HELP, + OPT_INFORM, OPT_OUTFORM, OPT_IN, OPT_OUT, + OPT_ENGINE, OPT_CHECK, OPT_TEXT, OPT_NOOUT, + OPT_RAND, OPT_DSAPARAM, OPT_C, OPT_2, OPT_5 +} OPTION_CHOICE; + +OPTIONS dhparam_options[] = { + {OPT_HELP_STR, 1, '-', "Usage: %s [flags] [numbits]\n"}, + {OPT_HELP_STR, 1, '-', "Valid options are:\n"}, + {"help", OPT_HELP, '-', "Display this summary"}, + {"in", OPT_IN, '<', "Input file"}, + {"inform", OPT_INFORM, 'F', "Input format, DER or PEM"}, + {"outform", OPT_OUTFORM, 'F', "Output format, DER or PEM"}, + {"out", OPT_OUT, '>', "Output file"}, + {"check", OPT_CHECK, '-', "Check the DH parameters"}, + {"text", OPT_TEXT, '-', "Print a text form of the DH parameters"}, + {"noout", OPT_NOOUT, '-', "Don't output any DH parameters"}, + {"rand", OPT_RAND, 's', + "Load the file(s) into the random number generator"}, + {"C", OPT_C, '-', "Print C code"}, + {"2", OPT_2, '-', "Generate parameters using 2 as the generator value"}, + {"5", OPT_5, '-', "Generate parameters using 5 as the generator value"}, +# ifndef OPENSSL_NO_DSA + {"dsaparam", OPT_DSAPARAM, '-', + "Read or generate DSA parameters, convert to DH"}, +# endif +# ifndef OPENSSL_NO_ENGINE + {"engine", OPT_ENGINE, 's', "Use engine e, possibly a hardware device"}, +# endif + {NULL} +}; + +int dhparam_main(int argc, char **argv) +{ + BIO *in = NULL, *out = NULL; + DH *dh = NULL; + char *infile = NULL, *outfile = NULL, *prog, *inrand = NULL; + ENGINE *e = NULL; +#ifndef OPENSSL_NO_DSA + int dsaparam = 0; +#endif + int i, text = 0, C = 0, ret = 1, num = 0, g = 0; + int informat = FORMAT_PEM, outformat = FORMAT_PEM, check = 0, noout = 0; + OPTION_CHOICE o; + + prog = opt_init(argc, argv, dhparam_options); + while ((o = opt_next()) != OPT_EOF) { + switch (o) { + case OPT_EOF: + case OPT_ERR: + opthelp: + BIO_printf(bio_err, "%s: Use -help for summary.\n", prog); + goto end; + case OPT_HELP: + opt_help(dhparam_options); + ret = 0; + goto end; + case OPT_INFORM: + if (!opt_format(opt_arg(), OPT_FMT_PEMDER, &informat)) + goto opthelp; + break; + case OPT_OUTFORM: + if (!opt_format(opt_arg(), OPT_FMT_PEMDER, &outformat)) + goto opthelp; + break; + case OPT_IN: + infile = opt_arg(); + break; + case OPT_OUT: + outfile = opt_arg(); + break; + case OPT_ENGINE: + e = setup_engine(opt_arg(), 0); + break; + case OPT_CHECK: + check = 1; + break; + case OPT_TEXT: + text = 1; + break; + case OPT_DSAPARAM: +#ifndef OPENSSL_NO_DSA + dsaparam = 1; +#endif + break; + case OPT_C: + C = 1; + break; + case OPT_2: + g = 2; + break; + case OPT_5: + g = 5; + break; + case OPT_NOOUT: + noout = 1; + break; + case OPT_RAND: + inrand = opt_arg(); + break; + } + } + argc = opt_num_rest(); + argv = opt_rest(); + + if (argv[0] && (!opt_int(argv[0], &num) || num <= 0)) + goto end; + + if (g && !num) + num = DEFBITS; + +# ifndef OPENSSL_NO_DSA + if (dsaparam && g) { + BIO_printf(bio_err, + "generator may not be chosen for DSA parameters\n"); + goto end; + } +# endif + /* DH parameters */ + if (num && !g) + g = 2; + + if (num) { + + BN_GENCB *cb; + cb = BN_GENCB_new(); + if (cb == NULL) { + ERR_print_errors(bio_err); + goto end; + } + + BN_GENCB_set(cb, dh_cb, bio_err); + if (!app_RAND_load_file(NULL, 1) && inrand == NULL) { + BIO_printf(bio_err, + "warning, not much extra random data, consider using the -rand option\n"); + } + if (inrand != NULL) + BIO_printf(bio_err, "%ld semi-random bytes loaded\n", + app_RAND_load_files(inrand)); + +# ifndef OPENSSL_NO_DSA + if (dsaparam) { + DSA *dsa = DSA_new(); + + BIO_printf(bio_err, + "Generating DSA parameters, %d bit long prime\n", num); + if (dsa == NULL + || !DSA_generate_parameters_ex(dsa, num, NULL, 0, NULL, NULL, + cb)) { + DSA_free(dsa); + BN_GENCB_free(cb); + ERR_print_errors(bio_err); + goto end; + } + + dh = DSA_dup_DH(dsa); + DSA_free(dsa); + if (dh == NULL) { + BN_GENCB_free(cb); + ERR_print_errors(bio_err); + goto end; + } + } else +# endif + { + dh = DH_new(); + BIO_printf(bio_err, + "Generating DH parameters, %d bit long safe prime, generator %d\n", + num, g); + BIO_printf(bio_err, "This is going to take a long time\n"); + if (dh == NULL || !DH_generate_parameters_ex(dh, num, g, cb)) { + BN_GENCB_free(cb); + ERR_print_errors(bio_err); + goto end; + } + } + + BN_GENCB_free(cb); + app_RAND_write_file(NULL); + } else { + + in = bio_open_default(infile, 'r', informat); + if (in == NULL) + goto end; + +# ifndef OPENSSL_NO_DSA + if (dsaparam) { + DSA *dsa; + + if (informat == FORMAT_ASN1) + dsa = d2i_DSAparams_bio(in, NULL); + else /* informat == FORMAT_PEM */ + dsa = PEM_read_bio_DSAparams(in, NULL, NULL, NULL); + + if (dsa == NULL) { + BIO_printf(bio_err, "unable to load DSA parameters\n"); + ERR_print_errors(bio_err); + goto end; + } + + dh = DSA_dup_DH(dsa); + DSA_free(dsa); + if (dh == NULL) { + ERR_print_errors(bio_err); + goto end; + } + } else +# endif + { + if (informat == FORMAT_ASN1) + dh = d2i_DHparams_bio(in, NULL); + else /* informat == FORMAT_PEM */ + dh = PEM_read_bio_DHparams(in, NULL, NULL, NULL); + + if (dh == NULL) { + BIO_printf(bio_err, "unable to load DH parameters\n"); + ERR_print_errors(bio_err); + goto end; + } + } + + /* dh != NULL */ + } + + out = bio_open_default(outfile, 'w', outformat); + if (out == NULL) + goto end; + + if (text) { + DHparams_print(out, dh); + } + + if (check) { + if (!DH_check(dh, &i)) { + ERR_print_errors(bio_err); + goto end; + } + if (i & DH_CHECK_P_NOT_PRIME) + BIO_printf(bio_err, "WARNING: p value is not prime\n"); + if (i & DH_CHECK_P_NOT_SAFE_PRIME) + BIO_printf(bio_err, "WARNING: p value is not a safe prime\n"); + if (i & DH_CHECK_Q_NOT_PRIME) + BIO_printf(bio_err, "WARNING: q value is not a prime\n"); + if (i & DH_CHECK_INVALID_Q_VALUE) + BIO_printf(bio_err, "WARNING: q value is invalid\n"); + if (i & DH_CHECK_INVALID_J_VALUE) + BIO_printf(bio_err, "WARNING: j value is invalid\n"); + if (i & DH_UNABLE_TO_CHECK_GENERATOR) + BIO_printf(bio_err, + "WARNING: unable to check the generator value\n"); + if (i & DH_NOT_SUITABLE_GENERATOR) + BIO_printf(bio_err, "WARNING: the g value is not a generator\n"); + if (i == 0) + BIO_printf(bio_err, "DH parameters appear to be ok.\n"); + if (num != 0 && i != 0) { + /* + * We have generated parameters but DH_check() indicates they are + * invalid! This should never happen! + */ + BIO_printf(bio_err, "ERROR: Invalid parameters generated\n"); + goto end; + } + } + if (C) { + unsigned char *data; + int len, bits; + const BIGNUM *pbn, *gbn; + + len = DH_size(dh); + bits = DH_bits(dh); + DH_get0_pqg(dh, &pbn, NULL, &gbn); + data = app_malloc(len, "print a BN"); + BIO_printf(out, "#ifndef HEADER_DH_H\n" + "# include \n" + "#endif\n" + "\n"); + BIO_printf(out, "DH *get_dh%d()\n{\n", bits); + print_bignum_var(out, pbn, "dhp", bits, data); + print_bignum_var(out, gbn, "dhg", bits, data); + BIO_printf(out, " DH *dh = DH_new();\n" + " BIGNUM *dhp_bn, *dhg_bn;\n" + "\n" + " if (dh == NULL)\n" + " return NULL;\n"); + BIO_printf(out, " dhp_bn = BN_bin2bn(dhp_%d, sizeof (dhp_%d), NULL);\n", + bits, bits); + BIO_printf(out, " dhg_bn = BN_bin2bn(dhg_%d, sizeof (dhg_%d), NULL);\n", + bits, bits); + BIO_printf(out, " if (dhp_bn == NULL || dhg_bn == NULL\n" + " || !DH_set0_pqg(dh, dhp_bn, NULL, dhg_bn)) {\n" + " DH_free(dh);\n" + " BN_free(dhp_bn);\n" + " BN_free(dhg_bn);\n" + " return NULL;\n" + " }\n"); + if (DH_get_length(dh) > 0) + BIO_printf(out, + " if (!DH_set_length(dh, %ld)) {\n" + " DH_free(dh);\n" + " }\n", DH_get_length(dh)); + BIO_printf(out, " return dh;\n}\n"); + OPENSSL_free(data); + } + + if (!noout) { + const BIGNUM *q; + DH_get0_pqg(dh, NULL, &q, NULL); + if (outformat == FORMAT_ASN1) + i = i2d_DHparams_bio(out, dh); + else if (q != NULL) + i = PEM_write_bio_DHxparams(out, dh); + else + i = PEM_write_bio_DHparams(out, dh); + if (!i) { + BIO_printf(bio_err, "unable to write DH parameters\n"); + ERR_print_errors(bio_err); + goto end; + } + } + ret = 0; + end: + BIO_free(in); + BIO_free_all(out); + DH_free(dh); + release_engine(e); + return (ret); +} + +static int dh_cb(int p, int n, BN_GENCB *cb) +{ + char c = '*'; + + if (p == 0) + c = '.'; + if (p == 1) + c = '+'; + if (p == 2) + c = '*'; + if (p == 3) + c = '\n'; + BIO_write(BN_GENCB_get_arg(cb), &c, 1); + (void)BIO_flush(BN_GENCB_get_arg(cb)); + return 1; +} +#endif diff --git a/apps/dsa-ca.pem b/apps/dsa-ca.pem new file mode 100644 index 000000000..3ce8dc605 --- /dev/null +++ b/apps/dsa-ca.pem @@ -0,0 +1,47 @@ +-----BEGIN DSA PRIVATE KEY----- +MIIBugIBAAKBgQCnP26Fv0FqKX3wn0cZMJCaCR3aajMexT2GlrMV4FMuj+BZgnOQ +PnUxmUd6UvuF5NmmezibaIqEm4fGHrV+hktTW1nPcWUZiG7OZq5riDb77Cjcwtel +u+UsOSZL2ppwGJU3lRBWI/YV7boEXt45T/23Qx+1pGVvzYAR5HCVW1DNSQIVAPcH +Me36bAYD1YWKHKycZedQZmVvAoGATd9MA6aRivUZb1BGJZnlaG8w42nh5bNdmLso +hkj83pkEP1+IDJxzJA0gXbkqmj8YlifkYofBe3RiU/xhJ6h6kQmdtvFNnFQPWAbu +SXQHzlV+I84W9srcWmEBfslxtU323DQph2j2XiCTs9v15AlsQReVkusBtXOlan7Y +Mu0OArgCgYAapll6iqz9XrZFlk2GCVcB+KihxWnH7IuHvSLw9YUrJahcBHmbpvt4 +94lF4gC5w3WPM+vXJofbusk4GoQEEsQNMDaah4m49uUqAylOVFJJJXuirVJ+o+0T +tOFDITEAl+YZZariXOD7tdOSOl9RLMPC6+daHKS9e68u3enxhqnDGQIUB78dhW77 +J6zsFbSEHaQGUmfSeoM= +-----END DSA PRIVATE KEY----- +-----BEGIN CERTIFICATE REQUEST----- +MIICVjCCAhMCAQAwUjELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUx +ITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDELMAkGA1UEAwwCQ0Ew +ggG2MIIBKwYHKoZIzjgEATCCAR4CgYEApz9uhb9Bail98J9HGTCQmgkd2mozHsU9 +hpazFeBTLo/gWYJzkD51MZlHelL7heTZpns4m2iKhJuHxh61foZLU1tZz3FlGYhu +zmaua4g2++wo3MLXpbvlLDkmS9qacBiVN5UQViP2Fe26BF7eOU/9t0MftaRlb82A +EeRwlVtQzUkCFQD3BzHt+mwGA9WFihysnGXnUGZlbwKBgE3fTAOmkYr1GW9QRiWZ +5WhvMONp4eWzXZi7KIZI/N6ZBD9fiAyccyQNIF25Kpo/GJYn5GKHwXt0YlP8YSeo +epEJnbbxTZxUD1gG7kl0B85VfiPOFvbK3FphAX7JcbVN9tw0KYdo9l4gk7Pb9eQJ +bEEXlZLrAbVzpWp+2DLtDgK4A4GEAAKBgBqmWXqKrP1etkWWTYYJVwH4qKHFacfs +i4e9IvD1hSslqFwEeZum+3j3iUXiALnDdY8z69cmh9u6yTgahAQSxA0wNpqHibj2 +5SoDKU5UUkkle6KtUn6j7RO04UMhMQCX5hllquJc4Pu105I6X1Esw8Lr51ocpL17 +ry7d6fGGqcMZoAAwCwYJYIZIAWUDBAMCAzAAMC0CFCp7rUwGJNtxK6Aqo6k6US+S +KP8sAhUAyfSi8Zs3QAvkJoFG0IMRaq8M03I= +-----END CERTIFICATE REQUEST----- +-----BEGIN CERTIFICATE----- +MIIDMDCCAuygAwIBAgIBAjALBglghkgBZQMEAwIwUzELMAkGA1UEBhMCQVUxEzAR +BgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5 +IEx0ZDEMMAoGA1UEAwwDUENBMCAXDTE2MDExMzIxNDE0OVoYDzMwMTUwNTE2MjE0 +MTQ5WjBSMQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UE +CgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMQswCQYDVQQDDAJDQTCCAbYwggEr +BgcqhkjOOAQBMIIBHgKBgQCnP26Fv0FqKX3wn0cZMJCaCR3aajMexT2GlrMV4FMu +j+BZgnOQPnUxmUd6UvuF5NmmezibaIqEm4fGHrV+hktTW1nPcWUZiG7OZq5riDb7 +7Cjcwtelu+UsOSZL2ppwGJU3lRBWI/YV7boEXt45T/23Qx+1pGVvzYAR5HCVW1DN +SQIVAPcHMe36bAYD1YWKHKycZedQZmVvAoGATd9MA6aRivUZb1BGJZnlaG8w42nh +5bNdmLsohkj83pkEP1+IDJxzJA0gXbkqmj8YlifkYofBe3RiU/xhJ6h6kQmdtvFN +nFQPWAbuSXQHzlV+I84W9srcWmEBfslxtU323DQph2j2XiCTs9v15AlsQReVkusB +tXOlan7YMu0OArgDgYQAAoGAGqZZeoqs/V62RZZNhglXAfioocVpx+yLh70i8PWF +KyWoXAR5m6b7ePeJReIAucN1jzPr1yaH27rJOBqEBBLEDTA2moeJuPblKgMpTlRS +SSV7oq1SfqPtE7ThQyExAJfmGWWq4lzg+7XTkjpfUSzDwuvnWhykvXuvLt3p8Yap +wxmjUDBOMB0GA1UdDgQWBBTMZcORcBEVlqO/CD4pf4V6N1NM1zAfBgNVHSMEGDAW +gBTGjwJ33uvjSa20RNrMKWoGptOLdDAMBgNVHRMEBTADAQH/MAsGCWCGSAFlAwQD +AgMxADAuAhUA4V6MrHufG8R79E+AtVO02olPxK8CFQDkZyo/TWpavsUBRDJbCeD9 +jgjIkA== +-----END CERTIFICATE----- diff --git a/apps/dsa-pca.pem b/apps/dsa-pca.pem new file mode 100644 index 000000000..a51a06ed1 --- /dev/null +++ b/apps/dsa-pca.pem @@ -0,0 +1,47 @@ +-----BEGIN DSA PRIVATE KEY----- +MIIBvAIBAAKBgQCnP26Fv0FqKX3wn0cZMJCaCR3aajMexT2GlrMV4FMuj+BZgnOQ +PnUxmUd6UvuF5NmmezibaIqEm4fGHrV+hktTW1nPcWUZiG7OZq5riDb77Cjcwtel +u+UsOSZL2ppwGJU3lRBWI/YV7boEXt45T/23Qx+1pGVvzYAR5HCVW1DNSQIVAPcH +Me36bAYD1YWKHKycZedQZmVvAoGATd9MA6aRivUZb1BGJZnlaG8w42nh5bNdmLso +hkj83pkEP1+IDJxzJA0gXbkqmj8YlifkYofBe3RiU/xhJ6h6kQmdtvFNnFQPWAbu +SXQHzlV+I84W9srcWmEBfslxtU323DQph2j2XiCTs9v15AlsQReVkusBtXOlan7Y +Mu0OArgCgYEApu25HkB1b4gKMIV7aLGNSIknMzYgrB7o1kQxeDf34dDVRM9OZ8tk +umz6tl+iUcNe5EoxdsYV1IXSddjOi08LOLsZq7AQlNnKvbtlmMDULpqkZJD0bO7A +29nisJfKy1URqABLw5DgfcPh1ZLXtmDfUgJvmjgTmvTPT2j9TPjq7RUCFQDNvrBz +6TicfImU7UFRn9h00j0lJQ== +-----END DSA PRIVATE KEY----- +-----BEGIN CERTIFICATE REQUEST----- +MIICWDCCAhUCAQAwUzELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUx +ITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEMMAoGA1UEAwwDUENB +MIIBtzCCASsGByqGSM44BAEwggEeAoGBAKc/boW/QWopffCfRxkwkJoJHdpqMx7F +PYaWsxXgUy6P4FmCc5A+dTGZR3pS+4Xk2aZ7OJtoioSbh8YetX6GS1NbWc9xZRmI +bs5mrmuINvvsKNzC16W75Sw5JkvamnAYlTeVEFYj9hXtugRe3jlP/bdDH7WkZW/N +gBHkcJVbUM1JAhUA9wcx7fpsBgPVhYocrJxl51BmZW8CgYBN30wDppGK9RlvUEYl +meVobzDjaeHls12YuyiGSPzemQQ/X4gMnHMkDSBduSqaPxiWJ+Rih8F7dGJT/GEn +qHqRCZ228U2cVA9YBu5JdAfOVX4jzhb2ytxaYQF+yXG1TfbcNCmHaPZeIJOz2/Xk +CWxBF5WS6wG1c6Vqftgy7Q4CuAOBhQACgYEApu25HkB1b4gKMIV7aLGNSIknMzYg +rB7o1kQxeDf34dDVRM9OZ8tkumz6tl+iUcNe5EoxdsYV1IXSddjOi08LOLsZq7AQ +lNnKvbtlmMDULpqkZJD0bO7A29nisJfKy1URqABLw5DgfcPh1ZLXtmDfUgJvmjgT +mvTPT2j9TPjq7RWgADALBglghkgBZQMEAwIDMAAwLQIVAPA6/jxCT1D2HgzE4iZR +AEup/C7YAhRPLTQvQnAiS5FRrA+8SwBLvDAsaw== +-----END CERTIFICATE REQUEST----- +-----BEGIN CERTIFICATE----- +MIIDMDCCAu6gAwIBAgIBATALBglghkgBZQMEAwIwUzELMAkGA1UEBhMCQVUxEzAR +BgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5 +IEx0ZDEMMAoGA1UEAwwDUENBMCAXDTE2MDExMzIxNDE0OVoYDzMwMTUwNTE2MjE0 +MTQ5WjBTMQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UE +CgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMQwwCgYDVQQDDANQQ0EwggG3MIIB +KwYHKoZIzjgEATCCAR4CgYEApz9uhb9Bail98J9HGTCQmgkd2mozHsU9hpazFeBT +Lo/gWYJzkD51MZlHelL7heTZpns4m2iKhJuHxh61foZLU1tZz3FlGYhuzmaua4g2 +++wo3MLXpbvlLDkmS9qacBiVN5UQViP2Fe26BF7eOU/9t0MftaRlb82AEeRwlVtQ +zUkCFQD3BzHt+mwGA9WFihysnGXnUGZlbwKBgE3fTAOmkYr1GW9QRiWZ5WhvMONp +4eWzXZi7KIZI/N6ZBD9fiAyccyQNIF25Kpo/GJYn5GKHwXt0YlP8YSeoepEJnbbx +TZxUD1gG7kl0B85VfiPOFvbK3FphAX7JcbVN9tw0KYdo9l4gk7Pb9eQJbEEXlZLr +AbVzpWp+2DLtDgK4A4GFAAKBgQCm7bkeQHVviAowhXtosY1IiSczNiCsHujWRDF4 +N/fh0NVEz05ny2S6bPq2X6JRw17kSjF2xhXUhdJ12M6LTws4uxmrsBCU2cq9u2WY +wNQumqRkkPRs7sDb2eKwl8rLVRGoAEvDkOB9w+HVkte2YN9SAm+aOBOa9M9PaP1M ++OrtFaNQME4wHQYDVR0OBBYEFMaPAnfe6+NJrbRE2swpagam04t0MB8GA1UdIwQY +MBaAFMaPAnfe6+NJrbRE2swpagam04t0MAwGA1UdEwQFMAMBAf8wCwYJYIZIAWUD +BAMCAy8AMCwCFFhdz4fzQo9BBF20U1CHldYTi/D7AhQydDnDMj21y+U1UhDZJrvh +lnt88g== +-----END CERTIFICATE----- diff --git a/apps/dsa.c b/apps/dsa.c new file mode 100644 index 000000000..9c9354916 --- /dev/null +++ b/apps/dsa.c @@ -0,0 +1,262 @@ +/* + * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#ifdef OPENSSL_NO_DSA +NON_EMPTY_TRANSLATION_UNIT +#else + +# include +# include +# include +# include +# include "apps.h" +# include +# include +# include +# include +# include +# include +# include + +typedef enum OPTION_choice { + OPT_ERR = -1, OPT_EOF = 0, OPT_HELP, + OPT_INFORM, OPT_OUTFORM, OPT_IN, OPT_OUT, OPT_ENGINE, + /* Do not change the order here; see case statements below */ + OPT_PVK_NONE, OPT_PVK_WEAK, OPT_PVK_STRONG, + OPT_NOOUT, OPT_TEXT, OPT_MODULUS, OPT_PUBIN, + OPT_PUBOUT, OPT_CIPHER, OPT_PASSIN, OPT_PASSOUT +} OPTION_CHOICE; + +OPTIONS dsa_options[] = { + {"help", OPT_HELP, '-', "Display this summary"}, + {"inform", OPT_INFORM, 'f', "Input format, DER PEM PVK"}, + {"outform", OPT_OUTFORM, 'f', "Output format, DER PEM PVK"}, + {"in", OPT_IN, 's', "Input key"}, + {"out", OPT_OUT, '>', "Output file"}, + {"noout", OPT_NOOUT, '-', "Don't print key out"}, + {"text", OPT_TEXT, '-', "Print the key in text"}, + {"modulus", OPT_MODULUS, '-', "Print the DSA public value"}, + {"pubin", OPT_PUBIN, '-', "Expect a public key in input file"}, + {"pubout", OPT_PUBOUT, '-', "Output public key, not private"}, + {"passin", OPT_PASSIN, 's', "Input file pass phrase source"}, + {"passout", OPT_PASSOUT, 's', "Output file pass phrase source"}, + {"", OPT_CIPHER, '-', "Any supported cipher"}, +# ifndef OPENSSL_NO_RC4 + {"pvk-strong", OPT_PVK_STRONG, '-', "Enable 'Strong' PVK encoding level (default)"}, + {"pvk-weak", OPT_PVK_WEAK, '-', "Enable 'Weak' PVK encoding level"}, + {"pvk-none", OPT_PVK_NONE, '-', "Don't enforce PVK encoding"}, +# endif +# ifndef OPENSSL_NO_ENGINE + {"engine", OPT_ENGINE, 's', "Use engine e, possibly a hardware device"}, +# endif + {NULL} +}; + +int dsa_main(int argc, char **argv) +{ + BIO *out = NULL; + DSA *dsa = NULL; + ENGINE *e = NULL; + const EVP_CIPHER *enc = NULL; + char *infile = NULL, *outfile = NULL, *prog; + char *passin = NULL, *passout = NULL, *passinarg = NULL, *passoutarg = NULL; + OPTION_CHOICE o; + int informat = FORMAT_PEM, outformat = FORMAT_PEM, text = 0, noout = 0; + int i, modulus = 0, pubin = 0, pubout = 0, ret = 1; +# ifndef OPENSSL_NO_RC4 + int pvk_encr = 2; +# endif + int private = 0; + + prog = opt_init(argc, argv, dsa_options); + while ((o = opt_next()) != OPT_EOF) { + switch (o) { + case OPT_EOF: + case OPT_ERR: + opthelp: + ret = 0; + BIO_printf(bio_err, "%s: Use -help for summary.\n", prog); + goto end; + case OPT_HELP: + opt_help(dsa_options); + ret = 0; + goto end; + case OPT_INFORM: + if (!opt_format(opt_arg(), OPT_FMT_ANY, &informat)) + goto opthelp; + break; + case OPT_IN: + infile = opt_arg(); + break; + case OPT_OUTFORM: + if (!opt_format(opt_arg(), OPT_FMT_ANY, &outformat)) + goto opthelp; + break; + case OPT_OUT: + outfile = opt_arg(); + break; + case OPT_ENGINE: + e = setup_engine(opt_arg(), 0); + break; + case OPT_PASSIN: + passinarg = opt_arg(); + break; + case OPT_PASSOUT: + passoutarg = opt_arg(); + break; + case OPT_PVK_STRONG: /* pvk_encr:= 2 */ + case OPT_PVK_WEAK: /* pvk_encr:= 1 */ + case OPT_PVK_NONE: /* pvk_encr:= 0 */ +#ifndef OPENSSL_NO_RC4 + pvk_encr = (o - OPT_PVK_NONE); +#endif + break; + case OPT_NOOUT: + noout = 1; + break; + case OPT_TEXT: + text = 1; + break; + case OPT_MODULUS: + modulus = 1; + break; + case OPT_PUBIN: + pubin = 1; + break; + case OPT_PUBOUT: + pubout = 1; + break; + case OPT_CIPHER: + if (!opt_cipher(opt_unknown(), &enc)) + goto end; + break; + } + } + argc = opt_num_rest(); + if (argc != 0) + goto opthelp; + + private = pubin || pubout ? 0 : 1; + if (text && !pubin) + private = 1; + + if (!app_passwd(passinarg, passoutarg, &passin, &passout)) { + BIO_printf(bio_err, "Error getting passwords\n"); + goto end; + } + + BIO_printf(bio_err, "read DSA key\n"); + { + EVP_PKEY *pkey; + + if (pubin) + pkey = load_pubkey(infile, informat, 1, passin, e, "Public Key"); + else + pkey = load_key(infile, informat, 1, passin, e, "Private Key"); + + if (pkey) { + dsa = EVP_PKEY_get1_DSA(pkey); + EVP_PKEY_free(pkey); + } + } + if (dsa == NULL) { + BIO_printf(bio_err, "unable to load Key\n"); + ERR_print_errors(bio_err); + goto end; + } + + out = bio_open_owner(outfile, outformat, private); + if (out == NULL) + goto end; + + if (text) { + assert(pubin || private); + if (!DSA_print(out, dsa, 0)) { + perror(outfile); + ERR_print_errors(bio_err); + goto end; + } + } + + if (modulus) { + const BIGNUM *pub_key = NULL; + DSA_get0_key(dsa, &pub_key, NULL); + BIO_printf(out, "Public Key="); + BN_print(out, pub_key); + BIO_printf(out, "\n"); + } + + if (noout) { + ret = 0; + goto end; + } + BIO_printf(bio_err, "writing DSA key\n"); + if (outformat == FORMAT_ASN1) { + if (pubin || pubout) + i = i2d_DSA_PUBKEY_bio(out, dsa); + else { + assert(private); + i = i2d_DSAPrivateKey_bio(out, dsa); + } + } else if (outformat == FORMAT_PEM) { + if (pubin || pubout) + i = PEM_write_bio_DSA_PUBKEY(out, dsa); + else { + assert(private); + i = PEM_write_bio_DSAPrivateKey(out, dsa, enc, + NULL, 0, NULL, passout); + } +# ifndef OPENSSL_NO_RSA + } else if (outformat == FORMAT_MSBLOB || outformat == FORMAT_PVK) { + EVP_PKEY *pk; + pk = EVP_PKEY_new(); + EVP_PKEY_set1_DSA(pk, dsa); + if (outformat == FORMAT_PVK) { + if (pubin) { + BIO_printf(bio_err, "PVK form impossible with public key input\n"); + EVP_PKEY_free(pk); + goto end; + } + assert(private); +# ifdef OPENSSL_NO_RC4 + BIO_printf(bio_err, "PVK format not supported\n"); + EVP_PKEY_free(pk); + goto end; +# else + i = i2b_PVK_bio(out, pk, pvk_encr, 0, passout); +# endif + } + else if (pubin || pubout) + i = i2b_PublicKey_bio(out, pk); + else { + assert(private); + i = i2b_PrivateKey_bio(out, pk); + } + EVP_PKEY_free(pk); +# endif + } else { + BIO_printf(bio_err, "bad output format specified for outfile\n"); + goto end; + } + if (i <= 0) { + BIO_printf(bio_err, "unable to write private key\n"); + ERR_print_errors(bio_err); + goto end; + } + ret = 0; + end: + BIO_free_all(out); + DSA_free(dsa); + release_engine(e); + OPENSSL_free(passin); + OPENSSL_free(passout); + return (ret); +} +#endif diff --git a/apps/dsa1024.pem b/apps/dsa1024.pem new file mode 100644 index 000000000..082dec389 --- /dev/null +++ b/apps/dsa1024.pem @@ -0,0 +1,9 @@ +-----BEGIN DSA PARAMETERS----- +MIIBHgKBgQCnP26Fv0FqKX3wn0cZMJCaCR3aajMexT2GlrMV4FMuj+BZgnOQPnUx +mUd6UvuF5NmmezibaIqEm4fGHrV+hktTW1nPcWUZiG7OZq5riDb77Cjcwtelu+Us +OSZL2ppwGJU3lRBWI/YV7boEXt45T/23Qx+1pGVvzYAR5HCVW1DNSQIVAPcHMe36 +bAYD1YWKHKycZedQZmVvAoGATd9MA6aRivUZb1BGJZnlaG8w42nh5bNdmLsohkj8 +3pkEP1+IDJxzJA0gXbkqmj8YlifkYofBe3RiU/xhJ6h6kQmdtvFNnFQPWAbuSXQH +zlV+I84W9srcWmEBfslxtU323DQph2j2XiCTs9v15AlsQReVkusBtXOlan7YMu0O +Arg= +-----END DSA PARAMETERS----- diff --git a/apps/dsa512.pem b/apps/dsa512.pem new file mode 100644 index 000000000..5f86d1a6e --- /dev/null +++ b/apps/dsa512.pem @@ -0,0 +1,6 @@ +-----BEGIN DSA PARAMETERS----- +MIGdAkEAnRtpjibb8isRcBmG9hnI+BnyGFOURgbQYlAzSwI8UjADizv5X9EkBk97 +TLqqQJv9luQ3M7stWtdaEUBmonZ9MQIVAPtT71C0QJIxVoZTeuiLIppJ+3GPAkEA +gz6I5cWJc847bAFJv7PHnwrqRJHlMKrZvltftxDXibeOdPvPKR7rqCxUUbgQ3qDO +L8wka5B33qJoplISogOdIA== +-----END DSA PARAMETERS----- diff --git a/apps/dsap.pem b/apps/dsap.pem new file mode 100644 index 000000000..d4dfdb305 --- /dev/null +++ b/apps/dsap.pem @@ -0,0 +1,6 @@ +-----BEGIN DSA PARAMETERS----- +MIGcAkEA+ZiKEvZmc9MtnaFZh4NiZ3oZS4J1PHvPrm9MXj5ntVheDPkdmBDTncya +GAJcMjwsyB/GvLDGd6yGCw/8eF+09wIVAK3VagOxGd/Q4Af5NbxR5FB7CXEjAkA2 +t/q7HgVLi0KeKvcDG8BRl3wuy7bCvpjgtWiJc/tpvcuzeuAayH89UofjAGueKjXD +ADiRffvSdhrNw5dkqdql +-----END DSA PARAMETERS----- diff --git a/apps/dsaparam.c b/apps/dsaparam.c new file mode 100644 index 000000000..925880300 --- /dev/null +++ b/apps/dsaparam.c @@ -0,0 +1,313 @@ +/* + * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#ifdef OPENSSL_NO_DSA +NON_EMPTY_TRANSLATION_UNIT +#else + +# include +# include +# include +# include +# include "apps.h" +# include +# include +# include +# include +# include +# include + +# ifdef GENCB_TEST + +static int stop_keygen_flag = 0; + +static void timebomb_sigalarm(int foo) +{ + stop_keygen_flag = 1; +} + +# endif + +static int dsa_cb(int p, int n, BN_GENCB *cb); + +typedef enum OPTION_choice { + OPT_ERR = -1, OPT_EOF = 0, OPT_HELP, + OPT_INFORM, OPT_OUTFORM, OPT_IN, OPT_OUT, OPT_TEXT, OPT_C, + OPT_NOOUT, OPT_GENKEY, OPT_RAND, OPT_ENGINE, + OPT_TIMEBOMB +} OPTION_CHOICE; + +OPTIONS dsaparam_options[] = { + {"help", OPT_HELP, '-', "Display this summary"}, + {"inform", OPT_INFORM, 'F', "Input format - DER or PEM"}, + {"in", OPT_IN, '<', "Input file"}, + {"outform", OPT_OUTFORM, 'F', "Output format - DER or PEM"}, + {"out", OPT_OUT, '>', "Output file"}, + {"text", OPT_TEXT, '-', "Print as text"}, + {"C", OPT_C, '-', "Output C code"}, + {"noout", OPT_NOOUT, '-', "No output"}, + {"genkey", OPT_GENKEY, '-', "Generate a DSA key"}, + {"rand", OPT_RAND, 's', "Files to use for random number input"}, +# ifdef GENCB_TEST + {"timebomb", OPT_TIMEBOMB, 'p', "Interrupt keygen after 'pnum' seconds"}, +# endif +# ifndef OPENSSL_NO_ENGINE + {"engine", OPT_ENGINE, 's', "Use engine e, possibly a hardware device"}, +# endif + {NULL} +}; + +int dsaparam_main(int argc, char **argv) +{ + ENGINE *e = NULL; + DSA *dsa = NULL; + BIO *in = NULL, *out = NULL; + BN_GENCB *cb = NULL; + int numbits = -1, num = 0, genkey = 0, need_rand = 0; + int informat = FORMAT_PEM, outformat = FORMAT_PEM, noout = 0, C = 0; + int ret = 1, i, text = 0, private = 0; +# ifdef GENCB_TEST + int timebomb = 0; +# endif + char *infile = NULL, *outfile = NULL, *prog, *inrand = NULL; + OPTION_CHOICE o; + + prog = opt_init(argc, argv, dsaparam_options); + while ((o = opt_next()) != OPT_EOF) { + switch (o) { + case OPT_EOF: + case OPT_ERR: + opthelp: + BIO_printf(bio_err, "%s: Use -help for summary.\n", prog); + goto end; + case OPT_HELP: + opt_help(dsaparam_options); + ret = 0; + goto end; + case OPT_INFORM: + if (!opt_format(opt_arg(), OPT_FMT_PEMDER, &informat)) + goto opthelp; + break; + case OPT_IN: + infile = opt_arg(); + break; + case OPT_OUTFORM: + if (!opt_format(opt_arg(), OPT_FMT_PEMDER, &outformat)) + goto opthelp; + break; + case OPT_OUT: + outfile = opt_arg(); + break; + case OPT_ENGINE: + e = setup_engine(opt_arg(), 0); + break; + case OPT_TIMEBOMB: +# ifdef GENCB_TEST + timebomb = atoi(opt_arg()); + break; +# endif + case OPT_TEXT: + text = 1; + break; + case OPT_C: + C = 1; + break; + case OPT_GENKEY: + genkey = need_rand = 1; + break; + case OPT_RAND: + inrand = opt_arg(); + need_rand = 1; + break; + case OPT_NOOUT: + noout = 1; + break; + } + } + argc = opt_num_rest(); + argv = opt_rest(); + + if (argc == 1) { + if (!opt_int(argv[0], &num) || num < 0) + goto end; + /* generate a key */ + numbits = num; + need_rand = 1; + } + private = genkey ? 1 : 0; + + in = bio_open_default(infile, 'r', informat); + if (in == NULL) + goto end; + out = bio_open_owner(outfile, outformat, private); + if (out == NULL) + goto end; + + if (need_rand) { + app_RAND_load_file(NULL, (inrand != NULL)); + if (inrand != NULL) + BIO_printf(bio_err, "%ld semi-random bytes loaded\n", + app_RAND_load_files(inrand)); + } + + if (numbits > 0) { + cb = BN_GENCB_new(); + if (cb == NULL) { + BIO_printf(bio_err, "Error allocating BN_GENCB object\n"); + goto end; + } + BN_GENCB_set(cb, dsa_cb, bio_err); + assert(need_rand); + dsa = DSA_new(); + if (dsa == NULL) { + BIO_printf(bio_err, "Error allocating DSA object\n"); + goto end; + } + BIO_printf(bio_err, "Generating DSA parameters, %d bit long prime\n", + num); + BIO_printf(bio_err, "This could take some time\n"); +# ifdef GENCB_TEST + if (timebomb > 0) { + struct sigaction act; + act.sa_handler = timebomb_sigalarm; + act.sa_flags = 0; + BIO_printf(bio_err, + "(though I'll stop it if not done within %d secs)\n", + timebomb); + if (sigaction(SIGALRM, &act, NULL) != 0) { + BIO_printf(bio_err, "Error, couldn't set SIGALRM handler\n"); + goto end; + } + alarm(timebomb); + } +# endif + if (!DSA_generate_parameters_ex(dsa, num, NULL, 0, NULL, NULL, cb)) { +# ifdef GENCB_TEST + if (stop_keygen_flag) { + BIO_printf(bio_err, "DSA key generation time-stopped\n"); + /* This is an asked-for behaviour! */ + ret = 0; + goto end; + } +# endif + ERR_print_errors(bio_err); + BIO_printf(bio_err, "Error, DSA key generation failed\n"); + goto end; + } + } else if (informat == FORMAT_ASN1) + dsa = d2i_DSAparams_bio(in, NULL); + else + dsa = PEM_read_bio_DSAparams(in, NULL, NULL, NULL); + if (dsa == NULL) { + BIO_printf(bio_err, "unable to load DSA parameters\n"); + ERR_print_errors(bio_err); + goto end; + } + + if (text) { + DSAparams_print(out, dsa); + } + + if (C) { + const BIGNUM *p = NULL, *q = NULL, *g = NULL; + unsigned char *data; + int len, bits_p; + + DSA_get0_pqg(dsa, &p, &q, &g); + len = BN_num_bytes(p); + bits_p = BN_num_bits(p); + + data = app_malloc(len + 20, "BN space"); + + BIO_printf(bio_out, "DSA *get_dsa%d()\n{\n", bits_p); + print_bignum_var(bio_out, p, "dsap", len, data); + print_bignum_var(bio_out, q, "dsaq", len, data); + print_bignum_var(bio_out, g, "dsag", len, data); + BIO_printf(bio_out, " DSA *dsa = DSA_new();\n" + "\n"); + BIO_printf(bio_out, " if (dsa == NULL)\n" + " return NULL;\n"); + BIO_printf(bio_out, " dsa->p = BN_bin2bn(dsap_%d, sizeof (dsap_%d), NULL);\n", + bits_p, bits_p); + BIO_printf(bio_out, " dsa->q = BN_bin2bn(dsaq_%d, sizeof (dsaq_%d), NULL);\n", + bits_p, bits_p); + BIO_printf(bio_out, " dsa->g = BN_bin2bn(dsag_%d, sizeof (dsag_%d), NULL);\n", + bits_p, bits_p); + BIO_printf(bio_out, " if (!dsa->p || !dsa->q || !dsa->g) {\n" + " DSA_free(dsa);\n" + " return NULL;\n" + " }\n" + " return(dsa);\n}\n"); + OPENSSL_free(data); + } + + if (!noout) { + if (outformat == FORMAT_ASN1) + i = i2d_DSAparams_bio(out, dsa); + else + i = PEM_write_bio_DSAparams(out, dsa); + if (!i) { + BIO_printf(bio_err, "unable to write DSA parameters\n"); + ERR_print_errors(bio_err); + goto end; + } + } + if (genkey) { + DSA *dsakey; + + assert(need_rand); + if ((dsakey = DSAparams_dup(dsa)) == NULL) + goto end; + if (!DSA_generate_key(dsakey)) { + ERR_print_errors(bio_err); + DSA_free(dsakey); + goto end; + } + assert(private); + if (outformat == FORMAT_ASN1) + i = i2d_DSAPrivateKey_bio(out, dsakey); + else + i = PEM_write_bio_DSAPrivateKey(out, dsakey, NULL, NULL, 0, NULL, + NULL); + DSA_free(dsakey); + } + if (need_rand) + app_RAND_write_file(NULL); + ret = 0; + end: + BN_GENCB_free(cb); + BIO_free(in); + BIO_free_all(out); + DSA_free(dsa); + release_engine(e); + return (ret); +} + +static int dsa_cb(int p, int n, BN_GENCB *cb) +{ + char c = '*'; + + if (p == 0) + c = '.'; + if (p == 1) + c = '+'; + if (p == 2) + c = '*'; + if (p == 3) + c = '\n'; + BIO_write(BN_GENCB_get_arg(cb), &c, 1); + (void)BIO_flush(BN_GENCB_get_arg(cb)); +# ifdef GENCB_TEST + if (stop_keygen_flag) + return 0; +# endif + return 1; +} +#endif diff --git a/apps/ec.c b/apps/ec.c new file mode 100644 index 000000000..3255d624c --- /dev/null +++ b/apps/ec.c @@ -0,0 +1,295 @@ +/* + * Copyright 2002-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#ifdef OPENSSL_NO_EC +NON_EMPTY_TRANSLATION_UNIT +#else + +# include +# include +# include +# include "apps.h" +# include +# include +# include +# include + +static OPT_PAIR conv_forms[] = { + {"compressed", POINT_CONVERSION_COMPRESSED}, + {"uncompressed", POINT_CONVERSION_UNCOMPRESSED}, + {"hybrid", POINT_CONVERSION_HYBRID}, + {NULL} +}; + +static OPT_PAIR param_enc[] = { + {"named_curve", OPENSSL_EC_NAMED_CURVE}, + {"explicit", 0}, + {NULL} +}; + +typedef enum OPTION_choice { + OPT_ERR = -1, OPT_EOF = 0, OPT_HELP, + OPT_INFORM, OPT_OUTFORM, OPT_ENGINE, OPT_IN, OPT_OUT, + OPT_NOOUT, OPT_TEXT, OPT_PARAM_OUT, OPT_PUBIN, OPT_PUBOUT, + OPT_PASSIN, OPT_PASSOUT, OPT_PARAM_ENC, OPT_CONV_FORM, OPT_CIPHER, + OPT_NO_PUBLIC, OPT_CHECK, OPT_CONFIG +} OPTION_CHOICE; + +OPTIONS ec_options[] = { + {"help", OPT_HELP, '-', "Display this summary"}, + {"in", OPT_IN, 's', "Input file"}, + {"inform", OPT_INFORM, 'f', "Input format - DER or PEM"}, + {"out", OPT_OUT, '>', "Output file"}, + {"outform", OPT_OUTFORM, 'F', "Output format - DER or PEM"}, + {"noout", OPT_NOOUT, '-', "Don't print key out"}, + {"text", OPT_TEXT, '-', "Print the key"}, + {"param_out", OPT_PARAM_OUT, '-', "Print the elliptic curve parameters"}, + {"pubin", OPT_PUBIN, '-', "Expect a public key in input file"}, + {"pubout", OPT_PUBOUT, '-', "Output public key, not private"}, + {"no_public", OPT_NO_PUBLIC, '-', "exclude public key from private key"}, + {"check", OPT_CHECK, '-', "check key consistency"}, + {"passin", OPT_PASSIN, 's', "Input file pass phrase source"}, + {"passout", OPT_PASSOUT, 's', "Output file pass phrase source"}, + {"param_enc", OPT_PARAM_ENC, 's', + "Specifies the way the ec parameters are encoded"}, + {"conv_form", OPT_CONV_FORM, 's', "Specifies the point conversion form "}, + {"", OPT_CIPHER, '-', "Any supported cipher"}, +# ifndef OPENSSL_NO_ENGINE + {"engine", OPT_ENGINE, 's', "Use engine, possibly a hardware device"}, + {"config", OPT_CONFIG, 's', "A config file"}, +# endif + {NULL} +}; + +int ec_main(int argc, char **argv) +{ + BIO *in = NULL, *out = NULL; + ENGINE *e = NULL; + EC_KEY *eckey = NULL; + const EC_GROUP *group; + const EVP_CIPHER *enc = NULL; + point_conversion_form_t form = POINT_CONVERSION_UNCOMPRESSED; + char *infile = NULL, *outfile = NULL, *prog; + char *passin = NULL, *passout = NULL, *passinarg = NULL, *passoutarg = NULL; + OPTION_CHOICE o; + int asn1_flag = OPENSSL_EC_NAMED_CURVE, new_form = 0, new_asn1_flag = 0; + int informat = FORMAT_PEM, outformat = FORMAT_PEM, text = 0, noout = 0; + int pubin = 0, pubout = 0, param_out = 0, i, ret = 1, private = 0; + int no_public = 0, check = 0; + CONF *conf = NULL; + char *configfile = default_config_file; + + prog = opt_init(argc, argv, ec_options); + while ((o = opt_next()) != OPT_EOF) { + switch (o) { + case OPT_EOF: + case OPT_ERR: + opthelp: + BIO_printf(bio_err, "%s: Use -help for summary.\n", prog); + goto end; + case OPT_HELP: + opt_help(ec_options); + ret = 0; + goto end; + case OPT_INFORM: + if (!opt_format(opt_arg(), OPT_FMT_ANY, &informat)) + goto opthelp; + break; + case OPT_IN: + infile = opt_arg(); + break; + case OPT_OUTFORM: + if (!opt_format(opt_arg(), OPT_FMT_PEMDER, &outformat)) + goto opthelp; + break; + case OPT_OUT: + outfile = opt_arg(); + break; + case OPT_NOOUT: + noout = 1; + break; + case OPT_TEXT: + text = 1; + break; + case OPT_PARAM_OUT: + param_out = 1; + break; + case OPT_PUBIN: + pubin = 1; + break; + case OPT_PUBOUT: + pubout = 1; + break; + case OPT_PASSIN: + passinarg = opt_arg(); + break; + case OPT_PASSOUT: + passoutarg = opt_arg(); + break; + case OPT_ENGINE: + e = setup_engine(opt_arg(), 0); + break; + case OPT_CIPHER: + if (!opt_cipher(opt_unknown(), &enc)) + goto opthelp; + break; + case OPT_CONV_FORM: + if (!opt_pair(opt_arg(), conv_forms, &i)) + goto opthelp; + new_form = 1; + form = i; + break; + case OPT_PARAM_ENC: + if (!opt_pair(opt_arg(), param_enc, &i)) + goto opthelp; + new_asn1_flag = 1; + asn1_flag = i; + break; + case OPT_NO_PUBLIC: + no_public = 1; + break; + case OPT_CHECK: + check = 1; + break; + case OPT_CONFIG: + configfile = opt_arg(); + break; + } + } + argc = opt_num_rest(); + if (argc != 0) + goto opthelp; + + BIO_printf(bio_err, "Using configuration from %s\n", configfile); + + if ((conf = app_load_config(configfile)) == NULL) + goto end; + if (configfile != default_config_file && !app_load_modules(conf)) + goto end; + + private = param_out || pubin || pubout ? 0 : 1; + if (text && !pubin) + private = 1; + + if (!app_passwd(passinarg, passoutarg, &passin, &passout)) { + BIO_printf(bio_err, "Error getting passwords\n"); + goto end; + } + + if (informat != FORMAT_ENGINE) { + in = bio_open_default(infile, 'r', informat); + if (in == NULL) + goto end; + } + + BIO_printf(bio_err, "read EC key\n"); + if (informat == FORMAT_ASN1) { + if (pubin) + eckey = d2i_EC_PUBKEY_bio(in, NULL); + else + eckey = d2i_ECPrivateKey_bio(in, NULL); + } else if (informat == FORMAT_ENGINE) { + EVP_PKEY *pkey; + if (pubin) + pkey = load_pubkey(infile, informat , 1, passin, e, "Public Key"); + else + pkey = load_key(infile, informat, 1, passin, e, "Private Key"); + if (pkey != NULL) { + eckey = EVP_PKEY_get1_EC_KEY(pkey); + EVP_PKEY_free(pkey); + } + } else { + if (pubin) + eckey = PEM_read_bio_EC_PUBKEY(in, NULL, NULL, NULL); + else + eckey = PEM_read_bio_ECPrivateKey(in, NULL, NULL, passin); + } + if (eckey == NULL) { + BIO_printf(bio_err, "unable to load Key\n"); + ERR_print_errors(bio_err); + goto end; + } + + out = bio_open_owner(outfile, outformat, private); + if (out == NULL) + goto end; + + group = EC_KEY_get0_group(eckey); + + if (new_form) + EC_KEY_set_conv_form(eckey, form); + + if (new_asn1_flag) + EC_KEY_set_asn1_flag(eckey, asn1_flag); + + if (no_public) + EC_KEY_set_enc_flags(eckey, EC_PKEY_NO_PUBKEY); + + if (text) { + assert(pubin || private); + if (!EC_KEY_print(out, eckey, 0)) { + perror(outfile); + ERR_print_errors(bio_err); + goto end; + } + } + + if (check) { + if (EC_KEY_check_key(eckey) == 1) { + BIO_printf(bio_err, "EC Key valid.\n"); + } else { + BIO_printf(bio_err, "EC Key Invalid!\n"); + ERR_print_errors(bio_err); + } + } + + if (noout) { + ret = 0; + goto end; + } + + BIO_printf(bio_err, "writing EC key\n"); + if (outformat == FORMAT_ASN1) { + if (param_out) + i = i2d_ECPKParameters_bio(out, group); + else if (pubin || pubout) + i = i2d_EC_PUBKEY_bio(out, eckey); + else { + assert(private); + i = i2d_ECPrivateKey_bio(out, eckey); + } + } else { + if (param_out) + i = PEM_write_bio_ECPKParameters(out, group); + else if (pubin || pubout) + i = PEM_write_bio_EC_PUBKEY(out, eckey); + else { + assert(private); + i = PEM_write_bio_ECPrivateKey(out, eckey, enc, + NULL, 0, NULL, passout); + } + } + + if (!i) { + BIO_printf(bio_err, "unable to write private key\n"); + ERR_print_errors(bio_err); + } else + ret = 0; + end: + BIO_free(in); + BIO_free_all(out); + EC_KEY_free(eckey); + release_engine(e); + OPENSSL_free(passin); + OPENSSL_free(passout); + NCONF_free(conf); + return (ret); +} +#endif diff --git a/apps/ecparam.c b/apps/ecparam.c new file mode 100644 index 000000000..5d71485aa --- /dev/null +++ b/apps/ecparam.c @@ -0,0 +1,482 @@ +/* + * Copyright 2002-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +/* ==================================================================== + * Copyright 2002 Sun Microsystems, Inc. ALL RIGHTS RESERVED. + * + * Portions of the attached software ("Contribution") are developed by + * SUN MICROSYSTEMS, INC., and are contributed to the OpenSSL project. + * + * The Contribution is licensed pursuant to the OpenSSL open source + * license provided above. + * + * The elliptic curve binary polynomial software is originally written by + * Sheueling Chang Shantz and Douglas Stebila of Sun Microsystems Laboratories. + * + */ + +#include +#ifdef OPENSSL_NO_EC +NON_EMPTY_TRANSLATION_UNIT +#else + +# include +# include +# include +# include +# include "apps.h" +# include +# include +# include +# include +# include +# include + +typedef enum OPTION_choice { + OPT_ERR = -1, OPT_EOF = 0, OPT_HELP, + OPT_INFORM, OPT_OUTFORM, OPT_IN, OPT_OUT, OPT_TEXT, OPT_C, + OPT_CHECK, OPT_LIST_CURVES, OPT_NO_SEED, OPT_NOOUT, OPT_NAME, + OPT_CONV_FORM, OPT_PARAM_ENC, OPT_GENKEY, OPT_RAND, OPT_ENGINE, + OPT_CONFIG +} OPTION_CHOICE; + +OPTIONS ecparam_options[] = { + {"help", OPT_HELP, '-', "Display this summary"}, + {"inform", OPT_INFORM, 'F', "Input format - default PEM (DER or PEM)"}, + {"outform", OPT_OUTFORM, 'F', "Output format - default PEM"}, + {"in", OPT_IN, '<', "Input file - default stdin"}, + {"out", OPT_OUT, '>', "Output file - default stdout"}, + {"text", OPT_TEXT, '-', "Print the ec parameters in text form"}, + {"C", OPT_C, '-', "Print a 'C' function creating the parameters"}, + {"check", OPT_CHECK, '-', "Validate the ec parameters"}, + {"list_curves", OPT_LIST_CURVES, '-', + "Prints a list of all curve 'short names'"}, + {"no_seed", OPT_NO_SEED, '-', + "If 'explicit' parameters are chosen do not use the seed"}, + {"noout", OPT_NOOUT, '-', "Do not print the ec parameter"}, + {"name", OPT_NAME, 's', + "Use the ec parameters with specified 'short name'"}, + {"conv_form", OPT_CONV_FORM, 's', "Specifies the point conversion form "}, + {"param_enc", OPT_PARAM_ENC, 's', + "Specifies the way the ec parameters are encoded"}, + {"genkey", OPT_GENKEY, '-', "Generate ec key"}, + {"rand", OPT_RAND, 's', "Files to use for random number input"}, +# ifndef OPENSSL_NO_ENGINE + {"engine", OPT_ENGINE, 's', "Use engine, possibly a hardware device"}, + {"config", OPT_CONFIG, 's', "A config file"}, +# endif + {NULL} +}; + +static OPT_PAIR forms[] = { + {"compressed", POINT_CONVERSION_COMPRESSED}, + {"uncompressed", POINT_CONVERSION_UNCOMPRESSED}, + {"hybrid", POINT_CONVERSION_HYBRID}, + {NULL} +}; + +static OPT_PAIR encodings[] = { + {"named_curve", OPENSSL_EC_NAMED_CURVE}, + {"explicit", 0}, + {NULL} +}; + +int ecparam_main(int argc, char **argv) +{ + ENGINE *e = NULL; + BIGNUM *ec_gen = NULL, *ec_order = NULL, *ec_cofactor = NULL; + BIGNUM *ec_p = NULL, *ec_a = NULL, *ec_b = NULL; + BIO *in = NULL, *out = NULL; + EC_GROUP *group = NULL; + point_conversion_form_t form = POINT_CONVERSION_UNCOMPRESSED; + char *curve_name = NULL, *inrand = NULL; + char *infile = NULL, *outfile = NULL, *prog; + unsigned char *buffer = NULL; + OPTION_CHOICE o; + int asn1_flag = OPENSSL_EC_NAMED_CURVE, new_asn1_flag = 0; + int informat = FORMAT_PEM, outformat = FORMAT_PEM, noout = 0, C = 0; + int ret = 1, private = 0; + int list_curves = 0, no_seed = 0, check = 0, new_form = 0; + int text = 0, i, need_rand = 0, genkey = 0; + CONF *conf = NULL; + char *configfile = default_config_file; + + prog = opt_init(argc, argv, ecparam_options); + while ((o = opt_next()) != OPT_EOF) { + switch (o) { + case OPT_EOF: + case OPT_ERR: + opthelp: + BIO_printf(bio_err, "%s: Use -help for summary.\n", prog); + goto end; + case OPT_HELP: + opt_help(ecparam_options); + ret = 0; + goto end; + case OPT_INFORM: + if (!opt_format(opt_arg(), OPT_FMT_PEMDER, &informat)) + goto opthelp; + break; + case OPT_IN: + infile = opt_arg(); + break; + case OPT_OUTFORM: + if (!opt_format(opt_arg(), OPT_FMT_PEMDER, &outformat)) + goto opthelp; + break; + case OPT_OUT: + outfile = opt_arg(); + break; + case OPT_TEXT: + text = 1; + break; + case OPT_C: + C = 1; + break; + case OPT_CHECK: + check = 1; + break; + case OPT_LIST_CURVES: + list_curves = 1; + break; + case OPT_NO_SEED: + no_seed = 1; + break; + case OPT_NOOUT: + noout = 1; + break; + case OPT_NAME: + curve_name = opt_arg(); + break; + case OPT_CONV_FORM: + if (!opt_pair(opt_arg(), forms, &new_form)) + goto opthelp; + form = new_form; + new_form = 1; + break; + case OPT_PARAM_ENC: + if (!opt_pair(opt_arg(), encodings, &asn1_flag)) + goto opthelp; + new_asn1_flag = 1; + break; + case OPT_GENKEY: + genkey = need_rand = 1; + break; + case OPT_RAND: + inrand = opt_arg(); + need_rand = 1; + break; + case OPT_ENGINE: + e = setup_engine(opt_arg(), 0); + break; + case OPT_CONFIG: + configfile = opt_arg(); + break; + } + } + argc = opt_num_rest(); + if (argc != 0) + goto opthelp; + + if (e) + BIO_printf(bio_err, "ecparam using engine configuration from %s\n", + configfile); + + if ((conf = app_load_config(configfile)) == NULL) + goto end; + if (configfile != default_config_file && !app_load_modules(conf)) + goto end; + + private = genkey ? 1 : 0; + + in = bio_open_default(infile, 'r', informat); + if (in == NULL) + goto end; + out = bio_open_owner(outfile, outformat, private); + if (out == NULL) + goto end; + + if (list_curves) { + EC_builtin_curve *curves = NULL; + size_t crv_len = EC_get_builtin_curves(NULL, 0); + size_t n; + + curves = app_malloc((int)sizeof(*curves) * crv_len, "list curves"); + if (!EC_get_builtin_curves(curves, crv_len)) { + OPENSSL_free(curves); + goto end; + } + + for (n = 0; n < crv_len; n++) { + const char *comment; + const char *sname; + comment = curves[n].comment; + sname = OBJ_nid2sn(curves[n].nid); + if (comment == NULL) + comment = "CURVE DESCRIPTION NOT AVAILABLE"; + if (sname == NULL) + sname = ""; + + BIO_printf(out, " %-10s: ", sname); + BIO_printf(out, "%s\n", comment); + } + + OPENSSL_free(curves); + ret = 0; + goto end; + } + + if (curve_name != NULL) { + int nid; + + /* + * workaround for the SECG curve names secp192r1 and secp256r1 (which + * are the same as the curves prime192v1 and prime256v1 defined in + * X9.62) + */ + if (strcmp(curve_name, "secp192r1") == 0) { + BIO_printf(bio_err, "using curve name prime192v1 " + "instead of secp192r1\n"); + nid = NID_X9_62_prime192v1; + } else if (strcmp(curve_name, "secp256r1") == 0) { + BIO_printf(bio_err, "using curve name prime256v1 " + "instead of secp256r1\n"); + nid = NID_X9_62_prime256v1; + } else + nid = OBJ_sn2nid(curve_name); + + if (nid == 0) + nid = EC_curve_nist2nid(curve_name); + + if (nid == 0) { + BIO_printf(bio_err, "unknown curve name (%s)\n", curve_name); + goto end; + } + + group = EC_GROUP_new_by_curve_name(nid); + if (group == NULL) { + BIO_printf(bio_err, "unable to create curve (%s)\n", curve_name); + goto end; + } + EC_GROUP_set_asn1_flag(group, asn1_flag); + EC_GROUP_set_point_conversion_form(group, form); + } else if (informat == FORMAT_ASN1) + group = d2i_ECPKParameters_bio(in, NULL); + else + group = PEM_read_bio_ECPKParameters(in, NULL, NULL, NULL); + if (group == NULL) { + BIO_printf(bio_err, "unable to load elliptic curve parameters\n"); + ERR_print_errors(bio_err); + goto end; + } + + if (new_form) + EC_GROUP_set_point_conversion_form(group, form); + + if (new_asn1_flag) + EC_GROUP_set_asn1_flag(group, asn1_flag); + + if (no_seed) { + EC_GROUP_set_seed(group, NULL, 0); + } + + if (text) { + if (!ECPKParameters_print(out, group, 0)) + goto end; + } + + if (check) { + BIO_printf(bio_err, "checking elliptic curve parameters: "); + if (!EC_GROUP_check(group, NULL)) { + BIO_printf(bio_err, "failed\n"); + ERR_print_errors(bio_err); + goto end; + } + BIO_printf(bio_err, "ok\n"); + + } + + if (C) { + size_t buf_len = 0, tmp_len = 0; + const EC_POINT *point; + int is_prime, len = 0; + const EC_METHOD *meth = EC_GROUP_method_of(group); + + if ((ec_p = BN_new()) == NULL + || (ec_a = BN_new()) == NULL + || (ec_b = BN_new()) == NULL + || (ec_gen = BN_new()) == NULL + || (ec_order = BN_new()) == NULL + || (ec_cofactor = BN_new()) == NULL) { + perror("Can't allocate BN"); + goto end; + } + + is_prime = (EC_METHOD_get_field_type(meth) == NID_X9_62_prime_field); + if (!is_prime) { + BIO_printf(bio_err, "Can only handle X9.62 prime fields\n"); + goto end; + } + + if (!EC_GROUP_get_curve_GFp(group, ec_p, ec_a, ec_b, NULL)) + goto end; + + if ((point = EC_GROUP_get0_generator(group)) == NULL) + goto end; + if (!EC_POINT_point2bn(group, point, + EC_GROUP_get_point_conversion_form(group), + ec_gen, NULL)) + goto end; + if (!EC_GROUP_get_order(group, ec_order, NULL)) + goto end; + if (!EC_GROUP_get_cofactor(group, ec_cofactor, NULL)) + goto end; + + if (!ec_p || !ec_a || !ec_b || !ec_gen || !ec_order || !ec_cofactor) + goto end; + + len = BN_num_bits(ec_order); + + if ((tmp_len = (size_t)BN_num_bytes(ec_p)) > buf_len) + buf_len = tmp_len; + if ((tmp_len = (size_t)BN_num_bytes(ec_a)) > buf_len) + buf_len = tmp_len; + if ((tmp_len = (size_t)BN_num_bytes(ec_b)) > buf_len) + buf_len = tmp_len; + if ((tmp_len = (size_t)BN_num_bytes(ec_gen)) > buf_len) + buf_len = tmp_len; + if ((tmp_len = (size_t)BN_num_bytes(ec_order)) > buf_len) + buf_len = tmp_len; + if ((tmp_len = (size_t)BN_num_bytes(ec_cofactor)) > buf_len) + buf_len = tmp_len; + + buffer = app_malloc(buf_len, "BN buffer"); + + BIO_printf(out, "EC_GROUP *get_ec_group_%d(void)\n{\n", len); + print_bignum_var(out, ec_p, "ec_p", len, buffer); + print_bignum_var(out, ec_a, "ec_a", len, buffer); + print_bignum_var(out, ec_b, "ec_b", len, buffer); + print_bignum_var(out, ec_gen, "ec_gen", len, buffer); + print_bignum_var(out, ec_order, "ec_order", len, buffer); + print_bignum_var(out, ec_cofactor, "ec_cofactor", len, buffer); + BIO_printf(out, " int ok = 0;\n" + " EC_GROUP *group = NULL;\n" + " EC_POINT *point = NULL;\n" + " BIGNUM *tmp_1 = NULL;\n" + " BIGNUM *tmp_2 = NULL;\n" + " BIGNUM *tmp_3 = NULL;\n" + "\n"); + + BIO_printf(out, " if ((tmp_1 = BN_bin2bn(ec_p_%d, sizeof (ec_p_%d), NULL)) == NULL)\n" + " goto err;\n", len, len); + BIO_printf(out, " if ((tmp_2 = BN_bin2bn(ec_a_%d, sizeof (ec_a_%d), NULL)) == NULL)\n" + " goto err;\n", len, len); + BIO_printf(out, " if ((tmp_3 = BN_bin2bn(ec_b_%d, sizeof (ec_b_%d), NULL)) == NULL)\n" + " goto err;\n", len, len); + BIO_printf(out, " if ((group = EC_GROUP_new_curve_GFp(tmp_1, tmp_2, tmp_3, NULL)) == NULL)\n" + " goto err;\n" + "\n"); + BIO_printf(out, " /* build generator */\n"); + BIO_printf(out, " if ((tmp_1 = BN_bin2bn(ec_gen_%d, sizeof (ec_gen_%d), tmp_1)) == NULL)\n" + " goto err;\n", len, len); + BIO_printf(out, " point = EC_POINT_bn2point(group, tmp_1, NULL, NULL);\n"); + BIO_printf(out, " if (point == NULL)\n" + " goto err;\n"); + BIO_printf(out, " if ((tmp_2 = BN_bin2bn(ec_order_%d, sizeof (ec_order_%d), tmp_2)) == NULL)\n" + " goto err;\n", len, len); + BIO_printf(out, " if ((tmp_3 = BN_bin2bn(ec_cofactor_%d, sizeof (ec_cofactor_%d), tmp_3)) == NULL)\n" + " goto err;\n", len, len); + BIO_printf(out, " if (!EC_GROUP_set_generator(group, point, tmp_2, tmp_3))\n" + " goto err;\n" + "ok = 1;" + "\n"); + BIO_printf(out, "err:\n" + " BN_free(tmp_1);\n" + " BN_free(tmp_2);\n" + " BN_free(tmp_3);\n" + " EC_POINT_free(point);\n" + " if (!ok) {\n" + " EC_GROUP_free(group);\n" + " return NULL;\n" + " }\n" + " return (group);\n" + "}\n"); + } + + if (!noout) { + if (outformat == FORMAT_ASN1) + i = i2d_ECPKParameters_bio(out, group); + else + i = PEM_write_bio_ECPKParameters(out, group); + if (!i) { + BIO_printf(bio_err, "unable to write elliptic " + "curve parameters\n"); + ERR_print_errors(bio_err); + goto end; + } + } + + if (need_rand) { + app_RAND_load_file(NULL, (inrand != NULL)); + if (inrand != NULL) + BIO_printf(bio_err, "%ld semi-random bytes loaded\n", + app_RAND_load_files(inrand)); + } + + if (genkey) { + EC_KEY *eckey = EC_KEY_new(); + + if (eckey == NULL) + goto end; + + assert(need_rand); + + if (EC_KEY_set_group(eckey, group) == 0) { + BIO_printf(bio_err, "unable to set group when generating key\n"); + EC_KEY_free(eckey); + ERR_print_errors(bio_err); + goto end; + } + + if (!EC_KEY_generate_key(eckey)) { + BIO_printf(bio_err, "unable to generate key\n"); + EC_KEY_free(eckey); + ERR_print_errors(bio_err); + goto end; + } + assert(private); + if (outformat == FORMAT_ASN1) + i = i2d_ECPrivateKey_bio(out, eckey); + else + i = PEM_write_bio_ECPrivateKey(out, eckey, NULL, + NULL, 0, NULL, NULL); + EC_KEY_free(eckey); + } + + if (need_rand) + app_RAND_write_file(NULL); + + ret = 0; + end: + BN_free(ec_p); + BN_free(ec_a); + BN_free(ec_b); + BN_free(ec_gen); + BN_free(ec_order); + BN_free(ec_cofactor); + OPENSSL_free(buffer); + EC_GROUP_free(group); + release_engine(e); + BIO_free(in); + BIO_free_all(out); + NCONF_free(conf); + return (ret); +} + +#endif diff --git a/apps/enc.c b/apps/enc.c new file mode 100644 index 000000000..989c57e8b --- /dev/null +++ b/apps/enc.c @@ -0,0 +1,618 @@ +/* + * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#include +#include +#include +#include "apps.h" +#include +#include +#include +#include +#include +#include +#include +#ifndef OPENSSL_NO_COMP +# include +#endif +#include + +#undef SIZE +#undef BSIZE +#define SIZE (512) +#define BSIZE (8*1024) + +static int set_hex(char *in, unsigned char *out, int size); +static void show_ciphers(const OBJ_NAME *name, void *bio_); + +typedef enum OPTION_choice { + OPT_ERR = -1, OPT_EOF = 0, OPT_HELP, + OPT_LIST, + OPT_E, OPT_IN, OPT_OUT, OPT_PASS, OPT_ENGINE, OPT_D, OPT_P, OPT_V, + OPT_NOPAD, OPT_SALT, OPT_NOSALT, OPT_DEBUG, OPT_UPPER_P, OPT_UPPER_A, + OPT_A, OPT_Z, OPT_BUFSIZE, OPT_K, OPT_KFILE, OPT_UPPER_K, OPT_NONE, + OPT_UPPER_S, OPT_IV, OPT_MD, OPT_CIPHER, OPT_CONFIG +} OPTION_CHOICE; + +OPTIONS enc_options[] = { + {"help", OPT_HELP, '-', "Display this summary"}, + {"ciphers", OPT_LIST, '-', "List ciphers"}, + {"in", OPT_IN, '<', "Input file"}, + {"out", OPT_OUT, '>', "Output file"}, + {"pass", OPT_PASS, 's', "Passphrase source"}, + {"e", OPT_E, '-', "Encrypt"}, + {"d", OPT_D, '-', "Decrypt"}, + {"p", OPT_P, '-', "Print the iv/key"}, + {"P", OPT_UPPER_P, '-', "Print the iv/key and exit"}, + {"v", OPT_V, '-', "Verbose output"}, + {"nopad", OPT_NOPAD, '-', "Disable standard block padding"}, + {"salt", OPT_SALT, '-', "Use salt in the KDF (default)"}, + {"nosalt", OPT_NOSALT, '-', "Do not use salt in the KDF"}, + {"debug", OPT_DEBUG, '-', "Print debug info"}, + {"a", OPT_A, '-', "Base64 encode/decode, depending on encryption flag"}, + {"base64", OPT_A, '-', "Same as option -a"}, + {"A", OPT_UPPER_A, '-', + "Used with -[base64|a] to specify base64 buffer as a single line"}, + {"bufsize", OPT_BUFSIZE, 's', "Buffer size"}, + {"k", OPT_K, 's', "Passphrase"}, + {"kfile", OPT_KFILE, '<', "Read passphrase from file"}, + {"K", OPT_UPPER_K, 's', "Raw key, in hex"}, + {"S", OPT_UPPER_S, 's', "Salt, in hex"}, + {"iv", OPT_IV, 's', "IV in hex"}, + {"md", OPT_MD, 's', "Use specified digest to create a key from the passphrase"}, + {"none", OPT_NONE, '-', "Don't encrypt"}, + {"", OPT_CIPHER, '-', "Any supported cipher"}, +#ifdef ZLIB + {"z", OPT_Z, '-', "Use zlib as the 'encryption'"}, +#endif +#ifndef OPENSSL_NO_ENGINE + {"engine", OPT_ENGINE, 's', "Use engine, possibly a hardware device"}, + {"config", OPT_CONFIG, 's', "A config file"}, +#endif + {NULL} +}; + +int enc_main(int argc, char **argv) +{ + static char buf[128]; + static const char magic[] = "Salted__"; + ENGINE *e = NULL; + BIO *in = NULL, *out = NULL, *b64 = NULL, *benc = NULL, *rbio = + NULL, *wbio = NULL; + EVP_CIPHER_CTX *ctx = NULL; + const EVP_CIPHER *cipher = NULL, *c; + const EVP_MD *dgst = NULL; + char *hkey = NULL, *hiv = NULL, *hsalt = NULL, *p; + char *infile = NULL, *outfile = NULL, *prog; + char *str = NULL, *passarg = NULL, *pass = NULL, *strbuf = NULL; + char mbuf[sizeof magic - 1]; + OPTION_CHOICE o; + int bsize = BSIZE, verbose = 0, debug = 0, olb64 = 0, nosalt = 0; + int enc = 1, printkey = 0, i, k; + int base64 = 0, informat = FORMAT_BINARY, outformat = FORMAT_BINARY; + int ret = 1, inl, nopad = 0; + unsigned char key[EVP_MAX_KEY_LENGTH], iv[EVP_MAX_IV_LENGTH]; + unsigned char *buff = NULL, salt[PKCS5_SALT_LEN]; + long n; +#ifdef ZLIB + int do_zlib = 0; + BIO *bzl = NULL; +#endif + CONF *conf = NULL; + char *configfile = default_config_file; + + /* first check the program name */ + prog = opt_progname(argv[0]); + if (strcmp(prog, "base64") == 0) + base64 = 1; +#ifdef ZLIB + else if (strcmp(prog, "zlib") == 0) + do_zlib = 1; +#endif + else { + cipher = EVP_get_cipherbyname(prog); + if (cipher == NULL && strcmp(prog, "enc") != 0) { + BIO_printf(bio_err, "%s is not a known cipher\n", prog); + goto end; + } + } + + prog = opt_init(argc, argv, enc_options); + while ((o = opt_next()) != OPT_EOF) { + switch (o) { + case OPT_EOF: + case OPT_ERR: + opthelp: + BIO_printf(bio_err, "%s: Use -help for summary.\n", prog); + goto end; + case OPT_HELP: + opt_help(enc_options); + ret = 0; + goto end; + case OPT_LIST: + BIO_printf(bio_err, "Supported ciphers:\n"); + OBJ_NAME_do_all_sorted(OBJ_NAME_TYPE_CIPHER_METH, + show_ciphers, bio_err); + BIO_printf(bio_err, "\n"); + goto end; + case OPT_E: + enc = 1; + break; + case OPT_IN: + infile = opt_arg(); + break; + case OPT_OUT: + outfile = opt_arg(); + break; + case OPT_PASS: + passarg = opt_arg(); + break; + case OPT_ENGINE: + e = setup_engine(opt_arg(), 0); + break; + case OPT_D: + enc = 0; + break; + case OPT_P: + printkey = 1; + break; + case OPT_V: + verbose = 1; + break; + case OPT_NOPAD: + nopad = 1; + break; + case OPT_SALT: + nosalt = 0; + break; + case OPT_NOSALT: + nosalt = 1; + break; + case OPT_DEBUG: + debug = 1; + break; + case OPT_UPPER_P: + printkey = 2; + break; + case OPT_UPPER_A: + olb64 = 1; + break; + case OPT_A: + base64 = 1; + break; + case OPT_Z: +#ifdef ZLIB + do_zlib = 1; +#endif + break; + case OPT_BUFSIZE: + p = opt_arg(); + i = (int)strlen(p) - 1; + k = i >= 1 && p[i] == 'k'; + if (k) + p[i] = '\0'; + if (!opt_long(opt_arg(), &n) + || n < 0 || (k && n >= LONG_MAX / 1024)) + goto opthelp; + if (k) + n *= 1024; + bsize = (int)n; + break; + case OPT_K: + str = opt_arg(); + break; + case OPT_KFILE: + in = bio_open_default(opt_arg(), 'r', FORMAT_TEXT); + if (in == NULL) + goto opthelp; + i = BIO_gets(in, buf, sizeof buf); + BIO_free(in); + in = NULL; + if (i <= 0) { + BIO_printf(bio_err, + "%s Can't read key from %s\n", prog, opt_arg()); + goto opthelp; + } + while (--i > 0 && (buf[i] == '\r' || buf[i] == '\n')) + buf[i] = '\0'; + if (i <= 0) { + BIO_printf(bio_err, "%s: zero length password\n", prog); + goto opthelp; + } + str = buf; + break; + case OPT_UPPER_K: + hkey = opt_arg(); + break; + case OPT_UPPER_S: + hsalt = opt_arg(); + break; + case OPT_IV: + hiv = opt_arg(); + break; + case OPT_MD: + if (!opt_md(opt_arg(), &dgst)) + goto opthelp; + break; + case OPT_CIPHER: + if (!opt_cipher(opt_unknown(), &c)) + goto opthelp; + cipher = c; + break; + case OPT_NONE: + cipher = NULL; + break; + case OPT_CONFIG: + configfile = opt_arg(); + break; + } + } + + //BIO_printf(bio_err, "Using configuration from %s\n", configfile); + + if ((conf = app_load_config(configfile)) == NULL) + goto end; + if (configfile != default_config_file && !app_load_modules(conf)) + goto end; + + if (cipher && EVP_CIPHER_flags(cipher) & EVP_CIPH_FLAG_AEAD_CIPHER) { + BIO_printf(bio_err, "%s: AEAD ciphers not supported\n", prog); + goto end; + } + + if (cipher && (EVP_CIPHER_mode(cipher) == EVP_CIPH_XTS_MODE)) { + BIO_printf(bio_err, "%s XTS ciphers not supported\n", prog); + goto end; + } + + if (dgst == NULL) + dgst = EVP_get_digestbynid(NID_sha256); + + /* It must be large enough for a base64 encoded line */ + if (base64 && bsize < 80) + bsize = 80; + if (verbose) + BIO_printf(bio_err, "bufsize=%d\n", bsize); + +#ifdef ZLIB + if (!do_zlib) +#endif + if (base64) { + if (enc) + outformat = FORMAT_BASE64; + else + informat = FORMAT_BASE64; + } + + strbuf = app_malloc(SIZE, "strbuf"); + buff = app_malloc(EVP_ENCODE_LENGTH(bsize), "evp buffer"); + + if (infile == NULL) { + unbuffer(stdin); + in = dup_bio_in(informat); + } else + in = bio_open_default(infile, 'r', informat); + if (in == NULL) + goto end; + + if (!str && passarg) { + if (!app_passwd(passarg, NULL, &pass, NULL)) { + BIO_printf(bio_err, "Error getting password\n"); + goto end; + } + str = pass; + } + + if ((str == NULL) && (cipher != NULL) && (hkey == NULL)) { + if (1) { +#ifndef OPENSSL_NO_UI + for (;;) { + char prompt[200]; + + BIO_snprintf(prompt, sizeof prompt, "enter %s %s password:", + OBJ_nid2ln(EVP_CIPHER_nid(cipher)), + (enc) ? "encryption" : "decryption"); + strbuf[0] = '\0'; + i = EVP_read_pw_string((char *)strbuf, SIZE, prompt, enc); + if (i == 0) { + if (strbuf[0] == '\0') { + ret = 1; + goto end; + } + str = strbuf; + break; + } + if (i < 0) { + BIO_printf(bio_err, "bad password read\n"); + goto end; + } + } + } else { +#endif + BIO_printf(bio_err, "password required\n"); + goto end; + } + } + + out = bio_open_default(outfile, 'w', outformat); + if (out == NULL) + goto end; + + if (debug) { + BIO_set_callback(in, BIO_debug_callback); + BIO_set_callback(out, BIO_debug_callback); + BIO_set_callback_arg(in, (char *)bio_err); + BIO_set_callback_arg(out, (char *)bio_err); + } + + rbio = in; + wbio = out; + +#ifdef ZLIB + if (do_zlib) { + if ((bzl = BIO_new(BIO_f_zlib())) == NULL) + goto end; + if (debug) { + BIO_set_callback(bzl, BIO_debug_callback); + BIO_set_callback_arg(bzl, (char *)bio_err); + } + if (enc) + wbio = BIO_push(bzl, wbio); + else + rbio = BIO_push(bzl, rbio); + } +#endif + + if (base64) { + if ((b64 = BIO_new(BIO_f_base64())) == NULL) + goto end; + if (debug) { + BIO_set_callback(b64, BIO_debug_callback); + BIO_set_callback_arg(b64, (char *)bio_err); + } + if (olb64) + BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL); + if (enc) + wbio = BIO_push(b64, wbio); + else + rbio = BIO_push(b64, rbio); + } + + if (cipher != NULL) { + /* + * Note that str is NULL if a key was passed on the command line, so + * we get no salt in that case. Is this a bug? + */ + if (str != NULL) { + /* + * Salt handling: if encrypting generate a salt and write to + * output BIO. If decrypting read salt from input BIO. + */ + unsigned char *sptr; + size_t str_len = strlen(str); + + if (nosalt) + sptr = NULL; + else { + if (enc) { + if (hsalt) { + if (!set_hex(hsalt, salt, sizeof salt)) { + BIO_printf(bio_err, "invalid hex salt value\n"); + goto end; + } + } else if (RAND_bytes(salt, sizeof salt) <= 0) + goto end; + /* + * If -P option then don't bother writing + */ + if ((printkey != 2) + && (BIO_write(wbio, magic, + sizeof magic - 1) != sizeof magic - 1 + || BIO_write(wbio, + (char *)salt, + sizeof salt) != sizeof salt)) { + BIO_printf(bio_err, "error writing output file\n"); + goto end; + } + } else if (BIO_read(rbio, mbuf, sizeof mbuf) != sizeof mbuf + || BIO_read(rbio, + (unsigned char *)salt, + sizeof salt) != sizeof salt) { + BIO_printf(bio_err, "error reading input file\n"); + goto end; + } else if (memcmp(mbuf, magic, sizeof magic - 1)) { + BIO_printf(bio_err, "bad magic number\n"); + goto end; + } + + sptr = salt; + } + + if (!EVP_BytesToKey(cipher, dgst, sptr, + (unsigned char *)str, + str_len, 1, key, iv)) { + BIO_printf(bio_err, "EVP_BytesToKey failed\n"); + goto end; + } + /* + * zero the complete buffer or the string passed from the command + * line bug picked up by Larry J. Hughes Jr. + */ + if (str == strbuf) + OPENSSL_cleanse(str, SIZE); + else + OPENSSL_cleanse(str, str_len); + } + if (hiv != NULL) { + int siz = EVP_CIPHER_iv_length(cipher); + if (siz == 0) { + BIO_printf(bio_err, "warning: iv not use by this cipher\n"); + } else if (!set_hex(hiv, iv, sizeof iv)) { + BIO_printf(bio_err, "invalid hex iv value\n"); + goto end; + } + } + if ((hiv == NULL) && (str == NULL) + && EVP_CIPHER_iv_length(cipher) != 0) { + /* + * No IV was explicitly set and no IV was generated during + * EVP_BytesToKey. Hence the IV is undefined, making correct + * decryption impossible. + */ + BIO_printf(bio_err, "iv undefined\n"); + goto end; + } + if ((hkey != NULL) && !set_hex(hkey, key, EVP_CIPHER_key_length(cipher))) { + BIO_printf(bio_err, "invalid hex key value\n"); + goto end; + } + + if ((benc = BIO_new(BIO_f_cipher())) == NULL) + goto end; + + /* + * Since we may be changing parameters work on the encryption context + * rather than calling BIO_set_cipher(). + */ + + BIO_get_cipher_ctx(benc, &ctx); + + if (!EVP_CipherInit_ex(ctx, cipher, e, NULL, NULL, enc)) { + BIO_printf(bio_err, "Error setting cipher %s\n", + EVP_CIPHER_name(cipher)); + ERR_print_errors(bio_err); + goto end; + } + + if (nopad) + EVP_CIPHER_CTX_set_padding(ctx, 0); + + if (!EVP_CipherInit_ex(ctx, NULL, NULL, key, iv, enc)) { + BIO_printf(bio_err, "Error setting cipher %s\n", + EVP_CIPHER_name(cipher)); + ERR_print_errors(bio_err); + goto end; + } + + if (debug) { + BIO_set_callback(benc, BIO_debug_callback); + BIO_set_callback_arg(benc, (char *)bio_err); + } + + if (printkey) { + if (!nosalt) { + printf("salt="); + for (i = 0; i < (int)sizeof(salt); i++) + printf("%02X", salt[i]); + printf("\n"); + } + if (EVP_CIPHER_key_length(cipher) > 0) { + printf("key="); + for (i = 0; i < EVP_CIPHER_key_length(cipher); i++) + printf("%02X", key[i]); + printf("\n"); + } + if (EVP_CIPHER_iv_length(cipher) > 0) { + printf("iv ="); + for (i = 0; i < EVP_CIPHER_iv_length(cipher); i++) + printf("%02X", iv[i]); + printf("\n"); + } + if (printkey == 2) { + ret = 0; + goto end; + } + } + } + + /* Only encrypt/decrypt as we write the file */ + if (benc != NULL) + wbio = BIO_push(benc, wbio); + + for (;;) { + inl = BIO_read(rbio, (char *)buff, bsize); + if (inl <= 0) + break; + if (BIO_write(wbio, (char *)buff, inl) != inl) { + BIO_printf(bio_err, "error writing output file\n"); + goto end; + } + } + if (!BIO_flush(wbio)) { + BIO_printf(bio_err, "bad decrypt\n"); + goto end; + } + + ret = 0; + if (verbose) { + BIO_printf(bio_err, "bytes read :%8"PRIu64"\n", BIO_number_read(in)); + BIO_printf(bio_err, "bytes written:%8"PRIu64"\n", BIO_number_written(out)); + } + end: + ERR_print_errors(bio_err); + OPENSSL_free(strbuf); + OPENSSL_free(buff); + BIO_free(in); + BIO_free_all(out); + BIO_free(benc); + BIO_free(b64); +#ifdef ZLIB + BIO_free(bzl); +#endif + release_engine(e); + OPENSSL_free(pass); + NCONF_free(conf); + return (ret); +} + +static void show_ciphers(const OBJ_NAME *name, void *bio_) +{ + BIO *bio = bio_; + static int n; + + if (!islower((unsigned char)*name->name)) + return; + + BIO_printf(bio, "-%-25s", name->name); + if (++n == 3) { + BIO_printf(bio, "\n"); + n = 0; + } else + BIO_printf(bio, " "); +} + +static int set_hex(char *in, unsigned char *out, int size) +{ + int i, n; + unsigned char j; + + n = strlen(in); + if (n > (size * 2)) { + BIO_printf(bio_err, "hex string is too long\n"); + return (0); + } + memset(out, 0, size); + for (i = 0; i < n; i++) { + j = (unsigned char)*in; + *(in++) = '\0'; + if (j == 0) + break; + if (!isxdigit(j)) { + BIO_printf(bio_err, "non-hex digit\n"); + return (0); + } + j = (unsigned char)OPENSSL_hexchar2int(j); + if (i & 1) + out[i / 2] |= j; + else + out[i / 2] = (j << 4); + } + return (1); +} diff --git a/apps/engine.c b/apps/engine.c new file mode 100644 index 000000000..3264a2b20 --- /dev/null +++ b/apps/engine.c @@ -0,0 +1,465 @@ +/* + * Copyright 2000-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#ifdef OPENSSL_NO_ENGINE +NON_EMPTY_TRANSLATION_UNIT +#else + +# include "apps.h" +# include +# include +# include +# include +# include +# include + +typedef enum OPTION_choice { + OPT_ERR = -1, OPT_EOF = 0, OPT_HELP, + OPT_C, OPT_T, OPT_TT, OPT_PRE, OPT_POST, + OPT_V = 100, OPT_VV, OPT_VVV, OPT_VVVV, + OPT_CONFIG +} OPTION_CHOICE; + +OPTIONS engine_options[] = { + {OPT_HELP_STR, 1, '-', "Usage: %s [options] engine...\n"}, + {OPT_HELP_STR, 1, '-', + " engine... Engines to load\n"}, + {"help", OPT_HELP, '-', "Display this summary"}, + {"v", OPT_V, '-', "List 'control commands' For each specified engine"}, + {"vv", OPT_VV, '-', "Also display each command's description"}, + {"vvv", OPT_VVV, '-', "Also add the input flags for each command"}, + {"vvvv", OPT_VVVV, '-', "Also show internal input flags"}, + {"c", OPT_C, '-', "List the capabilities of specified engine"}, + {"t", OPT_T, '-', "Check that specified engine is available"}, + {"tt", OPT_TT, '-', "Display error trace for unavailable engines"}, + {"pre", OPT_PRE, 's', "Run command against the ENGINE before loading it"}, + {"post", OPT_POST, 's', "Run command against the ENGINE after loading it"}, + {"config", OPT_CONFIG, 's', "A config file"}, + {OPT_MORE_STR, OPT_EOF, 1, + "Commands are like \"SO_PATH:/lib/libdriver.so\""}, + {NULL} +}; + +static int append_buf(char **buf, int *size, const char *s) +{ + if (*buf == NULL) { + *size = 256; + *buf = app_malloc(*size, "engine buffer"); + **buf = '\0'; + } + + if (strlen(*buf) + strlen(s) >= (unsigned int)*size) { + char *tmp; + *size += 256; + tmp = OPENSSL_realloc(*buf, *size); + if (tmp == NULL) { + OPENSSL_free(*buf); + *buf = NULL; + return 0; + } + *buf = tmp; + } + + if (**buf != '\0') + OPENSSL_strlcat(*buf, ", ", *size); + OPENSSL_strlcat(*buf, s, *size); + + return 1; +} + +static int util_flags(BIO *out, unsigned int flags, const char *indent) +{ + int started = 0, err = 0; + /* Indent before displaying input flags */ + BIO_printf(out, "%s%s(input flags): ", indent, indent); + if (flags == 0) { + BIO_printf(out, "\n"); + return 1; + } + /* + * If the object is internal, mark it in a way that shows instead of + * having it part of all the other flags, even if it really is. + */ + if (flags & ENGINE_CMD_FLAG_INTERNAL) { + BIO_printf(out, "[Internal] "); + } + + if (flags & ENGINE_CMD_FLAG_NUMERIC) { + BIO_printf(out, "NUMERIC"); + started = 1; + } + /* + * Now we check that no combinations of the mutually exclusive NUMERIC, + * STRING, and NO_INPUT flags have been used. Future flags that can be + * OR'd together with these would need to added after these to preserve + * the testing logic. + */ + if (flags & ENGINE_CMD_FLAG_STRING) { + if (started) { + BIO_printf(out, "|"); + err = 1; + } + BIO_printf(out, "STRING"); + started = 1; + } + if (flags & ENGINE_CMD_FLAG_NO_INPUT) { + if (started) { + BIO_printf(out, "|"); + err = 1; + } + BIO_printf(out, "NO_INPUT"); + started = 1; + } + /* Check for unknown flags */ + flags = flags & ~ENGINE_CMD_FLAG_NUMERIC & + ~ENGINE_CMD_FLAG_STRING & + ~ENGINE_CMD_FLAG_NO_INPUT & ~ENGINE_CMD_FLAG_INTERNAL; + if (flags) { + if (started) + BIO_printf(out, "|"); + BIO_printf(out, "<0x%04X>", flags); + } + if (err) + BIO_printf(out, " "); + BIO_printf(out, "\n"); + return 1; +} + +static int util_verbose(ENGINE *e, int verbose, BIO *out, const char *indent) +{ + static const int line_wrap = 78; + int num; + int ret = 0; + char *name = NULL; + char *desc = NULL; + int flags; + int xpos = 0; + STACK_OF(OPENSSL_STRING) *cmds = NULL; + if (!ENGINE_ctrl(e, ENGINE_CTRL_HAS_CTRL_FUNCTION, 0, NULL, NULL) || + ((num = ENGINE_ctrl(e, ENGINE_CTRL_GET_FIRST_CMD_TYPE, + 0, NULL, NULL)) <= 0)) { + return 1; + } + + cmds = sk_OPENSSL_STRING_new_null(); + if (!cmds) + goto err; + + do { + int len; + /* Get the command input flags */ + if ((flags = ENGINE_ctrl(e, ENGINE_CTRL_GET_CMD_FLAGS, num, + NULL, NULL)) < 0) + goto err; + if (!(flags & ENGINE_CMD_FLAG_INTERNAL) || verbose >= 4) { + /* Get the command name */ + if ((len = ENGINE_ctrl(e, ENGINE_CTRL_GET_NAME_LEN_FROM_CMD, num, + NULL, NULL)) <= 0) + goto err; + name = app_malloc(len + 1, "name buffer"); + if (ENGINE_ctrl(e, ENGINE_CTRL_GET_NAME_FROM_CMD, num, name, + NULL) <= 0) + goto err; + /* Get the command description */ + if ((len = ENGINE_ctrl(e, ENGINE_CTRL_GET_DESC_LEN_FROM_CMD, num, + NULL, NULL)) < 0) + goto err; + if (len > 0) { + desc = app_malloc(len + 1, "description buffer"); + if (ENGINE_ctrl(e, ENGINE_CTRL_GET_DESC_FROM_CMD, num, desc, + NULL) <= 0) + goto err; + } + /* Now decide on the output */ + if (xpos == 0) + /* Do an indent */ + xpos = BIO_puts(out, indent); + else + /* Otherwise prepend a ", " */ + xpos += BIO_printf(out, ", "); + if (verbose == 1) { + /* + * We're just listing names, comma-delimited + */ + if ((xpos > (int)strlen(indent)) && + (xpos + (int)strlen(name) > line_wrap)) { + BIO_printf(out, "\n"); + xpos = BIO_puts(out, indent); + } + xpos += BIO_printf(out, "%s", name); + } else { + /* We're listing names plus descriptions */ + BIO_printf(out, "%s: %s\n", name, + (desc == NULL) ? "" : desc); + /* ... and sometimes input flags */ + if ((verbose >= 3) && !util_flags(out, flags, indent)) + goto err; + xpos = 0; + } + } + OPENSSL_free(name); + name = NULL; + OPENSSL_free(desc); + desc = NULL; + /* Move to the next command */ + num = ENGINE_ctrl(e, ENGINE_CTRL_GET_NEXT_CMD_TYPE, num, NULL, NULL); + } while (num > 0); + if (xpos > 0) + BIO_printf(out, "\n"); + ret = 1; + err: + sk_OPENSSL_STRING_free(cmds); + OPENSSL_free(name); + OPENSSL_free(desc); + return ret; +} + +static void util_do_cmds(ENGINE *e, STACK_OF(OPENSSL_STRING) *cmds, + BIO *out, const char *indent) +{ + int loop, res, num = sk_OPENSSL_STRING_num(cmds); + + if (num < 0) { + BIO_printf(out, "[Error]: internal stack error\n"); + return; + } + for (loop = 0; loop < num; loop++) { + char buf[256]; + const char *cmd, *arg; + cmd = sk_OPENSSL_STRING_value(cmds, loop); + res = 1; /* assume success */ + /* Check if this command has no ":arg" */ + if ((arg = strstr(cmd, ":")) == NULL) { + if (!ENGINE_ctrl_cmd_string(e, cmd, NULL, 0)) + res = 0; + } else { + if ((int)(arg - cmd) > 254) { + BIO_printf(out, "[Error]: command name too long\n"); + return; + } + memcpy(buf, cmd, (int)(arg - cmd)); + buf[arg - cmd] = '\0'; + arg++; /* Move past the ":" */ + /* Call the command with the argument */ + if (!ENGINE_ctrl_cmd_string(e, buf, arg, 0)) + res = 0; + } + if (res) + BIO_printf(out, "[Success]: %s\n", cmd); + else { + BIO_printf(out, "[Failure]: %s\n", cmd); + ERR_print_errors(out); + } + } +} + +int engine_main(int argc, char **argv) +{ + int ret = 1, i; + int verbose = 0, list_cap = 0, test_avail = 0, test_avail_noise = 0; + ENGINE *e; + STACK_OF(OPENSSL_CSTRING) *engines = sk_OPENSSL_CSTRING_new_null(); + STACK_OF(OPENSSL_STRING) *pre_cmds = sk_OPENSSL_STRING_new_null(); + STACK_OF(OPENSSL_STRING) *post_cmds = sk_OPENSSL_STRING_new_null(); + BIO *out; + const char *indent = " "; + OPTION_CHOICE o; + char *prog; + char *argv1; + CONF *conf = NULL; + char *configfile = default_config_file; + + out = dup_bio_out(FORMAT_TEXT); + if (engines == NULL || pre_cmds == NULL || post_cmds == NULL) + goto end; + + /* Remember the original command name, parse/skip any leading engine + * names, and then setup to parse the rest of the line as flags. */ + prog = argv[0]; + while ((argv1 = argv[1]) != NULL && *argv1 != '-') { + sk_OPENSSL_CSTRING_push(engines, argv1); + argc--; + argv++; + } + argv[0] = prog; + opt_init(argc, argv, engine_options); + + while ((o = opt_next()) != OPT_EOF) { + switch (o) { + case OPT_EOF: + case OPT_ERR: + BIO_printf(bio_err, "%s: Use -help for summary.\n", prog); + goto end; + case OPT_HELP: + opt_help(engine_options); + ret = 0; + goto end; + case OPT_VVVV: + case OPT_VVV: + case OPT_VV: + case OPT_V: + /* Convert to an integer from one to four. */ + i = (int)(o - OPT_V) + 1; + if (verbose < i) + verbose = i; + break; + case OPT_C: + list_cap = 1; + break; + case OPT_TT: + test_avail_noise++; + case OPT_T: + test_avail++; + break; + case OPT_PRE: + sk_OPENSSL_STRING_push(pre_cmds, opt_arg()); + break; + case OPT_POST: + sk_OPENSSL_STRING_push(post_cmds, opt_arg()); + break; + case OPT_CONFIG: + configfile = opt_arg(); + break; + } + } + + /* Allow any trailing parameters as engine names. */ + argc = opt_num_rest(); + argv = opt_rest(); + for ( ; *argv; argv++) { + if (**argv == '-') { + BIO_printf(bio_err, "%s: Cannot mix flags and engine names.\n", + prog); + BIO_printf(bio_err, "%s: Use -help for summary.\n", prog); + goto end; + } + sk_OPENSSL_CSTRING_push(engines, *argv); + } + + BIO_printf(bio_err, "Using configuration from %s\n", configfile); + + if (sk_OPENSSL_CSTRING_num(engines) == 0) { + for (e = ENGINE_get_first(); e != NULL; e = ENGINE_get_next(e)) { + sk_OPENSSL_CSTRING_push(engines, ENGINE_get_id(e)); + } + } + + ret = 0; + for (i = 0; i < sk_OPENSSL_CSTRING_num(engines); i++) { + const char *id = sk_OPENSSL_CSTRING_value(engines, i); + if ((e = ENGINE_by_id(id)) != NULL) { + const char *name = ENGINE_get_name(e); + /* + * Do "id" first, then "name". Easier to auto-parse. + */ + BIO_printf(out, "(%s) %s\n", id, name); + util_do_cmds(e, pre_cmds, out, indent); + if (strcmp(ENGINE_get_id(e), id) != 0) { + BIO_printf(out, "Loaded: (%s) %s\n", + ENGINE_get_id(e), ENGINE_get_name(e)); + } + if (list_cap) { + int cap_size = 256; + char *cap_buf = NULL; + int k, n; + const int *nids; + ENGINE_CIPHERS_PTR fn_c; + ENGINE_DIGESTS_PTR fn_d; + ENGINE_PKEY_METHS_PTR fn_pk; + +#if 0 + if ((conf = app_load_config(configfile)) == NULL) + goto end; + if (configfile != default_config_file && !app_load_modules(conf)) + goto end; +#endif + + if (ENGINE_get_RSA(e) != NULL + && !append_buf(&cap_buf, &cap_size, "RSA")) + goto end; + if (ENGINE_get_EC(e) != NULL + && !append_buf(&cap_buf, &cap_size, "EC")) + goto end; + if (ENGINE_get_DSA(e) != NULL + && !append_buf(&cap_buf, &cap_size, "DSA")) + goto end; + if (ENGINE_get_DH(e) != NULL + && !append_buf(&cap_buf, &cap_size, "DH")) + goto end; + if (ENGINE_get_RAND(e) != NULL + && !append_buf(&cap_buf, &cap_size, "RAND")) + goto end; + + fn_c = ENGINE_get_ciphers(e); + if (!fn_c) + goto skip_ciphers; + n = fn_c(e, NULL, &nids, 0); + for (k = 0; k < n; ++k) + if (!append_buf(&cap_buf, &cap_size, OBJ_nid2sn(nids[k]))) + goto end; + + skip_ciphers: + fn_d = ENGINE_get_digests(e); + if (!fn_d) + goto skip_digests; + n = fn_d(e, NULL, &nids, 0); + for (k = 0; k < n; ++k) + if (!append_buf(&cap_buf, &cap_size, OBJ_nid2sn(nids[k]))) + goto end; + + skip_digests: + fn_pk = ENGINE_get_pkey_meths(e); + if (!fn_pk) + goto skip_pmeths; + n = fn_pk(e, NULL, &nids, 0); + for (k = 0; k < n; ++k) + if (!append_buf(&cap_buf, &cap_size, OBJ_nid2sn(nids[k]))) + goto end; + skip_pmeths: + if (cap_buf && (*cap_buf != '\0')) + BIO_printf(out, " [%s]\n", cap_buf); + + OPENSSL_free(cap_buf); + } + if (test_avail) { + BIO_printf(out, "%s", indent); + if (ENGINE_init(e)) { + BIO_printf(out, "[ available ]\n"); + util_do_cmds(e, post_cmds, out, indent); + ENGINE_finish(e); + } else { + BIO_printf(out, "[ unavailable ]\n"); + if (test_avail_noise) + ERR_print_errors_fp(stdout); + ERR_clear_error(); + } + } + if ((verbose > 0) && !util_verbose(e, verbose, out, indent)) + goto end; + ENGINE_free(e); + } else { + ERR_print_errors(bio_err); + /* because exit codes above 127 have special meaning on Unix */ + if (++ret > 127) + ret = 127; + } + } + + end: + + ERR_print_errors(bio_err); + sk_OPENSSL_CSTRING_free(engines); + sk_OPENSSL_STRING_free(pre_cmds); + sk_OPENSSL_STRING_free(post_cmds); + BIO_free_all(out); + NCONF_free(conf); + return (ret); +} +#endif diff --git a/apps/errstr.c b/apps/errstr.c new file mode 100644 index 000000000..5fda799d1 --- /dev/null +++ b/apps/errstr.c @@ -0,0 +1,67 @@ +/* + * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#include +#include +#include "apps.h" +#include +#include +#include +#include + +typedef enum OPTION_choice { + OPT_ERR = -1, OPT_EOF = 0, OPT_HELP +} OPTION_CHOICE; + +OPTIONS errstr_options[] = { + {OPT_HELP_STR, 1, '-', "Usage: %s [options] errnum...\n"}, + {OPT_HELP_STR, 1, '-', " errnum Error number\n"}, + {"help", OPT_HELP, '-', "Display this summary"}, + {NULL} +}; + +int errstr_main(int argc, char **argv) +{ + OPTION_CHOICE o; + char buf[256], *prog; + int ret = 1; + unsigned long l; + + prog = opt_init(argc, argv, errstr_options); + while ((o = opt_next()) != OPT_EOF) { + switch (o) { + case OPT_EOF: + case OPT_ERR: + BIO_printf(bio_err, "%s: Use -help for summary.\n", prog); + goto end; + case OPT_HELP: + opt_help(errstr_options); + ret = 0; + goto end; + } + } + + ret = 0; + for (argv = opt_rest(); *argv; argv++) { + if (sscanf(*argv, "%lx", &l) == 0) + ret++; + else { + /* We're not really an SSL application so this won't auto-init, but + * we're still interested in SSL error strings + */ + OPENSSL_init_ssl(OPENSSL_INIT_LOAD_SSL_STRINGS + | OPENSSL_INIT_LOAD_CRYPTO_STRINGS, NULL); + ERR_error_string_n(l, buf, sizeof buf); + BIO_printf(bio_out, "%s\n", buf); + } + } + end: + return (ret); +} diff --git a/apps/gendsa.c b/apps/gendsa.c new file mode 100644 index 000000000..bdef022cf --- /dev/null +++ b/apps/gendsa.c @@ -0,0 +1,147 @@ +/* + * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#ifdef OPENSSL_NO_DSA +NON_EMPTY_TRANSLATION_UNIT +#else + +# include +# include +# include +# include +# include "apps.h" +# include +# include +# include +# include +# include +# include + +typedef enum OPTION_choice { + OPT_ERR = -1, OPT_EOF = 0, OPT_HELP, + OPT_OUT, OPT_PASSOUT, OPT_ENGINE, OPT_RAND, OPT_CIPHER +} OPTION_CHOICE; + +OPTIONS gendsa_options[] = { + {OPT_HELP_STR, 1, '-', "Usage: %s [args] dsaparam-file\n"}, + {OPT_HELP_STR, 1, '-', "Valid options are:\n"}, + {"help", OPT_HELP, '-', "Display this summary"}, + {"out", OPT_OUT, '>', "Output the key to the specified file"}, + {"passout", OPT_PASSOUT, 's', "Output file pass phrase source"}, + {"rand", OPT_RAND, 's', + "Load the file(s) into the random number generator"}, + {"", OPT_CIPHER, '-', "Encrypt the output with any supported cipher"}, +# ifndef OPENSSL_NO_ENGINE + {"engine", OPT_ENGINE, 's', "Use engine, possibly a hardware device"}, +# endif + {NULL} +}; + +int gendsa_main(int argc, char **argv) +{ + ENGINE *e = NULL; + BIO *out = NULL, *in = NULL; + DSA *dsa = NULL; + const EVP_CIPHER *enc = NULL; + char *inrand = NULL, *dsaparams = NULL; + char *outfile = NULL, *passoutarg = NULL, *passout = NULL, *prog; + OPTION_CHOICE o; + int ret = 1, private = 0; + const BIGNUM *p = NULL; + + prog = opt_init(argc, argv, gendsa_options); + while ((o = opt_next()) != OPT_EOF) { + switch (o) { + case OPT_EOF: + case OPT_ERR: + opthelp: + BIO_printf(bio_err, "%s: Use -help for summary.\n", prog); + goto end; + case OPT_HELP: + ret = 0; + opt_help(gendsa_options); + goto end; + case OPT_OUT: + outfile = opt_arg(); + break; + case OPT_PASSOUT: + passoutarg = opt_arg(); + break; + case OPT_ENGINE: + e = setup_engine(opt_arg(), 0); + break; + case OPT_RAND: + inrand = opt_arg(); + break; + case OPT_CIPHER: + if (!opt_cipher(opt_unknown(), &enc)) + goto end; + break; + } + } + argc = opt_num_rest(); + argv = opt_rest(); + private = 1; + + if (argc != 1) + goto opthelp; + dsaparams = *argv; + + if (!app_passwd(NULL, passoutarg, NULL, &passout)) { + BIO_printf(bio_err, "Error getting password\n"); + goto end; + } + + in = bio_open_default(dsaparams, 'r', FORMAT_PEM); + if (in == NULL) + goto end2; + + if ((dsa = PEM_read_bio_DSAparams(in, NULL, NULL, NULL)) == NULL) { + BIO_printf(bio_err, "unable to load DSA parameter file\n"); + goto end; + } + BIO_free(in); + in = NULL; + + out = bio_open_owner(outfile, FORMAT_PEM, private); + if (out == NULL) + goto end2; + + if (!app_RAND_load_file(NULL, 1) && inrand == NULL) { + BIO_printf(bio_err, + "warning, not much extra random data, consider using the -rand option\n"); + } + if (inrand != NULL) + BIO_printf(bio_err, "%ld semi-random bytes loaded\n", + app_RAND_load_files(inrand)); + + DSA_get0_pqg(dsa, &p, NULL, NULL); + BIO_printf(bio_err, "Generating DSA key, %d bits\n", BN_num_bits(p)); + if (!DSA_generate_key(dsa)) + goto end; + + app_RAND_write_file(NULL); + + assert(private); + if (!PEM_write_bio_DSAPrivateKey(out, dsa, enc, NULL, 0, NULL, passout)) + goto end; + ret = 0; + end: + if (ret != 0) + ERR_print_errors(bio_err); + end2: + BIO_free(in); + BIO_free_all(out); + DSA_free(dsa); + release_engine(e); + OPENSSL_free(passout); + return (ret); +} +#endif diff --git a/apps/genpkey.c b/apps/genpkey.c new file mode 100644 index 000000000..2b1ea85d0 --- /dev/null +++ b/apps/genpkey.c @@ -0,0 +1,329 @@ +/* + * Copyright 2006-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#include +#include "apps.h" +#include +#include +#include +#ifndef OPENSSL_NO_ENGINE +# include +#endif + +static int init_keygen_file(EVP_PKEY_CTX **pctx, const char *file, ENGINE *e); +static int genpkey_cb(EVP_PKEY_CTX *ctx); + +typedef enum OPTION_choice { + OPT_ERR = -1, OPT_EOF = 0, OPT_HELP, + OPT_ENGINE, OPT_OUTFORM, OPT_OUT, OPT_PASS, OPT_PARAMFILE, + OPT_ALGORITHM, OPT_PKEYOPT, OPT_GENPARAM, OPT_TEXT, OPT_CIPHER, + OPT_CONFIG +} OPTION_CHOICE; + +OPTIONS genpkey_options[] = { + {"help", OPT_HELP, '-', "Display this summary"}, + {"out", OPT_OUT, '>', "Output file"}, + {"outform", OPT_OUTFORM, 'F', "output format (DER or PEM)"}, + {"pass", OPT_PASS, 's', "Output file pass phrase source"}, + {"paramfile", OPT_PARAMFILE, '<', "Parameters file"}, + {"algorithm", OPT_ALGORITHM, 's', "The public key algorithm"}, + {"pkeyopt", OPT_PKEYOPT, 's', + "Set the public key algorithm option as opt:value"}, + {"genparam", OPT_GENPARAM, '-', "Generate parameters, not key"}, + {"text", OPT_TEXT, '-', "Print the in text"}, + {"", OPT_CIPHER, '-', "Cipher to use to encrypt the key"}, +#ifndef OPENSSL_NO_ENGINE + {"engine", OPT_ENGINE, 's', "Use engine, possibly a hardware device"}, + {"config", OPT_CONFIG, 's', "A config file"}, +#endif + /* This is deliberately last. */ + {OPT_HELP_STR, 1, 1, + "Order of options may be important! See the documentation.\n"}, + {NULL} +}; + +int genpkey_main(int argc, char **argv) +{ + BIO *in = NULL, *out = NULL; + ENGINE *e = NULL; + EVP_PKEY *pkey = NULL; + EVP_PKEY_CTX *ctx = NULL; + char *outfile = NULL, *passarg = NULL, *pass = NULL, *prog; + const EVP_CIPHER *cipher = NULL; + OPTION_CHOICE o; + int outformat = FORMAT_PEM, text = 0, ret = 1, rv, do_param = 0; + int private = 0; + CONF *conf = NULL; + char *configfile = default_config_file; + + prog = opt_init(argc, argv, genpkey_options); + while ((o = opt_next()) != OPT_EOF) { + switch (o) { + case OPT_EOF: + case OPT_ERR: + opthelp: + BIO_printf(bio_err, "%s: Use -help for summary.\n", prog); + goto end; + case OPT_HELP: + ret = 0; + opt_help(genpkey_options); + goto end; + case OPT_OUTFORM: + if (!opt_format(opt_arg(), OPT_FMT_PEMDER, &outformat)) + goto opthelp; + break; + case OPT_OUT: + outfile = opt_arg(); + break; + case OPT_PASS: + passarg = opt_arg(); + break; + case OPT_ENGINE: + e = setup_engine(opt_arg(), 0); + break; + case OPT_PARAMFILE: + if (do_param == 1) + goto opthelp; + if (!init_keygen_file(&ctx, opt_arg(), e)) + goto end; + break; + case OPT_ALGORITHM: + if (!init_gen_str(&ctx, opt_arg(), e, do_param)) + goto end; + break; + case OPT_PKEYOPT: + if (ctx == NULL) { + BIO_printf(bio_err, "%s: No keytype specified.\n", prog); + goto opthelp; + } + if (pkey_ctrl_string(ctx, opt_arg()) <= 0) { + BIO_printf(bio_err, + "%s: Error setting %s parameter:\n", + prog, opt_arg()); + ERR_print_errors(bio_err); + goto end; + } + break; + case OPT_GENPARAM: + if (ctx != NULL) + goto opthelp; + do_param = 1; + break; + case OPT_TEXT: + text = 1; + break; + case OPT_CONFIG: + configfile = opt_arg(); + break; + case OPT_CIPHER: /* this should be the last */ + if (!opt_cipher(opt_unknown(), &cipher) + || do_param == 1) + goto opthelp; + } + } + argc = opt_num_rest(); + if (argc != 0) + goto opthelp; + + if (e) + BIO_printf(bio_err, "Using configuration from %s\n", configfile); + if ((conf = app_load_config(configfile)) == NULL) + goto end; + if (configfile != default_config_file && !app_load_modules(conf)) + goto end; + + private = do_param ? 0 : 1; + + if (ctx == NULL) + goto opthelp; + + if (!app_passwd(passarg, NULL, &pass, NULL)) { + BIO_puts(bio_err, "Error getting password\n"); + goto end; + } + + out = bio_open_owner(outfile, outformat, private); + if (out == NULL) + goto end; + + EVP_PKEY_CTX_set_cb(ctx, genpkey_cb); + EVP_PKEY_CTX_set_app_data(ctx, bio_err); + + if (do_param) { + if (EVP_PKEY_paramgen(ctx, &pkey) <= 0) { + BIO_puts(bio_err, "Error generating parameters\n"); + ERR_print_errors(bio_err); + goto end; + } + } else { + if (EVP_PKEY_keygen(ctx, &pkey) <= 0) { + BIO_puts(bio_err, "Error generating key\n"); + ERR_print_errors(bio_err); + goto end; + } + } + + if (do_param) + rv = PEM_write_bio_Parameters(out, pkey); + else if (outformat == FORMAT_PEM) { + assert(private); + rv = PEM_write_bio_PrivateKey(out, pkey, cipher, NULL, 0, NULL, pass); + } else if (outformat == FORMAT_ASN1) { + assert(private); + rv = i2d_PrivateKey_bio(out, pkey); + } else { + BIO_printf(bio_err, "Bad format specified for key\n"); + goto end; + } + + if (rv <= 0) { + BIO_puts(bio_err, "Error writing key\n"); + ERR_print_errors(bio_err); + } + + if (text) { + if (do_param) + rv = EVP_PKEY_print_params(out, pkey, 0, NULL); + else + rv = EVP_PKEY_print_private(out, pkey, 0, NULL); + + if (rv <= 0) { + BIO_puts(bio_err, "Error printing key\n"); + ERR_print_errors(bio_err); + } + } + + ret = 0; + + end: + EVP_PKEY_free(pkey); + EVP_PKEY_CTX_free(ctx); + BIO_free_all(out); + BIO_free(in); + release_engine(e); + OPENSSL_free(pass); + NCONF_free(conf); + return ret; +} + +static int init_keygen_file(EVP_PKEY_CTX **pctx, const char *file, ENGINE *e) +{ + BIO *pbio; + EVP_PKEY *pkey = NULL; + EVP_PKEY_CTX *ctx = NULL; + if (*pctx) { + BIO_puts(bio_err, "Parameters already set!\n"); + return 0; + } + + pbio = BIO_new_file(file, "r"); + if (!pbio) { + BIO_printf(bio_err, "Can't open parameter file %s\n", file); + return 0; + } + + pkey = PEM_read_bio_Parameters(pbio, NULL); + BIO_free(pbio); + + if (!pkey) { + BIO_printf(bio_err, "Error reading parameter file %s\n", file); + return 0; + } + + ctx = EVP_PKEY_CTX_new(pkey, e); + if (ctx == NULL) + goto err; + if (EVP_PKEY_keygen_init(ctx) <= 0) + goto err; + EVP_PKEY_free(pkey); + *pctx = ctx; + return 1; + + err: + BIO_puts(bio_err, "Error initializing context\n"); + ERR_print_errors(bio_err); + EVP_PKEY_CTX_free(ctx); + EVP_PKEY_free(pkey); + return 0; + +} + +int init_gen_str(EVP_PKEY_CTX **pctx, + const char *algname, ENGINE *e, int do_param) +{ + EVP_PKEY_CTX *ctx = NULL; + const EVP_PKEY_ASN1_METHOD *ameth; + ENGINE *tmpeng = NULL; + int pkey_id; + + if (*pctx) { + BIO_puts(bio_err, "Algorithm already set!\n"); + return 0; + } + + ameth = EVP_PKEY_asn1_find_str(&tmpeng, algname, -1); + +#ifndef OPENSSL_NO_ENGINE + if (!ameth && e) + ameth = ENGINE_get_pkey_asn1_meth_str(e, algname, -1); +#endif + + if (!ameth) { + BIO_printf(bio_err, "Algorithm %s not found\n", algname); + return 0; + } + + ERR_clear_error(); + + EVP_PKEY_asn1_get0_info(&pkey_id, NULL, NULL, NULL, NULL, ameth); +#ifndef OPENSSL_NO_ENGINE + ENGINE_finish(tmpeng); +#endif + ctx = EVP_PKEY_CTX_new_id(pkey_id, e); + + if (!ctx) + goto err; + if (do_param) { + if (EVP_PKEY_paramgen_init(ctx) <= 0) + goto err; + } else { + if (EVP_PKEY_keygen_init(ctx) <= 0) + goto err; + } + + *pctx = ctx; + return 1; + + err: + BIO_printf(bio_err, "Error initializing %s context\n", algname); + ERR_print_errors(bio_err); + EVP_PKEY_CTX_free(ctx); + return 0; + +} + +static int genpkey_cb(EVP_PKEY_CTX *ctx) +{ + char c = '*'; + BIO *b = EVP_PKEY_CTX_get_app_data(ctx); + int p; + p = EVP_PKEY_CTX_get_keygen_info(ctx, 0); + if (p == 0) + c = '.'; + if (p == 1) + c = '+'; + if (p == 2) + c = '*'; + if (p == 3) + c = '\n'; + BIO_write(b, &c, 1); + (void)BIO_flush(b); + return 1; +} diff --git a/apps/genrsa.c b/apps/genrsa.c new file mode 100644 index 000000000..16ca2287b --- /dev/null +++ b/apps/genrsa.c @@ -0,0 +1,207 @@ +/* + * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#ifdef OPENSSL_NO_RSA +NON_EMPTY_TRANSLATION_UNIT +#else + +# include +# include +# include +# include +# include "apps.h" +# include +# include +# include +# include +# include +# include +# include +# include + +# define DEFBITS 2048 + +static int genrsa_cb(int p, int n, BN_GENCB *cb); + +typedef enum OPTION_choice { + OPT_ERR = -1, OPT_EOF = 0, OPT_HELP, + OPT_3, OPT_F4, OPT_ENGINE, + OPT_OUT, OPT_RAND, OPT_PASSOUT, OPT_CIPHER, + OPT_CONFIG +} OPTION_CHOICE; + +OPTIONS genrsa_options[] = { + {"help", OPT_HELP, '-', "Display this summary"}, + {"3", OPT_3, '-', "Use 3 for the E value"}, + {"F4", OPT_F4, '-', "Use F4 (0x10001) for the E value"}, + {"f4", OPT_F4, '-', "Use F4 (0x10001) for the E value"}, + {"out", OPT_OUT, 's', "Output the key to specified file"}, + {"rand", OPT_RAND, 's', + "Load the file(s) into the random number generator"}, + {"passout", OPT_PASSOUT, 's', "Output file pass phrase source"}, + {"", OPT_CIPHER, '-', "Encrypt the output with any supported cipher"}, +# ifndef OPENSSL_NO_ENGINE + {"engine", OPT_ENGINE, 's', "Use engine, possibly a hardware device"}, + {"config", OPT_CONFIG, 's', "A config file"}, +# endif + {NULL} +}; + +int genrsa_main(int argc, char **argv) +{ + BN_GENCB *cb = BN_GENCB_new(); + PW_CB_DATA cb_data; + ENGINE *eng = NULL; + BIGNUM *bn = BN_new(); + BIO *out = NULL; + const BIGNUM *e; + RSA *rsa = NULL; + const EVP_CIPHER *enc = NULL; + int ret = 1, num = DEFBITS, private = 0; + unsigned long f4 = RSA_F4; + char *outfile = NULL, *passoutarg = NULL, *passout = NULL; + char *inrand = NULL, *prog, *hexe, *dece; + OPTION_CHOICE o; + CONF *conf = NULL; + char *configfile = default_config_file; + + if (bn == NULL || cb == NULL) + goto end; + + BN_GENCB_set(cb, genrsa_cb, bio_err); + + prog = opt_init(argc, argv, genrsa_options); + while ((o = opt_next()) != OPT_EOF) { + switch (o) { + case OPT_EOF: + case OPT_ERR: + BIO_printf(bio_err, "%s: Use -help for summary.\n", prog); + goto end; + case OPT_HELP: + ret = 0; + opt_help(genrsa_options); + goto end; + case OPT_3: + f4 = 3; + break; + case OPT_F4: + f4 = RSA_F4; + break; + case OPT_OUT: + outfile = opt_arg(); + break; + case OPT_ENGINE: + eng = setup_engine(opt_arg(), 0); + break; + case OPT_RAND: + inrand = opt_arg(); + break; + case OPT_PASSOUT: + passoutarg = opt_arg(); + break; + case OPT_CIPHER: + if (!opt_cipher(opt_unknown(), &enc)) + goto end; + break; + case OPT_CONFIG: + configfile = opt_arg(); + break; + } + } + argc = opt_num_rest(); + argv = opt_rest(); + private = 1; + + if (argv[0] && (!opt_int(argv[0], &num) || num <= 0)) + goto end; + + BIO_printf(bio_err, "Using configuration from %s\n", configfile); + + if ((conf = app_load_config(configfile)) == NULL) + goto end; + if (configfile != default_config_file && !app_load_modules(conf)) + goto end; + + if (!app_passwd(NULL, passoutarg, NULL, &passout)) { + BIO_printf(bio_err, "Error getting password\n"); + goto end; + } + + out = bio_open_owner(outfile, FORMAT_PEM, private); + if (out == NULL) + goto end; + + if (!app_RAND_load_file(NULL, 1) && inrand == NULL + && !RAND_status()) { + BIO_printf(bio_err, + "warning, not much extra random data, consider using the -rand option\n"); + } + if (inrand != NULL) + BIO_printf(bio_err, "%ld semi-random bytes loaded\n", + app_RAND_load_files(inrand)); + + BIO_printf(bio_err, "Generating RSA private key, %d bit long modulus\n", + num); + rsa = eng ? RSA_new_method(eng) : RSA_new(); + if (rsa == NULL) + goto end; + + if (!BN_set_word(bn, f4) || !RSA_generate_key_ex(rsa, num, bn, cb)) + goto end; + + app_RAND_write_file(NULL); + + RSA_get0_key(rsa, NULL, &e, NULL); + hexe = BN_bn2hex(e); + dece = BN_bn2dec(e); + if (hexe && dece) { + BIO_printf(bio_err, "e is %s (0x%s)\n", dece, hexe); + } + OPENSSL_free(hexe); + OPENSSL_free(dece); + cb_data.password = passout; + cb_data.prompt_info = outfile; + assert(private); + if (!PEM_write_bio_RSAPrivateKey(out, rsa, enc, NULL, 0, + (pem_password_cb *)password_callback, + &cb_data)) + goto end; + + ret = 0; + end: + BN_free(bn); + BN_GENCB_free(cb); + RSA_free(rsa); + BIO_free_all(out); + release_engine(eng); + OPENSSL_free(passout); + NCONF_free(conf); + if (ret != 0) + ERR_print_errors(bio_err); + return (ret); +} + +static int genrsa_cb(int p, int n, BN_GENCB *cb) +{ + char c = '*'; + + if (p == 0) + c = '.'; + if (p == 1) + c = '+'; + if (p == 2) + c = '*'; + if (p == 3) + c = '\n'; + BIO_write(BN_GENCB_get_arg(cb), &c, 1); + (void)BIO_flush(BN_GENCB_get_arg(cb)); + return 1; +} +#endif diff --git a/apps/gensm9.c b/apps/gensm9.c new file mode 100644 index 000000000..4bf5f6fca --- /dev/null +++ b/apps/gensm9.c @@ -0,0 +1,81 @@ +/* ==================================================================== + * Copyright (c) 2014 - 2017 The GmSSL Project. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the GmSSL Project. + * (http://gmssl.org/)" + * + * 4. The name "GmSSL Project" must not be used to endorse or promote + * products derived from this software without prior written + * permission. For written permission, please contact + * guanzhi1980@gmail.com. + * + * 5. Products derived from this software may not be called "GmSSL" + * nor may "GmSSL" appear in their names without prior written + * permission of the GmSSL Project. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the GmSSL Project + * (http://gmssl.org/)" + * + * THIS SOFTWARE IS PROVIDED BY THE GmSSL PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE GmSSL PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + */ + +#include +#ifdef OPENSSL_NO_SM9 +NON_EMPTY_TRANSLATION_UNIT +#else + +# include +# include +# include +# include +# include +# include +# include +# include +# include "apps.h" + +typedef enum OPTION_choice { + OPT_ERR = -1, + OPT_EOF = 0, + OPT_HELP +} OPTION_CHOICE; + +OPTIONS gensm9_options[] = { + {"help", OPT_HELP, '-', "Display this summary"}, + {NULL} +}; + +int gensm9_main(int argc, char **argv) +{ + printf("sm9 not implemented\n"); + return 0; +} +#endif diff --git a/apps/gmca/ca.cnf b/apps/gmca/ca.cnf new file mode 100644 index 000000000..ff9e22dce --- /dev/null +++ b/apps/gmca/ca.cnf @@ -0,0 +1,350 @@ +# +# OpenSSL example configuration file. +# This is mostly being used for generation of certificate requests. +# + +# This definition stops the following lines choking if HOME isn't +# defined. +HOME = . +RANDFILE = $ENV::HOME/.rnd + +# Extra OBJECT IDENTIFIER info: +#oid_file = $ENV::HOME/.oid +oid_section = new_oids + +# To use this configuration file with the "-extfile" option of the +# "openssl x509" utility, name here the section containing the +# X.509v3 extensions to use: +# extensions = +# (Alternatively, use a configuration file that has only +# X.509v3 extensions in its main [= default] section.) + +[ new_oids ] + +# We can add new OIDs in here for use by 'ca', 'req' and 'ts'. +# Add a simple OID like this: +# testoid1=1.2.3.4 +# Or use config file substitution like this: +# testoid2=${testoid1}.5.6 + +# Policies used by the TSA examples. +tsa_policy1 = 1.2.3.4.1 +tsa_policy2 = 1.2.3.4.5.6 +tsa_policy3 = 1.2.3.4.5.7 + +#################################################################### +[ ca ] +default_ca = CA_default # The default ca section + +#################################################################### +[ CA_default ] + +dir = .ca # Where everything is kept +certs = $dir/certs # Where the issued certs are kept +crl_dir = $dir/crl # Where the issued crl are kept +database = $dir/index.txt # database index file. +#unique_subject = no # Set to 'no' to allow creation of + # several ctificates with same subject. +new_certs_dir = $dir/newcerts # default place for new certs. + +certificate = $dir/cacert.pem # The CA certificate +serial = $dir/serial # The current serial number +crlnumber = $dir/crlnumber # the current crl number + # must be commented out to leave a V1 CRL +crl = $dir/crl.pem # The current CRL +private_key = $dir/private/cakey.pem# The private key +RANDFILE = $dir/private/.rand # private random number file + +x509_extensions = usr_cert # The extentions to add to the cert + +# Comment out the following two lines for the "traditional" +# (and highly broken) format. +name_opt = ca_default # Subject Name options +cert_opt = ca_default # Certificate field options + +# Extension copying option: use with caution. +# copy_extensions = copy + +# Extensions to add to a CRL. Note: Netscape communicator chokes on V2 CRLs +# so this is commented out by default to leave a V1 CRL. +# crlnumber must also be commented out to leave a V1 CRL. +# crl_extensions = crl_ext + +default_days = 365 # how long to certify for +default_crl_days= 30 # how long before next CRL +default_md = default # use public key default MD +preserve = no # keep passed DN ordering + +# A few difference way of specifying how similar the request should look +# For type CA, the listed attributes must be the same, and the optional +# and supplied fields are just that :-) +policy = policy_match + +# For the CA policy +[ policy_match ] +countryName = match +stateOrProvinceName = match +organizationName = match +organizationalUnitName = optional +commonName = supplied +emailAddress = optional + +# For the 'anything' policy +# At this point in time, you must list all acceptable 'object' +# types. +[ policy_anything ] +countryName = optional +stateOrProvinceName = optional +localityName = optional +organizationName = optional +organizationalUnitName = optional +commonName = supplied +emailAddress = optional + +#################################################################### +[ req ] +default_bits = 2048 +default_keyfile = privkey.pem +distinguished_name = req_distinguished_name +attributes = req_attributes +x509_extensions = v3_ca # The extentions to add to the self signed cert + +# Passwords for private keys if not present they will be prompted for +# input_password = secret +# output_password = secret + +# This sets a mask for permitted string types. There are several options. +# default: PrintableString, T61String, BMPString. +# pkix : PrintableString, BMPString (PKIX recommendation before 2004) +# utf8only: only UTF8Strings (PKIX recommendation after 2004). +# nombstr : PrintableString, T61String (no BMPStrings or UTF8Strings). +# MASK:XXXX a literal mask value. +# WARNING: ancient versions of Netscape crash on BMPStrings or UTF8Strings. +string_mask = utf8only + +# req_extensions = v3_req # The extensions to add to a certificate request + +[ req_distinguished_name ] +countryName = Country Name (2 letter code) +countryName_default = AU +countryName_min = 2 +countryName_max = 2 + +stateOrProvinceName = State or Province Name (full name) +stateOrProvinceName_default = Some-State + +localityName = Locality Name (eg, city) + +0.organizationName = Organization Name (eg, company) +0.organizationName_default = Internet Widgits Pty Ltd + +# we can do this but it is not needed normally :-) +#1.organizationName = Second Organization Name (eg, company) +#1.organizationName_default = World Wide Web Pty Ltd + +organizationalUnitName = Organizational Unit Name (eg, section) +#organizationalUnitName_default = + +commonName = Common Name (e.g. server FQDN or YOUR name) +commonName_max = 64 + +emailAddress = Email Address +emailAddress_max = 64 + +# SET-ex3 = SET extension number 3 + +[ req_attributes ] +challengePassword = A challenge password +challengePassword_min = 4 +challengePassword_max = 20 + +unstructuredName = An optional company name + +[ usr_cert ] + +# These extensions are added when 'ca' signs a request. + +# This goes against PKIX guidelines but some CAs do it and some software +# requires this to avoid interpreting an end user certificate as a CA. + +basicConstraints=CA:FALSE + +# Here are some examples of the usage of nsCertType. If it is omitted +# the certificate can be used for anything *except* object signing. + +# This is OK for an SSL server. +# nsCertType = server + +# For an object signing certificate this would be used. +# nsCertType = objsign + +# For normal client use this is typical +# nsCertType = client, email + +# and for everything including object signing: +# nsCertType = client, email, objsign + +# This is typical in keyUsage for a client certificate. +# keyUsage = nonRepudiation, digitalSignature, keyEncipherment + +# This will be displayed in Netscape's comment listbox. +nsComment = "OpenSSL Generated Certificate" + +# PKIX recommendations harmless if included in all certificates. +subjectKeyIdentifier=hash +authorityKeyIdentifier=keyid,issuer + +# This stuff is for subjectAltName and issuerAltname. +# Import the email address. +# subjectAltName=email:copy +# An alternative to produce certificates that aren't +# deprecated according to PKIX. +# subjectAltName=email:move + +# Copy subject details +# issuerAltName=issuer:copy + +#nsCaRevocationUrl = http://www.domain.dom/ca-crl.pem +#nsBaseUrl +#nsRevocationUrl +#nsRenewalUrl +#nsCaPolicyUrl +#nsSslServerName + +# This is required for TSA certificates. +# extendedKeyUsage = critical,timeStamping + +[ v3_req ] + +# Extensions to add to a certificate request + +basicConstraints = CA:FALSE +keyUsage = nonRepudiation, digitalSignature, keyEncipherment + +[ v3_ca ] + + +# Extensions for a typical CA + + +# PKIX recommendation. + +subjectKeyIdentifier=hash + +authorityKeyIdentifier=keyid:always,issuer + +# This is what PKIX recommends but some broken software chokes on critical +# extensions. +#basicConstraints = critical,CA:true +# So we do this instead. +basicConstraints = CA:true + +# Key usage: this is typical for a CA certificate. However since it will +# prevent it being used as an test self-signed certificate it is best +# left out by default. +# keyUsage = cRLSign, keyCertSign + +# Some might want this also +# nsCertType = sslCA, emailCA + +# Include email address in subject alt name: another PKIX recommendation +# subjectAltName=email:copy +# Copy issuer details +# issuerAltName=issuer:copy + +# DER hex encoding of an extension: beware experts only! +# obj=DER:02:03 +# Where 'obj' is a standard or added object +# You can even override a supported extension: +# basicConstraints= critical, DER:30:03:01:01:FF + +[ crl_ext ] + +# CRL extensions. +# Only issuerAltName and authorityKeyIdentifier make any sense in a CRL. + +# issuerAltName=issuer:copy +authorityKeyIdentifier=keyid:always + +[ proxy_cert_ext ] +# These extensions should be added when creating a proxy certificate + +# This goes against PKIX guidelines but some CAs do it and some software +# requires this to avoid interpreting an end user certificate as a CA. + +basicConstraints=CA:FALSE + +# Here are some examples of the usage of nsCertType. If it is omitted +# the certificate can be used for anything *except* object signing. + +# This is OK for an SSL server. +# nsCertType = server + +# For an object signing certificate this would be used. +# nsCertType = objsign + +# For normal client use this is typical +# nsCertType = client, email + +# and for everything including object signing: +# nsCertType = client, email, objsign + +# This is typical in keyUsage for a client certificate. +# keyUsage = nonRepudiation, digitalSignature, keyEncipherment + +# This will be displayed in Netscape's comment listbox. +nsComment = "OpenSSL Generated Certificate" + +# PKIX recommendations harmless if included in all certificates. +subjectKeyIdentifier=hash +authorityKeyIdentifier=keyid,issuer + +# This stuff is for subjectAltName and issuerAltname. +# Import the email address. +# subjectAltName=email:copy +# An alternative to produce certificates that aren't +# deprecated according to PKIX. +# subjectAltName=email:move + +# Copy subject details +# issuerAltName=issuer:copy + +#nsCaRevocationUrl = http://www.domain.dom/ca-crl.pem +#nsBaseUrl +#nsRevocationUrl +#nsRenewalUrl +#nsCaPolicyUrl +#nsSslServerName + +# This really needs to be in place for it to be a proxy certificate. +proxyCertInfo=critical,language:id-ppl-anyLanguage,pathlen:3,policy:foo + +#################################################################### +[ tsa ] + +default_tsa = tsa_config1 # the default TSA section + +[ tsa_config1 ] + +# These are used by the TSA reply generation only. +dir = ./demoCA # TSA root directory +serial = $dir/tsaserial # The current serial number (mandatory) +crypto_device = builtin # OpenSSL engine to use for signing +signer_cert = $dir/tsacert.pem # The TSA signing certificate + # (optional) +certs = $dir/cacert.pem # Certificate chain to include in reply + # (optional) +signer_key = $dir/private/tsakey.pem # The TSA private key (optional) + +default_policy = tsa_policy1 # Policy if request did not specify it + # (optional) +other_policies = tsa_policy2, tsa_policy3 # acceptable policies (optional) +digests = md5, sha1 # Acceptable message digests (mandatory) +accuracy = secs:1, millisecs:500, microsecs:100 # (optional) +clock_precision_digits = 0 # number of digits after dot. (optional) +ordering = yes # Is ordering defined for timestamps? + # (optional, default: no) +tsa_name = yes # Must the TSA name be included in the reply? + # (optional, default: no) +ess_cert_id_chain = no # Must the ESS cert id chain be included? + # (optional, default: no) diff --git a/apps/gmca/gmca b/apps/gmca/gmca new file mode 100755 index 000000000..83299cc4a --- /dev/null +++ b/apps/gmca/gmca @@ -0,0 +1,522 @@ +#!/bin/bash +# +# Copyright (c) 2018 The GmSSL Project. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# +# 3. All advertising materials mentioning features or use of this +# software must display the following acknowledgment: +# "This product includes software developed by the GmSSL Project. +# (http://gmssl.org/)" +# +# 4. The name "GmSSL Project" must not be used to endorse or promote +# products derived from this software without prior written +# permission. For written permission, please contact +# guanzhi1980@gmail.com. +# +# 5. Products derived from this software may not be called "GmSSL" +# nor may "GmSSL" appear in their names without prior written +# permission of the GmSSL Project. +# +# 6. Redistributions of any form whatsoever must retain the following +# acknowledgment: +# "This product includes software developed by the GmSSL Project +# (http://gmssl.org/)" +# +# THIS SOFTWARE IS PROVIDED BY THE GmSSL PROJECT ``AS IS'' AND ANY +# EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE GmSSL PROJECT OR +# ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +# OF THE POSSIBILITY OF SUCH DAMAGE. + + +curve=sm2p256v1 +md=sm3 + +function set_std { + curve=secp256k1 + md=sha256 + ca_engine="" + user_engine="" +} + +# CA ajustable parameters +ca_dn_prefix="/C=CN/ST=BJ/L=BJ/O=PKU" +ca_rootcert_days=3650 +ca_usercert_days=365 +#ca_engine="skf" +#ca_engine="sdf" +#user_engine="skf" +#user_engine="sdf" +user_dn_prefix="$ca_dn_prefix/OU=Sign" +user_dn_enc_prefix="$ca_dn_prefix/OU=Enc" + +# The following values should not be changed. +ca_dn="$ca_dn_prefix/OU=CA/CN=PKUCA" +ca_dir=".ca" +ca_rootcert_file="$ca_dir/cacert.pem" +ca_private_dir="$ca_dir/private" +ca_rootcertkey_file="$ca_private_dir/cakey.pem" +ca_rand_file="$ca_private_dir/.rand" +ca_serial_file="$ca_dir/serial" +ca_csr_dir="$ca_dir/csr" +ca_signed_csr_dir="$ca_csr_dir/signed" +ca_rejected_csr_dir="$ca_csr_dir/rejected" +ca_cert_dir="$ca_dir/certs" +ca_newcert_dir="$ca_dir/newcerts" +ca_index_file="$ca_dir/index.txt" +ca_crl_dir="$ca_dir/crl" +ca_crl_file="$ca_crl_dir/crl.pem" +ca_crl_number_file="$ca_dir/crlnumber" +ca_crl_days=60 +user_key_dir="$ca_dir/keys" + +function setup { + if [[ -n $1 ]]; then + ca_dn=$1 + fi + + rm -fr $ca_dir + mkdir $ca_dir + mkdir $ca_private_dir + mkdir $ca_crl_dir + mkdir $ca_csr_dir + mkdir $ca_signed_csr_dir + mkdir $ca_rejected_csr_dir + mkdir $ca_cert_dir + mkdir $ca_newcert_dir + mkdir $user_key_dir + touch $ca_index_file + touch $ca_rand_file + echo 01 > $ca_serial_file + echo 01 > $ca_crl_number_file + + case $ca_engine in + "") + gmssl ecparam -genkey -name $curve -noout -out $ca_rootcertkey_file + key="-key $ca_rootcertkey_file" + ;; + + -skf) + key="-engine skf -keyform engine -key sm2.sign" + ;; + + -sdf) + # admin has to use sdf management tools to manually init device + key="-engine sdf -keyform engine -key ecc_1.sign" + ;; + + *) + echo "usage: gmca -setup " + exit + ;; + esac + + gmssl req -new -x509 -subj=$ca_dn -days $ca_rootcert_days -$md $key -out $ca_rootcert_file + gmssl x509 -text -noout -in $ca_rootcert_file +} + +function showstatus { + echo "Valid certificates : " + echo "Valid Distinct Subjects : " + echo "In Validity but Revoked Certificaes : " + echo "Certificates near Expiration : " + echo "Certificates recently expired : " + echo "Overall Certificate Count : " + echo "Overall Distinct Subjects : " + echo "Overall Revoked Certificates : " + echo "Expired Certificates : " +} + +function showcacert { + gmssl x509 -text -noout -in $ca_rootcert_file +} + +function getcacert { + if [[ -n "$1" ]]; then + out="-out $1" + fi + gmssl x509 -in $ca_rootcert_file $out +} + +#FIXME: if common_name already exist in csr_dir or in certs_dir? +function gencsr { + local common_name=$1 + if [[ -z $common_name ]]; then + echo "No Common Name given" + exit + fi + local subject="$user_dn_prefix/CN=$common_name" + local csrfile="$ca_csr_dir/$common_name.csr" + + case $user_engine in + "") + local keyfile="$user_key_dir/$common_name.key" + gmssl ecparam -genkey -name $curve -out "$keyfile" + local key="-key $keyfile" + ;; + + skf) + ;; + + sdf) + ;; + + *) + ;; + esac + + gmssl req -new -$md -subj="$subject" $key -out "$csrfile" + gmssl req -text -in "$csrfile" +} + +function listcsrs { + for csrfile in $ca_csr_dir/*.csr; do + if [[ -f $csrfile ]]; then + basename $csrfile .csr + fi + done +} + +function showcsr { + if [[ -z "$1" ]]; then + echo "usage: gmca -showcsr " + exit + fi + local common_name=$1 + local csrfile="$ca_csr_dir/$common_name.csr" + #FIXME: check if $csrfile exist + gmssl req -text -noout -in "$csrfile" +} + +function getcsr { + if [[ -z "$1" ]]; then + echo "usage: gmca -getcsr " + exit + fi + local common_name=$1 + local csrfile="$ca_csr_dir/$common_name.csr" + #FIXME: check if $csrfile exist + gmssl req -in "$csrfile" +} + +function signcsr { + if [[ -z "$1" ]]; then + echo "usage: gmca -signcsr " + exit + fi + common_name=$1 + csrfile="$ca_csr_dir/$common_name.csr" + #FIXME: check if $csrfile exist + #FIXME: use -subj to change csrname + subject="$user_dn_prefix/CN=$common_name" + gmssl ca -config ./signcsr.cnf -batch -subj=$subject -md $md -days $ca_usercert_days -outdir $ca_cert_dir -infiles "$csrfile" +} + +function signenccsr { + common_name=$1 + csrfile="$ca_csr_dir/$common_name.csr" + subject="$user_dn_enc_prefix/CN=$common_name" + gmssl ca -config ./signenccsr.cnf -batch -subj=$subject -md $md -days 365 -outdir $ca_cert_dir -infiles "$csrfile" +} + +function gencert { + common_name=$1 + gencsr $common_name + signcsr $common_name +} + +#FIXME: how to handle double certs? +function genenccert { + common_name=$1 + gencsr $common_name + signenccsr $common_name +} + +function rejectcsr { + #FIXME: check argument exist + common_name=$1 + csrfile="$ca_csr_dir/$common_name.csr" + #FIXME: check if $csrfile exist + mv $csrfile $ca_rejected_csr_dir/ +} + +function listcerts { + # the ca txt_db is a tab seperated text file + # 1. Certificate validity, "V" for valid, "R" for revoked or E for expired + # 2. Expiration datetime, + # 3. Revokation datetime, will be null if not revoked + # 4. Serial number + # 5. File name of the certificate, always be "unknown" + # 6. Certificate subject name, in oneline format + awk -F'\t' '{print $2,$4,$6}' $ca_index_file +} + +function listcertsbyname { + #FIXME: check argument exist + awk -F'\t' '{print $2,$4,$6}' $ca_index_file | grep $1 +} + +function getcertbyserial { + #FIXME: check argument exist + local serial=$1 + local certfile=$ca_cert_dir/$serial.pem + gmssl x509 -in $certfile +} + +function getcertbyname { + local common_name=$1 + #FIXME: need better method than just grep + serial=`awk -F'\t' '{print $2,$4,$6}' $ca_index_file | grep $common_name | awk '{print $2}'` + getcertbyserial $serial +} + +function listrevokereasons { + echo " unspecified" + echo " keyCompromise" + echo " CACompromise" + echo " affiliationChanged" + echo " superseded" + echo " cessationOfOperation" + echo " certificateHold" + echo " removeFromCRL (should not be used)" +} + +function _revokecertfile { + certfile=$1 + if [[ -z "$certfile" ]]; then + echo "Usage: $0 " + echo "" + exit -1 + fi + time=`date +"%Y%m%d%H%M%S"`Z + #reason=unspecified + reason="keyCompromise -crl_compromise $time" + #reason="CACompromise -crl_CA_compromise $time" + #reason=affiliationChanged + #reason=superseded, + #reason=cessationOfOperation + #reason=certificateHold + #reason=removeFromCRL. + #gmssl ca -config ./ca.cnf -valid $certfile + gmssl ca -config ./ca.cnf -revoke $certfile -crl_reason $reason + #gmssl ca -config ./ca.cnf -valid $certfile +} + +function revokecertbyname { + common_name=$1 + serial=`awk -F'\t' '{print $2,$4,$6}' $ca_index_file | grep -E "CN=$common_name$" | awk '{print $2}'` + _revokecertfile "$ca_cert_dir/$serial.pem" +} + +function revokecertbyserial { + serial=$1 + _revokecertfile "$ca_cert_dir/$serial.pem" +} + +function gencrl { + time=`date +"%Y%m%d%H%M%S"` + crlfile="$ca_crl_dir/$time.crl" + gmssl ca -config ./ca.cnf -gencrl -md $md -crldays $ca_crl_days -out $crlfile + cp $crlfile $ca_crl_file + gmssl crl -text -noout -in $ca_crl_file -CAfile $ca_rootcert_file +} + +function showcrl { + # when use `-CAfile` option, verification result will be printed + gmssl crl -text -noout -in $ca_crl_file #-CAfile $ca_rootcert_file +} + +function getcrl { + gmssl crl -in $ca_crl_file #-CAfile $ca_rootcert_file +} + +function backup { + time=`date +"%Y%m%d%H%M%S"` + tar cvzf ca-$time.tar.gz $ca_dir +} + +POSITIONAL=() +while [[ $# -gt 0 ]] +do +opt="$1" + + +#FIXME: show help info, get more options +case $opt in + -verbose) + verbose=yes + shift + shift + ;; + -setup) + dn="$2" + setup $dn + shift + shift + ;; + -showcacert) + showcacert + shift + ;; + -getcacert) + file="$2" + getcacert $file + shift + shift + ;; + -showstatus) + showstatus + shift + ;; + -gencsr) + common_name="$2" + gencsr "$common_name" + shift + shift + ;; + -listcsrs) + listcsrs + shift + ;; + -showcsr) + common_name="$2" + showcsr "$common_name" + shift + shift + ;; + -getcsr) + common_name="$2" + getcsr "$common_name" + shift + shift + ;; + -signcsr) + common_name="$2" + signcsr "$common_name" + shift + shift + ;; + -signenccsr) + common_name="$2" + signenccsr "$common_name" + shift + shift + ;; + -rejectcsr) + common_name="$2" + rejectcsr "$common_name" + shift + shift + ;; + -gencert) + common_name="$2" + gencert $common_name + shift + shift + ;; + -genenccert) + common_name="$2" + genenccert $common_name + shift + shift + ;; + -listcerts) + listcerts + shift + ;; + -listcertsbyname) + name="$2" + listcertsbyname $name + shift + shift + ;; + -getcertbyserial) + serial="$2" + getcertbyserial $serial + shift + shift + ;; + -getcertbyname) + name="$2" + getcertbyname $name + shift + shift + ;; + -showcert) + cert="$2" + showcert "$cert" + shift + shift + ;; + -revokereasons) + revokereasons + shift + ;; + -revokecertbyname) + name="$2" + revokecertbyname "$name" + shift + shift + ;; + -revokecert) + certfile="$2" + revokebycert "$certfile" + shift + shift + ;; + -revokecertbyserial) + serial="$2" + revokecertbyserial $serial + shift + shift + ;; + -gencrl) + gencrl + shift + ;; + -crl) + getcrl + shift + ;; + -showcrl) + showcrl + shift + ;; + -getcrl) + getcrl + shift + ;; + -backup) + backup + shift + ;; + *) + echo "usage: ...." + POSITIONAL+=("$1") + shift + ;; +esac +done +set -- "${POSITIONAL[@]}" + +if [[ -n $1 ]]; then + echo "Last line of file specified as non-opt/last argument:" +fi diff --git a/apps/gmca/setup.cnf b/apps/gmca/setup.cnf new file mode 100644 index 000000000..5bb851cd6 --- /dev/null +++ b/apps/gmca/setup.cnf @@ -0,0 +1,26 @@ + +[ req ] +prompt = no +distinguished_name = req_distinguished_name +attributes = req_attributes +req_extensions = v3_ca +dirstring_type = nobmp + +[ req_distinguished_name ] +C=CN +ST=BJ +L=BJ +O=PKU +OU=CA +CN=PKUCA + +[ req_attributes ] +challengePassword = A challenge password +challengePassword_min = 4 +challengePassword_max = 20 + +[ v3_ca ] + +subjectKeyIdentifier=hash +authorityKeyIdentifier=keyid:always,issuer:always +basicConstraints = critical, CA:true diff --git a/apps/gmca/signcnf.cnf b/apps/gmca/signcnf.cnf new file mode 100644 index 000000000..4f8e34ce8 --- /dev/null +++ b/apps/gmca/signcnf.cnf @@ -0,0 +1,349 @@ +# +# OpenSSL example configuration file. +# This is mostly being used for generation of certificate requests. +# + +# This definition stops the following lines choking if HOME isn't +# defined. +HOME = . +RANDFILE = $ENV::HOME/.rnd + +# Extra OBJECT IDENTIFIER info: +#oid_file = $ENV::HOME/.oid +oid_section = new_oids + +# To use this configuration file with the "-extfile" option of the +# "openssl x509" utility, name here the section containing the +# X.509v3 extensions to use: +# extensions = +# (Alternatively, use a configuration file that has only +# X.509v3 extensions in its main [= default] section.) + +[ new_oids ] + +# We can add new OIDs in here for use by 'ca', 'req' and 'ts'. +# Add a simple OID like this: +# testoid1=1.2.3.4 +# Or use config file substitution like this: +# testoid2=${testoid1}.5.6 + +# Policies used by the TSA examples. +tsa_policy1 = 1.2.3.4.1 +tsa_policy2 = 1.2.3.4.5.6 +tsa_policy3 = 1.2.3.4.5.7 + +#################################################################### +[ ca ] +default_ca = CA_default # The default ca section + +#################################################################### +[ CA_default ] + +dir = ./demoCA # Where everything is kept +certs = $dir/certs # Where the issued certs are kept +crl_dir = $dir/crl # Where the issued crl are kept +database = $dir/index.txt # database index file. +#unique_subject = no # Set to 'no' to allow creation of + # several certs with same subject. +new_certs_dir = $dir/newcerts # default place for new certs. + +certificate = $dir/cacert.pem # The CA certificate +serial = $dir/serial # The current serial number +crlnumber = $dir/crlnumber # the current crl number + # must be commented out to leave a V1 CRL +crl = $dir/crl.pem # The current CRL +private_key = $dir/private/cakey.pem# The private key +RANDFILE = $dir/private/.rand # private random number file + +x509_extensions = usr_cert # The extensions to add to the cert + +# Comment out the following two lines for the "traditional" +# (and highly broken) format. +name_opt = ca_default # Subject Name options +cert_opt = ca_default # Certificate field options + +# Extension copying option: use with caution. +# copy_extensions = copy + +# Extensions to add to a CRL. Note: Netscape communicator chokes on V2 CRLs +# so this is commented out by default to leave a V1 CRL. +# crlnumber must also be commented out to leave a V1 CRL. +# crl_extensions = crl_ext + +default_days = 365 # how long to certify for +default_crl_days= 30 # how long before next CRL +default_md = sm3 # use public key default MD +preserve = no # keep passed DN ordering + +# A few difference way of specifying how similar the request should look +# For type CA, the listed attributes must be the same, and the optional +# and supplied fields are just that :-) +policy = policy_match + +# For the CA policy +[ policy_match ] +countryName = match +stateOrProvinceName = match +organizationName = match +organizationalUnitName = optional +commonName = supplied +emailAddress = optional + +# For the 'anything' policy +# At this point in time, you must list all acceptable 'object' +# types. +[ policy_anything ] +countryName = optional +stateOrProvinceName = optional +localityName = optional +organizationName = optional +organizationalUnitName = optional +commonName = supplied +emailAddress = optional + +#################################################################### +[ req ] +default_bits = 2048 +default_keyfile = privkey.pem +distinguished_name = req_distinguished_name +attributes = req_attributes +x509_extensions = v3_ca # The extensions to add to the self signed cert + +# Passwords for private keys if not present they will be prompted for +# input_password = secret +# output_password = secret + +# This sets a mask for permitted string types. There are several options. +# default: PrintableString, T61String, BMPString. +# pkix : PrintableString, BMPString (PKIX recommendation before 2004) +# utf8only: only UTF8Strings (PKIX recommendation after 2004). +# nombstr : PrintableString, T61String (no BMPStrings or UTF8Strings). +# MASK:XXXX a literal mask value. +# WARNING: ancient versions of Netscape crash on BMPStrings or UTF8Strings. +string_mask = utf8only + +# req_extensions = v3_req # The extensions to add to a certificate request + +[ req_distinguished_name ] +countryName = Country Name (2 letter code) +countryName_default = CN +countryName_min = 2 +countryName_max = 2 + +stateOrProvinceName = State or Province Name (full name) +stateOrProvinceName_default = BJ + +localityName = Locality Name (eg, city) +localityName_default = BJ + +0.organizationName = Organization Name (eg, company) +0.organizationName_default = PKU + +# we can do this but it is not needed normally :-) +#1.organizationName = Second Organization Name (eg, company) +#1.organizationName_default = World Wide Web Pty Ltd + +organizationalUnitName = Organizational Unit Name (eg, section) +organizationalUnitName_default = CS + +commonName = Common Name (e.g. server FQDN or YOUR name) +commonName_max = 64 + +emailAddress = Email Address +emailAddress_max = 64 + +# SET-ex3 = SET extension number 3 + +[ req_attributes ] +challengePassword = A challenge password +challengePassword_min = 4 +challengePassword_max = 20 + +unstructuredName = An optional company name + +[ usr_cert ] + +# These extensions are added when 'ca' signs a request. + +# This goes against PKIX guidelines but some CAs do it and some software +# requires this to avoid interpreting an end user certificate as a CA. + +basicConstraints=CA:FALSE + +# Here are some examples of the usage of nsCertType. If it is omitted +# the certificate can be used for anything *except* object signing. + +# This is OK for an SSL server. +# nsCertType = server + +# For an object signing certificate this would be used. +# nsCertType = objsign + +# For normal client use this is typical +# nsCertType = client, email + +# and for everything including object signing: +# nsCertType = client, email, objsign + +# This is typical in keyUsage for a client certificate. +# keyUsage = nonRepudiation, digitalSignature, keyEncipherment +keyUsage = digitalSignature +#keyUsage = keyEncipherment + +# This will be displayed in Netscape's comment listbox. +nsComment = "OpenSSL Generated Certificate" + +# PKIX recommendations harmless if included in all certificates. +subjectKeyIdentifier=hash +authorityKeyIdentifier=keyid,issuer + +# This stuff is for subjectAltName and issuerAltname. +# Import the email address. +# subjectAltName=email:copy +# An alternative to produce certificates that aren't +# deprecated according to PKIX. +# subjectAltName=email:move + +# Copy subject details +# issuerAltName=issuer:copy + +#nsCaRevocationUrl = http://www.domain.dom/ca-crl.pem +#nsBaseUrl +#nsRevocationUrl +#nsRenewalUrl +#nsCaPolicyUrl +#nsSslServerName + +# This is required for TSA certificates. +# extendedKeyUsage = critical,timeStamping + +[ v3_req ] + +# Extensions to add to a certificate request + +basicConstraints = CA:FALSE +keyUsage = nonRepudiation, digitalSignature, keyEncipherment + +[ v3_ca ] + + +# Extensions for a typical CA + + +# PKIX recommendation. + +subjectKeyIdentifier=hash + +authorityKeyIdentifier=keyid:always,issuer + +basicConstraints = critical,CA:true + +# Key usage: this is typical for a CA certificate. However since it will +# prevent it being used as an test self-signed certificate it is best +# left out by default. +# keyUsage = cRLSign, keyCertSign + +# Some might want this also +# nsCertType = sslCA, emailCA + +# Include email address in subject alt name: another PKIX recommendation +# subjectAltName=email:copy +# Copy issuer details +# issuerAltName=issuer:copy + +# DER hex encoding of an extension: beware experts only! +# obj=DER:02:03 +# Where 'obj' is a standard or added object +# You can even override a supported extension: +# basicConstraints= critical, DER:30:03:01:01:FF + +[ crl_ext ] + +# CRL extensions. +# Only issuerAltName and authorityKeyIdentifier make any sense in a CRL. + +# issuerAltName=issuer:copy +authorityKeyIdentifier=keyid:always + +[ proxy_cert_ext ] +# These extensions should be added when creating a proxy certificate + +# This goes against PKIX guidelines but some CAs do it and some software +# requires this to avoid interpreting an end user certificate as a CA. + +basicConstraints=CA:FALSE + +# Here are some examples of the usage of nsCertType. If it is omitted +# the certificate can be used for anything *except* object signing. + +# This is OK for an SSL server. +# nsCertType = server + +# For an object signing certificate this would be used. +# nsCertType = objsign + +# For normal client use this is typical +# nsCertType = client, email + +# and for everything including object signing: +# nsCertType = client, email, objsign + +# This is typical in keyUsage for a client certificate. +# keyUsage = nonRepudiation, digitalSignature, keyEncipherment + +# This will be displayed in Netscape's comment listbox. +nsComment = "OpenSSL Generated Certificate" + +# PKIX recommendations harmless if included in all certificates. +subjectKeyIdentifier=hash +authorityKeyIdentifier=keyid,issuer + +# This stuff is for subjectAltName and issuerAltname. +# Import the email address. +# subjectAltName=email:copy +# An alternative to produce certificates that aren't +# deprecated according to PKIX. +# subjectAltName=email:move + +# Copy subject details +# issuerAltName=issuer:copy + +#nsCaRevocationUrl = http://www.domain.dom/ca-crl.pem +#nsBaseUrl +#nsRevocationUrl +#nsRenewalUrl +#nsCaPolicyUrl +#nsSslServerName + +# This really needs to be in place for it to be a proxy certificate. +proxyCertInfo=critical,language:id-ppl-anyLanguage,pathlen:3,policy:foo + +#################################################################### +[ tsa ] + +default_tsa = tsa_config1 # the default TSA section + +[ tsa_config1 ] + +# These are used by the TSA reply generation only. +dir = ./demoCA # TSA root directory +serial = $dir/tsaserial # The current serial number (mandatory) +crypto_device = builtin # OpenSSL engine to use for signing +signer_cert = $dir/tsacert.pem # The TSA signing certificate + # (optional) +certs = $dir/cacert.pem # Certificate chain to include in reply + # (optional) +signer_key = $dir/private/tsakey.pem # The TSA private key (optional) +signer_digest = sha256 # Signing digest to use. (Optional) +default_policy = tsa_policy1 # Policy if request did not specify it + # (optional) +other_policies = tsa_policy2, tsa_policy3 # acceptable policies (optional) +digests = sha1, sha256, sha384, sha512 # Acceptable message digests (mandatory) +accuracy = secs:1, millisecs:500, microsecs:100 # (optional) +clock_precision_digits = 0 # number of digits after dot. (optional) +ordering = yes # Is ordering defined for timestamps? + # (optional, default: no) +tsa_name = yes # Must the TSA name be included in the reply? + # (optional, default: no) +ess_cert_id_chain = no # Must the ESS cert id chain be included? + # (optional, default: no) diff --git a/apps/gmca/signcsr.cnf b/apps/gmca/signcsr.cnf new file mode 100644 index 000000000..6f784d0db --- /dev/null +++ b/apps/gmca/signcsr.cnf @@ -0,0 +1,351 @@ +# +# OpenSSL example configuration file. +# This is mostly being used for generation of certificate requests. +# + +# This definition stops the following lines choking if HOME isn't +# defined. +HOME = . +RANDFILE = $ENV::HOME/.rnd + +# Extra OBJECT IDENTIFIER info: +#oid_file = $ENV::HOME/.oid +oid_section = new_oids + +# To use this configuration file with the "-extfile" option of the +# "openssl x509" utility, name here the section containing the +# X.509v3 extensions to use: +# extensions = +# (Alternatively, use a configuration file that has only +# X.509v3 extensions in its main [= default] section.) + +[ new_oids ] + +# We can add new OIDs in here for use by 'ca', 'req' and 'ts'. +# Add a simple OID like this: +# testoid1=1.2.3.4 +# Or use config file substitution like this: +# testoid2=${testoid1}.5.6 + +# Policies used by the TSA examples. +tsa_policy1 = 1.2.3.4.1 +tsa_policy2 = 1.2.3.4.5.6 +tsa_policy3 = 1.2.3.4.5.7 + +#################################################################### +[ ca ] +default_ca = CA_default # The default ca section + +#################################################################### +[ CA_default ] + +dir = .ca # Where everything is kept +certs = $dir/certs # Where the issued certs are kept +crl_dir = $dir/crl # Where the issued crl are kept +database = $dir/index.txt # database index file. +#unique_subject = no # Set to 'no' to allow creation of + # several ctificates with same subject. +new_certs_dir = $dir/newcerts # default place for new certs. + +certificate = $dir/cacert.pem # The CA certificate +serial = $dir/serial # The current serial number +crlnumber = $dir/crlnumber # the current crl number + # must be commented out to leave a V1 CRL +crl = $dir/crl.pem # The current CRL +private_key = $dir/private/cakey.pem# The private key +RANDFILE = $dir/private/.rand # private random number file + +x509_extensions = usr_cert # The extentions to add to the cert + +# Comment out the following two lines for the "traditional" +# (and highly broken) format. +name_opt = ca_default # Subject Name options +cert_opt = ca_default # Certificate field options + +# Extension copying option: use with caution. +# copy_extensions = copy + +# Extensions to add to a CRL. Note: Netscape communicator chokes on V2 CRLs +# so this is commented out by default to leave a V1 CRL. +# crlnumber must also be commented out to leave a V1 CRL. +# crl_extensions = crl_ext + +default_days = 365 # how long to certify for +default_crl_days= 30 # how long before next CRL +default_md = default # use public key default MD +preserve = no # keep passed DN ordering + +# A few difference way of specifying how similar the request should look +# For type CA, the listed attributes must be the same, and the optional +# and supplied fields are just that :-) +policy = policy_match + +# For the CA policy +[ policy_match ] +countryName = match +stateOrProvinceName = match +organizationName = match +organizationalUnitName = optional +commonName = supplied +emailAddress = optional + +# For the 'anything' policy +# At this point in time, you must list all acceptable 'object' +# types. +[ policy_anything ] +countryName = optional +stateOrProvinceName = optional +localityName = optional +organizationName = optional +organizationalUnitName = optional +commonName = supplied +emailAddress = optional + +#################################################################### +[ req ] +default_bits = 2048 +default_keyfile = privkey.pem +distinguished_name = req_distinguished_name +attributes = req_attributes +x509_extensions = v3_ca # The extentions to add to the self signed cert + +# Passwords for private keys if not present they will be prompted for +# input_password = secret +# output_password = secret + +# This sets a mask for permitted string types. There are several options. +# default: PrintableString, T61String, BMPString. +# pkix : PrintableString, BMPString (PKIX recommendation before 2004) +# utf8only: only UTF8Strings (PKIX recommendation after 2004). +# nombstr : PrintableString, T61String (no BMPStrings or UTF8Strings). +# MASK:XXXX a literal mask value. +# WARNING: ancient versions of Netscape crash on BMPStrings or UTF8Strings. +string_mask = utf8only + +# req_extensions = v3_req # The extensions to add to a certificate request + +[ req_distinguished_name ] +countryName = Country Name (2 letter code) +countryName_default = AU +countryName_min = 2 +countryName_max = 2 + +stateOrProvinceName = State or Province Name (full name) +stateOrProvinceName_default = Some-State + +localityName = Locality Name (eg, city) + +0.organizationName = Organization Name (eg, company) +0.organizationName_default = Internet Widgits Pty Ltd + +# we can do this but it is not needed normally :-) +#1.organizationName = Second Organization Name (eg, company) +#1.organizationName_default = World Wide Web Pty Ltd + +organizationalUnitName = Organizational Unit Name (eg, section) +#organizationalUnitName_default = + +commonName = Common Name (e.g. server FQDN or YOUR name) +commonName_max = 64 + +emailAddress = Email Address +emailAddress_max = 64 + +# SET-ex3 = SET extension number 3 + +[ req_attributes ] +challengePassword = A challenge password +challengePassword_min = 4 +challengePassword_max = 20 + +unstructuredName = An optional company name + +[ usr_cert ] + +# These extensions are added when 'ca' signs a request. + +# This goes against PKIX guidelines but some CAs do it and some software +# requires this to avoid interpreting an end user certificate as a CA. + +basicConstraints=CA:FALSE + +# Here are some examples of the usage of nsCertType. If it is omitted +# the certificate can be used for anything *except* object signing. + +# This is OK for an SSL server. +# nsCertType = server + +# For an object signing certificate this would be used. +# nsCertType = objsign + +# For normal client use this is typical +# nsCertType = client, email + +# and for everything including object signing: +# nsCertType = client, email, objsign + +# This is typical in keyUsage for a client certificate. +# keyUsage = nonRepudiation, digitalSignature, keyEncipherment +keyUsage = digitalSignature + +# This will be displayed in Netscape's comment listbox. +nsComment = "OpenSSL Generated Certificate" + +# PKIX recommendations harmless if included in all certificates. +subjectKeyIdentifier=hash +authorityKeyIdentifier=keyid,issuer + +# This stuff is for subjectAltName and issuerAltname. +# Import the email address. +# subjectAltName=email:copy +# An alternative to produce certificates that aren't +# deprecated according to PKIX. +# subjectAltName=email:move + +# Copy subject details +# issuerAltName=issuer:copy + +#nsCaRevocationUrl = http://www.domain.dom/ca-crl.pem +#nsBaseUrl +#nsRevocationUrl +#nsRenewalUrl +#nsCaPolicyUrl +#nsSslServerName + +# This is required for TSA certificates. +# extendedKeyUsage = critical,timeStamping + +[ v3_req ] + +# Extensions to add to a certificate request + +basicConstraints = CA:FALSE +keyUsage = nonRepudiation, digitalSignature, keyEncipherment + +[ v3_ca ] + + +# Extensions for a typical CA + + +# PKIX recommendation. + +subjectKeyIdentifier=hash + +authorityKeyIdentifier=keyid:always,issuer + +# This is what PKIX recommends but some broken software chokes on critical +# extensions. +#basicConstraints = critical,CA:true +# So we do this instead. +basicConstraints = CA:true + +# Key usage: this is typical for a CA certificate. However since it will +# prevent it being used as an test self-signed certificate it is best +# left out by default. +# keyUsage = cRLSign, keyCertSign + +# Some might want this also +# nsCertType = sslCA, emailCA + +# Include email address in subject alt name: another PKIX recommendation +# subjectAltName=email:copy +# Copy issuer details +# issuerAltName=issuer:copy + +# DER hex encoding of an extension: beware experts only! +# obj=DER:02:03 +# Where 'obj' is a standard or added object +# You can even override a supported extension: +# basicConstraints= critical, DER:30:03:01:01:FF + +[ crl_ext ] + +# CRL extensions. +# Only issuerAltName and authorityKeyIdentifier make any sense in a CRL. + +# issuerAltName=issuer:copy +authorityKeyIdentifier=keyid:always + +[ proxy_cert_ext ] +# These extensions should be added when creating a proxy certificate + +# This goes against PKIX guidelines but some CAs do it and some software +# requires this to avoid interpreting an end user certificate as a CA. + +basicConstraints=CA:FALSE + +# Here are some examples of the usage of nsCertType. If it is omitted +# the certificate can be used for anything *except* object signing. + +# This is OK for an SSL server. +# nsCertType = server + +# For an object signing certificate this would be used. +# nsCertType = objsign + +# For normal client use this is typical +# nsCertType = client, email + +# and for everything including object signing: +# nsCertType = client, email, objsign + +# This is typical in keyUsage for a client certificate. +# keyUsage = nonRepudiation, digitalSignature, keyEncipherment + +# This will be displayed in Netscape's comment listbox. +nsComment = "OpenSSL Generated Certificate" + +# PKIX recommendations harmless if included in all certificates. +subjectKeyIdentifier=hash +authorityKeyIdentifier=keyid,issuer + +# This stuff is for subjectAltName and issuerAltname. +# Import the email address. +# subjectAltName=email:copy +# An alternative to produce certificates that aren't +# deprecated according to PKIX. +# subjectAltName=email:move + +# Copy subject details +# issuerAltName=issuer:copy + +#nsCaRevocationUrl = http://www.domain.dom/ca-crl.pem +#nsBaseUrl +#nsRevocationUrl +#nsRenewalUrl +#nsCaPolicyUrl +#nsSslServerName + +# This really needs to be in place for it to be a proxy certificate. +proxyCertInfo=critical,language:id-ppl-anyLanguage,pathlen:3,policy:foo + +#################################################################### +[ tsa ] + +default_tsa = tsa_config1 # the default TSA section + +[ tsa_config1 ] + +# These are used by the TSA reply generation only. +dir = ./demoCA # TSA root directory +serial = $dir/tsaserial # The current serial number (mandatory) +crypto_device = builtin # OpenSSL engine to use for signing +signer_cert = $dir/tsacert.pem # The TSA signing certificate + # (optional) +certs = $dir/cacert.pem # Certificate chain to include in reply + # (optional) +signer_key = $dir/private/tsakey.pem # The TSA private key (optional) + +default_policy = tsa_policy1 # Policy if request did not specify it + # (optional) +other_policies = tsa_policy2, tsa_policy3 # acceptable policies (optional) +digests = md5, sha1 # Acceptable message digests (mandatory) +accuracy = secs:1, millisecs:500, microsecs:100 # (optional) +clock_precision_digits = 0 # number of digits after dot. (optional) +ordering = yes # Is ordering defined for timestamps? + # (optional, default: no) +tsa_name = yes # Must the TSA name be included in the reply? + # (optional, default: no) +ess_cert_id_chain = no # Must the ESS cert id chain be included? + # (optional, default: no) diff --git a/apps/gmca/signenccsr.cnf b/apps/gmca/signenccsr.cnf new file mode 100644 index 000000000..2a21fd42d --- /dev/null +++ b/apps/gmca/signenccsr.cnf @@ -0,0 +1,296 @@ +# +# OpenSSL example configuration file. +# This is mostly being used for generation of certificate requests. +# + +# This definition stops the following lines choking if HOME isn't +# defined. +HOME = . +RANDFILE = $ENV::HOME/.rnd + +#################################################################### +[ ca ] +default_ca = CA_default # The default ca section + +#################################################################### +[ CA_default ] + +dir = .ca # Where everything is kept +certs = $dir/certs # Where the issued certs are kept +crl_dir = $dir/crl # Where the issued crl are kept +database = $dir/index.txt # database index file. +#unique_subject = no # Set to 'no' to allow creation of + # several certs with same subject. +new_certs_dir = $dir/newcerts # default place for new certs. + +certificate = $dir/cacert.pem # The CA certificate +serial = $dir/serial # The current serial number +crlnumber = $dir/crlnumber # the current crl number + # must be commented out to leave a V1 CRL +crl = $dir/crl.pem # The current CRL +private_key = $dir/private/cakey.pem# The private key +RANDFILE = $dir/private/.rand # private random number file + +x509_extensions = usr_cert # The extensions to add to the cert + +# Comment out the following two lines for the "traditional" +# (and highly broken) format. +name_opt = ca_default # Subject Name options +cert_opt = ca_default # Certificate field options + +# Extension copying option: use with caution. +# copy_extensions = copy + +# Extensions to add to a CRL. Note: Netscape communicator chokes on V2 CRLs +# so this is commented out by default to leave a V1 CRL. +# crlnumber must also be commented out to leave a V1 CRL. +# crl_extensions = crl_ext + +default_days = 365 # how long to certify for +default_crl_days= 30 # how long before next CRL +default_md = sm3 # use public key default MD +preserve = no # keep passed DN ordering + +# A few difference way of specifying how similar the request should look +# For type CA, the listed attributes must be the same, and the optional +# and supplied fields are just that :-) +policy = policy_match + +# For the CA policy +[ policy_match ] +countryName = match +stateOrProvinceName = match +organizationName = match +organizationalUnitName = optional +commonName = supplied +emailAddress = optional + +# For the 'anything' policy +# At this point in time, you must list all acceptable 'object' +# types. +[ policy_anything ] +countryName = optional +stateOrProvinceName = optional +localityName = optional +organizationName = optional +organizationalUnitName = optional +commonName = supplied +emailAddress = optional + +#################################################################### +[ req ] +default_bits = 2048 +default_keyfile = privkey.pem +distinguished_name = req_distinguished_name +attributes = req_attributes +x509_extensions = v3_ca # The extensions to add to the self signed cert + +# Passwords for private keys if not present they will be prompted for +# input_password = secret +# output_password = secret + +# This sets a mask for permitted string types. There are several options. +# default: PrintableString, T61String, BMPString. +# pkix : PrintableString, BMPString (PKIX recommendation before 2004) +# utf8only: only UTF8Strings (PKIX recommendation after 2004). +# nombstr : PrintableString, T61String (no BMPStrings or UTF8Strings). +# MASK:XXXX a literal mask value. +# WARNING: ancient versions of Netscape crash on BMPStrings or UTF8Strings. +string_mask = utf8only + +# req_extensions = v3_req # The extensions to add to a certificate request + +[ req_distinguished_name ] +countryName = Country Name (2 letter code) +countryName_default = CN +countryName_min = 2 +countryName_max = 2 + +stateOrProvinceName = State or Province Name (full name) +stateOrProvinceName_default = BJ + +localityName = Locality Name (eg, city) +localityName_default = BJ + +0.organizationName = Organization Name (eg, company) +0.organizationName_default = PKU + +# we can do this but it is not needed normally :-) +#1.organizationName = Second Organization Name (eg, company) +#1.organizationName_default = World Wide Web Pty Ltd + +organizationalUnitName = Organizational Unit Name (eg, section) +organizationalUnitName_default = CS + +commonName = Common Name (e.g. server FQDN or YOUR name) +commonName_max = 64 + +emailAddress = Email Address +emailAddress_max = 64 + +# SET-ex3 = SET extension number 3 + +[ req_attributes ] +challengePassword = A challenge password +challengePassword_min = 4 +challengePassword_max = 20 + +unstructuredName = An optional company name + +[ usr_cert ] + +# These extensions are added when 'ca' signs a request. + +# This goes against PKIX guidelines but some CAs do it and some software +# requires this to avoid interpreting an end user certificate as a CA. + +basicConstraints=CA:FALSE + +# Here are some examples of the usage of nsCertType. If it is omitted +# the certificate can be used for anything *except* object signing. + +# This is OK for an SSL server. +# nsCertType = server + +# For an object signing certificate this would be used. +# nsCertType = objsign + +# For normal client use this is typical +# nsCertType = client, email + +# and for everything including object signing: +# nsCertType = client, email, objsign + +# This is typical in keyUsage for a client certificate. +# keyUsage = nonRepudiation, digitalSignature, keyEncipherment +#keyUsage = digitalSignature +keyUsage = keyEncipherment + +# This will be displayed in Netscape's comment listbox. +nsComment = "OpenSSL Generated Certificate" + +# PKIX recommendations harmless if included in all certificates. +subjectKeyIdentifier=hash +authorityKeyIdentifier=keyid,issuer + +# This stuff is for subjectAltName and issuerAltname. +# Import the email address. +# subjectAltName=email:copy +# An alternative to produce certificates that aren't +# deprecated according to PKIX. +# subjectAltName=email:move + +# Copy subject details +# issuerAltName=issuer:copy + +#nsCaRevocationUrl = http://www.domain.dom/ca-crl.pem +#nsBaseUrl +#nsRevocationUrl +#nsRenewalUrl +#nsCaPolicyUrl +#nsSslServerName + +# This is required for TSA certificates. +# extendedKeyUsage = critical,timeStamping + +[ v3_req ] + +# Extensions to add to a certificate request + +basicConstraints = CA:FALSE +keyUsage = nonRepudiation, digitalSignature, keyEncipherment + +[ v3_ca ] + + +# Extensions for a typical CA + + +# PKIX recommendation. + +subjectKeyIdentifier=hash + +authorityKeyIdentifier=keyid:always,issuer + +basicConstraints = critical,CA:true + +# Key usage: this is typical for a CA certificate. However since it will +# prevent it being used as an test self-signed certificate it is best +# left out by default. +# keyUsage = cRLSign, keyCertSign + +# Some might want this also +# nsCertType = sslCA, emailCA + +# Include email address in subject alt name: another PKIX recommendation +# subjectAltName=email:copy +# Copy issuer details +# issuerAltName=issuer:copy + +# DER hex encoding of an extension: beware experts only! +# obj=DER:02:03 +# Where 'obj' is a standard or added object +# You can even override a supported extension: +# basicConstraints= critical, DER:30:03:01:01:FF + +[ crl_ext ] + +# CRL extensions. +# Only issuerAltName and authorityKeyIdentifier make any sense in a CRL. + +# issuerAltName=issuer:copy +authorityKeyIdentifier=keyid:always + +[ proxy_cert_ext ] +# These extensions should be added when creating a proxy certificate + +# This goes against PKIX guidelines but some CAs do it and some software +# requires this to avoid interpreting an end user certificate as a CA. + +basicConstraints=CA:FALSE + +# Here are some examples of the usage of nsCertType. If it is omitted +# the certificate can be used for anything *except* object signing. + +# This is OK for an SSL server. +# nsCertType = server + +# For an object signing certificate this would be used. +# nsCertType = objsign + +# For normal client use this is typical +# nsCertType = client, email + +# and for everything including object signing: +# nsCertType = client, email, objsign + +# This is typical in keyUsage for a client certificate. +# keyUsage = nonRepudiation, digitalSignature, keyEncipherment + +# This will be displayed in Netscape's comment listbox. +nsComment = "OpenSSL Generated Certificate" + +# PKIX recommendations harmless if included in all certificates. +subjectKeyIdentifier=hash +authorityKeyIdentifier=keyid,issuer + +# This stuff is for subjectAltName and issuerAltname. +# Import the email address. +# subjectAltName=email:copy +# An alternative to produce certificates that aren't +# deprecated according to PKIX. +# subjectAltName=email:move + +# Copy subject details +# issuerAltName=issuer:copy + +#nsCaRevocationUrl = http://www.domain.dom/ca-crl.pem +#nsBaseUrl +#nsRevocationUrl +#nsRenewalUrl +#nsCaPolicyUrl +#nsSslServerName + +# This really needs to be in place for it to be a proxy certificate. +proxyCertInfo=critical,language:id-ppl-anyLanguage,pathlen:3,policy:foo + diff --git a/apps/gmca/test.sh b/apps/gmca/test.sh new file mode 100755 index 000000000..fcd05f8b9 --- /dev/null +++ b/apps/gmca/test.sh @@ -0,0 +1,54 @@ +#!/bin/bash -x + +gmca=./gmca + +echo "Generate self-signed root CA certificate, configurations and database" +$gmca -setup + +echo "Get root CA certificate" +$gmca -cacert + +echo "Generate CSR for Alice, Bob and Carol" +$gmca -gencsr alice@pku.edu.cn +$gmca -gencsr bob@pku.edu.cn +$gmca -gencsr carol@pku.edu.cn + +echo "CA sign CSRs" +$gmca -listcsrs +$gmca -showcsr alice@pku.edu.cn +$gmca -signcsr alice@pku.edu.cn +$gmca -signcsr bob@pku.edu.cn + +echo "Reject some CSRs" +$gmca -rejectcsr carol@pku.edu.cn +$gmca -listcsrs + +echo "List all certificates" +$gmca -listcerts + +echo "Search certificate with CommonName" +$gmca -listcertsbyname alice + +echo "Get certificate with CommonName" +$gmca -getcertbyname alice + +echo "Get certificate with Serial Number" +$gmca -getcertbyserial 01 + +#echo "Write certificate to SKF device" + +echo "Revoke certificate with Serial Number" +$gmca -revokecertbyserial 01 + +echo "Generate CRL" +$gmca -gencrl + +echo "Show CRL" +$gmca -showcrl + +echo "Get CRL" +$gmca -getcrl + +echo "Backup" +$gmca -backup + diff --git a/apps/gmssl.c b/apps/gmssl.c new file mode 100644 index 000000000..d1933846a --- /dev/null +++ b/apps/gmssl.c @@ -0,0 +1,701 @@ +/* + * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifndef OPENSSL_NO_ENGINE +# include +#endif +#include +#ifdef OPENSSL_FIPS +# include +#endif +#define USE_SOCKETS /* needed for the _O_BINARY defs in the MS world */ +#include "s_apps.h" +/* Needed to get the other O_xxx flags. */ +#ifdef OPENSSL_SYS_VMS +# include +#endif +#define INCLUDE_FUNCTION_TABLE +#include "apps.h" + + +#ifdef OPENSSL_NO_CAMELLIA +# define FORMAT "%-15s" +# define COLUMNS 5 +#else +# define FORMAT "%-18s" +# define COLUMNS 4 +#endif + +/* Special sentinel to exit the program. */ +#define EXIT_THE_PROGRAM (-1) + +/* + * The LHASH callbacks ("hash" & "cmp") have been replaced by functions with + * the base prototypes (we cast each variable inside the function to the + * required type of "FUNCTION*"). This removes the necessity for + * macro-generated wrapper functions. + */ +static LHASH_OF(FUNCTION) *prog_init(void); +static int do_cmd(LHASH_OF(FUNCTION) *prog, int argc, char *argv[]); +static void list_pkey(void); +static void list_type(FUNC_TYPE ft); +static void list_disabled(void); +char *default_config_file = NULL; + +static CONF *config = NULL; +BIO *bio_in = NULL; +BIO *bio_out = NULL; +BIO *bio_err = NULL; + +static int apps_startup() +{ +#ifdef SIGPIPE + signal(SIGPIPE, SIG_IGN); +#endif + + /* Set non-default library initialisation settings */ + if (!OPENSSL_init_crypto(OPENSSL_INIT_ENGINE_ALL_BUILTIN + | OPENSSL_INIT_LOAD_CONFIG, NULL)) + return 0; + +#ifndef OPENSSL_NO_UI + setup_ui_method(); +#endif + + return 1; +} + +static void apps_shutdown() +{ +#ifndef OPENSSL_NO_UI + destroy_ui_method(); +#endif +} + +static char *make_config_name() +{ + const char *t; + size_t len; + char *p; + + if ((t = getenv("OPENSSL_CONF")) != NULL) + return OPENSSL_strdup(t); + + t = X509_get_default_cert_area(); + len = strlen(t) + 1 + strlen(OPENSSL_CONF) + 1; + p = app_malloc(len, "config filename buffer"); + strcpy(p, t); +#ifndef OPENSSL_SYS_VMS + strcat(p, "/"); +#endif + strcat(p, OPENSSL_CONF); + + return p; +} + +int main(int argc, char *argv[]) +{ + FUNCTION f, *fp; + LHASH_OF(FUNCTION) *prog = NULL; + char **copied_argv = NULL; + char *p, *pname; + char buf[1024]; + const char *prompt; + ARGS arg; + int first, n, i, ret = 0; + + arg.argv = NULL; + arg.size = 0; + + /* Set up some of the environment. */ + default_config_file = make_config_name(); + bio_in = dup_bio_in(FORMAT_TEXT); + bio_out = dup_bio_out(FORMAT_TEXT); + bio_err = dup_bio_err(FORMAT_TEXT); + +#if defined(OPENSSL_SYS_VMS) && defined(__DECC) + copied_argv = argv = copy_argv(&argc, argv); +#elif defined(_WIN32) + /* + * Replace argv[] with UTF-8 encoded strings. + */ + win32_utf8argv(&argc, &argv); +#endif + + p = getenv("OPENSSL_DEBUG_MEMORY"); + if (p != NULL && strcmp(p, "on") == 0) + CRYPTO_set_mem_debug(1); + CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_ON); + + if (getenv("OPENSSL_FIPS")) { +#ifdef OPENSSL_FIPS + if (!FIPS_mode_set(1)) { + ERR_print_errors(bio_err); + return 1; + } +#else + BIO_printf(bio_err, "FIPS mode not supported.\n"); + return 1; +#endif + } + + if (!apps_startup()) + goto end; + + prog = prog_init(); + pname = opt_progname(argv[0]); + + /* first check the program name */ + f.name = pname; + fp = lh_FUNCTION_retrieve(prog, &f); + if (fp != NULL) { + argv[0] = pname; + ret = fp->func(argc, argv); + goto end; + } + + /* If there is stuff on the command line, run with that. */ + if (argc != 1) { + argc--; + argv++; + ret = do_cmd(prog, argc, argv); + if (ret < 0) + ret = 0; + goto end; + } + + /* ok, lets enter interactive mode */ + for (;;) { + ret = 0; + /* Read a line, continue reading if line ends with \ */ + for (p = buf, n = sizeof buf, i = 0, first = 1; n > 0; first = 0) { + prompt = first ? "GmSSL> " : "> "; + p[0] = '\0'; +#ifndef READLINE + fputs(prompt, stdout); + fflush(stdout); + if (!fgets(p, n, stdin)) + goto end; + if (p[0] == '\0') + goto end; + i = strlen(p); + if (i <= 1) + break; + if (p[i - 2] != '\\') + break; + i -= 2; + p += i; + n -= i; +#else + { + extern char *readline(const char *); + extern void add_history(const char *cp); + char *text; + + text = readline(prompt); + if (text == NULL) + goto end; + i = strlen(text); + if (i == 0 || i > n) + break; + if (text[i - 1] != '\\') { + p += strlen(strcpy(p, text)); + free(text); + add_history(buf); + break; + } + + text[i - 1] = '\0'; + p += strlen(strcpy(p, text)); + free(text); + n -= i; + } +#endif + } + + if (!chopup_args(&arg, buf)) { + BIO_printf(bio_err, "Can't parse (no memory?)\n"); + break; + } + + ret = do_cmd(prog, arg.argc, arg.argv); + if (ret == EXIT_THE_PROGRAM) { + ret = 0; + goto end; + } + if (ret != 0) + BIO_printf(bio_err, "error in %s\n", arg.argv[0]); + (void)BIO_flush(bio_out); + (void)BIO_flush(bio_err); + } + ret = 1; + end: + OPENSSL_free(copied_argv); + OPENSSL_free(default_config_file); + NCONF_free(config); + config = NULL; + lh_FUNCTION_free(prog); + OPENSSL_free(arg.argv); + + BIO_free(bio_in); + BIO_free_all(bio_out); + apps_shutdown(); +#ifndef OPENSSL_NO_CRYPTO_MDEBUG + if (CRYPTO_mem_leaks(bio_err) <= 0) + ret = 1; +#endif + BIO_free(bio_err); + EXIT(ret); +} + +OPTIONS exit_options[] = { + {NULL} +}; + +static void list_cipher_fn(const EVP_CIPHER *c, + const char *from, const char *to, void *arg) +{ + if (c) + BIO_printf(arg, "%s\n", EVP_CIPHER_name(c)); + else { + if (!from) + from = ""; + if (!to) + to = ""; + BIO_printf(arg, "%s => %s\n", from, to); + } +} + +static void list_md_fn(const EVP_MD *m, + const char *from, const char *to, void *arg) +{ + if (m) + BIO_printf(arg, "%s\n", EVP_MD_name(m)); + else { + if (!from) + from = ""; + if (!to) + to = ""; + BIO_printf((BIO *)arg, "%s => %s\n", from, to); + } +} + +/* Unified enum for help and list commands. */ +typedef enum HELPLIST_CHOICE { + OPT_ERR = -1, OPT_EOF = 0, OPT_HELP, + OPT_COMMANDS, OPT_DIGEST_COMMANDS, + OPT_DIGEST_ALGORITHMS, OPT_CIPHER_COMMANDS, OPT_CIPHER_ALGORITHMS, + OPT_PK_ALGORITHMS, OPT_DISABLED +} HELPLIST_CHOICE; + +OPTIONS list_options[] = { + {"help", OPT_HELP, '-', "Display this summary"}, + {"commands", OPT_COMMANDS, '-', "List of standard commands"}, + {"digest-commands", OPT_DIGEST_COMMANDS, '-', + "List of message digest commands"}, + {"digest-algorithms", OPT_DIGEST_ALGORITHMS, '-', + "List of message digest algorithms"}, + {"cipher-commands", OPT_CIPHER_COMMANDS, '-', "List of cipher commands"}, + {"cipher-algorithms", OPT_CIPHER_ALGORITHMS, '-', + "List of cipher algorithms"}, + {"public-key-algorithms", OPT_PK_ALGORITHMS, '-', + "List of public key algorithms"}, + {"disabled", OPT_DISABLED, '-', + "List of disabled features"}, + {NULL} +}; + +int list_main(int argc, char **argv) +{ + char *prog; + HELPLIST_CHOICE o; + int done = 0; + + prog = opt_init(argc, argv, list_options); + while ((o = opt_next()) != OPT_EOF) { + switch (o) { + case OPT_EOF: /* Never hit, but suppresses warning */ + case OPT_ERR: + BIO_printf(bio_err, "%s: Use -help for summary.\n", prog); + return 1; + case OPT_HELP: + opt_help(list_options); + break; + case OPT_COMMANDS: + list_type(FT_general); + break; + case OPT_DIGEST_COMMANDS: + list_type(FT_md); + break; + case OPT_DIGEST_ALGORITHMS: + EVP_MD_do_all_sorted(list_md_fn, bio_out); + break; + case OPT_CIPHER_COMMANDS: + list_type(FT_cipher); + break; + case OPT_CIPHER_ALGORITHMS: + EVP_CIPHER_do_all_sorted(list_cipher_fn, bio_out); + break; + case OPT_PK_ALGORITHMS: + list_pkey(); + break; + case OPT_DISABLED: + list_disabled(); + break; + } + done = 1; + } + + if (!done) { + BIO_printf(bio_err, "%s: Use -help for summary.\n", prog); + return 1; + } + + return 0; +} + +OPTIONS help_options[] = { + {"help", OPT_HELP, '-', "Display this summary"}, + {NULL} +}; + +int help_main(int argc, char **argv) +{ + FUNCTION *fp; + int i, nl; + FUNC_TYPE tp; + char *prog; + HELPLIST_CHOICE o; + + prog = opt_init(argc, argv, help_options); + while ((o = opt_next()) != OPT_EOF) { + switch (o) { + default: + BIO_printf(bio_err, "%s: Use -help for summary.\n", prog); + return 1; + case OPT_HELP: + opt_help(help_options); + return 0; + } + } + + if (opt_num_rest() != 0) { + BIO_printf(bio_err, "Usage: %s\n", prog); + return 1; + } + + BIO_printf(bio_err, "\nStandard commands"); + i = 0; + tp = FT_none; + for (fp = functions; fp->name != NULL; fp++) { + nl = 0; + if (((i++) % COLUMNS) == 0) { + BIO_printf(bio_err, "\n"); + nl = 1; + } + if (fp->type != tp) { + tp = fp->type; + if (!nl) + BIO_printf(bio_err, "\n"); + if (tp == FT_md) { + i = 1; + BIO_printf(bio_err, + "\nMessage Digest commands (see the `dgst' command for more details)\n"); + } else if (tp == FT_cipher) { + i = 1; + BIO_printf(bio_err, + "\nCipher commands (see the `enc' command for more details)\n"); + } + } + BIO_printf(bio_err, FORMAT, fp->name); + } + BIO_printf(bio_err, "\n\n"); + return 0; +} + +int exit_main(int argc, char **argv) +{ + return EXIT_THE_PROGRAM; +} + +static void list_type(FUNC_TYPE ft) +{ + FUNCTION *fp; + int i = 0; + + for (fp = functions; fp->name != NULL; fp++) + if (fp->type == ft) { + if ((i++ % COLUMNS) == 0) + BIO_printf(bio_out, "\n"); + BIO_printf(bio_out, FORMAT, fp->name); + } + BIO_printf(bio_out, "\n"); +} + +static int do_cmd(LHASH_OF(FUNCTION) *prog, int argc, char *argv[]) +{ + FUNCTION f, *fp; + + if (argc <= 0 || argv[0] == NULL) + return (0); + f.name = argv[0]; + fp = lh_FUNCTION_retrieve(prog, &f); + if (fp == NULL) { + if (EVP_get_digestbyname(argv[0])) { + f.type = FT_md; + f.func = dgst_main; + fp = &f; + } else if (EVP_get_cipherbyname(argv[0])) { + f.type = FT_cipher; + f.func = enc_main; + fp = &f; + } + } + if (fp != NULL) { + return (fp->func(argc, argv)); + } + if ((strncmp(argv[0], "no-", 3)) == 0) { + /* + * User is asking if foo is unsupported, by trying to "run" the + * no-foo command. Strange. + */ + f.name = argv[0] + 3; + if (lh_FUNCTION_retrieve(prog, &f) == NULL) { + BIO_printf(bio_out, "%s\n", argv[0]); + return (0); + } + BIO_printf(bio_out, "%s\n", argv[0] + 3); + return 1; + } + if (strcmp(argv[0], "quit") == 0 || strcmp(argv[0], "q") == 0 || + strcmp(argv[0], "exit") == 0 || strcmp(argv[0], "bye") == 0) + /* Special value to mean "exit the program. */ + return EXIT_THE_PROGRAM; + + BIO_printf(bio_err, "Invalid command '%s'; type \"help\" for a list.\n", + argv[0]); + return (1); +} + +static void list_pkey(void) +{ + int i; + + for (i = 0; i < EVP_PKEY_asn1_get_count(); i++) { + const EVP_PKEY_ASN1_METHOD *ameth; + int pkey_id, pkey_base_id, pkey_flags; + const char *pinfo, *pem_str; + ameth = EVP_PKEY_asn1_get0(i); + EVP_PKEY_asn1_get0_info(&pkey_id, &pkey_base_id, &pkey_flags, + &pinfo, &pem_str, ameth); + if (pkey_flags & ASN1_PKEY_ALIAS) { + BIO_printf(bio_out, "Name: %s\n", OBJ_nid2ln(pkey_id)); + BIO_printf(bio_out, "\tAlias for: %s\n", + OBJ_nid2ln(pkey_base_id)); + } else { + BIO_printf(bio_out, "Name: %s\n", pinfo); + BIO_printf(bio_out, "\tType: %s Algorithm\n", + pkey_flags & ASN1_PKEY_DYNAMIC ? + "External" : "Builtin"); + BIO_printf(bio_out, "\tOID: %s\n", OBJ_nid2ln(pkey_id)); + if (pem_str == NULL) + pem_str = "(none)"; + BIO_printf(bio_out, "\tPEM string: %s\n", pem_str); + } + + } +} + +static int function_cmp(const FUNCTION * a, const FUNCTION * b) +{ + return strncmp(a->name, b->name, 8); +} + +static unsigned long function_hash(const FUNCTION * a) +{ + return OPENSSL_LH_strhash(a->name); +} + +static int SortFnByName(const void *_f1, const void *_f2) +{ + const FUNCTION *f1 = _f1; + const FUNCTION *f2 = _f2; + + if (f1->type != f2->type) + return f1->type - f2->type; + return strcmp(f1->name, f2->name); +} + +static void list_disabled(void) +{ + BIO_puts(bio_out, "Disabled algorithms:\n"); +#ifdef OPENSSL_NO_SM9 + BIO_puts(bio_out, "SM9\n"); +#endif +#ifdef OPENSSL_NO_BF + BIO_puts(bio_out, "BF\n"); +#endif +#ifdef OPENSSL_NO_BLAKE2 + BIO_puts(bio_out, "BLAKE2\n"); +#endif +#ifdef OPENSSL_NO_CAMELLIA + BIO_puts(bio_out, "CAMELLIA\n"); +#endif +#ifdef OPENSSL_NO_CAST + BIO_puts(bio_out, "CAST\n"); +#endif +#ifdef OPENSSL_NO_CMAC + BIO_puts(bio_out, "CMAC\n"); +#endif +#ifdef OPENSSL_NO_CMS + BIO_puts(bio_out, "CMS\n"); +#endif +#ifdef OPENSSL_NO_COMP + BIO_puts(bio_out, "COMP\n"); +#endif +#ifdef OPENSSL_NO_DES + BIO_puts(bio_out, "DES\n"); +#endif +#ifdef OPENSSL_NO_DGRAM + BIO_puts(bio_out, "DGRAM\n"); +#endif +#ifdef OPENSSL_NO_DH + BIO_puts(bio_out, "DH\n"); +#endif +#ifdef OPENSSL_NO_DSA + BIO_puts(bio_out, "DSA\n"); +#endif +#if defined(OPENSSL_NO_DTLS) + BIO_puts(bio_out, "DTLS\n"); +#endif +#if defined(OPENSSL_NO_DTLS1) + BIO_puts(bio_out, "DTLS1\n"); +#endif +#if defined(OPENSSL_NO_DTLS1_2) + BIO_puts(bio_out, "DTLS1_2\n"); +#endif +#ifdef OPENSSL_NO_EC + BIO_puts(bio_out, "EC\n"); +#endif +#ifdef OPENSSL_NO_EC2M + BIO_puts(bio_out, "EC2M\n"); +#endif +#ifdef OPENSSL_NO_ENGINE + BIO_puts(bio_out, "ENGINE\n"); +#endif +#ifdef OPENSSL_NO_GOST + BIO_puts(bio_out, "GOST\n"); +#endif +#ifdef OPENSSL_NO_HEARTBEATS + BIO_puts(bio_out, "HEARTBEATS\n"); +#endif +#ifdef OPENSSL_NO_IDEA + BIO_puts(bio_out, "IDEA\n"); +#endif +#ifdef OPENSSL_NO_MD2 + BIO_puts(bio_out, "MD2\n"); +#endif +#ifdef OPENSSL_NO_MD4 + BIO_puts(bio_out, "MD4\n"); +#endif +#ifdef OPENSSL_NO_MD5 + BIO_puts(bio_out, "MD5\n"); +#endif +#ifdef OPENSSL_NO_MDC2 + BIO_puts(bio_out, "MDC2\n"); +#endif +#ifdef OPENSSL_NO_OCB + BIO_puts(bio_out, "OCB\n"); +#endif +#ifdef OPENSSL_NO_OCSP + BIO_puts(bio_out, "OCSP\n"); +#endif +#ifdef OPENSSL_NO_PSK + BIO_puts(bio_out, "PSK\n"); +#endif +#ifdef OPENSSL_NO_RC2 + BIO_puts(bio_out, "RC2\n"); +#endif +#ifdef OPENSSL_NO_RC4 + BIO_puts(bio_out, "RC4\n"); +#endif +#ifdef OPENSSL_NO_RC5 + BIO_puts(bio_out, "RC5\n"); +#endif +#ifdef OPENSSL_NO_RMD160 + BIO_puts(bio_out, "RMD160\n"); +#endif +#ifdef OPENSSL_NO_RSA + BIO_puts(bio_out, "RSA\n"); +#endif +#ifdef OPENSSL_NO_SCRYPT + BIO_puts(bio_out, "SCRYPT\n"); +#endif +#ifdef OPENSSL_NO_SCTP + BIO_puts(bio_out, "SCTP\n"); +#endif +#ifdef OPENSSL_NO_SEED + BIO_puts(bio_out, "SEED\n"); +#endif +#ifdef OPENSSL_NO_SOCK + BIO_puts(bio_out, "SOCK\n"); +#endif +#ifdef OPENSSL_NO_SRP + BIO_puts(bio_out, "SRP\n"); +#endif +#ifdef OPENSSL_NO_SRTP + BIO_puts(bio_out, "SRTP\n"); +#endif +#ifdef OPENSSL_NO_SSL3 + BIO_puts(bio_out, "SSL3\n"); +#endif +#ifdef OPENSSL_NO_TLS1 + BIO_puts(bio_out, "TLS1\n"); +#endif +#ifdef OPENSSL_NO_TLS1_1 + BIO_puts(bio_out, "TLS1_1\n"); +#endif +#ifdef OPENSSL_NO_TLS1_2 + BIO_puts(bio_out, "TLS1_2\n"); +#endif +#ifdef OPENSSL_NO_WHIRLPOOL + BIO_puts(bio_out, "WHIRLPOOL\n"); +#endif +#ifndef ZLIB + BIO_puts(bio_out, "ZLIB\n"); +#endif +} + +static LHASH_OF(FUNCTION) *prog_init(void) +{ + LHASH_OF(FUNCTION) *ret; + FUNCTION *f; + size_t i; + + /* Sort alphabetically within category. For nicer help displays. */ + for (i = 0, f = functions; f->name != NULL; ++f, ++i) ; + qsort(functions, i, sizeof(*functions), SortFnByName); + + if ((ret = lh_FUNCTION_new(function_hash, function_cmp)) == NULL) + return (NULL); + + for (f = functions; f->name != NULL; f++) + (void)lh_FUNCTION_insert(ret, f); + return (ret); +} diff --git a/apps/nseq.c b/apps/nseq.c new file mode 100644 index 000000000..018d5ebdb --- /dev/null +++ b/apps/nseq.c @@ -0,0 +1,113 @@ +/* + * Copyright 1999-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#include +#include "apps.h" +#include +#include + +typedef enum OPTION_choice { + OPT_ERR = -1, OPT_EOF = 0, OPT_HELP, + OPT_TOSEQ, OPT_IN, OPT_OUT +} OPTION_CHOICE; + +OPTIONS nseq_options[] = { + {"help", OPT_HELP, '-', "Display this summary"}, + {"toseq", OPT_TOSEQ, '-', "Output NS Sequence file"}, + {"in", OPT_IN, '<', "Input file"}, + {"out", OPT_OUT, '>', "Output file"}, + {NULL} +}; + +int nseq_main(int argc, char **argv) +{ + BIO *in = NULL, *out = NULL; + X509 *x509 = NULL; + NETSCAPE_CERT_SEQUENCE *seq = NULL; + OPTION_CHOICE o; + int toseq = 0, ret = 1, i; + char *infile = NULL, *outfile = NULL, *prog; + + prog = opt_init(argc, argv, nseq_options); + while ((o = opt_next()) != OPT_EOF) { + switch (o) { + case OPT_EOF: + case OPT_ERR: + opthelp: + BIO_printf(bio_err, "%s: Use -help for summary.\n", prog); + goto end; + case OPT_HELP: + ret = 0; + opt_help(nseq_options); + goto end; + case OPT_TOSEQ: + toseq = 1; + break; + case OPT_IN: + infile = opt_arg(); + break; + case OPT_OUT: + outfile = opt_arg(); + break; + } + } + argc = opt_num_rest(); + if (argc != 0) + goto opthelp; + + in = bio_open_default(infile, 'r', FORMAT_PEM); + if (in == NULL) + goto end; + out = bio_open_default(outfile, 'w', FORMAT_PEM); + if (out == NULL) + goto end; + + if (toseq) { + seq = NETSCAPE_CERT_SEQUENCE_new(); + if (seq == NULL) + goto end; + seq->certs = sk_X509_new_null(); + if (seq->certs == NULL) + goto end; + while ((x509 = PEM_read_bio_X509(in, NULL, NULL, NULL))) + sk_X509_push(seq->certs, x509); + + if (!sk_X509_num(seq->certs)) { + BIO_printf(bio_err, "%s: Error reading certs file %s\n", + prog, infile); + ERR_print_errors(bio_err); + goto end; + } + PEM_write_bio_NETSCAPE_CERT_SEQUENCE(out, seq); + ret = 0; + goto end; + } + + seq = PEM_read_bio_NETSCAPE_CERT_SEQUENCE(in, NULL, NULL, NULL); + if (seq == NULL) { + BIO_printf(bio_err, "%s: Error reading sequence file %s\n", + prog, infile); + ERR_print_errors(bio_err); + goto end; + } + + for (i = 0; i < sk_X509_num(seq->certs); i++) { + x509 = sk_X509_value(seq->certs, i); + dump_cert_text(out, x509); + PEM_write_bio_X509(out, x509); + } + ret = 0; + end: + BIO_free(in); + BIO_free_all(out); + NETSCAPE_CERT_SEQUENCE_free(seq); + + return (ret); +} diff --git a/apps/ocsp.c b/apps/ocsp.c new file mode 100644 index 000000000..41ea97030 --- /dev/null +++ b/apps/ocsp.c @@ -0,0 +1,1290 @@ +/* + * Copyright 2001-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include + +#ifdef OPENSSL_NO_OCSP +NON_EMPTY_TRANSLATION_UNIT +#else +# ifdef OPENSSL_SYS_VMS +# define _XOPEN_SOURCE_EXTENDED/* So fd_set and friends get properly defined + * on OpenVMS */ +# endif + +# define USE_SOCKETS + +# include +# include +# include +# include +# include + +/* Needs to be included before the openssl headers */ +# include "apps.h" +# include +# include +# include +# include +# include +# include +# include + +# if defined(NETWARE_CLIB) +# ifdef NETWARE_BSDSOCK +# include +# include +# else +# include +# endif +# elif defined(NETWARE_LIBC) +# ifdef NETWARE_BSDSOCK +# include +# else +# include +# endif +# endif + +/* Maximum leeway in validity period: default 5 minutes */ +# define MAX_VALIDITY_PERIOD (5 * 60) + +static int add_ocsp_cert(OCSP_REQUEST **req, X509 *cert, + const EVP_MD *cert_id_md, X509 *issuer, + STACK_OF(OCSP_CERTID) *ids); +static int add_ocsp_serial(OCSP_REQUEST **req, char *serial, + const EVP_MD *cert_id_md, X509 *issuer, + STACK_OF(OCSP_CERTID) *ids); +static void print_ocsp_summary(BIO *out, OCSP_BASICRESP *bs, OCSP_REQUEST *req, + STACK_OF(OPENSSL_STRING) *names, + STACK_OF(OCSP_CERTID) *ids, long nsec, + long maxage); +static void make_ocsp_response(OCSP_RESPONSE **resp, OCSP_REQUEST *req, + CA_DB *db, X509 *ca, X509 *rcert, + EVP_PKEY *rkey, const EVP_MD *md, + STACK_OF(X509) *rother, unsigned long flags, + int nmin, int ndays, int badsig); + +static char **lookup_serial(CA_DB *db, ASN1_INTEGER *ser); +static BIO *init_responder(const char *port); +static int do_responder(OCSP_REQUEST **preq, BIO **pcbio, BIO *acbio); +static int send_ocsp_response(BIO *cbio, OCSP_RESPONSE *resp); + +# ifndef OPENSSL_NO_SOCK +static OCSP_RESPONSE *query_responder(BIO *cbio, const char *host, + const char *path, + const STACK_OF(CONF_VALUE) *headers, + OCSP_REQUEST *req, int req_timeout); +# endif + +typedef enum OPTION_choice { + OPT_ERR = -1, OPT_EOF = 0, OPT_HELP, + OPT_OUTFILE, OPT_TIMEOUT, OPT_URL, OPT_HOST, OPT_PORT, + OPT_IGNORE_ERR, OPT_NOVERIFY, OPT_NONCE, OPT_NO_NONCE, + OPT_RESP_NO_CERTS, OPT_RESP_KEY_ID, OPT_NO_CERTS, + OPT_NO_SIGNATURE_VERIFY, OPT_NO_CERT_VERIFY, OPT_NO_CHAIN, + OPT_NO_CERT_CHECKS, OPT_NO_EXPLICIT, OPT_TRUST_OTHER, + OPT_NO_INTERN, OPT_BADSIG, OPT_TEXT, OPT_REQ_TEXT, OPT_RESP_TEXT, + OPT_REQIN, OPT_RESPIN, OPT_SIGNER, OPT_VAFILE, OPT_SIGN_OTHER, + OPT_VERIFY_OTHER, OPT_CAFILE, OPT_CAPATH, OPT_NOCAFILE, OPT_NOCAPATH, + OPT_VALIDITY_PERIOD, OPT_STATUS_AGE, OPT_SIGNKEY, OPT_REQOUT, + OPT_RESPOUT, OPT_PATH, OPT_ISSUER, OPT_CERT, OPT_SERIAL, + OPT_INDEX, OPT_CA, OPT_NMIN, OPT_REQUEST, OPT_NDAYS, OPT_RSIGNER, + OPT_RKEY, OPT_ROTHER, OPT_RMD, OPT_HEADER, + OPT_V_ENUM, + OPT_MD +} OPTION_CHOICE; + +OPTIONS ocsp_options[] = { + {"help", OPT_HELP, '-', "Display this summary"}, + {"out", OPT_OUTFILE, '>', "Output filename"}, + {"timeout", OPT_TIMEOUT, 'p', + "Connection timeout (in seconds) to the OCSP responder"}, + {"url", OPT_URL, 's', "Responder URL"}, + {"host", OPT_HOST, 's', "TCP/IP hostname:port to connect to"}, + {"port", OPT_PORT, 'p', "Port to run responder on"}, + {"ignore_err", OPT_IGNORE_ERR, '-', + "Ignore Error response from OCSP responder, and retry "}, + {"noverify", OPT_NOVERIFY, '-', "Don't verify response at all"}, + {"nonce", OPT_NONCE, '-', "Add OCSP nonce to request"}, + {"no_nonce", OPT_NO_NONCE, '-', "Don't add OCSP nonce to request"}, + {"resp_no_certs", OPT_RESP_NO_CERTS, '-', + "Don't include any certificates in response"}, + {"resp_key_id", OPT_RESP_KEY_ID, '-', + "Identify response by signing certificate key ID"}, + {"no_certs", OPT_NO_CERTS, '-', + "Don't include any certificates in signed request"}, + {"no_signature_verify", OPT_NO_SIGNATURE_VERIFY, '-', + "Don't check signature on response"}, + {"no_cert_verify", OPT_NO_CERT_VERIFY, '-', + "Don't check signing certificate"}, + {"no_chain", OPT_NO_CHAIN, '-', "Don't chain verify response"}, + {"no_cert_checks", OPT_NO_CERT_CHECKS, '-', + "Don't do additional checks on signing certificate"}, + {"no_explicit", OPT_NO_EXPLICIT, '-', + "Do not explicitly check the chain, just verify the root"}, + {"trust_other", OPT_TRUST_OTHER, '-', + "Don't verify additional certificates"}, + {"no_intern", OPT_NO_INTERN, '-', + "Don't search certificates contained in response for signer"}, + {"badsig", OPT_BADSIG, '-', + "Corrupt last byte of loaded OSCP response signature (for test)"}, + {"text", OPT_TEXT, '-', "Print text form of request and response"}, + {"req_text", OPT_REQ_TEXT, '-', "Print text form of request"}, + {"resp_text", OPT_RESP_TEXT, '-', "Print text form of response"}, + {"reqin", OPT_REQIN, 's', "File with the DER-encoded request"}, + {"respin", OPT_RESPIN, 's', "File with the DER-encoded response"}, + {"signer", OPT_SIGNER, '<', "Certificate to sign OCSP request with"}, + {"VAfile", OPT_VAFILE, '<', "Validator certificates file"}, + {"sign_other", OPT_SIGN_OTHER, '<', + "Additional certificates to include in signed request"}, + {"verify_other", OPT_VERIFY_OTHER, '<', + "Additional certificates to search for signer"}, + {"CAfile", OPT_CAFILE, '<', "Trusted certificates file"}, + {"CApath", OPT_CAPATH, '<', "Trusted certificates directory"}, + {"no-CAfile", OPT_NOCAFILE, '-', + "Do not load the default certificates file"}, + {"no-CApath", OPT_NOCAPATH, '-', + "Do not load certificates from the default certificates directory"}, + {"validity_period", OPT_VALIDITY_PERIOD, 'u', + "Maximum validity discrepancy in seconds"}, + {"status_age", OPT_STATUS_AGE, 'p', "Maximum status age in seconds"}, + {"signkey", OPT_SIGNKEY, 's', "Private key to sign OCSP request with"}, + {"reqout", OPT_REQOUT, 's', "Output file for the DER-encoded request"}, + {"respout", OPT_RESPOUT, 's', "Output file for the DER-encoded response"}, + {"path", OPT_PATH, 's', "Path to use in OCSP request"}, + {"issuer", OPT_ISSUER, '<', "Issuer certificate"}, + {"cert", OPT_CERT, '<', "Certificate to check"}, + {"serial", OPT_SERIAL, 's', "Serial number to check"}, + {"index", OPT_INDEX, '<', "Certificate status index file"}, + {"CA", OPT_CA, '<', "CA certificate"}, + {"nmin", OPT_NMIN, 'p', "Number of minutes before next update"}, + {"nrequest", OPT_REQUEST, 'p', + "Number of requests to accept (default unlimited)"}, + {"ndays", OPT_NDAYS, 'p', "Number of days before next update"}, + {"rsigner", OPT_RSIGNER, '<', + "Responder certificate to sign responses with"}, + {"rkey", OPT_RKEY, '<', "Responder key to sign responses with"}, + {"rother", OPT_ROTHER, '<', "Other certificates to include in response"}, + {"rmd", OPT_RMD, 's', "Digest Algorithm to use in signature of OCSP response"}, + {"header", OPT_HEADER, 's', "key=value header to add"}, + {"", OPT_MD, '-', "Any supported digest algorithm (sha1,sha256, ... )"}, + OPT_V_OPTIONS, + {NULL} +}; + +int ocsp_main(int argc, char **argv) +{ + BIO *acbio = NULL, *cbio = NULL, *derbio = NULL, *out = NULL; + const EVP_MD *cert_id_md = NULL, *rsign_md = NULL; + int trailing_md = 0; + CA_DB *rdb = NULL; + EVP_PKEY *key = NULL, *rkey = NULL; + OCSP_BASICRESP *bs = NULL; + OCSP_REQUEST *req = NULL; + OCSP_RESPONSE *resp = NULL; + STACK_OF(CONF_VALUE) *headers = NULL; + STACK_OF(OCSP_CERTID) *ids = NULL; + STACK_OF(OPENSSL_STRING) *reqnames = NULL; + STACK_OF(X509) *sign_other = NULL, *verify_other = NULL, *rother = NULL; + STACK_OF(X509) *issuers = NULL; + X509 *issuer = NULL, *cert = NULL, *rca_cert = NULL; + X509 *signer = NULL, *rsigner = NULL; + X509_STORE *store = NULL; + X509_VERIFY_PARAM *vpm = NULL; + const char *CAfile = NULL, *CApath = NULL; + char *header, *value; + char *host = NULL, *port = NULL, *path = "/", *outfile = NULL; + char *rca_filename = NULL, *reqin = NULL, *respin = NULL; + char *reqout = NULL, *respout = NULL, *ridx_filename = NULL; + char *rsignfile = NULL, *rkeyfile = NULL; + char *sign_certfile = NULL, *verify_certfile = NULL, *rcertfile = NULL; + char *signfile = NULL, *keyfile = NULL; + char *thost = NULL, *tport = NULL, *tpath = NULL; + int noCAfile = 0, noCApath = 0; + int accept_count = -1, add_nonce = 1, noverify = 0, use_ssl = -1; + int vpmtouched = 0, badsig = 0, i, ignore_err = 0, nmin = 0, ndays = -1; + int req_text = 0, resp_text = 0, ret = 1; +#ifndef OPENSSL_NO_SOCK + int req_timeout = -1; +#endif + long nsec = MAX_VALIDITY_PERIOD, maxage = -1; + unsigned long sign_flags = 0, verify_flags = 0, rflags = 0; + OPTION_CHOICE o; + char *prog; + + reqnames = sk_OPENSSL_STRING_new_null(); + if (!reqnames) + goto end; + ids = sk_OCSP_CERTID_new_null(); + if (!ids) + goto end; + if ((vpm = X509_VERIFY_PARAM_new()) == NULL) + return 1; + + prog = opt_init(argc, argv, ocsp_options); + while ((o = opt_next()) != OPT_EOF) { + switch (o) { + case OPT_EOF: + case OPT_ERR: + opthelp: + BIO_printf(bio_err, "%s: Use -help for summary.\n", prog); + goto end; + case OPT_HELP: + ret = 0; + opt_help(ocsp_options); + goto end; + case OPT_OUTFILE: + outfile = opt_arg(); + break; + case OPT_TIMEOUT: +#ifndef OPENSSL_NO_SOCK + req_timeout = atoi(opt_arg()); +#endif + break; + case OPT_URL: + OPENSSL_free(thost); + OPENSSL_free(tport); + OPENSSL_free(tpath); + thost = tport = tpath = NULL; + if (!OCSP_parse_url(opt_arg(), &host, &port, &path, &use_ssl)) { + BIO_printf(bio_err, "%s Error parsing URL\n", prog); + goto end; + } + thost = host; + tport = port; + tpath = path; + break; + case OPT_HOST: + host = opt_arg(); + break; + case OPT_PORT: + port = opt_arg(); + break; + case OPT_IGNORE_ERR: + ignore_err = 1; + break; + case OPT_NOVERIFY: + noverify = 1; + break; + case OPT_NONCE: + add_nonce = 2; + break; + case OPT_NO_NONCE: + add_nonce = 0; + break; + case OPT_RESP_NO_CERTS: + rflags |= OCSP_NOCERTS; + break; + case OPT_RESP_KEY_ID: + rflags |= OCSP_RESPID_KEY; + break; + case OPT_NO_CERTS: + sign_flags |= OCSP_NOCERTS; + break; + case OPT_NO_SIGNATURE_VERIFY: + verify_flags |= OCSP_NOSIGS; + break; + case OPT_NO_CERT_VERIFY: + verify_flags |= OCSP_NOVERIFY; + break; + case OPT_NO_CHAIN: + verify_flags |= OCSP_NOCHAIN; + break; + case OPT_NO_CERT_CHECKS: + verify_flags |= OCSP_NOCHECKS; + break; + case OPT_NO_EXPLICIT: + verify_flags |= OCSP_NOEXPLICIT; + break; + case OPT_TRUST_OTHER: + verify_flags |= OCSP_TRUSTOTHER; + break; + case OPT_NO_INTERN: + verify_flags |= OCSP_NOINTERN; + break; + case OPT_BADSIG: + badsig = 1; + break; + case OPT_TEXT: + req_text = resp_text = 1; + break; + case OPT_REQ_TEXT: + req_text = 1; + break; + case OPT_RESP_TEXT: + resp_text = 1; + break; + case OPT_REQIN: + reqin = opt_arg(); + break; + case OPT_RESPIN: + respin = opt_arg(); + break; + case OPT_SIGNER: + signfile = opt_arg(); + break; + case OPT_VAFILE: + verify_certfile = opt_arg(); + verify_flags |= OCSP_TRUSTOTHER; + break; + case OPT_SIGN_OTHER: + sign_certfile = opt_arg(); + break; + case OPT_VERIFY_OTHER: + verify_certfile = opt_arg(); + break; + case OPT_CAFILE: + CAfile = opt_arg(); + break; + case OPT_CAPATH: + CApath = opt_arg(); + break; + case OPT_NOCAFILE: + noCAfile = 1; + break; + case OPT_NOCAPATH: + noCApath = 1; + break; + case OPT_V_CASES: + if (!opt_verify(o, vpm)) + goto end; + vpmtouched++; + break; + case OPT_VALIDITY_PERIOD: + opt_long(opt_arg(), &nsec); + break; + case OPT_STATUS_AGE: + opt_long(opt_arg(), &maxage); + break; + case OPT_SIGNKEY: + keyfile = opt_arg(); + break; + case OPT_REQOUT: + reqout = opt_arg(); + break; + case OPT_RESPOUT: + respout = opt_arg(); + break; + case OPT_PATH: + path = opt_arg(); + break; + case OPT_ISSUER: + issuer = load_cert(opt_arg(), FORMAT_PEM, "issuer certificate"); + if (issuer == NULL) + goto end; + if (issuers == NULL) { + if ((issuers = sk_X509_new_null()) == NULL) + goto end; + } + sk_X509_push(issuers, issuer); + break; + case OPT_CERT: + X509_free(cert); + cert = load_cert(opt_arg(), FORMAT_PEM, "certificate"); + if (cert == NULL) + goto end; + if (cert_id_md == NULL) + cert_id_md = EVP_sha1(); + if (!add_ocsp_cert(&req, cert, cert_id_md, issuer, ids)) + goto end; + if (!sk_OPENSSL_STRING_push(reqnames, opt_arg())) + goto end; + trailing_md = 0; + break; + case OPT_SERIAL: + if (cert_id_md == NULL) + cert_id_md = EVP_sha1(); + if (!add_ocsp_serial(&req, opt_arg(), cert_id_md, issuer, ids)) + goto end; + if (!sk_OPENSSL_STRING_push(reqnames, opt_arg())) + goto end; + trailing_md = 0; + break; + case OPT_INDEX: + ridx_filename = opt_arg(); + break; + case OPT_CA: + rca_filename = opt_arg(); + break; + case OPT_NMIN: + opt_int(opt_arg(), &nmin); + if (ndays == -1) + ndays = 0; + break; + case OPT_REQUEST: + opt_int(opt_arg(), &accept_count); + break; + case OPT_NDAYS: + ndays = atoi(opt_arg()); + break; + case OPT_RSIGNER: + rsignfile = opt_arg(); + break; + case OPT_RKEY: + rkeyfile = opt_arg(); + break; + case OPT_ROTHER: + rcertfile = opt_arg(); + break; + case OPT_RMD: /* Response MessageDigest */ + if (!opt_md(opt_arg(), &rsign_md)) + goto end; + break; + case OPT_HEADER: + header = opt_arg(); + value = strchr(header, '='); + if (value == NULL) { + BIO_printf(bio_err, "Missing = in header key=value\n"); + goto opthelp; + } + *value++ = '\0'; + if (!X509V3_add_value(header, value, &headers)) + goto end; + break; + case OPT_MD: + if (trailing_md) { + BIO_printf(bio_err, + "%s: Digest must be before -cert or -serial\n", + prog); + goto opthelp; + } + if (!opt_md(opt_unknown(), &cert_id_md)) + goto opthelp; + trailing_md = 1; + break; + } + } + + if (trailing_md) { + BIO_printf(bio_err, "%s: Digest must be before -cert or -serial\n", + prog); + goto opthelp; + } + argc = opt_num_rest(); + if (argc != 0) + goto opthelp; + + /* Have we anything to do? */ + if (!req && !reqin && !respin && !(port && ridx_filename)) + goto opthelp; + + out = bio_open_default(outfile, 'w', FORMAT_TEXT); + if (out == NULL) + goto end; + + if (!req && (add_nonce != 2)) + add_nonce = 0; + + if (!req && reqin) { + derbio = bio_open_default(reqin, 'r', FORMAT_ASN1); + if (derbio == NULL) + goto end; + req = d2i_OCSP_REQUEST_bio(derbio, NULL); + BIO_free(derbio); + if (!req) { + BIO_printf(bio_err, "Error reading OCSP request\n"); + goto end; + } + } + + if (!req && port) { + acbio = init_responder(port); + if (!acbio) + goto end; + } + + if (rsignfile) { + if (!rkeyfile) + rkeyfile = rsignfile; + rsigner = load_cert(rsignfile, FORMAT_PEM, "responder certificate"); + if (!rsigner) { + BIO_printf(bio_err, "Error loading responder certificate\n"); + goto end; + } + rca_cert = load_cert(rca_filename, FORMAT_PEM, "CA certificate"); + if (rcertfile) { + if (!load_certs(rcertfile, &rother, FORMAT_PEM, NULL, + "responder other certificates")) + goto end; + } + rkey = load_key(rkeyfile, FORMAT_PEM, 0, NULL, NULL, + "responder private key"); + if (!rkey) + goto end; + } + if (acbio) + BIO_printf(bio_err, "Waiting for OCSP client connections...\n"); + + redo_accept: + + if (acbio) { + if (!do_responder(&req, &cbio, acbio)) + goto end; + if (!req) { + resp = + OCSP_response_create(OCSP_RESPONSE_STATUS_MALFORMEDREQUEST, + NULL); + send_ocsp_response(cbio, resp); + goto done_resp; + } + } + + if (!req && (signfile || reqout || host || add_nonce || ridx_filename)) { + BIO_printf(bio_err, "Need an OCSP request for this operation!\n"); + goto end; + } + + if (req && add_nonce) + OCSP_request_add1_nonce(req, NULL, -1); + + if (signfile) { + if (!keyfile) + keyfile = signfile; + signer = load_cert(signfile, FORMAT_PEM, "signer certificate"); + if (!signer) { + BIO_printf(bio_err, "Error loading signer certificate\n"); + goto end; + } + if (sign_certfile) { + if (!load_certs(sign_certfile, &sign_other, FORMAT_PEM, NULL, + "signer certificates")) + goto end; + } + key = load_key(keyfile, FORMAT_PEM, 0, NULL, NULL, + "signer private key"); + if (!key) + goto end; + + if (!OCSP_request_sign + (req, signer, key, NULL, sign_other, sign_flags)) { + BIO_printf(bio_err, "Error signing OCSP request\n"); + goto end; + } + } + + if (req_text && req) + OCSP_REQUEST_print(out, req, 0); + + if (reqout) { + derbio = bio_open_default(reqout, 'w', FORMAT_ASN1); + if (derbio == NULL) + goto end; + i2d_OCSP_REQUEST_bio(derbio, req); + BIO_free(derbio); + } + + if (ridx_filename && (!rkey || !rsigner || !rca_cert)) { + BIO_printf(bio_err, + "Need a responder certificate, key and CA for this operation!\n"); + goto end; + } + + if (ridx_filename && !rdb) { + rdb = load_index(ridx_filename, NULL); + if (!rdb) + goto end; + if (!index_index(rdb)) + goto end; + } + + if (rdb) { + make_ocsp_response(&resp, req, rdb, rca_cert, rsigner, rkey, + rsign_md, rother, rflags, nmin, ndays, badsig); + if (cbio) + send_ocsp_response(cbio, resp); + } else if (host) { +# ifndef OPENSSL_NO_SOCK + resp = process_responder(req, host, path, + port, use_ssl, headers, req_timeout); + if (!resp) + goto end; +# else + BIO_printf(bio_err, + "Error creating connect BIO - sockets not supported.\n"); + goto end; +# endif + } else if (respin) { + derbio = bio_open_default(respin, 'r', FORMAT_ASN1); + if (derbio == NULL) + goto end; + resp = d2i_OCSP_RESPONSE_bio(derbio, NULL); + BIO_free(derbio); + if (!resp) { + BIO_printf(bio_err, "Error reading OCSP response\n"); + goto end; + } + } else { + ret = 0; + goto end; + } + + done_resp: + + if (respout) { + derbio = bio_open_default(respout, 'w', FORMAT_ASN1); + if (derbio == NULL) + goto end; + i2d_OCSP_RESPONSE_bio(derbio, resp); + BIO_free(derbio); + } + + i = OCSP_response_status(resp); + if (i != OCSP_RESPONSE_STATUS_SUCCESSFUL) { + BIO_printf(out, "Responder Error: %s (%d)\n", + OCSP_response_status_str(i), i); + if (ignore_err) + goto redo_accept; + ret = 0; + goto end; + } + + if (resp_text) + OCSP_RESPONSE_print(out, resp, 0); + + /* If running as responder don't verify our own response */ + if (cbio) { + /* If not unlimited, see if we took all we should. */ + if (accept_count != -1 && --accept_count <= 0) { + ret = 0; + goto end; + } + BIO_free_all(cbio); + cbio = NULL; + OCSP_REQUEST_free(req); + req = NULL; + OCSP_RESPONSE_free(resp); + resp = NULL; + goto redo_accept; + } + if (ridx_filename) { + ret = 0; + goto end; + } + + if (!store) { + store = setup_verify(CAfile, CApath, noCAfile, noCApath); + if (!store) + goto end; + } + if (vpmtouched) + X509_STORE_set1_param(store, vpm); + if (verify_certfile) { + if (!load_certs(verify_certfile, &verify_other, FORMAT_PEM, NULL, + "validator certificate")) + goto end; + } + + bs = OCSP_response_get1_basic(resp); + if (!bs) { + BIO_printf(bio_err, "Error parsing response\n"); + goto end; + } + + ret = 0; + + if (!noverify) { + if (req && ((i = OCSP_check_nonce(req, bs)) <= 0)) { + if (i == -1) + BIO_printf(bio_err, "WARNING: no nonce in response\n"); + else { + BIO_printf(bio_err, "Nonce Verify error\n"); + ret = 1; + goto end; + } + } + + i = OCSP_basic_verify(bs, verify_other, store, verify_flags); + if (i <= 0 && issuers) { + i = OCSP_basic_verify(bs, issuers, store, OCSP_TRUSTOTHER); + if (i > 0) + ERR_clear_error(); + } + if (i <= 0) { + BIO_printf(bio_err, "Response Verify Failure\n"); + ERR_print_errors(bio_err); + ret = 1; + } else + BIO_printf(bio_err, "Response verify OK\n"); + + } + + print_ocsp_summary(out, bs, req, reqnames, ids, nsec, maxage); + + end: + ERR_print_errors(bio_err); + X509_free(signer); + X509_STORE_free(store); + X509_VERIFY_PARAM_free(vpm); + EVP_PKEY_free(key); + EVP_PKEY_free(rkey); + X509_free(cert); + sk_X509_pop_free(issuers, X509_free); + X509_free(rsigner); + X509_free(rca_cert); + free_index(rdb); + BIO_free_all(cbio); + BIO_free_all(acbio); + BIO_free(out); + OCSP_REQUEST_free(req); + OCSP_RESPONSE_free(resp); + OCSP_BASICRESP_free(bs); + sk_OPENSSL_STRING_free(reqnames); + sk_OCSP_CERTID_free(ids); + sk_X509_pop_free(sign_other, X509_free); + sk_X509_pop_free(verify_other, X509_free); + sk_CONF_VALUE_pop_free(headers, X509V3_conf_free); + OPENSSL_free(thost); + OPENSSL_free(tport); + OPENSSL_free(tpath); + + return (ret); +} + +static int add_ocsp_cert(OCSP_REQUEST **req, X509 *cert, + const EVP_MD *cert_id_md, X509 *issuer, + STACK_OF(OCSP_CERTID) *ids) +{ + OCSP_CERTID *id; + if (!issuer) { + BIO_printf(bio_err, "No issuer certificate specified\n"); + return 0; + } + if (*req == NULL) + *req = OCSP_REQUEST_new(); + if (*req == NULL) + goto err; + id = OCSP_cert_to_id(cert_id_md, cert, issuer); + if (!id || !sk_OCSP_CERTID_push(ids, id)) + goto err; + if (!OCSP_request_add0_id(*req, id)) + goto err; + return 1; + + err: + BIO_printf(bio_err, "Error Creating OCSP request\n"); + return 0; +} + +static int add_ocsp_serial(OCSP_REQUEST **req, char *serial, + const EVP_MD *cert_id_md, X509 *issuer, + STACK_OF(OCSP_CERTID) *ids) +{ + OCSP_CERTID *id; + X509_NAME *iname; + ASN1_BIT_STRING *ikey; + ASN1_INTEGER *sno; + if (!issuer) { + BIO_printf(bio_err, "No issuer certificate specified\n"); + return 0; + } + if (*req == NULL) + *req = OCSP_REQUEST_new(); + if (*req == NULL) + goto err; + iname = X509_get_subject_name(issuer); + ikey = X509_get0_pubkey_bitstr(issuer); + sno = s2i_ASN1_INTEGER(NULL, serial); + if (!sno) { + BIO_printf(bio_err, "Error converting serial number %s\n", serial); + return 0; + } + id = OCSP_cert_id_new(cert_id_md, iname, ikey, sno); + ASN1_INTEGER_free(sno); + if (id == NULL || !sk_OCSP_CERTID_push(ids, id)) + goto err; + if (!OCSP_request_add0_id(*req, id)) + goto err; + return 1; + + err: + BIO_printf(bio_err, "Error Creating OCSP request\n"); + return 0; +} + +static void print_ocsp_summary(BIO *out, OCSP_BASICRESP *bs, OCSP_REQUEST *req, + STACK_OF(OPENSSL_STRING) *names, + STACK_OF(OCSP_CERTID) *ids, long nsec, + long maxage) +{ + OCSP_CERTID *id; + const char *name; + int i, status, reason; + ASN1_GENERALIZEDTIME *rev, *thisupd, *nextupd; + + if (!bs || !req || !sk_OPENSSL_STRING_num(names) + || !sk_OCSP_CERTID_num(ids)) + return; + + for (i = 0; i < sk_OCSP_CERTID_num(ids); i++) { + id = sk_OCSP_CERTID_value(ids, i); + name = sk_OPENSSL_STRING_value(names, i); + BIO_printf(out, "%s: ", name); + + if (!OCSP_resp_find_status(bs, id, &status, &reason, + &rev, &thisupd, &nextupd)) { + BIO_puts(out, "ERROR: No Status found.\n"); + continue; + } + + /* + * Check validity: if invalid write to output BIO so we know which + * response this refers to. + */ + if (!OCSP_check_validity(thisupd, nextupd, nsec, maxage)) { + BIO_puts(out, "WARNING: Status times invalid.\n"); + ERR_print_errors(out); + } + BIO_printf(out, "%s\n", OCSP_cert_status_str(status)); + + BIO_puts(out, "\tThis Update: "); + ASN1_GENERALIZEDTIME_print(out, thisupd); + BIO_puts(out, "\n"); + + if (nextupd) { + BIO_puts(out, "\tNext Update: "); + ASN1_GENERALIZEDTIME_print(out, nextupd); + BIO_puts(out, "\n"); + } + + if (status != V_OCSP_CERTSTATUS_REVOKED) + continue; + + if (reason != -1) + BIO_printf(out, "\tReason: %s\n", OCSP_crl_reason_str(reason)); + + BIO_puts(out, "\tRevocation Time: "); + ASN1_GENERALIZEDTIME_print(out, rev); + BIO_puts(out, "\n"); + } +} + +static void make_ocsp_response(OCSP_RESPONSE **resp, OCSP_REQUEST *req, + CA_DB *db, X509 *ca, X509 *rcert, + EVP_PKEY *rkey, const EVP_MD *rmd, + STACK_OF(X509) *rother, unsigned long flags, + int nmin, int ndays, int badsig) +{ + ASN1_TIME *thisupd = NULL, *nextupd = NULL; + OCSP_CERTID *cid, *ca_id = NULL; + OCSP_BASICRESP *bs = NULL; + int i, id_count; + + id_count = OCSP_request_onereq_count(req); + + if (id_count <= 0) { + *resp = + OCSP_response_create(OCSP_RESPONSE_STATUS_MALFORMEDREQUEST, NULL); + goto end; + } + + bs = OCSP_BASICRESP_new(); + thisupd = X509_gmtime_adj(NULL, 0); + if (ndays != -1) + nextupd = X509_time_adj_ex(NULL, ndays, nmin * 60, NULL); + + /* Examine each certificate id in the request */ + for (i = 0; i < id_count; i++) { + OCSP_ONEREQ *one; + ASN1_INTEGER *serial; + char **inf; + ASN1_OBJECT *cert_id_md_oid; + const EVP_MD *cert_id_md; + one = OCSP_request_onereq_get0(req, i); + cid = OCSP_onereq_get0_id(one); + + OCSP_id_get0_info(NULL, &cert_id_md_oid, NULL, NULL, cid); + + cert_id_md = EVP_get_digestbyobj(cert_id_md_oid); + if (!cert_id_md) { + *resp = OCSP_response_create(OCSP_RESPONSE_STATUS_INTERNALERROR, + NULL); + goto end; + } + OCSP_CERTID_free(ca_id); + ca_id = OCSP_cert_to_id(cert_id_md, NULL, ca); + + /* Is this request about our CA? */ + if (OCSP_id_issuer_cmp(ca_id, cid)) { + OCSP_basic_add1_status(bs, cid, + V_OCSP_CERTSTATUS_UNKNOWN, + 0, NULL, thisupd, nextupd); + continue; + } + OCSP_id_get0_info(NULL, NULL, NULL, &serial, cid); + inf = lookup_serial(db, serial); + if (!inf) + OCSP_basic_add1_status(bs, cid, + V_OCSP_CERTSTATUS_UNKNOWN, + 0, NULL, thisupd, nextupd); + else if (inf[DB_type][0] == DB_TYPE_VAL) + OCSP_basic_add1_status(bs, cid, + V_OCSP_CERTSTATUS_GOOD, + 0, NULL, thisupd, nextupd); + else if (inf[DB_type][0] == DB_TYPE_REV) { + ASN1_OBJECT *inst = NULL; + ASN1_TIME *revtm = NULL; + ASN1_GENERALIZEDTIME *invtm = NULL; + OCSP_SINGLERESP *single; + int reason = -1; + unpack_revinfo(&revtm, &reason, &inst, &invtm, inf[DB_rev_date]); + single = OCSP_basic_add1_status(bs, cid, + V_OCSP_CERTSTATUS_REVOKED, + reason, revtm, thisupd, nextupd); + if (invtm) + OCSP_SINGLERESP_add1_ext_i2d(single, NID_invalidity_date, + invtm, 0, 0); + else if (inst) + OCSP_SINGLERESP_add1_ext_i2d(single, + NID_hold_instruction_code, inst, + 0, 0); + ASN1_OBJECT_free(inst); + ASN1_TIME_free(revtm); + ASN1_GENERALIZEDTIME_free(invtm); + } + } + + OCSP_copy_nonce(bs, req); + + OCSP_basic_sign(bs, rcert, rkey, rmd, rother, flags); + + if (badsig) { + const ASN1_OCTET_STRING *sig = OCSP_resp_get0_signature(bs); + corrupt_signature(sig); + } + + *resp = OCSP_response_create(OCSP_RESPONSE_STATUS_SUCCESSFUL, bs); + + end: + ASN1_TIME_free(thisupd); + ASN1_TIME_free(nextupd); + OCSP_CERTID_free(ca_id); + OCSP_BASICRESP_free(bs); +} + +static char **lookup_serial(CA_DB *db, ASN1_INTEGER *ser) +{ + int i; + BIGNUM *bn = NULL; + char *itmp, *row[DB_NUMBER], **rrow; + for (i = 0; i < DB_NUMBER; i++) + row[i] = NULL; + bn = ASN1_INTEGER_to_BN(ser, NULL); + OPENSSL_assert(bn); /* FIXME: should report an error at this + * point and abort */ + if (BN_is_zero(bn)) + itmp = OPENSSL_strdup("00"); + else + itmp = BN_bn2hex(bn); + row[DB_serial] = itmp; + BN_free(bn); + rrow = TXT_DB_get_by_index(db->db, DB_serial, row); + OPENSSL_free(itmp); + return rrow; +} + +/* Quick and dirty OCSP server: read in and parse input request */ + +static BIO *init_responder(const char *port) +{ +# ifdef OPENSSL_NO_SOCK + BIO_printf(bio_err, + "Error setting up accept BIO - sockets not supported.\n"); + return NULL; +# else + BIO *acbio = NULL, *bufbio = NULL; + + bufbio = BIO_new(BIO_f_buffer()); + if (bufbio == NULL) + goto err; + acbio = BIO_new(BIO_s_accept()); + if (acbio == NULL + || BIO_set_bind_mode(acbio, BIO_BIND_REUSEADDR) < 0 + || BIO_set_accept_port(acbio, port) < 0) { + BIO_printf(bio_err, "Error setting up accept BIO\n"); + ERR_print_errors(bio_err); + goto err; + } + + BIO_set_accept_bios(acbio, bufbio); + bufbio = NULL; + if (BIO_do_accept(acbio) <= 0) { + BIO_printf(bio_err, "Error starting accept\n"); + ERR_print_errors(bio_err); + goto err; + } + + return acbio; + + err: + BIO_free_all(acbio); + BIO_free(bufbio); + return NULL; +# endif +} + +# ifndef OPENSSL_NO_SOCK +/* + * Decode %xx URL-decoding in-place. Ignores mal-formed sequences. + */ +static int urldecode(char *p) +{ + unsigned char *out = (unsigned char *)p; + unsigned char *save = out; + + for (; *p; p++) { + if (*p != '%') + *out++ = *p; + else if (isxdigit(_UC(p[1])) && isxdigit(_UC(p[2]))) { + /* Don't check, can't fail because of ixdigit() call. */ + *out++ = (OPENSSL_hexchar2int(p[1]) << 4) + | OPENSSL_hexchar2int(p[2]); + p += 2; + } + else + return -1; + } + *out = '\0'; + return (int)(out - save); +} +# endif + +static int do_responder(OCSP_REQUEST **preq, BIO **pcbio, BIO *acbio) +{ +# ifdef OPENSSL_NO_SOCK + return 0; +# else + int len; + OCSP_REQUEST *req = NULL; + char inbuf[2048], reqbuf[2048]; + char *p, *q; + BIO *cbio = NULL, *getbio = NULL, *b64 = NULL; + + if (BIO_do_accept(acbio) <= 0) { + BIO_printf(bio_err, "Error accepting connection\n"); + ERR_print_errors(bio_err); + return 0; + } + + cbio = BIO_pop(acbio); + *pcbio = cbio; + + /* Read the request line. */ + len = BIO_gets(cbio, reqbuf, sizeof reqbuf); + if (len <= 0) + return 1; + if (strncmp(reqbuf, "GET ", 4) == 0) { + /* Expecting GET {sp} /URL {sp} HTTP/1.x */ + for (p = reqbuf + 4; *p == ' '; ++p) + continue; + if (*p != '/') { + BIO_printf(bio_err, "Invalid request -- bad URL\n"); + return 1; + } + p++; + + /* Splice off the HTTP version identifier. */ + for (q = p; *q; q++) + if (*q == ' ') + break; + if (strncmp(q, " HTTP/1.", 8) != 0) { + BIO_printf(bio_err, "Invalid request -- bad HTTP vesion\n"); + return 1; + } + *q = '\0'; + len = urldecode(p); + if (len <= 0) { + BIO_printf(bio_err, "Invalid request -- bad URL encoding\n"); + return 1; + } + if ((getbio = BIO_new_mem_buf(p, len)) == NULL + || (b64 = BIO_new(BIO_f_base64())) == NULL) { + BIO_printf(bio_err, "Could not allocate memory\n"); + ERR_print_errors(bio_err); + return 1; + } + BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL); + getbio = BIO_push(b64, getbio); + } else if (strncmp(reqbuf, "POST ", 5) != 0) { + BIO_printf(bio_err, "Invalid request -- bad HTTP verb\n"); + return 1; + } + + /* Read and skip past the headers. */ + for (;;) { + len = BIO_gets(cbio, inbuf, sizeof inbuf); + if (len <= 0) + return 1; + if ((inbuf[0] == '\r') || (inbuf[0] == '\n')) + break; + } + + /* Try to read OCSP request */ + if (getbio) { + req = d2i_OCSP_REQUEST_bio(getbio, NULL); + BIO_free_all(getbio); + } else + req = d2i_OCSP_REQUEST_bio(cbio, NULL); + + if (!req) { + BIO_printf(bio_err, "Error parsing OCSP request\n"); + ERR_print_errors(bio_err); + } + + *preq = req; + + return 1; +# endif +} + +static int send_ocsp_response(BIO *cbio, OCSP_RESPONSE *resp) +{ + char http_resp[] = + "HTTP/1.0 200 OK\r\nContent-type: application/ocsp-response\r\n" + "Content-Length: %d\r\n\r\n"; + if (!cbio) + return 0; + BIO_printf(cbio, http_resp, i2d_OCSP_RESPONSE(resp, NULL)); + i2d_OCSP_RESPONSE_bio(cbio, resp); + (void)BIO_flush(cbio); + return 1; +} + +# ifndef OPENSSL_NO_SOCK +static OCSP_RESPONSE *query_responder(BIO *cbio, const char *host, + const char *path, + const STACK_OF(CONF_VALUE) *headers, + OCSP_REQUEST *req, int req_timeout) +{ + int fd; + int rv; + int i; + int add_host = 1; + OCSP_REQ_CTX *ctx = NULL; + OCSP_RESPONSE *rsp = NULL; + fd_set confds; + struct timeval tv; + + if (req_timeout != -1) + BIO_set_nbio(cbio, 1); + + rv = BIO_do_connect(cbio); + + if ((rv <= 0) && ((req_timeout == -1) || !BIO_should_retry(cbio))) { + BIO_puts(bio_err, "Error connecting BIO\n"); + return NULL; + } + + if (BIO_get_fd(cbio, &fd) < 0) { + BIO_puts(bio_err, "Can't get connection fd\n"); + goto err; + } + + if (req_timeout != -1 && rv <= 0) { + FD_ZERO(&confds); + openssl_fdset(fd, &confds); + tv.tv_usec = 0; + tv.tv_sec = req_timeout; + rv = select(fd + 1, NULL, (void *)&confds, NULL, &tv); + if (rv == 0) { + BIO_puts(bio_err, "Timeout on connect\n"); + return NULL; + } + } + + ctx = OCSP_sendreq_new(cbio, path, NULL, -1); + if (ctx == NULL) + return NULL; + + for (i = 0; i < sk_CONF_VALUE_num(headers); i++) { + CONF_VALUE *hdr = sk_CONF_VALUE_value(headers, i); + if (add_host == 1 && strcasecmp("host", hdr->name) == 0) + add_host = 0; + if (!OCSP_REQ_CTX_add1_header(ctx, hdr->name, hdr->value)) + goto err; + } + + if (add_host == 1 && OCSP_REQ_CTX_add1_header(ctx, "Host", host) == 0) + goto err; + + if (!OCSP_REQ_CTX_set1_req(ctx, req)) + goto err; + + for (;;) { + rv = OCSP_sendreq_nbio(&rsp, ctx); + if (rv != -1) + break; + if (req_timeout == -1) + continue; + FD_ZERO(&confds); + openssl_fdset(fd, &confds); + tv.tv_usec = 0; + tv.tv_sec = req_timeout; + if (BIO_should_read(cbio)) + rv = select(fd + 1, (void *)&confds, NULL, NULL, &tv); + else if (BIO_should_write(cbio)) + rv = select(fd + 1, NULL, (void *)&confds, NULL, &tv); + else { + BIO_puts(bio_err, "Unexpected retry condition\n"); + goto err; + } + if (rv == 0) { + BIO_puts(bio_err, "Timeout on request\n"); + break; + } + if (rv == -1) { + BIO_puts(bio_err, "Select error\n"); + break; + } + + } + err: + OCSP_REQ_CTX_free(ctx); + + return rsp; +} + +OCSP_RESPONSE *process_responder(OCSP_REQUEST *req, + const char *host, const char *path, + const char *port, int use_ssl, + STACK_OF(CONF_VALUE) *headers, + int req_timeout) +{ + BIO *cbio = NULL; + SSL_CTX *ctx = NULL; + OCSP_RESPONSE *resp = NULL; + + cbio = BIO_new_connect(host); + if (!cbio) { + BIO_printf(bio_err, "Error creating connect BIO\n"); + goto end; + } + if (port) + BIO_set_conn_port(cbio, port); + if (use_ssl == 1) { + BIO *sbio; + ctx = SSL_CTX_new(TLS_client_method()); + if (ctx == NULL) { + BIO_printf(bio_err, "Error creating SSL context.\n"); + goto end; + } + SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY); + sbio = BIO_new_ssl(ctx, 1); + cbio = BIO_push(sbio, cbio); + } + + resp = query_responder(cbio, host, path, headers, req, req_timeout); + if (!resp) + BIO_printf(bio_err, "Error querying OCSP responder\n"); + end: + BIO_free_all(cbio); + SSL_CTX_free(ctx); + return resp; +} +# endif + +#endif diff --git a/apps/openssl-vms.cnf b/apps/openssl-vms.cnf new file mode 100644 index 000000000..9f35aead9 --- /dev/null +++ b/apps/openssl-vms.cnf @@ -0,0 +1,348 @@ +# +# GmSSL example configuration file. +# This is mostly being used for generation of certificate requests. +# + +# This definition stops the following lines choking if HOME isn't +# defined. +HOME = . +RANDFILE = $ENV::HOME/.rnd + +# Extra OBJECT IDENTIFIER info: +#oid_file = $ENV::HOME/.oid +oid_section = new_oids + +# To use this configuration file with the "-extfile" option of the +# "openssl x509" utility, name here the section containing the +# X.509v3 extensions to use: +# extensions = +# (Alternatively, use a configuration file that has only +# X.509v3 extensions in its main [= default] section.) + +[ new_oids ] + +# We can add new OIDs in here for use by 'ca', 'req' and 'ts'. +# Add a simple OID like this: +# testoid1=1.2.3.4 +# Or use config file substitution like this: +# testoid2=${testoid1}.5.6 + +# Policies used by the TSA examples. +tsa_policy1 = 1.2.3.4.1 +tsa_policy2 = 1.2.3.4.5.6 +tsa_policy3 = 1.2.3.4.5.7 + +#################################################################### +[ ca ] +default_ca = CA_default # The default ca section + +#################################################################### +[ CA_default ] + +dir = sys\$disk:[.demoCA # Where everything is kept +certs = $dir.certs] # Where the issued certs are kept +crl_dir = $dir.crl] # Where the issued crl are kept +database = $dir]index.txt # database index file. +#unique_subject = no # Set to 'no' to allow creation of + # several certs with same subject. +new_certs_dir = $dir.newcerts] # default place for new certs. + +certificate = $dir]cacert.pem # The CA certificate +serial = $dir]serial. # The current serial number +crlnumber = $dir]crlnumber. # the current crl number + # must be commented out to leave a V1 CRL +crl = $dir]crl.pem # The current CRL +private_key = $dir.private]cakey.pem # The private key +RANDFILE = $dir.private].rand # private random number file + +x509_extensions = usr_cert # The extensions to add to the cert + +# Comment out the following two lines for the "traditional" +# (and highly broken) format. +name_opt = ca_default # Subject Name options +cert_opt = ca_default # Certificate field options + +# Extension copying option: use with caution. +# copy_extensions = copy + +# Extensions to add to a CRL. Note: Netscape communicator chokes on V2 CRLs +# so this is commented out by default to leave a V1 CRL. +# crlnumber must also be commented out to leave a V1 CRL. +# crl_extensions = crl_ext + +default_days = 365 # how long to certify for +default_crl_days= 30 # how long before next CRL +default_md = default # use public key default MD +preserve = no # keep passed DN ordering + +# A few difference way of specifying how similar the request should look +# For type CA, the listed attributes must be the same, and the optional +# and supplied fields are just that :-) +policy = policy_match + +# For the CA policy +[ policy_match ] +countryName = match +stateOrProvinceName = match +organizationName = match +organizationalUnitName = optional +commonName = supplied +emailAddress = optional + +# For the 'anything' policy +# At this point in time, you must list all acceptable 'object' +# types. +[ policy_anything ] +countryName = optional +stateOrProvinceName = optional +localityName = optional +organizationName = optional +organizationalUnitName = optional +commonName = supplied +emailAddress = optional + +#################################################################### +[ req ] +default_bits = 2048 +default_keyfile = privkey.pem +distinguished_name = req_distinguished_name +attributes = req_attributes +x509_extensions = v3_ca # The extensions to add to the self signed cert + +# Passwords for private keys if not present they will be prompted for +# input_password = secret +# output_password = secret + +# This sets a mask for permitted string types. There are several options. +# default: PrintableString, T61String, BMPString. +# pkix : PrintableString, BMPString (PKIX recommendation before 2004) +# utf8only: only UTF8Strings (PKIX recommendation after 2004). +# nombstr : PrintableString, T61String (no BMPStrings or UTF8Strings). +# MASK:XXXX a literal mask value. +# WARNING: ancient versions of Netscape crash on BMPStrings or UTF8Strings. +string_mask = utf8only + +# req_extensions = v3_req # The extensions to add to a certificate request + +[ req_distinguished_name ] +countryName = Country Name (2 letter code) +countryName_default = CN +countryName_min = 2 +countryName_max = 2 + +stateOrProvinceName = State or Province Name (full name) +stateOrProvinceName_default = Some-State + +localityName = Locality Name (eg, city) + +0.organizationName = Organization Name (eg, company) +0.organizationName_default = Internet Widgits Pty Ltd + +# we can do this but it is not needed normally :-) +#1.organizationName = Second Organization Name (eg, company) +#1.organizationName_default = World Wide Web Pty Ltd + +organizationalUnitName = Organizational Unit Name (eg, section) +#organizationalUnitName_default = + +commonName = Common Name (e.g. server FQDN or YOUR name) +commonName_max = 64 + +emailAddress = Email Address +emailAddress_max = 64 + +# SET-ex3 = SET extension number 3 + +[ req_attributes ] +challengePassword = A challenge password +challengePassword_min = 4 +challengePassword_max = 20 + +unstructuredName = An optional company name + +[ usr_cert ] + +# These extensions are added when 'ca' signs a request. + +# This goes against PKIX guidelines but some CAs do it and some software +# requires this to avoid interpreting an end user certificate as a CA. + +basicConstraints=CA:FALSE + +# Here are some examples of the usage of nsCertType. If it is omitted +# the certificate can be used for anything *except* object signing. + +# This is OK for an SSL server. +# nsCertType = server + +# For an object signing certificate this would be used. +# nsCertType = objsign + +# For normal client use this is typical +# nsCertType = client, email + +# and for everything including object signing: +# nsCertType = client, email, objsign + +# This is typical in keyUsage for a client certificate. +# keyUsage = nonRepudiation, digitalSignature, keyEncipherment +# keyUsage = digitalSignature +keyUsage = keyEncipherment + +# This will be displayed in Netscape's comment listbox. +nsComment = "GmSSL Generated Certificate" + +# PKIX recommendations harmless if included in all certificates. +subjectKeyIdentifier=hash +authorityKeyIdentifier=keyid,issuer + +# This stuff is for subjectAltName and issuerAltname. +# Import the email address. +# subjectAltName=email:copy +# An alternative to produce certificates that aren't +# deprecated according to PKIX. +# subjectAltName=email:move + +# Copy subject details +# issuerAltName=issuer:copy + +#nsCaRevocationUrl = http://www.domain.dom/ca-crl.pem +#nsBaseUrl +#nsRevocationUrl +#nsRenewalUrl +#nsCaPolicyUrl +#nsSslServerName + +# This is required for TSA certificates. +# extendedKeyUsage = critical,timeStamping + +[ v3_req ] + +# Extensions to add to a certificate request + +basicConstraints = CA:FALSE +keyUsage = nonRepudiation, digitalSignature, keyEncipherment + +[ v3_ca ] + + +# Extensions for a typical CA + + +# PKIX recommendation. + +subjectKeyIdentifier=hash + +authorityKeyIdentifier=keyid:always,issuer + +basicConstraints = critical,CA:true + +# Key usage: this is typical for a CA certificate. However since it will +# prevent it being used as an test self-signed certificate it is best +# left out by default. +# keyUsage = cRLSign, keyCertSign + +# Some might want this also +# nsCertType = sslCA, emailCA + +# Include email address in subject alt name: another PKIX recommendation +# subjectAltName=email:copy +# Copy issuer details +# issuerAltName=issuer:copy + +# DER hex encoding of an extension: beware experts only! +# obj=DER:02:03 +# Where 'obj' is a standard or added object +# You can even override a supported extension: +# basicConstraints= critical, DER:30:03:01:01:FF + +[ crl_ext ] + +# CRL extensions. +# Only issuerAltName and authorityKeyIdentifier make any sense in a CRL. + +# issuerAltName=issuer:copy +authorityKeyIdentifier=keyid:always + +[ proxy_cert_ext ] +# These extensions should be added when creating a proxy certificate + +# This goes against PKIX guidelines but some CAs do it and some software +# requires this to avoid interpreting an end user certificate as a CA. + +basicConstraints=CA:FALSE + +# Here are some examples of the usage of nsCertType. If it is omitted +# the certificate can be used for anything *except* object signing. + +# This is OK for an SSL server. +# nsCertType = server + +# For an object signing certificate this would be used. +# nsCertType = objsign + +# For normal client use this is typical +# nsCertType = client, email + +# and for everything including object signing: +# nsCertType = client, email, objsign + +# This is typical in keyUsage for a client certificate. +# keyUsage = nonRepudiation, digitalSignature, keyEncipherment + +# This will be displayed in Netscape's comment listbox. +nsComment = "GmSSL Generated Certificate" + +# PKIX recommendations harmless if included in all certificates. +subjectKeyIdentifier=hash +authorityKeyIdentifier=keyid,issuer + +# This stuff is for subjectAltName and issuerAltname. +# Import the email address. +# subjectAltName=email:copy +# An alternative to produce certificates that aren't +# deprecated according to PKIX. +# subjectAltName=email:move + +# Copy subject details +# issuerAltName=issuer:copy + +#nsCaRevocationUrl = http://www.domain.dom/ca-crl.pem +#nsBaseUrl +#nsRevocationUrl +#nsRenewalUrl +#nsCaPolicyUrl +#nsSslServerName + +# This really needs to be in place for it to be a proxy certificate. +proxyCertInfo=critical,language:id-ppl-anyLanguage,pathlen:3,policy:foo + +#################################################################### +[ tsa ] + +default_tsa = tsa_config1 # the default TSA section + +[ tsa_config1 ] + +# These are used by the TSA reply generation only. +dir = sys\$disk:[.demoCA # TSA root directory +serial = $dir]tsaserial. # The current serial number (mandatory) +crypto_device = builtin # OpenSSL engine to use for signing +signer_cert = $dir/tsacert.pem # The TSA signing certificate + # (optional) +certs = $dir.cacert.pem] # Certificate chain to include in reply + # (optional) +signer_key = $dir/private/tsakey.pem # The TSA private key (optional) +signer_digest = sm3 # Signing digest to use. (Optional) +default_policy = tsa_policy1 # Policy if request did not specify it + # (optional) +other_policies = tsa_policy2, tsa_policy3 # acceptable policies (optional) +digests = sm3, sha256, sha384, sha512 # Acceptable message digests (mandatory) +accuracy = secs:1, millisecs:500, microsecs:100 # (optional) +clock_precision_digits = 0 # number of digits after dot. (optional) +ordering = yes # Is ordering defined for timestamps? + # (optional, default: no) +tsa_name = yes # Must the TSA name be included in the reply? + # (optional, default: no) +ess_cert_id_chain = no # Must the ESS cert id chain be included? + # (optional, default: no) diff --git a/apps/openssl.cnf b/apps/openssl.cnf new file mode 100644 index 000000000..90a9ebbfc --- /dev/null +++ b/apps/openssl.cnf @@ -0,0 +1,348 @@ +# +# GmSSL example configuration file. +# This is mostly being used for generation of certificate requests. +# + +# This definition stops the following lines choking if HOME isn't +# defined. +HOME = . +RANDFILE = $ENV::HOME/.rnd + +# Extra OBJECT IDENTIFIER info: +#oid_file = $ENV::HOME/.oid +oid_section = new_oids + +# To use this configuration file with the "-extfile" option of the +# "openssl x509" utility, name here the section containing the +# X.509v3 extensions to use: +# extensions = +# (Alternatively, use a configuration file that has only +# X.509v3 extensions in its main [= default] section.) + +[ new_oids ] + +# We can add new OIDs in here for use by 'ca', 'req' and 'ts'. +# Add a simple OID like this: +# testoid1=1.2.3.4 +# Or use config file substitution like this: +# testoid2=${testoid1}.5.6 + +# Policies used by the TSA examples. +tsa_policy1 = 1.2.3.4.1 +tsa_policy2 = 1.2.3.4.5.6 +tsa_policy3 = 1.2.3.4.5.7 + +#################################################################### +[ ca ] +default_ca = CA_default # The default ca section + +#################################################################### +[ CA_default ] + +dir = ./demoCA # Where everything is kept +certs = $dir/certs # Where the issued certs are kept +crl_dir = $dir/crl # Where the issued crl are kept +database = $dir/index.txt # database index file. +#unique_subject = no # Set to 'no' to allow creation of + # several certs with same subject. +new_certs_dir = $dir/newcerts # default place for new certs. + +certificate = $dir/cacert.pem # The CA certificate +serial = $dir/serial # The current serial number +crlnumber = $dir/crlnumber # the current crl number + # must be commented out to leave a V1 CRL +crl = $dir/crl.pem # The current CRL +private_key = $dir/private/cakey.pem # The private key +RANDFILE = $dir/private/.rand # private random number file + +x509_extensions = usr_cert # The extensions to add to the cert + +# Comment out the following two lines for the "traditional" +# (and highly broken) format. +name_opt = ca_default # Subject Name options +cert_opt = ca_default # Certificate field options + +# Extension copying option: use with caution. +# copy_extensions = copy + +# Extensions to add to a CRL. Note: Netscape communicator chokes on V2 CRLs +# so this is commented out by default to leave a V1 CRL. +# crlnumber must also be commented out to leave a V1 CRL. +# crl_extensions = crl_ext + +default_days = 365 # how long to certify for +default_crl_days= 30 # how long before next CRL +default_md = default # use public key default MD +preserve = no # keep passed DN ordering + +# A few difference way of specifying how similar the request should look +# For type CA, the listed attributes must be the same, and the optional +# and supplied fields are just that :-) +policy = policy_match + +# For the CA policy +[ policy_match ] +countryName = match +stateOrProvinceName = match +organizationName = match +organizationalUnitName = optional +commonName = supplied +emailAddress = optional + +# For the 'anything' policy +# At this point in time, you must list all acceptable 'object' +# types. +[ policy_anything ] +countryName = optional +stateOrProvinceName = optional +localityName = optional +organizationName = optional +organizationalUnitName = optional +commonName = supplied +emailAddress = optional + +#################################################################### +[ req ] +default_bits = 2048 +default_keyfile = privkey.pem +distinguished_name = req_distinguished_name +attributes = req_attributes +x509_extensions = v3_ca # The extensions to add to the self signed cert + +# Passwords for private keys if not present they will be prompted for +# input_password = secret +# output_password = secret + +# This sets a mask for permitted string types. There are several options. +# default: PrintableString, T61String, BMPString. +# pkix : PrintableString, BMPString (PKIX recommendation before 2004) +# utf8only: only UTF8Strings (PKIX recommendation after 2004). +# nombstr : PrintableString, T61String (no BMPStrings or UTF8Strings). +# MASK:XXXX a literal mask value. +# WARNING: ancient versions of Netscape crash on BMPStrings or UTF8Strings. +string_mask = utf8only + +# req_extensions = v3_req # The extensions to add to a certificate request + +[ req_distinguished_name ] +countryName = Country Name (2 letter code) +countryName_default = CN +countryName_min = 2 +countryName_max = 2 + +stateOrProvinceName = State or Province Name (full name) +stateOrProvinceName_default = Some-State + +localityName = Locality Name (eg, city) + +0.organizationName = Organization Name (eg, company) +0.organizationName_default = Internet Widgits Pty Ltd + +# we can do this but it is not needed normally :-) +#1.organizationName = Second Organization Name (eg, company) +#1.organizationName_default = World Wide Web Pty Ltd + +organizationalUnitName = Organizational Unit Name (eg, section) +#organizationalUnitName_default = + +commonName = Common Name (e.g. server FQDN or YOUR name) +commonName_max = 64 + +emailAddress = Email Address +emailAddress_max = 64 + +# SET-ex3 = SET extension number 3 + +[ req_attributes ] +challengePassword = A challenge password +challengePassword_min = 4 +challengePassword_max = 20 + +unstructuredName = An optional company name + +[ usr_cert ] + +# These extensions are added when 'ca' signs a request. + +# This goes against PKIX guidelines but some CAs do it and some software +# requires this to avoid interpreting an end user certificate as a CA. + +basicConstraints=CA:FALSE + +# Here are some examples of the usage of nsCertType. If it is omitted +# the certificate can be used for anything *except* object signing. + +# This is OK for an SSL server. +# nsCertType = server + +# For an object signing certificate this would be used. +# nsCertType = objsign + +# For normal client use this is typical +# nsCertType = client, email + +# and for everything including object signing: +# nsCertType = client, email, objsign + +# This is typical in keyUsage for a client certificate. +# keyUsage = nonRepudiation, digitalSignature, keyEncipherment +# keyUsage = digitalSignature +keyUsage = keyEncipherment + +# This will be displayed in Netscape's comment listbox. +nsComment = "GmSSL Generated Certificate" + +# PKIX recommendations harmless if included in all certificates. +subjectKeyIdentifier=hash +authorityKeyIdentifier=keyid,issuer + +# This stuff is for subjectAltName and issuerAltname. +# Import the email address. +# subjectAltName=email:copy +# An alternative to produce certificates that aren't +# deprecated according to PKIX. +# subjectAltName=email:move + +# Copy subject details +# issuerAltName=issuer:copy + +#nsCaRevocationUrl = http://www.domain.dom/ca-crl.pem +#nsBaseUrl +#nsRevocationUrl +#nsRenewalUrl +#nsCaPolicyUrl +#nsSslServerName + +# This is required for TSA certificates. +# extendedKeyUsage = critical,timeStamping + +[ v3_req ] + +# Extensions to add to a certificate request + +basicConstraints = CA:FALSE +keyUsage = nonRepudiation, digitalSignature, keyEncipherment + +[ v3_ca ] + + +# Extensions for a typical CA + + +# PKIX recommendation. + +subjectKeyIdentifier=hash + +authorityKeyIdentifier=keyid:always,issuer + +basicConstraints = critical,CA:true + +# Key usage: this is typical for a CA certificate. However since it will +# prevent it being used as an test self-signed certificate it is best +# left out by default. +# keyUsage = cRLSign, keyCertSign + +# Some might want this also +# nsCertType = sslCA, emailCA + +# Include email address in subject alt name: another PKIX recommendation +# subjectAltName=email:copy +# Copy issuer details +# issuerAltName=issuer:copy + +# DER hex encoding of an extension: beware experts only! +# obj=DER:02:03 +# Where 'obj' is a standard or added object +# You can even override a supported extension: +# basicConstraints= critical, DER:30:03:01:01:FF + +[ crl_ext ] + +# CRL extensions. +# Only issuerAltName and authorityKeyIdentifier make any sense in a CRL. + +# issuerAltName=issuer:copy +authorityKeyIdentifier=keyid:always + +[ proxy_cert_ext ] +# These extensions should be added when creating a proxy certificate + +# This goes against PKIX guidelines but some CAs do it and some software +# requires this to avoid interpreting an end user certificate as a CA. + +basicConstraints=CA:FALSE + +# Here are some examples of the usage of nsCertType. If it is omitted +# the certificate can be used for anything *except* object signing. + +# This is OK for an SSL server. +# nsCertType = server + +# For an object signing certificate this would be used. +# nsCertType = objsign + +# For normal client use this is typical +# nsCertType = client, email + +# and for everything including object signing: +# nsCertType = client, email, objsign + +# This is typical in keyUsage for a client certificate. +# keyUsage = nonRepudiation, digitalSignature, keyEncipherment + +# This will be displayed in Netscape's comment listbox. +nsComment = "GmSSL Generated Certificate" + +# PKIX recommendations harmless if included in all certificates. +subjectKeyIdentifier=hash +authorityKeyIdentifier=keyid,issuer + +# This stuff is for subjectAltName and issuerAltname. +# Import the email address. +# subjectAltName=email:copy +# An alternative to produce certificates that aren't +# deprecated according to PKIX. +# subjectAltName=email:move + +# Copy subject details +# issuerAltName=issuer:copy + +#nsCaRevocationUrl = http://www.domain.dom/ca-crl.pem +#nsBaseUrl +#nsRevocationUrl +#nsRenewalUrl +#nsCaPolicyUrl +#nsSslServerName + +# This really needs to be in place for it to be a proxy certificate. +proxyCertInfo=critical,language:id-ppl-anyLanguage,pathlen:3,policy:foo + +#################################################################### +[ tsa ] + +default_tsa = tsa_config1 # the default TSA section + +[ tsa_config1 ] + +# These are used by the TSA reply generation only. +dir = ./demoCA # TSA root directory +serial = $dir/tsaserial # The current serial number (mandatory) +crypto_device = builtin # OpenSSL engine to use for signing +signer_cert = $dir/tsacert.pem # The TSA signing certificate + # (optional) +certs = $dir/cacert.pem # Certificate chain to include in reply + # (optional) +signer_key = $dir/private/tsakey.pem # The TSA private key (optional) +signer_digest = sm3 # Signing digest to use. (Optional) +default_policy = tsa_policy1 # Policy if request did not specify it + # (optional) +other_policies = tsa_policy2, tsa_policy3 # acceptable policies (optional) +digests = sm3, sha256, sha384, sha512 # Acceptable message digests (mandatory) +accuracy = secs:1, millisecs:500, microsecs:100 # (optional) +clock_precision_digits = 0 # number of digits after dot. (optional) +ordering = yes # Is ordering defined for timestamps? + # (optional, default: no) +tsa_name = yes # Must the TSA name be included in the reply? + # (optional, default: no) +ess_cert_id_chain = no # Must the ESS cert id chain be included? + # (optional, default: no) diff --git a/apps/opt.c b/apps/opt.c new file mode 100644 index 000000000..c0d98cd57 --- /dev/null +++ b/apps/opt.c @@ -0,0 +1,983 @@ +/* + * Copyright 2015-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +/* #define COMPILE_STANDALONE_TEST_DRIVER */ +#include "apps.h" +#include +#if !defined(OPENSSL_SYS_MSDOS) +# include OPENSSL_UNISTD +#endif + +#include +#include +#include +#include +#include +#include + +#define MAX_OPT_HELP_WIDTH 30 +const char OPT_HELP_STR[] = "--"; +const char OPT_MORE_STR[] = "---"; + +/* Our state */ +static char **argv; +static int argc; +static int opt_index; +static char *arg; +static char *flag; +static char *dunno; +static const OPTIONS *unknown; +static const OPTIONS *opts; +static char prog[40]; + +/* + * Return the simple name of the program; removing various platform gunk. + */ +#if defined(OPENSSL_SYS_WIN32) +char *opt_progname(const char *argv0) +{ + size_t i, n; + const char *p; + char *q; + + /* find the last '/', '\' or ':' */ + for (p = argv0 + strlen(argv0); --p > argv0;) + if (*p == '/' || *p == '\\' || *p == ':') { + p++; + break; + } + + /* Strip off trailing nonsense. */ + n = strlen(p); + if (n > 4 && + (strcmp(&p[n - 4], ".exe") == 0 || strcmp(&p[n - 4], ".EXE") == 0)) + n -= 4; + + /* Copy over the name, in lowercase. */ + if (n > sizeof prog - 1) + n = sizeof prog - 1; + for (q = prog, i = 0; i < n; i++, p++) + *q++ = isupper(*p) ? tolower(*p) : *p; + *q = '\0'; + return prog; +} + +#elif defined(OPENSSL_SYS_VMS) + +char *opt_progname(const char *argv0) +{ + const char *p, *q; + + /* Find last special character sys:[foo.bar]openssl */ + for (p = argv0 + strlen(argv0); --p > argv0;) + if (*p == ':' || *p == ']' || *p == '>') { + p++; + break; + } + + q = strrchr(p, '.'); + strncpy(prog, p, sizeof prog - 1); + prog[sizeof prog - 1] = '\0'; + if (q != NULL && q - p < sizeof prog) + prog[q - p] = '\0'; + return prog; +} + +#else + +char *opt_progname(const char *argv0) +{ + const char *p; + + /* Could use strchr, but this is like the ones above. */ + for (p = argv0 + strlen(argv0); --p > argv0;) + if (*p == '/') { + p++; + break; + } + strncpy(prog, p, sizeof prog - 1); + prog[sizeof prog - 1] = '\0'; + return prog; +} +#endif + +char *opt_getprog(void) +{ + return prog; +} + +/* Set up the arg parsing. */ +char *opt_init(int ac, char **av, const OPTIONS *o) +{ + /* Store state. */ + argc = ac; + argv = av; + opt_index = 1; + opts = o; + opt_progname(av[0]); + unknown = NULL; + + for (; o->name; ++o) { +#ifndef NDEBUG + const OPTIONS *next; + int duplicated, i; +#endif + + if (o->name == OPT_HELP_STR || o->name == OPT_MORE_STR) + continue; +#ifndef NDEBUG + i = o->valtype; + + /* Make sure options are legit. */ + assert(o->name[0] != '-'); + assert(o->retval > 0); + switch (i) { + case 0: case '-': case '/': case '<': case '>': case 'E': case 'F': + case 'M': case 'U': case 'f': case 'l': case 'n': case 'p': case 's': + case 'u': case 'c': + break; + default: + assert(0); + } + + /* Make sure there are no duplicates. */ + for (next = o + 1; next->name; ++next) { + /* + * Some compilers inline strcmp and the assert string is too long. + */ + duplicated = strcmp(o->name, next->name) == 0; + assert(!duplicated); + } +#endif + if (o->name[0] == '\0') { + assert(unknown == NULL); + unknown = o; + assert(unknown->valtype == 0 || unknown->valtype == '-'); + } + } + return prog; +} + +static OPT_PAIR formats[] = { + {"PEM/DER", OPT_FMT_PEMDER}, + {"pkcs12", OPT_FMT_PKCS12}, + {"smime", OPT_FMT_SMIME}, + {"engine", OPT_FMT_ENGINE}, + {"msblob", OPT_FMT_MSBLOB}, + {"netscape", OPT_FMT_NETSCAPE}, + {"nss", OPT_FMT_NSS}, + {"text", OPT_FMT_TEXT}, + {"http", OPT_FMT_HTTP}, + {"pvk", OPT_FMT_PVK}, + {NULL} +}; + +/* Print an error message about a failed format parse. */ +int opt_format_error(const char *s, unsigned long flags) +{ + OPT_PAIR *ap; + + if (flags == OPT_FMT_PEMDER) + BIO_printf(bio_err, "%s: Bad format \"%s\"; must be pem or der\n", + prog, s); + else { + BIO_printf(bio_err, "%s: Bad format \"%s\"; must be one of:\n", + prog, s); + for (ap = formats; ap->name; ap++) + if (flags & ap->retval) + BIO_printf(bio_err, " %s\n", ap->name); + } + return 0; +} + +/* Parse a format string, put it into *result; return 0 on failure, else 1. */ +int opt_format(const char *s, unsigned long flags, int *result) +{ + switch (*s) { + default: + return 0; + case 'D': + case 'd': + if ((flags & OPT_FMT_PEMDER) == 0) + return opt_format_error(s, flags); + *result = FORMAT_ASN1; + break; + case 'T': + case 't': + if ((flags & OPT_FMT_TEXT) == 0) + return opt_format_error(s, flags); + *result = FORMAT_TEXT; + break; + case 'B': + case 'b': + if ((flags & OPT_FMT_BINARY) == 0) + return opt_format_error(s, flags); + *result = FORMAT_BINARY; + break; + case 'N': + case 'n': + if ((flags & OPT_FMT_NSS) == 0) + return opt_format_error(s, flags); + if (strcmp(s, "NSS") != 0 && strcmp(s, "nss") != 0) + return opt_format_error(s, flags); + *result = FORMAT_NSS; + break; + case 'S': + case 's': + if ((flags & OPT_FMT_SMIME) == 0) + return opt_format_error(s, flags); + *result = FORMAT_SMIME; + break; + case 'M': + case 'm': + if ((flags & OPT_FMT_MSBLOB) == 0) + return opt_format_error(s, flags); + *result = FORMAT_MSBLOB; + break; + case 'E': + case 'e': + if ((flags & OPT_FMT_ENGINE) == 0) + return opt_format_error(s, flags); + *result = FORMAT_ENGINE; + break; + case 'H': + case 'h': + if ((flags & OPT_FMT_HTTP) == 0) + return opt_format_error(s, flags); + *result = FORMAT_HTTP; + break; + case '1': + if ((flags & OPT_FMT_PKCS12) == 0) + return opt_format_error(s, flags); + *result = FORMAT_PKCS12; + break; + case 'P': + case 'p': + if (s[1] == '\0' || strcmp(s, "PEM") == 0 || strcmp(s, "pem") == 0) { + if ((flags & OPT_FMT_PEMDER) == 0) + return opt_format_error(s, flags); + *result = FORMAT_PEM; + } else if (strcmp(s, "PVK") == 0 || strcmp(s, "pvk") == 0) { + if ((flags & OPT_FMT_PVK) == 0) + return opt_format_error(s, flags); + *result = FORMAT_PVK; + } else if (strcmp(s, "P12") == 0 || strcmp(s, "p12") == 0 + || strcmp(s, "PKCS12") == 0 || strcmp(s, "pkcs12") == 0) { + if ((flags & OPT_FMT_PKCS12) == 0) + return opt_format_error(s, flags); + *result = FORMAT_PKCS12; + } else + return 0; + break; + } + return 1; +} + +/* Parse a cipher name, put it in *EVP_CIPHER; return 0 on failure, else 1. */ +int opt_cipher(const char *name, const EVP_CIPHER **cipherp) +{ + *cipherp = EVP_get_cipherbyname(name); + if (*cipherp) + return 1; + BIO_printf(bio_err, "%s: Unknown cipher %s\n", prog, name); + return 0; +} + +/* + * Parse message digest name, put it in *EVP_MD; return 0 on failure, else 1. + */ +int opt_md(const char *name, const EVP_MD **mdp) +{ + *mdp = EVP_get_digestbyname(name); + if (*mdp) + return 1; + BIO_printf(bio_err, "%s: Unknown digest %s\n", prog, name); + return 0; +} + +/* Look through a list of name/value pairs. */ +int opt_pair(const char *name, const OPT_PAIR* pairs, int *result) +{ + const OPT_PAIR *pp; + + for (pp = pairs; pp->name; pp++) + if (strcmp(pp->name, name) == 0) { + *result = pp->retval; + return 1; + } + BIO_printf(bio_err, "%s: Value must be one of:\n", prog); + for (pp = pairs; pp->name; pp++) + BIO_printf(bio_err, "\t%s\n", pp->name); + return 0; +} + +/* Parse an int, put it into *result; return 0 on failure, else 1. */ +int opt_int(const char *value, int *result) +{ + long l; + + if (!opt_long(value, &l)) + return 0; + *result = (int)l; + if (*result != l) { + BIO_printf(bio_err, "%s: Value \"%s\" outside integer range\n", + prog, value); + return 0; + } + return 1; +} + +/* Parse a long, put it into *result; return 0 on failure, else 1. */ +int opt_long(const char *value, long *result) +{ + int oerrno = errno; + long l; + char *endp; + + errno = 0; + l = strtol(value, &endp, 0); + if (*endp + || endp == value + || ((l == LONG_MAX || l == LONG_MIN) && errno == ERANGE) + || (l == 0 && errno != 0)) { + BIO_printf(bio_err, "%s: Can't parse \"%s\" as a number\n", + prog, value); + errno = oerrno; + return 0; + } + *result = l; + errno = oerrno; + return 1; +} + +#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L && \ + defined(INTMAX_MAX) && defined(UINTMAX_MAX) + +/* Parse an intmax_t, put it into *result; return 0 on failure, else 1. */ +int opt_imax(const char *value, intmax_t *result) +{ + int oerrno = errno; + intmax_t m; + char *endp; + + errno = 0; + m = strtoimax(value, &endp, 0); + if (*endp + || endp == value + || ((m == INTMAX_MAX || m == INTMAX_MIN) && errno == ERANGE) + || (m == 0 && errno != 0)) { + BIO_printf(bio_err, "%s: Can't parse \"%s\" as a number\n", + prog, value); + errno = oerrno; + return 0; + } + *result = m; + errno = oerrno; + return 1; +} + +/* Parse a uintmax_t, put it into *result; return 0 on failure, else 1. */ +int opt_umax(const char *value, uintmax_t *result) +{ + int oerrno = errno; + uintmax_t m; + char *endp; + + errno = 0; + m = strtoumax(value, &endp, 0); + if (*endp + || endp == value + || (m == UINTMAX_MAX && errno == ERANGE) + || (m == 0 && errno != 0)) { + BIO_printf(bio_err, "%s: Can't parse \"%s\" as a number\n", + prog, value); + errno = oerrno; + return 0; + } + *result = m; + errno = oerrno; + return 1; +} +#endif + +/* + * Parse an unsigned long, put it into *result; return 0 on failure, else 1. + */ +int opt_ulong(const char *value, unsigned long *result) +{ + int oerrno = errno; + char *endptr; + unsigned long l; + + errno = 0; + l = strtoul(value, &endptr, 0); + if (*endptr + || endptr == value + || ((l == ULONG_MAX) && errno == ERANGE) + || (l == 0 && errno != 0)) { + BIO_printf(bio_err, "%s: Can't parse \"%s\" as an unsigned number\n", + prog, value); + errno = oerrno; + return 0; + } + *result = l; + errno = oerrno; + return 1; +} + +/* + * We pass opt as an int but cast it to "enum range" so that all the + * items in the OPT_V_ENUM enumeration are caught; this makes -Wswitch + * in gcc do the right thing. + */ +enum range { OPT_V_ENUM }; + +int opt_verify(int opt, X509_VERIFY_PARAM *vpm) +{ + int i; + ossl_intmax_t t = 0; + ASN1_OBJECT *otmp; + X509_PURPOSE *xptmp; + const X509_VERIFY_PARAM *vtmp; + + assert(vpm != NULL); + assert(opt > OPT_V__FIRST); + assert(opt < OPT_V__LAST); + + switch ((enum range)opt) { + case OPT_V__FIRST: + case OPT_V__LAST: + return 0; + case OPT_V_POLICY: + otmp = OBJ_txt2obj(opt_arg(), 0); + if (otmp == NULL) { + BIO_printf(bio_err, "%s: Invalid Policy %s\n", prog, opt_arg()); + return 0; + } + X509_VERIFY_PARAM_add0_policy(vpm, otmp); + break; + case OPT_V_PURPOSE: + /* purpose name -> purpose index */ + i = X509_PURPOSE_get_by_sname(opt_arg()); + if (i < 0) { + BIO_printf(bio_err, "%s: Invalid purpose %s\n", prog, opt_arg()); + return 0; + } + + /* purpose index -> purpose object */ + xptmp = X509_PURPOSE_get0(i); + + /* purpose object -> purpose value */ + i = X509_PURPOSE_get_id(xptmp); + + if (!X509_VERIFY_PARAM_set_purpose(vpm, i)) { + BIO_printf(bio_err, + "%s: Internal error setting purpose %s\n", + prog, opt_arg()); + return 0; + } + break; + case OPT_V_VERIFY_NAME: + vtmp = X509_VERIFY_PARAM_lookup(opt_arg()); + if (vtmp == NULL) { + BIO_printf(bio_err, "%s: Invalid verify name %s\n", + prog, opt_arg()); + return 0; + } + X509_VERIFY_PARAM_set1(vpm, vtmp); + break; + case OPT_V_VERIFY_DEPTH: + i = atoi(opt_arg()); + if (i >= 0) + X509_VERIFY_PARAM_set_depth(vpm, i); + break; + case OPT_V_VERIFY_AUTH_LEVEL: + i = atoi(opt_arg()); + if (i >= 0) + X509_VERIFY_PARAM_set_auth_level(vpm, i); + break; + case OPT_V_ATTIME: + if (!opt_imax(opt_arg(), &t)) + return 0; + if (t != (time_t)t) { + BIO_printf(bio_err, "%s: epoch time out of range %s\n", + prog, opt_arg()); + return 0; + } + X509_VERIFY_PARAM_set_time(vpm, (time_t)t); + break; + case OPT_V_VERIFY_HOSTNAME: + if (!X509_VERIFY_PARAM_set1_host(vpm, opt_arg(), 0)) + return 0; + break; + case OPT_V_VERIFY_EMAIL: + if (!X509_VERIFY_PARAM_set1_email(vpm, opt_arg(), 0)) + return 0; + break; + case OPT_V_VERIFY_IP: + if (!X509_VERIFY_PARAM_set1_ip_asc(vpm, opt_arg())) + return 0; + break; + case OPT_V_IGNORE_CRITICAL: + X509_VERIFY_PARAM_set_flags(vpm, X509_V_FLAG_IGNORE_CRITICAL); + break; + case OPT_V_ISSUER_CHECKS: + /* NOP, deprecated */ + break; + case OPT_V_CRL_CHECK: + X509_VERIFY_PARAM_set_flags(vpm, X509_V_FLAG_CRL_CHECK); + break; + case OPT_V_CRL_CHECK_ALL: + X509_VERIFY_PARAM_set_flags(vpm, + X509_V_FLAG_CRL_CHECK | + X509_V_FLAG_CRL_CHECK_ALL); + break; + case OPT_V_POLICY_CHECK: + X509_VERIFY_PARAM_set_flags(vpm, X509_V_FLAG_POLICY_CHECK); + break; + case OPT_V_EXPLICIT_POLICY: + X509_VERIFY_PARAM_set_flags(vpm, X509_V_FLAG_EXPLICIT_POLICY); + break; + case OPT_V_INHIBIT_ANY: + X509_VERIFY_PARAM_set_flags(vpm, X509_V_FLAG_INHIBIT_ANY); + break; + case OPT_V_INHIBIT_MAP: + X509_VERIFY_PARAM_set_flags(vpm, X509_V_FLAG_INHIBIT_MAP); + break; + case OPT_V_X509_STRICT: + X509_VERIFY_PARAM_set_flags(vpm, X509_V_FLAG_X509_STRICT); + break; + case OPT_V_EXTENDED_CRL: + X509_VERIFY_PARAM_set_flags(vpm, X509_V_FLAG_EXTENDED_CRL_SUPPORT); + break; + case OPT_V_USE_DELTAS: + X509_VERIFY_PARAM_set_flags(vpm, X509_V_FLAG_USE_DELTAS); + break; + case OPT_V_POLICY_PRINT: + X509_VERIFY_PARAM_set_flags(vpm, X509_V_FLAG_NOTIFY_POLICY); + break; + case OPT_V_CHECK_SS_SIG: + X509_VERIFY_PARAM_set_flags(vpm, X509_V_FLAG_CHECK_SS_SIGNATURE); + break; + case OPT_V_TRUSTED_FIRST: + X509_VERIFY_PARAM_set_flags(vpm, X509_V_FLAG_TRUSTED_FIRST); + break; + case OPT_V_SUITEB_128_ONLY: + X509_VERIFY_PARAM_set_flags(vpm, X509_V_FLAG_SUITEB_128_LOS_ONLY); + break; + case OPT_V_SUITEB_128: + X509_VERIFY_PARAM_set_flags(vpm, X509_V_FLAG_SUITEB_128_LOS); + break; + case OPT_V_SUITEB_192: + X509_VERIFY_PARAM_set_flags(vpm, X509_V_FLAG_SUITEB_192_LOS); + break; + case OPT_V_PARTIAL_CHAIN: + X509_VERIFY_PARAM_set_flags(vpm, X509_V_FLAG_PARTIAL_CHAIN); + break; + case OPT_V_NO_ALT_CHAINS: + X509_VERIFY_PARAM_set_flags(vpm, X509_V_FLAG_NO_ALT_CHAINS); + break; + case OPT_V_NO_CHECK_TIME: + X509_VERIFY_PARAM_set_flags(vpm, X509_V_FLAG_NO_CHECK_TIME); + break; + case OPT_V_ALLOW_PROXY_CERTS: + X509_VERIFY_PARAM_set_flags(vpm, X509_V_FLAG_ALLOW_PROXY_CERTS); + break; + } + return 1; + +} + +/* + * Parse the next flag (and value if specified), return 0 if done, -1 on + * error, otherwise the flag's retval. + */ +int opt_next(void) +{ + char *p; + const OPTIONS *o; + int ival; + long lval; + unsigned long ulval; + ossl_intmax_t imval; + ossl_uintmax_t umval; + + /* Look at current arg; at end of the list? */ + arg = NULL; + p = argv[opt_index]; + if (p == NULL) + return 0; + + /* If word doesn't start with a -, we're done. */ + if (*p != '-') + return 0; + + /* Hit "--" ? We're done. */ + opt_index++; + if (strcmp(p, "--") == 0) + return 0; + + /* Allow -nnn and --nnn */ + if (*++p == '-') + p++; + flag = p - 1; + + /* If we have --flag=foo, snip it off */ + if ((arg = strchr(p, '=')) != NULL) + *arg++ = '\0'; + for (o = opts; o->name; ++o) { + /* If not this option, move on to the next one. */ + if (strcmp(p, o->name) != 0) + continue; + + /* If it doesn't take a value, make sure none was given. */ + if (o->valtype == 0 || o->valtype == '-') { + if (arg) { + BIO_printf(bio_err, + "%s: Option -%s does not take a value\n", prog, p); + return -1; + } + return o->retval; + } + + /* Want a value; get the next param if =foo not used. */ + if (arg == NULL) { + if (argv[opt_index] == NULL) { + BIO_printf(bio_err, + "%s: Option -%s needs a value\n", prog, o->name); + return -1; + } + arg = argv[opt_index++]; + } + + /* Syntax-check value. */ + switch (o->valtype) { + default: + case 's': + /* Just a string. */ + break; + case '/': + if (app_isdir(arg) >= 0) + break; + BIO_printf(bio_err, "%s: Not a directory: %s\n", prog, arg); + return -1; + case '<': + /* Input file. */ + if (strcmp(arg, "-") == 0 || app_access(arg, R_OK) >= 0) + break; + BIO_printf(bio_err, + "%s: Cannot open input file %s, %s\n", + prog, arg, strerror(errno)); + return -1; + case '>': + /* Output file. */ + if (strcmp(arg, "-") == 0 || app_access(arg, W_OK) >= 0 || errno == ENOENT) + break; + BIO_printf(bio_err, + "%s: Cannot open output file %s, %s\n", + prog, arg, strerror(errno)); + return -1; + case 'p': + case 'n': + if (!opt_int(arg, &ival) + || (o->valtype == 'p' && ival <= 0)) { + BIO_printf(bio_err, + "%s: Non-positive number \"%s\" for -%s\n", + prog, arg, o->name); + return -1; + } + break; + case 'M': + if (!opt_imax(arg, &imval)) { + BIO_printf(bio_err, + "%s: Invalid number \"%s\" for -%s\n", + prog, arg, o->name); + return -1; + } + break; + case 'U': + if (!opt_umax(arg, &umval)) { + BIO_printf(bio_err, + "%s: Invalid number \"%s\" for -%s\n", + prog, arg, o->name); + return -1; + } + break; + case 'l': + if (!opt_long(arg, &lval)) { + BIO_printf(bio_err, + "%s: Invalid number \"%s\" for -%s\n", + prog, arg, o->name); + return -1; + } + break; + case 'u': + if (!opt_ulong(arg, &ulval)) { + BIO_printf(bio_err, + "%s: Invalid number \"%s\" for -%s\n", + prog, arg, o->name); + return -1; + } + break; + case 'c': + case 'E': + case 'F': + case 'f': + if (opt_format(arg, + o->valtype == 'c' ? OPT_FMT_PDS : + o->valtype == 'E' ? OPT_FMT_PDE : + o->valtype == 'F' ? OPT_FMT_PEMDER + : OPT_FMT_ANY, &ival)) + break; + BIO_printf(bio_err, + "%s: Invalid format \"%s\" for -%s\n", + prog, arg, o->name); + return -1; + } + + /* Return the flag value. */ + return o->retval; + } + if (unknown != NULL) { + dunno = p; + return unknown->retval; + } + BIO_printf(bio_err, "%s: Option unknown option -%s\n", prog, p); + return -1; +} + +/* Return the most recent flag parameter. */ +char *opt_arg(void) +{ + return arg; +} + +/* Return the most recent flag. */ +char *opt_flag(void) +{ + return flag; +} + +/* Return the unknown option. */ +char *opt_unknown(void) +{ + return dunno; +} + +/* Return the rest of the arguments after parsing flags. */ +char **opt_rest(void) +{ + return &argv[opt_index]; +} + +/* How many items in remaining args? */ +int opt_num_rest(void) +{ + int i = 0; + char **pp; + + for (pp = opt_rest(); *pp; pp++, i++) + continue; + return i; +} + +/* Return a string describing the parameter type. */ +static const char *valtype2param(const OPTIONS *o) +{ + switch (o->valtype) { + case 0: + case '-': + return ""; + case 's': + return "val"; + case '/': + return "dir"; + case '<': + return "infile"; + case '>': + return "outfile"; + case 'p': + return "+int"; + case 'n': + return "int"; + case 'l': + return "long"; + case 'u': + return "ulong"; + case 'E': + return "PEM|DER|ENGINE"; + case 'F': + return "PEM|DER"; + case 'f': + return "format"; + case 'M': + return "intmax"; + case 'U': + return "uintmax"; + } + return "parm"; +} + +void opt_help(const OPTIONS *list) +{ + const OPTIONS *o; + int i; + int standard_prolog; + int width = 5; + char start[80 + 1]; + char *p; + const char *help; + + /* Starts with its own help message? */ + standard_prolog = list[0].name != OPT_HELP_STR; + + /* Find the widest help. */ + for (o = list; o->name; o++) { + if (o->name == OPT_MORE_STR) + continue; + i = 2 + (int)strlen(o->name); + if (o->valtype != '-') + i += 1 + strlen(valtype2param(o)); + if (i < MAX_OPT_HELP_WIDTH && i > width) + width = i; + assert(i < (int)sizeof start); + } + + if (standard_prolog) + BIO_printf(bio_err, "Usage: %s [options]\nValid options are:\n", + prog); + + /* Now let's print. */ + for (o = list; o->name; o++) { + help = o->helpstr ? o->helpstr : "(No additional info)"; + if (o->name == OPT_HELP_STR) { + BIO_printf(bio_err, help, prog); + continue; + } + + /* Pad out prefix */ + memset(start, ' ', sizeof(start) - 1); + start[sizeof start - 1] = '\0'; + + if (o->name == OPT_MORE_STR) { + /* Continuation of previous line; pad and print. */ + start[width] = '\0'; + BIO_printf(bio_err, "%s %s\n", start, help); + continue; + } + + /* Build up the "-flag [param]" part. */ + p = start; + *p++ = ' '; + *p++ = '-'; + if (o->name[0]) + p += strlen(strcpy(p, o->name)); + else + *p++ = '*'; + if (o->valtype != '-') { + *p++ = ' '; + p += strlen(strcpy(p, valtype2param(o))); + } + *p = ' '; + if ((int)(p - start) >= MAX_OPT_HELP_WIDTH) { + *p = '\0'; + BIO_printf(bio_err, "%s\n", start); + memset(start, ' ', sizeof(start)); + } + start[width] = '\0'; + BIO_printf(bio_err, "%s %s\n", start, help); + } +} + +#ifdef COMPILE_STANDALONE_TEST_DRIVER +# include + +typedef enum OPTION_choice { + OPT_ERR = -1, OPT_EOF = 0, OPT_HELP, + OPT_IN, OPT_INFORM, OPT_OUT, OPT_COUNT, OPT_U, OPT_FLAG, + OPT_STR, OPT_NOTUSED +} OPTION_CHOICE; + +static OPTIONS options[] = { + {OPT_HELP_STR, 1, '-', "Usage: %s flags\n"}, + {OPT_HELP_STR, 1, '-', "Valid options are:\n"}, + {"help", OPT_HELP, '-', "Display this summary"}, + {"in", OPT_IN, '<', "input file"}, + {OPT_MORE_STR, 1, '-', "more detail about input"}, + {"inform", OPT_INFORM, 'f', "input file format; defaults to pem"}, + {"out", OPT_OUT, '>', "output file"}, + {"count", OPT_COUNT, 'p', "a counter greater than zero"}, + {"u", OPT_U, 'u', "an unsigned number"}, + {"flag", OPT_FLAG, 0, "just some flag"}, + {"str", OPT_STR, 's', "the magic word"}, + {"areallyverylongoption", OPT_HELP, '-', "long way for help"}, + {NULL} +}; + +BIO *bio_err; + +int app_isdir(const char *name) +{ + struct stat sb; + + return name != NULL && stat(name, &sb) >= 0 && S_ISDIR(sb.st_mode); +} + +int main(int ac, char **av) +{ + OPTION_CHOICE o; + char **rest; + char *prog; + + bio_err = BIO_new_fp(stderr, BIO_NOCLOSE | BIO_FP_TEXT); + + prog = opt_init(ac, av, options); + while ((o = opt_next()) != OPT_EOF) { + switch (c) { + case OPT_NOTUSED: + case OPT_EOF: + case OPT_ERR: + printf("%s: Usage error; try -help.\n", prog); + return 1; + case OPT_HELP: + opt_help(options); + return 0; + case OPT_IN: + printf("in %s\n", opt_arg()); + break; + case OPT_INFORM: + printf("inform %s\n", opt_arg()); + break; + case OPT_OUT: + printf("out %s\n", opt_arg()); + break; + case OPT_COUNT: + printf("count %s\n", opt_arg()); + break; + case OPT_U: + printf("u %s\n", opt_arg()); + break; + case OPT_FLAG: + printf("flag\n"); + break; + case OPT_STR: + printf("str %s\n", opt_arg()); + break; + } + } + argc = opt_num_rest(); + argv = opt_rest(); + + printf("args = %d\n", argc); + if (argc) + while (*argv) + printf(" %s\n", *argv++); + return 0; +} +#endif diff --git a/apps/otp.c b/apps/otp.c new file mode 100644 index 000000000..338fa7d90 --- /dev/null +++ b/apps/otp.c @@ -0,0 +1,174 @@ +/* ==================================================================== + * Copyright (c) 2014 - 2018 The GmSSL Project. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the GmSSL Project. + * (http://gmssl.org/)" + * + * 4. The name "GmSSL Project" must not be used to endorse or promote + * products derived from this software without prior written + * permission. For written permission, please contact + * guanzhi1980@gmail.com. + * + * 5. Products derived from this software may not be called "GmSSL" + * nor may "GmSSL" appear in their names without prior written + * permission of the GmSSL Project. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the GmSSL Project + * (http://gmssl.org/)" + * + * THIS SOFTWARE IS PROVIDED BY THE GmSSL PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE GmSSL PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + */ + +#include +#ifdef OPENSSL_NO_OTP +NON_EMPTY_TRANSLATION_UNIT +#else + +# include +# include +# include +# include +# include +# include +# include +# include "apps.h" + +typedef enum OPTION_choice { + OPT_ERR = -1, OPT_EOF = 0, OPT_HELP, + OPT_IN, OPT_OUT, OPT_SETUP, OPT_GENKEY +} OPTION_CHOICE; + +OPTIONS otp_options[] = { + {"help", OPT_HELP, '-', "Display this summary"}, + {"in", OPT_IN, '<', "Input seed file - default stdin"}, + {"out", OPT_OUT, '>', "Output seed file - default stdout"}, + {"setup", OPT_SETUP, '-', "Generate seed"}, + {"genkey", OPT_GENKEY, '-', "Generate one-time password"}, + {NULL} +}; + +int otp_main(int argc, char **argv) +{ + int ret = 1; + BIO *bio = NULL; + char *infile = NULL, *outfile = NULL, *prog; + OPTION_CHOICE o; + int setup = 0, genkey = 0; + unsigned char key[32]; + + prog = opt_init(argc, argv, otp_options); + while ((o = opt_next()) != OPT_EOF) { + switch (o) { + case OPT_EOF: + case OPT_ERR: +opthelp: + BIO_printf(bio_err, "%s: Use -help for summary.\n", prog); + goto end; + case OPT_HELP: + opt_help(otp_options); + ret = 0; + goto end; + case OPT_IN: + infile = opt_arg(); + break; + case OPT_OUT: + outfile = opt_arg(); + break; + case OPT_SETUP: + setup = 1; + break; + case OPT_GENKEY: + genkey = 1; + break; + } + } + argc = opt_num_rest(); + if (argc != 0) + goto opthelp; + + if (!(setup ^ genkey)) + goto opthelp; + + if (setup) { + unsigned char key[32]; + + if (!RAND_bytes(key, sizeof(key))) { + ERR_print_errors(bio_err); + goto end; + } + + bio = bio_open_default(outfile, 'w', FORMAT_BINARY); + if (!bio) + goto end; + + if (BIO_write(bio, key, sizeof(key)) != sizeof(key)) { + goto end; + } + + BIO_printf(bio_err, "generate OTP seed in '%s'\n", outfile); + + } else if (genkey) { + + OTP_PARAMS params; + unsigned char event[] = "gmssl otp event default"; + unsigned int otp; + + params.type = NID_sm3; + params.te = 1; + params.option = NULL; + params.option_size = 0; + params.otp_digits = 6; + + bio = bio_open_default(infile, 'r', FORMAT_BINARY); + if (!bio) + goto end; + + if (BIO_read(bio, key, sizeof(key)) != sizeof(key)) { + ERR_print_errors(bio_err); + goto end; + } + + if (!OTP_generate(¶ms, event, sizeof(event), &otp, key, sizeof(key))) { + ERR_print_errors(bio_err); + goto end; + } + + printf("%06u\n", otp); + ret = 0; + } + +end: + BIO_free(bio); + OPENSSL_cleanse(key, sizeof(key)); + return ret; +} + +#endif diff --git a/apps/paiutl.c b/apps/paiutl.c new file mode 100644 index 000000000..18a7283df --- /dev/null +++ b/apps/paiutl.c @@ -0,0 +1,277 @@ +/* ==================================================================== + * Copyright (c) 2014 - 2019 The GmSSL Project. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the GmSSL Project. + * (http://gmssl.org/)" + * + * 4. The name "GmSSL Project" must not be used to endorse or promote + * products derived from this software without prior written + * permission. For written permission, please contact + * guanzhi1980@gmail.com. + * + * 5. Products derived from this software may not be called "GmSSL" + * nor may "GmSSL" appear in their names without prior written + * permission of the GmSSL Project. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the GmSSL Project + * (http://gmssl.org/)" + * + * THIS SOFTWARE IS PROVIDED BY THE GmSSL PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE GmSSL PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + */ + +#include +#ifdef OPENSSL_NO_PAILLIER +NON_EMPTY_TRANSLATION_UNIT +#else + +# include +# include +# include +# include +# include +# include +# include +# include "apps.h" + +#define KEY_NONE 0 +#define KEY_PRIVKEY 1 +#define KEY_PUBKEY 2 +#define KEY_CERT 3 + +typedef enum OPTION_choice { + OPT_ERR = -1, OPT_EOF = 0, OPT_HELP, + OPT_IN, OPT_OUT, OPT_ADD, OPT_SCALAR_MUL, + OPT_PUBIN, OPT_INKEY, OPT_KEYFORM, OPT_PASSIN, +} OPTION_CHOICE; + +OPTIONS paiutl_options[] = { + {"help", OPT_HELP, '-', "Display this summary"}, + {"in", OPT_IN, '<', "Input file - default stdin"}, + {"out", OPT_OUT, '>', "Output file - default stdout"}, + {"add", OPT_ADD, '-', "Add ciphertexts"}, + {"scalar_mul", OPT_SCALAR_MUL, 's', "Scalar multiply"}, + {"pubin", OPT_PUBIN, '-', "Input is a public key"}, + {"inkey", OPT_INKEY, 's', "Input private key file"}, + {"keyform", OPT_KEYFORM, 'E', "Private key format - default PEM"}, + {"passin", OPT_PASSIN, 's', "Input file pass phrase source"}, + {NULL} +}; + +int paiutl_main(int argc, char **argv) +{ + int ret = 1; + OPTION_CHOICE o; + char *prog; + char *infile = NULL; + char *outfile = NULL; + BIO *in = NULL; + BIO *out = NULL; + int op = PAILLIER_OP_UNDEF; + int scalar = 1; + char *keyfile = NULL; + int key_type = KEY_PRIVKEY; + int keyform = FORMAT_PEM; + char *passinarg = NULL; + char *passin = NULL; + EVP_PKEY *pkey = NULL; + PAILLIER *key; + ASN1_INTEGER *ai = NULL; + BIGNUM *a = NULL; + BIGNUM *r = NULL; + + prog = opt_init(argc, argv, paiutl_options); + while ((o = opt_next()) != OPT_EOF) { + switch (o) { + case OPT_EOF: + case OPT_ERR: +opthelp: + BIO_printf(bio_err, "%s: Use -help for summary.\n", prog); + goto end; + case OPT_HELP: + opt_help(paiutl_options); + ret = 0; + goto end; + case OPT_IN: + infile = opt_arg(); + break; + case OPT_OUT: + outfile = opt_arg(); + break; + case OPT_ADD: + op = PAILLIER_OP_ADD; + break; + case OPT_SCALAR_MUL: + op = PAILLIER_OP_SCALAR_MUL; + scalar = atoi(opt_arg()); + break; + case OPT_INKEY: + keyfile = opt_arg(); + break; + case OPT_PUBIN: + key_type = KEY_PUBKEY; + break; + case OPT_KEYFORM: + if (!opt_format(opt_arg(), OPT_FMT_PDE, &keyform)) + goto opthelp; + break; + case OPT_PASSIN: + passinarg = opt_arg(); + break; + } + } + argc = opt_num_rest(); + if (argc != 0) + goto opthelp; + + app_RAND_load_file(NULL, 0); + + if (!(in = bio_open_default(infile, 'r', FORMAT_BINARY))) { + BIO_printf(bio_err, "Error reading input file\n"); + goto end; + } + + if (!(out = bio_open_default(outfile, 'w', FORMAT_BINARY))) { + BIO_printf(bio_err, "Error writting output file\n"); + goto end; + } + + if (key_type == KEY_PRIVKEY) { + if (!app_passwd(passinarg, NULL, &passin, NULL)) { + BIO_printf(bio_err, "Error getting password\n"); + goto end; + } + if (!(pkey = load_key(keyfile, keyform, 0, passin, NULL, "Private Key"))) { + BIO_printf(bio_err, "Error reading private key\n"); + goto end; + } + } else { + if (!(pkey = load_pubkey(keyfile, keyform, 0, NULL, NULL, "Public Key"))) { + BIO_printf(bio_err, "Error reading public key\n"); + goto end; + } + } + + if (!(key = EVP_PKEY_get0_PAILLIER(pkey))) { + BIO_printf(bio_err, "Error key type\n"); + goto end; + } + + /* get the first oprand */ + if (!(ai = ASN1_item_d2i_bio(ASN1_ITEM_rptr(ASN1_INTEGER), in, NULL))) { + ERR_print_errors(bio_err); + goto end; + } + if (!(r = ASN1_INTEGER_to_BN(ai, NULL))) { + ERR_print_errors(bio_err); + goto end; + } + if (!(a = BN_new())) { + goto end; + } + + + if (op == PAILLIER_OP_ADD) { + + /* add the second oprand */ + if (!ASN1_item_d2i_bio(ASN1_ITEM_rptr(ASN1_INTEGER), in, &ai)) { + ERR_print_errors(bio_err); + goto end; + } + if (!ASN1_INTEGER_to_BN(ai, a)) { + ERR_print_errors(bio_err); + goto end; + } + if (!PAILLIER_ciphertext_add(r, r, a, key)) { + ERR_print_errors(bio_err); + goto end; + } + + /* (optional) continue */ + while (ASN1_item_d2i_bio(ASN1_ITEM_rptr(ASN1_INTEGER), in, &ai)) { + if (!ASN1_INTEGER_to_BN(ai, a)) { + ERR_print_errors(bio_err); + goto end; + } + if (!PAILLIER_ciphertext_add(r, r, a, key)) { + ERR_print_errors(bio_err); + goto end; + } + } + + /* output sum */ + if (!BN_to_ASN1_INTEGER(r, ai) + || !ASN1_item_i2d_bio(ASN1_ITEM_rptr(ASN1_INTEGER), out, ai)) { + ERR_print_errors(bio_err); + goto end; + } + + } else if (op == PAILLIER_OP_SCALAR_MUL) { + + /* scalar mul the first ciphertext */ + if (!BN_set_word(a, scalar)) { + ERR_print_errors(bio_err); + goto end; + } + if (!PAILLIER_ciphertext_scalar_mul(r, a, r, key)) { + ERR_print_errors(bio_err); + goto end; + } + if (!BN_to_ASN1_INTEGER(r, ai) + || !ASN1_item_i2d_bio(ASN1_ITEM_rptr(ASN1_INTEGER), out, ai)) { + ERR_print_errors(bio_err); + goto end; + } + + /* (optional) do more, do not decrypt the output with `pkeyutl` */ + while (ASN1_item_d2i_bio(ASN1_ITEM_rptr(ASN1_INTEGER), in, &ai)) { + if (!ASN1_INTEGER_to_BN(ai, r) + || !PAILLIER_ciphertext_scalar_mul(r, r, a, key) + || !BN_to_ASN1_INTEGER(r, ai) + || !ASN1_item_i2d_bio(ASN1_ITEM_rptr(ASN1_INTEGER), out, ai)) { + ERR_print_errors(bio_err); + goto end; + } + } + } else { + BIO_printf(bio_err, "No operation assigned\n"); + goto end; + } + + ret = 0; + +end: + OPENSSL_free(passin); + ASN1_INTEGER_free(ai); + BN_free(a); + BN_free(r); + return ret; +} +#endif diff --git a/apps/passwd.c b/apps/passwd.c new file mode 100644 index 000000000..7cfd5b0b4 --- /dev/null +++ b/apps/passwd.c @@ -0,0 +1,512 @@ +/* + * Copyright 2000-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ +#include + +#if defined OPENSSL_NO_MD5 || defined CHARSET_EBCDIC +# define NO_MD5CRYPT_1 +#endif + +# include + +# include "apps.h" + +#if !defined(OPENSSL_NO_DES) || !defined(NO_MD5CRYPT_1) +# include +# include +# include +# include +# ifndef OPENSSL_NO_DES +# include +# endif +# ifndef NO_MD5CRYPT_1 +# include +# endif + +static unsigned const char cov_2char[64] = { + /* from crypto/des/fcrypt.c */ + 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, + 0x36, 0x37, 0x38, 0x39, 0x41, 0x42, 0x43, 0x44, + 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, + 0x4D, 0x4E, 0x4F, 0x50, 0x51, 0x52, 0x53, 0x54, + 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x61, 0x62, + 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, + 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72, + 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A +}; + +static int do_passwd(int passed_salt, char **salt_p, char **salt_malloc_p, + char *passwd, BIO *out, int quiet, int table, + int reverse, size_t pw_maxlen, int usecrypt, int use1, + int useapr1); + +typedef enum OPTION_choice { + OPT_ERR = -1, OPT_EOF = 0, OPT_HELP, + OPT_IN, + OPT_NOVERIFY, OPT_QUIET, OPT_TABLE, OPT_REVERSE, OPT_APR1, + OPT_1, OPT_CRYPT, OPT_SALT, OPT_STDIN +} OPTION_CHOICE; + +OPTIONS passwd_options[] = { + {"help", OPT_HELP, '-', "Display this summary"}, + {"in", OPT_IN, '<', "Pead passwords from file"}, + {"noverify", OPT_NOVERIFY, '-', + "Never verify when reading password from terminal"}, + {"quiet", OPT_QUIET, '-', "No warnings"}, + {"table", OPT_TABLE, '-', "Format output as table"}, + {"reverse", OPT_REVERSE, '-', "Switch table columns"}, + {"salt", OPT_SALT, 's', "Use provided salt"}, + {"stdin", OPT_STDIN, '-', "Read passwords from stdin"}, +# ifndef NO_MD5CRYPT_1 + {"apr1", OPT_APR1, '-', "MD5-based password algorithm, Apache variant"}, + {"1", OPT_1, '-', "MD5-based password algorithm"}, +# endif +# ifndef OPENSSL_NO_DES + {"crypt", OPT_CRYPT, '-', "Standard Unix password algorithm (default)"}, +# endif + {NULL} +}; + +int passwd_main(int argc, char **argv) +{ + BIO *in = NULL; + char *infile = NULL, *salt = NULL, *passwd = NULL, **passwds = NULL; + char *salt_malloc = NULL, *passwd_malloc = NULL, *prog; + OPTION_CHOICE o; + int in_stdin = 0, pw_source_defined = 0; +#ifndef OPENSSL_NO_UI + int in_noverify = 0; +#endif + int passed_salt = 0, quiet = 0, table = 0, reverse = 0; + int ret = 1, usecrypt = 0, use1 = 0, useapr1 = 0; + size_t passwd_malloc_size = 0, pw_maxlen = 256; + + prog = opt_init(argc, argv, passwd_options); + while ((o = opt_next()) != OPT_EOF) { + switch (o) { + case OPT_EOF: + case OPT_ERR: + opthelp: + BIO_printf(bio_err, "%s: Use -help for summary.\n", prog); + goto end; + case OPT_HELP: + opt_help(passwd_options); + ret = 0; + goto end; + case OPT_IN: + if (pw_source_defined) + goto opthelp; + infile = opt_arg(); + pw_source_defined = 1; + break; + case OPT_NOVERIFY: +#ifndef OPENSSL_NO_UI + in_noverify = 1; +#endif + break; + case OPT_QUIET: + quiet = 1; + break; + case OPT_TABLE: + table = 1; + break; + case OPT_REVERSE: + reverse = 1; + break; + case OPT_1: + use1 = 1; + break; + case OPT_APR1: + useapr1 = 1; + break; + case OPT_CRYPT: + usecrypt = 1; + break; + case OPT_SALT: + passed_salt = 1; + salt = opt_arg(); + break; + case OPT_STDIN: + if (pw_source_defined) + goto opthelp; + in_stdin = 1; + pw_source_defined = 1; + break; + } + } + argc = opt_num_rest(); + argv = opt_rest(); + + if (*argv) { + if (pw_source_defined) + goto opthelp; + pw_source_defined = 1; + passwds = argv; + } + + if (!usecrypt && !use1 && !useapr1) { + /* use default */ + usecrypt = 1; + } + if (usecrypt + use1 + useapr1 > 1) { + /* conflict */ + goto opthelp; + } + +# ifdef OPENSSL_NO_DES + if (usecrypt) + goto opthelp; +# endif +# ifdef NO_MD5CRYPT_1 + if (use1 || useapr1) + goto opthelp; +# endif + + if (infile != NULL && in_stdin) { + BIO_printf(bio_err, "%s: Can't combine -in and -stdin\n", prog); + goto end; + } + + if (infile != NULL || in_stdin) { + /* + * If in_stdin is true, we know that infile is NULL, and that + * bio_open_default() will give us back an alias for stdin. + */ + in = bio_open_default(infile, 'r', FORMAT_TEXT); + if (in == NULL) + goto end; + } + + if (usecrypt) + pw_maxlen = 8; + else if (use1 || useapr1) + pw_maxlen = 256; /* arbitrary limit, should be enough for most + * passwords */ + + if (passwds == NULL) { + /* no passwords on the command line */ + + passwd_malloc_size = pw_maxlen + 2; + /* longer than necessary so that we can warn about truncation */ + passwd = passwd_malloc = + app_malloc(passwd_malloc_size, "password buffer"); + } + + if ((in == NULL) && (passwds == NULL)) { + if (1) { +#ifndef OPENSSL_NO_UI + /* build a null-terminated list */ + static char *passwds_static[2] = { NULL, NULL }; + + passwds = passwds_static; + if (in == NULL) + if (EVP_read_pw_string + (passwd_malloc, passwd_malloc_size, "Password: ", + !(passed_salt || in_noverify)) != 0) + goto end; + passwds[0] = passwd_malloc; + } else { +#endif + BIO_printf(bio_err, "password required\n"); + goto end; + } + } + + + if (in == NULL) { + assert(passwds != NULL); + assert(*passwds != NULL); + + do { /* loop over list of passwords */ + passwd = *passwds++; + if (!do_passwd(passed_salt, &salt, &salt_malloc, passwd, bio_out, + quiet, table, reverse, pw_maxlen, usecrypt, use1, + useapr1)) + goto end; + } + while (*passwds != NULL); + } else + /* in != NULL */ + { + int done; + + assert(passwd != NULL); + do { + int r = BIO_gets(in, passwd, pw_maxlen + 1); + if (r > 0) { + char *c = (strchr(passwd, '\n')); + if (c != NULL) + *c = 0; /* truncate at newline */ + else { + /* ignore rest of line */ + char trash[BUFSIZ]; + do + r = BIO_gets(in, trash, sizeof trash); + while ((r > 0) && (!strchr(trash, '\n'))); + } + + if (!do_passwd + (passed_salt, &salt, &salt_malloc, passwd, bio_out, quiet, + table, reverse, pw_maxlen, usecrypt, use1, useapr1)) + goto end; + } + done = (r <= 0); + } + while (!done); + } + ret = 0; + + end: + ERR_print_errors(bio_err); + OPENSSL_free(salt_malloc); + OPENSSL_free(passwd_malloc); + BIO_free(in); + return (ret); +} + +# ifndef NO_MD5CRYPT_1 +/* + * MD5-based password algorithm (should probably be available as a library + * function; then the static buffer would not be acceptable). For magic + * string "1", this should be compatible to the MD5-based BSD password + * algorithm. For 'magic' string "apr1", this is compatible to the MD5-based + * Apache password algorithm. (Apparently, the Apache password algorithm is + * identical except that the 'magic' string was changed -- the laziest + * application of the NIH principle I've ever encountered.) + */ +static char *md5crypt(const char *passwd, const char *magic, const char *salt) +{ + /* "$apr1$..salt..$.......md5hash..........\0" */ + static char out_buf[6 + 9 + 24 + 2]; + unsigned char buf[MD5_DIGEST_LENGTH]; + char *salt_out; + int n; + unsigned int i; + EVP_MD_CTX *md = NULL, *md2 = NULL; + size_t passwd_len, salt_len, magic_len; + + passwd_len = strlen(passwd); + out_buf[0] = '$'; + out_buf[1] = 0; + magic_len = strlen(magic); + + if (magic_len > 4) /* assert it's "1" or "apr1" */ + return NULL; + + OPENSSL_strlcat(out_buf, magic, sizeof out_buf); + OPENSSL_strlcat(out_buf, "$", sizeof out_buf); + OPENSSL_strlcat(out_buf, salt, sizeof out_buf); + + if (strlen(out_buf) > 6 + 8) /* assert "$apr1$..salt.." */ + return NULL; + + salt_out = out_buf + 2 + magic_len; + salt_len = strlen(salt_out); + + if (salt_len > 8) + return NULL; + + md = EVP_MD_CTX_new(); + if (md == NULL + || !EVP_DigestInit_ex(md, EVP_md5(), NULL) + || !EVP_DigestUpdate(md, passwd, passwd_len) + || !EVP_DigestUpdate(md, "$", 1) + || !EVP_DigestUpdate(md, magic, magic_len) + || !EVP_DigestUpdate(md, "$", 1) + || !EVP_DigestUpdate(md, salt_out, salt_len)) + goto err; + + md2 = EVP_MD_CTX_new(); + if (md2 == NULL + || !EVP_DigestInit_ex(md2, EVP_md5(), NULL) + || !EVP_DigestUpdate(md2, passwd, passwd_len) + || !EVP_DigestUpdate(md2, salt_out, salt_len) + || !EVP_DigestUpdate(md2, passwd, passwd_len) + || !EVP_DigestFinal_ex(md2, buf, NULL)) + goto err; + + for (i = passwd_len; i > sizeof buf; i -= sizeof buf) { + if (!EVP_DigestUpdate(md, buf, sizeof buf)) + goto err; + } + if (!EVP_DigestUpdate(md, buf, i)) + goto err; + + n = passwd_len; + while (n) { + if (!EVP_DigestUpdate(md, (n & 1) ? "\0" : passwd, 1)) + goto err; + n >>= 1; + } + if (!EVP_DigestFinal_ex(md, buf, NULL)) + return NULL; + + for (i = 0; i < 1000; i++) { + if (!EVP_DigestInit_ex(md2, EVP_md5(), NULL)) + goto err; + if (!EVP_DigestUpdate(md2, + (i & 1) ? (unsigned const char *)passwd : buf, + (i & 1) ? passwd_len : sizeof buf)) + goto err; + if (i % 3) { + if (!EVP_DigestUpdate(md2, salt_out, salt_len)) + goto err; + } + if (i % 7) { + if (!EVP_DigestUpdate(md2, passwd, passwd_len)) + goto err; + } + if (!EVP_DigestUpdate(md2, + (i & 1) ? buf : (unsigned const char *)passwd, + (i & 1) ? sizeof buf : passwd_len)) + goto err; + if (!EVP_DigestFinal_ex(md2, buf, NULL)) + goto err; + } + EVP_MD_CTX_free(md2); + EVP_MD_CTX_free(md); + md2 = NULL; + md = NULL; + + { + /* transform buf into output string */ + unsigned char buf_perm[sizeof buf]; + int dest, source; + char *output; + + /* silly output permutation */ + for (dest = 0, source = 0; dest < 14; + dest++, source = (source + 6) % 17) + buf_perm[dest] = buf[source]; + buf_perm[14] = buf[5]; + buf_perm[15] = buf[11]; +# ifndef PEDANTIC /* Unfortunately, this generates a "no + * effect" warning */ + assert(16 == sizeof buf_perm); +# endif + + output = salt_out + salt_len; + assert(output == out_buf + strlen(out_buf)); + + *output++ = '$'; + + for (i = 0; i < 15; i += 3) { + *output++ = cov_2char[buf_perm[i + 2] & 0x3f]; + *output++ = cov_2char[((buf_perm[i + 1] & 0xf) << 2) | + (buf_perm[i + 2] >> 6)]; + *output++ = cov_2char[((buf_perm[i] & 3) << 4) | + (buf_perm[i + 1] >> 4)]; + *output++ = cov_2char[buf_perm[i] >> 2]; + } + assert(i == 15); + *output++ = cov_2char[buf_perm[i] & 0x3f]; + *output++ = cov_2char[buf_perm[i] >> 6]; + *output = 0; + assert(strlen(out_buf) < sizeof(out_buf)); + } + + return out_buf; + + err: + EVP_MD_CTX_free(md2); + EVP_MD_CTX_free(md); + return NULL; +} +# endif + +static int do_passwd(int passed_salt, char **salt_p, char **salt_malloc_p, + char *passwd, BIO *out, int quiet, int table, + int reverse, size_t pw_maxlen, int usecrypt, int use1, + int useapr1) +{ + char *hash = NULL; + + assert(salt_p != NULL); + assert(salt_malloc_p != NULL); + + /* first make sure we have a salt */ + if (!passed_salt) { +# ifndef OPENSSL_NO_DES + if (usecrypt) { + if (*salt_malloc_p == NULL) { + *salt_p = *salt_malloc_p = app_malloc(3, "salt buffer"); + } + if (RAND_bytes((unsigned char *)*salt_p, 2) <= 0) + goto end; + (*salt_p)[0] = cov_2char[(*salt_p)[0] & 0x3f]; /* 6 bits */ + (*salt_p)[1] = cov_2char[(*salt_p)[1] & 0x3f]; /* 6 bits */ + (*salt_p)[2] = 0; +# ifdef CHARSET_EBCDIC + ascii2ebcdic(*salt_p, *salt_p, 2); /* des_crypt will convert back + * to ASCII */ +# endif + } +# endif /* !OPENSSL_NO_DES */ + +# ifndef NO_MD5CRYPT_1 + if (use1 || useapr1) { + int i; + + if (*salt_malloc_p == NULL) { + *salt_p = *salt_malloc_p = app_malloc(9, "salt buffer"); + } + if (RAND_bytes((unsigned char *)*salt_p, 8) <= 0) + goto end; + + for (i = 0; i < 8; i++) + (*salt_p)[i] = cov_2char[(*salt_p)[i] & 0x3f]; /* 6 bits */ + (*salt_p)[8] = 0; + } +# endif /* !NO_MD5CRYPT_1 */ + } + + assert(*salt_p != NULL); + + /* truncate password if necessary */ + if ((strlen(passwd) > pw_maxlen)) { + if (!quiet) + /* + * XXX: really we should know how to print a size_t, not cast it + */ + BIO_printf(bio_err, + "Warning: truncating password to %u characters\n", + (unsigned)pw_maxlen); + passwd[pw_maxlen] = 0; + } + assert(strlen(passwd) <= pw_maxlen); + + /* now compute password hash */ +# ifndef OPENSSL_NO_DES + if (usecrypt) + hash = DES_crypt(passwd, *salt_p); +# endif +# ifndef NO_MD5CRYPT_1 + if (use1 || useapr1) + hash = md5crypt(passwd, (use1 ? "1" : "apr1"), *salt_p); +# endif + assert(hash != NULL); + + if (table && !reverse) + BIO_printf(out, "%s\t%s\n", passwd, hash); + else if (table && reverse) + BIO_printf(out, "%s\t%s\n", hash, passwd); + else + BIO_printf(out, "%s\n", hash); + return 1; + + end: + return 0; +} +#else + +int passwd_main(int argc, char **argv) +{ + BIO_printf(bio_err, "Program not available.\n"); + return (1); +} +#endif diff --git a/apps/pca-cert.srl b/apps/pca-cert.srl new file mode 100644 index 000000000..2c7456e3e --- /dev/null +++ b/apps/pca-cert.srl @@ -0,0 +1 @@ +07 diff --git a/apps/pca-key.pem b/apps/pca-key.pem new file mode 100644 index 000000000..c6ad0e92d --- /dev/null +++ b/apps/pca-key.pem @@ -0,0 +1,16 @@ +-----BEGIN PRIVATE KEY----- +MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBALYYjjtpLs/lfkPF +xAFZ4V3He5mZFbsEakK9bA2fQaryreRwyfhbXbDJHyBV+c4xI5fbmmVd2t/us4k4 +rMhGsBtL89SqCEHhPJpLFywiQVmJTAjANYrWkZK5uR/++YmZyzuLfPHLButuK6cF +GKXw3NNToxjYooMf0mad2rPX3cKTAgMBAAECgYBvrJ+Nz/Pli9jjt2V9bqHH4Y7r +o/avuwVv6Ltbn0+mhy4d6w3yQhYzVSTBr/iDe59YglUt1WFl8/4nKZrNOIzHJlav +Sw4hd3fYBHxbT+DgZMQ9ikjHECWRdDffrnlTLsSJAcxnpMJBPe3dKCRDMUrqWUvB +IIKaxyqmXJms5Y/wAQJBAPFL9NMKJcWBftMKXCasxsV0ZGjgqHGZODYjtGFN9jJO +6AbZrxfCcapTWG4RCC2o/EDEMN8aArEhfdrYY3lhXGsCQQDBMRzFevkD7SYXTw5G +NA/gJOAsFMYbt7tebcCRsHT7t3ymVfO2QwK7ZF0f/SYvi7cMAPraHvO7s3kFdGTB +kDx5AkAHBICASsFCdzurA5gef9PgFjx9WFtNwnkCChPK6KuKVwUkfdw7wqnvnDDs +Mo6cVVfQwmPxeR4u7JxuavCprQ01AkEAp5ZGAh1J9Jj9CQ1AMbAp8WOrvzGKJTM9 +641Dll4/LLif/d7j2kDJFuvaSMyeGnKVqGkVMq/U+QeYPR4Z5TuM6QJAWK05qFed +wYgTZyVN0MY53ZOMAIWwjz0cr24TvDfmsZqIvguGL616GKQZKdKDZQyQHg+dCzqJ +HgIoacuFDKz5CA== +-----END PRIVATE KEY----- diff --git a/apps/pca-req.pem b/apps/pca-req.pem new file mode 100644 index 000000000..5a8c5cbf1 --- /dev/null +++ b/apps/pca-req.pem @@ -0,0 +1,11 @@ +-----BEGIN CERTIFICATE REQUEST----- +MIIBnDCCAQUCAQAwXDELMAkGA1UEBhMCQVUxEzARBgNVBAgMClF1ZWVuc2xhbmQx +GjAYBgNVBAoMEUNyeXB0U29mdCBQdHkgTHRkMRwwGgYDVQQDDBNUZXN0IFBDQSAo +MTAyNCBiaXQpMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC2GI47aS7P5X5D +xcQBWeFdx3uZmRW7BGpCvWwNn0Gq8q3kcMn4W12wyR8gVfnOMSOX25plXdrf7rOJ +OKzIRrAbS/PUqghB4TyaSxcsIkFZiUwIwDWK1pGSubkf/vmJmcs7i3zxywbrbiun +BRil8NzTU6MY2KKDH9Jmndqz193CkwIDAQABoAAwDQYJKoZIhvcNAQELBQADgYEA +eJdCB0nHnFK0hek4biAxX0GuJXkknuUy46NKEhv3GBwt4gtO29bfkbQTGOsBBKNs +KptlnkItscOXY+0lSva9K3XlwD9do7k2IZFtXJVayZVw1GcKybIY0l7B6kcSxG7T +f3CsO+ifdrsJKtyoZNs96lBMrtXyGybt3mgQNdZauQU= +-----END CERTIFICATE REQUEST----- diff --git a/apps/pkcs12.c b/apps/pkcs12.c new file mode 100644 index 000000000..e5a2f930a --- /dev/null +++ b/apps/pkcs12.c @@ -0,0 +1,946 @@ +/* + * Copyright 1999-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +//#if defined(OPENSSL_NO_DES) +//NON_EMPTY_TRANSLATION_UNIT +//#else + +# include +# include +# include +# include "apps.h" +# include +# include +# include +# include + +# define NOKEYS 0x1 +# define NOCERTS 0x2 +# define INFO 0x4 +# define CLCERTS 0x8 +# define CACERTS 0x10 + +static int get_cert_chain(X509 *cert, X509_STORE *store, + STACK_OF(X509) **chain); +int dump_certs_keys_p12(BIO *out, const PKCS12 *p12, + const char *pass, int passlen, int options, + char *pempass, const EVP_CIPHER *enc); +int dump_certs_pkeys_bags(BIO *out, const STACK_OF(PKCS12_SAFEBAG) *bags, + const char *pass, int passlen, int options, + char *pempass, const EVP_CIPHER *enc); +int dump_certs_pkeys_bag(BIO *out, const PKCS12_SAFEBAG *bags, + const char *pass, int passlen, + int options, char *pempass, const EVP_CIPHER *enc); +int print_attribs(BIO *out, const STACK_OF(X509_ATTRIBUTE) *attrlst, + const char *name); +void hex_prin(BIO *out, unsigned char *buf, int len); +static int alg_print(const X509_ALGOR *alg); +int cert_load(BIO *in, STACK_OF(X509) *sk); +static int set_pbe(int *ppbe, const char *str); + +typedef enum OPTION_choice { + OPT_ERR = -1, OPT_EOF = 0, OPT_HELP, + OPT_CIPHER, OPT_NOKEYS, OPT_KEYEX, OPT_KEYSIG, OPT_NOCERTS, OPT_CLCERTS, + OPT_CACERTS, OPT_NOOUT, OPT_INFO, OPT_CHAIN, OPT_TWOPASS, OPT_NOMACVER, + OPT_DESCERT, OPT_EXPORT, OPT_NOITER, OPT_MACITER, OPT_NOMACITER, + OPT_NOMAC, OPT_LMK, OPT_NODES, OPT_MACALG, OPT_CERTPBE, OPT_KEYPBE, + OPT_RAND, OPT_INKEY, OPT_CERTFILE, OPT_NAME, OPT_CSP, OPT_CANAME, + OPT_IN, OPT_OUT, OPT_PASSIN, OPT_PASSOUT, OPT_PASSWORD, OPT_CAPATH, + OPT_CAFILE, OPT_NOCAPATH, OPT_NOCAFILE, OPT_ENGINE +} OPTION_CHOICE; + +OPTIONS pkcs12_options[] = { + {"help", OPT_HELP, '-', "Display this summary"}, + {"nokeys", OPT_NOKEYS, '-', "Don't output private keys"}, + {"keyex", OPT_KEYEX, '-', "Set MS key exchange type"}, + {"keysig", OPT_KEYSIG, '-', "Set MS key signature type"}, + {"nocerts", OPT_NOCERTS, '-', "Don't output certificates"}, + {"clcerts", OPT_CLCERTS, '-', "Only output client certificates"}, + {"cacerts", OPT_CACERTS, '-', "Only output CA certificates"}, + {"noout", OPT_NOOUT, '-', "Don't output anything, just verify"}, + {"info", OPT_INFO, '-', "Print info about PKCS#12 structure"}, + {"chain", OPT_CHAIN, '-', "Add certificate chain"}, + {"twopass", OPT_TWOPASS, '-', "Separate MAC, encryption passwords"}, + {"nomacver", OPT_NOMACVER, '-', "Don't verify MAC"}, +# ifndef OPENSSL_NO_RC2 + {"descert", OPT_DESCERT, '-', + "Encrypt output with 3DES (default RC2-40)"}, + {"certpbe", OPT_CERTPBE, 's', + "Certificate PBE algorithm (default RC2-40)"}, +# else + {"descert", OPT_DESCERT, '-', "Encrypt output with 3DES (the default)"}, + {"certpbe", OPT_CERTPBE, 's', "Certificate PBE algorithm (default 3DES)"}, +# endif + {"export", OPT_EXPORT, '-', "Output PKCS12 file"}, + {"noiter", OPT_NOITER, '-', "Don't use encryption iteration"}, + {"maciter", OPT_MACITER, '-', "Use MAC iteration"}, + {"nomaciter", OPT_NOMACITER, '-', "Don't use MAC iteration"}, + {"nomac", OPT_NOMAC, '-', "Don't generate MAC"}, + {"LMK", OPT_LMK, '-', + "Add local machine keyset attribute to private key"}, + {"nodes", OPT_NODES, '-', "Don't encrypt private keys"}, + {"macalg", OPT_MACALG, 's', + "Digest algorithm used in MAC (default SHA1)"}, + {"keypbe", OPT_KEYPBE, 's', "Private key PBE algorithm (default 3DES)"}, + {"rand", OPT_RAND, 's', + "Load the file(s) into the random number generator"}, + {"inkey", OPT_INKEY, '<', "Private key if not infile"}, + {"certfile", OPT_CERTFILE, '<', "Load certs from file"}, + {"name", OPT_NAME, 's', "Use name as friendly name"}, + {"CSP", OPT_CSP, 's', "Microsoft CSP name"}, + {"caname", OPT_CANAME, 's', + "Use name as CA friendly name (can be repeated)"}, + {"in", OPT_IN, '<', "Input filename"}, + {"out", OPT_OUT, '>', "Output filename"}, + {"passin", OPT_PASSIN, 's', "Input file pass phrase source"}, + {"passout", OPT_PASSOUT, 's', "Output file pass phrase source"}, + {"password", OPT_PASSWORD, 's', "Set import/export password source"}, + {"CApath", OPT_CAPATH, '/', "PEM-format directory of CA's"}, + {"CAfile", OPT_CAFILE, '<', "PEM-format file of CA's"}, + {"no-CAfile", OPT_NOCAFILE, '-', + "Do not load the default certificates file"}, + {"no-CApath", OPT_NOCAPATH, '-', + "Do not load certificates from the default certificates directory"}, + {"", OPT_CIPHER, '-', "Any supported cipher"}, +# ifndef OPENSSL_NO_ENGINE + {"engine", OPT_ENGINE, 's', "Use engine, possibly a hardware device"}, +# endif + {NULL} +}; + +int pkcs12_main(int argc, char **argv) +{ + char *infile = NULL, *outfile = NULL, *keyname = NULL, *certfile = NULL; + char *name = NULL, *csp_name = NULL; + char pass[2048] = "", macpass[2048] = ""; + int export_cert = 0, options = 0, chain = 0, twopass = 0, keytype = 0; + int iter = PKCS12_DEFAULT_ITER, maciter = PKCS12_DEFAULT_ITER; +# if !defined(OPENSSL_NO_SHA) && !defined(OPENSSL_NO_RC2) + int cert_pbe = NID_pbe_WithSHA1And40BitRC2_CBC; +# elif !defined(OPENSSL_NO_SHA) && !defined(OPENSSL_NO_DES) + int cert_pbe = NID_pbe_WithSHA1And3_Key_TripleDES_CBC; +# elif !defined(OPENSSL_NO_SM3) && !defined(OPENSSL_NO_SMS4) + int cert_pbe = NID_pbe_WithSM3AndSMS4_CBC; +# else +# error "no avaiable cipher" +# endif + int key_pbe = NID_pbe_WithSHA1And3_Key_TripleDES_CBC; + int ret = 1, macver = 1, add_lmk = 0, private = 0; + int noprompt = 0; + char *passinarg = NULL, *passoutarg = NULL, *passarg = NULL; + char *passin = NULL, *passout = NULL, *inrand = NULL, *macalg = NULL; + char *cpass = NULL, *mpass = NULL, *badpass = NULL; + const char *CApath = NULL, *CAfile = NULL, *prog; + int noCApath = 0, noCAfile = 0; + ENGINE *e = NULL; + BIO *in = NULL, *out = NULL; + PKCS12 *p12 = NULL; + STACK_OF(OPENSSL_STRING) *canames = NULL; +#if !defined(OPENSSL_NO_DES) + const EVP_CIPHER *enc = EVP_des_ede3_cbc(); +#elif !defined(OPENSSL_NO_SMS4) + const EVP_CIPHER *enc = EVP_sms4_cbc(); +#else +# error "no avaible cipher" +#endif + + OPTION_CHOICE o; + + prog = opt_init(argc, argv, pkcs12_options); + while ((o = opt_next()) != OPT_EOF) { + switch (o) { + case OPT_EOF: + case OPT_ERR: + opthelp: + BIO_printf(bio_err, "%s: Use -help for summary.\n", prog); + goto end; + case OPT_HELP: + opt_help(pkcs12_options); + ret = 0; + goto end; + case OPT_NOKEYS: + options |= NOKEYS; + break; + case OPT_KEYEX: + keytype = KEY_EX; + break; + case OPT_KEYSIG: + keytype = KEY_SIG; + break; + case OPT_NOCERTS: + options |= NOCERTS; + break; + case OPT_CLCERTS: + options |= CLCERTS; + break; + case OPT_CACERTS: + options |= CACERTS; + break; + case OPT_NOOUT: + options |= (NOKEYS | NOCERTS); + break; + case OPT_INFO: + options |= INFO; + break; + case OPT_CHAIN: + chain = 1; + break; + case OPT_TWOPASS: + twopass = 1; + break; + case OPT_NOMACVER: + macver = 0; + break; + case OPT_DESCERT: + cert_pbe = NID_pbe_WithSHA1And3_Key_TripleDES_CBC; + break; + case OPT_EXPORT: + export_cert = 1; + break; + case OPT_CIPHER: + if (!opt_cipher(opt_unknown(), &enc)) + goto opthelp; + break; + case OPT_NOITER: + iter = 1; + break; + case OPT_MACITER: + maciter = PKCS12_DEFAULT_ITER; + break; + case OPT_NOMACITER: + maciter = 1; + break; + case OPT_NOMAC: + maciter = -1; + break; + case OPT_MACALG: + macalg = opt_arg(); + break; + case OPT_NODES: + enc = NULL; + break; + case OPT_CERTPBE: + if (!set_pbe(&cert_pbe, opt_arg())) + goto opthelp; + break; + case OPT_KEYPBE: + if (!set_pbe(&key_pbe, opt_arg())) + goto opthelp; + break; + case OPT_RAND: + inrand = opt_arg(); + break; + case OPT_INKEY: + keyname = opt_arg(); + break; + case OPT_CERTFILE: + certfile = opt_arg(); + break; + case OPT_NAME: + name = opt_arg(); + break; + case OPT_LMK: + add_lmk = 1; + break; + case OPT_CSP: + csp_name = opt_arg(); + break; + case OPT_CANAME: + if (canames == NULL + && (canames = sk_OPENSSL_STRING_new_null()) == NULL) + goto end; + sk_OPENSSL_STRING_push(canames, opt_arg()); + break; + case OPT_IN: + infile = opt_arg(); + break; + case OPT_OUT: + outfile = opt_arg(); + break; + case OPT_PASSIN: + passinarg = opt_arg(); + break; + case OPT_PASSOUT: + passoutarg = opt_arg(); + break; + case OPT_PASSWORD: + passarg = opt_arg(); + break; + case OPT_CAPATH: + CApath = opt_arg(); + break; + case OPT_CAFILE: + CAfile = opt_arg(); + break; + case OPT_NOCAPATH: + noCApath = 1; + break; + case OPT_NOCAFILE: + noCAfile = 1; + break; + case OPT_ENGINE: + e = setup_engine(opt_arg(), 0); + break; + } + } + argc = opt_num_rest(); + if (argc != 0) + goto opthelp; + + private = 1; + + if (passarg) { + if (export_cert) + passoutarg = passarg; + else + passinarg = passarg; + } + + if (!app_passwd(passinarg, passoutarg, &passin, &passout)) { + BIO_printf(bio_err, "Error getting passwords\n"); + goto end; + } + + if (!cpass) { + if (export_cert) + cpass = passout; + else + cpass = passin; + } + + if (cpass) { + mpass = cpass; + noprompt = 1; + } else { + cpass = pass; + mpass = macpass; + } + + if (export_cert || inrand) { + app_RAND_load_file(NULL, (inrand != NULL)); + if (inrand != NULL) + BIO_printf(bio_err, "%ld semi-random bytes loaded\n", + app_RAND_load_files(inrand)); + } + + if (twopass) { + if (1) { +#ifndef OPENSSL_NO_UI + if (EVP_read_pw_string + (macpass, sizeof macpass, "Enter MAC Password:", export_cert)) { + BIO_printf(bio_err, "Can't read Password\n"); + goto end; + } + } else { +#endif + BIO_printf(bio_err, "Unsupported option -twopass\n"); + goto end; + } + } + + if (export_cert) { + EVP_PKEY *key = NULL; + X509 *ucert = NULL, *x = NULL; + STACK_OF(X509) *certs = NULL; + const EVP_MD *macmd = NULL; + unsigned char *catmp = NULL; + int i; + + if ((options & (NOCERTS | NOKEYS)) == (NOCERTS | NOKEYS)) { + BIO_printf(bio_err, "Nothing to do!\n"); + goto export_end; + } + + if (options & NOCERTS) + chain = 0; + + if (!(options & NOKEYS)) { + key = load_key(keyname ? keyname : infile, + FORMAT_PEM, 1, passin, e, "private key"); + if (!key) + goto export_end; + } + + /* Load in all certs in input file */ + if (!(options & NOCERTS)) { + if (!load_certs(infile, &certs, FORMAT_PEM, NULL, + "certificates")) + goto export_end; + + if (key) { + /* Look for matching private key */ + for (i = 0; i < sk_X509_num(certs); i++) { + x = sk_X509_value(certs, i); + if (X509_check_private_key(x, key)) { + ucert = x; + /* Zero keyid and alias */ + X509_keyid_set1(ucert, NULL, 0); + X509_alias_set1(ucert, NULL, 0); + /* Remove from list */ + (void)sk_X509_delete(certs, i); + break; + } + } + if (!ucert) { + BIO_printf(bio_err, + "No certificate matches private key\n"); + goto export_end; + } + } + + } + + /* Add any more certificates asked for */ + if (certfile) { + if (!load_certs(certfile, &certs, FORMAT_PEM, NULL, + "certificates from certfile")) + goto export_end; + } + + /* If chaining get chain from user cert */ + if (chain) { + int vret; + STACK_OF(X509) *chain2; + X509_STORE *store; + if ((store = setup_verify(CAfile, CApath, noCAfile, noCApath)) + == NULL) + goto export_end; + + vret = get_cert_chain(ucert, store, &chain2); + X509_STORE_free(store); + + if (vret == X509_V_OK) { + /* Exclude verified certificate */ + for (i = 1; i < sk_X509_num(chain2); i++) + sk_X509_push(certs, sk_X509_value(chain2, i)); + /* Free first certificate */ + X509_free(sk_X509_value(chain2, 0)); + sk_X509_free(chain2); + } else { + if (vret != X509_V_ERR_UNSPECIFIED) + BIO_printf(bio_err, "Error %s getting chain.\n", + X509_verify_cert_error_string(vret)); + else + ERR_print_errors(bio_err); + goto export_end; + } + } + + /* Add any CA names */ + + for (i = 0; i < sk_OPENSSL_STRING_num(canames); i++) { + catmp = (unsigned char *)sk_OPENSSL_STRING_value(canames, i); + X509_alias_set1(sk_X509_value(certs, i), catmp, -1); + } + + if (csp_name && key) + EVP_PKEY_add1_attr_by_NID(key, NID_ms_csp_name, + MBSTRING_ASC, (unsigned char *)csp_name, + -1); + + if (add_lmk && key) + EVP_PKEY_add1_attr_by_NID(key, NID_LocalKeySet, 0, NULL, -1); + + if (!noprompt) { + if (1) { +#ifndef OPENSSL_NO_UI + if (EVP_read_pw_string(pass, sizeof pass, "Enter Export Password:", + 1)) { + BIO_printf(bio_err, "Can't read Password\n"); + goto export_end; + } + } else { +#endif + BIO_printf(bio_err, "Password required\n"); + goto export_end; + } + } + + if (!twopass) + OPENSSL_strlcpy(macpass, pass, sizeof macpass); + + p12 = PKCS12_create(cpass, name, key, ucert, certs, + key_pbe, cert_pbe, iter, -1, keytype); + + if (!p12) { + ERR_print_errors(bio_err); + goto export_end; + } + + if (macalg) { + if (!opt_md(macalg, &macmd)) + goto opthelp; + } + + if (maciter != -1) + PKCS12_set_mac(p12, mpass, -1, NULL, 0, maciter, macmd); + + assert(private); + + out = bio_open_owner(outfile, FORMAT_PKCS12, private); + if (out == NULL) + goto end; + + i2d_PKCS12_bio(out, p12); + + ret = 0; + + export_end: + + EVP_PKEY_free(key); + sk_X509_pop_free(certs, X509_free); + X509_free(ucert); + + goto end; + + } + + in = bio_open_default(infile, 'r', FORMAT_PKCS12); + if (in == NULL) + goto end; + out = bio_open_owner(outfile, FORMAT_PEM, private); + if (out == NULL) + goto end; + + if ((p12 = d2i_PKCS12_bio(in, NULL)) == NULL) { + ERR_print_errors(bio_err); + goto end; + } + + if (!noprompt) { + if (1) { +#ifndef OPENSSL_NO_UI + if (EVP_read_pw_string(pass, sizeof pass, "Enter Import Password:", + 0)) { + BIO_printf(bio_err, "Can't read Password\n"); + goto end; + } + } else { +#endif + BIO_printf(bio_err, "Password required\n"); + goto end; + } + } + + if (!twopass) + OPENSSL_strlcpy(macpass, pass, sizeof macpass); + + if ((options & INFO) && PKCS12_mac_present(p12)) { + const ASN1_INTEGER *tmaciter; + const X509_ALGOR *macalgid; + const ASN1_OBJECT *macobj; + PKCS12_get0_mac(NULL, &macalgid, NULL, &tmaciter, p12); + X509_ALGOR_get0(&macobj, NULL, NULL, macalgid); + BIO_puts(bio_err, "MAC:"); + i2a_ASN1_OBJECT(bio_err, macobj); + BIO_printf(bio_err, " Iteration %ld\n", + tmaciter != NULL ? ASN1_INTEGER_get(tmaciter) : 1L); + } + if (macver) { + /* If we enter empty password try no password first */ + if (!mpass[0] && PKCS12_verify_mac(p12, NULL, 0)) { + /* If mac and crypto pass the same set it to NULL too */ + if (!twopass) + cpass = NULL; + } else if (!PKCS12_verify_mac(p12, mpass, -1)) { + /* + * May be UTF8 from previous version of OpenSSL: + * convert to a UTF8 form which will translate + * to the same Unicode password. + */ + unsigned char *utmp; + int utmplen; + utmp = OPENSSL_asc2uni(mpass, -1, NULL, &utmplen); + if (utmp == NULL) + goto end; + badpass = OPENSSL_uni2utf8(utmp, utmplen); + OPENSSL_free(utmp); + if (!PKCS12_verify_mac(p12, badpass, -1)) { + BIO_printf(bio_err, "Mac verify error: invalid password?\n"); + ERR_print_errors(bio_err); + goto end; + } else { + BIO_printf(bio_err, "Warning: using broken algorithm\n"); + if (!twopass) + cpass = badpass; + } + } + } + + assert(private); + if (!dump_certs_keys_p12(out, p12, cpass, -1, options, passout, enc)) { + BIO_printf(bio_err, "Error outputting keys and certificates\n"); + ERR_print_errors(bio_err); + goto end; + } + ret = 0; + end: + PKCS12_free(p12); + if (export_cert || inrand) + app_RAND_write_file(NULL); + release_engine(e); + BIO_free(in); + BIO_free_all(out); + sk_OPENSSL_STRING_free(canames); + OPENSSL_free(badpass); + OPENSSL_free(passin); + OPENSSL_free(passout); + return (ret); +} + +int dump_certs_keys_p12(BIO *out, const PKCS12 *p12, const char *pass, + int passlen, int options, char *pempass, + const EVP_CIPHER *enc) +{ + STACK_OF(PKCS7) *asafes = NULL; + STACK_OF(PKCS12_SAFEBAG) *bags; + int i, bagnid; + int ret = 0; + PKCS7 *p7; + + if ((asafes = PKCS12_unpack_authsafes(p12)) == NULL) + return 0; + for (i = 0; i < sk_PKCS7_num(asafes); i++) { + p7 = sk_PKCS7_value(asafes, i); + bagnid = OBJ_obj2nid(p7->type); + if (bagnid == NID_pkcs7_data) { + bags = PKCS12_unpack_p7data(p7); + if (options & INFO) + BIO_printf(bio_err, "PKCS7 Data\n"); + } else if (bagnid == NID_pkcs7_encrypted) { + if (options & INFO) { + BIO_printf(bio_err, "PKCS7 Encrypted data: "); + alg_print(p7->d.encrypted->enc_data->algorithm); + } + bags = PKCS12_unpack_p7encdata(p7, pass, passlen); + } else + continue; + if (!bags) + goto err; + if (!dump_certs_pkeys_bags(out, bags, pass, passlen, + options, pempass, enc)) { + sk_PKCS12_SAFEBAG_pop_free(bags, PKCS12_SAFEBAG_free); + goto err; + } + sk_PKCS12_SAFEBAG_pop_free(bags, PKCS12_SAFEBAG_free); + bags = NULL; + } + ret = 1; + + err: + sk_PKCS7_pop_free(asafes, PKCS7_free); + return ret; +} + +int dump_certs_pkeys_bags(BIO *out, const STACK_OF(PKCS12_SAFEBAG) *bags, + const char *pass, int passlen, int options, + char *pempass, const EVP_CIPHER *enc) +{ + int i; + for (i = 0; i < sk_PKCS12_SAFEBAG_num(bags); i++) { + if (!dump_certs_pkeys_bag(out, + sk_PKCS12_SAFEBAG_value(bags, i), + pass, passlen, options, pempass, enc)) + return 0; + } + return 1; +} + +int dump_certs_pkeys_bag(BIO *out, const PKCS12_SAFEBAG *bag, + const char *pass, int passlen, int options, + char *pempass, const EVP_CIPHER *enc) +{ + EVP_PKEY *pkey; + PKCS8_PRIV_KEY_INFO *p8; + const PKCS8_PRIV_KEY_INFO *p8c; + X509 *x509; + const STACK_OF(X509_ATTRIBUTE) *attrs; + int ret = 0; + + attrs = PKCS12_SAFEBAG_get0_attrs(bag); + + switch (PKCS12_SAFEBAG_get_nid(bag)) { + case NID_keyBag: + if (options & INFO) + BIO_printf(bio_err, "Key bag\n"); + if (options & NOKEYS) + return 1; + print_attribs(out, attrs, "Bag Attributes"); + p8c = PKCS12_SAFEBAG_get0_p8inf(bag); + if ((pkey = EVP_PKCS82PKEY(p8c)) == NULL) + return 0; + print_attribs(out, PKCS8_pkey_get0_attrs(p8c), "Key Attributes"); + ret = PEM_write_bio_PrivateKey(out, pkey, enc, NULL, 0, NULL, pempass); + EVP_PKEY_free(pkey); + break; + + case NID_pkcs8ShroudedKeyBag: + if (options & INFO) { + const X509_SIG *tp8; + const X509_ALGOR *tp8alg; + + BIO_printf(bio_err, "Shrouded Keybag: "); + tp8 = PKCS12_SAFEBAG_get0_pkcs8(bag); + X509_SIG_get0(tp8, &tp8alg, NULL); + alg_print(tp8alg); + } + if (options & NOKEYS) + return 1; + print_attribs(out, attrs, "Bag Attributes"); + if ((p8 = PKCS12_decrypt_skey(bag, pass, passlen)) == NULL) + return 0; + if ((pkey = EVP_PKCS82PKEY(p8)) == NULL) { + PKCS8_PRIV_KEY_INFO_free(p8); + return 0; + } + print_attribs(out, PKCS8_pkey_get0_attrs(p8), "Key Attributes"); + PKCS8_PRIV_KEY_INFO_free(p8); + ret = PEM_write_bio_PrivateKey(out, pkey, enc, NULL, 0, NULL, pempass); + EVP_PKEY_free(pkey); + break; + + case NID_certBag: + if (options & INFO) + BIO_printf(bio_err, "Certificate bag\n"); + if (options & NOCERTS) + return 1; + if (PKCS12_SAFEBAG_get0_attr(bag, NID_localKeyID)) { + if (options & CACERTS) + return 1; + } else if (options & CLCERTS) + return 1; + print_attribs(out, attrs, "Bag Attributes"); + if (PKCS12_SAFEBAG_get_bag_nid(bag) != NID_x509Certificate) + return 1; + if ((x509 = PKCS12_SAFEBAG_get1_cert(bag)) == NULL) + return 0; + dump_cert_text(out, x509); + ret = PEM_write_bio_X509(out, x509); + X509_free(x509); + break; + + case NID_safeContentsBag: + if (options & INFO) + BIO_printf(bio_err, "Safe Contents bag\n"); + print_attribs(out, attrs, "Bag Attributes"); + return dump_certs_pkeys_bags(out, PKCS12_SAFEBAG_get0_safes(bag), + pass, passlen, options, pempass, enc); + + default: + BIO_printf(bio_err, "Warning unsupported bag type: "); + i2a_ASN1_OBJECT(bio_err, PKCS12_SAFEBAG_get0_type(bag)); + BIO_printf(bio_err, "\n"); + return 1; + } + return ret; +} + +/* Given a single certificate return a verified chain or NULL if error */ + +static int get_cert_chain(X509 *cert, X509_STORE *store, + STACK_OF(X509) **chain) +{ + X509_STORE_CTX *store_ctx = NULL; + STACK_OF(X509) *chn = NULL; + int i = 0; + + store_ctx = X509_STORE_CTX_new(); + if (store_ctx == NULL) { + i = X509_V_ERR_UNSPECIFIED; + goto end; + } + if (!X509_STORE_CTX_init(store_ctx, store, cert, NULL)) { + i = X509_V_ERR_UNSPECIFIED; + goto end; + } + + + if (X509_verify_cert(store_ctx) > 0) + chn = X509_STORE_CTX_get1_chain(store_ctx); + else if ((i = X509_STORE_CTX_get_error(store_ctx)) == 0) + i = X509_V_ERR_UNSPECIFIED; + +end: + X509_STORE_CTX_free(store_ctx); + *chain = chn; + return i; +} + +static int alg_print(const X509_ALGOR *alg) +{ + int pbenid, aparamtype; + const ASN1_OBJECT *aoid; + const void *aparam; + PBEPARAM *pbe = NULL; + + X509_ALGOR_get0(&aoid, &aparamtype, &aparam, alg); + + pbenid = OBJ_obj2nid(aoid); + + BIO_printf(bio_err, "%s", OBJ_nid2ln(pbenid)); + + /* + * If PBE algorithm is PBES2 decode algorithm parameters + * for additional details. + */ + if (pbenid == NID_pbes2) { + PBE2PARAM *pbe2 = NULL; + int encnid; + if (aparamtype == V_ASN1_SEQUENCE) + pbe2 = ASN1_item_unpack(aparam, ASN1_ITEM_rptr(PBE2PARAM)); + if (pbe2 == NULL) { + BIO_puts(bio_err, ""); + goto done; + } + X509_ALGOR_get0(&aoid, &aparamtype, &aparam, pbe2->keyfunc); + pbenid = OBJ_obj2nid(aoid); + X509_ALGOR_get0(&aoid, NULL, NULL, pbe2->encryption); + encnid = OBJ_obj2nid(aoid); + BIO_printf(bio_err, ", %s, %s", OBJ_nid2ln(pbenid), + OBJ_nid2sn(encnid)); + /* If KDF is PBKDF2 decode parameters */ + if (pbenid == NID_id_pbkdf2) { + PBKDF2PARAM *kdf = NULL; + int prfnid; + if (aparamtype == V_ASN1_SEQUENCE) + kdf = ASN1_item_unpack(aparam, ASN1_ITEM_rptr(PBKDF2PARAM)); + if (kdf == NULL) { + BIO_puts(bio_err, ""); + goto done; + } + + if (kdf->prf == NULL) { + prfnid = NID_hmacWithSHA1; + } else { + X509_ALGOR_get0(&aoid, NULL, NULL, kdf->prf); + prfnid = OBJ_obj2nid(aoid); + } + BIO_printf(bio_err, ", Iteration %ld, PRF %s", + ASN1_INTEGER_get(kdf->iter), OBJ_nid2sn(prfnid)); + PBKDF2PARAM_free(kdf); + } + PBE2PARAM_free(pbe2); + } else { + if (aparamtype == V_ASN1_SEQUENCE) + pbe = ASN1_item_unpack(aparam, ASN1_ITEM_rptr(PBEPARAM)); + if (pbe == NULL) { + BIO_puts(bio_err, ""); + goto done; + } + BIO_printf(bio_err, ", Iteration %ld", ASN1_INTEGER_get(pbe->iter)); + PBEPARAM_free(pbe); + } + done: + BIO_puts(bio_err, "\n"); + return 1; +} + +/* Load all certificates from a given file */ + +int cert_load(BIO *in, STACK_OF(X509) *sk) +{ + int ret; + X509 *cert; + ret = 0; + while ((cert = PEM_read_bio_X509(in, NULL, NULL, NULL))) { + ret = 1; + sk_X509_push(sk, cert); + } + if (ret) + ERR_clear_error(); + return ret; +} + +/* Generalised attribute print: handle PKCS#8 and bag attributes */ + +int print_attribs(BIO *out, const STACK_OF(X509_ATTRIBUTE) *attrlst, + const char *name) +{ + X509_ATTRIBUTE *attr; + ASN1_TYPE *av; + char *value; + int i, attr_nid; + if (!attrlst) { + BIO_printf(out, "%s: \n", name); + return 1; + } + if (!sk_X509_ATTRIBUTE_num(attrlst)) { + BIO_printf(out, "%s: \n", name); + return 1; + } + BIO_printf(out, "%s\n", name); + for (i = 0; i < sk_X509_ATTRIBUTE_num(attrlst); i++) { + ASN1_OBJECT *attr_obj; + attr = sk_X509_ATTRIBUTE_value(attrlst, i); + attr_obj = X509_ATTRIBUTE_get0_object(attr); + attr_nid = OBJ_obj2nid(attr_obj); + BIO_printf(out, " "); + if (attr_nid == NID_undef) { + i2a_ASN1_OBJECT(out, attr_obj); + BIO_printf(out, ": "); + } else + BIO_printf(out, "%s: ", OBJ_nid2ln(attr_nid)); + + if (X509_ATTRIBUTE_count(attr)) { + av = X509_ATTRIBUTE_get0_type(attr, 0); + switch (av->type) { + case V_ASN1_BMPSTRING: + value = OPENSSL_uni2asc(av->value.bmpstring->data, + av->value.bmpstring->length); + BIO_printf(out, "%s\n", value); + OPENSSL_free(value); + break; + + case V_ASN1_OCTET_STRING: + hex_prin(out, av->value.octet_string->data, + av->value.octet_string->length); + BIO_printf(out, "\n"); + break; + + case V_ASN1_BIT_STRING: + hex_prin(out, av->value.bit_string->data, + av->value.bit_string->length); + BIO_printf(out, "\n"); + break; + + default: + BIO_printf(out, "\n", av->type); + break; + } + } else + BIO_printf(out, "\n"); + } + return 1; +} + +void hex_prin(BIO *out, unsigned char *buf, int len) +{ + int i; + for (i = 0; i < len; i++) + BIO_printf(out, "%02X ", buf[i]); +} + +static int set_pbe(int *ppbe, const char *str) +{ + if (!str) + return 0; + if (strcmp(str, "NONE") == 0) { + *ppbe = -1; + return 1; + } + *ppbe = OBJ_txt2nid(str); + if (*ppbe == NID_undef) { + BIO_printf(bio_err, "Unknown PBE algorithm %s\n", str); + return 0; + } + return 1; +} + +//#endif diff --git a/apps/pkcs7.c b/apps/pkcs7.c new file mode 100644 index 000000000..209e30d63 --- /dev/null +++ b/apps/pkcs7.c @@ -0,0 +1,197 @@ +/* + * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#include +#include +#include +#include "apps.h" +#include +#include +#include +#include +#include +#include + +typedef enum OPTION_choice { + OPT_ERR = -1, OPT_EOF = 0, OPT_HELP, + OPT_INFORM, OPT_OUTFORM, OPT_IN, OPT_OUT, OPT_NOOUT, + OPT_TEXT, OPT_PRINT, OPT_PRINT_CERTS, OPT_ENGINE +} OPTION_CHOICE; + +OPTIONS pkcs7_options[] = { + {"help", OPT_HELP, '-', "Display this summary"}, + {"inform", OPT_INFORM, 'F', "Input format - DER or PEM"}, + {"in", OPT_IN, '<', "Input file"}, + {"outform", OPT_OUTFORM, 'F', "Output format - DER or PEM"}, + {"out", OPT_OUT, '>', "Output file"}, + {"noout", OPT_NOOUT, '-', "Don't output encoded data"}, + {"text", OPT_TEXT, '-', "Print full details of certificates"}, + {"print", OPT_PRINT, '-', "Print out all fields of the PKCS7 structure"}, + {"print_certs", OPT_PRINT_CERTS, '-', + "Print_certs print any certs or crl in the input"}, +#ifndef OPENSSL_NO_ENGINE + {"engine", OPT_ENGINE, 's', "Use engine, possibly a hardware device"}, +#endif + {NULL} +}; + +int pkcs7_main(int argc, char **argv) +{ + ENGINE *e = NULL; + PKCS7 *p7 = NULL; + BIO *in = NULL, *out = NULL; + int informat = FORMAT_PEM, outformat = FORMAT_PEM; + char *infile = NULL, *outfile = NULL, *prog; + int i, print_certs = 0, text = 0, noout = 0, p7_print = 0, ret = 1; + OPTION_CHOICE o; + + prog = opt_init(argc, argv, pkcs7_options); + while ((o = opt_next()) != OPT_EOF) { + switch (o) { + case OPT_EOF: + case OPT_ERR: + opthelp: + BIO_printf(bio_err, "%s: Use -help for summary.\n", prog); + goto end; + case OPT_HELP: + opt_help(pkcs7_options); + ret = 0; + goto end; + case OPT_INFORM: + if (!opt_format(opt_arg(), OPT_FMT_PEMDER, &informat)) + goto opthelp; + break; + case OPT_OUTFORM: + if (!opt_format(opt_arg(), OPT_FMT_PEMDER, &outformat)) + goto opthelp; + break; + case OPT_IN: + infile = opt_arg(); + break; + case OPT_OUT: + outfile = opt_arg(); + break; + case OPT_NOOUT: + noout = 1; + break; + case OPT_TEXT: + text = 1; + break; + case OPT_PRINT: + p7_print = 1; + break; + case OPT_PRINT_CERTS: + print_certs = 1; + break; + case OPT_ENGINE: + e = setup_engine(opt_arg(), 0); + break; + } + } + argc = opt_num_rest(); + if (argc != 0) + goto opthelp; + + in = bio_open_default(infile, 'r', informat); + if (in == NULL) + goto end; + + if (informat == FORMAT_ASN1) + p7 = d2i_PKCS7_bio(in, NULL); + else + p7 = PEM_read_bio_PKCS7(in, NULL, NULL, NULL); + if (p7 == NULL) { + BIO_printf(bio_err, "unable to load PKCS7 object\n"); + ERR_print_errors(bio_err); + goto end; + } + + out = bio_open_default(outfile, 'w', outformat); + if (out == NULL) + goto end; + + if (p7_print) + PKCS7_print_ctx(out, p7, 0, NULL); + + if (print_certs) { + STACK_OF(X509) *certs = NULL; + STACK_OF(X509_CRL) *crls = NULL; + + i = OBJ_obj2nid(p7->type); + switch (i) { + case NID_pkcs7_signed: + if (p7->d.sign != NULL) { + certs = p7->d.sign->cert; + crls = p7->d.sign->crl; + } + break; + case NID_pkcs7_signedAndEnveloped: + if (p7->d.signed_and_enveloped != NULL) { + certs = p7->d.signed_and_enveloped->cert; + crls = p7->d.signed_and_enveloped->crl; + } + break; + default: + break; + } + + if (certs != NULL) { + X509 *x; + + for (i = 0; i < sk_X509_num(certs); i++) { + x = sk_X509_value(certs, i); + if (text) + X509_print(out, x); + else + dump_cert_text(out, x); + + if (!noout) + PEM_write_bio_X509(out, x); + BIO_puts(out, "\n"); + } + } + if (crls != NULL) { + X509_CRL *crl; + + for (i = 0; i < sk_X509_CRL_num(crls); i++) { + crl = sk_X509_CRL_value(crls, i); + + X509_CRL_print(out, crl); + + if (!noout) + PEM_write_bio_X509_CRL(out, crl); + BIO_puts(out, "\n"); + } + } + + ret = 0; + goto end; + } + + if (!noout) { + if (outformat == FORMAT_ASN1) + i = i2d_PKCS7_bio(out, p7); + else + i = PEM_write_bio_PKCS7(out, p7); + + if (!i) { + BIO_printf(bio_err, "unable to write pkcs7 object\n"); + ERR_print_errors(bio_err); + goto end; + } + } + ret = 0; + end: + PKCS7_free(p7); + release_engine(e); + BIO_free(in); + BIO_free_all(out); + return (ret); +} diff --git a/apps/pkcs8.c b/apps/pkcs8.c new file mode 100644 index 000000000..16fdc42f4 --- /dev/null +++ b/apps/pkcs8.c @@ -0,0 +1,360 @@ +/* + * Copyright 1999-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#include +#include +#include "apps.h" +#include +#include +#include +#include + +/* pkcs8 command use aes-256-cbc as default cipher */ +#ifndef OPENSSL_NO_AES +# define PKCS8_DEFAULT_EVP_CIPHER EVP_aes_256_cbc() +#else +# define PKCS8_DEFAULT_EVP_CIPHER EVP_sms4_cbc() +#endif + +typedef enum OPTION_choice { + OPT_ERR = -1, OPT_EOF = 0, OPT_HELP, + OPT_INFORM, OPT_OUTFORM, OPT_ENGINE, OPT_IN, OPT_OUT, + OPT_TOPK8, OPT_NOITER, OPT_NOCRYPT, +#ifndef OPENSSL_NO_SCRYPT + OPT_SCRYPT, OPT_SCRYPT_N, OPT_SCRYPT_R, OPT_SCRYPT_P, +#endif + OPT_V2, OPT_V1, OPT_V2PRF, OPT_ITER, OPT_PASSIN, OPT_PASSOUT, + OPT_TRADITIONAL +} OPTION_CHOICE; + +OPTIONS pkcs8_options[] = { + {"help", OPT_HELP, '-', "Display this summary"}, + {"inform", OPT_INFORM, 'F', "Input format (DER or PEM)"}, + {"outform", OPT_OUTFORM, 'F', "Output format (DER or PEM)"}, + {"in", OPT_IN, '<', "Input file"}, + {"out", OPT_OUT, '>', "Output file"}, + {"topk8", OPT_TOPK8, '-', "Output PKCS8 file"}, + {"noiter", OPT_NOITER, '-', "Use 1 as iteration count"}, + {"nocrypt", OPT_NOCRYPT, '-', "Use or expect unencrypted private key"}, + {"v2", OPT_V2, 's', "Use PKCS#5 v2.0 and cipher"}, + {"v1", OPT_V1, 's', "Use PKCS#5 v1.5 and cipher"}, + {"v2prf", OPT_V2PRF, 's', "Set the PRF algorithm to use with PKCS#5 v2.0"}, + {"iter", OPT_ITER, 'p', "Specify the iteration count"}, + {"passin", OPT_PASSIN, 's', "Input file pass phrase source"}, + {"passout", OPT_PASSOUT, 's', "Output file pass phrase source"}, + {"traditional", OPT_TRADITIONAL, '-', "use traditional format private key"}, +#ifndef OPENSSL_NO_ENGINE + {"engine", OPT_ENGINE, 's', "Use engine, possibly a hardware device"}, +#endif +#ifndef OPENSSL_NO_SCRYPT + {"scrypt", OPT_SCRYPT, '-', "Use scrypt algorithm"}, + {"scrypt_N", OPT_SCRYPT_N, 's', "Set scrypt N parameter"}, + {"scrypt_r", OPT_SCRYPT_R, 's', "Set scrypt r parameter"}, + {"scrypt_p", OPT_SCRYPT_P, 's', "Set scrypt p parameter"}, +#endif + {NULL} +}; + +int pkcs8_main(int argc, char **argv) +{ + BIO *in = NULL, *out = NULL; + ENGINE *e = NULL; + EVP_PKEY *pkey = NULL; + PKCS8_PRIV_KEY_INFO *p8inf = NULL; + X509_SIG *p8 = NULL; + const EVP_CIPHER *cipher = NULL; + char *infile = NULL, *outfile = NULL; + char *passinarg = NULL, *passoutarg = NULL, *prog; +#ifndef OPENSSL_NO_UI + char pass[50]; +#endif + char *passin = NULL, *passout = NULL, *p8pass = NULL; + OPTION_CHOICE o; + int nocrypt = 0, ret = 1, iter = PKCS12_DEFAULT_ITER; + int informat = FORMAT_PEM, outformat = FORMAT_PEM, topk8 = 0, pbe_nid = -1; + int private = 0, traditional = 0; +#ifndef OPENSSL_NO_SCRYPT + long scrypt_N = 0, scrypt_r = 0, scrypt_p = 0; +#endif + + prog = opt_init(argc, argv, pkcs8_options); + while ((o = opt_next()) != OPT_EOF) { + switch (o) { + case OPT_EOF: + case OPT_ERR: + opthelp: + BIO_printf(bio_err, "%s: Use -help for summary.\n", prog); + goto end; + case OPT_HELP: + opt_help(pkcs8_options); + ret = 0; + goto end; + case OPT_INFORM: + if (!opt_format(opt_arg(), OPT_FMT_PEMDER, &informat)) + goto opthelp; + break; + case OPT_IN: + infile = opt_arg(); + break; + case OPT_OUTFORM: + if (!opt_format(opt_arg(), OPT_FMT_PEMDER, &outformat)) + goto opthelp; + break; + case OPT_OUT: + outfile = opt_arg(); + break; + case OPT_TOPK8: + topk8 = 1; + break; + case OPT_NOITER: + iter = 1; + break; + case OPT_NOCRYPT: + nocrypt = 1; + break; + case OPT_TRADITIONAL: + traditional = 1; + break; + case OPT_V2: + if (!opt_cipher(opt_arg(), &cipher)) + goto opthelp; + break; + case OPT_V1: + pbe_nid = OBJ_txt2nid(opt_arg()); + if (pbe_nid == NID_undef) { + BIO_printf(bio_err, + "%s: Unknown PBE algorithm %s\n", prog, opt_arg()); + goto opthelp; + } + break; + case OPT_V2PRF: + pbe_nid = OBJ_txt2nid(opt_arg()); + if (!EVP_PBE_find(EVP_PBE_TYPE_PRF, pbe_nid, NULL, NULL, 0)) { + BIO_printf(bio_err, + "%s: Unknown PRF algorithm %s\n", prog, opt_arg()); + goto opthelp; + } + if (cipher == NULL) + cipher = PKCS8_DEFAULT_EVP_CIPHER; + break; + case OPT_ITER: + if (!opt_int(opt_arg(), &iter)) + goto opthelp; + break; + case OPT_PASSIN: + passinarg = opt_arg(); + break; + case OPT_PASSOUT: + passoutarg = opt_arg(); + break; + case OPT_ENGINE: + e = setup_engine(opt_arg(), 0); + break; +#ifndef OPENSSL_NO_SCRYPT + case OPT_SCRYPT: + scrypt_N = 16384; + scrypt_r = 8; + scrypt_p = 1; + if (cipher == NULL) + cipher = PKCS8_DEFAULT_EVP_CIPHER; + break; + case OPT_SCRYPT_N: + if (!opt_long(opt_arg(), &scrypt_N) || scrypt_N <= 0) + goto opthelp; + break; + case OPT_SCRYPT_R: + if (!opt_long(opt_arg(), &scrypt_r) || scrypt_r <= 0) + goto opthelp; + break; + case OPT_SCRYPT_P: + if (!opt_long(opt_arg(), &scrypt_p) || scrypt_p <= 0) + goto opthelp; + break; +#endif + } + } + argc = opt_num_rest(); + if (argc != 0) + goto opthelp; + + private = 1; + + if (!app_passwd(passinarg, passoutarg, &passin, &passout)) { + BIO_printf(bio_err, "Error getting passwords\n"); + goto end; + } + + if ((pbe_nid == -1) && cipher == NULL) + cipher = PKCS8_DEFAULT_EVP_CIPHER; + + in = bio_open_default(infile, 'r', informat); + if (in == NULL) + goto end; + out = bio_open_owner(outfile, outformat, private); + if (out == NULL) + goto end; + + if (topk8) { + pkey = load_key(infile, informat, 1, passin, e, "key"); + if (!pkey) + goto end; + if ((p8inf = EVP_PKEY2PKCS8(pkey)) == NULL) { + BIO_printf(bio_err, "Error converting key\n"); + ERR_print_errors(bio_err); + goto end; + } + if (nocrypt) { + assert(private); + if (outformat == FORMAT_PEM) + PEM_write_bio_PKCS8_PRIV_KEY_INFO(out, p8inf); + else if (outformat == FORMAT_ASN1) + i2d_PKCS8_PRIV_KEY_INFO_bio(out, p8inf); + else { + BIO_printf(bio_err, "Bad format specified for key\n"); + goto end; + } + } else { + X509_ALGOR *pbe; + if (cipher) { +#ifndef OPENSSL_NO_SCRYPT + if (scrypt_N && scrypt_r && scrypt_p) + pbe = PKCS5_pbe2_set_scrypt(cipher, NULL, 0, NULL, + scrypt_N, scrypt_r, scrypt_p); + else +#endif + pbe = PKCS5_pbe2_set_iv(cipher, iter, NULL, 0, NULL, + pbe_nid); + } else { + pbe = PKCS5_pbe_set(pbe_nid, iter, NULL, 0); + } + if (pbe == NULL) { + BIO_printf(bio_err, "Error setting PBE algorithm\n"); + ERR_print_errors(bio_err); + goto end; + } + if (passout) + p8pass = passout; + else if (1) { +#ifndef OPENSSL_NO_UI + p8pass = pass; + if (EVP_read_pw_string + (pass, sizeof pass, "Enter Encryption Password:", 1)) { + X509_ALGOR_free(pbe); + goto end; + } + } else { +#endif + BIO_printf(bio_err, "Password required\n"); + goto end; + } + app_RAND_load_file(NULL, 0); + p8 = PKCS8_set0_pbe(p8pass, strlen(p8pass), p8inf, pbe); + if (p8 == NULL) { + X509_ALGOR_free(pbe); + BIO_printf(bio_err, "Error encrypting key\n"); + ERR_print_errors(bio_err); + goto end; + } + app_RAND_write_file(NULL); + assert(private); + if (outformat == FORMAT_PEM) + PEM_write_bio_PKCS8(out, p8); + else if (outformat == FORMAT_ASN1) + i2d_PKCS8_bio(out, p8); + else { + BIO_printf(bio_err, "Bad format specified for key\n"); + goto end; + } + } + + ret = 0; + goto end; + } + + if (nocrypt) { + if (informat == FORMAT_PEM) + p8inf = PEM_read_bio_PKCS8_PRIV_KEY_INFO(in, NULL, NULL, NULL); + else if (informat == FORMAT_ASN1) + p8inf = d2i_PKCS8_PRIV_KEY_INFO_bio(in, NULL); + else { + BIO_printf(bio_err, "Bad format specified for key\n"); + goto end; + } + } else { + if (informat == FORMAT_PEM) + p8 = PEM_read_bio_PKCS8(in, NULL, NULL, NULL); + else if (informat == FORMAT_ASN1) + p8 = d2i_PKCS8_bio(in, NULL); + else { + BIO_printf(bio_err, "Bad format specified for key\n"); + goto end; + } + + if (!p8) { + BIO_printf(bio_err, "Error reading key\n"); + ERR_print_errors(bio_err); + goto end; + } + if (passin) + p8pass = passin; + else if (1) { +#ifndef OPENSSL_NO_UI + p8pass = pass; + if (EVP_read_pw_string(pass, sizeof pass, "Enter Password:", 0)) { + BIO_printf(bio_err, "Can't read Password\n"); + goto end; + } + } else { +#endif + BIO_printf(bio_err, "Password required\n"); + goto end; + } + p8inf = PKCS8_decrypt(p8, p8pass, strlen(p8pass)); + } + + if (!p8inf) { + BIO_printf(bio_err, "Error decrypting key\n"); + ERR_print_errors(bio_err); + goto end; + } + + if ((pkey = EVP_PKCS82PKEY(p8inf)) == NULL) { + BIO_printf(bio_err, "Error converting key\n"); + ERR_print_errors(bio_err); + goto end; + } + + assert(private); + if (outformat == FORMAT_PEM) { + if (traditional) + PEM_write_bio_PrivateKey_traditional(out, pkey, NULL, NULL, 0, + NULL, passout); + else + PEM_write_bio_PrivateKey(out, pkey, NULL, NULL, 0, NULL, passout); + } else if (outformat == FORMAT_ASN1) { + i2d_PrivateKey_bio(out, pkey); + } else { + BIO_printf(bio_err, "Bad format specified for key\n"); + goto end; + } + ret = 0; + + end: + X509_SIG_free(p8); + PKCS8_PRIV_KEY_INFO_free(p8inf); + EVP_PKEY_free(pkey); + release_engine(e); + BIO_free_all(out); + BIO_free(in); + OPENSSL_free(passin); + OPENSSL_free(passout); + + return ret; +} diff --git a/apps/pkey.c b/apps/pkey.c new file mode 100644 index 000000000..694ef9036 --- /dev/null +++ b/apps/pkey.c @@ -0,0 +1,206 @@ +/* + * Copyright 2006-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#include +#include "apps.h" +#include +#include +#include + +typedef enum OPTION_choice { + OPT_ERR = -1, OPT_EOF = 0, OPT_HELP, + OPT_INFORM, OPT_OUTFORM, OPT_PASSIN, OPT_PASSOUT, OPT_ENGINE, + OPT_IN, OPT_OUT, OPT_PUBIN, OPT_PUBOUT, OPT_TEXT_PUB, + OPT_TEXT, OPT_NOOUT, OPT_MD, OPT_TRADITIONAL, + OPT_CONFIG +} OPTION_CHOICE; + +OPTIONS pkey_options[] = { + {"help", OPT_HELP, '-', "Display this summary"}, + {"inform", OPT_INFORM, 'f', "Input format (DER or PEM)"}, + {"outform", OPT_OUTFORM, 'F', "Output format (DER or PEM)"}, + {"passin", OPT_PASSIN, 's', "Input file pass phrase source"}, + {"passout", OPT_PASSOUT, 's', "Output file pass phrase source"}, + {"in", OPT_IN, 's', "Input key"}, + {"out", OPT_OUT, '>', "Output file"}, + {"pubin", OPT_PUBIN, '-', + "Read public key from input (default is private key)"}, + {"pubout", OPT_PUBOUT, '-', "Output public key, not private"}, + {"text_pub", OPT_TEXT_PUB, '-', "Only output public key components"}, + {"text", OPT_TEXT, '-', "Output in plaintext as well"}, + {"noout", OPT_NOOUT, '-', "Don't output the key"}, + {"", OPT_MD, '-', "Any supported cipher"}, + {"traditional", OPT_TRADITIONAL, '-', + "Use traditional format for private keys"}, +#ifndef OPENSSL_NO_ENGINE + {"engine", OPT_ENGINE, 's', "Use engine, possibly a hardware device"}, + {"config", OPT_CONFIG, 's', "A config file"}, +#endif + {NULL} +}; + +int pkey_main(int argc, char **argv) +{ + BIO *in = NULL, *out = NULL; + ENGINE *e = NULL; + EVP_PKEY *pkey = NULL; + const EVP_CIPHER *cipher = NULL; + char *infile = NULL, *outfile = NULL, *passin = NULL, *passout = NULL; + char *passinarg = NULL, *passoutarg = NULL, *prog; + OPTION_CHOICE o; + int informat = FORMAT_PEM, outformat = FORMAT_PEM; + int pubin = 0, pubout = 0, pubtext = 0, text = 0, noout = 0, ret = 1; + int private = 0, traditional = 0; + CONF *conf = NULL; + char *configfile = default_config_file; + + prog = opt_init(argc, argv, pkey_options); + while ((o = opt_next()) != OPT_EOF) { + switch (o) { + case OPT_EOF: + case OPT_ERR: + opthelp: + BIO_printf(bio_err, "%s: Use -help for summary.\n", prog); + goto end; + case OPT_HELP: + opt_help(pkey_options); + ret = 0; + goto end; + case OPT_INFORM: + if (!opt_format(opt_arg(), OPT_FMT_ANY, &informat)) + goto opthelp; + break; + case OPT_OUTFORM: + if (!opt_format(opt_arg(), OPT_FMT_PEMDER, &outformat)) + goto opthelp; + break; + case OPT_PASSIN: + passinarg = opt_arg(); + break; + case OPT_PASSOUT: + passoutarg = opt_arg(); + break; + case OPT_ENGINE: + e = setup_engine(opt_arg(), 0); + break; + case OPT_IN: + infile = opt_arg(); + break; + case OPT_OUT: + outfile = opt_arg(); + break; + case OPT_PUBIN: + pubin = pubout = pubtext = 1; + break; + case OPT_PUBOUT: + pubout = 1; + break; + case OPT_TEXT_PUB: + pubtext = text = 1; + break; + case OPT_TEXT: + text = 1; + break; + case OPT_NOOUT: + noout = 1; + break; + case OPT_TRADITIONAL: + traditional = 1; + break; + case OPT_MD: + if (!opt_cipher(opt_unknown(), &cipher)) + goto opthelp; + case OPT_CONFIG: + configfile = opt_arg(); + break; + } + } + argc = opt_num_rest(); + if (argc != 0) + goto opthelp; + + if (e) + BIO_printf(bio_err, "Using configuration from %s\n", configfile); + + if ((conf = app_load_config(configfile)) == NULL) + goto end; + if (configfile != default_config_file && !app_load_modules(conf)) + goto end; + + private = !noout && !pubout ? 1 : 0; + if (text && !pubtext) + private = 1; + + if (!app_passwd(passinarg, passoutarg, &passin, &passout)) { + BIO_printf(bio_err, "Error getting passwords\n"); + goto end; + } + + out = bio_open_owner(outfile, outformat, private); + if (out == NULL) + goto end; + + if (pubin) + pkey = load_pubkey(infile, informat, 1, passin, e, "Public Key"); + else + pkey = load_key(infile, informat, 1, passin, e, "key"); + if (!pkey) + goto end; + + if (!noout) { + if (outformat == FORMAT_PEM) { + if (pubout) + PEM_write_bio_PUBKEY(out, pkey); + else { + assert(private); + if (traditional) + PEM_write_bio_PrivateKey_traditional(out, pkey, cipher, + NULL, 0, NULL, + passout); + else + PEM_write_bio_PrivateKey(out, pkey, cipher, + NULL, 0, NULL, passout); + } + } else if (outformat == FORMAT_ASN1) { + if (pubout) + i2d_PUBKEY_bio(out, pkey); + else { + assert(private); + i2d_PrivateKey_bio(out, pkey); + } + } else { + BIO_printf(bio_err, "Bad format specified for key\n"); + goto end; + } + + } + + if (text) { + if (pubtext) + EVP_PKEY_print_public(out, pkey, 0, NULL); + else { + assert(private); + EVP_PKEY_print_private(out, pkey, 0, NULL); + } + } + + ret = 0; + + end: + EVP_PKEY_free(pkey); + release_engine(e); + BIO_free_all(out); + BIO_free(in); + OPENSSL_free(passin); + OPENSSL_free(passout); + NCONF_free(conf); + + return ret; +} diff --git a/apps/pkeyparam.c b/apps/pkeyparam.c new file mode 100644 index 000000000..0a1b2d128 --- /dev/null +++ b/apps/pkeyparam.c @@ -0,0 +1,104 @@ +/* + * Copyright 2006-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#include +#include "apps.h" +#include +#include +#include + +typedef enum OPTION_choice { + OPT_ERR = -1, OPT_EOF = 0, OPT_HELP, + OPT_IN, OPT_OUT, OPT_TEXT, OPT_NOOUT, OPT_ENGINE +} OPTION_CHOICE; + +OPTIONS pkeyparam_options[] = { + {"help", OPT_HELP, '-', "Display this summary"}, + {"in", OPT_IN, '<', "Input file"}, + {"out", OPT_OUT, '>', "Output file"}, + {"text", OPT_TEXT, '-', "Print parameters as text"}, + {"noout", OPT_NOOUT, '-', "Don't output encoded parameters"}, +#ifndef OPENSSL_NO_ENGINE + {"engine", OPT_ENGINE, 's', "Use engine, possibly a hardware device"}, +#endif + {NULL} +}; + +int pkeyparam_main(int argc, char **argv) +{ + ENGINE *e = NULL; + BIO *in = NULL, *out = NULL; + EVP_PKEY *pkey = NULL; + int text = 0, noout = 0, ret = 1; + OPTION_CHOICE o; + char *infile = NULL, *outfile = NULL, *prog; + + prog = opt_init(argc, argv, pkeyparam_options); + while ((o = opt_next()) != OPT_EOF) { + switch (o) { + case OPT_EOF: + case OPT_ERR: + opthelp: + BIO_printf(bio_err, "%s: Use -help for summary.\n", prog); + goto end; + case OPT_HELP: + opt_help(pkeyparam_options); + ret = 0; + goto end; + case OPT_IN: + infile = opt_arg(); + break; + case OPT_OUT: + outfile = opt_arg(); + break; + case OPT_ENGINE: + e = setup_engine(opt_arg(), 0); + break; + case OPT_TEXT: + text = 1; + break; + case OPT_NOOUT: + noout = 1; + break; + } + } + argc = opt_num_rest(); + if (argc != 0) + goto opthelp; + + in = bio_open_default(infile, 'r', FORMAT_PEM); + if (in == NULL) + goto end; + out = bio_open_default(outfile, 'w', FORMAT_PEM); + if (out == NULL) + goto end; + pkey = PEM_read_bio_Parameters(in, NULL); + if (!pkey) { + BIO_printf(bio_err, "Error reading parameters\n"); + ERR_print_errors(bio_err); + goto end; + } + + if (!noout) + PEM_write_bio_Parameters(out, pkey); + + if (text) + EVP_PKEY_print_params(out, pkey, 0, NULL); + + ret = 0; + + end: + EVP_PKEY_free(pkey); + release_engine(e); + BIO_free_all(out); + BIO_free(in); + + return ret; +} diff --git a/apps/pkeyutl.c b/apps/pkeyutl.c new file mode 100644 index 000000000..af8609d18 --- /dev/null +++ b/apps/pkeyutl.c @@ -0,0 +1,506 @@ +/* + * Copyright 2006-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include "apps.h" +#include +#include +#include +#include + +#define KEY_NONE 0 +#define KEY_PRIVKEY 1 +#define KEY_PUBKEY 2 +#define KEY_CERT 3 + +static EVP_PKEY_CTX *init_ctx(const char *kdfalg, int *pkeysize, + const char *keyfile, int keyform, int key_type, + char *passinarg, int pkey_op, ENGINE *e, + const int impl); + +static int setup_peer(EVP_PKEY_CTX *ctx, int peerform, const char *file, + ENGINE *e); + +static int do_keyop(EVP_PKEY_CTX *ctx, int pkey_op, + unsigned char *out, size_t *poutlen, + const unsigned char *in, size_t inlen); + +typedef enum OPTION_choice { + OPT_ERR = -1, OPT_EOF = 0, OPT_HELP, + OPT_ENGINE, OPT_ENGINE_IMPL, OPT_IN, OPT_OUT, + OPT_PUBIN, OPT_CERTIN, OPT_ASN1PARSE, OPT_HEXDUMP, OPT_SIGN, + OPT_VERIFY, OPT_VERIFYRECOVER, OPT_REV, OPT_ENCRYPT, OPT_DECRYPT, + OPT_DERIVE, OPT_SIGFILE, OPT_INKEY, OPT_PEERKEY, OPT_PASSIN, + OPT_PEERFORM, OPT_KEYFORM, OPT_PKEYOPT, OPT_KDF, OPT_KDFLEN, + OPT_CONFIG +} OPTION_CHOICE; + +OPTIONS pkeyutl_options[] = { + {"help", OPT_HELP, '-', "Display this summary"}, + {"in", OPT_IN, '<', "Input file - default stdin"}, + {"out", OPT_OUT, '>', "Output file - default stdout"}, + {"pubin", OPT_PUBIN, '-', "Input is a public key"}, + {"certin", OPT_CERTIN, '-', "Input is a cert with a public key"}, + {"asn1parse", OPT_ASN1PARSE, '-', "asn1parse the output data"}, + {"hexdump", OPT_HEXDUMP, '-', "Hex dump output"}, + {"sign", OPT_SIGN, '-', "Sign input data with private key"}, + {"verify", OPT_VERIFY, '-', "Verify with public key"}, + {"verifyrecover", OPT_VERIFYRECOVER, '-', + "Verify with public key, recover original data"}, + {"rev", OPT_REV, '-', "Reverse the order of the input buffer"}, + {"encrypt", OPT_ENCRYPT, '-', "Encrypt input data with public key"}, + {"decrypt", OPT_DECRYPT, '-', "Decrypt input data with private key"}, + {"derive", OPT_DERIVE, '-', "Derive shared secret"}, + {"kdf", OPT_KDF, 's', "Use KDF algorithm"}, + {"kdflen", OPT_KDFLEN, 'p', "KDF algorithm output length"}, + {"sigfile", OPT_SIGFILE, '<', "Signature file (verify operation only)"}, + {"inkey", OPT_INKEY, 's', "Input private key file"}, + {"peerkey", OPT_PEERKEY, 's', "Peer key file used in key derivation"}, + {"passin", OPT_PASSIN, 's', "Input file pass phrase source"}, + {"peerform", OPT_PEERFORM, 'E', "Peer key format - default PEM"}, + {"keyform", OPT_KEYFORM, 'E', "Private key format - default PEM"}, + {"pkeyopt", OPT_PKEYOPT, 's', "Public key options as opt:value"}, +#ifndef OPENSSL_NO_ENGINE + {"engine", OPT_ENGINE, 's', "Use engine, possibly a hardware device"}, + {"engine_impl", OPT_ENGINE_IMPL, '-', + "Also use engine given by -engine for crypto operations"}, + {"config", OPT_CONFIG, 's', "A config file"}, +#endif + {NULL} +}; + +int pkeyutl_main(int argc, char **argv) +{ + BIO *in = NULL, *out = NULL; + ENGINE *e = NULL; + EVP_PKEY_CTX *ctx = NULL; + char *infile = NULL, *outfile = NULL, *sigfile = NULL, *passinarg = NULL; + char hexdump = 0, asn1parse = 0, rev = 0, *prog; + unsigned char *buf_in = NULL, *buf_out = NULL, *sig = NULL; + OPTION_CHOICE o; + int buf_inlen = 0, siglen = -1, keyform = FORMAT_PEM, peerform = + FORMAT_PEM; + int keysize = -1, pkey_op = EVP_PKEY_OP_SIGN, key_type = KEY_PRIVKEY; + int engine_impl = 0; + int ret = 1, rv = -1; + size_t buf_outlen; + const char *inkey = NULL; + const char *peerkey = NULL; + const char *kdfalg = NULL; + int kdflen = 0; + STACK_OF(OPENSSL_STRING) *pkeyopts = NULL; + CONF *conf = NULL; + char *configfile = default_config_file; + + prog = opt_init(argc, argv, pkeyutl_options); + while ((o = opt_next()) != OPT_EOF) { + switch (o) { + case OPT_EOF: + case OPT_ERR: + opthelp: + BIO_printf(bio_err, "%s: Use -help for summary.\n", prog); + goto end; + case OPT_HELP: + opt_help(pkeyutl_options); + ret = 0; + goto end; + case OPT_IN: + infile = opt_arg(); + break; + case OPT_OUT: + outfile = opt_arg(); + break; + case OPT_SIGFILE: + sigfile = opt_arg(); + break; + case OPT_ENGINE_IMPL: + engine_impl = 1; + break; + case OPT_INKEY: + inkey = opt_arg(); + break; + case OPT_PEERKEY: + peerkey = opt_arg(); + break; + case OPT_PASSIN: + passinarg = opt_arg(); + break; + case OPT_PEERFORM: + if (!opt_format(opt_arg(), OPT_FMT_PDE, &peerform)) + goto opthelp; + break; + case OPT_KEYFORM: + if (!opt_format(opt_arg(), OPT_FMT_PDE, &keyform)) + goto opthelp; + break; + case OPT_ENGINE: + e = setup_engine(opt_arg(), 0); + break; + case OPT_PUBIN: + key_type = KEY_PUBKEY; + break; + case OPT_CERTIN: + key_type = KEY_CERT; + break; + case OPT_ASN1PARSE: + asn1parse = 1; + break; + case OPT_HEXDUMP: + hexdump = 1; + break; + case OPT_SIGN: + pkey_op = EVP_PKEY_OP_SIGN; + break; + case OPT_VERIFY: + pkey_op = EVP_PKEY_OP_VERIFY; + break; + case OPT_VERIFYRECOVER: + pkey_op = EVP_PKEY_OP_VERIFYRECOVER; + break; + case OPT_ENCRYPT: + pkey_op = EVP_PKEY_OP_ENCRYPT; + break; + case OPT_DECRYPT: + pkey_op = EVP_PKEY_OP_DECRYPT; + break; + case OPT_DERIVE: + pkey_op = EVP_PKEY_OP_DERIVE; + break; + case OPT_KDF: + pkey_op = EVP_PKEY_OP_DERIVE; + key_type = KEY_NONE; + kdfalg = opt_arg(); + break; + case OPT_KDFLEN: + kdflen = atoi(opt_arg()); + break; + case OPT_REV: + rev = 1; + break; + case OPT_PKEYOPT: + if ((pkeyopts == NULL && + (pkeyopts = sk_OPENSSL_STRING_new_null()) == NULL) || + sk_OPENSSL_STRING_push(pkeyopts, opt_arg()) == 0) { + BIO_puts(bio_err, "out of memory\n"); + goto end; + } + break; + case OPT_CONFIG: + configfile = opt_arg(); + break; + } + } + argc = opt_num_rest(); + if (argc != 0) + goto opthelp; + + if (e) + BIO_printf(bio_err, "Using configuration from %s\n", configfile); + + if ((conf = app_load_config(configfile)) == NULL) + goto end; + if (configfile != default_config_file && !app_load_modules(conf)) + goto end; + + if (kdfalg != NULL) { + if (kdflen == 0) + goto opthelp; + } else if ((inkey == NULL) + || (peerkey != NULL && pkey_op != EVP_PKEY_OP_DERIVE)) { + goto opthelp; + } + ctx = init_ctx(kdfalg, &keysize, inkey, keyform, key_type, + passinarg, pkey_op, e, engine_impl); + if (ctx == NULL) { + BIO_printf(bio_err, "%s: Error initializing context\n", prog); + ERR_print_errors(bio_err); + goto end; + } + if (peerkey != NULL && !setup_peer(ctx, peerform, peerkey, e)) { + BIO_printf(bio_err, "%s: Error setting up peer key\n", prog); + ERR_print_errors(bio_err); + goto end; + } + if (pkeyopts != NULL) { + int num = sk_OPENSSL_STRING_num(pkeyopts); + int i; + + for (i = 0; i < num; ++i) { + const char *opt = sk_OPENSSL_STRING_value(pkeyopts, i); + + if (pkey_ctrl_string(ctx, opt) <= 0) { + BIO_printf(bio_err, "%s: Can't set parameter:\n", prog); + ERR_print_errors(bio_err); + goto end; + } + } + } + + if (sigfile && (pkey_op != EVP_PKEY_OP_VERIFY)) { + BIO_printf(bio_err, + "%s: Signature file specified for non verify\n", prog); + goto end; + } + + if (!sigfile && (pkey_op == EVP_PKEY_OP_VERIFY)) { + BIO_printf(bio_err, + "%s: No signature file specified for verify\n", prog); + goto end; + } + +/* FIXME: seed PRNG only if needed */ + app_RAND_load_file(NULL, 0); + + if (pkey_op != EVP_PKEY_OP_DERIVE) { + in = bio_open_default(infile, 'r', FORMAT_BINARY); + if (in == NULL) + goto end; + } + out = bio_open_default(outfile, 'w', FORMAT_BINARY); + if (out == NULL) + goto end; + + if (sigfile) { + BIO *sigbio = BIO_new_file(sigfile, "rb"); + if (!sigbio) { + BIO_printf(bio_err, "Can't open signature file %s\n", sigfile); + goto end; + } + siglen = bio_to_mem(&sig, keysize * 10, sigbio); + BIO_free(sigbio); + if (siglen < 0) { + BIO_printf(bio_err, "Error reading signature data\n"); + goto end; + } + } + + if (in) { + /* Read the input data */ + buf_inlen = bio_to_mem(&buf_in, keysize * 10, in); + if (buf_inlen < 0) { + BIO_printf(bio_err, "Error reading input Data\n"); + exit(1); + } + if (rev) { + size_t i; + unsigned char ctmp; + size_t l = (size_t)buf_inlen; + for (i = 0; i < l / 2; i++) { + ctmp = buf_in[i]; + buf_in[i] = buf_in[l - 1 - i]; + buf_in[l - 1 - i] = ctmp; + } + } + } + + if (pkey_op == EVP_PKEY_OP_VERIFY) { + rv = EVP_PKEY_verify(ctx, sig, (size_t)siglen, + buf_in, (size_t)buf_inlen); + if (rv == 1) { + BIO_puts(out, "Signature Verified Successfully\n"); + ret = 0; + } else + BIO_puts(out, "Signature Verification Failure\n"); + goto end; + } + if (kdflen != 0) { + buf_outlen = kdflen; + rv = 1; + } else { + rv = do_keyop(ctx, pkey_op, NULL, (size_t *)&buf_outlen, + buf_in, (size_t)buf_inlen); + } + if (rv > 0 && buf_outlen != 0) { + buf_out = app_malloc(buf_outlen, "buffer output"); + rv = do_keyop(ctx, pkey_op, + buf_out, (size_t *)&buf_outlen, + buf_in, (size_t)buf_inlen); + } + if (rv <= 0) { + BIO_puts(bio_err, "Public Key operation error\n"); + ERR_print_errors(bio_err); + goto end; + } + ret = 0; + + if (asn1parse) { + if (!ASN1_parse_dump(out, buf_out, buf_outlen, 1, -1)) + ERR_print_errors(bio_err); + } else if (hexdump) + BIO_dump(out, (char *)buf_out, buf_outlen); + else + BIO_write(out, buf_out, buf_outlen); + + end: + EVP_PKEY_CTX_free(ctx); + release_engine(e); + BIO_free(in); + BIO_free_all(out); + OPENSSL_free(buf_in); + OPENSSL_free(buf_out); + OPENSSL_free(sig); + sk_OPENSSL_STRING_free(pkeyopts); + NCONF_free(conf); + return ret; +} + +static EVP_PKEY_CTX *init_ctx(const char *kdfalg, int *pkeysize, + const char *keyfile, int keyform, int key_type, + char *passinarg, int pkey_op, ENGINE *e, + const int engine_impl) +{ + EVP_PKEY *pkey = NULL; + EVP_PKEY_CTX *ctx = NULL; + ENGINE *impl = NULL; + char *passin = NULL; + int rv = -1; + X509 *x; + if (((pkey_op == EVP_PKEY_OP_SIGN) || (pkey_op == EVP_PKEY_OP_DECRYPT) + || (pkey_op == EVP_PKEY_OP_DERIVE)) + && (key_type != KEY_PRIVKEY && kdfalg == NULL)) { + BIO_printf(bio_err, "A private key is needed for this operation\n"); + goto end; + } + if (!app_passwd(passinarg, NULL, &passin, NULL)) { + BIO_printf(bio_err, "Error getting password\n"); + goto end; + } + switch (key_type) { + case KEY_PRIVKEY: + pkey = load_key(keyfile, keyform, 0, passin, e, "Private Key"); + break; + + case KEY_PUBKEY: + pkey = load_pubkey(keyfile, keyform, 0, NULL, e, "Public Key"); + break; + + case KEY_CERT: + x = load_cert(keyfile, keyform, "Certificate"); + if (x) { + pkey = X509_get_pubkey(x); + X509_free(x); + } + break; + + case KEY_NONE: + break; + + } + +#ifndef OPENSSL_NO_ENGINE + if (engine_impl) + impl = e; +#endif + + if (kdfalg) { + int kdfnid = OBJ_sn2nid(kdfalg); + if (kdfnid == NID_undef) + goto end; + ctx = EVP_PKEY_CTX_new_id(kdfnid, impl); + } else { + if (pkey == NULL) + goto end; + *pkeysize = EVP_PKEY_size(pkey); + ctx = EVP_PKEY_CTX_new(pkey, impl); + EVP_PKEY_free(pkey); + } + + if (ctx == NULL) + goto end; + + switch (pkey_op) { + case EVP_PKEY_OP_SIGN: + rv = EVP_PKEY_sign_init(ctx); + break; + + case EVP_PKEY_OP_VERIFY: + rv = EVP_PKEY_verify_init(ctx); + break; + + case EVP_PKEY_OP_VERIFYRECOVER: + rv = EVP_PKEY_verify_recover_init(ctx); + break; + + case EVP_PKEY_OP_ENCRYPT: + rv = EVP_PKEY_encrypt_init(ctx); + break; + + case EVP_PKEY_OP_DECRYPT: + rv = EVP_PKEY_decrypt_init(ctx); + break; + + case EVP_PKEY_OP_DERIVE: + rv = EVP_PKEY_derive_init(ctx); + break; + } + + if (rv <= 0) { + EVP_PKEY_CTX_free(ctx); + ctx = NULL; + } + + end: + OPENSSL_free(passin); + return ctx; + +} + +static int setup_peer(EVP_PKEY_CTX *ctx, int peerform, const char *file, + ENGINE* e) +{ + EVP_PKEY *peer = NULL; + ENGINE* engine = NULL; + int ret; + + if (peerform == FORMAT_ENGINE) + engine = e; + peer = load_pubkey(file, peerform, 0, NULL, engine, "Peer Key"); + if (!peer) { + BIO_printf(bio_err, "Error reading peer key %s\n", file); + ERR_print_errors(bio_err); + return 0; + } + + ret = EVP_PKEY_derive_set_peer(ctx, peer); + + EVP_PKEY_free(peer); + if (ret <= 0) + ERR_print_errors(bio_err); + return ret; +} + +static int do_keyop(EVP_PKEY_CTX *ctx, int pkey_op, + unsigned char *out, size_t *poutlen, + const unsigned char *in, size_t inlen) +{ + int rv = 0; + switch (pkey_op) { + case EVP_PKEY_OP_VERIFYRECOVER: + rv = EVP_PKEY_verify_recover(ctx, out, poutlen, in, inlen); + break; + + case EVP_PKEY_OP_SIGN: + rv = EVP_PKEY_sign(ctx, out, poutlen, in, inlen); + break; + + case EVP_PKEY_OP_ENCRYPT: + rv = EVP_PKEY_encrypt(ctx, out, poutlen, in, inlen); + break; + + case EVP_PKEY_OP_DECRYPT: + rv = EVP_PKEY_decrypt(ctx, out, poutlen, in, inlen); + break; + + case EVP_PKEY_OP_DERIVE: + rv = EVP_PKEY_derive(ctx, out, poutlen); + break; + + } + if (!rv) ERR_print_errors_fp(stderr); + return rv; +} diff --git a/apps/prime.c b/apps/prime.c new file mode 100644 index 000000000..b0f5969a2 --- /dev/null +++ b/apps/prime.c @@ -0,0 +1,126 @@ +/* + * Copyright 2004-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include + +#include "apps.h" +#include + +typedef enum OPTION_choice { + OPT_ERR = -1, OPT_EOF = 0, OPT_HELP, + OPT_HEX, OPT_GENERATE, OPT_BITS, OPT_SAFE, OPT_CHECKS +} OPTION_CHOICE; + +OPTIONS prime_options[] = { + {OPT_HELP_STR, 1, '-', "Usage: %s [options] [number...]\n"}, + {OPT_HELP_STR, 1, '-', + " number Number to check for primality\n"}, + {"help", OPT_HELP, '-', "Display this summary"}, + {"hex", OPT_HEX, '-', "Hex output"}, + {"generate", OPT_GENERATE, '-', "Generate a prime"}, + {"bits", OPT_BITS, 'p', "Size of number in bits"}, + {"safe", OPT_SAFE, '-', + "When used with -generate, generate a safe prime"}, + {"checks", OPT_CHECKS, 'p', "Number of checks"}, + {NULL} +}; + +int prime_main(int argc, char **argv) +{ + BIGNUM *bn = NULL; + int hex = 0, checks = 20, generate = 0, bits = 0, safe = 0, ret = 1; + char *prog; + OPTION_CHOICE o; + + prog = opt_init(argc, argv, prime_options); + while ((o = opt_next()) != OPT_EOF) { + switch (o) { + case OPT_EOF: + case OPT_ERR: + BIO_printf(bio_err, "%s: Use -help for summary.\n", prog); + goto end; + case OPT_HELP: + opt_help(prime_options); + ret = 0; + goto end; + case OPT_HEX: + hex = 1; + break; + case OPT_GENERATE: + generate = 1; + break; + case OPT_BITS: + bits = atoi(opt_arg()); + break; + case OPT_SAFE: + safe = 1; + break; + case OPT_CHECKS: + checks = atoi(opt_arg()); + break; + } + } + argc = opt_num_rest(); + argv = opt_rest(); + + if (argc == 0 && !generate) { + BIO_printf(bio_err, "%s: No prime specified\n", prog); + goto end; + } + + if (generate) { + char *s; + + if (!bits) { + BIO_printf(bio_err, "Specify the number of bits.\n"); + goto end; + } + bn = BN_new(); + if (bn == NULL) { + BIO_printf(bio_err, "Out of memory.\n"); + goto end; + } + if (!BN_generate_prime_ex(bn, bits, safe, NULL, NULL, NULL)) { + BIO_printf(bio_err, "Failed to generate prime.\n"); + goto end; + } + s = hex ? BN_bn2hex(bn) : BN_bn2dec(bn); + if (s == NULL) { + BIO_printf(bio_err, "Out of memory.\n"); + goto end; + } + BIO_printf(bio_out, "%s\n", s); + OPENSSL_free(s); + } else { + for ( ; *argv; argv++) { + int r; + + if (hex) + r = BN_hex2bn(&bn, argv[0]); + else + r = BN_dec2bn(&bn, argv[0]); + + if(!r) { + BIO_printf(bio_err, "Failed to process value (%s)\n", argv[0]); + goto end; + } + + BN_print(bio_out, bn); + BIO_printf(bio_out, " (%s) %s prime\n", + argv[0], + BN_is_prime_ex(bn, checks, NULL, NULL) + ? "is" : "is not"); + } + } + + ret = 0; + end: + BN_free(bn); + return ret; +} diff --git a/apps/privkey.pem b/apps/privkey.pem new file mode 100644 index 000000000..02f34981e --- /dev/null +++ b/apps/privkey.pem @@ -0,0 +1,16 @@ +-----BEGIN PRIVATE KEY----- +MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAMo7DFNMqywUA1O/ +qvWqCOm6rGrUAcR+dKsSXw6y2qiKO7APDDyotc0b4Mxwqjga98npex2RBIwUoCGJ +iEmMXo/a8RbXVUZ+ZwcAX7PC+XeXVC5qoajaBBkd2MvYmib/2PqnNrgvhHsUL5dO +xhC7cRqxLM/g45k3Yyw+nGa+WkTdAgMBAAECgYBMBT5w4dVG0I8foGFnz+9hzWab +Ee9IKjE5TcKmB93ilXQyjrWO5+zPmbc7ou6aAKk9IaPCTY1kCyzW7pho7Xdt+RFq +TgVXGZZfqtixO7f2/5oqZAkd00eOn9ZrhBpVMu4yXbbDvhDyFe4/oy0HGDjRUhxa +Lf6ZlBuTherxm4eFkQJBAPBQwRs9UtqaMAQlagA9pV5UsQjV1WT4IxDURMPfXgCd +ETNkB6pP0SmxQm5xhv9N2HY1UtoWpug9s0OU5IJB15sCQQDXbfbjiujNbuOxCFNw +68JZaCFVdNovyOWORkpenQLNEjVkmTCS9OayK09ADEYtsdpUGKeF+2EYBNkFr5px +CajnAkBMYI4PNz1HBuwt1SpMa0tMoMQnV7bbwVV7usskKbC5pzHZUHhzM6z5gEHp +0iEisT4Ty7zKXZqsgzefSgoaMAzzAkEAoCIaUhtwXzwdPfvNYnOs3J6doJMimECB ++lbfcyLM8TimvadtRt+KGEg/OYGmLNM2UiqdY+duzdbUpvhYGcwvYwJAQvaoi9z2 +CkiwSs/PFrLaNlfLJmXRsUBzmiWYoh6+IQJJorEXz7ewI72ee9RBO4s746cgUFwH +Ri+qO+HhZFUBqQ== +-----END PRIVATE KEY----- diff --git a/apps/progs.h b/apps/progs.h new file mode 100644 index 000000000..0df62d8f6 --- /dev/null +++ b/apps/progs.h @@ -0,0 +1,500 @@ +/* + * WARNING: do not edit! + * Generated by apps/progs.pl + * + * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +typedef enum FUNC_TYPE { + FT_none, FT_general, FT_md, FT_cipher, FT_pkey, + FT_md_alg, FT_cipher_alg +} FUNC_TYPE; + +typedef struct function_st { + FUNC_TYPE type; + const char *name; + int (*func)(int argc, char *argv[]); + const OPTIONS *help; +} FUNCTION; + +DEFINE_LHASH_OF(FUNCTION); + +extern int asn1parse_main(int argc, char *argv[]); +extern int ca_main(int argc, char *argv[]); +extern int ciphers_main(int argc, char *argv[]); +extern int cms_main(int argc, char *argv[]); +extern int crl_main(int argc, char *argv[]); +extern int crl2pkcs7_main(int argc, char *argv[]); +extern int dgst_main(int argc, char *argv[]); +extern int dhparam_main(int argc, char *argv[]); +extern int dsa_main(int argc, char *argv[]); +extern int dsaparam_main(int argc, char *argv[]); +extern int ec_main(int argc, char *argv[]); +extern int ecparam_main(int argc, char *argv[]); +extern int enc_main(int argc, char *argv[]); +extern int engine_main(int argc, char *argv[]); +extern int errstr_main(int argc, char *argv[]); +extern int exit_main(int argc, char *argv[]); +extern int gendsa_main(int argc, char *argv[]); +extern int genpkey_main(int argc, char *argv[]); +extern int genrsa_main(int argc, char *argv[]); +extern int gensm9_main(int argc, char *argv[]); +extern int help_main(int argc, char *argv[]); +extern int list_main(int argc, char *argv[]); +extern int nseq_main(int argc, char *argv[]); +extern int ocsp_main(int argc, char *argv[]); +extern int otp_main(int argc, char *argv[]); +extern int paiutl_main(int argc, char *argv[]); +extern int passwd_main(int argc, char *argv[]); +extern int pkcs12_main(int argc, char *argv[]); +extern int pkcs7_main(int argc, char *argv[]); +extern int pkcs8_main(int argc, char *argv[]); +extern int pkey_main(int argc, char *argv[]); +extern int pkeyparam_main(int argc, char *argv[]); +extern int pkeyutl_main(int argc, char *argv[]); +extern int prime_main(int argc, char *argv[]); +extern int rand_main(int argc, char *argv[]); +extern int rehash_main(int argc, char *argv[]); +extern int req_main(int argc, char *argv[]); +extern int rsa_main(int argc, char *argv[]); +extern int rsautl_main(int argc, char *argv[]); +extern int s_client_main(int argc, char *argv[]); +extern int s_server_main(int argc, char *argv[]); +extern int s_time_main(int argc, char *argv[]); +extern int sdf_main(int argc, char *argv[]); +extern int sess_id_main(int argc, char *argv[]); +extern int skf_main(int argc, char *argv[]); +extern int sm2_main(int argc, char *argv[]); +extern int sm2utl_main(int argc, char *argv[]); +extern int sm9_main(int argc, char *argv[]); +extern int sm9param_main(int argc, char *argv[]); +extern int sm9utl_main(int argc, char *argv[]); +extern int smime_main(int argc, char *argv[]); +extern int speed_main(int argc, char *argv[]); +extern int spkac_main(int argc, char *argv[]); +extern int srp_main(int argc, char *argv[]); +extern int ts_main(int argc, char *argv[]); +extern int verify_main(int argc, char *argv[]); +extern int version_main(int argc, char *argv[]); +extern int x509_main(int argc, char *argv[]); + +extern OPTIONS asn1parse_options[]; +extern OPTIONS ca_options[]; +extern OPTIONS ciphers_options[]; +extern OPTIONS cms_options[]; +extern OPTIONS crl_options[]; +extern OPTIONS crl2pkcs7_options[]; +extern OPTIONS dgst_options[]; +extern OPTIONS dhparam_options[]; +extern OPTIONS dsa_options[]; +extern OPTIONS dsaparam_options[]; +extern OPTIONS ec_options[]; +extern OPTIONS ecparam_options[]; +extern OPTIONS enc_options[]; +extern OPTIONS engine_options[]; +extern OPTIONS errstr_options[]; +extern OPTIONS exit_options[]; +extern OPTIONS gendsa_options[]; +extern OPTIONS genpkey_options[]; +extern OPTIONS genrsa_options[]; +extern OPTIONS gensm9_options[]; +extern OPTIONS help_options[]; +extern OPTIONS list_options[]; +extern OPTIONS nseq_options[]; +extern OPTIONS ocsp_options[]; +extern OPTIONS otp_options[]; +extern OPTIONS paiutl_options[]; +extern OPTIONS passwd_options[]; +extern OPTIONS pkcs12_options[]; +extern OPTIONS pkcs7_options[]; +extern OPTIONS pkcs8_options[]; +extern OPTIONS pkey_options[]; +extern OPTIONS pkeyparam_options[]; +extern OPTIONS pkeyutl_options[]; +extern OPTIONS prime_options[]; +extern OPTIONS rand_options[]; +extern OPTIONS rehash_options[]; +extern OPTIONS req_options[]; +extern OPTIONS rsa_options[]; +extern OPTIONS rsautl_options[]; +extern OPTIONS s_client_options[]; +extern OPTIONS s_server_options[]; +extern OPTIONS s_time_options[]; +extern OPTIONS sdf_options[]; +extern OPTIONS sess_id_options[]; +extern OPTIONS skf_options[]; +extern OPTIONS sm2_options[]; +extern OPTIONS sm2utl_options[]; +extern OPTIONS sm9_options[]; +extern OPTIONS sm9param_options[]; +extern OPTIONS sm9utl_options[]; +extern OPTIONS smime_options[]; +extern OPTIONS speed_options[]; +extern OPTIONS spkac_options[]; +extern OPTIONS srp_options[]; +extern OPTIONS ts_options[]; +extern OPTIONS verify_options[]; +extern OPTIONS version_options[]; +extern OPTIONS x509_options[]; + +#ifdef INCLUDE_FUNCTION_TABLE +static FUNCTION functions[] = { + { FT_general, "asn1parse", asn1parse_main, asn1parse_options }, +#ifndef OPENSSL_NO_CA + { FT_general, "ca", ca_main, ca_options }, +#endif +#ifndef OPENSSL_NO_SOCK + { FT_general, "ciphers", ciphers_main, ciphers_options }, +#endif +#ifndef OPENSSL_NO_CMS + { FT_general, "cms", cms_main, cms_options }, +#endif + { FT_general, "crl", crl_main, crl_options }, + { FT_general, "crl2pkcs7", crl2pkcs7_main, crl2pkcs7_options }, + { FT_general, "dgst", dgst_main, dgst_options }, +#ifndef OPENSSL_NO_DH + { FT_general, "dhparam", dhparam_main, dhparam_options }, +#endif +#ifndef OPENSSL_NO_DSA + { FT_general, "dsa", dsa_main, dsa_options }, +#endif +#ifndef OPENSSL_NO_DSA + { FT_general, "dsaparam", dsaparam_main, dsaparam_options }, +#endif +#ifndef OPENSSL_NO_EC + { FT_general, "ec", ec_main, ec_options }, +#endif +#ifndef OPENSSL_NO_EC + { FT_general, "ecparam", ecparam_main, ecparam_options }, +#endif + { FT_general, "enc", enc_main, enc_options }, +#ifndef OPENSSL_NO_ENGINE + { FT_general, "engine", engine_main, engine_options }, +#endif + { FT_general, "errstr", errstr_main, errstr_options }, + { FT_general, "exit", exit_main, exit_options }, +#ifndef OPENSSL_NO_DSA + { FT_general, "gendsa", gendsa_main, gendsa_options }, +#endif + { FT_general, "genpkey", genpkey_main, genpkey_options }, +#ifndef OPENSSL_NO_RSA + { FT_general, "genrsa", genrsa_main, genrsa_options }, +#endif + { FT_general, "gensm9", gensm9_main, gensm9_options }, + { FT_general, "help", help_main, help_options }, + { FT_general, "list", list_main, list_options }, + { FT_general, "nseq", nseq_main, nseq_options }, +#ifndef OPENSSL_NO_OCSP + { FT_general, "ocsp", ocsp_main, ocsp_options }, +#endif +#ifndef OPENSSL_NO_OTP + { FT_general, "otp", otp_main, otp_options }, +#endif + { FT_general, "paiutl", paiutl_main, paiutl_options }, + { FT_general, "passwd", passwd_main, passwd_options }, +#ifndef OPENSSL_NO_PKCS12 + { FT_general, "pkcs12", pkcs12_main, pkcs12_options }, +#endif +#ifndef OPENSSL_NO_PKCS7 + { FT_general, "pkcs7", pkcs7_main, pkcs7_options }, +#endif + { FT_general, "pkcs8", pkcs8_main, pkcs8_options }, + { FT_general, "pkey", pkey_main, pkey_options }, + { FT_general, "pkeyparam", pkeyparam_main, pkeyparam_options }, + { FT_general, "pkeyutl", pkeyutl_main, pkeyutl_options }, + { FT_general, "prime", prime_main, prime_options }, + { FT_general, "rand", rand_main, rand_options }, + { FT_general, "rehash", rehash_main, rehash_options }, + { FT_general, "req", req_main, req_options }, +#ifndef OPENSSL_NO_RSA + { FT_general, "rsa", rsa_main, rsa_options }, +#endif +#ifndef OPENSSL_NO_RSA + { FT_general, "rsautl", rsautl_main, rsautl_options }, +#endif +#ifndef OPENSSL_NO_SOCK + { FT_general, "s_client", s_client_main, s_client_options }, +#endif +#ifndef OPENSSL_NO_SOCK + { FT_general, "s_server", s_server_main, s_server_options }, +#endif +#ifndef OPENSSL_NO_SOCK + { FT_general, "s_time", s_time_main, s_time_options }, +#endif +#ifndef OPENSSL_NO_SDF + { FT_general, "sdf", sdf_main, sdf_options }, +#endif + { FT_general, "sess_id", sess_id_main, sess_id_options }, +#ifndef OPENSSL_NO_SKF + { FT_general, "skf", skf_main, skf_options }, +#endif +#ifndef OPENSSL_NO_SM2 + { FT_general, "sm2", sm2_main, sm2_options }, +#endif + { FT_general, "sm2utl", sm2utl_main, sm2utl_options }, +#ifndef OPENSSL_NO_SM9 + { FT_general, "sm9", sm9_main, sm9_options }, +#endif + { FT_general, "sm9param", sm9param_main, sm9param_options }, + { FT_general, "sm9utl", sm9utl_main, sm9utl_options }, + { FT_general, "smime", smime_main, smime_options }, + { FT_general, "speed", speed_main, speed_options }, + { FT_general, "spkac", spkac_main, spkac_options }, +#ifndef OPENSSL_NO_SRP + { FT_general, "srp", srp_main, srp_options }, +#endif +#ifndef OPENSSL_NO_TS + { FT_general, "ts", ts_main, ts_options }, +#endif + { FT_general, "verify", verify_main, verify_options }, + { FT_general, "version", version_main, version_options }, + { FT_general, "x509", x509_main, x509_options }, +#ifndef OPENSSL_NO_MD2 + { FT_md, "md2", dgst_main}, +#endif +#ifndef OPENSSL_NO_MD4 + { FT_md, "md4", dgst_main}, +#endif +#ifndef OPENSSL_NO_MD5 + { FT_md, "md5", dgst_main}, +#endif +#ifndef OPENSSL_NO_GOST + { FT_md, "gost", dgst_main}, +#endif +#ifndef OPENSSL_NO_SM3 + { FT_md, "sm3", dgst_main}, +#endif + { FT_md, "sha1", dgst_main}, + { FT_md, "sha224", dgst_main}, + { FT_md, "sha256", dgst_main}, + { FT_md, "sha384", dgst_main}, + { FT_md, "sha512", dgst_main}, +#ifndef OPENSSL_NO_MDC2 + { FT_md, "mdc2", dgst_main}, +#endif +#ifndef OPENSSL_NO_RMD160 + { FT_md, "rmd160", dgst_main}, +#endif +#ifndef OPENSSL_NO_BLAKE2 + { FT_md, "blake2b512", dgst_main}, +#endif +#ifndef OPENSSL_NO_BLAKE2 + { FT_md, "blake2s256", dgst_main}, +#endif +#ifndef OPENSSL_NO_SMS4 + { FT_cipher, "sms4", enc_main, enc_options }, +#endif +#ifndef OPENSSL_NO_SMS4 + { FT_cipher, "sms4-cbc", enc_main, enc_options }, +#endif +#ifndef OPENSSL_NO_SMS4 + { FT_cipher, "sms4-ecb", enc_main, enc_options }, +#endif +#ifndef OPENSSL_NO_SMS4 + { FT_cipher, "sms4-ofb", enc_main, enc_options }, +#endif +#ifndef OPENSSL_NO_SMS4 + { FT_cipher, "sms4-cfb", enc_main, enc_options }, +#endif +#ifndef OPENSSL_NO_ZUC + { FT_cipher, "zuc", enc_main, enc_options }, +#endif + { FT_cipher, "zuc256", enc_main, enc_options }, +#ifndef OPENSSL_NO_AES + { FT_cipher, "aes-128-cbc", enc_main, enc_options }, +#endif +#ifndef OPENSSL_NO_AES + { FT_cipher, "aes-128-ecb", enc_main, enc_options }, +#endif +#ifndef OPENSSL_NO_AES + { FT_cipher, "aes-192-cbc", enc_main, enc_options }, +#endif +#ifndef OPENSSL_NO_AES + { FT_cipher, "aes-192-ecb", enc_main, enc_options }, +#endif +#ifndef OPENSSL_NO_AES + { FT_cipher, "aes-256-cbc", enc_main, enc_options }, +#endif +#ifndef OPENSSL_NO_AES + { FT_cipher, "aes-256-ecb", enc_main, enc_options }, +#endif +#ifndef OPENSSL_NO_CAMELLIA + { FT_cipher, "camellia-128-cbc", enc_main, enc_options }, +#endif +#ifndef OPENSSL_NO_CAMELLIA + { FT_cipher, "camellia-128-ecb", enc_main, enc_options }, +#endif +#ifndef OPENSSL_NO_CAMELLIA + { FT_cipher, "camellia-192-cbc", enc_main, enc_options }, +#endif +#ifndef OPENSSL_NO_CAMELLIA + { FT_cipher, "camellia-192-ecb", enc_main, enc_options }, +#endif +#ifndef OPENSSL_NO_CAMELLIA + { FT_cipher, "camellia-256-cbc", enc_main, enc_options }, +#endif +#ifndef OPENSSL_NO_CAMELLIA + { FT_cipher, "camellia-256-ecb", enc_main, enc_options }, +#endif + { FT_cipher, "base64", enc_main, enc_options }, +#ifdef ZLIB + { FT_cipher, "zlib", enc_main, enc_options }, +#endif +#ifndef OPENSSL_NO_DES + { FT_cipher, "des", enc_main, enc_options }, +#endif +#ifndef OPENSSL_NO_DES + { FT_cipher, "des3", enc_main, enc_options }, +#endif +#ifndef OPENSSL_NO_DES + { FT_cipher, "desx", enc_main, enc_options }, +#endif +#ifndef OPENSSL_NO_IDEA + { FT_cipher, "idea", enc_main, enc_options }, +#endif +#ifndef OPENSSL_NO_SEED + { FT_cipher, "seed", enc_main, enc_options }, +#endif +#ifndef OPENSSL_NO_RC4 + { FT_cipher, "rc4", enc_main, enc_options }, +#endif +#ifndef OPENSSL_NO_RC4 + { FT_cipher, "rc4-40", enc_main, enc_options }, +#endif +#ifndef OPENSSL_NO_RC2 + { FT_cipher, "rc2", enc_main, enc_options }, +#endif +#ifndef OPENSSL_NO_BF + { FT_cipher, "bf", enc_main, enc_options }, +#endif +#ifndef OPENSSL_NO_CAST + { FT_cipher, "cast", enc_main, enc_options }, +#endif +#ifndef OPENSSL_NO_RC5 + { FT_cipher, "rc5", enc_main, enc_options }, +#endif +#ifndef OPENSSL_NO_DES + { FT_cipher, "des-ecb", enc_main, enc_options }, +#endif +#ifndef OPENSSL_NO_DES + { FT_cipher, "des-ede", enc_main, enc_options }, +#endif +#ifndef OPENSSL_NO_DES + { FT_cipher, "des-ede3", enc_main, enc_options }, +#endif +#ifndef OPENSSL_NO_DES + { FT_cipher, "des-cbc", enc_main, enc_options }, +#endif +#ifndef OPENSSL_NO_DES + { FT_cipher, "des-ede-cbc", enc_main, enc_options }, +#endif +#ifndef OPENSSL_NO_DES + { FT_cipher, "des-ede3-cbc", enc_main, enc_options }, +#endif +#ifndef OPENSSL_NO_DES + { FT_cipher, "des-cfb", enc_main, enc_options }, +#endif +#ifndef OPENSSL_NO_DES + { FT_cipher, "des-ede-cfb", enc_main, enc_options }, +#endif +#ifndef OPENSSL_NO_DES + { FT_cipher, "des-ede3-cfb", enc_main, enc_options }, +#endif +#ifndef OPENSSL_NO_DES + { FT_cipher, "des-ofb", enc_main, enc_options }, +#endif +#ifndef OPENSSL_NO_DES + { FT_cipher, "des-ede-ofb", enc_main, enc_options }, +#endif +#ifndef OPENSSL_NO_DES + { FT_cipher, "des-ede3-ofb", enc_main, enc_options }, +#endif +#ifndef OPENSSL_NO_IDEA + { FT_cipher, "idea-cbc", enc_main, enc_options }, +#endif +#ifndef OPENSSL_NO_IDEA + { FT_cipher, "idea-ecb", enc_main, enc_options }, +#endif +#ifndef OPENSSL_NO_IDEA + { FT_cipher, "idea-cfb", enc_main, enc_options }, +#endif +#ifndef OPENSSL_NO_IDEA + { FT_cipher, "idea-ofb", enc_main, enc_options }, +#endif +#ifndef OPENSSL_NO_SEED + { FT_cipher, "seed-cbc", enc_main, enc_options }, +#endif +#ifndef OPENSSL_NO_SEED + { FT_cipher, "seed-ecb", enc_main, enc_options }, +#endif +#ifndef OPENSSL_NO_SEED + { FT_cipher, "seed-cfb", enc_main, enc_options }, +#endif +#ifndef OPENSSL_NO_SEED + { FT_cipher, "seed-ofb", enc_main, enc_options }, +#endif +#ifndef OPENSSL_NO_RC2 + { FT_cipher, "rc2-cbc", enc_main, enc_options }, +#endif +#ifndef OPENSSL_NO_RC2 + { FT_cipher, "rc2-ecb", enc_main, enc_options }, +#endif +#ifndef OPENSSL_NO_RC2 + { FT_cipher, "rc2-cfb", enc_main, enc_options }, +#endif +#ifndef OPENSSL_NO_RC2 + { FT_cipher, "rc2-ofb", enc_main, enc_options }, +#endif +#ifndef OPENSSL_NO_RC2 + { FT_cipher, "rc2-64-cbc", enc_main, enc_options }, +#endif +#ifndef OPENSSL_NO_RC2 + { FT_cipher, "rc2-40-cbc", enc_main, enc_options }, +#endif +#ifndef OPENSSL_NO_BF + { FT_cipher, "bf-cbc", enc_main, enc_options }, +#endif +#ifndef OPENSSL_NO_BF + { FT_cipher, "bf-ecb", enc_main, enc_options }, +#endif +#ifndef OPENSSL_NO_BF + { FT_cipher, "bf-cfb", enc_main, enc_options }, +#endif +#ifndef OPENSSL_NO_BF + { FT_cipher, "bf-ofb", enc_main, enc_options }, +#endif +#ifndef OPENSSL_NO_CAST + { FT_cipher, "cast5-cbc", enc_main, enc_options }, +#endif +#ifndef OPENSSL_NO_CAST + { FT_cipher, "cast5-ecb", enc_main, enc_options }, +#endif +#ifndef OPENSSL_NO_CAST + { FT_cipher, "cast5-cfb", enc_main, enc_options }, +#endif +#ifndef OPENSSL_NO_CAST + { FT_cipher, "cast5-ofb", enc_main, enc_options }, +#endif +#ifndef OPENSSL_NO_CAST + { FT_cipher, "cast-cbc", enc_main, enc_options }, +#endif +#ifndef OPENSSL_NO_RC5 + { FT_cipher, "rc5-cbc", enc_main, enc_options }, +#endif +#ifndef OPENSSL_NO_RC5 + { FT_cipher, "rc5-ecb", enc_main, enc_options }, +#endif +#ifndef OPENSSL_NO_RC5 + { FT_cipher, "rc5-cfb", enc_main, enc_options }, +#endif +#ifndef OPENSSL_NO_RC5 + { FT_cipher, "rc5-ofb", enc_main, enc_options }, +#endif + { 0, NULL, NULL} +}; +#endif diff --git a/apps/progs.pl b/apps/progs.pl new file mode 100644 index 000000000..e9ad9ca26 --- /dev/null +++ b/apps/progs.pl @@ -0,0 +1,156 @@ +#! /usr/bin/env perl +# Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved. +# +# Licensed under the OpenSSL license (the "License"). You may not use +# this file except in compliance with the License. You can obtain a copy +# in the file LICENSE in the source distribution or at +# https://www.openssl.org/source/license.html + +# Generate progs.h file by looking for command mains in list of C files +# passed on the command line. + +use strict; +use warnings; +use configdata qw/@disablables/; + +my %commands = (); +my $cmdre = qr/^\s*int\s+([a-z_][a-z0-9_]*)_main\(\s*int\s+argc\s*,/; + +foreach my $filename (@ARGV) { + open F, $filename or die "Coudn't open $_: $!\n"; + foreach (grep /$cmdre/, ) { + my @foo = /$cmdre/; + $commands{$1} = 1; + } + close F; +} + +@ARGV = sort keys %commands; + +print <<'EOF'; +/* + * WARNING: do not edit! + * Generated by apps/progs.pl + * + * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +typedef enum FUNC_TYPE { + FT_none, FT_general, FT_md, FT_cipher, FT_pkey, + FT_md_alg, FT_cipher_alg +} FUNC_TYPE; + +typedef struct function_st { + FUNC_TYPE type; + const char *name; + int (*func)(int argc, char *argv[]); + const OPTIONS *help; +} FUNCTION; + +DEFINE_LHASH_OF(FUNCTION); + +EOF + +foreach (@ARGV) { + printf "extern int %s_main(int argc, char *argv[]);\n", $_; +} + +print "\n"; + +foreach (@ARGV) { + printf "extern OPTIONS %s_options[];\n", $_; +} + +print "\n#ifdef INCLUDE_FUNCTION_TABLE\n"; +print "static FUNCTION functions[] = {\n"; +my %cmd_disabler = ( + ciphers => "sock", + genrsa => "rsa", + rsautl => "rsa", + gendsa => "dsa", + dsaparam => "dsa", + gendh => "dh", + dhparam => "dh", + ecparam => "ec", + pkcs12 => "pkcs12", + ); +foreach my $cmd (@ARGV) { + my $str=" { FT_general, \"$cmd\", ${cmd}_main, ${cmd}_options },\n"; + if ($cmd =~ /^s_/) { + print "#ifndef OPENSSL_NO_SOCK\n${str}#endif\n"; + } elsif (grep { $cmd eq $_ } @disablables) { + print "#ifndef OPENSSL_NO_".uc($cmd)."\n${str}#endif\n"; + } elsif (my $disabler = $cmd_disabler{$cmd}) { + print "#ifndef OPENSSL_NO_".uc($disabler)."\n${str}#endif\n"; + } else { + print $str; + } +} + +my %md_disabler = ( + blake2b512 => "blake2", + blake2s256 => "blake2", + ); +foreach my $cmd ( + "md2", "md4", "md5", + "gost", "sm3", + "sha1", "sha224", "sha256", "sha384", "sha512", + "mdc2", "rmd160", "blake2b512", "blake2s256" +) { + my $str = " { FT_md, \"".$cmd."\", dgst_main},\n"; + if (grep { $cmd eq $_ } @disablables) { + print "#ifndef OPENSSL_NO_".uc($cmd)."\n${str}#endif\n"; + } elsif (my $disabler = $md_disabler{$cmd}) { + print "#ifndef OPENSSL_NO_".uc($disabler)."\n${str}#endif\n"; + } else { + print $str; + } +} + +my %cipher_disabler = ( + des3 => "des", + desx => "des", + cast5 => "cast", + ); +foreach my $cmd ( + "sms4", "sms4-cbc", "sms4-ecb", "sms4-ofb", "sms4-cfb", "zuc", "zuc256", + "aes-128-cbc", "aes-128-ecb", + "aes-192-cbc", "aes-192-ecb", + "aes-256-cbc", "aes-256-ecb", + "camellia-128-cbc", "camellia-128-ecb", + "camellia-192-cbc", "camellia-192-ecb", + "camellia-256-cbc", "camellia-256-ecb", + "base64", "zlib", + "des", "des3", "desx", "idea", "seed", "rc4", "rc4-40", + "rc2", "bf", "cast", "rc5", + "des-ecb", "des-ede", "des-ede3", + "des-cbc", "des-ede-cbc","des-ede3-cbc", + "des-cfb", "des-ede-cfb","des-ede3-cfb", + "des-ofb", "des-ede-ofb","des-ede3-ofb", + "idea-cbc","idea-ecb", "idea-cfb", "idea-ofb", + "seed-cbc","seed-ecb", "seed-cfb", "seed-ofb", + "rc2-cbc", "rc2-ecb", "rc2-cfb","rc2-ofb", "rc2-64-cbc", "rc2-40-cbc", + "bf-cbc", "bf-ecb", "bf-cfb", "bf-ofb", + "cast5-cbc","cast5-ecb", "cast5-cfb","cast5-ofb", + "cast-cbc", "rc5-cbc", "rc5-ecb", "rc5-cfb", "rc5-ofb" +) { + my $str=" { FT_cipher, \"$cmd\", enc_main, enc_options },\n"; + (my $algo= $cmd) =~ s/-.*//g; + if ($cmd eq "zlib") { + print "#ifdef ZLIB\n${str}#endif\n"; + } elsif (grep { $algo eq $_ } @disablables) { + print "#ifndef OPENSSL_NO_".uc($algo)."\n${str}#endif\n"; + } elsif (my $disabler = $cipher_disabler{$algo}) { + print "#ifndef OPENSSL_NO_".uc($disabler)."\n${str}#endif\n"; + } else { + print $str; + } +} + +print " { 0, NULL, NULL}\n};\n"; +print "#endif\n"; diff --git a/apps/rand.c b/apps/rand.c new file mode 100644 index 000000000..694c9725c --- /dev/null +++ b/apps/rand.c @@ -0,0 +1,147 @@ +/* + * Copyright 1998-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include "apps.h" + +#include +#include +#include + +#include +#include +#include + +typedef enum OPTION_choice { + OPT_ERR = -1, OPT_EOF = 0, OPT_HELP, + OPT_OUT, OPT_ENGINE, OPT_RAND, OPT_BASE64, OPT_HEX, + OPT_CONFIG +} OPTION_CHOICE; + +OPTIONS rand_options[] = { + {OPT_HELP_STR, 1, '-', "Usage: %s [flags] num\n"}, + {OPT_HELP_STR, 1, '-', "Valid options are:\n"}, + {"help", OPT_HELP, '-', "Display this summary"}, + {"out", OPT_OUT, '>', "Output file"}, + {"rand", OPT_RAND, 's', + "Load the file(s) into the random number generator"}, + {"base64", OPT_BASE64, '-', "Base64 encode output"}, + {"hex", OPT_HEX, '-', "Hex encode output"}, +#ifndef OPENSSL_NO_ENGINE + {"engine", OPT_ENGINE, 's', "Use engine, possibly a hardware device"}, + {"config", OPT_CONFIG, 's', "A config file"}, +#endif + {NULL} +}; + +int rand_main(int argc, char **argv) +{ + ENGINE *e = NULL; + BIO *out = NULL; + char *inrand = NULL, *outfile = NULL, *prog; + OPTION_CHOICE o; + int format = FORMAT_BINARY, i, num = -1, r, ret = 1; + CONF *conf = NULL; + char *configfile = default_config_file; + + prog = opt_init(argc, argv, rand_options); + while ((o = opt_next()) != OPT_EOF) { + switch (o) { + case OPT_EOF: + case OPT_ERR: + opthelp: + BIO_printf(bio_err, "%s: Use -help for summary.\n", prog); + goto end; + case OPT_HELP: + opt_help(rand_options); + ret = 0; + goto end; + case OPT_OUT: + outfile = opt_arg(); + break; + case OPT_ENGINE: + e = setup_engine(opt_arg(), 0); + break; + case OPT_RAND: + inrand = opt_arg(); + break; + case OPT_BASE64: + format = FORMAT_BASE64; + break; + case OPT_HEX: + format = FORMAT_TEXT; + break; + case OPT_CONFIG: + configfile = opt_arg(); + break; + } + } + argc = opt_num_rest(); + argv = opt_rest(); + + if (argc != 1 || !opt_int(argv[0], &num) || num < 0) + goto opthelp; + + BIO_printf(bio_err, "Using configuration from %s\n", configfile); + + if ((conf = app_load_config(configfile)) == NULL) + goto end; + if (configfile != default_config_file && !app_load_modules(conf)) + goto end; + + app_RAND_load_file(NULL, (inrand != NULL)); + if (inrand != NULL) + BIO_printf(bio_err, "%ld semi-random bytes loaded\n", + app_RAND_load_files(inrand)); + + out = bio_open_default(outfile, 'w', format); + if (out == NULL) + goto end; + + if (format == FORMAT_BASE64) { + BIO *b64 = BIO_new(BIO_f_base64()); + if (b64 == NULL) + goto end; + out = BIO_push(b64, out); + } + + while (num > 0) { + unsigned char buf[4096]; + int chunk; + + chunk = num; + if (chunk > (int)sizeof(buf)) + chunk = sizeof buf; + r = RAND_bytes(buf, chunk); + if (r <= 0) + goto end; + if (format != FORMAT_TEXT) { + if (BIO_write(out, buf, chunk) != chunk) + goto end; + } else { + for (i = 0; i < chunk; i++) + if (BIO_printf(out, "%02x", buf[i]) != 2) + goto end; + } + num -= chunk; + } + if (format == FORMAT_TEXT) + BIO_puts(out, "\n"); + if (BIO_flush(out) <= 0 || !app_RAND_write_file(NULL)) + goto end; + + ret = 0; + + end: + if (ret != 0) + ERR_print_errors(bio_err); + release_engine(e); + BIO_free_all(out); + NCONF_free(conf); + return (ret); +} diff --git a/apps/rehash.c b/apps/rehash.c new file mode 100644 index 000000000..46a74e048 --- /dev/null +++ b/apps/rehash.c @@ -0,0 +1,482 @@ +/* + * Copyright 2015-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +/* + * C implementation based on the original Perl and shell versions + * + * Copyright (c) 2013-2014 Timo Teräs + */ + +#include "apps.h" + +#if defined(OPENSSL_SYS_UNIX) || defined(__APPLE__) || \ + (defined(__VMS) && defined(__DECC) && __CTRL_VER >= 80300000) +# include +# include +# include +# include +# include +# include +# include + +# include "internal/o_dir.h" +# include +# include +# include + + +# ifndef PATH_MAX +# define PATH_MAX 4096 +# endif +# ifndef NAME_MAX +# define NAME_MAX 255 +# endif +# define MAX_COLLISIONS 256 + +typedef struct hentry_st { + struct hentry_st *next; + char *filename; + unsigned short old_id; + unsigned char need_symlink; + unsigned char digest[EVP_MAX_MD_SIZE]; +} HENTRY; + +typedef struct bucket_st { + struct bucket_st *next; + HENTRY *first_entry, *last_entry; + unsigned int hash; + unsigned short type; + unsigned short num_needed; +} BUCKET; + +enum Type { + /* Keep in sync with |suffixes|, below. */ + TYPE_CERT=0, TYPE_CRL=1 +}; + +enum Hash { + HASH_OLD, HASH_NEW, HASH_BOTH +}; + + +static int evpmdsize; +static const EVP_MD *evpmd; +static int remove_links = 1; +static int verbose = 0; +static BUCKET *hash_table[257]; + +static const char *suffixes[] = { "", "r" }; +static const char *extensions[] = { "pem", "crt", "cer", "crl" }; + + +static void bit_set(unsigned char *set, unsigned int bit) +{ + set[bit >> 3] |= 1 << (bit & 0x7); +} + +static int bit_isset(unsigned char *set, unsigned int bit) +{ + return set[bit >> 3] & (1 << (bit & 0x7)); +} + + +/* + * Process an entry; return number of errors. + */ +static int add_entry(enum Type type, unsigned int hash, const char *filename, + const unsigned char *digest, int need_symlink, + unsigned short old_id) +{ + static BUCKET nilbucket; + static HENTRY nilhentry; + BUCKET *bp; + HENTRY *ep, *found = NULL; + unsigned int ndx = (type + hash) % OSSL_NELEM(hash_table); + + for (bp = hash_table[ndx]; bp; bp = bp->next) + if (bp->type == type && bp->hash == hash) + break; + if (bp == NULL) { + bp = app_malloc(sizeof(*bp), "hash bucket"); + *bp = nilbucket; + bp->next = hash_table[ndx]; + bp->type = type; + bp->hash = hash; + hash_table[ndx] = bp; + } + + for (ep = bp->first_entry; ep; ep = ep->next) { + if (digest && memcmp(digest, ep->digest, evpmdsize) == 0) { + BIO_printf(bio_err, + "%s: skipping duplicate %s in %s\n", opt_getprog(), + type == TYPE_CERT ? "certificate" : "CRL", filename); + return 1; + } + if (strcmp(filename, ep->filename) == 0) { + found = ep; + if (digest == NULL) + break; + } + } + ep = found; + if (ep == NULL) { + if (bp->num_needed >= MAX_COLLISIONS) { + BIO_printf(bio_err, + "%s: hash table overflow for %s\n", + opt_getprog(), filename); + return 1; + } + ep = app_malloc(sizeof(*ep), "collision bucket"); + *ep = nilhentry; + ep->old_id = ~0; + ep->filename = OPENSSL_strdup(filename); + if (bp->last_entry) + bp->last_entry->next = ep; + if (bp->first_entry == NULL) + bp->first_entry = ep; + bp->last_entry = ep; + } + + if (old_id < ep->old_id) + ep->old_id = old_id; + if (need_symlink && !ep->need_symlink) { + ep->need_symlink = 1; + bp->num_needed++; + memcpy(ep->digest, digest, evpmdsize); + } + return 0; +} + +/* + * Check if a symlink goes to the right spot; return 0 if okay. + * This can be -1 if bad filename, or an error count. + */ +static int handle_symlink(const char *filename, const char *fullpath) +{ + unsigned int hash = 0; + int i, type, id; + unsigned char ch; + char linktarget[PATH_MAX], *endptr; + ossl_ssize_t n; + + for (i = 0; i < 8; i++) { + ch = filename[i]; + if (!isxdigit(ch)) + return -1; + hash <<= 4; + hash += OPENSSL_hexchar2int(ch); + } + if (filename[i++] != '.') + return -1; + for (type = OSSL_NELEM(suffixes) - 1; type > 0; type--) { + const char *suffix = suffixes[type]; + if (strncasecmp(suffix, &filename[i], strlen(suffix)) == 0) + break; + } + i += strlen(suffixes[type]); + + id = strtoul(&filename[i], &endptr, 10); + if (*endptr != '\0') + return -1; + + n = readlink(fullpath, linktarget, sizeof(linktarget)); + if (n < 0 || n >= (int)sizeof(linktarget)) + return -1; + linktarget[n] = 0; + + return add_entry(type, hash, linktarget, NULL, 0, id); +} + +/* + * process a file, return number of errors. + */ +static int do_file(const char *filename, const char *fullpath, enum Hash h) +{ + STACK_OF (X509_INFO) *inf = NULL; + X509_INFO *x; + X509_NAME *name = NULL; + BIO *b; + const char *ext; + unsigned char digest[EVP_MAX_MD_SIZE]; + int type, errs = 0; + size_t i; + + /* Does it end with a recognized extension? */ + if ((ext = strrchr(filename, '.')) == NULL) + goto end; + for (i = 0; i < OSSL_NELEM(extensions); i++) { + if (strcasecmp(extensions[i], ext + 1) == 0) + break; + } + if (i >= OSSL_NELEM(extensions)) + goto end; + + /* Does it have X.509 data in it? */ + if ((b = BIO_new_file(fullpath, "r")) == NULL) { + BIO_printf(bio_err, "%s: skipping %s, cannot open file\n", + opt_getprog(), filename); + errs++; + goto end; + } + inf = PEM_X509_INFO_read_bio(b, NULL, NULL, NULL); + BIO_free(b); + if (inf == NULL) + goto end; + + if (sk_X509_INFO_num(inf) != 1) { + BIO_printf(bio_err, + "%s: skipping %s," + "it does not contain exactly one certificate or CRL\n", + opt_getprog(), filename); + /* This is not an error. */ + goto end; + } + x = sk_X509_INFO_value(inf, 0); + if (x->x509) { + type = TYPE_CERT; + name = X509_get_subject_name(x->x509); + X509_digest(x->x509, evpmd, digest, NULL); + } else if (x->crl) { + type = TYPE_CRL; + name = X509_CRL_get_issuer(x->crl); + X509_CRL_digest(x->crl, evpmd, digest, NULL); + } else { + ++errs; + goto end; + } + if (name) { + if ((h == HASH_NEW) || (h == HASH_BOTH)) + errs += add_entry(type, X509_NAME_hash(name), filename, digest, 1, ~0); +#ifndef OPENSSL_NO_MD5 + if ((h == HASH_OLD) || (h == HASH_BOTH)) + errs += add_entry(type, X509_NAME_hash_old(name), filename, digest, 1, ~0); +#endif + } + +end: + sk_X509_INFO_pop_free(inf, X509_INFO_free); + return errs; +} + +static void str_free(char *s) +{ + OPENSSL_free(s); +} + +/* + * Process a directory; return number of errors found. + */ +static int do_dir(const char *dirname, enum Hash h) +{ + BUCKET *bp, *nextbp; + HENTRY *ep, *nextep; + OPENSSL_DIR_CTX *d = NULL; + struct stat st; + unsigned char idmask[MAX_COLLISIONS / 8]; + int n, numfiles, nextid, buflen, errs = 0; + size_t i; + const char *pathsep; + const char *filename; + char *buf, *copy; + STACK_OF(OPENSSL_STRING) *files = NULL; + + if (app_access(dirname, W_OK) < 0) { + BIO_printf(bio_err, "Skipping %s, can't write\n", dirname); + return 1; + } + buflen = strlen(dirname); + pathsep = (buflen && dirname[buflen - 1] == '/') ? "" : "/"; + buflen += NAME_MAX + 1 + 1; + buf = app_malloc(buflen, "filename buffer"); + + if (verbose) + BIO_printf(bio_out, "Doing %s\n", dirname); + + if ((files = sk_OPENSSL_STRING_new_null()) == NULL) { + BIO_printf(bio_err, "Skipping %s, out of memory\n", dirname); + exit(1); + } + while ((filename = OPENSSL_DIR_read(&d, dirname)) != NULL) { + if ((copy = strdup(filename)) == NULL + || sk_OPENSSL_STRING_push(files, copy) == 0) { + BIO_puts(bio_err, "out of memory\n"); + exit(1); + } + } + OPENSSL_DIR_end(&d); + sk_OPENSSL_STRING_sort(files); + + numfiles = sk_OPENSSL_STRING_num(files); + for (n = 0; n < numfiles; ++n) { + filename = sk_OPENSSL_STRING_value(files, n); + if (snprintf(buf, buflen, "%s%s%s", + dirname, pathsep, filename) >= buflen) + continue; + if (lstat(buf, &st) < 0) + continue; + if (S_ISLNK(st.st_mode) && handle_symlink(filename, buf) == 0) + continue; + errs += do_file(filename, buf, h); + } + sk_OPENSSL_STRING_pop_free(files, str_free); + + for (i = 0; i < OSSL_NELEM(hash_table); i++) { + for (bp = hash_table[i]; bp; bp = nextbp) { + nextbp = bp->next; + nextid = 0; + memset(idmask, 0, (bp->num_needed + 7) / 8); + for (ep = bp->first_entry; ep; ep = ep->next) + if (ep->old_id < bp->num_needed) + bit_set(idmask, ep->old_id); + + for (ep = bp->first_entry; ep; ep = nextep) { + nextep = ep->next; + if (ep->old_id < bp->num_needed) { + /* Link exists, and is used as-is */ + snprintf(buf, buflen, "%08x.%s%d", bp->hash, + suffixes[bp->type], ep->old_id); + if (verbose) + BIO_printf(bio_out, "link %s -> %s\n", + ep->filename, buf); + } else if (ep->need_symlink) { + /* New link needed (it may replace something) */ + while (bit_isset(idmask, nextid)) + nextid++; + + snprintf(buf, buflen, "%s%s%n%08x.%s%d", + dirname, pathsep, &n, bp->hash, + suffixes[bp->type], nextid); + if (verbose) + BIO_printf(bio_out, "link %s -> %s\n", + ep->filename, &buf[n]); + if (unlink(buf) < 0 && errno != ENOENT) { + BIO_printf(bio_err, + "%s: Can't unlink %s, %s\n", + opt_getprog(), buf, strerror(errno)); + errs++; + } + if (symlink(ep->filename, buf) < 0) { + BIO_printf(bio_err, + "%s: Can't symlink %s, %s\n", + opt_getprog(), ep->filename, + strerror(errno)); + errs++; + } + bit_set(idmask, nextid); + } else if (remove_links) { + /* Link to be deleted */ + snprintf(buf, buflen, "%s%s%n%08x.%s%d", + dirname, pathsep, &n, bp->hash, + suffixes[bp->type], ep->old_id); + if (verbose) + BIO_printf(bio_out, "unlink %s\n", + &buf[n]); + if (unlink(buf) < 0 && errno != ENOENT) { + BIO_printf(bio_err, + "%s: Can't unlink %s, %s\n", + opt_getprog(), buf, strerror(errno)); + errs++; + } + } + OPENSSL_free(ep->filename); + OPENSSL_free(ep); + } + OPENSSL_free(bp); + } + hash_table[i] = NULL; + } + + OPENSSL_free(buf); + return errs; +} + +typedef enum OPTION_choice { + OPT_ERR = -1, OPT_EOF = 0, OPT_HELP, + OPT_COMPAT, OPT_OLD, OPT_N, OPT_VERBOSE +} OPTION_CHOICE; + +OPTIONS rehash_options[] = { + {OPT_HELP_STR, 1, '-', "Usage: %s [options] [cert-directory...]\n"}, + {OPT_HELP_STR, 1, '-', "Valid options are:\n"}, + {"help", OPT_HELP, '-', "Display this summary"}, + {"h", OPT_HELP, '-', "Display this summary"}, + {"compat", OPT_COMPAT, '-', "Create both new- and old-style hash links"}, + {"old", OPT_OLD, '-', "Use old-style hash to generate links"}, + {"n", OPT_N, '-', "Do not remove existing links"}, + {"v", OPT_VERBOSE, '-', "Verbose output"}, + {NULL} +}; + + +int rehash_main(int argc, char **argv) +{ + const char *env, *prog; + char *e, *m; + int errs = 0; + OPTION_CHOICE o; + enum Hash h = HASH_NEW; + + prog = opt_init(argc, argv, rehash_options); + while ((o = opt_next()) != OPT_EOF) { + switch (o) { + case OPT_EOF: + case OPT_ERR: + BIO_printf(bio_err, "%s: Use -help for summary.\n", prog); + goto end; + case OPT_HELP: + opt_help(rehash_options); + goto end; + case OPT_COMPAT: + h = HASH_BOTH; + break; + case OPT_OLD: + h = HASH_OLD; + break; + case OPT_N: + remove_links = 0; + break; + case OPT_VERBOSE: + verbose = 1; + break; + } + } + argc = opt_num_rest(); + argv = opt_rest(); + + evpmd = EVP_get_digestbynid(NID_sha1); + evpmdsize = EVP_MD_size(evpmd); + + if (*argv) { + while (*argv) + errs += do_dir(*argv++, h); + } else if ((env = getenv("SSL_CERT_DIR")) != NULL) { + m = OPENSSL_strdup(env); + for (e = strtok(m, ":"); e != NULL; e = strtok(NULL, ":")) + errs += do_dir(e, h); + OPENSSL_free(m); + } else { + errs += do_dir("/etc/ssl/certs", h); + } + + end: + return errs; +} + +#else +OPTIONS rehash_options[] = { + {NULL} +}; + +int rehash_main(int argc, char **argv) +{ + BIO_printf(bio_err, "Not available; use c_rehash script\n"); + return (1); +} + +#endif /* defined(OPENSSL_SYS_UNIX) || defined(__APPLE__) */ diff --git a/apps/req.c b/apps/req.c new file mode 100644 index 000000000..406520927 --- /dev/null +++ b/apps/req.c @@ -0,0 +1,1518 @@ +/* + * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#include +#include +#include +#include "apps.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifndef OPENSSL_NO_RSA +# include +#endif +#ifndef OPENSSL_NO_DSA +# include +#endif + +#define SECTION "req" + +#define BITS "default_bits" +#define KEYFILE "default_keyfile" +#define PROMPT "prompt" +#define DISTINGUISHED_NAME "distinguished_name" +#define ATTRIBUTES "attributes" +#define V3_EXTENSIONS "x509_extensions" +#define REQ_EXTENSIONS "req_extensions" +#define STRING_MASK "string_mask" +#define UTF8_IN "utf8" + +#define DEFAULT_KEY_LENGTH 2048 +#define MIN_KEY_LENGTH 512 + +static int make_REQ(X509_REQ *req, EVP_PKEY *pkey, char *dn, int mutlirdn, + int attribs, unsigned long chtype); +static int build_subject(X509_REQ *req, const char *subj, unsigned long chtype, + int multirdn); +static int prompt_info(X509_REQ *req, + STACK_OF(CONF_VALUE) *dn_sk, const char *dn_sect, + STACK_OF(CONF_VALUE) *attr_sk, const char *attr_sect, + int attribs, unsigned long chtype); +static int auto_info(X509_REQ *req, STACK_OF(CONF_VALUE) *sk, + STACK_OF(CONF_VALUE) *attr, int attribs, + unsigned long chtype); +static int add_attribute_object(X509_REQ *req, char *text, const char *def, + char *value, int nid, int n_min, int n_max, + unsigned long chtype); +static int add_DN_object(X509_NAME *n, char *text, const char *def, + char *value, int nid, int n_min, int n_max, + unsigned long chtype, int mval); +static int genpkey_cb(EVP_PKEY_CTX *ctx); +static int req_check_len(int len, int n_min, int n_max); +static int check_end(const char *str, const char *end); +static EVP_PKEY_CTX *set_keygen_ctx(const char *gstr, + int *pkey_type, long *pkeylen, + char **palgnam, ENGINE *keygen_engine); +static CONF *req_conf = NULL; +static int batch = 0; + +typedef enum OPTION_choice { + OPT_ERR = -1, OPT_EOF = 0, OPT_HELP, + OPT_INFORM, OPT_OUTFORM, OPT_ENGINE, OPT_KEYGEN_ENGINE, OPT_KEY, + OPT_PUBKEY, OPT_NEW, OPT_CONFIG, OPT_KEYFORM, OPT_IN, OPT_OUT, + OPT_KEYOUT, OPT_PASSIN, OPT_PASSOUT, OPT_RAND, OPT_NEWKEY, + OPT_PKEYOPT, OPT_SIGOPT, OPT_BATCH, OPT_NEWHDR, OPT_MODULUS, + OPT_VERIFY, OPT_NODES, OPT_NOOUT, OPT_VERBOSE, OPT_UTF8, + OPT_NAMEOPT, OPT_REQOPT, OPT_SUBJ, OPT_SUBJECT, OPT_TEXT, OPT_X509, + OPT_MULTIVALUE_RDN, OPT_DAYS, OPT_SET_SERIAL, OPT_EXTENSIONS, + OPT_REQEXTS, OPT_MD +} OPTION_CHOICE; + +OPTIONS req_options[] = { + {"help", OPT_HELP, '-', "Display this summary"}, + {"inform", OPT_INFORM, 'F', "Input format - DER or PEM"}, + {"outform", OPT_OUTFORM, 'F', "Output format - DER or PEM"}, + {"in", OPT_IN, '<', "Input file"}, + {"out", OPT_OUT, '>', "Output file"}, + {"key", OPT_KEY, 's', "Private key to use"}, + {"keyform", OPT_KEYFORM, 'f', "Key file format"}, + {"pubkey", OPT_PUBKEY, '-', "Output public key"}, + {"new", OPT_NEW, '-', "New request"}, + {"config", OPT_CONFIG, '<', "Request template file"}, + {"keyout", OPT_KEYOUT, '>', "File to send the key to"}, + {"passin", OPT_PASSIN, 's', "Private key password source"}, + {"passout", OPT_PASSOUT, 's', "Output file pass phrase source"}, + {"rand", OPT_RAND, 's', + "Load the file(s) into the random number generator"}, + {"newkey", OPT_NEWKEY, 's', "Specify as type:bits"}, + {"pkeyopt", OPT_PKEYOPT, 's', "Public key options as opt:value"}, + {"sigopt", OPT_SIGOPT, 's', "Signature parameter in n:v form"}, + {"batch", OPT_BATCH, '-', + "Do not ask anything during request generation"}, + {"newhdr", OPT_NEWHDR, '-', "Output \"NEW\" in the header lines"}, + {"modulus", OPT_MODULUS, '-', "RSA modulus"}, + {"verify", OPT_VERIFY, '-', "Verify signature on REQ"}, + {"nodes", OPT_NODES, '-', "Don't encrypt the output key"}, + {"noout", OPT_NOOUT, '-', "Do not output REQ"}, + {"verbose", OPT_VERBOSE, '-', "Verbose output"}, + {"utf8", OPT_UTF8, '-', "Input characters are UTF8 (default ASCII)"}, + {"nameopt", OPT_NAMEOPT, 's', "Various certificate name options"}, + {"reqopt", OPT_REQOPT, 's', "Various request text options"}, + {"text", OPT_TEXT, '-', "Text form of request"}, +#ifndef OPENSSL_NO_CA + {"x509", OPT_X509, '-', + "Output a x509 structure instead of a cert request"}, +#endif + {OPT_MORE_STR, 1, 1, "(Required by some CA's)"}, + {"subj", OPT_SUBJ, 's', "Set or modify request subject"}, + {"subject", OPT_SUBJECT, '-', "Output the request's subject"}, + {"multivalue-rdn", OPT_MULTIVALUE_RDN, '-', + "Enable support for multivalued RDNs"}, + {"days", OPT_DAYS, 'p', "Number of days cert is valid for"}, + {"set_serial", OPT_SET_SERIAL, 'p', "Serial number to use"}, + {"extensions", OPT_EXTENSIONS, 's', + "Cert extension section (override value in config file)"}, + {"reqexts", OPT_REQEXTS, 's', + "Request extension section (override value in config file)"}, + {"", OPT_MD, '-', "Any supported digest"}, +#ifndef OPENSSL_NO_ENGINE + {"engine", OPT_ENGINE, 's', "Use engine, possibly a hardware device"}, + {"keygen_engine", OPT_KEYGEN_ENGINE, 's', + "Specify engine to be used for key generation operations"}, +#endif + {NULL} +}; + +int req_main(int argc, char **argv) +{ + ASN1_INTEGER *serial = NULL; + BIO *in = NULL, *out = NULL; + ENGINE *e = NULL, *gen_eng = NULL; + EVP_PKEY *pkey = NULL; + EVP_PKEY_CTX *genctx = NULL; + STACK_OF(OPENSSL_STRING) *pkeyopts = NULL, *sigopts = NULL; + X509 *x509ss = NULL; + X509_REQ *req = NULL; + const EVP_CIPHER *cipher = NULL; + const EVP_MD *md_alg = NULL, *digest = NULL; + char *extensions = NULL, *infile = NULL; + char *outfile = NULL, *keyfile = NULL, *inrand = NULL; + char *keyalgstr = NULL, *p, *prog, *passargin = NULL, *passargout = NULL; + char *passin = NULL, *passout = NULL; + char *nofree_passin = NULL, *nofree_passout = NULL; + char *req_exts = NULL, *subj = NULL; + char *template = default_config_file, *keyout = NULL; + const char *keyalg = NULL; + OPTION_CHOICE o; + int ret = 1, x509 = 0, days = 30, i = 0, newreq = 0, verbose = 0; + int pkey_type = -1, private = 0; + int informat = FORMAT_PEM, outformat = FORMAT_PEM, keyform = FORMAT_PEM; + int modulus = 0, multirdn = 0, verify = 0, noout = 0, text = 0; + int nodes = 0, newhdr = 0, subject = 0, pubkey = 0; + long newkey = -1; + unsigned long chtype = MBSTRING_ASC, nmflag = 0, reqflag = 0; + char nmflag_set = 0; + +#ifndef OPENSSL_NO_DES + cipher = EVP_des_ede3_cbc(); +#endif + + prog = opt_init(argc, argv, req_options); + while ((o = opt_next()) != OPT_EOF) { + switch (o) { + case OPT_EOF: + case OPT_ERR: + opthelp: + BIO_printf(bio_err, "%s: Use -help for summary.\n", prog); + goto end; + case OPT_HELP: + opt_help(req_options); + ret = 0; + goto end; + case OPT_INFORM: + if (!opt_format(opt_arg(), OPT_FMT_PEMDER, &informat)) + goto opthelp; + break; + case OPT_OUTFORM: + if (!opt_format(opt_arg(), OPT_FMT_PEMDER, &outformat)) + goto opthelp; + break; + case OPT_ENGINE: + e = setup_engine(opt_arg(), 0); + break; + case OPT_KEYGEN_ENGINE: +#ifndef OPENSSL_NO_ENGINE + gen_eng = ENGINE_by_id(opt_arg()); + if (gen_eng == NULL) { + BIO_printf(bio_err, "Can't find keygen engine %s\n", *argv); + goto opthelp; + } +#endif + break; + case OPT_KEY: + keyfile = opt_arg(); + break; + case OPT_PUBKEY: + pubkey = 1; + break; + case OPT_NEW: + newreq = 1; + break; + case OPT_CONFIG: + template = opt_arg(); + break; + case OPT_KEYFORM: + if (!opt_format(opt_arg(), OPT_FMT_ANY, &keyform)) + goto opthelp; + break; + case OPT_IN: + infile = opt_arg(); + break; + case OPT_OUT: + outfile = opt_arg(); + break; + case OPT_KEYOUT: + keyout = opt_arg(); + break; + case OPT_PASSIN: + passargin = opt_arg(); + break; + case OPT_PASSOUT: + passargout = opt_arg(); + break; + case OPT_RAND: + inrand = opt_arg(); + break; + case OPT_NEWKEY: + keyalg = opt_arg(); + newreq = 1; + break; + case OPT_PKEYOPT: + if (!pkeyopts) + pkeyopts = sk_OPENSSL_STRING_new_null(); + if (!pkeyopts || !sk_OPENSSL_STRING_push(pkeyopts, opt_arg())) + goto opthelp; + break; + case OPT_SIGOPT: + if (!sigopts) + sigopts = sk_OPENSSL_STRING_new_null(); + if (!sigopts || !sk_OPENSSL_STRING_push(sigopts, opt_arg())) + goto opthelp; + break; + case OPT_BATCH: + batch = 1; + break; + case OPT_NEWHDR: + newhdr = 1; + break; + case OPT_MODULUS: + modulus = 1; + break; + case OPT_VERIFY: + verify = 1; + break; + case OPT_NODES: + nodes = 1; + break; + case OPT_NOOUT: + noout = 1; + break; + case OPT_VERBOSE: + verbose = 1; + break; + case OPT_UTF8: + chtype = MBSTRING_UTF8; + break; + case OPT_NAMEOPT: + nmflag_set = 1; + if (!set_name_ex(&nmflag, opt_arg())) + goto opthelp; + break; + case OPT_REQOPT: + if (!set_cert_ex(&reqflag, opt_arg())) + goto opthelp; + break; + case OPT_TEXT: + text = 1; + break; +#ifndef OPENSSL_NO_CA + case OPT_X509: + x509 = 1; + newreq = 1; + break; +#endif + case OPT_DAYS: + days = atoi(opt_arg()); + break; + case OPT_SET_SERIAL: + if (serial != NULL) { + BIO_printf(bio_err, "Serial number supplied twice\n"); + goto opthelp; + } + serial = s2i_ASN1_INTEGER(NULL, opt_arg()); + if (serial == NULL) + goto opthelp; + break; + case OPT_SUBJECT: + subject = 1; + break; + case OPT_SUBJ: + subj = opt_arg(); + break; + case OPT_MULTIVALUE_RDN: + multirdn = 1; + break; + case OPT_EXTENSIONS: + extensions = opt_arg(); + break; + case OPT_REQEXTS: + req_exts = opt_arg(); + break; + case OPT_MD: + if (!opt_md(opt_unknown(), &md_alg)) + goto opthelp; + digest = md_alg; + break; + } + } + argc = opt_num_rest(); + if (argc != 0) + goto opthelp; + + if (!nmflag_set) + nmflag = XN_FLAG_ONELINE; + + /* TODO: simplify this as pkey is still always NULL here */ + private = newreq && (pkey == NULL) ? 1 : 0; + + if (!app_passwd(passargin, passargout, &passin, &passout)) { + BIO_printf(bio_err, "Error getting passwords\n"); + goto end; + } + + if (verbose) + BIO_printf(bio_err, "Using configuration from %s\n", template); + req_conf = app_load_config(template); + if (template != default_config_file && !app_load_modules(req_conf)) + goto end; + + if (req_conf != NULL) { + p = NCONF_get_string(req_conf, NULL, "oid_file"); + if (p == NULL) + ERR_clear_error(); + if (p != NULL) { + BIO *oid_bio; + + oid_bio = BIO_new_file(p, "r"); + if (oid_bio == NULL) { + /*- + BIO_printf(bio_err,"problems opening %s for extra oid's\n",p); + ERR_print_errors(bio_err); + */ + } else { + OBJ_create_objects(oid_bio); + BIO_free(oid_bio); + } + } + } + if (!add_oid_section(req_conf)) + goto end; + + if (md_alg == NULL) { + p = NCONF_get_string(req_conf, SECTION, "default_md"); + if (p == NULL) + ERR_clear_error(); + else { + if (!opt_md(p, &md_alg)) + goto opthelp; + digest = md_alg; + } + } + + if (!extensions) { + extensions = NCONF_get_string(req_conf, SECTION, V3_EXTENSIONS); + if (!extensions) + ERR_clear_error(); + } + if (extensions) { + /* Check syntax of file */ + X509V3_CTX ctx; + X509V3_set_ctx_test(&ctx); + X509V3_set_nconf(&ctx, req_conf); + if (!X509V3_EXT_add_nconf(req_conf, &ctx, extensions, NULL)) { + BIO_printf(bio_err, + "Error Loading extension section %s\n", extensions); + goto end; + } + } + + if (passin == NULL) { + passin = nofree_passin = + NCONF_get_string(req_conf, SECTION, "input_password"); + if (passin == NULL) + ERR_clear_error(); + } + + if (passout == NULL) { + passout = nofree_passout = + NCONF_get_string(req_conf, SECTION, "output_password"); + if (passout == NULL) + ERR_clear_error(); + } + + p = NCONF_get_string(req_conf, SECTION, STRING_MASK); + if (!p) + ERR_clear_error(); + + if (p && !ASN1_STRING_set_default_mask_asc(p)) { + BIO_printf(bio_err, "Invalid global string mask setting %s\n", p); + goto end; + } + + if (chtype != MBSTRING_UTF8) { + p = NCONF_get_string(req_conf, SECTION, UTF8_IN); + if (!p) + ERR_clear_error(); + else if (strcmp(p, "yes") == 0) + chtype = MBSTRING_UTF8; + } + + if (!req_exts) { + req_exts = NCONF_get_string(req_conf, SECTION, REQ_EXTENSIONS); + if (!req_exts) + ERR_clear_error(); + } + if (req_exts) { + /* Check syntax of file */ + X509V3_CTX ctx; + X509V3_set_ctx_test(&ctx); + X509V3_set_nconf(&ctx, req_conf); + if (!X509V3_EXT_add_nconf(req_conf, &ctx, req_exts, NULL)) { + BIO_printf(bio_err, + "Error Loading request extension section %s\n", + req_exts); + goto end; + } + } + + if (keyfile != NULL) { + pkey = load_key(keyfile, keyform, 0, passin, e, "Private Key"); + if (!pkey) { + /* load_key() has already printed an appropriate message */ + goto end; + } else { + char *randfile = NCONF_get_string(req_conf, SECTION, "RANDFILE"); + if (randfile == NULL) + ERR_clear_error(); + app_RAND_load_file(randfile, 0); + } + } + + if (newreq && (pkey == NULL)) { + char *randfile = NCONF_get_string(req_conf, SECTION, "RANDFILE"); + if (randfile == NULL) + ERR_clear_error(); + app_RAND_load_file(randfile, 0); + if (inrand) + app_RAND_load_files(inrand); + + if (!NCONF_get_number(req_conf, SECTION, BITS, &newkey)) { + newkey = DEFAULT_KEY_LENGTH; + } + + if (keyalg) { + genctx = set_keygen_ctx(keyalg, &pkey_type, &newkey, + &keyalgstr, gen_eng); + if (!genctx) + goto end; + } + + if (newkey < MIN_KEY_LENGTH + && (pkey_type == EVP_PKEY_RSA || pkey_type == EVP_PKEY_DSA)) { + BIO_printf(bio_err, "private key length is too short,\n"); + BIO_printf(bio_err, "it needs to be at least %d bits, not %ld\n", + MIN_KEY_LENGTH, newkey); + goto end; + } + + if (!genctx) { + genctx = set_keygen_ctx(NULL, &pkey_type, &newkey, + &keyalgstr, gen_eng); + if (!genctx) + goto end; + } + + if (pkeyopts) { + char *genopt; + for (i = 0; i < sk_OPENSSL_STRING_num(pkeyopts); i++) { + genopt = sk_OPENSSL_STRING_value(pkeyopts, i); + if (pkey_ctrl_string(genctx, genopt) <= 0) { + BIO_printf(bio_err, "parameter error \"%s\"\n", genopt); + ERR_print_errors(bio_err); + goto end; + } + } + } + + if (pkey_type == EVP_PKEY_EC) { + BIO_printf(bio_err, "Generating an EC private key\n"); + } else { + BIO_printf(bio_err, "Generating a %ld bit %s private key\n", + newkey, keyalgstr); + } + + EVP_PKEY_CTX_set_cb(genctx, genpkey_cb); + EVP_PKEY_CTX_set_app_data(genctx, bio_err); + + if (EVP_PKEY_keygen(genctx, &pkey) <= 0) { + BIO_puts(bio_err, "Error Generating Key\n"); + goto end; + } + + EVP_PKEY_CTX_free(genctx); + genctx = NULL; + + app_RAND_write_file(randfile); + + if (keyout == NULL) { + keyout = NCONF_get_string(req_conf, SECTION, KEYFILE); + if (keyout == NULL) + ERR_clear_error(); + } + + if (keyout == NULL) + BIO_printf(bio_err, "writing new private key to stdout\n"); + else + BIO_printf(bio_err, "writing new private key to '%s'\n", keyout); + out = bio_open_owner(keyout, outformat, private); + if (out == NULL) + goto end; + + p = NCONF_get_string(req_conf, SECTION, "encrypt_rsa_key"); + if (p == NULL) { + ERR_clear_error(); + p = NCONF_get_string(req_conf, SECTION, "encrypt_key"); + if (p == NULL) + ERR_clear_error(); + } + if ((p != NULL) && (strcmp(p, "no") == 0)) + cipher = NULL; + if (nodes) + cipher = NULL; + + i = 0; + loop: + assert(private); + if (!PEM_write_bio_PrivateKey(out, pkey, cipher, + NULL, 0, NULL, passout)) { + if ((ERR_GET_REASON(ERR_peek_error()) == + PEM_R_PROBLEMS_GETTING_PASSWORD) && (i < 3)) { + ERR_clear_error(); + i++; + goto loop; + } + goto end; + } + BIO_free(out); + out = NULL; + BIO_printf(bio_err, "-----\n"); + } + + if (!newreq) { + in = bio_open_default(infile, 'r', informat); + if (in == NULL) + goto end; + + if (informat == FORMAT_ASN1) + req = d2i_X509_REQ_bio(in, NULL); + else + req = PEM_read_bio_X509_REQ(in, NULL, NULL, NULL); + if (req == NULL) { + BIO_printf(bio_err, "unable to load X509 request\n"); + goto end; + } + } + + if (newreq) { + if (pkey == NULL) { + BIO_printf(bio_err, "you need to specify a private key\n"); + goto end; + } + + if (req == NULL) { + req = X509_REQ_new(); + if (req == NULL) { + goto end; + } + + i = make_REQ(req, pkey, subj, multirdn, !x509, chtype); + subj = NULL; /* done processing '-subj' option */ + if (!i) { + BIO_printf(bio_err, "problems making Certificate Request\n"); + goto end; + } + } + if (x509) { + EVP_PKEY *tmppkey; + X509V3_CTX ext_ctx; + if ((x509ss = X509_new()) == NULL) + goto end; + + /* Set version to V3 */ + if (extensions && !X509_set_version(x509ss, 2)) + goto end; + if (serial) { + if (!X509_set_serialNumber(x509ss, serial)) + goto end; + } else { + if (!rand_serial(NULL, X509_get_serialNumber(x509ss))) + goto end; + } + + if (!X509_set_issuer_name(x509ss, X509_REQ_get_subject_name(req))) + goto end; + if (!set_cert_times(x509ss, NULL, NULL, days)) + goto end; + if (!X509_set_subject_name + (x509ss, X509_REQ_get_subject_name(req))) + goto end; + tmppkey = X509_REQ_get0_pubkey(req); + if (!tmppkey || !X509_set_pubkey(x509ss, tmppkey)) + goto end; + + /* Set up V3 context struct */ + + X509V3_set_ctx(&ext_ctx, x509ss, x509ss, NULL, NULL, 0); + X509V3_set_nconf(&ext_ctx, req_conf); + + /* Add extensions */ + if (extensions && !X509V3_EXT_add_nconf(req_conf, + &ext_ctx, extensions, + x509ss)) { + BIO_printf(bio_err, "Error Loading extension section %s\n", + extensions); + goto end; + } + + i = do_X509_sign(x509ss, pkey, digest, sigopts); + if (!i) { + ERR_print_errors(bio_err); + goto end; + } + } else { + X509V3_CTX ext_ctx; + + /* Set up V3 context struct */ + + X509V3_set_ctx(&ext_ctx, NULL, NULL, req, NULL, 0); + X509V3_set_nconf(&ext_ctx, req_conf); + + /* Add extensions */ + if (req_exts && !X509V3_EXT_REQ_add_nconf(req_conf, + &ext_ctx, req_exts, + req)) { + BIO_printf(bio_err, "Error Loading extension section %s\n", + req_exts); + goto end; + } + i = do_X509_REQ_sign(req, pkey, digest, sigopts); + if (!i) { + ERR_print_errors(bio_err); + goto end; + } + } + } + + if (subj && x509) { + BIO_printf(bio_err, "Cannot modify certificate subject\n"); + goto end; + } + + if (subj && !x509) { + if (verbose) { + BIO_printf(bio_err, "Modifying Request's Subject\n"); + print_name(bio_err, "old subject=", + X509_REQ_get_subject_name(req), nmflag); + } + + if (build_subject(req, subj, chtype, multirdn) == 0) { + BIO_printf(bio_err, "ERROR: cannot modify subject\n"); + ret = 1; + goto end; + } + + if (verbose) { + print_name(bio_err, "new subject=", + X509_REQ_get_subject_name(req), nmflag); + } + } + + if (verify && !x509) { + EVP_PKEY *tpubkey = pkey; + + if (tpubkey == NULL) { + tpubkey = X509_REQ_get0_pubkey(req); + if (tpubkey == NULL) + goto end; + } + + i = X509_REQ_verify(req, tpubkey); + + if (i < 0) { + goto end; + } else if (i == 0) { + BIO_printf(bio_err, "verify failure\n"); + ERR_print_errors(bio_err); + } else /* if (i > 0) */ + BIO_printf(bio_err, "verify OK\n"); + } + + if (noout && !text && !modulus && !subject && !pubkey) { + ret = 0; + goto end; + } + + out = bio_open_default(outfile, + keyout != NULL && outfile != NULL && + strcmp(keyout, outfile) == 0 ? 'a' : 'w', + outformat); + if (out == NULL) + goto end; + + if (pubkey) { + EVP_PKEY *tpubkey = X509_REQ_get0_pubkey(req); + + if (tpubkey == NULL) { + BIO_printf(bio_err, "Error getting public key\n"); + ERR_print_errors(bio_err); + goto end; + } + PEM_write_bio_PUBKEY(out, tpubkey); + } + + if (text) { + if (x509) + X509_print_ex(out, x509ss, nmflag, reqflag); + else + X509_REQ_print_ex(out, req, nmflag, reqflag); + } + + if (subject) { + if (x509) + print_name(out, "subject=", X509_get_subject_name(x509ss), + nmflag); + else + print_name(out, "subject=", X509_REQ_get_subject_name(req), + nmflag); + } + + if (modulus) { + EVP_PKEY *tpubkey; + + if (x509) + tpubkey = X509_get0_pubkey(x509ss); + else + tpubkey = X509_REQ_get0_pubkey(req); + if (tpubkey == NULL) { + fprintf(stdout, "Modulus=unavailable\n"); + goto end; + } + fprintf(stdout, "Modulus="); +#ifndef OPENSSL_NO_RSA + if (EVP_PKEY_base_id(tpubkey) == EVP_PKEY_RSA) { + const BIGNUM *n; + RSA_get0_key(EVP_PKEY_get0_RSA(tpubkey), &n, NULL, NULL); + BN_print(out, n); + } else +#endif + fprintf(stdout, "Wrong Algorithm type"); + fprintf(stdout, "\n"); + } + + if (!noout && !x509) { + if (outformat == FORMAT_ASN1) + i = i2d_X509_REQ_bio(out, req); + else if (newhdr) + i = PEM_write_bio_X509_REQ_NEW(out, req); + else + i = PEM_write_bio_X509_REQ(out, req); + if (!i) { + BIO_printf(bio_err, "unable to write X509 request\n"); + goto end; + } + } + if (!noout && x509 && (x509ss != NULL)) { + if (outformat == FORMAT_ASN1) + i = i2d_X509_bio(out, x509ss); + else + i = PEM_write_bio_X509(out, x509ss); + if (!i) { + BIO_printf(bio_err, "unable to write X509 certificate\n"); + goto end; + } + } + ret = 0; + end: + if (ret) { + ERR_print_errors(bio_err); + } + NCONF_free(req_conf); + BIO_free(in); + BIO_free_all(out); + EVP_PKEY_free(pkey); + EVP_PKEY_CTX_free(genctx); + sk_OPENSSL_STRING_free(pkeyopts); + sk_OPENSSL_STRING_free(sigopts); +#ifndef OPENSSL_NO_ENGINE + ENGINE_free(gen_eng); +#endif + OPENSSL_free(keyalgstr); + X509_REQ_free(req); + X509_free(x509ss); + ASN1_INTEGER_free(serial); + release_engine(e); + if (passin != nofree_passin) + OPENSSL_free(passin); + if (passout != nofree_passout) + OPENSSL_free(passout); + return (ret); +} + +static int make_REQ(X509_REQ *req, EVP_PKEY *pkey, char *subj, int multirdn, + int attribs, unsigned long chtype) +{ + int ret = 0, i; + char no_prompt = 0; + STACK_OF(CONF_VALUE) *dn_sk, *attr_sk = NULL; + char *tmp, *dn_sect, *attr_sect; + + tmp = NCONF_get_string(req_conf, SECTION, PROMPT); + if (tmp == NULL) + ERR_clear_error(); + if ((tmp != NULL) && strcmp(tmp, "no") == 0) + no_prompt = 1; + +#if !defined(OPENSSL_NO_SKF) || !defined(OPENSSL_NO_SDF) + if (!subj) { +#endif + dn_sect = NCONF_get_string(req_conf, SECTION, DISTINGUISHED_NAME); + if (dn_sect == NULL) { + BIO_printf(bio_err, "unable to find '%s' in config\n", + DISTINGUISHED_NAME); + goto err; + } + dn_sk = NCONF_get_section(req_conf, dn_sect); + if (dn_sk == NULL) { + BIO_printf(bio_err, "unable to get '%s' section\n", dn_sect); + goto err; + } +#if !defined(OPENSSL_NO_SKF) || !defined(OPENSSL_NO_SDF) + } +#endif + + attr_sect = NCONF_get_string(req_conf, SECTION, ATTRIBUTES); + if (attr_sect == NULL) { + ERR_clear_error(); + attr_sk = NULL; + } else { + attr_sk = NCONF_get_section(req_conf, attr_sect); + if (attr_sk == NULL) { + BIO_printf(bio_err, "unable to get '%s' section\n", attr_sect); + goto err; + } + } + + /* setup version number */ + if (!X509_REQ_set_version(req, 0L)) + goto err; /* version 1 */ + + if (subj) + i = build_subject(req, subj, chtype, multirdn); + else if (no_prompt) + i = auto_info(req, dn_sk, attr_sk, attribs, chtype); + else + i = prompt_info(req, dn_sk, dn_sect, attr_sk, attr_sect, attribs, + chtype); + if (!i) + goto err; + + if (!X509_REQ_set_pubkey(req, pkey)) + goto err; + + ret = 1; + err: + return (ret); +} + +/* + * subject is expected to be in the format /type0=value0/type1=value1/type2=... + * where characters may be escaped by \ + */ +static int build_subject(X509_REQ *req, const char *subject, unsigned long chtype, + int multirdn) +{ + X509_NAME *n; + + if ((n = parse_name(subject, chtype, multirdn)) == NULL) + return 0; + + if (!X509_REQ_set_subject_name(req, n)) { + X509_NAME_free(n); + return 0; + } + X509_NAME_free(n); + return 1; +} + +static int prompt_info(X509_REQ *req, + STACK_OF(CONF_VALUE) *dn_sk, const char *dn_sect, + STACK_OF(CONF_VALUE) *attr_sk, const char *attr_sect, + int attribs, unsigned long chtype) +{ + int i; + char *p, *q; + char buf[100]; + int nid, mval; + long n_min, n_max; + char *type, *value; + const char *def; + CONF_VALUE *v; + X509_NAME *subj; + subj = X509_REQ_get_subject_name(req); + + if (!batch) { + BIO_printf(bio_err, + "You are about to be asked to enter information that will be incorporated\n"); + BIO_printf(bio_err, "into your certificate request.\n"); + BIO_printf(bio_err, + "What you are about to enter is what is called a Distinguished Name or a DN.\n"); + BIO_printf(bio_err, + "There are quite a few fields but you can leave some blank\n"); + BIO_printf(bio_err, + "For some fields there will be a default value,\n"); + BIO_printf(bio_err, + "If you enter '.', the field will be left blank.\n"); + BIO_printf(bio_err, "-----\n"); + } + + if (sk_CONF_VALUE_num(dn_sk)) { + i = -1; + start:for (;;) { + i++; + if (sk_CONF_VALUE_num(dn_sk) <= i) + break; + + v = sk_CONF_VALUE_value(dn_sk, i); + p = q = NULL; + type = v->name; + if (!check_end(type, "_min") || !check_end(type, "_max") || + !check_end(type, "_default") || !check_end(type, "_value")) + continue; + /* + * Skip past any leading X. X: X, etc to allow for multiple + * instances + */ + for (p = v->name; *p; p++) + if ((*p == ':') || (*p == ',') || (*p == '.')) { + p++; + if (*p) + type = p; + break; + } + if (*type == '+') { + mval = -1; + type++; + } else + mval = 0; + /* If OBJ not recognised ignore it */ + if ((nid = OBJ_txt2nid(type)) == NID_undef) + goto start; + if (BIO_snprintf(buf, sizeof buf, "%s_default", v->name) + >= (int)sizeof(buf)) { + BIO_printf(bio_err, "Name '%s' too long\n", v->name); + return 0; + } + + if ((def = NCONF_get_string(req_conf, dn_sect, buf)) == NULL) { + ERR_clear_error(); + def = ""; + } + + BIO_snprintf(buf, sizeof buf, "%s_value", v->name); + if ((value = NCONF_get_string(req_conf, dn_sect, buf)) == NULL) { + ERR_clear_error(); + value = NULL; + } + + BIO_snprintf(buf, sizeof buf, "%s_min", v->name); + if (!NCONF_get_number(req_conf, dn_sect, buf, &n_min)) { + ERR_clear_error(); + n_min = -1; + } + + BIO_snprintf(buf, sizeof buf, "%s_max", v->name); + if (!NCONF_get_number(req_conf, dn_sect, buf, &n_max)) { + ERR_clear_error(); + n_max = -1; + } + + if (!add_DN_object(subj, v->value, def, value, nid, + n_min, n_max, chtype, mval)) + return 0; + } + if (X509_NAME_entry_count(subj) == 0) { + BIO_printf(bio_err, + "error, no objects specified in config file\n"); + return 0; + } + + if (attribs) { + if ((attr_sk != NULL) && (sk_CONF_VALUE_num(attr_sk) > 0) + && (!batch)) { + BIO_printf(bio_err, + "\nPlease enter the following 'extra' attributes\n"); + BIO_printf(bio_err, + "to be sent with your certificate request\n"); + } + + i = -1; + start2: for (;;) { + i++; + if ((attr_sk == NULL) || (sk_CONF_VALUE_num(attr_sk) <= i)) + break; + + v = sk_CONF_VALUE_value(attr_sk, i); + type = v->name; + if ((nid = OBJ_txt2nid(type)) == NID_undef) + goto start2; + + if (BIO_snprintf(buf, sizeof buf, "%s_default", type) + >= (int)sizeof(buf)) { + BIO_printf(bio_err, "Name '%s' too long\n", v->name); + return 0; + } + + if ((def = NCONF_get_string(req_conf, attr_sect, buf)) + == NULL) { + ERR_clear_error(); + def = ""; + } + + BIO_snprintf(buf, sizeof buf, "%s_value", type); + if ((value = NCONF_get_string(req_conf, attr_sect, buf)) + == NULL) { + ERR_clear_error(); + value = NULL; + } + + BIO_snprintf(buf, sizeof buf, "%s_min", type); + if (!NCONF_get_number(req_conf, attr_sect, buf, &n_min)) { + ERR_clear_error(); + n_min = -1; + } + + BIO_snprintf(buf, sizeof buf, "%s_max", type); + if (!NCONF_get_number(req_conf, attr_sect, buf, &n_max)) { + ERR_clear_error(); + n_max = -1; + } + + if (!add_attribute_object(req, + v->value, def, value, nid, n_min, + n_max, chtype)) + return 0; + } + } + } else { + BIO_printf(bio_err, "No template, please set one up.\n"); + return 0; + } + + return 1; + +} + +static int auto_info(X509_REQ *req, STACK_OF(CONF_VALUE) *dn_sk, + STACK_OF(CONF_VALUE) *attr_sk, int attribs, + unsigned long chtype) +{ + int i, spec_char, plus_char; + char *p, *q; + char *type; + CONF_VALUE *v; + X509_NAME *subj; + + subj = X509_REQ_get_subject_name(req); + + for (i = 0; i < sk_CONF_VALUE_num(dn_sk); i++) { + int mval; + v = sk_CONF_VALUE_value(dn_sk, i); + p = q = NULL; + type = v->name; + /* + * Skip past any leading X. X: X, etc to allow for multiple instances + */ + for (p = v->name; *p; p++) { +#ifndef CHARSET_EBCDIC + spec_char = ((*p == ':') || (*p == ',') || (*p == '.')); +#else + spec_char = ((*p == os_toascii[':']) || (*p == os_toascii[',']) + || (*p == os_toascii['.'])); +#endif + if (spec_char) { + p++; + if (*p) + type = p; + break; + } + } +#ifndef CHARSET_EBCDIC + plus_char = (*type == '+'); +#else + plus_char = (*type == os_toascii['+']); +#endif + if (plus_char) { + type++; + mval = -1; + } else + mval = 0; + if (!X509_NAME_add_entry_by_txt(subj, type, chtype, + (unsigned char *)v->value, -1, -1, + mval)) + return 0; + + } + + if (!X509_NAME_entry_count(subj)) { + BIO_printf(bio_err, "error, no objects specified in config file\n"); + return 0; + } + if (attribs) { + for (i = 0; i < sk_CONF_VALUE_num(attr_sk); i++) { + v = sk_CONF_VALUE_value(attr_sk, i); + if (!X509_REQ_add1_attr_by_txt(req, v->name, chtype, + (unsigned char *)v->value, -1)) + return 0; + } + } + return 1; +} + +static int add_DN_object(X509_NAME *n, char *text, const char *def, + char *value, int nid, int n_min, int n_max, + unsigned long chtype, int mval) +{ + int i, ret = 0; + char buf[1024]; + start: + if (!batch) + BIO_printf(bio_err, "%s [%s]:", text, def); + (void)BIO_flush(bio_err); + if (value != NULL) { + OPENSSL_strlcpy(buf, value, sizeof buf); + OPENSSL_strlcat(buf, "\n", sizeof buf); + BIO_printf(bio_err, "%s\n", value); + } else { + buf[0] = '\0'; + if (!batch) { + if (!fgets(buf, sizeof buf, stdin)) + return 0; + } else { + buf[0] = '\n'; + buf[1] = '\0'; + } + } + + if (buf[0] == '\0') + return (0); + else if (buf[0] == '\n') { + if ((def == NULL) || (def[0] == '\0')) + return (1); + OPENSSL_strlcpy(buf, def, sizeof buf); + OPENSSL_strlcat(buf, "\n", sizeof buf); + } else if ((buf[0] == '.') && (buf[1] == '\n')) + return (1); + + i = strlen(buf); + if (buf[i - 1] != '\n') { + BIO_printf(bio_err, "weird input :-(\n"); + return (0); + } + buf[--i] = '\0'; +#ifdef CHARSET_EBCDIC + ebcdic2ascii(buf, buf, i); +#endif + if (!req_check_len(i, n_min, n_max)) { + if (batch || value) + return 0; + goto start; + } + + if (!X509_NAME_add_entry_by_NID(n, nid, chtype, + (unsigned char *)buf, -1, -1, mval)) + goto err; + ret = 1; + err: + return (ret); +} + +static int add_attribute_object(X509_REQ *req, char *text, const char *def, + char *value, int nid, int n_min, + int n_max, unsigned long chtype) +{ + int i; + static char buf[1024]; + + start: + if (!batch) + BIO_printf(bio_err, "%s [%s]:", text, def); + (void)BIO_flush(bio_err); + if (value != NULL) { + OPENSSL_strlcpy(buf, value, sizeof buf); + OPENSSL_strlcat(buf, "\n", sizeof buf); + BIO_printf(bio_err, "%s\n", value); + } else { + buf[0] = '\0'; + if (!batch) { + if (!fgets(buf, sizeof buf, stdin)) + return 0; + } else { + buf[0] = '\n'; + buf[1] = '\0'; + } + } + + if (buf[0] == '\0') + return (0); + else if (buf[0] == '\n') { + if ((def == NULL) || (def[0] == '\0')) + return (1); + OPENSSL_strlcpy(buf, def, sizeof buf); + OPENSSL_strlcat(buf, "\n", sizeof buf); + } else if ((buf[0] == '.') && (buf[1] == '\n')) + return (1); + + i = strlen(buf); + if (buf[i - 1] != '\n') { + BIO_printf(bio_err, "weird input :-(\n"); + return (0); + } + buf[--i] = '\0'; +#ifdef CHARSET_EBCDIC + ebcdic2ascii(buf, buf, i); +#endif + if (!req_check_len(i, n_min, n_max)) { + if (batch || value) + return 0; + goto start; + } + + if (!X509_REQ_add1_attr_by_NID(req, nid, chtype, + (unsigned char *)buf, -1)) { + BIO_printf(bio_err, "Error adding attribute\n"); + ERR_print_errors(bio_err); + goto err; + } + + return (1); + err: + return (0); +} + +static int req_check_len(int len, int n_min, int n_max) +{ + if ((n_min > 0) && (len < n_min)) { + BIO_printf(bio_err, + "string is too short, it needs to be at least %d bytes long\n", + n_min); + return (0); + } + if ((n_max >= 0) && (len > n_max)) { + BIO_printf(bio_err, + "string is too long, it needs to be less than %d bytes long\n", + n_max); + return (0); + } + return (1); +} + +/* Check if the end of a string matches 'end' */ +static int check_end(const char *str, const char *end) +{ + int elen, slen; + const char *tmp; + elen = strlen(end); + slen = strlen(str); + if (elen > slen) + return 1; + tmp = str + slen - elen; + return strcmp(tmp, end); +} + +static EVP_PKEY_CTX *set_keygen_ctx(const char *gstr, + int *pkey_type, long *pkeylen, + char **palgnam, ENGINE *keygen_engine) +{ + EVP_PKEY_CTX *gctx = NULL; + EVP_PKEY *param = NULL; + long keylen = -1; + BIO *pbio = NULL; + const char *paramfile = NULL; + + if (gstr == NULL) { + *pkey_type = EVP_PKEY_RSA; + keylen = *pkeylen; + } else if (gstr[0] >= '0' && gstr[0] <= '9') { + *pkey_type = EVP_PKEY_RSA; + keylen = atol(gstr); + *pkeylen = keylen; + } else if (strncmp(gstr, "param:", 6) == 0) + paramfile = gstr + 6; + else { + const char *p = strchr(gstr, ':'); + int len; + ENGINE *tmpeng; + const EVP_PKEY_ASN1_METHOD *ameth; + + if (p) + len = p - gstr; + else + len = strlen(gstr); + /* + * The lookup of a the string will cover all engines so keep a note + * of the implementation. + */ + + ameth = EVP_PKEY_asn1_find_str(&tmpeng, gstr, len); + + if (!ameth) { + BIO_printf(bio_err, "Unknown algorithm %.*s\n", len, gstr); + return NULL; + } + + EVP_PKEY_asn1_get0_info(NULL, pkey_type, NULL, NULL, NULL, ameth); +#ifndef OPENSSL_NO_ENGINE + ENGINE_finish(tmpeng); +#endif + if (*pkey_type == EVP_PKEY_RSA) { + if (p) { + keylen = atol(p + 1); + *pkeylen = keylen; + } else + keylen = *pkeylen; + } else if (p) + paramfile = p + 1; + } + + if (paramfile) { + pbio = BIO_new_file(paramfile, "r"); + if (!pbio) { + BIO_printf(bio_err, "Can't open parameter file %s\n", paramfile); + return NULL; + } + param = PEM_read_bio_Parameters(pbio, NULL); + + if (!param) { + X509 *x; + (void)BIO_reset(pbio); + x = PEM_read_bio_X509(pbio, NULL, NULL, NULL); + if (x) { + param = X509_get_pubkey(x); + X509_free(x); + } + } + + BIO_free(pbio); + + if (!param) { + BIO_printf(bio_err, "Error reading parameter file %s\n", paramfile); + return NULL; + } + if (*pkey_type == -1) + *pkey_type = EVP_PKEY_id(param); + else if (*pkey_type != EVP_PKEY_base_id(param)) { + BIO_printf(bio_err, "Key Type does not match parameters\n"); + EVP_PKEY_free(param); + return NULL; + } + } + + if (palgnam) { + const EVP_PKEY_ASN1_METHOD *ameth; + ENGINE *tmpeng; + const char *anam; + ameth = EVP_PKEY_asn1_find(&tmpeng, *pkey_type); + if (!ameth) { + BIO_puts(bio_err, "Internal error: can't find key algorithm\n"); + return NULL; + } + EVP_PKEY_asn1_get0_info(NULL, NULL, NULL, NULL, &anam, ameth); + *palgnam = OPENSSL_strdup(anam); +#ifndef OPENSSL_NO_ENGINE + ENGINE_finish(tmpeng); +#endif + } + + if (param) { + gctx = EVP_PKEY_CTX_new(param, keygen_engine); + *pkeylen = EVP_PKEY_bits(param); + EVP_PKEY_free(param); + } else + gctx = EVP_PKEY_CTX_new_id(*pkey_type, keygen_engine); + + if (gctx == NULL) { + BIO_puts(bio_err, "Error allocating keygen context\n"); + ERR_print_errors(bio_err); + return NULL; + } + + if (EVP_PKEY_keygen_init(gctx) <= 0) { + BIO_puts(bio_err, "Error initializing keygen context\n"); + ERR_print_errors(bio_err); + EVP_PKEY_CTX_free(gctx); + return NULL; + } +#ifndef OPENSSL_NO_RSA + if ((*pkey_type == EVP_PKEY_RSA) && (keylen != -1)) { + if (EVP_PKEY_CTX_set_rsa_keygen_bits(gctx, keylen) <= 0) { + BIO_puts(bio_err, "Error setting RSA keysize\n"); + ERR_print_errors(bio_err); + EVP_PKEY_CTX_free(gctx); + return NULL; + } + } +#endif + + return gctx; +} + +static int genpkey_cb(EVP_PKEY_CTX *ctx) +{ + char c = '*'; + BIO *b = EVP_PKEY_CTX_get_app_data(ctx); + int p; + p = EVP_PKEY_CTX_get_keygen_info(ctx, 0); + if (p == 0) + c = '.'; + if (p == 1) + c = '+'; + if (p == 2) + c = '*'; + if (p == 3) + c = '\n'; + BIO_write(b, &c, 1); + (void)BIO_flush(b); + return 1; +} + +static int do_sign_init(EVP_MD_CTX *ctx, EVP_PKEY *pkey, + const EVP_MD *md, STACK_OF(OPENSSL_STRING) *sigopts) +{ + EVP_PKEY_CTX *pkctx = NULL; + int i; + + if (ctx == NULL) + return 0; + if (!EVP_DigestSignInit(ctx, &pkctx, md, NULL, pkey)) + return 0; + for (i = 0; i < sk_OPENSSL_STRING_num(sigopts); i++) { + char *sigopt = sk_OPENSSL_STRING_value(sigopts, i); + if (pkey_ctrl_string(pkctx, sigopt) <= 0) { + BIO_printf(bio_err, "parameter error \"%s\"\n", sigopt); + ERR_print_errors(bio_err); + return 0; + } + } + return 1; +} + +int do_X509_sign(X509 *x, EVP_PKEY *pkey, const EVP_MD *md, + STACK_OF(OPENSSL_STRING) *sigopts) +{ + int rv; + EVP_MD_CTX *mctx = EVP_MD_CTX_new(); + + rv = do_sign_init(mctx, pkey, md, sigopts); + if (rv > 0) + rv = X509_sign_ctx(x, mctx); + EVP_MD_CTX_free(mctx); + return rv > 0 ? 1 : 0; +} + +int do_X509_REQ_sign(X509_REQ *x, EVP_PKEY *pkey, const EVP_MD *md, + STACK_OF(OPENSSL_STRING) *sigopts) +{ + int rv; + EVP_MD_CTX *mctx = EVP_MD_CTX_new(); + rv = do_sign_init(mctx, pkey, md, sigopts); + if (rv > 0) + rv = X509_REQ_sign_ctx(x, mctx); + EVP_MD_CTX_free(mctx); + return rv > 0 ? 1 : 0; +} + +int do_X509_CRL_sign(X509_CRL *x, EVP_PKEY *pkey, const EVP_MD *md, + STACK_OF(OPENSSL_STRING) *sigopts) +{ + int rv; + EVP_MD_CTX *mctx = EVP_MD_CTX_new(); + rv = do_sign_init(mctx, pkey, md, sigopts); + if (rv > 0) + rv = X509_CRL_sign_ctx(x, mctx); + EVP_MD_CTX_free(mctx); + return rv > 0 ? 1 : 0; +} diff --git a/apps/req.pem b/apps/req.pem new file mode 100644 index 000000000..5537df601 --- /dev/null +++ b/apps/req.pem @@ -0,0 +1,11 @@ +-----BEGIN CERTIFICATE REQUEST----- +MIIBlzCCAVcCAQAwXjELMAkGA1UEBhMCQVUxEzARBgNVBAgTClNvbWUtU3RhdGUx +ITAfBgNVBAoTGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEXMBUGA1UEAxMORXJp +YyB0aGUgWW91bmcwge8wgaYGBSsOAwIMMIGcAkEA+ZiKEvZmc9MtnaFZh4NiZ3oZ +S4J1PHvPrm9MXj5ntVheDPkdmBDTncyaGAJcMjwsyB/GvLDGd6yGCw/8eF+09wIV +AK3VagOxGd/Q4Af5NbxR5FB7CXEjAkA2t/q7HgVLi0KeKvcDG8BRl3wuy7bCvpjg +tWiJc/tpvcuzeuAayH89UofjAGueKjXDADiRffvSdhrNw5dkqdqlA0QAAkEAtUSo +84OekjitKGVjxLu0HvXck29pu+foad53vPKXAsuJdACj88BPqZ91Y9PIJf1GUh38 +CuiHWi7z3cEDfZCyCKAAMAkGBSsOAwIbBQADLwAwLAIUTg8amKVBE9oqC5B75dDQ +Chy3LdQCFHKodGEj3LjuTzdm/RTe2KZL9Uzf +-----END CERTIFICATE REQUEST----- diff --git a/apps/rsa.c b/apps/rsa.c new file mode 100644 index 000000000..018f977d2 --- /dev/null +++ b/apps/rsa.c @@ -0,0 +1,325 @@ +/* + * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#ifdef OPENSSL_NO_RSA +NON_EMPTY_TRANSLATION_UNIT +#else + +# include +# include +# include +# include +# include "apps.h" +# include +# include +# include +# include +# include +# include +# include + +typedef enum OPTION_choice { + OPT_ERR = -1, OPT_EOF = 0, OPT_HELP, + OPT_INFORM, OPT_OUTFORM, OPT_ENGINE, OPT_IN, OPT_OUT, + OPT_PUBIN, OPT_PUBOUT, OPT_PASSOUT, OPT_PASSIN, + OPT_RSAPUBKEY_IN, OPT_RSAPUBKEY_OUT, + /* Do not change the order here; see case statements below */ + OPT_PVK_NONE, OPT_PVK_WEAK, OPT_PVK_STRONG, + OPT_NOOUT, OPT_TEXT, OPT_MODULUS, OPT_CHECK, OPT_CIPHER, + OPT_CONFIG +} OPTION_CHOICE; + +OPTIONS rsa_options[] = { + {"help", OPT_HELP, '-', "Display this summary"}, + {"inform", OPT_INFORM, 'f', "Input format, one of DER NET PEM"}, + {"outform", OPT_OUTFORM, 'f', "Output format, one of DER NET PEM PVK"}, + {"in", OPT_IN, 's', "Input file"}, + {"out", OPT_OUT, '>', "Output file"}, + {"pubin", OPT_PUBIN, '-', "Expect a public key in input file"}, + {"pubout", OPT_PUBOUT, '-', "Output a public key"}, + {"passout", OPT_PASSOUT, 's', "Output file pass phrase source"}, + {"passin", OPT_PASSIN, 's', "Input file pass phrase source"}, + {"RSAPublicKey_in", OPT_RSAPUBKEY_IN, '-', "Input is an RSAPublicKey"}, + {"RSAPublicKey_out", OPT_RSAPUBKEY_OUT, '-', "Output is an RSAPublicKey"}, + {"noout", OPT_NOOUT, '-', "Don't print key out"}, + {"text", OPT_TEXT, '-', "Print the key in text"}, + {"modulus", OPT_MODULUS, '-', "Print the RSA key modulus"}, + {"check", OPT_CHECK, '-', "Verify key consistency"}, + {"", OPT_CIPHER, '-', "Any supported cipher"}, +# if !defined(OPENSSL_NO_DSA) && !defined(OPENSSL_NO_RC4) + {"pvk-strong", OPT_PVK_STRONG, '-', "Enable 'Strong' PVK encoding level (default)"}, + {"pvk-weak", OPT_PVK_WEAK, '-', "Enable 'Weak' PVK encoding level"}, + {"pvk-none", OPT_PVK_NONE, '-', "Don't enforce PVK encoding"}, +# endif +# ifndef OPENSSL_NO_ENGINE + {"engine", OPT_ENGINE, 's', "Use engine, possibly a hardware device"}, + {"config", OPT_CONFIG, 's', "A config file"}, +# endif + {NULL} +}; + +int rsa_main(int argc, char **argv) +{ + ENGINE *e = NULL; + BIO *out = NULL; + RSA *rsa = NULL; + const EVP_CIPHER *enc = NULL; + char *infile = NULL, *outfile = NULL, *prog; + char *passin = NULL, *passout = NULL, *passinarg = NULL, *passoutarg = NULL; + int i, private = 0; + int informat = FORMAT_PEM, outformat = FORMAT_PEM, text = 0, check = 0; + int noout = 0, modulus = 0, pubin = 0, pubout = 0, ret = 1; +# if !defined(OPENSSL_NO_DSA) && !defined(OPENSSL_NO_RC4) + int pvk_encr = 2; +# endif + OPTION_CHOICE o; + CONF *conf = NULL; + char *configfile = default_config_file; + + prog = opt_init(argc, argv, rsa_options); + while ((o = opt_next()) != OPT_EOF) { + switch (o) { + case OPT_EOF: + case OPT_ERR: + opthelp: + BIO_printf(bio_err, "%s: Use -help for summary.\n", prog); + goto end; + case OPT_HELP: + opt_help(rsa_options); + ret = 0; + goto end; + case OPT_INFORM: + if (!opt_format(opt_arg(), OPT_FMT_ANY, &informat)) + goto opthelp; + break; + case OPT_IN: + infile = opt_arg(); + break; + case OPT_OUTFORM: + if (!opt_format(opt_arg(), OPT_FMT_ANY, &outformat)) + goto opthelp; + break; + case OPT_OUT: + outfile = opt_arg(); + break; + case OPT_PASSIN: + passinarg = opt_arg(); + break; + case OPT_PASSOUT: + passoutarg = opt_arg(); + break; + case OPT_ENGINE: + e = setup_engine(opt_arg(), 0); + break; + case OPT_PUBIN: + pubin = 1; + break; + case OPT_PUBOUT: + pubout = 1; + break; + case OPT_RSAPUBKEY_IN: + pubin = 2; + break; + case OPT_RSAPUBKEY_OUT: + pubout = 2; + break; + case OPT_PVK_STRONG: /* pvk_encr:= 2 */ + case OPT_PVK_WEAK: /* pvk_encr:= 1 */ + case OPT_PVK_NONE: /* pvk_encr:= 0 */ +# if !defined(OPENSSL_NO_DSA) && !defined(OPENSSL_NO_RC4) + pvk_encr = (o - OPT_PVK_NONE); +# endif + break; + case OPT_NOOUT: + noout = 1; + break; + case OPT_TEXT: + text = 1; + break; + case OPT_MODULUS: + modulus = 1; + break; + case OPT_CHECK: + check = 1; + break; + case OPT_CIPHER: + if (!opt_cipher(opt_unknown(), &enc)) + goto opthelp; + break; + case OPT_CONFIG: + configfile = opt_arg(); + break; + } + } + argc = opt_num_rest(); + if (argc != 0) + goto opthelp; + + BIO_printf(bio_err, "Using configuration from %s\n", configfile); + + if ((conf = app_load_config(configfile)) == NULL) + goto end; + if (configfile != default_config_file && !app_load_modules(conf)) + goto end; + + private = (text && !pubin) || (!pubout && !noout) ? 1 : 0; + + if (!app_passwd(passinarg, passoutarg, &passin, &passout)) { + BIO_printf(bio_err, "Error getting passwords\n"); + goto end; + } + if (check && pubin) { + BIO_printf(bio_err, "Only private keys can be checked\n"); + goto end; + } + + { + EVP_PKEY *pkey; + + if (pubin) { + int tmpformat = -1; + if (pubin == 2) { + if (informat == FORMAT_PEM) + tmpformat = FORMAT_PEMRSA; + else if (informat == FORMAT_ASN1) + tmpformat = FORMAT_ASN1RSA; + } else + tmpformat = informat; + + pkey = load_pubkey(infile, tmpformat, 1, passin, e, "Public Key"); + } else + pkey = load_key(infile, informat, 1, passin, e, "Private Key"); + + if (pkey != NULL) + rsa = EVP_PKEY_get1_RSA(pkey); + EVP_PKEY_free(pkey); + } + + if (rsa == NULL) { + ERR_print_errors(bio_err); + goto end; + } + + out = bio_open_owner(outfile, outformat, private); + if (out == NULL) + goto end; + + if (text) { + assert(pubin || private); + if (!RSA_print(out, rsa, 0)) { + perror(outfile); + ERR_print_errors(bio_err); + goto end; + } + } + + if (modulus) { + const BIGNUM *n; + RSA_get0_key(rsa, &n, NULL, NULL); + BIO_printf(out, "Modulus="); + BN_print(out, n); + BIO_printf(out, "\n"); + } + + if (check) { + int r = RSA_check_key(rsa); + + if (r == 1) + BIO_printf(out, "RSA key ok\n"); + else if (r == 0) { + unsigned long err; + + while ((err = ERR_peek_error()) != 0 && + ERR_GET_LIB(err) == ERR_LIB_RSA && + ERR_GET_FUNC(err) == RSA_F_RSA_CHECK_KEY && + ERR_GET_REASON(err) != ERR_R_MALLOC_FAILURE) { + BIO_printf(out, "RSA key error: %s\n", + ERR_reason_error_string(err)); + ERR_get_error(); /* remove e from error stack */ + } + } else if (r == -1) { + ERR_print_errors(bio_err); + goto end; + } + } + + if (noout) { + ret = 0; + goto end; + } + BIO_printf(bio_err, "writing RSA key\n"); + if (outformat == FORMAT_ASN1) { + if (pubout || pubin) { + if (pubout == 2) + i = i2d_RSAPublicKey_bio(out, rsa); + else + i = i2d_RSA_PUBKEY_bio(out, rsa); + } else { + assert(private); + i = i2d_RSAPrivateKey_bio(out, rsa); + } + } + else if (outformat == FORMAT_PEM) { + if (pubout || pubin) { + if (pubout == 2) + i = PEM_write_bio_RSAPublicKey(out, rsa); + else + i = PEM_write_bio_RSA_PUBKEY(out, rsa); + } else { + assert(private); + i = PEM_write_bio_RSAPrivateKey(out, rsa, + enc, NULL, 0, NULL, passout); + } +# ifndef OPENSSL_NO_DSA + } else if (outformat == FORMAT_MSBLOB || outformat == FORMAT_PVK) { + EVP_PKEY *pk; + pk = EVP_PKEY_new(); + EVP_PKEY_set1_RSA(pk, rsa); + if (outformat == FORMAT_PVK) { + if (pubin) { + BIO_printf(bio_err, "PVK form impossible with public key input\n"); + EVP_PKEY_free(pk); + goto end; + } + assert(private); +# ifdef OPENSSL_NO_RC4 + BIO_printf(bio_err, "PVK format not supported\n"); + EVP_PKEY_free(pk); + goto end; +# else + i = i2b_PVK_bio(out, pk, pvk_encr, 0, passout); +# endif + } else if (pubin || pubout) { + i = i2b_PublicKey_bio(out, pk); + } else { + assert(private); + i = i2b_PrivateKey_bio(out, pk); + } + EVP_PKEY_free(pk); +# endif + } else { + BIO_printf(bio_err, "bad output format specified for outfile\n"); + goto end; + } + if (i <= 0) { + BIO_printf(bio_err, "unable to write key\n"); + ERR_print_errors(bio_err); + } else + ret = 0; + end: + release_engine(e); + BIO_free_all(out); + RSA_free(rsa); + OPENSSL_free(passin); + OPENSSL_free(passout); + NCONF_free(conf); + return (ret); +} +#endif diff --git a/apps/rsa8192.pem b/apps/rsa8192.pem new file mode 100644 index 000000000..946a6e543 --- /dev/null +++ b/apps/rsa8192.pem @@ -0,0 +1,101 @@ +-----BEGIN RSA PRIVATE KEY----- + +MIISKAIBAAKCBAEAiQ2f1X6Bte1DKD0OoCBKEikzPW+5w3oXk3WwnE97Wxzy6wJZ +ebbZC3CZKKBnJeBMrysPf+lK+9+fP6Vm8bp1wvbcSIA59BDrX6irFSuM/bdnkbuF +MFlDjt+uVrxwoyqfPi2IPot1HQg3l5mdyBqcTWvbOnU2L9HZxJfPUCjfzdTMPrMY +55/A20XL7tlV2opEfwhy3uVlveQBM0DnZ3MUQfrk+lRRNWv7yE4ScbOfER9fjvOm +yJc3ZbOa3e+AMGGU9OqJ/fyOl0SGYyP2k23omy/idBV4uOs8QWdnAvq8UOzDdua3 +tuf5Tn17XBurPJ8juwyPBNispkwwn8BjxAZVPhwUIcxFBg339IxJ9cW0WdVy4nNA +LWo/8Ahlf+kZNnFNGCPFytU9gGMLMhab9w/rLrwa9qNe4L8Fmu1JxONn1WfhMOKE +aFmycf2olJsYLgUIGYZrjnYu0p/7P3yhTOv8JIhmK+SzmA/I0xiQoF84rpaQzH2d +PvxICOA9oQSowou0gLuBSZWm6LiXirg1DZCziU46v33ErQlWM1dSyNaUSzihcV59 +mVD0nmzboXH75lGiyiZlp8cLbozzoCwvk9rYqpUGSBzbAy0ECCpabGpzO2Ug+oDi +71e5z4WMpeoR4IS8MaOG/GsJnwaXhiB/gNYfK+8pRADVk5StEAZDE2alSuCbDs0z +d9zYr4/em5T9VZsLetxRE7pm/Es9yELuViz8/Tm0/8MVdmNYc/xZU1t6qYYFdyQ2 +wlGDTiNPsjR8yXCkmBjKwqnuleu1X6LaZu3VPhEkXGcyFAquQUkSiMv0Yu74qAe0 +bQ2v+jjZzP6AM9LUo89cW4Kd8SGD96BdNlAVPNMXoBcIOsZBwsOtETBd4KAyvkXE +Ob17u+PLl4UPnSxm9ypKZunUNFRPxtKUyjySYnvlGL+kTjAXrIrZwKJqIn0uhnfa +Ck3o7bU6yVMK22ODxy2/Vi3E0P6k5JLwnrF0VIOBqGhts66qo6mWDP8l6MZHARFd +pU+nofssVmr8tLKmMmjYGMM5GmKIXRNBs0ksTwFnKRs9AmpE5owC8tTSVdTAkGuS +os7QwLvyvNzq7BGJiVr0Iy3Dhsl1vzR35acNOrCsDl3DcCQONKJ2sVXV4pD3dBah +mG3sR/jHgjasffJJ35uiGoAua9dbT7HG/+D0z1SHYaVqH8zO4VZSOnGJh/P9rtxx +cckFDbiag/JMWig2lbnCjebTtp/BcUsK3TNaDOb7vb0LvbAeRJadd1EFu6PSlH3K +LykSUPm4UedvUU3cWjqkSY5lITFJkVaIYOv/EljYtK7p7kFZFTaEwMAWxgsXU3pQ +tTzVmq1gZ4vXPwcUq0zK50Frq0F7SQc21ZsunwIDAQABAoIEADuQAkDEpBausJsS +PgL1RXuzECPJJJCBxTE+2qx0FoY4hJICCWTORHGmU8nGPE3Ht0wBiNDsULw6KXl9 +psmzYW6D3qRbpdQebky6fu/KZ5H0XTyGpJGomaXELH5hkwo2gdKB805LSXB+m7p0 +9o96kSdMkpBLVGtf5iZ8W4rY2LsZmlI9f7taQHSLVt/M8HTz1mTnBRU92QO3zZW6 +xVa+OrWaFl18u3ZeIaSh2X40tBK68cqstXVD0r2OWuXNKobcQeJW8/XABzBShZ0c +ihL0lzyqiN4uXrLu+Nbr22b+FU2OODy6dGk3U6/69NvI4piMCPlHsfhHOnFjd1ZW +RIVywyUlCtLNdcn11CchuRro+0J3c2Ba+i9Cl9r3qzT11xFEGF8/XLyUBBCB+uGf +1dR/xJQhCA7cXWWLXyI/semxcvTaGpImP6kiIl1MAjHjXZTSdvyw4JmfXyYGhSjI +P0mw3Xn7FXxJ/os9gOfNKz2nZHjr0q4sgWRYO+4vllkeL0GteZrg4oVaVpmZb7LH +77afhodLylhijlEtV5skfkPujbBLQk6E5Ez3U/huEt2NLg6guADmwxMxfBRliZO4 +4Ex/td4cuggpEj3FGJV74qRvdvj/MF/uF7IxC/3WapPIsFBFH4zrJsUYt6u3L68I +/KC/bfioDeUR/8ANw1DNh+UsnPV3GJIwDkIJKdppi2uXPahJyJQQ8Inps53nn8Gg +GifS+HnOXNgMoKOJnZ9IDGjXpfjIs8dJNrGfDHF0mH30N2WARq2v/a3cNUC+f8Bq +HSKQ9YrZopktMunsut8u7ZYbTmjIqJpXCaM0CCrSlzSMTDHFSj2tzLk6+qnxeGxB +ZwIdShbdeK+0ETG91lE1e9RPQs/uXQP9+uCHJV0YpqQcA6pkCLYJfYpoSMu/Bafy +AgfVZz6l5tyEnV0wCcbopsQShc1k9xtTbYNF1h9AQHknj6zeDW4iZMvmVeh3RovT +52OA2R8oLyauF+QaG6x2wUjEx13SJlaBarJZ4seZIOJ+a8+oNzKsbgokXc2cyC9p +5FAZz1OsOb68o93qD1Xvl7bY97fq2q55L7G1XHPPLtZE5lGiLGDtnAuwY8UPrdpr +7Mv2yIxB7xVGurXyHb5PvusR88XED6HMPfLBG/55ENHTal7G5mRix+IWSBAIkxA5 +KZ0j8r5Ng4+wELZhqFQai39799bIAyiV6CEz4kyDXlo0kSSexp8o4iz5sPq5vp6h +cCb7rdRw7uRnbXrHmXahxoB+ibXaurgV/6B2yurrU/UFoxEp2sHp8LXZGfF6ztY1 +dMhSQAACK2vGy5yNagbkTHLgVaHicG5zavJBqzCE+lbPlCqhOUQPdOIwvjHNjdS/ +DL3WV/ECggIBAMbW65wPk/i43nSyeZeYwcHtR1SUJqDXavYfBPC0VRhKz+7DVMFw +Nwnocn6gITABc445W1yl7U3uww+LGuDlSlFnd8WuiXpVYud9/jeNu6Mu4wvNsnWr +f4f4ua8CcS03GmqmcbROD2Z6by1AblCZ2UL1kv9cUX1FLVjPP1ESAGKoePt3BmZQ +J1uJfK8HilNT8dcUlj/5CBi2uHxttDhoG0sxXE/SVsG9OD/Pjme0mj7gdzc6Ztd+ +TALuvpNQR4pRzfo5XWDZBcEYntcEE3PxYJB1+vnZ8509ew5/yLHTbLjFxIcx71zY +fhH0gM36Sz7mz37r0+E/QkRkc5bVIDC4LDnWmjpAde6QUx0d218ShNx6sJo4kt5c +Dd7tEVx8nuX8AIZYgwsOb382anLyFRkkmEdK3gRvwQ6SWR36Ez5L7/mHWODpLAX5 +mVBKSG4/ccFbc633/g0xHw0Nwajir/klckdakuYPlwF0yAxJSKDLhmNctDhRmxjC +YP+fISkl5oTvFRzJH6HEyNu8M3ybRvmpPIjM5J5JpnB2IYbohYBR+T6/97C1DKrd +mzL5PjlrWm0c1/d7LlDoP65fOShDMmj2zCiBAHHOM0Alokx+v5LmMd8NJumZIwGJ +Rt5OpeMOhowz6j1AjYxYgV7PmJL6Ovpfb775od/aLaUbbwHz2uWIvfF7AoICAQCw +c7NaO7oJVLJClhYw6OCvjT6oqtgNVWaennnDiJgzY9lv5HEgV0MAG0eYuB3hvj+w +Y1P9DJxP1D+R+cshYrAFg8yU/3kaYVNI0Bl3ygX0eW1b/0HZTdocs+8kM/9PZQDR +WrKQoU5lHvqRt99dXlD4NWGI2YQtzdZ8iet9QLqnjwRZabgE96mF01qKisMnFcsh +KjT7ieheU4J15TZj/mdZRNK126d7e3q/rNj73e5EJ9tkYLcolSr4gpknUMJULSEi +JH1/Qx7C/mTAMRsN5SkOthnGq0djCNWfPv/3JV0H67Uf5krFlnwLebrgfTYoPPdo +yO7iBUNJzv6Qh22malLp4P8gzACkD7DGlSTnoB5cLwcjmDGg+i9WrUBbOiVTeQfZ +kOj1o+Tz35ndpq/DDUVlqliB9krcxva+QHeJPH53EGI+YVg1nD+s/vUDZ3mQMGX9 +DQou2L8uU6RnWNv/BihGcL8QvS4Ty6QyPOUPpD3zc70JQAEcQk9BxQNaELgJX0IN +22cYn22tYvElew9G41OpDqzBRcfbdJmKXQ2HcroShutYJQRGUpAXHk24fy6JVkIU +ojF5U6cwextMja1ZIIZgh9eugIRUeIE7319nQNDzuXWjRCcoBLA25P7wnpHWDRpz +D9ovXCIvdja74lL5psqobV6L5+fbLPkSgXoImKR0LQKCAgAIC9Jk8kxumCyIVGCP +PeM5Uby9M3GMuKrfYsn0Y5e97+kSJF1dpojTodBgR2KQar6eVrvXt+8uZCcIjfx8 +dUrYmHNEUJfHl4T1ESgkX1vkcpVFeQFruZDjk7EP3+1sgvpSroGTZkVBRFsTXbQZ +FuCv0Pgt1TKG+zGmklxhj3TsiRy8MEjWAxBUp++ftZJnZNI4feDGnfEx7tLwVhAg +6DWSiWDO6hgQpvOLwX5lu+0x9itc1MQsnDO/OqIDnBAJDN5k7cVVkfKlqbVjxgpz +eqUJs3yAd81f44kDQTCB4ahYocgeIGsrOqd/WoGL1EEPPo/O9wQP7VtlIRt8UwuG +bS18+a4sBUfAa56xYu/pnPo7YcubsgZfcSIujzFQqMpVTClJRnOnEuJ4J1+PXzRz +XAO9fs4VJ+CMEmgAyonUz4Xadxulnknlw//sO9VKgM69oFHCDHL/XamAAbqAdwvf +7R/+uy+Ol7romC0wMhb6SsIZazrvvH2mNtduAKZ638nAP1x/WbQp+6iVG7yJok7w +82Q7tO7baOePTXh12Rrt4mNPor0HLYxhra4GFgfqkumJ2Mz0esuZAozxJXFOq8ly +beo9CVtXP5zbT6qNpeNismX6PLICaev8t+1iOZSE56WSLtefuuj/cOVrTMNDz1Rr +pUkEVV2zjUSjlcScM538A9iL2QKCAgBLbBk0r6T0ihRsK9UucMxhnYEz/Vq+UEu9 +70Vi1AciqEJv9nh4d3Q3HnH7EHANZxG4Jqzm1DYYVUQa9GfkTFeq88xFv/GW2hUM +YY8RSfRDrIeXNEOETCe37x2AHw25dRXlZtw+wARPau91y9+Y/FCl18NqCHfcUEin +ERjsf/eI2bPlODAlR2tZvZ7M60VBdqpN8cmV3zvI3e88z43xLfQlDyr1+v7a5Evy +lEJnXlSTI2o+vKxtl103vjMSwA1gh63K90gBVsJWXQDZueOzi8mB9UqNRfcMmOEe +4YHttTXPxeu0x+4cCRfam9zKShsVFgI28vRQ/ijl6qmbQ5gV8wqf18GV1j1L4z0P +lP6iVynDA4MMrug/w9DqPsHsfK0pwekeETfSj4y0xVXyjWZBfHG2ZBrS6mDTf+RG +LC4sJgR0hjdILLnUqIX7PzuhieBHRrjBcopwvcryVWRHnI7kslAS0+yHjiWc5oW3 +x5mtlum4HzelNYuD9cAE/95P6CeSMfp9CyIE/KSX4VvsRm6gQVkoQRKMxnQIFQ3w +O5gl1l88vhjoo2HxYScgCp70BsDwiUNTqIR3NM+ZBHYFweVf3Gwz5LzHZT2rEZtD +6VXRP75Q/2wOLnqCO4bK4BUs6sqxcQZmOldruPkPynrY0oPfHHExjxZDvQu4/r80 +Ls3n0L8yvQKCAgEAnYWS6EikwaQNpJEfiUnOlglgFz4EE1eVkrDbBY4J3oPU+doz +DrqmsvgpSZIAfd2MUbkN4pOMsMTjbeIYWDnZDa1RoctKs3FhwFPHwAjQpznab4mn +Bp81FMHM40qyb0NaNuFRwghdXvoQvBBX1p8oEnFzDRvTiuS/vTPTA8KDY8IeRp8R +oGzKHpfziNwq/URpqj7pwi9odNjGZvR2IwYw9jCLPIqaEbMoSOdI0mg4MoYyqP4q +nm7d4wqSDwrYxiXZ6f3nYpkhEY1lb0Wbksp1ig8sKSF4nDZRGK1RSfE+6gjBp94H +X/Wog6Zb6NC9ZpusTiDLvuIUXcyUJvmHiWjSNqiTv8jurlwEsgSwhziEQfqLrtdV +QI3PRMolBkD1iCk+HFE53r05LMf1bp3r4MS+naaQrLbIrl1kgDNGwVdgS+SCM7Bg +TwEgE67iOb2iIoUpon/NyP4LesMzvdpsu2JFlfz13PmmQ34mFI7tWvOb3NA5DP3c +46C6SaWI0TD9B11nJbHGTYN3Si9n0EBgoDJEXUKeh3km9O47dgvkSug4WzhYsvrE +rMlMLtKfp2w8HlMZpsUlToNCx6CI+tJrohzcs3BAVAbjFAXRKWGijB1rxwyDdHPv +I+/wJTNaRNPQ1M0SwtEL/zJd21y3KSPn4eL+GP3efhlDSjtlDvZqkdAUsU8= +-----END RSA PRIVATE KEY----- + diff --git a/apps/rsautl.c b/apps/rsautl.c new file mode 100644 index 000000000..e0422d804 --- /dev/null +++ b/apps/rsautl.c @@ -0,0 +1,293 @@ +/* + * Copyright 2000-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#ifdef OPENSSL_NO_RSA +NON_EMPTY_TRANSLATION_UNIT +#else + +# include "apps.h" +# include +# include +# include +# include + +# define RSA_SIGN 1 +# define RSA_VERIFY 2 +# define RSA_ENCRYPT 3 +# define RSA_DECRYPT 4 + +# define KEY_PRIVKEY 1 +# define KEY_PUBKEY 2 +# define KEY_CERT 3 + +typedef enum OPTION_choice { + OPT_ERR = -1, OPT_EOF = 0, OPT_HELP, + OPT_ENGINE, OPT_IN, OPT_OUT, OPT_ASN1PARSE, OPT_HEXDUMP, + OPT_RAW, OPT_OAEP, OPT_SSL, OPT_PKCS, OPT_X931, + OPT_SIGN, OPT_VERIFY, OPT_REV, OPT_ENCRYPT, OPT_DECRYPT, + OPT_PUBIN, OPT_CERTIN, OPT_INKEY, OPT_PASSIN, OPT_KEYFORM, + OPT_CONFIG +} OPTION_CHOICE; + +OPTIONS rsautl_options[] = { + {"help", OPT_HELP, '-', "Display this summary"}, + {"in", OPT_IN, '<', "Input file"}, + {"out", OPT_OUT, '>', "Output file"}, + {"inkey", OPT_INKEY, 's', "Input key"}, + {"keyform", OPT_KEYFORM, 'E', "Private key format - default PEM"}, + {"pubin", OPT_PUBIN, '-', "Input is an RSA public"}, + {"certin", OPT_CERTIN, '-', "Input is a cert carrying an RSA public key"}, + {"ssl", OPT_SSL, '-', "Use SSL v2 padding"}, + {"raw", OPT_RAW, '-', "Use no padding"}, + {"pkcs", OPT_PKCS, '-', "Use PKCS#1 v1.5 padding (default)"}, + {"oaep", OPT_OAEP, '-', "Use PKCS#1 OAEP"}, + {"sign", OPT_SIGN, '-', "Sign with private key"}, + {"verify", OPT_VERIFY, '-', "Verify with public key"}, + {"asn1parse", OPT_ASN1PARSE, '-', + "Run output through asn1parse; useful with -verify"}, + {"hexdump", OPT_HEXDUMP, '-', "Hex dump output"}, + {"x931", OPT_X931, '-', "Use ANSI X9.31 padding"}, + {"rev", OPT_REV, '-', "Reverse the order of the input buffer"}, + {"encrypt", OPT_ENCRYPT, '-', "Encrypt with public key"}, + {"decrypt", OPT_DECRYPT, '-', "Decrypt with private key"}, + {"passin", OPT_PASSIN, 's', "Input file pass phrase source"}, +# ifndef OPENSSL_NO_ENGINE + {"engine", OPT_ENGINE, 's', "Use engine, possibly a hardware device"}, + {"config", OPT_CONFIG, 's', "A config file"}, +# endif + {NULL} +}; + +int rsautl_main(int argc, char **argv) +{ + BIO *in = NULL, *out = NULL; + ENGINE *e = NULL; + EVP_PKEY *pkey = NULL; + RSA *rsa = NULL; + X509 *x; + char *infile = NULL, *outfile = NULL, *keyfile = NULL; + char *passinarg = NULL, *passin = NULL, *prog; + char rsa_mode = RSA_VERIFY, key_type = KEY_PRIVKEY; + unsigned char *rsa_in = NULL, *rsa_out = NULL, pad = RSA_PKCS1_PADDING; + int rsa_inlen, keyformat = FORMAT_PEM, keysize, ret = 1; + int rsa_outlen = 0, hexdump = 0, asn1parse = 0, need_priv = 0, rev = 0; + OPTION_CHOICE o; + CONF *conf = NULL; + char *configfile = default_config_file; + + prog = opt_init(argc, argv, rsautl_options); + while ((o = opt_next()) != OPT_EOF) { + switch (o) { + case OPT_EOF: + case OPT_ERR: + opthelp: + BIO_printf(bio_err, "%s: Use -help for summary.\n", prog); + goto end; + case OPT_HELP: + opt_help(rsautl_options); + ret = 0; + goto end; + case OPT_KEYFORM: + if (!opt_format(opt_arg(), OPT_FMT_PDE, &keyformat)) + goto opthelp; + break; + case OPT_IN: + infile = opt_arg(); + break; + case OPT_OUT: + outfile = opt_arg(); + break; + case OPT_ENGINE: + e = setup_engine(opt_arg(), 0); + break; + case OPT_ASN1PARSE: + asn1parse = 1; + break; + case OPT_HEXDUMP: + hexdump = 1; + break; + case OPT_RAW: + pad = RSA_NO_PADDING; + break; + case OPT_OAEP: + pad = RSA_PKCS1_OAEP_PADDING; + break; + case OPT_SSL: + pad = RSA_SSLV23_PADDING; + break; + case OPT_PKCS: + pad = RSA_PKCS1_PADDING; + break; + case OPT_X931: + pad = RSA_X931_PADDING; + break; + case OPT_SIGN: + rsa_mode = RSA_SIGN; + need_priv = 1; + break; + case OPT_VERIFY: + rsa_mode = RSA_VERIFY; + break; + case OPT_REV: + rev = 1; + break; + case OPT_ENCRYPT: + rsa_mode = RSA_ENCRYPT; + break; + case OPT_DECRYPT: + rsa_mode = RSA_DECRYPT; + need_priv = 1; + break; + case OPT_PUBIN: + key_type = KEY_PUBKEY; + break; + case OPT_CERTIN: + key_type = KEY_CERT; + break; + case OPT_INKEY: + keyfile = opt_arg(); + break; + case OPT_PASSIN: + passinarg = opt_arg(); + break; + case OPT_CONFIG: + configfile = opt_arg(); + break; + } + } + argc = opt_num_rest(); + if (argc != 0) + goto opthelp; + + BIO_printf(bio_err, "Using configuration from %s\n", configfile); + + if ((conf = app_load_config(configfile)) == NULL) + goto end; + if (configfile != default_config_file && !app_load_modules(conf)) + goto end; + + if (need_priv && (key_type != KEY_PRIVKEY)) { + BIO_printf(bio_err, "A private key is needed for this operation\n"); + goto end; + } + + if (!app_passwd(passinarg, NULL, &passin, NULL)) { + BIO_printf(bio_err, "Error getting password\n"); + goto end; + } + +/* FIXME: seed PRNG only if needed */ + app_RAND_load_file(NULL, 0); + + switch (key_type) { + case KEY_PRIVKEY: + pkey = load_key(keyfile, keyformat, 0, passin, e, "Private Key"); + break; + + case KEY_PUBKEY: + pkey = load_pubkey(keyfile, keyformat, 0, NULL, e, "Public Key"); + break; + + case KEY_CERT: + x = load_cert(keyfile, keyformat, "Certificate"); + if (x) { + pkey = X509_get_pubkey(x); + X509_free(x); + } + break; + } + + if (!pkey) { + return 1; + } + + rsa = EVP_PKEY_get1_RSA(pkey); + EVP_PKEY_free(pkey); + + if (!rsa) { + BIO_printf(bio_err, "Error getting RSA key\n"); + ERR_print_errors(bio_err); + goto end; + } + + in = bio_open_default(infile, 'r', FORMAT_BINARY); + if (in == NULL) + goto end; + out = bio_open_default(outfile, 'w', FORMAT_BINARY); + if (out == NULL) + goto end; + + keysize = RSA_size(rsa); + + rsa_in = app_malloc(keysize * 2, "hold rsa key"); + rsa_out = app_malloc(keysize, "output rsa key"); + + /* Read the input data */ + rsa_inlen = BIO_read(in, rsa_in, keysize * 2); + if (rsa_inlen < 0) { + BIO_printf(bio_err, "Error reading input Data\n"); + goto end; + } + if (rev) { + int i; + unsigned char ctmp; + for (i = 0; i < rsa_inlen / 2; i++) { + ctmp = rsa_in[i]; + rsa_in[i] = rsa_in[rsa_inlen - 1 - i]; + rsa_in[rsa_inlen - 1 - i] = ctmp; + } + } + switch (rsa_mode) { + + case RSA_VERIFY: + rsa_outlen = RSA_public_decrypt(rsa_inlen, rsa_in, rsa_out, rsa, pad); + break; + + case RSA_SIGN: + rsa_outlen = + RSA_private_encrypt(rsa_inlen, rsa_in, rsa_out, rsa, pad); + break; + + case RSA_ENCRYPT: + rsa_outlen = RSA_public_encrypt(rsa_inlen, rsa_in, rsa_out, rsa, pad); + break; + + case RSA_DECRYPT: + rsa_outlen = + RSA_private_decrypt(rsa_inlen, rsa_in, rsa_out, rsa, pad); + break; + } + + if (rsa_outlen < 0) { + BIO_printf(bio_err, "RSA operation error\n"); + ERR_print_errors(bio_err); + goto end; + } + ret = 0; + if (asn1parse) { + if (!ASN1_parse_dump(out, rsa_out, rsa_outlen, 1, -1)) { + ERR_print_errors(bio_err); + } + } else if (hexdump) + BIO_dump(out, (char *)rsa_out, rsa_outlen); + else + BIO_write(out, rsa_out, rsa_outlen); + end: + RSA_free(rsa); + release_engine(e); + BIO_free(in); + BIO_free_all(out); + OPENSSL_free(rsa_in); + OPENSSL_free(rsa_out); + OPENSSL_free(passin); + NCONF_free(conf); + return ret; +} +#endif diff --git a/apps/s1024key.pem b/apps/s1024key.pem new file mode 100644 index 000000000..19e040357 --- /dev/null +++ b/apps/s1024key.pem @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICXgIBAAKBgQCzEfU8E+ZGTGtHXV5XhvM2Lg32fXUIjydXb34BGVPX6oN7+aNV +S9eWayvW/+9/vUb0aCqilJrpFesgItV2T8VhhjOE++XUz46uNpcMU7wHMEAXUufP +pztpFm8ZEk2tFKvadkSSoN8lb11juvZVkSkPlB65pFhSe4QKSp6J4HrkYwIDAQAB +AoGBAKy8jvb0Lzby8q11yNLf7+78wCVdYi7ugMHcYA1JVFK8+zb1WfSm44FLQo/0 +dSChAjgz36TTexeLODPYxleJndjVcOMVzsLJjSM8dLpXsTS4FCeMbhw2s2u+xqKY +bbPWfk+HOTyJjfnkcC5Nbg44eOmruq0gSmBeUXVM5UntlTnxAkEA7TGCA3h7kx5E +Bl4zl2pc3gPAGt+dyfk5Po9mGJUUXhF5p2zueGmYWW74TmOWB1kzt4QRdYMzFePq +zfDNXEa1CwJBAMFErdY0xp0UJ13WwBbUTk8rujqQdHtjw0klhpbuKkjxu2hN0wwM +6p0D9qxF7JHaghqVRI0fAW/EE0OzdHMR9QkCQQDNR26dMFXKsoPu+vItljj/UEGf +QG7gERiQ4yxaFBPHgdpGo0kT31eh9x9hQGDkxTe0GNG/YSgCRvm8+C3TMcKXAkBD +dhGn36wkUFCddMSAM4NSJ1VN8/Z0y5HzCmI8dM3VwGtGMUQlxKxwOl30LEQzdS5M +0SWojNYXiT2gOBfBwtbhAkEAhafl5QEOIgUz+XazS/IlZ8goNKdDVfYgK3mHHjvv +nY5G+AuGebdNkXJr4KSWxDcN+C2i47zuj4QXA16MAOandA== +-----END RSA PRIVATE KEY----- diff --git a/apps/s1024req.pem b/apps/s1024req.pem new file mode 100644 index 000000000..bb75e7eeb --- /dev/null +++ b/apps/s1024req.pem @@ -0,0 +1,11 @@ +-----BEGIN CERTIFICATE REQUEST----- +MIIBojCCAQsCAQAwZDELMAkGA1UEBhMCQVUxEzARBgNVBAgTClF1ZWVuc2xhbmQx +GjAYBgNVBAoTEUNyeXB0U29mdCBQdHkgTHRkMSQwIgYDVQQDExtTZXJ2ZXIgdGVz +dCBjZXJ0ICgxMDI0IGJpdCkwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBALMR +9TwT5kZMa0ddXleG8zYuDfZ9dQiPJ1dvfgEZU9fqg3v5o1VL15ZrK9b/73+9RvRo +KqKUmukV6yAi1XZPxWGGM4T75dTPjq42lwxTvAcwQBdS58+nO2kWbxkSTa0Uq9p2 +RJKg3yVvXWO69lWRKQ+UHrmkWFJ7hApKnongeuRjAgMBAAEwDQYJKoZIhvcNAQEE +BQADgYEAStHlk4pBbwiNeQ2/PKTPPXzITYC8Gn0XMbrU94e/6JIKiO7aArq9Espq +nrBSvC14dHcNl6NNvnkEKdQ7hAkcACfBbnOXA/oQvMBd4GD78cH3k0jVDoVUEjil +frLfWlckW6WzpTktt0ZPDdAjJCmKVh0ABHimi7Bo9FC3wIGIe5M= +-----END CERTIFICATE REQUEST----- diff --git a/apps/s512-key.pem b/apps/s512-key.pem new file mode 100644 index 000000000..0e3ff2d37 --- /dev/null +++ b/apps/s512-key.pem @@ -0,0 +1,9 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIBPAIBAAJBAJ+zw4Qnlf8SMVIPFe9GEcStgOY2Ww/dgNdhjeD8ckUJNP5VZkVD +TGiXav6ooKXfX3j/7tdkuD8Ey2//Kv7+ue0CAwEAAQJAN6W31vDEP2DjdqhzCDDu +OA4NACqoiFqyblo7yc2tM4h4xMbC3Yx5UKMN9ZkCtX0gzrz6DyF47bdKcWBzNWCj +gQIhANEoojVt7hq+SQ6MCN6FTAysGgQf56Q3TYoJMoWvdiXVAiEAw3e3rc+VJpOz +rHuDo6bgpjUAAXM+v3fcpsfZSNO6V7kCIQCtbVjanpUwvZkMI9by02oUk9taki3b +PzPfAfNPYAbCJQIhAJXNQDWyqwn/lGmR11cqY2y9nZ1+5w3yHGatLrcDnQHxAiEA +vnlEGo8K85u+KwIOimM48ZG8oTk7iFdkqLJR1utT3aU= +-----END RSA PRIVATE KEY----- diff --git a/apps/s512-req.pem b/apps/s512-req.pem new file mode 100644 index 000000000..ea314be55 --- /dev/null +++ b/apps/s512-req.pem @@ -0,0 +1,8 @@ +-----BEGIN CERTIFICATE REQUEST----- +MIIBGzCBxgIBADBjMQswCQYDVQQGEwJBVTETMBEGA1UECBMKUXVlZW5zbGFuZDEa +MBgGA1UEChMRQ3J5cHRTb2Z0IFB0eSBMdGQxIzAhBgNVBAMTGlNlcnZlciB0ZXN0 +IGNlcnQgKDUxMiBiaXQpMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAJ+zw4Qnlf8S +MVIPFe9GEcStgOY2Ww/dgNdhjeD8ckUJNP5VZkVDTGiXav6ooKXfX3j/7tdkuD8E +y2//Kv7+ue0CAwEAATANBgkqhkiG9w0BAQQFAANBAAB+uQi+qwn6qRSHB8EUTvsm +5TNTHzYDeN39nyIbZNX2s0se3Srn2Bxft5YCwD3moFZ9QoyDHxE0h6qLX5yjD+8= +-----END CERTIFICATE REQUEST----- diff --git a/apps/s_apps.h b/apps/s_apps.h new file mode 100644 index 000000000..c47932bfb --- /dev/null +++ b/apps/s_apps.h @@ -0,0 +1,102 @@ +/* + * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include + +#if defined(OPENSSL_SYS_WINDOWS) || defined(OPENSSL_SYS_MSDOS) +# include +#endif + +#if defined(OPENSSL_SYS_MSDOS) && !defined(_WIN32) +# define _kbhit kbhit +#endif + +#if defined(OPENSSL_SYS_VMS) && !defined(FD_SET) +/* + * VAX C does not defined fd_set and friends, but it's actually quite simple + */ +/* These definitions are borrowed from SOCKETSHR. /Richard Levitte */ +# define MAX_NOFILE 32 +# define NBBY 8 /* number of bits in a byte */ + +# ifndef FD_SETSIZE +# define FD_SETSIZE MAX_NOFILE +# endif /* FD_SETSIZE */ + +/* How many things we'll allow select to use. 0 if unlimited */ +# define MAXSELFD MAX_NOFILE +typedef int fd_mask; /* int here! VMS prototypes int, not long */ +# define NFDBITS (sizeof(fd_mask) * NBBY)/* bits per mask (power of 2!) */ +# define NFDSHIFT 5 /* Shift based on above */ + +typedef fd_mask fd_set; +# define FD_SET(n, p) (*(p) |= (1 << ((n) % NFDBITS))) +# define FD_CLR(n, p) (*(p) &= ~(1 << ((n) % NFDBITS))) +# define FD_ISSET(n, p) (*(p) & (1 << ((n) % NFDBITS))) +# define FD_ZERO(p) memset((p), 0, sizeof(*(p))) +#endif + +#define PORT "4433" +#define PROTOCOL "tcp" + +typedef int (*do_server_cb)(int s, int stype, unsigned char *context); +int do_server(int *accept_sock, const char *host, const char *port, + int family, int type, + do_server_cb cb, + unsigned char *context, int naccept); +#ifdef HEADER_X509_H +int verify_callback(int ok, X509_STORE_CTX *ctx); +#endif +#ifdef HEADER_SSL_H +int set_cert_stuff(SSL_CTX *ctx, char *cert_file, char *key_file); +int set_cert_key_stuff(SSL_CTX *ctx, X509 *cert, EVP_PKEY *key, + STACK_OF(X509) *chain, int build_chain); +int ssl_print_sigalgs(BIO *out, SSL *s); +int ssl_print_point_formats(BIO *out, SSL *s); +int ssl_print_curves(BIO *out, SSL *s, int noshared); +#endif +int ssl_print_tmp_key(BIO *out, SSL *s); +int init_client(int *sock, const char *host, const char *port, + int family, int type); +int should_retry(int i); + +long bio_dump_callback(BIO *bio, int cmd, const char *argp, + int argi, long argl, long ret); + +#ifdef HEADER_SSL_H +void apps_ssl_info_callback(const SSL *s, int where, int ret); +void msg_cb(int write_p, int version, int content_type, const void *buf, + size_t len, SSL *ssl, void *arg); +void tlsext_cb(SSL *s, int client_server, int type, const unsigned char *data, + int len, void *arg); +#endif + +int generate_cookie_callback(SSL *ssl, unsigned char *cookie, + unsigned int *cookie_len); +int verify_cookie_callback(SSL *ssl, const unsigned char *cookie, + unsigned int cookie_len); + +typedef struct ssl_excert_st SSL_EXCERT; + +void ssl_ctx_set_excert(SSL_CTX *ctx, SSL_EXCERT *exc); +void ssl_excert_free(SSL_EXCERT *exc); +int args_excert(int option, SSL_EXCERT **pexc); +int load_excert(SSL_EXCERT **pexc); +void print_verify_detail(SSL *s, BIO *bio); +void print_ssl_summary(SSL *s); +#ifdef HEADER_SSL_H +int config_ctx(SSL_CONF_CTX *cctx, STACK_OF(OPENSSL_STRING) *str, SSL_CTX *ctx); +int ssl_ctx_add_crls(SSL_CTX *ctx, STACK_OF(X509_CRL) *crls, + int crl_download); +int ssl_load_stores(SSL_CTX *ctx, const char *vfyCApath, + const char *vfyCAfile, const char *chCApath, + const char *chCAfile, STACK_OF(X509_CRL) *crls, + int crl_download); +void ssl_ctx_security_debug(SSL_CTX *ctx, int verbose); +#endif diff --git a/apps/s_cb.c b/apps/s_cb.c new file mode 100644 index 000000000..63ad99897 --- /dev/null +++ b/apps/s_cb.c @@ -0,0 +1,1351 @@ +/* + * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +/* callback functions used by s_client, s_server, and s_time */ +#include +#include +#include /* for memcpy() and strcmp() */ +#define USE_SOCKETS +#include "apps.h" +#undef USE_SOCKETS +#include +#include +#include +#include +#include +#ifndef OPENSSL_NO_DH +# include +#endif +#include "s_apps.h" + +#define COOKIE_SECRET_LENGTH 16 + +VERIFY_CB_ARGS verify_args = { 0, 0, X509_V_OK, 0 }; + +#ifndef OPENSSL_NO_SOCK +static unsigned char cookie_secret[COOKIE_SECRET_LENGTH]; +static int cookie_initialized = 0; +#endif + +static const char *lookup(int val, const STRINT_PAIR* list, const char* def) +{ + for ( ; list->name; ++list) + if (list->retval == val) + return list->name; + return def; +} + +int verify_callback(int ok, X509_STORE_CTX *ctx) +{ + X509 *err_cert; + int err, depth; + + err_cert = X509_STORE_CTX_get_current_cert(ctx); + err = X509_STORE_CTX_get_error(ctx); + depth = X509_STORE_CTX_get_error_depth(ctx); + + if (!verify_args.quiet || !ok) { + BIO_printf(bio_err, "depth=%d ", depth); + if (err_cert) { + X509_NAME_print_ex(bio_err, + X509_get_subject_name(err_cert), + 0, XN_FLAG_ONELINE); + BIO_puts(bio_err, "\n"); + } else + BIO_puts(bio_err, "\n"); + } + if (!ok) { + BIO_printf(bio_err, "verify error:num=%d:%s\n", err, + X509_verify_cert_error_string(err)); + if (verify_args.depth >= depth) { + if (!verify_args.return_error) + ok = 1; + verify_args.error = err; + } else { + ok = 0; + verify_args.error = X509_V_ERR_CERT_CHAIN_TOO_LONG; + } + } + switch (err) { + case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT: + BIO_puts(bio_err, "issuer= "); + X509_NAME_print_ex(bio_err, X509_get_issuer_name(err_cert), + 0, XN_FLAG_ONELINE); + BIO_puts(bio_err, "\n"); + break; + case X509_V_ERR_CERT_NOT_YET_VALID: + case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD: + BIO_printf(bio_err, "notBefore="); + ASN1_TIME_print(bio_err, X509_get0_notBefore(err_cert)); + BIO_printf(bio_err, "\n"); + break; + case X509_V_ERR_CERT_HAS_EXPIRED: + case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD: + BIO_printf(bio_err, "notAfter="); + ASN1_TIME_print(bio_err, X509_get0_notAfter(err_cert)); + BIO_printf(bio_err, "\n"); + break; + case X509_V_ERR_NO_EXPLICIT_POLICY: + if (!verify_args.quiet) + policies_print(ctx); + break; + } + if (err == X509_V_OK && ok == 2 && !verify_args.quiet) + policies_print(ctx); + if (ok && !verify_args.quiet) + BIO_printf(bio_err, "verify return:%d\n", ok); + return (ok); +} + +int set_cert_stuff(SSL_CTX *ctx, char *cert_file, char *key_file) +{ + if (cert_file != NULL) { + if (SSL_CTX_use_certificate_file(ctx, cert_file, + SSL_FILETYPE_PEM) <= 0) { + BIO_printf(bio_err, "unable to get certificate from '%s'\n", + cert_file); + ERR_print_errors(bio_err); + return (0); + } + if (key_file == NULL) + key_file = cert_file; + if (SSL_CTX_use_PrivateKey_file(ctx, key_file, SSL_FILETYPE_PEM) <= 0) { + BIO_printf(bio_err, "unable to get private key from '%s'\n", + key_file); + ERR_print_errors(bio_err); + return (0); + } + + /* + * If we are using DSA, we can copy the parameters from the private + * key + */ + + /* + * Now we know that a key and cert have been set against the SSL + * context + */ + if (!SSL_CTX_check_private_key(ctx)) { + BIO_printf(bio_err, + "Private key does not match the certificate public key\n"); + return (0); + } + } + return (1); +} + +int set_cert_key_stuff(SSL_CTX *ctx, X509 *cert, EVP_PKEY *key, + STACK_OF(X509) *chain, int build_chain) +{ + int chflags = chain ? SSL_BUILD_CHAIN_FLAG_CHECK : 0; + if (cert == NULL) + return 1; + if (SSL_CTX_use_certificate(ctx, cert) <= 0) { + BIO_printf(bio_err, "error setting certificate\n"); + ERR_print_errors(bio_err); + return 0; + } + + if (SSL_CTX_use_PrivateKey(ctx, key) <= 0) { + BIO_printf(bio_err, "error setting private key\n"); + ERR_print_errors(bio_err); + return 0; + } + + /* + * Now we know that a key and cert have been set against the SSL context + */ + if (!SSL_CTX_check_private_key(ctx)) { + BIO_printf(bio_err, + "Private key does not match the certificate public key\n"); + return 0; + } + if (chain && !SSL_CTX_set1_chain(ctx, chain)) { + BIO_printf(bio_err, "error setting certificate chain\n"); + ERR_print_errors(bio_err); + return 0; + } + if (build_chain && !SSL_CTX_build_cert_chain(ctx, chflags)) { + BIO_printf(bio_err, "error building certificate chain\n"); + ERR_print_errors(bio_err); + return 0; + } + return 1; +} + +static STRINT_PAIR cert_type_list[] = { + {"RSA sign", TLS_CT_RSA_SIGN}, + {"DSA sign", TLS_CT_DSS_SIGN}, + {"RSA fixed DH", TLS_CT_RSA_FIXED_DH}, + {"DSS fixed DH", TLS_CT_DSS_FIXED_DH}, + {"ECDSA sign", TLS_CT_ECDSA_SIGN}, + {"RSA fixed ECDH", TLS_CT_RSA_FIXED_ECDH}, + {"ECDSA fixed ECDH", TLS_CT_ECDSA_FIXED_ECDH}, + {"GOST01 Sign", TLS_CT_GOST01_SIGN}, + {"SM2 sign", TLS_CT_SM2_SIGN}, + {"SM2 fixed key exchange", TLS_CT_SM2_FIXED_EXCH}, + {"SM2 encrypt", TLS_CT_SM2_ENC}, + {NULL} +}; + +static void ssl_print_client_cert_types(BIO *bio, SSL *s) +{ + const unsigned char *p; + int i; + int cert_type_num = SSL_get0_certificate_types(s, &p); + if (!cert_type_num) + return; + BIO_puts(bio, "Client Certificate Types: "); + for (i = 0; i < cert_type_num; i++) { + unsigned char cert_type = p[i]; + const char *cname = lookup((int)cert_type, cert_type_list, NULL); + + if (i) + BIO_puts(bio, ", "); + if (cname) + BIO_puts(bio, cname); + else + BIO_printf(bio, "UNKNOWN (%d),", cert_type); + } + BIO_puts(bio, "\n"); +} + +static int do_print_sigalgs(BIO *out, SSL *s, int shared) +{ + int i, nsig, client; + client = SSL_is_server(s) ? 0 : 1; + if (shared) + nsig = SSL_get_shared_sigalgs(s, -1, NULL, NULL, NULL, NULL, NULL); + else + nsig = SSL_get_sigalgs(s, -1, NULL, NULL, NULL, NULL, NULL); + if (nsig == 0) + return 1; + + if (shared) + BIO_puts(out, "Shared "); + + if (client) + BIO_puts(out, "Requested "); + BIO_puts(out, "Signature Algorithms: "); + for (i = 0; i < nsig; i++) { + int hash_nid, sign_nid; + unsigned char rhash, rsign; + const char *sstr = NULL; + if (shared) + SSL_get_shared_sigalgs(s, i, &sign_nid, &hash_nid, NULL, + &rsign, &rhash); + else + SSL_get_sigalgs(s, i, &sign_nid, &hash_nid, NULL, &rsign, &rhash); + if (i) + BIO_puts(out, ":"); + if (sign_nid == EVP_PKEY_RSA) + sstr = "RSA"; + else if (sign_nid == EVP_PKEY_DSA) + sstr = "DSA"; + else if (sign_nid == EVP_PKEY_EC) + sstr = "ECDSA"; + else if (sign_nid == NID_sm2sign) + sstr = "SM2"; + if (sstr) + BIO_printf(out, "%s+", sstr); + else + BIO_printf(out, "0x%02X+", (int)rsign); + if (hash_nid != NID_undef) + BIO_printf(out, "%s", OBJ_nid2sn(hash_nid)); + else + BIO_printf(out, "0x%02X", (int)rhash); + } + BIO_puts(out, "\n"); + return 1; +} + +int ssl_print_sigalgs(BIO *out, SSL *s) +{ + int mdnid; + if (!SSL_is_server(s)) + ssl_print_client_cert_types(out, s); + do_print_sigalgs(out, s, 0); + do_print_sigalgs(out, s, 1); + if (SSL_get_peer_signature_nid(s, &mdnid)) + BIO_printf(out, "Peer signing digest: %s\n", OBJ_nid2sn(mdnid)); + return 1; +} + +#ifndef OPENSSL_NO_EC +int ssl_print_point_formats(BIO *out, SSL *s) +{ + int i, nformats; + const char *pformats; + nformats = SSL_get0_ec_point_formats(s, &pformats); + if (nformats <= 0) + return 1; + BIO_puts(out, "Supported Elliptic Curve Point Formats: "); + for (i = 0; i < nformats; i++, pformats++) { + if (i) + BIO_puts(out, ":"); + switch (*pformats) { + case TLSEXT_ECPOINTFORMAT_uncompressed: + BIO_puts(out, "uncompressed"); + break; + + case TLSEXT_ECPOINTFORMAT_ansiX962_compressed_prime: + BIO_puts(out, "ansiX962_compressed_prime"); + break; + + case TLSEXT_ECPOINTFORMAT_ansiX962_compressed_char2: + BIO_puts(out, "ansiX962_compressed_char2"); + break; + + default: + BIO_printf(out, "unknown(%d)", (int)*pformats); + break; + + } + } + BIO_puts(out, "\n"); + return 1; +} + +int ssl_print_curves(BIO *out, SSL *s, int noshared) +{ + int i, ncurves, *curves, nid; + const char *cname; + + ncurves = SSL_get1_curves(s, NULL); + if (ncurves <= 0) + return 1; + curves = app_malloc(ncurves * sizeof(int), "curves to print"); + SSL_get1_curves(s, curves); + + BIO_puts(out, "Supported Elliptic Curves: "); + for (i = 0; i < ncurves; i++) { + if (i) + BIO_puts(out, ":"); + nid = curves[i]; + /* If unrecognised print out hex version */ + if (nid & TLSEXT_nid_unknown) + BIO_printf(out, "0x%04X", nid & 0xFFFF); + else { + /* Use NIST name for curve if it exists */ + cname = EC_curve_nid2nist(nid); + if (!cname) + cname = OBJ_nid2sn(nid); + BIO_printf(out, "%s", cname); + } + } + OPENSSL_free(curves); + if (noshared) { + BIO_puts(out, "\n"); + return 1; + } + BIO_puts(out, "\nShared Elliptic curves: "); + ncurves = SSL_get_shared_curve(s, -1); + for (i = 0; i < ncurves; i++) { + if (i) + BIO_puts(out, ":"); + nid = SSL_get_shared_curve(s, i); + cname = EC_curve_nid2nist(nid); + if (!cname) + cname = OBJ_nid2sn(nid); + BIO_printf(out, "%s", cname); + } + if (ncurves == 0) + BIO_puts(out, "NONE"); + BIO_puts(out, "\n"); + return 1; +} +#endif +int ssl_print_tmp_key(BIO *out, SSL *s) +{ + EVP_PKEY *key; + if (!SSL_get_server_tmp_key(s, &key)) + return 1; + BIO_puts(out, "Server Temp Key: "); + switch (EVP_PKEY_id(key)) { + case EVP_PKEY_RSA: + BIO_printf(out, "RSA, %d bits\n", EVP_PKEY_bits(key)); + break; + + case EVP_PKEY_DH: + BIO_printf(out, "DH, %d bits\n", EVP_PKEY_bits(key)); + break; +#ifndef OPENSSL_NO_EC + case EVP_PKEY_EC: + { + EC_KEY *ec = EVP_PKEY_get1_EC_KEY(key); + int nid; + const char *cname; + nid = EC_GROUP_get_curve_name(EC_KEY_get0_group(ec)); + EC_KEY_free(ec); + cname = EC_curve_nid2nist(nid); + if (!cname) + cname = OBJ_nid2sn(nid); + BIO_printf(out, "ECDH, %s, %d bits\n", cname, EVP_PKEY_bits(key)); + } + break; +#endif + default: + BIO_printf(out, "%s, %d bits\n", OBJ_nid2sn(EVP_PKEY_id(key)), + EVP_PKEY_bits(key)); + } + EVP_PKEY_free(key); + return 1; +} + +long bio_dump_callback(BIO *bio, int cmd, const char *argp, + int argi, long argl, long ret) +{ + BIO *out; + + out = (BIO *)BIO_get_callback_arg(bio); + if (out == NULL) + return (ret); + + if (cmd == (BIO_CB_READ | BIO_CB_RETURN)) { + BIO_printf(out, "read from %p [%p] (%lu bytes => %ld (0x%lX))\n", + (void *)bio, (void *)argp, (unsigned long)argi, ret, ret); + BIO_dump(out, argp, (int)ret); + return (ret); + } else if (cmd == (BIO_CB_WRITE | BIO_CB_RETURN)) { + BIO_printf(out, "write to %p [%p] (%lu bytes => %ld (0x%lX))\n", + (void *)bio, (void *)argp, (unsigned long)argi, ret, ret); + BIO_dump(out, argp, (int)ret); + } + return (ret); +} + +void apps_ssl_info_callback(const SSL *s, int where, int ret) +{ + const char *str; + int w; + + w = where & ~SSL_ST_MASK; + + if (w & SSL_ST_CONNECT) + str = "SSL_connect"; + else if (w & SSL_ST_ACCEPT) + str = "SSL_accept"; + else + str = "undefined"; + + if (where & SSL_CB_LOOP) { + BIO_printf(bio_err, "%s:%s\n", str, SSL_state_string_long(s)); + } else if (where & SSL_CB_ALERT) { + str = (where & SSL_CB_READ) ? "read" : "write"; + BIO_printf(bio_err, "SSL3 alert %s:%s:%s\n", + str, + SSL_alert_type_string_long(ret), + SSL_alert_desc_string_long(ret)); + } else if (where & SSL_CB_EXIT) { + if (ret == 0) + BIO_printf(bio_err, "%s:failed in %s\n", + str, SSL_state_string_long(s)); + else if (ret < 0) { + BIO_printf(bio_err, "%s:error in %s\n", + str, SSL_state_string_long(s)); + } + } +} + +static STRINT_PAIR ssl_versions[] = { + {"SSL 3.0", SSL3_VERSION}, + {"TLS 1.0", TLS1_VERSION}, + {"TLS 1.1", TLS1_1_VERSION}, + {"TLS 1.2", TLS1_2_VERSION}, + {"DTLS 1.0", DTLS1_VERSION}, + {"DTLS 1.0 (bad)", DTLS1_BAD_VER}, + {"GMTLS 1.1", GMTLS_VERSION}, + {NULL} +}; +static STRINT_PAIR alert_types[] = { + {" close_notify", 0}, + {" unexpected_message", 10}, + {" bad_record_mac", 20}, + {" decryption_failed", 21}, + {" record_overflow", 22}, + {" decompression_failure", 30}, + {" handshake_failure", 40}, + {" bad_certificate", 42}, + {" unsupported_certificate", 43}, + {" certificate_revoked", 44}, + {" certificate_expired", 45}, + {" certificate_unknown", 46}, + {" illegal_parameter", 47}, + {" unknown_ca", 48}, + {" access_denied", 49}, + {" decode_error", 50}, + {" decrypt_error", 51}, + {" export_restriction", 60}, + {" protocol_version", 70}, + {" insufficient_security", 71}, + {" internal_error", 80}, + {" user_canceled", 90}, + {" no_renegotiation", 100}, + {" unsupported_extension", 110}, + {" certificate_unobtainable", 111}, + {" unrecognized_name", 112}, + {" bad_certificate_status_response", 113}, + {" bad_certificate_hash_value", 114}, + {" unknown_psk_identity", 115}, +#ifndef OPENSSL_NO_GMTLS + {" unsupported_site2site", 200}, + {" no_area", 201}, + {" unsupported_areatype", 202}, + {" bad_ibcparam", 203}, + {" unsupported_ibcparam", 204}, + {"identity_need", 205}, +#endif + {NULL} +}; + +static STRINT_PAIR handshakes[] = { + {", HelloRequest", 0}, + {", ClientHello", 1}, + {", ServerHello", 2}, + {", HelloVerifyRequest", 3}, + {", NewSessionTicket", 4}, + {", Certificate", 11}, + {", ServerKeyExchange", 12}, + {", CertificateRequest", 13}, + {", ServerHelloDone", 14}, + {", CertificateVerify", 15}, + {", ClientKeyExchange", 16}, + {", Finished", 20}, + {", CertificateUrl", 21}, + {", CertificateStatus", 22}, + {", SupplementalData", 23}, + {NULL} +}; + +void msg_cb(int write_p, int version, int content_type, const void *buf, + size_t len, SSL *ssl, void *arg) +{ + BIO *bio = arg; + const char *str_write_p = write_p ? ">>>" : "<<<"; + const char *str_version = lookup(version, ssl_versions, "???"); + const char *str_content_type = "", *str_details1 = "", *str_details2 = ""; + const unsigned char* bp = buf; + + if (version == SSL3_VERSION || + version == TLS1_VERSION || + version == TLS1_1_VERSION || + version == TLS1_2_VERSION || + version == DTLS1_VERSION || version == DTLS1_BAD_VER) { + switch (content_type) { + case 20: + str_content_type = "ChangeCipherSpec"; + break; + case 21: + str_content_type = "Alert"; + str_details1 = ", ???"; + if (len == 2) { + switch (bp[0]) { + case 1: + str_details1 = ", warning"; + break; + case 2: + str_details1 = ", fatal"; + break; + } + str_details2 = lookup((int)bp[1], alert_types, " ???"); + } + break; + case 22: + str_content_type = "Handshake"; + str_details1 = "???"; + if (len > 0) + str_details1 = lookup((int)bp[0], handshakes, "???"); + break; + case 23: + str_content_type = "ApplicationData"; + break; +#ifndef OPENSSL_NO_HEARTBEATS + case 24: + str_details1 = ", Heartbeat"; + + if (len > 0) { + switch (bp[0]) { + case 1: + str_details1 = ", HeartbeatRequest"; + break; + case 2: + str_details1 = ", HeartbeatResponse"; + break; + } + } + break; +#endif + } + } + + BIO_printf(bio, "%s %s%s [length %04lx]%s%s\n", str_write_p, str_version, + str_content_type, (unsigned long)len, str_details1, + str_details2); + + if (len > 0) { + size_t num, i; + + BIO_printf(bio, " "); + num = len; + for (i = 0; i < num; i++) { + if (i % 16 == 0 && i > 0) + BIO_printf(bio, "\n "); + BIO_printf(bio, " %02x", ((const unsigned char *)buf)[i]); + } + if (i < len) + BIO_printf(bio, " ..."); + BIO_printf(bio, "\n"); + } + (void)BIO_flush(bio); +} + +static STRINT_PAIR tlsext_types[] = { + {"server name", TLSEXT_TYPE_server_name}, + {"max fragment length", TLSEXT_TYPE_max_fragment_length}, + {"client certificate URL", TLSEXT_TYPE_client_certificate_url}, + {"trusted CA keys", TLSEXT_TYPE_trusted_ca_keys}, + {"truncated HMAC", TLSEXT_TYPE_truncated_hmac}, + {"status request", TLSEXT_TYPE_status_request}, + {"user mapping", TLSEXT_TYPE_user_mapping}, + {"client authz", TLSEXT_TYPE_client_authz}, + {"server authz", TLSEXT_TYPE_server_authz}, + {"cert type", TLSEXT_TYPE_cert_type}, + {"elliptic curves", TLSEXT_TYPE_elliptic_curves}, + {"EC point formats", TLSEXT_TYPE_ec_point_formats}, + {"SRP", TLSEXT_TYPE_srp}, + {"signature algorithms", TLSEXT_TYPE_signature_algorithms}, + {"use SRTP", TLSEXT_TYPE_use_srtp}, + {"heartbeat", TLSEXT_TYPE_heartbeat}, + {"session ticket", TLSEXT_TYPE_session_ticket}, + {"renegotiation info", TLSEXT_TYPE_renegotiate}, + {"signed certificate timestamps", TLSEXT_TYPE_signed_certificate_timestamp}, + {"TLS padding", TLSEXT_TYPE_padding}, +#ifdef TLSEXT_TYPE_next_proto_neg + {"next protocol", TLSEXT_TYPE_next_proto_neg}, +#endif +#ifdef TLSEXT_TYPE_encrypt_then_mac + {"encrypt-then-mac", TLSEXT_TYPE_encrypt_then_mac}, +#endif +#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation + {"application layer protocol negotiation", + TLSEXT_TYPE_application_layer_protocol_negotiation}, +#endif +#ifdef TLSEXT_TYPE_extended_master_secret + {"extended master secret", TLSEXT_TYPE_extended_master_secret}, +#endif + {NULL} +}; + +void tlsext_cb(SSL *s, int client_server, int type, + const unsigned char *data, int len, void *arg) +{ + BIO *bio = arg; + const char *extname = lookup(type, tlsext_types, "unknown"); + + BIO_printf(bio, "TLS %s extension \"%s\" (id=%d), len=%d\n", + client_server ? "server" : "client", extname, type, len); + BIO_dump(bio, (const char *)data, len); + (void)BIO_flush(bio); +} + +#ifndef OPENSSL_NO_SOCK +int generate_cookie_callback(SSL *ssl, unsigned char *cookie, + unsigned int *cookie_len) +{ + unsigned char *buffer; + size_t length; + unsigned short port; + BIO_ADDR *peer = NULL; + + /* Initialize a random secret */ + if (!cookie_initialized) { + if (RAND_bytes(cookie_secret, COOKIE_SECRET_LENGTH) <= 0) { + BIO_printf(bio_err, "error setting random cookie secret\n"); + return 0; + } + cookie_initialized = 1; + } + + peer = BIO_ADDR_new(); + if (peer == NULL) { + BIO_printf(bio_err, "memory full\n"); + return 0; + } + + /* Read peer information */ + (void)BIO_dgram_get_peer(SSL_get_rbio(ssl), peer); + + /* Create buffer with peer's address and port */ + BIO_ADDR_rawaddress(peer, NULL, &length); + OPENSSL_assert(length != 0); + port = BIO_ADDR_rawport(peer); + length += sizeof(port); + buffer = app_malloc(length, "cookie generate buffer"); + + memcpy(buffer, &port, sizeof(port)); + BIO_ADDR_rawaddress(peer, buffer + sizeof(port), NULL); + + /* Calculate HMAC of buffer using the secret */ + HMAC(EVP_get_digestbynid(NID_sha1), cookie_secret, COOKIE_SECRET_LENGTH, + buffer, length, cookie, cookie_len); + + OPENSSL_free(buffer); + BIO_ADDR_free(peer); + + return 1; +} + +int verify_cookie_callback(SSL *ssl, const unsigned char *cookie, + unsigned int cookie_len) +{ + unsigned char result[EVP_MAX_MD_SIZE]; + unsigned int resultlength; + + /* Note: we check cookie_initialized because if it's not, + * it cannot be valid */ + if (cookie_initialized + && generate_cookie_callback(ssl, result, &resultlength) + && cookie_len == resultlength + && memcmp(result, cookie, resultlength) == 0) + return 1; + + return 0; +} +#endif + +/* + * Example of extended certificate handling. Where the standard support of + * one certificate per algorithm is not sufficient an application can decide + * which certificate(s) to use at runtime based on whatever criteria it deems + * appropriate. + */ + +/* Linked list of certificates, keys and chains */ +struct ssl_excert_st { + int certform; + const char *certfile; + int keyform; + const char *keyfile; + const char *chainfile; + X509 *cert; + EVP_PKEY *key; + STACK_OF(X509) *chain; + int build_chain; + struct ssl_excert_st *next, *prev; +}; + +static STRINT_PAIR chain_flags[] = { + {"Overall Validity", CERT_PKEY_VALID}, + {"Sign with EE key", CERT_PKEY_SIGN}, + {"EE signature", CERT_PKEY_EE_SIGNATURE}, + {"CA signature", CERT_PKEY_CA_SIGNATURE}, + {"EE key parameters", CERT_PKEY_EE_PARAM}, + {"CA key parameters", CERT_PKEY_CA_PARAM}, + {"Explicitly sign with EE key", CERT_PKEY_EXPLICIT_SIGN}, + {"Issuer Name", CERT_PKEY_ISSUER_NAME}, + {"Certificate Type", CERT_PKEY_CERT_TYPE}, + {NULL} +}; + +static void print_chain_flags(SSL *s, int flags) +{ + STRINT_PAIR *pp; + + for (pp = chain_flags; pp->name; ++pp) + BIO_printf(bio_err, "\t%s: %s\n", + pp->name, + (flags & pp->retval) ? "OK" : "NOT OK"); + BIO_printf(bio_err, "\tSuite B: "); + if (SSL_set_cert_flags(s, 0) & SSL_CERT_FLAG_SUITEB_128_LOS) + BIO_puts(bio_err, flags & CERT_PKEY_SUITEB ? "OK\n" : "NOT OK\n"); + else + BIO_printf(bio_err, "not tested\n"); +} + +/* + * Very basic selection callback: just use any certificate chain reported as + * valid. More sophisticated could prioritise according to local policy. + */ +static int set_cert_cb(SSL *ssl, void *arg) +{ + int i, rv; + SSL_EXCERT *exc = arg; +#ifdef CERT_CB_TEST_RETRY + static int retry_cnt; + if (retry_cnt < 5) { + retry_cnt++; + BIO_printf(bio_err, + "Certificate callback retry test: count %d\n", + retry_cnt); + return -1; + } +#endif + SSL_certs_clear(ssl); + + if (!exc) + return 1; + + /* + * Go to end of list and traverse backwards since we prepend newer + * entries this retains the original order. + */ + while (exc->next) + exc = exc->next; + + i = 0; + + while (exc) { + i++; + rv = SSL_check_chain(ssl, exc->cert, exc->key, exc->chain); + BIO_printf(bio_err, "Checking cert chain %d:\nSubject: ", i); + X509_NAME_print_ex(bio_err, X509_get_subject_name(exc->cert), 0, + XN_FLAG_ONELINE); + BIO_puts(bio_err, "\n"); + print_chain_flags(ssl, rv); + if (rv & CERT_PKEY_VALID) { + if (!SSL_use_certificate(ssl, exc->cert) + || !SSL_use_PrivateKey(ssl, exc->key)) { + return 0; + } + /* + * NB: we wouldn't normally do this as it is not efficient + * building chains on each connection better to cache the chain + * in advance. + */ + if (exc->build_chain) { + if (!SSL_build_cert_chain(ssl, 0)) + return 0; + } else if (exc->chain) + SSL_set1_chain(ssl, exc->chain); + } + exc = exc->prev; + } + return 1; +} + +void ssl_ctx_set_excert(SSL_CTX *ctx, SSL_EXCERT *exc) +{ + SSL_CTX_set_cert_cb(ctx, set_cert_cb, exc); +} + +static int ssl_excert_prepend(SSL_EXCERT **pexc) +{ + SSL_EXCERT *exc = app_malloc(sizeof(*exc), "prepend cert"); + + memset(exc, 0, sizeof(*exc)); + + exc->next = *pexc; + *pexc = exc; + + if (exc->next) { + exc->certform = exc->next->certform; + exc->keyform = exc->next->keyform; + exc->next->prev = exc; + } else { + exc->certform = FORMAT_PEM; + exc->keyform = FORMAT_PEM; + } + return 1; + +} + +void ssl_excert_free(SSL_EXCERT *exc) +{ + SSL_EXCERT *curr; + + if (!exc) + return; + while (exc) { + X509_free(exc->cert); + EVP_PKEY_free(exc->key); + sk_X509_pop_free(exc->chain, X509_free); + curr = exc; + exc = exc->next; + OPENSSL_free(curr); + } +} + +int load_excert(SSL_EXCERT **pexc) +{ + SSL_EXCERT *exc = *pexc; + if (!exc) + return 1; + /* If nothing in list, free and set to NULL */ + if (!exc->certfile && !exc->next) { + ssl_excert_free(exc); + *pexc = NULL; + return 1; + } + for (; exc; exc = exc->next) { + if (!exc->certfile) { + BIO_printf(bio_err, "Missing filename\n"); + return 0; + } + exc->cert = load_cert(exc->certfile, exc->certform, + "Server Certificate"); + if (!exc->cert) + return 0; + if (exc->keyfile) { + exc->key = load_key(exc->keyfile, exc->keyform, + 0, NULL, NULL, "Server Key"); + } else { + exc->key = load_key(exc->certfile, exc->certform, + 0, NULL, NULL, "Server Key"); + } + if (!exc->key) + return 0; + if (exc->chainfile) { + if (!load_certs(exc->chainfile, &exc->chain, FORMAT_PEM, NULL, + "Server Chain")) + return 0; + } + } + return 1; +} + +enum range { OPT_X_ENUM }; + +int args_excert(int opt, SSL_EXCERT **pexc) +{ + SSL_EXCERT *exc = *pexc; + + assert(opt > OPT_X__FIRST); + assert(opt < OPT_X__LAST); + + if (exc == NULL) { + if (!ssl_excert_prepend(&exc)) { + BIO_printf(bio_err, " %s: Error initialising xcert\n", + opt_getprog()); + goto err; + } + *pexc = exc; + } + + switch ((enum range)opt) { + case OPT_X__FIRST: + case OPT_X__LAST: + return 0; + case OPT_X_CERT: + if (exc->certfile && !ssl_excert_prepend(&exc)) { + BIO_printf(bio_err, "%s: Error adding xcert\n", opt_getprog()); + goto err; + } + exc->certfile = opt_arg(); + break; + case OPT_X_KEY: + if (exc->keyfile) { + BIO_printf(bio_err, "%s: Key already specified\n", opt_getprog()); + goto err; + } + exc->keyfile = opt_arg(); + break; + case OPT_X_CHAIN: + if (exc->chainfile) { + BIO_printf(bio_err, "%s: Chain already specified\n", + opt_getprog()); + goto err; + } + exc->chainfile = opt_arg(); + break; + case OPT_X_CHAIN_BUILD: + exc->build_chain = 1; + break; + case OPT_X_CERTFORM: + if (!opt_format(opt_arg(), OPT_FMT_PEMDER, &exc->certform)) + return 0; + break; + case OPT_X_KEYFORM: + if (!opt_format(opt_arg(), OPT_FMT_PEMDER, &exc->keyform)) + return 0; + break; + } + return 1; + + err: + ERR_print_errors(bio_err); + ssl_excert_free(exc); + *pexc = NULL; + return 0; +} + +static void print_raw_cipherlist(SSL *s) +{ + const unsigned char *rlist; + static const unsigned char scsv_id[] = { 0, 0xFF }; + size_t i, rlistlen, num; + if (!SSL_is_server(s)) + return; + num = SSL_get0_raw_cipherlist(s, NULL); + OPENSSL_assert(num == 2); + rlistlen = SSL_get0_raw_cipherlist(s, &rlist); + BIO_puts(bio_err, "Client cipher list: "); + for (i = 0; i < rlistlen; i += num, rlist += num) { + const SSL_CIPHER *c = SSL_CIPHER_find(s, rlist); + if (i) + BIO_puts(bio_err, ":"); + if (c) + BIO_puts(bio_err, SSL_CIPHER_get_name(c)); + else if (!memcmp(rlist, scsv_id, num)) + BIO_puts(bio_err, "SCSV"); + else { + size_t j; + BIO_puts(bio_err, "0x"); + for (j = 0; j < num; j++) + BIO_printf(bio_err, "%02X", rlist[j]); + } + } + BIO_puts(bio_err, "\n"); +} + +/* + * Hex encoder for TLSA RRdata, not ':' delimited. + */ +static char *hexencode(const unsigned char *data, size_t len) +{ + static const char *hex = "0123456789abcdef"; + char *out; + char *cp; + size_t outlen = 2 * len + 1; + int ilen = (int) outlen; + + if (outlen < len || ilen < 0 || outlen != (size_t)ilen) { + BIO_printf(bio_err, "%s: %" PRIu64 "-byte buffer too large to hexencode\n", + opt_getprog(), (uint64_t)len); + exit(1); + } + cp = out = app_malloc(ilen, "TLSA hex data buffer"); + + while (len-- > 0) { + *cp++ = hex[(*data >> 4) & 0x0f]; + *cp++ = hex[*data++ & 0x0f]; + } + *cp = '\0'; + return out; +} + +void print_verify_detail(SSL *s, BIO *bio) +{ + int mdpth; + EVP_PKEY *mspki; + long verify_err = SSL_get_verify_result(s); + + if (verify_err == X509_V_OK) { + const char *peername = SSL_get0_peername(s); + + BIO_printf(bio, "Verification: OK\n"); + if (peername != NULL) + BIO_printf(bio, "Verified peername: %s\n", peername); + } else { + const char *reason = X509_verify_cert_error_string(verify_err); + + BIO_printf(bio, "Verification error: %s\n", reason); + } + + if ((mdpth = SSL_get0_dane_authority(s, NULL, &mspki)) >= 0) { + uint8_t usage, selector, mtype; + const unsigned char *data = NULL; + size_t dlen = 0; + char *hexdata; + + mdpth = SSL_get0_dane_tlsa(s, &usage, &selector, &mtype, &data, &dlen); + + /* + * The TLSA data field can be quite long when it is a certificate, + * public key or even a SHA2-512 digest. Because the initial octets of + * ASN.1 certificates and public keys contain mostly boilerplate OIDs + * and lengths, we show the last 12 bytes of the data instead, as these + * are more likely to distinguish distinct TLSA records. + */ +#define TLSA_TAIL_SIZE 12 + if (dlen > TLSA_TAIL_SIZE) + hexdata = hexencode(data + dlen - TLSA_TAIL_SIZE, TLSA_TAIL_SIZE); + else + hexdata = hexencode(data, dlen); + BIO_printf(bio, "DANE TLSA %d %d %d %s%s %s at depth %d\n", + usage, selector, mtype, + (dlen > TLSA_TAIL_SIZE) ? "..." : "", hexdata, + (mspki != NULL) ? "signed the certificate" : + mdpth ? "matched TA certificate" : "matched EE certificate", + mdpth); + OPENSSL_free(hexdata); + } +} + +void print_ssl_summary(SSL *s) +{ + const SSL_CIPHER *c; + X509 *peer; + /* const char *pnam = SSL_is_server(s) ? "client" : "server"; */ + + BIO_printf(bio_err, "Protocol version: %s\n", SSL_get_version(s)); + print_raw_cipherlist(s); + c = SSL_get_current_cipher(s); + BIO_printf(bio_err, "Ciphersuite: %s\n", SSL_CIPHER_get_name(c)); + do_print_sigalgs(bio_err, s, 0); + peer = SSL_get_peer_certificate(s); + if (peer) { + int nid; + + BIO_puts(bio_err, "Peer certificate: "); + X509_NAME_print_ex(bio_err, X509_get_subject_name(peer), + 0, XN_FLAG_ONELINE); + BIO_puts(bio_err, "\n"); + if (SSL_get_peer_signature_nid(s, &nid)) + BIO_printf(bio_err, "Hash used: %s\n", OBJ_nid2sn(nid)); + print_verify_detail(s, bio_err); + } else + BIO_puts(bio_err, "No peer certificate\n"); + X509_free(peer); +#ifndef OPENSSL_NO_EC + ssl_print_point_formats(bio_err, s); + if (SSL_is_server(s)) + ssl_print_curves(bio_err, s, 1); + else + ssl_print_tmp_key(bio_err, s); +#else + if (!SSL_is_server(s)) + ssl_print_tmp_key(bio_err, s); +#endif +} + +int config_ctx(SSL_CONF_CTX *cctx, STACK_OF(OPENSSL_STRING) *str, + SSL_CTX *ctx) +{ + int i; + + SSL_CONF_CTX_set_ssl_ctx(cctx, ctx); + for (i = 0; i < sk_OPENSSL_STRING_num(str); i += 2) { + const char *flag = sk_OPENSSL_STRING_value(str, i); + const char *arg = sk_OPENSSL_STRING_value(str, i + 1); + if (SSL_CONF_cmd(cctx, flag, arg) <= 0) { + if (arg) + BIO_printf(bio_err, "Error with command: \"%s %s\"\n", + flag, arg); + else + BIO_printf(bio_err, "Error with command: \"%s\"\n", flag); + ERR_print_errors(bio_err); + return 0; + } + } + if (!SSL_CONF_CTX_finish(cctx)) { + BIO_puts(bio_err, "Error finishing context\n"); + ERR_print_errors(bio_err); + return 0; + } + return 1; +} + +static int add_crls_store(X509_STORE *st, STACK_OF(X509_CRL) *crls) +{ + X509_CRL *crl; + int i; + for (i = 0; i < sk_X509_CRL_num(crls); i++) { + crl = sk_X509_CRL_value(crls, i); + X509_STORE_add_crl(st, crl); + } + return 1; +} + +int ssl_ctx_add_crls(SSL_CTX *ctx, STACK_OF(X509_CRL) *crls, int crl_download) +{ + X509_STORE *st; + st = SSL_CTX_get_cert_store(ctx); + add_crls_store(st, crls); + if (crl_download) + store_setup_crl_download(st); + return 1; +} + +int ssl_load_stores(SSL_CTX *ctx, + const char *vfyCApath, const char *vfyCAfile, + const char *chCApath, const char *chCAfile, + STACK_OF(X509_CRL) *crls, int crl_download) +{ + X509_STORE *vfy = NULL, *ch = NULL; + int rv = 0; + if (vfyCApath != NULL || vfyCAfile != NULL) { + vfy = X509_STORE_new(); + if (vfy == NULL) + goto err; + if (!X509_STORE_load_locations(vfy, vfyCAfile, vfyCApath)) + goto err; + add_crls_store(vfy, crls); + SSL_CTX_set1_verify_cert_store(ctx, vfy); + if (crl_download) + store_setup_crl_download(vfy); + } + if (chCApath != NULL || chCAfile != NULL) { + ch = X509_STORE_new(); + if (ch == NULL) + goto err; + if (!X509_STORE_load_locations(ch, chCAfile, chCApath)) + goto err; + SSL_CTX_set1_chain_cert_store(ctx, ch); + } + rv = 1; + err: + X509_STORE_free(vfy); + X509_STORE_free(ch); + return rv; +} + +/* Verbose print out of security callback */ + +typedef struct { + BIO *out; + int verbose; + int (*old_cb) (const SSL *s, const SSL_CTX *ctx, int op, int bits, int nid, + void *other, void *ex); +} security_debug_ex; + +static STRINT_PAIR callback_types[] = { + {"Supported Ciphersuite", SSL_SECOP_CIPHER_SUPPORTED}, + {"Shared Ciphersuite", SSL_SECOP_CIPHER_SHARED}, + {"Check Ciphersuite", SSL_SECOP_CIPHER_CHECK}, +#ifndef OPENSSL_NO_DH + {"Temp DH key bits", SSL_SECOP_TMP_DH}, +#endif + {"Supported Curve", SSL_SECOP_CURVE_SUPPORTED}, + {"Shared Curve", SSL_SECOP_CURVE_SHARED}, + {"Check Curve", SSL_SECOP_CURVE_CHECK}, + {"Supported Signature Algorithm digest", SSL_SECOP_SIGALG_SUPPORTED}, + {"Shared Signature Algorithm digest", SSL_SECOP_SIGALG_SHARED}, + {"Check Signature Algorithm digest", SSL_SECOP_SIGALG_CHECK}, + {"Signature Algorithm mask", SSL_SECOP_SIGALG_MASK}, + {"Certificate chain EE key", SSL_SECOP_EE_KEY}, + {"Certificate chain CA key", SSL_SECOP_CA_KEY}, + {"Peer Chain EE key", SSL_SECOP_PEER_EE_KEY}, + {"Peer Chain CA key", SSL_SECOP_PEER_CA_KEY}, + {"Certificate chain CA digest", SSL_SECOP_CA_MD}, + {"Peer chain CA digest", SSL_SECOP_PEER_CA_MD}, + {"SSL compression", SSL_SECOP_COMPRESSION}, + {"Session ticket", SSL_SECOP_TICKET}, + {NULL} +}; + +static int security_callback_debug(const SSL *s, const SSL_CTX *ctx, + int op, int bits, int nid, + void *other, void *ex) +{ + security_debug_ex *sdb = ex; + int rv, show_bits = 1, cert_md = 0; + const char *nm; + rv = sdb->old_cb(s, ctx, op, bits, nid, other, ex); + if (rv == 1 && sdb->verbose < 2) + return 1; + BIO_puts(sdb->out, "Security callback: "); + + nm = lookup(op, callback_types, NULL); + switch (op) { + case SSL_SECOP_TICKET: + case SSL_SECOP_COMPRESSION: + show_bits = 0; + nm = NULL; + break; + case SSL_SECOP_VERSION: + BIO_printf(sdb->out, "Version=%s", lookup(nid, ssl_versions, "???")); + show_bits = 0; + nm = NULL; + break; + case SSL_SECOP_CA_MD: + case SSL_SECOP_PEER_CA_MD: + cert_md = 1; + break; + } + if (nm) + BIO_printf(sdb->out, "%s=", nm); + + switch (op & SSL_SECOP_OTHER_TYPE) { + + case SSL_SECOP_OTHER_CIPHER: + BIO_puts(sdb->out, SSL_CIPHER_get_name(other)); + break; + +#ifndef OPENSSL_NO_EC + case SSL_SECOP_OTHER_CURVE: + { + const char *cname; + cname = EC_curve_nid2nist(nid); + if (cname == NULL) + cname = OBJ_nid2sn(nid); + BIO_puts(sdb->out, cname); + } + break; +#endif +#ifndef OPENSSL_NO_DH + case SSL_SECOP_OTHER_DH: + { + DH *dh = other; + BIO_printf(sdb->out, "%d", DH_bits(dh)); + break; + } +#endif + case SSL_SECOP_OTHER_CERT: + { + if (cert_md) { + int sig_nid = X509_get_signature_nid(other); + BIO_puts(sdb->out, OBJ_nid2sn(sig_nid)); + } else { + EVP_PKEY *pkey = X509_get0_pubkey(other); + const char *algname = ""; + EVP_PKEY_asn1_get0_info(NULL, NULL, NULL, NULL, + &algname, EVP_PKEY_get0_asn1(pkey)); + BIO_printf(sdb->out, "%s, bits=%d", + algname, EVP_PKEY_bits(pkey)); + } + break; + } + case SSL_SECOP_OTHER_SIGALG: + { + const unsigned char *salg = other; + const char *sname = NULL; + switch (salg[1]) { + case TLSEXT_signature_anonymous: + sname = "anonymous"; + break; + case TLSEXT_signature_rsa: + sname = "RSA"; + break; + case TLSEXT_signature_dsa: + sname = "DSA"; + break; + case TLSEXT_signature_ecdsa: + sname = "ECDSA"; + break; + case TLSEXT_signature_sm2sign: + sname = "SM2"; + break; + } + + BIO_puts(sdb->out, OBJ_nid2sn(nid)); + if (sname) + BIO_printf(sdb->out, ", algorithm=%s", sname); + else + BIO_printf(sdb->out, ", algid=%d", salg[1]); + break; + } + + } + + if (show_bits) + BIO_printf(sdb->out, ", security bits=%d", bits); + BIO_printf(sdb->out, ": %s\n", rv ? "yes" : "no"); + return rv; +} + +void ssl_ctx_security_debug(SSL_CTX *ctx, int verbose) +{ + static security_debug_ex sdb; + + sdb.out = bio_err; + sdb.verbose = verbose; + sdb.old_cb = SSL_CTX_get_security_callback(ctx); + SSL_CTX_set_security_callback(ctx, security_callback_debug); + SSL_CTX_set0_security_ex_data(ctx, &sdb); +} diff --git a/apps/s_client.c b/apps/s_client.c new file mode 100644 index 000000000..01b33c690 --- /dev/null +++ b/apps/s_client.c @@ -0,0 +1,2825 @@ +/* + * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +/* ==================================================================== + * Copyright 2005 Nokia. All rights reserved. + * + * The portions of the attached software ("Contribution") is developed by + * Nokia Corporation and is licensed pursuant to the OpenSSL open source + * license. + * + * The Contribution, originally written by Mika Kousa and Pasi Eronen of + * Nokia Corporation, consists of the "PSK" (Pre-Shared Key) ciphersuites + * support (see RFC 4279) to OpenSSL. + * + * No patent licenses or other rights except those expressly stated in + * the OpenSSL open source license shall be deemed granted or received + * expressly, by implication, estoppel, or otherwise. + * + * No assurances are provided by Nokia that the Contribution does not + * infringe the patent or other intellectual property rights of any third + * party or that the license provides you with all the necessary rights + * to make use of the Contribution. + * + * THE SOFTWARE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND. IN + * ADDITION TO THE DISCLAIMERS INCLUDED IN THE LICENSE, NOKIA + * SPECIFICALLY DISCLAIMS ANY LIABILITY FOR CLAIMS BROUGHT BY YOU OR ANY + * OTHER ENTITY BASED ON INFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS OR + * OTHERWISE. + */ + +#include +#include +#include +#include +#include +#include + +#ifndef OPENSSL_NO_SOCK + +/* + * With IPv6, it looks like Digital has mixed up the proper order of + * recursive header file inclusion, resulting in the compiler complaining + * that u_int isn't defined, but only if _POSIX_C_SOURCE is defined, which is + * needed to have fileno() declared correctly... So let's define u_int + */ +#if defined(OPENSSL_SYS_VMS_DECC) && !defined(__U_INT) +# define __U_INT +typedef unsigned int u_int; +#endif + +#define USE_SOCKETS +#include "apps.h" +#include +#include +#include +#include +#include +#include +#include +#include +#ifndef OPENSSL_NO_SRP +# include +#endif +#ifndef OPENSSL_NO_CT +# include +#endif +#include "s_apps.h" +#include "timeouts.h" + +#if defined(__has_feature) +# if __has_feature(memory_sanitizer) +# include +# endif +#endif + +#undef BUFSIZZ +#define BUFSIZZ 1024*8 +#define S_CLIENT_IRC_READ_TIMEOUT 8 + +static char *prog; +static int c_debug = 0; +static int c_showcerts = 0; +static char *keymatexportlabel = NULL; +static int keymatexportlen = 20; +static BIO *bio_c_out = NULL; +static int c_quiet = 0; + +static void print_stuff(BIO *berr, SSL *con, int full); +#ifndef OPENSSL_NO_OCSP +static int ocsp_resp_cb(SSL *s, void *arg); +#endif + +static int saved_errno; + +static void save_errno(void) +{ + saved_errno = errno; + errno = 0; +} + +static int restore_errno(void) +{ + int ret = errno; + errno = saved_errno; + return ret; +} + +static void do_ssl_shutdown(SSL *ssl) +{ + int ret; + + do { + /* We only do unidirectional shutdown */ + ret = SSL_shutdown(ssl); + if (ret < 0) { + switch (SSL_get_error(ssl, ret)) { + case SSL_ERROR_WANT_READ: + case SSL_ERROR_WANT_WRITE: + case SSL_ERROR_WANT_ASYNC: + case SSL_ERROR_WANT_ASYNC_JOB: + /* We just do busy waiting. Nothing clever */ + continue; + } + ret = 0; + } + } while (ret < 0); +} + +#ifndef OPENSSL_NO_PSK +/* Default PSK identity and key */ +static char *psk_identity = "Client_identity"; +/* + * char *psk_key=NULL; by default PSK is not used + */ + +static unsigned int psk_client_cb(SSL *ssl, const char *hint, char *identity, + unsigned int max_identity_len, + unsigned char *psk, + unsigned int max_psk_len) +{ + int ret; + long key_len; + unsigned char *key; + + if (c_debug) + BIO_printf(bio_c_out, "psk_client_cb\n"); + if (!hint) { + /* no ServerKeyExchange message */ + if (c_debug) + BIO_printf(bio_c_out, + "NULL received PSK identity hint, continuing anyway\n"); + } else if (c_debug) + BIO_printf(bio_c_out, "Received PSK identity hint '%s'\n", hint); + + /* + * lookup PSK identity and PSK key based on the given identity hint here + */ + ret = BIO_snprintf(identity, max_identity_len, "%s", psk_identity); + if (ret < 0 || (unsigned int)ret > max_identity_len) + goto out_err; + if (c_debug) + BIO_printf(bio_c_out, "created identity '%s' len=%d\n", identity, + ret); + + /* convert the PSK key to binary */ + key = OPENSSL_hexstr2buf(psk_key, &key_len); + if (key == NULL) { + BIO_printf(bio_err, "Could not convert PSK key '%s' to buffer\n", + psk_key); + return 0; + } + if (key_len > max_psk_len) { + BIO_printf(bio_err, + "psk buffer of callback is too small (%d) for key (%ld)\n", + max_psk_len, key_len); + OPENSSL_free(key); + return 0; + } + + memcpy(psk, key, key_len); + OPENSSL_free(key); + + if (c_debug) + BIO_printf(bio_c_out, "created PSK len=%ld\n", key_len); + + return key_len; + out_err: + if (c_debug) + BIO_printf(bio_err, "Error in PSK client callback\n"); + return 0; +} +#endif + +/* This is a context that we pass to callbacks */ +typedef struct tlsextctx_st { + BIO *biodebug; + int ack; +} tlsextctx; + +static int ssl_servername_cb(SSL *s, int *ad, void *arg) +{ + tlsextctx *p = (tlsextctx *) arg; + const char *hn = SSL_get_servername(s, TLSEXT_NAMETYPE_host_name); + if (SSL_get_servername_type(s) != -1) + p->ack = !SSL_session_reused(s) && hn != NULL; + else + BIO_printf(bio_err, "Can't use SSL_get_servername\n"); + + return SSL_TLSEXT_ERR_OK; +} + +#ifndef OPENSSL_NO_SRP + +/* This is a context that we pass to all callbacks */ +typedef struct srp_arg_st { + char *srppassin; + char *srplogin; + int msg; /* copy from c_msg */ + int debug; /* copy from c_debug */ + int amp; /* allow more groups */ + int strength; /* minimal size for N */ +} SRP_ARG; + +# define SRP_NUMBER_ITERATIONS_FOR_PRIME 64 + +static int srp_Verify_N_and_g(const BIGNUM *N, const BIGNUM *g) +{ + BN_CTX *bn_ctx = BN_CTX_new(); + BIGNUM *p = BN_new(); + BIGNUM *r = BN_new(); + int ret = + g != NULL && N != NULL && bn_ctx != NULL && BN_is_odd(N) && + BN_is_prime_ex(N, SRP_NUMBER_ITERATIONS_FOR_PRIME, bn_ctx, NULL) == 1 && + p != NULL && BN_rshift1(p, N) && + /* p = (N-1)/2 */ + BN_is_prime_ex(p, SRP_NUMBER_ITERATIONS_FOR_PRIME, bn_ctx, NULL) == 1 && + r != NULL && + /* verify g^((N-1)/2) == -1 (mod N) */ + BN_mod_exp(r, g, p, N, bn_ctx) && + BN_add_word(r, 1) && BN_cmp(r, N) == 0; + + BN_free(r); + BN_free(p); + BN_CTX_free(bn_ctx); + return ret; +} + +/*- + * This callback is used here for two purposes: + * - extended debugging + * - making some primality tests for unknown groups + * The callback is only called for a non default group. + * + * An application does not need the call back at all if + * only the standard groups are used. In real life situations, + * client and server already share well known groups, + * thus there is no need to verify them. + * Furthermore, in case that a server actually proposes a group that + * is not one of those defined in RFC 5054, it is more appropriate + * to add the group to a static list and then compare since + * primality tests are rather cpu consuming. + */ + +static int ssl_srp_verify_param_cb(SSL *s, void *arg) +{ + SRP_ARG *srp_arg = (SRP_ARG *)arg; + BIGNUM *N = NULL, *g = NULL; + + if (((N = SSL_get_srp_N(s)) == NULL) || ((g = SSL_get_srp_g(s)) == NULL)) + return 0; + if (srp_arg->debug || srp_arg->msg || srp_arg->amp == 1) { + BIO_printf(bio_err, "SRP parameters:\n"); + BIO_printf(bio_err, "\tN="); + BN_print(bio_err, N); + BIO_printf(bio_err, "\n\tg="); + BN_print(bio_err, g); + BIO_printf(bio_err, "\n"); + } + + if (SRP_check_known_gN_param(g, N)) + return 1; + + if (srp_arg->amp == 1) { + if (srp_arg->debug) + BIO_printf(bio_err, + "SRP param N and g are not known params, going to check deeper.\n"); + + /* + * The srp_moregroups is a real debugging feature. Implementors + * should rather add the value to the known ones. The minimal size + * has already been tested. + */ + if (BN_num_bits(g) <= BN_BITS && srp_Verify_N_and_g(N, g)) + return 1; + } + BIO_printf(bio_err, "SRP param N and g rejected.\n"); + return 0; +} + +# define PWD_STRLEN 1024 + +static char *ssl_give_srp_client_pwd_cb(SSL *s, void *arg) +{ + SRP_ARG *srp_arg = (SRP_ARG *)arg; + char *pass = app_malloc(PWD_STRLEN + 1, "SRP password buffer"); + PW_CB_DATA cb_tmp; + int l; + + cb_tmp.password = (char *)srp_arg->srppassin; + cb_tmp.prompt_info = "SRP user"; + if ((l = password_callback(pass, PWD_STRLEN, 0, &cb_tmp)) < 0) { + BIO_printf(bio_err, "Can't read Password\n"); + OPENSSL_free(pass); + return NULL; + } + *(pass + l) = '\0'; + + return pass; +} + +#endif + +static char *srtp_profiles = NULL; + +#ifndef OPENSSL_NO_NEXTPROTONEG +/* This the context that we pass to next_proto_cb */ +typedef struct tlsextnextprotoctx_st { + unsigned char *data; + size_t len; + int status; +} tlsextnextprotoctx; + +static tlsextnextprotoctx next_proto; + +static int next_proto_cb(SSL *s, unsigned char **out, unsigned char *outlen, + const unsigned char *in, unsigned int inlen, + void *arg) +{ + tlsextnextprotoctx *ctx = arg; + + if (!c_quiet) { + /* We can assume that |in| is syntactically valid. */ + unsigned i; + BIO_printf(bio_c_out, "Protocols advertised by server: "); + for (i = 0; i < inlen;) { + if (i) + BIO_write(bio_c_out, ", ", 2); + BIO_write(bio_c_out, &in[i + 1], in[i]); + i += in[i] + 1; + } + BIO_write(bio_c_out, "\n", 1); + } + + ctx->status = + SSL_select_next_proto(out, outlen, in, inlen, ctx->data, ctx->len); + return SSL_TLSEXT_ERR_OK; +} +#endif /* ndef OPENSSL_NO_NEXTPROTONEG */ + +static int serverinfo_cli_parse_cb(SSL *s, unsigned int ext_type, + const unsigned char *in, size_t inlen, + int *al, void *arg) +{ + char pem_name[100]; + unsigned char ext_buf[4 + 65536]; + + /* Reconstruct the type/len fields prior to extension data */ + ext_buf[0] = ext_type >> 8; + ext_buf[1] = ext_type & 0xFF; + ext_buf[2] = inlen >> 8; + ext_buf[3] = inlen & 0xFF; + memcpy(ext_buf + 4, in, inlen); + + BIO_snprintf(pem_name, sizeof(pem_name), "SERVERINFO FOR EXTENSION %d", + ext_type); + PEM_write_bio(bio_c_out, pem_name, "", ext_buf, 4 + inlen); + return 1; +} + +/* + * Hex decoder that tolerates optional whitespace. Returns number of bytes + * produced, advances inptr to end of input string. + */ +static ossl_ssize_t hexdecode(const char **inptr, void *result) +{ + unsigned char **out = (unsigned char **)result; + const char *in = *inptr; + unsigned char *ret = app_malloc(strlen(in) / 2, "hexdecode"); + unsigned char *cp = ret; + uint8_t byte; + int nibble = 0; + + if (ret == NULL) + return -1; + + for (byte = 0; *in; ++in) { + int x; + + if (isspace(_UC(*in))) + continue; + x = OPENSSL_hexchar2int(*in); + if (x < 0) { + OPENSSL_free(ret); + return 0; + } + byte |= (char)x; + if ((nibble ^= 1) == 0) { + *cp++ = byte; + byte = 0; + } else { + byte <<= 4; + } + } + if (nibble != 0) { + OPENSSL_free(ret); + return 0; + } + *inptr = in; + + return cp - (*out = ret); +} + +/* + * Decode unsigned 0..255, returns 1 on success, <= 0 on failure. Advances + * inptr to next field skipping leading whitespace. + */ +static ossl_ssize_t checked_uint8(const char **inptr, void *out) +{ + uint8_t *result = (uint8_t *)out; + const char *in = *inptr; + char *endp; + long v; + int e; + + save_errno(); + v = strtol(in, &endp, 10); + e = restore_errno(); + + if (((v == LONG_MIN || v == LONG_MAX) && e == ERANGE) || + endp == in || !isspace(_UC(*endp)) || + v != (*result = (uint8_t) v)) { + return -1; + } + for (in = endp; isspace(_UC(*in)); ++in) + continue; + + *inptr = in; + return 1; +} + +struct tlsa_field { + void *var; + const char *name; + ossl_ssize_t (*parser)(const char **, void *); +}; + +static int tlsa_import_rr(SSL *con, const char *rrdata) +{ + /* Not necessary to re-init these values; the "parsers" do that. */ + static uint8_t usage; + static uint8_t selector; + static uint8_t mtype; + static unsigned char *data; + static struct tlsa_field tlsa_fields[] = { + { &usage, "usage", checked_uint8 }, + { &selector, "selector", checked_uint8 }, + { &mtype, "mtype", checked_uint8 }, + { &data, "data", hexdecode }, + { NULL, } + }; + struct tlsa_field *f; + int ret; + const char *cp = rrdata; + ossl_ssize_t len = 0; + + for (f = tlsa_fields; f->var; ++f) { + /* Returns number of bytes produced, advances cp to next field */ + if ((len = f->parser(&cp, f->var)) <= 0) { + BIO_printf(bio_err, "%s: warning: bad TLSA %s field in: %s\n", + prog, f->name, rrdata); + return 0; + } + } + /* The data field is last, so len is its length */ + ret = SSL_dane_tlsa_add(con, usage, selector, mtype, data, len); + OPENSSL_free(data); + + if (ret == 0) { + ERR_print_errors(bio_err); + BIO_printf(bio_err, "%s: warning: unusable TLSA rrdata: %s\n", + prog, rrdata); + return 0; + } + if (ret < 0) { + ERR_print_errors(bio_err); + BIO_printf(bio_err, "%s: warning: error loading TLSA rrdata: %s\n", + prog, rrdata); + return 0; + } + return ret; +} + +static int tlsa_import_rrset(SSL *con, STACK_OF(OPENSSL_STRING) *rrset) +{ + int num = sk_OPENSSL_STRING_num(rrset); + int count = 0; + int i; + + for (i = 0; i < num; ++i) { + char *rrdata = sk_OPENSSL_STRING_value(rrset, i); + if (tlsa_import_rr(con, rrdata) > 0) + ++count; + } + return count > 0; +} + +typedef enum OPTION_choice { + OPT_ERR = -1, OPT_EOF = 0, OPT_HELP, + OPT_4, OPT_6, OPT_HOST, OPT_PORT, OPT_CONNECT, OPT_UNIX, + OPT_XMPPHOST, OPT_VERIFY, + OPT_CERT, OPT_CRL, OPT_CRL_DOWNLOAD, OPT_SESS_OUT, OPT_SESS_IN, + OPT_CERTFORM, OPT_CRLFORM, OPT_DCERTFORM, OPT_DCERT, OPT_DKEYFORM, + OPT_DKEY, OPT_VERIFY_RET_ERROR, OPT_VERIFY_QUIET, + OPT_BRIEF, OPT_PREXIT, OPT_CRLF, OPT_QUIET, OPT_NBIO, + OPT_SSL_CLIENT_ENGINE, OPT_RAND, OPT_IGN_EOF, OPT_NO_IGN_EOF, + OPT_DEBUG, OPT_TLSEXTDEBUG, OPT_STATUS, OPT_WDEBUG, + OPT_MSG, OPT_MSGFILE, OPT_ENGINE, OPT_TRACE, OPT_SECURITY_DEBUG, + OPT_SECURITY_DEBUG_VERBOSE, OPT_SHOWCERTS, OPT_NBIO_TEST, OPT_STATE, +#ifndef OPENSSL_NO_PSK + OPT_PSK_IDENTITY, OPT_PSK, +#endif +#ifndef OPENSSL_NO_SRP + OPT_SRPUSER, OPT_SRPPASS, OPT_SRP_STRENGTH, OPT_SRP_LATEUSER, + OPT_SRP_MOREGROUPS, +#endif + OPT_SSL3, OPT_CONFIG, OPT_SSL_CONFIG, + OPT_TLS1_2, OPT_TLS1_1, OPT_TLS1, OPT_DTLS, OPT_DTLS1, + OPT_GMTLS, + OPT_DTLS1_2, OPT_TIMEOUT, OPT_MTU, OPT_KEYFORM, OPT_PASS, + OPT_CERT_CHAIN, OPT_CAPATH, OPT_NOCAPATH, OPT_CHAINCAPATH, + OPT_VERIFYCAPATH, + OPT_KEY, OPT_RECONNECT, OPT_BUILD_CHAIN, OPT_CAFILE, OPT_NOCAFILE, + OPT_CHAINCAFILE, OPT_VERIFYCAFILE, OPT_NEXTPROTONEG, OPT_ALPN, + OPT_SERVERINFO, OPT_STARTTLS, OPT_SERVERNAME, + OPT_USE_SRTP, OPT_KEYMATEXPORT, OPT_KEYMATEXPORTLEN, OPT_SMTPHOST, + OPT_ASYNC, OPT_SPLIT_SEND_FRAG, OPT_MAX_PIPELINES, OPT_READ_BUF, + OPT_V_ENUM, + OPT_X_ENUM, + OPT_S_ENUM, + OPT_FALLBACKSCSV, OPT_NOCMDS, OPT_PROXY, OPT_DANE_TLSA_DOMAIN, +#ifndef OPENSSL_NO_CT + OPT_CT, OPT_NOCT, OPT_CTLOG_FILE, +#endif + OPT_DANE_TLSA_RRDATA, OPT_DANE_EE_NO_NAME +} OPTION_CHOICE; + +OPTIONS s_client_options[] = { + {"help", OPT_HELP, '-', "Display this summary"}, + {"host", OPT_HOST, 's', "Use -connect instead"}, + {"port", OPT_PORT, 'p', "Use -connect instead"}, + {"connect", OPT_CONNECT, 's', + "TCP/IP where to connect (default is :" PORT ")"}, + {"proxy", OPT_PROXY, 's', + "Connect to via specified proxy to the real server"}, +#ifdef AF_UNIX + {"unix", OPT_UNIX, 's', "Connect over the specified Unix-domain socket"}, +#endif + {"4", OPT_4, '-', "Use IPv4 only"}, +#ifdef AF_INET6 + {"6", OPT_6, '-', "Use IPv6 only"}, +#endif + {"verify", OPT_VERIFY, 'p', "Turn on peer certificate verification"}, + {"cert", OPT_CERT, '<', "Certificate file to use, PEM format assumed"}, + {"certform", OPT_CERTFORM, 'F', + "Certificate format (PEM or DER) PEM default"}, + {"key", OPT_KEY, 's', "Private key file to use, if not in -cert file"}, + {"keyform", OPT_KEYFORM, 'E', "Key format (PEM, DER or engine) PEM default"}, + {"pass", OPT_PASS, 's', "Private key file pass phrase source"}, + {"dcert", OPT_DCERT, '<', "Second certificate file to use (usually for DSA)"}, + {"dcertform", OPT_DCERTFORM, 'F', "Second certificate format (PEM or DER) PEM default"}, + {"dkey", OPT_DKEY, 's', "Second private key file to use (usually for DSA)"}, + {"dkeyform", OPT_DKEYFORM, 'F', "Second key format (PEM, DER or ENGINE) PEM default"}, + {"CApath", OPT_CAPATH, '/', "PEM format directory of CA's"}, + {"CAfile", OPT_CAFILE, '<', "PEM format file of CA's"}, + {"no-CAfile", OPT_NOCAFILE, '-', + "Do not load the default certificates file"}, + {"no-CApath", OPT_NOCAPATH, '-', + "Do not load certificates from the default certificates directory"}, + {"dane_tlsa_domain", OPT_DANE_TLSA_DOMAIN, 's', "DANE TLSA base domain"}, + {"dane_tlsa_rrdata", OPT_DANE_TLSA_RRDATA, 's', + "DANE TLSA rrdata presentation form"}, + {"dane_ee_no_namechecks", OPT_DANE_EE_NO_NAME, '-', + "Disable name checks when matching DANE-EE(3) TLSA records"}, + {"reconnect", OPT_RECONNECT, '-', + "Drop and re-make the connection with the same Session-ID"}, + {"showcerts", OPT_SHOWCERTS, '-', "Show all certificates in the chain"}, + {"debug", OPT_DEBUG, '-', "Extra output"}, + {"msg", OPT_MSG, '-', "Show protocol messages"}, + {"msgfile", OPT_MSGFILE, '>', + "File to send output of -msg or -trace, instead of stdout"}, + {"nbio_test", OPT_NBIO_TEST, '-', "More ssl protocol testing"}, + {"state", OPT_STATE, '-', "Print the ssl states"}, + {"crlf", OPT_CRLF, '-', "Convert LF from terminal into CRLF"}, + {"quiet", OPT_QUIET, '-', "No s_client output"}, + {"ign_eof", OPT_IGN_EOF, '-', "Ignore input eof (default when -quiet)"}, + {"no_ign_eof", OPT_NO_IGN_EOF, '-', "Don't ignore input eof"}, + {"starttls", OPT_STARTTLS, 's', + "Use the appropriate STARTTLS command before starting TLS"}, + {"xmpphost", OPT_XMPPHOST, 's', + "Host to use with \"-starttls xmpp[-server]\""}, + {"rand", OPT_RAND, 's', + "Load the file(s) into the random number generator"}, + {"sess_out", OPT_SESS_OUT, '>', "File to write SSL session to"}, + {"sess_in", OPT_SESS_IN, '<', "File to read SSL session from"}, + {"use_srtp", OPT_USE_SRTP, 's', + "Offer SRTP key management with a colon-separated profile list"}, + {"keymatexport", OPT_KEYMATEXPORT, 's', + "Export keying material using label"}, + {"keymatexportlen", OPT_KEYMATEXPORTLEN, 'p', + "Export len bytes of keying material (default 20)"}, + {"fallback_scsv", OPT_FALLBACKSCSV, '-', "Send the fallback SCSV"}, + {"name", OPT_SMTPHOST, 's', "Hostname to use for \"-starttls smtp\""}, + {"CRL", OPT_CRL, '<', "CRL file to use"}, + {"crl_download", OPT_CRL_DOWNLOAD, '-', "Download CRL from distribution points"}, + {"CRLform", OPT_CRLFORM, 'F', "CRL format (PEM or DER) PEM is default"}, + {"verify_return_error", OPT_VERIFY_RET_ERROR, '-', + "Close connection on verification error"}, + {"verify_quiet", OPT_VERIFY_QUIET, '-', "Restrict verify output to errors"}, + {"brief", OPT_BRIEF, '-', + "Restrict output to brief summary of connection parameters"}, + {"prexit", OPT_PREXIT, '-', + "Print session information when the program exits"}, + {"security_debug", OPT_SECURITY_DEBUG, '-', + "Enable security debug messages"}, + {"security_debug_verbose", OPT_SECURITY_DEBUG_VERBOSE, '-', + "Output more security debug output"}, + {"cert_chain", OPT_CERT_CHAIN, '<', + "Certificate chain file (in PEM format)"}, + {"chainCApath", OPT_CHAINCAPATH, '/', + "Use dir as certificate store path to build CA certificate chain"}, + {"verifyCApath", OPT_VERIFYCAPATH, '/', + "Use dir as certificate store path to verify CA certificate"}, + {"build_chain", OPT_BUILD_CHAIN, '-', "Build certificate chain"}, + {"chainCAfile", OPT_CHAINCAFILE, '<', + "CA file for certificate chain (PEM format)"}, + {"verifyCAfile", OPT_VERIFYCAFILE, '<', + "CA file for certificate verification (PEM format)"}, + {"nocommands", OPT_NOCMDS, '-', "Do not use interactive command letters"}, + {"servername", OPT_SERVERNAME, 's', + "Set TLS extension servername in ClientHello"}, + {"tlsextdebug", OPT_TLSEXTDEBUG, '-', + "Hex dump of all TLS extensions received"}, +#ifndef OPENSSL_NO_OCSP + {"status", OPT_STATUS, '-', "Request certificate status from server"}, +#endif + {"serverinfo", OPT_SERVERINFO, 's', + "types Send empty ClientHello extensions (comma-separated numbers)"}, + {"alpn", OPT_ALPN, 's', + "Enable ALPN extension, considering named protocols supported (comma-separated list)"}, + {"async", OPT_ASYNC, '-', "Support asynchronous operation"}, + {"config", OPT_CONFIG, 's', "Use configuration file"}, + {"ssl_config", OPT_SSL_CONFIG, 's', "Use specified configuration section"}, + {"split_send_frag", OPT_SPLIT_SEND_FRAG, 'n', + "Size used to split data for encrypt pipelines"}, + {"max_pipelines", OPT_MAX_PIPELINES, 'n', + "Maximum number of encrypt/decrypt pipelines to be used"}, + {"read_buf", OPT_READ_BUF, 'n', + "Default read buffer size to be used for connections"}, + OPT_S_OPTIONS, + OPT_V_OPTIONS, + OPT_X_OPTIONS, +#ifndef OPENSSL_NO_SSL3 + {"ssl3", OPT_SSL3, '-', "Just use SSLv3"}, +#endif +#ifndef OPENSSL_NO_TLS1 + {"tls1", OPT_TLS1, '-', "Just use TLSv1"}, +#endif +#ifndef OPENSSL_NO_TLS1_1 + {"tls1_1", OPT_TLS1_1, '-', "Just use TLSv1.1"}, +#endif +#ifndef OPENSSL_NO_TLS1_2 + {"tls1_2", OPT_TLS1_2, '-', "Just use TLSv1.2"}, +#endif +#ifndef OPENSSL_NO_DTLS + {"dtls", OPT_DTLS, '-', "Use any version of DTLS"}, + {"timeout", OPT_TIMEOUT, '-', + "Enable send/receive timeout on DTLS connections"}, + {"mtu", OPT_MTU, 'p', "Set the link layer MTU"}, +#endif +#ifndef OPENSSL_NO_DTLS1 + {"dtls1", OPT_DTLS1, '-', "Just use DTLSv1"}, +#endif +#ifndef OPENSSL_NO_DTLS1_2 + {"dtls1_2", OPT_DTLS1_2, '-', "Just use DTLSv1.2"}, +#endif +#ifndef OPENSSL_NO_GMTLS + {"gmtls", OPT_GMTLS, '-', "Just use GMTLS"}, +#endif +#ifndef OPENSSL_NO_SSL_TRACE + {"trace", OPT_TRACE, '-', "Show trace output of protocol messages"}, +#endif +#ifdef WATT32 + {"wdebug", OPT_WDEBUG, '-', "WATT-32 tcp debugging"}, +#endif + {"nbio", OPT_NBIO, '-', "Use non-blocking IO"}, +#ifndef OPENSSL_NO_PSK + {"psk_identity", OPT_PSK_IDENTITY, 's', "PSK identity"}, + {"psk", OPT_PSK, 's', "PSK in hex (without 0x)"}, +#endif +#ifndef OPENSSL_NO_SRP + {"srpuser", OPT_SRPUSER, 's', "SRP authentication for 'user'"}, + {"srppass", OPT_SRPPASS, 's', "Password for 'user'"}, + {"srp_lateuser", OPT_SRP_LATEUSER, '-', + "SRP username into second ClientHello message"}, + {"srp_moregroups", OPT_SRP_MOREGROUPS, '-', + "Tolerate other than the known g N values."}, + {"srp_strength", OPT_SRP_STRENGTH, 'p', "Minimal length in bits for N"}, +#endif +#ifndef OPENSSL_NO_NEXTPROTONEG + {"nextprotoneg", OPT_NEXTPROTONEG, 's', + "Enable NPN extension, considering named protocols supported (comma-separated list)"}, +#endif +#ifndef OPENSSL_NO_ENGINE + {"engine", OPT_ENGINE, 's', "Use engine, possibly a hardware device"}, + {"ssl_client_engine", OPT_SSL_CLIENT_ENGINE, 's', + "Specify engine to be used for client certificate operations"}, +#endif +#ifndef OPENSSL_NO_CT + {"ct", OPT_CT, '-', "Request and parse SCTs (also enables OCSP stapsection)"}, + {"noct", OPT_NOCT, '-', "Do not request or parse SCTs (default)"}, + {"ctlogfile", OPT_CTLOG_FILE, '<', "CT log list CONF file"}, +#endif + {NULL, OPT_EOF, 0x00, NULL} +}; + +typedef enum PROTOCOL_choice { + PROTO_OFF, + PROTO_SMTP, + PROTO_POP3, + PROTO_IMAP, + PROTO_FTP, + PROTO_TELNET, + PROTO_XMPP, + PROTO_XMPP_SERVER, + PROTO_CONNECT, + PROTO_IRC +} PROTOCOL_CHOICE; + +static const OPT_PAIR services[] = { + {"smtp", PROTO_SMTP}, + {"pop3", PROTO_POP3}, + {"imap", PROTO_IMAP}, + {"ftp", PROTO_FTP}, + {"xmpp", PROTO_XMPP}, + {"xmpp-server", PROTO_XMPP_SERVER}, + {"telnet", PROTO_TELNET}, + {"irc", PROTO_IRC}, + {NULL, 0} +}; + +#define IS_INET_FLAG(o) \ + (o == OPT_4 || o == OPT_6 || o == OPT_HOST || o == OPT_PORT || o == OPT_CONNECT) +#define IS_UNIX_FLAG(o) (o == OPT_UNIX) + +#define IS_PROT_FLAG(o) \ + (o == OPT_SSL3 || o == OPT_TLS1 || o == OPT_TLS1_1 || o == OPT_TLS1_2 \ + || o == OPT_GMTLS \ + || o == OPT_DTLS || o == OPT_DTLS1 || o == OPT_DTLS1_2) + +/* Free |*dest| and optionally set it to a copy of |source|. */ +static void freeandcopy(char **dest, const char *source) +{ + OPENSSL_free(*dest); + *dest = NULL; + if (source != NULL) + *dest = OPENSSL_strdup(source); +} + +int s_client_main(int argc, char **argv) +{ + BIO *sbio; + EVP_PKEY *key = NULL, *dkey = NULL; + SSL *con = NULL; + SSL_CTX *ctx = NULL; + STACK_OF(X509) *chain = NULL, *dchain = NULL; + X509 *cert = NULL, *dcert = NULL; + X509_VERIFY_PARAM *vpm = NULL; + SSL_EXCERT *exc = NULL; + SSL_CONF_CTX *cctx = NULL; + STACK_OF(OPENSSL_STRING) *ssl_args = NULL; + char *dane_tlsa_domain = NULL; + STACK_OF(OPENSSL_STRING) *dane_tlsa_rrset = NULL; + int dane_ee_no_name = 0; + STACK_OF(X509_CRL) *crls = NULL; + const SSL_METHOD *meth = TLS_client_method(); + const char *CApath = NULL, *CAfile = NULL; + char *cbuf = NULL, *sbuf = NULL; + char *mbuf = NULL, *proxystr = NULL, *connectstr = NULL; + char *cert_file = NULL, *key_file = NULL, *chain_file = NULL; + char *dcert_file = NULL, *dkey_file = NULL, *dchain_file = NULL; + char *chCApath = NULL, *chCAfile = NULL, *host = NULL; + char *port = OPENSSL_strdup(PORT); + char *inrand = NULL; + char *passarg = NULL, *pass = NULL, *vfyCApath = NULL, *vfyCAfile = NULL; + char *dpassarg = NULL, *dpass = NULL; + char *sess_in = NULL, *sess_out = NULL, *crl_file = NULL, *p; + char *xmpphost = NULL; + const char *ehlo = "mail.example.com"; + struct timeval timeout, *timeoutp; + fd_set readfds, writefds; + int noCApath = 0, noCAfile = 0; + int build_chain = 0, cbuf_len, cbuf_off, cert_format = FORMAT_PEM; + int key_format = FORMAT_PEM, crlf = 0, full_log = 1, mbuf_len = 0; + int dcert_format = FORMAT_PEM, dkey_format = FORMAT_PEM; + int prexit = 0; + int sdebug = 0; + int reconnect = 0, verify = SSL_VERIFY_NONE, vpmtouched = 0; + int ret = 1, in_init = 1, i, nbio_test = 0, s = -1, k, width, state = 0; + int sbuf_len, sbuf_off, cmdletters = 1; + int socket_family = AF_UNSPEC, socket_type = SOCK_STREAM; + int starttls_proto = PROTO_OFF, crl_format = FORMAT_PEM, crl_download = 0; + int write_tty, read_tty, write_ssl, read_ssl, tty_on, ssl_pending; +#if !defined(OPENSSL_SYS_WINDOWS) && !defined(OPENSSL_SYS_MSDOS) + int at_eof = 0; +#endif + int read_buf_len = 0; + int fallback_scsv = 0; + long randamt = 0; + OPTION_CHOICE o; +#ifndef OPENSSL_NO_DTLS + int enable_timeouts = 0; + long socket_mtu = 0; +#endif +#ifndef OPENSSL_NO_ENGINE + ENGINE *ssl_client_engine = NULL; +#endif + ENGINE *e = NULL; +#if defined(OPENSSL_SYS_WINDOWS) || defined(OPENSSL_SYS_MSDOS) + struct timeval tv; +#endif + char *servername = NULL; + const char *alpn_in = NULL; + tlsextctx tlsextcbp = { NULL, 0 }; + CONF *conf = NULL; + const char *configfile = default_config_file; + const char *ssl_config = NULL; +#define MAX_SI_TYPES 100 + unsigned short serverinfo_types[MAX_SI_TYPES]; + int serverinfo_count = 0, start = 0, len; +#ifndef OPENSSL_NO_NEXTPROTONEG + const char *next_proto_neg_in = NULL; +#endif +#ifndef OPENSSL_NO_SRP + char *srppass = NULL; + int srp_lateuser = 0; + SRP_ARG srp_arg = { NULL, NULL, 0, 0, 0, 1024 }; +#endif +#ifndef OPENSSL_NO_CT + char *ctlog_file = NULL; + int ct_validation = 0; +#endif + int min_version = 0, max_version = 0, prot_opt = 0, no_prot_opt = 0; + int async = 0; + unsigned int split_send_fragment = 0; + unsigned int max_pipelines = 0; + enum { use_inet, use_unix, use_unknown } connect_type = use_unknown; + int count4or6 = 0; + int c_nbio = 0, c_msg = 0, c_ign_eof = 0, c_brief = 0; + int c_tlsextdebug = 0; +#ifndef OPENSSL_NO_OCSP + int c_status_req = 0; +#endif + BIO *bio_c_msg = NULL; + + FD_ZERO(&readfds); + FD_ZERO(&writefds); +/* Known false-positive of MemorySanitizer. */ +#if defined(__has_feature) +# if __has_feature(memory_sanitizer) + __msan_unpoison(&readfds, sizeof(readfds)); + __msan_unpoison(&writefds, sizeof(writefds)); +# endif +#endif + + prog = opt_progname(argv[0]); + c_quiet = 0; + c_debug = 0; + c_showcerts = 0; + c_nbio = 0; + vpm = X509_VERIFY_PARAM_new(); + cctx = SSL_CONF_CTX_new(); + + if (vpm == NULL || cctx == NULL) { + BIO_printf(bio_err, "%s: out of memory\n", prog); + goto end; + } + + cbuf = app_malloc(BUFSIZZ, "cbuf"); + sbuf = app_malloc(BUFSIZZ, "sbuf"); + mbuf = app_malloc(BUFSIZZ, "mbuf"); + + SSL_CONF_CTX_set_flags(cctx, SSL_CONF_FLAG_CLIENT | SSL_CONF_FLAG_CMDLINE); + + prog = opt_init(argc, argv, s_client_options); + while ((o = opt_next()) != OPT_EOF) { + /* Check for intermixing flags. */ + if (connect_type == use_unix && IS_INET_FLAG(o)) { + BIO_printf(bio_err, + "%s: Intermixed protocol flags (unix and internet domains)\n", + prog); + goto end; + } + if (connect_type == use_inet && IS_UNIX_FLAG(o)) { + BIO_printf(bio_err, + "%s: Intermixed protocol flags (internet and unix domains)\n", + prog); + goto end; + } + + if (IS_PROT_FLAG(o) && ++prot_opt > 1) { + BIO_printf(bio_err, "Cannot supply multiple protocol flags\n"); + goto end; + } + if (IS_NO_PROT_FLAG(o)) + no_prot_opt++; + if (prot_opt == 1 && no_prot_opt) { + BIO_printf(bio_err, + "Cannot supply both a protocol flag and '-no_'\n"); + goto end; + } + + switch (o) { + case OPT_EOF: + case OPT_ERR: + opthelp: + BIO_printf(bio_err, "%s: Use -help for summary.\n", prog); + goto end; + case OPT_HELP: + opt_help(s_client_options); + ret = 0; + goto end; + case OPT_4: + connect_type = use_inet; + socket_family = AF_INET; + count4or6++; + break; +#ifdef AF_INET6 + case OPT_6: + connect_type = use_inet; + socket_family = AF_INET6; + count4or6++; + break; +#endif + case OPT_HOST: + connect_type = use_inet; + freeandcopy(&host, opt_arg()); + break; + case OPT_PORT: + connect_type = use_inet; + freeandcopy(&port, opt_arg()); + break; + case OPT_CONNECT: + connect_type = use_inet; + freeandcopy(&connectstr, opt_arg()); + break; + case OPT_PROXY: + proxystr = opt_arg(); + starttls_proto = PROTO_CONNECT; + break; +#ifdef AF_UNIX + case OPT_UNIX: + connect_type = use_unix; + socket_family = AF_UNIX; + freeandcopy(&host, opt_arg()); + break; +#endif + case OPT_XMPPHOST: + xmpphost = opt_arg(); + break; + case OPT_SMTPHOST: + ehlo = opt_arg(); + break; + case OPT_VERIFY: + verify = SSL_VERIFY_PEER; + verify_args.depth = atoi(opt_arg()); + if (!c_quiet) + BIO_printf(bio_err, "verify depth is %d\n", verify_args.depth); + break; + case OPT_CERT: + cert_file = opt_arg(); + break; + case OPT_CRL: + crl_file = opt_arg(); + break; + case OPT_CRL_DOWNLOAD: + crl_download = 1; + break; + case OPT_SESS_OUT: + sess_out = opt_arg(); + break; + case OPT_SESS_IN: + sess_in = opt_arg(); + break; + case OPT_CERTFORM: + if (!opt_format(opt_arg(), OPT_FMT_PEMDER, &cert_format)) + goto opthelp; + break; + case OPT_CRLFORM: + if (!opt_format(opt_arg(), OPT_FMT_PEMDER, &crl_format)) + goto opthelp; + break; + case OPT_DCERTFORM: + if (!opt_format(opt_arg(), OPT_FMT_PEMDER, &dcert_format)) + goto opthelp; + break; + case OPT_DCERT: + dcert_file = opt_arg(); + break; + case OPT_DKEYFORM: + if (!opt_format(opt_arg(), OPT_FMT_PEMDER, &dkey_format)) + goto opthelp; + break; + case OPT_DKEY: + dkey_file = opt_arg(); + break; + case OPT_VERIFY_RET_ERROR: + verify_args.return_error = 1; + break; + case OPT_VERIFY_QUIET: + verify_args.quiet = 1; + break; + case OPT_BRIEF: + c_brief = verify_args.quiet = c_quiet = 1; + break; + case OPT_S_CASES: + if (ssl_args == NULL) + ssl_args = sk_OPENSSL_STRING_new_null(); + if (ssl_args == NULL + || !sk_OPENSSL_STRING_push(ssl_args, opt_flag()) + || !sk_OPENSSL_STRING_push(ssl_args, opt_arg())) { + BIO_printf(bio_err, "%s: Memory allocation failure\n", prog); + goto end; + } + break; + case OPT_V_CASES: + if (!opt_verify(o, vpm)) + goto end; + vpmtouched++; + break; + case OPT_X_CASES: + if (!args_excert(o, &exc)) + goto end; + break; + case OPT_PREXIT: + prexit = 1; + break; + case OPT_CRLF: + crlf = 1; + break; + case OPT_QUIET: + c_quiet = c_ign_eof = 1; + break; + case OPT_NBIO: + c_nbio = 1; + break; + case OPT_NOCMDS: + cmdletters = 0; + break; + case OPT_ENGINE: + e = setup_engine(opt_arg(), 1); + break; + case OPT_SSL_CLIENT_ENGINE: +#ifndef OPENSSL_NO_ENGINE + ssl_client_engine = ENGINE_by_id(opt_arg()); + if (ssl_client_engine == NULL) { + BIO_printf(bio_err, "Error getting client auth engine\n"); + goto opthelp; + } +#endif + break; + case OPT_RAND: + inrand = opt_arg(); + break; + case OPT_IGN_EOF: + c_ign_eof = 1; + break; + case OPT_NO_IGN_EOF: + c_ign_eof = 0; + break; + case OPT_DEBUG: + c_debug = 1; + break; + case OPT_TLSEXTDEBUG: + c_tlsextdebug = 1; + break; + case OPT_STATUS: +#ifndef OPENSSL_NO_OCSP + c_status_req = 1; +#endif + break; + case OPT_WDEBUG: +#ifdef WATT32 + dbug_init(); +#endif + break; + case OPT_MSG: + c_msg = 1; + break; + case OPT_MSGFILE: + bio_c_msg = BIO_new_file(opt_arg(), "w"); + break; + case OPT_TRACE: +#ifndef OPENSSL_NO_SSL_TRACE + c_msg = 2; +#endif + break; + case OPT_SECURITY_DEBUG: + sdebug = 1; + break; + case OPT_SECURITY_DEBUG_VERBOSE: + sdebug = 2; + break; + case OPT_SHOWCERTS: + c_showcerts = 1; + break; + case OPT_NBIO_TEST: + nbio_test = 1; + break; + case OPT_STATE: + state = 1; + break; +#ifndef OPENSSL_NO_PSK + case OPT_PSK_IDENTITY: + psk_identity = opt_arg(); + break; + case OPT_PSK: + for (p = psk_key = opt_arg(); *p; p++) { + if (isxdigit(_UC(*p))) + continue; + BIO_printf(bio_err, "Not a hex number '%s'\n", psk_key); + goto end; + } + break; +#endif +#ifndef OPENSSL_NO_SRP + case OPT_SRPUSER: + srp_arg.srplogin = opt_arg(); + if (min_version < TLS1_VERSION) + min_version = TLS1_VERSION; + break; + case OPT_SRPPASS: + srppass = opt_arg(); + if (min_version < TLS1_VERSION) + min_version = TLS1_VERSION; + break; + case OPT_SRP_STRENGTH: + srp_arg.strength = atoi(opt_arg()); + BIO_printf(bio_err, "SRP minimal length for N is %d\n", + srp_arg.strength); + if (min_version < TLS1_VERSION) + min_version = TLS1_VERSION; + break; + case OPT_SRP_LATEUSER: + srp_lateuser = 1; + if (min_version < TLS1_VERSION) + min_version = TLS1_VERSION; + break; + case OPT_SRP_MOREGROUPS: + srp_arg.amp = 1; + if (min_version < TLS1_VERSION) + min_version = TLS1_VERSION; + break; +#endif + case OPT_CONFIG: + configfile = opt_arg(); + break; + case OPT_SSL_CONFIG: + ssl_config = opt_arg(); + break; + case OPT_SSL3: + min_version = SSL3_VERSION; + max_version = SSL3_VERSION; + break; + case OPT_TLS1_2: + min_version = TLS1_2_VERSION; + max_version = TLS1_2_VERSION; + break; + case OPT_TLS1_1: + min_version = TLS1_1_VERSION; + max_version = TLS1_1_VERSION; + break; + case OPT_TLS1: + min_version = TLS1_VERSION; + max_version = TLS1_VERSION; + break; + case OPT_DTLS: +#ifndef OPENSSL_NO_DTLS + meth = DTLS_client_method(); + socket_type = SOCK_DGRAM; +#endif + break; + case OPT_DTLS1: +#ifndef OPENSSL_NO_DTLS1 + meth = DTLS_client_method(); + min_version = DTLS1_VERSION; + max_version = DTLS1_VERSION; + socket_type = SOCK_DGRAM; +#endif + break; + case OPT_DTLS1_2: +#ifndef OPENSSL_NO_DTLS1_2 + meth = DTLS_client_method(); + min_version = DTLS1_2_VERSION; + max_version = DTLS1_2_VERSION; + socket_type = SOCK_DGRAM; +#endif + break; + case OPT_GMTLS: +#ifndef OPENSSL_NO_GMTLS + meth = GMTLS_client_method(); + //min_version = GMTLS_VERSION; + //max_version = GMTLS_VERSION; +#endif + break; + case OPT_TIMEOUT: +#ifndef OPENSSL_NO_DTLS + enable_timeouts = 1; +#endif + break; + case OPT_MTU: +#ifndef OPENSSL_NO_DTLS + socket_mtu = atol(opt_arg()); +#endif + break; + case OPT_FALLBACKSCSV: + fallback_scsv = 1; + break; + case OPT_KEYFORM: + if (!opt_format(opt_arg(), OPT_FMT_PDE, &key_format)) + goto opthelp; + break; + case OPT_PASS: + passarg = opt_arg(); + break; + case OPT_CERT_CHAIN: + chain_file = opt_arg(); + break; + case OPT_KEY: + key_file = opt_arg(); + break; + case OPT_RECONNECT: + reconnect = 5; + break; + case OPT_CAPATH: + CApath = opt_arg(); + break; + case OPT_NOCAPATH: + noCApath = 1; + break; + case OPT_CHAINCAPATH: + chCApath = opt_arg(); + break; + case OPT_VERIFYCAPATH: + vfyCApath = opt_arg(); + break; + case OPT_BUILD_CHAIN: + build_chain = 1; + break; + case OPT_CAFILE: + CAfile = opt_arg(); + break; + case OPT_NOCAFILE: + noCAfile = 1; + break; +#ifndef OPENSSL_NO_CT + case OPT_NOCT: + ct_validation = 0; + break; + case OPT_CT: + ct_validation = 1; + break; + case OPT_CTLOG_FILE: + ctlog_file = opt_arg(); + break; +#endif + case OPT_CHAINCAFILE: + chCAfile = opt_arg(); + break; + case OPT_VERIFYCAFILE: + vfyCAfile = opt_arg(); + break; + case OPT_DANE_TLSA_DOMAIN: + dane_tlsa_domain = opt_arg(); + break; + case OPT_DANE_TLSA_RRDATA: + if (dane_tlsa_rrset == NULL) + dane_tlsa_rrset = sk_OPENSSL_STRING_new_null(); + if (dane_tlsa_rrset == NULL || + !sk_OPENSSL_STRING_push(dane_tlsa_rrset, opt_arg())) { + BIO_printf(bio_err, "%s: Memory allocation failure\n", prog); + goto end; + } + break; + case OPT_DANE_EE_NO_NAME: + dane_ee_no_name = 1; + break; + case OPT_NEXTPROTONEG: +#ifndef OPENSSL_NO_NEXTPROTONEG + next_proto_neg_in = opt_arg(); +#endif + break; + case OPT_ALPN: + alpn_in = opt_arg(); + break; + case OPT_SERVERINFO: + p = opt_arg(); + len = strlen(p); + for (start = 0, i = 0; i <= len; ++i) { + if (i == len || p[i] == ',') { + serverinfo_types[serverinfo_count] = atoi(p + start); + if (++serverinfo_count == MAX_SI_TYPES) + break; + start = i + 1; + } + } + break; + case OPT_STARTTLS: + if (!opt_pair(opt_arg(), services, &starttls_proto)) + goto end; + break; + case OPT_SERVERNAME: + servername = opt_arg(); + break; + case OPT_USE_SRTP: + srtp_profiles = opt_arg(); + break; + case OPT_KEYMATEXPORT: + keymatexportlabel = opt_arg(); + break; + case OPT_KEYMATEXPORTLEN: + keymatexportlen = atoi(opt_arg()); + break; + case OPT_ASYNC: + async = 1; + break; + case OPT_SPLIT_SEND_FRAG: + split_send_fragment = atoi(opt_arg()); + if (split_send_fragment == 0) { + /* + * Not allowed - set to a deliberately bad value so we get an + * error message below + */ + split_send_fragment = SSL3_RT_MAX_PLAIN_LENGTH + 1; + } + break; + case OPT_MAX_PIPELINES: + max_pipelines = atoi(opt_arg()); + break; + case OPT_READ_BUF: + read_buf_len = atoi(opt_arg()); + break; + } + } + if (count4or6 >= 2) { + BIO_printf(bio_err, "%s: Can't use both -4 and -6\n", prog); + goto opthelp; + } + argc = opt_num_rest(); + if (argc != 0) + goto opthelp; + + if (e) + BIO_printf(bio_err, "Using configuration from %s\n", configfile); + if ((conf = app_load_config(configfile)) == NULL) + goto end; + if (configfile != default_config_file && !app_load_modules(conf)) + goto end; + + if (proxystr) { + int res; + char *tmp_host = host, *tmp_port = port; + if (connectstr == NULL) { + BIO_printf(bio_err, "%s: -proxy requires use of -connect\n", prog); + goto opthelp; + } + res = BIO_parse_hostserv(proxystr, &host, &port, BIO_PARSE_PRIO_HOST); + if (tmp_host != host) + OPENSSL_free(tmp_host); + if (tmp_port != port) + OPENSSL_free(tmp_port); + if (!res) { + BIO_printf(bio_err, + "%s: -proxy argument malformed or ambiguous\n", prog); + goto end; + } + } else { + int res = 1; + char *tmp_host = host, *tmp_port = port; + if (connectstr != NULL) + res = BIO_parse_hostserv(connectstr, &host, &port, + BIO_PARSE_PRIO_HOST); + if (tmp_host != host) + OPENSSL_free(tmp_host); + if (tmp_port != port) + OPENSSL_free(tmp_port); + if (!res) { + BIO_printf(bio_err, + "%s: -connect argument malformed or ambiguous\n", + prog); + goto end; + } + } + + if (socket_family == AF_UNIX && socket_type != SOCK_STREAM) { + BIO_printf(bio_err, + "Can't use unix sockets and datagrams together\n"); + goto end; + } + + if (split_send_fragment > SSL3_RT_MAX_PLAIN_LENGTH) { + BIO_printf(bio_err, "Bad split send fragment size\n"); + goto end; + } + + if (max_pipelines > SSL_MAX_PIPELINES) { + BIO_printf(bio_err, "Bad max pipelines value\n"); + goto end; + } + +#if !defined(OPENSSL_NO_NEXTPROTONEG) + next_proto.status = -1; + if (next_proto_neg_in) { + next_proto.data = + next_protos_parse(&next_proto.len, next_proto_neg_in); + if (next_proto.data == NULL) { + BIO_printf(bio_err, "Error parsing -nextprotoneg argument\n"); + goto end; + } + } else + next_proto.data = NULL; +#endif + + if (!app_passwd(passarg, dpassarg, &pass, &dpass)) { + BIO_printf(bio_err, "Error getting password\n"); + goto end; + } + + if (key_file == NULL) + key_file = cert_file; + + if (key_file) { + key = load_key(key_file, key_format, 0, pass, e, + "client certificate private key file"); + if (key == NULL) { + ERR_print_errors(bio_err); + goto end; + } + } + + if (cert_file) { + cert = load_cert(cert_file, cert_format, "client certificate file"); + if (cert == NULL) { + ERR_print_errors(bio_err); + goto end; + } + } + + if (chain_file) { + if (!load_certs(chain_file, &chain, FORMAT_PEM, NULL, + "client certificate chain")) + goto end; + } + + if (crl_file) { + X509_CRL *crl; + crl = load_crl(crl_file, crl_format); + if (crl == NULL) { + BIO_puts(bio_err, "Error loading CRL\n"); + ERR_print_errors(bio_err); + goto end; + } + crls = sk_X509_CRL_new_null(); + if (crls == NULL || !sk_X509_CRL_push(crls, crl)) { + BIO_puts(bio_err, "Error adding CRL\n"); + ERR_print_errors(bio_err); + X509_CRL_free(crl); + goto end; + } + } + + if (!load_excert(&exc)) + goto end; + + if (!app_RAND_load_file(NULL, 1) && inrand == NULL + && !RAND_status()) { + BIO_printf(bio_err, + "warning, not much extra random data, consider using the -rand option\n"); + } + if (inrand != NULL) { + randamt = app_RAND_load_files(inrand); + BIO_printf(bio_err, "%ld semi-random bytes loaded\n", randamt); + } + + if (dcert_file) { + if (dkey_file == NULL) { + dkey_file = dcert_file; + } + + dkey = load_key(dkey_file, dkey_format, 0, dpass, e, + "second certificate private key file"); + if (!dkey) { + ERR_print_errors(bio_err); + goto end; + } + + dcert = load_cert(dcert_file, dcert_format, + "second server certificate file"); + if (!dcert) { + ERR_print_errors(bio_err); + goto end; + } + } + + if (bio_c_out == NULL) { + if (c_quiet && !c_debug) { + bio_c_out = BIO_new(BIO_s_null()); + if (c_msg && !bio_c_msg) + bio_c_msg = dup_bio_out(FORMAT_TEXT); + } else if (bio_c_out == NULL) + bio_c_out = dup_bio_out(FORMAT_TEXT); + } +#ifndef OPENSSL_NO_SRP + if (!app_passwd(srppass, NULL, &srp_arg.srppassin, NULL)) { + BIO_printf(bio_err, "Error getting password\n"); + goto end; + } +#endif + + ctx = SSL_CTX_new(meth); + if (ctx == NULL) { + ERR_print_errors(bio_err); + goto end; + } + + if (sdebug) + ssl_ctx_security_debug(ctx, sdebug); + + if (ssl_config) { + if (SSL_CTX_config(ctx, ssl_config) == 0) { + BIO_printf(bio_err, "Error using configuration \"%s\"\n", + ssl_config); + ERR_print_errors(bio_err); + goto end; + } + } + + if (SSL_CTX_set_min_proto_version(ctx, min_version) == 0) + goto end; + if (SSL_CTX_set_max_proto_version(ctx, max_version) == 0) + goto end; + + if (vpmtouched && !SSL_CTX_set1_param(ctx, vpm)) { + BIO_printf(bio_err, "Error setting verify params\n"); + ERR_print_errors(bio_err); + goto end; + } + + if (async) { + SSL_CTX_set_mode(ctx, SSL_MODE_ASYNC); + } + if (split_send_fragment > 0) { + SSL_CTX_set_split_send_fragment(ctx, split_send_fragment); + } + if (max_pipelines > 0) { + SSL_CTX_set_max_pipelines(ctx, max_pipelines); + } + + if (read_buf_len > 0) { + SSL_CTX_set_default_read_buffer_len(ctx, read_buf_len); + } + + if (!config_ctx(cctx, ssl_args, ctx)) + goto end; + + if (!ssl_load_stores(ctx, vfyCApath, vfyCAfile, chCApath, chCAfile, + crls, crl_download)) { + BIO_printf(bio_err, "Error loading store locations\n"); + ERR_print_errors(bio_err); + goto end; + } +#ifndef OPENSSL_NO_ENGINE + if (ssl_client_engine) { + if (!SSL_CTX_set_client_cert_engine(ctx, ssl_client_engine)) { + BIO_puts(bio_err, "Error setting client auth engine\n"); + ERR_print_errors(bio_err); + ENGINE_free(ssl_client_engine); + goto end; + } + ENGINE_free(ssl_client_engine); + } +#endif + +#ifndef OPENSSL_NO_PSK + if (psk_key != NULL) { + if (c_debug) + BIO_printf(bio_c_out, "PSK key given, setting client callback\n"); + SSL_CTX_set_psk_client_callback(ctx, psk_client_cb); + } +#endif +#ifndef OPENSSL_NO_SRTP + if (srtp_profiles != NULL) { + /* Returns 0 on success! */ + if (SSL_CTX_set_tlsext_use_srtp(ctx, srtp_profiles) != 0) { + BIO_printf(bio_err, "Error setting SRTP profile\n"); + ERR_print_errors(bio_err); + goto end; + } + } +#endif + + if (exc) + ssl_ctx_set_excert(ctx, exc); + +#if !defined(OPENSSL_NO_NEXTPROTONEG) + if (next_proto.data) + SSL_CTX_set_next_proto_select_cb(ctx, next_proto_cb, &next_proto); +#endif + if (alpn_in) { + size_t alpn_len; + unsigned char *alpn = next_protos_parse(&alpn_len, alpn_in); + + if (alpn == NULL) { + BIO_printf(bio_err, "Error parsing -alpn argument\n"); + goto end; + } + /* Returns 0 on success! */ + if (SSL_CTX_set_alpn_protos(ctx, alpn, alpn_len) != 0) { + BIO_printf(bio_err, "Error setting ALPN\n"); + goto end; + } + OPENSSL_free(alpn); + } + + for (i = 0; i < serverinfo_count; i++) { + if (!SSL_CTX_add_client_custom_ext(ctx, + serverinfo_types[i], + NULL, NULL, NULL, + serverinfo_cli_parse_cb, NULL)) { + BIO_printf(bio_err, + "Warning: Unable to add custom extension %u, skipping\n", + serverinfo_types[i]); + } + } + + if (state) + SSL_CTX_set_info_callback(ctx, apps_ssl_info_callback); + +#ifndef OPENSSL_NO_CT + /* Enable SCT processing, without early connection termination */ + if (ct_validation && + !SSL_CTX_enable_ct(ctx, SSL_CT_VALIDATION_PERMISSIVE)) { + ERR_print_errors(bio_err); + goto end; + } + + if (!ctx_set_ctlog_list_file(ctx, ctlog_file)) { + if (ct_validation) { + ERR_print_errors(bio_err); + goto end; + } + + /* + * If CT validation is not enabled, the log list isn't needed so don't + * show errors or abort. We try to load it regardless because then we + * can show the names of the logs any SCTs came from (SCTs may be seen + * even with validation disabled). + */ + ERR_clear_error(); + } +#endif + + SSL_CTX_set_verify(ctx, verify, verify_callback); + + if (!ctx_set_verify_locations(ctx, CAfile, CApath, noCAfile, noCApath)) { + ERR_print_errors(bio_err); + goto end; + } + + ssl_ctx_add_crls(ctx, crls, crl_download); + + if (!set_cert_key_stuff(ctx, cert, key, chain, build_chain)) + goto end; + + if (dcert != NULL) { + if (!set_cert_key_stuff(ctx, dcert, dkey, dchain, build_chain)) { + goto end; + } + } + + if (servername != NULL) { + tlsextcbp.biodebug = bio_err; + SSL_CTX_set_tlsext_servername_callback(ctx, ssl_servername_cb); + SSL_CTX_set_tlsext_servername_arg(ctx, &tlsextcbp); + } +# ifndef OPENSSL_NO_SRP + if (srp_arg.srplogin) { + if (!srp_lateuser && !SSL_CTX_set_srp_username(ctx, srp_arg.srplogin)) { + BIO_printf(bio_err, "Unable to set SRP username\n"); + goto end; + } + srp_arg.msg = c_msg; + srp_arg.debug = c_debug; + SSL_CTX_set_srp_cb_arg(ctx, &srp_arg); + SSL_CTX_set_srp_client_pwd_callback(ctx, ssl_give_srp_client_pwd_cb); + SSL_CTX_set_srp_strength(ctx, srp_arg.strength); + if (c_msg || c_debug || srp_arg.amp == 0) + SSL_CTX_set_srp_verify_param_callback(ctx, + ssl_srp_verify_param_cb); + } +# endif + + if (dane_tlsa_domain != NULL) { + if (SSL_CTX_dane_enable(ctx) <= 0) { + BIO_printf(bio_err, + "%s: Error enabling DANE TLSA authentication.\n", + prog); + ERR_print_errors(bio_err); + goto end; + } + } + + con = SSL_new(ctx); + if (sess_in) { + SSL_SESSION *sess; + BIO *stmp = BIO_new_file(sess_in, "r"); + if (!stmp) { + BIO_printf(bio_err, "Can't open session file %s\n", sess_in); + ERR_print_errors(bio_err); + goto end; + } + sess = PEM_read_bio_SSL_SESSION(stmp, NULL, 0, NULL); + BIO_free(stmp); + if (!sess) { + BIO_printf(bio_err, "Can't open session file %s\n", sess_in); + ERR_print_errors(bio_err); + goto end; + } + if (!SSL_set_session(con, sess)) { + BIO_printf(bio_err, "Can't set session\n"); + ERR_print_errors(bio_err); + goto end; + } + SSL_SESSION_free(sess); + } + + if (fallback_scsv) + SSL_set_mode(con, SSL_MODE_SEND_FALLBACK_SCSV); + + if (servername != NULL) { + if (!SSL_set_tlsext_host_name(con, servername)) { + BIO_printf(bio_err, "Unable to set TLS servername extension.\n"); + ERR_print_errors(bio_err); + goto end; + } + } + + if (dane_tlsa_domain != NULL) { + if (SSL_dane_enable(con, dane_tlsa_domain) <= 0) { + BIO_printf(bio_err, "%s: Error enabling DANE TLSA " + "authentication.\n", prog); + ERR_print_errors(bio_err); + goto end; + } + if (dane_tlsa_rrset == NULL) { + BIO_printf(bio_err, "%s: DANE TLSA authentication requires at " + "least one -dane_tlsa_rrdata option.\n", prog); + goto end; + } + if (tlsa_import_rrset(con, dane_tlsa_rrset) <= 0) { + BIO_printf(bio_err, "%s: Failed to import any TLSA " + "records.\n", prog); + goto end; + } + if (dane_ee_no_name) + SSL_dane_set_flags(con, DANE_FLAG_NO_DANE_EE_NAMECHECKS); + } else if (dane_tlsa_rrset != NULL) { + BIO_printf(bio_err, "%s: DANE TLSA authentication requires the " + "-dane_tlsa_domain option.\n", prog); + goto end; + } + + re_start: + if (init_client(&s, host, port, socket_family, socket_type) == 0) { + BIO_printf(bio_err, "connect:errno=%d\n", get_last_socket_error()); + BIO_closesocket(s); + goto end; + } + BIO_printf(bio_c_out, "CONNECTED(%08X)\n", s); + + if (c_nbio) { + if (!BIO_socket_nbio(s, 1)) { + ERR_print_errors(bio_err); + goto end; + } + BIO_printf(bio_c_out, "Turned on non blocking io\n"); + } +#ifndef OPENSSL_NO_DTLS + if (socket_type == SOCK_DGRAM) { + union BIO_sock_info_u peer_info; + + sbio = BIO_new_dgram(s, BIO_NOCLOSE); + if ((peer_info.addr = BIO_ADDR_new()) == NULL) { + BIO_printf(bio_err, "memory allocation failure\n"); + BIO_closesocket(s); + goto end; + } + if (!BIO_sock_info(s, BIO_SOCK_INFO_ADDRESS, &peer_info)) { + BIO_printf(bio_err, "getsockname:errno=%d\n", + get_last_socket_error()); + BIO_ADDR_free(peer_info.addr); + BIO_closesocket(s); + goto end; + } + + (void)BIO_ctrl_set_connected(sbio, peer_info.addr); + BIO_ADDR_free(peer_info.addr); + peer_info.addr = NULL; + + if (enable_timeouts) { + timeout.tv_sec = 0; + timeout.tv_usec = DGRAM_RCV_TIMEOUT; + BIO_ctrl(sbio, BIO_CTRL_DGRAM_SET_RECV_TIMEOUT, 0, &timeout); + + timeout.tv_sec = 0; + timeout.tv_usec = DGRAM_SND_TIMEOUT; + BIO_ctrl(sbio, BIO_CTRL_DGRAM_SET_SEND_TIMEOUT, 0, &timeout); + } + + if (socket_mtu) { + if (socket_mtu < DTLS_get_link_min_mtu(con)) { + BIO_printf(bio_err, "MTU too small. Must be at least %ld\n", + DTLS_get_link_min_mtu(con)); + BIO_free(sbio); + goto shut; + } + SSL_set_options(con, SSL_OP_NO_QUERY_MTU); + if (!DTLS_set_link_mtu(con, socket_mtu)) { + BIO_printf(bio_err, "Failed to set MTU\n"); + BIO_free(sbio); + goto shut; + } + } else + /* want to do MTU discovery */ + BIO_ctrl(sbio, BIO_CTRL_DGRAM_MTU_DISCOVER, 0, NULL); + } else +#endif /* OPENSSL_NO_DTLS */ + sbio = BIO_new_socket(s, BIO_NOCLOSE); + + if (nbio_test) { + BIO *test; + + test = BIO_new(BIO_f_nbio_test()); + sbio = BIO_push(test, sbio); + } + + if (c_debug) { + BIO_set_callback(sbio, bio_dump_callback); + BIO_set_callback_arg(sbio, (char *)bio_c_out); + } + if (c_msg) { +#ifndef OPENSSL_NO_SSL_TRACE + if (c_msg == 2) + SSL_set_msg_callback(con, SSL_trace); + else +#endif + SSL_set_msg_callback(con, msg_cb); + SSL_set_msg_callback_arg(con, bio_c_msg ? bio_c_msg : bio_c_out); + } + + if (c_tlsextdebug) { + SSL_set_tlsext_debug_callback(con, tlsext_cb); + SSL_set_tlsext_debug_arg(con, bio_c_out); + } +#ifndef OPENSSL_NO_OCSP + if (c_status_req) { + SSL_set_tlsext_status_type(con, TLSEXT_STATUSTYPE_ocsp); + SSL_CTX_set_tlsext_status_cb(ctx, ocsp_resp_cb); + SSL_CTX_set_tlsext_status_arg(ctx, bio_c_out); + } +#endif + + SSL_set_bio(con, sbio, sbio); + SSL_set_connect_state(con); + + /* ok, lets connect */ + if (fileno_stdin() > SSL_get_fd(con)) + width = fileno_stdin() + 1; + else + width = SSL_get_fd(con) + 1; + + read_tty = 1; + write_tty = 0; + tty_on = 0; + read_ssl = 1; + write_ssl = 1; + + cbuf_len = 0; + cbuf_off = 0; + sbuf_len = 0; + sbuf_off = 0; + + switch ((PROTOCOL_CHOICE) starttls_proto) { + case PROTO_OFF: + break; + case PROTO_SMTP: + { + /* + * This is an ugly hack that does a lot of assumptions. We do + * have to handle multi-line responses which may come in a single + * packet or not. We therefore have to use BIO_gets() which does + * need a buffering BIO. So during the initial chitchat we do + * push a buffering BIO into the chain that is removed again + * later on to not disturb the rest of the s_client operation. + */ + int foundit = 0; + BIO *fbio = BIO_new(BIO_f_buffer()); + BIO_push(fbio, sbio); + /* wait for multi-line response to end from SMTP */ + do { + mbuf_len = BIO_gets(fbio, mbuf, BUFSIZZ); + } + while (mbuf_len > 3 && mbuf[3] == '-'); + BIO_printf(fbio, "EHLO %s\r\n", ehlo); + (void)BIO_flush(fbio); + /* wait for multi-line response to end EHLO SMTP response */ + do { + mbuf_len = BIO_gets(fbio, mbuf, BUFSIZZ); + if (strstr(mbuf, "STARTTLS")) + foundit = 1; + } + while (mbuf_len > 3 && mbuf[3] == '-'); + (void)BIO_flush(fbio); + BIO_pop(fbio); + BIO_free(fbio); + if (!foundit) + BIO_printf(bio_err, + "didn't find starttls in server response," + " trying anyway...\n"); + BIO_printf(sbio, "STARTTLS\r\n"); + BIO_read(sbio, sbuf, BUFSIZZ); + } + break; + case PROTO_POP3: + { + BIO_read(sbio, mbuf, BUFSIZZ); + BIO_printf(sbio, "STLS\r\n"); + mbuf_len = BIO_read(sbio, sbuf, BUFSIZZ); + if (mbuf_len < 0) { + BIO_printf(bio_err, "BIO_read failed\n"); + goto end; + } + } + break; + case PROTO_IMAP: + { + int foundit = 0; + BIO *fbio = BIO_new(BIO_f_buffer()); + BIO_push(fbio, sbio); + BIO_gets(fbio, mbuf, BUFSIZZ); + /* STARTTLS command requires CAPABILITY... */ + BIO_printf(fbio, ". CAPABILITY\r\n"); + (void)BIO_flush(fbio); + /* wait for multi-line CAPABILITY response */ + do { + mbuf_len = BIO_gets(fbio, mbuf, BUFSIZZ); + if (strstr(mbuf, "STARTTLS")) + foundit = 1; + } + while (mbuf_len > 3 && mbuf[0] != '.'); + (void)BIO_flush(fbio); + BIO_pop(fbio); + BIO_free(fbio); + if (!foundit) + BIO_printf(bio_err, + "didn't find STARTTLS in server response," + " trying anyway...\n"); + BIO_printf(sbio, ". STARTTLS\r\n"); + BIO_read(sbio, sbuf, BUFSIZZ); + } + break; + case PROTO_FTP: + { + BIO *fbio = BIO_new(BIO_f_buffer()); + BIO_push(fbio, sbio); + /* wait for multi-line response to end from FTP */ + do { + mbuf_len = BIO_gets(fbio, mbuf, BUFSIZZ); + } + while (mbuf_len > 3 && mbuf[3] == '-'); + (void)BIO_flush(fbio); + BIO_pop(fbio); + BIO_free(fbio); + BIO_printf(sbio, "AUTH TLS\r\n"); + BIO_read(sbio, sbuf, BUFSIZZ); + } + break; + case PROTO_XMPP: + case PROTO_XMPP_SERVER: + { + int seen = 0; + BIO_printf(sbio, "", + starttls_proto == PROTO_XMPP ? "client" : "server", + xmpphost ? xmpphost : host); + seen = BIO_read(sbio, mbuf, BUFSIZZ); + mbuf[seen] = 0; + while (!strstr + (mbuf, ""); + seen = BIO_read(sbio, sbuf, BUFSIZZ); + sbuf[seen] = 0; + if (!strstr(sbuf, " 2); + } + (void)BIO_flush(fbio); + BIO_pop(fbio); + BIO_free(fbio); + if (foundit != success) { + goto shut; + } + } + break; + case PROTO_IRC: + { + int numeric; + BIO *fbio = BIO_new(BIO_f_buffer()); + + BIO_push(fbio, sbio); + BIO_printf(fbio, "STARTTLS\r\n"); + (void)BIO_flush(fbio); + width = SSL_get_fd(con) + 1; + + do { + numeric = 0; + + FD_ZERO(&readfds); + openssl_fdset(SSL_get_fd(con), &readfds); + timeout.tv_sec = S_CLIENT_IRC_READ_TIMEOUT; + timeout.tv_usec = 0; + /* + * If the IRCd doesn't respond within + * S_CLIENT_IRC_READ_TIMEOUT seconds, assume + * it doesn't support STARTTLS. Many IRCds + * will not give _any_ sort of response to a + * STARTTLS command when it's not supported. + */ + if (!BIO_get_buffer_num_lines(fbio) + && !BIO_pending(fbio) + && !BIO_pending(sbio) + && select(width, (void *)&readfds, NULL, NULL, + &timeout) < 1) { + BIO_printf(bio_err, + "Timeout waiting for response (%d seconds).\n", + S_CLIENT_IRC_READ_TIMEOUT); + break; + } + + mbuf_len = BIO_gets(fbio, mbuf, BUFSIZZ); + if (mbuf_len < 1 || sscanf(mbuf, "%*s %d", &numeric) != 1) + break; + /* :example.net 451 STARTTLS :You have not registered */ + /* :example.net 421 STARTTLS :Unknown command */ + if ((numeric == 451 || numeric == 421) + && strstr(mbuf, "STARTTLS") != NULL) { + BIO_printf(bio_err, "STARTTLS not supported: %s", mbuf); + break; + } + if (numeric == 691) { + BIO_printf(bio_err, "STARTTLS negotiation failed: "); + ERR_print_errors(bio_err); + break; + } + } while (numeric != 670); + + (void)BIO_flush(fbio); + BIO_pop(fbio); + BIO_free(fbio); + if (numeric != 670) { + BIO_printf(bio_err, "Server does not support STARTTLS.\n"); + ret = 1; + goto shut; + } + } + } + + for (;;) { + FD_ZERO(&readfds); + FD_ZERO(&writefds); + + if ((SSL_version(con) == DTLS1_VERSION) && + DTLSv1_get_timeout(con, &timeout)) + timeoutp = &timeout; + else + timeoutp = NULL; + + if (SSL_in_init(con) && !SSL_total_renegotiations(con)) { + in_init = 1; + tty_on = 0; + } else { + tty_on = 1; + if (in_init) { + in_init = 0; + + if (servername != NULL && !SSL_session_reused(con)) { + BIO_printf(bio_c_out, + "Server did %sacknowledge servername extension.\n", + tlsextcbp.ack ? "" : "not "); + } + + if (sess_out) { + BIO *stmp = BIO_new_file(sess_out, "w"); + if (stmp) { + PEM_write_bio_SSL_SESSION(stmp, SSL_get_session(con)); + BIO_free(stmp); + } else + BIO_printf(bio_err, "Error writing session file %s\n", + sess_out); + } + if (c_brief) { + BIO_puts(bio_err, "CONNECTION ESTABLISHED\n"); + print_ssl_summary(con); + } + + print_stuff(bio_c_out, con, full_log); + if (full_log > 0) + full_log--; + + if (starttls_proto) { + BIO_write(bio_err, mbuf, mbuf_len); + /* We don't need to know any more */ + if (!reconnect) + starttls_proto = PROTO_OFF; + } + + if (reconnect) { + reconnect--; + BIO_printf(bio_c_out, + "drop connection and then reconnect\n"); + do_ssl_shutdown(con); + SSL_set_connect_state(con); + BIO_closesocket(SSL_get_fd(con)); + goto re_start; + } + } + } + + ssl_pending = read_ssl && SSL_has_pending(con); + + if (!ssl_pending) { +#if !defined(OPENSSL_SYS_WINDOWS) && !defined(OPENSSL_SYS_MSDOS) + if (tty_on) { + /* + * Note that select() returns when read _would not block_, + * and EOF satisfies that. To avoid a CPU-hogging loop, + * set the flag so we exit. + */ + if (read_tty && !at_eof) + openssl_fdset(fileno_stdin(), &readfds); +#if !defined(OPENSSL_SYS_VMS) + if (write_tty) + openssl_fdset(fileno_stdout(), &writefds); +#endif + } + if (read_ssl) + openssl_fdset(SSL_get_fd(con), &readfds); + if (write_ssl) + openssl_fdset(SSL_get_fd(con), &writefds); +#else + if (!tty_on || !write_tty) { + if (read_ssl) + openssl_fdset(SSL_get_fd(con), &readfds); + if (write_ssl) + openssl_fdset(SSL_get_fd(con), &writefds); + } +#endif + + /* + * Note: under VMS with SOCKETSHR the second parameter is + * currently of type (int *) whereas under other systems it is + * (void *) if you don't have a cast it will choke the compiler: + * if you do have a cast then you can either go for (int *) or + * (void *). + */ +#if defined(OPENSSL_SYS_WINDOWS) || defined(OPENSSL_SYS_MSDOS) + /* + * Under Windows/DOS we make the assumption that we can always + * write to the tty: therefore if we need to write to the tty we + * just fall through. Otherwise we timeout the select every + * second and see if there are any keypresses. Note: this is a + * hack, in a proper Windows application we wouldn't do this. + */ + i = 0; + if (!write_tty) { + if (read_tty) { + tv.tv_sec = 1; + tv.tv_usec = 0; + i = select(width, (void *)&readfds, (void *)&writefds, + NULL, &tv); + if (!i && (!has_stdin_waiting() || !read_tty)) + continue; + } else + i = select(width, (void *)&readfds, (void *)&writefds, + NULL, timeoutp); + } +#else + i = select(width, (void *)&readfds, (void *)&writefds, + NULL, timeoutp); +#endif + if (i < 0) { + BIO_printf(bio_err, "bad select %d\n", + get_last_socket_error()); + goto shut; + /* goto end; */ + } + } + + if ((SSL_version(con) == DTLS1_VERSION) + && DTLSv1_handle_timeout(con) > 0) { + BIO_printf(bio_err, "TIMEOUT occurred\n"); + } + + if (!ssl_pending && FD_ISSET(SSL_get_fd(con), &writefds)) { + k = SSL_write(con, &(cbuf[cbuf_off]), (unsigned int)cbuf_len); + switch (SSL_get_error(con, k)) { + case SSL_ERROR_NONE: + cbuf_off += k; + cbuf_len -= k; + if (k <= 0) + goto end; + /* we have done a write(con,NULL,0); */ + if (cbuf_len <= 0) { + read_tty = 1; + write_ssl = 0; + } else { /* if (cbuf_len > 0) */ + + read_tty = 0; + write_ssl = 1; + } + break; + case SSL_ERROR_WANT_WRITE: + BIO_printf(bio_c_out, "write W BLOCK\n"); + write_ssl = 1; + read_tty = 0; + break; + case SSL_ERROR_WANT_ASYNC: + BIO_printf(bio_c_out, "write A BLOCK\n"); + wait_for_async(con); + write_ssl = 1; + read_tty = 0; + break; + case SSL_ERROR_WANT_READ: + BIO_printf(bio_c_out, "write R BLOCK\n"); + write_tty = 0; + read_ssl = 1; + write_ssl = 0; + break; + case SSL_ERROR_WANT_X509_LOOKUP: + BIO_printf(bio_c_out, "write X BLOCK\n"); + break; + case SSL_ERROR_ZERO_RETURN: + if (cbuf_len != 0) { + BIO_printf(bio_c_out, "shutdown\n"); + ret = 0; + goto shut; + } else { + read_tty = 1; + write_ssl = 0; + break; + } + + case SSL_ERROR_SYSCALL: + if ((k != 0) || (cbuf_len != 0)) { + BIO_printf(bio_err, "write:errno=%d\n", + get_last_socket_error()); + goto shut; + } else { + read_tty = 1; + write_ssl = 0; + } + break; + case SSL_ERROR_WANT_ASYNC_JOB: + /* This shouldn't ever happen in s_client - treat as an error */ + case SSL_ERROR_SSL: + ERR_print_errors(bio_err); + goto shut; + } + } +#if defined(OPENSSL_SYS_WINDOWS) || defined(OPENSSL_SYS_MSDOS) || defined(OPENSSL_SYS_VMS) + /* Assume Windows/DOS/BeOS can always write */ + else if (!ssl_pending && write_tty) +#else + else if (!ssl_pending && FD_ISSET(fileno_stdout(), &writefds)) +#endif + { +#ifdef CHARSET_EBCDIC + ascii2ebcdic(&(sbuf[sbuf_off]), &(sbuf[sbuf_off]), sbuf_len); +#endif + i = raw_write_stdout(&(sbuf[sbuf_off]), sbuf_len); + + if (i <= 0) { + BIO_printf(bio_c_out, "DONE\n"); + ret = 0; + goto shut; + /* goto end; */ + } + + sbuf_len -= i;; + sbuf_off += i; + if (sbuf_len <= 0) { + read_ssl = 1; + write_tty = 0; + } + } else if (ssl_pending || FD_ISSET(SSL_get_fd(con), &readfds)) { +#ifdef RENEG + { + static int iiii; + if (++iiii == 52) { + SSL_renegotiate(con); + iiii = 0; + } + } +#endif + k = SSL_read(con, sbuf, 1024 /* BUFSIZZ */ ); + + switch (SSL_get_error(con, k)) { + case SSL_ERROR_NONE: + if (k <= 0) + goto end; + sbuf_off = 0; + sbuf_len = k; + + read_ssl = 0; + write_tty = 1; + break; + case SSL_ERROR_WANT_ASYNC: + BIO_printf(bio_c_out, "read A BLOCK\n"); + wait_for_async(con); + write_tty = 0; + read_ssl = 1; + if ((read_tty == 0) && (write_ssl == 0)) + write_ssl = 1; + break; + case SSL_ERROR_WANT_WRITE: + BIO_printf(bio_c_out, "read W BLOCK\n"); + write_ssl = 1; + read_tty = 0; + break; + case SSL_ERROR_WANT_READ: + BIO_printf(bio_c_out, "read R BLOCK\n"); + write_tty = 0; + read_ssl = 1; + if ((read_tty == 0) && (write_ssl == 0)) + write_ssl = 1; + break; + case SSL_ERROR_WANT_X509_LOOKUP: + BIO_printf(bio_c_out, "read X BLOCK\n"); + break; + case SSL_ERROR_SYSCALL: + ret = get_last_socket_error(); + if (c_brief) + BIO_puts(bio_err, "CONNECTION CLOSED BY SERVER\n"); + else + BIO_printf(bio_err, "read:errno=%d\n", ret); + goto shut; + case SSL_ERROR_ZERO_RETURN: + BIO_printf(bio_c_out, "closed\n"); + ret = 0; + goto shut; + case SSL_ERROR_WANT_ASYNC_JOB: + /* This shouldn't ever happen in s_client. Treat as an error */ + case SSL_ERROR_SSL: + ERR_print_errors(bio_err); + goto shut; + /* break; */ + } + } +/* OPENSSL_SYS_MSDOS includes OPENSSL_SYS_WINDOWS */ +#if defined(OPENSSL_SYS_MSDOS) + else if (has_stdin_waiting()) +#else + else if (FD_ISSET(fileno_stdin(), &readfds)) +#endif + { + if (crlf) { + int j, lf_num; + + i = raw_read_stdin(cbuf, BUFSIZZ / 2); + lf_num = 0; + /* both loops are skipped when i <= 0 */ + for (j = 0; j < i; j++) + if (cbuf[j] == '\n') + lf_num++; + for (j = i - 1; j >= 0; j--) { + cbuf[j + lf_num] = cbuf[j]; + if (cbuf[j] == '\n') { + lf_num--; + i++; + cbuf[j + lf_num] = '\r'; + } + } + assert(lf_num == 0); + } else + i = raw_read_stdin(cbuf, BUFSIZZ); +#if !defined(OPENSSL_SYS_WINDOWS) && !defined(OPENSSL_SYS_MSDOS) + if (i == 0) + at_eof = 1; +#endif + + if ((!c_ign_eof) && ((i <= 0) || (cbuf[0] == 'Q' && cmdletters))) { + BIO_printf(bio_err, "DONE\n"); + ret = 0; + goto shut; + } + + if ((!c_ign_eof) && (cbuf[0] == 'R' && cmdletters)) { + BIO_printf(bio_err, "RENEGOTIATING\n"); + SSL_renegotiate(con); + cbuf_len = 0; + } +#ifndef OPENSSL_NO_HEARTBEATS + else if ((!c_ign_eof) && (cbuf[0] == 'B' && cmdletters)) { + BIO_printf(bio_err, "HEARTBEATING\n"); + SSL_heartbeat(con); + cbuf_len = 0; + } +#endif + else { + cbuf_len = i; + cbuf_off = 0; +#ifdef CHARSET_EBCDIC + ebcdic2ascii(cbuf, cbuf, i); +#endif + } + + write_ssl = 1; + read_tty = 0; + } + } + + ret = 0; + shut: + if (in_init) + print_stuff(bio_c_out, con, full_log); + do_ssl_shutdown(con); +#if defined(OPENSSL_SYS_WINDOWS) + /* + * Give the socket time to send its last data before we close it. + * No amount of setting SO_LINGER etc on the socket seems to persuade + * Windows to send the data before closing the socket...but sleeping + * for a short time seems to do it (units in ms) + * TODO: Find a better way to do this + */ + Sleep(50); +#endif + BIO_closesocket(SSL_get_fd(con)); + end: + if (con != NULL) { + if (prexit != 0) + print_stuff(bio_c_out, con, 1); + SSL_free(con); + } +#if !defined(OPENSSL_NO_NEXTPROTONEG) + OPENSSL_free(next_proto.data); +#endif + SSL_CTX_free(ctx); + X509_free(cert); + X509_free(dcert); + sk_X509_CRL_pop_free(crls, X509_CRL_free); + EVP_PKEY_free(key); + EVP_PKEY_free(dkey); + sk_X509_pop_free(chain, X509_free); + sk_X509_pop_free(dchain, X509_free); + OPENSSL_free(pass); + OPENSSL_free(dpass); +#ifndef OPENSSL_NO_SRP + OPENSSL_free(srp_arg.srppassin); +#endif + OPENSSL_free(connectstr); + OPENSSL_free(host); + OPENSSL_free(port); + X509_VERIFY_PARAM_free(vpm); + ssl_excert_free(exc); + sk_OPENSSL_STRING_free(ssl_args); + sk_OPENSSL_STRING_free(dane_tlsa_rrset); + SSL_CONF_CTX_free(cctx); + OPENSSL_clear_free(cbuf, BUFSIZZ); + OPENSSL_clear_free(sbuf, BUFSIZZ); + OPENSSL_clear_free(mbuf, BUFSIZZ); + release_engine(e); + BIO_free(bio_c_out); + bio_c_out = NULL; + BIO_free(bio_c_msg); + bio_c_msg = NULL; + NCONF_free(conf); + return (ret); +} + +static void print_stuff(BIO *bio, SSL *s, int full) +{ + X509 *peer = NULL; + char buf[BUFSIZ]; + STACK_OF(X509) *sk; + STACK_OF(X509_NAME) *sk2; + const SSL_CIPHER *c; + X509_NAME *xn; + int i; +#ifndef OPENSSL_NO_COMP + const COMP_METHOD *comp, *expansion; +#endif + unsigned char *exportedkeymat; +#ifndef OPENSSL_NO_CT + const SSL_CTX *ctx = SSL_get_SSL_CTX(s); +#endif + + if (full) { + int got_a_chain = 0; + + sk = SSL_get_peer_cert_chain(s); + if (sk != NULL) { + got_a_chain = 1; + + BIO_printf(bio, "---\nCertificate chain\n"); + for (i = 0; i < sk_X509_num(sk); i++) { + X509_NAME_oneline(X509_get_subject_name(sk_X509_value(sk, i)), + buf, sizeof buf); + BIO_printf(bio, "%2d s:%s\n", i, buf); + X509_NAME_oneline(X509_get_issuer_name(sk_X509_value(sk, i)), + buf, sizeof buf); + BIO_printf(bio, " i:%s\n", buf); + if (c_showcerts) + PEM_write_bio_X509(bio, sk_X509_value(sk, i)); + } + } + + BIO_printf(bio, "---\n"); + peer = SSL_get_peer_certificate(s); + if (peer != NULL) { + BIO_printf(bio, "Server certificate\n"); + + /* Redundant if we showed the whole chain */ + if (!(c_showcerts && got_a_chain)) + PEM_write_bio_X509(bio, peer); + X509_NAME_oneline(X509_get_subject_name(peer), buf, sizeof buf); + BIO_printf(bio, "subject=%s\n", buf); + X509_NAME_oneline(X509_get_issuer_name(peer), buf, sizeof buf); + BIO_printf(bio, "issuer=%s\n", buf); + } else + BIO_printf(bio, "no peer certificate available\n"); + + sk2 = SSL_get_client_CA_list(s); + if ((sk2 != NULL) && (sk_X509_NAME_num(sk2) > 0)) { + BIO_printf(bio, "---\nAcceptable client certificate CA names\n"); + for (i = 0; i < sk_X509_NAME_num(sk2); i++) { + xn = sk_X509_NAME_value(sk2, i); + X509_NAME_oneline(xn, buf, sizeof(buf)); + BIO_write(bio, buf, strlen(buf)); + BIO_write(bio, "\n", 1); + } + } else { + BIO_printf(bio, "---\nNo client certificate CA names sent\n"); + } + + ssl_print_sigalgs(bio, s); + ssl_print_tmp_key(bio, s); + +#ifndef OPENSSL_NO_CT + /* + * When the SSL session is anonymous, or resumed via an abbreviated + * handshake, no SCTs are provided as part of the handshake. While in + * a resumed session SCTs may be present in the session's certificate, + * no callbacks are invoked to revalidate these, and in any case that + * set of SCTs may be incomplete. Thus it makes little sense to + * attempt to display SCTs from a resumed session's certificate, and of + * course none are associated with an anonymous peer. + */ + if (peer != NULL && !SSL_session_reused(s) && SSL_ct_is_enabled(s)) { + const STACK_OF(SCT) *scts = SSL_get0_peer_scts(s); + int sct_count = scts != NULL ? sk_SCT_num(scts) : 0; + + BIO_printf(bio, "---\nSCTs present (%i)\n", sct_count); + if (sct_count > 0) { + const CTLOG_STORE *log_store = SSL_CTX_get0_ctlog_store(ctx); + + BIO_printf(bio, "---\n"); + for (i = 0; i < sct_count; ++i) { + SCT *sct = sk_SCT_value(scts, i); + + BIO_printf(bio, "SCT validation status: %s\n", + SCT_validation_status_string(sct)); + SCT_print(sct, bio, 0, log_store); + if (i < sct_count - 1) + BIO_printf(bio, "\n---\n"); + } + BIO_printf(bio, "\n"); + } + } +#endif + + BIO_printf(bio, + "---\nSSL handshake has read %" PRIu64 + " bytes and written %" PRIu64 " bytes\n", + BIO_number_read(SSL_get_rbio(s)), + BIO_number_written(SSL_get_wbio(s))); + } + print_verify_detail(s, bio); + BIO_printf(bio, (SSL_session_reused(s) ? "---\nReused, " : "---\nNew, ")); + c = SSL_get_current_cipher(s); + BIO_printf(bio, "%s, Cipher is %s\n", + SSL_CIPHER_get_version(c), SSL_CIPHER_get_name(c)); + if (peer != NULL) { + EVP_PKEY *pktmp; + + pktmp = X509_get0_pubkey(peer); + BIO_printf(bio, "Server public key is %d bit\n", + EVP_PKEY_bits(pktmp)); + } + BIO_printf(bio, "Secure Renegotiation IS%s supported\n", + SSL_get_secure_renegotiation_support(s) ? "" : " NOT"); +#ifndef OPENSSL_NO_COMP + comp = SSL_get_current_compression(s); + expansion = SSL_get_current_expansion(s); + BIO_printf(bio, "Compression: %s\n", + comp ? SSL_COMP_get_name(comp) : "NONE"); + BIO_printf(bio, "Expansion: %s\n", + expansion ? SSL_COMP_get_name(expansion) : "NONE"); +#endif + +#ifdef SSL_DEBUG + { + /* Print out local port of connection: useful for debugging */ + int sock; + union BIO_sock_info_u info; + + sock = SSL_get_fd(s); + if ((info.addr = BIO_ADDR_new()) != NULL + && BIO_sock_info(sock, BIO_SOCK_INFO_ADDRESS, &info)) { + BIO_printf(bio_c_out, "LOCAL PORT is %u\n", + ntohs(BIO_ADDR_rawport(info.addr))); + } + BIO_ADDR_free(info.addr); + } +#endif + +#if !defined(OPENSSL_NO_NEXTPROTONEG) + if (next_proto.status != -1) { + const unsigned char *proto; + unsigned int proto_len; + SSL_get0_next_proto_negotiated(s, &proto, &proto_len); + BIO_printf(bio, "Next protocol: (%d) ", next_proto.status); + BIO_write(bio, proto, proto_len); + BIO_write(bio, "\n", 1); + } +#endif + { + const unsigned char *proto; + unsigned int proto_len; + SSL_get0_alpn_selected(s, &proto, &proto_len); + if (proto_len > 0) { + BIO_printf(bio, "ALPN protocol: "); + BIO_write(bio, proto, proto_len); + BIO_write(bio, "\n", 1); + } else + BIO_printf(bio, "No ALPN negotiated\n"); + } + +#ifndef OPENSSL_NO_SRTP + { + SRTP_PROTECTION_PROFILE *srtp_profile = + SSL_get_selected_srtp_profile(s); + + if (srtp_profile) + BIO_printf(bio, "SRTP Extension negotiated, profile=%s\n", + srtp_profile->name); + } +#endif + + SSL_SESSION_print(bio, SSL_get_session(s)); + if (SSL_get_session(s) != NULL && keymatexportlabel != NULL) { + BIO_printf(bio, "Keying material exporter:\n"); + BIO_printf(bio, " Label: '%s'\n", keymatexportlabel); + BIO_printf(bio, " Length: %i bytes\n", keymatexportlen); + exportedkeymat = app_malloc(keymatexportlen, "export key"); + if (!SSL_export_keying_material(s, exportedkeymat, + keymatexportlen, + keymatexportlabel, + strlen(keymatexportlabel), + NULL, 0, 0)) { + BIO_printf(bio, " Error\n"); + } else { + BIO_printf(bio, " Keying material: "); + for (i = 0; i < keymatexportlen; i++) + BIO_printf(bio, "%02X", exportedkeymat[i]); + BIO_printf(bio, "\n"); + } + OPENSSL_free(exportedkeymat); + } + BIO_printf(bio, "---\n"); + X509_free(peer); + /* flush, or debugging output gets mixed with http response */ + (void)BIO_flush(bio); +} + +# ifndef OPENSSL_NO_OCSP +static int ocsp_resp_cb(SSL *s, void *arg) +{ + const unsigned char *p; + int len; + OCSP_RESPONSE *rsp; + len = SSL_get_tlsext_status_ocsp_resp(s, &p); + BIO_puts(arg, "OCSP response: "); + if (!p) { + BIO_puts(arg, "no response sent\n"); + return 1; + } + rsp = d2i_OCSP_RESPONSE(NULL, &p, len); + if (!rsp) { + BIO_puts(arg, "response parse error\n"); + BIO_dump_indent(arg, (char *)p, len, 4); + return 0; + } + BIO_puts(arg, "\n======================================\n"); + OCSP_RESPONSE_print(arg, rsp, 0); + BIO_puts(arg, "======================================\n"); + OCSP_RESPONSE_free(rsp); + return 1; +} +# endif + +#endif /* OPENSSL_NO_SOCK */ diff --git a/apps/s_server.c b/apps/s_server.c new file mode 100644 index 000000000..0f2f4663e --- /dev/null +++ b/apps/s_server.c @@ -0,0 +1,3340 @@ +/* + * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +/* ==================================================================== + * Copyright 2002 Sun Microsystems, Inc. ALL RIGHTS RESERVED. + * ECC cipher suite support in OpenSSL originally developed by + * SUN MICROSYSTEMS, INC., and contributed to the OpenSSL project. + */ +/* ==================================================================== + * Copyright 2005 Nokia. All rights reserved. + * + * The portions of the attached software ("Contribution") is developed by + * Nokia Corporation and is licensed pursuant to the OpenSSL open source + * license. + * + * The Contribution, originally written by Mika Kousa and Pasi Eronen of + * Nokia Corporation, consists of the "PSK" (Pre-Shared Key) ciphersuites + * support (see RFC 4279) to OpenSSL. + * + * No patent licenses or other rights except those expressly stated in + * the OpenSSL open source license shall be deemed granted or received + * expressly, by implication, estoppel, or otherwise. + * + * No assurances are provided by Nokia that the Contribution does not + * infringe the patent or other intellectual property rights of any third + * party or that the license provides you with all the necessary rights + * to make use of the Contribution. + * + * THE SOFTWARE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND. IN + * ADDITION TO THE DISCLAIMERS INCLUDED IN THE LICENSE, NOKIA + * SPECIFICALLY DISCLAIMS ANY LIABILITY FOR CLAIMS BROUGHT BY YOU OR ANY + * OTHER ENTITY BASED ON INFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS OR + * OTHERWISE. + */ + +#include +#include +#include +#include +#if defined(_WIN32) +/* Included before async.h to avoid some warnings */ +# include +#endif + +#include +#include +#include + +#ifndef OPENSSL_NO_SOCK + +/* + * With IPv6, it looks like Digital has mixed up the proper order of + * recursive header file inclusion, resulting in the compiler complaining + * that u_int isn't defined, but only if _POSIX_C_SOURCE is defined, which is + * needed to have fileno() declared correctly... So let's define u_int + */ +#if defined(OPENSSL_SYS_VMS_DECC) && !defined(__U_INT) +# define __U_INT +typedef unsigned int u_int; +#endif + +#include +#include +#define USE_SOCKETS +#include "apps.h" +#include +#include +#include +#include +#include +#include +#ifndef OPENSSL_NO_DH +# include +#endif +#ifndef OPENSSL_NO_RSA +# include +#endif +#ifndef OPENSSL_NO_SRP +# include +#endif +#include "s_apps.h" +#include "timeouts.h" +#ifdef CHARSET_EBCDIC +#include +#endif + +static int not_resumable_sess_cb(SSL *s, int is_forward_secure); +static int sv_body(int s, int stype, unsigned char *context); +static int www_body(int s, int stype, unsigned char *context); +static int rev_body(int s, int stype, unsigned char *context); +static void close_accept_socket(void); +static int init_ssl_connection(SSL *s); +static void print_stats(BIO *bp, SSL_CTX *ctx); +static int generate_session_id(const SSL *ssl, unsigned char *id, + unsigned int *id_len); +static void init_session_cache_ctx(SSL_CTX *sctx); +static void free_sessions(void); +#ifndef OPENSSL_NO_DH +static DH *load_dh_param(const char *dhfile); +#endif + +/* static int load_CA(SSL_CTX *ctx, char *file);*/ + +static const int bufsize = 16 * 1024; +static int accept_socket = -1; + +#define TEST_CERT "server.pem" +#define TEST_CERT2 "server2.pem" + +static int s_nbio = 0; +static int s_nbio_test = 0; +static int s_crlf = 0; +static SSL_CTX *ctx = NULL; +static SSL_CTX *ctx2 = NULL; +static int www = 0; + +static BIO *bio_s_out = NULL; +static BIO *bio_s_msg = NULL; +static int s_debug = 0; +static int s_tlsextdebug = 0; +static int s_msg = 0; +static int s_quiet = 0; +static int s_ign_eof = 0; +static int s_brief = 0; + +static char *keymatexportlabel = NULL; +static int keymatexportlen = 20; + +static int async = 0; + +static const char *session_id_prefix = NULL; + +#ifndef OPENSSL_NO_DTLS +static int enable_timeouts = 0; +static long socket_mtu; + +#endif +static int dtlslisten = 0; + +#ifndef OPENSSL_NO_PSK +static char *psk_identity = "Client_identity"; +char *psk_key = NULL; /* by default PSK is not used */ + +static unsigned int psk_server_cb(SSL *ssl, const char *identity, + unsigned char *psk, + unsigned int max_psk_len) +{ + long key_len = 0; + unsigned char *key; + + if (s_debug) + BIO_printf(bio_s_out, "psk_server_cb\n"); + if (!identity) { + BIO_printf(bio_err, "Error: client did not send PSK identity\n"); + goto out_err; + } + if (s_debug) + BIO_printf(bio_s_out, "identity_len=%d identity=%s\n", + (int)strlen(identity), identity); + + /* here we could lookup the given identity e.g. from a database */ + if (strcmp(identity, psk_identity) != 0) { + BIO_printf(bio_s_out, "PSK error: client identity not found" + " (got '%s' expected '%s')\n", identity, psk_identity); + goto out_err; + } + if (s_debug) + BIO_printf(bio_s_out, "PSK client identity found\n"); + + /* convert the PSK key to binary */ + key = OPENSSL_hexstr2buf(psk_key, &key_len); + if (key == NULL) { + BIO_printf(bio_err, "Could not convert PSK key '%s' to buffer\n", + psk_key); + return 0; + } + if (key_len > (int)max_psk_len) { + BIO_printf(bio_err, + "psk buffer of callback is too small (%d) for key (%ld)\n", + max_psk_len, key_len); + OPENSSL_free(key); + return 0; + } + + memcpy(psk, key, key_len); + OPENSSL_free(key); + + if (s_debug) + BIO_printf(bio_s_out, "fetched PSK len=%ld\n", key_len); + return key_len; + out_err: + if (s_debug) + BIO_printf(bio_err, "Error in PSK server callback\n"); + (void)BIO_flush(bio_err); + (void)BIO_flush(bio_s_out); + return 0; +} +#endif + +#ifndef OPENSSL_NO_SRP +/* This is a context that we pass to callbacks */ +typedef struct srpsrvparm_st { + char *login; + SRP_VBASE *vb; + SRP_user_pwd *user; +} srpsrvparm; + +/* + * This callback pretends to require some asynchronous logic in order to + * obtain a verifier. When the callback is called for a new connection we + * return with a negative value. This will provoke the accept etc to return + * with an LOOKUP_X509. The main logic of the reinvokes the suspended call + * (which would normally occur after a worker has finished) and we set the + * user parameters. + */ +static int ssl_srp_server_param_cb(SSL *s, int *ad, void *arg) +{ + srpsrvparm *p = (srpsrvparm *) arg; + int ret = SSL3_AL_FATAL; + + if (p->login == NULL && p->user == NULL) { + p->login = SSL_get_srp_username(s); + BIO_printf(bio_err, "SRP username = \"%s\"\n", p->login); + return (-1); + } + + if (p->user == NULL) { + BIO_printf(bio_err, "User %s doesn't exist\n", p->login); + goto err; + } + + if (SSL_set_srp_server_param + (s, p->user->N, p->user->g, p->user->s, p->user->v, + p->user->info) < 0) { + *ad = SSL_AD_INTERNAL_ERROR; + goto err; + } + BIO_printf(bio_err, + "SRP parameters set: username = \"%s\" info=\"%s\" \n", + p->login, p->user->info); + ret = SSL_ERROR_NONE; + + err: + SRP_user_pwd_free(p->user); + p->user = NULL; + p->login = NULL; + return ret; +} + +#endif + +static int local_argc = 0; +static char **local_argv; + +#ifdef CHARSET_EBCDIC +static int ebcdic_new(BIO *bi); +static int ebcdic_free(BIO *a); +static int ebcdic_read(BIO *b, char *out, int outl); +static int ebcdic_write(BIO *b, const char *in, int inl); +static long ebcdic_ctrl(BIO *b, int cmd, long num, void *ptr); +static int ebcdic_gets(BIO *bp, char *buf, int size); +static int ebcdic_puts(BIO *bp, const char *str); + +# define BIO_TYPE_EBCDIC_FILTER (18|0x0200) +static BIO_METHOD *methods_ebcdic = NULL; + +/* This struct is "unwarranted chumminess with the compiler." */ +typedef struct { + size_t alloced; + char buff[1]; +} EBCDIC_OUTBUFF; + +static const BIO_METHOD *BIO_f_ebcdic_filter() +{ + if (methods_ebcdic == NULL) { + methods_ebcdic = BIO_meth_new(BIO_TYPE_EBCDIC_FILTER, + "EBCDIC/ASCII filter"); + if (methods_ebcdic == NULL + || !BIO_meth_set_write(methods_ebcdic, ebcdic_write) + || !BIO_meth_set_read(methods_ebcdic, ebcdic_read) + || !BIO_meth_set_puts(methods_ebcdic, ebcdic_puts) + || !BIO_meth_set_gets(methods_ebcdic, ebcdic_gets) + || !BIO_meth_set_ctrl(methods_ebcdic, ebcdic_ctrl) + || !BIO_meth_set_create(methods_ebcdic, ebcdic_new) + || !BIO_meth_set_destroy(methods_ebcdic, ebcdic_free)) + return NULL; + } + return methods_ebcdic; +} + +static int ebcdic_new(BIO *bi) +{ + EBCDIC_OUTBUFF *wbuf; + + wbuf = app_malloc(sizeof(*wbuf) + 1024, "ebcdic wbuf"); + wbuf->alloced = 1024; + wbuf->buff[0] = '\0'; + + BIO_set_data(bi, wbuf); + BIO_set_init(bi, 1); + return 1; +} + +static int ebcdic_free(BIO *a) +{ + EBCDIC_OUTBUFF *wbuf; + + if (a == NULL) + return 0; + wbuf = BIO_get_data(a); + OPENSSL_free(wbuf); + BIO_set_data(a, NULL); + BIO_set_init(a, 0); + + return 1; +} + +static int ebcdic_read(BIO *b, char *out, int outl) +{ + int ret = 0; + BIO *next = BIO_next(b); + + if (out == NULL || outl == 0) + return (0); + if (next == NULL) + return (0); + + ret = BIO_read(next, out, outl); + if (ret > 0) + ascii2ebcdic(out, out, ret); + return ret; +} + +static int ebcdic_write(BIO *b, const char *in, int inl) +{ + EBCDIC_OUTBUFF *wbuf; + BIO *next = BIO_next(b); + int ret = 0; + int num; + + if ((in == NULL) || (inl <= 0)) + return (0); + if (next == NULL) + return 0; + + wbuf = (EBCDIC_OUTBUFF *) BIO_get_data(b); + + if (inl > (num = wbuf->alloced)) { + num = num + num; /* double the size */ + if (num < inl) + num = inl; + OPENSSL_free(wbuf); + wbuf = app_malloc(sizeof(*wbuf) + num, "grow ebcdic wbuf"); + + wbuf->alloced = num; + wbuf->buff[0] = '\0'; + + BIO_set_data(b, wbuf); + } + + ebcdic2ascii(wbuf->buff, in, inl); + + ret = BIO_write(next, wbuf->buff, inl); + + return (ret); +} + +static long ebcdic_ctrl(BIO *b, int cmd, long num, void *ptr) +{ + long ret; + BIO *next = BIO_next(b); + + if (next == NULL) + return (0); + switch (cmd) { + case BIO_CTRL_DUP: + ret = 0L; + break; + default: + ret = BIO_ctrl(next, cmd, num, ptr); + break; + } + return (ret); +} + +static int ebcdic_gets(BIO *bp, char *buf, int size) +{ + int i, ret = 0; + BIO *next = BIO_next(bp); + + if (next == NULL) + return 0; +/* return(BIO_gets(bp->next_bio,buf,size));*/ + for (i = 0; i < size - 1; ++i) { + ret = ebcdic_read(bp, &buf[i], 1); + if (ret <= 0) + break; + else if (buf[i] == '\n') { + ++i; + break; + } + } + if (i < size) + buf[i] = '\0'; + return (ret < 0 && i == 0) ? ret : i; +} + +static int ebcdic_puts(BIO *bp, const char *str) +{ + if (BIO_next(bp) == NULL) + return 0; + return ebcdic_write(bp, str, strlen(str)); +} +#endif + +/* This is a context that we pass to callbacks */ +typedef struct tlsextctx_st { + char *servername; + BIO *biodebug; + int extension_error; +} tlsextctx; + +static int ssl_servername_cb(SSL *s, int *ad, void *arg) +{ + tlsextctx *p = (tlsextctx *) arg; + const char *servername = SSL_get_servername(s, TLSEXT_NAMETYPE_host_name); + if (servername && p->biodebug) + BIO_printf(p->biodebug, "Hostname in TLS extension: \"%s\"\n", + servername); + + if (!p->servername) + return SSL_TLSEXT_ERR_NOACK; + + if (servername) { + if (strcasecmp(servername, p->servername)) + return p->extension_error; + if (ctx2) { + BIO_printf(p->biodebug, "Switching server context.\n"); + SSL_set_SSL_CTX(s, ctx2); + } + } + return SSL_TLSEXT_ERR_OK; +} + +/* Structure passed to cert status callback */ + +typedef struct tlsextstatusctx_st { + /* Default responder to use */ + char *host, *path, *port; + int use_ssl; + int timeout; + int verbose; +} tlsextstatusctx; + +static tlsextstatusctx tlscstatp = { NULL, NULL, NULL, 0, -1, 0 }; + +#ifndef OPENSSL_NO_OCSP +/* + * Certificate Status callback. This is called when a client includes a + * certificate status request extension. This is a simplified version. It + * examines certificates each time and makes one OCSP responder query for + * each request. A full version would store details such as the OCSP + * certificate IDs and minimise the number of OCSP responses by caching them + * until they were considered "expired". + */ + +static int cert_status_cb(SSL *s, void *arg) +{ + tlsextstatusctx *srctx = arg; + char *host = NULL, *port = NULL, *path = NULL; + int use_ssl; + unsigned char *rspder = NULL; + int rspderlen; + STACK_OF(OPENSSL_STRING) *aia = NULL; + X509 *x = NULL; + X509_STORE_CTX *inctx = NULL; + X509_OBJECT *obj; + OCSP_REQUEST *req = NULL; + OCSP_RESPONSE *resp = NULL; + OCSP_CERTID *id = NULL; + STACK_OF(X509_EXTENSION) *exts; + int ret = SSL_TLSEXT_ERR_NOACK; + int i; + + if (srctx->verbose) + BIO_puts(bio_err, "cert_status: callback called\n"); + /* Build up OCSP query from server certificate */ + x = SSL_get_certificate(s); + aia = X509_get1_ocsp(x); + if (aia) { + if (!OCSP_parse_url(sk_OPENSSL_STRING_value(aia, 0), + &host, &port, &path, &use_ssl)) { + BIO_puts(bio_err, "cert_status: can't parse AIA URL\n"); + goto err; + } + if (srctx->verbose) + BIO_printf(bio_err, "cert_status: AIA URL: %s\n", + sk_OPENSSL_STRING_value(aia, 0)); + } else { + if (!srctx->host) { + BIO_puts(bio_err, + "cert_status: no AIA and no default responder URL\n"); + goto done; + } + host = srctx->host; + path = srctx->path; + port = srctx->port; + use_ssl = srctx->use_ssl; + } + + inctx = X509_STORE_CTX_new(); + if (inctx == NULL) + goto err; + if (!X509_STORE_CTX_init(inctx, + SSL_CTX_get_cert_store(SSL_get_SSL_CTX(s)), + NULL, NULL)) + goto err; + obj = X509_STORE_CTX_get_obj_by_subject(inctx, X509_LU_X509, + X509_get_issuer_name(x)); + if (obj == NULL) { + BIO_puts(bio_err, "cert_status: Can't retrieve issuer certificate.\n"); + goto done; + } + id = OCSP_cert_to_id(NULL, x, X509_OBJECT_get0_X509(obj)); + X509_OBJECT_free(obj); + if (!id) + goto err; + req = OCSP_REQUEST_new(); + if (req == NULL) + goto err; + if (!OCSP_request_add0_id(req, id)) + goto err; + id = NULL; + /* Add any extensions to the request */ + SSL_get_tlsext_status_exts(s, &exts); + for (i = 0; i < sk_X509_EXTENSION_num(exts); i++) { + X509_EXTENSION *ext = sk_X509_EXTENSION_value(exts, i); + if (!OCSP_REQUEST_add_ext(req, ext, -1)) + goto err; + } + resp = process_responder(req, host, path, port, use_ssl, NULL, + srctx->timeout); + if (!resp) { + BIO_puts(bio_err, "cert_status: error querying responder\n"); + goto done; + } + rspderlen = i2d_OCSP_RESPONSE(resp, &rspder); + if (rspderlen <= 0) + goto err; + SSL_set_tlsext_status_ocsp_resp(s, rspder, rspderlen); + if (srctx->verbose) { + BIO_puts(bio_err, "cert_status: ocsp response sent:\n"); + OCSP_RESPONSE_print(bio_err, resp, 2); + } + ret = SSL_TLSEXT_ERR_OK; + goto done; + + err: + ret = SSL_TLSEXT_ERR_ALERT_FATAL; + done: + if (ret != SSL_TLSEXT_ERR_OK) + ERR_print_errors(bio_err); + if (aia) { + OPENSSL_free(host); + OPENSSL_free(path); + OPENSSL_free(port); + X509_email_free(aia); + } + OCSP_CERTID_free(id); + OCSP_REQUEST_free(req); + OCSP_RESPONSE_free(resp); + X509_STORE_CTX_free(inctx); + return ret; +} +#endif + +#ifndef OPENSSL_NO_NEXTPROTONEG +/* This is the context that we pass to next_proto_cb */ +typedef struct tlsextnextprotoctx_st { + unsigned char *data; + unsigned int len; +} tlsextnextprotoctx; + +static int next_proto_cb(SSL *s, const unsigned char **data, + unsigned int *len, void *arg) +{ + tlsextnextprotoctx *next_proto = arg; + + *data = next_proto->data; + *len = next_proto->len; + + return SSL_TLSEXT_ERR_OK; +} +#endif /* ndef OPENSSL_NO_NEXTPROTONEG */ + +/* This the context that we pass to alpn_cb */ +typedef struct tlsextalpnctx_st { + unsigned char *data; + size_t len; +} tlsextalpnctx; + +static int alpn_cb(SSL *s, const unsigned char **out, unsigned char *outlen, + const unsigned char *in, unsigned int inlen, void *arg) +{ + tlsextalpnctx *alpn_ctx = arg; + + if (!s_quiet) { + /* We can assume that |in| is syntactically valid. */ + unsigned int i; + BIO_printf(bio_s_out, "ALPN protocols advertised by the client: "); + for (i = 0; i < inlen;) { + if (i) + BIO_write(bio_s_out, ", ", 2); + BIO_write(bio_s_out, &in[i + 1], in[i]); + i += in[i] + 1; + } + BIO_write(bio_s_out, "\n", 1); + } + + if (SSL_select_next_proto + ((unsigned char **)out, outlen, alpn_ctx->data, alpn_ctx->len, in, + inlen) != OPENSSL_NPN_NEGOTIATED) { + return SSL_TLSEXT_ERR_NOACK; + } + + if (!s_quiet) { + BIO_printf(bio_s_out, "ALPN protocols selected: "); + BIO_write(bio_s_out, *out, *outlen); + BIO_write(bio_s_out, "\n", 1); + } + + return SSL_TLSEXT_ERR_OK; +} + +static int not_resumable_sess_cb(SSL *s, int is_forward_secure) +{ + /* disable resumption for sessions with forward secure ciphers */ + return is_forward_secure; +} + +#ifndef OPENSSL_NO_SRP +static srpsrvparm srp_callback_parm; +#endif +#ifndef OPENSSL_NO_SRTP +static char *srtp_profiles = NULL; +#endif + +typedef enum OPTION_choice { + OPT_ERR = -1, OPT_EOF = 0, OPT_HELP, OPT_ENGINE, +#ifndef OPENSSL_NO_SKF + OPT_CONFIG, +#endif + OPT_4, OPT_6, OPT_ACCEPT, OPT_PORT, OPT_UNIX, OPT_UNLINK, OPT_NACCEPT, + OPT_VERIFY, OPT_UPPER_V_VERIFY, OPT_CONTEXT, OPT_CERT, OPT_CRL, + OPT_CRL_DOWNLOAD, OPT_SERVERINFO, OPT_CERTFORM, OPT_KEY, OPT_KEYFORM, + OPT_PASS, OPT_CERT_CHAIN, OPT_DHPARAM, OPT_DCERTFORM, OPT_DCERT, + OPT_DKEYFORM, OPT_DPASS, OPT_DKEY, OPT_DCERT_CHAIN, OPT_NOCERT, + OPT_CAPATH, OPT_NOCAPATH, OPT_CHAINCAPATH, OPT_VERIFYCAPATH, OPT_NO_CACHE, + OPT_EXT_CACHE, OPT_CRLFORM, OPT_VERIFY_RET_ERROR, OPT_VERIFY_QUIET, + OPT_BUILD_CHAIN, OPT_CAFILE, OPT_NOCAFILE, OPT_CHAINCAFILE, + OPT_VERIFYCAFILE, OPT_NBIO, OPT_NBIO_TEST, OPT_IGN_EOF, OPT_NO_IGN_EOF, + OPT_DEBUG, OPT_TLSEXTDEBUG, OPT_STATUS, OPT_STATUS_VERBOSE, + OPT_STATUS_TIMEOUT, OPT_STATUS_URL, OPT_MSG, OPT_MSGFILE, OPT_TRACE, + OPT_SECURITY_DEBUG, OPT_SECURITY_DEBUG_VERBOSE, OPT_STATE, OPT_CRLF, + OPT_QUIET, OPT_BRIEF, OPT_NO_DHE, + OPT_NO_RESUME_EPHEMERAL, OPT_PSK_HINT, OPT_PSK, OPT_SRPVFILE, + OPT_SRPUSERSEED, OPT_REV, OPT_WWW, OPT_UPPER_WWW, OPT_HTTP, OPT_ASYNC, + OPT_SSL_CONFIG, OPT_SPLIT_SEND_FRAG, OPT_MAX_PIPELINES, OPT_READ_BUF, + OPT_SSL3, OPT_TLS1_2, OPT_TLS1_1, OPT_TLS1, OPT_DTLS, OPT_DTLS1, + OPT_GMTLS, + OPT_DTLS1_2, OPT_TIMEOUT, OPT_MTU, OPT_LISTEN, + OPT_ID_PREFIX, OPT_RAND, OPT_SERVERNAME, OPT_SERVERNAME_FATAL, + OPT_CERT2, OPT_KEY2, OPT_NEXTPROTONEG, OPT_ALPN, + OPT_SRTP_PROFILES, OPT_KEYMATEXPORT, OPT_KEYMATEXPORTLEN, + OPT_S_ENUM, + OPT_V_ENUM, + OPT_X_ENUM +} OPTION_CHOICE; + +OPTIONS s_server_options[] = { + {"help", OPT_HELP, '-', "Display this summary"}, + {"port", OPT_PORT, 'p', + "TCP/IP port to listen on for connections (default is " PORT ")"}, + {"accept", OPT_ACCEPT, 's', + "TCP/IP optional host and port to listen on for connections (default is *:" PORT ")"}, +#ifdef AF_UNIX + {"unix", OPT_UNIX, 's', "Unix domain socket to accept on"}, +#endif + {"4", OPT_4, '-', "Use IPv4 only"}, + {"6", OPT_6, '-', "Use IPv6 only"}, +#ifdef AF_UNIX + {"unlink", OPT_UNLINK, '-', "For -unix, unlink existing socket first"}, +#endif + {"context", OPT_CONTEXT, 's', "Set session ID context"}, + {"verify", OPT_VERIFY, 'n', "Turn on peer certificate verification"}, + {"Verify", OPT_UPPER_V_VERIFY, 'n', + "Turn on peer certificate verification, must have a cert"}, + {"cert", OPT_CERT, '<', "Certificate file to use; default is " TEST_CERT}, + {"naccept", OPT_NACCEPT, 'p', "Terminate after #num connections"}, + {"serverinfo", OPT_SERVERINFO, 's', + "PEM serverinfo file for certificate"}, + {"certform", OPT_CERTFORM, 'F', + "Certificate format (PEM or DER) PEM default"}, + {"key", OPT_KEY, 's', + "Private Key if not in -cert; default is " TEST_CERT}, + {"keyform", OPT_KEYFORM, 'f', + "Key format (PEM, DER or ENGINE) PEM default"}, + {"pass", OPT_PASS, 's', "Private key file pass phrase source"}, + {"dcert", OPT_DCERT, '<', + "Second certificate file to use (usually for DSA)"}, + {"dcertform", OPT_DCERTFORM, 'F', + "Second certificate format (PEM or DER) PEM default"}, + {"dkey", OPT_DKEY, 's', + "Second private key file to use (usually for DSA)"}, + {"dkeyform", OPT_DKEYFORM, 'F', + "Second key format (PEM, DER or ENGINE) PEM default"}, + {"dpass", OPT_DPASS, 's', "Second private key file pass phrase source"}, + {"nbio_test", OPT_NBIO_TEST, '-', "Test with the non-blocking test bio"}, + {"crlf", OPT_CRLF, '-', "Convert LF from terminal into CRLF"}, + {"debug", OPT_DEBUG, '-', "Print more output"}, + {"msg", OPT_MSG, '-', "Show protocol messages"}, + {"msgfile", OPT_MSGFILE, '>', + "File to send output of -msg or -trace, instead of stdout"}, + {"state", OPT_STATE, '-', "Print the SSL states"}, + {"CAfile", OPT_CAFILE, '<', "PEM format file of CA's"}, + {"CApath", OPT_CAPATH, '/', "PEM format directory of CA's"}, + {"no-CAfile", OPT_NOCAFILE, '-', + "Do not load the default certificates file"}, + {"no-CApath", OPT_NOCAPATH, '-', + "Do not load certificates from the default certificates directory"}, + {"nocert", OPT_NOCERT, '-', "Don't use any certificates (Anon-DH)"}, + {"quiet", OPT_QUIET, '-', "No server output"}, + {"no_resume_ephemeral", OPT_NO_RESUME_EPHEMERAL, '-', + "Disable caching and tickets if ephemeral (EC)DH is used"}, + {"www", OPT_WWW, '-', "Respond to a 'GET /' with a status page"}, + {"WWW", OPT_UPPER_WWW, '-', "Respond to a 'GET with the file ./path"}, + {"servername", OPT_SERVERNAME, 's', + "Servername for HostName TLS extension"}, + {"servername_fatal", OPT_SERVERNAME_FATAL, '-', + "mismatch send fatal alert (default warning alert)"}, + {"cert2", OPT_CERT2, '<', + "Certificate file to use for servername; default is" TEST_CERT2}, + {"key2", OPT_KEY2, 's', + "-Private Key file to use for servername if not in -cert2"}, + {"tlsextdebug", OPT_TLSEXTDEBUG, '-', + "Hex dump of all TLS extensions received"}, + {"HTTP", OPT_HTTP, '-', "Like -WWW but ./path includes HTTP headers"}, + {"id_prefix", OPT_ID_PREFIX, 's', + "Generate SSL/TLS session IDs prefixed by arg"}, + {"rand", OPT_RAND, 's', + "Load the file(s) into the random number generator"}, + {"keymatexport", OPT_KEYMATEXPORT, 's', + "Export keying material using label"}, + {"keymatexportlen", OPT_KEYMATEXPORTLEN, 'p', + "Export len bytes of keying material (default 20)"}, + {"CRL", OPT_CRL, '<', "CRL file to use"}, + {"crl_download", OPT_CRL_DOWNLOAD, '-', + "Download CRL from distribution points"}, + {"cert_chain", OPT_CERT_CHAIN, '<', + "certificate chain file in PEM format"}, + {"dcert_chain", OPT_DCERT_CHAIN, '<', + "second certificate chain file in PEM format"}, + {"chainCApath", OPT_CHAINCAPATH, '/', + "use dir as certificate store path to build CA certificate chain"}, + {"verifyCApath", OPT_VERIFYCAPATH, '/', + "use dir as certificate store path to verify CA certificate"}, + {"no_cache", OPT_NO_CACHE, '-', "Disable session cache"}, + {"ext_cache", OPT_EXT_CACHE, '-', + "Disable internal cache, setup and use external cache"}, + {"CRLform", OPT_CRLFORM, 'F', "CRL format (PEM or DER) PEM is default"}, + {"verify_return_error", OPT_VERIFY_RET_ERROR, '-', + "Close connection on verification error"}, + {"verify_quiet", OPT_VERIFY_QUIET, '-', + "No verify output except verify errors"}, + {"build_chain", OPT_BUILD_CHAIN, '-', "Build certificate chain"}, + {"chainCAfile", OPT_CHAINCAFILE, '<', + "CA file for certificate chain (PEM format)"}, + {"verifyCAfile", OPT_VERIFYCAFILE, '<', + "CA file for certificate verification (PEM format)"}, + {"ign_eof", OPT_IGN_EOF, '-', "ignore input eof (default when -quiet)"}, + {"no_ign_eof", OPT_NO_IGN_EOF, '-', "Do not ignore input eof"}, +#ifndef OPENSSL_NO_OCSP + {"status", OPT_STATUS, '-', "Request certificate status from server"}, + {"status_verbose", OPT_STATUS_VERBOSE, '-', + "Print more output in certificate status callback"}, + {"status_timeout", OPT_STATUS_TIMEOUT, 'n', + "Status request responder timeout"}, + {"status_url", OPT_STATUS_URL, 's', "Status request fallback URL"}, +#endif +#ifndef OPENSSL_NO_SSL_TRACE + {"trace", OPT_TRACE, '-', "trace protocol messages"}, +#endif + {"security_debug", OPT_SECURITY_DEBUG, '-', + "Print output from SSL/TLS security framework"}, + {"security_debug_verbose", OPT_SECURITY_DEBUG_VERBOSE, '-', + "Print more output from SSL/TLS security framework"}, + {"brief", OPT_BRIEF, '-', + "Restrict output to brief summary of connection parameters"}, + {"rev", OPT_REV, '-', + "act as a simple test server which just sends back with the received text reversed"}, + {"async", OPT_ASYNC, '-', "Operate in asynchronous mode"}, + {"ssl_config", OPT_SSL_CONFIG, 's', + "Configure SSL_CTX using the configuration 'val'"}, + {"split_send_frag", OPT_SPLIT_SEND_FRAG, 'n', + "Size used to split data for encrypt pipelines"}, + {"max_pipelines", OPT_MAX_PIPELINES, 'n', + "Maximum number of encrypt/decrypt pipelines to be used"}, + {"read_buf", OPT_READ_BUF, 'n', + "Default read buffer size to be used for connections"}, + OPT_S_OPTIONS, + OPT_V_OPTIONS, + OPT_X_OPTIONS, + {"nbio", OPT_NBIO, '-', "Use non-blocking IO"}, +#ifndef OPENSSL_NO_PSK + {"psk_hint", OPT_PSK_HINT, 's', "PSK identity hint to use"}, + {"psk", OPT_PSK, 's', "PSK in hex (without 0x)"}, +#endif +#ifndef OPENSSL_NO_SRP + {"srpvfile", OPT_SRPVFILE, '<', "The verifier file for SRP"}, + {"srpuserseed", OPT_SRPUSERSEED, 's', + "A seed string for a default user salt"}, +#endif +#ifndef OPENSSL_NO_SSL3 + {"ssl3", OPT_SSL3, '-', "Just talk SSLv3"}, +#endif +#ifndef OPENSSL_NO_TLS1 + {"tls1", OPT_TLS1, '-', "Just talk TLSv1"}, +#endif +#ifndef OPENSSL_NO_TLS1_1 + {"tls1_1", OPT_TLS1_1, '-', "Just talk TLSv1.1"}, +#endif +#ifndef OPENSSL_NO_TLS1_2 + {"tls1_2", OPT_TLS1_2, '-', "just talk TLSv1.2"}, +#endif +#ifndef OPENSSL_NO_DTLS + {"dtls", OPT_DTLS, '-', "Use any DTLS version"}, + {"timeout", OPT_TIMEOUT, '-', "Enable timeouts"}, + {"mtu", OPT_MTU, 'p', "Set link layer MTU"}, + {"listen", OPT_LISTEN, '-', + "Listen for a DTLS ClientHello with a cookie and then connect"}, +#endif +#ifndef OPENSSL_NO_DTLS1 + {"dtls1", OPT_DTLS1, '-', "Just talk DTLSv1"}, +#endif +#ifndef OPENSSL_NO_DTLS1_2 + {"dtls1_2", OPT_DTLS1_2, '-', "Just talk DTLSv1.2"}, +#endif +#ifndef OPENSSL_NO_GMTLS + {"gmtls", OPT_GMTLS, '-', "Just talk GMTLS"}, +#endif +#ifndef OPENSSL_NO_DH + {"no_dhe", OPT_NO_DHE, '-', "Disable ephemeral DH"}, +#endif +#ifndef OPENSSL_NO_NEXTPROTONEG + {"nextprotoneg", OPT_NEXTPROTONEG, 's', + "Set the advertised protocols for the NPN extension (comma-separated list)"}, +#endif +#ifndef OPENSSL_NO_SRTP + {"use_srtp", OPT_SRTP_PROFILES, 's', + "Offer SRTP key management with a colon-separated profile list"}, +#endif + {"alpn", OPT_ALPN, 's', + "Set the advertised protocols for the ALPN extension (comma-separated list)"}, +#ifndef OPENSSL_NO_ENGINE + {"engine", OPT_ENGINE, 's', "Use engine, possibly a hardware device"}, +# ifndef OPENSSL_NO_SKF + {"config", OPT_CONFIG, 's', "Engine config file"}, +# endif +#endif + {NULL, OPT_EOF, 0, NULL} +}; + +#define IS_PROT_FLAG(o) \ + (o == OPT_SSL3 || o == OPT_TLS1 || o == OPT_TLS1_1 || o == OPT_TLS1_2 \ + || o == OPT_GMTLS \ + || o == OPT_DTLS || o == OPT_DTLS1 || o == OPT_DTLS1_2) + +int s_server_main(int argc, char *argv[]) +{ + ENGINE *engine = NULL; + EVP_PKEY *s_key = NULL, *s_dkey = NULL; + SSL_CONF_CTX *cctx = NULL; + const SSL_METHOD *meth = TLS_server_method(); + SSL_EXCERT *exc = NULL; + STACK_OF(OPENSSL_STRING) *ssl_args = NULL; + STACK_OF(X509) *s_chain = NULL, *s_dchain = NULL; + STACK_OF(X509_CRL) *crls = NULL; + X509 *s_cert = NULL, *s_dcert = NULL; + X509_VERIFY_PARAM *vpm = NULL; + const char *CApath = NULL, *CAfile = NULL, *chCApath = NULL, *chCAfile = NULL; + char *dpassarg = NULL, *dpass = NULL, *inrand = NULL; + char *passarg = NULL, *pass = NULL, *vfyCApath = NULL, *vfyCAfile = NULL; + char *crl_file = NULL, *prog; +#ifdef AF_UNIX + int unlink_unix_path = 0; +#endif + do_server_cb server_cb; + int vpmtouched = 0, build_chain = 0, no_cache = 0, ext_cache = 0; +#ifndef OPENSSL_NO_DH + char *dhfile = NULL; + int no_dhe = 0; +#endif + int nocert = 0, ret = 1; + int noCApath = 0, noCAfile = 0; + int s_cert_format = FORMAT_PEM, s_key_format = FORMAT_PEM; + int s_dcert_format = FORMAT_PEM, s_dkey_format = FORMAT_PEM; + int rev = 0, naccept = -1, sdebug = 0; + int socket_family = AF_UNSPEC, socket_type = SOCK_STREAM; + int state = 0, crl_format = FORMAT_PEM, crl_download = 0; + char *host = NULL; + char *port = BUF_strdup(PORT); + unsigned char *context = NULL; + OPTION_CHOICE o; + EVP_PKEY *s_key2 = NULL; + X509 *s_cert2 = NULL; + tlsextctx tlsextcbp = { NULL, NULL, SSL_TLSEXT_ERR_ALERT_WARNING }; + const char *ssl_config = NULL; + int read_buf_len = 0; +#ifndef OPENSSL_NO_NEXTPROTONEG + const char *next_proto_neg_in = NULL; + tlsextnextprotoctx next_proto = { NULL, 0 }; +#endif + const char *alpn_in = NULL; + tlsextalpnctx alpn_ctx = { NULL, 0 }; +#ifndef OPENSSL_NO_PSK + /* by default do not send a PSK identity hint */ + static char *psk_identity_hint = NULL; + char *p; +#endif +#ifndef OPENSSL_NO_SRP + char *srpuserseed = NULL; + char *srp_verifier_file = NULL; +#endif + int min_version = 0, max_version = 0, prot_opt = 0, no_prot_opt = 0; + int s_server_verify = SSL_VERIFY_NONE; + int s_server_session_id_context = 1; /* anything will do */ + const char *s_cert_file = TEST_CERT, *s_key_file = NULL, *s_chain_file = NULL; + const char *s_cert_file2 = TEST_CERT2, *s_key_file2 = NULL; + char *s_dcert_file = NULL, *s_dkey_file = NULL, *s_dchain_file = NULL; +#ifndef OPENSSL_NO_OCSP + int s_tlsextstatus = 0; +#endif + int no_resume_ephemeral = 0; + unsigned int split_send_fragment = 0, max_pipelines = 0; + const char *s_serverinfo_file = NULL; +#ifndef OPENSSL_NO_SKF + CONF *conf = NULL; + char *configfile = default_config_file; +#endif + + /* Init of few remaining global variables */ + local_argc = argc; + local_argv = argv; + + ctx = ctx2 = NULL; + s_nbio = s_nbio_test = 0; + www = 0; + bio_s_out = NULL; + s_debug = 0; + s_msg = 0; + s_quiet = 0; + s_brief = 0; + async = 0; + + cctx = SSL_CONF_CTX_new(); + vpm = X509_VERIFY_PARAM_new(); + if (cctx == NULL || vpm == NULL) + goto end; + SSL_CONF_CTX_set_flags(cctx, + SSL_CONF_FLAG_SERVER | SSL_CONF_FLAG_CMDLINE); + + prog = opt_init(argc, argv, s_server_options); + while ((o = opt_next()) != OPT_EOF) { + if (IS_PROT_FLAG(o) && ++prot_opt > 1) { + BIO_printf(bio_err, "Cannot supply multiple protocol flags\n"); + goto end; + } + if (IS_NO_PROT_FLAG(o)) + no_prot_opt++; + if (prot_opt == 1 && no_prot_opt) { + BIO_printf(bio_err, + "Cannot supply both a protocol flag and '-no_'\n"); + goto end; + } + switch (o) { + case OPT_EOF: + case OPT_ERR: + opthelp: + BIO_printf(bio_err, "%s: Use -help for summary.\n", prog); + goto end; + case OPT_HELP: + opt_help(s_server_options); + ret = 0; + goto end; + + case OPT_4: +#ifdef AF_UNIX + if (socket_family == AF_UNIX) { + OPENSSL_free(host); host = NULL; + OPENSSL_free(port); port = NULL; + } +#endif + socket_family = AF_INET; + break; + case OPT_6: + if (1) { +#ifdef AF_INET6 +#ifdef AF_UNIX + if (socket_family == AF_UNIX) { + OPENSSL_free(host); host = NULL; + OPENSSL_free(port); port = NULL; + } +#endif + socket_family = AF_INET6; + } else { +#endif + BIO_printf(bio_err, "%s: IPv6 domain sockets unsupported\n", prog); + goto end; + } + break; + case OPT_PORT: +#ifdef AF_UNIX + if (socket_family == AF_UNIX) { + socket_family = AF_UNSPEC; + } +#endif + OPENSSL_free(port); port = NULL; + OPENSSL_free(host); host = NULL; + if (BIO_parse_hostserv(opt_arg(), NULL, &port, BIO_PARSE_PRIO_SERV) < 1) { + BIO_printf(bio_err, + "%s: -port argument malformed or ambiguous\n", + port); + goto end; + } + break; + case OPT_ACCEPT: +#ifdef AF_UNIX + if (socket_family == AF_UNIX) { + socket_family = AF_UNSPEC; + } +#endif + OPENSSL_free(port); port = NULL; + OPENSSL_free(host); host = NULL; + if (BIO_parse_hostserv(opt_arg(), &host, &port, BIO_PARSE_PRIO_SERV) < 1) { + BIO_printf(bio_err, + "%s: -accept argument malformed or ambiguous\n", + port); + goto end; + } + break; +#ifdef AF_UNIX + case OPT_UNIX: + socket_family = AF_UNIX; + OPENSSL_free(host); host = BUF_strdup(opt_arg()); + OPENSSL_free(port); port = NULL; + break; + case OPT_UNLINK: + unlink_unix_path = 1; + break; +#endif + case OPT_NACCEPT: + naccept = atol(opt_arg()); + break; + case OPT_VERIFY: + s_server_verify = SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE; + verify_args.depth = atoi(opt_arg()); + if (!s_quiet) + BIO_printf(bio_err, "verify depth is %d\n", verify_args.depth); + break; + case OPT_UPPER_V_VERIFY: + s_server_verify = + SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT | + SSL_VERIFY_CLIENT_ONCE; + verify_args.depth = atoi(opt_arg()); + if (!s_quiet) + BIO_printf(bio_err, + "verify depth is %d, must return a certificate\n", + verify_args.depth); + break; + case OPT_CONTEXT: + context = (unsigned char *)opt_arg(); + break; + case OPT_CERT: + s_cert_file = opt_arg(); + break; + case OPT_CRL: + crl_file = opt_arg(); + break; + case OPT_CRL_DOWNLOAD: + crl_download = 1; + break; + case OPT_SERVERINFO: + s_serverinfo_file = opt_arg(); + break; + case OPT_CERTFORM: + if (!opt_format(opt_arg(), OPT_FMT_PEMDER, &s_cert_format)) + goto opthelp; + break; + case OPT_KEY: + s_key_file = opt_arg(); + break; + case OPT_KEYFORM: + if (!opt_format(opt_arg(), OPT_FMT_ANY, &s_key_format)) + goto opthelp; + break; + case OPT_PASS: + passarg = opt_arg(); + break; + case OPT_CERT_CHAIN: + s_chain_file = opt_arg(); + break; + case OPT_DHPARAM: +#ifndef OPENSSL_NO_DH + dhfile = opt_arg(); +#endif + break; + case OPT_DCERTFORM: + if (!opt_format(opt_arg(), OPT_FMT_PEMDER, &s_dcert_format)) + goto opthelp; + break; + case OPT_DCERT: + s_dcert_file = opt_arg(); + break; + case OPT_DKEYFORM: + if (!opt_format(opt_arg(), OPT_FMT_PEMDER, &s_dkey_format)) + goto opthelp; + break; + case OPT_DPASS: + dpassarg = opt_arg(); + break; + case OPT_DKEY: + s_dkey_file = opt_arg(); + break; + case OPT_DCERT_CHAIN: + s_dchain_file = opt_arg(); + break; + case OPT_NOCERT: + nocert = 1; + break; + case OPT_CAPATH: + CApath = opt_arg(); + break; + case OPT_NOCAPATH: + noCApath = 1; + break; + case OPT_CHAINCAPATH: + chCApath = opt_arg(); + break; + case OPT_VERIFYCAPATH: + vfyCApath = opt_arg(); + break; + case OPT_NO_CACHE: + no_cache = 1; + break; + case OPT_EXT_CACHE: + ext_cache = 1; + break; + case OPT_CRLFORM: + if (!opt_format(opt_arg(), OPT_FMT_PEMDER, &crl_format)) + goto opthelp; + break; + case OPT_S_CASES: + if (ssl_args == NULL) + ssl_args = sk_OPENSSL_STRING_new_null(); + if (ssl_args == NULL + || !sk_OPENSSL_STRING_push(ssl_args, opt_flag()) + || !sk_OPENSSL_STRING_push(ssl_args, opt_arg())) { + BIO_printf(bio_err, "%s: Memory allocation failure\n", prog); + goto end; + } + break; + case OPT_V_CASES: + if (!opt_verify(o, vpm)) + goto end; + vpmtouched++; + break; + case OPT_X_CASES: + if (!args_excert(o, &exc)) + goto end; + break; + case OPT_VERIFY_RET_ERROR: + verify_args.return_error = 1; + break; + case OPT_VERIFY_QUIET: + verify_args.quiet = 1; + break; + case OPT_BUILD_CHAIN: + build_chain = 1; + break; + case OPT_CAFILE: + CAfile = opt_arg(); + break; + case OPT_NOCAFILE: + noCAfile = 1; + break; + case OPT_CHAINCAFILE: + chCAfile = opt_arg(); + break; + case OPT_VERIFYCAFILE: + vfyCAfile = opt_arg(); + break; + case OPT_NBIO: + s_nbio = 1; + break; + case OPT_NBIO_TEST: + s_nbio = s_nbio_test = 1; + break; + case OPT_IGN_EOF: + s_ign_eof = 1; + break; + case OPT_NO_IGN_EOF: + s_ign_eof = 0; + break; + case OPT_DEBUG: + s_debug = 1; + break; + case OPT_TLSEXTDEBUG: + s_tlsextdebug = 1; + break; + case OPT_STATUS: +#ifndef OPENSSL_NO_OCSP + s_tlsextstatus = 1; +#endif + break; + case OPT_STATUS_VERBOSE: +#ifndef OPENSSL_NO_OCSP + s_tlsextstatus = tlscstatp.verbose = 1; +#endif + break; + case OPT_STATUS_TIMEOUT: +#ifndef OPENSSL_NO_OCSP + s_tlsextstatus = 1; + tlscstatp.timeout = atoi(opt_arg()); +#endif + break; + case OPT_STATUS_URL: +#ifndef OPENSSL_NO_OCSP + s_tlsextstatus = 1; + if (!OCSP_parse_url(opt_arg(), + &tlscstatp.host, + &tlscstatp.port, + &tlscstatp.path, &tlscstatp.use_ssl)) { + BIO_printf(bio_err, "Error parsing URL\n"); + goto end; + } +#endif + break; + case OPT_MSG: + s_msg = 1; + break; + case OPT_MSGFILE: + bio_s_msg = BIO_new_file(opt_arg(), "w"); + break; + case OPT_TRACE: +#ifndef OPENSSL_NO_SSL_TRACE + s_msg = 2; +#endif + break; + case OPT_SECURITY_DEBUG: + sdebug = 1; + break; + case OPT_SECURITY_DEBUG_VERBOSE: + sdebug = 2; + break; + case OPT_STATE: + state = 1; + break; + case OPT_CRLF: + s_crlf = 1; + break; + case OPT_QUIET: + s_quiet = 1; + break; + case OPT_BRIEF: + s_quiet = s_brief = verify_args.quiet = 1; + break; + case OPT_NO_DHE: +#ifndef OPENSSL_NO_DH + no_dhe = 1; +#endif + break; + case OPT_NO_RESUME_EPHEMERAL: + no_resume_ephemeral = 1; + break; + case OPT_PSK_HINT: +#ifndef OPENSSL_NO_PSK + psk_identity_hint = opt_arg(); +#endif + break; + case OPT_PSK: +#ifndef OPENSSL_NO_PSK + for (p = psk_key = opt_arg(); *p; p++) { + if (isxdigit(_UC(*p))) + continue; + BIO_printf(bio_err, "Not a hex number '%s'\n", *argv); + goto end; + } +#endif + break; + case OPT_SRPVFILE: +#ifndef OPENSSL_NO_SRP + srp_verifier_file = opt_arg(); + if (min_version < TLS1_VERSION) + min_version = TLS1_VERSION; +#endif + break; + case OPT_SRPUSERSEED: +#ifndef OPENSSL_NO_SRP + srpuserseed = opt_arg(); + if (min_version < TLS1_VERSION) + min_version = TLS1_VERSION; +#endif + break; + case OPT_REV: + rev = 1; + break; + case OPT_WWW: + www = 1; + break; + case OPT_UPPER_WWW: + www = 2; + break; + case OPT_HTTP: + www = 3; + break; + case OPT_SSL_CONFIG: + ssl_config = opt_arg(); + break; + case OPT_SSL3: + min_version = SSL3_VERSION; + max_version = SSL3_VERSION; + break; + case OPT_TLS1_2: + min_version = TLS1_2_VERSION; + max_version = TLS1_2_VERSION; + break; + case OPT_TLS1_1: + min_version = TLS1_1_VERSION; + max_version = TLS1_1_VERSION; + break; + case OPT_TLS1: + min_version = TLS1_VERSION; + max_version = TLS1_VERSION; + break; + case OPT_GMTLS: +#ifndef OPENSSL_NO_GMTLS + meth = GMTLS_server_method(); + //min_version = GMTLS_VERSION; + //max_version = GMTLS_VERSION; +#endif + break; + case OPT_DTLS: +#ifndef OPENSSL_NO_DTLS + meth = DTLS_server_method(); + socket_type = SOCK_DGRAM; +#endif + break; + case OPT_DTLS1: +#ifndef OPENSSL_NO_DTLS + meth = DTLS_server_method(); + min_version = DTLS1_VERSION; + max_version = DTLS1_VERSION; + socket_type = SOCK_DGRAM; +#endif + break; + case OPT_DTLS1_2: +#ifndef OPENSSL_NO_DTLS + meth = DTLS_server_method(); + min_version = DTLS1_2_VERSION; + max_version = DTLS1_2_VERSION; + socket_type = SOCK_DGRAM; +#endif + break; + case OPT_TIMEOUT: +#ifndef OPENSSL_NO_DTLS + enable_timeouts = 1; +#endif + break; + case OPT_MTU: +#ifndef OPENSSL_NO_DTLS + socket_mtu = atol(opt_arg()); +#endif + break; + case OPT_LISTEN: +#ifndef OPENSSL_NO_DTLS + dtlslisten = 1; +#endif + break; + case OPT_ID_PREFIX: + session_id_prefix = opt_arg(); + break; + case OPT_ENGINE: + engine = setup_engine(opt_arg(), 1); + break; +#ifndef OPENSSL_NO_SKF + case OPT_CONFIG: + configfile = opt_arg(); + break; +#endif + case OPT_RAND: + inrand = opt_arg(); + break; + case OPT_SERVERNAME: + tlsextcbp.servername = opt_arg(); + break; + case OPT_SERVERNAME_FATAL: + tlsextcbp.extension_error = SSL_TLSEXT_ERR_ALERT_FATAL; + break; + case OPT_CERT2: + s_cert_file2 = opt_arg(); + break; + case OPT_KEY2: + s_key_file2 = opt_arg(); + break; + case OPT_NEXTPROTONEG: +# ifndef OPENSSL_NO_NEXTPROTONEG + next_proto_neg_in = opt_arg(); +#endif + break; + case OPT_ALPN: + alpn_in = opt_arg(); + break; + case OPT_SRTP_PROFILES: +#ifndef OPENSSL_NO_SRTP + srtp_profiles = opt_arg(); +#endif + break; + case OPT_KEYMATEXPORT: + keymatexportlabel = opt_arg(); + break; + case OPT_KEYMATEXPORTLEN: + keymatexportlen = atoi(opt_arg()); + break; + case OPT_ASYNC: + async = 1; + break; + case OPT_SPLIT_SEND_FRAG: + split_send_fragment = atoi(opt_arg()); + if (split_send_fragment == 0) { + /* + * Not allowed - set to a deliberately bad value so we get an + * error message below + */ + split_send_fragment = SSL3_RT_MAX_PLAIN_LENGTH + 1; + } + break; + case OPT_MAX_PIPELINES: + max_pipelines = atoi(opt_arg()); + break; + case OPT_READ_BUF: + read_buf_len = atoi(opt_arg()); + break; + + } + } + argc = opt_num_rest(); + argv = opt_rest(); + +#ifndef OPENSSL_NO_SKF + if (engine) + BIO_printf(bio_err, "Using configuration from %s\n", configfile); + + if ((conf = app_load_config(configfile)) == NULL) + goto end; + if (configfile != default_config_file && !app_load_modules(conf)) + goto end; +#endif + +#ifndef OPENSSL_NO_DTLS + if (www && socket_type == SOCK_DGRAM) { + BIO_printf(bio_err, "Can't use -HTTP, -www or -WWW with DTLS\n"); + goto end; + } + + if (dtlslisten && socket_type != SOCK_DGRAM) { + BIO_printf(bio_err, "Can only use -listen with DTLS\n"); + goto end; + } +#endif + +#ifdef AF_UNIX + if (socket_family == AF_UNIX && socket_type != SOCK_STREAM) { + BIO_printf(bio_err, + "Can't use unix sockets and datagrams together\n"); + goto end; + } +#endif + + if (split_send_fragment > SSL3_RT_MAX_PLAIN_LENGTH) { + BIO_printf(bio_err, "Bad split send fragment size\n"); + goto end; + } + + if (max_pipelines > SSL_MAX_PIPELINES) { + BIO_printf(bio_err, "Bad max pipelines value\n"); + goto end; + } + + if (!app_passwd(passarg, dpassarg, &pass, &dpass)) { + BIO_printf(bio_err, "Error getting password\n"); + goto end; + } + + if (s_key_file == NULL) + s_key_file = s_cert_file; + + if (s_key_file2 == NULL) + s_key_file2 = s_cert_file2; + + if (!load_excert(&exc)) + goto end; + + if (nocert == 0) { + s_key = load_key(s_key_file, s_key_format, 0, pass, engine, + "server certificate private key file"); + if (!s_key) { + ERR_print_errors(bio_err); + goto end; + } + + s_cert = load_cert(s_cert_file, s_cert_format, + "server certificate file"); + if (!s_cert) { + ERR_print_errors(bio_err); + goto end; + } + if (s_chain_file) { + if (!load_certs(s_chain_file, &s_chain, FORMAT_PEM, NULL, + "server certificate chain")) + goto end; + } + + if (tlsextcbp.servername) { + s_key2 = load_key(s_key_file2, s_key_format, 0, pass, engine, + "second server certificate private key file"); + if (!s_key2) { + ERR_print_errors(bio_err); + goto end; + } + + s_cert2 = load_cert(s_cert_file2, s_cert_format, + "second server certificate file"); + + if (!s_cert2) { + ERR_print_errors(bio_err); + goto end; + } + } + } +#if !defined(OPENSSL_NO_NEXTPROTONEG) + if (next_proto_neg_in) { + size_t len; + next_proto.data = next_protos_parse(&len, next_proto_neg_in); + if (next_proto.data == NULL) + goto end; + next_proto.len = len; + } else { + next_proto.data = NULL; + } +#endif + alpn_ctx.data = NULL; + if (alpn_in) { + size_t len; + alpn_ctx.data = next_protos_parse(&len, alpn_in); + if (alpn_ctx.data == NULL) + goto end; + alpn_ctx.len = len; + } + + if (crl_file) { + X509_CRL *crl; + crl = load_crl(crl_file, crl_format); + if (!crl) { + BIO_puts(bio_err, "Error loading CRL\n"); + ERR_print_errors(bio_err); + goto end; + } + crls = sk_X509_CRL_new_null(); + if (!crls || !sk_X509_CRL_push(crls, crl)) { + BIO_puts(bio_err, "Error adding CRL\n"); + ERR_print_errors(bio_err); + X509_CRL_free(crl); + goto end; + } + } + + if (s_dcert_file) { + + if (s_dkey_file == NULL) + s_dkey_file = s_dcert_file; + + s_dkey = load_key(s_dkey_file, s_dkey_format, + 0, dpass, engine, "second certificate private key file"); + if (!s_dkey) { + ERR_print_errors(bio_err); + goto end; + } + + s_dcert = load_cert(s_dcert_file, s_dcert_format, + "second server certificate file"); + + if (!s_dcert) { + ERR_print_errors(bio_err); + goto end; + } + if (s_dchain_file) { + if (!load_certs(s_dchain_file, &s_dchain, FORMAT_PEM, NULL, + "second server certificate chain")) + goto end; + } + + } + + if (!app_RAND_load_file(NULL, 1) && inrand == NULL + && !RAND_status()) { + BIO_printf(bio_err, + "warning, not much extra random data, consider using the -rand option\n"); + } + if (inrand != NULL) + BIO_printf(bio_err, "%ld semi-random bytes loaded\n", + app_RAND_load_files(inrand)); + + if (bio_s_out == NULL) { + if (s_quiet && !s_debug) { + bio_s_out = BIO_new(BIO_s_null()); + if (s_msg && !bio_s_msg) + bio_s_msg = dup_bio_out(FORMAT_TEXT); + } else { + if (bio_s_out == NULL) + bio_s_out = dup_bio_out(FORMAT_TEXT); + } + } +#if !defined(OPENSSL_NO_RSA) || !defined(OPENSSL_NO_DSA) || !defined(OPENSSL_NO_EC) + if (nocert) +#endif + { + s_cert_file = NULL; + s_key_file = NULL; + s_dcert_file = NULL; + s_dkey_file = NULL; + s_cert_file2 = NULL; + s_key_file2 = NULL; + } + + ctx = SSL_CTX_new(meth); + if (ctx == NULL) { + ERR_print_errors(bio_err); + goto end; + } + if (sdebug) + ssl_ctx_security_debug(ctx, sdebug); + if (ssl_config) { + if (SSL_CTX_config(ctx, ssl_config) == 0) { + BIO_printf(bio_err, "Error using configuration \"%s\"\n", + ssl_config); + ERR_print_errors(bio_err); + goto end; + } + } + if (SSL_CTX_set_min_proto_version(ctx, min_version) == 0) + goto end; + if (SSL_CTX_set_max_proto_version(ctx, max_version) == 0) + goto end; + + if (session_id_prefix) { + if (strlen(session_id_prefix) >= 32) + BIO_printf(bio_err, + "warning: id_prefix is too long, only one new session will be possible\n"); + if (!SSL_CTX_set_generate_session_id(ctx, generate_session_id)) { + BIO_printf(bio_err, "error setting 'id_prefix'\n"); + ERR_print_errors(bio_err); + goto end; + } + BIO_printf(bio_err, "id_prefix '%s' set.\n", session_id_prefix); + } + SSL_CTX_set_quiet_shutdown(ctx, 1); + if (exc) + ssl_ctx_set_excert(ctx, exc); + + if (state) + SSL_CTX_set_info_callback(ctx, apps_ssl_info_callback); + if (no_cache) + SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF); + else if (ext_cache) + init_session_cache_ctx(ctx); + else + SSL_CTX_sess_set_cache_size(ctx, 128); + + if (async) { + SSL_CTX_set_mode(ctx, SSL_MODE_ASYNC); + } + if (split_send_fragment > 0) { + SSL_CTX_set_split_send_fragment(ctx, split_send_fragment); + } + if (max_pipelines > 0) { + SSL_CTX_set_max_pipelines(ctx, max_pipelines); + } + + if (read_buf_len > 0) { + SSL_CTX_set_default_read_buffer_len(ctx, read_buf_len); + } +#ifndef OPENSSL_NO_SRTP + if (srtp_profiles != NULL) { + /* Returns 0 on success! */ + if (SSL_CTX_set_tlsext_use_srtp(ctx, srtp_profiles) != 0) { + BIO_printf(bio_err, "Error setting SRTP profile\n"); + ERR_print_errors(bio_err); + goto end; + } + } +#endif + + if (!ctx_set_verify_locations(ctx, CAfile, CApath, noCAfile, noCApath)) { + ERR_print_errors(bio_err); + goto end; + } + if (vpmtouched && !SSL_CTX_set1_param(ctx, vpm)) { + BIO_printf(bio_err, "Error setting verify params\n"); + ERR_print_errors(bio_err); + goto end; + } + + ssl_ctx_add_crls(ctx, crls, 0); + if (!config_ctx(cctx, ssl_args, ctx)) + goto end; + + if (!ssl_load_stores(ctx, vfyCApath, vfyCAfile, chCApath, chCAfile, + crls, crl_download)) { + BIO_printf(bio_err, "Error loading store locations\n"); + ERR_print_errors(bio_err); + goto end; + } + + if (s_cert2) { + ctx2 = SSL_CTX_new(meth); + if (ctx2 == NULL) { + ERR_print_errors(bio_err); + goto end; + } + } + + if (ctx2) { + BIO_printf(bio_s_out, "Setting secondary ctx parameters\n"); + + if (sdebug) + ssl_ctx_security_debug(ctx, sdebug); + + if (session_id_prefix) { + if (strlen(session_id_prefix) >= 32) + BIO_printf(bio_err, + "warning: id_prefix is too long, only one new session will be possible\n"); + if (!SSL_CTX_set_generate_session_id(ctx2, generate_session_id)) { + BIO_printf(bio_err, "error setting 'id_prefix'\n"); + ERR_print_errors(bio_err); + goto end; + } + BIO_printf(bio_err, "id_prefix '%s' set.\n", session_id_prefix); + } + SSL_CTX_set_quiet_shutdown(ctx2, 1); + if (exc) + ssl_ctx_set_excert(ctx2, exc); + + if (state) + SSL_CTX_set_info_callback(ctx2, apps_ssl_info_callback); + + if (no_cache) + SSL_CTX_set_session_cache_mode(ctx2, SSL_SESS_CACHE_OFF); + else if (ext_cache) + init_session_cache_ctx(ctx2); + else + SSL_CTX_sess_set_cache_size(ctx2, 128); + + if (async) + SSL_CTX_set_mode(ctx2, SSL_MODE_ASYNC); + + if (!ctx_set_verify_locations(ctx2, CAfile, CApath, noCAfile, + noCApath)) { + ERR_print_errors(bio_err); + goto end; + } + if (vpmtouched && !SSL_CTX_set1_param(ctx2, vpm)) { + BIO_printf(bio_err, "Error setting verify params\n"); + ERR_print_errors(bio_err); + goto end; + } + + ssl_ctx_add_crls(ctx2, crls, 0); + if (!config_ctx(cctx, ssl_args, ctx2)) + goto end; + } +#ifndef OPENSSL_NO_NEXTPROTONEG + if (next_proto.data) + SSL_CTX_set_next_protos_advertised_cb(ctx, next_proto_cb, + &next_proto); +#endif + if (alpn_ctx.data) + SSL_CTX_set_alpn_select_cb(ctx, alpn_cb, &alpn_ctx); + +#ifndef OPENSSL_NO_DH + if (!no_dhe) { + DH *dh = NULL; + + if (dhfile) + dh = load_dh_param(dhfile); + else if (s_cert_file) + dh = load_dh_param(s_cert_file); + + if (dh != NULL) { + BIO_printf(bio_s_out, "Setting temp DH parameters\n"); + } else { + BIO_printf(bio_s_out, "Using default temp DH parameters\n"); + } + (void)BIO_flush(bio_s_out); + + if (dh == NULL) + SSL_CTX_set_dh_auto(ctx, 1); + else if (!SSL_CTX_set_tmp_dh(ctx, dh)) { + BIO_puts(bio_err, "Error setting temp DH parameters\n"); + ERR_print_errors(bio_err); + DH_free(dh); + goto end; + } + + if (ctx2) { + if (!dhfile) { + DH *dh2 = load_dh_param(s_cert_file2); + if (dh2 != NULL) { + BIO_printf(bio_s_out, "Setting temp DH parameters\n"); + (void)BIO_flush(bio_s_out); + + DH_free(dh); + dh = dh2; + } + } + if (dh == NULL) + SSL_CTX_set_dh_auto(ctx2, 1); + else if (!SSL_CTX_set_tmp_dh(ctx2, dh)) { + BIO_puts(bio_err, "Error setting temp DH parameters\n"); + ERR_print_errors(bio_err); + DH_free(dh); + goto end; + } + } + DH_free(dh); + } +#endif + + if (!set_cert_key_stuff(ctx, s_cert, s_key, s_chain, build_chain)) + goto end; + + if (s_serverinfo_file != NULL + && !SSL_CTX_use_serverinfo_file(ctx, s_serverinfo_file)) { + ERR_print_errors(bio_err); + goto end; + } + + if (ctx2 && !set_cert_key_stuff(ctx2, s_cert2, s_key2, NULL, build_chain)) + goto end; + + if (s_dcert != NULL) { + if (!set_cert_key_stuff(ctx, s_dcert, s_dkey, s_dchain, build_chain)) + goto end; + } + + if (no_resume_ephemeral) { + SSL_CTX_set_not_resumable_session_callback(ctx, + not_resumable_sess_cb); + + if (ctx2) + SSL_CTX_set_not_resumable_session_callback(ctx2, + not_resumable_sess_cb); + } +#ifndef OPENSSL_NO_PSK + if (psk_key != NULL) { + if (s_debug) + BIO_printf(bio_s_out, "PSK key given, setting server callback\n"); + SSL_CTX_set_psk_server_callback(ctx, psk_server_cb); + } + + if (!SSL_CTX_use_psk_identity_hint(ctx, psk_identity_hint)) { + BIO_printf(bio_err, "error setting PSK identity hint to context\n"); + ERR_print_errors(bio_err); + goto end; + } +#endif + + SSL_CTX_set_verify(ctx, s_server_verify, verify_callback); + if (!SSL_CTX_set_session_id_context(ctx, + (void *)&s_server_session_id_context, + sizeof s_server_session_id_context)) { + BIO_printf(bio_err, "error setting session id context\n"); + ERR_print_errors(bio_err); + goto end; + } + + /* Set DTLS cookie generation and verification callbacks */ + SSL_CTX_set_cookie_generate_cb(ctx, generate_cookie_callback); + SSL_CTX_set_cookie_verify_cb(ctx, verify_cookie_callback); + + if (ctx2) { + SSL_CTX_set_verify(ctx2, s_server_verify, verify_callback); + if (!SSL_CTX_set_session_id_context(ctx2, + (void *)&s_server_session_id_context, + sizeof s_server_session_id_context)) { + BIO_printf(bio_err, "error setting session id context\n"); + ERR_print_errors(bio_err); + goto end; + } + tlsextcbp.biodebug = bio_s_out; + SSL_CTX_set_tlsext_servername_callback(ctx2, ssl_servername_cb); + SSL_CTX_set_tlsext_servername_arg(ctx2, &tlsextcbp); + SSL_CTX_set_tlsext_servername_callback(ctx, ssl_servername_cb); + SSL_CTX_set_tlsext_servername_arg(ctx, &tlsextcbp); + } + +#ifndef OPENSSL_NO_SRP + if (srp_verifier_file != NULL) { + srp_callback_parm.vb = SRP_VBASE_new(srpuserseed); + srp_callback_parm.user = NULL; + srp_callback_parm.login = NULL; + if ((ret = + SRP_VBASE_init(srp_callback_parm.vb, + srp_verifier_file)) != SRP_NO_ERROR) { + BIO_printf(bio_err, + "Cannot initialize SRP verifier file \"%s\":ret=%d\n", + srp_verifier_file, ret); + goto end; + } + SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, verify_callback); + SSL_CTX_set_srp_cb_arg(ctx, &srp_callback_parm); + SSL_CTX_set_srp_username_callback(ctx, ssl_srp_server_param_cb); + } else +#endif + if (CAfile != NULL) { + SSL_CTX_set_client_CA_list(ctx, SSL_load_client_CA_file(CAfile)); + + if (ctx2) + SSL_CTX_set_client_CA_list(ctx2, SSL_load_client_CA_file(CAfile)); + } +#ifndef OPENSSL_NO_OCSP + if (s_tlsextstatus) { + SSL_CTX_set_tlsext_status_cb(ctx, cert_status_cb); + SSL_CTX_set_tlsext_status_arg(ctx, &tlscstatp); + if (ctx2) { + SSL_CTX_set_tlsext_status_cb(ctx2, cert_status_cb); + SSL_CTX_set_tlsext_status_arg(ctx2, &tlscstatp); + } + } +#endif + + BIO_printf(bio_s_out, "ACCEPT\n"); + (void)BIO_flush(bio_s_out); + if (rev) + server_cb = rev_body; + else if (www) + server_cb = www_body; + else + server_cb = sv_body; +#ifdef AF_UNIX + if (socket_family == AF_UNIX + && unlink_unix_path) + unlink(host); +#endif + do_server(&accept_socket, host, port, socket_family, socket_type, + server_cb, context, naccept); + print_stats(bio_s_out, ctx); + ret = 0; + end: + SSL_CTX_free(ctx); + X509_free(s_cert); + sk_X509_CRL_pop_free(crls, X509_CRL_free); + X509_free(s_dcert); + EVP_PKEY_free(s_key); + EVP_PKEY_free(s_dkey); + sk_X509_pop_free(s_chain, X509_free); + sk_X509_pop_free(s_dchain, X509_free); + OPENSSL_free(pass); + OPENSSL_free(dpass); + OPENSSL_free(host); + OPENSSL_free(port); + X509_VERIFY_PARAM_free(vpm); + free_sessions(); + OPENSSL_free(tlscstatp.host); + OPENSSL_free(tlscstatp.port); + OPENSSL_free(tlscstatp.path); + SSL_CTX_free(ctx2); + X509_free(s_cert2); + EVP_PKEY_free(s_key2); +#ifndef OPENSSL_NO_NEXTPROTONEG + OPENSSL_free(next_proto.data); +#endif + OPENSSL_free(alpn_ctx.data); + ssl_excert_free(exc); + sk_OPENSSL_STRING_free(ssl_args); + SSL_CONF_CTX_free(cctx); + release_engine(engine); + BIO_free(bio_s_out); + bio_s_out = NULL; + BIO_free(bio_s_msg); + bio_s_msg = NULL; +#ifdef CHARSET_EBCDIC + BIO_meth_free(methods_ebcdic); +#endif +#ifndef OPENSSL_NO_SKF + NCONF_free(conf); +#endif + return (ret); +} + +static void print_stats(BIO *bio, SSL_CTX *ssl_ctx) +{ + BIO_printf(bio, "%4ld items in the session cache\n", + SSL_CTX_sess_number(ssl_ctx)); + BIO_printf(bio, "%4ld client connects (SSL_connect())\n", + SSL_CTX_sess_connect(ssl_ctx)); + BIO_printf(bio, "%4ld client renegotiates (SSL_connect())\n", + SSL_CTX_sess_connect_renegotiate(ssl_ctx)); + BIO_printf(bio, "%4ld client connects that finished\n", + SSL_CTX_sess_connect_good(ssl_ctx)); + BIO_printf(bio, "%4ld server accepts (SSL_accept())\n", + SSL_CTX_sess_accept(ssl_ctx)); + BIO_printf(bio, "%4ld server renegotiates (SSL_accept())\n", + SSL_CTX_sess_accept_renegotiate(ssl_ctx)); + BIO_printf(bio, "%4ld server accepts that finished\n", + SSL_CTX_sess_accept_good(ssl_ctx)); + BIO_printf(bio, "%4ld session cache hits\n", SSL_CTX_sess_hits(ssl_ctx)); + BIO_printf(bio, "%4ld session cache misses\n", + SSL_CTX_sess_misses(ssl_ctx)); + BIO_printf(bio, "%4ld session cache timeouts\n", + SSL_CTX_sess_timeouts(ssl_ctx)); + BIO_printf(bio, "%4ld callback cache hits\n", + SSL_CTX_sess_cb_hits(ssl_ctx)); + BIO_printf(bio, "%4ld cache full overflows (%ld allowed)\n", + SSL_CTX_sess_cache_full(ssl_ctx), + SSL_CTX_sess_get_cache_size(ssl_ctx)); +} + +static int sv_body(int s, int stype, unsigned char *context) +{ + char *buf = NULL; + fd_set readfds; + int ret = 1, width; + int k, i; + unsigned long l; + SSL *con = NULL; + BIO *sbio; + struct timeval timeout; +#if defined(OPENSSL_SYS_WINDOWS) || defined(OPENSSL_SYS_MSDOS) + struct timeval tv; +#else + struct timeval *timeoutp; +#endif + + buf = app_malloc(bufsize, "server buffer"); + if (s_nbio) { + if (!BIO_socket_nbio(s, 1)) + ERR_print_errors(bio_err); + else if (!s_quiet) + BIO_printf(bio_err, "Turned on non blocking io\n"); + } + + if (con == NULL) { + con = SSL_new(ctx); + + if (s_tlsextdebug) { + SSL_set_tlsext_debug_callback(con, tlsext_cb); + SSL_set_tlsext_debug_arg(con, bio_s_out); + } + + if (context + && !SSL_set_session_id_context(con, + context, strlen((char *)context))) { + BIO_printf(bio_err, "Error setting session id context\n"); + ret = -1; + goto err; + } + } + if (!SSL_clear(con)) { + BIO_printf(bio_err, "Error clearing SSL connection\n"); + ret = -1; + goto err; + } +#ifndef OPENSSL_NO_DTLS + if (stype == SOCK_DGRAM) { + + sbio = BIO_new_dgram(s, BIO_NOCLOSE); + + if (enable_timeouts) { + timeout.tv_sec = 0; + timeout.tv_usec = DGRAM_RCV_TIMEOUT; + BIO_ctrl(sbio, BIO_CTRL_DGRAM_SET_RECV_TIMEOUT, 0, &timeout); + + timeout.tv_sec = 0; + timeout.tv_usec = DGRAM_SND_TIMEOUT; + BIO_ctrl(sbio, BIO_CTRL_DGRAM_SET_SEND_TIMEOUT, 0, &timeout); + } + + if (socket_mtu) { + if (socket_mtu < DTLS_get_link_min_mtu(con)) { + BIO_printf(bio_err, "MTU too small. Must be at least %ld\n", + DTLS_get_link_min_mtu(con)); + ret = -1; + BIO_free(sbio); + goto err; + } + SSL_set_options(con, SSL_OP_NO_QUERY_MTU); + if (!DTLS_set_link_mtu(con, socket_mtu)) { + BIO_printf(bio_err, "Failed to set MTU\n"); + ret = -1; + BIO_free(sbio); + goto err; + } + } else + /* want to do MTU discovery */ + BIO_ctrl(sbio, BIO_CTRL_DGRAM_MTU_DISCOVER, 0, NULL); + + /* turn on cookie exchange */ + SSL_set_options(con, SSL_OP_COOKIE_EXCHANGE); + } else +#endif + sbio = BIO_new_socket(s, BIO_NOCLOSE); + + if (s_nbio_test) { + BIO *test; + + test = BIO_new(BIO_f_nbio_test()); + sbio = BIO_push(test, sbio); + } + + SSL_set_bio(con, sbio, sbio); + SSL_set_accept_state(con); + /* SSL_set_fd(con,s); */ + + if (s_debug) { + BIO_set_callback(SSL_get_rbio(con), bio_dump_callback); + BIO_set_callback_arg(SSL_get_rbio(con), (char *)bio_s_out); + } + if (s_msg) { +#ifndef OPENSSL_NO_SSL_TRACE + if (s_msg == 2) + SSL_set_msg_callback(con, SSL_trace); + else +#endif + SSL_set_msg_callback(con, msg_cb); + SSL_set_msg_callback_arg(con, bio_s_msg ? bio_s_msg : bio_s_out); + } + + if (s_tlsextdebug) { + SSL_set_tlsext_debug_callback(con, tlsext_cb); + SSL_set_tlsext_debug_arg(con, bio_s_out); + } + + if (fileno_stdin() > s) + width = fileno_stdin() + 1; + else + width = s + 1; + for (;;) { + int read_from_terminal; + int read_from_sslcon; + + read_from_terminal = 0; + read_from_sslcon = SSL_has_pending(con) + || (async && SSL_waiting_for_async(con)); + + if (!read_from_sslcon) { + FD_ZERO(&readfds); +#if !defined(OPENSSL_SYS_WINDOWS) && !defined(OPENSSL_SYS_MSDOS) + openssl_fdset(fileno_stdin(), &readfds); +#endif + openssl_fdset(s, &readfds); + /* + * Note: under VMS with SOCKETSHR the second parameter is + * currently of type (int *) whereas under other systems it is + * (void *) if you don't have a cast it will choke the compiler: + * if you do have a cast then you can either go for (int *) or + * (void *). + */ +#if defined(OPENSSL_SYS_WINDOWS) || defined(OPENSSL_SYS_MSDOS) + /* + * Under DOS (non-djgpp) and Windows we can't select on stdin: + * only on sockets. As a workaround we timeout the select every + * second and check for any keypress. In a proper Windows + * application we wouldn't do this because it is inefficient. + */ + tv.tv_sec = 1; + tv.tv_usec = 0; + i = select(width, (void *)&readfds, NULL, NULL, &tv); + if (has_stdin_waiting()) + read_from_terminal = 1; + if ((i < 0) || (!i && !read_from_terminal)) + continue; +#else + if ((SSL_version(con) == DTLS1_VERSION) && + DTLSv1_get_timeout(con, &timeout)) + timeoutp = &timeout; + else + timeoutp = NULL; + + i = select(width, (void *)&readfds, NULL, NULL, timeoutp); + + if ((SSL_version(con) == DTLS1_VERSION) + && DTLSv1_handle_timeout(con) > 0) { + BIO_printf(bio_err, "TIMEOUT occurred\n"); + } + + if (i <= 0) + continue; + if (FD_ISSET(fileno_stdin(), &readfds)) + read_from_terminal = 1; +#endif + if (FD_ISSET(s, &readfds)) + read_from_sslcon = 1; + } + if (read_from_terminal) { + if (s_crlf) { + int j, lf_num; + + i = raw_read_stdin(buf, bufsize / 2); + lf_num = 0; + /* both loops are skipped when i <= 0 */ + for (j = 0; j < i; j++) + if (buf[j] == '\n') + lf_num++; + for (j = i - 1; j >= 0; j--) { + buf[j + lf_num] = buf[j]; + if (buf[j] == '\n') { + lf_num--; + i++; + buf[j + lf_num] = '\r'; + } + } + assert(lf_num == 0); + } else + i = raw_read_stdin(buf, bufsize); + + if (!s_quiet && !s_brief) { + if ((i <= 0) || (buf[0] == 'Q')) { + BIO_printf(bio_s_out, "DONE\n"); + (void)BIO_flush(bio_s_out); + BIO_closesocket(s); + close_accept_socket(); + ret = -11; + goto err; + } + if ((i <= 0) || (buf[0] == 'q')) { + BIO_printf(bio_s_out, "DONE\n"); + (void)BIO_flush(bio_s_out); + if (SSL_version(con) != DTLS1_VERSION) + BIO_closesocket(s); + /* + * close_accept_socket(); ret= -11; + */ + goto err; + } +#ifndef OPENSSL_NO_HEARTBEATS + if ((buf[0] == 'B') && ((buf[1] == '\n') || (buf[1] == '\r'))) { + BIO_printf(bio_err, "HEARTBEATING\n"); + SSL_heartbeat(con); + i = 0; + continue; + } +#endif + if ((buf[0] == 'r') && ((buf[1] == '\n') || (buf[1] == '\r'))) { + SSL_renegotiate(con); + i = SSL_do_handshake(con); + printf("SSL_do_handshake -> %d\n", i); + i = 0; /* 13; */ + continue; + /* + * strcpy(buf,"server side RE-NEGOTIATE\n"); + */ + } + if ((buf[0] == 'R') && ((buf[1] == '\n') || (buf[1] == '\r'))) { + SSL_set_verify(con, + SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE, + NULL); + SSL_renegotiate(con); + i = SSL_do_handshake(con); + printf("SSL_do_handshake -> %d\n", i); + i = 0; /* 13; */ + continue; + /* + * strcpy(buf,"server side RE-NEGOTIATE asking for client + * cert\n"); + */ + } + if (buf[0] == 'P') { + static const char *str = "Lets print some clear text\n"; + BIO_write(SSL_get_wbio(con), str, strlen(str)); + } + if (buf[0] == 'S') { + print_stats(bio_s_out, SSL_get_SSL_CTX(con)); + } + } +#ifdef CHARSET_EBCDIC + ebcdic2ascii(buf, buf, i); +#endif + l = k = 0; + for (;;) { + /* should do a select for the write */ +#ifdef RENEG + static count = 0; + if (++count == 100) { + count = 0; + SSL_renegotiate(con); + } +#endif + k = SSL_write(con, &(buf[l]), (unsigned int)i); +#ifndef OPENSSL_NO_SRP + while (SSL_get_error(con, k) == SSL_ERROR_WANT_X509_LOOKUP) { + BIO_printf(bio_s_out, "LOOKUP renego during write\n"); + SRP_user_pwd_free(srp_callback_parm.user); + srp_callback_parm.user = + SRP_VBASE_get1_by_user(srp_callback_parm.vb, + srp_callback_parm.login); + if (srp_callback_parm.user) + BIO_printf(bio_s_out, "LOOKUP done %s\n", + srp_callback_parm.user->info); + else + BIO_printf(bio_s_out, "LOOKUP not successful\n"); + k = SSL_write(con, &(buf[l]), (unsigned int)i); + } +#endif + switch (SSL_get_error(con, k)) { + case SSL_ERROR_NONE: + break; + case SSL_ERROR_WANT_ASYNC: + BIO_printf(bio_s_out, "Write BLOCK (Async)\n"); + (void)BIO_flush(bio_s_out); + wait_for_async(con); + break; + case SSL_ERROR_WANT_WRITE: + case SSL_ERROR_WANT_READ: + case SSL_ERROR_WANT_X509_LOOKUP: + BIO_printf(bio_s_out, "Write BLOCK\n"); + (void)BIO_flush(bio_s_out); + break; + case SSL_ERROR_WANT_ASYNC_JOB: + /* + * This shouldn't ever happen in s_server. Treat as an error + */ + case SSL_ERROR_SYSCALL: + case SSL_ERROR_SSL: + BIO_printf(bio_s_out, "ERROR\n"); + (void)BIO_flush(bio_s_out); + ERR_print_errors(bio_err); + ret = 1; + goto err; + /* break; */ + case SSL_ERROR_ZERO_RETURN: + BIO_printf(bio_s_out, "DONE\n"); + (void)BIO_flush(bio_s_out); + ret = 1; + goto err; + } + if (k > 0) { + l += k; + i -= k; + } + if (i <= 0) + break; + } + } + if (read_from_sslcon) { + /* + * init_ssl_connection handles all async events itself so if we're + * waiting for async then we shouldn't go back into + * init_ssl_connection + */ + if ((!async || !SSL_waiting_for_async(con)) + && !SSL_is_init_finished(con)) { + i = init_ssl_connection(con); + + if (i < 0) { + ret = 0; + goto err; + } else if (i == 0) { + ret = 1; + goto err; + } + } else { + again: + i = SSL_read(con, (char *)buf, bufsize); +#ifndef OPENSSL_NO_SRP + while (SSL_get_error(con, i) == SSL_ERROR_WANT_X509_LOOKUP) { + BIO_printf(bio_s_out, "LOOKUP renego during read\n"); + SRP_user_pwd_free(srp_callback_parm.user); + srp_callback_parm.user = + SRP_VBASE_get1_by_user(srp_callback_parm.vb, + srp_callback_parm.login); + if (srp_callback_parm.user) + BIO_printf(bio_s_out, "LOOKUP done %s\n", + srp_callback_parm.user->info); + else + BIO_printf(bio_s_out, "LOOKUP not successful\n"); + i = SSL_read(con, (char *)buf, bufsize); + } +#endif + switch (SSL_get_error(con, i)) { + case SSL_ERROR_NONE: +#ifdef CHARSET_EBCDIC + ascii2ebcdic(buf, buf, i); +#endif + raw_write_stdout(buf, (unsigned int)i); + (void)BIO_flush(bio_s_out); + if (SSL_has_pending(con)) + goto again; + break; + case SSL_ERROR_WANT_ASYNC: + BIO_printf(bio_s_out, "Read BLOCK (Async)\n"); + (void)BIO_flush(bio_s_out); + wait_for_async(con); + break; + case SSL_ERROR_WANT_WRITE: + case SSL_ERROR_WANT_READ: + BIO_printf(bio_s_out, "Read BLOCK\n"); + (void)BIO_flush(bio_s_out); + break; + case SSL_ERROR_WANT_ASYNC_JOB: + /* + * This shouldn't ever happen in s_server. Treat as an error + */ + case SSL_ERROR_SYSCALL: + case SSL_ERROR_SSL: + BIO_printf(bio_s_out, "ERROR\n"); + (void)BIO_flush(bio_s_out); + ERR_print_errors(bio_err); + ret = 1; + goto err; + case SSL_ERROR_ZERO_RETURN: + BIO_printf(bio_s_out, "DONE\n"); + (void)BIO_flush(bio_s_out); + ret = 1; + goto err; + } + } + } + } + err: + if (con != NULL) { + BIO_printf(bio_s_out, "shutting down SSL\n"); + SSL_set_shutdown(con, SSL_SENT_SHUTDOWN | SSL_RECEIVED_SHUTDOWN); + SSL_free(con); + } + BIO_printf(bio_s_out, "CONNECTION CLOSED\n"); + OPENSSL_clear_free(buf, bufsize); + if (ret >= 0) + BIO_printf(bio_s_out, "ACCEPT\n"); + (void)BIO_flush(bio_s_out); + return (ret); +} + +static void close_accept_socket(void) +{ + BIO_printf(bio_err, "shutdown accept socket\n"); + if (accept_socket >= 0) { + BIO_closesocket(accept_socket); + } +} + +static int init_ssl_connection(SSL *con) +{ + int i; + const char *str; + X509 *peer; + long verify_err; + char buf[BUFSIZ]; +#if !defined(OPENSSL_NO_NEXTPROTONEG) + const unsigned char *next_proto_neg; + unsigned next_proto_neg_len; +#endif + unsigned char *exportedkeymat; + int retry = 0; + +#ifndef OPENSSL_NO_DTLS + if (dtlslisten) { + BIO_ADDR *client = NULL; + + if ((client = BIO_ADDR_new()) == NULL) { + BIO_printf(bio_err, "ERROR - memory\n"); + return 0; + } + i = DTLSv1_listen(con, client); + if (i > 0) { + BIO *wbio; + int fd = -1; + + wbio = SSL_get_wbio(con); + if (wbio) { + BIO_get_fd(wbio, &fd); + } + + if (!wbio || BIO_connect(fd, client, 0) == 0) { + BIO_printf(bio_err, "ERROR - unable to connect\n"); + BIO_ADDR_free(client); + return 0; + } + BIO_ADDR_free(client); + dtlslisten = 0; + i = SSL_accept(con); + } else { + BIO_ADDR_free(client); + } + } else +#endif + + do { + i = SSL_accept(con); + + if (i <= 0) + retry = BIO_sock_should_retry(i); +#ifdef CERT_CB_TEST_RETRY + { + while (i <= 0 + && SSL_get_error(con, i) == SSL_ERROR_WANT_X509_LOOKUP + && SSL_get_state(con) == TLS_ST_SR_CLNT_HELLO) { + BIO_printf(bio_err, + "LOOKUP from certificate callback during accept\n"); + i = SSL_accept(con); + if (i <= 0) + retry = BIO_sock_should_retry(i); + } + } +#endif + +#ifndef OPENSSL_NO_SRP + while (i <= 0 + && SSL_get_error(con, i) == SSL_ERROR_WANT_X509_LOOKUP) { + BIO_printf(bio_s_out, "LOOKUP during accept %s\n", + srp_callback_parm.login); + SRP_user_pwd_free(srp_callback_parm.user); + srp_callback_parm.user = + SRP_VBASE_get1_by_user(srp_callback_parm.vb, + srp_callback_parm.login); + if (srp_callback_parm.user) + BIO_printf(bio_s_out, "LOOKUP done %s\n", + srp_callback_parm.user->info); + else + BIO_printf(bio_s_out, "LOOKUP not successful\n"); + i = SSL_accept(con); + if (i <= 0) + retry = BIO_sock_should_retry(i); + } +#endif + } while (i < 0 && SSL_waiting_for_async(con)); + + if (i <= 0) { + if ((dtlslisten && i == 0) + || (!dtlslisten && retry)) { + BIO_printf(bio_s_out, "DELAY\n"); + return (1); + } + + BIO_printf(bio_err, "ERROR\n"); + + verify_err = SSL_get_verify_result(con); + if (verify_err != X509_V_OK) { + BIO_printf(bio_err, "verify error:%s\n", + X509_verify_cert_error_string(verify_err)); + } + /* Always print any error messages */ + ERR_print_errors(bio_err); + return (0); + } + + if (s_brief) + print_ssl_summary(con); + + PEM_write_bio_SSL_SESSION(bio_s_out, SSL_get_session(con)); + + peer = SSL_get_peer_certificate(con); + if (peer != NULL) { + BIO_printf(bio_s_out, "Client certificate\n"); + PEM_write_bio_X509(bio_s_out, peer); + X509_NAME_oneline(X509_get_subject_name(peer), buf, sizeof buf); + BIO_printf(bio_s_out, "subject=%s\n", buf); + X509_NAME_oneline(X509_get_issuer_name(peer), buf, sizeof buf); + BIO_printf(bio_s_out, "issuer=%s\n", buf); + X509_free(peer); + peer = NULL; + } + + if (SSL_get_shared_ciphers(con, buf, sizeof buf) != NULL) + BIO_printf(bio_s_out, "Shared ciphers:%s\n", buf); + str = SSL_CIPHER_get_name(SSL_get_current_cipher(con)); + ssl_print_sigalgs(bio_s_out, con); +#ifndef OPENSSL_NO_EC + ssl_print_point_formats(bio_s_out, con); + ssl_print_curves(bio_s_out, con, 0); +#endif + BIO_printf(bio_s_out, "CIPHER is %s\n", (str != NULL) ? str : "(NONE)"); + +#if !defined(OPENSSL_NO_NEXTPROTONEG) + SSL_get0_next_proto_negotiated(con, &next_proto_neg, &next_proto_neg_len); + if (next_proto_neg) { + BIO_printf(bio_s_out, "NEXTPROTO is "); + BIO_write(bio_s_out, next_proto_neg, next_proto_neg_len); + BIO_printf(bio_s_out, "\n"); + } +#endif +#ifndef OPENSSL_NO_SRTP + { + SRTP_PROTECTION_PROFILE *srtp_profile + = SSL_get_selected_srtp_profile(con); + + if (srtp_profile) + BIO_printf(bio_s_out, "SRTP Extension negotiated, profile=%s\n", + srtp_profile->name); + } +#endif + if (SSL_session_reused(con)) + BIO_printf(bio_s_out, "Reused session-id\n"); + BIO_printf(bio_s_out, "Secure Renegotiation IS%s supported\n", + SSL_get_secure_renegotiation_support(con) ? "" : " NOT"); + if (keymatexportlabel != NULL) { + BIO_printf(bio_s_out, "Keying material exporter:\n"); + BIO_printf(bio_s_out, " Label: '%s'\n", keymatexportlabel); + BIO_printf(bio_s_out, " Length: %i bytes\n", keymatexportlen); + exportedkeymat = app_malloc(keymatexportlen, "export key"); + if (!SSL_export_keying_material(con, exportedkeymat, + keymatexportlen, + keymatexportlabel, + strlen(keymatexportlabel), + NULL, 0, 0)) { + BIO_printf(bio_s_out, " Error\n"); + } else { + BIO_printf(bio_s_out, " Keying material: "); + for (i = 0; i < keymatexportlen; i++) + BIO_printf(bio_s_out, "%02X", exportedkeymat[i]); + BIO_printf(bio_s_out, "\n"); + } + OPENSSL_free(exportedkeymat); + } + + (void)BIO_flush(bio_s_out); + return (1); +} + +#ifndef OPENSSL_NO_DH +static DH *load_dh_param(const char *dhfile) +{ + DH *ret = NULL; + BIO *bio; + + if ((bio = BIO_new_file(dhfile, "r")) == NULL) + goto err; + ret = PEM_read_bio_DHparams(bio, NULL, NULL, NULL); + err: + BIO_free(bio); + return (ret); +} +#endif + +static int www_body(int s, int stype, unsigned char *context) +{ + char *buf = NULL; + int ret = 1; + int i, j, k, dot; + SSL *con; + const SSL_CIPHER *c; + BIO *io, *ssl_bio, *sbio; +#ifdef RENEG + int total_bytes = 0; +#endif + int width; + fd_set readfds; + + /* Set width for a select call if needed */ + width = s + 1; + + buf = app_malloc(bufsize, "server www buffer"); + io = BIO_new(BIO_f_buffer()); + ssl_bio = BIO_new(BIO_f_ssl()); + if ((io == NULL) || (ssl_bio == NULL)) + goto err; + + if (s_nbio) { + if (!BIO_socket_nbio(s, 1)) + ERR_print_errors(bio_err); + else if (!s_quiet) + BIO_printf(bio_err, "Turned on non blocking io\n"); + } + + /* lets make the output buffer a reasonable size */ + if (!BIO_set_write_buffer_size(io, bufsize)) + goto err; + + if ((con = SSL_new(ctx)) == NULL) + goto err; + + if (s_tlsextdebug) { + SSL_set_tlsext_debug_callback(con, tlsext_cb); + SSL_set_tlsext_debug_arg(con, bio_s_out); + } + + if (context + && !SSL_set_session_id_context(con, context, + strlen((char *)context))) + goto err; + + sbio = BIO_new_socket(s, BIO_NOCLOSE); + if (s_nbio_test) { + BIO *test; + + test = BIO_new(BIO_f_nbio_test()); + sbio = BIO_push(test, sbio); + } + SSL_set_bio(con, sbio, sbio); + SSL_set_accept_state(con); + + /* SSL_set_fd(con,s); */ + BIO_set_ssl(ssl_bio, con, BIO_CLOSE); + BIO_push(io, ssl_bio); +#ifdef CHARSET_EBCDIC + io = BIO_push(BIO_new(BIO_f_ebcdic_filter()), io); +#endif + + if (s_debug) { + BIO_set_callback(SSL_get_rbio(con), bio_dump_callback); + BIO_set_callback_arg(SSL_get_rbio(con), (char *)bio_s_out); + } + if (s_msg) { +#ifndef OPENSSL_NO_SSL_TRACE + if (s_msg == 2) + SSL_set_msg_callback(con, SSL_trace); + else +#endif + SSL_set_msg_callback(con, msg_cb); + SSL_set_msg_callback_arg(con, bio_s_msg ? bio_s_msg : bio_s_out); + } + + for (;;) { + i = BIO_gets(io, buf, bufsize - 1); + if (i < 0) { /* error */ + if (!BIO_should_retry(io) && !SSL_waiting_for_async(con)) { + if (!s_quiet) + ERR_print_errors(bio_err); + goto err; + } else { + BIO_printf(bio_s_out, "read R BLOCK\n"); +#ifndef OPENSSL_NO_SRP + if (BIO_should_io_special(io) + && BIO_get_retry_reason(io) == BIO_RR_SSL_X509_LOOKUP) { + BIO_printf(bio_s_out, "LOOKUP renego during read\n"); + SRP_user_pwd_free(srp_callback_parm.user); + srp_callback_parm.user = + SRP_VBASE_get1_by_user(srp_callback_parm.vb, + srp_callback_parm.login); + if (srp_callback_parm.user) + BIO_printf(bio_s_out, "LOOKUP done %s\n", + srp_callback_parm.user->info); + else + BIO_printf(bio_s_out, "LOOKUP not successful\n"); + continue; + } +#endif +#if !defined(OPENSSL_SYS_MSDOS) + sleep(1); +#endif + continue; + } + } else if (i == 0) { /* end of input */ + ret = 1; + goto end; + } + + /* else we have data */ + if (((www == 1) && (strncmp("GET ", buf, 4) == 0)) || + ((www == 2) && (strncmp("GET /stats ", buf, 11) == 0))) { + char *p; + X509 *peer = NULL; + STACK_OF(SSL_CIPHER) *sk; + static const char *space = " "; + + if (www == 1 && strncmp("GET /reneg", buf, 10) == 0) { + if (strncmp("GET /renegcert", buf, 14) == 0) + SSL_set_verify(con, + SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE, + NULL); + i = SSL_renegotiate(con); + BIO_printf(bio_s_out, "SSL_renegotiate -> %d\n", i); + /* Send the HelloRequest */ + i = SSL_do_handshake(con); + if (i <= 0) { + BIO_printf(bio_s_out, "SSL_do_handshake() Retval %d\n", + SSL_get_error(con, i)); + ERR_print_errors(bio_err); + goto err; + } + /* Wait for a ClientHello to come back */ + FD_ZERO(&readfds); + openssl_fdset(s, &readfds); + i = select(width, (void *)&readfds, NULL, NULL, NULL); + if (i <= 0 || !FD_ISSET(s, &readfds)) { + BIO_printf(bio_s_out, + "Error waiting for client response\n"); + ERR_print_errors(bio_err); + goto err; + } + /* + * We're not actually expecting any data here and we ignore + * any that is sent. This is just to force the handshake that + * we're expecting to come from the client. If they haven't + * sent one there's not much we can do. + */ + BIO_gets(io, buf, bufsize - 1); + } + + BIO_puts(io, + "HTTP/1.0 200 ok\r\nContent-type: text/html\r\n\r\n"); + BIO_puts(io, "\n"); + BIO_puts(io, "
\n");
+            /* BIO_puts(io, OpenSSL_version(OPENSSL_VERSION)); */
+            BIO_puts(io, "\n");
+            for (i = 0; i < local_argc; i++) {
+                const char *myp;
+                for (myp = local_argv[i]; *myp; myp++)
+                    switch (*myp) {
+                    case '<':
+                        BIO_puts(io, "<");
+                        break;
+                    case '>':
+                        BIO_puts(io, ">");
+                        break;
+                    case '&':
+                        BIO_puts(io, "&");
+                        break;
+                    default:
+                        BIO_write(io, myp, 1);
+                        break;
+                    }
+                BIO_write(io, " ", 1);
+            }
+            BIO_puts(io, "\n");
+
+            BIO_printf(io,
+                       "Secure Renegotiation IS%s supported\n",
+                       SSL_get_secure_renegotiation_support(con) ?
+                       "" : " NOT");
+
+            /*
+             * The following is evil and should not really be done
+             */
+            BIO_printf(io, "Ciphers supported in s_server binary\n");
+            sk = SSL_get_ciphers(con);
+            j = sk_SSL_CIPHER_num(sk);
+            for (i = 0; i < j; i++) {
+                c = sk_SSL_CIPHER_value(sk, i);
+                BIO_printf(io, "%-11s:%-25s ",
+                           SSL_CIPHER_get_version(c), SSL_CIPHER_get_name(c));
+                if ((((i + 1) % 2) == 0) && (i + 1 != j))
+                    BIO_puts(io, "\n");
+            }
+            BIO_puts(io, "\n");
+            p = SSL_get_shared_ciphers(con, buf, bufsize);
+            if (p != NULL) {
+                BIO_printf(io,
+                           "---\nCiphers common between both SSL end points:\n");
+                j = i = 0;
+                while (*p) {
+                    if (*p == ':') {
+                        BIO_write(io, space, 26 - j);
+                        i++;
+                        j = 0;
+                        BIO_write(io, ((i % 3) ? " " : "\n"), 1);
+                    } else {
+                        BIO_write(io, p, 1);
+                        j++;
+                    }
+                    p++;
+                }
+                BIO_puts(io, "\n");
+            }
+            ssl_print_sigalgs(io, con);
+#ifndef OPENSSL_NO_EC
+            ssl_print_curves(io, con, 0);
+#endif
+            BIO_printf(io, (SSL_session_reused(con)
+                            ? "---\nReused, " : "---\nNew, "));
+            c = SSL_get_current_cipher(con);
+            BIO_printf(io, "%s, Cipher is %s\n",
+                       SSL_CIPHER_get_version(c), SSL_CIPHER_get_name(c));
+            SSL_SESSION_print(io, SSL_get_session(con));
+            BIO_printf(io, "---\n");
+            print_stats(io, SSL_get_SSL_CTX(con));
+            BIO_printf(io, "---\n");
+            peer = SSL_get_peer_certificate(con);
+            if (peer != NULL) {
+                BIO_printf(io, "Client certificate\n");
+                X509_print(io, peer);
+                PEM_write_bio_X509(io, peer);
+                X509_free(peer);
+                peer = NULL;
+            } else
+                BIO_puts(io, "no client certificate available\n");
+            BIO_puts(io, "\r\n\r\n");
+            break;
+        } else if ((www == 2 || www == 3)
+                   && (strncmp("GET /", buf, 5) == 0)) {
+            BIO *file;
+            char *p, *e;
+            static const char *text =
+                "HTTP/1.0 200 ok\r\nContent-type: text/plain\r\n\r\n";
+
+            /* skip the '/' */
+            p = &(buf[5]);
+
+            dot = 1;
+            for (e = p; *e != '\0'; e++) {
+                if (e[0] == ' ')
+                    break;
+
+                switch (dot) {
+                case 1:
+                    dot = (e[0] == '.') ? 2 : 0;
+                    break;
+                case 2:
+                    dot = (e[0] == '.') ? 3 : 0;
+                    break;
+                case 3:
+                    dot = (e[0] == '/') ? -1 : 0;
+                    break;
+                }
+                if (dot == 0)
+                    dot = (e[0] == '/') ? 1 : 0;
+            }
+            dot = (dot == 3) || (dot == -1); /* filename contains ".."
+                                              * component */
+
+            if (*e == '\0') {
+                BIO_puts(io, text);
+                BIO_printf(io, "'%s' is an invalid file name\r\n", p);
+                break;
+            }
+            *e = '\0';
+
+            if (dot) {
+                BIO_puts(io, text);
+                BIO_printf(io, "'%s' contains '..' reference\r\n", p);
+                break;
+            }
+
+            if (*p == '/') {
+                BIO_puts(io, text);
+                BIO_printf(io, "'%s' is an invalid path\r\n", p);
+                break;
+            }
+
+            /* if a directory, do the index thang */
+            if (app_isdir(p) > 0) {
+                BIO_puts(io, text);
+                BIO_printf(io, "'%s' is a directory\r\n", p);
+                break;
+            }
+
+            if ((file = BIO_new_file(p, "r")) == NULL) {
+                BIO_puts(io, text);
+                BIO_printf(io, "Error opening '%s'\r\n", p);
+                ERR_print_errors(io);
+                break;
+            }
+
+            if (!s_quiet)
+                BIO_printf(bio_err, "FILE:%s\n", p);
+
+            if (www == 2) {
+                i = strlen(p);
+                if (((i > 5) && (strcmp(&(p[i - 5]), ".html") == 0)) ||
+                    ((i > 4) && (strcmp(&(p[i - 4]), ".php") == 0)) ||
+                    ((i > 4) && (strcmp(&(p[i - 4]), ".htm") == 0)))
+                    BIO_puts(io,
+                             "HTTP/1.0 200 ok\r\nContent-type: text/html\r\n\r\n");
+                else
+                    BIO_puts(io,
+                             "HTTP/1.0 200 ok\r\nContent-type: text/plain\r\n\r\n");
+            }
+            /* send the file */
+            for (;;) {
+                i = BIO_read(file, buf, bufsize);
+                if (i <= 0)
+                    break;
+
+#ifdef RENEG
+                total_bytes += i;
+                BIO_printf(bio_err, "%d\n", i);
+                if (total_bytes > 3 * 1024) {
+                    total_bytes = 0;
+                    BIO_printf(bio_err, "RENEGOTIATE\n");
+                    SSL_renegotiate(con);
+                }
+#endif
+
+                for (j = 0; j < i;) {
+#ifdef RENEG
+                    static count = 0;
+                    if (++count == 13) {
+                        SSL_renegotiate(con);
+                    }
+#endif
+                    k = BIO_write(io, &(buf[j]), i - j);
+                    if (k <= 0) {
+                        if (!BIO_should_retry(io)
+                            && !SSL_waiting_for_async(con))
+                            goto write_error;
+                        else {
+                            BIO_printf(bio_s_out, "rwrite W BLOCK\n");
+                        }
+                    } else {
+                        j += k;
+                    }
+                }
+            }
+ write_error:
+            BIO_free(file);
+            break;
+        }
+    }
+
+    for (;;) {
+        i = (int)BIO_flush(io);
+        if (i <= 0) {
+            if (!BIO_should_retry(io))
+                break;
+        } else
+            break;
+    }
+ end:
+    /* make sure we re-use sessions */
+    SSL_set_shutdown(con, SSL_SENT_SHUTDOWN | SSL_RECEIVED_SHUTDOWN);
+
+ err:
+    if (ret >= 0)
+        BIO_printf(bio_s_out, "ACCEPT\n");
+    OPENSSL_free(buf);
+    BIO_free_all(io);
+    return (ret);
+}
+
+static int rev_body(int s, int stype, unsigned char *context)
+{
+    char *buf = NULL;
+    int i;
+    int ret = 1;
+    SSL *con;
+    BIO *io, *ssl_bio, *sbio;
+
+    buf = app_malloc(bufsize, "server rev buffer");
+    io = BIO_new(BIO_f_buffer());
+    ssl_bio = BIO_new(BIO_f_ssl());
+    if ((io == NULL) || (ssl_bio == NULL))
+        goto err;
+
+    /* lets make the output buffer a reasonable size */
+    if (!BIO_set_write_buffer_size(io, bufsize))
+        goto err;
+
+    if ((con = SSL_new(ctx)) == NULL)
+        goto err;
+
+    if (s_tlsextdebug) {
+        SSL_set_tlsext_debug_callback(con, tlsext_cb);
+        SSL_set_tlsext_debug_arg(con, bio_s_out);
+    }
+    if (context
+        && !SSL_set_session_id_context(con, context,
+                                       strlen((char *)context))) {
+        ERR_print_errors(bio_err);
+        goto err;
+    }
+
+    sbio = BIO_new_socket(s, BIO_NOCLOSE);
+    SSL_set_bio(con, sbio, sbio);
+    SSL_set_accept_state(con);
+
+    BIO_set_ssl(ssl_bio, con, BIO_CLOSE);
+    BIO_push(io, ssl_bio);
+#ifdef CHARSET_EBCDIC
+    io = BIO_push(BIO_new(BIO_f_ebcdic_filter()), io);
+#endif
+
+    if (s_debug) {
+        BIO_set_callback(SSL_get_rbio(con), bio_dump_callback);
+        BIO_set_callback_arg(SSL_get_rbio(con), (char *)bio_s_out);
+    }
+    if (s_msg) {
+#ifndef OPENSSL_NO_SSL_TRACE
+        if (s_msg == 2)
+            SSL_set_msg_callback(con, SSL_trace);
+        else
+#endif
+            SSL_set_msg_callback(con, msg_cb);
+        SSL_set_msg_callback_arg(con, bio_s_msg ? bio_s_msg : bio_s_out);
+    }
+
+    for (;;) {
+        i = BIO_do_handshake(io);
+        if (i > 0)
+            break;
+        if (!BIO_should_retry(io)) {
+            BIO_puts(bio_err, "CONNECTION FAILURE\n");
+            ERR_print_errors(bio_err);
+            goto end;
+        }
+#ifndef OPENSSL_NO_SRP
+        if (BIO_should_io_special(io)
+            && BIO_get_retry_reason(io) == BIO_RR_SSL_X509_LOOKUP) {
+            BIO_printf(bio_s_out, "LOOKUP renego during accept\n");
+            SRP_user_pwd_free(srp_callback_parm.user);
+            srp_callback_parm.user =
+                SRP_VBASE_get1_by_user(srp_callback_parm.vb,
+                                       srp_callback_parm.login);
+            if (srp_callback_parm.user)
+                BIO_printf(bio_s_out, "LOOKUP done %s\n",
+                           srp_callback_parm.user->info);
+            else
+                BIO_printf(bio_s_out, "LOOKUP not successful\n");
+            continue;
+        }
+#endif
+    }
+    BIO_printf(bio_err, "CONNECTION ESTABLISHED\n");
+    print_ssl_summary(con);
+
+    for (;;) {
+        i = BIO_gets(io, buf, bufsize - 1);
+        if (i < 0) {            /* error */
+            if (!BIO_should_retry(io)) {
+                if (!s_quiet)
+                    ERR_print_errors(bio_err);
+                goto err;
+            } else {
+                BIO_printf(bio_s_out, "read R BLOCK\n");
+#ifndef OPENSSL_NO_SRP
+                if (BIO_should_io_special(io)
+                    && BIO_get_retry_reason(io) == BIO_RR_SSL_X509_LOOKUP) {
+                    BIO_printf(bio_s_out, "LOOKUP renego during read\n");
+                    SRP_user_pwd_free(srp_callback_parm.user);
+                    srp_callback_parm.user =
+                        SRP_VBASE_get1_by_user(srp_callback_parm.vb,
+                                               srp_callback_parm.login);
+                    if (srp_callback_parm.user)
+                        BIO_printf(bio_s_out, "LOOKUP done %s\n",
+                                   srp_callback_parm.user->info);
+                    else
+                        BIO_printf(bio_s_out, "LOOKUP not successful\n");
+                    continue;
+                }
+#endif
+#if !defined(OPENSSL_SYS_MSDOS)
+                sleep(1);
+#endif
+                continue;
+            }
+        } else if (i == 0) {    /* end of input */
+            ret = 1;
+            BIO_printf(bio_err, "CONNECTION CLOSED\n");
+            goto end;
+        } else {
+            char *p = buf + i - 1;
+            while (i && (*p == '\n' || *p == '\r')) {
+                p--;
+                i--;
+            }
+            if (!s_ign_eof && (i == 5) && (strncmp(buf, "CLOSE", 5) == 0)) {
+                ret = 1;
+                BIO_printf(bio_err, "CONNECTION CLOSED\n");
+                goto end;
+            }
+            BUF_reverse((unsigned char *)buf, NULL, i);
+            buf[i] = '\n';
+            BIO_write(io, buf, i + 1);
+            for (;;) {
+                i = BIO_flush(io);
+                if (i > 0)
+                    break;
+                if (!BIO_should_retry(io))
+                    goto end;
+            }
+        }
+    }
+ end:
+    /* make sure we re-use sessions */
+    SSL_set_shutdown(con, SSL_SENT_SHUTDOWN | SSL_RECEIVED_SHUTDOWN);
+
+ err:
+
+    OPENSSL_free(buf);
+    BIO_free_all(io);
+    return (ret);
+}
+
+#define MAX_SESSION_ID_ATTEMPTS 10
+static int generate_session_id(const SSL *ssl, unsigned char *id,
+                               unsigned int *id_len)
+{
+    unsigned int count = 0;
+    do {
+        if (RAND_bytes(id, *id_len) <= 0)
+            return 0;
+        /*
+         * Prefix the session_id with the required prefix. NB: If our prefix
+         * is too long, clip it - but there will be worse effects anyway, eg.
+         * the server could only possibly create 1 session ID (ie. the
+         * prefix!) so all future session negotiations will fail due to
+         * conflicts.
+         */
+        memcpy(id, session_id_prefix,
+               (strlen(session_id_prefix) < *id_len) ?
+               strlen(session_id_prefix) : *id_len);
+    }
+    while (SSL_has_matching_session_id(ssl, id, *id_len) &&
+           (++count < MAX_SESSION_ID_ATTEMPTS));
+    if (count >= MAX_SESSION_ID_ATTEMPTS)
+        return 0;
+    return 1;
+}
+
+/*
+ * By default s_server uses an in-memory cache which caches SSL_SESSION
+ * structures without any serialisation. This hides some bugs which only
+ * become apparent in deployed servers. By implementing a basic external
+ * session cache some issues can be debugged using s_server.
+ */
+
+typedef struct simple_ssl_session_st {
+    unsigned char *id;
+    unsigned int idlen;
+    unsigned char *der;
+    int derlen;
+    struct simple_ssl_session_st *next;
+} simple_ssl_session;
+
+static simple_ssl_session *first = NULL;
+
+static int add_session(SSL *ssl, SSL_SESSION *session)
+{
+    simple_ssl_session *sess = app_malloc(sizeof(*sess), "get session");
+    unsigned char *p;
+
+    SSL_SESSION_get_id(session, &sess->idlen);
+    sess->derlen = i2d_SSL_SESSION(session, NULL);
+    if (sess->derlen < 0) {
+        BIO_printf(bio_err, "Error encoding session\n");
+        OPENSSL_free(sess);
+        return 0;
+    }
+
+    sess->id = OPENSSL_memdup(SSL_SESSION_get_id(session, NULL), sess->idlen);
+    sess->der = app_malloc(sess->derlen, "get session buffer");
+    if (!sess->id) {
+        BIO_printf(bio_err, "Out of memory adding to external cache\n");
+        OPENSSL_free(sess->id);
+        OPENSSL_free(sess->der);
+        OPENSSL_free(sess);
+        return 0;
+    }
+    p = sess->der;
+
+    /* Assume it still works. */
+    if (i2d_SSL_SESSION(session, &p) != sess->derlen) {
+        BIO_printf(bio_err, "Unexpected session encoding length\n");
+        OPENSSL_free(sess->id);
+        OPENSSL_free(sess->der);
+        OPENSSL_free(sess);
+        return 0;
+    }
+
+    sess->next = first;
+    first = sess;
+    BIO_printf(bio_err, "New session added to external cache\n");
+    return 0;
+}
+
+static SSL_SESSION *get_session(SSL *ssl, const unsigned char *id, int idlen,
+                                int *do_copy)
+{
+    simple_ssl_session *sess;
+    *do_copy = 0;
+    for (sess = first; sess; sess = sess->next) {
+        if (idlen == (int)sess->idlen && !memcmp(sess->id, id, idlen)) {
+            const unsigned char *p = sess->der;
+            BIO_printf(bio_err, "Lookup session: cache hit\n");
+            return d2i_SSL_SESSION(NULL, &p, sess->derlen);
+        }
+    }
+    BIO_printf(bio_err, "Lookup session: cache miss\n");
+    return NULL;
+}
+
+static void del_session(SSL_CTX *sctx, SSL_SESSION *session)
+{
+    simple_ssl_session *sess, *prev = NULL;
+    const unsigned char *id;
+    unsigned int idlen;
+    id = SSL_SESSION_get_id(session, &idlen);
+    for (sess = first; sess; sess = sess->next) {
+        if (idlen == sess->idlen && !memcmp(sess->id, id, idlen)) {
+            if (prev)
+                prev->next = sess->next;
+            else
+                first = sess->next;
+            OPENSSL_free(sess->id);
+            OPENSSL_free(sess->der);
+            OPENSSL_free(sess);
+            return;
+        }
+        prev = sess;
+    }
+}
+
+static void init_session_cache_ctx(SSL_CTX *sctx)
+{
+    SSL_CTX_set_session_cache_mode(sctx,
+                                   SSL_SESS_CACHE_NO_INTERNAL |
+                                   SSL_SESS_CACHE_SERVER);
+    SSL_CTX_sess_set_new_cb(sctx, add_session);
+    SSL_CTX_sess_set_get_cb(sctx, get_session);
+    SSL_CTX_sess_set_remove_cb(sctx, del_session);
+}
+
+static void free_sessions(void)
+{
+    simple_ssl_session *sess, *tsess;
+    for (sess = first; sess;) {
+        OPENSSL_free(sess->id);
+        OPENSSL_free(sess->der);
+        tsess = sess;
+        sess = sess->next;
+        OPENSSL_free(tsess);
+    }
+    first = NULL;
+}
+
+#endif                          /* OPENSSL_NO_SOCK */
diff --git a/apps/s_socket.c b/apps/s_socket.c
new file mode 100644
index 000000000..d16f5ad81
--- /dev/null
+++ b/apps/s_socket.c
@@ -0,0 +1,201 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/* socket-related functions used by s_client and s_server */
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+/*
+ * With IPv6, it looks like Digital has mixed up the proper order of
+ * recursive header file inclusion, resulting in the compiler complaining
+ * that u_int isn't defined, but only if _POSIX_C_SOURCE is defined, which is
+ * needed to have fileno() declared correctly...  So let's define u_int
+ */
+#if defined(OPENSSL_SYS_VMS_DECC) && !defined(__U_INT)
+# define __U_INT
+typedef unsigned int u_int;
+#endif
+
+#ifndef OPENSSL_NO_SOCK
+
+# define USE_SOCKETS
+# include "apps.h"
+# undef USE_SOCKETS
+# include "s_apps.h"
+
+# include 
+# include 
+
+/*
+ * init_client - helper routine to set up socket communication
+ * @sock: pointer to storage of resulting socket.
+ * @host: the host name or path (for AF_UNIX) to connect to.
+ * @port: the port to connect to (ignored for AF_UNIX).
+ * @family: desired socket family, may be AF_INET, AF_INET6, AF_UNIX or
+ *  AF_UNSPEC
+ * @type: socket type, must be SOCK_STREAM or SOCK_DGRAM
+ *
+ * This will create a socket and use it to connect to a host:port, or if
+ * family == AF_UNIX, to the path found in host.
+ *
+ * If the host has more than one address, it will try them one by one until
+ * a successful connection is established.  The resulting socket will be
+ * found in *sock on success, it will be given INVALID_SOCKET otherwise.
+ *
+ * Returns 1 on success, 0 on failure.
+ */
+int init_client(int *sock, const char *host, const char *port,
+                int family, int type)
+{
+    BIO_ADDRINFO *res = NULL;
+    const BIO_ADDRINFO *ai = NULL;
+    int ret;
+
+    if (!BIO_sock_init())
+        return 0;
+
+    ret = BIO_lookup(host, port, BIO_LOOKUP_CLIENT, family, type, &res);
+    if (ret == 0) {
+        ERR_print_errors(bio_err);
+        return 0;
+    }
+
+    ret = 0;
+    for (ai = res; ai != NULL; ai = BIO_ADDRINFO_next(ai)) {
+        /* Admittedly, these checks are quite paranoid, we should not get
+         * anything in the BIO_ADDRINFO chain that we haven't
+         * asked for. */
+        OPENSSL_assert((family == AF_UNSPEC || family == BIO_ADDRINFO_family(res))
+                       && (type == 0 || type == BIO_ADDRINFO_socktype(res)));
+
+        *sock = BIO_socket(BIO_ADDRINFO_family(ai), BIO_ADDRINFO_socktype(ai),
+                           BIO_ADDRINFO_protocol(res), 0);
+        if (*sock == INVALID_SOCKET) {
+            /* Maybe the kernel doesn't support the socket family, even if
+             * BIO_lookup() added it in the returned result...
+             */
+            continue;
+        }
+        if (!BIO_connect(*sock, BIO_ADDRINFO_address(ai), 0)) {
+            BIO_closesocket(*sock);
+            *sock = INVALID_SOCKET;
+            continue;
+        }
+
+        /* Success, don't try any more addresses */
+        break;
+    }
+
+    if (*sock == INVALID_SOCKET) {
+        ERR_print_errors(bio_err);
+    } else {
+        /* Remove any stale errors from previous connection attempts */
+        ERR_clear_error();
+        ret = 1;
+    }
+    BIO_ADDRINFO_free(res);
+    return ret;
+}
+
+/*
+ * do_server - helper routine to perform a server operation
+ * @accept_sock: pointer to storage of resulting socket.
+ * @host: the host name or path (for AF_UNIX) to connect to.
+ * @port: the port to connect to (ignored for AF_UNIX).
+ * @family: desired socket family, may be AF_INET, AF_INET6, AF_UNIX or
+ *  AF_UNSPEC
+ * @type: socket type, must be SOCK_STREAM or SOCK_DGRAM
+ * @cb: pointer to a function that receives the accepted socket and
+ *  should perform the communication with the connecting client.
+ * @context: pointer to memory that's passed verbatim to the cb function.
+ * @naccept: number of times an incoming connect should be accepted.  If -1,
+ *  unlimited number.
+ *
+ * This will create a socket and use it to listen to a host:port, or if
+ * family == AF_UNIX, to the path found in host, then start accepting
+ * incoming connections and run cb on the resulting socket.
+ *
+ * 0 on failure, something other on success.
+ */
+int do_server(int *accept_sock, const char *host, const char *port,
+              int family, int type, do_server_cb cb,
+              unsigned char *context, int naccept)
+{
+    int asock = 0;
+    int sock;
+    int i;
+    BIO_ADDRINFO *res = NULL;
+    int ret = 0;
+
+    if (!BIO_sock_init())
+        return 0;
+
+    if (!BIO_lookup(host, port, BIO_LOOKUP_SERVER, family, type, &res)) {
+        ERR_print_errors(bio_err);
+        return 0;
+    }
+
+    /* Admittedly, these checks are quite paranoid, we should not get
+     * anything in the BIO_ADDRINFO chain that we haven't asked for */
+    OPENSSL_assert((family == AF_UNSPEC || family == BIO_ADDRINFO_family(res))
+                   && (type == 0 || type == BIO_ADDRINFO_socktype(res)));
+
+    asock = BIO_socket(BIO_ADDRINFO_family(res), BIO_ADDRINFO_socktype(res),
+                       BIO_ADDRINFO_protocol(res), 0);
+    if (asock == INVALID_SOCKET
+        || !BIO_listen(asock, BIO_ADDRINFO_address(res), BIO_SOCK_REUSEADDR)) {
+        BIO_ADDRINFO_free(res);
+        ERR_print_errors(bio_err);
+        if (asock != INVALID_SOCKET)
+            BIO_closesocket(asock);
+        goto end;
+    }
+
+    BIO_ADDRINFO_free(res);
+    res = NULL;
+
+    if (accept_sock != NULL)
+        *accept_sock = asock;
+    for (;;) {
+        if (type == SOCK_STREAM) {
+            do {
+                sock = BIO_accept_ex(asock, NULL, 0);
+            } while (sock < 0 && BIO_sock_should_retry(ret));
+            if (sock < 0) {
+                ERR_print_errors(bio_err);
+                BIO_closesocket(asock);
+                break;
+            }
+            i = (*cb)(sock, type, context);
+            BIO_closesocket(sock);
+        } else {
+            i = (*cb)(asock, type, context);
+        }
+
+        if (naccept != -1)
+            naccept--;
+        if (i < 0 || naccept == 0) {
+            BIO_closesocket(asock);
+            ret = i;
+            break;
+        }
+    }
+ end:
+# ifdef AF_UNIX
+    if (family == AF_UNIX)
+        unlink(host);
+# endif
+    return ret;
+}
+
+#endif  /* OPENSSL_NO_SOCK */
diff --git a/apps/s_time.c b/apps/s_time.c
new file mode 100644
index 000000000..d01ff2e3f
--- /dev/null
+++ b/apps/s_time.c
@@ -0,0 +1,422 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#define NO_SHUTDOWN
+
+#include 
+#include 
+#include 
+
+#include 
+
+#ifndef OPENSSL_NO_SOCK
+
+#define USE_SOCKETS
+#include "apps.h"
+#include 
+#include 
+#include 
+#include "s_apps.h"
+#include 
+#if !defined(OPENSSL_SYS_MSDOS)
+# include OPENSSL_UNISTD
+#endif
+
+#undef ioctl
+#define ioctl ioctlsocket
+
+#define SSL_CONNECT_NAME        "localhost:4433"
+
+/* no default cert. */
+/*
+ * #define TEST_CERT "client.pem"
+ */
+
+#undef min
+#undef max
+#define min(a,b) (((a) < (b)) ? (a) : (b))
+#define max(a,b) (((a) > (b)) ? (a) : (b))
+
+#undef SECONDS
+#define SECONDS 30
+#define SECONDSSTR "30"
+
+static SSL *doConnection(SSL *scon, const char *host, SSL_CTX *ctx);
+
+static const char fmt_http_get_cmd[] = "GET %s HTTP/1.0\r\n\r\n";
+
+typedef enum OPTION_choice {
+    OPT_ERR = -1, OPT_EOF = 0, OPT_HELP,
+    OPT_CONNECT, OPT_CIPHER, OPT_CERT, OPT_KEY, OPT_CAPATH,
+    OPT_CAFILE, OPT_NOCAPATH, OPT_NOCAFILE, OPT_NEW, OPT_REUSE, OPT_BUGS,
+    OPT_VERIFY, OPT_TIME, OPT_SSL3,
+    OPT_WWW
+} OPTION_CHOICE;
+
+OPTIONS s_time_options[] = {
+    {"help", OPT_HELP, '-', "Display this summary"},
+    {"connect", OPT_CONNECT, 's',
+     "Where to connect as post:port (default is " SSL_CONNECT_NAME ")"},
+    {"cipher", OPT_CIPHER, 's', "Cipher to use, see 'openssl ciphers'"},
+    {"cert", OPT_CERT, '<', "Cert file to use, PEM format assumed"},
+    {"key", OPT_KEY, '<', "File with key, PEM; default is -cert file"},
+    {"CApath", OPT_CAPATH, '/', "PEM format directory of CA's"},
+    {"CAfile", OPT_CAFILE, '<', "PEM format file of CA's"},
+    {"no-CAfile", OPT_NOCAFILE, '-',
+     "Do not load the default certificates file"},
+    {"no-CApath", OPT_NOCAPATH, '-',
+     "Do not load certificates from the default certificates directory"},
+    {"new", OPT_NEW, '-', "Just time new connections"},
+    {"reuse", OPT_REUSE, '-', "Just time connection reuse"},
+    {"bugs", OPT_BUGS, '-', "Turn on SSL bug compatibility"},
+    {"verify", OPT_VERIFY, 'p',
+     "Turn on peer certificate verification, set depth"},
+    {"time", OPT_TIME, 'p', "Seconds to collect data, default " SECONDSSTR},
+    {"www", OPT_WWW, 's', "Fetch specified page from the site"},
+#ifndef OPENSSL_NO_SSL3
+    {"ssl3", OPT_SSL3, '-', "Just use SSLv3"},
+#endif
+    {NULL}
+};
+
+#define START   0
+#define STOP    1
+
+static double tm_Time_F(int s)
+{
+    return app_tminterval(s, 1);
+}
+
+int s_time_main(int argc, char **argv)
+{
+    char buf[1024 * 8];
+    SSL *scon = NULL;
+    SSL_CTX *ctx = NULL;
+    const SSL_METHOD *meth = NULL;
+    char *CApath = NULL, *CAfile = NULL, *cipher = NULL, *www_path = NULL;
+    char *host = SSL_CONNECT_NAME, *certfile = NULL, *keyfile = NULL, *prog;
+    double totalTime = 0.0;
+    int noCApath = 0, noCAfile = 0;
+    int maxtime = SECONDS, nConn = 0, perform = 3, ret = 1, i, st_bugs = 0;
+    long bytes_read = 0, finishtime = 0;
+    OPTION_CHOICE o;
+    int max_version = 0, ver, buf_len;
+    size_t buf_size;
+
+    meth = TLS_client_method();
+
+    prog = opt_init(argc, argv, s_time_options);
+    while ((o = opt_next()) != OPT_EOF) {
+        switch (o) {
+        case OPT_EOF:
+        case OPT_ERR:
+ opthelp:
+            BIO_printf(bio_err, "%s: Use -help for summary.\n", prog);
+            goto end;
+        case OPT_HELP:
+            opt_help(s_time_options);
+            ret = 0;
+            goto end;
+        case OPT_CONNECT:
+            host = opt_arg();
+            break;
+        case OPT_REUSE:
+            perform = 2;
+            break;
+        case OPT_NEW:
+            perform = 1;
+            break;
+        case OPT_VERIFY:
+            if (!opt_int(opt_arg(), &verify_args.depth))
+                goto opthelp;
+            BIO_printf(bio_err, "%s: verify depth is %d\n",
+                       prog, verify_args.depth);
+            break;
+        case OPT_CERT:
+            certfile = opt_arg();
+            break;
+        case OPT_KEY:
+            keyfile = opt_arg();
+            break;
+        case OPT_CAPATH:
+            CApath = opt_arg();
+            break;
+        case OPT_CAFILE:
+            CAfile = opt_arg();
+            break;
+        case OPT_NOCAPATH:
+            noCApath = 1;
+            break;
+        case OPT_NOCAFILE:
+            noCAfile = 1;
+            break;
+        case OPT_CIPHER:
+            cipher = opt_arg();
+            break;
+        case OPT_BUGS:
+            st_bugs = 1;
+            break;
+        case OPT_TIME:
+            if (!opt_int(opt_arg(), &maxtime))
+                goto opthelp;
+            break;
+        case OPT_WWW:
+            www_path = opt_arg();
+            buf_size = strlen(www_path) + sizeof(fmt_http_get_cmd) - 2;  /* 2 is for %s */
+            if (buf_size > sizeof(buf)) {
+                BIO_printf(bio_err, "%s: -www option is too long\n", prog);
+                goto end;
+            }
+            break;
+        case OPT_SSL3:
+            max_version = SSL3_VERSION;
+            break;
+        }
+    }
+    argc = opt_num_rest();
+    if (argc != 0)
+        goto opthelp;
+
+    if (cipher == NULL)
+        cipher = getenv("SSL_CIPHER");
+    if (cipher == NULL) {
+        BIO_printf(bio_err, "No CIPHER specified\n");
+        goto end;
+    }
+
+    if ((ctx = SSL_CTX_new(meth)) == NULL)
+        goto end;
+
+    SSL_CTX_set_quiet_shutdown(ctx, 1);
+    if (SSL_CTX_set_max_proto_version(ctx, max_version) == 0)
+        goto end;
+
+    if (st_bugs)
+        SSL_CTX_set_options(ctx, SSL_OP_ALL);
+    if (!SSL_CTX_set_cipher_list(ctx, cipher))
+        goto end;
+    if (!set_cert_stuff(ctx, certfile, keyfile))
+        goto end;
+
+    if (!ctx_set_verify_locations(ctx, CAfile, CApath, noCAfile, noCApath)) {
+        ERR_print_errors(bio_err);
+        goto end;
+    }
+    if (!(perform & 1))
+        goto next;
+    printf("Collecting connection statistics for %d seconds\n", maxtime);
+
+    /* Loop and time how long it takes to make connections */
+
+    bytes_read = 0;
+    finishtime = (long)time(NULL) + maxtime;
+    tm_Time_F(START);
+    for (;;) {
+        if (finishtime < (long)time(NULL))
+            break;
+
+        if ((scon = doConnection(NULL, host, ctx)) == NULL)
+            goto end;
+
+        if (www_path != NULL) {
+            buf_len = BIO_snprintf(buf, sizeof buf,
+                                   fmt_http_get_cmd, www_path);
+            if (SSL_write(scon, buf, buf_len) <= 0)
+                goto end;
+            while ((i = SSL_read(scon, buf, sizeof(buf))) > 0)
+                bytes_read += i;
+        }
+#ifdef NO_SHUTDOWN
+        SSL_set_shutdown(scon, SSL_SENT_SHUTDOWN | SSL_RECEIVED_SHUTDOWN);
+#else
+        SSL_shutdown(scon);
+#endif
+        BIO_closesocket(SSL_get_fd(scon));
+
+        nConn += 1;
+        if (SSL_session_reused(scon))
+            ver = 'r';
+        else {
+            ver = SSL_version(scon);
+            if (ver == TLS1_VERSION)
+                ver = 't';
+            else if (ver == SSL3_VERSION)
+                ver = '3';
+            else
+                ver = '*';
+        }
+        fputc(ver, stdout);
+        fflush(stdout);
+
+        SSL_free(scon);
+        scon = NULL;
+    }
+    totalTime += tm_Time_F(STOP); /* Add the time for this iteration */
+
+    i = (int)((long)time(NULL) - finishtime + maxtime);
+    printf
+        ("\n\n%d connections in %.2fs; %.2f connections/user sec, bytes read %ld\n",
+         nConn, totalTime, ((double)nConn / totalTime), bytes_read);
+    printf
+        ("%d connections in %ld real seconds, %ld bytes read per connection\n",
+         nConn, (long)time(NULL) - finishtime + maxtime, bytes_read / nConn);
+
+    /*
+     * Now loop and time connections using the same session id over and over
+     */
+
+ next:
+    if (!(perform & 2))
+        goto end;
+    printf("\n\nNow timing with session id reuse.\n");
+
+    /* Get an SSL object so we can reuse the session id */
+    if ((scon = doConnection(NULL, host, ctx)) == NULL) {
+        BIO_printf(bio_err, "Unable to get connection\n");
+        goto end;
+    }
+
+    if (www_path != NULL) {
+        buf_len = BIO_snprintf(buf, sizeof buf,
+                               fmt_http_get_cmd, www_path);
+        if (SSL_write(scon, buf, buf_len) <= 0)
+            goto end;
+        while (SSL_read(scon, buf, sizeof(buf)) > 0)
+            continue;
+    }
+#ifdef NO_SHUTDOWN
+    SSL_set_shutdown(scon, SSL_SENT_SHUTDOWN | SSL_RECEIVED_SHUTDOWN);
+#else
+    SSL_shutdown(scon);
+#endif
+    BIO_closesocket(SSL_get_fd(scon));
+
+    nConn = 0;
+    totalTime = 0.0;
+
+    finishtime = (long)time(NULL) + maxtime;
+
+    printf("starting\n");
+    bytes_read = 0;
+    tm_Time_F(START);
+
+    for (;;) {
+        if (finishtime < (long)time(NULL))
+            break;
+
+        if ((doConnection(scon, host, ctx)) == NULL)
+            goto end;
+
+        if (www_path) {
+            BIO_snprintf(buf, sizeof buf, "GET %s HTTP/1.0\r\n\r\n",
+                         www_path);
+            if (SSL_write(scon, buf, strlen(buf)) <= 0)
+                goto end;
+            while ((i = SSL_read(scon, buf, sizeof(buf))) > 0)
+                bytes_read += i;
+        }
+#ifdef NO_SHUTDOWN
+        SSL_set_shutdown(scon, SSL_SENT_SHUTDOWN | SSL_RECEIVED_SHUTDOWN);
+#else
+        SSL_shutdown(scon);
+#endif
+        BIO_closesocket(SSL_get_fd(scon));
+
+        nConn += 1;
+        if (SSL_session_reused(scon))
+            ver = 'r';
+        else {
+            ver = SSL_version(scon);
+            if (ver == TLS1_VERSION)
+                ver = 't';
+            else if (ver == SSL3_VERSION)
+                ver = '3';
+            else
+                ver = '*';
+        }
+        fputc(ver, stdout);
+        fflush(stdout);
+    }
+    totalTime += tm_Time_F(STOP); /* Add the time for this iteration */
+
+    printf
+        ("\n\n%d connections in %.2fs; %.2f connections/user sec, bytes read %ld\n",
+         nConn, totalTime, ((double)nConn / totalTime), bytes_read);
+    printf
+        ("%d connections in %ld real seconds, %ld bytes read per connection\n",
+         nConn, (long)time(NULL) - finishtime + maxtime, bytes_read / nConn);
+
+    ret = 0;
+
+ end:
+    SSL_free(scon);
+    SSL_CTX_free(ctx);
+    return (ret);
+}
+
+/*-
+ * doConnection - make a connection
+ */
+static SSL *doConnection(SSL *scon, const char *host, SSL_CTX *ctx)
+{
+    BIO *conn;
+    SSL *serverCon;
+    int width, i;
+    fd_set readfds;
+
+    if ((conn = BIO_new(BIO_s_connect())) == NULL)
+        return (NULL);
+
+    BIO_set_conn_hostname(conn, host);
+
+    if (scon == NULL)
+        serverCon = SSL_new(ctx);
+    else {
+        serverCon = scon;
+        SSL_set_connect_state(serverCon);
+    }
+
+    SSL_set_bio(serverCon, conn, conn);
+
+    /* ok, lets connect */
+    for (;;) {
+        i = SSL_connect(serverCon);
+        if (BIO_sock_should_retry(i)) {
+            BIO_printf(bio_err, "DELAY\n");
+
+            i = SSL_get_fd(serverCon);
+            width = i + 1;
+            FD_ZERO(&readfds);
+            openssl_fdset(i, &readfds);
+            /*
+             * Note: under VMS with SOCKETSHR the 2nd parameter is currently
+             * of type (int *) whereas under other systems it is (void *) if
+             * you don't have a cast it will choke the compiler: if you do
+             * have a cast then you can either go for (int *) or (void *).
+             */
+            select(width, (void *)&readfds, NULL, NULL, NULL);
+            continue;
+        }
+        break;
+    }
+    if (i <= 0) {
+        BIO_printf(bio_err, "ERROR\n");
+        if (verify_args.error != X509_V_OK)
+            BIO_printf(bio_err, "verify error:%s\n",
+                       X509_verify_cert_error_string(verify_args.error));
+        else
+            ERR_print_errors(bio_err);
+        if (scon == NULL)
+            SSL_free(serverCon);
+        return NULL;
+    }
+
+    return serverCon;
+}
+#endif /* OPENSSL_NO_SOCK */
diff --git a/apps/sdf.c b/apps/sdf.c
new file mode 100644
index 000000000..405dbdea1
--- /dev/null
+++ b/apps/sdf.c
@@ -0,0 +1,365 @@
+/* ====================================================================
+ * Copyright (c) 2014 - 2018 The GmSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the GmSSL Project.
+ *    (http://gmssl.org/)"
+ *
+ * 4. The name "GmSSL Project" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    guanzhi1980@gmail.com.
+ *
+ * 5. Products derived from this software may not be called "GmSSL"
+ *    nor may "GmSSL" appear in their names without prior written
+ *    permission of the GmSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the GmSSL Project
+ *    (http://gmssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE GmSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE GmSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+
+#include 
+#ifdef OPENSSL_NO_SDF
+NON_EMPTY_TRANSLATION_UNIT
+#else
+
+# include 
+# include 
+# include 
+# include 
+# include 
+# include 
+# include 
+# include 
+# include 
+# include "apps.h"
+
+typedef enum OPTION_choice {
+	OPT_ERR = -1, OPT_EOF = 0, OPT_HELP,
+	OPT_LIB, OPT_VENDOR, OPT_PRINTDEVINFO,
+	OPT_PRINTSM2SIGN, OPT_PRINTSM2ENC,
+	OPT_PRINTRSASIGN, OPT_PRINTRSAENC,
+	OPT_ACCESSKEY, OPT_PASS,
+	OPT_IMPORTOBJ, OPT_EXPORTOBJ, OPT_DELOBJ,
+	OPT_IN, OPT_OUT
+} OPTION_CHOICE;
+
+
+# define OP_NONE		0
+# define OP_PRINTDEVINFO	1
+# define OP_PRINTSM2SIGN	2
+# define OP_PRINTSM2ENC		3
+# define OP_PRINTRSASIGN	4
+# define OP_PRINTRSAENC		5
+# define OP_ACCESSKEY		6
+# define OP_IMPORTOBJ		7
+# define OP_EXPORTOBJ 		8
+# define OP_DELOBJ 		9
+
+OPTIONS sdf_options[] = {
+	{"help", OPT_HELP, '-', "Display this summary"},
+	{"lib", OPT_LIB, 's', "Vendor's SDF dynamic library"},
+	{"vendor", OPT_VENDOR, 's', "Vendor name"},
+	{"printdevinfo", OPT_PRINTDEVINFO, '-', "Print device information"},
+	{"printsm2sign", OPT_PRINTSM2SIGN, 's', "Print SM2 signing key with key index"},
+	{"printsm2enc", OPT_PRINTSM2ENC, 's', "Print SM2 encryption key with key index"},
+	{"printrsasign", OPT_PRINTRSASIGN, 's', "Print RSA signing key with key index"},
+	{"printrsaenc", OPT_PRINTRSAENC, 's', "Print RSA encryption key with key index"},
+	{"accesskey", OPT_ACCESSKEY, 's', "Access private key with the key index number"},
+	{"pass", OPT_PASS, 's', "Passphrase source for accessing private key"},
+	{"importobj", OPT_IMPORTOBJ, 's', "Import data object into device"},
+	{"exportobj", OPT_EXPORTOBJ, 's', "Export data object from device"},
+	{"delobj", OPT_DELOBJ, 's', "Delete data object from device"},
+	{"in", OPT_IN, '<', "File to be imported from"},
+	{"out", OPT_OUT, '>', "File to be exported to"},
+	{NULL}
+};
+
+int sdf_main(int argc, char **argv)
+{
+	int ret = 1;
+	char *infile = NULL, *outfile = NULL, *prog;
+	char *objname = NULL, *passarg = NULL, *pass = NULL;
+	BIO *in = NULL, *out = NULL;
+	char *lib = NULL, *vendor = NULL;
+	unsigned char *buf = NULL;
+	unsigned int ulen;
+	int len, key_idx = -1;
+	OPTION_CHOICE o;
+	int op = OP_NONE;
+	void *hDev = NULL;
+	void *hSession = NULL;
+
+	prog = opt_init(argc, argv, sdf_options);
+	while ((o = opt_next()) != OPT_EOF) {
+		switch (o) {
+		case OPT_EOF:
+		case OPT_ERR:
+opthelp:
+			BIO_printf(bio_err, "%s: Use -help for summary.\n", prog);
+			goto end;
+		case OPT_HELP:
+			opt_help(sdf_options);
+			ret = 0;
+			goto end;
+		case OPT_LIB:
+			lib = opt_arg();
+			break;
+		case OPT_VENDOR:
+			vendor = opt_arg();
+			break;
+		case OPT_PRINTDEVINFO:
+			if (op)
+				goto opthelp;
+			op = OP_PRINTDEVINFO;
+			break;
+		case OPT_PRINTSM2SIGN:
+			if (op)
+				goto opthelp;
+			op = OP_PRINTSM2SIGN;
+			key_idx = atoi(opt_arg());
+			break;
+		case OPT_PRINTSM2ENC:
+			if (op)
+				goto opthelp;
+			op = OP_PRINTSM2ENC;
+			key_idx = atoi(opt_arg());
+			break;
+		case OPT_PRINTRSASIGN:
+			if (op)
+				goto opthelp;
+			op = OP_PRINTRSASIGN;
+			key_idx = atoi(opt_arg());
+			break;
+		case OPT_PRINTRSAENC:
+			if (op)
+				goto opthelp;
+			op = OP_PRINTRSAENC;
+			key_idx = atoi(opt_arg());
+			break;
+		case OPT_ACCESSKEY:
+			key_idx = atoi(opt_arg());
+			break;
+		case OPT_PASS:
+			passarg = opt_arg();
+			break;
+		case OPT_IMPORTOBJ:
+			if (op)
+				goto opthelp;
+			op = OP_IMPORTOBJ;
+			objname = opt_arg();
+			break;
+		case OPT_EXPORTOBJ:
+			if (op)
+				goto opthelp;
+			op = OP_EXPORTOBJ;
+			objname = opt_arg();
+			break;
+		case OPT_DELOBJ:
+			if (op)
+				goto opthelp;
+			op = OP_DELOBJ;
+			objname = opt_arg();
+			break;
+		case OPT_IN:
+			infile = opt_arg();
+			break;
+		case OPT_OUT:
+			outfile = opt_arg();
+			break;
+		}
+	}
+	argc = opt_num_rest();
+	if (argc != 0)
+		goto opthelp;
+
+	if (!lib) {
+		BIO_printf(bio_err, "Option '-lib' required\n");
+		goto opthelp;
+	}
+	if (SDF_LoadLibrary(lib, vendor) != SDR_OK) {
+		ERR_print_errors(bio_err);
+		goto end;
+	}
+
+	if (op == OP_NONE) {
+		ret = 0;
+		goto end;
+	}
+
+	if (SDF_OpenDevice(&hDev) != SDR_OK
+		|| SDF_OpenSession(hDev, &hSession) != SDR_OK) {
+		ERR_print_errors(bio_err);
+		goto end;
+	}
+
+	switch (op) {
+	case OP_PRINTDEVINFO:
+	case OP_PRINTSM2SIGN:
+	case OP_PRINTSM2ENC:
+	case OP_PRINTRSASIGN:
+	case OP_PRINTRSAENC:
+		if (!(out = bio_open_default(outfile, 'w', FORMAT_TEXT))) {
+			goto opthelp;
+		}
+		break;
+	}
+
+	switch (op) {
+	case OP_PRINTSM2SIGN:
+	case OP_PRINTSM2ENC:
+	case OP_PRINTRSASIGN:
+	case OP_PRINTRSAENC:
+	case OP_ACCESSKEY:
+		if (key_idx < SDF_MIN_KEY_INDEX || key_idx > SDF_MAX_KEY_INDEX) {
+			BIO_printf(bio_err, "Invalid key index\n");
+			goto end;
+		}
+		break;
+	}
+
+	if (op == OP_PRINTDEVINFO) {
+		DEVICEINFO devInfo;
+		if (SDF_GetDeviceInfo(hSession, &devInfo) != SDR_OK
+			|| SDF_PrintDeviceInfo(out, &devInfo) != SDR_OK) {
+			ERR_print_errors(bio_err);
+			goto end;
+		}
+
+	} else if (op == OP_PRINTSM2SIGN || op == OP_PRINTSM2ENC) {
+		ECCrefPublicKey publicKey;
+		if (op == OP_PRINTSM2SIGN) {
+			if (SDF_ExportSignPublicKey_ECC(hSession,
+				key_idx, &publicKey) != SDR_OK) {
+				ERR_print_errors(bio_err);
+				goto end;
+			}
+			BIO_puts(out, "SM2 Signing Public Key:\n");
+		} else {
+			if (SDF_ExportEncPublicKey_ECC(hSession,
+				key_idx, &publicKey) != SDR_OK) {
+				ERR_print_errors(bio_err);
+				goto end;
+			}
+			BIO_puts(out, "SM2 Encryption Public Key:\n");
+		}
+		if (SDF_PrintECCPublicKey(out, &publicKey) != SDR_OK) {
+			ERR_print_errors(bio_err);
+			goto end;
+		}
+
+	} else if (op == OP_PRINTRSASIGN || op == OP_PRINTRSAENC) {
+		RSArefPublicKey publicKey;
+		if (op == OP_PRINTRSASIGN) {
+			if (SDF_ExportSignPublicKey_RSA(hSession,
+				key_idx, &publicKey) != SDR_OK) {
+				ERR_print_errors(bio_err);
+				goto end;
+			}
+			BIO_puts(out, "RSA Signing Public Key:\n");
+		} else {
+			if (SDF_ExportEncPublicKey_RSA(hSession,
+				key_idx, &publicKey) != SDR_OK) {
+				ERR_print_errors(bio_err);
+				goto end;
+			}
+			BIO_puts(out, "RSA Encryption Public Key:\n");
+		}
+		if (SDF_PrintRSAPublicKey(out, &publicKey) != SDR_OK) {
+			ERR_print_errors(bio_err);
+			goto end;
+		}
+
+	} else if (op == OP_ACCESSKEY) {
+		if (!app_passwd(passarg, NULL, &pass, NULL)) {
+			BIO_printf(bio_err, "Error getting password\n");
+			goto end;
+		}
+		if (SDF_GetPrivateKeyAccessRight(hSession, (unsigned int)key_idx,
+			(unsigned char *)pass, strlen(pass)) != SDR_OK) {
+			OPENSSL_cleanse(pass, sizeof(pass));
+			return 0;
+		}
+		(void)SDF_ReleasePrivateKeyAccessRight(hSession, (unsigned int)key_idx);
+		BIO_printf(bio_err, "Access private key %d success\n", key_idx);
+
+	} else if (op == OP_IMPORTOBJ) {
+		if (!(in = bio_open_default(infile, 'r', FORMAT_BINARY))) {
+			goto opthelp;
+		}
+		if ((len = bio_to_mem(&buf, SDF_MAX_FILE_SIZE, in)) < 0) {
+			BIO_printf(bio_err, "Error reading data object content\n");
+			goto end;
+		}
+		if (SDF_CreateFile(hSession, (unsigned char *)objname, strlen(objname), len) != SDR_OK
+			|| SDF_WriteFile(hSession, (unsigned char *)objname, strlen(objname), 0, len, buf) != SDR_OK) {
+			ERR_print_errors(bio_err);
+			goto end;
+		}
+		BIO_printf(bio_err, "Object '%s' (%d bytes) created\n", objname, len);
+
+	} else if (op == OP_EXPORTOBJ) {
+		if (!(out = bio_open_default(outfile, 'w', FORMAT_BINARY))) {
+			goto opthelp;
+		}
+		if (!(buf = OPENSSL_zalloc(SDF_MAX_FILE_SIZE))
+			|| SDF_ReadFile(hSession, (unsigned char *)objname, strlen(objname), 0, &ulen, buf) != SDR_OK
+			|| BIO_write(out, buf, ulen) != ulen) {
+			ERR_print_errors(bio_err);
+			goto end;
+		}
+		BIO_printf(bio_err, "Object '%s' (%u bytes) exported\n", objname, ulen);
+
+	} else if (op == OP_DELOBJ) {
+		if (SDF_DeleteFile(hSession, (unsigned char *)objname, strlen(objname)) != SDR_OK) {
+			ERR_print_errors(bio_err);
+			goto end;
+		}
+		BIO_printf(bio_err, "Object '%s' deleted\n", objname);
+
+	} else {
+		goto end;
+	}
+
+	ret = 0;
+
+end:
+	BIO_free(in);
+	BIO_free(out);
+	OPENSSL_free(buf);
+	OPENSSL_free(pass);
+	if (hSession) (void)SDF_CloseSession(hSession);
+	if (hDev) (void)SDF_CloseDevice(hDev);
+	if (lib) SDF_UnloadLibrary();
+	return ret;
+}
+#endif
diff --git a/apps/server.pem b/apps/server.pem
new file mode 100644
index 000000000..d0fc265f0
--- /dev/null
+++ b/apps/server.pem
@@ -0,0 +1,52 @@
+subject= C = UK, O = OpenSSL Group, OU = FOR TESTING PURPOSES ONLY, CN = Test Server Cert
+issuer= C = UK, O = OpenSSL Group, OU = FOR TESTING PURPOSES ONLY, CN = OpenSSL Test Intermediate CA
+-----BEGIN CERTIFICATE-----
+MIID5zCCAs+gAwIBAgIJALnu1NlVpZ6zMA0GCSqGSIb3DQEBBQUAMHAxCzAJBgNV
+BAYTAlVLMRYwFAYDVQQKDA1PcGVuU1NMIEdyb3VwMSIwIAYDVQQLDBlGT1IgVEVT
+VElORyBQVVJQT1NFUyBPTkxZMSUwIwYDVQQDDBxPcGVuU1NMIFRlc3QgSW50ZXJt
+ZWRpYXRlIENBMB4XDTExMTIwODE0MDE0OFoXDTIxMTAxNjE0MDE0OFowZDELMAkG
+A1UEBhMCVUsxFjAUBgNVBAoMDU9wZW5TU0wgR3JvdXAxIjAgBgNVBAsMGUZPUiBU
+RVNUSU5HIFBVUlBPU0VTIE9OTFkxGTAXBgNVBAMMEFRlc3QgU2VydmVyIENlcnQw
+ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDzhPOSNtyyRspmeuUpxfNJ
+KCLTuf7g3uQ4zu4iHOmRO5TQci+HhVlLZrHF9XqFXcIP0y4pWDbMSGuiorUmzmfi
+R7bfSdI/+qIQt8KXRH6HNG1t8ou0VSvWId5TS5Dq/er5ODUr9OaaDva7EquHIcMv
+vPQGuI+OEAcnleVCy9HVEIySrO4P3CNIicnGkwwiAud05yUAq/gPXBC1hTtmlPD7
+TVcGVSEiJdvzqqlgv02qedGrkki6GY4S7GjZxrrf7Foc2EP+51LJzwLQx3/JfrCU
+41NEWAsu/Sl0tQabXESN+zJ1pDqoZ3uHMgpQjeGiE0olr+YcsSW/tJmiU9OiAr8R
+AgMBAAGjgY8wgYwwDAYDVR0TAQH/BAIwADAOBgNVHQ8BAf8EBAMCBeAwLAYJYIZI
+AYb4QgENBB8WHU9wZW5TU0wgR2VuZXJhdGVkIENlcnRpZmljYXRlMB0GA1UdDgQW
+BBSCvM8AABPR9zklmifnr9LvIBturDAfBgNVHSMEGDAWgBQ2w2yI55X+sL3szj49
+hqshgYfa2jANBgkqhkiG9w0BAQUFAAOCAQEAqb1NV0B0/pbpK9Z4/bNjzPQLTRLK
+WnSNm/Jh5v0GEUOE/Beg7GNjNrmeNmqxAlpqWz9qoeoFZax+QBpIZYjROU3TS3fp
+yLsrnlr0CDQ5R7kCCDGa8dkXxemmpZZLbUCpW2Uoy8sAA4JjN9OtsZY7dvUXFgJ7
+vVNTRnI01ghknbtD+2SxSQd3CWF6QhcRMAzZJ1z1cbbwGDDzfvGFPzJ+Sq+zEPds
+xoVLLSetCiBc+40ZcDS5dV98h9XD7JMTQfxzA7mNGv73JoZJA6nFgj+ADSlJsY/t
+JBv+z1iQRueoh9Qeee+ZbRifPouCB8FDx+AltvHTANdAq0t/K3o+pplMVA==
+-----END CERTIFICATE-----
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEA84TzkjbcskbKZnrlKcXzSSgi07n+4N7kOM7uIhzpkTuU0HIv
+h4VZS2axxfV6hV3CD9MuKVg2zEhroqK1Js5n4ke230nSP/qiELfCl0R+hzRtbfKL
+tFUr1iHeU0uQ6v3q+Tg1K/Tmmg72uxKrhyHDL7z0BriPjhAHJ5XlQsvR1RCMkqzu
+D9wjSInJxpMMIgLndOclAKv4D1wQtYU7ZpTw+01XBlUhIiXb86qpYL9NqnnRq5JI
+uhmOEuxo2ca63+xaHNhD/udSyc8C0Md/yX6wlONTRFgLLv0pdLUGm1xEjfsydaQ6
+qGd7hzIKUI3hohNKJa/mHLElv7SZolPTogK/EQIDAQABAoIBAADq9FwNtuE5IRQn
+zGtO4q7Y5uCzZ8GDNYr9RKp+P2cbuWDbvVAecYq2NV9QoIiWJOAYZKklOvekIju3
+r0UZLA0PRiIrTg6NrESx3JrjWDK8QNlUO7CPTZ39/K+FrmMkV9lem9yxjJjyC34D
+AQB+YRTx+l14HppjdxNwHjAVQpIx/uO2F5xAMuk32+3K+pq9CZUtrofe1q4Agj9R
+5s8mSy9pbRo9kW9wl5xdEotz1LivFOEiqPUJTUq5J5PeMKao3vdK726XI4Z455Nm
+W2/MA0YV0ug2FYinHcZdvKM6dimH8GLfa3X8xKRfzjGjTiMSwsdjgMa4awY3tEHH
+674jhAECgYEA/zqMrc0zsbNk83sjgaYIug5kzEpN4ic020rSZsmQxSCerJTgNhmg
+utKSCt0Re09Jt3LqG48msahX8ycqDsHNvlEGPQSbMu9IYeO3Wr3fAm75GEtFWePY
+BhM73I7gkRt4s8bUiUepMG/wY45c5tRF23xi8foReHFFe9MDzh8fJFECgYEA9EFX
+4qAik1pOJGNei9BMwmx0I0gfVEIgu0tzeVqT45vcxbxr7RkTEaDoAG6PlbWP6D9a
+WQNLp4gsgRM90ZXOJ4up5DsAWDluvaF4/omabMA+MJJ5kGZ0gCj5rbZbKqUws7x8
+bp+6iBfUPJUbcqNqFmi/08Yt7vrDnMnyMw2A/sECgYEAiiuRMxnuzVm34hQcsbhH
+6ymVqf7j0PW2qK0F4H1ocT9qhzWFd+RB3kHWrCjnqODQoI6GbGr/4JepHUpre1ex
+4UEN5oSS3G0ru0rC3U4C59dZ5KwDHFm7ffZ1pr52ljfQDUsrjjIMRtuiwNK2OoRa
+WSsqiaL+SDzSB+nBmpnAizECgYBdt/y6rerWUx4MhDwwtTnel7JwHyo2MDFS6/5g
+n8qC2Lj6/fMDRE22w+CA2esp7EJNQJGv+b27iFpbJEDh+/Lf5YzIT4MwVskQ5bYB
+JFcmRxUVmf4e09D7o705U/DjCgMH09iCsbLmqQ38ONIRSHZaJtMDtNTHD1yi+jF+
+OT43gQKBgQC/2OHZoko6iRlNOAQ/tMVFNq7fL81GivoQ9F1U0Qr+DH3ZfaH8eIkX
+xT0ToMPJUzWAn8pZv0snA0um6SIgvkCuxO84OkANCVbttzXImIsL7pFzfcwV/ERK
+UM6j0ZuSMFOCr/lGPAoOQU0fskidGEHi1/kW+suSr28TqsyYZpwBDQ==
+-----END RSA PRIVATE KEY-----
diff --git a/apps/server.srl b/apps/server.srl
new file mode 100644
index 000000000..8a0f05e16
--- /dev/null
+++ b/apps/server.srl
@@ -0,0 +1 @@
+01
diff --git a/apps/server2.pem b/apps/server2.pem
new file mode 100644
index 000000000..a3927cf78
--- /dev/null
+++ b/apps/server2.pem
@@ -0,0 +1,52 @@
+subject= C = UK, O = OpenSSL Group, OU = FOR TESTING PURPOSES ONLY, CN = Test Server Cert #2
+issuer= C = UK, O = OpenSSL Group, OU = FOR TESTING PURPOSES ONLY, CN = OpenSSL Test Intermediate CA
+-----BEGIN CERTIFICATE-----
+MIID6jCCAtKgAwIBAgIJALnu1NlVpZ60MA0GCSqGSIb3DQEBBQUAMHAxCzAJBgNV
+BAYTAlVLMRYwFAYDVQQKDA1PcGVuU1NMIEdyb3VwMSIwIAYDVQQLDBlGT1IgVEVT
+VElORyBQVVJQT1NFUyBPTkxZMSUwIwYDVQQDDBxPcGVuU1NMIFRlc3QgSW50ZXJt
+ZWRpYXRlIENBMB4XDTExMTIwODE0MDE0OFoXDTIxMTAxNjE0MDE0OFowZzELMAkG
+A1UEBhMCVUsxFjAUBgNVBAoMDU9wZW5TU0wgR3JvdXAxIjAgBgNVBAsMGUZPUiBU
+RVNUSU5HIFBVUlBPU0VTIE9OTFkxHDAaBgNVBAMME1Rlc3QgU2VydmVyIENlcnQg
+IzIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDrdi7j9yctG+L4EjBy
+gjPmEqZzOJEQba26MoQGzglU7e5Xf59Rb/hgVQuKAoiZe7/R8rK4zJ4W7iXdXw0L
+qBpyG8B5aGKeI32w+A9TcBApoXXL2CrYQEQjZwUIpLlYBIi2NkJj3nVkq5dgl1gO
+ALiQ+W8jg3kzg5Ec9rimp9r93N8wsSL3awsafurmYCvOf7leHaMP1WJ/zDRGUNHG
+/WtDjXc8ZUG1+6EXU9Jc2Fs+2Omf7fcN0l00AK/wPg8OaNS0rKyGq9JdIT9FRGV1
+bXe/rx58FaE5CItdwCSYhJvF/O95LWQoxJXye5bCFLmvDTEyVq9FMSCptfsmbXjE
+ZGsXAgMBAAGjgY8wgYwwDAYDVR0TAQH/BAIwADAOBgNVHQ8BAf8EBAMCBeAwLAYJ
+YIZIAYb4QgENBB8WHU9wZW5TU0wgR2VuZXJhdGVkIENlcnRpZmljYXRlMB0GA1Ud
+DgQWBBR52UaWWTKzZGDH/X4mWNcuqeQVazAfBgNVHSMEGDAWgBQ2w2yI55X+sL3s
+zj49hqshgYfa2jANBgkqhkiG9w0BAQUFAAOCAQEANBW+XYLlHBqVY/31ie+3gRlS
+LPfy4SIqn0t3RJjagT29MXprblBO2cbMO8VGjkQdKGpmMXjxbht2arOOUXRHX4n/
+XTyn/QHEf0bcwIITMReO3DZUPAEw8hSjn9xEOM0IRVOCP+mH5fi74QzzQaZVCyYg
+5VtLKdww/+sc0nCbKl2KWgDluriH0nfVx95qgW3mg9dhXRr0zmf1w2zkBHYpARYL
+Dew6Z8EE4tS3HJu8/qM6meWzNtrfonQ3eiiMxjZBxzV46jchBwa2z9XYhP6AmpPb
+oeTSzcQNbWsxaGYzWo46oLDUZmJOwSBawbS31bZNMCoPIY6ukoesCzFSsUKZww==
+-----END CERTIFICATE-----
+-----BEGIN RSA PRIVATE KEY-----
+MIIEowIBAAKCAQEA63Yu4/cnLRvi+BIwcoIz5hKmcziREG2tujKEBs4JVO3uV3+f
+UW/4YFULigKImXu/0fKyuMyeFu4l3V8NC6gachvAeWhiniN9sPgPU3AQKaF1y9gq
+2EBEI2cFCKS5WASItjZCY951ZKuXYJdYDgC4kPlvI4N5M4ORHPa4pqfa/dzfMLEi
+92sLGn7q5mArzn+5Xh2jD9Vif8w0RlDRxv1rQ413PGVBtfuhF1PSXNhbPtjpn+33
+DdJdNACv8D4PDmjUtKyshqvSXSE/RURldW13v68efBWhOQiLXcAkmISbxfzveS1k
+KMSV8nuWwhS5rw0xMlavRTEgqbX7Jm14xGRrFwIDAQABAoIBAHLsTPihIfLnYIE5
+x4GsQQ5zXeBw5ITDM37ktwHnQDC+rIzyUl1aLD1AZRBoKinXd4lOTqLZ4/NHKx4A
+DYr58mZtWyUmqLOMmQVuHXTZBlp7XtYuXMMNovQwjQlp9LicBeoBU6gQ5PVMtubD
+F4xGF89Sn0cTHW3iMkqTtQ5KcR1j57OcJO0FEb1vPvk2MXI5ZyAatUYE7YacbEzd
+rg02uIwx3FqNSkuSI79uz4hMdV5TPtuhxx9nTwj9aLUhXFeZ0mn2PVgVzEnnMoJb
++znlsZDgzDlJqdaD744YGWh8Z3OEssB35KfzFcdOeO6yH8lmv2Zfznk7pNPT7LTb
+Lae9VgkCgYEA92p1qnAB3NtJtNcaW53i0S5WJgS1hxWKvUDx3lTB9s8X9fHpqL1a
+E94fDfWzp/hax6FefUKIvBOukPLQ6bYjTMiFoOHzVirghAIuIUoMI5VtLhwD1hKs
+Lr7l/dptMgKb1nZHyXoKHRBthsy3K4+udsPi8TzMvYElgEqyQIe/Rk0CgYEA86GL
+8HC6zLszzKERDPBxrboRmoFvVUCTQDhsfj1M8aR3nQ8V5LkdIJc7Wqm/Ggfk9QRf
+rJ8M2WUMlU5CNnCn/KCrKzCNZIReze3fV+HnKdbcXGLvgbHPrhnz8yYehUFG+RGq
+bVyDWRU94T38izy2s5qMYrMJWZEYyXncSPbfcPMCgYAtaXfxcZ+V5xYPQFARMtiX
+5nZfggvDoJuXgx0h3tK/N2HBfcaSdzbaYLG4gTmZggc/jwnl2dl5E++9oSPhUdIG
+3ONSFUbxsOsGr9PBvnKd8WZZyUCXAVRjPBzAzF+whzQNWCZy/5htnz9LN7YDI9s0
+5113Q96cheDZPFydZY0hHQKBgQDVbEhNukM5xCiNcu+f2SaMnLp9EjQ4h5g3IvaP
+5B16daw/Dw8LzcohWboqIxeAsze0GD/D1ZUJAEd0qBjC3g+a9BjefervCjKOzXng
+38mEUm+6EwVjJSQcjSmycEs+Sr/kwr/8i5WYvU32+jk4tFgMoC+o6tQe/Uesf68k
+z/dPVwKBgGbF7Vv1/3SmhlOy+zYyvJ0CrWtKxH9QP6tLIEgEpd8x7YTSuCH94yok
+kToMXYA3sWNPt22GbRDZ+rcp4c7HkDx6I6vpdP9aQEwJTp0EPy0sgWr2XwYmreIQ
+NFmkk8Itn9EY2R9VBaP7GLv5kvwxDdLAnmwGmzVtbmaVdxCaBwUk
+-----END RSA PRIVATE KEY-----
diff --git a/apps/sess_id.c b/apps/sess_id.c
new file mode 100644
index 000000000..2b63e69cd
--- /dev/null
+++ b/apps/sess_id.c
@@ -0,0 +1,190 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include 
+#include "apps.h"
+#include 
+#include 
+#include 
+#include 
+#include 
+
+typedef enum OPTION_choice {
+    OPT_ERR = -1, OPT_EOF = 0, OPT_HELP,
+    OPT_INFORM, OPT_OUTFORM, OPT_IN, OPT_OUT,
+    OPT_TEXT, OPT_CERT, OPT_NOOUT, OPT_CONTEXT
+} OPTION_CHOICE;
+
+OPTIONS sess_id_options[] = {
+    {"help", OPT_HELP, '-', "Display this summary"},
+    {"inform", OPT_INFORM, 'F', "Input format - default PEM (DER or PEM)"},
+    {"outform", OPT_OUTFORM, 'f',
+     "Output format - default PEM (PEM, DER or NSS)"},
+    {"in", OPT_IN, 's', "Input file - default stdin"},
+    {"out", OPT_OUT, 's', "Output file - default stdout"},
+    {"text", OPT_TEXT, '-', "Print ssl session id details"},
+    {"cert", OPT_CERT, '-', "Output certificate "},
+    {"noout", OPT_NOOUT, '-', "Don't output the encoded session info"},
+    {"context", OPT_CONTEXT, 's', "Set the session ID context"},
+    {NULL}
+};
+
+static SSL_SESSION *load_sess_id(char *file, int format);
+
+int sess_id_main(int argc, char **argv)
+{
+    SSL_SESSION *x = NULL;
+    X509 *peer = NULL;
+    BIO *out = NULL;
+    char *infile = NULL, *outfile = NULL, *context = NULL, *prog;
+    int informat = FORMAT_PEM, outformat = FORMAT_PEM;
+    int cert = 0, noout = 0, text = 0, ret = 1, i, num = 0;
+    OPTION_CHOICE o;
+
+    prog = opt_init(argc, argv, sess_id_options);
+    while ((o = opt_next()) != OPT_EOF) {
+        switch (o) {
+        case OPT_EOF:
+        case OPT_ERR:
+ opthelp:
+            BIO_printf(bio_err, "%s: Use -help for summary.\n", prog);
+            goto end;
+        case OPT_HELP:
+            opt_help(sess_id_options);
+            ret = 0;
+            goto end;
+        case OPT_INFORM:
+            if (!opt_format(opt_arg(), OPT_FMT_PEMDER, &informat))
+                goto opthelp;
+            break;
+        case OPT_OUTFORM:
+            if (!opt_format(opt_arg(), OPT_FMT_PEMDER | OPT_FMT_NSS,
+                            &outformat))
+                goto opthelp;
+            break;
+        case OPT_IN:
+            infile = opt_arg();
+            break;
+        case OPT_OUT:
+            outfile = opt_arg();
+            break;
+        case OPT_TEXT:
+            text = ++num;
+            break;
+        case OPT_CERT:
+            cert = ++num;
+            break;
+        case OPT_NOOUT:
+            noout = ++num;
+            break;
+        case OPT_CONTEXT:
+            context = opt_arg();
+            break;
+        }
+    }
+    argc = opt_num_rest();
+    if (argc != 0)
+        goto opthelp;
+
+    x = load_sess_id(infile, informat);
+    if (x == NULL) {
+        goto end;
+    }
+    peer = SSL_SESSION_get0_peer(x);
+
+    if (context) {
+        size_t ctx_len = strlen(context);
+        if (ctx_len > SSL_MAX_SID_CTX_LENGTH) {
+            BIO_printf(bio_err, "Context too long\n");
+            goto end;
+        }
+        if (!SSL_SESSION_set1_id_context(x, (unsigned char *)context,
+                    ctx_len)) {
+            BIO_printf(bio_err, "Error setting id context\n");
+            goto end;
+        }
+    }
+
+    if (!noout || text) {
+        out = bio_open_default(outfile, 'w', outformat);
+        if (out == NULL)
+            goto end;
+    }
+
+    if (text) {
+        SSL_SESSION_print(out, x);
+
+        if (cert) {
+            if (peer == NULL)
+                BIO_puts(out, "No certificate present\n");
+            else
+                X509_print(out, peer);
+        }
+    }
+
+    if (!noout && !cert) {
+        if (outformat == FORMAT_ASN1)
+            i = i2d_SSL_SESSION_bio(out, x);
+        else if (outformat == FORMAT_PEM)
+            i = PEM_write_bio_SSL_SESSION(out, x);
+        else if (outformat == FORMAT_NSS)
+            i = SSL_SESSION_print_keylog(out, x);
+        else {
+            BIO_printf(bio_err, "bad output format specified for outfile\n");
+            goto end;
+        }
+        if (!i) {
+            BIO_printf(bio_err, "unable to write SSL_SESSION\n");
+            goto end;
+        }
+    } else if (!noout && (peer != NULL)) { /* just print the certificate */
+        if (outformat == FORMAT_ASN1)
+            i = (int)i2d_X509_bio(out, peer);
+        else if (outformat == FORMAT_PEM)
+            i = PEM_write_bio_X509(out, peer);
+        else {
+            BIO_printf(bio_err, "bad output format specified for outfile\n");
+            goto end;
+        }
+        if (!i) {
+            BIO_printf(bio_err, "unable to write X509\n");
+            goto end;
+        }
+    }
+    ret = 0;
+ end:
+    BIO_free_all(out);
+    SSL_SESSION_free(x);
+    return (ret);
+}
+
+static SSL_SESSION *load_sess_id(char *infile, int format)
+{
+    SSL_SESSION *x = NULL;
+    BIO *in = NULL;
+
+    in = bio_open_default(infile, 'r', format);
+    if (in == NULL)
+        goto end;
+    if (format == FORMAT_ASN1)
+        x = d2i_SSL_SESSION_bio(in, NULL);
+    else
+        x = PEM_read_bio_SSL_SESSION(in, NULL, NULL, NULL);
+    if (x == NULL) {
+        BIO_printf(bio_err, "unable to load SSL_SESSION\n");
+        ERR_print_errors(bio_err);
+        goto end;
+    }
+
+ end:
+    BIO_free(in);
+    return (x);
+}
diff --git a/apps/skf.c b/apps/skf.c
new file mode 100644
index 000000000..2468dc12b
--- /dev/null
+++ b/apps/skf.c
@@ -0,0 +1,1484 @@
+/* ====================================================================
+ * Copyright (c) 2014 - 2019 The GmSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the GmSSL Project.
+ *    (http://gmssl.org/)"
+ *
+ * 4. The name "GmSSL Project" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    guanzhi1980@gmail.com.
+ *
+ * 5. Products derived from this software may not be called "GmSSL"
+ *    nor may "GmSSL" appear in their names without prior written
+ *    permission of the GmSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the GmSSL Project
+ *    (http://gmssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE GmSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE GmSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+
+#include 
+#ifdef OPENSSL_NO_SKF
+NON_EMPTY_TRANSLATION_UNIT
+#else
+
+# include 
+# include 
+# include 
+# include 
+# include 
+# include 
+# include 
+# include 
+# include 
+# include 
+# include "apps.h"
+
+
+# define OP_NONE		0
+# define OP_LISTDEVS		1
+# define OP_DEVINFO		2
+# define OP_NEWAUTHKEY		3
+# define OP_LABEL		4
+# define OP_TRANSMIT		5
+# define OP_LISTAPPS		6
+# define OP_NEWAPP		7
+# define OP_DELAPP		8
+# define OP_CHANGEPASS		9
+# define OP_UNBLOCK		10
+# define OP_LISTOBJS		11
+# define OP_IMPORTOBJ		12
+# define OP_EXPORTOBJ		13
+# define OP_DELOBJ		14
+# define OP_LISTCONTAINERS	15
+# define OP_NEWCONTAINER	16
+# define OP_DELCONTAINER	17
+# define OP_IMPORTENCKEY	18
+# define OP_EXPORTSIGNKEY	19
+# define OP_EXPORTENCKEY	20
+# define OP_PRINTKEYS		21
+# define OP_IMPORTCERT		22
+# define OP_EXPORTSIGNCERT	23
+# define OP_EXPORTENCCERT	24
+
+typedef enum OPTION_choice {
+	OPT_ERR = -1, OPT_EOF = 0, OPT_HELP,
+	OPT_LIB, OPT_VENDOR, OPT_LISTDEVS, OPT_DEV, OPT_DEVINFO, OPT_LABEL,
+	OPT_TRANSMIT, OPT_AUTHKEY, OPT_NEWAUTHKEY, OPT_LISTAPPS, OPT_NEWAPP,
+	OPT_DELAPP, OPT_APP, OPT_CHANGEPASS, OPT_PASS, OPT_ADMIN, OPT_ADMINPASS,
+	OPT_NEWPASS, OPT_UNBLOCK, OPT_LISTOBJS, OPT_IMPORTOBJ, OPT_EXPORTOBJ,
+	OPT_DELOBJ, OPT_OBJ, OPT_LISTCONTAINERS, OPT_NEWCONTAINER, OPT_ALGORITHM,
+	OPT_DELCONTAINER, OPT_CONTAINER, OPT_IMPORTENCKEY, OPT_KEYPASS,
+	OPT_EXPORTSIGNKEY, OPT_EXPORTENCKEY, OPT_PRINTKEYS,
+	OPT_IMPORTCERT, OPT_EXPORTSIGNCERT, OPT_EXPORTENCCERT,
+	OPT_IN, OPT_OUT, OPT_INFORM, OPT_OUTFORM,
+} OPTION_CHOICE;
+
+OPTIONS skf_options[] = {
+	{"help", OPT_HELP, '-', "Display this summary"},
+	{"lib", OPT_LIB, 's', "Vendor's SKF dynamic library"},
+	{"vendor", OPT_VENDOR, 's', "Vendor name"},
+	{"listdevs", OPT_LISTDEVS, '-', "List installed devices"},
+	{"dev", OPT_DEV, 's', "Device name"},
+	{"devinfo", OPT_DEVINFO, '-', "Print device information"},
+	{"label", OPT_LABEL, 's', "Set new device label"},
+	{"transmit", OPT_TRANSMIT, 's', "Transmit raw data packet"},
+	{"authkey", OPT_AUTHKEY, 's', "Device authentication key in Hex"},
+	{"newauthkey", OPT_NEWAUTHKEY, 's', "Set new device authentication key in Hex"},
+	{"listapps", OPT_LISTAPPS, '-', "List applications"},
+	{"newapp", OPT_NEWAPP, 's', "Create a new application with name"},
+	{"delapp", OPT_DELAPP, 's', "Delete an applicaiton by name"},
+	{"app", OPT_APP, 's', "Application name"},
+	{"changepass", OPT_CHANGEPASS, '-', "Change application user or admin passw-phrase"},
+	{"admin", OPT_ADMIN, '-', "Open application as administrator"},
+	{"pass", OPT_PASS, 's', "Application user or admin pass-phrase source"},
+	{"newpass", OPT_NEWPASS, 's', "Application user or admin new ass-phrase source"},
+	{"adminpass", OPT_ADMINPASS, 's', "Application admin pass-phrase source"},
+	{"unblock", OPT_UNBLOCK, '-', "Unblock application user pass-phrase"},
+	{"listobjs", OPT_LISTOBJS, '-', "List data objects"},
+	{"importobj", OPT_IMPORTOBJ, 's', "Import data object with name"},
+	{"exportobj", OPT_EXPORTOBJ, 's', "Export data object by name"},
+	{"delobj", OPT_DELOBJ, 's', "Delete data object by name"},
+	{"obj", OPT_OBJ, 's', "Data object name"},
+	{"listcontainers", OPT_LISTCONTAINERS, '-', "List containers"},
+	{"newcontainer", OPT_NEWCONTAINER, 's', "Create container with name"},
+	{"algorithm", OPT_ALGORITHM, 's', "Container public key algorithm - SM2 or RSA"},
+	{"delcontainer", OPT_DELCONTAINER, 's', "Delete container by name"},
+	{"container", OPT_CONTAINER, 's', "Container name"},
+	{"importenckey", OPT_IMPORTENCKEY, '-', "Import encryption private key into container"},
+	{"keypass", OPT_KEYPASS, 's', "Private key encryption pass-phrase"},
+	{"exportsignkey", OPT_EXPORTSIGNKEY, '-', "Export signing public key from container"},
+	{"exportenckey", OPT_EXPORTENCKEY, '-', "Export encryption public key from container"},
+	{"printkeys", OPT_PRINTKEYS, '-', "Print public keys in container"},
+	{"importcert", OPT_IMPORTCERT, '-', "Import certificate into container"},
+	{"exportsigncert", OPT_EXPORTSIGNCERT, '-', "Export signing certificate from container"},
+	{"exportenccert", OPT_EXPORTENCCERT, '-', "Export encryption certificate from container"},
+	{"in", OPT_IN, '<', "File to be imported from"},
+	{"out", OPT_OUT, '>', "File to be exported to"},
+	{"inform", OPT_INFORM, 'f', "Input format - DER or PEM"},
+	{"outform", OPT_OUTFORM, 'F', "Output format - DER or PEM"},
+	{NULL}
+};
+
+static int skf_listdevs(BIO *out)
+{
+	int ret = 0;
+	BOOL bPresent = TRUE;
+	char *nameList = NULL;
+	ULONG nameListLen;
+	const char *name;
+	int i;
+
+	if (SKF_EnumDev(bPresent, NULL, &nameListLen) != SAR_OK
+		|| !(nameList = OPENSSL_zalloc(nameListLen))
+		|| SKF_EnumDev(bPresent, (LPSTR)nameList, &nameListLen) != SAR_OK) {
+		ERR_print_errors(bio_err);
+		goto end;
+	}
+	for (name = nameList, i = 0; *name; name += strlen(name) + 1, i++) {
+		(void)BIO_printf(out, "  Device %d : %s\n", i, name);
+	}
+
+	ret = 1;
+end:
+	OPENSSL_free(nameList);
+	return ret;
+}
+
+static int skf_devinfo(const char *devname, BIO *out)
+{
+	int ret = 0;
+	DEVHANDLE hDev = NULL;
+	ULONG devState;
+	LPSTR devStateName;
+	DEVINFO devInfo = {{0,0}};
+
+	if (SKF_GetDevState((LPSTR)devname, &devState) != SAR_OK
+		|| SKF_GetDevStateName(devState, &devStateName) != SAR_OK
+		|| SKF_ConnectDev((LPSTR)devname, &hDev) != SAR_OK
+		|| SKF_GetDevInfo(hDev, &devInfo) != SAR_OK) {
+		ERR_print_errors(bio_err);
+		goto end;
+	}
+
+	(void)BIO_printf(out, "Device %s :\n", devname);
+	(void)BIO_printf(out, "  %-16s : %s\n", "Device State", (char *)devStateName);
+	(void)SKF_PrintDevInfo(out, &devInfo);
+	(void)BIO_puts(out, "\n");
+	ret = 1;
+
+end:
+	if (hDev && SKF_DisConnectDev(hDev) != SAR_OK) {
+		ERR_print_errors(bio_err);
+		ret = 0;
+	}
+	return ret;
+}
+
+static int skf_label(const char *devname, const char *label)
+{
+	int ret = 0;
+	DEVHANDLE hDev = NULL;
+	ULONG ulTimeOut = 0xffffffff;
+
+	if (SKF_ConnectDev((LPSTR)devname, &hDev) != SAR_OK
+		|| SKF_LockDev(hDev, ulTimeOut) != SAR_OK
+		|| SKF_SetLabel(hDev, (LPSTR)label) != SAR_OK) {
+		ERR_print_errors(bio_err);
+		goto end;
+	}
+	ret = 1;
+end:
+	if (hDev && SKF_DisConnectDev(hDev) != SAR_OK) {
+		ERR_print_errors(bio_err);
+		ret = 0;
+	}
+	return ret;
+}
+
+static int skf_transmit(const char *devName, const char *cmd, BIO *out)
+{
+	int ret = 0;
+	BYTE *inbuf = NULL;
+	long inlen;
+	DEVHANDLE hDev = NULL;
+	BYTE outbuf[256];
+	ULONG outlen = sizeof(outbuf);
+
+	if (!(inbuf = OPENSSL_hexstr2buf(cmd, &inlen))
+		|| SKF_ConnectDev((LPSTR)devName, &hDev) != SAR_OK
+		|| SKF_Transmit(hDev, inbuf, (ULONG)inlen, outbuf, &outlen) != SAR_OK
+		|| BIO_write(out, outbuf, (int)outlen) != outlen) {
+		ERR_print_errors(bio_err);
+		goto end;
+	}
+	ret = 1;
+end:
+	OPENSSL_free(inbuf);
+	if (hDev && SKF_DisConnectDev(hDev) != SAR_OK) {
+		ERR_print_errors(bio_err);
+		ret = 0;
+	}
+	return ret;
+}
+
+static int skf_opendev(const char *devname, const char *authkeyhex,
+	DEVINFO *devInfo, DEVHANDLE *phDev)
+{
+	int ret = 0;
+	unsigned char *authKey = NULL;
+	long len;
+
+	if (!(authKey = OPENSSL_hexstr2buf(authkeyhex, &len))) {
+		BIO_printf(bio_err, "Error on parsing authentication key\n");
+		ERR_print_errors(bio_err);
+		return 0;
+	}
+	if (len != 16) {
+		BIO_printf(bio_err, "Invlaid authentication key length\n");
+		goto end;
+	}
+	if (SKF_OpenDevice((LPSTR)devname, authKey, devInfo, phDev) != SAR_OK) {
+		BIO_printf(bio_err, "Error on opening device\n");
+		ERR_print_errors(bio_err);
+		goto end;
+	}
+	ret = 1;
+
+end:
+	OPENSSL_clear_free(authKey, len);
+	return ret;
+}
+
+static int skf_newauthkey(DEVHANDLE hDev, const char *authkeyhex)
+{
+	int ret = 0;
+	unsigned char *authKey = NULL;
+	long len;
+
+	if (!(authKey = OPENSSL_hexstr2buf(authkeyhex, &len))) {
+		BIO_printf(bio_err, "Error on parsing new authentication key\n");
+		ERR_print_errors(bio_err);
+		return 0;
+	}
+	if (len != 16) {
+		BIO_printf(bio_err, "Invalid new authentication key legnth\n");
+		goto end;
+	}
+	if (SKF_ChangeDevAuthKey(hDev, authKey, len) != SAR_OK) {
+		BIO_printf(bio_err, "Error on changing authentication key\n");
+		ERR_print_errors(bio_err);
+		goto end;
+	}
+	ret = 1;
+
+end:
+	OPENSSL_clear_free(authKey, len);
+	return ret;
+}
+
+static int skf_listapps(DEVHANDLE hDev, BIO *out)
+{
+	int ret = 0;
+	HAPPLICATION hApp = NULL;
+	char *nameList = NULL;
+	ULONG nameListLen;
+	const char *name;
+	int i;
+
+	if (SKF_EnumApplication(hDev, NULL, &nameListLen) != SAR_OK) {
+		ERR_print_errors(bio_err);
+		return 0;
+	}
+	if (nameListLen <= 1) {
+		BIO_printf(out, "No application found\n");
+		return 1;
+	}
+	if (!(nameList = OPENSSL_zalloc(nameListLen))) {
+		ERR_print_errors(bio_err);
+		return 0;
+	}
+	if (SKF_EnumApplication(hDev, (LPSTR)nameList, &nameListLen) != SAR_OK) {
+		ERR_print_errors(bio_err);
+		goto end;
+	}
+
+	for (name = nameList, i = 0; *name; name += strlen(name) + 1, i++) {
+		ULONG adminMaxRetry;
+		ULONG adminMinRetry;
+		ULONG userMaxRetry;
+		ULONG userMinRetry;
+		BOOL adminDefaultPin, userDefaultPin;
+
+		if (SKF_OpenApplication(hDev, (LPSTR)name, &hApp) != SAR_OK) {
+			ERR_print_errors(bio_err);
+			goto end;
+		}
+
+		if (SKF_GetPINInfo(hApp, ADMIN_TYPE, &adminMaxRetry,
+			&adminMinRetry, &adminDefaultPin) != SAR_OK) {
+			ERR_print_errors(bio_err);
+			goto end;
+		}
+		if (SKF_GetPINInfo(hApp, USER_TYPE, &userMaxRetry,
+			&userMinRetry, &userDefaultPin) != SAR_OK) {
+			ERR_print_errors(bio_err);
+			goto end;
+		}
+		if (SKF_CloseApplication(hApp) != SAR_OK) {
+			ERR_print_errors(bio_err);
+			goto end;
+		}
+		hApp = NULL;
+
+		(void)BIO_printf(out, "Application %d:\n", i);
+		(void)BIO_printf(out, "  %-16s : %s\n", "ApplicationName", name);
+		(void)BIO_printf(out, "  %-16s : %u\n", "AdminPinMaxRetry", adminMaxRetry);
+		(void)BIO_printf(out, "  %-16s : %u\n", "AdminPinMinRetry", adminMinRetry);
+		(void)BIO_printf(out, "  %-16s : %s\n", "AdminDefaultPin", adminDefaultPin ? "True" : "False");
+		(void)BIO_printf(out, "  %-16s : %u\n", "UserPinMaxRetry", userMaxRetry);
+		(void)BIO_printf(out, "  %-16s : %u\n", "UserPinMinRetry", userMinRetry);
+		(void)BIO_printf(out, "  %-16s : %s\n", "UserDefaultPin", userDefaultPin ? "True" : "False");
+		(void)BIO_puts(out, "\n");
+	}
+
+	ret = 1;
+
+end:
+	if (hApp && SKF_CloseApplication(hApp) != SAR_OK) {
+		ERR_print_errors(bio_err);
+		ret = 0;
+	}
+	return ret;
+}
+
+static int skf_newapp(DEVHANDLE hDev, const char *appname, const char *pass, const char *adminpass)
+{
+	int ret = 0;
+	CHAR *szAdminPin = NULL;
+	CHAR *szUserPin = NULL;
+	HAPPLICATION hApp = NULL;
+	ULONG skf_app_rights = SECURE_ANYONE_ACCOUNT;
+
+	if (!app_passwd(pass, adminpass, (char **)&szUserPin, (char **)&szAdminPin)) {
+		BIO_printf(bio_err, "No application found\n");
+		goto end;
+	}
+
+	if (!pass) {
+		int len = 64;
+		if (!(szUserPin = OPENSSL_zalloc(len))
+			|| EVP_read_pw_string((char *)szUserPin, len, "User PIN > ", 1) < 0) {
+			ERR_print_errors(bio_err);
+			goto end;
+		}
+	}
+
+	if (!adminpass) {
+		int len = 64;
+		if (!(szAdminPin = OPENSSL_zalloc(len))
+			|| EVP_read_pw_string((char *)szAdminPin, len, "Admin PIN > ", 1) < 0) {
+			ERR_print_errors(bio_err);
+			goto end;
+		}
+	}
+
+	if (SKF_CreateApplication(hDev, (LPSTR)appname,
+		szAdminPin, SKF_DEFAULT_ADMIN_PIN_RETRY_COUNT,
+		szUserPin, SKF_DEFAULT_USER_PIN_RETRY_COUNT,
+		skf_app_rights, &hApp) != SAR_OK) {
+		ERR_print_errors(bio_err);
+		goto end;
+	}
+
+	BIO_printf(bio_err, "Application '%s' created\n", appname);
+	ret = 1;
+
+end:
+	OPENSSL_clear_free(szUserPin, strlen((char *)szUserPin));
+	OPENSSL_clear_free(szAdminPin, strlen((char *)szAdminPin));
+	if (hApp && SKF_CloseApplication(hApp) != SAR_OK) {
+		ERR_print_errors(bio_err);
+		ret = 0;
+	}
+	return ret;
+}
+
+static int skf_delapp(DEVHANDLE hDev, const char *appname)
+{
+	if (SKF_DeleteApplication(hDev, (LPSTR)appname) != SAR_OK) {
+		ERR_print_errors(bio_err);
+		return 0;
+	}
+	return 1;
+}
+
+static int skf_changepass(DEVHANDLE hDev, const char *appname,
+	int admin, const char *pass, const char *newpass)
+{
+	int ret = 0;
+	HAPPLICATION hApp = NULL;
+	ULONG ulPINType = admin ? ADMIN_TYPE : USER_TYPE;
+	CHAR *szOldPin = NULL;
+	CHAR *szNewPin = NULL;
+	ULONG ulRetryCount = 0;
+
+	if (SKF_OpenApplication(hDev, (LPSTR)appname, &hApp) != SAR_OK) {
+		ERR_print_errors(bio_err);
+		goto end;
+	}
+
+	if (!pass || !newpass) {
+		int len = 64;
+		if (!(szOldPin = OPENSSL_zalloc(len))
+			|| !(szNewPin = OPENSSL_zalloc(len))
+			|| EVP_read_pw_string((char *)szOldPin, len, "Old PIN > ", 0) < 0
+			|| EVP_read_pw_string((char *)szNewPin, len, "New PIN > ", 0) < 0) {
+			ERR_print_errors(bio_err);
+			goto end;
+		}
+	}
+
+	if (!app_passwd(pass, newpass, (char **)&szOldPin, (char **)&szNewPin)) {
+		BIO_puts(bio_err, "Error getting password\n");
+		return 0;
+	}
+
+	if (SKF_ChangePIN(hApp, ulPINType, szOldPin, szNewPin,
+		&ulRetryCount) != SAR_OK) {
+		BIO_printf(bio_err, "Retry Count = %u\n", ulRetryCount);
+		ERR_print_errors(bio_err);
+		goto end;
+	}
+
+	ret = 1;
+end:
+	OPENSSL_clear_free(szOldPin, sizeof(szOldPin));
+	OPENSSL_clear_free(szNewPin, sizeof(szNewPin));
+	if (hApp && SKF_CloseApplication(hApp) != SAR_OK) {
+		ERR_print_errors(bio_err);
+		ret = 0;
+	}
+	return ret;
+}
+
+static int skf_unblock(DEVHANDLE hDev, const char *appname,
+	const char *adminpassarg, const char *userpassarg)
+{
+	int ret = 0;
+	HAPPLICATION hApp = NULL;
+	CHAR *szAdminPin = NULL;
+	CHAR *szNewUserPin = NULL;
+	ULONG ulRetryCount = 0;
+
+	if (SKF_OpenApplication(hDev, (LPSTR)appname, &hApp) != SAR_OK) {
+		ERR_print_errors(bio_err);
+		return 0;
+	}
+	if (!adminpassarg) {
+		int len = 64;
+		if (!(szAdminPin = OPENSSL_zalloc(len))
+			|| EVP_read_pw_string((char *)szAdminPin, len, "Admin PIN > ", 0) < 0) {
+			ERR_print_errors(bio_err);
+			goto end;
+		}
+	} else {
+		if (!app_passwd(adminpassarg, NULL, (char **)&szAdminPin, NULL)) {
+			BIO_puts(bio_err, "Error getting password\n");
+			goto end;
+		}
+	}
+
+	if (!userpassarg) {
+		int len = 64;
+		if (!(szNewUserPin = OPENSSL_zalloc(len))
+			|| EVP_read_pw_string((char *)szNewUserPin, len, "New User PIN > ", 0) < 0) {
+			ERR_print_errors(bio_err);
+			goto end;
+		}
+	} else {
+		if (!app_passwd(userpassarg, NULL, (char **)&szNewUserPin, NULL)) {
+			BIO_puts(bio_err, "Error getting password\n");
+			goto end;
+		}
+	}
+
+	if (SKF_UnblockPIN(hApp, szAdminPin, szNewUserPin, &ulRetryCount) != SAR_OK) {
+		BIO_printf(bio_err, "Invalid admin PIN, retry count = %u\n", ulRetryCount);
+		ERR_print_errors(bio_err);
+		goto end;
+	}
+	ret = 1;
+end:
+	OPENSSL_clear_free(szAdminPin, strlen((char *)szAdminPin));
+	OPENSSL_clear_free(szNewUserPin, strlen((char *)szNewUserPin));
+	if (hApp && SKF_CloseApplication(hApp) != SAR_OK) {
+		ERR_print_errors(bio_err);
+		ret = 0;
+	}
+	return ret;
+}
+
+static int skf_openapp(DEVHANDLE hDev, const char *name, int admin,
+	const char *passarg, HAPPLICATION *phApp)
+{
+	int ret = 0;
+	HAPPLICATION hApp = NULL;
+	CHAR *szPin = NULL;
+	ULONG numRetry;
+	ULONG user_type = admin ? ADMIN_TYPE : USER_TYPE;
+
+	if (SKF_OpenApplication(hDev, (LPSTR)name, &hApp) != SAR_OK) {
+		ERR_print_errors(bio_err);
+		return 0;
+	}
+
+	if (passarg) {
+		if (!app_passwd(passarg, NULL, (char **)&szPin, NULL)) {
+			BIO_printf(bio_err, "Error on reading password\n");
+			goto end;
+		}
+	} else {
+		int len = 64;
+		if (!(szPin = OPENSSL_zalloc(len))
+			|| EVP_read_pw_string((char *)szPin, len, "PIN >", 0) < 0) {
+			ERR_print_errors(bio_err);
+			goto end;
+		}
+	}
+
+	if (SKF_VerifyPIN(hApp, user_type, szPin, &numRetry) != SAR_OK) {
+		BIO_printf(bio_err, "Invalid %s PIN, retry count = %u\n",
+			admin ? "admin" :  "user", numRetry);
+		ERR_print_errors(bio_err);
+		goto end;
+	}
+	*phApp = hApp;
+	hApp = NULL;
+	ret = 1;
+
+end:
+
+	OPENSSL_clear_free(szPin, strlen((char *)szPin));
+	if (hApp && SKF_CloseApplication(hApp) != SAR_OK) {
+		ERR_print_errors(bio_err);
+		ret = 0;
+	}
+	return ret;
+}
+
+static int skf_listobjs(HAPPLICATION hApp, BIO *out)
+{
+	int ret = 0;
+	char *nameList = NULL;
+	ULONG nameListLen;
+	const char *name;
+	int i;
+
+	if (SKF_EnumFiles(hApp, NULL, &nameListLen) != SAR_OK) {
+		ERR_print_errors(bio_err);
+		return 0;
+	}
+
+	if (!(nameList = OPENSSL_malloc(nameListLen))) {
+		ERR_print_errors(bio_err);
+		return 0;
+	}
+
+	if (SKF_EnumFiles(hApp, (LPSTR)nameList, &nameListLen) != SAR_OK) {
+		ERR_print_errors(bio_err);
+		goto end;
+	}
+
+	for (name = nameList, i = 0; *name; name += strlen(name) + 1, i++) {
+		FILEATTRIBUTE fileInfo;
+
+		if (SKF_GetFileInfo(hApp, (LPSTR)name, &fileInfo) != SAR_OK) {
+			ERR_print_errors(bio_err);
+			goto end;
+		}
+
+		BIO_printf(out, "Object %d:\n", i);
+		BIO_printf(out, "  %-16s : %s\n", "Object Name", (char *)&(fileInfo.FileName));
+		BIO_printf(out, "  %-16s : %u\n", "Object Size", fileInfo.FileSize);
+		BIO_printf(out, "  %-16s : %08X\n", "Read Rights", fileInfo.ReadRights);
+		BIO_printf(out, "  %-16s : %08X\n", "Write Rights", fileInfo.WriteRights);
+		BIO_puts(out, "\n");
+	}
+
+	ret = 1;
+
+end:
+	OPENSSL_free(nameList);
+	return ret;
+}
+
+static int skf_importobj(HAPPLICATION happ, const char *objname, int admin, const char *infile)
+{
+	int ret = 0;
+	BIO *in = NULL;
+	ULONG ulReadRights = SECURE_ANYONE_ACCOUNT;
+	ULONG ulWriteRights = SECURE_USER_ACCOUNT;
+	unsigned char *buf = NULL;
+	int len = SKF_MAX_FILE_SIZE;
+
+	if (admin) {
+		ulWriteRights = SECURE_ADM_ACCOUNT;
+	}
+
+	if (!(in = bio_open_default(infile, 'r', FORMAT_BINARY))) {
+		goto end;
+	}
+	if ((len = bio_to_mem(&buf, SKF_MAX_FILE_SIZE, in)) <= 0) {
+		goto end;
+	}
+
+	if (SKF_CreateFile(happ, (LPSTR)objname, len,
+		ulReadRights, ulWriteRights) != SAR_OK) {
+		ERR_print_errors(bio_err);
+		goto end;
+	}
+
+	if (SKF_WriteFile(happ, (LPSTR)objname, 0, buf, len) != SAR_OK) {
+		ERR_print_errors(bio_err);
+		goto end;
+	}
+
+	BIO_printf(bio_err, "Object '%s' (%u bytes) created\n", objname, len);
+	ret = 1;
+
+end:
+	OPENSSL_free(buf);
+	return ret;
+}
+
+static int skf_exportobj(HAPPLICATION hApp, const char *objname, BIO *out)
+{
+	int ret = 0;
+	FILEATTRIBUTE fileInfo;
+	unsigned char *buf = NULL;
+	ULONG ulen = SKF_MAX_FILE_SIZE;
+	int len;
+
+	if (SKF_GetFileInfo(hApp, (LPSTR)objname, &fileInfo) != SAR_OK
+		|| !(buf = OPENSSL_malloc(fileInfo.FileSize))
+		|| SKF_ReadFile(hApp, (LPSTR)objname, 0, fileInfo.FileSize, buf, &ulen) != SAR_OK) {
+		ERR_print_errors(bio_err);
+		goto end;
+	}
+
+	if (ulen != fileInfo.FileSize) {
+		BIO_printf(bio_err, "Error on reading object\n");
+		goto end;
+	}
+
+	if ((len = BIO_write(out, buf, (int)ulen)) != (int)ulen) {
+		ERR_print_errors(bio_err);
+		goto end;
+	}
+	(void)BIO_printf(bio_err, "%d bytes exportd\n", len);
+
+	ret = 1;
+
+end:
+	OPENSSL_free(buf);
+	return ret;
+}
+
+static int skf_delobj(HAPPLICATION hApp, const char *objname)
+{
+	if (SKF_DeleteFile(hApp, (LPSTR)objname) != SAR_OK) {
+		ERR_print_errors(bio_err);
+		return 0;
+	}
+	return 1;
+}
+
+static int skf_listcontainers(HAPPLICATION hApp, BIO *out)
+{
+	int ret = 0;
+	HCONTAINER hContainer = NULL;
+	char *nameList = NULL;
+	ULONG nameListLen;
+	const char *name;
+	int i;
+
+	if (SKF_EnumContainer(hApp, NULL, &nameListLen) != SAR_OK) {
+		ERR_print_errors(bio_err);
+		return 0;
+	}
+	if (!nameListLen) {
+		(void)BIO_puts(bio_err, "No container found\n");
+		return 1;
+	}
+	if (!(nameList = OPENSSL_malloc(nameListLen))) {
+		ERR_print_errors(bio_err);
+		return 0;
+	}
+	if (SKF_EnumContainer(hApp, (LPSTR)nameList, &nameListLen) != SAR_OK) {
+		ERR_print_errors(bio_err);
+		goto end;
+	}
+
+	for (name = nameList, i = 0; *name; name += strlen(name) + 1, i++) {
+		ULONG containerType;
+		LPSTR containerTypeName;
+
+		if (SKF_OpenContainer(hApp, (LPSTR)name, &hContainer) != SAR_OK) {
+			ERR_print_errors(bio_err);
+			goto end;
+		}
+		if (SKF_GetContainerType(hContainer, &containerType) != SAR_OK) {
+			ERR_print_errors(bio_err);
+			goto end;
+		}
+		if (SKF_GetContainerTypeName(containerType, &containerTypeName) != SAR_OK) {
+			ERR_print_errors(bio_err);
+			goto end;
+		}
+		if (SKF_CloseContainer(hContainer) != SAR_OK) {
+			ERR_print_errors(bio_err);
+			goto end;
+		}
+		hContainer = NULL;
+
+		(void)BIO_printf(out, "  Container %d : %s (%s)\n",
+			i, name, (char *)containerTypeName);
+	}
+
+	ret = 1;
+
+end:
+	if (hContainer && SKF_CloseContainer(hContainer) != SAR_OK) {
+		ERR_print_errors(bio_err);
+		ret = 0;
+	}
+	return ret;
+}
+
+static int skf_newcontainer(HAPPLICATION hApp, const char *name, const char *algor)
+{
+	int ret = 0;
+	HCONTAINER hContainer = NULL;
+
+	if (SKF_CreateContainer(hApp, (LPSTR)name, &hContainer) != SAR_OK) {
+		ERR_print_errors(bio_err);
+		return 0;
+	}
+	(void)BIO_printf(bio_err, "Container '%s' created\n", name);
+
+	if (strcmp(algor, "SM2") == 0 || strcmp(algor, "sm2") == 0) {
+		ECCPUBLICKEYBLOB publicKey = {0, {0}, {0}};
+		if (SKF_GenECCKeyPair(hContainer, SGD_SM2_1, &publicKey) != SAR_OK) {
+			ERR_print_errors(bio_err);
+			goto end;
+		}
+
+		(void)BIO_printf(bio_err, "SM2 signing key pair generated\n");
+		(void)BIO_printf(bio_err, "SM2 Signing Public Key:\n");
+		if (SKF_PrintECCPublicKey(bio_err, &publicKey) != SAR_OK) {
+			ERR_print_errors(bio_err);
+			goto end;
+		}
+
+	} else if (strcmp(algor, "RSA") == 0 || strcmp(algor, "rsa")) {
+		RSAPUBLICKEYBLOB publicKey = {0, 0, {0}, {0}};
+		if (SKF_GenRSAKeyPair(hContainer, SGD_RSA, &publicKey) != SAR_OK) {
+			ERR_print_errors(bio_err);
+			goto end;
+		}
+
+		(void)BIO_printf(bio_err, "RSA signing key pair generated\n");
+		(void)BIO_printf(bio_err, "RSA Signing Public Key:\n");
+		if (SKF_PrintRSAPublicKey(bio_err, &publicKey) != SAR_OK) {
+			ERR_print_errors(bio_err);
+			goto end;
+		}
+
+	} else {
+		(void)BIO_printf(bio_err, "Invalid container type\n");
+		goto end;
+	}
+
+	ret = 1;
+
+end:
+	if (hContainer && SKF_CloseContainer(hContainer) != SAR_OK) {
+		ERR_print_errors(bio_err);
+		ret = 0;
+	}
+	return ret;
+}
+
+static int skf_delcontainer(HAPPLICATION hApp, const char *containername)
+{
+	if (SKF_DeleteContainer(hApp, (LPSTR)containername) != SAR_OK) {
+		ERR_print_errors(bio_err);
+		return 0;
+	}
+	return 1;
+}
+
+static int skf_importenckey(DEVHANDLE hDev, HCONTAINER hContainer, DEVINFO *devInfo,
+	const char *infile, int informat, const char *passarg)
+{
+	int ret = 0;
+	EVP_PKEY *pkey = NULL;
+
+        if (!(pkey = load_key(infile, informat, 0, passarg, NULL, "Private Key"))) {
+		BIO_printf(bio_err, "Error on reading private key\n");
+		return 0;
+	}
+	if (SKF_ImportPrivateKey(hDev, hContainer, pkey, devInfo->DevAuthAlgId) != SAR_OK) {
+		BIO_printf(bio_err, "Error on importing private key\n");
+		ERR_print_errors(bio_err);
+		goto end;
+	}
+	ret = 1;
+end:
+	EVP_PKEY_free(pkey);
+	return ret;
+}
+
+static int skf_exportkey(HCONTAINER hContainer, BOOL bSign, int outformat, BIO *out)
+{
+	int ret = 0;
+	EVP_PKEY *pkey = NULL;
+
+	if (SKF_ExportEVPPublicKey(hContainer, bSign, &pkey) != SAR_OK) {
+		BIO_printf(bio_err, "Error on exporting public key\n");
+		ERR_print_errors(bio_err);
+		return 0;
+	}
+
+	if (outformat == FORMAT_ASN1)
+		ret = i2d_PUBKEY_bio(out, pkey);
+	else if (outformat == FORMAT_PEM)
+		ret = PEM_write_bio_PUBKEY(out, pkey);
+	else {
+		BIO_printf(bio_err, "bad output format specified for outfile\n");
+		goto end;
+	}
+	if (!ret) {
+		BIO_printf(bio_err, "Unable to write certificate\n");
+		ERR_print_errors(bio_err);
+		goto end;
+	}
+
+end:
+	EVP_PKEY_free(pkey);
+	return ret;
+}
+
+static int skf_printkeys(HCONTAINER hContainer, BIO *out)
+{
+	int ret = 1;
+	ULONG containerType;
+	SKF_PUBLICKEYBLOB publicKey;
+	ULONG len = sizeof(SKF_PUBLICKEYBLOB);
+
+	if (SKF_GetContainerType(hContainer, &containerType) != SAR_OK) {
+		ERR_print_errors(bio_err);
+		return 0;
+	}
+	if (containerType == SKF_CONTAINER_TYPE_UNDEF) {
+		BIO_printf(bio_err, "Container not initialized\n");
+		return 0;
+	}
+
+	memset(&publicKey, 0, sizeof(publicKey));
+	if (SKF_ExportPublicKey(hContainer, SGD_TRUE, (BYTE *)&publicKey, &len) == SAR_OK) {
+		if (containerType == SKF_CONTAINER_TYPE_ECC) {
+			BIO_puts(out, "SM2 signing public key:\n");
+			if (SKF_PrintECCPublicKey(out,
+				(ECCPUBLICKEYBLOB *)&publicKey) != SAR_OK) {
+				ERR_print_errors(bio_err);
+				ret = 0;
+			}
+		} else {
+			BIO_puts(out, "RSA signing public key:\n");
+			if (SKF_PrintRSAPublicKey(out,
+				(RSAPUBLICKEYBLOB *)&publicKey) != SAR_OK) {
+				ERR_print_errors(bio_err);
+				ret = 0;
+			}
+		}
+	} else {
+		ERR_print_errors(bio_err);
+		ret = 0;
+	}
+
+	memset(&publicKey, 0, sizeof(publicKey));
+	if (SKF_ExportPublicKey(hContainer, SGD_FALSE, (BYTE *)&publicKey, &len) == SAR_OK) {
+		if (containerType == SKF_CONTAINER_TYPE_ECC) {
+			BIO_puts(out, "SM2 encryption public key:\n");
+			if (SKF_PrintECCPublicKey(out,
+				(ECCPUBLICKEYBLOB *)&publicKey) != SAR_OK) {
+				ERR_print_errors(bio_err);
+				ret = 0;
+			}
+		} else {
+			BIO_puts(out, "RSA encryption public key:\n");
+			if (SKF_PrintRSAPublicKey(out,
+				(RSAPUBLICKEYBLOB *)&publicKey) != SAR_OK) {
+				ERR_print_errors(bio_err);
+				ret = 0;
+			}
+		}
+	} else {
+		ERR_print_errors(bio_err);
+		ret = 0;
+	}
+
+	return ret;
+}
+
+static int skf_importcert(HCONTAINER hContainer, const char *infile, int informat)
+{
+	int ret = 0;
+	X509 *x509 = NULL;
+
+	if (!(x509 = load_cert(infile, informat, "Certificate"))) {
+		BIO_printf(bio_err, "Load certificate failure\n");
+		ERR_print_errors(bio_err);
+		return 0;
+	}
+	if (SKF_ImportX509CertificateByKeyUsage(hContainer, x509) != SAR_OK) {
+		BIO_printf(bio_err, "Error on importing certificate\n");
+		ERR_print_errors(bio_err);
+		goto end;
+	}
+	ret = 1;
+
+end:
+	X509_free(x509);
+	return ret;
+}
+
+static int skf_exportcert(HCONTAINER hContainer, BOOL bSign, int outform, BIO *out)
+{
+	int ret = 0;
+	X509 *x509 = NULL;
+
+	if (SKF_ExportX509Certificate(hContainer, bSign, &x509) != SAR_OK) {
+		BIO_printf(bio_err, "Error on exporing certificate\n");
+		ERR_print_errors(bio_err);
+		return 0;
+	}
+	if (outform == FORMAT_ASN1)
+		ret = i2d_X509_bio(out, x509);
+	else if (outform == FORMAT_PEM)
+		ret = PEM_write_bio_X509(out, x509);
+	else {
+		BIO_printf(bio_err, "bad output format specified for outfile\n");
+		goto end;
+	}
+	if (!ret) {
+		BIO_printf(bio_err, "Unable to write certificate\n");
+		ERR_print_errors(bio_err);
+		goto end;
+	}
+	ret = 1;
+
+end:
+	X509_free(x509);
+	return ret;
+}
+
+int skf_main(int argc, char **argv)
+{
+	int ret = 0;
+	char *infile = NULL, *outfile = NULL, *prog;
+	BIO *out = NULL;
+	OPTION_CHOICE o;
+	int informat = FORMAT_PEM, outformat = FORMAT_UNDEF;
+	char *lib = NULL, *vendor = NULL, *label = NULL;
+	char *authkey = NULL, *newauthkey = NULL;
+	char *devname = NULL, *appname = NULL, *containername = NULL, *objname = NULL;
+	char *passarg = NULL, *adminpassarg = NULL, *newpassarg = NULL;
+	char *keypassarg = NULL;
+
+	int op = OP_NONE;
+	int admin = 0;
+	char *algor = "SM2";
+
+	char *transmit_cmd = NULL;
+
+	DEVINFO devInfo = {{0,0}};
+	DEVHANDLE hdev = NULL;
+	HAPPLICATION happ = NULL;
+	HCONTAINER hcontainer = NULL;
+
+	prog = opt_init(argc, argv, skf_options);
+	while ((o = opt_next()) != OPT_EOF) {
+		switch (o) {
+		case OPT_EOF:
+		case OPT_ERR:
+opthelp:
+			BIO_printf(bio_err, "%s: Use -help for summary.\n", prog);
+			goto end;
+		case OPT_HELP:
+			opt_help(skf_options);
+			ret = 0;
+			goto end;
+		case OPT_LIB:
+			lib = opt_arg();
+			break;
+		case OPT_VENDOR:
+			vendor = opt_arg();
+			break;
+		case OPT_LISTDEVS:
+			if (op)
+				goto opthelp;
+			op = OP_LISTDEVS;
+			break;
+		case OPT_DEV:
+			devname = opt_arg();
+			break;
+		case OPT_DEVINFO:
+			if (op)
+				goto opthelp;
+			op = OP_DEVINFO;
+			break;
+		case OPT_LABEL:
+			if (op)
+				goto opthelp;
+			op = OP_LABEL;
+			label = opt_arg();
+			break;
+		case OPT_AUTHKEY:
+			authkey = opt_arg();
+			break;
+		case OPT_NEWAUTHKEY:
+			if (op)
+				goto opthelp;
+			op = OP_NEWAUTHKEY;
+			newauthkey = opt_arg();
+			break;
+		case OPT_TRANSMIT:
+			if (op)
+				goto opthelp;
+			op = OP_TRANSMIT;
+			transmit_cmd = opt_arg();
+			break;
+		case OPT_LISTAPPS:
+			if (op)
+				goto opthelp;
+			op = OP_LISTAPPS;
+			break;
+		case OPT_NEWAPP:
+			if (op)
+				goto opthelp;
+			op = OP_NEWAPP;
+			appname = opt_arg();
+			break;
+		case OPT_DELAPP:
+			if (op)
+				goto opthelp;
+			op = OP_DELAPP;
+			appname = opt_arg();
+			break;
+		case OPT_APP:
+			appname = opt_arg();
+			break;
+		case OPT_CHANGEPASS:
+			if (op)
+				goto opthelp;
+			op = OP_CHANGEPASS;
+			break;
+		case OPT_PASS:
+			passarg = opt_arg();
+			break;
+		case OPT_ADMIN:
+			admin = 1;
+			break;
+		case OPT_ADMINPASS:
+			adminpassarg = opt_arg();
+			break;
+		case OPT_NEWPASS:
+			newpassarg = opt_arg();
+			break;
+		case OPT_UNBLOCK:
+			if (op)
+				goto opthelp;
+			op = OP_UNBLOCK;
+			break;
+		case OPT_LISTOBJS:
+			if (op)
+				goto opthelp;
+			op = OP_LISTOBJS;
+			break;
+		case OPT_IMPORTOBJ:
+			if (op)
+				goto opthelp;
+			op = OP_IMPORTOBJ;
+			objname = opt_arg();
+			break;
+		case OPT_EXPORTOBJ:
+			if (op)
+				goto opthelp;
+			op = OP_EXPORTOBJ;
+			objname = opt_arg();
+			break;
+		case OPT_DELOBJ:
+			if (op)
+				goto opthelp;
+			op = OP_DELOBJ;
+			objname = opt_arg();
+			break;
+		case OPT_OBJ:
+			objname = opt_arg();
+			break;
+		case OPT_LISTCONTAINERS:
+			if (op)
+				goto opthelp;
+			op = OP_LISTCONTAINERS;
+			break;
+		case OPT_NEWCONTAINER:
+			if (op)
+				goto opthelp;
+			op = OP_NEWCONTAINER;
+			containername = opt_arg();
+			break;
+		case OPT_ALGORITHM:
+			algor = opt_arg();
+			break;
+		case OPT_DELCONTAINER:
+			if (op)
+				goto opthelp;
+			op = OP_DELCONTAINER;
+			containername = opt_arg();
+			break;
+		case OPT_CONTAINER:
+			containername = opt_arg();
+			break;
+		case OPT_IMPORTENCKEY:
+			if (op)
+				goto opthelp;
+			op = OP_IMPORTENCKEY;
+			break;
+		case OPT_KEYPASS:
+			keypassarg = opt_arg();
+			break;
+		case OPT_EXPORTSIGNKEY:
+			if (op)
+				goto opthelp;
+			op = OP_EXPORTSIGNKEY;
+			break;
+		case OPT_EXPORTENCKEY:
+			if (op)
+				goto opthelp;
+			op = OP_EXPORTENCKEY;
+			break;
+		case OPT_PRINTKEYS:
+			if (op)
+				goto opthelp;
+			op = OP_PRINTKEYS;
+			break;
+		case OPT_IMPORTCERT:
+			if (op)
+				goto opthelp;
+			op = OP_IMPORTCERT;
+			break;
+		case OPT_EXPORTSIGNCERT:
+			if (op)
+				goto opthelp;
+			op = OP_EXPORTSIGNCERT;
+			break;
+		case OPT_EXPORTENCCERT:
+			if (op)
+				goto opthelp;
+			op = OP_EXPORTENCCERT;
+			break;
+		case OPT_IN:
+			infile = opt_arg();
+			break;
+		case OPT_OUT:
+			outfile = opt_arg();
+			break;
+		case OPT_INFORM:
+			if (!opt_format(opt_arg(), OPT_FMT_ANY, &informat))
+				goto opthelp;
+			break;
+		case OPT_OUTFORM:
+			if (!opt_format(opt_arg(), OPT_FMT_ANY, &outformat))
+				goto opthelp;
+			break;
+		}
+	}
+	argc = opt_num_rest();
+	if (argc != 0)
+		goto opthelp;
+
+	if (!lib) {
+		BIO_printf(bio_err, "Option `-lib' not specified\n");
+		goto opthelp;
+	}
+	if (SKF_LoadLibrary((LPSTR)lib, (LPSTR)vendor) != SAR_OK) {
+		ERR_print_errors(bio_err);
+		goto end;
+	}
+
+	/* prepare output bio */
+	switch (op) {
+	case OP_LISTDEVS:
+	case OP_DEVINFO:
+	case OP_LISTAPPS:
+	case OP_LISTOBJS:
+	case OP_LISTCONTAINERS:
+	case OP_PRINTKEYS:
+		if (outformat == FORMAT_UNDEF) {
+			outformat = FORMAT_TEXT;
+		}
+		if (outformat != FORMAT_TEXT) {
+			BIO_printf(bio_err, "Invalid outform option\n");
+			goto opthelp;
+		}
+		break;
+	case OP_EXPORTOBJ:
+		if (outformat == FORMAT_UNDEF)
+			outformat = FORMAT_BINARY;
+		if (outformat != FORMAT_BINARY) {
+			BIO_printf(bio_err, "Invalid outform option\n");
+			goto opthelp;
+		}
+		break;
+	default:
+		if (outformat != FORMAT_UNDEF) {
+			BIO_printf(bio_err, "Invalid outform option\n");
+			goto opthelp;
+		}
+	}
+	if (!(out = bio_open_default(outfile, 'w', outformat))) {
+		goto end;
+	}
+
+	/* without devname */
+	switch (op) {
+	case OP_NONE:
+		ret = 1;
+		goto end;
+	case OP_LISTDEVS:
+		ret = skf_listdevs(out);
+		goto end;
+	}
+
+	/* without opendev */
+	if (!devname) {
+		BIO_printf(bio_err, "Error: `-dev` not specified\n");
+		goto opthelp;
+	}
+
+	switch (op) {
+	case OP_DEVINFO:
+		ret = skf_devinfo(devname, out);
+		goto end;
+	case OP_LABEL:
+		ret = skf_label(devname, label);
+		if (ret)
+			BIO_printf(bio_err, "Device label changed to %s\n", label);
+		goto end;
+	case OP_TRANSMIT:
+		ret = skf_transmit(devname, transmit_cmd, out);
+		if (ret)
+			BIO_printf(bio_err, "Tranmist finished\n");
+		goto end;
+	}
+
+	/* opendev */
+	if (!authkey) {
+		BIO_printf(bio_err, "Authentication key not specified\n");
+		goto opthelp;
+	}
+	if (!skf_opendev(devname, authkey, &devInfo, &hdev)) {
+		goto end;
+	}
+
+	/* without appname */
+	switch (op) {
+	case OP_NEWAUTHKEY:
+		ret = skf_newauthkey(hdev, newauthkey);
+		if (ret)
+			BIO_puts(bio_err, "Device authentication key changed\n");
+		goto end;
+	case OP_LISTAPPS:
+		ret = skf_listapps(hdev, out);
+		goto end;
+	}
+
+	/* without openapp */
+	if (!appname) {
+		BIO_printf(bio_err, "No application name\n");
+		goto opthelp;
+	}
+
+	switch (op) {
+	case OP_NEWAPP:
+		ret = skf_newapp(hdev, appname, passarg, adminpassarg);
+		if (ret)
+			BIO_printf(bio_err, "Application %s created\n", appname);
+		goto end;
+	case OP_DELAPP:
+		ret = skf_delapp(hdev, appname);
+		if (ret)
+			BIO_printf(bio_err, "Application %s deleted\n", appname);
+		goto end;
+	case OP_CHANGEPASS:
+		if (adminpassarg) {
+			admin = 1;
+			passarg = adminpassarg;
+		}
+		ret = skf_changepass(hdev, appname, admin, passarg, newpassarg);
+		if (ret)
+			BIO_printf(bio_err, "Application %s %s PIN changed\n",
+				appname, admin ? "administrator" : "user");
+		goto end;
+	case OP_UNBLOCK:
+		if (admin && passarg && !adminpassarg)
+			adminpassarg = passarg;
+		ret = skf_unblock(hdev, appname, adminpassarg, newpassarg);
+		if (ret)
+			BIO_printf(bio_err,
+				"Application %s user PIN unblocked\n", appname);
+		goto end;
+	}
+
+	/* open app */
+	if (adminpassarg) {
+		admin = 1;
+		passarg = adminpassarg;
+	}
+	if (!skf_openapp(hdev, appname, admin, passarg,  &happ)) {
+		goto end;
+	}
+	/*
+	BIO_printf(bio_err, "Application %s opened by %s\n", appname,
+		admin ? "administractor" : "user");
+	*/
+
+	switch (op) {
+	case OP_LISTOBJS:
+		BIO_printf(bio_err, "Application %s Objects:\n", appname);
+		ret = skf_listobjs(happ, out);
+		goto end;
+	case OP_LISTCONTAINERS:
+		BIO_printf(bio_err, "Application %s Containers:\n", appname);
+		ret = skf_listcontainers(happ, out);
+		goto end;
+	}
+
+	/* with objname */
+	if ((op == OP_IMPORTOBJ || op == OP_EXPORTOBJ || op == OP_DELOBJ) && !objname) {
+		BIO_printf(bio_err, "Data object name is not given\n");
+		goto opthelp;
+	}
+	switch (op) {
+	case OP_IMPORTOBJ:
+		ret = skf_importobj(happ, objname, admin, infile);
+		if (ret)
+			BIO_printf(bio_err, "Object %s imported\n", objname);
+		goto end;
+	case OP_EXPORTOBJ:
+		ret = skf_exportobj(happ, objname, out);
+		if (ret)
+			BIO_printf(bio_err, "Object %s exported\n", objname);
+		goto end;
+	case OP_DELOBJ:
+		ret = skf_delobj(happ, objname);
+		if (ret)
+			BIO_printf(bio_err, "Object %s deleted\n", objname);
+		goto end;
+	}
+
+	if (!containername) {
+		BIO_printf(bio_err, "No container name is given\n");
+		goto opthelp;
+	}
+	switch (op) {
+	case OP_NEWCONTAINER:
+		ret = skf_newcontainer(happ, containername, algor);
+		if (ret)
+			BIO_printf(bio_err, "Container %s created\n", containername);
+		goto end;
+	case OP_DELCONTAINER:
+		ret = skf_delcontainer(happ, containername);
+		if (ret)
+			BIO_printf(bio_err, "Container %s deleted\n", containername);
+		goto end;
+	}
+
+	if (SKF_OpenContainer(happ, (LPSTR)containername, &hcontainer) != SAR_OK) {
+		BIO_printf(bio_err, "Error on open container %s\n", containername);
+		ERR_print_errors(bio_err);
+		goto end;
+	}
+	switch (op) {
+	case OP_IMPORTENCKEY:
+		ret = skf_importenckey(hdev, hcontainer, &devInfo, infile, informat, keypassarg);
+		if (ret)
+			BIO_printf(bio_err, "Private key imported to container %s\n", containername);
+		break;
+	case OP_EXPORTSIGNKEY:
+		ret = skf_exportkey(hcontainer, SGD_TRUE, outformat, out);
+		break;
+	case OP_EXPORTENCKEY:
+		ret = skf_exportkey(hcontainer, SGD_FALSE, outformat, out);
+		break;
+	case OP_PRINTKEYS:
+		ret = skf_printkeys(hcontainer, out);
+		break;
+	case OP_IMPORTCERT:
+		ret = skf_importcert(hcontainer, infile, informat);
+		if (ret)
+			BIO_printf(bio_err, "Certificate imported to container %s\n", containername);
+		break;
+	case OP_EXPORTSIGNCERT:
+		ret = skf_exportcert(hcontainer, SGD_TRUE, outformat, out);
+		break;
+	case OP_EXPORTENCCERT:
+		ret = skf_exportcert(hcontainer, SGD_FALSE, outformat, out);
+		break;
+	}
+
+	if (ret)
+		ret = 0;
+	else
+		ret = 1;
+
+end:
+	if (hcontainer) SKF_CloseContainer(hcontainer);
+	if (happ) SKF_CloseApplication(happ);
+	if (hdev) SKF_DisConnectDev(hdev);
+	return ret;
+}
+#endif
diff --git a/apps/sm2.c b/apps/sm2.c
new file mode 100644
index 000000000..e483b0e33
--- /dev/null
+++ b/apps/sm2.c
@@ -0,0 +1,459 @@
+/* ====================================================================
+ * Copyright (c) 2014 - 2018 The GmSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the GmSSL Project.
+ *    (http://gmssl.org/)"
+ *
+ * 4. The name "GmSSL Project" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    guanzhi1980@gmail.com.
+ *
+ * 5. Products derived from this software may not be called "GmSSL"
+ *    nor may "GmSSL" appear in their names without prior written
+ *    permission of the GmSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the GmSSL Project
+ *    (http://gmssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE GmSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE GmSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+/*
+ * Copyright 2002-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#ifdef OPENSSL_NO_SM2
+NON_EMPTY_TRANSLATION_UNIT
+#else
+
+# include 
+# include 
+# include 
+# include 
+# include 
+# include 
+# include 
+# include 
+# include 
+# include "../crypto/ec/ec_lcl.h"
+# include "apps.h"
+
+static OPT_PAIR conv_forms[] = {
+    {"compressed", POINT_CONVERSION_COMPRESSED},
+    {"uncompressed", POINT_CONVERSION_UNCOMPRESSED},
+    {"hybrid", POINT_CONVERSION_HYBRID},
+    {NULL}
+};
+
+static OPT_PAIR param_enc[] = {
+    {"named_curve", OPENSSL_EC_NAMED_CURVE},
+    {"explicit", 0},
+    {NULL}
+};
+
+typedef enum OPTION_choice {
+    OPT_ERR = -1, OPT_EOF = 0, OPT_HELP,
+    OPT_INFORM, OPT_OUTFORM, OPT_ENGINE, OPT_IN, OPT_OUT,
+    OPT_NOOUT, OPT_TEXT, OPT_PARAM_OUT, OPT_PUBIN, OPT_PUBOUT,
+    OPT_PASSIN, OPT_PASSOUT, OPT_PARAM_ENC, OPT_CONV_FORM, OPT_CIPHER,
+    OPT_NO_PUBLIC, OPT_CHECK, OPT_CONFIG,
+    OPT_GENKEY, OPT_RAND, OPT_ID, OPT_GENZID
+} OPTION_CHOICE;
+
+OPTIONS sm2_options[] = {
+    {"help", OPT_HELP, '-', "Display this summary"},
+    {"in", OPT_IN, 's', "Input file"},
+    {"inform", OPT_INFORM, 'f', "Input format - DER or PEM"},
+    {"out", OPT_OUT, '>', "Output file"},
+    {"outform", OPT_OUTFORM, 'F', "Output format - DER or PEM"},
+    {"noout", OPT_NOOUT, '-', "Don't print key out"},
+    {"text", OPT_TEXT, '-', "Print the key"},
+    {"param_out", OPT_PARAM_OUT, '-', "Print 'sm2p256v1' elliptic curve parameters"},
+    {"pubin", OPT_PUBIN, '-', "Expect a public key in input file"},
+    {"pubout", OPT_PUBOUT, '-', "Output public key, not private"},
+    {"no_public", OPT_NO_PUBLIC, '-', "exclude public key from private key"},
+    {"check", OPT_CHECK, '-', "check key consistency"},
+    {"passin", OPT_PASSIN, 's', "Input file pass phrase source"},
+    {"passout", OPT_PASSOUT, 's', "Output file pass phrase source"},
+    {"param_enc", OPT_PARAM_ENC, 's',
+     "Specifies the way the ec parameters are encoded"},
+    {"conv_form", OPT_CONV_FORM, 's', "Specifies the point conversion form "},
+    {"genkey", OPT_GENKEY, '-', "Generate sm2 key"},
+    {"rand", OPT_RAND, 's', "Files to use for random number input"},
+    {"id", OPT_ID, 's', "SM2 key owner's identity"},
+    {"genzid", OPT_GENZID, '-', "Generate SM2 Z value from public key and identity"},
+    {"", OPT_CIPHER, '-', "Any supported cipher"},
+# ifndef OPENSSL_NO_ENGINE
+    {"engine", OPT_ENGINE, 's', "Use engine, possibly a hardware device"},
+    {"config", OPT_CONFIG, 's', "A config file"},
+# endif
+    {NULL}
+};
+
+int sm2_main(int argc, char **argv)
+{
+    BIO *in = NULL, *out = NULL;
+    ENGINE *e = NULL;
+    EC_KEY *eckey = NULL;
+    const EC_GROUP *group;
+    const EVP_CIPHER *enc = EVP_sms4_cbc();
+    point_conversion_form_t form = POINT_CONVERSION_UNCOMPRESSED;
+    char *infile = NULL, *outfile = NULL, *prog;
+    char *passin = NULL, *passout = NULL, *passinarg = NULL, *passoutarg = NULL;
+    OPTION_CHOICE o;
+    int asn1_flag = OPENSSL_EC_NAMED_CURVE, new_form = 0, new_asn1_flag = 0;
+    int informat = FORMAT_PEM, outformat = FORMAT_PEM, text = 0, noout = 0;
+    int pubin = 0, pubout = 0, param_out = 0, i, ret = 1, private = 0;
+    int no_public = 0, check = 0;
+    CONF *conf = NULL;
+    char *configfile = default_config_file;
+    int genkey = 0, need_rand = 0;
+    char *inrand = NULL;
+    int genzid = 0;
+    char *id = NULL;
+
+    prog = opt_init(argc, argv, sm2_options);
+    while ((o = opt_next()) != OPT_EOF) {
+        switch (o) {
+        case OPT_EOF:
+        case OPT_ERR:
+ opthelp:
+            BIO_printf(bio_err, "%s: Use -help for summary.\n", prog);
+            goto end;
+        case OPT_HELP:
+            opt_help(sm2_options);
+            ret = 0;
+            goto end;
+        case OPT_INFORM:
+            if (!opt_format(opt_arg(), OPT_FMT_ANY, &informat))
+                goto opthelp;
+            break;
+        case OPT_IN:
+            infile = opt_arg();
+            break;
+        case OPT_OUTFORM:
+            if (!opt_format(opt_arg(), OPT_FMT_PEMDER, &outformat))
+                goto opthelp;
+            break;
+        case OPT_OUT:
+            outfile = opt_arg();
+            break;
+        case OPT_NOOUT:
+            noout = 1;
+            break;
+        case OPT_TEXT:
+            text = 1;
+            break;
+        case OPT_PARAM_OUT:
+            param_out = 1;
+            break;
+        case OPT_PUBIN:
+            pubin = 1;
+            break;
+        case OPT_PUBOUT:
+            pubout = 1;
+            break;
+        case OPT_PASSIN:
+            passinarg = opt_arg();
+            break;
+        case OPT_PASSOUT:
+            passoutarg = opt_arg();
+            break;
+        case OPT_ENGINE:
+            e = setup_engine(opt_arg(), 0);
+            break;
+        case OPT_CIPHER:
+            if (!opt_cipher(opt_unknown(), &enc))
+                goto opthelp;
+            break;
+        case OPT_CONV_FORM:
+            if (!opt_pair(opt_arg(), conv_forms, &i))
+                goto opthelp;
+            new_form = 1;
+            form = i;
+            break;
+        case OPT_PARAM_ENC:
+            if (!opt_pair(opt_arg(), param_enc, &i))
+                goto opthelp;
+            new_asn1_flag = 1;
+            asn1_flag = i;
+            break;
+        case OPT_NO_PUBLIC:
+            no_public = 1;
+            break;
+        case OPT_CHECK:
+            check = 1;
+            break;
+        case OPT_CONFIG:
+            configfile = opt_arg();
+            break;
+        case OPT_GENKEY:
+            genkey = need_rand = 1;
+            break;
+        case OPT_RAND:
+            inrand = opt_arg();
+            need_rand = 1;
+            break;
+        case OPT_ID:
+            id = opt_arg();
+            break;
+        case OPT_GENZID:
+            genzid = 1;
+            break;
+        }
+    }
+    argc = opt_num_rest();
+    if (argc != 0)
+        goto opthelp;
+
+    BIO_printf(bio_err, "Using configuration from %s\n", configfile);
+
+    if ((conf = app_load_config(configfile)) == NULL)
+        goto end;
+    if (configfile != default_config_file && !app_load_modules(conf))
+        goto end;
+
+    private = param_out || pubin || pubout ? 0 : 1;
+    if (text && !pubin)
+        private = 1;
+
+    if (!app_passwd(passinarg, passoutarg, &passin, &passout)) {
+        BIO_printf(bio_err, "Error getting passwords\n");
+        goto end;
+    }
+
+    if (informat != FORMAT_ENGINE) {
+        in = bio_open_default(infile, 'r', informat);
+        if (in == NULL)
+            goto end;
+    }
+
+    if (need_rand) {
+        app_RAND_load_file(NULL, (inrand != NULL));
+        if (inrand != NULL)
+            BIO_printf(bio_err, "%ld semi-random bytes loaded\n",
+                       app_RAND_load_files(inrand));
+    }
+
+
+    if (genkey) {
+        BIO_printf(bio_err, "generate SM2 key\n");
+        if (!(eckey = EC_KEY_new_by_curve_name(NID_sm2p256v1))) {
+            goto end;
+        }
+        assert(need_rand);
+        if (!EC_KEY_generate_key(eckey)) {
+            BIO_printf(bio_err, "unable to generate key\n");
+            EC_KEY_free(eckey);
+            ERR_print_errors(bio_err);
+            goto end;
+        }
+    } else {
+        BIO_printf(bio_err, "read SM2 key\n");
+        if (informat == FORMAT_ASN1) {
+            if (pubin)
+                eckey = d2i_EC_PUBKEY_bio(in, NULL);
+            else
+                eckey = d2i_ECPrivateKey_bio(in, NULL);
+        } else if (informat == FORMAT_TEXT) {
+            unsigned char buf[256] = {0};
+            unsigned char *key = NULL;
+            long keylen;
+            if (!(eckey = EC_KEY_new_by_curve_name(NID_sm2p256v1))) {
+                ERR_print_errors(bio_err);
+                goto end;
+            }
+            if (BIO_read(in, buf, sizeof(buf) - 1) <= 0) {
+                ERR_print_errors(bio_err);
+                OPENSSL_cleanse(buf, sizeof(buf));
+                goto end;
+            }
+            if (!(key = OPENSSL_hexstr2buf((char *)buf, &keylen))) {
+                ERR_print_errors(bio_err);
+                OPENSSL_cleanse(buf, sizeof(buf));
+                goto end;
+            }
+            OPENSSL_cleanse(buf, sizeof(buf));
+            if (keylen != 32) {
+                BIO_printf(bio_err, "Invalid private key in hex format\n");
+		BIO_printf(bio_err, "Key length is %ld, not 32 byte\n", keylen);
+                OPENSSL_cleanse(key, keylen);
+                goto end;
+            }
+            if (!EC_KEY_oct2priv(eckey, key, keylen)) {
+                ERR_print_errors(bio_err);
+                OPENSSL_cleanse(key, keylen);
+                goto end;
+            }
+            OPENSSL_cleanse(key, keylen);
+
+            if (eckey->group->meth->keygenpub == NULL
+                || eckey->group->meth->keygenpub(eckey) == 0) {
+                BIO_printf(bio_err, "Generate public key from private key failed\n");
+                ERR_print_errors(bio_err);
+                goto end;
+            }
+
+        } else if (informat == FORMAT_ENGINE) {
+            EVP_PKEY *pkey;
+            if (pubin)
+                pkey = load_pubkey(infile, informat , 1, passin, e, "Public Key");
+            else
+                pkey = load_key(infile, informat, 1, passin, e, "Private Key");
+            if (pkey != NULL) {
+                eckey = EVP_PKEY_get1_EC_KEY(pkey);
+                EVP_PKEY_free(pkey);
+            }
+        } else {
+            if (pubin)
+                eckey = PEM_read_bio_EC_PUBKEY(in, NULL, NULL, NULL);
+            else
+                eckey = PEM_read_bio_ECPrivateKey(in, NULL, NULL, passin);
+        }
+        if (eckey == NULL) {
+            BIO_printf(bio_err, "unable to load Key\n");
+            ERR_print_errors(bio_err);
+            goto end;
+        }
+        if (!EC_KEY_is_sm2p256v1(eckey)) {
+            BIO_printf(bio_err, "curve is not sm2p256v1\n");
+            goto end;
+        }
+    }
+
+    out = bio_open_owner(outfile, outformat, private);
+    if (out == NULL)
+        goto end;
+
+    group = EC_KEY_get0_group(eckey);
+
+    if (new_form)
+        EC_KEY_set_conv_form(eckey, form);
+
+    if (new_asn1_flag)
+        EC_KEY_set_asn1_flag(eckey, asn1_flag);
+
+    if (no_public)
+        EC_KEY_set_enc_flags(eckey, EC_PKEY_NO_PUBKEY);
+
+    if (text) {
+        assert(pubin || private);
+        if (!EC_KEY_print(out, eckey, 0)) {
+            perror(outfile);
+            ERR_print_errors(bio_err);
+            goto end;
+        }
+    }
+
+    if (check) {
+        if (EC_KEY_check_key(eckey) == 1) {
+                if (EC_KEY_is_sm2p256v1(eckey)) {
+                    BIO_printf(bio_err, "SM2 Key valid.\n");
+                } else {
+                    BIO_printf(bio_err, "Not SM2 Key.\n");
+                }
+        } else {
+            BIO_printf(bio_err, "EC Key Invalid!\n");
+            ERR_print_errors(bio_err);
+        }
+    }
+
+    if (genzid) {
+        unsigned char z[64];
+        size_t zlen = sizeof(z);
+        if (!id) {
+            id = SM2_DEFAULT_ID;
+            BIO_printf(bio_err, "use default identity '%s'\n", id);
+        }
+        if (!SM2_compute_id_digest(EVP_sm3(), id, strlen(id), z, &zlen, eckey)) {
+            goto end;
+        }
+        BIO_printf(out, "id: %s\n", id);
+        BIO_puts(out, "Z:\n");
+	ASN1_buf_print(out, z, zlen, 4);
+
+
+        BIO_printf(out, "\n");
+    }
+
+    if (noout) {
+        ret = 0;
+        goto end;
+    }
+
+    BIO_printf(bio_err, "writing SM2 key\n");
+    if (outformat == FORMAT_ASN1) {
+        if (param_out)
+            i = i2d_ECPKParameters_bio(out, group);
+        else if (pubin || pubout)
+            i = i2d_EC_PUBKEY_bio(out, eckey);
+        else {
+            assert(private);
+            i = i2d_ECPrivateKey_bio(out, eckey);
+        }
+    } else {
+        if (param_out)
+            i = PEM_write_bio_ECPKParameters(out, group);
+        else if (pubin || pubout)
+            i = PEM_write_bio_EC_PUBKEY(out, eckey);
+        else {
+            assert(private);
+            //FIXME: use PKCS#8				
+            i = PEM_write_bio_ECPrivateKey(out, eckey, enc,
+                                           NULL, 0, NULL, passout);
+
+        }
+    }
+
+    if (!i) {
+        BIO_printf(bio_err, "unable to write private key\n");
+        ERR_print_errors(bio_err);
+    } else
+        ret = 0;
+
+ end:
+    BIO_free(in);
+    BIO_free_all(out);
+    EC_KEY_free(eckey);
+    release_engine(e);
+    OPENSSL_free(passin);
+    OPENSSL_free(passout);
+    NCONF_free(conf);
+    return (ret);
+}
+
+#endif
diff --git a/apps/sm2utl.c b/apps/sm2utl.c
new file mode 100644
index 000000000..968da64d5
--- /dev/null
+++ b/apps/sm2utl.c
@@ -0,0 +1,474 @@
+/* ====================================================================
+ * Copyright (c) 2014 - 2018 The GmSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the GmSSL Project.
+ *    (http://gmssl.org/)"
+ *
+ * 4. The name "GmSSL Project" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    guanzhi1980@gmail.com.
+ *
+ * 5. Products derived from this software may not be called "GmSSL"
+ *    nor may "GmSSL" appear in their names without prior written
+ *    permission of the GmSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the GmSSL Project
+ *    (http://gmssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE GmSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE GmSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+
+#include 
+#ifdef OPENSSL_NO_SM2
+NON_EMPTY_TRANSLATION_UNIT
+#else
+
+# include 
+# include 
+# include 
+# include 
+# include 
+# include 
+# include 
+# include "../crypto/sm2/sm2_lcl.h"
+# include "apps.h"
+
+
+# define OP_UNDEF	0
+# define OP_DGST	1
+# define OP_SIGN	2
+# define OP_VERIFY	3
+# define OP_ENCRYPT	4
+# define OP_DECRYPT	5
+
+# define KEY_NONE	0
+# define KEY_PRIVKEY	1
+# define KEY_PUBKEY	2
+# define KEY_CERT	3
+
+typedef enum OPTION_choice {
+	OPT_ERR = -1, OPT_EOF = 0, OPT_HELP,
+	OPT_IN,
+	OPT_OUT,
+	OPT_DGST,
+	OPT_SIGN,
+	OPT_VERIFY,
+	OPT_ENCRYPT,
+	OPT_DECRYPT,
+	OPT_ID,
+	OPT_SIGFILE,
+	OPT_INKEY,
+	OPT_PUBIN,
+	OPT_CERTIN,
+	OPT_PASSIN,
+	OPT_KEYFORM,
+	OPT_MD,
+	OPT_ENGINE,
+	OPT_ENGINE_IMPL,
+	OPT_CONFIG
+} OPTION_CHOICE;
+
+OPTIONS sm2utl_options[] = {
+	{"help", OPT_HELP, '-', "Display this summary"},
+	{"in", OPT_IN, '<', "Input file - default stdin"},
+	{"out", OPT_OUT, '>', "Output file - default stdout"},
+	{"dgst", OPT_DGST, '-', "Generate input data digest with Z value"},
+	{"sign", OPT_SIGN, '-', "Sign input data with private key and public parameters"},
+	{"verify", OPT_VERIFY, '-', "Verify with signer's ID and public parameters"},
+	{"encrypt", OPT_ENCRYPT, '-', "Encrypt input data with recipient's ID"},
+	{"decrypt", OPT_DECRYPT, '-', "Decrypt input data with private key"},
+	{"id", OPT_ID, 's', "Identity for Z value"},
+	{"sigfile", OPT_SIGFILE, '<', "Signature file (verify operation only)"},
+	{"inkey", OPT_INKEY, 's', "Private key for signing or decryption"},
+	{"pubin", OPT_PUBIN, '-', "Input is a public key"},
+	{"certin", OPT_CERTIN, '-', "Input is a cert with a public key"},
+	{"passin", OPT_PASSIN, 's', "Input file pass phrase source"},
+	{"keyform", OPT_KEYFORM, 'E', "Private key format - default PEM"},
+	{"", OPT_MD, '-', "Any supported digest"},
+# ifndef OPENSSL_NO_ENGINE
+	{"engine", OPT_ENGINE, 's', "Use engine, possibly a hardware device"},
+	{"engine_impl", OPT_ENGINE_IMPL, '-', "Also use engine given by -engine for crypto operations"},
+	{"config", OPT_CONFIG, 's', "A config file"},
+# endif
+	{NULL}
+};
+
+static int sm2utl_sign(const EVP_MD *md, BIO *in, BIO *out, const char *id,
+	ENGINE *e, EC_KEY *key, int sign);
+static int sm2utl_verify(const EVP_MD *md, BIO *in, BIO *out, BIO *sig,
+	const char *id, ENGINE *e, EC_KEY *ec_key);
+static int sm2utl_encrypt(const EVP_MD *md, BIO *in, BIO *out, EC_KEY *ec_key);
+static int sm2utl_decrypt(const EVP_MD *md, BIO *in, BIO *out, EC_KEY *ec_key);
+
+int sm2utl_main(int argc, char **argv)
+{
+	int ret = 1;
+	OPTION_CHOICE o;
+	char *prog;
+	char *infile = NULL, *outfile = NULL, *sigfile = NULL;
+	BIO *in = NULL, *out = NULL, *sig = NULL;
+	int op = OP_UNDEF;
+	char *id = NULL;
+	char *keyfile = NULL;
+	int key_type = KEY_PRIVKEY;
+	char *passinarg = NULL;
+	char *passin = NULL;
+	int keyform = FORMAT_PEM;
+	const EVP_MD *md = EVP_sm3(), *m;
+# ifndef OPENSSL_NO_ENGINE
+	ENGINE *e = NULL;
+	CONF *conf = NULL;
+	char *configfile = default_config_file;
+# endif
+	int engine_impl = 0;
+	EVP_PKEY *pkey = NULL;
+	EC_KEY *ec_key;
+
+	prog = opt_init(argc, argv, sm2utl_options);
+	while ((o = opt_next()) != OPT_EOF) {
+		switch (o) {
+		case OPT_EOF:
+		case OPT_ERR:
+opthelp:
+			BIO_printf(bio_err, "%s: Use -help for summary.\n", prog);
+			goto end;
+		case OPT_HELP:
+			opt_help(sm2utl_options);
+			ret = 0;
+			goto end;
+		case OPT_IN:
+			infile = opt_arg();
+			break;
+		case OPT_OUT:
+			outfile = opt_arg();
+			break;
+		case OPT_DGST:
+			op = OP_DGST;
+			break;
+		case OPT_SIGN:
+			op = OP_SIGN;
+			break;
+		case OPT_VERIFY:
+			op = OP_VERIFY;
+			break;
+		case OPT_ENCRYPT:
+			op = OP_ENCRYPT;
+			break;
+		case OPT_DECRYPT:
+			op = OP_DECRYPT;
+			break;
+		case OPT_ID:
+			id = opt_arg();
+			break;
+		case OPT_SIGFILE:
+			sigfile = opt_arg();
+			break;
+		case OPT_INKEY:
+			keyfile = opt_arg();
+			break;
+		case OPT_PUBIN:
+			key_type = KEY_PUBKEY;
+			break;
+		case OPT_CERTIN:
+			key_type = KEY_CERT;
+			break;
+		case OPT_PASSIN:
+			passinarg = opt_arg();
+			break;
+		case OPT_KEYFORM:
+			if (!opt_format(opt_arg(), OPT_FMT_PDE, &keyform))
+				goto opthelp;
+			break;
+		case OPT_ENGINE:
+			e = setup_engine(opt_arg(), 0);
+			break;
+		case OPT_ENGINE_IMPL:
+			engine_impl = 1;
+			break;
+		case OPT_CONFIG:
+			configfile = opt_arg();
+			break;
+		case OPT_MD:
+			if (!opt_md(opt_unknown(), &m))
+				goto opthelp;
+			md = m;
+			break;
+		}
+	}
+	argc = opt_num_rest();
+	if (argc != 0)
+		goto opthelp;
+
+# ifndef OPENSSL_NO_ENGINE
+	if (e)
+		BIO_printf(bio_err, "Using configuration from %s\n", configfile);
+
+	if ((conf = app_load_config(configfile)) == NULL)
+		goto end;
+	if (configfile != default_config_file && !app_load_modules(conf))
+		goto end;
+# endif
+
+	in = bio_open_default(infile, 'r', FORMAT_BINARY);
+	if (!in)
+		goto end;
+
+	out = bio_open_default(outfile, 'w', FORMAT_BINARY);
+	if (!out)
+		goto end;
+
+	if (sigfile) {
+		sig = BIO_new_file(sigfile, "rb");
+		if (!sig) {
+			BIO_printf(bio_err, "Can't open signature file %s\n", sigfile);
+			goto end;
+		}
+	}
+
+	app_RAND_load_file(NULL, 0);
+
+	switch (key_type) {
+	case KEY_PRIVKEY:
+		if (!(pkey = load_key(keyfile, keyform, 0, passin, e, "Private Key"))) {
+			ERR_print_errors(bio_err);
+			goto end;
+		}
+		break;
+
+	case KEY_PUBKEY:
+		if (!(pkey = load_pubkey(keyfile, keyform, 0, NULL, e, "Public Key"))) {
+			ERR_print_errors(bio_err);
+			goto end;
+		}
+		break;
+
+	case KEY_CERT:
+		{
+			X509 *x = load_cert(keyfile, keyform, "Certificate");
+			if (x) {
+				pkey = X509_get_pubkey(x);
+				X509_free(x);
+			}
+		}
+		break;
+	}
+
+	if (!(ec_key = EVP_PKEY_get0_EC_KEY(pkey))
+		|| !EC_KEY_is_sm2p256v1(ec_key)) {
+		BIO_printf(bio_err, "Invalid key type\n");
+		goto end;
+	}
+
+	switch (op) {
+	case OP_DGST:
+	case OP_SIGN:
+	case OP_VERIFY:
+		if (!id) {
+			BIO_printf(bio_err, "Option '-id' required\n");
+			goto end;
+		}
+		break;
+	}
+
+	switch (op) {
+	case OP_DGST:
+		return sm2utl_sign(md, in, out, id, e, ec_key, 0);
+	case OP_SIGN:
+		return sm2utl_sign(md, in, out, id, e, ec_key, 1);
+	case OP_VERIFY:
+		return sm2utl_verify(md, in, out, sig, id, e, ec_key);
+	case OP_ENCRYPT:
+		return sm2utl_encrypt(md, in, out, ec_key);
+	case OP_DECRYPT:
+		return sm2utl_decrypt(md, in, out, ec_key);
+	default:
+		BIO_printf(bio_err, "Cryptographic operation not specified\n");
+		goto end;
+	}
+
+
+end:
+	BIO_free(in);
+	BIO_free(out);
+	BIO_free(sig);
+	OPENSSL_free(passin);
+	return ret;
+}
+static int sm2utl_sign(const EVP_MD *md, BIO *in, BIO *out, const char *id,
+	ENGINE *e, EC_KEY *ec_key, int sign)
+{
+	int ret = 0;
+	EVP_MD_CTX *md_ctx = NULL;
+	ECDSA_SIG *sig = NULL;
+	unsigned char buf[1024];
+	size_t siz = sizeof(buf);
+	unsigned int ulen = sizeof(buf);
+	int len;
+
+	if (!(md_ctx = EVP_MD_CTX_new())
+		|| !EVP_DigestInit_ex(md_ctx, md, e)
+		|| !SM2_compute_id_digest(md, id, strlen(id), buf, &siz, ec_key)
+		|| !EVP_DigestUpdate(md_ctx, buf, siz)) {
+		ERR_print_errors(bio_err);
+		goto end;
+	}
+	while ((len = BIO_read(in, buf, sizeof(buf))) > 0) {
+		if (!EVP_DigestUpdate(md_ctx, buf, len)) {
+			ERR_print_errors(bio_err);
+			goto end;
+		}
+	}
+	if (!EVP_DigestFinal_ex(md_ctx, buf, &ulen)) {
+		ERR_print_errors(bio_err);
+		goto end;
+	}
+	len = (int)ulen;
+
+	if (sign) {
+		unsigned char *p = buf;
+		if (!(sig = SM2_do_sign(buf, len, ec_key))
+			|| (len = i2d_ECDSA_SIG(sig, &p)) <= 0) {
+			ERR_print_errors(bio_err);
+			goto end;
+		}
+	}
+	if (BIO_write(out, buf, len) != len) {
+		ERR_print_errors(bio_err);
+		goto end;
+	}
+
+	ret = 1;
+end:
+	EVP_MD_CTX_free(md_ctx);
+	ECDSA_SIG_free(sig);
+	return ret;
+}
+
+static int sm2utl_verify(const EVP_MD *md, BIO *in, BIO *out, BIO *sig,
+	const char *id, ENGINE *e, EC_KEY *ec_key)
+{
+	int ret = 0;
+	EVP_MD_CTX *md_ctx = NULL;
+	unsigned char *sigbuf = NULL;
+	unsigned char buf[1024];
+	size_t siz = sizeof(buf);
+	unsigned int ulen = sizeof(buf);
+	int siglen, len;
+
+	siglen = bio_to_mem(&sigbuf, 256, sig);
+	if (siglen < 0) {
+		BIO_printf(bio_err, "Error reading signature data\n");
+		goto end;
+	}
+
+	if (!(md_ctx = EVP_MD_CTX_new())
+		|| !EVP_DigestInit_ex(md_ctx, md, e)
+		|| !SM2_compute_id_digest(md, id, strlen(id), buf, &siz, ec_key)
+		|| !EVP_DigestUpdate(md_ctx, buf, siz)) {
+		ERR_print_errors(bio_err);
+		goto end;
+	}
+	while ((len = BIO_read(in, buf, sizeof(buf))) > 0) {
+		if (!EVP_DigestUpdate(md_ctx, buf, len)) {
+			ERR_print_errors(bio_err);
+			goto end;
+		}
+	}
+	siz = sizeof(buf);
+	if (!EVP_DigestFinal_ex(md_ctx, buf, &ulen)) {
+		ERR_print_errors(bio_err);
+		goto end;
+	}
+	/* SM2_verify() can check no suffix on signature */
+	ret = SM2_verify(NID_undef, buf, ulen, sigbuf, siglen, ec_key);
+	if (ret == 1) {
+		BIO_puts(out, "Signature Verification Successful\n");
+	} else {
+		BIO_puts(out, "Signature Verification Failure\n");
+		ret = 0;
+	}
+
+end:
+	OPENSSL_free(sigbuf);
+	EVP_MD_CTX_free(md_ctx);
+	return ret;
+}
+
+static int sm2utl_encrypt(const EVP_MD *md, BIO *in, BIO *out, EC_KEY *ec_key)
+{
+	int ret = 0;
+	SM2CiphertextValue *cval = NULL;
+	unsigned char *buf = NULL;
+	int len;
+
+	if (!(len = bio_to_mem(&buf, SM2_MAX_PLAINTEXT_LENGTH, in))) {
+		ERR_print_errors(bio_err);
+		BIO_printf(bio_err, "Error reading plaintext\n");
+		goto end;
+	}
+	if (!(cval = SM2_do_encrypt(md, buf, len, ec_key))
+		|| i2d_SM2CiphertextValue_bio(out, cval) <= 0) {
+		ERR_print_errors(bio_err);
+		goto end;
+	}
+
+	ret = 1;
+
+end:
+	OPENSSL_free(buf);
+	SM2CiphertextValue_free(cval);
+	return ret;
+}
+
+static int sm2utl_decrypt(const EVP_MD *md, BIO *in, BIO *out, EC_KEY *ec_key)
+{
+	int ret = 0;
+	SM2CiphertextValue *cval = NULL;
+	unsigned char *buf = NULL;
+	size_t siz;
+
+	if (!(cval = d2i_SM2CiphertextValue_bio(in, NULL))
+		|| !SM2_do_decrypt(md, cval, NULL, &siz, ec_key)
+		|| !(buf = OPENSSL_malloc(siz))
+		|| !SM2_do_decrypt(md, cval, buf, &siz, ec_key)
+		|| BIO_write(out, buf, siz) != siz) {
+		ERR_print_errors(bio_err);
+		goto end;
+	}
+	ret = 1;
+
+end:
+	SM2CiphertextValue_free(cval);
+	OPENSSL_free(buf);
+	return ret;
+}
+#endif
diff --git a/apps/sm9.c b/apps/sm9.c
new file mode 100644
index 000000000..71e7f2940
--- /dev/null
+++ b/apps/sm9.c
@@ -0,0 +1,211 @@
+/* ====================================================================
+ * Copyright (c) 2014 - 2017 The GmSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the GmSSL Project.
+ *    (http://gmssl.org/)"
+ *
+ * 4. The name "GmSSL Project" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    guanzhi1980@gmail.com.
+ *
+ * 5. Products derived from this software may not be called "GmSSL"
+ *    nor may "GmSSL" appear in their names without prior written
+ *    permission of the GmSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the GmSSL Project
+ *    (http://gmssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE GmSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE GmSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+
+#include 
+#ifdef OPENSSL_NO_SM9
+NON_EMPTY_TRANSLATION_UNIT
+#else
+
+# include 
+# include 
+# include 
+# include 
+# include 
+# include 
+# include 
+# include 
+# include "apps.h"
+
+typedef enum OPTION_choice {
+	OPT_ERR = -1, OPT_EOF = 0, OPT_HELP,
+	OPT_IN, OPT_OUT, OPT_INFORM, OPT_OUTFORM,
+	OPT_PASSIN, OPT_PASSOUT, OPT_TEXT, OPT_NOOUT
+} OPTION_CHOICE;
+
+OPTIONS sm9_options[] = {
+	{"help", OPT_HELP, '-', "Display this summary"},
+	{"in", OPT_IN, 's', "Input key"},
+	{"out", OPT_OUT, '>', "Output file"},
+	{"inform", OPT_INFORM, 'f', "Input format (DER or PEM)"},
+	{"outform", OPT_OUTFORM, 'F', "Output format (DER or PEM)"},
+	{"passin", OPT_PASSIN, 's', "Input file pass phrase source"},
+	{"passout", OPT_PASSOUT, 's', "Output file pass phrase source"},
+	{"text", OPT_TEXT, '-', "Output in plaintext as well"},
+	{"noout", OPT_NOOUT, '-', "Don't output the key"},
+	{NULL}
+};
+
+int sm9_main(int argc, char **argv)
+{
+	int ret = 1;
+	SM9_KEY *key = NULL;
+	BIO *in = NULL, *out = NULL;
+	char *infile = NULL, *outfile = NULL, *prog;
+	char *passin = NULL, *passout = NULL;
+	char *passinarg = NULL, *passoutarg = NULL;
+	OPTION_CHOICE o;
+	int informat = FORMAT_PEM, outformat = FORMAT_PEM;
+	int text = 0, noout = 0;
+
+	prog = opt_init(argc, argv, sm9_options);
+	while ((o = opt_next()) != OPT_EOF) {
+		switch (o) {
+		case OPT_EOF:
+		case OPT_ERR:
+opthelp:
+			BIO_printf(bio_err, "%s: Use -help for summary.\n", prog);
+			goto end;
+		case OPT_HELP:
+			opt_help(sm9_options);
+			ret = 0;
+			goto end;
+		case OPT_IN:
+			infile = opt_arg();
+			break;
+		case OPT_OUT:
+			outfile = opt_arg();
+			break;
+		case OPT_INFORM:
+			if (!opt_format(opt_arg(), OPT_FMT_ANY, &informat))
+				goto opthelp;
+			break;
+		case OPT_OUTFORM:
+			if (!opt_format(opt_arg(), OPT_FMT_PEMDER, &outformat))
+				goto opthelp;
+			break;
+		case OPT_PASSIN:
+			passinarg = opt_arg();
+			break;
+		case OPT_PASSOUT:
+			passoutarg = opt_arg();
+			break;
+		case OPT_TEXT:
+			text = 1;
+			break;
+		case OPT_NOOUT:
+			noout = 1;
+			break;
+		}
+	}
+	argc = opt_num_rest();
+	if (argc != 0)
+		goto opthelp;
+
+	if (!app_passwd(passinarg, passoutarg, &passin, &passout)) {
+		BIO_printf(bio_err, "Error getting passwords\n");
+		goto end;
+	}
+
+	if (informat != FORMAT_ENGINE) {
+		if (!(in = bio_open_default(infile, 'r', informat)))
+			goto end;
+	}
+
+	BIO_printf(bio_err, "read SM9 key\n");
+	if (informat == FORMAT_ASN1) {
+		if (!(key = d2i_SM9PrivateKey_bio(in, NULL))) {
+			BIO_printf(bio_err, "unable to load Key\n");
+			ERR_print_errors(bio_err);
+			goto end;
+		}
+	} else if (informat == FORMAT_PEM) {
+		if (!(key = PEM_read_bio_SM9PrivateKey(in, NULL, NULL, passin))) {
+			BIO_printf(bio_err, "unable to load Key\n");
+			ERR_print_errors(bio_err);
+			goto end;
+		}
+	} else {
+		BIO_printf(bio_err, "supported key format\n");
+		goto end;
+	}
+
+	if (!(out = bio_open_owner(outfile, outformat, 1))) {
+		goto end;
+	}
+
+	if (!noout) {
+		BIO_printf(bio_err, "writing SM9 key\n");
+		if (outformat == FORMAT_ASN1) {
+			if (!i2d_SM9PrivateKey_bio(out, key)) {
+				BIO_printf(bio_err, "unable to write private key\n");
+				ERR_print_errors(bio_err);
+				goto end;
+			}
+		} else if (outformat == FORMAT_PEM) {
+			if (!PEM_write_bio_SM9PrivateKey(out, key, EVP_sms4_cbc(), NULL, 0, NULL, passout)) {
+				BIO_printf(bio_err, "unable to write private key\n");
+				ERR_print_errors(bio_err);
+				goto end;
+			}
+		} else {
+			BIO_printf(bio_err, "unsupported key format\n");
+			goto end;
+		}
+	}
+
+	/* sm9 api does not support public key print, do it with evp api */
+	if (text) {
+		if (!SM9_KEY_print(out, key, 0)) {
+			perror(outfile);
+			ERR_print_errors(bio_err);
+			goto end;
+		}
+	}
+
+	ret = 0;
+
+end:
+	SM9_KEY_free(key);
+	BIO_free(in);
+	BIO_free_all(out);
+	OPENSSL_free(passin);
+	OPENSSL_free(passout);
+	return ret;
+}
+#endif
diff --git a/apps/sm9param.c b/apps/sm9param.c
new file mode 100644
index 000000000..37edbf4c1
--- /dev/null
+++ b/apps/sm9param.c
@@ -0,0 +1,81 @@
+/* ====================================================================
+ * Copyright (c) 2014 - 2018 The GmSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the GmSSL Project.
+ *    (http://gmssl.org/)"
+ *
+ * 4. The name "GmSSL Project" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    guanzhi1980@gmail.com.
+ *
+ * 5. Products derived from this software may not be called "GmSSL"
+ *    nor may "GmSSL" appear in their names without prior written
+ *    permission of the GmSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the GmSSL Project
+ *    (http://gmssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE GmSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE GmSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+
+#include 
+#ifdef OPENSSL_NO_SM9
+NON_EMPTY_TRANSLATION_UNIT
+#else
+
+# include 
+# include 
+# include 
+# include 
+# include 
+# include 
+# include 
+# include 
+# include "apps.h"
+
+typedef enum OPTION_choice {
+	OPT_ERR = -1,
+	OPT_EOF = 0,
+	OPT_HELP
+} OPTION_CHOICE;
+
+OPTIONS sm9param_options[] = {
+	{"help", OPT_HELP, '-', "Display this summary"},
+	{NULL}
+};
+
+int sm9param_main(int argc, char **argv)
+{
+	printf("sm9param not implemented\n");
+	return 0;
+}
+#endif
diff --git a/apps/sm9utl.c b/apps/sm9utl.c
new file mode 100644
index 000000000..13da20ee3
--- /dev/null
+++ b/apps/sm9utl.c
@@ -0,0 +1,418 @@
+/* ====================================================================
+ * Copyright (c) 2014 - 2018 The GmSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the GmSSL Project.
+ *    (http://gmssl.org/)"
+ *
+ * 4. The name "GmSSL Project" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    guanzhi1980@gmail.com.
+ *
+ * 5. Products derived from this software may not be called "GmSSL"
+ *    nor may "GmSSL" appear in their names without prior written
+ *    permission of the GmSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the GmSSL Project
+ *    (http://gmssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE GmSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE GmSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+
+#include 
+#ifdef OPENSSL_NO_SM9
+NON_EMPTY_TRANSLATION_UNIT
+#else
+
+# include 
+# include 
+# include 
+# include 
+# include 
+# include 
+# include 
+# include "../crypto/sm9/sm9_lcl.h"
+# include "apps.h"
+
+typedef enum OPTION_choice {
+	OPT_ERR = -1, OPT_EOF = 0, OPT_HELP,
+	OPT_IN, OPT_OUT, OPT_SIGFILE,
+	OPT_SIGN, OPT_VERIFY, OPT_ENCRYPT, OPT_DECRYPT,
+	OPT_MD, OPT_SCHEME,
+	OPT_PARAMFILE, OPT_ID, OPT_INKEY, OPT_PASSIN,
+	OPT_KEYFORM, OPT_PARAMFORM
+} OPTION_CHOICE;
+
+OPTIONS sm9utl_options[] = {
+	{"help", OPT_HELP, '-', "Display this summary"},
+	{"in", OPT_IN, '<', "Input file - default stdin"},
+	{"out", OPT_OUT, '>', "Output file - default stdout"},
+	{"sigfile", OPT_SIGFILE, '<', "Signature file (verify operation only)"},
+	{"sign", OPT_SIGN, '-', "Sign input data with private key and public parameters"},
+	{"verify", OPT_VERIFY, '-', "Verify with signer's ID and public parameters"},
+	{"encrypt", OPT_ENCRYPT, '-', "Encrypt input data with recipient's ID"},
+	{"decrypt", OPT_DECRYPT, '-', "Decrypt input data with private key"},
+	{"md", OPT_MD, 's', "Digest algorithm for signing or verification"},
+	{"scheme", OPT_SCHEME, 's', "Encryption scheme"},
+	{"paramfile", OPT_PARAMFILE, 's', "Public parameters file"},
+	{"id", OPT_ID, 's', "Recipient's or signer's ID"},
+	{"inkey", OPT_INKEY, 's', "Private key for signing or decryption"},
+	{"passin", OPT_PASSIN, 's', "Input file pass phrase source"},
+	{"keyform", OPT_KEYFORM, 'E', "Private key format - default PEM"},
+	{"paramform", OPT_PARAMFORM, 'E', "Public parameters format - default PEM"},
+	{NULL}
+};
+
+static int sm9_sign(const EVP_MD *md, BIO *in, BIO *out,
+	SM9_KEY *key, const char *prog)
+{
+	int ret = 0;
+	EVP_MD_CTX *md_ctx = NULL;
+	SM9Signature *sig = NULL;
+	unsigned char buf[1024];
+	int len;
+
+	if (!(md_ctx = EVP_MD_CTX_new())
+		|| !SM9_SignInit(md_ctx, md, NULL)) {
+		ERR_print_errors(bio_err);
+		goto end;
+	}
+	while ((len = BIO_read(in, buf, sizeof(buf))) > 0) {
+		if (!SM9_SignUpdate(md_ctx, buf, len)) {
+			ERR_print_errors(bio_err);
+			goto end;
+		}
+	}
+	if (!(sig = SM9_SignFinal(md_ctx, key))) {
+		ERR_print_errors(bio_err);
+		goto end;
+}
+	if (i2d_SM9Signature_bio(out, sig) <= 0) {
+		ERR_print_errors(bio_err);
+		goto end;
+	}
+
+	ret = 1;
+
+end:
+	EVP_MD_CTX_free(md_ctx);
+	SM9Signature_free(sig);
+	return ret;
+}
+
+static int sm9_verify(const EVP_MD *md, BIO *in, BIO *out, const char *sigfile,
+	SM9_MASTER_KEY *param, const char *id, const char *prog)
+{
+	int ret = 0;
+	BIO *sig_bio = NULL;
+	SM9_KEY *key = NULL;
+	EVP_MD_CTX *md_ctx = NULL;
+	SM9Signature *sig = NULL;
+	unsigned char buf[1024];
+	int len;
+
+	if (!sigfile) {
+		BIO_printf(bio_err, "%s: `-sigfile` required for verification\n", prog);
+		return 0;
+	}
+	if (!(sig_bio = BIO_new_file(sigfile, "rb"))
+		|| !(sig = d2i_SM9Signature_bio(sig_bio, NULL))) {
+		ERR_print_errors(bio_err);
+		goto end;
+	}
+
+	if (!param || !id) {
+		BIO_printf(bio_err, "%s: param and id required\n", prog);
+		goto end;
+	}
+	if (!(key = SM9_extract_public_key(param, id, strlen(id)))) {
+		ERR_print_errors(bio_err);
+		goto end;
+	}
+
+	if (!(md_ctx = EVP_MD_CTX_new())
+		|| !SM9_VerifyInit(md_ctx, md, NULL)) {
+		ERR_print_errors(bio_err);
+		goto end;
+	}
+	while ((len = BIO_read(in, buf, sizeof(buf))) > 0) {
+		if (!SM9_VerifyUpdate(md_ctx, buf, len)) {
+			ERR_print_errors(bio_err);
+			goto end;
+		}
+	}
+	if ((ret = SM9_VerifyFinal(md_ctx, sig, key)) != 1) {
+		ERR_print_errors(bio_err);
+	}
+	BIO_printf(out, "Signature Verified %s\n", ret ? "Successfully" : "Failure");
+
+end:
+	BIO_free(sig_bio);
+	SM9_KEY_free(key);
+	EVP_MD_CTX_free(md_ctx);
+	SM9Signature_free(sig);
+	return ret;
+}
+
+static int sm9_encrypt(int scheme, BIO *in, BIO *out,
+	SM9_MASTER_KEY *param, const char *id, const char *prog)
+{
+	int ret = 0;
+	unsigned char *inbuf = NULL;
+	unsigned char *outbuf = NULL;
+	int inlen;
+	size_t outlen;
+
+	if (!param || !id) {
+		BIO_printf(bio_err, "%s: param and id required\n", prog);
+		return 0;
+	}
+
+	if (!(inlen = bio_to_mem(&inbuf, SM9_MAX_PLAINTEXT_LENGTH, in))) {
+		BIO_printf(bio_err, "%s: error reading input\n", prog);
+		return 0;
+	}
+
+	if (!SM9_encrypt(scheme, inbuf, inlen, NULL, &outlen, param, id, strlen(id))
+		|| !(outbuf = OPENSSL_malloc(outlen))
+		|| !SM9_encrypt(scheme, inbuf, inlen, outbuf, &outlen,
+			param, id, strlen(id))
+		|| BIO_write(out, outbuf, outlen) != outlen) {
+		ERR_print_errors(bio_err);
+		goto end;
+	}
+
+	ret = 1;
+
+end:
+	OPENSSL_free(inbuf);
+	OPENSSL_free(outbuf);
+	return ret;
+}
+
+static int sm9_decrypt(int scheme, BIO *in, BIO *out, SM9_KEY *key, char *prog)
+{
+	int ret = 0;
+	unsigned char *inbuf = NULL;
+	unsigned char *outbuf = NULL;
+	int inlen;
+	size_t outlen;
+
+	if (!key) {
+		BIO_printf(bio_err, "%s: private key required\n", prog);
+		goto end;
+	}
+
+	if (!(inlen = bio_to_mem(&inbuf, SM9_MAX_CIPHERTEXT_LENGTH, in))) {
+		BIO_printf(bio_err, "%s: error reading input\n", prog);
+		goto end;
+	}
+
+	if (!SM9_decrypt(scheme, inbuf, inlen, NULL, &outlen, key)
+		|| !(outbuf = OPENSSL_malloc(outlen))
+		|| !SM9_decrypt(scheme, inbuf, inlen, outbuf, &outlen, key)
+		|| BIO_write(out, outbuf, outlen) != outlen) {
+		ERR_print_errors(bio_err);
+		goto end;
+	}
+
+	ret = 1;
+
+end:
+	OPENSSL_free(inbuf);
+	OPENSSL_free(outbuf);
+	return ret;
+}
+
+int sm9utl_main(int argc, char **argv)
+{
+	int ret = 1;
+	OPTION_CHOICE o;
+	char *prog;
+	char *infile = NULL;
+	char *outfile = NULL;
+	char *sigfile = NULL;
+	BIO *in = NULL;
+	BIO *out = NULL;
+	int op = EVP_PKEY_OP_UNDEFINED;
+	char *paramfile = NULL;
+	char *id = NULL;
+	char *keyfile = NULL;
+	char *passinarg = NULL;
+	int keyform = FORMAT_PEM;
+	int paramform = FORMAT_PEM;
+	char *dgst = NULL;
+	char *scheme = NULL;
+	const EVP_MD *md = EVP_sm3();
+	int enc_scheme = NID_sm9encrypt_with_sm3_xor;
+	char *passin = NULL;
+
+	EVP_PKEY *pkey = NULL;
+	SM9_MASTER_KEY *param;
+	SM9_KEY *key;
+
+	prog = opt_init(argc, argv, sm9utl_options);
+	while ((o = opt_next()) != OPT_EOF) {
+		switch (o) {
+		case OPT_EOF:
+		case OPT_ERR:
+opthelp:
+			BIO_printf(bio_err, "%s: Use -help for summary.\n", prog);
+			goto end;
+		case OPT_HELP:
+			opt_help(sm9utl_options);
+			ret = 0;
+			goto end;
+		case OPT_IN:
+			infile = opt_arg();
+			break;
+		case OPT_OUT:
+			outfile = opt_arg();
+			break;
+		case OPT_SIGFILE:
+			sigfile = opt_arg();
+			break;
+		case OPT_SIGN:
+			op = EVP_PKEY_OP_SIGN;
+			break;
+		case OPT_VERIFY:
+			op = EVP_PKEY_OP_VERIFY;
+			break;
+		case OPT_ENCRYPT:
+			op = EVP_PKEY_OP_ENCRYPT;
+			break;
+		case OPT_DECRYPT:
+			op = EVP_PKEY_OP_DECRYPT;
+			break;
+		case OPT_MD:
+			dgst = opt_arg();
+			break;
+		case OPT_SCHEME:
+			scheme = opt_arg();
+			break;
+		case OPT_PARAMFILE:
+			paramfile = opt_arg();
+			break;
+		case OPT_ID:
+			id = opt_arg();
+			break;
+		case OPT_INKEY:
+			keyfile = opt_arg();
+			break;
+		case OPT_PASSIN:
+			passinarg = opt_arg();
+			break;
+		case OPT_KEYFORM:
+			if (!opt_format(opt_arg(), OPT_FMT_PDE, &keyform))
+				goto opthelp;
+			break;
+		case OPT_PARAMFORM:
+			if (!opt_format(opt_arg(), OPT_FMT_PDE, ¶mform))
+				goto opthelp;
+			break;
+		}
+	}
+	argc = opt_num_rest();
+	if (argc != 0)
+		goto opthelp;
+
+	app_RAND_load_file(NULL, 0);
+
+
+	if (op == EVP_PKEY_OP_SIGN || op == EVP_PKEY_OP_DECRYPT) {
+
+		if (!keyfile) {
+			BIO_printf(bio_err, "Private key required\n");
+			goto end;
+		}
+
+		if (paramfile || id) {
+			BIO_printf(bio_err, "Parameters and ID not required\n");
+			goto end;
+		}
+
+		if (!app_passwd(passinarg, NULL, &passin, NULL)) {
+			BIO_printf(bio_err, "Error getting password\n");
+			goto end;
+		}
+
+		if (keyfile) {
+			pkey = load_key(keyfile, keyform, 0, passin, NULL, "Private Key");
+			if (!(key = EVP_PKEY_get0_SM9(pkey))) {
+				ERR_print_errors(bio_err);
+				goto end;
+			}
+		}
+
+		if (op == EVP_PKEY_OP_SIGN)
+			return sm9_sign(md, in, out, key, prog);
+		else
+			return sm9_decrypt(enc_scheme, in, out, key, prog);
+
+	} else if (op == EVP_PKEY_OP_VERIFY || op == EVP_PKEY_OP_ENCRYPT) {
+
+		if (!paramfile || !id) {
+			BIO_printf(bio_err, "Parameters and ID required\n");
+			goto end;
+		}
+
+		if (keyfile) {
+			BIO_printf(bio_err, "Private key not required\n");
+			goto end;
+		}
+
+		pkey = load_pubkey(keyfile, keyform,  0, NULL, NULL, "Public Key");
+		if (!(param = EVP_PKEY_get0_SM9_MASTER(pkey))) {
+			ERR_print_errors(bio_err);
+			goto end;
+		}
+
+		if (op == EVP_PKEY_OP_VERIFY) {
+			if (!sigfile) {
+				BIO_printf(bio_err, "Signature file required\n");
+				goto end;
+			}
+			return sm9_verify(md, in, out, sigfile, param, id, prog);
+		} else {
+			return sm9_encrypt(enc_scheme, in, out, param, id, prog);
+		}
+
+	} else {
+		BIO_printf(bio_err, "%s: operation not assigned\n", prog);
+		goto end;
+	}
+
+
+end:
+	OPENSSL_free(passin);
+	return ret;
+}
+#endif
diff --git a/apps/smime.c b/apps/smime.c
new file mode 100644
index 000000000..caa9252e2
--- /dev/null
+++ b/apps/smime.c
@@ -0,0 +1,656 @@
+/*
+ * Copyright 1999-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/* S/MIME utility function */
+
+#include 
+#include 
+#include "apps.h"
+#include 
+#include 
+#include 
+#include 
+#include 
+
+static int save_certs(char *signerfile, STACK_OF(X509) *signers);
+static int smime_cb(int ok, X509_STORE_CTX *ctx);
+
+#define SMIME_OP        0x10
+#define SMIME_IP        0x20
+#define SMIME_SIGNERS   0x40
+#define SMIME_ENCRYPT   (1 | SMIME_OP)
+#define SMIME_DECRYPT   (2 | SMIME_IP)
+#define SMIME_SIGN      (3 | SMIME_OP | SMIME_SIGNERS)
+#define SMIME_VERIFY    (4 | SMIME_IP)
+#define SMIME_PK7OUT    (5 | SMIME_IP | SMIME_OP)
+#define SMIME_RESIGN    (6 | SMIME_IP | SMIME_OP | SMIME_SIGNERS)
+
+typedef enum OPTION_choice {
+    OPT_ERR = -1, OPT_EOF = 0, OPT_HELP,
+    OPT_ENCRYPT, OPT_DECRYPT, OPT_SIGN, OPT_RESIGN, OPT_VERIFY,
+    OPT_PK7OUT, OPT_TEXT, OPT_NOINTERN, OPT_NOVERIFY, OPT_NOCHAIN,
+    OPT_NOCERTS, OPT_NOATTR, OPT_NODETACH, OPT_NOSMIMECAP,
+    OPT_BINARY, OPT_NOSIGS, OPT_STREAM, OPT_INDEF, OPT_NOINDEF,
+    OPT_CRLFEOL, OPT_RAND, OPT_ENGINE, OPT_PASSIN,
+    OPT_TO, OPT_FROM, OPT_SUBJECT, OPT_SIGNER, OPT_RECIP, OPT_MD,
+    OPT_CIPHER, OPT_INKEY, OPT_KEYFORM, OPT_CERTFILE, OPT_CAFILE,
+    OPT_V_ENUM,
+    OPT_CAPATH, OPT_NOCAFILE, OPT_NOCAPATH, OPT_IN, OPT_INFORM, OPT_OUT,
+    OPT_OUTFORM, OPT_CONTENT
+} OPTION_CHOICE;
+
+OPTIONS smime_options[] = {
+    {OPT_HELP_STR, 1, '-', "Usage: %s [options] cert.pem...\n"},
+    {OPT_HELP_STR, 1, '-',
+        "  cert.pem... recipient certs for encryption\n"},
+    {OPT_HELP_STR, 1, '-', "Valid options are:\n"},
+    {"help", OPT_HELP, '-', "Display this summary"},
+    {"encrypt", OPT_ENCRYPT, '-', "Encrypt message"},
+    {"decrypt", OPT_DECRYPT, '-', "Decrypt encrypted message"},
+    {"sign", OPT_SIGN, '-', "Sign message"},
+    {"verify", OPT_VERIFY, '-', "Verify signed message"},
+    {"pk7out", OPT_PK7OUT, '-', "Output PKCS#7 structure"},
+    {"nointern", OPT_NOINTERN, '-',
+     "Don't search certificates in message for signer"},
+    {"nosigs", OPT_NOSIGS, '-', "Don't verify message signature"},
+    {"noverify", OPT_NOVERIFY, '-', "Don't verify signers certificate"},
+    {"nocerts", OPT_NOCERTS, '-',
+     "Don't include signers certificate when signing"},
+    {"nodetach", OPT_NODETACH, '-', "Use opaque signing"},
+    {"noattr", OPT_NOATTR, '-', "Don't include any signed attributes"},
+    {"binary", OPT_BINARY, '-', "Don't translate message to text"},
+    {"certfile", OPT_CERTFILE, '<', "Other certificates file"},
+    {"signer", OPT_SIGNER, 's', "Signer certificate file"},
+    {"recip", OPT_RECIP, '<', "Recipient certificate file for decryption"},
+    {"in", OPT_IN, '<', "Input file"},
+    {"inform", OPT_INFORM, 'c', "Input format SMIME (default), PEM or DER"},
+    {"inkey", OPT_INKEY, '<',
+     "Input private key (if not signer or recipient)"},
+    {"keyform", OPT_KEYFORM, 'f', "Input private key format (PEM or ENGINE)"},
+    {"out", OPT_OUT, '>', "Output file"},
+    {"outform", OPT_OUTFORM, 'c',
+     "Output format SMIME (default), PEM or DER"},
+    {"content", OPT_CONTENT, '<',
+     "Supply or override content for detached signature"},
+    {"to", OPT_TO, 's', "To address"},
+    {"from", OPT_FROM, 's', "From address"},
+    {"subject", OPT_SUBJECT, 's', "Subject"},
+    {"text", OPT_TEXT, '-', "Include or delete text MIME headers"},
+    {"CApath", OPT_CAPATH, '/', "Trusted certificates directory"},
+    {"CAfile", OPT_CAFILE, '<', "Trusted certificates file"},
+    {"no-CAfile", OPT_NOCAFILE, '-',
+     "Do not load the default certificates file"},
+    {"no-CApath", OPT_NOCAPATH, '-',
+     "Do not load certificates from the default certificates directory"},
+    {"resign", OPT_RESIGN, '-', "Resign a signed message"},
+    {"nochain", OPT_NOCHAIN, '-', 
+     "set PKCS7_NOCHAIN so certificates contained in the message are not used as untrusted CAs" },
+    {"nosmimecap", OPT_NOSMIMECAP, '-', "Omit the SMIMECapabilities attribute"},
+    {"stream", OPT_STREAM, '-', "Enable CMS streaming" },
+    {"indef", OPT_INDEF, '-', "Same as -stream" },
+    {"noindef", OPT_NOINDEF, '-', "Disable CMS streaming"},
+    {"crlfeol", OPT_CRLFEOL, '-', "Use CRLF as EOL termination instead of CR only"},
+    {"rand", OPT_RAND, 's',
+     "Load the file(s) into the random number generator"},
+    {"passin", OPT_PASSIN, 's', "Input file pass phrase source"},
+    {"md", OPT_MD, 's', "Digest algorithm to use when signing or resigning"},
+    {"", OPT_CIPHER, '-', "Any supported cipher"},
+    OPT_V_OPTIONS,
+#ifndef OPENSSL_NO_ENGINE
+    {"engine", OPT_ENGINE, 's', "Use engine, possibly a hardware device"},
+#endif
+    {NULL}
+};
+
+int smime_main(int argc, char **argv)
+{
+    BIO *in = NULL, *out = NULL, *indata = NULL;
+    EVP_PKEY *key = NULL;
+    PKCS7 *p7 = NULL;
+    STACK_OF(OPENSSL_STRING) *sksigners = NULL, *skkeys = NULL;
+    STACK_OF(X509) *encerts = NULL, *other = NULL;
+    X509 *cert = NULL, *recip = NULL, *signer = NULL;
+    X509_STORE *store = NULL;
+    X509_VERIFY_PARAM *vpm = NULL;
+    const EVP_CIPHER *cipher = NULL;
+    const EVP_MD *sign_md = NULL;
+    const char *CAfile = NULL, *CApath = NULL, *prog = NULL;
+    char *certfile = NULL, *keyfile = NULL, *contfile = NULL, *inrand = NULL;
+    char *infile = NULL, *outfile = NULL, *signerfile = NULL, *recipfile =
+        NULL;
+    char *passinarg = NULL, *passin = NULL, *to = NULL, *from =
+        NULL, *subject = NULL;
+    OPTION_CHOICE o;
+    int noCApath = 0, noCAfile = 0;
+    int flags = PKCS7_DETACHED, operation = 0, ret = 0, need_rand = 0, indef =
+        0;
+    int informat = FORMAT_SMIME, outformat = FORMAT_SMIME, keyform =
+        FORMAT_PEM;
+    int vpmtouched = 0, rv = 0;
+    ENGINE *e = NULL;
+    const char *mime_eol = "\n";
+
+    if ((vpm = X509_VERIFY_PARAM_new()) == NULL)
+        return 1;
+
+    prog = opt_init(argc, argv, smime_options);
+    while ((o = opt_next()) != OPT_EOF) {
+        switch (o) {
+        case OPT_EOF:
+        case OPT_ERR:
+ opthelp:
+            BIO_printf(bio_err, "%s: Use -help for summary.\n", prog);
+            goto end;
+        case OPT_HELP:
+            opt_help(smime_options);
+            ret = 0;
+            goto end;
+        case OPT_INFORM:
+            if (!opt_format(opt_arg(), OPT_FMT_PDS, &informat))
+                goto opthelp;
+            break;
+        case OPT_IN:
+            infile = opt_arg();
+            break;
+        case OPT_OUTFORM:
+            if (!opt_format(opt_arg(), OPT_FMT_PDS, &outformat))
+                goto opthelp;
+            break;
+        case OPT_OUT:
+            outfile = opt_arg();
+            break;
+        case OPT_ENCRYPT:
+            operation = SMIME_ENCRYPT;
+            break;
+        case OPT_DECRYPT:
+            operation = SMIME_DECRYPT;
+            break;
+        case OPT_SIGN:
+            operation = SMIME_SIGN;
+            break;
+        case OPT_RESIGN:
+            operation = SMIME_RESIGN;
+            break;
+        case OPT_VERIFY:
+            operation = SMIME_VERIFY;
+            break;
+        case OPT_PK7OUT:
+            operation = SMIME_PK7OUT;
+            break;
+        case OPT_TEXT:
+            flags |= PKCS7_TEXT;
+            break;
+        case OPT_NOINTERN:
+            flags |= PKCS7_NOINTERN;
+            break;
+        case OPT_NOVERIFY:
+            flags |= PKCS7_NOVERIFY;
+            break;
+        case OPT_NOCHAIN:
+            flags |= PKCS7_NOCHAIN;
+            break;
+        case OPT_NOCERTS:
+            flags |= PKCS7_NOCERTS;
+            break;
+        case OPT_NOATTR:
+            flags |= PKCS7_NOATTR;
+            break;
+        case OPT_NODETACH:
+            flags &= ~PKCS7_DETACHED;
+            break;
+        case OPT_NOSMIMECAP:
+            flags |= PKCS7_NOSMIMECAP;
+            break;
+        case OPT_BINARY:
+            flags |= PKCS7_BINARY;
+            break;
+        case OPT_NOSIGS:
+            flags |= PKCS7_NOSIGS;
+            break;
+        case OPT_STREAM:
+        case OPT_INDEF:
+            indef = 1;
+            break;
+        case OPT_NOINDEF:
+            indef = 0;
+            break;
+        case OPT_CRLFEOL:
+            flags |= PKCS7_CRLFEOL;
+            mime_eol = "\r\n";
+            break;
+        case OPT_RAND:
+            inrand = opt_arg();
+            need_rand = 1;
+            break;
+        case OPT_ENGINE:
+            e = setup_engine(opt_arg(), 0);
+            break;
+        case OPT_PASSIN:
+            passinarg = opt_arg();
+            break;
+        case OPT_TO:
+            to = opt_arg();
+            break;
+        case OPT_FROM:
+            from = opt_arg();
+            break;
+        case OPT_SUBJECT:
+            subject = opt_arg();
+            break;
+        case OPT_SIGNER:
+            /* If previous -signer argument add signer to list */
+            if (signerfile) {
+                if (sksigners == NULL
+                    && (sksigners = sk_OPENSSL_STRING_new_null()) == NULL)
+                    goto end;
+                sk_OPENSSL_STRING_push(sksigners, signerfile);
+                if (keyfile == NULL)
+                    keyfile = signerfile;
+                if (skkeys == NULL
+                    && (skkeys = sk_OPENSSL_STRING_new_null()) == NULL)
+                    goto end;
+                sk_OPENSSL_STRING_push(skkeys, keyfile);
+                keyfile = NULL;
+            }
+            signerfile = opt_arg();
+            break;
+        case OPT_RECIP:
+            recipfile = opt_arg();
+            break;
+        case OPT_MD:
+            if (!opt_md(opt_arg(), &sign_md))
+                goto opthelp;
+            break;
+        case OPT_CIPHER:
+            if (!opt_cipher(opt_unknown(), &cipher))
+                goto opthelp;
+            break;
+        case OPT_INKEY:
+            /* If previous -inkey argument add signer to list */
+            if (keyfile) {
+                if (signerfile == NULL) {
+                    BIO_printf(bio_err,
+                               "%s: Must have -signer before -inkey\n", prog);
+                    goto opthelp;
+                }
+                if (sksigners == NULL
+                    && (sksigners = sk_OPENSSL_STRING_new_null()) == NULL)
+                    goto end;
+                sk_OPENSSL_STRING_push(sksigners, signerfile);
+                signerfile = NULL;
+                if (skkeys == NULL
+                    && (skkeys = sk_OPENSSL_STRING_new_null()) == NULL)
+                    goto end;
+                sk_OPENSSL_STRING_push(skkeys, keyfile);
+            }
+            keyfile = opt_arg();
+            break;
+        case OPT_KEYFORM:
+            if (!opt_format(opt_arg(), OPT_FMT_ANY, &keyform))
+                goto opthelp;
+            break;
+        case OPT_CERTFILE:
+            certfile = opt_arg();
+            break;
+        case OPT_CAFILE:
+            CAfile = opt_arg();
+            break;
+        case OPT_CAPATH:
+            CApath = opt_arg();
+            break;
+        case OPT_NOCAFILE:
+            noCAfile = 1;
+            break;
+        case OPT_NOCAPATH:
+            noCApath = 1;
+            break;
+        case OPT_CONTENT:
+            contfile = opt_arg();
+            break;
+        case OPT_V_CASES:
+            if (!opt_verify(o, vpm))
+                goto opthelp;
+            vpmtouched++;
+            break;
+        }
+    }
+    argc = opt_num_rest();
+    argv = opt_rest();
+
+    if (!(operation & SMIME_SIGNERS) && (skkeys || sksigners)) {
+        BIO_puts(bio_err, "Multiple signers or keys not allowed\n");
+        goto opthelp;
+    }
+
+    if (operation & SMIME_SIGNERS) {
+        /* Check to see if any final signer needs to be appended */
+        if (keyfile && !signerfile) {
+            BIO_puts(bio_err, "Illegal -inkey without -signer\n");
+            goto opthelp;
+        }
+        if (signerfile) {
+            if (!sksigners
+                && (sksigners = sk_OPENSSL_STRING_new_null()) == NULL)
+                goto end;
+            sk_OPENSSL_STRING_push(sksigners, signerfile);
+            if (!skkeys && (skkeys = sk_OPENSSL_STRING_new_null()) == NULL)
+                goto end;
+            if (!keyfile)
+                keyfile = signerfile;
+            sk_OPENSSL_STRING_push(skkeys, keyfile);
+        }
+        if (!sksigners) {
+            BIO_printf(bio_err, "No signer certificate specified\n");
+            goto opthelp;
+        }
+        signerfile = NULL;
+        keyfile = NULL;
+        need_rand = 1;
+    } else if (operation == SMIME_DECRYPT) {
+        if (!recipfile && !keyfile) {
+            BIO_printf(bio_err,
+                       "No recipient certificate or key specified\n");
+            goto opthelp;
+        }
+    } else if (operation == SMIME_ENCRYPT) {
+        if (argc == 0) {
+            BIO_printf(bio_err, "No recipient(s) certificate(s) specified\n");
+            goto opthelp;
+        }
+        need_rand = 1;
+    } else if (!operation)
+        goto opthelp;
+
+    if (!app_passwd(passinarg, NULL, &passin, NULL)) {
+        BIO_printf(bio_err, "Error getting password\n");
+        goto end;
+    }
+
+    if (need_rand) {
+        app_RAND_load_file(NULL, (inrand != NULL));
+        if (inrand != NULL)
+            BIO_printf(bio_err, "%ld semi-random bytes loaded\n",
+                       app_RAND_load_files(inrand));
+    }
+
+    ret = 2;
+
+    if (!(operation & SMIME_SIGNERS))
+        flags &= ~PKCS7_DETACHED;
+
+    if (!(operation & SMIME_OP)) {
+        if (flags & PKCS7_BINARY)
+            outformat = FORMAT_BINARY;
+    }
+
+    if (!(operation & SMIME_IP)) {
+        if (flags & PKCS7_BINARY)
+            informat = FORMAT_BINARY;
+    }
+
+    if (operation == SMIME_ENCRYPT) {
+        if (!cipher) {
+#ifndef OPENSSL_NO_DES
+            cipher = EVP_des_ede3_cbc();
+#else
+            BIO_printf(bio_err, "No cipher selected\n");
+            goto end;
+#endif
+        }
+        encerts = sk_X509_new_null();
+        if (!encerts)
+            goto end;
+        while (*argv) {
+            cert = load_cert(*argv, FORMAT_PEM,
+                             "recipient certificate file");
+            if (cert == NULL)
+                goto end;
+            sk_X509_push(encerts, cert);
+            cert = NULL;
+            argv++;
+        }
+    }
+
+    if (certfile) {
+        if (!load_certs(certfile, &other, FORMAT_PEM, NULL,
+                        "certificate file")) {
+            ERR_print_errors(bio_err);
+            goto end;
+        }
+    }
+
+    if (recipfile && (operation == SMIME_DECRYPT)) {
+        if ((recip = load_cert(recipfile, FORMAT_PEM,
+                               "recipient certificate file")) == NULL) {
+            ERR_print_errors(bio_err);
+            goto end;
+        }
+    }
+
+    if (operation == SMIME_DECRYPT) {
+        if (!keyfile)
+            keyfile = recipfile;
+    } else if (operation == SMIME_SIGN) {
+        if (!keyfile)
+            keyfile = signerfile;
+    } else
+        keyfile = NULL;
+
+    if (keyfile) {
+        key = load_key(keyfile, keyform, 0, passin, e, "signing key file");
+        if (!key)
+            goto end;
+    }
+
+    in = bio_open_default(infile, 'r', informat);
+    if (in == NULL)
+        goto end;
+
+    if (operation & SMIME_IP) {
+        if (informat == FORMAT_SMIME)
+            p7 = SMIME_read_PKCS7(in, &indata);
+        else if (informat == FORMAT_PEM)
+            p7 = PEM_read_bio_PKCS7(in, NULL, NULL, NULL);
+        else if (informat == FORMAT_ASN1)
+            p7 = d2i_PKCS7_bio(in, NULL);
+        else {
+            BIO_printf(bio_err, "Bad input format for PKCS#7 file\n");
+            goto end;
+        }
+
+        if (!p7) {
+            BIO_printf(bio_err, "Error reading S/MIME message\n");
+            goto end;
+        }
+        if (contfile) {
+            BIO_free(indata);
+            if ((indata = BIO_new_file(contfile, "rb")) == NULL) {
+                BIO_printf(bio_err, "Can't read content file %s\n", contfile);
+                goto end;
+            }
+        }
+    }
+
+    out = bio_open_default(outfile, 'w', outformat);
+    if (out == NULL)
+        goto end;
+
+    if (operation == SMIME_VERIFY) {
+        if ((store = setup_verify(CAfile, CApath, noCAfile, noCApath)) == NULL)
+            goto end;
+        X509_STORE_set_verify_cb(store, smime_cb);
+        if (vpmtouched)
+            X509_STORE_set1_param(store, vpm);
+    }
+
+    ret = 3;
+
+    if (operation == SMIME_ENCRYPT) {
+        if (indef)
+            flags |= PKCS7_STREAM;
+        p7 = PKCS7_encrypt(encerts, in, cipher, flags);
+    } else if (operation & SMIME_SIGNERS) {
+        int i;
+        /*
+         * If detached data content we only enable streaming if S/MIME output
+         * format.
+         */
+        if (operation == SMIME_SIGN) {
+            if (flags & PKCS7_DETACHED) {
+                if (outformat == FORMAT_SMIME)
+                    flags |= PKCS7_STREAM;
+            } else if (indef)
+                flags |= PKCS7_STREAM;
+            flags |= PKCS7_PARTIAL;
+            p7 = PKCS7_sign(NULL, NULL, other, in, flags);
+            if (!p7)
+                goto end;
+            if (flags & PKCS7_NOCERTS) {
+                for (i = 0; i < sk_X509_num(other); i++) {
+                    X509 *x = sk_X509_value(other, i);
+                    PKCS7_add_certificate(p7, x);
+                }
+            }
+        } else
+            flags |= PKCS7_REUSE_DIGEST;
+        for (i = 0; i < sk_OPENSSL_STRING_num(sksigners); i++) {
+            signerfile = sk_OPENSSL_STRING_value(sksigners, i);
+            keyfile = sk_OPENSSL_STRING_value(skkeys, i);
+            signer = load_cert(signerfile, FORMAT_PEM,
+                               "signer certificate");
+            if (!signer)
+                goto end;
+            key = load_key(keyfile, keyform, 0, passin, e, "signing key file");
+            if (!key)
+                goto end;
+            if (!PKCS7_sign_add_signer(p7, signer, key, sign_md, flags))
+                goto end;
+            X509_free(signer);
+            signer = NULL;
+            EVP_PKEY_free(key);
+            key = NULL;
+        }
+        /* If not streaming or resigning finalize structure */
+        if ((operation == SMIME_SIGN) && !(flags & PKCS7_STREAM)) {
+            if (!PKCS7_final(p7, in, flags))
+                goto end;
+        }
+    }
+
+    if (!p7) {
+        BIO_printf(bio_err, "Error creating PKCS#7 structure\n");
+        goto end;
+    }
+
+    ret = 4;
+    if (operation == SMIME_DECRYPT) {
+        if (!PKCS7_decrypt(p7, key, recip, out, flags)) {
+            BIO_printf(bio_err, "Error decrypting PKCS#7 structure\n");
+            goto end;
+        }
+    } else if (operation == SMIME_VERIFY) {
+        STACK_OF(X509) *signers;
+        if (PKCS7_verify(p7, other, store, indata, out, flags))
+            BIO_printf(bio_err, "Verification successful\n");
+        else {
+            BIO_printf(bio_err, "Verification failure\n");
+            goto end;
+        }
+        signers = PKCS7_get0_signers(p7, other, flags);
+        if (!save_certs(signerfile, signers)) {
+            BIO_printf(bio_err, "Error writing signers to %s\n", signerfile);
+            ret = 5;
+            goto end;
+        }
+        sk_X509_free(signers);
+    } else if (operation == SMIME_PK7OUT)
+        PEM_write_bio_PKCS7(out, p7);
+    else {
+        if (to)
+            BIO_printf(out, "To: %s%s", to, mime_eol);
+        if (from)
+            BIO_printf(out, "From: %s%s", from, mime_eol);
+        if (subject)
+            BIO_printf(out, "Subject: %s%s", subject, mime_eol);
+        if (outformat == FORMAT_SMIME) {
+            if (operation == SMIME_RESIGN)
+                rv = SMIME_write_PKCS7(out, p7, indata, flags);
+            else
+                rv = SMIME_write_PKCS7(out, p7, in, flags);
+        } else if (outformat == FORMAT_PEM)
+            rv = PEM_write_bio_PKCS7_stream(out, p7, in, flags);
+        else if (outformat == FORMAT_ASN1)
+            rv = i2d_PKCS7_bio_stream(out, p7, in, flags);
+        else {
+            BIO_printf(bio_err, "Bad output format for PKCS#7 file\n");
+            goto end;
+        }
+        if (rv == 0) {
+            BIO_printf(bio_err, "Error writing output\n");
+            ret = 3;
+            goto end;
+        }
+    }
+    ret = 0;
+ end:
+    if (need_rand)
+        app_RAND_write_file(NULL);
+    if (ret)
+        ERR_print_errors(bio_err);
+    sk_X509_pop_free(encerts, X509_free);
+    sk_X509_pop_free(other, X509_free);
+    X509_VERIFY_PARAM_free(vpm);
+    sk_OPENSSL_STRING_free(sksigners);
+    sk_OPENSSL_STRING_free(skkeys);
+    X509_STORE_free(store);
+    X509_free(cert);
+    X509_free(recip);
+    X509_free(signer);
+    EVP_PKEY_free(key);
+    PKCS7_free(p7);
+    release_engine(e);
+    BIO_free(in);
+    BIO_free(indata);
+    BIO_free_all(out);
+    OPENSSL_free(passin);
+    return (ret);
+}
+
+static int save_certs(char *signerfile, STACK_OF(X509) *signers)
+{
+    int i;
+    BIO *tmp;
+    if (!signerfile)
+        return 1;
+    tmp = BIO_new_file(signerfile, "w");
+    if (!tmp)
+        return 0;
+    for (i = 0; i < sk_X509_num(signers); i++)
+        PEM_write_bio_X509(tmp, sk_X509_value(signers, i));
+    BIO_free(tmp);
+    return 1;
+}
+
+/* Minimal callback just to output policy info (if any) */
+
+static int smime_cb(int ok, X509_STORE_CTX *ctx)
+{
+    int error;
+
+    error = X509_STORE_CTX_get_error(ctx);
+
+    if ((error != X509_V_ERR_NO_EXPLICIT_POLICY)
+        && ((error != X509_V_OK) || (ok != 2)))
+        return ok;
+
+    policies_print(ctx);
+
+    return ok;
+
+}
diff --git a/apps/speed.c b/apps/speed.c
new file mode 100644
index 000000000..f73f90482
--- /dev/null
+++ b/apps/speed.c
@@ -0,0 +1,4236 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/* ====================================================================
+ * Copyright 2002 Sun Microsystems, Inc. ALL RIGHTS RESERVED.
+ *
+ * Portions of the attached software ("Contribution") are developed by
+ * SUN MICROSYSTEMS, INC., and are contributed to the OpenSSL project.
+ *
+ * The Contribution is licensed pursuant to the OpenSSL open source
+ * license provided above.
+ *
+ * The ECDH and ECDSA speed test software is originally written by
+ * Sumit Gupta of Sun Microsystems Laboratories.
+ *
+ */
+
+#undef SECONDS
+#define SECONDS                  3
+#define PRIME_SECONDS           10
+#define RSA_SECONDS             10
+#define DSA_SECONDS             10
+#define ECDSA_SECONDS           10
+#define ECDH_SECONDS            10
+
+#include 
+#include 
+#include 
+#include 
+#include "apps.h"
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#if !defined(OPENSSL_SYS_MSDOS)
+# include OPENSSL_UNISTD
+#endif
+
+#if defined(_WIN32)
+# include 
+#endif
+
+#include 
+#ifndef OPENSSL_NO_DES
+# include 
+#endif
+#ifndef OPENSSL_NO_AES
+# include 
+#endif
+#ifndef OPENSSL_NO_CAMELLIA
+# include 
+#endif
+#ifndef OPENSSL_NO_MD2
+# include 
+#endif
+#ifndef OPENSSL_NO_MDC2
+# include 
+#endif
+#ifndef OPENSSL_NO_MD4
+# include 
+#endif
+#ifndef OPENSSL_NO_MD5
+# include 
+#endif
+#include 
+#ifndef OPENSSL_NO_SHA
+# include 
+#endif
+#ifndef OPENSSL_NO_RMD160
+# include 
+#endif
+#ifndef OPENSSL_NO_WHIRLPOOL
+# include 
+#endif
+#ifndef OPENSSL_NO_RC4
+# include 
+#endif
+#ifndef OPENSSL_NO_RC5
+# include 
+#endif
+#ifndef OPENSSL_NO_RC2
+# include 
+#endif
+#ifndef OPENSSL_NO_IDEA
+# include 
+#endif
+#ifndef OPENSSL_NO_SEED
+# include 
+#endif
+#ifndef OPENSSL_NO_BF
+# include 
+#endif
+#ifndef OPENSSL_NO_CAST
+# include 
+#endif
+#ifndef OPENSSL_NO_RSA
+# include 
+# include "./testrsa.h"
+#endif
+#include 
+#ifndef OPENSSL_NO_DSA
+# include 
+# include "./testdsa.h"
+#endif
+#ifndef OPENSSL_NO_EC
+# include 
+#endif
+#ifndef OPENSSL_NO_SM2
+# include 
+#endif
+#ifndef OPENSSL_NO_SM3
+# include 
+#endif
+#ifndef OPENSSL_NO_SMS4
+# include 
+#endif
+#ifndef OPENSSL_NO_ZUC
+# include 
+#endif
+#ifndef OPENSSL_NO_SM9
+# include 
+#endif
+#include 
+
+#ifndef HAVE_FORK
+# if defined(OPENSSL_SYS_VMS) || defined(OPENSSL_SYS_WINDOWS)
+#  define HAVE_FORK 0
+# else
+#  define HAVE_FORK 1
+# endif
+#endif
+
+#if HAVE_FORK
+# undef NO_FORK
+#else
+# define NO_FORK
+#endif
+
+#undef BUFSIZE
+#define BUFSIZE (1024*16+1)
+#define MAX_MISALIGNMENT 63
+
+#define ALGOR_NUM       34
+#define SIZE_NUM        6
+#define PRIME_NUM       3
+#define RSA_NUM         7
+#define DSA_NUM         3
+
+#define EC_NUM          17
+#define SM2_NUM         1
+#define SM9_NUM	        1
+#define MAX_ECDH_SIZE   256
+#define MISALIGN        64
+
+static volatile int run = 0;
+
+static int mr = 0;
+static int usertime = 1;
+
+typedef void *(*kdf_fn) (
+        const void *in, size_t inlen, void *out, size_t *xoutlen);
+
+typedef struct loopargs_st {
+    ASYNC_JOB *inprogress_job;
+    ASYNC_WAIT_CTX *wait_ctx;
+    unsigned char *buf;
+    unsigned char *buf2;
+    unsigned char *buf_malloc;
+    unsigned char *buf2_malloc;
+    unsigned int siglen;
+#ifndef OPENSSL_NO_RSA
+    RSA *rsa_key[RSA_NUM];
+#endif
+#ifndef OPENSSL_NO_DSA
+    DSA *dsa_key[DSA_NUM];
+#endif
+#ifndef OPENSSL_NO_EC
+    EC_KEY *ecdsa[EC_NUM];
+    EC_KEY *ecdh_a[EC_NUM];
+    EC_KEY *ecdh_b[EC_NUM];
+    unsigned char *secret_a;
+    unsigned char *secret_b;
+    size_t      outlen;
+    kdf_fn      kdf;
+#endif
+#ifndef OPENSSL_NO_SM2
+    EC_KEY *sm2[SM2_NUM];
+    size_t cipherlen;
+# if 0
+    unsigned char *sm2dh_a;
+    unsigned char *sm2dh_b;
+# endif
+#endif
+#ifndef OPENSSL_NO_SM9
+    SM9PublicParameters *sm9mpk[SM9_NUM];
+    SM9PrivateKey *sm9sk[SM9_NUM];
+    SM9MasterSecret *sm9mst[SM9_NUM];
+#endif
+    EVP_CIPHER_CTX *ctx;
+    HMAC_CTX *hctx;
+    GCM128_CONTEXT *gcm_ctx;
+} loopargs_t;
+
+#ifndef OPENSSL_NO_MD2
+static int EVP_Digest_MD2_loop(void *args);
+#endif
+#ifndef OPENSSL_NO_MDC2
+static int EVP_Digest_MDC2_loop(void *args);
+#endif
+#ifndef OPENSSL_NO_MD4
+static int EVP_Digest_MD4_loop(void *args);
+#endif
+#ifndef OPENSSL_NO_MD5
+static int MD5_loop(void *args);
+static int HMAC_loop(void *args);
+#endif
+#ifndef OPENSSL_NO_SM3
+static int SM3_loop(void *args);
+#endif
+#ifndef OPENSSL_NO_SHA
+static int SHA1_loop(void *args);
+static int SHA256_loop(void *args);
+static int SHA512_loop(void *args);
+#endif
+#ifndef OPENSSL_NO_WHIRLPOOL
+static int WHIRLPOOL_loop(void *args);
+#endif
+#ifndef OPENSSL_NO_RMD160
+static int EVP_Digest_RMD160_loop(void *args);
+#endif
+#ifndef OPENSSL_NO_RC4
+static int RC4_loop(void *args);
+#endif
+#ifndef OPENSSL_NO_DES
+static int DES_ncbc_encrypt_loop(void *args);
+static int DES_ede3_cbc_encrypt_loop(void *args);
+#endif
+#ifndef OPENSSL_NO_AES
+static int AES_cbc_128_encrypt_loop(void *args);
+static int AES_cbc_192_encrypt_loop(void *args);
+static int AES_ige_128_encrypt_loop(void *args);
+static int AES_cbc_256_encrypt_loop(void *args);
+static int AES_ige_192_encrypt_loop(void *args);
+static int AES_ige_256_encrypt_loop(void *args);
+static int CRYPTO_gcm128_aad_loop(void *args);
+#endif
+static int EVP_Update_loop(void *args);
+static int EVP_Digest_loop(void *args);
+#ifndef OPENSSL_NO_RSA
+static int RSA_sign_loop(void *args);
+static int RSA_verify_loop(void *args);
+#endif
+#ifndef OPENSSL_NO_DSA
+static int DSA_sign_loop(void *args);
+static int DSA_verify_loop(void *args);
+#endif
+#ifndef OPENSSL_NO_EC
+static int ECDSA_sign_loop(void *args);
+static int ECDSA_verify_loop(void *args);
+static int ECDH_compute_key_loop(void *args);
+#endif
+#ifndef OPENSSL_NO_SM2
+static int SM2_sign_loop(void *args);
+static int SM2_verify_loop(void *args);
+static int SM2_encrypt_loop(void *args);
+static int SM2_decrypt_loop(void *args);
+#endif
+#ifndef OPENSSL_NO_SM9
+static int SM9_sign_loop(void *args);
+static int SM9_verify_loop(void *args);
+static int SM9_encrypt_loop(void *args);
+static int SM9_decrypt_loop(void *args);
+#endif
+static int run_benchmark(int async_jobs, int (*loop_function)(void *), loopargs_t *loopargs);
+
+static double Time_F(int s);
+static void print_message(const char *s, long num, int length);
+static void pkey_print_message(const char *str, const char *str2,
+                               long num, int bits, int sec);
+static void print_result(int alg, int run_no, int count, double time_used);
+#ifndef NO_FORK
+static int do_multi(int multi);
+#endif
+
+static const char *names[ALGOR_NUM] = {
+    "md2", "mdc2", "md4", "md5", "hmac(md5)", "sha1", "rmd160", "rc4",
+    "des cbc", "des ede3", "idea cbc", "seed cbc",
+    "rc2 cbc", "rc5-32/12 cbc", "blowfish cbc", "cast cbc",
+    "aes-128 cbc", "aes-192 cbc", "aes-256 cbc",
+    "camellia-128 cbc", "camellia-192 cbc", "camellia-256 cbc",
+    "evp", "sha256", "sha512", "whirlpool",
+    "aes-128 ige", "aes-192 ige", "aes-256 ige", "ghash",
+    "sm3", "sms4 cbc", "zuc", "zuc256"
+};
+
+static double results[ALGOR_NUM][SIZE_NUM];
+
+static const int lengths[SIZE_NUM] = {
+    16, 64, 256, 1024, 8 * 1024, 16 * 1024
+};
+
+#ifndef OPENSSL_NO_RSA
+static double rsa_results[RSA_NUM][2];
+#endif
+#ifndef OPENSSL_NO_DSA
+static double dsa_results[DSA_NUM][2];
+#endif
+#ifndef OPENSSL_NO_EC
+static double ecdsa_results[EC_NUM][2];
+static double ecdh_results[EC_NUM][1];
+#endif
+#ifndef OPENSSL_NO_SM2
+static double sm2sign_results[SM2_NUM][2];
+static double sm2enc_results[SM2_NUM][2];
+#endif
+#ifndef OPENSSL_NO_SM9
+static double sm9sign_results[SM9_NUM][2];
+static double sm9enc_results[SM9_NUM][2];
+#endif
+
+#if !defined(OPENSSL_NO_DSA) || !defined(OPENSSL_NO_EC)
+static const char rnd_seed[] =
+    "string to make the random number generator think it has entropy";
+#endif
+
+#ifdef SIGALRM
+# if defined(__STDC__) || defined(sgi) || defined(_AIX)
+#  define SIGRETTYPE void
+# else
+#  define SIGRETTYPE int
+# endif
+
+static SIGRETTYPE sig_done(int sig);
+static SIGRETTYPE sig_done(int sig)
+{
+    signal(SIGALRM, sig_done);
+    run = 0;
+}
+#endif
+
+#define START   0
+#define STOP    1
+
+#if defined(_WIN32)
+
+# if !defined(SIGALRM)
+#  define SIGALRM
+# endif
+static unsigned int lapse, schlock;
+static void alarm_win32(unsigned int secs)
+{
+    lapse = secs * 1000;
+}
+
+# define alarm alarm_win32
+
+static DWORD WINAPI sleepy(VOID * arg)
+{
+    schlock = 1;
+    Sleep(lapse);
+    run = 0;
+    return 0;
+}
+
+static double Time_F(int s)
+{
+    double ret;
+    static HANDLE thr;
+
+    if (s == START) {
+        schlock = 0;
+        thr = CreateThread(NULL, 4096, sleepy, NULL, 0, NULL);
+        if (thr == NULL) {
+            DWORD err = GetLastError();
+            BIO_printf(bio_err, "unable to CreateThread (%lu)", err);
+            ExitProcess(err);
+        }
+        while (!schlock)
+            Sleep(0);           /* scheduler spinlock */
+        ret = app_tminterval(s, usertime);
+    } else {
+        ret = app_tminterval(s, usertime);
+        if (run)
+            TerminateThread(thr, 0);
+        CloseHandle(thr);
+    }
+
+    return ret;
+}
+#else
+
+static double Time_F(int s)
+{
+    double ret = app_tminterval(s, usertime);
+    if (s == STOP)
+        alarm(0);
+    return ret;
+}
+#endif
+
+static void multiblock_speed(const EVP_CIPHER *evp_cipher);
+
+static int found(const char *name, const OPT_PAIR *pairs, int *result)
+{
+    for (; pairs->name; pairs++)
+        if (strcmp(name, pairs->name) == 0) {
+            *result = pairs->retval;
+            return 1;
+        }
+    return 0;
+}
+
+typedef enum OPTION_choice {
+    OPT_ERR = -1, OPT_EOF = 0, OPT_HELP,
+    OPT_ELAPSED, OPT_EVP, OPT_DECRYPT, OPT_ENGINE, OPT_MULTI,
+    OPT_MR, OPT_MB, OPT_MISALIGN, OPT_ASYNCJOBS, OPT_NOPRE
+} OPTION_CHOICE;
+
+OPTIONS speed_options[] = {
+    {OPT_HELP_STR, 1, '-', "Usage: %s [options] ciphers...\n"},
+    {OPT_HELP_STR, 1, '-', "Valid options are:\n"},
+    {"help", OPT_HELP, '-', "Display this summary"},
+    {"evp", OPT_EVP, 's', "Use specified EVP cipher"},
+    {"decrypt", OPT_DECRYPT, '-',
+     "Time decryption instead of encryption (only EVP)"},
+    {"nopre", OPT_NOPRE, '-', "Disable pre-compute"},
+    {"mr", OPT_MR, '-', "Produce machine readable output"},
+    {"mb", OPT_MB, '-',
+     "Enable (tls1.1) multi-block mode on evp_cipher requested with -evp"},
+    {"misalign", OPT_MISALIGN, 'n', "Amount to mis-align buffers"},
+    {"elapsed", OPT_ELAPSED, '-',
+     "Measure time in real time instead of CPU user time"},
+#ifndef NO_FORK
+    {"multi", OPT_MULTI, 'p', "Run benchmarks in parallel"},
+#endif
+#ifndef OPENSSL_NO_ASYNC
+    {"async_jobs", OPT_ASYNCJOBS, 'p',
+     "Enable async mode and start pnum jobs"},
+#endif
+#ifndef OPENSSL_NO_ENGINE
+    {"engine", OPT_ENGINE, 's', "Use engine, possibly a hardware device"},
+#endif
+    {NULL},
+};
+
+#define D_MD2           0
+#define D_MDC2          1
+#define D_MD4           2
+#define D_MD5           3
+#define D_HMAC          4
+#define D_SHA1          5
+#define D_RMD160        6
+#define D_RC4           7
+#define D_CBC_DES       8
+#define D_EDE3_DES      9
+#define D_CBC_IDEA      10
+#define D_CBC_SEED      11
+#define D_CBC_RC2       12
+#define D_CBC_RC5       13
+#define D_CBC_BF        14
+#define D_CBC_CAST      15
+#define D_CBC_128_AES   16
+#define D_CBC_192_AES   17
+#define D_CBC_256_AES   18
+#define D_CBC_128_CML   19
+#define D_CBC_192_CML   20
+#define D_CBC_256_CML   21
+#define D_EVP           22
+#define D_SHA256        23
+#define D_SHA512        24
+#define D_WHIRLPOOL     25
+#define D_IGE_128_AES   26
+#define D_IGE_192_AES   27
+#define D_IGE_256_AES   28
+#define D_GHASH         29
+#define D_SM3           30
+#define D_CBC_SMS4      31
+#define D_ZUC           32
+#define D_ZUC256        33
+static OPT_PAIR doit_choices[] = {
+#ifndef OPENSSL_NO_MD2
+    {"md2", D_MD2},
+#endif
+#ifndef OPENSSL_NO_MDC2
+    {"mdc2", D_MDC2},
+#endif
+#ifndef OPENSSL_NO_MD4
+    {"md4", D_MD4},
+#endif
+#ifndef OPENSSL_NO_MD5
+    {"md5", D_MD5},
+    {"hmac", D_HMAC},
+#endif
+#ifndef OPENSSL_NO_SHA
+    {"sha1", D_SHA1},
+    {"sha256", D_SHA256},
+    {"sha512", D_SHA512},
+#endif
+#ifndef OPENSSL_NO_WHIRLPOOL
+    {"whirlpool", D_WHIRLPOOL},
+#endif
+#ifndef OPENSSL_NO_RMD160
+    {"ripemd", D_RMD160},
+    {"rmd160", D_RMD160},
+    {"ripemd160", D_RMD160},
+#endif
+#ifndef OPENSSL_NO_RC4
+    {"rc4", D_RC4},
+#endif
+#ifndef OPENSSL_NO_DES
+    {"des-cbc", D_CBC_DES},
+    {"des-ede3", D_EDE3_DES},
+#endif
+#ifndef OPENSSL_NO_AES
+    {"aes-128-cbc", D_CBC_128_AES},
+    {"aes-192-cbc", D_CBC_192_AES},
+    {"aes-256-cbc", D_CBC_256_AES},
+    {"aes-128-ige", D_IGE_128_AES},
+    {"aes-192-ige", D_IGE_192_AES},
+    {"aes-256-ige", D_IGE_256_AES},
+#endif
+#ifndef OPENSSL_NO_RC2
+    {"rc2-cbc", D_CBC_RC2},
+    {"rc2", D_CBC_RC2},
+#endif
+#ifndef OPENSSL_NO_RC5
+    {"rc5-cbc", D_CBC_RC5},
+    {"rc5", D_CBC_RC5},
+#endif
+#ifndef OPENSSL_NO_IDEA
+    {"idea-cbc", D_CBC_IDEA},
+    {"idea", D_CBC_IDEA},
+#endif
+#ifndef OPENSSL_NO_SEED
+    {"seed-cbc", D_CBC_SEED},
+    {"seed", D_CBC_SEED},
+#endif
+#ifndef OPENSSL_NO_BF
+    {"bf-cbc", D_CBC_BF},
+    {"blowfish", D_CBC_BF},
+    {"bf", D_CBC_BF},
+#endif
+#ifndef OPENSSL_NO_CAST
+    {"cast-cbc", D_CBC_CAST},
+    {"cast", D_CBC_CAST},
+    {"cast5", D_CBC_CAST},
+#endif
+    {"ghash", D_GHASH},
+#ifndef OPENSSL_NO_SM3
+    {"sm3", D_SM3},
+#endif
+#ifndef OPENSSL_NO_SMS4
+    {"sms4-cbc", D_CBC_SMS4},
+    {"sms4", D_CBC_SMS4},
+#endif
+#ifndef OPENSSL_NO_ZUC
+    {"zuc", D_ZUC},
+    {"zuc256", D_ZUC256},
+#endif
+    {NULL}
+};
+
+#ifndef OPENSSL_NO_DSA
+# define R_DSA_512       0
+# define R_DSA_1024      1
+# define R_DSA_2048      2
+static OPT_PAIR dsa_choices[] = {
+    {"dsa512", R_DSA_512},
+    {"dsa1024", R_DSA_1024},
+    {"dsa2048", R_DSA_2048},
+    {NULL},
+};
+#endif
+
+#ifndef OPENSSL_NO_RSA
+#define R_RSA_512       0
+#define R_RSA_1024      1
+#define R_RSA_2048      2
+#define R_RSA_3072      3
+#define R_RSA_4096      4
+#define R_RSA_7680      5
+#define R_RSA_15360     6
+static OPT_PAIR rsa_choices[] = {
+    {"rsa512", R_RSA_512},
+    {"rsa1024", R_RSA_1024},
+    {"rsa2048", R_RSA_2048},
+    {"rsa3072", R_RSA_3072},
+    {"rsa4096", R_RSA_4096},
+    {"rsa7680", R_RSA_7680},
+    {"rsa15360", R_RSA_15360},
+    {NULL}
+};
+#endif
+
+#define R_EC_P160    0
+#define R_EC_P192    1
+#define R_EC_P224    2
+#define R_EC_P256    3
+#define R_EC_P384    4
+#define R_EC_P521    5
+#define R_EC_K163    6
+#define R_EC_K233    7
+#define R_EC_K283    8
+#define R_EC_K409    9
+#define R_EC_K571    10
+#define R_EC_B163    11
+#define R_EC_B233    12
+#define R_EC_B283    13
+#define R_EC_B409    14
+#define R_EC_B571    15
+#define R_EC_X25519  16
+#ifndef OPENSSL_NO_EC
+static OPT_PAIR ecdsa_choices[] = {
+    {"ecdsap160", R_EC_P160},
+    {"ecdsap192", R_EC_P192},
+    {"ecdsap224", R_EC_P224},
+    {"ecdsap256", R_EC_P256},
+    {"ecdsap384", R_EC_P384},
+    {"ecdsap521", R_EC_P521},
+    {"ecdsak163", R_EC_K163},
+    {"ecdsak233", R_EC_K233},
+    {"ecdsak283", R_EC_K283},
+    {"ecdsak409", R_EC_K409},
+    {"ecdsak571", R_EC_K571},
+    {"ecdsab163", R_EC_B163},
+    {"ecdsab233", R_EC_B233},
+    {"ecdsab283", R_EC_B283},
+    {"ecdsab409", R_EC_B409},
+    {"ecdsab571", R_EC_B571},
+    {NULL}
+};
+
+static OPT_PAIR ecdh_choices[] = {
+    {"ecdhp160", R_EC_P160},
+    {"ecdhp192", R_EC_P192},
+    {"ecdhp224", R_EC_P224},
+    {"ecdhp256", R_EC_P256},
+    {"ecdhp384", R_EC_P384},
+    {"ecdhp521", R_EC_P521},
+    {"ecdhk163", R_EC_K163},
+    {"ecdhk233", R_EC_K233},
+    {"ecdhk283", R_EC_K283},
+    {"ecdhk409", R_EC_K409},
+    {"ecdhk571", R_EC_K571},
+    {"ecdhb163", R_EC_B163},
+    {"ecdhb233", R_EC_B233},
+    {"ecdhb283", R_EC_B283},
+    {"ecdhb409", R_EC_B409},
+    {"ecdhb571", R_EC_B571},
+    {"ecdhx25519", R_EC_X25519},
+    {NULL}
+};
+#endif
+
+#define R_SM2_P256   0
+#ifndef OPENSSL_NO_SM2
+static OPT_PAIR sm2sign_choices[] = {
+    {"sm2sign", R_SM2_P256},
+    {NULL}
+};
+
+static OPT_PAIR sm2enc_choices[] = {
+    {"sm2enc", R_SM2_P256},
+    {NULL}
+};
+#endif
+
+#define R_SM9_BN256  0
+#ifndef OPENSSL_NO_SM9
+static OPT_PAIR sm9sign_choices[] = {
+    {"sm9sign", R_SM9_BN256},
+    {NULL}
+};
+
+static OPT_PAIR sm9enc_choices[] = {
+    {"sm9enc", R_SM9_BN256},
+    {NULL}
+};
+#endif
+
+#ifndef SIGALRM
+# define COND(d) (count < (d))
+# define COUNT(d) (d)
+#else
+# define COND(unused_cond) (run && count<0x7fffffff)
+# define COUNT(d) (count)
+#endif                         /* SIGALRM */
+
+static int testnum;
+
+/* Nb of iterations to do per algorithm and key-size */
+static long c[ALGOR_NUM][SIZE_NUM];
+
+#ifndef OPENSSL_NO_MD2
+static int EVP_Digest_MD2_loop(void *args)
+{
+    loopargs_t *tempargs = *(loopargs_t **)args;
+    unsigned char *buf = tempargs->buf;
+    unsigned char md2[MD2_DIGEST_LENGTH];
+    int count;
+
+    for (count = 0; COND(c[D_MD2][testnum]); count++) {
+        if (!EVP_Digest(buf, (size_t)lengths[testnum], md2, NULL, EVP_md2(),
+                NULL))
+            return -1;
+    }
+    return count;
+}
+#endif
+
+#ifndef OPENSSL_NO_MDC2
+static int EVP_Digest_MDC2_loop(void *args)
+{
+    loopargs_t *tempargs = *(loopargs_t **)args;
+    unsigned char *buf = tempargs->buf;
+    unsigned char mdc2[MDC2_DIGEST_LENGTH];
+    int count;
+
+    for (count = 0; COND(c[D_MDC2][testnum]); count++) {
+        if (!EVP_Digest(buf, (size_t)lengths[testnum], mdc2, NULL, EVP_mdc2(),
+                NULL))
+            return -1;
+    }
+    return count;
+}
+#endif
+
+#ifndef OPENSSL_NO_MD4
+static int EVP_Digest_MD4_loop(void *args)
+{
+    loopargs_t *tempargs = *(loopargs_t **)args;
+    unsigned char *buf = tempargs->buf;
+    unsigned char md4[MD4_DIGEST_LENGTH];
+    int count;
+
+    for (count = 0; COND(c[D_MD4][testnum]); count++) {
+        if (!EVP_Digest(buf, (size_t)lengths[testnum], md4, NULL, EVP_md4(),
+                NULL))
+            return -1;
+    }
+    return count;
+}
+#endif
+
+#ifndef OPENSSL_NO_MD5
+static int MD5_loop(void *args)
+{
+    loopargs_t *tempargs = *(loopargs_t **)args;
+    unsigned char *buf = tempargs->buf;
+    unsigned char md5[MD5_DIGEST_LENGTH];
+    int count;
+    for (count = 0; COND(c[D_MD5][testnum]); count++)
+        MD5(buf, lengths[testnum], md5);
+    return count;
+}
+
+static int HMAC_loop(void *args)
+{
+    loopargs_t *tempargs = *(loopargs_t **)args;
+    unsigned char *buf = tempargs->buf;
+    HMAC_CTX *hctx = tempargs->hctx;
+    unsigned char hmac[MD5_DIGEST_LENGTH];
+    int count;
+
+    for (count = 0; COND(c[D_HMAC][testnum]); count++) {
+        HMAC_Init_ex(hctx, NULL, 0, NULL, NULL);
+        HMAC_Update(hctx, buf, lengths[testnum]);
+        HMAC_Final(hctx, hmac, NULL);
+    }
+    return count;
+}
+#endif
+
+#ifndef OPENSSL_NO_SM3
+static int SM3_loop(void *args)
+{
+    loopargs_t *tempargs = *(loopargs_t **)args;
+    unsigned char *buf = tempargs->buf;
+    unsigned char sm3md[SM3_DIGEST_LENGTH];
+    int count;
+    for (count = 0; COND(c[D_SM3][testnum]); count++)
+        sm3(buf, lengths[testnum], sm3md);
+    return count;
+}
+#endif
+
+#ifndef OPENSSL_NO_SHA
+static int SHA1_loop(void *args)
+{
+    loopargs_t *tempargs = *(loopargs_t **)args;
+    unsigned char *buf = tempargs->buf;
+    unsigned char sha[SHA_DIGEST_LENGTH];
+    int count;
+    for (count = 0; COND(c[D_SHA1][testnum]); count++)
+        SHA1(buf, lengths[testnum], sha);
+    return count;
+}
+
+static int SHA256_loop(void *args)
+{
+    loopargs_t *tempargs = *(loopargs_t **)args;
+    unsigned char *buf = tempargs->buf;
+    unsigned char sha256[SHA256_DIGEST_LENGTH];
+    int count;
+    for (count = 0; COND(c[D_SHA256][testnum]); count++)
+        SHA256(buf, lengths[testnum], sha256);
+    return count;
+}
+
+static int SHA512_loop(void *args)
+{
+    loopargs_t *tempargs = *(loopargs_t **)args;
+    unsigned char *buf = tempargs->buf;
+    unsigned char sha512[SHA512_DIGEST_LENGTH];
+    int count;
+    for (count = 0; COND(c[D_SHA512][testnum]); count++)
+        SHA512(buf, lengths[testnum], sha512);
+    return count;
+}
+#endif
+
+#ifndef OPENSSL_NO_WHIRLPOOL
+static int WHIRLPOOL_loop(void *args)
+{
+    loopargs_t *tempargs = *(loopargs_t **)args;
+    unsigned char *buf = tempargs->buf;
+    unsigned char whirlpool[WHIRLPOOL_DIGEST_LENGTH];
+    int count;
+    for (count = 0; COND(c[D_WHIRLPOOL][testnum]); count++)
+        WHIRLPOOL(buf, lengths[testnum], whirlpool);
+    return count;
+}
+#endif
+
+#ifndef OPENSSL_NO_RMD160
+static int EVP_Digest_RMD160_loop(void *args)
+{
+    loopargs_t *tempargs = *(loopargs_t **)args;
+    unsigned char *buf = tempargs->buf;
+    unsigned char rmd160[RIPEMD160_DIGEST_LENGTH];
+    int count;
+    for (count = 0; COND(c[D_RMD160][testnum]); count++) {
+        if (!EVP_Digest(buf, (size_t)lengths[testnum], &(rmd160[0]),
+                NULL, EVP_ripemd160(), NULL))
+            return -1;
+    }
+    return count;
+}
+#endif
+
+#ifndef OPENSSL_NO_RC4
+static RC4_KEY rc4_ks;
+static int RC4_loop(void *args)
+{
+    loopargs_t *tempargs = *(loopargs_t **)args;
+    unsigned char *buf = tempargs->buf;
+    int count;
+    for (count = 0; COND(c[D_RC4][testnum]); count++)
+        RC4(&rc4_ks, (size_t)lengths[testnum], buf, buf);
+    return count;
+}
+#endif
+
+#ifndef OPENSSL_NO_DES
+static unsigned char DES_iv[8];
+static DES_key_schedule sch;
+static DES_key_schedule sch2;
+static DES_key_schedule sch3;
+static int DES_ncbc_encrypt_loop(void *args)
+{
+    loopargs_t *tempargs = *(loopargs_t **)args;
+    unsigned char *buf = tempargs->buf;
+    int count;
+    for (count = 0; COND(c[D_CBC_DES][testnum]); count++)
+        DES_ncbc_encrypt(buf, buf, lengths[testnum], &sch,
+                &DES_iv, DES_ENCRYPT);
+    return count;
+}
+
+static int DES_ede3_cbc_encrypt_loop(void *args)
+{
+    loopargs_t *tempargs = *(loopargs_t **)args;
+    unsigned char *buf = tempargs->buf;
+    int count;
+    for (count = 0; COND(c[D_EDE3_DES][testnum]); count++)
+        DES_ede3_cbc_encrypt(buf, buf, lengths[testnum],
+                &sch, &sch2, &sch3,
+                &DES_iv, DES_ENCRYPT);
+    return count;
+}
+#endif
+
+#define MAX_BLOCK_SIZE 128
+
+static unsigned char iv[2 * MAX_BLOCK_SIZE / 8];
+
+#ifndef OPENSSL_NO_AES
+static AES_KEY aes_ks1, aes_ks2, aes_ks3;
+static int AES_cbc_128_encrypt_loop(void *args)
+{
+    loopargs_t *tempargs = *(loopargs_t **)args;
+    unsigned char *buf = tempargs->buf;
+    int count;
+    for (count = 0; COND(c[D_CBC_128_AES][testnum]); count++)
+        AES_cbc_encrypt(buf, buf,
+                (size_t)lengths[testnum], &aes_ks1,
+                iv, AES_ENCRYPT);
+    return count;
+}
+
+static int AES_cbc_192_encrypt_loop(void *args)
+{
+    loopargs_t *tempargs = *(loopargs_t **)args;
+    unsigned char *buf = tempargs->buf;
+    int count;
+    for (count = 0; COND(c[D_CBC_192_AES][testnum]); count++)
+        AES_cbc_encrypt(buf, buf,
+                (size_t)lengths[testnum], &aes_ks2,
+                iv, AES_ENCRYPT);
+    return count;
+}
+
+static int AES_cbc_256_encrypt_loop(void *args)
+{
+    loopargs_t *tempargs = *(loopargs_t **)args;
+    unsigned char *buf = tempargs->buf;
+    int count;
+    for (count = 0; COND(c[D_CBC_256_AES][testnum]); count++)
+        AES_cbc_encrypt(buf, buf,
+                (size_t)lengths[testnum], &aes_ks3,
+                iv, AES_ENCRYPT);
+    return count;
+}
+
+static int AES_ige_128_encrypt_loop(void *args)
+{
+    loopargs_t *tempargs = *(loopargs_t **)args;
+    unsigned char *buf = tempargs->buf;
+    unsigned char *buf2 = tempargs->buf2;
+    int count;
+    for (count = 0; COND(c[D_IGE_128_AES][testnum]); count++)
+        AES_ige_encrypt(buf, buf2,
+                (size_t)lengths[testnum], &aes_ks1,
+                iv, AES_ENCRYPT);
+    return count;
+}
+
+static int AES_ige_192_encrypt_loop(void *args)
+{
+    loopargs_t *tempargs = *(loopargs_t **)args;
+    unsigned char *buf = tempargs->buf;
+    unsigned char *buf2 = tempargs->buf2;
+    int count;
+    for (count = 0; COND(c[D_IGE_192_AES][testnum]); count++)
+        AES_ige_encrypt(buf, buf2,
+                (size_t)lengths[testnum], &aes_ks2,
+                iv, AES_ENCRYPT);
+    return count;
+}
+
+static int AES_ige_256_encrypt_loop(void *args)
+{
+    loopargs_t *tempargs = *(loopargs_t **)args;
+    unsigned char *buf = tempargs->buf;
+    unsigned char *buf2 = tempargs->buf2;
+    int count;
+    for (count = 0; COND(c[D_IGE_256_AES][testnum]); count++)
+        AES_ige_encrypt(buf, buf2,
+                (size_t)lengths[testnum], &aes_ks3,
+                iv, AES_ENCRYPT);
+    return count;
+}
+
+static int CRYPTO_gcm128_aad_loop(void *args)
+{
+    loopargs_t *tempargs = *(loopargs_t **)args;
+    unsigned char *buf = tempargs->buf;
+    GCM128_CONTEXT *gcm_ctx = tempargs->gcm_ctx;
+    int count;
+    for (count = 0; COND(c[D_GHASH][testnum]); count++)
+        CRYPTO_gcm128_aad(gcm_ctx, buf, lengths[testnum]);
+    return count;
+}
+#endif
+
+static long save_count = 0;
+static int decrypt = 0;
+static int EVP_Update_loop(void *args)
+{
+    loopargs_t *tempargs = *(loopargs_t **)args;
+    unsigned char *buf = tempargs->buf;
+    EVP_CIPHER_CTX *ctx = tempargs->ctx;
+    int outl, count;
+#ifndef SIGALRM
+    int nb_iter = save_count * 4 * lengths[0] / lengths[testnum];
+#endif
+    if (decrypt)
+        for (count = 0; COND(nb_iter); count++)
+            EVP_DecryptUpdate(ctx, buf, &outl, buf, lengths[testnum]);
+    else
+        for (count = 0; COND(nb_iter); count++)
+            EVP_EncryptUpdate(ctx, buf, &outl, buf, lengths[testnum]);
+    if (decrypt)
+        EVP_DecryptFinal_ex(ctx, buf, &outl);
+    else
+        EVP_EncryptFinal_ex(ctx, buf, &outl);
+    return count;
+}
+
+static const EVP_MD *evp_md = NULL;
+static int EVP_Digest_loop(void *args)
+{
+    loopargs_t *tempargs = *(loopargs_t **)args;
+    unsigned char *buf = tempargs->buf;
+    unsigned char md[EVP_MAX_MD_SIZE];
+    int count;
+#ifndef SIGALRM
+    int nb_iter = save_count * 4 * lengths[0] / lengths[testnum];
+#endif
+
+    for (count = 0; COND(nb_iter); count++) {
+        if (!EVP_Digest(buf, lengths[testnum], md, NULL, evp_md, NULL))
+            return -1;
+    }
+    return count;
+}
+
+#ifndef OPENSSL_NO_RSA
+static long rsa_c[RSA_NUM][2];  /* # RSA iteration test */
+
+static int RSA_sign_loop(void *args)
+{
+    loopargs_t *tempargs = *(loopargs_t **)args;
+    unsigned char *buf = tempargs->buf;
+    unsigned char *buf2 = tempargs->buf2;
+    unsigned int *rsa_num = &tempargs->siglen;
+    RSA **rsa_key = tempargs->rsa_key;
+    int ret, count;
+    for (count = 0; COND(rsa_c[testnum][0]); count++) {
+        ret = RSA_sign(NID_md5_sha1, buf, 36, buf2, rsa_num, rsa_key[testnum]);
+        if (ret == 0) {
+            BIO_printf(bio_err, "RSA sign failure\n");
+            ERR_print_errors(bio_err);
+            count = -1;
+            break;
+        }
+    }
+    return count;
+}
+
+static int RSA_verify_loop(void *args)
+{
+    loopargs_t *tempargs = *(loopargs_t **)args;
+    unsigned char *buf = tempargs->buf;
+    unsigned char *buf2 = tempargs->buf2;
+    unsigned int rsa_num = tempargs->siglen;
+    RSA **rsa_key = tempargs->rsa_key;
+    int ret, count;
+    for (count = 0; COND(rsa_c[testnum][1]); count++) {
+        ret = RSA_verify(NID_md5_sha1, buf, 36, buf2, rsa_num, rsa_key[testnum]);
+        if (ret <= 0) {
+            BIO_printf(bio_err, "RSA verify failure\n");
+            ERR_print_errors(bio_err);
+            count = -1;
+            break;
+        }
+    }
+    return count;
+}
+#endif
+
+#ifndef OPENSSL_NO_DSA
+static long dsa_c[DSA_NUM][2];
+static int DSA_sign_loop(void *args)
+{
+    loopargs_t *tempargs = *(loopargs_t **)args;
+    unsigned char *buf = tempargs->buf;
+    unsigned char *buf2 = tempargs->buf2;
+    DSA **dsa_key = tempargs->dsa_key;
+    unsigned int *siglen = &tempargs->siglen;
+    int ret, count;
+    for (count = 0; COND(dsa_c[testnum][0]); count++) {
+        ret = DSA_sign(0, buf, 20, buf2, siglen, dsa_key[testnum]);
+        if (ret == 0) {
+            BIO_printf(bio_err, "DSA sign failure\n");
+            ERR_print_errors(bio_err);
+            count = -1;
+            break;
+        }
+    }
+    return count;
+}
+
+static int DSA_verify_loop(void *args)
+{
+    loopargs_t *tempargs = *(loopargs_t **)args;
+    unsigned char *buf = tempargs->buf;
+    unsigned char *buf2 = tempargs->buf2;
+    DSA **dsa_key = tempargs->dsa_key;
+    unsigned int siglen = tempargs->siglen;
+    int ret, count;
+    for (count = 0; COND(dsa_c[testnum][1]); count++) {
+        ret = DSA_verify(0, buf, 20, buf2, siglen, dsa_key[testnum]);
+        if (ret <= 0) {
+            BIO_printf(bio_err, "DSA verify failure\n");
+            ERR_print_errors(bio_err);
+            count = -1;
+            break;
+        }
+    }
+    return count;
+}
+#endif
+
+#ifndef OPENSSL_NO_SM9
+static long sm9sign_c[SM9_NUM][2];
+
+static int SM9_sign_loop(void *args)
+{
+    loopargs_t *tempargs = *(loopargs_t **)args;
+    unsigned char *buf = tempargs->buf;
+    SM9PrivateKey **sm9 = tempargs->sm9sk;
+    unsigned char *sm9sig = tempargs->buf2;
+    unsigned int *sm9siglen = &tempargs->siglen;
+    int ret, count;
+    for (count = 0; COND(sm9sign_c[testnum][0]); count++) {
+        ret = SM9_sign(NID_sm3, buf, 32, sm9sig, sm9siglen, sm9[testnum]);
+        if (ret == 0) {
+            BIO_printf(bio_err, "SM9 sign failure\n");
+            ERR_print_errors(bio_err);
+            count = -1;
+            break;
+        }
+    }
+    return count;
+}
+
+static char* sm9verify_id = "sm9_verify";
+static size_t sm9verify_idlen = 10;
+static int SM9_verify_loop(void *args)
+{
+    loopargs_t *tempargs = *(loopargs_t **)args;
+    unsigned char *buf = tempargs->buf;
+    SM9PublicParameters **sm9 = tempargs->sm9mpk;
+    unsigned char *sm9sig = tempargs->buf2;
+    unsigned int sm9siglen = tempargs->siglen;
+    int ret, count;
+    for (count = 0; COND(sm9sign_c[testnum][1]); count++) {
+        ret = SM9_verify(NID_sm3, buf, 32, sm9sig, sm9siglen, sm9[testnum], sm9verify_id, sm9verify_idlen);
+        if (ret != 1) {
+            BIO_printf(bio_err, "SM9 verify failure\n");
+            ERR_print_errors(bio_err);
+            count = -1;
+            break;
+        }
+    }
+    return count;
+}
+
+static long sm9enc_c[SM9_NUM][2];
+static char* sm9enc_id = "sm9_enc";
+static size_t sm9enc_idlen = 7;
+static int SM9_encrypt_loop(void *args)
+{
+	loopargs_t *tempargs = *(loopargs_t **)args;
+    unsigned char *buf = tempargs->buf;
+    SM9PublicParameters **sm9 = tempargs->sm9mpk;
+    unsigned char *sm9cipher = tempargs->buf2;
+    size_t *sm9cipherlen = &tempargs->cipherlen;
+    int ret, count;
+    for (count = 0; COND(sm9enc_c[testnum][0]); count++) {
+        *sm9cipherlen = BUFSIZE;
+        ret = SM9_encrypt(NID_sm3, buf, 32, sm9cipher,
+                          sm9cipherlen, sm9[testnum], sm9enc_id, sm9enc_idlen);
+        if (ret == 0) {
+            BIO_printf(bio_err, "SM9 encrypt failure\n");
+            ERR_print_errors(bio_err);
+            count = -1;
+            break;
+        }
+    }
+    return count;
+}
+
+static int SM9_decrypt_loop(void *args)
+{
+	loopargs_t *tempargs = *(loopargs_t **)args;
+    unsigned char *buf = tempargs->buf;
+    SM9PrivateKey **sm9 = tempargs->sm9sk;
+    unsigned char *sm9cipher = tempargs->buf2;
+    size_t sm9cipherlen = tempargs->cipherlen;
+    int ret, count;
+    for (count = 0; COND(sm9enc_c[testnum][0]); count++) {
+        size_t len = sm9cipherlen;				
+	ret = SM9_decrypt(NID_sm3, sm9cipher, sm9cipherlen,
+                          buf, &len, sm9[testnum]);
+        if (ret == 0) {
+            BIO_printf(bio_err, "SM9 decrypt failure\n");
+            ERR_print_errors(bio_err);
+            count = -1;
+            break;
+        }
+    }
+    return count;
+}
+#endif
+
+#ifndef OPENSSL_NO_SM2
+static long sm2sign_c[SM2_NUM][2];
+
+static int SM2_sign_loop(void *args)
+{
+    loopargs_t *tempargs = *(loopargs_t **)args;
+    unsigned char *buf = tempargs->buf;
+    EC_KEY **sm2 = tempargs->sm2;
+    unsigned char *sm2sig = tempargs->buf2;
+    unsigned int *sm2siglen = &tempargs->siglen;
+    int ret, count;
+    for (count = 0; COND(sm2sign_c[testnum][0]); count++) {
+        ret = SM2_sign(0, buf, 32, sm2sig, sm2siglen, sm2[testnum]);
+        if (ret == 0) {
+            BIO_printf(bio_err, "SM2 sign failure\n");
+            ERR_print_errors(bio_err);
+            count = -1;
+            break;
+        }
+    }
+    return count;
+}
+
+static int SM2_verify_loop(void *args)
+{
+    loopargs_t *tempargs = *(loopargs_t **)args;
+    unsigned char *buf = tempargs->buf;
+    EC_KEY **sm2 = tempargs->sm2;
+    unsigned char *sm2sig = tempargs->buf2;
+    unsigned int sm2siglen = tempargs->siglen;
+    int ret, count;
+    for (count = 0; COND(sm2sign_c[testnum][1]); count++) {
+        ret = SM2_verify(0, buf, 32, sm2sig, sm2siglen, sm2[testnum]);
+        if (ret != 1) {
+            BIO_printf(bio_err, "SM2 verify failure\n");
+            ERR_print_errors(bio_err);
+            count = -1;
+            break;
+        }
+    }
+    return count;
+}
+
+static long sm2enc_c[SM2_NUM][2];
+static int SM2_encrypt_loop(void *args)
+{
+    loopargs_t *tempargs = *(loopargs_t **)args;
+    unsigned char *buf = tempargs->buf;
+    EC_KEY **sm2 = tempargs->sm2;
+    unsigned char *sm2cipher = tempargs->buf2;
+    size_t *sm2cipherlen = &tempargs->cipherlen;
+    int ret, count;
+    for (count = 0; COND(sm2enc_c[testnum][0]); count++) {
+        *sm2cipherlen = BUFSIZE;
+        ret = SM2_encrypt(NID_sm3, buf, 32, sm2cipher,
+                          sm2cipherlen, sm2[testnum]);
+        if (ret == 0) {
+            BIO_printf(bio_err, "SM2 encrypt failure\n");
+            ERR_print_errors(bio_err);
+            count = -1;
+            break;
+        }
+    }
+    return count;
+}
+
+static int SM2_decrypt_loop(void *args)
+{
+    loopargs_t *tempargs = *(loopargs_t **)args;
+    unsigned char *buf = tempargs->buf;
+    EC_KEY **sm2 = tempargs->sm2;
+    unsigned char *sm2cipher = tempargs->buf2;
+    size_t sm2cipherlen = tempargs->cipherlen;
+    int ret, count;
+    for (count = 0; COND(sm2enc_c[testnum][0]); count++) {
+        size_t len = sm2cipherlen;				
+	ret = SM2_decrypt(NID_sm3, sm2cipher, sm2cipherlen,
+                          buf, &len, sm2[testnum]);
+        if (ret == 0) {
+            BIO_printf(bio_err, "SM2 decrypt failure\n");
+            ERR_print_errors(bio_err);
+            count = -1;
+            break;
+        }
+    }
+    return count;
+}
+#endif
+
+#ifndef OPENSSL_NO_EC
+static long ecdsa_c[EC_NUM][2];
+static int ECDSA_sign_loop(void *args)
+{
+    loopargs_t *tempargs = *(loopargs_t **)args;
+    unsigned char *buf = tempargs->buf;
+    EC_KEY **ecdsa = tempargs->ecdsa;
+    unsigned char *ecdsasig = tempargs->buf2;
+    unsigned int *ecdsasiglen = &tempargs->siglen;
+    int ret, count;
+    for (count = 0; COND(ecdsa_c[testnum][0]); count++) {
+        ret = ECDSA_sign(0, buf, 20,
+                ecdsasig, ecdsasiglen, ecdsa[testnum]);
+        if (ret == 0) {
+            BIO_printf(bio_err, "ECDSA sign failure\n");
+            ERR_print_errors(bio_err);
+            count = -1;
+            break;
+        }
+    }
+    return count;
+}
+
+static int ECDSA_verify_loop(void *args)
+{
+    loopargs_t *tempargs = *(loopargs_t **)args;
+    unsigned char *buf = tempargs->buf;
+    EC_KEY **ecdsa = tempargs->ecdsa;
+    unsigned char *ecdsasig = tempargs->buf2;
+    unsigned int ecdsasiglen = tempargs->siglen;
+    int ret, count;
+    for (count = 0; COND(ecdsa_c[testnum][1]); count++) {
+        ret = ECDSA_verify(0, buf, 20, ecdsasig, ecdsasiglen,
+                ecdsa[testnum]);
+        if (ret != 1) {
+            BIO_printf(bio_err, "ECDSA verify failure\n");
+            ERR_print_errors(bio_err);
+            count = -1;
+            break;
+          }
+    }
+    return count;
+}
+
+/* ******************************************************************** */
+static long ecdh_c[EC_NUM][1];
+
+static int ECDH_compute_key_loop(void *args)
+{
+    loopargs_t *tempargs = *(loopargs_t **)args;
+    EC_KEY **ecdh_a = tempargs->ecdh_a;
+    EC_KEY **ecdh_b = tempargs->ecdh_b;
+    unsigned char *secret_a = tempargs->secret_a;
+    int count;
+    size_t outlen = tempargs->outlen;
+    kdf_fn kdf = tempargs->kdf;
+
+    for (count = 0; COND(ecdh_c[testnum][0]); count++) {
+        ECDH_compute_key(secret_a, outlen,
+                EC_KEY_get0_public_key(ecdh_b[testnum]),
+                ecdh_a[testnum], kdf);
+    }
+    return count;
+}
+
+static const size_t KDF1_SHA1_len = 20;
+static void *KDF1_SHA1(const void *in, size_t inlen, void *out,
+                       size_t *outlen)
+{
+# ifndef OPENSSL_NO_SHA
+    if (*outlen < SHA_DIGEST_LENGTH)
+        return NULL;
+    *outlen = SHA_DIGEST_LENGTH;
+    return SHA1(in, inlen, out);
+# else
+    *outlen = 20;
+    memcpy(out, in, 20);
+    return in;
+# endif
+}
+#endif                          /* OPENSSL_NO_EC */
+
+static int run_benchmark(int async_jobs,
+                         int (*loop_function)(void *), loopargs_t *loopargs)
+{
+    int job_op_count = 0;
+    int total_op_count = 0;
+    int num_inprogress = 0;
+    int error = 0, i = 0, ret = 0;
+    OSSL_ASYNC_FD job_fd = 0;
+    size_t num_job_fds = 0;
+
+    run = 1;
+
+    if (async_jobs == 0) {
+        return loop_function((void *)&loopargs);
+    }
+
+    for (i = 0; i < async_jobs && !error; i++) {
+        loopargs_t *looparg_item = loopargs + i;
+
+        /* Copy pointer content (looparg_t item address) into async context */
+        ret = ASYNC_start_job(&loopargs[i].inprogress_job, loopargs[i].wait_ctx,
+                              &job_op_count, loop_function,
+                              (void *)&looparg_item, sizeof(looparg_item));
+        switch (ret) {
+        case ASYNC_PAUSE:
+            ++num_inprogress;
+            break;
+        case ASYNC_FINISH:
+            if (job_op_count == -1) {
+                error = 1;
+            } else {
+                total_op_count += job_op_count;
+            }
+            break;
+        case ASYNC_NO_JOBS:
+        case ASYNC_ERR:
+            BIO_printf(bio_err, "Failure in the job\n");
+            ERR_print_errors(bio_err);
+            error = 1;
+            break;
+        }
+    }
+
+    while (num_inprogress > 0) {
+#if defined(OPENSSL_SYS_WINDOWS)
+        DWORD avail = 0;
+#elif defined(OPENSSL_SYS_UNIX)
+        int select_result = 0;
+        OSSL_ASYNC_FD max_fd = 0;
+        fd_set waitfdset;
+
+        FD_ZERO(&waitfdset);
+
+        for (i = 0; i < async_jobs && num_inprogress > 0; i++) {
+            if (loopargs[i].inprogress_job == NULL)
+                continue;
+
+            if (!ASYNC_WAIT_CTX_get_all_fds(loopargs[i].wait_ctx, NULL, &num_job_fds)
+                    || num_job_fds > 1) {
+                BIO_printf(bio_err, "Too many fds in ASYNC_WAIT_CTX\n");
+                ERR_print_errors(bio_err);
+                error = 1;
+                break;
+            }
+            ASYNC_WAIT_CTX_get_all_fds(loopargs[i].wait_ctx, &job_fd, &num_job_fds);
+            FD_SET(job_fd, &waitfdset);
+            if (job_fd > max_fd)
+                max_fd = job_fd;
+        }
+
+        if (max_fd >= (OSSL_ASYNC_FD)FD_SETSIZE) {
+            BIO_printf(bio_err,
+                    "Error: max_fd (%d) must be smaller than FD_SETSIZE (%d). "
+                    "Decrease the value of async_jobs\n",
+                    max_fd, FD_SETSIZE);
+            ERR_print_errors(bio_err);
+            error = 1;
+            break;
+        }
+
+        select_result = select(max_fd + 1, &waitfdset, NULL, NULL, NULL);
+        if (select_result == -1 && errno == EINTR)
+            continue;
+
+        if (select_result == -1) {
+            BIO_printf(bio_err, "Failure in the select\n");
+            ERR_print_errors(bio_err);
+            error = 1;
+            break;
+        }
+
+        if (select_result == 0)
+            continue;
+#endif
+
+        for (i = 0; i < async_jobs; i++) {
+            if (loopargs[i].inprogress_job == NULL)
+                continue;
+
+            if (!ASYNC_WAIT_CTX_get_all_fds(loopargs[i].wait_ctx, NULL, &num_job_fds)
+                    || num_job_fds > 1) {
+                BIO_printf(bio_err, "Too many fds in ASYNC_WAIT_CTX\n");
+                ERR_print_errors(bio_err);
+                error = 1;
+                break;
+            }
+            ASYNC_WAIT_CTX_get_all_fds(loopargs[i].wait_ctx, &job_fd, &num_job_fds);
+
+#if defined(OPENSSL_SYS_UNIX)
+            if (num_job_fds == 1 && !FD_ISSET(job_fd, &waitfdset))
+                continue;
+#elif defined(OPENSSL_SYS_WINDOWS)
+            if (num_job_fds == 1
+                && !PeekNamedPipe(job_fd, NULL, 0, NULL, &avail, NULL)
+                && avail > 0)
+                continue;
+#endif
+
+            ret = ASYNC_start_job(&loopargs[i].inprogress_job,
+                    loopargs[i].wait_ctx, &job_op_count, loop_function,
+                    (void *)(loopargs + i), sizeof(loopargs_t));
+            switch (ret) {
+            case ASYNC_PAUSE:
+                break;
+            case ASYNC_FINISH:
+                if (job_op_count == -1) {
+                    error = 1;
+                } else {
+                    total_op_count += job_op_count;
+                }
+                --num_inprogress;
+                loopargs[i].inprogress_job = NULL;
+                break;
+            case ASYNC_NO_JOBS:
+            case ASYNC_ERR:
+                --num_inprogress;
+                loopargs[i].inprogress_job = NULL;
+                BIO_printf(bio_err, "Failure in the job\n");
+                ERR_print_errors(bio_err);
+                error = 1;
+                break;
+            }
+        }
+    }
+
+    return error ? -1 : total_op_count;
+}
+
+int speed_main(int argc, char **argv)
+{
+    ENGINE *e = NULL;
+    loopargs_t *loopargs = NULL;
+    int async_init = 0;
+    int loopargs_len = 0;
+    char *prog;
+    const char *engine_id = NULL;
+    const EVP_CIPHER *evp_cipher = NULL;
+    double d = 0.0;
+    OPTION_CHOICE o;
+    int multiblock = 0, pr_header = 0;
+    int nopre = 0;
+    int doit[ALGOR_NUM] = { 0 };
+    int ret = 1, i, k, misalign = 0;
+    long count = 0;
+#ifndef NO_FORK
+    int multi = 0;
+#endif
+    int async_jobs = 0;
+#if !defined(OPENSSL_NO_RSA) || !defined(OPENSSL_NO_DSA) \
+    || !defined(OPENSSL_NO_EC)
+    long rsa_count = 1;
+#endif
+
+    /* What follows are the buffers and key material. */
+#ifndef OPENSSL_NO_RC5
+    RC5_32_KEY rc5_ks;
+#endif
+#ifndef OPENSSL_NO_RC2
+    RC2_KEY rc2_ks;
+#endif
+#ifndef OPENSSL_NO_IDEA
+    IDEA_KEY_SCHEDULE idea_ks;
+#endif
+#ifndef OPENSSL_NO_SEED
+    SEED_KEY_SCHEDULE seed_ks;
+#endif
+#ifndef OPENSSL_NO_SMS4
+    sms4_key_t sms4_ks;
+#endif
+#ifndef OPENSSL_NO_ZUC
+    ZUC_KEY zuc_ks;
+    ZUC256_KEY zuc256_ks;
+#endif
+#ifndef OPENSSL_NO_BF
+    BF_KEY bf_ks;
+#endif
+#ifndef OPENSSL_NO_CAST
+    CAST_KEY cast_ks;
+#endif
+    static const unsigned char key16[16] = {
+        0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0,
+        0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12
+    };
+#ifndef OPENSSL_NO_AES
+    static const unsigned char key24[24] = {
+        0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0,
+        0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12,
+        0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34
+    };
+    static const unsigned char key32[32] = {
+        0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0,
+        0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12,
+        0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34,
+        0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56
+    };
+#endif
+#ifndef OPENSSL_NO_CAMELLIA
+    static const unsigned char ckey24[24] = {
+        0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0,
+        0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12,
+        0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34
+    };
+    static const unsigned char ckey32[32] = {
+        0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0,
+        0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12,
+        0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34,
+        0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56
+    };
+    CAMELLIA_KEY camellia_ks1, camellia_ks2, camellia_ks3;
+#endif
+#ifndef OPENSSL_NO_DES
+    static DES_cblock key = {
+        0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0
+    };
+    static DES_cblock key2 = {
+        0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12
+    };
+    static DES_cblock key3 = {
+        0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34
+    };
+#endif
+#ifndef OPENSSL_NO_RSA
+    static const unsigned int rsa_bits[RSA_NUM] = {
+        512, 1024, 2048, 3072, 4096, 7680, 15360
+    };
+    static const unsigned char *rsa_data[RSA_NUM] = {
+        test512, test1024, test2048, test3072, test4096, test7680, test15360
+    };
+    static const int rsa_data_length[RSA_NUM] = {
+        sizeof(test512), sizeof(test1024),
+        sizeof(test2048), sizeof(test3072),
+        sizeof(test4096), sizeof(test7680),
+        sizeof(test15360)
+    };
+    int rsa_doit[RSA_NUM] = { 0 };
+#endif
+#ifndef OPENSSL_NO_DSA
+    static const unsigned int dsa_bits[DSA_NUM] = { 512, 1024, 2048 };
+    int dsa_doit[DSA_NUM] = { 0 };
+#endif
+#ifndef OPENSSL_NO_EC
+    /*
+     * We only test over the following curves as they are representative, To
+     * add tests over more curves, simply add the curve NID and curve name to
+     * the following arrays and increase the EC_NUM value accordingly.
+     */
+    static const unsigned int test_curves[EC_NUM] = {
+        /* Prime Curves */
+        NID_secp160r1, NID_X9_62_prime192v1, NID_secp224r1,
+        NID_X9_62_prime256v1, NID_secp384r1, NID_secp521r1,
+        /* Binary Curves */
+        NID_sect163k1, NID_sect233k1, NID_sect283k1,
+        NID_sect409k1, NID_sect571k1, NID_sect163r2,
+        NID_sect233r1, NID_sect283r1, NID_sect409r1,
+        NID_sect571r1,
+        /* Other */
+        NID_X25519,
+    };
+    static const char *test_curves_names[EC_NUM] = {
+        /* Prime Curves */
+        "secp160r1", "nistp192", "nistp224",
+        "nistp256", "nistp384", "nistp521",
+        /* Binary Curves */
+        "nistk163", "nistk233", "nistk283",
+        "nistk409", "nistk571", "nistb163",
+        "nistb233", "nistb283", "nistb409",
+        "nistb571",
+        /* Other */
+        "X25519",
+    };
+    static const int test_curves_bits[EC_NUM] = {
+        160, 192, 224,
+        256, 384, 521,
+        163, 233, 283,
+        409, 571, 163,
+        233, 283, 409,
+        571, 253 /* X25519 */,
+    };
+
+    int ecdsa_doit[EC_NUM] = { 0 };
+    int ecdh_doit[EC_NUM] = { 0 };
+#endif /* OPENSSL_NO_EC */
+#ifndef OPENSSL_NO_SM2
+    static const unsigned int test_sm2_curves[SM2_NUM] = {
+        NID_sm2p256v1,
+    };
+    static const char *test_sm2_curves_names[SM2_NUM] = {
+        "sm2p256v1",
+    };
+    static const int test_sm2_curves_bits[SM2_NUM] = {
+        256,
+    };
+    int sm2sign_doit[SM2_NUM] = { 0 };
+    int sm2enc_doit[SM2_NUM] = { 0 };
+#endif
+#ifndef OPENSSL_NO_SM9
+
+//do we need this ?			
+    static const unsigned int test_sm9_curves[SM9_NUM] = {
+        NID_sm9bn256v1,
+    };
+    static const char *test_sm9_curves_names[SM9_NUM] = {
+        "sm9bn256v1",
+    };
+    static const int test_sm9_curves_bits[SM9_NUM] = {
+        256,
+    };
+    static const int test_sm9_scheme[SM9_NUM] = {
+        NID_sm9sign,
+    };
+    static const int test_sm9_hash1[SM9_NUM] = {
+        NID_sm9hash1_with_sm3,
+    };
+    int sm9sign_doit[SM9_NUM] = { 0 };
+    int sm9enc_doit[SM9_NUM] = { 0 };
+#endif
+
+    prog = opt_init(argc, argv, speed_options);
+    while ((o = opt_next()) != OPT_EOF) {
+        switch (o) {
+        case OPT_EOF:
+        case OPT_ERR:
+ opterr:
+            BIO_printf(bio_err, "%s: Use -help for summary.\n", prog);
+            goto end;
+        case OPT_HELP:
+            opt_help(speed_options);
+            ret = 0;
+            goto end;
+        case OPT_ELAPSED:
+            usertime = 0;
+            break;
+        case OPT_EVP:
+            evp_cipher = EVP_get_cipherbyname(opt_arg());
+            if (evp_cipher == NULL)
+                evp_md = EVP_get_digestbyname(opt_arg());
+            if (evp_cipher == NULL && evp_md == NULL) {
+                BIO_printf(bio_err,
+                           "%s: %s is an unknown cipher or digest\n",
+                           prog, opt_arg());
+                goto end;
+            }
+            doit[D_EVP] = 1;
+            break;
+        case OPT_DECRYPT:
+            decrypt = 1;
+            break;
+        case OPT_ENGINE:
+            /*
+             * In a forked execution, an engine might need to be
+             * initialised by each child process, not by the parent.
+             * So store the name here and run setup_engine() later on.
+             */
+            engine_id = opt_arg();
+            break;
+        case OPT_MULTI:
+#ifndef NO_FORK
+            multi = atoi(opt_arg());
+#endif
+            break;
+        case OPT_ASYNCJOBS:
+#ifndef OPENSSL_NO_ASYNC
+            async_jobs = atoi(opt_arg());
+            if (!ASYNC_is_capable()) {
+                BIO_printf(bio_err,
+                           "%s: async_jobs specified but async not supported\n",
+                           prog);
+                goto opterr;
+            }
+#endif
+            break;
+        case OPT_MISALIGN:
+            if (!opt_int(opt_arg(), &misalign))
+                goto end;
+            if (misalign > MISALIGN) {
+                BIO_printf(bio_err,
+                           "%s: Maximum offset is %d\n", prog, MISALIGN);
+                goto opterr;
+            }
+            break;
+        case OPT_MR:
+            mr = 1;
+            break;
+        case OPT_MB:
+            multiblock = 1;
+#ifdef OPENSSL_NO_MULTIBLOCK
+            BIO_printf(bio_err,
+                       "%s: -mb specified but multi-block support is disabled\n",
+                       prog);
+            goto end;
+#endif
+            break;
+        case OPT_NOPRE:
+            nopre = 1;
+            break;
+        }
+    }
+    argc = opt_num_rest();
+    argv = opt_rest();
+
+    /* Remaining arguments are algorithms. */
+    for ( ; *argv; argv++) {
+        if (found(*argv, doit_choices, &i)) {
+            doit[i] = 1;
+            continue;
+        }
+#ifndef OPENSSL_NO_DES
+        if (strcmp(*argv, "des") == 0) {
+            doit[D_CBC_DES] = doit[D_EDE3_DES] = 1;
+            continue;
+        }
+#endif
+#ifndef OPENSSL_NO_SHA
+        if (strcmp(*argv, "sha") == 0) {
+            doit[D_SHA1] = doit[D_SHA256] = doit[D_SHA512] = 1;
+            continue;
+        }
+#endif
+#ifndef OPENSSL_NO_RSA
+# ifndef RSA_NULL
+        if (strcmp(*argv, "openssl") == 0) {
+            RSA_set_default_method(RSA_PKCS1_OpenSSL());
+            continue;
+        }
+# endif
+        if (strcmp(*argv, "rsa") == 0) {
+            rsa_doit[R_RSA_512] = rsa_doit[R_RSA_1024] =
+                rsa_doit[R_RSA_2048] = rsa_doit[R_RSA_3072] =
+                rsa_doit[R_RSA_4096] = rsa_doit[R_RSA_7680] =
+                rsa_doit[R_RSA_15360] = 1;
+            continue;
+        }
+        if (found(*argv, rsa_choices, &i)) {
+            rsa_doit[i] = 1;
+            continue;
+        }
+#endif
+#ifndef OPENSSL_NO_DSA
+        if (strcmp(*argv, "dsa") == 0) {
+            dsa_doit[R_DSA_512] = dsa_doit[R_DSA_1024] =
+                dsa_doit[R_DSA_2048] = 1;
+            continue;
+        }
+        if (found(*argv, dsa_choices, &i)) {
+            dsa_doit[i] = 2;
+            continue;
+        }
+#endif
+#ifndef OPENSSL_NO_AES
+        if (strcmp(*argv, "aes") == 0) {
+            doit[D_CBC_128_AES] = doit[D_CBC_192_AES] =
+                doit[D_CBC_256_AES] = 1;
+            continue;
+        }
+#endif
+#ifndef OPENSSL_NO_CAMELLIA
+        if (strcmp(*argv, "camellia") == 0) {
+            doit[D_CBC_128_CML] = doit[D_CBC_192_CML] =
+                doit[D_CBC_256_CML] = 1;
+            continue;
+        }
+#endif
+#ifndef OPENSSL_NO_EC
+        if (strcmp(*argv, "ecdsa") == 0) {
+            for (i = 0; i < EC_NUM; i++)
+                ecdsa_doit[i] = 1;
+            continue;
+        }
+        if (found(*argv, ecdsa_choices, &i)) {
+            ecdsa_doit[i] = 2;
+            continue;
+        }
+        if (strcmp(*argv, "ecdh") == 0) {
+            for (i = 0; i < EC_NUM; i++)
+                ecdh_doit[i] = 1;
+            continue;
+        }
+        if (found(*argv, ecdh_choices, &i)) {
+            ecdh_doit[i] = 2;
+            continue;
+        }
+#endif
+#ifndef OPENSSL_NO_SM2
+        if (strcmp(*argv, "sm2") == 0) {
+            for (i = 0; i < SM2_NUM; i++)
+                sm2sign_doit[i] = sm2enc_doit[i] = 1;
+            continue;
+        }
+        if (strcmp(*argv, "sm2sign") == 0) {
+            for (i = 0; i < SM2_NUM; i++)
+                sm2sign_doit[i] = 1;
+            continue;
+        }
+        if (found(*argv, sm2sign_choices, &i)) {
+            sm2sign_doit[i] = 2;
+            continue;
+        }
+        if (strcmp(*argv, "sm2enc") == 0) {
+            for (i = 0; i < SM2_NUM; i++)
+                sm2enc_doit[i] = 1;
+            continue;
+        }
+        if (found(*argv, sm2enc_choices, &i)) {
+            sm2enc_doit[i] = 2;
+            continue;
+        }
+#endif
+#ifndef OPENSSL_NO_SM9
+        if (strcmp(*argv, "sm9") == 0) {
+            for (i = 0; i < SM9_NUM; i++)
+                sm9sign_doit[i] = sm9enc_doit[i] = 1;
+            continue;
+        }
+        if (strcmp(*argv, "sm9sign") == 0) {
+            for (i = 0; i < SM9_NUM; i++)
+                sm9sign_doit[i] = 1;
+            continue;
+        }
+        if (found(*argv, sm9sign_choices, &i)) {
+            sm9sign_doit[i] = 2;
+            continue;
+        }
+        if (strcmp(*argv, "sm9encrypt") == 0) {
+            for (i = 0; i < SM9_NUM; i++)
+                sm9enc_doit[i] = 1;
+            continue;
+        }
+        if (found(*argv, sm9enc_choices, &i)) {
+            sm9enc_doit[i] = 2;
+            continue;
+        }
+#endif
+        BIO_printf(bio_err, "%s: Unknown algorithm %s\n", prog, *argv);
+        goto end;
+    }
+
+    /* Initialize the job pool if async mode is enabled */
+    if (async_jobs > 0) {
+        async_init = ASYNC_init_thread(async_jobs, async_jobs);
+        if (!async_init) {
+            BIO_printf(bio_err, "Error creating the ASYNC job pool\n");
+            goto end;
+        }
+    }
+
+    loopargs_len = (async_jobs == 0 ? 1 : async_jobs);
+    loopargs = app_malloc(loopargs_len * sizeof(loopargs_t), "array of loopargs");
+    memset(loopargs, 0, loopargs_len * sizeof(loopargs_t));
+
+    for (i = 0; i < loopargs_len; i++) {
+        if (async_jobs > 0) {
+            loopargs[i].wait_ctx = ASYNC_WAIT_CTX_new();
+            if (loopargs[i].wait_ctx == NULL) {
+                BIO_printf(bio_err, "Error creating the ASYNC_WAIT_CTX\n");
+                goto end;
+            }
+        }
+
+        loopargs[i].buf_malloc = app_malloc((int)BUFSIZE + MAX_MISALIGNMENT + 1, "input buffer");
+        loopargs[i].buf2_malloc = app_malloc((int)BUFSIZE + MAX_MISALIGNMENT + 1, "input buffer");
+        /* Align the start of buffers on a 64 byte boundary */
+        loopargs[i].buf = loopargs[i].buf_malloc + misalign;
+        loopargs[i].buf2 = loopargs[i].buf2_malloc + misalign;
+#ifndef OPENSSL_NO_EC
+        loopargs[i].secret_a = app_malloc(MAX_ECDH_SIZE, "ECDH secret a");
+        loopargs[i].secret_b = app_malloc(MAX_ECDH_SIZE, "ECDH secret b");
+#endif
+#ifndef OPENSSL_NO_SM2
+/*
+        loopargs[i].sm2dh_a = app_malloc(MAX_ECDH_SIZE, "SM2DH secret a");
+        loopargs[i].sm2dh_b = app_malloc(MAX_ECDH_SIZE, "SM2DH secret b");
+*/
+#endif
+    }
+
+#ifndef NO_FORK
+    if (multi && do_multi(multi))
+        goto show_res;
+#endif
+
+    /* Initialize the engine after the fork */
+    e = setup_engine(engine_id, 0);
+
+    /* No parameters; turn on everything. */
+    if ((argc == 0) && !doit[D_EVP]) {
+        for (i = 0; i < ALGOR_NUM; i++)
+            if (i != D_EVP)
+                doit[i] = 1;
+#ifndef OPENSSL_NO_RSA
+        for (i = 0; i < RSA_NUM; i++)
+            rsa_doit[i] = 1;
+#endif
+#ifndef OPENSSL_NO_DSA
+        for (i = 0; i < DSA_NUM; i++)
+            dsa_doit[i] = 1;
+#endif
+#ifndef OPENSSL_NO_EC
+        for (i = 0; i < EC_NUM; i++)
+            ecdsa_doit[i] = 1;
+        for (i = 0; i < EC_NUM; i++)
+            ecdh_doit[i] = 1;
+#endif
+#ifndef OPENSSL_NO_SM2
+        for (i = 0; i < SM2_NUM; i++)
+            sm2sign_doit[i] = 1;
+        for (i = 0; i < SM2_NUM; i++)
+            sm2enc_doit[i] = 1;
+#endif
+#ifndef OPENSSL_NO_SM9
+        for (i = 0; i < SM9_NUM; i++)
+            sm9sign_doit[i] = 1;
+        for (i = 0; i < SM9_NUM; i++)
+            sm9enc_doit[i] = 1;
+#endif
+    }
+    for (i = 0; i < ALGOR_NUM; i++)
+        if (doit[i])
+            pr_header++;
+
+    if (usertime == 0 && !mr)
+        BIO_printf(bio_err,
+                   "You have chosen to measure elapsed time "
+                   "instead of user CPU time.\n");
+
+#ifndef OPENSSL_NO_RSA
+    for (i = 0; i < loopargs_len; i++) {
+        for (k = 0; k < RSA_NUM; k++) {
+            const unsigned char *p;
+
+            p = rsa_data[k];
+            loopargs[i].rsa_key[k] = d2i_RSAPrivateKey(NULL, &p, rsa_data_length[k]);
+            if (loopargs[i].rsa_key[k] == NULL) {
+                BIO_printf(bio_err, "internal error loading RSA key number %d\n",
+                        k);
+                goto end;
+            }
+        }
+    }
+#endif
+#ifndef OPENSSL_NO_DSA
+    for (i = 0; i < loopargs_len; i++) {
+        loopargs[i].dsa_key[0] = get_dsa512();
+        loopargs[i].dsa_key[1] = get_dsa1024();
+        loopargs[i].dsa_key[2] = get_dsa2048();
+    }
+#endif
+#ifndef OPENSSL_NO_DES
+    DES_set_key_unchecked(&key, &sch);
+    DES_set_key_unchecked(&key2, &sch2);
+    DES_set_key_unchecked(&key3, &sch3);
+#endif
+#ifndef OPENSSL_NO_AES
+    AES_set_encrypt_key(key16, 128, &aes_ks1);
+    AES_set_encrypt_key(key24, 192, &aes_ks2);
+    AES_set_encrypt_key(key32, 256, &aes_ks3);
+#endif
+#ifndef OPENSSL_NO_CAMELLIA
+    Camellia_set_key(key16, 128, &camellia_ks1);
+    Camellia_set_key(ckey24, 192, &camellia_ks2);
+    Camellia_set_key(ckey32, 256, &camellia_ks3);
+#endif
+#ifndef OPENSSL_NO_IDEA
+    IDEA_set_encrypt_key(key16, &idea_ks);
+#endif
+#ifndef OPENSSL_NO_SEED
+    SEED_set_key(key16, &seed_ks);
+#endif
+#ifndef OPENSSL_NO_SMS4
+    sms4_set_encrypt_key(&sms4_ks, key16);
+#endif
+#ifndef OPENSSL_NO_ZUC
+    ZUC_set_key(&zuc_ks, key16, iv);
+    ZUC256_set_key(&zuc256_ks, key32, iv);
+#endif
+#ifndef OPENSSL_NO_RC4
+    RC4_set_key(&rc4_ks, 16, key16);
+#endif
+#ifndef OPENSSL_NO_RC2
+    RC2_set_key(&rc2_ks, 16, key16, 128);
+#endif
+#ifndef OPENSSL_NO_RC5
+    RC5_32_set_key(&rc5_ks, 16, key16, 12);
+#endif
+#ifndef OPENSSL_NO_BF
+    BF_set_key(&bf_ks, 16, key16);
+#endif
+#ifndef OPENSSL_NO_CAST
+    CAST_set_key(&cast_ks, 16, key16);
+#endif
+#ifndef SIGALRM
+# ifndef OPENSSL_NO_DES
+    BIO_printf(bio_err, "First we calculate the approximate speed ...\n");
+    count = 10;
+    do {
+        long it;
+        count *= 2;
+        Time_F(START);
+        for (it = count; it; it--)
+            DES_ecb_encrypt((DES_cblock *)loopargs[0].buf,
+                            (DES_cblock *)loopargs[0].buf, &sch, DES_ENCRYPT);
+        d = Time_F(STOP);
+    } while (d < 3);
+    save_count = count;
+    c[D_MD2][0] = count / 10;
+    c[D_MDC2][0] = count / 10;
+    c[D_MD4][0] = count;
+    c[D_MD5][0] = count;
+    c[D_HMAC][0] = count;
+    c[D_SHA1][0] = count;
+    c[D_RMD160][0] = count;
+    c[D_RC4][0] = count * 5;
+    c[D_CBC_DES][0] = count;
+    c[D_EDE3_DES][0] = count / 3;
+    c[D_CBC_IDEA][0] = count;
+    c[D_CBC_SEED][0] = count;
+    c[D_CBC_RC2][0] = count;
+    c[D_CBC_RC5][0] = count;
+    c[D_CBC_BF][0] = count;
+    c[D_CBC_CAST][0] = count;
+    c[D_CBC_128_AES][0] = count;
+    c[D_CBC_192_AES][0] = count;
+    c[D_CBC_256_AES][0] = count;
+    c[D_CBC_128_CML][0] = count;
+    c[D_CBC_192_CML][0] = count;
+    c[D_CBC_256_CML][0] = count;
+    c[D_SHA256][0] = count;
+    c[D_SHA512][0] = count;
+    c[D_WHIRLPOOL][0] = count;
+    c[D_IGE_128_AES][0] = count;
+    c[D_IGE_192_AES][0] = count;
+    c[D_IGE_256_AES][0] = count;
+    c[D_GHASH][0] = count;
+    c[D_SM3][0] = count;
+    c[D_CBC_SMS4][0] = count;
+    c[D_ZUC][0] = count;
+    c[D_ZUC256][0] = count;
+
+    for (i = 1; i < SIZE_NUM; i++) {
+        long l0, l1;
+
+        l0 = (long)lengths[0];
+        l1 = (long)lengths[i];
+
+        c[D_MD2][i] = c[D_MD2][0] * 4 * l0 / l1;
+        c[D_MDC2][i] = c[D_MDC2][0] * 4 * l0 / l1;
+        c[D_MD4][i] = c[D_MD4][0] * 4 * l0 / l1;
+        c[D_MD5][i] = c[D_MD5][0] * 4 * l0 / l1;
+        c[D_HMAC][i] = c[D_HMAC][0] * 4 * l0 / l1;
+        c[D_SHA1][i] = c[D_SHA1][0] * 4 * l0 / l1;
+        c[D_RMD160][i] = c[D_RMD160][0] * 4 * l0 / l1;
+        c[D_SHA256][i] = c[D_SHA256][0] * 4 * l0 / l1;
+        c[D_SHA512][i] = c[D_SHA512][0] * 4 * l0 / l1;
+        c[D_WHIRLPOOL][i] = c[D_WHIRLPOOL][0] * 4 * l0 / l1;
+        c[D_GHASH][i] = c[D_GHASH][0] * 4 * l0 / l1;
+        c[D_SM3][i] = c[D_SM3][0] * 4 * l0 / l1;
+
+        l0 = (long)lengths[i - 1];
+
+        c[D_RC4][i] = c[D_RC4][i - 1] * l0 / l1;
+        c[D_CBC_DES][i] = c[D_CBC_DES][i - 1] * l0 / l1;
+        c[D_EDE3_DES][i] = c[D_EDE3_DES][i - 1] * l0 / l1;
+        c[D_CBC_IDEA][i] = c[D_CBC_IDEA][i - 1] * l0 / l1;
+        c[D_CBC_SEED][i] = c[D_CBC_SEED][i - 1] * l0 / l1;
+        c[D_CBC_RC2][i] = c[D_CBC_RC2][i - 1] * l0 / l1;
+        c[D_CBC_RC5][i] = c[D_CBC_RC5][i - 1] * l0 / l1;
+        c[D_CBC_BF][i] = c[D_CBC_BF][i - 1] * l0 / l1;
+        c[D_CBC_CAST][i] = c[D_CBC_CAST][i - 1] * l0 / l1;
+        c[D_CBC_128_AES][i] = c[D_CBC_128_AES][i - 1] * l0 / l1;
+        c[D_CBC_192_AES][i] = c[D_CBC_192_AES][i - 1] * l0 / l1;
+        c[D_CBC_256_AES][i] = c[D_CBC_256_AES][i - 1] * l0 / l1;
+        c[D_CBC_128_CML][i] = c[D_CBC_128_CML][i - 1] * l0 / l1;
+        c[D_CBC_192_CML][i] = c[D_CBC_192_CML][i - 1] * l0 / l1;
+        c[D_CBC_256_CML][i] = c[D_CBC_256_CML][i - 1] * l0 / l1;
+        c[D_IGE_128_AES][i] = c[D_IGE_128_AES][i - 1] * l0 / l1;
+        c[D_IGE_192_AES][i] = c[D_IGE_192_AES][i - 1] * l0 / l1;
+        c[D_IGE_256_AES][i] = c[D_IGE_256_AES][i - 1] * l0 / l1;
+        c[D_CBC_SMS4][i] = c[D_CBC_SMS4][i - 1] * l0 / l1;
+        c[D_ZUC][i] = c[D_ZUC][i - 1] * l0 / l1;
+        c[D_ZUC256][i] = c[D_ZUC256][i - 1] * l0 / l1;
+    }
+
+#  ifndef OPENSSL_NO_RSA
+    rsa_c[R_RSA_512][0] = count / 2000;
+    rsa_c[R_RSA_512][1] = count / 400;
+    for (i = 1; i < RSA_NUM; i++) {
+        rsa_c[i][0] = rsa_c[i - 1][0] / 8;
+        rsa_c[i][1] = rsa_c[i - 1][1] / 4;
+        if (rsa_doit[i] <= 1 && rsa_c[i][0] == 0)
+            rsa_doit[i] = 0;
+        else {
+            if (rsa_c[i][0] == 0) {
+                rsa_c[i][0] = 1;            /* Set minimum iteration Nb to 1. */
+                rsa_c[i][1] = 20;
+            }
+        }
+    }
+#  endif
+
+#  ifndef OPENSSL_NO_DSA
+    dsa_c[R_DSA_512][0] = count / 1000;
+    dsa_c[R_DSA_512][1] = count / 1000 / 2;
+    for (i = 1; i < DSA_NUM; i++) {
+        dsa_c[i][0] = dsa_c[i - 1][0] / 4;
+        dsa_c[i][1] = dsa_c[i - 1][1] / 4;
+        if (dsa_doit[i] <= 1 && dsa_c[i][0] == 0)
+            dsa_doit[i] = 0;
+        else {
+            if (dsa_c[i][0] == 0) {
+                dsa_c[i][0] = 1;            /* Set minimum iteration Nb to 1. */
+                dsa_c[i][1] = 1;
+            }
+        }
+    }
+#  endif
+
+#  ifndef OPENSSL_NO_SM2
+    sm2sign_c[R_SM2_P256][0] = count / 1000 / 8;
+    sm2sign_c[R_SM2_P256][1] = count / 1000 / 8 / 2;
+    sm2enc_c[R_SM2_P256][0] = count / 1000 / 8;
+    sm2enc_c[R_SM2_P256][1] = count / 1000 / 8;
+#  endif
+
+#  ifndef OPENSSL_NO_SM9
+    sm9sign_c[R_SM9_BN256][0] = count / 1000 / 8;
+    sm9sign_c[R_SM9_BN256][1] = count / 1000 / 8 / 2;
+    sm9enc_c[R_SM9_BN256][0] = count / 1000 / 8;
+    sm9enc_c[R_SM9_BN256][1] = count / 1000 / 8;
+#  endif
+
+#  ifndef OPENSSL_NO_EC
+    ecdsa_c[R_EC_P160][0] = count / 1000;
+    ecdsa_c[R_EC_P160][1] = count / 1000 / 2;
+    for (i = R_EC_P192; i <= R_EC_P521; i++) {
+        ecdsa_c[i][0] = ecdsa_c[i - 1][0] / 2;
+        ecdsa_c[i][1] = ecdsa_c[i - 1][1] / 2;
+        if (ecdsa_doit[i] <= 1 && ecdsa_c[i][0] == 0)
+            ecdsa_doit[i] = 0;
+        else {
+            if (ecdsa_c[i][0] == 0) {
+                ecdsa_c[i][0] = 1;
+                ecdsa_c[i][1] = 1;
+            }
+        }
+    }
+    ecdsa_c[R_EC_K163][0] = count / 1000;
+    ecdsa_c[R_EC_K163][1] = count / 1000 / 2;
+    for (i = R_EC_K233; i <= R_EC_K571; i++) {
+        ecdsa_c[i][0] = ecdsa_c[i - 1][0] / 2;
+        ecdsa_c[i][1] = ecdsa_c[i - 1][1] / 2;
+        if (ecdsa_doit[i] <= 1 && ecdsa_c[i][0] == 0)
+            ecdsa_doit[i] = 0;
+        else {
+            if (ecdsa_c[i][0] == 0) {
+                ecdsa_c[i][0] = 1;
+                ecdsa_c[i][1] = 1;
+            }
+        }
+    }
+    ecdsa_c[R_EC_B163][0] = count / 1000;
+    ecdsa_c[R_EC_B163][1] = count / 1000 / 2;
+    for (i = R_EC_B233; i <= R_EC_B571; i++) {
+        ecdsa_c[i][0] = ecdsa_c[i - 1][0] / 2;
+        ecdsa_c[i][1] = ecdsa_c[i - 1][1] / 2;
+        if (ecdsa_doit[i] <= 1 && ecdsa_c[i][0] == 0)
+            ecdsa_doit[i] = 0;
+        else {
+            if (ecdsa_c[i][0] == 0) {
+                ecdsa_c[i][0] = 1;
+                ecdsa_c[i][1] = 1;
+            }
+        }
+    }
+
+    ecdh_c[R_EC_P160][0] = count / 1000;
+    for (i = R_EC_P192; i <= R_EC_P521; i++) {
+        ecdh_c[i][0] = ecdh_c[i - 1][0] / 2;
+        if (ecdh_doit[i] <= 1 && ecdh_c[i][0] == 0)
+            ecdh_doit[i] = 0;
+        else {
+            if (ecdh_c[i][0] == 0) {
+                ecdh_c[i][0] = 1;
+            }
+        }
+    }
+    ecdh_c[R_EC_K163][0] = count / 1000;
+    for (i = R_EC_K233; i <= R_EC_K571; i++) {
+        ecdh_c[i][0] = ecdh_c[i - 1][0] / 2;
+        if (ecdh_doit[i] <= 1 && ecdh_c[i][0] == 0)
+            ecdh_doit[i] = 0;
+        else {
+            if (ecdh_c[i][0] == 0) {
+                ecdh_c[i][0] = 1;
+            }
+        }
+    }
+    ecdh_c[R_EC_B163][0] = count / 1000;
+    for (i = R_EC_B233; i <= R_EC_B571; i++) {
+        ecdh_c[i][0] = ecdh_c[i - 1][0] / 2;
+        if (ecdh_doit[i] <= 1 && ecdh_c[i][0] == 0)
+            ecdh_doit[i] = 0;
+        else {
+            if (ecdh_c[i][0] == 0) {
+                ecdh_c[i][0] = 1;
+            }
+        }
+    }
+#  endif
+
+# else
+/* not worth fixing */
+#  error "You cannot disable DES on systems without SIGALRM."
+# endif                        /* OPENSSL_NO_DES */
+#else
+# ifndef _WIN32
+    signal(SIGALRM, sig_done);
+# endif
+#endif                         /* SIGALRM */
+
+#ifndef OPENSSL_NO_MD2
+    if (doit[D_MD2]) {
+        for (testnum = 0; testnum < SIZE_NUM; testnum++) {
+            print_message(names[D_MD2], c[D_MD2][testnum], lengths[testnum]);
+            Time_F(START);
+            count = run_benchmark(async_jobs, EVP_Digest_MD2_loop, loopargs);
+            d = Time_F(STOP);
+            print_result(D_MD2, testnum, count, d);
+        }
+    }
+#endif
+#ifndef OPENSSL_NO_MDC2
+    if (doit[D_MDC2]) {
+        for (testnum = 0; testnum < SIZE_NUM; testnum++) {
+            print_message(names[D_MDC2], c[D_MDC2][testnum], lengths[testnum]);
+            Time_F(START);
+            count = run_benchmark(async_jobs, EVP_Digest_MDC2_loop, loopargs);
+            d = Time_F(STOP);
+            print_result(D_MDC2, testnum, count, d);
+        }
+    }
+#endif
+
+#ifndef OPENSSL_NO_MD4
+    if (doit[D_MD4]) {
+        for (testnum = 0; testnum < SIZE_NUM; testnum++) {
+            print_message(names[D_MD4], c[D_MD4][testnum], lengths[testnum]);
+            Time_F(START);
+            count = run_benchmark(async_jobs, EVP_Digest_MD4_loop, loopargs);
+            d = Time_F(STOP);
+            print_result(D_MD4, testnum, count, d);
+        }
+    }
+#endif
+
+#ifndef OPENSSL_NO_MD5
+    if (doit[D_MD5]) {
+        for (testnum = 0; testnum < SIZE_NUM; testnum++) {
+            print_message(names[D_MD5], c[D_MD5][testnum], lengths[testnum]);
+            Time_F(START);
+            count = run_benchmark(async_jobs, MD5_loop, loopargs);
+            d = Time_F(STOP);
+            print_result(D_MD5, testnum, count, d);
+        }
+    }
+
+    if (doit[D_HMAC]) {
+        static const char hmac_key[] = "This is a key...";
+        int len = strlen(hmac_key);
+
+        for (i = 0; i < loopargs_len; i++) {
+            loopargs[i].hctx = HMAC_CTX_new();
+            if (loopargs[i].hctx == NULL) {
+                BIO_printf(bio_err, "HMAC malloc failure, exiting...");
+                exit(1);
+            }
+
+            HMAC_Init_ex(loopargs[i].hctx, hmac_key, len, EVP_md5(), NULL);
+        }
+        for (testnum = 0; testnum < SIZE_NUM; testnum++) {
+            print_message(names[D_HMAC], c[D_HMAC][testnum], lengths[testnum]);
+            Time_F(START);
+            count = run_benchmark(async_jobs, HMAC_loop, loopargs);
+            d = Time_F(STOP);
+            print_result(D_HMAC, testnum, count, d);
+        }
+        for (i = 0; i < loopargs_len; i++) {
+            HMAC_CTX_free(loopargs[i].hctx);
+        }
+    }
+#endif
+#ifndef OPENSSL_NO_SM3
+    if (doit[D_SM3]) {
+        for (testnum = 0; testnum < SIZE_NUM; testnum++) {
+            print_message(names[D_SM3], c[D_SM3][testnum], lengths[testnum]);
+            Time_F(START);
+            count = run_benchmark(async_jobs, SM3_loop, loopargs);
+            d = Time_F(STOP);
+            print_result(D_SM3, testnum, count, d);
+        }
+    }
+#endif
+#ifndef OPENSSL_NO_SHA
+    if (doit[D_SHA1]) {
+        for (testnum = 0; testnum < SIZE_NUM; testnum++) {
+            print_message(names[D_SHA1], c[D_SHA1][testnum], lengths[testnum]);
+            Time_F(START);
+            count = run_benchmark(async_jobs, SHA1_loop, loopargs);
+            d = Time_F(STOP);
+            print_result(D_SHA1, testnum, count, d);
+        }
+    }
+    if (doit[D_SHA256]) {
+        for (testnum = 0; testnum < SIZE_NUM; testnum++) {
+            print_message(names[D_SHA256], c[D_SHA256][testnum], lengths[testnum]);
+            Time_F(START);
+            count = run_benchmark(async_jobs, SHA256_loop, loopargs);
+            d = Time_F(STOP);
+            print_result(D_SHA256, testnum, count, d);
+        }
+    }
+    if (doit[D_SHA512]) {
+        for (testnum = 0; testnum < SIZE_NUM; testnum++) {
+            print_message(names[D_SHA512], c[D_SHA512][testnum], lengths[testnum]);
+            Time_F(START);
+            count = run_benchmark(async_jobs, SHA512_loop, loopargs);
+            d = Time_F(STOP);
+            print_result(D_SHA512, testnum, count, d);
+        }
+    }
+#endif
+
+#ifndef OPENSSL_NO_WHIRLPOOL
+    if (doit[D_WHIRLPOOL]) {
+        for (testnum = 0; testnum < SIZE_NUM; testnum++) {
+            print_message(names[D_WHIRLPOOL], c[D_WHIRLPOOL][testnum], lengths[testnum]);
+            Time_F(START);
+            count = run_benchmark(async_jobs, WHIRLPOOL_loop, loopargs);
+            d = Time_F(STOP);
+            print_result(D_WHIRLPOOL, testnum, count, d);
+        }
+    }
+#endif
+
+#ifndef OPENSSL_NO_RMD160
+    if (doit[D_RMD160]) {
+        for (testnum = 0; testnum < SIZE_NUM; testnum++) {
+            print_message(names[D_RMD160], c[D_RMD160][testnum], lengths[testnum]);
+            Time_F(START);
+            count = run_benchmark(async_jobs, EVP_Digest_RMD160_loop, loopargs);
+            d = Time_F(STOP);
+            print_result(D_RMD160, testnum, count, d);
+        }
+    }
+#endif
+#ifndef OPENSSL_NO_RC4
+    if (doit[D_RC4]) {
+        for (testnum = 0; testnum < SIZE_NUM; testnum++) {
+            print_message(names[D_RC4], c[D_RC4][testnum], lengths[testnum]);
+            Time_F(START);
+            count = run_benchmark(async_jobs, RC4_loop, loopargs);
+            d = Time_F(STOP);
+            print_result(D_RC4, testnum, count, d);
+        }
+    }
+#endif
+#ifndef OPENSSL_NO_DES
+    if (doit[D_CBC_DES]) {
+        for (testnum = 0; testnum < SIZE_NUM; testnum++) {
+            print_message(names[D_CBC_DES], c[D_CBC_DES][testnum], lengths[testnum]);
+            Time_F(START);
+            count = run_benchmark(async_jobs, DES_ncbc_encrypt_loop, loopargs);
+            d = Time_F(STOP);
+            print_result(D_CBC_DES, testnum, count, d);
+        }
+    }
+
+    if (doit[D_EDE3_DES]) {
+        for (testnum = 0; testnum < SIZE_NUM; testnum++) {
+            print_message(names[D_EDE3_DES], c[D_EDE3_DES][testnum], lengths[testnum]);
+            Time_F(START);
+            count = run_benchmark(async_jobs, DES_ede3_cbc_encrypt_loop, loopargs);
+            d = Time_F(STOP);
+            print_result(D_EDE3_DES, testnum, count, d);
+        }
+    }
+#endif
+
+#ifndef OPENSSL_NO_AES
+    if (doit[D_CBC_128_AES]) {
+        for (testnum = 0; testnum < SIZE_NUM; testnum++) {
+            print_message(names[D_CBC_128_AES], c[D_CBC_128_AES][testnum],
+                          lengths[testnum]);
+            Time_F(START);
+            count = run_benchmark(async_jobs, AES_cbc_128_encrypt_loop, loopargs);
+            d = Time_F(STOP);
+            print_result(D_CBC_128_AES, testnum, count, d);
+        }
+    }
+    if (doit[D_CBC_192_AES]) {
+        for (testnum = 0; testnum < SIZE_NUM; testnum++) {
+            print_message(names[D_CBC_192_AES], c[D_CBC_192_AES][testnum],
+                          lengths[testnum]);
+            Time_F(START);
+            count = run_benchmark(async_jobs, AES_cbc_192_encrypt_loop, loopargs);
+            d = Time_F(STOP);
+            print_result(D_CBC_192_AES, testnum, count, d);
+        }
+    }
+    if (doit[D_CBC_256_AES]) {
+        for (testnum = 0; testnum < SIZE_NUM; testnum++) {
+            print_message(names[D_CBC_256_AES], c[D_CBC_256_AES][testnum],
+                          lengths[testnum]);
+            Time_F(START);
+            count = run_benchmark(async_jobs, AES_cbc_256_encrypt_loop, loopargs);
+            d = Time_F(STOP);
+            print_result(D_CBC_256_AES, testnum, count, d);
+        }
+    }
+
+    if (doit[D_IGE_128_AES]) {
+        for (testnum = 0; testnum < SIZE_NUM; testnum++) {
+            print_message(names[D_IGE_128_AES], c[D_IGE_128_AES][testnum],
+                          lengths[testnum]);
+            Time_F(START);
+            count = run_benchmark(async_jobs, AES_ige_128_encrypt_loop, loopargs);
+            d = Time_F(STOP);
+            print_result(D_IGE_128_AES, testnum, count, d);
+        }
+    }
+    if (doit[D_IGE_192_AES]) {
+        for (testnum = 0; testnum < SIZE_NUM; testnum++) {
+            print_message(names[D_IGE_192_AES], c[D_IGE_192_AES][testnum],
+                          lengths[testnum]);
+            Time_F(START);
+            count = run_benchmark(async_jobs, AES_ige_192_encrypt_loop, loopargs);
+            d = Time_F(STOP);
+            print_result(D_IGE_192_AES, testnum, count, d);
+        }
+    }
+    if (doit[D_IGE_256_AES]) {
+        for (testnum = 0; testnum < SIZE_NUM; testnum++) {
+            print_message(names[D_IGE_256_AES], c[D_IGE_256_AES][testnum],
+                          lengths[testnum]);
+            Time_F(START);
+            count = run_benchmark(async_jobs, AES_ige_256_encrypt_loop, loopargs);
+            d = Time_F(STOP);
+            print_result(D_IGE_256_AES, testnum, count, d);
+        }
+    }
+    if (doit[D_GHASH]) {
+        for (i = 0; i < loopargs_len; i++) {
+            loopargs[i].gcm_ctx = CRYPTO_gcm128_new(&aes_ks1, (block128_f) AES_encrypt);
+            CRYPTO_gcm128_setiv(loopargs[i].gcm_ctx, (unsigned char *)"0123456789ab", 12);
+        }
+
+        for (testnum = 0; testnum < SIZE_NUM; testnum++) {
+            print_message(names[D_GHASH], c[D_GHASH][testnum], lengths[testnum]);
+            Time_F(START);
+            count = run_benchmark(async_jobs, CRYPTO_gcm128_aad_loop, loopargs);
+            d = Time_F(STOP);
+            print_result(D_GHASH, testnum, count, d);
+        }
+        for (i = 0; i < loopargs_len; i++)
+            CRYPTO_gcm128_release(loopargs[i].gcm_ctx);
+    }
+#endif
+
+#ifndef OPENSSL_NO_CAMELLIA
+    if (doit[D_CBC_128_CML]) {
+        if (async_jobs > 0) {
+            BIO_printf(bio_err, "Async mode is not supported with %s\n",
+                       names[D_CBC_128_CML]);
+            doit[D_CBC_128_CML] = 0;
+        }
+        for (testnum = 0; testnum < SIZE_NUM && async_init == 0; testnum++) {
+            print_message(names[D_CBC_128_CML], c[D_CBC_128_CML][testnum],
+                          lengths[testnum]);
+            Time_F(START);
+            for (count = 0, run = 1; COND(c[D_CBC_128_CML][testnum]); count++)
+                Camellia_cbc_encrypt(loopargs[0].buf, loopargs[0].buf,
+                                     (size_t)lengths[testnum], &camellia_ks1,
+                                     iv, CAMELLIA_ENCRYPT);
+            d = Time_F(STOP);
+            print_result(D_CBC_128_CML, testnum, count, d);
+        }
+    }
+    if (doit[D_CBC_192_CML]) {
+        if (async_jobs > 0) {
+            BIO_printf(bio_err, "Async mode is not supported with %s\n",
+                       names[D_CBC_192_CML]);
+            doit[D_CBC_192_CML] = 0;
+        }
+        for (testnum = 0; testnum < SIZE_NUM && async_init == 0; testnum++) {
+            print_message(names[D_CBC_192_CML], c[D_CBC_192_CML][testnum],
+                          lengths[testnum]);
+            if (async_jobs > 0) {
+                BIO_printf(bio_err, "Async mode is not supported, exiting...");
+                exit(1);
+            }
+            Time_F(START);
+            for (count = 0, run = 1; COND(c[D_CBC_192_CML][testnum]); count++)
+                Camellia_cbc_encrypt(loopargs[0].buf, loopargs[0].buf,
+                                     (size_t)lengths[testnum], &camellia_ks2,
+                                     iv, CAMELLIA_ENCRYPT);
+            d = Time_F(STOP);
+            print_result(D_CBC_192_CML, testnum, count, d);
+        }
+    }
+    if (doit[D_CBC_256_CML]) {
+        if (async_jobs > 0) {
+            BIO_printf(bio_err, "Async mode is not supported with %s\n",
+                       names[D_CBC_256_CML]);
+            doit[D_CBC_256_CML] = 0;
+        }
+        for (testnum = 0; testnum < SIZE_NUM && async_init == 0; testnum++) {
+            print_message(names[D_CBC_256_CML], c[D_CBC_256_CML][testnum],
+                          lengths[testnum]);
+            Time_F(START);
+            for (count = 0, run = 1; COND(c[D_CBC_256_CML][testnum]); count++)
+                Camellia_cbc_encrypt(loopargs[0].buf, loopargs[0].buf,
+                                     (size_t)lengths[testnum], &camellia_ks3,
+                                     iv, CAMELLIA_ENCRYPT);
+            d = Time_F(STOP);
+            print_result(D_CBC_256_CML, testnum, count, d);
+        }
+    }
+#endif
+#ifndef OPENSSL_NO_IDEA
+    if (doit[D_CBC_IDEA]) {
+        if (async_jobs > 0) {
+            BIO_printf(bio_err, "Async mode is not supported with %s\n",
+                       names[D_CBC_IDEA]);
+            doit[D_CBC_IDEA] = 0;
+        }
+        for (testnum = 0; testnum < SIZE_NUM && async_init == 0; testnum++) {
+            print_message(names[D_CBC_IDEA], c[D_CBC_IDEA][testnum], lengths[testnum]);
+            Time_F(START);
+            for (count = 0, run = 1; COND(c[D_CBC_IDEA][testnum]); count++)
+                IDEA_cbc_encrypt(loopargs[0].buf, loopargs[0].buf,
+                                 (size_t)lengths[testnum], &idea_ks,
+                                 iv, IDEA_ENCRYPT);
+            d = Time_F(STOP);
+            print_result(D_CBC_IDEA, testnum, count, d);
+        }
+    }
+#endif
+#ifndef OPENSSL_NO_SEED
+    if (doit[D_CBC_SEED]) {
+        if (async_jobs > 0) {
+            BIO_printf(bio_err, "Async mode is not supported with %s\n",
+                       names[D_CBC_SEED]);
+            doit[D_CBC_SEED] = 0;
+        }
+        for (testnum = 0; testnum < SIZE_NUM && async_init == 0; testnum++) {
+            print_message(names[D_CBC_SEED], c[D_CBC_SEED][testnum], lengths[testnum]);
+            Time_F(START);
+            for (count = 0, run = 1; COND(c[D_CBC_SEED][testnum]); count++)
+                SEED_cbc_encrypt(loopargs[0].buf, loopargs[0].buf,
+                                 (size_t)lengths[testnum], &seed_ks, iv, 1);
+            d = Time_F(STOP);
+            print_result(D_CBC_SEED, testnum, count, d);
+        }
+    }
+#endif
+#ifndef OPENSSL_NO_SMS4
+    if (doit[D_CBC_SMS4]) {
+        if (async_jobs > 0) {
+            BIO_printf(bio_err, "Async mode is not supported with %s\n",
+                       names[D_CBC_SMS4]);
+            doit[D_CBC_SMS4] = 0;
+        }
+        for (testnum = 0; testnum < SIZE_NUM && async_init == 0; testnum++) {
+            print_message(names[D_CBC_SMS4], c[D_CBC_SMS4][testnum], lengths[testnum]);
+            Time_F(START);
+            for (count = 0, run = 1; COND(c[D_CBC_SMS4][testnum]); count++)
+                sms4_cbc_encrypt(loopargs[0].buf, loopargs[0].buf,
+                                 (size_t)lengths[testnum], &sms4_ks, iv, 1);
+            d = Time_F(STOP);
+            print_result(D_CBC_SMS4, testnum, count, d);
+        }
+    }
+#endif
+#ifndef OPENSSL_NO_ZUC
+    if (doit[D_ZUC]) {
+        if (async_jobs > 0) {
+            BIO_printf(bio_err, "Async mode is not supported with %s\n",
+                       names[D_ZUC]);
+            doit[D_ZUC] = 0;
+        }
+        for (testnum = 0; testnum < SIZE_NUM && async_init == 0; testnum++) {
+            print_message(names[D_ZUC], c[D_ZUC][testnum], lengths[testnum]);
+            Time_F(START);
+            for (count = 0, run = 1; COND(c[D_ZUC][testnum]); count++)
+                ZUC_generate_keystream(&zuc_ks, lengths[testnum]/4,
+                                       (unsigned int *)loopargs[0].buf);
+            d = Time_F(STOP);
+            print_result(D_ZUC, testnum, count, d);
+        }
+    } 
+    if (doit[D_ZUC256]) {
+        if (async_jobs > 0) {
+            BIO_printf(bio_err, "Async mode is not supported with %s\n",
+                       names[D_ZUC256]);
+            doit[D_ZUC256] = 0;
+        }
+        for (testnum = 0; testnum < SIZE_NUM && async_init == 0; testnum++) {
+            print_message(names[D_ZUC256], c[D_ZUC256][testnum], lengths[testnum]);
+            Time_F(START);
+            for (count = 0, run = 1; COND(c[D_ZUC256][testnum]); count++)
+                ZUC256_generate_keystream(&zuc256_ks, lengths[testnum]/4,
+                                       (unsigned int *)loopargs[0].buf);
+            d = Time_F(STOP);
+            print_result(D_ZUC256, testnum, count, d);
+        }
+    }
+#endif
+#ifndef OPENSSL_NO_RC2
+    if (doit[D_CBC_RC2]) {
+        if (async_jobs > 0) {
+            BIO_printf(bio_err, "Async mode is not supported with %s\n",
+                       names[D_CBC_RC2]);
+            doit[D_CBC_RC2] = 0;
+        }
+        for (testnum = 0; testnum < SIZE_NUM && async_init == 0; testnum++) {
+            print_message(names[D_CBC_RC2], c[D_CBC_RC2][testnum], lengths[testnum]);
+            if (async_jobs > 0) {
+                BIO_printf(bio_err, "Async mode is not supported, exiting...");
+                exit(1);
+            }
+            Time_F(START);
+            for (count = 0, run = 1; COND(c[D_CBC_RC2][testnum]); count++)
+                RC2_cbc_encrypt(loopargs[0].buf, loopargs[0].buf,
+                                (size_t)lengths[testnum], &rc2_ks,
+                                iv, RC2_ENCRYPT);
+            d = Time_F(STOP);
+            print_result(D_CBC_RC2, testnum, count, d);
+        }
+    }
+#endif
+#ifndef OPENSSL_NO_RC5
+    if (doit[D_CBC_RC5]) {
+        if (async_jobs > 0) {
+            BIO_printf(bio_err, "Async mode is not supported with %s\n",
+                       names[D_CBC_RC5]);
+            doit[D_CBC_RC5] = 0;
+        }
+        for (testnum = 0; testnum < SIZE_NUM && async_init == 0; testnum++) {
+            print_message(names[D_CBC_RC5], c[D_CBC_RC5][testnum], lengths[testnum]);
+            if (async_jobs > 0) {
+                BIO_printf(bio_err, "Async mode is not supported, exiting...");
+                exit(1);
+            }
+            Time_F(START);
+            for (count = 0, run = 1; COND(c[D_CBC_RC5][testnum]); count++)
+                RC5_32_cbc_encrypt(loopargs[0].buf, loopargs[0].buf,
+                                   (size_t)lengths[testnum], &rc5_ks,
+                                   iv, RC5_ENCRYPT);
+            d = Time_F(STOP);
+            print_result(D_CBC_RC5, testnum, count, d);
+        }
+    }
+#endif
+#ifndef OPENSSL_NO_BF
+    if (doit[D_CBC_BF]) {
+        if (async_jobs > 0) {
+            BIO_printf(bio_err, "Async mode is not supported with %s\n",
+                       names[D_CBC_BF]);
+            doit[D_CBC_BF] = 0;
+        }
+        for (testnum = 0; testnum < SIZE_NUM && async_init == 0; testnum++) {
+            print_message(names[D_CBC_BF], c[D_CBC_BF][testnum], lengths[testnum]);
+            Time_F(START);
+            for (count = 0, run = 1; COND(c[D_CBC_BF][testnum]); count++)
+                BF_cbc_encrypt(loopargs[0].buf, loopargs[0].buf,
+                               (size_t)lengths[testnum], &bf_ks,
+                               iv, BF_ENCRYPT);
+            d = Time_F(STOP);
+            print_result(D_CBC_BF, testnum, count, d);
+        }
+    }
+#endif
+#ifndef OPENSSL_NO_CAST
+    if (doit[D_CBC_CAST]) {
+        if (async_jobs > 0) {
+            BIO_printf(bio_err, "Async mode is not supported with %s\n",
+                       names[D_CBC_CAST]);
+            doit[D_CBC_CAST] = 0;
+        }
+        for (testnum = 0; testnum < SIZE_NUM && async_init == 0; testnum++) {
+            print_message(names[D_CBC_CAST], c[D_CBC_CAST][testnum], lengths[testnum]);
+            Time_F(START);
+            for (count = 0, run = 1; COND(c[D_CBC_CAST][testnum]); count++)
+                CAST_cbc_encrypt(loopargs[0].buf, loopargs[0].buf,
+                                 (size_t)lengths[testnum], &cast_ks,
+                                 iv, CAST_ENCRYPT);
+            d = Time_F(STOP);
+            print_result(D_CBC_CAST, testnum, count, d);
+        }
+    }
+#endif
+
+    if (doit[D_EVP]) {
+        if (multiblock && evp_cipher) {
+            if (!
+                (EVP_CIPHER_flags(evp_cipher) &
+                 EVP_CIPH_FLAG_TLS1_1_MULTIBLOCK)) {
+                BIO_printf(bio_err, "%s is not multi-block capable\n",
+                           OBJ_nid2ln(EVP_CIPHER_nid(evp_cipher)));
+                goto end;
+            }
+            if (async_jobs > 0) {
+                BIO_printf(bio_err, "Async mode is not supported, exiting...");
+                exit(1);
+            }
+            multiblock_speed(evp_cipher);
+            ret = 0;
+            goto end;
+        }
+        for (testnum = 0; testnum < SIZE_NUM; testnum++) {
+            if (evp_cipher) {
+
+                names[D_EVP] = OBJ_nid2ln(EVP_CIPHER_nid(evp_cipher));
+                /*
+                 * -O3 -fschedule-insns messes up an optimization here!
+                 * names[D_EVP] somehow becomes NULL
+                 */
+                print_message(names[D_EVP], save_count, lengths[testnum]);
+
+                for (k = 0; k < loopargs_len; k++) {
+                    loopargs[k].ctx = EVP_CIPHER_CTX_new();
+                    if (decrypt)
+                        EVP_DecryptInit_ex(loopargs[k].ctx, evp_cipher, NULL, key16, iv);
+                    else
+                        EVP_EncryptInit_ex(loopargs[k].ctx, evp_cipher, NULL, key16, iv);
+                    EVP_CIPHER_CTX_set_padding(loopargs[k].ctx, 0);
+                }
+
+                Time_F(START);
+                count = run_benchmark(async_jobs, EVP_Update_loop, loopargs);
+                d = Time_F(STOP);
+                for (k = 0; k < loopargs_len; k++) {
+                    EVP_CIPHER_CTX_free(loopargs[k].ctx);
+                }
+            }
+            if (evp_md) {
+                names[D_EVP] = OBJ_nid2ln(EVP_MD_type(evp_md));
+                print_message(names[D_EVP], save_count, lengths[testnum]);
+                Time_F(START);
+                count = run_benchmark(async_jobs, EVP_Digest_loop, loopargs);
+                d = Time_F(STOP);
+            }
+            print_result(D_EVP, testnum, count, d);
+        }
+    }
+
+    for (i = 0; i < loopargs_len; i++)
+        RAND_bytes(loopargs[i].buf, 36);
+
+#ifndef OPENSSL_NO_RSA
+    for (testnum = 0; testnum < RSA_NUM; testnum++) {
+        int st = 0;
+        if (!rsa_doit[testnum])
+            continue;
+        for (i = 0; i < loopargs_len; i++) {
+            st = RSA_sign(NID_md5_sha1, loopargs[i].buf, 36, loopargs[i].buf2,
+                          &loopargs[i].siglen, loopargs[i].rsa_key[testnum]);
+            if (st == 0)
+                break;
+        }
+        if (st == 0) {
+            BIO_printf(bio_err,
+                       "RSA sign failure.  No RSA sign will be done.\n");
+            ERR_print_errors(bio_err);
+            rsa_count = 1;
+        } else {
+            pkey_print_message("private", "rsa",
+                               rsa_c[testnum][0], rsa_bits[testnum], RSA_SECONDS);
+            /* RSA_blinding_on(rsa_key[testnum],NULL); */
+            Time_F(START);
+            count = run_benchmark(async_jobs, RSA_sign_loop, loopargs);
+            d = Time_F(STOP);
+            BIO_printf(bio_err,
+                       mr ? "+R1:%ld:%d:%.2f\n"
+                       : "%ld %d bit private RSA's in %.2fs\n",
+                       count, rsa_bits[testnum], d);
+            rsa_results[testnum][0] = d / (double)count;
+            rsa_count = count;
+        }
+
+        for (i = 0; i < loopargs_len; i++) {
+            st = RSA_verify(NID_md5_sha1, loopargs[i].buf, 36, loopargs[i].buf2,
+                            loopargs[i].siglen, loopargs[i].rsa_key[testnum]);
+            if (st <= 0)
+                break;
+        }
+        if (st <= 0) {
+            BIO_printf(bio_err,
+                       "RSA verify failure.  No RSA verify will be done.\n");
+            ERR_print_errors(bio_err);
+            rsa_doit[testnum] = 0;
+        } else {
+            pkey_print_message("public", "rsa",
+                               rsa_c[testnum][1], rsa_bits[testnum], RSA_SECONDS);
+            Time_F(START);
+            count = run_benchmark(async_jobs, RSA_verify_loop, loopargs);
+            d = Time_F(STOP);
+            BIO_printf(bio_err,
+                       mr ? "+R2:%ld:%d:%.2f\n"
+                       : "%ld %d bit public RSA's in %.2fs\n",
+                       count, rsa_bits[testnum], d);
+            rsa_results[testnum][1] = d / (double)count;
+        }
+
+        if (rsa_count <= 1) {
+            /* if longer than 10s, don't do any more */
+            for (testnum++; testnum < RSA_NUM; testnum++)
+                rsa_doit[testnum] = 0;
+        }
+    }
+#endif                          /* OPENSSL_NO_RSA */
+
+    for (i = 0; i < loopargs_len; i++)
+        RAND_bytes(loopargs[i].buf, 36);
+
+#ifndef OPENSSL_NO_DSA
+    if (RAND_status() != 1) {
+        RAND_seed(rnd_seed, sizeof rnd_seed);
+    }
+    for (testnum = 0; testnum < DSA_NUM; testnum++) {
+        int st = 0;
+        if (!dsa_doit[testnum])
+            continue;
+
+        /* DSA_generate_key(dsa_key[testnum]); */
+        /* DSA_sign_setup(dsa_key[testnum],NULL); */
+        for (i = 0; i < loopargs_len; i++) {
+            st = DSA_sign(0, loopargs[i].buf, 20, loopargs[i].buf2,
+                          &loopargs[i].siglen, loopargs[i].dsa_key[testnum]);
+            if (st == 0)
+                break;
+        }
+        if (st == 0) {
+            BIO_printf(bio_err,
+                       "DSA sign failure.  No DSA sign will be done.\n");
+            ERR_print_errors(bio_err);
+            rsa_count = 1;
+        } else {
+            pkey_print_message("sign", "dsa",
+                               dsa_c[testnum][0], dsa_bits[testnum], DSA_SECONDS);
+            Time_F(START);
+            count = run_benchmark(async_jobs, DSA_sign_loop, loopargs);
+            d = Time_F(STOP);
+            BIO_printf(bio_err,
+                       mr ? "+R3:%ld:%d:%.2f\n"
+                       : "%ld %d bit DSA signs in %.2fs\n",
+                       count, dsa_bits[testnum], d);
+            dsa_results[testnum][0] = d / (double)count;
+            rsa_count = count;
+        }
+
+        for (i = 0; i < loopargs_len; i++) {
+            st = DSA_verify(0, loopargs[i].buf, 20, loopargs[i].buf2,
+                            loopargs[i].siglen, loopargs[i].dsa_key[testnum]);
+            if (st <= 0)
+                break;
+        }
+        if (st <= 0) {
+            BIO_printf(bio_err,
+                       "DSA verify failure.  No DSA verify will be done.\n");
+            ERR_print_errors(bio_err);
+            dsa_doit[testnum] = 0;
+        } else {
+            pkey_print_message("verify", "dsa",
+                               dsa_c[testnum][1], dsa_bits[testnum], DSA_SECONDS);
+            Time_F(START);
+            count = run_benchmark(async_jobs, DSA_verify_loop, loopargs);
+            d = Time_F(STOP);
+            BIO_printf(bio_err,
+                       mr ? "+R4:%ld:%d:%.2f\n"
+                       : "%ld %d bit DSA verify in %.2fs\n",
+                       count, dsa_bits[testnum], d);
+            dsa_results[testnum][1] = d / (double)count;
+        }
+
+        if (rsa_count <= 1) {
+            /* if longer than 10s, don't do any more */
+            for (testnum++; testnum < DSA_NUM; testnum++)
+                dsa_doit[testnum] = 0;
+        }
+    }
+#endif                          /* OPENSSL_NO_DSA */
+
+#ifndef OPENSSL_NO_EC
+    if (RAND_status() != 1) {
+        RAND_seed(rnd_seed, sizeof rnd_seed);
+    }
+    for (testnum = 0; testnum < EC_NUM; testnum++) {
+        int st = 1;
+
+        if (!ecdsa_doit[testnum])
+            continue;           /* Ignore Curve */
+        for (i = 0; i < loopargs_len; i++) {
+            loopargs[i].ecdsa[testnum] = EC_KEY_new_by_curve_name(test_curves[testnum]);
+            if (loopargs[i].ecdsa[testnum] == NULL) {
+                st = 0;
+                break;
+            }
+        }
+        if (st == 0) {
+            BIO_printf(bio_err, "ECDSA failure.\n");
+            ERR_print_errors(bio_err);
+            rsa_count = 1;
+        } else {
+            for (i = 0; i < loopargs_len; i++) {
+                if (!nopre)
+                    EC_KEY_precompute_mult(loopargs[i].ecdsa[testnum], NULL);
+                /* Perform ECDSA signature test */
+                EC_KEY_generate_key(loopargs[i].ecdsa[testnum]);
+                st = ECDSA_sign(0, loopargs[i].buf, 20, loopargs[i].buf2,
+                                &loopargs[i].siglen, loopargs[i].ecdsa[testnum]);
+                if (st == 0)
+                    break;
+            }
+            if (st == 0) {
+                BIO_printf(bio_err,
+                           "ECDSA sign failure.  No ECDSA sign will be done.\n");
+                ERR_print_errors(bio_err);
+                rsa_count = 1;
+            } else {
+                pkey_print_message("sign", "ecdsa",
+                                   ecdsa_c[testnum][0],
+                                   test_curves_bits[testnum], ECDSA_SECONDS);
+                Time_F(START);
+                count = run_benchmark(async_jobs, ECDSA_sign_loop, loopargs);
+                d = Time_F(STOP);
+
+                BIO_printf(bio_err,
+                           mr ? "+R5:%ld:%d:%.2f\n" :
+                           "%ld %d bit ECDSA signs in %.2fs \n",
+                           count, test_curves_bits[testnum], d);
+                ecdsa_results[testnum][0] = d / (double)count;
+                rsa_count = count;
+            }
+
+            /* Perform ECDSA verification test */
+            for (i = 0; i < loopargs_len; i++) {
+                st = ECDSA_verify(0, loopargs[i].buf, 20, loopargs[i].buf2,
+                                  loopargs[i].siglen, loopargs[i].ecdsa[testnum]);
+                if (st != 1)
+                    break;
+            }
+            if (st != 1) {
+                BIO_printf(bio_err,
+                           "ECDSA verify failure.  No ECDSA verify will be done.\n");
+                ERR_print_errors(bio_err);
+                ecdsa_doit[testnum] = 0;
+            } else {
+                pkey_print_message("verify", "ecdsa",
+                                   ecdsa_c[testnum][1],
+                                   test_curves_bits[testnum], ECDSA_SECONDS);
+                Time_F(START);
+                count = run_benchmark(async_jobs, ECDSA_verify_loop, loopargs);
+                d = Time_F(STOP);
+                BIO_printf(bio_err,
+                           mr ? "+R6:%ld:%d:%.2f\n"
+                           : "%ld %d bit ECDSA verify in %.2fs\n",
+                           count, test_curves_bits[testnum], d);
+                ecdsa_results[testnum][1] = d / (double)count;
+            }
+
+            if (rsa_count <= 1) {
+                /* if longer than 10s, don't do any more */
+                for (testnum++; testnum < EC_NUM; testnum++)
+                    ecdsa_doit[testnum] = 0;
+            }
+        }
+    }
+
+    if (RAND_status() != 1) {
+        RAND_seed(rnd_seed, sizeof rnd_seed);
+    }
+    for (testnum = 0; testnum < EC_NUM; testnum++) {
+        int ecdh_checks = 1;
+
+        if (!ecdh_doit[testnum])
+            continue;
+        for (i = 0; i < loopargs_len; i++) {
+            loopargs[i].ecdh_a[testnum] = EC_KEY_new_by_curve_name(test_curves[testnum]);
+            loopargs[i].ecdh_b[testnum] = EC_KEY_new_by_curve_name(test_curves[testnum]);
+            if (loopargs[i].ecdh_a[testnum] == NULL ||
+                loopargs[i].ecdh_b[testnum] == NULL) {
+                ecdh_checks = 0;
+                break;
+            }
+        }
+        if (ecdh_checks == 0) {
+            BIO_printf(bio_err, "ECDH failure.\n");
+            ERR_print_errors(bio_err);
+            rsa_count = 1;
+        } else {
+            for (i = 0; i < loopargs_len; i++) {
+                /* generate two ECDH key pairs */
+                if (!EC_KEY_generate_key(loopargs[i].ecdh_a[testnum]) ||
+                        !EC_KEY_generate_key(loopargs[i].ecdh_b[testnum])) {
+                    BIO_printf(bio_err, "ECDH key generation failure.\n");
+                    ERR_print_errors(bio_err);
+                    ecdh_checks = 0;
+                    rsa_count = 1;
+                } else {
+                    int secret_size_a, secret_size_b;
+                    /*
+                     * If field size is not more than 24 octets, then use SHA-1
+                     * hash of result; otherwise, use result (see section 4.8 of
+                     * draft-ietf-tls-ecc-03.txt).
+                     */
+                    int field_size = EC_GROUP_get_degree(
+                            EC_KEY_get0_group(loopargs[i].ecdh_a[testnum]));
+
+                    if (field_size <= 24 * 8) {                 /* 192 bits */
+                        loopargs[i].outlen = KDF1_SHA1_len;
+                        loopargs[i].kdf = KDF1_SHA1;
+                    } else {
+                        loopargs[i].outlen = (field_size + 7) / 8;
+                        loopargs[i].kdf = NULL;
+                    }
+                    secret_size_a =
+                        ECDH_compute_key(loopargs[i].secret_a, loopargs[i].outlen,
+                                EC_KEY_get0_public_key(loopargs[i].ecdh_b[testnum]),
+                                loopargs[i].ecdh_a[testnum], loopargs[i].kdf);
+                    secret_size_b =
+                        ECDH_compute_key(loopargs[i].secret_b, loopargs[i].outlen,
+                                EC_KEY_get0_public_key(loopargs[i].ecdh_a[testnum]),
+                                loopargs[i].ecdh_b[testnum], loopargs[i].kdf);
+                    if (secret_size_a != secret_size_b)
+                        ecdh_checks = 0;
+                    else
+                        ecdh_checks = 1;
+
+                    for (k = 0; k < secret_size_a && ecdh_checks == 1; k++) {
+                        if (loopargs[i].secret_a[k] != loopargs[i].secret_b[k])
+                            ecdh_checks = 0;
+                    }
+
+                    if (ecdh_checks == 0) {
+                        BIO_printf(bio_err, "ECDH computations don't match.\n");
+                        ERR_print_errors(bio_err);
+                        rsa_count = 1;
+                        break;
+                    }
+                }
+            }
+            if (ecdh_checks != 0) {
+                pkey_print_message("", "ecdh",
+                        ecdh_c[testnum][0],
+                        test_curves_bits[testnum], ECDH_SECONDS);
+                Time_F(START);
+                count = run_benchmark(async_jobs, ECDH_compute_key_loop, loopargs);
+                d = Time_F(STOP);
+                BIO_printf(bio_err,
+                        mr ? "+R7:%ld:%d:%.2f\n" :
+                        "%ld %d-bit ECDH ops in %.2fs\n", count,
+                        test_curves_bits[testnum], d);
+                ecdh_results[testnum][0] = d / (double)count;
+                rsa_count = count;
+            }
+        }
+
+        if (rsa_count <= 1) {
+            /* if longer than 10s, don't do any more */
+            for (testnum++; testnum < EC_NUM; testnum++)
+                ecdh_doit[testnum] = 0;
+        }
+    }
+#endif                          /* OPENSSL_NO_EC */
+#ifndef OPENSSL_NO_SM2
+
+    if (RAND_status() != 1) {
+        RAND_seed(rnd_seed, sizeof rnd_seed);
+    }
+    for (testnum = 0; testnum < SM2_NUM; testnum++) {
+        int st = 1;
+
+        if (!sm2sign_doit[testnum])
+            continue;           /* Ignore Curve */
+        for (i = 0; i < loopargs_len; i++) {
+            loopargs[i].sm2[testnum] = EC_KEY_new_by_curve_name(test_sm2_curves[testnum]);
+            if (loopargs[i].sm2[testnum] == NULL) {
+                st = 0;
+                break;
+            }
+        }
+        if (st == 0) {
+            BIO_printf(bio_err, "SM2 failure.\n");
+            ERR_print_errors(bio_err);
+            rsa_count = 1;
+        } else {
+            for (i = 0; i < loopargs_len; i++) {
+                if (!nopre)
+                    EC_KEY_precompute_mult(loopargs[i].sm2[testnum], NULL);
+                /* Perform SM2 signature test */
+                EC_KEY_generate_key(loopargs[i].sm2[testnum]);
+                st = SM2_sign(0, loopargs[i].buf, 32, loopargs[i].buf2,
+                              &loopargs[i].siglen, loopargs[i].sm2[testnum]);
+                if (st == 0)
+                    break;
+            }
+            if (st == 0) {
+                BIO_printf(bio_err,
+                           "SM2 sign failure.  No SM2 sign will be done.\n");
+                ERR_print_errors(bio_err);
+                rsa_count = 1;
+            } else {
+                pkey_print_message("sign", "sm2",
+                                   sm2sign_c[testnum][0],
+                                   test_sm2_curves_bits[testnum], ECDSA_SECONDS);
+                Time_F(START);
+                count = run_benchmark(async_jobs, SM2_sign_loop, loopargs);
+                d = Time_F(STOP);
+
+                BIO_printf(bio_err,
+                           mr ? "+R7:%ld:%d:%.2f\n" :
+                           "%ld %d bit SM2 signs in %.2fs \n",
+                           count, test_sm2_curves_bits[testnum], d);
+                sm2sign_results[testnum][0] = d / (double)count;
+                rsa_count = count;
+            }
+
+            /* Perform SM2 verification test */
+            for (i = 0; i < loopargs_len; i++) {
+                st = SM2_verify(0, loopargs[i].buf, 32, loopargs[i].buf2,
+                                loopargs[i].siglen, loopargs[i].sm2[testnum]);
+                if (st != 1)
+                    break;
+            }
+            if (st != 1) {
+                BIO_printf(bio_err,
+                           "SM2 verify failure.  No SM2 verify will be done.\n");
+                ERR_print_errors(bio_err);
+                sm2sign_doit[testnum] = 0;
+            } else {
+                pkey_print_message("verify", "sm2",
+                                   sm2sign_c[testnum][1],
+                                   test_sm2_curves_bits[testnum], ECDSA_SECONDS);
+                Time_F(START);
+                count = run_benchmark(async_jobs, SM2_verify_loop, loopargs);
+                d = Time_F(STOP);
+                BIO_printf(bio_err,
+                           mr ? "+R8:%ld:%d:%.2f\n"
+                           : "%ld %d bit SM2 verify in %.2fs\n",
+                           count, test_sm2_curves_bits[testnum], d);
+                sm2sign_results[testnum][1] = d / (double)count;
+            }
+
+            if (rsa_count <= 1) {
+                /* if longer than 10s, don't do any more */
+                for (testnum++; testnum < SM2_NUM; testnum++)
+                    sm2sign_doit[testnum] = 0;
+            }
+        }
+    }
+
+
+    if (RAND_status() != 1) {
+        RAND_seed(rnd_seed, sizeof rnd_seed);
+    }
+    for (testnum = 0; testnum < SM2_NUM; testnum++) {
+        int st = 1;
+
+        if (!sm2enc_doit[testnum])
+            continue;
+        for (i = 0; i < loopargs_len; i++) {
+            loopargs[i].sm2[testnum] = EC_KEY_new_by_curve_name(
+                                        test_sm2_curves[testnum]);
+            if (loopargs[i].sm2[testnum] == NULL) {
+                st = 0;
+                break;
+            }
+        }
+        if (st == 0) {
+            BIO_printf(bio_err, "SM2 failure.\n");
+            ERR_print_errors(bio_err);
+            rsa_count = 1;
+        } else {
+            for (i = 0; i < loopargs_len; i++) {
+                if (!nopre)
+                    EC_KEY_precompute_mult(loopargs[i].sm2[testnum], NULL);
+                /* Perform SM2 encryption test */
+                EC_KEY_generate_key(loopargs[i].sm2[testnum]);
+		loopargs[i].cipherlen = BUFSIZE;
+                st = SM2_encrypt(NID_sm3, loopargs[i].buf, 32, loopargs[i].buf2,
+                                 &loopargs[i].cipherlen, loopargs[i].sm2[testnum]);
+                if (st == 0)
+                    break;
+            }
+            if (st == 0) {
+                BIO_printf(bio_err,
+                           "SM2 encryption failure.  No SM2 encryption will be done.\n");
+                ERR_print_errors(bio_err);
+                rsa_count = 1;
+            } else {
+                pkey_print_message("encrypt", "sm2",
+                                   sm2enc_c[testnum][0],
+                                   test_sm2_curves_bits[testnum], ECDSA_SECONDS);
+                Time_F(START);
+                count = run_benchmark(async_jobs, SM2_encrypt_loop, loopargs);
+                d = Time_F(STOP);
+
+                BIO_printf(bio_err,
+                           mr ? "+R7:%ld:%d:%.2f\n" :
+                           "%ld %d bit SM2 encrypt in %.2fs \n",
+                           count, test_sm2_curves_bits[testnum], d);
+                sm2enc_results[testnum][0] = d / (double)count;
+                rsa_count = count;
+            }
+
+            /* Perform SM2 verification test */
+            for (i = 0; i < loopargs_len; i++) {
+                size_t len = loopargs[i].cipherlen;
+                st = SM2_decrypt(NID_sm3, loopargs[i].buf2, loopargs[i].cipherlen,
+                                 loopargs[i].buf, &len, loopargs[i].sm2[testnum]);
+                if (st == 0)
+                    break;
+            }
+            if (st != 1) {
+                BIO_printf(bio_err,
+                           "SM2 decrypt failure.  No SM2 decrypt will be done.\n");
+                ERR_print_errors(bio_err);
+                sm2enc_doit[testnum] = 0;
+            } else {
+                pkey_print_message("decrypt", "sm2",
+                                   sm2enc_c[testnum][1],
+                                   test_sm2_curves_bits[testnum], ECDSA_SECONDS);
+                Time_F(START);
+                count = run_benchmark(async_jobs, SM2_decrypt_loop, loopargs);
+                d = Time_F(STOP);
+                BIO_printf(bio_err,
+                           mr ? "+R8:%ld:%d:%.2f\n"
+                           : "%ld %d bit SM2 decrypt in %.2fs\n",
+                           count, test_sm2_curves_bits[testnum], d);
+                sm2enc_results[testnum][1] = d / (double)count;
+            }
+
+            if (rsa_count <= 1) {
+                /* if longer than 10s, don't do any more */
+                for (testnum++; testnum < SM2_NUM; testnum++)
+                    sm2sign_doit[testnum] = 0;
+            }
+        }
+    }
+
+#endif /* OPENSSL_NO_SM2 */
+#ifndef OPENSSL_NO_SM9
+
+	if (RAND_status() != 1) {
+        RAND_seed(rnd_seed, sizeof rnd_seed);
+    }
+    for (testnum = 0; testnum < SM9_NUM; testnum++) {
+        int st = 1;
+
+        if (!sm9sign_doit[testnum])
+            continue;           /* Ignore Curve */
+        for (i = 0; i < loopargs_len; i++) {
+        	loopargs[i].sm9mst[testnum] = SM9_generate_master_secret(test_sm9_curves[testnum],
+                                                test_sm9_scheme[testnum], test_sm9_hash1[testnum]);
+            if (loopargs[i].sm9mst[testnum] == NULL) {
+                st = 0;
+                break;
+            }
+        }
+        if (st == 0) {
+            BIO_printf(bio_err, "SM9 failure.\n");
+            ERR_print_errors(bio_err);
+            rsa_count = 1;
+        } else {
+            for (i = 0; i < loopargs_len; i++) {
+                /* Perform SM9 signature test */
+                loopargs[i].sm9sk[testnum] = SM9_extract_private_key(loopargs[i].sm9mst[testnum], sm9verify_id, sm9verify_idlen);
+                st = SM9_sign(NID_sm3, loopargs[i].buf, 32, loopargs[i].buf2,
+                              &loopargs[i].siglen, loopargs[i].sm9sk[testnum]);
+                if (st == 0)
+                    break;
+            }
+            if (st == 0) {
+                BIO_printf(bio_err,
+                           "SM9 sign failure.  No SM9 sign will be done.\n");
+                ERR_print_errors(bio_err);
+                rsa_count = 1;
+            } else {
+                pkey_print_message("sign", "sm9",
+                                   sm9sign_c[testnum][0],
+                                   test_sm9_curves_bits[testnum], ECDSA_SECONDS);
+                Time_F(START);
+                count = run_benchmark(async_jobs, SM9_sign_loop, loopargs);
+                d = Time_F(STOP);
+
+                BIO_printf(bio_err,
+                           mr ? "+R7:%ld:%d:%.2f\n" :
+                           "%ld %d bit SM9 signs in %.2fs \n",
+                           count, test_sm9_curves_bits[testnum], d);
+                sm9sign_results[testnum][0] = d / (double)count;
+                rsa_count = count;
+            }
+
+            /* Perform SM9 verification test */
+            for (i = 0; i < loopargs_len; i++) {
+                loopargs[i].sm9mpk[testnum] = SM9_extract_public_parameters(loopargs[i].sm9mst[testnum]);
+                st = SM9_verify(NID_sm3, loopargs[i].buf, 32, loopargs[i].buf2,
+                                loopargs[i].siglen, loopargs[i].sm9mpk[testnum], sm9verify_id, sm9verify_idlen);
+                if (st != 1)
+                    break;
+            }
+            if (st != 1) {
+                BIO_printf(bio_err,
+                           "SM9 verify failure.  No SM9 verify will be done.\n");
+                ERR_print_errors(bio_err);
+                sm9sign_doit[testnum] = 0;
+            } else {
+                pkey_print_message("verify", "sm9",
+                                   sm9sign_c[testnum][1],
+                                   test_sm9_curves_bits[testnum], ECDSA_SECONDS);
+                Time_F(START);
+                count = run_benchmark(async_jobs, SM9_verify_loop, loopargs);
+                d = Time_F(STOP);
+                BIO_printf(bio_err,
+                           mr ? "+R8:%ld:%d:%.2f\n"
+                           : "%ld %d bit SM9 verify in %.2fs\n",
+                           count, test_sm9_curves_bits[testnum], d);
+                sm9sign_results[testnum][1] = d / (double)count;
+            }
+
+            if (rsa_count <= 1) {
+                /* if longer than 10s, don't do any more */
+                for (testnum++; testnum < SM9_NUM; testnum++)
+                    sm9sign_doit[testnum] = 0;
+            }
+        }
+    }
+
+
+    if (RAND_status() != 1) {
+        RAND_seed(rnd_seed, sizeof rnd_seed);
+    }
+    for (testnum = 0; testnum < SM9_NUM; testnum++) {
+        int st = 1;
+
+        if (!sm9enc_doit[testnum])
+            continue;
+        for (i = 0; i < loopargs_len; i++) {
+            loopargs[i].sm9mst[testnum] = SM9_generate_master_secret(test_sm9_curves[testnum],
+                                                test_sm9_scheme[testnum], test_sm9_hash1[testnum]);
+            if (loopargs[i].sm9mst[testnum] == NULL) {
+                st = 0;
+                break;
+            }
+        }
+        if (st == 0) {
+            BIO_printf(bio_err, "SM9 failure.\n");
+            ERR_print_errors(bio_err);
+            rsa_count = 1;
+        } else {
+            for (i = 0; i < loopargs_len; i++) {
+            	/* these 2 lines should be modified ? */
+                /*if (!nopre)
+                    EC_KEY_precompute_mult(loopargs[i].sm9[testnum], NULL);*/ 
+                /* Perform SM9 encryption test */
+                loopargs[i].sm9mpk[testnum] = SM9_extract_public_parameters(loopargs[i].sm9mst[testnum]);
+				loopargs[i].cipherlen = BUFSIZE;
+                st = SM9_encrypt(NID_sm3, loopargs[i].buf, 32, loopargs[i].buf2,
+                                 &loopargs[i].cipherlen, loopargs[i].sm9mpk[testnum], sm9enc_id, sm9enc_idlen);
+                if (st == 0)
+                    break;
+            }
+            if (st == 0) {
+                BIO_printf(bio_err,
+                           "SM9 encryption failure.  No SM9 encryption will be done.\n");
+                ERR_print_errors(bio_err);
+                rsa_count = 1;
+            } else {
+                pkey_print_message("encrypt", "sm9",
+                                   sm9enc_c[testnum][0],
+                                   test_sm9_curves_bits[testnum], ECDSA_SECONDS);
+                Time_F(START);
+                count = run_benchmark(async_jobs, SM9_encrypt_loop, loopargs);
+                d = Time_F(STOP);
+
+                BIO_printf(bio_err,
+                           mr ? "+R7:%ld:%d:%.2f\n" :
+                           "%ld %d bit SM9 encrypt in %.2fs \n",
+                           count, test_sm9_curves_bits[testnum], d);
+                sm9enc_results[testnum][0] = d / (double)count;
+                rsa_count = count;
+            }
+
+            /* Perform SM9 verification test */
+            for (i = 0; i < loopargs_len; i++) {
+            	loopargs[i].sm9sk[testnum] = SM9_extract_private_key(loopargs[i].sm9mst[testnum], sm9enc_id, sm9enc_idlen);
+                size_t len = loopargs[i].cipherlen;
+                st = SM9_decrypt(NID_sm3, loopargs[i].buf2, loopargs[i].cipherlen,
+                                 loopargs[i].buf, &len, loopargs[i].sm9sk[testnum]);
+                if (st == 0)
+                    break;
+            }
+            if (st != 1) {
+                BIO_printf(bio_err,
+                           "SM9 decrypt failure.  No SM9 decrypt will be done.\n");
+                ERR_print_errors(bio_err);
+                sm9enc_doit[testnum] = 0;
+            } else {
+                pkey_print_message("decrypt", "sm9",
+                                   sm9enc_c[testnum][1],
+                                   test_sm9_curves_bits[testnum], ECDSA_SECONDS);
+                Time_F(START);
+                count = run_benchmark(async_jobs, SM9_decrypt_loop, loopargs);
+                d = Time_F(STOP);
+                BIO_printf(bio_err,
+                           mr ? "+R8:%ld:%d:%.2f\n"
+                           : "%ld %d bit SM9 decrypt in %.2fs\n",
+                           count, test_sm9_curves_bits[testnum], d);
+                sm9enc_results[testnum][1] = d / (double)count;
+            }
+
+            if (rsa_count <= 1) {
+                /* if longer than 10s, don't do any more */
+                for (testnum++; testnum < SM9_NUM; testnum++)
+                    sm9sign_doit[testnum] = 0;
+            }
+        }
+    }
+
+#endif /* OPENSSL_NO_SM9 */
+#ifndef NO_FORK
+ show_res:
+#endif
+    if (!mr) {
+        printf("%s\n", OpenSSL_version(OPENSSL_VERSION));
+        printf("%s\n", OpenSSL_version(OPENSSL_BUILT_ON));
+        printf("options:");
+        printf("%s ", BN_options());
+#ifndef OPENSSL_NO_MD2
+        printf("%s ", MD2_options());
+#endif
+#ifndef OPENSSL_NO_RC4
+        printf("%s ", RC4_options());
+#endif
+#ifndef OPENSSL_NO_DES
+        printf("%s ", DES_options());
+#endif
+#ifndef OPENSSL_NO_AES
+        printf("%s ", AES_options());
+#endif
+#ifndef OPENSSL_NO_IDEA
+        printf("%s ", IDEA_options());
+#endif
+#ifndef OPENSSL_NO_BF
+        printf("%s ", BF_options());
+#endif
+#ifndef OPENSSL_NO_SM3
+        //printf("%s ", SM3_options());
+#endif
+#ifndef OPENSSL_NO_SMS4
+        //printf("%s ", SMS4_options());
+#endif
+        printf("\n%s\n", OpenSSL_version(OPENSSL_CFLAGS));
+    }
+
+    if (pr_header) {
+        if (mr)
+            printf("+H");
+        else {
+            printf
+                ("The 'numbers' are in 1000s of bytes per second processed.\n");
+            printf("type        ");
+        }
+        for (testnum = 0; testnum < SIZE_NUM; testnum++)
+            printf(mr ? ":%d" : "%7d bytes", lengths[testnum]);
+        printf("\n");
+    }
+
+    for (k = 0; k < ALGOR_NUM; k++) {
+        if (!doit[k])
+            continue;
+        if (mr)
+            printf("+F:%d:%s", k, names[k]);
+        else
+            printf("%-13s", names[k]);
+        for (testnum = 0; testnum < SIZE_NUM; testnum++) {
+            if (results[k][testnum] > 10000 && !mr)
+                printf(" %11.2fk", results[k][testnum] / 1e3);
+            else
+                printf(mr ? ":%.2f" : " %11.2f ", results[k][testnum]);
+        }
+        printf("\n");
+    }
+#ifndef OPENSSL_NO_RSA
+    testnum = 1;
+    for (k = 0; k < RSA_NUM; k++) {
+        if (!rsa_doit[k])
+            continue;
+        if (testnum && !mr) {
+            printf("%18ssign    verify    sign/s verify/s\n", " ");
+            testnum = 0;
+        }
+        if (mr)
+            printf("+F2:%u:%u:%f:%f\n",
+                   k, rsa_bits[k], rsa_results[k][0], rsa_results[k][1]);
+        else
+            printf("rsa %4u bits %8.6fs %8.6fs %8.1f %8.1f\n",
+                   rsa_bits[k], rsa_results[k][0], rsa_results[k][1],
+                   1.0 / rsa_results[k][0], 1.0 / rsa_results[k][1]);
+    }
+#endif
+#ifndef OPENSSL_NO_DSA
+    testnum = 1;
+    for (k = 0; k < DSA_NUM; k++) {
+        if (!dsa_doit[k])
+            continue;
+        if (testnum && !mr) {
+            printf("%18ssign    verify    sign/s verify/s\n", " ");
+            testnum = 0;
+        }
+        if (mr)
+            printf("+F3:%u:%u:%f:%f\n",
+                   k, dsa_bits[k], dsa_results[k][0], dsa_results[k][1]);
+        else
+            printf("dsa %4u bits %8.6fs %8.6fs %8.1f %8.1f\n",
+                   dsa_bits[k], dsa_results[k][0], dsa_results[k][1],
+                   1.0 / dsa_results[k][0], 1.0 / dsa_results[k][1]);
+    }
+#endif
+#ifndef OPENSSL_NO_EC
+    testnum = 1;
+    for (k = 0; k < EC_NUM; k++) {
+        if (!ecdsa_doit[k])
+            continue;
+        if (testnum && !mr) {
+            printf("%30ssign    verify    sign/s verify/s\n", " ");
+            testnum = 0;
+        }
+
+        if (mr)
+            printf("+F4:%u:%u:%f:%f\n",
+                   k, test_curves_bits[k],
+                   ecdsa_results[k][0], ecdsa_results[k][1]);
+        else
+            printf("%4u bit ecdsa (%s) %8.4fs %8.4fs %8.1f %8.1f\n",
+                   test_curves_bits[k],
+                   test_curves_names[k],
+                   ecdsa_results[k][0], ecdsa_results[k][1],
+                   1.0 / ecdsa_results[k][0], 1.0 / ecdsa_results[k][1]);
+    }
+
+    testnum = 1;
+    for (k = 0; k < EC_NUM; k++) {
+        if (!ecdh_doit[k])
+            continue;
+        if (testnum && !mr) {
+            printf("%30sop      op/s\n", " ");
+            testnum = 0;
+        }
+        if (mr)
+            printf("+F5:%u:%u:%f:%f\n",
+                   k, test_curves_bits[k],
+                   ecdh_results[k][0], 1.0 / ecdh_results[k][0]);
+
+        else
+            printf("%4u bit ecdh (%s) %8.4fs %8.1f\n",
+                   test_curves_bits[k],
+                   test_curves_names[k],
+                   ecdh_results[k][0], 1.0 / ecdh_results[k][0]);
+    }
+#endif
+#ifndef OPENSSL_NO_SM2
+    testnum = 1;
+    for (k = 0; k < SM2_NUM; k++) {
+        if (!sm2sign_doit[k])
+            continue;
+        if (testnum && !mr) {
+            printf("%30ssign    verify    sign/s verify/s\n", " ");
+            testnum = 0;
+        }
+
+        if (mr)
+            printf("+F6:%u:%u:%f:%f\n",
+                   k, test_sm2_curves_bits[k],
+                   sm2sign_results[k][0], sm2sign_results[k][1]);
+        else
+            printf("%4u bit sm2 (%s) %8.4fs %8.4fs %8.1f %8.1f\n",
+                   test_sm2_curves_bits[k],
+                   test_sm2_curves_names[k],
+                   sm2sign_results[k][0], sm2sign_results[k][1],
+                   1.0 / sm2sign_results[k][0], 1.0 / sm2sign_results[k][1]);
+    }
+
+    testnum = 1;
+    for (k = 0; k < SM2_NUM; k++) {
+        if (!sm2enc_doit[k])
+            continue;
+        if (testnum && !mr) {
+            printf("%30sencrypt decrypt   enc/s  dec/s\n", " ");
+            testnum = 0;
+        }
+
+        if (mr)
+            printf("+F6:%u:%u:%f:%f\n",
+                   k, test_sm2_curves_bits[k],
+                   sm2enc_results[k][0], sm2enc_results[k][1]);
+        else
+            printf("%4u bit sm2 (%s) %8.4fs %8.4fs %8.1f %8.1f\n",
+                   test_sm2_curves_bits[k],
+                   test_sm2_curves_names[k],
+                   sm2enc_results[k][0], sm2enc_results[k][1],
+                   1.0 / sm2enc_results[k][0], 1.0 / sm2enc_results[k][1]);
+    }
+
+#endif
+#ifndef OPENSSL_NO_SM9
+    testnum = 1;
+    for (k = 0; k < SM9_NUM; k++) {
+        if (!sm9sign_doit[k])
+            continue;
+        if (testnum && !mr) {
+            printf("%30ssign    verify    sign/s verify/s\n", " ");
+            testnum = 0;
+        }
+
+        if (mr)
+            printf("+F6:%u:%u:%f:%f\n",
+                   k, test_sm9_curves_bits[k],
+                   sm9sign_results[k][0], sm9sign_results[k][1]);
+        else
+            printf("%4u bit sm9 (%s) %8.4fs %8.4fs %8.1f %8.1f\n",
+                   test_sm9_curves_bits[k],
+                   test_sm9_curves_names[k],
+                   sm9sign_results[k][0], sm9sign_results[k][1],
+                   1.0 / sm9sign_results[k][0], 1.0 / sm9sign_results[k][1]);
+    }
+
+    testnum = 1;
+    for (k = 0; k < SM9_NUM; k++) {
+        if (!sm9enc_doit[k])
+            continue;
+        if (testnum && !mr) {
+            printf("%30sencrypt decrypt   enc/s  dec/s\n", " ");
+            testnum = 0;
+        }
+
+        if (mr)
+            printf("+F6:%u:%u:%f:%f\n",
+                   k, test_sm9_curves_bits[k],
+                   sm9enc_results[k][0], sm9enc_results[k][1]);
+        else
+            printf("%4u bit sm9 (%s) %8.4fs %8.4fs %8.1f %8.1f\n",
+                   test_sm9_curves_bits[k],
+                   test_sm9_curves_names[k],
+                   sm9enc_results[k][0], sm9enc_results[k][1],
+                   1.0 / sm9enc_results[k][0], 1.0 / sm9enc_results[k][1]);
+    }
+
+#endif
+
+    ret = 0;
+
+ end:
+    ERR_print_errors(bio_err);
+    for (i = 0; i < loopargs_len; i++) {
+        OPENSSL_free(loopargs[i].buf_malloc);
+        OPENSSL_free(loopargs[i].buf2_malloc);
+
+#ifndef OPENSSL_NO_RSA
+        for (k = 0; k < RSA_NUM; k++)
+            RSA_free(loopargs[i].rsa_key[k]);
+#endif
+#ifndef OPENSSL_NO_DSA
+        for (k = 0; k < DSA_NUM; k++)
+            DSA_free(loopargs[i].dsa_key[k]);
+#endif
+#ifndef OPENSSL_NO_EC
+        for (k = 0; k < EC_NUM; k++) {
+            EC_KEY_free(loopargs[i].ecdsa[k]);
+            EC_KEY_free(loopargs[i].ecdh_a[k]);
+            EC_KEY_free(loopargs[i].ecdh_b[k]);
+        }
+        OPENSSL_free(loopargs[i].secret_a);
+        OPENSSL_free(loopargs[i].secret_b);
+#endif
+#ifndef OPENSSL_NO_SM2
+        for (k = 0; k < SM2_NUM; k++) {
+            EC_KEY_free(loopargs[i].sm2[k]);
+        }
+# if 0
+        OPENSSL_free(loopargs[i].sm2dh_a);
+        OPENSSL_free(loopargs[i].sm2dh_b);
+# endif
+#endif
+#ifndef OPENSSL_NO_SM9
+        for (k = 0; k < SM9_NUM; k++) {
+            SM9PublicParameters_free(loopargs[i].sm9mpk[k]);
+            SM9PrivateKey_free(loopargs[i].sm9sk[k]);
+            SM9MasterSecret_free(loopargs[i].sm9mst[k]);
+        }
+#endif
+    }
+
+    if (async_jobs > 0) {
+        for (i = 0; i < loopargs_len; i++)
+            ASYNC_WAIT_CTX_free(loopargs[i].wait_ctx);
+    }
+
+    if (async_init) {
+        ASYNC_cleanup_thread();
+    }
+    OPENSSL_free(loopargs);
+    release_engine(e);
+    return (ret);
+}
+
+static void print_message(const char *s, long num, int length)
+{
+#ifdef SIGALRM
+    BIO_printf(bio_err,
+               mr ? "+DT:%s:%d:%d\n"
+               : "Doing %s for %ds on %d size blocks: ", s, SECONDS, length);
+    (void)BIO_flush(bio_err);
+    alarm(SECONDS);
+#else
+    BIO_printf(bio_err,
+               mr ? "+DN:%s:%ld:%d\n"
+               : "Doing %s %ld times on %d size blocks: ", s, num, length);
+    (void)BIO_flush(bio_err);
+#endif
+}
+
+static void pkey_print_message(const char *str, const char *str2, long num,
+                               int bits, int tm)
+{
+#ifdef SIGALRM
+    BIO_printf(bio_err,
+               mr ? "+DTP:%d:%s:%s:%d\n"
+               : "Doing %d bit %s %s's for %ds: ", bits, str, str2, tm);
+    (void)BIO_flush(bio_err);
+    alarm(tm);
+#else
+    BIO_printf(bio_err,
+               mr ? "+DNP:%ld:%d:%s:%s\n"
+               : "Doing %ld %d bit %s %s's: ", num, bits, str, str2);
+    (void)BIO_flush(bio_err);
+#endif
+}
+
+static void print_result(int alg, int run_no, int count, double time_used)
+{
+    if (count == -1) {
+        BIO_puts(bio_err, "EVP error!\n");
+        exit(1);
+    }
+    BIO_printf(bio_err,
+               mr ? "+R:%d:%s:%f\n"
+               : "%d %s's in %.2fs\n", count, names[alg], time_used);
+    results[alg][run_no] = ((double)count) / time_used * lengths[run_no];
+}
+
+#ifndef NO_FORK
+static char *sstrsep(char **string, const char *delim)
+{
+    char isdelim[256];
+    char *token = *string;
+
+    if (**string == 0)
+        return NULL;
+
+    memset(isdelim, 0, sizeof isdelim);
+    isdelim[0] = 1;
+
+    while (*delim) {
+        isdelim[(unsigned char)(*delim)] = 1;
+        delim++;
+    }
+
+    while (!isdelim[(unsigned char)(**string)]) {
+        (*string)++;
+    }
+
+    if (**string) {
+        **string = 0;
+        (*string)++;
+    }
+
+    return token;
+}
+
+static int do_multi(int multi)
+{
+    int n;
+    int fd[2];
+    int *fds;
+    static char sep[] = ":";
+
+    fds = malloc(sizeof(*fds) * multi);
+    for (n = 0; n < multi; ++n) {
+        if (pipe(fd) == -1) {
+            BIO_printf(bio_err, "pipe failure\n");
+            exit(1);
+        }
+        fflush(stdout);
+        (void)BIO_flush(bio_err);
+        if (fork()) {
+            close(fd[1]);
+            fds[n] = fd[0];
+        } else {
+            close(fd[0]);
+            close(1);
+            if (dup(fd[1]) == -1) {
+                BIO_printf(bio_err, "dup failed\n");
+                exit(1);
+            }
+            close(fd[1]);
+            mr = 1;
+            usertime = 0;
+            free(fds);
+            return 0;
+        }
+        printf("Forked child %d\n", n);
+    }
+
+    /* for now, assume the pipe is long enough to take all the output */
+    for (n = 0; n < multi; ++n) {
+        FILE *f;
+        char buf[1024];
+        char *p;
+
+        f = fdopen(fds[n], "r");
+        while (fgets(buf, sizeof buf, f)) {
+            p = strchr(buf, '\n');
+            if (p)
+                *p = '\0';
+            if (buf[0] != '+') {
+                BIO_printf(bio_err, "Don't understand line '%s' from child %d\n",
+                        buf, n);
+                continue;
+            }
+            printf("Got: %s from %d\n", buf, n);
+            if (strncmp(buf, "+F:", 3) == 0) {
+                int alg;
+                int j;
+
+                p = buf + 3;
+                alg = atoi(sstrsep(&p, sep));
+                sstrsep(&p, sep);
+                for (j = 0; j < SIZE_NUM; ++j)
+                    results[alg][j] += atof(sstrsep(&p, sep));
+# ifndef OPENSSL_NO_RSA
+            } else if (strncmp(buf, "+F2:", 4) == 0) {
+                int k;
+                double d;
+
+                p = buf + 4;
+                k = atoi(sstrsep(&p, sep));
+                sstrsep(&p, sep);
+
+                d = atof(sstrsep(&p, sep));
+                if (n)
+                    rsa_results[k][0] = 1 / (1 / rsa_results[k][0] + 1 / d);
+                else
+                    rsa_results[k][0] = d;
+
+                d = atof(sstrsep(&p, sep));
+                if (n)
+                    rsa_results[k][1] = 1 / (1 / rsa_results[k][1] + 1 / d);
+                else
+                    rsa_results[k][1] = d;
+# endif
+            }
+# ifndef OPENSSL_NO_DSA
+            else if (strncmp(buf, "+F3:", 4) == 0) {
+                int k;
+                double d;
+
+                p = buf + 4;
+                k = atoi(sstrsep(&p, sep));
+                sstrsep(&p, sep);
+
+                d = atof(sstrsep(&p, sep));
+                if (n)
+                    dsa_results[k][0] = 1 / (1 / dsa_results[k][0] + 1 / d);
+                else
+                    dsa_results[k][0] = d;
+
+                d = atof(sstrsep(&p, sep));
+                if (n)
+                    dsa_results[k][1] = 1 / (1 / dsa_results[k][1] + 1 / d);
+                else
+                    dsa_results[k][1] = d;
+            }
+# endif
+# ifndef OPENSSL_NO_EC
+            else if (strncmp(buf, "+F4:", 4) == 0) {
+                int k;
+                double d;
+
+                p = buf + 4;
+                k = atoi(sstrsep(&p, sep));
+                sstrsep(&p, sep);
+
+                d = atof(sstrsep(&p, sep));
+                if (n)
+                    ecdsa_results[k][0] =
+                        1 / (1 / ecdsa_results[k][0] + 1 / d);
+                else
+                    ecdsa_results[k][0] = d;
+
+                d = atof(sstrsep(&p, sep));
+                if (n)
+                    ecdsa_results[k][1] =
+                        1 / (1 / ecdsa_results[k][1] + 1 / d);
+                else
+                    ecdsa_results[k][1] = d;
+            } else if (strncmp(buf, "+F5:", 4) == 0) {
+                int k;
+                double d;
+
+                p = buf + 4;
+                k = atoi(sstrsep(&p, sep));
+                sstrsep(&p, sep);
+
+                d = atof(sstrsep(&p, sep));
+                if (n)
+                    ecdh_results[k][0] = 1 / (1 / ecdh_results[k][0] + 1 / d);
+                else
+                    ecdh_results[k][0] = d;
+
+            }
+# endif
+# ifndef OPENSSL_NO_SM2
+            else if (strncmp(buf, "+F6:", 4) == 0) {
+                int k;
+                double d;
+
+                p = buf + 4;
+                k = atoi(sstrsep(&p, sep));
+                sstrsep(&p, sep);
+
+                d = atof(sstrsep(&p, sep));
+                if (n)
+                    sm2sign_results[k][0] =
+                        1 / (1 / sm2sign_results[k][0] + 1 / d);
+                else
+                    sm2sign_results[k][0] = d;
+
+                d = atof(sstrsep(&p, sep));
+                if (n)
+                    sm2sign_results[k][1] =
+                        1 / (1 / sm2sign_results[k][1] + 1 / d);
+                else
+                    sm2sign_results[k][1] = d;
+            } else if (strncmp(buf, "+F7:", 4) == 0) {
+                int k;
+                double d;
+
+                p = buf + 4;
+                k = atoi(sstrsep(&p, sep));
+                sstrsep(&p, sep);
+
+                d = atof(sstrsep(&p, sep));
+                if (n)
+                    sm2enc_results[k][0] =
+                        1 / (1 / sm2enc_results[k][0] + 1 / d);
+                else
+                    sm2enc_results[k][0] = d;
+
+                d = atof(sstrsep(&p, sep));
+                if (n)
+                    sm2enc_results[k][1] =
+                        1 / (1 / sm2enc_results[k][1] + 1 / d);
+                else
+                    sm2enc_results[k][1] = d;
+            }
+# endif
+# ifndef OPENSSL_NO_SM9
+            else if (strncmp(buf, "+F6:", 4) == 0) {
+                int k;
+                double d;
+
+                p = buf + 4;
+                k = atoi(sstrsep(&p, sep));
+                sstrsep(&p, sep);
+
+                d = atof(sstrsep(&p, sep));
+                if (n)
+                    sm9sign_results[k][0] =
+                        1 / (1 / sm9sign_results[k][0] + 1 / d);
+                else
+                    sm9sign_results[k][0] = d;
+
+                d = atof(sstrsep(&p, sep));
+                if (n)
+                    sm9sign_results[k][1] =
+                        1 / (1 / sm9sign_results[k][1] + 1 / d);
+                else
+                    sm9sign_results[k][1] = d;
+            } else if (strncmp(buf, "+F7:", 4) == 0) {
+                int k;
+                double d;
+
+                p = buf + 4;
+                k = atoi(sstrsep(&p, sep));
+                sstrsep(&p, sep);
+
+                d = atof(sstrsep(&p, sep));
+                if (n)
+                    sm9enc_results[k][0] =
+                        1 / (1 / sm9enc_results[k][0] + 1 / d);
+                else
+                    sm9enc_results[k][0] = d;
+
+                d = atof(sstrsep(&p, sep));
+                if (n)
+                    sm9enc_results[k][1] =
+                        1 / (1 / sm9enc_results[k][1] + 1 / d);
+                else
+                    sm9enc_results[k][1] = d;
+            }
+# endif
+            else if (strncmp(buf, "+H:", 3) == 0) {
+                ;
+            } else
+                BIO_printf(bio_err, "Unknown type '%s' from child %d\n", buf, n);
+        }
+
+        fclose(f);
+    }
+    free(fds);
+    return 1;
+}
+#endif
+
+static void multiblock_speed(const EVP_CIPHER *evp_cipher)
+{
+    static int mblengths[] =
+        { 8 * 1024, 2 * 8 * 1024, 4 * 8 * 1024, 8 * 8 * 1024, 8 * 16 * 1024 };
+    int j, count, num = OSSL_NELEM(mblengths);
+    const char *alg_name;
+    unsigned char *inp, *out, no_key[32], no_iv[16];
+    EVP_CIPHER_CTX *ctx;
+    double d = 0.0;
+
+    inp = app_malloc(mblengths[num - 1], "multiblock input buffer");
+    out = app_malloc(mblengths[num - 1] + 1024, "multiblock output buffer");
+    ctx = EVP_CIPHER_CTX_new();
+    EVP_EncryptInit_ex(ctx, evp_cipher, NULL, no_key, no_iv);
+    EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_MAC_KEY, sizeof(no_key),
+                        no_key);
+    alg_name = OBJ_nid2ln(EVP_CIPHER_nid(evp_cipher));
+
+    for (j = 0; j < num; j++) {
+        print_message(alg_name, 0, mblengths[j]);
+        Time_F(START);
+        for (count = 0, run = 1; run && count < 0x7fffffff; count++) {
+            unsigned char aad[EVP_AEAD_TLS1_AAD_LEN];
+            EVP_CTRL_TLS1_1_MULTIBLOCK_PARAM mb_param;
+            size_t len = mblengths[j];
+            int packlen;
+
+            memset(aad, 0, 8);  /* avoid uninitialized values */
+            aad[8] = 23;        /* SSL3_RT_APPLICATION_DATA */
+            aad[9] = 3;         /* version */
+            aad[10] = 2;
+            aad[11] = 0;        /* length */
+            aad[12] = 0;
+            mb_param.out = NULL;
+            mb_param.inp = aad;
+            mb_param.len = len;
+            mb_param.interleave = 8;
+
+            packlen = EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_TLS1_1_MULTIBLOCK_AAD,
+                                          sizeof(mb_param), &mb_param);
+
+            if (packlen > 0) {
+                mb_param.out = out;
+                mb_param.inp = inp;
+                mb_param.len = len;
+                EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_TLS1_1_MULTIBLOCK_ENCRYPT,
+                                    sizeof(mb_param), &mb_param);
+            } else {
+                int pad;
+
+                RAND_bytes(out, 16);
+                len += 16;
+                aad[11] = len >> 8;
+                aad[12] = len;
+                pad = EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_TLS1_AAD,
+                                          EVP_AEAD_TLS1_AAD_LEN, aad);
+                EVP_Cipher(ctx, out, inp, len + pad);
+            }
+        }
+        d = Time_F(STOP);
+        BIO_printf(bio_err, mr ? "+R:%d:%s:%f\n"
+                   : "%d %s's in %.2fs\n", count, "evp", d);
+        results[D_EVP][j] = ((double)count) / d * mblengths[j];
+    }
+
+    if (mr) {
+        fprintf(stdout, "+H");
+        for (j = 0; j < num; j++)
+            fprintf(stdout, ":%d", mblengths[j]);
+        fprintf(stdout, "\n");
+        fprintf(stdout, "+F:%d:%s", D_EVP, alg_name);
+        for (j = 0; j < num; j++)
+            fprintf(stdout, ":%.2f", results[D_EVP][j]);
+        fprintf(stdout, "\n");
+    } else {
+        fprintf(stdout,
+                "The 'numbers' are in 1000s of bytes per second processed.\n");
+        fprintf(stdout, "type                    ");
+        for (j = 0; j < num; j++)
+            fprintf(stdout, "%7d bytes", mblengths[j]);
+        fprintf(stdout, "\n");
+        fprintf(stdout, "%-24s", alg_name);
+
+        for (j = 0; j < num; j++) {
+            if (results[D_EVP][j] > 10000)
+                fprintf(stdout, " %11.2fk", results[D_EVP][j] / 1e3);
+            else
+                fprintf(stdout, " %11.2f ", results[D_EVP][j]);
+        }
+        fprintf(stdout, "\n");
+    }
+
+    OPENSSL_free(inp);
+    OPENSSL_free(out);
+    EVP_CIPHER_CTX_free(ctx);
+}
diff --git a/apps/spkac.c b/apps/spkac.c
new file mode 100644
index 000000000..848d67dcf
--- /dev/null
+++ b/apps/spkac.c
@@ -0,0 +1,199 @@
+/*
+ * Copyright 1999-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include "apps.h"
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#ifndef OPENSSL_NO_MD5
+# define SPKAC_DEFAULT_MD EVP_md5()
+#else
+# define SPKAC_DEFAULT_MD EVP_sm3()
+#endif
+
+typedef enum OPTION_choice {
+    OPT_ERR = -1, OPT_EOF = 0, OPT_HELP,
+    OPT_NOOUT, OPT_PUBKEY, OPT_VERIFY, OPT_IN, OPT_OUT,
+    OPT_ENGINE, OPT_KEY, OPT_CHALLENGE, OPT_PASSIN, OPT_SPKAC,
+    OPT_SPKSECT
+} OPTION_CHOICE;
+
+OPTIONS spkac_options[] = {
+    {"help", OPT_HELP, '-', "Display this summary"},
+    {"in", OPT_IN, '<', "Input file"},
+    {"out", OPT_OUT, '>', "Output file"},
+    {"key", OPT_KEY, '<', "Create SPKAC using private key"},
+    {"passin", OPT_PASSIN, 's', "Input file pass phrase source"},
+    {"challenge", OPT_CHALLENGE, 's', "Challenge string"},
+    {"spkac", OPT_SPKAC, 's', "Alternative SPKAC name"},
+    {"noout", OPT_NOOUT, '-', "Don't print SPKAC"},
+    {"pubkey", OPT_PUBKEY, '-', "Output public key"},
+    {"verify", OPT_VERIFY, '-', "Verify SPKAC signature"},
+    {"spksect", OPT_SPKSECT, 's',
+     "Specify the name of an SPKAC-dedicated section of configuration"},
+#ifndef OPENSSL_NO_ENGINE
+    {"engine", OPT_ENGINE, 's', "Use engine, possibly a hardware device"},
+#endif
+    {NULL}
+};
+
+int spkac_main(int argc, char **argv)
+{
+    BIO *out = NULL;
+    CONF *conf = NULL;
+    ENGINE *e = NULL;
+    EVP_PKEY *pkey = NULL;
+    NETSCAPE_SPKI *spki = NULL;
+    char *challenge = NULL, *keyfile = NULL;
+    char *infile = NULL, *outfile = NULL, *passinarg = NULL, *passin = NULL;
+    char *spkstr = NULL, *prog;
+    const char *spkac = "SPKAC", *spksect = "default";
+    int i, ret = 1, verify = 0, noout = 0, pubkey = 0;
+    OPTION_CHOICE o;
+
+    prog = opt_init(argc, argv, spkac_options);
+    while ((o = opt_next()) != OPT_EOF) {
+        switch (o) {
+        case OPT_EOF:
+        case OPT_ERR:
+ opthelp:
+            BIO_printf(bio_err, "%s: Use -help for summary.\n", prog);
+            goto end;
+        case OPT_HELP:
+            opt_help(spkac_options);
+            ret = 0;
+            goto end;
+        case OPT_IN:
+            infile = opt_arg();
+            break;
+        case OPT_OUT:
+            outfile = opt_arg();
+            break;
+        case OPT_NOOUT:
+            noout = 1;
+            break;
+        case OPT_PUBKEY:
+            pubkey = 1;
+            break;
+        case OPT_VERIFY:
+            verify = 1;
+            break;
+        case OPT_PASSIN:
+            passinarg = opt_arg();
+            break;
+        case OPT_KEY:
+            keyfile = opt_arg();
+            break;
+        case OPT_CHALLENGE:
+            challenge = opt_arg();
+            break;
+        case OPT_SPKAC:
+            spkac = opt_arg();
+            break;
+        case OPT_SPKSECT:
+            spksect = opt_arg();
+            break;
+        case OPT_ENGINE:
+            e = setup_engine(opt_arg(), 0);
+            break;
+        }
+    }
+    argc = opt_num_rest();
+    if (argc != 0)
+        goto opthelp;
+
+    if (!app_passwd(passinarg, NULL, &passin, NULL)) {
+        BIO_printf(bio_err, "Error getting password\n");
+        goto end;
+    }
+
+    if (keyfile) {
+        pkey = load_key(strcmp(keyfile, "-") ? keyfile : NULL,
+                        FORMAT_PEM, 1, passin, e, "private key");
+        if (!pkey) {
+            goto end;
+        }
+        spki = NETSCAPE_SPKI_new();
+        if (challenge)
+            ASN1_STRING_set(spki->spkac->challenge,
+                            challenge, (int)strlen(challenge));
+        NETSCAPE_SPKI_set_pubkey(spki, pkey);
+        NETSCAPE_SPKI_sign(spki, pkey, SPKAC_DEFAULT_MD);
+        spkstr = NETSCAPE_SPKI_b64_encode(spki);
+
+        out = bio_open_default(outfile, 'w', FORMAT_TEXT);
+        if (out == NULL) {
+            OPENSSL_free(spkstr);
+            goto end;
+        }
+        BIO_printf(out, "SPKAC=%s\n", spkstr);
+        OPENSSL_free(spkstr);
+        ret = 0;
+        goto end;
+    }
+
+    if ((conf = app_load_config(infile)) == NULL)
+        goto end;
+
+    spkstr = NCONF_get_string(conf, spksect, spkac);
+
+    if (spkstr == NULL) {
+        BIO_printf(bio_err, "Can't find SPKAC called \"%s\"\n", spkac);
+        ERR_print_errors(bio_err);
+        goto end;
+    }
+
+    spki = NETSCAPE_SPKI_b64_decode(spkstr, -1);
+
+    if (!spki) {
+        BIO_printf(bio_err, "Error loading SPKAC\n");
+        ERR_print_errors(bio_err);
+        goto end;
+    }
+
+    out = bio_open_default(outfile, 'w', FORMAT_TEXT);
+    if (out == NULL)
+        goto end;
+
+    if (!noout)
+        NETSCAPE_SPKI_print(out, spki);
+    pkey = NETSCAPE_SPKI_get_pubkey(spki);
+    if (verify) {
+        i = NETSCAPE_SPKI_verify(spki, pkey);
+        if (i > 0)
+            BIO_printf(bio_err, "Signature OK\n");
+        else {
+            BIO_printf(bio_err, "Signature Failure\n");
+            ERR_print_errors(bio_err);
+            goto end;
+        }
+    }
+    if (pubkey)
+        PEM_write_bio_PUBKEY(out, pkey);
+
+    ret = 0;
+
+ end:
+    NCONF_free(conf);
+    NETSCAPE_SPKI_free(spki);
+    BIO_free_all(out);
+    EVP_PKEY_free(pkey);
+    release_engine(e);
+    OPENSSL_free(passin);
+    return (ret);
+}
diff --git a/apps/srp.c b/apps/srp.c
new file mode 100644
index 000000000..add010037
--- /dev/null
+++ b/apps/srp.c
@@ -0,0 +1,609 @@
+/*
+ * Copyright 2004-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#ifdef OPENSSL_NO_SRP
+NON_EMPTY_TRANSLATION_UNIT
+#else
+
+# include 
+# include 
+# include 
+# include 
+# include 
+# include 
+# include 
+# include 
+# include 
+# include "apps.h"
+
+# define BASE_SECTION    "srp"
+# define CONFIG_FILE "openssl.cnf"
+
+# define ENV_RANDFILE            "RANDFILE"
+
+# define ENV_DATABASE            "srpvfile"
+# define ENV_DEFAULT_SRP         "default_srp"
+
+static int get_index(CA_DB *db, char *id, char type)
+{
+    char **pp;
+    int i;
+    if (id == NULL)
+        return -1;
+    if (type == DB_SRP_INDEX)
+        for (i = 0; i < sk_OPENSSL_PSTRING_num(db->db->data); i++) {
+            pp = sk_OPENSSL_PSTRING_value(db->db->data, i);
+            if (pp[DB_srptype][0] == DB_SRP_INDEX
+                && strcmp(id, pp[DB_srpid]) == 0)
+                return i;
+    } else
+        for (i = 0; i < sk_OPENSSL_PSTRING_num(db->db->data); i++) {
+            pp = sk_OPENSSL_PSTRING_value(db->db->data, i);
+
+            if (pp[DB_srptype][0] != DB_SRP_INDEX
+                && strcmp(id, pp[DB_srpid]) == 0)
+                return i;
+        }
+
+    return -1;
+}
+
+static void print_entry(CA_DB *db, int indx, int verbose, char *s)
+{
+    if (indx >= 0 && verbose) {
+        int j;
+        char **pp = sk_OPENSSL_PSTRING_value(db->db->data, indx);
+        BIO_printf(bio_err, "%s \"%s\"\n", s, pp[DB_srpid]);
+        for (j = 0; j < DB_NUMBER; j++) {
+            BIO_printf(bio_err, "  %d = \"%s\"\n", j, pp[j]);
+        }
+    }
+}
+
+static void print_index(CA_DB *db, int indexindex, int verbose)
+{
+    print_entry(db, indexindex, verbose, "g N entry");
+}
+
+static void print_user(CA_DB *db, int userindex, int verbose)
+{
+    if (verbose > 0) {
+        char **pp = sk_OPENSSL_PSTRING_value(db->db->data, userindex);
+
+        if (pp[DB_srptype][0] != 'I') {
+            print_entry(db, userindex, verbose, "User entry");
+            print_entry(db, get_index(db, pp[DB_srpgN], 'I'), verbose,
+                        "g N entry");
+        }
+
+    }
+}
+
+static int update_index(CA_DB *db, char **row)
+{
+    char **irow;
+    int i;
+
+    irow = app_malloc(sizeof(*irow) * (DB_NUMBER + 1), "row pointers");
+    for (i = 0; i < DB_NUMBER; i++) {
+        irow[i] = row[i];
+        row[i] = NULL;
+    }
+    irow[DB_NUMBER] = NULL;
+
+    if (!TXT_DB_insert(db->db, irow)) {
+        BIO_printf(bio_err, "failed to update srpvfile\n");
+        BIO_printf(bio_err, "TXT_DB error number %ld\n", db->db->error);
+        OPENSSL_free(irow);
+        return 0;
+    }
+    return 1;
+}
+
+static char *lookup_conf(const CONF *conf, const char *section, const char *tag)
+{
+    char *entry = NCONF_get_string(conf, section, tag);
+    if (entry == NULL)
+        BIO_printf(bio_err, "variable lookup failed for %s::%s\n", section, tag);
+    return entry;
+}
+
+static char *srp_verify_user(const char *user, const char *srp_verifier,
+                             char *srp_usersalt, const char *g, const char *N,
+                             const char *passin, int verbose)
+{
+    char password[1024];
+    PW_CB_DATA cb_tmp;
+    char *verifier = NULL;
+    char *gNid = NULL;
+
+    cb_tmp.prompt_info = user;
+    cb_tmp.password = passin;
+
+    if (password_callback(password, sizeof(password), 0, &cb_tmp) > 0) {
+        if (verbose)
+            BIO_printf(bio_err,
+                       "Validating\n   user=\"%s\"\n srp_verifier=\"%s\"\n srp_usersalt=\"%s\"\n g=\"%s\"\n N=\"%s\"\n",
+                       user, srp_verifier, srp_usersalt, g, N);
+        BIO_printf(bio_err, "Pass %s\n", password);
+
+        OPENSSL_assert(srp_usersalt != NULL);
+        if (!
+            (gNid =
+             SRP_create_verifier(user, password, &srp_usersalt, &verifier, N,
+                                 g))) {
+            BIO_printf(bio_err, "Internal error validating SRP verifier\n");
+        } else {
+            if (strcmp(verifier, srp_verifier))
+                gNid = NULL;
+            OPENSSL_free(verifier);
+        }
+    }
+    return gNid;
+}
+
+static char *srp_create_user(char *user, char **srp_verifier,
+                             char **srp_usersalt, char *g, char *N,
+                             char *passout, int verbose)
+{
+    char password[1024];
+    PW_CB_DATA cb_tmp;
+    char *gNid = NULL;
+    char *salt = NULL;
+    cb_tmp.prompt_info = user;
+    cb_tmp.password = passout;
+
+    if (password_callback(password, sizeof(password), 1, &cb_tmp) > 0) {
+        if (verbose)
+            BIO_printf(bio_err, "Creating\n user=\"%s\"\n g=\"%s\"\n N=\"%s\"\n",
+                       user, g, N);
+        if (!
+            (gNid =
+             SRP_create_verifier(user, password, &salt, srp_verifier, N,
+                                 g))) {
+            BIO_printf(bio_err, "Internal error creating SRP verifier\n");
+        } else
+            *srp_usersalt = salt;
+        if (verbose > 1)
+            BIO_printf(bio_err, "gNid=%s salt =\"%s\"\n verifier =\"%s\"\n", gNid,
+                       salt, *srp_verifier);
+
+    }
+    return gNid;
+}
+
+typedef enum OPTION_choice {
+    OPT_ERR = -1, OPT_EOF = 0, OPT_HELP,
+    OPT_VERBOSE, OPT_CONFIG, OPT_NAME, OPT_SRPVFILE, OPT_ADD,
+    OPT_DELETE, OPT_MODIFY, OPT_LIST, OPT_GN, OPT_USERINFO,
+    OPT_PASSIN, OPT_PASSOUT, OPT_ENGINE
+} OPTION_CHOICE;
+
+OPTIONS srp_options[] = {
+    {"help", OPT_HELP, '-', "Display this summary"},
+    {"verbose", OPT_VERBOSE, '-', "Talk a lot while doing things"},
+    {"config", OPT_CONFIG, '<', "A config file"},
+    {"name", OPT_NAME, 's', "The particular srp definition to use"},
+    {"srpvfile", OPT_SRPVFILE, '<', "The srp verifier file name"},
+    {"add", OPT_ADD, '-', "Add a user and srp verifier"},
+    {"modify", OPT_MODIFY, '-',
+     "Modify the srp verifier of an existing user"},
+    {"delete", OPT_DELETE, '-', "Delete user from verifier file"},
+    {"list", OPT_LIST, '-', "List users"},
+    {"gn", OPT_GN, 's', "Set g and N values to be used for new verifier"},
+    {"userinfo", OPT_USERINFO, 's', "Additional info to be set for user"},
+    {"passin", OPT_PASSIN, 's', "Input file pass phrase source"},
+    {"passout", OPT_PASSOUT, 's', "Output file pass phrase source"},
+# ifndef OPENSSL_NO_ENGINE
+    {"engine", OPT_ENGINE, 's', "Use engine, possibly a hardware device"},
+# endif
+    {NULL}
+};
+
+int srp_main(int argc, char **argv)
+{
+    ENGINE *e = NULL;
+    CA_DB *db = NULL;
+    CONF *conf = NULL;
+    int gNindex = -1, maxgN = -1, ret = 1, errors = 0, verbose = 0, i;
+    int doupdatedb = 0, mode = OPT_ERR;
+    char *user = NULL, *passinarg = NULL, *passoutarg = NULL;
+    char *passin = NULL, *passout = NULL, *gN = NULL, *userinfo = NULL;
+    char *randfile = NULL, *section = NULL;
+    char **gNrow = NULL, *configfile = NULL;
+    char *srpvfile = NULL, **pp, *prog;
+    OPTION_CHOICE o;
+
+    prog = opt_init(argc, argv, srp_options);
+    while ((o = opt_next()) != OPT_EOF) {
+        switch (o) {
+        case OPT_EOF:
+        case OPT_ERR:
+ opthelp:
+            BIO_printf(bio_err, "%s: Use -help for summary.\n", prog);
+            goto end;
+        case OPT_HELP:
+            opt_help(srp_options);
+            ret = 0;
+            goto end;
+        case OPT_VERBOSE:
+            verbose++;
+            break;
+        case OPT_CONFIG:
+            configfile = opt_arg();
+            break;
+        case OPT_NAME:
+            section = opt_arg();
+            break;
+        case OPT_SRPVFILE:
+            srpvfile = opt_arg();
+            break;
+        case OPT_ADD:
+        case OPT_DELETE:
+        case OPT_MODIFY:
+        case OPT_LIST:
+            if (mode != OPT_ERR) {
+                BIO_printf(bio_err,
+                           "%s: Only one of -add/delete-modify/-list\n",
+                           prog);
+                goto opthelp;
+            }
+            mode = o;
+            break;
+        case OPT_GN:
+            gN = opt_arg();
+            break;
+        case OPT_USERINFO:
+            userinfo = opt_arg();
+            break;
+        case OPT_PASSIN:
+            passinarg = opt_arg();
+            break;
+        case OPT_PASSOUT:
+            passoutarg = opt_arg();
+            break;
+        case OPT_ENGINE:
+            e = setup_engine(opt_arg(), 0);
+            break;
+        }
+    }
+    argc = opt_num_rest();
+    argv = opt_rest();
+
+    if (srpvfile && configfile) {
+        BIO_printf(bio_err,
+                   "-srpvfile and -configfile cannot be specified together.\n");
+        goto end;
+    }
+    if (mode == OPT_ERR) {
+        BIO_printf(bio_err,
+                   "Exactly one of the options -add, -delete, -modify -list must be specified.\n");
+        goto opthelp;
+    }
+    if ((mode == OPT_DELETE || mode == OPT_MODIFY || mode == OPT_ADD)
+        && argc < 1) {
+        BIO_printf(bio_err,
+                   "Need at least one user for options -add, -delete, -modify. \n");
+        goto opthelp;
+    }
+    if ((passin || passout) && argc != 1) {
+        BIO_printf(bio_err,
+                   "-passin, -passout arguments only valid with one user.\n");
+        goto opthelp;
+    }
+
+    if (!app_passwd(passinarg, passoutarg, &passin, &passout)) {
+        BIO_printf(bio_err, "Error getting passwords\n");
+        goto end;
+    }
+
+    if (!srpvfile) {
+        if (!configfile)
+            configfile = default_config_file;
+
+        if (verbose)
+            BIO_printf(bio_err, "Using configuration from %s\n",
+                       configfile);
+        conf = app_load_config(configfile);
+        if (conf == NULL)
+            goto end;
+        if (configfile != default_config_file && !app_load_modules(conf))
+            goto end;
+
+        /* Lets get the config section we are using */
+        if (section == NULL) {
+            if (verbose)
+                BIO_printf(bio_err,
+                           "trying to read " ENV_DEFAULT_SRP
+                           " in " BASE_SECTION "\n");
+
+            section = lookup_conf(conf, BASE_SECTION, ENV_DEFAULT_SRP);
+            if (section == NULL)
+                goto end;
+        }
+
+        if (randfile == NULL)
+            randfile = NCONF_get_string(conf, BASE_SECTION, "RANDFILE");
+
+        if (verbose)
+            BIO_printf(bio_err,
+                       "trying to read " ENV_DATABASE " in section \"%s\"\n",
+                       section);
+
+        srpvfile = lookup_conf(conf, section, ENV_DATABASE);
+        if (srpvfile == NULL)
+            goto end;
+    }
+    if (randfile == NULL)
+        ERR_clear_error();
+    else
+        app_RAND_load_file(randfile, 0);
+
+    if (verbose)
+        BIO_printf(bio_err, "Trying to read SRP verifier file \"%s\"\n",
+                   srpvfile);
+
+    db = load_index(srpvfile, NULL);
+    if (db == NULL)
+        goto end;
+
+    /* Lets check some fields */
+    for (i = 0; i < sk_OPENSSL_PSTRING_num(db->db->data); i++) {
+        pp = sk_OPENSSL_PSTRING_value(db->db->data, i);
+
+        if (pp[DB_srptype][0] == DB_SRP_INDEX) {
+            maxgN = i;
+            if ((gNindex < 0) && (gN != NULL) && strcmp(gN, pp[DB_srpid]) == 0)
+                gNindex = i;
+
+            print_index(db, i, verbose > 1);
+        }
+    }
+
+    if (verbose)
+        BIO_printf(bio_err, "Database initialised\n");
+
+    if (gNindex >= 0) {
+        gNrow = sk_OPENSSL_PSTRING_value(db->db->data, gNindex);
+        print_entry(db, gNindex, verbose > 1, "Default g and N");
+    } else if (maxgN > 0 && !SRP_get_default_gN(gN)) {
+        BIO_printf(bio_err, "No g and N value for index \"%s\"\n", gN);
+        goto end;
+    } else {
+        if (verbose)
+            BIO_printf(bio_err, "Database has no g N information.\n");
+        gNrow = NULL;
+    }
+
+    if (verbose > 1)
+        BIO_printf(bio_err, "Starting user processing\n");
+
+    if (argc > 0)
+        user = *(argv++);
+
+    while (mode == OPT_LIST || user) {
+        int userindex = -1;
+
+        if (user != NULL && verbose > 1)
+            BIO_printf(bio_err, "Processing user \"%s\"\n", user);
+        if ((userindex = get_index(db, user, 'U')) >= 0) {
+            print_user(db, userindex, (verbose > 0) || mode == OPT_LIST);
+        }
+
+        if (mode == OPT_LIST) {
+            if (user == NULL) {
+                BIO_printf(bio_err, "List all users\n");
+
+                for (i = 0; i < sk_OPENSSL_PSTRING_num(db->db->data); i++) {
+                    print_user(db, i, 1);
+                }
+            } else if (userindex < 0) {
+                BIO_printf(bio_err,
+                           "user \"%s\" does not exist, ignored. t\n", user);
+                errors++;
+            }
+        } else if (mode == OPT_ADD) {
+            if (userindex >= 0) {
+                /* reactivation of a new user */
+                char **row =
+                    sk_OPENSSL_PSTRING_value(db->db->data, userindex);
+                BIO_printf(bio_err, "user \"%s\" reactivated.\n", user);
+                row[DB_srptype][0] = 'V';
+
+                doupdatedb = 1;
+            } else {
+                char *row[DB_NUMBER];
+                char *gNid;
+                row[DB_srpverifier] = NULL;
+                row[DB_srpsalt] = NULL;
+                row[DB_srpinfo] = NULL;
+                if (!
+                    (gNid =
+                     srp_create_user(user, &(row[DB_srpverifier]),
+                                     &(row[DB_srpsalt]),
+                                     gNrow ? gNrow[DB_srpsalt] : gN,
+                                     gNrow ? gNrow[DB_srpverifier] : NULL,
+                                     passout, verbose))) {
+                    BIO_printf(bio_err,
+                               "Cannot create srp verifier for user \"%s\", operation abandoned .\n",
+                               user);
+                    errors++;
+                    goto end;
+                }
+                row[DB_srpid] = OPENSSL_strdup(user);
+                row[DB_srptype] = OPENSSL_strdup("v");
+                row[DB_srpgN] = OPENSSL_strdup(gNid);
+
+                if ((row[DB_srpid] == NULL)
+                    || (row[DB_srpgN] == NULL)
+                    || (row[DB_srptype] == NULL)
+                    || (row[DB_srpverifier] == NULL)
+                    || (row[DB_srpsalt] == NULL)
+                    || (userinfo
+                        && ((row[DB_srpinfo] = OPENSSL_strdup(userinfo)) == NULL))
+                    || !update_index(db, row)) {
+                    OPENSSL_free(row[DB_srpid]);
+                    OPENSSL_free(row[DB_srpgN]);
+                    OPENSSL_free(row[DB_srpinfo]);
+                    OPENSSL_free(row[DB_srptype]);
+                    OPENSSL_free(row[DB_srpverifier]);
+                    OPENSSL_free(row[DB_srpsalt]);
+                    goto end;
+                }
+                doupdatedb = 1;
+            }
+        } else if (mode == OPT_MODIFY) {
+            if (userindex < 0) {
+                BIO_printf(bio_err,
+                           "user \"%s\" does not exist, operation ignored.\n",
+                           user);
+                errors++;
+            } else {
+
+                char **row =
+                    sk_OPENSSL_PSTRING_value(db->db->data, userindex);
+                char type = row[DB_srptype][0];
+                if (type == 'v') {
+                    BIO_printf(bio_err,
+                               "user \"%s\" already updated, operation ignored.\n",
+                               user);
+                    errors++;
+                } else {
+                    char *gNid;
+
+                    if (row[DB_srptype][0] == 'V') {
+                        int user_gN;
+                        char **irow = NULL;
+                        if (verbose)
+                            BIO_printf(bio_err,
+                                       "Verifying password for user \"%s\"\n",
+                                       user);
+                        if ((user_gN =
+                             get_index(db, row[DB_srpgN], DB_SRP_INDEX)) >= 0)
+                            irow =
+                                sk_OPENSSL_PSTRING_value(db->db->data,
+                                                         userindex);
+
+                        if (!srp_verify_user
+                            (user, row[DB_srpverifier], row[DB_srpsalt],
+                             irow ? irow[DB_srpsalt] : row[DB_srpgN],
+                             irow ? irow[DB_srpverifier] : NULL, passin,
+                             verbose)) {
+                            BIO_printf(bio_err,
+                                       "Invalid password for user \"%s\", operation abandoned.\n",
+                                       user);
+                            errors++;
+                            goto end;
+                        }
+                    }
+                    if (verbose)
+                        BIO_printf(bio_err, "Password for user \"%s\" ok.\n",
+                                   user);
+
+                    if (!
+                        (gNid =
+                         srp_create_user(user, &(row[DB_srpverifier]),
+                                         &(row[DB_srpsalt]),
+                                         gNrow ? gNrow[DB_srpsalt] : NULL,
+                                         gNrow ? gNrow[DB_srpverifier] : NULL,
+                                         passout, verbose))) {
+                        BIO_printf(bio_err,
+                                   "Cannot create srp verifier for user \"%s\", operation abandoned.\n",
+                                   user);
+                        errors++;
+                        goto end;
+                    }
+
+                    row[DB_srptype][0] = 'v';
+                    row[DB_srpgN] = OPENSSL_strdup(gNid);
+
+                    if (row[DB_srpid] == NULL
+                        || row[DB_srpgN] == NULL
+                        || row[DB_srptype] == NULL
+                        || row[DB_srpverifier] == NULL
+                        || row[DB_srpsalt] == NULL
+                        || (userinfo
+                            && ((row[DB_srpinfo] = OPENSSL_strdup(userinfo))
+                                == NULL)))
+                        goto end;
+
+                    doupdatedb = 1;
+                }
+            }
+        } else if (mode == OPT_DELETE) {
+            if (userindex < 0) {
+                BIO_printf(bio_err,
+                           "user \"%s\" does not exist, operation ignored. t\n",
+                           user);
+                errors++;
+            } else {
+                char **xpp = sk_OPENSSL_PSTRING_value(db->db->data, userindex);
+
+                BIO_printf(bio_err, "user \"%s\" revoked. t\n", user);
+                xpp[DB_srptype][0] = 'R';
+                doupdatedb = 1;
+            }
+        }
+        if (--argc > 0)
+            user = *(argv++);
+        else {
+            user = NULL;
+        }
+    }
+
+    if (verbose)
+        BIO_printf(bio_err, "User procession done.\n");
+
+    if (doupdatedb) {
+        /* Lets check some fields */
+        for (i = 0; i < sk_OPENSSL_PSTRING_num(db->db->data); i++) {
+            pp = sk_OPENSSL_PSTRING_value(db->db->data, i);
+
+            if (pp[DB_srptype][0] == 'v') {
+                pp[DB_srptype][0] = 'V';
+                print_user(db, i, verbose);
+            }
+        }
+
+        if (verbose)
+            BIO_printf(bio_err, "Trying to update srpvfile.\n");
+        if (!save_index(srpvfile, "new", db))
+            goto end;
+
+        if (verbose)
+            BIO_printf(bio_err, "Temporary srpvfile created.\n");
+        if (!rotate_index(srpvfile, "new", "old"))
+            goto end;
+
+        if (verbose)
+            BIO_printf(bio_err, "srpvfile updated.\n");
+    }
+
+    ret = (errors != 0);
+ end:
+    if (errors != 0)
+        if (verbose)
+            BIO_printf(bio_err, "User errors %d.\n", errors);
+
+    if (verbose)
+        BIO_printf(bio_err, "SRP terminating with code %d.\n", ret);
+
+    OPENSSL_free(passin);
+    OPENSSL_free(passout);
+    if (ret)
+        ERR_print_errors(bio_err);
+    if (randfile)
+        app_RAND_write_file(randfile);
+    NCONF_free(conf);
+    free_index(db);
+    release_engine(e);
+    return (ret);
+}
+#endif
diff --git a/apps/testCA.pem b/apps/testCA.pem
new file mode 100644
index 000000000..dcb710aa9
--- /dev/null
+++ b/apps/testCA.pem
@@ -0,0 +1,8 @@
+-----BEGIN CERTIFICATE REQUEST-----
+MIIBBzCBsgIBADBNMQswCQYDVQQGEwJBVTETMBEGA1UECBMKUXVlZW5zbGFuZDEX
+MBUGA1UEChMOTWluY29tIFB0eSBMdGQxEDAOBgNVBAMTB1RFU1QgQ0EwXDANBgkq
+hkiG9w0BAQEFAANLADBIAkEAzW9brgA8efT2ODB+NrsflJZj3KKqKsm4OrXTRqfL
+VETj1ws/zCXl42XJAxdWQMCP0liKfc9Ut4xi1qCVI7N07wIDAQABoAAwDQYJKoZI
+hvcNAQEEBQADQQBjZZ42Det9Uw0AFwJy4ufUEy5Cv74pxBp5SZnljgHY+Az0Hs2S
+uNkIegr2ITX5azKi9nOkg9ZmsmGG13FIjiC/
+-----END CERTIFICATE REQUEST-----
diff --git a/apps/testdsa.h b/apps/testdsa.h
new file mode 100644
index 000000000..1e4502a10
--- /dev/null
+++ b/apps/testdsa.h
@@ -0,0 +1,290 @@
+/*
+ * Copyright 1998-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/* used by speed.c */
+DSA *get_dsa512(void);
+DSA *get_dsa1024(void);
+DSA *get_dsa2048(void);
+
+static unsigned char dsa512_priv[] = {
+    0x65, 0xe5, 0xc7, 0x38, 0x60, 0x24, 0xb5, 0x89, 0xd4, 0x9c, 0xeb, 0x4c,
+    0x9c, 0x1d, 0x7a, 0x22, 0xbd, 0xd1, 0xc2, 0xd2,
+};
+
+static unsigned char dsa512_pub[] = {
+    0x00, 0x95, 0xa7, 0x0d, 0xec, 0x93, 0x68, 0xba, 0x5f, 0xf7, 0x5f, 0x07,
+    0xf2, 0x3b, 0xad, 0x6b, 0x01, 0xdc, 0xbe, 0xec, 0xde, 0x04, 0x7a, 0x3a,
+    0x27, 0xb3, 0xec, 0x49, 0xfd, 0x08, 0x43, 0x3d, 0x7e, 0xa8, 0x2c, 0x5e,
+    0x7b, 0xbb, 0xfc, 0xf4, 0x6e, 0xeb, 0x6c, 0xb0, 0x6e, 0xf8, 0x02, 0x12,
+    0x8c, 0x38, 0x5d, 0x83, 0x56, 0x7d, 0xee, 0x53, 0x05, 0x3e, 0x24, 0x84,
+    0xbe, 0xba, 0x0a, 0x6b, 0xc8,
+};
+
+static unsigned char dsa512_p[] = {
+    0x9D, 0x1B, 0x69, 0x8E, 0x26, 0xDB, 0xF2, 0x2B, 0x11, 0x70, 0x19, 0x86,
+    0xF6, 0x19, 0xC8, 0xF8, 0x19, 0xF2, 0x18, 0x53, 0x94, 0x46, 0x06, 0xD0,
+    0x62, 0x50, 0x33, 0x4B, 0x02, 0x3C, 0x52, 0x30, 0x03, 0x8B, 0x3B, 0xF9,
+    0x5F, 0xD1, 0x24, 0x06, 0x4F, 0x7B, 0x4C, 0xBA, 0xAA, 0x40, 0x9B, 0xFD,
+    0x96, 0xE4, 0x37, 0x33, 0xBB, 0x2D, 0x5A, 0xD7, 0x5A, 0x11, 0x40, 0x66,
+    0xA2, 0x76, 0x7D, 0x31,
+};
+
+static unsigned char dsa512_q[] = {
+    0xFB, 0x53, 0xEF, 0x50, 0xB4, 0x40, 0x92, 0x31, 0x56, 0x86, 0x53, 0x7A,
+    0xE8, 0x8B, 0x22, 0x9A, 0x49, 0xFB, 0x71, 0x8F,
+};
+
+static unsigned char dsa512_g[] = {
+    0x83, 0x3E, 0x88, 0xE5, 0xC5, 0x89, 0x73, 0xCE, 0x3B, 0x6C, 0x01, 0x49,
+    0xBF, 0xB3, 0xC7, 0x9F, 0x0A, 0xEA, 0x44, 0x91, 0xE5, 0x30, 0xAA, 0xD9,
+    0xBE, 0x5B, 0x5F, 0xB7, 0x10, 0xD7, 0x89, 0xB7, 0x8E, 0x74, 0xFB, 0xCF,
+    0x29, 0x1E, 0xEB, 0xA8, 0x2C, 0x54, 0x51, 0xB8, 0x10, 0xDE, 0xA0, 0xCE,
+    0x2F, 0xCC, 0x24, 0x6B, 0x90, 0x77, 0xDE, 0xA2, 0x68, 0xA6, 0x52, 0x12,
+    0xA2, 0x03, 0x9D, 0x20,
+};
+
+DSA *get_dsa512()
+{
+    DSA *dsa;
+    BIGNUM *priv_key, *pub_key, *p, *q, *g;
+
+    if ((dsa = DSA_new()) == NULL)
+        return (NULL);
+    priv_key = BN_bin2bn(dsa512_priv, sizeof(dsa512_priv), NULL);
+    pub_key = BN_bin2bn(dsa512_pub, sizeof(dsa512_pub), NULL);
+    p = BN_bin2bn(dsa512_p, sizeof(dsa512_p), NULL);
+    q = BN_bin2bn(dsa512_q, sizeof(dsa512_q), NULL);
+    g = BN_bin2bn(dsa512_g, sizeof(dsa512_g), NULL);
+    if ((priv_key == NULL) || (pub_key == NULL) || (p == NULL) || (q == NULL)
+            || (g == NULL)) {
+        goto err;
+    }
+    if (!DSA_set0_pqg(dsa, p, q, g))
+        goto err;
+    p = q = g = NULL;
+
+    if (!DSA_set0_key(dsa, pub_key, priv_key))
+        goto err;
+
+    return dsa;
+ err:
+    DSA_free(dsa);
+    BN_free(priv_key);
+    BN_free(pub_key);
+    BN_free(p);
+    BN_free(q);
+    BN_free(g);
+    return NULL;
+}
+
+static unsigned char dsa1024_priv[] = {
+    0x7d, 0x21, 0xda, 0xbb, 0x62, 0x15, 0x47, 0x36, 0x07, 0x67, 0x12, 0xe8,
+    0x8c, 0xaa, 0x1c, 0xcd, 0x38, 0x12, 0x61, 0x18,
+};
+
+static unsigned char dsa1024_pub[] = {
+    0x3c, 0x4e, 0x9c, 0x2a, 0x7f, 0x16, 0xc1, 0x25, 0xeb, 0xac, 0x78, 0x63,
+    0x90, 0x14, 0x8c, 0x8b, 0xf4, 0x68, 0x43, 0x3c, 0x2d, 0xee, 0x65, 0x50,
+    0x7d, 0x9c, 0x8f, 0x8c, 0x8a, 0x51, 0xd6, 0x11, 0x2b, 0x99, 0xaf, 0x1e,
+    0x90, 0x97, 0xb5, 0xd3, 0xa6, 0x20, 0x25, 0xd6, 0xfe, 0x43, 0x02, 0xd5,
+    0x91, 0x7d, 0xa7, 0x8c, 0xdb, 0xc9, 0x85, 0xa3, 0x36, 0x48, 0xf7, 0x68,
+    0xaa, 0x60, 0xb1, 0xf7, 0x05, 0x68, 0x3a, 0xa3, 0x3f, 0xd3, 0x19, 0x82,
+    0xd8, 0x82, 0x7a, 0x77, 0xfb, 0xef, 0xf4, 0x15, 0x0a, 0xeb, 0x06, 0x04,
+    0x7f, 0x53, 0x07, 0x0c, 0xbc, 0xcb, 0x2d, 0x83, 0xdb, 0x3e, 0xd1, 0x28,
+    0xa5, 0xa1, 0x31, 0xe0, 0x67, 0xfa, 0x50, 0xde, 0x9b, 0x07, 0x83, 0x7e,
+    0x2c, 0x0b, 0xc3, 0x13, 0x50, 0x61, 0xe5, 0xad, 0xbd, 0x36, 0xb8, 0x97,
+    0x4e, 0x40, 0x7d, 0xe8, 0x83, 0x0d, 0xbc, 0x4b
+};
+
+static unsigned char dsa1024_p[] = {
+    0xA7, 0x3F, 0x6E, 0x85, 0xBF, 0x41, 0x6A, 0x29, 0x7D, 0xF0, 0x9F, 0x47,
+    0x19, 0x30, 0x90, 0x9A, 0x09, 0x1D, 0xDA, 0x6A, 0x33, 0x1E, 0xC5, 0x3D,
+    0x86, 0x96, 0xB3, 0x15, 0xE0, 0x53, 0x2E, 0x8F, 0xE0, 0x59, 0x82, 0x73,
+    0x90, 0x3E, 0x75, 0x31, 0x99, 0x47, 0x7A, 0x52, 0xFB, 0x85, 0xE4, 0xD9,
+    0xA6, 0x7B, 0x38, 0x9B, 0x68, 0x8A, 0x84, 0x9B, 0x87, 0xC6, 0x1E, 0xB5,
+    0x7E, 0x86, 0x4B, 0x53, 0x5B, 0x59, 0xCF, 0x71, 0x65, 0x19, 0x88, 0x6E,
+    0xCE, 0x66, 0xAE, 0x6B, 0x88, 0x36, 0xFB, 0xEC, 0x28, 0xDC, 0xC2, 0xD7,
+    0xA5, 0xBB, 0xE5, 0x2C, 0x39, 0x26, 0x4B, 0xDA, 0x9A, 0x70, 0x18, 0x95,
+    0x37, 0x95, 0x10, 0x56, 0x23, 0xF6, 0x15, 0xED, 0xBA, 0x04, 0x5E, 0xDE,
+    0x39, 0x4F, 0xFD, 0xB7, 0x43, 0x1F, 0xB5, 0xA4, 0x65, 0x6F, 0xCD, 0x80,
+    0x11, 0xE4, 0x70, 0x95, 0x5B, 0x50, 0xCD, 0x49,
+};
+
+static unsigned char dsa1024_q[] = {
+    0xF7, 0x07, 0x31, 0xED, 0xFA, 0x6C, 0x06, 0x03, 0xD5, 0x85, 0x8A, 0x1C,
+    0xAC, 0x9C, 0x65, 0xE7, 0x50, 0x66, 0x65, 0x6F,
+};
+
+static unsigned char dsa1024_g[] = {
+    0x4D, 0xDF, 0x4C, 0x03, 0xA6, 0x91, 0x8A, 0xF5, 0x19, 0x6F, 0x50, 0x46,
+    0x25, 0x99, 0xE5, 0x68, 0x6F, 0x30, 0xE3, 0x69, 0xE1, 0xE5, 0xB3, 0x5D,
+    0x98, 0xBB, 0x28, 0x86, 0x48, 0xFC, 0xDE, 0x99, 0x04, 0x3F, 0x5F, 0x88,
+    0x0C, 0x9C, 0x73, 0x24, 0x0D, 0x20, 0x5D, 0xB9, 0x2A, 0x9A, 0x3F, 0x18,
+    0x96, 0x27, 0xE4, 0x62, 0x87, 0xC1, 0x7B, 0x74, 0x62, 0x53, 0xFC, 0x61,
+    0x27, 0xA8, 0x7A, 0x91, 0x09, 0x9D, 0xB6, 0xF1, 0x4D, 0x9C, 0x54, 0x0F,
+    0x58, 0x06, 0xEE, 0x49, 0x74, 0x07, 0xCE, 0x55, 0x7E, 0x23, 0xCE, 0x16,
+    0xF6, 0xCA, 0xDC, 0x5A, 0x61, 0x01, 0x7E, 0xC9, 0x71, 0xB5, 0x4D, 0xF6,
+    0xDC, 0x34, 0x29, 0x87, 0x68, 0xF6, 0x5E, 0x20, 0x93, 0xB3, 0xDB, 0xF5,
+    0xE4, 0x09, 0x6C, 0x41, 0x17, 0x95, 0x92, 0xEB, 0x01, 0xB5, 0x73, 0xA5,
+    0x6A, 0x7E, 0xD8, 0x32, 0xED, 0x0E, 0x02, 0xB8,
+};
+
+DSA *get_dsa1024()
+{
+    DSA *dsa;
+    BIGNUM *priv_key, *pub_key, *p, *q, *g;
+
+    if ((dsa = DSA_new()) == NULL)
+        return (NULL);
+    priv_key = BN_bin2bn(dsa1024_priv, sizeof(dsa1024_priv), NULL);
+    pub_key = BN_bin2bn(dsa1024_pub, sizeof(dsa1024_pub), NULL);
+    p = BN_bin2bn(dsa1024_p, sizeof(dsa1024_p), NULL);
+    q = BN_bin2bn(dsa1024_q, sizeof(dsa1024_q), NULL);
+    g = BN_bin2bn(dsa1024_g, sizeof(dsa1024_g), NULL);
+    if ((priv_key == NULL) || (pub_key == NULL) || (p == NULL) || (q == NULL)
+            || (g == NULL)) {
+        goto err;
+    }
+    if (!DSA_set0_pqg(dsa, p, q, g))
+        goto err;
+    p = q = g = NULL;
+
+    if (!DSA_set0_key(dsa, pub_key, priv_key))
+        goto err;
+
+    return dsa;
+ err:
+    DSA_free(dsa);
+    BN_free(priv_key);
+    BN_free(pub_key);
+    BN_free(p);
+    BN_free(q);
+    BN_free(g);
+    return NULL;
+}
+
+static unsigned char dsa2048_priv[] = {
+    0x32, 0x67, 0x92, 0xf6, 0xc4, 0xe2, 0xe2, 0xe8, 0xa0, 0x8b, 0x6b, 0x45,
+    0x0c, 0x8a, 0x76, 0xb0, 0xee, 0xcf, 0x91, 0xa7,
+};
+
+static unsigned char dsa2048_pub[] = {
+    0x17, 0x8f, 0xa8, 0x11, 0x84, 0x92, 0xec, 0x83, 0x47, 0xc7, 0x6a, 0xb0,
+    0x92, 0xaf, 0x5a, 0x20, 0x37, 0xa3, 0x64, 0x79, 0xd2, 0xd0, 0x3d, 0xcd,
+    0xe0, 0x61, 0x88, 0x88, 0x21, 0xcc, 0x74, 0x5d, 0xce, 0x4c, 0x51, 0x47,
+    0xf0, 0xc5, 0x5c, 0x4c, 0x82, 0x7a, 0xaf, 0x72, 0xad, 0xb9, 0xe0, 0x53,
+    0xf2, 0x78, 0xb7, 0xf0, 0xb5, 0x48, 0x7f, 0x8a, 0x3a, 0x18, 0xd1, 0x9f,
+    0x8b, 0x7d, 0xa5, 0x47, 0xb7, 0x95, 0xab, 0x98, 0xf8, 0x7b, 0x74, 0x50,
+    0x56, 0x8e, 0x57, 0xf0, 0xee, 0xf5, 0xb7, 0xba, 0xab, 0x85, 0x86, 0xf9,
+    0x2b, 0xef, 0x41, 0x56, 0xa0, 0xa4, 0x9f, 0xb7, 0x38, 0x00, 0x46, 0x0a,
+    0xa6, 0xf1, 0xfc, 0x1f, 0xd8, 0x4e, 0x85, 0x44, 0x92, 0x43, 0x21, 0x5d,
+    0x6e, 0xcc, 0xc2, 0xcb, 0x26, 0x31, 0x0d, 0x21, 0xc4, 0xbd, 0x8d, 0x24,
+    0xbc, 0xd9, 0x18, 0x19, 0xd7, 0xdc, 0xf1, 0xe7, 0x93, 0x50, 0x48, 0x03,
+    0x2c, 0xae, 0x2e, 0xe7, 0x49, 0x88, 0x5f, 0x93, 0x57, 0x27, 0x99, 0x36,
+    0xb4, 0x20, 0xab, 0xfc, 0xa7, 0x2b, 0xf2, 0xd9, 0x98, 0xd7, 0xd4, 0x34,
+    0x9d, 0x96, 0x50, 0x58, 0x9a, 0xea, 0x54, 0xf3, 0xee, 0xf5, 0x63, 0x14,
+    0xee, 0x85, 0x83, 0x74, 0x76, 0xe1, 0x52, 0x95, 0xc3, 0xf7, 0xeb, 0x04,
+    0x04, 0x7b, 0xa7, 0x28, 0x1b, 0xcc, 0xea, 0x4a, 0x4e, 0x84, 0xda, 0xd8,
+    0x9c, 0x79, 0xd8, 0x9b, 0x66, 0x89, 0x2f, 0xcf, 0xac, 0xd7, 0x79, 0xf9,
+    0xa9, 0xd8, 0x45, 0x13, 0x78, 0xb9, 0x00, 0x14, 0xc9, 0x7e, 0x22, 0x51,
+    0x86, 0x67, 0xb0, 0x9f, 0x26, 0x11, 0x23, 0xc8, 0x38, 0xd7, 0x70, 0x1d,
+    0x15, 0x8e, 0x4d, 0x4f, 0x95, 0x97, 0x40, 0xa1, 0xc2, 0x7e, 0x01, 0x18,
+    0x72, 0xf4, 0x10, 0xe6, 0x8d, 0x52, 0x16, 0x7f, 0xf2, 0xc9, 0xf8, 0x33,
+    0x8b, 0x33, 0xb7, 0xce,
+};
+
+static unsigned char dsa2048_p[] = {
+    0xA0, 0x25, 0xFA, 0xAD, 0xF4, 0x8E, 0xB9, 0xE5, 0x99, 0xF3, 0x5D, 0x6F,
+    0x4F, 0x83, 0x34, 0xE2, 0x7E, 0xCF, 0x6F, 0xBF, 0x30, 0xAF, 0x6F, 0x81,
+    0xEB, 0xF8, 0xC4, 0x13, 0xD9, 0xA0, 0x5D, 0x8B, 0x5C, 0x8E, 0xDC, 0xC2,
+    0x1D, 0x0B, 0x41, 0x32, 0xB0, 0x1F, 0xFE, 0xEF, 0x0C, 0xC2, 0xA2, 0x7E,
+    0x68, 0x5C, 0x28, 0x21, 0xE9, 0xF5, 0xB1, 0x58, 0x12, 0x63, 0x4C, 0x19,
+    0x4E, 0xFF, 0x02, 0x4B, 0x92, 0xED, 0xD2, 0x07, 0x11, 0x4D, 0x8C, 0x58,
+    0x16, 0x5C, 0x55, 0x8E, 0xAD, 0xA3, 0x67, 0x7D, 0xB9, 0x86, 0x6E, 0x0B,
+    0xE6, 0x54, 0x6F, 0x40, 0xAE, 0x0E, 0x67, 0x4C, 0xF9, 0x12, 0x5B, 0x3C,
+    0x08, 0x7A, 0xF7, 0xFC, 0x67, 0x86, 0x69, 0xE7, 0x0A, 0x94, 0x40, 0xBF,
+    0x8B, 0x76, 0xFE, 0x26, 0xD1, 0xF2, 0xA1, 0x1A, 0x84, 0xA1, 0x43, 0x56,
+    0x28, 0xBC, 0x9A, 0x5F, 0xD7, 0x3B, 0x69, 0x89, 0x8A, 0x36, 0x2C, 0x51,
+    0xDF, 0x12, 0x77, 0x2F, 0x57, 0x7B, 0xA0, 0xAA, 0xDD, 0x7F, 0xA1, 0x62,
+    0x3B, 0x40, 0x7B, 0x68, 0x1A, 0x8F, 0x0D, 0x38, 0xBB, 0x21, 0x5D, 0x18,
+    0xFC, 0x0F, 0x46, 0xF7, 0xA3, 0xB0, 0x1D, 0x23, 0xC3, 0xD2, 0xC7, 0x72,
+    0x51, 0x18, 0xDF, 0x46, 0x95, 0x79, 0xD9, 0xBD, 0xB5, 0x19, 0x02, 0x2C,
+    0x87, 0xDC, 0xE7, 0x57, 0x82, 0x7E, 0xF1, 0x8B, 0x06, 0x3D, 0x00, 0xA5,
+    0x7B, 0x6B, 0x26, 0x27, 0x91, 0x0F, 0x6A, 0x77, 0xE4, 0xD5, 0x04, 0xE4,
+    0x12, 0x2C, 0x42, 0xFF, 0xD2, 0x88, 0xBB, 0xD3, 0x92, 0xA0, 0xF9, 0xC8,
+    0x51, 0x64, 0x14, 0x5C, 0xD8, 0xF9, 0x6C, 0x47, 0x82, 0xB4, 0x1C, 0x7F,
+    0x09, 0xB8, 0xF0, 0x25, 0x83, 0x1D, 0x3F, 0x3F, 0x05, 0xB3, 0x21, 0x0A,
+    0x5D, 0xA7, 0xD8, 0x54, 0xC3, 0x65, 0x7D, 0xC3, 0xB0, 0x1D, 0xBF, 0xAE,
+    0xF8, 0x68, 0xCF, 0x9B,
+};
+
+static unsigned char dsa2048_q[] = {
+    0x97, 0xE7, 0x33, 0x4D, 0xD3, 0x94, 0x3E, 0x0B, 0xDB, 0x62, 0x74, 0xC6,
+    0xA1, 0x08, 0xDD, 0x19, 0xA3, 0x75, 0x17, 0x1B,
+};
+
+static unsigned char dsa2048_g[] = {
+    0x2C, 0x78, 0x16, 0x59, 0x34, 0x63, 0xF4, 0xF3, 0x92, 0xFC, 0xB5, 0xA5,
+    0x4F, 0x13, 0xDE, 0x2F, 0x1C, 0xA4, 0x3C, 0xAE, 0xAD, 0x38, 0x3F, 0x7E,
+    0x90, 0xBF, 0x96, 0xA6, 0xAE, 0x25, 0x90, 0x72, 0xF5, 0x8E, 0x80, 0x0C,
+    0x39, 0x1C, 0xD9, 0xEC, 0xBA, 0x90, 0x5B, 0x3A, 0xE8, 0x58, 0x6C, 0x9E,
+    0x30, 0x42, 0x37, 0x02, 0x31, 0x82, 0xBC, 0x6A, 0xDF, 0x6A, 0x09, 0x29,
+    0xE3, 0xC0, 0x46, 0xD1, 0xCB, 0x85, 0xEC, 0x0C, 0x30, 0x5E, 0xEA, 0xC8,
+    0x39, 0x8E, 0x22, 0x9F, 0x22, 0x10, 0xD2, 0x34, 0x61, 0x68, 0x37, 0x3D,
+    0x2E, 0x4A, 0x5B, 0x9A, 0xF5, 0xC1, 0x48, 0xC6, 0xF6, 0xDC, 0x63, 0x1A,
+    0xD3, 0x96, 0x64, 0xBA, 0x34, 0xC9, 0xD1, 0xA0, 0xD1, 0xAE, 0x6C, 0x2F,
+    0x48, 0x17, 0x93, 0x14, 0x43, 0xED, 0xF0, 0x21, 0x30, 0x19, 0xC3, 0x1B,
+    0x5F, 0xDE, 0xA3, 0xF0, 0x70, 0x78, 0x18, 0xE1, 0xA8, 0xE4, 0xEE, 0x2E,
+    0x00, 0xA5, 0xE4, 0xB3, 0x17, 0xC8, 0x0C, 0x7D, 0x6E, 0x42, 0xDC, 0xB7,
+    0x46, 0x00, 0x36, 0x4D, 0xD4, 0x46, 0xAA, 0x3D, 0x3C, 0x46, 0x89, 0x40,
+    0xBF, 0x1D, 0x84, 0x77, 0x0A, 0x75, 0xF3, 0x87, 0x1D, 0x08, 0x4C, 0xA6,
+    0xD1, 0xA9, 0x1C, 0x1E, 0x12, 0x1E, 0xE1, 0xC7, 0x30, 0x28, 0x76, 0xA5,
+    0x7F, 0x6C, 0x85, 0x96, 0x2B, 0x6F, 0xDB, 0x80, 0x66, 0x26, 0xAE, 0xF5,
+    0x93, 0xC7, 0x8E, 0xAE, 0x9A, 0xED, 0xE4, 0xCA, 0x04, 0xEA, 0x3B, 0x72,
+    0xEF, 0xDC, 0x87, 0xED, 0x0D, 0xA5, 0x4C, 0x4A, 0xDD, 0x71, 0x22, 0x64,
+    0x59, 0x69, 0x4E, 0x8E, 0xBF, 0x43, 0xDC, 0xAB, 0x8E, 0x66, 0xBB, 0x01,
+    0xB6, 0xF4, 0xE7, 0xFD, 0xD2, 0xAD, 0x9F, 0x36, 0xC1, 0xA0, 0x29, 0x99,
+    0xD1, 0x96, 0x70, 0x59, 0x06, 0x78, 0x35, 0xBD, 0x65, 0x55, 0x52, 0x9E,
+    0xF8, 0xB2, 0xE5, 0x38,
+};
+
+DSA *get_dsa2048()
+{
+    DSA *dsa;
+    BIGNUM *priv_key, *pub_key, *p, *q, *g;
+
+    if ((dsa = DSA_new()) == NULL)
+        return (NULL);
+    priv_key = BN_bin2bn(dsa2048_priv, sizeof(dsa2048_priv), NULL);
+    pub_key = BN_bin2bn(dsa2048_pub, sizeof(dsa2048_pub), NULL);
+    p = BN_bin2bn(dsa2048_p, sizeof(dsa2048_p), NULL);
+    q = BN_bin2bn(dsa2048_q, sizeof(dsa2048_q), NULL);
+    g = BN_bin2bn(dsa2048_g, sizeof(dsa2048_g), NULL);
+    if ((priv_key == NULL) || (pub_key == NULL) || (p == NULL) || (q == NULL)
+            || (g == NULL)) {
+        goto err;
+    }
+    if (!DSA_set0_pqg(dsa, p, q, g))
+        goto err;
+    p = q = g = NULL;
+
+    if (!DSA_set0_key(dsa, pub_key, priv_key))
+        goto err;
+
+    return dsa;
+ err:
+    DSA_free(dsa);
+    BN_free(priv_key);
+    BN_free(pub_key);
+    BN_free(p);
+    BN_free(q);
+    BN_free(g);
+    return NULL;
+}
+
diff --git a/apps/testrsa.h b/apps/testrsa.h
new file mode 100644
index 000000000..1350ce54e
--- /dev/null
+++ b/apps/testrsa.h
@@ -0,0 +1,1960 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+static unsigned char test512[] = {
+    0x30, 0x82, 0x01, 0x3a, 0x02, 0x01, 0x00, 0x02, 0x41, 0x00,
+    0xd6, 0x33, 0xb9, 0xc8, 0xfb, 0x4f, 0x3c, 0x7d, 0xc0, 0x01,
+    0x86, 0xd0, 0xe7, 0xa0, 0x55, 0xf2, 0x95, 0x93, 0xcc, 0x4f,
+    0xb7, 0x5b, 0x67, 0x5b, 0x94, 0x68, 0xc9, 0x34, 0x15, 0xde,
+    0xa5, 0x2e, 0x1c, 0x33, 0xc2, 0x6e, 0xfc, 0x34, 0x5e, 0x71,
+    0x13, 0xb7, 0xd6, 0xee, 0xd8, 0xa5, 0x65, 0x05, 0x72, 0x87,
+    0xa8, 0xb0, 0x77, 0xfe, 0x57, 0xf5, 0xfc, 0x5f, 0x55, 0x83,
+    0x87, 0xdd, 0x57, 0x49, 0x02, 0x03, 0x01, 0x00, 0x01, 0x02,
+    0x41, 0x00, 0xa7, 0xf7, 0x91, 0xc5, 0x0f, 0x84, 0x57, 0xdc,
+    0x07, 0xf7, 0x6a, 0x7f, 0x60, 0x52, 0xb3, 0x72, 0xf1, 0x66,
+    0x1f, 0x7d, 0x97, 0x3b, 0x9e, 0xb6, 0x0a, 0x8f, 0x8c, 0xcf,
+    0x42, 0x23, 0x00, 0x04, 0xd4, 0x28, 0x0e, 0x1c, 0x90, 0xc4,
+    0x11, 0x25, 0x25, 0xa5, 0x93, 0xa5, 0x2f, 0x70, 0x02, 0xdf,
+    0x81, 0x9c, 0x49, 0x03, 0xa0, 0xf8, 0x6d, 0x54, 0x2e, 0x26,
+    0xde, 0xaa, 0x85, 0x59, 0xa8, 0x31, 0x02, 0x21, 0x00, 0xeb,
+    0x47, 0xd7, 0x3b, 0xf6, 0xc3, 0xdd, 0x5a, 0x46, 0xc5, 0xb9,
+    0x2b, 0x9a, 0xa0, 0x09, 0x8f, 0xa6, 0xfb, 0xf3, 0x78, 0x7a,
+    0x33, 0x70, 0x9d, 0x0f, 0x42, 0x6b, 0x13, 0x68, 0x24, 0xd3,
+    0x15, 0x02, 0x21, 0x00, 0xe9, 0x10, 0xb0, 0xb3, 0x0d, 0xe2,
+    0x82, 0x68, 0x77, 0x8a, 0x6e, 0x7c, 0xda, 0xbc, 0x3e, 0x53,
+    0x83, 0xfb, 0xd6, 0x22, 0xe7, 0xb5, 0xae, 0x6e, 0x80, 0xda,
+    0x00, 0x55, 0x97, 0xc1, 0xd0, 0x65, 0x02, 0x20, 0x4c, 0xf8,
+    0x73, 0xb1, 0x6a, 0x49, 0x29, 0x61, 0x1f, 0x46, 0x10, 0x0d,
+    0xf3, 0xc7, 0xe7, 0x58, 0xd7, 0x88, 0x15, 0x5e, 0x94, 0x9b,
+    0xbf, 0x7b, 0xa2, 0x42, 0x58, 0x45, 0x41, 0x0c, 0xcb, 0x01,
+    0x02, 0x20, 0x12, 0x11, 0xba, 0x31, 0x57, 0x9d, 0x3d, 0x11,
+    0x0e, 0x5b, 0x8c, 0x2f, 0x5f, 0xe2, 0x02, 0x4f, 0x05, 0x47,
+    0x8c, 0x15, 0x8e, 0xb3, 0x56, 0x3f, 0xb8, 0xfb, 0xad, 0xd4,
+    0xf4, 0xfc, 0x10, 0xc5, 0x02, 0x20, 0x18, 0xa1, 0x29, 0x99,
+    0x5b, 0xd9, 0xc8, 0xd4, 0xfc, 0x49, 0x7a, 0x2a, 0x21, 0x2c,
+    0x49, 0xe4, 0x4f, 0xeb, 0xef, 0x51, 0xf1, 0xab, 0x6d, 0xfb,
+    0x4b, 0x14, 0xe9, 0x4b, 0x52, 0xb5, 0x82, 0x2c,
+};
+
+static unsigned char test1024[] = {
+    0x30, 0x82, 0x02, 0x5c, 0x02, 0x01, 0x00, 0x02, 0x81, 0x81,
+    0x00, 0xdc, 0x98, 0x43, 0xe8, 0x3d, 0x43, 0x5b, 0xe4, 0x05,
+    0xcd, 0xd0, 0xa9, 0x3e, 0xcb, 0x83, 0x75, 0xf6, 0xb5, 0xa5,
+    0x9f, 0x6b, 0xe9, 0x34, 0x41, 0x29, 0x18, 0xfa, 0x6a, 0x55,
+    0x4d, 0x70, 0xfc, 0xec, 0xae, 0x87, 0x38, 0x0a, 0x20, 0xa9,
+    0xc0, 0x45, 0x77, 0x6e, 0x57, 0x60, 0x57, 0xf4, 0xed, 0x96,
+    0x22, 0xcb, 0x8f, 0xe1, 0x33, 0x3a, 0x17, 0x1f, 0xed, 0x37,
+    0xa5, 0x6f, 0xeb, 0xa6, 0xbc, 0x12, 0x80, 0x1d, 0x53, 0xbd,
+    0x70, 0xeb, 0x21, 0x76, 0x3e, 0xc9, 0x2f, 0x1a, 0x45, 0x24,
+    0x82, 0xff, 0xcd, 0x59, 0x32, 0x06, 0x2e, 0x12, 0x3b, 0x23,
+    0x78, 0xed, 0x12, 0x3d, 0xe0, 0x8d, 0xf9, 0x67, 0x4f, 0x37,
+    0x4e, 0x47, 0x02, 0x4c, 0x2d, 0xc0, 0x4f, 0x1f, 0xb3, 0x94,
+    0xe1, 0x41, 0x2e, 0x2d, 0x90, 0x10, 0xfc, 0x82, 0x91, 0x8b,
+    0x0f, 0x22, 0xd4, 0xf2, 0xfc, 0x2c, 0xab, 0x53, 0x55, 0x02,
+    0x03, 0x01, 0x00, 0x01, 0x02, 0x81, 0x80, 0x2b, 0xcc, 0x3f,
+    0x8f, 0x58, 0xba, 0x8b, 0x00, 0x16, 0xf6, 0xea, 0x3a, 0xf0,
+    0x30, 0xd0, 0x05, 0x17, 0xda, 0xb0, 0xeb, 0x9a, 0x2d, 0x4f,
+    0x26, 0xb0, 0xd6, 0x38, 0xc1, 0xeb, 0xf5, 0xd8, 0x3d, 0x1f,
+    0x70, 0xf7, 0x7f, 0xf4, 0xe2, 0xcf, 0x51, 0x51, 0x79, 0x88,
+    0xfa, 0xe8, 0x32, 0x0e, 0x7b, 0x2d, 0x97, 0xf2, 0xfa, 0xba,
+    0x27, 0xc5, 0x9c, 0xd9, 0xc5, 0xeb, 0x8a, 0x79, 0x52, 0x3c,
+    0x64, 0x34, 0x7d, 0xc2, 0xcf, 0x28, 0xc7, 0x4e, 0xd5, 0x43,
+    0x0b, 0xd1, 0xa6, 0xca, 0x6d, 0x03, 0x2d, 0x72, 0x23, 0xbc,
+    0x6d, 0x05, 0xfa, 0x16, 0x09, 0x2f, 0x2e, 0x5c, 0xb6, 0xee,
+    0x74, 0xdd, 0xd2, 0x48, 0x8e, 0x36, 0x0c, 0x06, 0x3d, 0x4d,
+    0xe5, 0x10, 0x82, 0xeb, 0x6a, 0xf3, 0x4b, 0x9f, 0xd6, 0xed,
+    0x11, 0xb1, 0x6e, 0xec, 0xf4, 0xfe, 0x8e, 0x75, 0x94, 0x20,
+    0x2f, 0xcb, 0xac, 0x46, 0xf1, 0x02, 0x41, 0x00, 0xf9, 0x8c,
+    0xa3, 0x85, 0xb1, 0xdd, 0x29, 0xaf, 0x65, 0xc1, 0x33, 0xf3,
+    0x95, 0xc5, 0x52, 0x68, 0x0b, 0xd4, 0xf1, 0xe5, 0x0e, 0x02,
+    0x9f, 0x4f, 0xfa, 0x77, 0xdc, 0x46, 0x9e, 0xc7, 0xa6, 0xe4,
+    0x16, 0x29, 0xda, 0xb0, 0x07, 0xcf, 0x5b, 0xa9, 0x12, 0x8a,
+    0xdd, 0x63, 0x0a, 0xde, 0x2e, 0x8c, 0x66, 0x8b, 0x8c, 0xdc,
+    0x19, 0xa3, 0x7e, 0xf4, 0x3b, 0xd0, 0x1a, 0x8c, 0xa4, 0xc2,
+    0xe1, 0xd3, 0x02, 0x41, 0x00, 0xe2, 0x4c, 0x05, 0xf2, 0x04,
+    0x86, 0x4e, 0x61, 0x43, 0xdb, 0xb0, 0xb9, 0x96, 0x86, 0x52,
+    0x2c, 0xca, 0x8d, 0x7b, 0xab, 0x0b, 0x13, 0x0d, 0x7e, 0x38,
+    0x5b, 0xe2, 0x2e, 0x7b, 0x0e, 0xe7, 0x19, 0x99, 0x38, 0xe7,
+    0xf2, 0x21, 0xbd, 0x85, 0x85, 0xe3, 0xfd, 0x28, 0x77, 0x20,
+    0x31, 0x71, 0x2c, 0xd0, 0xff, 0xfb, 0x2e, 0xaf, 0x85, 0xb4,
+    0x86, 0xca, 0xf3, 0xbb, 0xca, 0xaa, 0x0f, 0x95, 0x37, 0x02,
+    0x40, 0x0e, 0x41, 0x9a, 0x95, 0xe8, 0xb3, 0x59, 0xce, 0x4b,
+    0x61, 0xde, 0x35, 0xec, 0x38, 0x79, 0x9c, 0xb8, 0x10, 0x52,
+    0x41, 0x63, 0xab, 0x82, 0xae, 0x6f, 0x00, 0xa9, 0xf4, 0xde,
+    0xdd, 0x49, 0x0b, 0x7e, 0xb8, 0xa5, 0x65, 0xa9, 0x0c, 0x8f,
+    0x8f, 0xf9, 0x1f, 0x35, 0xc6, 0x92, 0xb8, 0x5e, 0xb0, 0x66,
+    0xab, 0x52, 0x40, 0xc0, 0xb6, 0x36, 0x6a, 0x7d, 0x80, 0x46,
+    0x04, 0x02, 0xe5, 0x9f, 0x41, 0x02, 0x41, 0x00, 0xc0, 0xad,
+    0xcc, 0x4e, 0x21, 0xee, 0x1d, 0x24, 0x91, 0xfb, 0xa7, 0x80,
+    0x8d, 0x9a, 0xb6, 0xb3, 0x2e, 0x8f, 0xc2, 0xe1, 0x82, 0xdf,
+    0x69, 0x18, 0xb4, 0x71, 0xff, 0xa6, 0x65, 0xde, 0xed, 0x84,
+    0x8d, 0x42, 0xb7, 0xb3, 0x21, 0x69, 0x56, 0x1c, 0x07, 0x60,
+    0x51, 0x29, 0x04, 0xff, 0x34, 0x06, 0xdd, 0xb9, 0x67, 0x2c,
+    0x7c, 0x04, 0x93, 0x0e, 0x46, 0x15, 0xbb, 0x2a, 0xb7, 0x1b,
+    0xe7, 0x87, 0x02, 0x40, 0x78, 0xda, 0x5d, 0x07, 0x51, 0x0c,
+    0x16, 0x7a, 0x9f, 0x29, 0x20, 0x84, 0x0d, 0x42, 0xfa, 0xd7,
+    0x00, 0xd8, 0x77, 0x7e, 0xb0, 0xb0, 0x6b, 0xd6, 0x5b, 0x53,
+    0xb8, 0x9b, 0x7a, 0xcd, 0xc7, 0x2b, 0xb8, 0x6a, 0x63, 0xa9,
+    0xfb, 0x6f, 0xa4, 0x72, 0xbf, 0x4c, 0x5d, 0x00, 0x14, 0xba,
+    0xfa, 0x59, 0x88, 0xed, 0xe4, 0xe0, 0x8c, 0xa2, 0xec, 0x14,
+    0x7e, 0x2d, 0xe2, 0xf0, 0x46, 0x49, 0x95, 0x45,
+};
+
+static unsigned char test2048[] = {
+    0x30, 0x82, 0x04, 0xa3, 0x02, 0x01, 0x00, 0x02, 0x82, 0x01,
+    0x01, 0x00, 0xc0, 0xc0, 0xce, 0x3e, 0x3c, 0x53, 0x67, 0x3f,
+    0x4f, 0xc5, 0x2f, 0xa4, 0xc2, 0x5a, 0x2f, 0x58, 0xfd, 0x27,
+    0x52, 0x6a, 0xe8, 0xcf, 0x4a, 0x73, 0x47, 0x8d, 0x25, 0x0f,
+    0x5f, 0x03, 0x26, 0x78, 0xef, 0xf0, 0x22, 0x12, 0xd3, 0xde,
+    0x47, 0xb2, 0x1c, 0x0b, 0x38, 0x63, 0x1a, 0x6c, 0x85, 0x7a,
+    0x80, 0xc6, 0x8f, 0xa0, 0x41, 0xaf, 0x62, 0xc4, 0x67, 0x32,
+    0x88, 0xf8, 0xa6, 0x9c, 0xf5, 0x23, 0x1d, 0xe4, 0xac, 0x3f,
+    0x29, 0xf9, 0xec, 0xe1, 0x8b, 0x26, 0x03, 0x2c, 0xb2, 0xab,
+    0xf3, 0x7d, 0xb5, 0xca, 0x49, 0xc0, 0x8f, 0x1c, 0xdf, 0x33,
+    0x3a, 0x60, 0xda, 0x3c, 0xb0, 0x16, 0xf8, 0xa9, 0x12, 0x8f,
+    0x64, 0xac, 0x23, 0x0c, 0x69, 0x64, 0x97, 0x5d, 0x99, 0xd4,
+    0x09, 0x83, 0x9b, 0x61, 0xd3, 0xac, 0xf0, 0xde, 0xdd, 0x5e,
+    0x9f, 0x44, 0x94, 0xdb, 0x3a, 0x4d, 0x97, 0xe8, 0x52, 0x29,
+    0xf7, 0xdb, 0x94, 0x07, 0x45, 0x90, 0x78, 0x1e, 0x31, 0x0b,
+    0x80, 0xf7, 0x57, 0xad, 0x1c, 0x79, 0xc5, 0xcb, 0x32, 0xb0,
+    0xce, 0xcd, 0x74, 0xb3, 0xe2, 0x94, 0xc5, 0x78, 0x2f, 0x34,
+    0x1a, 0x45, 0xf7, 0x8c, 0x52, 0xa5, 0xbc, 0x8d, 0xec, 0xd1,
+    0x2f, 0x31, 0x3b, 0xf0, 0x49, 0x59, 0x5e, 0x88, 0x9d, 0x15,
+    0x92, 0x35, 0x32, 0xc1, 0xe7, 0x61, 0xec, 0x50, 0x48, 0x7c,
+    0xba, 0x05, 0xf9, 0xf8, 0xf8, 0xa7, 0x8c, 0x83, 0xe8, 0x66,
+    0x5b, 0xeb, 0xfe, 0xd8, 0x4f, 0xdd, 0x6d, 0x36, 0xc0, 0xb2,
+    0x90, 0x0f, 0xb8, 0x52, 0xf9, 0x04, 0x9b, 0x40, 0x2c, 0x27,
+    0xd6, 0x36, 0x8e, 0xc2, 0x1b, 0x44, 0xf3, 0x92, 0xd5, 0x15,
+    0x9e, 0x9a, 0xbc, 0xf3, 0x7d, 0x03, 0xd7, 0x02, 0x14, 0x20,
+    0xe9, 0x10, 0x92, 0xfd, 0xf9, 0xfc, 0x8f, 0xe5, 0x18, 0xe1,
+    0x95, 0xcc, 0x9e, 0x60, 0xa6, 0xfa, 0x38, 0x4d, 0x02, 0x03,
+    0x01, 0x00, 0x01, 0x02, 0x82, 0x01, 0x00, 0x00, 0xc3, 0xc3,
+    0x0d, 0xb4, 0x27, 0x90, 0x8d, 0x4b, 0xbf, 0xb8, 0x84, 0xaa,
+    0xd0, 0xb8, 0xc7, 0x5d, 0x99, 0xbe, 0x55, 0xf6, 0x3e, 0x7c,
+    0x49, 0x20, 0xcb, 0x8a, 0x8e, 0x19, 0x0e, 0x66, 0x24, 0xac,
+    0xaf, 0x03, 0x33, 0x97, 0xeb, 0x95, 0xd5, 0x3b, 0x0f, 0x40,
+    0x56, 0x04, 0x50, 0xd1, 0xe6, 0xbe, 0x84, 0x0b, 0x25, 0xd3,
+    0x9c, 0xe2, 0x83, 0x6c, 0xf5, 0x62, 0x5d, 0xba, 0x2b, 0x7d,
+    0x3d, 0x7a, 0x6c, 0xe1, 0xd2, 0x0e, 0x54, 0x93, 0x80, 0x01,
+    0x91, 0x51, 0x09, 0xe8, 0x5b, 0x8e, 0x47, 0xbd, 0x64, 0xe4,
+    0x0e, 0x03, 0x83, 0x55, 0xcf, 0x5a, 0x37, 0xf0, 0x25, 0xb5,
+    0x7d, 0x21, 0xd7, 0x69, 0xdf, 0x6f, 0xc2, 0xcf, 0x10, 0xc9,
+    0x8a, 0x40, 0x9f, 0x7a, 0x70, 0xc0, 0xe8, 0xe8, 0xc0, 0xe6,
+    0x9a, 0x15, 0x0a, 0x8d, 0x4e, 0x46, 0xcb, 0x7a, 0xdb, 0xb3,
+    0xcb, 0x83, 0x02, 0xc4, 0xf0, 0xab, 0xeb, 0x02, 0x01, 0x0e,
+    0x23, 0xfc, 0x1d, 0xc4, 0xbd, 0xd4, 0xaa, 0x5d, 0x31, 0x46,
+    0x99, 0xce, 0x9e, 0xf8, 0x04, 0x75, 0x10, 0x67, 0xc4, 0x53,
+    0x47, 0x44, 0xfa, 0xc2, 0x25, 0x73, 0x7e, 0xd0, 0x8e, 0x59,
+    0xd1, 0xb2, 0x5a, 0xf4, 0xc7, 0x18, 0x92, 0x2f, 0x39, 0xab,
+    0xcd, 0xa3, 0xb5, 0xc2, 0xb9, 0xc7, 0xb9, 0x1b, 0x9f, 0x48,
+    0xfa, 0x13, 0xc6, 0x98, 0x4d, 0xca, 0x84, 0x9c, 0x06, 0xca,
+    0xe7, 0x89, 0x01, 0x04, 0xc4, 0x6c, 0xfd, 0x29, 0x59, 0x35,
+    0xe7, 0xf3, 0xdd, 0xce, 0x64, 0x59, 0xbf, 0x21, 0x13, 0xa9,
+    0x9f, 0x0e, 0xc5, 0xff, 0xbd, 0x33, 0x00, 0xec, 0xac, 0x6b,
+    0x11, 0xef, 0x51, 0x5e, 0xad, 0x07, 0x15, 0xde, 0xb8, 0x5f,
+    0xc6, 0xb9, 0xa3, 0x22, 0x65, 0x46, 0x83, 0x14, 0xdf, 0xd0,
+    0xf1, 0x44, 0x8a, 0xe1, 0x9c, 0x23, 0x33, 0xb4, 0x97, 0x33,
+    0xe6, 0x6b, 0x81, 0x02, 0x81, 0x81, 0x00, 0xec, 0x12, 0xa7,
+    0x59, 0x74, 0x6a, 0xde, 0x3e, 0xad, 0xd8, 0x36, 0x80, 0x50,
+    0xa2, 0xd5, 0x21, 0x81, 0x07, 0xf1, 0xd0, 0x91, 0xf2, 0x6c,
+    0x12, 0x2f, 0x9d, 0x1a, 0x26, 0xf8, 0x30, 0x65, 0xdf, 0xe8,
+    0xc0, 0x9b, 0x6a, 0x30, 0x98, 0x82, 0x87, 0xec, 0xa2, 0x56,
+    0x87, 0x62, 0x6f, 0xe7, 0x9f, 0xf6, 0x56, 0xe6, 0x71, 0x8f,
+    0x49, 0x86, 0x93, 0x5a, 0x4d, 0x34, 0x58, 0xfe, 0xd9, 0x04,
+    0x13, 0xaf, 0x79, 0xb7, 0xad, 0x11, 0xd1, 0x30, 0x9a, 0x14,
+    0x06, 0xa0, 0xfa, 0xb7, 0x55, 0xdc, 0x6c, 0x5a, 0x4c, 0x2c,
+    0x59, 0x56, 0xf6, 0xe8, 0x9d, 0xaf, 0x0a, 0x78, 0x99, 0x06,
+    0x06, 0x9e, 0xe7, 0x9c, 0x51, 0x55, 0x43, 0xfc, 0x3b, 0x6c,
+    0x0b, 0xbf, 0x2d, 0x41, 0xa7, 0xaf, 0xb7, 0xe0, 0xe8, 0x28,
+    0x18, 0xb4, 0x13, 0xd1, 0xe6, 0x97, 0xd0, 0x9f, 0x6a, 0x80,
+    0xca, 0xdd, 0x1a, 0x7e, 0x15, 0x02, 0x81, 0x81, 0x00, 0xd1,
+    0x06, 0x0c, 0x1f, 0xe3, 0xd0, 0xab, 0xd6, 0xca, 0x7c, 0xbc,
+    0x7d, 0x13, 0x35, 0xce, 0x27, 0xcd, 0xd8, 0x49, 0x51, 0x63,
+    0x64, 0x0f, 0xca, 0x06, 0x12, 0xfc, 0x07, 0x3e, 0xaf, 0x61,
+    0x6d, 0xe2, 0x53, 0x39, 0x27, 0xae, 0xc3, 0x11, 0x9e, 0x94,
+    0x01, 0x4f, 0xe3, 0xf3, 0x67, 0xf9, 0x77, 0xf9, 0xe7, 0x95,
+    0x3a, 0x6f, 0xe2, 0x20, 0x73, 0x3e, 0xa4, 0x7a, 0x28, 0xd4,
+    0x61, 0x97, 0xf6, 0x17, 0xa0, 0x23, 0x10, 0x2b, 0xce, 0x84,
+    0x57, 0x7e, 0x25, 0x1f, 0xf4, 0xa8, 0x54, 0xd2, 0x65, 0x94,
+    0xcc, 0x95, 0x0a, 0xab, 0x30, 0xc1, 0x59, 0x1f, 0x61, 0x8e,
+    0xb9, 0x6b, 0xd7, 0x4e, 0xb9, 0x83, 0x43, 0x79, 0x85, 0x11,
+    0xbc, 0x0f, 0xae, 0x25, 0x20, 0x05, 0xbc, 0xd2, 0x48, 0xa1,
+    0x68, 0x09, 0x84, 0xf6, 0x12, 0x9a, 0x66, 0xb9, 0x2b, 0xbb,
+    0x76, 0x03, 0x17, 0x46, 0x4e, 0x97, 0x59, 0x02, 0x81, 0x80,
+    0x09, 0x4c, 0xfa, 0xd6, 0xe5, 0x65, 0x48, 0x78, 0x43, 0xb5,
+    0x1f, 0x00, 0x93, 0x2c, 0xb7, 0x24, 0xe8, 0xc6, 0x7d, 0x5a,
+    0x70, 0x45, 0x92, 0xc8, 0x6c, 0xa3, 0xcd, 0xe1, 0xf7, 0x29,
+    0x40, 0xfa, 0x3f, 0x5b, 0x47, 0x44, 0x39, 0xc1, 0xe8, 0x72,
+    0x9e, 0x7a, 0x0e, 0xda, 0xaa, 0xa0, 0x2a, 0x09, 0xfd, 0x54,
+    0x93, 0x23, 0xaa, 0x37, 0x85, 0x5b, 0xcc, 0xd4, 0xf9, 0xd8,
+    0xff, 0xc1, 0x61, 0x0d, 0xbd, 0x7e, 0x18, 0x24, 0x73, 0x6d,
+    0x40, 0x72, 0xf1, 0x93, 0x09, 0x48, 0x97, 0x6c, 0x84, 0x90,
+    0xa8, 0x46, 0x14, 0x01, 0x39, 0x11, 0xe5, 0x3c, 0x41, 0x27,
+    0x32, 0x75, 0x24, 0xed, 0xa1, 0xd9, 0x12, 0x29, 0x8a, 0x28,
+    0x71, 0x89, 0x8d, 0xca, 0x30, 0xb0, 0x01, 0xc4, 0x2f, 0x82,
+    0x19, 0x14, 0x4c, 0x70, 0x1c, 0xb8, 0x23, 0x2e, 0xe8, 0x90,
+    0x49, 0x97, 0x92, 0x97, 0x6b, 0x7a, 0x9d, 0xb9, 0x02, 0x81,
+    0x80, 0x0f, 0x0e, 0xa1, 0x76, 0xf6, 0xa1, 0x44, 0x8f, 0xaf,
+    0x7c, 0x76, 0xd3, 0x87, 0xbb, 0xbb, 0x83, 0x10, 0x88, 0x01,
+    0x18, 0x14, 0xd1, 0xd3, 0x75, 0x59, 0x24, 0xaa, 0xf5, 0x16,
+    0xa5, 0xe9, 0x9d, 0xd1, 0xcc, 0xee, 0xf4, 0x15, 0xd9, 0xc5,
+    0x7e, 0x27, 0xe9, 0x44, 0x49, 0x06, 0x72, 0xb9, 0xfc, 0xd3,
+    0x8a, 0xc4, 0x2c, 0x36, 0x7d, 0x12, 0x9b, 0x5a, 0xaa, 0xdc,
+    0x85, 0xee, 0x6e, 0xad, 0x54, 0xb3, 0xf4, 0xfc, 0x31, 0xa1,
+    0x06, 0x3a, 0x70, 0x57, 0x0c, 0xf3, 0x95, 0x5b, 0x3e, 0xe8,
+    0xfd, 0x1a, 0x4f, 0xf6, 0x78, 0x93, 0x46, 0x6a, 0xd7, 0x31,
+    0xb4, 0x84, 0x64, 0x85, 0x09, 0x38, 0x89, 0x92, 0x94, 0x1c,
+    0xbf, 0xe2, 0x3c, 0x2a, 0xe0, 0xff, 0x99, 0xa3, 0xf0, 0x2b,
+    0x31, 0xc2, 0x36, 0xcd, 0x60, 0xbf, 0x9d, 0x2d, 0x74, 0x32,
+    0xe8, 0x9c, 0x93, 0x6e, 0xbb, 0x91, 0x7b, 0xfd, 0xd9, 0x02,
+    0x81, 0x81, 0x00, 0xa2, 0x71, 0x25, 0x38, 0xeb, 0x2a, 0xe9,
+    0x37, 0xcd, 0xfe, 0x44, 0xce, 0x90, 0x3f, 0x52, 0x87, 0x84,
+    0x52, 0x1b, 0xae, 0x8d, 0x22, 0x94, 0xce, 0x38, 0xe6, 0x04,
+    0x88, 0x76, 0x85, 0x9a, 0xd3, 0x14, 0x09, 0xe5, 0x69, 0x9a,
+    0xff, 0x58, 0x92, 0x02, 0x6a, 0x7d, 0x7c, 0x1e, 0x2c, 0xfd,
+    0xa8, 0xca, 0x32, 0x14, 0x4f, 0x0d, 0x84, 0x0d, 0x37, 0x43,
+    0xbf, 0xe4, 0x5d, 0x12, 0xc8, 0x24, 0x91, 0x27, 0x8d, 0x46,
+    0xd9, 0x54, 0x53, 0xe7, 0x62, 0x71, 0xa8, 0x2b, 0x71, 0x41,
+    0x8d, 0x75, 0xf8, 0x3a, 0xa0, 0x61, 0x29, 0x46, 0xa6, 0xe5,
+    0x82, 0xfa, 0x3a, 0xd9, 0x08, 0xfa, 0xfc, 0x63, 0xfd, 0x6b,
+    0x30, 0xbc, 0xf4, 0x4e, 0x9e, 0x8c, 0x25, 0x0c, 0xb6, 0x55,
+    0xe7, 0x3c, 0xd4, 0x4e, 0x0b, 0xfd, 0x8b, 0xc3, 0x0e, 0x1d,
+    0x9c, 0x44, 0x57, 0x8f, 0x1f, 0x86, 0xf7, 0xd5, 0x1b, 0xe4,
+    0x95,
+};
+
+static unsigned char test3072[] = {
+    0x30, 0x82, 0x06, 0xe3, 0x02, 0x01, 0x00, 0x02, 0x82, 0x01,
+    0x81, 0x00, 0xbc, 0x3b, 0x23, 0xc0, 0x33, 0xa7, 0x8b, 0xaa,
+    0xca, 0xa3, 0x8c, 0x94, 0xf2, 0x4c, 0x52, 0x08, 0x85, 0x80,
+    0xfc, 0x36, 0x15, 0xfa, 0x03, 0x06, 0xb6, 0xd6, 0x3f, 0x60,
+    0x8a, 0x89, 0x0d, 0xba, 0x1a, 0x51, 0x0b, 0x12, 0xea, 0x71,
+    0x77, 0xf6, 0x3a, 0x30, 0x21, 0x3d, 0x24, 0xf8, 0x2e, 0xd0,
+    0x17, 0x3a, 0x85, 0x94, 0x25, 0x42, 0x89, 0xff, 0x6a, 0x68,
+    0xdf, 0x1f, 0x86, 0xae, 0xa5, 0xbb, 0x9a, 0x79, 0xf6, 0x69,
+    0x94, 0xfe, 0xde, 0xfe, 0xce, 0x1b, 0x2e, 0xae, 0x1d, 0x91,
+    0xcb, 0xb9, 0xf1, 0x2d, 0xd8, 0x00, 0x82, 0x51, 0x8e, 0xf9,
+    0xfd, 0xac, 0xf1, 0x0e, 0x7f, 0xb7, 0x95, 0x85, 0x35, 0xf9,
+    0xcb, 0xbe, 0x5f, 0xd3, 0x58, 0xe3, 0xa1, 0x54, 0x9e, 0x30,
+    0xb1, 0x8d, 0x01, 0x97, 0x82, 0x06, 0x8e, 0x77, 0xfb, 0xce,
+    0x50, 0x2f, 0xbf, 0xf1, 0xff, 0x57, 0x0a, 0x42, 0x03, 0xfd,
+    0x0e, 0xba, 0x1e, 0xca, 0x85, 0xc1, 0x9b, 0xa5, 0x9d, 0x09,
+    0x0e, 0xe9, 0xbb, 0xc5, 0x73, 0x47, 0x0d, 0x39, 0x3c, 0x64,
+    0x06, 0x9a, 0x79, 0x3f, 0x50, 0x87, 0x9c, 0x18, 0x2d, 0x62,
+    0x01, 0xfc, 0xed, 0xc1, 0x58, 0x28, 0x21, 0x94, 0x1e, 0xf9,
+    0x2d, 0x96, 0x4f, 0xd0, 0xbc, 0xf1, 0xe0, 0x8a, 0xfa, 0x4d,
+    0xb6, 0x78, 0x4a, 0xde, 0x17, 0x59, 0xb0, 0x22, 0xa0, 0x9a,
+    0xd3, 0x70, 0xb6, 0xc2, 0xbe, 0xbc, 0x96, 0xca, 0x41, 0x5f,
+    0x58, 0x4e, 0xce, 0xef, 0x64, 0x45, 0xdd, 0x3f, 0x81, 0x92,
+    0xcc, 0x40, 0x79, 0xfc, 0x19, 0xe2, 0xbc, 0x77, 0x2f, 0x43,
+    0xfb, 0x8e, 0xad, 0x82, 0x4a, 0x0b, 0xb1, 0xbc, 0x09, 0x8a,
+    0x80, 0xc3, 0x0f, 0xef, 0xd2, 0x06, 0xd3, 0x4b, 0x0c, 0x7f,
+    0xae, 0x60, 0x3f, 0x2e, 0x52, 0xb4, 0xe4, 0xc2, 0x5c, 0xa6,
+    0x71, 0xc0, 0x13, 0x9c, 0xca, 0xa6, 0x0d, 0x13, 0xd7, 0xb7,
+    0x14, 0x94, 0x3f, 0x0d, 0x8b, 0x06, 0x70, 0x2f, 0x15, 0x82,
+    0x8d, 0x47, 0x45, 0xa6, 0x00, 0x8a, 0x14, 0x91, 0xde, 0x2f,
+    0x50, 0x17, 0xe3, 0x1d, 0x34, 0x29, 0x8c, 0xe4, 0x57, 0x74,
+    0x2a, 0x3a, 0x82, 0x65, 0x26, 0xf7, 0x8d, 0xcc, 0x1b, 0x8f,
+    0xaf, 0xe5, 0x85, 0xe5, 0xbe, 0x85, 0xd6, 0xb7, 0x04, 0xe8,
+    0xf5, 0xd4, 0x74, 0xe2, 0x54, 0x14, 0xdd, 0x58, 0xcf, 0x1f,
+    0x11, 0x8a, 0x9f, 0x82, 0xa2, 0x01, 0xf9, 0xc2, 0xdf, 0x7b,
+    0x84, 0xb1, 0xd8, 0x5b, 0x70, 0xbb, 0x24, 0xe7, 0xd0, 0x2a,
+    0x75, 0x3d, 0x55, 0xac, 0x45, 0xe9, 0xab, 0xc6, 0x84, 0x8a,
+    0xe7, 0x6d, 0x26, 0x12, 0x89, 0xb5, 0x67, 0xe8, 0x46, 0x9d,
+    0x46, 0x1a, 0xfa, 0x2d, 0xc0, 0x5b, 0x60, 0x46, 0x8b, 0xb7,
+    0x32, 0x03, 0xff, 0x75, 0xee, 0x9f, 0x3c, 0xdd, 0xb6, 0x35,
+    0x4e, 0x82, 0xbd, 0x99, 0x73, 0x51, 0x02, 0x03, 0x01, 0x00,
+    0x01, 0x02, 0x82, 0x01, 0x80, 0x42, 0xee, 0xa4, 0x9f, 0xcb,
+    0xbe, 0x60, 0x23, 0xb3, 0x3a, 0xc4, 0xda, 0x91, 0xee, 0x21,
+    0x9d, 0x76, 0x1b, 0x8f, 0x93, 0x8b, 0xed, 0x02, 0xf6, 0x78,
+    0x3d, 0x66, 0xfb, 0xe5, 0x47, 0x26, 0xe2, 0x6e, 0x49, 0x33,
+    0x2e, 0xde, 0xbe, 0xca, 0x71, 0x7b, 0xef, 0x71, 0x62, 0x54,
+    0xab, 0x0b, 0xba, 0x63, 0x08, 0x24, 0x47, 0xb1, 0x98, 0x1f,
+    0x89, 0xfb, 0x44, 0x9f, 0x52, 0x8e, 0x89, 0xbb, 0xd5, 0x21,
+    0xf1, 0x0c, 0x76, 0x2e, 0xcd, 0x12, 0x6e, 0x78, 0xcb, 0xa1,
+    0xa5, 0xb8, 0x4e, 0x07, 0xab, 0x6e, 0xdf, 0x66, 0x57, 0x87,
+    0xff, 0x88, 0x5f, 0xcc, 0x9c, 0x9a, 0x7b, 0x15, 0x5f, 0x2a,
+    0x83, 0xdb, 0xd5, 0x9f, 0x65, 0x6a, 0x9d, 0xb4, 0x95, 0xfc,
+    0xe0, 0x22, 0x00, 0x1e, 0xa2, 0x8d, 0x56, 0x5a, 0x9e, 0x0a,
+    0x3b, 0x10, 0x07, 0x24, 0xec, 0x55, 0xcc, 0xaf, 0x87, 0x3b,
+    0xd6, 0x8d, 0xa4, 0x86, 0x80, 0x18, 0x42, 0xdb, 0x9d, 0x24,
+    0xc3, 0x97, 0x3b, 0x89, 0x5a, 0x03, 0xb3, 0x0a, 0x72, 0xd1,
+    0x78, 0xf0, 0xc8, 0x80, 0xb0, 0x9d, 0x3c, 0xae, 0x5e, 0x0a,
+    0x5b, 0x6e, 0x87, 0xd3, 0x3d, 0x25, 0x2e, 0x03, 0x33, 0x01,
+    0xfd, 0xb1, 0xa5, 0xd9, 0x58, 0x01, 0xb9, 0xaf, 0xf6, 0x32,
+    0x6a, 0x38, 0xe7, 0x39, 0x63, 0x3c, 0xfc, 0x0c, 0x41, 0x90,
+    0x28, 0x40, 0x03, 0xcd, 0xfb, 0xde, 0x80, 0x74, 0x21, 0xaa,
+    0xae, 0x58, 0xe9, 0x97, 0x18, 0x85, 0x58, 0x3d, 0x2b, 0xd6,
+    0x61, 0xf6, 0xe8, 0xbc, 0x6d, 0x2a, 0xf3, 0xb8, 0xea, 0x8c,
+    0x64, 0x44, 0xc6, 0xd3, 0x9f, 0x00, 0x7b, 0xb2, 0x52, 0x18,
+    0x11, 0x04, 0x96, 0xb7, 0x05, 0xbb, 0xc2, 0x38, 0x5b, 0xa7,
+    0x0a, 0x84, 0xb6, 0x4f, 0x02, 0x63, 0xa4, 0x57, 0x00, 0xe3,
+    0xde, 0xe4, 0xf2, 0xb3, 0x55, 0xd9, 0x00, 0xa9, 0xd2, 0x5c,
+    0x69, 0x9f, 0xe5, 0x80, 0x4f, 0x23, 0x7c, 0xd9, 0xa7, 0x77,
+    0x4a, 0xbb, 0x09, 0x6d, 0x45, 0x02, 0xcf, 0x32, 0x90, 0xfd,
+    0x10, 0xb6, 0xb3, 0x93, 0xd9, 0x3b, 0x1d, 0x57, 0x66, 0xb5,
+    0xb3, 0xb1, 0x6e, 0x53, 0x5f, 0x04, 0x60, 0x29, 0xcd, 0xe8,
+    0xb8, 0xab, 0x62, 0x82, 0x33, 0x40, 0xc7, 0xf8, 0x64, 0x60,
+    0x0e, 0xab, 0x06, 0x3e, 0xa0, 0xa3, 0x62, 0x11, 0x3f, 0x67,
+    0x5d, 0x24, 0x9e, 0x60, 0x29, 0xdc, 0x4c, 0xd5, 0x13, 0xee,
+    0x3d, 0xb7, 0x84, 0x93, 0x27, 0xb5, 0x6a, 0xf9, 0xf0, 0xdd,
+    0x50, 0xac, 0x46, 0x3c, 0xe6, 0xd5, 0xec, 0xf7, 0xb7, 0x9f,
+    0x23, 0x39, 0x9c, 0x88, 0x8c, 0x5a, 0x62, 0x3f, 0x8d, 0x4a,
+    0xd7, 0xeb, 0x5e, 0x1e, 0x49, 0xf8, 0xa9, 0x53, 0x11, 0x75,
+    0xd0, 0x43, 0x1e, 0xc7, 0x29, 0x22, 0x80, 0x1f, 0xc5, 0x83,
+    0x8d, 0x20, 0x04, 0x87, 0x7f, 0x57, 0x8c, 0xf5, 0xa1, 0x02,
+    0x81, 0xc1, 0x00, 0xf7, 0xaa, 0xf5, 0xa5, 0x00, 0xdb, 0xd6,
+    0x11, 0xfc, 0x07, 0x6d, 0x22, 0x24, 0x2b, 0x4b, 0xc5, 0x67,
+    0x0f, 0x37, 0xa5, 0xdb, 0x8f, 0x38, 0xe2, 0x05, 0x43, 0x9a,
+    0x44, 0x05, 0x3f, 0xa9, 0xac, 0x4c, 0x98, 0x3c, 0x72, 0x38,
+    0xc3, 0x89, 0x33, 0x58, 0x73, 0x51, 0xcc, 0x5d, 0x2f, 0x8f,
+    0x6d, 0x3f, 0xa1, 0x22, 0x9e, 0xfb, 0x9a, 0xb4, 0xb8, 0x79,
+    0x95, 0xaf, 0x83, 0xcf, 0x5a, 0xb7, 0x14, 0x14, 0x0c, 0x51,
+    0x8a, 0x11, 0xe6, 0xd6, 0x21, 0x1e, 0x17, 0x13, 0xd3, 0x69,
+    0x7a, 0x3a, 0xd5, 0xaf, 0x3f, 0xb8, 0x25, 0x01, 0xcb, 0x2b,
+    0xe6, 0xfc, 0x03, 0xd8, 0xd4, 0xf7, 0x20, 0xe0, 0x21, 0xef,
+    0x1a, 0xca, 0x61, 0xeb, 0x8e, 0x96, 0x45, 0x8e, 0x5c, 0xe6,
+    0x81, 0x0b, 0x2d, 0x05, 0x32, 0xf9, 0x41, 0x62, 0xb4, 0x33,
+    0x98, 0x10, 0x3a, 0xcd, 0xf0, 0x7a, 0x8b, 0x1a, 0x48, 0xd7,
+    0x3b, 0x01, 0xf5, 0x18, 0x65, 0x8f, 0x3c, 0xc2, 0x31, 0x3b,
+    0xd3, 0xa7, 0x17, 0x5f, 0x7c, 0x0c, 0xe7, 0x25, 0x18, 0x5a,
+    0x08, 0xe1, 0x09, 0x89, 0x13, 0xa7, 0xc5, 0x12, 0xab, 0x88,
+    0x30, 0xcd, 0x06, 0xf9, 0xba, 0x6f, 0xca, 0x9c, 0x8a, 0xda,
+    0x3e, 0x53, 0x90, 0xd7, 0x16, 0x2e, 0xfc, 0xbc, 0xad, 0xd6,
+    0x3d, 0xc0, 0x66, 0x4c, 0x02, 0x3d, 0x31, 0xfd, 0x6c, 0xdb,
+    0x1c, 0xdf, 0x96, 0x33, 0x23, 0x02, 0x81, 0xc1, 0x00, 0xc2,
+    0x90, 0x47, 0xc4, 0xfb, 0x59, 0xf0, 0xc5, 0x14, 0x75, 0x29,
+    0xfa, 0x77, 0xa1, 0x8d, 0xd4, 0x90, 0xa1, 0x0d, 0x3f, 0x16,
+    0x88, 0xe3, 0x4c, 0x8f, 0x8f, 0x18, 0x8c, 0x9c, 0x8a, 0xd5,
+    0xa7, 0x41, 0x99, 0xf3, 0x80, 0x8e, 0xb1, 0xb8, 0x63, 0xd8,
+    0x3f, 0x95, 0xd0, 0xd0, 0x2b, 0xf5, 0xe6, 0x93, 0xe8, 0xfe,
+    0xd0, 0x73, 0xd5, 0xbd, 0xb4, 0xee, 0x51, 0x19, 0x6a, 0x10,
+    0xca, 0xc8, 0xba, 0xa4, 0x4d, 0x84, 0x54, 0x38, 0x17, 0xb5,
+    0xd0, 0xa8, 0x75, 0x22, 0xc5, 0x1b, 0x61, 0xa6, 0x51, 0x88,
+    0x63, 0xf0, 0x4f, 0xd1, 0x88, 0xd9, 0x16, 0x49, 0x30, 0xe1,
+    0xa8, 0x47, 0xc9, 0x30, 0x1d, 0x5c, 0x75, 0xd8, 0x89, 0xb6,
+    0x1d, 0x45, 0xd8, 0x0f, 0x94, 0x89, 0xb3, 0xe4, 0x51, 0xfa,
+    0x21, 0xff, 0x6f, 0xb6, 0x30, 0x6f, 0x33, 0x24, 0xbc, 0x09,
+    0x98, 0xe9, 0x20, 0x02, 0x0b, 0xde, 0xff, 0xc5, 0x06, 0xb6,
+    0x28, 0xa3, 0xa1, 0x07, 0xe8, 0xe1, 0xd2, 0xc2, 0xf1, 0xd1,
+    0x23, 0x6b, 0x4c, 0x3a, 0xae, 0x85, 0xec, 0xf9, 0xff, 0xa7,
+    0x9b, 0x25, 0xb8, 0x95, 0x1d, 0xa8, 0x14, 0x81, 0x4f, 0x79,
+    0x4f, 0xd6, 0x39, 0x5d, 0xe6, 0x5f, 0xd2, 0x34, 0x54, 0x8b,
+    0x1e, 0x40, 0x4c, 0x15, 0x5a, 0x45, 0xce, 0x0c, 0xb0, 0xdf,
+    0xa1, 0x17, 0xb8, 0xb0, 0x6a, 0x82, 0xa5, 0x97, 0x92, 0x70,
+    0xfb, 0x02, 0x81, 0xc0, 0x77, 0x46, 0x44, 0x2b, 0x04, 0xf0,
+    0xda, 0x75, 0xaa, 0xd4, 0xc0, 0xc0, 0x32, 0x7f, 0x0f, 0x6c,
+    0xb0, 0x27, 0x69, 0xfb, 0x5c, 0x73, 0xeb, 0x47, 0x1e, 0x95,
+    0xe2, 0x13, 0x64, 0x1b, 0xb6, 0xd1, 0x1d, 0xca, 0x2b, 0x42,
+    0x2f, 0x08, 0x2c, 0x69, 0x27, 0xed, 0xd1, 0xb5, 0x04, 0x23,
+    0xc5, 0x85, 0x2d, 0xa1, 0xa2, 0x94, 0xc2, 0x43, 0x4d, 0x49,
+    0x92, 0x74, 0x7e, 0x24, 0x92, 0x95, 0xf3, 0x99, 0x9d, 0xd6,
+    0x18, 0xe6, 0xcf, 0x9c, 0x45, 0xff, 0x89, 0x08, 0x40, 0x2a,
+    0x0e, 0xa0, 0x28, 0xf9, 0x83, 0xfe, 0xc1, 0xe6, 0x40, 0xa8,
+    0xe2, 0x29, 0xc9, 0xb0, 0xe8, 0x9a, 0x17, 0xb2, 0x23, 0x7e,
+    0xf4, 0x32, 0x08, 0xc9, 0x83, 0xb2, 0x15, 0xb8, 0xc5, 0xc9,
+    0x03, 0xd1, 0x9d, 0xda, 0x3e, 0xa8, 0xbf, 0xd5, 0xb7, 0x7d,
+    0x65, 0x63, 0x94, 0x5d, 0x5d, 0x94, 0xb4, 0xcf, 0x8d, 0x07,
+    0x0b, 0x70, 0x85, 0x8e, 0xce, 0x03, 0x0b, 0x2a, 0x8d, 0xb3,
+    0x3c, 0x46, 0xc0, 0x2f, 0xc7, 0x72, 0x6c, 0x9c, 0x5d, 0x07,
+    0x0f, 0x45, 0x3b, 0x6b, 0x66, 0x32, 0xab, 0x17, 0x83, 0xd8,
+    0x4c, 0x2c, 0x84, 0x71, 0x19, 0x8f, 0xaa, 0x0a, 0xff, 0xbc,
+    0xf7, 0x42, 0x10, 0xe8, 0xae, 0x4d, 0x26, 0xaf, 0xdd, 0x06,
+    0x33, 0x29, 0x66, 0x21, 0x5d, 0xf5, 0xae, 0x17, 0x07, 0x1f,
+    0x87, 0x9e, 0xae, 0x27, 0x1d, 0xd5, 0x02, 0x81, 0xc0, 0x56,
+    0x17, 0x4f, 0x9a, 0x8a, 0xf9, 0xde, 0x3e, 0xe6, 0x71, 0x7d,
+    0x94, 0xb5, 0xb0, 0xc7, 0xb8, 0x62, 0x12, 0xd1, 0x70, 0xb4,
+    0x00, 0xf8, 0x4a, 0xdd, 0x4f, 0x1d, 0x36, 0xc2, 0xe1, 0xef,
+    0xee, 0x25, 0x6a, 0x00, 0xc4, 0x46, 0xdf, 0xbe, 0xce, 0x77,
+    0x56, 0x93, 0x6d, 0x25, 0x5f, 0xfe, 0x5b, 0xfb, 0xe0, 0xe2,
+    0x37, 0xcc, 0xb9, 0xac, 0x4a, 0xce, 0x15, 0x16, 0xa0, 0xc7,
+    0x33, 0x63, 0xa4, 0xaa, 0xa5, 0x1e, 0x43, 0xc1, 0xda, 0x43,
+    0xfa, 0x43, 0x40, 0x29, 0x95, 0x7c, 0x2b, 0x36, 0x53, 0xe7,
+    0x7d, 0x09, 0x4d, 0xd8, 0x52, 0xac, 0x74, 0x5f, 0x08, 0x81,
+    0x21, 0x5c, 0x3a, 0x5a, 0xce, 0xf3, 0x25, 0xb6, 0x1e, 0x21,
+    0x76, 0x4c, 0x7c, 0x71, 0x50, 0x71, 0xaa, 0x27, 0x02, 0x5b,
+    0x23, 0x06, 0x0b, 0x21, 0x5b, 0xc7, 0x28, 0xa3, 0x3d, 0x8d,
+    0x25, 0x9b, 0x2a, 0x2d, 0x9d, 0xa1, 0x1c, 0x1d, 0xcb, 0x7d,
+    0x78, 0xf8, 0x06, 0x7e, 0x20, 0x7f, 0x24, 0x2a, 0x5c, 0xa4,
+    0x04, 0xff, 0x2a, 0x68, 0xe0, 0xe6, 0xa3, 0xd8, 0x6f, 0x56,
+    0x73, 0xa1, 0x3a, 0x4e, 0xc9, 0x23, 0xa1, 0x87, 0x22, 0x6a,
+    0x74, 0x78, 0x3f, 0x44, 0x1c, 0x77, 0x13, 0xe5, 0x51, 0xef,
+    0x89, 0x00, 0x3c, 0x6a, 0x4a, 0x5a, 0x8e, 0xf5, 0x30, 0xa2,
+    0x93, 0x7e, 0x92, 0x9b, 0x85, 0x55, 0xaf, 0xfe, 0x24, 0xaf,
+    0x57, 0x02, 0x81, 0xc1, 0x00, 0xa4, 0xc2, 0x6a, 0x59, 0x45,
+    0xea, 0x71, 0x7d, 0x4c, 0xaf, 0xaf, 0xd6, 0x55, 0x97, 0x73,
+    0xc5, 0xa1, 0x3c, 0xf6, 0x59, 0x23, 0xb6, 0x1f, 0x5e, 0x9c,
+    0x96, 0x0f, 0x97, 0x66, 0x82, 0x91, 0x48, 0x36, 0x70, 0x02,
+    0x67, 0xde, 0x34, 0xa6, 0x95, 0x7b, 0x51, 0x43, 0x66, 0xa4,
+    0x16, 0x45, 0x59, 0x12, 0xdb, 0x35, 0x19, 0x4b, 0xbf, 0x1d,
+    0xab, 0xf3, 0x3f, 0xb4, 0xb4, 0x6f, 0x66, 0xb0, 0x67, 0xc6,
+    0x77, 0x2c, 0x46, 0xa8, 0x03, 0x64, 0x9a, 0x13, 0x9d, 0x40,
+    0x22, 0x56, 0x76, 0x1a, 0x7c, 0x1e, 0xe2, 0xda, 0x7f, 0x09,
+    0xcf, 0x10, 0xe3, 0xf2, 0xf4, 0x2a, 0x3b, 0x46, 0xc7, 0x61,
+    0x9b, 0xef, 0x4a, 0x18, 0x60, 0x8c, 0x32, 0x71, 0xb9, 0xdd,
+    0xac, 0xa0, 0xc6, 0x8d, 0x3f, 0xab, 0xc3, 0x21, 0x2c, 0xeb,
+    0x91, 0x8f, 0xc7, 0x43, 0x0d, 0x0c, 0x67, 0x9e, 0xab, 0xe6,
+    0x8d, 0xb6, 0x2d, 0x41, 0xca, 0x43, 0xd8, 0xcb, 0x30, 0xfb,
+    0x3b, 0x40, 0x0d, 0x10, 0x9b, 0xb1, 0x55, 0x93, 0x73, 0x8b,
+    0x60, 0xef, 0xc0, 0xee, 0xc0, 0xa6, 0x7a, 0x79, 0x90, 0xfd,
+    0x4c, 0x25, 0xd4, 0x4f, 0x67, 0xbe, 0xf7, 0x86, 0x3c, 0x5d,
+    0x2b, 0x7d, 0x97, 0x3d, 0xa2, 0x91, 0xa5, 0x06, 0x69, 0xf6,
+    0x7a, 0xb8, 0x77, 0xe6, 0x70, 0xa9, 0xd8, 0x86, 0x4b, 0xa6,
+    0xcf, 0x67, 0x1d, 0x33, 0xcf, 0xfe, 0x3e
+};
+
+static unsigned char test4096[] = {
+    0x30, 0x82, 0x09, 0x29, 0x02, 0x01, 0x00, 0x02, 0x82, 0x02,
+    0x01, 0x00, 0xc0, 0x71, 0xac, 0x1a, 0x13, 0x88, 0x82, 0x43,
+    0x3b, 0x51, 0x57, 0x71, 0x8d, 0xb6, 0x2b, 0x82, 0x65, 0x21,
+    0x53, 0x5f, 0x28, 0x29, 0x4f, 0x8d, 0x7c, 0x8a, 0xb9, 0x44,
+    0xb3, 0x28, 0x41, 0x4f, 0xd3, 0xfa, 0x6a, 0xf8, 0xb9, 0x28,
+    0x50, 0x39, 0x67, 0x53, 0x2c, 0x3c, 0xd7, 0xcb, 0x96, 0x41,
+    0x40, 0x32, 0xbb, 0xeb, 0x70, 0xae, 0x1f, 0xb0, 0x65, 0xf7,
+    0x3a, 0xd9, 0x22, 0xfd, 0x10, 0xae, 0xbd, 0x02, 0xe2, 0xdd,
+    0xf3, 0xc2, 0x79, 0x3c, 0xc6, 0xfc, 0x75, 0xbb, 0xaf, 0x4e,
+    0x3a, 0x36, 0xc2, 0x4f, 0xea, 0x25, 0xdf, 0x13, 0x16, 0x4b,
+    0x20, 0xfe, 0x4b, 0x69, 0x16, 0xc4, 0x7f, 0x1a, 0x43, 0xa6,
+    0x17, 0x1b, 0xb9, 0x0a, 0xf3, 0x09, 0x86, 0x28, 0x89, 0xcf,
+    0x2c, 0xd0, 0xd4, 0x81, 0xaf, 0xc6, 0x6d, 0xe6, 0x21, 0x8d,
+    0xee, 0xef, 0xea, 0xdc, 0xb7, 0xc6, 0x3b, 0x63, 0x9f, 0x0e,
+    0xad, 0x89, 0x78, 0x23, 0x18, 0xbf, 0x70, 0x7e, 0x84, 0xe0,
+    0x37, 0xec, 0xdb, 0x8e, 0x9c, 0x3e, 0x6a, 0x19, 0xcc, 0x99,
+    0x72, 0xe6, 0xb5, 0x7d, 0x6d, 0xfa, 0xe5, 0xd3, 0xe4, 0x90,
+    0xb5, 0xb2, 0xb2, 0x12, 0x70, 0x4e, 0xca, 0xf8, 0x10, 0xf8,
+    0xa3, 0x14, 0xc2, 0x48, 0x19, 0xeb, 0x60, 0x99, 0xbb, 0x2a,
+    0x1f, 0xb1, 0x7a, 0xb1, 0x3d, 0x24, 0xfb, 0xa0, 0x29, 0xda,
+    0xbd, 0x1b, 0xd7, 0xa4, 0xbf, 0xef, 0x60, 0x2d, 0x22, 0xca,
+    0x65, 0x98, 0xf1, 0xc4, 0xe1, 0xc9, 0x02, 0x6b, 0x16, 0x28,
+    0x2f, 0xa1, 0xaa, 0x79, 0x00, 0xda, 0xdc, 0x7c, 0x43, 0xf7,
+    0x42, 0x3c, 0xa0, 0xef, 0x68, 0xf7, 0xdf, 0xb9, 0x69, 0xfb,
+    0x8e, 0x01, 0xed, 0x01, 0x42, 0xb5, 0x4e, 0x57, 0xa6, 0x26,
+    0xb8, 0xd0, 0x7b, 0x56, 0x6d, 0x03, 0xc6, 0x40, 0x8c, 0x8c,
+    0x2a, 0x55, 0xd7, 0x9c, 0x35, 0x00, 0x94, 0x93, 0xec, 0x03,
+    0xeb, 0x22, 0xef, 0x77, 0xbb, 0x79, 0x13, 0x3f, 0x15, 0xa1,
+    0x8f, 0xca, 0xdf, 0xfd, 0xd3, 0xb8, 0xe1, 0xd4, 0xcc, 0x09,
+    0x3f, 0x3c, 0x2c, 0xdb, 0xd1, 0x49, 0x7f, 0x38, 0x07, 0x83,
+    0x6d, 0xeb, 0x08, 0x66, 0xe9, 0x06, 0x44, 0x12, 0xac, 0x95,
+    0x22, 0x90, 0x23, 0x67, 0xd4, 0x08, 0xcc, 0xf4, 0xb7, 0xdc,
+    0xcc, 0x87, 0xd4, 0xac, 0x69, 0x35, 0x4c, 0xb5, 0x39, 0x36,
+    0xcd, 0xa4, 0xd2, 0x95, 0xca, 0x0d, 0xc5, 0xda, 0xc2, 0xc5,
+    0x22, 0x32, 0x28, 0x08, 0xe3, 0xd2, 0x8b, 0x38, 0x30, 0xdc,
+    0x8c, 0x75, 0x4f, 0x6a, 0xec, 0x7a, 0xac, 0x16, 0x3e, 0xa8,
+    0xd4, 0x6a, 0x45, 0xe1, 0xa8, 0x4f, 0x2e, 0x80, 0x34, 0xaa,
+    0x54, 0x1b, 0x02, 0x95, 0x7d, 0x8a, 0x6d, 0xcc, 0x79, 0xca,
+    0xf2, 0xa4, 0x2e, 0x8d, 0xfb, 0xfe, 0x15, 0x51, 0x10, 0x0e,
+    0x4d, 0x88, 0xb1, 0xc7, 0xf4, 0x79, 0xdb, 0xf0, 0xb4, 0x56,
+    0x44, 0x37, 0xca, 0x5a, 0xc1, 0x8c, 0x48, 0xac, 0xae, 0x48,
+    0x80, 0x83, 0x01, 0x3f, 0xde, 0xd9, 0xd3, 0x2c, 0x51, 0x46,
+    0xb1, 0x41, 0xb6, 0xc6, 0x91, 0x72, 0xf9, 0x83, 0x55, 0x1b,
+    0x8c, 0xba, 0xf3, 0x73, 0xe5, 0x2c, 0x74, 0x50, 0x3a, 0xbe,
+    0xc5, 0x2f, 0xa7, 0xb2, 0x6d, 0x8c, 0x9e, 0x13, 0x77, 0xa3,
+    0x13, 0xcd, 0x6d, 0x8c, 0x45, 0xe1, 0xfc, 0x0b, 0xb7, 0x69,
+    0xe9, 0x27, 0xbc, 0x65, 0xc3, 0xfa, 0x9b, 0xd0, 0xef, 0xfe,
+    0xe8, 0x1f, 0xb3, 0x5e, 0x34, 0xf4, 0x8c, 0xea, 0xfc, 0xd3,
+    0x81, 0xbf, 0x3d, 0x30, 0xb2, 0xb4, 0x01, 0xe8, 0x43, 0x0f,
+    0xba, 0x02, 0x23, 0x42, 0x76, 0x82, 0x31, 0x73, 0x91, 0xed,
+    0x07, 0x46, 0x61, 0x0d, 0x39, 0x83, 0x40, 0xce, 0x7a, 0xd4,
+    0xdb, 0x80, 0x2c, 0x1f, 0x0d, 0xd1, 0x34, 0xd4, 0x92, 0xe3,
+    0xd4, 0xf1, 0xc2, 0x01, 0x02, 0x03, 0x01, 0x00, 0x01, 0x02,
+    0x82, 0x02, 0x01, 0x00, 0x97, 0x6c, 0xda, 0x6e, 0xea, 0x4f,
+    0xcf, 0xaf, 0xf7, 0x4c, 0xd9, 0xf1, 0x90, 0x00, 0x77, 0xdb,
+    0xf2, 0x97, 0x76, 0x72, 0xb9, 0xb7, 0x47, 0xd1, 0x9c, 0xdd,
+    0xcb, 0x4a, 0x33, 0x6e, 0xc9, 0x75, 0x76, 0xe6, 0xe4, 0xa5,
+    0x31, 0x8c, 0x77, 0x13, 0xb4, 0x29, 0xcd, 0xf5, 0x52, 0x17,
+    0xef, 0xf3, 0x08, 0x00, 0xe3, 0xbd, 0x2e, 0xbc, 0xd4, 0x52,
+    0x88, 0xe9, 0x30, 0x75, 0x0b, 0x02, 0xf5, 0xcd, 0x89, 0x0c,
+    0x6c, 0x57, 0x19, 0x27, 0x3d, 0x1e, 0x85, 0xb4, 0xc1, 0x2f,
+    0x1d, 0x92, 0x00, 0x5c, 0x76, 0x29, 0x4b, 0xa4, 0xe1, 0x12,
+    0xb3, 0xc8, 0x09, 0xfe, 0x0e, 0x78, 0x72, 0x61, 0xcb, 0x61,
+    0x6f, 0x39, 0x91, 0x95, 0x4e, 0xd5, 0x3e, 0xc7, 0x8f, 0xb8,
+    0xf6, 0x36, 0xfe, 0x9c, 0x93, 0x9a, 0x38, 0x25, 0x7a, 0xf4,
+    0x4a, 0x12, 0xd4, 0xa0, 0x13, 0xbd, 0xf9, 0x1d, 0x12, 0x3e,
+    0x21, 0x39, 0xfb, 0x72, 0xe0, 0x05, 0x3d, 0xc3, 0xe5, 0x50,
+    0xa8, 0x5d, 0x85, 0xa3, 0xea, 0x5f, 0x1c, 0xb2, 0x3f, 0xea,
+    0x6d, 0x03, 0x91, 0x55, 0xd8, 0x19, 0x0a, 0x21, 0x12, 0x16,
+    0xd9, 0x12, 0xc4, 0xe6, 0x07, 0x18, 0x5b, 0x26, 0xa4, 0xae,
+    0xed, 0x2b, 0xb7, 0xa6, 0xed, 0xf8, 0xad, 0xec, 0x77, 0xe6,
+    0x7f, 0x4f, 0x76, 0x00, 0xc0, 0xfa, 0x15, 0x92, 0xb4, 0x2c,
+    0x22, 0xc2, 0xeb, 0x6a, 0xad, 0x14, 0x05, 0xb2, 0xe5, 0x8a,
+    0x9e, 0x85, 0x83, 0xcc, 0x04, 0xf1, 0x56, 0x78, 0x44, 0x5e,
+    0xde, 0xe0, 0x60, 0x1a, 0x65, 0x79, 0x31, 0x23, 0x05, 0xbb,
+    0x01, 0xff, 0xdd, 0x2e, 0xb7, 0xb3, 0xaa, 0x74, 0xe0, 0xa5,
+    0x94, 0xaf, 0x4b, 0xde, 0x58, 0x0f, 0x55, 0xde, 0x33, 0xf6,
+    0xe3, 0xd6, 0x34, 0x36, 0x57, 0xd6, 0x79, 0x91, 0x2e, 0xbe,
+    0x3b, 0xd9, 0x4e, 0xb6, 0x9d, 0x21, 0x5c, 0xd3, 0x48, 0x14,
+    0x7f, 0x4a, 0xc4, 0x60, 0xa9, 0x29, 0xf8, 0x53, 0x7f, 0x88,
+    0x11, 0x2d, 0xb5, 0xc5, 0x2d, 0x6f, 0xee, 0x85, 0x0b, 0xf7,
+    0x8d, 0x9a, 0xbe, 0xb0, 0x42, 0xf2, 0x2e, 0x71, 0xaf, 0x19,
+    0x31, 0x6d, 0xec, 0xcd, 0x6f, 0x2b, 0x23, 0xdf, 0xb4, 0x40,
+    0xaf, 0x2c, 0x0a, 0xc3, 0x1b, 0x7d, 0x7d, 0x03, 0x1d, 0x4b,
+    0xf3, 0xb5, 0xe0, 0x85, 0xd8, 0xdf, 0x91, 0x6b, 0x0a, 0x69,
+    0xf7, 0xf2, 0x69, 0x66, 0x5b, 0xf1, 0xcf, 0x46, 0x7d, 0xe9,
+    0x70, 0xfa, 0x6d, 0x7e, 0x75, 0x4e, 0xa9, 0x77, 0xe6, 0x8c,
+    0x02, 0xf7, 0x14, 0x4d, 0xa5, 0x41, 0x8f, 0x3f, 0xc1, 0x62,
+    0x1e, 0x71, 0x5e, 0x38, 0xb4, 0xd6, 0xe6, 0xe1, 0x4b, 0xc2,
+    0x2c, 0x30, 0x83, 0x81, 0x6f, 0x49, 0x2e, 0x96, 0xe6, 0xc9,
+    0x9a, 0xf7, 0x5d, 0x09, 0xa0, 0x55, 0x02, 0xa5, 0x3a, 0x25,
+    0x23, 0xd0, 0x92, 0xc3, 0xa3, 0xe3, 0x0e, 0x12, 0x2f, 0x4d,
+    0xef, 0xf3, 0x55, 0x5a, 0xbe, 0xe6, 0x19, 0x86, 0x31, 0xab,
+    0x75, 0x9a, 0xd3, 0xf0, 0x2c, 0xc5, 0x41, 0x92, 0xd9, 0x1f,
+    0x5f, 0x11, 0x8c, 0x75, 0x1c, 0x63, 0xd0, 0x02, 0x80, 0x2c,
+    0x68, 0xcb, 0x93, 0xfb, 0x51, 0x73, 0x49, 0xb4, 0x60, 0xda,
+    0xe2, 0x26, 0xaf, 0xa9, 0x46, 0x12, 0xb8, 0xec, 0x50, 0xdd,
+    0x12, 0x06, 0x5f, 0xce, 0x59, 0xe6, 0xf6, 0x1c, 0xe0, 0x54,
+    0x10, 0xad, 0xf6, 0xcd, 0x98, 0xcc, 0x0f, 0xfb, 0xcb, 0x41,
+    0x14, 0x9d, 0xed, 0xe4, 0xb4, 0x74, 0x5f, 0x09, 0x60, 0xc7,
+    0x12, 0xf6, 0x7b, 0x3c, 0x8f, 0xa7, 0x20, 0xbc, 0xe4, 0xb1,
+    0xef, 0xeb, 0xa4, 0x93, 0xc5, 0x06, 0xca, 0x9a, 0x27, 0x9d,
+    0x87, 0xf3, 0xde, 0xca, 0xe5, 0xe7, 0xf6, 0x1c, 0x01, 0x65,
+    0x5b, 0xfb, 0x19, 0x79, 0x6e, 0x08, 0x26, 0xc5, 0xc8, 0x28,
+    0x0e, 0xb6, 0x3b, 0x07, 0x08, 0xc1, 0x02, 0x82, 0x01, 0x01,
+    0x00, 0xe8, 0x1c, 0x73, 0xa6, 0xb8, 0xe0, 0x0e, 0x6d, 0x8d,
+    0x1b, 0xb9, 0x53, 0xed, 0x58, 0x94, 0xe6, 0x1d, 0x60, 0x14,
+    0x5c, 0x76, 0x43, 0xc4, 0x58, 0x19, 0xc4, 0x24, 0xe8, 0xbc,
+    0x1b, 0x3b, 0x0b, 0x13, 0x24, 0x45, 0x54, 0x0e, 0xcc, 0x37,
+    0xf0, 0xe0, 0x63, 0x7d, 0xc3, 0xf7, 0xfb, 0x81, 0x74, 0x81,
+    0xc4, 0x0f, 0x1a, 0x21, 0x48, 0xaf, 0xce, 0xc1, 0xc4, 0x94,
+    0x18, 0x06, 0x44, 0x8d, 0xd3, 0xd2, 0x22, 0x2d, 0x2d, 0x3e,
+    0x5a, 0x31, 0xdc, 0x95, 0x8e, 0xf4, 0x41, 0xfc, 0x58, 0xc9,
+    0x40, 0x92, 0x17, 0x5f, 0xe3, 0xda, 0xac, 0x9e, 0x3f, 0x1c,
+    0x2a, 0x6b, 0x58, 0x5f, 0x48, 0x78, 0x20, 0xb1, 0xaf, 0x24,
+    0x9b, 0x3c, 0x20, 0x8b, 0x93, 0x25, 0x9e, 0xe6, 0x6b, 0xbc,
+    0x13, 0x42, 0x14, 0x6c, 0x36, 0x31, 0xff, 0x7a, 0xd1, 0xc1,
+    0x1a, 0x26, 0x14, 0x7f, 0xa9, 0x76, 0xa7, 0x0c, 0xf8, 0xcc,
+    0xed, 0x07, 0x6a, 0xd2, 0xdf, 0x62, 0xee, 0x0a, 0x7c, 0x84,
+    0xcb, 0x49, 0x90, 0xb2, 0x03, 0x0d, 0xa2, 0x82, 0x06, 0x77,
+    0xf1, 0xcd, 0x67, 0xf2, 0x47, 0x21, 0x02, 0x3f, 0x43, 0x21,
+    0xf0, 0x46, 0x30, 0x62, 0x51, 0x72, 0xb1, 0xe7, 0x48, 0xc6,
+    0x67, 0x12, 0xcd, 0x9e, 0xd6, 0x15, 0xe5, 0x21, 0xed, 0xfa,
+    0x8f, 0x30, 0xa6, 0x41, 0xfe, 0xb6, 0xfa, 0x8f, 0x34, 0x14,
+    0x19, 0xe8, 0x11, 0xf7, 0xa5, 0x77, 0x3e, 0xb7, 0xf9, 0x39,
+    0x07, 0x8c, 0x67, 0x2a, 0xab, 0x7b, 0x08, 0xf8, 0xb0, 0x06,
+    0xa8, 0xea, 0x2f, 0x8f, 0xfa, 0xcc, 0xcc, 0x40, 0xce, 0xf3,
+    0x70, 0x4f, 0x3f, 0x7f, 0xe2, 0x0c, 0xea, 0x76, 0x4a, 0x35,
+    0x4e, 0x47, 0xad, 0x2b, 0xa7, 0x97, 0x5d, 0x74, 0x43, 0x97,
+    0x90, 0xd2, 0xfb, 0xd9, 0xf9, 0x96, 0x01, 0x33, 0x05, 0xed,
+    0x7b, 0x03, 0x05, 0xad, 0xf8, 0x49, 0x03, 0x02, 0x82, 0x01,
+    0x01, 0x00, 0xd4, 0x40, 0x17, 0x66, 0x10, 0x92, 0x95, 0xc8,
+    0xec, 0x62, 0xa9, 0x7a, 0xcb, 0x93, 0x8e, 0xe6, 0x53, 0xd4,
+    0x80, 0x48, 0x27, 0x4b, 0x41, 0xce, 0x61, 0xdf, 0xbf, 0x94,
+    0xa4, 0x3d, 0x71, 0x03, 0x0b, 0xed, 0x25, 0x71, 0x98, 0xa4,
+    0xd6, 0xd5, 0x4a, 0x57, 0xf5, 0x6c, 0x1b, 0xda, 0x21, 0x7d,
+    0x35, 0x45, 0xb3, 0xf3, 0x6a, 0xd9, 0xd3, 0x43, 0xe8, 0x5c,
+    0x54, 0x1c, 0x83, 0x1b, 0xb4, 0x5f, 0xf2, 0x97, 0x24, 0x2e,
+    0xdc, 0x40, 0xde, 0x92, 0x23, 0x59, 0x8e, 0xbc, 0xd2, 0xa1,
+    0xf2, 0xe0, 0x4c, 0xdd, 0x0b, 0xd1, 0xe7, 0xae, 0x65, 0xbc,
+    0xb5, 0xf5, 0x5b, 0x98, 0xe9, 0xd7, 0xc2, 0xb7, 0x0e, 0x55,
+    0x71, 0x0e, 0x3c, 0x0a, 0x24, 0x6b, 0xa6, 0xe6, 0x14, 0x61,
+    0x11, 0xfd, 0x33, 0x42, 0x99, 0x2b, 0x84, 0x77, 0x74, 0x92,
+    0x91, 0xf5, 0x79, 0x79, 0xcf, 0xad, 0x8e, 0x04, 0xef, 0x80,
+    0x1e, 0x57, 0xf4, 0x14, 0xf5, 0x35, 0x09, 0x74, 0xb2, 0x13,
+    0x71, 0x58, 0x6b, 0xea, 0x32, 0x5d, 0xf3, 0xd3, 0x76, 0x48,
+    0x39, 0x10, 0x23, 0x84, 0x9d, 0xbe, 0x92, 0x77, 0x4a, 0xed,
+    0x70, 0x3e, 0x1a, 0xa2, 0x6c, 0xb3, 0x81, 0x00, 0xc3, 0xc9,
+    0xe4, 0x52, 0xc8, 0x24, 0x88, 0x0c, 0x41, 0xad, 0x87, 0x5a,
+    0xea, 0xa3, 0x7a, 0x85, 0x1c, 0x5e, 0x31, 0x7f, 0xc3, 0x35,
+    0xc6, 0xfa, 0x10, 0xc8, 0x75, 0x10, 0xc4, 0x96, 0x99, 0xe7,
+    0xfe, 0x01, 0xb4, 0x74, 0xdb, 0xb4, 0x11, 0xc3, 0xc8, 0x8c,
+    0xf6, 0xf7, 0x3b, 0x66, 0x50, 0xfc, 0xdb, 0xeb, 0xca, 0x47,
+    0x85, 0x89, 0xe1, 0x65, 0xd9, 0x62, 0x34, 0x3c, 0x70, 0xd8,
+    0x2e, 0xb4, 0x2f, 0x65, 0x3c, 0x4a, 0xa6, 0x2a, 0xe7, 0xc7,
+    0xd8, 0x41, 0x8f, 0x8a, 0x43, 0xbf, 0x42, 0xf2, 0x4d, 0xbc,
+    0xfc, 0x9e, 0x27, 0x95, 0xfb, 0x75, 0xff, 0xab, 0x02, 0x82,
+    0x01, 0x00, 0x41, 0x2f, 0x44, 0x57, 0x6d, 0x12, 0x17, 0x5b,
+    0x32, 0xc6, 0xb7, 0x6c, 0x57, 0x7a, 0x8a, 0x0e, 0x79, 0xef,
+    0x72, 0xa8, 0x68, 0xda, 0x2d, 0x38, 0xe4, 0xbb, 0x8d, 0xf6,
+    0x02, 0x65, 0xcf, 0x56, 0x13, 0xe1, 0x1a, 0xcb, 0x39, 0x80,
+    0xa6, 0xb1, 0x32, 0x03, 0x1e, 0xdd, 0xbb, 0x35, 0xd9, 0xac,
+    0x43, 0x89, 0x31, 0x08, 0x90, 0x92, 0x5e, 0x35, 0x3d, 0x7b,
+    0x9c, 0x6f, 0x86, 0xcb, 0x17, 0xdd, 0x85, 0xe4, 0xed, 0x35,
+    0x08, 0x8e, 0xc1, 0xf4, 0x05, 0xd8, 0x68, 0xc6, 0x63, 0x3c,
+    0xf7, 0xff, 0xf7, 0x47, 0x33, 0x39, 0xc5, 0x3e, 0xb7, 0x0e,
+    0x58, 0x35, 0x9d, 0x81, 0xea, 0xf8, 0x6a, 0x2c, 0x1c, 0x5a,
+    0x68, 0x78, 0x64, 0x11, 0x6b, 0xc1, 0x3e, 0x4e, 0x7a, 0xbd,
+    0x84, 0xcb, 0x0f, 0xc2, 0xb6, 0x85, 0x1d, 0xd3, 0x76, 0xc5,
+    0x93, 0x6a, 0x69, 0x89, 0x56, 0x34, 0xdc, 0x4a, 0x9b, 0xbc,
+    0xff, 0xa8, 0x0d, 0x6e, 0x35, 0x9c, 0x60, 0xa7, 0x23, 0x30,
+    0xc7, 0x06, 0x64, 0x39, 0x8b, 0x94, 0x89, 0xee, 0xba, 0x7f,
+    0x60, 0x8d, 0xfa, 0xb6, 0x97, 0x76, 0xdc, 0x51, 0x4a, 0x3c,
+    0xeb, 0x3a, 0x14, 0x2c, 0x20, 0x60, 0x69, 0x4a, 0x86, 0xfe,
+    0x8c, 0x21, 0x84, 0x49, 0x54, 0xb3, 0x20, 0xe1, 0x01, 0x7f,
+    0x58, 0xdf, 0x7f, 0xb5, 0x21, 0x51, 0x8c, 0x47, 0x9f, 0x91,
+    0xeb, 0x97, 0x3e, 0xf2, 0x54, 0xcf, 0x16, 0x46, 0xf9, 0xd9,
+    0xb6, 0xe7, 0x64, 0xc9, 0xd0, 0x54, 0xea, 0x2f, 0xa1, 0xcf,
+    0xa5, 0x7f, 0x28, 0x8d, 0x84, 0xec, 0xd5, 0x39, 0x03, 0x76,
+    0x5b, 0x2d, 0x8e, 0x43, 0xf2, 0x01, 0x24, 0xc9, 0x6f, 0xc0,
+    0xf5, 0x69, 0x6f, 0x7d, 0xb5, 0x85, 0xd2, 0x5f, 0x7f, 0x78,
+    0x40, 0x07, 0x7f, 0x09, 0x15, 0xb5, 0x1f, 0x28, 0x65, 0x10,
+    0xe4, 0x19, 0xa8, 0xc6, 0x9e, 0x8d, 0xdc, 0xcb, 0x02, 0x82,
+    0x01, 0x00, 0x13, 0x01, 0xee, 0x56, 0x80, 0x93, 0x70, 0x00,
+    0x7f, 0x52, 0xd2, 0x94, 0xa1, 0x98, 0x84, 0x4a, 0x92, 0x25,
+    0x4c, 0x9b, 0xa9, 0x91, 0x2e, 0xc2, 0x79, 0xb7, 0x5c, 0xe3,
+    0xc5, 0xd5, 0x8e, 0xc2, 0x54, 0x16, 0x17, 0xad, 0x55, 0x9b,
+    0x25, 0x76, 0x12, 0x63, 0x50, 0x22, 0x2f, 0x58, 0x58, 0x79,
+    0x6b, 0x04, 0xe3, 0xf9, 0x9f, 0x8f, 0x04, 0x41, 0x67, 0x94,
+    0xa5, 0x1f, 0xac, 0x8a, 0x15, 0x9c, 0x26, 0x10, 0x6c, 0xf8,
+    0x19, 0x57, 0x61, 0xd7, 0x3a, 0x7d, 0x31, 0xb0, 0x2d, 0x38,
+    0xbd, 0x94, 0x62, 0xad, 0xc4, 0xfa, 0x36, 0x42, 0x42, 0xf0,
+    0x24, 0x67, 0x65, 0x9d, 0x8b, 0x0b, 0x7c, 0x6f, 0x82, 0x44,
+    0x1a, 0x8c, 0xc8, 0xc9, 0xab, 0xbb, 0x4c, 0x45, 0xfc, 0x7b,
+    0x38, 0xee, 0x30, 0xe1, 0xfc, 0xef, 0x8d, 0xbc, 0x58, 0xdf,
+    0x2b, 0x5d, 0x0d, 0x54, 0xe0, 0x49, 0x4d, 0x97, 0x99, 0x8f,
+    0x22, 0xa8, 0x83, 0xbe, 0x40, 0xbb, 0x50, 0x2e, 0x78, 0x28,
+    0x0f, 0x95, 0x78, 0x8c, 0x8f, 0x98, 0x24, 0x56, 0xc2, 0x97,
+    0xf3, 0x2c, 0x43, 0xd2, 0x03, 0x82, 0x66, 0x81, 0x72, 0x5f,
+    0x53, 0x16, 0xec, 0xb1, 0xb1, 0x04, 0x5e, 0x40, 0x20, 0x48,
+    0x7b, 0x3f, 0x02, 0x97, 0x6a, 0xeb, 0x96, 0x12, 0x21, 0x35,
+    0xfe, 0x1f, 0x47, 0xc0, 0x95, 0xea, 0xc5, 0x8a, 0x08, 0x84,
+    0x4f, 0x5e, 0x63, 0x94, 0x60, 0x0f, 0x71, 0x5b, 0x7f, 0x4a,
+    0xec, 0x4f, 0x60, 0xc6, 0xba, 0x4a, 0x24, 0xf1, 0x20, 0x8b,
+    0xa7, 0x2e, 0x3a, 0xce, 0x8d, 0xe0, 0x27, 0x1d, 0xb5, 0x8e,
+    0xb4, 0x21, 0xc5, 0xe2, 0xa6, 0x16, 0x0a, 0x51, 0x83, 0x55,
+    0x88, 0xd1, 0x30, 0x11, 0x63, 0xd5, 0xd7, 0x8d, 0xae, 0x16,
+    0x12, 0x82, 0xc4, 0x85, 0x00, 0x4e, 0x27, 0x83, 0xa5, 0x7c,
+    0x90, 0x2e, 0xe5, 0xa2, 0xa3, 0xd3, 0x4c, 0x63, 0x02, 0x82,
+    0x01, 0x01, 0x00, 0x86, 0x08, 0x98, 0x98, 0xa5, 0x00, 0x05,
+    0x39, 0x77, 0xd9, 0x66, 0xb3, 0xcf, 0xca, 0xa0, 0x71, 0xb3,
+    0x50, 0xce, 0x3d, 0xb1, 0x93, 0x95, 0x35, 0xc4, 0xd4, 0x2e,
+    0x90, 0xdf, 0x0f, 0xfc, 0x60, 0xc1, 0x94, 0x68, 0x61, 0x43,
+    0xca, 0x9a, 0x23, 0x4a, 0x1e, 0x45, 0x72, 0x99, 0xb5, 0x1e,
+    0x61, 0x8d, 0x77, 0x0f, 0xa0, 0xbb, 0xd7, 0x77, 0xb4, 0x2a,
+    0x15, 0x11, 0x88, 0x2d, 0xb3, 0x56, 0x61, 0x5e, 0x6a, 0xed,
+    0xa4, 0x46, 0x4a, 0x3f, 0x50, 0x11, 0xd6, 0xba, 0xb6, 0xd7,
+    0x95, 0x65, 0x53, 0xc3, 0xa1, 0x8f, 0xe0, 0xa3, 0xf5, 0x1c,
+    0xfd, 0xaf, 0x6e, 0x43, 0xd7, 0x17, 0xa7, 0xd3, 0x81, 0x1b,
+    0xa4, 0xdf, 0xe0, 0x97, 0x8a, 0x46, 0x03, 0xd3, 0x46, 0x0e,
+    0x83, 0x48, 0x4e, 0xd2, 0x02, 0xcb, 0xc0, 0xad, 0x79, 0x95,
+    0x8c, 0x96, 0xba, 0x40, 0x34, 0x11, 0x71, 0x5e, 0xe9, 0x11,
+    0xf9, 0xc5, 0x4a, 0x5e, 0x91, 0x9d, 0xf5, 0x92, 0x4f, 0xeb,
+    0xc6, 0x70, 0x02, 0x2d, 0x3d, 0x04, 0xaa, 0xe9, 0x3a, 0x8e,
+    0xd5, 0xa8, 0xad, 0xf7, 0xce, 0x0d, 0x16, 0xb2, 0xec, 0x0a,
+    0x9c, 0xf5, 0x94, 0x39, 0xb9, 0x8a, 0xfc, 0x1e, 0xf9, 0xcc,
+    0xf2, 0x5f, 0x21, 0x31, 0x74, 0x72, 0x6b, 0x64, 0xae, 0x35,
+    0x61, 0x8d, 0x0d, 0xcb, 0xe7, 0xda, 0x39, 0xca, 0xf3, 0x21,
+    0x66, 0x0b, 0x95, 0xd7, 0x0a, 0x7c, 0xca, 0xa1, 0xa9, 0x5a,
+    0xe8, 0xac, 0xe0, 0x71, 0x54, 0xaf, 0x28, 0xcf, 0xd5, 0x70,
+    0x89, 0xe0, 0xf3, 0x9e, 0x43, 0x6c, 0x8d, 0x7b, 0x99, 0x01,
+    0x68, 0x4d, 0xa1, 0x45, 0x46, 0x0c, 0x43, 0xbc, 0xcc, 0x2c,
+    0xdd, 0xc5, 0x46, 0xc8, 0x4e, 0x0e, 0xbe, 0xed, 0xb9, 0x26,
+    0xab, 0x2e, 0xdb, 0xeb, 0x8f, 0xff, 0xdb, 0xb0, 0xc6, 0x55,
+    0xaf, 0xf8, 0x2a, 0x91, 0x9d, 0x50, 0x44, 0x21, 0x17,
+};
+
+static unsigned char test7680[] = {
+    0x30, 0x82, 0x11, 0x09, 0x02, 0x01, 0x00, 0x02, 0x82, 0x03,
+    0xc1, 0x00, 0xe3, 0x27, 0x46, 0x99, 0xb5, 0x17, 0xab, 0xfa,
+    0x65, 0x05, 0x7a, 0x06, 0x81, 0x14, 0xce, 0x43, 0x21, 0x49,
+    0x0f, 0x08, 0xf1, 0x70, 0xb4, 0xc1, 0x10, 0xd1, 0x87, 0xf8,
+    0x29, 0x91, 0x36, 0x66, 0x2d, 0xbe, 0x7b, 0x1d, 0xa2, 0x0b,
+    0x20, 0x38, 0xd9, 0x8e, 0x78, 0x27, 0xcf, 0xb5, 0x45, 0x58,
+    0x3d, 0xf4, 0xda, 0xf0, 0xdc, 0x21, 0x17, 0x52, 0xcd, 0x68,
+    0xe2, 0x81, 0xac, 0x88, 0x61, 0x10, 0xbc, 0xb0, 0x7f, 0xe4,
+    0xf3, 0x78, 0xb7, 0x28, 0x6c, 0x5f, 0x5c, 0xc2, 0x8d, 0x3d,
+    0xb0, 0x87, 0x41, 0x15, 0x2e, 0x09, 0x5f, 0xea, 0x06, 0x7f,
+    0xe9, 0x35, 0x18, 0x90, 0x50, 0xad, 0xf6, 0xb9, 0xfd, 0x33,
+    0x02, 0x1a, 0x99, 0x9e, 0xa5, 0x7d, 0x2c, 0x3b, 0x24, 0xe7,
+    0x31, 0x35, 0x73, 0x9a, 0xb0, 0xfe, 0x03, 0xfc, 0xc6, 0x98,
+    0x78, 0xd9, 0x66, 0x95, 0xa5, 0x12, 0xbc, 0x1e, 0x82, 0xbc,
+    0xf1, 0xc5, 0x31, 0xcd, 0xa6, 0xb1, 0x0c, 0x02, 0xbf, 0x7f,
+    0xb7, 0xaf, 0x5f, 0xd6, 0xed, 0xf7, 0xc1, 0x59, 0x86, 0x3a,
+    0x35, 0x95, 0x54, 0x21, 0x8d, 0x6a, 0xb3, 0xd1, 0x2b, 0x71,
+    0xf5, 0xf1, 0x66, 0x00, 0xb1, 0x88, 0xee, 0x3b, 0xa4, 0x41,
+    0x52, 0x1a, 0xf5, 0x0e, 0x32, 0xb6, 0xbf, 0x52, 0xab, 0x51,
+    0x55, 0x91, 0x32, 0x4f, 0xaf, 0x91, 0xac, 0xf7, 0xff, 0x8e,
+    0x3b, 0x2b, 0x61, 0xe9, 0x6d, 0x1d, 0x68, 0x80, 0x90, 0x79,
+    0x34, 0x96, 0xca, 0x49, 0x43, 0x7c, 0x89, 0x4e, 0x5e, 0x31,
+    0xb5, 0xce, 0x01, 0x9b, 0x09, 0xaf, 0x92, 0x06, 0x24, 0xe7,
+    0x22, 0x35, 0xcc, 0xa2, 0x0b, 0xfb, 0x5b, 0x87, 0x65, 0x71,
+    0xff, 0x64, 0x3e, 0xf9, 0xe8, 0x33, 0xa0, 0xc3, 0x4e, 0xb2,
+    0x41, 0x98, 0x54, 0xeb, 0x13, 0x99, 0xfb, 0x32, 0x78, 0x7e,
+    0xda, 0x4f, 0xd3, 0x46, 0x6a, 0xb5, 0x78, 0x81, 0x3f, 0x04,
+    0x13, 0x5f, 0x67, 0xaf, 0x88, 0xa5, 0x9e, 0x0d, 0xc5, 0xf3,
+    0xe7, 0x4c, 0x51, 0xf5, 0x51, 0x4a, 0xa4, 0x58, 0x64, 0xd9,
+    0xa2, 0x32, 0x54, 0x36, 0xce, 0x38, 0xd8, 0xc2, 0x0e, 0x0d,
+    0x60, 0x8e, 0x32, 0x7f, 0x90, 0x8a, 0xbc, 0x88, 0xbe, 0x6a,
+    0xc0, 0x47, 0x0f, 0x02, 0x41, 0xff, 0x3b, 0x7e, 0xc5, 0xa6,
+    0x33, 0x1d, 0x19, 0xd1, 0xd5, 0x67, 0x6c, 0xbf, 0x16, 0xb0,
+    0x7e, 0x80, 0x10, 0xbf, 0x7f, 0xdd, 0xd0, 0xf4, 0xc3, 0x94,
+    0x2c, 0x9a, 0x2c, 0xda, 0x69, 0x4e, 0xd6, 0x7b, 0x40, 0x4d,
+    0x2a, 0x27, 0xcb, 0x5a, 0xe5, 0x2d, 0x3f, 0x7d, 0x51, 0x9d,
+    0x9f, 0x70, 0xde, 0x50, 0xb1, 0xd3, 0xd2, 0x38, 0x4d, 0x1c,
+    0xca, 0xc2, 0x1e, 0x80, 0xd0, 0x36, 0x82, 0x04, 0xe6, 0x17,
+    0x79, 0x9f, 0x2e, 0xc9, 0xed, 0x2b, 0xd5, 0x1b, 0xfa, 0x7d,
+    0x1a, 0x80, 0xb5, 0x0e, 0x2f, 0x05, 0xbe, 0x4a, 0x1b, 0xfe,
+    0x0a, 0xad, 0x01, 0xde, 0x91, 0xc8, 0xf9, 0x81, 0xbe, 0xc7,
+    0xaf, 0xe7, 0x87, 0xed, 0x9d, 0xb8, 0x6c, 0xad, 0x65, 0xed,
+    0x5e, 0xd3, 0x67, 0x8c, 0x62, 0x3a, 0xe7, 0xfd, 0x67, 0xe0,
+    0xbb, 0x57, 0xaf, 0x56, 0xeb, 0x4a, 0x58, 0x6e, 0xad, 0xf2,
+    0xbe, 0xc3, 0x70, 0x29, 0xf8, 0xeb, 0x68, 0x45, 0xa0, 0xbd,
+    0xcd, 0xa5, 0xb4, 0xd9, 0x01, 0xb7, 0x44, 0xeb, 0x97, 0xf3,
+    0x0c, 0x56, 0xe4, 0x26, 0xd0, 0xa5, 0xb1, 0xa3, 0x49, 0x6e,
+    0x88, 0xf2, 0x22, 0xe2, 0x7b, 0x58, 0x3a, 0xd9, 0x52, 0xa4,
+    0xb1, 0x4c, 0x5c, 0x7c, 0xf0, 0x88, 0x7b, 0x9f, 0x06, 0xe9,
+    0x32, 0x4e, 0xf2, 0x64, 0x83, 0x8b, 0xa2, 0xea, 0x1d, 0x25,
+    0xf1, 0x8d, 0x16, 0x8b, 0xe0, 0xab, 0xd2, 0xe9, 0xe4, 0x6b,
+    0x7d, 0x76, 0x98, 0x22, 0x53, 0x31, 0x6b, 0xcc, 0xf1, 0xe5,
+    0x1d, 0xd7, 0xa5, 0xb0, 0xea, 0x6b, 0x38, 0x14, 0x0c, 0x06,
+    0x10, 0x27, 0xd8, 0x33, 0xf3, 0x9a, 0xae, 0x94, 0xdd, 0x0b,
+    0xb4, 0x6d, 0xe5, 0x91, 0xdd, 0xf1, 0x0f, 0x27, 0xa4, 0x94,
+    0x55, 0xf0, 0xde, 0x07, 0x29, 0xe6, 0x3f, 0x26, 0x19, 0xa1,
+    0xdd, 0xd1, 0x06, 0x99, 0xda, 0x54, 0x23, 0x3c, 0xf5, 0x5c,
+    0x2e, 0x96, 0xa9, 0x21, 0x23, 0x25, 0x2e, 0x6f, 0xf1, 0xf9,
+    0x11, 0x54, 0xe5, 0x7b, 0xb9, 0x1f, 0x11, 0xe2, 0x9e, 0x6b,
+    0x61, 0x8b, 0xa3, 0x8b, 0xc1, 0x20, 0x9b, 0xfb, 0x51, 0xef,
+    0xbb, 0xb9, 0xf6, 0xaf, 0x66, 0xb3, 0x2c, 0x25, 0xef, 0x76,
+    0xcb, 0xbf, 0x7a, 0x93, 0x2f, 0xe1, 0x17, 0x56, 0xc1, 0x00,
+    0x33, 0xb5, 0xd9, 0x91, 0x05, 0x31, 0xcc, 0x72, 0xcd, 0x4a,
+    0x93, 0x9a, 0xe3, 0x21, 0x42, 0x9e, 0xb8, 0x4e, 0x6c, 0x27,
+    0x93, 0xf0, 0x7f, 0x22, 0xdb, 0xe5, 0xb3, 0xa3, 0xf7, 0xe7,
+    0x80, 0xbb, 0x91, 0xca, 0xf7, 0xe8, 0x52, 0xb8, 0x11, 0x64,
+    0x66, 0x25, 0x94, 0xf8, 0x6f, 0x0b, 0x3b, 0xb7, 0xff, 0x80,
+    0x9e, 0x36, 0xe9, 0x88, 0x2e, 0xab, 0x05, 0xbf, 0x99, 0x9f,
+    0x2b, 0x4f, 0xc6, 0xb1, 0x13, 0x5b, 0x06, 0xff, 0x0a, 0x7b,
+    0xbc, 0x7f, 0x07, 0xa0, 0x35, 0xc2, 0x2d, 0x44, 0x3e, 0xad,
+    0x44, 0xcb, 0x47, 0x18, 0x26, 0x71, 0x7b, 0x17, 0xc9, 0x6d,
+    0xb5, 0x4b, 0xcf, 0xdf, 0x14, 0x2c, 0x6c, 0xdf, 0x21, 0xce,
+    0x93, 0x49, 0x34, 0x69, 0x49, 0xfd, 0x3e, 0x71, 0x5b, 0xfa,
+    0x07, 0xc5, 0x7e, 0x5e, 0x54, 0x1a, 0x3c, 0xa6, 0x29, 0xb5,
+    0xbf, 0x0d, 0xf1, 0xc6, 0xa4, 0x61, 0xd6, 0x17, 0x1d, 0xf0,
+    0xa2, 0x78, 0x8f, 0xbc, 0x7e, 0x0c, 0xb4, 0xf0, 0x1e, 0x05,
+    0xea, 0xb5, 0xad, 0x68, 0x95, 0x0b, 0x27, 0xb4, 0x29, 0x7c,
+    0x70, 0x2a, 0x9a, 0x0a, 0x39, 0xd4, 0x76, 0xb7, 0x72, 0x30,
+    0x5e, 0xae, 0x9c, 0x4a, 0x55, 0xc7, 0x46, 0xd7, 0x5f, 0xbe,
+    0x10, 0x61, 0x25, 0x18, 0x7a, 0x9f, 0xd3, 0x05, 0x3d, 0x6f,
+    0x9a, 0x1e, 0xec, 0x2b, 0x03, 0xe0, 0x49, 0x6a, 0x9c, 0xd6,
+    0xdb, 0xc2, 0xa1, 0xe1, 0x0a, 0xbb, 0x31, 0x42, 0xc8, 0x43,
+    0x4e, 0x7c, 0xa9, 0x7c, 0x60, 0xea, 0xbe, 0xf1, 0x8b, 0xe8,
+    0xb2, 0x90, 0x83, 0x14, 0x21, 0xe4, 0xb3, 0x0d, 0x7c, 0x63,
+    0x3c, 0x98, 0x55, 0xc6, 0x44, 0xa6, 0xa8, 0x1e, 0x42, 0xb7,
+    0x89, 0xa8, 0xbd, 0xb8, 0x34, 0x3d, 0x09, 0x80, 0x99, 0x73,
+    0x9f, 0xaf, 0x17, 0x56, 0xf2, 0x73, 0x3e, 0x1e, 0x6e, 0xe9,
+    0x18, 0xa0, 0x5b, 0x69, 0xce, 0xfd, 0x3d, 0x77, 0x81, 0x95,
+    0x3b, 0xf1, 0xde, 0x26, 0xe9, 0x27, 0xef, 0x92, 0x2a, 0x97,
+    0xdc, 0x95, 0xa5, 0xa3, 0xb0, 0xfb, 0x96, 0x89, 0x4f, 0xe6,
+    0xc1, 0x42, 0x0b, 0xfd, 0xb4, 0x6d, 0x0a, 0x9f, 0x9b, 0x31,
+    0xd8, 0x21, 0x38, 0x8a, 0xee, 0xb6, 0x5c, 0x12, 0xa8, 0xb4,
+    0x07, 0x79, 0x41, 0xa7, 0x7f, 0x13, 0x74, 0xad, 0x0b, 0xee,
+    0x28, 0x52, 0xac, 0x2f, 0x4d, 0x30, 0x1c, 0xc5, 0xa6, 0xa5,
+    0x61, 0x42, 0xbd, 0xe1, 0x4f, 0xd3, 0xec, 0x66, 0xf2, 0x63,
+    0xf4, 0x93, 0xdb, 0x35, 0x2d, 0x3b, 0x71, 0x25, 0x09, 0xde,
+    0xda, 0x46, 0xda, 0xe2, 0xa7, 0xa3, 0xdf, 0xcd, 0xbf, 0x58,
+    0x05, 0x25, 0x02, 0x03, 0x01, 0x00, 0x01, 0x02, 0x82, 0x03,
+    0xc0, 0x5f, 0xd5, 0x15, 0x1b, 0x09, 0xe4, 0xa7, 0xc0, 0xa6,
+    0xd8, 0x0d, 0xa8, 0x2a, 0xd3, 0x1d, 0x46, 0x03, 0x07, 0xf0,
+    0x98, 0xe4, 0x4b, 0x99, 0x66, 0x8e, 0x72, 0xe7, 0xbb, 0x51,
+    0xc6, 0x1a, 0xbe, 0x36, 0xf4, 0x52, 0xba, 0xa8, 0xbf, 0xaa,
+    0xe3, 0x71, 0x1d, 0x83, 0x21, 0xc0, 0xa6, 0x88, 0x4f, 0xf7,
+    0x2b, 0x93, 0x26, 0xe4, 0xa7, 0xed, 0x50, 0x18, 0xaa, 0xf4,
+    0x4c, 0xa2, 0xfe, 0x92, 0x7c, 0xde, 0x2e, 0x54, 0x76, 0xc2,
+    0x25, 0x1e, 0x98, 0xa6, 0x48, 0x01, 0x39, 0x6f, 0x1f, 0x24,
+    0x97, 0x9b, 0x64, 0x95, 0x1c, 0x8d, 0x63, 0x8d, 0x44, 0x6f,
+    0x9d, 0xdf, 0xf4, 0x1a, 0xa5, 0x9a, 0x1e, 0xd3, 0x6c, 0xae,
+    0xa9, 0x8c, 0x3f, 0xfb, 0x2f, 0x78, 0xf6, 0xa6, 0xd6, 0x06,
+    0xd3, 0xb7, 0x26, 0xff, 0x1e, 0xdb, 0x8d, 0xcc, 0x37, 0x4d,
+    0x5c, 0xe2, 0xc3, 0xa5, 0x75, 0xe6, 0xf9, 0xb4, 0x4c, 0x84,
+    0x6f, 0x9e, 0x58, 0x55, 0xc8, 0x01, 0xfa, 0x32, 0xd2, 0x6e,
+    0x2b, 0x45, 0xf2, 0xc6, 0x48, 0xad, 0x40, 0xd8, 0xb9, 0x3c,
+    0x1b, 0xf8, 0xf7, 0x82, 0xd3, 0x0e, 0x73, 0xe3, 0xb1, 0x5b,
+    0x82, 0x71, 0x77, 0x3f, 0x6f, 0x36, 0x9a, 0xe0, 0xec, 0x51,
+    0xf8, 0x5f, 0x84, 0x92, 0xee, 0xb8, 0x7e, 0xe7, 0x1a, 0x14,
+    0x50, 0x82, 0x7a, 0x4d, 0xe6, 0xd6, 0xa3, 0x76, 0x24, 0x8a,
+    0x5f, 0xfe, 0x19, 0xdd, 0xd7, 0xf7, 0x5b, 0xae, 0x18, 0x04,
+    0x90, 0xcd, 0x5c, 0xe5, 0x64, 0xe8, 0x04, 0xb1, 0x06, 0xa5,
+    0xdd, 0xf8, 0x9d, 0x71, 0x13, 0xaa, 0x36, 0x7f, 0x61, 0x27,
+    0xf4, 0xac, 0x95, 0x7d, 0x1a, 0x99, 0x7d, 0xe0, 0xd5, 0x9c,
+    0x5a, 0xad, 0x9a, 0xff, 0x54, 0xb0, 0xb1, 0x55, 0x45, 0x2d,
+    0x19, 0x58, 0x52, 0x28, 0xdd, 0xe0, 0xb5, 0x65, 0x52, 0x97,
+    0x45, 0xf0, 0x2b, 0x98, 0x1f, 0x61, 0x6c, 0x9d, 0xaa, 0x59,
+    0x85, 0xf9, 0x97, 0x7b, 0xbd, 0xeb, 0x95, 0x81, 0xfb, 0x29,
+    0x8c, 0xf0, 0x52, 0xdf, 0xed, 0xee, 0xb2, 0x00, 0x32, 0x35,
+    0x14, 0xa8, 0xa4, 0xca, 0x91, 0xff, 0x18, 0xb7, 0x96, 0xfb,
+    0x32, 0x62, 0xa9, 0xa0, 0xd0, 0x77, 0x43, 0xf5, 0x99, 0xd1,
+    0xee, 0xe8, 0xad, 0x1a, 0x2c, 0xd4, 0xeb, 0xe1, 0xf5, 0x01,
+    0x41, 0x78, 0xc0, 0x27, 0x19, 0x50, 0x2e, 0xba, 0x22, 0xd1,
+    0xeb, 0xb3, 0xa5, 0x27, 0x0b, 0xec, 0xf9, 0x26, 0x7e, 0x1f,
+    0xe7, 0x17, 0x9f, 0x39, 0xa8, 0x72, 0x22, 0x63, 0x79, 0x6a,
+    0x9c, 0x89, 0x55, 0x9a, 0xb4, 0x61, 0x41, 0xbc, 0xaa, 0x14,
+    0x37, 0x29, 0x03, 0xc0, 0x52, 0x4e, 0x31, 0x44, 0x8f, 0x2e,
+    0x17, 0x81, 0x88, 0xf4, 0xce, 0xda, 0x41, 0xb8, 0xd5, 0x14,
+    0x91, 0x8c, 0xca, 0xd2, 0x0d, 0x99, 0x06, 0x09, 0xc2, 0xb7,
+    0xe8, 0xae, 0xfa, 0x01, 0xea, 0x99, 0x62, 0x68, 0xb6, 0xdf,
+    0xc8, 0x27, 0xae, 0xbf, 0xb0, 0x9b, 0x5b, 0x1a, 0xa2, 0xe2,
+    0x5a, 0x7a, 0xe5, 0x4b, 0x92, 0x1f, 0xff, 0x73, 0xae, 0x16,
+    0x40, 0x78, 0x42, 0x28, 0xbb, 0x13, 0x5e, 0xbc, 0x71, 0x7a,
+    0x78, 0x3e, 0xd8, 0x1b, 0xc2, 0x2c, 0xd6, 0xdc, 0xfa, 0x39,
+    0x72, 0xf8, 0xa2, 0x2c, 0x8b, 0x1c, 0x5d, 0xab, 0xb8, 0x07,
+    0xc7, 0xae, 0x29, 0x93, 0x68, 0xbf, 0x61, 0xe9, 0xa4, 0x37,
+    0x83, 0x7d, 0x13, 0xc7, 0x18, 0xf0, 0x7d, 0xa4, 0x20, 0x47,
+    0x14, 0x68, 0x95, 0x46, 0x56, 0x6d, 0xd5, 0x7b, 0xe1, 0x51,
+    0x8f, 0x96, 0xc1, 0x7b, 0x35, 0x09, 0x7a, 0x89, 0x0e, 0xdf,
+    0x12, 0xd5, 0xe1, 0x9c, 0x2a, 0x94, 0x95, 0x43, 0x93, 0x48,
+    0xa6, 0x23, 0xe6, 0xd8, 0xf2, 0xb8, 0x0e, 0xba, 0x6d, 0x61,
+    0x03, 0xaf, 0x40, 0x63, 0x2b, 0x2f, 0xee, 0x61, 0x4c, 0xc4,
+    0x70, 0x3d, 0x78, 0xc1, 0x4f, 0x8e, 0x0b, 0x9b, 0x06, 0x35,
+    0x6d, 0x6d, 0x83, 0x37, 0xbb, 0x39, 0x7d, 0x7f, 0x33, 0x93,
+    0xc4, 0xeb, 0x8e, 0xfc, 0xda, 0xf0, 0x54, 0xfe, 0x1d, 0xc4,
+    0xd3, 0x83, 0x99, 0xdf, 0x65, 0xee, 0x00, 0x7d, 0x86, 0x27,
+    0xd4, 0x3a, 0x6b, 0xe6, 0x82, 0x8e, 0x58, 0x2d, 0x03, 0x38,
+    0xef, 0x6c, 0x82, 0x87, 0x18, 0x3b, 0x47, 0xe7, 0xbc, 0xe1,
+    0x58, 0x70, 0x4d, 0x46, 0x96, 0x34, 0x60, 0x96, 0x15, 0x09,
+    0x3c, 0x84, 0x40, 0xaf, 0x80, 0x32, 0x75, 0xc7, 0x23, 0x6c,
+    0xfb, 0x1d, 0x57, 0x73, 0x19, 0x09, 0xe8, 0x1a, 0x4c, 0x02,
+    0x5c, 0x7e, 0x4e, 0xbe, 0x75, 0xf8, 0x73, 0xff, 0x2d, 0x54,
+    0x19, 0x55, 0xf5, 0xf4, 0x1b, 0xc9, 0xbc, 0xc2, 0x19, 0xcb,
+    0xb7, 0x4e, 0x6a, 0x0d, 0xff, 0xca, 0x7d, 0xd0, 0x88, 0x91,
+    0x8b, 0x9b, 0x21, 0xa4, 0xa2, 0x43, 0x0d, 0xbc, 0x9e, 0x73,
+    0x7d, 0x54, 0x7d, 0x95, 0xcc, 0x63, 0x5e, 0xc1, 0xb8, 0xe6,
+    0x27, 0xff, 0x20, 0x07, 0xe8, 0x6e, 0x7e, 0xf2, 0x0f, 0x5a,
+    0x09, 0xef, 0xe5, 0x4d, 0x80, 0x39, 0x95, 0xd5, 0xf4, 0xee,
+    0x3b, 0xca, 0x7c, 0x73, 0xf8, 0x39, 0x5a, 0xc1, 0x1d, 0x7d,
+    0x94, 0x72, 0x32, 0xad, 0x58, 0xe2, 0xfc, 0x71, 0x6e, 0x66,
+    0xaa, 0xa1, 0x59, 0xd6, 0xac, 0xab, 0xbe, 0x8c, 0x53, 0x99,
+    0xcd, 0xe8, 0x2d, 0xb5, 0xb3, 0x46, 0x58, 0x2e, 0x16, 0xd7,
+    0x4d, 0x8b, 0x7d, 0x4a, 0xb1, 0x4c, 0x85, 0x91, 0x1b, 0x57,
+    0x54, 0xf8, 0x14, 0x59, 0xdb, 0xc4, 0x2c, 0x9c, 0x08, 0x6d,
+    0x3d, 0xd7, 0xf6, 0xa6, 0xe6, 0xb3, 0x2a, 0xe7, 0x29, 0x1c,
+    0xab, 0xb4, 0xed, 0x13, 0x19, 0xf8, 0xb6, 0x60, 0x92, 0x44,
+    0x53, 0xd4, 0xa9, 0x7e, 0xba, 0x21, 0xa2, 0xdc, 0x6e, 0xa5,
+    0x5e, 0x53, 0x59, 0x3c, 0x52, 0x61, 0x7b, 0x5f, 0x19, 0xad,
+    0xc8, 0x6d, 0x68, 0x8d, 0x7a, 0xc9, 0xd6, 0xef, 0xeb, 0x67,
+    0x4f, 0xca, 0xe7, 0xf6, 0x29, 0x36, 0x97, 0xfb, 0x3e, 0x37,
+    0x95, 0x85, 0x71, 0x70, 0xf6, 0x63, 0x86, 0x2a, 0x29, 0xd7,
+    0x9a, 0x96, 0x76, 0xa7, 0x47, 0x98, 0x4e, 0x06, 0x31, 0xaf,
+    0xf3, 0x4f, 0x2a, 0x65, 0x90, 0x6a, 0x4b, 0x8e, 0x43, 0x79,
+    0xe2, 0xdd, 0xce, 0x08, 0x1c, 0x01, 0xec, 0x38, 0x41, 0xdd,
+    0x19, 0xd8, 0xf3, 0x36, 0x03, 0x35, 0x03, 0xaf, 0x1c, 0x45,
+    0x3c, 0xac, 0x13, 0xaa, 0x36, 0x16, 0x48, 0x77, 0xb3, 0xbe,
+    0xa3, 0xb3, 0x9d, 0x7f, 0x20, 0xca, 0x74, 0x65, 0xac, 0x93,
+    0xa7, 0x54, 0xad, 0xc8, 0x68, 0x0e, 0xf8, 0x44, 0x1f, 0xad,
+    0x2c, 0xb7, 0x9a, 0x9a, 0x07, 0xe5, 0xcd, 0x87, 0xe0, 0x14,
+    0xb5, 0xaf, 0xd3, 0xd7, 0xcf, 0x13, 0x9f, 0x3b, 0xbd, 0xfe,
+    0x29, 0x0b, 0x72, 0xf5, 0x4c, 0x54, 0x94, 0xc7, 0x66, 0xec,
+    0xa8, 0x41, 0x96, 0x3d, 0x17, 0xed, 0x19, 0xc0, 0x82, 0x3e,
+    0x5f, 0x9a, 0x91, 0xfe, 0xd1, 0x2f, 0xb8, 0x94, 0xaa, 0x58,
+    0x68, 0x95, 0x31, 0x87, 0x57, 0x9a, 0x75, 0x94, 0x4d, 0x38,
+    0x7d, 0x56, 0x82, 0x81, 0x9c, 0xb9, 0x34, 0x2b, 0xe7, 0x40,
+    0xd9, 0x3c, 0x77, 0x5b, 0x95, 0x51, 0x06, 0x11, 0x41, 0xe3,
+    0x8b, 0xb7, 0x32, 0xeb, 0xe1, 0x05, 0x1b, 0x10, 0xa8, 0x0e,
+    0xa1, 0x02, 0x82, 0x01, 0xe1, 0x00, 0xfa, 0x38, 0x34, 0xfe,
+    0x55, 0x87, 0x71, 0x62, 0x47, 0x00, 0x33, 0x64, 0x67, 0x70,
+    0x79, 0x76, 0xdf, 0xfe, 0xc3, 0x28, 0x38, 0xdf, 0x90, 0xd4,
+    0xc0, 0xee, 0x98, 0xbf, 0x9d, 0x9b, 0x85, 0xd8, 0x61, 0x65,
+    0xa5, 0x70, 0xf5, 0xd2, 0x2c, 0xbf, 0x2f, 0xb5, 0x55, 0x79,
+    0x92, 0x13, 0xba, 0x4d, 0x3c, 0x39, 0xbf, 0xd5, 0x31, 0x13,
+    0x7a, 0x31, 0xf4, 0x8b, 0xce, 0xf8, 0xd0, 0xd3, 0x9b, 0xe2,
+    0xee, 0x31, 0xdb, 0xba, 0xcc, 0x1a, 0xba, 0x1c, 0x8d, 0xee,
+    0xea, 0xcb, 0xd3, 0x5a, 0xad, 0x87, 0xd6, 0xf9, 0x15, 0x2f,
+    0x6e, 0x00, 0x06, 0x74, 0x25, 0x8d, 0xff, 0xc8, 0xa6, 0x11,
+    0x1c, 0xe8, 0x16, 0x1a, 0xde, 0x53, 0x05, 0xb9, 0x53, 0x55,
+    0x28, 0x83, 0x3d, 0xbe, 0x61, 0x0c, 0xc4, 0x98, 0x7d, 0xf6,
+    0xec, 0x36, 0xc3, 0xe5, 0xe7, 0x1d, 0x14, 0x64, 0xcb, 0x0d,
+    0x62, 0x5d, 0x7a, 0xcd, 0x88, 0xfc, 0x66, 0x4e, 0xf9, 0x36,
+    0x47, 0x95, 0x18, 0x3a, 0x48, 0x2a, 0xff, 0x62, 0x8f, 0x6c,
+    0xe2, 0xc2, 0xe9, 0xd3, 0x6a, 0x45, 0x5c, 0xf5, 0x89, 0x53,
+    0x5c, 0xbe, 0xcf, 0xad, 0x87, 0x22, 0x9c, 0x31, 0x48, 0xdb,
+    0xd8, 0xe4, 0xe5, 0x38, 0xae, 0xc2, 0xb0, 0xd2, 0xba, 0xb7,
+    0x30, 0x53, 0x2d, 0xb1, 0x35, 0xf1, 0x58, 0x0f, 0x8a, 0x06,
+    0x51, 0x76, 0xb9, 0x2c, 0x32, 0xe0, 0xd1, 0xaa, 0x82, 0x34,
+    0x69, 0x71, 0x1c, 0x5f, 0x35, 0xa8, 0x9d, 0x11, 0xac, 0x13,
+    0xdb, 0x7b, 0xf6, 0x93, 0xe3, 0xb9, 0xbd, 0xd9, 0xb2, 0x86,
+    0xff, 0x61, 0x88, 0x2b, 0x72, 0x5c, 0x84, 0xe1, 0x0c, 0x72,
+    0xab, 0x44, 0xff, 0x23, 0x13, 0xaf, 0xd1, 0x5a, 0xd3, 0xea,
+    0x73, 0xfe, 0xd5, 0xa4, 0x7d, 0x9e, 0x4e, 0xac, 0x03, 0x93,
+    0x72, 0x14, 0x2d, 0x96, 0x6f, 0xee, 0xb4, 0xcd, 0x4e, 0xab,
+    0xea, 0x71, 0x93, 0x81, 0xe0, 0x3d, 0xcd, 0x61, 0x96, 0x25,
+    0x76, 0xbd, 0xc4, 0xb5, 0xdd, 0x7c, 0xf1, 0xb9, 0xe1, 0x2c,
+    0x58, 0x1b, 0xa4, 0x46, 0x4b, 0x12, 0x57, 0x58, 0xaa, 0x3a,
+    0xae, 0x89, 0xa3, 0xb3, 0xcf, 0x1f, 0x8d, 0x67, 0xdf, 0x6d,
+    0x7e, 0x8e, 0xfa, 0xc5, 0x09, 0x73, 0x46, 0x56, 0x55, 0x90,
+    0xeb, 0x77, 0x4e, 0x16, 0x4f, 0x68, 0x7b, 0x1f, 0x61, 0x23,
+    0xec, 0xa9, 0x71, 0x30, 0x33, 0x25, 0xc7, 0x4e, 0x26, 0x2e,
+    0x4e, 0x2b, 0xc2, 0x64, 0x5f, 0xf5, 0x8f, 0x7a, 0x4b, 0x1c,
+    0x06, 0xb3, 0x91, 0xf6, 0x9b, 0x51, 0xb7, 0xb0, 0x64, 0x72,
+    0x04, 0xe5, 0xfa, 0x14, 0x2f, 0xed, 0x61, 0x29, 0x03, 0x73,
+    0x19, 0x15, 0x6e, 0x2c, 0x8b, 0x0e, 0xec, 0x4d, 0xf1, 0xe3,
+    0x6f, 0x58, 0x7c, 0xc9, 0x48, 0x67, 0x3f, 0x51, 0xb5, 0xb7,
+    0x26, 0x46, 0xa7, 0x25, 0x79, 0x55, 0xfe, 0x3a, 0x44, 0xb4,
+    0x44, 0xfc, 0xb8, 0x14, 0x34, 0x47, 0xd7, 0xa3, 0x0e, 0x76,
+    0xe7, 0x83, 0x9a, 0x02, 0xc3, 0xcf, 0x2b, 0xd9, 0x83, 0x93,
+    0xd5, 0xee, 0x99, 0x74, 0x45, 0x62, 0x23, 0xa6, 0x02, 0xc9,
+    0xc0, 0x10, 0x70, 0x0a, 0x99, 0x29, 0x0c, 0x79, 0x04, 0x4c,
+    0x77, 0x21, 0x96, 0xf0, 0xa5, 0x17, 0x22, 0xbe, 0xab, 0x9b,
+    0xd7, 0x42, 0xd3, 0xe9, 0xc0, 0x42, 0x44, 0x7d, 0x9d, 0xc9,
+    0x3d, 0xf9, 0x36, 0x97, 0x1b, 0x75, 0x52, 0x8f, 0xe9, 0xb9,
+    0x8c, 0xa7, 0x64, 0x19, 0x5b, 0x5d, 0x60, 0xb4, 0x42, 0x95,
+    0xc9, 0xdb, 0x82, 0x03, 0xc6, 0xb0, 0x28, 0x72, 0x64, 0x03,
+    0x41, 0x4d, 0x8f, 0xc6, 0xd0, 0xcd, 0x02, 0x82, 0x01, 0xe1,
+    0x00, 0xe8, 0x66, 0xa7, 0xf9, 0x0f, 0x5a, 0x21, 0xfc, 0x88,
+    0x4e, 0x91, 0xd5, 0x4a, 0xf0, 0xf4, 0x32, 0xe5, 0x0d, 0xf3,
+    0x06, 0x95, 0xd0, 0x4e, 0x47, 0x0c, 0x04, 0x66, 0x77, 0xfd,
+    0xb8, 0x93, 0x0d, 0xff, 0x8f, 0x97, 0xa0, 0x4a, 0x36, 0x37,
+    0xa6, 0x5e, 0x95, 0x79, 0xc8, 0xb2, 0x21, 0x98, 0x81, 0xf1,
+    0xb8, 0xf4, 0x52, 0xaf, 0x3c, 0x8c, 0x86, 0x85, 0x55, 0x56,
+    0xfc, 0x90, 0xe3, 0x32, 0x50, 0x7c, 0x54, 0x07, 0x9e, 0xed,
+    0xfc, 0xd4, 0xb9, 0x5c, 0x98, 0x22, 0xfb, 0x72, 0xd7, 0x83,
+    0xf0, 0xd1, 0x61, 0x10, 0xbd, 0x68, 0x5d, 0x72, 0xc1, 0xce,
+    0x92, 0x43, 0x77, 0x9f, 0xb8, 0x8d, 0x8e, 0xf2, 0xe3, 0x62,
+    0x4a, 0x93, 0x03, 0xd3, 0xd9, 0x01, 0xa8, 0x99, 0x6f, 0xa3,
+    0x4c, 0x6d, 0x7a, 0xf2, 0x9e, 0x8e, 0x6b, 0xbc, 0xe4, 0x9d,
+    0x8e, 0xe7, 0x25, 0x86, 0xa4, 0xa9, 0xc2, 0xef, 0xdf, 0xbb,
+    0x6e, 0x3d, 0x4b, 0x57, 0x95, 0x81, 0x6f, 0x68, 0x3f, 0x19,
+    0xa8, 0xff, 0x5a, 0x08, 0x7a, 0xe4, 0x4c, 0x4e, 0xb4, 0xea,
+    0xf4, 0xc8, 0x2f, 0xef, 0x8c, 0x5e, 0xcd, 0x62, 0x1c, 0x8c,
+    0x93, 0x60, 0x5d, 0xa3, 0x11, 0x64, 0x0b, 0xeb, 0x6d, 0x21,
+    0xbc, 0x3a, 0x5b, 0x5c, 0x0c, 0xa7, 0x8a, 0xc6, 0xa8, 0xe1,
+    0x48, 0x81, 0x01, 0xb5, 0x65, 0xab, 0x2e, 0xbe, 0x38, 0x94,
+    0xf7, 0xa6, 0x33, 0xc1, 0x6e, 0x0b, 0x88, 0x38, 0xe7, 0x1b,
+    0x04, 0x9a, 0x10, 0x2d, 0x1d, 0x3f, 0x5f, 0x5f, 0xc8, 0xef,
+    0xcd, 0xc5, 0x16, 0xdc, 0x84, 0xc0, 0x66, 0xe0, 0xa3, 0xfc,
+    0xfa, 0x96, 0xc7, 0xb7, 0xec, 0x4f, 0x40, 0x0a, 0xc5, 0xbe,
+    0x6d, 0x39, 0x4a, 0x7e, 0x91, 0x4f, 0xe1, 0x03, 0xd2, 0x39,
+    0xbc, 0x87, 0x69, 0xa1, 0xf0, 0x6d, 0x11, 0xf5, 0xb4, 0x9d,
+    0xae, 0x76, 0x6b, 0xc6, 0xbf, 0xe4, 0x47, 0xbc, 0x4d, 0x13,
+    0x88, 0xa8, 0x83, 0xf5, 0xae, 0x1d, 0xfb, 0x4d, 0x4c, 0x44,
+    0x03, 0xd8, 0xa4, 0x2e, 0x4d, 0xf8, 0x5f, 0x45, 0x94, 0x58,
+    0xd7, 0xd9, 0x4b, 0x47, 0xd8, 0xfc, 0x35, 0x05, 0xed, 0xb4,
+    0xb6, 0xc2, 0x36, 0x2e, 0xba, 0xd2, 0x7a, 0xba, 0x69, 0x34,
+    0xbf, 0xf1, 0xa1, 0x5e, 0x17, 0x71, 0x89, 0xd3, 0x54, 0x57,
+    0x05, 0x2b, 0x82, 0xe3, 0x0a, 0x64, 0x5c, 0x3b, 0x8c, 0x6b,
+    0xc7, 0x10, 0x8a, 0xb5, 0xd3, 0xd7, 0x90, 0xeb, 0xdb, 0x1d,
+    0xa0, 0xbf, 0x6b, 0xea, 0xcd, 0x31, 0x7a, 0x8d, 0x64, 0xcc,
+    0x58, 0xc0, 0x07, 0xa4, 0x6e, 0x14, 0x0b, 0xf3, 0xea, 0x3e,
+    0x87, 0x9f, 0x7c, 0xb8, 0x1c, 0x22, 0x26, 0x8a, 0x7d, 0x90,
+    0xdd, 0x57, 0x28, 0x38, 0xcc, 0x0e, 0x71, 0x92, 0x89, 0xee,
+    0x79, 0x88, 0xbc, 0x05, 0x21, 0xda, 0x42, 0x92, 0x52, 0x66,
+    0xac, 0x4a, 0xe5, 0xf5, 0x6e, 0x47, 0xd5, 0xba, 0x37, 0xd3,
+    0x7c, 0x89, 0xd4, 0xd8, 0x6f, 0xde, 0x63, 0x44, 0xb5, 0x88,
+    0xdd, 0xb1, 0x30, 0xb4, 0x6d, 0xcd, 0xbf, 0xc8, 0x34, 0x27,
+    0x59, 0x7d, 0x79, 0xdc, 0x96, 0x5b, 0x8e, 0xc0, 0x87, 0xc0,
+    0x4e, 0x40, 0x07, 0x13, 0x91, 0x6b, 0x3a, 0x12, 0x03, 0x64,
+    0x70, 0xaf, 0x80, 0x24, 0x1c, 0x5c, 0xfb, 0xf5, 0xc0, 0x74,
+    0x5e, 0xaf, 0x06, 0x18, 0x04, 0x67, 0x4a, 0xbd, 0xac, 0xd7,
+    0xca, 0xbe, 0x4e, 0xa1, 0x19, 0x48, 0x7d, 0xa6, 0x59, 0xf6,
+    0x1a, 0x62, 0x50, 0x53, 0x46, 0xa4, 0x5b, 0x9c, 0x5a, 0xfd,
+    0x89, 0x9d, 0xd4, 0xde, 0xf4, 0xa7, 0x3d, 0x88, 0x73, 0xa5,
+    0xb9, 0x02, 0x82, 0x01, 0xe1, 0x00, 0xe7, 0x70, 0x59, 0xc3,
+    0xed, 0xc4, 0x6b, 0xa1, 0xa5, 0x5e, 0x90, 0x2a, 0x8c, 0x6a,
+    0xc2, 0x4e, 0xab, 0xfc, 0xee, 0xf2, 0x23, 0x38, 0xd6, 0xb3,
+    0x93, 0x08, 0x9e, 0x0c, 0x8e, 0x71, 0x2d, 0xa9, 0xe8, 0xdc,
+    0xa5, 0xdc, 0x07, 0xe3, 0xb1, 0x33, 0xdd, 0xa2, 0xf2, 0x3e,
+    0x92, 0x58, 0xe0, 0xf7, 0x53, 0x7f, 0x6e, 0xea, 0x78, 0x8c,
+    0x35, 0x78, 0x43, 0x63, 0x95, 0xbb, 0x1b, 0x1c, 0xbf, 0x91,
+    0x75, 0x14, 0x74, 0xd3, 0x20, 0xba, 0x8f, 0xee, 0x9d, 0x71,
+    0xa1, 0x87, 0x8a, 0x24, 0xd3, 0x61, 0x53, 0xfb, 0xec, 0x16,
+    0x84, 0xbe, 0x4d, 0x39, 0xdd, 0x0a, 0xac, 0xce, 0x20, 0x9c,
+    0xaf, 0x8a, 0x13, 0xf8, 0x22, 0x2f, 0xd4, 0x99, 0x88, 0x74,
+    0xba, 0x16, 0x3a, 0x63, 0xff, 0x4c, 0x5a, 0x03, 0x5a, 0x6f,
+    0xac, 0x29, 0x33, 0xa5, 0x50, 0xd1, 0xda, 0xed, 0x27, 0xcb,
+    0x67, 0x72, 0x63, 0x85, 0xfc, 0xf0, 0xc8, 0x88, 0xbf, 0x85,
+    0xef, 0x4b, 0xfe, 0xae, 0xd9, 0xd5, 0xbb, 0x86, 0xa4, 0x76,
+    0xe8, 0x7f, 0xb4, 0xdb, 0xb1, 0xee, 0x1a, 0x7f, 0x99, 0xd7,
+    0x9b, 0x6f, 0x7a, 0x94, 0x5c, 0xec, 0x2c, 0x60, 0x81, 0xad,
+    0xa7, 0xbe, 0x80, 0x2e, 0x9f, 0xa6, 0xc0, 0xfb, 0x09, 0x6d,
+    0x2b, 0xab, 0xa4, 0x15, 0xc7, 0x79, 0x46, 0x24, 0x89, 0x5c,
+    0x32, 0xb9, 0x87, 0xa9, 0x54, 0x1e, 0x12, 0x90, 0x8e, 0x02,
+    0x80, 0x8c, 0xf8, 0xdb, 0x2f, 0xbc, 0x98, 0x1b, 0xa2, 0x78,
+    0x73, 0x89, 0x03, 0x97, 0xe3, 0x09, 0x08, 0x8b, 0x75, 0xcf,
+    0xdc, 0x23, 0x90, 0x59, 0xef, 0x5b, 0x98, 0x24, 0xb8, 0xe8,
+    0xcf, 0x75, 0xf0, 0x2f, 0xb7, 0xa3, 0xe6, 0x17, 0x06, 0xf0,
+    0x52, 0xfe, 0x21, 0x0a, 0x16, 0x8e, 0xf8, 0xe1, 0xae, 0x25,
+    0x11, 0x5d, 0x8c, 0x95, 0x1b, 0x4f, 0x45, 0xb8, 0xa8, 0xcd,
+    0xe6, 0xf9, 0xca, 0xa0, 0x54, 0x93, 0x95, 0x86, 0x6f, 0xe4,
+    0x93, 0x22, 0x0f, 0xf2, 0xcf, 0xbd, 0x23, 0xb0, 0xf4, 0x8f,
+    0x99, 0xa7, 0x67, 0x99, 0x05, 0x13, 0x1f, 0xeb, 0x88, 0xf8,
+    0xe2, 0x3b, 0xb9, 0x49, 0x35, 0x89, 0x4f, 0xb8, 0x06, 0x37,
+    0x36, 0xda, 0x75, 0x25, 0x0f, 0x0a, 0xaa, 0xc2, 0x6c, 0x3e,
+    0xb1, 0x2d, 0x16, 0xf3, 0x17, 0xdb, 0xe2, 0x16, 0x32, 0x39,
+    0x92, 0x4b, 0x5f, 0xc0, 0x5f, 0x6e, 0xd0, 0x1c, 0x7e, 0xc0,
+    0x51, 0xd9, 0xb3, 0xe2, 0x37, 0xc7, 0xe0, 0x40, 0x13, 0x7d,
+    0x06, 0xcd, 0xcd, 0x72, 0xb6, 0x53, 0x2d, 0x7e, 0x60, 0x49,
+    0xfe, 0x31, 0xe1, 0xd0, 0x0e, 0x4c, 0x98, 0x93, 0xe0, 0xf6,
+    0xf2, 0xfa, 0x99, 0x7f, 0x65, 0xd8, 0x15, 0xc6, 0x3a, 0xb8,
+    0x4d, 0x63, 0x21, 0x78, 0xe4, 0x19, 0x6b, 0xbd, 0xde, 0x40,
+    0x5b, 0x8c, 0xfa, 0x49, 0x75, 0x23, 0x8f, 0x14, 0xc2, 0x3b,
+    0xa3, 0x9b, 0xc5, 0x80, 0x1a, 0xa3, 0x60, 0xd7, 0x17, 0x27,
+    0xf0, 0x18, 0x0f, 0xba, 0x02, 0xf7, 0x7a, 0xed, 0xa4, 0x00,
+    0x77, 0xde, 0x4b, 0xdd, 0xf9, 0xd7, 0x3e, 0x75, 0xed, 0x1a,
+    0x43, 0x26, 0x71, 0x1b, 0xbc, 0x72, 0xf5, 0x70, 0x72, 0x03,
+    0x70, 0x25, 0x87, 0x81, 0x6a, 0x92, 0x2d, 0xb7, 0x02, 0xf0,
+    0x10, 0x79, 0x65, 0x9d, 0x4e, 0x11, 0x7d, 0x5c, 0x5b, 0x37,
+    0xaa, 0xb4, 0xfa, 0x43, 0x66, 0x48, 0x6c, 0x67, 0x64, 0x9e,
+    0x15, 0x75, 0x36, 0xe7, 0x25, 0x55, 0x07, 0x7f, 0x74, 0x1f,
+    0x2c, 0x28, 0x76, 0xe7, 0x9b, 0x3d, 0x91, 0x0b, 0xcd, 0x6a,
+    0x1d, 0x5a, 0xea, 0x63, 0xd0, 0xf9, 0x02, 0x82, 0x01, 0xe0,
+    0x3e, 0x31, 0xf2, 0xf4, 0x29, 0x92, 0xa2, 0x93, 0xd5, 0xda,
+    0xc9, 0x16, 0x7e, 0xf6, 0xdb, 0x33, 0x9f, 0xaf, 0x4b, 0x01,
+    0xd1, 0x28, 0x2d, 0x3a, 0xc0, 0x51, 0x91, 0x26, 0xbd, 0xa5,
+    0x1e, 0xdd, 0xd9, 0x2e, 0x11, 0x93, 0x19, 0x29, 0x47, 0x5d,
+    0x63, 0xe4, 0xb6, 0xf1, 0xea, 0x12, 0x29, 0xa1, 0x65, 0x12,
+    0x6d, 0x78, 0x8f, 0x63, 0x31, 0xec, 0x72, 0x54, 0x73, 0x72,
+    0x26, 0x48, 0x57, 0x57, 0xc8, 0xde, 0x28, 0x27, 0xf5, 0x62,
+    0xfb, 0x7f, 0x1b, 0xf3, 0xaf, 0x31, 0x01, 0xfc, 0x01, 0x58,
+    0x7a, 0x80, 0x72, 0x9d, 0x6e, 0x07, 0xcc, 0x45, 0x67, 0xc6,
+    0x26, 0xfe, 0x25, 0xa5, 0x9b, 0x64, 0xcd, 0x45, 0xe3, 0x31,
+    0x38, 0x05, 0x07, 0x36, 0x05, 0x46, 0x9c, 0xc1, 0x8e, 0xbf,
+    0x4e, 0x71, 0x5f, 0xea, 0xe5, 0x0c, 0x9a, 0x41, 0xc8, 0x94,
+    0xcc, 0xf1, 0x73, 0x06, 0x30, 0x54, 0x76, 0x23, 0xb7, 0x22,
+    0x7a, 0x8e, 0xe6, 0x42, 0xa1, 0xa0, 0x32, 0x12, 0xe9, 0x08,
+    0x1c, 0x46, 0x79, 0x0c, 0x82, 0x7a, 0x95, 0x79, 0xbf, 0x83,
+    0x80, 0xeb, 0xab, 0x3d, 0x32, 0xc5, 0xde, 0x62, 0xeb, 0x90,
+    0x29, 0x73, 0x05, 0xc8, 0x0a, 0xb1, 0x51, 0xf1, 0x23, 0xdd,
+    0x1e, 0xf5, 0x02, 0x3e, 0x74, 0xbc, 0x24, 0x0c, 0x60, 0x36,
+    0x2a, 0x28, 0x4d, 0xe6, 0x86, 0x98, 0x7c, 0xd9, 0xe1, 0xac,
+    0x21, 0x33, 0xaa, 0xa9, 0x8b, 0xb6, 0x8a, 0x1b, 0xf7, 0x54,
+    0x14, 0xf3, 0x0d, 0x4f, 0xcd, 0x7c, 0xf5, 0xc2, 0x6d, 0xc2,
+    0xf0, 0xe2, 0xfc, 0x63, 0x1e, 0xa6, 0xa9, 0xa9, 0xd9, 0x73,
+    0x2a, 0xd5, 0x0a, 0x38, 0xd8, 0xc0, 0xb7, 0xe1, 0x51, 0xe4,
+    0x23, 0x37, 0xf7, 0x85, 0x66, 0x0e, 0x3f, 0x1a, 0x8c, 0xcf,
+    0x12, 0xa2, 0x47, 0x6f, 0x73, 0x91, 0x21, 0xe3, 0x93, 0x6b,
+    0x74, 0x4f, 0xc5, 0xa1, 0xe7, 0x32, 0xf7, 0x86, 0xdd, 0x1a,
+    0x6e, 0x96, 0xda, 0x32, 0x1d, 0xdd, 0xfa, 0x42, 0xd5, 0xd4,
+    0xfd, 0xae, 0x7a, 0xa1, 0xed, 0x3d, 0x79, 0xfe, 0x88, 0x84,
+    0x43, 0xa7, 0xec, 0xf3, 0x7a, 0x13, 0xaa, 0xa1, 0x82, 0x02,
+    0x83, 0x19, 0x43, 0x0a, 0x46, 0x78, 0x07, 0xd9, 0x4d, 0xff,
+    0xac, 0x67, 0xd6, 0x29, 0x89, 0xfe, 0x2b, 0xab, 0x5f, 0x9a,
+    0x87, 0x99, 0x80, 0xaf, 0x70, 0x4a, 0x6a, 0xb9, 0x5a, 0xc2,
+    0xac, 0x7f, 0xa2, 0xc7, 0xad, 0xe2, 0x1f, 0xec, 0xc5, 0x12,
+    0x17, 0x08, 0x87, 0x8f, 0x20, 0x95, 0xbe, 0xaf, 0x62, 0x2c,
+    0xc2, 0x3f, 0x89, 0x56, 0xd8, 0x50, 0x96, 0x97, 0x72, 0xe2,
+    0x92, 0xe1, 0x2a, 0xd8, 0x84, 0x9f, 0x31, 0xe3, 0x06, 0xd8,
+    0xe5, 0x91, 0x63, 0x19, 0xe1, 0x27, 0xad, 0xe2, 0xf2, 0x0a,
+    0x5e, 0x78, 0x8b, 0x1b, 0x13, 0x31, 0x4b, 0xbd, 0x77, 0xb2,
+    0xd6, 0x5c, 0x92, 0x81, 0x50, 0x02, 0x37, 0xd2, 0xe6, 0xeb,
+    0x66, 0x6b, 0xaa, 0xfc, 0xcd, 0x54, 0x5d, 0xb8, 0x03, 0x87,
+    0xe8, 0xfa, 0xb2, 0xde, 0xcb, 0xf8, 0x6e, 0x58, 0xde, 0xcb,
+    0x09, 0x54, 0x8a, 0x9f, 0x46, 0xa3, 0x7e, 0x8d, 0x15, 0xff,
+    0x1b, 0x0d, 0x89, 0xc4, 0x1a, 0x21, 0x31, 0x5e, 0xed, 0x0b,
+    0x67, 0x3c, 0x70, 0xed, 0x92, 0x48, 0xef, 0xec, 0xf0, 0x77,
+    0xc2, 0x79, 0x6c, 0x06, 0x09, 0xaa, 0xab, 0xf6, 0x4c, 0xcd,
+    0xfa, 0x7e, 0x4a, 0x88, 0xdc, 0xa8, 0x9b, 0xd3, 0x69, 0x94,
+    0x88, 0x09, 0x1d, 0x30, 0x43, 0x9e, 0x2c, 0xcb, 0x01, 0x1d,
+    0x4a, 0x3b, 0x04, 0xec, 0x0e, 0xb1, 0xde, 0x09, 0xad, 0x29,
+    0x02, 0x82, 0x01, 0xe1, 0x00, 0x9f, 0x02, 0x13, 0x7a, 0xd0,
+    0xa9, 0x8a, 0x7a, 0xa0, 0x05, 0xbb, 0x44, 0x6f, 0xaf, 0xf7,
+    0xe3, 0xd4, 0x35, 0xef, 0x73, 0x39, 0xd5, 0xe0, 0xa2, 0x0f,
+    0x1a, 0x25, 0xa8, 0xf7, 0xc2, 0xa5, 0xec, 0x57, 0xf8, 0x0d,
+    0x2a, 0xb6, 0x64, 0x03, 0x8c, 0x22, 0x0f, 0xe7, 0x98, 0xa1,
+    0x12, 0xfe, 0x24, 0xef, 0x61, 0x28, 0x9f, 0xa7, 0x22, 0x6b,
+    0x6d, 0xab, 0x8d, 0x7d, 0x2a, 0x8b, 0xae, 0x8b, 0xfd, 0xcb,
+    0xd5, 0x0b, 0x79, 0x1b, 0x89, 0xcb, 0x5b, 0x7a, 0x8c, 0xdc,
+    0xe8, 0x8d, 0xdd, 0x35, 0x9f, 0x06, 0x69, 0x64, 0x12, 0xeb,
+    0x46, 0x79, 0xdf, 0x82, 0x2c, 0x89, 0x75, 0x9e, 0x7a, 0xec,
+    0xad, 0xe5, 0x88, 0x31, 0xfa, 0x86, 0x93, 0xca, 0xf1, 0x2d,
+    0x9b, 0x62, 0x5a, 0xe9, 0x43, 0x09, 0xf3, 0x8c, 0xe5, 0xc7,
+    0xc0, 0xce, 0x86, 0xe7, 0xdb, 0xc7, 0x4d, 0x27, 0xd5, 0xee,
+    0x76, 0xce, 0x35, 0x30, 0x47, 0xef, 0x00, 0x1b, 0x69, 0x9a,
+    0x3f, 0xa5, 0x2a, 0xc9, 0x07, 0xab, 0x99, 0xba, 0x2a, 0xe7,
+    0xfb, 0xa9, 0x4e, 0xb9, 0xae, 0x2c, 0x50, 0xfc, 0x35, 0x49,
+    0xe6, 0x97, 0x78, 0x3c, 0xb1, 0x59, 0xd7, 0x1d, 0x4e, 0x4e,
+    0xea, 0xde, 0xa0, 0xd0, 0xc4, 0x1d, 0xb1, 0xd3, 0x53, 0x1e,
+    0xf9, 0xbf, 0xb3, 0x6a, 0x17, 0xb4, 0xda, 0xcc, 0x27, 0x19,
+    0xc6, 0x35, 0xe8, 0x28, 0xd3, 0xe3, 0x76, 0x3a, 0xdc, 0xd0,
+    0x75, 0xc8, 0xb4, 0x6c, 0xbe, 0x84, 0x2a, 0x45, 0xd1, 0x43,
+    0x22, 0x54, 0xd7, 0xc5, 0xd0, 0xd7, 0x73, 0x35, 0x6b, 0xa8,
+    0xfa, 0xad, 0x60, 0xc0, 0x64, 0xc1, 0x58, 0x89, 0x09, 0x81,
+    0x0a, 0x0b, 0xea, 0x33, 0x91, 0xb0, 0xef, 0x53, 0x50, 0x41,
+    0xae, 0xd9, 0xee, 0xbe, 0x9e, 0xf0, 0x0b, 0xa0, 0x7c, 0xbf,
+    0x3f, 0xc9, 0x4b, 0xe0, 0x48, 0xd8, 0x10, 0xd5, 0x2e, 0xce,
+    0xf0, 0x7c, 0xd8, 0x05, 0xde, 0x09, 0x7e, 0x8c, 0x63, 0x4c,
+    0xdb, 0x8b, 0x91, 0xcd, 0x7f, 0xb6, 0x6b, 0xad, 0xce, 0xb1,
+    0x17, 0x6c, 0xf7, 0x08, 0x0d, 0x7c, 0xda, 0x4f, 0x0a, 0x07,
+    0xd0, 0xae, 0x72, 0x3c, 0x67, 0x4a, 0x44, 0x54, 0x47, 0xce,
+    0xe1, 0x17, 0x07, 0x12, 0xde, 0x52, 0xef, 0xef, 0x4c, 0x2b,
+    0x42, 0x7d, 0x09, 0x80, 0x36, 0x34, 0xdc, 0x45, 0x6f, 0xb0,
+    0x2d, 0xab, 0xa0, 0x0c, 0x58, 0xae, 0x35, 0xd3, 0x9b, 0x37,
+    0xc1, 0x1d, 0xeb, 0xfe, 0xc3, 0x04, 0xc9, 0x1d, 0xe7, 0x3d,
+    0x16, 0x64, 0xed, 0xf5, 0xe8, 0xdf, 0x99, 0xa4, 0xfb, 0xad,
+    0x79, 0x88, 0xd5, 0x8c, 0x62, 0x33, 0x9e, 0x35, 0xa6, 0x7f,
+    0x9d, 0xb6, 0x1a, 0x40, 0x6d, 0xc3, 0x89, 0x5d, 0x7b, 0xe2,
+    0xc8, 0xd3, 0x16, 0x13, 0x07, 0x9a, 0x38, 0x22, 0x33, 0x03,
+    0xac, 0x70, 0x3e, 0xce, 0x32, 0x56, 0x0b, 0x58, 0x56, 0xb8,
+    0xe9, 0xd8, 0x42, 0x35, 0x6c, 0xb9, 0x02, 0xb3, 0x64, 0xeb,
+    0xaa, 0x09, 0x3f, 0xac, 0x66, 0x08, 0xb4, 0x5f, 0x3e, 0xb4,
+    0xec, 0x39, 0xb1, 0x99, 0xe4, 0x5d, 0x1d, 0x32, 0x14, 0xc1,
+    0x48, 0x8f, 0x6c, 0x65, 0x87, 0x34, 0x50, 0xa4, 0xf4, 0x9b,
+    0x5b, 0x2e, 0xb5, 0x79, 0x0d, 0x11, 0x62, 0xa4, 0x35, 0x9c,
+    0x6f, 0x92, 0xd0, 0x68, 0x07, 0xdd, 0x69, 0x85, 0x48, 0xe3,
+    0x5d, 0x10, 0x34, 0xaf, 0xea, 0x41, 0x72, 0x5a, 0x71, 0x00,
+    0xf8, 0xe6, 0x47, 0x7f, 0xa0, 0x6f, 0x91, 0x96, 0x40, 0x00,
+    0x40, 0x70, 0xfb, 0x63, 0xcf, 0xc9, 0x36, 0x04, 0x1c, 0x3b,
+    0x11, 0x08, 0x29, 0x81, 0x9f
+};
+
+static unsigned char test15360[] = {
+    0x30, 0x82, 0x21, 0xe8, 0x02, 0x01, 0x00, 0x02, 0x82, 0x07,
+    0x81, 0x00, 0xad, 0x3f, 0xaa, 0xdc, 0x8c, 0x85, 0xcb, 0x60,
+    0xd2, 0xf5, 0x30, 0xa1, 0x0f, 0x26, 0xec, 0xdf, 0xfc, 0x91,
+    0x39, 0xbd, 0x3e, 0x8f, 0x99, 0x64, 0x1e, 0x51, 0xd2, 0x27,
+    0x5e, 0x76, 0xcd, 0x86, 0x33, 0x07, 0xf9, 0xbd, 0x3b, 0x06,
+    0xc3, 0x3c, 0x85, 0xcb, 0x7e, 0x91, 0x14, 0xb0, 0x0b, 0x77,
+    0x22, 0x30, 0x71, 0xb8, 0xbb, 0x74, 0x30, 0x33, 0x35, 0x56,
+    0x34, 0x47, 0x10, 0x8f, 0x88, 0xe2, 0x6f, 0xdc, 0x3b, 0xe9,
+    0x58, 0x9d, 0x0c, 0xdc, 0x8f, 0x70, 0x41, 0x7a, 0x12, 0xd2,
+    0x9a, 0x35, 0xbe, 0x0a, 0x57, 0x13, 0x0c, 0xe9, 0xbf, 0x77,
+    0x54, 0x00, 0x74, 0xb7, 0x1a, 0x3e, 0xa7, 0xe9, 0xb6, 0xe7,
+    0x4f, 0x1e, 0xa4, 0xc0, 0x7c, 0x4c, 0x66, 0xc5, 0xce, 0xad,
+    0x96, 0x1b, 0xe2, 0x1a, 0xf1, 0x3d, 0x8b, 0x50, 0xcf, 0xe2,
+    0x15, 0x21, 0x6d, 0x83, 0x95, 0x00, 0xee, 0x97, 0xc4, 0xae,
+    0xc9, 0x38, 0x62, 0x6c, 0xb2, 0xe7, 0x7f, 0x15, 0x0a, 0xab,
+    0x86, 0xb9, 0xd9, 0x8a, 0xf8, 0xeb, 0x88, 0x5d, 0xdc, 0x0c,
+    0x1e, 0xc5, 0xe6, 0xa1, 0x7b, 0xbf, 0xf1, 0x02, 0xe3, 0xad,
+    0xf8, 0xed, 0x17, 0x9f, 0x83, 0x11, 0x31, 0x3b, 0xad, 0xb4,
+    0xf9, 0x8d, 0x1d, 0x56, 0x9b, 0xac, 0x68, 0x55, 0x0a, 0x74,
+    0x20, 0xee, 0x57, 0xe7, 0x1c, 0x6d, 0x05, 0xa1, 0x4e, 0xa5,
+    0x11, 0x99, 0xb4, 0x86, 0xdb, 0x58, 0xe7, 0xf6, 0xb6, 0x4f,
+    0x92, 0x58, 0x57, 0x9b, 0x74, 0x04, 0xe5, 0xd1, 0x1d, 0x7c,
+    0x4b, 0xb8, 0x1f, 0x5d, 0x0e, 0x93, 0xee, 0x44, 0x18, 0xb6,
+    0x58, 0x0e, 0xa1, 0x0b, 0x8e, 0x2e, 0x99, 0x4c, 0x72, 0x91,
+    0xfa, 0xfa, 0xe2, 0x22, 0x05, 0x5d, 0x2b, 0x2d, 0xd8, 0x60,
+    0xd5, 0x1b, 0x08, 0x56, 0x2b, 0xb5, 0x21, 0xdb, 0x1a, 0xe6,
+    0xa8, 0x39, 0xa2, 0xf4, 0x58, 0xcb, 0xd2, 0xf9, 0xce, 0xc0,
+    0x1e, 0x1b, 0xf9, 0xa7, 0x37, 0xca, 0xa3, 0x77, 0x6e, 0xb1,
+    0xaf, 0x33, 0xb5, 0x6d, 0x5f, 0x33, 0x2e, 0x1a, 0x34, 0xdb,
+    0x42, 0xbe, 0x5f, 0xf9, 0x09, 0xb7, 0x9f, 0xd4, 0x09, 0xfb,
+    0x87, 0x13, 0x3c, 0xe2, 0x27, 0xb8, 0xf3, 0x1d, 0x7e, 0x92,
+    0xdd, 0x87, 0x86, 0x55, 0x69, 0x9b, 0x55, 0xcd, 0xef, 0x7a,
+    0x71, 0x5d, 0x81, 0x3a, 0xd9, 0xf7, 0x7f, 0xde, 0xe0, 0x92,
+    0xd9, 0x78, 0x0f, 0x1d, 0x43, 0xb1, 0x1e, 0x29, 0xc1, 0x49,
+    0xb6, 0x5e, 0x85, 0x83, 0xd9, 0x04, 0xfd, 0x79, 0xd8, 0x47,
+    0x03, 0x2e, 0x85, 0x19, 0xfd, 0x63, 0xe7, 0xa4, 0x8b, 0xc0,
+    0x94, 0x0e, 0xb7, 0x54, 0x97, 0xd6, 0x44, 0x5d, 0x63, 0x12,
+    0xff, 0xdd, 0xde, 0x2c, 0x00, 0x0e, 0xc9, 0xca, 0x7e, 0xa2,
+    0x65, 0x25, 0xb0, 0x1d, 0xa9, 0x20, 0x4f, 0xdd, 0xea, 0x3a,
+    0xb5, 0xe8, 0x0f, 0xf3, 0xb2, 0xb7, 0x00, 0x4a, 0xe8, 0xa4,
+    0x83, 0x49, 0xbd, 0x78, 0xdf, 0xac, 0x2c, 0x37, 0x81, 0xb3,
+    0xf3, 0xb7, 0x13, 0x93, 0x3e, 0xb2, 0x79, 0x55, 0xf2, 0xd8,
+    0x9c, 0xf7, 0xf2, 0xf1, 0xd5, 0x6c, 0x9c, 0xff, 0xec, 0xf4,
+    0xea, 0x08, 0x3c, 0x65, 0x35, 0xb7, 0x09, 0x03, 0x6d, 0x99,
+    0x1d, 0x5b, 0x73, 0x06, 0x61, 0xb4, 0xf0, 0xc5, 0xdb, 0x3e,
+    0xe0, 0x1d, 0xa8, 0x5b, 0x7a, 0x5b, 0x5b, 0x9c, 0x11, 0x75,
+    0x83, 0x1d, 0xf4, 0x73, 0x27, 0xf3, 0x79, 0xf2, 0x82, 0xd6,
+    0x28, 0x45, 0x58, 0x23, 0x6c, 0x29, 0xd3, 0x50, 0x51, 0x1b,
+    0x38, 0xef, 0x89, 0x90, 0x84, 0xa2, 0x4c, 0x35, 0x7b, 0x30,
+    0x5e, 0xbd, 0x1a, 0xd5, 0xdf, 0xcd, 0xcd, 0x74, 0x3f, 0x2e,
+    0x01, 0xea, 0x33, 0x07, 0x74, 0xfb, 0x86, 0x75, 0x20, 0x0e,
+    0x4f, 0xbf, 0x65, 0xd4, 0x15, 0x19, 0x6f, 0x8d, 0x37, 0xcd,
+    0xb6, 0x6f, 0x50, 0x9d, 0x5e, 0x04, 0x81, 0x7d, 0xec, 0xd6,
+    0xbb, 0x40, 0x1b, 0xe0, 0xf5, 0xd5, 0x86, 0x26, 0xc5, 0x41,
+    0x84, 0x0e, 0x3e, 0x73, 0xb7, 0xa4, 0xbe, 0x2a, 0xfe, 0xd7,
+    0xe4, 0x4d, 0x5c, 0x2d, 0x6a, 0x04, 0xe6, 0xdd, 0x28, 0xa0,
+    0x75, 0x4c, 0xe0, 0x23, 0x2c, 0xad, 0xec, 0xaa, 0x72, 0xfd,
+    0x03, 0xc0, 0x65, 0xfa, 0xc4, 0x3c, 0x25, 0x10, 0xae, 0x3f,
+    0x09, 0x96, 0x4e, 0xff, 0xfe, 0xc7, 0xe4, 0x9e, 0xec, 0xb5,
+    0x6e, 0xec, 0xf3, 0x7a, 0x83, 0x7a, 0x8b, 0xbb, 0x91, 0x8d,
+    0xab, 0x3c, 0x4d, 0x7f, 0x34, 0x77, 0xbe, 0x0c, 0x87, 0xf2,
+    0xc3, 0xd6, 0xcb, 0xcc, 0xfa, 0x1e, 0xaf, 0x21, 0x24, 0xe9,
+    0xaa, 0x89, 0x61, 0x0c, 0x7a, 0x1c, 0x7d, 0x00, 0x87, 0x69,
+    0x30, 0xa0, 0xb4, 0x3b, 0x96, 0x1c, 0x00, 0x14, 0x07, 0xb8,
+    0x3f, 0x59, 0x62, 0x3a, 0x3f, 0xfb, 0x68, 0xb8, 0x81, 0x7d,
+    0x4a, 0x9d, 0x1c, 0xa2, 0x07, 0xa3, 0xb1, 0x42, 0x7b, 0xfa,
+    0x9b, 0xbc, 0x94, 0x30, 0x7e, 0xea, 0xe7, 0x40, 0x7e, 0xd4,
+    0x0f, 0x33, 0x3b, 0x57, 0xda, 0x8b, 0x6d, 0x64, 0xd5, 0xe4,
+    0x91, 0x83, 0xf0, 0x3d, 0xae, 0x8b, 0x91, 0xf0, 0xcd, 0xb1,
+    0xa0, 0xe0, 0x0d, 0xe1, 0xbb, 0x22, 0x78, 0x1f, 0x3a, 0xe5,
+    0x53, 0x28, 0xf0, 0x35, 0xae, 0x71, 0xe6, 0xfd, 0x63, 0xb2,
+    0x9c, 0x3f, 0xdd, 0x95, 0x7b, 0xc4, 0xe9, 0x2f, 0xd9, 0x93,
+    0x3a, 0x10, 0x42, 0x1c, 0x90, 0xab, 0xfb, 0xd3, 0x02, 0xe9,
+    0x59, 0xbc, 0x53, 0x7e, 0xf3, 0xe1, 0x52, 0x15, 0xa6, 0x58,
+    0x9e, 0xc1, 0xa6, 0x0e, 0x2e, 0x35, 0x07, 0x3a, 0xc3, 0x1f,
+    0xaa, 0x58, 0xe7, 0xc6, 0x33, 0x6a, 0x39, 0x4b, 0x21, 0x15,
+    0x3d, 0x92, 0x4e, 0x5e, 0xf9, 0x01, 0xd6, 0x0f, 0x28, 0x61,
+    0x15, 0xdf, 0xed, 0x6f, 0x75, 0xc4, 0x8f, 0xcb, 0x16, 0x55,
+    0x09, 0xc7, 0x24, 0xb2, 0x0c, 0x49, 0x25, 0x8d, 0x5e, 0xf1,
+    0x0e, 0xe0, 0xe2, 0xc4, 0xcc, 0x1f, 0x4e, 0x60, 0x5c, 0x5e,
+    0xc6, 0x7f, 0x68, 0x7f, 0xdb, 0x1a, 0x01, 0x67, 0x07, 0xb1,
+    0x56, 0x93, 0xf2, 0x26, 0x81, 0xc0, 0x33, 0xb8, 0x48, 0xf9,
+    0x2c, 0x5c, 0x18, 0x29, 0xed, 0xe0, 0x6c, 0xa0, 0xac, 0xd2,
+    0x90, 0x4b, 0x52, 0x87, 0xbb, 0xb5, 0x05, 0xd8, 0x56, 0xc5,
+    0xb8, 0x8f, 0x3f, 0x49, 0x52, 0x9a, 0xa2, 0xd0, 0x40, 0x80,
+    0x5b, 0x16, 0x15, 0xbc, 0x74, 0x8e, 0x00, 0x10, 0xaf, 0xfb,
+    0x6d, 0xba, 0xcb, 0xbc, 0xe6, 0x13, 0x75, 0xce, 0x27, 0xae,
+    0x85, 0x57, 0x6c, 0xc0, 0x8a, 0x84, 0x6f, 0x34, 0x16, 0xd4,
+    0x35, 0xd2, 0xcc, 0x55, 0x00, 0xc1, 0xd8, 0x28, 0x2c, 0x9c,
+    0x84, 0x78, 0xbf, 0xf0, 0x3b, 0x0d, 0x9f, 0x81, 0xd4, 0xef,
+    0x99, 0x77, 0x53, 0xd2, 0x8e, 0x43, 0x52, 0xf0, 0x32, 0x7e,
+    0xba, 0xbf, 0xb6, 0x0e, 0x9d, 0x9b, 0x00, 0xd0, 0x50, 0x55,
+    0x67, 0x5a, 0x2c, 0x8b, 0x9b, 0x29, 0xfb, 0x41, 0x74, 0x4c,
+    0xb7, 0xd8, 0x98, 0xa2, 0xfb, 0x73, 0x07, 0x96, 0xef, 0xcd,
+    0x47, 0x13, 0x1d, 0xe2, 0xb1, 0xac, 0xf3, 0xcf, 0x47, 0x98,
+    0x7b, 0x6f, 0xf6, 0x32, 0x44, 0x41, 0x78, 0x09, 0x8e, 0x64,
+    0x0c, 0xbf, 0xe2, 0x0f, 0x8c, 0x44, 0x2f, 0x4e, 0x55, 0xe0,
+    0xc6, 0xfd, 0x05, 0x74, 0x18, 0x1a, 0xb9, 0xfa, 0xcb, 0xd3,
+    0xfa, 0x69, 0x50, 0x63, 0xce, 0x2b, 0xef, 0x92, 0x0f, 0x11,
+    0xd4, 0x9b, 0x53, 0x6c, 0xed, 0xc5, 0x0b, 0x7c, 0xbd, 0xa1,
+    0x5d, 0xdf, 0xab, 0xcf, 0xaa, 0x83, 0x5e, 0xa8, 0xc5, 0xfe,
+    0x91, 0x2b, 0x23, 0x1f, 0x39, 0x3d, 0x71, 0x74, 0xbf, 0xa2,
+    0xf1, 0xda, 0x2f, 0x29, 0x02, 0x9b, 0xea, 0x48, 0x2c, 0xaf,
+    0xe7, 0xa9, 0xf5, 0x68, 0xab, 0x8f, 0x18, 0xb9, 0x7b, 0x28,
+    0xf0, 0x92, 0xfb, 0x07, 0xd7, 0xbd, 0x43, 0xcd, 0x7f, 0xfc,
+    0xb9, 0x5f, 0x24, 0xf8, 0x48, 0x2e, 0xbe, 0x42, 0x87, 0x80,
+    0x38, 0x78, 0x9e, 0x8c, 0x52, 0x6d, 0xfa, 0x2e, 0x46, 0x35,
+    0x7a, 0x59, 0x88, 0xb9, 0x3e, 0xcb, 0x79, 0xb4, 0x8a, 0x9e,
+    0xd5, 0xd0, 0x30, 0x8c, 0xb2, 0x0c, 0x9d, 0x8d, 0x2d, 0x64,
+    0x0b, 0xf6, 0xeb, 0xf1, 0xde, 0xea, 0x74, 0xfc, 0xbc, 0x01,
+    0x18, 0x48, 0x4e, 0x35, 0x02, 0x83, 0x01, 0xb2, 0x50, 0xa0,
+    0x44, 0x19, 0x30, 0x00, 0x12, 0x4a, 0xa0, 0x6d, 0x6b, 0x8b,
+    0xf1, 0xce, 0xda, 0x2e, 0x16, 0x35, 0x52, 0x26, 0xf9, 0xbe,
+    0xb1, 0x37, 0xfc, 0x0a, 0x8b, 0x6f, 0x06, 0x11, 0x7b, 0xf7,
+    0xa8, 0x40, 0xbd, 0x8d, 0x94, 0xa4, 0xa2, 0xe0, 0xb6, 0xdf,
+    0x62, 0xc0, 0x6f, 0xb3, 0x5d, 0x84, 0xb9, 0xaa, 0x2f, 0xc1,
+    0x3b, 0xcb, 0x20, 0xc6, 0x68, 0x69, 0x15, 0x74, 0xbc, 0xdb,
+    0x43, 0x9c, 0x4a, 0xfc, 0x72, 0xc1, 0xf5, 0x87, 0x80, 0xe8,
+    0x6c, 0xd5, 0xc1, 0x2e, 0x34, 0x5e, 0x96, 0x76, 0x08, 0x3e,
+    0x45, 0xe4, 0xa0, 0x4a, 0x7a, 0xc1, 0x67, 0x38, 0xf2, 0x31,
+    0x1f, 0x7b, 0x0f, 0x54, 0xbd, 0x0d, 0x1f, 0x9e, 0x8e, 0x99,
+    0x8b, 0x58, 0xd9, 0x94, 0x87, 0xaa, 0x8b, 0x82, 0x5d, 0x5e,
+    0xe8, 0x50, 0xf4, 0xf2, 0xc7, 0xe9, 0x85, 0x6b, 0xd2, 0xef,
+    0x13, 0xc1, 0xed, 0x57, 0x2a, 0xc5, 0xd6, 0x5d, 0xa4, 0x3b,
+    0x29, 0xba, 0xab, 0x1b, 0xaa, 0x21, 0x41, 0xe9, 0xdc, 0x47,
+    0x88, 0xef, 0x0c, 0xfc, 0xb2, 0xdc, 0xf7, 0xdb, 0x55, 0x4d,
+    0x70, 0xc7, 0xe2, 0x8a, 0x8a, 0xe1, 0xde, 0xcf, 0xe5, 0xca,
+    0x23, 0x36, 0x29, 0xe5, 0xfc, 0x54, 0x66, 0xda, 0xe9, 0xab,
+    0x58, 0x20, 0xb2, 0x8e, 0xb2, 0x7d, 0x5d, 0xb8, 0xc7, 0x6c,
+    0x48, 0x53, 0x2b, 0x47, 0xe0, 0x12, 0x00, 0x0e, 0xfe, 0xa5,
+    0x93, 0x34, 0xf9, 0x3e, 0xa6, 0x3f, 0x56, 0xaa, 0x43, 0x65,
+    0xbb, 0x5a, 0x70, 0x3e, 0x62, 0xac, 0x3f, 0x5b, 0x90, 0x02,
+    0x50, 0x5d, 0x05, 0xa8, 0xd5, 0x67, 0x1a, 0x62, 0xec, 0xd4,
+    0xde, 0x29, 0x04, 0xac, 0x6d, 0x15, 0x5d, 0xa0, 0xec, 0xf2,
+    0x57, 0x13, 0x0e, 0x17, 0x96, 0x0c, 0x32, 0x6a, 0xc5, 0xe0,
+    0xa8, 0xff, 0x85, 0xa4, 0xa3, 0xe3, 0x0e, 0x35, 0x5d, 0xd1,
+    0x28, 0x84, 0xaa, 0xc4, 0x84, 0xcd, 0x25, 0x63, 0x85, 0x82,
+    0x3e, 0x12, 0x30, 0x17, 0x57, 0x45, 0xb8, 0xb4, 0x34, 0x01,
+    0x3a, 0xa2, 0x77, 0x61, 0xc8, 0x3d, 0x1f, 0xc5, 0x0e, 0x4a,
+    0xbb, 0xf6, 0xa0, 0x5d, 0x79, 0x4b, 0xc8, 0xf3, 0x9c, 0x87,
+    0x05, 0x2f, 0xea, 0x25, 0x28, 0x91, 0x69, 0x77, 0x7c, 0xba,
+    0xea, 0x4a, 0x75, 0x2e, 0x2b, 0x17, 0x83, 0x50, 0x32, 0x43,
+    0x4f, 0xcd, 0xf1, 0x77, 0xb1, 0x22, 0x0a, 0x8b, 0x69, 0x58,
+    0x09, 0x35, 0x07, 0x6d, 0x61, 0x4a, 0x8d, 0x18, 0x65, 0x6e,
+    0x9b, 0x62, 0x07, 0xd0, 0x6a, 0x92, 0x39, 0x05, 0x80, 0x14,
+    0xfa, 0x1c, 0x93, 0x84, 0x0c, 0xb5, 0x8c, 0x41, 0x91, 0x4e,
+    0x48, 0xf0, 0xf2, 0xba, 0x1d, 0x73, 0x2f, 0x1e, 0xa1, 0x55,
+    0xc3, 0x02, 0x8c, 0xb1, 0xf2, 0x37, 0xa6, 0x9a, 0x6b, 0xcd,
+    0x45, 0x2e, 0x08, 0x90, 0x26, 0x63, 0x91, 0xff, 0x22, 0x5e,
+    0xcd, 0xae, 0x9b, 0x19, 0x1e, 0x10, 0x62, 0x4e, 0x1f, 0x2d,
+    0x81, 0x69, 0x4f, 0x41, 0xe5, 0x94, 0xff, 0x7e, 0xcc, 0x15,
+    0x36, 0x1e, 0x29, 0x59, 0x37, 0xe7, 0x64, 0x40, 0x17, 0x1a,
+    0x32, 0xba, 0x01, 0x26, 0x30, 0x80, 0x60, 0x07, 0x86, 0x6e,
+    0xd4, 0xb3, 0xe2, 0x44, 0x16, 0x33, 0xf2, 0x4c, 0x84, 0x0e,
+    0xb1, 0x4a, 0xc7, 0x92, 0xa6, 0xa3, 0x42, 0x36, 0x05, 0x3e,
+    0x74, 0xa8, 0xb1, 0xc5, 0x63, 0x59, 0x0d, 0x1e, 0x36, 0x45,
+    0x2b, 0x36, 0x5e, 0xca, 0xab, 0x97, 0x49, 0xd3, 0xab, 0xae,
+    0x63, 0x0a, 0xd1, 0x03, 0x57, 0x88, 0xa4, 0xa4, 0x3c, 0xda,
+    0x15, 0x49, 0x1a, 0x5d, 0xe6, 0x5e, 0xb9, 0x82, 0x23, 0xc0,
+    0x83, 0x96, 0xfe, 0x38, 0x0b, 0x80, 0x0e, 0xde, 0x22, 0xeb,
+    0x5d, 0xe4, 0x56, 0x32, 0xbe, 0xe0, 0xc0, 0x6e, 0x69, 0x63,
+    0x27, 0x4e, 0x00, 0x58, 0x80, 0x70, 0xd9, 0xcc, 0x4e, 0xae,
+    0x6c, 0x5e, 0x6a, 0x43, 0x81, 0xfd, 0x45, 0xb2, 0xa4, 0x6c,
+    0xf0, 0x9c, 0x66, 0x5c, 0x7d, 0x5c, 0x78, 0x55, 0x33, 0x4b,
+    0x3c, 0x3b, 0x1d, 0x18, 0x58, 0x79, 0x6a, 0x02, 0xec, 0xce,
+    0x53, 0x69, 0xc0, 0x17, 0xed, 0x57, 0xaf, 0x71, 0x5b, 0x42,
+    0x1b, 0x49, 0xd8, 0xe8, 0x96, 0x80, 0xb6, 0x48, 0x1b, 0x7c,
+    0xf8, 0x74, 0x1c, 0xb1, 0xc4, 0x10, 0xb7, 0xf4, 0x97, 0x7e,
+    0x6b, 0x8f, 0x54, 0xba, 0x37, 0xb9, 0x35, 0x9e, 0x7b, 0x17,
+    0x16, 0x9b, 0x89, 0x39, 0xae, 0x4f, 0x2e, 0x18, 0x65, 0xb4,
+    0x76, 0x20, 0x9a, 0x58, 0xe2, 0x57, 0x6e, 0x1c, 0x3f, 0x8e,
+    0x9a, 0xbb, 0xd8, 0xfc, 0x4c, 0xd6, 0x2d, 0xc1, 0xa6, 0x46,
+    0xac, 0x13, 0x1e, 0xa7, 0xf7, 0x1d, 0x28, 0x3a, 0xf4, 0xd6,
+    0x48, 0xfb, 0xe5, 0xb3, 0x84, 0x94, 0x47, 0x92, 0xae, 0x9a,
+    0x58, 0xc5, 0xac, 0x23, 0x1b, 0xb5, 0xcd, 0x96, 0xd2, 0x5e,
+    0xb2, 0x41, 0xfc, 0x9a, 0xae, 0x19, 0xf1, 0x7b, 0x4b, 0x53,
+    0x1b, 0xfa, 0xa5, 0x0c, 0x49, 0x6d, 0xff, 0xf4, 0x51, 0x88,
+    0x19, 0x04, 0xd9, 0x85, 0x8e, 0xe2, 0x3a, 0x62, 0x31, 0x5c,
+    0x6e, 0xe8, 0x4d, 0x04, 0x1d, 0xd8, 0xc2, 0x7b, 0x51, 0xe7,
+    0x59, 0xbc, 0x85, 0x5c, 0xc4, 0xcc, 0xad, 0xcb, 0x93, 0x69,
+    0x18, 0xe4, 0x71, 0x9e, 0x63, 0x33, 0x99, 0xb6, 0x3b, 0x23,
+    0x11, 0x17, 0x7a, 0x3d, 0x6f, 0xb9, 0x6b, 0xf1, 0xf2, 0xa7,
+    0x03, 0xfd, 0xf0, 0xcd, 0x5b, 0xb5, 0xda, 0x9a, 0xd9, 0x95,
+    0x02, 0x76, 0xd8, 0x38, 0xd3, 0xbd, 0xa0, 0x4a, 0x9a, 0xab,
+    0x70, 0xde, 0xc6, 0xf9, 0xa5, 0x19, 0x9c, 0xc4, 0xf9, 0x07,
+    0x4d, 0xea, 0x15, 0xc2, 0x91, 0x4d, 0x54, 0xa9, 0x2c, 0xca,
+    0xdf, 0xaa, 0xd1, 0xc4, 0xc0, 0x18, 0x77, 0x28, 0x2a, 0x2c,
+    0xc3, 0x7c, 0x26, 0xbd, 0xd8, 0x0d, 0x51, 0xa1, 0x4d, 0xad,
+    0x76, 0x76, 0xaa, 0xa9, 0x45, 0x82, 0x4f, 0x76, 0xfb, 0x1a,
+    0xd3, 0x71, 0x3c, 0x55, 0xa2, 0x5c, 0xe0, 0xd6, 0xda, 0x35,
+    0xbe, 0x25, 0x23, 0x26, 0x51, 0xc6, 0xb4, 0xf3, 0x3e, 0x2c,
+    0x54, 0x09, 0xc7, 0x6f, 0xa5, 0x08, 0x81, 0xba, 0x75, 0xda,
+    0xcb, 0x4d, 0x05, 0xdd, 0xca, 0x93, 0x48, 0x30, 0xe8, 0x4a,
+    0x1f, 0xfd, 0x02, 0x03, 0x01, 0x00, 0x01, 0x02, 0x82, 0x07,
+    0x80, 0x25, 0x2f, 0xbc, 0x49, 0xf8, 0xb3, 0xa3, 0x32, 0xd6,
+    0x35, 0x20, 0xca, 0x01, 0x49, 0x96, 0xa0, 0x81, 0x42, 0xde,
+    0xc4, 0xdb, 0x0f, 0xd1, 0x99, 0xe6, 0xd4, 0x23, 0x2a, 0xa6,
+    0x21, 0x13, 0xfe, 0x51, 0x27, 0xce, 0x18, 0x2a, 0xfa, 0x49,
+    0x9f, 0xcd, 0x0c, 0x1f, 0xcf, 0x9e, 0x44, 0x27, 0x41, 0xdc,
+    0x09, 0xcf, 0xef, 0x19, 0xf5, 0x57, 0x7f, 0x36, 0x5c, 0x99,
+    0x7e, 0x03, 0x74, 0xfb, 0xa9, 0xb6, 0xde, 0xeb, 0xd1, 0x2b,
+    0x5f, 0x12, 0x6a, 0xa9, 0x33, 0x2c, 0x2a, 0xba, 0xad, 0x8f,
+    0xc2, 0x27, 0x57, 0x6a, 0xd7, 0x40, 0xf7, 0x4f, 0x4c, 0x9a,
+    0xb0, 0x3a, 0x5d, 0x2e, 0xf9, 0xf1, 0xea, 0xbd, 0x82, 0xaa,
+    0xbd, 0xe6, 0x19, 0x16, 0xd5, 0x03, 0x5e, 0x43, 0xfd, 0x88,
+    0x71, 0xd5, 0xb7, 0x78, 0xbe, 0x80, 0x0f, 0xc9, 0x7f, 0x3a,
+    0x8f, 0xe1, 0x44, 0xd4, 0x0f, 0xce, 0x26, 0xaf, 0x65, 0xe0,
+    0xf5, 0x04, 0x53, 0x56, 0x97, 0x4f, 0xf4, 0xc1, 0x44, 0x8d,
+    0xf7, 0x88, 0x55, 0x47, 0x16, 0xaf, 0x3f, 0x8e, 0x42, 0xdf,
+    0xbc, 0x14, 0xc3, 0xe6, 0x9f, 0x0d, 0x69, 0x54, 0x5b, 0x7c,
+    0x49, 0xcf, 0xbf, 0x42, 0x4f, 0xc7, 0x64, 0x8a, 0xe5, 0x84,
+    0x87, 0x20, 0x9b, 0xfd, 0x70, 0x25, 0x38, 0xd3, 0xb4, 0x97,
+    0x78, 0xf1, 0x4f, 0x3f, 0x0f, 0xbb, 0x9c, 0xa3, 0x17, 0xd5,
+    0x4e, 0x4b, 0xac, 0x82, 0x9a, 0x73, 0xb7, 0xc5, 0xec, 0x10,
+    0x7a, 0x7b, 0xdb, 0x77, 0x2c, 0xb1, 0xf3, 0x8f, 0xc3, 0xa5,
+    0x31, 0x11, 0x32, 0x55, 0x35, 0xb5, 0x77, 0xd2, 0x62, 0x19,
+    0x46, 0x92, 0x94, 0xbb, 0x61, 0x0f, 0x30, 0x94, 0x8a, 0xf6,
+    0xf7, 0x30, 0xe0, 0xa2, 0x8c, 0x1b, 0xff, 0x8c, 0x29, 0x44,
+    0xb4, 0xb7, 0xb6, 0x5f, 0x4d, 0x52, 0xc6, 0x07, 0xe1, 0x28,
+    0x8c, 0xae, 0x88, 0x8a, 0x22, 0xbd, 0xd7, 0x36, 0xe4, 0x8f,
+    0xd1, 0xeb, 0x65, 0x54, 0x19, 0x5f, 0xba, 0xfb, 0xfc, 0x91,
+    0xa1, 0xa4, 0xb8, 0xa4, 0x2d, 0x85, 0x20, 0xc4, 0xe5, 0xa7,
+    0x4e, 0xdb, 0xa4, 0xc5, 0xcc, 0x2f, 0x37, 0x41, 0x29, 0x47,
+    0x15, 0xff, 0x04, 0x80, 0x08, 0x37, 0xce, 0xc5, 0xe3, 0x5a,
+    0x3f, 0x83, 0xbb, 0x03, 0x9e, 0xfe, 0xec, 0xe4, 0x11, 0x41,
+    0x12, 0x13, 0xf2, 0x00, 0xe5, 0x1a, 0x02, 0x49, 0xeb, 0xdb,
+    0x57, 0xe4, 0xce, 0xa0, 0x3f, 0xfd, 0x3c, 0x73, 0x2b, 0x92,
+    0x44, 0x79, 0x9e, 0x12, 0x4f, 0xfa, 0xe4, 0x53, 0x62, 0xf2,
+    0xb0, 0xe2, 0x8a, 0xf0, 0x93, 0xa8, 0x1d, 0xee, 0x8d, 0x58,
+    0x7a, 0x4c, 0x29, 0x91, 0x29, 0xc1, 0xa4, 0xd5, 0xe6, 0x37,
+    0x1b, 0x75, 0x5b, 0xb6, 0x6b, 0x76, 0x2e, 0xcb, 0xbd, 0xa9,
+    0xbe, 0x4c, 0x2e, 0x21, 0xa6, 0x38, 0xde, 0x66, 0x2f, 0x51,
+    0xea, 0x4c, 0xba, 0x3f, 0x4a, 0xfe, 0x7a, 0x15, 0xb3, 0x72,
+    0x26, 0xba, 0xcf, 0x9e, 0x1b, 0x03, 0xa6, 0xaa, 0x65, 0x68,
+    0xd3, 0x8c, 0x15, 0x17, 0xe9, 0x11, 0x18, 0x3c, 0xb6, 0xf8,
+    0x02, 0x54, 0x98, 0x49, 0xfa, 0x35, 0x3c, 0xcd, 0xac, 0xc8,
+    0x2b, 0x1a, 0x63, 0x93, 0x03, 0x05, 0xa1, 0x41, 0xbe, 0x12,
+    0xca, 0x15, 0x47, 0x72, 0x63, 0x77, 0x26, 0xd0, 0xe7, 0x8f,
+    0x0d, 0x6e, 0x9c, 0xac, 0x07, 0xbe, 0x03, 0x22, 0xd0, 0x39,
+    0x63, 0x8d, 0x9b, 0xc6, 0x20, 0x81, 0xb5, 0x67, 0x15, 0xf6,
+    0xb0, 0xe3, 0xb9, 0x3e, 0xb7, 0x3f, 0x8f, 0x46, 0xc9, 0x74,
+    0x10, 0x1e, 0x53, 0xf1, 0xd4, 0x30, 0x4d, 0x6e, 0x72, 0xb4,
+    0x73, 0x1c, 0xb6, 0x79, 0x82, 0x60, 0x2e, 0x2a, 0x7d, 0x82,
+    0x95, 0xb5, 0x7c, 0x4d, 0x44, 0xcb, 0xd8, 0x8a, 0x17, 0xe8,
+    0x50, 0x29, 0xd8, 0x3a, 0xeb, 0x29, 0xc1, 0x83, 0x0f, 0xd9,
+    0xaf, 0xcc, 0xfa, 0xea, 0x3a, 0x47, 0x5d, 0x33, 0x1f, 0xe8,
+    0x33, 0x5b, 0x88, 0x8e, 0xdb, 0xd5, 0x1e, 0xaf, 0x4a, 0x5f,
+    0xc0, 0xfa, 0xf0, 0xb5, 0xa3, 0x5b, 0xda, 0x38, 0xb7, 0x38,
+    0x5e, 0xce, 0x81, 0x44, 0xf7, 0x66, 0x62, 0x64, 0x1d, 0x04,
+    0xf0, 0x8a, 0x4f, 0xa2, 0x80, 0x76, 0x83, 0x23, 0x89, 0x61,
+    0x6b, 0xc3, 0xb7, 0xee, 0xb5, 0x06, 0x33, 0xad, 0x63, 0x04,
+    0x78, 0xc9, 0xde, 0x32, 0xde, 0xcf, 0x18, 0xb9, 0xb0, 0x3b,
+    0xee, 0x0a, 0x58, 0xea, 0xad, 0xbc, 0x1e, 0x77, 0xa0, 0x93,
+    0xf7, 0xae, 0x9e, 0xb6, 0x31, 0x59, 0x8e, 0xb1, 0x03, 0x8f,
+    0xbb, 0xa4, 0x25, 0x0c, 0x2e, 0xd7, 0xe2, 0x62, 0x5c, 0xf1,
+    0x68, 0xe9, 0x76, 0xd7, 0x23, 0x14, 0x45, 0xaf, 0xcb, 0x09,
+    0x50, 0x05, 0x3f, 0xa0, 0xf9, 0xc3, 0x9e, 0x89, 0x05, 0xa8,
+    0x3b, 0x54, 0x55, 0x32, 0x74, 0x91, 0x46, 0xc1, 0x2c, 0x96,
+    0x7e, 0x60, 0xad, 0xfa, 0xbb, 0xcd, 0x09, 0x7b, 0x39, 0x10,
+    0x82, 0x8a, 0xc0, 0x5a, 0x0d, 0xab, 0xb3, 0x71, 0x45, 0xad,
+    0x39, 0x8e, 0xec, 0x4d, 0x91, 0x8d, 0xda, 0x8d, 0xfa, 0xb0,
+    0xad, 0x44, 0x3c, 0xc9, 0x21, 0x56, 0x22, 0xfc, 0xd3, 0xba,
+    0xb7, 0x3c, 0xe3, 0x8d, 0xda, 0x59, 0x34, 0x42, 0xdd, 0x04,
+    0x5b, 0x8e, 0x2b, 0xc7, 0x94, 0xd5, 0x42, 0xe0, 0x4a, 0x6f,
+    0x35, 0x5a, 0x27, 0x82, 0xd8, 0x82, 0x40, 0xee, 0x0f, 0xa6,
+    0xef, 0xe4, 0x70, 0xe3, 0x30, 0xb7, 0x2d, 0xd4, 0xbb, 0x27,
+    0xb2, 0xbf, 0xad, 0x49, 0x45, 0xbc, 0xeb, 0xbe, 0xb7, 0xd8,
+    0xe3, 0xb1, 0xf3, 0xeb, 0x41, 0x20, 0x9b, 0x21, 0x54, 0xc3,
+    0xa8, 0xaf, 0x9f, 0x20, 0x5c, 0x15, 0x8e, 0x25, 0xbc, 0xbc,
+    0x69, 0x91, 0xfe, 0xda, 0xad, 0xe5, 0x37, 0x7d, 0xb0, 0x51,
+    0x14, 0xae, 0x8f, 0x35, 0x15, 0x0a, 0xd4, 0x49, 0xa7, 0xd9,
+    0x20, 0x70, 0xa4, 0xf2, 0xf4, 0x24, 0x66, 0x52, 0xd1, 0xa5,
+    0x22, 0xea, 0x29, 0xd9, 0xb2, 0x82, 0x8d, 0x36, 0x66, 0x75,
+    0x6e, 0xd5, 0x8c, 0x54, 0x08, 0x21, 0xf2, 0xee, 0x78, 0xc7,
+    0x1f, 0x9c, 0x63, 0x5d, 0x88, 0x56, 0xd1, 0xa0, 0x80, 0x33,
+    0x60, 0x55, 0x23, 0x72, 0xd6, 0xb0, 0x1a, 0x50, 0xde, 0x25,
+    0x70, 0xb5, 0x77, 0x42, 0xf8, 0x19, 0x18, 0x15, 0x8f, 0xfd,
+    0x0c, 0x6a, 0x46, 0x1f, 0xbf, 0xe7, 0x60, 0x91, 0xe7, 0xbb,
+    0x25, 0x63, 0x66, 0xff, 0x11, 0x97, 0xbb, 0xfd, 0x3a, 0x17,
+    0x94, 0x77, 0xb4, 0xc5, 0x21, 0xba, 0x30, 0x94, 0xdd, 0xe5,
+    0xeb, 0x1d, 0x01, 0xba, 0xf9, 0xb0, 0x30, 0xdb, 0x11, 0x93,
+    0xb7, 0xfa, 0x79, 0xe8, 0x5e, 0xb3, 0x39, 0xf4, 0x51, 0x68,
+    0x31, 0xce, 0xe9, 0x0e, 0x93, 0xde, 0xff, 0xec, 0x27, 0xbd,
+    0xa6, 0x1a, 0x4c, 0xe0, 0x92, 0x5c, 0xd4, 0x07, 0xd2, 0xa1,
+    0xdd, 0x12, 0x83, 0xd2, 0x9a, 0x79, 0xb3, 0x3c, 0xfb, 0x07,
+    0xe3, 0x18, 0x1a, 0xa3, 0x24, 0x80, 0xb4, 0xcc, 0xf4, 0xc6,
+    0xa5, 0x6c, 0x25, 0xd7, 0x99, 0x1a, 0x30, 0xf0, 0xa9, 0xfc,
+    0x2e, 0x83, 0x44, 0xac, 0x64, 0x76, 0x34, 0xb0, 0xa6, 0x6f,
+    0x20, 0x5a, 0x14, 0xf2, 0x07, 0xa7, 0x6f, 0x4d, 0xab, 0xf5,
+    0xfc, 0x9d, 0xd6, 0x3e, 0x82, 0x48, 0x31, 0x25, 0x47, 0xc9,
+    0x0e, 0x1d, 0xdb, 0x98, 0x91, 0x56, 0xf5, 0xfe, 0x66, 0x8d,
+    0x48, 0xf0, 0x4c, 0x6c, 0x2c, 0x96, 0x54, 0x43, 0xec, 0x76,
+    0xf2, 0xe1, 0x76, 0x68, 0xc8, 0xe1, 0xde, 0x0d, 0x8e, 0x6f,
+    0xfc, 0x15, 0xd5, 0x93, 0x92, 0xfe, 0xca, 0x9b, 0x30, 0x61,
+    0x03, 0x0b, 0xca, 0x99, 0x2f, 0xd3, 0x15, 0xe9, 0x66, 0x81,
+    0xbd, 0x56, 0x17, 0x14, 0x4a, 0x2e, 0xf1, 0x34, 0x84, 0x55,
+    0x9d, 0xc0, 0x2b, 0xa7, 0x4a, 0xee, 0xf1, 0x7c, 0x67, 0xc7,
+    0xf3, 0x08, 0x1e, 0x6d, 0x6b, 0x5b, 0xcc, 0x81, 0x91, 0x5c,
+    0x94, 0x1a, 0x80, 0xda, 0x3a, 0xce, 0x36, 0x05, 0xb0, 0x7a,
+    0xe8, 0xd0, 0xb4, 0x57, 0x9c, 0xf9, 0xea, 0xf3, 0x26, 0x1d,
+    0xcb, 0xf8, 0xdd, 0x65, 0xaf, 0xf7, 0xcd, 0xf7, 0xa1, 0x3d,
+    0xfc, 0x9a, 0x3b, 0x08, 0xb9, 0xfa, 0x3c, 0x16, 0x49, 0x4a,
+    0xf1, 0xba, 0x4d, 0x31, 0xdd, 0x5e, 0x4f, 0x3d, 0x66, 0x22,
+    0x1b, 0x08, 0x91, 0x7d, 0xc6, 0xaf, 0x15, 0x07, 0x3c, 0xa1,
+    0xf7, 0x07, 0xfd, 0x3e, 0x90, 0xbb, 0x6f, 0x7a, 0xe9, 0xe1,
+    0x2f, 0xb9, 0xee, 0x91, 0x8e, 0x18, 0xcc, 0x8d, 0x1d, 0x22,
+    0xa0, 0xa0, 0x28, 0x25, 0xfc, 0xd4, 0x94, 0xd3, 0xaa, 0xcf,
+    0xce, 0xd0, 0x85, 0x82, 0x6f, 0x20, 0x9f, 0x55, 0x0e, 0xe5,
+    0x72, 0x0d, 0x17, 0x3e, 0x34, 0xc7, 0x2c, 0x0a, 0x14, 0x45,
+    0x27, 0xe2, 0xc7, 0x2f, 0x86, 0xa1, 0x55, 0x3e, 0x78, 0x03,
+    0xe9, 0x78, 0x2e, 0xd3, 0x99, 0xee, 0xa0, 0x14, 0xf8, 0xe3,
+    0x6c, 0xeb, 0x3f, 0x9a, 0xf3, 0x15, 0xce, 0xd5, 0x76, 0xf6,
+    0x3a, 0x86, 0x30, 0x76, 0xf9, 0x88, 0x30, 0xf5, 0x4a, 0x50,
+    0x58, 0x80, 0xe9, 0xd9, 0xd4, 0xb9, 0x34, 0x42, 0xa6, 0x4e,
+    0x9c, 0x1a, 0x07, 0x16, 0x9e, 0xee, 0xe4, 0x88, 0x04, 0x8e,
+    0xa8, 0xe7, 0xcd, 0xe8, 0x47, 0x1e, 0x54, 0x45, 0xd2, 0x65,
+    0xd8, 0xee, 0x4b, 0xbd, 0xd0, 0x85, 0xaa, 0xfb, 0x06, 0x53,
+    0x91, 0x7e, 0xe0, 0x59, 0x20, 0x57, 0x6a, 0xee, 0xd8, 0x9f,
+    0x77, 0x7f, 0xd7, 0x40, 0x63, 0xbb, 0x21, 0x75, 0x76, 0x11,
+    0x27, 0xcf, 0x05, 0xbb, 0x41, 0x30, 0x98, 0xbf, 0xdc, 0x5f,
+    0xc6, 0xa4, 0x1e, 0x30, 0xa1, 0x53, 0xd4, 0x36, 0x7f, 0x2e,
+    0x86, 0xd7, 0xd9, 0x95, 0x29, 0xd5, 0x46, 0x18, 0x60, 0x27,
+    0xe4, 0x6f, 0xcb, 0xf4, 0xe2, 0xfe, 0xff, 0x3e, 0xff, 0x15,
+    0xc6, 0xf2, 0x31, 0xf9, 0x2a, 0xc8, 0x05, 0x4e, 0x7c, 0x2e,
+    0x92, 0xc8, 0x41, 0x4f, 0x9e, 0x23, 0x21, 0x4d, 0x74, 0xf8,
+    0xc3, 0x44, 0x39, 0xc2, 0x69, 0x4b, 0x2e, 0x76, 0x5e, 0x44,
+    0x12, 0x65, 0x31, 0x98, 0xbe, 0x0a, 0x10, 0x11, 0x12, 0x2c,
+    0x67, 0x3d, 0x85, 0x2e, 0xd3, 0x97, 0x54, 0x1e, 0xb6, 0xad,
+    0xd9, 0x45, 0x11, 0x53, 0x04, 0x7c, 0x3f, 0xf4, 0xc9, 0xac,
+    0x82, 0x1b, 0x84, 0xf4, 0x20, 0x6b, 0xf1, 0xf5, 0x72, 0x04,
+    0x24, 0xc1, 0xd3, 0x42, 0x43, 0x52, 0x9d, 0x2d, 0xd3, 0x89,
+    0x8e, 0xd8, 0x28, 0xb9, 0xa2, 0xb4, 0xed, 0xbc, 0x76, 0x87,
+    0x55, 0x67, 0x39, 0xd9, 0xb7, 0x20, 0x6a, 0xec, 0xec, 0xb8,
+    0x14, 0x51, 0x91, 0xb9, 0x96, 0x0f, 0x7a, 0x3a, 0x12, 0xde,
+    0x14, 0x3b, 0x83, 0xcf, 0x41, 0x5b, 0x5d, 0xff, 0x33, 0x68,
+    0xdb, 0x53, 0x64, 0x93, 0xb1, 0xc3, 0x8a, 0x46, 0xa8, 0x44,
+    0x9c, 0x14, 0x12, 0x6c, 0x92, 0x6f, 0xae, 0xc3, 0x45, 0xb2,
+    0xa1, 0x67, 0x81, 0x3c, 0x22, 0x47, 0xfd, 0xa4, 0x7a, 0x79,
+    0xa8, 0x0a, 0xfb, 0x7a, 0x91, 0x6e, 0xe9, 0x53, 0xec, 0x98,
+    0x82, 0x57, 0xad, 0x05, 0x38, 0x55, 0xc1, 0xce, 0x3a, 0x04,
+    0x4d, 0x12, 0x72, 0x37, 0x4a, 0x36, 0x54, 0x3f, 0x67, 0x8a,
+    0xee, 0xd9, 0xf3, 0x80, 0xd5, 0xd7, 0xb8, 0xfc, 0x6e, 0x4f,
+    0x60, 0x2b, 0x5a, 0xa4, 0xc5, 0x05, 0xdb, 0xe5, 0x09, 0xe3,
+    0xeb, 0xa2, 0x51, 0x33, 0x30, 0x96, 0x46, 0x01, 0x26, 0x8f,
+    0x38, 0xc9, 0x97, 0x32, 0x2d, 0xb4, 0x59, 0x15, 0x15, 0x38,
+    0x66, 0x66, 0xfe, 0xcb, 0xee, 0xc1, 0xf6, 0x4e, 0xb7, 0xdf,
+    0x7b, 0x63, 0xe6, 0x3f, 0xe0, 0x1c, 0x97, 0xed, 0x86, 0xf3,
+    0xd2, 0xad, 0x42, 0x29, 0x20, 0x28, 0xa6, 0x59, 0x58, 0x7d,
+    0x8f, 0x5c, 0x43, 0x07, 0xd1, 0x7e, 0x83, 0xba, 0x9c, 0x1b,
+    0xfe, 0x17, 0x9e, 0xc8, 0x09, 0x63, 0x9a, 0x2d, 0x61, 0x33,
+    0x51, 0x46, 0x01, 0xa8, 0xe9, 0x43, 0x1e, 0x4e, 0xfe, 0x61,
+    0x1a, 0x28, 0x11, 0x65, 0x70, 0x43, 0x9f, 0xfc, 0x21, 0x1d,
+    0x76, 0x7b, 0x40, 0x08, 0x18, 0xd3, 0xe8, 0xc2, 0xe3, 0x8c,
+    0xe7, 0x27, 0xc2, 0xec, 0xb0, 0x08, 0x3e, 0x6b, 0x8f, 0x77,
+    0x6d, 0x9e, 0xa6, 0xab, 0xce, 0x9a, 0xf8, 0x8f, 0x77, 0xb3,
+    0xf4, 0xe8, 0x8b, 0xe7, 0xd9, 0xa1, 0x95, 0x40, 0x6b, 0xca,
+    0x21, 0x98, 0xff, 0xdc, 0xdc, 0x96, 0xc3, 0x08, 0x81, 0x72,
+    0x9a, 0xdd, 0xe2, 0xcf, 0x95, 0x99, 0xa6, 0xa3, 0x5e, 0x9e,
+    0x25, 0x60, 0xa3, 0xc3, 0x39, 0xf7, 0x54, 0x6c, 0xf2, 0x75,
+    0xa9, 0x38, 0x12, 0x38, 0x4d, 0x42, 0xe8, 0xec, 0x13, 0x25,
+    0xa0, 0xf8, 0x04, 0xb8, 0xf6, 0x66, 0x0b, 0x56, 0xe1, 0xfb,
+    0x26, 0x03, 0xe6, 0xa5, 0xf1, 0x4d, 0x7f, 0xa5, 0x9d, 0x58,
+    0x71, 0xd8, 0xc7, 0x6a, 0xbe, 0xdc, 0x90, 0x89, 0xb1, 0x36,
+    0xb4, 0xb6, 0xb4, 0xbb, 0xaf, 0x6e, 0x43, 0x10, 0xa6, 0xea,
+    0xee, 0x12, 0xcb, 0x08, 0x2c, 0x4e, 0x66, 0xf0, 0x1f, 0xf4,
+    0xbf, 0xd3, 0xeb, 0x63, 0x48, 0xd0, 0xbe, 0x8a, 0xed, 0x24,
+    0xdb, 0x0f, 0x23, 0x1d, 0x2e, 0x30, 0x97, 0x0f, 0xd8, 0xc6,
+    0x3b, 0x04, 0x2f, 0x33, 0x78, 0x20, 0x6e, 0xb1, 0x33, 0x03,
+    0x27, 0xac, 0x0a, 0x37, 0x15, 0x31, 0xef, 0x4d, 0x43, 0xcc,
+    0xa0, 0x49, 0x80, 0xe3, 0x8c, 0xc0, 0xf3, 0xf7, 0x2d, 0x37,
+    0x1d, 0xd3, 0x90, 0x5f, 0xad, 0x31, 0xb5, 0x95, 0x17, 0x69,
+    0x4b, 0xec, 0x84, 0x9d, 0x2b, 0x8d, 0xdd, 0x9b, 0x58, 0x04,
+    0xba, 0x28, 0x0e, 0x28, 0xc1, 0x54, 0x6c, 0xb0, 0x25, 0x0c,
+    0x4f, 0x98, 0x47, 0xf7, 0x93, 0xc2, 0xae, 0x2f, 0x6d, 0x29,
+    0x9c, 0x3d, 0xe3, 0xb5, 0xe3, 0x28, 0x43, 0x14, 0xe6, 0x92,
+    0x4c, 0x79, 0x90, 0x59, 0x75, 0x77, 0x56, 0x43, 0xda, 0xac,
+    0xa9, 0x42, 0xd7, 0xca, 0x95, 0x73, 0x26, 0x54, 0x1f, 0x3a,
+    0x8a, 0x37, 0x64, 0xd7, 0xcf, 0xe1, 0x31, 0xf7, 0x40, 0x59,
+    0xfd, 0xff, 0xea, 0x72, 0xfd, 0xc4, 0xde, 0xe3, 0x4d, 0x8a,
+    0xf5, 0x80, 0xc0, 0x61, 0x21, 0xbd, 0xbd, 0x8e, 0x42, 0xd5,
+    0x4c, 0xe4, 0xf4, 0x78, 0x31, 0xca, 0xf1, 0xec, 0x7c, 0x7b,
+    0x85, 0x6a, 0x05, 0x54, 0xbe, 0x38, 0x54, 0x2f, 0x1f, 0xda,
+    0x9f, 0x98, 0xe2, 0x79, 0xd7, 0x42, 0xca, 0xba, 0x85, 0x21,
+    0xe2, 0xcb, 0x2b, 0xae, 0x4a, 0x4e, 0x35, 0xfb, 0xcf, 0x3d,
+    0xc5, 0xae, 0x27, 0x30, 0xa9, 0x45, 0xe6, 0x3b, 0x43, 0x3e,
+    0x35, 0xe3, 0xf2, 0x0d, 0x53, 0x32, 0x2b, 0xf6, 0xe6, 0xc7,
+    0xd5, 0x02, 0x82, 0x03, 0xc1, 0x00, 0xd4, 0x04, 0x9b, 0xef,
+    0x5d, 0x58, 0xb0, 0xa3, 0xaa, 0xd2, 0xab, 0x53, 0x65, 0x99,
+    0x03, 0x49, 0x48, 0x4d, 0xf5, 0xdf, 0x5d, 0x16, 0x14, 0x11,
+    0x60, 0x45, 0x1b, 0xff, 0x4a, 0x60, 0x2b, 0x37, 0x63, 0xf6,
+    0xa7, 0x8a, 0xa8, 0xff, 0x08, 0x97, 0x08, 0xfc, 0xbb, 0xb3,
+    0x20, 0xa3, 0xcd, 0xd9, 0x58, 0xdb, 0x16, 0x1b, 0x88, 0x02,
+    0x1e, 0x0f, 0x43, 0x9b, 0x16, 0x7e, 0xbe, 0xb1, 0x9c, 0x13,
+    0x10, 0xdc, 0xa1, 0x56, 0xff, 0xa3, 0xff, 0x5e, 0x69, 0x30,
+    0xee, 0x7e, 0x76, 0x5f, 0x84, 0x94, 0xeb, 0x8f, 0x58, 0xf8,
+    0xcf, 0xbb, 0x99, 0x6e, 0xf0, 0xd8, 0x32, 0xf6, 0xce, 0x48,
+    0x6f, 0x7c, 0xc8, 0x8f, 0xd3, 0x86, 0x22, 0x49, 0x9f, 0xde,
+    0x11, 0x05, 0xa4, 0xdc, 0x92, 0xfb, 0x0f, 0xfa, 0x09, 0x4d,
+    0x17, 0x1a, 0xe2, 0x76, 0x67, 0x40, 0xa9, 0x5b, 0x1b, 0x54,
+    0x66, 0x48, 0xf7, 0xc3, 0x59, 0xd4, 0xcf, 0x55, 0xd0, 0x7f,
+    0x3b, 0xb0, 0xa2, 0xd8, 0xec, 0xb7, 0x88, 0xe7, 0xb0, 0x30,
+    0x72, 0x42, 0x65, 0xe2, 0x91, 0xa7, 0x9b, 0xf6, 0x07, 0x45,
+    0x52, 0x51, 0xaa, 0xbe, 0x32, 0x35, 0xe4, 0x88, 0x23, 0xe7,
+    0xcb, 0x3c, 0x1c, 0xfb, 0x0b, 0x96, 0xd5, 0xb3, 0x92, 0x86,
+    0x79, 0x5b, 0x47, 0x93, 0xd6, 0xbd, 0xc7, 0x21, 0x17, 0xd0,
+    0xc9, 0xc7, 0x69, 0x84, 0x80, 0x98, 0xaf, 0x2c, 0x63, 0xd1,
+    0xef, 0x6e, 0xca, 0x84, 0x30, 0x32, 0x83, 0x2d, 0x49, 0xbb,
+    0x1f, 0x2a, 0xfe, 0x40, 0x7c, 0x03, 0xd4, 0x45, 0xdc, 0xfe,
+    0x94, 0xf9, 0xe4, 0x36, 0x47, 0xfa, 0x7e, 0x2e, 0x93, 0x03,
+    0xf8, 0x15, 0xf9, 0xce, 0xc3, 0x5b, 0x76, 0x10, 0xec, 0x89,
+    0x8c, 0xce, 0x25, 0xa5, 0x77, 0x9a, 0xc5, 0x1e, 0xdd, 0x07,
+    0x1b, 0x5b, 0xac, 0x6f, 0xdb, 0x94, 0x85, 0xdf, 0x02, 0x22,
+    0xd1, 0xa9, 0x01, 0x8e, 0x63, 0xa1, 0xee, 0x94, 0x9c, 0xdb,
+    0xb4, 0x1a, 0x43, 0xe1, 0x1f, 0x4e, 0x2f, 0x68, 0x50, 0x0c,
+    0x2f, 0x5b, 0xc5, 0x1b, 0xe1, 0x8d, 0x4b, 0xe0, 0x63, 0x8d,
+    0x7a, 0x30, 0xbe, 0xb7, 0x2e, 0x02, 0xc6, 0x02, 0xac, 0xa8,
+    0xb8, 0x65, 0xc6, 0x28, 0xee, 0xe4, 0xec, 0x99, 0xa1, 0x9a,
+    0xfd, 0x1f, 0xb5, 0x85, 0x7a, 0x94, 0x16, 0xe2, 0xe7, 0x74,
+    0x06, 0x54, 0x1b, 0xd0, 0xaf, 0x58, 0x4e, 0x50, 0x7e, 0xd6,
+    0xe4, 0x31, 0xd2, 0x0c, 0xd7, 0x9d, 0xe2, 0x00, 0x30, 0xbe,
+    0x26, 0x30, 0x48, 0x99, 0x98, 0x58, 0x54, 0x5a, 0xc4, 0x0a,
+    0x6c, 0xa1, 0x06, 0xe9, 0x38, 0xe6, 0x79, 0x39, 0x00, 0x9e,
+    0xb6, 0xe3, 0xf7, 0x01, 0xcf, 0x2f, 0x82, 0x5e, 0xc3, 0x21,
+    0x1b, 0x79, 0x93, 0xb5, 0xe4, 0x39, 0x9d, 0x32, 0x9d, 0x72,
+    0xa4, 0xa8, 0xc9, 0x90, 0xce, 0xaf, 0xc0, 0x00, 0xad, 0x20,
+    0x87, 0x26, 0xc7, 0xd3, 0x5f, 0x2e, 0xf0, 0x5e, 0xf8, 0x8b,
+    0x85, 0xa3, 0xc6, 0x66, 0xd8, 0x2f, 0x86, 0xfe, 0x7d, 0x8d,
+    0x22, 0xa5, 0x6d, 0x68, 0x3e, 0x87, 0x6e, 0xf7, 0xf1, 0xf0,
+    0x07, 0xc4, 0xe3, 0xf1, 0x84, 0xc4, 0x93, 0x42, 0x06, 0x20,
+    0x80, 0x64, 0xb3, 0x52, 0x5c, 0xa5, 0xcf, 0xee, 0xfe, 0xa4,
+    0x09, 0x41, 0xbe, 0xaa, 0x78, 0x52, 0x76, 0x3f, 0xf7, 0xe8,
+    0xa1, 0x6b, 0x0a, 0xbc, 0x22, 0xbe, 0xdf, 0x72, 0x7b, 0xea,
+    0x90, 0x43, 0xee, 0xc2, 0x0b, 0x26, 0xdc, 0x02, 0x26, 0xa7,
+    0x50, 0x04, 0x7a, 0x06, 0x91, 0xae, 0x93, 0xd5, 0xd2, 0xc9,
+    0xa1, 0xe1, 0xfc, 0xb9, 0x8c, 0x94, 0xca, 0xa8, 0x1c, 0x2c,
+    0x57, 0x97, 0x3e, 0x50, 0xed, 0x93, 0x45, 0x7a, 0x2c, 0x59,
+    0x7b, 0x34, 0x8f, 0xcd, 0xd6, 0x17, 0x93, 0xd8, 0xde, 0xe8,
+    0xb0, 0x9e, 0x27, 0x15, 0xc5, 0xbb, 0xa5, 0xbb, 0xc2, 0x30,
+    0x9b, 0xc7, 0x27, 0x02, 0x18, 0xd8, 0xdb, 0xa4, 0x84, 0x37,
+    0x64, 0xf7, 0xf7, 0xf1, 0xc8, 0x86, 0x4c, 0x64, 0x97, 0x08,
+    0xe9, 0x4e, 0x0e, 0xb6, 0x92, 0xe9, 0x4c, 0x7b, 0x7f, 0xe1,
+    0xcc, 0xa0, 0x71, 0xa7, 0x34, 0x48, 0x46, 0xbb, 0x37, 0xce,
+    0xb0, 0x4d, 0x39, 0xa8, 0x0e, 0xab, 0xf6, 0x2f, 0x7c, 0x88,
+    0xae, 0xcf, 0x90, 0xc6, 0x01, 0xd3, 0x5b, 0x37, 0xe9, 0xb1,
+    0x28, 0x42, 0x14, 0xbf, 0x59, 0x35, 0x04, 0xab, 0x46, 0x6e,
+    0xa8, 0x29, 0xe2, 0x7a, 0x77, 0x0e, 0x07, 0x67, 0xe4, 0x2b,
+    0x03, 0xd2, 0x02, 0x36, 0x16, 0xd7, 0x81, 0x5d, 0x38, 0x9c,
+    0x68, 0x9c, 0xf5, 0x9e, 0x49, 0x7d, 0x99, 0xfd, 0xcd, 0x1d,
+    0xd2, 0xdf, 0x3c, 0x36, 0x19, 0x85, 0xaa, 0xb1, 0x30, 0x7a,
+    0x21, 0xb1, 0x83, 0x16, 0xcf, 0xd1, 0x75, 0xa5, 0x9d, 0xd7,
+    0xc1, 0x60, 0xa8, 0xdb, 0x1e, 0xb9, 0x3e, 0x9c, 0x12, 0x42,
+    0xe8, 0x47, 0x49, 0x18, 0x9f, 0x5c, 0x12, 0xd1, 0x69, 0xd5,
+    0x7d, 0xa8, 0x3c, 0xda, 0x35, 0x8a, 0x6c, 0x63, 0xb8, 0x62,
+    0x8a, 0x61, 0xfa, 0xf2, 0x61, 0x11, 0x1e, 0xb6, 0xf3, 0x5c,
+    0x62, 0x9d, 0xa7, 0x62, 0x0c, 0x87, 0x93, 0xe2, 0x23, 0x6c,
+    0x3d, 0xa9, 0x2c, 0x4b, 0xd5, 0x7f, 0xfe, 0x72, 0x27, 0x36,
+    0x06, 0xcb, 0x65, 0x38, 0xef, 0x13, 0x57, 0x6a, 0xc9, 0xc6,
+    0x4f, 0x51, 0xd0, 0x90, 0x06, 0xa0, 0x23, 0x65, 0x95, 0xce,
+    0x16, 0x8f, 0x8d, 0xb2, 0xf9, 0x7f, 0x3c, 0x2c, 0x30, 0x5a,
+    0x38, 0xf1, 0x62, 0x79, 0x4b, 0xe5, 0xd7, 0x0a, 0x3f, 0x83,
+    0x5f, 0x46, 0x26, 0x97, 0xb7, 0x08, 0x8c, 0x5b, 0xb8, 0x02,
+    0x28, 0xf2, 0x4d, 0xdf, 0x93, 0x97, 0xc5, 0x94, 0x4b, 0x0e,
+    0x42, 0xc3, 0x35, 0x91, 0x6b, 0x69, 0x61, 0x76, 0x7f, 0x94,
+    0xcf, 0x0b, 0x81, 0x33, 0xff, 0xf3, 0x0c, 0xc7, 0x01, 0x94,
+    0x94, 0xa9, 0xed, 0xcd, 0x4b, 0xc8, 0xcb, 0x91, 0xf9, 0x7a,
+    0x47, 0xcd, 0x79, 0x3c, 0xa6, 0xde, 0x52, 0xd2, 0x47, 0x5c,
+    0x10, 0x62, 0xbb, 0xe5, 0x32, 0xde, 0x83, 0xcf, 0xa8, 0x52,
+    0xb3, 0xe7, 0xf9, 0xec, 0x17, 0x34, 0xbf, 0x33, 0x5d, 0xb2,
+    0x4e, 0x56, 0xf7, 0x29, 0xd9, 0x5c, 0x1b, 0x83, 0x01, 0xbb,
+    0xb9, 0x2b, 0x95, 0x52, 0x08, 0xab, 0xa4, 0x51, 0x03, 0xa1,
+    0xfb, 0x6a, 0x50, 0xcd, 0xa8, 0x9d, 0x95, 0x6f, 0x7e, 0xb1,
+    0x80, 0x1e, 0x9d, 0x81, 0x01, 0x26, 0x41, 0x78, 0x36, 0x3c,
+    0x8a, 0x44, 0xf4, 0x98, 0x88, 0x1c, 0x5d, 0x06, 0xd3, 0xd2,
+    0xb2, 0x58, 0x7d, 0xa1, 0x45, 0x1b, 0xbf, 0x8c, 0xf6, 0x6a,
+    0xfa, 0xfd, 0x08, 0x29, 0x3e, 0x91, 0x57, 0xf1, 0x3d, 0x20,
+    0xed, 0x49, 0x6e, 0x9c, 0x46, 0xd5, 0x08, 0x8d, 0x9b, 0xf8,
+    0xef, 0xa3, 0x3a, 0x98, 0xcb, 0xb4, 0xcb, 0x5b, 0x30, 0x25,
+    0x20, 0xcc, 0x04, 0xa1, 0xeb, 0xeb, 0xee, 0x1b, 0x36, 0x85,
+    0xc1, 0x93, 0x16, 0x5a, 0x31, 0xdf, 0xd6, 0x0e, 0x73, 0x9e,
+    0x63, 0x6e, 0x96, 0x90, 0x54, 0xd2, 0xc2, 0x53, 0x69, 0x93,
+    0xd5, 0x54, 0xca, 0xd8, 0x84, 0xf7, 0x8f, 0x9a, 0xd1, 0x80,
+    0x0d, 0x57, 0xa8, 0x26, 0xbe, 0x45, 0x64, 0xd5, 0x2b, 0xbb,
+    0x45, 0xb5, 0x08, 0xb9, 0x37, 0x57, 0x02, 0x82, 0x03, 0xc1,
+    0x00, 0xd1, 0x30, 0x2e, 0xb7, 0x9b, 0xe7, 0x5d, 0x13, 0x74,
+    0x1f, 0x52, 0xf2, 0x02, 0x18, 0xe9, 0x07, 0x87, 0x9e, 0xed,
+    0xde, 0x83, 0x92, 0xcf, 0x73, 0x61, 0x21, 0xc4, 0x62, 0x30,
+    0x6c, 0xa2, 0x36, 0xbd, 0xe2, 0xc5, 0x19, 0xf6, 0xdf, 0x51,
+    0x7b, 0xca, 0xd4, 0xe4, 0x51, 0x83, 0x49, 0x27, 0xdd, 0xbd,
+    0xb0, 0x10, 0x79, 0x39, 0xdd, 0x0e, 0x3d, 0x65, 0xad, 0x6d,
+    0xa3, 0x95, 0x52, 0x85, 0xdb, 0x18, 0x94, 0x60, 0xaa, 0xc0,
+    0xc8, 0x8b, 0xdb, 0xfe, 0xf9, 0xf0, 0x86, 0xf9, 0x33, 0x8a,
+    0xd7, 0xbe, 0x8d, 0x43, 0x83, 0x4d, 0xe4, 0x17, 0x2b, 0x46,
+    0x54, 0x44, 0x1b, 0xbe, 0x52, 0x64, 0x47, 0x02, 0x6c, 0x4a,
+    0x64, 0xb4, 0x3f, 0x21, 0x2f, 0xbb, 0xe3, 0x72, 0x7c, 0x26,
+    0x14, 0xdf, 0x80, 0x50, 0xd4, 0x94, 0xe9, 0xc6, 0x7d, 0x71,
+    0xd8, 0xaf, 0xfb, 0x74, 0x36, 0x33, 0xbe, 0x58, 0x63, 0xad,
+    0xcb, 0xdf, 0xc0, 0x73, 0x9e, 0x19, 0xb0, 0x65, 0xe1, 0xd1,
+    0x10, 0x44, 0xf1, 0xf0, 0x08, 0xa3, 0x09, 0x25, 0xeb, 0xd5,
+    0xcb, 0xdd, 0x98, 0xdd, 0xbc, 0x09, 0x2c, 0xef, 0xc1, 0x8d,
+    0x43, 0x15, 0x41, 0xc2, 0xa1, 0x84, 0x37, 0x70, 0x5a, 0xd5,
+    0xf5, 0xb2, 0x6a, 0x1f, 0xbb, 0xcc, 0x30, 0xb9, 0xd9, 0xc7,
+    0x36, 0x21, 0xf3, 0x69, 0x3e, 0x91, 0x38, 0x4d, 0xa5, 0xc4,
+    0xf7, 0x84, 0x90, 0x34, 0x0e, 0x47, 0x7e, 0x26, 0xf2, 0x98,
+    0x25, 0x26, 0xda, 0xf0, 0x4e, 0x55, 0xea, 0x4d, 0x9b, 0x8a,
+    0x4a, 0xe1, 0x1f, 0xa0, 0x07, 0x90, 0x9e, 0x59, 0x64, 0xae,
+    0xd9, 0xd6, 0x7e, 0x72, 0xa1, 0xc4, 0xea, 0x7d, 0xbd, 0x1f,
+    0x7d, 0x2b, 0xd9, 0x2c, 0xdc, 0x8b, 0xc0, 0xda, 0x52, 0x0c,
+    0xd1, 0xd0, 0x56, 0xb7, 0x93, 0xc7, 0x26, 0x79, 0x71, 0xd0,
+    0x0d, 0xae, 0xaa, 0xa7, 0xe4, 0xc1, 0x59, 0x27, 0x68, 0x97,
+    0x9a, 0xff, 0x3d, 0x36, 0x07, 0x55, 0x77, 0x07, 0x97, 0x69,
+    0xf3, 0x99, 0x91, 0x3f, 0x63, 0xfd, 0x70, 0x8c, 0xa1, 0xeb,
+    0xc5, 0x21, 0xa3, 0xfe, 0x99, 0x96, 0x11, 0x37, 0xb9, 0xe6,
+    0x93, 0xf8, 0xd0, 0xb1, 0xa3, 0x57, 0x7a, 0xa8, 0x63, 0xdd,
+    0x09, 0x56, 0xb0, 0x3b, 0xa6, 0x59, 0xc7, 0x89, 0x54, 0x16,
+    0xe9, 0x2d, 0x78, 0x7d, 0xaf, 0x4e, 0x0a, 0x5b, 0x62, 0x3b,
+    0x0b, 0xcb, 0x24, 0x89, 0x4e, 0x1c, 0x3d, 0xe1, 0xbd, 0x5a,
+    0x3e, 0xc5, 0xfd, 0x15, 0x3d, 0x08, 0x38, 0x33, 0x5e, 0x37,
+    0x4c, 0xe3, 0xe3, 0xe9, 0xc4, 0x1d, 0x2b, 0xd4, 0x58, 0x25,
+    0x58, 0x23, 0x8e, 0xc6, 0x83, 0x9a, 0xf3, 0x9a, 0x78, 0xe9,
+    0xa7, 0xca, 0xd7, 0xdd, 0x89, 0x20, 0x6e, 0x02, 0xea, 0x6b,
+    0x37, 0x74, 0xda, 0xa0, 0xc2, 0x5a, 0x2b, 0x80, 0x1c, 0x28,
+    0x91, 0x0d, 0x50, 0x64, 0xf0, 0x12, 0xe7, 0xc4, 0x7e, 0xdd,
+    0x28, 0x3b, 0x26, 0x9a, 0xf4, 0x39, 0x56, 0xa4, 0x72, 0x4d,
+    0xcb, 0x67, 0x3c, 0x68, 0xb2, 0x6f, 0xf0, 0xd0, 0x15, 0x90,
+    0xc8, 0x08, 0xbb, 0x0b, 0x08, 0x6b, 0x8a, 0xde, 0x41, 0x57,
+    0xbc, 0x63, 0x0e, 0x00, 0x8d, 0xf8, 0xdd, 0x93, 0xce, 0x58,
+    0x7b, 0xa8, 0xb9, 0x64, 0x26, 0x06, 0xe7, 0x71, 0x23, 0x0f,
+    0x41, 0xf1, 0xb7, 0xae, 0x59, 0x2e, 0xd0, 0x73, 0xc5, 0xd9,
+    0xdc, 0x0e, 0x1c, 0x02, 0x58, 0x69, 0xb3, 0x15, 0x6d, 0x96,
+    0x2b, 0xdb, 0x7b, 0x3b, 0x6c, 0x38, 0x32, 0x6b, 0xd8, 0x08,
+    0xb2, 0xbd, 0xa7, 0x49, 0x43, 0xeb, 0x90, 0x42, 0x70, 0xc5,
+    0xba, 0xcd, 0x4a, 0x44, 0x8f, 0x83, 0x0d, 0x17, 0x51, 0x5a,
+    0x95, 0xa2, 0x57, 0x9a, 0x16, 0x19, 0x91, 0xbb, 0x90, 0x5c,
+    0x2a, 0x16, 0xe8, 0x26, 0x10, 0x3c, 0xb7, 0x10, 0x5c, 0xf8,
+    0xc5, 0x15, 0x2b, 0x70, 0x75, 0x69, 0xba, 0x7b, 0x3d, 0x0b,
+    0x57, 0xac, 0x39, 0x12, 0x2e, 0xd6, 0xd9, 0x13, 0x74, 0x8e,
+    0xa8, 0x0b, 0x17, 0xe1, 0x03, 0x7a, 0xba, 0x1d, 0x07, 0x91,
+    0x8c, 0x2a, 0x3a, 0x8d, 0xe0, 0x2a, 0x94, 0xd4, 0x16, 0x35,
+    0x64, 0x8b, 0x92, 0x2c, 0x2f, 0xa4, 0x18, 0xfe, 0x3f, 0x02,
+    0x19, 0x8c, 0xb9, 0xeb, 0xaf, 0x01, 0x06, 0xa8, 0x37, 0x7f,
+    0xe2, 0x44, 0x10, 0xce, 0xeb, 0x8d, 0xd0, 0x73, 0xc4, 0x1e,
+    0x3d, 0x2c, 0xaf, 0x77, 0xb2, 0xef, 0xe5, 0x95, 0x8b, 0xdf,
+    0x02, 0xfc, 0x93, 0xb8, 0xa9, 0x27, 0x88, 0x1d, 0x1d, 0x82,
+    0x9f, 0xb6, 0xe4, 0x12, 0x05, 0x79, 0xb6, 0x1c, 0x41, 0x0d,
+    0xc1, 0x53, 0x49, 0x8f, 0x3d, 0xc9, 0xad, 0x84, 0xcb, 0x0b,
+    0x88, 0x7e, 0xfe, 0x73, 0x59, 0x21, 0x64, 0xc5, 0x50, 0x53,
+    0xdc, 0x98, 0xc6, 0x43, 0xb8, 0xf5, 0xc3, 0xa1, 0xf5, 0xb2,
+    0xd8, 0x86, 0xe9, 0xae, 0x98, 0xf9, 0x3b, 0x99, 0xc0, 0xe7,
+    0xd7, 0x4a, 0xed, 0xac, 0x89, 0x84, 0xb0, 0x8e, 0xd3, 0xab,
+    0xec, 0x03, 0x02, 0x12, 0x4b, 0x44, 0x17, 0x4d, 0x98, 0x26,
+    0x1e, 0x51, 0xc5, 0xbb, 0xcd, 0xdc, 0x50, 0xab, 0x83, 0x37,
+    0x49, 0x90, 0x1e, 0x34, 0xad, 0x81, 0x22, 0x6c, 0xe4, 0xdd,
+    0x19, 0x01, 0x09, 0x25, 0x2d, 0x9e, 0x52, 0x90, 0x72, 0xa1,
+    0x68, 0x3d, 0x0c, 0x49, 0x99, 0x19, 0x75, 0x5a, 0xca, 0x08,
+    0x69, 0xa1, 0xd2, 0x88, 0x8c, 0xea, 0xcf, 0x9c, 0xbc, 0x23,
+    0xad, 0x3f, 0xb9, 0xfc, 0xb9, 0x30, 0x0d, 0xd6, 0xd9, 0x65,
+    0x0c, 0x7e, 0x99, 0x68, 0x35, 0x26, 0x07, 0xd1, 0x55, 0xbf,
+    0x8e, 0xde, 0xe7, 0xe7, 0x01, 0xcb, 0xca, 0x0a, 0x39, 0x2e,
+    0xcc, 0x19, 0xec, 0x77, 0xf3, 0xab, 0xb2, 0xe6, 0x0e, 0x54,
+    0x06, 0x01, 0x50, 0x77, 0xd3, 0x61, 0x36, 0x05, 0x90, 0xe4,
+    0xd8, 0xc4, 0x1d, 0xf5, 0xc7, 0xfa, 0x65, 0xf0, 0x46, 0x6a,
+    0x5f, 0xa7, 0xc3, 0x8c, 0x6f, 0x04, 0x7f, 0xcf, 0x97, 0xb9,
+    0x68, 0x92, 0x31, 0x09, 0x02, 0x9f, 0x22, 0xc9, 0xf8, 0xe6,
+    0x7e, 0xa8, 0x95, 0x5b, 0x6b, 0xfe, 0x9c, 0x4e, 0x63, 0x2d,
+    0x8c, 0x1a, 0x4c, 0x8b, 0x14, 0x79, 0x08, 0xd5, 0x96, 0x76,
+    0xd1, 0xb4, 0x2f, 0xae, 0x5d, 0x91, 0x88, 0x7c, 0xdd, 0xd2,
+    0x06, 0x86, 0xcf, 0x0a, 0x83, 0x6f, 0xda, 0xca, 0x71, 0x7c,
+    0xe7, 0xe5, 0x34, 0xa8, 0x9a, 0x53, 0x8d, 0xa5, 0xaa, 0x5d,
+    0xb5, 0x17, 0x81, 0x34, 0x6f, 0xbe, 0xbb, 0xb6, 0x58, 0x22,
+    0x90, 0x80, 0xf6, 0x9c, 0x1c, 0xb0, 0x79, 0x8f, 0x92, 0x5b,
+    0x7d, 0x1c, 0x71, 0x5f, 0xb4, 0x87, 0x36, 0xbe, 0x81, 0x8d,
+    0x4a, 0xfc, 0x28, 0x72, 0x81, 0xaf, 0x5f, 0xbd, 0x5f, 0x99,
+    0xe3, 0xc9, 0x37, 0xb0, 0x6e, 0xad, 0x70, 0x96, 0xfa, 0xe3,
+    0x99, 0xf7, 0x08, 0x14, 0x21, 0x21, 0xb7, 0x1a, 0xaa, 0xe8,
+    0x07, 0xb6, 0xfd, 0xa3, 0x7a, 0x2d, 0x93, 0x64, 0x8f, 0x89,
+    0x2c, 0x71, 0x49, 0x71, 0xb8, 0x45, 0xca, 0xe0, 0x7c, 0x00,
+    0x8d, 0xbd, 0xb8, 0x1c, 0x3a, 0x94, 0xa2, 0xa7, 0x6d, 0x0a,
+    0x2e, 0x84, 0xaf, 0xbd, 0xab, 0x05, 0x95, 0x64, 0x8b, 0x05,
+    0xc8, 0xc9, 0x4e, 0xea, 0xb5, 0x96, 0x4a, 0x47, 0xdd, 0xf2,
+    0xcb, 0x02, 0x82, 0x03, 0xc0, 0x59, 0xb3, 0xd9, 0x85, 0xdc,
+    0xa8, 0xb9, 0x93, 0x85, 0xa2, 0xbc, 0x79, 0xfc, 0x72, 0x50,
+    0xc1, 0xa0, 0xa5, 0xdb, 0x71, 0x35, 0xa1, 0x31, 0xbc, 0x68,
+    0x4e, 0xd5, 0x19, 0x9e, 0x0e, 0x32, 0x3a, 0xad, 0x40, 0x9e,
+    0x82, 0x3c, 0x1e, 0x2b, 0x34, 0x3b, 0xc9, 0x32, 0x61, 0x07,
+    0x5e, 0x46, 0xa9, 0xbe, 0xbe, 0x73, 0x0c, 0x12, 0xef, 0x52,
+    0x68, 0x82, 0xe2, 0x0b, 0x12, 0x74, 0xfc, 0x10, 0x5c, 0xc0,
+    0xb5, 0x98, 0x4d, 0x86, 0xbb, 0x8c, 0x40, 0x15, 0xa1, 0x6e,
+    0x46, 0x73, 0x2e, 0xd6, 0x99, 0x6b, 0x50, 0xab, 0x04, 0x1a,
+    0x5f, 0xf4, 0xfa, 0xcb, 0x4b, 0xad, 0xc4, 0x5e, 0x62, 0xa7,
+    0x48, 0xd4, 0x52, 0x85, 0xdc, 0x2a, 0x85, 0x9b, 0xee, 0x08,
+    0xa5, 0xaa, 0xaa, 0xe8, 0x44, 0xf0, 0xed, 0x89, 0x21, 0xe4,
+    0xb4, 0xab, 0x3c, 0x0d, 0x53, 0x7e, 0x53, 0xdd, 0xac, 0x47,
+    0xda, 0x77, 0x79, 0x5f, 0x78, 0x7a, 0x80, 0x84, 0x46, 0x50,
+    0xaa, 0xdb, 0x3b, 0x8c, 0x6b, 0xda, 0xb0, 0xac, 0x0a, 0xd3,
+    0x4c, 0xe4, 0x6e, 0x87, 0xd1, 0xb2, 0x5a, 0xd5, 0x98, 0xae,
+    0xcb, 0x7e, 0xc2, 0x19, 0xdc, 0x53, 0x64, 0x86, 0x4c, 0x7b,
+    0xe0, 0x63, 0x22, 0x94, 0x34, 0xad, 0x15, 0xdc, 0xd8, 0xa8,
+    0x5f, 0xc6, 0x58, 0xf6, 0x72, 0x34, 0xdd, 0xfb, 0x85, 0x8a,
+    0xd9, 0xa3, 0xfb, 0x3b, 0xad, 0x5d, 0xf0, 0x1a, 0x0b, 0xa8,
+    0x91, 0xe7, 0x7d, 0x26, 0x27, 0x38, 0xf8, 0xe0, 0x49, 0x1b,
+    0x56, 0xc5, 0x5b, 0xe3, 0x1c, 0x7b, 0xa3, 0x53, 0x6d, 0x22,
+    0xfa, 0xd7, 0x63, 0x5f, 0xf0, 0xcb, 0x92, 0x49, 0x01, 0x54,
+    0xe5, 0x77, 0x5b, 0xd3, 0xab, 0xce, 0xb8, 0x3a, 0x5b, 0xb8,
+    0x07, 0x40, 0x46, 0x51, 0xe4, 0x59, 0xa2, 0x45, 0x41, 0xcc,
+    0x81, 0x6c, 0xe3, 0xa6, 0xb3, 0xa0, 0x30, 0x4a, 0x67, 0x10,
+    0xed, 0xc0, 0x8a, 0xcd, 0xfc, 0xa5, 0x44, 0x9b, 0x59, 0x19,
+    0x4a, 0x43, 0x8d, 0xec, 0x00, 0xd8, 0x6d, 0xf9, 0xf0, 0x2d,
+    0xd9, 0x55, 0xfc, 0x05, 0xe2, 0x12, 0x48, 0x4d, 0xd6, 0x7d,
+    0xec, 0x41, 0xc4, 0x9e, 0xe2, 0xed, 0x84, 0x14, 0x29, 0x0e,
+    0x5b, 0x81, 0x0b, 0xb0, 0x87, 0x8a, 0xd3, 0x35, 0x5c, 0xad,
+    0xdb, 0xcc, 0xa1, 0x3c, 0xcb, 0x8b, 0x23, 0x55, 0x69, 0xf1,
+    0x83, 0x84, 0x81, 0x36, 0xae, 0xd5, 0xf3, 0x98, 0xb6, 0xb2,
+    0xb5, 0xa1, 0x79, 0x6d, 0x80, 0x8f, 0x2e, 0x25, 0x71, 0x4e,
+    0x16, 0xff, 0xa0, 0x7c, 0xa4, 0x62, 0x8c, 0x44, 0x85, 0x64,
+    0x90, 0x7c, 0xac, 0x10, 0x36, 0xf2, 0xf2, 0xfb, 0x20, 0x2b,
+    0xa1, 0x27, 0xd0, 0xcc, 0x27, 0xfd, 0xb0, 0xba, 0x3e, 0x37,
+    0xb1, 0xa8, 0x9d, 0x3c, 0x82, 0x63, 0xd0, 0x16, 0x6d, 0x7a,
+    0xdd, 0x2e, 0xea, 0xe5, 0x87, 0xd6, 0x64, 0x72, 0xdb, 0x60,
+    0x53, 0x38, 0x18, 0x66, 0x1d, 0x25, 0xf6, 0x08, 0x92, 0x7f,
+    0x68, 0x5b, 0x79, 0x07, 0xde, 0x93, 0xee, 0xf8, 0x8f, 0xce,
+    0x28, 0xcf, 0xb1, 0x5b, 0x43, 0x51, 0xdf, 0xf5, 0xac, 0xe8,
+    0x9c, 0x95, 0x14, 0x8a, 0x67, 0xe1, 0x25, 0xfe, 0x11, 0xa2,
+    0x40, 0xf8, 0xdd, 0xcf, 0xf5, 0x17, 0x94, 0xb6, 0x88, 0x10,
+    0xa2, 0x90, 0x58, 0xef, 0xaf, 0x73, 0xf8, 0x7c, 0x9b, 0x20,
+    0x30, 0x79, 0xca, 0x3f, 0xa9, 0x22, 0x40, 0xfd, 0xcc, 0xb0,
+    0x5d, 0x0d, 0x97, 0x6b, 0xc0, 0x75, 0x35, 0x33, 0xc5, 0x76,
+    0x45, 0x6e, 0x9b, 0x78, 0xe7, 0xb4, 0x04, 0xb3, 0xba, 0x3b,
+    0x93, 0xb1, 0xa9, 0x8f, 0xa1, 0x24, 0x5d, 0x1c, 0x0e, 0x66,
+    0xc0, 0xc6, 0xcc, 0xd6, 0xb7, 0x88, 0x9d, 0xb8, 0x45, 0xe3,
+    0xaa, 0xc9, 0x6c, 0xfd, 0x37, 0xdc, 0x85, 0xd5, 0x49, 0xfd,
+    0xef, 0xeb, 0xf9, 0x7a, 0x3f, 0x7a, 0x4f, 0x86, 0x49, 0xaa,
+    0x9f, 0x08, 0x12, 0x0b, 0x11, 0x35, 0x5c, 0xd5, 0xd3, 0xda,
+    0x14, 0x50, 0x03, 0x2c, 0x24, 0x26, 0x0e, 0x29, 0x18, 0xcc,
+    0x1d, 0x0a, 0x7c, 0x94, 0x8b, 0xc0, 0xa0, 0x3f, 0xea, 0xf8,
+    0xf8, 0xa9, 0x1d, 0x65, 0x31, 0x6f, 0x3b, 0xa6, 0xd0, 0xfc,
+    0x26, 0xb0, 0x4e, 0x3a, 0x66, 0xe7, 0x32, 0x10, 0x2e, 0x84,
+    0x47, 0xad, 0xa9, 0x18, 0xfc, 0xa3, 0x8b, 0x74, 0x84, 0x4f,
+    0xd4, 0x25, 0x93, 0x0f, 0xdb, 0x2e, 0xae, 0x88, 0x8e, 0x28,
+    0xf8, 0x0f, 0xaa, 0x60, 0xd4, 0xbe, 0xad, 0x66, 0x0c, 0x0d,
+    0x01, 0xbd, 0x8d, 0xc4, 0xfc, 0x48, 0xef, 0x78, 0x14, 0x34,
+    0xee, 0xb3, 0xbc, 0xd4, 0xbb, 0x1f, 0x7c, 0x12, 0x5c, 0x9b,
+    0xeb, 0x77, 0x3e, 0x2c, 0x6e, 0x31, 0x59, 0xe6, 0x78, 0xc5,
+    0xe8, 0xa4, 0xdd, 0xf1, 0xef, 0x5d, 0x27, 0x45, 0x31, 0x13,
+    0xd0, 0x21, 0xa1, 0x13, 0xce, 0xac, 0x7e, 0xbb, 0xfb, 0x32,
+    0xeb, 0x76, 0x31, 0xc4, 0xba, 0xdf, 0xfb, 0x5a, 0x1b, 0xc9,
+    0x9e, 0x74, 0xa0, 0x9e, 0x26, 0x82, 0xd5, 0x6e, 0x1d, 0xc3,
+    0x0e, 0xd1, 0x6d, 0xdb, 0x43, 0xb3, 0x0b, 0x14, 0xcb, 0xf1,
+    0xad, 0x62, 0x34, 0x49, 0xb8, 0xd3, 0x08, 0xca, 0x93, 0xf1,
+    0x42, 0xb2, 0x4b, 0x23, 0x79, 0x93, 0xde, 0x18, 0x58, 0xf3,
+    0x66, 0xfa, 0xdc, 0xab, 0xca, 0x33, 0x22, 0x2b, 0x5c, 0x8c,
+    0x12, 0xc1, 0x7b, 0x2e, 0x52, 0x72, 0xa7, 0x78, 0x4a, 0x49,
+    0xa1, 0x53, 0x02, 0x76, 0x2d, 0x2e, 0xf8, 0x43, 0x3c, 0xe8,
+    0xfa, 0xb7, 0xff, 0x39, 0xed, 0x74, 0x9e, 0x11, 0x61, 0x33,
+    0xde, 0x2a, 0x55, 0xe6, 0x4a, 0xe7, 0x97, 0xa6, 0xb2, 0xc3,
+    0x40, 0x41, 0x52, 0x66, 0xcf, 0xbf, 0xf8, 0x8e, 0x08, 0xea,
+    0x96, 0x4d, 0x03, 0xc9, 0xbe, 0x3c, 0x4e, 0x36, 0x8c, 0x6f,
+    0x4d, 0x1e, 0xcd, 0x31, 0x6d, 0x53, 0xea, 0x9e, 0xf0, 0x8e,
+    0x35, 0x97, 0x37, 0x54, 0xe9, 0x0f, 0xb8, 0x23, 0x25, 0x69,
+    0x5b, 0xb5, 0xff, 0xc3, 0x5a, 0x2d, 0x10, 0x6a, 0xc0, 0xb8,
+    0xee, 0x0d, 0x31, 0x5b, 0xe4, 0x69, 0x40, 0x62, 0xa7, 0x1b,
+    0x16, 0xfa, 0xd6, 0xb8, 0xba, 0xc8, 0x6a, 0xa3, 0x29, 0xdd,
+    0x9b, 0x4d, 0xd7, 0x96, 0xef, 0x31, 0x74, 0xac, 0x37, 0x10,
+    0x91, 0x30, 0x0c, 0x15, 0x3f, 0x09, 0xb6, 0x7d, 0x22, 0xfb,
+    0x8c, 0x6f, 0xc3, 0x93, 0xa3, 0x98, 0xa6, 0x23, 0xa4, 0x55,
+    0xe0, 0x9e, 0x23, 0x06, 0xa9, 0x78, 0xe9, 0xb3, 0x88, 0xc9,
+    0xb7, 0x83, 0x05, 0x46, 0x11, 0x3a, 0x0a, 0xb9, 0x74, 0x5b,
+    0xa0, 0xb5, 0x06, 0x96, 0x86, 0xb6, 0xf4, 0x9d, 0x0d, 0x86,
+    0x43, 0xa8, 0x40, 0x4b, 0x08, 0x93, 0x7c, 0xad, 0xb0, 0x50,
+    0xb4, 0xd0, 0xe7, 0xad, 0xd0, 0x54, 0x5e, 0x15, 0xaf, 0xad,
+    0x34, 0x12, 0x86, 0xb3, 0x29, 0x3b, 0x20, 0xc9, 0xad, 0xeb,
+    0xc2, 0x65, 0xf3, 0x5c, 0x2d, 0xe5, 0xff, 0xfd, 0x81, 0x79,
+    0xf5, 0x11, 0x6f, 0xf7, 0xca, 0x0c, 0x76, 0xf0, 0xd4, 0x02,
+    0x9d, 0xb7, 0x76, 0x39, 0x6d, 0x32, 0x6a, 0xb8, 0x30, 0xa4,
+    0x01, 0xcc, 0x10, 0xef, 0xb1, 0x0e, 0x41, 0x22, 0x82, 0x5b,
+    0x22, 0xcb, 0x32, 0x19, 0x2e, 0xa3, 0x0a, 0xce, 0x05, 0xdd,
+    0xe8, 0x4a, 0x58, 0x92, 0xe1, 0x02, 0x82, 0x03, 0xc0, 0x22,
+    0x0f, 0x95, 0x5b, 0xc2, 0x1f, 0xde, 0xf0, 0xde, 0xf4, 0x86,
+    0xbd, 0xef, 0x07, 0x7d, 0x52, 0x03, 0x8c, 0x26, 0x31, 0x17,
+    0xfd, 0x5c, 0x97, 0xed, 0xd5, 0xe0, 0xb3, 0x18, 0x2d, 0x68,
+    0x10, 0x3f, 0xc4, 0xdf, 0xd1, 0x05, 0x78, 0x81, 0x3d, 0x05,
+    0xde, 0xba, 0x3a, 0x67, 0x85, 0x0e, 0xdf, 0xb5, 0x16, 0x28,
+    0xe8, 0x84, 0x3a, 0x71, 0x2a, 0x20, 0x17, 0x28, 0x05, 0xfd,
+    0xb7, 0x4d, 0x22, 0x4a, 0x93, 0x46, 0x56, 0x27, 0x43, 0xc0,
+    0x3a, 0x16, 0xff, 0x3d, 0x61, 0xcc, 0xcb, 0xce, 0xac, 0xa8,
+    0x53, 0x3a, 0x0d, 0xf4, 0x2d, 0xd2, 0x73, 0xf2, 0x64, 0xa0,
+    0x1e, 0x60, 0x53, 0xec, 0x0d, 0xff, 0xe0, 0x00, 0x10, 0xfb,
+    0xa4, 0x57, 0xd3, 0xfc, 0xe4, 0xe0, 0xec, 0x44, 0x0b, 0x1c,
+    0x05, 0x39, 0xa4, 0x13, 0x87, 0x29, 0x11, 0x9d, 0xea, 0xe9,
+    0x64, 0xa9, 0x1c, 0x76, 0x3a, 0x65, 0x0b, 0xfd, 0xed, 0x77,
+    0x46, 0x4f, 0xcd, 0x0b, 0x63, 0xc4, 0x83, 0x0b, 0x56, 0x79,
+    0xd3, 0x67, 0x01, 0x11, 0x02, 0xd9, 0x50, 0xd8, 0x23, 0xf4,
+    0xb6, 0x02, 0x4c, 0xae, 0xb5, 0xc9, 0x68, 0x1b, 0x87, 0x33,
+    0xbb, 0xdc, 0x64, 0x0e, 0x32, 0x34, 0xb2, 0x25, 0xaa, 0x76,
+    0xdd, 0x7e, 0xc3, 0x46, 0x51, 0x1c, 0xc1, 0xd0, 0x05, 0x09,
+    0x6c, 0x27, 0xd3, 0xcf, 0x33, 0x7a, 0xb9, 0x26, 0x24, 0x23,
+    0x4a, 0x93, 0x9f, 0x4b, 0x96, 0xc7, 0xe2, 0xb2, 0x51, 0x42,
+    0x4d, 0x5d, 0xd9, 0x73, 0x75, 0xce, 0x23, 0x28, 0x56, 0x5e,
+    0xe7, 0x96, 0x58, 0x04, 0xfd, 0x33, 0x93, 0x08, 0x41, 0x62,
+    0x02, 0x7e, 0xc9, 0xc6, 0x55, 0x64, 0x19, 0xda, 0x39, 0xb8,
+    0x5d, 0x09, 0x47, 0xf3, 0xdd, 0x77, 0xee, 0xea, 0x35, 0x73,
+    0x95, 0xdb, 0x18, 0x4d, 0xd1, 0xfe, 0xee, 0x40, 0x31, 0x2a,
+    0x22, 0x91, 0x69, 0xd6, 0xed, 0x9c, 0x54, 0x14, 0x73, 0x61,
+    0x61, 0xe7, 0x1d, 0x34, 0x96, 0x47, 0xff, 0x28, 0x7a, 0x48,
+    0xa3, 0xf4, 0xcd, 0x64, 0x23, 0xe2, 0x52, 0x2f, 0x20, 0x8f,
+    0x04, 0xb3, 0xdc, 0xf0, 0x29, 0x67, 0x88, 0x76, 0x79, 0xdb,
+    0x86, 0xa7, 0x95, 0xf0, 0x15, 0x81, 0xbb, 0x98, 0xee, 0xff,
+    0x55, 0x7c, 0xb0, 0xee, 0x67, 0x65, 0xfd, 0xf2, 0x29, 0x0f,
+    0x85, 0x51, 0xf9, 0xac, 0x5c, 0x55, 0x5a, 0xde, 0x40, 0x62,
+    0x58, 0x55, 0x9f, 0x09, 0x4c, 0x2e, 0x28, 0x75, 0xbc, 0x48,
+    0xe2, 0x97, 0x85, 0xb3, 0x83, 0xeb, 0x21, 0x49, 0x21, 0xd4,
+    0xed, 0x74, 0x4f, 0xc1, 0x6c, 0x34, 0x8c, 0x11, 0xb0, 0x93,
+    0x41, 0x99, 0x23, 0x2e, 0xa4, 0xc1, 0x9f, 0x34, 0x74, 0x64,
+    0xbb, 0xd7, 0x4f, 0x8f, 0x9f, 0x3a, 0x0c, 0x4f, 0x5e, 0xdd,
+    0x41, 0x07, 0xf1, 0xfd, 0x5a, 0x9d, 0xe6, 0x77, 0xd8, 0x7e,
+    0x71, 0x7b, 0xad, 0xf7, 0x76, 0x13, 0x71, 0x90, 0xb3, 0x0f,
+    0x46, 0x8e, 0xee, 0x7b, 0x33, 0x97, 0x5d, 0x21, 0x3b, 0xa0,
+    0x58, 0x9e, 0xb7, 0x87, 0x30, 0x8f, 0xc1, 0x23, 0x2c, 0xde,
+    0xf7, 0x0d, 0xa9, 0xd6, 0x50, 0xeb, 0x35, 0x7a, 0x82, 0xab,
+    0x22, 0x49, 0x86, 0xd4, 0x61, 0xc7, 0xc2, 0x4e, 0x77, 0xfc,
+    0x16, 0x0b, 0xaf, 0x81, 0x6a, 0x47, 0xea, 0xac, 0x7e, 0x51,
+    0x4c, 0x56, 0x30, 0x21, 0x46, 0x41, 0xc3, 0x92, 0x60, 0x99,
+    0x4f, 0x88, 0x36, 0x3b, 0x27, 0xb4, 0xb2, 0x7e, 0x44, 0x2f,
+    0xdd, 0x95, 0xe4, 0x5e, 0x16, 0x1f, 0xa7, 0x32, 0x6b, 0x60,
+    0x24, 0x0f, 0xf2, 0xe6, 0x35, 0x3c, 0x0c, 0x3e, 0xb5, 0xd6,
+    0xdd, 0x63, 0xe2, 0x76, 0x35, 0x38, 0x79, 0xbf, 0xa5, 0x23,
+    0xa4, 0xdd, 0xeb, 0x01, 0x48, 0xd0, 0x60, 0x86, 0x11, 0x38,
+    0x5f, 0x9e, 0x6b, 0x00, 0x67, 0xd2, 0x5b, 0x41, 0x0a, 0x5e,
+    0x13, 0x0f, 0xa1, 0x9e, 0x90, 0x85, 0xa6, 0x7f, 0xe5, 0x4b,
+    0x9e, 0x93, 0x4e, 0x5b, 0x1f, 0x47, 0x62, 0xb0, 0x23, 0xbe,
+    0x82, 0xa9, 0xd9, 0xb6, 0x2e, 0xfd, 0xb1, 0x10, 0xca, 0xe0,
+    0xc9, 0x5d, 0xf6, 0x85, 0x18, 0x6c, 0x9c, 0x1d, 0x1f, 0x7c,
+    0xf6, 0x55, 0x09, 0x80, 0xcf, 0xac, 0xfe, 0x37, 0x6a, 0x4f,
+    0x96, 0xaa, 0x40, 0x79, 0x8b, 0x4a, 0xf2, 0x96, 0x79, 0x12,
+    0x1a, 0x26, 0x87, 0x06, 0x35, 0x4d, 0xd4, 0x3e, 0x14, 0x39,
+    0xe5, 0x6c, 0x39, 0x0f, 0x84, 0xb3, 0x5f, 0xed, 0xf4, 0xff,
+    0x89, 0x52, 0x05, 0x00, 0xf1, 0xd1, 0xc3, 0xcf, 0x54, 0x10,
+    0x24, 0x7c, 0xa6, 0xb5, 0x95, 0xa8, 0x6e, 0x13, 0x3e, 0x4a,
+    0x40, 0x6c, 0xf9, 0x63, 0x90, 0x44, 0x52, 0x07, 0x53, 0xb7,
+    0x51, 0xd9, 0x18, 0x47, 0x2e, 0xb0, 0x4e, 0x0f, 0x09, 0x99,
+    0x3a, 0x97, 0x26, 0x53, 0xa6, 0x02, 0x06, 0x0e, 0x93, 0xe1,
+    0x0b, 0xc5, 0xa9, 0x14, 0xd3, 0xd6, 0x8a, 0x29, 0x75, 0xcd,
+    0xb6, 0x7b, 0x64, 0x7c, 0xdd, 0x7e, 0xb4, 0x0a, 0x87, 0x48,
+    0x4a, 0x1b, 0x0e, 0x74, 0x4c, 0xd3, 0x0e, 0x96, 0x0e, 0x53,
+    0xc4, 0x3d, 0x7b, 0x1c, 0x87, 0x6a, 0x15, 0xd8, 0x77, 0xba,
+    0xe6, 0xa0, 0x2f, 0x2c, 0x1a, 0x9d, 0xde, 0x79, 0xfd, 0xab,
+    0x44, 0x80, 0xf0, 0x37, 0x9a, 0x3b, 0xf8, 0xde, 0x3d, 0x29,
+    0xcb, 0x89, 0x64, 0x4b, 0x57, 0xe7, 0x6b, 0x84, 0x09, 0x27,
+    0x17, 0x2f, 0xb2, 0xba, 0x3d, 0x09, 0xc9, 0x3c, 0x89, 0xe6,
+    0x19, 0x73, 0x83, 0xf7, 0xc6, 0x19, 0x18, 0x96, 0xb2, 0x7d,
+    0x1e, 0x9f, 0x70, 0x1f, 0xfc, 0x1f, 0xe2, 0xb5, 0x69, 0x1e,
+    0xf4, 0x65, 0x91, 0xce, 0x4b, 0xdc, 0x74, 0x49, 0x21, 0x64,
+    0x8b, 0x33, 0x50, 0xd2, 0xc1, 0x33, 0x62, 0x5b, 0xde, 0x0a,
+    0x72, 0xbe, 0xc0, 0x05, 0x51, 0x15, 0x80, 0xed, 0x32, 0x3a,
+    0x64, 0xa2, 0x73, 0x68, 0x5b, 0x16, 0xcf, 0x70, 0x5c, 0x98,
+    0xe5, 0x67, 0x45, 0x60, 0x57, 0x2b, 0x47, 0x0a, 0x22, 0x73,
+    0xc3, 0x56, 0x33, 0x3e, 0x14, 0x1d, 0x0c, 0xd1, 0x03, 0x08,
+    0x92, 0x21, 0x2b, 0xa9, 0x6e, 0x6b, 0xf9, 0x0c, 0x1e, 0x86,
+    0xdd, 0xb5, 0xbb, 0xa4, 0xa5, 0x82, 0x99, 0x98, 0x49, 0x36,
+    0xec, 0x98, 0x98, 0x95, 0xac, 0xc2, 0xa0, 0x1f, 0xa5, 0x7e,
+    0x67, 0xd1, 0xcf, 0x6a, 0xf4, 0x16, 0x08, 0x7a, 0x8d, 0x0b,
+    0xae, 0x12, 0x51, 0xe6, 0x8e, 0xe6, 0xcd, 0xa1, 0xaa, 0x6d,
+    0xe4, 0x54, 0xd4, 0x69, 0x1b, 0x09, 0x6a, 0xba, 0x5e, 0x0b,
+    0x11, 0x9c, 0x83, 0xb3, 0x5c, 0x67, 0xbb, 0x2d, 0xf8, 0x66,
+    0x1c, 0x33, 0xb8, 0x22, 0x58, 0x10, 0x96, 0xe9, 0x99, 0xaf,
+    0x0b, 0x2a, 0xf1, 0xe0, 0xcb, 0x56, 0xfb, 0x6d, 0x04, 0x40,
+    0xec, 0x37, 0x67, 0x1e, 0x08, 0x7a, 0x1c, 0xe9, 0xd8, 0x54,
+    0xf7, 0xd4, 0xc7, 0x3c, 0x45, 0x23, 0x2b, 0x76, 0xd2, 0x62,
+    0xc2, 0x53, 0xce, 0xfe, 0x02, 0xc4, 0xd9, 0xf6, 0x3c, 0xed,
+    0x49, 0x47, 0x21, 0xf9, 0x03, 0x3a, 0xa0, 0x16, 0x3a, 0xfe,
+    0x0c, 0x2f, 0x54, 0x7e, 0x85, 0x29, 0x7b, 0xc0, 0xaf, 0xa8,
+    0x5d, 0x31, 0x25, 0xda, 0xa7, 0xe3, 0x92, 0x1b, 0x64, 0x01,
+    0x1b, 0x3f, 0x6e, 0x47, 0xc5, 0x5a, 0x84, 0x52, 0x17, 0x02,
+    0x82, 0x03, 0xc1, 0x00, 0x81, 0x99, 0x2e, 0x72, 0x41, 0x6e,
+    0x86, 0xeb, 0x6f, 0x42, 0xd1, 0x38, 0x6e, 0xaa, 0x1a, 0xd5,
+    0x0a, 0xad, 0x51, 0xb1, 0xce, 0xd6, 0x35, 0xbe, 0x34, 0xd8,
+    0xc1, 0xe4, 0x5f, 0xdf, 0x2e, 0xe4, 0x90, 0xf2, 0x61, 0x21,
+    0x46, 0xc6, 0xfe, 0xab, 0x0f, 0x6c, 0x97, 0x78, 0xcd, 0x55,
+    0x86, 0x83, 0x61, 0x99, 0x49, 0x14, 0x86, 0xc6, 0x86, 0xf1,
+    0x41, 0x66, 0xc9, 0x39, 0x52, 0x99, 0x49, 0x07, 0xd6, 0x9d,
+    0xb7, 0x40, 0x34, 0x5f, 0xe7, 0x3a, 0xfa, 0x95, 0xeb, 0xa1,
+    0x03, 0xb7, 0x52, 0x71, 0x93, 0x30, 0x0b, 0x51, 0x58, 0x82,
+    0x07, 0x2f, 0x44, 0xa9, 0x4f, 0x9b, 0x1b, 0xf3, 0xd6, 0x21,
+    0x3d, 0x68, 0xef, 0x3f, 0xaf, 0xc2, 0x6f, 0xa0, 0xd5, 0x2b,
+    0xb8, 0x73, 0x84, 0x67, 0x36, 0x8b, 0xa4, 0x25, 0xe0, 0x86,
+    0xd9, 0x14, 0x5c, 0x6c, 0xd8, 0x61, 0xe1, 0x0a, 0x6c, 0xaf,
+    0xbb, 0x9c, 0xf6, 0x74, 0xca, 0x5a, 0x04, 0xac, 0x85, 0xc1,
+    0x1b, 0x4d, 0xf2, 0x07, 0xb6, 0x1e, 0x97, 0x7b, 0x75, 0xdf,
+    0x9b, 0x8a, 0x31, 0xc6, 0x90, 0xd5, 0x8d, 0x39, 0xc2, 0x54,
+    0xf4, 0xe2, 0x83, 0x57, 0x12, 0x19, 0xf5, 0xb2, 0xd2, 0x53,
+    0x81, 0x6d, 0xf0, 0x09, 0xc9, 0x80, 0x8b, 0x07, 0x7c, 0x59,
+    0xcd, 0x78, 0x00, 0xd6, 0x44, 0x7f, 0xe4, 0xdb, 0x77, 0x02,
+    0x00, 0x25, 0x79, 0x91, 0xc9, 0xde, 0xd0, 0xed, 0x3f, 0xfc,
+    0x37, 0x36, 0xea, 0xf0, 0x56, 0x50, 0xe7, 0x38, 0xca, 0xe1,
+    0x67, 0x12, 0x96, 0x55, 0x3e, 0xff, 0x97, 0xe5, 0xa7, 0x03,
+    0x5b, 0x72, 0x80, 0xd6, 0xa5, 0x23, 0x39, 0x78, 0x07, 0xc8,
+    0x83, 0x19, 0x74, 0xfb, 0x79, 0xc2, 0x9e, 0xbd, 0xf9, 0xaf,
+    0x09, 0x0f, 0xbd, 0x3d, 0x34, 0xe8, 0x44, 0x89, 0xb1, 0xf1,
+    0x2b, 0xa5, 0xff, 0x22, 0xc9, 0x47, 0xe2, 0x31, 0xb5, 0x6b,
+    0x8a, 0x65, 0x5f, 0x81, 0x5f, 0x89, 0xb0, 0x03, 0x5d, 0x53,
+    0x0e, 0xdd, 0xfb, 0xe5, 0x70, 0xaa, 0xd2, 0x37, 0x4d, 0xa1,
+    0x7c, 0xf2, 0xe4, 0x7f, 0xf1, 0x4a, 0xaf, 0x12, 0xd1, 0x83,
+    0xdc, 0xb2, 0x9e, 0xc1, 0x95, 0x3d, 0x04, 0x9f, 0xa3, 0xad,
+    0xcc, 0x78, 0x14, 0x9a, 0xf9, 0x58, 0x39, 0x08, 0x15, 0xda,
+    0x1b, 0x94, 0x50, 0x2d, 0x44, 0xc0, 0x23, 0x1c, 0x36, 0x5f,
+    0x16, 0x08, 0xa3, 0xdf, 0x9e, 0x4f, 0xbb, 0x07, 0xcd, 0xe3,
+    0x8c, 0xbf, 0xf1, 0xc3, 0x3e, 0x98, 0xf8, 0x49, 0x79, 0x58,
+    0xc9, 0x0f, 0x47, 0xc0, 0xab, 0x2f, 0x21, 0x63, 0xf6, 0xe6,
+    0xfe, 0x8a, 0xea, 0xbc, 0x32, 0x63, 0xca, 0x75, 0xf8, 0xa4,
+    0x1b, 0x6c, 0xfe, 0x9a, 0x6e, 0x68, 0x1f, 0x48, 0x59, 0xfb,
+    0x34, 0x43, 0x10, 0xd5, 0x0d, 0x80, 0x54, 0xcb, 0x67, 0x21,
+    0xc7, 0x13, 0x85, 0x38, 0x0c, 0xf9, 0x40, 0x2e, 0x2e, 0x4a,
+    0x05, 0x9e, 0x51, 0xae, 0xdd, 0xba, 0x23, 0x83, 0x66, 0x2a,
+    0xbf, 0x7f, 0xca, 0x9c, 0x6c, 0x2d, 0x6b, 0x7d, 0x68, 0x52,
+    0x81, 0x56, 0x2f, 0xea, 0xf9, 0xe7, 0xf1, 0x55, 0x16, 0xfc,
+    0x29, 0xe2, 0xa5, 0x1e, 0x0a, 0x06, 0xe0, 0x85, 0x4e, 0xa6,
+    0x5d, 0x20, 0x9d, 0x2b, 0xa2, 0xad, 0xaa, 0xd6, 0x9b, 0xd2,
+    0x98, 0x29, 0x45, 0x5c, 0x55, 0xc0, 0x91, 0xa2, 0x65, 0xcd,
+    0xac, 0xc6, 0x1a, 0x53, 0xa1, 0x46, 0x13, 0xf9, 0xfe, 0x1a,
+    0xf6, 0xdf, 0xa5, 0x1a, 0x58, 0x7c, 0x81, 0x2e, 0x46, 0x46,
+    0xf7, 0x2f, 0xd6, 0xaa, 0x21, 0xb0, 0x0e, 0x7e, 0xac, 0xb8,
+    0xc6, 0x76, 0x62, 0x82, 0x3b, 0x0a, 0x36, 0xbe, 0x97, 0x16,
+    0xd5, 0x79, 0x55, 0x15, 0x64, 0x2a, 0xbe, 0x19, 0x4e, 0x93,
+    0x3b, 0x44, 0x7c, 0xe2, 0xfc, 0x18, 0x4e, 0x83, 0x37, 0xfb,
+    0x26, 0x78, 0x6d, 0x24, 0x6b, 0x48, 0x21, 0x67, 0xde, 0xf5,
+    0x00, 0x22, 0x9a, 0xec, 0x40, 0x16, 0x96, 0x8a, 0x3f, 0xd5,
+    0xa6, 0x5e, 0x03, 0x84, 0xbb, 0x15, 0x4d, 0x55, 0x71, 0x00,
+    0x90, 0xc2, 0x96, 0x25, 0x01, 0xab, 0xe6, 0x47, 0x44, 0x6f,
+    0xf9, 0x53, 0x80, 0x2b, 0xa8, 0x83, 0xc8, 0x14, 0x77, 0x13,
+    0x00, 0x66, 0xee, 0x7e, 0x7a, 0xa0, 0x28, 0x65, 0xf3, 0x31,
+    0xb6, 0xac, 0xd7, 0x87, 0x84, 0x29, 0xed, 0x5b, 0xcd, 0x74,
+    0xc0, 0x89, 0x51, 0x11, 0x9a, 0xd5, 0x7b, 0xe0, 0x9c, 0xd0,
+    0x8d, 0x72, 0xe3, 0x77, 0xda, 0x0a, 0xc2, 0xdc, 0x6f, 0xad,
+    0x49, 0x03, 0xfa, 0xe6, 0x7e, 0xa6, 0x24, 0x32, 0xe6, 0x8f,
+    0xd9, 0x70, 0xfa, 0x59, 0x70, 0xa9, 0xa3, 0x08, 0x7d, 0x89,
+    0xc4, 0x96, 0x61, 0xc2, 0xf5, 0xe5, 0xb5, 0x3b, 0x0d, 0xec,
+    0xb8, 0x9c, 0xee, 0x09, 0x77, 0x27, 0xbd, 0x35, 0x66, 0x90,
+    0x9e, 0x46, 0xf7, 0xbd, 0xa6, 0xc5, 0x31, 0xd4, 0x6a, 0x52,
+    0x17, 0x5d, 0x0a, 0x0e, 0x2c, 0x34, 0x7a, 0x6a, 0x21, 0xac,
+    0x42, 0xf0, 0x31, 0xde, 0x48, 0xe0, 0x27, 0xd0, 0x79, 0xc9,
+    0x06, 0x94, 0x7b, 0x51, 0x4b, 0x5b, 0x02, 0x6a, 0x19, 0xba,
+    0x71, 0x45, 0x9c, 0xdf, 0xe6, 0x30, 0x9e, 0xaa, 0xad, 0xa1,
+    0x87, 0xf6, 0x37, 0xde, 0xa2, 0x97, 0x68, 0x20, 0x2d, 0x5a,
+    0xdc, 0xdd, 0x91, 0x63, 0x5f, 0x79, 0xda, 0x99, 0x20, 0x3a,
+    0x4b, 0xe5, 0x43, 0x0e, 0x12, 0x70, 0x57, 0x91, 0xfa, 0xee,
+    0xc4, 0xb6, 0xb6, 0xb1, 0xf1, 0x06, 0xbd, 0xcf, 0x8d, 0x2a,
+    0x05, 0xc0, 0x07, 0x23, 0x84, 0x85, 0xef, 0x9c, 0xbb, 0x6f,
+    0x5f, 0x4a, 0x9a, 0x27, 0x9f, 0x9f, 0x32, 0x97, 0xe8, 0x24,
+    0xb9, 0x64, 0x2c, 0x39, 0xff, 0x2f, 0x4b, 0xc4, 0x7e, 0x65,
+    0xfe, 0xbb, 0x5c, 0xa0, 0xb2, 0x6e, 0xc4, 0xb6, 0x93, 0x2b,
+    0x51, 0x9e, 0x2e, 0x1f, 0xd8, 0xcf, 0x60, 0xe0, 0x75, 0x15,
+    0xf9, 0xa0, 0x67, 0x99, 0x88, 0x2b, 0x76, 0xce, 0x41, 0x42,
+    0x10, 0x29, 0x89, 0xbf, 0xca, 0xb7, 0x61, 0x08, 0x94, 0xee,
+    0xa0, 0xb3, 0x3a, 0x09, 0xc5, 0x6f, 0x04, 0xf9, 0x1b, 0xb5,
+    0x64, 0x99, 0x08, 0xe4, 0xcc, 0xce, 0xdf, 0x71, 0x65, 0x8a,
+    0x6d, 0x62, 0xde, 0x76, 0x1d, 0x6d, 0x6b, 0x78, 0x22, 0x32,
+    0x63, 0xdd, 0x53, 0x7d, 0xec, 0xed, 0x9d, 0x82, 0xa9, 0x2c,
+    0x5c, 0x8a, 0x17, 0xdd, 0x85, 0xf9, 0xd2, 0xac, 0x6e, 0x98,
+    0x60, 0x2e, 0x08, 0xd4, 0x06, 0x76, 0xf4, 0x97, 0xca, 0xb1,
+    0x72, 0x50, 0x5b, 0x83, 0xea, 0xbb, 0x39, 0x0f, 0x18, 0xb3,
+    0xb8, 0x03, 0xee, 0x7c, 0x84, 0xa9, 0x69, 0xcd, 0x1d, 0xbd,
+    0xe2, 0xb7, 0xce, 0xe2, 0x6f, 0x03, 0x49, 0x52, 0x67, 0xa0,
+    0x1b, 0x23, 0x43, 0x92, 0x2c, 0x7c, 0x3b, 0x65, 0xe8, 0x61,
+    0x99, 0xde, 0xb5, 0xf1, 0x63, 0x73, 0x92, 0x6c, 0x70, 0x8b,
+    0x83, 0x10, 0xb4, 0x06, 0x2c, 0x99, 0x12, 0x73, 0xec, 0x87,
+    0x92, 0x09, 0x67, 0x96, 0xd6, 0x9c, 0x9f, 0x35, 0x48, 0x48,
+    0x3b, 0x44, 0x00, 0x73, 0x1c, 0x59, 0xeb, 0x81, 0x7b, 0xd1,
+    0xda, 0x76, 0xcf, 0xc2, 0x4d, 0xf1, 0xa2, 0x5b, 0x2f, 0x5f,
+    0x91, 0x29, 0x6e, 0x08, 0x37, 0xd6, 0xaa, 0xd2, 0xf8, 0x4f,
+    0x5e, 0x00, 0x16, 0x52
+};
diff --git a/apps/timeouts.h b/apps/timeouts.h
new file mode 100644
index 000000000..e023b0aa4
--- /dev/null
+++ b/apps/timeouts.h
@@ -0,0 +1,17 @@
+/*
+ * Copyright 1999-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#ifndef INCLUDED_TIMEOUTS_H
+# define INCLUDED_TIMEOUTS_H
+
+/* numbers in us */
+# define DGRAM_RCV_TIMEOUT         250000
+# define DGRAM_SND_TIMEOUT         250000
+
+#endif                          /* ! INCLUDED_TIMEOUTS_H */
diff --git a/apps/ts.c b/apps/ts.c
new file mode 100644
index 000000000..eda529705
--- /dev/null
+++ b/apps/ts.c
@@ -0,0 +1,989 @@
+/*
+ * Copyright 2006-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#ifdef OPENSSL_NO_TS
+NON_EMPTY_TRANSLATION_UNIT
+#else
+# include 
+# include 
+# include 
+# include "apps.h"
+# include 
+# include 
+# include 
+# include 
+# include 
+# include 
+
+/* Request nonce length, in bits (must be a multiple of 8). */
+# define NONCE_LENGTH            64
+
+/* Name of config entry that defines the OID file. */
+# define ENV_OID_FILE            "oid_file"
+
+/* Is |EXACTLY_ONE| of three pointers set? */
+# define EXACTLY_ONE(a, b, c) \
+        (( a && !b && !c) || \
+         ( b && !a && !c) || \
+         ( c && !a && !b))
+
+static ASN1_OBJECT *txt2obj(const char *oid);
+static CONF *load_config_file(const char *configfile);
+
+/* Query related functions. */
+static int query_command(const char *data, const char *digest,
+                         const EVP_MD *md, const char *policy, int no_nonce,
+                         int cert, const char *in, const char *out, int text);
+static TS_REQ *create_query(BIO *data_bio, const char *digest, const EVP_MD *md,
+                            const char *policy, int no_nonce, int cert);
+static int create_digest(BIO *input, const char *digest,
+                         const EVP_MD *md, unsigned char **md_value);
+static ASN1_INTEGER *create_nonce(int bits);
+
+/* Reply related functions. */
+static int reply_command(CONF *conf, const char *section, const char *engine,
+                         const char *queryfile, const char *passin, const char *inkey,
+                         const EVP_MD *md, const char *signer, const char *chain,
+                         const char *policy, const char *in, int token_in,
+                         const char *out, int token_out, int text);
+static TS_RESP *read_PKCS7(BIO *in_bio);
+static TS_RESP *create_response(CONF *conf, const char *section, const char *engine,
+                                const char *queryfile, const char *passin,
+                                const char *inkey, const EVP_MD *md, const char *signer,
+                                const char *chain, const char *policy);
+static ASN1_INTEGER *serial_cb(TS_RESP_CTX *ctx, void *data);
+static ASN1_INTEGER *next_serial(const char *serialfile);
+static int save_ts_serial(const char *serialfile, ASN1_INTEGER *serial);
+
+/* Verify related functions. */
+static int verify_command(const char *data, const char *digest, const char *queryfile,
+                          const char *in, int token_in,
+                          const char *CApath, const char *CAfile, const char *untrusted,
+                          X509_VERIFY_PARAM *vpm);
+static TS_VERIFY_CTX *create_verify_ctx(const char *data, const char *digest,
+                                        const char *queryfile,
+                                        const char *CApath, const char *CAfile,
+                                        const char *untrusted,
+                                        X509_VERIFY_PARAM *vpm);
+static X509_STORE *create_cert_store(const char *CApath, const char *CAfile,
+                                     X509_VERIFY_PARAM *vpm);
+static int verify_cb(int ok, X509_STORE_CTX *ctx);
+
+typedef enum OPTION_choice {
+    OPT_ERR = -1, OPT_EOF = 0, OPT_HELP,
+    OPT_ENGINE, OPT_CONFIG, OPT_SECTION, OPT_QUERY, OPT_DATA,
+    OPT_DIGEST, OPT_RAND, OPT_TSPOLICY, OPT_NO_NONCE, OPT_CERT,
+    OPT_IN, OPT_TOKEN_IN, OPT_OUT, OPT_TOKEN_OUT, OPT_TEXT,
+    OPT_REPLY, OPT_QUERYFILE, OPT_PASSIN, OPT_INKEY, OPT_SIGNER,
+    OPT_CHAIN, OPT_VERIFY, OPT_CAPATH, OPT_CAFILE, OPT_UNTRUSTED,
+    OPT_MD, OPT_V_ENUM
+} OPTION_CHOICE;
+
+OPTIONS ts_options[] = {
+    {"help", OPT_HELP, '-', "Display this summary"},
+    {"config", OPT_CONFIG, '<', "Configuration file"},
+    {"section", OPT_SECTION, 's', "Section to use within config file"},
+    {"query", OPT_QUERY, '-', "Generate a TS query"},
+    {"data", OPT_DATA, '<', "File to hash"},
+    {"digest", OPT_DIGEST, 's', "Digest (as a hex string)"},
+    {"rand", OPT_RAND, 's',
+     "Load the file(s) into the random number generator"},
+    {"tspolicy", OPT_TSPOLICY, 's', "Policy OID to use"},
+    {"no_nonce", OPT_NO_NONCE, '-', "Do not include a nonce"},
+    {"cert", OPT_CERT, '-', "Put cert request into query"},
+    {"in", OPT_IN, '<', "Input file"},
+    {"token_in", OPT_TOKEN_IN, '-', "Input is a PKCS#7 file"},
+    {"out", OPT_OUT, '>', "Output file"},
+    {"token_out", OPT_TOKEN_OUT, '-', "Output is a PKCS#7 file"},
+    {"text", OPT_TEXT, '-', "Output text (not DER)"},
+    {"reply", OPT_REPLY, '-', "Generate a TS reply"},
+    {"queryfile", OPT_QUERYFILE, '<', "File containing a TS query"},
+    {"passin", OPT_PASSIN, 's', "Input file pass phrase source"},
+    {"inkey", OPT_INKEY, '<', "File with private key for reply"},
+    {"signer", OPT_SIGNER, 's', "Signer certificate file"},
+    {"chain", OPT_CHAIN, '<', "File with signer CA chain"},
+    {"verify", OPT_VERIFY, '-', "Verify a TS response"},
+    {"CApath", OPT_CAPATH, '/', "Path to trusted CA files"},
+    {"CAfile", OPT_CAFILE, '<', "File with trusted CA certs"},
+    {"untrusted", OPT_UNTRUSTED, '<', "File with untrusted certs"},
+    {"", OPT_MD, '-', "Any supported digest"},
+# ifndef OPENSSL_NO_ENGINE
+    {"engine", OPT_ENGINE, 's', "Use engine, possibly a hardware device"},
+# endif
+    {OPT_HELP_STR, 1, '-', "\nOptions specific to 'ts -verify': \n"},
+    OPT_V_OPTIONS,
+    {OPT_HELP_STR, 1, '-', "\n"},
+    {NULL}
+};
+
+/*
+ * This command is so complex, special help is needed.
+ */
+static char* opt_helplist[] = {
+    "Typical uses:",
+    "ts -query [-rand file...] [-config file] [-data file]",
+    "          [-digest hexstring] [-tspolicy oid] [-no_nonce] [-cert]",
+    "          [-in file] [-out file] [-text]",
+    "  or",
+    "ts -reply [-config file] [-section tsa_section]",
+    "          [-queryfile file] [-passin password]",
+    "          [-signer tsa_cert.pem] [-inkey private_key.pem]",
+    "          [-chain certs_file.pem] [-tspolicy oid]",
+    "          [-in file] [-token_in] [-out file] [-token_out]",
+# ifndef OPENSSL_NO_ENGINE
+    "          [-text] [-engine id]",
+# else
+    "          [-text]",
+# endif
+    "  or",
+    "ts -verify -CApath dir -CAfile file.pem -untrusted file.pem",
+    "           [-data file] [-digest hexstring]",
+    "           [-queryfile file] -in file [-token_in]",
+    "           [[options specific to 'ts -verify']]",
+    NULL,
+};
+
+int ts_main(int argc, char **argv)
+{
+    CONF *conf = NULL;
+    const char *CAfile = NULL, *untrusted = NULL, *prog;
+    const char *configfile = default_config_file, *engine = NULL;
+    const char *section = NULL;
+    char **helpp;
+    char *password = NULL;
+    char *data = NULL, *digest = NULL, *rnd = NULL, *policy = NULL;
+    char *in = NULL, *out = NULL, *queryfile = NULL, *passin = NULL;
+    char *inkey = NULL, *signer = NULL, *chain = NULL, *CApath = NULL;
+    const EVP_MD *md = NULL;
+    OPTION_CHOICE o, mode = OPT_ERR;
+    int ret = 1, no_nonce = 0, cert = 0, text = 0;
+    int vpmtouched = 0;
+    X509_VERIFY_PARAM *vpm = NULL;
+    /* Input is ContentInfo instead of TimeStampResp. */
+    int token_in = 0;
+    /* Output is ContentInfo instead of TimeStampResp. */
+    int token_out = 0;
+
+    if ((vpm = X509_VERIFY_PARAM_new()) == NULL)
+        goto end;
+
+    prog = opt_init(argc, argv, ts_options);
+    while ((o = opt_next()) != OPT_EOF) {
+        switch (o) {
+        case OPT_EOF:
+        case OPT_ERR:
+ opthelp:
+            BIO_printf(bio_err, "%s: Use -help for summary.\n", prog);
+            goto end;
+        case OPT_HELP:
+            opt_help(ts_options);
+            for (helpp = opt_helplist; *helpp; ++helpp)
+                BIO_printf(bio_err, "%s\n", *helpp);
+            ret = 0;
+            goto end;
+        case OPT_CONFIG:
+            configfile = opt_arg();
+            break;
+        case OPT_SECTION:
+            section = opt_arg();
+            break;
+        case OPT_QUERY:
+        case OPT_REPLY:
+        case OPT_VERIFY:
+            if (mode != OPT_ERR)
+                goto opthelp;
+            mode = o;
+            break;
+        case OPT_DATA:
+            data = opt_arg();
+            break;
+        case OPT_DIGEST:
+            digest = opt_arg();
+            break;
+        case OPT_RAND:
+            rnd = opt_arg();
+            break;
+        case OPT_TSPOLICY:
+            policy = opt_arg();
+            break;
+        case OPT_NO_NONCE:
+            no_nonce = 1;
+            break;
+        case OPT_CERT:
+            cert = 1;
+            break;
+        case OPT_IN:
+            in = opt_arg();
+            break;
+        case OPT_TOKEN_IN:
+            token_in = 1;
+            break;
+        case OPT_OUT:
+            out = opt_arg();
+            break;
+        case OPT_TOKEN_OUT:
+            token_out = 1;
+            break;
+        case OPT_TEXT:
+            text = 1;
+            break;
+        case OPT_QUERYFILE:
+            queryfile = opt_arg();
+            break;
+        case OPT_PASSIN:
+            passin = opt_arg();
+            break;
+        case OPT_INKEY:
+            inkey = opt_arg();
+            break;
+        case OPT_SIGNER:
+            signer = opt_arg();
+            break;
+        case OPT_CHAIN:
+            chain = opt_arg();
+            break;
+        case OPT_CAPATH:
+            CApath = opt_arg();
+            break;
+        case OPT_CAFILE:
+            CAfile = opt_arg();
+            break;
+        case OPT_UNTRUSTED:
+            untrusted = opt_arg();
+            break;
+        case OPT_ENGINE:
+            engine = opt_arg();
+            break;
+        case OPT_MD:
+            if (!opt_md(opt_unknown(), &md))
+                goto opthelp;
+            break;
+        case OPT_V_CASES:
+            if (!opt_verify(o, vpm))
+                goto end;
+            vpmtouched++;
+            break;
+        }
+    }
+    if (mode == OPT_ERR || opt_num_rest() != 0)
+        goto opthelp;
+
+    /* Seed the random number generator if it is going to be used. */
+    if (mode == OPT_QUERY && !no_nonce) {
+        if (!app_RAND_load_file(NULL, 1) && rnd == NULL)
+            BIO_printf(bio_err, "warning, not much extra random "
+                       "data, consider using the -rand option\n");
+        if (rnd != NULL)
+            BIO_printf(bio_err, "%ld semi-random bytes loaded\n",
+                       app_RAND_load_files(rnd));
+    }
+
+    if (mode == OPT_REPLY && passin &&
+        !app_passwd(passin, NULL, &password, NULL)) {
+        BIO_printf(bio_err, "Error getting password.\n");
+        goto end;
+    }
+
+    conf = load_config_file(configfile);
+    if (configfile != default_config_file && !app_load_modules(conf))
+        goto end;
+
+    /* Check parameter consistency and execute the appropriate function. */
+    switch (mode) {
+    default:
+    case OPT_ERR:
+        goto opthelp;
+    case OPT_QUERY:
+        if (vpmtouched)
+            goto opthelp;
+        if ((data != NULL) && (digest != NULL))
+            goto opthelp;
+        ret = !query_command(data, digest, md, policy, no_nonce, cert,
+                             in, out, text);
+        break;
+    case OPT_REPLY:
+        if (vpmtouched)
+            goto opthelp;
+        if ((in != NULL) && (queryfile != NULL))
+            goto opthelp;
+        if (in == NULL) {
+            if ((conf == NULL) || (token_in != 0))
+                goto opthelp;
+        }
+        ret = !reply_command(conf, section, engine, queryfile,
+                             password, inkey, md, signer, chain, policy,
+                             in, token_in, out, token_out, text);
+        break;
+    case OPT_VERIFY:
+        if ((in == NULL) || !EXACTLY_ONE(queryfile, data, digest))
+            goto opthelp;
+        ret = !verify_command(data, digest, queryfile, in, token_in,
+                              CApath, CAfile, untrusted,
+                              vpmtouched ? vpm : NULL);
+    }
+
+ end:
+    X509_VERIFY_PARAM_free(vpm);
+    app_RAND_write_file(NULL);
+    NCONF_free(conf);
+    OPENSSL_free(password);
+    return (ret);
+}
+
+/*
+ * Configuration file-related function definitions.
+ */
+
+static ASN1_OBJECT *txt2obj(const char *oid)
+{
+    ASN1_OBJECT *oid_obj = NULL;
+
+    if ((oid_obj = OBJ_txt2obj(oid, 0)) == NULL)
+        BIO_printf(bio_err, "cannot convert %s to OID\n", oid);
+
+    return oid_obj;
+}
+
+static CONF *load_config_file(const char *configfile)
+{
+    CONF *conf = app_load_config(configfile);
+
+    if (conf != NULL) {
+        const char *p;
+
+        BIO_printf(bio_err, "Using configuration from %s\n", configfile);
+        p = NCONF_get_string(conf, NULL, ENV_OID_FILE);
+        if (p != NULL) {
+            BIO *oid_bio = BIO_new_file(p, "r");
+            if (!oid_bio)
+                ERR_print_errors(bio_err);
+            else {
+                OBJ_create_objects(oid_bio);
+                BIO_free_all(oid_bio);
+            }
+        } else
+            ERR_clear_error();
+        if (!add_oid_section(conf))
+            ERR_print_errors(bio_err);
+    }
+    return conf;
+}
+
+/*
+ * Query-related method definitions.
+ */
+static int query_command(const char *data, const char *digest, const EVP_MD *md,
+                         const char *policy, int no_nonce,
+                         int cert, const char *in, const char *out, int text)
+{
+    int ret = 0;
+    TS_REQ *query = NULL;
+    BIO *in_bio = NULL;
+    BIO *data_bio = NULL;
+    BIO *out_bio = NULL;
+
+    /* Build query object. */
+    if (in != NULL) {
+        if ((in_bio = bio_open_default(in, 'r', FORMAT_ASN1)) == NULL)
+            goto end;
+        query = d2i_TS_REQ_bio(in_bio, NULL);
+    } else {
+        if (digest == NULL
+            && (data_bio = bio_open_default(data, 'r', FORMAT_ASN1)) == NULL)
+            goto end;
+        query = create_query(data_bio, digest, md, policy, no_nonce, cert);
+    }
+    if (query == NULL)
+        goto end;
+
+    if (text) {
+        if ((out_bio = bio_open_default(out, 'w', FORMAT_TEXT)) == NULL)
+            goto end;
+        if (!TS_REQ_print_bio(out_bio, query))
+            goto end;
+    } else {
+        if ((out_bio = bio_open_default(out, 'w', FORMAT_ASN1)) == NULL)
+            goto end;
+        if (!i2d_TS_REQ_bio(out_bio, query))
+            goto end;
+    }
+
+    ret = 1;
+
+ end:
+    ERR_print_errors(bio_err);
+    BIO_free_all(in_bio);
+    BIO_free_all(data_bio);
+    BIO_free_all(out_bio);
+    TS_REQ_free(query);
+    return ret;
+}
+
+static TS_REQ *create_query(BIO *data_bio, const char *digest, const EVP_MD *md,
+                            const char *policy, int no_nonce, int cert)
+{
+    int ret = 0;
+    TS_REQ *ts_req = NULL;
+    int len;
+    TS_MSG_IMPRINT *msg_imprint = NULL;
+    X509_ALGOR *algo = NULL;
+    unsigned char *data = NULL;
+    ASN1_OBJECT *policy_obj = NULL;
+    ASN1_INTEGER *nonce_asn1 = NULL;
+
+    if (md == NULL && (md = EVP_get_digestbyname("sha1")) == NULL)
+        goto err;
+    if ((ts_req = TS_REQ_new()) == NULL)
+        goto err;
+    if (!TS_REQ_set_version(ts_req, 1))
+        goto err;
+    if ((msg_imprint = TS_MSG_IMPRINT_new()) == NULL)
+        goto err;
+    if ((algo = X509_ALGOR_new()) == NULL)
+        goto err;
+    if ((algo->algorithm = OBJ_nid2obj(EVP_MD_type(md))) == NULL)
+        goto err;
+    if ((algo->parameter = ASN1_TYPE_new()) == NULL)
+        goto err;
+    algo->parameter->type = V_ASN1_NULL;
+    if (!TS_MSG_IMPRINT_set_algo(msg_imprint, algo))
+        goto err;
+    if ((len = create_digest(data_bio, digest, md, &data)) == 0)
+        goto err;
+    if (!TS_MSG_IMPRINT_set_msg(msg_imprint, data, len))
+        goto err;
+    if (!TS_REQ_set_msg_imprint(ts_req, msg_imprint))
+        goto err;
+    if (policy && (policy_obj = txt2obj(policy)) == NULL)
+        goto err;
+    if (policy_obj && !TS_REQ_set_policy_id(ts_req, policy_obj))
+        goto err;
+
+    /* Setting nonce if requested. */
+    if (!no_nonce && (nonce_asn1 = create_nonce(NONCE_LENGTH)) == NULL)
+        goto err;
+    if (nonce_asn1 && !TS_REQ_set_nonce(ts_req, nonce_asn1))
+        goto err;
+    if (!TS_REQ_set_cert_req(ts_req, cert))
+        goto err;
+
+    ret = 1;
+ err:
+    if (!ret) {
+        TS_REQ_free(ts_req);
+        ts_req = NULL;
+        BIO_printf(bio_err, "could not create query\n");
+        ERR_print_errors(bio_err);
+    }
+    TS_MSG_IMPRINT_free(msg_imprint);
+    X509_ALGOR_free(algo);
+    OPENSSL_free(data);
+    ASN1_OBJECT_free(policy_obj);
+    ASN1_INTEGER_free(nonce_asn1);
+    return ts_req;
+}
+
+static int create_digest(BIO *input, const char *digest, const EVP_MD *md,
+                         unsigned char **md_value)
+{
+    int md_value_len;
+    int rv = 0;
+    EVP_MD_CTX *md_ctx = NULL;
+
+    md_value_len = EVP_MD_size(md);
+    if (md_value_len < 0)
+        return 0;
+
+    if (input) {
+        unsigned char buffer[4096];
+        int length;
+
+        md_ctx = EVP_MD_CTX_new();
+        if (md_ctx == NULL)
+            return 0;
+        *md_value = app_malloc(md_value_len, "digest buffer");
+        if (!EVP_DigestInit(md_ctx, md))
+            goto err;
+        while ((length = BIO_read(input, buffer, sizeof(buffer))) > 0) {
+            if (!EVP_DigestUpdate(md_ctx, buffer, length))
+                goto err;
+        }
+        if (!EVP_DigestFinal(md_ctx, *md_value, NULL))
+            goto err;
+        md_value_len = EVP_MD_size(md);
+    } else {
+        long digest_len;
+        *md_value = OPENSSL_hexstr2buf(digest, &digest_len);
+        if (!*md_value || md_value_len != digest_len) {
+            OPENSSL_free(*md_value);
+            *md_value = NULL;
+            BIO_printf(bio_err, "bad digest, %d bytes "
+                       "must be specified\n", md_value_len);
+            return 0;
+        }
+    }
+    rv = md_value_len;
+ err:
+    EVP_MD_CTX_free(md_ctx);
+    return rv;
+}
+
+static ASN1_INTEGER *create_nonce(int bits)
+{
+    unsigned char buf[20];
+    ASN1_INTEGER *nonce = NULL;
+    int len = (bits - 1) / 8 + 1;
+    int i;
+
+    if (len > (int)sizeof(buf))
+        goto err;
+    if (RAND_bytes(buf, len) <= 0)
+        goto err;
+
+    /* Find the first non-zero byte and creating ASN1_INTEGER object. */
+    for (i = 0; i < len && !buf[i]; ++i)
+        continue;
+    if ((nonce = ASN1_INTEGER_new()) == NULL)
+        goto err;
+    OPENSSL_free(nonce->data);
+    nonce->length = len - i;
+    nonce->data = app_malloc(nonce->length + 1, "nonce buffer");
+    memcpy(nonce->data, buf + i, nonce->length);
+    return nonce;
+
+ err:
+    BIO_printf(bio_err, "could not create nonce\n");
+    ASN1_INTEGER_free(nonce);
+    return NULL;
+}
+
+/*
+ * Reply-related method definitions.
+ */
+
+static int reply_command(CONF *conf, const char *section, const char *engine,
+                         const char *queryfile, const char *passin, const char *inkey,
+                         const EVP_MD *md, const char *signer, const char *chain,
+                         const char *policy, const char *in, int token_in,
+                         const char *out, int token_out, int text)
+{
+    int ret = 0;
+    TS_RESP *response = NULL;
+    BIO *in_bio = NULL;
+    BIO *query_bio = NULL;
+    BIO *inkey_bio = NULL;
+    BIO *signer_bio = NULL;
+    BIO *out_bio = NULL;
+
+    if (in != NULL) {
+        if ((in_bio = BIO_new_file(in, "rb")) == NULL)
+            goto end;
+        if (token_in) {
+            response = read_PKCS7(in_bio);
+        } else {
+            response = d2i_TS_RESP_bio(in_bio, NULL);
+        }
+    } else {
+        response = create_response(conf, section, engine, queryfile,
+                                   passin, inkey, md, signer, chain, policy);
+        if (response)
+            BIO_printf(bio_err, "Response has been generated.\n");
+        else
+            BIO_printf(bio_err, "Response is not generated.\n");
+    }
+    if (response == NULL)
+        goto end;
+
+    /* Write response. */
+    if (text) {
+        if ((out_bio = bio_open_default(out, 'w', FORMAT_TEXT)) == NULL)
+        goto end;
+        if (token_out) {
+            TS_TST_INFO *tst_info = TS_RESP_get_tst_info(response);
+            if (!TS_TST_INFO_print_bio(out_bio, tst_info))
+                goto end;
+        } else {
+            if (!TS_RESP_print_bio(out_bio, response))
+                goto end;
+        }
+    } else {
+        if ((out_bio = bio_open_default(out, 'w', FORMAT_ASN1)) == NULL)
+            goto end;
+        if (token_out) {
+            PKCS7 *token = TS_RESP_get_token(response);
+            if (!i2d_PKCS7_bio(out_bio, token))
+                goto end;
+        } else {
+            if (!i2d_TS_RESP_bio(out_bio, response))
+                goto end;
+        }
+    }
+
+    ret = 1;
+
+ end:
+    ERR_print_errors(bio_err);
+    BIO_free_all(in_bio);
+    BIO_free_all(query_bio);
+    BIO_free_all(inkey_bio);
+    BIO_free_all(signer_bio);
+    BIO_free_all(out_bio);
+    TS_RESP_free(response);
+    return ret;
+}
+
+/* Reads a PKCS7 token and adds default 'granted' status info to it. */
+static TS_RESP *read_PKCS7(BIO *in_bio)
+{
+    int ret = 0;
+    PKCS7 *token = NULL;
+    TS_TST_INFO *tst_info = NULL;
+    TS_RESP *resp = NULL;
+    TS_STATUS_INFO *si = NULL;
+
+    if ((token = d2i_PKCS7_bio(in_bio, NULL)) == NULL)
+        goto end;
+    if ((tst_info = PKCS7_to_TS_TST_INFO(token)) == NULL)
+        goto end;
+    if ((resp = TS_RESP_new()) == NULL)
+        goto end;
+    if ((si = TS_STATUS_INFO_new()) == NULL)
+        goto end;
+    if (!TS_STATUS_INFO_set_status(si, TS_STATUS_GRANTED))
+        goto end;
+    if (!TS_RESP_set_status_info(resp, si))
+        goto end;
+    TS_RESP_set_tst_info(resp, token, tst_info);
+    token = NULL;               /* Ownership is lost. */
+    tst_info = NULL;            /* Ownership is lost. */
+    ret = 1;
+
+ end:
+    PKCS7_free(token);
+    TS_TST_INFO_free(tst_info);
+    if (!ret) {
+        TS_RESP_free(resp);
+        resp = NULL;
+    }
+    TS_STATUS_INFO_free(si);
+    return resp;
+}
+
+static TS_RESP *create_response(CONF *conf, const char *section, const char *engine,
+                                const char *queryfile, const char *passin,
+                                const char *inkey, const EVP_MD *md, const char *signer,
+                                const char *chain, const char *policy)
+{
+    int ret = 0;
+    TS_RESP *response = NULL;
+    BIO *query_bio = NULL;
+    TS_RESP_CTX *resp_ctx = NULL;
+
+    if ((query_bio = BIO_new_file(queryfile, "rb")) == NULL)
+        goto end;
+    if ((section = TS_CONF_get_tsa_section(conf, section)) == NULL)
+        goto end;
+    if ((resp_ctx = TS_RESP_CTX_new()) == NULL)
+        goto end;
+    if (!TS_CONF_set_serial(conf, section, serial_cb, resp_ctx))
+        goto end;
+# ifndef OPENSSL_NO_ENGINE
+    if (!TS_CONF_set_crypto_device(conf, section, engine))
+        goto end;
+# endif
+    if (!TS_CONF_set_signer_cert(conf, section, signer, resp_ctx))
+        goto end;
+    if (!TS_CONF_set_certs(conf, section, chain, resp_ctx))
+        goto end;
+    if (!TS_CONF_set_signer_key(conf, section, inkey, passin, resp_ctx))
+        goto end;
+
+    if (md) {
+        if (!TS_RESP_CTX_set_signer_digest(resp_ctx, md))
+            goto end;
+    } else if (!TS_CONF_set_signer_digest(conf, section, NULL, resp_ctx)) {
+            goto end;
+    }
+
+    if (!TS_CONF_set_def_policy(conf, section, policy, resp_ctx))
+        goto end;
+    if (!TS_CONF_set_policies(conf, section, resp_ctx))
+        goto end;
+    if (!TS_CONF_set_digests(conf, section, resp_ctx))
+        goto end;
+    if (!TS_CONF_set_accuracy(conf, section, resp_ctx))
+        goto end;
+    if (!TS_CONF_set_clock_precision_digits(conf, section, resp_ctx))
+        goto end;
+    if (!TS_CONF_set_ordering(conf, section, resp_ctx))
+        goto end;
+    if (!TS_CONF_set_tsa_name(conf, section, resp_ctx))
+        goto end;
+    if (!TS_CONF_set_ess_cert_id_chain(conf, section, resp_ctx))
+        goto end;
+    if ((response = TS_RESP_create_response(resp_ctx, query_bio)) == NULL)
+        goto end;
+    ret = 1;
+
+ end:
+    if (!ret) {
+        TS_RESP_free(response);
+        response = NULL;
+    }
+    TS_RESP_CTX_free(resp_ctx);
+    BIO_free_all(query_bio);
+    return response;
+}
+
+static ASN1_INTEGER *serial_cb(TS_RESP_CTX *ctx, void *data)
+{
+    const char *serial_file = (const char *)data;
+    ASN1_INTEGER *serial = next_serial(serial_file);
+
+    if (!serial) {
+        TS_RESP_CTX_set_status_info(ctx, TS_STATUS_REJECTION,
+                                    "Error during serial number "
+                                    "generation.");
+        TS_RESP_CTX_add_failure_info(ctx, TS_INFO_ADD_INFO_NOT_AVAILABLE);
+    } else
+        save_ts_serial(serial_file, serial);
+
+    return serial;
+}
+
+static ASN1_INTEGER *next_serial(const char *serialfile)
+{
+    int ret = 0;
+    BIO *in = NULL;
+    ASN1_INTEGER *serial = NULL;
+    BIGNUM *bn = NULL;
+
+    if ((serial = ASN1_INTEGER_new()) == NULL)
+        goto err;
+
+    if ((in = BIO_new_file(serialfile, "r")) == NULL) {
+        ERR_clear_error();
+        BIO_printf(bio_err, "Warning: could not open file %s for "
+                   "reading, using serial number: 1\n", serialfile);
+        if (!ASN1_INTEGER_set(serial, 1))
+            goto err;
+    } else {
+        char buf[1024];
+        if (!a2i_ASN1_INTEGER(in, serial, buf, sizeof(buf))) {
+            BIO_printf(bio_err, "unable to load number from %s\n",
+                       serialfile);
+            goto err;
+        }
+        if ((bn = ASN1_INTEGER_to_BN(serial, NULL)) == NULL)
+            goto err;
+        ASN1_INTEGER_free(serial);
+        serial = NULL;
+        if (!BN_add_word(bn, 1))
+            goto err;
+        if ((serial = BN_to_ASN1_INTEGER(bn, NULL)) == NULL)
+            goto err;
+    }
+    ret = 1;
+
+ err:
+    if (!ret) {
+        ASN1_INTEGER_free(serial);
+        serial = NULL;
+    }
+    BIO_free_all(in);
+    BN_free(bn);
+    return serial;
+}
+
+static int save_ts_serial(const char *serialfile, ASN1_INTEGER *serial)
+{
+    int ret = 0;
+    BIO *out = NULL;
+
+    if ((out = BIO_new_file(serialfile, "w")) == NULL)
+        goto err;
+    if (i2a_ASN1_INTEGER(out, serial) <= 0)
+        goto err;
+    if (BIO_puts(out, "\n") <= 0)
+        goto err;
+    ret = 1;
+ err:
+    if (!ret)
+        BIO_printf(bio_err, "could not save serial number to %s\n",
+                   serialfile);
+    BIO_free_all(out);
+    return ret;
+}
+
+
+/*
+ * Verify-related method definitions.
+ */
+
+static int verify_command(const char *data, const char *digest, const char *queryfile,
+                          const char *in, int token_in,
+                          const char *CApath, const char *CAfile, const char *untrusted,
+                          X509_VERIFY_PARAM *vpm)
+{
+    BIO *in_bio = NULL;
+    PKCS7 *token = NULL;
+    TS_RESP *response = NULL;
+    TS_VERIFY_CTX *verify_ctx = NULL;
+    int ret = 0;
+
+    if ((in_bio = BIO_new_file(in, "rb")) == NULL)
+        goto end;
+    if (token_in) {
+        if ((token = d2i_PKCS7_bio(in_bio, NULL)) == NULL)
+            goto end;
+    } else {
+        if ((response = d2i_TS_RESP_bio(in_bio, NULL)) == NULL)
+            goto end;
+    }
+
+    if ((verify_ctx = create_verify_ctx(data, digest, queryfile,
+                                        CApath, CAfile, untrusted,
+                                        vpm)) == NULL)
+        goto end;
+
+    ret = token_in
+        ? TS_RESP_verify_token(verify_ctx, token)
+        : TS_RESP_verify_response(verify_ctx, response);
+
+ end:
+    printf("Verification: ");
+    if (ret)
+        printf("OK\n");
+    else {
+        printf("FAILED\n");
+        ERR_print_errors(bio_err);
+    }
+
+    BIO_free_all(in_bio);
+    PKCS7_free(token);
+    TS_RESP_free(response);
+    TS_VERIFY_CTX_free(verify_ctx);
+    return ret;
+}
+
+static TS_VERIFY_CTX *create_verify_ctx(const char *data, const char *digest,
+                                        const char *queryfile,
+                                        const char *CApath, const char *CAfile,
+                                        const char *untrusted,
+                                        X509_VERIFY_PARAM *vpm)
+{
+    TS_VERIFY_CTX *ctx = NULL;
+    BIO *input = NULL;
+    TS_REQ *request = NULL;
+    int ret = 0;
+    int f = 0;
+
+    if (data != NULL || digest != NULL) {
+        if ((ctx = TS_VERIFY_CTX_new()) == NULL)
+            goto err;
+        f = TS_VFY_VERSION | TS_VFY_SIGNER;
+        if (data != NULL) {
+            f |= TS_VFY_DATA;
+            if (TS_VERIFY_CTX_set_data(ctx, BIO_new_file(data, "rb")) == NULL)
+                goto err;
+        } else if (digest != NULL) {
+            long imprint_len;
+            unsigned char *hexstr = OPENSSL_hexstr2buf(digest, &imprint_len);
+            f |= TS_VFY_IMPRINT;
+            if (TS_VERIFY_CTX_set_imprint(ctx, hexstr, imprint_len) == NULL) {
+                BIO_printf(bio_err, "invalid digest string\n");
+                goto err;
+            }
+        }
+
+    } else if (queryfile != NULL) {
+        if ((input = BIO_new_file(queryfile, "rb")) == NULL)
+            goto err;
+        if ((request = d2i_TS_REQ_bio(input, NULL)) == NULL)
+            goto err;
+        if ((ctx = TS_REQ_to_TS_VERIFY_CTX(request, NULL)) == NULL)
+            goto err;
+    } else
+        return NULL;
+
+    /* Add the signature verification flag and arguments. */
+    TS_VERIFY_CTX_add_flags(ctx, f | TS_VFY_SIGNATURE);
+
+    /* Initialising the X509_STORE object. */
+    if (TS_VERIFY_CTX_set_store(ctx, create_cert_store(CApath, CAfile, vpm))
+            == NULL)
+        goto err;
+
+    /* Loading untrusted certificates. */
+    if (untrusted
+        && TS_VERIFY_CTS_set_certs(ctx, TS_CONF_load_certs(untrusted)) == NULL)
+        goto err;
+    ret = 1;
+
+ err:
+    if (!ret) {
+        TS_VERIFY_CTX_free(ctx);
+        ctx = NULL;
+    }
+    BIO_free_all(input);
+    TS_REQ_free(request);
+    return ctx;
+}
+
+static X509_STORE *create_cert_store(const char *CApath, const char *CAfile,
+                                     X509_VERIFY_PARAM *vpm)
+{
+    X509_STORE *cert_ctx = NULL;
+    X509_LOOKUP *lookup = NULL;
+    int i;
+
+    cert_ctx = X509_STORE_new();
+    X509_STORE_set_verify_cb(cert_ctx, verify_cb);
+    if (CApath != NULL) {
+        lookup = X509_STORE_add_lookup(cert_ctx, X509_LOOKUP_hash_dir());
+        if (lookup == NULL) {
+            BIO_printf(bio_err, "memory allocation failure\n");
+            goto err;
+        }
+        i = X509_LOOKUP_add_dir(lookup, CApath, X509_FILETYPE_PEM);
+        if (!i) {
+            BIO_printf(bio_err, "Error loading directory %s\n", CApath);
+            goto err;
+        }
+    }
+
+    if (CAfile != NULL) {
+        lookup = X509_STORE_add_lookup(cert_ctx, X509_LOOKUP_file());
+        if (lookup == NULL) {
+            BIO_printf(bio_err, "memory allocation failure\n");
+            goto err;
+        }
+        i = X509_LOOKUP_load_file(lookup, CAfile, X509_FILETYPE_PEM);
+        if (!i) {
+            BIO_printf(bio_err, "Error loading file %s\n", CAfile);
+            goto err;
+        }
+    }
+
+    if (vpm != NULL)
+        X509_STORE_set1_param(cert_ctx, vpm);
+
+    return cert_ctx;
+
+ err:
+    X509_STORE_free(cert_ctx);
+    return NULL;
+}
+
+static int verify_cb(int ok, X509_STORE_CTX *ctx)
+{
+    return ok;
+}
+#endif  /* ndef OPENSSL_NO_TS */
diff --git a/apps/tsget.in b/apps/tsget.in
new file mode 100644
index 000000000..89d1bc7ea
--- /dev/null
+++ b/apps/tsget.in
@@ -0,0 +1,201 @@
+#!{- $config{hashbangperl} -}
+# Copyright (c) 2002 The OpenTSA Project. All rights reserved.
+# Copyright 2002-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+use strict;
+use IO::Handle;
+use Getopt::Std;
+use File::Basename;
+use WWW::Curl::Easy;
+
+use vars qw(%options);
+
+# Callback for reading the body.
+sub read_body {
+    my ($maxlength, $state) = @_;
+    my $return_data = "";
+    my $data_len = length ${$state->{data}};
+    if ($state->{bytes} < $data_len) {
+	$data_len = $data_len - $state->{bytes};
+	$data_len = $maxlength if $data_len > $maxlength;
+	$return_data = substr ${$state->{data}}, $state->{bytes}, $data_len;
+	$state->{bytes} += $data_len;
+    }
+    return $return_data;
+}
+
+# Callback for writing the body into a variable.
+sub write_body {
+    my ($data, $pointer) = @_;
+    ${$pointer} .= $data;
+    return length($data);
+}
+
+# Initialise a new Curl object.
+sub create_curl {
+    my $url = shift;
+
+    # Create Curl object.
+    my $curl = WWW::Curl::Easy::new();
+
+    # Error-handling related options.
+    $curl->setopt(CURLOPT_VERBOSE, 1) if $options{d};
+    $curl->setopt(CURLOPT_FAILONERROR, 1);
+    $curl->setopt(CURLOPT_USERAGENT,
+        "OpenTSA tsget.pl/openssl-{- $config{version} -}");
+
+    # Options for POST method.
+    $curl->setopt(CURLOPT_UPLOAD, 1);
+    $curl->setopt(CURLOPT_CUSTOMREQUEST, "POST");
+    $curl->setopt(CURLOPT_HTTPHEADER,
+		["Content-Type: application/timestamp-query",
+		"Accept: application/timestamp-reply,application/timestamp-response"]);
+    $curl->setopt(CURLOPT_READFUNCTION, \&read_body);
+    $curl->setopt(CURLOPT_HEADERFUNCTION, sub { return length($_[0]); });
+
+    # Options for getting the result.
+    $curl->setopt(CURLOPT_WRITEFUNCTION, \&write_body);
+
+    # SSL related options.
+    $curl->setopt(CURLOPT_SSLKEYTYPE, "PEM");
+    $curl->setopt(CURLOPT_SSL_VERIFYPEER, 1);	# Verify server's certificate.
+    $curl->setopt(CURLOPT_SSL_VERIFYHOST, 2);	# Check server's CN.
+    $curl->setopt(CURLOPT_SSLKEY, $options{k}) if defined($options{k});
+    $curl->setopt(CURLOPT_SSLKEYPASSWD, $options{p}) if defined($options{p});
+    $curl->setopt(CURLOPT_SSLCERT, $options{c}) if defined($options{c});
+    $curl->setopt(CURLOPT_CAINFO, $options{C}) if defined($options{C});
+    $curl->setopt(CURLOPT_CAPATH, $options{P}) if defined($options{P});
+    $curl->setopt(CURLOPT_RANDOM_FILE, $options{r}) if defined($options{r});
+    $curl->setopt(CURLOPT_EGDSOCKET, $options{g}) if defined($options{g});
+
+    # Setting destination.
+    $curl->setopt(CURLOPT_URL, $url);
+
+    return $curl;
+}
+
+# Send a request and returns the body back.
+sub get_timestamp {
+    my $curl = shift;
+    my $body = shift;
+    my $ts_body;
+    local $::error_buf;
+
+    # Error-handling related options.
+    $curl->setopt(CURLOPT_ERRORBUFFER, "::error_buf");
+
+    # Options for POST method.
+    $curl->setopt(CURLOPT_INFILE, {data => $body, bytes => 0});
+    $curl->setopt(CURLOPT_INFILESIZE, length(${$body}));
+
+    # Options for getting the result.
+    $curl->setopt(CURLOPT_FILE, \$ts_body);
+
+    # Send the request...
+    my $error_code = $curl->perform();
+    my $error_string;
+    if ($error_code != 0) {
+        my $http_code = $curl->getinfo(CURLINFO_HTTP_CODE);
+	$error_string = "could not get timestamp";
+	$error_string .= ", http code: $http_code" unless $http_code == 0;
+	$error_string .= ", curl code: $error_code";
+	$error_string .= " ($::error_buf)" if defined($::error_buf);
+    } else {
+        my $ct = $curl->getinfo(CURLINFO_CONTENT_TYPE);
+	if (lc($ct) ne "application/timestamp-reply"
+	    && lc($ct) ne "application/timestamp-response") {
+	    $error_string = "unexpected content type returned: $ct";
+        }
+    }
+    return ($ts_body, $error_string);
+
+}
+
+# Print usage information and exists.
+sub usage {
+
+    print STDERR "usage: $0 -h  [-e ] [-o ] ";
+    print STDERR "[-v] [-d] [-k ] [-p ] ";
+    print STDERR "[-c ] [-C ] [-P ] ";
+    print STDERR "[-r ] [-g ] []...\n";
+    exit 1;
+}
+
+# ----------------------------------------------------------------------
+#   Main program
+# ----------------------------------------------------------------------
+
+# Getting command-line options (default comes from TSGET environment variable).
+my $getopt_arg =  "h:e:o:vdk:p:c:C:P:r:g:";
+if (exists $ENV{TSGET}) {
+    my @old_argv = @ARGV;
+    @ARGV = split /\s+/, $ENV{TSGET};
+    getopts($getopt_arg, \%options) or usage;
+    @ARGV = @old_argv;
+}
+getopts($getopt_arg, \%options) or usage;
+
+# Checking argument consistency.
+if (!exists($options{h}) || (@ARGV == 0 && !exists($options{o}))
+    || (@ARGV > 1 && exists($options{o}))) {
+    print STDERR "Inconsistent command line options.\n";
+    usage;
+}
+# Setting defaults.
+@ARGV = ("-") unless @ARGV != 0;
+$options{e} = ".tsr" unless defined($options{e});
+
+# Processing requests.
+my $curl = create_curl $options{h};
+undef $/;   # For reading whole files.
+REQUEST: foreach (@ARGV) {
+    my $input = $_;
+    my ($base, $path) = fileparse($input, '\.[^.]*');
+    my $output_base = $base . $options{e};
+    my $output = defined($options{o}) ? $options{o} : $path . $output_base;
+
+    STDERR->printflush("$input: ") if $options{v};
+    # Read request.
+    my $body;
+    if ($input eq "-") {
+	# Read the request from STDIN;
+	$body = ;
+    } else {
+	# Read the request from file.
+        open INPUT, "<" . $input
+	    or warn("$input: could not open input file: $!\n"), next REQUEST;
+        $body = ;
+        close INPUT
+	    or warn("$input: could not close input file: $!\n"), next REQUEST;
+    }
+
+    # Send request.
+    STDERR->printflush("sending request") if $options{v};
+
+    my ($ts_body, $error) = get_timestamp $curl, \$body;
+    if (defined($error)) {
+	die "$input: fatal error: $error\n";
+    }
+    STDERR->printflush(", reply received") if $options{v};
+
+    # Write response.
+    if ($output eq "-") {
+	# Write to STDOUT.
+        print $ts_body;
+    } else {
+	# Write to file.
+        open OUTPUT, ">", $output
+	    or warn("$output: could not open output file: $!\n"), next REQUEST;
+        print OUTPUT $ts_body;
+        close OUTPUT
+	    or warn("$output: could not close output file: $!\n"), next REQUEST;
+    }
+    STDERR->printflush(", $output written.\n") if $options{v};
+}
+$curl->cleanup();
+WWW::Curl::Easy::global_cleanup();
diff --git a/apps/verify.c b/apps/verify.c
new file mode 100644
index 000000000..3c4566393
--- /dev/null
+++ b/apps/verify.c
@@ -0,0 +1,311 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include 
+#include "apps.h"
+#include 
+#include 
+#include 
+#include 
+#include 
+
+static int cb(int ok, X509_STORE_CTX *ctx);
+static int check(X509_STORE *ctx, const char *file,
+                 STACK_OF(X509) *uchain, STACK_OF(X509) *tchain,
+                 STACK_OF(X509_CRL) *crls, int show_chain);
+static int v_verbose = 0, vflags = 0;
+
+typedef enum OPTION_choice {
+    OPT_ERR = -1, OPT_EOF = 0, OPT_HELP,
+    OPT_ENGINE, OPT_CAPATH, OPT_CAFILE, OPT_NOCAPATH, OPT_NOCAFILE,
+    OPT_UNTRUSTED, OPT_TRUSTED, OPT_CRLFILE, OPT_CRL_DOWNLOAD, OPT_SHOW_CHAIN,
+    OPT_V_ENUM,
+    OPT_VERBOSE
+} OPTION_CHOICE;
+
+OPTIONS verify_options[] = {
+    {OPT_HELP_STR, 1, '-', "Usage: %s [options] cert.pem...\n"},
+    {OPT_HELP_STR, 1, '-', "Valid options are:\n"},
+    {"help", OPT_HELP, '-', "Display this summary"},
+    {"verbose", OPT_VERBOSE, '-',
+        "Print extra information about the operations being performed."},
+    {"CApath", OPT_CAPATH, '/', "A directory of trusted certificates"},
+    {"CAfile", OPT_CAFILE, '<', "A file of trusted certificates"},
+    {"no-CAfile", OPT_NOCAFILE, '-',
+     "Do not load the default certificates file"},
+    {"no-CApath", OPT_NOCAPATH, '-',
+     "Do not load certificates from the default certificates directory"},
+    {"untrusted", OPT_UNTRUSTED, '<', "A file of untrusted certificates"},
+    {"trusted", OPT_TRUSTED, '<', "A file of trusted certificates"},
+    {"CRLfile", OPT_CRLFILE, '<',
+        "File containing one or more CRL's (in PEM format) to load"},
+    {"crl_download", OPT_CRL_DOWNLOAD, '-',
+        "Attempt to download CRL information for this certificate"},
+    {"show_chain", OPT_SHOW_CHAIN, '-',
+        "Display information about the certificate chain"},
+    OPT_V_OPTIONS,
+#ifndef OPENSSL_NO_ENGINE
+    {"engine", OPT_ENGINE, 's', "Use engine, possibly a hardware device"},
+#endif
+    {NULL}
+};
+
+int verify_main(int argc, char **argv)
+{
+    ENGINE *e = NULL;
+    STACK_OF(X509) *untrusted = NULL, *trusted = NULL;
+    STACK_OF(X509_CRL) *crls = NULL;
+    X509_STORE *store = NULL;
+    X509_VERIFY_PARAM *vpm = NULL;
+    const char *prog, *CApath = NULL, *CAfile = NULL;
+    int noCApath = 0, noCAfile = 0;
+    int vpmtouched = 0, crl_download = 0, show_chain = 0, i = 0, ret = 1;
+    OPTION_CHOICE o;
+
+    if ((vpm = X509_VERIFY_PARAM_new()) == NULL)
+        goto end;
+
+    prog = opt_init(argc, argv, verify_options);
+    while ((o = opt_next()) != OPT_EOF) {
+        switch (o) {
+        case OPT_EOF:
+        case OPT_ERR:
+            BIO_printf(bio_err, "%s: Use -help for summary.\n", prog);
+            goto end;
+        case OPT_HELP:
+            opt_help(verify_options);
+            BIO_printf(bio_err, "Recognized usages:\n");
+            for (i = 0; i < X509_PURPOSE_get_count(); i++) {
+                X509_PURPOSE *ptmp;
+                ptmp = X509_PURPOSE_get0(i);
+                BIO_printf(bio_err, "\t%-10s\t%s\n",
+                        X509_PURPOSE_get0_sname(ptmp),
+                        X509_PURPOSE_get0_name(ptmp));
+            }
+
+            BIO_printf(bio_err, "Recognized verify names:\n");
+            for (i = 0; i < X509_VERIFY_PARAM_get_count(); i++) {
+                const X509_VERIFY_PARAM *vptmp;
+                vptmp = X509_VERIFY_PARAM_get0(i);
+                BIO_printf(bio_err, "\t%-10s\n",
+                        X509_VERIFY_PARAM_get0_name(vptmp));
+            }
+            ret = 0;
+            goto end;
+        case OPT_V_CASES:
+            if (!opt_verify(o, vpm))
+                goto end;
+            vpmtouched++;
+            break;
+        case OPT_CAPATH:
+            CApath = opt_arg();
+            break;
+        case OPT_CAFILE:
+            CAfile = opt_arg();
+            break;
+        case OPT_NOCAPATH:
+            noCApath = 1;
+            break;
+        case OPT_NOCAFILE:
+            noCAfile = 1;
+            break;
+        case OPT_UNTRUSTED:
+            /* Zero or more times */
+            if (!load_certs(opt_arg(), &untrusted, FORMAT_PEM, NULL,
+                            "untrusted certificates"))
+                goto end;
+            break;
+        case OPT_TRUSTED:
+            /* Zero or more times */
+            noCAfile = 1;
+            noCApath = 1;
+            if (!load_certs(opt_arg(), &trusted, FORMAT_PEM, NULL,
+                            "trusted certificates"))
+                goto end;
+            break;
+        case OPT_CRLFILE:
+            /* Zero or more times */
+            if (!load_crls(opt_arg(), &crls, FORMAT_PEM, NULL,
+                           "other CRLs"))
+                goto end;
+            break;
+        case OPT_CRL_DOWNLOAD:
+            crl_download = 1;
+            break;
+        case OPT_ENGINE:
+            if ((e = setup_engine(opt_arg(), 0)) == NULL) {
+                /* Failure message already displayed */
+                goto end;
+            }
+            break;
+        case OPT_SHOW_CHAIN:
+            show_chain = 1;
+            break;
+        case OPT_VERBOSE:
+            v_verbose = 1;
+            break;
+        }
+    }
+    argc = opt_num_rest();
+    argv = opt_rest();
+    if (trusted != NULL && (CAfile || CApath)) {
+        BIO_printf(bio_err,
+                   "%s: Cannot use -trusted with -CAfile or -CApath\n",
+                   prog);
+        goto end;
+    }
+
+    if ((store = setup_verify(CAfile, CApath, noCAfile, noCApath)) == NULL)
+        goto end;
+    X509_STORE_set_verify_cb(store, cb);
+
+    if (vpmtouched)
+        X509_STORE_set1_param(store, vpm);
+
+    ERR_clear_error();
+
+    if (crl_download)
+        store_setup_crl_download(store);
+
+    ret = 0;
+    if (argc < 1) {
+        if (check(store, NULL, untrusted, trusted, crls, show_chain) != 1)
+            ret = -1;
+    } else {
+        for (i = 0; i < argc; i++)
+            if (check(store, argv[i], untrusted, trusted, crls,
+                      show_chain) != 1)
+                ret = -1;
+    }
+
+ end:
+    X509_VERIFY_PARAM_free(vpm);
+    X509_STORE_free(store);
+    sk_X509_pop_free(untrusted, X509_free);
+    sk_X509_pop_free(trusted, X509_free);
+    sk_X509_CRL_pop_free(crls, X509_CRL_free);
+    release_engine(e);
+    return (ret < 0 ? 2 : ret);
+}
+
+static int check(X509_STORE *ctx, const char *file,
+                 STACK_OF(X509) *uchain, STACK_OF(X509) *tchain,
+                 STACK_OF(X509_CRL) *crls, int show_chain)
+{
+    X509 *x = NULL;
+    int i = 0, ret = 0;
+    X509_STORE_CTX *csc;
+    STACK_OF(X509) *chain = NULL;
+    int num_untrusted;
+
+    x = load_cert(file, FORMAT_PEM, "certificate file");
+    if (x == NULL)
+        goto end;
+
+    csc = X509_STORE_CTX_new();
+    if (csc == NULL) {
+        printf("error %s: X.509 store context allocation failed\n",
+               (file == NULL) ? "stdin" : file);
+        goto end;
+    }
+
+    X509_STORE_set_flags(ctx, vflags);
+    if (!X509_STORE_CTX_init(csc, ctx, x, uchain)) {
+        printf("error %s: X.509 store context initialization failed\n",
+               (file == NULL) ? "stdin" : file);
+        goto end;
+    }
+    if (tchain)
+        X509_STORE_CTX_set0_trusted_stack(csc, tchain);
+    if (crls)
+        X509_STORE_CTX_set0_crls(csc, crls);
+    i = X509_verify_cert(csc);
+    if (i > 0 && X509_STORE_CTX_get_error(csc) == X509_V_OK) {
+        printf("%s: OK\n", (file == NULL) ? "stdin" : file);
+        ret = 1;
+        if (show_chain) {
+            int j;
+
+            chain = X509_STORE_CTX_get1_chain(csc);
+            num_untrusted = X509_STORE_CTX_get_num_untrusted(csc);
+            printf("Chain:\n");
+            for (j = 0; j < sk_X509_num(chain); j++) {
+                X509 *cert = sk_X509_value(chain, j);
+                printf("depth=%d: ", j);
+                X509_NAME_print_ex_fp(stdout,
+                                      X509_get_subject_name(cert),
+                                      0, XN_FLAG_ONELINE);
+                if (j < num_untrusted)
+                    printf(" (untrusted)");
+                printf("\n");
+            }
+            sk_X509_pop_free(chain, X509_free);
+        }
+    } else {
+        printf("error %s: verification failed\n", (file == NULL) ? "stdin" : file);
+    }
+    X509_STORE_CTX_free(csc);
+
+ end:
+    if (i <= 0)
+        ERR_print_errors(bio_err);
+    X509_free(x);
+
+    return ret;
+}
+
+static int cb(int ok, X509_STORE_CTX *ctx)
+{
+    int cert_error = X509_STORE_CTX_get_error(ctx);
+    X509 *current_cert = X509_STORE_CTX_get_current_cert(ctx);
+
+    if (!ok) {
+        if (current_cert) {
+            X509_NAME_print_ex(bio_err,
+                            X509_get_subject_name(current_cert),
+                            0, XN_FLAG_ONELINE);
+            BIO_printf(bio_err, "\n");
+        }
+        BIO_printf(bio_err, "%serror %d at %d depth lookup: %s\n",
+               X509_STORE_CTX_get0_parent_ctx(ctx) ? "[CRL path] " : "",
+               cert_error,
+               X509_STORE_CTX_get_error_depth(ctx),
+               X509_verify_cert_error_string(cert_error));
+        switch (cert_error) {
+        case X509_V_ERR_NO_EXPLICIT_POLICY:
+            policies_print(ctx);
+        case X509_V_ERR_CERT_HAS_EXPIRED:
+
+            /*
+             * since we are just checking the certificates, it is ok if they
+             * are self signed. But we should still warn the user.
+             */
+        case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
+            /* Continue after extension errors too */
+        case X509_V_ERR_INVALID_CA:
+        case X509_V_ERR_INVALID_NON_CA:
+        case X509_V_ERR_PATH_LENGTH_EXCEEDED:
+        case X509_V_ERR_INVALID_PURPOSE:
+        case X509_V_ERR_CRL_HAS_EXPIRED:
+        case X509_V_ERR_CRL_NOT_YET_VALID:
+        case X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION:
+            ok = 1;
+        }
+
+        return ok;
+
+    }
+    if (cert_error == X509_V_OK && ok == 2)
+        policies_print(ctx);
+    if (!v_verbose)
+        ERR_clear_error();
+    return (ok);
+}
diff --git a/apps/version.c b/apps/version.c
new file mode 100644
index 000000000..e3c8299fc
--- /dev/null
+++ b/apps/version.c
@@ -0,0 +1,140 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include 
+#include "apps.h"
+#include 
+#include 
+#include 
+#ifndef OPENSSL_NO_MD2
+# include 
+#endif
+#ifndef OPENSSL_NO_RC4
+# include 
+#endif
+#ifndef OPENSSL_NO_DES
+# include 
+#endif
+#ifndef OPENSSL_NO_IDEA
+# include 
+#endif
+#ifndef OPENSSL_NO_BF
+# include 
+#endif
+
+typedef enum OPTION_choice {
+    OPT_ERR = -1, OPT_EOF = 0, OPT_HELP,
+    OPT_B, OPT_D, OPT_E, OPT_F, OPT_O, OPT_P, OPT_V, OPT_A
+} OPTION_CHOICE;
+
+OPTIONS version_options[] = {
+    {"help", OPT_HELP, '-', "Display this summary"},
+    {"a", OPT_A, '-', "Show all data"},
+    {"b", OPT_B, '-', "Show build date"},
+    {"d", OPT_D, '-', "Show configuration directory"},
+    {"e", OPT_E, '-', "Show engines directory"},
+    {"f", OPT_F, '-', "Show compiler flags used"},
+    {"o", OPT_O, '-', "Show some internal datatype options"},
+    {"p", OPT_P, '-', "Show target build platform"},
+    {"v", OPT_V, '-', "Show library version"},
+    {NULL}
+};
+
+int version_main(int argc, char **argv)
+{
+    int ret = 1, dirty = 0;
+    int cflags = 0, version = 0, date = 0, options = 0, platform = 0, dir = 0;
+    int engdir = 0;
+    char *prog;
+    OPTION_CHOICE o;
+
+    prog = opt_init(argc, argv, version_options);
+    while ((o = opt_next()) != OPT_EOF) {
+        switch (o) {
+        case OPT_EOF:
+        case OPT_ERR:
+            BIO_printf(bio_err, "%s: Use -help for summary.\n", prog);
+            goto end;
+        case OPT_HELP:
+            opt_help(version_options);
+            ret = 0;
+            goto end;
+        case OPT_B:
+            dirty = date = 1;
+            break;
+        case OPT_D:
+            dirty = dir = 1;
+            break;
+        case OPT_E:
+            dirty = engdir = 1;
+            break;
+        case OPT_F:
+            dirty = cflags = 1;
+            break;
+        case OPT_O:
+            dirty = options = 1;
+            break;
+        case OPT_P:
+            dirty = platform = 1;
+            break;
+        case OPT_V:
+            dirty = version = 1;
+            break;
+        case OPT_A:
+            cflags = version = date = platform = dir = engdir = 1;
+            break;
+        }
+    }
+    if (!dirty)
+        version = 1;
+
+    if (version) {
+        if (OpenSSL_version_num() == OPENSSL_VERSION_NUMBER) {
+            printf("%s\n", OpenSSL_version(OPENSSL_VERSION));
+        } else {
+            printf("%s (Library: %s)\n",
+                   OPENSSL_VERSION_TEXT, OpenSSL_version(OPENSSL_VERSION));
+        }
+    }
+    if (date)
+        printf("%s\n", OpenSSL_version(OPENSSL_BUILT_ON));
+    if (platform)
+        printf("%s\n", OpenSSL_version(OPENSSL_PLATFORM));
+    if (options) {
+        printf("options:  ");
+        printf("%s ", BN_options());
+#ifndef OPENSSL_NO_MD2
+        printf("%s ", MD2_options());
+#endif
+#ifndef OPENSSL_NO_RC4
+        printf("%s ", RC4_options());
+#endif
+#ifndef OPENSSL_NO_DES
+        printf("%s ", DES_options());
+#endif
+#ifndef OPENSSL_NO_IDEA
+        printf("%s ", IDEA_options());
+#endif
+#ifndef OPENSSL_NO_BF
+        printf("%s ", BF_options());
+#endif
+        printf("\n");
+    }
+    if (cflags)
+        printf("%s\n", OpenSSL_version(OPENSSL_CFLAGS));
+    if (dir)
+        printf("%s\n", OpenSSL_version(OPENSSL_DIR));
+    if (engdir)
+        printf("%s\n", OpenSSL_version(OPENSSL_ENGINES_DIR));
+    ret = 0;
+ end:
+    return (ret);
+}
diff --git a/apps/vms_decc_init.c b/apps/vms_decc_init.c
new file mode 100644
index 000000000..f83f7168e
--- /dev/null
+++ b/apps/vms_decc_init.c
@@ -0,0 +1,214 @@
+/*
+ * Copyright 2010-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#if defined( __VMS) && !defined( OPENSSL_NO_DECC_INIT) && \
+ defined( __DECC) && !defined( __VAX) && (__CRTL_VER >= 70301000)
+# define USE_DECC_INIT 1
+#endif
+
+#ifdef USE_DECC_INIT
+
+/*
+ * ----------------------------------------------------------------------
+ * decc_init() On non-VAX systems, uses LIB$INITIALIZE to set a collection
+ * of C RTL features without using the DECC$* logical name method.
+ * ----------------------------------------------------------------------
+ */
+
+# include 
+# include 
+# include 
+
+# include "apps.h"
+
+/* Global storage. */
+
+/* Flag to sense if decc_init() was called. */
+
+int decc_init_done = -1;
+
+/* Structure to hold a DECC$* feature name and its desired value. */
+
+typedef struct {
+    char *name;
+    int value;
+} decc_feat_t;
+
+/*
+ * Array of DECC$* feature names and their desired values. Note:
+ * DECC$ARGV_PARSE_STYLE is the urgent one.
+ */
+
+decc_feat_t decc_feat_array[] = {
+    /* Preserve command-line case with SET PROCESS/PARSE_STYLE=EXTENDED */
+    {"DECC$ARGV_PARSE_STYLE", 1},
+
+    /* Preserve case for file names on ODS5 disks. */
+    {"DECC$EFS_CASE_PRESERVE", 1},
+
+    /*
+     * Enable multiple dots (and most characters) in ODS5 file names, while
+     * preserving VMS-ness of ";version".
+     */
+    {"DECC$EFS_CHARSET", 1},
+
+    /* List terminator. */
+    {(char *)NULL, 0}
+};
+
+
+char **copy_argv(int *argc, char *argv[])
+{
+    /*-
+     * The note below is for historical purpose.  On VMS now we always
+     * copy argv "safely."
+     *
+     * 2011-03-22 SMS.
+     * If we have 32-bit pointers everywhere, then we're safe, and
+     * we bypass this mess, as on non-VMS systems.
+     * Problem 1: Compaq/HP C before V7.3 always used 32-bit
+     * pointers for argv[].
+     * Fix 1: For a 32-bit argv[], when we're using 64-bit pointers
+     * everywhere else, we always allocate and use a 64-bit
+     * duplicate of argv[].
+     * Problem 2: Compaq/HP C V7.3 (Alpha, IA64) before ECO1 failed
+     * to NULL-terminate a 64-bit argv[].  (As this was written, the
+     * compiler ECO was available only on IA64.)
+     * Fix 2: Unless advised not to (VMS_TRUST_ARGV), we test a
+     * 64-bit argv[argc] for NULL, and, if necessary, use a
+     * (properly) NULL-terminated (64-bit) duplicate of argv[].
+     * The same code is used in either case to duplicate argv[].
+     * Some of these decisions could be handled in preprocessing,
+     * but the code tends to get even uglier, and the penalty for
+     * deciding at compile- or run-time is tiny.
+     */
+
+    int i, count = *argc;
+    char **newargv = app_malloc(sizeof(*newargv) * (count + 1), "argv copy");
+
+    for (i = 0; i < count; i++)
+        newargv[i] = argv[i];
+    newargv[i] = NULL;
+    *argc = i;
+    return newargv;
+}
+
+/* LIB$INITIALIZE initialization function. */
+
+static void decc_init(void)
+{
+    char *openssl_debug_decc_init;
+    int verbose = 0;
+    int feat_index;
+    int feat_value;
+    int feat_value_max;
+    int feat_value_min;
+    int i;
+    int sts;
+
+    /* Get debug option. */
+    openssl_debug_decc_init = getenv("OPENSSL_DEBUG_DECC_INIT");
+    if (openssl_debug_decc_init != NULL) {
+        verbose = strtol(openssl_debug_decc_init, NULL, 10);
+        if (verbose <= 0) {
+            verbose = 1;
+        }
+    }
+
+    /* Set the global flag to indicate that LIB$INITIALIZE worked. */
+    decc_init_done = 1;
+
+    /* Loop through all items in the decc_feat_array[]. */
+
+    for (i = 0; decc_feat_array[i].name != NULL; i++) {
+        /* Get the feature index. */
+        feat_index = decc$feature_get_index(decc_feat_array[i].name);
+        if (feat_index >= 0) {
+            /* Valid item.  Collect its properties. */
+            feat_value = decc$feature_get_value(feat_index, 1);
+            feat_value_min = decc$feature_get_value(feat_index, 2);
+            feat_value_max = decc$feature_get_value(feat_index, 3);
+
+            /* Check the validity of our desired value. */
+            if ((decc_feat_array[i].value >= feat_value_min) &&
+                (decc_feat_array[i].value <= feat_value_max)) {
+                /* Valid value.  Set it if necessary. */
+                if (feat_value != decc_feat_array[i].value) {
+                    sts = decc$feature_set_value(feat_index,
+                                                 1, decc_feat_array[i].value);
+
+                    if (verbose > 1) {
+                        fprintf(stderr, " %s = %d, sts = %d.\n",
+                                decc_feat_array[i].name,
+                                decc_feat_array[i].value, sts);
+                    }
+                }
+            } else {
+                /* Invalid DECC feature value. */
+                fprintf(stderr,
+                        " INVALID DECC$FEATURE VALUE, %d: %d <= %s <= %d.\n",
+                        feat_value,
+                        feat_value_min, decc_feat_array[i].name,
+                        feat_value_max);
+            }
+        } else {
+            /* Invalid DECC feature name. */
+            fprintf(stderr,
+                    " UNKNOWN DECC$FEATURE: %s.\n", decc_feat_array[i].name);
+        }
+    }
+
+    if (verbose > 0) {
+        fprintf(stderr, " DECC_INIT complete.\n");
+    }
+}
+
+/* Get "decc_init()" into a valid, loaded LIB$INITIALIZE PSECT. */
+
+# pragma nostandard
+
+/*
+ * Establish the LIB$INITIALIZE PSECTs, with proper alignment and other
+ * attributes.  Note that "nopic" is significant only on VAX.
+ */
+# pragma extern_model save
+
+# if __INITIAL_POINTER_SIZE == 64
+#  define PSECT_ALIGN 3
+# else
+#  define PSECT_ALIGN 2
+# endif
+
+# pragma extern_model strict_refdef "LIB$INITIALIZ" PSECT_ALIGN, nopic, nowrt
+const int spare[8] = { 0 };
+
+# pragma extern_model strict_refdef "LIB$INITIALIZE" PSECT_ALIGN, nopic, nowrt
+void (*const x_decc_init) () = decc_init;
+
+# pragma extern_model restore
+
+/* Fake reference to ensure loading the LIB$INITIALIZE PSECT. */
+
+# pragma extern_model save
+
+int LIB$INITIALIZE(void);
+
+# pragma extern_model strict_refdef
+int dmy_lib$initialize = (int)LIB$INITIALIZE;
+
+# pragma extern_model restore
+
+# pragma standard
+
+#else                           /* def USE_DECC_INIT */
+
+/* Dummy code to avoid a %CC-W-EMPTYFILE complaint. */
+int decc_init_dummy(void);
+
+#endif                          /* def USE_DECC_INIT */
diff --git a/apps/vms_term_sock.c b/apps/vms_term_sock.c
new file mode 100644
index 000000000..a7d87ff36
--- /dev/null
+++ b/apps/vms_term_sock.c
@@ -0,0 +1,590 @@
+/*
+ * Copyright 2016 VMS Software, Inc. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#ifdef __VMS
+# define OPENSSL_SYS_VMS
+# pragma message disable DOLLARID
+
+
+# include 
+
+# if !defined(_POSIX_C_SOURCE) && defined(OPENSSL_SYS_VMS)
+/*
+ * On VMS, you need to define this to get the declaration of fileno().  The
+ * value 2 is to make sure no function defined in POSIX-2 is left undefined.
+ */
+#  define _POSIX_C_SOURCE 2
+# endif
+
+# include 
+
+# undef _POSIX_C_SOURCE
+
+# include 
+# include 
+# include 
+# include 
+# include 
+# include 
+# include 
+# include 
+# include 
+# ifdef __alpha
+#  include 
+# else
+typedef struct _iosb {           /* Copied from IOSBDEF.H for Alpha  */
+#  pragma __nomember_alignment
+    __union  {
+        __struct  {
+            unsigned short int iosb$w_status; /* Final I/O status           */
+            __union  {
+                __struct  {             /* 16-bit byte count variant        */
+                    unsigned short int iosb$w_bcnt; /* 16-bit byte count    */
+                    __union  {
+                        unsigned int iosb$l_dev_depend; /* 32-bit device dependent info */
+                        unsigned int iosb$l_pid; /* 32-bit pid              */
+                    } iosb$r_l;
+                } iosb$r_bcnt_16;
+                __struct  {             /* 32-bit byte count variant        */
+                    unsigned int iosb$l_bcnt; /* 32-bit byte count (unaligned) */
+                    unsigned short int iosb$w_dev_depend_high; /* 16-bit device dependent info */
+                } iosb$r_bcnt_32;
+            } iosb$r_devdepend;
+        } iosb$r_io_64;
+        __struct  {
+            __union  {
+                unsigned int iosb$l_getxxi_status; /* Final GETxxI status   */
+                unsigned int iosb$l_reg_status; /* Final $Registry status   */
+            } iosb$r_l_status;
+            unsigned int iosb$l_reserved; /* Reserved field                 */
+        } iosb$r_get_64;
+    } iosb$r_io_get;
+} IOSB;
+
+#  if !defined(__VAXC)
+#   define iosb$w_status iosb$r_io_get.iosb$r_io_64.iosb$w_status
+#   define iosb$w_bcnt iosb$r_io_get.iosb$r_io_64.iosb$r_devdepend.iosb$r_bcnt_16.iosb$w_bcnt
+#   define iosb$r_l        iosb$r_io_get.iosb$r_io_64.iosb$r_devdepend.iosb$r_bcnt_16.iosb$r_l
+#   define iosb$l_dev_depend iosb$r_l.iosb$l_dev_depend
+#   define iosb$l_pid iosb$r_l.iosb$l_pid
+#   define iosb$l_bcnt iosb$r_io_get.iosb$r_io_64.iosb$r_devdepend.iosb$r_bcnt_32.iosb$l_bcnt
+#   define iosb$w_dev_depend_high iosb$r_io_get.iosb$r_io_64.iosb$r_devdepend.iosb$r_bcnt_32.iosb$w_dev_depend_high
+#   define iosb$l_getxxi_status iosb$r_io_get.iosb$r_get_64.iosb$r_l_status.iosb$l_getxxi_status
+#   define iosb$l_reg_status iosb$r_io_get.iosb$r_get_64.iosb$r_l_status.iosb$l_reg_status
+#  endif          /* #if !defined(__VAXC) */
+
+# endif  /* End of IOSBDEF */
+
+# include 
+# include 
+# include 
+# include 
+# include 
+# include 
+
+# include "vms_term_sock.h"
+
+# ifdef __alpha
+static struct _iosb TerminalDeviceIosb;
+# else
+IOSB TerminalDeviceIosb;
+# endif
+
+static char TerminalDeviceBuff[255 + 2];
+static int TerminalSocketPair[2] = {0, 0};
+static unsigned short TerminalDeviceChan = 0;
+
+static int CreateSocketPair (int, int, int, int *);
+static void SocketPairTimeoutAst (int);
+static int TerminalDeviceAst (int);
+static void LogMessage (char *, ...);
+
+/*
+** Socket Pair Timeout Value (must be 0-59 seconds)
+*/
+# define SOCKET_PAIR_TIMEOUT_VALUE 20
+
+/*
+** Socket Pair Timeout Block which is passed to timeout AST
+*/
+typedef struct _SocketPairTimeoutBlock {
+    unsigned short SockChan1;
+    unsigned short SockChan2;
+} SPTB;
+
+# ifdef TERM_SOCK_TEST
+
+/*----------------------------------------------------------------------------*/
+/*                                                                            */
+/*----------------------------------------------------------------------------*/
+int main (int argc, char *argv[], char *envp[])
+{
+    char TermBuff[80];
+    int TermSock,
+        status,
+        len;
+
+    LogMessage ("Enter 'q' or 'Q' to quit ...");
+    while (strcasecmp (TermBuff, "Q")) {
+        /*
+        ** Create the terminal socket
+        */
+        status = TerminalSocket (TERM_SOCK_CREATE, &TermSock);
+        if (status != TERM_SOCK_SUCCESS)
+            exit (1);
+
+        /*
+        ** Process the terminal input
+        */
+        LogMessage ("Waiting on terminal I/O ...\n");
+        len = recv (TermSock, TermBuff, sizeof (TermBuff), 0) ;
+        TermBuff[len] = '\0';
+        LogMessage ("Received terminal I/O [%s]", TermBuff);
+
+        /*
+        ** Delete the terminal socket
+        */
+        status = TerminalSocket (TERM_SOCK_DELETE, &TermSock);
+        if (status != TERM_SOCK_SUCCESS)
+            exit (1);
+    }
+
+    return 1;
+
+}
+# endif
+
+/*----------------------------------------------------------------------------*/
+/*                                                                            */
+/*----------------------------------------------------------------------------*/
+int TerminalSocket (int FunctionCode, int *ReturnSocket)
+{
+    int status;
+    $DESCRIPTOR (TerminalDeviceDesc, "SYS$COMMAND");
+
+    /*
+    ** Process the requested function code
+    */
+    switch (FunctionCode) {
+    case TERM_SOCK_CREATE:
+        /*
+        ** Create a socket pair
+        */
+        status = CreateSocketPair (AF_INET, SOCK_STREAM, 0, TerminalSocketPair);
+        if (status == -1) {
+            LogMessage ("TerminalSocket: CreateSocketPair () - %08X", status);
+            if (TerminalSocketPair[0])
+                close (TerminalSocketPair[0]);
+            if (TerminalSocketPair[1])
+                close (TerminalSocketPair[1]);
+            return (TERM_SOCK_FAILURE);
+        }
+
+        /*
+        ** Assign a channel to the terminal device
+        */
+        status = sys$assign (&TerminalDeviceDesc,
+                             &TerminalDeviceChan,
+                             0, 0, 0);
+        if (! (status & 1)) {
+            LogMessage ("TerminalSocket: SYS$ASSIGN () - %08X", status);
+            close (TerminalSocketPair[0]);
+            close (TerminalSocketPair[1]);
+            return (TERM_SOCK_FAILURE);
+        }
+
+        /*
+        ** Queue an async IO to the terminal device
+        */
+        status = sys$qio (EFN$C_ENF,
+                          TerminalDeviceChan,
+                          IO$_READVBLK,
+                          &TerminalDeviceIosb,
+                          TerminalDeviceAst,
+                          0,
+                          TerminalDeviceBuff,
+                          sizeof (TerminalDeviceBuff) - 2,
+                          0, 0, 0, 0);
+        if (! (status & 1)) {
+            LogMessage ("TerminalSocket: SYS$QIO () - %08X", status);
+            close (TerminalSocketPair[0]);
+            close (TerminalSocketPair[1]);
+            return (TERM_SOCK_FAILURE);
+        }
+
+        /*
+        ** Return the input side of the socket pair
+        */
+        *ReturnSocket = TerminalSocketPair[1];
+        break;
+
+    case TERM_SOCK_DELETE:
+        /*
+        ** Cancel any pending IO on the terminal channel
+        */
+        status = sys$cancel (TerminalDeviceChan);
+        if (! (status & 1)) {
+            LogMessage ("TerminalSocket: SYS$CANCEL () - %08X", status);
+            close (TerminalSocketPair[0]);
+            close (TerminalSocketPair[1]);
+            return (TERM_SOCK_FAILURE);
+        }
+
+        /*
+	** Deassign the terminal channel
+	*/
+        status = sys$dassgn (TerminalDeviceChan);
+        if (! (status & 1)) {
+            LogMessage ("TerminalSocket: SYS$DASSGN () - %08X", status);
+            close (TerminalSocketPair[0]);
+            close (TerminalSocketPair[1]);
+            return (TERM_SOCK_FAILURE);
+        }
+
+        /*
+        ** Close the terminal socket pair
+        */
+        close (TerminalSocketPair[0]);
+        close (TerminalSocketPair[1]);
+
+        /*
+	** Return the initialized socket
+	*/
+        *ReturnSocket = 0;
+        break;
+
+    default:
+        /*
+	** Invalid function code
+	*/
+        LogMessage ("TerminalSocket: Invalid Function Code - %d", FunctionCode);
+        return (TERM_SOCK_FAILURE);
+        break;
+    }
+
+    /*
+    ** Return success
+    */
+    return (TERM_SOCK_SUCCESS);
+
+}
+
+/*----------------------------------------------------------------------------*/
+/*                                                                            */
+/*----------------------------------------------------------------------------*/
+static int CreateSocketPair (int SocketFamily,
+                             int SocketType,
+                             int SocketProtocol,
+                             int *SocketPair)
+{
+    struct dsc$descriptor AscTimeDesc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, NULL};
+    static const char* LocalHostAddr = {"127.0.0.1"};
+    unsigned short TcpAcceptChan = 0,
+        TcpDeviceChan = 0;
+    unsigned long BinTimeBuff[2];
+    struct sockaddr_in sin;
+    char AscTimeBuff[32];
+    short LocalHostPort;
+    int status;
+    unsigned int slen;
+
+# ifdef __alpha
+    struct _iosb iosb;
+# else
+    IOSB iosb;
+# endif
+
+    int SockDesc1 = 0,
+        SockDesc2 = 0;
+    SPTB sptb;
+    $DESCRIPTOR (TcpDeviceDesc, "TCPIP$DEVICE");
+
+    /*
+    ** Create a socket
+    */
+    SockDesc1 = socket (SocketFamily, SocketType, 0);
+    if (SockDesc1 < 0) {
+        LogMessage ("CreateSocketPair: socket () - %d", errno);
+        return (-1);
+    }
+
+    /*
+    ** Initialize the socket information
+    */
+    slen = sizeof (sin);
+    memset ((char *) &sin, 0, slen);
+    sin.sin_family = SocketFamily;
+    sin.sin_addr.s_addr = inet_addr (LocalHostAddr);
+    sin.sin_port = 0;
+
+    /*
+    ** Bind the socket to the local IP
+    */
+    status = bind (SockDesc1, (struct sockaddr *) &sin, slen);
+    if (status < 0) {
+        LogMessage ("CreateSocketPair: bind () - %d", errno);
+        close (SockDesc1);
+        return (-1);
+    }
+
+    /*
+    ** Get the socket name so we can save the port number
+    */
+    status = getsockname (SockDesc1, (struct sockaddr *) &sin, &slen);
+    if (status < 0) {
+        LogMessage ("CreateSocketPair: getsockname () - %d", errno);
+        close (SockDesc1);
+        return (-1);
+    } else
+        LocalHostPort = sin.sin_port;
+
+    /*
+    ** Setup a listen for the socket
+    */
+    listen (SockDesc1, 5);
+
+    /*
+    ** Get the binary (64-bit) time of the specified timeout value
+    */
+    sprintf (AscTimeBuff, "0 0:0:%02d.00", SOCKET_PAIR_TIMEOUT_VALUE);
+    AscTimeDesc.dsc$w_length = strlen (AscTimeBuff);
+    AscTimeDesc.dsc$a_pointer = AscTimeBuff;
+    status = sys$bintim (&AscTimeDesc, BinTimeBuff);
+    if (! (status & 1)) {
+        LogMessage ("CreateSocketPair: SYS$BINTIM () - %08X", status);
+        close (SockDesc1);
+        return (-1);
+    }
+
+    /*
+    ** Assign another channel to the TCP/IP device for the accept.
+    ** This is the channel that ends up being connected to.
+    */
+    status = sys$assign (&TcpDeviceDesc, &TcpDeviceChan, 0, 0, 0);
+    if (! (status & 1)) {
+        LogMessage ("CreateSocketPair: SYS$ASSIGN () - %08X", status);
+        close (SockDesc1);
+        return (-1);
+    }
+
+    /*
+    ** Get the channel of the first socket for the accept
+    */
+    TcpAcceptChan = decc$get_sdc (SockDesc1);
+
+    /*
+    ** Perform the accept using $QIO so we can do this asynchronously
+    */
+    status = sys$qio (EFN$C_ENF,
+                      TcpAcceptChan,
+                      IO$_ACCESS | IO$M_ACCEPT,
+                      &iosb,
+                      0, 0, 0, 0, 0,
+                      &TcpDeviceChan,
+                      0, 0);
+    if (! (status & 1)) {
+        LogMessage ("CreateSocketPair: SYS$QIO () - %08X", status);
+        close (SockDesc1);
+        sys$dassgn (TcpDeviceChan);
+        return (-1);
+    }
+
+    /*
+    ** Create the second socket to do the connect
+    */
+    SockDesc2 = socket (SocketFamily, SocketType, 0);
+    if (SockDesc2 < 0) {
+        LogMessage ("CreateSocketPair: socket () - %d", errno);
+        sys$cancel (TcpAcceptChan);
+        close (SockDesc1);
+        sys$dassgn (TcpDeviceChan);
+        return (-1) ;
+    }
+
+    /*
+    ** Setup the Socket Pair Timeout Block
+    */
+    sptb.SockChan1 = TcpAcceptChan;
+    sptb.SockChan2 = decc$get_sdc (SockDesc2);
+
+    /*
+    ** Before we block on the connect, set a timer that can cancel I/O on our
+    ** two sockets if it never connects.
+    */
+    status = sys$setimr (EFN$C_ENF,
+                         BinTimeBuff,
+                         SocketPairTimeoutAst,
+                         &sptb,
+                         0);
+    if (! (status & 1)) {
+        LogMessage ("CreateSocketPair: SYS$SETIMR () - %08X", status);
+        sys$cancel (TcpAcceptChan);
+        close (SockDesc1);
+        close (SockDesc2);
+        sys$dassgn (TcpDeviceChan);
+        return (-1);
+    }
+
+    /*
+    ** Now issue the connect
+    */
+    memset ((char *) &sin, 0, sizeof (sin)) ;
+    sin.sin_family = SocketFamily;
+    sin.sin_addr.s_addr = inet_addr (LocalHostAddr) ;
+    sin.sin_port = LocalHostPort ;
+
+    status = connect (SockDesc2, (struct sockaddr *) &sin, sizeof (sin));
+    if (status < 0 ) {
+        LogMessage ("CreateSocketPair: connect () - %d", errno);
+        sys$cantim (&sptb, 0);
+        sys$cancel (TcpAcceptChan);
+        close (SockDesc1);
+        close (SockDesc2);
+        sys$dassgn (TcpDeviceChan);
+        return (-1);
+    }
+
+    /*
+    ** Wait for the asynch $QIO to finish.  Note that if the I/O was aborted
+    ** (SS$_ABORT), then we probably canceled it from the AST routine - so log
+    ** a timeout.
+    */
+    status = sys$synch (EFN$C_ENF, &iosb);
+    if (! (iosb.iosb$w_status & 1)) {
+        if (iosb.iosb$w_status == SS$_ABORT)
+            LogMessage ("CreateSocketPair: SYS$QIO(iosb) timeout");
+        else {
+            LogMessage ("CreateSocketPair: SYS$QIO(iosb) - %d",
+                        iosb.iosb$w_status);
+            sys$cantim (&sptb, 0);
+        }
+        close (SockDesc1);
+        close (SockDesc2);
+        sys$dassgn (TcpDeviceChan);
+        return (-1);
+    }
+
+    /*
+    ** Here we're successfully connected, so cancel the timer, convert the
+    ** I/O channel to a socket fd, close the listener socket and return the
+    ** connected pair.
+    */
+    sys$cantim (&sptb, 0);
+
+    close (SockDesc1) ;
+    SocketPair[0] = SockDesc2 ;
+    SocketPair[1] = socket_fd (TcpDeviceChan);
+
+    return (0) ;
+
+}
+
+/*----------------------------------------------------------------------------*/
+/*                                                                            */
+/*----------------------------------------------------------------------------*/
+static void SocketPairTimeoutAst (int astparm)
+{
+    SPTB *sptb = (SPTB *) astparm;
+
+    sys$cancel (sptb->SockChan2); /* Cancel the connect() */
+    sys$cancel (sptb->SockChan1); /* Cancel the accept() */
+
+    return;
+
+}
+
+/*----------------------------------------------------------------------------*/
+/*                                                                            */
+/*----------------------------------------------------------------------------*/
+static int TerminalDeviceAst (int astparm)
+{
+    int status;
+
+    /*
+    ** Terminate the terminal buffer
+    */
+    TerminalDeviceBuff[TerminalDeviceIosb.iosb$w_bcnt] = '\0';
+    strcat (TerminalDeviceBuff, "\n");
+
+    /*
+    ** Send the data read from the terminal device throught the socket pair
+    */
+    send (TerminalSocketPair[0], TerminalDeviceBuff,
+          TerminalDeviceIosb.iosb$w_bcnt + 1, 0);
+
+    /*
+    ** Queue another async IO to the terminal device
+    */
+    status = sys$qio (EFN$C_ENF,
+                      TerminalDeviceChan,
+                      IO$_READVBLK,
+                      &TerminalDeviceIosb,
+                      TerminalDeviceAst,
+                      0,
+                      TerminalDeviceBuff,
+                      sizeof (TerminalDeviceBuff) - 1,
+                      0, 0, 0, 0);
+
+    /*
+    ** Return status
+    */
+    return status;
+
+}
+
+/*----------------------------------------------------------------------------*/
+/*                                                                            */
+/*----------------------------------------------------------------------------*/
+static void LogMessage (char *msg, ...)
+{
+    char *Month[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
+                     "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
+    static unsigned int pid = 0;
+    va_list args;
+    time_t CurTime;
+    struct tm *LocTime;
+    char MsgBuff[256];
+
+    /*
+    ** Get the process pid
+    */
+    if (pid == 0)
+        pid = getpid ();
+
+    /*
+    ** Convert the current time into local time
+    */
+    CurTime = time (NULL);
+    LocTime = localtime (&CurTime);
+
+    /*
+    ** Format the message buffer
+    */
+    sprintf (MsgBuff, "%02d-%s-%04d %02d:%02d:%02d [%08X] %s\n",
+             LocTime->tm_mday, Month[LocTime->tm_mon],
+             (LocTime->tm_year + 1900), LocTime->tm_hour, LocTime->tm_min,
+             LocTime->tm_sec, pid, msg);
+
+    /*
+    ** Get any variable arguments and add them to the print of the message
+    ** buffer
+    */
+    va_start (args, msg);
+    vfprintf (stderr, MsgBuff, args);
+    va_end (args);
+
+    /*
+    ** Flush standard error output
+    */
+    fsync (fileno (stderr));
+
+    return;
+
+}
+#endif
diff --git a/apps/vms_term_sock.h b/apps/vms_term_sock.h
new file mode 100644
index 000000000..662fa0ada
--- /dev/null
+++ b/apps/vms_term_sock.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2016 VMS Software, Inc. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#ifndef TERM_SOCK_H
+# define TERM_SOCK_H
+
+/*
+** Terminal Socket Function Codes
+*/
+# define TERM_SOCK_CREATE       1
+# define TERM_SOCK_DELETE       2
+
+/*
+** Terminal Socket Status Codes
+*/
+# define TERM_SOCK_FAILURE      0
+# define TERM_SOCK_SUCCESS      1
+
+/*
+** Terminal Socket Prototype
+*/
+int TerminalSocket (int FunctionCode, int *ReturnSocket);
+
+#endif
diff --git a/apps/win32_init.c b/apps/win32_init.c
new file mode 100644
index 000000000..ebe92bcd4
--- /dev/null
+++ b/apps/win32_init.c
@@ -0,0 +1,307 @@
+/*
+ * Copyright 2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include 
+#include 
+
+#if defined(CP_UTF8)
+
+static UINT saved_cp;
+static int newargc;
+static char **newargv;
+
+static void cleanup(void)
+{
+    int i;
+
+    SetConsoleOutputCP(saved_cp);
+
+    for (i = 0; i < newargc; i++)
+        free(newargv[i]);
+
+    free(newargv);
+}
+
+/*
+ * Incrementally [re]allocate newargv and keep it NULL-terminated.
+ */
+static int validate_argv(int argc)
+{
+    static int size = 0;
+
+    if (argc >= size) {
+        char **ptr;
+
+        while (argc >= size)
+            size += 64;
+
+        ptr = realloc(newargv, size * sizeof(newargv[0]));
+        if (ptr == NULL)
+            return 0;
+
+        (newargv = ptr)[argc] = NULL;
+    } else {
+        newargv[argc] = NULL;
+    }
+
+    return 1;
+}
+
+static int process_glob(WCHAR *wstr, int wlen)
+{
+    int i, slash, udlen;
+    WCHAR saved_char;
+    WIN32_FIND_DATAW data;
+    HANDLE h;
+
+    /*
+     * Note that we support wildcard characters only in filename part
+     * of the path, and not in directories. Windows users are used to
+     * this, that's why recursive glob processing is not implemented.
+     */
+    /*
+     * Start by looking for last slash or backslash, ...
+     */
+    for (slash = 0, i = 0; i < wlen; i++)
+        if (wstr[i] == L'/' || wstr[i] == L'\\')
+            slash = i + 1;
+    /*
+     * ... then look for asterisk or question mark in the file name.
+     */
+    for (i = slash; i < wlen; i++)
+        if (wstr[i] == L'*' || wstr[i] == L'?')
+            break;
+
+    if (i == wlen)
+        return 0;   /* definitely not a glob */
+
+    saved_char = wstr[wlen];
+    wstr[wlen] = L'\0';
+    h = FindFirstFileW(wstr, &data);
+    wstr[wlen] = saved_char;
+    if (h == INVALID_HANDLE_VALUE)
+        return 0;   /* not a valid glob, just pass... */
+
+    if (slash)
+        udlen = WideCharToMultiByte(CP_UTF8, 0, wstr, slash,
+                                    NULL, 0, NULL, NULL);
+    else
+        udlen = 0;
+
+    do {
+        int uflen;
+        char *arg;
+
+        /*
+         * skip over . and ..
+         */
+        if (data.cFileName[0] == L'.') {
+            if ((data.cFileName[1] == L'\0') ||
+                (data.cFileName[1] == L'.' && data.cFileName[2] == L'\0'))
+                continue;
+        }
+
+        if (!validate_argv(newargc + 1))
+            break;
+
+        /*
+         * -1 below means "scan for trailing '\0' *and* count it",
+         * so that |uflen| covers even trailing '\0'.
+         */
+        uflen = WideCharToMultiByte(CP_UTF8, 0, data.cFileName, -1,
+                                    NULL, 0, NULL, NULL);
+
+        arg = malloc(udlen + uflen);
+        if (arg == NULL)
+            break;
+
+        if (udlen)
+            WideCharToMultiByte(CP_UTF8, 0, wstr, slash,
+                                arg, udlen, NULL, NULL);
+
+        WideCharToMultiByte(CP_UTF8, 0, data.cFileName, -1,
+                            arg + udlen, uflen, NULL, NULL);
+
+        newargv[newargc++] = arg;
+    } while (FindNextFileW(h, &data));
+
+    CloseHandle(h);
+
+    return 1;
+}
+
+void win32_utf8argv(int *argc, char **argv[])
+{
+    const WCHAR *wcmdline;
+    WCHAR *warg, *wend, *p;
+    int wlen, ulen, valid = 1;
+    char *arg;
+
+    if (GetEnvironmentVariableW(L"OPENSSL_WIN32_UTF8", NULL, 0) == 0)
+        return;
+
+    newargc = 0;
+    newargv = NULL;
+    if (!validate_argv(newargc))
+        return;
+
+    wcmdline = GetCommandLineW();
+    if (wcmdline == NULL) return;
+
+    /*
+     * make a copy of the command line, since we might have to modify it...
+     */
+    wlen = wcslen(wcmdline);
+    p = _alloca((wlen + 1) * sizeof(WCHAR));
+    wcscpy(p, wcmdline);
+
+    while (*p != L'\0') {
+        int in_quote = 0;
+
+        if (*p == L' ' || *p == L'\t') {
+            p++; /* skip over white spaces */
+            continue;
+        }
+
+        /*
+         * Note: because we may need to fiddle with the number of backslashes,
+         * the argument string is copied into itself.  This is safe because
+         * the number of characters will never expand.
+         */
+        warg = wend = p;
+        while (*p != L'\0'
+               && (in_quote || (*p != L' ' && *p != L'\t'))) {
+            switch (*p) {
+            case L'\\':
+                /*
+                 * Microsoft documentation on how backslashes are treated
+                 * is:
+                 *
+                 * + Backslashes are interpreted literally, unless they
+                 *   immediately precede a double quotation mark.
+                 * + If an even number of backslashes is followed by a double
+                 *   quotation mark, one backslash is placed in the argv array
+                 *   for every pair of backslashes, and the double quotation
+                 *   mark is interpreted as a string delimiter.
+                 * + If an odd number of backslashes is followed by a double
+                 *   quotation mark, one backslash is placed in the argv array
+                 *   for every pair of backslashes, and the double quotation
+                 *   mark is "escaped" by the remaining backslash, causing a
+                 *   literal double quotation mark (") to be placed in argv.
+                 *
+                 * Ref: https://msdn.microsoft.com/en-us/library/17w5ykft.aspx
+                 *
+                 * Though referred page doesn't mention it, multiple qouble
+                 * quotes are also special. Pair of double quotes in quoted
+                 * string is counted as single double quote.
+                 */
+                {
+                    const WCHAR *q = p;
+                    int i;
+
+                    while (*p == L'\\')
+                        p++;
+
+                    if (*p == L'"') {
+                        int i;
+
+                        for (i = (p - q) / 2; i > 0; i--)
+                            *wend++ = L'\\';
+
+                        /*
+                         * if odd amount of backslashes before the quote,
+                         * said quote is part of the argument, not a delimiter
+                         */
+                        if ((p - q) % 2 == 1)
+                            *wend++ = *p++;
+                    } else {
+                        for (i = p - q; i > 0; i--)
+                            *wend++ = L'\\';
+                    }
+                }
+                break;
+            case L'"':
+                /*
+                 * Without the preceding backslash (or when preceded with an
+                 * even number of backslashes), the double quote is a simple
+                 * string delimiter and just slightly change the parsing state
+                 */
+                if (in_quote && p[1] == L'"')
+                    *wend++ = *p++;
+                else
+                    in_quote = !in_quote;
+                p++;
+                break;
+            default:
+                /*
+                 * Any other non-delimiter character is just taken verbatim
+                 */
+                *wend++ = *p++;
+            }
+        }
+
+        wlen = wend - warg;
+
+        if (wlen == 0 || !process_glob(warg, wlen)) {
+            if (!validate_argv(newargc + 1)) {
+                valid = 0;
+                break;
+            }
+
+            ulen = 0;
+            if (wlen > 0) {
+                ulen = WideCharToMultiByte(CP_UTF8, 0, warg, wlen,
+                                           NULL, 0, NULL, NULL);
+                if (ulen <= 0)
+                    continue;
+            }
+
+            arg = malloc(ulen + 1);
+            if (arg == NULL) {
+                valid = 0;
+                break;
+            }
+
+            if (wlen > 0)
+                WideCharToMultiByte(CP_UTF8, 0, warg, wlen,
+                                    arg, ulen, NULL, NULL);
+            arg[ulen] = '\0';
+
+            newargv[newargc++] = arg;
+        }
+    }
+
+    if (valid) {
+        saved_cp = GetConsoleOutputCP();
+        SetConsoleOutputCP(CP_UTF8);
+
+        *argc = newargc;
+        *argv = newargv;
+
+        atexit(cleanup);
+    } else if (newargv != NULL) {
+        int i;
+
+        for (i = 0; i < newargc; i++)
+            free(newargv[i]);
+
+        free(newargv);
+
+        newargc = 0;
+        newargv = NULL;
+    }
+
+    return;
+}
+#else
+void win32_utf8argv(int &argc, char **argv[])
+{   return;   }
+#endif
diff --git a/apps/x509.c b/apps/x509.c
new file mode 100644
index 000000000..651a92404
--- /dev/null
+++ b/apps/x509.c
@@ -0,0 +1,1101 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include 
+#include "apps.h"
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#ifndef OPENSSL_NO_RSA
+# include 
+#endif
+#ifndef OPENSSL_NO_DSA
+# include 
+#endif
+
+#undef POSTFIX
+#define POSTFIX ".srl"
+#define DEF_DAYS        30
+
+static int callb(int ok, X509_STORE_CTX *ctx);
+static int sign(X509 *x, EVP_PKEY *pkey, int days, int clrext,
+                const EVP_MD *digest, CONF *conf, const char *section);
+static int x509_certify(X509_STORE *ctx, const char *CAfile, const EVP_MD *digest,
+                        X509 *x, X509 *xca, EVP_PKEY *pkey,
+                        STACK_OF(OPENSSL_STRING) *sigopts, const char *serialfile,
+                        int create, int days, int clrext, CONF *conf,
+                        const char *section, ASN1_INTEGER *sno, int reqfile);
+static int purpose_print(BIO *bio, X509 *cert, X509_PURPOSE *pt);
+
+typedef enum OPTION_choice {
+    OPT_ERR = -1, OPT_EOF = 0, OPT_HELP,
+    OPT_INFORM, OPT_OUTFORM, OPT_KEYFORM, OPT_REQ, OPT_CAFORM,
+    OPT_CAKEYFORM, OPT_SIGOPT, OPT_DAYS, OPT_PASSIN, OPT_EXTFILE,
+    OPT_EXTENSIONS, OPT_IN, OPT_OUT, OPT_SIGNKEY, OPT_CA,
+    OPT_CAKEY, OPT_CASERIAL, OPT_SET_SERIAL, OPT_FORCE_PUBKEY,
+    OPT_ADDTRUST, OPT_ADDREJECT, OPT_SETALIAS, OPT_CERTOPT, OPT_NAMEOPT,
+    OPT_C, OPT_EMAIL, OPT_OCSP_URI, OPT_SERIAL, OPT_NEXT_SERIAL,
+    OPT_MODULUS, OPT_PUBKEY, OPT_X509TOREQ, OPT_TEXT, OPT_HASH,
+    OPT_ISSUER_HASH, OPT_SUBJECT, OPT_ISSUER, OPT_FINGERPRINT, OPT_DATES,
+    OPT_PURPOSE, OPT_STARTDATE, OPT_ENDDATE, OPT_CHECKEND, OPT_CHECKHOST,
+    OPT_CHECKEMAIL, OPT_CHECKIP, OPT_NOOUT, OPT_TRUSTOUT, OPT_CLRTRUST,
+    OPT_CLRREJECT, OPT_ALIAS, OPT_CACREATESERIAL, OPT_CLREXT, OPT_OCSPID,
+    OPT_SUBJECT_HASH_OLD,
+    OPT_ISSUER_HASH_OLD,
+    OPT_BADSIG, OPT_MD, OPT_ENGINE, OPT_NOCERT
+} OPTION_CHOICE;
+
+OPTIONS x509_options[] = {
+    {"help", OPT_HELP, '-', "Display this summary"},
+    {"inform", OPT_INFORM, 'f',
+     "Input format - default PEM (one of DER, NET or PEM)"},
+    {"in", OPT_IN, '<', "Input file - default stdin"},
+    {"outform", OPT_OUTFORM, 'f',
+     "Output format - default PEM (one of DER, NET or PEM)"},
+    {"out", OPT_OUT, '>', "Output file - default stdout"},
+    {"keyform", OPT_KEYFORM, 'F', "Private key format - default PEM"},
+    {"passin", OPT_PASSIN, 's', "Private key password/pass-phrase source"},
+    {"serial", OPT_SERIAL, '-', "Print serial number value"},
+    {"subject_hash", OPT_HASH, '-', "Print subject hash value"},
+    {"issuer_hash", OPT_ISSUER_HASH, '-', "Print issuer hash value"},
+    {"hash", OPT_HASH, '-', "Synonym for -subject_hash"},
+    {"subject", OPT_SUBJECT, '-', "Print subject DN"},
+    {"issuer", OPT_ISSUER, '-', "Print issuer DN"},
+    {"email", OPT_EMAIL, '-', "Print email address(es)"},
+    {"startdate", OPT_STARTDATE, '-', "Set notBefore field"},
+    {"enddate", OPT_ENDDATE, '-', "Set notAfter field"},
+    {"purpose", OPT_PURPOSE, '-', "Print out certificate purposes"},
+    {"dates", OPT_DATES, '-', "Both Before and After dates"},
+    {"modulus", OPT_MODULUS, '-', "Print the RSA key modulus"},
+    {"pubkey", OPT_PUBKEY, '-', "Output the public key"},
+    {"fingerprint", OPT_FINGERPRINT, '-',
+     "Print the certificate fingerprint"},
+    {"alias", OPT_ALIAS, '-', "Output certificate alias"},
+    {"noout", OPT_NOOUT, '-', "No output, just status"},
+    {"nocert", OPT_NOCERT, '-', "No certificate output"},
+    {"ocspid", OPT_OCSPID, '-',
+     "Print OCSP hash values for the subject name and public key"},
+    {"ocsp_uri", OPT_OCSP_URI, '-', "Print OCSP Responder URL(s)"},
+    {"trustout", OPT_TRUSTOUT, '-', "Output a trusted certificate"},
+    {"clrtrust", OPT_CLRTRUST, '-', "Clear all trusted purposes"},
+    {"clrext", OPT_CLREXT, '-', "Clear all certificate extensions"},
+    {"addtrust", OPT_ADDTRUST, 's', "Trust certificate for a given purpose"},
+    {"addreject", OPT_ADDREJECT, 's',
+     "Reject certificate for a given purpose"},
+    {"setalias", OPT_SETALIAS, 's', "Set certificate alias"},
+    {"days", OPT_DAYS, 'n',
+     "How long till expiry of a signed certificate - def 30 days"},
+    {"checkend", OPT_CHECKEND, 'M',
+     "Check whether the cert expires in the next arg seconds"},
+    {OPT_MORE_STR, 1, 1, "Exit 1 if so, 0 if not"},
+    {"signkey", OPT_SIGNKEY, '<', "Self sign cert with arg"},
+    {"x509toreq", OPT_X509TOREQ, '-',
+     "Output a certification request object"},
+    {"req", OPT_REQ, '-', "Input is a certificate request, sign and output"},
+    {"CA", OPT_CA, '<', "Set the CA certificate, must be PEM format"},
+    {"CAkey", OPT_CAKEY, 's',
+     "The CA key, must be PEM format; if not in CAfile"},
+    {"CAcreateserial", OPT_CACREATESERIAL, '-',
+     "Create serial number file if it does not exist"},
+    {"CAserial", OPT_CASERIAL, 's', "Serial file"},
+    {"set_serial", OPT_SET_SERIAL, 's', "Serial number to use"},
+    {"text", OPT_TEXT, '-', "Print the certificate in text form"},
+    {"C", OPT_C, '-', "Print out C code forms"},
+    {"extfile", OPT_EXTFILE, '<', "File with X509V3 extensions to add"},
+    {"extensions", OPT_EXTENSIONS, 's', "Section from config file to use"},
+    {"nameopt", OPT_NAMEOPT, 's', "Various certificate name options"},
+    {"certopt", OPT_CERTOPT, 's', "Various certificate text options"},
+    {"checkhost", OPT_CHECKHOST, 's', "Check certificate matches host"},
+    {"checkemail", OPT_CHECKEMAIL, 's', "Check certificate matches email"},
+    {"checkip", OPT_CHECKIP, 's', "Check certificate matches ipaddr"},
+    {"CAform", OPT_CAFORM, 'F', "CA format - default PEM"},
+    {"CAkeyform", OPT_CAKEYFORM, 'F', "CA key format - default PEM"},
+    {"sigopt", OPT_SIGOPT, 's', "Signature parameter in n:v form"},
+    {"force_pubkey", OPT_FORCE_PUBKEY, '<', "Force the Key to put inside certificate"},
+    {"next_serial", OPT_NEXT_SERIAL, '-', "Increment current certificate serial number"},
+    {"clrreject", OPT_CLRREJECT, '-',
+     "Clears all the prohibited or rejected uses of the certificate"},
+    {"badsig", OPT_BADSIG, '-', "Corrupt last byte of certificate signature (for test)"},
+    {"", OPT_MD, '-', "Any supported digest"},
+#ifndef OPENSSL_NO_MD5
+    {"subject_hash_old", OPT_SUBJECT_HASH_OLD, '-',
+     "Print old-style (MD5) issuer hash value"},
+    {"issuer_hash_old", OPT_ISSUER_HASH_OLD, '-',
+     "Print old-style (MD5) subject hash value"},
+#endif
+#ifndef OPENSSL_NO_ENGINE
+    {"engine", OPT_ENGINE, 's', "Use engine, possibly a hardware device"},
+#endif
+    {NULL}
+};
+
+int x509_main(int argc, char **argv)
+{
+    ASN1_INTEGER *sno = NULL;
+    ASN1_OBJECT *objtmp = NULL;
+    BIO *out = NULL;
+    CONF *extconf = NULL;
+    EVP_PKEY *Upkey = NULL, *CApkey = NULL, *fkey = NULL;
+    STACK_OF(ASN1_OBJECT) *trust = NULL, *reject = NULL;
+    STACK_OF(OPENSSL_STRING) *sigopts = NULL;
+    X509 *x = NULL, *xca = NULL;
+    X509_REQ *req = NULL, *rq = NULL;
+    X509_STORE *ctx = NULL;
+    const EVP_MD *digest = NULL;
+    char *CAkeyfile = NULL, *CAserial = NULL, *fkeyfile = NULL, *alias = NULL;
+    char *checkhost = NULL, *checkemail = NULL, *checkip = NULL;
+    char *extsect = NULL, *extfile = NULL, *passin = NULL, *passinarg = NULL;
+    char *infile = NULL, *outfile = NULL, *keyfile = NULL, *CAfile = NULL;
+    char buf[256], *prog;
+    int x509req = 0, days = DEF_DAYS, modulus = 0, pubkey = 0, pprint = 0;
+    int C = 0, CAformat = FORMAT_PEM, CAkeyformat = FORMAT_PEM;
+    int fingerprint = 0, reqfile = 0, need_rand = 0, checkend = 0;
+    int informat = FORMAT_PEM, outformat = FORMAT_PEM, keyformat = FORMAT_PEM;
+    int next_serial = 0, subject_hash = 0, issuer_hash = 0, ocspid = 0;
+    int noout = 0, sign_flag = 0, CA_flag = 0, CA_createserial = 0, email = 0;
+    int ocsp_uri = 0, trustout = 0, clrtrust = 0, clrreject = 0, aliasout = 0;
+    int ret = 1, i, num = 0, badsig = 0, clrext = 0, nocert = 0;
+    int text = 0, serial = 0, subject = 0, issuer = 0, startdate = 0;
+    int enddate = 0;
+    time_t checkoffset = 0;
+    unsigned long nmflag = 0, certflag = 0;
+    char nmflag_set = 0;
+    OPTION_CHOICE o;
+    ENGINE *e = NULL;
+#ifndef OPENSSL_NO_MD5
+    int subject_hash_old = 0, issuer_hash_old = 0;
+#endif
+
+    ctx = X509_STORE_new();
+    if (ctx == NULL)
+        goto end;
+    X509_STORE_set_verify_cb(ctx, callb);
+
+    prog = opt_init(argc, argv, x509_options);
+    while ((o = opt_next()) != OPT_EOF) {
+        switch (o) {
+        case OPT_EOF:
+        case OPT_ERR:
+ opthelp:
+            BIO_printf(bio_err, "%s: Use -help for summary.\n", prog);
+            goto end;
+        case OPT_HELP:
+            opt_help(x509_options);
+            ret = 0;
+            goto end;
+        case OPT_INFORM:
+            if (!opt_format(opt_arg(), OPT_FMT_ANY, &informat))
+                goto opthelp;
+            break;
+        case OPT_IN:
+            infile = opt_arg();
+            break;
+        case OPT_OUTFORM:
+            if (!opt_format(opt_arg(), OPT_FMT_ANY, &outformat))
+                goto opthelp;
+            break;
+        case OPT_KEYFORM:
+            if (!opt_format(opt_arg(), OPT_FMT_PEMDER, &keyformat))
+                goto opthelp;
+            break;
+        case OPT_CAFORM:
+            if (!opt_format(opt_arg(), OPT_FMT_PEMDER, &CAformat))
+                goto opthelp;
+            break;
+        case OPT_CAKEYFORM:
+            if (!opt_format(opt_arg(), OPT_FMT_ANY, &CAkeyformat))
+                goto opthelp;
+            break;
+        case OPT_OUT:
+            outfile = opt_arg();
+            break;
+        case OPT_REQ:
+            reqfile = need_rand = 1;
+            break;
+
+        case OPT_SIGOPT:
+            if (!sigopts)
+                sigopts = sk_OPENSSL_STRING_new_null();
+            if (!sigopts || !sk_OPENSSL_STRING_push(sigopts, opt_arg()))
+                goto opthelp;
+            break;
+        case OPT_DAYS:
+            days = atoi(opt_arg());
+            break;
+        case OPT_PASSIN:
+            passinarg = opt_arg();
+            break;
+        case OPT_EXTFILE:
+            extfile = opt_arg();
+            break;
+        case OPT_EXTENSIONS:
+            extsect = opt_arg();
+            break;
+        case OPT_SIGNKEY:
+            keyfile = opt_arg();
+            sign_flag = ++num;
+            need_rand = 1;
+            break;
+        case OPT_CA:
+            CAfile = opt_arg();
+            CA_flag = ++num;
+            need_rand = 1;
+            break;
+        case OPT_CAKEY:
+            CAkeyfile = opt_arg();
+            break;
+        case OPT_CASERIAL:
+            CAserial = opt_arg();
+            break;
+        case OPT_SET_SERIAL:
+            if (sno != NULL) {
+                BIO_printf(bio_err, "Serial number supplied twice\n");
+                goto opthelp;
+            }
+            if ((sno = s2i_ASN1_INTEGER(NULL, opt_arg())) == NULL)
+                goto opthelp;
+            break;
+        case OPT_FORCE_PUBKEY:
+            fkeyfile = opt_arg();
+            break;
+        case OPT_ADDTRUST:
+            if ((objtmp = OBJ_txt2obj(opt_arg(), 0)) == NULL) {
+                BIO_printf(bio_err,
+                           "%s: Invalid trust object value %s\n",
+                           prog, opt_arg());
+                goto opthelp;
+            }
+            if (trust == NULL && (trust = sk_ASN1_OBJECT_new_null()) == NULL)
+                goto end;
+            sk_ASN1_OBJECT_push(trust, objtmp);
+            objtmp = NULL;
+            trustout = 1;
+            break;
+        case OPT_ADDREJECT:
+            if ((objtmp = OBJ_txt2obj(opt_arg(), 0)) == NULL) {
+                BIO_printf(bio_err,
+                           "%s: Invalid reject object value %s\n",
+                           prog, opt_arg());
+                goto opthelp;
+            }
+            if (reject == NULL
+                && (reject = sk_ASN1_OBJECT_new_null()) == NULL)
+                goto end;
+            sk_ASN1_OBJECT_push(reject, objtmp);
+            objtmp = NULL;
+            trustout = 1;
+            break;
+        case OPT_SETALIAS:
+            alias = opt_arg();
+            trustout = 1;
+            break;
+        case OPT_CERTOPT:
+            if (!set_cert_ex(&certflag, opt_arg()))
+                goto opthelp;
+            break;
+        case OPT_NAMEOPT:
+            nmflag_set = 1;
+            if (!set_name_ex(&nmflag, opt_arg()))
+                goto opthelp;
+            break;
+        case OPT_ENGINE:
+            e = setup_engine(opt_arg(), 0);
+            break;
+        case OPT_C:
+            C = ++num;
+            break;
+        case OPT_EMAIL:
+            email = ++num;
+            break;
+        case OPT_OCSP_URI:
+            ocsp_uri = ++num;
+            break;
+        case OPT_SERIAL:
+            serial = ++num;
+            break;
+        case OPT_NEXT_SERIAL:
+            next_serial = ++num;
+            break;
+        case OPT_MODULUS:
+            modulus = ++num;
+            break;
+        case OPT_PUBKEY:
+            pubkey = ++num;
+            break;
+        case OPT_X509TOREQ:
+            x509req = ++num;
+            break;
+        case OPT_TEXT:
+            text = ++num;
+            break;
+        case OPT_SUBJECT:
+            subject = ++num;
+            break;
+        case OPT_ISSUER:
+            issuer = ++num;
+            break;
+        case OPT_FINGERPRINT:
+            fingerprint = ++num;
+            break;
+        case OPT_HASH:
+            subject_hash = ++num;
+            break;
+        case OPT_ISSUER_HASH:
+            issuer_hash = ++num;
+            break;
+        case OPT_PURPOSE:
+            pprint = ++num;
+            break;
+        case OPT_STARTDATE:
+            startdate = ++num;
+            break;
+        case OPT_ENDDATE:
+            enddate = ++num;
+            break;
+        case OPT_NOOUT:
+            noout = ++num;
+            break;
+        case OPT_NOCERT:
+            nocert = 1;
+            break;
+        case OPT_TRUSTOUT:
+            trustout = 1;
+            break;
+        case OPT_CLRTRUST:
+            clrtrust = ++num;
+            break;
+        case OPT_CLRREJECT:
+            clrreject = ++num;
+            break;
+        case OPT_ALIAS:
+            aliasout = ++num;
+            break;
+        case OPT_CACREATESERIAL:
+            CA_createserial = ++num;
+            break;
+        case OPT_CLREXT:
+            clrext = 1;
+            break;
+        case OPT_OCSPID:
+            ocspid = ++num;
+            break;
+        case OPT_BADSIG:
+            badsig = 1;
+            break;
+#ifndef OPENSSL_NO_MD5
+        case OPT_SUBJECT_HASH_OLD:
+            subject_hash_old = ++num;
+            break;
+        case OPT_ISSUER_HASH_OLD:
+            issuer_hash_old = ++num;
+            break;
+#else
+        case OPT_SUBJECT_HASH_OLD:
+        case OPT_ISSUER_HASH_OLD:
+            break;
+#endif
+        case OPT_DATES:
+            startdate = ++num;
+            enddate = ++num;
+            break;
+        case OPT_CHECKEND:
+            checkend = 1;
+            {
+                intmax_t temp = 0;
+                if (!opt_imax(opt_arg(), &temp))
+                    goto opthelp;
+                checkoffset = (time_t)temp;
+                if ((intmax_t)checkoffset != temp) {
+                    BIO_printf(bio_err, "%s: checkend time out of range %s\n",
+                               prog, opt_arg());
+                    goto opthelp;
+                }
+            }
+            break;
+        case OPT_CHECKHOST:
+            checkhost = opt_arg();
+            break;
+        case OPT_CHECKEMAIL:
+            checkemail = opt_arg();
+            break;
+        case OPT_CHECKIP:
+            checkip = opt_arg();
+            break;
+        case OPT_MD:
+            if (!opt_md(opt_unknown(), &digest))
+                goto opthelp;
+        }
+    }
+    argc = opt_num_rest();
+    argv = opt_rest();
+    if (argc != 0) {
+        BIO_printf(bio_err, "%s: Unknown parameter %s\n", prog, argv[0]);
+        goto opthelp;
+    }
+
+    if (!nmflag_set)
+        nmflag = XN_FLAG_ONELINE;
+
+    out = bio_open_default(outfile, 'w', outformat);
+    if (out == NULL)
+        goto end;
+
+    if (need_rand)
+        app_RAND_load_file(NULL, 0);
+
+    if (!app_passwd(passinarg, NULL, &passin, NULL)) {
+        BIO_printf(bio_err, "Error getting password\n");
+        goto end;
+    }
+
+    if (!X509_STORE_set_default_paths(ctx)) {
+        ERR_print_errors(bio_err);
+        goto end;
+    }
+
+    if (fkeyfile) {
+        fkey = load_pubkey(fkeyfile, keyformat, 0, NULL, e, "Forced key");
+        if (fkey == NULL)
+            goto end;
+    }
+
+    if ((CAkeyfile == NULL) && (CA_flag) && (CAformat == FORMAT_PEM)) {
+        CAkeyfile = CAfile;
+    } else if ((CA_flag) && (CAkeyfile == NULL)) {
+        BIO_printf(bio_err,
+                   "need to specify a CAkey if using the CA command\n");
+        goto end;
+    }
+
+    if (extfile) {
+        X509V3_CTX ctx2;
+        if ((extconf = app_load_config(extfile)) == NULL)
+            goto end;
+        if (!extsect) {
+            extsect = NCONF_get_string(extconf, "default", "extensions");
+            if (!extsect) {
+                ERR_clear_error();
+                extsect = "default";
+            }
+        }
+        X509V3_set_ctx_test(&ctx2);
+        X509V3_set_nconf(&ctx2, extconf);
+        if (!X509V3_EXT_add_nconf(extconf, &ctx2, extsect, NULL)) {
+            BIO_printf(bio_err,
+                       "Error Loading extension section %s\n", extsect);
+            ERR_print_errors(bio_err);
+            goto end;
+        }
+    }
+
+    if (reqfile) {
+        EVP_PKEY *pkey;
+        BIO *in;
+
+        if (!sign_flag && !CA_flag) {
+            BIO_printf(bio_err, "We need a private key to sign with\n");
+            goto end;
+        }
+        in = bio_open_default(infile, 'r', informat);
+        if (in == NULL)
+            goto end;
+        req = PEM_read_bio_X509_REQ(in, NULL, NULL, NULL);
+        BIO_free(in);
+
+        if (req == NULL) {
+            ERR_print_errors(bio_err);
+            goto end;
+        }
+
+        if ((pkey = X509_REQ_get0_pubkey(req)) == NULL) {
+            BIO_printf(bio_err, "error unpacking public key\n");
+            goto end;
+        }
+        i = X509_REQ_verify(req, pkey);
+        if (i < 0) {
+            BIO_printf(bio_err, "Signature verification error\n");
+            ERR_print_errors(bio_err);
+            goto end;
+        }
+        if (i == 0) {
+            BIO_printf(bio_err,
+                       "Signature did not match the certificate request\n");
+            goto end;
+        } else
+            BIO_printf(bio_err, "Signature ok\n");
+
+        print_name(bio_err, "subject=", X509_REQ_get_subject_name(req),
+                   nmflag);
+
+        if ((x = X509_new()) == NULL)
+            goto end;
+
+        if (sno == NULL) {
+            sno = ASN1_INTEGER_new();
+            if (sno == NULL || !rand_serial(NULL, sno))
+                goto end;
+            if (!X509_set_serialNumber(x, sno))
+                goto end;
+            ASN1_INTEGER_free(sno);
+            sno = NULL;
+        } else if (!X509_set_serialNumber(x, sno))
+            goto end;
+
+        if (!X509_set_issuer_name(x, X509_REQ_get_subject_name(req)))
+            goto end;
+        if (!X509_set_subject_name(x, X509_REQ_get_subject_name(req)))
+            goto end;
+        if (!set_cert_times(x, NULL, NULL, days))
+            goto end;
+
+        if (fkey)
+            X509_set_pubkey(x, fkey);
+        else {
+            pkey = X509_REQ_get0_pubkey(req);
+            X509_set_pubkey(x, pkey);
+        }
+    } else
+        x = load_cert(infile, informat, "Certificate");
+
+    if (x == NULL)
+        goto end;
+    if (CA_flag) {
+        xca = load_cert(CAfile, CAformat, "CA Certificate");
+        if (xca == NULL)
+            goto end;
+    }
+
+    if (!noout || text || next_serial) {
+        OBJ_create("2.99999.3", "SET.ex3", "SET x509v3 extension 3");
+
+    }
+
+    if (alias)
+        X509_alias_set1(x, (unsigned char *)alias, -1);
+
+    if (clrtrust)
+        X509_trust_clear(x);
+    if (clrreject)
+        X509_reject_clear(x);
+
+    if (trust) {
+        for (i = 0; i < sk_ASN1_OBJECT_num(trust); i++) {
+            objtmp = sk_ASN1_OBJECT_value(trust, i);
+            X509_add1_trust_object(x, objtmp);
+        }
+        objtmp = NULL;
+    }
+
+    if (reject) {
+        for (i = 0; i < sk_ASN1_OBJECT_num(reject); i++) {
+            objtmp = sk_ASN1_OBJECT_value(reject, i);
+            X509_add1_reject_object(x, objtmp);
+        }
+        objtmp = NULL;
+    }
+
+    if (badsig) {
+        const ASN1_BIT_STRING *signature;
+
+        X509_get0_signature(&signature, NULL, x);
+        corrupt_signature(signature);
+    }
+
+    if (num) {
+        for (i = 1; i <= num; i++) {
+            if (issuer == i) {
+                print_name(out, "issuer=", X509_get_issuer_name(x), nmflag);
+            } else if (subject == i) {
+                print_name(out, "subject=",
+                           X509_get_subject_name(x), nmflag);
+            } else if (serial == i) {
+                BIO_printf(out, "serial=");
+                i2a_ASN1_INTEGER(out, X509_get_serialNumber(x));
+                BIO_printf(out, "\n");
+            } else if (next_serial == i) {
+                ASN1_INTEGER *ser = X509_get_serialNumber(x);
+                BIGNUM *bnser = ASN1_INTEGER_to_BN(ser, NULL);
+
+                if (!bnser)
+                    goto end;
+                if (!BN_add_word(bnser, 1))
+                    goto end;
+                ser = BN_to_ASN1_INTEGER(bnser, NULL);
+                if (!ser)
+                    goto end;
+                BN_free(bnser);
+                i2a_ASN1_INTEGER(out, ser);
+                ASN1_INTEGER_free(ser);
+                BIO_puts(out, "\n");
+            } else if ((email == i) || (ocsp_uri == i)) {
+                int j;
+                STACK_OF(OPENSSL_STRING) *emlst;
+                if (email == i)
+                    emlst = X509_get1_email(x);
+                else
+                    emlst = X509_get1_ocsp(x);
+                for (j = 0; j < sk_OPENSSL_STRING_num(emlst); j++)
+                    BIO_printf(out, "%s\n",
+                               sk_OPENSSL_STRING_value(emlst, j));
+                X509_email_free(emlst);
+            } else if (aliasout == i) {
+                unsigned char *alstr;
+                alstr = X509_alias_get0(x, NULL);
+                if (alstr)
+                    BIO_printf(out, "%s\n", alstr);
+                else
+                    BIO_puts(out, "\n");
+            } else if (subject_hash == i) {
+                BIO_printf(out, "%08lx\n", X509_subject_name_hash(x));
+            }
+#ifndef OPENSSL_NO_MD5
+            else if (subject_hash_old == i) {
+                BIO_printf(out, "%08lx\n", X509_subject_name_hash_old(x));
+            }
+#endif
+            else if (issuer_hash == i) {
+                BIO_printf(out, "%08lx\n", X509_issuer_name_hash(x));
+            }
+#ifndef OPENSSL_NO_MD5
+            else if (issuer_hash_old == i) {
+                BIO_printf(out, "%08lx\n", X509_issuer_name_hash_old(x));
+            }
+#endif
+            else if (pprint == i) {
+                X509_PURPOSE *ptmp;
+                int j;
+                BIO_printf(out, "Certificate purposes:\n");
+                for (j = 0; j < X509_PURPOSE_get_count(); j++) {
+                    ptmp = X509_PURPOSE_get0(j);
+                    purpose_print(out, x, ptmp);
+                }
+            } else if (modulus == i) {
+                EVP_PKEY *pkey;
+
+                pkey = X509_get0_pubkey(x);
+                if (pkey == NULL) {
+                    BIO_printf(bio_err, "Modulus=unavailable\n");
+                    ERR_print_errors(bio_err);
+                    goto end;
+                }
+                BIO_printf(out, "Modulus=");
+#ifndef OPENSSL_NO_RSA
+                if (EVP_PKEY_id(pkey) == EVP_PKEY_RSA) {
+                    const BIGNUM *n;
+                    RSA_get0_key(EVP_PKEY_get0_RSA(pkey), &n, NULL, NULL);
+                    BN_print(out, n);
+                } else
+#endif
+#ifndef OPENSSL_NO_DSA
+                if (EVP_PKEY_id(pkey) == EVP_PKEY_DSA) {
+                    const BIGNUM *dsapub = NULL;
+                    DSA_get0_key(EVP_PKEY_get0_DSA(pkey), &dsapub, NULL);
+                    BN_print(out, dsapub);
+                } else
+#endif
+                {
+                    BIO_printf(out, "Wrong Algorithm type");
+                }
+                BIO_printf(out, "\n");
+            } else if (pubkey == i) {
+                EVP_PKEY *pkey;
+
+                pkey = X509_get0_pubkey(x);
+                if (pkey == NULL) {
+                    BIO_printf(bio_err, "Error getting public key\n");
+                    ERR_print_errors(bio_err);
+                    goto end;
+                }
+                PEM_write_bio_PUBKEY(out, pkey);
+            } else if (C == i) {
+                unsigned char *d;
+                char *m;
+                int len;
+
+                X509_NAME_oneline(X509_get_subject_name(x), buf, sizeof buf);
+                BIO_printf(out, "/*\n"
+                                " * Subject: %s\n", buf);
+
+                X509_NAME_oneline(X509_get_issuer_name(x), buf, sizeof buf);
+                BIO_printf(out, " * Issuer:  %s\n"
+                                " */\n", buf);
+
+                len = i2d_X509(x, NULL);
+                m = app_malloc(len, "x509 name buffer");
+                d = (unsigned char *)m;
+                len = i2d_X509_NAME(X509_get_subject_name(x), &d);
+                print_array(out, "the_subject_name", len, (unsigned char *)m);
+                d = (unsigned char *)m;
+                len = i2d_X509_PUBKEY(X509_get_X509_PUBKEY(x), &d);
+                print_array(out, "the_public_key", len, (unsigned char *)m);
+                d = (unsigned char *)m;
+                len = i2d_X509(x, &d);
+                print_array(out, "the_certificate", len, (unsigned char *)m);
+                OPENSSL_free(m);
+            } else if (text == i) {
+                X509_print_ex(out, x, nmflag, certflag);
+            } else if (startdate == i) {
+                BIO_puts(out, "notBefore=");
+                ASN1_TIME_print(out, X509_get0_notBefore(x));
+                BIO_puts(out, "\n");
+            } else if (enddate == i) {
+                BIO_puts(out, "notAfter=");
+                ASN1_TIME_print(out, X509_get0_notAfter(x));
+                BIO_puts(out, "\n");
+            } else if (fingerprint == i) {
+                int j;
+                unsigned int n;
+                unsigned char md[EVP_MAX_MD_SIZE];
+                const EVP_MD *fdig = digest;
+
+                if (!fdig)
+                    fdig = EVP_get_digestbynid(NID_sha1);
+
+                if (!X509_digest(x, fdig, md, &n)) {
+                    BIO_printf(bio_err, "out of memory\n");
+                    goto end;
+                }
+                BIO_printf(out, "%s Fingerprint=",
+                           OBJ_nid2sn(EVP_MD_type(fdig)));
+                for (j = 0; j < (int)n; j++) {
+                    BIO_printf(out, "%02X%c", md[j], (j + 1 == (int)n)
+                               ? '\n' : ':');
+                }
+            }
+
+            /* should be in the library */
+            else if ((sign_flag == i) && (x509req == 0)) {
+                BIO_printf(bio_err, "Getting Private key\n");
+                if (Upkey == NULL) {
+                    Upkey = load_key(keyfile, keyformat, 0,
+                                     passin, e, "Private key");
+                    if (Upkey == NULL)
+                        goto end;
+                }
+
+                assert(need_rand);
+                if (!sign(x, Upkey, days, clrext, digest, extconf, extsect))
+                    goto end;
+            } else if (CA_flag == i) {
+                BIO_printf(bio_err, "Getting CA Private Key\n");
+                if (CAkeyfile != NULL) {
+                    CApkey = load_key(CAkeyfile, CAkeyformat,
+                                      0, passin, e, "CA Private Key");
+                    if (CApkey == NULL)
+                        goto end;
+                }
+
+                assert(need_rand);
+                if (!x509_certify(ctx, CAfile, digest, x, xca,
+                                  CApkey, sigopts,
+                                  CAserial, CA_createserial, days, clrext,
+                                  extconf, extsect, sno, reqfile))
+                    goto end;
+            } else if (x509req == i) {
+                EVP_PKEY *pk;
+
+                BIO_printf(bio_err, "Getting request Private Key\n");
+                if (keyfile == NULL) {
+                    BIO_printf(bio_err, "no request key file specified\n");
+                    goto end;
+                } else {
+                    pk = load_key(keyfile, keyformat, 0,
+                                  passin, e, "request key");
+                    if (pk == NULL)
+                        goto end;
+                }
+
+                BIO_printf(bio_err, "Generating certificate request\n");
+
+                rq = X509_to_X509_REQ(x, pk, digest);
+                EVP_PKEY_free(pk);
+                if (rq == NULL) {
+                    ERR_print_errors(bio_err);
+                    goto end;
+                }
+                if (!noout) {
+                    X509_REQ_print(out, rq);
+                    PEM_write_bio_X509_REQ(out, rq);
+                }
+                noout = 1;
+            } else if (ocspid == i) {
+                X509_ocspid_print(out, x);
+            }
+        }
+    }
+
+    if (checkend) {
+        time_t tcheck = time(NULL) + checkoffset;
+
+        if (X509_cmp_time(X509_get0_notAfter(x), &tcheck) < 0) {
+            BIO_printf(out, "Certificate will expire\n");
+            ret = 1;
+        } else {
+            BIO_printf(out, "Certificate will not expire\n");
+            ret = 0;
+        }
+        goto end;
+    }
+
+    print_cert_checks(out, x, checkhost, checkemail, checkip);
+
+    if (noout || nocert) {
+        ret = 0;
+        goto end;
+    }
+
+    if (outformat == FORMAT_ASN1)
+        i = i2d_X509_bio(out, x);
+    else if (outformat == FORMAT_PEM) {
+        if (trustout)
+            i = PEM_write_bio_X509_AUX(out, x);
+        else
+            i = PEM_write_bio_X509(out, x);
+    } else {
+        BIO_printf(bio_err, "bad output format specified for outfile\n");
+        goto end;
+    }
+    if (!i) {
+        BIO_printf(bio_err, "unable to write certificate\n");
+        ERR_print_errors(bio_err);
+        goto end;
+    }
+    ret = 0;
+ end:
+    if (need_rand)
+        app_RAND_write_file(NULL);
+    NCONF_free(extconf);
+    BIO_free_all(out);
+    X509_STORE_free(ctx);
+    X509_REQ_free(req);
+    X509_free(x);
+    X509_free(xca);
+    EVP_PKEY_free(Upkey);
+    EVP_PKEY_free(CApkey);
+    EVP_PKEY_free(fkey);
+    sk_OPENSSL_STRING_free(sigopts);
+    X509_REQ_free(rq);
+    ASN1_INTEGER_free(sno);
+    sk_ASN1_OBJECT_pop_free(trust, ASN1_OBJECT_free);
+    sk_ASN1_OBJECT_pop_free(reject, ASN1_OBJECT_free);
+    ASN1_OBJECT_free(objtmp);
+    release_engine(e);
+    OPENSSL_free(passin);
+    return (ret);
+}
+
+static ASN1_INTEGER *x509_load_serial(const char *CAfile, const char *serialfile,
+                                      int create)
+{
+    char *buf = NULL, *p;
+    ASN1_INTEGER *bs = NULL;
+    BIGNUM *serial = NULL;
+    size_t len;
+
+    len = ((serialfile == NULL)
+           ? (strlen(CAfile) + strlen(POSTFIX) + 1)
+           : (strlen(serialfile))) + 1;
+    buf = app_malloc(len, "serial# buffer");
+    if (serialfile == NULL) {
+        OPENSSL_strlcpy(buf, CAfile, len);
+        for (p = buf; *p; p++)
+            if (*p == '.') {
+                *p = '\0';
+                break;
+            }
+        OPENSSL_strlcat(buf, POSTFIX, len);
+    } else
+        OPENSSL_strlcpy(buf, serialfile, len);
+
+    serial = load_serial(buf, create, NULL);
+    if (serial == NULL)
+        goto end;
+
+    if (!BN_add_word(serial, 1)) {
+        BIO_printf(bio_err, "add_word failure\n");
+        goto end;
+    }
+
+    if (!save_serial(buf, NULL, serial, &bs))
+        goto end;
+
+ end:
+    OPENSSL_free(buf);
+    BN_free(serial);
+    return bs;
+}
+
+static int x509_certify(X509_STORE *ctx, const char *CAfile, const EVP_MD *digest,
+                        X509 *x, X509 *xca, EVP_PKEY *pkey,
+                        STACK_OF(OPENSSL_STRING) *sigopts,
+                        const char *serialfile, int create,
+                        int days, int clrext, CONF *conf, const char *section,
+                        ASN1_INTEGER *sno, int reqfile)
+{
+    int ret = 0;
+    ASN1_INTEGER *bs = NULL;
+    X509_STORE_CTX *xsc = NULL;
+    EVP_PKEY *upkey;
+
+    upkey = X509_get0_pubkey(xca);
+    if (upkey == NULL) {
+        BIO_printf(bio_err, "Error obtaining CA X509 public key\n");
+        goto end;
+    }
+    EVP_PKEY_copy_parameters(upkey, pkey);
+
+    xsc = X509_STORE_CTX_new();
+    if (xsc == NULL || !X509_STORE_CTX_init(xsc, ctx, x, NULL)) {
+        BIO_printf(bio_err, "Error initialising X509 store\n");
+        goto end;
+    }
+    if (sno)
+        bs = sno;
+    else if ((bs = x509_load_serial(CAfile, serialfile, create)) == NULL)
+        goto end;
+
+    /*
+     * NOTE: this certificate can/should be self signed, unless it was a
+     * certificate request in which case it is not.
+     */
+    X509_STORE_CTX_set_cert(xsc, x);
+    X509_STORE_CTX_set_flags(xsc, X509_V_FLAG_CHECK_SS_SIGNATURE);
+    if (!reqfile && X509_verify_cert(xsc) <= 0)
+        goto end;
+
+    if (!X509_check_private_key(xca, pkey)) {
+        BIO_printf(bio_err,
+                   "CA certificate and CA private key do not match\n");
+        goto end;
+    }
+
+    if (!X509_set_issuer_name(x, X509_get_subject_name(xca)))
+        goto end;
+    if (!X509_set_serialNumber(x, bs))
+        goto end;
+
+    if (!set_cert_times(x, NULL, NULL, days))
+        goto end;
+
+    if (clrext) {
+        while (X509_get_ext_count(x) > 0)
+            X509_delete_ext(x, 0);
+    }
+
+    if (conf) {
+        X509V3_CTX ctx2;
+        X509_set_version(x, 2); /* version 3 certificate */
+        X509V3_set_ctx(&ctx2, xca, x, NULL, NULL, 0);
+        X509V3_set_nconf(&ctx2, conf);
+        if (!X509V3_EXT_add_nconf(conf, &ctx2, section, x))
+            goto end;
+    }
+
+    if (!do_X509_sign(x, pkey, digest, sigopts))
+        goto end;
+    ret = 1;
+ end:
+    X509_STORE_CTX_free(xsc);
+    if (!ret)
+        ERR_print_errors(bio_err);
+    if (!sno)
+        ASN1_INTEGER_free(bs);
+    return ret;
+}
+
+static int callb(int ok, X509_STORE_CTX *ctx)
+{
+    int err;
+    X509 *err_cert;
+
+    /*
+     * it is ok to use a self signed certificate This case will catch both
+     * the initial ok == 0 and the final ok == 1 calls to this function
+     */
+    err = X509_STORE_CTX_get_error(ctx);
+    if (err == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT)
+        return 1;
+
+    /*
+     * BAD we should have gotten an error.  Normally if everything worked
+     * X509_STORE_CTX_get_error(ctx) will still be set to
+     * DEPTH_ZERO_SELF_....
+     */
+    if (ok) {
+        BIO_printf(bio_err,
+                   "error with certificate to be certified - should be self signed\n");
+        return 0;
+    } else {
+        err_cert = X509_STORE_CTX_get_current_cert(ctx);
+        print_name(bio_err, NULL, X509_get_subject_name(err_cert), 0);
+        BIO_printf(bio_err,
+                   "error with certificate - error %d at depth %d\n%s\n", err,
+                   X509_STORE_CTX_get_error_depth(ctx),
+                   X509_verify_cert_error_string(err));
+        return 1;
+    }
+}
+
+/* self sign */
+static int sign(X509 *x, EVP_PKEY *pkey, int days, int clrext,
+                const EVP_MD *digest, CONF *conf, const char *section)
+{
+
+    if (!X509_set_issuer_name(x, X509_get_subject_name(x)))
+        goto err;
+    if (!set_cert_times(x, NULL, NULL, days))
+        goto err;
+    if (!X509_set_pubkey(x, pkey))
+        goto err;
+    if (clrext) {
+        while (X509_get_ext_count(x) > 0)
+            X509_delete_ext(x, 0);
+    }
+    if (conf) {
+        X509V3_CTX ctx;
+        X509_set_version(x, 2); /* version 3 certificate */
+        X509V3_set_ctx(&ctx, x, x, NULL, NULL, 0);
+        X509V3_set_nconf(&ctx, conf);
+        if (!X509V3_EXT_add_nconf(conf, &ctx, section, x))
+            goto err;
+    }
+    if (!X509_sign(x, pkey, digest))
+        goto err;
+    return 1;
+ err:
+    ERR_print_errors(bio_err);
+    return 0;
+}
+
+static int purpose_print(BIO *bio, X509 *cert, X509_PURPOSE *pt)
+{
+    int id, i, idret;
+    const char *pname;
+    id = X509_PURPOSE_get_id(pt);
+    pname = X509_PURPOSE_get0_name(pt);
+    for (i = 0; i < 2; i++) {
+        idret = X509_check_purpose(cert, id, i);
+        BIO_printf(bio, "%s%s : ", pname, i ? " CA" : "");
+        if (idret == 1)
+            BIO_printf(bio, "Yes\n");
+        else if (idret == 0)
+            BIO_printf(bio, "No\n");
+        else
+            BIO_printf(bio, "Yes (WARNING code=%d)\n", idret);
+    }
+    return 1;
+}
diff --git a/appveyor.yml b/appveyor.yml
new file mode 100644
index 000000000..2d70f2023
--- /dev/null
+++ b/appveyor.yml
@@ -0,0 +1,56 @@
+platform:
+    - x64
+    - x86
+
+environment:
+    fast_finish: true
+    matrix:
+        - VSVER: 14
+
+configuration:
+    - shared
+    - plain
+
+before_build:
+    - ps: >-
+        If ($env:Platform -Match "x86") {
+            $env:VCVARS_PLATFORM="x86"
+            $env:TARGET="VC-WIN32 no-asm"
+        } Else {
+            $env:VCVARS_PLATFORM="amd64"
+            $env:TARGET="VC-WIN64A no-asm"
+        }
+    - ps: >-
+        If ($env:Configuration -Match "shared") {
+            $env:SHARED="no-makedepend"
+        } Else {
+            $env:SHARED="no-shared no-makedepend"
+        }
+    - ps: $env:VSCOMNTOOLS=(Get-Content ("env:VS" + "$env:VSVER" + "0COMNTOOLS"))
+    - call "%VSCOMNTOOLS%\..\..\VC\vcvarsall.bat" %VCVARS_PLATFORM%
+    - mkdir _build
+    - cd _build
+    - perl ..\Configure %TARGET% %SHARED%
+    - perl configdata.pm --dump
+    - cd ..
+    - ps: >-
+        if (-not $env:APPVEYOR_PULL_REQUEST_NUMBER`
+            -or (&git log -2 | Select-String "\[extended tests\]") ) {
+            $env:EXTENDED_TESTS="yes"
+        }
+build_script:
+    - cd _build
+    - ps: >-
+        If ($env:Configuration -Match "shared" -or $env:EXTENDED_TESTS) {
+            cmd /c "nmake 2>&1"
+        }
+    - cd ..
+
+test_script:
+    - cd _build
+    - ps: >-
+        if ($env:EXTENDED_TESTS) {
+            mkdir ..\_install
+            cmd /c "nmake install DESTDIR=..\_install 2>&1"
+        }
+    - cd ..
\ No newline at end of file
diff --git a/build.info b/build.info
new file mode 100644
index 000000000..fa136dc43
--- /dev/null
+++ b/build.info
@@ -0,0 +1,41 @@
+LIBS=libcrypto libssl
+ORDINALS[libcrypto]=crypto
+ORDINALS[libssl]=ssl
+INCLUDE[libcrypto]=. crypto/include include
+INCLUDE[libssl]=. include
+DEPEND[libssl]=libcrypto
+
+# Empty DEPEND "indices" means the dependencies are expected to be built
+# unconditionally before anything else.
+DEPEND[]=include/openssl/opensslconf.h crypto/include/internal/bn_conf.h \
+         crypto/include/internal/dso_conf.h
+DEPEND[include/openssl/opensslconf.h]=configdata.pm
+GENERATE[include/openssl/opensslconf.h]=include/openssl/opensslconf.h.in
+DEPEND[crypto/include/internal/bn_conf.h]=configdata.pm
+GENERATE[crypto/include/internal/bn_conf.h]=crypto/include/internal/bn_conf.h.in
+DEPEND[crypto/include/internal/dso_conf.h]=configdata.pm
+GENERATE[crypto/include/internal/dso_conf.h]=crypto/include/internal/dso_conf.h.in
+
+
+IF[{- $config{target} =~ /^Cygwin/ -}]
+ SHARED_NAME[libcrypto]=cygcrypto-{- $config{shlib_major}.".".$config{shlib_minor} -}
+ SHARED_NAME[libssl]=cygssl-{- $config{shlib_major}.".".$config{shlib_minor} -}
+ELSIF[{- $config{target} =~ /^mingw/ -}]
+ SHARED_NAME[libcrypto]=libcrypto-{- $config{shlib_major}."_".$config{shlib_minor} -}{- $config{target} eq "mingw64" ? "-x64" : "" -}
+ SHARED_NAME[libssl]=libssl-{- $config{shlib_major}."_".$config{shlib_minor} -}{- $config{target} eq "mingw64" ? "-x64" : "" -}
+ELSIF[{- $config{target} =~ /^VC-/ -}]
+ SHARED_NAME[libcrypto]=libcrypto-{- $config{shlib_major}."_".$config{shlib_minor} -}{- $target{multilib} -}
+ SHARED_NAME[libssl]=libssl-{- $config{shlib_major}."_".$config{shlib_minor} -}{- $target{multilib} -}
+ENDIF
+
+# VMS has a cultural standard where all libraries are prefixed.
+# For OpenSSL, the choice is 'ossl$' (this prefix was claimed in a
+# conversation with VSI, Tuesday January 26 2016)
+# Also, it seems it's usual to have the pointer size the libraries
+# were built for as part of the name.
+IF[{- $config{target} =~ /^vms/ -}]
+ RENAME[libcrypto]=ossl$libcrypto{- $target{pointer_size} -}
+ RENAME[libssl]=ossl$libssl{- $target{pointer_size} -}
+ SHARED_NAME[libcrypto]=ossl$libcrypto{- sprintf "%02d%02d", $config{shlib_major}, $config{shlib_minor} -}_shr{- $target{pointer_size} -}
+ SHARED_NAME[libssl]=ossl$libssl{- sprintf "%02d%02d", $config{shlib_major}, $config{shlib_minor} -}_shr{- $target{pointer_size} -}
+ENDIF
diff --git a/certs/mozilla-cacerts.pem b/certs/mozilla-cacerts.pem
new file mode 100755
index 000000000..c15368bdc
--- /dev/null
+++ b/certs/mozilla-cacerts.pem
@@ -0,0 +1,3865 @@
+##
+## Bundle of CA Root Certificates
+##
+## Certificate data from Mozilla as of: Wed Apr 20 03:12:05 2016
+##
+## This is a bundle of X.509 certificates of public Certificate Authorities
+## (CA). These were automatically extracted from Mozilla's root certificates
+## file (certdata.txt).  This file can be found in the mozilla source tree:
+## http://hg.mozilla.org/releases/mozilla-release/raw-file/default/security/nss/lib/ckfw/builtins/certdata.txt
+##
+## It contains the certificates in PEM format and therefore
+## can be directly used with curl / libcurl / php_curl, or with
+## an Apache+mod_ssl webserver for SSL client authentication.
+## Just configure this file as the SSLCACertificateFile.
+##
+## Conversion done with mk-ca-bundle.pl version 1.25.
+## SHA1: 5df367cda83086392e1acdf22bfef00c48d5eba6
+##
+
+
+GlobalSign Root CA
+==================
+-----BEGIN CERTIFICATE-----
+MIIDdTCCAl2gAwIBAgILBAAAAAABFUtaw5QwDQYJKoZIhvcNAQEFBQAwVzELMAkGA1UEBhMCQkUx
+GTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jvb3QgQ0ExGzAZBgNVBAMTEkds
+b2JhbFNpZ24gUm9vdCBDQTAeFw05ODA5MDExMjAwMDBaFw0yODAxMjgxMjAwMDBaMFcxCzAJBgNV
+BAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRswGQYD
+VQQDExJHbG9iYWxTaWduIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDa
+DuaZjc6j40+Kfvvxi4Mla+pIH/EqsLmVEQS98GPR4mdmzxzdzxtIK+6NiY6arymAZavpxy0Sy6sc
+THAHoT0KMM0VjU/43dSMUBUc71DuxC73/OlS8pF94G3VNTCOXkNz8kHp1Wrjsok6Vjk4bwY8iGlb
+Kk3Fp1S4bInMm/k8yuX9ifUSPJJ4ltbcdG6TRGHRjcdGsnUOhugZitVtbNV4FpWi6cgKOOvyJBNP
+c1STE4U6G7weNLWLBYy5d4ux2x8gkasJU26Qzns3dLlwR5EiUWMWea6xrkEmCMgZK9FGqkjWZCrX
+gzT/LCrBbBlDSgeF59N89iFo7+ryUp9/k5DPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV
+HRMBAf8EBTADAQH/MB0GA1UdDgQWBBRge2YaRQ2XyolQL30EzTSo//z9SzANBgkqhkiG9w0BAQUF
+AAOCAQEA1nPnfE920I2/7LqivjTFKDK1fPxsnCwrvQmeU79rXqoRSLblCKOzyj1hTdNGCbM+w6Dj
+Y1Ub8rrvrTnhQ7k4o+YviiY776BQVvnGCv04zcQLcFGUl5gE38NflNUVyRRBnMRddWQVDf9VMOyG
+j/8N7yy5Y0b2qvzfvGn9LhJIZJrglfCm7ymPAbEVtQwdpf5pLGkkeB6zpxxxYu7KyJesF12KwvhH
+hm4qxFYxldBniYUr+WymXUadDKqC5JlR3XC321Y9YeRq4VzW9v493kHMB65jUr9TU/Qr6cf9tveC
+X4XSQRjbgbMEHMUfpIBvFSDJ3gyICh3WZlXi/EjJKSZp4A==
+-----END CERTIFICATE-----
+
+GlobalSign Root CA - R2
+=======================
+-----BEGIN CERTIFICATE-----
+MIIDujCCAqKgAwIBAgILBAAAAAABD4Ym5g0wDQYJKoZIhvcNAQEFBQAwTDEgMB4GA1UECxMXR2xv
+YmFsU2lnbiBSb290IENBIC0gUjIxEzARBgNVBAoTCkdsb2JhbFNpZ24xEzARBgNVBAMTCkdsb2Jh
+bFNpZ24wHhcNMDYxMjE1MDgwMDAwWhcNMjExMjE1MDgwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxT
+aWduIFJvb3QgQ0EgLSBSMjETMBEGA1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2ln
+bjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKbPJA6+Lm8omUVCxKs+IVSbC9N/hHD6
+ErPLv4dfxn+G07IwXNb9rfF73OX4YJYJkhD10FPe+3t+c4isUoh7SqbKSaZeqKeMWhG8eoLrvozp
+s6yWJQeXSpkqBy+0Hne/ig+1AnwblrjFuTosvNYSuetZfeLQBoZfXklqtTleiDTsvHgMCJiEbKjN
+S7SgfQx5TfC4LcshytVsW33hoCmEofnTlEnLJGKRILzdC9XZzPnqJworc5HGnRusyMvo4KD0L5CL
+TfuwNhv2GXqF4G3yYROIXJ/gkwpRl4pazq+r1feqCapgvdzZX99yqWATXgAByUr6P6TqBwMhAo6C
+ygPCm48CAwEAAaOBnDCBmTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4E
+FgQUm+IHV2ccHsBqBt5ZtJot39wZhi4wNgYDVR0fBC8wLTAroCmgJ4YlaHR0cDovL2NybC5nbG9i
+YWxzaWduLm5ldC9yb290LXIyLmNybDAfBgNVHSMEGDAWgBSb4gdXZxwewGoG3lm0mi3f3BmGLjAN
+BgkqhkiG9w0BAQUFAAOCAQEAmYFThxxol4aR7OBKuEQLq4GsJ0/WwbgcQ3izDJr86iw8bmEbTUsp
+9Z8FHSbBuOmDAGJFtqkIk7mpM0sYmsL4h4hO291xNBrBVNpGP+DTKqttVCL1OmLNIG+6KYnX3ZHu
+01yiPqFbQfXf5WRDLenVOavSot+3i9DAgBkcRcAtjOj4LaR0VknFBbVPFd5uRHg5h6h+u/N5GJG7
+9G+dwfCMNYxdAfvDbbnvRG15RjF+Cv6pgsH/76tuIMRQyV+dTZsXjAzlAcmgQWpzU/qlULRuJQ/7
+TBj0/VLZjmmx6BEP3ojY+x1J96relc8geMJgEtslQIxq/H5COEBkEveegeGTLg==
+-----END CERTIFICATE-----
+
+Verisign Class 3 Public Primary Certification Authority - G3
+============================================================
+-----BEGIN CERTIFICATE-----
+MIIEGjCCAwICEQCbfgZJoz5iudXukEhxKe9XMA0GCSqGSIb3DQEBBQUAMIHKMQswCQYDVQQGEwJV
+UzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdv
+cmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNl
+IG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNh
+dGlvbiBBdXRob3JpdHkgLSBHMzAeFw05OTEwMDEwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMIHKMQsw
+CQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRy
+dXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhv
+cml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDMgUHVibGljIFByaW1hcnkg
+Q2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
+ggEBAMu6nFL8eB8aHm8bN3O9+MlrlBIwT/A2R/XQkQr1F8ilYcEWQE37imGQ5XYgwREGfassbqb1
+EUGO+i2tKmFZpGcmTNDovFJbcCAEWNF6yaRpvIMXZK0Fi7zQWM6NjPXr8EJJC52XJ2cybuGukxUc
+cLwgTS8Y3pKI6GyFVxEa6X7jJhFUokWWVYPKMIno3Nij7SqAP395ZVc+FSBmCC+Vk7+qRy+oRpfw
+EuL+wgorUeZ25rdGt+INpsyow0xZVYnm6FNcHOqd8GIWC6fJXwzw3sJ2zq/3avL6QaaiMxTJ5Xpj
+055iN9WFZZ4O5lMkdBteHRJTW8cs54NJOxWuimi5V5cCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEA
+ERSWwauSCPc/L8my/uRan2Te2yFPhpk0djZX3dAVL8WtfxUfN2JzPtTnX84XA9s1+ivbrmAJXx5f
+j267Cz3qWhMeDGBvtcC1IyIuBwvLqXTLR7sdwdela8wv0kL9Sd2nic9TutoAWii/gt/4uhMdUIaC
+/Y4wjylGsB49Ndo4YhYYSq3mtlFs3q9i6wHQHiT+eo8SGhJouPtmmRQURVyu565pF4ErWjfJXir0
+xuKhXFSbplQAz/DxwceYMBo7Nhbbo27q/a2ywtrvAkcTisDxszGtTxzhT5yvDwyd93gN2PQ1VoDa
+t20Xj50egWTh/sVFuq1ruQp6Tk9LhO5L8X3dEQ==
+-----END CERTIFICATE-----
+
+Entrust.net Premium 2048 Secure Server CA
+=========================================
+-----BEGIN CERTIFICATE-----
+MIIEKjCCAxKgAwIBAgIEOGPe+DANBgkqhkiG9w0BAQUFADCBtDEUMBIGA1UEChMLRW50cnVzdC5u
+ZXQxQDA+BgNVBAsUN3d3dy5lbnRydXN0Lm5ldC9DUFNfMjA0OCBpbmNvcnAuIGJ5IHJlZi4gKGxp
+bWl0cyBsaWFiLikxJTAjBgNVBAsTHChjKSAxOTk5IEVudHJ1c3QubmV0IExpbWl0ZWQxMzAxBgNV
+BAMTKkVudHJ1c3QubmV0IENlcnRpZmljYXRpb24gQXV0aG9yaXR5ICgyMDQ4KTAeFw05OTEyMjQx
+NzUwNTFaFw0yOTA3MjQxNDE1MTJaMIG0MRQwEgYDVQQKEwtFbnRydXN0Lm5ldDFAMD4GA1UECxQ3
+d3d3LmVudHJ1c3QubmV0L0NQU18yMDQ4IGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxpYWIuKTEl
+MCMGA1UECxMcKGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDEzMDEGA1UEAxMqRW50cnVzdC5u
+ZXQgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgKDIwNDgpMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
+MIIBCgKCAQEArU1LqRKGsuqjIAcVFmQqK0vRvwtKTY7tgHalZ7d4QMBzQshowNtTK91euHaYNZOL
+Gp18EzoOH1u3Hs/lJBQesYGpjX24zGtLA/ECDNyrpUAkAH90lKGdCCmziAv1h3edVc3kw37XamSr
+hRSGlVuXMlBvPci6Zgzj/L24ScF2iUkZ/cCovYmjZy/Gn7xxGWC4LeksyZB2ZnuU4q941mVTXTzW
+nLLPKQP5L6RQstRIzgUyVYr9smRMDuSYB3Xbf9+5CFVghTAp+XtIpGmG4zU/HoZdenoVve8AjhUi
+VBcAkCaTvA5JaJG/+EfTnZVCwQ5N328mz8MYIWJmQ3DW1cAH4QIDAQABo0IwQDAOBgNVHQ8BAf8E
+BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUVeSB0RGAvtiJuQijMfmhJAkWuXAwDQYJ
+KoZIhvcNAQEFBQADggEBADubj1abMOdTmXx6eadNl9cZlZD7Bh/KM3xGY4+WZiT6QBshJ8rmcnPy
+T/4xmf3IDExoU8aAghOY+rat2l098c5u9hURlIIM7j+VrxGrD9cv3h8Dj1csHsm7mhpElesYT6Yf
+zX1XEC+bBAlahLVu2B064dae0Wx5XnkcFMXj0EyTO2U87d89vqbllRrDtRnDvV5bu/8j72gZyxKT
+J1wDLW8w0B62GqzeWvfRqqgnpv55gcR5mTNXuhKwqeBCbJPKVt7+bYQLCIt+jerXmCHG8+c8eS9e
+nNFMFY3h7CI3zJpDC5fcgJCNs2ebb0gIFVbPv/ErfF6adulZkMV8gzURZVE=
+-----END CERTIFICATE-----
+
+Baltimore CyberTrust Root
+=========================
+-----BEGIN CERTIFICATE-----
+MIIDdzCCAl+gAwIBAgIEAgAAuTANBgkqhkiG9w0BAQUFADBaMQswCQYDVQQGEwJJRTESMBAGA1UE
+ChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSIwIAYDVQQDExlCYWx0aW1vcmUgQ3li
+ZXJUcnVzdCBSb290MB4XDTAwMDUxMjE4NDYwMFoXDTI1MDUxMjIzNTkwMFowWjELMAkGA1UEBhMC
+SUUxEjAQBgNVBAoTCUJhbHRpbW9yZTETMBEGA1UECxMKQ3liZXJUcnVzdDEiMCAGA1UEAxMZQmFs
+dGltb3JlIEN5YmVyVHJ1c3QgUm9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKME
+uyKrmD1X6CZymrV51Cni4eiVgLGw41uOKymaZN+hXe2wCQVt2yguzmKiYv60iNoS6zjrIZ3AQSsB
+UnuId9Mcj8e6uYi1agnnc+gRQKfRzMpijS3ljwumUNKoUMMo6vWrJYeKmpYcqWe4PwzV9/lSEy/C
+G9VwcPCPwBLKBsua4dnKM3p31vjsufFoREJIE9LAwqSuXmD+tqYF/LTdB1kC1FkYmGP1pWPgkAx9
+XbIGevOF6uvUA65ehD5f/xXtabz5OTZydc93Uk3zyZAsuT3lySNTPx8kmCFcB5kpvcY67Oduhjpr
+l3RjM71oGDHweI12v/yejl0qhqdNkNwnGjkCAwEAAaNFMEMwHQYDVR0OBBYEFOWdWTCCR1jMrPoI
+VDaGezq1BE3wMBIGA1UdEwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEB
+BQUAA4IBAQCFDF2O5G9RaEIFoN27TyclhAO992T9Ldcw46QQF+vaKSm2eT929hkTI7gQCvlYpNRh
+cL0EYWoSihfVCr3FvDB81ukMJY2GQE/szKN+OMY3EU/t3WgxjkzSswF07r51XgdIGn9w/xZchMB5
+hbgF/X++ZRGjD8ACtPhSNzkE1akxehi/oCr0Epn3o0WC4zxe9Z2etciefC7IpJ5OCBRLbf1wbWsa
+Y71k5h+3zvDyny67G7fyUIhzksLi4xaNmjICq44Y3ekQEe5+NauQrz4wlHrQMz2nZQ/1/I6eYs9H
+RCwBXbsdtTLSR9I4LtD+gdwyah617jzV/OeBHRnDJELqYzmp
+-----END CERTIFICATE-----
+
+AddTrust Low-Value Services Root
+================================
+-----BEGIN CERTIFICATE-----
+MIIEGDCCAwCgAwIBAgIBATANBgkqhkiG9w0BAQUFADBlMQswCQYDVQQGEwJTRTEUMBIGA1UEChML
+QWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3b3JrMSEwHwYDVQQDExhBZGRU
+cnVzdCBDbGFzcyAxIENBIFJvb3QwHhcNMDAwNTMwMTAzODMxWhcNMjAwNTMwMTAzODMxWjBlMQsw
+CQYDVQQGEwJTRTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBO
+ZXR3b3JrMSEwHwYDVQQDExhBZGRUcnVzdCBDbGFzcyAxIENBIFJvb3QwggEiMA0GCSqGSIb3DQEB
+AQUAA4IBDwAwggEKAoIBAQCWltQhSWDia+hBBwzexODcEyPNwTXH+9ZOEQpnXvUGW2ulCDtbKRY6
+54eyNAbFvAWlA3yCyykQruGIgb3WntP+LVbBFc7jJp0VLhD7Bo8wBN6ntGO0/7Gcrjyvd7ZWxbWr
+oulpOj0OM3kyP3CCkplhbY0wCI9xP6ZIVxn4JdxLZlyldI+Yrsj5wAYi56xz36Uu+1LcsRVlIPo1
+Zmne3yzxbrww2ywkEtvrNTVokMsAsJchPXQhI2U0K7t4WaPW4XY5mqRJjox0r26kmqPZm9I4XJui
+GMx1I4S+6+JNM3GOGvDC+Mcdoq0Dlyz4zyXG9rgkMbFjXZJ/Y/AlyVMuH79NAgMBAAGjgdIwgc8w
+HQYDVR0OBBYEFJWxtPCUtr3H2tERCSG+wa9J/RB7MAsGA1UdDwQEAwIBBjAPBgNVHRMBAf8EBTAD
+AQH/MIGPBgNVHSMEgYcwgYSAFJWxtPCUtr3H2tERCSG+wa9J/RB7oWmkZzBlMQswCQYDVQQGEwJT
+RTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3b3JrMSEw
+HwYDVQQDExhBZGRUcnVzdCBDbGFzcyAxIENBIFJvb3SCAQEwDQYJKoZIhvcNAQEFBQADggEBACxt
+ZBsfzQ3duQH6lmM0MkhHma6X7f1yFqZzR1r0693p9db7RcwpiURdv0Y5PejuvE1Uhh4dbOMXJ0Ph
+iVYrqW9yTkkz43J8KiOavD7/KCrto/8cI7pDVwlnTUtiBi34/2ydYB7YHEt9tTEv2dB8Xfjea4MY
+eDdXL+gzB2ffHsdrKpV2ro9Xo/D0UrSpUwjP4E/TelOL/bscVjby/rK25Xa71SJlpz/+0WatC7xr
+mYbvP33zGDLKe8bjq2RGlfgmadlVg3sslgf/WSxEo8bl6ancoWOAWiFeIc9TVPC6b4nbqKqVz4vj
+ccweGyBECMB6tkD9xOQ14R0WHNC8K47Wcdk=
+-----END CERTIFICATE-----
+
+AddTrust External Root
+======================
+-----BEGIN CERTIFICATE-----
+MIIENjCCAx6gAwIBAgIBATANBgkqhkiG9w0BAQUFADBvMQswCQYDVQQGEwJTRTEUMBIGA1UEChML
+QWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFkZFRydXN0IEV4dGVybmFsIFRUUCBOZXR3b3JrMSIwIAYD
+VQQDExlBZGRUcnVzdCBFeHRlcm5hbCBDQSBSb290MB4XDTAwMDUzMDEwNDgzOFoXDTIwMDUzMDEw
+NDgzOFowbzELMAkGA1UEBhMCU0UxFDASBgNVBAoTC0FkZFRydXN0IEFCMSYwJAYDVQQLEx1BZGRU
+cnVzdCBFeHRlcm5hbCBUVFAgTmV0d29yazEiMCAGA1UEAxMZQWRkVHJ1c3QgRXh0ZXJuYWwgQ0Eg
+Um9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALf3GjPm8gAELTngTlvtH7xsD821
++iO2zt6bETOXpClMfZOfvUq8k+0DGuOPz+VtUFrWlymUWoCwSXrbLpX9uMq/NzgtHj6RQa1wVsfw
+Tz/oMp50ysiQVOnGXw94nZpAPA6sYapeFI+eh6FqUNzXmk6vBbOmcZSccbNQYArHE504B4YCqOmo
+aSYYkKtMsE8jqzpPhNjfzp/haW+710LXa0Tkx63ubUFfclpxCDezeWWkWaCUN/cALw3CknLa0Dhy
+2xSoRcRdKn23tNbE7qzNE0S3ySvdQwAl+mG5aWpYIxG3pzOPVnVZ9c0p10a3CitlttNCbxWyuHv7
+7+ldU9U0WicCAwEAAaOB3DCB2TAdBgNVHQ4EFgQUrb2YejS0Jvf6xCZU7wO94CTLVBowCwYDVR0P
+BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wgZkGA1UdIwSBkTCBjoAUrb2YejS0Jvf6xCZU7wO94CTL
+VBqhc6RxMG8xCzAJBgNVBAYTAlNFMRQwEgYDVQQKEwtBZGRUcnVzdCBBQjEmMCQGA1UECxMdQWRk
+VHJ1c3QgRXh0ZXJuYWwgVFRQIE5ldHdvcmsxIjAgBgNVBAMTGUFkZFRydXN0IEV4dGVybmFsIENB
+IFJvb3SCAQEwDQYJKoZIhvcNAQEFBQADggEBALCb4IUlwtYj4g+WBpKdQZic2YR5gdkeWxQHIzZl
+j7DYd7usQWxHYINRsPkyPef89iYTx4AWpb9a/IfPeHmJIZriTAcKhjW88t5RxNKWt9x+Tu5w/Rw5
+6wwCURQtjr0W4MHfRnXnJK3s9EK0hZNwEGe6nQY1ShjTK3rMUUKhemPR5ruhxSvCNr4TDea9Y355
+e6cJDUCrat2PisP29owaQgVR1EX1n6diIWgVIEM8med8vSTYqZEXc4g/VhsxOBi0cQ+azcgOno4u
+G+GMmIPLHzHxREzGBHNJdmAPx/i9F4BrLunMTA5amnkPIAou1Z5jJh5VkpTYghdae9C8x49OhgQ=
+-----END CERTIFICATE-----
+
+AddTrust Public Services Root
+=============================
+-----BEGIN CERTIFICATE-----
+MIIEFTCCAv2gAwIBAgIBATANBgkqhkiG9w0BAQUFADBkMQswCQYDVQQGEwJTRTEUMBIGA1UEChML
+QWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3b3JrMSAwHgYDVQQDExdBZGRU
+cnVzdCBQdWJsaWMgQ0EgUm9vdDAeFw0wMDA1MzAxMDQxNTBaFw0yMDA1MzAxMDQxNTBaMGQxCzAJ
+BgNVBAYTAlNFMRQwEgYDVQQKEwtBZGRUcnVzdCBBQjEdMBsGA1UECxMUQWRkVHJ1c3QgVFRQIE5l
+dHdvcmsxIDAeBgNVBAMTF0FkZFRydXN0IFB1YmxpYyBDQSBSb290MIIBIjANBgkqhkiG9w0BAQEF
+AAOCAQ8AMIIBCgKCAQEA6Rowj4OIFMEg2Dybjxt+A3S72mnTRqX4jsIMEZBRpS9mVEBV6tsfSlbu
+nyNu9DnLoblv8n75XYcmYZ4c+OLspoH4IcUkzBEMP9smcnrHAZcHF/nXGCwwfQ56HmIexkvA/X1i
+d9NEHif2P0tEs7c42TkfYNVRknMDtABp4/MUTu7R3AnPdzRGULD4EfL+OHn3Bzn+UZKXC1sIXzSG
+Aa2Il+tmzV7R/9x98oTaunet3IAIx6eH1lWfl2royBFkuucZKT8Rs3iQhCBSWxHveNCD9tVIkNAw
+HM+A+WD+eeSI8t0A65RF62WUaUC6wNW0uLp9BBGo6zEFlpROWCGOn9Bg/QIDAQABo4HRMIHOMB0G
+A1UdDgQWBBSBPjfYkrAfd59ctKtzquf2NGAv+jALBgNVHQ8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB
+/zCBjgYDVR0jBIGGMIGDgBSBPjfYkrAfd59ctKtzquf2NGAv+qFopGYwZDELMAkGA1UEBhMCU0Ux
+FDASBgNVBAoTC0FkZFRydXN0IEFCMR0wGwYDVQQLExRBZGRUcnVzdCBUVFAgTmV0d29yazEgMB4G
+A1UEAxMXQWRkVHJ1c3QgUHVibGljIENBIFJvb3SCAQEwDQYJKoZIhvcNAQEFBQADggEBAAP3FUr4
+JNojVhaTdt02KLmuG7jD8WS6IBh4lSknVwW8fCr0uVFV2ocC3g8WFzH4qnkuCRO7r7IgGRLlk/lL
++YPoRNWyQSW/iHVv/xD8SlTQX/D67zZzfRs2RcYhbbQVuE7PnFylPVoAjgbjPGsye/Kf8Lb93/Ao
+GEjwxrzQvzSAlsJKsW2Ox5BF3i9nrEUEo3rcVZLJR2bYGozH7ZxOmuASu7VqTITh4SINhwBk/ox9
+Yjllpu9CtoAlEmEBqCQTcAARJl/6NVDFSMwGR+gn2HCNX2TmoUQmXiLsks3/QppEIW1cxeMiHV9H
+EufOX1362KqxMy3ZdvJOOjMMK7MtkAY=
+-----END CERTIFICATE-----
+
+AddTrust Qualified Certificates Root
+====================================
+-----BEGIN CERTIFICATE-----
+MIIEHjCCAwagAwIBAgIBATANBgkqhkiG9w0BAQUFADBnMQswCQYDVQQGEwJTRTEUMBIGA1UEChML
+QWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3b3JrMSMwIQYDVQQDExpBZGRU
+cnVzdCBRdWFsaWZpZWQgQ0EgUm9vdDAeFw0wMDA1MzAxMDQ0NTBaFw0yMDA1MzAxMDQ0NTBaMGcx
+CzAJBgNVBAYTAlNFMRQwEgYDVQQKEwtBZGRUcnVzdCBBQjEdMBsGA1UECxMUQWRkVHJ1c3QgVFRQ
+IE5ldHdvcmsxIzAhBgNVBAMTGkFkZFRydXN0IFF1YWxpZmllZCBDQSBSb290MIIBIjANBgkqhkiG
+9w0BAQEFAAOCAQ8AMIIBCgKCAQEA5B6a/twJWoekn0e+EV+vhDTbYjx5eLfpMLXsDBwqxBb/4Oxx
+64r1EW7tTw2R0hIYLUkVAcKkIhPHEWT/IhKauY5cLwjPcWqzZwFZ8V1G87B4pfYOQnrjfxvM0PC3
+KP0q6p6zsLkEqv32x7SxuCqg+1jxGaBvcCV+PmlKfw8i2O+tCBGaKZnhqkRFmhJePp1tUvznoD1o
+L/BLcHwTOK28FSXx1s6rosAx1i+f4P8UWfyEk9mHfExUE+uf0S0R+Bg6Ot4l2ffTQO2kBhLEO+GR
+wVY18BTcZTYJbqukB8c10cIDMzZbdSZtQvESa0NvS3GU+jQd7RNuyoB/mC9suWXY6QIDAQABo4HU
+MIHRMB0GA1UdDgQWBBQ5lYtii1zJ1IC6WA+XPxUIQ8yYpzALBgNVHQ8EBAMCAQYwDwYDVR0TAQH/
+BAUwAwEB/zCBkQYDVR0jBIGJMIGGgBQ5lYtii1zJ1IC6WA+XPxUIQ8yYp6FrpGkwZzELMAkGA1UE
+BhMCU0UxFDASBgNVBAoTC0FkZFRydXN0IEFCMR0wGwYDVQQLExRBZGRUcnVzdCBUVFAgTmV0d29y
+azEjMCEGA1UEAxMaQWRkVHJ1c3QgUXVhbGlmaWVkIENBIFJvb3SCAQEwDQYJKoZIhvcNAQEFBQAD
+ggEBABmrder4i2VhlRO6aQTvhsoToMeqT2QbPxj2qC0sVY8FtzDqQmodwCVRLae/DLPt7wh/bDxG
+GuoYQ992zPlmhpwsaPXpF/gxsxjE1kh9I0xowX67ARRvxdlu3rsEQmr49lx95dr6h+sNNVJn0J6X
+dgWTP5XHAeZpVTh/EGGZyeNfpso+gmNIquIISD6q8rKFYqa0p9m9N5xotS1WfbC3P6CxB9bpT9ze
+RXEwMn8bLgn5v1Kh7sKAPgZcLlVAwRv1cEWw3F369nJad9Jjzc9YiQBCYz95OdBEsIJuQRno3eDB
+iFrRHnGTHyQwdOUeqN48Jzd/g66ed8/wMLH/S5noxqE=
+-----END CERTIFICATE-----
+
+Entrust Root Certification Authority
+====================================
+-----BEGIN CERTIFICATE-----
+MIIEkTCCA3mgAwIBAgIERWtQVDANBgkqhkiG9w0BAQUFADCBsDELMAkGA1UEBhMCVVMxFjAUBgNV
+BAoTDUVudHJ1c3QsIEluYy4xOTA3BgNVBAsTMHd3dy5lbnRydXN0Lm5ldC9DUFMgaXMgaW5jb3Jw
+b3JhdGVkIGJ5IHJlZmVyZW5jZTEfMB0GA1UECxMWKGMpIDIwMDYgRW50cnVzdCwgSW5jLjEtMCsG
+A1UEAxMkRW50cnVzdCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA2MTEyNzIwMjM0
+MloXDTI2MTEyNzIwNTM0MlowgbAxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1FbnRydXN0LCBJbmMu
+MTkwNwYDVQQLEzB3d3cuZW50cnVzdC5uZXQvQ1BTIGlzIGluY29ycG9yYXRlZCBieSByZWZlcmVu
+Y2UxHzAdBgNVBAsTFihjKSAyMDA2IEVudHJ1c3QsIEluYy4xLTArBgNVBAMTJEVudHJ1c3QgUm9v
+dCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
+ALaVtkNC+sZtKm9I35RMOVcF7sN5EUFoNu3s/poBj6E4KPz3EEZmLk0eGrEaTsbRwJWIsMn/MYsz
+A9u3g3s+IIRe7bJWKKf44LlAcTfFy0cOlypowCKVYhXbR9n10Cv/gkvJrT7eTNuQgFA/CYqEAOww
+Cj0Yzfv9KlmaI5UXLEWeH25DeW0MXJj+SKfFI0dcXv1u5x609mhF0YaDW6KKjbHjKYD+JXGIrb68
+j6xSlkuqUY3kEzEZ6E5Nn9uss2rVvDlUccp6en+Q3X0dgNmBu1kmwhH+5pPi94DkZfs0Nw4pgHBN
+rziGLp5/V6+eF67rHMsoIV+2HNjnogQi+dPa2MsCAwEAAaOBsDCBrTAOBgNVHQ8BAf8EBAMCAQYw
+DwYDVR0TAQH/BAUwAwEB/zArBgNVHRAEJDAigA8yMDA2MTEyNzIwMjM0MlqBDzIwMjYxMTI3MjA1
+MzQyWjAfBgNVHSMEGDAWgBRokORnpKZTgMeGZqTx90tD+4S9bTAdBgNVHQ4EFgQUaJDkZ6SmU4DH
+hmak8fdLQ/uEvW0wHQYJKoZIhvZ9B0EABBAwDhsIVjcuMTo0LjADAgSQMA0GCSqGSIb3DQEBBQUA
+A4IBAQCT1DCw1wMgKtD5Y+iRDAUgqV8ZyntyTtSx29CW+1RaGSwMCPeyvIWonX9tO1KzKtvn1ISM
+Y/YPyyYBkVBs9F8U4pN0wBOeMDpQ47RgxRzwIkSNcUesyBrJ6ZuaAGAT/3B+XxFNSRuzFVJ7yVTa
+v52Vr2ua2J7p8eRDjeIRRDq/r72DQnNSi6q7pynP9WQcCk3RvKqsnyrQ/39/2n3qse0wJcGE2jTS
+W3iDVuycNsMm4hH2Z0kdkquM++v/eu6FSqdQgPCnXEqULl8FmTxSQeDNtGPPAUO6nIPcj2A781q0
+tHuu2guQOHXvgR1m0vdXcDazv/wor3ElhVsT/h5/WrQ8
+-----END CERTIFICATE-----
+
+RSA Security 2048 v3
+====================
+-----BEGIN CERTIFICATE-----
+MIIDYTCCAkmgAwIBAgIQCgEBAQAAAnwAAAAKAAAAAjANBgkqhkiG9w0BAQUFADA6MRkwFwYDVQQK
+ExBSU0EgU2VjdXJpdHkgSW5jMR0wGwYDVQQLExRSU0EgU2VjdXJpdHkgMjA0OCBWMzAeFw0wMTAy
+MjIyMDM5MjNaFw0yNjAyMjIyMDM5MjNaMDoxGTAXBgNVBAoTEFJTQSBTZWN1cml0eSBJbmMxHTAb
+BgNVBAsTFFJTQSBTZWN1cml0eSAyMDQ4IFYzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
+AQEAt49VcdKA3XtpeafwGFAyPGJn9gqVB93mG/Oe2dJBVGutn3y+Gc37RqtBaB4Y6lXIL5F4iSj7
+Jylg/9+PjDvJSZu1pJTOAeo+tWN7fyb9Gd3AIb2E0S1PRsNO3Ng3OTsor8udGuorryGlwSMiuLgb
+WhOHV4PR8CDn6E8jQrAApX2J6elhc5SYcSa8LWrg903w8bYqODGBDSnhAMFRD0xS+ARaqn1y07iH
+KrtjEAMqs6FPDVpeRrc9DvV07Jmf+T0kgYim3WBU6JU2PcYJk5qjEoAAVZkZR73QpXzDuvsf9/UP
++Ky5tfQ3mBMY3oVbtwyCO4dvlTlYMNpuAWgXIszACwIDAQABo2MwYTAPBgNVHRMBAf8EBTADAQH/
+MA4GA1UdDwEB/wQEAwIBBjAfBgNVHSMEGDAWgBQHw1EwpKrpRa41JPr/JCwz0LGdjDAdBgNVHQ4E
+FgQUB8NRMKSq6UWuNST6/yQsM9CxnYwwDQYJKoZIhvcNAQEFBQADggEBAF8+hnZuuDU8TjYcHnmY
+v/3VEhF5Ug7uMYm83X/50cYVIeiKAVQNOvtUudZj1LGqlk2iQk3UUx+LEN5/Zb5gEydxiKRz44Rj
+0aRV4VCT5hsOedBnvEbIvz8XDZXmxpBp3ue0L96VfdASPz0+f00/FGj1EVDVwfSQpQgdMWD/YIwj
+VAqv/qFuxdF6Kmh4zx6CCiC0H63lhbJqaHVOrSU3lIW+vaHU6rcMSzyd6BIA8F+sDeGscGNz9395
+nzIlQnQFgCi/vcEkllgVsRch6YlL2weIZ/QVrXA+L02FO8K32/6YaCOJ4XQP3vTFhGMpG8zLB8kA
+pKnXwiJPZ9d37CAFYd4=
+-----END CERTIFICATE-----
+
+GeoTrust Global CA
+==================
+-----BEGIN CERTIFICATE-----
+MIIDVDCCAjygAwIBAgIDAjRWMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlVTMRYwFAYDVQQK
+Ew1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9iYWwgQ0EwHhcNMDIwNTIxMDQw
+MDAwWhcNMjIwNTIxMDQwMDAwWjBCMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5j
+LjEbMBkGA1UEAxMSR2VvVHJ1c3QgR2xvYmFsIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
+CgKCAQEA2swYYzD99BcjGlZ+W988bDjkcbd4kdS8odhM+KhDtgPpTSEHCIjaWC9mOSm9BXiLnTjo
+BbdqfnGk5sRgprDvgOSJKA+eJdbtg/OtppHHmMlCGDUUna2YRpIuT8rxh0PBFpVXLVDviS2Aelet
+8u5fa9IAjbkU+BQVNdnARqN7csiRv8lVK83Qlz6cJmTM386DGXHKTubU1XupGc1V3sjs0l44U+Vc
+T4wt/lAjNvxm5suOpDkZALeVAjmRCw7+OC7RHQWa9k0+bw8HHa8sHo9gOeL6NlMTOdReJivbPagU
+vTLrGAMoUgRx5aszPeE4uwc2hGKceeoWMPRfwCvocWvk+QIDAQABo1MwUTAPBgNVHRMBAf8EBTAD
+AQH/MB0GA1UdDgQWBBTAephojYn7qwVkDBF9qn1luMrMTjAfBgNVHSMEGDAWgBTAephojYn7qwVk
+DBF9qn1luMrMTjANBgkqhkiG9w0BAQUFAAOCAQEANeMpauUvXVSOKVCUn5kaFOSPeCpilKInZ57Q
+zxpeR+nBsqTP3UEaBU6bS+5Kb1VSsyShNwrrZHYqLizz/Tt1kL/6cdjHPTfStQWVYrmm3ok9Nns4
+d0iXrKYgjy6myQzCsplFAMfOEVEiIuCl6rYVSAlk6l5PdPcFPseKUgzbFbS9bZvlxrFUaKnjaZC2
+mqUPuLk/IH2uSrW4nOQdtqvmlKXBx4Ot2/Unhw4EbNX/3aBd7YdStysVAq45pmp06drE57xNNB6p
+XE0zX5IJL4hmXXeXxx12E6nV5fEWCRE11azbJHFwLJhWC9kXtNHjUStedejV0NxPNO3CBWaAocvm
+Mw==
+-----END CERTIFICATE-----
+
+GeoTrust Global CA 2
+====================
+-----BEGIN CERTIFICATE-----
+MIIDZjCCAk6gAwIBAgIBATANBgkqhkiG9w0BAQUFADBEMQswCQYDVQQGEwJVUzEWMBQGA1UEChMN
+R2VvVHJ1c3QgSW5jLjEdMBsGA1UEAxMUR2VvVHJ1c3QgR2xvYmFsIENBIDIwHhcNMDQwMzA0MDUw
+MDAwWhcNMTkwMzA0MDUwMDAwWjBEMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5j
+LjEdMBsGA1UEAxMUR2VvVHJ1c3QgR2xvYmFsIENBIDIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
+ggEKAoIBAQDvPE1APRDfO1MA4Wf+lGAVPoWI8YkNkMgoI5kF6CsgncbzYEbYwbLVjDHZ3CB5JIG/
+NTL8Y2nbsSpr7iFY8gjpeMtvy/wWUsiRxP89c96xPqfCfWbB9X5SJBri1WeR0IIQ13hLTytCOb1k
+LUCgsBDTOEhGiKEMuzozKmKY+wCdE1l/bztyqu6mD4b5BWHqZ38MN5aL5mkWRxHCJ1kDs6ZgwiFA
+Vvqgx306E+PsV8ez1q6diYD3Aecs9pYrEw15LNnA5IZ7S4wMcoKK+xfNAGw6EzywhIdLFnopsk/b
+HdQL82Y3vdj2V7teJHq4PIu5+pIaGoSe2HSPqht/XvT+RSIhAgMBAAGjYzBhMA8GA1UdEwEB/wQF
+MAMBAf8wHQYDVR0OBBYEFHE4NvICMVNHK266ZUapEBVYIAUJMB8GA1UdIwQYMBaAFHE4NvICMVNH
+K266ZUapEBVYIAUJMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQUFAAOCAQEAA/e1K6tdEPx7
+srJerJsOflN4WT5CBP51o62sgU7XAotexC3IUnbHLB/8gTKY0UvGkpMzNTEv/NgdRN3ggX+d6Yvh
+ZJFiCzkIjKx0nVnZellSlxG5FntvRdOW2TF9AjYPnDtuzywNA0ZF66D0f0hExghAzN4bcLUprbqL
+OzRldRtxIR0sFAqwlpW41uryZfspuk/qkZN0abby/+Ea0AzRdoXLiiW9l14sbxWZJue2Kf8i7MkC
+x1YAzUm5s2x7UwQa4qjJqhIFI8LO57sEAszAR6LkxCkvW0VXiVHuPOtSCP8HNR6fNWpHSlaY0VqF
+H4z1Ir+rzoPz4iIprn2DQKi6bA==
+-----END CERTIFICATE-----
+
+GeoTrust Universal CA
+=====================
+-----BEGIN CERTIFICATE-----
+MIIFaDCCA1CgAwIBAgIBATANBgkqhkiG9w0BAQUFADBFMQswCQYDVQQGEwJVUzEWMBQGA1UEChMN
+R2VvVHJ1c3QgSW5jLjEeMBwGA1UEAxMVR2VvVHJ1c3QgVW5pdmVyc2FsIENBMB4XDTA0MDMwNDA1
+MDAwMFoXDTI5MDMwNDA1MDAwMFowRTELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IElu
+Yy4xHjAcBgNVBAMTFUdlb1RydXN0IFVuaXZlcnNhbCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIP
+ADCCAgoCggIBAKYVVaCjxuAfjJ0hUNfBvitbtaSeodlyWL0AG0y/YckUHUWCq8YdgNY96xCcOq9t
+JPi8cQGeBvV8Xx7BDlXKg5pZMK4ZyzBIle0iN430SppyZj6tlcDgFgDgEB8rMQ7XlFTTQjOgNB0e
+RXbdT8oYN+yFFXoZCPzVx5zw8qkuEKmS5j1YPakWaDwvdSEYfyh3peFhF7em6fgemdtzbvQKoiFs
+7tqqhZJmr/Z6a4LauiIINQ/PQvE1+mrufislzDoR5G2vc7J2Ha3QsnhnGqQ5HFELZ1aD/ThdDc7d
+8Lsrlh/eezJS/R27tQahsiFepdaVaH/wmZ7cRQg+59IJDTWU3YBOU5fXtQlEIGQWFwMCTFMNaN7V
+qnJNk22CDtucvc+081xdVHppCZbW2xHBjXWotM85yM48vCR85mLK4b19p71XZQvk/iXttmkQ3Cga
+Rr0BHdCXteGYO8A3ZNY9lO4L4fUorgtWv3GLIylBjobFS1J72HGrH4oVpjuDWtdYAVHGTEHZf9hB
+Z3KiKN9gg6meyHv8U3NyWfWTehd2Ds735VzZC1U0oqpbtWpU5xPKV+yXbfReBi9Fi1jUIxaS5BZu
+KGNZMN9QAZxjiRqf2xeUgnA3wySemkfWWspOqGmJch+RbNt+nhutxx9z3SxPGWX9f5NAEC7S8O08
+ni4oPmkmM8V7AgMBAAGjYzBhMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFNq7LqqwDLiIJlF0
+XG0D08DYj3rWMB8GA1UdIwQYMBaAFNq7LqqwDLiIJlF0XG0D08DYj3rWMA4GA1UdDwEB/wQEAwIB
+hjANBgkqhkiG9w0BAQUFAAOCAgEAMXjmx7XfuJRAyXHEqDXsRh3ChfMoWIawC/yOsjmPRFWrZIRc
+aanQmjg8+uUfNeVE44B5lGiku8SfPeE0zTBGi1QrlaXv9z+ZhP015s8xxtxqv6fXIwjhmF7DWgh2
+qaavdy+3YL1ERmrvl/9zlcGO6JP7/TG37FcREUWbMPEaiDnBTzynANXH/KttgCJwpQzgXQQpAvvL
+oJHRfNbDflDVnVi+QTjruXU8FdmbyUqDWcDaU/0zuzYYm4UPFd3uLax2k7nZAY1IEKj79TiG8dsK
+xr2EoyNB3tZ3b4XUhRxQ4K5RirqNPnbiucon8l+f725ZDQbYKxek0nxru18UGkiPGkzns0ccjkxF
+KyDuSN/n3QmOGKjaQI2SJhFTYXNd673nxE0pN2HrrDktZy4W1vUAg4WhzH92xH3kt0tm7wNFYGm2
+DFKWkoRepqO1pD4r2czYG0eq8kTaT/kD6PAUyz/zg97QwVTjt+gKN02LIFkDMBmhLMi9ER/frslK
+xfMnZmaGrGiR/9nmUxwPi1xpZQomyB40w11Re9epnAahNt3ViZS82eQtDF4JbAiXfKM9fJP/P6EU
+p8+1Xevb2xzEdt+Iub1FBZUbrvxGakyvSOPOrg/SfuvmbJxPgWp6ZKy7PtXny3YuxadIwVyQD8vI
+P/rmMuGNG2+k5o7Y+SlIis5z/iw=
+-----END CERTIFICATE-----
+
+GeoTrust Universal CA 2
+=======================
+-----BEGIN CERTIFICATE-----
+MIIFbDCCA1SgAwIBAgIBATANBgkqhkiG9w0BAQUFADBHMQswCQYDVQQGEwJVUzEWMBQGA1UEChMN
+R2VvVHJ1c3QgSW5jLjEgMB4GA1UEAxMXR2VvVHJ1c3QgVW5pdmVyc2FsIENBIDIwHhcNMDQwMzA0
+MDUwMDAwWhcNMjkwMzA0MDUwMDAwWjBHMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3Qg
+SW5jLjEgMB4GA1UEAxMXR2VvVHJ1c3QgVW5pdmVyc2FsIENBIDIwggIiMA0GCSqGSIb3DQEBAQUA
+A4ICDwAwggIKAoICAQCzVFLByT7y2dyxUxpZKeexw0Uo5dfR7cXFS6GqdHtXr0om/Nj1XqduGdt0
+DE81WzILAePb63p3NeqqWuDW6KFXlPCQo3RWlEQwAx5cTiuFJnSCegx2oG9NzkEtoBUGFF+3Qs17
+j1hhNNwqCPkuwwGmIkQcTAeC5lvO0Ep8BNMZcyfwqph/Lq9O64ceJHdqXbboW0W63MOhBW9Wjo8Q
+JqVJwy7XQYci4E+GymC16qFjwAGXEHm9ADwSbSsVsaxLse4YuU6W3Nx2/zu+z18DwPw76L5GG//a
+QMJS9/7jOvdqdzXQ2o3rXhhqMcceujwbKNZrVMaqW9eiLBsZzKIC9ptZvTdrhrVtgrrY6slWvKk2
+WP0+GfPtDCapkzj4T8FdIgbQl+rhrcZV4IErKIM6+vR7IVEAvlI4zs1meaj0gVbi0IMJR1FbUGrP
+20gaXT73y/Zl92zxlfgCOzJWgjl6W70viRu/obTo/3+NjN8D8WBOWBFM66M/ECuDmgFz2ZRthAAn
+ZqzwcEAJQpKtT5MNYQlRJNiS1QuUYbKHsu3/mjX/hVTK7URDrBs8FmtISgocQIgfksILAAX/8sgC
+SqSqqcyZlpwvWOB94b67B9xfBHJcMTTD7F8t4D1kkCLm0ey4Lt1ZrtmhN79UNdxzMk+MBB4zsslG
+8dhcyFVQyWi9qLo2CQIDAQABo2MwYTAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBR281Xh+qQ2
++/CfXGJx7Tz0RzgQKzAfBgNVHSMEGDAWgBR281Xh+qQ2+/CfXGJx7Tz0RzgQKzAOBgNVHQ8BAf8E
+BAMCAYYwDQYJKoZIhvcNAQEFBQADggIBAGbBxiPz2eAubl/oz66wsCVNK/g7WJtAJDday6sWSf+z
+dXkzoS9tcBc0kf5nfo/sm+VegqlVHy/c1FEHEv6sFj4sNcZj/NwQ6w2jqtB8zNHQL1EuxBRa3ugZ
+4T7GzKQp5y6EqgYweHZUcyiYWTjgAA1i00J9IZ+uPTqM1fp3DRgrFg5fNuH8KrUwJM/gYwx7WBr+
+mbpCErGR9Hxo4sjoryzqyX6uuyo9DRXcNJW2GHSoag/HtPQTxORb7QrSpJdMKu0vbBKJPfEncKpq
+A1Ihn0CoZ1Dy81of398j9tx4TuaYT1U6U+Pv8vSfx3zYWK8pIpe44L2RLrB27FcRz+8pRPPphXpg
+Y+RdM4kX2TGq2tbzGDVyz4crL2MjhF2EjD9XoIj8mZEoJmmZ1I+XRL6O1UixpCgp8RW04eWe3fiP
+pm8m1wk8OhwRDqZsN/etRIcsKMfYdIKz0G9KV7s1KSegi+ghp4dkNl3M2Basx7InQJJVOCiNUW7d
+FGdTbHFcJoRNdVq2fmBWqU2t+5sel/MN2dKXVHfaPRK34B7vCAas+YWH6aLcr34YEoP9VhdBLtUp
+gn2Z9DH2canPLAEnpQW5qrJITirvn5NSUZU8UnOOVkwXQMAJKOSLakhT2+zNVVXxxvjpoixMptEm
+X36vWkzaH6byHCx+rgIW0lbQL1dTR+iS
+-----END CERTIFICATE-----
+
+Visa eCommerce Root
+===================
+-----BEGIN CERTIFICATE-----
+MIIDojCCAoqgAwIBAgIQE4Y1TR0/BvLB+WUF1ZAcYjANBgkqhkiG9w0BAQUFADBrMQswCQYDVQQG
+EwJVUzENMAsGA1UEChMEVklTQTEvMC0GA1UECxMmVmlzYSBJbnRlcm5hdGlvbmFsIFNlcnZpY2Ug
+QXNzb2NpYXRpb24xHDAaBgNVBAMTE1Zpc2EgZUNvbW1lcmNlIFJvb3QwHhcNMDIwNjI2MDIxODM2
+WhcNMjIwNjI0MDAxNjEyWjBrMQswCQYDVQQGEwJVUzENMAsGA1UEChMEVklTQTEvMC0GA1UECxMm
+VmlzYSBJbnRlcm5hdGlvbmFsIFNlcnZpY2UgQXNzb2NpYXRpb24xHDAaBgNVBAMTE1Zpc2EgZUNv
+bW1lcmNlIFJvb3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvV95WHm6h2mCxlCfL
+F9sHP4CFT8icttD0b0/Pmdjh28JIXDqsOTPHH2qLJj0rNfVIsZHBAk4ElpF7sDPwsRROEW+1QK8b
+RaVK7362rPKgH1g/EkZgPI2h4H3PVz4zHvtH8aoVlwdVZqW1LS7YgFmypw23RuwhY/81q6UCzyr0
+TP579ZRdhE2o8mCP2w4lPJ9zcc+U30rq299yOIzzlr3xF7zSujtFWsan9sYXiwGd/BmoKoMWuDpI
+/k4+oKsGGelT84ATB+0tvz8KPFUgOSwsAGl0lUq8ILKpeeUYiZGo3BxN77t+Nwtd/jmliFKMAGzs
+GHxBvfaLdXe6YJ2E5/4tAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEG
+MB0GA1UdDgQWBBQVOIMPPyw/cDMezUb+B4wg4NfDtzANBgkqhkiG9w0BAQUFAAOCAQEAX/FBfXxc
+CLkr4NWSR/pnXKUTwwMhmytMiUbPWU3J/qVAtmPN3XEolWcRzCSs00Rsca4BIGsDoo8Ytyk6feUW
+YFN4PMCvFYP3j1IzJL1kk5fui/fbGKhtcbP3LBfQdCVp9/5rPJS+TUtBjE7ic9DjkCJzQ83z7+pz
+zkWKsKZJ/0x9nXGIxHYdkFsd7v3M9+79YKWxehZx0RbQfBI8bGmX265fOZpwLwU8GUYEmSA20GBu
+YQa7FkKMcPcw++DbZqMAAb3mLNqRX6BGi01qnD093QVG/na/oAo85ADmJ7f/hC3euiInlhBx6yLt
+398znM/jra6O1I7mT1GvFpLgXPYHDw==
+-----END CERTIFICATE-----
+
+Certum Root CA
+==============
+-----BEGIN CERTIFICATE-----
+MIIDDDCCAfSgAwIBAgIDAQAgMA0GCSqGSIb3DQEBBQUAMD4xCzAJBgNVBAYTAlBMMRswGQYDVQQK
+ExJVbml6ZXRvIFNwLiB6IG8uby4xEjAQBgNVBAMTCUNlcnR1bSBDQTAeFw0wMjA2MTExMDQ2Mzla
+Fw0yNzA2MTExMDQ2MzlaMD4xCzAJBgNVBAYTAlBMMRswGQYDVQQKExJVbml6ZXRvIFNwLiB6IG8u
+by4xEjAQBgNVBAMTCUNlcnR1bSBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAM6x
+wS7TT3zNJc4YPk/EjG+AanPIW1H4m9LcuwBcsaD8dQPugfCI7iNS6eYVM42sLQnFdvkrOYCJ5JdL
+kKWoePhzQ3ukYbDYWMzhbGZ+nPMJXlVjhNWo7/OxLjBos8Q82KxujZlakE403Daaj4GIULdtlkIJ
+89eVgw1BS7Bqa/j8D35in2fE7SZfECYPCE/wpFcozo+47UX2bu4lXapuOb7kky/ZR6By6/qmW6/K
+Uz/iDsaWVhFu9+lmqSbYf5VT7QqFiLpPKaVCjF62/IUgAKpoC6EahQGcxEZjgoi2IrHu/qpGWX7P
+NSzVttpd90gzFFS269lvzs2I1qsb2pY7HVkCAwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zANBgkq
+hkiG9w0BAQUFAAOCAQEAuI3O7+cUus/usESSbLQ5PqKEbq24IXfS1HeCh+YgQYHu4vgRt2PRFze+
+GXYkHAQaTOs9qmdvLdTN/mUxcMUbpgIKumB7bVjCmkn+YzILa+M6wKyrO7Do0wlRjBCDxjTgxSvg
+GrZgFCdsMneMvLJymM/NzD+5yCRCFNZX/OYmQ6kd5YCQzgNUKD73P9P4Te1qCjqTE5s7FCMTY5w/
+0YcneeVMUeMBrYVdGjux1XMQpNPyvG5k9VpWkKjHDkx0Dy5xO/fIR/RpbxXyEV6DHpx8Uq79AtoS
+qFlnGNu8cN2bsWntgM6JQEhqDjXKKWYVIZQs6GAqm4VKQPNriiTsBhYscw==
+-----END CERTIFICATE-----
+
+Comodo AAA Services root
+========================
+-----BEGIN CERTIFICATE-----
+MIIEMjCCAxqgAwIBAgIBATANBgkqhkiG9w0BAQUFADB7MQswCQYDVQQGEwJHQjEbMBkGA1UECAwS
+R3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRowGAYDVQQKDBFDb21vZG8gQ0Eg
+TGltaXRlZDEhMB8GA1UEAwwYQUFBIENlcnRpZmljYXRlIFNlcnZpY2VzMB4XDTA0MDEwMTAwMDAw
+MFoXDTI4MTIzMTIzNTk1OVowezELMAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFuY2hl
+c3RlcjEQMA4GA1UEBwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxITAfBgNV
+BAMMGEFBQSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
+ggEBAL5AnfRu4ep2hxxNRUSOvkbIgwadwSr+GB+O5AL686tdUIoWMQuaBtDFcCLNSS1UY8y2bmhG
+C1Pqy0wkwLxyTurxFa70VJoSCsN6sjNg4tqJVfMiWPPe3M/vg4aijJRPn2jymJBGhCfHdr/jzDUs
+i14HZGWCwEiwqJH5YZ92IFCokcdmtet4YgNW8IoaE+oxox6gmf049vYnMlhvB/VruPsUK6+3qszW
+Y19zjNoFmag4qMsXeDZRrOme9Hg6jc8P2ULimAyrL58OAd7vn5lJ8S3frHRNG5i1R8XlKdH5kBjH
+Ypy+g8cmez6KJcfA3Z3mNWgQIJ2P2N7Sw4ScDV7oL8kCAwEAAaOBwDCBvTAdBgNVHQ4EFgQUoBEK
+Iz6W8Qfs4q8p74Klf9AwpLQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wewYDVR0f
+BHQwcjA4oDagNIYyaHR0cDovL2NybC5jb21vZG9jYS5jb20vQUFBQ2VydGlmaWNhdGVTZXJ2aWNl
+cy5jcmwwNqA0oDKGMGh0dHA6Ly9jcmwuY29tb2RvLm5ldC9BQUFDZXJ0aWZpY2F0ZVNlcnZpY2Vz
+LmNybDANBgkqhkiG9w0BAQUFAAOCAQEACFb8AvCb6P+k+tZ7xkSAzk/ExfYAWMymtrwUSWgEdujm
+7l3sAg9g1o1QGE8mTgHj5rCl7r+8dFRBv/38ErjHT1r0iWAFf2C3BUrz9vHCv8S5dIa2LX1rzNLz
+Rt0vxuBqw8M0Ayx9lt1awg6nCpnBBYurDC/zXDrPbDdVCYfeU0BsWO/8tqtlbgT2G9w84FoVxp7Z
+8VlIMCFlA2zs6SFz7JsDoeA3raAVGI/6ugLOpyypEBMs1OUIJqsil2D4kF501KKaU73yqWjgom7C
+12yxow+ev+to51byrvLjKzg6CYG1a4XXvi3tPxq3smPi9WIsgtRqAEFQ8TmDn5XpNpaYbg==
+-----END CERTIFICATE-----
+
+Comodo Secure Services root
+===========================
+-----BEGIN CERTIFICATE-----
+MIIEPzCCAyegAwIBAgIBATANBgkqhkiG9w0BAQUFADB+MQswCQYDVQQGEwJHQjEbMBkGA1UECAwS
+R3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRowGAYDVQQKDBFDb21vZG8gQ0Eg
+TGltaXRlZDEkMCIGA1UEAwwbU2VjdXJlIENlcnRpZmljYXRlIFNlcnZpY2VzMB4XDTA0MDEwMTAw
+MDAwMFoXDTI4MTIzMTIzNTk1OVowfjELMAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFu
+Y2hlc3RlcjEQMA4GA1UEBwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxJDAi
+BgNVBAMMG1NlY3VyZSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczCCASIwDQYJKoZIhvcNAQEBBQADggEP
+ADCCAQoCggEBAMBxM4KK0HDrc4eCQNUd5MvJDkKQ+d40uaG6EfQlhfPMcm3ye5drswfxdySRXyWP
+9nQ95IDC+DwN879A6vfIUtFyb+/Iq0G4bi4XKpVpDM3SHpR7LZQdqnXXs5jLrLxkU0C8j6ysNstc
+rbvd4JQX7NFc0L/vpZXJkMWwrPsbQ996CF23uPJAGysnnlDOXmWCiIxe004MeuoIkbY2qitC++rC
+oznl2yY4rYsK7hljxxwk3wN42ubqwUcaCwtGCd0C/N7Lh1/XMGNooa7cMqG6vv5Eq2i2pRcV/b3V
+p6ea5EQz6YiO/O1R65NxTq0B50SOqy3LqP4BSUjwwN3HaNiS/j0CAwEAAaOBxzCBxDAdBgNVHQ4E
+FgQUPNiTiMLAggnMAZkGkyDpnnAJY08wDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8w
+gYEGA1UdHwR6MHgwO6A5oDeGNWh0dHA6Ly9jcmwuY29tb2RvY2EuY29tL1NlY3VyZUNlcnRpZmlj
+YXRlU2VydmljZXMuY3JsMDmgN6A1hjNodHRwOi8vY3JsLmNvbW9kby5uZXQvU2VjdXJlQ2VydGlm
+aWNhdGVTZXJ2aWNlcy5jcmwwDQYJKoZIhvcNAQEFBQADggEBAIcBbSMdflsXfcFhMs+P5/OKlFlm
+4J4oqF7Tt/Q05qo5spcWxYJvMqTpjOev/e/C6LlLqqP05tqNZSH7uoDrJiiFGv45jN5bBAS0VPmj
+Z55B+glSzAVIqMk/IQQezkhr/IXownuvf7fM+F86/TXGDe+X3EyrEeFryzHRbPtIgKvcnDe4IRRL
+DXE97IMzbtFuMhbsmMcWi1mmNKsFVy2T96oTy9IT4rcuO81rUBcJaD61JlfutuC23bkpgHl9j6Pw
+pCikFcSF9CfUa7/lXORlAnZUtOM3ZiTTGWHIUhDlizeauan5Hb/qmZJhlv8BzaFfDbxxvA6sCx1H
+RR3B7Hzs/Sk=
+-----END CERTIFICATE-----
+
+Comodo Trusted Services root
+============================
+-----BEGIN CERTIFICATE-----
+MIIEQzCCAyugAwIBAgIBATANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJHQjEbMBkGA1UECAwS
+R3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRowGAYDVQQKDBFDb21vZG8gQ0Eg
+TGltaXRlZDElMCMGA1UEAwwcVHJ1c3RlZCBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczAeFw0wNDAxMDEw
+MDAwMDBaFw0yODEyMzEyMzU5NTlaMH8xCzAJBgNVBAYTAkdCMRswGQYDVQQIDBJHcmVhdGVyIE1h
+bmNoZXN0ZXIxEDAOBgNVBAcMB1NhbGZvcmQxGjAYBgNVBAoMEUNvbW9kbyBDQSBMaW1pdGVkMSUw
+IwYDVQQDDBxUcnVzdGVkIENlcnRpZmljYXRlIFNlcnZpY2VzMIIBIjANBgkqhkiG9w0BAQEFAAOC
+AQ8AMIIBCgKCAQEA33FvNlhTWvI2VFeAxHQIIO0Yfyod5jWaHiWsnOWWfnJSoBVC21ndZHoa0Lh7
+3TkVvFVIxO06AOoxEbrycXQaZ7jPM8yoMa+j49d/vzMtTGo87IvDktJTdyR0nAducPy9C1t2ul/y
+/9c3S0pgePfw+spwtOpZqqPOSC+pw7ILfhdyFgymBwwbOM/JYrc/oJOlh0Hyt3BAd9i+FHzjqMB6
+juljatEPmsbS9Is6FARW1O24zG71++IsWL1/T2sr92AkWCTOJu80kTrV44HQsvAEAtdbtz6SrGsS
+ivnkBbA7kUlcsutT6vifR4buv5XAwAaf0lteERv0xwQ1KdJVXOTt6wIDAQABo4HJMIHGMB0GA1Ud
+DgQWBBTFe1i97doladL3WRaoszLAeydb9DAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB
+/zCBgwYDVR0fBHwwejA8oDqgOIY2aHR0cDovL2NybC5jb21vZG9jYS5jb20vVHJ1c3RlZENlcnRp
+ZmljYXRlU2VydmljZXMuY3JsMDqgOKA2hjRodHRwOi8vY3JsLmNvbW9kby5uZXQvVHJ1c3RlZENl
+cnRpZmljYXRlU2VydmljZXMuY3JsMA0GCSqGSIb3DQEBBQUAA4IBAQDIk4E7ibSvuIQSTI3S8Ntw
+uleGFTQQuS9/HrCoiWChisJ3DFBKmwCL2Iv0QeLQg4pKHBQGsKNoBXAxMKdTmw7pSqBYaWcOrp32
+pSxBvzwGa+RZzG0Q8ZZvH9/0BAKkn0U+yNj6NkZEUD+Cl5EfKNsYEYwq5GWDVxISjBc/lDb+XbDA
+BHcTuPQV1T84zJQ6VdCsmPW6AF/ghhmBeC8owH7TzEIK9a5QoNE+xqFx7D+gIIxmOom0jtTYsU0l
+R+4viMi14QVFwL4Ucd56/Y57fU0IlqUSc/AtyjcndBInTMu2l+nZrghtWjlA3QVHdWpaIbOjGM9O
+9y5Xt5hwXsjEeLBi
+-----END CERTIFICATE-----
+
+QuoVadis Root CA
+================
+-----BEGIN CERTIFICATE-----
+MIIF0DCCBLigAwIBAgIEOrZQizANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJCTTEZMBcGA1UE
+ChMQUXVvVmFkaXMgTGltaXRlZDElMCMGA1UECxMcUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0
+eTEuMCwGA1UEAxMlUXVvVmFkaXMgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wMTAz
+MTkxODMzMzNaFw0yMTAzMTcxODMzMzNaMH8xCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRp
+cyBMaW1pdGVkMSUwIwYDVQQLExxSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MS4wLAYDVQQD
+EyVRdW9WYWRpcyBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEF
+AAOCAQ8AMIIBCgKCAQEAv2G1lVO6V/z68mcLOhrfEYBklbTRvM16z/Ypli4kVEAkOPcahdxYTMuk
+J0KX0J+DisPkBgNbAKVRHnAEdOLB1Dqr1607BxgFjv2DrOpm2RgbaIr1VxqYuvXtdj182d6UajtL
+F8HVj71lODqV0D1VNk7feVcxKh7YWWVJWCCYfqtffp/p1k3sg3Spx2zY7ilKhSoGFPlU5tPaZQeL
+YzcS19Dsw3sgQUSj7cugF+FxZc4dZjH3dgEZyH0DWLaVSR2mEiboxgx24ONmy+pdpibu5cxfvWen
+AScOospUxbF6lR1xHkopigPcakXBpBlebzbNw6Kwt/5cOOJSvPhEQ+aQuwIDAQABo4ICUjCCAk4w
+PQYIKwYBBQUHAQEEMTAvMC0GCCsGAQUFBzABhiFodHRwczovL29jc3AucXVvdmFkaXNvZmZzaG9y
+ZS5jb20wDwYDVR0TAQH/BAUwAwEB/zCCARoGA1UdIASCAREwggENMIIBCQYJKwYBBAG+WAABMIH7
+MIHUBggrBgEFBQcCAjCBxxqBxFJlbGlhbmNlIG9uIHRoZSBRdW9WYWRpcyBSb290IENlcnRpZmlj
+YXRlIGJ5IGFueSBwYXJ0eSBhc3N1bWVzIGFjY2VwdGFuY2Ugb2YgdGhlIHRoZW4gYXBwbGljYWJs
+ZSBzdGFuZGFyZCB0ZXJtcyBhbmQgY29uZGl0aW9ucyBvZiB1c2UsIGNlcnRpZmljYXRpb24gcHJh
+Y3RpY2VzLCBhbmQgdGhlIFF1b1ZhZGlzIENlcnRpZmljYXRlIFBvbGljeS4wIgYIKwYBBQUHAgEW
+Fmh0dHA6Ly93d3cucXVvdmFkaXMuYm0wHQYDVR0OBBYEFItLbe3TKbkGGew5Oanwl4Rqy+/fMIGu
+BgNVHSMEgaYwgaOAFItLbe3TKbkGGew5Oanwl4Rqy+/foYGEpIGBMH8xCzAJBgNVBAYTAkJNMRkw
+FwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMSUwIwYDVQQLExxSb290IENlcnRpZmljYXRpb24gQXV0
+aG9yaXR5MS4wLAYDVQQDEyVRdW9WYWRpcyBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggQ6
+tlCLMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOCAQEAitQUtf70mpKnGdSkfnIYj9lo
+fFIk3WdvOXrEql494liwTXCYhGHoG+NpGA7O+0dQoE7/8CQfvbLO9Sf87C9TqnN7Az10buYWnuul
+LsS/VidQK2K6vkscPFVcQR0kvoIgR13VRH56FmjffU1RcHhXHTMe/QKZnAzNCgVPx7uOpHX6Sm2x
+gI4JVrmcGmD+XcHXetwReNDWXcG31a0ymQM6isxUJTkxgXsTIlG6Rmyhu576BGxJJnSP0nPrzDCi
+5upZIof4l/UO/erMkqQWxFIY6iHOsfHmhIHluqmGKPJDWl0Snawe2ajlCmqnf6CHKc/yiU3U7MXi
+5nrQNiOKSnQ2+Q==
+-----END CERTIFICATE-----
+
+QuoVadis Root CA 2
+==================
+-----BEGIN CERTIFICATE-----
+MIIFtzCCA5+gAwIBAgICBQkwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQk0xGTAXBgNVBAoT
+EFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMTElF1b1ZhZGlzIFJvb3QgQ0EgMjAeFw0wNjExMjQx
+ODI3MDBaFw0zMTExMjQxODIzMzNaMEUxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM
+aW1pdGVkMRswGQYDVQQDExJRdW9WYWRpcyBSb290IENBIDIwggIiMA0GCSqGSIb3DQEBAQUAA4IC
+DwAwggIKAoICAQCaGMpLlA0ALa8DKYrwD4HIrkwZhR0In6spRIXzL4GtMh6QRr+jhiYaHv5+HBg6
+XJxgFyo6dIMzMH1hVBHL7avg5tKifvVrbxi3Cgst/ek+7wrGsxDp3MJGF/hd/aTa/55JWpzmM+Yk
+lvc/ulsrHHo1wtZn/qtmUIttKGAr79dgw8eTvI02kfN/+NsRE8Scd3bBrrcCaoF6qUWD4gXmuVbB
+lDePSHFjIuwXZQeVikvfj8ZaCuWw419eaxGrDPmF60Tp+ARz8un+XJiM9XOva7R+zdRcAitMOeGy
+lZUtQofX1bOQQ7dsE/He3fbE+Ik/0XX1ksOR1YqI0JDs3G3eicJlcZaLDQP9nL9bFqyS2+r+eXyt
+66/3FsvbzSUr5R/7mp/iUcw6UwxI5g69ybR2BlLmEROFcmMDBOAENisgGQLodKcftslWZvB1Jdxn
+wQ5hYIizPtGo/KPaHbDRsSNU30R2be1B2MGyIrZTHN81Hdyhdyox5C315eXbyOD/5YDXC2Og/zOh
+D7osFRXql7PSorW+8oyWHhqPHWykYTe5hnMz15eWniN9gqRMgeKh0bpnX5UHoycR7hYQe7xFSkyy
+BNKr79X9DFHOUGoIMfmR2gyPZFwDwzqLID9ujWc9Otb+fVuIyV77zGHcizN300QyNQliBJIWENie
+J0f7OyHj+OsdWwIDAQABo4GwMIGtMA8GA1UdEwEB/wQFMAMBAf8wCwYDVR0PBAQDAgEGMB0GA1Ud
+DgQWBBQahGK8SEwzJQTU7tD2A8QZRtGUazBuBgNVHSMEZzBlgBQahGK8SEwzJQTU7tD2A8QZRtGU
+a6FJpEcwRTELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMT
+ElF1b1ZhZGlzIFJvb3QgQ0EgMoICBQkwDQYJKoZIhvcNAQEFBQADggIBAD4KFk2fBluornFdLwUv
+Z+YTRYPENvbzwCYMDbVHZF34tHLJRqUDGCdViXh9duqWNIAXINzng/iN/Ae42l9NLmeyhP3ZRPx3
+UIHmfLTJDQtyU/h2BwdBR5YM++CCJpNVjP4iH2BlfF/nJrP3MpCYUNQ3cVX2kiF495V5+vgtJodm
+VjB3pjd4M1IQWK4/YY7yarHvGH5KWWPKjaJW1acvvFYfzznB4vsKqBUsfU16Y8Zsl0Q80m/DShcK
++JDSV6IZUaUtl0HaB0+pUNqQjZRG4T7wlP0QADj1O+hA4bRuVhogzG9Yje0uRY/W6ZM/57Es3zrW
+IozchLsib9D45MY56QSIPMO661V6bYCZJPVsAfv4l7CUW+v90m/xd2gNNWQjrLhVoQPRTUIZ3Ph1
+WVaj+ahJefivDrkRoHy3au000LYmYjgahwz46P0u05B/B5EqHdZ+XIWDmbA4CD/pXvk1B+TJYm5X
+f6dQlfe6yJvmjqIBxdZmv3lh8zwc4bmCXF2gw+nYSL0ZohEUGW6yhhtoPkg3Goi3XZZenMfvJ2II
+4pEZXNLxId26F0KCl3GBUzGpn/Z9Yr9y4aOTHcyKJloJONDO1w2AFrR4pTqHTI2KpdVGl/IsELm8
+VCLAAVBpQ570su9t+Oza8eOx79+Rj1QqCyXBJhnEUhAFZdWCEOrCMc0u
+-----END CERTIFICATE-----
+
+QuoVadis Root CA 3
+==================
+-----BEGIN CERTIFICATE-----
+MIIGnTCCBIWgAwIBAgICBcYwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQk0xGTAXBgNVBAoT
+EFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMTElF1b1ZhZGlzIFJvb3QgQ0EgMzAeFw0wNjExMjQx
+OTExMjNaFw0zMTExMjQxOTA2NDRaMEUxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM
+aW1pdGVkMRswGQYDVQQDExJRdW9WYWRpcyBSb290IENBIDMwggIiMA0GCSqGSIb3DQEBAQUAA4IC
+DwAwggIKAoICAQDMV0IWVJzmmNPTTe7+7cefQzlKZbPoFog02w1ZkXTPkrgEQK0CSzGrvI2RaNgg
+DhoB4hp7Thdd4oq3P5kazethq8Jlph+3t723j/z9cI8LoGe+AaJZz3HmDyl2/7FWeUUrH556VOij
+KTVopAFPD6QuN+8bv+OPEKhyq1hX51SGyMnzW9os2l2ObjyjPtr7guXd8lyyBTNvijbO0BNO/79K
+DDRMpsMhvVAEVeuxu537RR5kFd5VAYwCdrXLoT9CabwvvWhDFlaJKjdhkf2mrk7AyxRllDdLkgbv
+BNDInIjbC3uBr7E9KsRlOni27tyAsdLTmZw67mtaa7ONt9XOnMK+pUsvFrGeaDsGb659n/je7Mwp
+p5ijJUMv7/FfJuGITfhebtfZFG4ZM2mnO4SJk8RTVROhUXhA+LjJou57ulJCg54U7QVSWllWp5f8
+nT8KKdjcT5EOE7zelaTfi5m+rJsziO+1ga8bxiJTyPbH7pcUsMV8eFLI8M5ud2CEpukqdiDtWAEX
+MJPpGovgc2PZapKUSU60rUqFxKMiMPwJ7Wgic6aIDFUhWMXhOp8q3crhkODZc6tsgLjoC2SToJyM
+Gf+z0gzskSaHirOi4XCPLArlzW1oUevaPwV/izLmE1xr/l9A4iLItLRkT9a6fUg+qGkM17uGcclz
+uD87nSVL2v9A6wIDAQABo4IBlTCCAZEwDwYDVR0TAQH/BAUwAwEB/zCB4QYDVR0gBIHZMIHWMIHT
+BgkrBgEEAb5YAAMwgcUwgZMGCCsGAQUFBwICMIGGGoGDQW55IHVzZSBvZiB0aGlzIENlcnRpZmlj
+YXRlIGNvbnN0aXR1dGVzIGFjY2VwdGFuY2Ugb2YgdGhlIFF1b1ZhZGlzIFJvb3QgQ0EgMyBDZXJ0
+aWZpY2F0ZSBQb2xpY3kgLyBDZXJ0aWZpY2F0aW9uIFByYWN0aWNlIFN0YXRlbWVudC4wLQYIKwYB
+BQUHAgEWIWh0dHA6Ly93d3cucXVvdmFkaXNnbG9iYWwuY29tL2NwczALBgNVHQ8EBAMCAQYwHQYD
+VR0OBBYEFPLAE+CCQz777i9nMpY1XNu4ywLQMG4GA1UdIwRnMGWAFPLAE+CCQz777i9nMpY1XNu4
+ywLQoUmkRzBFMQswCQYDVQQGEwJCTTEZMBcGA1UEChMQUXVvVmFkaXMgTGltaXRlZDEbMBkGA1UE
+AxMSUXVvVmFkaXMgUm9vdCBDQSAzggIFxjANBgkqhkiG9w0BAQUFAAOCAgEAT62gLEz6wPJv92ZV
+qyM07ucp2sNbtrCD2dDQ4iH782CnO11gUyeim/YIIirnv6By5ZwkajGxkHon24QRiSemd1o417+s
+hvzuXYO8BsbRd2sPbSQvS3pspweWyuOEn62Iix2rFo1bZhfZFvSLgNLd+LJ2w/w4E6oM3kJpK27z
+POuAJ9v1pkQNn1pVWQvVDVJIxa6f8i+AxeoyUDUSly7B4f/xI4hROJ/yZlZ25w9Rl6VSDE1JUZU2
+Pb+iSwwQHYaZTKrzchGT5Or2m9qoXadNt54CrnMAyNojA+j56hl0YgCUyyIgvpSnWbWCar6ZeXqp
+8kokUvd0/bpO5qgdAm6xDYBEwa7TIzdfu4V8K5Iu6H6li92Z4b8nby1dqnuH/grdS/yO9SbkbnBC
+bjPsMZ57k8HkyWkaPcBrTiJt7qtYTcbQQcEr6k8Sh17rRdhs9ZgC06DYVYoGmRmioHfRMJ6szHXu
+g/WwYjnPbFfiTNKRCw51KBuav/0aQ/HKd/s7j2G4aSgWQgRecCocIdiP4b0jWy10QJLZYxkNc91p
+vGJHvOB0K7Lrfb5BG7XARsWhIstfTsEokt4YutUqKLsRixeTmJlglFwjz1onl14LBQaTNx47aTbr
+qZ5hHY8y2o4M1nQ+ewkk2gF3R8Q7zTSMmfXK4SVhM7JZG+Ju1zdXtg2pEto=
+-----END CERTIFICATE-----
+
+Security Communication Root CA
+==============================
+-----BEGIN CERTIFICATE-----
+MIIDWjCCAkKgAwIBAgIBADANBgkqhkiG9w0BAQUFADBQMQswCQYDVQQGEwJKUDEYMBYGA1UEChMP
+U0VDT00gVHJ1c3QubmV0MScwJQYDVQQLEx5TZWN1cml0eSBDb21tdW5pY2F0aW9uIFJvb3RDQTEw
+HhcNMDMwOTMwMDQyMDQ5WhcNMjMwOTMwMDQyMDQ5WjBQMQswCQYDVQQGEwJKUDEYMBYGA1UEChMP
+U0VDT00gVHJ1c3QubmV0MScwJQYDVQQLEx5TZWN1cml0eSBDb21tdW5pY2F0aW9uIFJvb3RDQTEw
+ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCzs/5/022x7xZ8V6UMbXaKL0u/ZPtM7orw
+8yl89f/uKuDp6bpbZCKamm8sOiZpUQWZJtzVHGpxxpp9Hp3dfGzGjGdnSj74cbAZJ6kJDKaVv0uM
+DPpVmDvY6CKhS3E4eayXkmmziX7qIWgGmBSWh9JhNrxtJ1aeV+7AwFb9Ms+k2Y7CI9eNqPPYJayX
+5HA49LY6tJ07lyZDo6G8SVlyTCMwhwFY9k6+HGhWZq/NQV3Is00qVUarH9oe4kA92819uZKAnDfd
+DJZkndwi92SL32HeFZRSFaB9UslLqCHJxrHty8OVYNEP8Ktw+N/LTX7s1vqr2b1/VPKl6Xn62dZ2
+JChzAgMBAAGjPzA9MB0GA1UdDgQWBBSgc0mZaNyFW2XjmygvV5+9M7wHSDALBgNVHQ8EBAMCAQYw
+DwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAaECpqLvkT115swW1F7NgE+vGkl3g
+0dNq/vu+m22/xwVtWSDEHPC32oRYAmP6SBbvT6UL90qY8j+eG61Ha2POCEfrUj94nK9NrvjVT8+a
+mCoQQTlSxN3Zmw7vkwGusi7KaEIkQmywszo+zenaSMQVy+n5Bw+SUEmK3TGXX8npN6o7WWWXlDLJ
+s58+OmJYxUmtYg5xpTKqL8aJdkNAExNnPaJUJRDL8Try2frbSVa7pv6nQTXD4IhhyYjH3zYQIphZ
+6rBK+1YWc26sTfcioU+tHXotRSflMMFe8toTyyVCUZVHA4xsIcx0Qu1T/zOLjw9XARYvz6buyXAi
+FL39vmwLAw==
+-----END CERTIFICATE-----
+
+Sonera Class 2 Root CA
+======================
+-----BEGIN CERTIFICATE-----
+MIIDIDCCAgigAwIBAgIBHTANBgkqhkiG9w0BAQUFADA5MQswCQYDVQQGEwJGSTEPMA0GA1UEChMG
+U29uZXJhMRkwFwYDVQQDExBTb25lcmEgQ2xhc3MyIENBMB4XDTAxMDQwNjA3Mjk0MFoXDTIxMDQw
+NjA3Mjk0MFowOTELMAkGA1UEBhMCRkkxDzANBgNVBAoTBlNvbmVyYTEZMBcGA1UEAxMQU29uZXJh
+IENsYXNzMiBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJAXSjWdyvANlsdE+hY3
+/Ei9vX+ALTU74W+oZ6m/AxxNjG8yR9VBaKQTBME1DJqEQ/xcHf+Js+gXGM2RX/uJ4+q/Tl18GybT
+dXnt5oTjV+WtKcT0OijnpXuENmmz/V52vaMtmdOQTiMofRhj8VQ7Jp12W5dCsv+u8E7s3TmVToMG
+f+dJQMjFAbJUWmYdPfz56TwKnoG4cPABi+QjVHzIrviQHgCWctRUz2EjvOr7nQKV0ba5cTppCD8P
+tOFCx4j1P5iop7oc4HFx71hXgVB6XGt0Rg6DA5jDjqhu8nYybieDwnPz3BjotJPqdURrBGAgcVeH
+nfO+oJAjPYok4doh28MCAwEAAaMzMDEwDwYDVR0TAQH/BAUwAwEB/zARBgNVHQ4ECgQISqCqWITT
+XjwwCwYDVR0PBAQDAgEGMA0GCSqGSIb3DQEBBQUAA4IBAQBazof5FnIVV0sd2ZvnoiYw7JNn39Yt
+0jSv9zilzqsWuasvfDXLrNAPtEwr/IDva4yRXzZ299uzGxnq9LIR/WFxRL8oszodv7ND6J+/3DEI
+cbCdjdY0RzKQxmUk96BKfARzjzlvF4xytb1LyHr4e4PDKE6cCepnP7JnBBvDFNr450kkkdAdavph
+Oe9r5yF1BgfYErQhIHBCcYHaPJo2vqZbDWpsmh+Re/n570K6Tk6ezAyNlNzZRZxe7EJQY670XcSx
+EtzKO6gunRRaBXW37Ndj4ro1tgQIkejanZz2ZrUYrAqmVCY0M9IbwdR/GjqOC6oybtv8TyWf2TLH
+llpwrN9M
+-----END CERTIFICATE-----
+
+UTN USERFirst Hardware Root CA
+==============================
+-----BEGIN CERTIFICATE-----
+MIIEdDCCA1ygAwIBAgIQRL4Mi1AAJLQR0zYq/mUK/TANBgkqhkiG9w0BAQUFADCBlzELMAkGA1UE
+BhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2UgQ2l0eTEeMBwGA1UEChMVVGhl
+IFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExhodHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xHzAd
+BgNVBAMTFlVUTi1VU0VSRmlyc3QtSGFyZHdhcmUwHhcNOTkwNzA5MTgxMDQyWhcNMTkwNzA5MTgx
+OTIyWjCBlzELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2UgQ2l0
+eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExhodHRwOi8vd3d3LnVz
+ZXJ0cnVzdC5jb20xHzAdBgNVBAMTFlVUTi1VU0VSRmlyc3QtSGFyZHdhcmUwggEiMA0GCSqGSIb3
+DQEBAQUAA4IBDwAwggEKAoIBAQCx98M4P7Sof885glFn0G2f0v9Y8+efK+wNiVSZuTiZFvfgIXlI
+wrthdBKWHTxqctU8EGc6Oe0rE81m65UJM6Rsl7HoxuzBdXmcRl6Nq9Bq/bkqVRcQVLMZ8Jr28bFd
+tqdt++BxF2uiiPsA3/4aMXcMmgF6sTLjKwEHOG7DpV4jvEWbe1DByTCP2+UretNb+zNAHqDVmBe8
+i4fDidNdoI6yqqr2jmmIBsX6iSHzCJ1pLgkzmykNRg+MzEk0sGlRvfkGzWitZky8PqxhvQqIDsjf
+Pe58BEydCl5rkdbux+0ojatNh4lz0G6k0B4WixThdkQDf2Os5M1JnMWS9KsyoUhbAgMBAAGjgbkw
+gbYwCwYDVR0PBAQDAgHGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFKFyXyYbKJhDlV0HN9WF
+lp1L0sNFMEQGA1UdHwQ9MDswOaA3oDWGM2h0dHA6Ly9jcmwudXNlcnRydXN0LmNvbS9VVE4tVVNF
+UkZpcnN0LUhhcmR3YXJlLmNybDAxBgNVHSUEKjAoBggrBgEFBQcDAQYIKwYBBQUHAwUGCCsGAQUF
+BwMGBggrBgEFBQcDBzANBgkqhkiG9w0BAQUFAAOCAQEARxkP3nTGmZev/K0oXnWO6y1n7k57K9cM
+//bey1WiCuFMVGWTYGufEpytXoMs61quwOQt9ABjHbjAbPLPSbtNk28GpgoiskliCE7/yMgUsogW
+XecB5BKV5UU0s4tpvc+0hY91UZ59Ojg6FEgSxvunOxqNDYJAB+gECJChicsZUN/KHAG8HQQZexB2
+lzvukJDKxA4fFm517zP4029bHpbj4HR3dHuKom4t3XbWOTCC8KucUvIqx69JXn7HaOWCgchqJ/kn
+iCrVWFCVH/A7HFe7fRQ5YiuayZSSKqMiDP+JJn1fIytH1xUdqWqeUQ0qUZ6B+dQ7XnASfxAynB67
+nfhmqA==
+-----END CERTIFICATE-----
+
+Camerfirma Chambers of Commerce Root
+====================================
+-----BEGIN CERTIFICATE-----
+MIIEvTCCA6WgAwIBAgIBADANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJFVTEnMCUGA1UEChMe
+QUMgQ2FtZXJmaXJtYSBTQSBDSUYgQTgyNzQzMjg3MSMwIQYDVQQLExpodHRwOi8vd3d3LmNoYW1i
+ZXJzaWduLm9yZzEiMCAGA1UEAxMZQ2hhbWJlcnMgb2YgQ29tbWVyY2UgUm9vdDAeFw0wMzA5MzAx
+NjEzNDNaFw0zNzA5MzAxNjEzNDRaMH8xCzAJBgNVBAYTAkVVMScwJQYDVQQKEx5BQyBDYW1lcmZp
+cm1hIFNBIENJRiBBODI3NDMyODcxIzAhBgNVBAsTGmh0dHA6Ly93d3cuY2hhbWJlcnNpZ24ub3Jn
+MSIwIAYDVQQDExlDaGFtYmVycyBvZiBDb21tZXJjZSBSb290MIIBIDANBgkqhkiG9w0BAQEFAAOC
+AQ0AMIIBCAKCAQEAtzZV5aVdGDDg2olUkfzIx1L4L1DZ77F1c2VHfRtbunXF/KGIJPov7coISjlU
+xFF6tdpg6jg8gbLL8bvZkSM/SAFwdakFKq0fcfPJVD0dBmpAPrMMhe5cG3nCYsS4No41XQEMIwRH
+NaqbYE6gZj3LJgqcQKH0XZi/caulAGgq7YN6D6IUtdQis4CwPAxaUWktWBiP7Zme8a7ileb2R6jW
+DA+wWFjbw2Y3npuRVDM30pQcakjJyfKl2qUMI/cjDpwyVV5xnIQFUZot/eZOKjRa3spAN2cMVCFV
+d9oKDMyXroDclDZK9D7ONhMeU+SsTjoF7Nuucpw4i9A5O4kKPnf+dQIBA6OCAUQwggFAMBIGA1Ud
+EwEB/wQIMAYBAf8CAQwwPAYDVR0fBDUwMzAxoC+gLYYraHR0cDovL2NybC5jaGFtYmVyc2lnbi5v
+cmcvY2hhbWJlcnNyb290LmNybDAdBgNVHQ4EFgQU45T1sU3p26EpW1eLTXYGduHRooowDgYDVR0P
+AQH/BAQDAgEGMBEGCWCGSAGG+EIBAQQEAwIABzAnBgNVHREEIDAegRxjaGFtYmVyc3Jvb3RAY2hh
+bWJlcnNpZ24ub3JnMCcGA1UdEgQgMB6BHGNoYW1iZXJzcm9vdEBjaGFtYmVyc2lnbi5vcmcwWAYD
+VR0gBFEwTzBNBgsrBgEEAYGHLgoDATA+MDwGCCsGAQUFBwIBFjBodHRwOi8vY3BzLmNoYW1iZXJz
+aWduLm9yZy9jcHMvY2hhbWJlcnNyb290Lmh0bWwwDQYJKoZIhvcNAQEFBQADggEBAAxBl8IahsAi
+fJ/7kPMa0QOx7xP5IV8EnNrJpY0nbJaHkb5BkAFyk+cefV/2icZdp0AJPaxJRUXcLo0waLIJuvvD
+L8y6C98/d3tGfToSJI6WjzwFCm/SlCgdbQzALogi1djPHRPH8EjX1wWnz8dHnjs8NMiAT9QUu/wN
+UPf6s+xCX6ndbcj0dc97wXImsQEcXCz9ek60AcUFV7nnPKoF2YjpB0ZBzu9Bga5Y34OirsrXdx/n
+ADydb47kMgkdTXg0eDQ8lJsm7U9xxhl6vSAiSFr+S30Dt+dYvsYyTnQeaN2oaFuzPu5ifdmA6Ap1
+erfutGWaIZDgqtCYvDi1czyL+Nw=
+-----END CERTIFICATE-----
+
+Camerfirma Global Chambersign Root
+==================================
+-----BEGIN CERTIFICATE-----
+MIIExTCCA62gAwIBAgIBADANBgkqhkiG9w0BAQUFADB9MQswCQYDVQQGEwJFVTEnMCUGA1UEChMe
+QUMgQ2FtZXJmaXJtYSBTQSBDSUYgQTgyNzQzMjg3MSMwIQYDVQQLExpodHRwOi8vd3d3LmNoYW1i
+ZXJzaWduLm9yZzEgMB4GA1UEAxMXR2xvYmFsIENoYW1iZXJzaWduIFJvb3QwHhcNMDMwOTMwMTYx
+NDE4WhcNMzcwOTMwMTYxNDE4WjB9MQswCQYDVQQGEwJFVTEnMCUGA1UEChMeQUMgQ2FtZXJmaXJt
+YSBTQSBDSUYgQTgyNzQzMjg3MSMwIQYDVQQLExpodHRwOi8vd3d3LmNoYW1iZXJzaWduLm9yZzEg
+MB4GA1UEAxMXR2xvYmFsIENoYW1iZXJzaWduIFJvb3QwggEgMA0GCSqGSIb3DQEBAQUAA4IBDQAw
+ggEIAoIBAQCicKLQn0KuWxfH2H3PFIP8T8mhtxOviteePgQKkotgVvq0Mi+ITaFgCPS3CU6gSS9J
+1tPfnZdan5QEcOw/Wdm3zGaLmFIoCQLfxS+EjXqXd7/sQJ0lcqu1PzKY+7e3/HKE5TWH+VX6ox8O
+by4o3Wmg2UIQxvi1RMLQQ3/bvOSiPGpVeAp3qdjqGTK3L/5cPxvusZjsyq16aUXjlg9V9ubtdepl
+6DJWk0aJqCWKZQbua795B9Dxt6/tLE2Su8CoX6dnfQTyFQhwrJLWfQTSM/tMtgsL+xrJxI0DqX5c
+8lCrEqWhz0hQpe/SyBoT+rB/sYIcd2oPX9wLlY/vQ37mRQklAgEDo4IBUDCCAUwwEgYDVR0TAQH/
+BAgwBgEB/wIBDDA/BgNVHR8EODA2MDSgMqAwhi5odHRwOi8vY3JsLmNoYW1iZXJzaWduLm9yZy9j
+aGFtYmVyc2lnbnJvb3QuY3JsMB0GA1UdDgQWBBRDnDafsJ4wTcbOX60Qq+UDpfqpFDAOBgNVHQ8B
+Af8EBAMCAQYwEQYJYIZIAYb4QgEBBAQDAgAHMCoGA1UdEQQjMCGBH2NoYW1iZXJzaWducm9vdEBj
+aGFtYmVyc2lnbi5vcmcwKgYDVR0SBCMwIYEfY2hhbWJlcnNpZ25yb290QGNoYW1iZXJzaWduLm9y
+ZzBbBgNVHSAEVDBSMFAGCysGAQQBgYcuCgEBMEEwPwYIKwYBBQUHAgEWM2h0dHA6Ly9jcHMuY2hh
+bWJlcnNpZ24ub3JnL2Nwcy9jaGFtYmVyc2lnbnJvb3QuaHRtbDANBgkqhkiG9w0BAQUFAAOCAQEA
+PDtwkfkEVCeR4e3t/mh/YV3lQWVPMvEYBZRqHN4fcNs+ezICNLUMbKGKfKX0j//U2K0X1S0E0T9Y
+gOKBWYi+wONGkyT+kL0mojAt6JcmVzWJdJYY9hXiryQZVgICsroPFOrGimbBhkVVi76SvpykBMdJ
+PJ7oKXqJ1/6v/2j1pReQvayZzKWGVwlnRtvWFsJG8eSpUPWP0ZIV018+xgBJOm5YstHRJw0lyDL4
+IBHNfTIzSJRUTN3cecQwn+uOuFW114hcxWokPbLTBQNRxgfvzBRydD1ucs4YKIxKoHflCStFREes
+t2d/AYoFWpO+ocH/+OcOZ6RHSXZddZAa9SaP8A==
+-----END CERTIFICATE-----
+
+XRamp Global CA Root
+====================
+-----BEGIN CERTIFICATE-----
+MIIEMDCCAxigAwIBAgIQUJRs7Bjq1ZxN1ZfvdY+grTANBgkqhkiG9w0BAQUFADCBgjELMAkGA1UE
+BhMCVVMxHjAcBgNVBAsTFXd3dy54cmFtcHNlY3VyaXR5LmNvbTEkMCIGA1UEChMbWFJhbXAgU2Vj
+dXJpdHkgU2VydmljZXMgSW5jMS0wKwYDVQQDEyRYUmFtcCBHbG9iYWwgQ2VydGlmaWNhdGlvbiBB
+dXRob3JpdHkwHhcNMDQxMTAxMTcxNDA0WhcNMzUwMTAxMDUzNzE5WjCBgjELMAkGA1UEBhMCVVMx
+HjAcBgNVBAsTFXd3dy54cmFtcHNlY3VyaXR5LmNvbTEkMCIGA1UEChMbWFJhbXAgU2VjdXJpdHkg
+U2VydmljZXMgSW5jMS0wKwYDVQQDEyRYUmFtcCBHbG9iYWwgQ2VydGlmaWNhdGlvbiBBdXRob3Jp
+dHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCYJB69FbS638eMpSe2OAtp87ZOqCwu
+IR1cRN8hXX4jdP5efrRKt6atH67gBhbim1vZZ3RrXYCPKZ2GG9mcDZhtdhAoWORlsH9KmHmf4MMx
+foArtYzAQDsRhtDLooY2YKTVMIJt2W7QDxIEM5dfT2Fa8OT5kavnHTu86M/0ay00fOJIYRyO82FE
+zG+gSqmUsE3a56k0enI4qEHMPJQRfevIpoy3hsvKMzvZPTeL+3o+hiznc9cKV6xkmxnr9A8ECIqs
+AxcZZPRaJSKNNCyy9mgdEm3Tih4U2sSPpuIjhdV6Db1q4Ons7Be7QhtnqiXtRYMh/MHJfNViPvry
+xS3T/dRlAgMBAAGjgZ8wgZwwEwYJKwYBBAGCNxQCBAYeBABDAEEwCwYDVR0PBAQDAgGGMA8GA1Ud
+EwEB/wQFMAMBAf8wHQYDVR0OBBYEFMZPoj0GY4QJnM5i5ASsjVy16bYbMDYGA1UdHwQvMC0wK6Ap
+oCeGJWh0dHA6Ly9jcmwueHJhbXBzZWN1cml0eS5jb20vWEdDQS5jcmwwEAYJKwYBBAGCNxUBBAMC
+AQEwDQYJKoZIhvcNAQEFBQADggEBAJEVOQMBG2f7Shz5CmBbodpNl2L5JFMn14JkTpAuw0kbK5rc
+/Kh4ZzXxHfARvbdI4xD2Dd8/0sm2qlWkSLoC295ZLhVbO50WfUfXN+pfTXYSNrsf16GBBEYgoyxt
+qZ4Bfj8pzgCT3/3JknOJiWSe5yvkHJEs0rnOfc5vMZnT5r7SHpDwCRR5XCOrTdLaIR9NmXmd4c8n
+nxCbHIgNsIpkQTG4DmyQJKSbXHGPurt+HBvbaoAPIbzp26a3QPSyi6mx5O+aGtA9aZnuqCij4Tyz
+8LIRnM98QObd50N9otg6tamN8jSZxNQQ4Qb9CYQQO+7ETPTsJ3xCwnR8gooJybQDJbw=
+-----END CERTIFICATE-----
+
+Go Daddy Class 2 CA
+===================
+-----BEGIN CERTIFICATE-----
+MIIEADCCAuigAwIBAgIBADANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEhMB8GA1UEChMY
+VGhlIEdvIERhZGR5IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBEYWRkeSBDbGFzcyAyIENlcnRp
+ZmljYXRpb24gQXV0aG9yaXR5MB4XDTA0MDYyOTE3MDYyMFoXDTM0MDYyOTE3MDYyMFowYzELMAkG
+A1UEBhMCVVMxITAfBgNVBAoTGFRoZSBHbyBEYWRkeSBHcm91cCwgSW5jLjExMC8GA1UECxMoR28g
+RGFkZHkgQ2xhc3MgMiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASAwDQYJKoZIhvcNAQEBBQAD
+ggENADCCAQgCggEBAN6d1+pXGEmhW+vXX0iG6r7d/+TvZxz0ZWizV3GgXne77ZtJ6XCAPVYYYwhv
+2vLM0D9/AlQiVBDYsoHUwHU9S3/Hd8M+eKsaA7Ugay9qK7HFiH7Eux6wwdhFJ2+qN1j3hybX2C32
+qRe3H3I2TqYXP2WYktsqbl2i/ojgC95/5Y0V4evLOtXiEqITLdiOr18SPaAIBQi2XKVlOARFmR6j
+YGB0xUGlcmIbYsUfb18aQr4CUWWoriMYavx4A6lNf4DD+qta/KFApMoZFv6yyO9ecw3ud72a9nmY
+vLEHZ6IVDd2gWMZEewo+YihfukEHU1jPEX44dMX4/7VpkI+EdOqXG68CAQOjgcAwgb0wHQYDVR0O
+BBYEFNLEsNKR1EwRcbNhyz2h/t2oatTjMIGNBgNVHSMEgYUwgYKAFNLEsNKR1EwRcbNhyz2h/t2o
+atTjoWekZTBjMQswCQYDVQQGEwJVUzEhMB8GA1UEChMYVGhlIEdvIERhZGR5IEdyb3VwLCBJbmMu
+MTEwLwYDVQQLEyhHbyBEYWRkeSBDbGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggEAMAwG
+A1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBADJL87LKPpH8EsahB4yOd6AzBhRckB4Y9wim
+PQoZ+YeAEW5p5JYXMP80kWNyOO7MHAGjHZQopDH2esRU1/blMVgDoszOYtuURXO1v0XJJLXVggKt
+I3lpjbi2Tc7PTMozI+gciKqdi0FuFskg5YmezTvacPd+mSYgFFQlq25zheabIZ0KbIIOqPjCDPoQ
+HmyW74cNxA9hi63ugyuV+I6ShHI56yDqg+2DzZduCLzrTia2cyvk0/ZM/iZx4mERdEr/VxqHD3VI
+Ls9RaRegAhJhldXRQLIQTO7ErBBDpqWeCtWVYpoNz4iCxTIM5CufReYNnyicsbkqWletNw+vHX/b
+vZ8=
+-----END CERTIFICATE-----
+
+Starfield Class 2 CA
+====================
+-----BEGIN CERTIFICATE-----
+MIIEDzCCAvegAwIBAgIBADANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJVUzElMCMGA1UEChMc
+U3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMpU3RhcmZpZWxkIENsYXNzIDIg
+Q2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDQwNjI5MTczOTE2WhcNMzQwNjI5MTczOTE2WjBo
+MQswCQYDVQQGEwJVUzElMCMGA1UEChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAG
+A1UECxMpU3RhcmZpZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEgMA0GCSqG
+SIb3DQEBAQUAA4IBDQAwggEIAoIBAQC3Msj+6XGmBIWtDBFk385N78gDGIc/oav7PKaf8MOh2tTY
+bitTkPskpD6E8J7oX+zlJ0T1KKY/e97gKvDIr1MvnsoFAZMej2YcOadN+lq2cwQlZut3f+dZxkqZ
+JRRU6ybH838Z1TBwj6+wRir/resp7defqgSHo9T5iaU0X9tDkYI22WY8sbi5gv2cOj4QyDvvBmVm
+epsZGD3/cVE8MC5fvj13c7JdBmzDI1aaK4UmkhynArPkPw2vCHmCuDY96pzTNbO8acr1zJ3o/WSN
+F4Azbl5KXZnJHoe0nRrA1W4TNSNe35tfPe/W93bC6j67eA0cQmdrBNj41tpvi/JEoAGrAgEDo4HF
+MIHCMB0GA1UdDgQWBBS/X7fRzt0fhvRbVazc1xDCDqmI5zCBkgYDVR0jBIGKMIGHgBS/X7fRzt0f
+hvRbVazc1xDCDqmI56FspGowaDELMAkGA1UEBhMCVVMxJTAjBgNVBAoTHFN0YXJmaWVsZCBUZWNo
+bm9sb2dpZXMsIEluYy4xMjAwBgNVBAsTKVN0YXJmaWVsZCBDbGFzcyAyIENlcnRpZmljYXRpb24g
+QXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAAWdP4id0ckaVaGs
+afPzWdqbAYcaT1epoXkJKtv3L7IezMdeatiDh6GX70k1PncGQVhiv45YuApnP+yz3SFmH8lU+nLM
+PUxA2IGvd56Deruix/U0F47ZEUD0/CwqTRV/p2JdLiXTAAsgGh1o+Re49L2L7ShZ3U0WixeDyLJl
+xy16paq8U4Zt3VekyvggQQto8PT7dL5WXXp59fkdheMtlb71cZBDzI0fmgAKhynpVSJYACPq4xJD
+KVtHCN2MQWplBqjlIapBtJUhlbl90TSrE9atvNziPTnNvT51cKEYWQPJIrSPnNVeKtelttQKbfi3
+QBFGmh95DmK/D5fs4C8fF5Q=
+-----END CERTIFICATE-----
+
+StartCom Certification Authority
+================================
+-----BEGIN CERTIFICATE-----
+MIIHyTCCBbGgAwIBAgIBATANBgkqhkiG9w0BAQUFADB9MQswCQYDVQQGEwJJTDEWMBQGA1UEChMN
+U3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmlu
+ZzEpMCcGA1UEAxMgU3RhcnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDYwOTE3MTk0
+NjM2WhcNMzYwOTE3MTk0NjM2WjB9MQswCQYDVQQGEwJJTDEWMBQGA1UEChMNU3RhcnRDb20gTHRk
+LjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMg
+U3RhcnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw
+ggIKAoICAQDBiNsJvGxGfHiflXu1M5DycmLWwTYgIiRezul38kMKogZkpMyONvg45iPwbm2xPN1y
+o4UcodM9tDMr0y+v/uqwQVlntsQGfQqedIXWeUyAN3rfOQVSWff0G0ZDpNKFhdLDcfN1YjS6LIp/
+Ho/u7TTQEceWzVI9ujPW3U3eCztKS5/CJi/6tRYccjV3yjxd5srhJosaNnZcAdt0FCX+7bWgiA/d
+eMotHweXMAEtcnn6RtYTKqi5pquDSR3l8u/d5AGOGAqPY1MWhWKpDhk6zLVmpsJrdAfkK+F2PrRt
+2PZE4XNiHzvEvqBTViVsUQn3qqvKv3b9bZvzndu/PWa8DFaqr5hIlTpL36dYUNk4dalb6kMMAv+Z
+6+hsTXBbKWWc3apdzK8BMewM69KN6Oqce+Zu9ydmDBpI125C4z/eIT574Q1w+2OqqGwaVLRcJXrJ
+osmLFqa7LH4XXgVNWG4SHQHuEhANxjJ/GP/89PrNbpHoNkm+Gkhpi8KWTRoSsmkXwQqQ1vp5Iki/
+untp+HDH+no32NgN0nZPV/+Qt+OR0t3vwmC3Zzrd/qqc8NSLf3Iizsafl7b4r4qgEKjZ+xjGtrVc
+UjyJthkqcwEKDwOzEmDyei+B26Nu/yYwl/WL3YlXtq09s68rxbd2AvCl1iuahhQqcvbjM4xdCUsT
+37uMdBNSSwIDAQABo4ICUjCCAk4wDAYDVR0TBAUwAwEB/zALBgNVHQ8EBAMCAa4wHQYDVR0OBBYE
+FE4L7xqkQFulF2mHMMo0aEPQQa7yMGQGA1UdHwRdMFswLKAqoCiGJmh0dHA6Ly9jZXJ0LnN0YXJ0
+Y29tLm9yZy9zZnNjYS1jcmwuY3JsMCugKaAnhiVodHRwOi8vY3JsLnN0YXJ0Y29tLm9yZy9zZnNj
+YS1jcmwuY3JsMIIBXQYDVR0gBIIBVDCCAVAwggFMBgsrBgEEAYG1NwEBATCCATswLwYIKwYBBQUH
+AgEWI2h0dHA6Ly9jZXJ0LnN0YXJ0Y29tLm9yZy9wb2xpY3kucGRmMDUGCCsGAQUFBwIBFilodHRw
+Oi8vY2VydC5zdGFydGNvbS5vcmcvaW50ZXJtZWRpYXRlLnBkZjCB0AYIKwYBBQUHAgIwgcMwJxYg
+U3RhcnQgQ29tbWVyY2lhbCAoU3RhcnRDb20pIEx0ZC4wAwIBARqBl0xpbWl0ZWQgTGlhYmlsaXR5
+LCByZWFkIHRoZSBzZWN0aW9uICpMZWdhbCBMaW1pdGF0aW9ucyogb2YgdGhlIFN0YXJ0Q29tIENl
+cnRpZmljYXRpb24gQXV0aG9yaXR5IFBvbGljeSBhdmFpbGFibGUgYXQgaHR0cDovL2NlcnQuc3Rh
+cnRjb20ub3JnL3BvbGljeS5wZGYwEQYJYIZIAYb4QgEBBAQDAgAHMDgGCWCGSAGG+EIBDQQrFilT
+dGFydENvbSBGcmVlIFNTTCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTANBgkqhkiG9w0BAQUFAAOC
+AgEAFmyZ9GYMNPXQhV59CuzaEE44HF7fpiUFS5Eyweg78T3dRAlbB0mKKctmArexmvclmAk8jhvh
+3TaHK0u7aNM5Zj2gJsfyOZEdUauCe37Vzlrk4gNXcGmXCPleWKYK34wGmkUWFjgKXlf2Ysd6AgXm
+vB618p70qSmD+LIU424oh0TDkBreOKk8rENNZEXO3SipXPJzewT4F+irsfMuXGRuczE6Eri8sxHk
+fY+BUZo7jYn0TZNmezwD7dOaHZrzZVD1oNB1ny+v8OqCQ5j4aZyJecRDjkZy42Q2Eq/3JR44iZB3
+fsNrarnDy0RLrHiQi+fHLB5LEUTINFInzQpdn4XBidUaePKVEFMy3YCEZnXZtWgo+2EuvoSoOMCZ
+EoalHmdkrQYuL6lwhceWD3yJZfWOQ1QOq92lgDmUYMA0yZZwLKMS9R9Ie70cfmu3nZD0Ijuu+Pwq
+yvqCUqDvr0tVk+vBtfAii6w0TiYiBKGHLHVKt+V9E9e4DGTANtLJL4YSjCMJwRuCO3NJo2pXh5Tl
+1njFmUNj403gdy3hZZlyaQQaRwnmDwFWJPsfvw55qVguucQJAX6Vum0ABj6y6koQOdjQK/W/7HW/
+lwLFCRsI3FU34oH7N4RDYiDK51ZLZer+bMEkkyShNOsF/5oirpt9P/FlUQqmMGqz9IgcgA38coro
+g14=
+-----END CERTIFICATE-----
+
+Taiwan GRCA
+===========
+-----BEGIN CERTIFICATE-----
+MIIFcjCCA1qgAwIBAgIQH51ZWtcvwgZEpYAIaeNe9jANBgkqhkiG9w0BAQUFADA/MQswCQYDVQQG
+EwJUVzEwMC4GA1UECgwnR292ZXJubWVudCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4X
+DTAyMTIwNTEzMjMzM1oXDTMyMTIwNTEzMjMzM1owPzELMAkGA1UEBhMCVFcxMDAuBgNVBAoMJ0dv
+dmVybm1lbnQgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCAiIwDQYJKoZIhvcNAQEBBQAD
+ggIPADCCAgoCggIBAJoluOzMonWoe/fOW1mKydGGEghU7Jzy50b2iPN86aXfTEc2pBsBHH8eV4qN
+w8XRIePaJD9IK/ufLqGU5ywck9G/GwGHU5nOp/UKIXZ3/6m3xnOUT0b3EEk3+qhZSV1qgQdW8or5
+BtD3cCJNtLdBuTK4sfCxw5w/cP1T3YGq2GN49thTbqGsaoQkclSGxtKyyhwOeYHWtXBiCAEuTk8O
+1RGvqa/lmr/czIdtJuTJV6L7lvnM4T9TjGxMfptTCAtsF/tnyMKtsc2AtJfcdgEWFelq16TheEfO
+htX7MfP6Mb40qij7cEwdScevLJ1tZqa2jWR+tSBqnTuBto9AAGdLiYa4zGX+FVPpBMHWXx1E1wov
+J5pGfaENda1UhhXcSTvxls4Pm6Dso3pdvtUqdULle96ltqqvKKyskKw4t9VoNSZ63Pc78/1Fm9G7
+Q3hub/FCVGqY8A2tl+lSXunVanLeavcbYBT0peS2cWeqH+riTcFCQP5nRhc4L0c/cZyu5SHKYS1t
+B6iEfC3uUSXxY5Ce/eFXiGvviiNtsea9P63RPZYLhY3Naye7twWb7LuRqQoHEgKXTiCQ8P8NHuJB
+O9NAOueNXdpm5AKwB1KYXA6OM5zCppX7VRluTI6uSw+9wThNXo+EHWbNxWCWtFJaBYmOlXqYwZE8
+lSOyDvR5tMl8wUohAgMBAAGjajBoMB0GA1UdDgQWBBTMzO/MKWCkO7GStjz6MmKPrCUVOzAMBgNV
+HRMEBTADAQH/MDkGBGcqBwAEMTAvMC0CAQAwCQYFKw4DAhoFADAHBgVnKgMAAAQUA5vwIhP/lSg2
+09yewDL7MTqKUWUwDQYJKoZIhvcNAQEFBQADggIBAECASvomyc5eMN1PhnR2WPWus4MzeKR6dBcZ
+TulStbngCnRiqmjKeKBMmo4sIy7VahIkv9Ro04rQ2JyftB8M3jh+Vzj8jeJPXgyfqzvS/3WXy6Tj
+Zwj/5cAWtUgBfen5Cv8b5Wppv3ghqMKnI6mGq3ZW6A4M9hPdKmaKZEk9GhiHkASfQlK3T8v+R0F2
+Ne//AHY2RTKbxkaFXeIksB7jSJaYV0eUVXoPQbFEJPPB/hprv4j9wabak2BegUqZIJxIZhm1AHlU
+D7gsL0u8qV1bYH+Mh6XgUmMqvtg7hUAV/h62ZT/FS9p+tXo1KaMuephgIqP0fSdOLeq0dDzpD6Qz
+DxARvBMB1uUO07+1EqLhRSPAzAhuYbeJq4PjJB7mXQfnHyA+z2fI56wwbSdLaG5LKlwCCDTb+Hbk
+Z6MmnD+iMsJKxYEYMRBWqoTvLQr/uB930r+lWKBi5NdLkXWNiYCYfm3LU05er/ayl4WXudpVBrkk
+7tfGOB5jGxI7leFYrPLfhNVfmS8NVVvmONsuP3LpSIXLuykTjx44VbnzssQwmSNOXfJIoRIM3BKQ
+CZBUkQM8R+XVyWXgt0t97EfTsws+rZ7QdAAO671RrcDeLMDDav7v3Aun+kbfYNucpllQdSNpc5Oy
++fwC00fmcc4QAu4njIT/rEUNE1yDMuAlpYYsfPQS
+-----END CERTIFICATE-----
+
+Swisscom Root CA 1
+==================
+-----BEGIN CERTIFICATE-----
+MIIF2TCCA8GgAwIBAgIQXAuFXAvnWUHfV8w/f52oNjANBgkqhkiG9w0BAQUFADBkMQswCQYDVQQG
+EwJjaDERMA8GA1UEChMIU3dpc3Njb20xJTAjBgNVBAsTHERpZ2l0YWwgQ2VydGlmaWNhdGUgU2Vy
+dmljZXMxGzAZBgNVBAMTElN3aXNzY29tIFJvb3QgQ0EgMTAeFw0wNTA4MTgxMjA2MjBaFw0yNTA4
+MTgyMjA2MjBaMGQxCzAJBgNVBAYTAmNoMREwDwYDVQQKEwhTd2lzc2NvbTElMCMGA1UECxMcRGln
+aXRhbCBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczEbMBkGA1UEAxMSU3dpc3Njb20gUm9vdCBDQSAxMIIC
+IjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA0LmwqAzZuz8h+BvVM5OAFmUgdbI9m2BtRsiM
+MW8Xw/qabFbtPMWRV8PNq5ZJkCoZSx6jbVfd8StiKHVFXqrWW/oLJdihFvkcxC7mlSpnzNApbjyF
+NDhhSbEAn9Y6cV9Nbc5fuankiX9qUvrKm/LcqfmdmUc/TilftKaNXXsLmREDA/7n29uj/x2lzZAe
+AR81sH8A25Bvxn570e56eqeqDFdvpG3FEzuwpdntMhy0XmeLVNxzh+XTF3xmUHJd1BpYwdnP2IkC
+b6dJtDZd0KTeByy2dbcokdaXvij1mB7qWybJvbCXc9qukSbraMH5ORXWZ0sKbU/Lz7DkQnGMU3nn
+7uHbHaBuHYwadzVcFh4rUx80i9Fs/PJnB3r1re3WmquhsUvhzDdf/X/NTa64H5xD+SpYVUNFvJbN
+cA78yeNmuk6NO4HLFWR7uZToXTNShXEuT46iBhFRyePLoW4xCGQMwtI89Tbo19AOeCMgkckkKmUp
+WyL3Ic6DXqTz3kvTaI9GdVyDCW4pa8RwjPWd1yAv/0bSKzjCL3UcPX7ape8eYIVpQtPM+GP+HkM5
+haa2Y0EQs3MevNP6yn0WR+Kn1dCjigoIlmJWbjTb2QK5MHXjBNLnj8KwEUAKrNVxAmKLMb7dxiNY
+MUJDLXT5xp6mig/p/r+D5kNXJLrvRjSq1xIBOO0CAwEAAaOBhjCBgzAOBgNVHQ8BAf8EBAMCAYYw
+HQYDVR0hBBYwFDASBgdghXQBUwABBgdghXQBUwABMBIGA1UdEwEB/wQIMAYBAf8CAQcwHwYDVR0j
+BBgwFoAUAyUv3m+CATpcLNwroWm1Z9SM0/0wHQYDVR0OBBYEFAMlL95vggE6XCzcK6FptWfUjNP9
+MA0GCSqGSIb3DQEBBQUAA4ICAQA1EMvspgQNDQ/NwNurqPKIlwzfky9NfEBWMXrrpA9gzXrzvsMn
+jgM+pN0S734edAY8PzHyHHuRMSG08NBsl9Tpl7IkVh5WwzW9iAUPWxAaZOHHgjD5Mq2eUCzneAXQ
+MbFamIp1TpBcahQq4FJHgmDmHtqBsfsUC1rxn9KVuj7QG9YVHaO+htXbD8BJZLsuUBlL0iT43R4H
+VtA4oJVwIHaM190e3p9xxCPvgxNcoyQVTSlAPGrEqdi3pkSlDfTgnXceQHAm/NrZNuR55LU/vJtl
+vrsRls/bxig5OgjOR1tTWsWZ/l2p3e9M1MalrQLmjAcSHm8D0W+go/MpvRLHUKKwf4ipmXeascCl
+OS5cfGniLLDqN2qk4Vrh9VDlg++luyqI54zb/W1elxmofmZ1a3Hqv7HHb6D0jqTsNFFbjCYDcKF3
+1QESVwA12yPeDooomf2xEG9L/zgtYE4snOtnta1J7ksfrK/7DZBaZmBwXarNeNQk7shBoJMBkpxq
+nvy5JMWzFYJ+vq6VK+uxwNrjAWALXmmshFZhvnEX/h0TD/7Gh0Xp/jKgGg0TpJRVcaUWi7rKibCy
+x/yP2FS1k2Kdzs9Z+z0YzirLNRWCXf9UIltxUvu3yf5gmwBBZPCqKuy2QkPOiWaByIufOVQDJdMW
+NY6E0F/6MBr1mmz0DlP5OlvRHA==
+-----END CERTIFICATE-----
+
+DigiCert Assured ID Root CA
+===========================
+-----BEGIN CERTIFICATE-----
+MIIDtzCCAp+gAwIBAgIQDOfg5RfYRv6P5WD8G/AwOTANBgkqhkiG9w0BAQUFADBlMQswCQYDVQQG
+EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSQw
+IgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0EwHhcNMDYxMTEwMDAwMDAwWhcNMzEx
+MTEwMDAwMDAwWjBlMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQL
+ExB3d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0Ew
+ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCtDhXO5EOAXLGH87dg+XESpa7cJpSIqvTO
+9SA5KFhgDPiA2qkVlTJhPLWxKISKityfCgyDF3qPkKyK53lTXDGEKvYPmDI2dsze3Tyoou9q+yHy
+UmHfnyDXH+Kx2f4YZNISW1/5WBg1vEfNoTb5a3/UsDg+wRvDjDPZ2C8Y/igPs6eD1sNuRMBhNZYW
+/lmci3Zt1/GiSw0r/wty2p5g0I6QNcZ4VYcgoc/lbQrISXwxmDNsIumH0DJaoroTghHtORedmTpy
+oeb6pNnVFzF1roV9Iq4/AUaG9ih5yLHa5FcXxH4cDrC0kqZWs72yl+2qp/C3xag/lRbQ/6GW6whf
+GHdPAgMBAAGjYzBhMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRF
+66Kv9JLLgjEtUYunpyGd823IDzAfBgNVHSMEGDAWgBRF66Kv9JLLgjEtUYunpyGd823IDzANBgkq
+hkiG9w0BAQUFAAOCAQEAog683+Lt8ONyc3pklL/3cmbYMuRCdWKuh+vy1dneVrOfzM4UKLkNl2Bc
+EkxY5NM9g0lFWJc1aRqoR+pWxnmrEthngYTffwk8lOa4JiwgvT2zKIn3X/8i4peEH+ll74fg38Fn
+SbNd67IJKusm7Xi+fT8r87cmNW1fiQG2SVufAQWbqz0lwcy2f8Lxb4bG+mRo64EtlOtCt/qMHt1i
+8b5QZ7dsvfPxH2sMNgcWfzd8qVttevESRmCD1ycEvkvOl77DZypoEd+A5wwzZr8TDRRu838fYxAe
++o0bJW1sj6W3YQGx0qMmoRBxna3iw/nDmVG3KwcIzi7mULKn+gpFL6Lw8g==
+-----END CERTIFICATE-----
+
+DigiCert Global Root CA
+=======================
+-----BEGIN CERTIFICATE-----
+MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBhMQswCQYDVQQG
+EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSAw
+HgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBDQTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAw
+MDAwMDBaMGExCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3
+dy5kaWdpY2VydC5jb20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkq
+hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsBCSDMAZOn
+TjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97nh6Vfe63SKMI2tavegw5
+BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt43C/dxC//AH2hdmoRBBYMql1GNXRor5H
+4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7PT19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y
+7vrTC0LUq7dBMtoM1O/4gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQAB
+o2MwYTAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbRTLtm
+8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUwDQYJKoZIhvcNAQEF
+BQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/EsrhMAtudXH/vTBH1jLuG2cenTnmCmr
+EbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIt
+tep3Sp+dWOIrWcBAI+0tKIJFPnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886
+UAb3LujEV0lsYSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk
+CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4=
+-----END CERTIFICATE-----
+
+DigiCert High Assurance EV Root CA
+==================================
+-----BEGIN CERTIFICATE-----
+MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBsMQswCQYDVQQG
+EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSsw
+KQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5jZSBFViBSb290IENBMB4XDTA2MTExMDAwMDAw
+MFoXDTMxMTExMDAwMDAwMFowbDELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZ
+MBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNlcnQgSGlnaCBBc3N1cmFu
+Y2UgRVYgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbM5XPm+9S75S0t
+Mqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlBWTrT3JTWPNt0OKRKzE0lgvdKpVMS
+OO7zSW1xkX5jtqumX8OkhPhPYlG++MXs2ziS4wblCJEMxChBVfvLWokVfnHoNb9Ncgk9vjo4UFt3
+MRuNs8ckRZqnrG0AFFoEt7oT61EKmEFBIk5lYYeBQVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhakaHPQ
+NAQTXKFx01p8VdteZOE3hzBWBOURtCmAEvF5OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUe
+h10aUAsgEsxBu24LUTi4S8sCAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQFMAMB
+Af8wHQYDVR0OBBYEFLE+w2kD+L9HAdSYJhoIAu9jZCvDMB8GA1UdIwQYMBaAFLE+w2kD+L9HAdSY
+JhoIAu9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQAcGgaX3NecnzyIZgYIVyHbIUf4KmeqvxgydkAQ
+V8GK83rZEWWONfqe/EW1ntlMMUu4kehDLI6zeM7b41N5cdblIZQB2lWHmiRk9opmzN6cN82oNLFp
+myPInngiK3BD41VHMWEZ71jFhS9OMPagMRYjyOfiZRYzy78aG6A9+MpeizGLYAiJLQwGXFK3xPkK
+mNEVX58Svnw2Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe
+vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep+OkuE6N36B9K
+-----END CERTIFICATE-----
+
+Certplus Class 2 Primary CA
+===========================
+-----BEGIN CERTIFICATE-----
+MIIDkjCCAnqgAwIBAgIRAIW9S/PY2uNp9pTXX8OlRCMwDQYJKoZIhvcNAQEFBQAwPTELMAkGA1UE
+BhMCRlIxETAPBgNVBAoTCENlcnRwbHVzMRswGQYDVQQDExJDbGFzcyAyIFByaW1hcnkgQ0EwHhcN
+OTkwNzA3MTcwNTAwWhcNMTkwNzA2MjM1OTU5WjA9MQswCQYDVQQGEwJGUjERMA8GA1UEChMIQ2Vy
+dHBsdXMxGzAZBgNVBAMTEkNsYXNzIDIgUHJpbWFyeSBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEP
+ADCCAQoCggEBANxQltAS+DXSCHh6tlJw/W/uz7kRy1134ezpfgSN1sxvc0NXYKwzCkTsA18cgCSR
+5aiRVhKC9+Ar9NuuYS6JEI1rbLqzAr3VNsVINyPi8Fo3UjMXEuLRYE2+L0ER4/YXJQyLkcAbmXuZ
+Vg2v7tK8R1fjeUl7NIknJITesezpWE7+Tt9avkGtrAjFGA7v0lPubNCdEgETjdyAYveVqUSISnFO
+YFWe2yMZeVYHDD9jC1yw4r5+FfyUM1hBOHTE4Y+L3yasH7WLO7dDWWuwJKZtkIvEcupdM5i3y95e
+e++U8Rs+yskhwcWYAqqi9lt3m/V+llU0HGdpwPFC40es/CgcZlUCAwEAAaOBjDCBiTAPBgNVHRME
+CDAGAQH/AgEKMAsGA1UdDwQEAwIBBjAdBgNVHQ4EFgQU43Mt38sOKAze3bOkynm4jrvoMIkwEQYJ
+YIZIAYb4QgEBBAQDAgEGMDcGA1UdHwQwMC4wLKAqoCiGJmh0dHA6Ly93d3cuY2VydHBsdXMuY29t
+L0NSTC9jbGFzczIuY3JsMA0GCSqGSIb3DQEBBQUAA4IBAQCnVM+IRBnL39R/AN9WM2K191EBkOvD
+P9GIROkkXe/nFL0gt5o8AP5tn9uQ3Nf0YtaLcF3n5QRIqWh8yfFC82x/xXp8HVGIutIKPidd3i1R
+TtMTZGnkLuPT55sJmabglZvOGtd/vjzOUrMRFcEPF80Du5wlFbqidon8BvEY0JNLDnyCt6X09l/+
+7UCmnYR0ObncHoUW2ikbhiMAybuJfm6AiB4vFLQDJKgybwOaRywwvlbGp0ICcBvqQNi6BQNwB6SW
+//1IMwrh3KWBkJtN3X3n57LNXMhqlfil9o3EXXgIvnsG1knPGTZQIy4I5p4FTUcY1Rbpsda2ENW7
+l7+ijrRU
+-----END CERTIFICATE-----
+
+DST Root CA X3
+==============
+-----BEGIN CERTIFICATE-----
+MIIDSjCCAjKgAwIBAgIQRK+wgNajJ7qJMDmGLvhAazANBgkqhkiG9w0BAQUFADA/MSQwIgYDVQQK
+ExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMTDkRTVCBSb290IENBIFgzMB4X
+DTAwMDkzMDIxMTIxOVoXDTIxMDkzMDE0MDExNVowPzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1
+cmUgVHJ1c3QgQ28uMRcwFQYDVQQDEw5EU1QgUm9vdCBDQSBYMzCCASIwDQYJKoZIhvcNAQEBBQAD
+ggEPADCCAQoCggEBAN+v6ZdQCINXtMxiZfaQguzH0yxrMMpb7NnDfcdAwRgUi+DoM3ZJKuM/IUmT
+rE4Orz5Iy2Xu/NMhD2XSKtkyj4zl93ewEnu1lcCJo6m67XMuegwGMoOifooUMM0RoOEqOLl5CjH9
+UL2AZd+3UWODyOKIYepLYYHsUmu5ouJLGiifSKOeDNoJjj4XLh7dIN9bxiqKqy69cK3FCxolkHRy
+xXtqqzTWMIn/5WgTe1QLyNau7Fqckh49ZLOMxt+/yUFw7BZy1SbsOFU5Q9D8/RhcQPGX69Wam40d
+utolucbY38EVAjqr2m7xPi71XAicPNaDaeQQmxkqtilX4+U9m5/wAl0CAwEAAaNCMEAwDwYDVR0T
+AQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFMSnsaR7LHH62+FLkHX/xBVghYkQ
+MA0GCSqGSIb3DQEBBQUAA4IBAQCjGiybFwBcqR7uKGY3Or+Dxz9LwwmglSBd49lZRNI+DT69ikug
+dB/OEIKcdBodfpga3csTS7MgROSR6cz8faXbauX+5v3gTt23ADq1cEmv8uXrAvHRAosZy5Q6XkjE
+GB5YGV8eAlrwDPGxrancWYaLbumR9YbK+rlmM6pZW87ipxZzR8srzJmwN0jP41ZL9c8PDHIyh8bw
+RLtTcm1D9SZImlJnt1ir/md2cXjbDaJWFBM5JDGFoqgCWjBH4d1QB7wCCZAA62RjYJsWvIjJEubS
+fZGL+T0yjWW06XyxV3bqxbYoOb8VZRzI9neWagqNdwvYkQsEjgfbKbYK7p2CNTUQ
+-----END CERTIFICATE-----
+
+DST ACES CA X6
+==============
+-----BEGIN CERTIFICATE-----
+MIIECTCCAvGgAwIBAgIQDV6ZCtadt3js2AdWO4YV2TANBgkqhkiG9w0BAQUFADBbMQswCQYDVQQG
+EwJVUzEgMB4GA1UEChMXRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QxETAPBgNVBAsTCERTVCBBQ0VT
+MRcwFQYDVQQDEw5EU1QgQUNFUyBDQSBYNjAeFw0wMzExMjAyMTE5NThaFw0xNzExMjAyMTE5NTha
+MFsxCzAJBgNVBAYTAlVTMSAwHgYDVQQKExdEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdDERMA8GA1UE
+CxMIRFNUIEFDRVMxFzAVBgNVBAMTDkRTVCBBQ0VTIENBIFg2MIIBIjANBgkqhkiG9w0BAQEFAAOC
+AQ8AMIIBCgKCAQEAuT31LMmU3HWKlV1j6IR3dma5WZFcRt2SPp/5DgO0PWGSvSMmtWPuktKe1jzI
+DZBfZIGxqAgNTNj50wUoUrQBJcWVHAx+PhCEdc/BGZFjz+iokYi5Q1K7gLFViYsx+tC3dr5BPTCa
+pCIlF3PoHuLTrCq9Wzgh1SpL11V94zpVvddtawJXa+ZHfAjIgrrep4c9oW24MFbCswKBXy314pow
+GCi4ZtPLAZZv6opFVdbgnf9nKxcCpk4aahELfrd755jWjHZvwTvbUJN+5dCOHze4vbrGn2zpfDPy
+MjwmR/onJALJfh1biEITajV8fTXpLmaRcpPVMibEdPVTo7NdmvYJywIDAQABo4HIMIHFMA8GA1Ud
+EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgHGMB8GA1UdEQQYMBaBFHBraS1vcHNAdHJ1c3Rkc3Qu
+Y29tMGIGA1UdIARbMFkwVwYKYIZIAWUDAgEBATBJMEcGCCsGAQUFBwIBFjtodHRwOi8vd3d3LnRy
+dXN0ZHN0LmNvbS9jZXJ0aWZpY2F0ZXMvcG9saWN5L0FDRVMtaW5kZXguaHRtbDAdBgNVHQ4EFgQU
+CXIGThhDD+XWzMNqizF7eI+og7gwDQYJKoZIhvcNAQEFBQADggEBAKPYjtay284F5zLNAdMEA+V2
+5FYrnJmQ6AgwbN99Pe7lv7UkQIRJ4dEorsTCOlMwiPH1d25Ryvr/ma8kXxug/fKshMrfqfBfBC6t
+Fr8hlxCBPeP/h40y3JTlR4peahPJlJU90u7INJXQgNStMgiAVDzgvVJT11J8smk/f3rPanTK+gQq
+nExaBqXpIK1FZg9p8d2/6eMyi/rgwYZNcjwu2JN4Cir42NInPRmJX1p7ijvMDNpRrscL9yuwNwXs
+vFcj4jjSm2jzVhKIT0J8uDHEtdvkyCE06UgRNe76x5JXxZ805Mf29w4LTJxoeHtxMcfrHuBnQfO3
+oKfN5XozNmr6mis=
+-----END CERTIFICATE-----
+
+SwissSign Gold CA - G2
+======================
+-----BEGIN CERTIFICATE-----
+MIIFujCCA6KgAwIBAgIJALtAHEP1Xk+wMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNVBAYTAkNIMRUw
+EwYDVQQKEwxTd2lzc1NpZ24gQUcxHzAdBgNVBAMTFlN3aXNzU2lnbiBHb2xkIENBIC0gRzIwHhcN
+MDYxMDI1MDgzMDM1WhcNMzYxMDI1MDgzMDM1WjBFMQswCQYDVQQGEwJDSDEVMBMGA1UEChMMU3dp
+c3NTaWduIEFHMR8wHQYDVQQDExZTd2lzc1NpZ24gR29sZCBDQSAtIEcyMIICIjANBgkqhkiG9w0B
+AQEFAAOCAg8AMIICCgKCAgEAr+TufoskDhJuqVAtFkQ7kpJcyrhdhJJCEyq8ZVeCQD5XJM1QiyUq
+t2/876LQwB8CJEoTlo8jE+YoWACjR8cGp4QjK7u9lit/VcyLwVcfDmJlD909Vopz2q5+bbqBHH5C
+jCA12UNNhPqE21Is8w4ndwtrvxEvcnifLtg+5hg3Wipy+dpikJKVyh+c6bM8K8vzARO/Ws/BtQpg
+vd21mWRTuKCWs2/iJneRjOBiEAKfNA+k1ZIzUd6+jbqEemA8atufK+ze3gE/bk3lUIbLtK/tREDF
+ylqM2tIrfKjuvqblCqoOpd8FUrdVxyJdMmqXl2MT28nbeTZ7hTpKxVKJ+STnnXepgv9VHKVxaSvR
+AiTysybUa9oEVeXBCsdtMDeQKuSeFDNeFhdVxVu1yzSJkvGdJo+hB9TGsnhQ2wwMC3wLjEHXuend
+jIj3o02yMszYF9rNt85mndT9Xv+9lz4pded+p2JYryU0pUHHPbwNUMoDAw8IWh+Vc3hiv69yFGkO
+peUDDniOJihC8AcLYiAQZzlG+qkDzAQ4embvIIO1jEpWjpEA/I5cgt6IoMPiaG59je883WX0XaxR
+7ySArqpWl2/5rX3aYT+YdzylkbYcjCbaZaIJbcHiVOO5ykxMgI93e2CaHt+28kgeDrpOVG2Y4OGi
+GqJ3UM/EY5LsRxmd6+ZrzsECAwEAAaOBrDCBqTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUw
+AwEB/zAdBgNVHQ4EFgQUWyV7lqRlUX64OfPAeGZe6Drn8O4wHwYDVR0jBBgwFoAUWyV7lqRlUX64
+OfPAeGZe6Drn8O4wRgYDVR0gBD8wPTA7BglghXQBWQECAQEwLjAsBggrBgEFBQcCARYgaHR0cDov
+L3JlcG9zaXRvcnkuc3dpc3NzaWduLmNvbS8wDQYJKoZIhvcNAQEFBQADggIBACe645R88a7A3hfm
+5djV9VSwg/S7zV4Fe0+fdWavPOhWfvxyeDgD2StiGwC5+OlgzczOUYrHUDFu4Up+GC9pWbY9ZIEr
+44OE5iKHjn3g7gKZYbge9LgriBIWhMIxkziWMaa5O1M/wySTVltpkuzFwbs4AOPsF6m43Md8AYOf
+Mke6UiI0HTJ6CVanfCU2qT1L2sCCbwq7EsiHSycR+R4tx5M/nttfJmtS2S6K8RTGRI0Vqbe/vd6m
+Gu6uLftIdxf+u+yvGPUqUfA5hJeVbG4bwyvEdGB5JbAKJ9/fXtI5z0V9QkvfsywexcZdylU6oJxp
+mo/a77KwPJ+HbBIrZXAVUjEaJM9vMSNQH4xPjyPDdEFjHFWoFN0+4FFQz/EbMFYOkrCChdiDyyJk
+vC24JdVUorgG6q2SpCSgwYa1ShNqR88uC1aVVMvOmttqtKay20EIhid392qgQmwLOM7XdVAyksLf
+KzAiSNDVQTglXaTpXZ/GlHXQRf0wl0OPkKsKx4ZzYEppLd6leNcG2mqeSz53OiATIgHQv2ieY2Br
+NU0LbbqhPcCT4H8js1WtciVORvnSFu+wZMEBnunKoGqYDs/YYPIvSbjkQuE4NRb0yG5P94FW6Lqj
+viOvrv1vA+ACOzB2+httQc8Bsem4yWb02ybzOqR08kkkW8mw0FfB+j564ZfJ
+-----END CERTIFICATE-----
+
+SwissSign Silver CA - G2
+========================
+-----BEGIN CERTIFICATE-----
+MIIFvTCCA6WgAwIBAgIITxvUL1S7L0swDQYJKoZIhvcNAQEFBQAwRzELMAkGA1UEBhMCQ0gxFTAT
+BgNVBAoTDFN3aXNzU2lnbiBBRzEhMB8GA1UEAxMYU3dpc3NTaWduIFNpbHZlciBDQSAtIEcyMB4X
+DTA2MTAyNTA4MzI0NloXDTM2MTAyNTA4MzI0NlowRzELMAkGA1UEBhMCQ0gxFTATBgNVBAoTDFN3
+aXNzU2lnbiBBRzEhMB8GA1UEAxMYU3dpc3NTaWduIFNpbHZlciBDQSAtIEcyMIICIjANBgkqhkiG
+9w0BAQEFAAOCAg8AMIICCgKCAgEAxPGHf9N4Mfc4yfjDmUO8x/e8N+dOcbpLj6VzHVxumK4DV644
+N0MvFz0fyM5oEMF4rhkDKxD6LHmD9ui5aLlV8gREpzn5/ASLHvGiTSf5YXu6t+WiE7brYT7QbNHm
++/pe7R20nqA1W6GSy/BJkv6FCgU+5tkL4k+73JU3/JHpMjUi0R86TieFnbAVlDLaYQ1HTWBCrpJH
+6INaUFjpiou5XaHc3ZlKHzZnu0jkg7Y360g6rw9njxcH6ATK72oxh9TAtvmUcXtnZLi2kUpCe2Uu
+MGoM9ZDulebyzYLs2aFK7PayS+VFheZteJMELpyCbTapxDFkH4aDCyr0NQp4yVXPQbBH6TCfmb5h
+qAaEuSh6XzjZG6k4sIN/c8HDO0gqgg8hm7jMqDXDhBuDsz6+pJVpATqJAHgE2cn0mRmrVn5bi4Y5
+FZGkECwJMoBgs5PAKrYYC51+jUnyEEp/+dVGLxmSo5mnJqy7jDzmDrxHB9xzUfFwZC8I+bRHHTBs
+ROopN4WSaGa8gzj+ezku01DwH/teYLappvonQfGbGHLy9YR0SslnxFSuSGTfjNFusB3hB48IHpmc
+celM2KX3RxIfdNFRnobzwqIjQAtz20um53MGjMGg6cFZrEb65i/4z3GcRm25xBWNOHkDRUjvxF3X
+CO6HOSKGsg0PWEP3calILv3q1h8CAwEAAaOBrDCBqTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/
+BAUwAwEB/zAdBgNVHQ4EFgQUF6DNweRBtjpbO8tFnb0cwpj6hlgwHwYDVR0jBBgwFoAUF6DNweRB
+tjpbO8tFnb0cwpj6hlgwRgYDVR0gBD8wPTA7BglghXQBWQEDAQEwLjAsBggrBgEFBQcCARYgaHR0
+cDovL3JlcG9zaXRvcnkuc3dpc3NzaWduLmNvbS8wDQYJKoZIhvcNAQEFBQADggIBAHPGgeAn0i0P
+4JUw4ppBf1AsX19iYamGamkYDHRJ1l2E6kFSGG9YrVBWIGrGvShpWJHckRE1qTodvBqlYJ7YH39F
+kWnZfrt4csEGDyrOj4VwYaygzQu4OSlWhDJOhrs9xCrZ1x9y7v5RoSJBsXECYxqCsGKrXlcSH9/L
+3XWgwF15kIwb4FDm3jH+mHtwX6WQ2K34ArZv02DdQEsixT2tOnqfGhpHkXkzuoLcMmkDlm4fS/Bx
+/uNncqCxv1yL5PqZIseEuRuNI5c/7SXgz2W79WEE790eslpBIlqhn10s6FvJbakMDHiqYMZWjwFa
+DGi8aRl5xB9+lwW/xekkUV7U1UtT7dkjWjYDZaPBA61BMPNGG4WQr2W11bHkFlt4dR2Xem1ZqSqP
+e97Dh4kQmUlzeMg9vVE1dCrV8X5pGyq7O70luJpaPXJhkGaH7gzWTdQRdAtq/gsD/KNVV4n+Ssuu
+WxcFyPKNIzFTONItaj+CuY0IavdeQXRuwxF+B6wpYJE/OMpXEA29MC/HpeZBoNquBYeaoKRlbEwJ
+DIm6uNO5wJOKMPqN5ZprFQFOZ6raYlY+hAhm0sQ2fac+EPyI4NSA5QC9qvNOBqN6avlicuMJT+ub
+DgEj8Z+7fNzcbBGXJbLytGMU0gYqZ4yD9c7qB9iaah7s5Aq7KkzrCWA5zspi2C5u
+-----END CERTIFICATE-----
+
+GeoTrust Primary Certification Authority
+========================================
+-----BEGIN CERTIFICATE-----
+MIIDfDCCAmSgAwIBAgIQGKy1av1pthU6Y2yv2vrEoTANBgkqhkiG9w0BAQUFADBYMQswCQYDVQQG
+EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjExMC8GA1UEAxMoR2VvVHJ1c3QgUHJpbWFyeSBD
+ZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNjExMjcwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMFgx
+CzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMTEwLwYDVQQDEyhHZW9UcnVzdCBQ
+cmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
+CgKCAQEAvrgVe//UfH1nrYNke8hCUy3f9oQIIGHWAVlqnEQRr+92/ZV+zmEwu3qDXwK9AWbK7hWN
+b6EwnL2hhZ6UOvNWiAAxz9juapYC2e0DjPt1befquFUWBRaa9OBesYjAZIVcFU2Ix7e64HXprQU9
+nceJSOC7KMgD4TCTZF5SwFlwIjVXiIrxlQqD17wxcwE07e9GceBrAqg1cmuXm2bgyxx5X9gaBGge
+RwLmnWDiNpcB3841kt++Z8dtd1k7j53WkBWUvEI0EME5+bEnPn7WinXFsq+W06Lem+SYvn3h6YGt
+tm/81w7a4DSwDRp35+MImO9Y+pyEtzavwt+s0vQQBnBxNQIDAQABo0IwQDAPBgNVHRMBAf8EBTAD
+AQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQULNVQQZcVi/CPNmFbSvtr2ZnJM5IwDQYJKoZI
+hvcNAQEFBQADggEBAFpwfyzdtzRP9YZRqSa+S7iq8XEN3GHHoOo0Hnp3DwQ16CePbJC/kRYkRj5K
+Ts4rFtULUh38H2eiAkUxT87z+gOneZ1TatnaYzr4gNfTmeGl4b7UVXGYNTq+k+qurUKykG/g/CFN
+NWMziUnWm07Kx+dOCQD32sfvmWKZd7aVIl6KoKv0uHiYyjgZmclynnjNS6yvGaBzEi38wkG6gZHa
+Floxt/m0cYASSJlyc1pZU8FjUjPtp8nSOQJw+uCxQmYpqptR7TBUIhRf2asdweSU8Pj1K/fqynhG
+1riR/aYNKxoUAT6A8EKglQdebc3MS6RFjasS6LPeWuWgfOgPIh1a6Vk=
+-----END CERTIFICATE-----
+
+thawte Primary Root CA
+======================
+-----BEGIN CERTIFICATE-----
+MIIEIDCCAwigAwIBAgIQNE7VVyDV7exJ9C/ON9srbTANBgkqhkiG9w0BAQUFADCBqTELMAkGA1UE
+BhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2
+aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMvKGMpIDIwMDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhv
+cml6ZWQgdXNlIG9ubHkxHzAdBgNVBAMTFnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwHhcNMDYxMTE3
+MDAwMDAwWhcNMzYwNzE2MjM1OTU5WjCBqTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwg
+SW5jLjEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMv
+KGMpIDIwMDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxHzAdBgNVBAMT
+FnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCs
+oPD7gFnUnMekz52hWXMJEEUMDSxuaPFsW0hoSVk3/AszGcJ3f8wQLZU0HObrTQmnHNK4yZc2AreJ
+1CRfBsDMRJSUjQJib+ta3RGNKJpchJAQeg29dGYvajig4tVUROsdB58Hum/u6f1OCyn1PoSgAfGc
+q/gcfomk6KHYcWUNo1F77rzSImANuVud37r8UVsLr5iy6S7pBOhih94ryNdOwUxkHt3Ph1i6Sk/K
+aAcdHJ1KxtUvkcx8cXIcxcBn6zL9yZJclNqFwJu/U30rCfSMnZEfl2pSy94JNqR32HuHUETVPm4p
+afs5SSYeCaWAe0At6+gnhcn+Yf1+5nyXHdWdAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYD
+VR0PAQH/BAQDAgEGMB0GA1UdDgQWBBR7W0XPr87Lev0xkhpqtvNG61dIUDANBgkqhkiG9w0BAQUF
+AAOCAQEAeRHAS7ORtvzw6WfUDW5FvlXok9LOAz/t2iWwHVfLHjp2oEzsUHboZHIMpKnxuIvW1oeE
+uzLlQRHAd9mzYJ3rG9XRbkREqaYB7FViHXe4XI5ISXycO1cRrK1zN44veFyQaEfZYGDm/Ac9IiAX
+xPcW6cTYcvnIc3zfFi8VqT79aie2oetaupgf1eNNZAqdE8hhuvU5HIe6uL17In/2/qxAeeWsEG89
+jxt5dovEN7MhGITlNgDrYyCZuen+MwS7QcjBAvlEYyCegc5C09Y/LHbTY5xZ3Y+m4Q6gLkH3LpVH
+z7z9M/P2C2F+fpErgUfCJzDupxBdN49cOSvkBPB7jVaMaA==
+-----END CERTIFICATE-----
+
+VeriSign Class 3 Public Primary Certification Authority - G5
+============================================================
+-----BEGIN CERTIFICATE-----
+MIIE0zCCA7ugAwIBAgIQGNrRniZ96LtKIVjNzGs7SjANBgkqhkiG9w0BAQUFADCByjELMAkGA1UE
+BhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBO
+ZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVk
+IHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRp
+ZmljYXRpb24gQXV0aG9yaXR5IC0gRzUwHhcNMDYxMTA4MDAwMDAwWhcNMzYwNzE2MjM1OTU5WjCB
+yjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2ln
+biBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJpU2lnbiwgSW5jLiAtIEZvciBh
+dXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmlt
+YXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
+ggEKAoIBAQCvJAgIKXo1nmAMqudLO07cfLw8RRy7K+D+KQL5VwijZIUVJ/XxrcgxiV0i6CqqpkKz
+j/i5Vbext0uz/o9+B1fs70PbZmIVYc9gDaTY3vjgw2IIPVQT60nKWVSFJuUrjxuf6/WhkcIzSdhD
+Y2pSS9KP6HBRTdGJaXvHcPaz3BJ023tdS1bTlr8Vd6Gw9KIl8q8ckmcY5fQGBO+QueQA5N06tRn/
+Arr0PO7gi+s3i+z016zy9vA9r911kTMZHRxAy3QkGSGT2RT+rCpSx4/VBEnkjWNHiDxpg8v+R70r
+fk/Fla4OndTRQ8Bnc+MUCH7lP59zuDMKz10/NIeWiu5T6CUVAgMBAAGjgbIwga8wDwYDVR0TAQH/
+BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwbQYIKwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJaW1hZ2Uv
+Z2lmMCEwHzAHBgUrDgMCGgQUj+XTGoasjY5rw8+AatRIGCx7GS4wJRYjaHR0cDovL2xvZ28udmVy
+aXNpZ24uY29tL3ZzbG9nby5naWYwHQYDVR0OBBYEFH/TZafC3ey78DAJ80M5+gKvMzEzMA0GCSqG
+SIb3DQEBBQUAA4IBAQCTJEowX2LP2BqYLz3q3JktvXf2pXkiOOzEp6B4Eq1iDkVwZMXnl2YtmAl+
+X6/WzChl8gGqCBpH3vn5fJJaCGkgDdk+bW48DW7Y5gaRQBi5+MHt39tBquCWIMnNZBU4gcmU7qKE
+KQsTb47bDN0lAtukixlE0kF6BWlKWE9gyn6CagsCqiUXObXbf+eEZSqVir2G3l6BFoMtEMze/aiC
+Km0oHw0LxOXnGiYZ4fQRbxC1lfznQgUy286dUV4otp6F01vvpX1FQHKOtw5rDgb7MzVIcbidJ4vE
+ZV8NhnacRHr2lVz2XTIIM6RUthg/aFzyQkqFOFSDX9HoLPKsEdao7WNq
+-----END CERTIFICATE-----
+
+SecureTrust CA
+==============
+-----BEGIN CERTIFICATE-----
+MIIDuDCCAqCgAwIBAgIQDPCOXAgWpa1Cf/DrJxhZ0DANBgkqhkiG9w0BAQUFADBIMQswCQYDVQQG
+EwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24xFzAVBgNVBAMTDlNlY3VyZVRy
+dXN0IENBMB4XDTA2MTEwNzE5MzExOFoXDTI5MTIzMTE5NDA1NVowSDELMAkGA1UEBhMCVVMxIDAe
+BgNVBAoTF1NlY3VyZVRydXN0IENvcnBvcmF0aW9uMRcwFQYDVQQDEw5TZWN1cmVUcnVzdCBDQTCC
+ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKukgeWVzfX2FI7CT8rU4niVWJxB4Q2ZQCQX
+OZEzZum+4YOvYlyJ0fwkW2Gz4BERQRwdbvC4u/jep4G6pkjGnx29vo6pQT64lO0pGtSO0gMdA+9t
+DWccV9cGrcrI9f4Or2YlSASWC12juhbDCE/RRvgUXPLIXgGZbf2IzIaowW8xQmxSPmjL8xk037uH
+GFaAJsTQ3MBv396gwpEWoGQRS0S8Hvbn+mPeZqx2pHGj7DaUaHp3pLHnDi+BeuK1cobvomuL8A/b
+01k/unK8RCSc43Oz969XL0Imnal0ugBS8kvNU3xHCzaFDmapCJcWNFfBZveA4+1wVMeT4C4oFVmH
+ursCAwEAAaOBnTCBmjATBgkrBgEEAYI3FAIEBh4EAEMAQTALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/
+BAUwAwEB/zAdBgNVHQ4EFgQUQjK2FvoE/f5dS3rD/fdMQB1aQ68wNAYDVR0fBC0wKzApoCegJYYj
+aHR0cDovL2NybC5zZWN1cmV0cnVzdC5jb20vU1RDQS5jcmwwEAYJKwYBBAGCNxUBBAMCAQAwDQYJ
+KoZIhvcNAQEFBQADggEBADDtT0rhWDpSclu1pqNlGKa7UTt36Z3q059c4EVlew3KW+JwULKUBRSu
+SceNQQcSc5R+DCMh/bwQf2AQWnL1mA6s7Ll/3XpvXdMc9P+IBWlCqQVxyLesJugutIxq/3HcuLHf
+mbx8IVQr5Fiiu1cprp6poxkmD5kuCLDv/WnPmRoJjeOnnyvJNjR7JLN4TJUXpAYmHrZkUjZfYGfZ
+nMUFdAvnZyPSCPyI6a6Lf+Ew9Dd+/cYy2i2eRDAwbO4H3tI0/NL/QPZL9GZGBlSm8jIKYyYwa5vR
+3ItHuuG51WLQoqD0ZwV4KWMabwTW+MZMo5qxN7SN5ShLHZ4swrhovO0C7jE=
+-----END CERTIFICATE-----
+
+Secure Global CA
+================
+-----BEGIN CERTIFICATE-----
+MIIDvDCCAqSgAwIBAgIQB1YipOjUiolN9BPI8PjqpTANBgkqhkiG9w0BAQUFADBKMQswCQYDVQQG
+EwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24xGTAXBgNVBAMTEFNlY3VyZSBH
+bG9iYWwgQ0EwHhcNMDYxMTA3MTk0MjI4WhcNMjkxMjMxMTk1MjA2WjBKMQswCQYDVQQGEwJVUzEg
+MB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24xGTAXBgNVBAMTEFNlY3VyZSBHbG9iYWwg
+Q0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvNS7YrGxVaQZx5RNoJLNP2MwhR/jx
+YDiJiQPpvepeRlMJ3Fz1Wuj3RSoC6zFh1ykzTM7HfAo3fg+6MpjhHZevj8fcyTiW89sa/FHtaMbQ
+bqR8JNGuQsiWUGMu4P51/pinX0kuleM5M2SOHqRfkNJnPLLZ/kG5VacJjnIFHovdRIWCQtBJwB1g
+8NEXLJXr9qXBkqPFwqcIYA1gBBCWeZ4WNOaptvolRTnIHmX5k/Wq8VLcmZg9pYYaDDUz+kulBAYV
+HDGA76oYa8J719rO+TMg1fW9ajMtgQT7sFzUnKPiXB3jqUJ1XnvUd+85VLrJChgbEplJL4hL/VBi
+0XPnj3pDAgMBAAGjgZ0wgZowEwYJKwYBBAGCNxQCBAYeBABDAEEwCwYDVR0PBAQDAgGGMA8GA1Ud
+EwEB/wQFMAMBAf8wHQYDVR0OBBYEFK9EBMJBfkiD2045AuzshHrmzsmkMDQGA1UdHwQtMCswKaAn
+oCWGI2h0dHA6Ly9jcmwuc2VjdXJldHJ1c3QuY29tL1NHQ0EuY3JsMBAGCSsGAQQBgjcVAQQDAgEA
+MA0GCSqGSIb3DQEBBQUAA4IBAQBjGghAfaReUw132HquHw0LURYD7xh8yOOvaliTFGCRsoTciE6+
+OYo68+aCiV0BN7OrJKQVDpI1WkpEXk5X+nXOH0jOZvQ8QCaSmGwb7iRGDBezUqXbpZGRzzfTb+cn
+CDpOGR86p1hcF895P4vkp9MmI50mD1hp/Ed+stCNi5O/KU9DaXR2Z0vPB4zmAve14bRDtUstFJ/5
+3CYNv6ZHdAbYiNE6KTCEztI5gGIbqMdXSbxqVVFnFUq+NQfk1XWYN3kwFNspnWzFacxHVaIw98xc
+f8LDmBxrThaA63p4ZUWiABqvDA1VZDRIuJK58bRQKfJPIx/abKwfROHdI3hRW8cW
+-----END CERTIFICATE-----
+
+COMODO Certification Authority
+==============================
+-----BEGIN CERTIFICATE-----
+MIIEHTCCAwWgAwIBAgIQToEtioJl4AsC7j41AkblPTANBgkqhkiG9w0BAQUFADCBgTELMAkGA1UE
+BhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgG
+A1UEChMRQ09NT0RPIENBIExpbWl0ZWQxJzAlBgNVBAMTHkNPTU9ETyBDZXJ0aWZpY2F0aW9uIEF1
+dGhvcml0eTAeFw0wNjEyMDEwMDAwMDBaFw0yOTEyMzEyMzU5NTlaMIGBMQswCQYDVQQGEwJHQjEb
+MBkGA1UECBMSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHEwdTYWxmb3JkMRowGAYDVQQKExFD
+T01PRE8gQ0EgTGltaXRlZDEnMCUGA1UEAxMeQ09NT0RPIENlcnRpZmljYXRpb24gQXV0aG9yaXR5
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0ECLi3LjkRv3UcEbVASY06m/weaKXTuH
++7uIzg3jLz8GlvCiKVCZrts7oVewdFFxze1CkU1B/qnI2GqGd0S7WWaXUF601CxwRM/aN5VCaTww
+xHGzUvAhTaHYujl8HJ6jJJ3ygxaYqhZ8Q5sVW7euNJH+1GImGEaaP+vB+fGQV+useg2L23IwambV
+4EajcNxo2f8ESIl33rXp+2dtQem8Ob0y2WIC8bGoPW43nOIv4tOiJovGuFVDiOEjPqXSJDlqR6sA
+1KGzqSX+DT+nHbrTUcELpNqsOO9VUCQFZUaTNE8tja3G1CEZ0o7KBWFxB3NH5YoZEr0ETc5OnKVI
+rLsm9wIDAQABo4GOMIGLMB0GA1UdDgQWBBQLWOWLxkwVN6RAqTCpIb5HNlpW/zAOBgNVHQ8BAf8E
+BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zBJBgNVHR8EQjBAMD6gPKA6hjhodHRwOi8vY3JsLmNvbW9k
+b2NhLmNvbS9DT01PRE9DZXJ0aWZpY2F0aW9uQXV0aG9yaXR5LmNybDANBgkqhkiG9w0BAQUFAAOC
+AQEAPpiem/Yb6dc5t3iuHXIYSdOH5EOC6z/JqvWote9VfCFSZfnVDeFs9D6Mk3ORLgLETgdxb8CP
+OGEIqB6BCsAvIC9Bi5HcSEW88cbeunZrM8gALTFGTO3nnc+IlP8zwFboJIYmuNg4ON8qa90SzMc/
+RxdMosIGlgnW2/4/PEZB31jiVg88O8EckzXZOFKs7sjsLjBOlDW0JB9LeGna8gI4zJVSk/BwJVmc
+IGfE7vmLV2H0knZ9P4SNVbfo5azV8fUZVqZa+5Acr5Pr5RzUZ5ddBA6+C4OmF4O5MBKgxTMVBbkN
++8cFduPYSo38NBejxiEovjBFMR7HeL5YYTisO+IBZQ==
+-----END CERTIFICATE-----
+
+Network Solutions Certificate Authority
+=======================================
+-----BEGIN CERTIFICATE-----
+MIID5jCCAs6gAwIBAgIQV8szb8JcFuZHFhfjkDFo4DANBgkqhkiG9w0BAQUFADBiMQswCQYDVQQG
+EwJVUzEhMB8GA1UEChMYTmV0d29yayBTb2x1dGlvbnMgTC5MLkMuMTAwLgYDVQQDEydOZXR3b3Jr
+IFNvbHV0aW9ucyBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwHhcNMDYxMjAxMDAwMDAwWhcNMjkxMjMx
+MjM1OTU5WjBiMQswCQYDVQQGEwJVUzEhMB8GA1UEChMYTmV0d29yayBTb2x1dGlvbnMgTC5MLkMu
+MTAwLgYDVQQDEydOZXR3b3JrIFNvbHV0aW9ucyBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwggEiMA0G
+CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDkvH6SMG3G2I4rC7xGzuAnlt7e+foS0zwzc7MEL7xx
+jOWftiJgPl9dzgn/ggwbmlFQGiaJ3dVhXRncEg8tCqJDXRfQNJIg6nPPOCwGJgl6cvf6UDL4wpPT
+aaIjzkGxzOTVHzbRijr4jGPiFFlp7Q3Tf2vouAPlT2rlmGNpSAW+Lv8ztumXWWn4Zxmuk2GWRBXT
+crA/vGp97Eh/jcOrqnErU2lBUzS1sLnFBgrEsEX1QV1uiUV7PTsmjHTC5dLRfbIR1PtYMiKagMnc
+/Qzpf14Dl847ABSHJ3A4qY5usyd2mFHgBeMhqxrVhSI8KbWaFsWAqPS7azCPL0YCorEMIuDTAgMB
+AAGjgZcwgZQwHQYDVR0OBBYEFCEwyfsA106Y2oeqKtCnLrFAMadMMA4GA1UdDwEB/wQEAwIBBjAP
+BgNVHRMBAf8EBTADAQH/MFIGA1UdHwRLMEkwR6BFoEOGQWh0dHA6Ly9jcmwubmV0c29sc3NsLmNv
+bS9OZXR3b3JrU29sdXRpb25zQ2VydGlmaWNhdGVBdXRob3JpdHkuY3JsMA0GCSqGSIb3DQEBBQUA
+A4IBAQC7rkvnt1frf6ott3NHhWrB5KUd5Oc86fRZZXe1eltajSU24HqXLjjAV2CDmAaDn7l2em5Q
+4LqILPxFzBiwmZVRDuwduIj/h1AcgsLj4DKAv6ALR8jDMe+ZZzKATxcheQxpXN5eNK4CtSbqUN9/
+GGUsyfJj4akH/nxxH2szJGoeBfcFaMBqEssuXmHLrijTfsK0ZpEmXzwuJF/LWA/rKOyvEZbz3Htv
+wKeI8lN3s2Berq4o2jUsbzRF0ybh3uxbTydrFny9RAQYgrOJeRcQcT16ohZO9QHNpGxlaKFJdlxD
+ydi8NmdspZS11My5vWo1ViHe2MPr+8ukYEywVaCge1ey
+-----END CERTIFICATE-----
+
+WellsSecure Public Root Certificate Authority
+=============================================
+-----BEGIN CERTIFICATE-----
+MIIEvTCCA6WgAwIBAgIBATANBgkqhkiG9w0BAQUFADCBhTELMAkGA1UEBhMCVVMxIDAeBgNVBAoM
+F1dlbGxzIEZhcmdvIFdlbGxzU2VjdXJlMRwwGgYDVQQLDBNXZWxscyBGYXJnbyBCYW5rIE5BMTYw
+NAYDVQQDDC1XZWxsc1NlY3VyZSBQdWJsaWMgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwHhcN
+MDcxMjEzMTcwNzU0WhcNMjIxMjE0MDAwNzU0WjCBhTELMAkGA1UEBhMCVVMxIDAeBgNVBAoMF1dl
+bGxzIEZhcmdvIFdlbGxzU2VjdXJlMRwwGgYDVQQLDBNXZWxscyBGYXJnbyBCYW5rIE5BMTYwNAYD
+VQQDDC1XZWxsc1NlY3VyZSBQdWJsaWMgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwggEiMA0G
+CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDub7S9eeKPCCGeOARBJe+rWxxTkqxtnt3CxC5FlAM1
+iGd0V+PfjLindo8796jE2yljDpFoNoqXjopxaAkH5OjUDk/41itMpBb570OYj7OeUt9tkTmPOL13
+i0Nj67eT/DBMHAGTthP796EfvyXhdDcsHqRePGj4S78NuR4uNuip5Kf4D8uCdXw1LSLWwr8L87T8
+bJVhHlfXBIEyg1J55oNjz7fLY4sR4r1e6/aN7ZVyKLSsEmLpSjPmgzKuBXWVvYSV2ypcm44uDLiB
+K0HmOFafSZtsdvqKXfcBeYF8wYNABf5x/Qw/zE5gCQ5lRxAvAcAFP4/4s0HvWkJ+We/SlwxlAgMB
+AAGjggE0MIIBMDAPBgNVHRMBAf8EBTADAQH/MDkGA1UdHwQyMDAwLqAsoCqGKGh0dHA6Ly9jcmwu
+cGtpLndlbGxzZmFyZ28uY29tL3dzcHJjYS5jcmwwDgYDVR0PAQH/BAQDAgHGMB0GA1UdDgQWBBQm
+lRkQ2eihl5H/3BnZtQQ+0nMKajCBsgYDVR0jBIGqMIGngBQmlRkQ2eihl5H/3BnZtQQ+0nMKaqGB
+i6SBiDCBhTELMAkGA1UEBhMCVVMxIDAeBgNVBAoMF1dlbGxzIEZhcmdvIFdlbGxzU2VjdXJlMRww
+GgYDVQQLDBNXZWxscyBGYXJnbyBCYW5rIE5BMTYwNAYDVQQDDC1XZWxsc1NlY3VyZSBQdWJsaWMg
+Um9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHmCAQEwDQYJKoZIhvcNAQEFBQADggEBALkVsUSRzCPI
+K0134/iaeycNzXK7mQDKfGYZUMbVmO2rvwNa5U3lHshPcZeG1eMd/ZDJPHV3V3p9+N701NX3leZ0
+bh08rnyd2wIDBSxxSyU+B+NemvVmFymIGjifz6pBA4SXa5M4esowRBskRDPQ5NHcKDj0E0M1NSlj
+qHyita04pO2t/caaH/+Xc/77szWnk4bGdpEA5qxRFsQnMlzbc9qlk1eOPm01JghZ1edE13YgY+es
+E2fDbbFwRnzVlhE9iW9dqKHrjQrawx0zbKPqZxmamX9LPYNRKh3KL4YMon4QLSvUFpULB6ouFJJJ
+tylv2G0xffX8oRAHh84vWdw+WNs=
+-----END CERTIFICATE-----
+
+COMODO ECC Certification Authority
+==================================
+-----BEGIN CERTIFICATE-----
+MIICiTCCAg+gAwIBAgIQH0evqmIAcFBUTAGem2OZKjAKBggqhkjOPQQDAzCBhTELMAkGA1UEBhMC
+R0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UE
+ChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlvbiBB
+dXRob3JpdHkwHhcNMDgwMzA2MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMCR0Ix
+GzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMR
+Q09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlvbiBBdXRo
+b3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQDR3svdcmCFYX7deSRFtSrYpn1PlILBs5BAH+X
+4QokPB0BBO490o0JlwzgdeT6+3eKKvUDYEs2ixYjFq0JcfRK9ChQtP6IHG4/bC8vCVlbpVsLM5ni
+wz2J+Wos77LTBumjQjBAMB0GA1UdDgQWBBR1cacZSBm8nZ3qQUfflMRId5nTeTAOBgNVHQ8BAf8E
+BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjEA7wNbeqy3eApyt4jf/7VG
+FAkK+qDmfQjGGoe9GKhzvSbKYAydzpmfz1wPMOG+FDHqAjAU9JM8SaczepBGR7NjfRObTrdvGDeA
+U/7dIOA1mjbRxwG55tzd8/8dLDoWV9mSOdY=
+-----END CERTIFICATE-----
+
+IGC/A
+=====
+-----BEGIN CERTIFICATE-----
+MIIEAjCCAuqgAwIBAgIFORFFEJQwDQYJKoZIhvcNAQEFBQAwgYUxCzAJBgNVBAYTAkZSMQ8wDQYD
+VQQIEwZGcmFuY2UxDjAMBgNVBAcTBVBhcmlzMRAwDgYDVQQKEwdQTS9TR0ROMQ4wDAYDVQQLEwVE
+Q1NTSTEOMAwGA1UEAxMFSUdDL0ExIzAhBgkqhkiG9w0BCQEWFGlnY2FAc2dkbi5wbS5nb3V2LmZy
+MB4XDTAyMTIxMzE0MjkyM1oXDTIwMTAxNzE0MjkyMlowgYUxCzAJBgNVBAYTAkZSMQ8wDQYDVQQI
+EwZGcmFuY2UxDjAMBgNVBAcTBVBhcmlzMRAwDgYDVQQKEwdQTS9TR0ROMQ4wDAYDVQQLEwVEQ1NT
+STEOMAwGA1UEAxMFSUdDL0ExIzAhBgkqhkiG9w0BCQEWFGlnY2FAc2dkbi5wbS5nb3V2LmZyMIIB
+IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsh/R0GLFMzvABIaIs9z4iPf930Pfeo2aSVz2
+TqrMHLmh6yeJ8kbpO0px1R2OLc/mratjUMdUC24SyZA2xtgv2pGqaMVy/hcKshd+ebUyiHDKcMCW
+So7kVc0dJ5S/znIq7Fz5cyD+vfcuiWe4u0dzEvfRNWk68gq5rv9GQkaiv6GFGvm/5P9JhfejcIYy
+HF2fYPepraX/z9E0+X1bF8bc1g4oa8Ld8fUzaJ1O/Id8NhLWo4DoQw1VYZTqZDdH6nfK0LJYBcNd
+frGoRpAxVs5wKpayMLh35nnAvSk7/ZR3TL0gzUEl4C7HG7vupARB0l2tEmqKm0f7yd1GQOGdPDPQ
+tQIDAQABo3cwdTAPBgNVHRMBAf8EBTADAQH/MAsGA1UdDwQEAwIBRjAVBgNVHSAEDjAMMAoGCCqB
+egF5AQEBMB0GA1UdDgQWBBSjBS8YYFDCiQrdKyFP/45OqDAxNjAfBgNVHSMEGDAWgBSjBS8YYFDC
+iQrdKyFP/45OqDAxNjANBgkqhkiG9w0BAQUFAAOCAQEABdwm2Pp3FURo/C9mOnTgXeQp/wYHE4RK
+q89toB9RlPhJy3Q2FLwV3duJL92PoF189RLrn544pEfMs5bZvpwlqwN+Mw+VgQ39FuCIvjfwbF3Q
+MZsyK10XZZOYYLxuj7GoPB7ZHPOpJkL5ZB3C55L29B5aqhlSXa/oovdgoPaN8In1buAKBQGVyYsg
+Crpa/JosPL3Dt8ldeCUFP1YUmwza+zpI/pdpXsoQhvdOlgQITeywvl3cO45Pwf2aNjSaTFR+FwNI
+lQgRHAdvhQh+XU3Endv7rs6y0bO4g2wdsrN58dhwmX7wEwLOXt1R0982gaEbeC9xs/FZTEYYKKuF
+0mBWWg==
+-----END CERTIFICATE-----
+
+Security Communication EV RootCA1
+=================================
+-----BEGIN CERTIFICATE-----
+MIIDfTCCAmWgAwIBAgIBADANBgkqhkiG9w0BAQUFADBgMQswCQYDVQQGEwJKUDElMCMGA1UEChMc
+U0VDT00gVHJ1c3QgU3lzdGVtcyBDTy4sTFRELjEqMCgGA1UECxMhU2VjdXJpdHkgQ29tbXVuaWNh
+dGlvbiBFViBSb290Q0ExMB4XDTA3MDYwNjAyMTIzMloXDTM3MDYwNjAyMTIzMlowYDELMAkGA1UE
+BhMCSlAxJTAjBgNVBAoTHFNFQ09NIFRydXN0IFN5c3RlbXMgQ08uLExURC4xKjAoBgNVBAsTIVNl
+Y3VyaXR5IENvbW11bmljYXRpb24gRVYgUm9vdENBMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
+AQoCggEBALx/7FebJOD+nLpCeamIivqA4PUHKUPqjgo0No0c+qe1OXj/l3X3L+SqawSERMqm4miO
+/VVQYg+kcQ7OBzgtQoVQrTyWb4vVog7P3kmJPdZkLjjlHmy1V4qe70gOzXppFodEtZDkBp2uoQSX
+WHnvIEqCa4wiv+wfD+mEce3xDuS4GBPMVjZd0ZoeUWs5bmB2iDQL87PRsJ3KYeJkHcFGB7hj3R4z
+ZbOOCVVSPbW9/wfrrWFVGCypaZhKqkDFMxRldAD5kd6vA0jFQFTcD4SQaCDFkpbcLuUCRarAX1T4
+bepJz11sS6/vmsJWXMY1VkJqMF/Cq/biPT+zyRGPMUzXn0kCAwEAAaNCMEAwHQYDVR0OBBYEFDVK
+9U2vP9eCOKyrcWUXdYydVZPmMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MA0GCSqG
+SIb3DQEBBQUAA4IBAQCoh+ns+EBnXcPBZsdAS5f8hxOQWsTvoMpfi7ent/HWtWS3irO4G8za+6xm
+iEHO6Pzk2x6Ipu0nUBsCMCRGef4Eh3CXQHPRwMFXGZpppSeZq51ihPZRwSzJIxXYKLerJRO1RuGG
+Av8mjMSIkh1W/hln8lXkgKNrnKt34VFxDSDbEJrbvXZ5B3eZKK2aXtqxT0QsNY6llsf9g/BYxnnW
+mHyojf6GPgcWkuF75x3sM3Z+Qi5KhfmRiWiEA4Glm5q+4zfFVKtWOxgtQaQM+ELbmaDgcm+7XeEW
+T1MKZPlO9L9OVL14bIjqv5wTJMJwaaJ/D8g8rQjJsJhAoyrniIPtd490
+-----END CERTIFICATE-----
+
+OISTE WISeKey Global Root GA CA
+===============================
+-----BEGIN CERTIFICATE-----
+MIID8TCCAtmgAwIBAgIQQT1yx/RrH4FDffHSKFTfmjANBgkqhkiG9w0BAQUFADCBijELMAkGA1UE
+BhMCQ0gxEDAOBgNVBAoTB1dJU2VLZXkxGzAZBgNVBAsTEkNvcHlyaWdodCAoYykgMjAwNTEiMCAG
+A1UECxMZT0lTVEUgRm91bmRhdGlvbiBFbmRvcnNlZDEoMCYGA1UEAxMfT0lTVEUgV0lTZUtleSBH
+bG9iYWwgUm9vdCBHQSBDQTAeFw0wNTEyMTExNjAzNDRaFw0zNzEyMTExNjA5NTFaMIGKMQswCQYD
+VQQGEwJDSDEQMA4GA1UEChMHV0lTZUtleTEbMBkGA1UECxMSQ29weXJpZ2h0IChjKSAyMDA1MSIw
+IAYDVQQLExlPSVNURSBGb3VuZGF0aW9uIEVuZG9yc2VkMSgwJgYDVQQDEx9PSVNURSBXSVNlS2V5
+IEdsb2JhbCBSb290IEdBIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAy0+zAJs9
+Nt350UlqaxBJH+zYK7LG+DKBKUOVTJoZIyEVRd7jyBxRVVuuk+g3/ytr6dTqvirdqFEr12bDYVxg
+Asj1znJ7O7jyTmUIms2kahnBAbtzptf2w93NvKSLtZlhuAGio9RN1AU9ka34tAhxZK9w8RxrfvbD
+d50kc3vkDIzh2TbhmYsFmQvtRTEJysIA2/dyoJaqlYfQjse2YXMNdmaM3Bu0Y6Kff5MTMPGhJ9vZ
+/yxViJGg4E8HsChWjBgbl0SOid3gF27nKu+POQoxhILYQBRJLnpB5Kf+42TMwVlxSywhp1t94B3R
+LoGbw9ho972WG6xwsRYUC9tguSYBBQIDAQABo1EwTzALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUw
+AwEB/zAdBgNVHQ4EFgQUswN+rja8sHnR3JQmthG+IbJphpQwEAYJKwYBBAGCNxUBBAMCAQAwDQYJ
+KoZIhvcNAQEFBQADggEBAEuh/wuHbrP5wUOxSPMowB0uyQlB+pQAHKSkq0lPjz0e701vvbyk9vIm
+MMkQyh2I+3QZH4VFvbBsUfk2ftv1TDI6QU9bR8/oCy22xBmddMVHxjtqD6wU2zz0c5ypBd8A3HR4
++vg1YFkCExh8vPtNsCBtQ7tgMHpnM1zFmdH4LTlSc/uMqpclXHLZCB6rTjzjgTGfA6b7wP4piFXa
+hNVQA7bihKOmNqoROgHhGEvWRGizPflTdISzRpFGlgC3gCy24eMQ4tui5yiPAZZiFj4A4xylNoEY
+okxSdsARo27mHbrjWr42U8U+dY+GaSlYU7Wcu2+fXMUY7N0v4ZjJ/L7fCg0=
+-----END CERTIFICATE-----
+
+Microsec e-Szigno Root CA
+=========================
+-----BEGIN CERTIFICATE-----
+MIIHqDCCBpCgAwIBAgIRAMy4579OKRr9otxmpRwsDxEwDQYJKoZIhvcNAQEFBQAwcjELMAkGA1UE
+BhMCSFUxETAPBgNVBAcTCEJ1ZGFwZXN0MRYwFAYDVQQKEw1NaWNyb3NlYyBMdGQuMRQwEgYDVQQL
+EwtlLVN6aWdubyBDQTEiMCAGA1UEAxMZTWljcm9zZWMgZS1Temlnbm8gUm9vdCBDQTAeFw0wNTA0
+MDYxMjI4NDRaFw0xNzA0MDYxMjI4NDRaMHIxCzAJBgNVBAYTAkhVMREwDwYDVQQHEwhCdWRhcGVz
+dDEWMBQGA1UEChMNTWljcm9zZWMgTHRkLjEUMBIGA1UECxMLZS1Temlnbm8gQ0ExIjAgBgNVBAMT
+GU1pY3Jvc2VjIGUtU3ppZ25vIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
+AQDtyADVgXvNOABHzNuEwSFpLHSQDCHZU4ftPkNEU6+r+ICbPHiN1I2uuO/TEdyB5s87lozWbxXG
+d36hL+BfkrYn13aaHUM86tnsL+4582pnS4uCzyL4ZVX+LMsvfUh6PXX5qqAnu3jCBspRwn5mS6/N
+oqdNAoI/gqyFxuEPkEeZlApxcpMqyabAvjxWTHOSJ/FrtfX9/DAFYJLG65Z+AZHCabEeHXtTRbjc
+QR/Ji3HWVBTji1R4P770Yjtb9aPs1ZJ04nQw7wHb4dSrmZsqa/i9phyGI0Jf7Enemotb9HI6QMVJ
+PqW+jqpx62z69Rrkav17fVVA71hu5tnVvCSrwe+3AgMBAAGjggQ3MIIEMzBnBggrBgEFBQcBAQRb
+MFkwKAYIKwYBBQUHMAGGHGh0dHBzOi8vcmNhLmUtc3ppZ25vLmh1L29jc3AwLQYIKwYBBQUHMAKG
+IWh0dHA6Ly93d3cuZS1zemlnbm8uaHUvUm9vdENBLmNydDAPBgNVHRMBAf8EBTADAQH/MIIBcwYD
+VR0gBIIBajCCAWYwggFiBgwrBgEEAYGoGAIBAQEwggFQMCgGCCsGAQUFBwIBFhxodHRwOi8vd3d3
+LmUtc3ppZ25vLmh1L1NaU1ovMIIBIgYIKwYBBQUHAgIwggEUHoIBEABBACAAdABhAG4A+gBzAO0A
+dAB2AOEAbgB5ACAA6QByAHQAZQBsAG0AZQB6AOkAcwDpAGgAZQB6ACAA6QBzACAAZQBsAGYAbwBn
+AGEAZADhAHMA4QBoAG8AegAgAGEAIABTAHoAbwBsAGcA4QBsAHQAYQB0APMAIABTAHoAbwBsAGcA
+4QBsAHQAYQB0AOEAcwBpACAAUwB6AGEAYgDhAGwAeQB6AGEAdABhACAAcwB6AGUAcgBpAG4AdAAg
+AGsAZQBsAGwAIABlAGwAagDhAHIAbgBpADoAIABoAHQAdABwADoALwAvAHcAdwB3AC4AZQAtAHMA
+egBpAGcAbgBvAC4AaAB1AC8AUwBaAFMAWgAvMIHIBgNVHR8EgcAwgb0wgbqggbeggbSGIWh0dHA6
+Ly93d3cuZS1zemlnbm8uaHUvUm9vdENBLmNybIaBjmxkYXA6Ly9sZGFwLmUtc3ppZ25vLmh1L0NO
+PU1pY3Jvc2VjJTIwZS1Temlnbm8lMjBSb290JTIwQ0EsT1U9ZS1Temlnbm8lMjBDQSxPPU1pY3Jv
+c2VjJTIwTHRkLixMPUJ1ZGFwZXN0LEM9SFU/Y2VydGlmaWNhdGVSZXZvY2F0aW9uTGlzdDtiaW5h
+cnkwDgYDVR0PAQH/BAQDAgEGMIGWBgNVHREEgY4wgYuBEGluZm9AZS1zemlnbm8uaHWkdzB1MSMw
+IQYDVQQDDBpNaWNyb3NlYyBlLVN6aWduw7MgUm9vdCBDQTEWMBQGA1UECwwNZS1TemlnbsOzIEhT
+WjEWMBQGA1UEChMNTWljcm9zZWMgS2Z0LjERMA8GA1UEBxMIQnVkYXBlc3QxCzAJBgNVBAYTAkhV
+MIGsBgNVHSMEgaQwgaGAFMegSXUWYYTbMUuE0vE3QJDvTtz3oXakdDByMQswCQYDVQQGEwJIVTER
+MA8GA1UEBxMIQnVkYXBlc3QxFjAUBgNVBAoTDU1pY3Jvc2VjIEx0ZC4xFDASBgNVBAsTC2UtU3pp
+Z25vIENBMSIwIAYDVQQDExlNaWNyb3NlYyBlLVN6aWdubyBSb290IENBghEAzLjnv04pGv2i3Gal
+HCwPETAdBgNVHQ4EFgQUx6BJdRZhhNsxS4TS8TdAkO9O3PcwDQYJKoZIhvcNAQEFBQADggEBANMT
+nGZjWS7KXHAM/IO8VbH0jgdsZifOwTsgqRy7RlRw7lrMoHfqaEQn6/Ip3Xep1fvj1KcExJW4C+FE
+aGAHQzAxQmHl7tnlJNUb3+FKG6qfx1/4ehHqE5MAyopYse7tDk2016g2JnzgOsHVV4Lxdbb9iV/a
+86g4nzUGCM4ilb7N1fy+W955a9x6qWVmvrElWl/tftOsRm1M9DKHtCAE4Gx4sHfRhUZLphK3dehK
+yVZs15KrnfVJONJPU+NVkBHbmJbGSfI+9J8b4PeI3CVimUTYc78/MPMMNz7UwiiAc7EBt51alhQB
+S6kRnSlqLtBdgcDPsiBDxwPgN05dCtxZICU=
+-----END CERTIFICATE-----
+
+Certigna
+========
+-----BEGIN CERTIFICATE-----
+MIIDqDCCApCgAwIBAgIJAP7c4wEPyUj/MA0GCSqGSIb3DQEBBQUAMDQxCzAJBgNVBAYTAkZSMRIw
+EAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25hMB4XDTA3MDYyOTE1MTMwNVoXDTI3
+MDYyOTE1MTMwNVowNDELMAkGA1UEBhMCRlIxEjAQBgNVBAoMCURoaW15b3RpczERMA8GA1UEAwwI
+Q2VydGlnbmEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDIaPHJ1tazNHUmgh7stL7q
+XOEm7RFHYeGifBZ4QCHkYJ5ayGPhxLGWkv8YbWkj4Sti993iNi+RB7lIzw7sebYs5zRLcAglozyH
+GxnygQcPOJAZ0xH+hrTy0V4eHpbNgGzOOzGTtvKg0KmVEn2lmsxryIRWijOp5yIVUxbwzBfsV1/p
+ogqYCd7jX5xv3EjjhQsVWqa6n6xI4wmy9/Qy3l40vhx4XUJbzg4ij02Q130yGLMLLGq/jj8UEYkg
+DncUtT2UCIf3JR7VsmAA7G8qKCVuKj4YYxclPz5EIBb2JsglrgVKtOdjLPOMFlN+XPsRGgjBRmKf
+Irjxwo1p3Po6WAbfAgMBAAGjgbwwgbkwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUGu3+QTmQ
+tCRZvgHyUtVF9lo53BEwZAYDVR0jBF0wW4AUGu3+QTmQtCRZvgHyUtVF9lo53BGhOKQ2MDQxCzAJ
+BgNVBAYTAkZSMRIwEAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25hggkA/tzjAQ/J
+SP8wDgYDVR0PAQH/BAQDAgEGMBEGCWCGSAGG+EIBAQQEAwIABzANBgkqhkiG9w0BAQUFAAOCAQEA
+hQMeknH2Qq/ho2Ge6/PAD/Kl1NqV5ta+aDY9fm4fTIrv0Q8hbV6lUmPOEvjvKtpv6zf+EwLHyzs+
+ImvaYS5/1HI93TDhHkxAGYwP15zRgzB7mFncfca5DClMoTOi62c6ZYTTluLtdkVwj7Ur3vkj1klu
+PBS1xp81HlDQwY9qcEQCYsuuHWhBp6pX6FOqB9IG9tUUBguRA3UsbHK1YZWaDYu5Def131TN3ubY
+1gkIl2PlwS6wt0QmwCbAr1UwnjvVNioZBPRcHv/PLLf/0P2HQBHVESO7SMAhqaQoLf0V+LBOK/Qw
+WyH8EZE0vkHve52Xdf+XlcCWWC/qu0bXu+TZLg==
+-----END CERTIFICATE-----
+
+Deutsche Telekom Root CA 2
+==========================
+-----BEGIN CERTIFICATE-----
+MIIDnzCCAoegAwIBAgIBJjANBgkqhkiG9w0BAQUFADBxMQswCQYDVQQGEwJERTEcMBoGA1UEChMT
+RGV1dHNjaGUgVGVsZWtvbSBBRzEfMB0GA1UECxMWVC1UZWxlU2VjIFRydXN0IENlbnRlcjEjMCEG
+A1UEAxMaRGV1dHNjaGUgVGVsZWtvbSBSb290IENBIDIwHhcNOTkwNzA5MTIxMTAwWhcNMTkwNzA5
+MjM1OTAwWjBxMQswCQYDVQQGEwJERTEcMBoGA1UEChMTRGV1dHNjaGUgVGVsZWtvbSBBRzEfMB0G
+A1UECxMWVC1UZWxlU2VjIFRydXN0IENlbnRlcjEjMCEGA1UEAxMaRGV1dHNjaGUgVGVsZWtvbSBS
+b290IENBIDIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCrC6M14IspFLEUha88EOQ5
+bzVdSq7d6mGNlUn0b2SjGmBmpKlAIoTZ1KXleJMOaAGtuU1cOs7TuKhCQN/Po7qCWWqSG6wcmtoI
+KyUn+WkjR/Hg6yx6m/UTAtB+NHzCnjwAWav12gz1MjwrrFDa1sPeg5TKqAyZMg4ISFZbavva4VhY
+AUlfckE8FQYBjl2tqriTtM2e66foai1SNNs671x1Udrb8zH57nGYMsRUFUQM+ZtV7a3fGAigo4aK
+Se5TBY8ZTNXeWHmb0mocQqvF1afPaA+W5OFhmHZhyJF81j4A4pFQh+GdCuatl9Idxjp9y7zaAzTV
+jlsB9WoHtxa2bkp/AgMBAAGjQjBAMB0GA1UdDgQWBBQxw3kbuvVT1xfgiXotF2wKsyudMzAPBgNV
+HRMECDAGAQH/AgEFMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOCAQEAlGRZrTlk5ynr
+E/5aw4sTV8gEJPB0d8Bg42f76Ymmg7+Wgnxu1MM9756AbrsptJh6sTtU6zkXR34ajgv8HzFZMQSy
+zhfzLMdiNlXiItiJVbSYSKpk+tYcNthEeFpaIzpXl/V6ME+un2pMSyuOoAPjPuCp1NJ70rOo4nI8
+rZ7/gFnkm0W09juwzTkZmDLl6iFhkOQxIY40sfcvNUqFENrnijchvllj4PKFiDFT1FQUhXB59C4G
+dyd1Lx+4ivn+xbrYNuSD7Odlt79jWvNGr4GUN9RBjNYj1h7P9WgbRGOiWrqnNVmh5XAFmw4jV5mU
+Cm26OWMohpLzGITY+9HPBVZkVw==
+-----END CERTIFICATE-----
+
+Cybertrust Global Root
+======================
+-----BEGIN CERTIFICATE-----
+MIIDoTCCAomgAwIBAgILBAAAAAABD4WqLUgwDQYJKoZIhvcNAQEFBQAwOzEYMBYGA1UEChMPQ3li
+ZXJ0cnVzdCwgSW5jMR8wHQYDVQQDExZDeWJlcnRydXN0IEdsb2JhbCBSb290MB4XDTA2MTIxNTA4
+MDAwMFoXDTIxMTIxNTA4MDAwMFowOzEYMBYGA1UEChMPQ3liZXJ0cnVzdCwgSW5jMR8wHQYDVQQD
+ExZDeWJlcnRydXN0IEdsb2JhbCBSb290MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA
++Mi8vRRQZhP/8NN57CPytxrHjoXxEnOmGaoQ25yiZXRadz5RfVb23CO21O1fWLE3TdVJDm71aofW
+0ozSJ8bi/zafmGWgE07GKmSb1ZASzxQG9Dvj1Ci+6A74q05IlG2OlTEQXO2iLb3VOm2yHLtgwEZL
+AfVJrn5GitB0jaEMAs7u/OePuGtm839EAL9mJRQr3RAwHQeWP032a7iPt3sMpTjr3kfb1V05/Iin
+89cqdPHoWqI7n1C6poxFNcJQZZXcY4Lv3b93TZxiyWNzFtApD0mpSPCzqrdsxacwOUBdrsTiXSZT
+8M4cIwhhqJQZugRiQOwfOHB3EgZxpzAYXSUnpQIDAQABo4GlMIGiMA4GA1UdDwEB/wQEAwIBBjAP
+BgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBS2CHsNesysIEyGVjJez6tuhS1wVzA/BgNVHR8EODA2
+MDSgMqAwhi5odHRwOi8vd3d3Mi5wdWJsaWMtdHJ1c3QuY29tL2NybC9jdC9jdHJvb3QuY3JsMB8G
+A1UdIwQYMBaAFLYIew16zKwgTIZWMl7Pq26FLXBXMA0GCSqGSIb3DQEBBQUAA4IBAQBW7wojoFRO
+lZfJ+InaRcHUowAl9B8Tq7ejhVhpwjCt2BWKLePJzYFa+HMjWqd8BfP9IjsO0QbE2zZMcwSO5bAi
+5MXzLqXZI+O4Tkogp24CJJ8iYGd7ix1yCcUxXOl5n4BHPa2hCwcUPUf/A2kaDAtE52Mlp3+yybh2
+hO0j9n0Hq0V+09+zv+mKts2oomcrUtW3ZfA5TGOgkXmTUg9U3YO7n9GPp1Nzw8v/MOx8BLjYRB+T
+X3EJIrduPuocA06dGiBh+4E37F78CkWr1+cXVdCg6mCbpvbjjFspwgZgFJ0tl0ypkxWdYcQBX0jW
+WL1WMRJOEcgh4LMRkWXbtKaIOM5V
+-----END CERTIFICATE-----
+
+ePKI Root Certification Authority
+=================================
+-----BEGIN CERTIFICATE-----
+MIIFsDCCA5igAwIBAgIQFci9ZUdcr7iXAF7kBtK8nTANBgkqhkiG9w0BAQUFADBeMQswCQYDVQQG
+EwJUVzEjMCEGA1UECgwaQ2h1bmdod2EgVGVsZWNvbSBDby4sIEx0ZC4xKjAoBgNVBAsMIWVQS0kg
+Um9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNDEyMjAwMjMxMjdaFw0zNDEyMjAwMjMx
+MjdaMF4xCzAJBgNVBAYTAlRXMSMwIQYDVQQKDBpDaHVuZ2h3YSBUZWxlY29tIENvLiwgTHRkLjEq
+MCgGA1UECwwhZVBLSSBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIICIjANBgkqhkiG9w0B
+AQEFAAOCAg8AMIICCgKCAgEA4SUP7o3biDN1Z82tH306Tm2d0y8U82N0ywEhajfqhFAHSyZbCUNs
+IZ5qyNUD9WBpj8zwIuQf5/dqIjG3LBXy4P4AakP/h2XGtRrBp0xtInAhijHyl3SJCRImHJ7K2RKi
+lTza6We/CKBk49ZCt0Xvl/T29de1ShUCWH2YWEtgvM3XDZoTM1PRYfl61dd4s5oz9wCGzh1NlDiv
+qOx4UXCKXBCDUSH3ET00hl7lSM2XgYI1TBnsZfZrxQWh7kcT1rMhJ5QQCtkkO7q+RBNGMD+XPNjX
+12ruOzjjK9SXDrkb5wdJfzcq+Xd4z1TtW0ado4AOkUPB1ltfFLqfpo0kR0BZv3I4sjZsN/+Z0V0O
+WQqraffAsgRFelQArr5T9rXn4fg8ozHSqf4hUmTFpmfwdQcGlBSBVcYn5AGPF8Fqcde+S/uUWH1+
+ETOxQvdibBjWzwloPn9s9h6PYq2lY9sJpx8iQkEeb5mKPtf5P0B6ebClAZLSnT0IFaUQAS2zMnao
+lQ2zepr7BxB4EW/hj8e6DyUadCrlHJhBmd8hh+iVBmoKs2pHdmX2Os+PYhcZewoozRrSgx4hxyy/
+vv9haLdnG7t4TY3OZ+XkwY63I2binZB1NJipNiuKmpS5nezMirH4JYlcWrYvjB9teSSnUmjDhDXi
+Zo1jDiVN1Rmy5nk3pyKdVDECAwEAAaNqMGgwHQYDVR0OBBYEFB4M97Zn8uGSJglFwFU5Lnc/Qkqi
+MAwGA1UdEwQFMAMBAf8wOQYEZyoHAAQxMC8wLQIBADAJBgUrDgMCGgUAMAcGBWcqAwAABBRFsMLH
+ClZ87lt4DJX5GFPBphzYEDANBgkqhkiG9w0BAQUFAAOCAgEACbODU1kBPpVJufGBuvl2ICO1J2B0
+1GqZNF5sAFPZn/KmsSQHRGoqxqWOeBLoR9lYGxMqXnmbnwoqZ6YlPwZpVnPDimZI+ymBV3QGypzq
+KOg4ZyYr8dW1P2WT+DZdjo2NQCCHGervJ8A9tDkPJXtoUHRVnAxZfVo9QZQlUgjgRywVMRnVvwdV
+xrsStZf0X4OFunHB2WyBEXYKCrC/gpf36j36+uwtqSiUO1bd0lEursC9CBWMd1I0ltabrNMdjmEP
+NXubrjlpC2JgQCA2j6/7Nu4tCEoduL+bXPjqpRugc6bY+G7gMwRfaKonh+3ZwZCc7b3jajWvY9+r
+GNm65ulK6lCKD2GTHuItGeIwlDWSXQ62B68ZgI9HkFFLLk3dheLSClIKF5r8GrBQAuUBo2M3IUxE
+xJtRmREOc5wGj1QupyheRDmHVi03vYVElOEMSyycw5KFNGHLD7ibSkNS/jQ6fbjpKdx2qcgw+BRx
+gMYeNkh0IkFch4LoGHGLQYlE535YW6i4jRPpp2zDR+2zGp1iro2C6pSe3VkQw63d4k3jMdXH7Ojy
+sP6SHhYKGvzZ8/gntsm+HbRsZJB/9OTEW9c3rkIO3aQab3yIVMUWbuF6aC74Or8NpDyJO3inTmOD
+BCEIZ43ygknQW/2xzQ+DhNQ+IIX3Sj0rnP0qCglN6oH4EZw=
+-----END CERTIFICATE-----
+
+T\xc3\x9c\x42\xC4\xB0TAK UEKAE K\xC3\xB6k Sertifika Hizmet Sa\xC4\x9Flay\xc4\xb1\x63\xc4\xb1s\xc4\xb1 - S\xC3\xBCr\xC3\xBCm 3
+=============================================================================================================================
+-----BEGIN CERTIFICATE-----
+MIIFFzCCA/+gAwIBAgIBETANBgkqhkiG9w0BAQUFADCCASsxCzAJBgNVBAYTAlRSMRgwFgYDVQQH
+DA9HZWJ6ZSAtIEtvY2FlbGkxRzBFBgNVBAoMPlTDvHJraXllIEJpbGltc2VsIHZlIFRla25vbG9q
+aWsgQXJhxZ90xLFybWEgS3VydW11IC0gVMOcQsSwVEFLMUgwRgYDVQQLDD9VbHVzYWwgRWxla3Ry
+b25payB2ZSBLcmlwdG9sb2ppIEFyYcWfdMSxcm1hIEVuc3RpdMO8c8O8IC0gVUVLQUUxIzAhBgNV
+BAsMGkthbXUgU2VydGlmaWthc3lvbiBNZXJrZXppMUowSAYDVQQDDEFUw5xCxLBUQUsgVUVLQUUg
+S8O2ayBTZXJ0aWZpa2EgSGl6bWV0IFNhxJ9sYXnEsWPEsXPEsSAtIFPDvHLDvG0gMzAeFw0wNzA4
+MjQxMTM3MDdaFw0xNzA4MjExMTM3MDdaMIIBKzELMAkGA1UEBhMCVFIxGDAWBgNVBAcMD0dlYnpl
+IC0gS29jYWVsaTFHMEUGA1UECgw+VMO8cmtpeWUgQmlsaW1zZWwgdmUgVGVrbm9sb2ppayBBcmHF
+n3TEsXJtYSBLdXJ1bXUgLSBUw5xCxLBUQUsxSDBGBgNVBAsMP1VsdXNhbCBFbGVrdHJvbmlrIHZl
+IEtyaXB0b2xvamkgQXJhxZ90xLFybWEgRW5zdGl0w7xzw7wgLSBVRUtBRTEjMCEGA1UECwwaS2Ft
+dSBTZXJ0aWZpa2FzeW9uIE1lcmtlemkxSjBIBgNVBAMMQVTDnELEsFRBSyBVRUtBRSBLw7ZrIFNl
+cnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxIC0gU8O8csO8bSAzMIIBIjANBgkqhkiG9w0B
+AQEFAAOCAQ8AMIIBCgKCAQEAim1L/xCIOsP2fpTo6iBkcK4hgb46ezzb8R1Sf1n68yJMlaCQvEhO
+Eav7t7WNeoMojCZG2E6VQIdhn8WebYGHV2yKO7Rm6sxA/OOqbLLLAdsyv9Lrhc+hDVXDWzhXcLh1
+xnnRFDDtG1hba+818qEhTsXOfJlfbLm4IpNQp81McGq+agV/E5wrHur+R84EpW+sky58K5+eeROR
+6Oqeyjh1jmKwlZMq5d/pXpduIF9fhHpEORlAHLpVK/swsoHvhOPc7Jg4OQOFCKlUAwUp8MmPi+oL
+hmUZEdPpCSPeaJMDyTYcIW7OjGbxmTDY17PDHfiBLqi9ggtm/oLL4eAagsNAgQIDAQABo0IwQDAd
+BgNVHQ4EFgQUvYiHyY/2pAoLquvF/pEjnatKijIwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQF
+MAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAB18+kmPNOm3JpIWmgV050vQbTlswyb2zrgxvMTfvCr4
+N5EY3ATIZJkrGG2AA1nJrvhY0D7twyOfaTyGOBye79oneNGEN3GKPEs5z35FBtYt2IpNeBLWrcLT
+y9LQQfMmNkqblWwM7uXRQydmwYj3erMgbOqwaSvHIOgMA8RBBZniP+Rr+KCGgceExh/VS4ESshYh
+LBOhgLJeDEoTniDYYkCrkOpkSi+sDQESeUWoL4cZaMjihccwsnX5OD+ywJO0a+IDRM5noN+J1q2M
+dqMTw5RhK2vZbMEHCiIHhWyFJEapvj+LeISCfiQMnf2BN+MlqO02TpUsyZyQ2uypQjyttgI=
+-----END CERTIFICATE-----
+
+Buypass Class 2 CA 1
+====================
+-----BEGIN CERTIFICATE-----
+MIIDUzCCAjugAwIBAgIBATANBgkqhkiG9w0BAQUFADBLMQswCQYDVQQGEwJOTzEdMBsGA1UECgwU
+QnV5cGFzcyBBUy05ODMxNjMzMjcxHTAbBgNVBAMMFEJ1eXBhc3MgQ2xhc3MgMiBDQSAxMB4XDTA2
+MTAxMzEwMjUwOVoXDTE2MTAxMzEwMjUwOVowSzELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1eXBh
+c3MgQVMtOTgzMTYzMzI3MR0wGwYDVQQDDBRCdXlwYXNzIENsYXNzIDIgQ0EgMTCCASIwDQYJKoZI
+hvcNAQEBBQADggEPADCCAQoCggEBAIs8B0XY9t/mx8q6jUPFR42wWsE425KEHK8T1A9vNkYgxC7M
+cXA0ojTTNy7Y3Tp3L8DrKehc0rWpkTSHIln+zNvnma+WwajHQN2lFYxuyHyXA8vmIPLXl18xoS83
+0r7uvqmtqEyeIWZDO6i88wmjONVZJMHCR3axiFyCO7srpgTXjAePzdVBHfCuuCkslFJgNJQ72uA4
+0Z0zPhX0kzLFANq1KWYOOngPIVJfAuWSeyXTkh4vFZ2B5J2O6O+JzhRMVB0cgRJNcKi+EAUXfh/R
+uFdV7c27UsKwHnjCTTZoy1YmwVLBvXb3WNVyfh9EdrsAiR0WnVE1703CVu9r4Iw7DekCAwEAAaNC
+MEAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUP42aWYv8e3uco684sDntkHGA1sgwDgYDVR0P
+AQH/BAQDAgEGMA0GCSqGSIb3DQEBBQUAA4IBAQAVGn4TirnoB6NLJzKyQJHyIdFkhb5jatLPgcIV
+1Xp+DCmsNx4cfHZSldq1fyOhKXdlyTKdqC5Wq2B2zha0jX94wNWZUYN/Xtm+DKhQ7SLHrQVMdvvt
+7h5HZPb3J31cKA9FxVxiXqaakZG3Uxcu3K1gnZZkOb1naLKuBctN518fV4bVIJwo+28TOPX2EZL2
+fZleHwzoq0QkKXJAPTZSr4xYkHPB7GEseaHsh7U/2k3ZIQAw3pDaDtMaSKk+hQsUi4y8QZ5q9w5w
+wDX3OaJdZtB7WZ+oRxKaJyOkLY4ng5IgodcVf/EuGO70SH8vf/GhGLWhC5SgYiAynB321O+/TIho
+-----END CERTIFICATE-----
+
+EBG Elektronik Sertifika Hizmet Sa\xC4\x9Flay\xc4\xb1\x63\xc4\xb1s\xc4\xb1
+==========================================================================
+-----BEGIN CERTIFICATE-----
+MIIF5zCCA8+gAwIBAgIITK9zQhyOdAIwDQYJKoZIhvcNAQEFBQAwgYAxODA2BgNVBAMML0VCRyBF
+bGVrdHJvbmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxMTcwNQYDVQQKDC5FQkcg
+QmlsacWfaW0gVGVrbm9sb2ppbGVyaSB2ZSBIaXptZXRsZXJpIEEuxZ4uMQswCQYDVQQGEwJUUjAe
+Fw0wNjA4MTcwMDIxMDlaFw0xNjA4MTQwMDMxMDlaMIGAMTgwNgYDVQQDDC9FQkcgRWxla3Ryb25p
+ayBTZXJ0aWZpa2EgSGl6bWV0IFNhxJ9sYXnEsWPEsXPEsTE3MDUGA1UECgwuRUJHIEJpbGnFn2lt
+IFRla25vbG9qaWxlcmkgdmUgSGl6bWV0bGVyaSBBLsWeLjELMAkGA1UEBhMCVFIwggIiMA0GCSqG
+SIb3DQEBAQUAA4ICDwAwggIKAoICAQDuoIRh0DpqZhAy2DE4f6en5f2h4fuXd7hxlugTlkaDT7by
+X3JWbhNgpQGR4lvFzVcfd2NR/y8927k/qqk153nQ9dAktiHq6yOU/im/+4mRDGSaBUorzAzu8T2b
+gmmkTPiab+ci2hC6X5L8GCcKqKpE+i4stPtGmggDg3KriORqcsnlZR9uKg+ds+g75AxuetpX/dfr
+eYteIAbTdgtsApWjluTLdlHRKJ2hGvxEok3MenaoDT2/F08iiFD9rrbskFBKW5+VQarKD7JK/oCZ
+TqNGFav4c0JqwmZ2sQomFd2TkuzbqV9UIlKRcF0T6kjsbgNs2d1s/OsNA/+mgxKb8amTD8UmTDGy
+Y5lhcucqZJnSuOl14nypqZoaqsNW2xCaPINStnuWt6yHd6i58mcLlEOzrz5z+kI2sSXFCjEmN1Zn
+uqMLfdb3ic1nobc6HmZP9qBVFCVMLDMNpkGMvQQxahByCp0OLna9XvNRiYuoP1Vzv9s6xiQFlpJI
+qkuNKgPlV5EQ9GooFW5Hd4RcUXSfGenmHmMWOeMRFeNYGkS9y8RsZteEBt8w9DeiQyJ50hBs37vm
+ExH8nYQKE3vwO9D8owrXieqWfo1IhR5kX9tUoqzVegJ5a9KK8GfaZXINFHDk6Y54jzJ0fFfy1tb0
+Nokb+Clsi7n2l9GkLqq+CxnCRelwXQIDAJ3Zo2MwYTAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB
+/wQEAwIBBjAdBgNVHQ4EFgQU587GT/wWZ5b6SqMHwQSny2re2kcwHwYDVR0jBBgwFoAU587GT/wW
+Z5b6SqMHwQSny2re2kcwDQYJKoZIhvcNAQEFBQADggIBAJuYml2+8ygjdsZs93/mQJ7ANtyVDR2t
+FcU22NU57/IeIl6zgrRdu0waypIN30ckHrMk2pGI6YNw3ZPX6bqz3xZaPt7gyPvT/Wwp+BVGoGgm
+zJNSroIBk5DKd8pNSe/iWtkqvTDOTLKBtjDOWU/aWR1qeqRFsIImgYZ29fUQALjuswnoT4cCB64k
+XPBfrAowzIpAoHMEwfuJJPaaHFy3PApnNgUIMbOv2AFoKuB4j3TeuFGkjGwgPaL7s9QJ/XvCgKqT
+bCmYIai7FvOpEl90tYeY8pUm3zTvilORiF0alKM/fCL414i6poyWqD1SNGKfAB5UVUJnxk1Gj7sU
+RT0KlhaOEKGXmdXTMIXM3rRyt7yKPBgpaP3ccQfuJDlq+u2lrDgv+R4QDgZxGhBM/nV+/x5XOULK
+1+EVoVZVWRvRo68R2E7DpSvvkL/A7IITW43WciyTTo9qKd+FPNMN4KIYEsxVL0e3p5sC/kH2iExt
+2qkBR4NkJ2IQgtYSe14DHzSpyZH+r11thie3I6p1GMog57AP14kOpmciY/SDQSsGS7tY1dHXt7kQ
+Y9iJSrSq3RZj9W6+YKH47ejWkE8axsWgKdOnIaj1Wjz3x0miIZpKlVIglnKaZsv30oZDfCK+lvm9
+AahH3eU7QPl1K5srRmSGjR70j/sHd9DqSaIcjVIUpgqT
+-----END CERTIFICATE-----
+
+certSIGN ROOT CA
+================
+-----BEGIN CERTIFICATE-----
+MIIDODCCAiCgAwIBAgIGIAYFFnACMA0GCSqGSIb3DQEBBQUAMDsxCzAJBgNVBAYTAlJPMREwDwYD
+VQQKEwhjZXJ0U0lHTjEZMBcGA1UECxMQY2VydFNJR04gUk9PVCBDQTAeFw0wNjA3MDQxNzIwMDRa
+Fw0zMTA3MDQxNzIwMDRaMDsxCzAJBgNVBAYTAlJPMREwDwYDVQQKEwhjZXJ0U0lHTjEZMBcGA1UE
+CxMQY2VydFNJR04gUk9PVCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALczuX7I
+JUqOtdu0KBuqV5Do0SLTZLrTk+jUrIZhQGpgV2hUhE28alQCBf/fm5oqrl0Hj0rDKH/v+yv6efHH
+rfAQUySQi2bJqIirr1qjAOm+ukbuW3N7LBeCgV5iLKECZbO9xSsAfsT8AzNXDe3i+s5dRdY4zTW2
+ssHQnIFKquSyAVwdj1+ZxLGt24gh65AIgoDzMKND5pCCrlUoSe1b16kQOA7+j0xbm0bqQfWwCHTD
+0IgztnzXdN/chNFDDnU5oSVAKOp4yw4sLjmdjItuFhwvJoIQ4uNllAoEwF73XVv4EOLQunpL+943
+AAAaWyjj0pxzPjKHmKHJUS/X3qwzs08CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8B
+Af8EBAMCAcYwHQYDVR0OBBYEFOCMm9slSbPxfIbWskKHC9BroNnkMA0GCSqGSIb3DQEBBQUAA4IB
+AQA+0hyJLjX8+HXd5n9liPRyTMks1zJO890ZeUe9jjtbkw9QSSQTaxQGcu8J06Gh40CEyecYMnQ8
+SG4Pn0vU9x7Tk4ZkVJdjclDVVc/6IJMCopvDI5NOFlV2oHB5bc0hH88vLbwZ44gx+FkagQnIl6Z0
+x2DEW8xXjrJ1/RsCCdtZb3KTafcxQdaIOL+Hsr0Wefmq5L6IJd1hJyMctTEHBDa0GpC9oHRxUIlt
+vBTjD4au8as+x6AJzKNI0eDbZOeStc+vckNwi/nDhDwTqn6Sm1dTk/pwwpEOMfmbZ13pljheX7Nz
+TogVZ96edhBiIL5VaZVDADlN9u6wWk5JRFRYX0KD
+-----END CERTIFICATE-----
+
+CNNIC ROOT
+==========
+-----BEGIN CERTIFICATE-----
+MIIDVTCCAj2gAwIBAgIESTMAATANBgkqhkiG9w0BAQUFADAyMQswCQYDVQQGEwJDTjEOMAwGA1UE
+ChMFQ05OSUMxEzARBgNVBAMTCkNOTklDIFJPT1QwHhcNMDcwNDE2MDcwOTE0WhcNMjcwNDE2MDcw
+OTE0WjAyMQswCQYDVQQGEwJDTjEOMAwGA1UEChMFQ05OSUMxEzARBgNVBAMTCkNOTklDIFJPT1Qw
+ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDTNfc/c3et6FtzF8LRb+1VvG7q6KR5smzD
+o+/hn7E7SIX1mlwhIhAsxYLO2uOabjfhhyzcuQxauohV3/2q2x8x6gHx3zkBwRP9SFIhxFXf2tiz
+VHa6dLG3fdfA6PZZxU3Iva0fFNrfWEQlMhkqx35+jq44sDB7R3IJMfAw28Mbdim7aXZOV/kbZKKT
+VrdvmW7bCgScEeOAH8tjlBAKqeFkgjH5jCftppkA9nCTGPihNIaj3XrCGHn2emU1z5DrvTOTn1Or
+czvmmzQgLx3vqR1jGqCA2wMv+SYahtKNu6m+UjqHZ0gNv7Sg2Ca+I19zN38m5pIEo3/PIKe38zrK
+y5nLAgMBAAGjczBxMBEGCWCGSAGG+EIBAQQEAwIABzAfBgNVHSMEGDAWgBRl8jGtKvf33VKWCscC
+wQ7vptU7ETAPBgNVHRMBAf8EBTADAQH/MAsGA1UdDwQEAwIB/jAdBgNVHQ4EFgQUZfIxrSr3991S
+lgrHAsEO76bVOxEwDQYJKoZIhvcNAQEFBQADggEBAEs17szkrr/Dbq2flTtLP1se31cpolnKOOK5
+Gv+e5m4y3R6u6jW39ZORTtpC4cMXYFDy0VwmuYK36m3knITnA3kXr5g9lNvHugDnuL8BV8F3RTIM
+O/G0HAiw/VGgod2aHRM2mm23xzy54cXZF/qD1T0VoDy7HgviyJA/qIYM/PmLXoXLT1tLYhFHxUV8
+BS9BsZ4QaRuZluBVeftOhpm4lNqGOGqTo+fLbuXf6iFViZx9fX+Y9QCJ7uOEwFyWtcVG6kbghVW2
+G8kS1sHNzYDzAgE8yGnLRUhj2JTQ7IUOO04RZfSCjKY9ri4ilAnIXOo8gV0WKgOXFlUJ24pBgp5m
+mxE=
+-----END CERTIFICATE-----
+
+ApplicationCA - Japanese Government
+===================================
+-----BEGIN CERTIFICATE-----
+MIIDoDCCAoigAwIBAgIBMTANBgkqhkiG9w0BAQUFADBDMQswCQYDVQQGEwJKUDEcMBoGA1UEChMT
+SmFwYW5lc2UgR292ZXJubWVudDEWMBQGA1UECxMNQXBwbGljYXRpb25DQTAeFw0wNzEyMTIxNTAw
+MDBaFw0xNzEyMTIxNTAwMDBaMEMxCzAJBgNVBAYTAkpQMRwwGgYDVQQKExNKYXBhbmVzZSBHb3Zl
+cm5tZW50MRYwFAYDVQQLEw1BcHBsaWNhdGlvbkNBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
+CgKCAQEAp23gdE6Hj6UG3mii24aZS2QNcfAKBZuOquHMLtJqO8F6tJdhjYq+xpqcBrSGUeQ3DnR4
+fl+Kf5Sk10cI/VBaVuRorChzoHvpfxiSQE8tnfWuREhzNgaeZCw7NCPbXCbkcXmP1G55IrmTwcrN
+wVbtiGrXoDkhBFcsovW8R0FPXjQilbUfKW1eSvNNcr5BViCH/OlQR9cwFO5cjFW6WY2H/CPek9AE
+jP3vbb3QesmlOmpyM8ZKDQUXKi17safY1vC+9D/qDihtQWEjdnjDuGWk81quzMKq2edY3rZ+nYVu
+nyoKb58DKTCXKB28t89UKU5RMfkntigm/qJj5kEW8DOYRwIDAQABo4GeMIGbMB0GA1UdDgQWBBRU
+WssmP3HMlEYNllPqa0jQk/5CdTAOBgNVHQ8BAf8EBAMCAQYwWQYDVR0RBFIwUKROMEwxCzAJBgNV
+BAYTAkpQMRgwFgYDVQQKDA/ml6XmnKzlm73mlL/lupwxIzAhBgNVBAsMGuOCouODl+ODquOCseOD
+vOOCt+ODp+ODs0NBMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBADlqRHZ3ODrs
+o2dGD/mLBqj7apAxzn7s2tGJfHrrLgy9mTLnsCTWw//1sogJhyzjVOGjprIIC8CFqMjSnHH2HZ9g
+/DgzE+Ge3Atf2hZQKXsvcJEPmbo0NI2VdMV+eKlmXb3KIXdCEKxmJj3ekav9FfBv7WxfEPjzFvYD
+io+nEhEMy/0/ecGc/WLuo89UDNErXxc+4z6/wCs+CZv+iKZ+tJIX/COUgb1up8WMwusRRdv4QcmW
+dupwX3kSa+SjB1oF7ydJzyGfikwJcGapJsErEU4z0g781mzSDjJkaP+tBXhfAx2o45CsJOAPQKdL
+rosot4LKGAfmt1t06SAZf7IbiVQ=
+-----END CERTIFICATE-----
+
+GeoTrust Primary Certification Authority - G3
+=============================================
+-----BEGIN CERTIFICATE-----
+MIID/jCCAuagAwIBAgIQFaxulBmyeUtB9iepwxgPHzANBgkqhkiG9w0BAQsFADCBmDELMAkGA1UE
+BhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xOTA3BgNVBAsTMChjKSAyMDA4IEdlb1RydXN0
+IEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTE2MDQGA1UEAxMtR2VvVHJ1c3QgUHJpbWFy
+eSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEczMB4XDTA4MDQwMjAwMDAwMFoXDTM3MTIwMTIz
+NTk1OVowgZgxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMTkwNwYDVQQLEzAo
+YykgMjAwOCBHZW9UcnVzdCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxNjA0BgNVBAMT
+LUdlb1RydXN0IFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMzCCASIwDQYJKoZI
+hvcNAQEBBQADggEPADCCAQoCggEBANziXmJYHTNXOTIz+uvLh4yn1ErdBojqZI4xmKU4kB6Yzy5j
+K/BGvESyiaHAKAxJcCGVn2TAppMSAmUmhsalifD614SgcK9PGpc/BkTVyetyEH3kMSj7HGHmKAdE
+c5IiaacDiGydY8hS2pgn5whMcD60yRLBxWeDXTPzAxHsatBT4tG6NmCUgLthY2xbF37fQJQeqw3C
+IShwiP/WJmxsYAQlTlV+fe+/lEjetx3dcI0FX4ilm/LC7urRQEFtYjgdVgbFA0dRIBn8exALDmKu
+dlW/X3e+PkkBUz2YJQN2JFodtNuJ6nnltrM7P7pMKEF/BqxqjsHQ9gUdfeZChuOl1UcCAwEAAaNC
+MEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFMR5yo6hTgMdHNxr
+2zFblD4/MH8tMA0GCSqGSIb3DQEBCwUAA4IBAQAtxRPPVoB7eni9n64smefv2t+UXglpp+duaIy9
+cr5HqQ6XErhK8WTTOd8lNNTBzU6B8A8ExCSzNJbGpqow32hhc9f5joWJ7w5elShKKiePEI4ufIbE
+Ap7aDHdlDkQNkv39sxY2+hENHYwOB4lqKVb3cvTdFZx3NWZXqxNT2I7BQMXXExZacse3aQHEerGD
+AWh9jUGhlBjBJVz88P6DAod8DQ3PLghcSkANPuyBYeYk28rgDi0Hsj5W3I31QYUHSJsMC8tJP33s
+t/3LjWeJGqvtux6jAAgIFyqCXDFdRootD4abdNlF+9RAsXqqaC2Gspki4cErx5z481+oghLrGREt
+-----END CERTIFICATE-----
+
+thawte Primary Root CA - G2
+===========================
+-----BEGIN CERTIFICATE-----
+MIICiDCCAg2gAwIBAgIQNfwmXNmET8k9Jj1Xm67XVjAKBggqhkjOPQQDAzCBhDELMAkGA1UEBhMC
+VVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjE4MDYGA1UECxMvKGMpIDIwMDcgdGhhd3RlLCBJbmMu
+IC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxJDAiBgNVBAMTG3RoYXd0ZSBQcmltYXJ5IFJvb3Qg
+Q0EgLSBHMjAeFw0wNzExMDUwMDAwMDBaFw0zODAxMTgyMzU5NTlaMIGEMQswCQYDVQQGEwJVUzEV
+MBMGA1UEChMMdGhhd3RlLCBJbmMuMTgwNgYDVQQLEy8oYykgMjAwNyB0aGF3dGUsIEluYy4gLSBG
+b3IgYXV0aG9yaXplZCB1c2Ugb25seTEkMCIGA1UEAxMbdGhhd3RlIFByaW1hcnkgUm9vdCBDQSAt
+IEcyMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEotWcgnuVnfFSeIf+iha/BebfowJPDQfGAFG6DAJS
+LSKkQjnE/o/qycG+1E3/n3qe4rF8mq2nhglzh9HnmuN6papu+7qzcMBniKI11KOasf2twu8x+qi5
+8/sIxpHR+ymVo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQU
+mtgAMADna3+FGO6Lts6KDPgR4bswCgYIKoZIzj0EAwMDaQAwZgIxAN344FdHW6fmCsO99YCKlzUN
+G4k8VIZ3KMqh9HneteY4sPBlcIx/AlTCv//YoT7ZzwIxAMSNlPzcU9LcnXgWHxUzI1NS41oxXZ3K
+rr0TKUQNJ1uo52icEvdYPy5yAlejj6EULg==
+-----END CERTIFICATE-----
+
+thawte Primary Root CA - G3
+===========================
+-----BEGIN CERTIFICATE-----
+MIIEKjCCAxKgAwIBAgIQYAGXt0an6rS0mtZLL/eQ+zANBgkqhkiG9w0BAQsFADCBrjELMAkGA1UE
+BhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2
+aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMvKGMpIDIwMDggdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhv
+cml6ZWQgdXNlIG9ubHkxJDAiBgNVBAMTG3RoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EgLSBHMzAeFw0w
+ODA0MDIwMDAwMDBaFw0zNzEyMDEyMzU5NTlaMIGuMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMdGhh
+d3RlLCBJbmMuMSgwJgYDVQQLEx9DZXJ0aWZpY2F0aW9uIFNlcnZpY2VzIERpdmlzaW9uMTgwNgYD
+VQQLEy8oYykgMjAwOCB0aGF3dGUsIEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTEkMCIG
+A1UEAxMbdGhhd3RlIFByaW1hcnkgUm9vdCBDQSAtIEczMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
+MIIBCgKCAQEAsr8nLPvb2FvdeHsbnndmgcs+vHyu86YnmjSjaDFxODNi5PNxZnmxqWWjpYvVj2At
+P0LMqmsywCPLLEHd5N/8YZzic7IilRFDGF/Eth9XbAoFWCLINkw6fKXRz4aviKdEAhN0cXMKQlkC
++BsUa0Lfb1+6a4KinVvnSr0eAXLbS3ToO39/fR8EtCab4LRarEc9VbjXsCZSKAExQGbY2SS99irY
+7CFJXJv2eul/VTV+lmuNk5Mny5K76qxAwJ/C+IDPXfRa3M50hqY+bAtTyr2SzhkGcuYMXDhpxwTW
+vGzOW/b3aJzcJRVIiKHpqfiYnODz1TEoYRFsZ5aNOZnLwkUkOQIDAQABo0IwQDAPBgNVHRMBAf8E
+BTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUrWyqlGCc7eT/+j4KdCtjA/e2Wb8wDQYJ
+KoZIhvcNAQELBQADggEBABpA2JVlrAmSicY59BDlqQ5mU1143vokkbvnRFHfxhY0Cu9qRFHqKweK
+A3rD6z8KLFIWoCtDuSWQP3CpMyVtRRooOyfPqsMpQhvfO0zAMzRbQYi/aytlryjvsvXDqmbOe1bu
+t8jLZ8HJnBoYuMTDSQPxYA5QzUbF83d597YV4Djbxy8ooAw/dyZ02SUS2jHaGh7cKUGRIjxpp7sC
+8rZcJwOJ9Abqm+RyguOhCcHpABnTPtRwa7pxpqpYrvS76Wy274fMm7v/OeZWYdMKp8RcTGB7BXcm
+er/YB1IsYvdwY9k5vG8cwnncdimvzsUsZAReiDZuMdRAGmI0Nj81Aa6sY6A=
+-----END CERTIFICATE-----
+
+GeoTrust Primary Certification Authority - G2
+=============================================
+-----BEGIN CERTIFICATE-----
+MIICrjCCAjWgAwIBAgIQPLL0SAoA4v7rJDteYD7DazAKBggqhkjOPQQDAzCBmDELMAkGA1UEBhMC
+VVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xOTA3BgNVBAsTMChjKSAyMDA3IEdlb1RydXN0IElu
+Yy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTE2MDQGA1UEAxMtR2VvVHJ1c3QgUHJpbWFyeSBD
+ZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcyMB4XDTA3MTEwNTAwMDAwMFoXDTM4MDExODIzNTk1
+OVowgZgxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMTkwNwYDVQQLEzAoYykg
+MjAwNyBHZW9UcnVzdCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxNjA0BgNVBAMTLUdl
+b1RydXN0IFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMjB2MBAGByqGSM49AgEG
+BSuBBAAiA2IABBWx6P0DFUPlrOuHNxFi79KDNlJ9RVcLSo17VDs6bl8VAsBQps8lL33KSLjHUGMc
+KiEIfJo22Av+0SbFWDEwKCXzXV2juLaltJLtbCyf691DiaI8S0iRHVDsJt/WYC69IaNCMEAwDwYD
+VR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFBVfNVdRVfslsq0DafwBo/q+
+EVXVMAoGCCqGSM49BAMDA2cAMGQCMGSWWaboCd6LuvpaiIjwH5HTRqjySkwCY/tsXzjbLkGTqQ7m
+ndwxHLKgpxgceeHHNgIwOlavmnRs9vuD4DPTCF+hnMJbn0bWtsuRBmOiBuczrD6ogRLQy7rQkgu2
+npaqBA+K
+-----END CERTIFICATE-----
+
+VeriSign Universal Root Certification Authority
+===============================================
+-----BEGIN CERTIFICATE-----
+MIIEuTCCA6GgAwIBAgIQQBrEZCGzEyEDDrvkEhrFHTANBgkqhkiG9w0BAQsFADCBvTELMAkGA1UE
+BhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBO
+ZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwOCBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVk
+IHVzZSBvbmx5MTgwNgYDVQQDEy9WZXJpU2lnbiBVbml2ZXJzYWwgUm9vdCBDZXJ0aWZpY2F0aW9u
+IEF1dGhvcml0eTAeFw0wODA0MDIwMDAwMDBaFw0zNzEyMDEyMzU5NTlaMIG9MQswCQYDVQQGEwJV
+UzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdv
+cmsxOjA4BgNVBAsTMShjKSAyMDA4IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNl
+IG9ubHkxODA2BgNVBAMTL1ZlcmlTaWduIFVuaXZlcnNhbCBSb290IENlcnRpZmljYXRpb24gQXV0
+aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAx2E3XrEBNNti1xWb/1hajCMj
+1mCOkdeQmIN65lgZOIzF9uVkhbSicfvtvbnazU0AtMgtc6XHaXGVHzk8skQHnOgO+k1KxCHfKWGP
+MiJhgsWHH26MfF8WIFFE0XBPV+rjHOPMee5Y2A7Cs0WTwCznmhcrewA3ekEzeOEz4vMQGn+HLL72
+9fdC4uW/h2KJXwBL38Xd5HVEMkE6HnFuacsLdUYI0crSK5XQz/u5QGtkjFdN/BMReYTtXlT2NJ8I
+AfMQJQYXStrxHXpma5hgZqTZ79IugvHw7wnqRMkVauIDbjPTrJ9VAMf2CGqUuV/c4DPxhGD5WycR
+tPwW8rtWaoAljQIDAQABo4GyMIGvMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMG0G
+CCsGAQUFBwEMBGEwX6FdoFswWTBXMFUWCWltYWdlL2dpZjAhMB8wBwYFKw4DAhoEFI/l0xqGrI2O
+a8PPgGrUSBgsexkuMCUWI2h0dHA6Ly9sb2dvLnZlcmlzaWduLmNvbS92c2xvZ28uZ2lmMB0GA1Ud
+DgQWBBS2d/ppSEefUxLVwuoHMnYH0ZcHGTANBgkqhkiG9w0BAQsFAAOCAQEASvj4sAPmLGd75JR3
+Y8xuTPl9Dg3cyLk1uXBPY/ok+myDjEedO2Pzmvl2MpWRsXe8rJq+seQxIcaBlVZaDrHC1LGmWazx
+Y8u4TB1ZkErvkBYoH1quEPuBUDgMbMzxPcP1Y+Oz4yHJJDnp/RVmRvQbEdBNc6N9Rvk97ahfYtTx
+P/jgdFcrGJ2BtMQo2pSXpXDrrB2+BxHw1dvd5Yzw1TKwg+ZX4o+/vqGqvz0dtdQ46tewXDpPaj+P
+wGZsY6rp2aQW9IHRlRQOfc2VNNnSj3BzgXucfr2YYdhFh5iQxeuGMMY1v/D/w1WIg0vvBZIGcfK4
+mJO37M2CYfE45k+XmCpajQ==
+-----END CERTIFICATE-----
+
+VeriSign Class 3 Public Primary Certification Authority - G4
+============================================================
+-----BEGIN CERTIFICATE-----
+MIIDhDCCAwqgAwIBAgIQL4D+I4wOIg9IZxIokYesszAKBggqhkjOPQQDAzCByjELMAkGA1UEBhMC
+VVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBOZXR3
+b3JrMTowOAYDVQQLEzEoYykgMjAwNyBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVz
+ZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmlj
+YXRpb24gQXV0aG9yaXR5IC0gRzQwHhcNMDcxMTA1MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCByjEL
+MAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBU
+cnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNyBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRo
+b3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5
+IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzQwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAASnVnp8
+Utpkmw4tXNherJI9/gHmGUo9FANL+mAnINmDiWn6VMaaGF5VKmTeBvaNSjutEDxlPZCIBIngMGGz
+rl0Bp3vefLK+ymVhAIau2o970ImtTR1ZmkGxvEeA3J5iw/mjgbIwga8wDwYDVR0TAQH/BAUwAwEB
+/zAOBgNVHQ8BAf8EBAMCAQYwbQYIKwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJaW1hZ2UvZ2lmMCEw
+HzAHBgUrDgMCGgQUj+XTGoasjY5rw8+AatRIGCx7GS4wJRYjaHR0cDovL2xvZ28udmVyaXNpZ24u
+Y29tL3ZzbG9nby5naWYwHQYDVR0OBBYEFLMWkf3upm7ktS5Jj4d4gYDs5bG1MAoGCCqGSM49BAMD
+A2gAMGUCMGYhDBgmYFo4e1ZC4Kf8NoRRkSAsdk1DPcQdhCPQrNZ8NQbOzWm9kA3bbEhCHQ6qQgIx
+AJw9SDkjOVgaFRJZap7v1VmyHVIsmXHNxynfGyphe3HR3vPA5Q06Sqotp9iGKt0uEA==
+-----END CERTIFICATE-----
+
+NetLock Arany (Class Gold) Főtanúsítvány
+============================================
+-----BEGIN CERTIFICATE-----
+MIIEFTCCAv2gAwIBAgIGSUEs5AAQMA0GCSqGSIb3DQEBCwUAMIGnMQswCQYDVQQGEwJIVTERMA8G
+A1UEBwwIQnVkYXBlc3QxFTATBgNVBAoMDE5ldExvY2sgS2Z0LjE3MDUGA1UECwwuVGFuw7pzw610
+dsOhbnlraWFkw7NrIChDZXJ0aWZpY2F0aW9uIFNlcnZpY2VzKTE1MDMGA1UEAwwsTmV0TG9jayBB
+cmFueSAoQ2xhc3MgR29sZCkgRsWRdGFuw7pzw610dsOhbnkwHhcNMDgxMjExMTUwODIxWhcNMjgx
+MjA2MTUwODIxWjCBpzELMAkGA1UEBhMCSFUxETAPBgNVBAcMCEJ1ZGFwZXN0MRUwEwYDVQQKDAxO
+ZXRMb2NrIEtmdC4xNzA1BgNVBAsMLlRhbsO6c8OtdHbDoW55a2lhZMOzayAoQ2VydGlmaWNhdGlv
+biBTZXJ2aWNlcykxNTAzBgNVBAMMLE5ldExvY2sgQXJhbnkgKENsYXNzIEdvbGQpIEbFkXRhbsO6
+c8OtdHbDoW55MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxCRec75LbRTDofTjl5Bu
+0jBFHjzuZ9lk4BqKf8owyoPjIMHj9DrTlF8afFttvzBPhCf2nx9JvMaZCpDyD/V/Q4Q3Y1GLeqVw
+/HpYzY6b7cNGbIRwXdrzAZAj/E4wqX7hJ2Pn7WQ8oLjJM2P+FpD/sLj916jAwJRDC7bVWaaeVtAk
+H3B5r9s5VA1lddkVQZQBr17s9o3x/61k/iCa11zr/qYfCGSji3ZVrR47KGAuhyXoqq8fxmRGILdw
+fzzeSNuWU7c5d+Qa4scWhHaXWy+7GRWF+GmF9ZmnqfI0p6m2pgP8b4Y9VHx2BJtr+UBdADTHLpl1
+neWIA6pN+APSQnbAGwIDAKiLo0UwQzASBgNVHRMBAf8ECDAGAQH/AgEEMA4GA1UdDwEB/wQEAwIB
+BjAdBgNVHQ4EFgQUzPpnk/C2uNClwB7zU/2MU9+D15YwDQYJKoZIhvcNAQELBQADggEBAKt/7hwW
+qZw8UQCgwBEIBaeZ5m8BiFRhbvG5GK1Krf6BQCOUL/t1fC8oS2IkgYIL9WHxHG64YTjrgfpioTta
+YtOUZcTh5m2C+C8lcLIhJsFyUR+MLMOEkMNaj7rP9KdlpeuY0fsFskZ1FSNqb4VjMIDw1Z4fKRzC
+bLBQWV2QWzuoDTDPv31/zvGdg73JRm4gpvlhUbohL3u+pRVjodSVh/GeufOJ8z2FuLjbvrW5Kfna
+NwUASZQDhETnv0Mxz3WLJdH0pmT1kvarBes96aULNmLazAZfNou2XjG4Kvte9nHfRCaexOYNkbQu
+dZWAUWpLMKawYqGT8ZvYzsRjdT9ZR7E=
+-----END CERTIFICATE-----
+
+Staat der Nederlanden Root CA - G2
+==================================
+-----BEGIN CERTIFICATE-----
+MIIFyjCCA7KgAwIBAgIEAJiWjDANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJOTDEeMBwGA1UE
+CgwVU3RhYXQgZGVyIE5lZGVybGFuZGVuMSswKQYDVQQDDCJTdGFhdCBkZXIgTmVkZXJsYW5kZW4g
+Um9vdCBDQSAtIEcyMB4XDTA4MDMyNjExMTgxN1oXDTIwMDMyNTExMDMxMFowWjELMAkGA1UEBhMC
+TkwxHjAcBgNVBAoMFVN0YWF0IGRlciBOZWRlcmxhbmRlbjErMCkGA1UEAwwiU3RhYXQgZGVyIE5l
+ZGVybGFuZGVuIFJvb3QgQ0EgLSBHMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMVZ
+5291qj5LnLW4rJ4L5PnZyqtdj7U5EILXr1HgO+EASGrP2uEGQxGZqhQlEq0i6ABtQ8SpuOUfiUtn
+vWFI7/3S4GCI5bkYYCjDdyutsDeqN95kWSpGV+RLufg3fNU254DBtvPUZ5uW6M7XxgpT0GtJlvOj
+CwV3SPcl5XCsMBQgJeN/dVrlSPhOewMHBPqCYYdu8DvEpMfQ9XQ+pV0aCPKbJdL2rAQmPlU6Yiil
+e7Iwr/g3wtG61jj99O9JMDeZJiFIhQGp5Rbn3JBV3w/oOM2ZNyFPXfUib2rFEhZgF1XyZWampzCR
+OME4HYYEhLoaJXhena/MUGDWE4dS7WMfbWV9whUYdMrhfmQpjHLYFhN9C0lK8SgbIHRrxT3dsKpI
+CT0ugpTNGmXZK4iambwYfp/ufWZ8Pr2UuIHOzZgweMFvZ9C+X+Bo7d7iscksWXiSqt8rYGPy5V65
+48r6f1CGPqI0GAwJaCgRHOThuVw+R7oyPxjMW4T182t0xHJ04eOLoEq9jWYv6q012iDTiIJh8BIi
+trzQ1aTsr1SIJSQ8p22xcik/Plemf1WvbibG/ufMQFxRRIEKeN5KzlW/HdXZt1bv8Hb/C3m1r737
+qWmRRpdogBQ2HbN/uymYNqUg+oJgYjOk7Na6B6duxc8UpufWkjTYgfX8HV2qXB72o007uPc5AgMB
+AAGjgZcwgZQwDwYDVR0TAQH/BAUwAwEB/zBSBgNVHSAESzBJMEcGBFUdIAAwPzA9BggrBgEFBQcC
+ARYxaHR0cDovL3d3dy5wa2lvdmVyaGVpZC5ubC9wb2xpY2llcy9yb290LXBvbGljeS1HMjAOBgNV
+HQ8BAf8EBAMCAQYwHQYDVR0OBBYEFJFoMocVHYnitfGsNig0jQt8YojrMA0GCSqGSIb3DQEBCwUA
+A4ICAQCoQUpnKpKBglBu4dfYszk78wIVCVBR7y29JHuIhjv5tLySCZa59sCrI2AGeYwRTlHSeYAz
++51IvuxBQ4EffkdAHOV6CMqqi3WtFMTC6GY8ggen5ieCWxjmD27ZUD6KQhgpxrRW/FYQoAUXvQwj
+f/ST7ZwaUb7dRUG/kSS0H4zpX897IZmflZ85OkYcbPnNe5yQzSipx6lVu6xiNGI1E0sUOlWDuYaN
+kqbG9AclVMwWVxJKgnjIFNkXgiYtXSAfea7+1HAWFpWD2DU5/1JddRwWxRNVz0fMdWVSSt7wsKfk
+CpYL+63C4iWEst3kvX5ZbJvw8NjnyvLplzh+ib7M+zkXYT9y2zqR2GUBGR2tUKRXCnxLvJxxcypF
+URmFzI79R6d0lR2o0a9OF7FpJsKqeFdbxU2n5Z4FF5TKsl+gSRiNNOkmbEgeqmiSBeGCc1qb3Adb
+CG19ndeNIdn8FCCqwkXfP+cAslHkwvgFuXkajDTznlvkN1trSt8sV4pAWja63XVECDdCcAz+3F4h
+oKOKwJCcaNpQ5kUQR3i2TtJlycM33+FCY7BXN0Ute4qcvwXqZVUz9zkQxSgqIXobisQk+T8VyJoV
+IPVVYpbtbZNQvOSqeK3Zywplh6ZmwcSBo3c6WB4L7oOLnR7SUqTMHW+wmG2UMbX4cQrcufx9MmDm
+66+KAQ==
+-----END CERTIFICATE-----
+
+Juur-SK
+=======
+-----BEGIN CERTIFICATE-----
+MIIE5jCCA86gAwIBAgIEO45L/DANBgkqhkiG9w0BAQUFADBdMRgwFgYJKoZIhvcNAQkBFglwa2lA
+c2suZWUxCzAJBgNVBAYTAkVFMSIwIAYDVQQKExlBUyBTZXJ0aWZpdHNlZXJpbWlza2Vza3VzMRAw
+DgYDVQQDEwdKdXVyLVNLMB4XDTAxMDgzMDE0MjMwMVoXDTE2MDgyNjE0MjMwMVowXTEYMBYGCSqG
+SIb3DQEJARYJcGtpQHNrLmVlMQswCQYDVQQGEwJFRTEiMCAGA1UEChMZQVMgU2VydGlmaXRzZWVy
+aW1pc2tlc2t1czEQMA4GA1UEAxMHSnV1ci1TSzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
+ggEBAIFxNj4zB9bjMI0TfncyRsvPGbJgMUaXhvSYRqTCZUXP00B841oiqBB4M8yIsdOBSvZiF3tf
+TQou0M+LI+5PAk676w7KvRhj6IAcjeEcjT3g/1tf6mTll+g/mX8MCgkzABpTpyHhOEvWgxutr2TC
++Rx6jGZITWYfGAriPrsfB2WThbkasLnE+w0R9vXW+RvHLCu3GFH+4Hv2qEivbDtPL+/40UceJlfw
+UR0zlv/vWT3aTdEVNMfqPxZIe5EcgEMPPbgFPtGzlc3Yyg/CQ2fbt5PgIoIuvvVoKIO5wTtpeyDa
+Tpxt4brNj3pssAki14sL2xzVWiZbDcDq5WDQn/413z8CAwEAAaOCAawwggGoMA8GA1UdEwEB/wQF
+MAMBAf8wggEWBgNVHSAEggENMIIBCTCCAQUGCisGAQQBzh8BAQEwgfYwgdAGCCsGAQUFBwICMIHD
+HoHAAFMAZQBlACAAcwBlAHIAdABpAGYAaQBrAGEAYQB0ACAAbwBuACAAdgDkAGwAagBhAHMAdABh
+AHQAdQBkACAAQQBTAC0AaQBzACAAUwBlAHIAdABpAGYAaQB0AHMAZQBlAHIAaQBtAGkAcwBrAGUA
+cwBrAHUAcwAgAGEAbABhAG0ALQBTAEsAIABzAGUAcgB0AGkAZgBpAGsAYQBhAHQAaQBkAGUAIABr
+AGkAbgBuAGkAdABhAG0AaQBzAGUAawBzMCEGCCsGAQUFBwIBFhVodHRwOi8vd3d3LnNrLmVlL2Nw
+cy8wKwYDVR0fBCQwIjAgoB6gHIYaaHR0cDovL3d3dy5zay5lZS9qdXVyL2NybC8wHQYDVR0OBBYE
+FASqekej5ImvGs8KQKcYP2/v6X2+MB8GA1UdIwQYMBaAFASqekej5ImvGs8KQKcYP2/v6X2+MA4G
+A1UdDwEB/wQEAwIB5jANBgkqhkiG9w0BAQUFAAOCAQEAe8EYlFOiCfP+JmeaUOTDBS8rNXiRTHyo
+ERF5TElZrMj3hWVcRrs7EKACr81Ptcw2Kuxd/u+gkcm2k298gFTsxwhwDY77guwqYHhpNjbRxZyL
+abVAyJRld/JXIWY7zoVAtjNjGr95HvxcHdMdkxuLDF2FvZkwMhgJkVLpfKG6/2SSmuz+Ne6ML678
+IIbsSt4beDI3poHSna9aEhbKmVv8b20OxaAehsmR0FyYgl9jDIpaq9iVpszLita/ZEuOyoqysOkh
+Mp6qqIWYNIE5ITuoOlIyPfZrN4YGWhWY3PARZv40ILcD9EEQfTmEeZZyY7aWAuVrua0ZTbvGRNs2
+yyqcjg==
+-----END CERTIFICATE-----
+
+Hongkong Post Root CA 1
+=======================
+-----BEGIN CERTIFICATE-----
+MIIDMDCCAhigAwIBAgICA+gwDQYJKoZIhvcNAQEFBQAwRzELMAkGA1UEBhMCSEsxFjAUBgNVBAoT
+DUhvbmdrb25nIFBvc3QxIDAeBgNVBAMTF0hvbmdrb25nIFBvc3QgUm9vdCBDQSAxMB4XDTAzMDUx
+NTA1MTMxNFoXDTIzMDUxNTA0NTIyOVowRzELMAkGA1UEBhMCSEsxFjAUBgNVBAoTDUhvbmdrb25n
+IFBvc3QxIDAeBgNVBAMTF0hvbmdrb25nIFBvc3QgUm9vdCBDQSAxMIIBIjANBgkqhkiG9w0BAQEF
+AAOCAQ8AMIIBCgKCAQEArP84tulmAknjorThkPlAj3n54r15/gK97iSSHSL22oVyaf7XPwnU3ZG1
+ApzQjVrhVcNQhrkpJsLj2aDxaQMoIIBFIi1WpztUlVYiWR8o3x8gPW2iNr4joLFutbEnPzlTCeqr
+auh0ssJlXI6/fMN4hM2eFvz1Lk8gKgifd/PFHsSaUmYeSF7jEAaPIpjhZY4bXSNmO7ilMlHIhqqh
+qZ5/dpTCpmy3QfDVyAY45tQM4vM7TG1QjMSDJ8EThFk9nnV0ttgCXjqQesBCNnLsak3c78QA3xMY
+V18meMjWCnl3v/evt3a5pQuEF10Q6m/hq5URX208o1xNg1vysxmKgIsLhwIDAQABoyYwJDASBgNV
+HRMBAf8ECDAGAQH/AgEDMA4GA1UdDwEB/wQEAwIBxjANBgkqhkiG9w0BAQUFAAOCAQEADkbVPK7i
+h9legYsCmEEIjEy82tvuJxuC52pF7BaLT4Wg87JwvVqWuspube5Gi27nKi6Wsxkz67SfqLI37pio
+l7Yutmcn1KZJ/RyTZXaeQi/cImyaT/JaFTmxcdcrUehtHJjA2Sr0oYJ71clBoiMBdDhViw+5Lmei
+IAQ32pwL0xch4I+XeTRvhEgCIDMb5jREn5Fw9IBehEPCKdJsEhTkYY2sEJCehFC78JZvRZ+K88ps
+T/oROhUVRsPNH4NbLUES7VBnQRM9IauUiqpOfMGx+6fWtScvl6tu4B3i0RwsH0Ti/L6RoZz71ilT
+c4afU9hDDl3WY4JxHYB0yvbiAmvZWg==
+-----END CERTIFICATE-----
+
+SecureSign RootCA11
+===================
+-----BEGIN CERTIFICATE-----
+MIIDbTCCAlWgAwIBAgIBATANBgkqhkiG9w0BAQUFADBYMQswCQYDVQQGEwJKUDErMCkGA1UEChMi
+SmFwYW4gQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcywgSW5jLjEcMBoGA1UEAxMTU2VjdXJlU2lnbiBS
+b290Q0ExMTAeFw0wOTA0MDgwNDU2NDdaFw0yOTA0MDgwNDU2NDdaMFgxCzAJBgNVBAYTAkpQMSsw
+KQYDVQQKEyJKYXBhbiBDZXJ0aWZpY2F0aW9uIFNlcnZpY2VzLCBJbmMuMRwwGgYDVQQDExNTZWN1
+cmVTaWduIFJvb3RDQTExMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA/XeqpRyQBTvL
+TJszi1oURaTnkBbR31fSIRCkF/3frNYfp+TbfPfs37gD2pRY/V1yfIw/XwFndBWW4wI8h9uuywGO
+wvNmxoVF9ALGOrVisq/6nL+k5tSAMJjzDbaTj6nU2DbysPyKyiyhFTOVMdrAG/LuYpmGYz+/3ZMq
+g6h2uRMft85OQoWPIucuGvKVCbIFtUROd6EgvanyTgp9UK31BQ1FT0Zx/Sg+U/sE2C3XZR1KG/rP
+O7AxmjVuyIsG0wCR8pQIZUyxNAYAeoni8McDWc/V1uinMrPmmECGxc0nEovMe863ETxiYAcjPitA
+bpSACW22s293bzUIUPsCh8U+iQIDAQABo0IwQDAdBgNVHQ4EFgQUW/hNT7KlhtQ60vFjmqC+CfZX
+t94wDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAKCh
+OBZmLqdWHyGcBvod7bkixTgm2E5P7KN/ed5GIaGHd48HCJqypMWvDzKYC3xmKbabfSVSSUOrTC4r
+bnpwrxYO4wJs+0LmGJ1F2FXI6Dvd5+H0LgscNFxsWEr7jIhQX5Ucv+2rIrVls4W6ng+4reV6G4pQ
+Oh29Dbx7VFALuUKvVaAYga1lme++5Jy/xIWrQbJUb9wlze144o4MjQlJ3WN7WmmWAiGovVJZ6X01
+y8hSyn+B/tlr0/cR7SXf+Of5pPpyl4RTDaXQMhhRdlkUbA/r7F+AjHVDg8OFmP9Mni0N5HeDk061
+lgeLKBObjBmNQSdJQO7e5iNEOdyhIta6A/I=
+-----END CERTIFICATE-----
+
+ACEDICOM Root
+=============
+-----BEGIN CERTIFICATE-----
+MIIFtTCCA52gAwIBAgIIYY3HhjsBggUwDQYJKoZIhvcNAQEFBQAwRDEWMBQGA1UEAwwNQUNFRElD
+T00gUm9vdDEMMAoGA1UECwwDUEtJMQ8wDQYDVQQKDAZFRElDT00xCzAJBgNVBAYTAkVTMB4XDTA4
+MDQxODE2MjQyMloXDTI4MDQxMzE2MjQyMlowRDEWMBQGA1UEAwwNQUNFRElDT00gUm9vdDEMMAoG
+A1UECwwDUEtJMQ8wDQYDVQQKDAZFRElDT00xCzAJBgNVBAYTAkVTMIICIjANBgkqhkiG9w0BAQEF
+AAOCAg8AMIICCgKCAgEA/5KV4WgGdrQsyFhIyv2AVClVYyT/kGWbEHV7w2rbYgIB8hiGtXxaOLHk
+WLn709gtn70yN78sFW2+tfQh0hOR2QetAQXW8713zl9CgQr5auODAKgrLlUTY4HKRxx7XBZXehuD
+YAQ6PmXDzQHe3qTWDLqO3tkE7hdWIpuPY/1NFgu3e3eM+SW10W2ZEi5PGrjm6gSSrj0RuVFCPYew
+MYWveVqc/udOXpJPQ/yrOq2lEiZmueIM15jO1FillUAKt0SdE3QrwqXrIhWYENiLxQSfHY9g5QYb
+m8+5eaA9oiM/Qj9r+hwDezCNzmzAv+YbX79nuIQZ1RXve8uQNjFiybwCq0Zfm/4aaJQ0PZCOrfbk
+HQl/Sog4P75n/TSW9R28MHTLOO7VbKvU/PQAtwBbhTIWdjPp2KOZnQUAqhbm84F9b32qhm2tFXTT
+xKJxqvQUfecyuB+81fFOvW8XAjnXDpVCOscAPukmYxHqC9FK/xidstd7LzrZlvvoHpKuE1XI2Sf2
+3EgbsCTBheN3nZqk8wwRHQ3ItBTutYJXCb8gWH8vIiPYcMt5bMlL8qkqyPyHK9caUPgn6C9D4zq9
+2Fdx/c6mUlv53U3t5fZvie27k5x2IXXwkkwp9y+cAS7+UEaeZAwUswdbxcJzbPEHXEUkFDWug/Fq
+TYl6+rPYLWbwNof1K1MCAwEAAaOBqjCBpzAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFKaz
+4SsrSbbXc6GqlPUB53NlTKxQMA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUprPhKytJttdzoaqU
+9QHnc2VMrFAwRAYDVR0gBD0wOzA5BgRVHSAAMDEwLwYIKwYBBQUHAgEWI2h0dHA6Ly9hY2VkaWNv
+bS5lZGljb21ncm91cC5jb20vZG9jMA0GCSqGSIb3DQEBBQUAA4ICAQDOLAtSUWImfQwng4/F9tqg
+aHtPkl7qpHMyEVNEskTLnewPeUKzEKbHDZ3Ltvo/Onzqv4hTGzz3gvoFNTPhNahXwOf9jU8/kzJP
+eGYDdwdY6ZXIfj7QeQCM8htRM5u8lOk6e25SLTKeI6RF+7YuE7CLGLHdztUdp0J/Vb77W7tH1Pwk
+zQSulgUV1qzOMPPKC8W64iLgpq0i5ALudBF/TP94HTXa5gI06xgSYXcGCRZj6hitoocf8seACQl1
+ThCojz2GuHURwCRiipZ7SkXp7FnFvmuD5uHorLUwHv4FB4D54SMNUI8FmP8sX+g7tq3PgbUhh8oI
+KiMnMCArz+2UW6yyetLHKKGKC5tNSixthT8Jcjxn4tncB7rrZXtaAWPWkFtPF2Y9fwsZo5NjEFIq
+nxQWWOLcpfShFosOkYuByptZ+thrkQdlVV9SH686+5DdaaVbnG0OLLb6zqylfDJKZ0DcMDQj3dcE
+I2bw/FWAp/tmGYI1Z2JwOV5vx+qQQEQIHriy1tvuWacNGHk0vFQYXlPKNFHtRQrmjseCNj6nOGOp
+MCwXEGCSn1WHElkQwg9naRHMTh5+Spqtr0CodaxWkHS4oJyleW/c6RrIaQXpuvoDs3zk4E7Czp3o
+tkYNbn5XOmeUwssfnHdKZ05phkOTOPu220+DkdRgfks+KzgHVZhepA==
+-----END CERTIFICATE-----
+
+Microsec e-Szigno Root CA 2009
+==============================
+-----BEGIN CERTIFICATE-----
+MIIECjCCAvKgAwIBAgIJAMJ+QwRORz8ZMA0GCSqGSIb3DQEBCwUAMIGCMQswCQYDVQQGEwJIVTER
+MA8GA1UEBwwIQnVkYXBlc3QxFjAUBgNVBAoMDU1pY3Jvc2VjIEx0ZC4xJzAlBgNVBAMMHk1pY3Jv
+c2VjIGUtU3ppZ25vIFJvb3QgQ0EgMjAwOTEfMB0GCSqGSIb3DQEJARYQaW5mb0BlLXN6aWduby5o
+dTAeFw0wOTA2MTYxMTMwMThaFw0yOTEyMzAxMTMwMThaMIGCMQswCQYDVQQGEwJIVTERMA8GA1UE
+BwwIQnVkYXBlc3QxFjAUBgNVBAoMDU1pY3Jvc2VjIEx0ZC4xJzAlBgNVBAMMHk1pY3Jvc2VjIGUt
+U3ppZ25vIFJvb3QgQ0EgMjAwOTEfMB0GCSqGSIb3DQEJARYQaW5mb0BlLXN6aWduby5odTCCASIw
+DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOn4j/NjrdqG2KfgQvvPkd6mJviZpWNwrZuuyjNA
+fW2WbqEORO7hE52UQlKavXWFdCyoDh2Tthi3jCyoz/tccbna7P7ofo/kLx2yqHWH2Leh5TvPmUpG
+0IMZfcChEhyVbUr02MelTTMuhTlAdX4UfIASmFDHQWe4oIBhVKZsTh/gnQ4H6cm6M+f+wFUoLAKA
+pxn1ntxVUwOXewdI/5n7N4okxFnMUBBjjqqpGrCEGob5X7uxUG6k0QrM1XF+H6cbfPVTbiJfyyvm
+1HxdrtbCxkzlBQHZ7Vf8wSN5/PrIJIOV87VqUQHQd9bpEqH5GoP7ghu5sJf0dgYzQ0mg/wu1+rUC
+AwEAAaOBgDB+MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBTLD8bf
+QkPMPcu1SCOhGnqmKrs0aDAfBgNVHSMEGDAWgBTLD8bfQkPMPcu1SCOhGnqmKrs0aDAbBgNVHREE
+FDASgRBpbmZvQGUtc3ppZ25vLmh1MA0GCSqGSIb3DQEBCwUAA4IBAQDJ0Q5eLtXMs3w+y/w9/w0o
+lZMEyL/azXm4Q5DwpL7v8u8hmLzU1F0G9u5C7DBsoKqpyvGvivo/C3NqPuouQH4frlRheesuCDfX
+I/OMn74dseGkddug4lQUsbocKaQY9hK6ohQU4zE1yED/t+AFdlfBHFny+L/k7SViXITwfn4fs775
+tyERzAMBVnCnEJIeGzSBHq2cGsMEPO0CYdYeBvNfOofyK/FFh+U9rNHHV4S9a67c2Pm2G2JwCz02
+yULyMtd6YebS2z3PyKnJm9zbWETXbzivf3jTo60adbocwTZ8jx5tHMN1Rq41Bab2XD0h7lbwyYIi
+LXpUq3DDfSJlgnCW
+-----END CERTIFICATE-----
+
+GlobalSign Root CA - R3
+=======================
+-----BEGIN CERTIFICATE-----
+MIIDXzCCAkegAwIBAgILBAAAAAABIVhTCKIwDQYJKoZIhvcNAQELBQAwTDEgMB4GA1UECxMXR2xv
+YmFsU2lnbiBSb290IENBIC0gUjMxEzARBgNVBAoTCkdsb2JhbFNpZ24xEzARBgNVBAMTCkdsb2Jh
+bFNpZ24wHhcNMDkwMzE4MTAwMDAwWhcNMjkwMzE4MTAwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxT
+aWduIFJvb3QgQ0EgLSBSMzETMBEGA1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2ln
+bjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMwldpB5BngiFvXAg7aEyiie/QV2EcWt
+iHL8RgJDx7KKnQRfJMsuS+FggkbhUqsMgUdwbN1k0ev1LKMPgj0MK66X17YUhhB5uzsTgHeMCOFJ
+0mpiLx9e+pZo34knlTifBtc+ycsmWQ1z3rDI6SYOgxXG71uL0gRgykmmKPZpO/bLyCiR5Z2KYVc3
+rHQU3HTgOu5yLy6c+9C7v/U9AOEGM+iCK65TpjoWc4zdQQ4gOsC0p6Hpsk+QLjJg6VfLuQSSaGjl
+OCZgdbKfd/+RFO+uIEn8rUAVSNECMWEZXriX7613t2Saer9fwRPvm2L7DWzgVGkWqQPabumDk3F2
+xmmFghcCAwEAAaNCMEAwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYE
+FI/wS3+oLkUkrk1Q+mOai97i3Ru8MA0GCSqGSIb3DQEBCwUAA4IBAQBLQNvAUKr+yAzv95ZURUm7
+lgAJQayzE4aGKAczymvmdLm6AC2upArT9fHxD4q/c2dKg8dEe3jgr25sbwMpjjM5RcOO5LlXbKr8
+EpbsU8Yt5CRsuZRj+9xTaGdWPoO4zzUhw8lo/s7awlOqzJCK6fBdRoyV3XpYKBovHd7NADdBj+1E
+bddTKJd+82cEHhXXipa0095MJ6RMG3NzdvQXmcIfeg7jLQitChws/zyrVQ4PkX4268NXSb7hLi18
+YIvDQVETI53O9zJrlAGomecsMx86OyXShkDOOyyGeMlhLxS67ttVb9+E7gUJTb0o2HLO02JQZR7r
+kpeDMdmztcpHWD9f
+-----END CERTIFICATE-----
+
+Autoridad de Certificacion Firmaprofesional CIF A62634068
+=========================================================
+-----BEGIN CERTIFICATE-----
+MIIGFDCCA/ygAwIBAgIIU+w77vuySF8wDQYJKoZIhvcNAQEFBQAwUTELMAkGA1UEBhMCRVMxQjBA
+BgNVBAMMOUF1dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uIEZpcm1hcHJvZmVzaW9uYWwgQ0lGIEE2
+MjYzNDA2ODAeFw0wOTA1MjAwODM4MTVaFw0zMDEyMzEwODM4MTVaMFExCzAJBgNVBAYTAkVTMUIw
+QAYDVQQDDDlBdXRvcmlkYWQgZGUgQ2VydGlmaWNhY2lvbiBGaXJtYXByb2Zlc2lvbmFsIENJRiBB
+NjI2MzQwNjgwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDKlmuO6vj78aI14H9M2uDD
+Utd9thDIAl6zQyrET2qyyhxdKJp4ERppWVevtSBC5IsP5t9bpgOSL/UR5GLXMnE42QQMcas9UX4P
+B99jBVzpv5RvwSmCwLTaUbDBPLutN0pcyvFLNg4kq7/DhHf9qFD0sefGL9ItWY16Ck6WaVICqjaY
+7Pz6FIMMNx/Jkjd/14Et5cS54D40/mf0PmbR0/RAz15iNA9wBj4gGFrO93IbJWyTdBSTo3OxDqqH
+ECNZXyAFGUftaI6SEspd/NYrspI8IM/hX68gvqB2f3bl7BqGYTM+53u0P6APjqK5am+5hyZvQWyI
+plD9amML9ZMWGxmPsu2bm8mQ9QEM3xk9Dz44I8kvjwzRAv4bVdZO0I08r0+k8/6vKtMFnXkIoctX
+MbScyJCyZ/QYFpM6/EfY0XiWMR+6KwxfXZmtY4laJCB22N/9q06mIqqdXuYnin1oKaPnirjaEbsX
+LZmdEyRG98Xi2J+Of8ePdG1asuhy9azuJBCtLxTa/y2aRnFHvkLfuwHb9H/TKI8xWVvTyQKmtFLK
+bpf7Q8UIJm+K9Lv9nyiqDdVF8xM6HdjAeI9BZzwelGSuewvF6NkBiDkal4ZkQdU7hwxu+g/GvUgU
+vzlN1J5Bto+WHWOWk9mVBngxaJ43BjuAiUVhOSPHG0SjFeUc+JIwuwIDAQABo4HvMIHsMBIGA1Ud
+EwEB/wQIMAYBAf8CAQEwDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBRlzeurNR4APn7VdMActHNH
+DhpkLzCBpgYDVR0gBIGeMIGbMIGYBgRVHSAAMIGPMC8GCCsGAQUFBwIBFiNodHRwOi8vd3d3LmZp
+cm1hcHJvZmVzaW9uYWwuY29tL2NwczBcBggrBgEFBQcCAjBQHk4AUABhAHMAZQBvACAAZABlACAA
+bABhACAAQgBvAG4AYQBuAG8AdgBhACAANAA3ACAAQgBhAHIAYwBlAGwAbwBuAGEAIAAwADgAMAAx
+ADcwDQYJKoZIhvcNAQEFBQADggIBABd9oPm03cXF661LJLWhAqvdpYhKsg9VSytXjDvlMd3+xDLx
+51tkljYyGOylMnfX40S2wBEqgLk9am58m9Ot/MPWo+ZkKXzR4Tgegiv/J2Wv+xYVxC5xhOW1//qk
+R71kMrv2JYSiJ0L1ILDCExARzRAVukKQKtJE4ZYm6zFIEv0q2skGz3QeqUvVhyj5eTSSPi5E6PaP
+T481PyWzOdxjKpBrIF/EUhJOlywqrJ2X3kjyo2bbwtKDlaZmp54lD+kLM5FlClrD2VQS3a/DTg4f
+Jl4N3LON7NWBcN7STyQF82xO9UxJZo3R/9ILJUFI/lGExkKvgATP0H5kSeTy36LssUzAKh3ntLFl
+osS88Zj0qnAHY7S42jtM+kAiMFsRpvAFDsYCA0irhpuF3dvd6qJ2gHN99ZwExEWN57kci57q13XR
+crHedUTnQn3iV2t93Jm8PYMo6oCTjcVMZcFwgbg4/EMxsvYDNEeyrPsiBsse3RdHHF9mudMaotoR
+saS8I8nkvof/uZS2+F0gStRf571oe2XyFR7SOqkt6dhrJKyXWERHrVkY8SFlcN7ONGCoQPHzPKTD
+KCOM/iczQ0CgFzzr6juwcqajuUpLXhZI9LK8yIySxZ2frHI2vDSANGupi5LAuBft7HZT9SQBjLMi
+6Et8Vcad+qMUu2WFbm5PEn4KPJ2V
+-----END CERTIFICATE-----
+
+Izenpe.com
+==========
+-----BEGIN CERTIFICATE-----
+MIIF8TCCA9mgAwIBAgIQALC3WhZIX7/hy/WL1xnmfTANBgkqhkiG9w0BAQsFADA4MQswCQYDVQQG
+EwJFUzEUMBIGA1UECgwLSVpFTlBFIFMuQS4xEzARBgNVBAMMCkl6ZW5wZS5jb20wHhcNMDcxMjEz
+MTMwODI4WhcNMzcxMjEzMDgyNzI1WjA4MQswCQYDVQQGEwJFUzEUMBIGA1UECgwLSVpFTlBFIFMu
+QS4xEzARBgNVBAMMCkl6ZW5wZS5jb20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDJ
+03rKDx6sp4boFmVqscIbRTJxldn+EFvMr+eleQGPicPK8lVx93e+d5TzcqQsRNiekpsUOqHnJJAK
+ClaOxdgmlOHZSOEtPtoKct2jmRXagaKH9HtuJneJWK3W6wyyQXpzbm3benhB6QiIEn6HLmYRY2xU
++zydcsC8Lv/Ct90NduM61/e0aL6i9eOBbsFGb12N4E3GVFWJGjMxCrFXuaOKmMPsOzTFlUFpfnXC
+PCDFYbpRR6AgkJOhkEvzTnyFRVSa0QUmQbC1TR0zvsQDyCV8wXDbO/QJLVQnSKwv4cSsPsjLkkxT
+OTcj7NMB+eAJRE1NZMDhDVqHIrytG6P+JrUV86f8hBnp7KGItERphIPzidF0BqnMC9bC3ieFUCbK
+F7jJeodWLBoBHmy+E60QrLUk9TiRodZL2vG70t5HtfG8gfZZa88ZU+mNFctKy6lvROUbQc/hhqfK
+0GqfvEyNBjNaooXlkDWgYlwWTvDjovoDGrQscbNYLN57C9saD+veIR8GdwYDsMnvmfzAuU8Lhij+
+0rnq49qlw0dpEuDb8PYZi+17cNcC1u2HGCgsBCRMd+RIihrGO5rUD8r6ddIBQFqNeb+Lz0vPqhbB
+leStTIo+F5HUsWLlguWABKQDfo2/2n+iD5dPDNMN+9fR5XJ+HMh3/1uaD7euBUbl8agW7EekFwID
+AQABo4H2MIHzMIGwBgNVHREEgagwgaWBD2luZm9AaXplbnBlLmNvbaSBkTCBjjFHMEUGA1UECgw+
+SVpFTlBFIFMuQS4gLSBDSUYgQTAxMzM3MjYwLVJNZXJjLlZpdG9yaWEtR2FzdGVpeiBUMTA1NSBG
+NjIgUzgxQzBBBgNVBAkMOkF2ZGEgZGVsIE1lZGl0ZXJyYW5lbyBFdG9yYmlkZWEgMTQgLSAwMTAx
+MCBWaXRvcmlhLUdhc3RlaXowDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0O
+BBYEFB0cZQ6o8iV7tJHP5LGx5r1VdGwFMA0GCSqGSIb3DQEBCwUAA4ICAQB4pgwWSp9MiDrAyw6l
+Fn2fuUhfGI8NYjb2zRlrrKvV9pF9rnHzP7MOeIWblaQnIUdCSnxIOvVFfLMMjlF4rJUT3sb9fbga
+kEyrkgPH7UIBzg/YsfqikuFgba56awmqxinuaElnMIAkejEWOVt+8Rwu3WwJrfIxwYJOubv5vr8q
+hT/AQKM6WfxZSzwoJNu0FXWuDYi6LnPAvViH5ULy617uHjAimcs30cQhbIHsvm0m5hzkQiCeR7Cs
+g1lwLDXWrzY0tM07+DKo7+N4ifuNRSzanLh+QBxh5z6ikixL8s36mLYp//Pye6kfLqCTVyvehQP5
+aTfLnnhqBbTFMXiJ7HqnheG5ezzevh55hM6fcA5ZwjUukCox2eRFekGkLhObNA5me0mrZJfQRsN5
+nXJQY6aYWwa9SG3YOYNw6DXwBdGqvOPbyALqfP2C2sJbUjWumDqtujWTI6cfSN01RpiyEGjkpTHC
+ClguGYEQyVB1/OpaFs4R1+7vUIgtYf8/QnMFlEPVjjxOAToZpR9GTnfQXeWBIiGH/pR9hNiTrdZo
+Q0iy2+tzJOeRf1SktoA+naM8THLCV8Sg1Mw4J87VBp6iSNnpn86CcDaTmjvfliHjWbcM2pE38P1Z
+WrOZyGlsQyYBNWNgVYkDOnXYukrZVP/u3oDYLdE41V4tC5h9Pmzb/CaIxw==
+-----END CERTIFICATE-----
+
+Chambers of Commerce Root - 2008
+================================
+-----BEGIN CERTIFICATE-----
+MIIHTzCCBTegAwIBAgIJAKPaQn6ksa7aMA0GCSqGSIb3DQEBBQUAMIGuMQswCQYDVQQGEwJFVTFD
+MEEGA1UEBxM6TWFkcmlkIChzZWUgY3VycmVudCBhZGRyZXNzIGF0IHd3dy5jYW1lcmZpcm1hLmNv
+bS9hZGRyZXNzKTESMBAGA1UEBRMJQTgyNzQzMjg3MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMu
+QS4xKTAnBgNVBAMTIENoYW1iZXJzIG9mIENvbW1lcmNlIFJvb3QgLSAyMDA4MB4XDTA4MDgwMTEy
+Mjk1MFoXDTM4MDczMTEyMjk1MFowga4xCzAJBgNVBAYTAkVVMUMwQQYDVQQHEzpNYWRyaWQgKHNl
+ZSBjdXJyZW50IGFkZHJlc3MgYXQgd3d3LmNhbWVyZmlybWEuY29tL2FkZHJlc3MpMRIwEAYDVQQF
+EwlBODI3NDMyODcxGzAZBgNVBAoTEkFDIENhbWVyZmlybWEgUy5BLjEpMCcGA1UEAxMgQ2hhbWJl
+cnMgb2YgQ29tbWVyY2UgUm9vdCAtIDIwMDgwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
+AQCvAMtwNyuAWko6bHiUfaN/Gh/2NdW928sNRHI+JrKQUrpjOyhYb6WzbZSm891kDFX29ufyIiKA
+XuFixrYp4YFs8r/lfTJqVKAyGVn+H4vXPWCGhSRv4xGzdz4gljUha7MI2XAuZPeEklPWDrCQiorj
+h40G072QDuKZoRuGDtqaCrsLYVAGUvGef3bsyw/QHg3PmTA9HMRFEFis1tPo1+XqxQEHd9ZR5gN/
+ikilTWh1uem8nk4ZcfUyS5xtYBkL+8ydddy/Js2Pk3g5eXNeJQ7KXOt3EgfLZEFHcpOrUMPrCXZk
+NNI5t3YRCQ12RcSprj1qr7V9ZS+UWBDsXHyvfuK2GNnQm05aSd+pZgvMPMZ4fKecHePOjlO+Bd5g
+D2vlGts/4+EhySnB8esHnFIbAURRPHsl18TlUlRdJQfKFiC4reRB7noI/plvg6aRArBsNlVq5331
+lubKgdaX8ZSD6e2wsWsSaR6s+12pxZjptFtYer49okQ6Y1nUCyXeG0+95QGezdIp1Z8XGQpvvwyQ
+0wlf2eOKNcx5Wk0ZN5K3xMGtr/R5JJqyAQuxr1yW84Ay+1w9mPGgP0revq+ULtlVmhduYJ1jbLhj
+ya6BXBg14JC7vjxPNyK5fuvPnnchpj04gftI2jE9K+OJ9dC1vX7gUMQSibMjmhAxhduub+84Mxh2
+EQIDAQABo4IBbDCCAWgwEgYDVR0TAQH/BAgwBgEB/wIBDDAdBgNVHQ4EFgQU+SSsD7K1+HnA+mCI
+G8TZTQKeFxkwgeMGA1UdIwSB2zCB2IAU+SSsD7K1+HnA+mCIG8TZTQKeFxmhgbSkgbEwga4xCzAJ
+BgNVBAYTAkVVMUMwQQYDVQQHEzpNYWRyaWQgKHNlZSBjdXJyZW50IGFkZHJlc3MgYXQgd3d3LmNh
+bWVyZmlybWEuY29tL2FkZHJlc3MpMRIwEAYDVQQFEwlBODI3NDMyODcxGzAZBgNVBAoTEkFDIENh
+bWVyZmlybWEgUy5BLjEpMCcGA1UEAxMgQ2hhbWJlcnMgb2YgQ29tbWVyY2UgUm9vdCAtIDIwMDiC
+CQCj2kJ+pLGu2jAOBgNVHQ8BAf8EBAMCAQYwPQYDVR0gBDYwNDAyBgRVHSAAMCowKAYIKwYBBQUH
+AgEWHGh0dHA6Ly9wb2xpY3kuY2FtZXJmaXJtYS5jb20wDQYJKoZIhvcNAQEFBQADggIBAJASryI1
+wqM58C7e6bXpeHxIvj99RZJe6dqxGfwWPJ+0W2aeaufDuV2I6A+tzyMP3iU6XsxPpcG1Lawk0lgH
+3qLPaYRgM+gQDROpI9CF5Y57pp49chNyM/WqfcZjHwj0/gF/JM8rLFQJ3uIrbZLGOU8W6jx+ekbU
+RWpGqOt1glanq6B8aBMz9p0w8G8nOSQjKpD9kCk18pPfNKXG9/jvjA9iSnyu0/VU+I22mlaHFoI6
+M6taIgj3grrqLuBHmrS1RaMFO9ncLkVAO+rcf+g769HsJtg1pDDFOqxXnrN2pSB7+R5KBWIBpih1
+YJeSDW4+TTdDDZIVnBgizVGZoCkaPF+KMjNbMMeJL0eYD6MDxvbxrN8y8NmBGuScvfaAFPDRLLmF
+9dijscilIeUcE5fuDr3fKanvNFNb0+RqE4QGtjICxFKuItLcsiFCGtpA8CnJ7AoMXOLQusxI0zcK
+zBIKinmwPQN/aUv0NCB9szTqjktk9T79syNnFQ0EuPAtwQlRPLJsFfClI9eDdOTlLsn+mCdCxqvG
+nrDQWzilm1DefhiYtUU79nm06PcaewaD+9CL2rvHvRirCG88gGtAPxkZumWK5r7VXNM21+9AUiRg
+OGcEMeyP84LG3rlV8zsxkVrctQgVrXYlCg17LofiDKYGvCYQbTed7N14jHyAxfDZd0jQ
+-----END CERTIFICATE-----
+
+Global Chambersign Root - 2008
+==============================
+-----BEGIN CERTIFICATE-----
+MIIHSTCCBTGgAwIBAgIJAMnN0+nVfSPOMA0GCSqGSIb3DQEBBQUAMIGsMQswCQYDVQQGEwJFVTFD
+MEEGA1UEBxM6TWFkcmlkIChzZWUgY3VycmVudCBhZGRyZXNzIGF0IHd3dy5jYW1lcmZpcm1hLmNv
+bS9hZGRyZXNzKTESMBAGA1UEBRMJQTgyNzQzMjg3MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMu
+QS4xJzAlBgNVBAMTHkdsb2JhbCBDaGFtYmVyc2lnbiBSb290IC0gMjAwODAeFw0wODA4MDExMjMx
+NDBaFw0zODA3MzExMjMxNDBaMIGsMQswCQYDVQQGEwJFVTFDMEEGA1UEBxM6TWFkcmlkIChzZWUg
+Y3VycmVudCBhZGRyZXNzIGF0IHd3dy5jYW1lcmZpcm1hLmNvbS9hZGRyZXNzKTESMBAGA1UEBRMJ
+QTgyNzQzMjg3MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMuQS4xJzAlBgNVBAMTHkdsb2JhbCBD
+aGFtYmVyc2lnbiBSb290IC0gMjAwODCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMDf
+VtPkOpt2RbQT2//BthmLN0EYlVJH6xedKYiONWwGMi5HYvNJBL99RDaxccy9Wglz1dmFRP+RVyXf
+XjaOcNFccUMd2drvXNL7G706tcuto8xEpw2uIRU/uXpbknXYpBI4iRmKt4DS4jJvVpyR1ogQC7N0
+ZJJ0YPP2zxhPYLIj0Mc7zmFLmY/CDNBAspjcDahOo7kKrmCgrUVSY7pmvWjg+b4aqIG7HkF4ddPB
+/gBVsIdU6CeQNR1MM62X/JcumIS/LMmjv9GYERTtY/jKmIhYF5ntRQOXfjyGHoiMvvKRhI9lNNgA
+TH23MRdaKXoKGCQwoze1eqkBfSbW+Q6OWfH9GzO1KTsXO0G2Id3UwD2ln58fQ1DJu7xsepeY7s2M
+H/ucUa6LcL0nn3HAa6x9kGbo1106DbDVwo3VyJ2dwW3Q0L9R5OP4wzg2rtandeavhENdk5IMagfe
+Ox2YItaswTXbo6Al/3K1dh3ebeksZixShNBFks4c5eUzHdwHU1SjqoI7mjcv3N2gZOnm3b2u/GSF
+HTynyQbehP9r6GsaPMWis0L7iwk+XwhSx2LE1AVxv8Rk5Pihg+g+EpuoHtQ2TS9x9o0o9oOpE9Jh
+wZG7SMA0j0GMS0zbaRL/UJScIINZc+18ofLx/d33SdNDWKBWY8o9PeU1VlnpDsogzCtLkykPAgMB
+AAGjggFqMIIBZjASBgNVHRMBAf8ECDAGAQH/AgEMMB0GA1UdDgQWBBS5CcqcHtvTbDprru1U8VuT
+BjUuXjCB4QYDVR0jBIHZMIHWgBS5CcqcHtvTbDprru1U8VuTBjUuXqGBsqSBrzCBrDELMAkGA1UE
+BhMCRVUxQzBBBgNVBAcTOk1hZHJpZCAoc2VlIGN1cnJlbnQgYWRkcmVzcyBhdCB3d3cuY2FtZXJm
+aXJtYS5jb20vYWRkcmVzcykxEjAQBgNVBAUTCUE4Mjc0MzI4NzEbMBkGA1UEChMSQUMgQ2FtZXJm
+aXJtYSBTLkEuMScwJQYDVQQDEx5HbG9iYWwgQ2hhbWJlcnNpZ24gUm9vdCAtIDIwMDiCCQDJzdPp
+1X0jzjAOBgNVHQ8BAf8EBAMCAQYwPQYDVR0gBDYwNDAyBgRVHSAAMCowKAYIKwYBBQUHAgEWHGh0
+dHA6Ly9wb2xpY3kuY2FtZXJmaXJtYS5jb20wDQYJKoZIhvcNAQEFBQADggIBAICIf3DekijZBZRG
+/5BXqfEv3xoNa/p8DhxJJHkn2EaqbylZUohwEurdPfWbU1Rv4WCiqAm57OtZfMY18dwY6fFn5a+6
+ReAJ3spED8IXDneRRXozX1+WLGiLwUePmJs9wOzL9dWCkoQ10b42OFZyMVtHLaoXpGNR6woBrX/s
+dZ7LoR/xfxKxueRkf2fWIyr0uDldmOghp+G9PUIadJpwr2hsUF1Jz//7Dl3mLEfXgTpZALVza2Mg
+9jFFCDkO9HB+QHBaP9BrQql0PSgvAm11cpUJjUhjxsYjV5KTXjXBjfkK9yydYhz2rXzdpjEetrHH
+foUm+qRqtdpjMNHvkzeyZi99Bffnt0uYlDXA2TopwZ2yUDMdSqlapskD7+3056huirRXhOukP9Du
+qqqHW2Pok+JrqNS4cnhrG+055F3Lm6qH1U9OAP7Zap88MQ8oAgF9mOinsKJknnn4SPIVqczmyETr
+P3iZ8ntxPjzxmKfFGBI/5rsoM0LpRQp8bfKGeS/Fghl9CYl8slR2iK7ewfPM4W7bMdaTrpmg7yVq
+c5iJWzouE4gev8CSlDQb4ye3ix5vQv/n6TebUB0tovkC7stYWDpxvGjjqsGvHCgfotwjZT+B6q6Z
+09gwzxMNTxXJhLynSC34MCN32EZLeW32jO06f2ARePTpm67VVMB0gNELQp/B
+-----END CERTIFICATE-----
+
+Go Daddy Root Certificate Authority - G2
+========================================
+-----BEGIN CERTIFICATE-----
+MIIDxTCCAq2gAwIBAgIBADANBgkqhkiG9w0BAQsFADCBgzELMAkGA1UEBhMCVVMxEDAOBgNVBAgT
+B0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxGjAYBgNVBAoTEUdvRGFkZHkuY29tLCBJbmMu
+MTEwLwYDVQQDEyhHbyBEYWRkeSBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5
+MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgYMxCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6
+b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMRowGAYDVQQKExFHb0RhZGR5LmNvbSwgSW5jLjExMC8G
+A1UEAxMoR28gRGFkZHkgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZI
+hvcNAQEBBQADggEPADCCAQoCggEBAL9xYgjx+lk09xvJGKP3gElY6SKDE6bFIEMBO4Tx5oVJnyfq
+9oQbTqC023CYxzIBsQU+B07u9PpPL1kwIuerGVZr4oAH/PMWdYA5UXvl+TW2dE6pjYIT5LY/qQOD
++qK+ihVqf94Lw7YZFAXK6sOoBJQ7RnwyDfMAZiLIjWltNowRGLfTshxgtDj6AozO091GB94KPutd
+fMh8+7ArU6SSYmlRJQVhGkSBjCypQ5Yj36w6gZoOKcUcqeldHraenjAKOc7xiID7S13MMuyFYkMl
+NAJWJwGRtDtwKj9useiciAF9n9T521NtYJ2/LOdYq7hfRvzOxBsDPAnrSTFcaUaz4EcCAwEAAaNC
+MEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFDqahQcQZyi27/a9
+BUFuIMGU2g/eMA0GCSqGSIb3DQEBCwUAA4IBAQCZ21151fmXWWcDYfF+OwYxdS2hII5PZYe096ac
+vNjpL9DbWu7PdIxztDhC2gV7+AJ1uP2lsdeu9tfeE8tTEH6KRtGX+rcuKxGrkLAngPnon1rpN5+r
+5N9ss4UXnT3ZJE95kTXWXwTrgIOrmgIttRD02JDHBHNA7XIloKmf7J6raBKZV8aPEjoJpL1E/QYV
+N8Gb5DKj7Tjo2GTzLH4U/ALqn83/B2gX2yKQOC16jdFU8WnjXzPKej17CuPKf1855eJ1usV2GDPO
+LPAvTK33sefOT6jEm0pUBsV/fdUID+Ic/n4XuKxe9tQWskMJDE32p2u0mYRlynqI4uJEvlz36hz1
+-----END CERTIFICATE-----
+
+Starfield Root Certificate Authority - G2
+=========================================
+-----BEGIN CERTIFICATE-----
+MIID3TCCAsWgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBjzELMAkGA1UEBhMCVVMxEDAOBgNVBAgT
+B0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoTHFN0YXJmaWVsZCBUZWNobm9s
+b2dpZXMsIEluYy4xMjAwBgNVBAMTKVN0YXJmaWVsZCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0
+eSAtIEcyMB4XDTA5MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgY8xCzAJBgNVBAYTAlVTMRAw
+DgYDVQQIEwdBcml6b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFyZmllbGQg
+VGVjaG5vbG9naWVzLCBJbmMuMTIwMAYDVQQDEylTdGFyZmllbGQgUm9vdCBDZXJ0aWZpY2F0ZSBB
+dXRob3JpdHkgLSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL3twQP89o/8ArFv
+W59I2Z154qK3A2FWGMNHttfKPTUuiUP3oWmb3ooa/RMgnLRJdzIpVv257IzdIvpy3Cdhl+72WoTs
+bhm5iSzchFvVdPtrX8WJpRBSiUZV9Lh1HOZ/5FSuS/hVclcCGfgXcVnrHigHdMWdSL5stPSksPNk
+N3mSwOxGXn/hbVNMYq/NHwtjuzqd+/x5AJhhdM8mgkBj87JyahkNmcrUDnXMN/uLicFZ8WJ/X7Nf
+ZTD4p7dNdloedl40wOiWVpmKs/B/pM293DIxfJHP4F8R+GuqSVzRmZTRouNjWwl2tVZi4Ut0HZbU
+JtQIBFnQmA4O5t78w+wfkPECAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC
+AQYwHQYDVR0OBBYEFHwMMh+n2TB/xH1oo2Kooc6rB1snMA0GCSqGSIb3DQEBCwUAA4IBAQARWfol
+TwNvlJk7mh+ChTnUdgWUXuEok21iXQnCoKjUsHU48TRqneSfioYmUeYs0cYtbpUgSpIB7LiKZ3sx
+4mcujJUDJi5DnUox9g61DLu34jd/IroAow57UvtruzvE03lRTs2Q9GcHGcg8RnoNAX3FWOdt5oUw
+F5okxBDgBPfg8n/Uqgr/Qh037ZTlZFkSIHc40zI+OIF1lnP6aI+xy84fxez6nH7PfrHxBy22/L/K
+pL/QlwVKvOoYKAKQvVR4CSFx09F9HdkWsKlhPdAKACL8x3vLCWRFCztAgfd9fDL1mMpYjn0q7pBZ
+c2T5NnReJaH1ZgUufzkVqSr7UIuOhWn0
+-----END CERTIFICATE-----
+
+Starfield Services Root Certificate Authority - G2
+==================================================
+-----BEGIN CERTIFICATE-----
+MIID7zCCAtegAwIBAgIBADANBgkqhkiG9w0BAQsFADCBmDELMAkGA1UEBhMCVVMxEDAOBgNVBAgT
+B0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoTHFN0YXJmaWVsZCBUZWNobm9s
+b2dpZXMsIEluYy4xOzA5BgNVBAMTMlN0YXJmaWVsZCBTZXJ2aWNlcyBSb290IENlcnRpZmljYXRl
+IEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgZgxCzAJBgNV
+BAYTAlVTMRAwDgYDVQQIEwdBcml6b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxT
+dGFyZmllbGQgVGVjaG5vbG9naWVzLCBJbmMuMTswOQYDVQQDEzJTdGFyZmllbGQgU2VydmljZXMg
+Um9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
+AQoCggEBANUMOsQq+U7i9b4Zl1+OiFOxHz/Lz58gE20pOsgPfTz3a3Y4Y9k2YKibXlwAgLIvWX/2
+h/klQ4bnaRtSmpDhcePYLQ1Ob/bISdm28xpWriu2dBTrz/sm4xq6HZYuajtYlIlHVv8loJNwU4Pa
+hHQUw2eeBGg6345AWh1KTs9DkTvnVtYAcMtS7nt9rjrnvDH5RfbCYM8TWQIrgMw0R9+53pBlbQLP
+LJGmpufehRhJfGZOozptqbXuNC66DQO4M99H67FrjSXZm86B0UVGMpZwh94CDklDhbZsc7tk6mFB
+rMnUVN+HL8cisibMn1lUaJ/8viovxFUcdUBgF4UCVTmLfwUCAwEAAaNCMEAwDwYDVR0TAQH/BAUw
+AwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFJxfAN+qAdcwKziIorhtSpzyEZGDMA0GCSqG
+SIb3DQEBCwUAA4IBAQBLNqaEd2ndOxmfZyMIbw5hyf2E3F/YNoHN2BtBLZ9g3ccaaNnRbobhiCPP
+E95Dz+I0swSdHynVv/heyNXBve6SbzJ08pGCL72CQnqtKrcgfU28elUSwhXqvfdqlS5sdJ/PHLTy
+xQGjhdByPq1zqwubdQxtRbeOlKyWN7Wg0I8VRw7j6IPdj/3vQQF3zCepYoUz8jcI73HPdwbeyBkd
+iEDPfUYd/x7H4c7/I9vG+o1VTqkC50cRRj70/b17KSa7qWFiNyi2LSr2EIZkyXCn0q23KXB56jza
+YyWf/Wi3MOxw+3WKt21gZ7IeyLnp2KhvAotnDU0mV3HaIPzBSlCNsSi6
+-----END CERTIFICATE-----
+
+AffirmTrust Commercial
+======================
+-----BEGIN CERTIFICATE-----
+MIIDTDCCAjSgAwIBAgIId3cGJyapsXwwDQYJKoZIhvcNAQELBQAwRDELMAkGA1UEBhMCVVMxFDAS
+BgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBDb21tZXJjaWFsMB4XDTEw
+MDEyOTE0MDYwNloXDTMwMTIzMTE0MDYwNlowRDELMAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmly
+bVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBDb21tZXJjaWFsMIIBIjANBgkqhkiG9w0BAQEF
+AAOCAQ8AMIIBCgKCAQEA9htPZwcroRX1BiLLHwGy43NFBkRJLLtJJRTWzsO3qyxPxkEylFf6Eqdb
+DuKPHx6GGaeqtS25Xw2Kwq+FNXkyLbscYjfysVtKPcrNcV/pQr6U6Mje+SJIZMblq8Yrba0F8PrV
+C8+a5fBQpIs7R6UjW3p6+DM/uO+Zl+MgwdYoic+U+7lF7eNAFxHUdPALMeIrJmqbTFeurCA+ukV6
+BfO9m2kVrn1OIGPENXY6BwLJN/3HR+7o8XYdcxXyl6S1yHp52UKqK39c/s4mT6NmgTWvRLpUHhww
+MmWd5jyTXlBOeuM61G7MGvv50jeuJCqrVwMiKA1JdX+3KNp1v47j3A55MQIDAQABo0IwQDAdBgNV
+HQ4EFgQUnZPGU4teyq8/nx4P5ZmVvCT2lI8wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC
+AQYwDQYJKoZIhvcNAQELBQADggEBAFis9AQOzcAN/wr91LoWXym9e2iZWEnStB03TX8nfUYGXUPG
+hi4+c7ImfU+TqbbEKpqrIZcUsd6M06uJFdhrJNTxFq7YpFzUf1GO7RgBsZNjvbz4YYCanrHOQnDi
+qX0GJX0nof5v7LMeJNrjS1UaADs1tDvZ110w/YETifLCBivtZ8SOyUOyXGsViQK8YvxO8rUzqrJv
+0wqiUOP2O+guRMLbZjipM1ZI8W0bM40NjD9gN53Tym1+NH4Nn3J2ixufcv1SNUFFApYvHLKac0kh
+sUlHRUe072o0EclNmsxZt9YCnlpOZbWUrhvfKbAW8b8Angc6F2S1BLUjIZkKlTuXfO8=
+-----END CERTIFICATE-----
+
+AffirmTrust Networking
+======================
+-----BEGIN CERTIFICATE-----
+MIIDTDCCAjSgAwIBAgIIfE8EORzUmS0wDQYJKoZIhvcNAQEFBQAwRDELMAkGA1UEBhMCVVMxFDAS
+BgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBOZXR3b3JraW5nMB4XDTEw
+MDEyOTE0MDgyNFoXDTMwMTIzMTE0MDgyNFowRDELMAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmly
+bVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBOZXR3b3JraW5nMIIBIjANBgkqhkiG9w0BAQEF
+AAOCAQ8AMIIBCgKCAQEAtITMMxcua5Rsa2FSoOujz3mUTOWUgJnLVWREZY9nZOIG41w3SfYvm4SE
+Hi3yYJ0wTsyEheIszx6e/jarM3c1RNg1lho9Nuh6DtjVR6FqaYvZ/Ls6rnla1fTWcbuakCNrmreI
+dIcMHl+5ni36q1Mr3Lt2PpNMCAiMHqIjHNRqrSK6mQEubWXLviRmVSRLQESxG9fhwoXA3hA/Pe24
+/PHxI1Pcv2WXb9n5QHGNfb2V1M6+oF4nI979ptAmDgAp6zxG8D1gvz9Q0twmQVGeFDdCBKNwV6gb
+h+0t+nvujArjqWaJGctB+d1ENmHP4ndGyH329JKBNv3bNPFyfvMMFr20FQIDAQABo0IwQDAdBgNV
+HQ4EFgQUBx/S55zawm6iQLSwelAQUHTEyL0wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC
+AQYwDQYJKoZIhvcNAQEFBQADggEBAIlXshZ6qML91tmbmzTCnLQyFE2npN/svqe++EPbkTfOtDIu
+UFUaNU52Q3Eg75N3ThVwLofDwR1t3Mu1J9QsVtFSUzpE0nPIxBsFZVpikpzuQY0x2+c06lkh1QF6
+12S4ZDnNye2v7UsDSKegmQGA3GWjNq5lWUhPgkvIZfFXHeVZLgo/bNjR9eUJtGxUAArgFU2HdW23
+WJZa3W3SAKD0m0i+wzekujbgfIeFlxoVot4uolu9rxj5kFDNcFn4J2dHy8egBzp90SxdbBk6ZrV9
+/ZFvgrG+CJPbFEfxojfHRZ48x3evZKiT3/Zpg4Jg8klCNO1aAFSFHBY2kgxc+qatv9s=
+-----END CERTIFICATE-----
+
+AffirmTrust Premium
+===================
+-----BEGIN CERTIFICATE-----
+MIIFRjCCAy6gAwIBAgIIbYwURrGmCu4wDQYJKoZIhvcNAQEMBQAwQTELMAkGA1UEBhMCVVMxFDAS
+BgNVBAoMC0FmZmlybVRydXN0MRwwGgYDVQQDDBNBZmZpcm1UcnVzdCBQcmVtaXVtMB4XDTEwMDEy
+OTE0MTAzNloXDTQwMTIzMTE0MTAzNlowQTELMAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRy
+dXN0MRwwGgYDVQQDDBNBZmZpcm1UcnVzdCBQcmVtaXVtMIICIjANBgkqhkiG9w0BAQEFAAOCAg8A
+MIICCgKCAgEAxBLfqV/+Qd3d9Z+K4/as4Tx4mrzY8H96oDMq3I0gW64tb+eT2TZwamjPjlGjhVtn
+BKAQJG9dKILBl1fYSCkTtuG+kU3fhQxTGJoeJKJPj/CihQvL9Cl/0qRY7iZNyaqoe5rZ+jjeRFcV
+5fiMyNlI4g0WJx0eyIOFJbe6qlVBzAMiSy2RjYvmia9mx+n/K+k8rNrSs8PhaJyJ+HoAVt70VZVs
++7pk3WKL3wt3MutizCaam7uqYoNMtAZ6MMgpv+0GTZe5HMQxK9VfvFMSF5yZVylmd2EhMQcuJUmd
+GPLu8ytxjLW6OQdJd/zvLpKQBY0tL3d770O/Nbua2Plzpyzy0FfuKE4mX4+QaAkvuPjcBukumj5R
+p9EixAqnOEhss/n/fauGV+O61oV4d7pD6kh/9ti+I20ev9E2bFhc8e6kGVQa9QPSdubhjL08s9NI
+S+LI+H+SqHZGnEJlPqQewQcDWkYtuJfzt9WyVSHvutxMAJf7FJUnM7/oQ0dG0giZFmA7mn7S5u04
+6uwBHjxIVkkJx0w3AJ6IDsBz4W9m6XJHMD4Q5QsDyZpCAGzFlH5hxIrff4IaC1nEWTJ3s7xgaVY5
+/bQGeyzWZDbZvUjthB9+pSKPKrhC9IK31FOQeE4tGv2Bb0TXOwF0lkLgAOIua+rF7nKsu7/+6qqo
++Nz2snmKtmcCAwEAAaNCMEAwHQYDVR0OBBYEFJ3AZ6YMItkm9UWrpmVSESfYRaxjMA8GA1UdEwEB
+/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBDAUAA4ICAQCzV00QYk465KzquByv
+MiPIs0laUZx2KI15qldGF9X1Uva3ROgIRL8YhNILgM3FEv0AVQVhh0HctSSePMTYyPtwni94loMg
+Nt58D2kTiKV1NpgIpsbfrM7jWNa3Pt668+s0QNiigfV4Py/VpfzZotReBA4Xrf5B8OWycvpEgjNC
+6C1Y91aMYj+6QrCcDFx+LmUmXFNPALJ4fqENmS2NuB2OosSw/WDQMKSOyARiqcTtNd56l+0OOF6S
+L5Nwpamcb6d9Ex1+xghIsV5n61EIJenmJWtSKZGc0jlzCFfemQa0W50QBuHCAKi4HEoCChTQwUHK
++4w1IX2COPKpVJEZNZOUbWo6xbLQu4mGk+ibyQ86p3q4ofB4Rvr8Ny/lioTz3/4E2aFooC8k4gmV
+BtWVyuEklut89pMFu+1z6S3RdTnX5yTb2E5fQ4+e0BQ5v1VwSJlXMbSc7kqYA5YwH2AG7hsj/oFg
+IxpHYoWlzBk0gG+zrBrjn/B7SK3VAdlntqlyk+otZrWyuOQ9PLLvTIzq6we/qzWaVYa8GKa1qF60
+g2xraUDTn9zxw2lrueFtCfTxqlB2Cnp9ehehVZZCmTEJ3WARjQUwfuaORtGdFNrHF+QFlozEJLUb
+zxQHskD4o55BhrwE0GuWyCqANP2/7waj3VjFhT0+j/6eKeC2uAloGRwYQw==
+-----END CERTIFICATE-----
+
+AffirmTrust Premium ECC
+=======================
+-----BEGIN CERTIFICATE-----
+MIIB/jCCAYWgAwIBAgIIdJclisc/elQwCgYIKoZIzj0EAwMwRTELMAkGA1UEBhMCVVMxFDASBgNV
+BAoMC0FmZmlybVRydXN0MSAwHgYDVQQDDBdBZmZpcm1UcnVzdCBQcmVtaXVtIEVDQzAeFw0xMDAx
+MjkxNDIwMjRaFw00MDEyMzExNDIwMjRaMEUxCzAJBgNVBAYTAlVTMRQwEgYDVQQKDAtBZmZpcm1U
+cnVzdDEgMB4GA1UEAwwXQWZmaXJtVHJ1c3QgUHJlbWl1bSBFQ0MwdjAQBgcqhkjOPQIBBgUrgQQA
+IgNiAAQNMF4bFZ0D0KF5Nbc6PJJ6yhUczWLznCZcBz3lVPqj1swS6vQUX+iOGasvLkjmrBhDeKzQ
+N8O9ss0s5kfiGuZjuD0uL3jET9v0D6RoTFVya5UdThhClXjMNzyR4ptlKymjQjBAMB0GA1UdDgQW
+BBSaryl6wBE1NSZRMADDav5A1a7WPDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAK
+BggqhkjOPQQDAwNnADBkAjAXCfOHiFBar8jAQr9HX/VsaobgxCd05DhT1wV/GzTjxi+zygk8N53X
+57hG8f2h4nECMEJZh0PUUd+60wkyWs6Iflc9nF9Ca/UHLbXwgpP5WW+uZPpY5Yse42O+tYHNbwKM
+eQ==
+-----END CERTIFICATE-----
+
+Certum Trusted Network CA
+=========================
+-----BEGIN CERTIFICATE-----
+MIIDuzCCAqOgAwIBAgIDBETAMA0GCSqGSIb3DQEBBQUAMH4xCzAJBgNVBAYTAlBMMSIwIAYDVQQK
+ExlVbml6ZXRvIFRlY2hub2xvZ2llcyBTLkEuMScwJQYDVQQLEx5DZXJ0dW0gQ2VydGlmaWNhdGlv
+biBBdXRob3JpdHkxIjAgBgNVBAMTGUNlcnR1bSBUcnVzdGVkIE5ldHdvcmsgQ0EwHhcNMDgxMDIy
+MTIwNzM3WhcNMjkxMjMxMTIwNzM3WjB+MQswCQYDVQQGEwJQTDEiMCAGA1UEChMZVW5pemV0byBU
+ZWNobm9sb2dpZXMgUy5BLjEnMCUGA1UECxMeQ2VydHVtIENlcnRpZmljYXRpb24gQXV0aG9yaXR5
+MSIwIAYDVQQDExlDZXJ0dW0gVHJ1c3RlZCBOZXR3b3JrIENBMIIBIjANBgkqhkiG9w0BAQEFAAOC
+AQ8AMIIBCgKCAQEA4/t9o3K6wvDJFIf1awFO4W5AB7ptJ11/91sts1rHUV+rpDKmYYe2bg+G0jAC
+l/jXaVehGDldamR5xgFZrDwxSjh80gTSSyjoIF87B6LMTXPb865Px1bVWqeWifrzq2jUI4ZZJ88J
+J7ysbnKDHDBy3+Ci6dLhdHUZvSqeexVUBBvXQzmtVSjF4hq79MDkrjhJM8x2hZ85RdKknvISjFH4
+fOQtf/WsX+sWn7Et0brMkUJ3TCXJkDhv2/DM+44el1k+1WBO5gUo7Ul5E0u6SNsv+XLTOcr+H9g0
+cvW0QM8xAcPs3hEtF10fuFDRXhmnad4HMyjKUJX5p1TLVIZQRan5SQIDAQABo0IwQDAPBgNVHRMB
+Af8EBTADAQH/MB0GA1UdDgQWBBQIds3LB/8k9sXN7buQvOKEN0Z19zAOBgNVHQ8BAf8EBAMCAQYw
+DQYJKoZIhvcNAQEFBQADggEBAKaorSLOAT2mo/9i0Eidi15ysHhE49wcrwn9I0j6vSrEuVUEtRCj
+jSfeC4Jj0O7eDDd5QVsisrCaQVymcODU0HfLI9MA4GxWL+FpDQ3Zqr8hgVDZBqWo/5U30Kr+4rP1
+mS1FhIrlQgnXdAIv94nYmem8J9RHjboNRhx3zxSkHLmkMcScKHQDNP8zGSal6Q10tz6XxnboJ5aj
+Zt3hrvJBW8qYVoNzcOSGGtIxQbovvi0TWnZvTuhOgQ4/WwMioBK+ZlgRSssDxLQqKi2WF+A5VLxI
+03YnnZotBqbJ7DnSq9ufmgsnAjUpsUCV5/nonFWIGUbWtzT1fs45mtk48VH3Tyw=
+-----END CERTIFICATE-----
+
+Certinomis - Autorité Racine
+=============================
+-----BEGIN CERTIFICATE-----
+MIIFnDCCA4SgAwIBAgIBATANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJGUjETMBEGA1UEChMK
+Q2VydGlub21pczEXMBUGA1UECxMOMDAwMiA0MzM5OTg5MDMxJjAkBgNVBAMMHUNlcnRpbm9taXMg
+LSBBdXRvcml0w6kgUmFjaW5lMB4XDTA4MDkxNzA4Mjg1OVoXDTI4MDkxNzA4Mjg1OVowYzELMAkG
+A1UEBhMCRlIxEzARBgNVBAoTCkNlcnRpbm9taXMxFzAVBgNVBAsTDjAwMDIgNDMzOTk4OTAzMSYw
+JAYDVQQDDB1DZXJ0aW5vbWlzIC0gQXV0b3JpdMOpIFJhY2luZTCCAiIwDQYJKoZIhvcNAQEBBQAD
+ggIPADCCAgoCggIBAJ2Fn4bT46/HsmtuM+Cet0I0VZ35gb5j2CN2DpdUzZlMGvE5x4jYF1AMnmHa
+wE5V3udauHpOd4cN5bjr+p5eex7Ezyh0x5P1FMYiKAT5kcOrJ3NqDi5N8y4oH3DfVS9O7cdxbwly
+Lu3VMpfQ8Vh30WC8Tl7bmoT2R2FFK/ZQpn9qcSdIhDWerP5pqZ56XjUl+rSnSTV3lqc2W+HN3yNw
+2F1MpQiD8aYkOBOo7C+ooWfHpi2GR+6K/OybDnT0K0kCe5B1jPyZOQE51kqJ5Z52qz6WKDgmi92N
+jMD2AR5vpTESOH2VwnHu7XSu5DaiQ3XV8QCb4uTXzEIDS3h65X27uK4uIJPT5GHfceF2Z5c/tt9q
+c1pkIuVC28+BA5PY9OMQ4HL2AHCs8MF6DwV/zzRpRbWT5BnbUhYjBYkOjUjkJW+zeL9i9Qf6lSTC
+lrLooyPCXQP8w9PlfMl1I9f09bze5N/NgL+RiH2nE7Q5uiy6vdFrzPOlKO1Enn1So2+WLhl+HPNb
+xxaOu2B9d2ZHVIIAEWBsMsGoOBvrbpgT1u449fCfDu/+MYHB0iSVL1N6aaLwD4ZFjliCK0wi1F6g
+530mJ0jfJUaNSih8hp75mxpZuWW/Bd22Ql095gBIgl4g9xGC3srYn+Y3RyYe63j3YcNBZFgCQfna
+4NH4+ej9Uji29YnfAgMBAAGjWzBZMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0G
+A1UdDgQWBBQNjLZh2kS40RR9w759XkjwzspqsDAXBgNVHSAEEDAOMAwGCiqBegFWAgIAAQEwDQYJ
+KoZIhvcNAQEFBQADggIBACQ+YAZ+He86PtvqrxyaLAEL9MW12Ukx9F1BjYkMTv9sov3/4gbIOZ/x
+WqndIlgVqIrTseYyCYIDbNc/CMf4uboAbbnW/FIyXaR/pDGUu7ZMOH8oMDX/nyNTt7buFHAAQCva
+R6s0fl6nVjBhK4tDrP22iCj1a7Y+YEq6QpA0Z43q619FVDsXrIvkxmUP7tCMXWY5zjKn2BCXwH40
+nJ+U8/aGH88bc62UeYdocMMzpXDn2NU4lG9jeeu/Cg4I58UvD0KgKxRA/yHgBcUn4YQRE7rWhh1B
+CxMjidPJC+iKunqjo3M3NYB9Ergzd0A4wPpeMNLytqOx1qKVl4GbUu1pTP+A5FPbVFsDbVRfsbjv
+JL1vnxHDx2TCDyhihWZeGnuyt++uNckZM6i4J9szVb9o4XVIRFb7zdNIu0eJOqxp9YDG5ERQL1TE
+qkPFMTFYvZbF6nVsmnWxTfj3l/+WFvKXTej28xH5On2KOG4Ey+HTRRWqpdEdnV1j6CTmNhTih60b
+WfVEm/vXd3wfAXBioSAaosUaKPQhA+4u2cGA6rnZgtZbdsLLO7XSAPCjDuGtbkD326C00EauFddE
+wk01+dIL8hf2rGbVJLJP0RyZwG71fet0BLj5TXcJ17TPBzAJ8bgAVtkXFhYKK4bfjwEZGuW7gmP/
+vgt2Fl43N+bYdJeimUV5
+-----END CERTIFICATE-----
+
+Root CA Generalitat Valenciana
+==============================
+-----BEGIN CERTIFICATE-----
+MIIGizCCBXOgAwIBAgIEO0XlaDANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJFUzEfMB0GA1UE
+ChMWR2VuZXJhbGl0YXQgVmFsZW5jaWFuYTEPMA0GA1UECxMGUEtJR1ZBMScwJQYDVQQDEx5Sb290
+IENBIEdlbmVyYWxpdGF0IFZhbGVuY2lhbmEwHhcNMDEwNzA2MTYyMjQ3WhcNMjEwNzAxMTUyMjQ3
+WjBoMQswCQYDVQQGEwJFUzEfMB0GA1UEChMWR2VuZXJhbGl0YXQgVmFsZW5jaWFuYTEPMA0GA1UE
+CxMGUEtJR1ZBMScwJQYDVQQDEx5Sb290IENBIEdlbmVyYWxpdGF0IFZhbGVuY2lhbmEwggEiMA0G
+CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDGKqtXETcvIorKA3Qdyu0togu8M1JAJke+WmmmO3I2
+F0zo37i7L3bhQEZ0ZQKQUgi0/6iMweDHiVYQOTPvaLRfX9ptI6GJXiKjSgbwJ/BXufjpTjJ3Cj9B
+ZPPrZe52/lSqfR0grvPXdMIKX/UIKFIIzFVd0g/bmoGlu6GzwZTNVOAydTGRGmKy3nXiz0+J2ZGQ
+D0EbtFpKd71ng+CT516nDOeB0/RSrFOyA8dEJvt55cs0YFAQexvba9dHq198aMpunUEDEO5rmXte
+JajCq+TA81yc477OMUxkHl6AovWDfgzWyoxVjr7gvkkHD6MkQXpYHYTqWBLI4bft75PelAgxAgMB
+AAGjggM7MIIDNzAyBggrBgEFBQcBAQQmMCQwIgYIKwYBBQUHMAGGFmh0dHA6Ly9vY3NwLnBraS5n
+dmEuZXMwEgYDVR0TAQH/BAgwBgEB/wIBAjCCAjQGA1UdIASCAiswggInMIICIwYKKwYBBAG/VQIB
+ADCCAhMwggHoBggrBgEFBQcCAjCCAdoeggHWAEEAdQB0AG8AcgBpAGQAYQBkACAAZABlACAAQwBl
+AHIAdABpAGYAaQBjAGEAYwBpAPMAbgAgAFIAYQDtAHoAIABkAGUAIABsAGEAIABHAGUAbgBlAHIA
+YQBsAGkAdABhAHQAIABWAGEAbABlAG4AYwBpAGEAbgBhAC4ADQAKAEwAYQAgAEQAZQBjAGwAYQBy
+AGEAYwBpAPMAbgAgAGQAZQAgAFAAcgDhAGMAdABpAGMAYQBzACAAZABlACAAQwBlAHIAdABpAGYA
+aQBjAGEAYwBpAPMAbgAgAHEAdQBlACAAcgBpAGcAZQAgAGUAbAAgAGYAdQBuAGMAaQBvAG4AYQBt
+AGkAZQBuAHQAbwAgAGQAZQAgAGwAYQAgAHAAcgBlAHMAZQBuAHQAZQAgAEEAdQB0AG8AcgBpAGQA
+YQBkACAAZABlACAAQwBlAHIAdABpAGYAaQBjAGEAYwBpAPMAbgAgAHMAZQAgAGUAbgBjAHUAZQBu
+AHQAcgBhACAAZQBuACAAbABhACAAZABpAHIAZQBjAGMAaQDzAG4AIAB3AGUAYgAgAGgAdAB0AHAA
+OgAvAC8AdwB3AHcALgBwAGsAaQAuAGcAdgBhAC4AZQBzAC8AYwBwAHMwJQYIKwYBBQUHAgEWGWh0
+dHA6Ly93d3cucGtpLmd2YS5lcy9jcHMwHQYDVR0OBBYEFHs100DSHHgZZu90ECjcPk+yeAT8MIGV
+BgNVHSMEgY0wgYqAFHs100DSHHgZZu90ECjcPk+yeAT8oWykajBoMQswCQYDVQQGEwJFUzEfMB0G
+A1UEChMWR2VuZXJhbGl0YXQgVmFsZW5jaWFuYTEPMA0GA1UECxMGUEtJR1ZBMScwJQYDVQQDEx5S
+b290IENBIEdlbmVyYWxpdGF0IFZhbGVuY2lhbmGCBDtF5WgwDQYJKoZIhvcNAQEFBQADggEBACRh
+TvW1yEICKrNcda3FbcrnlD+laJWIwVTAEGmiEi8YPyVQqHxK6sYJ2fR1xkDar1CdPaUWu20xxsdz
+Ckj+IHLtb8zog2EWRpABlUt9jppSCS/2bxzkoXHPjCpaF3ODR00PNvsETUlR4hTJZGH71BTg9J63
+NI8KJr2XXPR5OkowGcytT6CYirQxlyric21+eLj4iIlPsSKRZEv1UN4D2+XFducTZnV+ZfsBn5OH
+iJ35Rld8TWCvmHMTI6QgkYH60GFmuH3Rr9ZvHmw96RH9qfmCIoaZM3Fa6hlXPZHNqcCjbgcTpsnt
++GijnsNacgmHKNHEc8RzGF9QdRYxn7fofMM=
+-----END CERTIFICATE-----
+
+TWCA Root Certification Authority
+=================================
+-----BEGIN CERTIFICATE-----
+MIIDezCCAmOgAwIBAgIBATANBgkqhkiG9w0BAQUFADBfMQswCQYDVQQGEwJUVzESMBAGA1UECgwJ
+VEFJV0FOLUNBMRAwDgYDVQQLDAdSb290IENBMSowKAYDVQQDDCFUV0NBIFJvb3QgQ2VydGlmaWNh
+dGlvbiBBdXRob3JpdHkwHhcNMDgwODI4MDcyNDMzWhcNMzAxMjMxMTU1OTU5WjBfMQswCQYDVQQG
+EwJUVzESMBAGA1UECgwJVEFJV0FOLUNBMRAwDgYDVQQLDAdSb290IENBMSowKAYDVQQDDCFUV0NB
+IFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
+AoIBAQCwfnK4pAOU5qfeCTiRShFAh6d8WWQUe7UREN3+v9XAu1bihSX0NXIP+FPQQeFEAcK0HMMx
+QhZHhTMidrIKbw/lJVBPhYa+v5guEGcevhEFhgWQxFnQfHgQsIBct+HHK3XLfJ+utdGdIzdjp9xC
+oi2SBBtQwXu4PhvJVgSLL1KbralW6cH/ralYhzC2gfeXRfwZVzsrb+RH9JlF/h3x+JejiB03HFyP
+4HYlmlD4oFT/RJB2I9IyxsOrBr/8+7/zrX2SYgJbKdM1o5OaQ2RgXbL6Mv87BK9NQGr5x+PvI/1r
+y+UPizgN7gr8/g+YnzAx3WxSZfmLgb4i4RxYA7qRG4kHAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIB
+BjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRqOFsmjd6LWvJPelSDGRjjCDWmujANBgkqhkiG
+9w0BAQUFAAOCAQEAPNV3PdrfibqHDAhUaiBQkr6wQT25JmSDCi/oQMCXKCeCMErJk/9q56YAf4lC
+mtYR5VPOL8zy2gXE/uJQxDqGfczafhAJO5I1KlOy/usrBdlsXebQ79NqZp4VKIV66IIArB6nCWlW
+QtNoURi+VJq/REG6Sb4gumlc7rh3zc5sH62Dlhh9DrUUOYTxKOkto557HnpyWoOzeW/vtPzQCqVY
+T0bf+215WfKEIlKuD8z7fDvnaspHYcN6+NOSBB+4IIThNlQWx0DeO4pz3N/GCUzf7Nr/1FNCocny
+Yh0igzyXxfkZYiesZSLX0zzG5Y6yU8xJzrww/nsOM5D77dIUkR8Hrw==
+-----END CERTIFICATE-----
+
+Security Communication RootCA2
+==============================
+-----BEGIN CERTIFICATE-----
+MIIDdzCCAl+gAwIBAgIBADANBgkqhkiG9w0BAQsFADBdMQswCQYDVQQGEwJKUDElMCMGA1UEChMc
+U0VDT00gVHJ1c3QgU3lzdGVtcyBDTy4sTFRELjEnMCUGA1UECxMeU2VjdXJpdHkgQ29tbXVuaWNh
+dGlvbiBSb290Q0EyMB4XDTA5MDUyOTA1MDAzOVoXDTI5MDUyOTA1MDAzOVowXTELMAkGA1UEBhMC
+SlAxJTAjBgNVBAoTHFNFQ09NIFRydXN0IFN5c3RlbXMgQ08uLExURC4xJzAlBgNVBAsTHlNlY3Vy
+aXR5IENvbW11bmljYXRpb24gUm9vdENBMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
+ANAVOVKxUrO6xVmCxF1SrjpDZYBLx/KWvNs2l9amZIyoXvDjChz335c9S672XewhtUGrzbl+dp++
++T42NKA7wfYxEUV0kz1XgMX5iZnK5atq1LXaQZAQwdbWQonCv/Q4EpVMVAX3NuRFg3sUZdbcDE3R
+3n4MqzvEFb46VqZab3ZpUql6ucjrappdUtAtCms1FgkQhNBqyjoGADdH5H5XTz+L62e4iKrFvlNV
+spHEfbmwhRkGeC7bYRr6hfVKkaHnFtWOojnflLhwHyg/i/xAXmODPIMqGplrz95Zajv8bxbXH/1K
+EOtOghY6rCcMU/Gt1SSwawNQwS08Ft1ENCcadfsCAwEAAaNCMEAwHQYDVR0OBBYEFAqFqXdlBZh8
+QIH4D5csOPEK7DzPMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEB
+CwUAA4IBAQBMOqNErLlFsceTfsgLCkLfZOoc7llsCLqJX2rKSpWeeo8HxdpFcoJxDjrSzG+ntKEj
+u/Ykn8sX/oymzsLS28yN/HH8AynBbF0zX2S2ZTuJbxh2ePXcokgfGT+Ok+vx+hfuzU7jBBJV1uXk
+3fs+BXziHV7Gp7yXT2g69ekuCkO2r1dcYmh8t/2jioSgrGK+KwmHNPBqAbubKVY8/gA3zyNs8U6q
+tnRGEmyR7jTV7JqR50S+kDFy1UkC9gLl9B/rfNmWVan/7Ir5mUf/NVoCqgTLiluHcSmRvaS0eg29
+mvVXIwAHIRc/SjnRBUkLp7Y3gaVdjKozXoEofKd9J+sAro03
+-----END CERTIFICATE-----
+
+EC-ACC
+======
+-----BEGIN CERTIFICATE-----
+MIIFVjCCBD6gAwIBAgIQ7is969Qh3hSoYqwE893EATANBgkqhkiG9w0BAQUFADCB8zELMAkGA1UE
+BhMCRVMxOzA5BgNVBAoTMkFnZW5jaWEgQ2F0YWxhbmEgZGUgQ2VydGlmaWNhY2lvIChOSUYgUS0w
+ODAxMTc2LUkpMSgwJgYDVQQLEx9TZXJ2ZWlzIFB1YmxpY3MgZGUgQ2VydGlmaWNhY2lvMTUwMwYD
+VQQLEyxWZWdldSBodHRwczovL3d3dy5jYXRjZXJ0Lm5ldC92ZXJhcnJlbCAoYykwMzE1MDMGA1UE
+CxMsSmVyYXJxdWlhIEVudGl0YXRzIGRlIENlcnRpZmljYWNpbyBDYXRhbGFuZXMxDzANBgNVBAMT
+BkVDLUFDQzAeFw0wMzAxMDcyMzAwMDBaFw0zMTAxMDcyMjU5NTlaMIHzMQswCQYDVQQGEwJFUzE7
+MDkGA1UEChMyQWdlbmNpYSBDYXRhbGFuYSBkZSBDZXJ0aWZpY2FjaW8gKE5JRiBRLTA4MDExNzYt
+SSkxKDAmBgNVBAsTH1NlcnZlaXMgUHVibGljcyBkZSBDZXJ0aWZpY2FjaW8xNTAzBgNVBAsTLFZl
+Z2V1IGh0dHBzOi8vd3d3LmNhdGNlcnQubmV0L3ZlcmFycmVsIChjKTAzMTUwMwYDVQQLEyxKZXJh
+cnF1aWEgRW50aXRhdHMgZGUgQ2VydGlmaWNhY2lvIENhdGFsYW5lczEPMA0GA1UEAxMGRUMtQUND
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsyLHT+KXQpWIR4NA9h0X84NzJB5R85iK
+w5K4/0CQBXCHYMkAqbWUZRkiFRfCQ2xmRJoNBD45b6VLeqpjt4pEndljkYRm4CgPukLjbo73FCeT
+ae6RDqNfDrHrZqJyTxIThmV6PttPB/SnCWDaOkKZx7J/sxaVHMf5NLWUhdWZXqBIoH7nF2W4onW4
+HvPlQn2v7fOKSGRdghST2MDk/7NQcvJ29rNdQlB50JQ+awwAvthrDk4q7D7SzIKiGGUzE3eeml0a
+E9jD2z3Il3rucO2n5nzbcc8tlGLfbdb1OL4/pYUKGbio2Al1QnDE6u/LDsg0qBIimAy4E5S2S+zw
+0JDnJwIDAQABo4HjMIHgMB0GA1UdEQQWMBSBEmVjX2FjY0BjYXRjZXJ0Lm5ldDAPBgNVHRMBAf8E
+BTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUoMOLRKo3pUW/l4Ba0fF4opvpXY0wfwYD
+VR0gBHgwdjB0BgsrBgEEAfV4AQMBCjBlMCwGCCsGAQUFBwIBFiBodHRwczovL3d3dy5jYXRjZXJ0
+Lm5ldC92ZXJhcnJlbDA1BggrBgEFBQcCAjApGidWZWdldSBodHRwczovL3d3dy5jYXRjZXJ0Lm5l
+dC92ZXJhcnJlbCAwDQYJKoZIhvcNAQEFBQADggEBAKBIW4IB9k1IuDlVNZyAelOZ1Vr/sXE7zDkJ
+lF7W2u++AVtd0x7Y/X1PzaBB4DSTv8vihpw3kpBWHNzrKQXlxJ7HNd+KDM3FIUPpqojlNcAZQmNa
+Al6kSBg6hW/cnbw/nZzBh7h6YQjpdwt/cKt63dmXLGQehb+8dJahw3oS7AwaboMMPOhyRp/7SNVe
+l+axofjk70YllJyJ22k4vuxcDlbHZVHlUIiIv0LVKz3l+bqeLrPK9HOSAgu+TGbrIP65y7WZf+a2
+E/rKS03Z7lNGBjvGTq2TWoF+bCpLagVFjPIhpDGQh2xlnJ2lYJU6Un/10asIbvPuW/mIPX64b24D
+5EI=
+-----END CERTIFICATE-----
+
+Hellenic Academic and Research Institutions RootCA 2011
+=======================================================
+-----BEGIN CERTIFICATE-----
+MIIEMTCCAxmgAwIBAgIBADANBgkqhkiG9w0BAQUFADCBlTELMAkGA1UEBhMCR1IxRDBCBgNVBAoT
+O0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgQ2VydC4gQXV0aG9y
+aXR5MUAwPgYDVQQDEzdIZWxsZW5pYyBBY2FkZW1pYyBhbmQgUmVzZWFyY2ggSW5zdGl0dXRpb25z
+IFJvb3RDQSAyMDExMB4XDTExMTIwNjEzNDk1MloXDTMxMTIwMTEzNDk1MlowgZUxCzAJBgNVBAYT
+AkdSMUQwQgYDVQQKEztIZWxsZW5pYyBBY2FkZW1pYyBhbmQgUmVzZWFyY2ggSW5zdGl0dXRpb25z
+IENlcnQuIEF1dGhvcml0eTFAMD4GA1UEAxM3SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2VhcmNo
+IEluc3RpdHV0aW9ucyBSb290Q0EgMjAxMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
+AKlTAOMupvaO+mDYLZU++CwqVE7NuYRhlFhPjz2L5EPzdYmNUeTDN9KKiE15HrcS3UN4SoqS5tdI
+1Q+kOilENbgH9mgdVc04UfCMJDGFr4PJfel3r+0ae50X+bOdOFAPplp5kYCvN66m0zH7tSYJnTxa
+71HFK9+WXesyHgLacEnsbgzImjeN9/E2YEsmLIKe0HjzDQ9jpFEw4fkrJxIH2Oq9GGKYsFk3fb7u
+8yBRQlqD75O6aRXxYp2fmTmCobd0LovUxQt7L/DICto9eQqakxylKHJzkUOap9FNhYS5qXSPFEDH
+3N6sQWRstBmbAmNtJGSPRLIl6s5ddAxjMlyNh+UCAwEAAaOBiTCBhjAPBgNVHRMBAf8EBTADAQH/
+MAsGA1UdDwQEAwIBBjAdBgNVHQ4EFgQUppFC/RNhSiOeCKQp5dgTBCPuQSUwRwYDVR0eBEAwPqA8
+MAWCAy5ncjAFggMuZXUwBoIELmVkdTAGggQub3JnMAWBAy5ncjAFgQMuZXUwBoEELmVkdTAGgQQu
+b3JnMA0GCSqGSIb3DQEBBQUAA4IBAQAf73lB4XtuP7KMhjdCSk4cNx6NZrokgclPEg8hwAOXhiVt
+XdMiKahsog2p6z0GW5k6x8zDmjR/qw7IThzh+uTczQ2+vyT+bOdrwg3IBp5OjWEopmr95fZi6hg8
+TqBTnbI6nOulnJEWtk2C4AwFSKls9cz4y51JtPACpf1wA+2KIaWuE4ZJwzNzvoc7dIsXRSZMFpGD
+/md9zU1jZ/rzAxKWeAaNsWftjj++n08C9bMJL/NMh98qy5V8AcysNnq/onN694/BtZqhFLKPM58N
+7yLcZnuEvUUXBj08yrl3NI/K6s8/MT7jiOOASSXIl7WdmplNsDz4SgCbZN2fOUvRJ9e4
+-----END CERTIFICATE-----
+
+Actalis Authentication Root CA
+==============================
+-----BEGIN CERTIFICATE-----
+MIIFuzCCA6OgAwIBAgIIVwoRl0LE48wwDQYJKoZIhvcNAQELBQAwazELMAkGA1UEBhMCSVQxDjAM
+BgNVBAcMBU1pbGFuMSMwIQYDVQQKDBpBY3RhbGlzIFMucC5BLi8wMzM1ODUyMDk2NzEnMCUGA1UE
+AwweQWN0YWxpcyBBdXRoZW50aWNhdGlvbiBSb290IENBMB4XDTExMDkyMjExMjIwMloXDTMwMDky
+MjExMjIwMlowazELMAkGA1UEBhMCSVQxDjAMBgNVBAcMBU1pbGFuMSMwIQYDVQQKDBpBY3RhbGlz
+IFMucC5BLi8wMzM1ODUyMDk2NzEnMCUGA1UEAwweQWN0YWxpcyBBdXRoZW50aWNhdGlvbiBSb290
+IENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAp8bEpSmkLO/lGMWwUKNvUTufClrJ
+wkg4CsIcoBh/kbWHuUA/3R1oHwiD1S0eiKD4j1aPbZkCkpAW1V8IbInX4ay8IMKx4INRimlNAJZa
+by/ARH6jDuSRzVju3PvHHkVH3Se5CAGfpiEd9UEtL0z9KK3giq0itFZljoZUj5NDKd45RnijMCO6
+zfB9E1fAXdKDa0hMxKufgFpbOr3JpyI/gCczWw63igxdBzcIy2zSekciRDXFzMwujt0q7bd9Zg1f
+YVEiVRvjRuPjPdA1YprbrxTIW6HMiRvhMCb8oJsfgadHHwTrozmSBp+Z07/T6k9QnBn+locePGX2
+oxgkg4YQ51Q+qDp2JE+BIcXjDwL4k5RHILv+1A7TaLndxHqEguNTVHnd25zS8gebLra8Pu2Fbe8l
+EfKXGkJh90qX6IuxEAf6ZYGyojnP9zz/GPvG8VqLWeICrHuS0E4UT1lF9gxeKF+w6D9Fz8+vm2/7
+hNN3WpVvrJSEnu68wEqPSpP4RCHiMUVhUE4Q2OM1fEwZtN4Fv6MGn8i1zeQf1xcGDXqVdFUNaBr8
+EBtiZJ1t4JWgw5QHVw0U5r0F+7if5t+L4sbnfpb2U8WANFAoWPASUHEXMLrmeGO89LKtmyuy/uE5
+jF66CyCU3nuDuP/jVo23Eek7jPKxwV2dpAtMK9myGPW1n0sCAwEAAaNjMGEwHQYDVR0OBBYEFFLY
+iDrIn3hm7YnzezhwlMkCAjbQMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUUtiIOsifeGbt
+ifN7OHCUyQICNtAwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBCwUAA4ICAQALe3KHwGCmSUyI
+WOYdiPcUZEim2FgKDk8TNd81HdTtBjHIgT5q1d07GjLukD0R0i70jsNjLiNmsGe+b7bAEzlgqqI0
+JZN1Ut6nna0Oh4lScWoWPBkdg/iaKWW+9D+a2fDzWochcYBNy+A4mz+7+uAwTc+G02UQGRjRlwKx
+K3JCaKygvU5a2hi/a5iB0P2avl4VSM0RFbnAKVy06Ij3Pjaut2L9HmLecHgQHEhb2rykOLpn7VU+
+Xlff1ANATIGk0k9jpwlCCRT8AKnCgHNPLsBA2RF7SOp6AsDT6ygBJlh0wcBzIm2Tlf05fbsq4/aC
+4yyXX04fkZT6/iyj2HYauE2yOE+b+h1IYHkm4vP9qdCa6HCPSXrW5b0KDtst842/6+OkfcvHlXHo
+2qN8xcL4dJIEG4aspCJTQLas/kx2z/uUMsA1n3Y/buWQbqCmJqK4LL7RK4X9p2jIugErsWx0Hbhz
+lefut8cl8ABMALJ+tguLHPPAUJ4lueAI3jZm/zel0btUZCzJJ7VLkn5l/9Mt4blOvH+kQSGQQXem
+OR/qnuOf0GZvBeyqdn6/axag67XH/JJULysRJyU3eExRarDzzFhdFPFqSBX/wge2sY0PjlxQRrM9
+vwGYT7JZVEc+NHt4bVaTLnPqZih4zR0Uv6CPLy64Lo7yFIrM6bV8+2ydDKXhlg==
+-----END CERTIFICATE-----
+
+Trustis FPS Root CA
+===================
+-----BEGIN CERTIFICATE-----
+MIIDZzCCAk+gAwIBAgIQGx+ttiD5JNM2a/fH8YygWTANBgkqhkiG9w0BAQUFADBFMQswCQYDVQQG
+EwJHQjEYMBYGA1UEChMPVHJ1c3RpcyBMaW1pdGVkMRwwGgYDVQQLExNUcnVzdGlzIEZQUyBSb290
+IENBMB4XDTAzMTIyMzEyMTQwNloXDTI0MDEyMTExMzY1NFowRTELMAkGA1UEBhMCR0IxGDAWBgNV
+BAoTD1RydXN0aXMgTGltaXRlZDEcMBoGA1UECxMTVHJ1c3RpcyBGUFMgUm9vdCBDQTCCASIwDQYJ
+KoZIhvcNAQEBBQADggEPADCCAQoCggEBAMVQe547NdDfxIzNjpvto8A2mfRC6qc+gIMPpqdZh8mQ
+RUN+AOqGeSoDvT03mYlmt+WKVoaTnGhLaASMk5MCPjDSNzoiYYkchU59j9WvezX2fihHiTHcDnlk
+H5nSW7r+f2C/revnPDgpai/lkQtV/+xvWNUtyd5MZnGPDNcE2gfmHhjjvSkCqPoc4Vu5g6hBSLwa
+cY3nYuUtsuvffM/bq1rKMfFMIvMFE/eC+XN5DL7XSxzA0RU8k0Fk0ea+IxciAIleH2ulrG6nS4zt
+o3Lmr2NNL4XSFDWaLk6M6jKYKIahkQlBOrTh4/L68MkKokHdqeMDx4gVOxzUGpTXn2RZEm0CAwEA
+AaNTMFEwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBS6+nEleYtXQSUhhgtx67JkDoshZzAd
+BgNVHQ4EFgQUuvpxJXmLV0ElIYYLceuyZA6LIWcwDQYJKoZIhvcNAQEFBQADggEBAH5Y//01GX2c
+GE+esCu8jowU/yyg2kdbw++BLa8F6nRIW/M+TgfHbcWzk88iNVy2P3UnXwmWzaD+vkAMXBJV+JOC
+yinpXj9WV4s4NvdFGkwozZ5BuO1WTISkQMi4sKUraXAEasP41BIy+Q7DsdwyhEQsb8tGD+pmQQ9P
+8Vilpg0ND2HepZ5dfWWhPBfnqFVO76DH7cZEf1T1o+CP8HxVIo8ptoGj4W1OLBuAZ+ytIJ8MYmHV
+l/9D7S3B2l0pKoU/rGXuhg8FjZBf3+6f9L/uHfuY5H+QK4R4EA5sSVPvFVtlRkpdr7r7OnIdzfYl
+iB6XzCGcKQENZetX2fNXlrtIzYE=
+-----END CERTIFICATE-----
+
+StartCom Certification Authority
+================================
+-----BEGIN CERTIFICATE-----
+MIIHhzCCBW+gAwIBAgIBLTANBgkqhkiG9w0BAQsFADB9MQswCQYDVQQGEwJJTDEWMBQGA1UEChMN
+U3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmlu
+ZzEpMCcGA1UEAxMgU3RhcnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDYwOTE3MTk0
+NjM3WhcNMzYwOTE3MTk0NjM2WjB9MQswCQYDVQQGEwJJTDEWMBQGA1UEChMNU3RhcnRDb20gTHRk
+LjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMg
+U3RhcnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw
+ggIKAoICAQDBiNsJvGxGfHiflXu1M5DycmLWwTYgIiRezul38kMKogZkpMyONvg45iPwbm2xPN1y
+o4UcodM9tDMr0y+v/uqwQVlntsQGfQqedIXWeUyAN3rfOQVSWff0G0ZDpNKFhdLDcfN1YjS6LIp/
+Ho/u7TTQEceWzVI9ujPW3U3eCztKS5/CJi/6tRYccjV3yjxd5srhJosaNnZcAdt0FCX+7bWgiA/d
+eMotHweXMAEtcnn6RtYTKqi5pquDSR3l8u/d5AGOGAqPY1MWhWKpDhk6zLVmpsJrdAfkK+F2PrRt
+2PZE4XNiHzvEvqBTViVsUQn3qqvKv3b9bZvzndu/PWa8DFaqr5hIlTpL36dYUNk4dalb6kMMAv+Z
+6+hsTXBbKWWc3apdzK8BMewM69KN6Oqce+Zu9ydmDBpI125C4z/eIT574Q1w+2OqqGwaVLRcJXrJ
+osmLFqa7LH4XXgVNWG4SHQHuEhANxjJ/GP/89PrNbpHoNkm+Gkhpi8KWTRoSsmkXwQqQ1vp5Iki/
+untp+HDH+no32NgN0nZPV/+Qt+OR0t3vwmC3Zzrd/qqc8NSLf3Iizsafl7b4r4qgEKjZ+xjGtrVc
+UjyJthkqcwEKDwOzEmDyei+B26Nu/yYwl/WL3YlXtq09s68rxbd2AvCl1iuahhQqcvbjM4xdCUsT
+37uMdBNSSwIDAQABo4ICEDCCAgwwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYD
+VR0OBBYEFE4L7xqkQFulF2mHMMo0aEPQQa7yMB8GA1UdIwQYMBaAFE4L7xqkQFulF2mHMMo0aEPQ
+Qa7yMIIBWgYDVR0gBIIBUTCCAU0wggFJBgsrBgEEAYG1NwEBATCCATgwLgYIKwYBBQUHAgEWImh0
+dHA6Ly93d3cuc3RhcnRzc2wuY29tL3BvbGljeS5wZGYwNAYIKwYBBQUHAgEWKGh0dHA6Ly93d3cu
+c3RhcnRzc2wuY29tL2ludGVybWVkaWF0ZS5wZGYwgc8GCCsGAQUFBwICMIHCMCcWIFN0YXJ0IENv
+bW1lcmNpYWwgKFN0YXJ0Q29tKSBMdGQuMAMCAQEagZZMaW1pdGVkIExpYWJpbGl0eSwgcmVhZCB0
+aGUgc2VjdGlvbiAqTGVnYWwgTGltaXRhdGlvbnMqIG9mIHRoZSBTdGFydENvbSBDZXJ0aWZpY2F0
+aW9uIEF1dGhvcml0eSBQb2xpY3kgYXZhaWxhYmxlIGF0IGh0dHA6Ly93d3cuc3RhcnRzc2wuY29t
+L3BvbGljeS5wZGYwEQYJYIZIAYb4QgEBBAQDAgAHMDgGCWCGSAGG+EIBDQQrFilTdGFydENvbSBG
+cmVlIFNTTCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTANBgkqhkiG9w0BAQsFAAOCAgEAjo/n3JR5
+fPGFf59Jb2vKXfuM/gTFwWLRfUKKvFO3lANmMD+x5wqnUCBVJX92ehQN6wQOQOY+2IirByeDqXWm
+N3PH/UvSTa0XQMhGvjt/UfzDtgUx3M2FIk5xt/JxXrAaxrqTi3iSSoX4eA+D/i+tLPfkpLst0OcN
+Org+zvZ49q5HJMqjNTbOx8aHmNrs++myziebiMMEofYLWWivydsQD032ZGNcpRJvkrKTlMeIFw6T
+tn5ii5B/q06f/ON1FE8qMt9bDeD1e5MNq6HPh+GlBEXoPBKlCcWw0bdT82AUuoVpaiF8H3VhFyAX
+e2w7QSlc4axa0c2Mm+tgHRns9+Ww2vl5GKVFP0lDV9LdJNUso/2RjSe15esUBppMeyG7Oq0wBhjA
+2MFrLH9ZXF2RsXAiV+uKa0hK1Q8p7MZAwC+ITGgBF3f0JBlPvfrhsiAhS90a2Cl9qrjeVOwhVYBs
+HvUwyKMQ5bLmKhQxw4UtjJixhlpPiVktucf3HMiKf8CdBUrmQk9io20ppB+Fq9vlgcitKj1MXVuE
+JnHEhV5xJMqlG2zYYdMa4FTbzrqpMrUi9nNBCV24F10OD5mQ1kfabwo6YigUZ4LZ8dCAWZvLMdib
+D4x3TrVoivJs9iQOLWxwxXPR3hTQcY+203sC9uO41Alua551hDnmfyWl8kgAwKQB2j8=
+-----END CERTIFICATE-----
+
+StartCom Certification Authority G2
+===================================
+-----BEGIN CERTIFICATE-----
+MIIFYzCCA0ugAwIBAgIBOzANBgkqhkiG9w0BAQsFADBTMQswCQYDVQQGEwJJTDEWMBQGA1UEChMN
+U3RhcnRDb20gTHRkLjEsMCoGA1UEAxMjU3RhcnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkg
+RzIwHhcNMTAwMTAxMDEwMDAxWhcNMzkxMjMxMjM1OTAxWjBTMQswCQYDVQQGEwJJTDEWMBQGA1UE
+ChMNU3RhcnRDb20gTHRkLjEsMCoGA1UEAxMjU3RhcnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3Jp
+dHkgRzIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC2iTZbB7cgNr2Cu+EWIAOVeq8O
+o1XJJZlKxdBWQYeQTSFgpBSHO839sj60ZwNq7eEPS8CRhXBF4EKe3ikj1AENoBB5uNsDvfOpL9HG
+4A/LnooUCri99lZi8cVytjIl2bLzvWXFDSxu1ZJvGIsAQRSCb0AgJnooD/Uefyf3lLE3PbfHkffi
+Aez9lInhzG7TNtYKGXmu1zSCZf98Qru23QumNK9LYP5/Q0kGi4xDuFby2X8hQxfqp0iVAXV16iul
+Q5XqFYSdCI0mblWbq9zSOdIxHWDirMxWRST1HFSr7obdljKF+ExP6JV2tgXdNiNnvP8V4so75qbs
+O+wmETRIjfaAKxojAuuKHDp2KntWFhxyKrOq42ClAJ8Em+JvHhRYW6Vsi1g8w7pOOlz34ZYrPu8H
+vKTlXcxNnw3h3Kq74W4a7I/htkxNeXJdFzULHdfBR9qWJODQcqhaX2YtENwvKhOuJv4KHBnM0D4L
+nMgJLvlblnpHnOl68wVQdJVznjAJ85eCXuaPOQgeWeU1FEIT/wCc976qUM/iUUjXuG+v+E5+M5iS
+FGI6dWPPe/regjupuznixL0sAA7IF6wT700ljtizkC+p2il9Ha90OrInwMEePnWjFqmveiJdnxMa
+z6eg6+OGCtP95paV1yPIN93EfKo2rJgaErHgTuixO/XWb/Ew1wIDAQABo0IwQDAPBgNVHRMBAf8E
+BTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUS8W0QGutHLOlHGVuRjaJhwUMDrYwDQYJ
+KoZIhvcNAQELBQADggIBAHNXPyzVlTJ+N9uWkusZXn5T50HsEbZH77Xe7XRcxfGOSeD8bpkTzZ+K
+2s06Ctg6Wgk/XzTQLwPSZh0avZyQN8gMjgdalEVGKua+etqhqaRpEpKwfTbURIfXUfEpY9Z1zRbk
+J4kd+MIySP3bmdCPX1R0zKxnNBFi2QwKN4fRoxdIjtIXHfbX/dtl6/2o1PXWT6RbdejF0mCy2wl+
+JYt7ulKSnj7oxXehPOBKc2thz4bcQ///If4jXSRK9dNtD2IEBVeC2m6kMyV5Sy5UGYvMLD0w6dEG
+/+gyRr61M3Z3qAFdlsHB1b6uJcDJHgoJIIihDsnzb02CVAAgp9KP5DlUFy6NHrgbuxu9mk47EDTc
+nIhT76IxW1hPkWLIwpqazRVdOKnWvvgTtZ8SafJQYqz7Fzf07rh1Z2AQ+4NQ+US1dZxAF7L+/Xld
+blhYXzD8AK6vM8EOTmy6p6ahfzLbOOCxchcKK5HsamMm7YnUeMx0HgX4a/6ManY5Ka5lIxKVCCIc
+l85bBu4M4ru8H0ST9tg4RQUh7eStqxK2A6RCLi3ECToDZ2mEmuFZkIoohdVddLHRDiBYmxOlsGOm
+7XtH/UVVMKTumtTm4ofvmMkyghEpIrwACjFeLQ/Ajulrso8uBtjRkcfGEvRM/TAXw8HaOFvjqerm
+obp573PYtlNXLfbQ4ddI
+-----END CERTIFICATE-----
+
+Buypass Class 2 Root CA
+=======================
+-----BEGIN CERTIFICATE-----
+MIIFWTCCA0GgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJOTzEdMBsGA1UECgwU
+QnV5cGFzcyBBUy05ODMxNjMzMjcxIDAeBgNVBAMMF0J1eXBhc3MgQ2xhc3MgMiBSb290IENBMB4X
+DTEwMTAyNjA4MzgwM1oXDTQwMTAyNjA4MzgwM1owTjELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1
+eXBhc3MgQVMtOTgzMTYzMzI3MSAwHgYDVQQDDBdCdXlwYXNzIENsYXNzIDIgUm9vdCBDQTCCAiIw
+DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANfHXvfBB9R3+0Mh9PT1aeTuMgHbo4Yf5FkNuud1
+g1Lr6hxhFUi7HQfKjK6w3Jad6sNgkoaCKHOcVgb/S2TwDCo3SbXlzwx87vFKu3MwZfPVL4O2fuPn
+9Z6rYPnT8Z2SdIrkHJasW4DptfQxh6NR/Md+oW+OU3fUl8FVM5I+GC911K2GScuVr1QGbNgGE41b
+/+EmGVnAJLqBcXmQRFBoJJRfuLMR8SlBYaNByyM21cHxMlAQTn/0hpPshNOOvEu/XAFOBz3cFIqU
+CqTqc/sLUegTBxj6DvEr0VQVfTzh97QZQmdiXnfgolXsttlpF9U6r0TtSsWe5HonfOV116rLJeff
+awrbD02TTqigzXsu8lkBarcNuAeBfos4GzjmCleZPe4h6KP1DBbdi+w0jpwqHAAVF41og9JwnxgI
+zRFo1clrUs3ERo/ctfPYV3Me6ZQ5BL/T3jjetFPsaRyifsSP5BtwrfKi+fv3FmRmaZ9JUaLiFRhn
+Bkp/1Wy1TbMz4GHrXb7pmA8y1x1LPC5aAVKRCfLf6o3YBkBjqhHk/sM3nhRSP/TizPJhk9H9Z2vX
+Uq6/aKtAQ6BXNVN48FP4YUIHZMbXb5tMOA1jrGKvNouicwoN9SG9dKpN6nIDSdvHXx1iY8f93ZHs
+M+71bbRuMGjeyNYmsHVee7QHIJihdjK4TWxPAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYD
+VR0OBBYEFMmAd+BikoL1RpzzuvdMw964o605MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsF
+AAOCAgEAU18h9bqwOlI5LJKwbADJ784g7wbylp7ppHR/ehb8t/W2+xUbP6umwHJdELFx7rxP462s
+A20ucS6vxOOto70MEae0/0qyexAQH6dXQbLArvQsWdZHEIjzIVEpMMpghq9Gqx3tOluwlN5E40EI
+osHsHdb9T7bWR9AUC8rmyrV7d35BH16Dx7aMOZawP5aBQW9gkOLo+fsicdl9sz1Gv7SEr5AcD48S
+aq/v7h56rgJKihcrdv6sVIkkLE8/trKnToyokZf7KcZ7XC25y2a2t6hbElGFtQl+Ynhw/qlqYLYd
+DnkM/crqJIByw5c/8nerQyIKx+u2DISCLIBrQYoIwOula9+ZEsuK1V6ADJHgJgg2SMX6OBE1/yWD
+LfJ6v9r9jv6ly0UsH8SIU653DtmadsWOLB2jutXsMq7Aqqz30XpN69QH4kj3Io6wpJ9qzo6ysmD0
+oyLQI+uUWnpp3Q+/QFesa1lQ2aOZ4W7+jQF5JyMV3pKdewlNWudLSDBaGOYKbeaP4NK75t98biGC
+wWg5TbSYWGZizEqQXsP6JwSxeRV0mcy+rSDeJmAc61ZRpqPq5KM/p/9h3PFaTWwyI0PurKju7koS
+CTxdccK+efrCh2gdC/1cacwG0Jp9VJkqyTkaGa9LKkPzY11aWOIv4x3kqdbQCtCev9eBCfHJxyYN
+rJgWVqA=
+-----END CERTIFICATE-----
+
+Buypass Class 3 Root CA
+=======================
+-----BEGIN CERTIFICATE-----
+MIIFWTCCA0GgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJOTzEdMBsGA1UECgwU
+QnV5cGFzcyBBUy05ODMxNjMzMjcxIDAeBgNVBAMMF0J1eXBhc3MgQ2xhc3MgMyBSb290IENBMB4X
+DTEwMTAyNjA4Mjg1OFoXDTQwMTAyNjA4Mjg1OFowTjELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1
+eXBhc3MgQVMtOTgzMTYzMzI3MSAwHgYDVQQDDBdCdXlwYXNzIENsYXNzIDMgUm9vdCBDQTCCAiIw
+DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAKXaCpUWUOOV8l6ddjEGMnqb8RB2uACatVI2zSRH
+sJ8YZLya9vrVediQYkwiL944PdbgqOkcLNt4EemOaFEVcsfzM4fkoF0LXOBXByow9c3EN3coTRiR
+5r/VUv1xLXA+58bEiuPwKAv0dpihi4dVsjoT/Lc+JzeOIuOoTyrvYLs9tznDDgFHmV0ST9tD+leh
+7fmdvhFHJlsTmKtdFoqwNxxXnUX/iJY2v7vKB3tvh2PX0DJq1l1sDPGzbjniazEuOQAnFN44wOwZ
+ZoYS6J1yFhNkUsepNxz9gjDthBgd9K5c/3ATAOux9TN6S9ZV+AWNS2mw9bMoNlwUxFFzTWsL8TQH
+2xc519woe2v1n/MuwU8XKhDzzMro6/1rqy6any2CbgTUUgGTLT2G/H783+9CHaZr77kgxve9oKeV
+/afmiSTYzIw0bOIjL9kSGiG5VZFvC5F5GQytQIgLcOJ60g7YaEi7ghM5EFjp2CoHxhLbWNvSO1UQ
+RwUVZ2J+GGOmRj8JDlQyXr8NYnon74Do29lLBlo3WiXQCBJ31G8JUJc9yB3D34xFMFbG02SrZvPA
+Xpacw8Tvw3xrizp5f7NJzz3iiZ+gMEuFuZyUJHmPfWupRWgPK9Dx2hzLabjKSWJtyNBjYt1gD1iq
+j6G8BaVmos8bdrKEZLFMOVLAMLrwjEsCsLa3AgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYD
+VR0OBBYEFEe4zf/lb+74suwvTg75JbCOPGvDMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsF
+AAOCAgEAACAjQTUEkMJAYmDv4jVM1z+s4jSQuKFvdvoWFqRINyzpkMLyPPgKn9iB5btb2iUspKdV
+cSQy9sgL8rxq+JOssgfCX5/bzMiKqr5qb+FJEMwx14C7u8jYog5kV+qi9cKpMRXSIGrs/CIBKM+G
+uIAeqcwRpTzyFrNHnfzSgCHEy9BHcEGhyoMZCCxt8l13nIoUE9Q2HJLw5QY33KbmkJs4j1xrG0aG
+Q0JfPgEHU1RdZX33inOhmlRaHylDFCfChQ+1iHsaO5S3HWCntZznKWlXWpuTekMwGwPXYshApqr8
+ZORK15FTAaggiG6cX0S5y2CBNOxv033aSF/rtJC8LakcC6wc1aJoIIAE1vyxjy+7SjENSoYc6+I2
+KSb12tjE8nVhz36udmNKekBlk4f4HoCMhuWG1o8O/FMsYOgWYRqiPkN7zTlgVGr18okmAWiDSKIz
+6MkEkbIRNBE+6tBDGR8Dk5AM/1E9V/RBbuHLoL7ryWPNbczk+DaqaJ3tvV2XcEQNtg413OEMXbug
+UZTLfhbrES+jkkXITHHZvMmZUldGL1DPvTVp9D0VzgalLA8+9oG6lLvDu79leNKGef9JOxqDDPDe
+eOzI8k1MGt6CKfjBWtrt7uYnXuhF0J0cUahoq0Tj0Itq4/g7u9xN12TyUb7mqqta6THuBrxzvxNi
+Cp/HuZc=
+-----END CERTIFICATE-----
+
+T-TeleSec GlobalRoot Class 3
+============================
+-----BEGIN CERTIFICATE-----
+MIIDwzCCAqugAwIBAgIBATANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoM
+IlQtU3lzdGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBU
+cnVzdCBDZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDMwHhcNMDgx
+MDAxMTAyOTU2WhcNMzMxMDAxMjM1OTU5WjCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoMIlQtU3lz
+dGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBD
+ZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDMwggEiMA0GCSqGSIb3
+DQEBAQUAA4IBDwAwggEKAoIBAQC9dZPwYiJvJK7genasfb3ZJNW4t/zN8ELg63iIVl6bmlQdTQyK
+9tPPcPRStdiTBONGhnFBSivwKixVA9ZIw+A5OO3yXDw/RLyTPWGrTs0NvvAgJ1gORH8EGoel15YU
+NpDQSXuhdfsaa3Ox+M6pCSzyU9XDFES4hqX2iys52qMzVNn6chr3IhUciJFrf2blw2qAsCTz34ZF
+iP0Zf3WHHx+xGwpzJFu5ZeAsVMhg02YXP+HMVDNzkQI6pn97djmiH5a2OK61yJN0HZ65tOVgnS9W
+0eDrXltMEnAMbEQgqxHY9Bn20pxSN+f6tsIxO0rUFJmtxxr1XV/6B7h8DR/Wgx6zAgMBAAGjQjBA
+MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBS1A/d2O2GCahKqGFPr
+AyGUv/7OyjANBgkqhkiG9w0BAQsFAAOCAQEAVj3vlNW92nOyWL6ukK2YJ5f+AbGwUgC4TeQbIXQb
+fsDuXmkqJa9c1h3a0nnJ85cp4IaH3gRZD/FZ1GSFS5mvJQQeyUapl96Cshtwn5z2r3Ex3XsFpSzT
+ucpH9sry9uetuUg/vBa3wW306gmv7PO15wWeph6KU1HWk4HMdJP2udqmJQV0eVp+QD6CSyYRMG7h
+P0HHRwA11fXT91Q+gT3aSWqas+8QPebrb9HIIkfLzM8BMZLZGOMivgkeGj5asuRrDFR6fUNOuIml
+e9eiPZaGzPImNC1qkp2aGtAw4l1OBLBfiyB+d8E9lYLRRpo7PHi4b6HQDWSieB4pTpPDpFQUWw==
+-----END CERTIFICATE-----
+
+EE Certification Centre Root CA
+===============================
+-----BEGIN CERTIFICATE-----
+MIIEAzCCAuugAwIBAgIQVID5oHPtPwBMyonY43HmSjANBgkqhkiG9w0BAQUFADB1MQswCQYDVQQG
+EwJFRTEiMCAGA1UECgwZQVMgU2VydGlmaXRzZWVyaW1pc2tlc2t1czEoMCYGA1UEAwwfRUUgQ2Vy
+dGlmaWNhdGlvbiBDZW50cmUgUm9vdCBDQTEYMBYGCSqGSIb3DQEJARYJcGtpQHNrLmVlMCIYDzIw
+MTAxMDMwMTAxMDMwWhgPMjAzMDEyMTcyMzU5NTlaMHUxCzAJBgNVBAYTAkVFMSIwIAYDVQQKDBlB
+UyBTZXJ0aWZpdHNlZXJpbWlza2Vza3VzMSgwJgYDVQQDDB9FRSBDZXJ0aWZpY2F0aW9uIENlbnRy
+ZSBSb290IENBMRgwFgYJKoZIhvcNAQkBFglwa2lAc2suZWUwggEiMA0GCSqGSIb3DQEBAQUAA4IB
+DwAwggEKAoIBAQDIIMDs4MVLqwd4lfNE7vsLDP90jmG7sWLqI9iroWUyeuuOF0+W2Ap7kaJjbMeM
+TC55v6kF/GlclY1i+blw7cNRfdCT5mzrMEvhvH2/UpvObntl8jixwKIy72KyaOBhU8E2lf/slLo2
+rpwcpzIP5Xy0xm90/XsY6KxX7QYgSzIwWFv9zajmofxwvI6Sc9uXp3whrj3B9UiHbCe9nyV0gVWw
+93X2PaRka9ZP585ArQ/dMtO8ihJTmMmJ+xAdTX7Nfh9WDSFwhfYggx/2uh8Ej+p3iDXE/+pOoYtN
+P2MbRMNE1CV2yreN1x5KZmTNXMWcg+HCCIia7E6j8T4cLNlsHaFLAgMBAAGjgYowgYcwDwYDVR0T
+AQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFBLyWj7qVhy/zQas8fElyalL1BSZ
+MEUGA1UdJQQ+MDwGCCsGAQUFBwMCBggrBgEFBQcDAQYIKwYBBQUHAwMGCCsGAQUFBwMEBggrBgEF
+BQcDCAYIKwYBBQUHAwkwDQYJKoZIhvcNAQEFBQADggEBAHv25MANqhlHt01Xo/6tu7Fq1Q+e2+Rj
+xY6hUFaTlrg4wCQiZrxTFGGVv9DHKpY5P30osxBAIWrEr7BSdxjhlthWXePdNl4dp1BUoMUq5KqM
+lIpPnTX/dqQGE5Gion0ARD9V04I8GtVbvFZMIi5GQ4okQC3zErg7cBqklrkar4dBGmoYDQZPxz5u
+uSlNDUmJEYcyW+ZLBMjkXOZ0c5RdFpgTlf7727FE5TpwrDdr5rMzcijJs1eg9gIWiAYLtqZLICjU
+3j2LrTcFU3T+bsy8QxdxXvnFzBqpYe73dgzzcvRyrc9yAjYHR8/vGVCJYMzpJJUPwssd8m92kMfM
+dcGWxZ0=
+-----END CERTIFICATE-----
+
+TURKTRUST Certificate Services Provider Root 2007
+=================================================
+-----BEGIN CERTIFICATE-----
+MIIEPTCCAyWgAwIBAgIBATANBgkqhkiG9w0BAQUFADCBvzE/MD0GA1UEAww2VMOcUktUUlVTVCBF
+bGVrdHJvbmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxMQswCQYDVQQGEwJUUjEP
+MA0GA1UEBwwGQW5rYXJhMV4wXAYDVQQKDFVUw5xSS1RSVVNUIEJpbGdpIMSwbGV0acWfaW0gdmUg
+QmlsacWfaW0gR8O8dmVubGnEn2kgSGl6bWV0bGVyaSBBLsWeLiAoYykgQXJhbMSxayAyMDA3MB4X
+DTA3MTIyNTE4MzcxOVoXDTE3MTIyMjE4MzcxOVowgb8xPzA9BgNVBAMMNlTDnFJLVFJVU1QgRWxl
+a3Ryb25payBTZXJ0aWZpa2EgSGl6bWV0IFNhxJ9sYXnEsWPEsXPEsTELMAkGA1UEBhMCVFIxDzAN
+BgNVBAcMBkFua2FyYTFeMFwGA1UECgxVVMOcUktUUlVTVCBCaWxnaSDEsGxldGnFn2ltIHZlIEJp
+bGnFn2ltIEfDvHZlbmxpxJ9pIEhpem1ldGxlcmkgQS7Fni4gKGMpIEFyYWzEsWsgMjAwNzCCASIw
+DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKu3PgqMyKVYFeaK7yc9SrToJdPNM8Ig3BnuiD9N
+YvDdE3ePYakqtdTyuTFYKTsvP2qcb3N2Je40IIDu6rfwxArNK4aUyeNgsURSsloptJGXg9i3phQv
+KUmi8wUG+7RP2qFsmmaf8EMJyupyj+sA1zU511YXRxcw9L6/P8JorzZAwan0qafoEGsIiveGHtya
+KhUG9qPw9ODHFNRRf8+0222vR5YXm3dx2KdxnSQM9pQ/hTEST7ruToK4uT6PIzdezKKqdfcYbwnT
+rqdUKDT74eA7YH2gvnmJhsifLfkKS8RQouf9eRbHegsYz85M733WB2+Y8a+xwXrXgTW4qhe04MsC
+AwEAAaNCMEAwHQYDVR0OBBYEFCnFkKslrxHkYb+j/4hhkeYO/pyBMA4GA1UdDwEB/wQEAwIBBjAP
+BgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBBQUAA4IBAQAQDdr4Ouwo0RSVgrESLFF6QSU2TJ/s
+Px+EnWVUXKgWAkD6bho3hO9ynYYKVZ1WKKxmLNA6VpM0ByWtCLCPyA8JWcqdmBzlVPi5RX9ql2+I
+aE1KBiY3iAIOtsbWcpnOa3faYjGkVh+uX4132l32iPwa2Z61gfAyuOOI0JzzaqC5mxRZNTZPz/OO
+Xl0XrRWV2N2y1RVuAE6zS89mlOTgzbUF2mNXi+WzqtvALhyQRNsaXRik7r4EW5nVcV9VZWRi1aKb
+BFmGyGJ353yCRWo9F7/snXUMrqNvWtMvmDb08PUZqxFdyKbjKlhqQgnDvZImZjINXQhVdP+MmNAK
+poRq0Tl9
+-----END CERTIFICATE-----
+
+D-TRUST Root Class 3 CA 2 2009
+==============================
+-----BEGIN CERTIFICATE-----
+MIIEMzCCAxugAwIBAgIDCYPzMA0GCSqGSIb3DQEBCwUAME0xCzAJBgNVBAYTAkRFMRUwEwYDVQQK
+DAxELVRydXN0IEdtYkgxJzAlBgNVBAMMHkQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgMjAwOTAe
+Fw0wOTExMDUwODM1NThaFw0yOTExMDUwODM1NThaME0xCzAJBgNVBAYTAkRFMRUwEwYDVQQKDAxE
+LVRydXN0IEdtYkgxJzAlBgNVBAMMHkQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgMjAwOTCCASIw
+DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANOySs96R+91myP6Oi/WUEWJNTrGa9v+2wBoqOAD
+ER03UAifTUpolDWzU9GUY6cgVq/eUXjsKj3zSEhQPgrfRlWLJ23DEE0NkVJD2IfgXU42tSHKXzlA
+BF9bfsyjxiupQB7ZNoTWSPOSHjRGICTBpFGOShrvUD9pXRl/RcPHAY9RySPocq60vFYJfxLLHLGv
+KZAKyVXMD9O0Gu1HNVpK7ZxzBCHQqr0ME7UAyiZsxGsMlFqVlNpQmvH/pStmMaTJOKDfHR+4CS7z
+p+hnUquVH+BGPtikw8paxTGA6Eian5Rp/hnd2HN8gcqW3o7tszIFZYQ05ub9VxC1X3a/L7AQDcUC
+AwEAAaOCARowggEWMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFP3aFMSfMN4hvR5COfyrYyNJ
+4PGEMA4GA1UdDwEB/wQEAwIBBjCB0wYDVR0fBIHLMIHIMIGAoH6gfIZ6bGRhcDovL2RpcmVjdG9y
+eS5kLXRydXN0Lm5ldC9DTj1ELVRSVVNUJTIwUm9vdCUyMENsYXNzJTIwMyUyMENBJTIwMiUyMDIw
+MDksTz1ELVRydXN0JTIwR21iSCxDPURFP2NlcnRpZmljYXRlcmV2b2NhdGlvbmxpc3QwQ6BBoD+G
+PWh0dHA6Ly93d3cuZC10cnVzdC5uZXQvY3JsL2QtdHJ1c3Rfcm9vdF9jbGFzc18zX2NhXzJfMjAw
+OS5jcmwwDQYJKoZIhvcNAQELBQADggEBAH+X2zDI36ScfSF6gHDOFBJpiBSVYEQBrLLpME+bUMJm
+2H6NMLVwMeniacfzcNsgFYbQDfC+rAF1hM5+n02/t2A7nPPKHeJeaNijnZflQGDSNiH+0LS4F9p0
+o3/U37CYAqxva2ssJSRyoWXuJVrl5jLn8t+rSfrzkGkj2wTZ51xY/GXUl77M/C4KzCUqNQT4YJEV
+dT1B/yMfGchs64JTBKbkTCJNjYy6zltz7GRUUG3RnFX7acM2w4y8PIWmawomDeCTmGCufsYkl4ph
+X5GOZpIJhzbNi5stPvZR1FDUWSi9g/LMKHtThm3YJohw1+qRzT65ysCQblrGXnRl11z+o+I=
+-----END CERTIFICATE-----
+
+D-TRUST Root Class 3 CA 2 EV 2009
+=================================
+-----BEGIN CERTIFICATE-----
+MIIEQzCCAyugAwIBAgIDCYP0MA0GCSqGSIb3DQEBCwUAMFAxCzAJBgNVBAYTAkRFMRUwEwYDVQQK
+DAxELVRydXN0IEdtYkgxKjAoBgNVBAMMIUQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgRVYgMjAw
+OTAeFw0wOTExMDUwODUwNDZaFw0yOTExMDUwODUwNDZaMFAxCzAJBgNVBAYTAkRFMRUwEwYDVQQK
+DAxELVRydXN0IEdtYkgxKjAoBgNVBAMMIUQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgRVYgMjAw
+OTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJnxhDRwui+3MKCOvXwEz75ivJn9gpfS
+egpnljgJ9hBOlSJzmY3aFS3nBfwZcyK3jpgAvDw9rKFs+9Z5JUut8Mxk2og+KbgPCdM03TP1YtHh
+zRnp7hhPTFiu4h7WDFsVWtg6uMQYZB7jM7K1iXdODL/ZlGsTl28So/6ZqQTMFexgaDbtCHu39b+T
+7WYxg4zGcTSHThfqr4uRjRxWQa4iN1438h3Z0S0NL2lRp75mpoo6Kr3HGrHhFPC+Oh25z1uxav60
+sUYgovseO3Dvk5h9jHOW8sXvhXCtKSb8HgQ+HKDYD8tSg2J87otTlZCpV6LqYQXY+U3EJ/pure35
+11H3a6UCAwEAAaOCASQwggEgMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFNOUikxiEyoZLsyv
+cop9NteaHNxnMA4GA1UdDwEB/wQEAwIBBjCB3QYDVR0fBIHVMIHSMIGHoIGEoIGBhn9sZGFwOi8v
+ZGlyZWN0b3J5LmQtdHJ1c3QubmV0L0NOPUQtVFJVU1QlMjBSb290JTIwQ2xhc3MlMjAzJTIwQ0El
+MjAyJTIwRVYlMjAyMDA5LE89RC1UcnVzdCUyMEdtYkgsQz1ERT9jZXJ0aWZpY2F0ZXJldm9jYXRp
+b25saXN0MEagRKBChkBodHRwOi8vd3d3LmQtdHJ1c3QubmV0L2NybC9kLXRydXN0X3Jvb3RfY2xh
+c3NfM19jYV8yX2V2XzIwMDkuY3JsMA0GCSqGSIb3DQEBCwUAA4IBAQA07XtaPKSUiO8aEXUHL7P+
+PPoeUSbrh/Yp3uDx1MYkCenBz1UbtDDZzhr+BlGmFaQt77JLvyAoJUnRpjZ3NOhk31KxEcdzes05
+nsKtjHEh8lprr988TlWvsoRlFIm5d8sqMb7Po23Pb0iUMkZv53GMoKaEGTcH8gNFCSuGdXzfX2lX
+ANtu2KZyIktQ1HWYVt+3GP9DQ1CuekR78HlR10M9p9OB0/DJT7naxpeG0ILD5EJt/rDiZE4OJudA
+NCa1CInXCGNjOCd1HjPqbqjdn5lPdE2BiYBL3ZqXKVwvvoFBuYz/6n1gBp7N1z3TLqMVvKjmJuVv
+w9y4AyHqnxbxLFS1
+-----END CERTIFICATE-----
+
+PSCProcert
+==========
+-----BEGIN CERTIFICATE-----
+MIIJhjCCB26gAwIBAgIBCzANBgkqhkiG9w0BAQsFADCCAR4xPjA8BgNVBAMTNUF1dG9yaWRhZCBk
+ZSBDZXJ0aWZpY2FjaW9uIFJhaXogZGVsIEVzdGFkbyBWZW5lem9sYW5vMQswCQYDVQQGEwJWRTEQ
+MA4GA1UEBxMHQ2FyYWNhczEZMBcGA1UECBMQRGlzdHJpdG8gQ2FwaXRhbDE2MDQGA1UEChMtU2lz
+dGVtYSBOYWNpb25hbCBkZSBDZXJ0aWZpY2FjaW9uIEVsZWN0cm9uaWNhMUMwQQYDVQQLEzpTdXBl
+cmludGVuZGVuY2lhIGRlIFNlcnZpY2lvcyBkZSBDZXJ0aWZpY2FjaW9uIEVsZWN0cm9uaWNhMSUw
+IwYJKoZIhvcNAQkBFhZhY3JhaXpAc3VzY2VydGUuZ29iLnZlMB4XDTEwMTIyODE2NTEwMFoXDTIw
+MTIyNTIzNTk1OVowgdExJjAkBgkqhkiG9w0BCQEWF2NvbnRhY3RvQHByb2NlcnQubmV0LnZlMQ8w
+DQYDVQQHEwZDaGFjYW8xEDAOBgNVBAgTB01pcmFuZGExKjAoBgNVBAsTIVByb3ZlZWRvciBkZSBD
+ZXJ0aWZpY2Fkb3MgUFJPQ0VSVDE2MDQGA1UEChMtU2lzdGVtYSBOYWNpb25hbCBkZSBDZXJ0aWZp
+Y2FjaW9uIEVsZWN0cm9uaWNhMQswCQYDVQQGEwJWRTETMBEGA1UEAxMKUFNDUHJvY2VydDCCAiIw
+DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANW39KOUM6FGqVVhSQ2oh3NekS1wwQYalNo97BVC
+wfWMrmoX8Yqt/ICV6oNEolt6Vc5Pp6XVurgfoCfAUFM+jbnADrgV3NZs+J74BCXfgI8Qhd19L3uA
+3VcAZCP4bsm+lU/hdezgfl6VzbHvvnpC2Mks0+saGiKLt38GieU89RLAu9MLmV+QfI4tL3czkkoh
+RqipCKzx9hEC2ZUWno0vluYC3XXCFCpa1sl9JcLB/KpnheLsvtF8PPqv1W7/U0HU9TI4seJfxPmO
+EO8GqQKJ/+MMbpfg353bIdD0PghpbNjU5Db4g7ayNo+c7zo3Fn2/omnXO1ty0K+qP1xmk6wKImG2
+0qCZyFSTXai20b1dCl53lKItwIKOvMoDKjSuc/HUtQy9vmebVOvh+qBa7Dh+PsHMosdEMXXqP+UH
+0quhJZb25uSgXTcYOWEAM11G1ADEtMo88aKjPvM6/2kwLkDd9p+cJsmWN63nOaK/6mnbVSKVUyqU
+td+tFjiBdWbjxywbk5yqjKPK2Ww8F22c3HxT4CAnQzb5EuE8XL1mv6JpIzi4mWCZDlZTOpx+FIyw
+Bm/xhnaQr/2v/pDGj59/i5IjnOcVdo/Vi5QTcmn7K2FjiO/mpF7moxdqWEfLcU8UC17IAggmosvp
+r2uKGcfLFFb14dq12fy/czja+eevbqQ34gcnAgMBAAGjggMXMIIDEzASBgNVHRMBAf8ECDAGAQH/
+AgEBMDcGA1UdEgQwMC6CD3N1c2NlcnRlLmdvYi52ZaAbBgVghl4CAqASDBBSSUYtRy0yMDAwNDAz
+Ni0wMB0GA1UdDgQWBBRBDxk4qpl/Qguk1yeYVKIXTC1RVDCCAVAGA1UdIwSCAUcwggFDgBStuyId
+xuDSAaj9dlBSk+2YwU2u06GCASakggEiMIIBHjE+MDwGA1UEAxM1QXV0b3JpZGFkIGRlIENlcnRp
+ZmljYWNpb24gUmFpeiBkZWwgRXN0YWRvIFZlbmV6b2xhbm8xCzAJBgNVBAYTAlZFMRAwDgYDVQQH
+EwdDYXJhY2FzMRkwFwYDVQQIExBEaXN0cml0byBDYXBpdGFsMTYwNAYDVQQKEy1TaXN0ZW1hIE5h
+Y2lvbmFsIGRlIENlcnRpZmljYWNpb24gRWxlY3Ryb25pY2ExQzBBBgNVBAsTOlN1cGVyaW50ZW5k
+ZW5jaWEgZGUgU2VydmljaW9zIGRlIENlcnRpZmljYWNpb24gRWxlY3Ryb25pY2ExJTAjBgkqhkiG
+9w0BCQEWFmFjcmFpekBzdXNjZXJ0ZS5nb2IudmWCAQowDgYDVR0PAQH/BAQDAgEGME0GA1UdEQRG
+MESCDnByb2NlcnQubmV0LnZloBUGBWCGXgIBoAwMClBTQy0wMDAwMDKgGwYFYIZeAgKgEgwQUklG
+LUotMzE2MzUzNzMtNzB2BgNVHR8EbzBtMEagRKBChkBodHRwOi8vd3d3LnN1c2NlcnRlLmdvYi52
+ZS9sY3IvQ0VSVElGSUNBRE8tUkFJWi1TSEEzODRDUkxERVIuY3JsMCOgIaAfhh1sZGFwOi8vYWNy
+YWl6LnN1c2NlcnRlLmdvYi52ZTA3BggrBgEFBQcBAQQrMCkwJwYIKwYBBQUHMAGGG2h0dHA6Ly9v
+Y3NwLnN1c2NlcnRlLmdvYi52ZTBBBgNVHSAEOjA4MDYGBmCGXgMBAjAsMCoGCCsGAQUFBwIBFh5o
+dHRwOi8vd3d3LnN1c2NlcnRlLmdvYi52ZS9kcGMwDQYJKoZIhvcNAQELBQADggIBACtZ6yKZu4Sq
+T96QxtGGcSOeSwORR3C7wJJg7ODU523G0+1ng3dS1fLld6c2suNUvtm7CpsR72H0xpkzmfWvADmN
+g7+mvTV+LFwxNG9s2/NkAZiqlCxB3RWGymspThbASfzXg0gTB1GEMVKIu4YXx2sviiCtxQuPcD4q
+uxtxj7mkoP3YldmvWb8lK5jpY5MvYB7Eqvh39YtsL+1+LrVPQA3uvFd359m21D+VJzog1eWuq2w1
+n8GhHVnchIHuTQfiSLaeS5UtQbHh6N5+LwUeaO6/u5BlOsju6rEYNxxik6SgMexxbJHmpHmJWhSn
+FFAFTKQAVzAswbVhltw+HoSvOULP5dAssSS830DD7X9jSr3hTxJkhpXzsOfIt+FTvZLm8wyWuevo
+5pLtp4EJFAv8lXrPj9Y0TzYS3F7RNHXGRoAvlQSMx4bEqCaJqD8Zm4G7UaRKhqsLEQ+xrmNTbSjq
+3TNWOByyrYDT13K9mmyZY+gAu0F2BbdbmRiKw7gSXFbPVgx96OLP7bx0R/vu0xdOIk9W/1DzLuY5
+poLWccret9W6aAjtmcz9opLLabid+Qqkpj5PkygqYWwHJgD/ll9ohri4zspV4KuxPX+Y1zMOWj3Y
+eMLEYC/HYvBhkdI4sPaeVdtAgAUSM84dkpvRabP/v/GSCmE1P93+hvS84Bpxs2Km
+-----END CERTIFICATE-----
+
+China Internet Network Information Center EV Certificates Root
+==============================================================
+-----BEGIN CERTIFICATE-----
+MIID9zCCAt+gAwIBAgIESJ8AATANBgkqhkiG9w0BAQUFADCBijELMAkGA1UEBhMCQ04xMjAwBgNV
+BAoMKUNoaW5hIEludGVybmV0IE5ldHdvcmsgSW5mb3JtYXRpb24gQ2VudGVyMUcwRQYDVQQDDD5D
+aGluYSBJbnRlcm5ldCBOZXR3b3JrIEluZm9ybWF0aW9uIENlbnRlciBFViBDZXJ0aWZpY2F0ZXMg
+Um9vdDAeFw0xMDA4MzEwNzExMjVaFw0zMDA4MzEwNzExMjVaMIGKMQswCQYDVQQGEwJDTjEyMDAG
+A1UECgwpQ2hpbmEgSW50ZXJuZXQgTmV0d29yayBJbmZvcm1hdGlvbiBDZW50ZXIxRzBFBgNVBAMM
+PkNoaW5hIEludGVybmV0IE5ldHdvcmsgSW5mb3JtYXRpb24gQ2VudGVyIEVWIENlcnRpZmljYXRl
+cyBSb290MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAm35z7r07eKpkQ0H1UN+U8i6y
+jUqORlTSIRLIOTJCBumD1Z9S7eVnAztUwYyZmczpwA//DdmEEbK40ctb3B75aDFk4Zv6dOtouSCV
+98YPjUesWgbdYavi7NifFy2cyjw1l1VxzUOFsUcW9SxTgHbP0wBkvUCZ3czY28Sf1hNfQYOL+Q2H
+klY0bBoQCxfVWhyXWIQ8hBouXJE0bhlffxdpxWXvayHG1VA6v2G5BY3vbzQ6sm8UY78WO5upKv23
+KzhmBsUs4qpnHkWnjQRmQvaPK++IIGmPMowUc9orhpFjIpryp9vOiYurXccUwVswah+xt54ugQEC
+7c+WXmPbqOY4twIDAQABo2MwYTAfBgNVHSMEGDAWgBR8cks5x8DbYqVPm6oYNJKiyoOCWTAPBgNV
+HRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUfHJLOcfA22KlT5uqGDSSosqD
+glkwDQYJKoZIhvcNAQEFBQADggEBACrDx0M3j92tpLIM7twUbY8opJhJywyA6vPtI2Z1fcXTIWd5
+0XPFtQO3WKwMVC/GVhMPMdoG52U7HW8228gd+f2ABsqjPWYWqJ1MFn3AlUa1UeTiH9fqBk1jjZaM
+7+czV0I664zBechNdn3e9rG3geCg+aF4RhcaVpjwTj2rHO3sOdwHSPdj/gauwqRcalsyiMXHM4Ws
+ZkJHwlgkmeHlPuV1LI5D1l08eB6olYIpUNHRFrrvwb562bTYzB5MRuF3sTGrvSrIzo9uoV1/A3U0
+5K2JRVRevq4opbs/eHnrc7MKDf2+yfdWrPa37S+bISnHOLaVxATywy39FCqQmbkHzJ8=
+-----END CERTIFICATE-----
+
+Swisscom Root CA 2
+==================
+-----BEGIN CERTIFICATE-----
+MIIF2TCCA8GgAwIBAgIQHp4o6Ejy5e/DfEoeWhhntjANBgkqhkiG9w0BAQsFADBkMQswCQYDVQQG
+EwJjaDERMA8GA1UEChMIU3dpc3Njb20xJTAjBgNVBAsTHERpZ2l0YWwgQ2VydGlmaWNhdGUgU2Vy
+dmljZXMxGzAZBgNVBAMTElN3aXNzY29tIFJvb3QgQ0EgMjAeFw0xMTA2MjQwODM4MTRaFw0zMTA2
+MjUwNzM4MTRaMGQxCzAJBgNVBAYTAmNoMREwDwYDVQQKEwhTd2lzc2NvbTElMCMGA1UECxMcRGln
+aXRhbCBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczEbMBkGA1UEAxMSU3dpc3Njb20gUm9vdCBDQSAyMIIC
+IjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAlUJOhJ1R5tMJ6HJaI2nbeHCOFvErjw0DzpPM
+LgAIe6szjPTpQOYXTKueuEcUMncy3SgM3hhLX3af+Dk7/E6J2HzFZ++r0rk0X2s682Q2zsKwzxNo
+ysjL67XiPS4h3+os1OD5cJZM/2pYmLcX5BtS5X4HAB1f2uY+lQS3aYg5oUFgJWFLlTloYhyxCwWJ
+wDaCFCE/rtuh/bxvHGCGtlOUSbkrRsVPACu/obvLP+DHVxxX6NZp+MEkUp2IVd3Chy50I9AU/SpH
+Wrumnf2U5NGKpV+GY3aFy6//SSj8gO1MedK75MDvAe5QQQg1I3ArqRa0jG6F6bYRzzHdUyYb3y1a
+SgJA/MTAtukxGggo5WDDH8SQjhBiYEQN7Aq+VRhxLKX0srwVYv8c474d2h5Xszx+zYIdkeNL6yxS
+NLCK/RJOlrDrcH+eOfdmQrGrrFLadkBXeyq96G4DsguAhYidDMfCd7Camlf0uPoTXGiTOmekl9Ab
+mbeGMktg2M7v0Ax/lZ9vh0+Hio5fCHyqW/xavqGRn1V9TrALacywlKinh/LTSlDcX3KwFnUey7QY
+Ypqwpzmqm59m2I2mbJYV4+by+PGDYmy7Velhk6M99bFXi08jsJvllGov34zflVEpYKELKeRcVVi3
+qPyZ7iVNTA6z00yPhOgpD/0QVAKFyPnlw4vP5w8CAwEAAaOBhjCBgzAOBgNVHQ8BAf8EBAMCAYYw
+HQYDVR0hBBYwFDASBgdghXQBUwIBBgdghXQBUwIBMBIGA1UdEwEB/wQIMAYBAf8CAQcwHQYDVR0O
+BBYEFE0mICKJS9PVpAqhb97iEoHF8TwuMB8GA1UdIwQYMBaAFE0mICKJS9PVpAqhb97iEoHF8Twu
+MA0GCSqGSIb3DQEBCwUAA4ICAQAyCrKkG8t9voJXiblqf/P0wS4RfbgZPnm3qKhyN2abGu2sEzsO
+v2LwnN+ee6FTSA5BesogpxcbtnjsQJHzQq0Qw1zv/2BZf82Fo4s9SBwlAjxnffUy6S8w5X2lejjQ
+82YqZh6NM4OKb3xuqFp1mrjX2lhIREeoTPpMSQpKwhI3qEAMw8jh0FcNlzKVxzqfl9NX+Ave5XLz
+o9v/tdhZsnPdTSpxsrpJ9csc1fV5yJmz/MFMdOO0vSk3FQQoHt5FRnDsr7p4DooqzgB53MBfGWcs
+a0vvaGgLQ+OswWIJ76bdZWGgr4RVSJFSHMYlkSrQwSIjYVmvRRGFHQEkNI/Ps/8XciATwoCqISxx
+OQ7Qj1zB09GOInJGTB2Wrk9xseEFKZZZ9LuedT3PDTcNYtsmjGOpI99nBjx8Oto0QuFmtEYE3saW
+mA9LSHokMnWRn6z3aOkquVVlzl1h0ydw2Df+n7mvoC5Wt6NlUe07qxS/TFED6F+KBZvuim6c779o
++sjaC+NCydAXFJy3SuCvkychVSa1ZC+N8f+mQAWFBVzKBxlcCxMoTFh/wqXvRdpg065lYZ1Tg3TC
+rvJcwhbtkj6EPnNgiLx29CzP0H1907he0ZESEOnN3col49XtmS++dYFLJPlFRpTJKSFTnCZFqhMX
+5OfNeOI5wSsSnqaeG8XmDtkx2Q==
+-----END CERTIFICATE-----
+
+Swisscom Root EV CA 2
+=====================
+-----BEGIN CERTIFICATE-----
+MIIF4DCCA8igAwIBAgIRAPL6ZOJ0Y9ON/RAdBB92ylgwDQYJKoZIhvcNAQELBQAwZzELMAkGA1UE
+BhMCY2gxETAPBgNVBAoTCFN3aXNzY29tMSUwIwYDVQQLExxEaWdpdGFsIENlcnRpZmljYXRlIFNl
+cnZpY2VzMR4wHAYDVQQDExVTd2lzc2NvbSBSb290IEVWIENBIDIwHhcNMTEwNjI0MDk0NTA4WhcN
+MzEwNjI1MDg0NTA4WjBnMQswCQYDVQQGEwJjaDERMA8GA1UEChMIU3dpc3Njb20xJTAjBgNVBAsT
+HERpZ2l0YWwgQ2VydGlmaWNhdGUgU2VydmljZXMxHjAcBgNVBAMTFVN3aXNzY29tIFJvb3QgRVYg
+Q0EgMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMT3HS9X6lds93BdY7BxUglgRCgz
+o3pOCvrY6myLURYaVa5UJsTMRQdBTxB5f3HSek4/OE6zAMaVylvNwSqD1ycfMQ4jFrclyxy0uYAy
+Xhqdk/HoPGAsp15XGVhRXrwsVgu42O+LgrQ8uMIkqBPHoCE2G3pXKSinLr9xJZDzRINpUKTk4Rti
+GZQJo/PDvO/0vezbE53PnUgJUmfANykRHvvSEaeFGHR55E+FFOtSN+KxRdjMDUN/rhPSays/p8Li
+qG12W0OfvrSdsyaGOx9/5fLoZigWJdBLlzin5M8J0TbDC77aO0RYjb7xnglrPvMyxyuHxuxenPaH
+Za0zKcQvidm5y8kDnftslFGXEBuGCxobP/YCfnvUxVFkKJ3106yDgYjTdLRZncHrYTNaRdHLOdAG
+alNgHa/2+2m8atwBz735j9m9W8E6X47aD0upm50qKGsaCnw8qyIL5XctcfaCNYGu+HuB5ur+rPQa
+m3Rc6I8k9l2dRsQs0h4rIWqDJ2dVSqTjyDKXZpBy2uPUZC5f46Fq9mDU5zXNysRojddxyNMkM3Ox
+bPlq4SjbX8Y96L5V5jcb7STZDxmPX2MYWFCBUWVv8p9+agTnNCRxunZLWB4ZvRVgRaoMEkABnRDi
+xzgHcgplwLa7JSnaFp6LNYth7eVxV4O1PHGf40+/fh6Bn0GXAgMBAAGjgYYwgYMwDgYDVR0PAQH/
+BAQDAgGGMB0GA1UdIQQWMBQwEgYHYIV0AVMCAgYHYIV0AVMCAjASBgNVHRMBAf8ECDAGAQH/AgED
+MB0GA1UdDgQWBBRF2aWBbj2ITY1x0kbBbkUe88SAnTAfBgNVHSMEGDAWgBRF2aWBbj2ITY1x0kbB
+bkUe88SAnTANBgkqhkiG9w0BAQsFAAOCAgEAlDpzBp9SSzBc1P6xXCX5145v9Ydkn+0UjrgEjihL
+j6p7jjm02Vj2e6E1CqGdivdj5eu9OYLU43otb98TPLr+flaYC/NUn81ETm484T4VvwYmneTwkLbU
+wp4wLh/vx3rEUMfqe9pQy3omywC0Wqu1kx+AiYQElY2NfwmTv9SoqORjbdlk5LgpWgi/UOGED1V7
+XwgiG/W9mR4U9s70WBCCswo9GcG/W6uqmdjyMb3lOGbcWAXH7WMaLgqXfIeTK7KK4/HsGOV1timH
+59yLGn602MnTihdsfSlEvoqq9X46Lmgxk7lq2prg2+kupYTNHAq4Sgj5nPFhJpiTt3tm7JFe3VE/
+23MPrQRYCd0EApUKPtN236YQHoA96M2kZNEzx5LH4k5E4wnJTsJdhw4Snr8PyQUQ3nqjsTzyP6Wq
+J3mtMX0f/fwZacXduT98zca0wjAefm6S139hdlqP65VNvBFuIXxZN5nQBrz5Bm0yFqXZaajh3DyA
+HmBR3NdUIR7KYndP+tiPsys6DXhyyWhBWkdKwqPrGtcKqzwyVcgKEZzfdNbwQBUdyLmPtTbFr/gi
+uMod89a2GQ+fYWVq6nTIfI/DT11lgh/ZDYnadXL77/FHZxOzyNEZiCcmmpl5fx7kLD977vHeTYuW
+l8PVP3wbI+2ksx0WckNLIOFZfsLorSa/ovc=
+-----END CERTIFICATE-----
+
+CA Disig Root R1
+================
+-----BEGIN CERTIFICATE-----
+MIIFaTCCA1GgAwIBAgIJAMMDmu5QkG4oMA0GCSqGSIb3DQEBBQUAMFIxCzAJBgNVBAYTAlNLMRMw
+EQYDVQQHEwpCcmF0aXNsYXZhMRMwEQYDVQQKEwpEaXNpZyBhLnMuMRkwFwYDVQQDExBDQSBEaXNp
+ZyBSb290IFIxMB4XDTEyMDcxOTA5MDY1NloXDTQyMDcxOTA5MDY1NlowUjELMAkGA1UEBhMCU0sx
+EzARBgNVBAcTCkJyYXRpc2xhdmExEzARBgNVBAoTCkRpc2lnIGEucy4xGTAXBgNVBAMTEENBIERp
+c2lnIFJvb3QgUjEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCqw3j33Jijp1pedxiy
+3QRkD2P9m5YJgNXoqqXinCaUOuiZc4yd39ffg/N4T0Dhf9Kn0uXKE5Pn7cZ3Xza1lK/oOI7bm+V8
+u8yN63Vz4STN5qctGS7Y1oprFOsIYgrY3LMATcMjfF9DCCMyEtztDK3AfQ+lekLZWnDZv6fXARz2
+m6uOt0qGeKAeVjGu74IKgEH3G8muqzIm1Cxr7X1r5OJeIgpFy4QxTaz+29FHuvlglzmxZcfe+5nk
+CiKxLU3lSCZpq+Kq8/v8kiky6bM+TR8noc2OuRf7JT7JbvN32g0S9l3HuzYQ1VTW8+DiR0jm3hTa
+YVKvJrT1cU/J19IG32PK/yHoWQbgCNWEFVP3Q+V8xaCJmGtzxmjOZd69fwX3se72V6FglcXM6pM6
+vpmumwKjrckWtc7dXpl4fho5frLABaTAgqWjR56M6ly2vGfb5ipN0gTco65F97yLnByn1tUD3AjL
+LhbKXEAz6GfDLuemROoRRRw1ZS0eRWEkG4IupZ0zXWX4Qfkuy5Q/H6MMMSRE7cderVC6xkGbrPAX
+ZcD4XW9boAo0PO7X6oifmPmvTiT6l7Jkdtqr9O3jw2Dv1fkCyC2fg69naQanMVXVz0tv/wQFx1is
+XxYb5dKj6zHbHzMVTdDypVP1y+E9Tmgt2BLdqvLmTZtJ5cUoobqwWsagtQIDAQABo0IwQDAPBgNV
+HRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUiQq0OJMa5qvum5EY+fU8PjXQ
+04IwDQYJKoZIhvcNAQEFBQADggIBADKL9p1Kyb4U5YysOMo6CdQbzoaz3evUuii+Eq5FLAR0rBNR
+xVgYZk2C2tXck8An4b58n1KeElb21Zyp9HWc+jcSjxyT7Ff+Bw+r1RL3D65hXlaASfX8MPWbTx9B
+LxyE04nH4toCdu0Jz2zBuByDHBb6lM19oMgY0sidbvW9adRtPTXoHqJPYNcHKfyyo6SdbhWSVhlM
+CrDpfNIZTUJG7L399ldb3Zh+pE3McgODWF3vkzpBemOqfDqo9ayk0d2iLbYq/J8BjuIQscTK5Gfb
+VSUZP/3oNn6z4eGBrxEWi1CXYBmCAMBrTXO40RMHPuq2MU/wQppt4hF05ZSsjYSVPCGvxdpHyN85
+YmLLW1AL14FABZyb7bq2ix4Eb5YgOe2kfSnbSM6C3NQCjR0EMVrHS/BsYVLXtFHCgWzN4funodKS
+ds+xDzdYpPJScWc/DIh4gInByLUfkmO+p3qKViwaqKactV2zY9ATIKHrkWzQjX2v3wvkF7mGnjix
+lAxYjOBVqjtjbZqJYLhkKpLGN/R+Q0O3c+gB53+XD9fyexn9GtePyfqFa3qdnom2piiZk4hA9z7N
+UaPK6u95RyG1/jLix8NRb76AdPCkwzryT+lf3xkK8jsTQ6wxpLPn6/wY1gGp8yqPNg7rtLG8t0zJ
+a7+h89n07eLw4+1knj0vllJPgFOL
+-----END CERTIFICATE-----
+
+CA Disig Root R2
+================
+-----BEGIN CERTIFICATE-----
+MIIFaTCCA1GgAwIBAgIJAJK4iNuwisFjMA0GCSqGSIb3DQEBCwUAMFIxCzAJBgNVBAYTAlNLMRMw
+EQYDVQQHEwpCcmF0aXNsYXZhMRMwEQYDVQQKEwpEaXNpZyBhLnMuMRkwFwYDVQQDExBDQSBEaXNp
+ZyBSb290IFIyMB4XDTEyMDcxOTA5MTUzMFoXDTQyMDcxOTA5MTUzMFowUjELMAkGA1UEBhMCU0sx
+EzARBgNVBAcTCkJyYXRpc2xhdmExEzARBgNVBAoTCkRpc2lnIGEucy4xGTAXBgNVBAMTEENBIERp
+c2lnIFJvb3QgUjIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCio8QACdaFXS1tFPbC
+w3OeNcJxVX6B+6tGUODBfEl45qt5WDza/3wcn9iXAng+a0EE6UG9vgMsRfYvZNSrXaNHPWSb6Wia
+xswbP7q+sos0Ai6YVRn8jG+qX9pMzk0DIaPY0jSTVpbLTAwAFjxfGs3Ix2ymrdMxp7zo5eFm1tL7
+A7RBZckQrg4FY8aAamkw/dLukO8NJ9+flXP04SXabBbeQTg06ov80egEFGEtQX6sx3dOy1FU+16S
+GBsEWmjGycT6txOgmLcRK7fWV8x8nhfRyyX+hk4kLlYMeE2eARKmK6cBZW58Yh2EhN/qwGu1pSqV
+g8NTEQxzHQuyRpDRQjrOQG6Vrf/GlK1ul4SOfW+eioANSW1z4nuSHsPzwfPrLgVv2RvPN3YEyLRa
+5Beny912H9AZdugsBbPWnDTYltxhh5EF5EQIM8HauQhl1K6yNg3ruji6DOWbnuuNZt2Zz9aJQfYE
+koopKW1rOhzndX0CcQ7zwOe9yxndnWCywmZgtrEE7snmhrmaZkCo5xHtgUUDi/ZnWejBBhG93c+A
+Ak9lQHhcR1DIm+YfgXvkRKhbhZri3lrVx/k6RGZL5DJUfORsnLMOPReisjQS1n6yqEm70XooQL6i
+Fh/f5DcfEXP7kAplQ6INfPgGAVUzfbANuPT1rqVCV3w2EYx7XsQDnYx5nQIDAQABo0IwQDAPBgNV
+HRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUtZn4r7CU9eMg1gqtzk5WpC5u
+Qu0wDQYJKoZIhvcNAQELBQADggIBACYGXnDnZTPIgm7ZnBc6G3pmsgH2eDtpXi/q/075KMOYKmFM
+tCQSin1tERT3nLXK5ryeJ45MGcipvXrA1zYObYVybqjGom32+nNjf7xueQgcnYqfGopTpti72TVV
+sRHFqQOzVju5hJMiXn7B9hJSi+osZ7z+Nkz1uM/Rs0mSO9MpDpkblvdhuDvEK7Z4bLQjb/D907Je
+dR+Zlais9trhxTF7+9FGs9K8Z7RiVLoJ92Owk6Ka+elSLotgEqv89WBW7xBci8QaQtyDW2QOy7W8
+1k/BfDxujRNt+3vrMNDcTa/F1balTFtxyegxvug4BkihGuLq0t4SOVga/4AOgnXmt8kHbA7v/zjx
+mHHEt38OFdAlab0inSvtBfZGR6ztwPDUO+Ls7pZbkBNOHlY667DvlruWIxG68kOGdGSVyCh13x01
+utI3gzhTODY7z2zp+WsO0PsE6E9312UBeIYMej4hYvF/Y3EMyZ9E26gnonW+boE+18DrG5gPcFw0
+sorMwIUY6256s/daoQe/qUKS82Ail+QUoQebTnbAjn39pCXHR+3/H3OszMOl6W8KjptlwlCFtaOg
+UxLMVYdh84GuEEZhvUQhuMI9dM9+JDX6HAcOmz0iyu8xL4ysEr3vQCj8KWefshNPZiTEUxnpHikV
+7+ZtsH8tZ/3zbBt1RqPlShfppNcL
+-----END CERTIFICATE-----
+
+ACCVRAIZ1
+=========
+-----BEGIN CERTIFICATE-----
+MIIH0zCCBbugAwIBAgIIXsO3pkN/pOAwDQYJKoZIhvcNAQEFBQAwQjESMBAGA1UEAwwJQUNDVlJB
+SVoxMRAwDgYDVQQLDAdQS0lBQ0NWMQ0wCwYDVQQKDARBQ0NWMQswCQYDVQQGEwJFUzAeFw0xMTA1
+MDUwOTM3MzdaFw0zMDEyMzEwOTM3MzdaMEIxEjAQBgNVBAMMCUFDQ1ZSQUlaMTEQMA4GA1UECwwH
+UEtJQUNDVjENMAsGA1UECgwEQUNDVjELMAkGA1UEBhMCRVMwggIiMA0GCSqGSIb3DQEBAQUAA4IC
+DwAwggIKAoICAQCbqau/YUqXry+XZpp0X9DZlv3P4uRm7x8fRzPCRKPfmt4ftVTdFXxpNRFvu8gM
+jmoYHtiP2Ra8EEg2XPBjs5BaXCQ316PWywlxufEBcoSwfdtNgM3802/J+Nq2DoLSRYWoG2ioPej0
+RGy9ocLLA76MPhMAhN9KSMDjIgro6TenGEyxCQ0jVn8ETdkXhBilyNpAlHPrzg5XPAOBOp0KoVdD
+aaxXbXmQeOW1tDvYvEyNKKGno6e6Ak4l0Squ7a4DIrhrIA8wKFSVf+DuzgpmndFALW4ir50awQUZ
+0m/A8p/4e7MCQvtQqR0tkw8jq8bBD5L/0KIV9VMJcRz/RROE5iZe+OCIHAr8Fraocwa48GOEAqDG
+WuzndN9wrqODJerWx5eHk6fGioozl2A3ED6XPm4pFdahD9GILBKfb6qkxkLrQaLjlUPTAYVtjrs7
+8yM2x/474KElB0iryYl0/wiPgL/AlmXz7uxLaL2diMMxs0Dx6M/2OLuc5NF/1OVYm3z61PMOm3WR
+5LpSLhl+0fXNWhn8ugb2+1KoS5kE3fj5tItQo05iifCHJPqDQsGH+tUtKSpacXpkatcnYGMN285J
+9Y0fkIkyF/hzQ7jSWpOGYdbhdQrqeWZ2iE9x6wQl1gpaepPluUsXQA+xtrn13k/c4LOsOxFwYIRK
+Q26ZIMApcQrAZQIDAQABo4ICyzCCAscwfQYIKwYBBQUHAQEEcTBvMEwGCCsGAQUFBzAChkBodHRw
+Oi8vd3d3LmFjY3YuZXMvZmlsZWFkbWluL0FyY2hpdm9zL2NlcnRpZmljYWRvcy9yYWl6YWNjdjEu
+Y3J0MB8GCCsGAQUFBzABhhNodHRwOi8vb2NzcC5hY2N2LmVzMB0GA1UdDgQWBBTSh7Tj3zcnk1X2
+VuqB5TbMjB4/vTAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFNKHtOPfNyeTVfZW6oHlNsyM
+Hj+9MIIBcwYDVR0gBIIBajCCAWYwggFiBgRVHSAAMIIBWDCCASIGCCsGAQUFBwICMIIBFB6CARAA
+QQB1AHQAbwByAGkAZABhAGQAIABkAGUAIABDAGUAcgB0AGkAZgBpAGMAYQBjAGkA8wBuACAAUgBh
+AO0AegAgAGQAZQAgAGwAYQAgAEEAQwBDAFYAIAAoAEEAZwBlAG4AYwBpAGEAIABkAGUAIABUAGUA
+YwBuAG8AbABvAGcA7QBhACAAeQAgAEMAZQByAHQAaQBmAGkAYwBhAGMAaQDzAG4AIABFAGwAZQBj
+AHQAcgDzAG4AaQBjAGEALAAgAEMASQBGACAAUQA0ADYAMAAxADEANQA2AEUAKQAuACAAQwBQAFMA
+IABlAG4AIABoAHQAdABwADoALwAvAHcAdwB3AC4AYQBjAGMAdgAuAGUAczAwBggrBgEFBQcCARYk
+aHR0cDovL3d3dy5hY2N2LmVzL2xlZ2lzbGFjaW9uX2MuaHRtMFUGA1UdHwROMEwwSqBIoEaGRGh0
+dHA6Ly93d3cuYWNjdi5lcy9maWxlYWRtaW4vQXJjaGl2b3MvY2VydGlmaWNhZG9zL3JhaXphY2N2
+MV9kZXIuY3JsMA4GA1UdDwEB/wQEAwIBBjAXBgNVHREEEDAOgQxhY2N2QGFjY3YuZXMwDQYJKoZI
+hvcNAQEFBQADggIBAJcxAp/n/UNnSEQU5CmH7UwoZtCPNdpNYbdKl02125DgBS4OxnnQ8pdpD70E
+R9m+27Up2pvZrqmZ1dM8MJP1jaGo/AaNRPTKFpV8M9xii6g3+CfYCS0b78gUJyCpZET/LtZ1qmxN
+YEAZSUNUY9rizLpm5U9EelvZaoErQNV/+QEnWCzI7UiRfD+mAM/EKXMRNt6GGT6d7hmKG9Ww7Y49
+nCrADdg9ZuM8Db3VlFzi4qc1GwQA9j9ajepDvV+JHanBsMyZ4k0ACtrJJ1vnE5Bc5PUzolVt3OAJ
+TS+xJlsndQAJxGJ3KQhfnlmstn6tn1QwIgPBHnFk/vk4CpYY3QIUrCPLBhwepH2NDd4nQeit2hW3
+sCPdK6jT2iWH7ehVRE2I9DZ+hJp4rPcOVkkO1jMl1oRQQmwgEh0q1b688nCBpHBgvgW1m54ERL5h
+I6zppSSMEYCUWqKiuUnSwdzRp+0xESyeGabu4VXhwOrPDYTkF7eifKXeVSUG7szAh1xA2syVP1Xg
+Nce4hL60Xc16gwFy7ofmXx2utYXGJt/mwZrpHgJHnyqobalbz+xFd3+YJ5oyXSrjhO7FmGYvliAd
+3djDJ9ew+f7Zfc3Qn48LFFhRny+Lwzgt3uiP1o2HpPVWQxaZLPSkVrQ0uGE3ycJYgBugl6H8WY3p
+EfbRD0tVNEYqi4Y7
+-----END CERTIFICATE-----
+
+TWCA Global Root CA
+===================
+-----BEGIN CERTIFICATE-----
+MIIFQTCCAymgAwIBAgICDL4wDQYJKoZIhvcNAQELBQAwUTELMAkGA1UEBhMCVFcxEjAQBgNVBAoT
+CVRBSVdBTi1DQTEQMA4GA1UECxMHUm9vdCBDQTEcMBoGA1UEAxMTVFdDQSBHbG9iYWwgUm9vdCBD
+QTAeFw0xMjA2MjcwNjI4MzNaFw0zMDEyMzExNTU5NTlaMFExCzAJBgNVBAYTAlRXMRIwEAYDVQQK
+EwlUQUlXQU4tQ0ExEDAOBgNVBAsTB1Jvb3QgQ0ExHDAaBgNVBAMTE1RXQ0EgR2xvYmFsIFJvb3Qg
+Q0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCwBdvI64zEbooh745NnHEKH1Jw7W2C
+nJfF10xORUnLQEK1EjRsGcJ0pDFfhQKX7EMzClPSnIyOt7h52yvVavKOZsTuKwEHktSz0ALfUPZV
+r2YOy+BHYC8rMjk1Ujoog/h7FsYYuGLWRyWRzvAZEk2tY/XTP3VfKfChMBwqoJimFb3u/Rk28OKR
+Q4/6ytYQJ0lM793B8YVwm8rqqFpD/G2Gb3PpN0Wp8DbHzIh1HrtsBv+baz4X7GGqcXzGHaL3SekV
+tTzWoWH1EfcFbx39Eb7QMAfCKbAJTibc46KokWofwpFFiFzlmLhxpRUZyXx1EcxwdE8tmx2RRP1W
+KKD+u4ZqyPpcC1jcxkt2yKsi2XMPpfRaAok/T54igu6idFMqPVMnaR1sjjIsZAAmY2E2TqNGtz99
+sy2sbZCilaLOz9qC5wc0GZbpuCGqKX6mOL6OKUohZnkfs8O1CWfe1tQHRvMq2uYiN2DLgbYPoA/p
+yJV/v1WRBXrPPRXAb94JlAGD1zQbzECl8LibZ9WYkTunhHiVJqRaCPgrdLQABDzfuBSO6N+pjWxn
+kjMdwLfS7JLIvgm/LCkFbwJrnu+8vyq8W8BQj0FwcYeyTbcEqYSjMq+u7msXi7Kx/mzhkIyIqJdI
+zshNy/MGz19qCkKxHh53L46g5pIOBvwFItIm4TFRfTLcDwIDAQABoyMwITAOBgNVHQ8BAf8EBAMC
+AQYwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAgEAXzSBdu+WHdXltdkCY4QWwa6g
+cFGn90xHNcgL1yg9iXHZqjNB6hQbbCEAwGxCGX6faVsgQt+i0trEfJdLjbDorMjupWkEmQqSpqsn
+LhpNgb+E1HAerUf+/UqdM+DyucRFCCEK2mlpc3INvjT+lIutwx4116KD7+U4x6WFH6vPNOw/KP4M
+8VeGTslV9xzU2KV9Bnpv1d8Q34FOIWWxtuEXeZVFBs5fzNxGiWNoRI2T9GRwoD2dKAXDOXC4Ynsg
+/eTb6QihuJ49CcdP+yz4k3ZB3lLg4VfSnQO8d57+nile98FRYB/e2guyLXW3Q0iT5/Z5xoRdgFlg
+lPx4mI88k1HtQJAH32RjJMtOcQWh15QaiDLxInQirqWm2BJpTGCjAu4r7NRjkgtevi92a6O2JryP
+A9gK8kxkRr05YuWW6zRjESjMlfGt7+/cgFhI6Uu46mWs6fyAtbXIRfmswZ/ZuepiiI7E8UuDEq3m
+i4TWnsLrgxifarsbJGAzcMzs9zLzXNl5fe+epP7JI8Mk7hWSsT2RTyaGvWZzJBPqpK5jwa19hAM8
+EHiGG3njxPPyBJUgriOCxLM6AGK/5jYk4Ve6xx6QddVfP5VhK8E7zeWzaGHQRiapIVJpLesux+t3
+zqY6tQMzT3bR51xUAV3LePTJDL/PEo4XLSNolOer/qmyKwbQBM0=
+-----END CERTIFICATE-----
+
+TeliaSonera Root CA v1
+======================
+-----BEGIN CERTIFICATE-----
+MIIFODCCAyCgAwIBAgIRAJW+FqD3LkbxezmCcvqLzZYwDQYJKoZIhvcNAQEFBQAwNzEUMBIGA1UE
+CgwLVGVsaWFTb25lcmExHzAdBgNVBAMMFlRlbGlhU29uZXJhIFJvb3QgQ0EgdjEwHhcNMDcxMDE4
+MTIwMDUwWhcNMzIxMDE4MTIwMDUwWjA3MRQwEgYDVQQKDAtUZWxpYVNvbmVyYTEfMB0GA1UEAwwW
+VGVsaWFTb25lcmEgUm9vdCBDQSB2MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMK+
+6yfwIaPzaSZVfp3FVRaRXP3vIb9TgHot0pGMYzHw7CTww6XScnwQbfQ3t+XmfHnqjLWCi65ItqwA
+3GV17CpNX8GH9SBlK4GoRz6JI5UwFpB/6FcHSOcZrr9FZ7E3GwYq/t75rH2D+1665I+XZ75Ljo1k
+B1c4VWk0Nj0TSO9P4tNmHqTPGrdeNjPUtAa9GAH9d4RQAEX1jF3oI7x+/jXh7VB7qTCNGdMJjmhn
+Xb88lxhTuylixcpecsHHltTbLaC0H2kD7OriUPEMPPCs81Mt8Bz17Ww5OXOAFshSsCPN4D7c3TxH
+oLs1iuKYaIu+5b9y7tL6pe0S7fyYGKkmdtwoSxAgHNN/Fnct7W+A90m7UwW7XWjH1Mh1Fj+JWov3
+F0fUTPHSiXk+TT2YqGHeOh7S+F4D4MHJHIzTjU3TlTazN19jY5szFPAtJmtTfImMMsJu7D0hADnJ
+oWjiUIMusDor8zagrC/kb2HCUQk5PotTubtn2txTuXZZNp1D5SDgPTJghSJRt8czu90VL6R4pgd7
+gUY2BIbdeTXHlSw7sKMXNeVzH7RcWe/a6hBle3rQf5+ztCo3O3CLm1u5K7fsslESl1MpWtTwEhDc
+TwK7EpIvYtQ/aUN8Ddb8WHUBiJ1YFkveupD/RwGJBmr2X7KQarMCpgKIv7NHfirZ1fpoeDVNAgMB
+AAGjPzA9MA8GA1UdEwEB/wQFMAMBAf8wCwYDVR0PBAQDAgEGMB0GA1UdDgQWBBTwj1k4ALP1j5qW
+DNXr+nuqF+gTEjANBgkqhkiG9w0BAQUFAAOCAgEAvuRcYk4k9AwI//DTDGjkk0kiP0Qnb7tt3oNm
+zqjMDfz1mgbldxSR651Be5kqhOX//CHBXfDkH1e3damhXwIm/9fH907eT/j3HEbAek9ALCI18Bmx
+0GtnLLCo4MBANzX2hFxc469CeP6nyQ1Q6g2EdvZR74NTxnr/DlZJLo961gzmJ1TjTQpgcmLNkQfW
+pb/ImWvtxBnmq0wROMVvMeJuScg/doAmAyYp4Db29iBT4xdwNBedY2gea+zDTYa4EzAvXUYNR0PV
+G6pZDrlcjQZIrXSHX8f8MVRBE+LHIQ6e4B4N4cB7Q4WQxYpYxmUKeFfyxiMPAdkgS94P+5KFdSpc
+c41teyWRyu5FrgZLAMzTsVlQ2jqIOylDRl6XK1TOU2+NSueW+r9xDkKLfP0ooNBIytrEgUy7onOT
+JsjrDNYmiLbAJM+7vVvrdX3pCI6GMyx5dwlppYn8s3CQh3aP0yK7Qs69cwsgJirQmz1wHiRszYd2
+qReWt88NkvuOGKmYSdGe/mBEciG5Ge3C9THxOUiIkCR1VBatzvT4aRRkOfujuLpwQMcnHL/EVlP6
+Y2XQ8xwOFvVrhlhNGNTkDY6lnVuR3HYkUD/GKvvZt5y11ubQ2egZixVxSK236thZiNSQvxaz2ems
+WWFUyBy6ysHK4bkgTI86k4mloMy/0/Z1pHWWbVY=
+-----END CERTIFICATE-----
+
+E-Tugra Certification Authority
+===============================
+-----BEGIN CERTIFICATE-----
+MIIGSzCCBDOgAwIBAgIIamg+nFGby1MwDQYJKoZIhvcNAQELBQAwgbIxCzAJBgNVBAYTAlRSMQ8w
+DQYDVQQHDAZBbmthcmExQDA+BgNVBAoMN0UtVHXEn3JhIEVCRyBCaWxpxZ9pbSBUZWtub2xvamls
+ZXJpIHZlIEhpem1ldGxlcmkgQS7Fni4xJjAkBgNVBAsMHUUtVHVncmEgU2VydGlmaWthc3lvbiBN
+ZXJrZXppMSgwJgYDVQQDDB9FLVR1Z3JhIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTEzMDMw
+NTEyMDk0OFoXDTIzMDMwMzEyMDk0OFowgbIxCzAJBgNVBAYTAlRSMQ8wDQYDVQQHDAZBbmthcmEx
+QDA+BgNVBAoMN0UtVHXEn3JhIEVCRyBCaWxpxZ9pbSBUZWtub2xvamlsZXJpIHZlIEhpem1ldGxl
+cmkgQS7Fni4xJjAkBgNVBAsMHUUtVHVncmEgU2VydGlmaWthc3lvbiBNZXJrZXppMSgwJgYDVQQD
+DB9FLVR1Z3JhIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIICIjANBgkqhkiG9w0BAQEFAAOCAg8A
+MIICCgKCAgEA4vU/kwVRHoViVF56C/UYB4Oufq9899SKa6VjQzm5S/fDxmSJPZQuVIBSOTkHS0vd
+hQd2h8y/L5VMzH2nPbxHD5hw+IyFHnSOkm0bQNGZDbt1bsipa5rAhDGvykPL6ys06I+XawGb1Q5K
+CKpbknSFQ9OArqGIW66z6l7LFpp3RMih9lRozt6Plyu6W0ACDGQXwLWTzeHxE2bODHnv0ZEoq1+g
+ElIwcxmOj+GMB6LDu0rw6h8VqO4lzKRG+Bsi77MOQ7osJLjFLFzUHPhdZL3Dk14opz8n8Y4e0ypQ
+BaNV2cvnOVPAmJ6MVGKLJrD3fY185MaeZkJVgkfnsliNZvcHfC425lAcP9tDJMW/hkd5s3kc91r0
+E+xs+D/iWR+V7kI+ua2oMoVJl0b+SzGPWsutdEcf6ZG33ygEIqDUD13ieU/qbIWGvaimzuT6w+Gz
+rt48Ue7LE3wBf4QOXVGUnhMMti6lTPk5cDZvlsouDERVxcr6XQKj39ZkjFqzAQqptQpHF//vkUAq
+jqFGOjGY5RH8zLtJVor8udBhmm9lbObDyz51Sf6Pp+KJxWfXnUYTTjF2OySznhFlhqt/7x3U+Lzn
+rFpct1pHXFXOVbQicVtbC/DP3KBhZOqp12gKY6fgDT+gr9Oq0n7vUaDmUStVkhUXU8u3Zg5mTPj5
+dUyQ5xJwx0UCAwEAAaNjMGEwHQYDVR0OBBYEFC7j27JJ0JxUeVz6Jyr+zE7S6E5UMA8GA1UdEwEB
+/wQFMAMBAf8wHwYDVR0jBBgwFoAULuPbsknQnFR5XPonKv7MTtLoTlQwDgYDVR0PAQH/BAQDAgEG
+MA0GCSqGSIb3DQEBCwUAA4ICAQAFNzr0TbdF4kV1JI+2d1LoHNgQk2Xz8lkGpD4eKexd0dCrfOAK
+kEh47U6YA5n+KGCRHTAduGN8qOY1tfrTYXbm1gdLymmasoR6d5NFFxWfJNCYExL/u6Au/U5Mh/jO
+XKqYGwXgAEZKgoClM4so3O0409/lPun++1ndYYRP0lSWE2ETPo+Aab6TR7U1Q9Jauz1c77NCR807
+VRMGsAnb/WP2OogKmW9+4c4bU2pEZiNRCHu8W1Ki/QY3OEBhj0qWuJA3+GbHeJAAFS6LrVE1Uweo
+a2iu+U48BybNCAVwzDk/dr2l02cmAYamU9JgO3xDf1WKvJUawSg5TB9D0pH0clmKuVb8P7Sd2nCc
+dlqMQ1DujjByTd//SffGqWfZbawCEeI6FiWnWAjLb1NBnEg4R2gz0dfHj9R0IdTDBZB6/86WiLEV
+KV0jq9BgoRJP3vQXzTLlyb/IQ639Lo7xr+L0mPoSHyDYwKcMhcWQ9DstliaxLL5Mq+ux0orJ23gT
+Dx4JnW2PAJ8C2sH6H3p6CcRK5ogql5+Ji/03X186zjhZhkuvcQu02PJwT58yE+Owp1fl2tpDy4Q0
+8ijE6m30Ku/Ba3ba+367hTzSU8JNvnHhRdH9I2cNE3X7z2VnIp2usAnRCf8dNL/+I5c30jn6PQ0G
+C7TbO6Orb1wdtn7os4I07QZcJA==
+-----END CERTIFICATE-----
+
+T-TeleSec GlobalRoot Class 2
+============================
+-----BEGIN CERTIFICATE-----
+MIIDwzCCAqugAwIBAgIBATANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoM
+IlQtU3lzdGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBU
+cnVzdCBDZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDIwHhcNMDgx
+MDAxMTA0MDE0WhcNMzMxMDAxMjM1OTU5WjCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoMIlQtU3lz
+dGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBD
+ZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDIwggEiMA0GCSqGSIb3
+DQEBAQUAA4IBDwAwggEKAoIBAQCqX9obX+hzkeXaXPSi5kfl82hVYAUdAqSzm1nzHoqvNK38DcLZ
+SBnuaY/JIPwhqgcZ7bBcrGXHX+0CfHt8LRvWurmAwhiCFoT6ZrAIxlQjgeTNuUk/9k9uN0goOA/F
+vudocP05l03Sx5iRUKrERLMjfTlH6VJi1hKTXrcxlkIF+3anHqP1wvzpesVsqXFP6st4vGCvx970
+2cu+fjOlbpSD8DT6IavqjnKgP6TeMFvvhk1qlVtDRKgQFRzlAVfFmPHmBiiRqiDFt1MmUUOyCxGV
+WOHAD3bZwI18gfNycJ5v/hqO2V81xrJvNHy+SE/iWjnX2J14np+GPgNeGYtEotXHAgMBAAGjQjBA
+MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBS/WSA2AHmgoCJrjNXy
+YdK4LMuCSjANBgkqhkiG9w0BAQsFAAOCAQEAMQOiYQsfdOhyNsZt+U2e+iKo4YFWz827n+qrkRk4
+r6p8FU3ztqONpfSO9kSpp+ghla0+AGIWiPACuvxhI+YzmzB6azZie60EI4RYZeLbK4rnJVM3YlNf
+vNoBYimipidx5joifsFvHZVwIEoHNN/q/xWA5brXethbdXwFeilHfkCoMRN3zUA7tFFHei4R40cR
+3p1m0IvVVGb6g1XqfMIpiRvpb7PO4gWEyS8+eIVibslfwXhjdFjASBgMmTnrpMwatXlajRWc2BQN
+9noHV8cigwUtPJslJj0Ys6lDfMjIq2SPDqO/nBudMNva0Bkuqjzx+zOAduTNrRlPBSeOE6Fuwg==
+-----END CERTIFICATE-----
+
+Atos TrustedRoot 2011
+=====================
+-----BEGIN CERTIFICATE-----
+MIIDdzCCAl+gAwIBAgIIXDPLYixfszIwDQYJKoZIhvcNAQELBQAwPDEeMBwGA1UEAwwVQXRvcyBU
+cnVzdGVkUm9vdCAyMDExMQ0wCwYDVQQKDARBdG9zMQswCQYDVQQGEwJERTAeFw0xMTA3MDcxNDU4
+MzBaFw0zMDEyMzEyMzU5NTlaMDwxHjAcBgNVBAMMFUF0b3MgVHJ1c3RlZFJvb3QgMjAxMTENMAsG
+A1UECgwEQXRvczELMAkGA1UEBhMCREUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCV
+hTuXbyo7LjvPpvMpNb7PGKw+qtn4TaA+Gke5vJrf8v7MPkfoepbCJI419KkM/IL9bcFyYie96mvr
+54rMVD6QUM+A1JX76LWC1BTFtqlVJVfbsVD2sGBkWXppzwO3bw2+yj5vdHLqqjAqc2K+SZFhyBH+
+DgMq92og3AIVDV4VavzjgsG1xZ1kCWyjWZgHJ8cblithdHFsQ/H3NYkQ4J7sVaE3IqKHBAUsR320
+HLliKWYoyrfhk/WklAOZuXCFteZI6o1Q/NnezG8HDt0Lcp2AMBYHlT8oDv3FdU9T1nSatCQujgKR
+z3bFmx5VdJx4IbHwLfELn8LVlhgf8FQieowHAgMBAAGjfTB7MB0GA1UdDgQWBBSnpQaxLKYJYO7R
+l+lwrrw7GWzbITAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFKelBrEspglg7tGX6XCuvDsZ
+bNshMBgGA1UdIAQRMA8wDQYLKwYBBAGwLQMEAQEwDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEB
+CwUAA4IBAQAmdzTblEiGKkGdLD4GkGDEjKwLVLgfuXvTBznk+j57sj1O7Z8jvZfza1zv7v1Apt+h
+k6EKhqzvINB5Ab149xnYJDE0BAGmuhWawyfc2E8PzBhj/5kPDpFrdRbhIfzYJsdHt6bPWHJxfrrh
+TZVHO8mvbaG0weyJ9rQPOLXiZNwlz6bb65pcmaHFCN795trV1lpFDMS3wrUU77QR/w4VtfX128a9
+61qn8FYiqTxlVMYVqL2Gns2Dlmh6cYGJ4Qvh6hEbaAjMaZ7snkGeRDImeuKHCnE96+RapNLbxc3G
+3mB/ufNPRJLvKrcYPqcZ2Qt9sTdBQrC6YB3y/gkRsPCHe6ed
+-----END CERTIFICATE-----
+
+QuoVadis Root CA 1 G3
+=====================
+-----BEGIN CERTIFICATE-----
+MIIFYDCCA0igAwIBAgIUeFhfLq0sGUvjNwc1NBMotZbUZZMwDQYJKoZIhvcNAQELBQAwSDELMAkG
+A1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAcBgNVBAMTFVF1b1ZhZGlzIFJv
+b3QgQ0EgMSBHMzAeFw0xMjAxMTIxNzI3NDRaFw00MjAxMTIxNzI3NDRaMEgxCzAJBgNVBAYTAkJN
+MRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDEg
+RzMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCgvlAQjunybEC0BJyFuTHK3C3kEakE
+PBtVwedYMB0ktMPvhd6MLOHBPd+C5k+tR4ds7FtJwUrVu4/sh6x/gpqG7D0DmVIB0jWerNrwU8lm
+PNSsAgHaJNM7qAJGr6Qc4/hzWHa39g6QDbXwz8z6+cZM5cOGMAqNF34168Xfuw6cwI2H44g4hWf6
+Pser4BOcBRiYz5P1sZK0/CPTz9XEJ0ngnjybCKOLXSoh4Pw5qlPafX7PGglTvF0FBM+hSo+LdoIN
+ofjSxxR3W5A2B4GbPgb6Ul5jxaYA/qXpUhtStZI5cgMJYr2wYBZupt0lwgNm3fME0UDiTouG9G/l
+g6AnhF4EwfWQvTA9xO+oabw4m6SkltFi2mnAAZauy8RRNOoMqv8hjlmPSlzkYZqn0ukqeI1RPToV
+7qJZjqlc3sX5kCLliEVx3ZGZbHqfPT2YfF72vhZooF6uCyP8Wg+qInYtyaEQHeTTRCOQiJ/GKubX
+9ZqzWB4vMIkIG1SitZgj7Ah3HJVdYdHLiZxfokqRmu8hqkkWCKi9YSgxyXSthfbZxbGL0eUQMk1f
+iyA6PEkfM4VZDdvLCXVDaXP7a3F98N/ETH3Goy7IlXnLc6KOTk0k+17kBL5yG6YnLUlamXrXXAkg
+t3+UuU/xDRxeiEIbEbfnkduebPRq34wGmAOtzCjvpUfzUwIDAQABo0IwQDAPBgNVHRMBAf8EBTAD
+AQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUo5fW816iEOGrRZ88F2Q87gFwnMwwDQYJKoZI
+hvcNAQELBQADggIBABj6W3X8PnrHX3fHyt/PX8MSxEBd1DKquGrX1RUVRpgjpeaQWxiZTOOtQqOC
+MTaIzen7xASWSIsBx40Bz1szBpZGZnQdT+3Btrm0DWHMY37XLneMlhwqI2hrhVd2cDMT/uFPpiN3
+GPoajOi9ZcnPP/TJF9zrx7zABC4tRi9pZsMbj/7sPtPKlL92CiUNqXsCHKnQO18LwIE6PWThv6ct
+Tr1NxNgpxiIY0MWscgKCP6o6ojoilzHdCGPDdRS5YCgtW2jgFqlmgiNR9etT2DGbe+m3nUvriBbP
++V04ikkwj+3x6xn0dxoxGE1nVGwvb2X52z3sIexe9PSLymBlVNFxZPT5pqOBMzYzcfCkeF9OrYMh
+3jRJjehZrJ3ydlo28hP0r+AJx2EqbPfgna67hkooby7utHnNkDPDs3b69fBsnQGQ+p6Q9pxyz0fa
+wx/kNSBT8lTR32GDpgLiJTjehTItXnOQUl1CxM49S+H5GYQd1aJQzEH7QRTDvdbJWqNjZgKAvQU6
+O0ec7AAmTPWIUb+oI38YB7AL7YsmoWTTYUrrXJ/es69nA7Mf3W1daWhpq1467HxpvMc7hU6eFbm0
+FU/DlXpY18ls6Wy58yljXrQs8C097Vpl4KlbQMJImYFtnh8GKjwStIsPm6Ik8KaN1nrgS7ZklmOV
+hMJKzRwuJIczYOXD
+-----END CERTIFICATE-----
+
+QuoVadis Root CA 2 G3
+=====================
+-----BEGIN CERTIFICATE-----
+MIIFYDCCA0igAwIBAgIURFc0JFuBiZs18s64KztbpybwdSgwDQYJKoZIhvcNAQELBQAwSDELMAkG
+A1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAcBgNVBAMTFVF1b1ZhZGlzIFJv
+b3QgQ0EgMiBHMzAeFw0xMjAxMTIxODU5MzJaFw00MjAxMTIxODU5MzJaMEgxCzAJBgNVBAYTAkJN
+MRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDIg
+RzMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQChriWyARjcV4g/Ruv5r+LrI3HimtFh
+ZiFfqq8nUeVuGxbULX1QsFN3vXg6YOJkApt8hpvWGo6t/x8Vf9WVHhLL5hSEBMHfNrMWn4rjyduY
+NM7YMxcoRvynyfDStNVNCXJJ+fKH46nafaF9a7I6JaltUkSs+L5u+9ymc5GQYaYDFCDy54ejiK2t
+oIz/pgslUiXnFgHVy7g1gQyjO/Dh4fxaXc6AcW34Sas+O7q414AB+6XrW7PFXmAqMaCvN+ggOp+o
+MiwMzAkd056OXbxMmO7FGmh77FOm6RQ1o9/NgJ8MSPsc9PG/Srj61YxxSscfrf5BmrODXfKEVu+l
+V0POKa2Mq1W/xPtbAd0jIaFYAI7D0GoT7RPjEiuA3GfmlbLNHiJuKvhB1PLKFAeNilUSxmn1uIZo
+L1NesNKqIcGY5jDjZ1XHm26sGahVpkUG0CM62+tlXSoREfA7T8pt9DTEceT/AFr2XK4jYIVz8eQQ
+sSWu1ZK7E8EM4DnatDlXtas1qnIhO4M15zHfeiFuuDIIfR0ykRVKYnLP43ehvNURG3YBZwjgQQvD
+6xVu+KQZ2aKrr+InUlYrAoosFCT5v0ICvybIxo/gbjh9Uy3l7ZizlWNof/k19N+IxWA1ksB8aRxh
+lRbQ694Lrz4EEEVlWFA4r0jyWbYW8jwNkALGcC4BrTwV1wIDAQABo0IwQDAPBgNVHRMBAf8EBTAD
+AQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQU7edvdlq/YOxJW8ald7tyFnGbxD0wDQYJKoZI
+hvcNAQELBQADggIBAJHfgD9DCX5xwvfrs4iP4VGyvD11+ShdyLyZm3tdquXK4Qr36LLTn91nMX66
+AarHakE7kNQIXLJgapDwyM4DYvmL7ftuKtwGTTwpD4kWilhMSA/ohGHqPHKmd+RCroijQ1h5fq7K
+pVMNqT1wvSAZYaRsOPxDMuHBR//47PERIjKWnML2W2mWeyAMQ0GaW/ZZGYjeVYg3UQt4XAoeo0L9
+x52ID8DyeAIkVJOviYeIyUqAHerQbj5hLja7NQ4nlv1mNDthcnPxFlxHBlRJAHpYErAK74X9sbgz
+dWqTHBLmYF5vHX/JHyPLhGGfHoJE+V+tYlUkmlKY7VHnoX6XOuYvHxHaU4AshZ6rNRDbIl9qxV6X
+U/IyAgkwo1jwDQHVcsaxfGl7w/U2Rcxhbl5MlMVerugOXou/983g7aEOGzPuVBj+D77vfoRrQ+Nw
+mNtddbINWQeFFSM51vHfqSYP1kjHs6Yi9TM3WpVHn3u6GBVv/9YUZINJ0gpnIdsPNWNgKCLjsZWD
+zYWm3S8P52dSbrsvhXz1SnPnxT7AvSESBT/8twNJAlvIJebiVDj1eYeMHVOyToV7BjjHLPj4sHKN
+JeV3UvQDHEimUF+IIDBu8oJDqz2XhOdT+yHBTw8imoa4WSr2Rz0ZiC3oheGe7IUIarFsNMkd7Egr
+O3jtZsSOeWmD3n+M
+-----END CERTIFICATE-----
+
+QuoVadis Root CA 3 G3
+=====================
+-----BEGIN CERTIFICATE-----
+MIIFYDCCA0igAwIBAgIULvWbAiin23r/1aOp7r0DoM8Sah0wDQYJKoZIhvcNAQELBQAwSDELMAkG
+A1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAcBgNVBAMTFVF1b1ZhZGlzIFJv
+b3QgQ0EgMyBHMzAeFw0xMjAxMTIyMDI2MzJaFw00MjAxMTIyMDI2MzJaMEgxCzAJBgNVBAYTAkJN
+MRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDMg
+RzMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCzyw4QZ47qFJenMioKVjZ/aEzHs286
+IxSR/xl/pcqs7rN2nXrpixurazHb+gtTTK/FpRp5PIpM/6zfJd5O2YIyC0TeytuMrKNuFoM7pmRL
+Mon7FhY4futD4tN0SsJiCnMK3UmzV9KwCoWdcTzeo8vAMvMBOSBDGzXRU7Ox7sWTaYI+FrUoRqHe
+6okJ7UO4BUaKhvVZR74bbwEhELn9qdIoyhA5CcoTNs+cra1AdHkrAj80//ogaX3T7mH1urPnMNA3
+I4ZyYUUpSFlob3emLoG+B01vr87ERRORFHAGjx+f+IdpsQ7vw4kZ6+ocYfx6bIrc1gMLnia6Et3U
+VDmrJqMz6nWB2i3ND0/kA9HvFZcba5DFApCTZgIhsUfei5pKgLlVj7WiL8DWM2fafsSntARE60f7
+5li59wzweyuxwHApw0BiLTtIadwjPEjrewl5qW3aqDCYz4ByA4imW0aucnl8CAMhZa634RylsSqi
+Md5mBPfAdOhx3v89WcyWJhKLhZVXGqtrdQtEPREoPHtht+KPZ0/l7DxMYIBpVzgeAVuNVejH38DM
+dyM0SXV89pgR6y3e7UEuFAUCf+D+IOs15xGsIs5XPd7JMG0QA4XN8f+MFrXBsj6IbGB/kE+V9/Yt
+rQE5BwT6dYB9v0lQ7e/JxHwc64B+27bQ3RP+ydOc17KXqQIDAQABo0IwQDAPBgNVHRMBAf8EBTAD
+AQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUxhfQvKjqAkPyGwaZXSuQILnXnOQwDQYJKoZI
+hvcNAQELBQADggIBADRh2Va1EodVTd2jNTFGu6QHcrxfYWLopfsLN7E8trP6KZ1/AvWkyaiTt3px
+KGmPc+FSkNrVvjrlt3ZqVoAh313m6Tqe5T72omnHKgqwGEfcIHB9UqM+WXzBusnIFUBhynLWcKzS
+t/Ac5IYp8M7vaGPQtSCKFWGafoaYtMnCdvvMujAWzKNhxnQT5WvvoxXqA/4Ti2Tk08HS6IT7SdEQ
+TXlm66r99I0xHnAUrdzeZxNMgRVhvLfZkXdxGYFgu/BYpbWcC/ePIlUnwEsBbTuZDdQdm2NnL9Du
+DcpmvJRPpq3t/O5jrFc/ZSXPsoaP0Aj/uHYUbt7lJ+yreLVTubY/6CD50qi+YUbKh4yE8/nxoGib
+Ih6BJpsQBJFxwAYf3KDTuVan45gtf4Od34wrnDKOMpTwATwiKp9Dwi7DmDkHOHv8XgBCH/MyJnmD
+hPbl8MFREsALHgQjDFSlTC9JxUrRtm5gDWv8a4uFJGS3iQ6rJUdbPM9+Sb3H6QrG2vd+DhcI00iX
+0HGS8A85PjRqHH3Y8iKuu2n0M7SmSFXRDw4m6Oy2Cy2nhTXN/VnIn9HNPlopNLk9hM6xZdRZkZFW
+dSHBd575euFgndOtBBj0fOtek49TSiIp+EgrPk2GrFt/ywaZWWDYWGWVjUTR939+J399roD1B0y2
+PpxxVJkES/1Y+Zj0
+-----END CERTIFICATE-----
+
+DigiCert Assured ID Root G2
+===========================
+-----BEGIN CERTIFICATE-----
+MIIDljCCAn6gAwIBAgIQC5McOtY5Z+pnI7/Dr5r0SzANBgkqhkiG9w0BAQsFADBlMQswCQYDVQQG
+EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSQw
+IgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzIwHhcNMTMwODAxMTIwMDAwWhcNMzgw
+MTE1MTIwMDAwWjBlMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQL
+ExB3d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzIw
+ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDZ5ygvUj82ckmIkzTz+GoeMVSAn61UQbVH
+35ao1K+ALbkKz3X9iaV9JPrjIgwrvJUXCzO/GU1BBpAAvQxNEP4HteccbiJVMWWXvdMX0h5i89vq
+bFCMP4QMls+3ywPgym2hFEwbid3tALBSfK+RbLE4E9HpEgjAALAcKxHad3A2m67OeYfcgnDmCXRw
+VWmvo2ifv922ebPynXApVfSr/5Vh88lAbx3RvpO704gqu52/clpWcTs/1PPRCv4o76Pu2ZmvA9OP
+YLfykqGxvYmJHzDNw6YuYjOuFgJ3RFrngQo8p0Quebg/BLxcoIfhG69Rjs3sLPr4/m3wOnyqi+Rn
+lTGNAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBTO
+w0q5mVXyuNtgv6l+vVa1lzan1jANBgkqhkiG9w0BAQsFAAOCAQEAyqVVjOPIQW5pJ6d1Ee88hjZv
+0p3GeDgdaZaikmkuOGybfQTUiaWxMTeKySHMq2zNixya1r9I0jJmwYrA8y8678Dj1JGG0VDjA9tz
+d29KOVPt3ibHtX2vK0LRdWLjSisCx1BL4GnilmwORGYQRI+tBev4eaymG+g3NJ1TyWGqolKvSnAW
+hsI6yLETcDbYz+70CjTVW0z9B5yiutkBclzzTcHdDrEcDcRjvq30FPuJ7KJBDkzMyFdA0G4Dqs0M
+jomZmWzwPDCvON9vvKO+KSAnq3T/EyJ43pdSVR6DtVQgA+6uwE9W3jfMw3+qBCe703e4YtsXfJwo
+IhNzbM8m9Yop5w==
+-----END CERTIFICATE-----
+
+DigiCert Assured ID Root G3
+===========================
+-----BEGIN CERTIFICATE-----
+MIICRjCCAc2gAwIBAgIQC6Fa+h3foLVJRK/NJKBs7DAKBggqhkjOPQQDAzBlMQswCQYDVQQGEwJV
+UzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSQwIgYD
+VQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzMwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1
+MTIwMDAwWjBlMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
+d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzMwdjAQ
+BgcqhkjOPQIBBgUrgQQAIgNiAAQZ57ysRGXtzbg/WPuNsVepRC0FFfLvC/8QdJ+1YlJfZn4f5dwb
+RXkLzMZTCp2NXQLZqVneAlr2lSoOjThKiknGvMYDOAdfVdp+CW7if17QRSAPWXYQ1qAk8C3eNvJs
+KTmjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBTL0L2p4ZgF
+UaFNN6KDec6NHSrkhDAKBggqhkjOPQQDAwNnADBkAjAlpIFFAmsSS3V0T8gj43DydXLefInwz5Fy
+YZ5eEJJZVrmDxxDnOOlYJjZ91eQ0hjkCMHw2U/Aw5WJjOpnitqM7mzT6HtoQknFekROn3aRukswy
+1vUhZscv6pZjamVFkpUBtA==
+-----END CERTIFICATE-----
+
+DigiCert Global Root G2
+=======================
+-----BEGIN CERTIFICATE-----
+MIIDjjCCAnagAwIBAgIQAzrx5qcRqaC7KGSxHQn65TANBgkqhkiG9w0BAQsFADBhMQswCQYDVQQG
+EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSAw
+HgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBHMjAeFw0xMzA4MDExMjAwMDBaFw0zODAxMTUx
+MjAwMDBaMGExCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3
+dy5kaWdpY2VydC5jb20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEcyMIIBIjANBgkq
+hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuzfNNNx7a8myaJCtSnX/RrohCgiN9RlUyfuI2/Ou8jqJ
+kTx65qsGGmvPrC3oXgkkRLpimn7Wo6h+4FR1IAWsULecYxpsMNzaHxmx1x7e/dfgy5SDN67sH0NO
+3Xss0r0upS/kqbitOtSZpLYl6ZtrAGCSYP9PIUkY92eQq2EGnI/yuum06ZIya7XzV+hdG82MHauV
+BJVJ8zUtluNJbd134/tJS7SsVQepj5WztCO7TG1F8PapspUwtP1MVYwnSlcUfIKdzXOS0xZKBgyM
+UNGPHgm+F6HmIcr9g+UQvIOlCsRnKPZzFBQ9RnbDhxSJITRNrw9FDKZJobq7nMWxM4MphQIDAQAB
+o0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUTiJUIBiV5uNu
+5g/6+rkS7QYXjzkwDQYJKoZIhvcNAQELBQADggEBAGBnKJRvDkhj6zHd6mcY1Yl9PMWLSn/pvtsr
+F9+wX3N3KjITOYFnQoQj8kVnNeyIv/iPsGEMNKSuIEyExtv4NeF22d+mQrvHRAiGfzZ0JFrabA0U
+WTW98kndth/Jsw1HKj2ZL7tcu7XUIOGZX1NGFdtom/DzMNU+MeKNhJ7jitralj41E6Vf8PlwUHBH
+QRFXGU7Aj64GxJUTFy8bJZ918rGOmaFvE7FBcf6IKshPECBV1/MUReXgRPTqh5Uykw7+U0b6LJ3/
+iyK5S9kJRaTepLiaWN0bfVKfjllDiIGknibVb63dDcY3fe0Dkhvld1927jyNxF1WW6LZZm6zNTfl
+MrY=
+-----END CERTIFICATE-----
+
+DigiCert Global Root G3
+=======================
+-----BEGIN CERTIFICATE-----
+MIICPzCCAcWgAwIBAgIQBVVWvPJepDU1w6QP1atFcjAKBggqhkjOPQQDAzBhMQswCQYDVQQGEwJV
+UzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSAwHgYD
+VQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBHMzAeFw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAw
+MDBaMGExCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5k
+aWdpY2VydC5jb20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEczMHYwEAYHKoZIzj0C
+AQYFK4EEACIDYgAE3afZu4q4C/sLfyHS8L6+c/MzXRq8NOrexpu80JX28MzQC7phW1FGfp4tn+6O
+YwwX7Adw9c+ELkCDnOg/QW07rdOkFFk2eJ0DQ+4QE2xy3q6Ip6FrtUPOZ9wj/wMco+I+o0IwQDAP
+BgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUs9tIpPmhxdiuNkHMEWNp
+Yim8S8YwCgYIKoZIzj0EAwMDaAAwZQIxAK288mw/EkrRLTnDCgmXc/SINoyIJ7vmiI1Qhadj+Z4y
+3maTD/HMsQmP3Wyr+mt/oAIwOWZbwmSNuJ5Q3KjVSaLtx9zRSX8XAbjIho9OjIgrqJqpisXRAL34
+VOKa5Vt8sycX
+-----END CERTIFICATE-----
+
+DigiCert Trusted Root G4
+========================
+-----BEGIN CERTIFICATE-----
+MIIFkDCCA3igAwIBAgIQBZsbV56OITLiOQe9p3d1XDANBgkqhkiG9w0BAQwFADBiMQswCQYDVQQG
+EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSEw
+HwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3QgRzQwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1
+MTIwMDAwWjBiMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
+d3cuZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3QgRzQwggIiMA0G
+CSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC/5pBzaN675F1KPDAiMGkz7MKnJS7JIT3yithZwuEp
+pz1Yq3aaza57G4QNxDAf8xukOBbrVsaXbR2rsnnyyhHS5F/WBTxSD1Ifxp4VpX6+n6lXFllVcq9o
+k3DCsrp1mWpzMpTREEQQLt+C8weE5nQ7bXHiLQwb7iDVySAdYyktzuxeTsiT+CFhmzTrBcZe7Fsa
+vOvJz82sNEBfsXpm7nfISKhmV1efVFiODCu3T6cw2Vbuyntd463JT17lNecxy9qTXtyOj4DatpGY
+QJB5w3jHtrHEtWoYOAMQjdjUN6QuBX2I9YI+EJFwq1WCQTLX2wRzKm6RAXwhTNS8rhsDdV14Ztk6
+MUSaM0C/CNdaSaTC5qmgZ92kJ7yhTzm1EVgX9yRcRo9k98FpiHaYdj1ZXUJ2h4mXaXpI8OCiEhtm
+mnTK3kse5w5jrubU75KSOp493ADkRSWJtppEGSt+wJS00mFt6zPZxd9LBADMfRyVw4/3IbKyEbe7
+f/LVjHAsQWCqsWMYRJUadmJ+9oCw++hkpjPRiQfhvbfmQ6QYuKZ3AeEPlAwhHbJUKSWJbOUOUlFH
+dL4mrLZBdd56rF+NP8m800ERElvlEFDrMcXKchYiCd98THU/Y+whX8QgUWtvsauGi0/C1kVfnSD8
+oR7FwI+isX4KJpn15GkvmB0t9dmpsh3lGwIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1Ud
+DwEB/wQEAwIBhjAdBgNVHQ4EFgQU7NfjgtJxXWRM3y5nP+e6mK4cD08wDQYJKoZIhvcNAQEMBQAD
+ggIBALth2X2pbL4XxJEbw6GiAI3jZGgPVs93rnD5/ZpKmbnJeFwMDF/k5hQpVgs2SV1EY+CtnJYY
+ZhsjDT156W1r1lT40jzBQ0CuHVD1UvyQO7uYmWlrx8GnqGikJ9yd+SeuMIW59mdNOj6PWTkiU0Tr
+yF0Dyu1Qen1iIQqAyHNm0aAFYF/opbSnr6j3bTWcfFqK1qI4mfN4i/RN0iAL3gTujJtHgXINwBQy
+7zBZLq7gcfJW5GqXb5JQbZaNaHqasjYUegbyJLkJEVDXCLG4iXqEI2FCKeWjzaIgQdfRnGTZ6iah
+ixTXTBmyUEFxPT9NcCOGDErcgdLMMpSEDQgJlxxPwO5rIHQw0uA5NBCFIRUBCOhVMt5xSdkoF1BN
+5r5N0XWs0Mr7QbhDparTwwVETyw2m+L64kW4I1NsBm9nVX9GtUw/bihaeSbSpKhil9Ie4u1Ki7wb
+/UdKDd9nZn6yW0HQO+T0O/QEY+nvwlQAUaCKKsnOeMzV6ocEGLPOr0mIr/OSmbaz5mEP0oUA51Aa
+5BuVnRmhuZyxm7EAHu/QD09CbMkKvO5D+jpxpchNJqU1/YldvIViHTLSoCtU7ZpXwdv6EM8Zt4tK
+G48BtieVU+i2iW1bvGjUI+iLUaJW+fCmgKDWHrO8Dw9TdSmq6hN35N6MgSGtBxBHEa2HPQfRdbzP
+82Z+
+-----END CERTIFICATE-----
+
+WoSign
+======
+-----BEGIN CERTIFICATE-----
+MIIFdjCCA16gAwIBAgIQXmjWEXGUY1BWAGjzPsnFkTANBgkqhkiG9w0BAQUFADBVMQswCQYDVQQG
+EwJDTjEaMBgGA1UEChMRV29TaWduIENBIExpbWl0ZWQxKjAoBgNVBAMTIUNlcnRpZmljYXRpb24g
+QXV0aG9yaXR5IG9mIFdvU2lnbjAeFw0wOTA4MDgwMTAwMDFaFw0zOTA4MDgwMTAwMDFaMFUxCzAJ
+BgNVBAYTAkNOMRowGAYDVQQKExFXb1NpZ24gQ0EgTGltaXRlZDEqMCgGA1UEAxMhQ2VydGlmaWNh
+dGlvbiBBdXRob3JpdHkgb2YgV29TaWduMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA
+vcqNrLiRFVaXe2tcesLea9mhsMMQI/qnobLMMfo+2aYpbxY94Gv4uEBf2zmoAHqLoE1UfcIiePyO
+CbiohdfMlZdLdNiefvAA5A6JrkkoRBoQmTIPJYhTpA2zDxIIFgsDcSccf+Hb0v1naMQFXQoOXXDX
+2JegvFNBmpGN9J42Znp+VsGQX+axaCA2pIwkLCxHC1l2ZjC1vt7tj/id07sBMOby8w7gLJKA84X5
+KIq0VC6a7fd2/BVoFutKbOsuEo/Uz/4Mx1wdC34FMr5esAkqQtXJTpCzWQ27en7N1QhatH/YHGkR
++ScPewavVIMYe+HdVHpRaG53/Ma/UkpmRqGyZxq7o093oL5d//xWC0Nyd5DKnvnyOfUNqfTq1+ez
+EC8wQjchzDBwyYaYD8xYTYO7feUapTeNtqwylwA6Y3EkHp43xP901DfA4v6IRmAR3Qg/UDaruHqk
+lWJqbrDKaiFaafPz+x1wOZXzp26mgYmhiMU7ccqjUu6Du/2gd/Tkb+dC221KmYo0SLwX3OSACCK2
+8jHAPwQ+658geda4BmRkAjHXqc1S+4RFaQkAKtxVi8QGRkvASh0JWzko/amrzgD5LkhLJuYwTKVY
+yrREgk/nkR4zw7CT/xH8gdLKH3Ep3XZPkiWvHYG3Dy+MwwbMLyejSuQOmbp8HkUff6oZRZb9/D0C
+AwEAAaNCMEAwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFOFmzw7R
+8bNLtwYgFP6HEtX2/vs+MA0GCSqGSIb3DQEBBQUAA4ICAQCoy3JAsnbBfnv8rWTjMnvMPLZdRtP1
+LOJwXcgu2AZ9mNELIaCJWSQBnfmvCX0KI4I01fx8cpm5o9dU9OpScA7F9dY74ToJMuYhOZO9sxXq
+T2r09Ys/L3yNWC7F4TmgPsc9SnOeQHrAK2GpZ8nzJLmzbVUsWh2eJXLOC62qx1ViC777Y7NhRCOj
+y+EaDveaBk3e1CNOIZZbOVtXHS9dCF4Jef98l7VNg64N1uajeeAz0JmWAjCnPv/So0M/BVoG6kQC
+2nz4SNAzqfkHx5Xh9T71XXG68pWpdIhhWeO/yloTunK0jF02h+mmxTwTv97QRCbut+wucPrXnbes
+5cVAWubXbHssw1abR80LzvobtCHXt2a49CUwi1wNuepnsvRtrtWhnk/Yn+knArAdBtaP4/tIEp9/
+EaEQPkxROpaw0RPxx9gmrjrKkcRpnd8BKWRRb2jaFOwIQZeQjdCygPLPwj2/kWjFgGcexGATVdVh
+mVd8upUPYUk6ynW8yQqTP2cOEvIo4jEbwFcW3wh8GcF+Dx+FHgo2fFt+J7x6v+Db9NpSvd4MVHAx
+kUOVyLzwPt0JfjBkUO1/AaQzZ01oT74V77D2AhGiGxMlOtzCWfHjXEa7ZywCRuoeSKbmW9m1vFGi
+kpbbqsY3Iqb+zCB0oy2pLmvLwIIRIbWTee5Ehr7XHuQe+w==
+-----END CERTIFICATE-----
+
+WoSign China
+============
+-----BEGIN CERTIFICATE-----
+MIIFWDCCA0CgAwIBAgIQUHBrzdgT/BtOOzNy0hFIjTANBgkqhkiG9w0BAQsFADBGMQswCQYDVQQG
+EwJDTjEaMBgGA1UEChMRV29TaWduIENBIExpbWl0ZWQxGzAZBgNVBAMMEkNBIOayg+mAmuagueiv
+geS5pjAeFw0wOTA4MDgwMTAwMDFaFw0zOTA4MDgwMTAwMDFaMEYxCzAJBgNVBAYTAkNOMRowGAYD
+VQQKExFXb1NpZ24gQ0EgTGltaXRlZDEbMBkGA1UEAwwSQ0Eg5rKD6YCa5qC56K+B5LmmMIICIjAN
+BgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA0EkhHiX8h8EqwqzbdoYGTufQdDTc7WU1/FDWiD+k
+8H/rD195L4mx/bxjWDeTmzj4t1up+thxx7S8gJeNbEvxUNUqKaqoGXqW5pWOdO2XCld19AXbbQs5
+uQF/qvbW2mzmBeCkTVL829B0txGMe41P/4eDrv8FAxNXUDf+jJZSEExfv5RxadmWPgxDT74wwJ85
+dE8GRV2j1lY5aAfMh09Qd5Nx2UQIsYo06Yms25tO4dnkUkWMLhQfkWsZHWgpLFbE4h4TV2TwYeO5
+Ed+w4VegG63XX9Gv2ystP9Bojg/qnw+LNVgbExz03jWhCl3W6t8Sb8D7aQdGctyB9gQjF+BNdeFy
+b7Ao65vh4YOhn0pdr8yb+gIgthhid5E7o9Vlrdx8kHccREGkSovrlXLp9glk3Kgtn3R46MGiCWOc
+76DbT52VqyBPt7D3h1ymoOQ3OMdc4zUPLK2jgKLsLl3Az+2LBcLmc272idX10kaO6m1jGx6KyX2m
++Jzr5dVjhU1zZmkR/sgO9MHHZklTfuQZa/HpelmjbX7FF+Ynxu8b22/8DU0GAbQOXDBGVWCvOGU6
+yke6rCzMRh+yRpY/8+0mBe53oWprfi1tWFxK1I5nuPHa1UaKJ/kR8slC/k7e3x9cxKSGhxYzoacX
+GKUN5AXlK8IrC6KVkLn9YDxOiT7nnO4fuwECAwEAAaNCMEAwDgYDVR0PAQH/BAQDAgEGMA8GA1Ud
+EwEB/wQFMAMBAf8wHQYDVR0OBBYEFOBNv9ybQV0T6GTwp+kVpOGBwboxMA0GCSqGSIb3DQEBCwUA
+A4ICAQBqinA4WbbaixjIvirTthnVZil6Xc1bL3McJk6jfW+rtylNpumlEYOnOXOvEESS5iVdT2H6
+yAa+Tkvv/vMx/sZ8cApBWNromUuWyXi8mHwCKe0JgOYKOoICKuLJL8hWGSbueBwj/feTZU7n85iY
+r83d2Z5AiDEoOqsuC7CsDCT6eiaY8xJhEPRdF/d+4niXVOKM6Cm6jBAyvd0zaziGfjk9DgNyp115
+j0WKWa5bIW4xRtVZjc8VX90xJc/bYNaBRHIpAlf2ltTW/+op2znFuCyKGo3Oy+dCMYYFaA6eFN0A
+kLppRQjbbpCBhqcqBT/mhDn4t/lXX0ykeVoQDF7Va/81XwVRHmyjdanPUIPTfPRm94KNPQx96N97
+qA4bLJyuQHCH2u2nFoJavjVsIE4iYdm8UXrNemHcSxH5/mc0zy4EZmFcV5cjjPOGG0jfKq+nwf/Y
+jj4Du9gqsPoUJbJRa4ZDhS4HIxaAjUz7tGM7zMN07RujHv41D198HRaG9Q7DlfEvr10lO1Hm13ZB
+ONFLAzkopR6RctR9q5czxNM+4Gm2KHmgCY0c0f9BckgG/Jou5yD5m6Leie2uPAmvylezkolwQOQv
+T8Jwg0DXJCxr5wkf09XHwQj02w47HAcLQxGEIYbpgNR12KvxAmLBsX5VYc8T1yaw15zLKYs4SgsO
+kI26oQ==
+-----END CERTIFICATE-----
+
+COMODO RSA Certification Authority
+==================================
+-----BEGIN CERTIFICATE-----
+MIIF2DCCA8CgAwIBAgIQTKr5yttjb+Af907YWwOGnTANBgkqhkiG9w0BAQwFADCBhTELMAkGA1UE
+BhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgG
+A1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNhdGlv
+biBBdXRob3JpdHkwHhcNMTAwMTE5MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMC
+R0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UE
+ChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNhdGlvbiBB
+dXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCR6FSS0gpWsawNJN3Fz0Rn
+dJkrN6N9I3AAcbxT38T6KhKPS38QVr2fcHK3YX/JSw8Xpz3jsARh7v8Rl8f0hj4K+j5c+ZPmNHrZ
+FGvnnLOFoIJ6dq9xkNfs/Q36nGz637CC9BR++b7Epi9Pf5l/tfxnQ3K9DADWietrLNPtj5gcFKt+
+5eNu/Nio5JIk2kNrYrhV/erBvGy2i/MOjZrkm2xpmfh4SDBF1a3hDTxFYPwyllEnvGfDyi62a+pG
+x8cgoLEfZd5ICLqkTqnyg0Y3hOvozIFIQ2dOciqbXL1MGyiKXCJ7tKuY2e7gUYPDCUZObT6Z+pUX
+2nwzV0E8jVHtC7ZcryxjGt9XyD+86V3Em69FmeKjWiS0uqlWPc9vqv9JWL7wqP/0uK3pN/u6uPQL
+OvnoQ0IeidiEyxPx2bvhiWC4jChWrBQdnArncevPDt09qZahSL0896+1DSJMwBGB7FY79tOi4lu3
+sgQiUpWAk2nojkxl8ZEDLXB0AuqLZxUpaVICu9ffUGpVRr+goyhhf3DQw6KqLCGqR84onAZFdr+C
+GCe01a60y1Dma/RMhnEw6abfFobg2P9A3fvQQoh/ozM6LlweQRGBY84YcWsr7KaKtzFcOmpH4MN5
+WdYgGq/yapiqcrxXStJLnbsQ/LBMQeXtHT1eKJ2czL+zUdqnR+WEUwIDAQABo0IwQDAdBgNVHQ4E
+FgQUu69+Aj36pvE8hI6t7jiY7NkyMtQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8w
+DQYJKoZIhvcNAQEMBQADggIBAArx1UaEt65Ru2yyTUEUAJNMnMvlwFTPoCWOAvn9sKIN9SCYPBMt
+rFaisNZ+EZLpLrqeLppysb0ZRGxhNaKatBYSaVqM4dc+pBroLwP0rmEdEBsqpIt6xf4FpuHA1sj+
+nq6PK7o9mfjYcwlYRm6mnPTXJ9OV2jeDchzTc+CiR5kDOF3VSXkAKRzH7JsgHAckaVd4sjn8OoSg
+tZx8jb8uk2IntznaFxiuvTwJaP+EmzzV1gsD41eeFPfR60/IvYcjt7ZJQ3mFXLrrkguhxuhoqEwW
+sRqZCuhTLJK7oQkYdQxlqHvLI7cawiiFwxv/0Cti76R7CZGYZ4wUAc1oBmpjIXUDgIiKboHGhfKp
+pC3n9KUkEEeDys30jXlYsQab5xoq2Z0B15R97QNKyvDb6KkBPvVWmckejkk9u+UJueBPSZI9FoJA
+zMxZxuY67RIuaTxslbH9qh17f4a+Hg4yRvv7E491f0yLS0Zj/gA0QHDBw7mh3aZw4gSzQbzpgJHq
+ZJx64SIDqZxubw5lT2yHh17zbqD5daWbQOhTsiedSrnAdyGN/4fy3ryM7xfft0kL0fJuMAsaDk52
+7RH89elWsn2/x20Kk4yl0MC2Hb46TpSi125sC8KKfPog88Tk5c0NqMuRkrF8hey1FGlmDoLnzc7I
+LaZRfyHBNVOFBkpdn627G190
+-----END CERTIFICATE-----
+
+USERTrust RSA Certification Authority
+=====================================
+-----BEGIN CERTIFICATE-----
+MIIF3jCCA8agAwIBAgIQAf1tMPyjylGoG7xkDjUDLTANBgkqhkiG9w0BAQwFADCBiDELMAkGA1UE
+BhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQK
+ExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBSU0EgQ2VydGlmaWNh
+dGlvbiBBdXRob3JpdHkwHhcNMTAwMjAxMDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UE
+BhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQK
+ExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBSU0EgQ2VydGlmaWNh
+dGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCAEmUXNg7D2wiz
+0KxXDXbtzSfTTK1Qg2HiqiBNCS1kCdzOiZ/MPans9s/B3PHTsdZ7NygRK0faOca8Ohm0X6a9fZ2j
+Y0K2dvKpOyuR+OJv0OwWIJAJPuLodMkYtJHUYmTbf6MG8YgYapAiPLz+E/CHFHv25B+O1ORRxhFn
+RghRy4YUVD+8M/5+bJz/Fp0YvVGONaanZshyZ9shZrHUm3gDwFA66Mzw3LyeTP6vBZY1H1dat//O
++T23LLb2VN3I5xI6Ta5MirdcmrS3ID3KfyI0rn47aGYBROcBTkZTmzNg95S+UzeQc0PzMsNT79uq
+/nROacdrjGCT3sTHDN/hMq7MkztReJVni+49Vv4M0GkPGw/zJSZrM233bkf6c0Plfg6lZrEpfDKE
+Y1WJxA3Bk1QwGROs0303p+tdOmw1XNtB1xLaqUkL39iAigmTYo61Zs8liM2EuLE/pDkP2QKe6xJM
+lXzzawWpXhaDzLhn4ugTncxbgtNMs+1b/97lc6wjOy0AvzVVdAlJ2ElYGn+SNuZRkg7zJn0cTRe8
+yexDJtC/QV9AqURE9JnnV4eeUB9XVKg+/XRjL7FQZQnmWEIuQxpMtPAlR1n6BB6T1CZGSlCBst6+
+eLf8ZxXhyVeEHg9j1uliutZfVS7qXMYoCAQlObgOK6nyTJccBz8NUvXt7y+CDwIDAQABo0IwQDAd
+BgNVHQ4EFgQUU3m/WqorSs9UgOHYm8Cd8rIDZsswDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQF
+MAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAFzUfA3P9wF9QZllDHPFUp/L+M+ZBn8b2kMVn54CVVeW
+FPFSPCeHlCjtHzoBN6J2/FNQwISbxmtOuowhT6KOVWKR82kV2LyI48SqC/3vqOlLVSoGIG1VeCkZ
+7l8wXEskEVX/JJpuXior7gtNn3/3ATiUFJVDBwn7YKnuHKsSjKCaXqeYalltiz8I+8jRRa8YFWSQ
+Eg9zKC7F4iRO/Fjs8PRF/iKz6y+O0tlFYQXBl2+odnKPi4w2r78NBc5xjeambx9spnFixdjQg3IM
+8WcRiQycE0xyNN+81XHfqnHd4blsjDwSXWXavVcStkNr/+XeTWYRUc+ZruwXtuhxkYzeSf7dNXGi
+FSeUHM9h4ya7b6NnJSFd5t0dCy5oGzuCr+yDZ4XUmFF0sbmZgIn/f3gZXHlKYC6SQK5MNyosycdi
+yA5d9zZbyuAlJQG03RoHnHcAP9Dc1ew91Pq7P8yF1m9/qS3fuQL39ZeatTXaw2ewh0qpKJ4jjv9c
+J2vhsE/zB+4ALtRZh8tSQZXq9EfX7mRBVXyNWQKV3WKdwrnuWih0hKWbt5DHDAff9Yk2dDLWKMGw
+sAvgnEzDHNb842m1R0aBL6KCq9NjRHDEjf8tM7qtj3u1cIiuPhnPQCjY/MiQu12ZIvVS5ljFH4gx
+Q+6IHdfGjjxDah2nGN59PRbxYvnKkKj9
+-----END CERTIFICATE-----
+
+USERTrust ECC Certification Authority
+=====================================
+-----BEGIN CERTIFICATE-----
+MIICjzCCAhWgAwIBAgIQXIuZxVqUxdJxVt7NiYDMJjAKBggqhkjOPQQDAzCBiDELMAkGA1UEBhMC
+VVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVU
+aGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBFQ0MgQ2VydGlmaWNhdGlv
+biBBdXRob3JpdHkwHhcNMTAwMjAxMDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UEBhMC
+VVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVU
+aGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBFQ0MgQ2VydGlmaWNhdGlv
+biBBdXRob3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQarFRaqfloI+d61SRvU8Za2EurxtW2
+0eZzca7dnNYMYf3boIkDuAUU7FfO7l0/4iGzzvfUinngo4N+LZfQYcTxmdwlkWOrfzCjtHDix6Ez
+nPO/LlxTsV+zfTJ/ijTjeXmjQjBAMB0GA1UdDgQWBBQ64QmG1M8ZwpZ2dEl23OA1xmNjmjAOBgNV
+HQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjA2Z6EWCNzklwBB
+HU6+4WMBzzuqQhFkoJ2UOQIReVx7Hfpkue4WQrO/isIJxOzksU0CMQDpKmFHjFJKS04YcPbWRNZu
+9YO6bVi9JNlWSOrvxKJGgYhqOkbRqZtNyWHa0V1Xahg=
+-----END CERTIFICATE-----
+
+GlobalSign ECC Root CA - R4
+===========================
+-----BEGIN CERTIFICATE-----
+MIIB4TCCAYegAwIBAgIRKjikHJYKBN5CsiilC+g0mAIwCgYIKoZIzj0EAwIwUDEkMCIGA1UECxMb
+R2xvYmFsU2lnbiBFQ0MgUm9vdCBDQSAtIFI0MRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQD
+EwpHbG9iYWxTaWduMB4XDTEyMTExMzAwMDAwMFoXDTM4MDExOTAzMTQwN1owUDEkMCIGA1UECxMb
+R2xvYmFsU2lnbiBFQ0MgUm9vdCBDQSAtIFI0MRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQD
+EwpHbG9iYWxTaWduMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEuMZ5049sJQ6fLjkZHAOkrprl
+OQcJFspjsbmG+IpXwVfOQvpzofdlQv8ewQCybnMO/8ch5RikqtlxP6jUuc6MHaNCMEAwDgYDVR0P
+AQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFFSwe61FuOJAf/sKbvu+M8k8o4TV
+MAoGCCqGSM49BAMCA0gAMEUCIQDckqGgE6bPA7DmxCGXkPoUVy0D7O48027KqGx2vKLeuwIgJ6iF
+JzWbVsaj8kfSt24bAgAXqmemFZHe+pTsewv4n4Q=
+-----END CERTIFICATE-----
+
+GlobalSign ECC Root CA - R5
+===========================
+-----BEGIN CERTIFICATE-----
+MIICHjCCAaSgAwIBAgIRYFlJ4CYuu1X5CneKcflK2GwwCgYIKoZIzj0EAwMwUDEkMCIGA1UECxMb
+R2xvYmFsU2lnbiBFQ0MgUm9vdCBDQSAtIFI1MRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQD
+EwpHbG9iYWxTaWduMB4XDTEyMTExMzAwMDAwMFoXDTM4MDExOTAzMTQwN1owUDEkMCIGA1UECxMb
+R2xvYmFsU2lnbiBFQ0MgUm9vdCBDQSAtIFI1MRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQD
+EwpHbG9iYWxTaWduMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAER0UOlvt9Xb/pOdEh+J8LttV7HpI6
+SFkc8GIxLcB6KP4ap1yztsyX50XUWPrRd21DosCHZTQKH3rd6zwzocWdTaRvQZU4f8kehOvRnkmS
+h5SHDDqFSmafnVmTTZdhBoZKo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAd
+BgNVHQ4EFgQUPeYpSJvqB8ohREom3m7e0oPQn1kwCgYIKoZIzj0EAwMDaAAwZQIxAOVpEslu28Yx
+uglB4Zf4+/2a4n0Sye18ZNPLBSWLVtmg515dTguDnFt2KaAJJiFqYgIwcdK1j1zqO+F4CYWodZI7
+yFz9SO8NdCKoCOJuxUnOxwy8p2Fp8fc74SrL+SvzZpA3
+-----END CERTIFICATE-----
+
+Staat der Nederlanden Root CA - G3
+==================================
+-----BEGIN CERTIFICATE-----
+MIIFdDCCA1ygAwIBAgIEAJiiOTANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJOTDEeMBwGA1UE
+CgwVU3RhYXQgZGVyIE5lZGVybGFuZGVuMSswKQYDVQQDDCJTdGFhdCBkZXIgTmVkZXJsYW5kZW4g
+Um9vdCBDQSAtIEczMB4XDTEzMTExNDExMjg0MloXDTI4MTExMzIzMDAwMFowWjELMAkGA1UEBhMC
+TkwxHjAcBgNVBAoMFVN0YWF0IGRlciBOZWRlcmxhbmRlbjErMCkGA1UEAwwiU3RhYXQgZGVyIE5l
+ZGVybGFuZGVuIFJvb3QgQ0EgLSBHMzCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAL4y
+olQPcPssXFnrbMSkUeiFKrPMSjTysF/zDsccPVMeiAho2G89rcKezIJnByeHaHE6n3WWIkYFsO2t
+x1ueKt6c/DrGlaf1F2cY5y9JCAxcz+bMNO14+1Cx3Gsy8KL+tjzk7FqXxz8ecAgwoNzFs21v0IJy
+EavSgWhZghe3eJJg+szeP4TrjTgzkApyI/o1zCZxMdFyKJLZWyNtZrVtB0LrpjPOktvA9mxjeM3K
+Tj215VKb8b475lRgsGYeCasH/lSJEULR9yS6YHgamPfJEf0WwTUaVHXvQ9Plrk7O53vDxk5hUUur
+mkVLoR9BvUhTFXFkC4az5S6+zqQbwSmEorXLCCN2QyIkHxcE1G6cxvx/K2Ya7Irl1s9N9WMJtxU5
+1nus6+N86U78dULI7ViVDAZCopz35HCz33JvWjdAidiFpNfxC95DGdRKWCyMijmev4SH8RY7Ngzp
+07TKbBlBUgmhHbBqv4LvcFEhMtwFdozL92TkA1CvjJFnq8Xy7ljY3r735zHPbMk7ccHViLVlvMDo
+FxcHErVc0qsgk7TmgoNwNsXNo42ti+yjwUOH5kPiNL6VizXtBznaqB16nzaeErAMZRKQFWDZJkBE
+41ZgpRDUajz9QdwOWke275dhdU/Z/seyHdTtXUmzqWrLZoQT1Vyg3N9udwbRcXXIV2+vD3dbAgMB
+AAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBRUrfrHkleu
+yjWcLhL75LpdINyUVzANBgkqhkiG9w0BAQsFAAOCAgEAMJmdBTLIXg47mAE6iqTnB/d6+Oea31BD
+U5cqPco8R5gu4RV78ZLzYdqQJRZlwJ9UXQ4DO1t3ApyEtg2YXzTdO2PCwyiBwpwpLiniyMMB8jPq
+KqrMCQj3ZWfGzd/TtiunvczRDnBfuCPRy5FOCvTIeuXZYzbB1N/8Ipf3YF3qKS9Ysr1YvY2WTxB1
+v0h7PVGHoTx0IsL8B3+A3MSs/mrBcDCw6Y5p4ixpgZQJut3+TcCDjJRYwEYgr5wfAvg1VUkvRtTA
+8KCWAg8zxXHzniN9lLf9OtMJgwYh/WA9rjLA0u6NpvDntIJ8CsxwyXmA+P5M9zWEGYox+wrZ13+b
+8KKaa8MFSu1BYBQw0aoRQm7TIwIEC8Zl3d1Sd9qBa7Ko+gE4uZbqKmxnl4mUnrzhVNXkanjvSr0r
+mj1AfsbAddJu+2gw7OyLnflJNZoaLNmzlTnVHpL3prllL+U9bTpITAjc5CgSKL59NVzq4BZ+Extq
+1z7XnvwtdbLBFNUjA9tbbws+eC8N3jONFrdI54OagQ97wUNNVQQXOEpR1VmiiXTTn74eS9fGbbeI
+JG9gkaSChVtWQbzQRKtqE77RLFi3EjNYsjdj3BP1lB0/QFH1T/U67cjF68IeHRaVesd+QnGTbksV
+tzDfqu1XhUisHWrdOWnk4Xl4vs4Fv6EM94B7IWcnMFk=
+-----END CERTIFICATE-----
+
+Staat der Nederlanden EV Root CA
+================================
+-----BEGIN CERTIFICATE-----
+MIIFcDCCA1igAwIBAgIEAJiWjTANBgkqhkiG9w0BAQsFADBYMQswCQYDVQQGEwJOTDEeMBwGA1UE
+CgwVU3RhYXQgZGVyIE5lZGVybGFuZGVuMSkwJwYDVQQDDCBTdGFhdCBkZXIgTmVkZXJsYW5kZW4g
+RVYgUm9vdCBDQTAeFw0xMDEyMDgxMTE5MjlaFw0yMjEyMDgxMTEwMjhaMFgxCzAJBgNVBAYTAk5M
+MR4wHAYDVQQKDBVTdGFhdCBkZXIgTmVkZXJsYW5kZW4xKTAnBgNVBAMMIFN0YWF0IGRlciBOZWRl
+cmxhbmRlbiBFViBSb290IENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA48d+ifkk
+SzrSM4M1LGns3Amk41GoJSt5uAg94JG6hIXGhaTK5skuU6TJJB79VWZxXSzFYGgEt9nCUiY4iKTW
+O0Cmws0/zZiTs1QUWJZV1VD+hq2kY39ch/aO5ieSZxeSAgMs3NZmdO3dZ//BYY1jTw+bbRcwJu+r
+0h8QoPnFfxZpgQNH7R5ojXKhTbImxrpsX23Wr9GxE46prfNeaXUmGD5BKyF/7otdBwadQ8QpCiv8
+Kj6GyzyDOvnJDdrFmeK8eEEzduG/L13lpJhQDBXd4Pqcfzho0LKmeqfRMb1+ilgnQ7O6M5HTp5gV
+XJrm0w912fxBmJc+qiXbj5IusHsMX/FjqTf5m3VpTCgmJdrV8hJwRVXj33NeN/UhbJCONVrJ0yPr
+08C+eKxCKFhmpUZtcALXEPlLVPxdhkqHz3/KRawRWrUgUY0viEeXOcDPusBCAUCZSCELa6fS/ZbV
+0b5GnUngC6agIk440ME8MLxwjyx1zNDFjFE7PZQIZCZhfbnDZY8UnCHQqv0XcgOPvZuM5l5Tnrmd
+74K74bzickFbIZTTRTeU0d8JOV3nI6qaHcptqAqGhYqCvkIH1vI4gnPah1vlPNOePqc7nvQDs/nx
+fRN0Av+7oeX6AHkcpmZBiFxgV6YuCcS6/ZrPpx9Aw7vMWgpVSzs4dlG4Y4uElBbmVvMCAwEAAaNC
+MEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFP6rAJCYniT8qcwa
+ivsnuL8wbqg7MA0GCSqGSIb3DQEBCwUAA4ICAQDPdyxuVr5Os7aEAJSrR8kN0nbHhp8dB9O2tLsI
+eK9p0gtJ3jPFrK3CiAJ9Brc1AsFgyb/E6JTe1NOpEyVa/m6irn0F3H3zbPB+po3u2dfOWBfoqSmu
+c0iH55vKbimhZF8ZE/euBhD/UcabTVUlT5OZEAFTdfETzsemQUHSv4ilf0X8rLiltTMMgsT7B/Zq
+5SWEXwbKwYY5EdtYzXc7LMJMD16a4/CrPmEbUCTCwPTxGfARKbalGAKb12NMcIxHowNDXLldRqAN
+b/9Zjr7dn3LDWyvfjFvO5QxGbJKyCqNMVEIYFRIYvdr8unRu/8G2oGTYqV9Vrp9canaW2HNnh/tN
+f1zuacpzEPuKqf2evTY4SUmH9A4U8OmHuD+nT3pajnnUk+S7aFKErGzp85hwVXIy+TSrK0m1zSBi
+5Dp6Z2Orltxtrpfs/J92VoguZs9btsmksNcFuuEnL5O7Jiqik7Ab846+HUCjuTaPPoIaGl6I6lD4
+WeKDRikL40Rc4ZW2aZCaFG+XroHPaO+Zmr615+F/+PoTRxZMzG0IQOeLeG9QgkRQP2YGiqtDhFZK
+DyAthg710tvSeopLzaXoTvFeJiUBWSOgftL2fiFX1ye8FVdMpEbB4IMeDExNH08GGeL5qPQ6gqGy
+eUN51q1veieQA6TqJIc/2b3Z6fJfUEkc7uzXLg==
+-----END CERTIFICATE-----
+
+IdenTrust Commercial Root CA 1
+==============================
+-----BEGIN CERTIFICATE-----
+MIIFYDCCA0igAwIBAgIQCgFCgAAAAUUjyES1AAAAAjANBgkqhkiG9w0BAQsFADBKMQswCQYDVQQG
+EwJVUzESMBAGA1UEChMJSWRlblRydXN0MScwJQYDVQQDEx5JZGVuVHJ1c3QgQ29tbWVyY2lhbCBS
+b290IENBIDEwHhcNMTQwMTE2MTgxMjIzWhcNMzQwMTE2MTgxMjIzWjBKMQswCQYDVQQGEwJVUzES
+MBAGA1UEChMJSWRlblRydXN0MScwJQYDVQQDEx5JZGVuVHJ1c3QgQ29tbWVyY2lhbCBSb290IENB
+IDEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCnUBneP5k91DNG8W9RYYKyqU+PZ4ld
+hNlT3Qwo2dfw/66VQ3KZ+bVdfIrBQuExUHTRgQ18zZshq0PirK1ehm7zCYofWjK9ouuU+ehcCuz/
+mNKvcbO0U59Oh++SvL3sTzIwiEsXXlfEU8L2ApeN2WIrvyQfYo3fw7gpS0l4PJNgiCL8mdo2yMKi
+1CxUAGc1bnO/AljwpN3lsKImesrgNqUZFvX9t++uP0D1bVoE/c40yiTcdCMbXTMTEl3EASX2MN0C
+XZ/g1Ue9tOsbobtJSdifWwLziuQkkORiT0/Br4sOdBeo0XKIanoBScy0RnnGF7HamB4HWfp1IYVl
+3ZBWzvurpWCdxJ35UrCLvYf5jysjCiN2O/cz4ckA82n5S6LgTrx+kzmEB/dEcH7+B1rlsazRGMzy
+NeVJSQjKVsk9+w8YfYs7wRPCTY/JTw436R+hDmrfYi7LNQZReSzIJTj0+kuniVyc0uMNOYZKdHzV
+WYfCP04MXFL0PfdSgvHqo6z9STQaKPNBiDoT7uje/5kdX7rL6B7yuVBgwDHTc+XvvqDtMwt0viAg
+xGds8AgDelWAf0ZOlqf0Hj7h9tgJ4TNkK2PXMl6f+cB7D3hvl7yTmvmcEpB4eoCHFddydJxVdHix
+uuFucAS6T6C6aMN7/zHwcz09lCqxC0EOoP5NiGVreTO01wIDAQABo0IwQDAOBgNVHQ8BAf8EBAMC
+AQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU7UQZwNPwBovupHu+QucmVMiONnYwDQYJKoZI
+hvcNAQELBQADggIBAA2ukDL2pkt8RHYZYR4nKM1eVO8lvOMIkPkp165oCOGUAFjvLi5+U1KMtlwH
+6oi6mYtQlNeCgN9hCQCTrQ0U5s7B8jeUeLBfnLOic7iPBZM4zY0+sLj7wM+x8uwtLRvM7Kqas6pg
+ghstO8OEPVeKlh6cdbjTMM1gCIOQ045U8U1mwF10A0Cj7oV+wh93nAbowacYXVKV7cndJZ5t+qnt
+ozo00Fl72u1Q8zW/7esUTTHHYPTa8Yec4kjixsU3+wYQ+nVZZjFHKdp2mhzpgq7vmrlR94gjmmmV
+YjzlVYA211QC//G5Xc7UI2/YRYRKW2XviQzdFKcgyxilJbQN+QHwotL0AMh0jqEqSI5l2xPE4iUX
+feu+h1sXIFRRk0pTAwvsXcoz7WL9RccvW9xYoIA55vrX/hMUpu09lEpCdNTDd1lzzY9GvlU47/ro
+kTLql1gEIt44w8y8bckzOmoKaT+gyOpyj4xjhiO9bTyWnpXgSUyqorkqG5w2gXjtw+hG4iZZRHUe
+2XWJUc0QhJ1hYMtd+ZciTY6Y5uN/9lu7rs3KSoFrXgvzUeF0K+l+J6fZmUlO+KWA2yUPHGNiiskz
+Z2s8EIPGrd6ozRaOjfAHN3Gf8qv8QfXBi+wAN10J5U6A7/qxXDgGpRtK4dw4LTzcqx+QGtVKnO7R
+cGzM7vRX+Bi6hG6H
+-----END CERTIFICATE-----
+
+IdenTrust Public Sector Root CA 1
+=================================
+-----BEGIN CERTIFICATE-----
+MIIFZjCCA06gAwIBAgIQCgFCgAAAAUUjz0Z8AAAAAjANBgkqhkiG9w0BAQsFADBNMQswCQYDVQQG
+EwJVUzESMBAGA1UEChMJSWRlblRydXN0MSowKAYDVQQDEyFJZGVuVHJ1c3QgUHVibGljIFNlY3Rv
+ciBSb290IENBIDEwHhcNMTQwMTE2MTc1MzMyWhcNMzQwMTE2MTc1MzMyWjBNMQswCQYDVQQGEwJV
+UzESMBAGA1UEChMJSWRlblRydXN0MSowKAYDVQQDEyFJZGVuVHJ1c3QgUHVibGljIFNlY3RvciBS
+b290IENBIDEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC2IpT8pEiv6EdrCvsnduTy
+P4o7ekosMSqMjbCpwzFrqHd2hCa2rIFCDQjrVVi7evi8ZX3yoG2LqEfpYnYeEe4IFNGyRBb06tD6
+Hi9e28tzQa68ALBKK0CyrOE7S8ItneShm+waOh7wCLPQ5CQ1B5+ctMlSbdsHyo+1W/CD80/HLaXI
+rcuVIKQxKFdYWuSNG5qrng0M8gozOSI5Cpcu81N3uURF/YTLNiCBWS2ab21ISGHKTN9T0a9SvESf
+qy9rg3LvdYDaBjMbXcjaY8ZNzaxmMc3R3j6HEDbhuaR672BQssvKplbgN6+rNBM5Jeg5ZuSYeqoS
+mJxZZoY+rfGwyj4GD3vwEUs3oERte8uojHH01bWRNszwFcYr3lEXsZdMUD2xlVl8BX0tIdUAvwFn
+ol57plzy9yLxkA2T26pEUWbMfXYD62qoKjgZl3YNa4ph+bz27nb9cCvdKTz4Ch5bQhyLVi9VGxyh
+LrXHFub4qjySjmm2AcG1hp2JDws4lFTo6tyePSW8Uybt1as5qsVATFSrsrTZ2fjXctscvG29ZV/v
+iDUqZi/u9rNl8DONfJhBaUYPQxxp+pu10GFqzcpL2UyQRqsVWaFHVCkugyhfHMKiq3IXAAaOReyL
+4jM9f9oZRORicsPfIsbyVtTdX5Vy7W1f90gDW/3FKqD2cyOEEBsB5wIDAQABo0IwQDAOBgNVHQ8B
+Af8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU43HgntinQtnbcZFrlJPrw6PRFKMw
+DQYJKoZIhvcNAQELBQADggIBAEf63QqwEZE4rU1d9+UOl1QZgkiHVIyqZJnYWv6IAcVYpZmxI1Qj
+t2odIFflAWJBF9MJ23XLblSQdf4an4EKwt3X9wnQW3IV5B4Jaj0z8yGa5hV+rVHVDRDtfULAj+7A
+mgjVQdZcDiFpboBhDhXAuM/FSRJSzL46zNQuOAXeNf0fb7iAaJg9TaDKQGXSc3z1i9kKlT/YPyNt
+GtEqJBnZhbMX73huqVjRI9PHE+1yJX9dsXNw0H8GlwmEKYBhHfpe/3OsoOOJuBxxFcbeMX8S3OFt
+m6/n6J91eEyrRjuazr8FGF1NFTwWmhlQBJqymm9li1JfPFgEKCXAZmExfrngdbkaqIHWchezxQMx
+NRF4eKLg6TCMf4DfWN88uieW4oA0beOY02QnrEh+KHdcxiVhJfiFDGX6xDIvpZgF5PgLZxYWxoK4
+Mhn5+bl53B/N66+rDt0b20XkeucC4pVd/GnwU2lhlXV5C15V5jgclKlZM57IcXR5f1GJtshquDDI
+ajjDbp7hNxbqBWJMWxJH7ae0s1hWx0nzfxJoCTFx8G34Tkf71oXuxVhAGaQdp/lLQzfcaFpPz+vC
+ZHTetBXZ9FRUGi8c15dxVJCO2SCdUyt/q4/i6jC8UDfv8Ue1fXwsBOxonbRJRBD0ckscZOf85muQ
+3Wl9af0AVqW3rLatt8o+Ae+c
+-----END CERTIFICATE-----
+
+Entrust Root Certification Authority - G2
+=========================================
+-----BEGIN CERTIFICATE-----
+MIIEPjCCAyagAwIBAgIESlOMKDANBgkqhkiG9w0BAQsFADCBvjELMAkGA1UEBhMCVVMxFjAUBgNV
+BAoTDUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50cnVzdC5uZXQvbGVnYWwtdGVy
+bXMxOTA3BgNVBAsTMChjKSAyMDA5IEVudHJ1c3QsIEluYy4gLSBmb3IgYXV0aG9yaXplZCB1c2Ug
+b25seTEyMDAGA1UEAxMpRW50cnVzdCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzIw
+HhcNMDkwNzA3MTcyNTU0WhcNMzAxMjA3MTc1NTU0WjCBvjELMAkGA1UEBhMCVVMxFjAUBgNVBAoT
+DUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50cnVzdC5uZXQvbGVnYWwtdGVybXMx
+OTA3BgNVBAsTMChjKSAyMDA5IEVudHJ1c3QsIEluYy4gLSBmb3IgYXV0aG9yaXplZCB1c2Ugb25s
+eTEyMDAGA1UEAxMpRW50cnVzdCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzIwggEi
+MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6hLZy254Ma+KZ6TABp3bqMriVQRrJ2mFOWHLP
+/vaCeb9zYQYKpSfYs1/TRU4cctZOMvJyig/3gxnQaoCAAEUesMfnmr8SVycco2gvCoe9amsOXmXz
+HHfV1IWNcCG0szLni6LVhjkCsbjSR87kyUnEO6fe+1R9V77w6G7CebI6C1XiUJgWMhNcL3hWwcKU
+s/Ja5CeanyTXxuzQmyWC48zCxEXFjJd6BmsqEZ+pCm5IO2/b1BEZQvePB7/1U1+cPvQXLOZprE4y
+TGJ36rfo5bs0vBmLrpxR57d+tVOxMyLlbc9wPBr64ptntoP0jaWvYkxN4FisZDQSA/i2jZRjJKRx
+AgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRqciZ6
+0B7vfec7aVHUbI2fkBJmqzANBgkqhkiG9w0BAQsFAAOCAQEAeZ8dlsa2eT8ijYfThwMEYGprmi5Z
+iXMRrEPR9RP/jTkrwPK9T3CMqS/qF8QLVJ7UG5aYMzyorWKiAHarWWluBh1+xLlEjZivEtRh2woZ
+Rkfz6/djwUAFQKXSt/S1mja/qYh2iARVBCuch38aNzx+LaUa2NSJXsq9rD1s2G2v1fN2D807iDgi
+nWyTmsQ9v4IbZT+mD12q/OWyFcq1rca8PdCE6OoGcrBNOTJ4vz4RnAuknZoh8/CbCzB428Hch0P+
+vGOaysXCHMnHjf87ElgI5rY97HosTvuDls4MPGmHVHOkc8KT/1EQrBVUAdj8BbGJoX90g5pJ19xO
+e4pIb4tF9g==
+-----END CERTIFICATE-----
+
+Entrust Root Certification Authority - EC1
+==========================================
+-----BEGIN CERTIFICATE-----
+MIIC+TCCAoCgAwIBAgINAKaLeSkAAAAAUNCR+TAKBggqhkjOPQQDAzCBvzELMAkGA1UEBhMCVVMx
+FjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50cnVzdC5uZXQvbGVn
+YWwtdGVybXMxOTA3BgNVBAsTMChjKSAyMDEyIEVudHJ1c3QsIEluYy4gLSBmb3IgYXV0aG9yaXpl
+ZCB1c2Ugb25seTEzMDEGA1UEAxMqRW50cnVzdCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5
+IC0gRUMxMB4XDTEyMTIxODE1MjUzNloXDTM3MTIxODE1NTUzNlowgb8xCzAJBgNVBAYTAlVTMRYw
+FAYDVQQKEw1FbnRydXN0LCBJbmMuMSgwJgYDVQQLEx9TZWUgd3d3LmVudHJ1c3QubmV0L2xlZ2Fs
+LXRlcm1zMTkwNwYDVQQLEzAoYykgMjAxMiBFbnRydXN0LCBJbmMuIC0gZm9yIGF1dGhvcml6ZWQg
+dXNlIG9ubHkxMzAxBgNVBAMTKkVudHJ1c3QgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAt
+IEVDMTB2MBAGByqGSM49AgEGBSuBBAAiA2IABIQTydC6bUF74mzQ61VfZgIaJPRbiWlH47jCffHy
+AsWfoPZb1YsGGYZPUxBtByQnoaD41UcZYUx9ypMn6nQM72+WCf5j7HBdNq1nd67JnXxVRDqiY1Ef
+9eNi1KlHBz7MIKNCMEAwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYE
+FLdj5xrdjekIplWDpOBqUEFlEUJJMAoGCCqGSM49BAMDA2cAMGQCMGF52OVCR98crlOZF7ZvHH3h
+vxGU0QOIdeSNiaSKd0bebWHvAvX7td/M/k7//qnmpwIwW5nXhTcGtXsI/esni0qU+eH6p44mCOh8
+kmhtc9hvJqwhAriZtyZBWyVgrtBIGu4G
+-----END CERTIFICATE-----
+
+CFCA EV ROOT
+============
+-----BEGIN CERTIFICATE-----
+MIIFjTCCA3WgAwIBAgIEGErM1jANBgkqhkiG9w0BAQsFADBWMQswCQYDVQQGEwJDTjEwMC4GA1UE
+CgwnQ2hpbmEgRmluYW5jaWFsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MRUwEwYDVQQDDAxDRkNB
+IEVWIFJPT1QwHhcNMTIwODA4MDMwNzAxWhcNMjkxMjMxMDMwNzAxWjBWMQswCQYDVQQGEwJDTjEw
+MC4GA1UECgwnQ2hpbmEgRmluYW5jaWFsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MRUwEwYDVQQD
+DAxDRkNBIEVWIFJPT1QwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDXXWvNED8fBVnV
+BU03sQ7smCuOFR36k0sXgiFxEFLXUWRwFsJVaU2OFW2fvwwbwuCjZ9YMrM8irq93VCpLTIpTUnrD
+7i7es3ElweldPe6hL6P3KjzJIx1qqx2hp/Hz7KDVRM8Vz3IvHWOX6Jn5/ZOkVIBMUtRSqy5J35DN
+uF++P96hyk0g1CXohClTt7GIH//62pCfCqktQT+x8Rgp7hZZLDRJGqgG16iI0gNyejLi6mhNbiyW
+ZXvKWfry4t3uMCz7zEasxGPrb382KzRzEpR/38wmnvFyXVBlWY9ps4deMm/DGIq1lY+wejfeWkU7
+xzbh72fROdOXW3NiGUgthxwG+3SYIElz8AXSG7Ggo7cbcNOIabla1jj0Ytwli3i/+Oh+uFzJlU9f
+py25IGvPa931DfSCt/SyZi4QKPaXWnuWFo8BGS1sbn85WAZkgwGDg8NNkt0yxoekN+kWzqotaK8K
+gWU6cMGbrU1tVMoqLUuFG7OA5nBFDWteNfB/O7ic5ARwiRIlk9oKmSJgamNgTnYGmE69g60dWIol
+hdLHZR4tjsbftsbhf4oEIRUpdPA+nJCdDC7xij5aqgwJHsfVPKPtl8MeNPo4+QgO48BdK4PRVmrJ
+tqhUUy54Mmc9gn900PvhtgVguXDbjgv5E1hvcWAQUhC5wUEJ73IfZzF4/5YFjQIDAQABo2MwYTAf
+BgNVHSMEGDAWgBTj/i39KNALtbq2osS/BqoFjJP7LzAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB
+/wQEAwIBBjAdBgNVHQ4EFgQU4/4t/SjQC7W6tqLEvwaqBYyT+y8wDQYJKoZIhvcNAQELBQADggIB
+ACXGumvrh8vegjmWPfBEp2uEcwPenStPuiB/vHiyz5ewG5zz13ku9Ui20vsXiObTej/tUxPQ4i9q
+ecsAIyjmHjdXNYmEwnZPNDatZ8POQQaIxffu2Bq41gt/UP+TqhdLjOztUmCypAbqTuv0axn96/Ua
+4CUqmtzHQTb3yHQFhDmVOdYLO6Qn+gjYXB74BGBSESgoA//vU2YApUo0FmZ8/Qmkrp5nGm9BC2sG
+E5uPhnEFtC+NiWYzKXZUmhH4J/qyP5Hgzg0b8zAarb8iXRvTvyUFTeGSGn+ZnzxEk8rUQElsgIfX
+BDrDMlI1Dlb4pd19xIsNER9Tyx6yF7Zod1rg1MvIB671Oi6ON7fQAUtDKXeMOZePglr4UeWJoBjn
+aH9dCi77o0cOPaYjesYBx4/IXr9tgFa+iiS6M+qf4TIRnvHST4D2G0CvOJ4RUHlzEhLN5mydLIhy
+PDCBBpEi6lmt2hkuIsKNuYyH4Ga8cyNfIWRjgEj1oDwYPZTISEEdQLpe/v5WOaHIz16eGWRGENoX
+kbcFgKyLmZJ956LYBws2J+dIeWCKw9cTXPhyQN9Ky8+ZAAoACxGV2lZFA4gKn2fQ1XmxqI1AbQ3C
+ekD6819kR5LLU7m7Wc5P/dAVUwHY3+vZ5nbv0CO7O6l5s9UCKc2Jo5YPSjXnTkLAdc0Hz+Ys63su
+-----END CERTIFICATE-----
+
+TÜRKTRUST Elektronik Sertifika Hizmet Sağlayıcısı H5
+=========================================================
+-----BEGIN CERTIFICATE-----
+MIIEJzCCAw+gAwIBAgIHAI4X/iQggTANBgkqhkiG9w0BAQsFADCBsTELMAkGA1UEBhMCVFIxDzAN
+BgNVBAcMBkFua2FyYTFNMEsGA1UECgxEVMOcUktUUlVTVCBCaWxnaSDEsGxldGnFn2ltIHZlIEJp
+bGnFn2ltIEfDvHZlbmxpxJ9pIEhpem1ldGxlcmkgQS7Fni4xQjBABgNVBAMMOVTDnFJLVFJVU1Qg
+RWxla3Ryb25payBTZXJ0aWZpa2EgSGl6bWV0IFNhxJ9sYXnEsWPEsXPEsSBINTAeFw0xMzA0MzAw
+ODA3MDFaFw0yMzA0MjgwODA3MDFaMIGxMQswCQYDVQQGEwJUUjEPMA0GA1UEBwwGQW5rYXJhMU0w
+SwYDVQQKDERUw5xSS1RSVVNUIEJpbGdpIMSwbGV0acWfaW0gdmUgQmlsacWfaW0gR8O8dmVubGnE
+n2kgSGl6bWV0bGVyaSBBLsWeLjFCMEAGA1UEAww5VMOcUktUUlVTVCBFbGVrdHJvbmlrIFNlcnRp
+ZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxIEg1MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
+CgKCAQEApCUZ4WWe60ghUEoI5RHwWrom/4NZzkQqL/7hzmAD/I0Dpe3/a6i6zDQGn1k19uwsu537
+jVJp45wnEFPzpALFp/kRGml1bsMdi9GYjZOHp3GXDSHHmflS0yxjXVW86B8BSLlg/kJK9siArs1m
+ep5Fimh34khon6La8eHBEJ/rPCmBp+EyCNSgBbGM+42WAA4+Jd9ThiI7/PS98wl+d+yG6w8z5UNP
+9FR1bSmZLmZaQ9/LXMrI5Tjxfjs1nQ/0xVqhzPMggCTTV+wVunUlm+hkS7M0hO8EuPbJbKoCPrZV
+4jI3X/xml1/N1p7HIL9Nxqw/dV8c7TKcfGkAaZHjIxhT6QIDAQABo0IwQDAdBgNVHQ4EFgQUVpkH
+HtOsDGlktAxQR95DLL4gwPswDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZI
+hvcNAQELBQADggEBAJ5FdnsXSDLyOIspve6WSk6BGLFRRyDN0GSxDsnZAdkJzsiZ3GglE9Rc8qPo
+BP5yCccLqh0lVX6Wmle3usURehnmp349hQ71+S4pL+f5bFgWV1Al9j4uPqrtd3GqqpmWRgqujuwq
+URawXs3qZwQcWDD1YIq9pr1N5Za0/EKJAWv2cMhQOQwt1WbZyNKzMrcbGW3LM/nfpeYVhDfwwvJl
+lpKQd/Ct9JDpEXjXk4nAPQu6KfTomZ1yju2dL+6SfaHx/126M2CFYv4HAqGEVka+lgqaE9chTLd8
+B59OTj+RdPsnnRHM3eaxynFNExc5JsUpISuTKWqW+qtB4Uu2NQvAmxU=
+-----END CERTIFICATE-----
+
+TÜRKTRUST Elektronik Sertifika Hizmet Sağlayıcısı H6
+=========================================================
+-----BEGIN CERTIFICATE-----
+MIIEJjCCAw6gAwIBAgIGfaHyZeyKMA0GCSqGSIb3DQEBCwUAMIGxMQswCQYDVQQGEwJUUjEPMA0G
+A1UEBwwGQW5rYXJhMU0wSwYDVQQKDERUw5xSS1RSVVNUIEJpbGdpIMSwbGV0acWfaW0gdmUgQmls
+acWfaW0gR8O8dmVubGnEn2kgSGl6bWV0bGVyaSBBLsWeLjFCMEAGA1UEAww5VMOcUktUUlVTVCBF
+bGVrdHJvbmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxIEg2MB4XDTEzMTIxODA5
+MDQxMFoXDTIzMTIxNjA5MDQxMFowgbExCzAJBgNVBAYTAlRSMQ8wDQYDVQQHDAZBbmthcmExTTBL
+BgNVBAoMRFTDnFJLVFJVU1QgQmlsZ2kgxLBsZXRpxZ9pbSB2ZSBCaWxpxZ9pbSBHw7x2ZW5sacSf
+aSBIaXptZXRsZXJpIEEuxZ4uMUIwQAYDVQQDDDlUw5xSS1RSVVNUIEVsZWt0cm9uaWsgU2VydGlm
+aWthIEhpem1ldCBTYcSfbGF5xLFjxLFzxLEgSDYwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
+AoIBAQCdsGjW6L0UlqMACprx9MfMkU1xeHe59yEmFXNRFpQJRwXiM/VomjX/3EsvMsew7eKC5W/a
+2uqsxgbPJQ1BgfbBOCK9+bGlprMBvD9QFyv26WZV1DOzXPhDIHiTVRZwGTLmiddk671IUP320EED
+wnS3/faAz1vFq6TWlRKb55cTMgPp1KtDWxbtMyJkKbbSk60vbNg9tvYdDjTu0n2pVQ8g9P0pu5Fb
+HH3GQjhtQiht1AH7zYiXSX6484P4tZgvsycLSF5W506jM7NE1qXyGJTtHB6plVxiSvgNZ1GpryHV
++DKdeboaX+UEVU0TRv/yz3THGmNtwx8XEsMeED5gCLMxAgMBAAGjQjBAMB0GA1UdDgQWBBTdVRcT
+9qzoSCHK77Wv0QAy7Z6MtTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG
+9w0BAQsFAAOCAQEAb1gNl0OqFlQ+v6nfkkU/hQu7VtMMUszIv3ZnXuaqs6fvuay0EBQNdH49ba3R
+fdCaqaXKGDsCQC4qnFAUi/5XfldcEQlLNkVS9z2sFP1E34uXI9TDwe7UU5X+LEr+DXCqu4svLcsy
+o4LyVN/Y8t3XSHLuSqMplsNEzm61kod2pLv0kmzOLBQJZo6NrRa1xxsJYTvjIKIDgI6tflEATseW
+hvtDmHd9KMeP2Cpu54Rvl0EpABZeTeIT6lnAY2c6RPuY/ATTMHKm9ocJV612ph1jmv3XZch4gyt1
+O6VbuA1df74jrlZVlFjvH4GMKrLN5ptjnhi85WsGtAuYSyher4hYyw==
+-----END CERTIFICATE-----
+
+Certinomis - Root CA
+====================
+-----BEGIN CERTIFICATE-----
+MIIFkjCCA3qgAwIBAgIBATANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJGUjETMBEGA1UEChMK
+Q2VydGlub21pczEXMBUGA1UECxMOMDAwMiA0MzM5OTg5MDMxHTAbBgNVBAMTFENlcnRpbm9taXMg
+LSBSb290IENBMB4XDTEzMTAyMTA5MTcxOFoXDTMzMTAyMTA5MTcxOFowWjELMAkGA1UEBhMCRlIx
+EzARBgNVBAoTCkNlcnRpbm9taXMxFzAVBgNVBAsTDjAwMDIgNDMzOTk4OTAzMR0wGwYDVQQDExRD
+ZXJ0aW5vbWlzIC0gUm9vdCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANTMCQos
+P5L2fxSeC5yaah1AMGT9qt8OHgZbn1CF6s2Nq0Nn3rD6foCWnoR4kkjW4znuzuRZWJflLieY6pOo
+d5tK8O90gC3rMB+12ceAnGInkYjwSond3IjmFPnVAy//ldu9n+ws+hQVWZUKxkd8aRi5pwP5ynap
+z8dvtF4F/u7BUrJ1Mofs7SlmO/NKFoL21prbcpjp3vDFTKWrteoB4owuZH9kb/2jJZOLyKIOSY00
+8B/sWEUuNKqEUL3nskoTuLAPrjhdsKkb5nPJWqHZZkCqqU2mNAKthH6yI8H7KsZn9DS2sJVqM09x
+RLWtwHkziOC/7aOgFLScCbAK42C++PhmiM1b8XcF4LVzbsF9Ri6OSyemzTUK/eVNfaoqoynHWmgE
+6OXWk6RiwsXm9E/G+Z8ajYJJGYrKWUM66A0ywfRMEwNvbqY/kXPLynNvEiCL7sCCeN5LLsJJwx3t
+FvYk9CcbXFcx3FXuqB5vbKziRcxXV4p1VxngtViZSTYxPDMBbRZKzbgqg4SGm/lg0h9tkQPTYKbV
+PZrdd5A9NaSfD171UkRpucC63M9933zZxKyGIjK8e2uR73r4F2iw4lNVYC2vPsKD2NkJK/DAZNuH
+i5HMkesE/Xa0lZrmFAYb1TQdvtj/dBxThZngWVJKYe2InmtJiUZ+IFrZ50rlau7SZRFDAgMBAAGj
+YzBhMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTvkUz1pcMw6C8I
+6tNxIqSSaHh02TAfBgNVHSMEGDAWgBTvkUz1pcMw6C8I6tNxIqSSaHh02TANBgkqhkiG9w0BAQsF
+AAOCAgEAfj1U2iJdGlg+O1QnurrMyOMaauo++RLrVl89UM7g6kgmJs95Vn6RHJk/0KGRHCwPT5iV
+WVO90CLYiF2cN/z7ZMF4jIuaYAnq1fohX9B0ZedQxb8uuQsLrbWwF6YSjNRieOpWauwK0kDDPAUw
+Pk2Ut59KA9N9J0u2/kTO+hkzGm2kQtHdzMjI1xZSg081lLMSVX3l4kLr5JyTCcBMWwerx20RoFAX
+lCOotQqSD7J6wWAsOMwaplv/8gzjqh8c3LigkyfeY+N/IZ865Z764BNqdeuWXGKRlI5nU7aJ+BIJ
+y29SWwNyhlCVCNSNh4YVH5Uk2KRvms6knZtt0rJ2BobGVgjF6wnaNsIbW0G+YSrjcOa4pvi2WsS9
+Iff/ql+hbHY5ZtbqTFXhADObE5hjyW/QASAJN1LnDE8+zbz1X5YnpyACleAu6AdBBR8Vbtaw5Bng
+DwKTACdyxYvRVB9dSsNAl35VpnzBMwQUAR1JIGkLGZOdblgi90AMRgwjY/M50n92Uaf0yKHxDHYi
+I0ZSKS3io0EHVmmY0gUJvGnHWmHNj4FgFU2A3ZDifcRQ8ow7bkrHxuaAKzyBvBGAFhAn1/DNP3nM
+cyrDflOR1m749fPH0FFNjkulW+YZFzvWgQncItzujrnEj1PhZ7szuIgVRs/taTX/dQ1G885x4cVr
+hkIGuUE=
+-----END CERTIFICATE-----
+
+OISTE WISeKey Global Root GB CA
+===============================
+-----BEGIN CERTIFICATE-----
+MIIDtTCCAp2gAwIBAgIQdrEgUnTwhYdGs/gjGvbCwDANBgkqhkiG9w0BAQsFADBtMQswCQYDVQQG
+EwJDSDEQMA4GA1UEChMHV0lTZUtleTEiMCAGA1UECxMZT0lTVEUgRm91bmRhdGlvbiBFbmRvcnNl
+ZDEoMCYGA1UEAxMfT0lTVEUgV0lTZUtleSBHbG9iYWwgUm9vdCBHQiBDQTAeFw0xNDEyMDExNTAw
+MzJaFw0zOTEyMDExNTEwMzFaMG0xCzAJBgNVBAYTAkNIMRAwDgYDVQQKEwdXSVNlS2V5MSIwIAYD
+VQQLExlPSVNURSBGb3VuZGF0aW9uIEVuZG9yc2VkMSgwJgYDVQQDEx9PSVNURSBXSVNlS2V5IEds
+b2JhbCBSb290IEdCIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2Be3HEokKtaX
+scriHvt9OO+Y9bI5mE4nuBFde9IllIiCFSZqGzG7qFshISvYD06fWvGxWuR51jIjK+FTzJlFXHtP
+rby/h0oLS5daqPZI7H17Dc0hBt+eFf1Biki3IPShehtX1F1Q/7pn2COZH8g/497/b1t3sWtuuMlk
+9+HKQUYOKXHQuSP8yYFfTvdv37+ErXNku7dCjmn21HYdfp2nuFeKUWdy19SouJVUQHMD9ur06/4o
+Qnc/nSMbsrY9gBQHTC5P99UKFg29ZkM3fiNDecNAhvVMKdqOmq0NpQSHiB6F4+lT1ZvIiwNjeOvg
+GUpuuy9rM2RYk61pv48b74JIxwIDAQABo1EwTzALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB
+/zAdBgNVHQ4EFgQUNQ/INmNe4qPs+TtmFc5RUuORmj0wEAYJKwYBBAGCNxUBBAMCAQAwDQYJKoZI
+hvcNAQELBQADggEBAEBM+4eymYGQfp3FsLAmzYh7KzKNbrghcViXfa43FK8+5/ea4n32cZiZBKpD
+dHij40lhPnOMTZTg+XHEthYOU3gf1qKHLwI5gSk8rxWYITD+KJAAjNHhy/peyP34EEY7onhCkRd0
+VQreUGdNZtGn//3ZwLWoo4rOZvUPQ82nK1d7Y0Zqqi5S2PTt4W2tKZB4SLrhI6qjiey1q5bAtEui
+HZeeevJuQHHfaPFlTc58Bd9TZaml8LGXBHAVRgOY1NK/VLSgWH1Sb9pWJmLU2NuJMW8c8CLC02Ic
+Nc1MaRVUGpCY3useX8p3x8uOPUNpnJpY0CQ73xtAln41rYHHTnG6iBM=
+-----END CERTIFICATE-----
+
+Certification Authority of WoSign G2
+====================================
+-----BEGIN CERTIFICATE-----
+MIIDfDCCAmSgAwIBAgIQayXaioidfLwPBbOxemFFRDANBgkqhkiG9w0BAQsFADBYMQswCQYDVQQG
+EwJDTjEaMBgGA1UEChMRV29TaWduIENBIExpbWl0ZWQxLTArBgNVBAMTJENlcnRpZmljYXRpb24g
+QXV0aG9yaXR5IG9mIFdvU2lnbiBHMjAeFw0xNDExMDgwMDU4NThaFw00NDExMDgwMDU4NThaMFgx
+CzAJBgNVBAYTAkNOMRowGAYDVQQKExFXb1NpZ24gQ0EgTGltaXRlZDEtMCsGA1UEAxMkQ2VydGlm
+aWNhdGlvbiBBdXRob3JpdHkgb2YgV29TaWduIEcyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
+CgKCAQEAvsXEoCKASU+/2YcRxlPhuw+9YH+v9oIOH9ywjj2X4FA8jzrvZjtFB5sg+OPXJYY1kBai
+XW8wGQiHC38Gsp1ij96vkqVg1CuAmlI/9ZqD6TRay9nVYlzmDuDfBpgOgHzKtB0TiGsOqCR3A9Du
+W/PKaZE1OVbFbeP3PU9ekzgkyhjpJMuSA93MHD0JcOQg5PGurLtzaaNjOg9FD6FKmsLRY6zLEPg9
+5k4ot+vElbGs/V6r+kHLXZ1L3PR8du9nfwB6jdKgGlxNIuG12t12s9R23164i5jIFFTMaxeSt+BK
+v0mUYQs4kI9dJGwlezt52eJ+na2fmKEG/HgUYFf47oB3sQIDAQABo0IwQDAOBgNVHQ8BAf8EBAMC
+AQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU+mCp62XF3RYUCE4MD42b4Pdkr2cwDQYJKoZI
+hvcNAQELBQADggEBAFfDejaCnI2Y4qtAqkePx6db7XznPWZaOzG73/MWM5H8fHulwqZm46qwtyeY
+P0nXYGdnPzZPSsvxFPpahygc7Y9BMsaV+X3avXtbwrAh449G3CE4Q3RM+zD4F3LBMvzIkRfEzFg3
+TgvMWvchNSiDbGAtROtSjFA9tWwS1/oJu2yySrHFieT801LYYRf+epSEj3m2M1m6D8QL4nCgS3gu
++sif/a+RZQp4OBXllxcU3fngLDT4ONCEIgDAFFEYKwLcMFrw6AF8NTojrwjkr6qOKEJJLvD1mTS+
+7Q9LGOHSJDy7XUe3IfKN0QqZjuNuPq1w4I+5ysxugTH2e5x6eeRncRg=
+-----END CERTIFICATE-----
+
+CA WoSign ECC Root
+==================
+-----BEGIN CERTIFICATE-----
+MIICCTCCAY+gAwIBAgIQaEpYcIBr8I8C+vbe6LCQkDAKBggqhkjOPQQDAzBGMQswCQYDVQQGEwJD
+TjEaMBgGA1UEChMRV29TaWduIENBIExpbWl0ZWQxGzAZBgNVBAMTEkNBIFdvU2lnbiBFQ0MgUm9v
+dDAeFw0xNDExMDgwMDU4NThaFw00NDExMDgwMDU4NThaMEYxCzAJBgNVBAYTAkNOMRowGAYDVQQK
+ExFXb1NpZ24gQ0EgTGltaXRlZDEbMBkGA1UEAxMSQ0EgV29TaWduIEVDQyBSb290MHYwEAYHKoZI
+zj0CAQYFK4EEACIDYgAE4f2OuEMkq5Z7hcK6C62N4DrjJLnSsb6IOsq/Srj57ywvr1FQPEd1bPiU
+t5v8KB7FVMxjnRZLU8HnIKvNrCXSf4/CwVqCXjCLelTOA7WRf6qU0NGKSMyCBSah1VES1ns2o0Iw
+QDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUqv3VWqP2h4syhf3R
+MluARZPzA7gwCgYIKoZIzj0EAwMDaAAwZQIxAOSkhLCB1T2wdKyUpOgOPQB0TKGXa/kNUTyh2Tv0
+Daupn75OcsqF1NnstTJFGG+rrQIwfcf3aWMvoeGY7xMQ0Xk/0f7qO3/eVvSQsRUR2LIiFdAvwyYu
+a/GRspBl9JrmkO5K
+-----END CERTIFICATE-----
+
+SZAFIR ROOT CA2
+===============
+-----BEGIN CERTIFICATE-----
+MIIDcjCCAlqgAwIBAgIUPopdB+xV0jLVt+O2XwHrLdzk1uQwDQYJKoZIhvcNAQELBQAwUTELMAkG
+A1UEBhMCUEwxKDAmBgNVBAoMH0tyYWpvd2EgSXpiYSBSb3psaWN6ZW5pb3dhIFMuQS4xGDAWBgNV
+BAMMD1NaQUZJUiBST09UIENBMjAeFw0xNTEwMTkwNzQzMzBaFw0zNTEwMTkwNzQzMzBaMFExCzAJ
+BgNVBAYTAlBMMSgwJgYDVQQKDB9LcmFqb3dhIEl6YmEgUm96bGljemVuaW93YSBTLkEuMRgwFgYD
+VQQDDA9TWkFGSVIgUk9PVCBDQTIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC3vD5Q
+qEvNQLXOYeeWyrSh2gwisPq1e3YAd4wLz32ohswmUeQgPYUM1ljj5/QqGJ3a0a4m7utT3PSQ1hNK
+DJA8w/Ta0o4NkjrcsbH/ON7Dui1fgLkCvUqdGw+0w8LBZwPd3BucPbOw3gAeqDRHu5rr/gsUvTaE
+2g0gv/pby6kWIK05YO4vdbbnl5z5Pv1+TW9NL++IDWr63fE9biCloBK0TXC5ztdyO4mTp4CEHCdJ
+ckm1/zuVnsHMyAHs6A6KCpbns6aH5db5BSsNl0BwPLqsdVqc1U2dAgrSS5tmS0YHF2Wtn2yIANwi
+ieDhZNRnvDF5YTy7ykHNXGoAyDw4jlivAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0P
+AQH/BAQDAgEGMB0GA1UdDgQWBBQuFqlKGLXLzPVvUPMjX/hd56zwyDANBgkqhkiG9w0BAQsFAAOC
+AQEAtXP4A9xZWx126aMqe5Aosk3AM0+qmrHUuOQn/6mWmc5G4G18TKI4pAZw8PRBEew/R40/cof5
+O/2kbytTAOD/OblqBw7rHRz2onKQy4I9EYKL0rufKq8h5mOGnXkZ7/e7DDWQw4rtTw/1zBLZpD67
+oPwglV9PJi8RI4NOdQcPv5vRtB3pEAT+ymCPoky4rc/hkA/NrgrHXXu3UNLUYfrVFdvXn4dRVOul
+4+vJhaAlIDf7js4MNIThPIGyd05DpYhfhmehPea0XGG2Ptv+tyjFogeutcrKjSoS75ftwjCkySp6
++/NNIxuZMzSgLvWpCz/UXeHPhJ/iGcJfitYgHuNztw==
+-----END CERTIFICATE-----
+
+Certum Trusted Network CA 2
+===========================
+-----BEGIN CERTIFICATE-----
+MIIF0jCCA7qgAwIBAgIQIdbQSk8lD8kyN/yqXhKN6TANBgkqhkiG9w0BAQ0FADCBgDELMAkGA1UE
+BhMCUEwxIjAgBgNVBAoTGVVuaXpldG8gVGVjaG5vbG9naWVzIFMuQS4xJzAlBgNVBAsTHkNlcnR1
+bSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEkMCIGA1UEAxMbQ2VydHVtIFRydXN0ZWQgTmV0d29y
+ayBDQSAyMCIYDzIwMTExMDA2MDgzOTU2WhgPMjA0NjEwMDYwODM5NTZaMIGAMQswCQYDVQQGEwJQ
+TDEiMCAGA1UEChMZVW5pemV0byBUZWNobm9sb2dpZXMgUy5BLjEnMCUGA1UECxMeQ2VydHVtIENl
+cnRpZmljYXRpb24gQXV0aG9yaXR5MSQwIgYDVQQDExtDZXJ0dW0gVHJ1c3RlZCBOZXR3b3JrIENB
+IDIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC9+Xj45tWADGSdhhuWZGc/IjoedQF9
+7/tcZ4zJzFxrqZHmuULlIEub2pt7uZld2ZuAS9eEQCsn0+i6MLs+CRqnSZXvK0AkwpfHp+6bJe+o
+CgCXhVqqndwpyeI1B+twTUrWwbNWuKFBOJvR+zF/j+Bf4bE/D44WSWDXBo0Y+aomEKsq09DRZ40b
+Rr5HMNUuctHFY9rnY3lEfktjJImGLjQ/KUxSiyqnwOKRKIm5wFv5HdnnJ63/mgKXwcZQkpsCLL2p
+uTRZCr+ESv/f/rOf69me4Jgj7KZrdxYq28ytOxykh9xGc14ZYmhFV+SQgkK7QtbwYeDBoz1mo130
+GO6IyY0XRSmZMnUCMe4pJshrAua1YkV/NxVaI2iJ1D7eTiew8EAMvE0Xy02isx7QBlrd9pPPV3WZ
+9fqGGmd4s7+W/jTcvedSVuWz5XV710GRBdxdaeOVDUO5/IOWOZV7bIBaTxNyxtd9KXpEulKkKtVB
+Rgkg/iKgtlswjbyJDNXXcPiHUv3a76xRLgezTv7QCdpw75j6VuZt27VXS9zlLCUVyJ4ueE742pye
+hizKV/Ma5ciSixqClnrDvFASadgOWkaLOusm+iPJtrCBvkIApPjW/jAux9JG9uWOdf3yzLnQh1vM
+BhBgu4M1t15n3kfsmUjxpKEV/q2MYo45VU85FrmxY53/twIDAQABo0IwQDAPBgNVHRMBAf8EBTAD
+AQH/MB0GA1UdDgQWBBS2oVQ5AsOgP46KvPrU+Bym0ToO/TAOBgNVHQ8BAf8EBAMCAQYwDQYJKoZI
+hvcNAQENBQADggIBAHGlDs7k6b8/ONWJWsQCYftMxRQXLYtPU2sQF/xlhMcQSZDe28cmk4gmb3DW
+Al45oPePq5a1pRNcgRRtDoGCERuKTsZPpd1iHkTfCVn0W3cLN+mLIMb4Ck4uWBzrM9DPhmDJ2vuA
+L55MYIR4PSFk1vtBHxgP58l1cb29XN40hz5BsA72udY/CROWFC/emh1auVbONTqwX3BNXuMp8SMo
+clm2q8KMZiYcdywmdjWLKKdpoPk79SPdhRB0yZADVpHnr7pH1BKXESLjokmUbOe3lEu6LaTaM4tM
+pkT/WjzGHWTYtTHkpjx6qFcL2+1hGsvxznN3Y6SHb0xRONbkX8eftoEq5IVIeVheO/jbAoJnwTnb
+w3RLPTYe+SmTiGhbqEQZIfCn6IENLOiTNrQ3ssqwGyZ6miUfmpqAnksqP/ujmv5zMnHCnsZy4Ypo
+J/HkD7TETKVhk/iXEAcqMCWpuchxuO9ozC1+9eB+D4Kob7a6bINDd82Kkhehnlt4Fj1F4jNy3eFm
+ypnTycUm/Q1oBEauttmbjL4ZvrHG8hnjXALKLNhvSgfZyTXaQHXyxKcZb55CEJh15pWLYLztxRLX
+is7VmFxWlgPF7ncGNf/P5O4/E2Hu29othfDNrp2yGAlFw5Khchf8R7agCyzxxN5DaAhqXzvwdmP7
+zAYspsbiDrW5viSP
+-----END CERTIFICATE-----
diff --git a/cmake/openssl_interop_commands.cmake b/cmake/openssl_interop_commands.cmake
deleted file mode 100644
index 7ff9438eb..000000000
--- a/cmake/openssl_interop_commands.cmake
+++ /dev/null
@@ -1,168 +0,0 @@
-include("${CMAKE_CURRENT_LIST_DIR}/tls_command_test.cmake")
-
-if(NOT DEFINED OPENSSL_EXECUTABLE)
-	find_program(OPENSSL_EXECUTABLE openssl)
-endif()
-if(NOT OPENSSL_EXECUTABLE)
-	message(FATAL_ERROR "openssl executable not found")
-endif()
-
-function(gmssl_openssl_s_client_supports option result)
-	execute_process(
-		COMMAND ${OPENSSL_EXECUTABLE} s_client -help
-		OUTPUT_VARIABLE OPENSSL_S_CLIENT_HELP_OUT
-		ERROR_VARIABLE OPENSSL_S_CLIENT_HELP_ERR
-	)
-	set(OPENSSL_S_CLIENT_HELP "${OPENSSL_S_CLIENT_HELP_OUT}\n${OPENSSL_S_CLIENT_HELP_ERR}")
-	string(FIND "${OPENSSL_S_CLIENT_HELP}" "${option}" OPTION_INDEX)
-	if(${OPTION_INDEX} EQUAL -1)
-		set(${result} OFF PARENT_SCOPE)
-	else()
-		set(${result} ON PARENT_SCOPE)
-	endif()
-endfunction()
-
-gmssl_require_file(p256_root_ca_cert.pem)
-gmssl_require_file(p256_tls_server_ca2_cert.pem)
-gmssl_require_file(p256_tls_server_cert.pem)
-gmssl_require_file(p256_tls_server_cert_chain.pem)
-gmssl_require_file(p256_tls_server_certs.pem)
-gmssl_require_file(p256_tls_server_key.pem)
-gmssl_require_file(p256_tls_server_key.exp)
-
-if(NOT DEFINED TEST_CASE)
-	set(TEST_CASE tls12_openssl_server)
-endif()
-
-set(TLS13_PSK 1122334455667788112233445566778811223344556677881122334455667788)
-
-if(TEST_CASE STREQUAL tls12_openssl_server)
-	set(TEST_NAME tls12_openssl_server)
-	set(TEST_PORT 4450)
-	set(SERVER_COMMAND "${OPENSSL_EXECUTABLE} s_server -accept ${TEST_PORT} -cert p256_tls_server_cert.pem -cert_chain p256_tls_server_cert_chain.pem -key p256_tls_server_key.exp -tls1_2 -cipher ECDHE-ECDSA-AES128-SHA256 -named_curve prime256v1 -www -naccept 1 -quiet")
-	set(CLIENT_COMMAND "bin/gmssl tls12_client -host 127.0.0.1 -port ${TEST_PORT} -server_name localhost -cacert p256_root_ca_cert.pem -cipher_suite TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 -supported_group prime256v1 -sig_alg ecdsa_secp256r1_sha256 -get /")
-	gmssl_run_command_interop_test(
-		TEST_NAME ${TEST_NAME}
-		PORT ${TEST_PORT}
-		SERVER_COMMAND "${SERVER_COMMAND}"
-		CLIENT_COMMAND "${CLIENT_COMMAND}"
-		EXPECT_CLIENT_LOG "Connection established")
-elseif(TEST_CASE STREQUAL tls12_openssl_server_renegotiation_info)
-	set(TEST_NAME tls12_openssl_server_renegotiation_info)
-	set(TEST_PORT 4459)
-	set(SERVER_COMMAND "${OPENSSL_EXECUTABLE} s_server -accept ${TEST_PORT} -cert p256_tls_server_cert.pem -cert_chain p256_tls_server_cert_chain.pem -key p256_tls_server_key.exp -tls1_2 -cipher ECDHE-ECDSA-AES128-SHA256 -named_curve prime256v1 -www -naccept 1 -quiet")
-	set(CLIENT_COMMAND "bin/gmssl tls12_client -host 127.0.0.1 -port ${TEST_PORT} -server_name localhost -cacert p256_root_ca_cert.pem -cipher_suite TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 -supported_group prime256v1 -sig_alg ecdsa_secp256r1_sha256 -renegotiation_info -get /")
-	gmssl_run_command_interop_test(
-		TEST_NAME ${TEST_NAME}
-		PORT ${TEST_PORT}
-		SERVER_COMMAND "${SERVER_COMMAND}"
-		CLIENT_COMMAND "${CLIENT_COMMAND}"
-		EXPECT_CLIENT_LOG "Connection established")
-elseif(TEST_CASE STREQUAL tls12_openssl_server_renegotiation_info_scsv)
-	set(TEST_NAME tls12_openssl_server_renegotiation_info_scsv)
-	set(TEST_PORT 4460)
-	set(SERVER_COMMAND "${OPENSSL_EXECUTABLE} s_server -accept ${TEST_PORT} -cert p256_tls_server_cert.pem -cert_chain p256_tls_server_cert_chain.pem -key p256_tls_server_key.exp -tls1_2 -cipher ECDHE-ECDSA-AES128-SHA256 -named_curve prime256v1 -www -naccept 1 -quiet")
-	set(CLIENT_COMMAND "bin/gmssl tls12_client -host 127.0.0.1 -port ${TEST_PORT} -server_name localhost -cacert p256_root_ca_cert.pem -cipher_suite TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 -supported_group prime256v1 -sig_alg ecdsa_secp256r1_sha256 -renegotiation_info_scsv -get /")
-	gmssl_run_command_interop_test(
-		TEST_NAME ${TEST_NAME}
-		PORT ${TEST_PORT}
-		SERVER_COMMAND "${SERVER_COMMAND}"
-		CLIENT_COMMAND "${CLIENT_COMMAND}"
-		EXPECT_CLIENT_LOG "Connection established")
-elseif(TEST_CASE STREQUAL tls12_openssl_client)
-	set(TEST_NAME tls12_openssl_client)
-	set(TEST_PORT 4451)
-	set(SERVER_COMMAND "bin/gmssl tls12_server -port ${TEST_PORT} -cert p256_tls_server_certs.pem -key p256_tls_server_key.pem -pass P@ssw0rd -cipher_suite TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 -supported_group prime256v1 -sig_alg ecdsa_secp256r1_sha256 -renegotiation_info")
-	set(CLIENT_COMMAND "printf 'GET / HTTP/1.0\\r\\n\\r\\n' | ${OPENSSL_EXECUTABLE} s_client -connect 127.0.0.1:${TEST_PORT} -tls1_2 -CAfile p256_root_ca_cert.pem -cipher ECDHE-ECDSA-AES128-SHA256 -groups prime256v1 -servername localhost -brief")
-	gmssl_run_command_interop_test(
-		TEST_NAME ${TEST_NAME}
-		PORT ${TEST_PORT}
-		SERVER_COMMAND "${SERVER_COMMAND}"
-		CLIENT_COMMAND "${CLIENT_COMMAND}"
-		EXPECT_CLIENT_LOG "Verification: OK")
-elseif(TEST_CASE STREQUAL tls13_openssl_server)
-	set(TEST_NAME tls13_openssl_server)
-	set(TEST_PORT 4452)
-	set(SERVER_COMMAND "${OPENSSL_EXECUTABLE} s_server -accept ${TEST_PORT} -cert p256_tls_server_cert.pem -cert_chain p256_tls_server_cert_chain.pem -key p256_tls_server_key.exp -tls1_3 -ciphersuites TLS_AES_128_GCM_SHA256 -groups prime256v1 -no_middlebox -www -naccept 1 -quiet")
-	set(CLIENT_COMMAND "bin/gmssl tls13_client -host 127.0.0.1 -port ${TEST_PORT} -server_name localhost -cacert p256_root_ca_cert.pem -cipher_suite TLS_AES_128_GCM_SHA256 -supported_group prime256v1 -sig_alg ecdsa_secp256r1_sha256 -get /")
-	gmssl_run_command_interop_test(
-		TEST_NAME ${TEST_NAME}
-		PORT ${TEST_PORT}
-		SERVER_COMMAND "${SERVER_COMMAND}"
-		CLIENT_COMMAND "${CLIENT_COMMAND}"
-		EXPECT_CLIENT_LOG "Connection established")
-elseif(TEST_CASE STREQUAL tls13_openssl_client)
-	set(TEST_NAME tls13_openssl_client)
-	set(TEST_PORT 4453)
-	set(SERVER_COMMAND "bin/gmssl tls13_server -port ${TEST_PORT} -cert p256_tls_server_certs.pem -key p256_tls_server_key.pem -pass P@ssw0rd -cipher_suite TLS_AES_128_GCM_SHA256 -supported_group prime256v1 -sig_alg ecdsa_secp256r1_sha256")
-	set(CLIENT_COMMAND "printf 'GET / HTTP/1.0\\r\\n\\r\\n' | ${OPENSSL_EXECUTABLE} s_client -connect 127.0.0.1:${TEST_PORT} -tls1_3 -CAfile p256_root_ca_cert.pem -ciphersuites TLS_AES_128_GCM_SHA256 -groups prime256v1 -sigalgs ecdsa_secp256r1_sha256 -servername localhost -no_middlebox -brief")
-	gmssl_run_command_interop_test(
-		TEST_NAME ${TEST_NAME}
-		PORT ${TEST_PORT}
-		SERVER_COMMAND "${SERVER_COMMAND}"
-		CLIENT_COMMAND "${CLIENT_COMMAND}"
-		EXPECT_CLIENT_LOG "Verification: OK")
-elseif(TEST_CASE STREQUAL tls13_hrr_openssl_client)
-	set(TEST_NAME tls13_hrr_openssl_client)
-	set(TEST_PORT 4454)
-	set(SERVER_COMMAND "bin/gmssl tls13_server -port ${TEST_PORT} -cert p256_tls_server_certs.pem -key p256_tls_server_key.pem -pass P@ssw0rd -cipher_suite TLS_AES_128_GCM_SHA256 -supported_group prime256v1 -sig_alg ecdsa_secp256r1_sha256 -verbose")
-	set(CLIENT_COMMAND "printf 'GET / HTTP/1.0\\r\\n\\r\\n' | ${OPENSSL_EXECUTABLE} s_client -connect 127.0.0.1:${TEST_PORT} -tls1_3 -CAfile p256_root_ca_cert.pem -ciphersuites TLS_AES_128_GCM_SHA256 -groups secp384r1:prime256v1 -sigalgs ecdsa_secp256r1_sha256 -servername localhost -no_middlebox -brief -msg")
-	gmssl_run_command_interop_test(
-		TEST_NAME ${TEST_NAME}
-		PORT ${TEST_PORT}
-		SERVER_COMMAND "${SERVER_COMMAND}"
-		CLIENT_COMMAND "${CLIENT_COMMAND}"
-		EXPECT_CLIENT_LOG "Verification: OK"
-		EXPECT_SERVER_LOG "selected_group: secp256r1")
-elseif(TEST_CASE STREQUAL tls13_psk_dhe_openssl_server)
-	set(TEST_NAME tls13_psk_dhe_openssl_server)
-	set(TEST_PORT 4455)
-	set(SERVER_COMMAND "${OPENSSL_EXECUTABLE} s_server -accept ${TEST_PORT} -tls1_3 -no_middlebox -nocert -psk_identity 001 -psk ${TLS13_PSK} -ciphersuites TLS_AES_128_GCM_SHA256 -groups prime256v1 -www -naccept 1 -quiet")
-	set(CLIENT_COMMAND "bin/gmssl tls13_client -host 127.0.0.1 -port ${TEST_PORT} -cipher_suite TLS_AES_128_GCM_SHA256 -supported_group prime256v1 -psk_dhe_ke -psk_identity 001 -psk_cipher_suite TLS_AES_128_GCM_SHA256 -psk_key ${TLS13_PSK} -get /")
-	gmssl_run_command_interop_test(
-		TEST_NAME ${TEST_NAME}
-		PORT ${TEST_PORT}
-		SERVER_COMMAND "${SERVER_COMMAND}"
-		CLIENT_COMMAND "${CLIENT_COMMAND}"
-		EXPECT_CLIENT_LOG "HTTP/1.0 200 ok")
-elseif(TEST_CASE STREQUAL tls13_psk_dhe_openssl_client)
-	set(TEST_NAME tls13_psk_dhe_openssl_client)
-	set(TEST_PORT 4456)
-	set(SERVER_COMMAND "bin/gmssl tls13_server -port ${TEST_PORT} -cert p256_tls_server_certs.pem -key p256_tls_server_key.pem -pass P@ssw0rd -cipher_suite TLS_AES_128_GCM_SHA256 -supported_group prime256v1 -psk_dhe_ke -psk_identity 001 -psk_cipher_suite TLS_AES_128_GCM_SHA256 -psk_key ${TLS13_PSK}")
-	set(CLIENT_COMMAND "printf 'GET / HTTP/1.0\\r\\n\\r\\n' | ${OPENSSL_EXECUTABLE} s_client -connect 127.0.0.1:${TEST_PORT} -tls1_3 -psk_identity 001 -psk ${TLS13_PSK} -ciphersuites TLS_AES_128_GCM_SHA256 -groups prime256v1 -no_middlebox -brief")
-	gmssl_run_command_interop_test(
-		TEST_NAME ${TEST_NAME}
-		PORT ${TEST_PORT}
-		SERVER_COMMAND "${SERVER_COMMAND}"
-		CLIENT_COMMAND "${CLIENT_COMMAND}"
-		EXPECT_CLIENT_LOG "CONNECTION ESTABLISHED")
-elseif(TEST_CASE STREQUAL tls13_psk_only_openssl_server)
-	set(TEST_NAME tls13_psk_only_openssl_server)
-	set(TEST_PORT 4457)
-	set(SERVER_COMMAND "${OPENSSL_EXECUTABLE} s_server -accept ${TEST_PORT} -tls1_3 -no_middlebox -allow_no_dhe_kex -nocert -psk_identity 001 -psk ${TLS13_PSK} -ciphersuites TLS_AES_128_GCM_SHA256 -www -naccept 1 -quiet")
-	set(CLIENT_COMMAND "bin/gmssl tls13_client -host 127.0.0.1 -port ${TEST_PORT} -cipher_suite TLS_AES_128_GCM_SHA256 -psk_ke -psk_identity 001 -psk_cipher_suite TLS_AES_128_GCM_SHA256 -psk_key ${TLS13_PSK} -get /")
-	gmssl_run_command_interop_test(
-		TEST_NAME ${TEST_NAME}
-		PORT ${TEST_PORT}
-		SERVER_COMMAND "${SERVER_COMMAND}"
-		CLIENT_COMMAND "${CLIENT_COMMAND}"
-		EXPECT_CLIENT_LOG "HTTP/1.0 200 ok")
-elseif(TEST_CASE STREQUAL tls13_psk_only_openssl_client)
-	set(TEST_NAME tls13_psk_only_openssl_client)
-	gmssl_openssl_s_client_supports("-prefer_no_dhe_kex" OPENSSL_S_CLIENT_HAS_PREFER_NO_DHE_KEX)
-	if(NOT OPENSSL_S_CLIENT_HAS_PREFER_NO_DHE_KEX)
-		message(STATUS "skipping ${TEST_NAME}: openssl s_client does not support -prefer_no_dhe_kex")
-		return()
-	endif()
-	set(TEST_PORT 4458)
-	set(SERVER_COMMAND "bin/gmssl tls13_server -port ${TEST_PORT} -cert p256_tls_server_certs.pem -key p256_tls_server_key.pem -pass P@ssw0rd -cipher_suite TLS_AES_128_GCM_SHA256 -psk_ke -psk_identity 001 -psk_cipher_suite TLS_AES_128_GCM_SHA256 -psk_key ${TLS13_PSK}")
-	set(CLIENT_COMMAND "printf 'GET / HTTP/1.0\\r\\n\\r\\n' | ${OPENSSL_EXECUTABLE} s_client -connect 127.0.0.1:${TEST_PORT} -tls1_3 -psk_identity 001 -psk ${TLS13_PSK} -ciphersuites TLS_AES_128_GCM_SHA256 -allow_no_dhe_kex -prefer_no_dhe_kex -no_middlebox -brief")
-	gmssl_run_command_interop_test(
-		TEST_NAME ${TEST_NAME}
-		PORT ${TEST_PORT}
-		SERVER_COMMAND "${SERVER_COMMAND}"
-		CLIENT_COMMAND "${CLIENT_COMMAND}"
-		EXPECT_CLIENT_LOG "CONNECTION ESTABLISHED")
-else()
-	message(FATAL_ERROR "unknown OpenSSL interop test case: ${TEST_CASE}")
-endif()
diff --git a/cmake/tlcp_commands.cmake b/cmake/tlcp_commands.cmake
deleted file mode 100644
index ff90fd3d1..000000000
--- a/cmake/tlcp_commands.cmake
+++ /dev/null
@@ -1,85 +0,0 @@
-include("${CMAKE_CURRENT_LIST_DIR}/tls_command_test.cmake")
-
-gmssl_require_file(sm2_root_ca_cert.pem)
-gmssl_require_file(sm2_tlcp_server_certs.pem)
-gmssl_require_file(sm2_tlcp_server_keys.pem)
-gmssl_require_file(sm2_tls_client_certs.pem)
-gmssl_require_file(sm2_tls_client_key.pem)
-gmssl_require_file(sm2_tlcp_client_certs.pem)
-gmssl_require_file(sm2_tlcp_client_keys.pem)
-
-if(NOT DEFINED TEST_CASE)
-	set(TEST_CASE tlcp_sm4_gcm_sni)
-endif()
-
-if(TEST_CASE STREQUAL tlcp_sm4_gcm_sni)
-	set(TEST_NAME tlcp_sm4_gcm_sni)
-	set(TEST_PORT 4435)
-	set(TEST_CIPHER_SUITE TLS_ECC_SM4_GCM_SM3)
-	set(TEST_CLIENT_CERT OFF)
-elseif(TEST_CASE STREQUAL tlcp_sm4_cbc_sni)
-	set(TEST_NAME tlcp_sm4_cbc_sni)
-	set(TEST_PORT 4431)
-	set(TEST_CIPHER_SUITE TLS_ECC_SM4_CBC_SM3)
-	set(TEST_CLIENT_CERT OFF)
-elseif(TEST_CASE STREQUAL tlcp_sm4_gcm_client_cert)
-	set(TEST_NAME tlcp_sm4_gcm_client_cert)
-	set(TEST_PORT 4436)
-	set(TEST_CIPHER_SUITE TLS_ECC_SM4_GCM_SM3)
-	set(TEST_CLIENT_CERT ON)
-elseif(TEST_CASE STREQUAL tlcp_ecdhe_sm4_cbc_client_cert)
-	set(TEST_NAME tlcp_ecdhe_sm4_cbc_client_cert)
-	set(TEST_PORT 4437)
-	set(TEST_CIPHER_SUITE TLS_ECDHE_SM4_CBC_SM3)
-	set(TEST_CLIENT_CERT ON)
-	set(TEST_CLIENT_DOUBLE_CERT ON)
-elseif(TEST_CASE STREQUAL tlcp_ecdhe_sm4_gcm_client_cert)
-	set(TEST_NAME tlcp_ecdhe_sm4_gcm_client_cert)
-	set(TEST_PORT 4438)
-	set(TEST_CIPHER_SUITE TLS_ECDHE_SM4_GCM_SM3)
-	set(TEST_CLIENT_CERT ON)
-	set(TEST_CLIENT_DOUBLE_CERT ON)
-else()
-	message(FATAL_ERROR "unknown TLCP test case: ${TEST_CASE}")
-endif()
-
-set(TEST_SERVER_ARGS
-	tlcp_server
-	-port ${TEST_PORT}
-	-cipher_suite ${TEST_CIPHER_SUITE}
-	-cert sm2_tlcp_server_certs.pem
-	-key sm2_tlcp_server_keys.pem
-	-pass P@ssw0rd)
-
-set(TEST_CLIENT_ARGS
-	tlcp_client
-	-host 127.0.0.1
-	-port ${TEST_PORT}
-	-server_name localhost
-	-cacert sm2_root_ca_cert.pem
-	-cipher_suite ${TEST_CIPHER_SUITE}
-	-in ${TEST_NAME}_message.txt)
-
-if(TEST_CLIENT_CERT)
-	list(APPEND TEST_SERVER_ARGS
-		-cacert sm2_root_ca_cert.pem
-		-cert_request)
-	if(TEST_CLIENT_DOUBLE_CERT)
-		list(APPEND TEST_CLIENT_ARGS
-			-cert sm2_tlcp_client_certs.pem
-			-key sm2_tlcp_client_keys.pem
-			-pass P@ssw0rd)
-	else()
-		list(APPEND TEST_CLIENT_ARGS
-			-cert sm2_tls_client_certs.pem
-			-key sm2_tls_client_key.pem
-			-pass P@ssw0rd)
-	endif()
-endif()
-
-gmssl_run_tls_command_test(
-	TEST_NAME ${TEST_NAME}
-	PORT ${TEST_PORT}
-	SERVER_ARGS ${TEST_SERVER_ARGS}
-	CLIENT_ARGS ${TEST_CLIENT_ARGS}
-)
diff --git a/cmake/tls12_commands.cmake b/cmake/tls12_commands.cmake
deleted file mode 100644
index 2c6057e02..000000000
--- a/cmake/tls12_commands.cmake
+++ /dev/null
@@ -1,85 +0,0 @@
-include("${CMAKE_CURRENT_LIST_DIR}/tls_command_test.cmake")
-
-gmssl_require_file(sm2_root_ca_cert.pem)
-gmssl_require_file(sm2_tls_server_certs.pem)
-gmssl_require_file(sm2_tls_server_key.pem)
-gmssl_require_file(sm2_tls_client_certs.pem)
-gmssl_require_file(sm2_tls_client_key.pem)
-
-if(NOT DEFINED TEST_CASE)
-	set(TEST_CASE tls12_sm4_gcm_sni)
-endif()
-
-if(TEST_CASE STREQUAL tls12_sm4_gcm_sni)
-	set(TEST_NAME tls12_sm4_gcm_sni)
-	set(TEST_PORT 4434)
-	set(TEST_CIPHER_SUITE TLS_ECDHE_SM4_GCM_SM3)
-	set(TEST_CLIENT_CERT OFF)
-elseif(TEST_CASE STREQUAL tls12_sm4_gcm_renegotiation_info)
-	set(TEST_NAME tls12_sm4_gcm_renegotiation_info)
-	set(TEST_PORT 4461)
-	set(TEST_CIPHER_SUITE TLS_ECDHE_SM4_GCM_SM3)
-	set(TEST_CLIENT_CERT OFF)
-	set(TEST_RENEGOTIATION_ARG -renegotiation_info)
-elseif(TEST_CASE STREQUAL tls12_sm4_gcm_renegotiation_info_scsv)
-	set(TEST_NAME tls12_sm4_gcm_renegotiation_info_scsv)
-	set(TEST_PORT 4462)
-	set(TEST_CIPHER_SUITE TLS_ECDHE_SM4_GCM_SM3)
-	set(TEST_CLIENT_CERT OFF)
-	set(TEST_RENEGOTIATION_ARG -renegotiation_info_scsv)
-elseif(TEST_CASE STREQUAL tls12_sm4_cbc_sni)
-	set(TEST_NAME tls12_sm4_cbc_sni)
-	set(TEST_PORT 4432)
-	set(TEST_CIPHER_SUITE TLS_ECDHE_SM4_CBC_SM3)
-	set(TEST_CLIENT_CERT OFF)
-elseif(TEST_CASE STREQUAL tls12_sm4_gcm_client_cert)
-	set(TEST_NAME tls12_sm4_gcm_client_cert)
-	set(TEST_PORT 4438)
-	set(TEST_CIPHER_SUITE TLS_ECDHE_SM4_GCM_SM3)
-	set(TEST_CLIENT_CERT ON)
-else()
-	message(FATAL_ERROR "unknown TLS 1.2 test case: ${TEST_CASE}")
-endif()
-
-set(TEST_SERVER_ARGS
-	tls12_server
-	-port ${TEST_PORT}
-	-cipher_suite ${TEST_CIPHER_SUITE}
-	-supported_group sm2p256v1
-	-sig_alg sm2sig_sm3
-	-cert sm2_tls_server_certs.pem
-	-key sm2_tls_server_key.pem
-	-pass P@ssw0rd)
-
-set(TEST_CLIENT_ARGS
-	tls12_client
-	-host 127.0.0.1
-	-port ${TEST_PORT}
-	-server_name localhost
-	-cacert sm2_root_ca_cert.pem
-	-cipher_suite ${TEST_CIPHER_SUITE}
-	-supported_group sm2p256v1
-	-sig_alg sm2sig_sm3
-	-in ${TEST_NAME}_message.txt)
-
-if(TEST_RENEGOTIATION_ARG)
-	list(APPEND TEST_SERVER_ARGS -renegotiation_info)
-	list(APPEND TEST_CLIENT_ARGS ${TEST_RENEGOTIATION_ARG})
-endif()
-
-if(TEST_CLIENT_CERT)
-	list(APPEND TEST_SERVER_ARGS
-		-cacert sm2_root_ca_cert.pem
-		-cert_request)
-	list(APPEND TEST_CLIENT_ARGS
-		-cert sm2_tls_client_certs.pem
-		-key sm2_tls_client_key.pem
-		-pass P@ssw0rd)
-endif()
-
-gmssl_run_tls_command_test(
-	TEST_NAME ${TEST_NAME}
-	PORT ${TEST_PORT}
-	SERVER_ARGS ${TEST_SERVER_ARGS}
-	CLIENT_ARGS ${TEST_CLIENT_ARGS}
-)
diff --git a/cmake/tls13_commands.cmake b/cmake/tls13_commands.cmake
deleted file mode 100644
index 3e6c9d248..000000000
--- a/cmake/tls13_commands.cmake
+++ /dev/null
@@ -1,177 +0,0 @@
-include("${CMAKE_CURRENT_LIST_DIR}/tls_command_test.cmake")
-
-gmssl_require_file(sm2_root_ca_cert.pem)
-gmssl_require_file(sm2_tls_server_certs.pem)
-gmssl_require_file(sm2_tls_server_key.pem)
-gmssl_require_file(sm2_tls_client_certs.pem)
-gmssl_require_file(sm2_tls_client_key.pem)
-
-set(TLS13_PSK 1122334455667788112233445566778811223344556677881122334455667788)
-
-if(NOT DEFINED TEST_CASE)
-	set(TEST_CASE tls13_sm4_gcm_sni)
-endif()
-
-if(TEST_CASE STREQUAL tls13_sm4_gcm_sni)
-	set(TEST_NAME tls13_sm4_gcm_sni)
-	set(TEST_PORT 4433)
-	set(TEST_CLIENT_CERT OFF)
-elseif(TEST_CASE STREQUAL tls13_sm4_gcm_client_cert)
-	set(TEST_NAME tls13_sm4_gcm_client_cert)
-	set(TEST_PORT 4439)
-	set(TEST_CLIENT_CERT ON)
-endif()
-
-if(DEFINED TEST_NAME)
-	set(TEST_SERVER_ARGS
-		tls13_server
-		-port ${TEST_PORT}
-		-cipher_suite TLS_SM4_GCM_SM3
-		-supported_group sm2p256v1
-		-sig_alg sm2sig_sm3
-		-cert sm2_tls_server_certs.pem
-		-key sm2_tls_server_key.pem
-		-pass P@ssw0rd)
-
-	set(TEST_CLIENT_ARGS
-		tls13_client
-		-host 127.0.0.1
-		-port ${TEST_PORT}
-		-server_name localhost
-		-cacert sm2_root_ca_cert.pem
-		-cipher_suite TLS_SM4_GCM_SM3
-		-supported_group sm2p256v1
-		-sig_alg sm2sig_sm3
-		-in ${TEST_NAME}_message.txt)
-
-	if(TEST_CLIENT_CERT)
-		list(APPEND TEST_SERVER_ARGS
-			-cacert sm2_root_ca_cert.pem
-			-cert_request)
-		list(APPEND TEST_CLIENT_ARGS
-			-cert sm2_tls_client_certs.pem
-			-key sm2_tls_client_key.pem
-			-pass P@ssw0rd)
-	endif()
-
-	gmssl_run_tls_command_test(
-		TEST_NAME ${TEST_NAME}
-		PORT ${TEST_PORT}
-		SERVER_ARGS ${TEST_SERVER_ARGS}
-		CLIENT_ARGS ${TEST_CLIENT_ARGS}
-	)
-elseif(TEST_CASE STREQUAL tls13_hrr_sm4_gcm)
-	gmssl_run_tls_command_test(
-		TEST_NAME tls13_hrr_sm4_gcm
-		PORT 4460
-		EXPECT_CLIENT_LOG "selected_group: sm2p256v1"
-		SERVER_ARGS
-			tls13_server
-			-port 4460
-			-cert sm2_tls_server_certs.pem
-			-key sm2_tls_server_key.pem
-			-pass P@ssw0rd
-			-cipher_suite TLS_SM4_GCM_SM3
-			-supported_group sm2p256v1
-			-sig_alg sm2sig_sm3
-			-verbose
-		CLIENT_ARGS
-			tls13_client
-			-host 127.0.0.1
-			-port 4460
-			-server_name localhost
-			-cacert sm2_root_ca_cert.pem
-			-cipher_suite TLS_SM4_GCM_SM3
-			-supported_group prime256v1
-			-supported_group sm2p256v1
-			-sig_alg sm2sig_sm3
-			-max_key_exchanges 1
-			-in tls13_hrr_sm4_gcm_message.txt
-			-verbose
-	)
-elseif(TEST_CASE STREQUAL tls13_psk_dhe_sm4_gcm)
-	gmssl_run_tls_command_test(
-		TEST_NAME tls13_psk_dhe_sm4_gcm
-		PORT 4437
-		SERVER_ARGS
-			tls13_server
-			-port 4437
-			-cert sm2_tls_server_certs.pem
-			-key sm2_tls_server_key.pem
-			-pass P@ssw0rd
-			-cipher_suite TLS_SM4_GCM_SM3
-			-supported_group sm2p256v1
-			-psk_dhe_ke
-			-psk_identity 001
-			-psk_cipher_suite TLS_SM4_GCM_SM3
-			-psk_key ${TLS13_PSK}
-		CLIENT_ARGS
-			tls13_client
-			-host 127.0.0.1
-			-port 4437
-			-cipher_suite TLS_SM4_GCM_SM3
-			-supported_group sm2p256v1
-			-psk_dhe_ke
-			-psk_identity 001
-			-psk_cipher_suite TLS_SM4_GCM_SM3
-			-psk_key ${TLS13_PSK}
-			-in tls13_psk_dhe_sm4_gcm_message.txt
-	)
-elseif(TEST_CASE STREQUAL tls13_psk_only_sm4_gcm)
-	gmssl_run_tls_command_test(
-		TEST_NAME tls13_psk_only_sm4_gcm
-		PORT 4461
-		SERVER_ARGS
-			tls13_server
-			-port 4461
-			-cert sm2_tls_server_certs.pem
-			-key sm2_tls_server_key.pem
-			-pass P@ssw0rd
-			-cipher_suite TLS_SM4_GCM_SM3
-			-psk_ke
-			-psk_identity 001
-			-psk_cipher_suite TLS_SM4_GCM_SM3
-			-psk_key ${TLS13_PSK}
-		CLIENT_ARGS
-			tls13_client
-			-host 127.0.0.1
-			-port 4461
-			-cipher_suite TLS_SM4_GCM_SM3
-			-psk_ke
-			-psk_identity 001
-			-psk_cipher_suite TLS_SM4_GCM_SM3
-			-psk_key ${TLS13_PSK}
-			-in tls13_psk_only_sm4_gcm_message.txt
-	)
-elseif(TEST_CASE STREQUAL tls13_early_data_sm4_gcm)
-	gmssl_run_tls_command_test(
-		TEST_NAME tls13_early_data_sm4_gcm
-		PORT 4462
-		EXPECT_SERVER_LOG "EarlyData"
-		SERVER_ARGS
-			tls13_server
-			-port 4462
-			-cert sm2_tls_server_certs.pem
-			-key sm2_tls_server_key.pem
-			-pass P@ssw0rd
-			-cipher_suite TLS_SM4_GCM_SM3
-			-psk_ke
-			-psk_identity 001
-			-psk_cipher_suite TLS_SM4_GCM_SM3
-			-psk_key ${TLS13_PSK}
-			-early_data
-		CLIENT_ARGS
-			tls13_client
-			-host 127.0.0.1
-			-port 4462
-			-cipher_suite TLS_SM4_GCM_SM3
-			-psk_ke
-			-psk_identity 001
-			-psk_cipher_suite TLS_SM4_GCM_SM3
-			-psk_key ${TLS13_PSK}
-			-early_data tls13_early_data_sm4_gcm_early_data.txt
-			-in tls13_early_data_sm4_gcm_message.txt
-	)
-else()
-	message(FATAL_ERROR "unknown TLS 1.3 test case: ${TEST_CASE}")
-endif()
diff --git a/cmake/tls_command_test.cmake b/cmake/tls_command_test.cmake
deleted file mode 100644
index 644b24e1b..000000000
--- a/cmake/tls_command_test.cmake
+++ /dev/null
@@ -1,201 +0,0 @@
-function(gmssl_require_file file)
-	if(NOT EXISTS "${file}")
-		message(FATAL_ERROR "required file does not exist: ${file}")
-	endif()
-endfunction()
-
-function(gmssl_run_command_interop_test)
-	if(POLICY CMP0053)
-		cmake_policy(PUSH)
-		cmake_policy(SET CMP0053 NEW)
-	endif()
-
-	set(one_value_args TEST_NAME PORT SERVER_COMMAND CLIENT_COMMAND EXPECT_CLIENT_LOG EXPECT_SERVER_LOG)
-	cmake_parse_arguments(TEST "" "${one_value_args}" "" ${ARGN})
-
-	if(NOT TEST_TEST_NAME)
-		message(FATAL_ERROR "TEST_NAME is required")
-	endif()
-	if(NOT TEST_PORT)
-		message(FATAL_ERROR "PORT is required")
-	endif()
-	if(NOT TEST_SERVER_COMMAND)
-		message(FATAL_ERROR "SERVER_COMMAND is required")
-	endif()
-	if(NOT TEST_CLIENT_COMMAND)
-		message(FATAL_ERROR "CLIENT_COMMAND is required")
-	endif()
-
-	set(SERVER_LOG "${TEST_TEST_NAME}_server.log")
-	set(CLIENT_LOG "${TEST_TEST_NAME}_client.log")
-	set(SERVER_PID_FILE "${TEST_TEST_NAME}_server.pid")
-
-	file(REMOVE "${SERVER_LOG}" "${CLIENT_LOG}" "${SERVER_PID_FILE}")
-
-	execute_process(
-		COMMAND bash -c "nohup ${TEST_SERVER_COMMAND} > ${SERVER_LOG} 2>&1 & echo $! > ${SERVER_PID_FILE}"
-		RESULT_VARIABLE SERVER_RESULT
-		TIMEOUT 5
-	)
-	if(NOT ${SERVER_RESULT} EQUAL 0)
-		message(FATAL_ERROR "server failed to start")
-	endif()
-
-	execute_process(COMMAND ${CMAKE_COMMAND} -E sleep 1)
-
-	execute_process(
-		COMMAND bash -c "${TEST_CLIENT_COMMAND} > ${CLIENT_LOG} 2>&1"
-		RESULT_VARIABLE CLIENT_RESULT
-		TIMEOUT 30
-	)
-
-	execute_process(
-		COMMAND bash -c "if test -f ${SERVER_PID_FILE}; then kill $(cat ${SERVER_PID_FILE}) 2>/dev/null || true; fi"
-		OUTPUT_QUIET
-		ERROR_QUIET
-	)
-	execute_process(COMMAND ${CMAKE_COMMAND} -E sleep 1)
-
-	set(SERVER_LOG_CONTENT "")
-	set(CLIENT_LOG_CONTENT "")
-	if(EXISTS "${SERVER_LOG}")
-		file(READ "${SERVER_LOG}" SERVER_LOG_CONTENT)
-	endif()
-	if(EXISTS "${CLIENT_LOG}")
-		file(READ "${CLIENT_LOG}" CLIENT_LOG_CONTENT)
-	endif()
-
-	if(NOT ${CLIENT_RESULT} EQUAL 0)
-		message(STATUS "${SERVER_LOG}:\n${SERVER_LOG_CONTENT}")
-		message(STATUS "${CLIENT_LOG}:\n${CLIENT_LOG_CONTENT}")
-		message(FATAL_ERROR "client failed with result ${CLIENT_RESULT}")
-	endif()
-
-	if(TEST_EXPECT_CLIENT_LOG)
-		string(FIND "${CLIENT_LOG_CONTENT}" "${TEST_EXPECT_CLIENT_LOG}" FOUND_INDEX)
-		if(${FOUND_INDEX} EQUAL -1)
-			message(STATUS "${CLIENT_LOG}:\n${CLIENT_LOG_CONTENT}")
-			message(FATAL_ERROR "client log does not contain expected text: ${TEST_EXPECT_CLIENT_LOG}")
-		endif()
-	endif()
-
-	if(TEST_EXPECT_SERVER_LOG)
-		string(FIND "${SERVER_LOG_CONTENT}" "${TEST_EXPECT_SERVER_LOG}" FOUND_INDEX)
-		if(${FOUND_INDEX} EQUAL -1)
-			message(STATUS "${SERVER_LOG}:\n${SERVER_LOG_CONTENT}")
-			message(FATAL_ERROR "server log does not contain expected text: ${TEST_EXPECT_SERVER_LOG}")
-		endif()
-	endif()
-
-	if(POLICY CMP0053)
-		cmake_policy(POP)
-	endif()
-endfunction()
-
-function(gmssl_run_tls_command_test)
-	if(POLICY CMP0053)
-		cmake_policy(PUSH)
-		cmake_policy(SET CMP0053 NEW)
-	endif()
-
-	set(one_value_args TEST_NAME PORT EXPECT_CLIENT_LOG EXPECT_SERVER_LOG)
-	set(multi_value_args SERVER_ARGS CLIENT_ARGS)
-	cmake_parse_arguments(TEST "" "${one_value_args}" "${multi_value_args}" ${ARGN})
-
-	if(NOT TEST_TEST_NAME)
-		message(FATAL_ERROR "TEST_NAME is required")
-	endif()
-	if(NOT TEST_PORT)
-		message(FATAL_ERROR "PORT is required")
-	endif()
-	if(NOT TEST_SERVER_ARGS)
-		message(FATAL_ERROR "SERVER_ARGS is required")
-	endif()
-	if(NOT TEST_CLIENT_ARGS)
-		message(FATAL_ERROR "CLIENT_ARGS is required")
-	endif()
-
-	list(GET TEST_SERVER_ARGS 0 SERVER_TOOL)
-	set(SERVER_LOG "${TEST_TEST_NAME}_server.log")
-	set(CLIENT_LOG "${TEST_TEST_NAME}_client.log")
-	set(SERVER_PID_FILE "${TEST_TEST_NAME}_server.pid")
-
-	file(REMOVE "${SERVER_LOG}" "${CLIENT_LOG}" "${SERVER_PID_FILE}")
-	file(WRITE "${TEST_TEST_NAME}_message.txt" "GmSSL ${TEST_TEST_NAME} command test\n")
-	file(WRITE "${TEST_TEST_NAME}_early_data.txt" "GmSSL ${TEST_TEST_NAME} early data\n")
-
-	string(REPLACE ";" " " SERVER_CMD "${TEST_SERVER_ARGS}")
-	string(REPLACE ";" " " CLIENT_CMD "${TEST_CLIENT_ARGS}")
-
-	execute_process(
-		COMMAND pkill -f "gmssl ${SERVER_TOOL} -port ${TEST_PORT}"
-		OUTPUT_QUIET
-		ERROR_QUIET
-	)
-
-	execute_process(
-		COMMAND bash -c "nohup bin/gmssl ${SERVER_CMD} > ${SERVER_LOG} 2>&1 & echo $! > ${SERVER_PID_FILE}"
-		RESULT_VARIABLE SERVER_RESULT
-		TIMEOUT 5
-	)
-	if(NOT ${SERVER_RESULT} EQUAL 0)
-		message(FATAL_ERROR "server failed to start")
-	endif()
-
-	execute_process(COMMAND ${CMAKE_COMMAND} -E sleep 1)
-
-	execute_process(
-		COMMAND bash -c "bin/gmssl ${CLIENT_CMD} > ${CLIENT_LOG} 2>&1"
-		RESULT_VARIABLE CLIENT_RESULT
-		TIMEOUT 30
-	)
-
-	execute_process(
-		COMMAND pkill -f "gmssl ${SERVER_TOOL} -port ${TEST_PORT}"
-		OUTPUT_QUIET
-		ERROR_QUIET
-	)
-	execute_process(COMMAND ${CMAKE_COMMAND} -E sleep 1)
-
-	set(SERVER_LOG_CONTENT "")
-	set(CLIENT_LOG_CONTENT "")
-	if(EXISTS "${SERVER_LOG}")
-		file(READ "${SERVER_LOG}" SERVER_LOG_CONTENT)
-	endif()
-	if(EXISTS "${CLIENT_LOG}")
-		file(READ "${CLIENT_LOG}" CLIENT_LOG_CONTENT)
-	endif()
-
-	if(NOT ${CLIENT_RESULT} EQUAL 0)
-		message(STATUS "${SERVER_LOG}:\n${SERVER_LOG_CONTENT}")
-		message(STATUS "${CLIENT_LOG}:\n${CLIENT_LOG_CONTENT}")
-		message(FATAL_ERROR "client failed with result ${CLIENT_RESULT}")
-	endif()
-
-	string(FIND "${CLIENT_LOG_CONTENT}" "Connection established" FOUND_INDEX)
-	if(${FOUND_INDEX} EQUAL -1)
-		message(STATUS "${SERVER_LOG}:\n${SERVER_LOG_CONTENT}")
-		message(STATUS "${CLIENT_LOG}:\n${CLIENT_LOG_CONTENT}")
-		message(FATAL_ERROR "client did not establish connection with server")
-	endif()
-
-	if(TEST_EXPECT_CLIENT_LOG)
-		string(FIND "${CLIENT_LOG_CONTENT}" "${TEST_EXPECT_CLIENT_LOG}" FOUND_INDEX)
-		if(${FOUND_INDEX} EQUAL -1)
-			message(STATUS "${CLIENT_LOG}:\n${CLIENT_LOG_CONTENT}")
-			message(FATAL_ERROR "client log does not contain expected text: ${TEST_EXPECT_CLIENT_LOG}")
-		endif()
-	endif()
-
-	if(TEST_EXPECT_SERVER_LOG)
-		string(FIND "${SERVER_LOG_CONTENT}" "${TEST_EXPECT_SERVER_LOG}" FOUND_INDEX)
-		if(${FOUND_INDEX} EQUAL -1)
-			message(STATUS "${SERVER_LOG}:\n${SERVER_LOG_CONTENT}")
-			message(FATAL_ERROR "server log does not contain expected text: ${TEST_EXPECT_SERVER_LOG}")
-		endif()
-	endif()
-
-	if(POLICY CMP0053)
-		cmake_policy(POP)
-	endif()
-endfunction()
diff --git a/cmake/tool_cert.cmake b/cmake/tool_cert.cmake
deleted file mode 100644
index 814fafcf1..000000000
--- a/cmake/tool_cert.cmake
+++ /dev/null
@@ -1,257 +0,0 @@
-set(GMSSL_TEST_PASS P@ssw0rd)
-set(GMSSL_TEST_SUBJECT -C CN -ST Beijing -L Haidian -O GmSSL -OU Test)
-
-function(gmssl_run)
-	execute_process(
-		COMMAND ${ARGN}
-		RESULT_VARIABLE TEST_RESULT
-		ERROR_VARIABLE TEST_STDERR
-	)
-	if(NOT ${TEST_RESULT} EQUAL 0)
-		message(FATAL_ERROR "command failed: ${ARGN}\nstderr: ${TEST_STDERR}")
-	endif()
-endfunction()
-
-function(gmssl_require_generated_file file)
-	if(NOT EXISTS "${file}")
-		message(FATAL_ERROR "generated file does not exist: ${file}")
-	endif()
-endfunction()
-
-function(gmssl_read_generated_pem file expected_header)
-	gmssl_require_generated_file("${file}")
-	file(READ "${file}" FILE_CONTENT)
-	if(NOT FILE_CONTENT MATCHES "^${expected_header}")
-		message(FATAL_ERROR "generated file has unexpected PEM header: ${file}")
-	endif()
-endfunction()
-
-function(gmssl_generate_sm2_key key_file)
-	gmssl_run(bin/gmssl sm2keygen -pass ${GMSSL_TEST_PASS} -out "${key_file}")
-	gmssl_require_generated_file("${key_file}")
-endfunction()
-
-function(gmssl_generate_p256_key key_file export_file)
-	if(export_file)
-		gmssl_run(bin/gmssl eckeygen -curve secp256r1 -pass ${GMSSL_TEST_PASS} -out "${key_file}" -export "${export_file}")
-		gmssl_require_generated_file("${export_file}")
-	else()
-		gmssl_run(bin/gmssl eckeygen -curve secp256r1 -pass ${GMSSL_TEST_PASS} -out "${key_file}")
-	endif()
-	gmssl_require_generated_file("${key_file}")
-endfunction()
-
-function(gmssl_generate_key alg key_file export_file)
-	if(alg STREQUAL SM2)
-		gmssl_generate_sm2_key("${key_file}")
-	elseif(alg STREQUAL P256)
-		gmssl_generate_p256_key("${key_file}" "${export_file}")
-	else()
-		message(FATAL_ERROR "unknown key algorithm: ${alg}")
-	endif()
-endfunction()
-
-function(gmssl_x509_sig_alg alg out_var)
-	if(alg STREQUAL SM2)
-		set(${out_var} sm2sign-with-sm3 PARENT_SCOPE)
-	elseif(alg STREQUAL P256)
-		set(${out_var} ecdsa-with-sha256 PARENT_SCOPE)
-	else()
-		message(FATAL_ERROR "unknown key algorithm: ${alg}")
-	endif()
-endfunction()
-
-function(gmssl_generate_root_ca alg prefix common_name)
-	gmssl_generate_key(${alg} "${prefix}_key.pem" "${prefix}_key.exp")
-	gmssl_x509_sig_alg(${alg} sig_alg)
-	gmssl_run(bin/gmssl certgen
-		${GMSSL_TEST_SUBJECT}
-		-CN "${common_name}"
-		-days 3650
-		-key "${prefix}_key.pem"
-		-pass ${GMSSL_TEST_PASS}
-		-sig_alg ${sig_alg}
-		-out "${prefix}_cert.pem"
-		-key_usage keyCertSign
-		-key_usage cRLSign
-		-ca)
-	gmssl_read_generated_pem("${prefix}_cert.pem" "-----BEGIN CERTIFICATE-----")
-endfunction()
-
-function(gmssl_generate_ca alg prefix common_name issuer_cert issuer_key path_len)
-	gmssl_generate_key(${alg} "${prefix}_key.pem" "${prefix}_key.exp")
-	gmssl_x509_sig_alg(${alg} sig_alg)
-	gmssl_run(bin/gmssl reqgen
-		${GMSSL_TEST_SUBJECT}
-		-CN "${common_name}"
-		-key "${prefix}_key.pem"
-		-pass ${GMSSL_TEST_PASS}
-		-sig_alg ${sig_alg}
-		-out "${prefix}_req.pem")
-	gmssl_read_generated_pem("${prefix}_req.pem" "-----BEGIN CERTIFICATE REQUEST-----")
-	gmssl_run(bin/gmssl reqsign
-		-in "${prefix}_req.pem"
-		-days 1825
-		-key_usage keyCertSign
-		-key_usage cRLSign
-		-path_len_constraint ${path_len}
-		-cacert "${issuer_cert}"
-		-key "${issuer_key}"
-		-pass ${GMSSL_TEST_PASS}
-		-sig_alg ${sig_alg}
-		-out "${prefix}_cert.pem"
-		-ca)
-	gmssl_read_generated_pem("${prefix}_cert.pem" "-----BEGIN CERTIFICATE-----")
-endfunction()
-
-function(gmssl_generate_end_entity alg prefix common_name issuer_cert issuer_key key_usage ext_key_usage subject_dns_name export_key)
-	if(export_key)
-		set(export_file "${prefix}_key.exp")
-	else()
-		set(export_file "")
-	endif()
-	gmssl_generate_key(${alg} "${prefix}_key.pem" "${export_file}")
-	gmssl_x509_sig_alg(${alg} sig_alg)
-	gmssl_run(bin/gmssl reqgen
-		${GMSSL_TEST_SUBJECT}
-		-CN "${common_name}"
-		-key "${prefix}_key.pem"
-		-pass ${GMSSL_TEST_PASS}
-		-sig_alg ${sig_alg}
-		-out "${prefix}_req.pem")
-	gmssl_read_generated_pem("${prefix}_req.pem" "-----BEGIN CERTIFICATE REQUEST-----")
-
-	set(sign_args
-		-in "${prefix}_req.pem"
-		-days 365
-		-key_usage ${key_usage}
-		-cacert "${issuer_cert}"
-		-key "${issuer_key}"
-		-pass ${GMSSL_TEST_PASS}
-		-sig_alg ${sig_alg}
-		-out "${prefix}_cert.pem")
-	if(ext_key_usage)
-		list(APPEND sign_args -ext_key_usage ${ext_key_usage})
-	endif()
-	if(subject_dns_name)
-		list(APPEND sign_args -subject_dns_name ${subject_dns_name})
-	endif()
-	gmssl_run(bin/gmssl reqsign ${sign_args})
-	gmssl_read_generated_pem("${prefix}_cert.pem" "-----BEGIN CERTIFICATE-----")
-endfunction()
-
-function(gmssl_write_bundle out_file)
-	file(WRITE "${out_file}" "")
-	foreach(pem_file IN LISTS ARGN)
-		gmssl_require_generated_file("${pem_file}")
-		file(READ "${pem_file}" PEM_CONTENT)
-		file(APPEND "${out_file}" "${PEM_CONTENT}")
-	endforeach()
-	gmssl_require_generated_file("${out_file}")
-endfunction()
-
-# Root CAs
-gmssl_generate_root_ca(SM2 sm2_root_ca "GmSSL SM2 Test Root CA")
-gmssl_generate_root_ca(P256 p256_root_ca "GmSSL P256 Test Root CA")
-
-# SM2 TLS server chain: root -> server CA 1 -> server CA 2 -> server certificate
-gmssl_generate_ca(SM2 sm2_tls_server_ca1 "GmSSL SM2 TLS Server CA 1"
-	sm2_root_ca_cert.pem sm2_root_ca_key.pem 1)
-gmssl_generate_ca(SM2 sm2_tls_server_ca2 "GmSSL SM2 TLS Server CA 2"
-	sm2_tls_server_ca1_cert.pem sm2_tls_server_ca1_key.pem 0)
-gmssl_generate_end_entity(SM2 sm2_tls_server "GmSSL SM2 TLS Server"
-	sm2_tls_server_ca2_cert.pem sm2_tls_server_ca2_key.pem
-	digitalSignature serverAuth localhost OFF)
-gmssl_write_bundle(sm2_tls_server_certs.pem
-	sm2_tls_server_cert.pem sm2_tls_server_ca2_cert.pem sm2_tls_server_ca1_cert.pem)
-
-# P256 TLS server chain: root -> server CA 1 -> server CA 2 -> server certificate
-gmssl_generate_ca(P256 p256_tls_server_ca1 "GmSSL P256 TLS Server CA 1"
-	p256_root_ca_cert.pem p256_root_ca_key.pem 1)
-gmssl_generate_ca(P256 p256_tls_server_ca2 "GmSSL P256 TLS Server CA 2"
-	p256_tls_server_ca1_cert.pem p256_tls_server_ca1_key.pem 0)
-gmssl_generate_end_entity(P256 p256_tls_server "GmSSL P256 TLS Server"
-	p256_tls_server_ca2_cert.pem p256_tls_server_ca2_key.pem
-	digitalSignature serverAuth localhost ON)
-gmssl_write_bundle(p256_tls_server_certs.pem
-	p256_tls_server_cert.pem p256_tls_server_ca2_cert.pem p256_tls_server_ca1_cert.pem)
-gmssl_write_bundle(p256_tls_server_cert_chain.pem
-	p256_tls_server_ca2_cert.pem p256_tls_server_ca1_cert.pem)
-
-# SM2 TLS client chain: root -> client CA -> client certificate
-gmssl_generate_ca(SM2 sm2_tls_client_ca "GmSSL SM2 TLS Client CA"
-	sm2_root_ca_cert.pem sm2_root_ca_key.pem 0)
-gmssl_generate_end_entity(SM2 sm2_tls_client "GmSSL SM2 TLS Client"
-	sm2_tls_client_ca_cert.pem sm2_tls_client_ca_key.pem
-	digitalSignature clientAuth "" OFF)
-gmssl_write_bundle(sm2_tls_client_certs.pem
-	sm2_tls_client_cert.pem sm2_tls_client_ca_cert.pem)
-
-# SM2 TLCP client chain reuses the SM2 TLS client CA and adds an encryption certificate.
-gmssl_generate_end_entity(SM2 sm2_tlcp_client_sign "GmSSL SM2 TLCP Client"
-	sm2_tls_client_ca_cert.pem sm2_tls_client_ca_key.pem
-	digitalSignature clientAuth "" OFF)
-gmssl_generate_end_entity(SM2 sm2_tlcp_client_enc "GmSSL SM2 TLCP Client"
-	sm2_tls_client_ca_cert.pem sm2_tls_client_ca_key.pem
-	keyEncipherment clientAuth "" OFF)
-gmssl_write_bundle(sm2_tlcp_client_certs.pem
-	sm2_tlcp_client_sign_cert.pem
-	sm2_tlcp_client_enc_cert.pem
-	sm2_tls_client_ca_cert.pem)
-gmssl_write_bundle(sm2_tlcp_client_keys.pem
-	sm2_tlcp_client_sign_key.pem sm2_tlcp_client_enc_key.pem)
-
-# P256 TLS client chain: root -> client CA -> client certificate
-gmssl_generate_ca(P256 p256_tls_client_ca "GmSSL P256 TLS Client CA"
-	p256_root_ca_cert.pem p256_root_ca_key.pem 0)
-gmssl_generate_end_entity(P256 p256_tls_client "GmSSL P256 TLS Client"
-	p256_tls_client_ca_cert.pem p256_tls_client_ca_key.pem
-	digitalSignature clientAuth "" ON)
-gmssl_write_bundle(p256_tls_client_certs.pem
-	p256_tls_client_cert.pem p256_tls_client_ca_cert.pem)
-
-# OCSP delegated responders for certificates issued by the TLS server CA2s.
-gmssl_generate_end_entity(SM2 sm2_ocsp_responder "GmSSL SM2 OCSP Responder"
-	sm2_tls_server_ca2_cert.pem sm2_tls_server_ca2_key.pem
-	digitalSignature OCSPSigning "" OFF)
-gmssl_generate_end_entity(P256 p256_ocsp_responder "GmSSL P256 OCSP Responder"
-	p256_tls_server_ca2_cert.pem p256_tls_server_ca2_key.pem
-	digitalSignature OCSPSigning "" ON)
-
-# TLCP server chain reuses the SM2 TLS server CA chain and adds an encryption certificate.
-gmssl_generate_end_entity(SM2 sm2_tlcp_server_sign "GmSSL SM2 TLCP Server"
-	sm2_tls_server_ca2_cert.pem sm2_tls_server_ca2_key.pem
-	digitalSignature serverAuth localhost OFF)
-gmssl_generate_end_entity(SM2 sm2_tlcp_server_enc "GmSSL SM2 TLCP Server"
-	sm2_tls_server_ca2_cert.pem sm2_tls_server_ca2_key.pem
-	keyEncipherment serverAuth localhost OFF)
-gmssl_write_bundle(sm2_tlcp_server_certs.pem
-	sm2_tlcp_server_sign_cert.pem
-	sm2_tlcp_server_enc_cert.pem
-	sm2_tls_server_ca2_cert.pem
-	sm2_tls_server_ca1_cert.pem)
-gmssl_write_bundle(sm2_tlcp_server_keys.pem
-	sm2_tlcp_server_sign_key.pem sm2_tlcp_server_enc_key.pem)
-
-gmssl_write_bundle(test_root_certs.pem
-	sm2_root_ca_cert.pem p256_root_ca_cert.pem)
-
-gmssl_run(bin/gmssl certparse -in sm2_tlcp_server_certs.pem -out tool_certparse.txt)
-gmssl_require_generated_file(tool_certparse.txt)
-
-gmssl_run(bin/gmssl certverify
-	-tlcp_server
-	-in sm2_tlcp_server_certs.pem
-	-cacert sm2_root_ca_cert.pem
-	-hostname localhost)
-
-gmssl_run(bin/gmssl certverify
-	-server
-	-in sm2_tls_server_certs.pem
-	-cacert sm2_root_ca_cert.pem
-	-hostname LOCALHOST)
-
-gmssl_run(bin/gmssl certverify
-	-client
-	-in sm2_tls_client_certs.pem
-	-cacert sm2_root_ca_cert.pem)
diff --git a/cmake/tool_cms.cmake b/cmake/tool_cms.cmake
deleted file mode 100644
index aacae1200..000000000
--- a/cmake/tool_cms.cmake
+++ /dev/null
@@ -1,32 +0,0 @@
-include("${CMAKE_CURRENT_LIST_DIR}/tool_helpers.cmake")
-
-file(WRITE tool_cms_message.txt "CMS command line test message")
-
-gmssl_run(cmssign
-	-key sm2_tls_server_key.pem
-	-pass P@ssw0rd
-	-cert sm2_tls_server_cert.pem
-	-in tool_cms_message.txt
-	-out tool_cms_signed.pem)
-gmssl_require_file(tool_cms_signed.pem)
-
-gmssl_run(cmsparse -in tool_cms_signed.pem)
-
-gmssl_run(cmsverify
-	-in tool_cms_signed.pem
-	-out tool_cms_verified.txt)
-gmssl_expect_file_text(tool_cms_verified.txt "CMS command line test message")
-
-gmssl_run(cmsencrypt
-	-rcptcert sm2_tlcp_server_enc_cert.pem
-	-in tool_cms_message.txt
-	-out tool_cms_enveloped.pem)
-gmssl_require_file(tool_cms_enveloped.pem)
-
-gmssl_run(cmsdecrypt
-	-key sm2_tlcp_server_enc_key.pem
-	-pass P@ssw0rd
-	-cert sm2_tlcp_server_enc_cert.pem
-	-in tool_cms_enveloped.pem
-	-out tool_cms_decrypted.txt)
-gmssl_expect_file_text(tool_cms_decrypted.txt "CMS command line test message")
diff --git a/cmake/tool_crl.cmake b/cmake/tool_crl.cmake
deleted file mode 100644
index 32f34e9ff..000000000
--- a/cmake/tool_crl.cmake
+++ /dev/null
@@ -1,26 +0,0 @@
-include("${CMAKE_CURRENT_LIST_DIR}/tool_helpers.cmake")
-
-gmssl_run(certrevoke
-	-in sm2_tls_server_cert.pem
-	-reason keyCompromise
-	-invalid_date 20260101000000Z
-	-out tool_revoked_certs.der)
-gmssl_require_file(tool_revoked_certs.der)
-
-gmssl_run(crlgen
-	-in tool_revoked_certs.der
-	-cacert sm2_tls_server_ca2_cert.pem
-	-key sm2_tls_server_ca2_key.pem
-	-pass P@ssw0rd
-	-sig_alg sm2sign-with-sm3
-	-next_update 20270101000000Z
-	-gen_authority_key_id
-	-crl_num 1
-	-out tool_crl.der)
-gmssl_require_file(tool_crl.der)
-
-gmssl_run(crlparse -in tool_crl.der -out tool_crl.txt)
-gmssl_require_file(tool_crl.txt)
-
-gmssl_expect_stdout("Verification success\n"
-	crlverify -in tool_crl.der -cacert sm2_tls_server_ca2_cert.pem)
diff --git a/cmake/tool_ghash.cmake b/cmake/tool_ghash.cmake
deleted file mode 100644
index 8bf94769c..000000000
--- a/cmake/tool_ghash.cmake
+++ /dev/null
@@ -1,9 +0,0 @@
-include("${CMAKE_CURRENT_LIST_DIR}/tool_helpers.cmake")
-
-gmssl_expect_stdout("50db43e2ab4a2bbddd6e1182de2cc22b\n"
-	ghash -h 0123456789abcdeffedcba9876543210 -aad_hex 001122 -in_str abc)
-
-file(WRITE tool_ghash_input.txt "abc")
-gmssl_run(ghash -h 0123456789abcdeffedcba9876543210 -aad "aad" -bin
-	-in tool_ghash_input.txt -out tool_ghash.bin)
-gmssl_require_file(tool_ghash.bin)
diff --git a/cmake/tool_helpers.cmake b/cmake/tool_helpers.cmake
deleted file mode 100644
index 8346a74df..000000000
--- a/cmake/tool_helpers.cmake
+++ /dev/null
@@ -1,79 +0,0 @@
-set(GMSSL_BIN bin/gmssl)
-
-function(gmssl_run)
-	execute_process(
-		COMMAND ${GMSSL_BIN} ${ARGN}
-		RESULT_VARIABLE TEST_RESULT
-		ERROR_VARIABLE TEST_STDERR
-		OUTPUT_VARIABLE TEST_STDOUT
-	)
-	if(NOT TEST_RESULT EQUAL 0)
-		message(FATAL_ERROR "command failed: ${GMSSL_BIN} ${ARGN}\nstderr: ${TEST_STDERR}\nstdout: ${TEST_STDOUT}")
-	endif()
-endfunction()
-
-function(gmssl_run_capture out_var)
-	execute_process(
-		COMMAND ${GMSSL_BIN} ${ARGN}
-		RESULT_VARIABLE TEST_RESULT
-		ERROR_VARIABLE TEST_STDERR
-		OUTPUT_VARIABLE TEST_STDOUT
-	)
-	if(NOT TEST_RESULT EQUAL 0)
-		message(FATAL_ERROR "command failed: ${GMSSL_BIN} ${ARGN}\nstderr: ${TEST_STDERR}\nstdout: ${TEST_STDOUT}")
-	endif()
-	set(${out_var} "${TEST_STDOUT}" PARENT_SCOPE)
-endfunction()
-
-function(gmssl_expect_fail)
-	execute_process(
-		COMMAND ${GMSSL_BIN} ${ARGN}
-		RESULT_VARIABLE TEST_RESULT
-		ERROR_VARIABLE TEST_STDERR
-		OUTPUT_VARIABLE TEST_STDOUT
-	)
-	if(TEST_RESULT EQUAL 0)
-		message(FATAL_ERROR "command unexpectedly succeeded: ${GMSSL_BIN} ${ARGN}\nstderr: ${TEST_STDERR}\nstdout: ${TEST_STDOUT}")
-	endif()
-endfunction()
-
-function(gmssl_expect_stdout expected)
-	gmssl_run_capture(TEST_STDOUT ${ARGN})
-	if(NOT TEST_STDOUT STREQUAL "${expected}")
-		message(FATAL_ERROR "unexpected stdout for ${GMSSL_BIN} ${ARGN}\nexpected: ${expected}\nactual: ${TEST_STDOUT}")
-	endif()
-endfunction()
-
-function(gmssl_require_file file)
-	if(NOT EXISTS "${file}")
-		message(FATAL_ERROR "generated file does not exist: ${file}")
-	endif()
-endfunction()
-
-function(gmssl_files_equal expected actual)
-	gmssl_require_file("${expected}")
-	gmssl_require_file("${actual}")
-	file(SHA256 "${expected}" EXPECTED_HASH)
-	file(SHA256 "${actual}" ACTUAL_HASH)
-	if(NOT EXPECTED_HASH STREQUAL ACTUAL_HASH)
-		message(FATAL_ERROR "file mismatch: ${expected} ${actual}")
-	endif()
-endfunction()
-
-function(gmssl_expect_file_hex file expected_hex)
-	gmssl_require_file("${file}")
-	file(READ "${file}" ACTUAL_HEX HEX)
-	string(TOLOWER "${ACTUAL_HEX}" ACTUAL_HEX)
-	string(TOLOWER "${expected_hex}" EXPECTED_HEX)
-	if(NOT ACTUAL_HEX STREQUAL EXPECTED_HEX)
-		message(FATAL_ERROR "unexpected hex in ${file}\nexpected: ${EXPECTED_HEX}\nactual: ${ACTUAL_HEX}")
-	endif()
-endfunction()
-
-function(gmssl_expect_file_text file expected_text)
-	gmssl_require_file("${file}")
-	file(READ "${file}" ACTUAL_TEXT)
-	if(NOT ACTUAL_TEXT STREQUAL "${expected_text}")
-		message(FATAL_ERROR "unexpected text in ${file}\nexpected: ${expected_text}\nactual: ${ACTUAL_TEXT}")
-	endif()
-endfunction()
diff --git a/cmake/tool_ocsp.cmake b/cmake/tool_ocsp.cmake
deleted file mode 100644
index a41fbe9f6..000000000
--- a/cmake/tool_ocsp.cmake
+++ /dev/null
@@ -1,35 +0,0 @@
-include("${CMAKE_CURRENT_LIST_DIR}/tool_helpers.cmake")
-
-file(READ sm2_tls_server_cert.pem OCSP_CERT)
-file(READ sm2_tls_server_ca2_cert.pem OCSP_ISSUER)
-file(WRITE tool_ocsp_chain.pem "${OCSP_CERT}${OCSP_ISSUER}")
-
-gmssl_run(ocspreq
-	-in tool_ocsp_chain.pem
-	-digest sm3
-	-out tool_ocsp_req.der
-	-verbose)
-gmssl_require_file(tool_ocsp_req.der)
-
-gmssl_run(ocspsign
-	-reqin tool_ocsp_req.der
-	-cacert sm2_tls_server_ca2_cert.pem
-	-signer sm2_ocsp_responder_cert.pem
-	-key sm2_ocsp_responder_key.pem
-	-pass P@ssw0rd
-	-sig_alg sm2sign-with-sm3
-	-status good
-	-certs sm2_ocsp_responder_cert.pem
-	-out tool_ocsp_resp.der
-	-verbose)
-gmssl_require_file(tool_ocsp_resp.der)
-
-gmssl_expect_stdout("Verification success\n"
-	ocspverify
-	-reqin tool_ocsp_req.der
-	-respin tool_ocsp_resp.der
-	-cacert sm2_tls_server_ca2_cert.pem
-	-signer sm2_ocsp_responder_cert.pem
-	-certs sm2_ocsp_responder_cert.pem
-	-clock_skew 300
-	-verbose)
diff --git a/cmake/tool_pqc.cmake b/cmake/tool_pqc.cmake
deleted file mode 100644
index 4038a4fbf..000000000
--- a/cmake/tool_pqc.cmake
+++ /dev/null
@@ -1,40 +0,0 @@
-include("${CMAKE_CURRENT_LIST_DIR}/tool_helpers.cmake")
-
-file(WRITE tool_pqc_message.txt "PQC command line test message")
-
-function(gmssl_signature_roundtrip name keygen sign verify)
-	cmake_parse_arguments(ARG "" "" "KEYGEN_ARGS;SIGN_ARGS;VERIFY_ARGS" ${ARGN})
-	gmssl_run(${keygen} ${ARG_KEYGEN_ARGS}
-		-out "${name}_key.pem" -pubout "${name}_pub.pem")
-	gmssl_run(${sign} -key "${name}_key.pem"
-		-in tool_pqc_message.txt -out "${name}.sig" ${ARG_SIGN_ARGS})
-	gmssl_run(${verify} -pubkey "${name}_pub.pem"
-		-in tool_pqc_message.txt -sig "${name}.sig" ${ARG_VERIFY_ARGS})
-endfunction()
-
-if(ENABLE_LMS)
-	gmssl_signature_roundtrip(tool_lms lmskeygen lmssign lmsverify
-		KEYGEN_ARGS -lms_type LMS_SM3_M32_H5)
-	gmssl_signature_roundtrip(tool_hss hsskeygen hsssign hssverify
-		KEYGEN_ARGS -lms_types LMS_SM3_M32_H5:LMS_SM3_M32_H5)
-endif()
-
-if(ENABLE_XMSS)
-	gmssl_signature_roundtrip(tool_xmss xmsskeygen xmsssign xmssverify
-		KEYGEN_ARGS -xmss_type XMSS_SM3_10_256)
-	gmssl_signature_roundtrip(tool_xmssmt xmssmtkeygen xmssmtsign xmssmtverify
-		KEYGEN_ARGS -xmssmt_type XMSSMT_SM3_20_2_256)
-endif()
-
-if(ENABLE_SPHINCS)
-	gmssl_signature_roundtrip(tool_sphincs sphincskeygen sphincssign sphincsverify)
-endif()
-
-if(ENABLE_KYBER)
-	gmssl_run(kyberkeygen -out tool_kyber_key.pem -pubout tool_kyber_pub.pem)
-	gmssl_run(kyberencap -pubkey tool_kyber_pub.pem
-		-out tool_kyber_cipher.bin -outkey tool_kyber_secret.bin)
-	gmssl_run(kyberdecap -key tool_kyber_key.pem
-		-in tool_kyber_cipher.bin -out tool_kyber_dec_secret.bin)
-	gmssl_files_equal(tool_kyber_secret.bin tool_kyber_dec_secret.bin)
-endif()
diff --git a/cmake/tool_rand.cmake b/cmake/tool_rand.cmake
deleted file mode 100644
index f0b8fabe3..000000000
--- a/cmake/tool_rand.cmake
+++ /dev/null
@@ -1,15 +0,0 @@
-include("${CMAKE_CURRENT_LIST_DIR}/tool_helpers.cmake")
-
-gmssl_run(rand -outlen 16 -out tool_rand.bin)
-file(SIZE tool_rand.bin RAND_BIN_SIZE)
-if(NOT RAND_BIN_SIZE EQUAL 16)
-	message(FATAL_ERROR "unexpected rand output size: ${RAND_BIN_SIZE}")
-endif()
-
-gmssl_run_capture(RAND_HEX rand -hex -outlen 16)
-string(STRIP "${RAND_HEX}" RAND_HEX_STRIPPED)
-string(REGEX MATCH "^[0-9a-fA-F]+$" RAND_HEX_MATCH "${RAND_HEX_STRIPPED}")
-string(LENGTH "${RAND_HEX_STRIPPED}" RAND_HEX_LEN)
-if(NOT RAND_HEX_MATCH OR NOT RAND_HEX_LEN EQUAL 32)
-	message(FATAL_ERROR "unexpected rand hex output: ${RAND_HEX}")
-endif()
diff --git a/cmake/tool_sm2.cmake b/cmake/tool_sm2.cmake
deleted file mode 100644
index c8f02a736..000000000
--- a/cmake/tool_sm2.cmake
+++ /dev/null
@@ -1,298 +0,0 @@
-execute_process(
-	COMMAND bin/gmssl sm2keygen -pass P@ssw0rd -out sm2.pem -pubout sm2pub.pem
-	RESULT_VARIABLE TEST_RESULT
-	ERROR_VARIABLE TEST_STDERR
-)
-if(NOT ${TEST_RESULT} EQUAL 0)
-	message(FATAL_ERROR "stderr: ${TEST_STDERR}")
-endif()
-if(NOT EXISTS sm2.pem)
-	message(FATAL_ERROR "generated file does not exist")
-endif()
-if(NOT EXISTS sm2pub.pem)
-	message(FATAL_ERROR "generated file does not exist")
-endif()
-
-set(SECRET_MESSAGE "Secret message")
-file(WRITE message.txt "${SECRET_MESSAGE}")
-
-execute_process(
-	COMMAND bin/gmssl sm2sign -key sm2.pem -pass P@ssw0rd -in message.txt -out sm2.sig
-	RESULT_VARIABLE TEST_RESULT
-	ERROR_VARIABLE TEST_STDERR
-)
-if(NOT ${TEST_RESULT} EQUAL 0)
-	message(FATAL_ERROR "stderr: ${TEST_STDERR}")
-endif()
-
-
-execute_process(
-	COMMAND bin/gmssl sm2verify -pubkey sm2pub.pem -in message.txt -sig sm2.sig
-	RESULT_VARIABLE TEST_RESULT
-	ERROR_VARIABLE TEST_STDERR
-	OUTPUT_VARIABLE TEST_STDOUT
-)
-if(NOT ${TEST_RESULT} EQUAL 0)
-	message(FATAL_ERROR "stderr: ${TEST_STDERR}")
-endif()
-string(FIND "${TEST_STDOUT}" "success" VERIFY_SUCCESS)
-if(VERIFY_SUCCESS EQUAL -1)
-    message(FATAL_ERROR "verify failure")
-endif()
-
-
-execute_process(
-	COMMAND bin/gmssl sm2encrypt -pubkey sm2pub.pem -in message.txt -out sm2.der
-	RESULT_VARIABLE TEST_RESULT
-	ERROR_VARIABLE TEST_STDERR
-	OUTPUT_VARIABLE TEST_STDOUT
-)
-if(NOT ${TEST_RESULT} EQUAL 0)
-	message(FATAL_ERROR "stderr: ${TEST_STDERR}")
-endif()
-
-execute_process(
-	COMMAND bin/gmssl sm2decrypt -key sm2.pem -pass P@ssw0rd -in sm2.der
-	RESULT_VARIABLE TEST_RESULT
-	ERROR_VARIABLE TEST_STDERR
-	OUTPUT_VARIABLE TEST_STDOUT
-)
-if(NOT ${TEST_RESULT} EQUAL 0)
-	message(FATAL_ERROR "stderr: ${TEST_STDERR}")
-endif()
-if(NOT "${TEST_STDOUT}" STREQUAL "${SECRET_MESSAGE}")
-	message(FATAL_ERROR "stdout: ${TEST_STDOUT}")
-endif()
-
-execute_process(
-	COMMAND bin/gmssl sm2keygen -pass P@ssw0rd -out sm2_peer.pem -pubout sm2_peer_pub.pem
-	RESULT_VARIABLE TEST_RESULT
-	ERROR_VARIABLE TEST_STDERR
-)
-if(NOT ${TEST_RESULT} EQUAL 0)
-	message(FATAL_ERROR "stderr: ${TEST_STDERR}")
-endif()
-
-execute_process(
-	COMMAND bin/gmssl sm2exch -stage init
-		-exch_keyout sm2exch_alice_ra.pem -exch_pass P@ssw0rd
-		-out sm2exch_ra.bin
-	RESULT_VARIABLE TEST_RESULT
-	ERROR_VARIABLE TEST_STDERR
-)
-if(NOT ${TEST_RESULT} EQUAL 0)
-	message(FATAL_ERROR "stderr: ${TEST_STDERR}")
-endif()
-
-execute_process(
-	COMMAND bin/gmssl sm2exch -stage respond
-		-key sm2_peer.pem -pass P@ssw0rd -id Bob
-		-peer_pubkey sm2pub.pem -peer_id Alice -in sm2exch_ra.bin
-		-exch_keyout sm2exch_bob_rb.pem -exch_pass P@ssw0rd
-		-secret_state_out sm2exch_bob_secret_state.bin
-		-out sm2exch_rb_sb.bin
-	RESULT_VARIABLE TEST_RESULT
-	ERROR_VARIABLE TEST_STDERR
-)
-if(NOT ${TEST_RESULT} EQUAL 0)
-	message(FATAL_ERROR "stderr: ${TEST_STDERR}")
-endif()
-
-execute_process(
-	COMMAND bin/gmssl sm2exch -stage confirm
-		-key sm2.pem -pass P@ssw0rd -id Alice
-		-peer_pubkey sm2_peer_pub.pem -peer_id Bob
-		-exch_key sm2exch_alice_ra.pem -exch_pass P@ssw0rd
-		-in sm2exch_rb_sb.bin
-		-keylen 48 -keyout sm2exch_alice_key.bin
-		-secret_state_out sm2exch_alice_secret_state.bin
-		-out sm2exch_sa.bin
-	RESULT_VARIABLE TEST_RESULT
-	ERROR_VARIABLE TEST_STDERR
-)
-if(NOT ${TEST_RESULT} EQUAL 0)
-	message(FATAL_ERROR "stderr: ${TEST_STDERR}")
-endif()
-
-execute_process(
-	COMMAND bin/gmssl sm2exch -stage finish
-		-key sm2_peer.pem -pass P@ssw0rd -id Bob
-		-peer_pubkey sm2pub.pem -peer_id Alice
-		-exch_key sm2exch_bob_rb.pem -exch_pass P@ssw0rd
-		-secret_state sm2exch_bob_secret_state.bin
-		-in sm2exch_sa.bin
-		-keylen 48 -keyout sm2exch_bob_key.bin
-	RESULT_VARIABLE TEST_RESULT
-	ERROR_VARIABLE TEST_STDERR
-)
-if(NOT ${TEST_RESULT} EQUAL 0)
-	message(FATAL_ERROR "stderr: ${TEST_STDERR}")
-endif()
-
-execute_process(
-	COMMAND ${CMAKE_COMMAND} -E compare_files sm2exch_alice_key.bin sm2exch_bob_key.bin
-	RESULT_VARIABLE TEST_RESULT
-)
-if(NOT ${TEST_RESULT} EQUAL 0)
-	message(FATAL_ERROR "SM2 key exchange output mismatch")
-endif()
-
-execute_process(
-	COMMAND ${CMAKE_COMMAND} -E compare_files sm2exch_alice_secret_state.bin sm2exch_bob_secret_state.bin
-	RESULT_VARIABLE TEST_RESULT
-)
-if(NOT ${TEST_RESULT} EQUAL 0)
-	message(FATAL_ERROR "SM2 key exchange secret_state mismatch")
-endif()
-
-execute_process(
-	COMMAND bin/gmssl sm2exch -stage init -bin
-		-exch_keyout sm2exch_bin_alice_ra.pem -exch_pass P@ssw0rd
-		-out sm2exch_bin_ra.bin
-	RESULT_VARIABLE TEST_RESULT
-	ERROR_VARIABLE TEST_STDERR
-)
-if(NOT ${TEST_RESULT} EQUAL 0)
-	message(FATAL_ERROR "stderr: ${TEST_STDERR}")
-endif()
-
-execute_process(
-	COMMAND bin/gmssl sm2exch -stage respond -bin
-		-key sm2_peer.pem -pass P@ssw0rd -id Bob
-		-peer_pubkey sm2pub.pem -peer_id Alice -in sm2exch_bin_ra.bin
-		-exch_keyout sm2exch_bin_bob_rb.pem -exch_pass P@ssw0rd
-		-secret_state_out sm2exch_bin_bob_secret_state.bin
-		-out sm2exch_bin_rb_sb.bin
-	RESULT_VARIABLE TEST_RESULT
-	ERROR_VARIABLE TEST_STDERR
-)
-if(NOT ${TEST_RESULT} EQUAL 0)
-	message(FATAL_ERROR "stderr: ${TEST_STDERR}")
-endif()
-
-execute_process(
-	COMMAND bin/gmssl sm2exch -stage confirm -bin
-		-key sm2.pem -pass P@ssw0rd -id Alice
-		-peer_pubkey sm2_peer_pub.pem -peer_id Bob
-		-exch_key sm2exch_bin_alice_ra.pem -exch_pass P@ssw0rd
-		-in sm2exch_bin_rb_sb.bin
-		-keylen 48 -keyout sm2exch_bin_alice_key.bin
-		-secret_state_out sm2exch_bin_alice_secret_state.bin
-		-out sm2exch_bin_sa.bin
-	RESULT_VARIABLE TEST_RESULT
-	ERROR_VARIABLE TEST_STDERR
-)
-if(NOT ${TEST_RESULT} EQUAL 0)
-	message(FATAL_ERROR "stderr: ${TEST_STDERR}")
-endif()
-
-execute_process(
-	COMMAND bin/gmssl sm2exch -stage finish -bin
-		-key sm2_peer.pem -pass P@ssw0rd -id Bob
-		-peer_pubkey sm2pub.pem -peer_id Alice
-		-exch_key sm2exch_bin_bob_rb.pem -exch_pass P@ssw0rd
-		-secret_state sm2exch_bin_bob_secret_state.bin
-		-in sm2exch_bin_sa.bin
-		-keylen 48 -keyout sm2exch_bin_bob_key.bin
-	RESULT_VARIABLE TEST_RESULT
-	ERROR_VARIABLE TEST_STDERR
-)
-if(NOT ${TEST_RESULT} EQUAL 0)
-	message(FATAL_ERROR "stderr: ${TEST_STDERR}")
-endif()
-
-execute_process(
-	COMMAND ${CMAKE_COMMAND} -E compare_files sm2exch_bin_alice_key.bin sm2exch_bin_bob_key.bin
-	RESULT_VARIABLE TEST_RESULT
-)
-if(NOT ${TEST_RESULT} EQUAL 0)
-	message(FATAL_ERROR "SM2 binary key exchange output mismatch")
-endif()
-
-execute_process(
-	COMMAND bin/gmssl sm2exch -stage init
-		-exch_keyout sm2exch_stdio_alice_ra.pem -exch_pass P@ssw0rd
-	OUTPUT_FILE sm2exch_stdio_ra.hex
-	RESULT_VARIABLE TEST_RESULT
-	ERROR_VARIABLE TEST_STDERR
-)
-if(NOT ${TEST_RESULT} EQUAL 0)
-	message(FATAL_ERROR "stderr: ${TEST_STDERR}")
-endif()
-
-execute_process(
-	COMMAND bin/gmssl sm2exch -stage respond
-		-key sm2_peer.pem -pass P@ssw0rd -id Bob
-		-peer_pubkey sm2pub.pem -peer_id Alice
-		-exch_keyout sm2exch_stdio_bob_rb.pem -exch_pass P@ssw0rd
-		-secret_state_out sm2exch_stdio_bob_secret_state.hex
-	INPUT_FILE sm2exch_stdio_ra.hex
-	OUTPUT_FILE sm2exch_stdio_rb_sb.hex
-	RESULT_VARIABLE TEST_RESULT
-	ERROR_VARIABLE TEST_STDERR
-)
-if(NOT ${TEST_RESULT} EQUAL 0)
-	message(FATAL_ERROR "stderr: ${TEST_STDERR}")
-endif()
-
-execute_process(
-	COMMAND bin/gmssl sm2exch -stage confirm
-		-key sm2.pem -pass P@ssw0rd -id Alice
-		-peer_pubkey sm2_peer_pub.pem -peer_id Bob
-		-exch_key sm2exch_stdio_alice_ra.pem -exch_pass P@ssw0rd
-		-keylen 48 -keyout sm2exch_stdio_alice_key.hex
-		-secret_state_out sm2exch_stdio_alice_secret_state.hex
-	INPUT_FILE sm2exch_stdio_rb_sb.hex
-	OUTPUT_FILE sm2exch_stdio_sa.hex
-	RESULT_VARIABLE TEST_RESULT
-	ERROR_VARIABLE TEST_STDERR
-)
-if(NOT ${TEST_RESULT} EQUAL 0)
-	message(FATAL_ERROR "stderr: ${TEST_STDERR}")
-endif()
-
-execute_process(
-	COMMAND bin/gmssl sm2exch -stage finish
-		-key sm2_peer.pem -pass P@ssw0rd -id Bob
-		-peer_pubkey sm2pub.pem -peer_id Alice
-		-exch_key sm2exch_stdio_bob_rb.pem -exch_pass P@ssw0rd
-		-secret_state sm2exch_stdio_bob_secret_state.hex
-		-keylen 48 -keyout sm2exch_stdio_bob_key.hex
-	INPUT_FILE sm2exch_stdio_sa.hex
-	RESULT_VARIABLE TEST_RESULT
-	ERROR_VARIABLE TEST_STDERR
-)
-if(NOT ${TEST_RESULT} EQUAL 0)
-	message(FATAL_ERROR "stderr: ${TEST_STDERR}")
-endif()
-
-execute_process(
-	COMMAND ${CMAKE_COMMAND} -E compare_files sm2exch_stdio_alice_key.hex sm2exch_stdio_bob_key.hex
-	RESULT_VARIABLE TEST_RESULT
-)
-if(NOT ${TEST_RESULT} EQUAL 0)
-	message(FATAL_ERROR "SM2 stdio key exchange output mismatch")
-endif()
-
-execute_process(
-	COMMAND bin/gmssl sm2sign -key sm2.pem -pass P@ssw0rd -id Alice -in message.txt -out sm2_id.sig
-	RESULT_VARIABLE TEST_RESULT
-	ERROR_VARIABLE TEST_STDERR
-)
-if(NOT ${TEST_RESULT} EQUAL 0)
-	message(FATAL_ERROR "stderr: ${TEST_STDERR}")
-endif()
-
-execute_process(
-	COMMAND bin/gmssl sm2verify -pubkey sm2pub.pem -id Alice -in message.txt -sig sm2_id.sig
-	RESULT_VARIABLE TEST_RESULT
-	ERROR_VARIABLE TEST_STDERR
-	OUTPUT_VARIABLE TEST_STDOUT
-)
-if(NOT ${TEST_RESULT} EQUAL 0)
-	message(FATAL_ERROR "stderr: ${TEST_STDERR}")
-endif()
-string(FIND "${TEST_STDOUT}" "success" VERIFY_SUCCESS)
-if(VERIFY_SUCCESS EQUAL -1)
-    message(FATAL_ERROR "verify failure")
-endif()
diff --git a/cmake/tool_sm3.cmake b/cmake/tool_sm3.cmake
deleted file mode 100644
index 10e59a3d5..000000000
--- a/cmake/tool_sm3.cmake
+++ /dev/null
@@ -1,20 +0,0 @@
-include("${CMAKE_CURRENT_LIST_DIR}/tool_helpers.cmake")
-
-gmssl_expect_stdout("66c7f0f462eeedd9d1f2d46bdc10e4e24167c4875cf2f7a2297da02b8f4ba8e0\n"
-	sm3 -in_str abc)
-
-file(WRITE tool_sm3_input.txt "abc")
-gmssl_expect_stdout("66c7f0f462eeedd9d1f2d46bdc10e4e24167c4875cf2f7a2297da02b8f4ba8e0\n"
-	sm3 -hex -in tool_sm3_input.txt)
-gmssl_run(sm3 -bin -in tool_sm3_input.txt -out tool_sm3.bin)
-gmssl_expect_file_hex(tool_sm3.bin "66c7f0f462eeedd9d1f2d46bdc10e4e24167c4875cf2f7a2297da02b8f4ba8e0")
-
-gmssl_expect_stdout("28d8a61be67d8bf7652c4eda7092b612f88be62184f55005c57ddf076e764199\n"
-	sm3_hmac -key 0123456789abcdeffedcba9876543210 -in_str abc)
-gmssl_run(sm3_hmac -key 0123456789abcdeffedcba9876543210 -bin -in tool_sm3_input.txt -out tool_sm3_hmac.bin)
-gmssl_expect_file_hex(tool_sm3_hmac.bin "28d8a61be67d8bf7652c4eda7092b612f88be62184f55005c57ddf076e764199")
-
-gmssl_expect_stdout("df6b713d38d5a35df6861959e529ed22\n"
-	sm3_pbkdf2 -pass password -salt 0011223344556677 -iter 10000 -outlen 16 -hex)
-gmssl_run(sm3_pbkdf2 -pass password -salt 0011223344556677 -iter 10000 -outlen 16 -bin -out tool_sm3_pbkdf2.bin)
-gmssl_expect_file_hex(tool_sm3_pbkdf2.bin "df6b713d38d5a35df6861959e529ed22")
diff --git a/cmake/tool_sm4.cmake b/cmake/tool_sm4.cmake
deleted file mode 100644
index 93bac5639..000000000
--- a/cmake/tool_sm4.cmake
+++ /dev/null
@@ -1,79 +0,0 @@
-include("${CMAKE_CURRENT_LIST_DIR}/tool_helpers.cmake")
-
-set(SM4_KEY 0123456789abcdeffedcba9876543210)
-set(SM4_IV 00000000000000000000000000000000)
-set(SM4_XTS_KEY 0123456789abcdeffedcba987654321000112233445566778899aabbccddeeff)
-set(SM4_FF1_KEY 2b7e151628aed2a6abf7158809cf4f3c)
-set(SM4_TEXT "0123456789abcdef0123456789abcdef")
-
-function(gmssl_symmetric_roundtrip name)
-	file(WRITE "${name}.plain" "${SM4_TEXT}")
-	gmssl_run(${ARGN} -encrypt -in "${name}.plain" -out "${name}.cipher")
-	gmssl_run(${ARGN} -decrypt -in "${name}.cipher" -out "${name}.decrypt")
-	gmssl_files_equal("${name}.plain" "${name}.decrypt")
-endfunction()
-
-file(WRITE tool_sm4_cbc_kat.plain "0123456789abcdef")
-gmssl_run(sm4_cbc -encrypt -key ${SM4_KEY} -iv ${SM4_IV}
-	-in tool_sm4_cbc_kat.plain -out tool_sm4_cbc_kat.cipher)
-gmssl_expect_file_hex(tool_sm4_cbc_kat.cipher
-	"e6887b77dbabb572ffa07fed7548b192")
-gmssl_run(sm4_cbc -decrypt -key ${SM4_KEY} -iv ${SM4_IV}
-	-in tool_sm4_cbc_kat.cipher -out tool_sm4_cbc_kat.decrypt)
-gmssl_files_equal(tool_sm4_cbc_kat.plain tool_sm4_cbc_kat.decrypt)
-
-file(WRITE tool_sm4_cbc_short.plain "abc")
-gmssl_expect_fail(sm4_cbc -encrypt -key ${SM4_KEY} -iv ${SM4_IV}
-	-in tool_sm4_cbc_short.plain -out tool_sm4_cbc_short.cipher)
-gmssl_run(sm4_cbc -encrypt -pkcs7_padding -key ${SM4_KEY} -iv ${SM4_IV}
-	-in tool_sm4_cbc_short.plain -out tool_sm4_cbc_short_pkcs7.cipher)
-gmssl_run(sm4_cbc -decrypt -pkcs7_padding -key ${SM4_KEY} -iv ${SM4_IV}
-	-in tool_sm4_cbc_short_pkcs7.cipher -out tool_sm4_cbc_short_pkcs7.decrypt)
-gmssl_files_equal(tool_sm4_cbc_short.plain tool_sm4_cbc_short_pkcs7.decrypt)
-
-file(WRITE tool_sm4_cbc_pkcs7_kat.plain "0123456789abcdef")
-gmssl_run(sm4_cbc -encrypt -pkcs7_padding -key ${SM4_KEY} -iv ${SM4_IV}
-	-in tool_sm4_cbc_pkcs7_kat.plain -out tool_sm4_cbc_pkcs7_kat.cipher)
-gmssl_expect_file_hex(tool_sm4_cbc_pkcs7_kat.cipher
-	"e6887b77dbabb572ffa07fed7548b192ceaace11f2b90b94c2b7a4d9382e471e")
-gmssl_run(sm4_cbc -decrypt -pkcs7_padding -key ${SM4_KEY} -iv ${SM4_IV}
-	-in tool_sm4_cbc_pkcs7_kat.cipher -out tool_sm4_cbc_pkcs7_kat.decrypt)
-gmssl_files_equal(tool_sm4_cbc_pkcs7_kat.plain tool_sm4_cbc_pkcs7_kat.decrypt)
-
-gmssl_symmetric_roundtrip(tool_sm4_cbc sm4_cbc -key ${SM4_KEY} -iv ${SM4_IV})
-gmssl_symmetric_roundtrip(tool_sm4_cbc_pkcs7 sm4_cbc -pkcs7_padding -key ${SM4_KEY} -iv ${SM4_IV})
-gmssl_symmetric_roundtrip(tool_sm4_ctr sm4_ctr -key ${SM4_KEY} -iv ${SM4_IV})
-gmssl_symmetric_roundtrip(tool_sm4_gcm sm4_gcm -key ${SM4_KEY} -iv 000000000000000000000000 -aad_hex 001122 -taglen 16)
-
-if(ENABLE_SM4_ECB)
-	gmssl_symmetric_roundtrip(tool_sm4_ecb sm4_ecb -key ${SM4_KEY})
-endif()
-if(ENABLE_SM4_CFB)
-	gmssl_symmetric_roundtrip(tool_sm4_cfb sm4_cfb -sbytes 16 -key ${SM4_KEY} -iv ${SM4_IV})
-endif()
-if(ENABLE_SM4_OFB)
-	gmssl_symmetric_roundtrip(tool_sm4_ofb sm4_ofb -key ${SM4_KEY} -iv ${SM4_IV})
-endif()
-if(ENABLE_SM4_CCM)
-	gmssl_symmetric_roundtrip(tool_sm4_ccm sm4_ccm -key ${SM4_KEY} -iv 000000000000000000000000 -aad_hex 001122 -taglen 16)
-endif()
-if(ENABLE_SM4_XTS)
-	file(WRITE tool_sm4_xts.plain "0123456789abcdef0123456789abcdef")
-	gmssl_run(sm4_xts -encrypt -key ${SM4_XTS_KEY} -iv ${SM4_IV} -data_unit_size 32
-		-in tool_sm4_xts.plain -out tool_sm4_xts.cipher)
-	gmssl_run(sm4_xts -decrypt -key ${SM4_XTS_KEY} -iv ${SM4_IV} -data_unit_size 32
-		-in tool_sm4_xts.cipher -out tool_sm4_xts.decrypt)
-	gmssl_files_equal(tool_sm4_xts.plain tool_sm4_xts.decrypt)
-endif()
-if(ENABLE_SM4_FF1)
-	gmssl_expect_stdout("2326982895499381\n"
-		sm4_ff1 -encrypt -key ${SM4_FF1_KEY} -tweak 39383736353433323130 -digits 6226090102675688)
-	gmssl_expect_stdout("6226090102675688\n"
-		sm4_ff1 -decrypt -key ${SM4_FF1_KEY} -tweak 39383736353433323130 -digits 2326982895499381)
-endif()
-if(ENABLE_SM4_CBC_MAC)
-	gmssl_expect_stdout("9054fccff72871fdad5202c821dbea05\n"
-		sm4_cbc_mac -key ${SM4_KEY} -in_str abc)
-	gmssl_run(sm4_cbc_mac -key ${SM4_KEY} -bin -in_str abc -out tool_sm4_cbc_mac.bin)
-	gmssl_expect_file_hex(tool_sm4_cbc_mac.bin "9054fccff72871fdad5202c821dbea05")
-endif()
diff --git a/cmake/tool_sm9.cmake b/cmake/tool_sm9.cmake
deleted file mode 100644
index 9076f50b3..000000000
--- a/cmake/tool_sm9.cmake
+++ /dev/null
@@ -1,142 +0,0 @@
-include("${CMAKE_CURRENT_LIST_DIR}/tool_helpers.cmake")
-
-set(SM9_PASS P@ssw0rd)
-set(SM9_USER_PASS 123456)
-set(SM9_ID Alice)
-set(SM9_TEXT "SM9 command line test message")
-
-file(WRITE tool_sm9_message.txt "${SM9_TEXT}")
-
-gmssl_run(sm9setup -alg sm9sign -pass ${SM9_PASS}
-	-out tool_sm9_sign_msk.pem -pubout tool_sm9_sign_mpk.pem)
-gmssl_run(sm9keygen -alg sm9sign
-	-in tool_sm9_sign_msk.pem -inpass ${SM9_PASS}
-	-id ${SM9_ID}
-	-out tool_sm9_sign_key.pem -outpass ${SM9_USER_PASS})
-gmssl_run(sm9sign -key tool_sm9_sign_key.pem -pass ${SM9_USER_PASS}
-	-in tool_sm9_message.txt -out tool_sm9.sig)
-gmssl_run(sm9verify -pubmaster tool_sm9_sign_mpk.pem -id ${SM9_ID}
-	-in tool_sm9_message.txt -sig tool_sm9.sig)
-
-gmssl_run(sm9setup -alg sm9encrypt -pass ${SM9_PASS}
-	-out tool_sm9_enc_msk.pem -pubout tool_sm9_enc_mpk.pem)
-gmssl_run(sm9keygen -alg sm9encrypt
-	-in tool_sm9_enc_msk.pem -inpass ${SM9_PASS}
-	-id ${SM9_ID}
-	-out tool_sm9_enc_key.pem -outpass ${SM9_USER_PASS})
-gmssl_run(sm9keygen -alg sm9keyagreement
-	-in tool_sm9_enc_msk.pem -inpass ${SM9_PASS}
-	-id Bob
-	-out tool_sm9_bob_exch_key.pem -outpass ${SM9_USER_PASS})
-gmssl_run(sm9encrypt -pubmaster tool_sm9_enc_mpk.pem -id ${SM9_ID}
-	-in tool_sm9_message.txt -out tool_sm9_cipher.der)
-gmssl_run(sm9decrypt -key tool_sm9_enc_key.pem -pass ${SM9_USER_PASS} -id ${SM9_ID}
-	-in tool_sm9_cipher.der -out tool_sm9_plain.txt)
-gmssl_expect_file_text(tool_sm9_plain.txt "${SM9_TEXT}")
-
-gmssl_run(sm9keygen -alg sm9keyagreement
-	-in tool_sm9_enc_msk.pem -inpass ${SM9_PASS}
-	-id ${SM9_ID}
-	-out tool_sm9_alice_exch_key.pem -outpass ${SM9_USER_PASS})
-gmssl_run(sm9exch -stage init
-	-pubmaster tool_sm9_enc_mpk.pem -peer_id Bob
-	-exch_keyout tool_sm9_alice_ra.pem -out tool_sm9_ra.bin)
-gmssl_run(sm9exch -stage respond
-	-pubmaster tool_sm9_enc_mpk.pem
-	-key tool_sm9_bob_exch_key.pem -pass ${SM9_USER_PASS} -id Bob
-	-peer_id ${SM9_ID} -in tool_sm9_ra.bin
-	-exch_keyout tool_sm9_bob_rb.pem -out tool_sm9_rb_sb.bin)
-gmssl_run(sm9exch -stage confirm
-	-pubmaster tool_sm9_enc_mpk.pem
-	-key tool_sm9_alice_exch_key.pem -pass ${SM9_USER_PASS} -id ${SM9_ID}
-	-peer_id Bob -exch_key tool_sm9_alice_ra.pem
-	-in tool_sm9_rb_sb.bin
-	-keylen 48 -keyout tool_sm9_alice_shared_key.bin
-	-out tool_sm9_sa.bin)
-gmssl_run(sm9exch -stage finish
-	-pubmaster tool_sm9_enc_mpk.pem
-	-key tool_sm9_bob_exch_key.pem -pass ${SM9_USER_PASS} -id Bob
-	-peer_id ${SM9_ID}
-	-exch_key tool_sm9_bob_rb.pem
-	-in tool_sm9_sa.bin
-	-keylen 48 -keyout tool_sm9_bob_shared_key.bin)
-gmssl_files_equal(tool_sm9_alice_shared_key.bin tool_sm9_bob_shared_key.bin)
-
-gmssl_run(sm9exch -stage init -bin
-	-pubmaster tool_sm9_enc_mpk.pem -peer_id Bob
-	-exch_keyout tool_sm9_bin_alice_ra.pem -out tool_sm9_bin_ra.bin)
-gmssl_run(sm9exch -stage respond -bin
-	-pubmaster tool_sm9_enc_mpk.pem
-	-key tool_sm9_bob_exch_key.pem -pass ${SM9_USER_PASS} -id Bob
-	-peer_id ${SM9_ID} -in tool_sm9_bin_ra.bin
-	-exch_keyout tool_sm9_bin_bob_rb.pem -out tool_sm9_bin_rb_sb.bin)
-gmssl_run(sm9exch -stage confirm -bin
-	-pubmaster tool_sm9_enc_mpk.pem
-	-key tool_sm9_alice_exch_key.pem -pass ${SM9_USER_PASS} -id ${SM9_ID}
-	-peer_id Bob -exch_key tool_sm9_bin_alice_ra.pem
-	-in tool_sm9_bin_rb_sb.bin
-	-keylen 48 -keyout tool_sm9_bin_alice_shared_key.bin
-	-out tool_sm9_bin_sa.bin)
-gmssl_run(sm9exch -stage finish -bin
-	-pubmaster tool_sm9_enc_mpk.pem
-	-key tool_sm9_bob_exch_key.pem -pass ${SM9_USER_PASS} -id Bob
-	-peer_id ${SM9_ID}
-	-exch_key tool_sm9_bin_bob_rb.pem
-	-in tool_sm9_bin_sa.bin
-	-keylen 48 -keyout tool_sm9_bin_bob_shared_key.bin)
-gmssl_files_equal(tool_sm9_bin_alice_shared_key.bin tool_sm9_bin_bob_shared_key.bin)
-
-execute_process(
-	COMMAND ${GMSSL_BIN} sm9exch -stage init
-		-pubmaster tool_sm9_enc_mpk.pem -peer_id Bob
-		-exch_keyout tool_sm9_stdio_alice_ra.pem
-	OUTPUT_FILE tool_sm9_stdio_ra.hex
-	RESULT_VARIABLE TEST_RESULT
-	ERROR_VARIABLE TEST_STDERR
-)
-if(NOT TEST_RESULT EQUAL 0)
-	message(FATAL_ERROR "command failed: ${GMSSL_BIN} sm9exch -stage init\nstderr: ${TEST_STDERR}")
-endif()
-execute_process(
-	COMMAND ${GMSSL_BIN} sm9exch -stage respond
-		-pubmaster tool_sm9_enc_mpk.pem
-		-key tool_sm9_bob_exch_key.pem -pass ${SM9_USER_PASS} -id Bob
-		-peer_id ${SM9_ID}
-		-exch_keyout tool_sm9_stdio_bob_rb.pem
-	INPUT_FILE tool_sm9_stdio_ra.hex
-	OUTPUT_FILE tool_sm9_stdio_rb_sb.hex
-	RESULT_VARIABLE TEST_RESULT
-	ERROR_VARIABLE TEST_STDERR
-)
-if(NOT TEST_RESULT EQUAL 0)
-	message(FATAL_ERROR "command failed: ${GMSSL_BIN} sm9exch -stage respond\nstderr: ${TEST_STDERR}")
-endif()
-execute_process(
-	COMMAND ${GMSSL_BIN} sm9exch -stage confirm
-		-pubmaster tool_sm9_enc_mpk.pem
-		-key tool_sm9_alice_exch_key.pem -pass ${SM9_USER_PASS} -id ${SM9_ID}
-		-peer_id Bob -exch_key tool_sm9_stdio_alice_ra.pem
-		-keylen 48 -keyout tool_sm9_stdio_alice_shared_key.hex
-	INPUT_FILE tool_sm9_stdio_rb_sb.hex
-	OUTPUT_FILE tool_sm9_stdio_sa.hex
-	RESULT_VARIABLE TEST_RESULT
-	ERROR_VARIABLE TEST_STDERR
-)
-if(NOT TEST_RESULT EQUAL 0)
-	message(FATAL_ERROR "command failed: ${GMSSL_BIN} sm9exch -stage confirm\nstderr: ${TEST_STDERR}")
-endif()
-execute_process(
-	COMMAND ${GMSSL_BIN} sm9exch -stage finish
-		-pubmaster tool_sm9_enc_mpk.pem
-		-key tool_sm9_bob_exch_key.pem -pass ${SM9_USER_PASS} -id Bob
-		-peer_id ${SM9_ID}
-		-exch_key tool_sm9_stdio_bob_rb.pem
-		-keylen 48 -keyout tool_sm9_stdio_bob_shared_key.hex
-	INPUT_FILE tool_sm9_stdio_sa.hex
-	RESULT_VARIABLE TEST_RESULT
-	ERROR_VARIABLE TEST_STDERR
-)
-if(NOT TEST_RESULT EQUAL 0)
-	message(FATAL_ERROR "command failed: ${GMSSL_BIN} sm9exch -stage finish\nstderr: ${TEST_STDERR}")
-endif()
-gmssl_files_equal(tool_sm9_stdio_alice_shared_key.hex tool_sm9_stdio_bob_shared_key.hex)
diff --git a/cmake/tool_zuc.cmake b/cmake/tool_zuc.cmake
deleted file mode 100644
index 10cecb9b1..000000000
--- a/cmake/tool_zuc.cmake
+++ /dev/null
@@ -1,32 +0,0 @@
-include("${CMAKE_CURRENT_LIST_DIR}/tool_helpers.cmake")
-
-set(ZUC_KEY 00000000000000000000000000000000)
-set(ZUC_IV 00000000000000000000000000000000)
-file(WRITE tool_zuc.plain "0123456789abcdef")
-
-gmssl_run(zuc -key ${ZUC_KEY} -iv ${ZUC_IV} -in tool_zuc.plain -out tool_zuc.cipher)
-gmssl_expect_file_hex(tool_zuc.cipher "178fec4735b5b4edbfed84d4fc7cda00")
-gmssl_run(zuc -key ${ZUC_KEY} -iv ${ZUC_IV} -in tool_zuc.cipher -out tool_zuc.decrypt)
-gmssl_files_equal(tool_zuc.plain tool_zuc.decrypt)
-
-gmssl_run(zuc_128_eea3
-	-key 173d14ba5003731d7a60049470f00a29
-	-count 0x66035492 -bearer 15 -direction 0
-	-in tool_zuc.plain
-	-out tool_zuc_128_eea3.cipher)
-gmssl_expect_file_hex(tool_zuc_128_eea3.cipher
-	"fa0f3eb52d9be1af9e521680d313c40c")
-gmssl_expect_stdout("b0361765\n" zuc_128_eia3
-	-key 00000000000000000000000000000000
-	-count 0 -bearer 0 -direction 0 -in tool_zuc.plain)
-
-gmssl_run(zuc256
-	-key 0000000000000000000000000000000000000000000000000000000000000000
-	-iv 0000000000000000000000000000000000000000000000
-	-in tool_zuc.plain -out tool_zuc256.cipher)
-gmssl_expect_file_hex(tool_zuc256.cipher "68e108e51a361ad5e2c509585ad9ae65")
-gmssl_run(zuc256
-	-key 0000000000000000000000000000000000000000000000000000000000000000
-	-iv 0000000000000000000000000000000000000000000000
-	-in tool_zuc256.cipher -out tool_zuc256.decrypt)
-gmssl_files_equal(tool_zuc.plain tool_zuc256.decrypt)
diff --git a/config b/config
new file mode 100755
index 000000000..1341fd622
--- /dev/null
+++ b/config
@@ -0,0 +1,924 @@
+#!/bin/sh
+# Copyright 1998-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+# OpenSSL config: determine the operating system and run ./Configure
+# Derived from minarch and GuessOS from Apache.
+#
+# Do "config -h" for usage information.
+SUFFIX=""
+DRYRUN="false"
+VERBOSE="false"
+EXE=""
+THERE=`dirname $0`
+
+# pick up any command line args to config
+for i
+do
+case "$i" in 
+-d*) options=$options" --debug";;
+-t*) DRYRUN="true" VERBOSE="true";;
+-v*) VERBOSE="true";;
+-h*) DRYRUN="true"; cat </dev/null` || MACHINE="unknown"
+[ "$RELEASE" ] || RELEASE=`(uname -r) 2>/dev/null` || RELEASE="unknown"
+[ "$SYSTEM" ] || SYSTEM=`(uname -s) 2>/dev/null`  || SYSTEM="unknown"
+[ "$BUILD" ] || VERSION=`(uname -v) 2>/dev/null` || VERSION="unknown"
+
+
+# Now test for ISC and SCO, since it is has a braindamaged uname.
+#
+# We need to work around FreeBSD 1.1.5.1 
+(
+XREL=`uname -X 2>/dev/null | grep "^Release" | awk '{print $3}'`
+if [ "x$XREL" != "x" ]; then
+    if [ -f /etc/kconfig ]; then
+	case "$XREL" in
+	    4.0|4.1)
+		    echo "${MACHINE}-whatever-isc4"; exit 0
+		;;
+	esac
+    else
+	case "$XREL" in
+	    3.2v4.2)
+		echo "whatever-whatever-sco3"; exit 0
+		;;
+	    3.2v5.0*)
+		echo "whatever-whatever-sco5"; exit 0
+		;;
+	    4.2MP)
+		case "x${VERSION}" in
+		    x2.0*) echo "whatever-whatever-unixware20"; exit 0 ;;
+		    x2.1*) echo "whatever-whatever-unixware21"; exit 0 ;;
+		    x2*)   echo "whatever-whatever-unixware2";  exit 0 ;;
+		esac
+		;;
+	    4.2)
+		echo "whatever-whatever-unixware1"; exit 0
+		;;
+	    5*)
+		case "x${VERSION}" in
+		    # We hardcode i586 in place of ${MACHINE} for the
+		    # following reason. The catch is that even though Pentium
+		    # is minimum requirement for platforms in question,
+		    # ${MACHINE} gets always assigned to i386. Now, problem
+		    # with i386 is that it makes ./config pass 386 to
+		    # ./Configure, which in turn makes make generate
+		    # inefficient SHA-1 (for this moment) code.
+		    x[678]*)  echo "i586-sco-unixware7"; exit 0 ;;
+		esac
+		;;
+	esac
+    fi
+fi
+# Now we simply scan though... In most cases, the SYSTEM info is enough
+#
+case "${SYSTEM}:${RELEASE}:${VERSION}:${MACHINE}" in
+    A/UX:*)
+	echo "m68k-apple-aux3"; exit 0
+	;;
+
+    AIX:[3-9]:4:*)
+	echo "${MACHINE}-ibm-aix"; exit 0
+	;;
+
+    AIX:*:[5-9]:*)
+	echo "${MACHINE}-ibm-aix"; exit 0
+	;;
+
+    AIX:*)
+	echo "${MACHINE}-ibm-aix3"; exit 0
+	;;
+
+    HI-UX:*)
+	echo "${MACHINE}-hi-hiux"; exit 0
+	;;
+
+    HP-UX:*)
+	HPUXVER=`echo ${RELEASE}|sed -e 's/[^.]*.[0B]*//'`
+	case "$HPUXVER" in
+	    1[0-9].*)	# HPUX 10 and 11 targets are unified
+		echo "${MACHINE}-hp-hpux1x"; exit 0
+		;;
+	    *)
+		echo "${MACHINE}-hp-hpux"; exit 0
+		;;
+	esac
+	;;
+
+    IRIX:6.*)
+	echo "mips3-sgi-irix"; exit 0
+	;;
+
+    IRIX64:*)
+	echo "mips4-sgi-irix64"; exit 0
+	;;
+
+    Linux:[2-9].*)
+	echo "${MACHINE}-whatever-linux2"; exit 0
+	;;
+
+    Linux:1.*)
+	echo "${MACHINE}-whatever-linux1"; exit 0
+	;;
+
+    GNU*)
+	echo "hurd-x86"; exit 0;
+	;;
+
+    LynxOS:*)
+	echo "${MACHINE}-lynx-lynxos"; exit 0
+	;;
+
+    BSD/OS:4.*)  # BSD/OS always says 386
+	echo "i486-whatever-bsdi4"; exit 0
+	;;
+
+    BSD/386:*:*:*486*|BSD/OS:*:*:*:*486*)
+        case `/sbin/sysctl -n hw.model` in
+	    Pentium*)
+                echo "i586-whatever-bsdi"; exit 0
+                ;;
+            *)
+                echo "i386-whatever-bsdi"; exit 0
+                ;;
+            esac;
+	;;
+
+    BSD/386:*|BSD/OS:*)
+	echo "${MACHINE}-whatever-bsdi"; exit 0
+	;;
+
+    FreeBSD:*:*:*386*)
+        VERS=`echo ${RELEASE} | sed -e 's/[-(].*//'`
+        MACH=`sysctl -n hw.model`
+        ARCH='whatever'
+        case ${MACH} in
+           *386*       ) MACH="i386"     ;;
+           *486*       ) MACH="i486"     ;;
+           Pentium\ II*) MACH="i686"     ;;
+           Pentium*    ) MACH="i586"     ;;
+           *           ) MACH="$MACHINE" ;;
+        esac
+        case ${MACH} in
+           i[0-9]86 ) ARCH="pc" ;;
+        esac
+        echo "${MACH}-${ARCH}-freebsd${VERS}"; exit 0
+        ;;
+
+    FreeBSD:*)
+	echo "${MACHINE}-whatever-freebsd"; exit 0
+	;;
+
+    Haiku:*)
+	echo "${MACHINE}-whatever-haiku"; exit 0
+	;;
+
+    NetBSD:*:*:*386*)
+        echo "`(/usr/sbin/sysctl -n hw.model || /sbin/sysctl -n hw.model) | sed 's,.*\(.\)86-class.*,i\186,'`-whatever-netbsd"; exit 0
+	;;
+
+    NetBSD:*)
+	echo "${MACHINE}-whatever-netbsd"; exit 0
+	;;
+
+    OpenBSD:*)
+	echo "${MACHINE}-whatever-openbsd"; exit 0
+	;;
+
+    OpenUNIX:*)
+	echo "${MACHINE}-unknown-OpenUNIX${VERSION}"; exit 0
+	;;
+
+    OSF1:*:*:*alpha*)
+	OSFMAJOR=`echo ${RELEASE}| sed -e 's/^V\([0-9]*\)\..*$/\1/'`
+	case "$OSFMAJOR" in
+	    4|5)
+		echo "${MACHINE}-dec-tru64"; exit 0
+		;;
+	    1|2|3)
+		echo "${MACHINE}-dec-osf"; exit 0
+		;;
+	    *)
+		echo "${MACHINE}-dec-osf"; exit 0
+		;;
+	esac
+	;;
+
+    QNX:*)
+	case "$RELEASE" in
+	    4*)
+		echo "${MACHINE}-whatever-qnx4"
+		;;
+	    6*)
+		echo "${MACHINE}-whatever-qnx6"
+		;;
+	    *)
+		echo "${MACHINE}-whatever-qnx"
+		;;
+	esac
+	exit 0
+	;;
+
+    Paragon*:*:*:*)
+	echo "i860-intel-osf1"; exit 0
+	;;
+
+    Rhapsody:*)
+	echo "ppc-apple-rhapsody"; exit 0
+	;;
+
+    Darwin:*)
+	case "$MACHINE" in
+	    Power*)
+		echo "ppc-apple-darwin${VERSION}"
+		;;
+	    x86_64)
+		echo "x86_64-apple-darwin${VERSION}"
+		;;
+	    *)
+		echo "i686-apple-darwin${VERSION}"
+		;;
+	esac
+	exit 0
+	;;
+
+    SunOS:5.*)
+	echo "${MACHINE}-whatever-solaris2"; exit 0
+	;;
+
+    SunOS:*)
+	echo "${MACHINE}-sun-sunos4"; exit 0
+	;;
+
+    UNIX_System_V:4.*:*)
+	echo "${MACHINE}-whatever-sysv4"; exit 0
+	;;
+
+    VOS:*:*:i786)
+     echo "i386-stratus-vos"; exit 0
+     ;;
+
+    VOS:*:*:*)
+     echo "hppa1.1-stratus-vos"; exit 0
+     ;;
+
+    *:4*:R4*:m88k)
+	echo "${MACHINE}-whatever-sysv4"; exit 0
+	;;
+
+    DYNIX/ptx:4*:*)
+	echo "${MACHINE}-whatever-sysv4"; exit 0
+	;;
+
+    *:4.0:3.0:3[34]?? | *:4.0:3.0:3[34]??,*)
+	echo "i486-ncr-sysv4"; exit 0
+	;;
+
+    ULTRIX:*)
+	echo "${MACHINE}-unknown-ultrix"; exit 0
+	;;
+
+    POSIX-BC*)
+	echo "${MACHINE}-siemens-sysv4"; exit 0   # Here, $MACHINE == "BS2000"
+	;;
+
+    machten:*)
+       echo "${MACHINE}-tenon-${SYSTEM}"; exit 0;
+       ;;
+
+    library:*)
+	echo "${MACHINE}-ncr-sysv4"; exit 0
+	;;
+
+    ConvexOS:*:11.0:*)
+	echo "${MACHINE}-v11-${SYSTEM}"; exit 0;
+	;;
+
+    MINGW*)
+	echo "${MACHINE}-whatever-mingw"; exit 0;
+	;;
+    CYGWIN*)
+	echo "${MACHINE}-pc-cygwin"; exit 0
+	;;
+
+    vxworks*)
+       echo "${MACHINE}-whatever-vxworks"; exit 0;
+       ;;
+esac
+
+#
+# Ugg. These are all we can determine by what we know about
+# the output of uname. Be more creative:
+#
+
+# Do the Apollo stuff first. Here, we just simply assume
+# that the existence of the /usr/apollo directory is proof
+# enough
+if [ -d /usr/apollo ]; then
+    echo "whatever-apollo-whatever"
+    exit 0
+fi
+
+# Now NeXT
+ISNEXT=`hostinfo 2>/dev/null`
+case "$ISNEXT" in
+    *'NeXT Mach 3.3'*)
+	echo "whatever-next-nextstep3.3"; exit 0
+	;;
+    *NeXT*)
+	echo "whatever-next-nextstep"; exit 0
+	;;
+esac
+
+# At this point we gone through all the one's
+# we know of: Punt
+
+echo "${MACHINE}-whatever-${SYSTEM}" 
+exit 0
+) 2>/dev/null | (
+
+# ---------------------------------------------------------------------------
+# this is where the translation occurs into SSLeay terms
+# ---------------------------------------------------------------------------
+
+# Only set CC if not supplied already
+if [ -z "$CROSS_COMPILE$CC" ]; then
+  GCCVER=`sh -c "gcc -dumpversion" 2>/dev/null`
+  if [ "$GCCVER" != "" ]; then
+    # then strip off whatever prefix egcs prepends the number with...
+    # Hopefully, this will work for any future prefixes as well.
+    GCCVER=`echo $GCCVER | LC_ALL=C sed 's/^[a-zA-Z]*\-//'`
+    # Since gcc 3.1 gcc --version behaviour has changed.  gcc -dumpversion
+    # does give us what we want though, so we use that.  We just just the
+    # major and minor version numbers.
+    # peak single digit before and after first dot, e.g. 2.95.1 gives 29
+    GCCVER=`echo $GCCVER | sed 's/\([0-9]\)\.\([0-9]\).*/\1\2/'`
+    CC=gcc
+  else
+    CC=cc
+  fi
+fi
+GCCVER=${GCCVER:-0}
+if [ "$SYSTEM" = "HP-UX" ];then
+  # By default gcc is a ILP32 compiler (with long long == 64).
+  GCC_BITS="32"
+  if [ $GCCVER -ge 30 ]; then
+    # PA64 support only came in with gcc 3.0.x.
+    # We check if the preprocessor symbol __LP64__ is defined...
+    if echo "__LP64__" | gcc -v -E -x c - 2>/dev/null | grep "^__LP64__" 2>&1 > /dev/null; then
+      : # __LP64__ has slipped through, it therefore is not defined
+    else
+      GCC_BITS="64"
+    fi
+  fi
+fi
+if [ "$SYSTEM" = "SunOS" ]; then
+  if [ $GCCVER -ge 30 ]; then
+    # 64-bit ABI isn't officially supported in gcc 3.0, but it appears
+    # to be working, at the very least 'make test' passes...
+    if gcc -v -E -x c /dev/null 2>&1 | grep __arch64__ > /dev/null; then
+      GCC_ARCH="-m64"
+    else
+      GCC_ARCH="-m32"
+    fi
+  fi
+  # check for WorkShop C, expected output is "cc: blah-blah C x.x"
+  CCVER=`(cc -V 2>&1) 2>/dev/null | \
+  	egrep -e '^cc: .* C [0-9]\.[0-9]' | \
+	sed 's/.* C \([0-9]\)\.\([0-9]\).*/\1\2/'`
+  CCVER=${CCVER:-0}
+  if [ $MACHINE != i86pc -a $CCVER -gt 40 ]; then
+    CC=cc	# overrides gcc!!!
+    if [ $CCVER -eq 50 ]; then
+      echo "WARNING! Detected WorkShop C 5.0. Do make sure you have"
+      echo "         patch #107357-01 or later applied."
+      sleep 5
+    fi
+  fi
+fi
+
+if [ "${SYSTEM}" = "AIX" ]; then	# favor vendor cc over gcc
+    (cc) 2>&1 | grep -iv "not found" > /dev/null && CC=cc
+fi
+
+CCVER=${CCVER:-0}
+
+# read the output of the embedded GuessOS 
+read GUESSOS
+
+echo Operating system: $GUESSOS
+
+# now map the output into SSLeay terms ... really should hack into the
+# script above so we end up with values in vars but that would take
+# more time that I want to waste at the moment
+case "$GUESSOS" in
+  uClinux*64*)
+    OUT=uClinux-dist64
+	;;
+  uClinux*)
+    OUT=uClinux-dist
+	;;
+  mips3-sgi-irix)
+	#CPU=`(hinv -t cpu) 2>/dev/null | head -1 | sed 's/^CPU:[^R]*R\([0-9]*\).*/\1/'`
+	#CPU=${CPU:-0}
+	#if [ $CPU -ge 5000 ]; then
+	#	options="$options -mips4"
+	#else
+	#	options="$options -mips3"
+	#fi
+	OUT="irix-mips3-$CC"
+	;;
+  mips4-sgi-irix64)
+	echo "WARNING! If you wish to build 64-bit library, then you have to"
+	echo "         invoke '$THERE/Configure irix64-mips4-$CC' *manually*."
+	if [ "$DRYRUN" = "false" -a -t 1 ]; then
+	  echo "         You have about 5 seconds to press Ctrl-C to abort."
+	  (trap "stty `stty -g`; exit 0" 2 0; stty -icanon min 0 time 50; read waste) <&1
+	fi
+        #CPU=`(hinv -t cpu) 2>/dev/null | head -1 | sed 's/^CPU:[^R]*R\([0-9]*\).*/\1/'`
+        #CPU=${CPU:-0}
+        #if [ $CPU -ge 5000 ]; then
+        #        options="$options -mips4"
+        #else
+        #        options="$options -mips3"
+        #fi
+	OUT="irix-mips3-$CC"
+	;;
+  ppc-apple-rhapsody) OUT="rhapsody-ppc-cc" ;;
+  ppc-apple-darwin*)
+	ISA64=`(sysctl -n hw.optional.64bitops) 2>/dev/null`
+	if [ "$ISA64" = "1" -a -z "$KERNEL_BITS" ]; then
+	    echo "WARNING! If you wish to build 64-bit library, then you have to"
+	    echo "         invoke '$THERE/Configure darwin64-ppc-cc' *manually*."
+	    if [ "$DRYRUN" = "false" -a -t 1 ]; then
+	      echo "         You have about 5 seconds to press Ctrl-C to abort."
+	      (trap "stty `stty -g`; exit 0" 2 0; stty -icanon min 0 time 50; read waste) <&1
+	    fi
+	fi
+	if [ "$ISA64" = "1" -a "$KERNEL_BITS" = "64" ]; then
+	    OUT="darwin64-ppc-cc"
+	else
+	    OUT="darwin-ppc-cc"
+	fi ;;
+  i?86-apple-darwin*)
+	ISA64=`(sysctl -n hw.optional.x86_64) 2>/dev/null`
+	if [ "$ISA64" = "1" -a -z "$KERNEL_BITS" ]; then
+	    echo "WARNING! If you wish to build 64-bit library, then you have to"
+	    echo "         invoke 'KERNEL_BITS=64 $THERE/config $options'."
+	    if [ "$DRYRUN" = "false" -a -t 1 ]; then
+	      echo "         You have about 5 seconds to press Ctrl-C to abort."
+	      # The stty technique used elsewhere doesn't work on
+	      # MacOS. At least, right now on this Mac.
+	      sleep 5
+	    fi
+	fi
+	if [ "$ISA64" = "1" -a "$KERNEL_BITS" = "64" ]; then
+	    OUT="darwin64-x86_64-cc"
+	else
+	    OUT="darwin-i386-cc"
+	fi ;;
+  x86_64-apple-darwin*)
+	if [ -z "$KERNEL_BITS" ]; then
+	    echo "WARNING! If you wish to build 32-bit library, then you have to"
+	    echo "         invoke 'KERNEL_BITS=32 $THERE/config $options'."
+	    if [ "$DRYRUN" = "false" -a -t 1 ]; then
+	      echo "         You have about 5 seconds to press Ctrl-C to abort."
+	      # The stty technique used elsewhere doesn't work on
+	      # MacOS. At least, right now on this Mac.
+	      sleep 5
+	    fi
+	fi
+	if [ "$KERNEL_BITS" = "32" ]; then
+	    OUT="darwin-i386-cc"
+	else
+	    OUT="darwin64-x86_64-cc"
+	fi ;;
+  armv6+7-*-iphoneos)
+	options="$options -arch%20armv6 -arch%20armv7"
+	OUT="iphoneos-cross" ;;
+  *-*-iphoneos)
+	options="$options -arch%20${MACHINE}"
+	OUT="iphoneos-cross" ;;
+  arm64-*-iphoneos|*-*-ios64)
+	OUT="ios64-cross" ;;
+  alpha-*-linux2)
+        ISA=`awk '/cpu model/{print$4;exit(0);}' /proc/cpuinfo`
+	case ${ISA:-generic} in
+	*[678])	OUT="linux-alpha+bwx-$CC" ;;
+	*)	OUT="linux-alpha-$CC" ;;
+	esac
+	if [ "$CC" = "gcc" ]; then
+	    case ${ISA:-generic} in
+	    EV5|EV45)		options="$options -mcpu=ev5";;
+	    EV56|PCA56)		options="$options -mcpu=ev56";;
+	    *)			options="$options -mcpu=ev6";;
+	    esac
+	fi
+	;;
+  ppc64-*-linux2)
+	if [ -z "$KERNEL_BITS" ]; then
+	    echo "WARNING! If you wish to build 64-bit library, then you have to"
+	    echo "         invoke '$THERE/Configure linux-ppc64' *manually*."
+	    if [ "$DRYRUN" = "false" -a -t 1 ]; then
+		echo "         You have about 5 seconds to press Ctrl-C to abort."
+		(trap "stty `stty -g`; exit 0" 2 0; stty -icanon min 0 time 50; read waste) <&1
+	    fi
+	fi
+	if [ "$KERNEL_BITS" = "64" ]; then
+	    OUT="linux-ppc64"
+	else
+	    OUT="linux-ppc"
+	    (echo "__LP64__" | gcc -E -x c - 2>/dev/null | grep "^__LP64__" 2>&1 > /dev/null) || options="$options -m32"
+	fi
+	;;
+  ppc64le-*-linux2) OUT="linux-ppc64le" ;;
+  ppc-*-linux2) OUT="linux-ppc" ;;
+  mips64*-*-linux2)
+	echo "WARNING! If you wish to build 64-bit library, then you have to"
+	echo "         invoke '$THERE/Configure linux64-mips64' *manually*."
+	if [ "$DRYRUN" = "false" -a -t 1 ]; then
+	    echo "         You have about 5 seconds to press Ctrl-C to abort."
+	    (trap "stty `stty -g`; exit 0" 2 0; stty -icanon min 0 time 50; read waste) <&1
+	fi
+	OUT="linux-mips64"
+	;;
+  mips*-*-linux2) OUT="linux-mips32" ;;
+  ppc60x-*-vxworks*) OUT="vxworks-ppc60x" ;;
+  ppcgen-*-vxworks*) OUT="vxworks-ppcgen" ;;
+  pentium-*-vxworks*) OUT="vxworks-pentium" ;;
+  simlinux-*-vxworks*) OUT="vxworks-simlinux" ;;
+  mips-*-vxworks*) OUT="vxworks-mips";;
+  ia64-*-linux?) OUT="linux-ia64" ;;
+  sparc64-*-linux2)
+	echo "WARNING! If you *know* that your GNU C supports 64-bit/V9 ABI"
+	echo "         and wish to build 64-bit library, then you have to"
+	echo "         invoke '$THERE/Configure linux64-sparcv9' *manually*."
+	if [ "$DRYRUN" = "false" -a -t 1 ]; then
+	  echo "          You have about 5 seconds to press Ctrl-C to abort."
+	  (trap "stty `stty -g`; exit 0" 2 0; stty -icanon min 0 time 50; read waste) <&1
+	fi
+	OUT="linux-sparcv9" ;;
+  sparc-*-linux2)
+	KARCH=`awk '/^type/{print$3;exit(0);}' /proc/cpuinfo`
+	case ${KARCH:-sun4} in
+	sun4u*)	OUT="linux-sparcv9" ;;
+	sun4m)	OUT="linux-sparcv8" ;;
+	sun4d)	OUT="linux-sparcv8" ;;
+	*)	OUT="linux-generic32"; options="$options -DB_ENDIAN" ;;
+	esac ;;
+  parisc*-*-linux2)
+	# 64-bit builds under parisc64 linux are not supported and
+	# compiler is expected to generate 32-bit objects...
+	CPUARCH=`awk '/cpu family/{print substr($5,1,3); exit(0);}' /proc/cpuinfo`
+	CPUSCHEDULE=`awk '/^cpu.[ 	]*: PA/{print substr($3,3); exit(0);}' /proc/cpuinfo`
+
+	# ??TODO ??  Model transformations
+	# 0. CPU Architecture for the 1.1 processor has letter suffixes. We strip that off
+	#    assuming no further arch. identification will ever be used by GCC.
+	# 1. I'm most concerned about whether is a 7300LC is closer to a 7100 versus a 7100LC.
+	# 2. The variant 64-bit processors cause concern should GCC support explicit schedulers
+	#    for these chips in the future.
+	#         PA7300LC -> 7100LC (1.1)
+	#         PA8200   -> 8000   (2.0)
+	#         PA8500   -> 8000   (2.0)
+	#         PA8600   -> 8000   (2.0)
+
+	CPUSCHEDULE=`echo $CPUSCHEDULE|sed -e 's/7300LC/7100LC/' -e 's/8.00/8000/'`
+	# Finish Model transformations
+
+	options="$options -DB_ENDIAN -mschedule=$CPUSCHEDULE -march=$CPUARCH"
+	OUT="linux-generic32" ;;
+  armv[1-3]*-*-linux2) OUT="linux-generic32" ;;
+  armv[7-9]*-*-linux2) OUT="linux-armv4"; options="$options -march=armv7-a" ;;
+  arm*-*-linux2) OUT="linux-armv4" ;;
+  aarch64-*-linux2) OUT="linux-aarch64" ;;
+  sh*b-*-linux2) OUT="linux-generic32"; options="$options -DB_ENDIAN" ;;
+  sh*-*-linux2)  OUT="linux-generic32"; options="$options -DL_ENDIAN" ;;
+  m68k*-*-linux2) OUT="linux-generic32"; options="$options -DB_ENDIAN" ;;
+  s390-*-linux2) OUT="linux-generic32"; options="$options -DB_ENDIAN" ;;
+  s390x-*-linux2)
+	# To be uncommented when glibc bug is fixed, see Configure...
+	#if egrep -e '^features.* highgprs' /proc/cpuinfo >/dev/null ; then
+	#  echo "WARNING! If you wish to build \"highgprs\" 32-bit library, then you"
+	#  echo "         have to invoke './Configure linux32-s390x' *manually*."
+	#  if [ "$DRYRUN" = "false" -a -t -1 ]; then
+	#    echo "         You have about 5 seconds to press Ctrl-C to abort."
+	#    (trap "stty `stty -g`; exit 0" 2 0; stty -icanon min 0 time 50; read waste) <&1
+	#  fi
+	#fi
+	OUT="linux64-s390x"
+	;;
+  x86_64-*-linux?)
+	if $CC -dM -E -x c /dev/null 2>&1 | grep -q ILP32 > /dev/null; then
+	    OUT="linux-x32"
+	else
+	    OUT="linux-x86_64"
+	fi ;;
+  *86-*-linux2)
+        # On machines where the compiler understands -m32, prefer a
+        # config target that uses it
+        if $CC -m32 -E -x c /dev/null > /dev/null 2>&1; then
+            OUT="linux-x86"
+        else
+            OUT="linux-elf"
+        fi ;;
+  *86-*-linux1) OUT="linux-aout" ;;
+  *-*-linux?) OUT="linux-generic32" ;;
+  sun4[uv]*-*-solaris2)
+	OUT="solaris-sparcv9-$CC"
+	ISA64=`(isainfo) 2>/dev/null | grep sparcv9`
+	if [ "$ISA64" != "" -a "$KERNEL_BITS" = "" ]; then
+	    if [ "$CC" = "cc" -a $CCVER -ge 50 ]; then
+		echo "WARNING! If you wish to build 64-bit library, then you have to"
+		echo "         invoke '$THERE/Configure solaris64-sparcv9-cc' *manually*."
+		if [ "$DRYRUN" = "false" -a -t 1 ]; then
+		  echo "         You have about 5 seconds to press Ctrl-C to abort."
+		  (trap "stty `stty -g`; exit 0" 2 0; stty -icanon min 0 time 50; read waste) <&1
+		fi
+	    elif [ "$CC" = "gcc" -a "$GCC_ARCH" = "-m64" ]; then
+		# $GCC_ARCH denotes default ABI chosen by compiler driver
+		# (first one found on the $PATH). I assume that user
+		# expects certain consistency with the rest of his builds
+		# and therefore switch over to 64-bit. 
+		OUT="solaris64-sparcv9-gcc"
+		echo "WARNING! If you wish to build 32-bit library, then you have to"
+		echo "         invoke '$THERE/Configure solaris-sparcv9-gcc' *manually*."
+		if [ "$DRYRUN" = "false" -a -t 1 ]; then
+		  echo "         You have about 5 seconds to press Ctrl-C to abort."
+		  (trap "stty `stty -g`; exit 0" 2 0; stty -icanon min 0 time 50; read waste) <&1
+		fi
+	    elif [ "$GCC_ARCH" = "-m32" ]; then
+		echo "NOTICE! If you *know* that your GNU C supports 64-bit/V9 ABI"
+		echo "        and wish to build 64-bit library, then you have to"
+		echo "        invoke '$THERE/Configure solaris64-sparcv9-gcc' *manually*."
+		if [ "$DRYRUN" = "false" -a -t 1 ]; then
+		  echo "         You have about 5 seconds to press Ctrl-C to abort."
+		  (trap "stty `stty -g`; exit 0" 2 0; stty -icanon min 0 time 50; read waste) <&1
+		fi
+	    fi
+	fi
+	if [ "$ISA64" != "" -a "$KERNEL_BITS" = "64" ]; then
+	    OUT="solaris64-sparcv9-$CC"
+	fi
+	;;
+  sun4m-*-solaris2)	OUT="solaris-sparcv8-$CC" ;;
+  sun4d-*-solaris2)	OUT="solaris-sparcv8-$CC" ;;
+  sun4*-*-solaris2)	OUT="solaris-sparcv7-$CC" ;;
+  *86*-*-solaris2)
+	ISA64=`(isainfo) 2>/dev/null | grep amd64`
+	if [ "$ISA64" != "" -a ${KERNEL_BITS:-64} -eq 64 ]; then
+	    OUT="solaris64-x86_64-$CC"
+	else
+	    OUT="solaris-x86-$CC"
+	    if [ `uname -r | sed -e 's/5\.//'` -lt 10 ]; then
+		options="$options no-sse2"
+	    fi
+	fi
+	;;
+  *-*-sunos4)		OUT="sunos-$CC" ;;
+
+  *86*-*-bsdi4)		OUT="BSD-x86-elf"; options="$options no-sse2 -ldl" ;;
+  alpha*-*-*bsd*)	OUT="BSD-generic64"; options="$options -DL_ENDIAN" ;;
+  powerpc64-*-*bsd*)	OUT="BSD-generic64"; options="$options -DB_ENDIAN" ;;
+  sparc64-*-*bsd*)	OUT="BSD-sparc64" ;;
+  ia64-*-*bsd*)		OUT="BSD-ia64" ;;
+  amd64-*-*bsd*)	OUT="BSD-x86_64" ;;
+  *86*-*-*bsd*)		# mimic ld behaviour when it's looking for libc...
+			if [ -L /usr/lib/libc.so ]; then	# [Free|Net]BSD
+			    libc=/usr/lib/libc.so
+			else					# OpenBSD
+			    # ld searches for highest libc.so.* and so do we
+			    libc=`(ls /usr/lib/libc.so.* /lib/libc.so.* | tail -1) 2>/dev/null`
+			fi
+			case "`(file -L $libc) 2>/dev/null`" in
+			*ELF*)	OUT="BSD-x86-elf" ;;
+			*)	OUT="BSD-x86"; options="$options no-sse2" ;;
+			esac ;;
+  *-*-*bsd*)		OUT="BSD-generic32" ;;
+
+  x86_64-*-haiku)	OUT="haiku-x86_64" ;;
+  *-*-haiku)		OUT="haiku-x86" ;;
+
+  *-*-osf)		OUT="osf1-alpha-cc" ;;
+  *-*-tru64)		OUT="tru64-alpha-cc" ;;
+  *-*-[Uu]nix[Ww]are7)
+	if [ "$CC" = "gcc" ]; then
+	  OUT="unixware-7-gcc" ; options="$options no-sse2"
+	else    
+	  OUT="unixware-7" ; options="$options no-sse2 -D__i386__"
+	fi
+	;;
+  *-*-[Uu]nix[Ww]are20*) OUT="unixware-2.0"; options="$options no-sse2 no-sha512" ;;
+  *-*-[Uu]nix[Ww]are21*) OUT="unixware-2.1"; options="$options no-sse2 no-sha512" ;;
+  *-*-vos)
+	options="$options no-threads no-shared no-asm no-dso"
+	EXE=".pm"
+	OUT="vos-$CC" ;;
+  BS2000-siemens-sysv4) OUT="BS2000-OSD" ;;
+  *-hpux1*)
+	if [ $CC = "gcc" -a $GCC_BITS = "64" ]; then
+	    OUT="hpux64-parisc2-gcc"
+	fi
+	[ "$KERNEL_BITS" ] || KERNEL_BITS=`(getconf KERNEL_BITS) 2>/dev/null`
+	KERNEL_BITS=${KERNEL_BITS:-32}
+	CPU_VERSION=`(getconf CPU_VERSION) 2>/dev/null`
+	CPU_VERSION=${CPU_VERSION:-0}
+	# See  for further info on CPU_VERSION.
+	if   [ $CPU_VERSION -ge 768 ]; then	# IA-64 CPU
+	     if [ $KERNEL_BITS -eq 64 -a "$CC" = "cc" ]; then
+	        OUT="hpux64-ia64-cc"
+             else
+	        OUT="hpux-ia64-cc"
+             fi
+	elif [ $CPU_VERSION -ge 532 ]; then	# PA-RISC 2.x CPU
+	     OUT=${OUT:-"hpux-parisc2-${CC}"}
+	     if [ $KERNEL_BITS -eq 64 -a "$CC" = "cc" ]; then
+		echo "WARNING! If you wish to build 64-bit library then you have to"
+		echo "         invoke '$THERE/Configure hpux64-parisc2-cc' *manually*."
+		if [ "$DRYRUN" = "false" -a -t 1 ]; then
+		  echo "         You have about 5 seconds to press Ctrl-C to abort."
+		  (trap "stty `stty -g`; exit 0" 2 0; stty -icanon min 0 time 50; read waste) <&1
+		fi
+	     fi
+	     # PA-RISC 2.0 is no longer supported as separate 32-bit
+	     # target. This is compensated for by run-time detection
+	     # in most critical assembly modules and taking advantage
+	     # of 2.0 architecture in PA-RISC 1.1 build.
+	     OUT="hpux-parisc1_1-${CC}"
+	elif [ $CPU_VERSION -ge 528 ]; then	# PA-RISC 1.1+ CPU
+	     OUT="hpux-parisc1_1-${CC}"
+	elif [ $CPU_VERSION -ge 523 ]; then	# PA-RISC 1.0 CPU
+	     OUT="hpux-parisc-${CC}"
+	else					# Motorola(?) CPU
+	     OUT="hpux-$CC"
+	fi
+	options="$options -D_REENTRANT" ;;
+  *-hpux)	OUT="hpux-parisc-$CC" ;;
+  *-aix)
+	[ "$KERNEL_BITS" ] || KERNEL_BITS=`(getconf KERNEL_BITMODE) 2>/dev/null`
+	KERNEL_BITS=${KERNEL_BITS:-32}
+	OBJECT_MODE=${OBJECT_MODE:-32}
+	if [ "$CC" = "gcc" ]; then
+	    OUT="aix-gcc"
+          if [ $OBJECT_MODE -eq 64 ]; then
+            echo 'Your $OBJECT_MODE was found to be set to 64'
+            OUT="aix64-gcc"
+          fi
+	elif [ $OBJECT_MODE -eq 64 ]; then
+	    echo 'Your $OBJECT_MODE was found to be set to 64' 
+	    OUT="aix64-cc"
+	else
+	    OUT="aix-cc"
+	    if [ $KERNEL_BITS -eq 64 ]; then
+		echo "WARNING! If you wish to build 64-bit kit, then you have to"
+		echo "         invoke '$THERE/Configure aix64-cc' *manually*."
+		if [ "$DRYRUN" = "false" -a -t 1 ]; then
+		    echo "         You have ~5 seconds to press Ctrl-C to abort."
+		    (trap "stty `stty -g`; exit 0" 2 0; stty -icanon min 0 time 50; read waste) <&1
+		fi
+	    fi
+	fi
+	if (lsattr -E -O -l `lsdev -c processor|awk '{print$1;exit}'` | grep -i powerpc) >/dev/null 2>&1; then
+	    :	# this applies even to Power3 and later, as they return PowerPC_POWER[345]
+	else
+	    options="$options no-asm"
+	fi
+	;;
+  # these are all covered by the catchall below
+  i[3456]86-*-cygwin) OUT="Cygwin-x86" ;;
+  *-*-cygwin) OUT="Cygwin-${MACHINE}" ;;
+  x86pc-*-qnx6) OUT="QNX6-i386" ;;
+  *-*-qnx6) OUT="QNX6" ;;
+  x86-*-android|i?86-*-android) OUT="android-x86" ;;
+  armv[7-9]*-*-android)
+      OUT="android-armeabi"; options="$options -march=armv7-a" ;;
+  arm*-*-android) OUT="android-armeabi" ;;
+  *) OUT=`echo $GUESSOS | awk -F- '{print $3}'`;;
+esac
+
+# NB: This atalla support has been superseded by the ENGINE support
+# That contains its own header and definitions anyway. Support can
+# be enabled or disabled on any supported platform without external
+# headers, eg. by adding the "hw-atalla" switch to ./config or
+# perl Configure
+#
+# See whether we can compile Atalla support
+#if [ -f /usr/include/atasi.h ]
+#then
+#  options="$options -DATALLA"
+#fi
+
+if [ -n "$CONFIG_OPTIONS" ]; then
+  options="$options $CONFIG_OPTIONS"
+fi
+
+if expr "$options" : '.*no\-asm' > /dev/null; then :; else
+  sh -c "$CROSS_COMPILE${CC:-gcc} -Wa,--help -c -o /tmp/null.$$.o -x assembler /dev/null && rm /tmp/null.$$.o" 2>&1 | \
+  grep \\--noexecstack >/dev/null && \
+  options="$options -Wa,--noexecstack"
+fi
+
+# gcc < 2.8 does not support -march=ultrasparc
+if [ "$OUT" = solaris-sparcv9-gcc -a $GCCVER -lt 28 ]
+then
+  echo "WARNING! Falling down to 'solaris-sparcv8-gcc'."
+  echo "         Upgrade to gcc-2.8 or later."
+  sleep 5
+  OUT=solaris-sparcv8-gcc
+fi
+if [ "$OUT" = "linux-sparcv9" -a $GCCVER -lt 28 ]
+then
+  echo "WARNING! Falling down to 'linux-sparcv8'."
+  echo "         Upgrade to gcc-2.8 or later."
+  sleep 5
+  OUT=linux-sparcv8
+fi
+
+case "$GUESSOS" in
+  i386-*) options="$options 386" ;;
+esac
+
+for i in aes bf camellia cast des dh dsa ec hmac idea md2 md5 mdc2 rc2 rc4 rc5 ripemd rsa seed sha
+do
+  if [ ! -d $THERE/crypto/$i ]
+  then
+    options="$options no-$i"
+  fi
+done
+
+if [ -z "$OUT" ]; then
+  OUT="$CC"
+fi
+
+if [ ".$PERL" = . ] ; then
+	for i in . `echo $PATH | sed 's/:/ /g'`; do
+		if [ -f "$i/perl5$EXE" ] ; then
+			PERL="$i/perl5$EXE"
+			break;
+		fi;
+	done
+fi
+
+if [ ".$PERL" = . ] ; then
+	for i in . `echo $PATH | sed 's/:/ /g'`; do
+		if [ -f "$i/perl$EXE" ] ; then
+			if "$i/perl$EXE" -e 'exit($]<5.0)'; then
+				PERL="$i/perl$EXE"
+				break;
+			fi;
+		fi;
+	done
+fi
+
+if [ ".$PERL" = . ] ; then
+	echo "You need Perl 5."
+	exit 1
+fi
+
+# run Configure to check to see if we need to specify the 
+# compiler for the platform ... in which case we add it on
+# the end ... otherwise we leave it off
+
+$PERL $THERE/Configure LIST | grep "$OUT-$CC" > /dev/null
+if [ $? = "0" ]; then
+  OUT="$OUT-$CC"
+fi
+
+OUT="$OUT"
+
+$PERL $THERE/Configure LIST | grep "$OUT" > /dev/null
+if [ $? = "0" ]; then
+  echo Configuring for $OUT
+
+  if [ "$VERBOSE" = "true" ]; then
+    echo $PERL $THERE/Configure $OUT $options
+  fi  
+  if [ "$DRYRUN" = "false" ]; then
+    $PERL $THERE/Configure $OUT $options
+  fi
+else
+  echo "This system ($OUT) is not supported. See file INSTALL for details."
+fi
+)
diff --git a/config.com b/config.com
new file mode 100644
index 000000000..5b549955d
--- /dev/null
+++ b/config.com
@@ -0,0 +1,93 @@
+$	! OpenSSL config: determine the architecture and run Configure
+$	! Copyright 2016 The OpenSSL Project Authors. All Rights Reserved.
+$	!
+$	! Licensed under the OpenSSL license (the "License").  You may not use
+$	! this file except in compliance with the License.  You can obtain a
+$	! copy in the file LICENSE in the source distribution or at
+$	! https://www.openssl.org/source/license.html
+$	!
+$	! Very simple for the moment, it will take the following arguments:
+$	!
+$	! -32 or 32	sets /POINTER_SIZE=32
+$	! -64 or 64	sets /POINTER_SIZE=64
+$	! -d		sets debugging
+$	! -h		prints a usage and exits
+$	! -t		test mode, doesn't run Configure
+$
+$	arch = f$edit( f$getsyi( "arch_name"), "lowercase")
+$	pointer_size = ""
+$	dryrun = 0
+$	verbose = 0
+$	here = F$PARSE("A.;",F$ENVIRONMENT("PROCEDURE"),,,"SYNTAX_ONLY") - "A.;"
+$
+$	collected_args = ""
+$	P_index = 0
+$	LOOP1:
+$	    P_index = P_index + 1
+$	    IF P_index .GT. 8 THEN GOTO ENDLOOP1
+$	    P = F$EDIT(P1,"TRIM,LOWERCASE")
+$	    IF P .EQS. "-h"
+$           THEN
+$               dryrun = 1
+$               P = ""
+$               TYPE SYS$INPUT
+$               DECK
+Usage: @config [options]
+
+  -32 or 32	Build with 32-bit pointer size.
+  -64 or 64	Build with 64-bit pointer size.
+  -d		Build with debugging.
+  -t            Test mode, do not run the Configure perl script.
+  -v            Verbose mode, show the exact Configure call that is being made.
+  -h		This help.
+
+Any other text will be passed to the Configure perl script.
+See INSTALL for instructions.
+
+$               EOD
+$           ENDIF
+$	    IF P .EQS. "-t"
+$	    THEN
+$		dryrun = 1
+$		verbose = 1
+$		P = ""
+$	    ENDIF
+$	    IF P .EQS. "-v"
+$	    THEN
+$		verbose = 1
+$		P = ""
+$	    ENDIF
+$	    IF P .EQS. "-32" .OR. P .EQS. "32"
+$	    THEN
+$		pointer_size = "-P32"
+$		P = ""
+$	    ENDIF
+$	    IF P .EQS. "-64" .OR. P .EQS. "64"
+$	    THEN
+$		pointer_size = "-P64"
+$		P = ""
+$	    ENDIF
+$	    IF P .EQS. "-d"
+$	    THEN
+$               collected_args = collected_args + " --debug"
+$		P = ""
+$	    ENDIF
+$	    IF P .NES. "" THEN -
+	       collected_args = collected_args + " " + P1
+$	    P1 = P2
+$	    P2 = P3
+$	    P3 = P4
+$	    P4 = P5
+$	    P5 = P6
+$	    P6 = P7
+$	    P7 = P8
+$	    P8 = ""
+$	    GOTO LOOP1
+$	ENDLOOP1:
+$
+$	target = "vms-''arch'''pointer_size'"
+$       IF verbose THEN -
+           WRITE SYS$OUTPUT "PERL ''here'Configure ""''target'""''collected_args'"
+$       IF .not. dryrun THEN -
+           PERL 'here'Configure "''target'" 'debug' 'collected_args'
+$       EXIT $STATUS
diff --git a/crypto/LPdir_nyi.c b/crypto/LPdir_nyi.c
new file mode 100644
index 000000000..049044c4c
--- /dev/null
+++ b/crypto/LPdir_nyi.c
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2004-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * Copyright (c) 2004, Richard Levitte 
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef LPDIR_H
+# include "LPdir.h"
+#endif
+
+struct LP_dir_context_st {
+    void *dummy;
+};
+const char *LP_find_file(LP_DIR_CTX **ctx, const char *directory)
+{
+    errno = EINVAL;
+    return 0;
+}
+
+int LP_find_file_end(LP_DIR_CTX **ctx)
+{
+    errno = EINVAL;
+    return 0;
+}
diff --git a/crypto/LPdir_unix.c b/crypto/LPdir_unix.c
new file mode 100644
index 000000000..1bb2940b9
--- /dev/null
+++ b/crypto/LPdir_unix.c
@@ -0,0 +1,131 @@
+/*
+ * Copyright 2004-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * Copyright (c) 2004, Richard Levitte 
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#ifndef LPDIR_H
+# include "LPdir.h"
+#endif
+
+/*
+ * The POSIXly macro for the maximum number of characters in a file path is
+ * NAME_MAX.  However, some operating systems use PATH_MAX instead.
+ * Therefore, it seems natural to first check for PATH_MAX and use that, and
+ * if it doesn't exist, use NAME_MAX.
+ */
+#if defined(PATH_MAX)
+# define LP_ENTRY_SIZE PATH_MAX
+#elif defined(NAME_MAX)
+# define LP_ENTRY_SIZE NAME_MAX
+#endif
+
+/*
+ * Of course, there's the possibility that neither PATH_MAX nor NAME_MAX
+ * exist.  It's also possible that NAME_MAX exists but is define to a very
+ * small value (HP-UX offers 14), so we need to check if we got a result, and
+ * if it meets a minimum standard, and create or change it if not.
+ */
+#if !defined(LP_ENTRY_SIZE) || LP_ENTRY_SIZE<255
+# undef LP_ENTRY_SIZE
+# define LP_ENTRY_SIZE 255
+#endif
+
+struct LP_dir_context_st {
+    DIR *dir;
+    char entry_name[LP_ENTRY_SIZE + 1];
+};
+
+const char *LP_find_file(LP_DIR_CTX **ctx, const char *directory)
+{
+    struct dirent *direntry = NULL;
+
+    if (ctx == NULL || directory == NULL) {
+        errno = EINVAL;
+        return 0;
+    }
+
+    errno = 0;
+    if (*ctx == NULL) {
+        *ctx = malloc(sizeof(**ctx));
+        if (*ctx == NULL) {
+            errno = ENOMEM;
+            return 0;
+        }
+        memset(*ctx, 0, sizeof(**ctx));
+
+        (*ctx)->dir = opendir(directory);
+        if ((*ctx)->dir == NULL) {
+            int save_errno = errno; /* Probably not needed, but I'm paranoid */
+            free(*ctx);
+            *ctx = NULL;
+            errno = save_errno;
+            return 0;
+        }
+    }
+
+    direntry = readdir((*ctx)->dir);
+    if (direntry == NULL) {
+        return 0;
+    }
+
+    strncpy((*ctx)->entry_name, direntry->d_name,
+            sizeof((*ctx)->entry_name) - 1);
+    (*ctx)->entry_name[sizeof((*ctx)->entry_name) - 1] = '\0';
+    return (*ctx)->entry_name;
+}
+
+int LP_find_file_end(LP_DIR_CTX **ctx)
+{
+    if (ctx != NULL && *ctx != NULL) {
+        int ret = closedir((*ctx)->dir);
+
+        free(*ctx);
+        switch (ret) {
+        case 0:
+            return 1;
+        case -1:
+            return 0;
+        default:
+            break;
+        }
+    }
+    errno = EINVAL;
+    return 0;
+}
diff --git a/crypto/LPdir_vms.c b/crypto/LPdir_vms.c
new file mode 100644
index 000000000..1a5b60feb
--- /dev/null
+++ b/crypto/LPdir_vms.c
@@ -0,0 +1,204 @@
+/*
+ * Copyright 2004-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * Copyright (c) 2004, Richard Levitte 
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#ifndef LPDIR_H
+# include "LPdir.h"
+#endif
+#include "vms_rms.h"
+
+/* Some compiler options hide EVMSERR. */
+#ifndef EVMSERR
+# define EVMSERR        65535   /* error for non-translatable VMS errors */
+#endif
+
+struct LP_dir_context_st {
+    unsigned long VMS_context;
+    char filespec[NAMX_MAXRSS + 1];
+    char result[NAMX_MAXRSS + 1];
+    struct dsc$descriptor_d filespec_dsc;
+    struct dsc$descriptor_d result_dsc;
+};
+
+const char *LP_find_file(LP_DIR_CTX **ctx, const char *directory)
+{
+    int status;
+    char *p, *r;
+    size_t l;
+    unsigned long flags = 0;
+
+/* Arrange 32-bit pointer to (copied) string storage, if needed. */
+#if __INITIAL_POINTER_SIZE == 64
+# pragma pointer_size save
+# pragma pointer_size 32
+    char *ctx_filespec_32p;
+# pragma pointer_size restore
+    char ctx_filespec_32[NAMX_MAXRSS + 1];
+#endif                          /* __INITIAL_POINTER_SIZE == 64 */
+
+#ifdef NAML$C_MAXRSS
+    flags |= LIB$M_FIL_LONG_NAMES;
+#endif
+
+    if (ctx == NULL || directory == NULL) {
+        errno = EINVAL;
+        return 0;
+    }
+
+    errno = 0;
+    if (*ctx == NULL) {
+        size_t filespeclen = strlen(directory);
+        char *filespec = NULL;
+
+        if (filespeclen == 0) {
+            errno = ENOENT;
+            return 0;
+        }
+
+        /* MUST be a VMS directory specification!  Let's estimate if it is. */
+        if (directory[filespeclen - 1] != ']'
+            && directory[filespeclen - 1] != '>'
+            && directory[filespeclen - 1] != ':') {
+            errno = EINVAL;
+            return 0;
+        }
+
+        filespeclen += 4;       /* "*.*;" */
+
+        if (filespeclen > NAMX_MAXRSS) {
+            errno = ENAMETOOLONG;
+            return 0;
+        }
+
+        *ctx = malloc(sizeof(**ctx));
+        if (*ctx == NULL) {
+            errno = ENOMEM;
+            return 0;
+        }
+        memset(*ctx, 0, sizeof(**ctx));
+
+        strcpy((*ctx)->filespec, directory);
+        strcat((*ctx)->filespec, "*.*;");
+
+/* Arrange 32-bit pointer to (copied) string storage, if needed. */
+#if __INITIAL_POINTER_SIZE == 64
+# define CTX_FILESPEC ctx_filespec_32p
+        /* Copy the file name to storage with a 32-bit pointer. */
+        ctx_filespec_32p = ctx_filespec_32;
+        strcpy(ctx_filespec_32p, (*ctx)->filespec);
+#else                           /* __INITIAL_POINTER_SIZE == 64 */
+# define CTX_FILESPEC (*ctx)->filespec
+#endif                          /* __INITIAL_POINTER_SIZE == 64 [else] */
+
+        (*ctx)->filespec_dsc.dsc$w_length = filespeclen;
+        (*ctx)->filespec_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
+        (*ctx)->filespec_dsc.dsc$b_class = DSC$K_CLASS_S;
+        (*ctx)->filespec_dsc.dsc$a_pointer = CTX_FILESPEC;
+    }
+
+    (*ctx)->result_dsc.dsc$w_length = 0;
+    (*ctx)->result_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
+    (*ctx)->result_dsc.dsc$b_class = DSC$K_CLASS_D;
+    (*ctx)->result_dsc.dsc$a_pointer = 0;
+
+    status = lib$find_file(&(*ctx)->filespec_dsc, &(*ctx)->result_dsc,
+                           &(*ctx)->VMS_context, 0, 0, 0, &flags);
+
+    if (status == RMS$_NMF) {
+        errno = 0;
+        vaxc$errno = status;
+        return NULL;
+    }
+
+    if (!$VMS_STATUS_SUCCESS(status)) {
+        errno = EVMSERR;
+        vaxc$errno = status;
+        return NULL;
+    }
+
+    /*
+     * Quick, cheap and dirty way to discard any device and directory, since
+     * we only want file names
+     */
+    l = (*ctx)->result_dsc.dsc$w_length;
+    p = (*ctx)->result_dsc.dsc$a_pointer;
+    r = p;
+    for (; *p; p++) {
+        if (*p == '^' && p[1] != '\0') { /* Take care of ODS-5 escapes */
+            p++;
+        } else if (*p == ':' || *p == '>' || *p == ']') {
+            l -= p + 1 - r;
+            r = p + 1;
+        } else if (*p == ';') {
+            l = p - r;
+            break;
+        }
+    }
+
+    strncpy((*ctx)->result, r, l);
+    (*ctx)->result[l] = '\0';
+    str$free1_dx(&(*ctx)->result_dsc);
+
+    return (*ctx)->result;
+}
+
+int LP_find_file_end(LP_DIR_CTX **ctx)
+{
+    if (ctx != NULL && *ctx != NULL) {
+        int status = lib$find_file_end(&(*ctx)->VMS_context);
+
+        free(*ctx);
+
+        if (!$VMS_STATUS_SUCCESS(status)) {
+            errno = EVMSERR;
+            vaxc$errno = status;
+            return 0;
+        }
+        return 1;
+    }
+    errno = EINVAL;
+    return 0;
+}
diff --git a/crypto/LPdir_win.c b/crypto/LPdir_win.c
new file mode 100644
index 000000000..8f674d305
--- /dev/null
+++ b/crypto/LPdir_win.c
@@ -0,0 +1,211 @@
+/*
+ * Copyright 2004-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * Copyright (c) 2004, Richard Levitte 
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include 
+#include 
+#include "internal/numbers.h"
+#ifndef LPDIR_H
+# include "LPdir.h"
+#endif
+
+/*
+ * We're most likely overcautious here, but let's reserve for broken WinCE
+ * headers and explicitly opt for UNICODE call. Keep in mind that our WinCE
+ * builds are compiled with -DUNICODE [as well as -D_UNICODE].
+ */
+#if defined(LP_SYS_WINCE) && !defined(FindFirstFile)
+# define FindFirstFile FindFirstFileW
+#endif
+#if defined(LP_SYS_WINCE) && !defined(FindNextFile)
+# define FindNextFile FindNextFileW
+#endif
+
+#ifndef NAME_MAX
+# define NAME_MAX 255
+#endif
+
+#ifdef CP_UTF8
+# define CP_DEFAULT CP_UTF8
+#else
+# define CP_DEFAULT CP_ACP
+#endif
+
+struct LP_dir_context_st {
+    WIN32_FIND_DATA ctx;
+    HANDLE handle;
+    char entry_name[NAME_MAX + 1];
+};
+
+const char *LP_find_file(LP_DIR_CTX **ctx, const char *directory)
+{
+    if (ctx == NULL || directory == NULL) {
+        errno = EINVAL;
+        return 0;
+    }
+
+    errno = 0;
+    if (*ctx == NULL) {
+        size_t dirlen = strlen(directory);
+
+        if (dirlen == 0 || dirlen > INT_MAX - 3) {
+            errno = ENOENT;
+            return 0;
+        }
+
+        *ctx = malloc(sizeof(**ctx));
+        if (*ctx == NULL) {
+            errno = ENOMEM;
+            return 0;
+        }
+        memset(*ctx, 0, sizeof(**ctx));
+
+        if (sizeof(TCHAR) != sizeof(char)) {
+            TCHAR *wdir = NULL;
+            /* len_0 denotes string length *with* trailing 0 */
+            size_t index = 0, len_0 = dirlen + 1;
+#ifdef LP_MULTIBYTE_AVAILABLE
+            int sz = 0;
+            UINT cp;
+
+            do {
+# ifdef CP_UTF8
+                if ((sz = MultiByteToWideChar((cp = CP_UTF8), 0,
+                                              directory, len_0,
+                                              NULL, 0)) > 0 ||
+                    GetLastError() != ERROR_NO_UNICODE_TRANSLATION)
+                    break;
+# endif
+                sz = MultiByteToWideChar((cp = CP_ACP), 0,
+                                         directory, len_0,
+                                         NULL, 0);
+            } while (0);
+
+            if (sz > 0) {
+                /*
+                 * allocate two additional characters in case we need to
+                 * concatenate asterisk, |sz| covers trailing '\0'!
+                 */
+                wdir = _alloca((sz + 2) * sizeof(TCHAR));
+                if (!MultiByteToWideChar(cp, 0, directory, len_0,
+                                         (WCHAR *)wdir, sz)) {
+                    free(*ctx);
+                    *ctx = NULL;
+                    errno = EINVAL;
+                    return 0;
+                }
+            } else
+#endif
+            {
+                sz = len_0;
+                /*
+                 * allocate two additional characters in case we need to
+                 * concatenate asterisk, |sz| covers trailing '\0'!
+                 */
+                wdir = _alloca((sz + 2) * sizeof(TCHAR));
+                for (index = 0; index < len_0; index++)
+                    wdir[index] = (TCHAR)directory[index];
+            }
+
+            sz--; /* wdir[sz] is trailing '\0' now */
+            if (wdir[sz - 1] != TEXT('*')) {
+                if (wdir[sz - 1] != TEXT('/') && wdir[sz - 1] != TEXT('\\'))
+                    _tcscpy(wdir + sz, TEXT("/*"));
+                else
+                    _tcscpy(wdir + sz, TEXT("*"));
+            }
+
+            (*ctx)->handle = FindFirstFile(wdir, &(*ctx)->ctx);
+        } else {
+            if (directory[dirlen - 1] != '*') {
+                char *buf = _alloca(dirlen + 3);
+
+                strcpy(buf, directory);
+                if (buf[dirlen - 1] != '/' && buf[dirlen - 1] != '\\')
+                    strcpy(buf + dirlen, "/*");
+                else
+                    strcpy(buf + dirlen, "*");
+
+                directory = buf;
+            }
+
+            (*ctx)->handle = FindFirstFile((TCHAR *)directory, &(*ctx)->ctx);
+        }
+
+        if ((*ctx)->handle == INVALID_HANDLE_VALUE) {
+            free(*ctx);
+            *ctx = NULL;
+            errno = EINVAL;
+            return 0;
+        }
+    } else {
+        if (FindNextFile((*ctx)->handle, &(*ctx)->ctx) == FALSE) {
+            return 0;
+        }
+    }
+    if (sizeof(TCHAR) != sizeof(char)) {
+        TCHAR *wdir = (*ctx)->ctx.cFileName;
+        size_t index, len_0 = 0;
+
+        while (wdir[len_0] && len_0 < (sizeof((*ctx)->entry_name) - 1))
+            len_0++;
+        len_0++;
+
+#ifdef LP_MULTIBYTE_AVAILABLE
+        if (!WideCharToMultiByte(CP_DEFAULT, 0, (WCHAR *)wdir, len_0,
+                                 (*ctx)->entry_name,
+                                 sizeof((*ctx)->entry_name), NULL, 0))
+#endif
+            for (index = 0; index < len_0; index++)
+                (*ctx)->entry_name[index] = (char)wdir[index];
+    } else
+        strncpy((*ctx)->entry_name, (const char *)(*ctx)->ctx.cFileName,
+                sizeof((*ctx)->entry_name) - 1);
+
+    (*ctx)->entry_name[sizeof((*ctx)->entry_name) - 1] = '\0';
+
+    return (*ctx)->entry_name;
+}
+
+int LP_find_file_end(LP_DIR_CTX **ctx)
+{
+    if (ctx != NULL && *ctx != NULL) {
+        FindClose((*ctx)->handle);
+        free(*ctx);
+        *ctx = NULL;
+        return 1;
+    }
+    errno = EINVAL;
+    return 0;
+}
diff --git a/crypto/LPdir_win32.c b/crypto/LPdir_win32.c
new file mode 100644
index 000000000..59ed48579
--- /dev/null
+++ b/crypto/LPdir_win32.c
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2004-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * Copyright (c) 2004, Richard Levitte 
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define LP_SYS_WIN32
+#define LP_MULTIBYTE_AVAILABLE
+#include "LPdir_win.c"
diff --git a/crypto/LPdir_wince.c b/crypto/LPdir_wince.c
new file mode 100644
index 000000000..dbc10529d
--- /dev/null
+++ b/crypto/LPdir_wince.c
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2004-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * Copyright (c) 2004, Richard Levitte 
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define LP_SYS_WINCE
+/*
+ * We might want to define LP_MULTIBYTE_AVAILABLE here.  It's currently under
+ * investigation what the exact conditions would be
+ */
+#include "LPdir_win.c"
diff --git a/crypto/aes/aes_cbc.c b/crypto/aes/aes_cbc.c
new file mode 100644
index 000000000..342841fc4
--- /dev/null
+++ b/crypto/aes/aes_cbc.c
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2002-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+
+void AES_cbc_encrypt(const unsigned char *in, unsigned char *out,
+                     size_t len, const AES_KEY *key,
+                     unsigned char *ivec, const int enc)
+{
+
+    if (enc)
+        CRYPTO_cbc128_encrypt(in, out, len, key, ivec,
+                              (block128_f) AES_encrypt);
+    else
+        CRYPTO_cbc128_decrypt(in, out, len, key, ivec,
+                              (block128_f) AES_decrypt);
+}
diff --git a/crypto/aes/aes_cfb.c b/crypto/aes/aes_cfb.c
new file mode 100644
index 000000000..f010e3c4e
--- /dev/null
+++ b/crypto/aes/aes_cfb.c
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2002-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+
+/*
+ * The input and output encrypted as though 128bit cfb mode is being used.
+ * The extra state information to record how much of the 128bit block we have
+ * used is contained in *num;
+ */
+
+void AES_cfb128_encrypt(const unsigned char *in, unsigned char *out,
+                        size_t length, const AES_KEY *key,
+                        unsigned char *ivec, int *num, const int enc)
+{
+
+    CRYPTO_cfb128_encrypt(in, out, length, key, ivec, num, enc,
+                          (block128_f) AES_encrypt);
+}
+
+/* N.B. This expects the input to be packed, MS bit first */
+void AES_cfb1_encrypt(const unsigned char *in, unsigned char *out,
+                      size_t length, const AES_KEY *key,
+                      unsigned char *ivec, int *num, const int enc)
+{
+    CRYPTO_cfb128_1_encrypt(in, out, length, key, ivec, num, enc,
+                            (block128_f) AES_encrypt);
+}
+
+void AES_cfb8_encrypt(const unsigned char *in, unsigned char *out,
+                      size_t length, const AES_KEY *key,
+                      unsigned char *ivec, int *num, const int enc)
+{
+    CRYPTO_cfb128_8_encrypt(in, out, length, key, ivec, num, enc,
+                            (block128_f) AES_encrypt);
+}
diff --git a/crypto/aes/aes_core.c b/crypto/aes/aes_core.c
new file mode 100644
index 000000000..bd5c7793b
--- /dev/null
+++ b/crypto/aes/aes_core.c
@@ -0,0 +1,1367 @@
+/*
+ * Copyright 2002-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/**
+ * rijndael-alg-fst.c
+ *
+ * @version 3.0 (December 2000)
+ *
+ * Optimised ANSI C code for the Rijndael cipher (now AES)
+ *
+ * @author Vincent Rijmen 
+ * @author Antoon Bosselaers 
+ * @author Paulo Barreto 
+ *
+ * This code is hereby placed in the public domain.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* Note: rewritten a little bit to provide error control and an OpenSSL-
+   compatible API */
+
+#include 
+
+#include 
+#include 
+#include 
+#include "aes_locl.h"
+
+#ifndef AES_ASM
+/*-
+Te0[x] = S [x].[02, 01, 01, 03];
+Te1[x] = S [x].[03, 02, 01, 01];
+Te2[x] = S [x].[01, 03, 02, 01];
+Te3[x] = S [x].[01, 01, 03, 02];
+
+Td0[x] = Si[x].[0e, 09, 0d, 0b];
+Td1[x] = Si[x].[0b, 0e, 09, 0d];
+Td2[x] = Si[x].[0d, 0b, 0e, 09];
+Td3[x] = Si[x].[09, 0d, 0b, 0e];
+Td4[x] = Si[x].[01];
+*/
+
+static const u32 Te0[256] = {
+    0xc66363a5U, 0xf87c7c84U, 0xee777799U, 0xf67b7b8dU,
+    0xfff2f20dU, 0xd66b6bbdU, 0xde6f6fb1U, 0x91c5c554U,
+    0x60303050U, 0x02010103U, 0xce6767a9U, 0x562b2b7dU,
+    0xe7fefe19U, 0xb5d7d762U, 0x4dababe6U, 0xec76769aU,
+    0x8fcaca45U, 0x1f82829dU, 0x89c9c940U, 0xfa7d7d87U,
+    0xeffafa15U, 0xb25959ebU, 0x8e4747c9U, 0xfbf0f00bU,
+    0x41adadecU, 0xb3d4d467U, 0x5fa2a2fdU, 0x45afafeaU,
+    0x239c9cbfU, 0x53a4a4f7U, 0xe4727296U, 0x9bc0c05bU,
+    0x75b7b7c2U, 0xe1fdfd1cU, 0x3d9393aeU, 0x4c26266aU,
+    0x6c36365aU, 0x7e3f3f41U, 0xf5f7f702U, 0x83cccc4fU,
+    0x6834345cU, 0x51a5a5f4U, 0xd1e5e534U, 0xf9f1f108U,
+    0xe2717193U, 0xabd8d873U, 0x62313153U, 0x2a15153fU,
+    0x0804040cU, 0x95c7c752U, 0x46232365U, 0x9dc3c35eU,
+    0x30181828U, 0x379696a1U, 0x0a05050fU, 0x2f9a9ab5U,
+    0x0e070709U, 0x24121236U, 0x1b80809bU, 0xdfe2e23dU,
+    0xcdebeb26U, 0x4e272769U, 0x7fb2b2cdU, 0xea75759fU,
+    0x1209091bU, 0x1d83839eU, 0x582c2c74U, 0x341a1a2eU,
+    0x361b1b2dU, 0xdc6e6eb2U, 0xb45a5aeeU, 0x5ba0a0fbU,
+    0xa45252f6U, 0x763b3b4dU, 0xb7d6d661U, 0x7db3b3ceU,
+    0x5229297bU, 0xdde3e33eU, 0x5e2f2f71U, 0x13848497U,
+    0xa65353f5U, 0xb9d1d168U, 0x00000000U, 0xc1eded2cU,
+    0x40202060U, 0xe3fcfc1fU, 0x79b1b1c8U, 0xb65b5bedU,
+    0xd46a6abeU, 0x8dcbcb46U, 0x67bebed9U, 0x7239394bU,
+    0x944a4adeU, 0x984c4cd4U, 0xb05858e8U, 0x85cfcf4aU,
+    0xbbd0d06bU, 0xc5efef2aU, 0x4faaaae5U, 0xedfbfb16U,
+    0x864343c5U, 0x9a4d4dd7U, 0x66333355U, 0x11858594U,
+    0x8a4545cfU, 0xe9f9f910U, 0x04020206U, 0xfe7f7f81U,
+    0xa05050f0U, 0x783c3c44U, 0x259f9fbaU, 0x4ba8a8e3U,
+    0xa25151f3U, 0x5da3a3feU, 0x804040c0U, 0x058f8f8aU,
+    0x3f9292adU, 0x219d9dbcU, 0x70383848U, 0xf1f5f504U,
+    0x63bcbcdfU, 0x77b6b6c1U, 0xafdada75U, 0x42212163U,
+    0x20101030U, 0xe5ffff1aU, 0xfdf3f30eU, 0xbfd2d26dU,
+    0x81cdcd4cU, 0x180c0c14U, 0x26131335U, 0xc3ecec2fU,
+    0xbe5f5fe1U, 0x359797a2U, 0x884444ccU, 0x2e171739U,
+    0x93c4c457U, 0x55a7a7f2U, 0xfc7e7e82U, 0x7a3d3d47U,
+    0xc86464acU, 0xba5d5de7U, 0x3219192bU, 0xe6737395U,
+    0xc06060a0U, 0x19818198U, 0x9e4f4fd1U, 0xa3dcdc7fU,
+    0x44222266U, 0x542a2a7eU, 0x3b9090abU, 0x0b888883U,
+    0x8c4646caU, 0xc7eeee29U, 0x6bb8b8d3U, 0x2814143cU,
+    0xa7dede79U, 0xbc5e5ee2U, 0x160b0b1dU, 0xaddbdb76U,
+    0xdbe0e03bU, 0x64323256U, 0x743a3a4eU, 0x140a0a1eU,
+    0x924949dbU, 0x0c06060aU, 0x4824246cU, 0xb85c5ce4U,
+    0x9fc2c25dU, 0xbdd3d36eU, 0x43acacefU, 0xc46262a6U,
+    0x399191a8U, 0x319595a4U, 0xd3e4e437U, 0xf279798bU,
+    0xd5e7e732U, 0x8bc8c843U, 0x6e373759U, 0xda6d6db7U,
+    0x018d8d8cU, 0xb1d5d564U, 0x9c4e4ed2U, 0x49a9a9e0U,
+    0xd86c6cb4U, 0xac5656faU, 0xf3f4f407U, 0xcfeaea25U,
+    0xca6565afU, 0xf47a7a8eU, 0x47aeaee9U, 0x10080818U,
+    0x6fbabad5U, 0xf0787888U, 0x4a25256fU, 0x5c2e2e72U,
+    0x381c1c24U, 0x57a6a6f1U, 0x73b4b4c7U, 0x97c6c651U,
+    0xcbe8e823U, 0xa1dddd7cU, 0xe874749cU, 0x3e1f1f21U,
+    0x964b4bddU, 0x61bdbddcU, 0x0d8b8b86U, 0x0f8a8a85U,
+    0xe0707090U, 0x7c3e3e42U, 0x71b5b5c4U, 0xcc6666aaU,
+    0x904848d8U, 0x06030305U, 0xf7f6f601U, 0x1c0e0e12U,
+    0xc26161a3U, 0x6a35355fU, 0xae5757f9U, 0x69b9b9d0U,
+    0x17868691U, 0x99c1c158U, 0x3a1d1d27U, 0x279e9eb9U,
+    0xd9e1e138U, 0xebf8f813U, 0x2b9898b3U, 0x22111133U,
+    0xd26969bbU, 0xa9d9d970U, 0x078e8e89U, 0x339494a7U,
+    0x2d9b9bb6U, 0x3c1e1e22U, 0x15878792U, 0xc9e9e920U,
+    0x87cece49U, 0xaa5555ffU, 0x50282878U, 0xa5dfdf7aU,
+    0x038c8c8fU, 0x59a1a1f8U, 0x09898980U, 0x1a0d0d17U,
+    0x65bfbfdaU, 0xd7e6e631U, 0x844242c6U, 0xd06868b8U,
+    0x824141c3U, 0x299999b0U, 0x5a2d2d77U, 0x1e0f0f11U,
+    0x7bb0b0cbU, 0xa85454fcU, 0x6dbbbbd6U, 0x2c16163aU,
+};
+static const u32 Te1[256] = {
+    0xa5c66363U, 0x84f87c7cU, 0x99ee7777U, 0x8df67b7bU,
+    0x0dfff2f2U, 0xbdd66b6bU, 0xb1de6f6fU, 0x5491c5c5U,
+    0x50603030U, 0x03020101U, 0xa9ce6767U, 0x7d562b2bU,
+    0x19e7fefeU, 0x62b5d7d7U, 0xe64dababU, 0x9aec7676U,
+    0x458fcacaU, 0x9d1f8282U, 0x4089c9c9U, 0x87fa7d7dU,
+    0x15effafaU, 0xebb25959U, 0xc98e4747U, 0x0bfbf0f0U,
+    0xec41adadU, 0x67b3d4d4U, 0xfd5fa2a2U, 0xea45afafU,
+    0xbf239c9cU, 0xf753a4a4U, 0x96e47272U, 0x5b9bc0c0U,
+    0xc275b7b7U, 0x1ce1fdfdU, 0xae3d9393U, 0x6a4c2626U,
+    0x5a6c3636U, 0x417e3f3fU, 0x02f5f7f7U, 0x4f83ccccU,
+    0x5c683434U, 0xf451a5a5U, 0x34d1e5e5U, 0x08f9f1f1U,
+    0x93e27171U, 0x73abd8d8U, 0x53623131U, 0x3f2a1515U,
+    0x0c080404U, 0x5295c7c7U, 0x65462323U, 0x5e9dc3c3U,
+    0x28301818U, 0xa1379696U, 0x0f0a0505U, 0xb52f9a9aU,
+    0x090e0707U, 0x36241212U, 0x9b1b8080U, 0x3ddfe2e2U,
+    0x26cdebebU, 0x694e2727U, 0xcd7fb2b2U, 0x9fea7575U,
+    0x1b120909U, 0x9e1d8383U, 0x74582c2cU, 0x2e341a1aU,
+    0x2d361b1bU, 0xb2dc6e6eU, 0xeeb45a5aU, 0xfb5ba0a0U,
+    0xf6a45252U, 0x4d763b3bU, 0x61b7d6d6U, 0xce7db3b3U,
+    0x7b522929U, 0x3edde3e3U, 0x715e2f2fU, 0x97138484U,
+    0xf5a65353U, 0x68b9d1d1U, 0x00000000U, 0x2cc1ededU,
+    0x60402020U, 0x1fe3fcfcU, 0xc879b1b1U, 0xedb65b5bU,
+    0xbed46a6aU, 0x468dcbcbU, 0xd967bebeU, 0x4b723939U,
+    0xde944a4aU, 0xd4984c4cU, 0xe8b05858U, 0x4a85cfcfU,
+    0x6bbbd0d0U, 0x2ac5efefU, 0xe54faaaaU, 0x16edfbfbU,
+    0xc5864343U, 0xd79a4d4dU, 0x55663333U, 0x94118585U,
+    0xcf8a4545U, 0x10e9f9f9U, 0x06040202U, 0x81fe7f7fU,
+    0xf0a05050U, 0x44783c3cU, 0xba259f9fU, 0xe34ba8a8U,
+    0xf3a25151U, 0xfe5da3a3U, 0xc0804040U, 0x8a058f8fU,
+    0xad3f9292U, 0xbc219d9dU, 0x48703838U, 0x04f1f5f5U,
+    0xdf63bcbcU, 0xc177b6b6U, 0x75afdadaU, 0x63422121U,
+    0x30201010U, 0x1ae5ffffU, 0x0efdf3f3U, 0x6dbfd2d2U,
+    0x4c81cdcdU, 0x14180c0cU, 0x35261313U, 0x2fc3ececU,
+    0xe1be5f5fU, 0xa2359797U, 0xcc884444U, 0x392e1717U,
+    0x5793c4c4U, 0xf255a7a7U, 0x82fc7e7eU, 0x477a3d3dU,
+    0xacc86464U, 0xe7ba5d5dU, 0x2b321919U, 0x95e67373U,
+    0xa0c06060U, 0x98198181U, 0xd19e4f4fU, 0x7fa3dcdcU,
+    0x66442222U, 0x7e542a2aU, 0xab3b9090U, 0x830b8888U,
+    0xca8c4646U, 0x29c7eeeeU, 0xd36bb8b8U, 0x3c281414U,
+    0x79a7dedeU, 0xe2bc5e5eU, 0x1d160b0bU, 0x76addbdbU,
+    0x3bdbe0e0U, 0x56643232U, 0x4e743a3aU, 0x1e140a0aU,
+    0xdb924949U, 0x0a0c0606U, 0x6c482424U, 0xe4b85c5cU,
+    0x5d9fc2c2U, 0x6ebdd3d3U, 0xef43acacU, 0xa6c46262U,
+    0xa8399191U, 0xa4319595U, 0x37d3e4e4U, 0x8bf27979U,
+    0x32d5e7e7U, 0x438bc8c8U, 0x596e3737U, 0xb7da6d6dU,
+    0x8c018d8dU, 0x64b1d5d5U, 0xd29c4e4eU, 0xe049a9a9U,
+    0xb4d86c6cU, 0xfaac5656U, 0x07f3f4f4U, 0x25cfeaeaU,
+    0xafca6565U, 0x8ef47a7aU, 0xe947aeaeU, 0x18100808U,
+    0xd56fbabaU, 0x88f07878U, 0x6f4a2525U, 0x725c2e2eU,
+    0x24381c1cU, 0xf157a6a6U, 0xc773b4b4U, 0x5197c6c6U,
+    0x23cbe8e8U, 0x7ca1ddddU, 0x9ce87474U, 0x213e1f1fU,
+    0xdd964b4bU, 0xdc61bdbdU, 0x860d8b8bU, 0x850f8a8aU,
+    0x90e07070U, 0x427c3e3eU, 0xc471b5b5U, 0xaacc6666U,
+    0xd8904848U, 0x05060303U, 0x01f7f6f6U, 0x121c0e0eU,
+    0xa3c26161U, 0x5f6a3535U, 0xf9ae5757U, 0xd069b9b9U,
+    0x91178686U, 0x5899c1c1U, 0x273a1d1dU, 0xb9279e9eU,
+    0x38d9e1e1U, 0x13ebf8f8U, 0xb32b9898U, 0x33221111U,
+    0xbbd26969U, 0x70a9d9d9U, 0x89078e8eU, 0xa7339494U,
+    0xb62d9b9bU, 0x223c1e1eU, 0x92158787U, 0x20c9e9e9U,
+    0x4987ceceU, 0xffaa5555U, 0x78502828U, 0x7aa5dfdfU,
+    0x8f038c8cU, 0xf859a1a1U, 0x80098989U, 0x171a0d0dU,
+    0xda65bfbfU, 0x31d7e6e6U, 0xc6844242U, 0xb8d06868U,
+    0xc3824141U, 0xb0299999U, 0x775a2d2dU, 0x111e0f0fU,
+    0xcb7bb0b0U, 0xfca85454U, 0xd66dbbbbU, 0x3a2c1616U,
+};
+static const u32 Te2[256] = {
+    0x63a5c663U, 0x7c84f87cU, 0x7799ee77U, 0x7b8df67bU,
+    0xf20dfff2U, 0x6bbdd66bU, 0x6fb1de6fU, 0xc55491c5U,
+    0x30506030U, 0x01030201U, 0x67a9ce67U, 0x2b7d562bU,
+    0xfe19e7feU, 0xd762b5d7U, 0xabe64dabU, 0x769aec76U,
+    0xca458fcaU, 0x829d1f82U, 0xc94089c9U, 0x7d87fa7dU,
+    0xfa15effaU, 0x59ebb259U, 0x47c98e47U, 0xf00bfbf0U,
+    0xadec41adU, 0xd467b3d4U, 0xa2fd5fa2U, 0xafea45afU,
+    0x9cbf239cU, 0xa4f753a4U, 0x7296e472U, 0xc05b9bc0U,
+    0xb7c275b7U, 0xfd1ce1fdU, 0x93ae3d93U, 0x266a4c26U,
+    0x365a6c36U, 0x3f417e3fU, 0xf702f5f7U, 0xcc4f83ccU,
+    0x345c6834U, 0xa5f451a5U, 0xe534d1e5U, 0xf108f9f1U,
+    0x7193e271U, 0xd873abd8U, 0x31536231U, 0x153f2a15U,
+    0x040c0804U, 0xc75295c7U, 0x23654623U, 0xc35e9dc3U,
+    0x18283018U, 0x96a13796U, 0x050f0a05U, 0x9ab52f9aU,
+    0x07090e07U, 0x12362412U, 0x809b1b80U, 0xe23ddfe2U,
+    0xeb26cdebU, 0x27694e27U, 0xb2cd7fb2U, 0x759fea75U,
+    0x091b1209U, 0x839e1d83U, 0x2c74582cU, 0x1a2e341aU,
+    0x1b2d361bU, 0x6eb2dc6eU, 0x5aeeb45aU, 0xa0fb5ba0U,
+    0x52f6a452U, 0x3b4d763bU, 0xd661b7d6U, 0xb3ce7db3U,
+    0x297b5229U, 0xe33edde3U, 0x2f715e2fU, 0x84971384U,
+    0x53f5a653U, 0xd168b9d1U, 0x00000000U, 0xed2cc1edU,
+    0x20604020U, 0xfc1fe3fcU, 0xb1c879b1U, 0x5bedb65bU,
+    0x6abed46aU, 0xcb468dcbU, 0xbed967beU, 0x394b7239U,
+    0x4ade944aU, 0x4cd4984cU, 0x58e8b058U, 0xcf4a85cfU,
+    0xd06bbbd0U, 0xef2ac5efU, 0xaae54faaU, 0xfb16edfbU,
+    0x43c58643U, 0x4dd79a4dU, 0x33556633U, 0x85941185U,
+    0x45cf8a45U, 0xf910e9f9U, 0x02060402U, 0x7f81fe7fU,
+    0x50f0a050U, 0x3c44783cU, 0x9fba259fU, 0xa8e34ba8U,
+    0x51f3a251U, 0xa3fe5da3U, 0x40c08040U, 0x8f8a058fU,
+    0x92ad3f92U, 0x9dbc219dU, 0x38487038U, 0xf504f1f5U,
+    0xbcdf63bcU, 0xb6c177b6U, 0xda75afdaU, 0x21634221U,
+    0x10302010U, 0xff1ae5ffU, 0xf30efdf3U, 0xd26dbfd2U,
+    0xcd4c81cdU, 0x0c14180cU, 0x13352613U, 0xec2fc3ecU,
+    0x5fe1be5fU, 0x97a23597U, 0x44cc8844U, 0x17392e17U,
+    0xc45793c4U, 0xa7f255a7U, 0x7e82fc7eU, 0x3d477a3dU,
+    0x64acc864U, 0x5de7ba5dU, 0x192b3219U, 0x7395e673U,
+    0x60a0c060U, 0x81981981U, 0x4fd19e4fU, 0xdc7fa3dcU,
+    0x22664422U, 0x2a7e542aU, 0x90ab3b90U, 0x88830b88U,
+    0x46ca8c46U, 0xee29c7eeU, 0xb8d36bb8U, 0x143c2814U,
+    0xde79a7deU, 0x5ee2bc5eU, 0x0b1d160bU, 0xdb76addbU,
+    0xe03bdbe0U, 0x32566432U, 0x3a4e743aU, 0x0a1e140aU,
+    0x49db9249U, 0x060a0c06U, 0x246c4824U, 0x5ce4b85cU,
+    0xc25d9fc2U, 0xd36ebdd3U, 0xacef43acU, 0x62a6c462U,
+    0x91a83991U, 0x95a43195U, 0xe437d3e4U, 0x798bf279U,
+    0xe732d5e7U, 0xc8438bc8U, 0x37596e37U, 0x6db7da6dU,
+    0x8d8c018dU, 0xd564b1d5U, 0x4ed29c4eU, 0xa9e049a9U,
+    0x6cb4d86cU, 0x56faac56U, 0xf407f3f4U, 0xea25cfeaU,
+    0x65afca65U, 0x7a8ef47aU, 0xaee947aeU, 0x08181008U,
+    0xbad56fbaU, 0x7888f078U, 0x256f4a25U, 0x2e725c2eU,
+    0x1c24381cU, 0xa6f157a6U, 0xb4c773b4U, 0xc65197c6U,
+    0xe823cbe8U, 0xdd7ca1ddU, 0x749ce874U, 0x1f213e1fU,
+    0x4bdd964bU, 0xbddc61bdU, 0x8b860d8bU, 0x8a850f8aU,
+    0x7090e070U, 0x3e427c3eU, 0xb5c471b5U, 0x66aacc66U,
+    0x48d89048U, 0x03050603U, 0xf601f7f6U, 0x0e121c0eU,
+    0x61a3c261U, 0x355f6a35U, 0x57f9ae57U, 0xb9d069b9U,
+    0x86911786U, 0xc15899c1U, 0x1d273a1dU, 0x9eb9279eU,
+    0xe138d9e1U, 0xf813ebf8U, 0x98b32b98U, 0x11332211U,
+    0x69bbd269U, 0xd970a9d9U, 0x8e89078eU, 0x94a73394U,
+    0x9bb62d9bU, 0x1e223c1eU, 0x87921587U, 0xe920c9e9U,
+    0xce4987ceU, 0x55ffaa55U, 0x28785028U, 0xdf7aa5dfU,
+    0x8c8f038cU, 0xa1f859a1U, 0x89800989U, 0x0d171a0dU,
+    0xbfda65bfU, 0xe631d7e6U, 0x42c68442U, 0x68b8d068U,
+    0x41c38241U, 0x99b02999U, 0x2d775a2dU, 0x0f111e0fU,
+    0xb0cb7bb0U, 0x54fca854U, 0xbbd66dbbU, 0x163a2c16U,
+};
+static const u32 Te3[256] = {
+    0x6363a5c6U, 0x7c7c84f8U, 0x777799eeU, 0x7b7b8df6U,
+    0xf2f20dffU, 0x6b6bbdd6U, 0x6f6fb1deU, 0xc5c55491U,
+    0x30305060U, 0x01010302U, 0x6767a9ceU, 0x2b2b7d56U,
+    0xfefe19e7U, 0xd7d762b5U, 0xababe64dU, 0x76769aecU,
+    0xcaca458fU, 0x82829d1fU, 0xc9c94089U, 0x7d7d87faU,
+    0xfafa15efU, 0x5959ebb2U, 0x4747c98eU, 0xf0f00bfbU,
+    0xadadec41U, 0xd4d467b3U, 0xa2a2fd5fU, 0xafafea45U,
+    0x9c9cbf23U, 0xa4a4f753U, 0x727296e4U, 0xc0c05b9bU,
+    0xb7b7c275U, 0xfdfd1ce1U, 0x9393ae3dU, 0x26266a4cU,
+    0x36365a6cU, 0x3f3f417eU, 0xf7f702f5U, 0xcccc4f83U,
+    0x34345c68U, 0xa5a5f451U, 0xe5e534d1U, 0xf1f108f9U,
+    0x717193e2U, 0xd8d873abU, 0x31315362U, 0x15153f2aU,
+    0x04040c08U, 0xc7c75295U, 0x23236546U, 0xc3c35e9dU,
+    0x18182830U, 0x9696a137U, 0x05050f0aU, 0x9a9ab52fU,
+    0x0707090eU, 0x12123624U, 0x80809b1bU, 0xe2e23ddfU,
+    0xebeb26cdU, 0x2727694eU, 0xb2b2cd7fU, 0x75759feaU,
+    0x09091b12U, 0x83839e1dU, 0x2c2c7458U, 0x1a1a2e34U,
+    0x1b1b2d36U, 0x6e6eb2dcU, 0x5a5aeeb4U, 0xa0a0fb5bU,
+    0x5252f6a4U, 0x3b3b4d76U, 0xd6d661b7U, 0xb3b3ce7dU,
+    0x29297b52U, 0xe3e33eddU, 0x2f2f715eU, 0x84849713U,
+    0x5353f5a6U, 0xd1d168b9U, 0x00000000U, 0xeded2cc1U,
+    0x20206040U, 0xfcfc1fe3U, 0xb1b1c879U, 0x5b5bedb6U,
+    0x6a6abed4U, 0xcbcb468dU, 0xbebed967U, 0x39394b72U,
+    0x4a4ade94U, 0x4c4cd498U, 0x5858e8b0U, 0xcfcf4a85U,
+    0xd0d06bbbU, 0xefef2ac5U, 0xaaaae54fU, 0xfbfb16edU,
+    0x4343c586U, 0x4d4dd79aU, 0x33335566U, 0x85859411U,
+    0x4545cf8aU, 0xf9f910e9U, 0x02020604U, 0x7f7f81feU,
+    0x5050f0a0U, 0x3c3c4478U, 0x9f9fba25U, 0xa8a8e34bU,
+    0x5151f3a2U, 0xa3a3fe5dU, 0x4040c080U, 0x8f8f8a05U,
+    0x9292ad3fU, 0x9d9dbc21U, 0x38384870U, 0xf5f504f1U,
+    0xbcbcdf63U, 0xb6b6c177U, 0xdada75afU, 0x21216342U,
+    0x10103020U, 0xffff1ae5U, 0xf3f30efdU, 0xd2d26dbfU,
+    0xcdcd4c81U, 0x0c0c1418U, 0x13133526U, 0xecec2fc3U,
+    0x5f5fe1beU, 0x9797a235U, 0x4444cc88U, 0x1717392eU,
+    0xc4c45793U, 0xa7a7f255U, 0x7e7e82fcU, 0x3d3d477aU,
+    0x6464acc8U, 0x5d5de7baU, 0x19192b32U, 0x737395e6U,
+    0x6060a0c0U, 0x81819819U, 0x4f4fd19eU, 0xdcdc7fa3U,
+    0x22226644U, 0x2a2a7e54U, 0x9090ab3bU, 0x8888830bU,
+    0x4646ca8cU, 0xeeee29c7U, 0xb8b8d36bU, 0x14143c28U,
+    0xdede79a7U, 0x5e5ee2bcU, 0x0b0b1d16U, 0xdbdb76adU,
+    0xe0e03bdbU, 0x32325664U, 0x3a3a4e74U, 0x0a0a1e14U,
+    0x4949db92U, 0x06060a0cU, 0x24246c48U, 0x5c5ce4b8U,
+    0xc2c25d9fU, 0xd3d36ebdU, 0xacacef43U, 0x6262a6c4U,
+    0x9191a839U, 0x9595a431U, 0xe4e437d3U, 0x79798bf2U,
+    0xe7e732d5U, 0xc8c8438bU, 0x3737596eU, 0x6d6db7daU,
+    0x8d8d8c01U, 0xd5d564b1U, 0x4e4ed29cU, 0xa9a9e049U,
+    0x6c6cb4d8U, 0x5656faacU, 0xf4f407f3U, 0xeaea25cfU,
+    0x6565afcaU, 0x7a7a8ef4U, 0xaeaee947U, 0x08081810U,
+    0xbabad56fU, 0x787888f0U, 0x25256f4aU, 0x2e2e725cU,
+    0x1c1c2438U, 0xa6a6f157U, 0xb4b4c773U, 0xc6c65197U,
+    0xe8e823cbU, 0xdddd7ca1U, 0x74749ce8U, 0x1f1f213eU,
+    0x4b4bdd96U, 0xbdbddc61U, 0x8b8b860dU, 0x8a8a850fU,
+    0x707090e0U, 0x3e3e427cU, 0xb5b5c471U, 0x6666aaccU,
+    0x4848d890U, 0x03030506U, 0xf6f601f7U, 0x0e0e121cU,
+    0x6161a3c2U, 0x35355f6aU, 0x5757f9aeU, 0xb9b9d069U,
+    0x86869117U, 0xc1c15899U, 0x1d1d273aU, 0x9e9eb927U,
+    0xe1e138d9U, 0xf8f813ebU, 0x9898b32bU, 0x11113322U,
+    0x6969bbd2U, 0xd9d970a9U, 0x8e8e8907U, 0x9494a733U,
+    0x9b9bb62dU, 0x1e1e223cU, 0x87879215U, 0xe9e920c9U,
+    0xcece4987U, 0x5555ffaaU, 0x28287850U, 0xdfdf7aa5U,
+    0x8c8c8f03U, 0xa1a1f859U, 0x89898009U, 0x0d0d171aU,
+    0xbfbfda65U, 0xe6e631d7U, 0x4242c684U, 0x6868b8d0U,
+    0x4141c382U, 0x9999b029U, 0x2d2d775aU, 0x0f0f111eU,
+    0xb0b0cb7bU, 0x5454fca8U, 0xbbbbd66dU, 0x16163a2cU,
+};
+
+static const u32 Td0[256] = {
+    0x51f4a750U, 0x7e416553U, 0x1a17a4c3U, 0x3a275e96U,
+    0x3bab6bcbU, 0x1f9d45f1U, 0xacfa58abU, 0x4be30393U,
+    0x2030fa55U, 0xad766df6U, 0x88cc7691U, 0xf5024c25U,
+    0x4fe5d7fcU, 0xc52acbd7U, 0x26354480U, 0xb562a38fU,
+    0xdeb15a49U, 0x25ba1b67U, 0x45ea0e98U, 0x5dfec0e1U,
+    0xc32f7502U, 0x814cf012U, 0x8d4697a3U, 0x6bd3f9c6U,
+    0x038f5fe7U, 0x15929c95U, 0xbf6d7aebU, 0x955259daU,
+    0xd4be832dU, 0x587421d3U, 0x49e06929U, 0x8ec9c844U,
+    0x75c2896aU, 0xf48e7978U, 0x99583e6bU, 0x27b971ddU,
+    0xbee14fb6U, 0xf088ad17U, 0xc920ac66U, 0x7dce3ab4U,
+    0x63df4a18U, 0xe51a3182U, 0x97513360U, 0x62537f45U,
+    0xb16477e0U, 0xbb6bae84U, 0xfe81a01cU, 0xf9082b94U,
+    0x70486858U, 0x8f45fd19U, 0x94de6c87U, 0x527bf8b7U,
+    0xab73d323U, 0x724b02e2U, 0xe31f8f57U, 0x6655ab2aU,
+    0xb2eb2807U, 0x2fb5c203U, 0x86c57b9aU, 0xd33708a5U,
+    0x302887f2U, 0x23bfa5b2U, 0x02036abaU, 0xed16825cU,
+    0x8acf1c2bU, 0xa779b492U, 0xf307f2f0U, 0x4e69e2a1U,
+    0x65daf4cdU, 0x0605bed5U, 0xd134621fU, 0xc4a6fe8aU,
+    0x342e539dU, 0xa2f355a0U, 0x058ae132U, 0xa4f6eb75U,
+    0x0b83ec39U, 0x4060efaaU, 0x5e719f06U, 0xbd6e1051U,
+    0x3e218af9U, 0x96dd063dU, 0xdd3e05aeU, 0x4de6bd46U,
+    0x91548db5U, 0x71c45d05U, 0x0406d46fU, 0x605015ffU,
+    0x1998fb24U, 0xd6bde997U, 0x894043ccU, 0x67d99e77U,
+    0xb0e842bdU, 0x07898b88U, 0xe7195b38U, 0x79c8eedbU,
+    0xa17c0a47U, 0x7c420fe9U, 0xf8841ec9U, 0x00000000U,
+    0x09808683U, 0x322bed48U, 0x1e1170acU, 0x6c5a724eU,
+    0xfd0efffbU, 0x0f853856U, 0x3daed51eU, 0x362d3927U,
+    0x0a0fd964U, 0x685ca621U, 0x9b5b54d1U, 0x24362e3aU,
+    0x0c0a67b1U, 0x9357e70fU, 0xb4ee96d2U, 0x1b9b919eU,
+    0x80c0c54fU, 0x61dc20a2U, 0x5a774b69U, 0x1c121a16U,
+    0xe293ba0aU, 0xc0a02ae5U, 0x3c22e043U, 0x121b171dU,
+    0x0e090d0bU, 0xf28bc7adU, 0x2db6a8b9U, 0x141ea9c8U,
+    0x57f11985U, 0xaf75074cU, 0xee99ddbbU, 0xa37f60fdU,
+    0xf701269fU, 0x5c72f5bcU, 0x44663bc5U, 0x5bfb7e34U,
+    0x8b432976U, 0xcb23c6dcU, 0xb6edfc68U, 0xb8e4f163U,
+    0xd731dccaU, 0x42638510U, 0x13972240U, 0x84c61120U,
+    0x854a247dU, 0xd2bb3df8U, 0xaef93211U, 0xc729a16dU,
+    0x1d9e2f4bU, 0xdcb230f3U, 0x0d8652ecU, 0x77c1e3d0U,
+    0x2bb3166cU, 0xa970b999U, 0x119448faU, 0x47e96422U,
+    0xa8fc8cc4U, 0xa0f03f1aU, 0x567d2cd8U, 0x223390efU,
+    0x87494ec7U, 0xd938d1c1U, 0x8ccaa2feU, 0x98d40b36U,
+    0xa6f581cfU, 0xa57ade28U, 0xdab78e26U, 0x3fadbfa4U,
+    0x2c3a9de4U, 0x5078920dU, 0x6a5fcc9bU, 0x547e4662U,
+    0xf68d13c2U, 0x90d8b8e8U, 0x2e39f75eU, 0x82c3aff5U,
+    0x9f5d80beU, 0x69d0937cU, 0x6fd52da9U, 0xcf2512b3U,
+    0xc8ac993bU, 0x10187da7U, 0xe89c636eU, 0xdb3bbb7bU,
+    0xcd267809U, 0x6e5918f4U, 0xec9ab701U, 0x834f9aa8U,
+    0xe6956e65U, 0xaaffe67eU, 0x21bccf08U, 0xef15e8e6U,
+    0xbae79bd9U, 0x4a6f36ceU, 0xea9f09d4U, 0x29b07cd6U,
+    0x31a4b2afU, 0x2a3f2331U, 0xc6a59430U, 0x35a266c0U,
+    0x744ebc37U, 0xfc82caa6U, 0xe090d0b0U, 0x33a7d815U,
+    0xf104984aU, 0x41ecdaf7U, 0x7fcd500eU, 0x1791f62fU,
+    0x764dd68dU, 0x43efb04dU, 0xccaa4d54U, 0xe49604dfU,
+    0x9ed1b5e3U, 0x4c6a881bU, 0xc12c1fb8U, 0x4665517fU,
+    0x9d5eea04U, 0x018c355dU, 0xfa877473U, 0xfb0b412eU,
+    0xb3671d5aU, 0x92dbd252U, 0xe9105633U, 0x6dd64713U,
+    0x9ad7618cU, 0x37a10c7aU, 0x59f8148eU, 0xeb133c89U,
+    0xcea927eeU, 0xb761c935U, 0xe11ce5edU, 0x7a47b13cU,
+    0x9cd2df59U, 0x55f2733fU, 0x1814ce79U, 0x73c737bfU,
+    0x53f7cdeaU, 0x5ffdaa5bU, 0xdf3d6f14U, 0x7844db86U,
+    0xcaaff381U, 0xb968c43eU, 0x3824342cU, 0xc2a3405fU,
+    0x161dc372U, 0xbce2250cU, 0x283c498bU, 0xff0d9541U,
+    0x39a80171U, 0x080cb3deU, 0xd8b4e49cU, 0x6456c190U,
+    0x7bcb8461U, 0xd532b670U, 0x486c5c74U, 0xd0b85742U,
+};
+static const u32 Td1[256] = {
+    0x5051f4a7U, 0x537e4165U, 0xc31a17a4U, 0x963a275eU,
+    0xcb3bab6bU, 0xf11f9d45U, 0xabacfa58U, 0x934be303U,
+    0x552030faU, 0xf6ad766dU, 0x9188cc76U, 0x25f5024cU,
+    0xfc4fe5d7U, 0xd7c52acbU, 0x80263544U, 0x8fb562a3U,
+    0x49deb15aU, 0x6725ba1bU, 0x9845ea0eU, 0xe15dfec0U,
+    0x02c32f75U, 0x12814cf0U, 0xa38d4697U, 0xc66bd3f9U,
+    0xe7038f5fU, 0x9515929cU, 0xebbf6d7aU, 0xda955259U,
+    0x2dd4be83U, 0xd3587421U, 0x2949e069U, 0x448ec9c8U,
+    0x6a75c289U, 0x78f48e79U, 0x6b99583eU, 0xdd27b971U,
+    0xb6bee14fU, 0x17f088adU, 0x66c920acU, 0xb47dce3aU,
+    0x1863df4aU, 0x82e51a31U, 0x60975133U, 0x4562537fU,
+    0xe0b16477U, 0x84bb6baeU, 0x1cfe81a0U, 0x94f9082bU,
+    0x58704868U, 0x198f45fdU, 0x8794de6cU, 0xb7527bf8U,
+    0x23ab73d3U, 0xe2724b02U, 0x57e31f8fU, 0x2a6655abU,
+    0x07b2eb28U, 0x032fb5c2U, 0x9a86c57bU, 0xa5d33708U,
+    0xf2302887U, 0xb223bfa5U, 0xba02036aU, 0x5ced1682U,
+    0x2b8acf1cU, 0x92a779b4U, 0xf0f307f2U, 0xa14e69e2U,
+    0xcd65daf4U, 0xd50605beU, 0x1fd13462U, 0x8ac4a6feU,
+    0x9d342e53U, 0xa0a2f355U, 0x32058ae1U, 0x75a4f6ebU,
+    0x390b83ecU, 0xaa4060efU, 0x065e719fU, 0x51bd6e10U,
+    0xf93e218aU, 0x3d96dd06U, 0xaedd3e05U, 0x464de6bdU,
+    0xb591548dU, 0x0571c45dU, 0x6f0406d4U, 0xff605015U,
+    0x241998fbU, 0x97d6bde9U, 0xcc894043U, 0x7767d99eU,
+    0xbdb0e842U, 0x8807898bU, 0x38e7195bU, 0xdb79c8eeU,
+    0x47a17c0aU, 0xe97c420fU, 0xc9f8841eU, 0x00000000U,
+    0x83098086U, 0x48322bedU, 0xac1e1170U, 0x4e6c5a72U,
+    0xfbfd0effU, 0x560f8538U, 0x1e3daed5U, 0x27362d39U,
+    0x640a0fd9U, 0x21685ca6U, 0xd19b5b54U, 0x3a24362eU,
+    0xb10c0a67U, 0x0f9357e7U, 0xd2b4ee96U, 0x9e1b9b91U,
+    0x4f80c0c5U, 0xa261dc20U, 0x695a774bU, 0x161c121aU,
+    0x0ae293baU, 0xe5c0a02aU, 0x433c22e0U, 0x1d121b17U,
+    0x0b0e090dU, 0xadf28bc7U, 0xb92db6a8U, 0xc8141ea9U,
+    0x8557f119U, 0x4caf7507U, 0xbbee99ddU, 0xfda37f60U,
+    0x9ff70126U, 0xbc5c72f5U, 0xc544663bU, 0x345bfb7eU,
+    0x768b4329U, 0xdccb23c6U, 0x68b6edfcU, 0x63b8e4f1U,
+    0xcad731dcU, 0x10426385U, 0x40139722U, 0x2084c611U,
+    0x7d854a24U, 0xf8d2bb3dU, 0x11aef932U, 0x6dc729a1U,
+    0x4b1d9e2fU, 0xf3dcb230U, 0xec0d8652U, 0xd077c1e3U,
+    0x6c2bb316U, 0x99a970b9U, 0xfa119448U, 0x2247e964U,
+    0xc4a8fc8cU, 0x1aa0f03fU, 0xd8567d2cU, 0xef223390U,
+    0xc787494eU, 0xc1d938d1U, 0xfe8ccaa2U, 0x3698d40bU,
+    0xcfa6f581U, 0x28a57adeU, 0x26dab78eU, 0xa43fadbfU,
+    0xe42c3a9dU, 0x0d507892U, 0x9b6a5fccU, 0x62547e46U,
+    0xc2f68d13U, 0xe890d8b8U, 0x5e2e39f7U, 0xf582c3afU,
+    0xbe9f5d80U, 0x7c69d093U, 0xa96fd52dU, 0xb3cf2512U,
+    0x3bc8ac99U, 0xa710187dU, 0x6ee89c63U, 0x7bdb3bbbU,
+    0x09cd2678U, 0xf46e5918U, 0x01ec9ab7U, 0xa8834f9aU,
+    0x65e6956eU, 0x7eaaffe6U, 0x0821bccfU, 0xe6ef15e8U,
+    0xd9bae79bU, 0xce4a6f36U, 0xd4ea9f09U, 0xd629b07cU,
+    0xaf31a4b2U, 0x312a3f23U, 0x30c6a594U, 0xc035a266U,
+    0x37744ebcU, 0xa6fc82caU, 0xb0e090d0U, 0x1533a7d8U,
+    0x4af10498U, 0xf741ecdaU, 0x0e7fcd50U, 0x2f1791f6U,
+    0x8d764dd6U, 0x4d43efb0U, 0x54ccaa4dU, 0xdfe49604U,
+    0xe39ed1b5U, 0x1b4c6a88U, 0xb8c12c1fU, 0x7f466551U,
+    0x049d5eeaU, 0x5d018c35U, 0x73fa8774U, 0x2efb0b41U,
+    0x5ab3671dU, 0x5292dbd2U, 0x33e91056U, 0x136dd647U,
+    0x8c9ad761U, 0x7a37a10cU, 0x8e59f814U, 0x89eb133cU,
+    0xeecea927U, 0x35b761c9U, 0xede11ce5U, 0x3c7a47b1U,
+    0x599cd2dfU, 0x3f55f273U, 0x791814ceU, 0xbf73c737U,
+    0xea53f7cdU, 0x5b5ffdaaU, 0x14df3d6fU, 0x867844dbU,
+    0x81caaff3U, 0x3eb968c4U, 0x2c382434U, 0x5fc2a340U,
+    0x72161dc3U, 0x0cbce225U, 0x8b283c49U, 0x41ff0d95U,
+    0x7139a801U, 0xde080cb3U, 0x9cd8b4e4U, 0x906456c1U,
+    0x617bcb84U, 0x70d532b6U, 0x74486c5cU, 0x42d0b857U,
+};
+static const u32 Td2[256] = {
+    0xa75051f4U, 0x65537e41U, 0xa4c31a17U, 0x5e963a27U,
+    0x6bcb3babU, 0x45f11f9dU, 0x58abacfaU, 0x03934be3U,
+    0xfa552030U, 0x6df6ad76U, 0x769188ccU, 0x4c25f502U,
+    0xd7fc4fe5U, 0xcbd7c52aU, 0x44802635U, 0xa38fb562U,
+    0x5a49deb1U, 0x1b6725baU, 0x0e9845eaU, 0xc0e15dfeU,
+    0x7502c32fU, 0xf012814cU, 0x97a38d46U, 0xf9c66bd3U,
+    0x5fe7038fU, 0x9c951592U, 0x7aebbf6dU, 0x59da9552U,
+    0x832dd4beU, 0x21d35874U, 0x692949e0U, 0xc8448ec9U,
+    0x896a75c2U, 0x7978f48eU, 0x3e6b9958U, 0x71dd27b9U,
+    0x4fb6bee1U, 0xad17f088U, 0xac66c920U, 0x3ab47dceU,
+    0x4a1863dfU, 0x3182e51aU, 0x33609751U, 0x7f456253U,
+    0x77e0b164U, 0xae84bb6bU, 0xa01cfe81U, 0x2b94f908U,
+    0x68587048U, 0xfd198f45U, 0x6c8794deU, 0xf8b7527bU,
+    0xd323ab73U, 0x02e2724bU, 0x8f57e31fU, 0xab2a6655U,
+    0x2807b2ebU, 0xc2032fb5U, 0x7b9a86c5U, 0x08a5d337U,
+    0x87f23028U, 0xa5b223bfU, 0x6aba0203U, 0x825ced16U,
+    0x1c2b8acfU, 0xb492a779U, 0xf2f0f307U, 0xe2a14e69U,
+    0xf4cd65daU, 0xbed50605U, 0x621fd134U, 0xfe8ac4a6U,
+    0x539d342eU, 0x55a0a2f3U, 0xe132058aU, 0xeb75a4f6U,
+    0xec390b83U, 0xefaa4060U, 0x9f065e71U, 0x1051bd6eU,
+    0x8af93e21U, 0x063d96ddU, 0x05aedd3eU, 0xbd464de6U,
+    0x8db59154U, 0x5d0571c4U, 0xd46f0406U, 0x15ff6050U,
+    0xfb241998U, 0xe997d6bdU, 0x43cc8940U, 0x9e7767d9U,
+    0x42bdb0e8U, 0x8b880789U, 0x5b38e719U, 0xeedb79c8U,
+    0x0a47a17cU, 0x0fe97c42U, 0x1ec9f884U, 0x00000000U,
+    0x86830980U, 0xed48322bU, 0x70ac1e11U, 0x724e6c5aU,
+    0xfffbfd0eU, 0x38560f85U, 0xd51e3daeU, 0x3927362dU,
+    0xd9640a0fU, 0xa621685cU, 0x54d19b5bU, 0x2e3a2436U,
+    0x67b10c0aU, 0xe70f9357U, 0x96d2b4eeU, 0x919e1b9bU,
+    0xc54f80c0U, 0x20a261dcU, 0x4b695a77U, 0x1a161c12U,
+    0xba0ae293U, 0x2ae5c0a0U, 0xe0433c22U, 0x171d121bU,
+    0x0d0b0e09U, 0xc7adf28bU, 0xa8b92db6U, 0xa9c8141eU,
+    0x198557f1U, 0x074caf75U, 0xddbbee99U, 0x60fda37fU,
+    0x269ff701U, 0xf5bc5c72U, 0x3bc54466U, 0x7e345bfbU,
+    0x29768b43U, 0xc6dccb23U, 0xfc68b6edU, 0xf163b8e4U,
+    0xdccad731U, 0x85104263U, 0x22401397U, 0x112084c6U,
+    0x247d854aU, 0x3df8d2bbU, 0x3211aef9U, 0xa16dc729U,
+    0x2f4b1d9eU, 0x30f3dcb2U, 0x52ec0d86U, 0xe3d077c1U,
+    0x166c2bb3U, 0xb999a970U, 0x48fa1194U, 0x642247e9U,
+    0x8cc4a8fcU, 0x3f1aa0f0U, 0x2cd8567dU, 0x90ef2233U,
+    0x4ec78749U, 0xd1c1d938U, 0xa2fe8ccaU, 0x0b3698d4U,
+    0x81cfa6f5U, 0xde28a57aU, 0x8e26dab7U, 0xbfa43fadU,
+    0x9de42c3aU, 0x920d5078U, 0xcc9b6a5fU, 0x4662547eU,
+    0x13c2f68dU, 0xb8e890d8U, 0xf75e2e39U, 0xaff582c3U,
+    0x80be9f5dU, 0x937c69d0U, 0x2da96fd5U, 0x12b3cf25U,
+    0x993bc8acU, 0x7da71018U, 0x636ee89cU, 0xbb7bdb3bU,
+    0x7809cd26U, 0x18f46e59U, 0xb701ec9aU, 0x9aa8834fU,
+    0x6e65e695U, 0xe67eaaffU, 0xcf0821bcU, 0xe8e6ef15U,
+    0x9bd9bae7U, 0x36ce4a6fU, 0x09d4ea9fU, 0x7cd629b0U,
+    0xb2af31a4U, 0x23312a3fU, 0x9430c6a5U, 0x66c035a2U,
+    0xbc37744eU, 0xcaa6fc82U, 0xd0b0e090U, 0xd81533a7U,
+    0x984af104U, 0xdaf741ecU, 0x500e7fcdU, 0xf62f1791U,
+    0xd68d764dU, 0xb04d43efU, 0x4d54ccaaU, 0x04dfe496U,
+    0xb5e39ed1U, 0x881b4c6aU, 0x1fb8c12cU, 0x517f4665U,
+    0xea049d5eU, 0x355d018cU, 0x7473fa87U, 0x412efb0bU,
+    0x1d5ab367U, 0xd25292dbU, 0x5633e910U, 0x47136dd6U,
+    0x618c9ad7U, 0x0c7a37a1U, 0x148e59f8U, 0x3c89eb13U,
+    0x27eecea9U, 0xc935b761U, 0xe5ede11cU, 0xb13c7a47U,
+    0xdf599cd2U, 0x733f55f2U, 0xce791814U, 0x37bf73c7U,
+    0xcdea53f7U, 0xaa5b5ffdU, 0x6f14df3dU, 0xdb867844U,
+    0xf381caafU, 0xc43eb968U, 0x342c3824U, 0x405fc2a3U,
+    0xc372161dU, 0x250cbce2U, 0x498b283cU, 0x9541ff0dU,
+    0x017139a8U, 0xb3de080cU, 0xe49cd8b4U, 0xc1906456U,
+    0x84617bcbU, 0xb670d532U, 0x5c74486cU, 0x5742d0b8U,
+};
+static const u32 Td3[256] = {
+    0xf4a75051U, 0x4165537eU, 0x17a4c31aU, 0x275e963aU,
+    0xab6bcb3bU, 0x9d45f11fU, 0xfa58abacU, 0xe303934bU,
+    0x30fa5520U, 0x766df6adU, 0xcc769188U, 0x024c25f5U,
+    0xe5d7fc4fU, 0x2acbd7c5U, 0x35448026U, 0x62a38fb5U,
+    0xb15a49deU, 0xba1b6725U, 0xea0e9845U, 0xfec0e15dU,
+    0x2f7502c3U, 0x4cf01281U, 0x4697a38dU, 0xd3f9c66bU,
+    0x8f5fe703U, 0x929c9515U, 0x6d7aebbfU, 0x5259da95U,
+    0xbe832dd4U, 0x7421d358U, 0xe0692949U, 0xc9c8448eU,
+    0xc2896a75U, 0x8e7978f4U, 0x583e6b99U, 0xb971dd27U,
+    0xe14fb6beU, 0x88ad17f0U, 0x20ac66c9U, 0xce3ab47dU,
+    0xdf4a1863U, 0x1a3182e5U, 0x51336097U, 0x537f4562U,
+    0x6477e0b1U, 0x6bae84bbU, 0x81a01cfeU, 0x082b94f9U,
+    0x48685870U, 0x45fd198fU, 0xde6c8794U, 0x7bf8b752U,
+    0x73d323abU, 0x4b02e272U, 0x1f8f57e3U, 0x55ab2a66U,
+    0xeb2807b2U, 0xb5c2032fU, 0xc57b9a86U, 0x3708a5d3U,
+    0x2887f230U, 0xbfa5b223U, 0x036aba02U, 0x16825cedU,
+    0xcf1c2b8aU, 0x79b492a7U, 0x07f2f0f3U, 0x69e2a14eU,
+    0xdaf4cd65U, 0x05bed506U, 0x34621fd1U, 0xa6fe8ac4U,
+    0x2e539d34U, 0xf355a0a2U, 0x8ae13205U, 0xf6eb75a4U,
+    0x83ec390bU, 0x60efaa40U, 0x719f065eU, 0x6e1051bdU,
+    0x218af93eU, 0xdd063d96U, 0x3e05aeddU, 0xe6bd464dU,
+    0x548db591U, 0xc45d0571U, 0x06d46f04U, 0x5015ff60U,
+    0x98fb2419U, 0xbde997d6U, 0x4043cc89U, 0xd99e7767U,
+    0xe842bdb0U, 0x898b8807U, 0x195b38e7U, 0xc8eedb79U,
+    0x7c0a47a1U, 0x420fe97cU, 0x841ec9f8U, 0x00000000U,
+    0x80868309U, 0x2bed4832U, 0x1170ac1eU, 0x5a724e6cU,
+    0x0efffbfdU, 0x8538560fU, 0xaed51e3dU, 0x2d392736U,
+    0x0fd9640aU, 0x5ca62168U, 0x5b54d19bU, 0x362e3a24U,
+    0x0a67b10cU, 0x57e70f93U, 0xee96d2b4U, 0x9b919e1bU,
+    0xc0c54f80U, 0xdc20a261U, 0x774b695aU, 0x121a161cU,
+    0x93ba0ae2U, 0xa02ae5c0U, 0x22e0433cU, 0x1b171d12U,
+    0x090d0b0eU, 0x8bc7adf2U, 0xb6a8b92dU, 0x1ea9c814U,
+    0xf1198557U, 0x75074cafU, 0x99ddbbeeU, 0x7f60fda3U,
+    0x01269ff7U, 0x72f5bc5cU, 0x663bc544U, 0xfb7e345bU,
+    0x4329768bU, 0x23c6dccbU, 0xedfc68b6U, 0xe4f163b8U,
+    0x31dccad7U, 0x63851042U, 0x97224013U, 0xc6112084U,
+    0x4a247d85U, 0xbb3df8d2U, 0xf93211aeU, 0x29a16dc7U,
+    0x9e2f4b1dU, 0xb230f3dcU, 0x8652ec0dU, 0xc1e3d077U,
+    0xb3166c2bU, 0x70b999a9U, 0x9448fa11U, 0xe9642247U,
+    0xfc8cc4a8U, 0xf03f1aa0U, 0x7d2cd856U, 0x3390ef22U,
+    0x494ec787U, 0x38d1c1d9U, 0xcaa2fe8cU, 0xd40b3698U,
+    0xf581cfa6U, 0x7ade28a5U, 0xb78e26daU, 0xadbfa43fU,
+    0x3a9de42cU, 0x78920d50U, 0x5fcc9b6aU, 0x7e466254U,
+    0x8d13c2f6U, 0xd8b8e890U, 0x39f75e2eU, 0xc3aff582U,
+    0x5d80be9fU, 0xd0937c69U, 0xd52da96fU, 0x2512b3cfU,
+    0xac993bc8U, 0x187da710U, 0x9c636ee8U, 0x3bbb7bdbU,
+    0x267809cdU, 0x5918f46eU, 0x9ab701ecU, 0x4f9aa883U,
+    0x956e65e6U, 0xffe67eaaU, 0xbccf0821U, 0x15e8e6efU,
+    0xe79bd9baU, 0x6f36ce4aU, 0x9f09d4eaU, 0xb07cd629U,
+    0xa4b2af31U, 0x3f23312aU, 0xa59430c6U, 0xa266c035U,
+    0x4ebc3774U, 0x82caa6fcU, 0x90d0b0e0U, 0xa7d81533U,
+    0x04984af1U, 0xecdaf741U, 0xcd500e7fU, 0x91f62f17U,
+    0x4dd68d76U, 0xefb04d43U, 0xaa4d54ccU, 0x9604dfe4U,
+    0xd1b5e39eU, 0x6a881b4cU, 0x2c1fb8c1U, 0x65517f46U,
+    0x5eea049dU, 0x8c355d01U, 0x877473faU, 0x0b412efbU,
+    0x671d5ab3U, 0xdbd25292U, 0x105633e9U, 0xd647136dU,
+    0xd7618c9aU, 0xa10c7a37U, 0xf8148e59U, 0x133c89ebU,
+    0xa927eeceU, 0x61c935b7U, 0x1ce5ede1U, 0x47b13c7aU,
+    0xd2df599cU, 0xf2733f55U, 0x14ce7918U, 0xc737bf73U,
+    0xf7cdea53U, 0xfdaa5b5fU, 0x3d6f14dfU, 0x44db8678U,
+    0xaff381caU, 0x68c43eb9U, 0x24342c38U, 0xa3405fc2U,
+    0x1dc37216U, 0xe2250cbcU, 0x3c498b28U, 0x0d9541ffU,
+    0xa8017139U, 0x0cb3de08U, 0xb4e49cd8U, 0x56c19064U,
+    0xcb84617bU, 0x32b670d5U, 0x6c5c7448U, 0xb85742d0U,
+};
+static const u8 Td4[256] = {
+    0x52U, 0x09U, 0x6aU, 0xd5U, 0x30U, 0x36U, 0xa5U, 0x38U,
+    0xbfU, 0x40U, 0xa3U, 0x9eU, 0x81U, 0xf3U, 0xd7U, 0xfbU,
+    0x7cU, 0xe3U, 0x39U, 0x82U, 0x9bU, 0x2fU, 0xffU, 0x87U,
+    0x34U, 0x8eU, 0x43U, 0x44U, 0xc4U, 0xdeU, 0xe9U, 0xcbU,
+    0x54U, 0x7bU, 0x94U, 0x32U, 0xa6U, 0xc2U, 0x23U, 0x3dU,
+    0xeeU, 0x4cU, 0x95U, 0x0bU, 0x42U, 0xfaU, 0xc3U, 0x4eU,
+    0x08U, 0x2eU, 0xa1U, 0x66U, 0x28U, 0xd9U, 0x24U, 0xb2U,
+    0x76U, 0x5bU, 0xa2U, 0x49U, 0x6dU, 0x8bU, 0xd1U, 0x25U,
+    0x72U, 0xf8U, 0xf6U, 0x64U, 0x86U, 0x68U, 0x98U, 0x16U,
+    0xd4U, 0xa4U, 0x5cU, 0xccU, 0x5dU, 0x65U, 0xb6U, 0x92U,
+    0x6cU, 0x70U, 0x48U, 0x50U, 0xfdU, 0xedU, 0xb9U, 0xdaU,
+    0x5eU, 0x15U, 0x46U, 0x57U, 0xa7U, 0x8dU, 0x9dU, 0x84U,
+    0x90U, 0xd8U, 0xabU, 0x00U, 0x8cU, 0xbcU, 0xd3U, 0x0aU,
+    0xf7U, 0xe4U, 0x58U, 0x05U, 0xb8U, 0xb3U, 0x45U, 0x06U,
+    0xd0U, 0x2cU, 0x1eU, 0x8fU, 0xcaU, 0x3fU, 0x0fU, 0x02U,
+    0xc1U, 0xafU, 0xbdU, 0x03U, 0x01U, 0x13U, 0x8aU, 0x6bU,
+    0x3aU, 0x91U, 0x11U, 0x41U, 0x4fU, 0x67U, 0xdcU, 0xeaU,
+    0x97U, 0xf2U, 0xcfU, 0xceU, 0xf0U, 0xb4U, 0xe6U, 0x73U,
+    0x96U, 0xacU, 0x74U, 0x22U, 0xe7U, 0xadU, 0x35U, 0x85U,
+    0xe2U, 0xf9U, 0x37U, 0xe8U, 0x1cU, 0x75U, 0xdfU, 0x6eU,
+    0x47U, 0xf1U, 0x1aU, 0x71U, 0x1dU, 0x29U, 0xc5U, 0x89U,
+    0x6fU, 0xb7U, 0x62U, 0x0eU, 0xaaU, 0x18U, 0xbeU, 0x1bU,
+    0xfcU, 0x56U, 0x3eU, 0x4bU, 0xc6U, 0xd2U, 0x79U, 0x20U,
+    0x9aU, 0xdbU, 0xc0U, 0xfeU, 0x78U, 0xcdU, 0x5aU, 0xf4U,
+    0x1fU, 0xddU, 0xa8U, 0x33U, 0x88U, 0x07U, 0xc7U, 0x31U,
+    0xb1U, 0x12U, 0x10U, 0x59U, 0x27U, 0x80U, 0xecU, 0x5fU,
+    0x60U, 0x51U, 0x7fU, 0xa9U, 0x19U, 0xb5U, 0x4aU, 0x0dU,
+    0x2dU, 0xe5U, 0x7aU, 0x9fU, 0x93U, 0xc9U, 0x9cU, 0xefU,
+    0xa0U, 0xe0U, 0x3bU, 0x4dU, 0xaeU, 0x2aU, 0xf5U, 0xb0U,
+    0xc8U, 0xebU, 0xbbU, 0x3cU, 0x83U, 0x53U, 0x99U, 0x61U,
+    0x17U, 0x2bU, 0x04U, 0x7eU, 0xbaU, 0x77U, 0xd6U, 0x26U,
+    0xe1U, 0x69U, 0x14U, 0x63U, 0x55U, 0x21U, 0x0cU, 0x7dU,
+};
+static const u32 rcon[] = {
+    0x01000000, 0x02000000, 0x04000000, 0x08000000,
+    0x10000000, 0x20000000, 0x40000000, 0x80000000,
+    0x1B000000, 0x36000000, /* for 128-bit blocks, Rijndael never uses more than 10 rcon values */
+};
+
+/**
+ * Expand the cipher key into the encryption key schedule.
+ */
+int AES_set_encrypt_key(const unsigned char *userKey, const int bits,
+                        AES_KEY *key)
+{
+
+    u32 *rk;
+    int i = 0;
+    u32 temp;
+
+    if (!userKey || !key)
+        return -1;
+    if (bits != 128 && bits != 192 && bits != 256)
+        return -2;
+
+    rk = key->rd_key;
+
+    if (bits == 128)
+        key->rounds = 10;
+    else if (bits == 192)
+        key->rounds = 12;
+    else
+        key->rounds = 14;
+
+    rk[0] = GETU32(userKey     );
+    rk[1] = GETU32(userKey +  4);
+    rk[2] = GETU32(userKey +  8);
+    rk[3] = GETU32(userKey + 12);
+    if (bits == 128) {
+        while (1) {
+            temp  = rk[3];
+            rk[4] = rk[0] ^
+                (Te2[(temp >> 16) & 0xff] & 0xff000000) ^
+                (Te3[(temp >>  8) & 0xff] & 0x00ff0000) ^
+                (Te0[(temp      ) & 0xff] & 0x0000ff00) ^
+                (Te1[(temp >> 24)       ] & 0x000000ff) ^
+                rcon[i];
+            rk[5] = rk[1] ^ rk[4];
+            rk[6] = rk[2] ^ rk[5];
+            rk[7] = rk[3] ^ rk[6];
+            if (++i == 10) {
+                return 0;
+            }
+            rk += 4;
+        }
+    }
+    rk[4] = GETU32(userKey + 16);
+    rk[5] = GETU32(userKey + 20);
+    if (bits == 192) {
+        while (1) {
+            temp = rk[ 5];
+            rk[ 6] = rk[ 0] ^
+                (Te2[(temp >> 16) & 0xff] & 0xff000000) ^
+                (Te3[(temp >>  8) & 0xff] & 0x00ff0000) ^
+                (Te0[(temp      ) & 0xff] & 0x0000ff00) ^
+                (Te1[(temp >> 24)       ] & 0x000000ff) ^
+                rcon[i];
+            rk[ 7] = rk[ 1] ^ rk[ 6];
+            rk[ 8] = rk[ 2] ^ rk[ 7];
+            rk[ 9] = rk[ 3] ^ rk[ 8];
+            if (++i == 8) {
+                return 0;
+            }
+            rk[10] = rk[ 4] ^ rk[ 9];
+            rk[11] = rk[ 5] ^ rk[10];
+            rk += 6;
+        }
+    }
+    rk[6] = GETU32(userKey + 24);
+    rk[7] = GETU32(userKey + 28);
+    if (bits == 256) {
+        while (1) {
+            temp = rk[ 7];
+            rk[ 8] = rk[ 0] ^
+                (Te2[(temp >> 16) & 0xff] & 0xff000000) ^
+                (Te3[(temp >>  8) & 0xff] & 0x00ff0000) ^
+                (Te0[(temp      ) & 0xff] & 0x0000ff00) ^
+                (Te1[(temp >> 24)       ] & 0x000000ff) ^
+                rcon[i];
+            rk[ 9] = rk[ 1] ^ rk[ 8];
+            rk[10] = rk[ 2] ^ rk[ 9];
+            rk[11] = rk[ 3] ^ rk[10];
+            if (++i == 7) {
+                return 0;
+            }
+            temp = rk[11];
+            rk[12] = rk[ 4] ^
+                (Te2[(temp >> 24)       ] & 0xff000000) ^
+                (Te3[(temp >> 16) & 0xff] & 0x00ff0000) ^
+                (Te0[(temp >>  8) & 0xff] & 0x0000ff00) ^
+                (Te1[(temp      ) & 0xff] & 0x000000ff);
+            rk[13] = rk[ 5] ^ rk[12];
+            rk[14] = rk[ 6] ^ rk[13];
+            rk[15] = rk[ 7] ^ rk[14];
+
+            rk += 8;
+            }
+    }
+    return 0;
+}
+
+/**
+ * Expand the cipher key into the decryption key schedule.
+ */
+int AES_set_decrypt_key(const unsigned char *userKey, const int bits,
+                        AES_KEY *key)
+{
+
+    u32 *rk;
+    int i, j, status;
+    u32 temp;
+
+    /* first, start with an encryption schedule */
+    status = AES_set_encrypt_key(userKey, bits, key);
+    if (status < 0)
+        return status;
+
+    rk = key->rd_key;
+
+    /* invert the order of the round keys: */
+    for (i = 0, j = 4*(key->rounds); i < j; i += 4, j -= 4) {
+        temp = rk[i    ]; rk[i    ] = rk[j    ]; rk[j    ] = temp;
+        temp = rk[i + 1]; rk[i + 1] = rk[j + 1]; rk[j + 1] = temp;
+        temp = rk[i + 2]; rk[i + 2] = rk[j + 2]; rk[j + 2] = temp;
+        temp = rk[i + 3]; rk[i + 3] = rk[j + 3]; rk[j + 3] = temp;
+    }
+    /* apply the inverse MixColumn transform to all round keys but the first and the last: */
+    for (i = 1; i < (key->rounds); i++) {
+        rk += 4;
+        rk[0] =
+            Td0[Te1[(rk[0] >> 24)       ] & 0xff] ^
+            Td1[Te1[(rk[0] >> 16) & 0xff] & 0xff] ^
+            Td2[Te1[(rk[0] >>  8) & 0xff] & 0xff] ^
+            Td3[Te1[(rk[0]      ) & 0xff] & 0xff];
+        rk[1] =
+            Td0[Te1[(rk[1] >> 24)       ] & 0xff] ^
+            Td1[Te1[(rk[1] >> 16) & 0xff] & 0xff] ^
+            Td2[Te1[(rk[1] >>  8) & 0xff] & 0xff] ^
+            Td3[Te1[(rk[1]      ) & 0xff] & 0xff];
+        rk[2] =
+            Td0[Te1[(rk[2] >> 24)       ] & 0xff] ^
+            Td1[Te1[(rk[2] >> 16) & 0xff] & 0xff] ^
+            Td2[Te1[(rk[2] >>  8) & 0xff] & 0xff] ^
+            Td3[Te1[(rk[2]      ) & 0xff] & 0xff];
+        rk[3] =
+            Td0[Te1[(rk[3] >> 24)       ] & 0xff] ^
+            Td1[Te1[(rk[3] >> 16) & 0xff] & 0xff] ^
+            Td2[Te1[(rk[3] >>  8) & 0xff] & 0xff] ^
+            Td3[Te1[(rk[3]      ) & 0xff] & 0xff];
+    }
+    return 0;
+}
+
+/*
+ * Encrypt a single block
+ * in and out can overlap
+ */
+void AES_encrypt(const unsigned char *in, unsigned char *out,
+                 const AES_KEY *key) {
+
+    const u32 *rk;
+    u32 s0, s1, s2, s3, t0, t1, t2, t3;
+#ifndef FULL_UNROLL
+    int r;
+#endif /* ?FULL_UNROLL */
+
+    assert(in && out && key);
+    rk = key->rd_key;
+
+    /*
+     * map byte array block to cipher state
+     * and add initial round key:
+     */
+    s0 = GETU32(in     ) ^ rk[0];
+    s1 = GETU32(in +  4) ^ rk[1];
+    s2 = GETU32(in +  8) ^ rk[2];
+    s3 = GETU32(in + 12) ^ rk[3];
+#ifdef FULL_UNROLL
+    /* round 1: */
+    t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >>  8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[ 4];
+    t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >>  8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[ 5];
+    t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >>  8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[ 6];
+    t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >>  8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[ 7];
+    /* round 2: */
+    s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >>  8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[ 8];
+    s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >>  8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[ 9];
+    s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >>  8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[10];
+    s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >>  8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[11];
+    /* round 3: */
+    t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >>  8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[12];
+    t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >>  8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[13];
+    t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >>  8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[14];
+    t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >>  8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[15];
+    /* round 4: */
+    s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >>  8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[16];
+    s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >>  8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[17];
+    s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >>  8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[18];
+    s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >>  8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[19];
+    /* round 5: */
+    t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >>  8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[20];
+    t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >>  8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[21];
+    t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >>  8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[22];
+    t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >>  8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[23];
+    /* round 6: */
+    s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >>  8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[24];
+    s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >>  8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[25];
+    s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >>  8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[26];
+    s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >>  8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[27];
+    /* round 7: */
+    t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >>  8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[28];
+    t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >>  8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[29];
+    t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >>  8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[30];
+    t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >>  8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[31];
+    /* round 8: */
+    s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >>  8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[32];
+    s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >>  8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[33];
+    s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >>  8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[34];
+    s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >>  8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[35];
+    /* round 9: */
+    t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >>  8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[36];
+    t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >>  8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[37];
+    t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >>  8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[38];
+    t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >>  8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[39];
+    if (key->rounds > 10) {
+        /* round 10: */
+        s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >>  8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[40];
+        s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >>  8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[41];
+        s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >>  8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[42];
+        s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >>  8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[43];
+        /* round 11: */
+        t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >>  8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[44];
+        t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >>  8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[45];
+        t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >>  8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[46];
+        t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >>  8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[47];
+        if (key->rounds > 12) {
+            /* round 12: */
+            s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >>  8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[48];
+            s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >>  8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[49];
+            s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >>  8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[50];
+            s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >>  8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[51];
+            /* round 13: */
+            t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >>  8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[52];
+            t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >>  8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[53];
+            t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >>  8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[54];
+            t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >>  8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[55];
+        }
+    }
+    rk += key->rounds << 2;
+#else  /* !FULL_UNROLL */
+    /*
+     * Nr - 1 full rounds:
+     */
+    r = key->rounds >> 1;
+    for (;;) {
+        t0 =
+            Te0[(s0 >> 24)       ] ^
+            Te1[(s1 >> 16) & 0xff] ^
+            Te2[(s2 >>  8) & 0xff] ^
+            Te3[(s3      ) & 0xff] ^
+            rk[4];
+        t1 =
+            Te0[(s1 >> 24)       ] ^
+            Te1[(s2 >> 16) & 0xff] ^
+            Te2[(s3 >>  8) & 0xff] ^
+            Te3[(s0      ) & 0xff] ^
+            rk[5];
+        t2 =
+            Te0[(s2 >> 24)       ] ^
+            Te1[(s3 >> 16) & 0xff] ^
+            Te2[(s0 >>  8) & 0xff] ^
+            Te3[(s1      ) & 0xff] ^
+            rk[6];
+        t3 =
+            Te0[(s3 >> 24)       ] ^
+            Te1[(s0 >> 16) & 0xff] ^
+            Te2[(s1 >>  8) & 0xff] ^
+            Te3[(s2      ) & 0xff] ^
+            rk[7];
+
+        rk += 8;
+        if (--r == 0) {
+            break;
+        }
+
+        s0 =
+            Te0[(t0 >> 24)       ] ^
+            Te1[(t1 >> 16) & 0xff] ^
+            Te2[(t2 >>  8) & 0xff] ^
+            Te3[(t3      ) & 0xff] ^
+            rk[0];
+        s1 =
+            Te0[(t1 >> 24)       ] ^
+            Te1[(t2 >> 16) & 0xff] ^
+            Te2[(t3 >>  8) & 0xff] ^
+            Te3[(t0      ) & 0xff] ^
+            rk[1];
+        s2 =
+            Te0[(t2 >> 24)       ] ^
+            Te1[(t3 >> 16) & 0xff] ^
+            Te2[(t0 >>  8) & 0xff] ^
+            Te3[(t1      ) & 0xff] ^
+            rk[2];
+        s3 =
+            Te0[(t3 >> 24)       ] ^
+            Te1[(t0 >> 16) & 0xff] ^
+            Te2[(t1 >>  8) & 0xff] ^
+            Te3[(t2      ) & 0xff] ^
+            rk[3];
+    }
+#endif /* ?FULL_UNROLL */
+    /*
+     * apply last round and
+     * map cipher state to byte array block:
+     */
+    s0 =
+        (Te2[(t0 >> 24)       ] & 0xff000000) ^
+        (Te3[(t1 >> 16) & 0xff] & 0x00ff0000) ^
+        (Te0[(t2 >>  8) & 0xff] & 0x0000ff00) ^
+        (Te1[(t3      ) & 0xff] & 0x000000ff) ^
+        rk[0];
+    PUTU32(out     , s0);
+    s1 =
+        (Te2[(t1 >> 24)       ] & 0xff000000) ^
+        (Te3[(t2 >> 16) & 0xff] & 0x00ff0000) ^
+        (Te0[(t3 >>  8) & 0xff] & 0x0000ff00) ^
+        (Te1[(t0      ) & 0xff] & 0x000000ff) ^
+        rk[1];
+    PUTU32(out +  4, s1);
+    s2 =
+        (Te2[(t2 >> 24)       ] & 0xff000000) ^
+        (Te3[(t3 >> 16) & 0xff] & 0x00ff0000) ^
+        (Te0[(t0 >>  8) & 0xff] & 0x0000ff00) ^
+        (Te1[(t1      ) & 0xff] & 0x000000ff) ^
+        rk[2];
+    PUTU32(out +  8, s2);
+    s3 =
+        (Te2[(t3 >> 24)       ] & 0xff000000) ^
+        (Te3[(t0 >> 16) & 0xff] & 0x00ff0000) ^
+        (Te0[(t1 >>  8) & 0xff] & 0x0000ff00) ^
+        (Te1[(t2      ) & 0xff] & 0x000000ff) ^
+        rk[3];
+    PUTU32(out + 12, s3);
+}
+
+/*
+ * Decrypt a single block
+ * in and out can overlap
+ */
+void AES_decrypt(const unsigned char *in, unsigned char *out,
+                 const AES_KEY *key)
+{
+
+    const u32 *rk;
+    u32 s0, s1, s2, s3, t0, t1, t2, t3;
+#ifndef FULL_UNROLL
+    int r;
+#endif /* ?FULL_UNROLL */
+
+    assert(in && out && key);
+    rk = key->rd_key;
+
+    /*
+     * map byte array block to cipher state
+     * and add initial round key:
+     */
+    s0 = GETU32(in     ) ^ rk[0];
+    s1 = GETU32(in +  4) ^ rk[1];
+    s2 = GETU32(in +  8) ^ rk[2];
+    s3 = GETU32(in + 12) ^ rk[3];
+#ifdef FULL_UNROLL
+    /* round 1: */
+    t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >>  8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[ 4];
+    t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >>  8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[ 5];
+    t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >>  8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[ 6];
+    t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >>  8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[ 7];
+    /* round 2: */
+    s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >>  8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[ 8];
+    s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >>  8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[ 9];
+    s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >>  8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[10];
+    s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >>  8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[11];
+    /* round 3: */
+    t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >>  8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[12];
+    t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >>  8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[13];
+    t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >>  8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[14];
+    t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >>  8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[15];
+    /* round 4: */
+    s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >>  8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[16];
+    s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >>  8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[17];
+    s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >>  8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[18];
+    s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >>  8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[19];
+    /* round 5: */
+    t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >>  8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[20];
+    t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >>  8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[21];
+    t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >>  8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[22];
+    t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >>  8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[23];
+    /* round 6: */
+    s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >>  8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[24];
+    s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >>  8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[25];
+    s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >>  8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[26];
+    s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >>  8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[27];
+    /* round 7: */
+    t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >>  8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[28];
+    t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >>  8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[29];
+    t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >>  8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[30];
+    t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >>  8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[31];
+    /* round 8: */
+    s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >>  8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[32];
+    s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >>  8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[33];
+    s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >>  8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[34];
+    s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >>  8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[35];
+    /* round 9: */
+    t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >>  8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[36];
+    t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >>  8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[37];
+    t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >>  8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[38];
+    t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >>  8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[39];
+    if (key->rounds > 10) {
+        /* round 10: */
+        s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >>  8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[40];
+        s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >>  8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[41];
+        s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >>  8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[42];
+        s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >>  8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[43];
+        /* round 11: */
+        t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >>  8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[44];
+        t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >>  8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[45];
+        t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >>  8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[46];
+        t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >>  8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[47];
+        if (key->rounds > 12) {
+            /* round 12: */
+            s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >>  8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[48];
+            s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >>  8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[49];
+            s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >>  8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[50];
+            s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >>  8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[51];
+            /* round 13: */
+            t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >>  8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[52];
+            t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >>  8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[53];
+            t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >>  8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[54];
+            t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >>  8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[55];
+        }
+    }
+    rk += key->rounds << 2;
+#else  /* !FULL_UNROLL */
+    /*
+     * Nr - 1 full rounds:
+     */
+    r = key->rounds >> 1;
+    for (;;) {
+        t0 =
+            Td0[(s0 >> 24)       ] ^
+            Td1[(s3 >> 16) & 0xff] ^
+            Td2[(s2 >>  8) & 0xff] ^
+            Td3[(s1      ) & 0xff] ^
+            rk[4];
+        t1 =
+            Td0[(s1 >> 24)       ] ^
+            Td1[(s0 >> 16) & 0xff] ^
+            Td2[(s3 >>  8) & 0xff] ^
+            Td3[(s2      ) & 0xff] ^
+            rk[5];
+        t2 =
+            Td0[(s2 >> 24)       ] ^
+            Td1[(s1 >> 16) & 0xff] ^
+            Td2[(s0 >>  8) & 0xff] ^
+            Td3[(s3      ) & 0xff] ^
+            rk[6];
+        t3 =
+            Td0[(s3 >> 24)       ] ^
+            Td1[(s2 >> 16) & 0xff] ^
+            Td2[(s1 >>  8) & 0xff] ^
+            Td3[(s0      ) & 0xff] ^
+            rk[7];
+
+        rk += 8;
+        if (--r == 0) {
+            break;
+        }
+
+        s0 =
+            Td0[(t0 >> 24)       ] ^
+            Td1[(t3 >> 16) & 0xff] ^
+            Td2[(t2 >>  8) & 0xff] ^
+            Td3[(t1      ) & 0xff] ^
+            rk[0];
+        s1 =
+            Td0[(t1 >> 24)       ] ^
+            Td1[(t0 >> 16) & 0xff] ^
+            Td2[(t3 >>  8) & 0xff] ^
+            Td3[(t2      ) & 0xff] ^
+            rk[1];
+        s2 =
+            Td0[(t2 >> 24)       ] ^
+            Td1[(t1 >> 16) & 0xff] ^
+            Td2[(t0 >>  8) & 0xff] ^
+            Td3[(t3      ) & 0xff] ^
+            rk[2];
+        s3 =
+            Td0[(t3 >> 24)       ] ^
+            Td1[(t2 >> 16) & 0xff] ^
+            Td2[(t1 >>  8) & 0xff] ^
+            Td3[(t0      ) & 0xff] ^
+            rk[3];
+    }
+#endif /* ?FULL_UNROLL */
+    /*
+     * apply last round and
+     * map cipher state to byte array block:
+     */
+    s0 =
+        ((u32)Td4[(t0 >> 24)       ] << 24) ^
+        ((u32)Td4[(t3 >> 16) & 0xff] << 16) ^
+        ((u32)Td4[(t2 >>  8) & 0xff] <<  8) ^
+        ((u32)Td4[(t1      ) & 0xff])       ^
+        rk[0];
+    PUTU32(out     , s0);
+    s1 =
+        ((u32)Td4[(t1 >> 24)       ] << 24) ^
+        ((u32)Td4[(t0 >> 16) & 0xff] << 16) ^
+        ((u32)Td4[(t3 >>  8) & 0xff] <<  8) ^
+        ((u32)Td4[(t2      ) & 0xff])       ^
+        rk[1];
+    PUTU32(out +  4, s1);
+    s2 =
+        ((u32)Td4[(t2 >> 24)       ] << 24) ^
+        ((u32)Td4[(t1 >> 16) & 0xff] << 16) ^
+        ((u32)Td4[(t0 >>  8) & 0xff] <<  8) ^
+        ((u32)Td4[(t3      ) & 0xff])       ^
+        rk[2];
+    PUTU32(out +  8, s2);
+    s3 =
+        ((u32)Td4[(t3 >> 24)       ] << 24) ^
+        ((u32)Td4[(t2 >> 16) & 0xff] << 16) ^
+        ((u32)Td4[(t1 >>  8) & 0xff] <<  8) ^
+        ((u32)Td4[(t0      ) & 0xff])       ^
+        rk[3];
+    PUTU32(out + 12, s3);
+}
+
+#else /* AES_ASM */
+
+static const u8 Te4[256] = {
+    0x63U, 0x7cU, 0x77U, 0x7bU, 0xf2U, 0x6bU, 0x6fU, 0xc5U,
+    0x30U, 0x01U, 0x67U, 0x2bU, 0xfeU, 0xd7U, 0xabU, 0x76U,
+    0xcaU, 0x82U, 0xc9U, 0x7dU, 0xfaU, 0x59U, 0x47U, 0xf0U,
+    0xadU, 0xd4U, 0xa2U, 0xafU, 0x9cU, 0xa4U, 0x72U, 0xc0U,
+    0xb7U, 0xfdU, 0x93U, 0x26U, 0x36U, 0x3fU, 0xf7U, 0xccU,
+    0x34U, 0xa5U, 0xe5U, 0xf1U, 0x71U, 0xd8U, 0x31U, 0x15U,
+    0x04U, 0xc7U, 0x23U, 0xc3U, 0x18U, 0x96U, 0x05U, 0x9aU,
+    0x07U, 0x12U, 0x80U, 0xe2U, 0xebU, 0x27U, 0xb2U, 0x75U,
+    0x09U, 0x83U, 0x2cU, 0x1aU, 0x1bU, 0x6eU, 0x5aU, 0xa0U,
+    0x52U, 0x3bU, 0xd6U, 0xb3U, 0x29U, 0xe3U, 0x2fU, 0x84U,
+    0x53U, 0xd1U, 0x00U, 0xedU, 0x20U, 0xfcU, 0xb1U, 0x5bU,
+    0x6aU, 0xcbU, 0xbeU, 0x39U, 0x4aU, 0x4cU, 0x58U, 0xcfU,
+    0xd0U, 0xefU, 0xaaU, 0xfbU, 0x43U, 0x4dU, 0x33U, 0x85U,
+    0x45U, 0xf9U, 0x02U, 0x7fU, 0x50U, 0x3cU, 0x9fU, 0xa8U,
+    0x51U, 0xa3U, 0x40U, 0x8fU, 0x92U, 0x9dU, 0x38U, 0xf5U,
+    0xbcU, 0xb6U, 0xdaU, 0x21U, 0x10U, 0xffU, 0xf3U, 0xd2U,
+    0xcdU, 0x0cU, 0x13U, 0xecU, 0x5fU, 0x97U, 0x44U, 0x17U,
+    0xc4U, 0xa7U, 0x7eU, 0x3dU, 0x64U, 0x5dU, 0x19U, 0x73U,
+    0x60U, 0x81U, 0x4fU, 0xdcU, 0x22U, 0x2aU, 0x90U, 0x88U,
+    0x46U, 0xeeU, 0xb8U, 0x14U, 0xdeU, 0x5eU, 0x0bU, 0xdbU,
+    0xe0U, 0x32U, 0x3aU, 0x0aU, 0x49U, 0x06U, 0x24U, 0x5cU,
+    0xc2U, 0xd3U, 0xacU, 0x62U, 0x91U, 0x95U, 0xe4U, 0x79U,
+    0xe7U, 0xc8U, 0x37U, 0x6dU, 0x8dU, 0xd5U, 0x4eU, 0xa9U,
+    0x6cU, 0x56U, 0xf4U, 0xeaU, 0x65U, 0x7aU, 0xaeU, 0x08U,
+    0xbaU, 0x78U, 0x25U, 0x2eU, 0x1cU, 0xa6U, 0xb4U, 0xc6U,
+    0xe8U, 0xddU, 0x74U, 0x1fU, 0x4bU, 0xbdU, 0x8bU, 0x8aU,
+    0x70U, 0x3eU, 0xb5U, 0x66U, 0x48U, 0x03U, 0xf6U, 0x0eU,
+    0x61U, 0x35U, 0x57U, 0xb9U, 0x86U, 0xc1U, 0x1dU, 0x9eU,
+    0xe1U, 0xf8U, 0x98U, 0x11U, 0x69U, 0xd9U, 0x8eU, 0x94U,
+    0x9bU, 0x1eU, 0x87U, 0xe9U, 0xceU, 0x55U, 0x28U, 0xdfU,
+    0x8cU, 0xa1U, 0x89U, 0x0dU, 0xbfU, 0xe6U, 0x42U, 0x68U,
+    0x41U, 0x99U, 0x2dU, 0x0fU, 0xb0U, 0x54U, 0xbbU, 0x16U
+};
+static const u32 rcon[] = {
+    0x01000000, 0x02000000, 0x04000000, 0x08000000,
+    0x10000000, 0x20000000, 0x40000000, 0x80000000,
+    0x1B000000, 0x36000000, /* for 128-bit blocks, Rijndael never uses more than 10 rcon values */
+};
+
+/**
+ * Expand the cipher key into the encryption key schedule.
+ */
+int AES_set_encrypt_key(const unsigned char *userKey, const int bits,
+                        AES_KEY *key)
+{
+    u32 *rk;
+    int i = 0;
+    u32 temp;
+
+    if (!userKey || !key)
+        return -1;
+    if (bits != 128 && bits != 192 && bits != 256)
+        return -2;
+
+    rk = key->rd_key;
+
+    if (bits == 128)
+        key->rounds = 10;
+    else if (bits == 192)
+        key->rounds = 12;
+    else
+        key->rounds = 14;
+
+    rk[0] = GETU32(userKey     );
+    rk[1] = GETU32(userKey +  4);
+    rk[2] = GETU32(userKey +  8);
+    rk[3] = GETU32(userKey + 12);
+    if (bits == 128) {
+        while (1) {
+            temp  = rk[3];
+            rk[4] = rk[0] ^
+                ((u32)Te4[(temp >> 16) & 0xff] << 24) ^
+                ((u32)Te4[(temp >>  8) & 0xff] << 16) ^
+                ((u32)Te4[(temp      ) & 0xff] << 8) ^
+                ((u32)Te4[(temp >> 24)       ]) ^
+                rcon[i];
+            rk[5] = rk[1] ^ rk[4];
+            rk[6] = rk[2] ^ rk[5];
+            rk[7] = rk[3] ^ rk[6];
+            if (++i == 10) {
+                return 0;
+            }
+            rk += 4;
+        }
+    }
+    rk[4] = GETU32(userKey + 16);
+    rk[5] = GETU32(userKey + 20);
+    if (bits == 192) {
+        while (1) {
+            temp = rk[ 5];
+            rk[ 6] = rk[ 0] ^
+                ((u32)Te4[(temp >> 16) & 0xff] << 24) ^
+                ((u32)Te4[(temp >>  8) & 0xff] << 16) ^
+                ((u32)Te4[(temp      ) & 0xff] << 8) ^
+                ((u32)Te4[(temp >> 24)       ]) ^
+                rcon[i];
+            rk[ 7] = rk[ 1] ^ rk[ 6];
+            rk[ 8] = rk[ 2] ^ rk[ 7];
+            rk[ 9] = rk[ 3] ^ rk[ 8];
+            if (++i == 8) {
+                return 0;
+            }
+            rk[10] = rk[ 4] ^ rk[ 9];
+            rk[11] = rk[ 5] ^ rk[10];
+            rk += 6;
+        }
+    }
+    rk[6] = GETU32(userKey + 24);
+    rk[7] = GETU32(userKey + 28);
+    if (bits == 256) {
+        while (1) {
+            temp = rk[ 7];
+            rk[ 8] = rk[ 0] ^
+                ((u32)Te4[(temp >> 16) & 0xff] << 24) ^
+                ((u32)Te4[(temp >>  8) & 0xff] << 16) ^
+                ((u32)Te4[(temp      ) & 0xff] << 8) ^
+                ((u32)Te4[(temp >> 24)       ]) ^
+                rcon[i];
+            rk[ 9] = rk[ 1] ^ rk[ 8];
+            rk[10] = rk[ 2] ^ rk[ 9];
+            rk[11] = rk[ 3] ^ rk[10];
+            if (++i == 7) {
+                return 0;
+            }
+            temp = rk[11];
+            rk[12] = rk[ 4] ^
+                ((u32)Te4[(temp >> 24)       ] << 24) ^
+                ((u32)Te4[(temp >> 16) & 0xff] << 16) ^
+                ((u32)Te4[(temp >>  8) & 0xff] << 8) ^
+                ((u32)Te4[(temp      ) & 0xff]);
+            rk[13] = rk[ 5] ^ rk[12];
+            rk[14] = rk[ 6] ^ rk[13];
+            rk[15] = rk[ 7] ^ rk[14];
+
+            rk += 8;
+        }
+    }
+    return 0;
+}
+
+/**
+ * Expand the cipher key into the decryption key schedule.
+ */
+int AES_set_decrypt_key(const unsigned char *userKey, const int bits,
+                        AES_KEY *key)
+{
+
+    u32 *rk;
+    int i, j, status;
+    u32 temp;
+
+    /* first, start with an encryption schedule */
+    status = AES_set_encrypt_key(userKey, bits, key);
+    if (status < 0)
+        return status;
+
+    rk = key->rd_key;
+
+    /* invert the order of the round keys: */
+    for (i = 0, j = 4*(key->rounds); i < j; i += 4, j -= 4) {
+        temp = rk[i    ]; rk[i    ] = rk[j    ]; rk[j    ] = temp;
+        temp = rk[i + 1]; rk[i + 1] = rk[j + 1]; rk[j + 1] = temp;
+        temp = rk[i + 2]; rk[i + 2] = rk[j + 2]; rk[j + 2] = temp;
+        temp = rk[i + 3]; rk[i + 3] = rk[j + 3]; rk[j + 3] = temp;
+    }
+    /* apply the inverse MixColumn transform to all round keys but the first and the last: */
+    for (i = 1; i < (key->rounds); i++) {
+        rk += 4;
+        for (j = 0; j < 4; j++) {
+            u32 tp1, tp2, tp4, tp8, tp9, tpb, tpd, tpe, m;
+
+            tp1 = rk[j];
+            m = tp1 & 0x80808080;
+            tp2 = ((tp1 & 0x7f7f7f7f) << 1) ^
+                ((m - (m >> 7)) & 0x1b1b1b1b);
+            m = tp2 & 0x80808080;
+            tp4 = ((tp2 & 0x7f7f7f7f) << 1) ^
+                ((m - (m >> 7)) & 0x1b1b1b1b);
+            m = tp4 & 0x80808080;
+            tp8 = ((tp4 & 0x7f7f7f7f) << 1) ^
+                ((m - (m >> 7)) & 0x1b1b1b1b);
+            tp9 = tp8 ^ tp1;
+            tpb = tp9 ^ tp2;
+            tpd = tp9 ^ tp4;
+            tpe = tp8 ^ tp4 ^ tp2;
+#if defined(ROTATE)
+            rk[j] = tpe ^ ROTATE(tpd,16) ^
+                ROTATE(tp9,24) ^ ROTATE(tpb,8);
+#else
+            rk[j] = tpe ^ (tpd >> 16) ^ (tpd << 16) ^
+                (tp9 >> 8) ^ (tp9 << 24) ^
+                (tpb >> 24) ^ (tpb << 8);
+#endif
+        }
+    }
+    return 0;
+}
+
+#endif /* AES_ASM */
diff --git a/crypto/aes/aes_ecb.c b/crypto/aes/aes_ecb.c
new file mode 100644
index 000000000..29bfc1ad6
--- /dev/null
+++ b/crypto/aes/aes_ecb.c
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2002-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+
+#include 
+#include "aes_locl.h"
+
+void AES_ecb_encrypt(const unsigned char *in, unsigned char *out,
+                     const AES_KEY *key, const int enc)
+{
+
+    assert(in && out && key);
+    assert((AES_ENCRYPT == enc) || (AES_DECRYPT == enc));
+
+    if (AES_ENCRYPT == enc)
+        AES_encrypt(in, out, key);
+    else
+        AES_decrypt(in, out, key);
+}
diff --git a/crypto/aes/aes_ige.c b/crypto/aes/aes_ige.c
new file mode 100644
index 000000000..9125264ed
--- /dev/null
+++ b/crypto/aes/aes_ige.c
@@ -0,0 +1,281 @@
+/*
+ * Copyright 2006-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "internal/cryptlib.h"
+
+#include 
+#include "aes_locl.h"
+
+#define N_WORDS (AES_BLOCK_SIZE / sizeof(unsigned long))
+typedef struct {
+    unsigned long data[N_WORDS];
+} aes_block_t;
+
+/* XXX: probably some better way to do this */
+#if defined(__i386__) || defined(__x86_64__)
+# define UNALIGNED_MEMOPS_ARE_FAST 1
+#else
+# define UNALIGNED_MEMOPS_ARE_FAST 0
+#endif
+
+#if UNALIGNED_MEMOPS_ARE_FAST
+# define load_block(d, s)        (d) = *(const aes_block_t *)(s)
+# define store_block(d, s)       *(aes_block_t *)(d) = (s)
+#else
+# define load_block(d, s)        memcpy((d).data, (s), AES_BLOCK_SIZE)
+# define store_block(d, s)       memcpy((d), (s).data, AES_BLOCK_SIZE)
+#endif
+
+/* N.B. The IV for this mode is _twice_ the block size */
+
+void AES_ige_encrypt(const unsigned char *in, unsigned char *out,
+                     size_t length, const AES_KEY *key,
+                     unsigned char *ivec, const int enc)
+{
+    size_t n;
+    size_t len = length;
+
+    OPENSSL_assert(in && out && key && ivec);
+    OPENSSL_assert((AES_ENCRYPT == enc) || (AES_DECRYPT == enc));
+    OPENSSL_assert((length % AES_BLOCK_SIZE) == 0);
+
+    len = length / AES_BLOCK_SIZE;
+
+    if (AES_ENCRYPT == enc) {
+        if (in != out &&
+            (UNALIGNED_MEMOPS_ARE_FAST
+             || ((size_t)in | (size_t)out | (size_t)ivec) % sizeof(long) ==
+             0)) {
+            aes_block_t *ivp = (aes_block_t *) ivec;
+            aes_block_t *iv2p = (aes_block_t *) (ivec + AES_BLOCK_SIZE);
+
+            while (len) {
+                aes_block_t *inp = (aes_block_t *) in;
+                aes_block_t *outp = (aes_block_t *) out;
+
+                for (n = 0; n < N_WORDS; ++n)
+                    outp->data[n] = inp->data[n] ^ ivp->data[n];
+                AES_encrypt((unsigned char *)outp->data,
+                            (unsigned char *)outp->data, key);
+                for (n = 0; n < N_WORDS; ++n)
+                    outp->data[n] ^= iv2p->data[n];
+                ivp = outp;
+                iv2p = inp;
+                --len;
+                in += AES_BLOCK_SIZE;
+                out += AES_BLOCK_SIZE;
+            }
+            memcpy(ivec, ivp->data, AES_BLOCK_SIZE);
+            memcpy(ivec + AES_BLOCK_SIZE, iv2p->data, AES_BLOCK_SIZE);
+        } else {
+            aes_block_t tmp, tmp2;
+            aes_block_t iv;
+            aes_block_t iv2;
+
+            load_block(iv, ivec);
+            load_block(iv2, ivec + AES_BLOCK_SIZE);
+
+            while (len) {
+                load_block(tmp, in);
+                for (n = 0; n < N_WORDS; ++n)
+                    tmp2.data[n] = tmp.data[n] ^ iv.data[n];
+                AES_encrypt((unsigned char *)tmp2.data,
+                            (unsigned char *)tmp2.data, key);
+                for (n = 0; n < N_WORDS; ++n)
+                    tmp2.data[n] ^= iv2.data[n];
+                store_block(out, tmp2);
+                iv = tmp2;
+                iv2 = tmp;
+                --len;
+                in += AES_BLOCK_SIZE;
+                out += AES_BLOCK_SIZE;
+            }
+            memcpy(ivec, iv.data, AES_BLOCK_SIZE);
+            memcpy(ivec + AES_BLOCK_SIZE, iv2.data, AES_BLOCK_SIZE);
+        }
+    } else {
+        if (in != out &&
+            (UNALIGNED_MEMOPS_ARE_FAST
+             || ((size_t)in | (size_t)out | (size_t)ivec) % sizeof(long) ==
+             0)) {
+            aes_block_t *ivp = (aes_block_t *) ivec;
+            aes_block_t *iv2p = (aes_block_t *) (ivec + AES_BLOCK_SIZE);
+
+            while (len) {
+                aes_block_t tmp;
+                aes_block_t *inp = (aes_block_t *) in;
+                aes_block_t *outp = (aes_block_t *) out;
+
+                for (n = 0; n < N_WORDS; ++n)
+                    tmp.data[n] = inp->data[n] ^ iv2p->data[n];
+                AES_decrypt((unsigned char *)tmp.data,
+                            (unsigned char *)outp->data, key);
+                for (n = 0; n < N_WORDS; ++n)
+                    outp->data[n] ^= ivp->data[n];
+                ivp = inp;
+                iv2p = outp;
+                --len;
+                in += AES_BLOCK_SIZE;
+                out += AES_BLOCK_SIZE;
+            }
+            memcpy(ivec, ivp->data, AES_BLOCK_SIZE);
+            memcpy(ivec + AES_BLOCK_SIZE, iv2p->data, AES_BLOCK_SIZE);
+        } else {
+            aes_block_t tmp, tmp2;
+            aes_block_t iv;
+            aes_block_t iv2;
+
+            load_block(iv, ivec);
+            load_block(iv2, ivec + AES_BLOCK_SIZE);
+
+            while (len) {
+                load_block(tmp, in);
+                tmp2 = tmp;
+                for (n = 0; n < N_WORDS; ++n)
+                    tmp.data[n] ^= iv2.data[n];
+                AES_decrypt((unsigned char *)tmp.data,
+                            (unsigned char *)tmp.data, key);
+                for (n = 0; n < N_WORDS; ++n)
+                    tmp.data[n] ^= iv.data[n];
+                store_block(out, tmp);
+                iv = tmp2;
+                iv2 = tmp;
+                --len;
+                in += AES_BLOCK_SIZE;
+                out += AES_BLOCK_SIZE;
+            }
+            memcpy(ivec, iv.data, AES_BLOCK_SIZE);
+            memcpy(ivec + AES_BLOCK_SIZE, iv2.data, AES_BLOCK_SIZE);
+        }
+    }
+}
+
+/*
+ * Note that its effectively impossible to do biIGE in anything other
+ * than a single pass, so no provision is made for chaining.
+ */
+
+/* N.B. The IV for this mode is _four times_ the block size */
+
+void AES_bi_ige_encrypt(const unsigned char *in, unsigned char *out,
+                        size_t length, const AES_KEY *key,
+                        const AES_KEY *key2, const unsigned char *ivec,
+                        const int enc)
+{
+    size_t n;
+    size_t len = length;
+    unsigned char tmp[AES_BLOCK_SIZE];
+    unsigned char tmp2[AES_BLOCK_SIZE];
+    unsigned char tmp3[AES_BLOCK_SIZE];
+    unsigned char prev[AES_BLOCK_SIZE];
+    const unsigned char *iv;
+    const unsigned char *iv2;
+
+    OPENSSL_assert(in && out && key && ivec);
+    OPENSSL_assert((AES_ENCRYPT == enc) || (AES_DECRYPT == enc));
+    OPENSSL_assert((length % AES_BLOCK_SIZE) == 0);
+
+    if (AES_ENCRYPT == enc) {
+        /*
+         * XXX: Do a separate case for when in != out (strictly should check
+         * for overlap, too)
+         */
+
+        /* First the forward pass */
+        iv = ivec;
+        iv2 = ivec + AES_BLOCK_SIZE;
+        while (len >= AES_BLOCK_SIZE) {
+            for (n = 0; n < AES_BLOCK_SIZE; ++n)
+                out[n] = in[n] ^ iv[n];
+            AES_encrypt(out, out, key);
+            for (n = 0; n < AES_BLOCK_SIZE; ++n)
+                out[n] ^= iv2[n];
+            iv = out;
+            memcpy(prev, in, AES_BLOCK_SIZE);
+            iv2 = prev;
+            len -= AES_BLOCK_SIZE;
+            in += AES_BLOCK_SIZE;
+            out += AES_BLOCK_SIZE;
+        }
+
+        /* And now backwards */
+        iv = ivec + AES_BLOCK_SIZE * 2;
+        iv2 = ivec + AES_BLOCK_SIZE * 3;
+        len = length;
+        while (len >= AES_BLOCK_SIZE) {
+            out -= AES_BLOCK_SIZE;
+            /*
+             * XXX: reduce copies by alternating between buffers
+             */
+            memcpy(tmp, out, AES_BLOCK_SIZE);
+            for (n = 0; n < AES_BLOCK_SIZE; ++n)
+                out[n] ^= iv[n];
+            /*
+             * hexdump(stdout, "out ^ iv", out, AES_BLOCK_SIZE);
+             */
+            AES_encrypt(out, out, key);
+            /*
+             * hexdump(stdout,"enc", out, AES_BLOCK_SIZE);
+             */
+            /*
+             * hexdump(stdout,"iv2", iv2, AES_BLOCK_SIZE);
+             */
+            for (n = 0; n < AES_BLOCK_SIZE; ++n)
+                out[n] ^= iv2[n];
+            /*
+             * hexdump(stdout,"out", out, AES_BLOCK_SIZE);
+             */
+            iv = out;
+            memcpy(prev, tmp, AES_BLOCK_SIZE);
+            iv2 = prev;
+            len -= AES_BLOCK_SIZE;
+        }
+    } else {
+        /* First backwards */
+        iv = ivec + AES_BLOCK_SIZE * 2;
+        iv2 = ivec + AES_BLOCK_SIZE * 3;
+        in += length;
+        out += length;
+        while (len >= AES_BLOCK_SIZE) {
+            in -= AES_BLOCK_SIZE;
+            out -= AES_BLOCK_SIZE;
+            memcpy(tmp, in, AES_BLOCK_SIZE);
+            memcpy(tmp2, in, AES_BLOCK_SIZE);
+            for (n = 0; n < AES_BLOCK_SIZE; ++n)
+                tmp[n] ^= iv2[n];
+            AES_decrypt(tmp, out, key);
+            for (n = 0; n < AES_BLOCK_SIZE; ++n)
+                out[n] ^= iv[n];
+            memcpy(tmp3, tmp2, AES_BLOCK_SIZE);
+            iv = tmp3;
+            iv2 = out;
+            len -= AES_BLOCK_SIZE;
+        }
+
+        /* And now forwards */
+        iv = ivec;
+        iv2 = ivec + AES_BLOCK_SIZE;
+        len = length;
+        while (len >= AES_BLOCK_SIZE) {
+            memcpy(tmp, out, AES_BLOCK_SIZE);
+            memcpy(tmp2, out, AES_BLOCK_SIZE);
+            for (n = 0; n < AES_BLOCK_SIZE; ++n)
+                tmp[n] ^= iv2[n];
+            AES_decrypt(tmp, out, key);
+            for (n = 0; n < AES_BLOCK_SIZE; ++n)
+                out[n] ^= iv[n];
+            memcpy(tmp3, tmp2, AES_BLOCK_SIZE);
+            iv = tmp3;
+            iv2 = out;
+            len -= AES_BLOCK_SIZE;
+            in += AES_BLOCK_SIZE;
+            out += AES_BLOCK_SIZE;
+        }
+    }
+}
diff --git a/crypto/aes/aes_locl.h b/crypto/aes/aes_locl.h
new file mode 100644
index 000000000..adee29df8
--- /dev/null
+++ b/crypto/aes/aes_locl.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2002-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#ifndef HEADER_AES_LOCL_H
+# define HEADER_AES_LOCL_H
+
+# include 
+# include 
+# include 
+# include 
+
+# if defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_AMD64) || defined(_M_X64))
+#  define SWAP(x) (_lrotl(x, 8) & 0x00ff00ff | _lrotr(x, 8) & 0xff00ff00)
+#  define GETU32(p) SWAP(*((u32 *)(p)))
+#  define PUTU32(ct, st) { *((u32 *)(ct)) = SWAP((st)); }
+# else
+#  define GETU32(pt) (((u32)(pt)[0] << 24) ^ ((u32)(pt)[1] << 16) ^ ((u32)(pt)[2] <<  8) ^ ((u32)(pt)[3]))
+#  define PUTU32(ct, st) { (ct)[0] = (u8)((st) >> 24); (ct)[1] = (u8)((st) >> 16); (ct)[2] = (u8)((st) >>  8); (ct)[3] = (u8)(st); }
+# endif
+
+# ifdef AES_LONG
+typedef unsigned long u32;
+# else
+typedef unsigned int u32;
+# endif
+typedef unsigned short u16;
+typedef unsigned char u8;
+
+# define MAXKC   (256/32)
+# define MAXKB   (256/8)
+# define MAXNR   14
+
+/* This controls loop-unrolling in aes_core.c */
+# undef FULL_UNROLL
+
+#endif                          /* !HEADER_AES_LOCL_H */
diff --git a/crypto/aes/aes_misc.c b/crypto/aes/aes_misc.c
new file mode 100644
index 000000000..7403c84f8
--- /dev/null
+++ b/crypto/aes/aes_misc.c
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2002-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include "aes_locl.h"
+
+const char *AES_options(void)
+{
+#ifdef FULL_UNROLL
+    return "aes(full)";
+#else
+    return "aes(partial)";
+#endif
+}
diff --git a/crypto/aes/aes_ofb.c b/crypto/aes/aes_ofb.c
new file mode 100644
index 000000000..215b53858
--- /dev/null
+++ b/crypto/aes/aes_ofb.c
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2002-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+
+void AES_ofb128_encrypt(const unsigned char *in, unsigned char *out,
+                        size_t length, const AES_KEY *key,
+                        unsigned char *ivec, int *num)
+{
+    CRYPTO_ofb128_encrypt(in, out, length, key, ivec, num,
+                          (block128_f) AES_encrypt);
+}
diff --git a/crypto/aes/aes_wrap.c b/crypto/aes/aes_wrap.c
new file mode 100644
index 000000000..cae0b2122
--- /dev/null
+++ b/crypto/aes/aes_wrap.c
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2008-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "internal/cryptlib.h"
+#include 
+#include 
+
+int AES_wrap_key(AES_KEY *key, const unsigned char *iv,
+                 unsigned char *out,
+                 const unsigned char *in, unsigned int inlen)
+{
+    return CRYPTO_128_wrap(key, iv, out, in, inlen, (block128_f) AES_encrypt);
+}
+
+int AES_unwrap_key(AES_KEY *key, const unsigned char *iv,
+                   unsigned char *out,
+                   const unsigned char *in, unsigned int inlen)
+{
+    return CRYPTO_128_unwrap(key, iv, out, in, inlen,
+                             (block128_f) AES_decrypt);
+}
diff --git a/crypto/aes/aes_x86core.c b/crypto/aes/aes_x86core.c
new file mode 100644
index 000000000..95b49bbab
--- /dev/null
+++ b/crypto/aes/aes_x86core.c
@@ -0,0 +1,1075 @@
+/*
+ * Copyright 2006-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/**
+ * rijndael-alg-fst.c
+ *
+ * @version 3.0 (December 2000)
+ *
+ * Optimised ANSI C code for the Rijndael cipher (now AES)
+ *
+ * @author Vincent Rijmen 
+ * @author Antoon Bosselaers 
+ * @author Paulo Barreto 
+ *
+ * This code is hereby placed in the public domain.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * This is experimental x86[_64] derivative. It assumes little-endian
+ * byte order and expects CPU to sustain unaligned memory references.
+ * It is used as playground for cache-time attack mitigations and
+ * serves as reference C implementation for x86[_64] assembler.
+ *
+ *                  
+ */
+
+
+#include 
+
+#include 
+#include 
+#include "aes_locl.h"
+
+/*
+ * These two parameters control which table, 256-byte or 2KB, is
+ * referenced in outer and respectively inner rounds.
+ */
+#define AES_COMPACT_IN_OUTER_ROUNDS
+#ifdef  AES_COMPACT_IN_OUTER_ROUNDS
+/* AES_COMPACT_IN_OUTER_ROUNDS costs ~30% in performance, while
+ * adding AES_COMPACT_IN_INNER_ROUNDS reduces benchmark *further*
+ * by factor of ~2. */
+# undef  AES_COMPACT_IN_INNER_ROUNDS
+#endif
+
+#if 1
+static void prefetch256(const void *table)
+{
+    volatile unsigned long *t=(void *)table,ret;
+    unsigned long sum;
+    int i;
+
+    /* 32 is common least cache-line size */
+    for (sum=0,i=0;i<256/sizeof(t[0]);i+=32/sizeof(t[0]))   sum ^= t[i];
+
+    ret = sum;
+}
+#else
+# define prefetch256(t)
+#endif
+
+#undef GETU32
+#define GETU32(p) (*((u32*)(p)))
+
+#if (defined(_WIN32) || defined(_WIN64)) && !defined(__MINGW32__)
+typedef unsigned __int64 u64;
+#define U64(C)  C##UI64
+#elif defined(__arch64__)
+typedef unsigned long u64;
+#define U64(C)  C##UL
+#else
+typedef unsigned long long u64;
+#define U64(C)  C##ULL
+#endif
+
+#undef ROTATE
+#if defined(_MSC_VER)
+# define ROTATE(a,n)    _lrotl(a,n)
+#elif defined(__ICC)
+# define ROTATE(a,n)    _rotl(a,n)
+#elif defined(__GNUC__) && __GNUC__>=2
+# if defined(__i386) || defined(__i386__) || defined(__x86_64) || defined(__x86_64__)
+#   define ROTATE(a,n)  ({ register unsigned int ret;   \
+                asm (           \
+                "roll %1,%0"        \
+                : "=r"(ret)     \
+                : "I"(n), "0"(a)    \
+                : "cc");        \
+               ret;             \
+            })
+# endif
+#endif
+/*-
+Te [x] = S [x].[02, 01, 01, 03, 02, 01, 01, 03];
+Te0[x] = S [x].[02, 01, 01, 03];
+Te1[x] = S [x].[03, 02, 01, 01];
+Te2[x] = S [x].[01, 03, 02, 01];
+Te3[x] = S [x].[01, 01, 03, 02];
+*/
+#define Te0 (u32)((u64*)((u8*)Te+0))
+#define Te1 (u32)((u64*)((u8*)Te+3))
+#define Te2 (u32)((u64*)((u8*)Te+2))
+#define Te3 (u32)((u64*)((u8*)Te+1))
+/*-
+Td [x] = Si[x].[0e, 09, 0d, 0b, 0e, 09, 0d, 0b];
+Td0[x] = Si[x].[0e, 09, 0d, 0b];
+Td1[x] = Si[x].[0b, 0e, 09, 0d];
+Td2[x] = Si[x].[0d, 0b, 0e, 09];
+Td3[x] = Si[x].[09, 0d, 0b, 0e];
+Td4[x] = Si[x].[01];
+*/
+#define Td0 (u32)((u64*)((u8*)Td+0))
+#define Td1 (u32)((u64*)((u8*)Td+3))
+#define Td2 (u32)((u64*)((u8*)Td+2))
+#define Td3 (u32)((u64*)((u8*)Td+1))
+
+static const u64 Te[256] = {
+    U64(0xa56363c6a56363c6), U64(0x847c7cf8847c7cf8),
+    U64(0x997777ee997777ee), U64(0x8d7b7bf68d7b7bf6),
+    U64(0x0df2f2ff0df2f2ff), U64(0xbd6b6bd6bd6b6bd6),
+    U64(0xb16f6fdeb16f6fde), U64(0x54c5c59154c5c591),
+    U64(0x5030306050303060), U64(0x0301010203010102),
+    U64(0xa96767cea96767ce), U64(0x7d2b2b567d2b2b56),
+    U64(0x19fefee719fefee7), U64(0x62d7d7b562d7d7b5),
+    U64(0xe6abab4de6abab4d), U64(0x9a7676ec9a7676ec),
+    U64(0x45caca8f45caca8f), U64(0x9d82821f9d82821f),
+    U64(0x40c9c98940c9c989), U64(0x877d7dfa877d7dfa),
+    U64(0x15fafaef15fafaef), U64(0xeb5959b2eb5959b2),
+    U64(0xc947478ec947478e), U64(0x0bf0f0fb0bf0f0fb),
+    U64(0xecadad41ecadad41), U64(0x67d4d4b367d4d4b3),
+    U64(0xfda2a25ffda2a25f), U64(0xeaafaf45eaafaf45),
+    U64(0xbf9c9c23bf9c9c23), U64(0xf7a4a453f7a4a453),
+    U64(0x967272e4967272e4), U64(0x5bc0c09b5bc0c09b),
+    U64(0xc2b7b775c2b7b775), U64(0x1cfdfde11cfdfde1),
+    U64(0xae93933dae93933d), U64(0x6a26264c6a26264c),
+    U64(0x5a36366c5a36366c), U64(0x413f3f7e413f3f7e),
+    U64(0x02f7f7f502f7f7f5), U64(0x4fcccc834fcccc83),
+    U64(0x5c3434685c343468), U64(0xf4a5a551f4a5a551),
+    U64(0x34e5e5d134e5e5d1), U64(0x08f1f1f908f1f1f9),
+    U64(0x937171e2937171e2), U64(0x73d8d8ab73d8d8ab),
+    U64(0x5331316253313162), U64(0x3f15152a3f15152a),
+    U64(0x0c0404080c040408), U64(0x52c7c79552c7c795),
+    U64(0x6523234665232346), U64(0x5ec3c39d5ec3c39d),
+    U64(0x2818183028181830), U64(0xa1969637a1969637),
+    U64(0x0f05050a0f05050a), U64(0xb59a9a2fb59a9a2f),
+    U64(0x0907070e0907070e), U64(0x3612122436121224),
+    U64(0x9b80801b9b80801b), U64(0x3de2e2df3de2e2df),
+    U64(0x26ebebcd26ebebcd), U64(0x6927274e6927274e),
+    U64(0xcdb2b27fcdb2b27f), U64(0x9f7575ea9f7575ea),
+    U64(0x1b0909121b090912), U64(0x9e83831d9e83831d),
+    U64(0x742c2c58742c2c58), U64(0x2e1a1a342e1a1a34),
+    U64(0x2d1b1b362d1b1b36), U64(0xb26e6edcb26e6edc),
+    U64(0xee5a5ab4ee5a5ab4), U64(0xfba0a05bfba0a05b),
+    U64(0xf65252a4f65252a4), U64(0x4d3b3b764d3b3b76),
+    U64(0x61d6d6b761d6d6b7), U64(0xceb3b37dceb3b37d),
+    U64(0x7b2929527b292952), U64(0x3ee3e3dd3ee3e3dd),
+    U64(0x712f2f5e712f2f5e), U64(0x9784841397848413),
+    U64(0xf55353a6f55353a6), U64(0x68d1d1b968d1d1b9),
+    U64(0x0000000000000000), U64(0x2cededc12cededc1),
+    U64(0x6020204060202040), U64(0x1ffcfce31ffcfce3),
+    U64(0xc8b1b179c8b1b179), U64(0xed5b5bb6ed5b5bb6),
+    U64(0xbe6a6ad4be6a6ad4), U64(0x46cbcb8d46cbcb8d),
+    U64(0xd9bebe67d9bebe67), U64(0x4b3939724b393972),
+    U64(0xde4a4a94de4a4a94), U64(0xd44c4c98d44c4c98),
+    U64(0xe85858b0e85858b0), U64(0x4acfcf854acfcf85),
+    U64(0x6bd0d0bb6bd0d0bb), U64(0x2aefefc52aefefc5),
+    U64(0xe5aaaa4fe5aaaa4f), U64(0x16fbfbed16fbfbed),
+    U64(0xc5434386c5434386), U64(0xd74d4d9ad74d4d9a),
+    U64(0x5533336655333366), U64(0x9485851194858511),
+    U64(0xcf45458acf45458a), U64(0x10f9f9e910f9f9e9),
+    U64(0x0602020406020204), U64(0x817f7ffe817f7ffe),
+    U64(0xf05050a0f05050a0), U64(0x443c3c78443c3c78),
+    U64(0xba9f9f25ba9f9f25), U64(0xe3a8a84be3a8a84b),
+    U64(0xf35151a2f35151a2), U64(0xfea3a35dfea3a35d),
+    U64(0xc0404080c0404080), U64(0x8a8f8f058a8f8f05),
+    U64(0xad92923fad92923f), U64(0xbc9d9d21bc9d9d21),
+    U64(0x4838387048383870), U64(0x04f5f5f104f5f5f1),
+    U64(0xdfbcbc63dfbcbc63), U64(0xc1b6b677c1b6b677),
+    U64(0x75dadaaf75dadaaf), U64(0x6321214263212142),
+    U64(0x3010102030101020), U64(0x1affffe51affffe5),
+    U64(0x0ef3f3fd0ef3f3fd), U64(0x6dd2d2bf6dd2d2bf),
+    U64(0x4ccdcd814ccdcd81), U64(0x140c0c18140c0c18),
+    U64(0x3513132635131326), U64(0x2fececc32fececc3),
+    U64(0xe15f5fbee15f5fbe), U64(0xa2979735a2979735),
+    U64(0xcc444488cc444488), U64(0x3917172e3917172e),
+    U64(0x57c4c49357c4c493), U64(0xf2a7a755f2a7a755),
+    U64(0x827e7efc827e7efc), U64(0x473d3d7a473d3d7a),
+    U64(0xac6464c8ac6464c8), U64(0xe75d5dbae75d5dba),
+    U64(0x2b1919322b191932), U64(0x957373e6957373e6),
+    U64(0xa06060c0a06060c0), U64(0x9881811998818119),
+    U64(0xd14f4f9ed14f4f9e), U64(0x7fdcdca37fdcdca3),
+    U64(0x6622224466222244), U64(0x7e2a2a547e2a2a54),
+    U64(0xab90903bab90903b), U64(0x8388880b8388880b),
+    U64(0xca46468cca46468c), U64(0x29eeeec729eeeec7),
+    U64(0xd3b8b86bd3b8b86b), U64(0x3c1414283c141428),
+    U64(0x79dedea779dedea7), U64(0xe25e5ebce25e5ebc),
+    U64(0x1d0b0b161d0b0b16), U64(0x76dbdbad76dbdbad),
+    U64(0x3be0e0db3be0e0db), U64(0x5632326456323264),
+    U64(0x4e3a3a744e3a3a74), U64(0x1e0a0a141e0a0a14),
+    U64(0xdb494992db494992), U64(0x0a06060c0a06060c),
+    U64(0x6c2424486c242448), U64(0xe45c5cb8e45c5cb8),
+    U64(0x5dc2c29f5dc2c29f), U64(0x6ed3d3bd6ed3d3bd),
+    U64(0xefacac43efacac43), U64(0xa66262c4a66262c4),
+    U64(0xa8919139a8919139), U64(0xa4959531a4959531),
+    U64(0x37e4e4d337e4e4d3), U64(0x8b7979f28b7979f2),
+    U64(0x32e7e7d532e7e7d5), U64(0x43c8c88b43c8c88b),
+    U64(0x5937376e5937376e), U64(0xb76d6ddab76d6dda),
+    U64(0x8c8d8d018c8d8d01), U64(0x64d5d5b164d5d5b1),
+    U64(0xd24e4e9cd24e4e9c), U64(0xe0a9a949e0a9a949),
+    U64(0xb46c6cd8b46c6cd8), U64(0xfa5656acfa5656ac),
+    U64(0x07f4f4f307f4f4f3), U64(0x25eaeacf25eaeacf),
+    U64(0xaf6565caaf6565ca), U64(0x8e7a7af48e7a7af4),
+    U64(0xe9aeae47e9aeae47), U64(0x1808081018080810),
+    U64(0xd5baba6fd5baba6f), U64(0x887878f0887878f0),
+    U64(0x6f25254a6f25254a), U64(0x722e2e5c722e2e5c),
+    U64(0x241c1c38241c1c38), U64(0xf1a6a657f1a6a657),
+    U64(0xc7b4b473c7b4b473), U64(0x51c6c69751c6c697),
+    U64(0x23e8e8cb23e8e8cb), U64(0x7cdddda17cdddda1),
+    U64(0x9c7474e89c7474e8), U64(0x211f1f3e211f1f3e),
+    U64(0xdd4b4b96dd4b4b96), U64(0xdcbdbd61dcbdbd61),
+    U64(0x868b8b0d868b8b0d), U64(0x858a8a0f858a8a0f),
+    U64(0x907070e0907070e0), U64(0x423e3e7c423e3e7c),
+    U64(0xc4b5b571c4b5b571), U64(0xaa6666ccaa6666cc),
+    U64(0xd8484890d8484890), U64(0x0503030605030306),
+    U64(0x01f6f6f701f6f6f7), U64(0x120e0e1c120e0e1c),
+    U64(0xa36161c2a36161c2), U64(0x5f35356a5f35356a),
+    U64(0xf95757aef95757ae), U64(0xd0b9b969d0b9b969),
+    U64(0x9186861791868617), U64(0x58c1c19958c1c199),
+    U64(0x271d1d3a271d1d3a), U64(0xb99e9e27b99e9e27),
+    U64(0x38e1e1d938e1e1d9), U64(0x13f8f8eb13f8f8eb),
+    U64(0xb398982bb398982b), U64(0x3311112233111122),
+    U64(0xbb6969d2bb6969d2), U64(0x70d9d9a970d9d9a9),
+    U64(0x898e8e07898e8e07), U64(0xa7949433a7949433),
+    U64(0xb69b9b2db69b9b2d), U64(0x221e1e3c221e1e3c),
+    U64(0x9287871592878715), U64(0x20e9e9c920e9e9c9),
+    U64(0x49cece8749cece87), U64(0xff5555aaff5555aa),
+    U64(0x7828285078282850), U64(0x7adfdfa57adfdfa5),
+    U64(0x8f8c8c038f8c8c03), U64(0xf8a1a159f8a1a159),
+    U64(0x8089890980898909), U64(0x170d0d1a170d0d1a),
+    U64(0xdabfbf65dabfbf65), U64(0x31e6e6d731e6e6d7),
+    U64(0xc6424284c6424284), U64(0xb86868d0b86868d0),
+    U64(0xc3414182c3414182), U64(0xb0999929b0999929),
+    U64(0x772d2d5a772d2d5a), U64(0x110f0f1e110f0f1e),
+    U64(0xcbb0b07bcbb0b07b), U64(0xfc5454a8fc5454a8),
+    U64(0xd6bbbb6dd6bbbb6d), U64(0x3a16162c3a16162c)
+};
+
+static const u8 Te4[256] = {
+    0x63U, 0x7cU, 0x77U, 0x7bU, 0xf2U, 0x6bU, 0x6fU, 0xc5U,
+    0x30U, 0x01U, 0x67U, 0x2bU, 0xfeU, 0xd7U, 0xabU, 0x76U,
+    0xcaU, 0x82U, 0xc9U, 0x7dU, 0xfaU, 0x59U, 0x47U, 0xf0U,
+    0xadU, 0xd4U, 0xa2U, 0xafU, 0x9cU, 0xa4U, 0x72U, 0xc0U,
+    0xb7U, 0xfdU, 0x93U, 0x26U, 0x36U, 0x3fU, 0xf7U, 0xccU,
+    0x34U, 0xa5U, 0xe5U, 0xf1U, 0x71U, 0xd8U, 0x31U, 0x15U,
+    0x04U, 0xc7U, 0x23U, 0xc3U, 0x18U, 0x96U, 0x05U, 0x9aU,
+    0x07U, 0x12U, 0x80U, 0xe2U, 0xebU, 0x27U, 0xb2U, 0x75U,
+    0x09U, 0x83U, 0x2cU, 0x1aU, 0x1bU, 0x6eU, 0x5aU, 0xa0U,
+    0x52U, 0x3bU, 0xd6U, 0xb3U, 0x29U, 0xe3U, 0x2fU, 0x84U,
+    0x53U, 0xd1U, 0x00U, 0xedU, 0x20U, 0xfcU, 0xb1U, 0x5bU,
+    0x6aU, 0xcbU, 0xbeU, 0x39U, 0x4aU, 0x4cU, 0x58U, 0xcfU,
+    0xd0U, 0xefU, 0xaaU, 0xfbU, 0x43U, 0x4dU, 0x33U, 0x85U,
+    0x45U, 0xf9U, 0x02U, 0x7fU, 0x50U, 0x3cU, 0x9fU, 0xa8U,
+    0x51U, 0xa3U, 0x40U, 0x8fU, 0x92U, 0x9dU, 0x38U, 0xf5U,
+    0xbcU, 0xb6U, 0xdaU, 0x21U, 0x10U, 0xffU, 0xf3U, 0xd2U,
+    0xcdU, 0x0cU, 0x13U, 0xecU, 0x5fU, 0x97U, 0x44U, 0x17U,
+    0xc4U, 0xa7U, 0x7eU, 0x3dU, 0x64U, 0x5dU, 0x19U, 0x73U,
+    0x60U, 0x81U, 0x4fU, 0xdcU, 0x22U, 0x2aU, 0x90U, 0x88U,
+    0x46U, 0xeeU, 0xb8U, 0x14U, 0xdeU, 0x5eU, 0x0bU, 0xdbU,
+    0xe0U, 0x32U, 0x3aU, 0x0aU, 0x49U, 0x06U, 0x24U, 0x5cU,
+    0xc2U, 0xd3U, 0xacU, 0x62U, 0x91U, 0x95U, 0xe4U, 0x79U,
+    0xe7U, 0xc8U, 0x37U, 0x6dU, 0x8dU, 0xd5U, 0x4eU, 0xa9U,
+    0x6cU, 0x56U, 0xf4U, 0xeaU, 0x65U, 0x7aU, 0xaeU, 0x08U,
+    0xbaU, 0x78U, 0x25U, 0x2eU, 0x1cU, 0xa6U, 0xb4U, 0xc6U,
+    0xe8U, 0xddU, 0x74U, 0x1fU, 0x4bU, 0xbdU, 0x8bU, 0x8aU,
+    0x70U, 0x3eU, 0xb5U, 0x66U, 0x48U, 0x03U, 0xf6U, 0x0eU,
+    0x61U, 0x35U, 0x57U, 0xb9U, 0x86U, 0xc1U, 0x1dU, 0x9eU,
+    0xe1U, 0xf8U, 0x98U, 0x11U, 0x69U, 0xd9U, 0x8eU, 0x94U,
+    0x9bU, 0x1eU, 0x87U, 0xe9U, 0xceU, 0x55U, 0x28U, 0xdfU,
+    0x8cU, 0xa1U, 0x89U, 0x0dU, 0xbfU, 0xe6U, 0x42U, 0x68U,
+    0x41U, 0x99U, 0x2dU, 0x0fU, 0xb0U, 0x54U, 0xbbU, 0x16U
+};
+
+static const u64 Td[256] = {
+    U64(0x50a7f45150a7f451), U64(0x5365417e5365417e),
+    U64(0xc3a4171ac3a4171a), U64(0x965e273a965e273a),
+    U64(0xcb6bab3bcb6bab3b), U64(0xf1459d1ff1459d1f),
+    U64(0xab58faacab58faac), U64(0x9303e34b9303e34b),
+    U64(0x55fa302055fa3020), U64(0xf66d76adf66d76ad),
+    U64(0x9176cc889176cc88), U64(0x254c02f5254c02f5),
+    U64(0xfcd7e54ffcd7e54f), U64(0xd7cb2ac5d7cb2ac5),
+    U64(0x8044352680443526), U64(0x8fa362b58fa362b5),
+    U64(0x495ab1de495ab1de), U64(0x671bba25671bba25),
+    U64(0x980eea45980eea45), U64(0xe1c0fe5de1c0fe5d),
+    U64(0x02752fc302752fc3), U64(0x12f04c8112f04c81),
+    U64(0xa397468da397468d), U64(0xc6f9d36bc6f9d36b),
+    U64(0xe75f8f03e75f8f03), U64(0x959c9215959c9215),
+    U64(0xeb7a6dbfeb7a6dbf), U64(0xda595295da595295),
+    U64(0x2d83bed42d83bed4), U64(0xd3217458d3217458),
+    U64(0x2969e0492969e049), U64(0x44c8c98e44c8c98e),
+    U64(0x6a89c2756a89c275), U64(0x78798ef478798ef4),
+    U64(0x6b3e58996b3e5899), U64(0xdd71b927dd71b927),
+    U64(0xb64fe1beb64fe1be), U64(0x17ad88f017ad88f0),
+    U64(0x66ac20c966ac20c9), U64(0xb43ace7db43ace7d),
+    U64(0x184adf63184adf63), U64(0x82311ae582311ae5),
+    U64(0x6033519760335197), U64(0x457f5362457f5362),
+    U64(0xe07764b1e07764b1), U64(0x84ae6bbb84ae6bbb),
+    U64(0x1ca081fe1ca081fe), U64(0x942b08f9942b08f9),
+    U64(0x5868487058684870), U64(0x19fd458f19fd458f),
+    U64(0x876cde94876cde94), U64(0xb7f87b52b7f87b52),
+    U64(0x23d373ab23d373ab), U64(0xe2024b72e2024b72),
+    U64(0x578f1fe3578f1fe3), U64(0x2aab55662aab5566),
+    U64(0x0728ebb20728ebb2), U64(0x03c2b52f03c2b52f),
+    U64(0x9a7bc5869a7bc586), U64(0xa50837d3a50837d3),
+    U64(0xf2872830f2872830), U64(0xb2a5bf23b2a5bf23),
+    U64(0xba6a0302ba6a0302), U64(0x5c8216ed5c8216ed),
+    U64(0x2b1ccf8a2b1ccf8a), U64(0x92b479a792b479a7),
+    U64(0xf0f207f3f0f207f3), U64(0xa1e2694ea1e2694e),
+    U64(0xcdf4da65cdf4da65), U64(0xd5be0506d5be0506),
+    U64(0x1f6234d11f6234d1), U64(0x8afea6c48afea6c4),
+    U64(0x9d532e349d532e34), U64(0xa055f3a2a055f3a2),
+    U64(0x32e18a0532e18a05), U64(0x75ebf6a475ebf6a4),
+    U64(0x39ec830b39ec830b), U64(0xaaef6040aaef6040),
+    U64(0x069f715e069f715e), U64(0x51106ebd51106ebd),
+    U64(0xf98a213ef98a213e), U64(0x3d06dd963d06dd96),
+    U64(0xae053eddae053edd), U64(0x46bde64d46bde64d),
+    U64(0xb58d5491b58d5491), U64(0x055dc471055dc471),
+    U64(0x6fd406046fd40604), U64(0xff155060ff155060),
+    U64(0x24fb981924fb9819), U64(0x97e9bdd697e9bdd6),
+    U64(0xcc434089cc434089), U64(0x779ed967779ed967),
+    U64(0xbd42e8b0bd42e8b0), U64(0x888b8907888b8907),
+    U64(0x385b19e7385b19e7), U64(0xdbeec879dbeec879),
+    U64(0x470a7ca1470a7ca1), U64(0xe90f427ce90f427c),
+    U64(0xc91e84f8c91e84f8), U64(0x0000000000000000),
+    U64(0x8386800983868009), U64(0x48ed2b3248ed2b32),
+    U64(0xac70111eac70111e), U64(0x4e725a6c4e725a6c),
+    U64(0xfbff0efdfbff0efd), U64(0x5638850f5638850f),
+    U64(0x1ed5ae3d1ed5ae3d), U64(0x27392d3627392d36),
+    U64(0x64d90f0a64d90f0a), U64(0x21a65c6821a65c68),
+    U64(0xd1545b9bd1545b9b), U64(0x3a2e36243a2e3624),
+    U64(0xb1670a0cb1670a0c), U64(0x0fe757930fe75793),
+    U64(0xd296eeb4d296eeb4), U64(0x9e919b1b9e919b1b),
+    U64(0x4fc5c0804fc5c080), U64(0xa220dc61a220dc61),
+    U64(0x694b775a694b775a), U64(0x161a121c161a121c),
+    U64(0x0aba93e20aba93e2), U64(0xe52aa0c0e52aa0c0),
+    U64(0x43e0223c43e0223c), U64(0x1d171b121d171b12),
+    U64(0x0b0d090e0b0d090e), U64(0xadc78bf2adc78bf2),
+    U64(0xb9a8b62db9a8b62d), U64(0xc8a91e14c8a91e14),
+    U64(0x8519f1578519f157), U64(0x4c0775af4c0775af),
+    U64(0xbbdd99eebbdd99ee), U64(0xfd607fa3fd607fa3),
+    U64(0x9f2601f79f2601f7), U64(0xbcf5725cbcf5725c),
+    U64(0xc53b6644c53b6644), U64(0x347efb5b347efb5b),
+    U64(0x7629438b7629438b), U64(0xdcc623cbdcc623cb),
+    U64(0x68fcedb668fcedb6), U64(0x63f1e4b863f1e4b8),
+    U64(0xcadc31d7cadc31d7), U64(0x1085634210856342),
+    U64(0x4022971340229713), U64(0x2011c6842011c684),
+    U64(0x7d244a857d244a85), U64(0xf83dbbd2f83dbbd2),
+    U64(0x1132f9ae1132f9ae), U64(0x6da129c76da129c7),
+    U64(0x4b2f9e1d4b2f9e1d), U64(0xf330b2dcf330b2dc),
+    U64(0xec52860dec52860d), U64(0xd0e3c177d0e3c177),
+    U64(0x6c16b32b6c16b32b), U64(0x99b970a999b970a9),
+    U64(0xfa489411fa489411), U64(0x2264e9472264e947),
+    U64(0xc48cfca8c48cfca8), U64(0x1a3ff0a01a3ff0a0),
+    U64(0xd82c7d56d82c7d56), U64(0xef903322ef903322),
+    U64(0xc74e4987c74e4987), U64(0xc1d138d9c1d138d9),
+    U64(0xfea2ca8cfea2ca8c), U64(0x360bd498360bd498),
+    U64(0xcf81f5a6cf81f5a6), U64(0x28de7aa528de7aa5),
+    U64(0x268eb7da268eb7da), U64(0xa4bfad3fa4bfad3f),
+    U64(0xe49d3a2ce49d3a2c), U64(0x0d9278500d927850),
+    U64(0x9bcc5f6a9bcc5f6a), U64(0x62467e5462467e54),
+    U64(0xc2138df6c2138df6), U64(0xe8b8d890e8b8d890),
+    U64(0x5ef7392e5ef7392e), U64(0xf5afc382f5afc382),
+    U64(0xbe805d9fbe805d9f), U64(0x7c93d0697c93d069),
+    U64(0xa92dd56fa92dd56f), U64(0xb31225cfb31225cf),
+    U64(0x3b99acc83b99acc8), U64(0xa77d1810a77d1810),
+    U64(0x6e639ce86e639ce8), U64(0x7bbb3bdb7bbb3bdb),
+    U64(0x097826cd097826cd), U64(0xf418596ef418596e),
+    U64(0x01b79aec01b79aec), U64(0xa89a4f83a89a4f83),
+    U64(0x656e95e6656e95e6), U64(0x7ee6ffaa7ee6ffaa),
+    U64(0x08cfbc2108cfbc21), U64(0xe6e815efe6e815ef),
+    U64(0xd99be7bad99be7ba), U64(0xce366f4ace366f4a),
+    U64(0xd4099fead4099fea), U64(0xd67cb029d67cb029),
+    U64(0xafb2a431afb2a431), U64(0x31233f2a31233f2a),
+    U64(0x3094a5c63094a5c6), U64(0xc066a235c066a235),
+    U64(0x37bc4e7437bc4e74), U64(0xa6ca82fca6ca82fc),
+    U64(0xb0d090e0b0d090e0), U64(0x15d8a73315d8a733),
+    U64(0x4a9804f14a9804f1), U64(0xf7daec41f7daec41),
+    U64(0x0e50cd7f0e50cd7f), U64(0x2ff691172ff69117),
+    U64(0x8dd64d768dd64d76), U64(0x4db0ef434db0ef43),
+    U64(0x544daacc544daacc), U64(0xdf0496e4df0496e4),
+    U64(0xe3b5d19ee3b5d19e), U64(0x1b886a4c1b886a4c),
+    U64(0xb81f2cc1b81f2cc1), U64(0x7f5165467f516546),
+    U64(0x04ea5e9d04ea5e9d), U64(0x5d358c015d358c01),
+    U64(0x737487fa737487fa), U64(0x2e410bfb2e410bfb),
+    U64(0x5a1d67b35a1d67b3), U64(0x52d2db9252d2db92),
+    U64(0x335610e9335610e9), U64(0x1347d66d1347d66d),
+    U64(0x8c61d79a8c61d79a), U64(0x7a0ca1377a0ca137),
+    U64(0x8e14f8598e14f859), U64(0x893c13eb893c13eb),
+    U64(0xee27a9ceee27a9ce), U64(0x35c961b735c961b7),
+    U64(0xede51ce1ede51ce1), U64(0x3cb1477a3cb1477a),
+    U64(0x59dfd29c59dfd29c), U64(0x3f73f2553f73f255),
+    U64(0x79ce141879ce1418), U64(0xbf37c773bf37c773),
+    U64(0xeacdf753eacdf753), U64(0x5baafd5f5baafd5f),
+    U64(0x146f3ddf146f3ddf), U64(0x86db447886db4478),
+    U64(0x81f3afca81f3afca), U64(0x3ec468b93ec468b9),
+    U64(0x2c3424382c342438), U64(0x5f40a3c25f40a3c2),
+    U64(0x72c31d1672c31d16), U64(0x0c25e2bc0c25e2bc),
+    U64(0x8b493c288b493c28), U64(0x41950dff41950dff),
+    U64(0x7101a8397101a839), U64(0xdeb30c08deb30c08),
+    U64(0x9ce4b4d89ce4b4d8), U64(0x90c1566490c15664),
+    U64(0x6184cb7b6184cb7b), U64(0x70b632d570b632d5),
+    U64(0x745c6c48745c6c48), U64(0x4257b8d04257b8d0)
+};
+static const u8 Td4[256] = {
+    0x52U, 0x09U, 0x6aU, 0xd5U, 0x30U, 0x36U, 0xa5U, 0x38U,
+    0xbfU, 0x40U, 0xa3U, 0x9eU, 0x81U, 0xf3U, 0xd7U, 0xfbU,
+    0x7cU, 0xe3U, 0x39U, 0x82U, 0x9bU, 0x2fU, 0xffU, 0x87U,
+    0x34U, 0x8eU, 0x43U, 0x44U, 0xc4U, 0xdeU, 0xe9U, 0xcbU,
+    0x54U, 0x7bU, 0x94U, 0x32U, 0xa6U, 0xc2U, 0x23U, 0x3dU,
+    0xeeU, 0x4cU, 0x95U, 0x0bU, 0x42U, 0xfaU, 0xc3U, 0x4eU,
+    0x08U, 0x2eU, 0xa1U, 0x66U, 0x28U, 0xd9U, 0x24U, 0xb2U,
+    0x76U, 0x5bU, 0xa2U, 0x49U, 0x6dU, 0x8bU, 0xd1U, 0x25U,
+    0x72U, 0xf8U, 0xf6U, 0x64U, 0x86U, 0x68U, 0x98U, 0x16U,
+    0xd4U, 0xa4U, 0x5cU, 0xccU, 0x5dU, 0x65U, 0xb6U, 0x92U,
+    0x6cU, 0x70U, 0x48U, 0x50U, 0xfdU, 0xedU, 0xb9U, 0xdaU,
+    0x5eU, 0x15U, 0x46U, 0x57U, 0xa7U, 0x8dU, 0x9dU, 0x84U,
+    0x90U, 0xd8U, 0xabU, 0x00U, 0x8cU, 0xbcU, 0xd3U, 0x0aU,
+    0xf7U, 0xe4U, 0x58U, 0x05U, 0xb8U, 0xb3U, 0x45U, 0x06U,
+    0xd0U, 0x2cU, 0x1eU, 0x8fU, 0xcaU, 0x3fU, 0x0fU, 0x02U,
+    0xc1U, 0xafU, 0xbdU, 0x03U, 0x01U, 0x13U, 0x8aU, 0x6bU,
+    0x3aU, 0x91U, 0x11U, 0x41U, 0x4fU, 0x67U, 0xdcU, 0xeaU,
+    0x97U, 0xf2U, 0xcfU, 0xceU, 0xf0U, 0xb4U, 0xe6U, 0x73U,
+    0x96U, 0xacU, 0x74U, 0x22U, 0xe7U, 0xadU, 0x35U, 0x85U,
+    0xe2U, 0xf9U, 0x37U, 0xe8U, 0x1cU, 0x75U, 0xdfU, 0x6eU,
+    0x47U, 0xf1U, 0x1aU, 0x71U, 0x1dU, 0x29U, 0xc5U, 0x89U,
+    0x6fU, 0xb7U, 0x62U, 0x0eU, 0xaaU, 0x18U, 0xbeU, 0x1bU,
+    0xfcU, 0x56U, 0x3eU, 0x4bU, 0xc6U, 0xd2U, 0x79U, 0x20U,
+    0x9aU, 0xdbU, 0xc0U, 0xfeU, 0x78U, 0xcdU, 0x5aU, 0xf4U,
+    0x1fU, 0xddU, 0xa8U, 0x33U, 0x88U, 0x07U, 0xc7U, 0x31U,
+    0xb1U, 0x12U, 0x10U, 0x59U, 0x27U, 0x80U, 0xecU, 0x5fU,
+    0x60U, 0x51U, 0x7fU, 0xa9U, 0x19U, 0xb5U, 0x4aU, 0x0dU,
+    0x2dU, 0xe5U, 0x7aU, 0x9fU, 0x93U, 0xc9U, 0x9cU, 0xefU,
+    0xa0U, 0xe0U, 0x3bU, 0x4dU, 0xaeU, 0x2aU, 0xf5U, 0xb0U,
+    0xc8U, 0xebU, 0xbbU, 0x3cU, 0x83U, 0x53U, 0x99U, 0x61U,
+    0x17U, 0x2bU, 0x04U, 0x7eU, 0xbaU, 0x77U, 0xd6U, 0x26U,
+    0xe1U, 0x69U, 0x14U, 0x63U, 0x55U, 0x21U, 0x0cU, 0x7dU
+};
+
+static const u32 rcon[] = {
+    0x00000001U, 0x00000002U, 0x00000004U, 0x00000008U,
+    0x00000010U, 0x00000020U, 0x00000040U, 0x00000080U,
+    0x0000001bU, 0x00000036U, /* for 128-bit blocks, Rijndael never uses more than 10 rcon values */
+};
+
+/**
+ * Expand the cipher key into the encryption key schedule.
+ */
+int AES_set_encrypt_key(const unsigned char *userKey, const int bits,
+                        AES_KEY *key)
+{
+
+    u32 *rk;
+    int i = 0;
+    u32 temp;
+
+    if (!userKey || !key)
+        return -1;
+    if (bits != 128 && bits != 192 && bits != 256)
+        return -2;
+
+    rk = key->rd_key;
+
+    if (bits==128)
+        key->rounds = 10;
+    else if (bits==192)
+        key->rounds = 12;
+    else
+        key->rounds = 14;
+
+    rk[0] = GETU32(userKey     );
+    rk[1] = GETU32(userKey +  4);
+    rk[2] = GETU32(userKey +  8);
+    rk[3] = GETU32(userKey + 12);
+    if (bits == 128) {
+        while (1) {
+            temp  = rk[3];
+            rk[4] = rk[0] ^
+                ((u32)Te4[(temp >>  8) & 0xff]      ) ^
+                ((u32)Te4[(temp >> 16) & 0xff] <<  8) ^
+                ((u32)Te4[(temp >> 24)       ] << 16) ^
+                ((u32)Te4[(temp      ) & 0xff] << 24) ^
+                rcon[i];
+            rk[5] = rk[1] ^ rk[4];
+            rk[6] = rk[2] ^ rk[5];
+            rk[7] = rk[3] ^ rk[6];
+            if (++i == 10) {
+                return 0;
+            }
+            rk += 4;
+        }
+    }
+    rk[4] = GETU32(userKey + 16);
+    rk[5] = GETU32(userKey + 20);
+    if (bits == 192) {
+        while (1) {
+            temp = rk[ 5];
+            rk[ 6] = rk[ 0] ^
+                ((u32)Te4[(temp >>  8) & 0xff]      ) ^
+                ((u32)Te4[(temp >> 16) & 0xff] <<  8) ^
+                ((u32)Te4[(temp >> 24)       ] << 16) ^
+                ((u32)Te4[(temp      ) & 0xff] << 24) ^
+                rcon[i];
+            rk[ 7] = rk[ 1] ^ rk[ 6];
+            rk[ 8] = rk[ 2] ^ rk[ 7];
+            rk[ 9] = rk[ 3] ^ rk[ 8];
+            if (++i == 8) {
+                return 0;
+            }
+            rk[10] = rk[ 4] ^ rk[ 9];
+            rk[11] = rk[ 5] ^ rk[10];
+            rk += 6;
+        }
+    }
+    rk[6] = GETU32(userKey + 24);
+    rk[7] = GETU32(userKey + 28);
+    if (bits == 256) {
+        while (1) {
+            temp = rk[ 7];
+            rk[ 8] = rk[ 0] ^
+                ((u32)Te4[(temp >>  8) & 0xff]      ) ^
+                ((u32)Te4[(temp >> 16) & 0xff] <<  8) ^
+                ((u32)Te4[(temp >> 24)       ] << 16) ^
+                ((u32)Te4[(temp      ) & 0xff] << 24) ^
+                rcon[i];
+            rk[ 9] = rk[ 1] ^ rk[ 8];
+            rk[10] = rk[ 2] ^ rk[ 9];
+            rk[11] = rk[ 3] ^ rk[10];
+            if (++i == 7) {
+                return 0;
+            }
+            temp = rk[11];
+            rk[12] = rk[ 4] ^
+                ((u32)Te4[(temp      ) & 0xff]      ) ^
+                ((u32)Te4[(temp >>  8) & 0xff] <<  8) ^
+                ((u32)Te4[(temp >> 16) & 0xff] << 16) ^
+                ((u32)Te4[(temp >> 24)       ] << 24);
+            rk[13] = rk[ 5] ^ rk[12];
+            rk[14] = rk[ 6] ^ rk[13];
+            rk[15] = rk[ 7] ^ rk[14];
+
+            rk += 8;
+            }
+    }
+    return 0;
+}
+
+/**
+ * Expand the cipher key into the decryption key schedule.
+ */
+int AES_set_decrypt_key(const unsigned char *userKey, const int bits,
+                        AES_KEY *key)
+{
+
+    u32 *rk;
+    int i, j, status;
+    u32 temp;
+
+    /* first, start with an encryption schedule */
+    status = AES_set_encrypt_key(userKey, bits, key);
+    if (status < 0)
+        return status;
+
+    rk = key->rd_key;
+
+    /* invert the order of the round keys: */
+    for (i = 0, j = 4*(key->rounds); i < j; i += 4, j -= 4) {
+        temp = rk[i    ]; rk[i    ] = rk[j    ]; rk[j    ] = temp;
+        temp = rk[i + 1]; rk[i + 1] = rk[j + 1]; rk[j + 1] = temp;
+        temp = rk[i + 2]; rk[i + 2] = rk[j + 2]; rk[j + 2] = temp;
+        temp = rk[i + 3]; rk[i + 3] = rk[j + 3]; rk[j + 3] = temp;
+    }
+    /* apply the inverse MixColumn transform to all round keys but the first and the last: */
+    for (i = 1; i < (key->rounds); i++) {
+        rk += 4;
+#if 1
+        for (j = 0; j < 4; j++) {
+            u32 tp1, tp2, tp4, tp8, tp9, tpb, tpd, tpe, m;
+
+            tp1 = rk[j];
+            m = tp1 & 0x80808080;
+            tp2 = ((tp1 & 0x7f7f7f7f) << 1) ^
+                ((m - (m >> 7)) & 0x1b1b1b1b);
+            m = tp2 & 0x80808080;
+            tp4 = ((tp2 & 0x7f7f7f7f) << 1) ^
+                ((m - (m >> 7)) & 0x1b1b1b1b);
+            m = tp4 & 0x80808080;
+            tp8 = ((tp4 & 0x7f7f7f7f) << 1) ^
+                ((m - (m >> 7)) & 0x1b1b1b1b);
+            tp9 = tp8 ^ tp1;
+            tpb = tp9 ^ tp2;
+            tpd = tp9 ^ tp4;
+            tpe = tp8 ^ tp4 ^ tp2;
+#if defined(ROTATE)
+            rk[j] = tpe ^ ROTATE(tpd,16) ^
+                ROTATE(tp9,8) ^ ROTATE(tpb,24);
+#else
+            rk[j] = tpe ^ (tpd >> 16) ^ (tpd << 16) ^
+                (tp9 >> 24) ^ (tp9 << 8) ^
+                (tpb >> 8) ^ (tpb << 24);
+#endif
+        }
+#else
+        rk[0] =
+            Td0[Te2[(rk[0]      ) & 0xff] & 0xff] ^
+            Td1[Te2[(rk[0] >>  8) & 0xff] & 0xff] ^
+            Td2[Te2[(rk[0] >> 16) & 0xff] & 0xff] ^
+            Td3[Te2[(rk[0] >> 24)       ] & 0xff];
+        rk[1] =
+            Td0[Te2[(rk[1]      ) & 0xff] & 0xff] ^
+            Td1[Te2[(rk[1] >>  8) & 0xff] & 0xff] ^
+            Td2[Te2[(rk[1] >> 16) & 0xff] & 0xff] ^
+            Td3[Te2[(rk[1] >> 24)       ] & 0xff];
+        rk[2] =
+            Td0[Te2[(rk[2]      ) & 0xff] & 0xff] ^
+            Td1[Te2[(rk[2] >>  8) & 0xff] & 0xff] ^
+            Td2[Te2[(rk[2] >> 16) & 0xff] & 0xff] ^
+            Td3[Te2[(rk[2] >> 24)       ] & 0xff];
+        rk[3] =
+            Td0[Te2[(rk[3]      ) & 0xff] & 0xff] ^
+            Td1[Te2[(rk[3] >>  8) & 0xff] & 0xff] ^
+            Td2[Te2[(rk[3] >> 16) & 0xff] & 0xff] ^
+            Td3[Te2[(rk[3] >> 24)       ] & 0xff];
+#endif
+    }
+    return 0;
+}
+
+/*
+ * Encrypt a single block
+ * in and out can overlap
+ */
+void AES_encrypt(const unsigned char *in, unsigned char *out,
+                 const AES_KEY *key)
+{
+
+    const u32 *rk;
+    u32 s0, s1, s2, s3, t[4];
+    int r;
+
+    assert(in && out && key);
+    rk = key->rd_key;
+
+    /*
+     * map byte array block to cipher state
+     * and add initial round key:
+     */
+    s0 = GETU32(in     ) ^ rk[0];
+    s1 = GETU32(in +  4) ^ rk[1];
+    s2 = GETU32(in +  8) ^ rk[2];
+    s3 = GETU32(in + 12) ^ rk[3];
+
+#if defined(AES_COMPACT_IN_OUTER_ROUNDS)
+    prefetch256(Te4);
+
+    t[0] = (u32)Te4[(s0      ) & 0xff]       ^
+           (u32)Te4[(s1 >>  8) & 0xff] <<  8 ^
+           (u32)Te4[(s2 >> 16) & 0xff] << 16 ^
+           (u32)Te4[(s3 >> 24)       ] << 24;
+    t[1] = (u32)Te4[(s1      ) & 0xff]       ^
+           (u32)Te4[(s2 >>  8) & 0xff] <<  8 ^
+           (u32)Te4[(s3 >> 16) & 0xff] << 16 ^
+           (u32)Te4[(s0 >> 24)       ] << 24;
+    t[2] = (u32)Te4[(s2      ) & 0xff]       ^
+           (u32)Te4[(s3 >>  8) & 0xff] <<  8 ^
+           (u32)Te4[(s0 >> 16) & 0xff] << 16 ^
+           (u32)Te4[(s1 >> 24)       ] << 24;
+    t[3] = (u32)Te4[(s3      ) & 0xff]       ^
+           (u32)Te4[(s0 >>  8) & 0xff] <<  8 ^
+           (u32)Te4[(s1 >> 16) & 0xff] << 16 ^
+           (u32)Te4[(s2 >> 24)       ] << 24;
+
+    /* now do the linear transform using words */
+    {   int i;
+        u32 r0, r1, r2;
+
+        for (i = 0; i < 4; i++) {
+            r0 = t[i];
+            r1 = r0 & 0x80808080;
+            r2 = ((r0 & 0x7f7f7f7f) << 1) ^
+                ((r1 - (r1 >> 7)) & 0x1b1b1b1b);
+#if defined(ROTATE)
+            t[i] = r2 ^ ROTATE(r2,24) ^ ROTATE(r0,24) ^
+                ROTATE(r0,16) ^ ROTATE(r0,8);
+#else
+            t[i] = r2 ^ ((r2 ^ r0) << 24) ^ ((r2 ^ r0) >> 8) ^
+                (r0 << 16) ^ (r0 >> 16) ^
+                (r0 << 8) ^ (r0 >> 24);
+#endif
+            t[i] ^= rk[4+i];
+        }
+    }
+#else
+    t[0] =  Te0[(s0      ) & 0xff] ^
+        Te1[(s1 >>  8) & 0xff] ^
+        Te2[(s2 >> 16) & 0xff] ^
+        Te3[(s3 >> 24)       ] ^
+        rk[4];
+    t[1] =  Te0[(s1      ) & 0xff] ^
+        Te1[(s2 >>  8) & 0xff] ^
+        Te2[(s3 >> 16) & 0xff] ^
+        Te3[(s0 >> 24)       ] ^
+        rk[5];
+    t[2] =  Te0[(s2      ) & 0xff] ^
+        Te1[(s3 >>  8) & 0xff] ^
+        Te2[(s0 >> 16) & 0xff] ^
+        Te3[(s1 >> 24)       ] ^
+        rk[6];
+    t[3] =  Te0[(s3      ) & 0xff] ^
+        Te1[(s0 >>  8) & 0xff] ^
+        Te2[(s1 >> 16) & 0xff] ^
+        Te3[(s2 >> 24)       ] ^
+        rk[7];
+#endif
+    s0 = t[0]; s1 = t[1]; s2 = t[2]; s3 = t[3];
+
+    /*
+     * Nr - 2 full rounds:
+     */
+    for (rk+=8,r=key->rounds-2; r>0; rk+=4,r--) {
+#if defined(AES_COMPACT_IN_INNER_ROUNDS)
+        t[0] = (u32)Te4[(s0      ) & 0xff]       ^
+               (u32)Te4[(s1 >>  8) & 0xff] <<  8 ^
+               (u32)Te4[(s2 >> 16) & 0xff] << 16 ^
+               (u32)Te4[(s3 >> 24)       ] << 24;
+        t[1] = (u32)Te4[(s1      ) & 0xff]       ^
+               (u32)Te4[(s2 >>  8) & 0xff] <<  8 ^
+               (u32)Te4[(s3 >> 16) & 0xff] << 16 ^
+               (u32)Te4[(s0 >> 24)       ] << 24;
+        t[2] = (u32)Te4[(s2      ) & 0xff]       ^
+               (u32)Te4[(s3 >>  8) & 0xff] <<  8 ^
+               (u32)Te4[(s0 >> 16) & 0xff] << 16 ^
+               (u32)Te4[(s1 >> 24)       ] << 24;
+        t[3] = (u32)Te4[(s3      ) & 0xff]       ^
+               (u32)Te4[(s0 >>  8) & 0xff] <<  8 ^
+               (u32)Te4[(s1 >> 16) & 0xff] << 16 ^
+               (u32)Te4[(s2 >> 24)       ] << 24;
+
+        /* now do the linear transform using words */
+        {
+            int i;
+            u32 r0, r1, r2;
+
+            for (i = 0; i < 4; i++) {
+                r0 = t[i];
+                r1 = r0 & 0x80808080;
+                r2 = ((r0 & 0x7f7f7f7f) << 1) ^
+                    ((r1 - (r1 >> 7)) & 0x1b1b1b1b);
+#if defined(ROTATE)
+                t[i] = r2 ^ ROTATE(r2,24) ^ ROTATE(r0,24) ^
+                    ROTATE(r0,16) ^ ROTATE(r0,8);
+#else
+                t[i] = r2 ^ ((r2 ^ r0) << 24) ^ ((r2 ^ r0) >> 8) ^
+                    (r0 << 16) ^ (r0 >> 16) ^
+                    (r0 << 8) ^ (r0 >> 24);
+#endif
+                t[i] ^= rk[i];
+            }
+        }
+#else
+        t[0] =  Te0[(s0      ) & 0xff] ^
+            Te1[(s1 >>  8) & 0xff] ^
+            Te2[(s2 >> 16) & 0xff] ^
+            Te3[(s3 >> 24)       ] ^
+            rk[0];
+        t[1] =  Te0[(s1      ) & 0xff] ^
+            Te1[(s2 >>  8) & 0xff] ^
+            Te2[(s3 >> 16) & 0xff] ^
+            Te3[(s0 >> 24)       ] ^
+            rk[1];
+        t[2] =  Te0[(s2      ) & 0xff] ^
+            Te1[(s3 >>  8) & 0xff] ^
+            Te2[(s0 >> 16) & 0xff] ^
+            Te3[(s1 >> 24)       ] ^
+            rk[2];
+        t[3] =  Te0[(s3      ) & 0xff] ^
+            Te1[(s0 >>  8) & 0xff] ^
+            Te2[(s1 >> 16) & 0xff] ^
+            Te3[(s2 >> 24)       ] ^
+            rk[3];
+#endif
+        s0 = t[0]; s1 = t[1]; s2 = t[2]; s3 = t[3];
+    }
+    /*
+     * apply last round and
+     * map cipher state to byte array block:
+     */
+#if defined(AES_COMPACT_IN_OUTER_ROUNDS)
+    prefetch256(Te4);
+
+    *(u32*)(out+0) =
+           (u32)Te4[(s0      ) & 0xff]       ^
+           (u32)Te4[(s1 >>  8) & 0xff] <<  8 ^
+           (u32)Te4[(s2 >> 16) & 0xff] << 16 ^
+           (u32)Te4[(s3 >> 24)       ] << 24 ^
+        rk[0];
+    *(u32*)(out+4) =
+           (u32)Te4[(s1      ) & 0xff]       ^
+           (u32)Te4[(s2 >>  8) & 0xff] <<  8 ^
+           (u32)Te4[(s3 >> 16) & 0xff] << 16 ^
+           (u32)Te4[(s0 >> 24)       ] << 24 ^
+        rk[1];
+    *(u32*)(out+8) =
+           (u32)Te4[(s2      ) & 0xff]       ^
+           (u32)Te4[(s3 >>  8) & 0xff] <<  8 ^
+           (u32)Te4[(s0 >> 16) & 0xff] << 16 ^
+           (u32)Te4[(s1 >> 24)       ] << 24 ^
+        rk[2];
+    *(u32*)(out+12) =
+           (u32)Te4[(s3      ) & 0xff]       ^
+           (u32)Te4[(s0 >>  8) & 0xff] <<  8 ^
+           (u32)Te4[(s1 >> 16) & 0xff] << 16 ^
+           (u32)Te4[(s2 >> 24)       ] << 24 ^
+        rk[3];
+#else
+    *(u32*)(out+0) =
+        (Te2[(s0      ) & 0xff] & 0x000000ffU) ^
+        (Te3[(s1 >>  8) & 0xff] & 0x0000ff00U) ^
+        (Te0[(s2 >> 16) & 0xff] & 0x00ff0000U) ^
+        (Te1[(s3 >> 24)       ] & 0xff000000U) ^
+        rk[0];
+    *(u32*)(out+4) =
+        (Te2[(s1      ) & 0xff] & 0x000000ffU) ^
+        (Te3[(s2 >>  8) & 0xff] & 0x0000ff00U) ^
+        (Te0[(s3 >> 16) & 0xff] & 0x00ff0000U) ^
+        (Te1[(s0 >> 24)       ] & 0xff000000U) ^
+        rk[1];
+    *(u32*)(out+8) =
+        (Te2[(s2      ) & 0xff] & 0x000000ffU) ^
+        (Te3[(s3 >>  8) & 0xff] & 0x0000ff00U) ^
+        (Te0[(s0 >> 16) & 0xff] & 0x00ff0000U) ^
+        (Te1[(s1 >> 24)       ] & 0xff000000U) ^
+        rk[2];
+    *(u32*)(out+12) =
+        (Te2[(s3      ) & 0xff] & 0x000000ffU) ^
+        (Te3[(s0 >>  8) & 0xff] & 0x0000ff00U) ^
+        (Te0[(s1 >> 16) & 0xff] & 0x00ff0000U) ^
+        (Te1[(s2 >> 24)       ] & 0xff000000U) ^
+        rk[3];
+#endif
+}
+
+/*
+ * Decrypt a single block
+ * in and out can overlap
+ */
+void AES_decrypt(const unsigned char *in, unsigned char *out,
+                 const AES_KEY *key)
+{
+
+    const u32 *rk;
+    u32 s0, s1, s2, s3, t[4];
+    int r;
+
+    assert(in && out && key);
+    rk = key->rd_key;
+
+    /*
+     * map byte array block to cipher state
+     * and add initial round key:
+     */
+    s0 = GETU32(in     ) ^ rk[0];
+    s1 = GETU32(in +  4) ^ rk[1];
+    s2 = GETU32(in +  8) ^ rk[2];
+    s3 = GETU32(in + 12) ^ rk[3];
+
+#if defined(AES_COMPACT_IN_OUTER_ROUNDS)
+    prefetch256(Td4);
+
+    t[0] = (u32)Td4[(s0      ) & 0xff]       ^
+           (u32)Td4[(s3 >>  8) & 0xff] <<  8 ^
+           (u32)Td4[(s2 >> 16) & 0xff] << 16 ^
+           (u32)Td4[(s1 >> 24)       ] << 24;
+    t[1] = (u32)Td4[(s1      ) & 0xff]       ^
+           (u32)Td4[(s0 >>  8) & 0xff] <<  8 ^
+           (u32)Td4[(s3 >> 16) & 0xff] << 16 ^
+           (u32)Td4[(s2 >> 24)       ] << 24;
+    t[2] = (u32)Td4[(s2      ) & 0xff]       ^
+           (u32)Td4[(s1 >>  8) & 0xff] <<  8 ^
+           (u32)Td4[(s0 >> 16) & 0xff] << 16 ^
+           (u32)Td4[(s3 >> 24)       ] << 24;
+    t[3] = (u32)Td4[(s3      ) & 0xff]       ^
+           (u32)Td4[(s2 >>  8) & 0xff] <<  8 ^
+           (u32)Td4[(s1 >> 16) & 0xff] << 16 ^
+           (u32)Td4[(s0 >> 24)       ] << 24;
+
+    /* now do the linear transform using words */
+    {
+        int i;
+        u32 tp1, tp2, tp4, tp8, tp9, tpb, tpd, tpe, m;
+
+        for (i = 0; i < 4; i++) {
+            tp1 = t[i];
+            m = tp1 & 0x80808080;
+            tp2 = ((tp1 & 0x7f7f7f7f) << 1) ^
+                ((m - (m >> 7)) & 0x1b1b1b1b);
+            m = tp2 & 0x80808080;
+            tp4 = ((tp2 & 0x7f7f7f7f) << 1) ^
+                ((m - (m >> 7)) & 0x1b1b1b1b);
+            m = tp4 & 0x80808080;
+            tp8 = ((tp4 & 0x7f7f7f7f) << 1) ^
+                ((m - (m >> 7)) & 0x1b1b1b1b);
+            tp9 = tp8 ^ tp1;
+            tpb = tp9 ^ tp2;
+            tpd = tp9 ^ tp4;
+            tpe = tp8 ^ tp4 ^ tp2;
+#if defined(ROTATE)
+            t[i] = tpe ^ ROTATE(tpd,16) ^
+                ROTATE(tp9,8) ^ ROTATE(tpb,24);
+#else
+            t[i] = tpe ^ (tpd >> 16) ^ (tpd << 16) ^
+                (tp9 >> 24) ^ (tp9 << 8) ^
+                (tpb >> 8) ^ (tpb << 24);
+#endif
+            t[i] ^= rk[4+i];
+        }
+    }
+#else
+    t[0] =  Td0[(s0      ) & 0xff] ^
+        Td1[(s3 >>  8) & 0xff] ^
+        Td2[(s2 >> 16) & 0xff] ^
+        Td3[(s1 >> 24)       ] ^
+        rk[4];
+    t[1] =  Td0[(s1      ) & 0xff] ^
+        Td1[(s0 >>  8) & 0xff] ^
+        Td2[(s3 >> 16) & 0xff] ^
+        Td3[(s2 >> 24)       ] ^
+        rk[5];
+    t[2] =  Td0[(s2      ) & 0xff] ^
+        Td1[(s1 >>  8) & 0xff] ^
+        Td2[(s0 >> 16) & 0xff] ^
+        Td3[(s3 >> 24)       ] ^
+        rk[6];
+    t[3] =  Td0[(s3      ) & 0xff] ^
+        Td1[(s2 >>  8) & 0xff] ^
+        Td2[(s1 >> 16) & 0xff] ^
+        Td3[(s0 >> 24)       ] ^
+        rk[7];
+#endif
+    s0 = t[0]; s1 = t[1]; s2 = t[2]; s3 = t[3];
+
+    /*
+     * Nr - 2 full rounds:
+     */
+    for (rk+=8,r=key->rounds-2; r>0; rk+=4,r--) {
+#if defined(AES_COMPACT_IN_INNER_ROUNDS)
+        t[0] = (u32)Td4[(s0      ) & 0xff]       ^
+               (u32)Td4[(s3 >>  8) & 0xff] <<  8 ^
+               (u32)Td4[(s2 >> 16) & 0xff] << 16 ^
+               (u32)Td4[(s1 >> 24)       ] << 24;
+        t[1] = (u32)Td4[(s1      ) & 0xff]       ^
+               (u32)Td4[(s0 >>  8) & 0xff] <<  8 ^
+               (u32)Td4[(s3 >> 16) & 0xff] << 16 ^
+               (u32)Td4[(s2 >> 24)       ] << 24;
+        t[2] = (u32)Td4[(s2      ) & 0xff]       ^
+               (u32)Td4[(s1 >>  8) & 0xff] <<  8 ^
+               (u32)Td4[(s0 >> 16) & 0xff] << 16 ^
+               (u32)Td4[(s3 >> 24)       ] << 24;
+        t[3] = (u32)Td4[(s3      ) & 0xff]       ^
+               (u32)Td4[(s2 >>  8) & 0xff] <<  8 ^
+               (u32)Td4[(s1 >> 16) & 0xff] << 16 ^
+               (u32)Td4[(s0 >> 24)       ] << 24;
+
+    /* now do the linear transform using words */
+    {
+        int i;
+        u32 tp1, tp2, tp4, tp8, tp9, tpb, tpd, tpe, m;
+
+        for (i = 0; i < 4; i++) {
+            tp1 = t[i];
+            m = tp1 & 0x80808080;
+            tp2 = ((tp1 & 0x7f7f7f7f) << 1) ^
+                ((m - (m >> 7)) & 0x1b1b1b1b);
+            m = tp2 & 0x80808080;
+            tp4 = ((tp2 & 0x7f7f7f7f) << 1) ^
+                ((m - (m >> 7)) & 0x1b1b1b1b);
+            m = tp4 & 0x80808080;
+            tp8 = ((tp4 & 0x7f7f7f7f) << 1) ^
+                ((m - (m >> 7)) & 0x1b1b1b1b);
+            tp9 = tp8 ^ tp1;
+            tpb = tp9 ^ tp2;
+            tpd = tp9 ^ tp4;
+            tpe = tp8 ^ tp4 ^ tp2;
+#if defined(ROTATE)
+            t[i] = tpe ^ ROTATE(tpd,16) ^
+                ROTATE(tp9,8) ^ ROTATE(tpb,24);
+#else
+            t[i] = tpe ^ (tpd >> 16) ^ (tpd << 16) ^
+                (tp9 >> 24) ^ (tp9 << 8) ^
+                (tpb >> 8) ^ (tpb << 24);
+#endif
+            t[i] ^= rk[i];
+        }
+    }
+#else
+    t[0] =  Td0[(s0      ) & 0xff] ^
+        Td1[(s3 >>  8) & 0xff] ^
+        Td2[(s2 >> 16) & 0xff] ^
+        Td3[(s1 >> 24)       ] ^
+        rk[0];
+    t[1] =  Td0[(s1      ) & 0xff] ^
+        Td1[(s0 >>  8) & 0xff] ^
+        Td2[(s3 >> 16) & 0xff] ^
+        Td3[(s2 >> 24)       ] ^
+        rk[1];
+    t[2] =  Td0[(s2      ) & 0xff] ^
+        Td1[(s1 >>  8) & 0xff] ^
+        Td2[(s0 >> 16) & 0xff] ^
+        Td3[(s3 >> 24)       ] ^
+        rk[2];
+    t[3] =  Td0[(s3      ) & 0xff] ^
+        Td1[(s2 >>  8) & 0xff] ^
+        Td2[(s1 >> 16) & 0xff] ^
+        Td3[(s0 >> 24)       ] ^
+        rk[3];
+#endif
+    s0 = t[0]; s1 = t[1]; s2 = t[2]; s3 = t[3];
+    }
+    /*
+     * apply last round and
+     * map cipher state to byte array block:
+     */
+    prefetch256(Td4);
+
+    *(u32*)(out+0) =
+        ((u32)Td4[(s0      ) & 0xff])    ^
+        ((u32)Td4[(s3 >>  8) & 0xff] <<  8) ^
+        ((u32)Td4[(s2 >> 16) & 0xff] << 16) ^
+        ((u32)Td4[(s1 >> 24)       ] << 24) ^
+        rk[0];
+    *(u32*)(out+4) =
+        ((u32)Td4[(s1      ) & 0xff])     ^
+        ((u32)Td4[(s0 >>  8) & 0xff] <<  8) ^
+        ((u32)Td4[(s3 >> 16) & 0xff] << 16) ^
+        ((u32)Td4[(s2 >> 24)       ] << 24) ^
+        rk[1];
+    *(u32*)(out+8) =
+        ((u32)Td4[(s2      ) & 0xff])     ^
+        ((u32)Td4[(s1 >>  8) & 0xff] <<  8) ^
+        ((u32)Td4[(s0 >> 16) & 0xff] << 16) ^
+        ((u32)Td4[(s3 >> 24)       ] << 24) ^
+        rk[2];
+    *(u32*)(out+12) =
+        ((u32)Td4[(s3      ) & 0xff])     ^
+        ((u32)Td4[(s2 >>  8) & 0xff] <<  8) ^
+        ((u32)Td4[(s1 >> 16) & 0xff] << 16) ^
+        ((u32)Td4[(s0 >> 24)       ] << 24) ^
+        rk[3];
+}
diff --git a/crypto/aes/asm/aes-586.pl b/crypto/aes/asm/aes-586.pl
new file mode 100755
index 000000000..1ba356508
--- /dev/null
+++ b/crypto/aes/asm/aes-586.pl
@@ -0,0 +1,3000 @@
+#! /usr/bin/env perl
+# Copyright 2004-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+#
+# ====================================================================
+# Written by Andy Polyakov  for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+#
+# Version 4.3.
+#
+# You might fail to appreciate this module performance from the first
+# try. If compared to "vanilla" linux-ia32-icc target, i.e. considered
+# to be *the* best Intel C compiler without -KPIC, performance appears
+# to be virtually identical... But try to re-configure with shared
+# library support... Aha! Intel compiler "suddenly" lags behind by 30%
+# [on P4, more on others]:-) And if compared to position-independent
+# code generated by GNU C, this code performs *more* than *twice* as
+# fast! Yes, all this buzz about PIC means that unlike other hand-
+# coded implementations, this one was explicitly designed to be safe
+# to use even in shared library context... This also means that this
+# code isn't necessarily absolutely fastest "ever," because in order
+# to achieve position independence an extra register has to be
+# off-loaded to stack, which affects the benchmark result.
+#
+# Special note about instruction choice. Do you recall RC4_INT code
+# performing poorly on P4? It might be the time to figure out why.
+# RC4_INT code implies effective address calculations in base+offset*4
+# form. Trouble is that it seems that offset scaling turned to be
+# critical path... At least eliminating scaling resulted in 2.8x RC4
+# performance improvement [as you might recall]. As AES code is hungry
+# for scaling too, I [try to] avoid the latter by favoring off-by-2
+# shifts and masking the result with 0xFF<<2 instead of "boring" 0xFF.
+#
+# As was shown by Dean Gaudet , the above note turned
+# void. Performance improvement with off-by-2 shifts was observed on
+# intermediate implementation, which was spilling yet another register
+# to stack... Final offset*4 code below runs just a tad faster on P4,
+# but exhibits up to 10% improvement on other cores.
+#
+# Second version is "monolithic" replacement for aes_core.c, which in
+# addition to AES_[de|en]crypt implements AES_set_[de|en]cryption_key.
+# This made it possible to implement little-endian variant of the
+# algorithm without modifying the base C code. Motivating factor for
+# the undertaken effort was that it appeared that in tight IA-32
+# register window little-endian flavor could achieve slightly higher
+# Instruction Level Parallelism, and it indeed resulted in up to 15%
+# better performance on most recent µ-archs...
+#
+# Third version adds AES_cbc_encrypt implementation, which resulted in
+# up to 40% performance imrovement of CBC benchmark results. 40% was
+# observed on P4 core, where "overall" imrovement coefficient, i.e. if
+# compared to PIC generated by GCC and in CBC mode, was observed to be
+# as large as 4x:-) CBC performance is virtually identical to ECB now
+# and on some platforms even better, e.g. 17.6 "small" cycles/byte on
+# Opteron, because certain function prologues and epilogues are
+# effectively taken out of the loop...
+#
+# Version 3.2 implements compressed tables and prefetch of these tables
+# in CBC[!] mode. Former means that 3/4 of table references are now
+# misaligned, which unfortunately has negative impact on elder IA-32
+# implementations, Pentium suffered 30% penalty, PIII - 10%.
+#
+# Version 3.3 avoids L1 cache aliasing between stack frame and
+# S-boxes, and 3.4 - L1 cache aliasing even between key schedule. The
+# latter is achieved by copying the key schedule to controlled place in
+# stack. This unfortunately has rather strong impact on small block CBC
+# performance, ~2x deterioration on 16-byte block if compared to 3.3.
+#
+# Version 3.5 checks if there is L1 cache aliasing between user-supplied
+# key schedule and S-boxes and abstains from copying the former if
+# there is no. This allows end-user to consciously retain small block
+# performance by aligning key schedule in specific manner.
+#
+# Version 3.6 compresses Td4 to 256 bytes and prefetches it in ECB.
+#
+# Current ECB performance numbers for 128-bit key in CPU cycles per
+# processed byte [measure commonly used by AES benchmarkers] are:
+#
+#		small footprint		fully unrolled
+# P4		24			22
+# AMD K8	20			19
+# PIII		25			23
+# Pentium	81			78
+#
+# Version 3.7 reimplements outer rounds as "compact." Meaning that
+# first and last rounds reference compact 256 bytes S-box. This means
+# that first round consumes a lot more CPU cycles and that encrypt
+# and decrypt performance becomes asymmetric. Encrypt performance
+# drops by 10-12%, while decrypt - by 20-25%:-( 256 bytes S-box is
+# aggressively pre-fetched.
+#
+# Version 4.0 effectively rolls back to 3.6 and instead implements
+# additional set of functions, _[x86|sse]_AES_[en|de]crypt_compact,
+# which use exclusively 256 byte S-box. These functions are to be
+# called in modes not concealing plain text, such as ECB, or when
+# we're asked to process smaller amount of data [or unconditionally
+# on hyper-threading CPU]. Currently it's called unconditionally from
+# AES_[en|de]crypt, which affects all modes, but CBC. CBC routine
+# still needs to be modified to switch between slower and faster
+# mode when appropriate... But in either case benchmark landscape
+# changes dramatically and below numbers are CPU cycles per processed
+# byte for 128-bit key.
+#
+#		ECB encrypt	ECB decrypt	CBC large chunk
+# P4		52[54]		83[95]		23
+# AMD K8	46[41]		66[70]		18
+# PIII		41[50]		60[77]		24
+# Core 2	31[36]		45[64]		18.5
+# Atom		76[100]		96[138]		60
+# Pentium	115		150		77
+#
+# Version 4.1 switches to compact S-box even in key schedule setup.
+#
+# Version 4.2 prefetches compact S-box in every SSE round or in other
+# words every cache-line is *guaranteed* to be accessed within ~50
+# cycles window. Why just SSE? Because it's needed on hyper-threading
+# CPU! Which is also why it's prefetched with 64 byte stride. Best
+# part is that it has no negative effect on performance:-)  
+#
+# Version 4.3 implements switch between compact and non-compact block
+# functions in AES_cbc_encrypt depending on how much data was asked
+# to be processed in one stroke.
+#
+######################################################################
+# Timing attacks are classified in two classes: synchronous when
+# attacker consciously initiates cryptographic operation and collects
+# timing data of various character afterwards, and asynchronous when
+# malicious code is executed on same CPU simultaneously with AES,
+# instruments itself and performs statistical analysis of this data.
+#
+# As far as synchronous attacks go the root to the AES timing
+# vulnerability is twofold. Firstly, of 256 S-box elements at most 160
+# are referred to in single 128-bit block operation. Well, in C
+# implementation with 4 distinct tables it's actually as little as 40
+# references per 256 elements table, but anyway... Secondly, even
+# though S-box elements are clustered into smaller amount of cache-
+# lines, smaller than 160 and even 40, it turned out that for certain
+# plain-text pattern[s] or simply put chosen plain-text and given key
+# few cache-lines remain unaccessed during block operation. Now, if
+# attacker can figure out this access pattern, he can deduct the key
+# [or at least part of it]. The natural way to mitigate this kind of
+# attacks is to minimize the amount of cache-lines in S-box and/or
+# prefetch them to ensure that every one is accessed for more uniform
+# timing. But note that *if* plain-text was concealed in such way that
+# input to block function is distributed *uniformly*, then attack
+# wouldn't apply. Now note that some encryption modes, most notably
+# CBC, do mask the plain-text in this exact way [secure cipher output
+# is distributed uniformly]. Yes, one still might find input that
+# would reveal the information about given key, but if amount of
+# candidate inputs to be tried is larger than amount of possible key
+# combinations then attack becomes infeasible. This is why revised
+# AES_cbc_encrypt "dares" to switch to larger S-box when larger chunk
+# of data is to be processed in one stroke. The current size limit of
+# 512 bytes is chosen to provide same [diminishigly low] probability
+# for cache-line to remain untouched in large chunk operation with
+# large S-box as for single block operation with compact S-box and
+# surely needs more careful consideration...
+#
+# As for asynchronous attacks. There are two flavours: attacker code
+# being interleaved with AES on hyper-threading CPU at *instruction*
+# level, and two processes time sharing single core. As for latter.
+# Two vectors. 1. Given that attacker process has higher priority,
+# yield execution to process performing AES just before timer fires
+# off the scheduler, immediately regain control of CPU and analyze the
+# cache state. For this attack to be efficient attacker would have to
+# effectively slow down the operation by several *orders* of magnitute,
+# by ratio of time slice to duration of handful of AES rounds, which
+# unlikely to remain unnoticed. Not to mention that this also means
+# that he would spend correspondigly more time to collect enough
+# statistical data to mount the attack. It's probably appropriate to
+# say that if adeversary reckons that this attack is beneficial and
+# risks to be noticed, you probably have larger problems having him
+# mere opportunity. In other words suggested code design expects you
+# to preclude/mitigate this attack by overall system security design.
+# 2. Attacker manages to make his code interrupt driven. In order for
+# this kind of attack to be feasible, interrupt rate has to be high
+# enough, again comparable to duration of handful of AES rounds. But
+# is there interrupt source of such rate? Hardly, not even 1Gbps NIC
+# generates interrupts at such raging rate...
+#
+# And now back to the former, hyper-threading CPU or more specifically
+# Intel P4. Recall that asynchronous attack implies that malicious
+# code instruments itself. And naturally instrumentation granularity
+# has be noticeably lower than duration of codepath accessing S-box.
+# Given that all cache-lines are accessed during that time that is.
+# Current implementation accesses *all* cache-lines within ~50 cycles
+# window, which is actually *less* than RDTSC latency on Intel P4!
+
+$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+push(@INC,"${dir}","${dir}../../perlasm");
+require "x86asm.pl";
+
+$output = pop;
+open OUT,">$output";
+*STDOUT=*OUT;
+
+&asm_init($ARGV[0],"aes-586.pl",$x86only = $ARGV[$#ARGV] eq "386");
+&static_label("AES_Te");
+&static_label("AES_Td");
+
+$s0="eax";
+$s1="ebx";
+$s2="ecx";
+$s3="edx";
+$key="edi";
+$acc="esi";
+$tbl="ebp";
+
+# stack frame layout in _[x86|sse]_AES_* routines, frame is allocated
+# by caller
+$__ra=&DWP(0,"esp");	# return address
+$__s0=&DWP(4,"esp");	# s0 backing store
+$__s1=&DWP(8,"esp");	# s1 backing store
+$__s2=&DWP(12,"esp");	# s2 backing store
+$__s3=&DWP(16,"esp");	# s3 backing store
+$__key=&DWP(20,"esp");	# pointer to key schedule
+$__end=&DWP(24,"esp");	# pointer to end of key schedule
+$__tbl=&DWP(28,"esp");	# %ebp backing store
+
+# stack frame layout in AES_[en|crypt] routines, which differs from
+# above by 4 and overlaps by %ebp backing store
+$_tbl=&DWP(24,"esp");
+$_esp=&DWP(28,"esp");
+
+sub _data_word() { my $i; while(defined($i=shift)) { &data_word($i,$i); } }
+
+$speed_limit=512;	# chunks smaller than $speed_limit are
+			# processed with compact routine in CBC mode
+$small_footprint=1;	# $small_footprint=1 code is ~5% slower [on
+			# recent µ-archs], but ~5 times smaller!
+			# I favor compact code to minimize cache
+			# contention and in hope to "collect" 5% back
+			# in real-life applications...
+
+$vertical_spin=0;	# shift "verticaly" defaults to 0, because of
+			# its proof-of-concept status...
+# Note that there is no decvert(), as well as last encryption round is
+# performed with "horizontal" shifts. This is because this "vertical"
+# implementation [one which groups shifts on a given $s[i] to form a
+# "column," unlike "horizontal" one, which groups shifts on different
+# $s[i] to form a "row"] is work in progress. It was observed to run
+# few percents faster on Intel cores, but not AMD. On AMD K8 core it's
+# whole 12% slower:-( So we face a trade-off... Shall it be resolved
+# some day? Till then the code is considered experimental and by
+# default remains dormant...
+
+sub encvert()
+{ my ($te,@s) = @_;
+  my ($v0,$v1) = ($acc,$key);
+
+	&mov	($v0,$s[3]);				# copy s3
+	&mov	(&DWP(4,"esp"),$s[2]);			# save s2
+	&mov	($v1,$s[0]);				# copy s0
+	&mov	(&DWP(8,"esp"),$s[1]);			# save s1
+
+	&movz	($s[2],&HB($s[0]));
+	&and	($s[0],0xFF);
+	&mov	($s[0],&DWP(0,$te,$s[0],8));		# s0>>0
+	&shr	($v1,16);
+	&mov	($s[3],&DWP(3,$te,$s[2],8));		# s0>>8
+	&movz	($s[1],&HB($v1));
+	&and	($v1,0xFF);
+	&mov	($s[2],&DWP(2,$te,$v1,8));		# s0>>16
+	 &mov	($v1,$v0);
+	&mov	($s[1],&DWP(1,$te,$s[1],8));		# s0>>24
+
+	&and	($v0,0xFF);
+	&xor	($s[3],&DWP(0,$te,$v0,8));		# s3>>0
+	&movz	($v0,&HB($v1));
+	&shr	($v1,16);
+	&xor	($s[2],&DWP(3,$te,$v0,8));		# s3>>8
+	&movz	($v0,&HB($v1));
+	&and	($v1,0xFF);
+	&xor	($s[1],&DWP(2,$te,$v1,8));		# s3>>16
+	 &mov	($v1,&DWP(4,"esp"));			# restore s2
+	&xor	($s[0],&DWP(1,$te,$v0,8));		# s3>>24
+
+	&mov	($v0,$v1);
+	&and	($v1,0xFF);
+	&xor	($s[2],&DWP(0,$te,$v1,8));		# s2>>0
+	&movz	($v1,&HB($v0));
+	&shr	($v0,16);
+	&xor	($s[1],&DWP(3,$te,$v1,8));		# s2>>8
+	&movz	($v1,&HB($v0));
+	&and	($v0,0xFF);
+	&xor	($s[0],&DWP(2,$te,$v0,8));		# s2>>16
+	 &mov	($v0,&DWP(8,"esp"));			# restore s1
+	&xor	($s[3],&DWP(1,$te,$v1,8));		# s2>>24
+
+	&mov	($v1,$v0);
+	&and	($v0,0xFF);
+	&xor	($s[1],&DWP(0,$te,$v0,8));		# s1>>0
+	&movz	($v0,&HB($v1));
+	&shr	($v1,16);
+	&xor	($s[0],&DWP(3,$te,$v0,8));		# s1>>8
+	&movz	($v0,&HB($v1));
+	&and	($v1,0xFF);
+	&xor	($s[3],&DWP(2,$te,$v1,8));		# s1>>16
+	 &mov	($key,$__key);				# reincarnate v1 as key
+	&xor	($s[2],&DWP(1,$te,$v0,8));		# s1>>24
+}
+
+# Another experimental routine, which features "horizontal spin," but
+# eliminates one reference to stack. Strangely enough runs slower...
+sub enchoriz()
+{ my ($v0,$v1) = ($key,$acc);
+
+	&movz	($v0,&LB($s0));			#  3, 2, 1, 0*
+	&rotr	($s2,8);			#  8,11,10, 9
+	&mov	($v1,&DWP(0,$te,$v0,8));	#  0
+	&movz	($v0,&HB($s1));			#  7, 6, 5*, 4
+	&rotr	($s3,16);			# 13,12,15,14
+	&xor	($v1,&DWP(3,$te,$v0,8));	#  5
+	&movz	($v0,&HB($s2));			#  8,11,10*, 9
+	&rotr	($s0,16);			#  1, 0, 3, 2
+	&xor	($v1,&DWP(2,$te,$v0,8));	# 10
+	&movz	($v0,&HB($s3));			# 13,12,15*,14
+	&xor	($v1,&DWP(1,$te,$v0,8));	# 15, t[0] collected
+	&mov	($__s0,$v1);			# t[0] saved
+
+	&movz	($v0,&LB($s1));			#  7, 6, 5, 4*
+	&shr	($s1,16);			#  -, -, 7, 6
+	&mov	($v1,&DWP(0,$te,$v0,8));	#  4
+	&movz	($v0,&LB($s3));			# 13,12,15,14*
+	&xor	($v1,&DWP(2,$te,$v0,8));	# 14
+	&movz	($v0,&HB($s0));			#  1, 0, 3*, 2
+	&and	($s3,0xffff0000);		# 13,12, -, -
+	&xor	($v1,&DWP(1,$te,$v0,8));	#  3
+	&movz	($v0,&LB($s2));			#  8,11,10, 9*
+	&or	($s3,$s1);			# 13,12, 7, 6
+	&xor	($v1,&DWP(3,$te,$v0,8));	#  9, t[1] collected
+	&mov	($s1,$v1);			#  s[1]=t[1]
+
+	&movz	($v0,&LB($s0));			#  1, 0, 3, 2*
+	&shr	($s2,16);			#  -, -, 8,11
+	&mov	($v1,&DWP(2,$te,$v0,8));	#  2
+	&movz	($v0,&HB($s3));			# 13,12, 7*, 6
+	&xor	($v1,&DWP(1,$te,$v0,8));	#  7
+	&movz	($v0,&HB($s2));			#  -, -, 8*,11
+	&xor	($v1,&DWP(0,$te,$v0,8));	#  8
+	&mov	($v0,$s3);
+	&shr	($v0,24);			# 13
+	&xor	($v1,&DWP(3,$te,$v0,8));	# 13, t[2] collected
+
+	&movz	($v0,&LB($s2));			#  -, -, 8,11*
+	&shr	($s0,24);			#  1*
+	&mov	($s2,&DWP(1,$te,$v0,8));	# 11
+	&xor	($s2,&DWP(3,$te,$s0,8));	#  1
+	&mov	($s0,$__s0);			# s[0]=t[0]
+	&movz	($v0,&LB($s3));			# 13,12, 7, 6*
+	&shr	($s3,16);			#   ,  ,13,12
+	&xor	($s2,&DWP(2,$te,$v0,8));	#  6
+	&mov	($key,$__key);			# reincarnate v0 as key
+	&and	($s3,0xff);			#   ,  ,13,12*
+	&mov	($s3,&DWP(0,$te,$s3,8));	# 12
+	&xor	($s3,$s2);			# s[2]=t[3] collected
+	&mov	($s2,$v1);			# s[2]=t[2]
+}
+
+# More experimental code... SSE one... Even though this one eliminates
+# *all* references to stack, it's not faster...
+sub sse_encbody()
+{
+	&movz	($acc,&LB("eax"));		#  0
+	&mov	("ecx",&DWP(0,$tbl,$acc,8));	#  0
+	&pshufw	("mm2","mm0",0x0d);		#  7, 6, 3, 2
+	&movz	("edx",&HB("eax"));		#  1
+	&mov	("edx",&DWP(3,$tbl,"edx",8));	#  1
+	&shr	("eax",16);			#  5, 4
+
+	&movz	($acc,&LB("ebx"));		# 10
+	&xor	("ecx",&DWP(2,$tbl,$acc,8));	# 10
+	&pshufw	("mm6","mm4",0x08);		# 13,12, 9, 8
+	&movz	($acc,&HB("ebx"));		# 11
+	&xor	("edx",&DWP(1,$tbl,$acc,8));	# 11
+	&shr	("ebx",16);			# 15,14
+
+	&movz	($acc,&HB("eax"));		#  5
+	&xor	("ecx",&DWP(3,$tbl,$acc,8));	#  5
+	&movq	("mm3",QWP(16,$key));
+	&movz	($acc,&HB("ebx"));		# 15
+	&xor	("ecx",&DWP(1,$tbl,$acc,8));	# 15
+	&movd	("mm0","ecx");			# t[0] collected
+
+	&movz	($acc,&LB("eax"));		#  4
+	&mov	("ecx",&DWP(0,$tbl,$acc,8));	#  4
+	&movd	("eax","mm2");			#  7, 6, 3, 2
+	&movz	($acc,&LB("ebx"));		# 14
+	&xor	("ecx",&DWP(2,$tbl,$acc,8));	# 14
+	&movd	("ebx","mm6");			# 13,12, 9, 8
+
+	&movz	($acc,&HB("eax"));		#  3
+	&xor	("ecx",&DWP(1,$tbl,$acc,8));	#  3
+	&movz	($acc,&HB("ebx"));		#  9
+	&xor	("ecx",&DWP(3,$tbl,$acc,8));	#  9
+	&movd	("mm1","ecx");			# t[1] collected
+
+	&movz	($acc,&LB("eax"));		#  2
+	&mov	("ecx",&DWP(2,$tbl,$acc,8));	#  2
+	&shr	("eax",16);			#  7, 6
+	&punpckldq	("mm0","mm1");		# t[0,1] collected
+	&movz	($acc,&LB("ebx"));		#  8
+	&xor	("ecx",&DWP(0,$tbl,$acc,8));	#  8
+	&shr	("ebx",16);			# 13,12
+
+	&movz	($acc,&HB("eax"));		#  7
+	&xor	("ecx",&DWP(1,$tbl,$acc,8));	#  7
+	&pxor	("mm0","mm3");
+	&movz	("eax",&LB("eax"));		#  6
+	&xor	("edx",&DWP(2,$tbl,"eax",8));	#  6
+	&pshufw	("mm1","mm0",0x08);		#  5, 4, 1, 0
+	&movz	($acc,&HB("ebx"));		# 13
+	&xor	("ecx",&DWP(3,$tbl,$acc,8));	# 13
+	&xor	("ecx",&DWP(24,$key));		# t[2]
+	&movd	("mm4","ecx");			# t[2] collected
+	&movz	("ebx",&LB("ebx"));		# 12
+	&xor	("edx",&DWP(0,$tbl,"ebx",8));	# 12
+	&shr	("ecx",16);
+	&movd	("eax","mm1");			#  5, 4, 1, 0
+	&mov	("ebx",&DWP(28,$key));		# t[3]
+	&xor	("ebx","edx");
+	&movd	("mm5","ebx");			# t[3] collected
+	&and	("ebx",0xffff0000);
+	&or	("ebx","ecx");
+
+	&punpckldq	("mm4","mm5");		# t[2,3] collected
+}
+
+######################################################################
+# "Compact" block function
+######################################################################
+
+sub enccompact()
+{ my $Fn = \&mov;
+  while ($#_>5) { pop(@_); $Fn=sub{}; }
+  my ($i,$te,@s)=@_;
+  my $tmp = $key;
+  my $out = $i==3?$s[0]:$acc;
+
+	# $Fn is used in first compact round and its purpose is to
+	# void restoration of some values from stack, so that after
+	# 4xenccompact with extra argument $key value is left there...
+	if ($i==3)  {	&$Fn	($key,$__key);			}##%edx
+	else        {	&mov	($out,$s[0]);			}
+			&and	($out,0xFF);
+	if ($i==1)  {	&shr	($s[0],16);			}#%ebx[1]
+	if ($i==2)  {	&shr	($s[0],24);			}#%ecx[2]
+			&movz	($out,&BP(-128,$te,$out,1));
+
+	if ($i==3)  {	$tmp=$s[1];				}##%eax
+			&movz	($tmp,&HB($s[1]));
+			&movz	($tmp,&BP(-128,$te,$tmp,1));
+			&shl	($tmp,8);
+			&xor	($out,$tmp);
+
+	if ($i==3)  {	$tmp=$s[2]; &mov ($s[1],$__s0);		}##%ebx
+	else        {	&mov	($tmp,$s[2]);
+			&shr	($tmp,16);			}
+	if ($i==2)  {	&and	($s[1],0xFF);			}#%edx[2]
+			&and	($tmp,0xFF);
+			&movz	($tmp,&BP(-128,$te,$tmp,1));
+			&shl	($tmp,16);
+			&xor	($out,$tmp);
+
+	if ($i==3)  {	$tmp=$s[3]; &mov ($s[2],$__s1);		}##%ecx
+	elsif($i==2){	&movz	($tmp,&HB($s[3]));		}#%ebx[2]
+	else        {	&mov	($tmp,$s[3]);
+			&shr	($tmp,24);			}
+			&movz	($tmp,&BP(-128,$te,$tmp,1));
+			&shl	($tmp,24);
+			&xor	($out,$tmp);
+	if ($i<2)   {	&mov	(&DWP(4+4*$i,"esp"),$out);	}
+	if ($i==3)  {	&mov	($s[3],$acc);			}
+	&comment();
+}
+
+sub enctransform()
+{ my @s = ($s0,$s1,$s2,$s3);
+  my $i = shift;
+  my $tmp = $tbl;
+  my $r2  = $key ;
+
+	&and	($tmp,$s[$i]);
+	&lea	($r2,&DWP(0,$s[$i],$s[$i]));
+	&mov	($acc,$tmp);
+	&shr	($tmp,7);
+	&and	($r2,0xfefefefe);
+	&sub	($acc,$tmp);
+	&mov	($tmp,$s[$i]);
+	&and	($acc,0x1b1b1b1b);
+	&rotr	($tmp,16);
+	&xor	($acc,$r2);	# r2
+	&mov	($r2,$s[$i]);
+
+	&xor	($s[$i],$acc);	# r0 ^ r2
+	&rotr	($r2,16+8);
+	&xor	($acc,$tmp);
+	&rotl	($s[$i],24);
+	&xor	($acc,$r2);
+	&mov	($tmp,0x80808080)	if ($i!=1);
+	&xor	($s[$i],$acc);	# ROTATE(r2^r0,24) ^ r2
+}
+
+&function_begin_B("_x86_AES_encrypt_compact");
+	# note that caller is expected to allocate stack frame for me!
+	&mov	($__key,$key);			# save key
+
+	&xor	($s0,&DWP(0,$key));		# xor with key
+	&xor	($s1,&DWP(4,$key));
+	&xor	($s2,&DWP(8,$key));
+	&xor	($s3,&DWP(12,$key));
+
+	&mov	($acc,&DWP(240,$key));		# load key->rounds
+	&lea	($acc,&DWP(-2,$acc,$acc));
+	&lea	($acc,&DWP(0,$key,$acc,8));
+	&mov	($__end,$acc);			# end of key schedule
+
+	# prefetch Te4
+	&mov	($key,&DWP(0-128,$tbl));
+	&mov	($acc,&DWP(32-128,$tbl));
+	&mov	($key,&DWP(64-128,$tbl));
+	&mov	($acc,&DWP(96-128,$tbl));
+	&mov	($key,&DWP(128-128,$tbl));
+	&mov	($acc,&DWP(160-128,$tbl));
+	&mov	($key,&DWP(192-128,$tbl));
+	&mov	($acc,&DWP(224-128,$tbl));
+
+	&set_label("loop",16);
+
+		&enccompact(0,$tbl,$s0,$s1,$s2,$s3,1);
+		&enccompact(1,$tbl,$s1,$s2,$s3,$s0,1);
+		&enccompact(2,$tbl,$s2,$s3,$s0,$s1,1);
+		&enccompact(3,$tbl,$s3,$s0,$s1,$s2,1);
+		&mov	($tbl,0x80808080);
+		&enctransform(2);
+		&enctransform(3);
+		&enctransform(0);
+		&enctransform(1);
+		&mov 	($key,$__key);
+		&mov	($tbl,$__tbl);
+		&add	($key,16);		# advance rd_key
+		&xor	($s0,&DWP(0,$key));
+		&xor	($s1,&DWP(4,$key));
+		&xor	($s2,&DWP(8,$key));
+		&xor	($s3,&DWP(12,$key));
+
+	&cmp	($key,$__end);
+	&mov	($__key,$key);
+	&jb	(&label("loop"));
+
+	&enccompact(0,$tbl,$s0,$s1,$s2,$s3);
+	&enccompact(1,$tbl,$s1,$s2,$s3,$s0);
+	&enccompact(2,$tbl,$s2,$s3,$s0,$s1);
+	&enccompact(3,$tbl,$s3,$s0,$s1,$s2);
+
+	&xor	($s0,&DWP(16,$key));
+	&xor	($s1,&DWP(20,$key));
+	&xor	($s2,&DWP(24,$key));
+	&xor	($s3,&DWP(28,$key));
+
+	&ret	();
+&function_end_B("_x86_AES_encrypt_compact");
+
+######################################################################
+# "Compact" SSE block function.
+######################################################################
+#
+# Performance is not actually extraordinary in comparison to pure
+# x86 code. In particular encrypt performance is virtually the same.
+# Decrypt performance on the other hand is 15-20% better on newer
+# µ-archs [but we're thankful for *any* improvement here], and ~50%
+# better on PIII:-) And additionally on the pros side this code
+# eliminates redundant references to stack and thus relieves/
+# minimizes the pressure on the memory bus.
+#
+# MMX register layout                           lsb
+# +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+# |          mm4          |          mm0          |
+# +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+# |     s3    |     s2    |     s1    |     s0    |    
+# +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+# |15|14|13|12|11|10| 9| 8| 7| 6| 5| 4| 3| 2| 1| 0|
+# +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+#
+# Indexes translate as s[N/4]>>(8*(N%4)), e.g. 5 means s1>>8.
+# In this terms encryption and decryption "compact" permutation
+# matrices can be depicted as following:
+#
+# encryption              lsb	# decryption              lsb
+# +----++----+----+----+----+	# +----++----+----+----+----+
+# | t0 || 15 | 10 |  5 |  0 |	# | t0 ||  7 | 10 | 13 |  0 |
+# +----++----+----+----+----+	# +----++----+----+----+----+
+# | t1 ||  3 | 14 |  9 |  4 |	# | t1 || 11 | 14 |  1 |  4 |
+# +----++----+----+----+----+	# +----++----+----+----+----+
+# | t2 ||  7 |  2 | 13 |  8 |	# | t2 || 15 |  2 |  5 |  8 |
+# +----++----+----+----+----+	# +----++----+----+----+----+
+# | t3 || 11 |  6 |  1 | 12 |	# | t3 ||  3 |  6 |  9 | 12 |
+# +----++----+----+----+----+	# +----++----+----+----+----+
+#
+######################################################################
+# Why not xmm registers? Short answer. It was actually tested and
+# was not any faster, but *contrary*, most notably on Intel CPUs.
+# Longer answer. Main advantage of using mm registers is that movd
+# latency is lower, especially on Intel P4. While arithmetic
+# instructions are twice as many, they can be scheduled every cycle
+# and not every second one when they are operating on xmm register,
+# so that "arithmetic throughput" remains virtually the same. And
+# finally the code can be executed even on elder SSE-only CPUs:-)
+
+sub sse_enccompact()
+{
+	&pshufw	("mm1","mm0",0x08);		#  5, 4, 1, 0
+	&pshufw	("mm5","mm4",0x0d);		# 15,14,11,10
+	&movd	("eax","mm1");			#  5, 4, 1, 0
+	&movd	("ebx","mm5");			# 15,14,11,10
+	&mov	($__key,$key);
+
+	&movz	($acc,&LB("eax"));		#  0
+	&movz	("edx",&HB("eax"));		#  1
+	&pshufw	("mm2","mm0",0x0d);		#  7, 6, 3, 2
+	&movz	("ecx",&BP(-128,$tbl,$acc,1));	#  0
+	&movz	($key,&LB("ebx"));		# 10
+	&movz	("edx",&BP(-128,$tbl,"edx",1));	#  1
+	&shr	("eax",16);			#  5, 4
+	&shl	("edx",8);			#  1
+
+	&movz	($acc,&BP(-128,$tbl,$key,1));	# 10
+	&movz	($key,&HB("ebx"));		# 11
+	&shl	($acc,16);			# 10
+	&pshufw	("mm6","mm4",0x08);		# 13,12, 9, 8
+	&or	("ecx",$acc);			# 10
+	&movz	($acc,&BP(-128,$tbl,$key,1));	# 11
+	&movz	($key,&HB("eax"));		#  5
+	&shl	($acc,24);			# 11
+	&shr	("ebx",16);			# 15,14
+	&or	("edx",$acc);			# 11
+
+	&movz	($acc,&BP(-128,$tbl,$key,1));	#  5
+	&movz	($key,&HB("ebx"));		# 15
+	&shl	($acc,8);			#  5
+	&or	("ecx",$acc);			#  5
+	&movz	($acc,&BP(-128,$tbl,$key,1));	# 15
+	&movz	($key,&LB("eax"));		#  4
+	&shl	($acc,24);			# 15
+	&or	("ecx",$acc);			# 15
+
+	&movz	($acc,&BP(-128,$tbl,$key,1));	#  4
+	&movz	($key,&LB("ebx"));		# 14
+	&movd	("eax","mm2");			#  7, 6, 3, 2
+	&movd	("mm0","ecx");			# t[0] collected
+	&movz	("ecx",&BP(-128,$tbl,$key,1));	# 14
+	&movz	($key,&HB("eax"));		#  3
+	&shl	("ecx",16);			# 14
+	&movd	("ebx","mm6");			# 13,12, 9, 8
+	&or	("ecx",$acc);			# 14
+
+	&movz	($acc,&BP(-128,$tbl,$key,1));	#  3
+	&movz	($key,&HB("ebx"));		#  9
+	&shl	($acc,24);			#  3
+	&or	("ecx",$acc);			#  3
+	&movz	($acc,&BP(-128,$tbl,$key,1));	#  9
+	&movz	($key,&LB("ebx"));		#  8
+	&shl	($acc,8);			#  9
+	&shr	("ebx",16);			# 13,12
+	&or	("ecx",$acc);			#  9
+
+	&movz	($acc,&BP(-128,$tbl,$key,1));	#  8
+	&movz	($key,&LB("eax"));		#  2
+	&shr	("eax",16);			#  7, 6
+	&movd	("mm1","ecx");			# t[1] collected
+	&movz	("ecx",&BP(-128,$tbl,$key,1));	#  2
+	&movz	($key,&HB("eax"));		#  7
+	&shl	("ecx",16);			#  2
+	&and	("eax",0xff);			#  6
+	&or	("ecx",$acc);			#  2
+
+	&punpckldq	("mm0","mm1");		# t[0,1] collected
+
+	&movz	($acc,&BP(-128,$tbl,$key,1));	#  7
+	&movz	($key,&HB("ebx"));		# 13
+	&shl	($acc,24);			#  7
+	&and	("ebx",0xff);			# 12
+	&movz	("eax",&BP(-128,$tbl,"eax",1));	#  6
+	&or	("ecx",$acc);			#  7
+	&shl	("eax",16);			#  6
+	&movz	($acc,&BP(-128,$tbl,$key,1));	# 13
+	&or	("edx","eax");			#  6
+	&shl	($acc,8);			# 13
+	&movz	("ebx",&BP(-128,$tbl,"ebx",1));	# 12
+	&or	("ecx",$acc);			# 13
+	&or	("edx","ebx");			# 12
+	&mov	($key,$__key);
+	&movd	("mm4","ecx");			# t[2] collected
+	&movd	("mm5","edx");			# t[3] collected
+
+	&punpckldq	("mm4","mm5");		# t[2,3] collected
+}
+
+					if (!$x86only) {
+&function_begin_B("_sse_AES_encrypt_compact");
+	&pxor	("mm0",&QWP(0,$key));	#  7, 6, 5, 4, 3, 2, 1, 0
+	&pxor	("mm4",&QWP(8,$key));	# 15,14,13,12,11,10, 9, 8
+
+	# note that caller is expected to allocate stack frame for me!
+	&mov	($acc,&DWP(240,$key));		# load key->rounds
+	&lea	($acc,&DWP(-2,$acc,$acc));
+	&lea	($acc,&DWP(0,$key,$acc,8));
+	&mov	($__end,$acc);			# end of key schedule
+
+	&mov	($s0,0x1b1b1b1b);		# magic constant
+	&mov	(&DWP(8,"esp"),$s0);
+	&mov	(&DWP(12,"esp"),$s0);
+
+	# prefetch Te4
+	&mov	($s0,&DWP(0-128,$tbl));
+	&mov	($s1,&DWP(32-128,$tbl));
+	&mov	($s2,&DWP(64-128,$tbl));
+	&mov	($s3,&DWP(96-128,$tbl));
+	&mov	($s0,&DWP(128-128,$tbl));
+	&mov	($s1,&DWP(160-128,$tbl));
+	&mov	($s2,&DWP(192-128,$tbl));
+	&mov	($s3,&DWP(224-128,$tbl));
+
+	&set_label("loop",16);
+		&sse_enccompact();
+		&add	($key,16);
+		&cmp	($key,$__end);
+		&ja	(&label("out"));
+
+		&movq	("mm2",&QWP(8,"esp"));
+		&pxor	("mm3","mm3");		&pxor	("mm7","mm7");
+		&movq	("mm1","mm0");		&movq	("mm5","mm4");	# r0
+		&pcmpgtb("mm3","mm0");		&pcmpgtb("mm7","mm4");
+		&pand	("mm3","mm2");		&pand	("mm7","mm2");
+		&pshufw	("mm2","mm0",0xb1);	&pshufw	("mm6","mm4",0xb1);# ROTATE(r0,16)
+		&paddb	("mm0","mm0");		&paddb	("mm4","mm4");
+		&pxor	("mm0","mm3");		&pxor	("mm4","mm7");	# = r2
+		&pshufw	("mm3","mm2",0xb1);	&pshufw	("mm7","mm6",0xb1);# r0
+		&pxor	("mm1","mm0");		&pxor	("mm5","mm4");	# r0^r2
+		&pxor	("mm0","mm2");		&pxor	("mm4","mm6");	# ^= ROTATE(r0,16)
+
+		&movq	("mm2","mm3");		&movq	("mm6","mm7");
+		&pslld	("mm3",8);		&pslld	("mm7",8);
+		&psrld	("mm2",24);		&psrld	("mm6",24);
+		&pxor	("mm0","mm3");		&pxor	("mm4","mm7");	# ^= r0<<8
+		&pxor	("mm0","mm2");		&pxor	("mm4","mm6");	# ^= r0>>24
+
+		&movq	("mm3","mm1");		&movq	("mm7","mm5");
+		&movq	("mm2",&QWP(0,$key));	&movq	("mm6",&QWP(8,$key));
+		&psrld	("mm1",8);		&psrld	("mm5",8);
+		&mov	($s0,&DWP(0-128,$tbl));
+		&pslld	("mm3",24);		&pslld	("mm7",24);
+		&mov	($s1,&DWP(64-128,$tbl));
+		&pxor	("mm0","mm1");		&pxor	("mm4","mm5");	# ^= (r2^r0)<<8
+		&mov	($s2,&DWP(128-128,$tbl));
+		&pxor	("mm0","mm3");		&pxor	("mm4","mm7");	# ^= (r2^r0)>>24
+		&mov	($s3,&DWP(192-128,$tbl));
+
+		&pxor	("mm0","mm2");		&pxor	("mm4","mm6");
+	&jmp	(&label("loop"));
+
+	&set_label("out",16);
+	&pxor	("mm0",&QWP(0,$key));
+	&pxor	("mm4",&QWP(8,$key));
+
+	&ret	();
+&function_end_B("_sse_AES_encrypt_compact");
+					}
+
+######################################################################
+# Vanilla block function.
+######################################################################
+
+sub encstep()
+{ my ($i,$te,@s) = @_;
+  my $tmp = $key;
+  my $out = $i==3?$s[0]:$acc;
+
+	# lines marked with #%e?x[i] denote "reordered" instructions...
+	if ($i==3)  {	&mov	($key,$__key);			}##%edx
+	else        {	&mov	($out,$s[0]);
+			&and	($out,0xFF);			}
+	if ($i==1)  {	&shr	($s[0],16);			}#%ebx[1]
+	if ($i==2)  {	&shr	($s[0],24);			}#%ecx[2]
+			&mov	($out,&DWP(0,$te,$out,8));
+
+	if ($i==3)  {	$tmp=$s[1];				}##%eax
+			&movz	($tmp,&HB($s[1]));
+			&xor	($out,&DWP(3,$te,$tmp,8));
+
+	if ($i==3)  {	$tmp=$s[2]; &mov ($s[1],$__s0);		}##%ebx
+	else        {	&mov	($tmp,$s[2]);
+			&shr	($tmp,16);			}
+	if ($i==2)  {	&and	($s[1],0xFF);			}#%edx[2]
+			&and	($tmp,0xFF);
+			&xor	($out,&DWP(2,$te,$tmp,8));
+
+	if ($i==3)  {	$tmp=$s[3]; &mov ($s[2],$__s1);		}##%ecx
+	elsif($i==2){	&movz	($tmp,&HB($s[3]));		}#%ebx[2]
+	else        {	&mov	($tmp,$s[3]); 
+			&shr	($tmp,24)			}
+			&xor	($out,&DWP(1,$te,$tmp,8));
+	if ($i<2)   {	&mov	(&DWP(4+4*$i,"esp"),$out);	}
+	if ($i==3)  {	&mov	($s[3],$acc);			}
+			&comment();
+}
+
+sub enclast()
+{ my ($i,$te,@s)=@_;
+  my $tmp = $key;
+  my $out = $i==3?$s[0]:$acc;
+
+	if ($i==3)  {	&mov	($key,$__key);			}##%edx
+	else        {	&mov	($out,$s[0]);			}
+			&and	($out,0xFF);
+	if ($i==1)  {	&shr	($s[0],16);			}#%ebx[1]
+	if ($i==2)  {	&shr	($s[0],24);			}#%ecx[2]
+			&mov	($out,&DWP(2,$te,$out,8));
+			&and	($out,0x000000ff);
+
+	if ($i==3)  {	$tmp=$s[1];				}##%eax
+			&movz	($tmp,&HB($s[1]));
+			&mov	($tmp,&DWP(0,$te,$tmp,8));
+			&and	($tmp,0x0000ff00);
+			&xor	($out,$tmp);
+
+	if ($i==3)  {	$tmp=$s[2]; &mov ($s[1],$__s0);		}##%ebx
+	else        {	&mov	($tmp,$s[2]);
+			&shr	($tmp,16);			}
+	if ($i==2)  {	&and	($s[1],0xFF);			}#%edx[2]
+			&and	($tmp,0xFF);
+			&mov	($tmp,&DWP(0,$te,$tmp,8));
+			&and	($tmp,0x00ff0000);
+			&xor	($out,$tmp);
+
+	if ($i==3)  {	$tmp=$s[3]; &mov ($s[2],$__s1);		}##%ecx
+	elsif($i==2){	&movz	($tmp,&HB($s[3]));		}#%ebx[2]
+	else        {	&mov	($tmp,$s[3]);
+			&shr	($tmp,24);			}
+			&mov	($tmp,&DWP(2,$te,$tmp,8));
+			&and	($tmp,0xff000000);
+			&xor	($out,$tmp);
+	if ($i<2)   {	&mov	(&DWP(4+4*$i,"esp"),$out);	}
+	if ($i==3)  {	&mov	($s[3],$acc);			}
+}
+
+&function_begin_B("_x86_AES_encrypt");
+	if ($vertical_spin) {
+		# I need high parts of volatile registers to be accessible...
+		&exch	($s1="edi",$key="ebx");
+		&mov	($s2="esi",$acc="ecx");
+	}
+
+	# note that caller is expected to allocate stack frame for me!
+	&mov	($__key,$key);			# save key
+
+	&xor	($s0,&DWP(0,$key));		# xor with key
+	&xor	($s1,&DWP(4,$key));
+	&xor	($s2,&DWP(8,$key));
+	&xor	($s3,&DWP(12,$key));
+
+	&mov	($acc,&DWP(240,$key));		# load key->rounds
+
+	if ($small_footprint) {
+	    &lea	($acc,&DWP(-2,$acc,$acc));
+	    &lea	($acc,&DWP(0,$key,$acc,8));
+	    &mov	($__end,$acc);		# end of key schedule
+
+	    &set_label("loop",16);
+		if ($vertical_spin) {
+		    &encvert($tbl,$s0,$s1,$s2,$s3);
+		} else {
+		    &encstep(0,$tbl,$s0,$s1,$s2,$s3);
+		    &encstep(1,$tbl,$s1,$s2,$s3,$s0);
+		    &encstep(2,$tbl,$s2,$s3,$s0,$s1);
+		    &encstep(3,$tbl,$s3,$s0,$s1,$s2);
+		}
+		&add	($key,16);		# advance rd_key
+		&xor	($s0,&DWP(0,$key));
+		&xor	($s1,&DWP(4,$key));
+		&xor	($s2,&DWP(8,$key));
+		&xor	($s3,&DWP(12,$key));
+	    &cmp	($key,$__end);
+	    &mov	($__key,$key);
+	    &jb		(&label("loop"));
+	}
+	else {
+	    &cmp	($acc,10);
+	    &jle	(&label("10rounds"));
+	    &cmp	($acc,12);
+	    &jle	(&label("12rounds"));
+
+	&set_label("14rounds",4);
+	    for ($i=1;$i<3;$i++) {
+		if ($vertical_spin) {
+		    &encvert($tbl,$s0,$s1,$s2,$s3);
+		} else {
+		    &encstep(0,$tbl,$s0,$s1,$s2,$s3);
+		    &encstep(1,$tbl,$s1,$s2,$s3,$s0);
+		    &encstep(2,$tbl,$s2,$s3,$s0,$s1);
+		    &encstep(3,$tbl,$s3,$s0,$s1,$s2);
+		}
+		&xor	($s0,&DWP(16*$i+0,$key));
+		&xor	($s1,&DWP(16*$i+4,$key));
+		&xor	($s2,&DWP(16*$i+8,$key));
+		&xor	($s3,&DWP(16*$i+12,$key));
+	    }
+	    &add	($key,32);
+	    &mov	($__key,$key);		# advance rd_key
+	&set_label("12rounds",4);
+	    for ($i=1;$i<3;$i++) {
+		if ($vertical_spin) {
+		    &encvert($tbl,$s0,$s1,$s2,$s3);
+		} else {
+		    &encstep(0,$tbl,$s0,$s1,$s2,$s3);
+		    &encstep(1,$tbl,$s1,$s2,$s3,$s0);
+		    &encstep(2,$tbl,$s2,$s3,$s0,$s1);
+		    &encstep(3,$tbl,$s3,$s0,$s1,$s2);
+		}
+		&xor	($s0,&DWP(16*$i+0,$key));
+		&xor	($s1,&DWP(16*$i+4,$key));
+		&xor	($s2,&DWP(16*$i+8,$key));
+		&xor	($s3,&DWP(16*$i+12,$key));
+	    }
+	    &add	($key,32);
+	    &mov	($__key,$key);		# advance rd_key
+	&set_label("10rounds",4);
+	    for ($i=1;$i<10;$i++) {
+		if ($vertical_spin) {
+		    &encvert($tbl,$s0,$s1,$s2,$s3);
+		} else {
+		    &encstep(0,$tbl,$s0,$s1,$s2,$s3);
+		    &encstep(1,$tbl,$s1,$s2,$s3,$s0);
+		    &encstep(2,$tbl,$s2,$s3,$s0,$s1);
+		    &encstep(3,$tbl,$s3,$s0,$s1,$s2);
+		}
+		&xor	($s0,&DWP(16*$i+0,$key));
+		&xor	($s1,&DWP(16*$i+4,$key));
+		&xor	($s2,&DWP(16*$i+8,$key));
+		&xor	($s3,&DWP(16*$i+12,$key));
+	    }
+	}
+
+	if ($vertical_spin) {
+	    # "reincarnate" some registers for "horizontal" spin...
+	    &mov	($s1="ebx",$key="edi");
+	    &mov	($s2="ecx",$acc="esi");
+	}
+	&enclast(0,$tbl,$s0,$s1,$s2,$s3);
+	&enclast(1,$tbl,$s1,$s2,$s3,$s0);
+	&enclast(2,$tbl,$s2,$s3,$s0,$s1);
+	&enclast(3,$tbl,$s3,$s0,$s1,$s2);
+
+	&add	($key,$small_footprint?16:160);
+	&xor	($s0,&DWP(0,$key));
+	&xor	($s1,&DWP(4,$key));
+	&xor	($s2,&DWP(8,$key));
+	&xor	($s3,&DWP(12,$key));
+
+	&ret	();
+
+&set_label("AES_Te",64);	# Yes! I keep it in the code segment!
+	&_data_word(0xa56363c6, 0x847c7cf8, 0x997777ee, 0x8d7b7bf6);
+	&_data_word(0x0df2f2ff, 0xbd6b6bd6, 0xb16f6fde, 0x54c5c591);
+	&_data_word(0x50303060, 0x03010102, 0xa96767ce, 0x7d2b2b56);
+	&_data_word(0x19fefee7, 0x62d7d7b5, 0xe6abab4d, 0x9a7676ec);
+	&_data_word(0x45caca8f, 0x9d82821f, 0x40c9c989, 0x877d7dfa);
+	&_data_word(0x15fafaef, 0xeb5959b2, 0xc947478e, 0x0bf0f0fb);
+	&_data_word(0xecadad41, 0x67d4d4b3, 0xfda2a25f, 0xeaafaf45);
+	&_data_word(0xbf9c9c23, 0xf7a4a453, 0x967272e4, 0x5bc0c09b);
+	&_data_word(0xc2b7b775, 0x1cfdfde1, 0xae93933d, 0x6a26264c);
+	&_data_word(0x5a36366c, 0x413f3f7e, 0x02f7f7f5, 0x4fcccc83);
+	&_data_word(0x5c343468, 0xf4a5a551, 0x34e5e5d1, 0x08f1f1f9);
+	&_data_word(0x937171e2, 0x73d8d8ab, 0x53313162, 0x3f15152a);
+	&_data_word(0x0c040408, 0x52c7c795, 0x65232346, 0x5ec3c39d);
+	&_data_word(0x28181830, 0xa1969637, 0x0f05050a, 0xb59a9a2f);
+	&_data_word(0x0907070e, 0x36121224, 0x9b80801b, 0x3de2e2df);
+	&_data_word(0x26ebebcd, 0x6927274e, 0xcdb2b27f, 0x9f7575ea);
+	&_data_word(0x1b090912, 0x9e83831d, 0x742c2c58, 0x2e1a1a34);
+	&_data_word(0x2d1b1b36, 0xb26e6edc, 0xee5a5ab4, 0xfba0a05b);
+	&_data_word(0xf65252a4, 0x4d3b3b76, 0x61d6d6b7, 0xceb3b37d);
+	&_data_word(0x7b292952, 0x3ee3e3dd, 0x712f2f5e, 0x97848413);
+	&_data_word(0xf55353a6, 0x68d1d1b9, 0x00000000, 0x2cededc1);
+	&_data_word(0x60202040, 0x1ffcfce3, 0xc8b1b179, 0xed5b5bb6);
+	&_data_word(0xbe6a6ad4, 0x46cbcb8d, 0xd9bebe67, 0x4b393972);
+	&_data_word(0xde4a4a94, 0xd44c4c98, 0xe85858b0, 0x4acfcf85);
+	&_data_word(0x6bd0d0bb, 0x2aefefc5, 0xe5aaaa4f, 0x16fbfbed);
+	&_data_word(0xc5434386, 0xd74d4d9a, 0x55333366, 0x94858511);
+	&_data_word(0xcf45458a, 0x10f9f9e9, 0x06020204, 0x817f7ffe);
+	&_data_word(0xf05050a0, 0x443c3c78, 0xba9f9f25, 0xe3a8a84b);
+	&_data_word(0xf35151a2, 0xfea3a35d, 0xc0404080, 0x8a8f8f05);
+	&_data_word(0xad92923f, 0xbc9d9d21, 0x48383870, 0x04f5f5f1);
+	&_data_word(0xdfbcbc63, 0xc1b6b677, 0x75dadaaf, 0x63212142);
+	&_data_word(0x30101020, 0x1affffe5, 0x0ef3f3fd, 0x6dd2d2bf);
+	&_data_word(0x4ccdcd81, 0x140c0c18, 0x35131326, 0x2fececc3);
+	&_data_word(0xe15f5fbe, 0xa2979735, 0xcc444488, 0x3917172e);
+	&_data_word(0x57c4c493, 0xf2a7a755, 0x827e7efc, 0x473d3d7a);
+	&_data_word(0xac6464c8, 0xe75d5dba, 0x2b191932, 0x957373e6);
+	&_data_word(0xa06060c0, 0x98818119, 0xd14f4f9e, 0x7fdcdca3);
+	&_data_word(0x66222244, 0x7e2a2a54, 0xab90903b, 0x8388880b);
+	&_data_word(0xca46468c, 0x29eeeec7, 0xd3b8b86b, 0x3c141428);
+	&_data_word(0x79dedea7, 0xe25e5ebc, 0x1d0b0b16, 0x76dbdbad);
+	&_data_word(0x3be0e0db, 0x56323264, 0x4e3a3a74, 0x1e0a0a14);
+	&_data_word(0xdb494992, 0x0a06060c, 0x6c242448, 0xe45c5cb8);
+	&_data_word(0x5dc2c29f, 0x6ed3d3bd, 0xefacac43, 0xa66262c4);
+	&_data_word(0xa8919139, 0xa4959531, 0x37e4e4d3, 0x8b7979f2);
+	&_data_word(0x32e7e7d5, 0x43c8c88b, 0x5937376e, 0xb76d6dda);
+	&_data_word(0x8c8d8d01, 0x64d5d5b1, 0xd24e4e9c, 0xe0a9a949);
+	&_data_word(0xb46c6cd8, 0xfa5656ac, 0x07f4f4f3, 0x25eaeacf);
+	&_data_word(0xaf6565ca, 0x8e7a7af4, 0xe9aeae47, 0x18080810);
+	&_data_word(0xd5baba6f, 0x887878f0, 0x6f25254a, 0x722e2e5c);
+	&_data_word(0x241c1c38, 0xf1a6a657, 0xc7b4b473, 0x51c6c697);
+	&_data_word(0x23e8e8cb, 0x7cdddda1, 0x9c7474e8, 0x211f1f3e);
+	&_data_word(0xdd4b4b96, 0xdcbdbd61, 0x868b8b0d, 0x858a8a0f);
+	&_data_word(0x907070e0, 0x423e3e7c, 0xc4b5b571, 0xaa6666cc);
+	&_data_word(0xd8484890, 0x05030306, 0x01f6f6f7, 0x120e0e1c);
+	&_data_word(0xa36161c2, 0x5f35356a, 0xf95757ae, 0xd0b9b969);
+	&_data_word(0x91868617, 0x58c1c199, 0x271d1d3a, 0xb99e9e27);
+	&_data_word(0x38e1e1d9, 0x13f8f8eb, 0xb398982b, 0x33111122);
+	&_data_word(0xbb6969d2, 0x70d9d9a9, 0x898e8e07, 0xa7949433);
+	&_data_word(0xb69b9b2d, 0x221e1e3c, 0x92878715, 0x20e9e9c9);
+	&_data_word(0x49cece87, 0xff5555aa, 0x78282850, 0x7adfdfa5);
+	&_data_word(0x8f8c8c03, 0xf8a1a159, 0x80898909, 0x170d0d1a);
+	&_data_word(0xdabfbf65, 0x31e6e6d7, 0xc6424284, 0xb86868d0);
+	&_data_word(0xc3414182, 0xb0999929, 0x772d2d5a, 0x110f0f1e);
+	&_data_word(0xcbb0b07b, 0xfc5454a8, 0xd6bbbb6d, 0x3a16162c);
+
+#Te4	# four copies of Te4 to choose from to avoid L1 aliasing
+	&data_byte(0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5);
+	&data_byte(0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76);
+	&data_byte(0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0);
+	&data_byte(0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0);
+	&data_byte(0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc);
+	&data_byte(0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15);
+	&data_byte(0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a);
+	&data_byte(0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75);
+	&data_byte(0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0);
+	&data_byte(0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84);
+	&data_byte(0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b);
+	&data_byte(0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf);
+	&data_byte(0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85);
+	&data_byte(0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8);
+	&data_byte(0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5);
+	&data_byte(0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2);
+	&data_byte(0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17);
+	&data_byte(0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73);
+	&data_byte(0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88);
+	&data_byte(0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb);
+	&data_byte(0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c);
+	&data_byte(0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79);
+	&data_byte(0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9);
+	&data_byte(0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08);
+	&data_byte(0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6);
+	&data_byte(0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a);
+	&data_byte(0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e);
+	&data_byte(0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e);
+	&data_byte(0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94);
+	&data_byte(0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf);
+	&data_byte(0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68);
+	&data_byte(0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16);
+
+	&data_byte(0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5);
+	&data_byte(0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76);
+	&data_byte(0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0);
+	&data_byte(0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0);
+	&data_byte(0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc);
+	&data_byte(0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15);
+	&data_byte(0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a);
+	&data_byte(0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75);
+	&data_byte(0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0);
+	&data_byte(0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84);
+	&data_byte(0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b);
+	&data_byte(0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf);
+	&data_byte(0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85);
+	&data_byte(0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8);
+	&data_byte(0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5);
+	&data_byte(0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2);
+	&data_byte(0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17);
+	&data_byte(0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73);
+	&data_byte(0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88);
+	&data_byte(0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb);
+	&data_byte(0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c);
+	&data_byte(0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79);
+	&data_byte(0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9);
+	&data_byte(0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08);
+	&data_byte(0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6);
+	&data_byte(0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a);
+	&data_byte(0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e);
+	&data_byte(0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e);
+	&data_byte(0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94);
+	&data_byte(0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf);
+	&data_byte(0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68);
+	&data_byte(0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16);
+
+	&data_byte(0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5);
+	&data_byte(0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76);
+	&data_byte(0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0);
+	&data_byte(0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0);
+	&data_byte(0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc);
+	&data_byte(0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15);
+	&data_byte(0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a);
+	&data_byte(0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75);
+	&data_byte(0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0);
+	&data_byte(0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84);
+	&data_byte(0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b);
+	&data_byte(0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf);
+	&data_byte(0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85);
+	&data_byte(0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8);
+	&data_byte(0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5);
+	&data_byte(0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2);
+	&data_byte(0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17);
+	&data_byte(0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73);
+	&data_byte(0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88);
+	&data_byte(0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb);
+	&data_byte(0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c);
+	&data_byte(0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79);
+	&data_byte(0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9);
+	&data_byte(0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08);
+	&data_byte(0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6);
+	&data_byte(0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a);
+	&data_byte(0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e);
+	&data_byte(0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e);
+	&data_byte(0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94);
+	&data_byte(0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf);
+	&data_byte(0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68);
+	&data_byte(0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16);
+
+	&data_byte(0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5);
+	&data_byte(0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76);
+	&data_byte(0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0);
+	&data_byte(0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0);
+	&data_byte(0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc);
+	&data_byte(0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15);
+	&data_byte(0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a);
+	&data_byte(0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75);
+	&data_byte(0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0);
+	&data_byte(0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84);
+	&data_byte(0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b);
+	&data_byte(0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf);
+	&data_byte(0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85);
+	&data_byte(0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8);
+	&data_byte(0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5);
+	&data_byte(0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2);
+	&data_byte(0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17);
+	&data_byte(0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73);
+	&data_byte(0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88);
+	&data_byte(0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb);
+	&data_byte(0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c);
+	&data_byte(0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79);
+	&data_byte(0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9);
+	&data_byte(0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08);
+	&data_byte(0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6);
+	&data_byte(0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a);
+	&data_byte(0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e);
+	&data_byte(0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e);
+	&data_byte(0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94);
+	&data_byte(0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf);
+	&data_byte(0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68);
+	&data_byte(0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16);
+#rcon:
+	&data_word(0x00000001, 0x00000002, 0x00000004, 0x00000008);
+	&data_word(0x00000010, 0x00000020, 0x00000040, 0x00000080);
+	&data_word(0x0000001b, 0x00000036, 0x00000000, 0x00000000);
+	&data_word(0x00000000, 0x00000000, 0x00000000, 0x00000000);
+&function_end_B("_x86_AES_encrypt");
+
+# void AES_encrypt (const void *inp,void *out,const AES_KEY *key);
+&function_begin("AES_encrypt");
+	&mov	($acc,&wparam(0));		# load inp
+	&mov	($key,&wparam(2));		# load key
+
+	&mov	($s0,"esp");
+	&sub	("esp",36);
+	&and	("esp",-64);			# align to cache-line
+
+	# place stack frame just "above" the key schedule
+	&lea	($s1,&DWP(-64-63,$key));
+	&sub	($s1,"esp");
+	&neg	($s1);
+	&and	($s1,0x3C0);	# modulo 1024, but aligned to cache-line
+	&sub	("esp",$s1);
+	&add	("esp",4);	# 4 is reserved for caller's return address
+	&mov	($_esp,$s0);			# save stack pointer
+
+	&call   (&label("pic_point"));          # make it PIC!
+	&set_label("pic_point");
+	&blindpop($tbl);
+	&picmeup($s0,"OPENSSL_ia32cap_P",$tbl,&label("pic_point")) if (!$x86only);
+	&lea    ($tbl,&DWP(&label("AES_Te")."-".&label("pic_point"),$tbl));
+
+	# pick Te4 copy which can't "overlap" with stack frame or key schedule
+	&lea	($s1,&DWP(768-4,"esp"));
+	&sub	($s1,$tbl);
+	&and	($s1,0x300);
+	&lea	($tbl,&DWP(2048+128,$tbl,$s1));
+
+					if (!$x86only) {
+	&bt	(&DWP(0,$s0),25);	# check for SSE bit
+	&jnc	(&label("x86"));
+
+	&movq	("mm0",&QWP(0,$acc));
+	&movq	("mm4",&QWP(8,$acc));
+	&call	("_sse_AES_encrypt_compact");
+	&mov	("esp",$_esp);			# restore stack pointer
+	&mov	($acc,&wparam(1));		# load out
+	&movq	(&QWP(0,$acc),"mm0");		# write output data
+	&movq	(&QWP(8,$acc),"mm4");
+	&emms	();
+	&function_end_A();
+					}
+	&set_label("x86",16);
+	&mov	($_tbl,$tbl);
+	&mov	($s0,&DWP(0,$acc));		# load input data
+	&mov	($s1,&DWP(4,$acc));
+	&mov	($s2,&DWP(8,$acc));
+	&mov	($s3,&DWP(12,$acc));
+	&call	("_x86_AES_encrypt_compact");
+	&mov	("esp",$_esp);			# restore stack pointer
+	&mov	($acc,&wparam(1));		# load out
+	&mov	(&DWP(0,$acc),$s0);		# write output data
+	&mov	(&DWP(4,$acc),$s1);
+	&mov	(&DWP(8,$acc),$s2);
+	&mov	(&DWP(12,$acc),$s3);
+&function_end("AES_encrypt");
+
+#--------------------------------------------------------------------#
+
+######################################################################
+# "Compact" block function
+######################################################################
+
+sub deccompact()
+{ my $Fn = \&mov;
+  while ($#_>5) { pop(@_); $Fn=sub{}; }
+  my ($i,$td,@s)=@_;
+  my $tmp = $key;
+  my $out = $i==3?$s[0]:$acc;
+
+	# $Fn is used in first compact round and its purpose is to
+	# void restoration of some values from stack, so that after
+	# 4xdeccompact with extra argument $key, $s0 and $s1 values
+	# are left there...
+	if($i==3)   {	&$Fn	($key,$__key);			}
+	else        {	&mov	($out,$s[0]);			}
+			&and	($out,0xFF);
+			&movz	($out,&BP(-128,$td,$out,1));
+
+	if ($i==3)  {	$tmp=$s[1];				}
+			&movz	($tmp,&HB($s[1]));
+			&movz	($tmp,&BP(-128,$td,$tmp,1));
+			&shl	($tmp,8);
+			&xor	($out,$tmp);
+
+	if ($i==3)  {	$tmp=$s[2]; &mov ($s[1],$acc);		}
+	else        {	mov	($tmp,$s[2]);			}
+			&shr	($tmp,16);
+			&and	($tmp,0xFF);
+			&movz	($tmp,&BP(-128,$td,$tmp,1));
+			&shl	($tmp,16);
+			&xor	($out,$tmp);
+
+	if ($i==3)  {	$tmp=$s[3]; &$Fn ($s[2],$__s1);		}
+	else        {	&mov	($tmp,$s[3]);			}
+			&shr	($tmp,24);
+			&movz	($tmp,&BP(-128,$td,$tmp,1));
+			&shl	($tmp,24);
+			&xor	($out,$tmp);
+	if ($i<2)   {	&mov	(&DWP(4+4*$i,"esp"),$out);	}
+	if ($i==3)  {	&$Fn	($s[3],$__s0);			}
+}
+
+# must be called with 2,3,0,1 as argument sequence!!!
+sub dectransform()
+{ my @s = ($s0,$s1,$s2,$s3);
+  my $i = shift;
+  my $tmp = $key;
+  my $tp2 = @s[($i+2)%4]; $tp2 = @s[2] if ($i==1);
+  my $tp4 = @s[($i+3)%4]; $tp4 = @s[3] if ($i==1);
+  my $tp8 = $tbl;
+
+	&mov	($tmp,0x80808080);
+	&and	($tmp,$s[$i]);
+	&mov	($acc,$tmp);
+	&shr	($tmp,7);
+	&lea	($tp2,&DWP(0,$s[$i],$s[$i]));
+	&sub	($acc,$tmp);
+	&and	($tp2,0xfefefefe);
+	&and	($acc,0x1b1b1b1b);
+	&xor	($tp2,$acc);
+	&mov	($tmp,0x80808080);
+
+	&and	($tmp,$tp2);
+	&mov	($acc,$tmp);
+	&shr	($tmp,7);
+	&lea	($tp4,&DWP(0,$tp2,$tp2));
+	&sub	($acc,$tmp);
+	&and	($tp4,0xfefefefe);
+	&and	($acc,0x1b1b1b1b);
+	 &xor	($tp2,$s[$i]);	# tp2^tp1
+	&xor	($tp4,$acc);
+	&mov	($tmp,0x80808080);
+
+	&and	($tmp,$tp4);
+	&mov	($acc,$tmp);
+	&shr	($tmp,7);
+	&lea	($tp8,&DWP(0,$tp4,$tp4));
+	&sub	($acc,$tmp);
+	&and	($tp8,0xfefefefe);
+	&and	($acc,0x1b1b1b1b);
+	 &xor	($tp4,$s[$i]);	# tp4^tp1
+	 &rotl	($s[$i],8);	# = ROTATE(tp1,8)
+	&xor	($tp8,$acc);
+
+	&xor	($s[$i],$tp2);
+	&xor	($tp2,$tp8);
+	&xor	($s[$i],$tp4);
+	&xor	($tp4,$tp8);
+	&rotl	($tp2,24);
+	&xor	($s[$i],$tp8);	# ^= tp8^(tp4^tp1)^(tp2^tp1)
+	&rotl	($tp4,16);
+	&xor	($s[$i],$tp2);	# ^= ROTATE(tp8^tp2^tp1,24)
+	&rotl	($tp8,8);
+	&xor	($s[$i],$tp4);	# ^= ROTATE(tp8^tp4^tp1,16)
+	 &mov	($s[0],$__s0)			if($i==2); #prefetch $s0
+	 &mov	($s[1],$__s1)			if($i==3); #prefetch $s1
+	 &mov	($s[2],$__s2)			if($i==1);
+	&xor	($s[$i],$tp8);	# ^= ROTATE(tp8,8)
+
+	&mov	($s[3],$__s3)			if($i==1);
+	&mov	(&DWP(4+4*$i,"esp"),$s[$i])	if($i>=2);
+}
+
+&function_begin_B("_x86_AES_decrypt_compact");
+	# note that caller is expected to allocate stack frame for me!
+	&mov	($__key,$key);			# save key
+
+	&xor	($s0,&DWP(0,$key));		# xor with key
+	&xor	($s1,&DWP(4,$key));
+	&xor	($s2,&DWP(8,$key));
+	&xor	($s3,&DWP(12,$key));
+
+	&mov	($acc,&DWP(240,$key));		# load key->rounds
+
+	&lea	($acc,&DWP(-2,$acc,$acc));
+	&lea	($acc,&DWP(0,$key,$acc,8));
+	&mov	($__end,$acc);			# end of key schedule
+
+	# prefetch Td4
+	&mov	($key,&DWP(0-128,$tbl));
+	&mov	($acc,&DWP(32-128,$tbl));
+	&mov	($key,&DWP(64-128,$tbl));
+	&mov	($acc,&DWP(96-128,$tbl));
+	&mov	($key,&DWP(128-128,$tbl));
+	&mov	($acc,&DWP(160-128,$tbl));
+	&mov	($key,&DWP(192-128,$tbl));
+	&mov	($acc,&DWP(224-128,$tbl));
+
+	&set_label("loop",16);
+
+		&deccompact(0,$tbl,$s0,$s3,$s2,$s1,1);
+		&deccompact(1,$tbl,$s1,$s0,$s3,$s2,1);
+		&deccompact(2,$tbl,$s2,$s1,$s0,$s3,1);
+		&deccompact(3,$tbl,$s3,$s2,$s1,$s0,1);
+		&dectransform(2);
+		&dectransform(3);
+		&dectransform(0);
+		&dectransform(1);
+		&mov 	($key,$__key);
+		&mov	($tbl,$__tbl);
+		&add	($key,16);		# advance rd_key
+		&xor	($s0,&DWP(0,$key));
+		&xor	($s1,&DWP(4,$key));
+		&xor	($s2,&DWP(8,$key));
+		&xor	($s3,&DWP(12,$key));
+
+	&cmp	($key,$__end);
+	&mov	($__key,$key);
+	&jb	(&label("loop"));
+
+	&deccompact(0,$tbl,$s0,$s3,$s2,$s1);
+	&deccompact(1,$tbl,$s1,$s0,$s3,$s2);
+	&deccompact(2,$tbl,$s2,$s1,$s0,$s3);
+	&deccompact(3,$tbl,$s3,$s2,$s1,$s0);
+
+	&xor	($s0,&DWP(16,$key));
+	&xor	($s1,&DWP(20,$key));
+	&xor	($s2,&DWP(24,$key));
+	&xor	($s3,&DWP(28,$key));
+
+	&ret	();
+&function_end_B("_x86_AES_decrypt_compact");
+
+######################################################################
+# "Compact" SSE block function.
+######################################################################
+
+sub sse_deccompact()
+{
+	&pshufw	("mm1","mm0",0x0c);		#  7, 6, 1, 0
+	&pshufw	("mm5","mm4",0x09);		# 13,12,11,10
+	&movd	("eax","mm1");			#  7, 6, 1, 0
+	&movd	("ebx","mm5");			# 13,12,11,10
+	&mov	($__key,$key);
+
+	&movz	($acc,&LB("eax"));		#  0
+	&movz	("edx",&HB("eax"));		#  1
+	&pshufw	("mm2","mm0",0x06);		#  3, 2, 5, 4
+	&movz	("ecx",&BP(-128,$tbl,$acc,1));	#  0
+	&movz	($key,&LB("ebx"));		# 10
+	&movz	("edx",&BP(-128,$tbl,"edx",1));	#  1
+	&shr	("eax",16);			#  7, 6
+	&shl	("edx",8);			#  1
+
+	&movz	($acc,&BP(-128,$tbl,$key,1));	# 10
+	&movz	($key,&HB("ebx"));		# 11
+	&shl	($acc,16);			# 10
+	&pshufw	("mm6","mm4",0x03);		# 9, 8,15,14
+	&or	("ecx",$acc);			# 10
+	&movz	($acc,&BP(-128,$tbl,$key,1));	# 11
+	&movz	($key,&HB("eax"));		#  7
+	&shl	($acc,24);			# 11
+	&shr	("ebx",16);			# 13,12
+	&or	("edx",$acc);			# 11
+
+	&movz	($acc,&BP(-128,$tbl,$key,1));	#  7
+	&movz	($key,&HB("ebx"));		# 13
+	&shl	($acc,24);			#  7
+	&or	("ecx",$acc);			#  7
+	&movz	($acc,&BP(-128,$tbl,$key,1));	# 13
+	&movz	($key,&LB("eax"));		#  6
+	&shl	($acc,8);			# 13
+	&movd	("eax","mm2");			#  3, 2, 5, 4
+	&or	("ecx",$acc);			# 13
+
+	&movz	($acc,&BP(-128,$tbl,$key,1));	#  6
+	&movz	($key,&LB("ebx"));		# 12
+	&shl	($acc,16);			#  6
+	&movd	("ebx","mm6");			#  9, 8,15,14
+	&movd	("mm0","ecx");			# t[0] collected
+	&movz	("ecx",&BP(-128,$tbl,$key,1));	# 12
+	&movz	($key,&LB("eax"));		#  4
+	&or	("ecx",$acc);			# 12
+
+	&movz	($acc,&BP(-128,$tbl,$key,1));	#  4
+	&movz	($key,&LB("ebx"));		# 14
+	&or	("edx",$acc);			#  4
+	&movz	($acc,&BP(-128,$tbl,$key,1));	# 14
+	&movz	($key,&HB("eax"));		#  5
+	&shl	($acc,16);			# 14
+	&shr	("eax",16);			#  3, 2
+	&or	("edx",$acc);			# 14
+
+	&movz	($acc,&BP(-128,$tbl,$key,1));	#  5
+	&movz	($key,&HB("ebx"));		# 15
+	&shr	("ebx",16);			#  9, 8
+	&shl	($acc,8);			#  5
+	&movd	("mm1","edx");			# t[1] collected
+	&movz	("edx",&BP(-128,$tbl,$key,1));	# 15
+	&movz	($key,&HB("ebx"));		#  9
+	&shl	("edx",24);			# 15
+	&and	("ebx",0xff);			#  8
+	&or	("edx",$acc);			# 15
+
+	&punpckldq	("mm0","mm1");		# t[0,1] collected
+
+	&movz	($acc,&BP(-128,$tbl,$key,1));	#  9
+	&movz	($key,&LB("eax"));		#  2
+	&shl	($acc,8);			#  9
+	&movz	("eax",&HB("eax"));		#  3
+	&movz	("ebx",&BP(-128,$tbl,"ebx",1));	#  8
+	&or	("ecx",$acc);			#  9
+	&movz	($acc,&BP(-128,$tbl,$key,1));	#  2
+	&or	("edx","ebx");			#  8
+	&shl	($acc,16);			#  2
+	&movz	("eax",&BP(-128,$tbl,"eax",1));	#  3
+	&or	("edx",$acc);			#  2
+	&shl	("eax",24);			#  3
+	&or	("ecx","eax");			#  3
+	&mov	($key,$__key);
+	&movd	("mm4","edx");			# t[2] collected
+	&movd	("mm5","ecx");			# t[3] collected
+
+	&punpckldq	("mm4","mm5");		# t[2,3] collected
+}
+
+					if (!$x86only) {
+&function_begin_B("_sse_AES_decrypt_compact");
+	&pxor	("mm0",&QWP(0,$key));	#  7, 6, 5, 4, 3, 2, 1, 0
+	&pxor	("mm4",&QWP(8,$key));	# 15,14,13,12,11,10, 9, 8
+
+	# note that caller is expected to allocate stack frame for me!
+	&mov	($acc,&DWP(240,$key));		# load key->rounds
+	&lea	($acc,&DWP(-2,$acc,$acc));
+	&lea	($acc,&DWP(0,$key,$acc,8));
+	&mov	($__end,$acc);			# end of key schedule
+
+	&mov	($s0,0x1b1b1b1b);		# magic constant
+	&mov	(&DWP(8,"esp"),$s0);
+	&mov	(&DWP(12,"esp"),$s0);
+
+	# prefetch Td4
+	&mov	($s0,&DWP(0-128,$tbl));
+	&mov	($s1,&DWP(32-128,$tbl));
+	&mov	($s2,&DWP(64-128,$tbl));
+	&mov	($s3,&DWP(96-128,$tbl));
+	&mov	($s0,&DWP(128-128,$tbl));
+	&mov	($s1,&DWP(160-128,$tbl));
+	&mov	($s2,&DWP(192-128,$tbl));
+	&mov	($s3,&DWP(224-128,$tbl));
+
+	&set_label("loop",16);
+		&sse_deccompact();
+		&add	($key,16);
+		&cmp	($key,$__end);
+		&ja	(&label("out"));
+
+		# ROTATE(x^y,N) == ROTATE(x,N)^ROTATE(y,N)
+		&movq	("mm3","mm0");		&movq	("mm7","mm4");
+		&movq	("mm2","mm0",1);	&movq	("mm6","mm4",1);
+		&movq	("mm1","mm0");		&movq	("mm5","mm4");
+		&pshufw	("mm0","mm0",0xb1);	&pshufw	("mm4","mm4",0xb1);# = ROTATE(tp0,16)
+		&pslld	("mm2",8);		&pslld	("mm6",8);
+		&psrld	("mm3",8);		&psrld	("mm7",8);
+		&pxor	("mm0","mm2");		&pxor	("mm4","mm6");	# ^= tp0<<8
+		&pxor	("mm0","mm3");		&pxor	("mm4","mm7");	# ^= tp0>>8
+		&pslld	("mm2",16);		&pslld	("mm6",16);
+		&psrld	("mm3",16);		&psrld	("mm7",16);
+		&pxor	("mm0","mm2");		&pxor	("mm4","mm6");	# ^= tp0<<24
+		&pxor	("mm0","mm3");		&pxor	("mm4","mm7");	# ^= tp0>>24
+
+		&movq	("mm3",&QWP(8,"esp"));
+		&pxor	("mm2","mm2");		&pxor	("mm6","mm6");
+		&pcmpgtb("mm2","mm1");		&pcmpgtb("mm6","mm5");
+		&pand	("mm2","mm3");		&pand	("mm6","mm3");
+		&paddb	("mm1","mm1");		&paddb	("mm5","mm5");
+		&pxor	("mm1","mm2");		&pxor	("mm5","mm6");	# tp2
+		&movq	("mm3","mm1");		&movq	("mm7","mm5");
+		&movq	("mm2","mm1");		&movq	("mm6","mm5");
+		&pxor	("mm0","mm1");		&pxor	("mm4","mm5");	# ^= tp2
+		&pslld	("mm3",24);		&pslld	("mm7",24);
+		&psrld	("mm2",8);		&psrld	("mm6",8);
+		&pxor	("mm0","mm3");		&pxor	("mm4","mm7");	# ^= tp2<<24
+		&pxor	("mm0","mm2");		&pxor	("mm4","mm6");	# ^= tp2>>8
+
+		&movq	("mm2",&QWP(8,"esp"));
+		&pxor	("mm3","mm3");		&pxor	("mm7","mm7");
+		&pcmpgtb("mm3","mm1");		&pcmpgtb("mm7","mm5");
+		&pand	("mm3","mm2");		&pand	("mm7","mm2");
+		&paddb	("mm1","mm1");		&paddb	("mm5","mm5");
+		&pxor	("mm1","mm3");		&pxor	("mm5","mm7");	# tp4
+		&pshufw	("mm3","mm1",0xb1);	&pshufw	("mm7","mm5",0xb1);
+		&pxor	("mm0","mm1");		&pxor	("mm4","mm5");	# ^= tp4
+		&pxor	("mm0","mm3");		&pxor	("mm4","mm7");	# ^= ROTATE(tp4,16)	
+
+		&pxor	("mm3","mm3");		&pxor	("mm7","mm7");
+		&pcmpgtb("mm3","mm1");		&pcmpgtb("mm7","mm5");
+		&pand	("mm3","mm2");		&pand	("mm7","mm2");
+		&paddb	("mm1","mm1");		&paddb	("mm5","mm5");
+		&pxor	("mm1","mm3");		&pxor	("mm5","mm7");	# tp8
+		&pxor	("mm0","mm1");		&pxor	("mm4","mm5");	# ^= tp8
+		&movq	("mm3","mm1");		&movq	("mm7","mm5");
+		&pshufw	("mm2","mm1",0xb1);	&pshufw	("mm6","mm5",0xb1);
+		&pxor	("mm0","mm2");		&pxor	("mm4","mm6");	# ^= ROTATE(tp8,16)
+		&pslld	("mm1",8);		&pslld	("mm5",8);
+		&psrld	("mm3",8);		&psrld	("mm7",8);
+		&movq	("mm2",&QWP(0,$key));	&movq	("mm6",&QWP(8,$key));
+		&pxor	("mm0","mm1");		&pxor	("mm4","mm5");	# ^= tp8<<8
+		&pxor	("mm0","mm3");		&pxor	("mm4","mm7");	# ^= tp8>>8
+		&mov	($s0,&DWP(0-128,$tbl));
+		&pslld	("mm1",16);		&pslld	("mm5",16);
+		&mov	($s1,&DWP(64-128,$tbl));
+		&psrld	("mm3",16);		&psrld	("mm7",16);
+		&mov	($s2,&DWP(128-128,$tbl));
+		&pxor	("mm0","mm1");		&pxor	("mm4","mm5");	# ^= tp8<<24
+		&mov	($s3,&DWP(192-128,$tbl));
+		&pxor	("mm0","mm3");		&pxor	("mm4","mm7");	# ^= tp8>>24
+
+		&pxor	("mm0","mm2");		&pxor	("mm4","mm6");
+	&jmp	(&label("loop"));
+
+	&set_label("out",16);
+	&pxor	("mm0",&QWP(0,$key));
+	&pxor	("mm4",&QWP(8,$key));
+
+	&ret	();
+&function_end_B("_sse_AES_decrypt_compact");
+					}
+
+######################################################################
+# Vanilla block function.
+######################################################################
+
+sub decstep()
+{ my ($i,$td,@s) = @_;
+  my $tmp = $key;
+  my $out = $i==3?$s[0]:$acc;
+
+	# no instructions are reordered, as performance appears
+	# optimal... or rather that all attempts to reorder didn't
+	# result in better performance [which by the way is not a
+	# bit lower than ecryption].
+	if($i==3)   {	&mov	($key,$__key);			}
+	else        {	&mov	($out,$s[0]);			}
+			&and	($out,0xFF);
+			&mov	($out,&DWP(0,$td,$out,8));
+
+	if ($i==3)  {	$tmp=$s[1];				}
+			&movz	($tmp,&HB($s[1]));
+			&xor	($out,&DWP(3,$td,$tmp,8));
+
+	if ($i==3)  {	$tmp=$s[2]; &mov ($s[1],$acc);		}
+	else        {	&mov	($tmp,$s[2]);			}
+			&shr	($tmp,16);
+			&and	($tmp,0xFF);
+			&xor	($out,&DWP(2,$td,$tmp,8));
+
+	if ($i==3)  {	$tmp=$s[3]; &mov ($s[2],$__s1);		}
+	else        {	&mov	($tmp,$s[3]);			}
+			&shr	($tmp,24);
+			&xor	($out,&DWP(1,$td,$tmp,8));
+	if ($i<2)   {	&mov	(&DWP(4+4*$i,"esp"),$out);	}
+	if ($i==3)  {	&mov	($s[3],$__s0);			}
+			&comment();
+}
+
+sub declast()
+{ my ($i,$td,@s)=@_;
+  my $tmp = $key;
+  my $out = $i==3?$s[0]:$acc;
+
+	if($i==0)   {	&lea	($td,&DWP(2048+128,$td));
+			&mov	($tmp,&DWP(0-128,$td));
+			&mov	($acc,&DWP(32-128,$td));
+			&mov	($tmp,&DWP(64-128,$td));
+			&mov	($acc,&DWP(96-128,$td));
+			&mov	($tmp,&DWP(128-128,$td));
+			&mov	($acc,&DWP(160-128,$td));
+			&mov	($tmp,&DWP(192-128,$td));
+			&mov	($acc,&DWP(224-128,$td));
+			&lea	($td,&DWP(-128,$td));		}
+	if($i==3)   {	&mov	($key,$__key);			}
+	else        {	&mov	($out,$s[0]);			}
+			&and	($out,0xFF);
+			&movz	($out,&BP(0,$td,$out,1));
+
+	if ($i==3)  {	$tmp=$s[1];				}
+			&movz	($tmp,&HB($s[1]));
+			&movz	($tmp,&BP(0,$td,$tmp,1));
+			&shl	($tmp,8);
+			&xor	($out,$tmp);
+
+	if ($i==3)  {	$tmp=$s[2]; &mov ($s[1],$acc);		}
+	else        {	mov	($tmp,$s[2]);			}
+			&shr	($tmp,16);
+			&and	($tmp,0xFF);
+			&movz	($tmp,&BP(0,$td,$tmp,1));
+			&shl	($tmp,16);
+			&xor	($out,$tmp);
+
+	if ($i==3)  {	$tmp=$s[3]; &mov ($s[2],$__s1);		}
+	else        {	&mov	($tmp,$s[3]);			}
+			&shr	($tmp,24);
+			&movz	($tmp,&BP(0,$td,$tmp,1));
+			&shl	($tmp,24);
+			&xor	($out,$tmp);
+	if ($i<2)   {	&mov	(&DWP(4+4*$i,"esp"),$out);	}
+	if ($i==3)  {	&mov	($s[3],$__s0);
+			&lea	($td,&DWP(-2048,$td));		}
+}
+
+&function_begin_B("_x86_AES_decrypt");
+	# note that caller is expected to allocate stack frame for me!
+	&mov	($__key,$key);			# save key
+
+	&xor	($s0,&DWP(0,$key));		# xor with key
+	&xor	($s1,&DWP(4,$key));
+	&xor	($s2,&DWP(8,$key));
+	&xor	($s3,&DWP(12,$key));
+
+	&mov	($acc,&DWP(240,$key));		# load key->rounds
+
+	if ($small_footprint) {
+	    &lea	($acc,&DWP(-2,$acc,$acc));
+	    &lea	($acc,&DWP(0,$key,$acc,8));
+	    &mov	($__end,$acc);		# end of key schedule
+	    &set_label("loop",16);
+		&decstep(0,$tbl,$s0,$s3,$s2,$s1);
+		&decstep(1,$tbl,$s1,$s0,$s3,$s2);
+		&decstep(2,$tbl,$s2,$s1,$s0,$s3);
+		&decstep(3,$tbl,$s3,$s2,$s1,$s0);
+		&add	($key,16);		# advance rd_key
+		&xor	($s0,&DWP(0,$key));
+		&xor	($s1,&DWP(4,$key));
+		&xor	($s2,&DWP(8,$key));
+		&xor	($s3,&DWP(12,$key));
+	    &cmp	($key,$__end);
+	    &mov	($__key,$key);
+	    &jb		(&label("loop"));
+	}
+	else {
+	    &cmp	($acc,10);
+	    &jle	(&label("10rounds"));
+	    &cmp	($acc,12);
+	    &jle	(&label("12rounds"));
+
+	&set_label("14rounds",4);
+	    for ($i=1;$i<3;$i++) {
+		&decstep(0,$tbl,$s0,$s3,$s2,$s1);
+		&decstep(1,$tbl,$s1,$s0,$s3,$s2);
+		&decstep(2,$tbl,$s2,$s1,$s0,$s3);
+		&decstep(3,$tbl,$s3,$s2,$s1,$s0);
+		&xor	($s0,&DWP(16*$i+0,$key));
+		&xor	($s1,&DWP(16*$i+4,$key));
+		&xor	($s2,&DWP(16*$i+8,$key));
+		&xor	($s3,&DWP(16*$i+12,$key));
+	    }
+	    &add	($key,32);
+	    &mov	($__key,$key);		# advance rd_key
+	&set_label("12rounds",4);
+	    for ($i=1;$i<3;$i++) {
+		&decstep(0,$tbl,$s0,$s3,$s2,$s1);
+		&decstep(1,$tbl,$s1,$s0,$s3,$s2);
+		&decstep(2,$tbl,$s2,$s1,$s0,$s3);
+		&decstep(3,$tbl,$s3,$s2,$s1,$s0);
+		&xor	($s0,&DWP(16*$i+0,$key));
+		&xor	($s1,&DWP(16*$i+4,$key));
+		&xor	($s2,&DWP(16*$i+8,$key));
+		&xor	($s3,&DWP(16*$i+12,$key));
+	    }
+	    &add	($key,32);
+	    &mov	($__key,$key);		# advance rd_key
+	&set_label("10rounds",4);
+	    for ($i=1;$i<10;$i++) {
+		&decstep(0,$tbl,$s0,$s3,$s2,$s1);
+		&decstep(1,$tbl,$s1,$s0,$s3,$s2);
+		&decstep(2,$tbl,$s2,$s1,$s0,$s3);
+		&decstep(3,$tbl,$s3,$s2,$s1,$s0);
+		&xor	($s0,&DWP(16*$i+0,$key));
+		&xor	($s1,&DWP(16*$i+4,$key));
+		&xor	($s2,&DWP(16*$i+8,$key));
+		&xor	($s3,&DWP(16*$i+12,$key));
+	    }
+	}
+
+	&declast(0,$tbl,$s0,$s3,$s2,$s1);
+	&declast(1,$tbl,$s1,$s0,$s3,$s2);
+	&declast(2,$tbl,$s2,$s1,$s0,$s3);
+	&declast(3,$tbl,$s3,$s2,$s1,$s0);
+
+	&add	($key,$small_footprint?16:160);
+	&xor	($s0,&DWP(0,$key));
+	&xor	($s1,&DWP(4,$key));
+	&xor	($s2,&DWP(8,$key));
+	&xor	($s3,&DWP(12,$key));
+
+	&ret	();
+
+&set_label("AES_Td",64);	# Yes! I keep it in the code segment!
+	&_data_word(0x50a7f451, 0x5365417e, 0xc3a4171a, 0x965e273a);
+	&_data_word(0xcb6bab3b, 0xf1459d1f, 0xab58faac, 0x9303e34b);
+	&_data_word(0x55fa3020, 0xf66d76ad, 0x9176cc88, 0x254c02f5);
+	&_data_word(0xfcd7e54f, 0xd7cb2ac5, 0x80443526, 0x8fa362b5);
+	&_data_word(0x495ab1de, 0x671bba25, 0x980eea45, 0xe1c0fe5d);
+	&_data_word(0x02752fc3, 0x12f04c81, 0xa397468d, 0xc6f9d36b);
+	&_data_word(0xe75f8f03, 0x959c9215, 0xeb7a6dbf, 0xda595295);
+	&_data_word(0x2d83bed4, 0xd3217458, 0x2969e049, 0x44c8c98e);
+	&_data_word(0x6a89c275, 0x78798ef4, 0x6b3e5899, 0xdd71b927);
+	&_data_word(0xb64fe1be, 0x17ad88f0, 0x66ac20c9, 0xb43ace7d);
+	&_data_word(0x184adf63, 0x82311ae5, 0x60335197, 0x457f5362);
+	&_data_word(0xe07764b1, 0x84ae6bbb, 0x1ca081fe, 0x942b08f9);
+	&_data_word(0x58684870, 0x19fd458f, 0x876cde94, 0xb7f87b52);
+	&_data_word(0x23d373ab, 0xe2024b72, 0x578f1fe3, 0x2aab5566);
+	&_data_word(0x0728ebb2, 0x03c2b52f, 0x9a7bc586, 0xa50837d3);
+	&_data_word(0xf2872830, 0xb2a5bf23, 0xba6a0302, 0x5c8216ed);
+	&_data_word(0x2b1ccf8a, 0x92b479a7, 0xf0f207f3, 0xa1e2694e);
+	&_data_word(0xcdf4da65, 0xd5be0506, 0x1f6234d1, 0x8afea6c4);
+	&_data_word(0x9d532e34, 0xa055f3a2, 0x32e18a05, 0x75ebf6a4);
+	&_data_word(0x39ec830b, 0xaaef6040, 0x069f715e, 0x51106ebd);
+	&_data_word(0xf98a213e, 0x3d06dd96, 0xae053edd, 0x46bde64d);
+	&_data_word(0xb58d5491, 0x055dc471, 0x6fd40604, 0xff155060);
+	&_data_word(0x24fb9819, 0x97e9bdd6, 0xcc434089, 0x779ed967);
+	&_data_word(0xbd42e8b0, 0x888b8907, 0x385b19e7, 0xdbeec879);
+	&_data_word(0x470a7ca1, 0xe90f427c, 0xc91e84f8, 0x00000000);
+	&_data_word(0x83868009, 0x48ed2b32, 0xac70111e, 0x4e725a6c);
+	&_data_word(0xfbff0efd, 0x5638850f, 0x1ed5ae3d, 0x27392d36);
+	&_data_word(0x64d90f0a, 0x21a65c68, 0xd1545b9b, 0x3a2e3624);
+	&_data_word(0xb1670a0c, 0x0fe75793, 0xd296eeb4, 0x9e919b1b);
+	&_data_word(0x4fc5c080, 0xa220dc61, 0x694b775a, 0x161a121c);
+	&_data_word(0x0aba93e2, 0xe52aa0c0, 0x43e0223c, 0x1d171b12);
+	&_data_word(0x0b0d090e, 0xadc78bf2, 0xb9a8b62d, 0xc8a91e14);
+	&_data_word(0x8519f157, 0x4c0775af, 0xbbdd99ee, 0xfd607fa3);
+	&_data_word(0x9f2601f7, 0xbcf5725c, 0xc53b6644, 0x347efb5b);
+	&_data_word(0x7629438b, 0xdcc623cb, 0x68fcedb6, 0x63f1e4b8);
+	&_data_word(0xcadc31d7, 0x10856342, 0x40229713, 0x2011c684);
+	&_data_word(0x7d244a85, 0xf83dbbd2, 0x1132f9ae, 0x6da129c7);
+	&_data_word(0x4b2f9e1d, 0xf330b2dc, 0xec52860d, 0xd0e3c177);
+	&_data_word(0x6c16b32b, 0x99b970a9, 0xfa489411, 0x2264e947);
+	&_data_word(0xc48cfca8, 0x1a3ff0a0, 0xd82c7d56, 0xef903322);
+	&_data_word(0xc74e4987, 0xc1d138d9, 0xfea2ca8c, 0x360bd498);
+	&_data_word(0xcf81f5a6, 0x28de7aa5, 0x268eb7da, 0xa4bfad3f);
+	&_data_word(0xe49d3a2c, 0x0d927850, 0x9bcc5f6a, 0x62467e54);
+	&_data_word(0xc2138df6, 0xe8b8d890, 0x5ef7392e, 0xf5afc382);
+	&_data_word(0xbe805d9f, 0x7c93d069, 0xa92dd56f, 0xb31225cf);
+	&_data_word(0x3b99acc8, 0xa77d1810, 0x6e639ce8, 0x7bbb3bdb);
+	&_data_word(0x097826cd, 0xf418596e, 0x01b79aec, 0xa89a4f83);
+	&_data_word(0x656e95e6, 0x7ee6ffaa, 0x08cfbc21, 0xe6e815ef);
+	&_data_word(0xd99be7ba, 0xce366f4a, 0xd4099fea, 0xd67cb029);
+	&_data_word(0xafb2a431, 0x31233f2a, 0x3094a5c6, 0xc066a235);
+	&_data_word(0x37bc4e74, 0xa6ca82fc, 0xb0d090e0, 0x15d8a733);
+	&_data_word(0x4a9804f1, 0xf7daec41, 0x0e50cd7f, 0x2ff69117);
+	&_data_word(0x8dd64d76, 0x4db0ef43, 0x544daacc, 0xdf0496e4);
+	&_data_word(0xe3b5d19e, 0x1b886a4c, 0xb81f2cc1, 0x7f516546);
+	&_data_word(0x04ea5e9d, 0x5d358c01, 0x737487fa, 0x2e410bfb);
+	&_data_word(0x5a1d67b3, 0x52d2db92, 0x335610e9, 0x1347d66d);
+	&_data_word(0x8c61d79a, 0x7a0ca137, 0x8e14f859, 0x893c13eb);
+	&_data_word(0xee27a9ce, 0x35c961b7, 0xede51ce1, 0x3cb1477a);
+	&_data_word(0x59dfd29c, 0x3f73f255, 0x79ce1418, 0xbf37c773);
+	&_data_word(0xeacdf753, 0x5baafd5f, 0x146f3ddf, 0x86db4478);
+	&_data_word(0x81f3afca, 0x3ec468b9, 0x2c342438, 0x5f40a3c2);
+	&_data_word(0x72c31d16, 0x0c25e2bc, 0x8b493c28, 0x41950dff);
+	&_data_word(0x7101a839, 0xdeb30c08, 0x9ce4b4d8, 0x90c15664);
+	&_data_word(0x6184cb7b, 0x70b632d5, 0x745c6c48, 0x4257b8d0);
+
+#Td4:	# four copies of Td4 to choose from to avoid L1 aliasing
+	&data_byte(0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38);
+	&data_byte(0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb);
+	&data_byte(0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87);
+	&data_byte(0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb);
+	&data_byte(0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d);
+	&data_byte(0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e);
+	&data_byte(0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2);
+	&data_byte(0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25);
+	&data_byte(0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16);
+	&data_byte(0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92);
+	&data_byte(0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda);
+	&data_byte(0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84);
+	&data_byte(0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a);
+	&data_byte(0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06);
+	&data_byte(0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02);
+	&data_byte(0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b);
+	&data_byte(0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea);
+	&data_byte(0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73);
+	&data_byte(0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85);
+	&data_byte(0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e);
+	&data_byte(0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89);
+	&data_byte(0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b);
+	&data_byte(0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20);
+	&data_byte(0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4);
+	&data_byte(0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31);
+	&data_byte(0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f);
+	&data_byte(0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d);
+	&data_byte(0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef);
+	&data_byte(0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0);
+	&data_byte(0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61);
+	&data_byte(0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26);
+	&data_byte(0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d);
+
+	&data_byte(0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38);
+	&data_byte(0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb);
+	&data_byte(0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87);
+	&data_byte(0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb);
+	&data_byte(0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d);
+	&data_byte(0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e);
+	&data_byte(0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2);
+	&data_byte(0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25);
+	&data_byte(0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16);
+	&data_byte(0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92);
+	&data_byte(0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda);
+	&data_byte(0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84);
+	&data_byte(0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a);
+	&data_byte(0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06);
+	&data_byte(0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02);
+	&data_byte(0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b);
+	&data_byte(0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea);
+	&data_byte(0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73);
+	&data_byte(0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85);
+	&data_byte(0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e);
+	&data_byte(0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89);
+	&data_byte(0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b);
+	&data_byte(0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20);
+	&data_byte(0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4);
+	&data_byte(0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31);
+	&data_byte(0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f);
+	&data_byte(0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d);
+	&data_byte(0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef);
+	&data_byte(0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0);
+	&data_byte(0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61);
+	&data_byte(0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26);
+	&data_byte(0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d);
+
+	&data_byte(0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38);
+	&data_byte(0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb);
+	&data_byte(0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87);
+	&data_byte(0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb);
+	&data_byte(0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d);
+	&data_byte(0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e);
+	&data_byte(0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2);
+	&data_byte(0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25);
+	&data_byte(0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16);
+	&data_byte(0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92);
+	&data_byte(0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda);
+	&data_byte(0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84);
+	&data_byte(0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a);
+	&data_byte(0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06);
+	&data_byte(0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02);
+	&data_byte(0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b);
+	&data_byte(0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea);
+	&data_byte(0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73);
+	&data_byte(0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85);
+	&data_byte(0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e);
+	&data_byte(0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89);
+	&data_byte(0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b);
+	&data_byte(0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20);
+	&data_byte(0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4);
+	&data_byte(0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31);
+	&data_byte(0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f);
+	&data_byte(0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d);
+	&data_byte(0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef);
+	&data_byte(0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0);
+	&data_byte(0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61);
+	&data_byte(0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26);
+	&data_byte(0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d);
+
+	&data_byte(0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38);
+	&data_byte(0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb);
+	&data_byte(0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87);
+	&data_byte(0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb);
+	&data_byte(0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d);
+	&data_byte(0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e);
+	&data_byte(0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2);
+	&data_byte(0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25);
+	&data_byte(0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16);
+	&data_byte(0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92);
+	&data_byte(0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda);
+	&data_byte(0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84);
+	&data_byte(0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a);
+	&data_byte(0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06);
+	&data_byte(0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02);
+	&data_byte(0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b);
+	&data_byte(0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea);
+	&data_byte(0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73);
+	&data_byte(0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85);
+	&data_byte(0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e);
+	&data_byte(0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89);
+	&data_byte(0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b);
+	&data_byte(0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20);
+	&data_byte(0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4);
+	&data_byte(0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31);
+	&data_byte(0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f);
+	&data_byte(0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d);
+	&data_byte(0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef);
+	&data_byte(0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0);
+	&data_byte(0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61);
+	&data_byte(0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26);
+	&data_byte(0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d);
+&function_end_B("_x86_AES_decrypt");
+
+# void AES_decrypt (const void *inp,void *out,const AES_KEY *key);
+&function_begin("AES_decrypt");
+	&mov	($acc,&wparam(0));		# load inp
+	&mov	($key,&wparam(2));		# load key
+
+	&mov	($s0,"esp");
+	&sub	("esp",36);
+	&and	("esp",-64);			# align to cache-line
+
+	# place stack frame just "above" the key schedule
+	&lea	($s1,&DWP(-64-63,$key));
+	&sub	($s1,"esp");
+	&neg	($s1);
+	&and	($s1,0x3C0);	# modulo 1024, but aligned to cache-line
+	&sub	("esp",$s1);
+	&add	("esp",4);	# 4 is reserved for caller's return address
+	&mov	($_esp,$s0);	# save stack pointer
+
+	&call   (&label("pic_point"));          # make it PIC!
+	&set_label("pic_point");
+	&blindpop($tbl);
+	&picmeup($s0,"OPENSSL_ia32cap_P",$tbl,&label("pic_point")) if(!$x86only);
+	&lea    ($tbl,&DWP(&label("AES_Td")."-".&label("pic_point"),$tbl));
+
+	# pick Td4 copy which can't "overlap" with stack frame or key schedule
+	&lea	($s1,&DWP(768-4,"esp"));
+	&sub	($s1,$tbl);
+	&and	($s1,0x300);
+	&lea	($tbl,&DWP(2048+128,$tbl,$s1));
+
+					if (!$x86only) {
+	&bt	(&DWP(0,$s0),25);	# check for SSE bit
+	&jnc	(&label("x86"));
+
+	&movq	("mm0",&QWP(0,$acc));
+	&movq	("mm4",&QWP(8,$acc));
+	&call	("_sse_AES_decrypt_compact");
+	&mov	("esp",$_esp);			# restore stack pointer
+	&mov	($acc,&wparam(1));		# load out
+	&movq	(&QWP(0,$acc),"mm0");		# write output data
+	&movq	(&QWP(8,$acc),"mm4");
+	&emms	();
+	&function_end_A();
+					}
+	&set_label("x86",16);
+	&mov	($_tbl,$tbl);
+	&mov	($s0,&DWP(0,$acc));		# load input data
+	&mov	($s1,&DWP(4,$acc));
+	&mov	($s2,&DWP(8,$acc));
+	&mov	($s3,&DWP(12,$acc));
+	&call	("_x86_AES_decrypt_compact");
+	&mov	("esp",$_esp);			# restore stack pointer
+	&mov	($acc,&wparam(1));		# load out
+	&mov	(&DWP(0,$acc),$s0);		# write output data
+	&mov	(&DWP(4,$acc),$s1);
+	&mov	(&DWP(8,$acc),$s2);
+	&mov	(&DWP(12,$acc),$s3);
+&function_end("AES_decrypt");
+
+# void AES_cbc_encrypt (const void char *inp, unsigned char *out,
+#			size_t length, const AES_KEY *key,
+#			unsigned char *ivp,const int enc);
+{
+# stack frame layout
+#             -4(%esp)		# return address	 0(%esp)
+#              0(%esp)		# s0 backing store	 4(%esp)	
+#              4(%esp)		# s1 backing store	 8(%esp)
+#              8(%esp)		# s2 backing store	12(%esp)
+#             12(%esp)		# s3 backing store	16(%esp)
+#             16(%esp)		# key backup		20(%esp)
+#             20(%esp)		# end of key schedule	24(%esp)
+#             24(%esp)		# %ebp backup		28(%esp)
+#             28(%esp)		# %esp backup
+my $_inp=&DWP(32,"esp");	# copy of wparam(0)
+my $_out=&DWP(36,"esp");	# copy of wparam(1)
+my $_len=&DWP(40,"esp");	# copy of wparam(2)
+my $_key=&DWP(44,"esp");	# copy of wparam(3)
+my $_ivp=&DWP(48,"esp");	# copy of wparam(4)
+my $_tmp=&DWP(52,"esp");	# volatile variable
+#
+my $ivec=&DWP(60,"esp");	# ivec[16]
+my $aes_key=&DWP(76,"esp");	# copy of aes_key
+my $mark=&DWP(76+240,"esp");	# copy of aes_key->rounds
+
+&function_begin("AES_cbc_encrypt");
+	&mov	($s2 eq "ecx"? $s2 : "",&wparam(2));	# load len
+	&cmp	($s2,0);
+	&je	(&label("drop_out"));
+
+	&call   (&label("pic_point"));		# make it PIC!
+	&set_label("pic_point");
+	&blindpop($tbl);
+	&picmeup($s0,"OPENSSL_ia32cap_P",$tbl,&label("pic_point")) if(!$x86only);
+
+	&cmp	(&wparam(5),0);
+	&lea    ($tbl,&DWP(&label("AES_Te")."-".&label("pic_point"),$tbl));
+	&jne	(&label("picked_te"));
+	&lea	($tbl,&DWP(&label("AES_Td")."-".&label("AES_Te"),$tbl));
+	&set_label("picked_te");
+
+	# one can argue if this is required
+	&pushf	();
+	&cld	();
+
+	&cmp	($s2,$speed_limit);
+	&jb	(&label("slow_way"));
+	&test	($s2,15);
+	&jnz	(&label("slow_way"));
+					if (!$x86only) {
+	&bt	(&DWP(0,$s0),28);	# check for hyper-threading bit
+	&jc	(&label("slow_way"));
+					}
+	# pre-allocate aligned stack frame...
+	&lea	($acc,&DWP(-80-244,"esp"));
+	&and	($acc,-64);
+
+	# ... and make sure it doesn't alias with $tbl modulo 4096
+	&mov	($s0,$tbl);
+	&lea	($s1,&DWP(2048+256,$tbl));
+	&mov	($s3,$acc);
+	&and	($s0,0xfff);		# s = %ebp&0xfff
+	&and	($s1,0xfff);		# e = (%ebp+2048+256)&0xfff
+	&and	($s3,0xfff);		# p = %esp&0xfff
+
+	&cmp	($s3,$s1);		# if (p>=e) %esp =- (p-e);
+	&jb	(&label("tbl_break_out"));
+	&sub	($s3,$s1);
+	&sub	($acc,$s3);
+	&jmp	(&label("tbl_ok"));
+	&set_label("tbl_break_out",4);	# else %esp -= (p-s)&0xfff + framesz;
+	&sub	($s3,$s0);
+	&and	($s3,0xfff);
+	&add	($s3,384);
+	&sub	($acc,$s3);
+	&set_label("tbl_ok",4);
+
+	&lea	($s3,&wparam(0));	# obtain pointer to parameter block
+	&exch	("esp",$acc);		# allocate stack frame
+	&add	("esp",4);		# reserve for return address!
+	&mov	($_tbl,$tbl);		# save %ebp
+	&mov	($_esp,$acc);		# save %esp
+
+	&mov	($s0,&DWP(0,$s3));	# load inp
+	&mov	($s1,&DWP(4,$s3));	# load out
+	#&mov	($s2,&DWP(8,$s3));	# load len
+	&mov	($key,&DWP(12,$s3));	# load key
+	&mov	($acc,&DWP(16,$s3));	# load ivp
+	&mov	($s3,&DWP(20,$s3));	# load enc flag
+
+	&mov	($_inp,$s0);		# save copy of inp
+	&mov	($_out,$s1);		# save copy of out
+	&mov	($_len,$s2);		# save copy of len
+	&mov	($_key,$key);		# save copy of key
+	&mov	($_ivp,$acc);		# save copy of ivp
+
+	&mov	($mark,0);		# copy of aes_key->rounds = 0;
+	# do we copy key schedule to stack?
+	&mov	($s1 eq "ebx" ? $s1 : "",$key);
+	&mov	($s2 eq "ecx" ? $s2 : "",244/4);
+	&sub	($s1,$tbl);
+	&mov	("esi",$key);
+	&and	($s1,0xfff);
+	&lea	("edi",$aes_key);
+	&cmp	($s1,2048+256);
+	&jb	(&label("do_copy"));
+	&cmp	($s1,4096-244);
+	&jb	(&label("skip_copy"));
+	&set_label("do_copy",4);
+		&mov	($_key,"edi");
+		&data_word(0xA5F3F689);	# rep movsd
+	&set_label("skip_copy");
+
+	&mov	($key,16);
+	&set_label("prefetch_tbl",4);
+		&mov	($s0,&DWP(0,$tbl));
+		&mov	($s1,&DWP(32,$tbl));
+		&mov	($s2,&DWP(64,$tbl));
+		&mov	($acc,&DWP(96,$tbl));
+		&lea	($tbl,&DWP(128,$tbl));
+		&sub	($key,1);
+	&jnz	(&label("prefetch_tbl"));
+	&sub	($tbl,2048);
+
+	&mov	($acc,$_inp);
+	&mov	($key,$_ivp);
+
+	&cmp	($s3,0);
+	&je	(&label("fast_decrypt"));
+
+#----------------------------- ENCRYPT -----------------------------#
+	&mov	($s0,&DWP(0,$key));		# load iv
+	&mov	($s1,&DWP(4,$key));
+
+	&set_label("fast_enc_loop",16);
+		&mov	($s2,&DWP(8,$key));
+		&mov	($s3,&DWP(12,$key));
+
+		&xor	($s0,&DWP(0,$acc));	# xor input data
+		&xor	($s1,&DWP(4,$acc));
+		&xor	($s2,&DWP(8,$acc));
+		&xor	($s3,&DWP(12,$acc));
+
+		&mov	($key,$_key);		# load key
+		&call	("_x86_AES_encrypt");
+
+		&mov	($acc,$_inp);		# load inp
+		&mov	($key,$_out);		# load out
+
+		&mov	(&DWP(0,$key),$s0);	# save output data
+		&mov	(&DWP(4,$key),$s1);
+		&mov	(&DWP(8,$key),$s2);
+		&mov	(&DWP(12,$key),$s3);
+
+		&lea	($acc,&DWP(16,$acc));	# advance inp
+		&mov	($s2,$_len);		# load len
+		&mov	($_inp,$acc);		# save inp
+		&lea	($s3,&DWP(16,$key));	# advance out
+		&mov	($_out,$s3);		# save out
+		&sub	($s2,16);		# decrease len
+		&mov	($_len,$s2);		# save len
+	&jnz	(&label("fast_enc_loop"));
+	&mov	($acc,$_ivp);		# load ivp
+	&mov	($s2,&DWP(8,$key));	# restore last 2 dwords
+	&mov	($s3,&DWP(12,$key));
+	&mov	(&DWP(0,$acc),$s0);	# save ivec
+	&mov	(&DWP(4,$acc),$s1);
+	&mov	(&DWP(8,$acc),$s2);
+	&mov	(&DWP(12,$acc),$s3);
+
+	&cmp	($mark,0);		# was the key schedule copied?
+	&mov	("edi",$_key);
+	&je	(&label("skip_ezero"));
+	# zero copy of key schedule
+	&mov	("ecx",240/4);
+	&xor	("eax","eax");
+	&align	(4);
+	&data_word(0xABF3F689);		# rep stosd
+	&set_label("skip_ezero");
+	&mov	("esp",$_esp);
+	&popf	();
+    &set_label("drop_out");
+	&function_end_A();
+	&pushf	();			# kludge, never executed
+
+#----------------------------- DECRYPT -----------------------------#
+&set_label("fast_decrypt",16);
+
+	&cmp	($acc,$_out);
+	&je	(&label("fast_dec_in_place"));	# in-place processing...
+
+	&mov	($_tmp,$key);
+
+	&align	(4);
+	&set_label("fast_dec_loop",16);
+		&mov	($s0,&DWP(0,$acc));	# read input
+		&mov	($s1,&DWP(4,$acc));
+		&mov	($s2,&DWP(8,$acc));
+		&mov	($s3,&DWP(12,$acc));
+
+		&mov	($key,$_key);		# load key
+		&call	("_x86_AES_decrypt");
+
+		&mov	($key,$_tmp);		# load ivp
+		&mov	($acc,$_len);		# load len
+		&xor	($s0,&DWP(0,$key));	# xor iv
+		&xor	($s1,&DWP(4,$key));
+		&xor	($s2,&DWP(8,$key));
+		&xor	($s3,&DWP(12,$key));
+
+		&mov	($key,$_out);		# load out
+		&mov	($acc,$_inp);		# load inp
+
+		&mov	(&DWP(0,$key),$s0);	# write output
+		&mov	(&DWP(4,$key),$s1);
+		&mov	(&DWP(8,$key),$s2);
+		&mov	(&DWP(12,$key),$s3);
+
+		&mov	($s2,$_len);		# load len
+		&mov	($_tmp,$acc);		# save ivp
+		&lea	($acc,&DWP(16,$acc));	# advance inp
+		&mov	($_inp,$acc);		# save inp
+		&lea	($key,&DWP(16,$key));	# advance out
+		&mov	($_out,$key);		# save out
+		&sub	($s2,16);		# decrease len
+		&mov	($_len,$s2);		# save len
+	&jnz	(&label("fast_dec_loop"));
+	&mov	($key,$_tmp);		# load temp ivp
+	&mov	($acc,$_ivp);		# load user ivp
+	&mov	($s0,&DWP(0,$key));	# load iv
+	&mov	($s1,&DWP(4,$key));
+	&mov	($s2,&DWP(8,$key));
+	&mov	($s3,&DWP(12,$key));
+	&mov	(&DWP(0,$acc),$s0);	# copy back to user
+	&mov	(&DWP(4,$acc),$s1);
+	&mov	(&DWP(8,$acc),$s2);
+	&mov	(&DWP(12,$acc),$s3);
+	&jmp	(&label("fast_dec_out"));
+
+    &set_label("fast_dec_in_place",16);
+	&set_label("fast_dec_in_place_loop");
+		&mov	($s0,&DWP(0,$acc));	# read input
+		&mov	($s1,&DWP(4,$acc));
+		&mov	($s2,&DWP(8,$acc));
+		&mov	($s3,&DWP(12,$acc));
+
+		&lea	($key,$ivec);
+		&mov	(&DWP(0,$key),$s0);	# copy to temp
+		&mov	(&DWP(4,$key),$s1);
+		&mov	(&DWP(8,$key),$s2);
+		&mov	(&DWP(12,$key),$s3);
+
+		&mov	($key,$_key);		# load key
+		&call	("_x86_AES_decrypt");
+
+		&mov	($key,$_ivp);		# load ivp
+		&mov	($acc,$_out);		# load out
+		&xor	($s0,&DWP(0,$key));	# xor iv
+		&xor	($s1,&DWP(4,$key));
+		&xor	($s2,&DWP(8,$key));
+		&xor	($s3,&DWP(12,$key));
+
+		&mov	(&DWP(0,$acc),$s0);	# write output
+		&mov	(&DWP(4,$acc),$s1);
+		&mov	(&DWP(8,$acc),$s2);
+		&mov	(&DWP(12,$acc),$s3);
+
+		&lea	($acc,&DWP(16,$acc));	# advance out
+		&mov	($_out,$acc);		# save out
+
+		&lea	($acc,$ivec);
+		&mov	($s0,&DWP(0,$acc));	# read temp
+		&mov	($s1,&DWP(4,$acc));
+		&mov	($s2,&DWP(8,$acc));
+		&mov	($s3,&DWP(12,$acc));
+
+		&mov	(&DWP(0,$key),$s0);	# copy iv
+		&mov	(&DWP(4,$key),$s1);
+		&mov	(&DWP(8,$key),$s2);
+		&mov	(&DWP(12,$key),$s3);
+
+		&mov	($acc,$_inp);		# load inp
+		&mov	($s2,$_len);		# load len
+		&lea	($acc,&DWP(16,$acc));	# advance inp
+		&mov	($_inp,$acc);		# save inp
+		&sub	($s2,16);		# decrease len
+		&mov	($_len,$s2);		# save len
+	&jnz	(&label("fast_dec_in_place_loop"));
+
+    &set_label("fast_dec_out",4);
+	&cmp	($mark,0);		# was the key schedule copied?
+	&mov	("edi",$_key);
+	&je	(&label("skip_dzero"));
+	# zero copy of key schedule
+	&mov	("ecx",240/4);
+	&xor	("eax","eax");
+	&align	(4);
+	&data_word(0xABF3F689);		# rep stosd
+	&set_label("skip_dzero");
+	&mov	("esp",$_esp);
+	&popf	();
+	&function_end_A();
+	&pushf	();			# kludge, never executed
+
+#--------------------------- SLOW ROUTINE ---------------------------#
+&set_label("slow_way",16);
+
+	&mov	($s0,&DWP(0,$s0)) if (!$x86only);# load OPENSSL_ia32cap
+	&mov	($key,&wparam(3));	# load key
+
+	# pre-allocate aligned stack frame...
+	&lea	($acc,&DWP(-80,"esp"));
+	&and	($acc,-64);
+
+	# ... and make sure it doesn't alias with $key modulo 1024
+	&lea	($s1,&DWP(-80-63,$key));
+	&sub	($s1,$acc);
+	&neg	($s1);
+	&and	($s1,0x3C0);	# modulo 1024, but aligned to cache-line
+	&sub	($acc,$s1);
+
+	# pick S-box copy which can't overlap with stack frame or $key
+	&lea	($s1,&DWP(768,$acc));
+	&sub	($s1,$tbl);
+	&and	($s1,0x300);
+	&lea	($tbl,&DWP(2048+128,$tbl,$s1));
+
+	&lea	($s3,&wparam(0));	# pointer to parameter block
+
+	&exch	("esp",$acc);
+	&add	("esp",4);		# reserve for return address!
+	&mov	($_tbl,$tbl);		# save %ebp
+	&mov	($_esp,$acc);		# save %esp
+	&mov	($_tmp,$s0);		# save OPENSSL_ia32cap
+
+	&mov	($s0,&DWP(0,$s3));	# load inp
+	&mov	($s1,&DWP(4,$s3));	# load out
+	#&mov	($s2,&DWP(8,$s3));	# load len
+	#&mov	($key,&DWP(12,$s3));	# load key
+	&mov	($acc,&DWP(16,$s3));	# load ivp
+	&mov	($s3,&DWP(20,$s3));	# load enc flag
+
+	&mov	($_inp,$s0);		# save copy of inp
+	&mov	($_out,$s1);		# save copy of out
+	&mov	($_len,$s2);		# save copy of len
+	&mov	($_key,$key);		# save copy of key
+	&mov	($_ivp,$acc);		# save copy of ivp
+
+	&mov	($key,$acc);
+	&mov	($acc,$s0);
+
+	&cmp	($s3,0);
+	&je	(&label("slow_decrypt"));
+
+#--------------------------- SLOW ENCRYPT ---------------------------#
+	&cmp	($s2,16);
+	&mov	($s3,$s1);
+	&jb	(&label("slow_enc_tail"));
+
+					if (!$x86only) {
+	&bt	($_tmp,25);		# check for SSE bit
+	&jnc	(&label("slow_enc_x86"));
+
+	&movq	("mm0",&QWP(0,$key));	# load iv
+	&movq	("mm4",&QWP(8,$key));
+
+	&set_label("slow_enc_loop_sse",16);
+		&pxor	("mm0",&QWP(0,$acc));	# xor input data
+		&pxor	("mm4",&QWP(8,$acc));
+
+		&mov	($key,$_key);
+		&call	("_sse_AES_encrypt_compact");
+
+		&mov	($acc,$_inp);		# load inp
+		&mov	($key,$_out);		# load out
+		&mov	($s2,$_len);		# load len
+
+		&movq	(&QWP(0,$key),"mm0");	# save output data
+		&movq	(&QWP(8,$key),"mm4");
+
+		&lea	($acc,&DWP(16,$acc));	# advance inp
+		&mov	($_inp,$acc);		# save inp
+		&lea	($s3,&DWP(16,$key));	# advance out
+		&mov	($_out,$s3);		# save out
+		&sub	($s2,16);		# decrease len
+		&cmp	($s2,16);
+		&mov	($_len,$s2);		# save len
+	&jae	(&label("slow_enc_loop_sse"));
+	&test	($s2,15);
+	&jnz	(&label("slow_enc_tail"));
+	&mov	($acc,$_ivp);		# load ivp
+	&movq	(&QWP(0,$acc),"mm0");	# save ivec
+	&movq	(&QWP(8,$acc),"mm4");
+	&emms	();
+	&mov	("esp",$_esp);
+	&popf	();
+	&function_end_A();
+	&pushf	();			# kludge, never executed
+					}
+    &set_label("slow_enc_x86",16);
+	&mov	($s0,&DWP(0,$key));	# load iv
+	&mov	($s1,&DWP(4,$key));
+
+	&set_label("slow_enc_loop_x86",4);
+		&mov	($s2,&DWP(8,$key));
+		&mov	($s3,&DWP(12,$key));
+
+		&xor	($s0,&DWP(0,$acc));	# xor input data
+		&xor	($s1,&DWP(4,$acc));
+		&xor	($s2,&DWP(8,$acc));
+		&xor	($s3,&DWP(12,$acc));
+
+		&mov	($key,$_key);		# load key
+		&call	("_x86_AES_encrypt_compact");
+
+		&mov	($acc,$_inp);		# load inp
+		&mov	($key,$_out);		# load out
+
+		&mov	(&DWP(0,$key),$s0);	# save output data
+		&mov	(&DWP(4,$key),$s1);
+		&mov	(&DWP(8,$key),$s2);
+		&mov	(&DWP(12,$key),$s3);
+
+		&mov	($s2,$_len);		# load len
+		&lea	($acc,&DWP(16,$acc));	# advance inp
+		&mov	($_inp,$acc);		# save inp
+		&lea	($s3,&DWP(16,$key));	# advance out
+		&mov	($_out,$s3);		# save out
+		&sub	($s2,16);		# decrease len
+		&cmp	($s2,16);
+		&mov	($_len,$s2);		# save len
+	&jae	(&label("slow_enc_loop_x86"));
+	&test	($s2,15);
+	&jnz	(&label("slow_enc_tail"));
+	&mov	($acc,$_ivp);		# load ivp
+	&mov	($s2,&DWP(8,$key));	# restore last dwords
+	&mov	($s3,&DWP(12,$key));
+	&mov	(&DWP(0,$acc),$s0);	# save ivec
+	&mov	(&DWP(4,$acc),$s1);
+	&mov	(&DWP(8,$acc),$s2);
+	&mov	(&DWP(12,$acc),$s3);
+
+	&mov	("esp",$_esp);
+	&popf	();
+	&function_end_A();
+	&pushf	();			# kludge, never executed
+
+    &set_label("slow_enc_tail",16);
+	&emms	()	if (!$x86only);
+	&mov	($key eq "edi"? $key:"",$s3);	# load out to edi
+	&mov	($s1,16);
+	&sub	($s1,$s2);
+	&cmp	($key,$acc eq "esi"? $acc:"");	# compare with inp
+	&je	(&label("enc_in_place"));
+	&align	(4);
+	&data_word(0xA4F3F689);	# rep movsb	# copy input
+	&jmp	(&label("enc_skip_in_place"));
+    &set_label("enc_in_place");
+	&lea	($key,&DWP(0,$key,$s2));
+    &set_label("enc_skip_in_place");
+	&mov	($s2,$s1);
+	&xor	($s0,$s0);
+	&align	(4);
+	&data_word(0xAAF3F689);	# rep stosb	# zero tail
+
+	&mov	($key,$_ivp);			# restore ivp
+	&mov	($acc,$s3);			# output as input
+	&mov	($s0,&DWP(0,$key));
+	&mov	($s1,&DWP(4,$key));
+	&mov	($_len,16);			# len=16
+	&jmp	(&label("slow_enc_loop_x86"));	# one more spin...
+
+#--------------------------- SLOW DECRYPT ---------------------------#
+&set_label("slow_decrypt",16);
+					if (!$x86only) {
+	&bt	($_tmp,25);		# check for SSE bit
+	&jnc	(&label("slow_dec_loop_x86"));
+
+	&set_label("slow_dec_loop_sse",4);
+		&movq	("mm0",&QWP(0,$acc));	# read input
+		&movq	("mm4",&QWP(8,$acc));
+
+		&mov	($key,$_key);
+		&call	("_sse_AES_decrypt_compact");
+
+		&mov	($acc,$_inp);		# load inp
+		&lea	($s0,$ivec);
+		&mov	($s1,$_out);		# load out
+		&mov	($s2,$_len);		# load len
+		&mov	($key,$_ivp);		# load ivp
+
+		&movq	("mm1",&QWP(0,$acc));	# re-read input
+		&movq	("mm5",&QWP(8,$acc));
+
+		&pxor	("mm0",&QWP(0,$key));	# xor iv
+		&pxor	("mm4",&QWP(8,$key));
+
+		&movq	(&QWP(0,$key),"mm1");	# copy input to iv
+		&movq	(&QWP(8,$key),"mm5");
+
+		&sub	($s2,16);		# decrease len
+		&jc	(&label("slow_dec_partial_sse"));
+
+		&movq	(&QWP(0,$s1),"mm0");	# write output
+		&movq	(&QWP(8,$s1),"mm4");
+
+		&lea	($s1,&DWP(16,$s1));	# advance out
+		&mov	($_out,$s1);		# save out
+		&lea	($acc,&DWP(16,$acc));	# advance inp
+		&mov	($_inp,$acc);		# save inp
+		&mov	($_len,$s2);		# save len
+	&jnz	(&label("slow_dec_loop_sse"));
+	&emms	();
+	&mov	("esp",$_esp);
+	&popf	();
+	&function_end_A();
+	&pushf	();			# kludge, never executed
+
+    &set_label("slow_dec_partial_sse",16);
+	&movq	(&QWP(0,$s0),"mm0");	# save output to temp
+	&movq	(&QWP(8,$s0),"mm4");
+	&emms	();
+
+	&add	($s2 eq "ecx" ? "ecx":"",16);
+	&mov	("edi",$s1);		# out
+	&mov	("esi",$s0);		# temp
+	&align	(4);
+	&data_word(0xA4F3F689);		# rep movsb # copy partial output
+
+	&mov	("esp",$_esp);
+	&popf	();
+	&function_end_A();
+	&pushf	();			# kludge, never executed
+					}
+	&set_label("slow_dec_loop_x86",16);
+		&mov	($s0,&DWP(0,$acc));	# read input
+		&mov	($s1,&DWP(4,$acc));
+		&mov	($s2,&DWP(8,$acc));
+		&mov	($s3,&DWP(12,$acc));
+
+		&lea	($key,$ivec);
+		&mov	(&DWP(0,$key),$s0);	# copy to temp
+		&mov	(&DWP(4,$key),$s1);
+		&mov	(&DWP(8,$key),$s2);
+		&mov	(&DWP(12,$key),$s3);
+
+		&mov	($key,$_key);		# load key
+		&call	("_x86_AES_decrypt_compact");
+
+		&mov	($key,$_ivp);		# load ivp
+		&mov	($acc,$_len);		# load len
+		&xor	($s0,&DWP(0,$key));	# xor iv
+		&xor	($s1,&DWP(4,$key));
+		&xor	($s2,&DWP(8,$key));
+		&xor	($s3,&DWP(12,$key));
+
+		&sub	($acc,16);
+		&jc	(&label("slow_dec_partial_x86"));
+
+		&mov	($_len,$acc);		# save len
+		&mov	($acc,$_out);		# load out
+
+		&mov	(&DWP(0,$acc),$s0);	# write output
+		&mov	(&DWP(4,$acc),$s1);
+		&mov	(&DWP(8,$acc),$s2);
+		&mov	(&DWP(12,$acc),$s3);
+
+		&lea	($acc,&DWP(16,$acc));	# advance out
+		&mov	($_out,$acc);		# save out
+
+		&lea	($acc,$ivec);
+		&mov	($s0,&DWP(0,$acc));	# read temp
+		&mov	($s1,&DWP(4,$acc));
+		&mov	($s2,&DWP(8,$acc));
+		&mov	($s3,&DWP(12,$acc));
+
+		&mov	(&DWP(0,$key),$s0);	# copy it to iv
+		&mov	(&DWP(4,$key),$s1);
+		&mov	(&DWP(8,$key),$s2);
+		&mov	(&DWP(12,$key),$s3);
+
+		&mov	($acc,$_inp);		# load inp
+		&lea	($acc,&DWP(16,$acc));	# advance inp
+		&mov	($_inp,$acc);		# save inp
+	&jnz	(&label("slow_dec_loop_x86"));
+	&mov	("esp",$_esp);
+	&popf	();
+	&function_end_A();
+	&pushf	();			# kludge, never executed
+
+    &set_label("slow_dec_partial_x86",16);
+	&lea	($acc,$ivec);
+	&mov	(&DWP(0,$acc),$s0);	# save output to temp
+	&mov	(&DWP(4,$acc),$s1);
+	&mov	(&DWP(8,$acc),$s2);
+	&mov	(&DWP(12,$acc),$s3);
+
+	&mov	($acc,$_inp);
+	&mov	($s0,&DWP(0,$acc));	# re-read input
+	&mov	($s1,&DWP(4,$acc));
+	&mov	($s2,&DWP(8,$acc));
+	&mov	($s3,&DWP(12,$acc));
+
+	&mov	(&DWP(0,$key),$s0);	# copy it to iv
+	&mov	(&DWP(4,$key),$s1);
+	&mov	(&DWP(8,$key),$s2);
+	&mov	(&DWP(12,$key),$s3);
+
+	&mov	("ecx",$_len);
+	&mov	("edi",$_out);
+	&lea	("esi",$ivec);
+	&align	(4);
+	&data_word(0xA4F3F689);		# rep movsb # copy partial output
+
+	&mov	("esp",$_esp);
+	&popf	();
+&function_end("AES_cbc_encrypt");
+}
+
+#------------------------------------------------------------------#
+
+sub enckey()
+{
+	&movz	("esi",&LB("edx"));		# rk[i]>>0
+	&movz	("ebx",&BP(-128,$tbl,"esi",1));
+	&movz	("esi",&HB("edx"));		# rk[i]>>8
+	&shl	("ebx",24);
+	&xor	("eax","ebx");
+
+	&movz	("ebx",&BP(-128,$tbl,"esi",1));
+	&shr	("edx",16);
+	&movz	("esi",&LB("edx"));		# rk[i]>>16
+	&xor	("eax","ebx");
+
+	&movz	("ebx",&BP(-128,$tbl,"esi",1));
+	&movz	("esi",&HB("edx"));		# rk[i]>>24
+	&shl	("ebx",8);
+	&xor	("eax","ebx");
+
+	&movz	("ebx",&BP(-128,$tbl,"esi",1));
+	&shl	("ebx",16);
+	&xor	("eax","ebx");
+
+	&xor	("eax",&DWP(1024-128,$tbl,"ecx",4));	# rcon
+}
+
+&function_begin("_x86_AES_set_encrypt_key");
+	&mov	("esi",&wparam(1));		# user supplied key
+	&mov	("edi",&wparam(3));		# private key schedule
+
+	&test	("esi",-1);
+	&jz	(&label("badpointer"));
+	&test	("edi",-1);
+	&jz	(&label("badpointer"));
+
+	&call	(&label("pic_point"));
+	&set_label("pic_point");
+	&blindpop($tbl);
+	&lea	($tbl,&DWP(&label("AES_Te")."-".&label("pic_point"),$tbl));
+	&lea	($tbl,&DWP(2048+128,$tbl));
+
+	# prefetch Te4
+	&mov	("eax",&DWP(0-128,$tbl));
+	&mov	("ebx",&DWP(32-128,$tbl));
+	&mov	("ecx",&DWP(64-128,$tbl));
+	&mov	("edx",&DWP(96-128,$tbl));
+	&mov	("eax",&DWP(128-128,$tbl));
+	&mov	("ebx",&DWP(160-128,$tbl));
+	&mov	("ecx",&DWP(192-128,$tbl));
+	&mov	("edx",&DWP(224-128,$tbl));
+
+	&mov	("ecx",&wparam(2));		# number of bits in key
+	&cmp	("ecx",128);
+	&je	(&label("10rounds"));
+	&cmp	("ecx",192);
+	&je	(&label("12rounds"));
+	&cmp	("ecx",256);
+	&je	(&label("14rounds"));
+	&mov	("eax",-2);			# invalid number of bits
+	&jmp	(&label("exit"));
+
+    &set_label("10rounds");
+	&mov	("eax",&DWP(0,"esi"));		# copy first 4 dwords
+	&mov	("ebx",&DWP(4,"esi"));
+	&mov	("ecx",&DWP(8,"esi"));
+	&mov	("edx",&DWP(12,"esi"));
+	&mov	(&DWP(0,"edi"),"eax");
+	&mov	(&DWP(4,"edi"),"ebx");
+	&mov	(&DWP(8,"edi"),"ecx");
+	&mov	(&DWP(12,"edi"),"edx");
+
+	&xor	("ecx","ecx");
+	&jmp	(&label("10shortcut"));
+
+	&align	(4);
+	&set_label("10loop");
+		&mov	("eax",&DWP(0,"edi"));		# rk[0]
+		&mov	("edx",&DWP(12,"edi"));		# rk[3]
+	&set_label("10shortcut");
+		&enckey	();
+
+		&mov	(&DWP(16,"edi"),"eax");		# rk[4]
+		&xor	("eax",&DWP(4,"edi"));
+		&mov	(&DWP(20,"edi"),"eax");		# rk[5]
+		&xor	("eax",&DWP(8,"edi"));
+		&mov	(&DWP(24,"edi"),"eax");		# rk[6]
+		&xor	("eax",&DWP(12,"edi"));
+		&mov	(&DWP(28,"edi"),"eax");		# rk[7]
+		&inc	("ecx");
+		&add	("edi",16);
+		&cmp	("ecx",10);
+	&jl	(&label("10loop"));
+
+	&mov	(&DWP(80,"edi"),10);		# setup number of rounds
+	&xor	("eax","eax");
+	&jmp	(&label("exit"));
+		
+    &set_label("12rounds");
+	&mov	("eax",&DWP(0,"esi"));		# copy first 6 dwords
+	&mov	("ebx",&DWP(4,"esi"));
+	&mov	("ecx",&DWP(8,"esi"));
+	&mov	("edx",&DWP(12,"esi"));
+	&mov	(&DWP(0,"edi"),"eax");
+	&mov	(&DWP(4,"edi"),"ebx");
+	&mov	(&DWP(8,"edi"),"ecx");
+	&mov	(&DWP(12,"edi"),"edx");
+	&mov	("ecx",&DWP(16,"esi"));
+	&mov	("edx",&DWP(20,"esi"));
+	&mov	(&DWP(16,"edi"),"ecx");
+	&mov	(&DWP(20,"edi"),"edx");
+
+	&xor	("ecx","ecx");
+	&jmp	(&label("12shortcut"));
+
+	&align	(4);
+	&set_label("12loop");
+		&mov	("eax",&DWP(0,"edi"));		# rk[0]
+		&mov	("edx",&DWP(20,"edi"));		# rk[5]
+	&set_label("12shortcut");
+		&enckey	();
+
+		&mov	(&DWP(24,"edi"),"eax");		# rk[6]
+		&xor	("eax",&DWP(4,"edi"));
+		&mov	(&DWP(28,"edi"),"eax");		# rk[7]
+		&xor	("eax",&DWP(8,"edi"));
+		&mov	(&DWP(32,"edi"),"eax");		# rk[8]
+		&xor	("eax",&DWP(12,"edi"));
+		&mov	(&DWP(36,"edi"),"eax");		# rk[9]
+
+		&cmp	("ecx",7);
+		&je	(&label("12break"));
+		&inc	("ecx");
+
+		&xor	("eax",&DWP(16,"edi"));
+		&mov	(&DWP(40,"edi"),"eax");		# rk[10]
+		&xor	("eax",&DWP(20,"edi"));
+		&mov	(&DWP(44,"edi"),"eax");		# rk[11]
+
+		&add	("edi",24);
+	&jmp	(&label("12loop"));
+
+	&set_label("12break");
+	&mov	(&DWP(72,"edi"),12);		# setup number of rounds
+	&xor	("eax","eax");
+	&jmp	(&label("exit"));
+
+    &set_label("14rounds");
+	&mov	("eax",&DWP(0,"esi"));		# copy first 8 dwords
+	&mov	("ebx",&DWP(4,"esi"));
+	&mov	("ecx",&DWP(8,"esi"));
+	&mov	("edx",&DWP(12,"esi"));
+	&mov	(&DWP(0,"edi"),"eax");
+	&mov	(&DWP(4,"edi"),"ebx");
+	&mov	(&DWP(8,"edi"),"ecx");
+	&mov	(&DWP(12,"edi"),"edx");
+	&mov	("eax",&DWP(16,"esi"));
+	&mov	("ebx",&DWP(20,"esi"));
+	&mov	("ecx",&DWP(24,"esi"));
+	&mov	("edx",&DWP(28,"esi"));
+	&mov	(&DWP(16,"edi"),"eax");
+	&mov	(&DWP(20,"edi"),"ebx");
+	&mov	(&DWP(24,"edi"),"ecx");
+	&mov	(&DWP(28,"edi"),"edx");
+
+	&xor	("ecx","ecx");
+	&jmp	(&label("14shortcut"));
+
+	&align	(4);
+	&set_label("14loop");
+		&mov	("edx",&DWP(28,"edi"));		# rk[7]
+	&set_label("14shortcut");
+		&mov	("eax",&DWP(0,"edi"));		# rk[0]
+
+		&enckey	();
+
+		&mov	(&DWP(32,"edi"),"eax");		# rk[8]
+		&xor	("eax",&DWP(4,"edi"));
+		&mov	(&DWP(36,"edi"),"eax");		# rk[9]
+		&xor	("eax",&DWP(8,"edi"));
+		&mov	(&DWP(40,"edi"),"eax");		# rk[10]
+		&xor	("eax",&DWP(12,"edi"));
+		&mov	(&DWP(44,"edi"),"eax");		# rk[11]
+
+		&cmp	("ecx",6);
+		&je	(&label("14break"));
+		&inc	("ecx");
+
+		&mov	("edx","eax");
+		&mov	("eax",&DWP(16,"edi"));		# rk[4]
+		&movz	("esi",&LB("edx"));		# rk[11]>>0
+		&movz	("ebx",&BP(-128,$tbl,"esi",1));
+		&movz	("esi",&HB("edx"));		# rk[11]>>8
+		&xor	("eax","ebx");
+
+		&movz	("ebx",&BP(-128,$tbl,"esi",1));
+		&shr	("edx",16);
+		&shl	("ebx",8);
+		&movz	("esi",&LB("edx"));		# rk[11]>>16
+		&xor	("eax","ebx");
+
+		&movz	("ebx",&BP(-128,$tbl,"esi",1));
+		&movz	("esi",&HB("edx"));		# rk[11]>>24
+		&shl	("ebx",16);
+		&xor	("eax","ebx");
+
+		&movz	("ebx",&BP(-128,$tbl,"esi",1));
+		&shl	("ebx",24);
+		&xor	("eax","ebx");
+
+		&mov	(&DWP(48,"edi"),"eax");		# rk[12]
+		&xor	("eax",&DWP(20,"edi"));
+		&mov	(&DWP(52,"edi"),"eax");		# rk[13]
+		&xor	("eax",&DWP(24,"edi"));
+		&mov	(&DWP(56,"edi"),"eax");		# rk[14]
+		&xor	("eax",&DWP(28,"edi"));
+		&mov	(&DWP(60,"edi"),"eax");		# rk[15]
+
+		&add	("edi",32);
+	&jmp	(&label("14loop"));
+
+	&set_label("14break");
+	&mov	(&DWP(48,"edi"),14);		# setup number of rounds
+	&xor	("eax","eax");
+	&jmp	(&label("exit"));
+
+    &set_label("badpointer");
+	&mov	("eax",-1);
+    &set_label("exit");
+&function_end("_x86_AES_set_encrypt_key");
+
+# int AES_set_encrypt_key(const unsigned char *userKey, const int bits,
+#                        AES_KEY *key)
+&function_begin_B("AES_set_encrypt_key");
+	&call	("_x86_AES_set_encrypt_key");
+	&ret	();
+&function_end_B("AES_set_encrypt_key");
+
+sub deckey()
+{ my ($i,$key,$tp1,$tp2,$tp4,$tp8) = @_;
+  my $tmp = $tbl;
+
+	&mov	($tmp,0x80808080);
+	&and	($tmp,$tp1);
+	&lea	($tp2,&DWP(0,$tp1,$tp1));
+	&mov	($acc,$tmp);
+	&shr	($tmp,7);
+	&sub	($acc,$tmp);
+	&and	($tp2,0xfefefefe);
+	&and	($acc,0x1b1b1b1b);
+	&xor	($tp2,$acc);
+	&mov	($tmp,0x80808080);
+
+	&and	($tmp,$tp2);
+	&lea	($tp4,&DWP(0,$tp2,$tp2));
+	&mov	($acc,$tmp);
+	&shr	($tmp,7);
+	&sub	($acc,$tmp);
+	&and	($tp4,0xfefefefe);
+	&and	($acc,0x1b1b1b1b);
+	 &xor	($tp2,$tp1);	# tp2^tp1
+	&xor	($tp4,$acc);
+	&mov	($tmp,0x80808080);
+
+	&and	($tmp,$tp4);
+	&lea	($tp8,&DWP(0,$tp4,$tp4));
+	&mov	($acc,$tmp);
+	&shr	($tmp,7);
+	 &xor	($tp4,$tp1);	# tp4^tp1
+	&sub	($acc,$tmp);
+	&and	($tp8,0xfefefefe);
+	&and	($acc,0x1b1b1b1b);
+	 &rotl	($tp1,8);	# = ROTATE(tp1,8)
+	&xor	($tp8,$acc);
+
+	&mov	($tmp,&DWP(4*($i+1),$key));	# modulo-scheduled load
+
+	&xor	($tp1,$tp2);
+	&xor	($tp2,$tp8);
+	&xor	($tp1,$tp4);
+	&rotl	($tp2,24);
+	&xor	($tp4,$tp8);
+	&xor	($tp1,$tp8);	# ^= tp8^(tp4^tp1)^(tp2^tp1)
+	&rotl	($tp4,16);
+	&xor	($tp1,$tp2);	# ^= ROTATE(tp8^tp2^tp1,24)
+	&rotl	($tp8,8);
+	&xor	($tp1,$tp4);	# ^= ROTATE(tp8^tp4^tp1,16)
+	&mov	($tp2,$tmp);
+	&xor	($tp1,$tp8);	# ^= ROTATE(tp8,8)
+
+	&mov	(&DWP(4*$i,$key),$tp1);
+}
+
+# int AES_set_decrypt_key(const unsigned char *userKey, const int bits,
+#                        AES_KEY *key)
+&function_begin_B("AES_set_decrypt_key");
+	&call	("_x86_AES_set_encrypt_key");
+	&cmp	("eax",0);
+	&je	(&label("proceed"));
+	&ret	();
+
+    &set_label("proceed");
+	&push	("ebp");
+	&push	("ebx");
+	&push	("esi");
+	&push	("edi");
+
+	&mov	("esi",&wparam(2));
+	&mov	("ecx",&DWP(240,"esi"));	# pull number of rounds
+	&lea	("ecx",&DWP(0,"","ecx",4));
+	&lea	("edi",&DWP(0,"esi","ecx",4));	# pointer to last chunk
+
+	&set_label("invert",4);			# invert order of chunks
+		&mov	("eax",&DWP(0,"esi"));
+		&mov	("ebx",&DWP(4,"esi"));
+		&mov	("ecx",&DWP(0,"edi"));
+		&mov	("edx",&DWP(4,"edi"));
+		&mov	(&DWP(0,"edi"),"eax");
+		&mov	(&DWP(4,"edi"),"ebx");
+		&mov	(&DWP(0,"esi"),"ecx");
+		&mov	(&DWP(4,"esi"),"edx");
+		&mov	("eax",&DWP(8,"esi"));
+		&mov	("ebx",&DWP(12,"esi"));
+		&mov	("ecx",&DWP(8,"edi"));
+		&mov	("edx",&DWP(12,"edi"));
+		&mov	(&DWP(8,"edi"),"eax");
+		&mov	(&DWP(12,"edi"),"ebx");
+		&mov	(&DWP(8,"esi"),"ecx");
+		&mov	(&DWP(12,"esi"),"edx");
+		&add	("esi",16);
+		&sub	("edi",16);
+		&cmp	("esi","edi");
+	&jne	(&label("invert"));
+
+	&mov	($key,&wparam(2));
+	&mov	($acc,&DWP(240,$key));		# pull number of rounds
+	&lea	($acc,&DWP(-2,$acc,$acc));
+	&lea	($acc,&DWP(0,$key,$acc,8));
+	&mov	(&wparam(2),$acc);
+
+	&mov	($s0,&DWP(16,$key));		# modulo-scheduled load
+	&set_label("permute",4);		# permute the key schedule
+		&add	($key,16);
+		&deckey	(0,$key,$s0,$s1,$s2,$s3);
+		&deckey	(1,$key,$s1,$s2,$s3,$s0);
+		&deckey	(2,$key,$s2,$s3,$s0,$s1);
+		&deckey	(3,$key,$s3,$s0,$s1,$s2);
+		&cmp	($key,&wparam(2));
+	&jb	(&label("permute"));
+
+	&xor	("eax","eax");			# return success
+&function_end("AES_set_decrypt_key");
+&asciz("AES for x86, CRYPTOGAMS by ");
+
+&asm_finish();
+
+close STDOUT;
diff --git a/crypto/aes/asm/aes-armv4.pl b/crypto/aes/asm/aes-armv4.pl
new file mode 100644
index 000000000..16d79aae5
--- /dev/null
+++ b/crypto/aes/asm/aes-armv4.pl
@@ -0,0 +1,1245 @@
+#! /usr/bin/env perl
+# Copyright 2007-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+
+# ====================================================================
+# Written by Andy Polyakov  for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+
+# AES for ARMv4
+
+# January 2007.
+#
+# Code uses single 1K S-box and is >2 times faster than code generated
+# by gcc-3.4.1. This is thanks to unique feature of ARMv4 ISA, which
+# allows to merge logical or arithmetic operation with shift or rotate
+# in one instruction and emit combined result every cycle. The module
+# is endian-neutral. The performance is ~42 cycles/byte for 128-bit
+# key [on single-issue Xscale PXA250 core].
+
+# May 2007.
+#
+# AES_set_[en|de]crypt_key is added.
+
+# July 2010.
+#
+# Rescheduling for dual-issue pipeline resulted in 12% improvement on
+# Cortex A8 core and ~25 cycles per byte processed with 128-bit key.
+
+# February 2011.
+#
+# Profiler-assisted and platform-specific optimization resulted in 16%
+# improvement on Cortex A8 core and ~21.5 cycles per byte.
+
+$flavour = shift;
+if ($flavour=~/\w[\w\-]*\.\w+$/) { $output=$flavour; undef $flavour; }
+else { while (($output=shift) && ($output!~/\w[\w\-]*\.\w+$/)) {} }
+
+if ($flavour && $flavour ne "void") {
+    $0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+    ( $xlate="${dir}arm-xlate.pl" and -f $xlate ) or
+    ( $xlate="${dir}../../perlasm/arm-xlate.pl" and -f $xlate) or
+    die "can't locate arm-xlate.pl";
+
+    open STDOUT,"| \"$^X\" $xlate $flavour $output";
+} else {
+    open STDOUT,">$output";
+}
+
+$s0="r0";
+$s1="r1";
+$s2="r2";
+$s3="r3";
+$t1="r4";
+$t2="r5";
+$t3="r6";
+$i1="r7";
+$i2="r8";
+$i3="r9";
+
+$tbl="r10";
+$key="r11";
+$rounds="r12";
+
+$code=<<___;
+#ifndef __KERNEL__
+# include "arm_arch.h"
+#else
+# define __ARM_ARCH__ __LINUX_ARM_ARCH__
+#endif
+
+.text
+#if defined(__thumb2__) && !defined(__APPLE__)
+.syntax	unified
+.thumb
+#else
+.code	32
+#undef __thumb2__
+#endif
+
+.type	AES_Te,%object
+.align	5
+AES_Te:
+.word	0xc66363a5, 0xf87c7c84, 0xee777799, 0xf67b7b8d
+.word	0xfff2f20d, 0xd66b6bbd, 0xde6f6fb1, 0x91c5c554
+.word	0x60303050, 0x02010103, 0xce6767a9, 0x562b2b7d
+.word	0xe7fefe19, 0xb5d7d762, 0x4dababe6, 0xec76769a
+.word	0x8fcaca45, 0x1f82829d, 0x89c9c940, 0xfa7d7d87
+.word	0xeffafa15, 0xb25959eb, 0x8e4747c9, 0xfbf0f00b
+.word	0x41adadec, 0xb3d4d467, 0x5fa2a2fd, 0x45afafea
+.word	0x239c9cbf, 0x53a4a4f7, 0xe4727296, 0x9bc0c05b
+.word	0x75b7b7c2, 0xe1fdfd1c, 0x3d9393ae, 0x4c26266a
+.word	0x6c36365a, 0x7e3f3f41, 0xf5f7f702, 0x83cccc4f
+.word	0x6834345c, 0x51a5a5f4, 0xd1e5e534, 0xf9f1f108
+.word	0xe2717193, 0xabd8d873, 0x62313153, 0x2a15153f
+.word	0x0804040c, 0x95c7c752, 0x46232365, 0x9dc3c35e
+.word	0x30181828, 0x379696a1, 0x0a05050f, 0x2f9a9ab5
+.word	0x0e070709, 0x24121236, 0x1b80809b, 0xdfe2e23d
+.word	0xcdebeb26, 0x4e272769, 0x7fb2b2cd, 0xea75759f
+.word	0x1209091b, 0x1d83839e, 0x582c2c74, 0x341a1a2e
+.word	0x361b1b2d, 0xdc6e6eb2, 0xb45a5aee, 0x5ba0a0fb
+.word	0xa45252f6, 0x763b3b4d, 0xb7d6d661, 0x7db3b3ce
+.word	0x5229297b, 0xdde3e33e, 0x5e2f2f71, 0x13848497
+.word	0xa65353f5, 0xb9d1d168, 0x00000000, 0xc1eded2c
+.word	0x40202060, 0xe3fcfc1f, 0x79b1b1c8, 0xb65b5bed
+.word	0xd46a6abe, 0x8dcbcb46, 0x67bebed9, 0x7239394b
+.word	0x944a4ade, 0x984c4cd4, 0xb05858e8, 0x85cfcf4a
+.word	0xbbd0d06b, 0xc5efef2a, 0x4faaaae5, 0xedfbfb16
+.word	0x864343c5, 0x9a4d4dd7, 0x66333355, 0x11858594
+.word	0x8a4545cf, 0xe9f9f910, 0x04020206, 0xfe7f7f81
+.word	0xa05050f0, 0x783c3c44, 0x259f9fba, 0x4ba8a8e3
+.word	0xa25151f3, 0x5da3a3fe, 0x804040c0, 0x058f8f8a
+.word	0x3f9292ad, 0x219d9dbc, 0x70383848, 0xf1f5f504
+.word	0x63bcbcdf, 0x77b6b6c1, 0xafdada75, 0x42212163
+.word	0x20101030, 0xe5ffff1a, 0xfdf3f30e, 0xbfd2d26d
+.word	0x81cdcd4c, 0x180c0c14, 0x26131335, 0xc3ecec2f
+.word	0xbe5f5fe1, 0x359797a2, 0x884444cc, 0x2e171739
+.word	0x93c4c457, 0x55a7a7f2, 0xfc7e7e82, 0x7a3d3d47
+.word	0xc86464ac, 0xba5d5de7, 0x3219192b, 0xe6737395
+.word	0xc06060a0, 0x19818198, 0x9e4f4fd1, 0xa3dcdc7f
+.word	0x44222266, 0x542a2a7e, 0x3b9090ab, 0x0b888883
+.word	0x8c4646ca, 0xc7eeee29, 0x6bb8b8d3, 0x2814143c
+.word	0xa7dede79, 0xbc5e5ee2, 0x160b0b1d, 0xaddbdb76
+.word	0xdbe0e03b, 0x64323256, 0x743a3a4e, 0x140a0a1e
+.word	0x924949db, 0x0c06060a, 0x4824246c, 0xb85c5ce4
+.word	0x9fc2c25d, 0xbdd3d36e, 0x43acacef, 0xc46262a6
+.word	0x399191a8, 0x319595a4, 0xd3e4e437, 0xf279798b
+.word	0xd5e7e732, 0x8bc8c843, 0x6e373759, 0xda6d6db7
+.word	0x018d8d8c, 0xb1d5d564, 0x9c4e4ed2, 0x49a9a9e0
+.word	0xd86c6cb4, 0xac5656fa, 0xf3f4f407, 0xcfeaea25
+.word	0xca6565af, 0xf47a7a8e, 0x47aeaee9, 0x10080818
+.word	0x6fbabad5, 0xf0787888, 0x4a25256f, 0x5c2e2e72
+.word	0x381c1c24, 0x57a6a6f1, 0x73b4b4c7, 0x97c6c651
+.word	0xcbe8e823, 0xa1dddd7c, 0xe874749c, 0x3e1f1f21
+.word	0x964b4bdd, 0x61bdbddc, 0x0d8b8b86, 0x0f8a8a85
+.word	0xe0707090, 0x7c3e3e42, 0x71b5b5c4, 0xcc6666aa
+.word	0x904848d8, 0x06030305, 0xf7f6f601, 0x1c0e0e12
+.word	0xc26161a3, 0x6a35355f, 0xae5757f9, 0x69b9b9d0
+.word	0x17868691, 0x99c1c158, 0x3a1d1d27, 0x279e9eb9
+.word	0xd9e1e138, 0xebf8f813, 0x2b9898b3, 0x22111133
+.word	0xd26969bb, 0xa9d9d970, 0x078e8e89, 0x339494a7
+.word	0x2d9b9bb6, 0x3c1e1e22, 0x15878792, 0xc9e9e920
+.word	0x87cece49, 0xaa5555ff, 0x50282878, 0xa5dfdf7a
+.word	0x038c8c8f, 0x59a1a1f8, 0x09898980, 0x1a0d0d17
+.word	0x65bfbfda, 0xd7e6e631, 0x844242c6, 0xd06868b8
+.word	0x824141c3, 0x299999b0, 0x5a2d2d77, 0x1e0f0f11
+.word	0x7bb0b0cb, 0xa85454fc, 0x6dbbbbd6, 0x2c16163a
+@ Te4[256]
+.byte	0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5
+.byte	0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76
+.byte	0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0
+.byte	0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0
+.byte	0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc
+.byte	0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15
+.byte	0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a
+.byte	0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75
+.byte	0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0
+.byte	0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84
+.byte	0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b
+.byte	0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf
+.byte	0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85
+.byte	0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8
+.byte	0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5
+.byte	0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2
+.byte	0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17
+.byte	0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73
+.byte	0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88
+.byte	0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb
+.byte	0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c
+.byte	0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79
+.byte	0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9
+.byte	0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08
+.byte	0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6
+.byte	0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a
+.byte	0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e
+.byte	0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e
+.byte	0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94
+.byte	0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf
+.byte	0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68
+.byte	0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16
+@ rcon[]
+.word	0x01000000, 0x02000000, 0x04000000, 0x08000000
+.word	0x10000000, 0x20000000, 0x40000000, 0x80000000
+.word	0x1B000000, 0x36000000, 0, 0, 0, 0, 0, 0
+.size	AES_Te,.-AES_Te
+
+@ void AES_encrypt(const unsigned char *in, unsigned char *out,
+@ 		 const AES_KEY *key) {
+.global AES_encrypt
+.type   AES_encrypt,%function
+.align	5
+AES_encrypt:
+#ifndef	__thumb2__
+	sub	r3,pc,#8		@ AES_encrypt
+#else
+	adr	r3,AES_encrypt
+#endif
+	stmdb   sp!,{r1,r4-r12,lr}
+#ifdef	__APPLE__
+	adr	$tbl,AES_Te
+#else
+	sub	$tbl,r3,#AES_encrypt-AES_Te	@ Te
+#endif
+	mov	$rounds,r0		@ inp
+	mov	$key,r2
+#if __ARM_ARCH__<7
+	ldrb	$s0,[$rounds,#3]	@ load input data in endian-neutral
+	ldrb	$t1,[$rounds,#2]	@ manner...
+	ldrb	$t2,[$rounds,#1]
+	ldrb	$t3,[$rounds,#0]
+	orr	$s0,$s0,$t1,lsl#8
+	ldrb	$s1,[$rounds,#7]
+	orr	$s0,$s0,$t2,lsl#16
+	ldrb	$t1,[$rounds,#6]
+	orr	$s0,$s0,$t3,lsl#24
+	ldrb	$t2,[$rounds,#5]
+	ldrb	$t3,[$rounds,#4]
+	orr	$s1,$s1,$t1,lsl#8
+	ldrb	$s2,[$rounds,#11]
+	orr	$s1,$s1,$t2,lsl#16
+	ldrb	$t1,[$rounds,#10]
+	orr	$s1,$s1,$t3,lsl#24
+	ldrb	$t2,[$rounds,#9]
+	ldrb	$t3,[$rounds,#8]
+	orr	$s2,$s2,$t1,lsl#8
+	ldrb	$s3,[$rounds,#15]
+	orr	$s2,$s2,$t2,lsl#16
+	ldrb	$t1,[$rounds,#14]
+	orr	$s2,$s2,$t3,lsl#24
+	ldrb	$t2,[$rounds,#13]
+	ldrb	$t3,[$rounds,#12]
+	orr	$s3,$s3,$t1,lsl#8
+	orr	$s3,$s3,$t2,lsl#16
+	orr	$s3,$s3,$t3,lsl#24
+#else
+	ldr	$s0,[$rounds,#0]
+	ldr	$s1,[$rounds,#4]
+	ldr	$s2,[$rounds,#8]
+	ldr	$s3,[$rounds,#12]
+#ifdef __ARMEL__
+	rev	$s0,$s0
+	rev	$s1,$s1
+	rev	$s2,$s2
+	rev	$s3,$s3
+#endif
+#endif
+	bl	_armv4_AES_encrypt
+
+	ldr	$rounds,[sp],#4		@ pop out
+#if __ARM_ARCH__>=7
+#ifdef __ARMEL__
+	rev	$s0,$s0
+	rev	$s1,$s1
+	rev	$s2,$s2
+	rev	$s3,$s3
+#endif
+	str	$s0,[$rounds,#0]
+	str	$s1,[$rounds,#4]
+	str	$s2,[$rounds,#8]
+	str	$s3,[$rounds,#12]
+#else
+	mov	$t1,$s0,lsr#24		@ write output in endian-neutral
+	mov	$t2,$s0,lsr#16		@ manner...
+	mov	$t3,$s0,lsr#8
+	strb	$t1,[$rounds,#0]
+	strb	$t2,[$rounds,#1]
+	mov	$t1,$s1,lsr#24
+	strb	$t3,[$rounds,#2]
+	mov	$t2,$s1,lsr#16
+	strb	$s0,[$rounds,#3]
+	mov	$t3,$s1,lsr#8
+	strb	$t1,[$rounds,#4]
+	strb	$t2,[$rounds,#5]
+	mov	$t1,$s2,lsr#24
+	strb	$t3,[$rounds,#6]
+	mov	$t2,$s2,lsr#16
+	strb	$s1,[$rounds,#7]
+	mov	$t3,$s2,lsr#8
+	strb	$t1,[$rounds,#8]
+	strb	$t2,[$rounds,#9]
+	mov	$t1,$s3,lsr#24
+	strb	$t3,[$rounds,#10]
+	mov	$t2,$s3,lsr#16
+	strb	$s2,[$rounds,#11]
+	mov	$t3,$s3,lsr#8
+	strb	$t1,[$rounds,#12]
+	strb	$t2,[$rounds,#13]
+	strb	$t3,[$rounds,#14]
+	strb	$s3,[$rounds,#15]
+#endif
+#if __ARM_ARCH__>=5
+	ldmia	sp!,{r4-r12,pc}
+#else
+	ldmia   sp!,{r4-r12,lr}
+	tst	lr,#1
+	moveq	pc,lr			@ be binary compatible with V4, yet
+	bx	lr			@ interoperable with Thumb ISA:-)
+#endif
+.size	AES_encrypt,.-AES_encrypt
+
+.type   _armv4_AES_encrypt,%function
+.align	2
+_armv4_AES_encrypt:
+	str	lr,[sp,#-4]!		@ push lr
+	ldmia	$key!,{$t1-$i1}
+	eor	$s0,$s0,$t1
+	ldr	$rounds,[$key,#240-16]
+	eor	$s1,$s1,$t2
+	eor	$s2,$s2,$t3
+	eor	$s3,$s3,$i1
+	sub	$rounds,$rounds,#1
+	mov	lr,#255
+
+	and	$i1,lr,$s0
+	and	$i2,lr,$s0,lsr#8
+	and	$i3,lr,$s0,lsr#16
+	mov	$s0,$s0,lsr#24
+.Lenc_loop:
+	ldr	$t1,[$tbl,$i1,lsl#2]	@ Te3[s0>>0]
+	and	$i1,lr,$s1,lsr#16	@ i0
+	ldr	$t2,[$tbl,$i2,lsl#2]	@ Te2[s0>>8]
+	and	$i2,lr,$s1
+	ldr	$t3,[$tbl,$i3,lsl#2]	@ Te1[s0>>16]
+	and	$i3,lr,$s1,lsr#8
+	ldr	$s0,[$tbl,$s0,lsl#2]	@ Te0[s0>>24]
+	mov	$s1,$s1,lsr#24
+
+	ldr	$i1,[$tbl,$i1,lsl#2]	@ Te1[s1>>16]
+	ldr	$i2,[$tbl,$i2,lsl#2]	@ Te3[s1>>0]
+	ldr	$i3,[$tbl,$i3,lsl#2]	@ Te2[s1>>8]
+	eor	$s0,$s0,$i1,ror#8
+	ldr	$s1,[$tbl,$s1,lsl#2]	@ Te0[s1>>24]
+	and	$i1,lr,$s2,lsr#8	@ i0
+	eor	$t2,$t2,$i2,ror#8
+	and	$i2,lr,$s2,lsr#16	@ i1
+	eor	$t3,$t3,$i3,ror#8
+	and	$i3,lr,$s2
+	ldr	$i1,[$tbl,$i1,lsl#2]	@ Te2[s2>>8]
+	eor	$s1,$s1,$t1,ror#24
+	ldr	$i2,[$tbl,$i2,lsl#2]	@ Te1[s2>>16]
+	mov	$s2,$s2,lsr#24
+
+	ldr	$i3,[$tbl,$i3,lsl#2]	@ Te3[s2>>0]
+	eor	$s0,$s0,$i1,ror#16
+	ldr	$s2,[$tbl,$s2,lsl#2]	@ Te0[s2>>24]
+	and	$i1,lr,$s3		@ i0
+	eor	$s1,$s1,$i2,ror#8
+	and	$i2,lr,$s3,lsr#8	@ i1
+	eor	$t3,$t3,$i3,ror#16
+	and	$i3,lr,$s3,lsr#16	@ i2
+	ldr	$i1,[$tbl,$i1,lsl#2]	@ Te3[s3>>0]
+	eor	$s2,$s2,$t2,ror#16
+	ldr	$i2,[$tbl,$i2,lsl#2]	@ Te2[s3>>8]
+	mov	$s3,$s3,lsr#24
+
+	ldr	$i3,[$tbl,$i3,lsl#2]	@ Te1[s3>>16]
+	eor	$s0,$s0,$i1,ror#24
+	ldr	$i1,[$key],#16
+	eor	$s1,$s1,$i2,ror#16
+	ldr	$s3,[$tbl,$s3,lsl#2]	@ Te0[s3>>24]
+	eor	$s2,$s2,$i3,ror#8
+	ldr	$t1,[$key,#-12]
+	eor	$s3,$s3,$t3,ror#8
+
+	ldr	$t2,[$key,#-8]
+	eor	$s0,$s0,$i1
+	ldr	$t3,[$key,#-4]
+	and	$i1,lr,$s0
+	eor	$s1,$s1,$t1
+	and	$i2,lr,$s0,lsr#8
+	eor	$s2,$s2,$t2
+	and	$i3,lr,$s0,lsr#16
+	eor	$s3,$s3,$t3
+	mov	$s0,$s0,lsr#24
+
+	subs	$rounds,$rounds,#1
+	bne	.Lenc_loop
+
+	add	$tbl,$tbl,#2
+
+	ldrb	$t1,[$tbl,$i1,lsl#2]	@ Te4[s0>>0]
+	and	$i1,lr,$s1,lsr#16	@ i0
+	ldrb	$t2,[$tbl,$i2,lsl#2]	@ Te4[s0>>8]
+	and	$i2,lr,$s1
+	ldrb	$t3,[$tbl,$i3,lsl#2]	@ Te4[s0>>16]
+	and	$i3,lr,$s1,lsr#8
+	ldrb	$s0,[$tbl,$s0,lsl#2]	@ Te4[s0>>24]
+	mov	$s1,$s1,lsr#24
+
+	ldrb	$i1,[$tbl,$i1,lsl#2]	@ Te4[s1>>16]
+	ldrb	$i2,[$tbl,$i2,lsl#2]	@ Te4[s1>>0]
+	ldrb	$i3,[$tbl,$i3,lsl#2]	@ Te4[s1>>8]
+	eor	$s0,$i1,$s0,lsl#8
+	ldrb	$s1,[$tbl,$s1,lsl#2]	@ Te4[s1>>24]
+	and	$i1,lr,$s2,lsr#8	@ i0
+	eor	$t2,$i2,$t2,lsl#8
+	and	$i2,lr,$s2,lsr#16	@ i1
+	eor	$t3,$i3,$t3,lsl#8
+	and	$i3,lr,$s2
+	ldrb	$i1,[$tbl,$i1,lsl#2]	@ Te4[s2>>8]
+	eor	$s1,$t1,$s1,lsl#24
+	ldrb	$i2,[$tbl,$i2,lsl#2]	@ Te4[s2>>16]
+	mov	$s2,$s2,lsr#24
+
+	ldrb	$i3,[$tbl,$i3,lsl#2]	@ Te4[s2>>0]
+	eor	$s0,$i1,$s0,lsl#8
+	ldrb	$s2,[$tbl,$s2,lsl#2]	@ Te4[s2>>24]
+	and	$i1,lr,$s3		@ i0
+	eor	$s1,$s1,$i2,lsl#16
+	and	$i2,lr,$s3,lsr#8	@ i1
+	eor	$t3,$i3,$t3,lsl#8
+	and	$i3,lr,$s3,lsr#16	@ i2
+	ldrb	$i1,[$tbl,$i1,lsl#2]	@ Te4[s3>>0]
+	eor	$s2,$t2,$s2,lsl#24
+	ldrb	$i2,[$tbl,$i2,lsl#2]	@ Te4[s3>>8]
+	mov	$s3,$s3,lsr#24
+
+	ldrb	$i3,[$tbl,$i3,lsl#2]	@ Te4[s3>>16]
+	eor	$s0,$i1,$s0,lsl#8
+	ldr	$i1,[$key,#0]
+	ldrb	$s3,[$tbl,$s3,lsl#2]	@ Te4[s3>>24]
+	eor	$s1,$s1,$i2,lsl#8
+	ldr	$t1,[$key,#4]
+	eor	$s2,$s2,$i3,lsl#16
+	ldr	$t2,[$key,#8]
+	eor	$s3,$t3,$s3,lsl#24
+	ldr	$t3,[$key,#12]
+
+	eor	$s0,$s0,$i1
+	eor	$s1,$s1,$t1
+	eor	$s2,$s2,$t2
+	eor	$s3,$s3,$t3
+
+	sub	$tbl,$tbl,#2
+	ldr	pc,[sp],#4		@ pop and return
+.size	_armv4_AES_encrypt,.-_armv4_AES_encrypt
+
+.global AES_set_encrypt_key
+.type   AES_set_encrypt_key,%function
+.align	5
+AES_set_encrypt_key:
+_armv4_AES_set_encrypt_key:
+#ifndef	__thumb2__
+	sub	r3,pc,#8		@ AES_set_encrypt_key
+#else
+	adr	r3,AES_set_encrypt_key
+#endif
+	teq	r0,#0
+#ifdef	__thumb2__
+	itt	eq			@ Thumb2 thing, sanity check in ARM
+#endif
+	moveq	r0,#-1
+	beq	.Labrt
+	teq	r2,#0
+#ifdef	__thumb2__
+	itt	eq			@ Thumb2 thing, sanity check in ARM
+#endif
+	moveq	r0,#-1
+	beq	.Labrt
+
+	teq	r1,#128
+	beq	.Lok
+	teq	r1,#192
+	beq	.Lok
+	teq	r1,#256
+#ifdef	__thumb2__
+	itt	ne			@ Thumb2 thing, sanity check in ARM
+#endif
+	movne	r0,#-1
+	bne	.Labrt
+
+.Lok:	stmdb   sp!,{r4-r12,lr}
+	mov	$rounds,r0		@ inp
+	mov	lr,r1			@ bits
+	mov	$key,r2			@ key
+
+#ifdef	__APPLE__
+	adr	$tbl,AES_Te+1024				@ Te4
+#else
+	sub	$tbl,r3,#_armv4_AES_set_encrypt_key-AES_Te-1024	@ Te4
+#endif
+
+#if __ARM_ARCH__<7
+	ldrb	$s0,[$rounds,#3]	@ load input data in endian-neutral
+	ldrb	$t1,[$rounds,#2]	@ manner...
+	ldrb	$t2,[$rounds,#1]
+	ldrb	$t3,[$rounds,#0]
+	orr	$s0,$s0,$t1,lsl#8
+	ldrb	$s1,[$rounds,#7]
+	orr	$s0,$s0,$t2,lsl#16
+	ldrb	$t1,[$rounds,#6]
+	orr	$s0,$s0,$t3,lsl#24
+	ldrb	$t2,[$rounds,#5]
+	ldrb	$t3,[$rounds,#4]
+	orr	$s1,$s1,$t1,lsl#8
+	ldrb	$s2,[$rounds,#11]
+	orr	$s1,$s1,$t2,lsl#16
+	ldrb	$t1,[$rounds,#10]
+	orr	$s1,$s1,$t3,lsl#24
+	ldrb	$t2,[$rounds,#9]
+	ldrb	$t3,[$rounds,#8]
+	orr	$s2,$s2,$t1,lsl#8
+	ldrb	$s3,[$rounds,#15]
+	orr	$s2,$s2,$t2,lsl#16
+	ldrb	$t1,[$rounds,#14]
+	orr	$s2,$s2,$t3,lsl#24
+	ldrb	$t2,[$rounds,#13]
+	ldrb	$t3,[$rounds,#12]
+	orr	$s3,$s3,$t1,lsl#8
+	str	$s0,[$key],#16
+	orr	$s3,$s3,$t2,lsl#16
+	str	$s1,[$key,#-12]
+	orr	$s3,$s3,$t3,lsl#24
+	str	$s2,[$key,#-8]
+	str	$s3,[$key,#-4]
+#else
+	ldr	$s0,[$rounds,#0]
+	ldr	$s1,[$rounds,#4]
+	ldr	$s2,[$rounds,#8]
+	ldr	$s3,[$rounds,#12]
+#ifdef __ARMEL__
+	rev	$s0,$s0
+	rev	$s1,$s1
+	rev	$s2,$s2
+	rev	$s3,$s3
+#endif
+	str	$s0,[$key],#16
+	str	$s1,[$key,#-12]
+	str	$s2,[$key,#-8]
+	str	$s3,[$key,#-4]
+#endif
+
+	teq	lr,#128
+	bne	.Lnot128
+	mov	$rounds,#10
+	str	$rounds,[$key,#240-16]
+	add	$t3,$tbl,#256			@ rcon
+	mov	lr,#255
+
+.L128_loop:
+	and	$t2,lr,$s3,lsr#24
+	and	$i1,lr,$s3,lsr#16
+	ldrb	$t2,[$tbl,$t2]
+	and	$i2,lr,$s3,lsr#8
+	ldrb	$i1,[$tbl,$i1]
+	and	$i3,lr,$s3
+	ldrb	$i2,[$tbl,$i2]
+	orr	$t2,$t2,$i1,lsl#24
+	ldrb	$i3,[$tbl,$i3]
+	orr	$t2,$t2,$i2,lsl#16
+	ldr	$t1,[$t3],#4			@ rcon[i++]
+	orr	$t2,$t2,$i3,lsl#8
+	eor	$t2,$t2,$t1
+	eor	$s0,$s0,$t2			@ rk[4]=rk[0]^...
+	eor	$s1,$s1,$s0			@ rk[5]=rk[1]^rk[4]
+	str	$s0,[$key],#16
+	eor	$s2,$s2,$s1			@ rk[6]=rk[2]^rk[5]
+	str	$s1,[$key,#-12]
+	eor	$s3,$s3,$s2			@ rk[7]=rk[3]^rk[6]
+	str	$s2,[$key,#-8]
+	subs	$rounds,$rounds,#1
+	str	$s3,[$key,#-4]
+	bne	.L128_loop
+	sub	r2,$key,#176
+	b	.Ldone
+
+.Lnot128:
+#if __ARM_ARCH__<7
+	ldrb	$i2,[$rounds,#19]
+	ldrb	$t1,[$rounds,#18]
+	ldrb	$t2,[$rounds,#17]
+	ldrb	$t3,[$rounds,#16]
+	orr	$i2,$i2,$t1,lsl#8
+	ldrb	$i3,[$rounds,#23]
+	orr	$i2,$i2,$t2,lsl#16
+	ldrb	$t1,[$rounds,#22]
+	orr	$i2,$i2,$t3,lsl#24
+	ldrb	$t2,[$rounds,#21]
+	ldrb	$t3,[$rounds,#20]
+	orr	$i3,$i3,$t1,lsl#8
+	orr	$i3,$i3,$t2,lsl#16
+	str	$i2,[$key],#8
+	orr	$i3,$i3,$t3,lsl#24
+	str	$i3,[$key,#-4]
+#else
+	ldr	$i2,[$rounds,#16]
+	ldr	$i3,[$rounds,#20]
+#ifdef __ARMEL__
+	rev	$i2,$i2
+	rev	$i3,$i3
+#endif
+	str	$i2,[$key],#8
+	str	$i3,[$key,#-4]
+#endif
+
+	teq	lr,#192
+	bne	.Lnot192
+	mov	$rounds,#12
+	str	$rounds,[$key,#240-24]
+	add	$t3,$tbl,#256			@ rcon
+	mov	lr,#255
+	mov	$rounds,#8
+
+.L192_loop:
+	and	$t2,lr,$i3,lsr#24
+	and	$i1,lr,$i3,lsr#16
+	ldrb	$t2,[$tbl,$t2]
+	and	$i2,lr,$i3,lsr#8
+	ldrb	$i1,[$tbl,$i1]
+	and	$i3,lr,$i3
+	ldrb	$i2,[$tbl,$i2]
+	orr	$t2,$t2,$i1,lsl#24
+	ldrb	$i3,[$tbl,$i3]
+	orr	$t2,$t2,$i2,lsl#16
+	ldr	$t1,[$t3],#4			@ rcon[i++]
+	orr	$t2,$t2,$i3,lsl#8
+	eor	$i3,$t2,$t1
+	eor	$s0,$s0,$i3			@ rk[6]=rk[0]^...
+	eor	$s1,$s1,$s0			@ rk[7]=rk[1]^rk[6]
+	str	$s0,[$key],#24
+	eor	$s2,$s2,$s1			@ rk[8]=rk[2]^rk[7]
+	str	$s1,[$key,#-20]
+	eor	$s3,$s3,$s2			@ rk[9]=rk[3]^rk[8]
+	str	$s2,[$key,#-16]
+	subs	$rounds,$rounds,#1
+	str	$s3,[$key,#-12]
+#ifdef	__thumb2__
+	itt	eq				@ Thumb2 thing, sanity check in ARM
+#endif
+	subeq	r2,$key,#216
+	beq	.Ldone
+
+	ldr	$i1,[$key,#-32]
+	ldr	$i2,[$key,#-28]
+	eor	$i1,$i1,$s3			@ rk[10]=rk[4]^rk[9]
+	eor	$i3,$i2,$i1			@ rk[11]=rk[5]^rk[10]
+	str	$i1,[$key,#-8]
+	str	$i3,[$key,#-4]
+	b	.L192_loop
+
+.Lnot192:
+#if __ARM_ARCH__<7
+	ldrb	$i2,[$rounds,#27]
+	ldrb	$t1,[$rounds,#26]
+	ldrb	$t2,[$rounds,#25]
+	ldrb	$t3,[$rounds,#24]
+	orr	$i2,$i2,$t1,lsl#8
+	ldrb	$i3,[$rounds,#31]
+	orr	$i2,$i2,$t2,lsl#16
+	ldrb	$t1,[$rounds,#30]
+	orr	$i2,$i2,$t3,lsl#24
+	ldrb	$t2,[$rounds,#29]
+	ldrb	$t3,[$rounds,#28]
+	orr	$i3,$i3,$t1,lsl#8
+	orr	$i3,$i3,$t2,lsl#16
+	str	$i2,[$key],#8
+	orr	$i3,$i3,$t3,lsl#24
+	str	$i3,[$key,#-4]
+#else
+	ldr	$i2,[$rounds,#24]
+	ldr	$i3,[$rounds,#28]
+#ifdef __ARMEL__
+	rev	$i2,$i2
+	rev	$i3,$i3
+#endif
+	str	$i2,[$key],#8
+	str	$i3,[$key,#-4]
+#endif
+
+	mov	$rounds,#14
+	str	$rounds,[$key,#240-32]
+	add	$t3,$tbl,#256			@ rcon
+	mov	lr,#255
+	mov	$rounds,#7
+
+.L256_loop:
+	and	$t2,lr,$i3,lsr#24
+	and	$i1,lr,$i3,lsr#16
+	ldrb	$t2,[$tbl,$t2]
+	and	$i2,lr,$i3,lsr#8
+	ldrb	$i1,[$tbl,$i1]
+	and	$i3,lr,$i3
+	ldrb	$i2,[$tbl,$i2]
+	orr	$t2,$t2,$i1,lsl#24
+	ldrb	$i3,[$tbl,$i3]
+	orr	$t2,$t2,$i2,lsl#16
+	ldr	$t1,[$t3],#4			@ rcon[i++]
+	orr	$t2,$t2,$i3,lsl#8
+	eor	$i3,$t2,$t1
+	eor	$s0,$s0,$i3			@ rk[8]=rk[0]^...
+	eor	$s1,$s1,$s0			@ rk[9]=rk[1]^rk[8]
+	str	$s0,[$key],#32
+	eor	$s2,$s2,$s1			@ rk[10]=rk[2]^rk[9]
+	str	$s1,[$key,#-28]
+	eor	$s3,$s3,$s2			@ rk[11]=rk[3]^rk[10]
+	str	$s2,[$key,#-24]
+	subs	$rounds,$rounds,#1
+	str	$s3,[$key,#-20]
+#ifdef	__thumb2__
+	itt	eq				@ Thumb2 thing, sanity check in ARM
+#endif
+	subeq	r2,$key,#256
+	beq	.Ldone
+
+	and	$t2,lr,$s3
+	and	$i1,lr,$s3,lsr#8
+	ldrb	$t2,[$tbl,$t2]
+	and	$i2,lr,$s3,lsr#16
+	ldrb	$i1,[$tbl,$i1]
+	and	$i3,lr,$s3,lsr#24
+	ldrb	$i2,[$tbl,$i2]
+	orr	$t2,$t2,$i1,lsl#8
+	ldrb	$i3,[$tbl,$i3]
+	orr	$t2,$t2,$i2,lsl#16
+	ldr	$t1,[$key,#-48]
+	orr	$t2,$t2,$i3,lsl#24
+
+	ldr	$i1,[$key,#-44]
+	ldr	$i2,[$key,#-40]
+	eor	$t1,$t1,$t2			@ rk[12]=rk[4]^...
+	ldr	$i3,[$key,#-36]
+	eor	$i1,$i1,$t1			@ rk[13]=rk[5]^rk[12]
+	str	$t1,[$key,#-16]
+	eor	$i2,$i2,$i1			@ rk[14]=rk[6]^rk[13]
+	str	$i1,[$key,#-12]
+	eor	$i3,$i3,$i2			@ rk[15]=rk[7]^rk[14]
+	str	$i2,[$key,#-8]
+	str	$i3,[$key,#-4]
+	b	.L256_loop
+
+.align	2
+.Ldone:	mov	r0,#0
+	ldmia   sp!,{r4-r12,lr}
+.Labrt:
+#if __ARM_ARCH__>=5
+	ret				@ bx lr
+#else
+	tst	lr,#1
+	moveq	pc,lr			@ be binary compatible with V4, yet
+	bx	lr			@ interoperable with Thumb ISA:-)
+#endif
+.size	AES_set_encrypt_key,.-AES_set_encrypt_key
+
+.global AES_set_decrypt_key
+.type   AES_set_decrypt_key,%function
+.align	5
+AES_set_decrypt_key:
+	str	lr,[sp,#-4]!            @ push lr
+	bl	_armv4_AES_set_encrypt_key
+	teq	r0,#0
+	ldr	lr,[sp],#4              @ pop lr
+	bne	.Labrt
+
+	mov	r0,r2			@ AES_set_encrypt_key preserves r2,
+	mov	r1,r2			@ which is AES_KEY *key
+	b	_armv4_AES_set_enc2dec_key
+.size	AES_set_decrypt_key,.-AES_set_decrypt_key
+
+@ void AES_set_enc2dec_key(const AES_KEY *inp,AES_KEY *out)
+.global	AES_set_enc2dec_key
+.type	AES_set_enc2dec_key,%function
+.align	5
+AES_set_enc2dec_key:
+_armv4_AES_set_enc2dec_key:
+	stmdb   sp!,{r4-r12,lr}
+
+	ldr	$rounds,[r0,#240]
+	mov	$i1,r0			@ input
+	add	$i2,r0,$rounds,lsl#4
+	mov	$key,r1			@ output
+	add	$tbl,r1,$rounds,lsl#4
+	str	$rounds,[r1,#240]
+
+.Linv:	ldr	$s0,[$i1],#16
+	ldr	$s1,[$i1,#-12]
+	ldr	$s2,[$i1,#-8]
+	ldr	$s3,[$i1,#-4]
+	ldr	$t1,[$i2],#-16
+	ldr	$t2,[$i2,#16+4]
+	ldr	$t3,[$i2,#16+8]
+	ldr	$i3,[$i2,#16+12]
+	str	$s0,[$tbl],#-16
+	str	$s1,[$tbl,#16+4]
+	str	$s2,[$tbl,#16+8]
+	str	$s3,[$tbl,#16+12]
+	str	$t1,[$key],#16
+	str	$t2,[$key,#-12]
+	str	$t3,[$key,#-8]
+	str	$i3,[$key,#-4]
+	teq	$i1,$i2
+	bne	.Linv
+
+	ldr	$s0,[$i1]
+	ldr	$s1,[$i1,#4]
+	ldr	$s2,[$i1,#8]
+	ldr	$s3,[$i1,#12]
+	str	$s0,[$key]
+	str	$s1,[$key,#4]
+	str	$s2,[$key,#8]
+	str	$s3,[$key,#12]
+	sub	$key,$key,$rounds,lsl#3
+___
+$mask80=$i1;
+$mask1b=$i2;
+$mask7f=$i3;
+$code.=<<___;
+	ldr	$s0,[$key,#16]!		@ prefetch tp1
+	mov	$mask80,#0x80
+	mov	$mask1b,#0x1b
+	orr	$mask80,$mask80,#0x8000
+	orr	$mask1b,$mask1b,#0x1b00
+	orr	$mask80,$mask80,$mask80,lsl#16
+	orr	$mask1b,$mask1b,$mask1b,lsl#16
+	sub	$rounds,$rounds,#1
+	mvn	$mask7f,$mask80
+	mov	$rounds,$rounds,lsl#2	@ (rounds-1)*4
+
+.Lmix:	and	$t1,$s0,$mask80
+	and	$s1,$s0,$mask7f
+	sub	$t1,$t1,$t1,lsr#7
+	and	$t1,$t1,$mask1b
+	eor	$s1,$t1,$s1,lsl#1	@ tp2
+
+	and	$t1,$s1,$mask80
+	and	$s2,$s1,$mask7f
+	sub	$t1,$t1,$t1,lsr#7
+	and	$t1,$t1,$mask1b
+	eor	$s2,$t1,$s2,lsl#1	@ tp4
+
+	and	$t1,$s2,$mask80
+	and	$s3,$s2,$mask7f
+	sub	$t1,$t1,$t1,lsr#7
+	and	$t1,$t1,$mask1b
+	eor	$s3,$t1,$s3,lsl#1	@ tp8
+
+	eor	$t1,$s1,$s2
+	eor	$t2,$s0,$s3		@ tp9
+	eor	$t1,$t1,$s3		@ tpe
+	eor	$t1,$t1,$s1,ror#24
+	eor	$t1,$t1,$t2,ror#24	@ ^= ROTATE(tpb=tp9^tp2,8)
+	eor	$t1,$t1,$s2,ror#16
+	eor	$t1,$t1,$t2,ror#16	@ ^= ROTATE(tpd=tp9^tp4,16)
+	eor	$t1,$t1,$t2,ror#8	@ ^= ROTATE(tp9,24)
+
+	ldr	$s0,[$key,#4]		@ prefetch tp1
+	str	$t1,[$key],#4
+	subs	$rounds,$rounds,#1
+	bne	.Lmix
+
+	mov	r0,#0
+#if __ARM_ARCH__>=5
+	ldmia	sp!,{r4-r12,pc}
+#else
+	ldmia   sp!,{r4-r12,lr}
+	tst	lr,#1
+	moveq	pc,lr			@ be binary compatible with V4, yet
+	bx	lr			@ interoperable with Thumb ISA:-)
+#endif
+.size	AES_set_enc2dec_key,.-AES_set_enc2dec_key
+
+.type	AES_Td,%object
+.align	5
+AES_Td:
+.word	0x51f4a750, 0x7e416553, 0x1a17a4c3, 0x3a275e96
+.word	0x3bab6bcb, 0x1f9d45f1, 0xacfa58ab, 0x4be30393
+.word	0x2030fa55, 0xad766df6, 0x88cc7691, 0xf5024c25
+.word	0x4fe5d7fc, 0xc52acbd7, 0x26354480, 0xb562a38f
+.word	0xdeb15a49, 0x25ba1b67, 0x45ea0e98, 0x5dfec0e1
+.word	0xc32f7502, 0x814cf012, 0x8d4697a3, 0x6bd3f9c6
+.word	0x038f5fe7, 0x15929c95, 0xbf6d7aeb, 0x955259da
+.word	0xd4be832d, 0x587421d3, 0x49e06929, 0x8ec9c844
+.word	0x75c2896a, 0xf48e7978, 0x99583e6b, 0x27b971dd
+.word	0xbee14fb6, 0xf088ad17, 0xc920ac66, 0x7dce3ab4
+.word	0x63df4a18, 0xe51a3182, 0x97513360, 0x62537f45
+.word	0xb16477e0, 0xbb6bae84, 0xfe81a01c, 0xf9082b94
+.word	0x70486858, 0x8f45fd19, 0x94de6c87, 0x527bf8b7
+.word	0xab73d323, 0x724b02e2, 0xe31f8f57, 0x6655ab2a
+.word	0xb2eb2807, 0x2fb5c203, 0x86c57b9a, 0xd33708a5
+.word	0x302887f2, 0x23bfa5b2, 0x02036aba, 0xed16825c
+.word	0x8acf1c2b, 0xa779b492, 0xf307f2f0, 0x4e69e2a1
+.word	0x65daf4cd, 0x0605bed5, 0xd134621f, 0xc4a6fe8a
+.word	0x342e539d, 0xa2f355a0, 0x058ae132, 0xa4f6eb75
+.word	0x0b83ec39, 0x4060efaa, 0x5e719f06, 0xbd6e1051
+.word	0x3e218af9, 0x96dd063d, 0xdd3e05ae, 0x4de6bd46
+.word	0x91548db5, 0x71c45d05, 0x0406d46f, 0x605015ff
+.word	0x1998fb24, 0xd6bde997, 0x894043cc, 0x67d99e77
+.word	0xb0e842bd, 0x07898b88, 0xe7195b38, 0x79c8eedb
+.word	0xa17c0a47, 0x7c420fe9, 0xf8841ec9, 0x00000000
+.word	0x09808683, 0x322bed48, 0x1e1170ac, 0x6c5a724e
+.word	0xfd0efffb, 0x0f853856, 0x3daed51e, 0x362d3927
+.word	0x0a0fd964, 0x685ca621, 0x9b5b54d1, 0x24362e3a
+.word	0x0c0a67b1, 0x9357e70f, 0xb4ee96d2, 0x1b9b919e
+.word	0x80c0c54f, 0x61dc20a2, 0x5a774b69, 0x1c121a16
+.word	0xe293ba0a, 0xc0a02ae5, 0x3c22e043, 0x121b171d
+.word	0x0e090d0b, 0xf28bc7ad, 0x2db6a8b9, 0x141ea9c8
+.word	0x57f11985, 0xaf75074c, 0xee99ddbb, 0xa37f60fd
+.word	0xf701269f, 0x5c72f5bc, 0x44663bc5, 0x5bfb7e34
+.word	0x8b432976, 0xcb23c6dc, 0xb6edfc68, 0xb8e4f163
+.word	0xd731dcca, 0x42638510, 0x13972240, 0x84c61120
+.word	0x854a247d, 0xd2bb3df8, 0xaef93211, 0xc729a16d
+.word	0x1d9e2f4b, 0xdcb230f3, 0x0d8652ec, 0x77c1e3d0
+.word	0x2bb3166c, 0xa970b999, 0x119448fa, 0x47e96422
+.word	0xa8fc8cc4, 0xa0f03f1a, 0x567d2cd8, 0x223390ef
+.word	0x87494ec7, 0xd938d1c1, 0x8ccaa2fe, 0x98d40b36
+.word	0xa6f581cf, 0xa57ade28, 0xdab78e26, 0x3fadbfa4
+.word	0x2c3a9de4, 0x5078920d, 0x6a5fcc9b, 0x547e4662
+.word	0xf68d13c2, 0x90d8b8e8, 0x2e39f75e, 0x82c3aff5
+.word	0x9f5d80be, 0x69d0937c, 0x6fd52da9, 0xcf2512b3
+.word	0xc8ac993b, 0x10187da7, 0xe89c636e, 0xdb3bbb7b
+.word	0xcd267809, 0x6e5918f4, 0xec9ab701, 0x834f9aa8
+.word	0xe6956e65, 0xaaffe67e, 0x21bccf08, 0xef15e8e6
+.word	0xbae79bd9, 0x4a6f36ce, 0xea9f09d4, 0x29b07cd6
+.word	0x31a4b2af, 0x2a3f2331, 0xc6a59430, 0x35a266c0
+.word	0x744ebc37, 0xfc82caa6, 0xe090d0b0, 0x33a7d815
+.word	0xf104984a, 0x41ecdaf7, 0x7fcd500e, 0x1791f62f
+.word	0x764dd68d, 0x43efb04d, 0xccaa4d54, 0xe49604df
+.word	0x9ed1b5e3, 0x4c6a881b, 0xc12c1fb8, 0x4665517f
+.word	0x9d5eea04, 0x018c355d, 0xfa877473, 0xfb0b412e
+.word	0xb3671d5a, 0x92dbd252, 0xe9105633, 0x6dd64713
+.word	0x9ad7618c, 0x37a10c7a, 0x59f8148e, 0xeb133c89
+.word	0xcea927ee, 0xb761c935, 0xe11ce5ed, 0x7a47b13c
+.word	0x9cd2df59, 0x55f2733f, 0x1814ce79, 0x73c737bf
+.word	0x53f7cdea, 0x5ffdaa5b, 0xdf3d6f14, 0x7844db86
+.word	0xcaaff381, 0xb968c43e, 0x3824342c, 0xc2a3405f
+.word	0x161dc372, 0xbce2250c, 0x283c498b, 0xff0d9541
+.word	0x39a80171, 0x080cb3de, 0xd8b4e49c, 0x6456c190
+.word	0x7bcb8461, 0xd532b670, 0x486c5c74, 0xd0b85742
+@ Td4[256]
+.byte	0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38
+.byte	0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb
+.byte	0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87
+.byte	0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb
+.byte	0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d
+.byte	0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e
+.byte	0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2
+.byte	0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25
+.byte	0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16
+.byte	0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92
+.byte	0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda
+.byte	0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84
+.byte	0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a
+.byte	0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06
+.byte	0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02
+.byte	0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b
+.byte	0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea
+.byte	0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73
+.byte	0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85
+.byte	0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e
+.byte	0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89
+.byte	0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b
+.byte	0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20
+.byte	0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4
+.byte	0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31
+.byte	0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f
+.byte	0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d
+.byte	0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef
+.byte	0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0
+.byte	0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61
+.byte	0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26
+.byte	0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d
+.size	AES_Td,.-AES_Td
+
+@ void AES_decrypt(const unsigned char *in, unsigned char *out,
+@ 		 const AES_KEY *key) {
+.global AES_decrypt
+.type   AES_decrypt,%function
+.align	5
+AES_decrypt:
+#ifndef	__thumb2__
+	sub	r3,pc,#8		@ AES_decrypt
+#else
+	adr	r3,AES_decrypt
+#endif
+	stmdb   sp!,{r1,r4-r12,lr}
+#ifdef	__APPLE__
+	adr	$tbl,AES_Td
+#else
+	sub	$tbl,r3,#AES_decrypt-AES_Td	@ Td
+#endif
+	mov	$rounds,r0		@ inp
+	mov	$key,r2
+#if __ARM_ARCH__<7
+	ldrb	$s0,[$rounds,#3]	@ load input data in endian-neutral
+	ldrb	$t1,[$rounds,#2]	@ manner...
+	ldrb	$t2,[$rounds,#1]
+	ldrb	$t3,[$rounds,#0]
+	orr	$s0,$s0,$t1,lsl#8
+	ldrb	$s1,[$rounds,#7]
+	orr	$s0,$s0,$t2,lsl#16
+	ldrb	$t1,[$rounds,#6]
+	orr	$s0,$s0,$t3,lsl#24
+	ldrb	$t2,[$rounds,#5]
+	ldrb	$t3,[$rounds,#4]
+	orr	$s1,$s1,$t1,lsl#8
+	ldrb	$s2,[$rounds,#11]
+	orr	$s1,$s1,$t2,lsl#16
+	ldrb	$t1,[$rounds,#10]
+	orr	$s1,$s1,$t3,lsl#24
+	ldrb	$t2,[$rounds,#9]
+	ldrb	$t3,[$rounds,#8]
+	orr	$s2,$s2,$t1,lsl#8
+	ldrb	$s3,[$rounds,#15]
+	orr	$s2,$s2,$t2,lsl#16
+	ldrb	$t1,[$rounds,#14]
+	orr	$s2,$s2,$t3,lsl#24
+	ldrb	$t2,[$rounds,#13]
+	ldrb	$t3,[$rounds,#12]
+	orr	$s3,$s3,$t1,lsl#8
+	orr	$s3,$s3,$t2,lsl#16
+	orr	$s3,$s3,$t3,lsl#24
+#else
+	ldr	$s0,[$rounds,#0]
+	ldr	$s1,[$rounds,#4]
+	ldr	$s2,[$rounds,#8]
+	ldr	$s3,[$rounds,#12]
+#ifdef __ARMEL__
+	rev	$s0,$s0
+	rev	$s1,$s1
+	rev	$s2,$s2
+	rev	$s3,$s3
+#endif
+#endif
+	bl	_armv4_AES_decrypt
+
+	ldr	$rounds,[sp],#4		@ pop out
+#if __ARM_ARCH__>=7
+#ifdef __ARMEL__
+	rev	$s0,$s0
+	rev	$s1,$s1
+	rev	$s2,$s2
+	rev	$s3,$s3
+#endif
+	str	$s0,[$rounds,#0]
+	str	$s1,[$rounds,#4]
+	str	$s2,[$rounds,#8]
+	str	$s3,[$rounds,#12]
+#else
+	mov	$t1,$s0,lsr#24		@ write output in endian-neutral
+	mov	$t2,$s0,lsr#16		@ manner...
+	mov	$t3,$s0,lsr#8
+	strb	$t1,[$rounds,#0]
+	strb	$t2,[$rounds,#1]
+	mov	$t1,$s1,lsr#24
+	strb	$t3,[$rounds,#2]
+	mov	$t2,$s1,lsr#16
+	strb	$s0,[$rounds,#3]
+	mov	$t3,$s1,lsr#8
+	strb	$t1,[$rounds,#4]
+	strb	$t2,[$rounds,#5]
+	mov	$t1,$s2,lsr#24
+	strb	$t3,[$rounds,#6]
+	mov	$t2,$s2,lsr#16
+	strb	$s1,[$rounds,#7]
+	mov	$t3,$s2,lsr#8
+	strb	$t1,[$rounds,#8]
+	strb	$t2,[$rounds,#9]
+	mov	$t1,$s3,lsr#24
+	strb	$t3,[$rounds,#10]
+	mov	$t2,$s3,lsr#16
+	strb	$s2,[$rounds,#11]
+	mov	$t3,$s3,lsr#8
+	strb	$t1,[$rounds,#12]
+	strb	$t2,[$rounds,#13]
+	strb	$t3,[$rounds,#14]
+	strb	$s3,[$rounds,#15]
+#endif
+#if __ARM_ARCH__>=5
+	ldmia	sp!,{r4-r12,pc}
+#else
+	ldmia   sp!,{r4-r12,lr}
+	tst	lr,#1
+	moveq	pc,lr			@ be binary compatible with V4, yet
+	bx	lr			@ interoperable with Thumb ISA:-)
+#endif
+.size	AES_decrypt,.-AES_decrypt
+
+.type   _armv4_AES_decrypt,%function
+.align	2
+_armv4_AES_decrypt:
+	str	lr,[sp,#-4]!		@ push lr
+	ldmia	$key!,{$t1-$i1}
+	eor	$s0,$s0,$t1
+	ldr	$rounds,[$key,#240-16]
+	eor	$s1,$s1,$t2
+	eor	$s2,$s2,$t3
+	eor	$s3,$s3,$i1
+	sub	$rounds,$rounds,#1
+	mov	lr,#255
+
+	and	$i1,lr,$s0,lsr#16
+	and	$i2,lr,$s0,lsr#8
+	and	$i3,lr,$s0
+	mov	$s0,$s0,lsr#24
+.Ldec_loop:
+	ldr	$t1,[$tbl,$i1,lsl#2]	@ Td1[s0>>16]
+	and	$i1,lr,$s1		@ i0
+	ldr	$t2,[$tbl,$i2,lsl#2]	@ Td2[s0>>8]
+	and	$i2,lr,$s1,lsr#16
+	ldr	$t3,[$tbl,$i3,lsl#2]	@ Td3[s0>>0]
+	and	$i3,lr,$s1,lsr#8
+	ldr	$s0,[$tbl,$s0,lsl#2]	@ Td0[s0>>24]
+	mov	$s1,$s1,lsr#24
+
+	ldr	$i1,[$tbl,$i1,lsl#2]	@ Td3[s1>>0]
+	ldr	$i2,[$tbl,$i2,lsl#2]	@ Td1[s1>>16]
+	ldr	$i3,[$tbl,$i3,lsl#2]	@ Td2[s1>>8]
+	eor	$s0,$s0,$i1,ror#24
+	ldr	$s1,[$tbl,$s1,lsl#2]	@ Td0[s1>>24]
+	and	$i1,lr,$s2,lsr#8	@ i0
+	eor	$t2,$i2,$t2,ror#8
+	and	$i2,lr,$s2		@ i1
+	eor	$t3,$i3,$t3,ror#8
+	and	$i3,lr,$s2,lsr#16
+	ldr	$i1,[$tbl,$i1,lsl#2]	@ Td2[s2>>8]
+	eor	$s1,$s1,$t1,ror#8
+	ldr	$i2,[$tbl,$i2,lsl#2]	@ Td3[s2>>0]
+	mov	$s2,$s2,lsr#24
+
+	ldr	$i3,[$tbl,$i3,lsl#2]	@ Td1[s2>>16]
+	eor	$s0,$s0,$i1,ror#16
+	ldr	$s2,[$tbl,$s2,lsl#2]	@ Td0[s2>>24]
+	and	$i1,lr,$s3,lsr#16	@ i0
+	eor	$s1,$s1,$i2,ror#24
+	and	$i2,lr,$s3,lsr#8	@ i1
+	eor	$t3,$i3,$t3,ror#8
+	and	$i3,lr,$s3		@ i2
+	ldr	$i1,[$tbl,$i1,lsl#2]	@ Td1[s3>>16]
+	eor	$s2,$s2,$t2,ror#8
+	ldr	$i2,[$tbl,$i2,lsl#2]	@ Td2[s3>>8]
+	mov	$s3,$s3,lsr#24
+
+	ldr	$i3,[$tbl,$i3,lsl#2]	@ Td3[s3>>0]
+	eor	$s0,$s0,$i1,ror#8
+	ldr	$i1,[$key],#16
+	eor	$s1,$s1,$i2,ror#16
+	ldr	$s3,[$tbl,$s3,lsl#2]	@ Td0[s3>>24]
+	eor	$s2,$s2,$i3,ror#24
+
+	ldr	$t1,[$key,#-12]
+	eor	$s0,$s0,$i1
+	ldr	$t2,[$key,#-8]
+	eor	$s3,$s3,$t3,ror#8
+	ldr	$t3,[$key,#-4]
+	and	$i1,lr,$s0,lsr#16
+	eor	$s1,$s1,$t1
+	and	$i2,lr,$s0,lsr#8
+	eor	$s2,$s2,$t2
+	and	$i3,lr,$s0
+	eor	$s3,$s3,$t3
+	mov	$s0,$s0,lsr#24
+
+	subs	$rounds,$rounds,#1
+	bne	.Ldec_loop
+
+	add	$tbl,$tbl,#1024
+
+	ldr	$t2,[$tbl,#0]		@ prefetch Td4
+	ldr	$t3,[$tbl,#32]
+	ldr	$t1,[$tbl,#64]
+	ldr	$t2,[$tbl,#96]
+	ldr	$t3,[$tbl,#128]
+	ldr	$t1,[$tbl,#160]
+	ldr	$t2,[$tbl,#192]
+	ldr	$t3,[$tbl,#224]
+
+	ldrb	$s0,[$tbl,$s0]		@ Td4[s0>>24]
+	ldrb	$t1,[$tbl,$i1]		@ Td4[s0>>16]
+	and	$i1,lr,$s1		@ i0
+	ldrb	$t2,[$tbl,$i2]		@ Td4[s0>>8]
+	and	$i2,lr,$s1,lsr#16
+	ldrb	$t3,[$tbl,$i3]		@ Td4[s0>>0]
+	and	$i3,lr,$s1,lsr#8
+
+	add	$s1,$tbl,$s1,lsr#24
+	ldrb	$i1,[$tbl,$i1]		@ Td4[s1>>0]
+	ldrb	$s1,[$s1]		@ Td4[s1>>24]
+	ldrb	$i2,[$tbl,$i2]		@ Td4[s1>>16]
+	eor	$s0,$i1,$s0,lsl#24
+	ldrb	$i3,[$tbl,$i3]		@ Td4[s1>>8]
+	eor	$s1,$t1,$s1,lsl#8
+	and	$i1,lr,$s2,lsr#8	@ i0
+	eor	$t2,$t2,$i2,lsl#8
+	and	$i2,lr,$s2		@ i1
+	ldrb	$i1,[$tbl,$i1]		@ Td4[s2>>8]
+	eor	$t3,$t3,$i3,lsl#8
+	ldrb	$i2,[$tbl,$i2]		@ Td4[s2>>0]
+	and	$i3,lr,$s2,lsr#16
+
+	add	$s2,$tbl,$s2,lsr#24
+	ldrb	$s2,[$s2]		@ Td4[s2>>24]
+	eor	$s0,$s0,$i1,lsl#8
+	ldrb	$i3,[$tbl,$i3]		@ Td4[s2>>16]
+	eor	$s1,$i2,$s1,lsl#16
+	and	$i1,lr,$s3,lsr#16	@ i0
+	eor	$s2,$t2,$s2,lsl#16
+	and	$i2,lr,$s3,lsr#8	@ i1
+	ldrb	$i1,[$tbl,$i1]		@ Td4[s3>>16]
+	eor	$t3,$t3,$i3,lsl#16
+	ldrb	$i2,[$tbl,$i2]		@ Td4[s3>>8]
+	and	$i3,lr,$s3		@ i2
+
+	add	$s3,$tbl,$s3,lsr#24
+	ldrb	$i3,[$tbl,$i3]		@ Td4[s3>>0]
+	ldrb	$s3,[$s3]		@ Td4[s3>>24]
+	eor	$s0,$s0,$i1,lsl#16
+	ldr	$i1,[$key,#0]
+	eor	$s1,$s1,$i2,lsl#8
+	ldr	$t1,[$key,#4]
+	eor	$s2,$i3,$s2,lsl#8
+	ldr	$t2,[$key,#8]
+	eor	$s3,$t3,$s3,lsl#24
+	ldr	$t3,[$key,#12]
+
+	eor	$s0,$s0,$i1
+	eor	$s1,$s1,$t1
+	eor	$s2,$s2,$t2
+	eor	$s3,$s3,$t3
+
+	sub	$tbl,$tbl,#1024
+	ldr	pc,[sp],#4		@ pop and return
+.size	_armv4_AES_decrypt,.-_armv4_AES_decrypt
+.asciz	"AES for ARMv4, CRYPTOGAMS by "
+.align	2
+___
+
+$code =~ s/\bbx\s+lr\b/.word\t0xe12fff1e/gm;	# make it possible to compile with -march=armv4
+$code =~ s/\bret\b/bx\tlr/gm;
+
+open SELF,$0;
+while() {
+	next if (/^#!/);
+	last if (!s/^#/@/ and !/^$/);
+	print;
+}
+close SELF;
+
+print $code;
+close STDOUT;	# enforce flush
diff --git a/crypto/aes/asm/aes-c64xplus.pl b/crypto/aes/asm/aes-c64xplus.pl
new file mode 100644
index 000000000..19d2cc176
--- /dev/null
+++ b/crypto/aes/asm/aes-c64xplus.pl
@@ -0,0 +1,1382 @@
+#! /usr/bin/env perl
+# Copyright 2012-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+#
+# ====================================================================
+# Written by Andy Polyakov  for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+#
+# [Endian-neutral] AES for C64x+.
+#
+# Even though SPLOOPs are scheduled for 13 cycles, and thus expected
+# performance is ~8.5 cycles per byte processed with 128-bit key,
+# measured performance turned to be ~10 cycles per byte. Discrepancy
+# must be caused by limitations of L1D memory banking(*), see SPRU871
+# TI publication for further details. If any consolation it's still
+# ~20% faster than TI's linear assembly module anyway... Compared to
+# aes_core.c compiled with cl6x 6.0 with -mv6400+ -o2 options this
+# code is 3.75x faster and almost 3x smaller (tables included).
+#
+# (*)	This means that there might be subtle correlation between data
+#	and timing and one can wonder if it can be ... attacked:-(
+#	On the other hand this also means that *if* one chooses to
+#	implement *4* T-tables variant [instead of 1 T-table as in
+#	this implementation, or in addition to], then one ought to
+#	*interleave* them. Even though it complicates addressing,
+#	references to interleaved tables would be guaranteed not to
+#	clash. I reckon that it should be possible to break 8 cycles
+#	per byte "barrier," i.e. improve by ~20%, naturally at the
+#	cost of 8x increased pressure on L1D. 8x because you'd have
+#	to interleave both Te and Td tables...
+
+while (($output=shift) && ($output!~/\w[\w\-]*\.\w+$/)) {}
+open STDOUT,">$output";
+
+($TEA,$TEB)=("A5","B5");
+($KPA,$KPB)=("A3","B1");
+@K=("A6","B6","A7","B7");
+@s=("A8","B8","A9","B9");
+@Te0=@Td0=("A16","B16","A17","B17");
+@Te1=@Td1=("A18","B18","A19","B19");
+@Te2=@Td2=("A20","B20","A21","B21");
+@Te3=@Td3=("A22","B22","A23","B23");
+
+$code=<<___;
+	.text
+
+	.if	.ASSEMBLER_VERSION<7000000
+	.asg	0,__TI_EABI__
+	.endif
+	.if	__TI_EABI__
+	.nocmp
+	.asg	AES_encrypt,_AES_encrypt
+	.asg	AES_decrypt,_AES_decrypt
+	.asg	AES_set_encrypt_key,_AES_set_encrypt_key
+	.asg	AES_set_decrypt_key,_AES_set_decrypt_key
+	.asg	AES_ctr32_encrypt,_AES_ctr32_encrypt
+	.endif
+
+	.asg	B3,RA
+	.asg	A4,INP
+	.asg	B4,OUT
+	.asg	A6,KEY
+	.asg	A4,RET
+	.asg	B15,SP
+
+	.eval	24,EXT0
+	.eval	16,EXT1
+	.eval	8,EXT2
+	.eval	0,EXT3
+	.eval	8,TBL1
+	.eval	16,TBL2
+	.eval	24,TBL3
+
+	.if	.BIG_ENDIAN
+	.eval	24-EXT0,EXT0
+	.eval	24-EXT1,EXT1
+	.eval	24-EXT2,EXT2
+	.eval	24-EXT3,EXT3
+	.eval	32-TBL1,TBL1
+	.eval	32-TBL2,TBL2
+	.eval	32-TBL3,TBL3
+	.endif
+
+	.global	_AES_encrypt
+_AES_encrypt:
+	.asmfunc
+	MVK	1,B2
+__encrypt:
+	.if	__TI_EABI__
+   [B2]	LDNDW	*INP++,A9:A8			; load input
+||	MVKL	\$PCR_OFFSET(AES_Te,__encrypt),$TEA
+||	ADDKPC	__encrypt,B0
+   [B2]	LDNDW	*INP++,B9:B8
+||	MVKH	\$PCR_OFFSET(AES_Te,__encrypt),$TEA
+||	ADD	0,KEY,$KPA
+||	ADD	4,KEY,$KPB
+	.else
+   [B2]	LDNDW	*INP++,A9:A8			; load input
+||	MVKL	(AES_Te-__encrypt),$TEA
+||	ADDKPC	__encrypt,B0
+   [B2]	LDNDW	*INP++,B9:B8
+||	MVKH	(AES_Te-__encrypt),$TEA
+||	ADD	0,KEY,$KPA
+||	ADD	4,KEY,$KPB
+	.endif
+	LDW	*$KPA++[2],$Te0[0]		; zero round key
+||	LDW	*$KPB++[2],$Te0[1]
+||	MVK	60,A0
+||	ADD	B0,$TEA,$TEA			; AES_Te
+	LDW	*KEY[A0],B0			; rounds
+||	MVK	1024,A0				; sizeof(AES_Te)
+	LDW	*$KPA++[2],$Te0[2]
+||	LDW	*$KPB++[2],$Te0[3]
+||	MV	$TEA,$TEB
+	NOP
+	.if	.BIG_ENDIAN
+	MV	A9,$s[0]
+||	MV	A8,$s[1]
+||	MV	B9,$s[2]
+||	MV	B8,$s[3]
+	.else
+	MV	A8,$s[0]
+||	MV	A9,$s[1]
+||	MV	B8,$s[2]
+||	MV	B9,$s[3]
+	.endif
+	XOR	$Te0[0],$s[0],$s[0]
+||	XOR	$Te0[1],$s[1],$s[1]
+||	LDW	*$KPA++[2],$K[0]		; 1st round key
+||	LDW	*$KPB++[2],$K[1]
+	SUB	B0,2,B0
+
+	SPLOOPD	13
+||	MVC	B0,ILC
+||	LDW	*$KPA++[2],$K[2]
+||	LDW	*$KPB++[2],$K[3]
+;;====================================================================
+	EXTU	$s[1],EXT1,24,$Te1[1]
+||	EXTU	$s[0],EXT3,24,$Te3[0]
+	LDW	*${TEB}[$Te1[1]],$Te1[1]	; Te1[s1>>8],	t0
+||	LDW	*${TEA}[$Te3[0]],$Te3[0]	; Te3[s0>>24],	t1
+||	XOR	$s[2],$Te0[2],$s[2]		; modulo-scheduled
+||	XOR	$s[3],$Te0[3],$s[3]		; modulo-scheduled
+||	EXTU	$s[1],EXT3,24,$Te3[1]
+||	EXTU	$s[0],EXT1,24,$Te1[0]
+	LDW	*${TEB}[$Te3[1]],$Te3[1]	; Te3[s1>>24],	t2
+||	LDW	*${TEA}[$Te1[0]],$Te1[0]	; Te1[s0>>8],	t3
+||	EXTU	$s[2],EXT2,24,$Te2[2]
+||	EXTU	$s[3],EXT2,24,$Te2[3]
+	LDW	*${TEA}[$Te2[2]],$Te2[2]	; Te2[s2>>16],	t0
+||	LDW	*${TEB}[$Te2[3]],$Te2[3]	; Te2[s3>>16],	t1
+||	EXTU	$s[3],EXT3,24,$Te3[3]
+||	EXTU	$s[2],EXT1,24,$Te1[2]
+	LDW	*${TEB}[$Te3[3]],$Te3[3]	; Te3[s3>>24],	t0
+||	LDW	*${TEA}[$Te1[2]],$Te1[2]	; Te1[s2>>8],	t1
+||	EXTU	$s[0],EXT2,24,$Te2[0]
+||	EXTU	$s[1],EXT2,24,$Te2[1]
+	LDW	*${TEA}[$Te2[0]],$Te2[0]	; Te2[s0>>16],	t2
+||	LDW	*${TEB}[$Te2[1]],$Te2[1]	; Te2[s1>>16],	t3
+||	EXTU	$s[3],EXT1,24,$Te1[3]
+||	EXTU	$s[2],EXT3,24,$Te3[2]
+	LDW	*${TEB}[$Te1[3]],$Te1[3]	; Te1[s3>>8],	t2
+||	LDW	*${TEA}[$Te3[2]],$Te3[2]	; Te3[s2>>24],	t3
+||	ROTL	$Te1[1],TBL1,$Te3[0]		; t0
+||	ROTL	$Te3[0],TBL3,$Te1[1]		; t1
+||	EXTU	$s[0],EXT0,24,$Te0[0]
+||	EXTU	$s[1],EXT0,24,$Te0[1]
+	LDW	*${TEA}[$Te0[0]],$Te0[0]	; Te0[s0],	t0
+||	LDW	*${TEB}[$Te0[1]],$Te0[1]	; Te0[s1],	t1
+||	ROTL	$Te3[1],TBL3,$Te1[0]		; t2
+||	ROTL	$Te1[0],TBL1,$Te3[1]		; t3
+||	EXTU	$s[2],EXT0,24,$Te0[2]
+||	EXTU	$s[3],EXT0,24,$Te0[3]
+	LDW	*${TEA}[$Te0[2]],$Te0[2]	; Te0[s2],	t2
+||	LDW	*${TEB}[$Te0[3]],$Te0[3]	; Te0[s3],	t3
+||	ROTL	$Te2[2],TBL2,$Te2[2]		; t0
+||	ROTL	$Te2[3],TBL2,$Te2[3]		; t1
+||	XOR	$K[0],$Te3[0],$s[0]
+||	XOR	$K[1],$Te1[1],$s[1]
+	ROTL	$Te3[3],TBL3,$Te1[2]		; t0
+||	ROTL	$Te1[2],TBL1,$Te3[3]		; t1
+||	XOR	$K[2],$Te1[0],$s[2]
+||	XOR	$K[3],$Te3[1],$s[3]
+||	LDW	*$KPA++[2],$K[0]		; next round key
+||	LDW	*$KPB++[2],$K[1]
+	ROTL	$Te2[0],TBL2,$Te2[0]		; t2
+||	ROTL	$Te2[1],TBL2,$Te2[1]		; t3
+||	XOR	$s[0],$Te2[2],$s[0]
+||	XOR	$s[1],$Te2[3],$s[1]
+||	LDW	*$KPA++[2],$K[2]
+||	LDW	*$KPB++[2],$K[3]
+	ROTL	$Te1[3],TBL1,$Te3[2]		; t2
+||	ROTL	$Te3[2],TBL3,$Te1[3]		; t3
+||	XOR	$s[0],$Te1[2],$s[0]
+||	XOR	$s[1],$Te3[3],$s[1]
+	XOR	$s[2],$Te2[0],$s[2]
+||	XOR	$s[3],$Te2[1],$s[3]
+||	XOR	$s[0],$Te0[0],$s[0]
+||	XOR	$s[1],$Te0[1],$s[1]
+	SPKERNEL
+||	XOR.L	$s[2],$Te3[2],$s[2]
+||	XOR.L	$s[3],$Te1[3],$s[3]
+;;====================================================================
+	ADD.D	${TEA},A0,${TEA}		; point to Te4
+||	ADD.D	${TEB},A0,${TEB}
+||	EXTU	$s[1],EXT1,24,$Te1[1]
+||	EXTU	$s[0],EXT3,24,$Te3[0]
+	LDBU	*${TEB}[$Te1[1]],$Te1[1]	; Te1[s1>>8],	t0
+||	LDBU	*${TEA}[$Te3[0]],$Te3[0]	; Te3[s0>>24],	t1
+||	XOR	$s[2],$Te0[2],$s[2]		; modulo-scheduled
+||	XOR	$s[3],$Te0[3],$s[3]		; modulo-scheduled
+||	EXTU	$s[0],EXT0,24,$Te0[0]
+||	EXTU	$s[1],EXT0,24,$Te0[1]
+	LDBU	*${TEA}[$Te0[0]],$Te0[0]	; Te0[s0],	t0
+||	LDBU	*${TEB}[$Te0[1]],$Te0[1]	; Te0[s1],	t1
+||	EXTU	$s[3],EXT3,24,$Te3[3]
+||	EXTU	$s[2],EXT1,24,$Te1[2]
+	LDBU	*${TEB}[$Te3[3]],$Te3[3]	; Te3[s3>>24],	t0
+||	LDBU	*${TEA}[$Te1[2]],$Te1[2]	; Te1[s2>>8],	t1
+||	EXTU	$s[2],EXT2,24,$Te2[2]
+||	EXTU	$s[3],EXT2,24,$Te2[3]
+	LDBU	*${TEA}[$Te2[2]],$Te2[2]	; Te2[s2>>16],	t0
+||	LDBU	*${TEB}[$Te2[3]],$Te2[3]	; Te2[s3>>16],	t1
+||	EXTU	$s[1],EXT3,24,$Te3[1]
+||	EXTU	$s[0],EXT1,24,$Te1[0]
+	LDBU	*${TEB}[$Te3[1]],$Te3[1]	; Te3[s1>>24],	t2
+||	LDBU	*${TEA}[$Te1[0]],$Te1[0]	; Te1[s0>>8],	t3
+||	EXTU	$s[3],EXT1,24,$Te1[3]
+||	EXTU	$s[2],EXT3,24,$Te3[2]
+	LDBU	*${TEB}[$Te1[3]],$Te1[3]	; Te1[s3>>8],	t2
+||	LDBU	*${TEA}[$Te3[2]],$Te3[2]	; Te3[s2>>24],	t3
+||	EXTU	$s[2],EXT0,24,$Te0[2]
+||	EXTU	$s[3],EXT0,24,$Te0[3]
+	LDBU	*${TEA}[$Te0[2]],$Te0[2]	; Te0[s2],	t2
+||	LDBU	*${TEB}[$Te0[3]],$Te0[3]	; Te0[s3],	t3
+||	EXTU	$s[0],EXT2,24,$Te2[0]
+||	EXTU	$s[1],EXT2,24,$Te2[1]
+	LDBU	*${TEA}[$Te2[0]],$Te2[0]	; Te2[s0>>16],	t2
+||	LDBU	*${TEB}[$Te2[1]],$Te2[1]	; Te2[s1>>16],	t3
+
+	.if	.BIG_ENDIAN
+	PACK2	$Te0[0],$Te1[1],$Te0[0]
+||	PACK2	$Te0[1],$Te1[2],$Te0[1]
+	PACK2	$Te2[2],$Te3[3],$Te2[2]
+||	PACK2	$Te2[3],$Te3[0],$Te2[3]
+	PACKL4	$Te0[0],$Te2[2],$Te0[0]
+||	PACKL4	$Te0[1],$Te2[3],$Te0[1]
+	XOR	$K[0],$Te0[0],$Te0[0]		; s[0]
+||	XOR	$K[1],$Te0[1],$Te0[1]		; s[1]
+
+	PACK2	$Te0[2],$Te1[3],$Te0[2]
+||	PACK2	$Te0[3],$Te1[0],$Te0[3]
+	PACK2	$Te2[0],$Te3[1],$Te2[0]
+||	PACK2	$Te2[1],$Te3[2],$Te2[1]
+||	BNOP	RA
+	PACKL4	$Te0[2],$Te2[0],$Te0[2]
+||	PACKL4	$Te0[3],$Te2[1],$Te0[3]
+	XOR	$K[2],$Te0[2],$Te0[2]		; s[2]
+||	XOR	$K[3],$Te0[3],$Te0[3]		; s[3]
+
+	MV	$Te0[0],A9
+||	MV	$Te0[1],A8
+	MV	$Te0[2],B9
+||	MV	$Te0[3],B8
+|| [B2]	STNDW	A9:A8,*OUT++
+   [B2]	STNDW	B9:B8,*OUT++
+	.else
+	PACK2	$Te1[1],$Te0[0],$Te1[1]
+||	PACK2	$Te1[2],$Te0[1],$Te1[2]
+	PACK2	$Te3[3],$Te2[2],$Te3[3]
+||	PACK2	$Te3[0],$Te2[3],$Te3[0]
+	PACKL4	$Te3[3],$Te1[1],$Te1[1]
+||	PACKL4	$Te3[0],$Te1[2],$Te1[2]
+	XOR	$K[0],$Te1[1],$Te1[1]		; s[0]
+||	XOR	$K[1],$Te1[2],$Te1[2]		; s[1]
+
+	PACK2	$Te1[3],$Te0[2],$Te1[3]
+||	PACK2	$Te1[0],$Te0[3],$Te1[0]
+	PACK2	$Te3[1],$Te2[0],$Te3[1]
+||	PACK2	$Te3[2],$Te2[1],$Te3[2]
+||	BNOP	RA
+	PACKL4	$Te3[1],$Te1[3],$Te1[3]
+||	PACKL4	$Te3[2],$Te1[0],$Te1[0]
+	XOR	$K[2],$Te1[3],$Te1[3]		; s[2]
+||	XOR	$K[3],$Te1[0],$Te1[0]		; s[3]
+
+	MV	$Te1[1],A8
+||	MV	$Te1[2],A9
+	MV	$Te1[3],B8
+||	MV	$Te1[0],B9
+|| [B2]	STNDW	A9:A8,*OUT++
+   [B2]	STNDW	B9:B8,*OUT++
+	.endif
+	.endasmfunc
+
+	.global	_AES_decrypt
+_AES_decrypt:
+	.asmfunc
+	MVK	1,B2
+__decrypt:
+	.if	__TI_EABI__
+   [B2]	LDNDW	*INP++,A9:A8			; load input
+||	MVKL	\$PCR_OFFSET(AES_Td,__decrypt),$TEA
+||	ADDKPC	__decrypt,B0
+   [B2]	LDNDW	*INP++,B9:B8
+||	MVKH	\$PCR_OFFSET(AES_Td,__decrypt),$TEA
+||	ADD	0,KEY,$KPA
+||	ADD	4,KEY,$KPB
+	.else
+   [B2]	LDNDW	*INP++,A9:A8			; load input
+||	MVKL	(AES_Td-__decrypt),$TEA
+||	ADDKPC	__decrypt,B0
+   [B2]	LDNDW	*INP++,B9:B8
+||	MVKH	(AES_Td-__decrypt),$TEA
+||	ADD	0,KEY,$KPA
+||	ADD	4,KEY,$KPB
+	.endif
+	LDW	*$KPA++[2],$Td0[0]		; zero round key
+||	LDW	*$KPB++[2],$Td0[1]
+||	MVK	60,A0
+||	ADD	B0,$TEA,$TEA			; AES_Td
+	LDW	*KEY[A0],B0			; rounds
+||	MVK	1024,A0				; sizeof(AES_Td)
+	LDW	*$KPA++[2],$Td0[2]
+||	LDW	*$KPB++[2],$Td0[3]
+||	MV	$TEA,$TEB
+	NOP
+	.if	.BIG_ENDIAN
+	MV	A9,$s[0]
+||	MV	A8,$s[1]
+||	MV	B9,$s[2]
+||	MV	B8,$s[3]
+	.else
+	MV	A8,$s[0]
+||	MV	A9,$s[1]
+||	MV	B8,$s[2]
+||	MV	B9,$s[3]
+	.endif
+	XOR	$Td0[0],$s[0],$s[0]
+||	XOR	$Td0[1],$s[1],$s[1]
+||	LDW	*$KPA++[2],$K[0]		; 1st round key
+||	LDW	*$KPB++[2],$K[1]
+	SUB	B0,2,B0
+
+	SPLOOPD	13
+||	MVC	B0,ILC
+||	LDW	*$KPA++[2],$K[2]
+||	LDW	*$KPB++[2],$K[3]
+;;====================================================================
+	EXTU	$s[1],EXT3,24,$Td3[1]
+||	EXTU	$s[0],EXT1,24,$Td1[0]
+	LDW	*${TEB}[$Td3[1]],$Td3[1]	; Td3[s1>>24],	t0
+||	LDW	*${TEA}[$Td1[0]],$Td1[0]	; Td1[s0>>8],	t1
+||	XOR	$s[2],$Td0[2],$s[2]		; modulo-scheduled
+||	XOR	$s[3],$Td0[3],$s[3]		; modulo-scheduled
+||	EXTU	$s[1],EXT1,24,$Td1[1]
+||	EXTU	$s[0],EXT3,24,$Td3[0]
+	LDW	*${TEB}[$Td1[1]],$Td1[1]	; Td1[s1>>8],	t2
+||	LDW	*${TEA}[$Td3[0]],$Td3[0]	; Td3[s0>>24],	t3
+||	EXTU	$s[2],EXT2,24,$Td2[2]
+||	EXTU	$s[3],EXT2,24,$Td2[3]
+	LDW	*${TEA}[$Td2[2]],$Td2[2]	; Td2[s2>>16],	t0
+||	LDW	*${TEB}[$Td2[3]],$Td2[3]	; Td2[s3>>16],	t1
+||	EXTU	$s[3],EXT1,24,$Td1[3]
+||	EXTU	$s[2],EXT3,24,$Td3[2]
+	LDW	*${TEB}[$Td1[3]],$Td1[3]	; Td1[s3>>8],	t0
+||	LDW	*${TEA}[$Td3[2]],$Td3[2]	; Td3[s2>>24],	t1
+||	EXTU	$s[0],EXT2,24,$Td2[0]
+||	EXTU	$s[1],EXT2,24,$Td2[1]
+	LDW	*${TEA}[$Td2[0]],$Td2[0]	; Td2[s0>>16],	t2
+||	LDW	*${TEB}[$Td2[1]],$Td2[1]	; Td2[s1>>16],	t3
+||	EXTU	$s[3],EXT3,24,$Td3[3]
+||	EXTU	$s[2],EXT1,24,$Td1[2]
+	LDW	*${TEB}[$Td3[3]],$Td3[3]	; Td3[s3>>24],	t2
+||	LDW	*${TEA}[$Td1[2]],$Td1[2]	; Td1[s2>>8],	t3
+||	ROTL	$Td3[1],TBL3,$Td1[0]		; t0
+||	ROTL	$Td1[0],TBL1,$Td3[1]		; t1
+||	EXTU	$s[0],EXT0,24,$Td0[0]
+||	EXTU	$s[1],EXT0,24,$Td0[1]
+	LDW	*${TEA}[$Td0[0]],$Td0[0]	; Td0[s0],	t0
+||	LDW	*${TEB}[$Td0[1]],$Td0[1]	; Td0[s1],	t1
+||	ROTL	$Td1[1],TBL1,$Td3[0]		; t2
+||	ROTL	$Td3[0],TBL3,$Td1[1]		; t3
+||	EXTU	$s[2],EXT0,24,$Td0[2]
+||	EXTU	$s[3],EXT0,24,$Td0[3]
+	LDW	*${TEA}[$Td0[2]],$Td0[2]	; Td0[s2],	t2
+||	LDW	*${TEB}[$Td0[3]],$Td0[3]	; Td0[s3],	t3
+||	ROTL	$Td2[2],TBL2,$Td2[2]		; t0
+||	ROTL	$Td2[3],TBL2,$Td2[3]		; t1
+||	XOR	$K[0],$Td1[0],$s[0]
+||	XOR	$K[1],$Td3[1],$s[1]
+	ROTL	$Td1[3],TBL1,$Td3[2]		; t0
+||	ROTL	$Td3[2],TBL3,$Td1[3]		; t1
+||	XOR	$K[2],$Td3[0],$s[2]
+||	XOR	$K[3],$Td1[1],$s[3]
+||	LDW	*$KPA++[2],$K[0]		; next round key
+||	LDW	*$KPB++[2],$K[1]
+	ROTL	$Td2[0],TBL2,$Td2[0]		; t2
+||	ROTL	$Td2[1],TBL2,$Td2[1]		; t3
+||	XOR	$s[0],$Td2[2],$s[0]
+||	XOR	$s[1],$Td2[3],$s[1]
+||	LDW	*$KPA++[2],$K[2]
+||	LDW	*$KPB++[2],$K[3]
+	ROTL	$Td3[3],TBL3,$Td1[2]		; t2
+||	ROTL	$Td1[2],TBL1,$Td3[3]		; t3
+||	XOR	$s[0],$Td3[2],$s[0]
+||	XOR	$s[1],$Td1[3],$s[1]
+	XOR	$s[2],$Td2[0],$s[2]
+||	XOR	$s[3],$Td2[1],$s[3]
+||	XOR	$s[0],$Td0[0],$s[0]
+||	XOR	$s[1],$Td0[1],$s[1]
+	SPKERNEL
+||	XOR.L	$s[2],$Td1[2],$s[2]
+||	XOR.L	$s[3],$Td3[3],$s[3]
+;;====================================================================
+	ADD.D	${TEA},A0,${TEA}		; point to Td4
+||	ADD.D	${TEB},A0,${TEB}
+||	EXTU	$s[1],EXT3,24,$Td3[1]
+||	EXTU	$s[0],EXT1,24,$Td1[0]
+	LDBU	*${TEB}[$Td3[1]],$Td3[1]	; Td3[s1>>24],	t0
+||	LDBU	*${TEA}[$Td1[0]],$Td1[0]	; Td1[s0>>8],	t1
+||	XOR	$s[2],$Td0[2],$s[2]		; modulo-scheduled
+||	XOR	$s[3],$Td0[3],$s[3]		; modulo-scheduled
+||	EXTU	$s[0],EXT0,24,$Td0[0]
+||	EXTU	$s[1],EXT0,24,$Td0[1]
+	LDBU	*${TEA}[$Td0[0]],$Td0[0]	; Td0[s0],	t0
+||	LDBU	*${TEB}[$Td0[1]],$Td0[1]	; Td0[s1],	t1
+||	EXTU	$s[2],EXT2,24,$Td2[2]
+||	EXTU	$s[3],EXT2,24,$Td2[3]
+	LDBU	*${TEA}[$Td2[2]],$Td2[2]	; Td2[s2>>16],	t0
+||	LDBU	*${TEB}[$Td2[3]],$Td2[3]	; Td2[s3>>16],	t1
+||	EXTU	$s[3],EXT1,24,$Td1[3]
+||	EXTU	$s[2],EXT3,24,$Td3[2]
+	LDBU	*${TEB}[$Td1[3]],$Td1[3]	; Td1[s3>>8],	t0
+||	LDBU	*${TEA}[$Td3[2]],$Td3[2]	; Td3[s2>>24],	t1
+||	EXTU	$s[1],EXT1,24,$Td1[1]
+||	EXTU	$s[0],EXT3,24,$Td3[0]
+	LDBU	*${TEB}[$Td1[1]],$Td1[1]	; Td1[s1>>8],	t2
+||	LDBU	*${TEA}[$Td3[0]],$Td3[0]	; Td3[s0>>24],	t3
+||	EXTU	$s[0],EXT2,24,$Td2[0]
+||	EXTU	$s[1],EXT2,24,$Td2[1]
+	LDBU	*${TEA}[$Td2[0]],$Td2[0]	; Td2[s0>>16],	t2
+||	LDBU	*${TEB}[$Td2[1]],$Td2[1]	; Td2[s1>>16],	t3
+||	EXTU	$s[3],EXT3,24,$Td3[3]
+||	EXTU	$s[2],EXT1,24,$Td1[2]
+	LDBU	*${TEB}[$Td3[3]],$Td3[3]	; Td3[s3>>24],	t2
+||	LDBU	*${TEA}[$Td1[2]],$Td1[2]	; Td1[s2>>8],	t3
+||	EXTU	$s[2],EXT0,24,$Td0[2]
+||	EXTU	$s[3],EXT0,24,$Td0[3]
+	LDBU	*${TEA}[$Td0[2]],$Td0[2]	; Td0[s2],	t2
+||	LDBU	*${TEB}[$Td0[3]],$Td0[3]	; Td0[s3],	t3
+
+	.if	.BIG_ENDIAN
+	PACK2	$Td0[0],$Td1[3],$Td0[0]
+||	PACK2	$Td0[1],$Td1[0],$Td0[1]
+	PACK2	$Td2[2],$Td3[1],$Td2[2]
+||	PACK2	$Td2[3],$Td3[2],$Td2[3]
+	PACKL4	$Td0[0],$Td2[2],$Td0[0]
+||	PACKL4	$Td0[1],$Td2[3],$Td0[1]
+	XOR	$K[0],$Td0[0],$Td0[0]		; s[0]
+||	XOR	$K[1],$Td0[1],$Td0[1]		; s[1]
+
+	PACK2	$Td0[2],$Td1[1],$Td0[2]
+||	PACK2	$Td0[3],$Td1[2],$Td0[3]
+	PACK2	$Td2[0],$Td3[3],$Td2[0]
+||	PACK2	$Td2[1],$Td3[0],$Td2[1]
+||	BNOP	RA
+	PACKL4	$Td0[2],$Td2[0],$Td0[2]
+||	PACKL4	$Td0[3],$Td2[1],$Td0[3]
+	XOR	$K[2],$Td0[2],$Td0[2]		; s[2]
+||	XOR	$K[3],$Td0[3],$Td0[3]		; s[3]
+
+	MV	$Td0[0],A9
+||	MV	$Td0[1],A8
+	MV	$Td0[2],B9
+||	MV	$Td0[3],B8
+|| [B2]	STNDW	A9:A8,*OUT++
+   [B2]	STNDW	B9:B8,*OUT++
+	.else
+	PACK2	$Td1[3],$Td0[0],$Td1[3]
+||	PACK2	$Td1[0],$Td0[1],$Td1[0]
+	PACK2	$Td3[1],$Td2[2],$Td3[1]
+||	PACK2	$Td3[2],$Td2[3],$Td3[2]
+	PACKL4	$Td3[1],$Td1[3],$Td1[3]
+||	PACKL4	$Td3[2],$Td1[0],$Td1[0]
+	XOR	$K[0],$Td1[3],$Td1[3]		; s[0]
+||	XOR	$K[1],$Td1[0],$Td1[0]		; s[1]
+
+	PACK2	$Td1[1],$Td0[2],$Td1[1]
+||	PACK2	$Td1[2],$Td0[3],$Td1[2]
+	PACK2	$Td3[3],$Td2[0],$Td3[3]
+||	PACK2	$Td3[0],$Td2[1],$Td3[0]
+||	BNOP	RA
+	PACKL4	$Td3[3],$Td1[1],$Td1[1]
+||	PACKL4	$Td3[0],$Td1[2],$Td1[2]
+	XOR	$K[2],$Td1[1],$Td1[1]		; s[2]
+||	XOR	$K[3],$Td1[2],$Td1[2]		; s[3]
+
+	MV	$Td1[3],A8
+||	MV	$Td1[0],A9
+	MV	$Td1[1],B8
+||	MV	$Td1[2],B9
+|| [B2]	STNDW	A9:A8,*OUT++
+   [B2]	STNDW	B9:B8,*OUT++
+	.endif
+	.endasmfunc
+___
+{
+my @K=(@K,@s);			# extended key
+my @Te4=map("B$_",(16..19));
+
+my @Kx9=@Te0;			# used in AES_set_decrypt_key
+my @KxB=@Te1;
+my @KxD=@Te2;
+my @KxE=@Te3;
+
+$code.=<<___;
+	.asg	OUT,BITS
+
+	.global	_AES_set_encrypt_key
+_AES_set_encrypt_key:
+__set_encrypt_key:
+	.asmfunc
+	MV	INP,A0
+||	SHRU	BITS,5,BITS			; 128-192-256 -> 4-6-8
+||	MV	KEY,A1
+  [!A0]	B	RA
+||[!A0]	MVK	-1,RET
+||[!A0]	MVK	1,A1				; only one B RA
+  [!A1]	B	RA
+||[!A1]	MVK	-1,RET
+||[!A1]	MVK	0,A0
+||	MVK	0,B0
+||	MVK	0,A1
+   [A0]	LDNDW	*INP++,A9:A8
+|| [A0]	CMPEQ	4,BITS,B0
+|| [A0]	CMPLT	3,BITS,A1
+   [B0]	B	key128?
+|| [A1]	LDNDW	*INP++,B9:B8
+|| [A0]	CMPEQ	6,BITS,B0
+|| [A0]	CMPLT	5,BITS,A1
+   [B0]	B	key192?
+|| [A1]	LDNDW	*INP++,B17:B16
+|| [A0]	CMPEQ	8,BITS,B0
+|| [A0]	CMPLT	7,BITS,A1
+   [B0]	B	key256?
+|| [A1]	LDNDW	*INP++,B19:B18
+
+	.if	__TI_EABI__
+   [A0]	ADD	0,KEY,$KPA
+|| [A0]	ADD	4,KEY,$KPB
+|| [A0]	MVKL	\$PCR_OFFSET(AES_Te4,__set_encrypt_key),$TEA
+|| [A0]	ADDKPC	__set_encrypt_key,B6
+   [A0]	MVKH	\$PCR_OFFSET(AES_Te4,__set_encrypt_key),$TEA
+   [A0]	ADD	B6,$TEA,$TEA			; AES_Te4
+	.else
+   [A0]	ADD	0,KEY,$KPA
+|| [A0]	ADD	4,KEY,$KPB
+|| [A0]	MVKL	(AES_Te4-__set_encrypt_key),$TEA
+|| [A0]	ADDKPC	__set_encrypt_key,B6
+   [A0]	MVKH	(AES_Te4-__set_encrypt_key),$TEA
+   [A0]	ADD	B6,$TEA,$TEA			; AES_Te4
+	.endif
+	NOP
+	NOP
+
+	BNOP	RA,5
+||	MVK	-2,RET				; unknown bit length
+||	MVK	0,B0				; redundant
+;;====================================================================
+;;====================================================================
+key128?:
+	.if	.BIG_ENDIAN
+	MV	A9,$K[0]
+||	MV	A8,$K[1]
+||	MV	B9,$Te4[2]
+||	MV	B8,$K[3]
+	.else
+	MV	A8,$K[0]
+||	MV	A9,$K[1]
+||	MV	B8,$Te4[2]
+||	MV	B9,$K[3]
+	.endif
+
+	MVK	256,A0
+||	MVK	9,B0
+
+	SPLOOPD	14
+||	MVC	B0,ILC
+||	MV	$TEA,$TEB
+||	ADD	$TEA,A0,A30			; rcon
+;;====================================================================
+	LDW	*A30++[1],A31			; rcon[i]
+||	MV	$Te4[2],$K[2]
+||	EXTU	$K[3],EXT1,24,$Te4[0]
+	LDBU	*${TEB}[$Te4[0]],$Te4[0]
+||	MV	$K[3],A0
+||	EXTU	$K[3],EXT2,24,$Te4[1]
+	LDBU	*${TEB}[$Te4[1]],$Te4[1]
+||	EXTU	A0,EXT3,24,A0
+||	EXTU	$K[3],EXT0,24,$Te4[3]
+	.if	.BIG_ENDIAN
+	LDBU	*${TEA}[A0],$Te4[3]
+||	LDBU	*${TEB}[$Te4[3]],A0
+	.else
+	LDBU	*${TEA}[A0],A0
+||	LDBU	*${TEB}[$Te4[3]],$Te4[3]
+	.endif
+
+	STW	$K[0],*$KPA++[2]
+||	STW	$K[1],*$KPB++[2]
+	STW	$K[2],*$KPA++[2]
+||	STW	$K[3],*$KPB++[2]
+
+	XOR	A31,$K[0],$K[0]			; ^=rcon[i]
+	.if	.BIG_ENDIAN
+	PACK2	$Te4[0],$Te4[1],$Te4[1]
+	PACK2	$Te4[3],A0,$Te4[3]
+	PACKL4	$Te4[1],$Te4[3],$Te4[3]
+	.else
+	PACK2	$Te4[1],$Te4[0],$Te4[1]
+	PACK2	$Te4[3],A0,$Te4[3]
+	PACKL4	$Te4[3],$Te4[1],$Te4[3]
+	.endif
+	XOR	$Te4[3],$K[0],$Te4[0]		; K[0]
+	XOR	$Te4[0],$K[1],$K[1]		; K[1]
+	MV	$Te4[0],$K[0]
+||	XOR	$K[1],$K[2],$Te4[2]		; K[2]
+	XOR	$Te4[2],$K[3],$K[3]		; K[3]
+	SPKERNEL
+;;====================================================================
+	BNOP	RA
+	MV	$Te4[2],$K[2]
+||	STW	$K[0],*$KPA++[2]
+||	STW	$K[1],*$KPB++[2]
+	STW	$K[2],*$KPA++[2]
+||	STW	$K[3],*$KPB++[2]
+	MVK	10,B0				; rounds
+	STW	B0,*++${KPB}[15]
+	MVK	0,RET
+;;====================================================================
+;;====================================================================
+key192?:
+	.if	.BIG_ENDIAN
+	MV	A9,$K[0]
+||	MV	A8,$K[1]
+||	MV	B9,$K[2]
+||	MV	B8,$K[3]
+	MV	B17,$Te4[2]
+||	MV	B16,$K[5]
+	.else
+	MV	A8,$K[0]
+||	MV	A9,$K[1]
+||	MV	B8,$K[2]
+||	MV	B9,$K[3]
+	MV	B16,$Te4[2]
+||	MV	B17,$K[5]
+	.endif
+
+	MVK	256,A0
+||	MVK	6,B0
+	MV	$TEA,$TEB
+||	ADD	$TEA,A0,A30			; rcon
+;;====================================================================
+loop192?:
+	LDW	*A30++[1],A31			; rcon[i]
+||	MV	$Te4[2],$K[4]
+||	EXTU	$K[5],EXT1,24,$Te4[0]
+	LDBU	*${TEB}[$Te4[0]],$Te4[0]
+||	MV	$K[5],A0
+||	EXTU	$K[5],EXT2,24,$Te4[1]
+	LDBU	*${TEB}[$Te4[1]],$Te4[1]
+||	EXTU	A0,EXT3,24,A0
+||	EXTU	$K[5],EXT0,24,$Te4[3]
+	.if	.BIG_ENDIAN
+	LDBU	*${TEA}[A0],$Te4[3]
+||	LDBU	*${TEB}[$Te4[3]],A0
+	.else
+	LDBU	*${TEA}[A0],A0
+||	LDBU	*${TEB}[$Te4[3]],$Te4[3]
+	.endif
+
+	STW	$K[0],*$KPA++[2]
+||	STW	$K[1],*$KPB++[2]
+	STW	$K[2],*$KPA++[2]
+||	STW	$K[3],*$KPB++[2]
+	STW	$K[4],*$KPA++[2]
+||	STW	$K[5],*$KPB++[2]
+
+	XOR	A31,$K[0],$K[0]			; ^=rcon[i]
+	.if	.BIG_ENDIAN
+	PACK2	$Te4[0],$Te4[1],$Te4[1]
+||	PACK2	$Te4[3],A0,$Te4[3]
+	PACKL4	$Te4[1],$Te4[3],$Te4[3]
+	.else
+	PACK2	$Te4[1],$Te4[0],$Te4[1]
+||	PACK2	$Te4[3],A0,$Te4[3]
+	PACKL4	$Te4[3],$Te4[1],$Te4[3]
+	.endif
+	BDEC	loop192?,B0
+||	XOR	$Te4[3],$K[0],$Te4[0]		; K[0]
+	XOR	$Te4[0],$K[1],$K[1]		; K[1]
+	MV	$Te4[0],$K[0]
+||	XOR	$K[1],$K[2],$Te4[2]		; K[2]
+	XOR	$Te4[2],$K[3],$K[3]		; K[3]
+	MV	$Te4[2],$K[2]
+||	XOR	$K[3],$K[4],$Te4[2]		; K[4]
+	XOR	$Te4[2],$K[5],$K[5]		; K[5]
+;;====================================================================
+	BNOP	RA
+	STW	$K[0],*$KPA++[2]
+||	STW	$K[1],*$KPB++[2]
+	STW	$K[2],*$KPA++[2]
+||	STW	$K[3],*$KPB++[2]
+	MVK	12,B0				; rounds
+	STW	B0,*++${KPB}[7]
+	MVK	0,RET
+;;====================================================================
+;;====================================================================
+key256?:
+	.if	.BIG_ENDIAN
+	MV	A9,$K[0]
+||	MV	A8,$K[1]
+||	MV	B9,$K[2]
+||	MV	B8,$K[3]
+	MV	B17,$K[4]
+||	MV	B16,$K[5]
+||	MV	B19,$Te4[2]
+||	MV	B18,$K[7]
+	.else
+	MV	A8,$K[0]
+||	MV	A9,$K[1]
+||	MV	B8,$K[2]
+||	MV	B9,$K[3]
+	MV	B16,$K[4]
+||	MV	B17,$K[5]
+||	MV	B18,$Te4[2]
+||	MV	B19,$K[7]
+	.endif
+
+	MVK	256,A0
+||	MVK	6,B0
+	MV	$TEA,$TEB
+||	ADD	$TEA,A0,A30			; rcon
+;;====================================================================
+loop256?:
+	LDW	*A30++[1],A31			; rcon[i]
+||	MV	$Te4[2],$K[6]
+||	EXTU	$K[7],EXT1,24,$Te4[0]
+	LDBU	*${TEB}[$Te4[0]],$Te4[0]
+||	MV	$K[7],A0
+||	EXTU	$K[7],EXT2,24,$Te4[1]
+	LDBU	*${TEB}[$Te4[1]],$Te4[1]
+||	EXTU	A0,EXT3,24,A0
+||	EXTU	$K[7],EXT0,24,$Te4[3]
+	.if	.BIG_ENDIAN
+	LDBU	*${TEA}[A0],$Te4[3]
+||	LDBU	*${TEB}[$Te4[3]],A0
+	.else
+	LDBU	*${TEA}[A0],A0
+||	LDBU	*${TEB}[$Te4[3]],$Te4[3]
+	.endif
+
+	STW	$K[0],*$KPA++[2]
+||	STW	$K[1],*$KPB++[2]
+	STW	$K[2],*$KPA++[2]
+||	STW	$K[3],*$KPB++[2]
+	STW	$K[4],*$KPA++[2]
+||	STW	$K[5],*$KPB++[2]
+	STW	$K[6],*$KPA++[2]
+||	STW	$K[7],*$KPB++[2]
+||	XOR	A31,$K[0],$K[0]			; ^=rcon[i]
+	.if	.BIG_ENDIAN
+	PACK2	$Te4[0],$Te4[1],$Te4[1]
+||	PACK2	$Te4[3],A0,$Te4[3]
+	PACKL4	$Te4[1],$Te4[3],$Te4[3]
+||[!B0]	B	done256?
+	.else
+	PACK2	$Te4[1],$Te4[0],$Te4[1]
+||	PACK2	$Te4[3],A0,$Te4[3]
+	PACKL4	$Te4[3],$Te4[1],$Te4[3]
+||[!B0]	B	done256?
+	.endif
+	XOR	$Te4[3],$K[0],$Te4[0]		; K[0]
+	XOR	$Te4[0],$K[1],$K[1]		; K[1]
+	MV	$Te4[0],$K[0]
+||	XOR	$K[1],$K[2],$Te4[2]		; K[2]
+	XOR	$Te4[2],$K[3],$K[3]		; K[3]
+
+	MV	$Te4[2],$K[2]
+|| [B0]	EXTU	$K[3],EXT0,24,$Te4[0]
+|| [B0]	SUB	B0,1,B0
+	LDBU	*${TEB}[$Te4[0]],$Te4[0]
+||	MV	$K[3],A0
+||	EXTU	$K[3],EXT1,24,$Te4[1]
+	LDBU	*${TEB}[$Te4[1]],$Te4[1]
+||	EXTU	A0,EXT2,24,A0
+||	EXTU	$K[3],EXT3,24,$Te4[3]
+
+	.if	.BIG_ENDIAN
+	LDBU	*${TEA}[A0],$Te4[3]
+||	LDBU	*${TEB}[$Te4[3]],A0
+	NOP	3
+	PACK2	$Te4[0],$Te4[1],$Te4[1]
+	PACK2	$Te4[3],A0,$Te4[3]
+||	B	loop256?
+	PACKL4	$Te4[1],$Te4[3],$Te4[3]
+	.else
+	LDBU	*${TEA}[A0],A0
+||	LDBU	*${TEB}[$Te4[3]],$Te4[3]
+	NOP	3
+	PACK2	$Te4[1],$Te4[0],$Te4[1]
+	PACK2	$Te4[3],A0,$Te4[3]
+||	B	loop256?
+	PACKL4	$Te4[3],$Te4[1],$Te4[3]
+	.endif
+
+	XOR	$Te4[3],$K[4],$Te4[0]		; K[4]
+	XOR	$Te4[0],$K[5],$K[5]		; K[5]
+	MV	$Te4[0],$K[4]
+||	XOR	$K[5],$K[6],$Te4[2]		; K[6]
+	XOR	$Te4[2],$K[7],$K[7]		; K[7]
+;;====================================================================
+done256?:
+	BNOP	RA
+	STW	$K[0],*$KPA++[2]
+||	STW	$K[1],*$KPB++[2]
+	STW	$K[2],*$KPA++[2]
+||	STW	$K[3],*$KPB++[2]
+	MVK	14,B0				; rounds
+	STW	B0,*--${KPB}[1]
+	MVK	0,RET
+	.endasmfunc
+
+	.global	_AES_set_decrypt_key
+_AES_set_decrypt_key:
+	.asmfunc
+	B	__set_encrypt_key		; guarantee local call
+	MV	KEY,B30				; B30 is not modified
+	MV	RA, B31				; B31 is not modified
+	ADDKPC	ret?,RA,2
+ret?:						; B0 holds rounds or zero
+  [!B0]	BNOP	B31				; return if zero
+   [B0]	SHL	B0,4,A0				; offset to last round key
+   [B0]	SHRU	B0,1,B1
+   [B0]	SUB	B1,1,B1
+   [B0]	MVK	0x0000001B,B3			; AES polynomial
+   [B0]	MVKH	0x07000000,B3
+
+	SPLOOPD	9				; flip round keys
+||	MVC	B1,ILC
+||	MV	B30,$KPA
+||	ADD	B30,A0,$KPB
+||	MVK	16,A0				; sizeof(round key)
+;;====================================================================
+	LDW	*${KPA}[0],A16
+||	LDW	*${KPB}[0],B16
+	LDW	*${KPA}[1],A17
+||	LDW	*${KPB}[1],B17
+	LDW	*${KPA}[2],A18
+||	LDW	*${KPB}[2],B18
+	LDW	*${KPA}[3],A19
+||	ADD	$KPA,A0,$KPA
+||	LDW	*${KPB}[3],B19
+||	SUB	$KPB,A0,$KPB
+	NOP
+	STW	B16,*${KPA}[-4]
+||	STW	A16,*${KPB}[4]
+	STW	B17,*${KPA}[-3]
+||	STW	A17,*${KPB}[5]
+	STW	B18,*${KPA}[-2]
+||	STW	A18,*${KPB}[6]
+	STW	B19,*${KPA}[-1]
+||	STW	A19,*${KPB}[7]
+	SPKERNEL
+;;====================================================================
+	SUB	B0,1,B0				; skip last round
+||	ADD	B30,A0,$KPA			; skip first round
+||	ADD	B30,A0,$KPB
+||	MVC	GFPGFR,B30			; save GFPGFR
+	LDW	*${KPA}[0],$K[0]
+||	LDW	*${KPB}[1],$K[1]
+||	MVC	B3,GFPGFR
+	LDW	*${KPA}[2],$K[2]
+||	LDW	*${KPB}[3],$K[3]
+	MVK	0x00000909,A24
+||	MVK	0x00000B0B,B24
+	MVKH	0x09090000,A24
+||	MVKH	0x0B0B0000,B24
+	MVC	B0,ILC
+||	SUB	B0,1,B0
+
+	GMPY4	$K[0],A24,$Kx9[0]		; ·0x09
+||	GMPY4	$K[1],A24,$Kx9[1]
+||	MVK	0x00000D0D,A25
+||	MVK	0x00000E0E,B25
+	GMPY4	$K[2],A24,$Kx9[2]
+||	GMPY4	$K[3],A24,$Kx9[3]
+||	MVKH	0x0D0D0000,A25
+||	MVKH	0x0E0E0000,B25
+
+	GMPY4	$K[0],B24,$KxB[0]		; ·0x0B
+||	GMPY4	$K[1],B24,$KxB[1]
+	GMPY4	$K[2],B24,$KxB[2]
+||	GMPY4	$K[3],B24,$KxB[3]
+
+	SPLOOP	11				; InvMixColumns
+;;====================================================================
+	GMPY4	$K[0],A25,$KxD[0]		; ·0x0D
+||	GMPY4	$K[1],A25,$KxD[1]
+||	SWAP2	$Kx9[0],$Kx9[0]			; rotate by 16
+||	SWAP2	$Kx9[1],$Kx9[1]
+||	MV	$K[0],$s[0]			; this or DINT
+||	MV	$K[1],$s[1]
+|| [B0]	LDW	*${KPA}[4],$K[0]
+|| [B0]	LDW	*${KPB}[5],$K[1]
+	GMPY4	$K[2],A25,$KxD[2]
+||	GMPY4	$K[3],A25,$KxD[3]
+||	SWAP2	$Kx9[2],$Kx9[2]
+||	SWAP2	$Kx9[3],$Kx9[3]
+||	MV	$K[2],$s[2]
+||	MV	$K[3],$s[3]
+|| [B0]	LDW	*${KPA}[6],$K[2]
+|| [B0]	LDW	*${KPB}[7],$K[3]
+
+	GMPY4	$s[0],B25,$KxE[0]		; ·0x0E
+||	GMPY4	$s[1],B25,$KxE[1]
+||	XOR	$Kx9[0],$KxB[0],$KxB[0]
+||	XOR	$Kx9[1],$KxB[1],$KxB[1]
+	GMPY4	$s[2],B25,$KxE[2]
+||	GMPY4	$s[3],B25,$KxE[3]
+||	XOR	$Kx9[2],$KxB[2],$KxB[2]
+||	XOR	$Kx9[3],$KxB[3],$KxB[3]
+
+	ROTL	$KxB[0],TBL3,$KxB[0]
+||	ROTL	$KxB[1],TBL3,$KxB[1]
+||	SWAP2	$KxD[0],$KxD[0]			; rotate by 16
+||	SWAP2	$KxD[1],$KxD[1]
+	ROTL	$KxB[2],TBL3,$KxB[2]
+||	ROTL	$KxB[3],TBL3,$KxB[3]
+||	SWAP2	$KxD[2],$KxD[2]
+||	SWAP2	$KxD[3],$KxD[3]
+
+	XOR	$KxE[0],$KxD[0],$KxE[0]
+||	XOR	$KxE[1],$KxD[1],$KxE[1]
+|| [B0]	GMPY4	$K[0],A24,$Kx9[0]		; ·0x09
+|| [B0]	GMPY4	$K[1],A24,$Kx9[1]
+||	ADDAW	$KPA,4,$KPA
+	XOR	$KxE[2],$KxD[2],$KxE[2]
+||	XOR	$KxE[3],$KxD[3],$KxE[3]
+|| [B0]	GMPY4	$K[2],A24,$Kx9[2]
+|| [B0]	GMPY4	$K[3],A24,$Kx9[3]
+||	ADDAW	$KPB,4,$KPB
+
+	XOR	$KxB[0],$KxE[0],$KxE[0]
+||	XOR	$KxB[1],$KxE[1],$KxE[1]
+|| [B0]	GMPY4	$K[0],B24,$KxB[0]		; ·0x0B
+|| [B0]	GMPY4	$K[1],B24,$KxB[1]
+	XOR	$KxB[2],$KxE[2],$KxE[2]
+||	XOR	$KxB[3],$KxE[3],$KxE[3]
+|| [B0]	GMPY4	$K[2],B24,$KxB[2]
+|| [B0]	GMPY4	$K[3],B24,$KxB[3]
+||	STW	$KxE[0],*${KPA}[-4]
+||	STW	$KxE[1],*${KPB}[-3]
+	STW	$KxE[2],*${KPA}[-2]
+||	STW	$KxE[3],*${KPB}[-1]
+|| [B0]	SUB	B0,1,B0
+	SPKERNEL
+;;====================================================================
+	BNOP	B31,3
+	MVC	B30,GFPGFR			; restore GFPGFR(*)
+	MVK	0,RET
+	.endasmfunc
+___
+# (*)	Even though ABI doesn't specify GFPGFR as non-volatile, there
+#	are code samples out there that *assume* its default value.
+}
+{
+my ($inp,$out,$blocks,$key,$ivp)=("A4","B4","A6","B6","A8");
+$code.=<<___;
+	.global	_AES_ctr32_encrypt
+_AES_ctr32_encrypt:
+	.asmfunc
+	LDNDW	*${ivp}[0],A31:A30	; load counter value
+||	MV	$blocks,A2		; reassign $blocks
+||	DMV	RA,$key,B27:B26		; reassign RA and $key
+	LDNDW	*${ivp}[1],B31:B30
+||	MVK	0,B2			; don't let __encrypt load input
+||	MVK	0,A1			; and postpone writing output
+	.if	.BIG_ENDIAN
+	NOP
+	.else
+	NOP	4
+	SWAP2	B31,B31			; keep least significant 32 bits
+	SWAP4	B31,B31			; in host byte order
+	.endif
+ctr32_loop?:
+   [A2]	BNOP	__encrypt
+|| [A1]	XOR	A29,A9,A9		; input^Ek(counter)
+|| [A1]	XOR	A28,A8,A8
+|| [A2]	LDNDW	*INP++,A29:A28		; load input
+  [!A2]	BNOP	B27			; return
+|| [A1]	XOR	B29,B9,B9
+|| [A1]	XOR	B28,B8,B8
+|| [A2]	LDNDW	*INP++,B29:B28
+	.if	.BIG_ENDIAN
+   [A1]	STNDW	A9:A8,*OUT++		; save output
+|| [A2]	DMV	A31,A30,A9:A8		; pass counter value to __encrypt
+   [A1]	STNDW	B9:B8,*OUT++
+|| [A2]	DMV	B31,B30,B9:B8
+|| [A2]	ADD	B30,1,B30		; counter++
+	.else
+   [A1]	STNDW	A9:A8,*OUT++		; save output
+|| [A2]	DMV	A31,A30,A9:A8
+|| [A2]	SWAP2	B31,B0
+|| [A2]	ADD	B31,1,B31		; counter++
+   [A1]	STNDW	B9:B8,*OUT++
+|| [A2]	MV	B30,B8
+|| [A2]	SWAP4	B0,B9
+	.endif
+   [A2]	ADDKPC	ctr32_loop?,RA		; return to ctr32_loop?
+|| [A2]	MV	B26,KEY			; pass $key
+|| [A2]	SUB	A2,1,A2			; $blocks--
+||[!A1]	MVK	1,A1
+	NOP
+	NOP
+	.endasmfunc
+___
+}
+# Tables are kept in endian-neutral manner
+$code.=<<___;
+	.if	__TI_EABI__
+	.sect	".text:aes_asm.const"
+	.else
+	.sect	".const:aes_asm"
+	.endif
+	.align	128
+AES_Te:
+	.byte	0xc6,0x63,0x63,0xa5,	0xf8,0x7c,0x7c,0x84
+	.byte	0xee,0x77,0x77,0x99,	0xf6,0x7b,0x7b,0x8d
+	.byte	0xff,0xf2,0xf2,0x0d,	0xd6,0x6b,0x6b,0xbd
+	.byte	0xde,0x6f,0x6f,0xb1,	0x91,0xc5,0xc5,0x54
+	.byte	0x60,0x30,0x30,0x50,	0x02,0x01,0x01,0x03
+	.byte	0xce,0x67,0x67,0xa9,	0x56,0x2b,0x2b,0x7d
+	.byte	0xe7,0xfe,0xfe,0x19,	0xb5,0xd7,0xd7,0x62
+	.byte	0x4d,0xab,0xab,0xe6,	0xec,0x76,0x76,0x9a
+	.byte	0x8f,0xca,0xca,0x45,	0x1f,0x82,0x82,0x9d
+	.byte	0x89,0xc9,0xc9,0x40,	0xfa,0x7d,0x7d,0x87
+	.byte	0xef,0xfa,0xfa,0x15,	0xb2,0x59,0x59,0xeb
+	.byte	0x8e,0x47,0x47,0xc9,	0xfb,0xf0,0xf0,0x0b
+	.byte	0x41,0xad,0xad,0xec,	0xb3,0xd4,0xd4,0x67
+	.byte	0x5f,0xa2,0xa2,0xfd,	0x45,0xaf,0xaf,0xea
+	.byte	0x23,0x9c,0x9c,0xbf,	0x53,0xa4,0xa4,0xf7
+	.byte	0xe4,0x72,0x72,0x96,	0x9b,0xc0,0xc0,0x5b
+	.byte	0x75,0xb7,0xb7,0xc2,	0xe1,0xfd,0xfd,0x1c
+	.byte	0x3d,0x93,0x93,0xae,	0x4c,0x26,0x26,0x6a
+	.byte	0x6c,0x36,0x36,0x5a,	0x7e,0x3f,0x3f,0x41
+	.byte	0xf5,0xf7,0xf7,0x02,	0x83,0xcc,0xcc,0x4f
+	.byte	0x68,0x34,0x34,0x5c,	0x51,0xa5,0xa5,0xf4
+	.byte	0xd1,0xe5,0xe5,0x34,	0xf9,0xf1,0xf1,0x08
+	.byte	0xe2,0x71,0x71,0x93,	0xab,0xd8,0xd8,0x73
+	.byte	0x62,0x31,0x31,0x53,	0x2a,0x15,0x15,0x3f
+	.byte	0x08,0x04,0x04,0x0c,	0x95,0xc7,0xc7,0x52
+	.byte	0x46,0x23,0x23,0x65,	0x9d,0xc3,0xc3,0x5e
+	.byte	0x30,0x18,0x18,0x28,	0x37,0x96,0x96,0xa1
+	.byte	0x0a,0x05,0x05,0x0f,	0x2f,0x9a,0x9a,0xb5
+	.byte	0x0e,0x07,0x07,0x09,	0x24,0x12,0x12,0x36
+	.byte	0x1b,0x80,0x80,0x9b,	0xdf,0xe2,0xe2,0x3d
+	.byte	0xcd,0xeb,0xeb,0x26,	0x4e,0x27,0x27,0x69
+	.byte	0x7f,0xb2,0xb2,0xcd,	0xea,0x75,0x75,0x9f
+	.byte	0x12,0x09,0x09,0x1b,	0x1d,0x83,0x83,0x9e
+	.byte	0x58,0x2c,0x2c,0x74,	0x34,0x1a,0x1a,0x2e
+	.byte	0x36,0x1b,0x1b,0x2d,	0xdc,0x6e,0x6e,0xb2
+	.byte	0xb4,0x5a,0x5a,0xee,	0x5b,0xa0,0xa0,0xfb
+	.byte	0xa4,0x52,0x52,0xf6,	0x76,0x3b,0x3b,0x4d
+	.byte	0xb7,0xd6,0xd6,0x61,	0x7d,0xb3,0xb3,0xce
+	.byte	0x52,0x29,0x29,0x7b,	0xdd,0xe3,0xe3,0x3e
+	.byte	0x5e,0x2f,0x2f,0x71,	0x13,0x84,0x84,0x97
+	.byte	0xa6,0x53,0x53,0xf5,	0xb9,0xd1,0xd1,0x68
+	.byte	0x00,0x00,0x00,0x00,	0xc1,0xed,0xed,0x2c
+	.byte	0x40,0x20,0x20,0x60,	0xe3,0xfc,0xfc,0x1f
+	.byte	0x79,0xb1,0xb1,0xc8,	0xb6,0x5b,0x5b,0xed
+	.byte	0xd4,0x6a,0x6a,0xbe,	0x8d,0xcb,0xcb,0x46
+	.byte	0x67,0xbe,0xbe,0xd9,	0x72,0x39,0x39,0x4b
+	.byte	0x94,0x4a,0x4a,0xde,	0x98,0x4c,0x4c,0xd4
+	.byte	0xb0,0x58,0x58,0xe8,	0x85,0xcf,0xcf,0x4a
+	.byte	0xbb,0xd0,0xd0,0x6b,	0xc5,0xef,0xef,0x2a
+	.byte	0x4f,0xaa,0xaa,0xe5,	0xed,0xfb,0xfb,0x16
+	.byte	0x86,0x43,0x43,0xc5,	0x9a,0x4d,0x4d,0xd7
+	.byte	0x66,0x33,0x33,0x55,	0x11,0x85,0x85,0x94
+	.byte	0x8a,0x45,0x45,0xcf,	0xe9,0xf9,0xf9,0x10
+	.byte	0x04,0x02,0x02,0x06,	0xfe,0x7f,0x7f,0x81
+	.byte	0xa0,0x50,0x50,0xf0,	0x78,0x3c,0x3c,0x44
+	.byte	0x25,0x9f,0x9f,0xba,	0x4b,0xa8,0xa8,0xe3
+	.byte	0xa2,0x51,0x51,0xf3,	0x5d,0xa3,0xa3,0xfe
+	.byte	0x80,0x40,0x40,0xc0,	0x05,0x8f,0x8f,0x8a
+	.byte	0x3f,0x92,0x92,0xad,	0x21,0x9d,0x9d,0xbc
+	.byte	0x70,0x38,0x38,0x48,	0xf1,0xf5,0xf5,0x04
+	.byte	0x63,0xbc,0xbc,0xdf,	0x77,0xb6,0xb6,0xc1
+	.byte	0xaf,0xda,0xda,0x75,	0x42,0x21,0x21,0x63
+	.byte	0x20,0x10,0x10,0x30,	0xe5,0xff,0xff,0x1a
+	.byte	0xfd,0xf3,0xf3,0x0e,	0xbf,0xd2,0xd2,0x6d
+	.byte	0x81,0xcd,0xcd,0x4c,	0x18,0x0c,0x0c,0x14
+	.byte	0x26,0x13,0x13,0x35,	0xc3,0xec,0xec,0x2f
+	.byte	0xbe,0x5f,0x5f,0xe1,	0x35,0x97,0x97,0xa2
+	.byte	0x88,0x44,0x44,0xcc,	0x2e,0x17,0x17,0x39
+	.byte	0x93,0xc4,0xc4,0x57,	0x55,0xa7,0xa7,0xf2
+	.byte	0xfc,0x7e,0x7e,0x82,	0x7a,0x3d,0x3d,0x47
+	.byte	0xc8,0x64,0x64,0xac,	0xba,0x5d,0x5d,0xe7
+	.byte	0x32,0x19,0x19,0x2b,	0xe6,0x73,0x73,0x95
+	.byte	0xc0,0x60,0x60,0xa0,	0x19,0x81,0x81,0x98
+	.byte	0x9e,0x4f,0x4f,0xd1,	0xa3,0xdc,0xdc,0x7f
+	.byte	0x44,0x22,0x22,0x66,	0x54,0x2a,0x2a,0x7e
+	.byte	0x3b,0x90,0x90,0xab,	0x0b,0x88,0x88,0x83
+	.byte	0x8c,0x46,0x46,0xca,	0xc7,0xee,0xee,0x29
+	.byte	0x6b,0xb8,0xb8,0xd3,	0x28,0x14,0x14,0x3c
+	.byte	0xa7,0xde,0xde,0x79,	0xbc,0x5e,0x5e,0xe2
+	.byte	0x16,0x0b,0x0b,0x1d,	0xad,0xdb,0xdb,0x76
+	.byte	0xdb,0xe0,0xe0,0x3b,	0x64,0x32,0x32,0x56
+	.byte	0x74,0x3a,0x3a,0x4e,	0x14,0x0a,0x0a,0x1e
+	.byte	0x92,0x49,0x49,0xdb,	0x0c,0x06,0x06,0x0a
+	.byte	0x48,0x24,0x24,0x6c,	0xb8,0x5c,0x5c,0xe4
+	.byte	0x9f,0xc2,0xc2,0x5d,	0xbd,0xd3,0xd3,0x6e
+	.byte	0x43,0xac,0xac,0xef,	0xc4,0x62,0x62,0xa6
+	.byte	0x39,0x91,0x91,0xa8,	0x31,0x95,0x95,0xa4
+	.byte	0xd3,0xe4,0xe4,0x37,	0xf2,0x79,0x79,0x8b
+	.byte	0xd5,0xe7,0xe7,0x32,	0x8b,0xc8,0xc8,0x43
+	.byte	0x6e,0x37,0x37,0x59,	0xda,0x6d,0x6d,0xb7
+	.byte	0x01,0x8d,0x8d,0x8c,	0xb1,0xd5,0xd5,0x64
+	.byte	0x9c,0x4e,0x4e,0xd2,	0x49,0xa9,0xa9,0xe0
+	.byte	0xd8,0x6c,0x6c,0xb4,	0xac,0x56,0x56,0xfa
+	.byte	0xf3,0xf4,0xf4,0x07,	0xcf,0xea,0xea,0x25
+	.byte	0xca,0x65,0x65,0xaf,	0xf4,0x7a,0x7a,0x8e
+	.byte	0x47,0xae,0xae,0xe9,	0x10,0x08,0x08,0x18
+	.byte	0x6f,0xba,0xba,0xd5,	0xf0,0x78,0x78,0x88
+	.byte	0x4a,0x25,0x25,0x6f,	0x5c,0x2e,0x2e,0x72
+	.byte	0x38,0x1c,0x1c,0x24,	0x57,0xa6,0xa6,0xf1
+	.byte	0x73,0xb4,0xb4,0xc7,	0x97,0xc6,0xc6,0x51
+	.byte	0xcb,0xe8,0xe8,0x23,	0xa1,0xdd,0xdd,0x7c
+	.byte	0xe8,0x74,0x74,0x9c,	0x3e,0x1f,0x1f,0x21
+	.byte	0x96,0x4b,0x4b,0xdd,	0x61,0xbd,0xbd,0xdc
+	.byte	0x0d,0x8b,0x8b,0x86,	0x0f,0x8a,0x8a,0x85
+	.byte	0xe0,0x70,0x70,0x90,	0x7c,0x3e,0x3e,0x42
+	.byte	0x71,0xb5,0xb5,0xc4,	0xcc,0x66,0x66,0xaa
+	.byte	0x90,0x48,0x48,0xd8,	0x06,0x03,0x03,0x05
+	.byte	0xf7,0xf6,0xf6,0x01,	0x1c,0x0e,0x0e,0x12
+	.byte	0xc2,0x61,0x61,0xa3,	0x6a,0x35,0x35,0x5f
+	.byte	0xae,0x57,0x57,0xf9,	0x69,0xb9,0xb9,0xd0
+	.byte	0x17,0x86,0x86,0x91,	0x99,0xc1,0xc1,0x58
+	.byte	0x3a,0x1d,0x1d,0x27,	0x27,0x9e,0x9e,0xb9
+	.byte	0xd9,0xe1,0xe1,0x38,	0xeb,0xf8,0xf8,0x13
+	.byte	0x2b,0x98,0x98,0xb3,	0x22,0x11,0x11,0x33
+	.byte	0xd2,0x69,0x69,0xbb,	0xa9,0xd9,0xd9,0x70
+	.byte	0x07,0x8e,0x8e,0x89,	0x33,0x94,0x94,0xa7
+	.byte	0x2d,0x9b,0x9b,0xb6,	0x3c,0x1e,0x1e,0x22
+	.byte	0x15,0x87,0x87,0x92,	0xc9,0xe9,0xe9,0x20
+	.byte	0x87,0xce,0xce,0x49,	0xaa,0x55,0x55,0xff
+	.byte	0x50,0x28,0x28,0x78,	0xa5,0xdf,0xdf,0x7a
+	.byte	0x03,0x8c,0x8c,0x8f,	0x59,0xa1,0xa1,0xf8
+	.byte	0x09,0x89,0x89,0x80,	0x1a,0x0d,0x0d,0x17
+	.byte	0x65,0xbf,0xbf,0xda,	0xd7,0xe6,0xe6,0x31
+	.byte	0x84,0x42,0x42,0xc6,	0xd0,0x68,0x68,0xb8
+	.byte	0x82,0x41,0x41,0xc3,	0x29,0x99,0x99,0xb0
+	.byte	0x5a,0x2d,0x2d,0x77,	0x1e,0x0f,0x0f,0x11
+	.byte	0x7b,0xb0,0xb0,0xcb,	0xa8,0x54,0x54,0xfc
+	.byte	0x6d,0xbb,0xbb,0xd6,	0x2c,0x16,0x16,0x3a
+AES_Te4:
+	.byte	0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5
+	.byte	0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76
+	.byte	0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0
+	.byte	0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0
+	.byte	0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc
+	.byte	0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15
+	.byte	0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a
+	.byte	0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75
+	.byte	0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0
+	.byte	0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84
+	.byte	0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b
+	.byte	0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf
+	.byte	0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85
+	.byte	0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8
+	.byte	0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5
+	.byte	0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2
+	.byte	0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17
+	.byte	0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73
+	.byte	0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88
+	.byte	0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb
+	.byte	0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c
+	.byte	0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79
+	.byte	0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9
+	.byte	0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08
+	.byte	0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6
+	.byte	0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a
+	.byte	0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e
+	.byte	0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e
+	.byte	0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94
+	.byte	0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf
+	.byte	0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68
+	.byte	0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16
+rcon:
+	.byte	0x01,0x00,0x00,0x00,	0x02,0x00,0x00,0x00
+	.byte	0x04,0x00,0x00,0x00,	0x08,0x00,0x00,0x00
+	.byte	0x10,0x00,0x00,0x00,	0x20,0x00,0x00,0x00
+	.byte	0x40,0x00,0x00,0x00,	0x80,0x00,0x00,0x00
+	.byte	0x1B,0x00,0x00,0x00,	0x36,0x00,0x00,0x00
+	.align	128
+AES_Td:
+	.byte	0x51,0xf4,0xa7,0x50,	0x7e,0x41,0x65,0x53
+	.byte	0x1a,0x17,0xa4,0xc3,	0x3a,0x27,0x5e,0x96
+	.byte	0x3b,0xab,0x6b,0xcb,	0x1f,0x9d,0x45,0xf1
+	.byte	0xac,0xfa,0x58,0xab,	0x4b,0xe3,0x03,0x93
+	.byte	0x20,0x30,0xfa,0x55,	0xad,0x76,0x6d,0xf6
+	.byte	0x88,0xcc,0x76,0x91,	0xf5,0x02,0x4c,0x25
+	.byte	0x4f,0xe5,0xd7,0xfc,	0xc5,0x2a,0xcb,0xd7
+	.byte	0x26,0x35,0x44,0x80,	0xb5,0x62,0xa3,0x8f
+	.byte	0xde,0xb1,0x5a,0x49,	0x25,0xba,0x1b,0x67
+	.byte	0x45,0xea,0x0e,0x98,	0x5d,0xfe,0xc0,0xe1
+	.byte	0xc3,0x2f,0x75,0x02,	0x81,0x4c,0xf0,0x12
+	.byte	0x8d,0x46,0x97,0xa3,	0x6b,0xd3,0xf9,0xc6
+	.byte	0x03,0x8f,0x5f,0xe7,	0x15,0x92,0x9c,0x95
+	.byte	0xbf,0x6d,0x7a,0xeb,	0x95,0x52,0x59,0xda
+	.byte	0xd4,0xbe,0x83,0x2d,	0x58,0x74,0x21,0xd3
+	.byte	0x49,0xe0,0x69,0x29,	0x8e,0xc9,0xc8,0x44
+	.byte	0x75,0xc2,0x89,0x6a,	0xf4,0x8e,0x79,0x78
+	.byte	0x99,0x58,0x3e,0x6b,	0x27,0xb9,0x71,0xdd
+	.byte	0xbe,0xe1,0x4f,0xb6,	0xf0,0x88,0xad,0x17
+	.byte	0xc9,0x20,0xac,0x66,	0x7d,0xce,0x3a,0xb4
+	.byte	0x63,0xdf,0x4a,0x18,	0xe5,0x1a,0x31,0x82
+	.byte	0x97,0x51,0x33,0x60,	0x62,0x53,0x7f,0x45
+	.byte	0xb1,0x64,0x77,0xe0,	0xbb,0x6b,0xae,0x84
+	.byte	0xfe,0x81,0xa0,0x1c,	0xf9,0x08,0x2b,0x94
+	.byte	0x70,0x48,0x68,0x58,	0x8f,0x45,0xfd,0x19
+	.byte	0x94,0xde,0x6c,0x87,	0x52,0x7b,0xf8,0xb7
+	.byte	0xab,0x73,0xd3,0x23,	0x72,0x4b,0x02,0xe2
+	.byte	0xe3,0x1f,0x8f,0x57,	0x66,0x55,0xab,0x2a
+	.byte	0xb2,0xeb,0x28,0x07,	0x2f,0xb5,0xc2,0x03
+	.byte	0x86,0xc5,0x7b,0x9a,	0xd3,0x37,0x08,0xa5
+	.byte	0x30,0x28,0x87,0xf2,	0x23,0xbf,0xa5,0xb2
+	.byte	0x02,0x03,0x6a,0xba,	0xed,0x16,0x82,0x5c
+	.byte	0x8a,0xcf,0x1c,0x2b,	0xa7,0x79,0xb4,0x92
+	.byte	0xf3,0x07,0xf2,0xf0,	0x4e,0x69,0xe2,0xa1
+	.byte	0x65,0xda,0xf4,0xcd,	0x06,0x05,0xbe,0xd5
+	.byte	0xd1,0x34,0x62,0x1f,	0xc4,0xa6,0xfe,0x8a
+	.byte	0x34,0x2e,0x53,0x9d,	0xa2,0xf3,0x55,0xa0
+	.byte	0x05,0x8a,0xe1,0x32,	0xa4,0xf6,0xeb,0x75
+	.byte	0x0b,0x83,0xec,0x39,	0x40,0x60,0xef,0xaa
+	.byte	0x5e,0x71,0x9f,0x06,	0xbd,0x6e,0x10,0x51
+	.byte	0x3e,0x21,0x8a,0xf9,	0x96,0xdd,0x06,0x3d
+	.byte	0xdd,0x3e,0x05,0xae,	0x4d,0xe6,0xbd,0x46
+	.byte	0x91,0x54,0x8d,0xb5,	0x71,0xc4,0x5d,0x05
+	.byte	0x04,0x06,0xd4,0x6f,	0x60,0x50,0x15,0xff
+	.byte	0x19,0x98,0xfb,0x24,	0xd6,0xbd,0xe9,0x97
+	.byte	0x89,0x40,0x43,0xcc,	0x67,0xd9,0x9e,0x77
+	.byte	0xb0,0xe8,0x42,0xbd,	0x07,0x89,0x8b,0x88
+	.byte	0xe7,0x19,0x5b,0x38,	0x79,0xc8,0xee,0xdb
+	.byte	0xa1,0x7c,0x0a,0x47,	0x7c,0x42,0x0f,0xe9
+	.byte	0xf8,0x84,0x1e,0xc9,	0x00,0x00,0x00,0x00
+	.byte	0x09,0x80,0x86,0x83,	0x32,0x2b,0xed,0x48
+	.byte	0x1e,0x11,0x70,0xac,	0x6c,0x5a,0x72,0x4e
+	.byte	0xfd,0x0e,0xff,0xfb,	0x0f,0x85,0x38,0x56
+	.byte	0x3d,0xae,0xd5,0x1e,	0x36,0x2d,0x39,0x27
+	.byte	0x0a,0x0f,0xd9,0x64,	0x68,0x5c,0xa6,0x21
+	.byte	0x9b,0x5b,0x54,0xd1,	0x24,0x36,0x2e,0x3a
+	.byte	0x0c,0x0a,0x67,0xb1,	0x93,0x57,0xe7,0x0f
+	.byte	0xb4,0xee,0x96,0xd2,	0x1b,0x9b,0x91,0x9e
+	.byte	0x80,0xc0,0xc5,0x4f,	0x61,0xdc,0x20,0xa2
+	.byte	0x5a,0x77,0x4b,0x69,	0x1c,0x12,0x1a,0x16
+	.byte	0xe2,0x93,0xba,0x0a,	0xc0,0xa0,0x2a,0xe5
+	.byte	0x3c,0x22,0xe0,0x43,	0x12,0x1b,0x17,0x1d
+	.byte	0x0e,0x09,0x0d,0x0b,	0xf2,0x8b,0xc7,0xad
+	.byte	0x2d,0xb6,0xa8,0xb9,	0x14,0x1e,0xa9,0xc8
+	.byte	0x57,0xf1,0x19,0x85,	0xaf,0x75,0x07,0x4c
+	.byte	0xee,0x99,0xdd,0xbb,	0xa3,0x7f,0x60,0xfd
+	.byte	0xf7,0x01,0x26,0x9f,	0x5c,0x72,0xf5,0xbc
+	.byte	0x44,0x66,0x3b,0xc5,	0x5b,0xfb,0x7e,0x34
+	.byte	0x8b,0x43,0x29,0x76,	0xcb,0x23,0xc6,0xdc
+	.byte	0xb6,0xed,0xfc,0x68,	0xb8,0xe4,0xf1,0x63
+	.byte	0xd7,0x31,0xdc,0xca,	0x42,0x63,0x85,0x10
+	.byte	0x13,0x97,0x22,0x40,	0x84,0xc6,0x11,0x20
+	.byte	0x85,0x4a,0x24,0x7d,	0xd2,0xbb,0x3d,0xf8
+	.byte	0xae,0xf9,0x32,0x11,	0xc7,0x29,0xa1,0x6d
+	.byte	0x1d,0x9e,0x2f,0x4b,	0xdc,0xb2,0x30,0xf3
+	.byte	0x0d,0x86,0x52,0xec,	0x77,0xc1,0xe3,0xd0
+	.byte	0x2b,0xb3,0x16,0x6c,	0xa9,0x70,0xb9,0x99
+	.byte	0x11,0x94,0x48,0xfa,	0x47,0xe9,0x64,0x22
+	.byte	0xa8,0xfc,0x8c,0xc4,	0xa0,0xf0,0x3f,0x1a
+	.byte	0x56,0x7d,0x2c,0xd8,	0x22,0x33,0x90,0xef
+	.byte	0x87,0x49,0x4e,0xc7,	0xd9,0x38,0xd1,0xc1
+	.byte	0x8c,0xca,0xa2,0xfe,	0x98,0xd4,0x0b,0x36
+	.byte	0xa6,0xf5,0x81,0xcf,	0xa5,0x7a,0xde,0x28
+	.byte	0xda,0xb7,0x8e,0x26,	0x3f,0xad,0xbf,0xa4
+	.byte	0x2c,0x3a,0x9d,0xe4,	0x50,0x78,0x92,0x0d
+	.byte	0x6a,0x5f,0xcc,0x9b,	0x54,0x7e,0x46,0x62
+	.byte	0xf6,0x8d,0x13,0xc2,	0x90,0xd8,0xb8,0xe8
+	.byte	0x2e,0x39,0xf7,0x5e,	0x82,0xc3,0xaf,0xf5
+	.byte	0x9f,0x5d,0x80,0xbe,	0x69,0xd0,0x93,0x7c
+	.byte	0x6f,0xd5,0x2d,0xa9,	0xcf,0x25,0x12,0xb3
+	.byte	0xc8,0xac,0x99,0x3b,	0x10,0x18,0x7d,0xa7
+	.byte	0xe8,0x9c,0x63,0x6e,	0xdb,0x3b,0xbb,0x7b
+	.byte	0xcd,0x26,0x78,0x09,	0x6e,0x59,0x18,0xf4
+	.byte	0xec,0x9a,0xb7,0x01,	0x83,0x4f,0x9a,0xa8
+	.byte	0xe6,0x95,0x6e,0x65,	0xaa,0xff,0xe6,0x7e
+	.byte	0x21,0xbc,0xcf,0x08,	0xef,0x15,0xe8,0xe6
+	.byte	0xba,0xe7,0x9b,0xd9,	0x4a,0x6f,0x36,0xce
+	.byte	0xea,0x9f,0x09,0xd4,	0x29,0xb0,0x7c,0xd6
+	.byte	0x31,0xa4,0xb2,0xaf,	0x2a,0x3f,0x23,0x31
+	.byte	0xc6,0xa5,0x94,0x30,	0x35,0xa2,0x66,0xc0
+	.byte	0x74,0x4e,0xbc,0x37,	0xfc,0x82,0xca,0xa6
+	.byte	0xe0,0x90,0xd0,0xb0,	0x33,0xa7,0xd8,0x15
+	.byte	0xf1,0x04,0x98,0x4a,	0x41,0xec,0xda,0xf7
+	.byte	0x7f,0xcd,0x50,0x0e,	0x17,0x91,0xf6,0x2f
+	.byte	0x76,0x4d,0xd6,0x8d,	0x43,0xef,0xb0,0x4d
+	.byte	0xcc,0xaa,0x4d,0x54,	0xe4,0x96,0x04,0xdf
+	.byte	0x9e,0xd1,0xb5,0xe3,	0x4c,0x6a,0x88,0x1b
+	.byte	0xc1,0x2c,0x1f,0xb8,	0x46,0x65,0x51,0x7f
+	.byte	0x9d,0x5e,0xea,0x04,	0x01,0x8c,0x35,0x5d
+	.byte	0xfa,0x87,0x74,0x73,	0xfb,0x0b,0x41,0x2e
+	.byte	0xb3,0x67,0x1d,0x5a,	0x92,0xdb,0xd2,0x52
+	.byte	0xe9,0x10,0x56,0x33,	0x6d,0xd6,0x47,0x13
+	.byte	0x9a,0xd7,0x61,0x8c,	0x37,0xa1,0x0c,0x7a
+	.byte	0x59,0xf8,0x14,0x8e,	0xeb,0x13,0x3c,0x89
+	.byte	0xce,0xa9,0x27,0xee,	0xb7,0x61,0xc9,0x35
+	.byte	0xe1,0x1c,0xe5,0xed,	0x7a,0x47,0xb1,0x3c
+	.byte	0x9c,0xd2,0xdf,0x59,	0x55,0xf2,0x73,0x3f
+	.byte	0x18,0x14,0xce,0x79,	0x73,0xc7,0x37,0xbf
+	.byte	0x53,0xf7,0xcd,0xea,	0x5f,0xfd,0xaa,0x5b
+	.byte	0xdf,0x3d,0x6f,0x14,	0x78,0x44,0xdb,0x86
+	.byte	0xca,0xaf,0xf3,0x81,	0xb9,0x68,0xc4,0x3e
+	.byte	0x38,0x24,0x34,0x2c,	0xc2,0xa3,0x40,0x5f
+	.byte	0x16,0x1d,0xc3,0x72,	0xbc,0xe2,0x25,0x0c
+	.byte	0x28,0x3c,0x49,0x8b,	0xff,0x0d,0x95,0x41
+	.byte	0x39,0xa8,0x01,0x71,	0x08,0x0c,0xb3,0xde
+	.byte	0xd8,0xb4,0xe4,0x9c,	0x64,0x56,0xc1,0x90
+	.byte	0x7b,0xcb,0x84,0x61,	0xd5,0x32,0xb6,0x70
+	.byte	0x48,0x6c,0x5c,0x74,	0xd0,0xb8,0x57,0x42
+AES_Td4:
+	.byte	0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38
+	.byte	0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb
+	.byte	0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87
+	.byte	0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb
+	.byte	0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d
+	.byte	0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e
+	.byte	0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2
+	.byte	0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25
+	.byte	0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16
+	.byte	0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92
+	.byte	0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda
+	.byte	0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84
+	.byte	0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a
+	.byte	0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06
+	.byte	0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02
+	.byte	0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b
+	.byte	0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea
+	.byte	0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73
+	.byte	0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85
+	.byte	0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e
+	.byte	0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89
+	.byte	0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b
+	.byte	0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20
+	.byte	0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4
+	.byte	0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31
+	.byte	0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f
+	.byte	0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d
+	.byte	0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef
+	.byte	0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0
+	.byte	0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61
+	.byte	0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26
+	.byte	0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d
+	.cstring "AES for C64x+, CRYPTOGAMS by "
+	.align	4
+___
+
+print $code;
+close STDOUT;
diff --git a/crypto/aes/asm/aes-ia64.S b/crypto/aes/asm/aes-ia64.S
new file mode 100644
index 000000000..f7f1f63c9
--- /dev/null
+++ b/crypto/aes/asm/aes-ia64.S
@@ -0,0 +1,1130 @@
+// Copyright 2004-2016 The OpenSSL Project Authors. All Rights Reserved.
+//
+// Licensed under the OpenSSL license (the "License").  You may not use
+// this file except in compliance with the License.  You can obtain a copy
+// in the file LICENSE in the source distribution or at
+// https://www.openssl.org/source/license.html
+//
+// ====================================================================
+// Written by Andy Polyakov  for the OpenSSL
+// project. Rights for redistribution and usage in source and binary
+// forms are granted according to the OpenSSL license.
+// ====================================================================
+//
+// What's wrong with compiler generated code? Compiler never uses
+// variable 'shr' which is pairable with 'extr'/'dep' instructions.
+// Then it uses 'zxt' which is an I-type, but can be replaced with
+// 'and' which in turn can be assigned to M-port [there're double as
+// much M-ports as there're I-ports on Itanium 2]. By sacrificing few
+// registers for small constants (255, 24 and 16) to be used with
+// 'shr' and 'and' instructions I can achieve better ILP, Instruction
+// Level Parallelism, and performance. This code outperforms GCC 3.3
+// generated code by over factor of 2 (two), GCC 3.4 - by 70% and
+// HP C - by 40%. Measured best-case scenario, i.e. aligned
+// big-endian input, ECB timing on Itanium 2 is (18 + 13*rounds)
+// ticks per block, or 9.25 CPU cycles per byte for 128 bit key.
+
+// Version 1.2 mitigates the hazard of cache-timing attacks by
+// a) compressing S-boxes from 8KB to 2KB+256B, b) scheduling
+// references to S-boxes for L2 cache latency, c) prefetching T[ed]4
+// prior last round. As result performance dropped to (26 + 15*rounds)
+// ticks per block or 11 cycles per byte processed with 128-bit key.
+// This is ~16% deterioration. For reference Itanium 2 L1 cache has
+// 64 bytes line size and L2 - 128 bytes...
+
+.ident	"aes-ia64.S, version 1.2"
+.ident	"IA-64 ISA artwork by Andy Polyakov "
+.explicit
+.text
+
+rk0=r8;     rk1=r9;
+
+pfssave=r2;
+lcsave=r10;
+prsave=r3;
+maskff=r11;
+twenty4=r14;
+sixteen=r15;
+
+te00=r16;   te11=r17;   te22=r18;   te33=r19;
+te01=r20;   te12=r21;   te23=r22;   te30=r23;
+te02=r24;   te13=r25;   te20=r26;   te31=r27;
+te03=r28;   te10=r29;   te21=r30;   te32=r31;
+
+// these are rotating...
+t0=r32;     s0=r33;
+t1=r34;     s1=r35;
+t2=r36;     s2=r37;
+t3=r38;     s3=r39;
+
+te0=r40;    te1=r41;    te2=r42;    te3=r43;
+
+#if defined(_HPUX_SOURCE) && !defined(_LP64)
+# define ADDP	addp4
+#else
+# define ADDP	add
+#endif
+
+// Offsets from Te0
+#define TE0	0
+#define TE2	2
+#if defined(_HPUX_SOURCE) || defined(B_ENDIAN)
+#define TE1	3
+#define TE3	1
+#else
+#define TE1	1
+#define TE3	3
+#endif
+
+// This implies that AES_KEY comprises 32-bit key schedule elements
+// even on LP64 platforms.
+#ifndef	KSZ
+# define KSZ	4
+# define LDKEY	ld4
+#endif
+
+.proc	_ia64_AES_encrypt#
+// Input:	rk0-rk1
+//		te0
+//		te3	as AES_KEY->rounds!!!
+//		s0-s3
+//		maskff,twenty4,sixteen
+// Output:	r16,r20,r24,r28 as s0-s3
+// Clobber:	r16-r31,rk0-rk1,r32-r43
+.align	32
+_ia64_AES_encrypt:
+	.prologue
+	.altrp	b6
+	.body
+{ .mmi;	alloc	r16=ar.pfs,12,0,0,8
+	LDKEY	t0=[rk0],2*KSZ
+	mov	pr.rot=1<<16	}
+{ .mmi;	LDKEY	t1=[rk1],2*KSZ
+	add	te1=TE1,te0
+	add	te3=-3,te3	};;
+{ .mib;	LDKEY	t2=[rk0],2*KSZ
+	mov	ar.ec=2		}
+{ .mib;	LDKEY	t3=[rk1],2*KSZ
+	add	te2=TE2,te0
+	brp.loop.imp	.Le_top,.Le_end-16	};;
+
+{ .mmi;	xor	s0=s0,t0
+	xor	s1=s1,t1
+	mov	ar.lc=te3	}
+{ .mmi;	xor	s2=s2,t2
+	xor	s3=s3,t3
+	add	te3=TE3,te0	};;
+
+.align	32
+.Le_top:
+{ .mmi;	(p0)	LDKEY	t0=[rk0],2*KSZ		// 0/0:rk[0]
+	(p0)	and	te33=s3,maskff		// 0/0:s3&0xff
+	(p0)	extr.u	te22=s2,8,8	}	// 0/0:s2>>8&0xff
+{ .mmi; (p0)	LDKEY	t1=[rk1],2*KSZ		// 0/1:rk[1]
+	(p0)	and	te30=s0,maskff		// 0/1:s0&0xff
+	(p0)	shr.u	te00=s0,twenty4	};;	// 0/0:s0>>24
+{ .mmi;	(p0)	LDKEY	t2=[rk0],2*KSZ		// 1/2:rk[2]
+	(p0)	shladd	te33=te33,3,te3		// 1/0:te0+s0>>24
+	(p0)	extr.u	te23=s3,8,8	}	// 1/1:s3>>8&0xff
+{ .mmi;	(p0)	LDKEY	t3=[rk1],2*KSZ		// 1/3:rk[3]
+	(p0)	shladd	te30=te30,3,te3		// 1/1:te3+s0
+	(p0)	shr.u	te01=s1,twenty4	};;	// 1/1:s1>>24
+{ .mmi;	(p0)	ld4	te33=[te33]		// 2/0:te3[s3&0xff]
+	(p0)	shladd	te22=te22,3,te2		// 2/0:te2+s2>>8&0xff
+	(p0)	extr.u	te20=s0,8,8	}	// 2/2:s0>>8&0xff
+{ .mmi;	(p0)	ld4	te30=[te30]		// 2/1:te3[s0]
+	(p0)	shladd	te23=te23,3,te2		// 2/1:te2+s3>>8
+	(p0)	shr.u	te02=s2,twenty4	};;	// 2/2:s2>>24
+{ .mmi;	(p0)	ld4	te22=[te22]		// 3/0:te2[s2>>8]
+	(p0)	shladd	te20=te20,3,te2		// 3/2:te2+s0>>8
+	(p0)	extr.u	te21=s1,8,8	}	// 3/3:s1>>8&0xff
+{ .mmi;	(p0)	ld4	te23=[te23]		// 3/1:te2[s3>>8]
+	(p0)	shladd	te00=te00,3,te0		// 3/0:te0+s0>>24
+	(p0)	shr.u	te03=s3,twenty4	};;	// 3/3:s3>>24
+{ .mmi;	(p0)	ld4	te20=[te20]		// 4/2:te2[s0>>8]
+	(p0)	shladd	te21=te21,3,te2		// 4/3:te3+s2
+	(p0)	extr.u	te11=s1,16,8	}	// 4/0:s1>>16&0xff
+{ .mmi;	(p0)	ld4	te00=[te00]		// 4/0:te0[s0>>24]
+	(p0)	shladd	te01=te01,3,te0		// 4/1:te0+s1>>24
+	(p0)	shr.u	te13=s3,sixteen	};;	// 4/2:s3>>16
+{ .mmi;	(p0)	ld4	te21=[te21]		// 5/3:te2[s1>>8]
+	(p0)	shladd	te11=te11,3,te1		// 5/0:te1+s1>>16
+	(p0)	extr.u	te12=s2,16,8	}	// 5/1:s2>>16&0xff
+{ .mmi;	(p0)	ld4	te01=[te01]		// 5/1:te0[s1>>24]
+	(p0)	shladd	te02=te02,3,te0		// 5/2:te0+s2>>24
+	(p0)	and	te31=s1,maskff	};;	// 5/2:s1&0xff
+{ .mmi;	(p0)	ld4	te11=[te11]		// 6/0:te1[s1>>16]
+	(p0)	shladd	te12=te12,3,te1		// 6/1:te1+s2>>16
+	(p0)	extr.u	te10=s0,16,8	}	// 6/3:s0>>16&0xff
+{ .mmi;	(p0)	ld4	te02=[te02]		// 6/2:te0[s2>>24]
+	(p0)	shladd	te03=te03,3,te0		// 6/3:te1+s0>>16
+	(p0)	and	te32=s2,maskff	};;	// 6/3:s2&0xff
+
+{ .mmi;	(p0)	ld4	te12=[te12]		// 7/1:te1[s2>>16]
+	(p0)	shladd	te31=te31,3,te3		// 7/2:te3+s1&0xff
+	(p0)	and	te13=te13,maskff}	// 7/2:s3>>16&0xff
+{ .mmi;	(p0)	ld4	te03=[te03]		// 7/3:te0[s3>>24]
+	(p0)	shladd	te32=te32,3,te3		// 7/3:te3+s2
+	(p0)	xor	t0=t0,te33	};;	// 7/0:
+{ .mmi;	(p0)	ld4	te31=[te31]		// 8/2:te3[s1]
+	(p0)	shladd	te13=te13,3,te1		// 8/2:te1+s3>>16
+	(p0)	xor	t0=t0,te22	}	// 8/0:
+{ .mmi;	(p0)	ld4	te32=[te32]		// 8/3:te3[s2]
+	(p0)	shladd	te10=te10,3,te1		// 8/3:te1+s0>>16
+	(p0)	xor	t1=t1,te30	};;	// 8/1:
+{ .mmi;	(p0)	ld4	te13=[te13]		// 9/2:te1[s3>>16]
+	(p0)	ld4	te10=[te10]		// 9/3:te1[s0>>16]
+	(p0)	xor	t0=t0,te00	};;	// 9/0:		!L2 scheduling
+{ .mmi;	(p0)	xor	t1=t1,te23		// 10[9]/1:	
+	(p0)	xor	t2=t2,te20		// 10[9]/2:
+	(p0)	xor	t3=t3,te21	};;	// 10[9]/3:
+{ .mmi;	(p0)	xor	t0=t0,te11		// 11[10]/0:done!
+	(p0)	xor	t1=t1,te01		// 11[10]/1:
+	(p0)	xor	t2=t2,te02	};;	// 11[10]/2:	!L2 scheduling
+{ .mmi;	(p0)	xor	t3=t3,te03		// 12[10]/3:
+	(p16)	cmp.eq	p0,p17=r0,r0 	};;	// 12[10]/clear (p17)
+{ .mmi;	(p0)	xor	t1=t1,te12		// 13[11]/1:done!
+	(p0)	xor	t2=t2,te31		// 13[11]/2:
+	(p0)	xor	t3=t3,te32	}	// 13[11]/3:
+{ .mmi;	(p17)	add	te0=2048,te0		// 13[11]/
+	(p17)	add	te1=2048+64-TE1,te1};;	// 13[11]/
+{ .mib;	(p0)	xor	t2=t2,te13		// 14[12]/2:done!
+	(p17)	add	te2=2048+128-TE2,te2}	// 14[12]/
+{ .mib;	(p0)	xor	t3=t3,te10		// 14[12]/3:done!
+	(p17)	add	te3=2048+192-TE3,te3	// 14[12]/
+	br.ctop.sptk	.Le_top		};;
+.Le_end:
+
+
+{ .mmi;	ld8	te12=[te0]		// prefetch Te4
+	ld8	te31=[te1]	}
+{ .mmi;	ld8	te10=[te2]
+	ld8	te32=[te3]	}
+
+{ .mmi;	LDKEY	t0=[rk0],2*KSZ		// 0/0:rk[0]
+	and	te33=s3,maskff		// 0/0:s3&0xff
+	extr.u	te22=s2,8,8	}	// 0/0:s2>>8&0xff
+{ .mmi; LDKEY	t1=[rk1],2*KSZ		// 0/1:rk[1]
+	and	te30=s0,maskff		// 0/1:s0&0xff
+	shr.u	te00=s0,twenty4	};;	// 0/0:s0>>24
+{ .mmi;	LDKEY	t2=[rk0],2*KSZ		// 1/2:rk[2]
+	add	te33=te33,te0		// 1/0:te0+s0>>24
+	extr.u	te23=s3,8,8	}	// 1/1:s3>>8&0xff
+{ .mmi;	LDKEY	t3=[rk1],2*KSZ		// 1/3:rk[3]
+	add	te30=te30,te0		// 1/1:te0+s0
+	shr.u	te01=s1,twenty4	};;	// 1/1:s1>>24
+{ .mmi;	ld1	te33=[te33]		// 2/0:te0[s3&0xff]
+	add	te22=te22,te0		// 2/0:te0+s2>>8&0xff
+	extr.u	te20=s0,8,8	}	// 2/2:s0>>8&0xff
+{ .mmi;	ld1	te30=[te30]		// 2/1:te0[s0]
+	add	te23=te23,te0		// 2/1:te0+s3>>8
+	shr.u	te02=s2,twenty4	};;	// 2/2:s2>>24
+{ .mmi;	ld1	te22=[te22]		// 3/0:te0[s2>>8]
+	add	te20=te20,te0		// 3/2:te0+s0>>8
+	extr.u	te21=s1,8,8	}	// 3/3:s1>>8&0xff
+{ .mmi;	ld1	te23=[te23]		// 3/1:te0[s3>>8]
+	add	te00=te00,te0		// 3/0:te0+s0>>24
+	shr.u	te03=s3,twenty4	};;	// 3/3:s3>>24
+{ .mmi;	ld1	te20=[te20]		// 4/2:te0[s0>>8]
+	add	te21=te21,te0		// 4/3:te0+s2
+	extr.u	te11=s1,16,8	}	// 4/0:s1>>16&0xff
+{ .mmi;	ld1	te00=[te00]		// 4/0:te0[s0>>24]
+	add	te01=te01,te0		// 4/1:te0+s1>>24
+	shr.u	te13=s3,sixteen	};;	// 4/2:s3>>16
+{ .mmi;	ld1	te21=[te21]		// 5/3:te0[s1>>8]
+	add	te11=te11,te0		// 5/0:te0+s1>>16
+	extr.u	te12=s2,16,8	}	// 5/1:s2>>16&0xff
+{ .mmi;	ld1	te01=[te01]		// 5/1:te0[s1>>24]
+	add	te02=te02,te0		// 5/2:te0+s2>>24
+	and	te31=s1,maskff	};;	// 5/2:s1&0xff
+{ .mmi;	ld1	te11=[te11]		// 6/0:te0[s1>>16]
+	add	te12=te12,te0		// 6/1:te0+s2>>16
+	extr.u	te10=s0,16,8	}	// 6/3:s0>>16&0xff
+{ .mmi;	ld1	te02=[te02]		// 6/2:te0[s2>>24]
+	add	te03=te03,te0		// 6/3:te0+s0>>16
+	and	te32=s2,maskff	};;	// 6/3:s2&0xff
+
+{ .mmi;	ld1	te12=[te12]		// 7/1:te0[s2>>16]
+	add	te31=te31,te0		// 7/2:te0+s1&0xff
+	dep	te33=te22,te33,8,8}	// 7/0:
+{ .mmi;	ld1	te03=[te03]		// 7/3:te0[s3>>24]
+	add	te32=te32,te0		// 7/3:te0+s2
+	and	te13=te13,maskff};;	// 7/2:s3>>16&0xff
+{ .mmi;	ld1	te31=[te31]		// 8/2:te0[s1]
+	add	te13=te13,te0		// 8/2:te0+s3>>16
+	dep	te30=te23,te30,8,8}	// 8/1:
+{ .mmi;	ld1	te32=[te32]		// 8/3:te0[s2]
+	add	te10=te10,te0		// 8/3:te0+s0>>16
+	shl	te00=te00,twenty4};;	// 8/0:
+{ .mii;	ld1	te13=[te13]		// 9/2:te0[s3>>16]
+	dep	te33=te11,te33,16,8	// 9/0:
+	shl	te01=te01,twenty4};;	// 9/1:
+{ .mii;	ld1	te10=[te10]		// 10/3:te0[s0>>16]
+	dep	te31=te20,te31,8,8	// 10/2:
+	shl	te02=te02,twenty4};;	// 10/2:
+{ .mii;	xor	t0=t0,te33		// 11/0:
+	dep	te32=te21,te32,8,8	// 11/3:
+	shl	te12=te12,sixteen};;	// 11/1:
+{ .mii;	xor	r16=t0,te00		// 12/0:done!
+	dep	te31=te13,te31,16,8	// 12/2:
+	shl	te03=te03,twenty4};;	// 12/3:
+{ .mmi;	xor	t1=t1,te01		// 13/1:
+	xor	t2=t2,te02		// 13/2:
+	dep	te32=te10,te32,16,8};;	// 13/3:
+{ .mmi;	xor	t1=t1,te30		// 14/1:
+	xor	r24=t2,te31		// 14/2:done!
+	xor	t3=t3,te32	};;	// 14/3:
+{ .mib;	xor	r20=t1,te12		// 15/1:done!
+	xor	r28=t3,te03		// 15/3:done!
+	br.ret.sptk	b6	};;
+.endp	_ia64_AES_encrypt#
+
+// void AES_encrypt (const void *in,void *out,const AES_KEY *key);
+.global	AES_encrypt#
+.proc	AES_encrypt#
+.align	32
+AES_encrypt:
+	.prologue
+	.save	ar.pfs,pfssave
+{ .mmi;	alloc	pfssave=ar.pfs,3,1,12,0
+	and	out0=3,in0
+	mov	r3=ip			}
+{ .mmi;	ADDP	in0=0,in0
+	mov	loc0=psr.um
+	ADDP	out11=KSZ*60,in2	};;	// &AES_KEY->rounds
+
+{ .mmi;	ld4	out11=[out11]			// AES_KEY->rounds
+	add	out8=(AES_Te#-AES_encrypt#),r3	// Te0
+	.save	pr,prsave
+	mov	prsave=pr		}
+{ .mmi;	rum	1<<3				// clear um.ac
+	.save	ar.lc,lcsave
+	mov	lcsave=ar.lc		};;
+
+	.body
+#if defined(_HPUX_SOURCE)	// HPUX is big-endian, cut 15+15 cycles...
+{ .mib; cmp.ne	p6,p0=out0,r0
+	add	out0=4,in0
+(p6)	br.dpnt.many	.Le_i_unaligned	};;
+
+{ .mmi;	ld4	out1=[in0],8		// s0
+	and	out9=3,in1
+	mov	twenty4=24		}
+{ .mmi;	ld4	out3=[out0],8		// s1
+	ADDP	rk0=0,in2
+	mov	sixteen=16		};;
+{ .mmi;	ld4	out5=[in0]		// s2
+	cmp.ne	p6,p0=out9,r0
+	mov	maskff=0xff		}
+{ .mmb;	ld4	out7=[out0]		// s3
+	ADDP	rk1=KSZ,in2
+	br.call.sptk.many	b6=_ia64_AES_encrypt	};;
+
+{ .mib;	ADDP	in0=4,in1
+	ADDP	in1=0,in1
+(p6)	br.spnt	.Le_o_unaligned		};;
+
+{ .mii;	mov	psr.um=loc0
+	mov	ar.pfs=pfssave
+	mov	ar.lc=lcsave		};;
+{ .mmi;	st4	[in1]=r16,8		// s0
+	st4	[in0]=r20,8		// s1
+	mov	pr=prsave,0x1ffff	};;
+{ .mmb;	st4	[in1]=r24		// s2
+	st4	[in0]=r28		// s3
+	br.ret.sptk.many	b0	};;
+#endif
+
+.align	32
+.Le_i_unaligned:
+{ .mmi;	add	out0=1,in0
+	add	out2=2,in0
+	add	out4=3,in0	};;
+{ .mmi;	ld1	r16=[in0],4
+	ld1	r17=[out0],4	}//;;
+{ .mmi;	ld1	r18=[out2],4
+	ld1	out1=[out4],4	};;	// s0
+{ .mmi;	ld1	r20=[in0],4
+	ld1	r21=[out0],4	}//;;
+{ .mmi;	ld1	r22=[out2],4
+	ld1	out3=[out4],4	};;	// s1
+{ .mmi;	ld1	r24=[in0],4
+	ld1	r25=[out0],4	}//;;
+{ .mmi;	ld1	r26=[out2],4
+	ld1	out5=[out4],4	};;	// s2
+{ .mmi;	ld1	r28=[in0]
+	ld1	r29=[out0]	}//;;
+{ .mmi;	ld1	r30=[out2]
+	ld1	out7=[out4]	};;	// s3
+
+{ .mii;
+	dep	out1=r16,out1,24,8	//;;
+	dep	out3=r20,out3,24,8	}//;;
+{ .mii;	ADDP	rk0=0,in2
+	dep	out5=r24,out5,24,8	//;;
+	dep	out7=r28,out7,24,8	};;
+{ .mii;	ADDP	rk1=KSZ,in2
+	dep	out1=r17,out1,16,8	//;;
+	dep	out3=r21,out3,16,8	}//;;
+{ .mii;	mov	twenty4=24
+	dep	out5=r25,out5,16,8	//;;
+	dep	out7=r29,out7,16,8	};;
+{ .mii;	mov	sixteen=16
+	dep	out1=r18,out1,8,8	//;;
+	dep	out3=r22,out3,8,8	}//;;
+{ .mii;	mov	maskff=0xff
+	dep	out5=r26,out5,8,8	//;;
+	dep	out7=r30,out7,8,8	};;
+
+{ .mib;	br.call.sptk.many	b6=_ia64_AES_encrypt	};;
+
+.Le_o_unaligned:
+{ .mii;	ADDP	out0=0,in1
+	extr.u	r17=r16,8,8			// s0
+	shr.u	r19=r16,twenty4		}//;;
+{ .mii;	ADDP	out1=1,in1
+	extr.u	r18=r16,16,8
+	shr.u	r23=r20,twenty4		}//;;	// s1
+{ .mii;	ADDP	out2=2,in1
+	extr.u	r21=r20,8,8
+	shr.u	r22=r20,sixteen		}//;;
+{ .mii;	ADDP	out3=3,in1
+	extr.u	r25=r24,8,8			// s2
+	shr.u	r27=r24,twenty4		};;
+{ .mii;	st1	[out3]=r16,4
+	extr.u	r26=r24,16,8
+	shr.u	r31=r28,twenty4		}//;;	// s3
+{ .mii;	st1	[out2]=r17,4
+	extr.u	r29=r28,8,8
+	shr.u	r30=r28,sixteen		}//;;
+
+{ .mmi;	st1	[out1]=r18,4
+	st1	[out0]=r19,4		};;
+{ .mmi;	st1	[out3]=r20,4
+	st1	[out2]=r21,4		}//;;
+{ .mmi;	st1	[out1]=r22,4
+	st1	[out0]=r23,4		};;
+{ .mmi;	st1	[out3]=r24,4
+	st1	[out2]=r25,4
+	mov	pr=prsave,0x1ffff	}//;;
+{ .mmi;	st1	[out1]=r26,4
+	st1	[out0]=r27,4
+	mov	ar.pfs=pfssave		};;
+{ .mmi;	st1	[out3]=r28
+	st1	[out2]=r29
+	mov	ar.lc=lcsave		}//;;
+{ .mmi;	st1	[out1]=r30
+	st1	[out0]=r31		}
+{ .mfb;	mov	psr.um=loc0			// restore user mask
+	br.ret.sptk.many	b0	};;
+.endp	AES_encrypt#
+
+// *AES_decrypt are autogenerated by the following script:
+#if 0
+#!/usr/bin/env perl
+print "// *AES_decrypt are autogenerated by the following script:\n#if 0\n";
+open(PROG,'<'.$0); while() { print; } close(PROG);
+print "#endif\n";
+while(<>) {
+	$process=1	if (/\.proc\s+_ia64_AES_encrypt/);
+	next		if (!$process);
+
+	#s/te00=s0/td00=s0/;	s/te00/td00/g;
+	s/te11=s1/td13=s3/;	s/te11/td13/g;
+	#s/te22=s2/td22=s2/;	s/te22/td22/g;
+	s/te33=s3/td31=s1/;	s/te33/td31/g;
+
+	#s/te01=s1/td01=s1/;	s/te01/td01/g;
+	s/te12=s2/td10=s0/;	s/te12/td10/g;
+	#s/te23=s3/td23=s3/;	s/te23/td23/g;
+	s/te30=s0/td32=s2/;	s/te30/td32/g;
+
+	#s/te02=s2/td02=s2/;	s/te02/td02/g;
+	s/te13=s3/td11=s1/;	s/te13/td11/g;
+	#s/te20=s0/td20=s0/;	s/te20/td20/g;
+	s/te31=s1/td33=s3/;	s/te31/td33/g;
+
+	#s/te03=s3/td03=s3/;	s/te03/td03/g;
+	s/te10=s0/td12=s2/;	s/te10/td12/g;
+	#s/te21=s1/td21=s1/;	s/te21/td21/g;
+	s/te32=s2/td30=s0/;	s/te32/td30/g;
+
+	s/td/te/g;
+
+	s/AES_encrypt/AES_decrypt/g;
+	s/\.Le_/.Ld_/g;
+	s/AES_Te#/AES_Td#/g;
+
+	print;
+
+	exit		if (/\.endp\s+AES_decrypt/);
+}
+#endif
+.proc	_ia64_AES_decrypt#
+// Input:	rk0-rk1
+//		te0
+//		te3	as AES_KEY->rounds!!!
+//		s0-s3
+//		maskff,twenty4,sixteen
+// Output:	r16,r20,r24,r28 as s0-s3
+// Clobber:	r16-r31,rk0-rk1,r32-r43
+.align	32
+_ia64_AES_decrypt:
+	.prologue
+	.altrp	b6
+	.body
+{ .mmi;	alloc	r16=ar.pfs,12,0,0,8
+	LDKEY	t0=[rk0],2*KSZ
+	mov	pr.rot=1<<16	}
+{ .mmi;	LDKEY	t1=[rk1],2*KSZ
+	add	te1=TE1,te0
+	add	te3=-3,te3	};;
+{ .mib;	LDKEY	t2=[rk0],2*KSZ
+	mov	ar.ec=2		}
+{ .mib;	LDKEY	t3=[rk1],2*KSZ
+	add	te2=TE2,te0
+	brp.loop.imp	.Ld_top,.Ld_end-16	};;
+
+{ .mmi;	xor	s0=s0,t0
+	xor	s1=s1,t1
+	mov	ar.lc=te3	}
+{ .mmi;	xor	s2=s2,t2
+	xor	s3=s3,t3
+	add	te3=TE3,te0	};;
+
+.align	32
+.Ld_top:
+{ .mmi;	(p0)	LDKEY	t0=[rk0],2*KSZ		// 0/0:rk[0]
+	(p0)	and	te31=s1,maskff		// 0/0:s3&0xff
+	(p0)	extr.u	te22=s2,8,8	}	// 0/0:s2>>8&0xff
+{ .mmi; (p0)	LDKEY	t1=[rk1],2*KSZ		// 0/1:rk[1]
+	(p0)	and	te32=s2,maskff		// 0/1:s0&0xff
+	(p0)	shr.u	te00=s0,twenty4	};;	// 0/0:s0>>24
+{ .mmi;	(p0)	LDKEY	t2=[rk0],2*KSZ		// 1/2:rk[2]
+	(p0)	shladd	te31=te31,3,te3		// 1/0:te0+s0>>24
+	(p0)	extr.u	te23=s3,8,8	}	// 1/1:s3>>8&0xff
+{ .mmi;	(p0)	LDKEY	t3=[rk1],2*KSZ		// 1/3:rk[3]
+	(p0)	shladd	te32=te32,3,te3		// 1/1:te3+s0
+	(p0)	shr.u	te01=s1,twenty4	};;	// 1/1:s1>>24
+{ .mmi;	(p0)	ld4	te31=[te31]		// 2/0:te3[s3&0xff]
+	(p0)	shladd	te22=te22,3,te2		// 2/0:te2+s2>>8&0xff
+	(p0)	extr.u	te20=s0,8,8	}	// 2/2:s0>>8&0xff
+{ .mmi;	(p0)	ld4	te32=[te32]		// 2/1:te3[s0]
+	(p0)	shladd	te23=te23,3,te2		// 2/1:te2+s3>>8
+	(p0)	shr.u	te02=s2,twenty4	};;	// 2/2:s2>>24
+{ .mmi;	(p0)	ld4	te22=[te22]		// 3/0:te2[s2>>8]
+	(p0)	shladd	te20=te20,3,te2		// 3/2:te2+s0>>8
+	(p0)	extr.u	te21=s1,8,8	}	// 3/3:s1>>8&0xff
+{ .mmi;	(p0)	ld4	te23=[te23]		// 3/1:te2[s3>>8]
+	(p0)	shladd	te00=te00,3,te0		// 3/0:te0+s0>>24
+	(p0)	shr.u	te03=s3,twenty4	};;	// 3/3:s3>>24
+{ .mmi;	(p0)	ld4	te20=[te20]		// 4/2:te2[s0>>8]
+	(p0)	shladd	te21=te21,3,te2		// 4/3:te3+s2
+	(p0)	extr.u	te13=s3,16,8	}	// 4/0:s1>>16&0xff
+{ .mmi;	(p0)	ld4	te00=[te00]		// 4/0:te0[s0>>24]
+	(p0)	shladd	te01=te01,3,te0		// 4/1:te0+s1>>24
+	(p0)	shr.u	te11=s1,sixteen	};;	// 4/2:s3>>16
+{ .mmi;	(p0)	ld4	te21=[te21]		// 5/3:te2[s1>>8]
+	(p0)	shladd	te13=te13,3,te1		// 5/0:te1+s1>>16
+	(p0)	extr.u	te10=s0,16,8	}	// 5/1:s2>>16&0xff
+{ .mmi;	(p0)	ld4	te01=[te01]		// 5/1:te0[s1>>24]
+	(p0)	shladd	te02=te02,3,te0		// 5/2:te0+s2>>24
+	(p0)	and	te33=s3,maskff	};;	// 5/2:s1&0xff
+{ .mmi;	(p0)	ld4	te13=[te13]		// 6/0:te1[s1>>16]
+	(p0)	shladd	te10=te10,3,te1		// 6/1:te1+s2>>16
+	(p0)	extr.u	te12=s2,16,8	}	// 6/3:s0>>16&0xff
+{ .mmi;	(p0)	ld4	te02=[te02]		// 6/2:te0[s2>>24]
+	(p0)	shladd	te03=te03,3,te0		// 6/3:te1+s0>>16
+	(p0)	and	te30=s0,maskff	};;	// 6/3:s2&0xff
+
+{ .mmi;	(p0)	ld4	te10=[te10]		// 7/1:te1[s2>>16]
+	(p0)	shladd	te33=te33,3,te3		// 7/2:te3+s1&0xff
+	(p0)	and	te11=te11,maskff}	// 7/2:s3>>16&0xff
+{ .mmi;	(p0)	ld4	te03=[te03]		// 7/3:te0[s3>>24]
+	(p0)	shladd	te30=te30,3,te3		// 7/3:te3+s2
+	(p0)	xor	t0=t0,te31	};;	// 7/0:
+{ .mmi;	(p0)	ld4	te33=[te33]		// 8/2:te3[s1]
+	(p0)	shladd	te11=te11,3,te1		// 8/2:te1+s3>>16
+	(p0)	xor	t0=t0,te22	}	// 8/0:
+{ .mmi;	(p0)	ld4	te30=[te30]		// 8/3:te3[s2]
+	(p0)	shladd	te12=te12,3,te1		// 8/3:te1+s0>>16
+	(p0)	xor	t1=t1,te32	};;	// 8/1:
+{ .mmi;	(p0)	ld4	te11=[te11]		// 9/2:te1[s3>>16]
+	(p0)	ld4	te12=[te12]		// 9/3:te1[s0>>16]
+	(p0)	xor	t0=t0,te00	};;	// 9/0:		!L2 scheduling
+{ .mmi;	(p0)	xor	t1=t1,te23		// 10[9]/1:	
+	(p0)	xor	t2=t2,te20		// 10[9]/2:
+	(p0)	xor	t3=t3,te21	};;	// 10[9]/3:
+{ .mmi;	(p0)	xor	t0=t0,te13		// 11[10]/0:done!
+	(p0)	xor	t1=t1,te01		// 11[10]/1:
+	(p0)	xor	t2=t2,te02	};;	// 11[10]/2:	!L2 scheduling
+{ .mmi;	(p0)	xor	t3=t3,te03		// 12[10]/3:
+	(p16)	cmp.eq	p0,p17=r0,r0 	};;	// 12[10]/clear (p17)
+{ .mmi;	(p0)	xor	t1=t1,te10		// 13[11]/1:done!
+	(p0)	xor	t2=t2,te33		// 13[11]/2:
+	(p0)	xor	t3=t3,te30	}	// 13[11]/3:
+{ .mmi;	(p17)	add	te0=2048,te0		// 13[11]/
+	(p17)	add	te1=2048+64-TE1,te1};;	// 13[11]/
+{ .mib;	(p0)	xor	t2=t2,te11		// 14[12]/2:done!
+	(p17)	add	te2=2048+128-TE2,te2}	// 14[12]/
+{ .mib;	(p0)	xor	t3=t3,te12		// 14[12]/3:done!
+	(p17)	add	te3=2048+192-TE3,te3	// 14[12]/
+	br.ctop.sptk	.Ld_top		};;
+.Ld_end:
+
+
+{ .mmi;	ld8	te10=[te0]		// prefetch Td4
+	ld8	te33=[te1]	}
+{ .mmi;	ld8	te12=[te2]
+	ld8	te30=[te3]	}
+
+{ .mmi;	LDKEY	t0=[rk0],2*KSZ		// 0/0:rk[0]
+	and	te31=s1,maskff		// 0/0:s3&0xff
+	extr.u	te22=s2,8,8	}	// 0/0:s2>>8&0xff
+{ .mmi; LDKEY	t1=[rk1],2*KSZ		// 0/1:rk[1]
+	and	te32=s2,maskff		// 0/1:s0&0xff
+	shr.u	te00=s0,twenty4	};;	// 0/0:s0>>24
+{ .mmi;	LDKEY	t2=[rk0],2*KSZ		// 1/2:rk[2]
+	add	te31=te31,te0		// 1/0:te0+s0>>24
+	extr.u	te23=s3,8,8	}	// 1/1:s3>>8&0xff
+{ .mmi;	LDKEY	t3=[rk1],2*KSZ		// 1/3:rk[3]
+	add	te32=te32,te0		// 1/1:te0+s0
+	shr.u	te01=s1,twenty4	};;	// 1/1:s1>>24
+{ .mmi;	ld1	te31=[te31]		// 2/0:te0[s3&0xff]
+	add	te22=te22,te0		// 2/0:te0+s2>>8&0xff
+	extr.u	te20=s0,8,8	}	// 2/2:s0>>8&0xff
+{ .mmi;	ld1	te32=[te32]		// 2/1:te0[s0]
+	add	te23=te23,te0		// 2/1:te0+s3>>8
+	shr.u	te02=s2,twenty4	};;	// 2/2:s2>>24
+{ .mmi;	ld1	te22=[te22]		// 3/0:te0[s2>>8]
+	add	te20=te20,te0		// 3/2:te0+s0>>8
+	extr.u	te21=s1,8,8	}	// 3/3:s1>>8&0xff
+{ .mmi;	ld1	te23=[te23]		// 3/1:te0[s3>>8]
+	add	te00=te00,te0		// 3/0:te0+s0>>24
+	shr.u	te03=s3,twenty4	};;	// 3/3:s3>>24
+{ .mmi;	ld1	te20=[te20]		// 4/2:te0[s0>>8]
+	add	te21=te21,te0		// 4/3:te0+s2
+	extr.u	te13=s3,16,8	}	// 4/0:s1>>16&0xff
+{ .mmi;	ld1	te00=[te00]		// 4/0:te0[s0>>24]
+	add	te01=te01,te0		// 4/1:te0+s1>>24
+	shr.u	te11=s1,sixteen	};;	// 4/2:s3>>16
+{ .mmi;	ld1	te21=[te21]		// 5/3:te0[s1>>8]
+	add	te13=te13,te0		// 5/0:te0+s1>>16
+	extr.u	te10=s0,16,8	}	// 5/1:s2>>16&0xff
+{ .mmi;	ld1	te01=[te01]		// 5/1:te0[s1>>24]
+	add	te02=te02,te0		// 5/2:te0+s2>>24
+	and	te33=s3,maskff	};;	// 5/2:s1&0xff
+{ .mmi;	ld1	te13=[te13]		// 6/0:te0[s1>>16]
+	add	te10=te10,te0		// 6/1:te0+s2>>16
+	extr.u	te12=s2,16,8	}	// 6/3:s0>>16&0xff
+{ .mmi;	ld1	te02=[te02]		// 6/2:te0[s2>>24]
+	add	te03=te03,te0		// 6/3:te0+s0>>16
+	and	te30=s0,maskff	};;	// 6/3:s2&0xff
+
+{ .mmi;	ld1	te10=[te10]		// 7/1:te0[s2>>16]
+	add	te33=te33,te0		// 7/2:te0+s1&0xff
+	dep	te31=te22,te31,8,8}	// 7/0:
+{ .mmi;	ld1	te03=[te03]		// 7/3:te0[s3>>24]
+	add	te30=te30,te0		// 7/3:te0+s2
+	and	te11=te11,maskff};;	// 7/2:s3>>16&0xff
+{ .mmi;	ld1	te33=[te33]		// 8/2:te0[s1]
+	add	te11=te11,te0		// 8/2:te0+s3>>16
+	dep	te32=te23,te32,8,8}	// 8/1:
+{ .mmi;	ld1	te30=[te30]		// 8/3:te0[s2]
+	add	te12=te12,te0		// 8/3:te0+s0>>16
+	shl	te00=te00,twenty4};;	// 8/0:
+{ .mii;	ld1	te11=[te11]		// 9/2:te0[s3>>16]
+	dep	te31=te13,te31,16,8	// 9/0:
+	shl	te01=te01,twenty4};;	// 9/1:
+{ .mii;	ld1	te12=[te12]		// 10/3:te0[s0>>16]
+	dep	te33=te20,te33,8,8	// 10/2:
+	shl	te02=te02,twenty4};;	// 10/2:
+{ .mii;	xor	t0=t0,te31		// 11/0:
+	dep	te30=te21,te30,8,8	// 11/3:
+	shl	te10=te10,sixteen};;	// 11/1:
+{ .mii;	xor	r16=t0,te00		// 12/0:done!
+	dep	te33=te11,te33,16,8	// 12/2:
+	shl	te03=te03,twenty4};;	// 12/3:
+{ .mmi;	xor	t1=t1,te01		// 13/1:
+	xor	t2=t2,te02		// 13/2:
+	dep	te30=te12,te30,16,8};;	// 13/3:
+{ .mmi;	xor	t1=t1,te32		// 14/1:
+	xor	r24=t2,te33		// 14/2:done!
+	xor	t3=t3,te30	};;	// 14/3:
+{ .mib;	xor	r20=t1,te10		// 15/1:done!
+	xor	r28=t3,te03		// 15/3:done!
+	br.ret.sptk	b6	};;
+.endp	_ia64_AES_decrypt#
+
+// void AES_decrypt (const void *in,void *out,const AES_KEY *key);
+.global	AES_decrypt#
+.proc	AES_decrypt#
+.align	32
+AES_decrypt:
+	.prologue
+	.save	ar.pfs,pfssave
+{ .mmi;	alloc	pfssave=ar.pfs,3,1,12,0
+	and	out0=3,in0
+	mov	r3=ip			}
+{ .mmi;	ADDP	in0=0,in0
+	mov	loc0=psr.um
+	ADDP	out11=KSZ*60,in2	};;	// &AES_KEY->rounds
+
+{ .mmi;	ld4	out11=[out11]			// AES_KEY->rounds
+	add	out8=(AES_Td#-AES_decrypt#),r3	// Te0
+	.save	pr,prsave
+	mov	prsave=pr		}
+{ .mmi;	rum	1<<3				// clear um.ac
+	.save	ar.lc,lcsave
+	mov	lcsave=ar.lc		};;
+
+	.body
+#if defined(_HPUX_SOURCE)	// HPUX is big-endian, cut 15+15 cycles...
+{ .mib; cmp.ne	p6,p0=out0,r0
+	add	out0=4,in0
+(p6)	br.dpnt.many	.Ld_i_unaligned	};;
+
+{ .mmi;	ld4	out1=[in0],8		// s0
+	and	out9=3,in1
+	mov	twenty4=24		}
+{ .mmi;	ld4	out3=[out0],8		// s1
+	ADDP	rk0=0,in2
+	mov	sixteen=16		};;
+{ .mmi;	ld4	out5=[in0]		// s2
+	cmp.ne	p6,p0=out9,r0
+	mov	maskff=0xff		}
+{ .mmb;	ld4	out7=[out0]		// s3
+	ADDP	rk1=KSZ,in2
+	br.call.sptk.many	b6=_ia64_AES_decrypt	};;
+
+{ .mib;	ADDP	in0=4,in1
+	ADDP	in1=0,in1
+(p6)	br.spnt	.Ld_o_unaligned		};;
+
+{ .mii;	mov	psr.um=loc0
+	mov	ar.pfs=pfssave
+	mov	ar.lc=lcsave		};;
+{ .mmi;	st4	[in1]=r16,8		// s0
+	st4	[in0]=r20,8		// s1
+	mov	pr=prsave,0x1ffff	};;
+{ .mmb;	st4	[in1]=r24		// s2
+	st4	[in0]=r28		// s3
+	br.ret.sptk.many	b0	};;
+#endif
+
+.align	32
+.Ld_i_unaligned:
+{ .mmi;	add	out0=1,in0
+	add	out2=2,in0
+	add	out4=3,in0	};;
+{ .mmi;	ld1	r16=[in0],4
+	ld1	r17=[out0],4	}//;;
+{ .mmi;	ld1	r18=[out2],4
+	ld1	out1=[out4],4	};;	// s0
+{ .mmi;	ld1	r20=[in0],4
+	ld1	r21=[out0],4	}//;;
+{ .mmi;	ld1	r22=[out2],4
+	ld1	out3=[out4],4	};;	// s1
+{ .mmi;	ld1	r24=[in0],4
+	ld1	r25=[out0],4	}//;;
+{ .mmi;	ld1	r26=[out2],4
+	ld1	out5=[out4],4	};;	// s2
+{ .mmi;	ld1	r28=[in0]
+	ld1	r29=[out0]	}//;;
+{ .mmi;	ld1	r30=[out2]
+	ld1	out7=[out4]	};;	// s3
+
+{ .mii;
+	dep	out1=r16,out1,24,8	//;;
+	dep	out3=r20,out3,24,8	}//;;
+{ .mii;	ADDP	rk0=0,in2
+	dep	out5=r24,out5,24,8	//;;
+	dep	out7=r28,out7,24,8	};;
+{ .mii;	ADDP	rk1=KSZ,in2
+	dep	out1=r17,out1,16,8	//;;
+	dep	out3=r21,out3,16,8	}//;;
+{ .mii;	mov	twenty4=24
+	dep	out5=r25,out5,16,8	//;;
+	dep	out7=r29,out7,16,8	};;
+{ .mii;	mov	sixteen=16
+	dep	out1=r18,out1,8,8	//;;
+	dep	out3=r22,out3,8,8	}//;;
+{ .mii;	mov	maskff=0xff
+	dep	out5=r26,out5,8,8	//;;
+	dep	out7=r30,out7,8,8	};;
+
+{ .mib;	br.call.sptk.many	b6=_ia64_AES_decrypt	};;
+
+.Ld_o_unaligned:
+{ .mii;	ADDP	out0=0,in1
+	extr.u	r17=r16,8,8			// s0
+	shr.u	r19=r16,twenty4		}//;;
+{ .mii;	ADDP	out1=1,in1
+	extr.u	r18=r16,16,8
+	shr.u	r23=r20,twenty4		}//;;	// s1
+{ .mii;	ADDP	out2=2,in1
+	extr.u	r21=r20,8,8
+	shr.u	r22=r20,sixteen		}//;;
+{ .mii;	ADDP	out3=3,in1
+	extr.u	r25=r24,8,8			// s2
+	shr.u	r27=r24,twenty4		};;
+{ .mii;	st1	[out3]=r16,4
+	extr.u	r26=r24,16,8
+	shr.u	r31=r28,twenty4		}//;;	// s3
+{ .mii;	st1	[out2]=r17,4
+	extr.u	r29=r28,8,8
+	shr.u	r30=r28,sixteen		}//;;
+
+{ .mmi;	st1	[out1]=r18,4
+	st1	[out0]=r19,4		};;
+{ .mmi;	st1	[out3]=r20,4
+	st1	[out2]=r21,4		}//;;
+{ .mmi;	st1	[out1]=r22,4
+	st1	[out0]=r23,4		};;
+{ .mmi;	st1	[out3]=r24,4
+	st1	[out2]=r25,4
+	mov	pr=prsave,0x1ffff	}//;;
+{ .mmi;	st1	[out1]=r26,4
+	st1	[out0]=r27,4
+	mov	ar.pfs=pfssave		};;
+{ .mmi;	st1	[out3]=r28
+	st1	[out2]=r29
+	mov	ar.lc=lcsave		}//;;
+{ .mmi;	st1	[out1]=r30
+	st1	[out0]=r31		}
+{ .mfb;	mov	psr.um=loc0			// restore user mask
+	br.ret.sptk.many	b0	};;
+.endp	AES_decrypt#
+
+// leave it in .text segment...
+.align	64
+.global	AES_Te#
+.type	AES_Te#,@object
+AES_Te:	data4	0xc66363a5,0xc66363a5, 0xf87c7c84,0xf87c7c84
+	data4	0xee777799,0xee777799, 0xf67b7b8d,0xf67b7b8d
+	data4	0xfff2f20d,0xfff2f20d, 0xd66b6bbd,0xd66b6bbd
+	data4	0xde6f6fb1,0xde6f6fb1, 0x91c5c554,0x91c5c554
+	data4	0x60303050,0x60303050, 0x02010103,0x02010103
+	data4	0xce6767a9,0xce6767a9, 0x562b2b7d,0x562b2b7d
+	data4	0xe7fefe19,0xe7fefe19, 0xb5d7d762,0xb5d7d762
+	data4	0x4dababe6,0x4dababe6, 0xec76769a,0xec76769a
+	data4	0x8fcaca45,0x8fcaca45, 0x1f82829d,0x1f82829d
+	data4	0x89c9c940,0x89c9c940, 0xfa7d7d87,0xfa7d7d87
+	data4	0xeffafa15,0xeffafa15, 0xb25959eb,0xb25959eb
+	data4	0x8e4747c9,0x8e4747c9, 0xfbf0f00b,0xfbf0f00b
+	data4	0x41adadec,0x41adadec, 0xb3d4d467,0xb3d4d467
+	data4	0x5fa2a2fd,0x5fa2a2fd, 0x45afafea,0x45afafea
+	data4	0x239c9cbf,0x239c9cbf, 0x53a4a4f7,0x53a4a4f7
+	data4	0xe4727296,0xe4727296, 0x9bc0c05b,0x9bc0c05b
+	data4	0x75b7b7c2,0x75b7b7c2, 0xe1fdfd1c,0xe1fdfd1c
+	data4	0x3d9393ae,0x3d9393ae, 0x4c26266a,0x4c26266a
+	data4	0x6c36365a,0x6c36365a, 0x7e3f3f41,0x7e3f3f41
+	data4	0xf5f7f702,0xf5f7f702, 0x83cccc4f,0x83cccc4f
+	data4	0x6834345c,0x6834345c, 0x51a5a5f4,0x51a5a5f4
+	data4	0xd1e5e534,0xd1e5e534, 0xf9f1f108,0xf9f1f108
+	data4	0xe2717193,0xe2717193, 0xabd8d873,0xabd8d873
+	data4	0x62313153,0x62313153, 0x2a15153f,0x2a15153f
+	data4	0x0804040c,0x0804040c, 0x95c7c752,0x95c7c752
+	data4	0x46232365,0x46232365, 0x9dc3c35e,0x9dc3c35e
+	data4	0x30181828,0x30181828, 0x379696a1,0x379696a1
+	data4	0x0a05050f,0x0a05050f, 0x2f9a9ab5,0x2f9a9ab5
+	data4	0x0e070709,0x0e070709, 0x24121236,0x24121236
+	data4	0x1b80809b,0x1b80809b, 0xdfe2e23d,0xdfe2e23d
+	data4	0xcdebeb26,0xcdebeb26, 0x4e272769,0x4e272769
+	data4	0x7fb2b2cd,0x7fb2b2cd, 0xea75759f,0xea75759f
+	data4	0x1209091b,0x1209091b, 0x1d83839e,0x1d83839e
+	data4	0x582c2c74,0x582c2c74, 0x341a1a2e,0x341a1a2e
+	data4	0x361b1b2d,0x361b1b2d, 0xdc6e6eb2,0xdc6e6eb2
+	data4	0xb45a5aee,0xb45a5aee, 0x5ba0a0fb,0x5ba0a0fb
+	data4	0xa45252f6,0xa45252f6, 0x763b3b4d,0x763b3b4d
+	data4	0xb7d6d661,0xb7d6d661, 0x7db3b3ce,0x7db3b3ce
+	data4	0x5229297b,0x5229297b, 0xdde3e33e,0xdde3e33e
+	data4	0x5e2f2f71,0x5e2f2f71, 0x13848497,0x13848497
+	data4	0xa65353f5,0xa65353f5, 0xb9d1d168,0xb9d1d168
+	data4	0x00000000,0x00000000, 0xc1eded2c,0xc1eded2c
+	data4	0x40202060,0x40202060, 0xe3fcfc1f,0xe3fcfc1f
+	data4	0x79b1b1c8,0x79b1b1c8, 0xb65b5bed,0xb65b5bed
+	data4	0xd46a6abe,0xd46a6abe, 0x8dcbcb46,0x8dcbcb46
+	data4	0x67bebed9,0x67bebed9, 0x7239394b,0x7239394b
+	data4	0x944a4ade,0x944a4ade, 0x984c4cd4,0x984c4cd4
+	data4	0xb05858e8,0xb05858e8, 0x85cfcf4a,0x85cfcf4a
+	data4	0xbbd0d06b,0xbbd0d06b, 0xc5efef2a,0xc5efef2a
+	data4	0x4faaaae5,0x4faaaae5, 0xedfbfb16,0xedfbfb16
+	data4	0x864343c5,0x864343c5, 0x9a4d4dd7,0x9a4d4dd7
+	data4	0x66333355,0x66333355, 0x11858594,0x11858594
+	data4	0x8a4545cf,0x8a4545cf, 0xe9f9f910,0xe9f9f910
+	data4	0x04020206,0x04020206, 0xfe7f7f81,0xfe7f7f81
+	data4	0xa05050f0,0xa05050f0, 0x783c3c44,0x783c3c44
+	data4	0x259f9fba,0x259f9fba, 0x4ba8a8e3,0x4ba8a8e3
+	data4	0xa25151f3,0xa25151f3, 0x5da3a3fe,0x5da3a3fe
+	data4	0x804040c0,0x804040c0, 0x058f8f8a,0x058f8f8a
+	data4	0x3f9292ad,0x3f9292ad, 0x219d9dbc,0x219d9dbc
+	data4	0x70383848,0x70383848, 0xf1f5f504,0xf1f5f504
+	data4	0x63bcbcdf,0x63bcbcdf, 0x77b6b6c1,0x77b6b6c1
+	data4	0xafdada75,0xafdada75, 0x42212163,0x42212163
+	data4	0x20101030,0x20101030, 0xe5ffff1a,0xe5ffff1a
+	data4	0xfdf3f30e,0xfdf3f30e, 0xbfd2d26d,0xbfd2d26d
+	data4	0x81cdcd4c,0x81cdcd4c, 0x180c0c14,0x180c0c14
+	data4	0x26131335,0x26131335, 0xc3ecec2f,0xc3ecec2f
+	data4	0xbe5f5fe1,0xbe5f5fe1, 0x359797a2,0x359797a2
+	data4	0x884444cc,0x884444cc, 0x2e171739,0x2e171739
+	data4	0x93c4c457,0x93c4c457, 0x55a7a7f2,0x55a7a7f2
+	data4	0xfc7e7e82,0xfc7e7e82, 0x7a3d3d47,0x7a3d3d47
+	data4	0xc86464ac,0xc86464ac, 0xba5d5de7,0xba5d5de7
+	data4	0x3219192b,0x3219192b, 0xe6737395,0xe6737395
+	data4	0xc06060a0,0xc06060a0, 0x19818198,0x19818198
+	data4	0x9e4f4fd1,0x9e4f4fd1, 0xa3dcdc7f,0xa3dcdc7f
+	data4	0x44222266,0x44222266, 0x542a2a7e,0x542a2a7e
+	data4	0x3b9090ab,0x3b9090ab, 0x0b888883,0x0b888883
+	data4	0x8c4646ca,0x8c4646ca, 0xc7eeee29,0xc7eeee29
+	data4	0x6bb8b8d3,0x6bb8b8d3, 0x2814143c,0x2814143c
+	data4	0xa7dede79,0xa7dede79, 0xbc5e5ee2,0xbc5e5ee2
+	data4	0x160b0b1d,0x160b0b1d, 0xaddbdb76,0xaddbdb76
+	data4	0xdbe0e03b,0xdbe0e03b, 0x64323256,0x64323256
+	data4	0x743a3a4e,0x743a3a4e, 0x140a0a1e,0x140a0a1e
+	data4	0x924949db,0x924949db, 0x0c06060a,0x0c06060a
+	data4	0x4824246c,0x4824246c, 0xb85c5ce4,0xb85c5ce4
+	data4	0x9fc2c25d,0x9fc2c25d, 0xbdd3d36e,0xbdd3d36e
+	data4	0x43acacef,0x43acacef, 0xc46262a6,0xc46262a6
+	data4	0x399191a8,0x399191a8, 0x319595a4,0x319595a4
+	data4	0xd3e4e437,0xd3e4e437, 0xf279798b,0xf279798b
+	data4	0xd5e7e732,0xd5e7e732, 0x8bc8c843,0x8bc8c843
+	data4	0x6e373759,0x6e373759, 0xda6d6db7,0xda6d6db7
+	data4	0x018d8d8c,0x018d8d8c, 0xb1d5d564,0xb1d5d564
+	data4	0x9c4e4ed2,0x9c4e4ed2, 0x49a9a9e0,0x49a9a9e0
+	data4	0xd86c6cb4,0xd86c6cb4, 0xac5656fa,0xac5656fa
+	data4	0xf3f4f407,0xf3f4f407, 0xcfeaea25,0xcfeaea25
+	data4	0xca6565af,0xca6565af, 0xf47a7a8e,0xf47a7a8e
+	data4	0x47aeaee9,0x47aeaee9, 0x10080818,0x10080818
+	data4	0x6fbabad5,0x6fbabad5, 0xf0787888,0xf0787888
+	data4	0x4a25256f,0x4a25256f, 0x5c2e2e72,0x5c2e2e72
+	data4	0x381c1c24,0x381c1c24, 0x57a6a6f1,0x57a6a6f1
+	data4	0x73b4b4c7,0x73b4b4c7, 0x97c6c651,0x97c6c651
+	data4	0xcbe8e823,0xcbe8e823, 0xa1dddd7c,0xa1dddd7c
+	data4	0xe874749c,0xe874749c, 0x3e1f1f21,0x3e1f1f21
+	data4	0x964b4bdd,0x964b4bdd, 0x61bdbddc,0x61bdbddc
+	data4	0x0d8b8b86,0x0d8b8b86, 0x0f8a8a85,0x0f8a8a85
+	data4	0xe0707090,0xe0707090, 0x7c3e3e42,0x7c3e3e42
+	data4	0x71b5b5c4,0x71b5b5c4, 0xcc6666aa,0xcc6666aa
+	data4	0x904848d8,0x904848d8, 0x06030305,0x06030305
+	data4	0xf7f6f601,0xf7f6f601, 0x1c0e0e12,0x1c0e0e12
+	data4	0xc26161a3,0xc26161a3, 0x6a35355f,0x6a35355f
+	data4	0xae5757f9,0xae5757f9, 0x69b9b9d0,0x69b9b9d0
+	data4	0x17868691,0x17868691, 0x99c1c158,0x99c1c158
+	data4	0x3a1d1d27,0x3a1d1d27, 0x279e9eb9,0x279e9eb9
+	data4	0xd9e1e138,0xd9e1e138, 0xebf8f813,0xebf8f813
+	data4	0x2b9898b3,0x2b9898b3, 0x22111133,0x22111133
+	data4	0xd26969bb,0xd26969bb, 0xa9d9d970,0xa9d9d970
+	data4	0x078e8e89,0x078e8e89, 0x339494a7,0x339494a7
+	data4	0x2d9b9bb6,0x2d9b9bb6, 0x3c1e1e22,0x3c1e1e22
+	data4	0x15878792,0x15878792, 0xc9e9e920,0xc9e9e920
+	data4	0x87cece49,0x87cece49, 0xaa5555ff,0xaa5555ff
+	data4	0x50282878,0x50282878, 0xa5dfdf7a,0xa5dfdf7a
+	data4	0x038c8c8f,0x038c8c8f, 0x59a1a1f8,0x59a1a1f8
+	data4	0x09898980,0x09898980, 0x1a0d0d17,0x1a0d0d17
+	data4	0x65bfbfda,0x65bfbfda, 0xd7e6e631,0xd7e6e631
+	data4	0x844242c6,0x844242c6, 0xd06868b8,0xd06868b8
+	data4	0x824141c3,0x824141c3, 0x299999b0,0x299999b0
+	data4	0x5a2d2d77,0x5a2d2d77, 0x1e0f0f11,0x1e0f0f11
+	data4	0x7bb0b0cb,0x7bb0b0cb, 0xa85454fc,0xa85454fc
+	data4	0x6dbbbbd6,0x6dbbbbd6, 0x2c16163a,0x2c16163a
+// Te4:
+	data1	0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5
+	data1	0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76
+	data1	0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0
+	data1	0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0
+	data1	0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc
+	data1	0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15
+	data1	0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a
+	data1	0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75
+	data1	0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0
+	data1	0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84
+	data1	0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b
+	data1	0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf
+	data1	0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85
+	data1	0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8
+	data1	0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5
+	data1	0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2
+	data1	0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17
+	data1	0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73
+	data1	0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88
+	data1	0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb
+	data1	0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c
+	data1	0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79
+	data1	0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9
+	data1	0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08
+	data1	0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6
+	data1	0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a
+	data1	0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e
+	data1	0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e
+	data1	0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94
+	data1	0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf
+	data1	0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68
+	data1	0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16
+.size	AES_Te#,2048+256	// HP-UX assembler fails to ".-AES_Te#"
+
+.align	64
+.global	AES_Td#
+.type	AES_Td#,@object
+AES_Td:	data4	0x51f4a750,0x51f4a750, 0x7e416553,0x7e416553
+	data4	0x1a17a4c3,0x1a17a4c3, 0x3a275e96,0x3a275e96
+	data4	0x3bab6bcb,0x3bab6bcb, 0x1f9d45f1,0x1f9d45f1
+	data4	0xacfa58ab,0xacfa58ab, 0x4be30393,0x4be30393
+	data4	0x2030fa55,0x2030fa55, 0xad766df6,0xad766df6
+	data4	0x88cc7691,0x88cc7691, 0xf5024c25,0xf5024c25
+	data4	0x4fe5d7fc,0x4fe5d7fc, 0xc52acbd7,0xc52acbd7
+	data4	0x26354480,0x26354480, 0xb562a38f,0xb562a38f
+	data4	0xdeb15a49,0xdeb15a49, 0x25ba1b67,0x25ba1b67
+	data4	0x45ea0e98,0x45ea0e98, 0x5dfec0e1,0x5dfec0e1
+	data4	0xc32f7502,0xc32f7502, 0x814cf012,0x814cf012
+	data4	0x8d4697a3,0x8d4697a3, 0x6bd3f9c6,0x6bd3f9c6
+	data4	0x038f5fe7,0x038f5fe7, 0x15929c95,0x15929c95
+	data4	0xbf6d7aeb,0xbf6d7aeb, 0x955259da,0x955259da
+	data4	0xd4be832d,0xd4be832d, 0x587421d3,0x587421d3
+	data4	0x49e06929,0x49e06929, 0x8ec9c844,0x8ec9c844
+	data4	0x75c2896a,0x75c2896a, 0xf48e7978,0xf48e7978
+	data4	0x99583e6b,0x99583e6b, 0x27b971dd,0x27b971dd
+	data4	0xbee14fb6,0xbee14fb6, 0xf088ad17,0xf088ad17
+	data4	0xc920ac66,0xc920ac66, 0x7dce3ab4,0x7dce3ab4
+	data4	0x63df4a18,0x63df4a18, 0xe51a3182,0xe51a3182
+	data4	0x97513360,0x97513360, 0x62537f45,0x62537f45
+	data4	0xb16477e0,0xb16477e0, 0xbb6bae84,0xbb6bae84
+	data4	0xfe81a01c,0xfe81a01c, 0xf9082b94,0xf9082b94
+	data4	0x70486858,0x70486858, 0x8f45fd19,0x8f45fd19
+	data4	0x94de6c87,0x94de6c87, 0x527bf8b7,0x527bf8b7
+	data4	0xab73d323,0xab73d323, 0x724b02e2,0x724b02e2
+	data4	0xe31f8f57,0xe31f8f57, 0x6655ab2a,0x6655ab2a
+	data4	0xb2eb2807,0xb2eb2807, 0x2fb5c203,0x2fb5c203
+	data4	0x86c57b9a,0x86c57b9a, 0xd33708a5,0xd33708a5
+	data4	0x302887f2,0x302887f2, 0x23bfa5b2,0x23bfa5b2
+	data4	0x02036aba,0x02036aba, 0xed16825c,0xed16825c
+	data4	0x8acf1c2b,0x8acf1c2b, 0xa779b492,0xa779b492
+	data4	0xf307f2f0,0xf307f2f0, 0x4e69e2a1,0x4e69e2a1
+	data4	0x65daf4cd,0x65daf4cd, 0x0605bed5,0x0605bed5
+	data4	0xd134621f,0xd134621f, 0xc4a6fe8a,0xc4a6fe8a
+	data4	0x342e539d,0x342e539d, 0xa2f355a0,0xa2f355a0
+	data4	0x058ae132,0x058ae132, 0xa4f6eb75,0xa4f6eb75
+	data4	0x0b83ec39,0x0b83ec39, 0x4060efaa,0x4060efaa
+	data4	0x5e719f06,0x5e719f06, 0xbd6e1051,0xbd6e1051
+	data4	0x3e218af9,0x3e218af9, 0x96dd063d,0x96dd063d
+	data4	0xdd3e05ae,0xdd3e05ae, 0x4de6bd46,0x4de6bd46
+	data4	0x91548db5,0x91548db5, 0x71c45d05,0x71c45d05
+	data4	0x0406d46f,0x0406d46f, 0x605015ff,0x605015ff
+	data4	0x1998fb24,0x1998fb24, 0xd6bde997,0xd6bde997
+	data4	0x894043cc,0x894043cc, 0x67d99e77,0x67d99e77
+	data4	0xb0e842bd,0xb0e842bd, 0x07898b88,0x07898b88
+	data4	0xe7195b38,0xe7195b38, 0x79c8eedb,0x79c8eedb
+	data4	0xa17c0a47,0xa17c0a47, 0x7c420fe9,0x7c420fe9
+	data4	0xf8841ec9,0xf8841ec9, 0x00000000,0x00000000
+	data4	0x09808683,0x09808683, 0x322bed48,0x322bed48
+	data4	0x1e1170ac,0x1e1170ac, 0x6c5a724e,0x6c5a724e
+	data4	0xfd0efffb,0xfd0efffb, 0x0f853856,0x0f853856
+	data4	0x3daed51e,0x3daed51e, 0x362d3927,0x362d3927
+	data4	0x0a0fd964,0x0a0fd964, 0x685ca621,0x685ca621
+	data4	0x9b5b54d1,0x9b5b54d1, 0x24362e3a,0x24362e3a
+	data4	0x0c0a67b1,0x0c0a67b1, 0x9357e70f,0x9357e70f
+	data4	0xb4ee96d2,0xb4ee96d2, 0x1b9b919e,0x1b9b919e
+	data4	0x80c0c54f,0x80c0c54f, 0x61dc20a2,0x61dc20a2
+	data4	0x5a774b69,0x5a774b69, 0x1c121a16,0x1c121a16
+	data4	0xe293ba0a,0xe293ba0a, 0xc0a02ae5,0xc0a02ae5
+	data4	0x3c22e043,0x3c22e043, 0x121b171d,0x121b171d
+	data4	0x0e090d0b,0x0e090d0b, 0xf28bc7ad,0xf28bc7ad
+	data4	0x2db6a8b9,0x2db6a8b9, 0x141ea9c8,0x141ea9c8
+	data4	0x57f11985,0x57f11985, 0xaf75074c,0xaf75074c
+	data4	0xee99ddbb,0xee99ddbb, 0xa37f60fd,0xa37f60fd
+	data4	0xf701269f,0xf701269f, 0x5c72f5bc,0x5c72f5bc
+	data4	0x44663bc5,0x44663bc5, 0x5bfb7e34,0x5bfb7e34
+	data4	0x8b432976,0x8b432976, 0xcb23c6dc,0xcb23c6dc
+	data4	0xb6edfc68,0xb6edfc68, 0xb8e4f163,0xb8e4f163
+	data4	0xd731dcca,0xd731dcca, 0x42638510,0x42638510
+	data4	0x13972240,0x13972240, 0x84c61120,0x84c61120
+	data4	0x854a247d,0x854a247d, 0xd2bb3df8,0xd2bb3df8
+	data4	0xaef93211,0xaef93211, 0xc729a16d,0xc729a16d
+	data4	0x1d9e2f4b,0x1d9e2f4b, 0xdcb230f3,0xdcb230f3
+	data4	0x0d8652ec,0x0d8652ec, 0x77c1e3d0,0x77c1e3d0
+	data4	0x2bb3166c,0x2bb3166c, 0xa970b999,0xa970b999
+	data4	0x119448fa,0x119448fa, 0x47e96422,0x47e96422
+	data4	0xa8fc8cc4,0xa8fc8cc4, 0xa0f03f1a,0xa0f03f1a
+	data4	0x567d2cd8,0x567d2cd8, 0x223390ef,0x223390ef
+	data4	0x87494ec7,0x87494ec7, 0xd938d1c1,0xd938d1c1
+	data4	0x8ccaa2fe,0x8ccaa2fe, 0x98d40b36,0x98d40b36
+	data4	0xa6f581cf,0xa6f581cf, 0xa57ade28,0xa57ade28
+	data4	0xdab78e26,0xdab78e26, 0x3fadbfa4,0x3fadbfa4
+	data4	0x2c3a9de4,0x2c3a9de4, 0x5078920d,0x5078920d
+	data4	0x6a5fcc9b,0x6a5fcc9b, 0x547e4662,0x547e4662
+	data4	0xf68d13c2,0xf68d13c2, 0x90d8b8e8,0x90d8b8e8
+	data4	0x2e39f75e,0x2e39f75e, 0x82c3aff5,0x82c3aff5
+	data4	0x9f5d80be,0x9f5d80be, 0x69d0937c,0x69d0937c
+	data4	0x6fd52da9,0x6fd52da9, 0xcf2512b3,0xcf2512b3
+	data4	0xc8ac993b,0xc8ac993b, 0x10187da7,0x10187da7
+	data4	0xe89c636e,0xe89c636e, 0xdb3bbb7b,0xdb3bbb7b
+	data4	0xcd267809,0xcd267809, 0x6e5918f4,0x6e5918f4
+	data4	0xec9ab701,0xec9ab701, 0x834f9aa8,0x834f9aa8
+	data4	0xe6956e65,0xe6956e65, 0xaaffe67e,0xaaffe67e
+	data4	0x21bccf08,0x21bccf08, 0xef15e8e6,0xef15e8e6
+	data4	0xbae79bd9,0xbae79bd9, 0x4a6f36ce,0x4a6f36ce
+	data4	0xea9f09d4,0xea9f09d4, 0x29b07cd6,0x29b07cd6
+	data4	0x31a4b2af,0x31a4b2af, 0x2a3f2331,0x2a3f2331
+	data4	0xc6a59430,0xc6a59430, 0x35a266c0,0x35a266c0
+	data4	0x744ebc37,0x744ebc37, 0xfc82caa6,0xfc82caa6
+	data4	0xe090d0b0,0xe090d0b0, 0x33a7d815,0x33a7d815
+	data4	0xf104984a,0xf104984a, 0x41ecdaf7,0x41ecdaf7
+	data4	0x7fcd500e,0x7fcd500e, 0x1791f62f,0x1791f62f
+	data4	0x764dd68d,0x764dd68d, 0x43efb04d,0x43efb04d
+	data4	0xccaa4d54,0xccaa4d54, 0xe49604df,0xe49604df
+	data4	0x9ed1b5e3,0x9ed1b5e3, 0x4c6a881b,0x4c6a881b
+	data4	0xc12c1fb8,0xc12c1fb8, 0x4665517f,0x4665517f
+	data4	0x9d5eea04,0x9d5eea04, 0x018c355d,0x018c355d
+	data4	0xfa877473,0xfa877473, 0xfb0b412e,0xfb0b412e
+	data4	0xb3671d5a,0xb3671d5a, 0x92dbd252,0x92dbd252
+	data4	0xe9105633,0xe9105633, 0x6dd64713,0x6dd64713
+	data4	0x9ad7618c,0x9ad7618c, 0x37a10c7a,0x37a10c7a
+	data4	0x59f8148e,0x59f8148e, 0xeb133c89,0xeb133c89
+	data4	0xcea927ee,0xcea927ee, 0xb761c935,0xb761c935
+	data4	0xe11ce5ed,0xe11ce5ed, 0x7a47b13c,0x7a47b13c
+	data4	0x9cd2df59,0x9cd2df59, 0x55f2733f,0x55f2733f
+	data4	0x1814ce79,0x1814ce79, 0x73c737bf,0x73c737bf
+	data4	0x53f7cdea,0x53f7cdea, 0x5ffdaa5b,0x5ffdaa5b
+	data4	0xdf3d6f14,0xdf3d6f14, 0x7844db86,0x7844db86
+	data4	0xcaaff381,0xcaaff381, 0xb968c43e,0xb968c43e
+	data4	0x3824342c,0x3824342c, 0xc2a3405f,0xc2a3405f
+	data4	0x161dc372,0x161dc372, 0xbce2250c,0xbce2250c
+	data4	0x283c498b,0x283c498b, 0xff0d9541,0xff0d9541
+	data4	0x39a80171,0x39a80171, 0x080cb3de,0x080cb3de
+	data4	0xd8b4e49c,0xd8b4e49c, 0x6456c190,0x6456c190
+	data4	0x7bcb8461,0x7bcb8461, 0xd532b670,0xd532b670
+	data4	0x486c5c74,0x486c5c74, 0xd0b85742,0xd0b85742
+// Td4:
+	data1	0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38
+	data1	0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb
+	data1	0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87
+	data1	0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb
+	data1	0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d
+	data1	0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e
+	data1	0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2
+	data1	0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25
+	data1	0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16
+	data1	0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92
+	data1	0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda
+	data1	0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84
+	data1	0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a
+	data1	0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06
+	data1	0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02
+	data1	0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b
+	data1	0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea
+	data1	0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73
+	data1	0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85
+	data1	0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e
+	data1	0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89
+	data1	0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b
+	data1	0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20
+	data1	0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4
+	data1	0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31
+	data1	0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f
+	data1	0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d
+	data1	0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef
+	data1	0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0
+	data1	0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61
+	data1	0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26
+	data1	0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d
+.size	AES_Td#,2048+256	// HP-UX assembler fails to ".-AES_Td#"
diff --git a/crypto/aes/asm/aes-mips.pl b/crypto/aes/asm/aes-mips.pl
new file mode 100644
index 000000000..439578d9c
--- /dev/null
+++ b/crypto/aes/asm/aes-mips.pl
@@ -0,0 +1,2131 @@
+#! /usr/bin/env perl
+# Copyright 2010-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+
+# ====================================================================
+# Written by Andy Polyakov  for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+
+# AES for MIPS
+
+# October 2010
+#
+# Code uses 1K[+256B] S-box and on single-issue core [such as R5000]
+# spends ~68 cycles per byte processed with 128-bit key. This is ~16%
+# faster than gcc-generated code, which is not very impressive. But
+# recall that compressed S-box requires extra processing, namely
+# additional rotations. Rotations are implemented with lwl/lwr pairs,
+# which is normally used for loading unaligned data. Another cool
+# thing about this module is its endian neutrality, which means that
+# it processes data without ever changing byte order...
+
+# September 2012
+#
+# Add MIPS32R2 (~10% less instructions) and SmartMIPS ASE (further
+# ~25% less instructions) code. Note that there is no run-time switch,
+# instead, code path is chosen upon pre-process time, pass -mips32r2
+# or/and -msmartmips.
+
+######################################################################
+# There is a number of MIPS ABI in use, O32 and N32/64 are most
+# widely used. Then there is a new contender: NUBI. It appears that if
+# one picks the latter, it's possible to arrange code in ABI neutral
+# manner. Therefore let's stick to NUBI register layout:
+#
+($zero,$at,$t0,$t1,$t2)=map("\$$_",(0..2,24,25));
+($a0,$a1,$a2,$a3,$a4,$a5,$a6,$a7)=map("\$$_",(4..11));
+($s0,$s1,$s2,$s3,$s4,$s5,$s6,$s7,$s8,$s9,$s10,$s11)=map("\$$_",(12..23));
+($gp,$tp,$sp,$fp,$ra)=map("\$$_",(3,28..31));
+#
+# The return value is placed in $a0. Following coding rules facilitate
+# interoperability:
+#
+# - never ever touch $tp, "thread pointer", former $gp;
+# - copy return value to $t0, former $v0 [or to $a0 if you're adapting
+#   old code];
+# - on O32 populate $a4-$a7 with 'lw $aN,4*N($sp)' if necessary;
+#
+# For reference here is register layout for N32/64 MIPS ABIs:
+#
+# ($zero,$at,$v0,$v1)=map("\$$_",(0..3));
+# ($a0,$a1,$a2,$a3,$a4,$a5,$a6,$a7)=map("\$$_",(4..11));
+# ($t0,$t1,$t2,$t3,$t8,$t9)=map("\$$_",(12..15,24,25));
+# ($s0,$s1,$s2,$s3,$s4,$s5,$s6,$s7)=map("\$$_",(16..23));
+# ($gp,$sp,$fp,$ra)=map("\$$_",(28..31));
+#
+$flavour = shift || "o32"; # supported flavours are o32,n32,64,nubi32,nubi64
+
+if ($flavour =~ /64|n32/i) {
+	$PTR_LA="dla";
+	$PTR_ADD="dadd";	# incidentally works even on n32
+	$PTR_SUB="dsub";	# incidentally works even on n32
+	$PTR_INS="dins";
+	$REG_S="sd";
+	$REG_L="ld";
+	$PTR_SLL="dsll";	# incidentally works even on n32
+	$SZREG=8;
+} else {
+	$PTR_LA="la";
+	$PTR_ADD="add";
+	$PTR_SUB="sub";
+	$PTR_INS="ins";
+	$REG_S="sw";
+	$REG_L="lw";
+	$PTR_SLL="sll";
+	$SZREG=4;
+}
+$pf = ($flavour =~ /nubi/i) ? $t0 : $t2;
+#
+# 
+#
+######################################################################
+
+$big_endian=(`echo MIPSEL | $ENV{CC} -E -`=~/MIPSEL/)?1:0 if ($ENV{CC});
+
+for (@ARGV) {	$output=$_ if (/\w[\w\-]*\.\w+$/);	}
+open STDOUT,">$output";
+
+if (!defined($big_endian))
+{    $big_endian=(unpack('L',pack('N',1))==1);   }
+
+while (($output=shift) && ($output!~/\w[\w\-]*\.\w+$/)) {}
+open STDOUT,">$output";
+
+my ($MSB,$LSB)=(0,3);	# automatically converted to little-endian
+
+$code.=<<___;
+.text
+#ifdef OPENSSL_FIPSCANISTER
+# include 
+#endif
+
+#if defined(__mips_smartmips) && !defined(_MIPS_ARCH_MIPS32R2)
+#define _MIPS_ARCH_MIPS32R2
+#endif
+
+#if !defined(__mips_eabi) && (!defined(__vxworks) || defined(__pic__))
+.option	pic2
+#endif
+.set	noat
+___
+
+{{{
+my $FRAMESIZE=16*$SZREG;
+my $SAVED_REGS_MASK = ($flavour =~ /nubi/i) ? "0xc0fff008" : "0xc0ff0000";
+
+my ($inp,$out,$key,$Tbl,$s0,$s1,$s2,$s3)=($a0,$a1,$a2,$a3,$a4,$a5,$a6,$a7);
+my ($i0,$i1,$i2,$i3)=($at,$t0,$t1,$t2);
+my ($t0,$t1,$t2,$t3,$t4,$t5,$t6,$t7,$t8,$t9,$t10,$t11) = map("\$$_",(12..23));
+my ($key0,$cnt)=($gp,$fp);
+
+# instuction ordering is "stolen" from output from MIPSpro assembler
+# invoked with -mips3 -O3 arguments...
+$code.=<<___;
+.align	5
+.ent	_mips_AES_encrypt
+_mips_AES_encrypt:
+	.frame	$sp,0,$ra
+	.set	reorder
+	lw	$t0,0($key)
+	lw	$t1,4($key)
+	lw	$t2,8($key)
+	lw	$t3,12($key)
+	lw	$cnt,240($key)
+	$PTR_ADD $key0,$key,16
+
+	xor	$s0,$t0
+	xor	$s1,$t1
+	xor	$s2,$t2
+	xor	$s3,$t3
+
+	sub	$cnt,1
+#if defined(__mips_smartmips)
+	ext	$i0,$s1,16,8
+.Loop_enc:
+	ext	$i1,$s2,16,8
+	ext	$i2,$s3,16,8
+	ext	$i3,$s0,16,8
+	lwxs	$t0,$i0($Tbl)		# Te1[s1>>16]
+	ext	$i0,$s2,8,8
+	lwxs	$t1,$i1($Tbl)		# Te1[s2>>16]
+	ext	$i1,$s3,8,8
+	lwxs	$t2,$i2($Tbl)		# Te1[s3>>16]
+	ext	$i2,$s0,8,8
+	lwxs	$t3,$i3($Tbl)		# Te1[s0>>16]
+	ext	$i3,$s1,8,8
+
+	lwxs	$t4,$i0($Tbl)		# Te2[s2>>8]
+	ext	$i0,$s3,0,8
+	lwxs	$t5,$i1($Tbl)		# Te2[s3>>8]
+	ext	$i1,$s0,0,8
+	lwxs	$t6,$i2($Tbl)		# Te2[s0>>8]
+	ext	$i2,$s1,0,8
+	lwxs	$t7,$i3($Tbl)		# Te2[s1>>8]
+	ext	$i3,$s2,0,8
+
+	lwxs	$t8,$i0($Tbl)		# Te3[s3]
+	ext	$i0,$s0,24,8
+	lwxs	$t9,$i1($Tbl)		# Te3[s0]
+	ext	$i1,$s1,24,8
+	lwxs	$t10,$i2($Tbl)		# Te3[s1]
+	ext	$i2,$s2,24,8
+	lwxs	$t11,$i3($Tbl)		# Te3[s2]
+	ext	$i3,$s3,24,8
+
+	rotr	$t0,$t0,8
+	rotr	$t1,$t1,8
+	rotr	$t2,$t2,8
+	rotr	$t3,$t3,8
+
+	rotr	$t4,$t4,16
+	rotr	$t5,$t5,16
+	rotr	$t6,$t6,16
+	rotr	$t7,$t7,16
+
+	xor	$t0,$t4
+	lwxs	$t4,$i0($Tbl)		# Te0[s0>>24]
+	xor	$t1,$t5
+	lwxs	$t5,$i1($Tbl)		# Te0[s1>>24]
+	xor	$t2,$t6
+	lwxs	$t6,$i2($Tbl)		# Te0[s2>>24]
+	xor	$t3,$t7
+	lwxs	$t7,$i3($Tbl)		# Te0[s3>>24]
+
+	rotr	$t8,$t8,24
+	lw	$s0,0($key0)
+	rotr	$t9,$t9,24
+	lw	$s1,4($key0)
+	rotr	$t10,$t10,24
+	lw	$s2,8($key0)
+	rotr	$t11,$t11,24
+	lw	$s3,12($key0)
+
+	xor	$t0,$t8
+	xor	$t1,$t9
+	xor	$t2,$t10
+	xor	$t3,$t11
+
+	xor	$t0,$t4
+	xor	$t1,$t5
+	xor	$t2,$t6
+	xor	$t3,$t7
+
+	sub	$cnt,1
+	$PTR_ADD $key0,16
+	xor	$s0,$t0
+	xor	$s1,$t1
+	xor	$s2,$t2
+	xor	$s3,$t3
+	.set	noreorder
+	bnez	$cnt,.Loop_enc
+	ext	$i0,$s1,16,8
+
+	_xtr	$i0,$s1,16-2
+#else
+	_xtr	$i0,$s1,16-2
+.Loop_enc:
+	_xtr	$i1,$s2,16-2
+	_xtr	$i2,$s3,16-2
+	_xtr	$i3,$s0,16-2
+	and	$i0,0x3fc
+	and	$i1,0x3fc
+	and	$i2,0x3fc
+	and	$i3,0x3fc
+	$PTR_ADD $i0,$Tbl
+	$PTR_ADD $i1,$Tbl
+	$PTR_ADD $i2,$Tbl
+	$PTR_ADD $i3,$Tbl
+#if defined(_MIPS_ARCH_MIPS32R2) || defined(_MIPS_ARCH_MIPS64R2)
+	lw	$t0,0($i0)		# Te1[s1>>16]
+	_xtr	$i0,$s2,8-2
+	lw	$t1,0($i1)		# Te1[s2>>16]
+	_xtr	$i1,$s3,8-2
+	lw	$t2,0($i2)		# Te1[s3>>16]
+	_xtr	$i2,$s0,8-2
+	lw	$t3,0($i3)		# Te1[s0>>16]
+	_xtr	$i3,$s1,8-2
+#else
+	lwl	$t0,3($i0)		# Te1[s1>>16]
+	lwl	$t1,3($i1)		# Te1[s2>>16]
+	lwl	$t2,3($i2)		# Te1[s3>>16]
+	lwl	$t3,3($i3)		# Te1[s0>>16]
+	lwr	$t0,2($i0)		# Te1[s1>>16]
+	_xtr	$i0,$s2,8-2
+	lwr	$t1,2($i1)		# Te1[s2>>16]
+	_xtr	$i1,$s3,8-2
+	lwr	$t2,2($i2)		# Te1[s3>>16]
+	_xtr	$i2,$s0,8-2
+	lwr	$t3,2($i3)		# Te1[s0>>16]
+	_xtr	$i3,$s1,8-2
+#endif
+	and	$i0,0x3fc
+	and	$i1,0x3fc
+	and	$i2,0x3fc
+	and	$i3,0x3fc
+	$PTR_ADD $i0,$Tbl
+	$PTR_ADD $i1,$Tbl
+	$PTR_ADD $i2,$Tbl
+	$PTR_ADD $i3,$Tbl
+#if defined(_MIPS_ARCH_MIPS32R2) || defined(_MIPS_ARCH_MIPS64R2)
+	rotr	$t0,$t0,8
+	rotr	$t1,$t1,8
+	rotr	$t2,$t2,8
+	rotr	$t3,$t3,8
+# if defined(_MIPSEL)
+	lw	$t4,0($i0)		# Te2[s2>>8]
+	_xtr	$i0,$s3,0-2
+	lw	$t5,0($i1)		# Te2[s3>>8]
+	_xtr	$i1,$s0,0-2
+	lw	$t6,0($i2)		# Te2[s0>>8]
+	_xtr	$i2,$s1,0-2
+	lw	$t7,0($i3)		# Te2[s1>>8]
+	_xtr	$i3,$s2,0-2
+
+	and	$i0,0x3fc
+	and	$i1,0x3fc
+	and	$i2,0x3fc
+	and	$i3,0x3fc
+	$PTR_ADD $i0,$Tbl
+	$PTR_ADD $i1,$Tbl
+	$PTR_ADD $i2,$Tbl
+	$PTR_ADD $i3,$Tbl
+	lw	$t8,0($i0)		# Te3[s3]
+	$PTR_INS $i0,$s0,2,8
+	lw	$t9,0($i1)		# Te3[s0]
+	$PTR_INS $i1,$s1,2,8
+	lw	$t10,0($i2)		# Te3[s1]
+	$PTR_INS $i2,$s2,2,8
+	lw	$t11,0($i3)		# Te3[s2]
+	$PTR_INS $i3,$s3,2,8
+# else
+	lw	$t4,0($i0)		# Te2[s2>>8]
+	$PTR_INS $i0,$s3,2,8
+	lw	$t5,0($i1)		# Te2[s3>>8]
+	$PTR_INS $i1,$s0,2,8
+	lw	$t6,0($i2)		# Te2[s0>>8]
+	$PTR_INS $i2,$s1,2,8
+	lw	$t7,0($i3)		# Te2[s1>>8]
+	$PTR_INS $i3,$s2,2,8
+
+	lw	$t8,0($i0)		# Te3[s3]
+	_xtr	$i0,$s0,24-2
+	lw	$t9,0($i1)		# Te3[s0]
+	_xtr	$i1,$s1,24-2
+	lw	$t10,0($i2)		# Te3[s1]
+	_xtr	$i2,$s2,24-2
+	lw	$t11,0($i3)		# Te3[s2]
+	_xtr	$i3,$s3,24-2
+
+	and	$i0,0x3fc
+	and	$i1,0x3fc
+	and	$i2,0x3fc
+	and	$i3,0x3fc
+	$PTR_ADD $i0,$Tbl
+	$PTR_ADD $i1,$Tbl
+	$PTR_ADD $i2,$Tbl
+	$PTR_ADD $i3,$Tbl
+# endif
+	rotr	$t4,$t4,16
+	rotr	$t5,$t5,16
+	rotr	$t6,$t6,16
+	rotr	$t7,$t7,16
+
+	rotr	$t8,$t8,24
+	rotr	$t9,$t9,24
+	rotr	$t10,$t10,24
+	rotr	$t11,$t11,24
+#else
+	lwl	$t4,2($i0)		# Te2[s2>>8]
+	lwl	$t5,2($i1)		# Te2[s3>>8]
+	lwl	$t6,2($i2)		# Te2[s0>>8]
+	lwl	$t7,2($i3)		# Te2[s1>>8]
+	lwr	$t4,1($i0)		# Te2[s2>>8]
+	_xtr	$i0,$s3,0-2
+	lwr	$t5,1($i1)		# Te2[s3>>8]
+	_xtr	$i1,$s0,0-2
+	lwr	$t6,1($i2)		# Te2[s0>>8]
+	_xtr	$i2,$s1,0-2
+	lwr	$t7,1($i3)		# Te2[s1>>8]
+	_xtr	$i3,$s2,0-2
+
+	and	$i0,0x3fc
+	and	$i1,0x3fc
+	and	$i2,0x3fc
+	and	$i3,0x3fc
+	$PTR_ADD $i0,$Tbl
+	$PTR_ADD $i1,$Tbl
+	$PTR_ADD $i2,$Tbl
+	$PTR_ADD $i3,$Tbl
+	lwl	$t8,1($i0)		# Te3[s3]
+	lwl	$t9,1($i1)		# Te3[s0]
+	lwl	$t10,1($i2)		# Te3[s1]
+	lwl	$t11,1($i3)		# Te3[s2]
+	lwr	$t8,0($i0)		# Te3[s3]
+	_xtr	$i0,$s0,24-2
+	lwr	$t9,0($i1)		# Te3[s0]
+	_xtr	$i1,$s1,24-2
+	lwr	$t10,0($i2)		# Te3[s1]
+	_xtr	$i2,$s2,24-2
+	lwr	$t11,0($i3)		# Te3[s2]
+	_xtr	$i3,$s3,24-2
+
+	and	$i0,0x3fc
+	and	$i1,0x3fc
+	and	$i2,0x3fc
+	and	$i3,0x3fc
+	$PTR_ADD $i0,$Tbl
+	$PTR_ADD $i1,$Tbl
+	$PTR_ADD $i2,$Tbl
+	$PTR_ADD $i3,$Tbl
+#endif
+	xor	$t0,$t4
+	lw	$t4,0($i0)		# Te0[s0>>24]
+	xor	$t1,$t5
+	lw	$t5,0($i1)		# Te0[s1>>24]
+	xor	$t2,$t6
+	lw	$t6,0($i2)		# Te0[s2>>24]
+	xor	$t3,$t7
+	lw	$t7,0($i3)		# Te0[s3>>24]
+
+	xor	$t0,$t8
+	lw	$s0,0($key0)
+	xor	$t1,$t9
+	lw	$s1,4($key0)
+	xor	$t2,$t10
+	lw	$s2,8($key0)
+	xor	$t3,$t11
+	lw	$s3,12($key0)
+
+	xor	$t0,$t4
+	xor	$t1,$t5
+	xor	$t2,$t6
+	xor	$t3,$t7
+
+	sub	$cnt,1
+	$PTR_ADD $key0,16
+	xor	$s0,$t0
+	xor	$s1,$t1
+	xor	$s2,$t2
+	xor	$s3,$t3
+	.set	noreorder
+	bnez	$cnt,.Loop_enc
+	_xtr	$i0,$s1,16-2
+#endif
+
+	.set	reorder
+	_xtr	$i1,$s2,16-2
+	_xtr	$i2,$s3,16-2
+	_xtr	$i3,$s0,16-2
+	and	$i0,0x3fc
+	and	$i1,0x3fc
+	and	$i2,0x3fc
+	and	$i3,0x3fc
+	$PTR_ADD $i0,$Tbl
+	$PTR_ADD $i1,$Tbl
+	$PTR_ADD $i2,$Tbl
+	$PTR_ADD $i3,$Tbl
+	lbu	$t0,2($i0)		# Te4[s1>>16]
+	_xtr	$i0,$s2,8-2
+	lbu	$t1,2($i1)		# Te4[s2>>16]
+	_xtr	$i1,$s3,8-2
+	lbu	$t2,2($i2)		# Te4[s3>>16]
+	_xtr	$i2,$s0,8-2
+	lbu	$t3,2($i3)		# Te4[s0>>16]
+	_xtr	$i3,$s1,8-2
+
+	and	$i0,0x3fc
+	and	$i1,0x3fc
+	and	$i2,0x3fc
+	and	$i3,0x3fc
+	$PTR_ADD $i0,$Tbl
+	$PTR_ADD $i1,$Tbl
+	$PTR_ADD $i2,$Tbl
+	$PTR_ADD $i3,$Tbl
+#if defined(_MIPS_ARCH_MIPS32R2) || defined(_MIPS_ARCH_MIPS64R2)
+# if defined(_MIPSEL)
+	lbu	$t4,2($i0)		# Te4[s2>>8]
+	$PTR_INS $i0,$s0,2,8
+	lbu	$t5,2($i1)		# Te4[s3>>8]
+	$PTR_INS $i1,$s1,2,8
+	lbu	$t6,2($i2)		# Te4[s0>>8]
+	$PTR_INS $i2,$s2,2,8
+	lbu	$t7,2($i3)		# Te4[s1>>8]
+	$PTR_INS $i3,$s3,2,8
+
+	lbu	$t8,2($i0)		# Te4[s0>>24]
+	_xtr	$i0,$s3,0-2
+	lbu	$t9,2($i1)		# Te4[s1>>24]
+	_xtr	$i1,$s0,0-2
+	lbu	$t10,2($i2)		# Te4[s2>>24]
+	_xtr	$i2,$s1,0-2
+	lbu	$t11,2($i3)		# Te4[s3>>24]
+	_xtr	$i3,$s2,0-2
+
+	and	$i0,0x3fc
+	and	$i1,0x3fc
+	and	$i2,0x3fc
+	and	$i3,0x3fc
+	$PTR_ADD $i0,$Tbl
+	$PTR_ADD $i1,$Tbl
+	$PTR_ADD $i2,$Tbl
+	$PTR_ADD $i3,$Tbl
+# else
+	lbu	$t4,2($i0)		# Te4[s2>>8]
+	_xtr	$i0,$s0,24-2
+	lbu	$t5,2($i1)		# Te4[s3>>8]
+	_xtr	$i1,$s1,24-2
+	lbu	$t6,2($i2)		# Te4[s0>>8]
+	_xtr	$i2,$s2,24-2
+	lbu	$t7,2($i3)		# Te4[s1>>8]
+	_xtr	$i3,$s3,24-2
+
+	and	$i0,0x3fc
+	and	$i1,0x3fc
+	and	$i2,0x3fc
+	and	$i3,0x3fc
+	$PTR_ADD $i0,$Tbl
+	$PTR_ADD $i1,$Tbl
+	$PTR_ADD $i2,$Tbl
+	$PTR_ADD $i3,$Tbl
+	lbu	$t8,2($i0)		# Te4[s0>>24]
+	$PTR_INS $i0,$s3,2,8
+	lbu	$t9,2($i1)		# Te4[s1>>24]
+	$PTR_INS $i1,$s0,2,8
+	lbu	$t10,2($i2)		# Te4[s2>>24]
+	$PTR_INS $i2,$s1,2,8
+	lbu	$t11,2($i3)		# Te4[s3>>24]
+	$PTR_INS $i3,$s2,2,8
+# endif
+	_ins	$t0,16
+	_ins	$t1,16
+	_ins	$t2,16
+	_ins	$t3,16
+
+	_ins2	$t0,$t4,8
+	lbu	$t4,2($i0)		# Te4[s3]
+	_ins2	$t1,$t5,8
+	lbu	$t5,2($i1)		# Te4[s0]
+	_ins2	$t2,$t6,8
+	lbu	$t6,2($i2)		# Te4[s1]
+	_ins2	$t3,$t7,8
+	lbu	$t7,2($i3)		# Te4[s2]
+
+	_ins2	$t0,$t8,24
+	lw	$s0,0($key0)
+	_ins2	$t1,$t9,24
+	lw	$s1,4($key0)
+	_ins2	$t2,$t10,24
+	lw	$s2,8($key0)
+	_ins2	$t3,$t11,24
+	lw	$s3,12($key0)
+
+	_ins2	$t0,$t4,0
+	_ins2	$t1,$t5,0
+	_ins2	$t2,$t6,0
+	_ins2	$t3,$t7,0
+#else
+	lbu	$t4,2($i0)		# Te4[s2>>8]
+	_xtr	$i0,$s0,24-2
+	lbu	$t5,2($i1)		# Te4[s3>>8]
+	_xtr	$i1,$s1,24-2
+	lbu	$t6,2($i2)		# Te4[s0>>8]
+	_xtr	$i2,$s2,24-2
+	lbu	$t7,2($i3)		# Te4[s1>>8]
+	_xtr	$i3,$s3,24-2
+
+	and	$i0,0x3fc
+	and	$i1,0x3fc
+	and	$i2,0x3fc
+	and	$i3,0x3fc
+	$PTR_ADD $i0,$Tbl
+	$PTR_ADD $i1,$Tbl
+	$PTR_ADD $i2,$Tbl
+	$PTR_ADD $i3,$Tbl
+	lbu	$t8,2($i0)		# Te4[s0>>24]
+	_xtr	$i0,$s3,0-2
+	lbu	$t9,2($i1)		# Te4[s1>>24]
+	_xtr	$i1,$s0,0-2
+	lbu	$t10,2($i2)		# Te4[s2>>24]
+	_xtr	$i2,$s1,0-2
+	lbu	$t11,2($i3)		# Te4[s3>>24]
+	_xtr	$i3,$s2,0-2
+
+	and	$i0,0x3fc
+	and	$i1,0x3fc
+	and	$i2,0x3fc
+	and	$i3,0x3fc
+	$PTR_ADD $i0,$Tbl
+	$PTR_ADD $i1,$Tbl
+	$PTR_ADD $i2,$Tbl
+	$PTR_ADD $i3,$Tbl
+
+	_ins	$t0,16
+	_ins	$t1,16
+	_ins	$t2,16
+	_ins	$t3,16
+
+	_ins	$t4,8
+	_ins	$t5,8
+	_ins	$t6,8
+	_ins	$t7,8
+
+	xor	$t0,$t4
+	lbu	$t4,2($i0)		# Te4[s3]
+	xor	$t1,$t5
+	lbu	$t5,2($i1)		# Te4[s0]
+	xor	$t2,$t6
+	lbu	$t6,2($i2)		# Te4[s1]
+	xor	$t3,$t7
+	lbu	$t7,2($i3)		# Te4[s2]
+
+	_ins	$t8,24
+	lw	$s0,0($key0)
+	_ins	$t9,24
+	lw	$s1,4($key0)
+	_ins	$t10,24
+	lw	$s2,8($key0)
+	_ins	$t11,24
+	lw	$s3,12($key0)
+
+	xor	$t0,$t8
+	xor	$t1,$t9
+	xor	$t2,$t10
+	xor	$t3,$t11
+
+	_ins	$t4,0
+	_ins	$t5,0
+	_ins	$t6,0
+	_ins	$t7,0
+
+	xor	$t0,$t4
+	xor	$t1,$t5
+	xor	$t2,$t6
+	xor	$t3,$t7
+#endif
+	xor	$s0,$t0
+	xor	$s1,$t1
+	xor	$s2,$t2
+	xor	$s3,$t3
+
+	jr	$ra
+.end	_mips_AES_encrypt
+
+.align	5
+.globl	AES_encrypt
+.ent	AES_encrypt
+AES_encrypt:
+	.frame	$sp,$FRAMESIZE,$ra
+	.mask	$SAVED_REGS_MASK,-$SZREG
+	.set	noreorder
+___
+$code.=<<___ if ($flavour =~ /o32/i);	# o32 PIC-ification
+	.cpload	$pf
+___
+$code.=<<___;
+	$PTR_SUB $sp,$FRAMESIZE
+	$REG_S	$ra,$FRAMESIZE-1*$SZREG($sp)
+	$REG_S	$fp,$FRAMESIZE-2*$SZREG($sp)
+	$REG_S	$s11,$FRAMESIZE-3*$SZREG($sp)
+	$REG_S	$s10,$FRAMESIZE-4*$SZREG($sp)
+	$REG_S	$s9,$FRAMESIZE-5*$SZREG($sp)
+	$REG_S	$s8,$FRAMESIZE-6*$SZREG($sp)
+	$REG_S	$s7,$FRAMESIZE-7*$SZREG($sp)
+	$REG_S	$s6,$FRAMESIZE-8*$SZREG($sp)
+	$REG_S	$s5,$FRAMESIZE-9*$SZREG($sp)
+	$REG_S	$s4,$FRAMESIZE-10*$SZREG($sp)
+___
+$code.=<<___ if ($flavour =~ /nubi/i);	# optimize non-nubi prologue
+	$REG_S	\$15,$FRAMESIZE-11*$SZREG($sp)
+	$REG_S	\$14,$FRAMESIZE-12*$SZREG($sp)
+	$REG_S	\$13,$FRAMESIZE-13*$SZREG($sp)
+	$REG_S	\$12,$FRAMESIZE-14*$SZREG($sp)
+	$REG_S	$gp,$FRAMESIZE-15*$SZREG($sp)
+___
+$code.=<<___ if ($flavour !~ /o32/i);	# non-o32 PIC-ification
+	.cplocal	$Tbl
+	.cpsetup	$pf,$zero,AES_encrypt
+___
+$code.=<<___;
+	.set	reorder
+	$PTR_LA	$Tbl,AES_Te		# PIC-ified 'load address'
+
+	lwl	$s0,0+$MSB($inp)
+	lwl	$s1,4+$MSB($inp)
+	lwl	$s2,8+$MSB($inp)
+	lwl	$s3,12+$MSB($inp)
+	lwr	$s0,0+$LSB($inp)
+	lwr	$s1,4+$LSB($inp)
+	lwr	$s2,8+$LSB($inp)
+	lwr	$s3,12+$LSB($inp)
+
+	bal	_mips_AES_encrypt
+
+	swr	$s0,0+$LSB($out)
+	swr	$s1,4+$LSB($out)
+	swr	$s2,8+$LSB($out)
+	swr	$s3,12+$LSB($out)
+	swl	$s0,0+$MSB($out)
+	swl	$s1,4+$MSB($out)
+	swl	$s2,8+$MSB($out)
+	swl	$s3,12+$MSB($out)
+
+	.set	noreorder
+	$REG_L	$ra,$FRAMESIZE-1*$SZREG($sp)
+	$REG_L	$fp,$FRAMESIZE-2*$SZREG($sp)
+	$REG_L	$s11,$FRAMESIZE-3*$SZREG($sp)
+	$REG_L	$s10,$FRAMESIZE-4*$SZREG($sp)
+	$REG_L	$s9,$FRAMESIZE-5*$SZREG($sp)
+	$REG_L	$s8,$FRAMESIZE-6*$SZREG($sp)
+	$REG_L	$s7,$FRAMESIZE-7*$SZREG($sp)
+	$REG_L	$s6,$FRAMESIZE-8*$SZREG($sp)
+	$REG_L	$s5,$FRAMESIZE-9*$SZREG($sp)
+	$REG_L	$s4,$FRAMESIZE-10*$SZREG($sp)
+___
+$code.=<<___ if ($flavour =~ /nubi/i);
+	$REG_L	\$15,$FRAMESIZE-11*$SZREG($sp)
+	$REG_L	\$14,$FRAMESIZE-12*$SZREG($sp)
+	$REG_L	\$13,$FRAMESIZE-13*$SZREG($sp)
+	$REG_L	\$12,$FRAMESIZE-14*$SZREG($sp)
+	$REG_L	$gp,$FRAMESIZE-15*$SZREG($sp)
+___
+$code.=<<___;
+	jr	$ra
+	$PTR_ADD $sp,$FRAMESIZE
+.end	AES_encrypt
+___
+
+$code.=<<___;
+.align	5
+.ent	_mips_AES_decrypt
+_mips_AES_decrypt:
+	.frame	$sp,0,$ra
+	.set	reorder
+	lw	$t0,0($key)
+	lw	$t1,4($key)
+	lw	$t2,8($key)
+	lw	$t3,12($key)
+	lw	$cnt,240($key)
+	$PTR_ADD $key0,$key,16
+
+	xor	$s0,$t0
+	xor	$s1,$t1
+	xor	$s2,$t2
+	xor	$s3,$t3
+
+	sub	$cnt,1
+#if defined(__mips_smartmips)
+	ext	$i0,$s3,16,8
+.Loop_dec:
+	ext	$i1,$s0,16,8
+	ext	$i2,$s1,16,8
+	ext	$i3,$s2,16,8
+	lwxs	$t0,$i0($Tbl)		# Td1[s3>>16]
+	ext	$i0,$s2,8,8
+	lwxs	$t1,$i1($Tbl)		# Td1[s0>>16]
+	ext	$i1,$s3,8,8
+	lwxs	$t2,$i2($Tbl)		# Td1[s1>>16]
+	ext	$i2,$s0,8,8
+	lwxs	$t3,$i3($Tbl)		# Td1[s2>>16]
+	ext	$i3,$s1,8,8
+
+	lwxs	$t4,$i0($Tbl)		# Td2[s2>>8]
+	ext	$i0,$s1,0,8
+	lwxs	$t5,$i1($Tbl)		# Td2[s3>>8]
+	ext	$i1,$s2,0,8
+	lwxs	$t6,$i2($Tbl)		# Td2[s0>>8]
+	ext	$i2,$s3,0,8
+	lwxs	$t7,$i3($Tbl)		# Td2[s1>>8]
+	ext	$i3,$s0,0,8
+
+	lwxs	$t8,$i0($Tbl)		# Td3[s1]
+	ext	$i0,$s0,24,8
+	lwxs	$t9,$i1($Tbl)		# Td3[s2]
+	ext	$i1,$s1,24,8
+	lwxs	$t10,$i2($Tbl)		# Td3[s3]
+	ext	$i2,$s2,24,8
+	lwxs	$t11,$i3($Tbl)		# Td3[s0]
+	ext	$i3,$s3,24,8
+
+	rotr	$t0,$t0,8
+	rotr	$t1,$t1,8
+	rotr	$t2,$t2,8
+	rotr	$t3,$t3,8
+
+	rotr	$t4,$t4,16
+	rotr	$t5,$t5,16
+	rotr	$t6,$t6,16
+	rotr	$t7,$t7,16
+
+	xor	$t0,$t4
+	lwxs	$t4,$i0($Tbl)		# Td0[s0>>24]
+	xor	$t1,$t5
+	lwxs	$t5,$i1($Tbl)		# Td0[s1>>24]
+	xor	$t2,$t6
+	lwxs	$t6,$i2($Tbl)		# Td0[s2>>24]
+	xor	$t3,$t7
+	lwxs	$t7,$i3($Tbl)		# Td0[s3>>24]
+
+	rotr	$t8,$t8,24
+	lw	$s0,0($key0)
+	rotr	$t9,$t9,24
+	lw	$s1,4($key0)
+	rotr	$t10,$t10,24
+	lw	$s2,8($key0)
+	rotr	$t11,$t11,24
+	lw	$s3,12($key0)
+
+	xor	$t0,$t8
+	xor	$t1,$t9
+	xor	$t2,$t10
+	xor	$t3,$t11
+
+	xor	$t0,$t4
+	xor	$t1,$t5
+	xor	$t2,$t6
+	xor	$t3,$t7
+
+	sub	$cnt,1
+	$PTR_ADD $key0,16
+	xor	$s0,$t0
+	xor	$s1,$t1
+	xor	$s2,$t2
+	xor	$s3,$t3
+	.set	noreorder
+	bnez	$cnt,.Loop_dec
+	ext	$i0,$s3,16,8
+
+	_xtr	$i0,$s3,16-2
+#else
+	_xtr	$i0,$s3,16-2
+.Loop_dec:
+	_xtr	$i1,$s0,16-2
+	_xtr	$i2,$s1,16-2
+	_xtr	$i3,$s2,16-2
+	and	$i0,0x3fc
+	and	$i1,0x3fc
+	and	$i2,0x3fc
+	and	$i3,0x3fc
+	$PTR_ADD $i0,$Tbl
+	$PTR_ADD $i1,$Tbl
+	$PTR_ADD $i2,$Tbl
+	$PTR_ADD $i3,$Tbl
+#if defined(_MIPS_ARCH_MIPS32R2) || defined(_MIPS_ARCH_MIPS64R2)
+	lw	$t0,0($i0)		# Td1[s3>>16]
+	_xtr	$i0,$s2,8-2
+	lw	$t1,0($i1)		# Td1[s0>>16]
+	_xtr	$i1,$s3,8-2
+	lw	$t2,0($i2)		# Td1[s1>>16]
+	_xtr	$i2,$s0,8-2
+	lw	$t3,0($i3)		# Td1[s2>>16]
+	_xtr	$i3,$s1,8-2
+#else
+	lwl	$t0,3($i0)		# Td1[s3>>16]
+	lwl	$t1,3($i1)		# Td1[s0>>16]
+	lwl	$t2,3($i2)		# Td1[s1>>16]
+	lwl	$t3,3($i3)		# Td1[s2>>16]
+	lwr	$t0,2($i0)		# Td1[s3>>16]
+	_xtr	$i0,$s2,8-2
+	lwr	$t1,2($i1)		# Td1[s0>>16]
+	_xtr	$i1,$s3,8-2
+	lwr	$t2,2($i2)		# Td1[s1>>16]
+	_xtr	$i2,$s0,8-2
+	lwr	$t3,2($i3)		# Td1[s2>>16]
+	_xtr	$i3,$s1,8-2
+#endif
+
+	and	$i0,0x3fc
+	and	$i1,0x3fc
+	and	$i2,0x3fc
+	and	$i3,0x3fc
+	$PTR_ADD $i0,$Tbl
+	$PTR_ADD $i1,$Tbl
+	$PTR_ADD $i2,$Tbl
+	$PTR_ADD $i3,$Tbl
+#if defined(_MIPS_ARCH_MIPS32R2) || defined(_MIPS_ARCH_MIPS64R2)
+	rotr	$t0,$t0,8
+	rotr	$t1,$t1,8
+	rotr	$t2,$t2,8
+	rotr	$t3,$t3,8
+# if defined(_MIPSEL)
+	lw	$t4,0($i0)		# Td2[s2>>8]
+	_xtr	$i0,$s1,0-2
+	lw	$t5,0($i1)		# Td2[s3>>8]
+	_xtr	$i1,$s2,0-2
+	lw	$t6,0($i2)		# Td2[s0>>8]
+	_xtr	$i2,$s3,0-2
+	lw	$t7,0($i3)		# Td2[s1>>8]
+	_xtr	$i3,$s0,0-2
+
+	and	$i0,0x3fc
+	and	$i1,0x3fc
+	and	$i2,0x3fc
+	and	$i3,0x3fc
+	$PTR_ADD $i0,$Tbl
+	$PTR_ADD $i1,$Tbl
+	$PTR_ADD $i2,$Tbl
+	$PTR_ADD $i3,$Tbl
+	lw	$t8,0($i0)		# Td3[s1]
+	$PTR_INS $i0,$s0,2,8
+	lw	$t9,0($i1)		# Td3[s2]
+	$PTR_INS $i1,$s1,2,8
+	lw	$t10,0($i2)		# Td3[s3]
+	$PTR_INS $i2,$s2,2,8
+	lw	$t11,0($i3)		# Td3[s0]
+	$PTR_INS $i3,$s3,2,8
+#else
+	lw	$t4,0($i0)		# Td2[s2>>8]
+	$PTR_INS $i0,$s1,2,8
+	lw	$t5,0($i1)		# Td2[s3>>8]
+	$PTR_INS $i1,$s2,2,8
+	lw	$t6,0($i2)		# Td2[s0>>8]
+	$PTR_INS $i2,$s3,2,8
+	lw	$t7,0($i3)		# Td2[s1>>8]
+	$PTR_INS $i3,$s0,2,8
+
+	lw	$t8,0($i0)		# Td3[s1]
+	_xtr	$i0,$s0,24-2
+	lw	$t9,0($i1)		# Td3[s2]
+	_xtr	$i1,$s1,24-2
+	lw	$t10,0($i2)		# Td3[s3]
+	_xtr	$i2,$s2,24-2
+	lw	$t11,0($i3)		# Td3[s0]
+	_xtr	$i3,$s3,24-2
+
+	and	$i0,0x3fc
+	and	$i1,0x3fc
+	and	$i2,0x3fc
+	and	$i3,0x3fc
+	$PTR_ADD $i0,$Tbl
+	$PTR_ADD $i1,$Tbl
+	$PTR_ADD $i2,$Tbl
+	$PTR_ADD $i3,$Tbl
+#endif
+	rotr	$t4,$t4,16
+	rotr	$t5,$t5,16
+	rotr	$t6,$t6,16
+	rotr	$t7,$t7,16
+
+	rotr	$t8,$t8,24
+	rotr	$t9,$t9,24
+	rotr	$t10,$t10,24
+	rotr	$t11,$t11,24
+#else
+	lwl	$t4,2($i0)		# Td2[s2>>8]
+	lwl	$t5,2($i1)		# Td2[s3>>8]
+	lwl	$t6,2($i2)		# Td2[s0>>8]
+	lwl	$t7,2($i3)		# Td2[s1>>8]
+	lwr	$t4,1($i0)		# Td2[s2>>8]
+	_xtr	$i0,$s1,0-2
+	lwr	$t5,1($i1)		# Td2[s3>>8]
+	_xtr	$i1,$s2,0-2
+	lwr	$t6,1($i2)		# Td2[s0>>8]
+	_xtr	$i2,$s3,0-2
+	lwr	$t7,1($i3)		# Td2[s1>>8]
+	_xtr	$i3,$s0,0-2
+
+	and	$i0,0x3fc
+	and	$i1,0x3fc
+	and	$i2,0x3fc
+	and	$i3,0x3fc
+	$PTR_ADD $i0,$Tbl
+	$PTR_ADD $i1,$Tbl
+	$PTR_ADD $i2,$Tbl
+	$PTR_ADD $i3,$Tbl
+	lwl	$t8,1($i0)		# Td3[s1]
+	lwl	$t9,1($i1)		# Td3[s2]
+	lwl	$t10,1($i2)		# Td3[s3]
+	lwl	$t11,1($i3)		# Td3[s0]
+	lwr	$t8,0($i0)		# Td3[s1]
+	_xtr	$i0,$s0,24-2
+	lwr	$t9,0($i1)		# Td3[s2]
+	_xtr	$i1,$s1,24-2
+	lwr	$t10,0($i2)		# Td3[s3]
+	_xtr	$i2,$s2,24-2
+	lwr	$t11,0($i3)		# Td3[s0]
+	_xtr	$i3,$s3,24-2
+
+	and	$i0,0x3fc
+	and	$i1,0x3fc
+	and	$i2,0x3fc
+	and	$i3,0x3fc
+	$PTR_ADD $i0,$Tbl
+	$PTR_ADD $i1,$Tbl
+	$PTR_ADD $i2,$Tbl
+	$PTR_ADD $i3,$Tbl
+#endif
+
+	xor	$t0,$t4
+	lw	$t4,0($i0)		# Td0[s0>>24]
+	xor	$t1,$t5
+	lw	$t5,0($i1)		# Td0[s1>>24]
+	xor	$t2,$t6
+	lw	$t6,0($i2)		# Td0[s2>>24]
+	xor	$t3,$t7
+	lw	$t7,0($i3)		# Td0[s3>>24]
+
+	xor	$t0,$t8
+	lw	$s0,0($key0)
+	xor	$t1,$t9
+	lw	$s1,4($key0)
+	xor	$t2,$t10
+	lw	$s2,8($key0)
+	xor	$t3,$t11
+	lw	$s3,12($key0)
+
+	xor	$t0,$t4
+	xor	$t1,$t5
+	xor	$t2,$t6
+	xor	$t3,$t7
+
+	sub	$cnt,1
+	$PTR_ADD $key0,16
+	xor	$s0,$t0
+	xor	$s1,$t1
+	xor	$s2,$t2
+	xor	$s3,$t3
+	.set	noreorder
+	bnez	$cnt,.Loop_dec
+	_xtr	$i0,$s3,16-2
+#endif
+
+	.set	reorder
+	lw	$t4,1024($Tbl)		# prefetch Td4
+	_xtr	$i0,$s3,16
+	lw	$t5,1024+32($Tbl)
+	_xtr	$i1,$s0,16
+	lw	$t6,1024+64($Tbl)
+	_xtr	$i2,$s1,16
+	lw	$t7,1024+96($Tbl)
+	_xtr	$i3,$s2,16
+	lw	$t8,1024+128($Tbl)
+	and	$i0,0xff
+	lw	$t9,1024+160($Tbl)
+	and	$i1,0xff
+	lw	$t10,1024+192($Tbl)
+	and	$i2,0xff
+	lw	$t11,1024+224($Tbl)
+	and	$i3,0xff
+
+	$PTR_ADD $i0,$Tbl
+	$PTR_ADD $i1,$Tbl
+	$PTR_ADD $i2,$Tbl
+	$PTR_ADD $i3,$Tbl
+	lbu	$t0,1024($i0)		# Td4[s3>>16]
+	_xtr	$i0,$s2,8
+	lbu	$t1,1024($i1)		# Td4[s0>>16]
+	_xtr	$i1,$s3,8
+	lbu	$t2,1024($i2)		# Td4[s1>>16]
+	_xtr	$i2,$s0,8
+	lbu	$t3,1024($i3)		# Td4[s2>>16]
+	_xtr	$i3,$s1,8
+
+	and	$i0,0xff
+	and	$i1,0xff
+	and	$i2,0xff
+	and	$i3,0xff
+	$PTR_ADD $i0,$Tbl
+	$PTR_ADD $i1,$Tbl
+	$PTR_ADD $i2,$Tbl
+	$PTR_ADD $i3,$Tbl
+#if defined(_MIPS_ARCH_MIPS32R2) || defined(_MIPS_ARCH_MIPS64R2)
+# if defined(_MIPSEL)
+	lbu	$t4,1024($i0)		# Td4[s2>>8]
+	$PTR_INS $i0,$s0,0,8
+	lbu	$t5,1024($i1)		# Td4[s3>>8]
+	$PTR_INS $i1,$s1,0,8
+	lbu	$t6,1024($i2)		# Td4[s0>>8]
+	$PTR_INS $i2,$s2,0,8
+	lbu	$t7,1024($i3)		# Td4[s1>>8]
+	$PTR_INS $i3,$s3,0,8
+
+	lbu	$t8,1024($i0)		# Td4[s0>>24]
+	_xtr	$i0,$s1,0
+	lbu	$t9,1024($i1)		# Td4[s1>>24]
+	_xtr	$i1,$s2,0
+	lbu	$t10,1024($i2)		# Td4[s2>>24]
+	_xtr	$i2,$s3,0
+	lbu	$t11,1024($i3)		# Td4[s3>>24]
+	_xtr	$i3,$s0,0
+
+	$PTR_ADD $i0,$Tbl
+	$PTR_ADD $i1,$Tbl
+	$PTR_ADD $i2,$Tbl
+	$PTR_ADD $i3,$Tbl
+# else
+	lbu	$t4,1024($i0)		# Td4[s2>>8]
+	_xtr	$i0,$s0,24
+	lbu	$t5,1024($i1)		# Td4[s3>>8]
+	_xtr	$i1,$s1,24
+	lbu	$t6,1024($i2)		# Td4[s0>>8]
+	_xtr	$i2,$s2,24
+	lbu	$t7,1024($i3)		# Td4[s1>>8]
+	_xtr	$i3,$s3,24
+
+	$PTR_ADD $i0,$Tbl
+	$PTR_ADD $i1,$Tbl
+	$PTR_ADD $i2,$Tbl
+	$PTR_ADD $i3,$Tbl
+	lbu	$t8,1024($i0)		# Td4[s0>>24]
+	$PTR_INS $i0,$s1,0,8
+	lbu	$t9,1024($i1)		# Td4[s1>>24]
+	$PTR_INS $i1,$s2,0,8
+	lbu	$t10,1024($i2)		# Td4[s2>>24]
+	$PTR_INS $i2,$s3,0,8
+	lbu	$t11,1024($i3)		# Td4[s3>>24]
+	$PTR_INS $i3,$s0,0,8
+# endif
+	_ins	$t0,16
+	_ins	$t1,16
+	_ins	$t2,16
+	_ins	$t3,16
+
+	_ins2	$t0,$t4,8
+	lbu	$t4,1024($i0)		# Td4[s1]
+	_ins2	$t1,$t5,8
+	lbu	$t5,1024($i1)		# Td4[s2]
+	_ins2	$t2,$t6,8
+	lbu	$t6,1024($i2)		# Td4[s3]
+	_ins2	$t3,$t7,8
+	lbu	$t7,1024($i3)		# Td4[s0]
+
+	_ins2	$t0,$t8,24
+	lw	$s0,0($key0)
+	_ins2	$t1,$t9,24
+	lw	$s1,4($key0)
+	_ins2	$t2,$t10,24
+	lw	$s2,8($key0)
+	_ins2	$t3,$t11,24
+	lw	$s3,12($key0)
+
+	_ins2	$t0,$t4,0
+	_ins2	$t1,$t5,0
+	_ins2	$t2,$t6,0
+	_ins2	$t3,$t7,0
+#else
+	lbu	$t4,1024($i0)		# Td4[s2>>8]
+	_xtr	$i0,$s0,24
+	lbu	$t5,1024($i1)		# Td4[s3>>8]
+	_xtr	$i1,$s1,24
+	lbu	$t6,1024($i2)		# Td4[s0>>8]
+	_xtr	$i2,$s2,24
+	lbu	$t7,1024($i3)		# Td4[s1>>8]
+	_xtr	$i3,$s3,24
+
+	$PTR_ADD $i0,$Tbl
+	$PTR_ADD $i1,$Tbl
+	$PTR_ADD $i2,$Tbl
+	$PTR_ADD $i3,$Tbl
+	lbu	$t8,1024($i0)		# Td4[s0>>24]
+	_xtr	$i0,$s1,0
+	lbu	$t9,1024($i1)		# Td4[s1>>24]
+	_xtr	$i1,$s2,0
+	lbu	$t10,1024($i2)		# Td4[s2>>24]
+	_xtr	$i2,$s3,0
+	lbu	$t11,1024($i3)		# Td4[s3>>24]
+	_xtr	$i3,$s0,0
+
+	$PTR_ADD $i0,$Tbl
+	$PTR_ADD $i1,$Tbl
+	$PTR_ADD $i2,$Tbl
+	$PTR_ADD $i3,$Tbl
+
+	_ins	$t0,16
+	_ins	$t1,16
+	_ins	$t2,16
+	_ins	$t3,16
+
+	_ins	$t4,8
+	_ins	$t5,8
+	_ins	$t6,8
+	_ins	$t7,8
+
+	xor	$t0,$t4
+	lbu	$t4,1024($i0)		# Td4[s1]
+	xor	$t1,$t5
+	lbu	$t5,1024($i1)		# Td4[s2]
+	xor	$t2,$t6
+	lbu	$t6,1024($i2)		# Td4[s3]
+	xor	$t3,$t7
+	lbu	$t7,1024($i3)		# Td4[s0]
+
+	_ins	$t8,24
+	lw	$s0,0($key0)
+	_ins	$t9,24
+	lw	$s1,4($key0)
+	_ins	$t10,24
+	lw	$s2,8($key0)
+	_ins	$t11,24
+	lw	$s3,12($key0)
+
+	xor	$t0,$t8
+	xor	$t1,$t9
+	xor	$t2,$t10
+	xor	$t3,$t11
+
+	_ins	$t4,0
+	_ins	$t5,0
+	_ins	$t6,0
+	_ins	$t7,0
+
+	xor	$t0,$t4
+	xor	$t1,$t5
+	xor	$t2,$t6
+	xor	$t3,$t7
+#endif
+
+	xor	$s0,$t0
+	xor	$s1,$t1
+	xor	$s2,$t2
+	xor	$s3,$t3
+
+	jr	$ra
+.end	_mips_AES_decrypt
+
+.align	5
+.globl	AES_decrypt
+.ent	AES_decrypt
+AES_decrypt:
+	.frame	$sp,$FRAMESIZE,$ra
+	.mask	$SAVED_REGS_MASK,-$SZREG
+	.set	noreorder
+___
+$code.=<<___ if ($flavour =~ /o32/i);	# o32 PIC-ification
+	.cpload	$pf
+___
+$code.=<<___;
+	$PTR_SUB $sp,$FRAMESIZE
+	$REG_S	$ra,$FRAMESIZE-1*$SZREG($sp)
+	$REG_S	$fp,$FRAMESIZE-2*$SZREG($sp)
+	$REG_S	$s11,$FRAMESIZE-3*$SZREG($sp)
+	$REG_S	$s10,$FRAMESIZE-4*$SZREG($sp)
+	$REG_S	$s9,$FRAMESIZE-5*$SZREG($sp)
+	$REG_S	$s8,$FRAMESIZE-6*$SZREG($sp)
+	$REG_S	$s7,$FRAMESIZE-7*$SZREG($sp)
+	$REG_S	$s6,$FRAMESIZE-8*$SZREG($sp)
+	$REG_S	$s5,$FRAMESIZE-9*$SZREG($sp)
+	$REG_S	$s4,$FRAMESIZE-10*$SZREG($sp)
+___
+$code.=<<___ if ($flavour =~ /nubi/i);	# optimize non-nubi prologue
+	$REG_S	\$15,$FRAMESIZE-11*$SZREG($sp)
+	$REG_S	\$14,$FRAMESIZE-12*$SZREG($sp)
+	$REG_S	\$13,$FRAMESIZE-13*$SZREG($sp)
+	$REG_S	\$12,$FRAMESIZE-14*$SZREG($sp)
+	$REG_S	$gp,$FRAMESIZE-15*$SZREG($sp)
+___
+$code.=<<___ if ($flavour !~ /o32/i);	# non-o32 PIC-ification
+	.cplocal	$Tbl
+	.cpsetup	$pf,$zero,AES_decrypt
+___
+$code.=<<___;
+	.set	reorder
+	$PTR_LA	$Tbl,AES_Td		# PIC-ified 'load address'
+
+	lwl	$s0,0+$MSB($inp)
+	lwl	$s1,4+$MSB($inp)
+	lwl	$s2,8+$MSB($inp)
+	lwl	$s3,12+$MSB($inp)
+	lwr	$s0,0+$LSB($inp)
+	lwr	$s1,4+$LSB($inp)
+	lwr	$s2,8+$LSB($inp)
+	lwr	$s3,12+$LSB($inp)
+
+	bal	_mips_AES_decrypt
+
+	swr	$s0,0+$LSB($out)
+	swr	$s1,4+$LSB($out)
+	swr	$s2,8+$LSB($out)
+	swr	$s3,12+$LSB($out)
+	swl	$s0,0+$MSB($out)
+	swl	$s1,4+$MSB($out)
+	swl	$s2,8+$MSB($out)
+	swl	$s3,12+$MSB($out)
+
+	.set	noreorder
+	$REG_L	$ra,$FRAMESIZE-1*$SZREG($sp)
+	$REG_L	$fp,$FRAMESIZE-2*$SZREG($sp)
+	$REG_L	$s11,$FRAMESIZE-3*$SZREG($sp)
+	$REG_L	$s10,$FRAMESIZE-4*$SZREG($sp)
+	$REG_L	$s9,$FRAMESIZE-5*$SZREG($sp)
+	$REG_L	$s8,$FRAMESIZE-6*$SZREG($sp)
+	$REG_L	$s7,$FRAMESIZE-7*$SZREG($sp)
+	$REG_L	$s6,$FRAMESIZE-8*$SZREG($sp)
+	$REG_L	$s5,$FRAMESIZE-9*$SZREG($sp)
+	$REG_L	$s4,$FRAMESIZE-10*$SZREG($sp)
+___
+$code.=<<___ if ($flavour =~ /nubi/i);
+	$REG_L	\$15,$FRAMESIZE-11*$SZREG($sp)
+	$REG_L	\$14,$FRAMESIZE-12*$SZREG($sp)
+	$REG_L	\$13,$FRAMESIZE-13*$SZREG($sp)
+	$REG_L	\$12,$FRAMESIZE-14*$SZREG($sp)
+	$REG_L	$gp,$FRAMESIZE-15*$SZREG($sp)
+___
+$code.=<<___;
+	jr	$ra
+	$PTR_ADD $sp,$FRAMESIZE
+.end	AES_decrypt
+___
+}}}
+
+{{{
+my $FRAMESIZE=8*$SZREG;
+my $SAVED_REGS_MASK = ($flavour =~ /nubi/i) ? "0xc000f008" : "0xc0000000";
+
+my ($inp,$bits,$key,$Tbl)=($a0,$a1,$a2,$a3);
+my ($rk0,$rk1,$rk2,$rk3,$rk4,$rk5,$rk6,$rk7)=($a4,$a5,$a6,$a7,$s0,$s1,$s2,$s3);
+my ($i0,$i1,$i2,$i3)=($at,$t0,$t1,$t2);
+my ($rcon,$cnt)=($gp,$fp);
+
+$code.=<<___;
+.align	5
+.ent	_mips_AES_set_encrypt_key
+_mips_AES_set_encrypt_key:
+	.frame	$sp,0,$ra
+	.set	noreorder
+	beqz	$inp,.Lekey_done
+	li	$t0,-1
+	beqz	$key,.Lekey_done
+	$PTR_ADD $rcon,$Tbl,256
+
+	.set	reorder
+	lwl	$rk0,0+$MSB($inp)	# load 128 bits
+	lwl	$rk1,4+$MSB($inp)
+	lwl	$rk2,8+$MSB($inp)
+	lwl	$rk3,12+$MSB($inp)
+	li	$at,128
+	lwr	$rk0,0+$LSB($inp)
+	lwr	$rk1,4+$LSB($inp)
+	lwr	$rk2,8+$LSB($inp)
+	lwr	$rk3,12+$LSB($inp)
+	.set	noreorder
+	beq	$bits,$at,.L128bits
+	li	$cnt,10
+
+	.set	reorder
+	lwl	$rk4,16+$MSB($inp)	# load 192 bits
+	lwl	$rk5,20+$MSB($inp)
+	li	$at,192
+	lwr	$rk4,16+$LSB($inp)
+	lwr	$rk5,20+$LSB($inp)
+	.set	noreorder
+	beq	$bits,$at,.L192bits
+	li	$cnt,8
+
+	.set	reorder
+	lwl	$rk6,24+$MSB($inp)	# load 256 bits
+	lwl	$rk7,28+$MSB($inp)
+	li	$at,256
+	lwr	$rk6,24+$LSB($inp)
+	lwr	$rk7,28+$LSB($inp)
+	.set	noreorder
+	beq	$bits,$at,.L256bits
+	li	$cnt,7
+
+	b	.Lekey_done
+	li	$t0,-2
+
+.align	4
+.L128bits:
+	.set	reorder
+	srl	$i0,$rk3,16
+	srl	$i1,$rk3,8
+	and	$i0,0xff
+	and	$i1,0xff
+	and	$i2,$rk3,0xff
+	srl	$i3,$rk3,24
+	$PTR_ADD $i0,$Tbl
+	$PTR_ADD $i1,$Tbl
+	$PTR_ADD $i2,$Tbl
+	$PTR_ADD $i3,$Tbl
+	lbu	$i0,0($i0)
+	lbu	$i1,0($i1)
+	lbu	$i2,0($i2)
+	lbu	$i3,0($i3)
+
+	sw	$rk0,0($key)
+	sw	$rk1,4($key)
+	sw	$rk2,8($key)
+	sw	$rk3,12($key)
+	sub	$cnt,1
+	$PTR_ADD $key,16
+
+	_bias	$i0,24
+	_bias	$i1,16
+	_bias	$i2,8
+	_bias	$i3,0
+
+	xor	$rk0,$i0
+	lw	$i0,0($rcon)
+	xor	$rk0,$i1
+	xor	$rk0,$i2
+	xor	$rk0,$i3
+	xor	$rk0,$i0
+
+	xor	$rk1,$rk0
+	xor	$rk2,$rk1
+	xor	$rk3,$rk2
+
+	.set	noreorder
+	bnez	$cnt,.L128bits
+	$PTR_ADD $rcon,4
+
+	sw	$rk0,0($key)
+	sw	$rk1,4($key)
+	sw	$rk2,8($key)
+	li	$cnt,10
+	sw	$rk3,12($key)
+	li	$t0,0
+	sw	$cnt,80($key)
+	b	.Lekey_done
+	$PTR_SUB $key,10*16
+
+.align	4
+.L192bits:
+	.set	reorder
+	srl	$i0,$rk5,16
+	srl	$i1,$rk5,8
+	and	$i0,0xff
+	and	$i1,0xff
+	and	$i2,$rk5,0xff
+	srl	$i3,$rk5,24
+	$PTR_ADD $i0,$Tbl
+	$PTR_ADD $i1,$Tbl
+	$PTR_ADD $i2,$Tbl
+	$PTR_ADD $i3,$Tbl
+	lbu	$i0,0($i0)
+	lbu	$i1,0($i1)
+	lbu	$i2,0($i2)
+	lbu	$i3,0($i3)
+
+	sw	$rk0,0($key)
+	sw	$rk1,4($key)
+	sw	$rk2,8($key)
+	sw	$rk3,12($key)
+	sw	$rk4,16($key)
+	sw	$rk5,20($key)
+	sub	$cnt,1
+	$PTR_ADD $key,24
+
+	_bias	$i0,24
+	_bias	$i1,16
+	_bias	$i2,8
+	_bias	$i3,0
+
+	xor	$rk0,$i0
+	lw	$i0,0($rcon)
+	xor	$rk0,$i1
+	xor	$rk0,$i2
+	xor	$rk0,$i3
+	xor	$rk0,$i0
+
+	xor	$rk1,$rk0
+	xor	$rk2,$rk1
+	xor	$rk3,$rk2
+	xor	$rk4,$rk3
+	xor	$rk5,$rk4
+
+	.set	noreorder
+	bnez	$cnt,.L192bits
+	$PTR_ADD $rcon,4
+
+	sw	$rk0,0($key)
+	sw	$rk1,4($key)
+	sw	$rk2,8($key)
+	li	$cnt,12
+	sw	$rk3,12($key)
+	li	$t0,0
+	sw	$cnt,48($key)
+	b	.Lekey_done
+	$PTR_SUB $key,12*16
+
+.align	4
+.L256bits:
+	.set	reorder
+	srl	$i0,$rk7,16
+	srl	$i1,$rk7,8
+	and	$i0,0xff
+	and	$i1,0xff
+	and	$i2,$rk7,0xff
+	srl	$i3,$rk7,24
+	$PTR_ADD $i0,$Tbl
+	$PTR_ADD $i1,$Tbl
+	$PTR_ADD $i2,$Tbl
+	$PTR_ADD $i3,$Tbl
+	lbu	$i0,0($i0)
+	lbu	$i1,0($i1)
+	lbu	$i2,0($i2)
+	lbu	$i3,0($i3)
+
+	sw	$rk0,0($key)
+	sw	$rk1,4($key)
+	sw	$rk2,8($key)
+	sw	$rk3,12($key)
+	sw	$rk4,16($key)
+	sw	$rk5,20($key)
+	sw	$rk6,24($key)
+	sw	$rk7,28($key)
+	sub	$cnt,1
+
+	_bias	$i0,24
+	_bias	$i1,16
+	_bias	$i2,8
+	_bias	$i3,0
+
+	xor	$rk0,$i0
+	lw	$i0,0($rcon)
+	xor	$rk0,$i1
+	xor	$rk0,$i2
+	xor	$rk0,$i3
+	xor	$rk0,$i0
+
+	xor	$rk1,$rk0
+	xor	$rk2,$rk1
+	xor	$rk3,$rk2
+	beqz	$cnt,.L256bits_done
+
+	srl	$i0,$rk3,24
+	srl	$i1,$rk3,16
+	srl	$i2,$rk3,8
+	and	$i3,$rk3,0xff
+	and	$i1,0xff
+	and	$i2,0xff
+	$PTR_ADD $i0,$Tbl
+	$PTR_ADD $i1,$Tbl
+	$PTR_ADD $i2,$Tbl
+	$PTR_ADD $i3,$Tbl
+	lbu	$i0,0($i0)
+	lbu	$i1,0($i1)
+	lbu	$i2,0($i2)
+	lbu	$i3,0($i3)
+	sll	$i0,24
+	sll	$i1,16
+	sll	$i2,8
+
+	xor	$rk4,$i0
+	xor	$rk4,$i1
+	xor	$rk4,$i2
+	xor	$rk4,$i3
+
+	xor	$rk5,$rk4
+	xor	$rk6,$rk5
+	xor	$rk7,$rk6
+
+	$PTR_ADD $key,32
+	.set	noreorder
+	b	.L256bits
+	$PTR_ADD $rcon,4
+
+.L256bits_done:
+	sw	$rk0,32($key)
+	sw	$rk1,36($key)
+	sw	$rk2,40($key)
+	li	$cnt,14
+	sw	$rk3,44($key)
+	li	$t0,0
+	sw	$cnt,48($key)
+	$PTR_SUB $key,12*16
+
+.Lekey_done:
+	jr	$ra
+	nop
+.end	_mips_AES_set_encrypt_key
+
+.globl	AES_set_encrypt_key
+.ent	AES_set_encrypt_key
+AES_set_encrypt_key:
+	.frame	$sp,$FRAMESIZE,$ra
+	.mask	$SAVED_REGS_MASK,-$SZREG
+	.set	noreorder
+___
+$code.=<<___ if ($flavour =~ /o32/i);	# o32 PIC-ification
+	.cpload	$pf
+___
+$code.=<<___;
+	$PTR_SUB $sp,$FRAMESIZE
+	$REG_S	$ra,$FRAMESIZE-1*$SZREG($sp)
+	$REG_S	$fp,$FRAMESIZE-2*$SZREG($sp)
+___
+$code.=<<___ if ($flavour =~ /nubi/i);	# optimize non-nubi prologue
+	$REG_S	$s3,$FRAMESIZE-3*$SZREG($sp)
+	$REG_S	$s2,$FRAMESIZE-4*$SZREG($sp)
+	$REG_S	$s1,$FRAMESIZE-5*$SZREG($sp)
+	$REG_S	$s0,$FRAMESIZE-6*$SZREG($sp)
+	$REG_S	$gp,$FRAMESIZE-7*$SZREG($sp)
+___
+$code.=<<___ if ($flavour !~ /o32/i);	# non-o32 PIC-ification
+	.cplocal	$Tbl
+	.cpsetup	$pf,$zero,AES_set_encrypt_key
+___
+$code.=<<___;
+	.set	reorder
+	$PTR_LA	$Tbl,AES_Te4		# PIC-ified 'load address'
+
+	bal	_mips_AES_set_encrypt_key
+
+	.set	noreorder
+	move	$a0,$t0
+	$REG_L	$ra,$FRAMESIZE-1*$SZREG($sp)
+	$REG_L	$fp,$FRAMESIZE-2*$SZREG($sp)
+___
+$code.=<<___ if ($flavour =~ /nubi/i);
+	$REG_L	$s3,$FRAMESIZE-11*$SZREG($sp)
+	$REG_L	$s2,$FRAMESIZE-12*$SZREG($sp)
+	$REG_L	$s1,$FRAMESIZE-13*$SZREG($sp)
+	$REG_L	$s0,$FRAMESIZE-14*$SZREG($sp)
+	$REG_L	$gp,$FRAMESIZE-15*$SZREG($sp)
+___
+$code.=<<___;
+	jr	$ra
+	$PTR_ADD $sp,$FRAMESIZE
+.end	AES_set_encrypt_key
+___
+
+my ($head,$tail)=($inp,$bits);
+my ($tp1,$tp2,$tp4,$tp8,$tp9,$tpb,$tpd,$tpe)=($a4,$a5,$a6,$a7,$s0,$s1,$s2,$s3);
+my ($m,$x80808080,$x7f7f7f7f,$x1b1b1b1b)=($at,$t0,$t1,$t2);
+$code.=<<___;
+.align	5
+.globl	AES_set_decrypt_key
+.ent	AES_set_decrypt_key
+AES_set_decrypt_key:
+	.frame	$sp,$FRAMESIZE,$ra
+	.mask	$SAVED_REGS_MASK,-$SZREG
+	.set	noreorder
+___
+$code.=<<___ if ($flavour =~ /o32/i);	# o32 PIC-ification
+	.cpload	$pf
+___
+$code.=<<___;
+	$PTR_SUB $sp,$FRAMESIZE
+	$REG_S	$ra,$FRAMESIZE-1*$SZREG($sp)
+	$REG_S	$fp,$FRAMESIZE-2*$SZREG($sp)
+___
+$code.=<<___ if ($flavour =~ /nubi/i);	# optimize non-nubi prologue
+	$REG_S	$s3,$FRAMESIZE-3*$SZREG($sp)
+	$REG_S	$s2,$FRAMESIZE-4*$SZREG($sp)
+	$REG_S	$s1,$FRAMESIZE-5*$SZREG($sp)
+	$REG_S	$s0,$FRAMESIZE-6*$SZREG($sp)
+	$REG_S	$gp,$FRAMESIZE-7*$SZREG($sp)
+___
+$code.=<<___ if ($flavour !~ /o32/i);	# non-o32 PIC-ification
+	.cplocal	$Tbl
+	.cpsetup	$pf,$zero,AES_set_decrypt_key
+___
+$code.=<<___;
+	.set	reorder
+	$PTR_LA	$Tbl,AES_Te4		# PIC-ified 'load address'
+
+	bal	_mips_AES_set_encrypt_key
+
+	bltz	$t0,.Ldkey_done
+
+	sll	$at,$cnt,4
+	$PTR_ADD $head,$key,0
+	$PTR_ADD $tail,$key,$at
+.align	4
+.Lswap:
+	lw	$rk0,0($head)
+	lw	$rk1,4($head)
+	lw	$rk2,8($head)
+	lw	$rk3,12($head)
+	lw	$rk4,0($tail)
+	lw	$rk5,4($tail)
+	lw	$rk6,8($tail)
+	lw	$rk7,12($tail)
+	sw	$rk0,0($tail)
+	sw	$rk1,4($tail)
+	sw	$rk2,8($tail)
+	sw	$rk3,12($tail)
+	$PTR_ADD $head,16
+	$PTR_SUB $tail,16
+	sw	$rk4,-16($head)
+	sw	$rk5,-12($head)
+	sw	$rk6,-8($head)
+	sw	$rk7,-4($head)
+	bne	$head,$tail,.Lswap
+
+	lw	$tp1,16($key)		# modulo-scheduled
+	lui	$x80808080,0x8080
+	sub	$cnt,1
+	or	$x80808080,0x8080
+	sll	$cnt,2
+	$PTR_ADD $key,16
+	lui	$x1b1b1b1b,0x1b1b
+	nor	$x7f7f7f7f,$zero,$x80808080
+	or	$x1b1b1b1b,0x1b1b
+.align	4
+.Lmix:
+	and	$m,$tp1,$x80808080
+	and	$tp2,$tp1,$x7f7f7f7f
+	srl	$tp4,$m,7
+	addu	$tp2,$tp2		# tp2<<1
+	subu	$m,$tp4
+	and	$m,$x1b1b1b1b
+	xor	$tp2,$m
+
+	and	$m,$tp2,$x80808080
+	and	$tp4,$tp2,$x7f7f7f7f
+	srl	$tp8,$m,7
+	addu	$tp4,$tp4		# tp4<<1
+	subu	$m,$tp8
+	and	$m,$x1b1b1b1b
+	xor	$tp4,$m
+
+	and	$m,$tp4,$x80808080
+	and	$tp8,$tp4,$x7f7f7f7f
+	srl	$tp9,$m,7
+	addu	$tp8,$tp8		# tp8<<1
+	subu	$m,$tp9
+	and	$m,$x1b1b1b1b
+	xor	$tp8,$m
+
+	xor	$tp9,$tp8,$tp1
+	xor	$tpe,$tp8,$tp4
+	xor	$tpb,$tp9,$tp2
+	xor	$tpd,$tp9,$tp4
+
+#if defined(_MIPS_ARCH_MIPS32R2) || defined(_MIPS_ARCH_MIPS64R2)
+	rotr	$tp1,$tpd,16
+	 xor	$tpe,$tp2
+	rotr	$tp2,$tp9,8
+	xor	$tpe,$tp1
+	rotr	$tp4,$tpb,24
+	xor	$tpe,$tp2
+	lw	$tp1,4($key)		# modulo-scheduled
+	xor	$tpe,$tp4
+#else
+	_ror	$tp1,$tpd,16
+	 xor	$tpe,$tp2
+	_ror	$tp2,$tpd,-16
+	xor	$tpe,$tp1
+	_ror	$tp1,$tp9,8
+	xor	$tpe,$tp2
+	_ror	$tp2,$tp9,-24
+	xor	$tpe,$tp1
+	_ror	$tp1,$tpb,24
+	xor	$tpe,$tp2
+	_ror	$tp2,$tpb,-8
+	xor	$tpe,$tp1
+	lw	$tp1,4($key)		# modulo-scheduled
+	xor	$tpe,$tp2
+#endif
+	sub	$cnt,1
+	sw	$tpe,0($key)
+	$PTR_ADD $key,4
+	bnez	$cnt,.Lmix
+
+	li	$t0,0
+.Ldkey_done:
+	.set	noreorder
+	move	$a0,$t0
+	$REG_L	$ra,$FRAMESIZE-1*$SZREG($sp)
+	$REG_L	$fp,$FRAMESIZE-2*$SZREG($sp)
+___
+$code.=<<___ if ($flavour =~ /nubi/i);
+	$REG_L	$s3,$FRAMESIZE-11*$SZREG($sp)
+	$REG_L	$s2,$FRAMESIZE-12*$SZREG($sp)
+	$REG_L	$s1,$FRAMESIZE-13*$SZREG($sp)
+	$REG_L	$s0,$FRAMESIZE-14*$SZREG($sp)
+	$REG_L	$gp,$FRAMESIZE-15*$SZREG($sp)
+___
+$code.=<<___;
+	jr	$ra
+	$PTR_ADD $sp,$FRAMESIZE
+.end	AES_set_decrypt_key
+___
+}}}
+
+######################################################################
+# Tables are kept in endian-neutral manner
+$code.=<<___;
+.rdata
+.align	10
+AES_Te:
+.byte	0xc6,0x63,0x63,0xa5,	0xf8,0x7c,0x7c,0x84	# Te0
+.byte	0xee,0x77,0x77,0x99,	0xf6,0x7b,0x7b,0x8d
+.byte	0xff,0xf2,0xf2,0x0d,	0xd6,0x6b,0x6b,0xbd
+.byte	0xde,0x6f,0x6f,0xb1,	0x91,0xc5,0xc5,0x54
+.byte	0x60,0x30,0x30,0x50,	0x02,0x01,0x01,0x03
+.byte	0xce,0x67,0x67,0xa9,	0x56,0x2b,0x2b,0x7d
+.byte	0xe7,0xfe,0xfe,0x19,	0xb5,0xd7,0xd7,0x62
+.byte	0x4d,0xab,0xab,0xe6,	0xec,0x76,0x76,0x9a
+.byte	0x8f,0xca,0xca,0x45,	0x1f,0x82,0x82,0x9d
+.byte	0x89,0xc9,0xc9,0x40,	0xfa,0x7d,0x7d,0x87
+.byte	0xef,0xfa,0xfa,0x15,	0xb2,0x59,0x59,0xeb
+.byte	0x8e,0x47,0x47,0xc9,	0xfb,0xf0,0xf0,0x0b
+.byte	0x41,0xad,0xad,0xec,	0xb3,0xd4,0xd4,0x67
+.byte	0x5f,0xa2,0xa2,0xfd,	0x45,0xaf,0xaf,0xea
+.byte	0x23,0x9c,0x9c,0xbf,	0x53,0xa4,0xa4,0xf7
+.byte	0xe4,0x72,0x72,0x96,	0x9b,0xc0,0xc0,0x5b
+.byte	0x75,0xb7,0xb7,0xc2,	0xe1,0xfd,0xfd,0x1c
+.byte	0x3d,0x93,0x93,0xae,	0x4c,0x26,0x26,0x6a
+.byte	0x6c,0x36,0x36,0x5a,	0x7e,0x3f,0x3f,0x41
+.byte	0xf5,0xf7,0xf7,0x02,	0x83,0xcc,0xcc,0x4f
+.byte	0x68,0x34,0x34,0x5c,	0x51,0xa5,0xa5,0xf4
+.byte	0xd1,0xe5,0xe5,0x34,	0xf9,0xf1,0xf1,0x08
+.byte	0xe2,0x71,0x71,0x93,	0xab,0xd8,0xd8,0x73
+.byte	0x62,0x31,0x31,0x53,	0x2a,0x15,0x15,0x3f
+.byte	0x08,0x04,0x04,0x0c,	0x95,0xc7,0xc7,0x52
+.byte	0x46,0x23,0x23,0x65,	0x9d,0xc3,0xc3,0x5e
+.byte	0x30,0x18,0x18,0x28,	0x37,0x96,0x96,0xa1
+.byte	0x0a,0x05,0x05,0x0f,	0x2f,0x9a,0x9a,0xb5
+.byte	0x0e,0x07,0x07,0x09,	0x24,0x12,0x12,0x36
+.byte	0x1b,0x80,0x80,0x9b,	0xdf,0xe2,0xe2,0x3d
+.byte	0xcd,0xeb,0xeb,0x26,	0x4e,0x27,0x27,0x69
+.byte	0x7f,0xb2,0xb2,0xcd,	0xea,0x75,0x75,0x9f
+.byte	0x12,0x09,0x09,0x1b,	0x1d,0x83,0x83,0x9e
+.byte	0x58,0x2c,0x2c,0x74,	0x34,0x1a,0x1a,0x2e
+.byte	0x36,0x1b,0x1b,0x2d,	0xdc,0x6e,0x6e,0xb2
+.byte	0xb4,0x5a,0x5a,0xee,	0x5b,0xa0,0xa0,0xfb
+.byte	0xa4,0x52,0x52,0xf6,	0x76,0x3b,0x3b,0x4d
+.byte	0xb7,0xd6,0xd6,0x61,	0x7d,0xb3,0xb3,0xce
+.byte	0x52,0x29,0x29,0x7b,	0xdd,0xe3,0xe3,0x3e
+.byte	0x5e,0x2f,0x2f,0x71,	0x13,0x84,0x84,0x97
+.byte	0xa6,0x53,0x53,0xf5,	0xb9,0xd1,0xd1,0x68
+.byte	0x00,0x00,0x00,0x00,	0xc1,0xed,0xed,0x2c
+.byte	0x40,0x20,0x20,0x60,	0xe3,0xfc,0xfc,0x1f
+.byte	0x79,0xb1,0xb1,0xc8,	0xb6,0x5b,0x5b,0xed
+.byte	0xd4,0x6a,0x6a,0xbe,	0x8d,0xcb,0xcb,0x46
+.byte	0x67,0xbe,0xbe,0xd9,	0x72,0x39,0x39,0x4b
+.byte	0x94,0x4a,0x4a,0xde,	0x98,0x4c,0x4c,0xd4
+.byte	0xb0,0x58,0x58,0xe8,	0x85,0xcf,0xcf,0x4a
+.byte	0xbb,0xd0,0xd0,0x6b,	0xc5,0xef,0xef,0x2a
+.byte	0x4f,0xaa,0xaa,0xe5,	0xed,0xfb,0xfb,0x16
+.byte	0x86,0x43,0x43,0xc5,	0x9a,0x4d,0x4d,0xd7
+.byte	0x66,0x33,0x33,0x55,	0x11,0x85,0x85,0x94
+.byte	0x8a,0x45,0x45,0xcf,	0xe9,0xf9,0xf9,0x10
+.byte	0x04,0x02,0x02,0x06,	0xfe,0x7f,0x7f,0x81
+.byte	0xa0,0x50,0x50,0xf0,	0x78,0x3c,0x3c,0x44
+.byte	0x25,0x9f,0x9f,0xba,	0x4b,0xa8,0xa8,0xe3
+.byte	0xa2,0x51,0x51,0xf3,	0x5d,0xa3,0xa3,0xfe
+.byte	0x80,0x40,0x40,0xc0,	0x05,0x8f,0x8f,0x8a
+.byte	0x3f,0x92,0x92,0xad,	0x21,0x9d,0x9d,0xbc
+.byte	0x70,0x38,0x38,0x48,	0xf1,0xf5,0xf5,0x04
+.byte	0x63,0xbc,0xbc,0xdf,	0x77,0xb6,0xb6,0xc1
+.byte	0xaf,0xda,0xda,0x75,	0x42,0x21,0x21,0x63
+.byte	0x20,0x10,0x10,0x30,	0xe5,0xff,0xff,0x1a
+.byte	0xfd,0xf3,0xf3,0x0e,	0xbf,0xd2,0xd2,0x6d
+.byte	0x81,0xcd,0xcd,0x4c,	0x18,0x0c,0x0c,0x14
+.byte	0x26,0x13,0x13,0x35,	0xc3,0xec,0xec,0x2f
+.byte	0xbe,0x5f,0x5f,0xe1,	0x35,0x97,0x97,0xa2
+.byte	0x88,0x44,0x44,0xcc,	0x2e,0x17,0x17,0x39
+.byte	0x93,0xc4,0xc4,0x57,	0x55,0xa7,0xa7,0xf2
+.byte	0xfc,0x7e,0x7e,0x82,	0x7a,0x3d,0x3d,0x47
+.byte	0xc8,0x64,0x64,0xac,	0xba,0x5d,0x5d,0xe7
+.byte	0x32,0x19,0x19,0x2b,	0xe6,0x73,0x73,0x95
+.byte	0xc0,0x60,0x60,0xa0,	0x19,0x81,0x81,0x98
+.byte	0x9e,0x4f,0x4f,0xd1,	0xa3,0xdc,0xdc,0x7f
+.byte	0x44,0x22,0x22,0x66,	0x54,0x2a,0x2a,0x7e
+.byte	0x3b,0x90,0x90,0xab,	0x0b,0x88,0x88,0x83
+.byte	0x8c,0x46,0x46,0xca,	0xc7,0xee,0xee,0x29
+.byte	0x6b,0xb8,0xb8,0xd3,	0x28,0x14,0x14,0x3c
+.byte	0xa7,0xde,0xde,0x79,	0xbc,0x5e,0x5e,0xe2
+.byte	0x16,0x0b,0x0b,0x1d,	0xad,0xdb,0xdb,0x76
+.byte	0xdb,0xe0,0xe0,0x3b,	0x64,0x32,0x32,0x56
+.byte	0x74,0x3a,0x3a,0x4e,	0x14,0x0a,0x0a,0x1e
+.byte	0x92,0x49,0x49,0xdb,	0x0c,0x06,0x06,0x0a
+.byte	0x48,0x24,0x24,0x6c,	0xb8,0x5c,0x5c,0xe4
+.byte	0x9f,0xc2,0xc2,0x5d,	0xbd,0xd3,0xd3,0x6e
+.byte	0x43,0xac,0xac,0xef,	0xc4,0x62,0x62,0xa6
+.byte	0x39,0x91,0x91,0xa8,	0x31,0x95,0x95,0xa4
+.byte	0xd3,0xe4,0xe4,0x37,	0xf2,0x79,0x79,0x8b
+.byte	0xd5,0xe7,0xe7,0x32,	0x8b,0xc8,0xc8,0x43
+.byte	0x6e,0x37,0x37,0x59,	0xda,0x6d,0x6d,0xb7
+.byte	0x01,0x8d,0x8d,0x8c,	0xb1,0xd5,0xd5,0x64
+.byte	0x9c,0x4e,0x4e,0xd2,	0x49,0xa9,0xa9,0xe0
+.byte	0xd8,0x6c,0x6c,0xb4,	0xac,0x56,0x56,0xfa
+.byte	0xf3,0xf4,0xf4,0x07,	0xcf,0xea,0xea,0x25
+.byte	0xca,0x65,0x65,0xaf,	0xf4,0x7a,0x7a,0x8e
+.byte	0x47,0xae,0xae,0xe9,	0x10,0x08,0x08,0x18
+.byte	0x6f,0xba,0xba,0xd5,	0xf0,0x78,0x78,0x88
+.byte	0x4a,0x25,0x25,0x6f,	0x5c,0x2e,0x2e,0x72
+.byte	0x38,0x1c,0x1c,0x24,	0x57,0xa6,0xa6,0xf1
+.byte	0x73,0xb4,0xb4,0xc7,	0x97,0xc6,0xc6,0x51
+.byte	0xcb,0xe8,0xe8,0x23,	0xa1,0xdd,0xdd,0x7c
+.byte	0xe8,0x74,0x74,0x9c,	0x3e,0x1f,0x1f,0x21
+.byte	0x96,0x4b,0x4b,0xdd,	0x61,0xbd,0xbd,0xdc
+.byte	0x0d,0x8b,0x8b,0x86,	0x0f,0x8a,0x8a,0x85
+.byte	0xe0,0x70,0x70,0x90,	0x7c,0x3e,0x3e,0x42
+.byte	0x71,0xb5,0xb5,0xc4,	0xcc,0x66,0x66,0xaa
+.byte	0x90,0x48,0x48,0xd8,	0x06,0x03,0x03,0x05
+.byte	0xf7,0xf6,0xf6,0x01,	0x1c,0x0e,0x0e,0x12
+.byte	0xc2,0x61,0x61,0xa3,	0x6a,0x35,0x35,0x5f
+.byte	0xae,0x57,0x57,0xf9,	0x69,0xb9,0xb9,0xd0
+.byte	0x17,0x86,0x86,0x91,	0x99,0xc1,0xc1,0x58
+.byte	0x3a,0x1d,0x1d,0x27,	0x27,0x9e,0x9e,0xb9
+.byte	0xd9,0xe1,0xe1,0x38,	0xeb,0xf8,0xf8,0x13
+.byte	0x2b,0x98,0x98,0xb3,	0x22,0x11,0x11,0x33
+.byte	0xd2,0x69,0x69,0xbb,	0xa9,0xd9,0xd9,0x70
+.byte	0x07,0x8e,0x8e,0x89,	0x33,0x94,0x94,0xa7
+.byte	0x2d,0x9b,0x9b,0xb6,	0x3c,0x1e,0x1e,0x22
+.byte	0x15,0x87,0x87,0x92,	0xc9,0xe9,0xe9,0x20
+.byte	0x87,0xce,0xce,0x49,	0xaa,0x55,0x55,0xff
+.byte	0x50,0x28,0x28,0x78,	0xa5,0xdf,0xdf,0x7a
+.byte	0x03,0x8c,0x8c,0x8f,	0x59,0xa1,0xa1,0xf8
+.byte	0x09,0x89,0x89,0x80,	0x1a,0x0d,0x0d,0x17
+.byte	0x65,0xbf,0xbf,0xda,	0xd7,0xe6,0xe6,0x31
+.byte	0x84,0x42,0x42,0xc6,	0xd0,0x68,0x68,0xb8
+.byte	0x82,0x41,0x41,0xc3,	0x29,0x99,0x99,0xb0
+.byte	0x5a,0x2d,0x2d,0x77,	0x1e,0x0f,0x0f,0x11
+.byte	0x7b,0xb0,0xb0,0xcb,	0xa8,0x54,0x54,0xfc
+.byte	0x6d,0xbb,0xbb,0xd6,	0x2c,0x16,0x16,0x3a
+
+AES_Td:
+.byte	0x51,0xf4,0xa7,0x50,	0x7e,0x41,0x65,0x53	# Td0
+.byte	0x1a,0x17,0xa4,0xc3,	0x3a,0x27,0x5e,0x96
+.byte	0x3b,0xab,0x6b,0xcb,	0x1f,0x9d,0x45,0xf1
+.byte	0xac,0xfa,0x58,0xab,	0x4b,0xe3,0x03,0x93
+.byte	0x20,0x30,0xfa,0x55,	0xad,0x76,0x6d,0xf6
+.byte	0x88,0xcc,0x76,0x91,	0xf5,0x02,0x4c,0x25
+.byte	0x4f,0xe5,0xd7,0xfc,	0xc5,0x2a,0xcb,0xd7
+.byte	0x26,0x35,0x44,0x80,	0xb5,0x62,0xa3,0x8f
+.byte	0xde,0xb1,0x5a,0x49,	0x25,0xba,0x1b,0x67
+.byte	0x45,0xea,0x0e,0x98,	0x5d,0xfe,0xc0,0xe1
+.byte	0xc3,0x2f,0x75,0x02,	0x81,0x4c,0xf0,0x12
+.byte	0x8d,0x46,0x97,0xa3,	0x6b,0xd3,0xf9,0xc6
+.byte	0x03,0x8f,0x5f,0xe7,	0x15,0x92,0x9c,0x95
+.byte	0xbf,0x6d,0x7a,0xeb,	0x95,0x52,0x59,0xda
+.byte	0xd4,0xbe,0x83,0x2d,	0x58,0x74,0x21,0xd3
+.byte	0x49,0xe0,0x69,0x29,	0x8e,0xc9,0xc8,0x44
+.byte	0x75,0xc2,0x89,0x6a,	0xf4,0x8e,0x79,0x78
+.byte	0x99,0x58,0x3e,0x6b,	0x27,0xb9,0x71,0xdd
+.byte	0xbe,0xe1,0x4f,0xb6,	0xf0,0x88,0xad,0x17
+.byte	0xc9,0x20,0xac,0x66,	0x7d,0xce,0x3a,0xb4
+.byte	0x63,0xdf,0x4a,0x18,	0xe5,0x1a,0x31,0x82
+.byte	0x97,0x51,0x33,0x60,	0x62,0x53,0x7f,0x45
+.byte	0xb1,0x64,0x77,0xe0,	0xbb,0x6b,0xae,0x84
+.byte	0xfe,0x81,0xa0,0x1c,	0xf9,0x08,0x2b,0x94
+.byte	0x70,0x48,0x68,0x58,	0x8f,0x45,0xfd,0x19
+.byte	0x94,0xde,0x6c,0x87,	0x52,0x7b,0xf8,0xb7
+.byte	0xab,0x73,0xd3,0x23,	0x72,0x4b,0x02,0xe2
+.byte	0xe3,0x1f,0x8f,0x57,	0x66,0x55,0xab,0x2a
+.byte	0xb2,0xeb,0x28,0x07,	0x2f,0xb5,0xc2,0x03
+.byte	0x86,0xc5,0x7b,0x9a,	0xd3,0x37,0x08,0xa5
+.byte	0x30,0x28,0x87,0xf2,	0x23,0xbf,0xa5,0xb2
+.byte	0x02,0x03,0x6a,0xba,	0xed,0x16,0x82,0x5c
+.byte	0x8a,0xcf,0x1c,0x2b,	0xa7,0x79,0xb4,0x92
+.byte	0xf3,0x07,0xf2,0xf0,	0x4e,0x69,0xe2,0xa1
+.byte	0x65,0xda,0xf4,0xcd,	0x06,0x05,0xbe,0xd5
+.byte	0xd1,0x34,0x62,0x1f,	0xc4,0xa6,0xfe,0x8a
+.byte	0x34,0x2e,0x53,0x9d,	0xa2,0xf3,0x55,0xa0
+.byte	0x05,0x8a,0xe1,0x32,	0xa4,0xf6,0xeb,0x75
+.byte	0x0b,0x83,0xec,0x39,	0x40,0x60,0xef,0xaa
+.byte	0x5e,0x71,0x9f,0x06,	0xbd,0x6e,0x10,0x51
+.byte	0x3e,0x21,0x8a,0xf9,	0x96,0xdd,0x06,0x3d
+.byte	0xdd,0x3e,0x05,0xae,	0x4d,0xe6,0xbd,0x46
+.byte	0x91,0x54,0x8d,0xb5,	0x71,0xc4,0x5d,0x05
+.byte	0x04,0x06,0xd4,0x6f,	0x60,0x50,0x15,0xff
+.byte	0x19,0x98,0xfb,0x24,	0xd6,0xbd,0xe9,0x97
+.byte	0x89,0x40,0x43,0xcc,	0x67,0xd9,0x9e,0x77
+.byte	0xb0,0xe8,0x42,0xbd,	0x07,0x89,0x8b,0x88
+.byte	0xe7,0x19,0x5b,0x38,	0x79,0xc8,0xee,0xdb
+.byte	0xa1,0x7c,0x0a,0x47,	0x7c,0x42,0x0f,0xe9
+.byte	0xf8,0x84,0x1e,0xc9,	0x00,0x00,0x00,0x00
+.byte	0x09,0x80,0x86,0x83,	0x32,0x2b,0xed,0x48
+.byte	0x1e,0x11,0x70,0xac,	0x6c,0x5a,0x72,0x4e
+.byte	0xfd,0x0e,0xff,0xfb,	0x0f,0x85,0x38,0x56
+.byte	0x3d,0xae,0xd5,0x1e,	0x36,0x2d,0x39,0x27
+.byte	0x0a,0x0f,0xd9,0x64,	0x68,0x5c,0xa6,0x21
+.byte	0x9b,0x5b,0x54,0xd1,	0x24,0x36,0x2e,0x3a
+.byte	0x0c,0x0a,0x67,0xb1,	0x93,0x57,0xe7,0x0f
+.byte	0xb4,0xee,0x96,0xd2,	0x1b,0x9b,0x91,0x9e
+.byte	0x80,0xc0,0xc5,0x4f,	0x61,0xdc,0x20,0xa2
+.byte	0x5a,0x77,0x4b,0x69,	0x1c,0x12,0x1a,0x16
+.byte	0xe2,0x93,0xba,0x0a,	0xc0,0xa0,0x2a,0xe5
+.byte	0x3c,0x22,0xe0,0x43,	0x12,0x1b,0x17,0x1d
+.byte	0x0e,0x09,0x0d,0x0b,	0xf2,0x8b,0xc7,0xad
+.byte	0x2d,0xb6,0xa8,0xb9,	0x14,0x1e,0xa9,0xc8
+.byte	0x57,0xf1,0x19,0x85,	0xaf,0x75,0x07,0x4c
+.byte	0xee,0x99,0xdd,0xbb,	0xa3,0x7f,0x60,0xfd
+.byte	0xf7,0x01,0x26,0x9f,	0x5c,0x72,0xf5,0xbc
+.byte	0x44,0x66,0x3b,0xc5,	0x5b,0xfb,0x7e,0x34
+.byte	0x8b,0x43,0x29,0x76,	0xcb,0x23,0xc6,0xdc
+.byte	0xb6,0xed,0xfc,0x68,	0xb8,0xe4,0xf1,0x63
+.byte	0xd7,0x31,0xdc,0xca,	0x42,0x63,0x85,0x10
+.byte	0x13,0x97,0x22,0x40,	0x84,0xc6,0x11,0x20
+.byte	0x85,0x4a,0x24,0x7d,	0xd2,0xbb,0x3d,0xf8
+.byte	0xae,0xf9,0x32,0x11,	0xc7,0x29,0xa1,0x6d
+.byte	0x1d,0x9e,0x2f,0x4b,	0xdc,0xb2,0x30,0xf3
+.byte	0x0d,0x86,0x52,0xec,	0x77,0xc1,0xe3,0xd0
+.byte	0x2b,0xb3,0x16,0x6c,	0xa9,0x70,0xb9,0x99
+.byte	0x11,0x94,0x48,0xfa,	0x47,0xe9,0x64,0x22
+.byte	0xa8,0xfc,0x8c,0xc4,	0xa0,0xf0,0x3f,0x1a
+.byte	0x56,0x7d,0x2c,0xd8,	0x22,0x33,0x90,0xef
+.byte	0x87,0x49,0x4e,0xc7,	0xd9,0x38,0xd1,0xc1
+.byte	0x8c,0xca,0xa2,0xfe,	0x98,0xd4,0x0b,0x36
+.byte	0xa6,0xf5,0x81,0xcf,	0xa5,0x7a,0xde,0x28
+.byte	0xda,0xb7,0x8e,0x26,	0x3f,0xad,0xbf,0xa4
+.byte	0x2c,0x3a,0x9d,0xe4,	0x50,0x78,0x92,0x0d
+.byte	0x6a,0x5f,0xcc,0x9b,	0x54,0x7e,0x46,0x62
+.byte	0xf6,0x8d,0x13,0xc2,	0x90,0xd8,0xb8,0xe8
+.byte	0x2e,0x39,0xf7,0x5e,	0x82,0xc3,0xaf,0xf5
+.byte	0x9f,0x5d,0x80,0xbe,	0x69,0xd0,0x93,0x7c
+.byte	0x6f,0xd5,0x2d,0xa9,	0xcf,0x25,0x12,0xb3
+.byte	0xc8,0xac,0x99,0x3b,	0x10,0x18,0x7d,0xa7
+.byte	0xe8,0x9c,0x63,0x6e,	0xdb,0x3b,0xbb,0x7b
+.byte	0xcd,0x26,0x78,0x09,	0x6e,0x59,0x18,0xf4
+.byte	0xec,0x9a,0xb7,0x01,	0x83,0x4f,0x9a,0xa8
+.byte	0xe6,0x95,0x6e,0x65,	0xaa,0xff,0xe6,0x7e
+.byte	0x21,0xbc,0xcf,0x08,	0xef,0x15,0xe8,0xe6
+.byte	0xba,0xe7,0x9b,0xd9,	0x4a,0x6f,0x36,0xce
+.byte	0xea,0x9f,0x09,0xd4,	0x29,0xb0,0x7c,0xd6
+.byte	0x31,0xa4,0xb2,0xaf,	0x2a,0x3f,0x23,0x31
+.byte	0xc6,0xa5,0x94,0x30,	0x35,0xa2,0x66,0xc0
+.byte	0x74,0x4e,0xbc,0x37,	0xfc,0x82,0xca,0xa6
+.byte	0xe0,0x90,0xd0,0xb0,	0x33,0xa7,0xd8,0x15
+.byte	0xf1,0x04,0x98,0x4a,	0x41,0xec,0xda,0xf7
+.byte	0x7f,0xcd,0x50,0x0e,	0x17,0x91,0xf6,0x2f
+.byte	0x76,0x4d,0xd6,0x8d,	0x43,0xef,0xb0,0x4d
+.byte	0xcc,0xaa,0x4d,0x54,	0xe4,0x96,0x04,0xdf
+.byte	0x9e,0xd1,0xb5,0xe3,	0x4c,0x6a,0x88,0x1b
+.byte	0xc1,0x2c,0x1f,0xb8,	0x46,0x65,0x51,0x7f
+.byte	0x9d,0x5e,0xea,0x04,	0x01,0x8c,0x35,0x5d
+.byte	0xfa,0x87,0x74,0x73,	0xfb,0x0b,0x41,0x2e
+.byte	0xb3,0x67,0x1d,0x5a,	0x92,0xdb,0xd2,0x52
+.byte	0xe9,0x10,0x56,0x33,	0x6d,0xd6,0x47,0x13
+.byte	0x9a,0xd7,0x61,0x8c,	0x37,0xa1,0x0c,0x7a
+.byte	0x59,0xf8,0x14,0x8e,	0xeb,0x13,0x3c,0x89
+.byte	0xce,0xa9,0x27,0xee,	0xb7,0x61,0xc9,0x35
+.byte	0xe1,0x1c,0xe5,0xed,	0x7a,0x47,0xb1,0x3c
+.byte	0x9c,0xd2,0xdf,0x59,	0x55,0xf2,0x73,0x3f
+.byte	0x18,0x14,0xce,0x79,	0x73,0xc7,0x37,0xbf
+.byte	0x53,0xf7,0xcd,0xea,	0x5f,0xfd,0xaa,0x5b
+.byte	0xdf,0x3d,0x6f,0x14,	0x78,0x44,0xdb,0x86
+.byte	0xca,0xaf,0xf3,0x81,	0xb9,0x68,0xc4,0x3e
+.byte	0x38,0x24,0x34,0x2c,	0xc2,0xa3,0x40,0x5f
+.byte	0x16,0x1d,0xc3,0x72,	0xbc,0xe2,0x25,0x0c
+.byte	0x28,0x3c,0x49,0x8b,	0xff,0x0d,0x95,0x41
+.byte	0x39,0xa8,0x01,0x71,	0x08,0x0c,0xb3,0xde
+.byte	0xd8,0xb4,0xe4,0x9c,	0x64,0x56,0xc1,0x90
+.byte	0x7b,0xcb,0x84,0x61,	0xd5,0x32,0xb6,0x70
+.byte	0x48,0x6c,0x5c,0x74,	0xd0,0xb8,0x57,0x42
+
+.byte	0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38	# Td4
+.byte	0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb
+.byte	0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87
+.byte	0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb
+.byte	0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d
+.byte	0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e
+.byte	0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2
+.byte	0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25
+.byte	0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16
+.byte	0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92
+.byte	0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda
+.byte	0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84
+.byte	0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a
+.byte	0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06
+.byte	0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02
+.byte	0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b
+.byte	0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea
+.byte	0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73
+.byte	0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85
+.byte	0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e
+.byte	0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89
+.byte	0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b
+.byte	0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20
+.byte	0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4
+.byte	0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31
+.byte	0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f
+.byte	0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d
+.byte	0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef
+.byte	0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0
+.byte	0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61
+.byte	0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26
+.byte	0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d
+
+AES_Te4:
+.byte	0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5	# Te4
+.byte	0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76
+.byte	0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0
+.byte	0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0
+.byte	0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc
+.byte	0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15
+.byte	0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a
+.byte	0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75
+.byte	0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0
+.byte	0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84
+.byte	0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b
+.byte	0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf
+.byte	0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85
+.byte	0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8
+.byte	0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5
+.byte	0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2
+.byte	0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17
+.byte	0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73
+.byte	0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88
+.byte	0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb
+.byte	0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c
+.byte	0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79
+.byte	0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9
+.byte	0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08
+.byte	0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6
+.byte	0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a
+.byte	0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e
+.byte	0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e
+.byte	0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94
+.byte	0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf
+.byte	0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68
+.byte	0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16
+
+.byte	0x01,0x00,0x00,0x00,	0x02,0x00,0x00,0x00	# rcon
+.byte	0x04,0x00,0x00,0x00,	0x08,0x00,0x00,0x00
+.byte	0x10,0x00,0x00,0x00,	0x20,0x00,0x00,0x00
+.byte	0x40,0x00,0x00,0x00,	0x80,0x00,0x00,0x00
+.byte	0x1B,0x00,0x00,0x00,	0x36,0x00,0x00,0x00
+___
+
+foreach (split("\n",$code)) {
+	s/\`([^\`]*)\`/eval $1/ge;
+
+	# made-up _instructions, _xtr, _ins, _ror and _bias, cope
+	# with byte order dependencies...
+	if (/^\s+_/) {
+	    s/(_[a-z]+\s+)(\$[0-9]+),([^,]+)(#.*)*$/$1$2,$2,$3/;
+
+	    s/_xtr\s+(\$[0-9]+),(\$[0-9]+),([0-9]+(\-2)*)/
+		sprintf("srl\t$1,$2,%d",$big_endian ?	eval($3)
+					:		eval("24-$3"))/e or
+	    s/_ins\s+(\$[0-9]+),(\$[0-9]+),([0-9]+)/
+		sprintf("sll\t$1,$2,%d",$big_endian ?	eval($3)
+					:		eval("24-$3"))/e or
+	    s/_ins2\s+(\$[0-9]+),(\$[0-9]+),([0-9]+)/
+		sprintf("ins\t$1,$2,%d,8",$big_endian ?	eval($3)
+					:		eval("24-$3"))/e or
+	    s/_ror\s+(\$[0-9]+),(\$[0-9]+),(\-?[0-9]+)/
+		sprintf("srl\t$1,$2,%d",$big_endian ?	eval($3)
+					:		eval("$3*-1"))/e or
+	    s/_bias\s+(\$[0-9]+),(\$[0-9]+),([0-9]+)/
+		sprintf("sll\t$1,$2,%d",$big_endian ?	eval($3)
+					:		eval("($3-16)&31"))/e;
+
+	    s/srl\s+(\$[0-9]+),(\$[0-9]+),\-([0-9]+)/
+		sprintf("sll\t$1,$2,$3")/e				or
+	    s/srl\s+(\$[0-9]+),(\$[0-9]+),0/
+		sprintf("and\t$1,$2,0xff")/e				or
+	    s/(sll\s+\$[0-9]+,\$[0-9]+,0)/#$1/;
+	}
+
+	# convert lwl/lwr and swr/swl to little-endian order
+	if (!$big_endian && /^\s+[sl]w[lr]\s+/) {
+	    s/([sl]wl.*)([0-9]+)\((\$[0-9]+)\)/
+		sprintf("$1%d($3)",eval("$2-$2%4+($2%4-1)&3"))/e	or
+	    s/([sl]wr.*)([0-9]+)\((\$[0-9]+)\)/
+		sprintf("$1%d($3)",eval("$2-$2%4+($2%4+1)&3"))/e;
+	}
+
+	if (!$big_endian) {
+	    s/(rotr\s+\$[0-9]+,\$[0-9]+),([0-9]+)/sprintf("$1,%d",32-$2)/e;
+	    s/(ext\s+\$[0-9]+,\$[0-9]+),([0-9]+),8/sprintf("$1,%d,8",24-$2)/e;
+	}
+
+	print $_,"\n";
+}
+
+close STDOUT;
diff --git a/crypto/aes/asm/aes-parisc.pl b/crypto/aes/asm/aes-parisc.pl
new file mode 100644
index 000000000..2c785bc56
--- /dev/null
+++ b/crypto/aes/asm/aes-parisc.pl
@@ -0,0 +1,1029 @@
+#! /usr/bin/env perl
+# Copyright 2009-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+
+# ====================================================================
+# Written by Andy Polyakov  for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+
+# AES for PA-RISC.
+#
+# June 2009.
+#
+# The module is mechanical transliteration of aes-sparcv9.pl, but with
+# a twist: S-boxes are compressed even further down to 1K+256B. On
+# PA-7100LC performance is ~40% better than gcc 3.2 generated code and
+# is about 33 cycles per byte processed with 128-bit key. Newer CPUs
+# perform at 16 cycles per byte. It's not faster than code generated
+# by vendor compiler, but recall that it has compressed S-boxes, which
+# requires extra processing.
+#
+# Special thanks to polarhome.com for providing HP-UX account.
+
+$flavour = shift;
+$output = shift;
+open STDOUT,">$output";
+
+if ($flavour =~ /64/) {
+	$LEVEL		="2.0W";
+	$SIZE_T		=8;
+	$FRAME_MARKER	=80;
+	$SAVED_RP	=16;
+	$PUSH		="std";
+	$PUSHMA		="std,ma";
+	$POP		="ldd";
+	$POPMB		="ldd,mb";
+} else {
+	$LEVEL		="1.0";
+	$SIZE_T		=4;
+	$FRAME_MARKER	=48;
+	$SAVED_RP	=20;
+	$PUSH		="stw";
+	$PUSHMA		="stwm";
+	$POP		="ldw";
+	$POPMB		="ldwm";
+}
+
+$FRAME=16*$SIZE_T+$FRAME_MARKER;# 16 saved regs + frame marker
+				#                 [+ argument transfer]
+$inp="%r26";	# arg0
+$out="%r25";	# arg1
+$key="%r24";	# arg2
+
+($s0,$s1,$s2,$s3) = ("%r1","%r2","%r3","%r4");
+($t0,$t1,$t2,$t3) = ("%r5","%r6","%r7","%r8");
+
+($acc0, $acc1, $acc2, $acc3, $acc4, $acc5, $acc6, $acc7,
+ $acc8, $acc9,$acc10,$acc11,$acc12,$acc13,$acc14,$acc15) =
+("%r9","%r10","%r11","%r12","%r13","%r14","%r15","%r16",
+"%r17","%r18","%r19","%r20","%r21","%r22","%r23","%r26");
+
+$tbl="%r28";
+$rounds="%r29";
+
+$code=<<___;
+	.LEVEL	$LEVEL
+	.SPACE	\$TEXT\$
+	.SUBSPA	\$CODE\$,QUAD=0,ALIGN=8,ACCESS=0x2C,CODE_ONLY
+
+	.EXPORT	AES_encrypt,ENTRY,ARGW0=GR,ARGW1=GR,ARGW2=GR
+	.ALIGN	64
+AES_encrypt
+	.PROC
+	.CALLINFO	FRAME=`$FRAME-16*$SIZE_T`,NO_CALLS,SAVE_RP,ENTRY_GR=18
+	.ENTRY
+	$PUSH	%r2,-$SAVED_RP(%sp)	; standard prologue
+	$PUSHMA	%r3,$FRAME(%sp)
+	$PUSH	%r4,`-$FRAME+1*$SIZE_T`(%sp)
+	$PUSH	%r5,`-$FRAME+2*$SIZE_T`(%sp)
+	$PUSH	%r6,`-$FRAME+3*$SIZE_T`(%sp)
+	$PUSH	%r7,`-$FRAME+4*$SIZE_T`(%sp)
+	$PUSH	%r8,`-$FRAME+5*$SIZE_T`(%sp)
+	$PUSH	%r9,`-$FRAME+6*$SIZE_T`(%sp)
+	$PUSH	%r10,`-$FRAME+7*$SIZE_T`(%sp)
+	$PUSH	%r11,`-$FRAME+8*$SIZE_T`(%sp)
+	$PUSH	%r12,`-$FRAME+9*$SIZE_T`(%sp)
+	$PUSH	%r13,`-$FRAME+10*$SIZE_T`(%sp)
+	$PUSH	%r14,`-$FRAME+11*$SIZE_T`(%sp)
+	$PUSH	%r15,`-$FRAME+12*$SIZE_T`(%sp)
+	$PUSH	%r16,`-$FRAME+13*$SIZE_T`(%sp)
+	$PUSH	%r17,`-$FRAME+14*$SIZE_T`(%sp)
+	$PUSH	%r18,`-$FRAME+15*$SIZE_T`(%sp)
+
+	blr	%r0,$tbl
+	ldi	3,$t0
+L\$enc_pic
+	andcm	$tbl,$t0,$tbl
+	ldo	L\$AES_Te-L\$enc_pic($tbl),$tbl
+
+	and	$inp,$t0,$t0
+	sub	$inp,$t0,$inp
+	ldw	0($inp),$s0
+	ldw	4($inp),$s1
+	ldw	8($inp),$s2
+	comib,=	0,$t0,L\$enc_inp_aligned
+	ldw	12($inp),$s3
+
+	sh3addl	$t0,%r0,$t0
+	subi	32,$t0,$t0
+	mtctl	$t0,%cr11
+	ldw	16($inp),$t1
+	vshd	$s0,$s1,$s0
+	vshd	$s1,$s2,$s1
+	vshd	$s2,$s3,$s2
+	vshd	$s3,$t1,$s3
+
+L\$enc_inp_aligned
+	bl	_parisc_AES_encrypt,%r31
+	nop
+
+	extru,<> $out,31,2,%r0
+	b	L\$enc_out_aligned
+	nop
+
+	_srm	$s0,24,$acc0
+	_srm	$s0,16,$acc1
+	stb	$acc0,0($out)
+	_srm	$s0,8,$acc2
+	stb	$acc1,1($out)
+	_srm	$s1,24,$acc4
+	stb	$acc2,2($out)
+	_srm	$s1,16,$acc5
+	stb	$s0,3($out)
+	_srm	$s1,8,$acc6
+	stb	$acc4,4($out)
+	_srm	$s2,24,$acc0
+	stb	$acc5,5($out)
+	_srm	$s2,16,$acc1
+	stb	$acc6,6($out)
+	_srm	$s2,8,$acc2
+	stb	$s1,7($out)
+	_srm	$s3,24,$acc4
+	stb	$acc0,8($out)
+	_srm	$s3,16,$acc5
+	stb	$acc1,9($out)
+	_srm	$s3,8,$acc6
+	stb	$acc2,10($out)
+	stb	$s2,11($out)
+	stb	$acc4,12($out)
+	stb	$acc5,13($out)
+	stb	$acc6,14($out)
+	b	L\$enc_done
+	stb	$s3,15($out)
+
+L\$enc_out_aligned
+	stw	$s0,0($out)
+	stw	$s1,4($out)
+	stw	$s2,8($out)
+	stw	$s3,12($out)
+
+L\$enc_done
+	$POP	`-$FRAME-$SAVED_RP`(%sp),%r2	; standard epilogue
+	$POP	`-$FRAME+1*$SIZE_T`(%sp),%r4
+	$POP	`-$FRAME+2*$SIZE_T`(%sp),%r5
+	$POP	`-$FRAME+3*$SIZE_T`(%sp),%r6
+	$POP	`-$FRAME+4*$SIZE_T`(%sp),%r7
+	$POP	`-$FRAME+5*$SIZE_T`(%sp),%r8
+	$POP	`-$FRAME+6*$SIZE_T`(%sp),%r9
+	$POP	`-$FRAME+7*$SIZE_T`(%sp),%r10
+	$POP	`-$FRAME+8*$SIZE_T`(%sp),%r11
+	$POP	`-$FRAME+9*$SIZE_T`(%sp),%r12
+	$POP	`-$FRAME+10*$SIZE_T`(%sp),%r13
+	$POP	`-$FRAME+11*$SIZE_T`(%sp),%r14
+	$POP	`-$FRAME+12*$SIZE_T`(%sp),%r15
+	$POP	`-$FRAME+13*$SIZE_T`(%sp),%r16
+	$POP	`-$FRAME+14*$SIZE_T`(%sp),%r17
+	$POP	`-$FRAME+15*$SIZE_T`(%sp),%r18
+	bv	(%r2)
+	.EXIT
+	$POPMB	-$FRAME(%sp),%r3
+	.PROCEND
+
+	.ALIGN	16
+_parisc_AES_encrypt
+	.PROC
+	.CALLINFO	MILLICODE
+	.ENTRY
+	ldw	240($key),$rounds
+	ldw	0($key),$t0
+	ldw	4($key),$t1
+	ldw	8($key),$t2
+	_srm	$rounds,1,$rounds
+	xor	$t0,$s0,$s0
+	ldw	12($key),$t3
+	_srm	$s0,24,$acc0
+	xor	$t1,$s1,$s1
+	ldw	16($key),$t0
+	_srm	$s1,16,$acc1
+	xor	$t2,$s2,$s2
+	ldw	20($key),$t1
+	xor	$t3,$s3,$s3
+	ldw	24($key),$t2
+	ldw	28($key),$t3
+L\$enc_loop
+	_srm	$s2,8,$acc2
+	ldwx,s	$acc0($tbl),$acc0
+	_srm	$s3,0,$acc3
+	ldwx,s	$acc1($tbl),$acc1
+	_srm	$s1,24,$acc4
+	ldwx,s	$acc2($tbl),$acc2
+	_srm	$s2,16,$acc5
+	ldwx,s	$acc3($tbl),$acc3
+	_srm	$s3,8,$acc6
+	ldwx,s	$acc4($tbl),$acc4
+	_srm	$s0,0,$acc7
+	ldwx,s	$acc5($tbl),$acc5
+	_srm	$s2,24,$acc8
+	ldwx,s	$acc6($tbl),$acc6
+	_srm	$s3,16,$acc9
+	ldwx,s	$acc7($tbl),$acc7
+	_srm	$s0,8,$acc10
+	ldwx,s	$acc8($tbl),$acc8
+	_srm	$s1,0,$acc11
+	ldwx,s	$acc9($tbl),$acc9
+	_srm	$s3,24,$acc12
+	ldwx,s	$acc10($tbl),$acc10
+	_srm	$s0,16,$acc13
+	ldwx,s	$acc11($tbl),$acc11
+	_srm	$s1,8,$acc14
+	ldwx,s	$acc12($tbl),$acc12
+	_srm	$s2,0,$acc15
+	ldwx,s	$acc13($tbl),$acc13
+	ldwx,s	$acc14($tbl),$acc14
+	ldwx,s	$acc15($tbl),$acc15
+	addib,= -1,$rounds,L\$enc_last
+	ldo	32($key),$key
+
+		_ror	$acc1,8,$acc1
+		xor	$acc0,$t0,$t0
+	ldw	0($key),$s0
+		_ror	$acc2,16,$acc2
+		xor	$acc1,$t0,$t0
+	ldw	4($key),$s1
+		_ror	$acc3,24,$acc3
+		xor	$acc2,$t0,$t0
+	ldw	8($key),$s2
+		_ror	$acc5,8,$acc5
+		xor	$acc3,$t0,$t0
+	ldw	12($key),$s3
+		_ror	$acc6,16,$acc6
+		xor	$acc4,$t1,$t1
+		_ror	$acc7,24,$acc7
+		xor	$acc5,$t1,$t1
+		_ror	$acc9,8,$acc9
+		xor	$acc6,$t1,$t1
+		_ror	$acc10,16,$acc10
+		xor	$acc7,$t1,$t1
+		_ror	$acc11,24,$acc11
+		xor	$acc8,$t2,$t2
+		_ror	$acc13,8,$acc13
+		xor	$acc9,$t2,$t2
+		_ror	$acc14,16,$acc14
+		xor	$acc10,$t2,$t2
+		_ror	$acc15,24,$acc15
+		xor	$acc11,$t2,$t2
+		xor	$acc12,$acc14,$acc14
+		xor	$acc13,$t3,$t3
+	_srm	$t0,24,$acc0
+		xor	$acc14,$t3,$t3
+	_srm	$t1,16,$acc1
+		xor	$acc15,$t3,$t3
+
+	_srm	$t2,8,$acc2
+	ldwx,s	$acc0($tbl),$acc0
+	_srm	$t3,0,$acc3
+	ldwx,s	$acc1($tbl),$acc1
+	_srm	$t1,24,$acc4
+	ldwx,s	$acc2($tbl),$acc2
+	_srm	$t2,16,$acc5
+	ldwx,s	$acc3($tbl),$acc3
+	_srm	$t3,8,$acc6
+	ldwx,s	$acc4($tbl),$acc4
+	_srm	$t0,0,$acc7
+	ldwx,s	$acc5($tbl),$acc5
+	_srm	$t2,24,$acc8
+	ldwx,s	$acc6($tbl),$acc6
+	_srm	$t3,16,$acc9
+	ldwx,s	$acc7($tbl),$acc7
+	_srm	$t0,8,$acc10
+	ldwx,s	$acc8($tbl),$acc8
+	_srm	$t1,0,$acc11
+	ldwx,s	$acc9($tbl),$acc9
+	_srm	$t3,24,$acc12
+	ldwx,s	$acc10($tbl),$acc10
+	_srm	$t0,16,$acc13
+	ldwx,s	$acc11($tbl),$acc11
+	_srm	$t1,8,$acc14
+	ldwx,s	$acc12($tbl),$acc12
+	_srm	$t2,0,$acc15
+	ldwx,s	$acc13($tbl),$acc13
+		_ror	$acc1,8,$acc1
+	ldwx,s	$acc14($tbl),$acc14
+
+		_ror	$acc2,16,$acc2
+		xor	$acc0,$s0,$s0
+	ldwx,s	$acc15($tbl),$acc15
+		_ror	$acc3,24,$acc3
+		xor	$acc1,$s0,$s0
+	ldw	16($key),$t0
+		_ror	$acc5,8,$acc5
+		xor	$acc2,$s0,$s0
+	ldw	20($key),$t1
+		_ror	$acc6,16,$acc6
+		xor	$acc3,$s0,$s0
+	ldw	24($key),$t2
+		_ror	$acc7,24,$acc7
+		xor	$acc4,$s1,$s1
+	ldw	28($key),$t3
+		_ror	$acc9,8,$acc9
+		xor	$acc5,$s1,$s1
+	ldw	1024+0($tbl),%r0		; prefetch te4
+		_ror	$acc10,16,$acc10
+		xor	$acc6,$s1,$s1
+	ldw	1024+32($tbl),%r0		; prefetch te4
+		_ror	$acc11,24,$acc11
+		xor	$acc7,$s1,$s1
+	ldw	1024+64($tbl),%r0		; prefetch te4
+		_ror	$acc13,8,$acc13
+		xor	$acc8,$s2,$s2
+	ldw	1024+96($tbl),%r0		; prefetch te4
+		_ror	$acc14,16,$acc14
+		xor	$acc9,$s2,$s2
+	ldw	1024+128($tbl),%r0		; prefetch te4
+		_ror	$acc15,24,$acc15
+		xor	$acc10,$s2,$s2
+	ldw	1024+160($tbl),%r0		; prefetch te4
+	_srm	$s0,24,$acc0
+		xor	$acc11,$s2,$s2
+	ldw	1024+192($tbl),%r0		; prefetch te4
+		xor	$acc12,$acc14,$acc14
+		xor	$acc13,$s3,$s3
+	ldw	1024+224($tbl),%r0		; prefetch te4
+	_srm	$s1,16,$acc1
+		xor	$acc14,$s3,$s3
+	b	L\$enc_loop
+		xor	$acc15,$s3,$s3
+
+	.ALIGN	16
+L\$enc_last
+	ldo	1024($tbl),$rounds
+		_ror	$acc1,8,$acc1
+		xor	$acc0,$t0,$t0
+	ldw	0($key),$s0
+		_ror	$acc2,16,$acc2
+		xor	$acc1,$t0,$t0
+	ldw	4($key),$s1
+		_ror	$acc3,24,$acc3
+		xor	$acc2,$t0,$t0
+	ldw	8($key),$s2
+		_ror	$acc5,8,$acc5
+		xor	$acc3,$t0,$t0
+	ldw	12($key),$s3
+		_ror	$acc6,16,$acc6
+		xor	$acc4,$t1,$t1
+		_ror	$acc7,24,$acc7
+		xor	$acc5,$t1,$t1
+		_ror	$acc9,8,$acc9
+		xor	$acc6,$t1,$t1
+		_ror	$acc10,16,$acc10
+		xor	$acc7,$t1,$t1
+		_ror	$acc11,24,$acc11
+		xor	$acc8,$t2,$t2
+		_ror	$acc13,8,$acc13
+		xor	$acc9,$t2,$t2
+		_ror	$acc14,16,$acc14
+		xor	$acc10,$t2,$t2
+		_ror	$acc15,24,$acc15
+		xor	$acc11,$t2,$t2
+		xor	$acc12,$acc14,$acc14
+		xor	$acc13,$t3,$t3
+	_srm	$t0,24,$acc0
+		xor	$acc14,$t3,$t3
+	_srm	$t1,16,$acc1
+		xor	$acc15,$t3,$t3
+
+	_srm	$t2,8,$acc2
+	ldbx	$acc0($rounds),$acc0
+	_srm	$t1,24,$acc4
+	ldbx	$acc1($rounds),$acc1
+	_srm	$t2,16,$acc5
+	_srm	$t3,0,$acc3
+	ldbx	$acc2($rounds),$acc2
+	ldbx	$acc3($rounds),$acc3
+	_srm	$t3,8,$acc6
+	ldbx	$acc4($rounds),$acc4
+	_srm	$t2,24,$acc8
+	ldbx	$acc5($rounds),$acc5
+	_srm	$t3,16,$acc9
+	_srm	$t0,0,$acc7
+	ldbx	$acc6($rounds),$acc6
+	ldbx	$acc7($rounds),$acc7
+	_srm	$t0,8,$acc10
+	ldbx	$acc8($rounds),$acc8
+	_srm	$t3,24,$acc12
+	ldbx	$acc9($rounds),$acc9
+	_srm	$t0,16,$acc13
+	_srm	$t1,0,$acc11
+	ldbx	$acc10($rounds),$acc10
+	_srm	$t1,8,$acc14
+	ldbx	$acc11($rounds),$acc11
+	ldbx	$acc12($rounds),$acc12
+	ldbx	$acc13($rounds),$acc13
+	_srm	$t2,0,$acc15
+	ldbx	$acc14($rounds),$acc14
+
+		dep	$acc0,7,8,$acc3
+	ldbx	$acc15($rounds),$acc15
+		dep	$acc4,7,8,$acc7
+		dep	$acc1,15,8,$acc3
+		dep	$acc5,15,8,$acc7
+		dep	$acc2,23,8,$acc3
+		dep	$acc6,23,8,$acc7
+		xor	$acc3,$s0,$s0
+		xor	$acc7,$s1,$s1
+		dep	$acc8,7,8,$acc11
+		dep	$acc12,7,8,$acc15
+		dep	$acc9,15,8,$acc11
+		dep	$acc13,15,8,$acc15
+		dep	$acc10,23,8,$acc11
+		dep	$acc14,23,8,$acc15
+		xor	$acc11,$s2,$s2
+
+	bv	(%r31)
+	.EXIT
+		xor	$acc15,$s3,$s3
+	.PROCEND
+
+	.ALIGN	64
+L\$AES_Te
+	.WORD	0xc66363a5, 0xf87c7c84, 0xee777799, 0xf67b7b8d
+	.WORD	0xfff2f20d, 0xd66b6bbd, 0xde6f6fb1, 0x91c5c554
+	.WORD	0x60303050, 0x02010103, 0xce6767a9, 0x562b2b7d
+	.WORD	0xe7fefe19, 0xb5d7d762, 0x4dababe6, 0xec76769a
+	.WORD	0x8fcaca45, 0x1f82829d, 0x89c9c940, 0xfa7d7d87
+	.WORD	0xeffafa15, 0xb25959eb, 0x8e4747c9, 0xfbf0f00b
+	.WORD	0x41adadec, 0xb3d4d467, 0x5fa2a2fd, 0x45afafea
+	.WORD	0x239c9cbf, 0x53a4a4f7, 0xe4727296, 0x9bc0c05b
+	.WORD	0x75b7b7c2, 0xe1fdfd1c, 0x3d9393ae, 0x4c26266a
+	.WORD	0x6c36365a, 0x7e3f3f41, 0xf5f7f702, 0x83cccc4f
+	.WORD	0x6834345c, 0x51a5a5f4, 0xd1e5e534, 0xf9f1f108
+	.WORD	0xe2717193, 0xabd8d873, 0x62313153, 0x2a15153f
+	.WORD	0x0804040c, 0x95c7c752, 0x46232365, 0x9dc3c35e
+	.WORD	0x30181828, 0x379696a1, 0x0a05050f, 0x2f9a9ab5
+	.WORD	0x0e070709, 0x24121236, 0x1b80809b, 0xdfe2e23d
+	.WORD	0xcdebeb26, 0x4e272769, 0x7fb2b2cd, 0xea75759f
+	.WORD	0x1209091b, 0x1d83839e, 0x582c2c74, 0x341a1a2e
+	.WORD	0x361b1b2d, 0xdc6e6eb2, 0xb45a5aee, 0x5ba0a0fb
+	.WORD	0xa45252f6, 0x763b3b4d, 0xb7d6d661, 0x7db3b3ce
+	.WORD	0x5229297b, 0xdde3e33e, 0x5e2f2f71, 0x13848497
+	.WORD	0xa65353f5, 0xb9d1d168, 0x00000000, 0xc1eded2c
+	.WORD	0x40202060, 0xe3fcfc1f, 0x79b1b1c8, 0xb65b5bed
+	.WORD	0xd46a6abe, 0x8dcbcb46, 0x67bebed9, 0x7239394b
+	.WORD	0x944a4ade, 0x984c4cd4, 0xb05858e8, 0x85cfcf4a
+	.WORD	0xbbd0d06b, 0xc5efef2a, 0x4faaaae5, 0xedfbfb16
+	.WORD	0x864343c5, 0x9a4d4dd7, 0x66333355, 0x11858594
+	.WORD	0x8a4545cf, 0xe9f9f910, 0x04020206, 0xfe7f7f81
+	.WORD	0xa05050f0, 0x783c3c44, 0x259f9fba, 0x4ba8a8e3
+	.WORD	0xa25151f3, 0x5da3a3fe, 0x804040c0, 0x058f8f8a
+	.WORD	0x3f9292ad, 0x219d9dbc, 0x70383848, 0xf1f5f504
+	.WORD	0x63bcbcdf, 0x77b6b6c1, 0xafdada75, 0x42212163
+	.WORD	0x20101030, 0xe5ffff1a, 0xfdf3f30e, 0xbfd2d26d
+	.WORD	0x81cdcd4c, 0x180c0c14, 0x26131335, 0xc3ecec2f
+	.WORD	0xbe5f5fe1, 0x359797a2, 0x884444cc, 0x2e171739
+	.WORD	0x93c4c457, 0x55a7a7f2, 0xfc7e7e82, 0x7a3d3d47
+	.WORD	0xc86464ac, 0xba5d5de7, 0x3219192b, 0xe6737395
+	.WORD	0xc06060a0, 0x19818198, 0x9e4f4fd1, 0xa3dcdc7f
+	.WORD	0x44222266, 0x542a2a7e, 0x3b9090ab, 0x0b888883
+	.WORD	0x8c4646ca, 0xc7eeee29, 0x6bb8b8d3, 0x2814143c
+	.WORD	0xa7dede79, 0xbc5e5ee2, 0x160b0b1d, 0xaddbdb76
+	.WORD	0xdbe0e03b, 0x64323256, 0x743a3a4e, 0x140a0a1e
+	.WORD	0x924949db, 0x0c06060a, 0x4824246c, 0xb85c5ce4
+	.WORD	0x9fc2c25d, 0xbdd3d36e, 0x43acacef, 0xc46262a6
+	.WORD	0x399191a8, 0x319595a4, 0xd3e4e437, 0xf279798b
+	.WORD	0xd5e7e732, 0x8bc8c843, 0x6e373759, 0xda6d6db7
+	.WORD	0x018d8d8c, 0xb1d5d564, 0x9c4e4ed2, 0x49a9a9e0
+	.WORD	0xd86c6cb4, 0xac5656fa, 0xf3f4f407, 0xcfeaea25
+	.WORD	0xca6565af, 0xf47a7a8e, 0x47aeaee9, 0x10080818
+	.WORD	0x6fbabad5, 0xf0787888, 0x4a25256f, 0x5c2e2e72
+	.WORD	0x381c1c24, 0x57a6a6f1, 0x73b4b4c7, 0x97c6c651
+	.WORD	0xcbe8e823, 0xa1dddd7c, 0xe874749c, 0x3e1f1f21
+	.WORD	0x964b4bdd, 0x61bdbddc, 0x0d8b8b86, 0x0f8a8a85
+	.WORD	0xe0707090, 0x7c3e3e42, 0x71b5b5c4, 0xcc6666aa
+	.WORD	0x904848d8, 0x06030305, 0xf7f6f601, 0x1c0e0e12
+	.WORD	0xc26161a3, 0x6a35355f, 0xae5757f9, 0x69b9b9d0
+	.WORD	0x17868691, 0x99c1c158, 0x3a1d1d27, 0x279e9eb9
+	.WORD	0xd9e1e138, 0xebf8f813, 0x2b9898b3, 0x22111133
+	.WORD	0xd26969bb, 0xa9d9d970, 0x078e8e89, 0x339494a7
+	.WORD	0x2d9b9bb6, 0x3c1e1e22, 0x15878792, 0xc9e9e920
+	.WORD	0x87cece49, 0xaa5555ff, 0x50282878, 0xa5dfdf7a
+	.WORD	0x038c8c8f, 0x59a1a1f8, 0x09898980, 0x1a0d0d17
+	.WORD	0x65bfbfda, 0xd7e6e631, 0x844242c6, 0xd06868b8
+	.WORD	0x824141c3, 0x299999b0, 0x5a2d2d77, 0x1e0f0f11
+	.WORD	0x7bb0b0cb, 0xa85454fc, 0x6dbbbbd6, 0x2c16163a
+	.BYTE	0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5
+	.BYTE	0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76
+	.BYTE	0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0
+	.BYTE	0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0
+	.BYTE	0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc
+	.BYTE	0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15
+	.BYTE	0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a
+	.BYTE	0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75
+	.BYTE	0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0
+	.BYTE	0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84
+	.BYTE	0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b
+	.BYTE	0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf
+	.BYTE	0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85
+	.BYTE	0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8
+	.BYTE	0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5
+	.BYTE	0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2
+	.BYTE	0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17
+	.BYTE	0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73
+	.BYTE	0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88
+	.BYTE	0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb
+	.BYTE	0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c
+	.BYTE	0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79
+	.BYTE	0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9
+	.BYTE	0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08
+	.BYTE	0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6
+	.BYTE	0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a
+	.BYTE	0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e
+	.BYTE	0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e
+	.BYTE	0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94
+	.BYTE	0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf
+	.BYTE	0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68
+	.BYTE	0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16
+___
+
+$code.=<<___;
+	.EXPORT	AES_decrypt,ENTRY,ARGW0=GR,ARGW1=GR,ARGW2=GR
+	.ALIGN	16
+AES_decrypt
+	.PROC
+	.CALLINFO	FRAME=`$FRAME-16*$SIZE_T`,NO_CALLS,SAVE_RP,ENTRY_GR=18
+	.ENTRY
+	$PUSH	%r2,-$SAVED_RP(%sp)	; standard prologue
+	$PUSHMA	%r3,$FRAME(%sp)
+	$PUSH	%r4,`-$FRAME+1*$SIZE_T`(%sp)
+	$PUSH	%r5,`-$FRAME+2*$SIZE_T`(%sp)
+	$PUSH	%r6,`-$FRAME+3*$SIZE_T`(%sp)
+	$PUSH	%r7,`-$FRAME+4*$SIZE_T`(%sp)
+	$PUSH	%r8,`-$FRAME+5*$SIZE_T`(%sp)
+	$PUSH	%r9,`-$FRAME+6*$SIZE_T`(%sp)
+	$PUSH	%r10,`-$FRAME+7*$SIZE_T`(%sp)
+	$PUSH	%r11,`-$FRAME+8*$SIZE_T`(%sp)
+	$PUSH	%r12,`-$FRAME+9*$SIZE_T`(%sp)
+	$PUSH	%r13,`-$FRAME+10*$SIZE_T`(%sp)
+	$PUSH	%r14,`-$FRAME+11*$SIZE_T`(%sp)
+	$PUSH	%r15,`-$FRAME+12*$SIZE_T`(%sp)
+	$PUSH	%r16,`-$FRAME+13*$SIZE_T`(%sp)
+	$PUSH	%r17,`-$FRAME+14*$SIZE_T`(%sp)
+	$PUSH	%r18,`-$FRAME+15*$SIZE_T`(%sp)
+
+	blr	%r0,$tbl
+	ldi	3,$t0
+L\$dec_pic
+	andcm	$tbl,$t0,$tbl
+	ldo	L\$AES_Td-L\$dec_pic($tbl),$tbl
+
+	and	$inp,$t0,$t0
+	sub	$inp,$t0,$inp
+	ldw	0($inp),$s0
+	ldw	4($inp),$s1
+	ldw	8($inp),$s2
+	comib,=	0,$t0,L\$dec_inp_aligned
+	ldw	12($inp),$s3
+
+	sh3addl	$t0,%r0,$t0
+	subi	32,$t0,$t0
+	mtctl	$t0,%cr11
+	ldw	16($inp),$t1
+	vshd	$s0,$s1,$s0
+	vshd	$s1,$s2,$s1
+	vshd	$s2,$s3,$s2
+	vshd	$s3,$t1,$s3
+
+L\$dec_inp_aligned
+	bl	_parisc_AES_decrypt,%r31
+	nop
+
+	extru,<> $out,31,2,%r0
+	b	L\$dec_out_aligned
+	nop
+
+	_srm	$s0,24,$acc0
+	_srm	$s0,16,$acc1
+	stb	$acc0,0($out)
+	_srm	$s0,8,$acc2
+	stb	$acc1,1($out)
+	_srm	$s1,24,$acc4
+	stb	$acc2,2($out)
+	_srm	$s1,16,$acc5
+	stb	$s0,3($out)
+	_srm	$s1,8,$acc6
+	stb	$acc4,4($out)
+	_srm	$s2,24,$acc0
+	stb	$acc5,5($out)
+	_srm	$s2,16,$acc1
+	stb	$acc6,6($out)
+	_srm	$s2,8,$acc2
+	stb	$s1,7($out)
+	_srm	$s3,24,$acc4
+	stb	$acc0,8($out)
+	_srm	$s3,16,$acc5
+	stb	$acc1,9($out)
+	_srm	$s3,8,$acc6
+	stb	$acc2,10($out)
+	stb	$s2,11($out)
+	stb	$acc4,12($out)
+	stb	$acc5,13($out)
+	stb	$acc6,14($out)
+	b	L\$dec_done
+	stb	$s3,15($out)
+
+L\$dec_out_aligned
+	stw	$s0,0($out)
+	stw	$s1,4($out)
+	stw	$s2,8($out)
+	stw	$s3,12($out)
+
+L\$dec_done
+	$POP	`-$FRAME-$SAVED_RP`(%sp),%r2	; standard epilogue
+	$POP	`-$FRAME+1*$SIZE_T`(%sp),%r4
+	$POP	`-$FRAME+2*$SIZE_T`(%sp),%r5
+	$POP	`-$FRAME+3*$SIZE_T`(%sp),%r6
+	$POP	`-$FRAME+4*$SIZE_T`(%sp),%r7
+	$POP	`-$FRAME+5*$SIZE_T`(%sp),%r8
+	$POP	`-$FRAME+6*$SIZE_T`(%sp),%r9
+	$POP	`-$FRAME+7*$SIZE_T`(%sp),%r10
+	$POP	`-$FRAME+8*$SIZE_T`(%sp),%r11
+	$POP	`-$FRAME+9*$SIZE_T`(%sp),%r12
+	$POP	`-$FRAME+10*$SIZE_T`(%sp),%r13
+	$POP	`-$FRAME+11*$SIZE_T`(%sp),%r14
+	$POP	`-$FRAME+12*$SIZE_T`(%sp),%r15
+	$POP	`-$FRAME+13*$SIZE_T`(%sp),%r16
+	$POP	`-$FRAME+14*$SIZE_T`(%sp),%r17
+	$POP	`-$FRAME+15*$SIZE_T`(%sp),%r18
+	bv	(%r2)
+	.EXIT
+	$POPMB	-$FRAME(%sp),%r3
+	.PROCEND
+
+	.ALIGN	16
+_parisc_AES_decrypt
+	.PROC
+	.CALLINFO	MILLICODE
+	.ENTRY
+	ldw	240($key),$rounds
+	ldw	0($key),$t0
+	ldw	4($key),$t1
+	ldw	8($key),$t2
+	ldw	12($key),$t3
+	_srm	$rounds,1,$rounds
+	xor	$t0,$s0,$s0
+	ldw	16($key),$t0
+	xor	$t1,$s1,$s1
+	ldw	20($key),$t1
+	_srm	$s0,24,$acc0
+	xor	$t2,$s2,$s2
+	ldw	24($key),$t2
+	xor	$t3,$s3,$s3
+	ldw	28($key),$t3
+	_srm	$s3,16,$acc1
+L\$dec_loop
+	_srm	$s2,8,$acc2
+	ldwx,s	$acc0($tbl),$acc0
+	_srm	$s1,0,$acc3
+	ldwx,s	$acc1($tbl),$acc1
+	_srm	$s1,24,$acc4
+	ldwx,s	$acc2($tbl),$acc2
+	_srm	$s0,16,$acc5
+	ldwx,s	$acc3($tbl),$acc3
+	_srm	$s3,8,$acc6
+	ldwx,s	$acc4($tbl),$acc4
+	_srm	$s2,0,$acc7
+	ldwx,s	$acc5($tbl),$acc5
+	_srm	$s2,24,$acc8
+	ldwx,s	$acc6($tbl),$acc6
+	_srm	$s1,16,$acc9
+	ldwx,s	$acc7($tbl),$acc7
+	_srm	$s0,8,$acc10
+	ldwx,s	$acc8($tbl),$acc8
+	_srm	$s3,0,$acc11
+	ldwx,s	$acc9($tbl),$acc9
+	_srm	$s3,24,$acc12
+	ldwx,s	$acc10($tbl),$acc10
+	_srm	$s2,16,$acc13
+	ldwx,s	$acc11($tbl),$acc11
+	_srm	$s1,8,$acc14
+	ldwx,s	$acc12($tbl),$acc12
+	_srm	$s0,0,$acc15
+	ldwx,s	$acc13($tbl),$acc13
+	ldwx,s	$acc14($tbl),$acc14
+	ldwx,s	$acc15($tbl),$acc15
+	addib,= -1,$rounds,L\$dec_last
+	ldo	32($key),$key
+
+		_ror	$acc1,8,$acc1
+		xor	$acc0,$t0,$t0
+	ldw	0($key),$s0
+		_ror	$acc2,16,$acc2
+		xor	$acc1,$t0,$t0
+	ldw	4($key),$s1
+		_ror	$acc3,24,$acc3
+		xor	$acc2,$t0,$t0
+	ldw	8($key),$s2
+		_ror	$acc5,8,$acc5
+		xor	$acc3,$t0,$t0
+	ldw	12($key),$s3
+		_ror	$acc6,16,$acc6
+		xor	$acc4,$t1,$t1
+		_ror	$acc7,24,$acc7
+		xor	$acc5,$t1,$t1
+		_ror	$acc9,8,$acc9
+		xor	$acc6,$t1,$t1
+		_ror	$acc10,16,$acc10
+		xor	$acc7,$t1,$t1
+		_ror	$acc11,24,$acc11
+		xor	$acc8,$t2,$t2
+		_ror	$acc13,8,$acc13
+		xor	$acc9,$t2,$t2
+		_ror	$acc14,16,$acc14
+		xor	$acc10,$t2,$t2
+		_ror	$acc15,24,$acc15
+		xor	$acc11,$t2,$t2
+		xor	$acc12,$acc14,$acc14
+		xor	$acc13,$t3,$t3
+	_srm	$t0,24,$acc0
+		xor	$acc14,$t3,$t3
+		xor	$acc15,$t3,$t3
+	_srm	$t3,16,$acc1
+
+	_srm	$t2,8,$acc2
+	ldwx,s	$acc0($tbl),$acc0
+	_srm	$t1,0,$acc3
+	ldwx,s	$acc1($tbl),$acc1
+	_srm	$t1,24,$acc4
+	ldwx,s	$acc2($tbl),$acc2
+	_srm	$t0,16,$acc5
+	ldwx,s	$acc3($tbl),$acc3
+	_srm	$t3,8,$acc6
+	ldwx,s	$acc4($tbl),$acc4
+	_srm	$t2,0,$acc7
+	ldwx,s	$acc5($tbl),$acc5
+	_srm	$t2,24,$acc8
+	ldwx,s	$acc6($tbl),$acc6
+	_srm	$t1,16,$acc9
+	ldwx,s	$acc7($tbl),$acc7
+	_srm	$t0,8,$acc10
+	ldwx,s	$acc8($tbl),$acc8
+	_srm	$t3,0,$acc11
+	ldwx,s	$acc9($tbl),$acc9
+	_srm	$t3,24,$acc12
+	ldwx,s	$acc10($tbl),$acc10
+	_srm	$t2,16,$acc13
+	ldwx,s	$acc11($tbl),$acc11
+	_srm	$t1,8,$acc14
+	ldwx,s	$acc12($tbl),$acc12
+	_srm	$t0,0,$acc15
+	ldwx,s	$acc13($tbl),$acc13
+		_ror	$acc1,8,$acc1
+	ldwx,s	$acc14($tbl),$acc14
+
+		_ror	$acc2,16,$acc2
+		xor	$acc0,$s0,$s0
+	ldwx,s	$acc15($tbl),$acc15
+		_ror	$acc3,24,$acc3
+		xor	$acc1,$s0,$s0
+	ldw	16($key),$t0
+		_ror	$acc5,8,$acc5
+		xor	$acc2,$s0,$s0
+	ldw	20($key),$t1
+		_ror	$acc6,16,$acc6
+		xor	$acc3,$s0,$s0
+	ldw	24($key),$t2
+		_ror	$acc7,24,$acc7
+		xor	$acc4,$s1,$s1
+	ldw	28($key),$t3
+		_ror	$acc9,8,$acc9
+		xor	$acc5,$s1,$s1
+	ldw	1024+0($tbl),%r0		; prefetch td4
+		_ror	$acc10,16,$acc10
+		xor	$acc6,$s1,$s1
+	ldw	1024+32($tbl),%r0		; prefetch td4
+		_ror	$acc11,24,$acc11
+		xor	$acc7,$s1,$s1
+	ldw	1024+64($tbl),%r0		; prefetch td4
+		_ror	$acc13,8,$acc13
+		xor	$acc8,$s2,$s2
+	ldw	1024+96($tbl),%r0		; prefetch td4
+		_ror	$acc14,16,$acc14
+		xor	$acc9,$s2,$s2
+	ldw	1024+128($tbl),%r0		; prefetch td4
+		_ror	$acc15,24,$acc15
+		xor	$acc10,$s2,$s2
+	ldw	1024+160($tbl),%r0		; prefetch td4
+	_srm	$s0,24,$acc0
+		xor	$acc11,$s2,$s2
+	ldw	1024+192($tbl),%r0		; prefetch td4
+		xor	$acc12,$acc14,$acc14
+		xor	$acc13,$s3,$s3
+	ldw	1024+224($tbl),%r0		; prefetch td4
+		xor	$acc14,$s3,$s3
+		xor	$acc15,$s3,$s3
+	b	L\$dec_loop
+	_srm	$s3,16,$acc1
+
+	.ALIGN	16
+L\$dec_last
+	ldo	1024($tbl),$rounds
+		_ror	$acc1,8,$acc1
+		xor	$acc0,$t0,$t0
+	ldw	0($key),$s0
+		_ror	$acc2,16,$acc2
+		xor	$acc1,$t0,$t0
+	ldw	4($key),$s1
+		_ror	$acc3,24,$acc3
+		xor	$acc2,$t0,$t0
+	ldw	8($key),$s2
+		_ror	$acc5,8,$acc5
+		xor	$acc3,$t0,$t0
+	ldw	12($key),$s3
+		_ror	$acc6,16,$acc6
+		xor	$acc4,$t1,$t1
+		_ror	$acc7,24,$acc7
+		xor	$acc5,$t1,$t1
+		_ror	$acc9,8,$acc9
+		xor	$acc6,$t1,$t1
+		_ror	$acc10,16,$acc10
+		xor	$acc7,$t1,$t1
+		_ror	$acc11,24,$acc11
+		xor	$acc8,$t2,$t2
+		_ror	$acc13,8,$acc13
+		xor	$acc9,$t2,$t2
+		_ror	$acc14,16,$acc14
+		xor	$acc10,$t2,$t2
+		_ror	$acc15,24,$acc15
+		xor	$acc11,$t2,$t2
+		xor	$acc12,$acc14,$acc14
+		xor	$acc13,$t3,$t3
+	_srm	$t0,24,$acc0
+		xor	$acc14,$t3,$t3
+		xor	$acc15,$t3,$t3
+	_srm	$t3,16,$acc1
+
+	_srm	$t2,8,$acc2
+	ldbx	$acc0($rounds),$acc0
+	_srm	$t1,24,$acc4
+	ldbx	$acc1($rounds),$acc1
+	_srm	$t0,16,$acc5
+	_srm	$t1,0,$acc3
+	ldbx	$acc2($rounds),$acc2
+	ldbx	$acc3($rounds),$acc3
+	_srm	$t3,8,$acc6
+	ldbx	$acc4($rounds),$acc4
+	_srm	$t2,24,$acc8
+	ldbx	$acc5($rounds),$acc5
+	_srm	$t1,16,$acc9
+	_srm	$t2,0,$acc7
+	ldbx	$acc6($rounds),$acc6
+	ldbx	$acc7($rounds),$acc7
+	_srm	$t0,8,$acc10
+	ldbx	$acc8($rounds),$acc8
+	_srm	$t3,24,$acc12
+	ldbx	$acc9($rounds),$acc9
+	_srm	$t2,16,$acc13
+	_srm	$t3,0,$acc11
+	ldbx	$acc10($rounds),$acc10
+	_srm	$t1,8,$acc14
+	ldbx	$acc11($rounds),$acc11
+	ldbx	$acc12($rounds),$acc12
+	ldbx	$acc13($rounds),$acc13
+	_srm	$t0,0,$acc15
+	ldbx	$acc14($rounds),$acc14
+
+		dep	$acc0,7,8,$acc3
+	ldbx	$acc15($rounds),$acc15
+		dep	$acc4,7,8,$acc7
+		dep	$acc1,15,8,$acc3
+		dep	$acc5,15,8,$acc7
+		dep	$acc2,23,8,$acc3
+		dep	$acc6,23,8,$acc7
+		xor	$acc3,$s0,$s0
+		xor	$acc7,$s1,$s1
+		dep	$acc8,7,8,$acc11
+		dep	$acc12,7,8,$acc15
+		dep	$acc9,15,8,$acc11
+		dep	$acc13,15,8,$acc15
+		dep	$acc10,23,8,$acc11
+		dep	$acc14,23,8,$acc15
+		xor	$acc11,$s2,$s2
+
+	bv	(%r31)
+	.EXIT
+		xor	$acc15,$s3,$s3
+	.PROCEND
+
+	.ALIGN	64
+L\$AES_Td
+	.WORD	0x51f4a750, 0x7e416553, 0x1a17a4c3, 0x3a275e96
+	.WORD	0x3bab6bcb, 0x1f9d45f1, 0xacfa58ab, 0x4be30393
+	.WORD	0x2030fa55, 0xad766df6, 0x88cc7691, 0xf5024c25
+	.WORD	0x4fe5d7fc, 0xc52acbd7, 0x26354480, 0xb562a38f
+	.WORD	0xdeb15a49, 0x25ba1b67, 0x45ea0e98, 0x5dfec0e1
+	.WORD	0xc32f7502, 0x814cf012, 0x8d4697a3, 0x6bd3f9c6
+	.WORD	0x038f5fe7, 0x15929c95, 0xbf6d7aeb, 0x955259da
+	.WORD	0xd4be832d, 0x587421d3, 0x49e06929, 0x8ec9c844
+	.WORD	0x75c2896a, 0xf48e7978, 0x99583e6b, 0x27b971dd
+	.WORD	0xbee14fb6, 0xf088ad17, 0xc920ac66, 0x7dce3ab4
+	.WORD	0x63df4a18, 0xe51a3182, 0x97513360, 0x62537f45
+	.WORD	0xb16477e0, 0xbb6bae84, 0xfe81a01c, 0xf9082b94
+	.WORD	0x70486858, 0x8f45fd19, 0x94de6c87, 0x527bf8b7
+	.WORD	0xab73d323, 0x724b02e2, 0xe31f8f57, 0x6655ab2a
+	.WORD	0xb2eb2807, 0x2fb5c203, 0x86c57b9a, 0xd33708a5
+	.WORD	0x302887f2, 0x23bfa5b2, 0x02036aba, 0xed16825c
+	.WORD	0x8acf1c2b, 0xa779b492, 0xf307f2f0, 0x4e69e2a1
+	.WORD	0x65daf4cd, 0x0605bed5, 0xd134621f, 0xc4a6fe8a
+	.WORD	0x342e539d, 0xa2f355a0, 0x058ae132, 0xa4f6eb75
+	.WORD	0x0b83ec39, 0x4060efaa, 0x5e719f06, 0xbd6e1051
+	.WORD	0x3e218af9, 0x96dd063d, 0xdd3e05ae, 0x4de6bd46
+	.WORD	0x91548db5, 0x71c45d05, 0x0406d46f, 0x605015ff
+	.WORD	0x1998fb24, 0xd6bde997, 0x894043cc, 0x67d99e77
+	.WORD	0xb0e842bd, 0x07898b88, 0xe7195b38, 0x79c8eedb
+	.WORD	0xa17c0a47, 0x7c420fe9, 0xf8841ec9, 0x00000000
+	.WORD	0x09808683, 0x322bed48, 0x1e1170ac, 0x6c5a724e
+	.WORD	0xfd0efffb, 0x0f853856, 0x3daed51e, 0x362d3927
+	.WORD	0x0a0fd964, 0x685ca621, 0x9b5b54d1, 0x24362e3a
+	.WORD	0x0c0a67b1, 0x9357e70f, 0xb4ee96d2, 0x1b9b919e
+	.WORD	0x80c0c54f, 0x61dc20a2, 0x5a774b69, 0x1c121a16
+	.WORD	0xe293ba0a, 0xc0a02ae5, 0x3c22e043, 0x121b171d
+	.WORD	0x0e090d0b, 0xf28bc7ad, 0x2db6a8b9, 0x141ea9c8
+	.WORD	0x57f11985, 0xaf75074c, 0xee99ddbb, 0xa37f60fd
+	.WORD	0xf701269f, 0x5c72f5bc, 0x44663bc5, 0x5bfb7e34
+	.WORD	0x8b432976, 0xcb23c6dc, 0xb6edfc68, 0xb8e4f163
+	.WORD	0xd731dcca, 0x42638510, 0x13972240, 0x84c61120
+	.WORD	0x854a247d, 0xd2bb3df8, 0xaef93211, 0xc729a16d
+	.WORD	0x1d9e2f4b, 0xdcb230f3, 0x0d8652ec, 0x77c1e3d0
+	.WORD	0x2bb3166c, 0xa970b999, 0x119448fa, 0x47e96422
+	.WORD	0xa8fc8cc4, 0xa0f03f1a, 0x567d2cd8, 0x223390ef
+	.WORD	0x87494ec7, 0xd938d1c1, 0x8ccaa2fe, 0x98d40b36
+	.WORD	0xa6f581cf, 0xa57ade28, 0xdab78e26, 0x3fadbfa4
+	.WORD	0x2c3a9de4, 0x5078920d, 0x6a5fcc9b, 0x547e4662
+	.WORD	0xf68d13c2, 0x90d8b8e8, 0x2e39f75e, 0x82c3aff5
+	.WORD	0x9f5d80be, 0x69d0937c, 0x6fd52da9, 0xcf2512b3
+	.WORD	0xc8ac993b, 0x10187da7, 0xe89c636e, 0xdb3bbb7b
+	.WORD	0xcd267809, 0x6e5918f4, 0xec9ab701, 0x834f9aa8
+	.WORD	0xe6956e65, 0xaaffe67e, 0x21bccf08, 0xef15e8e6
+	.WORD	0xbae79bd9, 0x4a6f36ce, 0xea9f09d4, 0x29b07cd6
+	.WORD	0x31a4b2af, 0x2a3f2331, 0xc6a59430, 0x35a266c0
+	.WORD	0x744ebc37, 0xfc82caa6, 0xe090d0b0, 0x33a7d815
+	.WORD	0xf104984a, 0x41ecdaf7, 0x7fcd500e, 0x1791f62f
+	.WORD	0x764dd68d, 0x43efb04d, 0xccaa4d54, 0xe49604df
+	.WORD	0x9ed1b5e3, 0x4c6a881b, 0xc12c1fb8, 0x4665517f
+	.WORD	0x9d5eea04, 0x018c355d, 0xfa877473, 0xfb0b412e
+	.WORD	0xb3671d5a, 0x92dbd252, 0xe9105633, 0x6dd64713
+	.WORD	0x9ad7618c, 0x37a10c7a, 0x59f8148e, 0xeb133c89
+	.WORD	0xcea927ee, 0xb761c935, 0xe11ce5ed, 0x7a47b13c
+	.WORD	0x9cd2df59, 0x55f2733f, 0x1814ce79, 0x73c737bf
+	.WORD	0x53f7cdea, 0x5ffdaa5b, 0xdf3d6f14, 0x7844db86
+	.WORD	0xcaaff381, 0xb968c43e, 0x3824342c, 0xc2a3405f
+	.WORD	0x161dc372, 0xbce2250c, 0x283c498b, 0xff0d9541
+	.WORD	0x39a80171, 0x080cb3de, 0xd8b4e49c, 0x6456c190
+	.WORD	0x7bcb8461, 0xd532b670, 0x486c5c74, 0xd0b85742
+	.BYTE	0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38
+	.BYTE	0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb
+	.BYTE	0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87
+	.BYTE	0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb
+	.BYTE	0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d
+	.BYTE	0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e
+	.BYTE	0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2
+	.BYTE	0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25
+	.BYTE	0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16
+	.BYTE	0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92
+	.BYTE	0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda
+	.BYTE	0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84
+	.BYTE	0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a
+	.BYTE	0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06
+	.BYTE	0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02
+	.BYTE	0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b
+	.BYTE	0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea
+	.BYTE	0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73
+	.BYTE	0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85
+	.BYTE	0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e
+	.BYTE	0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89
+	.BYTE	0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b
+	.BYTE	0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20
+	.BYTE	0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4
+	.BYTE	0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31
+	.BYTE	0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f
+	.BYTE	0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d
+	.BYTE	0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef
+	.BYTE	0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0
+	.BYTE	0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61
+	.BYTE	0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26
+	.BYTE	0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d
+	.STRINGZ "AES for PA-RISC, CRYPTOGAMS by "
+___
+
+foreach (split("\n",$code)) {
+	s/\`([^\`]*)\`/eval $1/ge;
+
+	# translate made up instructons: _ror, _srm
+	s/_ror(\s+)(%r[0-9]+),/shd$1$2,$2,/				or
+
+	s/_srm(\s+%r[0-9]+),([0-9]+),/
+		$SIZE_T==4 ? sprintf("extru%s,%d,8,",$1,31-$2)
+		:            sprintf("extrd,u%s,%d,8,",$1,63-$2)/e;
+
+	s/,\*/,/			if ($SIZE_T==4);
+	s/\bbv\b(.*\(%r2\))/bve$1/	if ($SIZE_T==8);
+	print $_,"\n";
+}
+close STDOUT;
diff --git a/crypto/aes/asm/aes-ppc.pl b/crypto/aes/asm/aes-ppc.pl
new file mode 100644
index 000000000..1558d8e45
--- /dev/null
+++ b/crypto/aes/asm/aes-ppc.pl
@@ -0,0 +1,1459 @@
+#! /usr/bin/env perl
+# Copyright 2007-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+
+# ====================================================================
+# Written by Andy Polyakov  for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+
+# Needs more work: key setup, CBC routine...
+#
+# ppc_AES_[en|de]crypt perform at 18 cycles per byte processed with
+# 128-bit key, which is ~40% better than 64-bit code generated by gcc
+# 4.0. But these are not the ones currently used! Their "compact"
+# counterparts are, for security reason. ppc_AES_encrypt_compact runs
+# at 1/2 of ppc_AES_encrypt speed, while ppc_AES_decrypt_compact -
+# at 1/3 of ppc_AES_decrypt.
+
+# February 2010
+#
+# Rescheduling instructions to favour Power6 pipeline gave 10%
+# performance improvement on the platform in question (and marginal
+# improvement even on others). It should be noted that Power6 fails
+# to process byte in 18 cycles, only in 23, because it fails to issue
+# 4 load instructions in two cycles, only in 3. As result non-compact
+# block subroutines are 25% slower than one would expect. Compact
+# functions scale better, because they have pure computational part,
+# which scales perfectly with clock frequency. To be specific
+# ppc_AES_encrypt_compact operates at 42 cycles per byte, while
+# ppc_AES_decrypt_compact - at 55 (in 64-bit build).
+
+$flavour = shift;
+
+if ($flavour =~ /64/) {
+	$SIZE_T	=8;
+	$LRSAVE	=2*$SIZE_T;
+	$STU	="stdu";
+	$POP	="ld";
+	$PUSH	="std";
+} elsif ($flavour =~ /32/) {
+	$SIZE_T	=4;
+	$LRSAVE	=$SIZE_T;
+	$STU	="stwu";
+	$POP	="lwz";
+	$PUSH	="stw";
+} else { die "nonsense $flavour"; }
+
+$LITTLE_ENDIAN = ($flavour=~/le$/) ? $SIZE_T : 0;
+
+$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+( $xlate="${dir}ppc-xlate.pl" and -f $xlate ) or
+( $xlate="${dir}../../perlasm/ppc-xlate.pl" and -f $xlate) or
+die "can't locate ppc-xlate.pl";
+
+open STDOUT,"| $^X $xlate $flavour ".shift || die "can't call $xlate: $!";
+
+$FRAME=32*$SIZE_T;
+
+sub _data_word()
+{ my $i;
+    while(defined($i=shift)) { $code.=sprintf"\t.long\t0x%08x,0x%08x\n",$i,$i; }
+}
+
+$sp="r1";
+$toc="r2";
+$inp="r3";
+$out="r4";
+$key="r5";
+
+$Tbl0="r3";
+$Tbl1="r6";
+$Tbl2="r7";
+$Tbl3=$out;	# stay away from "r2"; $out is offloaded to stack
+
+$s0="r8";
+$s1="r9";
+$s2="r10";
+$s3="r11";
+
+$t0="r12";
+$t1="r0";	# stay away from "r13";
+$t2="r14";
+$t3="r15";
+
+$acc00="r16";
+$acc01="r17";
+$acc02="r18";
+$acc03="r19";
+
+$acc04="r20";
+$acc05="r21";
+$acc06="r22";
+$acc07="r23";
+
+$acc08="r24";
+$acc09="r25";
+$acc10="r26";
+$acc11="r27";
+
+$acc12="r28";
+$acc13="r29";
+$acc14="r30";
+$acc15="r31";
+
+$mask80=$Tbl2;
+$mask1b=$Tbl3;
+
+$code.=<<___;
+.machine	"any"
+.text
+
+.align	7
+LAES_Te:
+	mflr	r0
+	bcl	20,31,\$+4
+	mflr	$Tbl0	;    vvvvv "distance" between . and 1st data entry
+	addi	$Tbl0,$Tbl0,`128-8`
+	mtlr	r0
+	blr
+	.long	0
+	.byte	0,12,0x14,0,0,0,0,0
+	.space	`64-9*4`
+LAES_Td:
+	mflr	r0
+	bcl	20,31,\$+4
+	mflr	$Tbl0	;    vvvvvvvv "distance" between . and 1st data entry
+	addi	$Tbl0,$Tbl0,`128-64-8+2048+256`
+	mtlr	r0
+	blr
+	.long	0
+	.byte	0,12,0x14,0,0,0,0,0
+	.space	`128-64-9*4`
+___
+&_data_word(
+	0xc66363a5, 0xf87c7c84, 0xee777799, 0xf67b7b8d,
+	0xfff2f20d, 0xd66b6bbd, 0xde6f6fb1, 0x91c5c554,
+	0x60303050, 0x02010103, 0xce6767a9, 0x562b2b7d,
+	0xe7fefe19, 0xb5d7d762, 0x4dababe6, 0xec76769a,
+	0x8fcaca45, 0x1f82829d, 0x89c9c940, 0xfa7d7d87,
+	0xeffafa15, 0xb25959eb, 0x8e4747c9, 0xfbf0f00b,
+	0x41adadec, 0xb3d4d467, 0x5fa2a2fd, 0x45afafea,
+	0x239c9cbf, 0x53a4a4f7, 0xe4727296, 0x9bc0c05b,
+	0x75b7b7c2, 0xe1fdfd1c, 0x3d9393ae, 0x4c26266a,
+	0x6c36365a, 0x7e3f3f41, 0xf5f7f702, 0x83cccc4f,
+	0x6834345c, 0x51a5a5f4, 0xd1e5e534, 0xf9f1f108,
+	0xe2717193, 0xabd8d873, 0x62313153, 0x2a15153f,
+	0x0804040c, 0x95c7c752, 0x46232365, 0x9dc3c35e,
+	0x30181828, 0x379696a1, 0x0a05050f, 0x2f9a9ab5,
+	0x0e070709, 0x24121236, 0x1b80809b, 0xdfe2e23d,
+	0xcdebeb26, 0x4e272769, 0x7fb2b2cd, 0xea75759f,
+	0x1209091b, 0x1d83839e, 0x582c2c74, 0x341a1a2e,
+	0x361b1b2d, 0xdc6e6eb2, 0xb45a5aee, 0x5ba0a0fb,
+	0xa45252f6, 0x763b3b4d, 0xb7d6d661, 0x7db3b3ce,
+	0x5229297b, 0xdde3e33e, 0x5e2f2f71, 0x13848497,
+	0xa65353f5, 0xb9d1d168, 0x00000000, 0xc1eded2c,
+	0x40202060, 0xe3fcfc1f, 0x79b1b1c8, 0xb65b5bed,
+	0xd46a6abe, 0x8dcbcb46, 0x67bebed9, 0x7239394b,
+	0x944a4ade, 0x984c4cd4, 0xb05858e8, 0x85cfcf4a,
+	0xbbd0d06b, 0xc5efef2a, 0x4faaaae5, 0xedfbfb16,
+	0x864343c5, 0x9a4d4dd7, 0x66333355, 0x11858594,
+	0x8a4545cf, 0xe9f9f910, 0x04020206, 0xfe7f7f81,
+	0xa05050f0, 0x783c3c44, 0x259f9fba, 0x4ba8a8e3,
+	0xa25151f3, 0x5da3a3fe, 0x804040c0, 0x058f8f8a,
+	0x3f9292ad, 0x219d9dbc, 0x70383848, 0xf1f5f504,
+	0x63bcbcdf, 0x77b6b6c1, 0xafdada75, 0x42212163,
+	0x20101030, 0xe5ffff1a, 0xfdf3f30e, 0xbfd2d26d,
+	0x81cdcd4c, 0x180c0c14, 0x26131335, 0xc3ecec2f,
+	0xbe5f5fe1, 0x359797a2, 0x884444cc, 0x2e171739,
+	0x93c4c457, 0x55a7a7f2, 0xfc7e7e82, 0x7a3d3d47,
+	0xc86464ac, 0xba5d5de7, 0x3219192b, 0xe6737395,
+	0xc06060a0, 0x19818198, 0x9e4f4fd1, 0xa3dcdc7f,
+	0x44222266, 0x542a2a7e, 0x3b9090ab, 0x0b888883,
+	0x8c4646ca, 0xc7eeee29, 0x6bb8b8d3, 0x2814143c,
+	0xa7dede79, 0xbc5e5ee2, 0x160b0b1d, 0xaddbdb76,
+	0xdbe0e03b, 0x64323256, 0x743a3a4e, 0x140a0a1e,
+	0x924949db, 0x0c06060a, 0x4824246c, 0xb85c5ce4,
+	0x9fc2c25d, 0xbdd3d36e, 0x43acacef, 0xc46262a6,
+	0x399191a8, 0x319595a4, 0xd3e4e437, 0xf279798b,
+	0xd5e7e732, 0x8bc8c843, 0x6e373759, 0xda6d6db7,
+	0x018d8d8c, 0xb1d5d564, 0x9c4e4ed2, 0x49a9a9e0,
+	0xd86c6cb4, 0xac5656fa, 0xf3f4f407, 0xcfeaea25,
+	0xca6565af, 0xf47a7a8e, 0x47aeaee9, 0x10080818,
+	0x6fbabad5, 0xf0787888, 0x4a25256f, 0x5c2e2e72,
+	0x381c1c24, 0x57a6a6f1, 0x73b4b4c7, 0x97c6c651,
+	0xcbe8e823, 0xa1dddd7c, 0xe874749c, 0x3e1f1f21,
+	0x964b4bdd, 0x61bdbddc, 0x0d8b8b86, 0x0f8a8a85,
+	0xe0707090, 0x7c3e3e42, 0x71b5b5c4, 0xcc6666aa,
+	0x904848d8, 0x06030305, 0xf7f6f601, 0x1c0e0e12,
+	0xc26161a3, 0x6a35355f, 0xae5757f9, 0x69b9b9d0,
+	0x17868691, 0x99c1c158, 0x3a1d1d27, 0x279e9eb9,
+	0xd9e1e138, 0xebf8f813, 0x2b9898b3, 0x22111133,
+	0xd26969bb, 0xa9d9d970, 0x078e8e89, 0x339494a7,
+	0x2d9b9bb6, 0x3c1e1e22, 0x15878792, 0xc9e9e920,
+	0x87cece49, 0xaa5555ff, 0x50282878, 0xa5dfdf7a,
+	0x038c8c8f, 0x59a1a1f8, 0x09898980, 0x1a0d0d17,
+	0x65bfbfda, 0xd7e6e631, 0x844242c6, 0xd06868b8,
+	0x824141c3, 0x299999b0, 0x5a2d2d77, 0x1e0f0f11,
+	0x7bb0b0cb, 0xa85454fc, 0x6dbbbbd6, 0x2c16163a);
+$code.=<<___;
+.byte	0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5
+.byte	0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76
+.byte	0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0
+.byte	0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0
+.byte	0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc
+.byte	0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15
+.byte	0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a
+.byte	0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75
+.byte	0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0
+.byte	0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84
+.byte	0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b
+.byte	0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf
+.byte	0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85
+.byte	0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8
+.byte	0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5
+.byte	0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2
+.byte	0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17
+.byte	0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73
+.byte	0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88
+.byte	0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb
+.byte	0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c
+.byte	0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79
+.byte	0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9
+.byte	0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08
+.byte	0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6
+.byte	0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a
+.byte	0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e
+.byte	0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e
+.byte	0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94
+.byte	0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf
+.byte	0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68
+.byte	0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16
+___
+&_data_word(
+	0x51f4a750, 0x7e416553, 0x1a17a4c3, 0x3a275e96,
+	0x3bab6bcb, 0x1f9d45f1, 0xacfa58ab, 0x4be30393,
+	0x2030fa55, 0xad766df6, 0x88cc7691, 0xf5024c25,
+	0x4fe5d7fc, 0xc52acbd7, 0x26354480, 0xb562a38f,
+	0xdeb15a49, 0x25ba1b67, 0x45ea0e98, 0x5dfec0e1,
+	0xc32f7502, 0x814cf012, 0x8d4697a3, 0x6bd3f9c6,
+	0x038f5fe7, 0x15929c95, 0xbf6d7aeb, 0x955259da,
+	0xd4be832d, 0x587421d3, 0x49e06929, 0x8ec9c844,
+	0x75c2896a, 0xf48e7978, 0x99583e6b, 0x27b971dd,
+	0xbee14fb6, 0xf088ad17, 0xc920ac66, 0x7dce3ab4,
+	0x63df4a18, 0xe51a3182, 0x97513360, 0x62537f45,
+	0xb16477e0, 0xbb6bae84, 0xfe81a01c, 0xf9082b94,
+	0x70486858, 0x8f45fd19, 0x94de6c87, 0x527bf8b7,
+	0xab73d323, 0x724b02e2, 0xe31f8f57, 0x6655ab2a,
+	0xb2eb2807, 0x2fb5c203, 0x86c57b9a, 0xd33708a5,
+	0x302887f2, 0x23bfa5b2, 0x02036aba, 0xed16825c,
+	0x8acf1c2b, 0xa779b492, 0xf307f2f0, 0x4e69e2a1,
+	0x65daf4cd, 0x0605bed5, 0xd134621f, 0xc4a6fe8a,
+	0x342e539d, 0xa2f355a0, 0x058ae132, 0xa4f6eb75,
+	0x0b83ec39, 0x4060efaa, 0x5e719f06, 0xbd6e1051,
+	0x3e218af9, 0x96dd063d, 0xdd3e05ae, 0x4de6bd46,
+	0x91548db5, 0x71c45d05, 0x0406d46f, 0x605015ff,
+	0x1998fb24, 0xd6bde997, 0x894043cc, 0x67d99e77,
+	0xb0e842bd, 0x07898b88, 0xe7195b38, 0x79c8eedb,
+	0xa17c0a47, 0x7c420fe9, 0xf8841ec9, 0x00000000,
+	0x09808683, 0x322bed48, 0x1e1170ac, 0x6c5a724e,
+	0xfd0efffb, 0x0f853856, 0x3daed51e, 0x362d3927,
+	0x0a0fd964, 0x685ca621, 0x9b5b54d1, 0x24362e3a,
+	0x0c0a67b1, 0x9357e70f, 0xb4ee96d2, 0x1b9b919e,
+	0x80c0c54f, 0x61dc20a2, 0x5a774b69, 0x1c121a16,
+	0xe293ba0a, 0xc0a02ae5, 0x3c22e043, 0x121b171d,
+	0x0e090d0b, 0xf28bc7ad, 0x2db6a8b9, 0x141ea9c8,
+	0x57f11985, 0xaf75074c, 0xee99ddbb, 0xa37f60fd,
+	0xf701269f, 0x5c72f5bc, 0x44663bc5, 0x5bfb7e34,
+	0x8b432976, 0xcb23c6dc, 0xb6edfc68, 0xb8e4f163,
+	0xd731dcca, 0x42638510, 0x13972240, 0x84c61120,
+	0x854a247d, 0xd2bb3df8, 0xaef93211, 0xc729a16d,
+	0x1d9e2f4b, 0xdcb230f3, 0x0d8652ec, 0x77c1e3d0,
+	0x2bb3166c, 0xa970b999, 0x119448fa, 0x47e96422,
+	0xa8fc8cc4, 0xa0f03f1a, 0x567d2cd8, 0x223390ef,
+	0x87494ec7, 0xd938d1c1, 0x8ccaa2fe, 0x98d40b36,
+	0xa6f581cf, 0xa57ade28, 0xdab78e26, 0x3fadbfa4,
+	0x2c3a9de4, 0x5078920d, 0x6a5fcc9b, 0x547e4662,
+	0xf68d13c2, 0x90d8b8e8, 0x2e39f75e, 0x82c3aff5,
+	0x9f5d80be, 0x69d0937c, 0x6fd52da9, 0xcf2512b3,
+	0xc8ac993b, 0x10187da7, 0xe89c636e, 0xdb3bbb7b,
+	0xcd267809, 0x6e5918f4, 0xec9ab701, 0x834f9aa8,
+	0xe6956e65, 0xaaffe67e, 0x21bccf08, 0xef15e8e6,
+	0xbae79bd9, 0x4a6f36ce, 0xea9f09d4, 0x29b07cd6,
+	0x31a4b2af, 0x2a3f2331, 0xc6a59430, 0x35a266c0,
+	0x744ebc37, 0xfc82caa6, 0xe090d0b0, 0x33a7d815,
+	0xf104984a, 0x41ecdaf7, 0x7fcd500e, 0x1791f62f,
+	0x764dd68d, 0x43efb04d, 0xccaa4d54, 0xe49604df,
+	0x9ed1b5e3, 0x4c6a881b, 0xc12c1fb8, 0x4665517f,
+	0x9d5eea04, 0x018c355d, 0xfa877473, 0xfb0b412e,
+	0xb3671d5a, 0x92dbd252, 0xe9105633, 0x6dd64713,
+	0x9ad7618c, 0x37a10c7a, 0x59f8148e, 0xeb133c89,
+	0xcea927ee, 0xb761c935, 0xe11ce5ed, 0x7a47b13c,
+	0x9cd2df59, 0x55f2733f, 0x1814ce79, 0x73c737bf,
+	0x53f7cdea, 0x5ffdaa5b, 0xdf3d6f14, 0x7844db86,
+	0xcaaff381, 0xb968c43e, 0x3824342c, 0xc2a3405f,
+	0x161dc372, 0xbce2250c, 0x283c498b, 0xff0d9541,
+	0x39a80171, 0x080cb3de, 0xd8b4e49c, 0x6456c190,
+	0x7bcb8461, 0xd532b670, 0x486c5c74, 0xd0b85742);
+$code.=<<___;
+.byte	0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38
+.byte	0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb
+.byte	0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87
+.byte	0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb
+.byte	0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d
+.byte	0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e
+.byte	0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2
+.byte	0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25
+.byte	0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16
+.byte	0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92
+.byte	0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda
+.byte	0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84
+.byte	0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a
+.byte	0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06
+.byte	0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02
+.byte	0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b
+.byte	0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea
+.byte	0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73
+.byte	0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85
+.byte	0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e
+.byte	0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89
+.byte	0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b
+.byte	0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20
+.byte	0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4
+.byte	0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31
+.byte	0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f
+.byte	0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d
+.byte	0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef
+.byte	0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0
+.byte	0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61
+.byte	0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26
+.byte	0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d
+
+
+.globl	.AES_encrypt
+.align	7
+.AES_encrypt:
+	$STU	$sp,-$FRAME($sp)
+	mflr	r0
+
+	$PUSH	$out,`$FRAME-$SIZE_T*19`($sp)
+	$PUSH	r14,`$FRAME-$SIZE_T*18`($sp)
+	$PUSH	r15,`$FRAME-$SIZE_T*17`($sp)
+	$PUSH	r16,`$FRAME-$SIZE_T*16`($sp)
+	$PUSH	r17,`$FRAME-$SIZE_T*15`($sp)
+	$PUSH	r18,`$FRAME-$SIZE_T*14`($sp)
+	$PUSH	r19,`$FRAME-$SIZE_T*13`($sp)
+	$PUSH	r20,`$FRAME-$SIZE_T*12`($sp)
+	$PUSH	r21,`$FRAME-$SIZE_T*11`($sp)
+	$PUSH	r22,`$FRAME-$SIZE_T*10`($sp)
+	$PUSH	r23,`$FRAME-$SIZE_T*9`($sp)
+	$PUSH	r24,`$FRAME-$SIZE_T*8`($sp)
+	$PUSH	r25,`$FRAME-$SIZE_T*7`($sp)
+	$PUSH	r26,`$FRAME-$SIZE_T*6`($sp)
+	$PUSH	r27,`$FRAME-$SIZE_T*5`($sp)
+	$PUSH	r28,`$FRAME-$SIZE_T*4`($sp)
+	$PUSH	r29,`$FRAME-$SIZE_T*3`($sp)
+	$PUSH	r30,`$FRAME-$SIZE_T*2`($sp)
+	$PUSH	r31,`$FRAME-$SIZE_T*1`($sp)
+	$PUSH	r0,`$FRAME+$LRSAVE`($sp)
+
+	andi.	$t0,$inp,3
+	andi.	$t1,$out,3
+	or.	$t0,$t0,$t1
+	bne	Lenc_unaligned
+
+Lenc_unaligned_ok:
+___
+$code.=<<___ if (!$LITTLE_ENDIAN);
+	lwz	$s0,0($inp)
+	lwz	$s1,4($inp)
+	lwz	$s2,8($inp)
+	lwz	$s3,12($inp)
+___
+$code.=<<___ if ($LITTLE_ENDIAN);
+	lwz	$t0,0($inp)
+	lwz	$t1,4($inp)
+	lwz	$t2,8($inp)
+	lwz	$t3,12($inp)
+	rotlwi	$s0,$t0,8
+	rotlwi	$s1,$t1,8
+	rotlwi	$s2,$t2,8
+	rotlwi	$s3,$t3,8
+	rlwimi	$s0,$t0,24,0,7
+	rlwimi	$s1,$t1,24,0,7
+	rlwimi	$s2,$t2,24,0,7
+	rlwimi	$s3,$t3,24,0,7
+	rlwimi	$s0,$t0,24,16,23
+	rlwimi	$s1,$t1,24,16,23
+	rlwimi	$s2,$t2,24,16,23
+	rlwimi	$s3,$t3,24,16,23
+___
+$code.=<<___;
+	bl	LAES_Te
+	bl	Lppc_AES_encrypt_compact
+	$POP	$out,`$FRAME-$SIZE_T*19`($sp)
+___
+$code.=<<___ if ($LITTLE_ENDIAN);
+	rotlwi	$t0,$s0,8
+	rotlwi	$t1,$s1,8
+	rotlwi	$t2,$s2,8
+	rotlwi	$t3,$s3,8
+	rlwimi	$t0,$s0,24,0,7
+	rlwimi	$t1,$s1,24,0,7
+	rlwimi	$t2,$s2,24,0,7
+	rlwimi	$t3,$s3,24,0,7
+	rlwimi	$t0,$s0,24,16,23
+	rlwimi	$t1,$s1,24,16,23
+	rlwimi	$t2,$s2,24,16,23
+	rlwimi	$t3,$s3,24,16,23
+	stw	$t0,0($out)
+	stw	$t1,4($out)
+	stw	$t2,8($out)
+	stw	$t3,12($out)
+___
+$code.=<<___ if (!$LITTLE_ENDIAN);
+	stw	$s0,0($out)
+	stw	$s1,4($out)
+	stw	$s2,8($out)
+	stw	$s3,12($out)
+___
+$code.=<<___;
+	b	Lenc_done
+
+Lenc_unaligned:
+	subfic	$t0,$inp,4096
+	subfic	$t1,$out,4096
+	andi.	$t0,$t0,4096-16
+	beq	Lenc_xpage
+	andi.	$t1,$t1,4096-16
+	bne	Lenc_unaligned_ok
+
+Lenc_xpage:
+	lbz	$acc00,0($inp)
+	lbz	$acc01,1($inp)
+	lbz	$acc02,2($inp)
+	lbz	$s0,3($inp)
+	lbz	$acc04,4($inp)
+	lbz	$acc05,5($inp)
+	lbz	$acc06,6($inp)
+	lbz	$s1,7($inp)
+	lbz	$acc08,8($inp)
+	lbz	$acc09,9($inp)
+	lbz	$acc10,10($inp)
+	insrwi	$s0,$acc00,8,0
+	lbz	$s2,11($inp)
+	insrwi	$s1,$acc04,8,0
+	lbz	$acc12,12($inp)
+	insrwi	$s0,$acc01,8,8
+	lbz	$acc13,13($inp)
+	insrwi	$s1,$acc05,8,8
+	lbz	$acc14,14($inp)
+	insrwi	$s0,$acc02,8,16
+	lbz	$s3,15($inp)
+	insrwi	$s1,$acc06,8,16
+	insrwi	$s2,$acc08,8,0
+	insrwi	$s3,$acc12,8,0
+	insrwi	$s2,$acc09,8,8
+	insrwi	$s3,$acc13,8,8
+	insrwi	$s2,$acc10,8,16
+	insrwi	$s3,$acc14,8,16
+
+	bl	LAES_Te
+	bl	Lppc_AES_encrypt_compact
+	$POP	$out,`$FRAME-$SIZE_T*19`($sp)
+
+	extrwi	$acc00,$s0,8,0
+	extrwi	$acc01,$s0,8,8
+	stb	$acc00,0($out)
+	extrwi	$acc02,$s0,8,16
+	stb	$acc01,1($out)
+	stb	$acc02,2($out)
+	extrwi	$acc04,$s1,8,0
+	stb	$s0,3($out)
+	extrwi	$acc05,$s1,8,8
+	stb	$acc04,4($out)
+	extrwi	$acc06,$s1,8,16
+	stb	$acc05,5($out)
+	stb	$acc06,6($out)
+	extrwi	$acc08,$s2,8,0
+	stb	$s1,7($out)
+	extrwi	$acc09,$s2,8,8
+	stb	$acc08,8($out)
+	extrwi	$acc10,$s2,8,16
+	stb	$acc09,9($out)
+	stb	$acc10,10($out)
+	extrwi	$acc12,$s3,8,0
+	stb	$s2,11($out)
+	extrwi	$acc13,$s3,8,8
+	stb	$acc12,12($out)
+	extrwi	$acc14,$s3,8,16
+	stb	$acc13,13($out)
+	stb	$acc14,14($out)
+	stb	$s3,15($out)
+
+Lenc_done:
+	$POP	r0,`$FRAME+$LRSAVE`($sp)
+	$POP	r14,`$FRAME-$SIZE_T*18`($sp)
+	$POP	r15,`$FRAME-$SIZE_T*17`($sp)
+	$POP	r16,`$FRAME-$SIZE_T*16`($sp)
+	$POP	r17,`$FRAME-$SIZE_T*15`($sp)
+	$POP	r18,`$FRAME-$SIZE_T*14`($sp)
+	$POP	r19,`$FRAME-$SIZE_T*13`($sp)
+	$POP	r20,`$FRAME-$SIZE_T*12`($sp)
+	$POP	r21,`$FRAME-$SIZE_T*11`($sp)
+	$POP	r22,`$FRAME-$SIZE_T*10`($sp)
+	$POP	r23,`$FRAME-$SIZE_T*9`($sp)
+	$POP	r24,`$FRAME-$SIZE_T*8`($sp)
+	$POP	r25,`$FRAME-$SIZE_T*7`($sp)
+	$POP	r26,`$FRAME-$SIZE_T*6`($sp)
+	$POP	r27,`$FRAME-$SIZE_T*5`($sp)
+	$POP	r28,`$FRAME-$SIZE_T*4`($sp)
+	$POP	r29,`$FRAME-$SIZE_T*3`($sp)
+	$POP	r30,`$FRAME-$SIZE_T*2`($sp)
+	$POP	r31,`$FRAME-$SIZE_T*1`($sp)
+	mtlr	r0
+	addi	$sp,$sp,$FRAME
+	blr
+	.long	0
+	.byte	0,12,4,1,0x80,18,3,0
+	.long	0
+
+.align	5
+Lppc_AES_encrypt:
+	lwz	$acc00,240($key)
+	addi	$Tbl1,$Tbl0,3
+	lwz	$t0,0($key)
+	addi	$Tbl2,$Tbl0,2
+	lwz	$t1,4($key)
+	addi	$Tbl3,$Tbl0,1
+	lwz	$t2,8($key)
+	addi	$acc00,$acc00,-1
+	lwz	$t3,12($key)
+	addi	$key,$key,16
+	xor	$s0,$s0,$t0
+	xor	$s1,$s1,$t1
+	xor	$s2,$s2,$t2
+	xor	$s3,$s3,$t3
+	mtctr	$acc00
+.align	4
+Lenc_loop:
+	rlwinm	$acc00,$s0,`32-24+3`,21,28
+	rlwinm	$acc01,$s1,`32-24+3`,21,28
+	rlwinm	$acc02,$s2,`32-24+3`,21,28
+	rlwinm	$acc03,$s3,`32-24+3`,21,28
+	lwz	$t0,0($key)
+	rlwinm	$acc04,$s1,`32-16+3`,21,28
+	lwz	$t1,4($key)
+	rlwinm	$acc05,$s2,`32-16+3`,21,28
+	lwz	$t2,8($key)
+	rlwinm	$acc06,$s3,`32-16+3`,21,28
+	lwz	$t3,12($key)
+	rlwinm	$acc07,$s0,`32-16+3`,21,28
+	lwzx	$acc00,$Tbl0,$acc00
+	rlwinm	$acc08,$s2,`32-8+3`,21,28
+	lwzx	$acc01,$Tbl0,$acc01
+	rlwinm	$acc09,$s3,`32-8+3`,21,28
+	lwzx	$acc02,$Tbl0,$acc02
+	rlwinm	$acc10,$s0,`32-8+3`,21,28
+	lwzx	$acc03,$Tbl0,$acc03
+	rlwinm	$acc11,$s1,`32-8+3`,21,28
+	lwzx	$acc04,$Tbl1,$acc04
+	rlwinm	$acc12,$s3,`0+3`,21,28
+	lwzx	$acc05,$Tbl1,$acc05
+	rlwinm	$acc13,$s0,`0+3`,21,28
+	lwzx	$acc06,$Tbl1,$acc06
+	rlwinm	$acc14,$s1,`0+3`,21,28
+	lwzx	$acc07,$Tbl1,$acc07
+	rlwinm	$acc15,$s2,`0+3`,21,28
+	lwzx	$acc08,$Tbl2,$acc08
+	xor	$t0,$t0,$acc00
+	lwzx	$acc09,$Tbl2,$acc09
+	xor	$t1,$t1,$acc01
+	lwzx	$acc10,$Tbl2,$acc10
+	xor	$t2,$t2,$acc02
+	lwzx	$acc11,$Tbl2,$acc11
+	xor	$t3,$t3,$acc03
+	lwzx	$acc12,$Tbl3,$acc12
+	xor	$t0,$t0,$acc04
+	lwzx	$acc13,$Tbl3,$acc13
+	xor	$t1,$t1,$acc05
+	lwzx	$acc14,$Tbl3,$acc14
+	xor	$t2,$t2,$acc06
+	lwzx	$acc15,$Tbl3,$acc15
+	xor	$t3,$t3,$acc07
+	xor	$t0,$t0,$acc08
+	xor	$t1,$t1,$acc09
+	xor	$t2,$t2,$acc10
+	xor	$t3,$t3,$acc11
+	xor	$s0,$t0,$acc12
+	xor	$s1,$t1,$acc13
+	xor	$s2,$t2,$acc14
+	xor	$s3,$t3,$acc15
+	addi	$key,$key,16
+	bdnz	Lenc_loop
+
+	addi	$Tbl2,$Tbl0,2048
+	nop
+	lwz	$t0,0($key)
+	rlwinm	$acc00,$s0,`32-24`,24,31
+	lwz	$t1,4($key)
+	rlwinm	$acc01,$s1,`32-24`,24,31
+	lwz	$t2,8($key)
+	rlwinm	$acc02,$s2,`32-24`,24,31
+	lwz	$t3,12($key)
+	rlwinm	$acc03,$s3,`32-24`,24,31
+	lwz	$acc08,`2048+0`($Tbl0)	! prefetch Te4
+	rlwinm	$acc04,$s1,`32-16`,24,31
+	lwz	$acc09,`2048+32`($Tbl0)
+	rlwinm	$acc05,$s2,`32-16`,24,31
+	lwz	$acc10,`2048+64`($Tbl0)
+	rlwinm	$acc06,$s3,`32-16`,24,31
+	lwz	$acc11,`2048+96`($Tbl0)
+	rlwinm	$acc07,$s0,`32-16`,24,31
+	lwz	$acc12,`2048+128`($Tbl0)
+	rlwinm	$acc08,$s2,`32-8`,24,31
+	lwz	$acc13,`2048+160`($Tbl0)
+	rlwinm	$acc09,$s3,`32-8`,24,31
+	lwz	$acc14,`2048+192`($Tbl0)
+	rlwinm	$acc10,$s0,`32-8`,24,31
+	lwz	$acc15,`2048+224`($Tbl0)
+	rlwinm	$acc11,$s1,`32-8`,24,31
+	lbzx	$acc00,$Tbl2,$acc00
+	rlwinm	$acc12,$s3,`0`,24,31
+	lbzx	$acc01,$Tbl2,$acc01
+	rlwinm	$acc13,$s0,`0`,24,31
+	lbzx	$acc02,$Tbl2,$acc02
+	rlwinm	$acc14,$s1,`0`,24,31
+	lbzx	$acc03,$Tbl2,$acc03
+	rlwinm	$acc15,$s2,`0`,24,31
+	lbzx	$acc04,$Tbl2,$acc04
+	rlwinm	$s0,$acc00,24,0,7
+	lbzx	$acc05,$Tbl2,$acc05
+	rlwinm	$s1,$acc01,24,0,7
+	lbzx	$acc06,$Tbl2,$acc06
+	rlwinm	$s2,$acc02,24,0,7
+	lbzx	$acc07,$Tbl2,$acc07
+	rlwinm	$s3,$acc03,24,0,7
+	lbzx	$acc08,$Tbl2,$acc08
+	rlwimi	$s0,$acc04,16,8,15
+	lbzx	$acc09,$Tbl2,$acc09
+	rlwimi	$s1,$acc05,16,8,15
+	lbzx	$acc10,$Tbl2,$acc10
+	rlwimi	$s2,$acc06,16,8,15
+	lbzx	$acc11,$Tbl2,$acc11
+	rlwimi	$s3,$acc07,16,8,15
+	lbzx	$acc12,$Tbl2,$acc12
+	rlwimi	$s0,$acc08,8,16,23
+	lbzx	$acc13,$Tbl2,$acc13
+	rlwimi	$s1,$acc09,8,16,23
+	lbzx	$acc14,$Tbl2,$acc14
+	rlwimi	$s2,$acc10,8,16,23
+	lbzx	$acc15,$Tbl2,$acc15
+	rlwimi	$s3,$acc11,8,16,23
+	or	$s0,$s0,$acc12
+	or	$s1,$s1,$acc13
+	or	$s2,$s2,$acc14
+	or	$s3,$s3,$acc15
+	xor	$s0,$s0,$t0
+	xor	$s1,$s1,$t1
+	xor	$s2,$s2,$t2
+	xor	$s3,$s3,$t3
+	blr
+	.long	0
+	.byte	0,12,0x14,0,0,0,0,0
+
+.align	4
+Lppc_AES_encrypt_compact:
+	lwz	$acc00,240($key)
+	addi	$Tbl1,$Tbl0,2048
+	lwz	$t0,0($key)
+	lis	$mask80,0x8080
+	lwz	$t1,4($key)
+	lis	$mask1b,0x1b1b
+	lwz	$t2,8($key)
+	ori	$mask80,$mask80,0x8080
+	lwz	$t3,12($key)
+	ori	$mask1b,$mask1b,0x1b1b
+	addi	$key,$key,16
+	mtctr	$acc00
+.align	4
+Lenc_compact_loop:
+	xor	$s0,$s0,$t0
+	xor	$s1,$s1,$t1
+	rlwinm	$acc00,$s0,`32-24`,24,31
+	xor	$s2,$s2,$t2
+	rlwinm	$acc01,$s1,`32-24`,24,31
+	xor	$s3,$s3,$t3
+	rlwinm	$acc02,$s2,`32-24`,24,31
+	rlwinm	$acc03,$s3,`32-24`,24,31
+	rlwinm	$acc04,$s1,`32-16`,24,31
+	rlwinm	$acc05,$s2,`32-16`,24,31
+	rlwinm	$acc06,$s3,`32-16`,24,31
+	rlwinm	$acc07,$s0,`32-16`,24,31
+	lbzx	$acc00,$Tbl1,$acc00
+	rlwinm	$acc08,$s2,`32-8`,24,31
+	lbzx	$acc01,$Tbl1,$acc01
+	rlwinm	$acc09,$s3,`32-8`,24,31
+	lbzx	$acc02,$Tbl1,$acc02
+	rlwinm	$acc10,$s0,`32-8`,24,31
+	lbzx	$acc03,$Tbl1,$acc03
+	rlwinm	$acc11,$s1,`32-8`,24,31
+	lbzx	$acc04,$Tbl1,$acc04
+	rlwinm	$acc12,$s3,`0`,24,31
+	lbzx	$acc05,$Tbl1,$acc05
+	rlwinm	$acc13,$s0,`0`,24,31
+	lbzx	$acc06,$Tbl1,$acc06
+	rlwinm	$acc14,$s1,`0`,24,31
+	lbzx	$acc07,$Tbl1,$acc07
+	rlwinm	$acc15,$s2,`0`,24,31
+	lbzx	$acc08,$Tbl1,$acc08
+	rlwinm	$s0,$acc00,24,0,7
+	lbzx	$acc09,$Tbl1,$acc09
+	rlwinm	$s1,$acc01,24,0,7
+	lbzx	$acc10,$Tbl1,$acc10
+	rlwinm	$s2,$acc02,24,0,7
+	lbzx	$acc11,$Tbl1,$acc11
+	rlwinm	$s3,$acc03,24,0,7
+	lbzx	$acc12,$Tbl1,$acc12
+	rlwimi	$s0,$acc04,16,8,15
+	lbzx	$acc13,$Tbl1,$acc13
+	rlwimi	$s1,$acc05,16,8,15
+	lbzx	$acc14,$Tbl1,$acc14
+	rlwimi	$s2,$acc06,16,8,15
+	lbzx	$acc15,$Tbl1,$acc15
+	rlwimi	$s3,$acc07,16,8,15
+	rlwimi	$s0,$acc08,8,16,23
+	rlwimi	$s1,$acc09,8,16,23
+	rlwimi	$s2,$acc10,8,16,23
+	rlwimi	$s3,$acc11,8,16,23
+	lwz	$t0,0($key)
+	or	$s0,$s0,$acc12
+	lwz	$t1,4($key)
+	or	$s1,$s1,$acc13
+	lwz	$t2,8($key)
+	or	$s2,$s2,$acc14
+	lwz	$t3,12($key)
+	or	$s3,$s3,$acc15
+
+	addi	$key,$key,16
+	bdz	Lenc_compact_done
+
+	and	$acc00,$s0,$mask80	# r1=r0&0x80808080
+	and	$acc01,$s1,$mask80
+	and	$acc02,$s2,$mask80
+	and	$acc03,$s3,$mask80
+	srwi	$acc04,$acc00,7		# r1>>7
+	andc	$acc08,$s0,$mask80	# r0&0x7f7f7f7f
+	srwi	$acc05,$acc01,7
+	andc	$acc09,$s1,$mask80
+	srwi	$acc06,$acc02,7
+	andc	$acc10,$s2,$mask80
+	srwi	$acc07,$acc03,7
+	andc	$acc11,$s3,$mask80
+	sub	$acc00,$acc00,$acc04	# r1-(r1>>7)
+	sub	$acc01,$acc01,$acc05
+	sub	$acc02,$acc02,$acc06
+	sub	$acc03,$acc03,$acc07
+	add	$acc08,$acc08,$acc08	# (r0&0x7f7f7f7f)<<1
+	add	$acc09,$acc09,$acc09
+	add	$acc10,$acc10,$acc10
+	add	$acc11,$acc11,$acc11
+	and	$acc00,$acc00,$mask1b	# (r1-(r1>>7))&0x1b1b1b1b
+	and	$acc01,$acc01,$mask1b
+	and	$acc02,$acc02,$mask1b
+	and	$acc03,$acc03,$mask1b
+	xor	$acc00,$acc00,$acc08	# r2
+	xor	$acc01,$acc01,$acc09
+	 rotlwi	$acc12,$s0,16		# ROTATE(r0,16)
+	xor	$acc02,$acc02,$acc10
+	 rotlwi	$acc13,$s1,16
+	xor	$acc03,$acc03,$acc11
+	 rotlwi	$acc14,$s2,16
+
+	xor	$s0,$s0,$acc00		# r0^r2
+	rotlwi	$acc15,$s3,16
+	xor	$s1,$s1,$acc01
+	rotrwi	$s0,$s0,24		# ROTATE(r2^r0,24)
+	xor	$s2,$s2,$acc02
+	rotrwi	$s1,$s1,24
+	xor	$s3,$s3,$acc03
+	rotrwi	$s2,$s2,24
+	xor	$s0,$s0,$acc00		# ROTATE(r2^r0,24)^r2
+	rotrwi	$s3,$s3,24
+	xor	$s1,$s1,$acc01
+	xor	$s2,$s2,$acc02
+	xor	$s3,$s3,$acc03
+	rotlwi	$acc08,$acc12,8		# ROTATE(r0,24)
+	xor	$s0,$s0,$acc12		#
+	rotlwi	$acc09,$acc13,8
+	xor	$s1,$s1,$acc13
+	rotlwi	$acc10,$acc14,8
+	xor	$s2,$s2,$acc14
+	rotlwi	$acc11,$acc15,8
+	xor	$s3,$s3,$acc15
+	xor	$s0,$s0,$acc08		#
+	xor	$s1,$s1,$acc09
+	xor	$s2,$s2,$acc10
+	xor	$s3,$s3,$acc11
+
+	b	Lenc_compact_loop
+.align	4
+Lenc_compact_done:
+	xor	$s0,$s0,$t0
+	xor	$s1,$s1,$t1
+	xor	$s2,$s2,$t2
+	xor	$s3,$s3,$t3
+	blr
+	.long	0
+	.byte	0,12,0x14,0,0,0,0,0
+.size	.AES_encrypt,.-.AES_encrypt
+
+.globl	.AES_decrypt
+.align	7
+.AES_decrypt:
+	$STU	$sp,-$FRAME($sp)
+	mflr	r0
+
+	$PUSH	$out,`$FRAME-$SIZE_T*19`($sp)
+	$PUSH	r14,`$FRAME-$SIZE_T*18`($sp)
+	$PUSH	r15,`$FRAME-$SIZE_T*17`($sp)
+	$PUSH	r16,`$FRAME-$SIZE_T*16`($sp)
+	$PUSH	r17,`$FRAME-$SIZE_T*15`($sp)
+	$PUSH	r18,`$FRAME-$SIZE_T*14`($sp)
+	$PUSH	r19,`$FRAME-$SIZE_T*13`($sp)
+	$PUSH	r20,`$FRAME-$SIZE_T*12`($sp)
+	$PUSH	r21,`$FRAME-$SIZE_T*11`($sp)
+	$PUSH	r22,`$FRAME-$SIZE_T*10`($sp)
+	$PUSH	r23,`$FRAME-$SIZE_T*9`($sp)
+	$PUSH	r24,`$FRAME-$SIZE_T*8`($sp)
+	$PUSH	r25,`$FRAME-$SIZE_T*7`($sp)
+	$PUSH	r26,`$FRAME-$SIZE_T*6`($sp)
+	$PUSH	r27,`$FRAME-$SIZE_T*5`($sp)
+	$PUSH	r28,`$FRAME-$SIZE_T*4`($sp)
+	$PUSH	r29,`$FRAME-$SIZE_T*3`($sp)
+	$PUSH	r30,`$FRAME-$SIZE_T*2`($sp)
+	$PUSH	r31,`$FRAME-$SIZE_T*1`($sp)
+	$PUSH	r0,`$FRAME+$LRSAVE`($sp)
+
+	andi.	$t0,$inp,3
+	andi.	$t1,$out,3
+	or.	$t0,$t0,$t1
+	bne	Ldec_unaligned
+
+Ldec_unaligned_ok:
+___
+$code.=<<___ if (!$LITTLE_ENDIAN);
+	lwz	$s0,0($inp)
+	lwz	$s1,4($inp)
+	lwz	$s2,8($inp)
+	lwz	$s3,12($inp)
+___
+$code.=<<___ if ($LITTLE_ENDIAN);
+	lwz	$t0,0($inp)
+	lwz	$t1,4($inp)
+	lwz	$t2,8($inp)
+	lwz	$t3,12($inp)
+	rotlwi	$s0,$t0,8
+	rotlwi	$s1,$t1,8
+	rotlwi	$s2,$t2,8
+	rotlwi	$s3,$t3,8
+	rlwimi	$s0,$t0,24,0,7
+	rlwimi	$s1,$t1,24,0,7
+	rlwimi	$s2,$t2,24,0,7
+	rlwimi	$s3,$t3,24,0,7
+	rlwimi	$s0,$t0,24,16,23
+	rlwimi	$s1,$t1,24,16,23
+	rlwimi	$s2,$t2,24,16,23
+	rlwimi	$s3,$t3,24,16,23
+___
+$code.=<<___;
+	bl	LAES_Td
+	bl	Lppc_AES_decrypt_compact
+	$POP	$out,`$FRAME-$SIZE_T*19`($sp)
+___
+$code.=<<___ if ($LITTLE_ENDIAN);
+	rotlwi	$t0,$s0,8
+	rotlwi	$t1,$s1,8
+	rotlwi	$t2,$s2,8
+	rotlwi	$t3,$s3,8
+	rlwimi	$t0,$s0,24,0,7
+	rlwimi	$t1,$s1,24,0,7
+	rlwimi	$t2,$s2,24,0,7
+	rlwimi	$t3,$s3,24,0,7
+	rlwimi	$t0,$s0,24,16,23
+	rlwimi	$t1,$s1,24,16,23
+	rlwimi	$t2,$s2,24,16,23
+	rlwimi	$t3,$s3,24,16,23
+	stw	$t0,0($out)
+	stw	$t1,4($out)
+	stw	$t2,8($out)
+	stw	$t3,12($out)
+___
+$code.=<<___ if (!$LITTLE_ENDIAN);
+	stw	$s0,0($out)
+	stw	$s1,4($out)
+	stw	$s2,8($out)
+	stw	$s3,12($out)
+___
+$code.=<<___;
+	b	Ldec_done
+
+Ldec_unaligned:
+	subfic	$t0,$inp,4096
+	subfic	$t1,$out,4096
+	andi.	$t0,$t0,4096-16
+	beq	Ldec_xpage
+	andi.	$t1,$t1,4096-16
+	bne	Ldec_unaligned_ok
+
+Ldec_xpage:
+	lbz	$acc00,0($inp)
+	lbz	$acc01,1($inp)
+	lbz	$acc02,2($inp)
+	lbz	$s0,3($inp)
+	lbz	$acc04,4($inp)
+	lbz	$acc05,5($inp)
+	lbz	$acc06,6($inp)
+	lbz	$s1,7($inp)
+	lbz	$acc08,8($inp)
+	lbz	$acc09,9($inp)
+	lbz	$acc10,10($inp)
+	insrwi	$s0,$acc00,8,0
+	lbz	$s2,11($inp)
+	insrwi	$s1,$acc04,8,0
+	lbz	$acc12,12($inp)
+	insrwi	$s0,$acc01,8,8
+	lbz	$acc13,13($inp)
+	insrwi	$s1,$acc05,8,8
+	lbz	$acc14,14($inp)
+	insrwi	$s0,$acc02,8,16
+	lbz	$s3,15($inp)
+	insrwi	$s1,$acc06,8,16
+	insrwi	$s2,$acc08,8,0
+	insrwi	$s3,$acc12,8,0
+	insrwi	$s2,$acc09,8,8
+	insrwi	$s3,$acc13,8,8
+	insrwi	$s2,$acc10,8,16
+	insrwi	$s3,$acc14,8,16
+
+	bl	LAES_Td
+	bl	Lppc_AES_decrypt_compact
+	$POP	$out,`$FRAME-$SIZE_T*19`($sp)
+
+	extrwi	$acc00,$s0,8,0
+	extrwi	$acc01,$s0,8,8
+	stb	$acc00,0($out)
+	extrwi	$acc02,$s0,8,16
+	stb	$acc01,1($out)
+	stb	$acc02,2($out)
+	extrwi	$acc04,$s1,8,0
+	stb	$s0,3($out)
+	extrwi	$acc05,$s1,8,8
+	stb	$acc04,4($out)
+	extrwi	$acc06,$s1,8,16
+	stb	$acc05,5($out)
+	stb	$acc06,6($out)
+	extrwi	$acc08,$s2,8,0
+	stb	$s1,7($out)
+	extrwi	$acc09,$s2,8,8
+	stb	$acc08,8($out)
+	extrwi	$acc10,$s2,8,16
+	stb	$acc09,9($out)
+	stb	$acc10,10($out)
+	extrwi	$acc12,$s3,8,0
+	stb	$s2,11($out)
+	extrwi	$acc13,$s3,8,8
+	stb	$acc12,12($out)
+	extrwi	$acc14,$s3,8,16
+	stb	$acc13,13($out)
+	stb	$acc14,14($out)
+	stb	$s3,15($out)
+
+Ldec_done:
+	$POP	r0,`$FRAME+$LRSAVE`($sp)
+	$POP	r14,`$FRAME-$SIZE_T*18`($sp)
+	$POP	r15,`$FRAME-$SIZE_T*17`($sp)
+	$POP	r16,`$FRAME-$SIZE_T*16`($sp)
+	$POP	r17,`$FRAME-$SIZE_T*15`($sp)
+	$POP	r18,`$FRAME-$SIZE_T*14`($sp)
+	$POP	r19,`$FRAME-$SIZE_T*13`($sp)
+	$POP	r20,`$FRAME-$SIZE_T*12`($sp)
+	$POP	r21,`$FRAME-$SIZE_T*11`($sp)
+	$POP	r22,`$FRAME-$SIZE_T*10`($sp)
+	$POP	r23,`$FRAME-$SIZE_T*9`($sp)
+	$POP	r24,`$FRAME-$SIZE_T*8`($sp)
+	$POP	r25,`$FRAME-$SIZE_T*7`($sp)
+	$POP	r26,`$FRAME-$SIZE_T*6`($sp)
+	$POP	r27,`$FRAME-$SIZE_T*5`($sp)
+	$POP	r28,`$FRAME-$SIZE_T*4`($sp)
+	$POP	r29,`$FRAME-$SIZE_T*3`($sp)
+	$POP	r30,`$FRAME-$SIZE_T*2`($sp)
+	$POP	r31,`$FRAME-$SIZE_T*1`($sp)
+	mtlr	r0
+	addi	$sp,$sp,$FRAME
+	blr
+	.long	0
+	.byte	0,12,4,1,0x80,18,3,0
+	.long	0
+
+.align	5
+Lppc_AES_decrypt:
+	lwz	$acc00,240($key)
+	addi	$Tbl1,$Tbl0,3
+	lwz	$t0,0($key)
+	addi	$Tbl2,$Tbl0,2
+	lwz	$t1,4($key)
+	addi	$Tbl3,$Tbl0,1
+	lwz	$t2,8($key)
+	addi	$acc00,$acc00,-1
+	lwz	$t3,12($key)
+	addi	$key,$key,16
+	xor	$s0,$s0,$t0
+	xor	$s1,$s1,$t1
+	xor	$s2,$s2,$t2
+	xor	$s3,$s3,$t3
+	mtctr	$acc00
+.align	4
+Ldec_loop:
+	rlwinm	$acc00,$s0,`32-24+3`,21,28
+	rlwinm	$acc01,$s1,`32-24+3`,21,28
+	rlwinm	$acc02,$s2,`32-24+3`,21,28
+	rlwinm	$acc03,$s3,`32-24+3`,21,28
+	lwz	$t0,0($key)
+	rlwinm	$acc04,$s3,`32-16+3`,21,28
+	lwz	$t1,4($key)
+	rlwinm	$acc05,$s0,`32-16+3`,21,28
+	lwz	$t2,8($key)
+	rlwinm	$acc06,$s1,`32-16+3`,21,28
+	lwz	$t3,12($key)
+	rlwinm	$acc07,$s2,`32-16+3`,21,28
+	lwzx	$acc00,$Tbl0,$acc00
+	rlwinm	$acc08,$s2,`32-8+3`,21,28
+	lwzx	$acc01,$Tbl0,$acc01
+	rlwinm	$acc09,$s3,`32-8+3`,21,28
+	lwzx	$acc02,$Tbl0,$acc02
+	rlwinm	$acc10,$s0,`32-8+3`,21,28
+	lwzx	$acc03,$Tbl0,$acc03
+	rlwinm	$acc11,$s1,`32-8+3`,21,28
+	lwzx	$acc04,$Tbl1,$acc04
+	rlwinm	$acc12,$s1,`0+3`,21,28
+	lwzx	$acc05,$Tbl1,$acc05
+	rlwinm	$acc13,$s2,`0+3`,21,28
+	lwzx	$acc06,$Tbl1,$acc06
+	rlwinm	$acc14,$s3,`0+3`,21,28
+	lwzx	$acc07,$Tbl1,$acc07
+	rlwinm	$acc15,$s0,`0+3`,21,28
+	lwzx	$acc08,$Tbl2,$acc08
+	xor	$t0,$t0,$acc00
+	lwzx	$acc09,$Tbl2,$acc09
+	xor	$t1,$t1,$acc01
+	lwzx	$acc10,$Tbl2,$acc10
+	xor	$t2,$t2,$acc02
+	lwzx	$acc11,$Tbl2,$acc11
+	xor	$t3,$t3,$acc03
+	lwzx	$acc12,$Tbl3,$acc12
+	xor	$t0,$t0,$acc04
+	lwzx	$acc13,$Tbl3,$acc13
+	xor	$t1,$t1,$acc05
+	lwzx	$acc14,$Tbl3,$acc14
+	xor	$t2,$t2,$acc06
+	lwzx	$acc15,$Tbl3,$acc15
+	xor	$t3,$t3,$acc07
+	xor	$t0,$t0,$acc08
+	xor	$t1,$t1,$acc09
+	xor	$t2,$t2,$acc10
+	xor	$t3,$t3,$acc11
+	xor	$s0,$t0,$acc12
+	xor	$s1,$t1,$acc13
+	xor	$s2,$t2,$acc14
+	xor	$s3,$t3,$acc15
+	addi	$key,$key,16
+	bdnz	Ldec_loop
+
+	addi	$Tbl2,$Tbl0,2048
+	nop
+	lwz	$t0,0($key)
+	rlwinm	$acc00,$s0,`32-24`,24,31
+	lwz	$t1,4($key)
+	rlwinm	$acc01,$s1,`32-24`,24,31
+	lwz	$t2,8($key)
+	rlwinm	$acc02,$s2,`32-24`,24,31
+	lwz	$t3,12($key)
+	rlwinm	$acc03,$s3,`32-24`,24,31
+	lwz	$acc08,`2048+0`($Tbl0)	! prefetch Td4
+	rlwinm	$acc04,$s3,`32-16`,24,31
+	lwz	$acc09,`2048+32`($Tbl0)
+	rlwinm	$acc05,$s0,`32-16`,24,31
+	lwz	$acc10,`2048+64`($Tbl0)
+	lbzx	$acc00,$Tbl2,$acc00
+	lwz	$acc11,`2048+96`($Tbl0)
+	lbzx	$acc01,$Tbl2,$acc01
+	lwz	$acc12,`2048+128`($Tbl0)
+	rlwinm	$acc06,$s1,`32-16`,24,31
+	lwz	$acc13,`2048+160`($Tbl0)
+	rlwinm	$acc07,$s2,`32-16`,24,31
+	lwz	$acc14,`2048+192`($Tbl0)
+	rlwinm	$acc08,$s2,`32-8`,24,31
+	lwz	$acc15,`2048+224`($Tbl0)
+	rlwinm	$acc09,$s3,`32-8`,24,31
+	lbzx	$acc02,$Tbl2,$acc02
+	rlwinm	$acc10,$s0,`32-8`,24,31
+	lbzx	$acc03,$Tbl2,$acc03
+	rlwinm	$acc11,$s1,`32-8`,24,31
+	lbzx	$acc04,$Tbl2,$acc04
+	rlwinm	$acc12,$s1,`0`,24,31
+	lbzx	$acc05,$Tbl2,$acc05
+	rlwinm	$acc13,$s2,`0`,24,31
+	lbzx	$acc06,$Tbl2,$acc06
+	rlwinm	$acc14,$s3,`0`,24,31
+	lbzx	$acc07,$Tbl2,$acc07
+	rlwinm	$acc15,$s0,`0`,24,31
+	lbzx	$acc08,$Tbl2,$acc08
+	rlwinm	$s0,$acc00,24,0,7
+	lbzx	$acc09,$Tbl2,$acc09
+	rlwinm	$s1,$acc01,24,0,7
+	lbzx	$acc10,$Tbl2,$acc10
+	rlwinm	$s2,$acc02,24,0,7
+	lbzx	$acc11,$Tbl2,$acc11
+	rlwinm	$s3,$acc03,24,0,7
+	lbzx	$acc12,$Tbl2,$acc12
+	rlwimi	$s0,$acc04,16,8,15
+	lbzx	$acc13,$Tbl2,$acc13
+	rlwimi	$s1,$acc05,16,8,15
+	lbzx	$acc14,$Tbl2,$acc14
+	rlwimi	$s2,$acc06,16,8,15
+	lbzx	$acc15,$Tbl2,$acc15
+	rlwimi	$s3,$acc07,16,8,15
+	rlwimi	$s0,$acc08,8,16,23
+	rlwimi	$s1,$acc09,8,16,23
+	rlwimi	$s2,$acc10,8,16,23
+	rlwimi	$s3,$acc11,8,16,23
+	or	$s0,$s0,$acc12
+	or	$s1,$s1,$acc13
+	or	$s2,$s2,$acc14
+	or	$s3,$s3,$acc15
+	xor	$s0,$s0,$t0
+	xor	$s1,$s1,$t1
+	xor	$s2,$s2,$t2
+	xor	$s3,$s3,$t3
+	blr
+	.long	0
+	.byte	0,12,0x14,0,0,0,0,0
+
+.align	4
+Lppc_AES_decrypt_compact:
+	lwz	$acc00,240($key)
+	addi	$Tbl1,$Tbl0,2048
+	lwz	$t0,0($key)
+	lis	$mask80,0x8080
+	lwz	$t1,4($key)
+	lis	$mask1b,0x1b1b
+	lwz	$t2,8($key)
+	ori	$mask80,$mask80,0x8080
+	lwz	$t3,12($key)
+	ori	$mask1b,$mask1b,0x1b1b
+	addi	$key,$key,16
+___
+$code.=<<___ if ($SIZE_T==8);
+	insrdi	$mask80,$mask80,32,0
+	insrdi	$mask1b,$mask1b,32,0
+___
+$code.=<<___;
+	mtctr	$acc00
+.align	4
+Ldec_compact_loop:
+	xor	$s0,$s0,$t0
+	xor	$s1,$s1,$t1
+	rlwinm	$acc00,$s0,`32-24`,24,31
+	xor	$s2,$s2,$t2
+	rlwinm	$acc01,$s1,`32-24`,24,31
+	xor	$s3,$s3,$t3
+	rlwinm	$acc02,$s2,`32-24`,24,31
+	rlwinm	$acc03,$s3,`32-24`,24,31
+	rlwinm	$acc04,$s3,`32-16`,24,31
+	rlwinm	$acc05,$s0,`32-16`,24,31
+	rlwinm	$acc06,$s1,`32-16`,24,31
+	rlwinm	$acc07,$s2,`32-16`,24,31
+	lbzx	$acc00,$Tbl1,$acc00
+	rlwinm	$acc08,$s2,`32-8`,24,31
+	lbzx	$acc01,$Tbl1,$acc01
+	rlwinm	$acc09,$s3,`32-8`,24,31
+	lbzx	$acc02,$Tbl1,$acc02
+	rlwinm	$acc10,$s0,`32-8`,24,31
+	lbzx	$acc03,$Tbl1,$acc03
+	rlwinm	$acc11,$s1,`32-8`,24,31
+	lbzx	$acc04,$Tbl1,$acc04
+	rlwinm	$acc12,$s1,`0`,24,31
+	lbzx	$acc05,$Tbl1,$acc05
+	rlwinm	$acc13,$s2,`0`,24,31
+	lbzx	$acc06,$Tbl1,$acc06
+	rlwinm	$acc14,$s3,`0`,24,31
+	lbzx	$acc07,$Tbl1,$acc07
+	rlwinm	$acc15,$s0,`0`,24,31
+	lbzx	$acc08,$Tbl1,$acc08
+	rlwinm	$s0,$acc00,24,0,7
+	lbzx	$acc09,$Tbl1,$acc09
+	rlwinm	$s1,$acc01,24,0,7
+	lbzx	$acc10,$Tbl1,$acc10
+	rlwinm	$s2,$acc02,24,0,7
+	lbzx	$acc11,$Tbl1,$acc11
+	rlwinm	$s3,$acc03,24,0,7
+	lbzx	$acc12,$Tbl1,$acc12
+	rlwimi	$s0,$acc04,16,8,15
+	lbzx	$acc13,$Tbl1,$acc13
+	rlwimi	$s1,$acc05,16,8,15
+	lbzx	$acc14,$Tbl1,$acc14
+	rlwimi	$s2,$acc06,16,8,15
+	lbzx	$acc15,$Tbl1,$acc15
+	rlwimi	$s3,$acc07,16,8,15
+	rlwimi	$s0,$acc08,8,16,23
+	rlwimi	$s1,$acc09,8,16,23
+	rlwimi	$s2,$acc10,8,16,23
+	rlwimi	$s3,$acc11,8,16,23
+	lwz	$t0,0($key)
+	or	$s0,$s0,$acc12
+	lwz	$t1,4($key)
+	or	$s1,$s1,$acc13
+	lwz	$t2,8($key)
+	or	$s2,$s2,$acc14
+	lwz	$t3,12($key)
+	or	$s3,$s3,$acc15
+
+	addi	$key,$key,16
+	bdz	Ldec_compact_done
+___
+$code.=<<___ if ($SIZE_T==8);
+	# vectorized permutation improves decrypt performance by 10%
+	insrdi	$s0,$s1,32,0
+	insrdi	$s2,$s3,32,0
+
+	and	$acc00,$s0,$mask80	# r1=r0&0x80808080
+	and	$acc02,$s2,$mask80
+	srdi	$acc04,$acc00,7		# r1>>7
+	srdi	$acc06,$acc02,7
+	andc	$acc08,$s0,$mask80	# r0&0x7f7f7f7f
+	andc	$acc10,$s2,$mask80
+	sub	$acc00,$acc00,$acc04	# r1-(r1>>7)
+	sub	$acc02,$acc02,$acc06
+	add	$acc08,$acc08,$acc08	# (r0&0x7f7f7f7f)<<1
+	add	$acc10,$acc10,$acc10
+	and	$acc00,$acc00,$mask1b	# (r1-(r1>>7))&0x1b1b1b1b
+	and	$acc02,$acc02,$mask1b
+	xor	$acc00,$acc00,$acc08	# r2
+	xor	$acc02,$acc02,$acc10
+
+	and	$acc04,$acc00,$mask80	# r1=r2&0x80808080
+	and	$acc06,$acc02,$mask80
+	srdi	$acc08,$acc04,7		# r1>>7
+	srdi	$acc10,$acc06,7
+	andc	$acc12,$acc00,$mask80	# r2&0x7f7f7f7f
+	andc	$acc14,$acc02,$mask80
+	sub	$acc04,$acc04,$acc08	# r1-(r1>>7)
+	sub	$acc06,$acc06,$acc10
+	add	$acc12,$acc12,$acc12	# (r2&0x7f7f7f7f)<<1
+	add	$acc14,$acc14,$acc14
+	and	$acc04,$acc04,$mask1b	# (r1-(r1>>7))&0x1b1b1b1b
+	and	$acc06,$acc06,$mask1b
+	xor	$acc04,$acc04,$acc12	# r4
+	xor	$acc06,$acc06,$acc14
+
+	and	$acc08,$acc04,$mask80	# r1=r4&0x80808080
+	and	$acc10,$acc06,$mask80
+	srdi	$acc12,$acc08,7		# r1>>7
+	srdi	$acc14,$acc10,7
+	sub	$acc08,$acc08,$acc12	# r1-(r1>>7)
+	sub	$acc10,$acc10,$acc14
+	andc	$acc12,$acc04,$mask80	# r4&0x7f7f7f7f
+	andc	$acc14,$acc06,$mask80
+	add	$acc12,$acc12,$acc12	# (r4&0x7f7f7f7f)<<1
+	add	$acc14,$acc14,$acc14
+	and	$acc08,$acc08,$mask1b	# (r1-(r1>>7))&0x1b1b1b1b
+	and	$acc10,$acc10,$mask1b
+	xor	$acc08,$acc08,$acc12	# r8
+	xor	$acc10,$acc10,$acc14
+
+	xor	$acc00,$acc00,$s0	# r2^r0
+	xor	$acc02,$acc02,$s2
+	xor	$acc04,$acc04,$s0	# r4^r0
+	xor	$acc06,$acc06,$s2
+
+	extrdi	$acc01,$acc00,32,0
+	extrdi	$acc03,$acc02,32,0
+	extrdi	$acc05,$acc04,32,0
+	extrdi	$acc07,$acc06,32,0
+	extrdi	$acc09,$acc08,32,0
+	extrdi	$acc11,$acc10,32,0
+___
+$code.=<<___ if ($SIZE_T==4);
+	and	$acc00,$s0,$mask80	# r1=r0&0x80808080
+	and	$acc01,$s1,$mask80
+	and	$acc02,$s2,$mask80
+	and	$acc03,$s3,$mask80
+	srwi	$acc04,$acc00,7		# r1>>7
+	andc	$acc08,$s0,$mask80	# r0&0x7f7f7f7f
+	srwi	$acc05,$acc01,7
+	andc	$acc09,$s1,$mask80
+	srwi	$acc06,$acc02,7
+	andc	$acc10,$s2,$mask80
+	srwi	$acc07,$acc03,7
+	andc	$acc11,$s3,$mask80
+	sub	$acc00,$acc00,$acc04	# r1-(r1>>7)
+	sub	$acc01,$acc01,$acc05
+	sub	$acc02,$acc02,$acc06
+	sub	$acc03,$acc03,$acc07
+	add	$acc08,$acc08,$acc08	# (r0&0x7f7f7f7f)<<1
+	add	$acc09,$acc09,$acc09
+	add	$acc10,$acc10,$acc10
+	add	$acc11,$acc11,$acc11
+	and	$acc00,$acc00,$mask1b	# (r1-(r1>>7))&0x1b1b1b1b
+	and	$acc01,$acc01,$mask1b
+	and	$acc02,$acc02,$mask1b
+	and	$acc03,$acc03,$mask1b
+	xor	$acc00,$acc00,$acc08	# r2
+	xor	$acc01,$acc01,$acc09
+	xor	$acc02,$acc02,$acc10
+	xor	$acc03,$acc03,$acc11
+
+	and	$acc04,$acc00,$mask80	# r1=r2&0x80808080
+	and	$acc05,$acc01,$mask80
+	and	$acc06,$acc02,$mask80
+	and	$acc07,$acc03,$mask80
+	srwi	$acc08,$acc04,7		# r1>>7
+	andc	$acc12,$acc00,$mask80	# r2&0x7f7f7f7f
+	srwi	$acc09,$acc05,7
+	andc	$acc13,$acc01,$mask80
+	srwi	$acc10,$acc06,7
+	andc	$acc14,$acc02,$mask80
+	srwi	$acc11,$acc07,7
+	andc	$acc15,$acc03,$mask80
+	sub	$acc04,$acc04,$acc08	# r1-(r1>>7)
+	sub	$acc05,$acc05,$acc09
+	sub	$acc06,$acc06,$acc10
+	sub	$acc07,$acc07,$acc11
+	add	$acc12,$acc12,$acc12	# (r2&0x7f7f7f7f)<<1
+	add	$acc13,$acc13,$acc13
+	add	$acc14,$acc14,$acc14
+	add	$acc15,$acc15,$acc15
+	and	$acc04,$acc04,$mask1b	# (r1-(r1>>7))&0x1b1b1b1b
+	and	$acc05,$acc05,$mask1b
+	and	$acc06,$acc06,$mask1b
+	and	$acc07,$acc07,$mask1b
+	xor	$acc04,$acc04,$acc12	# r4
+	xor	$acc05,$acc05,$acc13
+	xor	$acc06,$acc06,$acc14
+	xor	$acc07,$acc07,$acc15
+
+	and	$acc08,$acc04,$mask80	# r1=r4&0x80808080
+	and	$acc09,$acc05,$mask80
+	srwi	$acc12,$acc08,7		# r1>>7
+	and	$acc10,$acc06,$mask80
+	srwi	$acc13,$acc09,7
+	and	$acc11,$acc07,$mask80
+	srwi	$acc14,$acc10,7
+	sub	$acc08,$acc08,$acc12	# r1-(r1>>7)
+	srwi	$acc15,$acc11,7
+	sub	$acc09,$acc09,$acc13
+	sub	$acc10,$acc10,$acc14
+	sub	$acc11,$acc11,$acc15
+	andc	$acc12,$acc04,$mask80	# r4&0x7f7f7f7f
+	andc	$acc13,$acc05,$mask80
+	andc	$acc14,$acc06,$mask80
+	andc	$acc15,$acc07,$mask80
+	add	$acc12,$acc12,$acc12	# (r4&0x7f7f7f7f)<<1
+	add	$acc13,$acc13,$acc13
+	add	$acc14,$acc14,$acc14
+	add	$acc15,$acc15,$acc15
+	and	$acc08,$acc08,$mask1b	# (r1-(r1>>7))&0x1b1b1b1b
+	and	$acc09,$acc09,$mask1b
+	and	$acc10,$acc10,$mask1b
+	and	$acc11,$acc11,$mask1b
+	xor	$acc08,$acc08,$acc12	# r8
+	xor	$acc09,$acc09,$acc13
+	xor	$acc10,$acc10,$acc14
+	xor	$acc11,$acc11,$acc15
+
+	xor	$acc00,$acc00,$s0	# r2^r0
+	xor	$acc01,$acc01,$s1
+	xor	$acc02,$acc02,$s2
+	xor	$acc03,$acc03,$s3
+	xor	$acc04,$acc04,$s0	# r4^r0
+	xor	$acc05,$acc05,$s1
+	xor	$acc06,$acc06,$s2
+	xor	$acc07,$acc07,$s3
+___
+$code.=<<___;
+	rotrwi	$s0,$s0,8		# = ROTATE(r0,8)
+	rotrwi	$s1,$s1,8
+	xor	$s0,$s0,$acc00		# ^= r2^r0
+	rotrwi	$s2,$s2,8
+	xor	$s1,$s1,$acc01
+	rotrwi	$s3,$s3,8
+	xor	$s2,$s2,$acc02
+	xor	$s3,$s3,$acc03
+	xor	$acc00,$acc00,$acc08
+	xor	$acc01,$acc01,$acc09
+	xor	$acc02,$acc02,$acc10
+	xor	$acc03,$acc03,$acc11
+	xor	$s0,$s0,$acc04		# ^= r4^r0
+	rotrwi	$acc00,$acc00,24
+	xor	$s1,$s1,$acc05
+	rotrwi	$acc01,$acc01,24
+	xor	$s2,$s2,$acc06
+	rotrwi	$acc02,$acc02,24
+	xor	$s3,$s3,$acc07
+	rotrwi	$acc03,$acc03,24
+	xor	$acc04,$acc04,$acc08
+	xor	$acc05,$acc05,$acc09
+	xor	$acc06,$acc06,$acc10
+	xor	$acc07,$acc07,$acc11
+	xor	$s0,$s0,$acc08		# ^= r8 [^((r4^r0)^(r2^r0)=r4^r2)]
+	rotrwi	$acc04,$acc04,16
+	xor	$s1,$s1,$acc09
+	rotrwi	$acc05,$acc05,16
+	xor	$s2,$s2,$acc10
+	rotrwi	$acc06,$acc06,16
+	xor	$s3,$s3,$acc11
+	rotrwi	$acc07,$acc07,16
+	xor	$s0,$s0,$acc00		# ^= ROTATE(r8^r2^r0,24)
+	rotrwi	$acc08,$acc08,8
+	xor	$s1,$s1,$acc01
+	rotrwi	$acc09,$acc09,8
+	xor	$s2,$s2,$acc02
+	rotrwi	$acc10,$acc10,8
+	xor	$s3,$s3,$acc03
+	rotrwi	$acc11,$acc11,8
+	xor	$s0,$s0,$acc04		# ^= ROTATE(r8^r4^r0,16)
+	xor	$s1,$s1,$acc05
+	xor	$s2,$s2,$acc06
+	xor	$s3,$s3,$acc07
+	xor	$s0,$s0,$acc08		# ^= ROTATE(r8,8)	
+	xor	$s1,$s1,$acc09	
+	xor	$s2,$s2,$acc10	
+	xor	$s3,$s3,$acc11	
+
+	b	Ldec_compact_loop
+.align	4
+Ldec_compact_done:
+	xor	$s0,$s0,$t0
+	xor	$s1,$s1,$t1
+	xor	$s2,$s2,$t2
+	xor	$s3,$s3,$t3
+	blr
+	.long	0
+	.byte	0,12,0x14,0,0,0,0,0
+.size	.AES_decrypt,.-.AES_decrypt
+
+.asciz	"AES for PPC, CRYPTOGAMS by "
+.align	7
+___
+
+$code =~ s/\`([^\`]*)\`/eval $1/gem;
+print $code;
+close STDOUT;
diff --git a/crypto/aes/asm/aes-s390x.pl b/crypto/aes/asm/aes-s390x.pl
new file mode 100644
index 000000000..a93d60191
--- /dev/null
+++ b/crypto/aes/asm/aes-s390x.pl
@@ -0,0 +1,2235 @@
+#! /usr/bin/env perl
+# Copyright 2007-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+
+# ====================================================================
+# Written by Andy Polyakov  for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+
+# AES for s390x.
+
+# April 2007.
+#
+# Software performance improvement over gcc-generated code is ~70% and
+# in absolute terms is ~73 cycles per byte processed with 128-bit key.
+# You're likely to exclaim "why so slow?" Keep in mind that z-CPUs are
+# *strictly* in-order execution and issued instruction [in this case
+# load value from memory is critical] has to complete before execution
+# flow proceeds. S-boxes are compressed to 2KB[+256B].
+#
+# As for hardware acceleration support. It's basically a "teaser," as
+# it can and should be improved in several ways. Most notably support
+# for CBC is not utilized, nor multiple blocks are ever processed.
+# Then software key schedule can be postponed till hardware support
+# detection... Performance improvement over assembler is reportedly
+# ~2.5x, but can reach >8x [naturally on larger chunks] if proper
+# support is implemented.
+
+# May 2007.
+#
+# Implement AES_set_[en|de]crypt_key. Key schedule setup is avoided
+# for 128-bit keys, if hardware support is detected.
+
+# Januray 2009.
+#
+# Add support for hardware AES192/256 and reschedule instructions to
+# minimize/avoid Address Generation Interlock hazard and to favour
+# dual-issue z10 pipeline. This gave ~25% improvement on z10 and
+# almost 50% on z9. The gain is smaller on z10, because being dual-
+# issue z10 makes it improssible to eliminate the interlock condition:
+# critial path is not long enough. Yet it spends ~24 cycles per byte
+# processed with 128-bit key.
+#
+# Unlike previous version hardware support detection takes place only
+# at the moment of key schedule setup, which is denoted in key->rounds.
+# This is done, because deferred key setup can't be made MT-safe, not
+# for keys longer than 128 bits.
+#
+# Add AES_cbc_encrypt, which gives incredible performance improvement,
+# it was measured to be ~6.6x. It's less than previously mentioned 8x,
+# because software implementation was optimized.
+
+# May 2010.
+#
+# Add AES_ctr32_encrypt. If hardware-assisted, it provides up to 4.3x
+# performance improvement over "generic" counter mode routine relying
+# on single-block, also hardware-assisted, AES_encrypt. "Up to" refers
+# to the fact that exact throughput value depends on current stack
+# frame alignment within 4KB page. In worst case you get ~75% of the
+# maximum, but *on average* it would be as much as ~98%. Meaning that
+# worst case is unlike, it's like hitting ravine on plateau.
+
+# November 2010.
+#
+# Adapt for -m31 build. If kernel supports what's called "highgprs"
+# feature on Linux [see /proc/cpuinfo], it's possible to use 64-bit
+# instructions and achieve "64-bit" performance even in 31-bit legacy
+# application context. The feature is not specific to any particular
+# processor, as long as it's "z-CPU". Latter implies that the code
+# remains z/Architecture specific. On z990 it was measured to perform
+# 2x better than code generated by gcc 4.3.
+
+# December 2010.
+#
+# Add support for z196 "cipher message with counter" instruction.
+# Note however that it's disengaged, because it was measured to
+# perform ~12% worse than vanilla km-based code...
+
+# February 2011.
+#
+# Add AES_xts_[en|de]crypt. This includes support for z196 km-xts-aes
+# instructions, which deliver ~70% improvement at 8KB block size over
+# vanilla km-based code, 37% - at most like 512-bytes block size.
+
+$flavour = shift;
+
+if ($flavour =~ /3[12]/) {
+	$SIZE_T=4;
+	$g="";
+} else {
+	$SIZE_T=8;
+	$g="g";
+}
+
+while (($output=shift) && ($output!~/\w[\w\-]*\.\w+$/)) {}
+open STDOUT,">$output";
+
+$softonly=0;	# allow hardware support
+
+$t0="%r0";	$mask="%r0";
+$t1="%r1";
+$t2="%r2";	$inp="%r2";
+$t3="%r3";	$out="%r3";	$bits="%r3";
+$key="%r4";
+$i1="%r5";
+$i2="%r6";
+$i3="%r7";
+$s0="%r8";
+$s1="%r9";
+$s2="%r10";
+$s3="%r11";
+$tbl="%r12";
+$rounds="%r13";
+$ra="%r14";
+$sp="%r15";
+
+$stdframe=16*$SIZE_T+4*8;
+
+sub _data_word()
+{ my $i;
+    while(defined($i=shift)) { $code.=sprintf".long\t0x%08x,0x%08x\n",$i,$i; }
+}
+
+$code=<<___;
+.text
+
+.type	AES_Te,\@object
+.align	256
+AES_Te:
+___
+&_data_word(
+	0xc66363a5, 0xf87c7c84, 0xee777799, 0xf67b7b8d,
+	0xfff2f20d, 0xd66b6bbd, 0xde6f6fb1, 0x91c5c554,
+	0x60303050, 0x02010103, 0xce6767a9, 0x562b2b7d,
+	0xe7fefe19, 0xb5d7d762, 0x4dababe6, 0xec76769a,
+	0x8fcaca45, 0x1f82829d, 0x89c9c940, 0xfa7d7d87,
+	0xeffafa15, 0xb25959eb, 0x8e4747c9, 0xfbf0f00b,
+	0x41adadec, 0xb3d4d467, 0x5fa2a2fd, 0x45afafea,
+	0x239c9cbf, 0x53a4a4f7, 0xe4727296, 0x9bc0c05b,
+	0x75b7b7c2, 0xe1fdfd1c, 0x3d9393ae, 0x4c26266a,
+	0x6c36365a, 0x7e3f3f41, 0xf5f7f702, 0x83cccc4f,
+	0x6834345c, 0x51a5a5f4, 0xd1e5e534, 0xf9f1f108,
+	0xe2717193, 0xabd8d873, 0x62313153, 0x2a15153f,
+	0x0804040c, 0x95c7c752, 0x46232365, 0x9dc3c35e,
+	0x30181828, 0x379696a1, 0x0a05050f, 0x2f9a9ab5,
+	0x0e070709, 0x24121236, 0x1b80809b, 0xdfe2e23d,
+	0xcdebeb26, 0x4e272769, 0x7fb2b2cd, 0xea75759f,
+	0x1209091b, 0x1d83839e, 0x582c2c74, 0x341a1a2e,
+	0x361b1b2d, 0xdc6e6eb2, 0xb45a5aee, 0x5ba0a0fb,
+	0xa45252f6, 0x763b3b4d, 0xb7d6d661, 0x7db3b3ce,
+	0x5229297b, 0xdde3e33e, 0x5e2f2f71, 0x13848497,
+	0xa65353f5, 0xb9d1d168, 0x00000000, 0xc1eded2c,
+	0x40202060, 0xe3fcfc1f, 0x79b1b1c8, 0xb65b5bed,
+	0xd46a6abe, 0x8dcbcb46, 0x67bebed9, 0x7239394b,
+	0x944a4ade, 0x984c4cd4, 0xb05858e8, 0x85cfcf4a,
+	0xbbd0d06b, 0xc5efef2a, 0x4faaaae5, 0xedfbfb16,
+	0x864343c5, 0x9a4d4dd7, 0x66333355, 0x11858594,
+	0x8a4545cf, 0xe9f9f910, 0x04020206, 0xfe7f7f81,
+	0xa05050f0, 0x783c3c44, 0x259f9fba, 0x4ba8a8e3,
+	0xa25151f3, 0x5da3a3fe, 0x804040c0, 0x058f8f8a,
+	0x3f9292ad, 0x219d9dbc, 0x70383848, 0xf1f5f504,
+	0x63bcbcdf, 0x77b6b6c1, 0xafdada75, 0x42212163,
+	0x20101030, 0xe5ffff1a, 0xfdf3f30e, 0xbfd2d26d,
+	0x81cdcd4c, 0x180c0c14, 0x26131335, 0xc3ecec2f,
+	0xbe5f5fe1, 0x359797a2, 0x884444cc, 0x2e171739,
+	0x93c4c457, 0x55a7a7f2, 0xfc7e7e82, 0x7a3d3d47,
+	0xc86464ac, 0xba5d5de7, 0x3219192b, 0xe6737395,
+	0xc06060a0, 0x19818198, 0x9e4f4fd1, 0xa3dcdc7f,
+	0x44222266, 0x542a2a7e, 0x3b9090ab, 0x0b888883,
+	0x8c4646ca, 0xc7eeee29, 0x6bb8b8d3, 0x2814143c,
+	0xa7dede79, 0xbc5e5ee2, 0x160b0b1d, 0xaddbdb76,
+	0xdbe0e03b, 0x64323256, 0x743a3a4e, 0x140a0a1e,
+	0x924949db, 0x0c06060a, 0x4824246c, 0xb85c5ce4,
+	0x9fc2c25d, 0xbdd3d36e, 0x43acacef, 0xc46262a6,
+	0x399191a8, 0x319595a4, 0xd3e4e437, 0xf279798b,
+	0xd5e7e732, 0x8bc8c843, 0x6e373759, 0xda6d6db7,
+	0x018d8d8c, 0xb1d5d564, 0x9c4e4ed2, 0x49a9a9e0,
+	0xd86c6cb4, 0xac5656fa, 0xf3f4f407, 0xcfeaea25,
+	0xca6565af, 0xf47a7a8e, 0x47aeaee9, 0x10080818,
+	0x6fbabad5, 0xf0787888, 0x4a25256f, 0x5c2e2e72,
+	0x381c1c24, 0x57a6a6f1, 0x73b4b4c7, 0x97c6c651,
+	0xcbe8e823, 0xa1dddd7c, 0xe874749c, 0x3e1f1f21,
+	0x964b4bdd, 0x61bdbddc, 0x0d8b8b86, 0x0f8a8a85,
+	0xe0707090, 0x7c3e3e42, 0x71b5b5c4, 0xcc6666aa,
+	0x904848d8, 0x06030305, 0xf7f6f601, 0x1c0e0e12,
+	0xc26161a3, 0x6a35355f, 0xae5757f9, 0x69b9b9d0,
+	0x17868691, 0x99c1c158, 0x3a1d1d27, 0x279e9eb9,
+	0xd9e1e138, 0xebf8f813, 0x2b9898b3, 0x22111133,
+	0xd26969bb, 0xa9d9d970, 0x078e8e89, 0x339494a7,
+	0x2d9b9bb6, 0x3c1e1e22, 0x15878792, 0xc9e9e920,
+	0x87cece49, 0xaa5555ff, 0x50282878, 0xa5dfdf7a,
+	0x038c8c8f, 0x59a1a1f8, 0x09898980, 0x1a0d0d17,
+	0x65bfbfda, 0xd7e6e631, 0x844242c6, 0xd06868b8,
+	0x824141c3, 0x299999b0, 0x5a2d2d77, 0x1e0f0f11,
+	0x7bb0b0cb, 0xa85454fc, 0x6dbbbbd6, 0x2c16163a);
+$code.=<<___;
+# Te4[256]
+.byte	0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5
+.byte	0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76
+.byte	0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0
+.byte	0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0
+.byte	0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc
+.byte	0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15
+.byte	0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a
+.byte	0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75
+.byte	0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0
+.byte	0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84
+.byte	0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b
+.byte	0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf
+.byte	0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85
+.byte	0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8
+.byte	0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5
+.byte	0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2
+.byte	0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17
+.byte	0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73
+.byte	0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88
+.byte	0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb
+.byte	0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c
+.byte	0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79
+.byte	0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9
+.byte	0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08
+.byte	0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6
+.byte	0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a
+.byte	0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e
+.byte	0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e
+.byte	0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94
+.byte	0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf
+.byte	0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68
+.byte	0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16
+# rcon[]
+.long	0x01000000, 0x02000000, 0x04000000, 0x08000000
+.long	0x10000000, 0x20000000, 0x40000000, 0x80000000
+.long	0x1B000000, 0x36000000, 0, 0, 0, 0, 0, 0
+.align	256
+.size	AES_Te,.-AES_Te
+
+# void AES_encrypt(const unsigned char *inp, unsigned char *out,
+# 		 const AES_KEY *key) {
+.globl	AES_encrypt
+.type	AES_encrypt,\@function
+AES_encrypt:
+___
+$code.=<<___ if (!$softonly);
+	l	%r0,240($key)
+	lhi	%r1,16
+	clr	%r0,%r1
+	jl	.Lesoft
+
+	la	%r1,0($key)
+	#la	%r2,0($inp)
+	la	%r4,0($out)
+	lghi	%r3,16		# single block length
+	.long	0xb92e0042	# km %r4,%r2
+	brc	1,.-4		# can this happen?
+	br	%r14
+.align	64
+.Lesoft:
+___
+$code.=<<___;
+	stm${g}	%r3,$ra,3*$SIZE_T($sp)
+
+	llgf	$s0,0($inp)
+	llgf	$s1,4($inp)
+	llgf	$s2,8($inp)
+	llgf	$s3,12($inp)
+
+	larl	$tbl,AES_Te
+	bras	$ra,_s390x_AES_encrypt
+
+	l${g}	$out,3*$SIZE_T($sp)
+	st	$s0,0($out)
+	st	$s1,4($out)
+	st	$s2,8($out)
+	st	$s3,12($out)
+
+	lm${g}	%r6,$ra,6*$SIZE_T($sp)
+	br	$ra
+.size	AES_encrypt,.-AES_encrypt
+
+.type   _s390x_AES_encrypt,\@function
+.align	16
+_s390x_AES_encrypt:
+	st${g}	$ra,15*$SIZE_T($sp)
+	x	$s0,0($key)
+	x	$s1,4($key)
+	x	$s2,8($key)
+	x	$s3,12($key)
+	l	$rounds,240($key)
+	llill	$mask,`0xff<<3`
+	aghi	$rounds,-1
+	j	.Lenc_loop
+.align	16
+.Lenc_loop:
+	sllg	$t1,$s0,`0+3`
+	srlg	$t2,$s0,`8-3`
+	srlg	$t3,$s0,`16-3`
+	srl	$s0,`24-3`
+	nr	$s0,$mask
+	ngr	$t1,$mask
+	nr	$t2,$mask
+	nr	$t3,$mask
+
+	srlg	$i1,$s1,`16-3`	# i0
+	sllg	$i2,$s1,`0+3`
+	srlg	$i3,$s1,`8-3`
+	srl	$s1,`24-3`
+	nr	$i1,$mask
+	nr	$s1,$mask
+	ngr	$i2,$mask
+	nr	$i3,$mask
+
+	l	$s0,0($s0,$tbl)	# Te0[s0>>24]
+	l	$t1,1($t1,$tbl)	# Te3[s0>>0]
+	l	$t2,2($t2,$tbl) # Te2[s0>>8]
+	l	$t3,3($t3,$tbl)	# Te1[s0>>16]
+
+	x	$s0,3($i1,$tbl)	# Te1[s1>>16]
+	l	$s1,0($s1,$tbl)	# Te0[s1>>24]
+	x	$t2,1($i2,$tbl)	# Te3[s1>>0]
+	x	$t3,2($i3,$tbl)	# Te2[s1>>8]
+
+	srlg	$i1,$s2,`8-3`	# i0
+	srlg	$i2,$s2,`16-3`	# i1
+	nr	$i1,$mask
+	nr	$i2,$mask
+	sllg	$i3,$s2,`0+3`
+	srl	$s2,`24-3`
+	nr	$s2,$mask
+	ngr	$i3,$mask
+
+	xr	$s1,$t1
+	srlg	$ra,$s3,`8-3`	# i1
+	sllg	$t1,$s3,`0+3`	# i0
+	nr	$ra,$mask
+	la	$key,16($key)
+	ngr	$t1,$mask
+
+	x	$s0,2($i1,$tbl)	# Te2[s2>>8]
+	x	$s1,3($i2,$tbl)	# Te1[s2>>16]
+	l	$s2,0($s2,$tbl)	# Te0[s2>>24]
+	x	$t3,1($i3,$tbl)	# Te3[s2>>0]
+
+	srlg	$i3,$s3,`16-3`	# i2
+	xr	$s2,$t2
+	srl	$s3,`24-3`
+	nr	$i3,$mask
+	nr	$s3,$mask
+
+	x	$s0,0($key)
+	x	$s1,4($key)
+	x	$s2,8($key)
+	x	$t3,12($key)
+
+	x	$s0,1($t1,$tbl)	# Te3[s3>>0]
+	x	$s1,2($ra,$tbl)	# Te2[s3>>8]
+	x	$s2,3($i3,$tbl)	# Te1[s3>>16]
+	l	$s3,0($s3,$tbl)	# Te0[s3>>24]
+	xr	$s3,$t3
+
+	brct	$rounds,.Lenc_loop
+	.align	16
+
+	sllg	$t1,$s0,`0+3`
+	srlg	$t2,$s0,`8-3`
+	ngr	$t1,$mask
+	srlg	$t3,$s0,`16-3`
+	srl	$s0,`24-3`
+	nr	$s0,$mask
+	nr	$t2,$mask
+	nr	$t3,$mask
+
+	srlg	$i1,$s1,`16-3`	# i0
+	sllg	$i2,$s1,`0+3`
+	ngr	$i2,$mask
+	srlg	$i3,$s1,`8-3`
+	srl	$s1,`24-3`
+	nr	$i1,$mask
+	nr	$s1,$mask
+	nr	$i3,$mask
+
+	llgc	$s0,2($s0,$tbl)	# Te4[s0>>24]
+	llgc	$t1,2($t1,$tbl)	# Te4[s0>>0]
+	sll	$s0,24
+	llgc	$t2,2($t2,$tbl)	# Te4[s0>>8]
+	llgc	$t3,2($t3,$tbl)	# Te4[s0>>16]
+	sll	$t2,8
+	sll	$t3,16
+
+	llgc	$i1,2($i1,$tbl)	# Te4[s1>>16]
+	llgc	$s1,2($s1,$tbl)	# Te4[s1>>24]
+	llgc	$i2,2($i2,$tbl)	# Te4[s1>>0]
+	llgc	$i3,2($i3,$tbl)	# Te4[s1>>8]
+	sll	$i1,16
+	sll	$s1,24
+	sll	$i3,8
+	or	$s0,$i1
+	or	$s1,$t1
+	or	$t2,$i2
+	or	$t3,$i3
+	
+	srlg	$i1,$s2,`8-3`	# i0
+	srlg	$i2,$s2,`16-3`	# i1
+	nr	$i1,$mask
+	nr	$i2,$mask
+	sllg	$i3,$s2,`0+3`
+	srl	$s2,`24-3`
+	ngr	$i3,$mask
+	nr	$s2,$mask
+
+	sllg	$t1,$s3,`0+3`	# i0
+	srlg	$ra,$s3,`8-3`	# i1
+	ngr	$t1,$mask
+
+	llgc	$i1,2($i1,$tbl)	# Te4[s2>>8]
+	llgc	$i2,2($i2,$tbl)	# Te4[s2>>16]
+	sll	$i1,8
+	llgc	$s2,2($s2,$tbl)	# Te4[s2>>24]
+	llgc	$i3,2($i3,$tbl)	# Te4[s2>>0]
+	sll	$i2,16
+	nr	$ra,$mask
+	sll	$s2,24
+	or	$s0,$i1
+	or	$s1,$i2
+	or	$s2,$t2
+	or	$t3,$i3
+
+	srlg	$i3,$s3,`16-3`	# i2
+	srl	$s3,`24-3`
+	nr	$i3,$mask
+	nr	$s3,$mask
+
+	l	$t0,16($key)
+	l	$t2,20($key)
+
+	llgc	$i1,2($t1,$tbl)	# Te4[s3>>0]
+	llgc	$i2,2($ra,$tbl)	# Te4[s3>>8]
+	llgc	$i3,2($i3,$tbl)	# Te4[s3>>16]
+	llgc	$s3,2($s3,$tbl)	# Te4[s3>>24]
+	sll	$i2,8
+	sll	$i3,16
+	sll	$s3,24
+	or	$s0,$i1
+	or	$s1,$i2
+	or	$s2,$i3
+	or	$s3,$t3
+
+	l${g}	$ra,15*$SIZE_T($sp)
+	xr	$s0,$t0
+	xr	$s1,$t2
+	x	$s2,24($key)
+	x	$s3,28($key)
+
+	br	$ra	
+.size	_s390x_AES_encrypt,.-_s390x_AES_encrypt
+___
+
+$code.=<<___;
+.type	AES_Td,\@object
+.align	256
+AES_Td:
+___
+&_data_word(
+	0x51f4a750, 0x7e416553, 0x1a17a4c3, 0x3a275e96,
+	0x3bab6bcb, 0x1f9d45f1, 0xacfa58ab, 0x4be30393,
+	0x2030fa55, 0xad766df6, 0x88cc7691, 0xf5024c25,
+	0x4fe5d7fc, 0xc52acbd7, 0x26354480, 0xb562a38f,
+	0xdeb15a49, 0x25ba1b67, 0x45ea0e98, 0x5dfec0e1,
+	0xc32f7502, 0x814cf012, 0x8d4697a3, 0x6bd3f9c6,
+	0x038f5fe7, 0x15929c95, 0xbf6d7aeb, 0x955259da,
+	0xd4be832d, 0x587421d3, 0x49e06929, 0x8ec9c844,
+	0x75c2896a, 0xf48e7978, 0x99583e6b, 0x27b971dd,
+	0xbee14fb6, 0xf088ad17, 0xc920ac66, 0x7dce3ab4,
+	0x63df4a18, 0xe51a3182, 0x97513360, 0x62537f45,
+	0xb16477e0, 0xbb6bae84, 0xfe81a01c, 0xf9082b94,
+	0x70486858, 0x8f45fd19, 0x94de6c87, 0x527bf8b7,
+	0xab73d323, 0x724b02e2, 0xe31f8f57, 0x6655ab2a,
+	0xb2eb2807, 0x2fb5c203, 0x86c57b9a, 0xd33708a5,
+	0x302887f2, 0x23bfa5b2, 0x02036aba, 0xed16825c,
+	0x8acf1c2b, 0xa779b492, 0xf307f2f0, 0x4e69e2a1,
+	0x65daf4cd, 0x0605bed5, 0xd134621f, 0xc4a6fe8a,
+	0x342e539d, 0xa2f355a0, 0x058ae132, 0xa4f6eb75,
+	0x0b83ec39, 0x4060efaa, 0x5e719f06, 0xbd6e1051,
+	0x3e218af9, 0x96dd063d, 0xdd3e05ae, 0x4de6bd46,
+	0x91548db5, 0x71c45d05, 0x0406d46f, 0x605015ff,
+	0x1998fb24, 0xd6bde997, 0x894043cc, 0x67d99e77,
+	0xb0e842bd, 0x07898b88, 0xe7195b38, 0x79c8eedb,
+	0xa17c0a47, 0x7c420fe9, 0xf8841ec9, 0x00000000,
+	0x09808683, 0x322bed48, 0x1e1170ac, 0x6c5a724e,
+	0xfd0efffb, 0x0f853856, 0x3daed51e, 0x362d3927,
+	0x0a0fd964, 0x685ca621, 0x9b5b54d1, 0x24362e3a,
+	0x0c0a67b1, 0x9357e70f, 0xb4ee96d2, 0x1b9b919e,
+	0x80c0c54f, 0x61dc20a2, 0x5a774b69, 0x1c121a16,
+	0xe293ba0a, 0xc0a02ae5, 0x3c22e043, 0x121b171d,
+	0x0e090d0b, 0xf28bc7ad, 0x2db6a8b9, 0x141ea9c8,
+	0x57f11985, 0xaf75074c, 0xee99ddbb, 0xa37f60fd,
+	0xf701269f, 0x5c72f5bc, 0x44663bc5, 0x5bfb7e34,
+	0x8b432976, 0xcb23c6dc, 0xb6edfc68, 0xb8e4f163,
+	0xd731dcca, 0x42638510, 0x13972240, 0x84c61120,
+	0x854a247d, 0xd2bb3df8, 0xaef93211, 0xc729a16d,
+	0x1d9e2f4b, 0xdcb230f3, 0x0d8652ec, 0x77c1e3d0,
+	0x2bb3166c, 0xa970b999, 0x119448fa, 0x47e96422,
+	0xa8fc8cc4, 0xa0f03f1a, 0x567d2cd8, 0x223390ef,
+	0x87494ec7, 0xd938d1c1, 0x8ccaa2fe, 0x98d40b36,
+	0xa6f581cf, 0xa57ade28, 0xdab78e26, 0x3fadbfa4,
+	0x2c3a9de4, 0x5078920d, 0x6a5fcc9b, 0x547e4662,
+	0xf68d13c2, 0x90d8b8e8, 0x2e39f75e, 0x82c3aff5,
+	0x9f5d80be, 0x69d0937c, 0x6fd52da9, 0xcf2512b3,
+	0xc8ac993b, 0x10187da7, 0xe89c636e, 0xdb3bbb7b,
+	0xcd267809, 0x6e5918f4, 0xec9ab701, 0x834f9aa8,
+	0xe6956e65, 0xaaffe67e, 0x21bccf08, 0xef15e8e6,
+	0xbae79bd9, 0x4a6f36ce, 0xea9f09d4, 0x29b07cd6,
+	0x31a4b2af, 0x2a3f2331, 0xc6a59430, 0x35a266c0,
+	0x744ebc37, 0xfc82caa6, 0xe090d0b0, 0x33a7d815,
+	0xf104984a, 0x41ecdaf7, 0x7fcd500e, 0x1791f62f,
+	0x764dd68d, 0x43efb04d, 0xccaa4d54, 0xe49604df,
+	0x9ed1b5e3, 0x4c6a881b, 0xc12c1fb8, 0x4665517f,
+	0x9d5eea04, 0x018c355d, 0xfa877473, 0xfb0b412e,
+	0xb3671d5a, 0x92dbd252, 0xe9105633, 0x6dd64713,
+	0x9ad7618c, 0x37a10c7a, 0x59f8148e, 0xeb133c89,
+	0xcea927ee, 0xb761c935, 0xe11ce5ed, 0x7a47b13c,
+	0x9cd2df59, 0x55f2733f, 0x1814ce79, 0x73c737bf,
+	0x53f7cdea, 0x5ffdaa5b, 0xdf3d6f14, 0x7844db86,
+	0xcaaff381, 0xb968c43e, 0x3824342c, 0xc2a3405f,
+	0x161dc372, 0xbce2250c, 0x283c498b, 0xff0d9541,
+	0x39a80171, 0x080cb3de, 0xd8b4e49c, 0x6456c190,
+	0x7bcb8461, 0xd532b670, 0x486c5c74, 0xd0b85742);
+$code.=<<___;
+# Td4[256]
+.byte	0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38
+.byte	0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb
+.byte	0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87
+.byte	0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb
+.byte	0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d
+.byte	0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e
+.byte	0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2
+.byte	0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25
+.byte	0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16
+.byte	0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92
+.byte	0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda
+.byte	0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84
+.byte	0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a
+.byte	0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06
+.byte	0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02
+.byte	0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b
+.byte	0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea
+.byte	0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73
+.byte	0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85
+.byte	0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e
+.byte	0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89
+.byte	0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b
+.byte	0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20
+.byte	0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4
+.byte	0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31
+.byte	0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f
+.byte	0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d
+.byte	0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef
+.byte	0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0
+.byte	0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61
+.byte	0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26
+.byte	0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d
+.size	AES_Td,.-AES_Td
+
+# void AES_decrypt(const unsigned char *inp, unsigned char *out,
+# 		 const AES_KEY *key) {
+.globl	AES_decrypt
+.type	AES_decrypt,\@function
+AES_decrypt:
+___
+$code.=<<___ if (!$softonly);
+	l	%r0,240($key)
+	lhi	%r1,16
+	clr	%r0,%r1
+	jl	.Ldsoft
+
+	la	%r1,0($key)
+	#la	%r2,0($inp)
+	la	%r4,0($out)
+	lghi	%r3,16		# single block length
+	.long	0xb92e0042	# km %r4,%r2
+	brc	1,.-4		# can this happen?
+	br	%r14
+.align	64
+.Ldsoft:
+___
+$code.=<<___;
+	stm${g}	%r3,$ra,3*$SIZE_T($sp)
+
+	llgf	$s0,0($inp)
+	llgf	$s1,4($inp)
+	llgf	$s2,8($inp)
+	llgf	$s3,12($inp)
+
+	larl	$tbl,AES_Td
+	bras	$ra,_s390x_AES_decrypt
+
+	l${g}	$out,3*$SIZE_T($sp)
+	st	$s0,0($out)
+	st	$s1,4($out)
+	st	$s2,8($out)
+	st	$s3,12($out)
+
+	lm${g}	%r6,$ra,6*$SIZE_T($sp)
+	br	$ra
+.size	AES_decrypt,.-AES_decrypt
+
+.type   _s390x_AES_decrypt,\@function
+.align	16
+_s390x_AES_decrypt:
+	st${g}	$ra,15*$SIZE_T($sp)
+	x	$s0,0($key)
+	x	$s1,4($key)
+	x	$s2,8($key)
+	x	$s3,12($key)
+	l	$rounds,240($key)
+	llill	$mask,`0xff<<3`
+	aghi	$rounds,-1
+	j	.Ldec_loop
+.align	16
+.Ldec_loop:
+	srlg	$t1,$s0,`16-3`
+	srlg	$t2,$s0,`8-3`
+	sllg	$t3,$s0,`0+3`
+	srl	$s0,`24-3`
+	nr	$s0,$mask
+	nr	$t1,$mask
+	nr	$t2,$mask
+	ngr	$t3,$mask
+
+	sllg	$i1,$s1,`0+3`	# i0
+	srlg	$i2,$s1,`16-3`
+	srlg	$i3,$s1,`8-3`
+	srl	$s1,`24-3`
+	ngr	$i1,$mask
+	nr	$s1,$mask
+	nr	$i2,$mask
+	nr	$i3,$mask
+
+	l	$s0,0($s0,$tbl)	# Td0[s0>>24]
+	l	$t1,3($t1,$tbl)	# Td1[s0>>16]
+	l	$t2,2($t2,$tbl)	# Td2[s0>>8]
+	l	$t3,1($t3,$tbl)	# Td3[s0>>0]
+
+	x	$s0,1($i1,$tbl)	# Td3[s1>>0]
+	l	$s1,0($s1,$tbl)	# Td0[s1>>24]
+	x	$t2,3($i2,$tbl)	# Td1[s1>>16]
+	x	$t3,2($i3,$tbl)	# Td2[s1>>8]
+
+	srlg	$i1,$s2,`8-3`	# i0
+	sllg	$i2,$s2,`0+3`	# i1
+	srlg	$i3,$s2,`16-3`
+	srl	$s2,`24-3`
+	nr	$i1,$mask
+	ngr	$i2,$mask
+	nr	$s2,$mask
+	nr	$i3,$mask
+
+	xr	$s1,$t1
+	srlg	$ra,$s3,`8-3`	# i1
+	srlg	$t1,$s3,`16-3`	# i0
+	nr	$ra,$mask
+	la	$key,16($key)
+	nr	$t1,$mask
+
+	x	$s0,2($i1,$tbl)	# Td2[s2>>8]
+	x	$s1,1($i2,$tbl)	# Td3[s2>>0]
+	l	$s2,0($s2,$tbl)	# Td0[s2>>24]
+	x	$t3,3($i3,$tbl)	# Td1[s2>>16]
+
+	sllg	$i3,$s3,`0+3`	# i2
+	srl	$s3,`24-3`
+	ngr	$i3,$mask
+	nr	$s3,$mask
+
+	xr	$s2,$t2
+	x	$s0,0($key)
+	x	$s1,4($key)
+	x	$s2,8($key)
+	x	$t3,12($key)
+
+	x	$s0,3($t1,$tbl)	# Td1[s3>>16]
+	x	$s1,2($ra,$tbl)	# Td2[s3>>8]
+	x	$s2,1($i3,$tbl)	# Td3[s3>>0]
+	l	$s3,0($s3,$tbl)	# Td0[s3>>24]
+	xr	$s3,$t3
+
+	brct	$rounds,.Ldec_loop
+	.align	16
+
+	l	$t1,`2048+0`($tbl)	# prefetch Td4
+	l	$t2,`2048+64`($tbl)
+	l	$t3,`2048+128`($tbl)
+	l	$i1,`2048+192`($tbl)
+	llill	$mask,0xff
+
+	srlg	$i3,$s0,24	# i0
+	srlg	$t1,$s0,16
+	srlg	$t2,$s0,8
+	nr	$s0,$mask	# i3
+	nr	$t1,$mask
+
+	srlg	$i1,$s1,24
+	nr	$t2,$mask
+	srlg	$i2,$s1,16
+	srlg	$ra,$s1,8
+	nr	$s1,$mask	# i0
+	nr	$i2,$mask
+	nr	$ra,$mask
+
+	llgc	$i3,2048($i3,$tbl)	# Td4[s0>>24]
+	llgc	$t1,2048($t1,$tbl)	# Td4[s0>>16]
+	llgc	$t2,2048($t2,$tbl)	# Td4[s0>>8]
+	sll	$t1,16
+	llgc	$t3,2048($s0,$tbl)	# Td4[s0>>0]
+	sllg	$s0,$i3,24
+	sll	$t2,8
+
+	llgc	$s1,2048($s1,$tbl)	# Td4[s1>>0]
+	llgc	$i1,2048($i1,$tbl)	# Td4[s1>>24]
+	llgc	$i2,2048($i2,$tbl)	# Td4[s1>>16]
+	sll	$i1,24
+	llgc	$i3,2048($ra,$tbl)	# Td4[s1>>8]
+	sll	$i2,16
+	sll	$i3,8
+	or	$s0,$s1
+	or	$t1,$i1
+	or	$t2,$i2
+	or	$t3,$i3
+
+	srlg	$i1,$s2,8	# i0
+	srlg	$i2,$s2,24
+	srlg	$i3,$s2,16
+	nr	$s2,$mask	# i1
+	nr	$i1,$mask
+	nr	$i3,$mask
+	llgc	$i1,2048($i1,$tbl)	# Td4[s2>>8]
+	llgc	$s1,2048($s2,$tbl)	# Td4[s2>>0]
+	llgc	$i2,2048($i2,$tbl)	# Td4[s2>>24]
+	llgc	$i3,2048($i3,$tbl)	# Td4[s2>>16]
+	sll	$i1,8
+	sll	$i2,24
+	or	$s0,$i1
+	sll	$i3,16
+	or	$t2,$i2
+	or	$t3,$i3
+
+	srlg	$i1,$s3,16	# i0
+	srlg	$i2,$s3,8	# i1
+	srlg	$i3,$s3,24
+	nr	$s3,$mask	# i2
+	nr	$i1,$mask
+	nr	$i2,$mask
+
+	l${g}	$ra,15*$SIZE_T($sp)
+	or	$s1,$t1
+	l	$t0,16($key)
+	l	$t1,20($key)
+
+	llgc	$i1,2048($i1,$tbl)	# Td4[s3>>16]
+	llgc	$i2,2048($i2,$tbl)	# Td4[s3>>8]
+	sll	$i1,16
+	llgc	$s2,2048($s3,$tbl)	# Td4[s3>>0]
+	llgc	$s3,2048($i3,$tbl)	# Td4[s3>>24]
+	sll	$i2,8
+	sll	$s3,24
+	or	$s0,$i1
+	or	$s1,$i2
+	or	$s2,$t2
+	or	$s3,$t3
+
+	xr	$s0,$t0
+	xr	$s1,$t1
+	x	$s2,24($key)
+	x	$s3,28($key)
+
+	br	$ra	
+.size	_s390x_AES_decrypt,.-_s390x_AES_decrypt
+___
+
+$code.=<<___;
+# void AES_set_encrypt_key(const unsigned char *in, int bits,
+# 		 AES_KEY *key) {
+.globl	AES_set_encrypt_key
+.type	AES_set_encrypt_key,\@function
+.align	16
+AES_set_encrypt_key:
+_s390x_AES_set_encrypt_key:
+	lghi	$t0,0
+	cl${g}r	$inp,$t0
+	je	.Lminus1
+	cl${g}r	$key,$t0
+	je	.Lminus1
+
+	lghi	$t0,128
+	clr	$bits,$t0
+	je	.Lproceed
+	lghi	$t0,192
+	clr	$bits,$t0
+	je	.Lproceed
+	lghi	$t0,256
+	clr	$bits,$t0
+	je	.Lproceed
+	lghi	%r2,-2
+	br	%r14
+
+.align	16
+.Lproceed:
+___
+$code.=<<___ if (!$softonly);
+	# convert bits to km code, [128,192,256]->[18,19,20]
+	lhi	%r5,-128
+	lhi	%r0,18
+	ar	%r5,$bits
+	srl	%r5,6
+	ar	%r5,%r0
+
+	larl	%r1,OPENSSL_s390xcap_P
+	lg	%r0,0(%r1)
+	tmhl	%r0,0x4000	# check for message-security assist
+	jz	.Lekey_internal
+
+	llihh	%r0,0x8000
+	srlg	%r0,%r0,0(%r5)
+	ng	%r0,48(%r1)	# check kmc capability vector
+	jz	.Lekey_internal
+
+	lmg	%r0,%r1,0($inp)	# just copy 128 bits...
+	stmg	%r0,%r1,0($key)
+	lhi	%r0,192
+	cr	$bits,%r0
+	jl	1f
+	lg	%r1,16($inp)
+	stg	%r1,16($key)
+	je	1f
+	lg	%r1,24($inp)
+	stg	%r1,24($key)
+1:	st	$bits,236($key)	# save bits [for debugging purposes]
+	lgr	$t0,%r5
+	st	%r5,240($key)	# save km code
+	lghi	%r2,0
+	br	%r14
+___
+$code.=<<___;
+.align	16
+.Lekey_internal:
+	stm${g}	%r4,%r13,4*$SIZE_T($sp)	# all non-volatile regs and $key
+
+	larl	$tbl,AES_Te+2048
+
+	llgf	$s0,0($inp)
+	llgf	$s1,4($inp)
+	llgf	$s2,8($inp)
+	llgf	$s3,12($inp)
+	st	$s0,0($key)
+	st	$s1,4($key)
+	st	$s2,8($key)
+	st	$s3,12($key)
+	lghi	$t0,128
+	cr	$bits,$t0
+	jne	.Lnot128
+
+	llill	$mask,0xff
+	lghi	$t3,0			# i=0
+	lghi	$rounds,10
+	st	$rounds,240($key)
+
+	llgfr	$t2,$s3			# temp=rk[3]
+	srlg	$i1,$s3,8
+	srlg	$i2,$s3,16
+	srlg	$i3,$s3,24
+	nr	$t2,$mask
+	nr	$i1,$mask
+	nr	$i2,$mask
+
+.align	16
+.L128_loop:
+	la	$t2,0($t2,$tbl)
+	la	$i1,0($i1,$tbl)
+	la	$i2,0($i2,$tbl)
+	la	$i3,0($i3,$tbl)
+	icm	$t2,2,0($t2)		# Te4[rk[3]>>0]<<8
+	icm	$t2,4,0($i1)		# Te4[rk[3]>>8]<<16
+	icm	$t2,8,0($i2)		# Te4[rk[3]>>16]<<24
+	icm	$t2,1,0($i3)		# Te4[rk[3]>>24]
+	x	$t2,256($t3,$tbl)	# rcon[i]
+	xr	$s0,$t2			# rk[4]=rk[0]^...
+	xr	$s1,$s0			# rk[5]=rk[1]^rk[4]
+	xr	$s2,$s1			# rk[6]=rk[2]^rk[5]
+	xr	$s3,$s2			# rk[7]=rk[3]^rk[6]
+
+	llgfr	$t2,$s3			# temp=rk[3]
+	srlg	$i1,$s3,8
+	srlg	$i2,$s3,16
+	nr	$t2,$mask
+	nr	$i1,$mask
+	srlg	$i3,$s3,24
+	nr	$i2,$mask
+
+	st	$s0,16($key)
+	st	$s1,20($key)
+	st	$s2,24($key)
+	st	$s3,28($key)
+	la	$key,16($key)		# key+=4
+	la	$t3,4($t3)		# i++
+	brct	$rounds,.L128_loop
+	lghi	$t0,10
+	lghi	%r2,0
+	lm${g}	%r4,%r13,4*$SIZE_T($sp)
+	br	$ra
+
+.align	16
+.Lnot128:
+	llgf	$t0,16($inp)
+	llgf	$t1,20($inp)
+	st	$t0,16($key)
+	st	$t1,20($key)
+	lghi	$t0,192
+	cr	$bits,$t0
+	jne	.Lnot192
+
+	llill	$mask,0xff
+	lghi	$t3,0			# i=0
+	lghi	$rounds,12
+	st	$rounds,240($key)
+	lghi	$rounds,8
+
+	srlg	$i1,$t1,8
+	srlg	$i2,$t1,16
+	srlg	$i3,$t1,24
+	nr	$t1,$mask
+	nr	$i1,$mask
+	nr	$i2,$mask
+
+.align	16
+.L192_loop:
+	la	$t1,0($t1,$tbl)
+	la	$i1,0($i1,$tbl)
+	la	$i2,0($i2,$tbl)
+	la	$i3,0($i3,$tbl)
+	icm	$t1,2,0($t1)		# Te4[rk[5]>>0]<<8
+	icm	$t1,4,0($i1)		# Te4[rk[5]>>8]<<16
+	icm	$t1,8,0($i2)		# Te4[rk[5]>>16]<<24
+	icm	$t1,1,0($i3)		# Te4[rk[5]>>24]
+	x	$t1,256($t3,$tbl)	# rcon[i]
+	xr	$s0,$t1			# rk[6]=rk[0]^...
+	xr	$s1,$s0			# rk[7]=rk[1]^rk[6]
+	xr	$s2,$s1			# rk[8]=rk[2]^rk[7]
+	xr	$s3,$s2			# rk[9]=rk[3]^rk[8]
+
+	st	$s0,24($key)
+	st	$s1,28($key)
+	st	$s2,32($key)
+	st	$s3,36($key)
+	brct	$rounds,.L192_continue
+	lghi	$t0,12
+	lghi	%r2,0
+	lm${g}	%r4,%r13,4*$SIZE_T($sp)
+	br	$ra
+
+.align	16
+.L192_continue:
+	lgr	$t1,$s3
+	x	$t1,16($key)		# rk[10]=rk[4]^rk[9]
+	st	$t1,40($key)
+	x	$t1,20($key)		# rk[11]=rk[5]^rk[10]
+	st	$t1,44($key)
+
+	srlg	$i1,$t1,8
+	srlg	$i2,$t1,16
+	srlg	$i3,$t1,24
+	nr	$t1,$mask
+	nr	$i1,$mask
+	nr	$i2,$mask
+
+	la	$key,24($key)		# key+=6
+	la	$t3,4($t3)		# i++
+	j	.L192_loop
+
+.align	16
+.Lnot192:
+	llgf	$t0,24($inp)
+	llgf	$t1,28($inp)
+	st	$t0,24($key)
+	st	$t1,28($key)
+	llill	$mask,0xff
+	lghi	$t3,0			# i=0
+	lghi	$rounds,14
+	st	$rounds,240($key)
+	lghi	$rounds,7
+
+	srlg	$i1,$t1,8
+	srlg	$i2,$t1,16
+	srlg	$i3,$t1,24
+	nr	$t1,$mask
+	nr	$i1,$mask
+	nr	$i2,$mask
+
+.align	16
+.L256_loop:
+	la	$t1,0($t1,$tbl)
+	la	$i1,0($i1,$tbl)
+	la	$i2,0($i2,$tbl)
+	la	$i3,0($i3,$tbl)
+	icm	$t1,2,0($t1)		# Te4[rk[7]>>0]<<8
+	icm	$t1,4,0($i1)		# Te4[rk[7]>>8]<<16
+	icm	$t1,8,0($i2)		# Te4[rk[7]>>16]<<24
+	icm	$t1,1,0($i3)		# Te4[rk[7]>>24]
+	x	$t1,256($t3,$tbl)	# rcon[i]
+	xr	$s0,$t1			# rk[8]=rk[0]^...
+	xr	$s1,$s0			# rk[9]=rk[1]^rk[8]
+	xr	$s2,$s1			# rk[10]=rk[2]^rk[9]
+	xr	$s3,$s2			# rk[11]=rk[3]^rk[10]
+	st	$s0,32($key)
+	st	$s1,36($key)
+	st	$s2,40($key)
+	st	$s3,44($key)
+	brct	$rounds,.L256_continue
+	lghi	$t0,14
+	lghi	%r2,0
+	lm${g}	%r4,%r13,4*$SIZE_T($sp)
+	br	$ra
+
+.align	16
+.L256_continue:
+	lgr	$t1,$s3			# temp=rk[11]
+	srlg	$i1,$s3,8
+	srlg	$i2,$s3,16
+	srlg	$i3,$s3,24
+	nr	$t1,$mask
+	nr	$i1,$mask
+	nr	$i2,$mask
+	la	$t1,0($t1,$tbl)
+	la	$i1,0($i1,$tbl)
+	la	$i2,0($i2,$tbl)
+	la	$i3,0($i3,$tbl)
+	llgc	$t1,0($t1)		# Te4[rk[11]>>0]
+	icm	$t1,2,0($i1)		# Te4[rk[11]>>8]<<8
+	icm	$t1,4,0($i2)		# Te4[rk[11]>>16]<<16
+	icm	$t1,8,0($i3)		# Te4[rk[11]>>24]<<24
+	x	$t1,16($key)		# rk[12]=rk[4]^...
+	st	$t1,48($key)
+	x	$t1,20($key)		# rk[13]=rk[5]^rk[12]
+	st	$t1,52($key)
+	x	$t1,24($key)		# rk[14]=rk[6]^rk[13]
+	st	$t1,56($key)
+	x	$t1,28($key)		# rk[15]=rk[7]^rk[14]
+	st	$t1,60($key)
+
+	srlg	$i1,$t1,8
+	srlg	$i2,$t1,16
+	srlg	$i3,$t1,24
+	nr	$t1,$mask
+	nr	$i1,$mask
+	nr	$i2,$mask
+
+	la	$key,32($key)		# key+=8
+	la	$t3,4($t3)		# i++
+	j	.L256_loop
+
+.Lminus1:
+	lghi	%r2,-1
+	br	$ra
+.size	AES_set_encrypt_key,.-AES_set_encrypt_key
+
+# void AES_set_decrypt_key(const unsigned char *in, int bits,
+# 		 AES_KEY *key) {
+.globl	AES_set_decrypt_key
+.type	AES_set_decrypt_key,\@function
+.align	16
+AES_set_decrypt_key:
+	#st${g}	$key,4*$SIZE_T($sp)	# I rely on AES_set_encrypt_key to
+	st${g}	$ra,14*$SIZE_T($sp)	# save non-volatile registers and $key!
+	bras	$ra,_s390x_AES_set_encrypt_key
+	#l${g}	$key,4*$SIZE_T($sp)
+	l${g}	$ra,14*$SIZE_T($sp)
+	ltgr	%r2,%r2
+	bnzr	$ra
+___
+$code.=<<___ if (!$softonly);
+	#l	$t0,240($key)
+	lhi	$t1,16
+	cr	$t0,$t1
+	jl	.Lgo
+	oill	$t0,0x80	# set "decrypt" bit
+	st	$t0,240($key)
+	br	$ra
+___
+$code.=<<___;
+.align	16
+.Lgo:	lgr	$rounds,$t0	#llgf	$rounds,240($key)
+	la	$i1,0($key)
+	sllg	$i2,$rounds,4
+	la	$i2,0($i2,$key)
+	srl	$rounds,1
+	lghi	$t1,-16
+
+.align	16
+.Linv:	lmg	$s0,$s1,0($i1)
+	lmg	$s2,$s3,0($i2)
+	stmg	$s0,$s1,0($i2)
+	stmg	$s2,$s3,0($i1)
+	la	$i1,16($i1)
+	la	$i2,0($t1,$i2)
+	brct	$rounds,.Linv
+___
+$mask80=$i1;
+$mask1b=$i2;
+$maskfe=$i3;
+$code.=<<___;
+	llgf	$rounds,240($key)
+	aghi	$rounds,-1
+	sll	$rounds,2	# (rounds-1)*4
+	llilh	$mask80,0x8080
+	llilh	$mask1b,0x1b1b
+	llilh	$maskfe,0xfefe
+	oill	$mask80,0x8080
+	oill	$mask1b,0x1b1b
+	oill	$maskfe,0xfefe
+
+.align	16
+.Lmix:	l	$s0,16($key)	# tp1
+	lr	$s1,$s0
+	ngr	$s1,$mask80
+	srlg	$t1,$s1,7
+	slr	$s1,$t1
+	nr	$s1,$mask1b
+	sllg	$t1,$s0,1
+	nr	$t1,$maskfe
+	xr	$s1,$t1		# tp2
+
+	lr	$s2,$s1
+	ngr	$s2,$mask80
+	srlg	$t1,$s2,7
+	slr	$s2,$t1
+	nr	$s2,$mask1b
+	sllg	$t1,$s1,1
+	nr	$t1,$maskfe
+	xr	$s2,$t1		# tp4
+
+	lr	$s3,$s2
+	ngr	$s3,$mask80
+	srlg	$t1,$s3,7
+	slr	$s3,$t1
+	nr	$s3,$mask1b
+	sllg	$t1,$s2,1
+	nr	$t1,$maskfe
+	xr	$s3,$t1		# tp8
+
+	xr	$s1,$s0		# tp2^tp1
+	xr	$s2,$s0		# tp4^tp1
+	rll	$s0,$s0,24	# = ROTATE(tp1,8)
+	xr	$s2,$s3		# ^=tp8
+	xr	$s0,$s1		# ^=tp2^tp1
+	xr	$s1,$s3		# tp2^tp1^tp8
+	xr	$s0,$s2		# ^=tp4^tp1^tp8
+	rll	$s1,$s1,8
+	rll	$s2,$s2,16
+	xr	$s0,$s1		# ^= ROTATE(tp8^tp2^tp1,24)
+	rll	$s3,$s3,24
+	xr	$s0,$s2    	# ^= ROTATE(tp8^tp4^tp1,16)
+	xr	$s0,$s3		# ^= ROTATE(tp8,8)
+
+	st	$s0,16($key)
+	la	$key,4($key)
+	brct	$rounds,.Lmix
+
+	lm${g}	%r6,%r13,6*$SIZE_T($sp)# as was saved by AES_set_encrypt_key!
+	lghi	%r2,0
+	br	$ra
+.size	AES_set_decrypt_key,.-AES_set_decrypt_key
+___
+
+########################################################################
+# void AES_cbc_encrypt(const unsigned char *in, unsigned char *out,
+#                     size_t length, const AES_KEY *key,
+#                     unsigned char *ivec, const int enc)
+{
+my $inp="%r2";
+my $out="%r4";	# length and out are swapped
+my $len="%r3";
+my $key="%r5";
+my $ivp="%r6";
+
+$code.=<<___;
+.globl	AES_cbc_encrypt
+.type	AES_cbc_encrypt,\@function
+.align	16
+AES_cbc_encrypt:
+	xgr	%r3,%r4		# flip %r3 and %r4, out and len
+	xgr	%r4,%r3
+	xgr	%r3,%r4
+___
+$code.=<<___ if (!$softonly);
+	lhi	%r0,16
+	cl	%r0,240($key)
+	jh	.Lcbc_software
+
+	lg	%r0,0($ivp)	# copy ivec
+	lg	%r1,8($ivp)
+	stmg	%r0,%r1,16($sp)
+	lmg	%r0,%r1,0($key)	# copy key, cover 256 bit
+	stmg	%r0,%r1,32($sp)
+	lmg	%r0,%r1,16($key)
+	stmg	%r0,%r1,48($sp)
+	l	%r0,240($key)	# load kmc code
+	lghi	$key,15		# res=len%16, len-=res;
+	ngr	$key,$len
+	sl${g}r	$len,$key
+	la	%r1,16($sp)	# parameter block - ivec || key
+	jz	.Lkmc_truncated
+	.long	0xb92f0042	# kmc %r4,%r2
+	brc	1,.-4		# pay attention to "partial completion"
+	ltr	$key,$key
+	jnz	.Lkmc_truncated
+.Lkmc_done:
+	lmg	%r0,%r1,16($sp)	# copy ivec to caller
+	stg	%r0,0($ivp)
+	stg	%r1,8($ivp)
+	br	$ra
+.align	16
+.Lkmc_truncated:
+	ahi	$key,-1		# it's the way it's encoded in mvc
+	tmll	%r0,0x80
+	jnz	.Lkmc_truncated_dec
+	lghi	%r1,0
+	stg	%r1,16*$SIZE_T($sp)
+	stg	%r1,16*$SIZE_T+8($sp)
+	bras	%r1,1f
+	mvc	16*$SIZE_T(1,$sp),0($inp)
+1:	ex	$key,0(%r1)
+	la	%r1,16($sp)	# restore parameter block
+	la	$inp,16*$SIZE_T($sp)
+	lghi	$len,16
+	.long	0xb92f0042	# kmc %r4,%r2
+	j	.Lkmc_done
+.align	16
+.Lkmc_truncated_dec:
+	st${g}	$out,4*$SIZE_T($sp)
+	la	$out,16*$SIZE_T($sp)
+	lghi	$len,16
+	.long	0xb92f0042	# kmc %r4,%r2
+	l${g}	$out,4*$SIZE_T($sp)
+	bras	%r1,2f
+	mvc	0(1,$out),16*$SIZE_T($sp)
+2:	ex	$key,0(%r1)
+	j	.Lkmc_done
+.align	16
+.Lcbc_software:
+___
+$code.=<<___;
+	stm${g}	$key,$ra,5*$SIZE_T($sp)
+	lhi	%r0,0
+	cl	%r0,`$stdframe+$SIZE_T-4`($sp)
+	je	.Lcbc_decrypt
+
+	larl	$tbl,AES_Te
+
+	llgf	$s0,0($ivp)
+	llgf	$s1,4($ivp)
+	llgf	$s2,8($ivp)
+	llgf	$s3,12($ivp)
+
+	lghi	$t0,16
+	sl${g}r	$len,$t0
+	brc	4,.Lcbc_enc_tail	# if borrow
+.Lcbc_enc_loop:
+	stm${g}	$inp,$out,2*$SIZE_T($sp)
+	x	$s0,0($inp)
+	x	$s1,4($inp)
+	x	$s2,8($inp)
+	x	$s3,12($inp)
+	lgr	%r4,$key
+
+	bras	$ra,_s390x_AES_encrypt
+
+	lm${g}	$inp,$key,2*$SIZE_T($sp)
+	st	$s0,0($out)
+	st	$s1,4($out)
+	st	$s2,8($out)
+	st	$s3,12($out)
+
+	la	$inp,16($inp)
+	la	$out,16($out)
+	lghi	$t0,16
+	lt${g}r	$len,$len
+	jz	.Lcbc_enc_done
+	sl${g}r	$len,$t0
+	brc	4,.Lcbc_enc_tail	# if borrow
+	j	.Lcbc_enc_loop
+.align	16
+.Lcbc_enc_done:
+	l${g}	$ivp,6*$SIZE_T($sp)
+	st	$s0,0($ivp)
+	st	$s1,4($ivp)	
+	st	$s2,8($ivp)
+	st	$s3,12($ivp)
+
+	lm${g}	%r7,$ra,7*$SIZE_T($sp)
+	br	$ra
+
+.align	16
+.Lcbc_enc_tail:
+	aghi	$len,15
+	lghi	$t0,0
+	stg	$t0,16*$SIZE_T($sp)
+	stg	$t0,16*$SIZE_T+8($sp)
+	bras	$t1,3f
+	mvc	16*$SIZE_T(1,$sp),0($inp)
+3:	ex	$len,0($t1)
+	lghi	$len,0
+	la	$inp,16*$SIZE_T($sp)
+	j	.Lcbc_enc_loop
+
+.align	16
+.Lcbc_decrypt:
+	larl	$tbl,AES_Td
+
+	lg	$t0,0($ivp)
+	lg	$t1,8($ivp)
+	stmg	$t0,$t1,16*$SIZE_T($sp)
+
+.Lcbc_dec_loop:
+	stm${g}	$inp,$out,2*$SIZE_T($sp)
+	llgf	$s0,0($inp)
+	llgf	$s1,4($inp)
+	llgf	$s2,8($inp)
+	llgf	$s3,12($inp)
+	lgr	%r4,$key
+
+	bras	$ra,_s390x_AES_decrypt
+
+	lm${g}	$inp,$key,2*$SIZE_T($sp)
+	sllg	$s0,$s0,32
+	sllg	$s2,$s2,32
+	lr	$s0,$s1
+	lr	$s2,$s3
+
+	lg	$t0,0($inp)
+	lg	$t1,8($inp)
+	xg	$s0,16*$SIZE_T($sp)
+	xg	$s2,16*$SIZE_T+8($sp)
+	lghi	$s1,16
+	sl${g}r	$len,$s1
+	brc	4,.Lcbc_dec_tail	# if borrow
+	brc	2,.Lcbc_dec_done	# if zero
+	stg	$s0,0($out)
+	stg	$s2,8($out)
+	stmg	$t0,$t1,16*$SIZE_T($sp)
+
+	la	$inp,16($inp)
+	la	$out,16($out)
+	j	.Lcbc_dec_loop
+
+.Lcbc_dec_done:
+	stg	$s0,0($out)
+	stg	$s2,8($out)
+.Lcbc_dec_exit:
+	lm${g}	%r6,$ra,6*$SIZE_T($sp)
+	stmg	$t0,$t1,0($ivp)
+
+	br	$ra
+
+.align	16
+.Lcbc_dec_tail:
+	aghi	$len,15
+	stg	$s0,16*$SIZE_T($sp)
+	stg	$s2,16*$SIZE_T+8($sp)
+	bras	$s1,4f
+	mvc	0(1,$out),16*$SIZE_T($sp)
+4:	ex	$len,0($s1)
+	j	.Lcbc_dec_exit
+.size	AES_cbc_encrypt,.-AES_cbc_encrypt
+___
+}
+########################################################################
+# void AES_ctr32_encrypt(const unsigned char *in, unsigned char *out,
+#                     size_t blocks, const AES_KEY *key,
+#                     const unsigned char *ivec)
+{
+my $inp="%r2";
+my $out="%r4";	# blocks and out are swapped
+my $len="%r3";
+my $key="%r5";	my $iv0="%r5";
+my $ivp="%r6";
+my $fp ="%r7";
+
+$code.=<<___;
+.globl	AES_ctr32_encrypt
+.type	AES_ctr32_encrypt,\@function
+.align	16
+AES_ctr32_encrypt:
+	xgr	%r3,%r4		# flip %r3 and %r4, $out and $len
+	xgr	%r4,%r3
+	xgr	%r3,%r4
+	llgfr	$len,$len	# safe in ctr32 subroutine even in 64-bit case
+___
+$code.=<<___ if (!$softonly);
+	l	%r0,240($key)
+	lhi	%r1,16
+	clr	%r0,%r1
+	jl	.Lctr32_software
+
+	stm${g}	%r6,$s3,6*$SIZE_T($sp)
+
+	slgr	$out,$inp
+	la	%r1,0($key)	# %r1 is permanent copy of $key
+	lg	$iv0,0($ivp)	# load ivec
+	lg	$ivp,8($ivp)
+
+	# prepare and allocate stack frame at the top of 4K page
+	# with 1K reserved for eventual signal handling
+	lghi	$s0,-1024-256-16# guarantee at least 256-bytes buffer
+	lghi	$s1,-4096
+	algr	$s0,$sp
+	lgr	$fp,$sp
+	ngr	$s0,$s1		# align at page boundary
+	slgr	$fp,$s0		# total buffer size
+	lgr	$s2,$sp
+	lghi	$s1,1024+16	# sl[g]fi is extended-immediate facility
+	slgr	$fp,$s1		# deduct reservation to get usable buffer size
+	# buffer size is at lest 256 and at most 3072+256-16
+
+	la	$sp,1024($s0)	# alloca
+	srlg	$fp,$fp,4	# convert bytes to blocks, minimum 16
+	st${g}	$s2,0($sp)	# back-chain
+	st${g}	$fp,$SIZE_T($sp)
+
+	slgr	$len,$fp
+	brc	1,.Lctr32_hw_switch	# not zero, no borrow
+	algr	$fp,$len	# input is shorter than allocated buffer
+	lghi	$len,0
+	st${g}	$fp,$SIZE_T($sp)
+
+.Lctr32_hw_switch:
+___
+$code.=<<___ if (0);	######### kmctr code was measured to be ~12% slower
+	larl	$s0,OPENSSL_s390xcap_P
+	lg	$s0,8($s0)
+	tmhh	$s0,0x0004	# check for message_security-assist-4
+	jz	.Lctr32_km_loop
+
+	llgfr	$s0,%r0
+	lgr	$s1,%r1
+	larl	%r1,OPENSSL_s390xcap_P
+	llihh	%r0,0x8000	# check if kmctr supports the function code
+	srlg	%r0,%r0,0($s0)
+	ng	%r0,64(%r1)	# check kmctr capability vector
+	lgr	%r0,$s0
+	lgr	%r1,$s1
+	jz	.Lctr32_km_loop
+
+####### kmctr code
+	algr	$out,$inp	# restore $out
+	lgr	$s1,$len	# $s1 undertakes $len
+	j	.Lctr32_kmctr_loop
+.align	16
+.Lctr32_kmctr_loop:
+	la	$s2,16($sp)
+	lgr	$s3,$fp
+.Lctr32_kmctr_prepare:
+	stg	$iv0,0($s2)
+	stg	$ivp,8($s2)
+	la	$s2,16($s2)
+	ahi	$ivp,1		# 32-bit increment, preserves upper half
+	brct	$s3,.Lctr32_kmctr_prepare
+
+	#la	$inp,0($inp)	# inp
+	sllg	$len,$fp,4	# len
+	#la	$out,0($out)	# out
+	la	$s2,16($sp)	# iv
+	.long	0xb92da042	# kmctr $out,$s2,$inp
+	brc	1,.-4		# pay attention to "partial completion"
+
+	slgr	$s1,$fp
+	brc	1,.Lctr32_kmctr_loop	# not zero, no borrow
+	algr	$fp,$s1
+	lghi	$s1,0
+	brc	4+1,.Lctr32_kmctr_loop	# not zero
+
+	l${g}	$sp,0($sp)
+	lm${g}	%r6,$s3,6*$SIZE_T($sp)
+	br	$ra
+.align	16
+___
+$code.=<<___;
+.Lctr32_km_loop:
+	la	$s2,16($sp)
+	lgr	$s3,$fp
+.Lctr32_km_prepare:
+	stg	$iv0,0($s2)
+	stg	$ivp,8($s2)
+	la	$s2,16($s2)
+	ahi	$ivp,1		# 32-bit increment, preserves upper half
+	brct	$s3,.Lctr32_km_prepare
+
+	la	$s0,16($sp)	# inp
+	sllg	$s1,$fp,4	# len
+	la	$s2,16($sp)	# out
+	.long	0xb92e00a8	# km %r10,%r8
+	brc	1,.-4		# pay attention to "partial completion"
+
+	la	$s2,16($sp)
+	lgr	$s3,$fp
+	slgr	$s2,$inp
+.Lctr32_km_xor:
+	lg	$s0,0($inp)
+	lg	$s1,8($inp)
+	xg	$s0,0($s2,$inp)
+	xg	$s1,8($s2,$inp)
+	stg	$s0,0($out,$inp)
+	stg	$s1,8($out,$inp)
+	la	$inp,16($inp)
+	brct	$s3,.Lctr32_km_xor
+
+	slgr	$len,$fp
+	brc	1,.Lctr32_km_loop	# not zero, no borrow
+	algr	$fp,$len
+	lghi	$len,0
+	brc	4+1,.Lctr32_km_loop	# not zero
+
+	l${g}	$s0,0($sp)
+	l${g}	$s1,$SIZE_T($sp)
+	la	$s2,16($sp)
+.Lctr32_km_zap:
+	stg	$s0,0($s2)
+	stg	$s0,8($s2)
+	la	$s2,16($s2)
+	brct	$s1,.Lctr32_km_zap
+
+	la	$sp,0($s0)
+	lm${g}	%r6,$s3,6*$SIZE_T($sp)
+	br	$ra
+.align	16
+.Lctr32_software:
+___
+$code.=<<___;
+	stm${g}	$key,$ra,5*$SIZE_T($sp)
+	sl${g}r	$inp,$out
+	larl	$tbl,AES_Te
+	llgf	$t1,12($ivp)
+
+.Lctr32_loop:
+	stm${g}	$inp,$out,2*$SIZE_T($sp)
+	llgf	$s0,0($ivp)
+	llgf	$s1,4($ivp)
+	llgf	$s2,8($ivp)
+	lgr	$s3,$t1
+	st	$t1,16*$SIZE_T($sp)
+	lgr	%r4,$key
+
+	bras	$ra,_s390x_AES_encrypt
+
+	lm${g}	$inp,$ivp,2*$SIZE_T($sp)
+	llgf	$t1,16*$SIZE_T($sp)
+	x	$s0,0($inp,$out)
+	x	$s1,4($inp,$out)
+	x	$s2,8($inp,$out)
+	x	$s3,12($inp,$out)
+	stm	$s0,$s3,0($out)
+
+	la	$out,16($out)
+	ahi	$t1,1		# 32-bit increment
+	brct	$len,.Lctr32_loop
+
+	lm${g}	%r6,$ra,6*$SIZE_T($sp)
+	br	$ra
+.size	AES_ctr32_encrypt,.-AES_ctr32_encrypt
+___
+}
+
+########################################################################
+# void AES_xts_encrypt(const char *inp,char *out,size_t len,
+#	const AES_KEY *key1, const AES_KEY *key2,
+#	const unsigned char iv[16]);
+#
+{
+my $inp="%r2";
+my $out="%r4";	# len and out are swapped
+my $len="%r3";
+my $key1="%r5";	# $i1
+my $key2="%r6";	# $i2
+my $fp="%r7";	# $i3
+my $tweak=16*$SIZE_T+16;	# or $stdframe-16, bottom of the frame...
+
+$code.=<<___;
+.type	_s390x_xts_km,\@function
+.align	16
+_s390x_xts_km:
+___
+$code.=<<___ if(1);
+	llgfr	$s0,%r0			# put aside the function code
+	lghi	$s1,0x7f
+	nr	$s1,%r0
+	larl	%r1,OPENSSL_s390xcap_P
+	llihh	%r0,0x8000
+	srlg	%r0,%r0,32($s1)		# check for 32+function code
+	ng	%r0,32(%r1)		# check km capability vector
+	lgr	%r0,$s0			# restore the function code
+	la	%r1,0($key1)		# restore $key1
+	jz	.Lxts_km_vanilla
+
+	lmg	$i2,$i3,$tweak($sp)	# put aside the tweak value
+	algr	$out,$inp
+
+	oill	%r0,32			# switch to xts function code
+	aghi	$s1,-18			#
+	sllg	$s1,$s1,3		# (function code - 18)*8, 0 or 16
+	la	%r1,$tweak-16($sp)
+	slgr	%r1,$s1			# parameter block position
+	lmg	$s0,$s3,0($key1)	# load 256 bits of key material,
+	stmg	$s0,$s3,0(%r1)		# and copy it to parameter block.
+					# yes, it contains junk and overlaps
+					# with the tweak in 128-bit case.
+					# it's done to avoid conditional
+					# branch.
+	stmg	$i2,$i3,$tweak($sp)	# "re-seat" the tweak value
+
+	.long	0xb92e0042		# km %r4,%r2
+	brc	1,.-4			# pay attention to "partial completion"
+
+	lrvg	$s0,$tweak+0($sp)	# load the last tweak
+	lrvg	$s1,$tweak+8($sp)
+	stmg	%r0,%r3,$tweak-32($sp)	# wipe copy of the key
+
+	nill	%r0,0xffdf		# switch back to original function code
+	la	%r1,0($key1)		# restore pointer to $key1
+	slgr	$out,$inp
+
+	llgc	$len,2*$SIZE_T-1($sp)
+	nill	$len,0x0f		# $len%=16
+	br	$ra
+	
+.align	16
+.Lxts_km_vanilla:
+___
+$code.=<<___;
+	# prepare and allocate stack frame at the top of 4K page
+	# with 1K reserved for eventual signal handling
+	lghi	$s0,-1024-256-16# guarantee at least 256-bytes buffer
+	lghi	$s1,-4096
+	algr	$s0,$sp
+	lgr	$fp,$sp
+	ngr	$s0,$s1		# align at page boundary
+	slgr	$fp,$s0		# total buffer size
+	lgr	$s2,$sp
+	lghi	$s1,1024+16	# sl[g]fi is extended-immediate facility
+	slgr	$fp,$s1		# deduct reservation to get usable buffer size
+	# buffer size is at lest 256 and at most 3072+256-16
+
+	la	$sp,1024($s0)	# alloca
+	nill	$fp,0xfff0	# round to 16*n
+	st${g}	$s2,0($sp)	# back-chain
+	nill	$len,0xfff0	# redundant
+	st${g}	$fp,$SIZE_T($sp)
+
+	slgr	$len,$fp
+	brc	1,.Lxts_km_go	# not zero, no borrow
+	algr	$fp,$len	# input is shorter than allocated buffer
+	lghi	$len,0
+	st${g}	$fp,$SIZE_T($sp)
+
+.Lxts_km_go:
+	lrvg	$s0,$tweak+0($s2)	# load the tweak value in little-endian
+	lrvg	$s1,$tweak+8($s2)
+
+	la	$s2,16($sp)		# vector of ascending tweak values
+	slgr	$s2,$inp
+	srlg	$s3,$fp,4
+	j	.Lxts_km_start
+
+.Lxts_km_loop:
+	la	$s2,16($sp)
+	slgr	$s2,$inp
+	srlg	$s3,$fp,4
+.Lxts_km_prepare:
+	lghi	$i1,0x87
+	srag	$i2,$s1,63		# broadcast upper bit
+	ngr	$i1,$i2			# rem
+	algr	$s0,$s0
+	alcgr	$s1,$s1
+	xgr	$s0,$i1
+.Lxts_km_start:
+	lrvgr	$i1,$s0			# flip byte order
+	lrvgr	$i2,$s1
+	stg	$i1,0($s2,$inp)
+	stg	$i2,8($s2,$inp)
+	xg	$i1,0($inp)
+	xg	$i2,8($inp)
+	stg	$i1,0($out,$inp)
+	stg	$i2,8($out,$inp)
+	la	$inp,16($inp)
+	brct	$s3,.Lxts_km_prepare
+
+	slgr	$inp,$fp		# rewind $inp
+	la	$s2,0($out,$inp)
+	lgr	$s3,$fp
+	.long	0xb92e00aa		# km $s2,$s2
+	brc	1,.-4			# pay attention to "partial completion"
+
+	la	$s2,16($sp)
+	slgr	$s2,$inp
+	srlg	$s3,$fp,4
+.Lxts_km_xor:
+	lg	$i1,0($out,$inp)
+	lg	$i2,8($out,$inp)
+	xg	$i1,0($s2,$inp)
+	xg	$i2,8($s2,$inp)
+	stg	$i1,0($out,$inp)
+	stg	$i2,8($out,$inp)
+	la	$inp,16($inp)
+	brct	$s3,.Lxts_km_xor
+
+	slgr	$len,$fp
+	brc	1,.Lxts_km_loop		# not zero, no borrow
+	algr	$fp,$len
+	lghi	$len,0
+	brc	4+1,.Lxts_km_loop	# not zero
+
+	l${g}	$i1,0($sp)		# back-chain
+	llgf	$fp,`2*$SIZE_T-4`($sp)	# bytes used
+	la	$i2,16($sp)
+	srlg	$fp,$fp,4
+.Lxts_km_zap:
+	stg	$i1,0($i2)
+	stg	$i1,8($i2)
+	la	$i2,16($i2)
+	brct	$fp,.Lxts_km_zap
+
+	la	$sp,0($i1)
+	llgc	$len,2*$SIZE_T-1($i1)
+	nill	$len,0x0f		# $len%=16
+	bzr	$ra
+
+	# generate one more tweak...
+	lghi	$i1,0x87
+	srag	$i2,$s1,63		# broadcast upper bit
+	ngr	$i1,$i2			# rem
+	algr	$s0,$s0
+	alcgr	$s1,$s1
+	xgr	$s0,$i1
+
+	ltr	$len,$len		# clear zero flag
+	br	$ra
+.size	_s390x_xts_km,.-_s390x_xts_km
+
+.globl	AES_xts_encrypt
+.type	AES_xts_encrypt,\@function
+.align	16
+AES_xts_encrypt:
+	xgr	%r3,%r4			# flip %r3 and %r4, $out and $len
+	xgr	%r4,%r3
+	xgr	%r3,%r4
+___
+$code.=<<___ if ($SIZE_T==4);
+	llgfr	$len,$len
+___
+$code.=<<___;
+	st${g}	$len,1*$SIZE_T($sp)	# save copy of $len
+	srag	$len,$len,4		# formally wrong, because it expands
+					# sign byte, but who can afford asking
+					# to process more than 2^63-1 bytes?
+					# I use it, because it sets condition
+					# code...
+	bcr	8,$ra			# abort if zero (i.e. less than 16)
+___
+$code.=<<___ if (!$softonly);
+	llgf	%r0,240($key2)
+	lhi	%r1,16
+	clr	%r0,%r1
+	jl	.Lxts_enc_software
+
+	st${g}	$ra,5*$SIZE_T($sp)
+	stm${g}	%r6,$s3,6*$SIZE_T($sp)
+
+	sllg	$len,$len,4		# $len&=~15
+	slgr	$out,$inp
+
+	# generate the tweak value
+	l${g}	$s3,$stdframe($sp)	# pointer to iv
+	la	$s2,$tweak($sp)
+	lmg	$s0,$s1,0($s3)
+	lghi	$s3,16
+	stmg	$s0,$s1,0($s2)
+	la	%r1,0($key2)		# $key2 is not needed anymore
+	.long	0xb92e00aa		# km $s2,$s2, generate the tweak
+	brc	1,.-4			# can this happen?
+
+	l	%r0,240($key1)
+	la	%r1,0($key1)		# $key1 is not needed anymore
+	bras	$ra,_s390x_xts_km
+	jz	.Lxts_enc_km_done
+
+	aghi	$inp,-16		# take one step back
+	la	$i3,0($out,$inp)	# put aside real $out
+.Lxts_enc_km_steal:
+	llgc	$i1,16($inp)
+	llgc	$i2,0($out,$inp)
+	stc	$i1,0($out,$inp)
+	stc	$i2,16($out,$inp)
+	la	$inp,1($inp)
+	brct	$len,.Lxts_enc_km_steal
+
+	la	$s2,0($i3)
+	lghi	$s3,16
+	lrvgr	$i1,$s0			# flip byte order
+	lrvgr	$i2,$s1
+	xg	$i1,0($s2)
+	xg	$i2,8($s2)
+	stg	$i1,0($s2)
+	stg	$i2,8($s2)
+	.long	0xb92e00aa		# km $s2,$s2
+	brc	1,.-4			# can this happen?
+	lrvgr	$i1,$s0			# flip byte order
+	lrvgr	$i2,$s1
+	xg	$i1,0($i3)
+	xg	$i2,8($i3)
+	stg	$i1,0($i3)
+	stg	$i2,8($i3)
+
+.Lxts_enc_km_done:
+	stg	$sp,$tweak+0($sp)	# wipe tweak
+	stg	$sp,$tweak+8($sp)
+	l${g}	$ra,5*$SIZE_T($sp)
+	lm${g}	%r6,$s3,6*$SIZE_T($sp)
+	br	$ra
+.align	16
+.Lxts_enc_software:
+___
+$code.=<<___;
+	stm${g}	%r6,$ra,6*$SIZE_T($sp)
+
+	slgr	$out,$inp
+
+	l${g}	$s3,$stdframe($sp)	# ivp
+	llgf	$s0,0($s3)		# load iv
+	llgf	$s1,4($s3)
+	llgf	$s2,8($s3)
+	llgf	$s3,12($s3)
+	stm${g}	%r2,%r5,2*$SIZE_T($sp)
+	la	$key,0($key2)
+	larl	$tbl,AES_Te
+	bras	$ra,_s390x_AES_encrypt	# generate the tweak
+	lm${g}	%r2,%r5,2*$SIZE_T($sp)
+	stm	$s0,$s3,$tweak($sp)	# save the tweak
+	j	.Lxts_enc_enter
+
+.align	16
+.Lxts_enc_loop:
+	lrvg	$s1,$tweak+0($sp)	# load the tweak in little-endian
+	lrvg	$s3,$tweak+8($sp)
+	lghi	%r1,0x87
+	srag	%r0,$s3,63		# broadcast upper bit
+	ngr	%r1,%r0			# rem
+	algr	$s1,$s1
+	alcgr	$s3,$s3
+	xgr	$s1,%r1
+	lrvgr	$s1,$s1			# flip byte order
+	lrvgr	$s3,$s3
+	srlg	$s0,$s1,32		# smash the tweak to 4x32-bits 
+	stg	$s1,$tweak+0($sp)	# save the tweak
+	llgfr	$s1,$s1
+	srlg	$s2,$s3,32
+	stg	$s3,$tweak+8($sp)
+	llgfr	$s3,$s3
+	la	$inp,16($inp)		# $inp+=16
+.Lxts_enc_enter:
+	x	$s0,0($inp)		# ^=*($inp)
+	x	$s1,4($inp)
+	x	$s2,8($inp)
+	x	$s3,12($inp)
+	stm${g}	%r2,%r3,2*$SIZE_T($sp)	# only two registers are changing
+	la	$key,0($key1)
+	bras	$ra,_s390x_AES_encrypt
+	lm${g}	%r2,%r5,2*$SIZE_T($sp)
+	x	$s0,$tweak+0($sp)	# ^=tweak
+	x	$s1,$tweak+4($sp)
+	x	$s2,$tweak+8($sp)
+	x	$s3,$tweak+12($sp)
+	st	$s0,0($out,$inp)
+	st	$s1,4($out,$inp)
+	st	$s2,8($out,$inp)
+	st	$s3,12($out,$inp)
+	brct${g}	$len,.Lxts_enc_loop
+
+	llgc	$len,`2*$SIZE_T-1`($sp)
+	nill	$len,0x0f		# $len%16
+	jz	.Lxts_enc_done
+
+	la	$i3,0($inp,$out)	# put aside real $out
+.Lxts_enc_steal:
+	llgc	%r0,16($inp)
+	llgc	%r1,0($out,$inp)
+	stc	%r0,0($out,$inp)
+	stc	%r1,16($out,$inp)
+	la	$inp,1($inp)
+	brct	$len,.Lxts_enc_steal
+	la	$out,0($i3)		# restore real $out
+
+	# generate last tweak...
+	lrvg	$s1,$tweak+0($sp)	# load the tweak in little-endian
+	lrvg	$s3,$tweak+8($sp)
+	lghi	%r1,0x87
+	srag	%r0,$s3,63		# broadcast upper bit
+	ngr	%r1,%r0			# rem
+	algr	$s1,$s1
+	alcgr	$s3,$s3
+	xgr	$s1,%r1
+	lrvgr	$s1,$s1			# flip byte order
+	lrvgr	$s3,$s3
+	srlg	$s0,$s1,32		# smash the tweak to 4x32-bits 
+	stg	$s1,$tweak+0($sp)	# save the tweak
+	llgfr	$s1,$s1
+	srlg	$s2,$s3,32
+	stg	$s3,$tweak+8($sp)
+	llgfr	$s3,$s3
+
+	x	$s0,0($out)		# ^=*(inp)|stolen cipther-text
+	x	$s1,4($out)
+	x	$s2,8($out)
+	x	$s3,12($out)
+	st${g}	$out,4*$SIZE_T($sp)
+	la	$key,0($key1)
+	bras	$ra,_s390x_AES_encrypt
+	l${g}	$out,4*$SIZE_T($sp)
+	x	$s0,`$tweak+0`($sp)	# ^=tweak
+	x	$s1,`$tweak+4`($sp)
+	x	$s2,`$tweak+8`($sp)
+	x	$s3,`$tweak+12`($sp)
+	st	$s0,0($out)
+	st	$s1,4($out)
+	st	$s2,8($out)
+	st	$s3,12($out)
+
+.Lxts_enc_done:
+	stg	$sp,$tweak+0($sp)	# wipe tweak
+	stg	$sp,$twesk+8($sp)
+	lm${g}	%r6,$ra,6*$SIZE_T($sp)
+	br	$ra
+.size	AES_xts_encrypt,.-AES_xts_encrypt
+___
+# void AES_xts_decrypt(const char *inp,char *out,size_t len,
+#	const AES_KEY *key1, const AES_KEY *key2,
+#	const unsigned char iv[16]);
+#
+$code.=<<___;
+.globl	AES_xts_decrypt
+.type	AES_xts_decrypt,\@function
+.align	16
+AES_xts_decrypt:
+	xgr	%r3,%r4			# flip %r3 and %r4, $out and $len
+	xgr	%r4,%r3
+	xgr	%r3,%r4
+___
+$code.=<<___ if ($SIZE_T==4);
+	llgfr	$len,$len
+___
+$code.=<<___;
+	st${g}	$len,1*$SIZE_T($sp)	# save copy of $len
+	aghi	$len,-16
+	bcr	4,$ra			# abort if less than zero. formally
+					# wrong, because $len is unsigned,
+					# but who can afford asking to
+					# process more than 2^63-1 bytes?
+	tmll	$len,0x0f
+	jnz	.Lxts_dec_proceed
+	aghi	$len,16
+.Lxts_dec_proceed:
+___
+$code.=<<___ if (!$softonly);
+	llgf	%r0,240($key2)
+	lhi	%r1,16
+	clr	%r0,%r1
+	jl	.Lxts_dec_software
+
+	st${g}	$ra,5*$SIZE_T($sp)
+	stm${g}	%r6,$s3,6*$SIZE_T($sp)
+
+	nill	$len,0xfff0		# $len&=~15
+	slgr	$out,$inp
+
+	# generate the tweak value
+	l${g}	$s3,$stdframe($sp)	# pointer to iv
+	la	$s2,$tweak($sp)
+	lmg	$s0,$s1,0($s3)
+	lghi	$s3,16
+	stmg	$s0,$s1,0($s2)
+	la	%r1,0($key2)		# $key2 is not needed past this point
+	.long	0xb92e00aa		# km $s2,$s2, generate the tweak
+	brc	1,.-4			# can this happen?
+
+	l	%r0,240($key1)
+	la	%r1,0($key1)		# $key1 is not needed anymore
+
+	ltgr	$len,$len
+	jz	.Lxts_dec_km_short
+	bras	$ra,_s390x_xts_km
+	jz	.Lxts_dec_km_done
+
+	lrvgr	$s2,$s0			# make copy in reverse byte order
+	lrvgr	$s3,$s1
+	j	.Lxts_dec_km_2ndtweak
+
+.Lxts_dec_km_short:
+	llgc	$len,`2*$SIZE_T-1`($sp)
+	nill	$len,0x0f		# $len%=16
+	lrvg	$s0,$tweak+0($sp)	# load the tweak
+	lrvg	$s1,$tweak+8($sp)
+	lrvgr	$s2,$s0			# make copy in reverse byte order
+	lrvgr	$s3,$s1
+
+.Lxts_dec_km_2ndtweak:
+	lghi	$i1,0x87
+	srag	$i2,$s1,63		# broadcast upper bit
+	ngr	$i1,$i2			# rem
+	algr	$s0,$s0
+	alcgr	$s1,$s1
+	xgr	$s0,$i1
+	lrvgr	$i1,$s0			# flip byte order
+	lrvgr	$i2,$s1
+
+	xg	$i1,0($inp)
+	xg	$i2,8($inp)
+	stg	$i1,0($out,$inp)
+	stg	$i2,8($out,$inp)
+	la	$i2,0($out,$inp)
+	lghi	$i3,16
+	.long	0xb92e0066		# km $i2,$i2
+	brc	1,.-4			# can this happen?
+	lrvgr	$i1,$s0
+	lrvgr	$i2,$s1
+	xg	$i1,0($out,$inp)
+	xg	$i2,8($out,$inp)
+	stg	$i1,0($out,$inp)
+	stg	$i2,8($out,$inp)
+
+	la	$i3,0($out,$inp)	# put aside real $out
+.Lxts_dec_km_steal:
+	llgc	$i1,16($inp)
+	llgc	$i2,0($out,$inp)
+	stc	$i1,0($out,$inp)
+	stc	$i2,16($out,$inp)
+	la	$inp,1($inp)
+	brct	$len,.Lxts_dec_km_steal
+
+	lgr	$s0,$s2
+	lgr	$s1,$s3
+	xg	$s0,0($i3)
+	xg	$s1,8($i3)
+	stg	$s0,0($i3)
+	stg	$s1,8($i3)
+	la	$s0,0($i3)
+	lghi	$s1,16
+	.long	0xb92e0088		# km $s0,$s0
+	brc	1,.-4			# can this happen?
+	xg	$s2,0($i3)
+	xg	$s3,8($i3)
+	stg	$s2,0($i3)
+	stg	$s3,8($i3)
+.Lxts_dec_km_done:
+	stg	$sp,$tweak+0($sp)	# wipe tweak
+	stg	$sp,$tweak+8($sp)
+	l${g}	$ra,5*$SIZE_T($sp)
+	lm${g}	%r6,$s3,6*$SIZE_T($sp)
+	br	$ra
+.align	16
+.Lxts_dec_software:
+___
+$code.=<<___;
+	stm${g}	%r6,$ra,6*$SIZE_T($sp)
+
+	srlg	$len,$len,4
+	slgr	$out,$inp
+
+	l${g}	$s3,$stdframe($sp)	# ivp
+	llgf	$s0,0($s3)		# load iv
+	llgf	$s1,4($s3)
+	llgf	$s2,8($s3)
+	llgf	$s3,12($s3)
+	stm${g}	%r2,%r5,2*$SIZE_T($sp)
+	la	$key,0($key2)
+	larl	$tbl,AES_Te
+	bras	$ra,_s390x_AES_encrypt	# generate the tweak
+	lm${g}	%r2,%r5,2*$SIZE_T($sp)
+	larl	$tbl,AES_Td
+	lt${g}r	$len,$len
+	stm	$s0,$s3,$tweak($sp)	# save the tweak
+	jz	.Lxts_dec_short
+	j	.Lxts_dec_enter
+
+.align	16
+.Lxts_dec_loop:
+	lrvg	$s1,$tweak+0($sp)	# load the tweak in little-endian
+	lrvg	$s3,$tweak+8($sp)
+	lghi	%r1,0x87
+	srag	%r0,$s3,63		# broadcast upper bit
+	ngr	%r1,%r0			# rem
+	algr	$s1,$s1
+	alcgr	$s3,$s3
+	xgr	$s1,%r1
+	lrvgr	$s1,$s1			# flip byte order
+	lrvgr	$s3,$s3
+	srlg	$s0,$s1,32		# smash the tweak to 4x32-bits 
+	stg	$s1,$tweak+0($sp)	# save the tweak
+	llgfr	$s1,$s1
+	srlg	$s2,$s3,32
+	stg	$s3,$tweak+8($sp)
+	llgfr	$s3,$s3
+.Lxts_dec_enter:
+	x	$s0,0($inp)		# tweak^=*(inp)
+	x	$s1,4($inp)
+	x	$s2,8($inp)
+	x	$s3,12($inp)
+	stm${g}	%r2,%r3,2*$SIZE_T($sp)	# only two registers are changing
+	la	$key,0($key1)
+	bras	$ra,_s390x_AES_decrypt
+	lm${g}	%r2,%r5,2*$SIZE_T($sp)
+	x	$s0,$tweak+0($sp)	# ^=tweak
+	x	$s1,$tweak+4($sp)
+	x	$s2,$tweak+8($sp)
+	x	$s3,$tweak+12($sp)
+	st	$s0,0($out,$inp)
+	st	$s1,4($out,$inp)
+	st	$s2,8($out,$inp)
+	st	$s3,12($out,$inp)
+	la	$inp,16($inp)
+	brct${g}	$len,.Lxts_dec_loop
+
+	llgc	$len,`2*$SIZE_T-1`($sp)
+	nill	$len,0x0f		# $len%16
+	jz	.Lxts_dec_done
+
+	# generate pair of tweaks...
+	lrvg	$s1,$tweak+0($sp)	# load the tweak in little-endian
+	lrvg	$s3,$tweak+8($sp)
+	lghi	%r1,0x87
+	srag	%r0,$s3,63		# broadcast upper bit
+	ngr	%r1,%r0			# rem
+	algr	$s1,$s1
+	alcgr	$s3,$s3
+	xgr	$s1,%r1
+	lrvgr	$i2,$s1			# flip byte order
+	lrvgr	$i3,$s3
+	stmg	$i2,$i3,$tweak($sp)	# save the 1st tweak
+	j	.Lxts_dec_2ndtweak
+
+.align	16
+.Lxts_dec_short:
+	llgc	$len,`2*$SIZE_T-1`($sp)
+	nill	$len,0x0f		# $len%16
+	lrvg	$s1,$tweak+0($sp)	# load the tweak in little-endian
+	lrvg	$s3,$tweak+8($sp)
+.Lxts_dec_2ndtweak:
+	lghi	%r1,0x87
+	srag	%r0,$s3,63		# broadcast upper bit
+	ngr	%r1,%r0			# rem
+	algr	$s1,$s1
+	alcgr	$s3,$s3
+	xgr	$s1,%r1
+	lrvgr	$s1,$s1			# flip byte order
+	lrvgr	$s3,$s3
+	srlg	$s0,$s1,32		# smash the tweak to 4x32-bits
+	stg	$s1,$tweak-16+0($sp)	# save the 2nd tweak
+	llgfr	$s1,$s1
+	srlg	$s2,$s3,32
+	stg	$s3,$tweak-16+8($sp)
+	llgfr	$s3,$s3
+
+	x	$s0,0($inp)		# tweak_the_2nd^=*(inp)
+	x	$s1,4($inp)
+	x	$s2,8($inp)
+	x	$s3,12($inp)
+	stm${g}	%r2,%r3,2*$SIZE_T($sp)
+	la	$key,0($key1)
+	bras	$ra,_s390x_AES_decrypt
+	lm${g}	%r2,%r5,2*$SIZE_T($sp)
+	x	$s0,$tweak-16+0($sp)	# ^=tweak_the_2nd
+	x	$s1,$tweak-16+4($sp)
+	x	$s2,$tweak-16+8($sp)
+	x	$s3,$tweak-16+12($sp)
+	st	$s0,0($out,$inp)
+	st	$s1,4($out,$inp)
+	st	$s2,8($out,$inp)
+	st	$s3,12($out,$inp)
+
+	la	$i3,0($out,$inp)	# put aside real $out
+.Lxts_dec_steal:
+	llgc	%r0,16($inp)
+	llgc	%r1,0($out,$inp)
+	stc	%r0,0($out,$inp)
+	stc	%r1,16($out,$inp)
+	la	$inp,1($inp)
+	brct	$len,.Lxts_dec_steal
+	la	$out,0($i3)		# restore real $out
+
+	lm	$s0,$s3,$tweak($sp)	# load the 1st tweak
+	x	$s0,0($out)		# tweak^=*(inp)|stolen cipher-text
+	x	$s1,4($out)
+	x	$s2,8($out)
+	x	$s3,12($out)
+	st${g}	$out,4*$SIZE_T($sp)
+	la	$key,0($key1)
+	bras	$ra,_s390x_AES_decrypt
+	l${g}	$out,4*$SIZE_T($sp)
+	x	$s0,$tweak+0($sp)	# ^=tweak
+	x	$s1,$tweak+4($sp)
+	x	$s2,$tweak+8($sp)
+	x	$s3,$tweak+12($sp)
+	st	$s0,0($out)
+	st	$s1,4($out)
+	st	$s2,8($out)
+	st	$s3,12($out)
+	stg	$sp,$tweak-16+0($sp)	# wipe 2nd tweak
+	stg	$sp,$tweak-16+8($sp)
+.Lxts_dec_done:
+	stg	$sp,$tweak+0($sp)	# wipe tweak
+	stg	$sp,$twesk+8($sp)
+	lm${g}	%r6,$ra,6*$SIZE_T($sp)
+	br	$ra
+.size	AES_xts_decrypt,.-AES_xts_decrypt
+___
+}
+$code.=<<___;
+.string	"AES for s390x, CRYPTOGAMS by "
+.comm	OPENSSL_s390xcap_P,80,8
+___
+
+$code =~ s/\`([^\`]*)\`/eval $1/gem;
+print $code;
+close STDOUT;	# force flush
diff --git a/crypto/aes/asm/aes-sparcv9.pl b/crypto/aes/asm/aes-sparcv9.pl
new file mode 100755
index 000000000..883fae820
--- /dev/null
+++ b/crypto/aes/asm/aes-sparcv9.pl
@@ -0,0 +1,1192 @@
+#! /usr/bin/env perl
+# Copyright 2005-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+#
+# ====================================================================
+# Written by Andy Polyakov  for the OpenSSL
+# project. Rights for redistribution and usage in source and binary
+# forms are granted according to the OpenSSL license.
+# ====================================================================
+#
+# Version 1.1
+#
+# The major reason for undertaken effort was to mitigate the hazard of
+# cache-timing attack. This is [currently and initially!] addressed in
+# two ways. 1. S-boxes are compressed from 5KB to 2KB+256B size each.
+# 2. References to them are scheduled for L2 cache latency, meaning
+# that the tables don't have to reside in L1 cache. Once again, this
+# is an initial draft and one should expect more countermeasures to
+# be implemented...
+#
+# Version 1.1 prefetches T[ed]4 in order to mitigate attack on last
+# round.
+#
+# Even though performance was not the primary goal [on the contrary,
+# extra shifts "induced" by compressed S-box and longer loop epilogue
+# "induced" by scheduling for L2 have negative effect on performance],
+# the code turned out to run in ~23 cycles per processed byte en-/
+# decrypted with 128-bit key. This is pretty good result for code
+# with mentioned qualities and UltraSPARC core. Compared to Sun C
+# generated code my encrypt procedure runs just few percents faster,
+# while decrypt one - whole 50% faster [yes, Sun C failed to generate
+# optimal decrypt procedure]. Compared to GNU C generated code both
+# procedures are more than 60% faster:-)
+
+$output = pop;
+open STDOUT,">$output";
+
+$frame="STACK_FRAME";
+$bias="STACK_BIAS";
+$locals=16;
+
+$acc0="%l0";
+$acc1="%o0";
+$acc2="%o1";
+$acc3="%o2";
+
+$acc4="%l1";
+$acc5="%o3";
+$acc6="%o4";
+$acc7="%o5";
+
+$acc8="%l2";
+$acc9="%o7";
+$acc10="%g1";
+$acc11="%g2";
+
+$acc12="%l3";
+$acc13="%g3";
+$acc14="%g4";
+$acc15="%g5";
+
+$t0="%l4";
+$t1="%l5";
+$t2="%l6";
+$t3="%l7";
+
+$s0="%i0";
+$s1="%i1";
+$s2="%i2";
+$s3="%i3";
+$tbl="%i4";
+$key="%i5";
+$rounds="%i7";	# aliases with return address, which is off-loaded to stack
+
+sub _data_word()
+{ my $i;
+    while(defined($i=shift)) { $code.=sprintf"\t.long\t0x%08x,0x%08x\n",$i,$i; }
+}
+
+$code.=<<___;
+#include "sparc_arch.h"
+
+#ifdef  __arch64__
+.register	%g2,#scratch
+.register	%g3,#scratch
+#endif
+.section	".text",#alloc,#execinstr
+
+.align	256
+AES_Te:
+___
+&_data_word(
+	0xc66363a5, 0xf87c7c84, 0xee777799, 0xf67b7b8d,
+	0xfff2f20d, 0xd66b6bbd, 0xde6f6fb1, 0x91c5c554,
+	0x60303050, 0x02010103, 0xce6767a9, 0x562b2b7d,
+	0xe7fefe19, 0xb5d7d762, 0x4dababe6, 0xec76769a,
+	0x8fcaca45, 0x1f82829d, 0x89c9c940, 0xfa7d7d87,
+	0xeffafa15, 0xb25959eb, 0x8e4747c9, 0xfbf0f00b,
+	0x41adadec, 0xb3d4d467, 0x5fa2a2fd, 0x45afafea,
+	0x239c9cbf, 0x53a4a4f7, 0xe4727296, 0x9bc0c05b,
+	0x75b7b7c2, 0xe1fdfd1c, 0x3d9393ae, 0x4c26266a,
+	0x6c36365a, 0x7e3f3f41, 0xf5f7f702, 0x83cccc4f,
+	0x6834345c, 0x51a5a5f4, 0xd1e5e534, 0xf9f1f108,
+	0xe2717193, 0xabd8d873, 0x62313153, 0x2a15153f,
+	0x0804040c, 0x95c7c752, 0x46232365, 0x9dc3c35e,
+	0x30181828, 0x379696a1, 0x0a05050f, 0x2f9a9ab5,
+	0x0e070709, 0x24121236, 0x1b80809b, 0xdfe2e23d,
+	0xcdebeb26, 0x4e272769, 0x7fb2b2cd, 0xea75759f,
+	0x1209091b, 0x1d83839e, 0x582c2c74, 0x341a1a2e,
+	0x361b1b2d, 0xdc6e6eb2, 0xb45a5aee, 0x5ba0a0fb,
+	0xa45252f6, 0x763b3b4d, 0xb7d6d661, 0x7db3b3ce,
+	0x5229297b, 0xdde3e33e, 0x5e2f2f71, 0x13848497,
+	0xa65353f5, 0xb9d1d168, 0x00000000, 0xc1eded2c,
+	0x40202060, 0xe3fcfc1f, 0x79b1b1c8, 0xb65b5bed,
+	0xd46a6abe, 0x8dcbcb46, 0x67bebed9, 0x7239394b,
+	0x944a4ade, 0x984c4cd4, 0xb05858e8, 0x85cfcf4a,
+	0xbbd0d06b, 0xc5efef2a, 0x4faaaae5, 0xedfbfb16,
+	0x864343c5, 0x9a4d4dd7, 0x66333355, 0x11858594,
+	0x8a4545cf, 0xe9f9f910, 0x04020206, 0xfe7f7f81,
+	0xa05050f0, 0x783c3c44, 0x259f9fba, 0x4ba8a8e3,
+	0xa25151f3, 0x5da3a3fe, 0x804040c0, 0x058f8f8a,
+	0x3f9292ad, 0x219d9dbc, 0x70383848, 0xf1f5f504,
+	0x63bcbcdf, 0x77b6b6c1, 0xafdada75, 0x42212163,
+	0x20101030, 0xe5ffff1a, 0xfdf3f30e, 0xbfd2d26d,
+	0x81cdcd4c, 0x180c0c14, 0x26131335, 0xc3ecec2f,
+	0xbe5f5fe1, 0x359797a2, 0x884444cc, 0x2e171739,
+	0x93c4c457, 0x55a7a7f2, 0xfc7e7e82, 0x7a3d3d47,
+	0xc86464ac, 0xba5d5de7, 0x3219192b, 0xe6737395,
+	0xc06060a0, 0x19818198, 0x9e4f4fd1, 0xa3dcdc7f,
+	0x44222266, 0x542a2a7e, 0x3b9090ab, 0x0b888883,
+	0x8c4646ca, 0xc7eeee29, 0x6bb8b8d3, 0x2814143c,
+	0xa7dede79, 0xbc5e5ee2, 0x160b0b1d, 0xaddbdb76,
+	0xdbe0e03b, 0x64323256, 0x743a3a4e, 0x140a0a1e,
+	0x924949db, 0x0c06060a, 0x4824246c, 0xb85c5ce4,
+	0x9fc2c25d, 0xbdd3d36e, 0x43acacef, 0xc46262a6,
+	0x399191a8, 0x319595a4, 0xd3e4e437, 0xf279798b,
+	0xd5e7e732, 0x8bc8c843, 0x6e373759, 0xda6d6db7,
+	0x018d8d8c, 0xb1d5d564, 0x9c4e4ed2, 0x49a9a9e0,
+	0xd86c6cb4, 0xac5656fa, 0xf3f4f407, 0xcfeaea25,
+	0xca6565af, 0xf47a7a8e, 0x47aeaee9, 0x10080818,
+	0x6fbabad5, 0xf0787888, 0x4a25256f, 0x5c2e2e72,
+	0x381c1c24, 0x57a6a6f1, 0x73b4b4c7, 0x97c6c651,
+	0xcbe8e823, 0xa1dddd7c, 0xe874749c, 0x3e1f1f21,
+	0x964b4bdd, 0x61bdbddc, 0x0d8b8b86, 0x0f8a8a85,
+	0xe0707090, 0x7c3e3e42, 0x71b5b5c4, 0xcc6666aa,
+	0x904848d8, 0x06030305, 0xf7f6f601, 0x1c0e0e12,
+	0xc26161a3, 0x6a35355f, 0xae5757f9, 0x69b9b9d0,
+	0x17868691, 0x99c1c158, 0x3a1d1d27, 0x279e9eb9,
+	0xd9e1e138, 0xebf8f813, 0x2b9898b3, 0x22111133,
+	0xd26969bb, 0xa9d9d970, 0x078e8e89, 0x339494a7,
+	0x2d9b9bb6, 0x3c1e1e22, 0x15878792, 0xc9e9e920,
+	0x87cece49, 0xaa5555ff, 0x50282878, 0xa5dfdf7a,
+	0x038c8c8f, 0x59a1a1f8, 0x09898980, 0x1a0d0d17,
+	0x65bfbfda, 0xd7e6e631, 0x844242c6, 0xd06868b8,
+	0x824141c3, 0x299999b0, 0x5a2d2d77, 0x1e0f0f11,
+	0x7bb0b0cb, 0xa85454fc, 0x6dbbbbd6, 0x2c16163a);
+$code.=<<___;
+	.byte	0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5
+	.byte	0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76
+	.byte	0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0
+	.byte	0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0
+	.byte	0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc
+	.byte	0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15
+	.byte	0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a
+	.byte	0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75
+	.byte	0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0
+	.byte	0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84
+	.byte	0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b
+	.byte	0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf
+	.byte	0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85
+	.byte	0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8
+	.byte	0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5
+	.byte	0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2
+	.byte	0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17
+	.byte	0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73
+	.byte	0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88
+	.byte	0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb
+	.byte	0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c
+	.byte	0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79
+	.byte	0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9
+	.byte	0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08
+	.byte	0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6
+	.byte	0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a
+	.byte	0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e
+	.byte	0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e
+	.byte	0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94
+	.byte	0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf
+	.byte	0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68
+	.byte	0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16
+.type	AES_Te,#object
+.size	AES_Te,(.-AES_Te)
+
+.align	64
+.skip	16
+_sparcv9_AES_encrypt:
+	save	%sp,-$frame-$locals,%sp
+	stx	%i7,[%sp+$bias+$frame+0]	! off-load return address
+	ld	[$key+240],$rounds
+	ld	[$key+0],$t0
+	ld	[$key+4],$t1			!
+	ld	[$key+8],$t2
+	srl	$rounds,1,$rounds
+	xor	$t0,$s0,$s0
+	ld	[$key+12],$t3
+	srl	$s0,21,$acc0
+	xor	$t1,$s1,$s1
+	ld	[$key+16],$t0
+	srl	$s1,13,$acc1			!
+	xor	$t2,$s2,$s2
+	ld	[$key+20],$t1
+	xor	$t3,$s3,$s3
+	ld	[$key+24],$t2
+	and	$acc0,2040,$acc0
+	ld	[$key+28],$t3
+	nop
+.Lenc_loop:
+	srl	$s2,5,$acc2			!
+	and	$acc1,2040,$acc1
+	ldx	[$tbl+$acc0],$acc0
+	sll	$s3,3,$acc3
+	and	$acc2,2040,$acc2
+	ldx	[$tbl+$acc1],$acc1
+	srl	$s1,21,$acc4
+	and	$acc3,2040,$acc3
+	ldx	[$tbl+$acc2],$acc2		!
+	srl	$s2,13,$acc5
+	and	$acc4,2040,$acc4
+	ldx	[$tbl+$acc3],$acc3
+	srl	$s3,5,$acc6
+	and	$acc5,2040,$acc5
+	ldx	[$tbl+$acc4],$acc4
+	fmovs	%f0,%f0
+	sll	$s0,3,$acc7			!
+	and	$acc6,2040,$acc6
+	ldx	[$tbl+$acc5],$acc5
+	srl	$s2,21,$acc8
+	and	$acc7,2040,$acc7
+	ldx	[$tbl+$acc6],$acc6
+	srl	$s3,13,$acc9
+	and	$acc8,2040,$acc8
+	ldx	[$tbl+$acc7],$acc7		!
+	srl	$s0,5,$acc10
+	and	$acc9,2040,$acc9
+	ldx	[$tbl+$acc8],$acc8
+	sll	$s1,3,$acc11
+	and	$acc10,2040,$acc10
+	ldx	[$tbl+$acc9],$acc9
+	fmovs	%f0,%f0
+	srl	$s3,21,$acc12			!
+	and	$acc11,2040,$acc11
+	ldx	[$tbl+$acc10],$acc10
+	srl	$s0,13,$acc13
+	and	$acc12,2040,$acc12
+	ldx	[$tbl+$acc11],$acc11
+	srl	$s1,5,$acc14
+	and	$acc13,2040,$acc13
+	ldx	[$tbl+$acc12],$acc12		!
+	sll	$s2,3,$acc15
+	and	$acc14,2040,$acc14
+	ldx	[$tbl+$acc13],$acc13
+	and	$acc15,2040,$acc15
+	add	$key,32,$key
+	ldx	[$tbl+$acc14],$acc14
+	fmovs	%f0,%f0
+	subcc	$rounds,1,$rounds		!
+	ldx	[$tbl+$acc15],$acc15
+	bz,a,pn	%icc,.Lenc_last
+	add	$tbl,2048,$rounds
+
+		srlx	$acc1,8,$acc1
+		xor	$acc0,$t0,$t0
+	ld	[$key+0],$s0
+	fmovs	%f0,%f0
+		srlx	$acc2,16,$acc2		!
+		xor	$acc1,$t0,$t0
+	ld	[$key+4],$s1
+		srlx	$acc3,24,$acc3
+		xor	$acc2,$t0,$t0
+	ld	[$key+8],$s2
+		srlx	$acc5,8,$acc5
+		xor	$acc3,$t0,$t0
+	ld	[$key+12],$s3			!
+		srlx	$acc6,16,$acc6
+		xor	$acc4,$t1,$t1
+	fmovs	%f0,%f0
+		srlx	$acc7,24,$acc7
+		xor	$acc5,$t1,$t1
+		srlx	$acc9,8,$acc9
+		xor	$acc6,$t1,$t1
+		srlx	$acc10,16,$acc10	!
+		xor	$acc7,$t1,$t1
+		srlx	$acc11,24,$acc11
+		xor	$acc8,$t2,$t2
+		srlx	$acc13,8,$acc13
+		xor	$acc9,$t2,$t2
+		srlx	$acc14,16,$acc14
+		xor	$acc10,$t2,$t2
+		srlx	$acc15,24,$acc15	!
+		xor	$acc11,$t2,$t2
+		xor	$acc12,$acc14,$acc14
+		xor	$acc13,$t3,$t3
+	srl	$t0,21,$acc0
+		xor	$acc14,$t3,$t3
+	srl	$t1,13,$acc1
+		xor	$acc15,$t3,$t3
+
+	and	$acc0,2040,$acc0		!
+	srl	$t2,5,$acc2
+	and	$acc1,2040,$acc1
+	ldx	[$tbl+$acc0],$acc0
+	sll	$t3,3,$acc3
+	and	$acc2,2040,$acc2
+	ldx	[$tbl+$acc1],$acc1
+	fmovs	%f0,%f0
+	srl	$t1,21,$acc4			!
+	and	$acc3,2040,$acc3
+	ldx	[$tbl+$acc2],$acc2
+	srl	$t2,13,$acc5
+	and	$acc4,2040,$acc4
+	ldx	[$tbl+$acc3],$acc3
+	srl	$t3,5,$acc6
+	and	$acc5,2040,$acc5
+	ldx	[$tbl+$acc4],$acc4		!
+	sll	$t0,3,$acc7
+	and	$acc6,2040,$acc6
+	ldx	[$tbl+$acc5],$acc5
+	srl	$t2,21,$acc8
+	and	$acc7,2040,$acc7
+	ldx	[$tbl+$acc6],$acc6
+	fmovs	%f0,%f0
+	srl	$t3,13,$acc9			!
+	and	$acc8,2040,$acc8
+	ldx	[$tbl+$acc7],$acc7
+	srl	$t0,5,$acc10
+	and	$acc9,2040,$acc9
+	ldx	[$tbl+$acc8],$acc8
+	sll	$t1,3,$acc11
+	and	$acc10,2040,$acc10
+	ldx	[$tbl+$acc9],$acc9		!
+	srl	$t3,21,$acc12
+	and	$acc11,2040,$acc11
+	ldx	[$tbl+$acc10],$acc10
+	srl	$t0,13,$acc13
+	and	$acc12,2040,$acc12
+	ldx	[$tbl+$acc11],$acc11
+	fmovs	%f0,%f0
+	srl	$t1,5,$acc14			!
+	and	$acc13,2040,$acc13
+	ldx	[$tbl+$acc12],$acc12
+	sll	$t2,3,$acc15
+	and	$acc14,2040,$acc14
+	ldx	[$tbl+$acc13],$acc13
+		srlx	$acc1,8,$acc1
+	and	$acc15,2040,$acc15
+	ldx	[$tbl+$acc14],$acc14		!
+
+		srlx	$acc2,16,$acc2
+		xor	$acc0,$s0,$s0
+	ldx	[$tbl+$acc15],$acc15
+		srlx	$acc3,24,$acc3
+		xor	$acc1,$s0,$s0
+	ld	[$key+16],$t0
+	fmovs	%f0,%f0
+		srlx	$acc5,8,$acc5		!
+		xor	$acc2,$s0,$s0
+	ld	[$key+20],$t1
+		srlx	$acc6,16,$acc6
+		xor	$acc3,$s0,$s0
+	ld	[$key+24],$t2
+		srlx	$acc7,24,$acc7
+		xor	$acc4,$s1,$s1
+	ld	[$key+28],$t3			!
+		srlx	$acc9,8,$acc9
+		xor	$acc5,$s1,$s1
+	ldx	[$tbl+2048+0],%g0		! prefetch te4
+		srlx	$acc10,16,$acc10
+		xor	$acc6,$s1,$s1
+	ldx	[$tbl+2048+32],%g0		! prefetch te4
+		srlx	$acc11,24,$acc11
+		xor	$acc7,$s1,$s1
+	ldx	[$tbl+2048+64],%g0		! prefetch te4
+		srlx	$acc13,8,$acc13
+		xor	$acc8,$s2,$s2
+	ldx	[$tbl+2048+96],%g0		! prefetch te4
+		srlx	$acc14,16,$acc14	!
+		xor	$acc9,$s2,$s2
+	ldx	[$tbl+2048+128],%g0		! prefetch te4
+		srlx	$acc15,24,$acc15
+		xor	$acc10,$s2,$s2
+	ldx	[$tbl+2048+160],%g0		! prefetch te4
+	srl	$s0,21,$acc0
+		xor	$acc11,$s2,$s2
+	ldx	[$tbl+2048+192],%g0		! prefetch te4
+		xor	$acc12,$acc14,$acc14
+		xor	$acc13,$s3,$s3
+	ldx	[$tbl+2048+224],%g0		! prefetch te4
+	srl	$s1,13,$acc1			!
+		xor	$acc14,$s3,$s3
+		xor	$acc15,$s3,$s3
+	ba	.Lenc_loop
+	and	$acc0,2040,$acc0
+
+.align	32
+.Lenc_last:
+		srlx	$acc1,8,$acc1		!
+		xor	$acc0,$t0,$t0
+	ld	[$key+0],$s0
+		srlx	$acc2,16,$acc2
+		xor	$acc1,$t0,$t0
+	ld	[$key+4],$s1
+		srlx	$acc3,24,$acc3
+		xor	$acc2,$t0,$t0
+	ld	[$key+8],$s2			!
+		srlx	$acc5,8,$acc5
+		xor	$acc3,$t0,$t0
+	ld	[$key+12],$s3
+		srlx	$acc6,16,$acc6
+		xor	$acc4,$t1,$t1
+		srlx	$acc7,24,$acc7
+		xor	$acc5,$t1,$t1
+		srlx	$acc9,8,$acc9		!
+		xor	$acc6,$t1,$t1
+		srlx	$acc10,16,$acc10
+		xor	$acc7,$t1,$t1
+		srlx	$acc11,24,$acc11
+		xor	$acc8,$t2,$t2
+		srlx	$acc13,8,$acc13
+		xor	$acc9,$t2,$t2
+		srlx	$acc14,16,$acc14	!
+		xor	$acc10,$t2,$t2
+		srlx	$acc15,24,$acc15
+		xor	$acc11,$t2,$t2
+		xor	$acc12,$acc14,$acc14
+		xor	$acc13,$t3,$t3
+	srl	$t0,24,$acc0
+		xor	$acc14,$t3,$t3
+	srl	$t1,16,$acc1			!
+		xor	$acc15,$t3,$t3
+
+	srl	$t2,8,$acc2
+	and	$acc1,255,$acc1
+	ldub	[$rounds+$acc0],$acc0
+	srl	$t1,24,$acc4
+	and	$acc2,255,$acc2
+	ldub	[$rounds+$acc1],$acc1
+	srl	$t2,16,$acc5			!
+	and	$t3,255,$acc3
+	ldub	[$rounds+$acc2],$acc2
+	ldub	[$rounds+$acc3],$acc3
+	srl	$t3,8,$acc6
+	and	$acc5,255,$acc5
+	ldub	[$rounds+$acc4],$acc4
+	fmovs	%f0,%f0
+	srl	$t2,24,$acc8			!
+	and	$acc6,255,$acc6
+	ldub	[$rounds+$acc5],$acc5
+	srl	$t3,16,$acc9
+	and	$t0,255,$acc7
+	ldub	[$rounds+$acc6],$acc6
+	ldub	[$rounds+$acc7],$acc7
+	fmovs	%f0,%f0
+	srl	$t0,8,$acc10			!
+	and	$acc9,255,$acc9
+	ldub	[$rounds+$acc8],$acc8
+	srl	$t3,24,$acc12
+	and	$acc10,255,$acc10
+	ldub	[$rounds+$acc9],$acc9
+	srl	$t0,16,$acc13
+	and	$t1,255,$acc11
+	ldub	[$rounds+$acc10],$acc10		!
+	srl	$t1,8,$acc14
+	and	$acc13,255,$acc13
+	ldub	[$rounds+$acc11],$acc11
+	ldub	[$rounds+$acc12],$acc12
+	and	$acc14,255,$acc14
+	ldub	[$rounds+$acc13],$acc13
+	and	$t2,255,$acc15
+	ldub	[$rounds+$acc14],$acc14		!
+
+		sll	$acc0,24,$acc0
+		xor	$acc3,$s0,$s0
+	ldub	[$rounds+$acc15],$acc15
+		sll	$acc1,16,$acc1
+		xor	$acc0,$s0,$s0
+	ldx	[%sp+$bias+$frame+0],%i7	! restore return address
+	fmovs	%f0,%f0
+		sll	$acc2,8,$acc2		!
+		xor	$acc1,$s0,$s0
+		sll	$acc4,24,$acc4
+		xor	$acc2,$s0,$s0
+		sll	$acc5,16,$acc5
+		xor	$acc7,$s1,$s1
+		sll	$acc6,8,$acc6
+		xor	$acc4,$s1,$s1
+		sll	$acc8,24,$acc8		!
+		xor	$acc5,$s1,$s1
+		sll	$acc9,16,$acc9
+		xor	$acc11,$s2,$s2
+		sll	$acc10,8,$acc10
+		xor	$acc6,$s1,$s1
+		sll	$acc12,24,$acc12
+		xor	$acc8,$s2,$s2
+		sll	$acc13,16,$acc13	!
+		xor	$acc9,$s2,$s2
+		sll	$acc14,8,$acc14
+		xor	$acc10,$s2,$s2
+		xor	$acc12,$acc14,$acc14
+		xor	$acc13,$s3,$s3
+		xor	$acc14,$s3,$s3
+		xor	$acc15,$s3,$s3
+
+	ret
+	restore
+.type	_sparcv9_AES_encrypt,#function
+.size	_sparcv9_AES_encrypt,(.-_sparcv9_AES_encrypt)
+
+.align	32
+.globl	AES_encrypt
+AES_encrypt:
+	or	%o0,%o1,%g1
+	andcc	%g1,3,%g0
+	bnz,pn	%xcc,.Lunaligned_enc
+	save	%sp,-$frame,%sp
+
+	ld	[%i0+0],%o0
+	ld	[%i0+4],%o1
+	ld	[%i0+8],%o2
+	ld	[%i0+12],%o3
+
+1:	call	.+8
+	add	%o7,AES_Te-1b,%o4
+	call	_sparcv9_AES_encrypt
+	mov	%i2,%o5
+
+	st	%o0,[%i1+0]
+	st	%o1,[%i1+4]
+	st	%o2,[%i1+8]
+	st	%o3,[%i1+12]
+
+	ret
+	restore
+
+.align	32
+.Lunaligned_enc:
+	ldub	[%i0+0],%l0
+	ldub	[%i0+1],%l1
+	ldub	[%i0+2],%l2
+
+	sll	%l0,24,%l0
+	ldub	[%i0+3],%l3
+	sll	%l1,16,%l1
+	ldub	[%i0+4],%l4
+	sll	%l2,8,%l2
+	or	%l1,%l0,%l0
+	ldub	[%i0+5],%l5
+	sll	%l4,24,%l4
+	or	%l3,%l2,%l2
+	ldub	[%i0+6],%l6
+	sll	%l5,16,%l5
+	or	%l0,%l2,%o0
+	ldub	[%i0+7],%l7
+
+	sll	%l6,8,%l6
+	or	%l5,%l4,%l4
+	ldub	[%i0+8],%l0
+	or	%l7,%l6,%l6
+	ldub	[%i0+9],%l1
+	or	%l4,%l6,%o1
+	ldub	[%i0+10],%l2
+
+	sll	%l0,24,%l0
+	ldub	[%i0+11],%l3
+	sll	%l1,16,%l1
+	ldub	[%i0+12],%l4
+	sll	%l2,8,%l2
+	or	%l1,%l0,%l0
+	ldub	[%i0+13],%l5
+	sll	%l4,24,%l4
+	or	%l3,%l2,%l2
+	ldub	[%i0+14],%l6
+	sll	%l5,16,%l5
+	or	%l0,%l2,%o2
+	ldub	[%i0+15],%l7
+
+	sll	%l6,8,%l6
+	or	%l5,%l4,%l4
+	or	%l7,%l6,%l6
+	or	%l4,%l6,%o3
+
+1:	call	.+8
+	add	%o7,AES_Te-1b,%o4
+	call	_sparcv9_AES_encrypt
+	mov	%i2,%o5
+
+	srl	%o0,24,%l0
+	srl	%o0,16,%l1
+	stb	%l0,[%i1+0]
+	srl	%o0,8,%l2
+	stb	%l1,[%i1+1]
+	stb	%l2,[%i1+2]
+	srl	%o1,24,%l4
+	stb	%o0,[%i1+3]
+
+	srl	%o1,16,%l5
+	stb	%l4,[%i1+4]
+	srl	%o1,8,%l6
+	stb	%l5,[%i1+5]
+	stb	%l6,[%i1+6]
+	srl	%o2,24,%l0
+	stb	%o1,[%i1+7]
+
+	srl	%o2,16,%l1
+	stb	%l0,[%i1+8]
+	srl	%o2,8,%l2
+	stb	%l1,[%i1+9]
+	stb	%l2,[%i1+10]
+	srl	%o3,24,%l4
+	stb	%o2,[%i1+11]
+
+	srl	%o3,16,%l5
+	stb	%l4,[%i1+12]
+	srl	%o3,8,%l6
+	stb	%l5,[%i1+13]
+	stb	%l6,[%i1+14]
+	stb	%o3,[%i1+15]
+
+	ret
+	restore
+.type	AES_encrypt,#function
+.size	AES_encrypt,(.-AES_encrypt)
+
+___
+
+$code.=<<___;
+.align	256
+AES_Td:
+___
+&_data_word(
+	0x51f4a750, 0x7e416553, 0x1a17a4c3, 0x3a275e96,
+	0x3bab6bcb, 0x1f9d45f1, 0xacfa58ab, 0x4be30393,
+	0x2030fa55, 0xad766df6, 0x88cc7691, 0xf5024c25,
+	0x4fe5d7fc, 0xc52acbd7, 0x26354480, 0xb562a38f,
+	0xdeb15a49, 0x25ba1b67, 0x45ea0e98, 0x5dfec0e1,
+	0xc32f7502, 0x814cf012, 0x8d4697a3, 0x6bd3f9c6,
+	0x038f5fe7, 0x15929c95, 0xbf6d7aeb, 0x955259da,
+	0xd4be832d, 0x587421d3, 0x49e06929, 0x8ec9c844,
+	0x75c2896a, 0xf48e7978, 0x99583e6b, 0x27b971dd,
+	0xbee14fb6, 0xf088ad17, 0xc920ac66, 0x7dce3ab4,
+	0x63df4a18, 0xe51a3182, 0x97513360, 0x62537f45,
+	0xb16477e0, 0xbb6bae84, 0xfe81a01c, 0xf9082b94,
+	0x70486858, 0x8f45fd19, 0x94de6c87, 0x527bf8b7,
+	0xab73d323, 0x724b02e2, 0xe31f8f57, 0x6655ab2a,
+	0xb2eb2807, 0x2fb5c203, 0x86c57b9a, 0xd33708a5,
+	0x302887f2, 0x23bfa5b2, 0x02036aba, 0xed16825c,
+	0x8acf1c2b, 0xa779b492, 0xf307f2f0, 0x4e69e2a1,
+	0x65daf4cd, 0x0605bed5, 0xd134621f, 0xc4a6fe8a,
+	0x342e539d, 0xa2f355a0, 0x058ae132, 0xa4f6eb75,
+	0x0b83ec39, 0x4060efaa, 0x5e719f06, 0xbd6e1051,
+	0x3e218af9, 0x96dd063d, 0xdd3e05ae, 0x4de6bd46,
+	0x91548db5, 0x71c45d05, 0x0406d46f, 0x605015ff,
+	0x1998fb24, 0xd6bde997, 0x894043cc, 0x67d99e77,
+	0xb0e842bd, 0x07898b88, 0xe7195b38, 0x79c8eedb,
+	0xa17c0a47, 0x7c420fe9, 0xf8841ec9, 0x00000000,
+	0x09808683, 0x322bed48, 0x1e1170ac, 0x6c5a724e,
+	0xfd0efffb, 0x0f853856, 0x3daed51e, 0x362d3927,
+	0x0a0fd964, 0x685ca621, 0x9b5b54d1, 0x24362e3a,
+	0x0c0a67b1, 0x9357e70f, 0xb4ee96d2, 0x1b9b919e,
+	0x80c0c54f, 0x61dc20a2, 0x5a774b69, 0x1c121a16,
+	0xe293ba0a, 0xc0a02ae5, 0x3c22e043, 0x121b171d,
+	0x0e090d0b, 0xf28bc7ad, 0x2db6a8b9, 0x141ea9c8,
+	0x57f11985, 0xaf75074c, 0xee99ddbb, 0xa37f60fd,
+	0xf701269f, 0x5c72f5bc, 0x44663bc5, 0x5bfb7e34,
+	0x8b432976, 0xcb23c6dc, 0xb6edfc68, 0xb8e4f163,
+	0xd731dcca, 0x42638510, 0x13972240, 0x84c61120,
+	0x854a247d, 0xd2bb3df8, 0xaef93211, 0xc729a16d,
+	0x1d9e2f4b, 0xdcb230f3, 0x0d8652ec, 0x77c1e3d0,
+	0x2bb3166c, 0xa970b999, 0x119448fa, 0x47e96422,
+	0xa8fc8cc4, 0xa0f03f1a, 0x567d2cd8, 0x223390ef,
+	0x87494ec7, 0xd938d1c1, 0x8ccaa2fe, 0x98d40b36,
+	0xa6f581cf, 0xa57ade28, 0xdab78e26, 0x3fadbfa4,
+	0x2c3a9de4, 0x5078920d, 0x6a5fcc9b, 0x547e4662,
+	0xf68d13c2, 0x90d8b8e8, 0x2e39f75e, 0x82c3aff5,
+	0x9f5d80be, 0x69d0937c, 0x6fd52da9, 0xcf2512b3,
+	0xc8ac993b, 0x10187da7, 0xe89c636e, 0xdb3bbb7b,
+	0xcd267809, 0x6e5918f4, 0xec9ab701, 0x834f9aa8,
+	0xe6956e65, 0xaaffe67e, 0x21bccf08, 0xef15e8e6,
+	0xbae79bd9, 0x4a6f36ce, 0xea9f09d4, 0x29b07cd6,
+	0x31a4b2af, 0x2a3f2331, 0xc6a59430, 0x35a266c0,
+	0x744ebc37, 0xfc82caa6, 0xe090d0b0, 0x33a7d815,
+	0xf104984a, 0x41ecdaf7, 0x7fcd500e, 0x1791f62f,
+	0x764dd68d, 0x43efb04d, 0xccaa4d54, 0xe49604df,
+	0x9ed1b5e3, 0x4c6a881b, 0xc12c1fb8, 0x4665517f,
+	0x9d5eea04, 0x018c355d, 0xfa877473, 0xfb0b412e,
+	0xb3671d5a, 0x92dbd252, 0xe9105633, 0x6dd64713,
+	0x9ad7618c, 0x37a10c7a, 0x59f8148e, 0xeb133c89,
+	0xcea927ee, 0xb761c935, 0xe11ce5ed, 0x7a47b13c,
+	0x9cd2df59, 0x55f2733f, 0x1814ce79, 0x73c737bf,
+	0x53f7cdea, 0x5ffdaa5b, 0xdf3d6f14, 0x7844db86,
+	0xcaaff381, 0xb968c43e, 0x3824342c, 0xc2a3405f,
+	0x161dc372, 0xbce2250c, 0x283c498b, 0xff0d9541,
+	0x39a80171, 0x080cb3de, 0xd8b4e49c, 0x6456c190,
+	0x7bcb8461, 0xd532b670, 0x486c5c74, 0xd0b85742);
+$code.=<<___;
+	.byte	0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38
+	.byte	0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb
+	.byte	0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87
+	.byte	0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb
+	.byte	0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d
+	.byte	0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e
+	.byte	0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2
+	.byte	0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25
+	.byte	0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16
+	.byte	0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92
+	.byte	0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda
+	.byte	0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84
+	.byte	0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a
+	.byte	0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06
+	.byte	0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02
+	.byte	0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b
+	.byte	0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea
+	.byte	0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73
+	.byte	0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85
+	.byte	0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e
+	.byte	0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89
+	.byte	0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b
+	.byte	0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20
+	.byte	0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4
+	.byte	0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31
+	.byte	0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f
+	.byte	0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d
+	.byte	0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef
+	.byte	0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0
+	.byte	0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61
+	.byte	0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26
+	.byte	0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d
+.type	AES_Td,#object
+.size	AES_Td,(.-AES_Td)
+
+.align	64
+.skip	16
+_sparcv9_AES_decrypt:
+	save	%sp,-$frame-$locals,%sp
+	stx	%i7,[%sp+$bias+$frame+0]	! off-load return address
+	ld	[$key+240],$rounds
+	ld	[$key+0],$t0
+	ld	[$key+4],$t1			!
+	ld	[$key+8],$t2
+	ld	[$key+12],$t3
+	srl	$rounds,1,$rounds
+	xor	$t0,$s0,$s0
+	ld	[$key+16],$t0
+	xor	$t1,$s1,$s1
+	ld	[$key+20],$t1
+	srl	$s0,21,$acc0			!
+	xor	$t2,$s2,$s2
+	ld	[$key+24],$t2
+	xor	$t3,$s3,$s3
+	and	$acc0,2040,$acc0
+	ld	[$key+28],$t3
+	srl	$s3,13,$acc1
+	nop
+.Ldec_loop:
+	srl	$s2,5,$acc2			!
+	and	$acc1,2040,$acc1
+	ldx	[$tbl+$acc0],$acc0
+	sll	$s1,3,$acc3
+	and	$acc2,2040,$acc2
+	ldx	[$tbl+$acc1],$acc1
+	srl	$s1,21,$acc4
+	and	$acc3,2040,$acc3
+	ldx	[$tbl+$acc2],$acc2		!
+	srl	$s0,13,$acc5
+	and	$acc4,2040,$acc4
+	ldx	[$tbl+$acc3],$acc3
+	srl	$s3,5,$acc6
+	and	$acc5,2040,$acc5
+	ldx	[$tbl+$acc4],$acc4
+	fmovs	%f0,%f0
+	sll	$s2,3,$acc7			!
+	and	$acc6,2040,$acc6
+	ldx	[$tbl+$acc5],$acc5
+	srl	$s2,21,$acc8
+	and	$acc7,2040,$acc7
+	ldx	[$tbl+$acc6],$acc6
+	srl	$s1,13,$acc9
+	and	$acc8,2040,$acc8
+	ldx	[$tbl+$acc7],$acc7		!
+	srl	$s0,5,$acc10
+	and	$acc9,2040,$acc9
+	ldx	[$tbl+$acc8],$acc8
+	sll	$s3,3,$acc11
+	and	$acc10,2040,$acc10
+	ldx	[$tbl+$acc9],$acc9
+	fmovs	%f0,%f0
+	srl	$s3,21,$acc12			!
+	and	$acc11,2040,$acc11
+	ldx	[$tbl+$acc10],$acc10
+	srl	$s2,13,$acc13
+	and	$acc12,2040,$acc12
+	ldx	[$tbl+$acc11],$acc11
+	srl	$s1,5,$acc14
+	and	$acc13,2040,$acc13
+	ldx	[$tbl+$acc12],$acc12		!
+	sll	$s0,3,$acc15
+	and	$acc14,2040,$acc14
+	ldx	[$tbl+$acc13],$acc13
+	and	$acc15,2040,$acc15
+	add	$key,32,$key
+	ldx	[$tbl+$acc14],$acc14
+	fmovs	%f0,%f0
+	subcc	$rounds,1,$rounds		!
+	ldx	[$tbl+$acc15],$acc15
+	bz,a,pn	%icc,.Ldec_last
+	add	$tbl,2048,$rounds
+
+		srlx	$acc1,8,$acc1
+		xor	$acc0,$t0,$t0
+	ld	[$key+0],$s0
+	fmovs	%f0,%f0
+		srlx	$acc2,16,$acc2		!
+		xor	$acc1,$t0,$t0
+	ld	[$key+4],$s1
+		srlx	$acc3,24,$acc3
+		xor	$acc2,$t0,$t0
+	ld	[$key+8],$s2
+		srlx	$acc5,8,$acc5
+		xor	$acc3,$t0,$t0
+	ld	[$key+12],$s3			!
+		srlx	$acc6,16,$acc6
+		xor	$acc4,$t1,$t1
+	fmovs	%f0,%f0
+		srlx	$acc7,24,$acc7
+		xor	$acc5,$t1,$t1
+		srlx	$acc9,8,$acc9
+		xor	$acc6,$t1,$t1
+		srlx	$acc10,16,$acc10	!
+		xor	$acc7,$t1,$t1
+		srlx	$acc11,24,$acc11
+		xor	$acc8,$t2,$t2
+		srlx	$acc13,8,$acc13
+		xor	$acc9,$t2,$t2
+		srlx	$acc14,16,$acc14
+		xor	$acc10,$t2,$t2
+		srlx	$acc15,24,$acc15	!
+		xor	$acc11,$t2,$t2
+		xor	$acc12,$acc14,$acc14
+		xor	$acc13,$t3,$t3
+	srl	$t0,21,$acc0
+		xor	$acc14,$t3,$t3
+		xor	$acc15,$t3,$t3
+	srl	$t3,13,$acc1
+
+	and	$acc0,2040,$acc0		!
+	srl	$t2,5,$acc2
+	and	$acc1,2040,$acc1
+	ldx	[$tbl+$acc0],$acc0
+	sll	$t1,3,$acc3
+	and	$acc2,2040,$acc2
+	ldx	[$tbl+$acc1],$acc1
+	fmovs	%f0,%f0
+	srl	$t1,21,$acc4			!
+	and	$acc3,2040,$acc3
+	ldx	[$tbl+$acc2],$acc2
+	srl	$t0,13,$acc5
+	and	$acc4,2040,$acc4
+	ldx	[$tbl+$acc3],$acc3
+	srl	$t3,5,$acc6
+	and	$acc5,2040,$acc5
+	ldx	[$tbl+$acc4],$acc4		!
+	sll	$t2,3,$acc7
+	and	$acc6,2040,$acc6
+	ldx	[$tbl+$acc5],$acc5
+	srl	$t2,21,$acc8
+	and	$acc7,2040,$acc7
+	ldx	[$tbl+$acc6],$acc6
+	fmovs	%f0,%f0
+	srl	$t1,13,$acc9			!
+	and	$acc8,2040,$acc8
+	ldx	[$tbl+$acc7],$acc7
+	srl	$t0,5,$acc10
+	and	$acc9,2040,$acc9
+	ldx	[$tbl+$acc8],$acc8
+	sll	$t3,3,$acc11
+	and	$acc10,2040,$acc10
+	ldx	[$tbl+$acc9],$acc9		!
+	srl	$t3,21,$acc12
+	and	$acc11,2040,$acc11
+	ldx	[$tbl+$acc10],$acc10
+	srl	$t2,13,$acc13
+	and	$acc12,2040,$acc12
+	ldx	[$tbl+$acc11],$acc11
+	fmovs	%f0,%f0
+	srl	$t1,5,$acc14			!
+	and	$acc13,2040,$acc13
+	ldx	[$tbl+$acc12],$acc12
+	sll	$t0,3,$acc15
+	and	$acc14,2040,$acc14
+	ldx	[$tbl+$acc13],$acc13
+		srlx	$acc1,8,$acc1
+	and	$acc15,2040,$acc15
+	ldx	[$tbl+$acc14],$acc14		!
+
+		srlx	$acc2,16,$acc2
+		xor	$acc0,$s0,$s0
+	ldx	[$tbl+$acc15],$acc15
+		srlx	$acc3,24,$acc3
+		xor	$acc1,$s0,$s0
+	ld	[$key+16],$t0
+	fmovs	%f0,%f0
+		srlx	$acc5,8,$acc5		!
+		xor	$acc2,$s0,$s0
+	ld	[$key+20],$t1
+		srlx	$acc6,16,$acc6
+		xor	$acc3,$s0,$s0
+	ld	[$key+24],$t2
+		srlx	$acc7,24,$acc7
+		xor	$acc4,$s1,$s1
+	ld	[$key+28],$t3			!
+		srlx	$acc9,8,$acc9
+		xor	$acc5,$s1,$s1
+	ldx	[$tbl+2048+0],%g0		! prefetch td4
+		srlx	$acc10,16,$acc10
+		xor	$acc6,$s1,$s1
+	ldx	[$tbl+2048+32],%g0		! prefetch td4
+		srlx	$acc11,24,$acc11
+		xor	$acc7,$s1,$s1
+	ldx	[$tbl+2048+64],%g0		! prefetch td4
+		srlx	$acc13,8,$acc13
+		xor	$acc8,$s2,$s2
+	ldx	[$tbl+2048+96],%g0		! prefetch td4
+		srlx	$acc14,16,$acc14	!
+		xor	$acc9,$s2,$s2
+	ldx	[$tbl+2048+128],%g0		! prefetch td4
+		srlx	$acc15,24,$acc15
+		xor	$acc10,$s2,$s2
+	ldx	[$tbl+2048+160],%g0		! prefetch td4
+	srl	$s0,21,$acc0
+		xor	$acc11,$s2,$s2
+	ldx	[$tbl+2048+192],%g0		! prefetch td4
+		xor	$acc12,$acc14,$acc14
+		xor	$acc13,$s3,$s3
+	ldx	[$tbl+2048+224],%g0		! prefetch td4
+	and	$acc0,2040,$acc0		!
+		xor	$acc14,$s3,$s3
+		xor	$acc15,$s3,$s3
+	ba	.Ldec_loop
+	srl	$s3,13,$acc1
+
+.align	32
+.Ldec_last:
+		srlx	$acc1,8,$acc1		!
+		xor	$acc0,$t0,$t0
+	ld	[$key+0],$s0
+		srlx	$acc2,16,$acc2
+		xor	$acc1,$t0,$t0
+	ld	[$key+4],$s1
+		srlx	$acc3,24,$acc3
+		xor	$acc2,$t0,$t0
+	ld	[$key+8],$s2			!
+		srlx	$acc5,8,$acc5
+		xor	$acc3,$t0,$t0
+	ld	[$key+12],$s3
+		srlx	$acc6,16,$acc6
+		xor	$acc4,$t1,$t1
+		srlx	$acc7,24,$acc7
+		xor	$acc5,$t1,$t1
+		srlx	$acc9,8,$acc9		!
+		xor	$acc6,$t1,$t1
+		srlx	$acc10,16,$acc10
+		xor	$acc7,$t1,$t1
+		srlx	$acc11,24,$acc11
+		xor	$acc8,$t2,$t2
+		srlx	$acc13,8,$acc13
+		xor	$acc9,$t2,$t2
+		srlx	$acc14,16,$acc14	!
+		xor	$acc10,$t2,$t2
+		srlx	$acc15,24,$acc15
+		xor	$acc11,$t2,$t2
+		xor	$acc12,$acc14,$acc14
+		xor	$acc13,$t3,$t3
+	srl	$t0,24,$acc0
+		xor	$acc14,$t3,$t3
+		xor	$acc15,$t3,$t3		!
+	srl	$t3,16,$acc1
+
+	srl	$t2,8,$acc2
+	and	$acc1,255,$acc1
+	ldub	[$rounds+$acc0],$acc0
+	srl	$t1,24,$acc4
+	and	$acc2,255,$acc2
+	ldub	[$rounds+$acc1],$acc1
+	srl	$t0,16,$acc5			!
+	and	$t1,255,$acc3
+	ldub	[$rounds+$acc2],$acc2
+	ldub	[$rounds+$acc3],$acc3
+	srl	$t3,8,$acc6
+	and	$acc5,255,$acc5
+	ldub	[$rounds+$acc4],$acc4
+	fmovs	%f0,%f0
+	srl	$t2,24,$acc8			!
+	and	$acc6,255,$acc6
+	ldub	[$rounds+$acc5],$acc5
+	srl	$t1,16,$acc9
+	and	$t2,255,$acc7
+	ldub	[$rounds+$acc6],$acc6
+	ldub	[$rounds+$acc7],$acc7
+	fmovs	%f0,%f0
+	srl	$t0,8,$acc10			!
+	and	$acc9,255,$acc9
+	ldub	[$rounds+$acc8],$acc8
+	srl	$t3,24,$acc12
+	and	$acc10,255,$acc10
+	ldub	[$rounds+$acc9],$acc9
+	srl	$t2,16,$acc13
+	and	$t3,255,$acc11
+	ldub	[$rounds+$acc10],$acc10		!
+	srl	$t1,8,$acc14
+	and	$acc13,255,$acc13
+	ldub	[$rounds+$acc11],$acc11
+	ldub	[$rounds+$acc12],$acc12
+	and	$acc14,255,$acc14
+	ldub	[$rounds+$acc13],$acc13
+	and	$t0,255,$acc15
+	ldub	[$rounds+$acc14],$acc14		!
+
+		sll	$acc0,24,$acc0
+		xor	$acc3,$s0,$s0
+	ldub	[$rounds+$acc15],$acc15
+		sll	$acc1,16,$acc1
+		xor	$acc0,$s0,$s0
+	ldx	[%sp+$bias+$frame+0],%i7	! restore return address
+	fmovs	%f0,%f0
+		sll	$acc2,8,$acc2		!
+		xor	$acc1,$s0,$s0
+		sll	$acc4,24,$acc4
+		xor	$acc2,$s0,$s0
+		sll	$acc5,16,$acc5
+		xor	$acc7,$s1,$s1
+		sll	$acc6,8,$acc6
+		xor	$acc4,$s1,$s1
+		sll	$acc8,24,$acc8		!
+		xor	$acc5,$s1,$s1
+		sll	$acc9,16,$acc9
+		xor	$acc11,$s2,$s2
+		sll	$acc10,8,$acc10
+		xor	$acc6,$s1,$s1
+		sll	$acc12,24,$acc12
+		xor	$acc8,$s2,$s2
+		sll	$acc13,16,$acc13	!
+		xor	$acc9,$s2,$s2
+		sll	$acc14,8,$acc14
+		xor	$acc10,$s2,$s2
+		xor	$acc12,$acc14,$acc14
+		xor	$acc13,$s3,$s3
+		xor	$acc14,$s3,$s3
+		xor	$acc15,$s3,$s3
+
+	ret
+	restore
+.type	_sparcv9_AES_decrypt,#function
+.size	_sparcv9_AES_decrypt,(.-_sparcv9_AES_decrypt)
+
+.align	32
+.globl	AES_decrypt
+AES_decrypt:
+	or	%o0,%o1,%g1
+	andcc	%g1,3,%g0
+	bnz,pn	%xcc,.Lunaligned_dec
+	save	%sp,-$frame,%sp
+
+	ld	[%i0+0],%o0
+	ld	[%i0+4],%o1
+	ld	[%i0+8],%o2
+	ld	[%i0+12],%o3
+
+1:	call	.+8
+	add	%o7,AES_Td-1b,%o4
+	call	_sparcv9_AES_decrypt
+	mov	%i2,%o5
+
+	st	%o0,[%i1+0]
+	st	%o1,[%i1+4]
+	st	%o2,[%i1+8]
+	st	%o3,[%i1+12]
+
+	ret
+	restore
+
+.align	32
+.Lunaligned_dec:
+	ldub	[%i0+0],%l0
+	ldub	[%i0+1],%l1
+	ldub	[%i0+2],%l2
+
+	sll	%l0,24,%l0
+	ldub	[%i0+3],%l3
+	sll	%l1,16,%l1
+	ldub	[%i0+4],%l4
+	sll	%l2,8,%l2
+	or	%l1,%l0,%l0
+	ldub	[%i0+5],%l5
+	sll	%l4,24,%l4
+	or	%l3,%l2,%l2
+	ldub	[%i0+6],%l6
+	sll	%l5,16,%l5
+	or	%l0,%l2,%o0
+	ldub	[%i0+7],%l7
+
+	sll	%l6,8,%l6
+	or	%l5,%l4,%l4
+	ldub	[%i0+8],%l0
+	or	%l7,%l6,%l6
+	ldub	[%i0+9],%l1
+	or	%l4,%l6,%o1
+	ldub	[%i0+10],%l2
+
+	sll	%l0,24,%l0
+	ldub	[%i0+11],%l3
+	sll	%l1,16,%l1
+	ldub	[%i0+12],%l4
+	sll	%l2,8,%l2
+	or	%l1,%l0,%l0
+	ldub	[%i0+13],%l5
+	sll	%l4,24,%l4
+	or	%l3,%l2,%l2
+	ldub	[%i0+14],%l6
+	sll	%l5,16,%l5
+	or	%l0,%l2,%o2
+	ldub	[%i0+15],%l7
+
+	sll	%l6,8,%l6
+	or	%l5,%l4,%l4
+	or	%l7,%l6,%l6
+	or	%l4,%l6,%o3
+
+1:	call	.+8
+	add	%o7,AES_Td-1b,%o4
+	call	_sparcv9_AES_decrypt
+	mov	%i2,%o5
+
+	srl	%o0,24,%l0
+	srl	%o0,16,%l1
+	stb	%l0,[%i1+0]
+	srl	%o0,8,%l2
+	stb	%l1,[%i1+1]
+	stb	%l2,[%i1+2]
+	srl	%o1,24,%l4
+	stb	%o0,[%i1+3]
+
+	srl	%o1,16,%l5
+	stb	%l4,[%i1+4]
+	srl	%o1,8,%l6
+	stb	%l5,[%i1+5]
+	stb	%l6,[%i1+6]
+	srl	%o2,24,%l0
+	stb	%o1,[%i1+7]
+
+	srl	%o2,16,%l1
+	stb	%l0,[%i1+8]
+	srl	%o2,8,%l2
+	stb	%l1,[%i1+9]
+	stb	%l2,[%i1+10]
+	srl	%o3,24,%l4
+	stb	%o2,[%i1+11]
+
+	srl	%o3,16,%l5
+	stb	%l4,[%i1+12]
+	srl	%o3,8,%l6
+	stb	%l5,[%i1+13]
+	stb	%l6,[%i1+14]
+	stb	%o3,[%i1+15]
+
+	ret
+	restore
+.type	AES_decrypt,#function
+.size	AES_decrypt,(.-AES_decrypt)
+___
+
+# fmovs instructions substituting for FP nops were originally added
+# to meet specific instruction alignment requirements to maximize ILP.
+# As UltraSPARC T1, a.k.a. Niagara, has shared FPU, FP nops can have
+# undesired effect, so just omit them and sacrifice some portion of
+# percent in performance...
+$code =~ s/fmovs.*$//gm;
+
+print $code;
+close STDOUT;	# ensure flush
diff --git a/crypto/aes/asm/aes-x86_64.pl b/crypto/aes/asm/aes-x86_64.pl
new file mode 100755
index 000000000..ce4ca30b1
--- /dev/null
+++ b/crypto/aes/asm/aes-x86_64.pl
@@ -0,0 +1,2820 @@
+#! /usr/bin/env perl
+# Copyright 2005-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+#
+# ====================================================================
+# Written by Andy Polyakov  for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+#
+# Version 2.1.
+#
+# aes-*-cbc benchmarks are improved by >70% [compared to gcc 3.3.2 on
+# Opteron 240 CPU] plus all the bells-n-whistles from 32-bit version
+# [you'll notice a lot of resemblance], such as compressed S-boxes
+# in little-endian byte order, prefetch of these tables in CBC mode,
+# as well as avoiding L1 cache aliasing between stack frame and key
+# schedule and already mentioned tables, compressed Td4...
+#
+# Performance in number of cycles per processed byte for 128-bit key:
+#
+#		ECB encrypt	ECB decrypt	CBC large chunk
+# AMD64		33		43		13.0
+# EM64T		38		56		18.6(*)
+# Core 2	30		42		14.5(*)
+# Atom		65		86		32.1(*)
+#
+# (*) with hyper-threading off
+
+$flavour = shift;
+$output  = shift;
+if ($flavour =~ /\./) { $output = $flavour; undef $flavour; }
+
+$win64=0; $win64=1 if ($flavour =~ /[nm]asm|mingw64/ || $output =~ /\.asm$/);
+
+$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+( $xlate="${dir}x86_64-xlate.pl" and -f $xlate ) or
+( $xlate="${dir}../../perlasm/x86_64-xlate.pl" and -f $xlate) or
+die "can't locate x86_64-xlate.pl";
+
+open OUT,"| \"$^X\" \"$xlate\" $flavour \"$output\"";
+*STDOUT=*OUT;
+
+$verticalspin=1;	# unlike 32-bit version $verticalspin performs
+			# ~15% better on both AMD and Intel cores
+$speed_limit=512;	# see aes-586.pl for details
+
+$code=".text\n";
+
+$s0="%eax";
+$s1="%ebx";
+$s2="%ecx";
+$s3="%edx";
+$acc0="%esi";	$mask80="%rsi";
+$acc1="%edi";	$maskfe="%rdi";
+$acc2="%ebp";	$mask1b="%rbp";
+$inp="%r8";
+$out="%r9";
+$t0="%r10d";
+$t1="%r11d";
+$t2="%r12d";
+$rnds="%r13d";
+$sbox="%r14";
+$key="%r15";
+
+sub hi() { my $r=shift;	$r =~ s/%[er]([a-d])x/%\1h/;	$r; }
+sub lo() { my $r=shift;	$r =~ s/%[er]([a-d])x/%\1l/;
+			$r =~ s/%[er]([sd]i)/%\1l/;
+			$r =~ s/%(r[0-9]+)[d]?/%\1b/;	$r; }
+sub LO() { my $r=shift; $r =~ s/%r([a-z]+)/%e\1/;
+			$r =~ s/%r([0-9]+)/%r\1d/;	$r; }
+sub _data_word()
+{ my $i;
+    while(defined($i=shift)) { $code.=sprintf".long\t0x%08x,0x%08x\n",$i,$i; }
+}
+sub data_word()
+{ my $i;
+  my $last=pop(@_);
+    $code.=".long\t";
+    while(defined($i=shift)) { $code.=sprintf"0x%08x,",$i; }
+    $code.=sprintf"0x%08x\n",$last;
+}
+
+sub data_byte()
+{ my $i;
+  my $last=pop(@_);
+    $code.=".byte\t";
+    while(defined($i=shift)) { $code.=sprintf"0x%02x,",$i&0xff; }
+    $code.=sprintf"0x%02x\n",$last&0xff;
+}
+
+sub encvert()
+{ my $t3="%r8d";	# zaps $inp!
+
+$code.=<<___;
+	# favor 3-way issue Opteron pipeline...
+	movzb	`&lo("$s0")`,$acc0
+	movzb	`&lo("$s1")`,$acc1
+	movzb	`&lo("$s2")`,$acc2
+	mov	0($sbox,$acc0,8),$t0
+	mov	0($sbox,$acc1,8),$t1
+	mov	0($sbox,$acc2,8),$t2
+
+	movzb	`&hi("$s1")`,$acc0
+	movzb	`&hi("$s2")`,$acc1
+	movzb	`&lo("$s3")`,$acc2
+	xor	3($sbox,$acc0,8),$t0
+	xor	3($sbox,$acc1,8),$t1
+	mov	0($sbox,$acc2,8),$t3
+
+	movzb	`&hi("$s3")`,$acc0
+	shr	\$16,$s2
+	movzb	`&hi("$s0")`,$acc2
+	xor	3($sbox,$acc0,8),$t2
+	shr	\$16,$s3
+	xor	3($sbox,$acc2,8),$t3
+
+	shr	\$16,$s1
+	lea	16($key),$key
+	shr	\$16,$s0
+
+	movzb	`&lo("$s2")`,$acc0
+	movzb	`&lo("$s3")`,$acc1
+	movzb	`&lo("$s0")`,$acc2
+	xor	2($sbox,$acc0,8),$t0
+	xor	2($sbox,$acc1,8),$t1
+	xor	2($sbox,$acc2,8),$t2
+
+	movzb	`&hi("$s3")`,$acc0
+	movzb	`&hi("$s0")`,$acc1
+	movzb	`&lo("$s1")`,$acc2
+	xor	1($sbox,$acc0,8),$t0
+	xor	1($sbox,$acc1,8),$t1
+	xor	2($sbox,$acc2,8),$t3
+
+	mov	12($key),$s3
+	movzb	`&hi("$s1")`,$acc1
+	movzb	`&hi("$s2")`,$acc2
+	mov	0($key),$s0
+	xor	1($sbox,$acc1,8),$t2
+	xor	1($sbox,$acc2,8),$t3
+
+	mov	4($key),$s1
+	mov	8($key),$s2
+	xor	$t0,$s0
+	xor	$t1,$s1
+	xor	$t2,$s2
+	xor	$t3,$s3
+___
+}
+
+sub enclastvert()
+{ my $t3="%r8d";	# zaps $inp!
+
+$code.=<<___;
+	movzb	`&lo("$s0")`,$acc0
+	movzb	`&lo("$s1")`,$acc1
+	movzb	`&lo("$s2")`,$acc2
+	movzb	2($sbox,$acc0,8),$t0
+	movzb	2($sbox,$acc1,8),$t1
+	movzb	2($sbox,$acc2,8),$t2
+
+	movzb	`&lo("$s3")`,$acc0
+	movzb	`&hi("$s1")`,$acc1
+	movzb	`&hi("$s2")`,$acc2
+	movzb	2($sbox,$acc0,8),$t3
+	mov	0($sbox,$acc1,8),$acc1	#$t0
+	mov	0($sbox,$acc2,8),$acc2	#$t1
+
+	and	\$0x0000ff00,$acc1
+	and	\$0x0000ff00,$acc2
+
+	xor	$acc1,$t0
+	xor	$acc2,$t1
+	shr	\$16,$s2
+
+	movzb	`&hi("$s3")`,$acc0
+	movzb	`&hi("$s0")`,$acc1
+	shr	\$16,$s3
+	mov	0($sbox,$acc0,8),$acc0	#$t2
+	mov	0($sbox,$acc1,8),$acc1	#$t3
+
+	and	\$0x0000ff00,$acc0
+	and	\$0x0000ff00,$acc1
+	shr	\$16,$s1
+	xor	$acc0,$t2
+	xor	$acc1,$t3
+	shr	\$16,$s0
+
+	movzb	`&lo("$s2")`,$acc0
+	movzb	`&lo("$s3")`,$acc1
+	movzb	`&lo("$s0")`,$acc2
+	mov	0($sbox,$acc0,8),$acc0	#$t0
+	mov	0($sbox,$acc1,8),$acc1	#$t1
+	mov	0($sbox,$acc2,8),$acc2	#$t2
+
+	and	\$0x00ff0000,$acc0
+	and	\$0x00ff0000,$acc1
+	and	\$0x00ff0000,$acc2
+
+	xor	$acc0,$t0
+	xor	$acc1,$t1
+	xor	$acc2,$t2
+
+	movzb	`&lo("$s1")`,$acc0
+	movzb	`&hi("$s3")`,$acc1
+	movzb	`&hi("$s0")`,$acc2
+	mov	0($sbox,$acc0,8),$acc0	#$t3
+	mov	2($sbox,$acc1,8),$acc1	#$t0
+	mov	2($sbox,$acc2,8),$acc2	#$t1
+
+	and	\$0x00ff0000,$acc0
+	and	\$0xff000000,$acc1
+	and	\$0xff000000,$acc2
+
+	xor	$acc0,$t3
+	xor	$acc1,$t0
+	xor	$acc2,$t1
+
+	movzb	`&hi("$s1")`,$acc0
+	movzb	`&hi("$s2")`,$acc1
+	mov	16+12($key),$s3
+	mov	2($sbox,$acc0,8),$acc0	#$t2
+	mov	2($sbox,$acc1,8),$acc1	#$t3
+	mov	16+0($key),$s0
+
+	and	\$0xff000000,$acc0
+	and	\$0xff000000,$acc1
+
+	xor	$acc0,$t2
+	xor	$acc1,$t3
+
+	mov	16+4($key),$s1
+	mov	16+8($key),$s2
+	xor	$t0,$s0
+	xor	$t1,$s1
+	xor	$t2,$s2
+	xor	$t3,$s3
+___
+}
+
+sub encstep()
+{ my ($i,@s) = @_;
+  my $tmp0=$acc0;
+  my $tmp1=$acc1;
+  my $tmp2=$acc2;
+  my $out=($t0,$t1,$t2,$s[0])[$i];
+
+	if ($i==3) {
+		$tmp0=$s[1];
+		$tmp1=$s[2];
+		$tmp2=$s[3];
+	}
+	$code.="	movzb	".&lo($s[0]).",$out\n";
+	$code.="	mov	$s[2],$tmp1\n"		if ($i!=3);
+	$code.="	lea	16($key),$key\n"	if ($i==0);
+
+	$code.="	movzb	".&hi($s[1]).",$tmp0\n";
+	$code.="	mov	0($sbox,$out,8),$out\n";
+
+	$code.="	shr	\$16,$tmp1\n";
+	$code.="	mov	$s[3],$tmp2\n"		if ($i!=3);
+	$code.="	xor	3($sbox,$tmp0,8),$out\n";
+
+	$code.="	movzb	".&lo($tmp1).",$tmp1\n";
+	$code.="	shr	\$24,$tmp2\n";
+	$code.="	xor	4*$i($key),$out\n";
+
+	$code.="	xor	2($sbox,$tmp1,8),$out\n";
+	$code.="	xor	1($sbox,$tmp2,8),$out\n";
+
+	$code.="	mov	$t0,$s[1]\n"		if ($i==3);
+	$code.="	mov	$t1,$s[2]\n"		if ($i==3);
+	$code.="	mov	$t2,$s[3]\n"		if ($i==3);
+	$code.="\n";
+}
+
+sub enclast()
+{ my ($i,@s)=@_;
+  my $tmp0=$acc0;
+  my $tmp1=$acc1;
+  my $tmp2=$acc2;
+  my $out=($t0,$t1,$t2,$s[0])[$i];
+
+	if ($i==3) {
+		$tmp0=$s[1];
+		$tmp1=$s[2];
+		$tmp2=$s[3];
+	}
+	$code.="	movzb	".&lo($s[0]).",$out\n";
+	$code.="	mov	$s[2],$tmp1\n"		if ($i!=3);
+
+	$code.="	mov	2($sbox,$out,8),$out\n";
+	$code.="	shr	\$16,$tmp1\n";
+	$code.="	mov	$s[3],$tmp2\n"		if ($i!=3);
+
+	$code.="	and	\$0x000000ff,$out\n";
+	$code.="	movzb	".&hi($s[1]).",$tmp0\n";
+	$code.="	movzb	".&lo($tmp1).",$tmp1\n";
+	$code.="	shr	\$24,$tmp2\n";
+
+	$code.="	mov	0($sbox,$tmp0,8),$tmp0\n";
+	$code.="	mov	0($sbox,$tmp1,8),$tmp1\n";
+	$code.="	mov	2($sbox,$tmp2,8),$tmp2\n";
+
+	$code.="	and	\$0x0000ff00,$tmp0\n";
+	$code.="	and	\$0x00ff0000,$tmp1\n";
+	$code.="	and	\$0xff000000,$tmp2\n";
+
+	$code.="	xor	$tmp0,$out\n";
+	$code.="	mov	$t0,$s[1]\n"		if ($i==3);
+	$code.="	xor	$tmp1,$out\n";
+	$code.="	mov	$t1,$s[2]\n"		if ($i==3);
+	$code.="	xor	$tmp2,$out\n";
+	$code.="	mov	$t2,$s[3]\n"		if ($i==3);
+	$code.="\n";
+}
+
+$code.=<<___;
+.type	_x86_64_AES_encrypt,\@abi-omnipotent
+.align	16
+_x86_64_AES_encrypt:
+	xor	0($key),$s0			# xor with key
+	xor	4($key),$s1
+	xor	8($key),$s2
+	xor	12($key),$s3
+
+	mov	240($key),$rnds			# load key->rounds
+	sub	\$1,$rnds
+	jmp	.Lenc_loop
+.align	16
+.Lenc_loop:
+___
+	if ($verticalspin) { &encvert(); }
+	else {	&encstep(0,$s0,$s1,$s2,$s3);
+		&encstep(1,$s1,$s2,$s3,$s0);
+		&encstep(2,$s2,$s3,$s0,$s1);
+		&encstep(3,$s3,$s0,$s1,$s2);
+	}
+$code.=<<___;
+	sub	\$1,$rnds
+	jnz	.Lenc_loop
+___
+	if ($verticalspin) { &enclastvert(); }
+	else {	&enclast(0,$s0,$s1,$s2,$s3);
+		&enclast(1,$s1,$s2,$s3,$s0);
+		&enclast(2,$s2,$s3,$s0,$s1);
+		&enclast(3,$s3,$s0,$s1,$s2);
+		$code.=<<___;
+		xor	16+0($key),$s0		# xor with key
+		xor	16+4($key),$s1
+		xor	16+8($key),$s2
+		xor	16+12($key),$s3
+___
+	}
+$code.=<<___;
+	.byte	0xf3,0xc3			# rep ret
+.size	_x86_64_AES_encrypt,.-_x86_64_AES_encrypt
+___
+
+# it's possible to implement this by shifting tN by 8, filling least
+# significant byte with byte load and finally bswap-ing at the end,
+# but such partial register load kills Core 2...
+sub enccompactvert()
+{ my ($t3,$t4,$t5)=("%r8d","%r9d","%r13d");
+
+$code.=<<___;
+	movzb	`&lo("$s0")`,$t0
+	movzb	`&lo("$s1")`,$t1
+	movzb	`&lo("$s2")`,$t2
+	movzb	`&lo("$s3")`,$t3
+	movzb	`&hi("$s1")`,$acc0
+	movzb	`&hi("$s2")`,$acc1
+	shr	\$16,$s2
+	movzb	`&hi("$s3")`,$acc2
+	movzb	($sbox,$t0,1),$t0
+	movzb	($sbox,$t1,1),$t1
+	movzb	($sbox,$t2,1),$t2
+	movzb	($sbox,$t3,1),$t3
+
+	movzb	($sbox,$acc0,1),$t4	#$t0
+	movzb	`&hi("$s0")`,$acc0
+	movzb	($sbox,$acc1,1),$t5	#$t1
+	movzb	`&lo("$s2")`,$acc1
+	movzb	($sbox,$acc2,1),$acc2	#$t2
+	movzb	($sbox,$acc0,1),$acc0	#$t3
+
+	shl	\$8,$t4
+	shr	\$16,$s3
+	shl	\$8,$t5
+	xor	$t4,$t0
+	shr	\$16,$s0
+	movzb	`&lo("$s3")`,$t4
+	shr	\$16,$s1
+	xor	$t5,$t1
+	shl	\$8,$acc2
+	movzb	`&lo("$s0")`,$t5
+	movzb	($sbox,$acc1,1),$acc1	#$t0
+	xor	$acc2,$t2
+
+	shl	\$8,$acc0
+	movzb	`&lo("$s1")`,$acc2
+	shl	\$16,$acc1
+	xor	$acc0,$t3
+	movzb	($sbox,$t4,1),$t4	#$t1
+	movzb	`&hi("$s3")`,$acc0
+	movzb	($sbox,$t5,1),$t5	#$t2
+	xor	$acc1,$t0
+
+	shr	\$8,$s2
+	movzb	`&hi("$s0")`,$acc1
+	shl	\$16,$t4
+	shr	\$8,$s1
+	shl	\$16,$t5
+	xor	$t4,$t1
+	movzb	($sbox,$acc2,1),$acc2	#$t3
+	movzb	($sbox,$acc0,1),$acc0	#$t0
+	movzb	($sbox,$acc1,1),$acc1	#$t1
+	movzb	($sbox,$s2,1),$s3	#$t3
+	movzb	($sbox,$s1,1),$s2	#$t2
+
+	shl	\$16,$acc2
+	xor	$t5,$t2
+	shl	\$24,$acc0
+	xor	$acc2,$t3
+	shl	\$24,$acc1
+	xor	$acc0,$t0
+	shl	\$24,$s3
+	xor	$acc1,$t1
+	shl	\$24,$s2
+	mov	$t0,$s0
+	mov	$t1,$s1
+	xor	$t2,$s2
+	xor	$t3,$s3
+___
+}
+
+sub enctransform_ref()
+{ my $sn = shift;
+  my ($acc,$r2,$tmp)=("%r8d","%r9d","%r13d");
+
+$code.=<<___;
+	mov	$sn,$acc
+	and	\$0x80808080,$acc
+	mov	$acc,$tmp
+	shr	\$7,$tmp
+	lea	($sn,$sn),$r2
+	sub	$tmp,$acc
+	and	\$0xfefefefe,$r2
+	and	\$0x1b1b1b1b,$acc
+	mov	$sn,$tmp
+	xor	$acc,$r2
+
+	xor	$r2,$sn
+	rol	\$24,$sn
+	xor	$r2,$sn
+	ror	\$16,$tmp
+	xor	$tmp,$sn
+	ror	\$8,$tmp
+	xor	$tmp,$sn
+___
+}
+
+# unlike decrypt case it does not pay off to parallelize enctransform
+sub enctransform()
+{ my ($t3,$r20,$r21)=($acc2,"%r8d","%r9d");
+
+$code.=<<___;
+	mov	\$0x80808080,$t0
+	mov	\$0x80808080,$t1
+	and	$s0,$t0
+	and	$s1,$t1
+	mov	$t0,$acc0
+	mov	$t1,$acc1
+	shr	\$7,$t0
+	lea	($s0,$s0),$r20
+	shr	\$7,$t1
+	lea	($s1,$s1),$r21
+	sub	$t0,$acc0
+	sub	$t1,$acc1
+	and	\$0xfefefefe,$r20
+	and	\$0xfefefefe,$r21
+	and	\$0x1b1b1b1b,$acc0
+	and	\$0x1b1b1b1b,$acc1
+	mov	$s0,$t0
+	mov	$s1,$t1
+	xor	$acc0,$r20
+	xor	$acc1,$r21
+
+	xor	$r20,$s0
+	xor	$r21,$s1
+	 mov	\$0x80808080,$t2
+	rol	\$24,$s0
+	 mov	\$0x80808080,$t3
+	rol	\$24,$s1
+	 and	$s2,$t2
+	 and	$s3,$t3
+	xor	$r20,$s0
+	xor	$r21,$s1
+	 mov	$t2,$acc0
+	ror	\$16,$t0
+	 mov	$t3,$acc1
+	ror	\$16,$t1
+	 lea	($s2,$s2),$r20
+	 shr	\$7,$t2
+	xor	$t0,$s0
+	 shr	\$7,$t3
+	xor	$t1,$s1
+	ror	\$8,$t0
+	 lea	($s3,$s3),$r21
+	ror	\$8,$t1
+	 sub	$t2,$acc0
+	 sub	$t3,$acc1
+	xor	$t0,$s0
+	xor	$t1,$s1
+
+	and	\$0xfefefefe,$r20
+	and	\$0xfefefefe,$r21
+	and	\$0x1b1b1b1b,$acc0
+	and	\$0x1b1b1b1b,$acc1
+	mov	$s2,$t2
+	mov	$s3,$t3
+	xor	$acc0,$r20
+	xor	$acc1,$r21
+
+	ror	\$16,$t2
+	xor	$r20,$s2
+	ror	\$16,$t3
+	xor	$r21,$s3
+	rol	\$24,$s2
+	mov	0($sbox),$acc0			# prefetch Te4
+	rol	\$24,$s3
+	xor	$r20,$s2
+	mov	64($sbox),$acc1
+	xor	$r21,$s3
+	mov	128($sbox),$r20
+	xor	$t2,$s2
+	ror	\$8,$t2
+	xor	$t3,$s3
+	ror	\$8,$t3
+	xor	$t2,$s2
+	mov	192($sbox),$r21
+	xor	$t3,$s3
+___
+}
+
+$code.=<<___;
+.type	_x86_64_AES_encrypt_compact,\@abi-omnipotent
+.align	16
+_x86_64_AES_encrypt_compact:
+	lea	128($sbox),$inp			# size optimization
+	mov	0-128($inp),$acc1		# prefetch Te4
+	mov	32-128($inp),$acc2
+	mov	64-128($inp),$t0
+	mov	96-128($inp),$t1
+	mov	128-128($inp),$acc1
+	mov	160-128($inp),$acc2
+	mov	192-128($inp),$t0
+	mov	224-128($inp),$t1
+	jmp	.Lenc_loop_compact
+.align	16
+.Lenc_loop_compact:
+		xor	0($key),$s0		# xor with key
+		xor	4($key),$s1
+		xor	8($key),$s2
+		xor	12($key),$s3
+		lea	16($key),$key
+___
+		&enccompactvert();
+$code.=<<___;
+		cmp	16(%rsp),$key
+		je	.Lenc_compact_done
+___
+		&enctransform();
+$code.=<<___;
+	jmp	.Lenc_loop_compact
+.align	16
+.Lenc_compact_done:
+	xor	0($key),$s0
+	xor	4($key),$s1
+	xor	8($key),$s2
+	xor	12($key),$s3
+	.byte	0xf3,0xc3			# rep ret
+.size	_x86_64_AES_encrypt_compact,.-_x86_64_AES_encrypt_compact
+___
+
+# void AES_encrypt (const void *inp,void *out,const AES_KEY *key);
+$code.=<<___;
+.globl	AES_encrypt
+.type	AES_encrypt,\@function,3
+.align	16
+.globl	asm_AES_encrypt
+.hidden	asm_AES_encrypt
+asm_AES_encrypt:
+AES_encrypt:
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+
+	# allocate frame "above" key schedule
+	mov	%rsp,%r10
+	lea	-63(%rdx),%rcx	# %rdx is key argument
+	and	\$-64,%rsp
+	sub	%rsp,%rcx
+	neg	%rcx
+	and	\$0x3c0,%rcx
+	sub	%rcx,%rsp
+	sub	\$32,%rsp
+
+	mov	%rsi,16(%rsp)	# save out
+	mov	%r10,24(%rsp)	# save real stack pointer
+.Lenc_prologue:
+
+	mov	%rdx,$key
+	mov	240($key),$rnds	# load rounds
+
+	mov	0(%rdi),$s0	# load input vector
+	mov	4(%rdi),$s1
+	mov	8(%rdi),$s2
+	mov	12(%rdi),$s3
+
+	shl	\$4,$rnds
+	lea	($key,$rnds),%rbp
+	mov	$key,(%rsp)	# key schedule
+	mov	%rbp,8(%rsp)	# end of key schedule
+
+	# pick Te4 copy which can't "overlap" with stack frame or key schedule
+	lea	.LAES_Te+2048(%rip),$sbox
+	lea	768(%rsp),%rbp
+	sub	$sbox,%rbp
+	and	\$0x300,%rbp
+	lea	($sbox,%rbp),$sbox
+
+	call	_x86_64_AES_encrypt_compact
+
+	mov	16(%rsp),$out	# restore out
+	mov	24(%rsp),%rsi	# restore saved stack pointer
+	mov	$s0,0($out)	# write output vector
+	mov	$s1,4($out)
+	mov	$s2,8($out)
+	mov	$s3,12($out)
+
+	mov	(%rsi),%r15
+	mov	8(%rsi),%r14
+	mov	16(%rsi),%r13
+	mov	24(%rsi),%r12
+	mov	32(%rsi),%rbp
+	mov	40(%rsi),%rbx
+	lea	48(%rsi),%rsp
+.Lenc_epilogue:
+	ret
+.size	AES_encrypt,.-AES_encrypt
+___
+
+#------------------------------------------------------------------#
+
+sub decvert()
+{ my $t3="%r8d";	# zaps $inp!
+
+$code.=<<___;
+	# favor 3-way issue Opteron pipeline...
+	movzb	`&lo("$s0")`,$acc0
+	movzb	`&lo("$s1")`,$acc1
+	movzb	`&lo("$s2")`,$acc2
+	mov	0($sbox,$acc0,8),$t0
+	mov	0($sbox,$acc1,8),$t1
+	mov	0($sbox,$acc2,8),$t2
+
+	movzb	`&hi("$s3")`,$acc0
+	movzb	`&hi("$s0")`,$acc1
+	movzb	`&lo("$s3")`,$acc2
+	xor	3($sbox,$acc0,8),$t0
+	xor	3($sbox,$acc1,8),$t1
+	mov	0($sbox,$acc2,8),$t3
+
+	movzb	`&hi("$s1")`,$acc0
+	shr	\$16,$s0
+	movzb	`&hi("$s2")`,$acc2
+	xor	3($sbox,$acc0,8),$t2
+	shr	\$16,$s3
+	xor	3($sbox,$acc2,8),$t3
+
+	shr	\$16,$s1
+	lea	16($key),$key
+	shr	\$16,$s2
+
+	movzb	`&lo("$s2")`,$acc0
+	movzb	`&lo("$s3")`,$acc1
+	movzb	`&lo("$s0")`,$acc2
+	xor	2($sbox,$acc0,8),$t0
+	xor	2($sbox,$acc1,8),$t1
+	xor	2($sbox,$acc2,8),$t2
+
+	movzb	`&hi("$s1")`,$acc0
+	movzb	`&hi("$s2")`,$acc1
+	movzb	`&lo("$s1")`,$acc2
+	xor	1($sbox,$acc0,8),$t0
+	xor	1($sbox,$acc1,8),$t1
+	xor	2($sbox,$acc2,8),$t3
+
+	movzb	`&hi("$s3")`,$acc0
+	mov	12($key),$s3
+	movzb	`&hi("$s0")`,$acc2
+	xor	1($sbox,$acc0,8),$t2
+	mov	0($key),$s0
+	xor	1($sbox,$acc2,8),$t3
+
+	xor	$t0,$s0
+	mov	4($key),$s1
+	mov	8($key),$s2
+	xor	$t2,$s2
+	xor	$t1,$s1
+	xor	$t3,$s3
+___
+}
+
+sub declastvert()
+{ my $t3="%r8d";	# zaps $inp!
+
+$code.=<<___;
+	lea	2048($sbox),$sbox	# size optimization
+	movzb	`&lo("$s0")`,$acc0
+	movzb	`&lo("$s1")`,$acc1
+	movzb	`&lo("$s2")`,$acc2
+	movzb	($sbox,$acc0,1),$t0
+	movzb	($sbox,$acc1,1),$t1
+	movzb	($sbox,$acc2,1),$t2
+
+	movzb	`&lo("$s3")`,$acc0
+	movzb	`&hi("$s3")`,$acc1
+	movzb	`&hi("$s0")`,$acc2
+	movzb	($sbox,$acc0,1),$t3
+	movzb	($sbox,$acc1,1),$acc1	#$t0
+	movzb	($sbox,$acc2,1),$acc2	#$t1
+
+	shl	\$8,$acc1
+	shl	\$8,$acc2
+
+	xor	$acc1,$t0
+	xor	$acc2,$t1
+	shr	\$16,$s3
+
+	movzb	`&hi("$s1")`,$acc0
+	movzb	`&hi("$s2")`,$acc1
+	shr	\$16,$s0
+	movzb	($sbox,$acc0,1),$acc0	#$t2
+	movzb	($sbox,$acc1,1),$acc1	#$t3
+
+	shl	\$8,$acc0
+	shl	\$8,$acc1
+	shr	\$16,$s1
+	xor	$acc0,$t2
+	xor	$acc1,$t3
+	shr	\$16,$s2
+
+	movzb	`&lo("$s2")`,$acc0
+	movzb	`&lo("$s3")`,$acc1
+	movzb	`&lo("$s0")`,$acc2
+	movzb	($sbox,$acc0,1),$acc0	#$t0
+	movzb	($sbox,$acc1,1),$acc1	#$t1
+	movzb	($sbox,$acc2,1),$acc2	#$t2
+
+	shl	\$16,$acc0
+	shl	\$16,$acc1
+	shl	\$16,$acc2
+
+	xor	$acc0,$t0
+	xor	$acc1,$t1
+	xor	$acc2,$t2
+
+	movzb	`&lo("$s1")`,$acc0
+	movzb	`&hi("$s1")`,$acc1
+	movzb	`&hi("$s2")`,$acc2
+	movzb	($sbox,$acc0,1),$acc0	#$t3
+	movzb	($sbox,$acc1,1),$acc1	#$t0
+	movzb	($sbox,$acc2,1),$acc2	#$t1
+
+	shl	\$16,$acc0
+	shl	\$24,$acc1
+	shl	\$24,$acc2
+
+	xor	$acc0,$t3
+	xor	$acc1,$t0
+	xor	$acc2,$t1
+
+	movzb	`&hi("$s3")`,$acc0
+	movzb	`&hi("$s0")`,$acc1
+	mov	16+12($key),$s3
+	movzb	($sbox,$acc0,1),$acc0	#$t2
+	movzb	($sbox,$acc1,1),$acc1	#$t3
+	mov	16+0($key),$s0
+
+	shl	\$24,$acc0
+	shl	\$24,$acc1
+
+	xor	$acc0,$t2
+	xor	$acc1,$t3
+
+	mov	16+4($key),$s1
+	mov	16+8($key),$s2
+	lea	-2048($sbox),$sbox
+	xor	$t0,$s0
+	xor	$t1,$s1
+	xor	$t2,$s2
+	xor	$t3,$s3
+___
+}
+
+sub decstep()
+{ my ($i,@s) = @_;
+  my $tmp0=$acc0;
+  my $tmp1=$acc1;
+  my $tmp2=$acc2;
+  my $out=($t0,$t1,$t2,$s[0])[$i];
+
+	$code.="	mov	$s[0],$out\n"		if ($i!=3);
+			$tmp1=$s[2]			if ($i==3);
+	$code.="	mov	$s[2],$tmp1\n"		if ($i!=3);
+	$code.="	and	\$0xFF,$out\n";
+
+	$code.="	mov	0($sbox,$out,8),$out\n";
+	$code.="	shr	\$16,$tmp1\n";
+			$tmp2=$s[3]			if ($i==3);
+	$code.="	mov	$s[3],$tmp2\n"		if ($i!=3);
+
+			$tmp0=$s[1]			if ($i==3);
+	$code.="	movzb	".&hi($s[1]).",$tmp0\n";
+	$code.="	and	\$0xFF,$tmp1\n";
+	$code.="	shr	\$24,$tmp2\n";
+
+	$code.="	xor	3($sbox,$tmp0,8),$out\n";
+	$code.="	xor	2($sbox,$tmp1,8),$out\n";
+	$code.="	xor	1($sbox,$tmp2,8),$out\n";
+
+	$code.="	mov	$t2,$s[1]\n"		if ($i==3);
+	$code.="	mov	$t1,$s[2]\n"		if ($i==3);
+	$code.="	mov	$t0,$s[3]\n"		if ($i==3);
+	$code.="\n";
+}
+
+sub declast()
+{ my ($i,@s)=@_;
+  my $tmp0=$acc0;
+  my $tmp1=$acc1;
+  my $tmp2=$acc2;
+  my $out=($t0,$t1,$t2,$s[0])[$i];
+
+	$code.="	mov	$s[0],$out\n"		if ($i!=3);
+			$tmp1=$s[2]			if ($i==3);
+	$code.="	mov	$s[2],$tmp1\n"		if ($i!=3);
+	$code.="	and	\$0xFF,$out\n";
+
+	$code.="	movzb	2048($sbox,$out,1),$out\n";
+	$code.="	shr	\$16,$tmp1\n";
+			$tmp2=$s[3]			if ($i==3);
+	$code.="	mov	$s[3],$tmp2\n"		if ($i!=3);
+
+			$tmp0=$s[1]			if ($i==3);
+	$code.="	movzb	".&hi($s[1]).",$tmp0\n";
+	$code.="	and	\$0xFF,$tmp1\n";
+	$code.="	shr	\$24,$tmp2\n";
+
+	$code.="	movzb	2048($sbox,$tmp0,1),$tmp0\n";
+	$code.="	movzb	2048($sbox,$tmp1,1),$tmp1\n";
+	$code.="	movzb	2048($sbox,$tmp2,1),$tmp2\n";
+
+	$code.="	shl	\$8,$tmp0\n";
+	$code.="	shl	\$16,$tmp1\n";
+	$code.="	shl	\$24,$tmp2\n";
+
+	$code.="	xor	$tmp0,$out\n";
+	$code.="	mov	$t2,$s[1]\n"		if ($i==3);
+	$code.="	xor	$tmp1,$out\n";
+	$code.="	mov	$t1,$s[2]\n"		if ($i==3);
+	$code.="	xor	$tmp2,$out\n";
+	$code.="	mov	$t0,$s[3]\n"		if ($i==3);
+	$code.="\n";
+}
+
+$code.=<<___;
+.type	_x86_64_AES_decrypt,\@abi-omnipotent
+.align	16
+_x86_64_AES_decrypt:
+	xor	0($key),$s0			# xor with key
+	xor	4($key),$s1
+	xor	8($key),$s2
+	xor	12($key),$s3
+
+	mov	240($key),$rnds			# load key->rounds
+	sub	\$1,$rnds
+	jmp	.Ldec_loop
+.align	16
+.Ldec_loop:
+___
+	if ($verticalspin) { &decvert(); }
+	else {	&decstep(0,$s0,$s3,$s2,$s1);
+		&decstep(1,$s1,$s0,$s3,$s2);
+		&decstep(2,$s2,$s1,$s0,$s3);
+		&decstep(3,$s3,$s2,$s1,$s0);
+		$code.=<<___;
+		lea	16($key),$key
+		xor	0($key),$s0			# xor with key
+		xor	4($key),$s1
+		xor	8($key),$s2
+		xor	12($key),$s3
+___
+	}
+$code.=<<___;
+	sub	\$1,$rnds
+	jnz	.Ldec_loop
+___
+	if ($verticalspin) { &declastvert(); }
+	else {	&declast(0,$s0,$s3,$s2,$s1);
+		&declast(1,$s1,$s0,$s3,$s2);
+		&declast(2,$s2,$s1,$s0,$s3);
+		&declast(3,$s3,$s2,$s1,$s0);
+		$code.=<<___;
+		xor	16+0($key),$s0			# xor with key
+		xor	16+4($key),$s1
+		xor	16+8($key),$s2
+		xor	16+12($key),$s3
+___
+	}
+$code.=<<___;
+	.byte	0xf3,0xc3			# rep ret
+.size	_x86_64_AES_decrypt,.-_x86_64_AES_decrypt
+___
+
+sub deccompactvert()
+{ my ($t3,$t4,$t5)=("%r8d","%r9d","%r13d");
+
+$code.=<<___;
+	movzb	`&lo("$s0")`,$t0
+	movzb	`&lo("$s1")`,$t1
+	movzb	`&lo("$s2")`,$t2
+	movzb	`&lo("$s3")`,$t3
+	movzb	`&hi("$s3")`,$acc0
+	movzb	`&hi("$s0")`,$acc1
+	shr	\$16,$s3
+	movzb	`&hi("$s1")`,$acc2
+	movzb	($sbox,$t0,1),$t0
+	movzb	($sbox,$t1,1),$t1
+	movzb	($sbox,$t2,1),$t2
+	movzb	($sbox,$t3,1),$t3
+
+	movzb	($sbox,$acc0,1),$t4	#$t0
+	movzb	`&hi("$s2")`,$acc0
+	movzb	($sbox,$acc1,1),$t5	#$t1
+	movzb	($sbox,$acc2,1),$acc2	#$t2
+	movzb	($sbox,$acc0,1),$acc0	#$t3
+
+	shr	\$16,$s2
+	shl	\$8,$t5
+	shl	\$8,$t4
+	movzb	`&lo("$s2")`,$acc1
+	shr	\$16,$s0
+	xor	$t4,$t0
+	shr	\$16,$s1
+	movzb	`&lo("$s3")`,$t4
+
+	shl	\$8,$acc2
+	xor	$t5,$t1
+	shl	\$8,$acc0
+	movzb	`&lo("$s0")`,$t5
+	movzb	($sbox,$acc1,1),$acc1	#$t0
+	xor	$acc2,$t2
+	movzb	`&lo("$s1")`,$acc2
+
+	shl	\$16,$acc1
+	xor	$acc0,$t3
+	movzb	($sbox,$t4,1),$t4	#$t1
+	movzb	`&hi("$s1")`,$acc0
+	movzb	($sbox,$acc2,1),$acc2	#$t3
+	xor	$acc1,$t0
+	movzb	($sbox,$t5,1),$t5	#$t2
+	movzb	`&hi("$s2")`,$acc1
+
+	shl	\$16,$acc2
+	shl	\$16,$t4
+	shl	\$16,$t5
+	xor	$acc2,$t3
+	movzb	`&hi("$s3")`,$acc2
+	xor	$t4,$t1
+	shr	\$8,$s0
+	xor	$t5,$t2
+
+	movzb	($sbox,$acc0,1),$acc0	#$t0
+	movzb	($sbox,$acc1,1),$s1	#$t1
+	movzb	($sbox,$acc2,1),$s2	#$t2
+	movzb	($sbox,$s0,1),$s3	#$t3
+
+	mov	$t0,$s0
+	shl	\$24,$acc0
+	shl	\$24,$s1
+	shl	\$24,$s2
+	xor	$acc0,$s0
+	shl	\$24,$s3
+	xor	$t1,$s1
+	xor	$t2,$s2
+	xor	$t3,$s3
+___
+}
+
+# parallelized version! input is pair of 64-bit values: %rax=s1.s0
+# and %rcx=s3.s2, output is four 32-bit values in %eax=s0, %ebx=s1,
+# %ecx=s2 and %edx=s3.
+sub dectransform()
+{ my ($tp10,$tp20,$tp40,$tp80,$acc0)=("%rax","%r8", "%r9", "%r10","%rbx");
+  my ($tp18,$tp28,$tp48,$tp88,$acc8)=("%rcx","%r11","%r12","%r13","%rdx");
+  my $prefetch = shift;
+
+$code.=<<___;
+	mov	$mask80,$tp40
+	mov	$mask80,$tp48
+	and	$tp10,$tp40
+	and	$tp18,$tp48
+	mov	$tp40,$acc0
+	mov	$tp48,$acc8
+	shr	\$7,$tp40
+	lea	($tp10,$tp10),$tp20
+	shr	\$7,$tp48
+	lea	($tp18,$tp18),$tp28
+	sub	$tp40,$acc0
+	sub	$tp48,$acc8
+	and	$maskfe,$tp20
+	and	$maskfe,$tp28
+	and	$mask1b,$acc0
+	and	$mask1b,$acc8
+	xor	$acc0,$tp20
+	xor	$acc8,$tp28
+	mov	$mask80,$tp80
+	mov	$mask80,$tp88
+
+	and	$tp20,$tp80
+	and	$tp28,$tp88
+	mov	$tp80,$acc0
+	mov	$tp88,$acc8
+	shr	\$7,$tp80
+	lea	($tp20,$tp20),$tp40
+	shr	\$7,$tp88
+	lea	($tp28,$tp28),$tp48
+	sub	$tp80,$acc0
+	sub	$tp88,$acc8
+	and	$maskfe,$tp40
+	and	$maskfe,$tp48
+	and	$mask1b,$acc0
+	and	$mask1b,$acc8
+	xor	$acc0,$tp40
+	xor	$acc8,$tp48
+	mov	$mask80,$tp80
+	mov	$mask80,$tp88
+
+	and	$tp40,$tp80
+	and	$tp48,$tp88
+	mov	$tp80,$acc0
+	mov	$tp88,$acc8
+	shr	\$7,$tp80
+	 xor	$tp10,$tp20		# tp2^=tp1
+	shr	\$7,$tp88
+	 xor	$tp18,$tp28		# tp2^=tp1
+	sub	$tp80,$acc0
+	sub	$tp88,$acc8
+	lea	($tp40,$tp40),$tp80
+	lea	($tp48,$tp48),$tp88
+	 xor	$tp10,$tp40		# tp4^=tp1
+	 xor	$tp18,$tp48		# tp4^=tp1
+	and	$maskfe,$tp80
+	and	$maskfe,$tp88
+	and	$mask1b,$acc0
+	and	$mask1b,$acc8
+	xor	$acc0,$tp80
+	xor	$acc8,$tp88
+
+	xor	$tp80,$tp10		# tp1^=tp8
+	xor	$tp88,$tp18		# tp1^=tp8
+	xor	$tp80,$tp20		# tp2^tp1^=tp8
+	xor	$tp88,$tp28		# tp2^tp1^=tp8
+	mov	$tp10,$acc0
+	mov	$tp18,$acc8
+	xor	$tp80,$tp40		# tp4^tp1^=tp8
+	shr	\$32,$acc0
+	xor	$tp88,$tp48		# tp4^tp1^=tp8
+	shr	\$32,$acc8
+	xor	$tp20,$tp80		# tp8^=tp8^tp2^tp1=tp2^tp1
+	rol	\$8,`&LO("$tp10")`	# ROTATE(tp1^tp8,8)
+	xor	$tp28,$tp88		# tp8^=tp8^tp2^tp1=tp2^tp1
+	rol	\$8,`&LO("$tp18")`	# ROTATE(tp1^tp8,8)
+	xor	$tp40,$tp80		# tp2^tp1^=tp8^tp4^tp1=tp8^tp4^tp2
+	rol	\$8,`&LO("$acc0")`	# ROTATE(tp1^tp8,8)
+	xor	$tp48,$tp88		# tp2^tp1^=tp8^tp4^tp1=tp8^tp4^tp2
+
+	rol	\$8,`&LO("$acc8")`	# ROTATE(tp1^tp8,8)
+	xor	`&LO("$tp80")`,`&LO("$tp10")`
+	shr	\$32,$tp80
+	xor	`&LO("$tp88")`,`&LO("$tp18")`
+	shr	\$32,$tp88
+	xor	`&LO("$tp80")`,`&LO("$acc0")`
+	xor	`&LO("$tp88")`,`&LO("$acc8")`
+
+	mov	$tp20,$tp80
+	rol	\$24,`&LO("$tp20")`	# ROTATE(tp2^tp1^tp8,24)
+	mov	$tp28,$tp88
+	rol	\$24,`&LO("$tp28")`	# ROTATE(tp2^tp1^tp8,24)
+	shr	\$32,$tp80
+	xor	`&LO("$tp20")`,`&LO("$tp10")`
+	shr	\$32,$tp88
+	xor	`&LO("$tp28")`,`&LO("$tp18")`
+	rol	\$24,`&LO("$tp80")`	# ROTATE(tp2^tp1^tp8,24)
+	mov	$tp40,$tp20
+	rol	\$24,`&LO("$tp88")`	# ROTATE(tp2^tp1^tp8,24)
+	mov	$tp48,$tp28
+	shr	\$32,$tp20
+	xor	`&LO("$tp80")`,`&LO("$acc0")`
+	shr	\$32,$tp28
+	xor	`&LO("$tp88")`,`&LO("$acc8")`
+
+	`"mov	0($sbox),$mask80"	if ($prefetch)`
+	rol	\$16,`&LO("$tp40")`	# ROTATE(tp4^tp1^tp8,16)
+	`"mov	64($sbox),$maskfe"	if ($prefetch)`
+	rol	\$16,`&LO("$tp48")`	# ROTATE(tp4^tp1^tp8,16)
+	`"mov	128($sbox),$mask1b"	if ($prefetch)`
+	rol	\$16,`&LO("$tp20")`	# ROTATE(tp4^tp1^tp8,16)
+	`"mov	192($sbox),$tp80"	if ($prefetch)`
+	xor	`&LO("$tp40")`,`&LO("$tp10")`
+	rol	\$16,`&LO("$tp28")`	# ROTATE(tp4^tp1^tp8,16)
+	xor	`&LO("$tp48")`,`&LO("$tp18")`
+	`"mov	256($sbox),$tp88"	if ($prefetch)`
+	xor	`&LO("$tp20")`,`&LO("$acc0")`
+	xor	`&LO("$tp28")`,`&LO("$acc8")`
+___
+}
+
+$code.=<<___;
+.type	_x86_64_AES_decrypt_compact,\@abi-omnipotent
+.align	16
+_x86_64_AES_decrypt_compact:
+	lea	128($sbox),$inp			# size optimization
+	mov	0-128($inp),$acc1		# prefetch Td4
+	mov	32-128($inp),$acc2
+	mov	64-128($inp),$t0
+	mov	96-128($inp),$t1
+	mov	128-128($inp),$acc1
+	mov	160-128($inp),$acc2
+	mov	192-128($inp),$t0
+	mov	224-128($inp),$t1
+	jmp	.Ldec_loop_compact
+
+.align	16
+.Ldec_loop_compact:
+		xor	0($key),$s0		# xor with key
+		xor	4($key),$s1
+		xor	8($key),$s2
+		xor	12($key),$s3
+		lea	16($key),$key
+___
+		&deccompactvert();
+$code.=<<___;
+		cmp	16(%rsp),$key
+		je	.Ldec_compact_done
+
+		mov	256+0($sbox),$mask80
+		shl	\$32,%rbx
+		shl	\$32,%rdx
+		mov	256+8($sbox),$maskfe
+		or	%rbx,%rax
+		or	%rdx,%rcx
+		mov	256+16($sbox),$mask1b
+___
+		&dectransform(1);
+$code.=<<___;
+	jmp	.Ldec_loop_compact
+.align	16
+.Ldec_compact_done:
+	xor	0($key),$s0
+	xor	4($key),$s1
+	xor	8($key),$s2
+	xor	12($key),$s3
+	.byte	0xf3,0xc3			# rep ret
+.size	_x86_64_AES_decrypt_compact,.-_x86_64_AES_decrypt_compact
+___
+
+# void AES_decrypt (const void *inp,void *out,const AES_KEY *key);
+$code.=<<___;
+.globl	AES_decrypt
+.type	AES_decrypt,\@function,3
+.align	16
+.globl	asm_AES_decrypt
+.hidden	asm_AES_decrypt
+asm_AES_decrypt:
+AES_decrypt:
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+
+	# allocate frame "above" key schedule
+	mov	%rsp,%r10
+	lea	-63(%rdx),%rcx	# %rdx is key argument
+	and	\$-64,%rsp
+	sub	%rsp,%rcx
+	neg	%rcx
+	and	\$0x3c0,%rcx
+	sub	%rcx,%rsp
+	sub	\$32,%rsp
+
+	mov	%rsi,16(%rsp)	# save out
+	mov	%r10,24(%rsp)	# save real stack pointer
+.Ldec_prologue:
+
+	mov	%rdx,$key
+	mov	240($key),$rnds	# load rounds
+
+	mov	0(%rdi),$s0	# load input vector
+	mov	4(%rdi),$s1
+	mov	8(%rdi),$s2
+	mov	12(%rdi),$s3
+
+	shl	\$4,$rnds
+	lea	($key,$rnds),%rbp
+	mov	$key,(%rsp)	# key schedule
+	mov	%rbp,8(%rsp)	# end of key schedule
+
+	# pick Td4 copy which can't "overlap" with stack frame or key schedule
+	lea	.LAES_Td+2048(%rip),$sbox
+	lea	768(%rsp),%rbp
+	sub	$sbox,%rbp
+	and	\$0x300,%rbp
+	lea	($sbox,%rbp),$sbox
+	shr	\$3,%rbp	# recall "magic" constants!
+	add	%rbp,$sbox
+
+	call	_x86_64_AES_decrypt_compact
+
+	mov	16(%rsp),$out	# restore out
+	mov	24(%rsp),%rsi	# restore saved stack pointer
+	mov	$s0,0($out)	# write output vector
+	mov	$s1,4($out)
+	mov	$s2,8($out)
+	mov	$s3,12($out)
+
+	mov	(%rsi),%r15
+	mov	8(%rsi),%r14
+	mov	16(%rsi),%r13
+	mov	24(%rsi),%r12
+	mov	32(%rsi),%rbp
+	mov	40(%rsi),%rbx
+	lea	48(%rsi),%rsp
+.Ldec_epilogue:
+	ret
+.size	AES_decrypt,.-AES_decrypt
+___
+#------------------------------------------------------------------#
+
+sub enckey()
+{
+$code.=<<___;
+	movz	%dl,%esi		# rk[i]>>0
+	movzb	-128(%rbp,%rsi),%ebx
+	movz	%dh,%esi		# rk[i]>>8
+	shl	\$24,%ebx
+	xor	%ebx,%eax
+
+	movzb	-128(%rbp,%rsi),%ebx
+	shr	\$16,%edx
+	movz	%dl,%esi		# rk[i]>>16
+	xor	%ebx,%eax
+
+	movzb	-128(%rbp,%rsi),%ebx
+	movz	%dh,%esi		# rk[i]>>24
+	shl	\$8,%ebx
+	xor	%ebx,%eax
+
+	movzb	-128(%rbp,%rsi),%ebx
+	shl	\$16,%ebx
+	xor	%ebx,%eax
+
+	xor	1024-128(%rbp,%rcx,4),%eax		# rcon
+___
+}
+
+# int AES_set_encrypt_key(const unsigned char *userKey, const int bits,
+#                        AES_KEY *key)
+$code.=<<___;
+.globl	AES_set_encrypt_key
+.type	AES_set_encrypt_key,\@function,3
+.align	16
+AES_set_encrypt_key:
+	push	%rbx
+	push	%rbp
+	push	%r12			# redundant, but allows to share 
+	push	%r13			# exception handler...
+	push	%r14
+	push	%r15
+	sub	\$8,%rsp
+.Lenc_key_prologue:
+
+	call	_x86_64_AES_set_encrypt_key
+
+	mov	40(%rsp),%rbp
+	mov	48(%rsp),%rbx
+	add	\$56,%rsp
+.Lenc_key_epilogue:
+	ret
+.size	AES_set_encrypt_key,.-AES_set_encrypt_key
+
+.type	_x86_64_AES_set_encrypt_key,\@abi-omnipotent
+.align	16
+_x86_64_AES_set_encrypt_key:
+	mov	%esi,%ecx			# %ecx=bits
+	mov	%rdi,%rsi			# %rsi=userKey
+	mov	%rdx,%rdi			# %rdi=key
+
+	test	\$-1,%rsi
+	jz	.Lbadpointer
+	test	\$-1,%rdi
+	jz	.Lbadpointer
+
+	lea	.LAES_Te(%rip),%rbp
+	lea	2048+128(%rbp),%rbp
+
+	# prefetch Te4
+	mov	0-128(%rbp),%eax
+	mov	32-128(%rbp),%ebx
+	mov	64-128(%rbp),%r8d
+	mov	96-128(%rbp),%edx
+	mov	128-128(%rbp),%eax
+	mov	160-128(%rbp),%ebx
+	mov	192-128(%rbp),%r8d
+	mov	224-128(%rbp),%edx
+
+	cmp	\$128,%ecx
+	je	.L10rounds
+	cmp	\$192,%ecx
+	je	.L12rounds
+	cmp	\$256,%ecx
+	je	.L14rounds
+	mov	\$-2,%rax			# invalid number of bits
+	jmp	.Lexit
+
+.L10rounds:
+	mov	0(%rsi),%rax			# copy first 4 dwords
+	mov	8(%rsi),%rdx
+	mov	%rax,0(%rdi)
+	mov	%rdx,8(%rdi)
+
+	shr	\$32,%rdx
+	xor	%ecx,%ecx
+	jmp	.L10shortcut
+.align	4
+.L10loop:
+		mov	0(%rdi),%eax			# rk[0]
+		mov	12(%rdi),%edx			# rk[3]
+.L10shortcut:
+___
+		&enckey	();
+$code.=<<___;
+		mov	%eax,16(%rdi)			# rk[4]
+		xor	4(%rdi),%eax
+		mov	%eax,20(%rdi)			# rk[5]
+		xor	8(%rdi),%eax
+		mov	%eax,24(%rdi)			# rk[6]
+		xor	12(%rdi),%eax
+		mov	%eax,28(%rdi)			# rk[7]
+		add	\$1,%ecx
+		lea	16(%rdi),%rdi
+		cmp	\$10,%ecx
+	jl	.L10loop
+
+	movl	\$10,80(%rdi)			# setup number of rounds
+	xor	%rax,%rax
+	jmp	.Lexit
+
+.L12rounds:
+	mov	0(%rsi),%rax			# copy first 6 dwords
+	mov	8(%rsi),%rbx
+	mov	16(%rsi),%rdx
+	mov	%rax,0(%rdi)
+	mov	%rbx,8(%rdi)
+	mov	%rdx,16(%rdi)
+
+	shr	\$32,%rdx
+	xor	%ecx,%ecx
+	jmp	.L12shortcut
+.align	4
+.L12loop:
+		mov	0(%rdi),%eax			# rk[0]
+		mov	20(%rdi),%edx			# rk[5]
+.L12shortcut:
+___
+		&enckey	();
+$code.=<<___;
+		mov	%eax,24(%rdi)			# rk[6]
+		xor	4(%rdi),%eax
+		mov	%eax,28(%rdi)			# rk[7]
+		xor	8(%rdi),%eax
+		mov	%eax,32(%rdi)			# rk[8]
+		xor	12(%rdi),%eax
+		mov	%eax,36(%rdi)			# rk[9]
+
+		cmp	\$7,%ecx
+		je	.L12break
+		add	\$1,%ecx
+
+		xor	16(%rdi),%eax
+		mov	%eax,40(%rdi)			# rk[10]
+		xor	20(%rdi),%eax
+		mov	%eax,44(%rdi)			# rk[11]
+
+		lea	24(%rdi),%rdi
+	jmp	.L12loop
+.L12break:
+	movl	\$12,72(%rdi)		# setup number of rounds
+	xor	%rax,%rax
+	jmp	.Lexit
+
+.L14rounds:		
+	mov	0(%rsi),%rax			# copy first 8 dwords
+	mov	8(%rsi),%rbx
+	mov	16(%rsi),%rcx
+	mov	24(%rsi),%rdx
+	mov	%rax,0(%rdi)
+	mov	%rbx,8(%rdi)
+	mov	%rcx,16(%rdi)
+	mov	%rdx,24(%rdi)
+
+	shr	\$32,%rdx
+	xor	%ecx,%ecx
+	jmp	.L14shortcut
+.align	4
+.L14loop:
+		mov	0(%rdi),%eax			# rk[0]
+		mov	28(%rdi),%edx			# rk[4]
+.L14shortcut:
+___
+		&enckey	();
+$code.=<<___;
+		mov	%eax,32(%rdi)			# rk[8]
+		xor	4(%rdi),%eax
+		mov	%eax,36(%rdi)			# rk[9]
+		xor	8(%rdi),%eax
+		mov	%eax,40(%rdi)			# rk[10]
+		xor	12(%rdi),%eax
+		mov	%eax,44(%rdi)			# rk[11]
+
+		cmp	\$6,%ecx
+		je	.L14break
+		add	\$1,%ecx
+
+		mov	%eax,%edx
+		mov	16(%rdi),%eax			# rk[4]
+		movz	%dl,%esi			# rk[11]>>0
+		movzb	-128(%rbp,%rsi),%ebx
+		movz	%dh,%esi			# rk[11]>>8
+		xor	%ebx,%eax
+
+		movzb	-128(%rbp,%rsi),%ebx
+		shr	\$16,%edx
+		shl	\$8,%ebx
+		movz	%dl,%esi			# rk[11]>>16
+		xor	%ebx,%eax
+
+		movzb	-128(%rbp,%rsi),%ebx
+		movz	%dh,%esi			# rk[11]>>24
+		shl	\$16,%ebx
+		xor	%ebx,%eax
+
+		movzb	-128(%rbp,%rsi),%ebx
+		shl	\$24,%ebx
+		xor	%ebx,%eax
+
+		mov	%eax,48(%rdi)			# rk[12]
+		xor	20(%rdi),%eax
+		mov	%eax,52(%rdi)			# rk[13]
+		xor	24(%rdi),%eax
+		mov	%eax,56(%rdi)			# rk[14]
+		xor	28(%rdi),%eax
+		mov	%eax,60(%rdi)			# rk[15]
+
+		lea	32(%rdi),%rdi
+	jmp	.L14loop
+.L14break:
+	movl	\$14,48(%rdi)		# setup number of rounds
+	xor	%rax,%rax
+	jmp	.Lexit
+
+.Lbadpointer:
+	mov	\$-1,%rax
+.Lexit:
+	.byte	0xf3,0xc3			# rep ret
+.size	_x86_64_AES_set_encrypt_key,.-_x86_64_AES_set_encrypt_key
+___
+
+sub deckey_ref()
+{ my ($i,$ptr,$te,$td) = @_;
+  my ($tp1,$tp2,$tp4,$tp8,$acc)=("%eax","%ebx","%edi","%edx","%r8d");
+$code.=<<___;
+	mov	$i($ptr),$tp1
+	mov	$tp1,$acc
+	and	\$0x80808080,$acc
+	mov	$acc,$tp4
+	shr	\$7,$tp4
+	lea	0($tp1,$tp1),$tp2
+	sub	$tp4,$acc
+	and	\$0xfefefefe,$tp2
+	and	\$0x1b1b1b1b,$acc
+	xor	$tp2,$acc
+	mov	$acc,$tp2
+
+	and	\$0x80808080,$acc
+	mov	$acc,$tp8
+	shr	\$7,$tp8
+	lea	0($tp2,$tp2),$tp4
+	sub	$tp8,$acc
+	and	\$0xfefefefe,$tp4
+	and	\$0x1b1b1b1b,$acc
+	 xor	$tp1,$tp2		# tp2^tp1
+	xor	$tp4,$acc
+	mov	$acc,$tp4
+
+	and	\$0x80808080,$acc
+	mov	$acc,$tp8
+	shr	\$7,$tp8
+	sub	$tp8,$acc
+	lea	0($tp4,$tp4),$tp8
+	 xor	$tp1,$tp4		# tp4^tp1
+	and	\$0xfefefefe,$tp8
+	and	\$0x1b1b1b1b,$acc
+	xor	$acc,$tp8
+
+	xor	$tp8,$tp1		# tp1^tp8
+	rol	\$8,$tp1		# ROTATE(tp1^tp8,8)
+	xor	$tp8,$tp2		# tp2^tp1^tp8
+	xor	$tp8,$tp4		# tp4^tp1^tp8
+	xor	$tp2,$tp8
+	xor	$tp4,$tp8		# tp8^(tp8^tp4^tp1)^(tp8^tp2^tp1)=tp8^tp4^tp2
+
+	xor	$tp8,$tp1
+	rol	\$24,$tp2		# ROTATE(tp2^tp1^tp8,24)
+	xor	$tp2,$tp1
+	rol	\$16,$tp4		# ROTATE(tp4^tp1^tp8,16)
+	xor	$tp4,$tp1
+
+	mov	$tp1,$i($ptr)
+___
+}
+
+# int AES_set_decrypt_key(const unsigned char *userKey, const int bits,
+#                        AES_KEY *key)
+$code.=<<___;
+.globl	AES_set_decrypt_key
+.type	AES_set_decrypt_key,\@function,3
+.align	16
+AES_set_decrypt_key:
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+	push	%rdx			# save key schedule
+.Ldec_key_prologue:
+
+	call	_x86_64_AES_set_encrypt_key
+	mov	(%rsp),%r8		# restore key schedule
+	cmp	\$0,%eax
+	jne	.Labort
+
+	mov	240(%r8),%r14d		# pull number of rounds
+	xor	%rdi,%rdi
+	lea	(%rdi,%r14d,4),%rcx
+	mov	%r8,%rsi
+	lea	(%r8,%rcx,4),%rdi	# pointer to last chunk
+.align	4
+.Linvert:
+		mov	0(%rsi),%rax
+		mov	8(%rsi),%rbx
+		mov	0(%rdi),%rcx
+		mov	8(%rdi),%rdx
+		mov	%rax,0(%rdi)
+		mov	%rbx,8(%rdi)
+		mov	%rcx,0(%rsi)
+		mov	%rdx,8(%rsi)
+		lea	16(%rsi),%rsi
+		lea	-16(%rdi),%rdi
+		cmp	%rsi,%rdi
+	jne	.Linvert
+
+	lea	.LAES_Te+2048+1024(%rip),%rax	# rcon
+
+	mov	40(%rax),$mask80
+	mov	48(%rax),$maskfe
+	mov	56(%rax),$mask1b
+
+	mov	%r8,$key
+	sub	\$1,%r14d
+.align	4
+.Lpermute:
+		lea	16($key),$key
+		mov	0($key),%rax
+		mov	8($key),%rcx
+___
+		&dectransform ();
+$code.=<<___;
+		mov	%eax,0($key)
+		mov	%ebx,4($key)
+		mov	%ecx,8($key)
+		mov	%edx,12($key)
+		sub	\$1,%r14d
+	jnz	.Lpermute
+
+	xor	%rax,%rax
+.Labort:
+	mov	8(%rsp),%r15
+	mov	16(%rsp),%r14
+	mov	24(%rsp),%r13
+	mov	32(%rsp),%r12
+	mov	40(%rsp),%rbp
+	mov	48(%rsp),%rbx
+	add	\$56,%rsp
+.Ldec_key_epilogue:
+	ret
+.size	AES_set_decrypt_key,.-AES_set_decrypt_key
+___
+
+# void AES_cbc_encrypt (const void char *inp, unsigned char *out,
+#			size_t length, const AES_KEY *key,
+#			unsigned char *ivp,const int enc);
+{
+# stack frame layout
+# -8(%rsp)		return address
+my $keyp="0(%rsp)";		# one to pass as $key
+my $keyend="8(%rsp)";		# &(keyp->rd_key[4*keyp->rounds])
+my $_rsp="16(%rsp)";		# saved %rsp
+my $_inp="24(%rsp)";		# copy of 1st parameter, inp
+my $_out="32(%rsp)";		# copy of 2nd parameter, out
+my $_len="40(%rsp)";		# copy of 3rd parameter, length
+my $_key="48(%rsp)";		# copy of 4th parameter, key
+my $_ivp="56(%rsp)";		# copy of 5th parameter, ivp
+my $ivec="64(%rsp)";		# ivec[16]
+my $aes_key="80(%rsp)";		# copy of aes_key
+my $mark="80+240(%rsp)";	# copy of aes_key->rounds
+
+$code.=<<___;
+.globl	AES_cbc_encrypt
+.type	AES_cbc_encrypt,\@function,6
+.align	16
+.extern	OPENSSL_ia32cap_P
+.globl	asm_AES_cbc_encrypt
+.hidden	asm_AES_cbc_encrypt
+asm_AES_cbc_encrypt:
+AES_cbc_encrypt:
+	cmp	\$0,%rdx	# check length
+	je	.Lcbc_epilogue
+	pushfq
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+.Lcbc_prologue:
+
+	cld
+	mov	%r9d,%r9d	# clear upper half of enc
+
+	lea	.LAES_Te(%rip),$sbox
+	cmp	\$0,%r9
+	jne	.Lcbc_picked_te
+	lea	.LAES_Td(%rip),$sbox
+.Lcbc_picked_te:
+
+	mov	OPENSSL_ia32cap_P(%rip),%r10d
+	cmp	\$$speed_limit,%rdx
+	jb	.Lcbc_slow_prologue
+	test	\$15,%rdx
+	jnz	.Lcbc_slow_prologue
+	bt	\$28,%r10d
+	jc	.Lcbc_slow_prologue
+
+	# allocate aligned stack frame...
+	lea	-88-248(%rsp),$key
+	and	\$-64,$key
+
+	# ... and make sure it doesn't alias with AES_T[ed] modulo 4096
+	mov	$sbox,%r10
+	lea	2304($sbox),%r11
+	mov	$key,%r12
+	and	\$0xFFF,%r10	# s = $sbox&0xfff
+	and	\$0xFFF,%r11	# e = ($sbox+2048)&0xfff
+	and	\$0xFFF,%r12	# p = %rsp&0xfff
+
+	cmp	%r11,%r12	# if (p=>e) %rsp =- (p-e);
+	jb	.Lcbc_te_break_out
+	sub	%r11,%r12
+	sub	%r12,$key
+	jmp	.Lcbc_te_ok
+.Lcbc_te_break_out:		# else %rsp -= (p-s)&0xfff + framesz
+	sub	%r10,%r12
+	and	\$0xFFF,%r12
+	add	\$320,%r12
+	sub	%r12,$key
+.align	4
+.Lcbc_te_ok:
+
+	xchg	%rsp,$key
+	#add	\$8,%rsp	# reserve for return address!
+	mov	$key,$_rsp	# save %rsp
+.Lcbc_fast_body:
+	mov	%rdi,$_inp	# save copy of inp
+	mov	%rsi,$_out	# save copy of out
+	mov	%rdx,$_len	# save copy of len
+	mov	%rcx,$_key	# save copy of key
+	mov	%r8,$_ivp	# save copy of ivp
+	movl	\$0,$mark	# copy of aes_key->rounds = 0;
+	mov	%r8,%rbp	# rearrange input arguments
+	mov	%r9,%rbx
+	mov	%rsi,$out
+	mov	%rdi,$inp
+	mov	%rcx,$key
+
+	mov	240($key),%eax		# key->rounds
+	# do we copy key schedule to stack?
+	mov	$key,%r10
+	sub	$sbox,%r10
+	and	\$0xfff,%r10
+	cmp	\$2304,%r10
+	jb	.Lcbc_do_ecopy
+	cmp	\$4096-248,%r10
+	jb	.Lcbc_skip_ecopy
+.align	4
+.Lcbc_do_ecopy:
+		mov	$key,%rsi
+		lea	$aes_key,%rdi
+		lea	$aes_key,$key
+		mov	\$240/8,%ecx
+		.long	0x90A548F3	# rep movsq
+		mov	%eax,(%rdi)	# copy aes_key->rounds
+.Lcbc_skip_ecopy:
+	mov	$key,$keyp	# save key pointer
+
+	mov	\$18,%ecx
+.align	4
+.Lcbc_prefetch_te:
+		mov	0($sbox),%r10
+		mov	32($sbox),%r11
+		mov	64($sbox),%r12
+		mov	96($sbox),%r13
+		lea	128($sbox),$sbox
+		sub	\$1,%ecx
+	jnz	.Lcbc_prefetch_te
+	lea	-2304($sbox),$sbox
+
+	cmp	\$0,%rbx
+	je	.LFAST_DECRYPT
+
+#----------------------------- ENCRYPT -----------------------------#
+	mov	0(%rbp),$s0		# load iv
+	mov	4(%rbp),$s1
+	mov	8(%rbp),$s2
+	mov	12(%rbp),$s3
+
+.align	4
+.Lcbc_fast_enc_loop:
+		xor	0($inp),$s0
+		xor	4($inp),$s1
+		xor	8($inp),$s2
+		xor	12($inp),$s3
+		mov	$keyp,$key	# restore key
+		mov	$inp,$_inp	# if ($verticalspin) save inp
+
+		call	_x86_64_AES_encrypt
+
+		mov	$_inp,$inp	# if ($verticalspin) restore inp
+		mov	$_len,%r10
+		mov	$s0,0($out)
+		mov	$s1,4($out)
+		mov	$s2,8($out)
+		mov	$s3,12($out)
+
+		lea	16($inp),$inp
+		lea	16($out),$out
+		sub	\$16,%r10
+		test	\$-16,%r10
+		mov	%r10,$_len
+	jnz	.Lcbc_fast_enc_loop
+	mov	$_ivp,%rbp	# restore ivp
+	mov	$s0,0(%rbp)	# save ivec
+	mov	$s1,4(%rbp)
+	mov	$s2,8(%rbp)
+	mov	$s3,12(%rbp)
+
+	jmp	.Lcbc_fast_cleanup
+
+#----------------------------- DECRYPT -----------------------------#
+.align	16
+.LFAST_DECRYPT:
+	cmp	$inp,$out
+	je	.Lcbc_fast_dec_in_place
+
+	mov	%rbp,$ivec
+.align	4
+.Lcbc_fast_dec_loop:
+		mov	0($inp),$s0	# read input
+		mov	4($inp),$s1
+		mov	8($inp),$s2
+		mov	12($inp),$s3
+		mov	$keyp,$key	# restore key
+		mov	$inp,$_inp	# if ($verticalspin) save inp
+
+		call	_x86_64_AES_decrypt
+
+		mov	$ivec,%rbp	# load ivp
+		mov	$_inp,$inp	# if ($verticalspin) restore inp
+		mov	$_len,%r10	# load len
+		xor	0(%rbp),$s0	# xor iv
+		xor	4(%rbp),$s1
+		xor	8(%rbp),$s2
+		xor	12(%rbp),$s3
+		mov	$inp,%rbp	# current input, next iv
+
+		sub	\$16,%r10
+		mov	%r10,$_len	# update len
+		mov	%rbp,$ivec	# update ivp
+
+		mov	$s0,0($out)	# write output
+		mov	$s1,4($out)
+		mov	$s2,8($out)
+		mov	$s3,12($out)
+
+		lea	16($inp),$inp
+		lea	16($out),$out
+	jnz	.Lcbc_fast_dec_loop
+	mov	$_ivp,%r12		# load user ivp
+	mov	0(%rbp),%r10		# load iv
+	mov	8(%rbp),%r11
+	mov	%r10,0(%r12)		# copy back to user
+	mov	%r11,8(%r12)
+	jmp	.Lcbc_fast_cleanup
+
+.align	16
+.Lcbc_fast_dec_in_place:
+	mov	0(%rbp),%r10		# copy iv to stack
+	mov	8(%rbp),%r11
+	mov	%r10,0+$ivec
+	mov	%r11,8+$ivec
+.align	4
+.Lcbc_fast_dec_in_place_loop:
+		mov	0($inp),$s0	# load input
+		mov	4($inp),$s1
+		mov	8($inp),$s2
+		mov	12($inp),$s3
+		mov	$keyp,$key	# restore key
+		mov	$inp,$_inp	# if ($verticalspin) save inp
+
+		call	_x86_64_AES_decrypt
+
+		mov	$_inp,$inp	# if ($verticalspin) restore inp
+		mov	$_len,%r10
+		xor	0+$ivec,$s0
+		xor	4+$ivec,$s1
+		xor	8+$ivec,$s2
+		xor	12+$ivec,$s3
+
+		mov	0($inp),%r11	# load input
+		mov	8($inp),%r12
+		sub	\$16,%r10
+		jz	.Lcbc_fast_dec_in_place_done
+
+		mov	%r11,0+$ivec	# copy input to iv
+		mov	%r12,8+$ivec
+
+		mov	$s0,0($out)	# save output [zaps input]
+		mov	$s1,4($out)
+		mov	$s2,8($out)
+		mov	$s3,12($out)
+
+		lea	16($inp),$inp
+		lea	16($out),$out
+		mov	%r10,$_len
+	jmp	.Lcbc_fast_dec_in_place_loop
+.Lcbc_fast_dec_in_place_done:
+	mov	$_ivp,%rdi
+	mov	%r11,0(%rdi)	# copy iv back to user
+	mov	%r12,8(%rdi)
+
+	mov	$s0,0($out)	# save output [zaps input]
+	mov	$s1,4($out)
+	mov	$s2,8($out)
+	mov	$s3,12($out)
+
+.align	4
+.Lcbc_fast_cleanup:
+	cmpl	\$0,$mark	# was the key schedule copied?
+	lea	$aes_key,%rdi
+	je	.Lcbc_exit
+		mov	\$240/8,%ecx
+		xor	%rax,%rax
+		.long	0x90AB48F3	# rep stosq
+
+	jmp	.Lcbc_exit
+
+#--------------------------- SLOW ROUTINE ---------------------------#
+.align	16
+.Lcbc_slow_prologue:
+	# allocate aligned stack frame...
+	lea	-88(%rsp),%rbp
+	and	\$-64,%rbp
+	# ... just "above" key schedule
+	lea	-88-63(%rcx),%r10
+	sub	%rbp,%r10
+	neg	%r10
+	and	\$0x3c0,%r10
+	sub	%r10,%rbp
+
+	xchg	%rsp,%rbp
+	#add	\$8,%rsp	# reserve for return address!
+	mov	%rbp,$_rsp	# save %rsp
+.Lcbc_slow_body:
+	#mov	%rdi,$_inp	# save copy of inp
+	#mov	%rsi,$_out	# save copy of out
+	#mov	%rdx,$_len	# save copy of len
+	#mov	%rcx,$_key	# save copy of key
+	mov	%r8,$_ivp	# save copy of ivp
+	mov	%r8,%rbp	# rearrange input arguments
+	mov	%r9,%rbx
+	mov	%rsi,$out
+	mov	%rdi,$inp
+	mov	%rcx,$key
+	mov	%rdx,%r10
+
+	mov	240($key),%eax
+	mov	$key,$keyp	# save key pointer
+	shl	\$4,%eax
+	lea	($key,%rax),%rax
+	mov	%rax,$keyend
+
+	# pick Te4 copy which can't "overlap" with stack frame or key scdedule
+	lea	2048($sbox),$sbox
+	lea	768-8(%rsp),%rax
+	sub	$sbox,%rax
+	and	\$0x300,%rax
+	lea	($sbox,%rax),$sbox
+
+	cmp	\$0,%rbx
+	je	.LSLOW_DECRYPT
+
+#--------------------------- SLOW ENCRYPT ---------------------------#
+	test	\$-16,%r10		# check upon length
+	mov	0(%rbp),$s0		# load iv
+	mov	4(%rbp),$s1
+	mov	8(%rbp),$s2
+	mov	12(%rbp),$s3
+	jz	.Lcbc_slow_enc_tail	# short input...
+
+.align	4
+.Lcbc_slow_enc_loop:
+		xor	0($inp),$s0
+		xor	4($inp),$s1
+		xor	8($inp),$s2
+		xor	12($inp),$s3
+		mov	$keyp,$key	# restore key
+		mov	$inp,$_inp	# save inp
+		mov	$out,$_out	# save out
+		mov	%r10,$_len	# save len
+
+		call	_x86_64_AES_encrypt_compact
+
+		mov	$_inp,$inp	# restore inp
+		mov	$_out,$out	# restore out
+		mov	$_len,%r10	# restore len
+		mov	$s0,0($out)
+		mov	$s1,4($out)
+		mov	$s2,8($out)
+		mov	$s3,12($out)
+
+		lea	16($inp),$inp
+		lea	16($out),$out
+		sub	\$16,%r10
+		test	\$-16,%r10
+	jnz	.Lcbc_slow_enc_loop
+	test	\$15,%r10
+	jnz	.Lcbc_slow_enc_tail
+	mov	$_ivp,%rbp	# restore ivp
+	mov	$s0,0(%rbp)	# save ivec
+	mov	$s1,4(%rbp)
+	mov	$s2,8(%rbp)
+	mov	$s3,12(%rbp)
+
+	jmp	.Lcbc_exit
+
+.align	4
+.Lcbc_slow_enc_tail:
+	mov	%rax,%r11
+	mov	%rcx,%r12
+	mov	%r10,%rcx
+	mov	$inp,%rsi
+	mov	$out,%rdi
+	.long	0x9066A4F3		# rep movsb
+	mov	\$16,%rcx		# zero tail
+	sub	%r10,%rcx
+	xor	%rax,%rax
+	.long	0x9066AAF3		# rep stosb
+	mov	$out,$inp		# this is not a mistake!
+	mov	\$16,%r10		# len=16
+	mov	%r11,%rax
+	mov	%r12,%rcx
+	jmp	.Lcbc_slow_enc_loop	# one more spin...
+#--------------------------- SLOW DECRYPT ---------------------------#
+.align	16
+.LSLOW_DECRYPT:
+	shr	\$3,%rax
+	add	%rax,$sbox		# recall "magic" constants!
+
+	mov	0(%rbp),%r11		# copy iv to stack
+	mov	8(%rbp),%r12
+	mov	%r11,0+$ivec
+	mov	%r12,8+$ivec
+
+.align	4
+.Lcbc_slow_dec_loop:
+		mov	0($inp),$s0	# load input
+		mov	4($inp),$s1
+		mov	8($inp),$s2
+		mov	12($inp),$s3
+		mov	$keyp,$key	# restore key
+		mov	$inp,$_inp	# save inp
+		mov	$out,$_out	# save out
+		mov	%r10,$_len	# save len
+
+		call	_x86_64_AES_decrypt_compact
+
+		mov	$_inp,$inp	# restore inp
+		mov	$_out,$out	# restore out
+		mov	$_len,%r10
+		xor	0+$ivec,$s0
+		xor	4+$ivec,$s1
+		xor	8+$ivec,$s2
+		xor	12+$ivec,$s3
+
+		mov	0($inp),%r11	# load input
+		mov	8($inp),%r12
+		sub	\$16,%r10
+		jc	.Lcbc_slow_dec_partial
+		jz	.Lcbc_slow_dec_done
+
+		mov	%r11,0+$ivec	# copy input to iv
+		mov	%r12,8+$ivec
+
+		mov	$s0,0($out)	# save output [can zap input]
+		mov	$s1,4($out)
+		mov	$s2,8($out)
+		mov	$s3,12($out)
+
+		lea	16($inp),$inp
+		lea	16($out),$out
+	jmp	.Lcbc_slow_dec_loop
+.Lcbc_slow_dec_done:
+	mov	$_ivp,%rdi
+	mov	%r11,0(%rdi)		# copy iv back to user
+	mov	%r12,8(%rdi)
+
+	mov	$s0,0($out)		# save output [can zap input]
+	mov	$s1,4($out)
+	mov	$s2,8($out)
+	mov	$s3,12($out)
+
+	jmp	.Lcbc_exit
+
+.align	4
+.Lcbc_slow_dec_partial:
+	mov	$_ivp,%rdi
+	mov	%r11,0(%rdi)		# copy iv back to user
+	mov	%r12,8(%rdi)
+
+	mov	$s0,0+$ivec		# save output to stack
+	mov	$s1,4+$ivec
+	mov	$s2,8+$ivec
+	mov	$s3,12+$ivec
+
+	mov	$out,%rdi
+	lea	$ivec,%rsi
+	lea	16(%r10),%rcx
+	.long	0x9066A4F3	# rep movsb
+	jmp	.Lcbc_exit
+
+.align	16
+.Lcbc_exit:
+	mov	$_rsp,%rsi
+	mov	(%rsi),%r15
+	mov	8(%rsi),%r14
+	mov	16(%rsi),%r13
+	mov	24(%rsi),%r12
+	mov	32(%rsi),%rbp
+	mov	40(%rsi),%rbx
+	lea	48(%rsi),%rsp
+.Lcbc_popfq:
+	popfq
+.Lcbc_epilogue:
+	ret
+.size	AES_cbc_encrypt,.-AES_cbc_encrypt
+___
+}
+
+$code.=<<___;
+.align	64
+.LAES_Te:
+___
+	&_data_word(0xa56363c6, 0x847c7cf8, 0x997777ee, 0x8d7b7bf6);
+	&_data_word(0x0df2f2ff, 0xbd6b6bd6, 0xb16f6fde, 0x54c5c591);
+	&_data_word(0x50303060, 0x03010102, 0xa96767ce, 0x7d2b2b56);
+	&_data_word(0x19fefee7, 0x62d7d7b5, 0xe6abab4d, 0x9a7676ec);
+	&_data_word(0x45caca8f, 0x9d82821f, 0x40c9c989, 0x877d7dfa);
+	&_data_word(0x15fafaef, 0xeb5959b2, 0xc947478e, 0x0bf0f0fb);
+	&_data_word(0xecadad41, 0x67d4d4b3, 0xfda2a25f, 0xeaafaf45);
+	&_data_word(0xbf9c9c23, 0xf7a4a453, 0x967272e4, 0x5bc0c09b);
+	&_data_word(0xc2b7b775, 0x1cfdfde1, 0xae93933d, 0x6a26264c);
+	&_data_word(0x5a36366c, 0x413f3f7e, 0x02f7f7f5, 0x4fcccc83);
+	&_data_word(0x5c343468, 0xf4a5a551, 0x34e5e5d1, 0x08f1f1f9);
+	&_data_word(0x937171e2, 0x73d8d8ab, 0x53313162, 0x3f15152a);
+	&_data_word(0x0c040408, 0x52c7c795, 0x65232346, 0x5ec3c39d);
+	&_data_word(0x28181830, 0xa1969637, 0x0f05050a, 0xb59a9a2f);
+	&_data_word(0x0907070e, 0x36121224, 0x9b80801b, 0x3de2e2df);
+	&_data_word(0x26ebebcd, 0x6927274e, 0xcdb2b27f, 0x9f7575ea);
+	&_data_word(0x1b090912, 0x9e83831d, 0x742c2c58, 0x2e1a1a34);
+	&_data_word(0x2d1b1b36, 0xb26e6edc, 0xee5a5ab4, 0xfba0a05b);
+	&_data_word(0xf65252a4, 0x4d3b3b76, 0x61d6d6b7, 0xceb3b37d);
+	&_data_word(0x7b292952, 0x3ee3e3dd, 0x712f2f5e, 0x97848413);
+	&_data_word(0xf55353a6, 0x68d1d1b9, 0x00000000, 0x2cededc1);
+	&_data_word(0x60202040, 0x1ffcfce3, 0xc8b1b179, 0xed5b5bb6);
+	&_data_word(0xbe6a6ad4, 0x46cbcb8d, 0xd9bebe67, 0x4b393972);
+	&_data_word(0xde4a4a94, 0xd44c4c98, 0xe85858b0, 0x4acfcf85);
+	&_data_word(0x6bd0d0bb, 0x2aefefc5, 0xe5aaaa4f, 0x16fbfbed);
+	&_data_word(0xc5434386, 0xd74d4d9a, 0x55333366, 0x94858511);
+	&_data_word(0xcf45458a, 0x10f9f9e9, 0x06020204, 0x817f7ffe);
+	&_data_word(0xf05050a0, 0x443c3c78, 0xba9f9f25, 0xe3a8a84b);
+	&_data_word(0xf35151a2, 0xfea3a35d, 0xc0404080, 0x8a8f8f05);
+	&_data_word(0xad92923f, 0xbc9d9d21, 0x48383870, 0x04f5f5f1);
+	&_data_word(0xdfbcbc63, 0xc1b6b677, 0x75dadaaf, 0x63212142);
+	&_data_word(0x30101020, 0x1affffe5, 0x0ef3f3fd, 0x6dd2d2bf);
+	&_data_word(0x4ccdcd81, 0x140c0c18, 0x35131326, 0x2fececc3);
+	&_data_word(0xe15f5fbe, 0xa2979735, 0xcc444488, 0x3917172e);
+	&_data_word(0x57c4c493, 0xf2a7a755, 0x827e7efc, 0x473d3d7a);
+	&_data_word(0xac6464c8, 0xe75d5dba, 0x2b191932, 0x957373e6);
+	&_data_word(0xa06060c0, 0x98818119, 0xd14f4f9e, 0x7fdcdca3);
+	&_data_word(0x66222244, 0x7e2a2a54, 0xab90903b, 0x8388880b);
+	&_data_word(0xca46468c, 0x29eeeec7, 0xd3b8b86b, 0x3c141428);
+	&_data_word(0x79dedea7, 0xe25e5ebc, 0x1d0b0b16, 0x76dbdbad);
+	&_data_word(0x3be0e0db, 0x56323264, 0x4e3a3a74, 0x1e0a0a14);
+	&_data_word(0xdb494992, 0x0a06060c, 0x6c242448, 0xe45c5cb8);
+	&_data_word(0x5dc2c29f, 0x6ed3d3bd, 0xefacac43, 0xa66262c4);
+	&_data_word(0xa8919139, 0xa4959531, 0x37e4e4d3, 0x8b7979f2);
+	&_data_word(0x32e7e7d5, 0x43c8c88b, 0x5937376e, 0xb76d6dda);
+	&_data_word(0x8c8d8d01, 0x64d5d5b1, 0xd24e4e9c, 0xe0a9a949);
+	&_data_word(0xb46c6cd8, 0xfa5656ac, 0x07f4f4f3, 0x25eaeacf);
+	&_data_word(0xaf6565ca, 0x8e7a7af4, 0xe9aeae47, 0x18080810);
+	&_data_word(0xd5baba6f, 0x887878f0, 0x6f25254a, 0x722e2e5c);
+	&_data_word(0x241c1c38, 0xf1a6a657, 0xc7b4b473, 0x51c6c697);
+	&_data_word(0x23e8e8cb, 0x7cdddda1, 0x9c7474e8, 0x211f1f3e);
+	&_data_word(0xdd4b4b96, 0xdcbdbd61, 0x868b8b0d, 0x858a8a0f);
+	&_data_word(0x907070e0, 0x423e3e7c, 0xc4b5b571, 0xaa6666cc);
+	&_data_word(0xd8484890, 0x05030306, 0x01f6f6f7, 0x120e0e1c);
+	&_data_word(0xa36161c2, 0x5f35356a, 0xf95757ae, 0xd0b9b969);
+	&_data_word(0x91868617, 0x58c1c199, 0x271d1d3a, 0xb99e9e27);
+	&_data_word(0x38e1e1d9, 0x13f8f8eb, 0xb398982b, 0x33111122);
+	&_data_word(0xbb6969d2, 0x70d9d9a9, 0x898e8e07, 0xa7949433);
+	&_data_word(0xb69b9b2d, 0x221e1e3c, 0x92878715, 0x20e9e9c9);
+	&_data_word(0x49cece87, 0xff5555aa, 0x78282850, 0x7adfdfa5);
+	&_data_word(0x8f8c8c03, 0xf8a1a159, 0x80898909, 0x170d0d1a);
+	&_data_word(0xdabfbf65, 0x31e6e6d7, 0xc6424284, 0xb86868d0);
+	&_data_word(0xc3414182, 0xb0999929, 0x772d2d5a, 0x110f0f1e);
+	&_data_word(0xcbb0b07b, 0xfc5454a8, 0xd6bbbb6d, 0x3a16162c);
+
+#Te4	# four copies of Te4 to choose from to avoid L1 aliasing
+	&data_byte(0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5);
+	&data_byte(0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76);
+	&data_byte(0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0);
+	&data_byte(0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0);
+	&data_byte(0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc);
+	&data_byte(0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15);
+	&data_byte(0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a);
+	&data_byte(0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75);
+	&data_byte(0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0);
+	&data_byte(0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84);
+	&data_byte(0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b);
+	&data_byte(0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf);
+	&data_byte(0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85);
+	&data_byte(0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8);
+	&data_byte(0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5);
+	&data_byte(0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2);
+	&data_byte(0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17);
+	&data_byte(0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73);
+	&data_byte(0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88);
+	&data_byte(0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb);
+	&data_byte(0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c);
+	&data_byte(0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79);
+	&data_byte(0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9);
+	&data_byte(0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08);
+	&data_byte(0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6);
+	&data_byte(0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a);
+	&data_byte(0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e);
+	&data_byte(0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e);
+	&data_byte(0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94);
+	&data_byte(0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf);
+	&data_byte(0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68);
+	&data_byte(0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16);
+
+	&data_byte(0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5);
+	&data_byte(0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76);
+	&data_byte(0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0);
+	&data_byte(0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0);
+	&data_byte(0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc);
+	&data_byte(0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15);
+	&data_byte(0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a);
+	&data_byte(0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75);
+	&data_byte(0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0);
+	&data_byte(0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84);
+	&data_byte(0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b);
+	&data_byte(0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf);
+	&data_byte(0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85);
+	&data_byte(0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8);
+	&data_byte(0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5);
+	&data_byte(0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2);
+	&data_byte(0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17);
+	&data_byte(0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73);
+	&data_byte(0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88);
+	&data_byte(0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb);
+	&data_byte(0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c);
+	&data_byte(0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79);
+	&data_byte(0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9);
+	&data_byte(0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08);
+	&data_byte(0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6);
+	&data_byte(0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a);
+	&data_byte(0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e);
+	&data_byte(0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e);
+	&data_byte(0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94);
+	&data_byte(0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf);
+	&data_byte(0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68);
+	&data_byte(0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16);
+
+	&data_byte(0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5);
+	&data_byte(0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76);
+	&data_byte(0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0);
+	&data_byte(0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0);
+	&data_byte(0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc);
+	&data_byte(0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15);
+	&data_byte(0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a);
+	&data_byte(0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75);
+	&data_byte(0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0);
+	&data_byte(0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84);
+	&data_byte(0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b);
+	&data_byte(0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf);
+	&data_byte(0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85);
+	&data_byte(0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8);
+	&data_byte(0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5);
+	&data_byte(0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2);
+	&data_byte(0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17);
+	&data_byte(0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73);
+	&data_byte(0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88);
+	&data_byte(0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb);
+	&data_byte(0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c);
+	&data_byte(0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79);
+	&data_byte(0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9);
+	&data_byte(0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08);
+	&data_byte(0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6);
+	&data_byte(0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a);
+	&data_byte(0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e);
+	&data_byte(0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e);
+	&data_byte(0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94);
+	&data_byte(0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf);
+	&data_byte(0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68);
+	&data_byte(0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16);
+
+	&data_byte(0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5);
+	&data_byte(0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76);
+	&data_byte(0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0);
+	&data_byte(0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0);
+	&data_byte(0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc);
+	&data_byte(0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15);
+	&data_byte(0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a);
+	&data_byte(0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75);
+	&data_byte(0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0);
+	&data_byte(0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84);
+	&data_byte(0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b);
+	&data_byte(0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf);
+	&data_byte(0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85);
+	&data_byte(0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8);
+	&data_byte(0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5);
+	&data_byte(0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2);
+	&data_byte(0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17);
+	&data_byte(0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73);
+	&data_byte(0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88);
+	&data_byte(0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb);
+	&data_byte(0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c);
+	&data_byte(0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79);
+	&data_byte(0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9);
+	&data_byte(0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08);
+	&data_byte(0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6);
+	&data_byte(0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a);
+	&data_byte(0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e);
+	&data_byte(0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e);
+	&data_byte(0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94);
+	&data_byte(0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf);
+	&data_byte(0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68);
+	&data_byte(0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16);
+#rcon:
+$code.=<<___;
+	.long	0x00000001, 0x00000002, 0x00000004, 0x00000008
+	.long	0x00000010, 0x00000020, 0x00000040, 0x00000080
+	.long	0x0000001b, 0x00000036, 0x80808080, 0x80808080
+	.long	0xfefefefe, 0xfefefefe, 0x1b1b1b1b, 0x1b1b1b1b
+___
+$code.=<<___;
+.align	64
+.LAES_Td:
+___
+	&_data_word(0x50a7f451, 0x5365417e, 0xc3a4171a, 0x965e273a);
+	&_data_word(0xcb6bab3b, 0xf1459d1f, 0xab58faac, 0x9303e34b);
+	&_data_word(0x55fa3020, 0xf66d76ad, 0x9176cc88, 0x254c02f5);
+	&_data_word(0xfcd7e54f, 0xd7cb2ac5, 0x80443526, 0x8fa362b5);
+	&_data_word(0x495ab1de, 0x671bba25, 0x980eea45, 0xe1c0fe5d);
+	&_data_word(0x02752fc3, 0x12f04c81, 0xa397468d, 0xc6f9d36b);
+	&_data_word(0xe75f8f03, 0x959c9215, 0xeb7a6dbf, 0xda595295);
+	&_data_word(0x2d83bed4, 0xd3217458, 0x2969e049, 0x44c8c98e);
+	&_data_word(0x6a89c275, 0x78798ef4, 0x6b3e5899, 0xdd71b927);
+	&_data_word(0xb64fe1be, 0x17ad88f0, 0x66ac20c9, 0xb43ace7d);
+	&_data_word(0x184adf63, 0x82311ae5, 0x60335197, 0x457f5362);
+	&_data_word(0xe07764b1, 0x84ae6bbb, 0x1ca081fe, 0x942b08f9);
+	&_data_word(0x58684870, 0x19fd458f, 0x876cde94, 0xb7f87b52);
+	&_data_word(0x23d373ab, 0xe2024b72, 0x578f1fe3, 0x2aab5566);
+	&_data_word(0x0728ebb2, 0x03c2b52f, 0x9a7bc586, 0xa50837d3);
+	&_data_word(0xf2872830, 0xb2a5bf23, 0xba6a0302, 0x5c8216ed);
+	&_data_word(0x2b1ccf8a, 0x92b479a7, 0xf0f207f3, 0xa1e2694e);
+	&_data_word(0xcdf4da65, 0xd5be0506, 0x1f6234d1, 0x8afea6c4);
+	&_data_word(0x9d532e34, 0xa055f3a2, 0x32e18a05, 0x75ebf6a4);
+	&_data_word(0x39ec830b, 0xaaef6040, 0x069f715e, 0x51106ebd);
+	&_data_word(0xf98a213e, 0x3d06dd96, 0xae053edd, 0x46bde64d);
+	&_data_word(0xb58d5491, 0x055dc471, 0x6fd40604, 0xff155060);
+	&_data_word(0x24fb9819, 0x97e9bdd6, 0xcc434089, 0x779ed967);
+	&_data_word(0xbd42e8b0, 0x888b8907, 0x385b19e7, 0xdbeec879);
+	&_data_word(0x470a7ca1, 0xe90f427c, 0xc91e84f8, 0x00000000);
+	&_data_word(0x83868009, 0x48ed2b32, 0xac70111e, 0x4e725a6c);
+	&_data_word(0xfbff0efd, 0x5638850f, 0x1ed5ae3d, 0x27392d36);
+	&_data_word(0x64d90f0a, 0x21a65c68, 0xd1545b9b, 0x3a2e3624);
+	&_data_word(0xb1670a0c, 0x0fe75793, 0xd296eeb4, 0x9e919b1b);
+	&_data_word(0x4fc5c080, 0xa220dc61, 0x694b775a, 0x161a121c);
+	&_data_word(0x0aba93e2, 0xe52aa0c0, 0x43e0223c, 0x1d171b12);
+	&_data_word(0x0b0d090e, 0xadc78bf2, 0xb9a8b62d, 0xc8a91e14);
+	&_data_word(0x8519f157, 0x4c0775af, 0xbbdd99ee, 0xfd607fa3);
+	&_data_word(0x9f2601f7, 0xbcf5725c, 0xc53b6644, 0x347efb5b);
+	&_data_word(0x7629438b, 0xdcc623cb, 0x68fcedb6, 0x63f1e4b8);
+	&_data_word(0xcadc31d7, 0x10856342, 0x40229713, 0x2011c684);
+	&_data_word(0x7d244a85, 0xf83dbbd2, 0x1132f9ae, 0x6da129c7);
+	&_data_word(0x4b2f9e1d, 0xf330b2dc, 0xec52860d, 0xd0e3c177);
+	&_data_word(0x6c16b32b, 0x99b970a9, 0xfa489411, 0x2264e947);
+	&_data_word(0xc48cfca8, 0x1a3ff0a0, 0xd82c7d56, 0xef903322);
+	&_data_word(0xc74e4987, 0xc1d138d9, 0xfea2ca8c, 0x360bd498);
+	&_data_word(0xcf81f5a6, 0x28de7aa5, 0x268eb7da, 0xa4bfad3f);
+	&_data_word(0xe49d3a2c, 0x0d927850, 0x9bcc5f6a, 0x62467e54);
+	&_data_word(0xc2138df6, 0xe8b8d890, 0x5ef7392e, 0xf5afc382);
+	&_data_word(0xbe805d9f, 0x7c93d069, 0xa92dd56f, 0xb31225cf);
+	&_data_word(0x3b99acc8, 0xa77d1810, 0x6e639ce8, 0x7bbb3bdb);
+	&_data_word(0x097826cd, 0xf418596e, 0x01b79aec, 0xa89a4f83);
+	&_data_word(0x656e95e6, 0x7ee6ffaa, 0x08cfbc21, 0xe6e815ef);
+	&_data_word(0xd99be7ba, 0xce366f4a, 0xd4099fea, 0xd67cb029);
+	&_data_word(0xafb2a431, 0x31233f2a, 0x3094a5c6, 0xc066a235);
+	&_data_word(0x37bc4e74, 0xa6ca82fc, 0xb0d090e0, 0x15d8a733);
+	&_data_word(0x4a9804f1, 0xf7daec41, 0x0e50cd7f, 0x2ff69117);
+	&_data_word(0x8dd64d76, 0x4db0ef43, 0x544daacc, 0xdf0496e4);
+	&_data_word(0xe3b5d19e, 0x1b886a4c, 0xb81f2cc1, 0x7f516546);
+	&_data_word(0x04ea5e9d, 0x5d358c01, 0x737487fa, 0x2e410bfb);
+	&_data_word(0x5a1d67b3, 0x52d2db92, 0x335610e9, 0x1347d66d);
+	&_data_word(0x8c61d79a, 0x7a0ca137, 0x8e14f859, 0x893c13eb);
+	&_data_word(0xee27a9ce, 0x35c961b7, 0xede51ce1, 0x3cb1477a);
+	&_data_word(0x59dfd29c, 0x3f73f255, 0x79ce1418, 0xbf37c773);
+	&_data_word(0xeacdf753, 0x5baafd5f, 0x146f3ddf, 0x86db4478);
+	&_data_word(0x81f3afca, 0x3ec468b9, 0x2c342438, 0x5f40a3c2);
+	&_data_word(0x72c31d16, 0x0c25e2bc, 0x8b493c28, 0x41950dff);
+	&_data_word(0x7101a839, 0xdeb30c08, 0x9ce4b4d8, 0x90c15664);
+	&_data_word(0x6184cb7b, 0x70b632d5, 0x745c6c48, 0x4257b8d0);
+
+#Td4:	# four copies of Td4 to choose from to avoid L1 aliasing
+	&data_byte(0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38);
+	&data_byte(0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb);
+	&data_byte(0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87);
+	&data_byte(0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb);
+	&data_byte(0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d);
+	&data_byte(0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e);
+	&data_byte(0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2);
+	&data_byte(0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25);
+	&data_byte(0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16);
+	&data_byte(0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92);
+	&data_byte(0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda);
+	&data_byte(0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84);
+	&data_byte(0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a);
+	&data_byte(0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06);
+	&data_byte(0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02);
+	&data_byte(0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b);
+	&data_byte(0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea);
+	&data_byte(0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73);
+	&data_byte(0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85);
+	&data_byte(0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e);
+	&data_byte(0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89);
+	&data_byte(0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b);
+	&data_byte(0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20);
+	&data_byte(0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4);
+	&data_byte(0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31);
+	&data_byte(0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f);
+	&data_byte(0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d);
+	&data_byte(0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef);
+	&data_byte(0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0);
+	&data_byte(0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61);
+	&data_byte(0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26);
+	&data_byte(0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d);
+$code.=<<___;
+	.long	0x80808080, 0x80808080, 0xfefefefe, 0xfefefefe
+	.long	0x1b1b1b1b, 0x1b1b1b1b, 0, 0
+___
+	&data_byte(0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38);
+	&data_byte(0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb);
+	&data_byte(0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87);
+	&data_byte(0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb);
+	&data_byte(0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d);
+	&data_byte(0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e);
+	&data_byte(0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2);
+	&data_byte(0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25);
+	&data_byte(0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16);
+	&data_byte(0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92);
+	&data_byte(0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda);
+	&data_byte(0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84);
+	&data_byte(0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a);
+	&data_byte(0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06);
+	&data_byte(0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02);
+	&data_byte(0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b);
+	&data_byte(0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea);
+	&data_byte(0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73);
+	&data_byte(0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85);
+	&data_byte(0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e);
+	&data_byte(0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89);
+	&data_byte(0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b);
+	&data_byte(0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20);
+	&data_byte(0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4);
+	&data_byte(0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31);
+	&data_byte(0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f);
+	&data_byte(0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d);
+	&data_byte(0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef);
+	&data_byte(0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0);
+	&data_byte(0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61);
+	&data_byte(0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26);
+	&data_byte(0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d);
+$code.=<<___;
+	.long	0x80808080, 0x80808080, 0xfefefefe, 0xfefefefe
+	.long	0x1b1b1b1b, 0x1b1b1b1b, 0, 0
+___
+	&data_byte(0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38);
+	&data_byte(0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb);
+	&data_byte(0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87);
+	&data_byte(0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb);
+	&data_byte(0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d);
+	&data_byte(0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e);
+	&data_byte(0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2);
+	&data_byte(0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25);
+	&data_byte(0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16);
+	&data_byte(0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92);
+	&data_byte(0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda);
+	&data_byte(0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84);
+	&data_byte(0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a);
+	&data_byte(0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06);
+	&data_byte(0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02);
+	&data_byte(0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b);
+	&data_byte(0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea);
+	&data_byte(0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73);
+	&data_byte(0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85);
+	&data_byte(0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e);
+	&data_byte(0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89);
+	&data_byte(0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b);
+	&data_byte(0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20);
+	&data_byte(0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4);
+	&data_byte(0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31);
+	&data_byte(0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f);
+	&data_byte(0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d);
+	&data_byte(0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef);
+	&data_byte(0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0);
+	&data_byte(0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61);
+	&data_byte(0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26);
+	&data_byte(0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d);
+$code.=<<___;
+	.long	0x80808080, 0x80808080, 0xfefefefe, 0xfefefefe
+	.long	0x1b1b1b1b, 0x1b1b1b1b, 0, 0
+___
+	&data_byte(0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38);
+	&data_byte(0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb);
+	&data_byte(0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87);
+	&data_byte(0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb);
+	&data_byte(0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d);
+	&data_byte(0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e);
+	&data_byte(0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2);
+	&data_byte(0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25);
+	&data_byte(0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16);
+	&data_byte(0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92);
+	&data_byte(0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda);
+	&data_byte(0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84);
+	&data_byte(0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a);
+	&data_byte(0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06);
+	&data_byte(0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02);
+	&data_byte(0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b);
+	&data_byte(0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea);
+	&data_byte(0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73);
+	&data_byte(0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85);
+	&data_byte(0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e);
+	&data_byte(0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89);
+	&data_byte(0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b);
+	&data_byte(0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20);
+	&data_byte(0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4);
+	&data_byte(0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31);
+	&data_byte(0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f);
+	&data_byte(0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d);
+	&data_byte(0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef);
+	&data_byte(0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0);
+	&data_byte(0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61);
+	&data_byte(0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26);
+	&data_byte(0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d);
+$code.=<<___;
+	.long	0x80808080, 0x80808080, 0xfefefefe, 0xfefefefe
+	.long	0x1b1b1b1b, 0x1b1b1b1b, 0, 0
+.asciz  "AES for x86_64, CRYPTOGAMS by "
+.align	64
+___
+
+# EXCEPTION_DISPOSITION handler (EXCEPTION_RECORD *rec,ULONG64 frame,
+#		CONTEXT *context,DISPATCHER_CONTEXT *disp)
+if ($win64) {
+$rec="%rcx";
+$frame="%rdx";
+$context="%r8";
+$disp="%r9";
+
+$code.=<<___;
+.extern	__imp_RtlVirtualUnwind
+.type	block_se_handler,\@abi-omnipotent
+.align	16
+block_se_handler:
+	push	%rsi
+	push	%rdi
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+	pushfq
+	sub	\$64,%rsp
+
+	mov	120($context),%rax	# pull context->Rax
+	mov	248($context),%rbx	# pull context->Rip
+
+	mov	8($disp),%rsi		# disp->ImageBase
+	mov	56($disp),%r11		# disp->HandlerData
+
+	mov	0(%r11),%r10d		# HandlerData[0]
+	lea	(%rsi,%r10),%r10	# prologue label
+	cmp	%r10,%rbx		# context->RipRsp
+
+	mov	4(%r11),%r10d		# HandlerData[1]
+	lea	(%rsi,%r10),%r10	# epilogue label
+	cmp	%r10,%rbx		# context->Rip>=epilogue label
+	jae	.Lin_block_prologue
+
+	mov	24(%rax),%rax		# pull saved real stack pointer
+	lea	48(%rax),%rax		# adjust...
+
+	mov	-8(%rax),%rbx
+	mov	-16(%rax),%rbp
+	mov	-24(%rax),%r12
+	mov	-32(%rax),%r13
+	mov	-40(%rax),%r14
+	mov	-48(%rax),%r15
+	mov	%rbx,144($context)	# restore context->Rbx
+	mov	%rbp,160($context)	# restore context->Rbp
+	mov	%r12,216($context)	# restore context->R12
+	mov	%r13,224($context)	# restore context->R13
+	mov	%r14,232($context)	# restore context->R14
+	mov	%r15,240($context)	# restore context->R15
+
+.Lin_block_prologue:
+	mov	8(%rax),%rdi
+	mov	16(%rax),%rsi
+	mov	%rax,152($context)	# restore context->Rsp
+	mov	%rsi,168($context)	# restore context->Rsi
+	mov	%rdi,176($context)	# restore context->Rdi
+
+	jmp	.Lcommon_seh_exit
+.size	block_se_handler,.-block_se_handler
+
+.type	key_se_handler,\@abi-omnipotent
+.align	16
+key_se_handler:
+	push	%rsi
+	push	%rdi
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+	pushfq
+	sub	\$64,%rsp
+
+	mov	120($context),%rax	# pull context->Rax
+	mov	248($context),%rbx	# pull context->Rip
+
+	mov	8($disp),%rsi		# disp->ImageBase
+	mov	56($disp),%r11		# disp->HandlerData
+
+	mov	0(%r11),%r10d		# HandlerData[0]
+	lea	(%rsi,%r10),%r10	# prologue label
+	cmp	%r10,%rbx		# context->RipRsp
+
+	mov	4(%r11),%r10d		# HandlerData[1]
+	lea	(%rsi,%r10),%r10	# epilogue label
+	cmp	%r10,%rbx		# context->Rip>=epilogue label
+	jae	.Lin_key_prologue
+
+	lea	56(%rax),%rax
+
+	mov	-8(%rax),%rbx
+	mov	-16(%rax),%rbp
+	mov	-24(%rax),%r12
+	mov	-32(%rax),%r13
+	mov	-40(%rax),%r14
+	mov	-48(%rax),%r15
+	mov	%rbx,144($context)	# restore context->Rbx
+	mov	%rbp,160($context)	# restore context->Rbp
+	mov	%r12,216($context)	# restore context->R12
+	mov	%r13,224($context)	# restore context->R13
+	mov	%r14,232($context)	# restore context->R14
+	mov	%r15,240($context)	# restore context->R15
+
+.Lin_key_prologue:
+	mov	8(%rax),%rdi
+	mov	16(%rax),%rsi
+	mov	%rax,152($context)	# restore context->Rsp
+	mov	%rsi,168($context)	# restore context->Rsi
+	mov	%rdi,176($context)	# restore context->Rdi
+
+	jmp	.Lcommon_seh_exit
+.size	key_se_handler,.-key_se_handler
+
+.type	cbc_se_handler,\@abi-omnipotent
+.align	16
+cbc_se_handler:
+	push	%rsi
+	push	%rdi
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+	pushfq
+	sub	\$64,%rsp
+
+	mov	120($context),%rax	# pull context->Rax
+	mov	248($context),%rbx	# pull context->Rip
+
+	lea	.Lcbc_prologue(%rip),%r10
+	cmp	%r10,%rbx		# context->Rip<.Lcbc_prologue
+	jb	.Lin_cbc_prologue
+
+	lea	.Lcbc_fast_body(%rip),%r10
+	cmp	%r10,%rbx		# context->Rip<.Lcbc_fast_body
+	jb	.Lin_cbc_frame_setup
+
+	lea	.Lcbc_slow_prologue(%rip),%r10
+	cmp	%r10,%rbx		# context->Rip<.Lcbc_slow_prologue
+	jb	.Lin_cbc_body
+
+	lea	.Lcbc_slow_body(%rip),%r10
+	cmp	%r10,%rbx		# context->Rip<.Lcbc_slow_body
+	jb	.Lin_cbc_frame_setup
+
+.Lin_cbc_body:
+	mov	152($context),%rax	# pull context->Rsp
+
+	lea	.Lcbc_epilogue(%rip),%r10
+	cmp	%r10,%rbx		# context->Rip>=.Lcbc_epilogue
+	jae	.Lin_cbc_prologue
+
+	lea	8(%rax),%rax
+
+	lea	.Lcbc_popfq(%rip),%r10
+	cmp	%r10,%rbx		# context->Rip>=.Lcbc_popfq
+	jae	.Lin_cbc_prologue
+
+	mov	`16-8`(%rax),%rax	# biased $_rsp
+	lea	56(%rax),%rax
+
+.Lin_cbc_frame_setup:
+	mov	-16(%rax),%rbx
+	mov	-24(%rax),%rbp
+	mov	-32(%rax),%r12
+	mov	-40(%rax),%r13
+	mov	-48(%rax),%r14
+	mov	-56(%rax),%r15
+	mov	%rbx,144($context)	# restore context->Rbx
+	mov	%rbp,160($context)	# restore context->Rbp
+	mov	%r12,216($context)	# restore context->R12
+	mov	%r13,224($context)	# restore context->R13
+	mov	%r14,232($context)	# restore context->R14
+	mov	%r15,240($context)	# restore context->R15
+
+.Lin_cbc_prologue:
+	mov	8(%rax),%rdi
+	mov	16(%rax),%rsi
+	mov	%rax,152($context)	# restore context->Rsp
+	mov	%rsi,168($context)	# restore context->Rsi
+	mov	%rdi,176($context)	# restore context->Rdi
+
+.Lcommon_seh_exit:
+
+	mov	40($disp),%rdi		# disp->ContextRecord
+	mov	$context,%rsi		# context
+	mov	\$`1232/8`,%ecx		# sizeof(CONTEXT)
+	.long	0xa548f3fc		# cld; rep movsq
+
+	mov	$disp,%rsi
+	xor	%rcx,%rcx		# arg1, UNW_FLAG_NHANDLER
+	mov	8(%rsi),%rdx		# arg2, disp->ImageBase
+	mov	0(%rsi),%r8		# arg3, disp->ControlPc
+	mov	16(%rsi),%r9		# arg4, disp->FunctionEntry
+	mov	40(%rsi),%r10		# disp->ContextRecord
+	lea	56(%rsi),%r11		# &disp->HandlerData
+	lea	24(%rsi),%r12		# &disp->EstablisherFrame
+	mov	%r10,32(%rsp)		# arg5
+	mov	%r11,40(%rsp)		# arg6
+	mov	%r12,48(%rsp)		# arg7
+	mov	%rcx,56(%rsp)		# arg8, (NULL)
+	call	*__imp_RtlVirtualUnwind(%rip)
+
+	mov	\$1,%eax		# ExceptionContinueSearch
+	add	\$64,%rsp
+	popfq
+	pop	%r15
+	pop	%r14
+	pop	%r13
+	pop	%r12
+	pop	%rbp
+	pop	%rbx
+	pop	%rdi
+	pop	%rsi
+	ret
+.size	cbc_se_handler,.-cbc_se_handler
+
+.section	.pdata
+.align	4
+	.rva	.LSEH_begin_AES_encrypt
+	.rva	.LSEH_end_AES_encrypt
+	.rva	.LSEH_info_AES_encrypt
+
+	.rva	.LSEH_begin_AES_decrypt
+	.rva	.LSEH_end_AES_decrypt
+	.rva	.LSEH_info_AES_decrypt
+
+	.rva	.LSEH_begin_AES_set_encrypt_key
+	.rva	.LSEH_end_AES_set_encrypt_key
+	.rva	.LSEH_info_AES_set_encrypt_key
+
+	.rva	.LSEH_begin_AES_set_decrypt_key
+	.rva	.LSEH_end_AES_set_decrypt_key
+	.rva	.LSEH_info_AES_set_decrypt_key
+
+	.rva	.LSEH_begin_AES_cbc_encrypt
+	.rva	.LSEH_end_AES_cbc_encrypt
+	.rva	.LSEH_info_AES_cbc_encrypt
+
+.section	.xdata
+.align	8
+.LSEH_info_AES_encrypt:
+	.byte	9,0,0,0
+	.rva	block_se_handler
+	.rva	.Lenc_prologue,.Lenc_epilogue	# HandlerData[]
+.LSEH_info_AES_decrypt:
+	.byte	9,0,0,0
+	.rva	block_se_handler
+	.rva	.Ldec_prologue,.Ldec_epilogue	# HandlerData[]
+.LSEH_info_AES_set_encrypt_key:
+	.byte	9,0,0,0
+	.rva	key_se_handler
+	.rva	.Lenc_key_prologue,.Lenc_key_epilogue	# HandlerData[]
+.LSEH_info_AES_set_decrypt_key:
+	.byte	9,0,0,0
+	.rva	key_se_handler
+	.rva	.Ldec_key_prologue,.Ldec_key_epilogue	# HandlerData[]
+.LSEH_info_AES_cbc_encrypt:
+	.byte	9,0,0,0
+	.rva	cbc_se_handler
+___
+}
+
+$code =~ s/\`([^\`]*)\`/eval($1)/gem;
+
+print $code;
+
+close STDOUT;
diff --git a/crypto/aes/asm/aesfx-sparcv9.pl b/crypto/aes/asm/aesfx-sparcv9.pl
new file mode 100644
index 000000000..04b3cf711
--- /dev/null
+++ b/crypto/aes/asm/aesfx-sparcv9.pl
@@ -0,0 +1,1270 @@
+#! /usr/bin/env perl
+# Copyright 2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+#
+# ====================================================================
+# Written by Andy Polyakov  for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+
+# March 2016
+#
+# Initial support for Fujitsu SPARC64 X/X+ comprises minimally
+# required key setup and single-block procedures.
+#
+# April 2016
+#
+# Add "teaser" CBC and CTR mode-specific subroutines. "Teaser" means
+# that parallelizeable nature of CBC decrypt and CTR is not utilized
+# yet. CBC encrypt on the other hand is as good as it can possibly
+# get processing one byte in 4.1 cycles with 128-bit key on SPARC64 X.
+# This is ~6x faster than pure software implementation...
+#
+# July 2016
+#
+# Switch from faligndata to fshiftorx, which allows to omit alignaddr
+# instructions and improve single-block and short-input performance
+# with misaligned data.
+
+$output = pop;
+open STDOUT,">$output";
+
+{
+my ($inp,$out,$key,$rounds,$tmp,$mask) = map("%o$_",(0..5));
+
+$code.=<<___;
+#include "sparc_arch.h"
+
+#define LOCALS (STACK_BIAS+STACK_FRAME)
+
+.text
+
+.globl	aes_fx_encrypt
+.align	32
+aes_fx_encrypt:
+	and		$inp, 7, $tmp		! is input aligned?
+	andn		$inp, 7, $inp
+	ldd		[$key +  0], %f6	! round[0]
+	ldd		[$key +  8], %f8
+	mov		%o7, %g1
+	ld		[$key + 240], $rounds
+
+1:	call		.+8
+	add		%o7, .Linp_align-1b, %o7
+
+	sll		$tmp, 3, $tmp
+	ldd		[$inp + 0], %f0		! load input
+	brz,pt		$tmp, .Lenc_inp_aligned
+	ldd		[$inp + 8], %f2
+
+	ldd		[%o7 + $tmp], %f14	! shift left params
+	ldd		[$inp + 16], %f4
+	fshiftorx	%f0, %f2, %f14, %f0
+	fshiftorx	%f2, %f4, %f14, %f2
+
+.Lenc_inp_aligned:
+	ldd		[$key + 16], %f10	! round[1]
+	ldd		[$key + 24], %f12
+
+	fxor		%f0, %f6, %f0		! ^=round[0]
+	fxor		%f2, %f8, %f2
+	ldd		[$key + 32], %f6	! round[2]
+	ldd		[$key + 40], %f8
+	add		$key, 32, $key
+	sub		$rounds, 4, $rounds
+
+.Loop_enc:
+	fmovd		%f0, %f4
+	faesencx	%f2, %f10, %f0
+	faesencx	%f4, %f12, %f2
+	ldd		[$key + 16], %f10
+	ldd		[$key + 24], %f12
+	add		$key, 32, $key
+
+	fmovd		%f0, %f4
+	faesencx	%f2, %f6, %f0
+	faesencx	%f4, %f8, %f2
+	ldd		[$key +  0], %f6
+	ldd		[$key +  8], %f8
+
+	brnz,a		$rounds, .Loop_enc
+	sub		$rounds, 2, $rounds
+
+	andcc		$out, 7, $tmp		! is output aligned?
+	andn		$out, 7, $out
+	mov		0xff, $mask
+	srl		$mask, $tmp, $mask
+	add		%o7, 64, %o7
+	sll		$tmp, 3, $tmp
+
+	fmovd		%f0, %f4
+	faesencx	%f2, %f10, %f0
+	faesencx	%f4, %f12, %f2
+	ldd		[%o7 + $tmp], %f14	! shift right params
+
+	fmovd		%f0, %f4
+	faesenclx	%f2, %f6, %f0
+	faesenclx	%f4, %f8, %f2
+
+	bnz,pn		%icc, .Lenc_out_unaligned
+	mov		%g1, %o7
+
+	std		%f0, [$out + 0]
+	retl
+	std		%f2, [$out + 8]
+
+.align	16
+.Lenc_out_unaligned:
+	add		$out, 16, $inp
+	orn		%g0, $mask, $tmp
+	fshiftorx	%f0, %f0, %f14, %f4
+	fshiftorx	%f0, %f2, %f14, %f6
+	fshiftorx	%f2, %f2, %f14, %f8
+
+	stda		%f4, [$out + $mask]0xc0	! partial store
+	std		%f6, [$out + 8]
+	stda		%f8, [$inp + $tmp]0xc0	! partial store
+	retl
+	nop
+.type	aes_fx_encrypt,#function
+.size	aes_fx_encrypt,.-aes_fx_encrypt
+
+.globl	aes_fx_decrypt
+.align	32
+aes_fx_decrypt:
+	and		$inp, 7, $tmp		! is input aligned?
+	andn		$inp, 7, $inp
+	ldd		[$key +  0], %f6	! round[0]
+	ldd		[$key +  8], %f8
+	mov		%o7, %g1
+	ld		[$key + 240], $rounds
+
+1:	call		.+8
+	add		%o7, .Linp_align-1b, %o7
+
+	sll		$tmp, 3, $tmp
+	ldd		[$inp + 0], %f0		! load input
+	brz,pt		$tmp, .Ldec_inp_aligned
+	ldd		[$inp + 8], %f2
+
+	ldd		[%o7 + $tmp], %f14	! shift left params
+	ldd		[$inp + 16], %f4
+	fshiftorx	%f0, %f2, %f14, %f0
+	fshiftorx	%f2, %f4, %f14, %f2
+
+.Ldec_inp_aligned:
+	ldd		[$key + 16], %f10	! round[1]
+	ldd		[$key + 24], %f12
+
+	fxor		%f0, %f6, %f0		! ^=round[0]
+	fxor		%f2, %f8, %f2
+	ldd		[$key + 32], %f6	! round[2]
+	ldd		[$key + 40], %f8
+	add		$key, 32, $key
+	sub		$rounds, 4, $rounds
+
+.Loop_dec:
+	fmovd		%f0, %f4
+	faesdecx	%f2, %f10, %f0
+	faesdecx	%f4, %f12, %f2
+	ldd		[$key + 16], %f10
+	ldd		[$key + 24], %f12
+	add		$key, 32, $key
+
+	fmovd		%f0, %f4
+	faesdecx	%f2, %f6, %f0
+	faesdecx	%f4, %f8, %f2
+	ldd		[$key +  0], %f6
+	ldd		[$key +  8], %f8
+
+	brnz,a		$rounds, .Loop_dec
+	sub		$rounds, 2, $rounds
+
+	andcc		$out, 7, $tmp		! is output aligned?
+	andn		$out, 7, $out
+	mov		0xff, $mask
+	srl		$mask, $tmp, $mask
+	add		%o7, 64, %o7
+	sll		$tmp, 3, $tmp
+
+	fmovd		%f0, %f4
+	faesdecx	%f2, %f10, %f0
+	faesdecx	%f4, %f12, %f2
+	ldd		[%o7 + $tmp], %f14	! shift right params
+
+	fmovd		%f0, %f4
+	faesdeclx	%f2, %f6, %f0
+	faesdeclx	%f4, %f8, %f2
+
+	bnz,pn		%icc, .Ldec_out_unaligned
+	mov		%g1, %o7
+
+	std		%f0, [$out + 0]
+	retl
+	std		%f2, [$out + 8]
+
+.align	16
+.Ldec_out_unaligned:
+	add		$out, 16, $inp
+	orn		%g0, $mask, $tmp
+	fshiftorx	%f0, %f0, %f14, %f4
+	fshiftorx	%f0, %f2, %f14, %f6
+	fshiftorx	%f2, %f2, %f14, %f8
+
+	stda		%f4, [$out + $mask]0xc0	! partial store
+	std		%f6, [$out + 8]
+	stda		%f8, [$inp + $tmp]0xc0	! partial store
+	retl
+	nop
+.type	aes_fx_decrypt,#function
+.size	aes_fx_decrypt,.-aes_fx_decrypt
+___
+}
+{
+my ($inp,$bits,$out,$tmp,$inc) = map("%o$_",(0..5));
+$code.=<<___;
+.globl	aes_fx_set_decrypt_key
+.align	32
+aes_fx_set_decrypt_key:
+	b		.Lset_encrypt_key
+	mov		-1, $inc
+	retl
+	nop
+.type	aes_fx_set_decrypt_key,#function
+.size	aes_fx_set_decrypt_key,.-aes_fx_set_decrypt_key
+
+.globl	aes_fx_set_encrypt_key
+.align	32
+aes_fx_set_encrypt_key:
+	mov		1, $inc
+	nop
+.Lset_encrypt_key:
+	and		$inp, 7, $tmp
+	andn		$inp, 7, $inp
+	sll		$tmp, 3, $tmp
+	mov		%o7, %g1
+
+1:	call		.+8
+	add		%o7, .Linp_align-1b, %o7
+
+	ldd		[%o7 + $tmp], %f10	! shift left params
+	mov		%g1, %o7
+
+	cmp		$bits, 192
+	ldd		[$inp + 0], %f0
+	bl,pt		%icc, .L128
+	ldd		[$inp + 8], %f2
+
+	be,pt		%icc, .L192
+	ldd		[$inp + 16], %f4
+	brz,pt		$tmp, .L256aligned
+	ldd		[$inp + 24], %f6
+
+	ldd		[$inp + 32], %f8
+	fshiftorx	%f0, %f2, %f10, %f0
+	fshiftorx	%f2, %f4, %f10, %f2
+	fshiftorx	%f4, %f6, %f10, %f4
+	fshiftorx	%f6, %f8, %f10, %f6
+
+.L256aligned:
+	mov		14, $bits
+	and		$inc, `14*16`, $tmp
+	st		$bits, [$out + 240]	! store rounds
+	add		$out, $tmp, $out	! start or end of key schedule
+	sllx		$inc, 4, $inc		! 16 or -16
+___
+for ($i=0; $i<6; $i++) {
+    $code.=<<___;
+	std		%f0, [$out + 0]
+	faeskeyx	%f6, `0x10+$i`, %f0
+	std		%f2, [$out + 8]
+	add		$out, $inc, $out
+	faeskeyx	%f0, 0x00, %f2
+	std		%f4, [$out + 0]
+	faeskeyx	%f2, 0x01, %f4
+	std		%f6, [$out + 8]
+	add		$out, $inc, $out
+	faeskeyx	%f4, 0x00, %f6
+___
+}
+$code.=<<___;
+	std		%f0, [$out + 0]
+	faeskeyx	%f6, `0x10+$i`, %f0
+	std		%f2, [$out + 8]
+	add		$out, $inc, $out
+	faeskeyx	%f0, 0x00, %f2
+	std		%f4,[$out + 0]
+	std		%f6,[$out + 8]
+	add		$out, $inc, $out
+	std		%f0,[$out + 0]
+	std		%f2,[$out + 8]
+	retl
+	xor		%o0, %o0, %o0		! return 0
+
+.align	16
+.L192:
+	brz,pt		$tmp, .L192aligned
+	nop
+
+	ldd		[$inp + 24], %f6
+	fshiftorx	%f0, %f2, %f10, %f0
+	fshiftorx	%f2, %f4, %f10, %f2
+	fshiftorx	%f4, %f6, %f10, %f4
+
+.L192aligned:
+	mov		12, $bits
+	and		$inc, `12*16`, $tmp
+	st		$bits, [$out + 240]	! store rounds
+	add		$out, $tmp, $out	! start or end of key schedule
+	sllx		$inc, 4, $inc		! 16 or -16
+___
+for ($i=0; $i<8; $i+=2) {
+    $code.=<<___;
+	std		%f0, [$out + 0]
+	faeskeyx	%f4, `0x10+$i`, %f0
+	std		%f2, [$out + 8]
+	add		$out, $inc, $out
+	faeskeyx	%f0, 0x00, %f2
+	std		%f4, [$out + 0]
+	faeskeyx	%f2, 0x00, %f4
+	std		%f0, [$out + 8]
+	add		$out, $inc, $out
+	faeskeyx	%f4, `0x10+$i+1`, %f0
+	std		%f2, [$out + 0]
+	faeskeyx	%f0, 0x00, %f2
+	std		%f4, [$out + 8]
+	add		$out, $inc, $out
+___
+$code.=<<___		if ($i<6);
+	faeskeyx	%f2, 0x00, %f4
+___
+}
+$code.=<<___;
+	std		%f0, [$out + 0]
+	std		%f2, [$out + 8]
+	retl
+	xor		%o0, %o0, %o0		! return 0
+
+.align	16
+.L128:
+	brz,pt		$tmp, .L128aligned
+	nop
+
+	ldd		[$inp + 16], %f4
+	fshiftorx	%f0, %f2, %f10, %f0
+	fshiftorx	%f2, %f4, %f10, %f2
+
+.L128aligned:
+	mov		10, $bits
+	and		$inc, `10*16`, $tmp
+	st		$bits, [$out + 240]	! store rounds
+	add		$out, $tmp, $out	! start or end of key schedule
+	sllx		$inc, 4, $inc		! 16 or -16
+___
+for ($i=0; $i<10; $i++) {
+    $code.=<<___;
+	std		%f0, [$out + 0]
+	faeskeyx	%f2, `0x10+$i`, %f0
+	std		%f2, [$out + 8]
+	add		$out, $inc, $out
+	faeskeyx	%f0, 0x00, %f2
+___
+}
+$code.=<<___;
+	std		%f0, [$out + 0]
+	std		%f2, [$out + 8]
+	retl
+	xor		%o0, %o0, %o0		! return 0
+.type	aes_fx_set_encrypt_key,#function
+.size	aes_fx_set_encrypt_key,.-aes_fx_set_encrypt_key
+___
+}
+{
+my ($inp,$out,$len,$key,$ivp,$dir) = map("%i$_",(0..5));
+my ($rounds,$inner,$end,$inc,$ialign,$oalign,$mask) = map("%l$_",(0..7));
+my ($iv0,$iv1,$r0hi,$r0lo,$rlhi,$rllo,$in0,$in1,$intail,$outhead,$fshift)
+   = map("%f$_",grep { !($_ & 1) } (16 .. 62));
+my ($ileft,$iright) = ($ialign,$oalign);
+
+$code.=<<___;
+.globl	aes_fx_cbc_encrypt
+.align	32
+aes_fx_cbc_encrypt:
+	save		%sp, -STACK_FRAME-16, %sp
+	srln		$len, 4, $len
+	and		$inp, 7, $ialign
+	andn		$inp, 7, $inp
+	brz,pn		$len, .Lcbc_no_data
+	sll		$ialign, 3, $ileft
+
+1:	call		.+8
+	add		%o7, .Linp_align-1b, %o7
+
+	ld		[$key + 240], $rounds
+	and		$out, 7, $oalign
+	ld		[$ivp + 0], %f0		! load ivec
+	andn		$out, 7, $out
+	ld		[$ivp + 4], %f1
+	sll		$oalign, 3, $mask
+	ld		[$ivp + 8], %f2
+	ld		[$ivp + 12], %f3
+
+	sll		$rounds, 4, $rounds
+	add		$rounds, $key, $end
+	ldd		[$key + 0], $r0hi	! round[0]
+	ldd		[$key + 8], $r0lo
+
+	add		$inp, 16, $inp
+	sub		$len,  1, $len
+	ldd		[$end + 0], $rlhi	! round[last]
+	ldd		[$end + 8], $rllo
+
+	mov		16, $inc
+	movrz		$len, 0, $inc
+	ldd		[$key + 16], %f10	! round[1]
+	ldd		[$key + 24], %f12
+
+	ldd		[%o7 + $ileft], $fshift	! shift left params
+	add		%o7, 64, %o7
+	ldd		[$inp - 16], $in0	! load input
+	ldd		[$inp -  8], $in1
+	ldda		[$inp]0x82, $intail	! non-faulting load
+	brz		$dir, .Lcbc_decrypt
+	add		$inp, $inc, $inp	! inp+=16
+
+	fxor		$r0hi, %f0, %f0		! ivec^=round[0]
+	fxor		$r0lo, %f2, %f2
+	fshiftorx	$in0, $in1, $fshift, $in0
+	fshiftorx	$in1, $intail, $fshift, $in1
+	nop
+
+.Loop_cbc_enc:
+	fxor		$in0, %f0, %f0		! inp^ivec^round[0]
+	fxor		$in1, %f2, %f2
+	ldd		[$key + 32], %f6	! round[2]
+	ldd		[$key + 40], %f8
+	add		$key, 32, $end
+	sub		$rounds, 16*6, $inner
+
+.Lcbc_enc:
+	fmovd		%f0, %f4
+	faesencx	%f2, %f10, %f0
+	faesencx	%f4, %f12, %f2
+	ldd		[$end + 16], %f10
+	ldd		[$end + 24], %f12
+	add		$end, 32, $end
+
+	fmovd		%f0, %f4
+	faesencx	%f2, %f6, %f0
+	faesencx	%f4, %f8, %f2
+	ldd		[$end + 0], %f6
+	ldd		[$end + 8], %f8
+
+	brnz,a		$inner, .Lcbc_enc
+	sub		$inner, 16*2, $inner
+
+	fmovd		%f0, %f4
+	faesencx	%f2, %f10, %f0
+	faesencx	%f4, %f12, %f2
+	ldd		[$end + 16], %f10	! round[last-1]
+	ldd		[$end + 24], %f12
+
+	movrz		$len, 0, $inc
+	fmovd		$intail, $in0
+	ldd		[$inp - 8], $in1	! load next input block
+	ldda		[$inp]0x82, $intail	! non-faulting load
+	add		$inp, $inc, $inp	! inp+=16
+
+	fmovd		%f0, %f4
+	faesencx	%f2, %f6, %f0
+	faesencx	%f4, %f8, %f2
+
+	fshiftorx	$in0, $in1, $fshift, $in0
+	fshiftorx	$in1, $intail, $fshift, $in1
+
+	fmovd		%f0, %f4
+	faesencx	%f2, %f10, %f0
+	faesencx	%f4, %f12, %f2
+	ldd		[$key + 16], %f10	! round[1]
+	ldd		[$key + 24], %f12
+
+	fxor		$r0hi, $in0, $in0	! inp^=round[0]
+	fxor		$r0lo, $in1, $in1
+
+	fmovd		%f0, %f4
+	faesenclx	%f2, $rlhi, %f0
+	faesenclx	%f4, $rllo, %f2
+
+	brnz,pn		$oalign, .Lcbc_enc_unaligned_out
+	nop
+
+	std		%f0, [$out + 0]
+	std		%f2, [$out + 8]
+	add		$out, 16, $out
+
+	brnz,a		$len, .Loop_cbc_enc
+	sub		$len, 1, $len
+
+	st		%f0, [$ivp + 0]		! output ivec
+	st		%f1, [$ivp + 4]
+	st		%f2, [$ivp + 8]
+	st		%f3, [$ivp + 12]
+
+.Lcbc_no_data:
+	ret
+	restore
+
+.align	32
+.Lcbc_enc_unaligned_out:
+	ldd		[%o7 + $mask], $fshift	! shift right params
+	mov		0xff, $mask
+	srl		$mask, $oalign, $mask
+	sub		%g0, $ileft, $iright
+
+	fshiftorx	%f0, %f0, $fshift, %f6
+	fshiftorx	%f0, %f2, $fshift, %f8
+
+	stda		%f6, [$out + $mask]0xc0	! partial store
+	orn		%g0, $mask, $mask
+	std		%f8, [$out + 8]
+	add		$out, 16, $out
+	brz		$len, .Lcbc_enc_unaligned_out_done
+	sub		$len, 1, $len
+	b		.Loop_cbc_enc_unaligned_out
+	nop
+
+.align	32
+.Loop_cbc_enc_unaligned_out:
+	fmovd		%f2, $outhead
+	fxor		$in0, %f0, %f0		! inp^ivec^round[0]
+	fxor		$in1, %f2, %f2
+	ldd		[$key + 32], %f6	! round[2]
+	ldd		[$key + 40], %f8
+
+	fmovd		%f0, %f4
+	faesencx	%f2, %f10, %f0
+	faesencx	%f4, %f12, %f2
+	ldd		[$key + 48], %f10	! round[3]
+	ldd		[$key + 56], %f12
+
+	ldx		[$inp - 16], %o0
+	ldx		[$inp -  8], %o1
+	brz		$ileft, .Lcbc_enc_aligned_inp
+	movrz		$len, 0, $inc
+
+	ldx		[$inp], %o2
+	sllx		%o0, $ileft, %o0
+	srlx		%o1, $iright, %g1
+	sllx		%o1, $ileft, %o1
+	or		%g1, %o0, %o0
+	srlx		%o2, $iright, %o2
+	or		%o2, %o1, %o1
+
+.Lcbc_enc_aligned_inp:
+	fmovd		%f0, %f4
+	faesencx	%f2, %f6, %f0
+	faesencx	%f4, %f8, %f2
+	ldd		[$key + 64], %f6	! round[4]
+	ldd		[$key + 72], %f8
+	add		$key, 64, $end
+	sub		$rounds, 16*8, $inner
+
+	stx		%o0, [%sp + LOCALS + 0]
+	stx		%o1, [%sp + LOCALS + 8]
+	add		$inp, $inc, $inp	! inp+=16
+	nop
+
+.Lcbc_enc_unaligned:
+	fmovd		%f0, %f4
+	faesencx	%f2, %f10, %f0
+	faesencx	%f4, %f12, %f2
+	ldd		[$end + 16], %f10
+	ldd		[$end + 24], %f12
+	add		$end, 32, $end
+
+	fmovd		%f0, %f4
+	faesencx	%f2, %f6, %f0
+	faesencx	%f4, %f8, %f2
+	ldd		[$end + 0], %f6
+	ldd		[$end + 8], %f8
+
+	brnz,a		$inner, .Lcbc_enc_unaligned
+	sub		$inner, 16*2, $inner
+
+	fmovd		%f0, %f4
+	faesencx	%f2, %f10, %f0
+	faesencx	%f4, %f12, %f2
+	ldd		[$end + 16], %f10	! round[last-1]
+	ldd		[$end + 24], %f12
+
+	fmovd		%f0, %f4
+	faesencx	%f2, %f6, %f0
+	faesencx	%f4, %f8, %f2
+
+	ldd		[%sp + LOCALS + 0], $in0
+	ldd		[%sp + LOCALS + 8], $in1
+
+	fmovd		%f0, %f4
+	faesencx	%f2, %f10, %f0
+	faesencx	%f4, %f12, %f2
+	ldd		[$key + 16], %f10	! round[1]
+	ldd		[$key + 24], %f12
+
+	fxor		$r0hi, $in0, $in0	! inp^=round[0]
+	fxor		$r0lo, $in1, $in1
+
+	fmovd		%f0, %f4
+	faesenclx	%f2, $rlhi, %f0
+	faesenclx	%f4, $rllo, %f2
+
+	fshiftorx	$outhead, %f0, $fshift, %f6
+	fshiftorx	%f0, %f2, $fshift, %f8
+	std		%f6, [$out + 0]
+	std		%f8, [$out + 8]
+	add		$out, 16, $out
+
+	brnz,a		$len, .Loop_cbc_enc_unaligned_out
+	sub		$len, 1, $len
+
+.Lcbc_enc_unaligned_out_done:
+	fshiftorx	%f2, %f2, $fshift, %f8
+	stda		%f8, [$out + $mask]0xc0	! partial store
+
+	st		%f0, [$ivp + 0]		! output ivec
+	st		%f1, [$ivp + 4]
+	st		%f2, [$ivp + 8]
+	st		%f3, [$ivp + 12]
+
+	ret
+	restore
+
+.align	32
+.Lcbc_decrypt:
+	fshiftorx	$in0, $in1, $fshift, $in0
+	fshiftorx	$in1, $intail, $fshift, $in1
+	fmovd		%f0, $iv0
+	fmovd		%f2, $iv1
+
+.Loop_cbc_dec:
+	fxor		$in0, $r0hi, %f0	! inp^round[0]
+	fxor		$in1, $r0lo, %f2
+	ldd		[$key + 32], %f6	! round[2]
+	ldd		[$key + 40], %f8
+	add		$key, 32, $end
+	sub		$rounds, 16*6, $inner
+
+.Lcbc_dec:
+	fmovd		%f0, %f4
+	faesdecx	%f2, %f10, %f0
+	faesdecx	%f4, %f12, %f2
+	ldd		[$end + 16], %f10
+	ldd		[$end + 24], %f12
+	add		$end, 32, $end
+
+	fmovd		%f0, %f4
+	faesdecx	%f2, %f6, %f0
+	faesdecx	%f4, %f8, %f2
+	ldd		[$end + 0], %f6
+	ldd		[$end + 8], %f8
+
+	brnz,a		$inner, .Lcbc_dec
+	sub		$inner, 16*2, $inner
+
+	fmovd		%f0, %f4
+	faesdecx	%f2, %f10, %f0
+	faesdecx	%f4, %f12, %f2
+	ldd		[$end + 16], %f10	! round[last-1]
+	ldd		[$end + 24], %f12
+
+	fmovd		%f0, %f4
+	faesdecx	%f2, %f6, %f0
+	faesdecx	%f4, %f8, %f2
+	fxor		$iv0, $rlhi, %f6	! ivec^round[last]
+	fxor		$iv1, $rllo, %f8
+	fmovd		$in0, $iv0
+	fmovd		$in1, $iv1
+
+	movrz		$len, 0, $inc
+	fmovd		$intail, $in0
+	ldd		[$inp - 8], $in1	! load next input block
+	ldda		[$inp]0x82, $intail	! non-faulting load
+	add		$inp, $inc, $inp	! inp+=16
+
+	fmovd		%f0, %f4
+	faesdecx	%f2, %f10, %f0
+	faesdecx	%f4, %f12, %f2
+	ldd		[$key + 16], %f10	! round[1]
+	ldd		[$key + 24], %f12
+
+	fshiftorx	$in0, $in1, $fshift, $in0
+	fshiftorx	$in1, $intail, $fshift, $in1
+
+	fmovd		%f0, %f4
+	faesdeclx	%f2, %f6, %f0
+	faesdeclx	%f4, %f8, %f2
+
+	brnz,pn		$oalign, .Lcbc_dec_unaligned_out
+	nop
+
+	std		%f0, [$out + 0]
+	std		%f2, [$out + 8]
+	add		$out, 16, $out
+
+	brnz,a		$len, .Loop_cbc_dec
+	sub		$len, 1, $len
+
+	st		$iv0,    [$ivp + 0]	! output ivec
+	st		$iv0#lo, [$ivp + 4]
+	st		$iv1,    [$ivp + 8]
+	st		$iv1#lo, [$ivp + 12]
+
+	ret
+	restore
+
+.align	32
+.Lcbc_dec_unaligned_out:
+	ldd		[%o7 + $mask], $fshift	! shift right params
+	mov		0xff, $mask
+	srl		$mask, $oalign, $mask
+	sub		%g0, $ileft, $iright
+
+	fshiftorx	%f0, %f0, $fshift, %f6
+	fshiftorx	%f0, %f2, $fshift, %f8
+
+	stda		%f6, [$out + $mask]0xc0	! partial store
+	orn		%g0, $mask, $mask
+	std		%f8, [$out + 8]
+	add		$out, 16, $out
+	brz		$len, .Lcbc_dec_unaligned_out_done
+	sub		$len, 1, $len
+	b		.Loop_cbc_dec_unaligned_out
+	nop
+
+.align	32
+.Loop_cbc_dec_unaligned_out:
+	fmovd		%f2, $outhead
+	fxor		$in0, $r0hi, %f0	! inp^round[0]
+	fxor		$in1, $r0lo, %f2
+	ldd		[$key + 32], %f6	! round[2]
+	ldd		[$key + 40], %f8
+
+	fmovd		%f0, %f4
+	faesdecx	%f2, %f10, %f0
+	faesdecx	%f4, %f12, %f2
+	ldd		[$key + 48], %f10	! round[3]
+	ldd		[$key + 56], %f12
+
+	ldx		[$inp - 16], %o0
+	ldx		[$inp - 8], %o1
+	brz		$ileft, .Lcbc_dec_aligned_inp
+	movrz		$len, 0, $inc
+
+	ldx		[$inp], %o2
+	sllx		%o0, $ileft, %o0
+	srlx		%o1, $iright, %g1
+	sllx		%o1, $ileft, %o1
+	or		%g1, %o0, %o0
+	srlx		%o2, $iright, %o2
+	or		%o2, %o1, %o1
+
+.Lcbc_dec_aligned_inp:
+	fmovd		%f0, %f4
+	faesdecx	%f2, %f6, %f0
+	faesdecx	%f4, %f8, %f2
+	ldd		[$key + 64], %f6	! round[4]
+	ldd		[$key + 72], %f8
+	add		$key, 64, $end
+	sub		$rounds, 16*8, $inner
+
+	stx		%o0, [%sp + LOCALS + 0]
+	stx		%o1, [%sp + LOCALS + 8]
+	add		$inp, $inc, $inp	! inp+=16
+	nop
+
+.Lcbc_dec_unaligned:
+	fmovd		%f0, %f4
+	faesdecx	%f2, %f10, %f0
+	faesdecx	%f4, %f12, %f2
+	ldd		[$end + 16], %f10
+	ldd		[$end + 24], %f12
+	add		$end, 32, $end
+
+	fmovd		%f0, %f4
+	faesdecx	%f2, %f6, %f0
+	faesdecx	%f4, %f8, %f2
+	ldd		[$end + 0], %f6
+	ldd		[$end + 8], %f8
+
+	brnz,a		$inner, .Lcbc_dec_unaligned
+	sub		$inner, 16*2, $inner
+
+	fmovd		%f0, %f4
+	faesdecx	%f2, %f10, %f0
+	faesdecx	%f4, %f12, %f2
+	ldd		[$end + 16], %f10	! round[last-1]
+	ldd		[$end + 24], %f12
+
+	fmovd		%f0, %f4
+	faesdecx	%f2, %f6, %f0
+	faesdecx	%f4, %f8, %f2
+
+	fxor		$iv0, $rlhi, %f6	! ivec^round[last]
+	fxor		$iv1, $rllo, %f8
+	fmovd		$in0, $iv0
+	fmovd		$in1, $iv1
+	ldd		[%sp + LOCALS + 0], $in0
+	ldd		[%sp + LOCALS + 8], $in1
+
+	fmovd		%f0, %f4
+	faesdecx	%f2, %f10, %f0
+	faesdecx	%f4, %f12, %f2
+	ldd		[$key + 16], %f10	! round[1]
+	ldd		[$key + 24], %f12
+
+	fmovd		%f0, %f4
+	faesdeclx	%f2, %f6, %f0
+	faesdeclx	%f4, %f8, %f2
+
+	fshiftorx	$outhead, %f0, $fshift, %f6
+	fshiftorx	%f0, %f2, $fshift, %f8
+	std		%f6, [$out + 0]
+	std		%f8, [$out + 8]
+	add		$out, 16, $out
+
+	brnz,a		$len, .Loop_cbc_dec_unaligned_out
+	sub		$len, 1, $len
+
+.Lcbc_dec_unaligned_out_done:
+	fshiftorx	%f2, %f2, $fshift, %f8
+	stda		%f8, [$out + $mask]0xc0	! partial store
+
+	st		$iv0,    [$ivp + 0]	! output ivec
+	st		$iv0#lo, [$ivp + 4]
+	st		$iv1,    [$ivp + 8]
+	st		$iv1#lo, [$ivp + 12]
+
+	ret
+	restore
+.type	aes_fx_cbc_encrypt,#function
+.size	aes_fx_cbc_encrypt,.-aes_fx_cbc_encrypt
+___
+}
+{
+my ($inp,$out,$len,$key,$ivp) = map("%i$_",(0..5));
+my ($rounds,$inner,$end,$inc,$ialign,$oalign,$mask) = map("%l$_",(0..7));
+my ($ctr0,$ctr1,$r0hi,$r0lo,$rlhi,$rllo,$in0,$in1,$intail,$outhead,$fshift)
+   = map("%f$_",grep { !($_ & 1) } (16 .. 62));
+my ($ileft,$iright) = ($ialign, $oalign);
+my $one = "%f14";
+
+$code.=<<___;
+.globl	aes_fx_ctr32_encrypt_blocks
+.align	32
+aes_fx_ctr32_encrypt_blocks:
+	save		%sp, -STACK_FRAME-16, %sp
+	srln		$len, 0, $len
+	and		$inp, 7, $ialign
+	andn		$inp, 7, $inp
+	brz,pn		$len, .Lctr32_no_data
+	sll		$ialign, 3, $ileft
+
+.Lpic:	call		.+8
+	add		%o7, .Linp_align - .Lpic, %o7
+
+	ld		[$key + 240], $rounds
+	and		$out, 7, $oalign
+	ld		[$ivp +  0], $ctr0	! load counter
+	andn		$out, 7, $out
+	ld		[$ivp +  4], $ctr0#lo
+	sll		$oalign, 3, $mask
+	ld		[$ivp +  8], $ctr1
+	ld		[$ivp + 12], $ctr1#lo
+	ldd		[%o7 + 128], $one
+
+	sll		$rounds, 4, $rounds
+	add		$rounds, $key, $end
+	ldd		[$key + 0], $r0hi	! round[0]
+	ldd		[$key + 8], $r0lo
+
+	add		$inp, 16, $inp
+	sub		$len, 1, $len
+	ldd		[$key + 16], %f10	! round[1]
+	ldd		[$key + 24], %f12
+
+	mov		16, $inc
+	movrz		$len, 0, $inc
+	ldd		[$end + 0], $rlhi	! round[last]
+	ldd		[$end + 8], $rllo
+
+	ldd		[%o7 + $ileft], $fshift	! shiftleft params
+	add		%o7, 64, %o7
+	ldd		[$inp - 16], $in0	! load input
+	ldd		[$inp -  8], $in1
+	ldda		[$inp]0x82, $intail	! non-faulting load
+	add		$inp, $inc, $inp	! inp+=16
+
+	fshiftorx	$in0, $in1, $fshift, $in0
+	fshiftorx	$in1, $intail, $fshift, $in1
+
+.Loop_ctr32:
+	fxor		$ctr0, $r0hi, %f0	! counter^round[0]
+	fxor		$ctr1, $r0lo, %f2
+	ldd		[$key + 32], %f6	! round[2]
+	ldd		[$key + 40], %f8
+	add		$key, 32, $end
+	sub		$rounds, 16*6, $inner
+
+.Lctr32_enc:
+	fmovd		%f0, %f4
+	faesencx	%f2, %f10, %f0
+	faesencx	%f4, %f12, %f2
+	ldd		[$end + 16], %f10
+	ldd		[$end + 24], %f12
+	add		$end, 32, $end
+
+	fmovd		%f0, %f4
+	faesencx	%f2, %f6, %f0
+	faesencx	%f4, %f8, %f2
+	ldd		[$end + 0], %f6
+	ldd		[$end + 8], %f8
+
+	brnz,a		$inner, .Lctr32_enc
+	sub		$inner, 16*2, $inner
+
+	fmovd		%f0, %f4
+	faesencx	%f2, %f10, %f0
+	faesencx	%f4, %f12, %f2
+	ldd		[$end + 16], %f10	! round[last-1]
+	ldd		[$end + 24], %f12
+
+	fmovd		%f0, %f4
+	faesencx	%f2, %f6, %f0
+	faesencx	%f4, %f8, %f2
+	fxor		$in0, $rlhi, %f6	! inp^round[last]
+	fxor		$in1, $rllo, %f8
+
+	movrz		$len, 0, $inc
+	fmovd		$intail, $in0
+	ldd		[$inp - 8], $in1	! load next input block
+	ldda		[$inp]0x82, $intail	! non-faulting load
+	add		$inp, $inc, $inp	! inp+=16
+
+	fmovd		%f0, %f4
+	faesencx	%f2, %f10, %f0
+	faesencx	%f4, %f12, %f2
+	ldd		[$key + 16], %f10	! round[1]
+	ldd		[$key + 24], %f12
+
+	fshiftorx	$in0, $in1, $fshift, $in0
+	fshiftorx	$in1, $intail, $fshift, $in1
+	fpadd32		$ctr1, $one, $ctr1	! increment counter
+
+	fmovd		%f0, %f4
+	faesenclx	%f2, %f6, %f0
+	faesenclx	%f4, %f8, %f2
+
+	brnz,pn		$oalign, .Lctr32_unaligned_out
+	nop
+
+	std		%f0, [$out + 0]
+	std		%f2, [$out + 8]
+	add		$out, 16, $out
+
+	brnz,a		$len, .Loop_ctr32
+	sub		$len, 1, $len
+
+.Lctr32_no_data:
+	ret
+	restore
+
+.align	32
+.Lctr32_unaligned_out:
+	ldd		[%o7 + $mask], $fshift	! shift right params
+	mov		0xff, $mask
+	srl		$mask, $oalign, $mask
+	sub		%g0, $ileft, $iright
+
+	fshiftorx	%f0, %f0, $fshift, %f6
+	fshiftorx	%f0, %f2, $fshift, %f8
+
+	stda		%f6, [$out + $mask]0xc0	! partial store
+	orn		%g0, $mask, $mask
+	std		%f8, [$out + 8]
+	add		$out, 16, $out
+	brz		$len, .Lctr32_unaligned_out_done
+	sub		$len, 1, $len
+	b		.Loop_ctr32_unaligned_out
+	nop
+
+.align	32
+.Loop_ctr32_unaligned_out:
+	fmovd		%f2, $outhead
+	fxor		$ctr0, $r0hi, %f0	! counter^round[0]
+	fxor		$ctr1, $r0lo, %f2
+	ldd		[$key + 32], %f6	! round[2]
+	ldd		[$key + 40], %f8
+
+	fmovd		%f0, %f4
+	faesencx	%f2, %f10, %f0
+	faesencx	%f4, %f12, %f2
+	ldd		[$key + 48], %f10	! round[3]
+	ldd		[$key + 56], %f12
+
+	ldx		[$inp - 16], %o0
+	ldx		[$inp -  8], %o1
+	brz		$ileft, .Lctr32_aligned_inp
+	movrz		$len, 0, $inc
+
+	ldx		[$inp], %o2
+	sllx		%o0, $ileft, %o0
+	srlx		%o1, $iright, %g1
+	sllx		%o1, $ileft, %o1
+	or		%g1, %o0, %o0
+	srlx		%o2, $iright, %o2
+	or		%o2, %o1, %o1
+
+.Lctr32_aligned_inp:
+	fmovd		%f0, %f4
+	faesencx	%f2, %f6, %f0
+	faesencx	%f4, %f8, %f2
+	ldd		[$key + 64], %f6	! round[4]
+	ldd		[$key + 72], %f8
+	add		$key, 64, $end
+	sub		$rounds, 16*8, $inner
+
+	stx		%o0, [%sp + LOCALS + 0]
+	stx		%o1, [%sp + LOCALS + 8]
+	add		$inp, $inc, $inp	! inp+=16
+	nop
+
+.Lctr32_enc_unaligned:
+	fmovd		%f0, %f4
+	faesencx	%f2, %f10, %f0
+	faesencx	%f4, %f12, %f2
+	ldd		[$end + 16], %f10
+	ldd		[$end + 24], %f12
+	add		$end, 32, $end
+
+	fmovd		%f0, %f4
+	faesencx	%f2, %f6, %f0
+	faesencx	%f4, %f8, %f2
+	ldd		[$end + 0], %f6
+	ldd		[$end + 8], %f8
+
+	brnz,a		$inner, .Lctr32_enc_unaligned
+	sub		$inner, 16*2, $inner
+
+	fmovd		%f0, %f4
+	faesencx	%f2, %f10, %f0
+	faesencx	%f4, %f12, %f2
+	ldd		[$end + 16], %f10	! round[last-1]
+	ldd		[$end + 24], %f12
+	fpadd32		$ctr1, $one, $ctr1	! increment counter
+
+	fmovd		%f0, %f4
+	faesencx	%f2, %f6, %f0
+	faesencx	%f4, %f8, %f2
+	fxor		$in0, $rlhi, %f6	! inp^round[last]
+	fxor		$in1, $rllo, %f8
+	ldd		[%sp + LOCALS + 0], $in0
+	ldd		[%sp + LOCALS + 8], $in1
+
+	fmovd		%f0, %f4
+	faesencx	%f2, %f10, %f0
+	faesencx	%f4, %f12, %f2
+	ldd		[$key + 16], %f10	! round[1]
+	ldd		[$key + 24], %f12
+
+	fmovd		%f0, %f4
+	faesenclx	%f2, %f6, %f0
+	faesenclx	%f4, %f8, %f2
+
+	fshiftorx	$outhead, %f0, $fshift, %f6
+	fshiftorx	%f0, %f2, $fshift, %f8
+	std		%f6, [$out + 0]
+	std		%f8, [$out + 8]
+	add		$out, 16, $out
+
+	brnz,a		$len, .Loop_ctr32_unaligned_out
+	sub		$len, 1, $len
+
+.Lctr32_unaligned_out_done:
+	fshiftorx	%f2, %f2, $fshift, %f8
+	stda		%f8, [$out + $mask]0xc0	! partial store
+
+	ret
+	restore
+.type	aes_fx_ctr32_encrypt_blocks,#function
+.size	aes_fx_ctr32_encrypt_blocks,.-aes_fx_ctr32_encrypt_blocks
+
+.align	32
+.Linp_align:		! fshiftorx parameters for left shift toward %rs1
+	.byte	0, 0, 64,  0,	0, 64,  0, -64
+	.byte	0, 0, 56,  8,	0, 56,  8, -56
+	.byte	0, 0, 48, 16,	0, 48, 16, -48
+	.byte	0, 0, 40, 24,	0, 40, 24, -40
+	.byte	0, 0, 32, 32,	0, 32, 32, -32
+	.byte	0, 0, 24, 40,	0, 24, 40, -24
+	.byte	0, 0, 16, 48,	0, 16, 48, -16
+	.byte	0, 0,  8, 56,	0,  8, 56, -8
+.Lout_align:		! fshiftorx parameters for right shift toward %rs2
+	.byte	0, 0,  0, 64,	0,  0, 64,   0
+	.byte	0, 0,  8, 56,	0,  8, 56,  -8
+	.byte	0, 0, 16, 48,	0, 16, 48, -16
+	.byte	0, 0, 24, 40,	0, 24, 40, -24
+	.byte	0, 0, 32, 32,	0, 32, 32, -32
+	.byte	0, 0, 40, 24,	0, 40, 24, -40
+	.byte	0, 0, 48, 16,	0, 48, 16, -48
+	.byte	0, 0, 56,  8,	0, 56,  8, -56
+.Lone:
+	.word	0, 1
+.asciz	"AES for Fujitsu SPARC64 X, CRYPTOGAMS by "
+.align	4
+___
+}
+# Purpose of these subroutines is to explicitly encode VIS instructions,
+# so that one can compile the module without having to specify VIS
+# extensions on compiler command line, e.g. -xarch=v9 vs. -xarch=v9a.
+# Idea is to reserve for option to produce "universal" binary and let
+# programmer detect if current CPU is VIS capable at run-time.
+sub unvis {
+my ($mnemonic,$rs1,$rs2,$rd)=@_;
+my ($ref,$opf);
+my %visopf = (	"faligndata"	=> 0x048,
+		"bshuffle"	=> 0x04c,
+		"fpadd32"	=> 0x052,
+		"fxor"		=> 0x06c,
+		"fsrc2"		=> 0x078	);
+
+    $ref = "$mnemonic\t$rs1,$rs2,$rd";
+
+    if ($opf=$visopf{$mnemonic}) {
+	foreach ($rs1,$rs2,$rd) {
+	    return $ref if (!/%f([0-9]{1,2})/);
+	    $_=$1;
+	    if ($1>=32) {
+		return $ref if ($1&1);
+		# re-encode for upper double register addressing
+		$_=($1|$1>>5)&31;
+	    }
+	}
+
+	return	sprintf ".word\t0x%08x !%s",
+			0x81b00000|$rd<<25|$rs1<<14|$opf<<5|$rs2,
+			$ref;
+    } else {
+	return $ref;
+    }
+}
+
+sub unvis3 {
+my ($mnemonic,$rs1,$rs2,$rd)=@_;
+my %bias = ( "g" => 0, "o" => 8, "l" => 16, "i" => 24 );
+my ($ref,$opf);
+my %visopf = (	"alignaddr"	=> 0x018,
+		"bmask"		=> 0x019,
+		"alignaddrl"	=> 0x01a	);
+
+    $ref = "$mnemonic\t$rs1,$rs2,$rd";
+
+    if ($opf=$visopf{$mnemonic}) {
+	foreach ($rs1,$rs2,$rd) {
+	    return $ref if (!/%([goli])([0-9])/);
+	    $_=$bias{$1}+$2;
+	}
+
+	return	sprintf ".word\t0x%08x !%s",
+			0x81b00000|$rd<<25|$rs1<<14|$opf<<5|$rs2,
+			$ref;
+    } else {
+	return $ref;
+    }
+}
+
+sub unfx {
+my ($mnemonic,$rs1,$rs2,$rd)=@_;
+my ($ref,$opf);
+my %aesopf = (	"faesencx"	=> 0x90,
+		"faesdecx"	=> 0x91,
+		"faesenclx"	=> 0x92,
+		"faesdeclx"	=> 0x93,
+		"faeskeyx"	=> 0x94	);
+
+    $ref = "$mnemonic\t$rs1,$rs2,$rd";
+
+    if (defined($opf=$aesopf{$mnemonic})) {
+	$rs2 = ($rs2 =~ /%f([0-6]*[02468])/) ? (($1|$1>>5)&31) : $rs2;
+	$rs2 = oct($rs2) if ($rs2 =~ /^0/);
+
+	foreach ($rs1,$rd) {
+	    return $ref if (!/%f([0-9]{1,2})/);
+	    $_=$1;
+	    if ($1>=32) {
+		return $ref if ($1&1);
+		# re-encode for upper double register addressing
+		$_=($1|$1>>5)&31;
+	    }
+	}
+
+	return	sprintf ".word\t0x%08x !%s",
+			2<<30|$rd<<25|0x36<<19|$rs1<<14|$opf<<5|$rs2,
+			$ref;
+    } else {
+	return $ref;
+    }
+}
+
+sub unfx3src {
+my ($mnemonic,$rs1,$rs2,$rs3,$rd)=@_;
+my ($ref,$opf);
+my %aesopf = (	"fshiftorx"	=> 0x0b	);
+
+    $ref = "$mnemonic\t$rs1,$rs2,$rs3,$rd";
+
+    if (defined($opf=$aesopf{$mnemonic})) {
+	foreach ($rs1,$rs2,$rs3,$rd) {
+	    return $ref if (!/%f([0-9]{1,2})/);
+	    $_=$1;
+	    if ($1>=32) {
+		return $ref if ($1&1);
+		# re-encode for upper double register addressing
+		$_=($1|$1>>5)&31;
+	    }
+	}
+
+	return	sprintf ".word\t0x%08x !%s",
+			2<<30|$rd<<25|0x37<<19|$rs1<<14|$rs3<<9|$opf<<5|$rs2,
+			$ref;
+    } else {
+	return $ref;
+    }
+}
+
+foreach (split("\n",$code)) {
+    s/\`([^\`]*)\`/eval $1/ge;
+
+    s/%f([0-9]+)#lo/sprintf "%%f%d",$1+1/ge;
+
+    s/\b(faes[^x]{3,4}x)\s+(%f[0-9]{1,2}),\s*([%fx0-9]+),\s*(%f[0-9]{1,2})/
+		&unfx($1,$2,$3,$4)
+     /ge or
+    s/\b([f][^\s]*)\s+(%f[0-9]{1,2}),\s*(%f[0-9]{1,2}),\s*(%f[0-9]{1,2}),\s*(%f[0-9]{1,2})/
+		&unfx3src($1,$2,$3,$4,$5)
+     /ge or
+    s/\b([fb][^\s]*)\s+(%f[0-9]{1,2}),\s*(%f[0-9]{1,2}),\s*(%f[0-9]{1,2})/
+		&unvis($1,$2,$3,$4)
+     /ge or
+    s/\b(alignaddr[l]*)\s+(%[goli][0-7]),\s*(%[goli][0-7]),\s*(%[goli][0-7])/
+		&unvis3($1,$2,$3,$4)
+     /ge;
+    print $_,"\n";
+}
+
+close STDOUT;
diff --git a/crypto/aes/asm/aesni-mb-x86_64.pl b/crypto/aes/asm/aesni-mb-x86_64.pl
new file mode 100644
index 000000000..aa2735e06
--- /dev/null
+++ b/crypto/aes/asm/aesni-mb-x86_64.pl
@@ -0,0 +1,1402 @@
+#! /usr/bin/env perl
+# Copyright 2013-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+
+# ====================================================================
+# Written by Andy Polyakov  for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+
+# Multi-buffer AES-NI procedures process several independent buffers
+# in parallel by interleaving independent instructions.
+#
+# Cycles per byte for interleave factor 4:
+#
+#			asymptotic	measured
+#			---------------------------
+# Westmere		5.00/4=1.25	5.13/4=1.28
+# Atom			15.0/4=3.75	?15.7/4=3.93
+# Sandy Bridge		5.06/4=1.27	5.18/4=1.29
+# Ivy Bridge		5.06/4=1.27	5.14/4=1.29
+# Haswell		4.44/4=1.11	4.44/4=1.11
+# Bulldozer		5.75/4=1.44	5.76/4=1.44
+#
+# Cycles per byte for interleave factor 8 (not implemented for
+# pre-AVX processors, where higher interleave factor incidentally
+# doesn't result in improvement):
+#
+#			asymptotic	measured
+#			---------------------------
+# Sandy Bridge		5.06/8=0.64	7.10/8=0.89(*)
+# Ivy Bridge		5.06/8=0.64	7.14/8=0.89(*)
+# Haswell		5.00/8=0.63	5.00/8=0.63
+# Bulldozer		5.75/8=0.72	5.77/8=0.72
+#
+# (*)	Sandy/Ivy Bridge are known to handle high interleave factors
+#	suboptimally;
+
+$flavour = shift;
+$output  = shift;
+if ($flavour =~ /\./) { $output = $flavour; undef $flavour; }
+
+$win64=0; $win64=1 if ($flavour =~ /[nm]asm|mingw64/ || $output =~ /\.asm$/);
+
+$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+( $xlate="${dir}x86_64-xlate.pl" and -f $xlate ) or
+( $xlate="${dir}../../perlasm/x86_64-xlate.pl" and -f $xlate) or
+die "can't locate x86_64-xlate.pl";
+
+$avx=0;
+
+if (`$ENV{CC} -Wa,-v -c -o /dev/null -x assembler /dev/null 2>&1`
+		=~ /GNU assembler version ([2-9]\.[0-9]+)/) {
+	$avx = ($1>=2.19) + ($1>=2.22);
+}
+
+if (!$avx && $win64 && ($flavour =~ /nasm/ || $ENV{ASM} =~ /nasm/) &&
+	   `nasm -v 2>&1` =~ /NASM version ([2-9]\.[0-9]+)/) {
+	$avx = ($1>=2.09) + ($1>=2.10);
+}
+
+if (!$avx && $win64 && ($flavour =~ /masm/ || $ENV{ASM} =~ /ml64/) &&
+	   `ml64 2>&1` =~ /Version ([0-9]+)\./) {
+	$avx = ($1>=10) + ($1>=11);
+}
+
+if (!$avx && `$ENV{CC} -v 2>&1` =~ /((?:^clang|LLVM) version|.*based on LLVM) ([3-9]\.[0-9]+)/) {
+	$avx = ($2>=3.0) + ($2>3.0);
+}
+
+open OUT,"| \"$^X\" \"$xlate\" $flavour \"$output\"";
+*STDOUT=*OUT;
+
+# void aesni_multi_cbc_encrypt (
+#     struct {	void *inp,*out; int blocks; double iv[2]; } inp[8];
+#     const AES_KEY *key,
+#     int num);		/* 1 or 2 */
+#
+$inp="%rdi";	# 1st arg
+$key="%rsi";	# 2nd arg
+$num="%edx";
+
+@inptr=map("%r$_",(8..11));
+@outptr=map("%r$_",(12..15));
+
+($rndkey0,$rndkey1)=("%xmm0","%xmm1");
+@out=map("%xmm$_",(2..5));
+@inp=map("%xmm$_",(6..9));
+($counters,$mask,$zero)=map("%xmm$_",(10..12));
+
+($rounds,$one,$sink,$offset)=("%eax","%ecx","%rbp","%rbx");
+
+$code.=<<___;
+.text
+
+.extern	OPENSSL_ia32cap_P
+
+.globl	aesni_multi_cbc_encrypt
+.type	aesni_multi_cbc_encrypt,\@function,3
+.align	32
+aesni_multi_cbc_encrypt:
+___
+$code.=<<___ if ($avx);
+	cmp	\$2,$num
+	jb	.Lenc_non_avx
+	mov	OPENSSL_ia32cap_P+4(%rip),%ecx
+	test	\$`1<<28`,%ecx			# AVX bit
+	jnz	_avx_cbc_enc_shortcut
+	jmp	.Lenc_non_avx
+.align	16
+.Lenc_non_avx:
+___
+$code.=<<___;
+	mov	%rsp,%rax
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+___
+$code.=<<___ if ($win64);
+	lea	-0xa8(%rsp),%rsp
+	movaps	%xmm6,(%rsp)
+	movaps	%xmm7,0x10(%rsp)
+	movaps	%xmm8,0x20(%rsp)
+	movaps	%xmm9,0x30(%rsp)
+	movaps	%xmm10,0x40(%rsp)
+	movaps	%xmm11,0x50(%rsp)
+	movaps	%xmm12,0x60(%rsp)
+	movaps	%xmm13,-0x68(%rax)	# not used, saved to share se_handler 
+	movaps	%xmm14,-0x58(%rax)
+	movaps	%xmm15,-0x48(%rax)
+___
+$code.=<<___;
+	# stack layout
+	#
+	# +0	output sink
+	# +16	input sink [original %rsp and $num]
+	# +32	counters
+
+	sub	\$48,%rsp
+	and	\$-64,%rsp
+	mov	%rax,16(%rsp)			# original %rsp
+
+.Lenc4x_body:
+	movdqu	($key),$zero			# 0-round key
+	lea	0x78($key),$key			# size optimization
+	lea	40*2($inp),$inp
+
+.Lenc4x_loop_grande:
+	mov	$num,24(%rsp)			# original $num
+	xor	$num,$num
+___
+for($i=0;$i<4;$i++) {
+    $code.=<<___;
+	mov	`40*$i+16-40*2`($inp),$one	# borrow $one for number of blocks
+	mov	`40*$i+0-40*2`($inp),@inptr[$i]
+	cmp	$num,$one
+	mov	`40*$i+8-40*2`($inp),@outptr[$i]
+	cmovg	$one,$num			# find maximum
+	test	$one,$one
+	movdqu	`40*$i+24-40*2`($inp),@out[$i]	# load IV
+	mov	$one,`32+4*$i`(%rsp)		# initialize counters
+	cmovle	%rsp,@inptr[$i]			# cancel input
+___
+}
+$code.=<<___;
+	test	$num,$num
+	jz	.Lenc4x_done
+
+	movups	0x10-0x78($key),$rndkey1
+	 pxor	$zero,@out[0]
+	movups	0x20-0x78($key),$rndkey0
+	 pxor	$zero,@out[1]
+	mov	0xf0-0x78($key),$rounds
+	 pxor	$zero,@out[2]
+	movdqu	(@inptr[0]),@inp[0]		# load inputs
+	 pxor	$zero,@out[3]
+	movdqu	(@inptr[1]),@inp[1]
+	 pxor	@inp[0],@out[0]
+	movdqu	(@inptr[2]),@inp[2]
+	 pxor	@inp[1],@out[1]
+	movdqu	(@inptr[3]),@inp[3]
+	 pxor	@inp[2],@out[2]
+	 pxor	@inp[3],@out[3]
+	movdqa	32(%rsp),$counters		# load counters
+	xor	$offset,$offset
+	jmp	.Loop_enc4x
+
+.align	32
+.Loop_enc4x:
+	add	\$16,$offset
+	lea	16(%rsp),$sink			# sink pointer
+	mov	\$1,$one			# constant of 1
+	sub	$offset,$sink
+
+	aesenc		$rndkey1,@out[0]
+	prefetcht0	31(@inptr[0],$offset)	# prefetch input
+	prefetcht0	31(@inptr[1],$offset)
+	aesenc		$rndkey1,@out[1]
+	prefetcht0	31(@inptr[2],$offset)
+	prefetcht0	31(@inptr[2],$offset)
+	aesenc		$rndkey1,@out[2]
+	aesenc		$rndkey1,@out[3]
+	movups		0x30-0x78($key),$rndkey1
+___
+for($i=0;$i<4;$i++) {
+my $rndkey = ($i&1) ? $rndkey1 : $rndkey0;
+$code.=<<___;
+	 cmp		`32+4*$i`(%rsp),$one
+	aesenc		$rndkey,@out[0]
+	aesenc		$rndkey,@out[1]
+	aesenc		$rndkey,@out[2]
+	 cmovge		$sink,@inptr[$i]	# cancel input
+	 cmovg		$sink,@outptr[$i]	# sink output
+	aesenc		$rndkey,@out[3]
+	movups		`0x40+16*$i-0x78`($key),$rndkey
+___
+}
+$code.=<<___;
+	 movdqa		$counters,$mask
+	aesenc		$rndkey0,@out[0]
+	prefetcht0	15(@outptr[0],$offset)	# prefetch output
+	prefetcht0	15(@outptr[1],$offset)
+	aesenc		$rndkey0,@out[1]
+	prefetcht0	15(@outptr[2],$offset)
+	prefetcht0	15(@outptr[3],$offset)
+	aesenc		$rndkey0,@out[2]
+	aesenc		$rndkey0,@out[3]
+	movups		0x80-0x78($key),$rndkey0
+	 pxor		$zero,$zero
+
+	aesenc		$rndkey1,@out[0]
+	 pcmpgtd	$zero,$mask
+	 movdqu		-0x78($key),$zero	# reload 0-round key
+	aesenc		$rndkey1,@out[1]
+	 paddd		$mask,$counters		# decrement counters
+	 movdqa		$counters,32(%rsp)	# update counters
+	aesenc		$rndkey1,@out[2]
+	aesenc		$rndkey1,@out[3]
+	movups		0x90-0x78($key),$rndkey1
+
+	cmp	\$11,$rounds
+
+	aesenc		$rndkey0,@out[0]
+	aesenc		$rndkey0,@out[1]
+	aesenc		$rndkey0,@out[2]
+	aesenc		$rndkey0,@out[3]
+	movups		0xa0-0x78($key),$rndkey0
+
+	jb	.Lenc4x_tail
+
+	aesenc		$rndkey1,@out[0]
+	aesenc		$rndkey1,@out[1]
+	aesenc		$rndkey1,@out[2]
+	aesenc		$rndkey1,@out[3]
+	movups		0xb0-0x78($key),$rndkey1
+
+	aesenc		$rndkey0,@out[0]
+	aesenc		$rndkey0,@out[1]
+	aesenc		$rndkey0,@out[2]
+	aesenc		$rndkey0,@out[3]
+	movups		0xc0-0x78($key),$rndkey0
+
+	je	.Lenc4x_tail
+
+	aesenc		$rndkey1,@out[0]
+	aesenc		$rndkey1,@out[1]
+	aesenc		$rndkey1,@out[2]
+	aesenc		$rndkey1,@out[3]
+	movups		0xd0-0x78($key),$rndkey1
+
+	aesenc		$rndkey0,@out[0]
+	aesenc		$rndkey0,@out[1]
+	aesenc		$rndkey0,@out[2]
+	aesenc		$rndkey0,@out[3]
+	movups		0xe0-0x78($key),$rndkey0
+	jmp	.Lenc4x_tail
+
+.align	32
+.Lenc4x_tail:
+	aesenc		$rndkey1,@out[0]
+	aesenc		$rndkey1,@out[1]
+	aesenc		$rndkey1,@out[2]
+	aesenc		$rndkey1,@out[3]
+	 movdqu		(@inptr[0],$offset),@inp[0]
+	movdqu		0x10-0x78($key),$rndkey1
+
+	aesenclast	$rndkey0,@out[0]
+	 movdqu		(@inptr[1],$offset),@inp[1]
+	 pxor		$zero,@inp[0]
+	aesenclast	$rndkey0,@out[1]
+	 movdqu		(@inptr[2],$offset),@inp[2]
+	 pxor		$zero,@inp[1]
+	aesenclast	$rndkey0,@out[2]
+	 movdqu		(@inptr[3],$offset),@inp[3]
+	 pxor		$zero,@inp[2]
+	aesenclast	$rndkey0,@out[3]
+	movdqu		0x20-0x78($key),$rndkey0
+	 pxor		$zero,@inp[3]
+
+	movups		@out[0],-16(@outptr[0],$offset)
+	 pxor		@inp[0],@out[0]
+	movups		@out[1],-16(@outptr[1],$offset)	
+	 pxor		@inp[1],@out[1]
+	movups		@out[2],-16(@outptr[2],$offset)	
+	 pxor		@inp[2],@out[2]
+	movups		@out[3],-16(@outptr[3],$offset)
+	 pxor		@inp[3],@out[3]
+
+	dec	$num
+	jnz	.Loop_enc4x
+
+	mov	16(%rsp),%rax			# original %rsp
+	mov	24(%rsp),$num
+
+	#pxor	@inp[0],@out[0]
+	#pxor	@inp[1],@out[1]
+	#movdqu	@out[0],`40*0+24-40*2`($inp)	# output iv FIX ME!
+	#pxor	@inp[2],@out[2]
+	#movdqu	@out[1],`40*1+24-40*2`($inp)
+	#pxor	@inp[3],@out[3]
+	#movdqu	@out[2],`40*2+24-40*2`($inp)	# won't fix, let caller
+	#movdqu	@out[3],`40*3+24-40*2`($inp)	# figure this out...
+
+	lea	`40*4`($inp),$inp
+	dec	$num
+	jnz	.Lenc4x_loop_grande
+
+.Lenc4x_done:
+___
+$code.=<<___ if ($win64);
+	movaps	-0xd8(%rax),%xmm6
+	movaps	-0xc8(%rax),%xmm7
+	movaps	-0xb8(%rax),%xmm8
+	movaps	-0xa8(%rax),%xmm9
+	movaps	-0x98(%rax),%xmm10
+	movaps	-0x88(%rax),%xmm11
+	movaps	-0x78(%rax),%xmm12
+	#movaps	-0x68(%rax),%xmm13
+	#movaps	-0x58(%rax),%xmm14
+	#movaps	-0x48(%rax),%xmm15
+___
+$code.=<<___;
+	mov	-48(%rax),%r15
+	mov	-40(%rax),%r14
+	mov	-32(%rax),%r13
+	mov	-24(%rax),%r12
+	mov	-16(%rax),%rbp
+	mov	-8(%rax),%rbx
+	lea	(%rax),%rsp
+.Lenc4x_epilogue:
+	ret
+.size	aesni_multi_cbc_encrypt,.-aesni_multi_cbc_encrypt
+
+.globl	aesni_multi_cbc_decrypt
+.type	aesni_multi_cbc_decrypt,\@function,3
+.align	32
+aesni_multi_cbc_decrypt:
+___
+$code.=<<___ if ($avx);
+	cmp	\$2,$num
+	jb	.Ldec_non_avx
+	mov	OPENSSL_ia32cap_P+4(%rip),%ecx
+	test	\$`1<<28`,%ecx			# AVX bit
+	jnz	_avx_cbc_dec_shortcut
+	jmp	.Ldec_non_avx
+.align	16
+.Ldec_non_avx:
+___
+$code.=<<___;
+	mov	%rsp,%rax
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+___
+$code.=<<___ if ($win64);
+	lea	-0xa8(%rsp),%rsp
+	movaps	%xmm6,(%rsp)
+	movaps	%xmm7,0x10(%rsp)
+	movaps	%xmm8,0x20(%rsp)
+	movaps	%xmm9,0x30(%rsp)
+	movaps	%xmm10,0x40(%rsp)
+	movaps	%xmm11,0x50(%rsp)
+	movaps	%xmm12,0x60(%rsp)
+	movaps	%xmm13,-0x68(%rax)	# not used, saved to share se_handler 
+	movaps	%xmm14,-0x58(%rax)
+	movaps	%xmm15,-0x48(%rax)
+___
+$code.=<<___;
+	# stack layout
+	#
+	# +0	output sink
+	# +16	input sink [original %rsp and $num]
+	# +32	counters
+
+	sub	\$48,%rsp
+	and	\$-64,%rsp
+	mov	%rax,16(%rsp)			# original %rsp
+
+.Ldec4x_body:
+	movdqu	($key),$zero			# 0-round key
+	lea	0x78($key),$key			# size optimization
+	lea	40*2($inp),$inp
+
+.Ldec4x_loop_grande:
+	mov	$num,24(%rsp)			# original $num
+	xor	$num,$num
+___
+for($i=0;$i<4;$i++) {
+    $code.=<<___;
+	mov	`40*$i+16-40*2`($inp),$one	# borrow $one for number of blocks
+	mov	`40*$i+0-40*2`($inp),@inptr[$i]
+	cmp	$num,$one
+	mov	`40*$i+8-40*2`($inp),@outptr[$i]
+	cmovg	$one,$num			# find maximum
+	test	$one,$one
+	movdqu	`40*$i+24-40*2`($inp),@inp[$i]	# load IV
+	mov	$one,`32+4*$i`(%rsp)		# initialize counters
+	cmovle	%rsp,@inptr[$i]			# cancel input
+___
+}
+$code.=<<___;
+	test	$num,$num
+	jz	.Ldec4x_done
+
+	movups	0x10-0x78($key),$rndkey1
+	movups	0x20-0x78($key),$rndkey0
+	mov	0xf0-0x78($key),$rounds
+	movdqu	(@inptr[0]),@out[0]		# load inputs
+	movdqu	(@inptr[1]),@out[1]
+	 pxor	$zero,@out[0]
+	movdqu	(@inptr[2]),@out[2]
+	 pxor	$zero,@out[1]
+	movdqu	(@inptr[3]),@out[3]
+	 pxor	$zero,@out[2]
+	 pxor	$zero,@out[3]
+	movdqa	32(%rsp),$counters		# load counters
+	xor	$offset,$offset
+	jmp	.Loop_dec4x
+
+.align	32
+.Loop_dec4x:
+	add	\$16,$offset
+	lea	16(%rsp),$sink			# sink pointer
+	mov	\$1,$one			# constant of 1
+	sub	$offset,$sink
+
+	aesdec		$rndkey1,@out[0]
+	prefetcht0	31(@inptr[0],$offset)	# prefetch input
+	prefetcht0	31(@inptr[1],$offset)
+	aesdec		$rndkey1,@out[1]
+	prefetcht0	31(@inptr[2],$offset)
+	prefetcht0	31(@inptr[3],$offset)
+	aesdec		$rndkey1,@out[2]
+	aesdec		$rndkey1,@out[3]
+	movups		0x30-0x78($key),$rndkey1
+___
+for($i=0;$i<4;$i++) {
+my $rndkey = ($i&1) ? $rndkey1 : $rndkey0;
+$code.=<<___;
+	 cmp		`32+4*$i`(%rsp),$one
+	aesdec		$rndkey,@out[0]
+	aesdec		$rndkey,@out[1]
+	aesdec		$rndkey,@out[2]
+	 cmovge		$sink,@inptr[$i]	# cancel input
+	 cmovg		$sink,@outptr[$i]	# sink output
+	aesdec		$rndkey,@out[3]
+	movups		`0x40+16*$i-0x78`($key),$rndkey
+___
+}
+$code.=<<___;
+	 movdqa		$counters,$mask
+	aesdec		$rndkey0,@out[0]
+	prefetcht0	15(@outptr[0],$offset)	# prefetch output
+	prefetcht0	15(@outptr[1],$offset)
+	aesdec		$rndkey0,@out[1]
+	prefetcht0	15(@outptr[2],$offset)
+	prefetcht0	15(@outptr[3],$offset)
+	aesdec		$rndkey0,@out[2]
+	aesdec		$rndkey0,@out[3]
+	movups		0x80-0x78($key),$rndkey0
+	 pxor		$zero,$zero
+
+	aesdec		$rndkey1,@out[0]
+	 pcmpgtd	$zero,$mask
+	 movdqu		-0x78($key),$zero	# reload 0-round key
+	aesdec		$rndkey1,@out[1]
+	 paddd		$mask,$counters		# decrement counters
+	 movdqa		$counters,32(%rsp)	# update counters
+	aesdec		$rndkey1,@out[2]
+	aesdec		$rndkey1,@out[3]
+	movups		0x90-0x78($key),$rndkey1
+
+	cmp	\$11,$rounds
+
+	aesdec		$rndkey0,@out[0]
+	aesdec		$rndkey0,@out[1]
+	aesdec		$rndkey0,@out[2]
+	aesdec		$rndkey0,@out[3]
+	movups		0xa0-0x78($key),$rndkey0
+
+	jb	.Ldec4x_tail
+
+	aesdec		$rndkey1,@out[0]
+	aesdec		$rndkey1,@out[1]
+	aesdec		$rndkey1,@out[2]
+	aesdec		$rndkey1,@out[3]
+	movups		0xb0-0x78($key),$rndkey1
+
+	aesdec		$rndkey0,@out[0]
+	aesdec		$rndkey0,@out[1]
+	aesdec		$rndkey0,@out[2]
+	aesdec		$rndkey0,@out[3]
+	movups		0xc0-0x78($key),$rndkey0
+
+	je	.Ldec4x_tail
+
+	aesdec		$rndkey1,@out[0]
+	aesdec		$rndkey1,@out[1]
+	aesdec		$rndkey1,@out[2]
+	aesdec		$rndkey1,@out[3]
+	movups		0xd0-0x78($key),$rndkey1
+
+	aesdec		$rndkey0,@out[0]
+	aesdec		$rndkey0,@out[1]
+	aesdec		$rndkey0,@out[2]
+	aesdec		$rndkey0,@out[3]
+	movups		0xe0-0x78($key),$rndkey0
+	jmp	.Ldec4x_tail
+
+.align	32
+.Ldec4x_tail:
+	aesdec		$rndkey1,@out[0]
+	aesdec		$rndkey1,@out[1]
+	aesdec		$rndkey1,@out[2]
+	 pxor		$rndkey0,@inp[0]
+	 pxor		$rndkey0,@inp[1]
+	aesdec		$rndkey1,@out[3]
+	movdqu		0x10-0x78($key),$rndkey1
+	 pxor		$rndkey0,@inp[2]
+	 pxor		$rndkey0,@inp[3]
+	movdqu		0x20-0x78($key),$rndkey0
+
+	aesdeclast	@inp[0],@out[0]
+	aesdeclast	@inp[1],@out[1]
+	 movdqu		-16(@inptr[0],$offset),@inp[0]	# load next IV
+	 movdqu		-16(@inptr[1],$offset),@inp[1]
+	aesdeclast	@inp[2],@out[2]
+	aesdeclast	@inp[3],@out[3]
+	 movdqu		-16(@inptr[2],$offset),@inp[2]
+	 movdqu		-16(@inptr[3],$offset),@inp[3]
+
+	movups		@out[0],-16(@outptr[0],$offset)
+	 movdqu		(@inptr[0],$offset),@out[0]
+	movups		@out[1],-16(@outptr[1],$offset)	
+	 movdqu		(@inptr[1],$offset),@out[1]
+	 pxor		$zero,@out[0]
+	movups		@out[2],-16(@outptr[2],$offset)	
+	 movdqu		(@inptr[2],$offset),@out[2]
+	 pxor		$zero,@out[1]
+	movups		@out[3],-16(@outptr[3],$offset)
+	 movdqu		(@inptr[3],$offset),@out[3]
+	 pxor		$zero,@out[2]
+	 pxor		$zero,@out[3]
+
+	dec	$num
+	jnz	.Loop_dec4x
+
+	mov	16(%rsp),%rax			# original %rsp
+	mov	24(%rsp),$num
+
+	lea	`40*4`($inp),$inp
+	dec	$num
+	jnz	.Ldec4x_loop_grande
+
+.Ldec4x_done:
+___
+$code.=<<___ if ($win64);
+	movaps	-0xd8(%rax),%xmm6
+	movaps	-0xc8(%rax),%xmm7
+	movaps	-0xb8(%rax),%xmm8
+	movaps	-0xa8(%rax),%xmm9
+	movaps	-0x98(%rax),%xmm10
+	movaps	-0x88(%rax),%xmm11
+	movaps	-0x78(%rax),%xmm12
+	#movaps	-0x68(%rax),%xmm13
+	#movaps	-0x58(%rax),%xmm14
+	#movaps	-0x48(%rax),%xmm15
+___
+$code.=<<___;
+	mov	-48(%rax),%r15
+	mov	-40(%rax),%r14
+	mov	-32(%rax),%r13
+	mov	-24(%rax),%r12
+	mov	-16(%rax),%rbp
+	mov	-8(%rax),%rbx
+	lea	(%rax),%rsp
+.Ldec4x_epilogue:
+	ret
+.size	aesni_multi_cbc_decrypt,.-aesni_multi_cbc_decrypt
+___
+
+						if ($avx) {{{
+my @ptr=map("%r$_",(8..15));
+my $offload=$sink;
+
+my @out=map("%xmm$_",(2..9));
+my @inp=map("%xmm$_",(10..13));
+my ($counters,$zero)=("%xmm14","%xmm15");
+
+$code.=<<___;
+.type	aesni_multi_cbc_encrypt_avx,\@function,3
+.align	32
+aesni_multi_cbc_encrypt_avx:
+_avx_cbc_enc_shortcut:
+	mov	%rsp,%rax
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+___
+$code.=<<___ if ($win64);
+	lea	-0xa8(%rsp),%rsp
+	movaps	%xmm6,(%rsp)
+	movaps	%xmm7,0x10(%rsp)
+	movaps	%xmm8,0x20(%rsp)
+	movaps	%xmm9,0x30(%rsp)
+	movaps	%xmm10,0x40(%rsp)
+	movaps	%xmm11,0x50(%rsp)
+	movaps	%xmm12,-0x78(%rax)
+	movaps	%xmm13,-0x68(%rax)
+	movaps	%xmm14,-0x58(%rax)
+	movaps	%xmm15,-0x48(%rax)
+___
+$code.=<<___;
+	# stack layout
+	#
+	# +0	output sink
+	# +16	input sink [original %rsp and $num]
+	# +32	counters
+	# +64	distances between inputs and outputs
+	# +128	off-load area for @inp[0..3]
+
+	sub	\$192,%rsp
+	and	\$-128,%rsp
+	mov	%rax,16(%rsp)			# original %rsp
+
+.Lenc8x_body:
+	vzeroupper
+	vmovdqu	($key),$zero			# 0-round key
+	lea	0x78($key),$key			# size optimization
+	lea	40*4($inp),$inp
+	shr	\$1,$num
+
+.Lenc8x_loop_grande:
+	#mov	$num,24(%rsp)			# original $num
+	xor	$num,$num
+___
+for($i=0;$i<8;$i++) {
+  my $temp = $i ? $offload : $offset;
+    $code.=<<___;
+	mov	`40*$i+16-40*4`($inp),$one	# borrow $one for number of blocks
+	mov	`40*$i+0-40*4`($inp),@ptr[$i]	# input pointer
+	cmp	$num,$one
+	mov	`40*$i+8-40*4`($inp),$temp	# output pointer
+	cmovg	$one,$num			# find maximum
+	test	$one,$one
+	vmovdqu	`40*$i+24-40*4`($inp),@out[$i]	# load IV
+	mov	$one,`32+4*$i`(%rsp)		# initialize counters
+	cmovle	%rsp,@ptr[$i]			# cancel input
+	sub	@ptr[$i],$temp			# distance between input and output
+	mov	$temp,`64+8*$i`(%rsp)		# initialize distances
+___
+}
+$code.=<<___;
+	test	$num,$num
+	jz	.Lenc8x_done
+
+	vmovups	0x10-0x78($key),$rndkey1
+	vmovups	0x20-0x78($key),$rndkey0
+	mov	0xf0-0x78($key),$rounds
+
+	vpxor	(@ptr[0]),$zero,@inp[0]		# load inputs and xor with 0-round
+	 lea	128(%rsp),$offload		# offload area
+	vpxor	(@ptr[1]),$zero,@inp[1]
+	vpxor	(@ptr[2]),$zero,@inp[2]
+	vpxor	(@ptr[3]),$zero,@inp[3]
+	 vpxor	@inp[0],@out[0],@out[0]
+	vpxor	(@ptr[4]),$zero,@inp[0]
+	 vpxor	@inp[1],@out[1],@out[1]
+	vpxor	(@ptr[5]),$zero,@inp[1]
+	 vpxor	@inp[2],@out[2],@out[2]
+	vpxor	(@ptr[6]),$zero,@inp[2]
+	 vpxor	@inp[3],@out[3],@out[3]
+	vpxor	(@ptr[7]),$zero,@inp[3]
+	 vpxor	@inp[0],@out[4],@out[4]
+	mov	\$1,$one			# constant of 1
+	 vpxor	@inp[1],@out[5],@out[5]
+	 vpxor	@inp[2],@out[6],@out[6]
+	 vpxor	@inp[3],@out[7],@out[7]
+	jmp	.Loop_enc8x
+
+.align	32
+.Loop_enc8x:
+___
+for($i=0;$i<8;$i++) {
+my $rndkey=($i&1)?$rndkey0:$rndkey1;
+$code.=<<___;
+	vaesenc		$rndkey,@out[0],@out[0]
+	 cmp		32+4*$i(%rsp),$one
+___
+$code.=<<___ if ($i);
+	 mov		64+8*$i(%rsp),$offset
+___
+$code.=<<___;
+	vaesenc		$rndkey,@out[1],@out[1]
+	prefetcht0	31(@ptr[$i])			# prefetch input
+	vaesenc		$rndkey,@out[2],@out[2]
+___
+$code.=<<___ if ($i>1);
+	prefetcht0	15(@ptr[$i-2])			# prefetch output
+___
+$code.=<<___;
+	vaesenc		$rndkey,@out[3],@out[3]
+	 lea		(@ptr[$i],$offset),$offset
+	 cmovge		%rsp,@ptr[$i]			# cancel input
+	vaesenc		$rndkey,@out[4],@out[4]
+	 cmovg		%rsp,$offset			# sink output
+	vaesenc		$rndkey,@out[5],@out[5]
+	 sub		@ptr[$i],$offset
+	vaesenc		$rndkey,@out[6],@out[6]
+	 vpxor		16(@ptr[$i]),$zero,@inp[$i%4]	# load input and xor with 0-round
+	 mov		$offset,64+8*$i(%rsp)
+	vaesenc		$rndkey,@out[7],@out[7]
+	vmovups		`16*(3+$i)-0x78`($key),$rndkey
+	 lea		16(@ptr[$i],$offset),@ptr[$i]	# switch to output
+___
+$code.=<<___ if ($i<4)
+	 vmovdqu	@inp[$i%4],`16*$i`($offload)	# off-load
+___
+}
+$code.=<<___;
+	 vmovdqu	32(%rsp),$counters
+	prefetcht0	15(@ptr[$i-2])			# prefetch output
+	prefetcht0	15(@ptr[$i-1])
+	cmp	\$11,$rounds
+	jb	.Lenc8x_tail
+
+	vaesenc		$rndkey1,@out[0],@out[0]
+	vaesenc		$rndkey1,@out[1],@out[1]
+	vaesenc		$rndkey1,@out[2],@out[2]
+	vaesenc		$rndkey1,@out[3],@out[3]
+	vaesenc		$rndkey1,@out[4],@out[4]
+	vaesenc		$rndkey1,@out[5],@out[5]
+	vaesenc		$rndkey1,@out[6],@out[6]
+	vaesenc		$rndkey1,@out[7],@out[7]
+	vmovups		0xb0-0x78($key),$rndkey1
+
+	vaesenc		$rndkey0,@out[0],@out[0]
+	vaesenc		$rndkey0,@out[1],@out[1]
+	vaesenc		$rndkey0,@out[2],@out[2]
+	vaesenc		$rndkey0,@out[3],@out[3]
+	vaesenc		$rndkey0,@out[4],@out[4]
+	vaesenc		$rndkey0,@out[5],@out[5]
+	vaesenc		$rndkey0,@out[6],@out[6]
+	vaesenc		$rndkey0,@out[7],@out[7]
+	vmovups		0xc0-0x78($key),$rndkey0
+	je	.Lenc8x_tail
+
+	vaesenc		$rndkey1,@out[0],@out[0]
+	vaesenc		$rndkey1,@out[1],@out[1]
+	vaesenc		$rndkey1,@out[2],@out[2]
+	vaesenc		$rndkey1,@out[3],@out[3]
+	vaesenc		$rndkey1,@out[4],@out[4]
+	vaesenc		$rndkey1,@out[5],@out[5]
+	vaesenc		$rndkey1,@out[6],@out[6]
+	vaesenc		$rndkey1,@out[7],@out[7]
+	vmovups		0xd0-0x78($key),$rndkey1
+
+	vaesenc		$rndkey0,@out[0],@out[0]
+	vaesenc		$rndkey0,@out[1],@out[1]
+	vaesenc		$rndkey0,@out[2],@out[2]
+	vaesenc		$rndkey0,@out[3],@out[3]
+	vaesenc		$rndkey0,@out[4],@out[4]
+	vaesenc		$rndkey0,@out[5],@out[5]
+	vaesenc		$rndkey0,@out[6],@out[6]
+	vaesenc		$rndkey0,@out[7],@out[7]
+	vmovups		0xe0-0x78($key),$rndkey0
+
+.Lenc8x_tail:
+	vaesenc		$rndkey1,@out[0],@out[0]
+	 vpxor		$zero,$zero,$zero
+	vaesenc		$rndkey1,@out[1],@out[1]
+	vaesenc		$rndkey1,@out[2],@out[2]
+	 vpcmpgtd	$zero,$counters,$zero
+	vaesenc		$rndkey1,@out[3],@out[3]
+	vaesenc		$rndkey1,@out[4],@out[4]
+	 vpaddd		$counters,$zero,$zero		# decrement counters
+	 vmovdqu	48(%rsp),$counters
+	vaesenc		$rndkey1,@out[5],@out[5]
+	 mov		64(%rsp),$offset		# pre-load 1st offset
+	vaesenc		$rndkey1,@out[6],@out[6]
+	vaesenc		$rndkey1,@out[7],@out[7]
+	vmovups		0x10-0x78($key),$rndkey1
+
+	vaesenclast	$rndkey0,@out[0],@out[0]
+	 vmovdqa	$zero,32(%rsp)			# update counters
+	 vpxor		$zero,$zero,$zero
+	vaesenclast	$rndkey0,@out[1],@out[1]
+	vaesenclast	$rndkey0,@out[2],@out[2]
+	 vpcmpgtd	$zero,$counters,$zero
+	vaesenclast	$rndkey0,@out[3],@out[3]
+	vaesenclast	$rndkey0,@out[4],@out[4]
+	 vpaddd		$zero,$counters,$counters	# decrement counters
+	 vmovdqu	-0x78($key),$zero		# 0-round
+	vaesenclast	$rndkey0,@out[5],@out[5]
+	vaesenclast	$rndkey0,@out[6],@out[6]
+	 vmovdqa	$counters,48(%rsp)		# update counters
+	vaesenclast	$rndkey0,@out[7],@out[7]
+	vmovups		0x20-0x78($key),$rndkey0
+
+	vmovups		@out[0],-16(@ptr[0])		# write output
+	 sub		$offset,@ptr[0]			# switch to input
+	 vpxor		0x00($offload),@out[0],@out[0]
+	vmovups		@out[1],-16(@ptr[1])	
+	 sub		`64+1*8`(%rsp),@ptr[1]
+	 vpxor		0x10($offload),@out[1],@out[1]
+	vmovups		@out[2],-16(@ptr[2])	
+	 sub		`64+2*8`(%rsp),@ptr[2]
+	 vpxor		0x20($offload),@out[2],@out[2]
+	vmovups		@out[3],-16(@ptr[3])
+	 sub		`64+3*8`(%rsp),@ptr[3]
+	 vpxor		0x30($offload),@out[3],@out[3]
+	vmovups		@out[4],-16(@ptr[4])
+	 sub		`64+4*8`(%rsp),@ptr[4]
+	 vpxor		@inp[0],@out[4],@out[4]
+	vmovups		@out[5],-16(@ptr[5])	
+	 sub		`64+5*8`(%rsp),@ptr[5]
+	 vpxor		@inp[1],@out[5],@out[5]
+	vmovups		@out[6],-16(@ptr[6])	
+	 sub		`64+6*8`(%rsp),@ptr[6]
+	 vpxor		@inp[2],@out[6],@out[6]
+	vmovups		@out[7],-16(@ptr[7])
+	 sub		`64+7*8`(%rsp),@ptr[7]
+	 vpxor		@inp[3],@out[7],@out[7]
+
+	dec	$num
+	jnz	.Loop_enc8x
+
+	mov	16(%rsp),%rax			# original %rsp
+	#mov	24(%rsp),$num
+	#lea	`40*8`($inp),$inp
+	#dec	$num
+	#jnz	.Lenc8x_loop_grande
+
+.Lenc8x_done:
+	vzeroupper
+___
+$code.=<<___ if ($win64);
+	movaps	-0xd8(%rax),%xmm6
+	movaps	-0xc8(%rax),%xmm7
+	movaps	-0xb8(%rax),%xmm8
+	movaps	-0xa8(%rax),%xmm9
+	movaps	-0x98(%rax),%xmm10
+	movaps	-0x88(%rax),%xmm11
+	movaps	-0x78(%rax),%xmm12
+	movaps	-0x68(%rax),%xmm13
+	movaps	-0x58(%rax),%xmm14
+	movaps	-0x48(%rax),%xmm15
+___
+$code.=<<___;
+	mov	-48(%rax),%r15
+	mov	-40(%rax),%r14
+	mov	-32(%rax),%r13
+	mov	-24(%rax),%r12
+	mov	-16(%rax),%rbp
+	mov	-8(%rax),%rbx
+	lea	(%rax),%rsp
+.Lenc8x_epilogue:
+	ret
+.size	aesni_multi_cbc_encrypt_avx,.-aesni_multi_cbc_encrypt_avx
+
+.type	aesni_multi_cbc_decrypt_avx,\@function,3
+.align	32
+aesni_multi_cbc_decrypt_avx:
+_avx_cbc_dec_shortcut:
+	mov	%rsp,%rax
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+___
+$code.=<<___ if ($win64);
+	lea	-0xa8(%rsp),%rsp
+	movaps	%xmm6,(%rsp)
+	movaps	%xmm7,0x10(%rsp)
+	movaps	%xmm8,0x20(%rsp)
+	movaps	%xmm9,0x30(%rsp)
+	movaps	%xmm10,0x40(%rsp)
+	movaps	%xmm11,0x50(%rsp)
+	movaps	%xmm12,-0x78(%rax)
+	movaps	%xmm13,-0x68(%rax)
+	movaps	%xmm14,-0x58(%rax)
+	movaps	%xmm15,-0x48(%rax)
+___
+$code.=<<___;
+	# stack layout
+	#
+	# +0	output sink
+	# +16	input sink [original %rsp and $num]
+	# +32	counters
+	# +64	distances between inputs and outputs
+	# +128	off-load area for @inp[0..3]
+	# +192	IV/input offload
+
+	sub	\$256,%rsp
+	and	\$-256,%rsp
+	sub	\$192,%rsp
+	mov	%rax,16(%rsp)			# original %rsp
+
+.Ldec8x_body:
+	vzeroupper
+	vmovdqu	($key),$zero			# 0-round key
+	lea	0x78($key),$key			# size optimization
+	lea	40*4($inp),$inp
+	shr	\$1,$num
+
+.Ldec8x_loop_grande:
+	#mov	$num,24(%rsp)			# original $num
+	xor	$num,$num
+___
+for($i=0;$i<8;$i++) {
+  my $temp = $i ? $offload : $offset;
+    $code.=<<___;
+	mov	`40*$i+16-40*4`($inp),$one	# borrow $one for number of blocks
+	mov	`40*$i+0-40*4`($inp),@ptr[$i]	# input pointer
+	cmp	$num,$one
+	mov	`40*$i+8-40*4`($inp),$temp	# output pointer
+	cmovg	$one,$num			# find maximum
+	test	$one,$one
+	vmovdqu	`40*$i+24-40*4`($inp),@out[$i]	# load IV
+	mov	$one,`32+4*$i`(%rsp)		# initialize counters
+	cmovle	%rsp,@ptr[$i]			# cancel input
+	sub	@ptr[$i],$temp			# distance between input and output
+	mov	$temp,`64+8*$i`(%rsp)		# initialize distances
+	vmovdqu	@out[$i],`192+16*$i`(%rsp)	# offload IV
+___
+}
+$code.=<<___;
+	test	$num,$num
+	jz	.Ldec8x_done
+
+	vmovups	0x10-0x78($key),$rndkey1
+	vmovups	0x20-0x78($key),$rndkey0
+	mov	0xf0-0x78($key),$rounds
+	 lea	192+128(%rsp),$offload		# offload area
+
+	vmovdqu	(@ptr[0]),@out[0]		# load inputs
+	vmovdqu	(@ptr[1]),@out[1]
+	vmovdqu	(@ptr[2]),@out[2]
+	vmovdqu	(@ptr[3]),@out[3]
+	vmovdqu	(@ptr[4]),@out[4]
+	vmovdqu	(@ptr[5]),@out[5]
+	vmovdqu	(@ptr[6]),@out[6]
+	vmovdqu	(@ptr[7]),@out[7]
+	vmovdqu	@out[0],0x00($offload)		# offload inputs
+	vpxor	$zero,@out[0],@out[0]		# xor inputs with 0-round
+	vmovdqu	@out[1],0x10($offload)
+	vpxor	$zero,@out[1],@out[1]
+	vmovdqu	@out[2],0x20($offload)
+	vpxor	$zero,@out[2],@out[2]
+	vmovdqu	@out[3],0x30($offload)
+	vpxor	$zero,@out[3],@out[3]
+	vmovdqu	@out[4],0x40($offload)
+	vpxor	$zero,@out[4],@out[4]
+	vmovdqu	@out[5],0x50($offload)
+	vpxor	$zero,@out[5],@out[5]
+	vmovdqu	@out[6],0x60($offload)
+	vpxor	$zero,@out[6],@out[6]
+	vmovdqu	@out[7],0x70($offload)
+	vpxor	$zero,@out[7],@out[7]
+	xor	\$0x80,$offload
+	mov	\$1,$one			# constant of 1
+	jmp	.Loop_dec8x
+
+.align	32
+.Loop_dec8x:
+___
+for($i=0;$i<8;$i++) {
+my $rndkey=($i&1)?$rndkey0:$rndkey1;
+$code.=<<___;
+	vaesdec		$rndkey,@out[0],@out[0]
+	 cmp		32+4*$i(%rsp),$one
+___
+$code.=<<___ if ($i);
+	 mov		64+8*$i(%rsp),$offset
+___
+$code.=<<___;
+	vaesdec		$rndkey,@out[1],@out[1]
+	prefetcht0	31(@ptr[$i])			# prefetch input
+	vaesdec		$rndkey,@out[2],@out[2]
+___
+$code.=<<___ if ($i>1);
+	prefetcht0	15(@ptr[$i-2])			# prefetch output
+___
+$code.=<<___;
+	vaesdec		$rndkey,@out[3],@out[3]
+	 lea		(@ptr[$i],$offset),$offset
+	 cmovge		%rsp,@ptr[$i]			# cancel input
+	vaesdec		$rndkey,@out[4],@out[4]
+	 cmovg		%rsp,$offset			# sink output
+	vaesdec		$rndkey,@out[5],@out[5]
+	 sub		@ptr[$i],$offset
+	vaesdec		$rndkey,@out[6],@out[6]
+	 vmovdqu	16(@ptr[$i]),@inp[$i%4]		# load input
+	 mov		$offset,64+8*$i(%rsp)
+	vaesdec		$rndkey,@out[7],@out[7]
+	vmovups		`16*(3+$i)-0x78`($key),$rndkey
+	 lea		16(@ptr[$i],$offset),@ptr[$i]	# switch to output
+___
+$code.=<<___ if ($i<4);
+	 vmovdqu	@inp[$i%4],`128+16*$i`(%rsp)	# off-load
+___
+}
+$code.=<<___;
+	 vmovdqu	32(%rsp),$counters
+	prefetcht0	15(@ptr[$i-2])			# prefetch output
+	prefetcht0	15(@ptr[$i-1])
+	cmp	\$11,$rounds
+	jb	.Ldec8x_tail
+
+	vaesdec		$rndkey1,@out[0],@out[0]
+	vaesdec		$rndkey1,@out[1],@out[1]
+	vaesdec		$rndkey1,@out[2],@out[2]
+	vaesdec		$rndkey1,@out[3],@out[3]
+	vaesdec		$rndkey1,@out[4],@out[4]
+	vaesdec		$rndkey1,@out[5],@out[5]
+	vaesdec		$rndkey1,@out[6],@out[6]
+	vaesdec		$rndkey1,@out[7],@out[7]
+	vmovups		0xb0-0x78($key),$rndkey1
+
+	vaesdec		$rndkey0,@out[0],@out[0]
+	vaesdec		$rndkey0,@out[1],@out[1]
+	vaesdec		$rndkey0,@out[2],@out[2]
+	vaesdec		$rndkey0,@out[3],@out[3]
+	vaesdec		$rndkey0,@out[4],@out[4]
+	vaesdec		$rndkey0,@out[5],@out[5]
+	vaesdec		$rndkey0,@out[6],@out[6]
+	vaesdec		$rndkey0,@out[7],@out[7]
+	vmovups		0xc0-0x78($key),$rndkey0
+	je	.Ldec8x_tail
+
+	vaesdec		$rndkey1,@out[0],@out[0]
+	vaesdec		$rndkey1,@out[1],@out[1]
+	vaesdec		$rndkey1,@out[2],@out[2]
+	vaesdec		$rndkey1,@out[3],@out[3]
+	vaesdec		$rndkey1,@out[4],@out[4]
+	vaesdec		$rndkey1,@out[5],@out[5]
+	vaesdec		$rndkey1,@out[6],@out[6]
+	vaesdec		$rndkey1,@out[7],@out[7]
+	vmovups		0xd0-0x78($key),$rndkey1
+
+	vaesdec		$rndkey0,@out[0],@out[0]
+	vaesdec		$rndkey0,@out[1],@out[1]
+	vaesdec		$rndkey0,@out[2],@out[2]
+	vaesdec		$rndkey0,@out[3],@out[3]
+	vaesdec		$rndkey0,@out[4],@out[4]
+	vaesdec		$rndkey0,@out[5],@out[5]
+	vaesdec		$rndkey0,@out[6],@out[6]
+	vaesdec		$rndkey0,@out[7],@out[7]
+	vmovups		0xe0-0x78($key),$rndkey0
+
+.Ldec8x_tail:
+	vaesdec		$rndkey1,@out[0],@out[0]
+	 vpxor		$zero,$zero,$zero
+	vaesdec		$rndkey1,@out[1],@out[1]
+	vaesdec		$rndkey1,@out[2],@out[2]
+	 vpcmpgtd	$zero,$counters,$zero
+	vaesdec		$rndkey1,@out[3],@out[3]
+	vaesdec		$rndkey1,@out[4],@out[4]
+	 vpaddd		$counters,$zero,$zero		# decrement counters
+	 vmovdqu	48(%rsp),$counters
+	vaesdec		$rndkey1,@out[5],@out[5]
+	 mov		64(%rsp),$offset		# pre-load 1st offset
+	vaesdec		$rndkey1,@out[6],@out[6]
+	vaesdec		$rndkey1,@out[7],@out[7]
+	vmovups		0x10-0x78($key),$rndkey1
+
+	vaesdeclast	$rndkey0,@out[0],@out[0]
+	 vmovdqa	$zero,32(%rsp)			# update counters
+	 vpxor		$zero,$zero,$zero
+	vaesdeclast	$rndkey0,@out[1],@out[1]
+	vpxor		0x00($offload),@out[0],@out[0]	# xor with IV
+	vaesdeclast	$rndkey0,@out[2],@out[2]
+	vpxor		0x10($offload),@out[1],@out[1]
+	 vpcmpgtd	$zero,$counters,$zero
+	vaesdeclast	$rndkey0,@out[3],@out[3]
+	vpxor		0x20($offload),@out[2],@out[2]
+	vaesdeclast	$rndkey0,@out[4],@out[4]
+	vpxor		0x30($offload),@out[3],@out[3]
+	 vpaddd		$zero,$counters,$counters	# decrement counters
+	 vmovdqu	-0x78($key),$zero		# 0-round
+	vaesdeclast	$rndkey0,@out[5],@out[5]
+	vpxor		0x40($offload),@out[4],@out[4]
+	vaesdeclast	$rndkey0,@out[6],@out[6]
+	vpxor		0x50($offload),@out[5],@out[5]
+	 vmovdqa	$counters,48(%rsp)		# update counters
+	vaesdeclast	$rndkey0,@out[7],@out[7]
+	vpxor		0x60($offload),@out[6],@out[6]
+	vmovups		0x20-0x78($key),$rndkey0
+
+	vmovups		@out[0],-16(@ptr[0])		# write output
+	 sub		$offset,@ptr[0]			# switch to input
+	 vmovdqu	128+0(%rsp),@out[0]
+	vpxor		0x70($offload),@out[7],@out[7]
+	vmovups		@out[1],-16(@ptr[1])	
+	 sub		`64+1*8`(%rsp),@ptr[1]
+	 vmovdqu	@out[0],0x00($offload)
+	 vpxor		$zero,@out[0],@out[0]
+	 vmovdqu	128+16(%rsp),@out[1]
+	vmovups		@out[2],-16(@ptr[2])	
+	 sub		`64+2*8`(%rsp),@ptr[2]
+	 vmovdqu	@out[1],0x10($offload)
+	 vpxor		$zero,@out[1],@out[1]
+	 vmovdqu	128+32(%rsp),@out[2]
+	vmovups		@out[3],-16(@ptr[3])
+	 sub		`64+3*8`(%rsp),@ptr[3]
+	 vmovdqu	@out[2],0x20($offload)
+	 vpxor		$zero,@out[2],@out[2]
+	 vmovdqu	128+48(%rsp),@out[3]
+	vmovups		@out[4],-16(@ptr[4])
+	 sub		`64+4*8`(%rsp),@ptr[4]
+	 vmovdqu	@out[3],0x30($offload)
+	 vpxor		$zero,@out[3],@out[3]
+	 vmovdqu	@inp[0],0x40($offload)
+	 vpxor		@inp[0],$zero,@out[4]
+	vmovups		@out[5],-16(@ptr[5])	
+	 sub		`64+5*8`(%rsp),@ptr[5]
+	 vmovdqu	@inp[1],0x50($offload)
+	 vpxor		@inp[1],$zero,@out[5]
+	vmovups		@out[6],-16(@ptr[6])	
+	 sub		`64+6*8`(%rsp),@ptr[6]
+	 vmovdqu	@inp[2],0x60($offload)
+	 vpxor		@inp[2],$zero,@out[6]
+	vmovups		@out[7],-16(@ptr[7])
+	 sub		`64+7*8`(%rsp),@ptr[7]
+	 vmovdqu	@inp[3],0x70($offload)
+	 vpxor		@inp[3],$zero,@out[7]
+
+	xor	\$128,$offload
+	dec	$num
+	jnz	.Loop_dec8x
+
+	mov	16(%rsp),%rax			# original %rsp
+	#mov	24(%rsp),$num
+	#lea	`40*8`($inp),$inp
+	#dec	$num
+	#jnz	.Ldec8x_loop_grande
+
+.Ldec8x_done:
+	vzeroupper
+___
+$code.=<<___ if ($win64);
+	movaps	-0xd8(%rax),%xmm6
+	movaps	-0xc8(%rax),%xmm7
+	movaps	-0xb8(%rax),%xmm8
+	movaps	-0xa8(%rax),%xmm9
+	movaps	-0x98(%rax),%xmm10
+	movaps	-0x88(%rax),%xmm11
+	movaps	-0x78(%rax),%xmm12
+	movaps	-0x68(%rax),%xmm13
+	movaps	-0x58(%rax),%xmm14
+	movaps	-0x48(%rax),%xmm15
+___
+$code.=<<___;
+	mov	-48(%rax),%r15
+	mov	-40(%rax),%r14
+	mov	-32(%rax),%r13
+	mov	-24(%rax),%r12
+	mov	-16(%rax),%rbp
+	mov	-8(%rax),%rbx
+	lea	(%rax),%rsp
+.Ldec8x_epilogue:
+	ret
+.size	aesni_multi_cbc_decrypt_avx,.-aesni_multi_cbc_decrypt_avx
+___
+						}}}
+
+if ($win64) {
+# EXCEPTION_DISPOSITION handler (EXCEPTION_RECORD *rec,ULONG64 frame,
+#		CONTEXT *context,DISPATCHER_CONTEXT *disp)
+$rec="%rcx";
+$frame="%rdx";
+$context="%r8";
+$disp="%r9";
+
+$code.=<<___;
+.extern	__imp_RtlVirtualUnwind
+.type	se_handler,\@abi-omnipotent
+.align	16
+se_handler:
+	push	%rsi
+	push	%rdi
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+	pushfq
+	sub	\$64,%rsp
+
+	mov	120($context),%rax	# pull context->Rax
+	mov	248($context),%rbx	# pull context->Rip
+
+	mov	8($disp),%rsi		# disp->ImageBase
+	mov	56($disp),%r11		# disp->HandlerData
+
+	mov	0(%r11),%r10d		# HandlerData[0]
+	lea	(%rsi,%r10),%r10	# prologue label
+	cmp	%r10,%rbx		# context->Rip<.Lprologue
+	jb	.Lin_prologue
+
+	mov	152($context),%rax	# pull context->Rsp
+
+	mov	4(%r11),%r10d		# HandlerData[1]
+	lea	(%rsi,%r10),%r10	# epilogue label
+	cmp	%r10,%rbx		# context->Rip>=.Lepilogue
+	jae	.Lin_prologue
+
+	mov	16(%rax),%rax		# pull saved stack pointer
+
+	mov	-8(%rax),%rbx
+	mov	-16(%rax),%rbp
+	mov	-24(%rax),%r12
+	mov	-32(%rax),%r13
+	mov	-40(%rax),%r14
+	mov	-48(%rax),%r15
+	mov	%rbx,144($context)	# restore context->Rbx
+	mov	%rbp,160($context)	# restore context->Rbp
+	mov	%r12,216($context)	# restore cotnext->R12
+	mov	%r13,224($context)	# restore cotnext->R13
+	mov	%r14,232($context)	# restore cotnext->R14
+	mov	%r15,240($context)	# restore cotnext->R15
+
+	lea	-56-10*16(%rax),%rsi
+	lea	512($context),%rdi	# &context.Xmm6
+	mov	\$20,%ecx
+	.long	0xa548f3fc		# cld; rep movsq
+
+.Lin_prologue:
+	mov	8(%rax),%rdi
+	mov	16(%rax),%rsi
+	mov	%rax,152($context)	# restore context->Rsp
+	mov	%rsi,168($context)	# restore context->Rsi
+	mov	%rdi,176($context)	# restore context->Rdi
+
+	mov	40($disp),%rdi		# disp->ContextRecord
+	mov	$context,%rsi		# context
+	mov	\$154,%ecx		# sizeof(CONTEXT)
+	.long	0xa548f3fc		# cld; rep movsq
+
+	mov	$disp,%rsi
+	xor	%rcx,%rcx		# arg1, UNW_FLAG_NHANDLER
+	mov	8(%rsi),%rdx		# arg2, disp->ImageBase
+	mov	0(%rsi),%r8		# arg3, disp->ControlPc
+	mov	16(%rsi),%r9		# arg4, disp->FunctionEntry
+	mov	40(%rsi),%r10		# disp->ContextRecord
+	lea	56(%rsi),%r11		# &disp->HandlerData
+	lea	24(%rsi),%r12		# &disp->EstablisherFrame
+	mov	%r10,32(%rsp)		# arg5
+	mov	%r11,40(%rsp)		# arg6
+	mov	%r12,48(%rsp)		# arg7
+	mov	%rcx,56(%rsp)		# arg8, (NULL)
+	call	*__imp_RtlVirtualUnwind(%rip)
+
+	mov	\$1,%eax		# ExceptionContinueSearch
+	add	\$64,%rsp
+	popfq
+	pop	%r15
+	pop	%r14
+	pop	%r13
+	pop	%r12
+	pop	%rbp
+	pop	%rbx
+	pop	%rdi
+	pop	%rsi
+	ret
+.size	se_handler,.-se_handler
+
+.section	.pdata
+.align	4
+	.rva	.LSEH_begin_aesni_multi_cbc_encrypt
+	.rva	.LSEH_end_aesni_multi_cbc_encrypt
+	.rva	.LSEH_info_aesni_multi_cbc_encrypt
+	.rva	.LSEH_begin_aesni_multi_cbc_decrypt
+	.rva	.LSEH_end_aesni_multi_cbc_decrypt
+	.rva	.LSEH_info_aesni_multi_cbc_decrypt
+___
+$code.=<<___ if ($avx);
+	.rva	.LSEH_begin_aesni_multi_cbc_encrypt_avx
+	.rva	.LSEH_end_aesni_multi_cbc_encrypt_avx
+	.rva	.LSEH_info_aesni_multi_cbc_encrypt_avx
+	.rva	.LSEH_begin_aesni_multi_cbc_decrypt_avx
+	.rva	.LSEH_end_aesni_multi_cbc_decrypt_avx
+	.rva	.LSEH_info_aesni_multi_cbc_decrypt_avx
+___
+$code.=<<___;
+.section	.xdata
+.align	8
+.LSEH_info_aesni_multi_cbc_encrypt:
+	.byte	9,0,0,0
+	.rva	se_handler
+	.rva	.Lenc4x_body,.Lenc4x_epilogue		# HandlerData[]
+.LSEH_info_aesni_multi_cbc_decrypt:
+	.byte	9,0,0,0
+	.rva	se_handler
+	.rva	.Ldec4x_body,.Ldec4x_epilogue		# HandlerData[]
+___
+$code.=<<___ if ($avx);
+.LSEH_info_aesni_multi_cbc_encrypt_avx:
+	.byte	9,0,0,0
+	.rva	se_handler
+	.rva	.Lenc8x_body,.Lenc8x_epilogue		# HandlerData[]
+.LSEH_info_aesni_multi_cbc_decrypt_avx:
+	.byte	9,0,0,0
+	.rva	se_handler
+	.rva	.Ldec8x_body,.Ldec8x_epilogue		# HandlerData[]
+___
+}
+####################################################################
+
+sub rex {
+  local *opcode=shift;
+  my ($dst,$src)=@_;
+  my $rex=0;
+
+    $rex|=0x04			if($dst>=8);
+    $rex|=0x01			if($src>=8);
+    push @opcode,$rex|0x40	if($rex);
+}
+
+sub aesni {
+  my $line=shift;
+  my @opcode=(0x66);
+
+    if ($line=~/(aeskeygenassist)\s+\$([x0-9a-f]+),\s*%xmm([0-9]+),\s*%xmm([0-9]+)/) {
+	rex(\@opcode,$4,$3);
+	push @opcode,0x0f,0x3a,0xdf;
+	push @opcode,0xc0|($3&7)|(($4&7)<<3);	# ModR/M
+	my $c=$2;
+	push @opcode,$c=~/^0/?oct($c):$c;
+	return ".byte\t".join(',',@opcode);
+    }
+    elsif ($line=~/(aes[a-z]+)\s+%xmm([0-9]+),\s*%xmm([0-9]+)/) {
+	my %opcodelet = (
+		"aesimc" => 0xdb,
+		"aesenc" => 0xdc,	"aesenclast" => 0xdd,
+		"aesdec" => 0xde,	"aesdeclast" => 0xdf
+	);
+	return undef if (!defined($opcodelet{$1}));
+	rex(\@opcode,$3,$2);
+	push @opcode,0x0f,0x38,$opcodelet{$1};
+	push @opcode,0xc0|($2&7)|(($3&7)<<3);	# ModR/M
+	return ".byte\t".join(',',@opcode);
+    }
+    elsif ($line=~/(aes[a-z]+)\s+([0x1-9a-fA-F]*)\(%rsp\),\s*%xmm([0-9]+)/) {
+	my %opcodelet = (
+		"aesenc" => 0xdc,	"aesenclast" => 0xdd,
+		"aesdec" => 0xde,	"aesdeclast" => 0xdf
+	);
+	return undef if (!defined($opcodelet{$1}));
+	my $off = $2;
+	push @opcode,0x44 if ($3>=8);
+	push @opcode,0x0f,0x38,$opcodelet{$1};
+	push @opcode,0x44|(($3&7)<<3),0x24;	# ModR/M
+	push @opcode,($off=~/^0/?oct($off):$off)&0xff;
+	return ".byte\t".join(',',@opcode);
+    }
+    return $line;
+}
+
+$code =~ s/\`([^\`]*)\`/eval($1)/gem;
+$code =~ s/\b(aes.*%xmm[0-9]+).*$/aesni($1)/gem;
+
+print $code;
+close STDOUT;
diff --git a/crypto/aes/asm/aesni-sha1-x86_64.pl b/crypto/aes/asm/aesni-sha1-x86_64.pl
new file mode 100644
index 000000000..4b979a734
--- /dev/null
+++ b/crypto/aes/asm/aesni-sha1-x86_64.pl
@@ -0,0 +1,2066 @@
+#! /usr/bin/env perl
+# Copyright 2011-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+#
+# ====================================================================
+# Written by Andy Polyakov  for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+#
+# June 2011
+#
+# This is AESNI-CBC+SHA1 "stitch" implementation. The idea, as spelled
+# in http://download.intel.com/design/intarch/papers/323686.pdf, is
+# that since AESNI-CBC encrypt exhibit *very* low instruction-level
+# parallelism, interleaving it with another algorithm would allow to
+# utilize processor resources better and achieve better performance.
+# SHA1 instruction sequences(*) are taken from sha1-x86_64.pl and
+# AESNI code is weaved into it. Below are performance numbers in
+# cycles per processed byte, less is better, for standalone AESNI-CBC
+# encrypt, sum of the latter and standalone SHA1, and "stitched"
+# subroutine:
+#
+#		AES-128-CBC	+SHA1		stitch      gain
+# Westmere	3.77[+5.3]	9.07		6.55	    +38%
+# Sandy Bridge	5.05[+5.0(6.1)]	10.06(11.15)	5.98(7.05)  +68%(+58%)
+# Ivy Bridge	5.05[+4.6]	9.65		5.54        +74%
+# Haswell	4.43[+3.6(4.2)]	8.00(8.58)	4.55(5.21)  +75%(+65%)
+# Skylake	2.63[+3.5(4.1)]	6.17(6.69)	4.23(4.44)  +46%(+51%)
+# Bulldozer	5.77[+6.0]	11.72		6.37        +84%
+#
+#		AES-192-CBC
+# Westmere	4.51		9.81		6.80	    +44%
+# Sandy Bridge	6.05		11.06(12.15)	6.11(7.19)  +81%(+69%)
+# Ivy Bridge	6.05		10.65		6.07        +75%
+# Haswell	5.29		8.86(9.44)	5.32(5.32)  +67%(+77%)
+# Bulldozer	6.89		12.84		6.96        +84%
+#
+#		AES-256-CBC
+# Westmere	5.25		10.55		7.21	    +46%
+# Sandy Bridge	7.05		12.06(13.15)	7.12(7.72)  +69%(+70%)
+# Ivy Bridge	7.05		11.65		7.12        +64%
+# Haswell	6.19		9.76(10.34)	6.21(6.25)  +57%(+65%)
+# Skylake	3.62		7.16(7.68)	4.56(4.76)  +57%(+61$)
+# Bulldozer	8.00		13.95		8.25        +69%
+#
+# (*)	There are two code paths: SSSE3 and AVX. See sha1-568.pl for
+#	background information. Above numbers in parentheses are SSSE3
+#	results collected on AVX-capable CPU, i.e. apply on OSes that
+#	don't support AVX.
+#
+# Needless to mention that it makes no sense to implement "stitched"
+# *decrypt* subroutine. Because *both* AESNI-CBC decrypt and SHA1
+# fully utilize parallelism, so stitching would not give any gain
+# anyway. Well, there might be some, e.g. because of better cache
+# locality... For reference, here are performance results for
+# standalone AESNI-CBC decrypt:
+#
+#		AES-128-CBC	AES-192-CBC	AES-256-CBC
+# Westmere	1.25		1.50		1.75
+# Sandy Bridge	0.74		0.91		1.09
+# Ivy Bridge	0.74		0.90		1.11
+# Haswell	0.63		0.76		0.88
+# Bulldozer	0.70		0.85		0.99
+
+# And indeed:
+#
+#		AES-256-CBC	+SHA1		stitch      gain
+# Westmere	1.75		7.20		6.68        +7.8%
+# Sandy Bridge	1.09		6.09(7.22)	5.82(6.95)  +4.6%(+3.9%)
+# Ivy Bridge	1.11		5.70		5.45        +4.6%
+# Haswell	0.88		4.45(5.00)	4.39(4.69)  +1.4%(*)(+6.6%)
+# Bulldozer	0.99		6.95		5.95        +17%(**)
+#
+# (*)	Tiny improvement coefficient on Haswell is because we compare
+#	AVX1 stitch to sum with AVX2 SHA1.
+# (**)	Execution is fully dominated by integer code sequence and
+#	SIMD still hardly shows [in single-process benchmark;-]
+
+$flavour = shift;
+$output  = shift;
+if ($flavour =~ /\./) { $output = $flavour; undef $flavour; }
+
+$win64=0; $win64=1 if ($flavour =~ /[nm]asm|mingw64/ || $output =~ /\.asm$/);
+
+$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+( $xlate="${dir}x86_64-xlate.pl" and -f $xlate ) or
+( $xlate="${dir}../../perlasm/x86_64-xlate.pl" and -f $xlate) or
+die "can't locate x86_64-xlate.pl";
+
+$avx=1 if (`$ENV{CC} -Wa,-v -c -o /dev/null -x assembler /dev/null 2>&1`
+		=~ /GNU assembler version ([2-9]\.[0-9]+)/ &&
+	   $1>=2.19);
+$avx=1 if (!$avx && $win64 && ($flavour =~ /nasm/ || $ENV{ASM} =~ /nasm/) &&
+	   `nasm -v 2>&1` =~ /NASM version ([2-9]\.[0-9]+)/ &&
+	   $1>=2.09);
+$avx=1 if (!$avx && $win64 && ($flavour =~ /masm/ || $ENV{ASM} =~ /ml64/) &&
+	   `ml64 2>&1` =~ /Version ([0-9]+)\./ &&
+	   $1>=10);
+$avx=1 if (!$avx && `$ENV{CC} -v 2>&1` =~ /((?:^clang|LLVM) version|.*based on LLVM) ([3-9]\.[0-9]+)/ && $2>=3.0);
+
+$shaext=1;	### set to zero if compiling for 1.0.1
+
+$stitched_decrypt=0;
+
+open OUT,"| \"$^X\" \"$xlate\" $flavour \"$output\"";
+*STDOUT=*OUT;
+
+# void aesni_cbc_sha1_enc(const void *inp,
+#			void *out,
+#			size_t length,
+#			const AES_KEY *key,
+#			unsigned char *iv,
+#			SHA_CTX *ctx,
+#			const void *in0);
+
+$code.=<<___;
+.text
+.extern	OPENSSL_ia32cap_P
+
+.globl	aesni_cbc_sha1_enc
+.type	aesni_cbc_sha1_enc,\@abi-omnipotent
+.align	32
+aesni_cbc_sha1_enc:
+	# caller should check for SSSE3 and AES-NI bits
+	mov	OPENSSL_ia32cap_P+0(%rip),%r10d
+	mov	OPENSSL_ia32cap_P+4(%rip),%r11
+___
+$code.=<<___ if ($shaext);
+	bt	\$61,%r11		# check SHA bit
+	jc	aesni_cbc_sha1_enc_shaext
+___
+$code.=<<___ if ($avx);
+	and	\$`1<<28`,%r11d		# mask AVX bit
+	and	\$`1<<30`,%r10d		# mask "Intel CPU" bit
+	or	%r11d,%r10d
+	cmp	\$`1<<28|1<<30`,%r10d
+	je	aesni_cbc_sha1_enc_avx
+___
+$code.=<<___;
+	jmp	aesni_cbc_sha1_enc_ssse3
+	ret
+.size	aesni_cbc_sha1_enc,.-aesni_cbc_sha1_enc
+___
+
+my ($in0,$out,$len,$key,$ivp,$ctx,$inp)=("%rdi","%rsi","%rdx","%rcx","%r8","%r9","%r10");
+
+my $Xi=4;
+my @X=map("%xmm$_",(4..7,0..3));
+my @Tx=map("%xmm$_",(8..10));
+my @V=($A,$B,$C,$D,$E)=("%eax","%ebx","%ecx","%edx","%ebp");	# size optimization
+my @T=("%esi","%edi");
+my $j=0; my $jj=0; my $r=0; my $sn=0; my $rx=0;
+my $K_XX_XX="%r11";
+my ($rndkey0,$iv,$in)=map("%xmm$_",(11..13));			# for enc
+my @rndkey=("%xmm14","%xmm15");					# for enc
+my ($inout0,$inout1,$inout2,$inout3)=map("%xmm$_",(12..15));	# for dec
+
+if (1) {	# reassign for Atom Silvermont
+    # The goal is to minimize amount of instructions with more than
+    # 3 prefix bytes. Or in more practical terms to keep AES-NI *and*
+    # SSSE3 instructions to upper half of the register bank.
+    @X=map("%xmm$_",(8..11,4..7));
+    @Tx=map("%xmm$_",(12,13,3));
+    ($iv,$in,$rndkey0)=map("%xmm$_",(2,14,15));
+    @rndkey=("%xmm0","%xmm1");
+}
+
+sub AUTOLOAD()		# thunk [simplified] 32-bit style perlasm
+{ my $opcode = $AUTOLOAD; $opcode =~ s/.*:://;
+  my $arg = pop;
+    $arg = "\$$arg" if ($arg*1 eq $arg);
+    $code .= "\t$opcode\t".join(',',$arg,reverse @_)."\n";
+}
+
+my $_rol=sub { &rol(@_) };
+my $_ror=sub { &ror(@_) };
+
+$code.=<<___;
+.type	aesni_cbc_sha1_enc_ssse3,\@function,6
+.align	32
+aesni_cbc_sha1_enc_ssse3:
+	mov	`($win64?56:8)`(%rsp),$inp	# load 7th argument
+	#shr	\$6,$len			# debugging artefact
+	#jz	.Lepilogue_ssse3		# debugging artefact
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+	lea	`-104-($win64?10*16:0)`(%rsp),%rsp
+	#mov	$in0,$inp			# debugging artefact
+	#lea	64(%rsp),$ctx			# debugging artefact
+___
+$code.=<<___ if ($win64);
+	movaps	%xmm6,96+0(%rsp)
+	movaps	%xmm7,96+16(%rsp)
+	movaps	%xmm8,96+32(%rsp)
+	movaps	%xmm9,96+48(%rsp)
+	movaps	%xmm10,96+64(%rsp)
+	movaps	%xmm11,96+80(%rsp)
+	movaps	%xmm12,96+96(%rsp)
+	movaps	%xmm13,96+112(%rsp)
+	movaps	%xmm14,96+128(%rsp)
+	movaps	%xmm15,96+144(%rsp)
+.Lprologue_ssse3:
+___
+$code.=<<___;
+	mov	$in0,%r12			# reassign arguments
+	mov	$out,%r13
+	mov	$len,%r14
+	lea	112($key),%r15			# size optimization
+	movdqu	($ivp),$iv			# load IV
+	mov	$ivp,88(%rsp)			# save $ivp
+___
+($in0,$out,$len,$key)=map("%r$_",(12..15));	# reassign arguments
+my $rounds="${ivp}d";
+$code.=<<___;
+	shl	\$6,$len
+	sub	$in0,$out
+	mov	240-112($key),$rounds
+	add	$inp,$len		# end of input
+
+	lea	K_XX_XX(%rip),$K_XX_XX
+	mov	0($ctx),$A		# load context
+	mov	4($ctx),$B
+	mov	8($ctx),$C
+	mov	12($ctx),$D
+	mov	$B,@T[0]		# magic seed
+	mov	16($ctx),$E
+	mov	$C,@T[1]
+	xor	$D,@T[1]
+	and	@T[1],@T[0]
+
+	movdqa	64($K_XX_XX),@Tx[2]	# pbswap mask
+	movdqa	0($K_XX_XX),@Tx[1]	# K_00_19
+	movdqu	0($inp),@X[-4&7]	# load input to %xmm[0-3]
+	movdqu	16($inp),@X[-3&7]
+	movdqu	32($inp),@X[-2&7]
+	movdqu	48($inp),@X[-1&7]
+	pshufb	@Tx[2],@X[-4&7]		# byte swap
+	pshufb	@Tx[2],@X[-3&7]
+	pshufb	@Tx[2],@X[-2&7]
+	add	\$64,$inp
+	paddd	@Tx[1],@X[-4&7]		# add K_00_19
+	pshufb	@Tx[2],@X[-1&7]
+	paddd	@Tx[1],@X[-3&7]
+	paddd	@Tx[1],@X[-2&7]
+	movdqa	@X[-4&7],0(%rsp)	# X[]+K xfer to IALU
+	psubd	@Tx[1],@X[-4&7]		# restore X[]
+	movdqa	@X[-3&7],16(%rsp)
+	psubd	@Tx[1],@X[-3&7]
+	movdqa	@X[-2&7],32(%rsp)
+	psubd	@Tx[1],@X[-2&7]
+	movups	-112($key),$rndkey0	# $key[0]
+	movups	16-112($key),$rndkey[0]	# forward reference
+	jmp	.Loop_ssse3
+___
+
+my $aesenc=sub {
+  use integer;
+  my ($n,$k)=($r/10,$r%10);
+    if ($k==0) {
+      $code.=<<___;
+	movups		`16*$n`($in0),$in		# load input
+	xorps		$rndkey0,$in
+___
+      $code.=<<___ if ($n);
+	movups		$iv,`16*($n-1)`($out,$in0)	# write output
+___
+      $code.=<<___;
+	xorps		$in,$iv
+	movups		`32+16*$k-112`($key),$rndkey[1]
+	aesenc		$rndkey[0],$iv
+___
+    } elsif ($k==9) {
+      $sn++;
+      $code.=<<___;
+	cmp		\$11,$rounds
+	jb		.Laesenclast$sn
+	movups		`32+16*($k+0)-112`($key),$rndkey[1]
+	aesenc		$rndkey[0],$iv
+	movups		`32+16*($k+1)-112`($key),$rndkey[0]
+	aesenc		$rndkey[1],$iv
+	je		.Laesenclast$sn
+	movups		`32+16*($k+2)-112`($key),$rndkey[1]
+	aesenc		$rndkey[0],$iv
+	movups		`32+16*($k+3)-112`($key),$rndkey[0]
+	aesenc		$rndkey[1],$iv
+.Laesenclast$sn:
+	aesenclast	$rndkey[0],$iv
+	movups		16-112($key),$rndkey[1]		# forward reference
+___
+    } else {
+      $code.=<<___;
+	movups		`32+16*$k-112`($key),$rndkey[1]
+	aesenc		$rndkey[0],$iv
+___
+    }
+    $r++;	unshift(@rndkey,pop(@rndkey));
+};
+
+sub Xupdate_ssse3_16_31()		# recall that $Xi starts wtih 4
+{ use integer;
+  my $body = shift;
+  my @insns = (&$body,&$body,&$body,&$body);	# 40 instructions
+  my ($a,$b,$c,$d,$e);
+
+	 eval(shift(@insns));		# ror
+	&pshufd	(@X[0],@X[-4&7],0xee);	# was &movdqa	(@X[0],@X[-3&7]);
+	 eval(shift(@insns));
+	&movdqa	(@Tx[0],@X[-1&7]);
+	  &paddd	(@Tx[1],@X[-1&7]);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	&punpcklqdq(@X[0],@X[-3&7]);	# compose "X[-14]" in "X[0]", was &palignr(@X[0],@X[-4&7],8);
+	 eval(shift(@insns));
+	 eval(shift(@insns));		# rol
+	 eval(shift(@insns));
+	&psrldq	(@Tx[0],4);		# "X[-3]", 3 dwords
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	&pxor	(@X[0],@X[-4&7]);	# "X[0]"^="X[-16]"
+	 eval(shift(@insns));
+	 eval(shift(@insns));		# ror
+	&pxor	(@Tx[0],@X[-2&7]);	# "X[-3]"^"X[-8]"
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	&pxor	(@X[0],@Tx[0]);		# "X[0]"^="X[-3]"^"X[-8]"
+	 eval(shift(@insns));
+	 eval(shift(@insns));		# rol
+	  &movdqa	(eval(16*(($Xi-1)&3))."(%rsp)",@Tx[1]);	# X[]+K xfer to IALU
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	&movdqa	(@Tx[2],@X[0]);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));		# ror
+	&movdqa	(@Tx[0],@X[0]);
+	 eval(shift(@insns));
+
+	&pslldq	(@Tx[2],12);		# "X[0]"<<96, extract one dword
+	&paddd	(@X[0],@X[0]);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	&psrld	(@Tx[0],31);
+	 eval(shift(@insns));
+	 eval(shift(@insns));		# rol
+	 eval(shift(@insns));
+	&movdqa	(@Tx[1],@Tx[2]);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	&psrld	(@Tx[2],30);
+	 eval(shift(@insns));
+	 eval(shift(@insns));		# ror
+	&por	(@X[0],@Tx[0]);		# "X[0]"<<<=1
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	&pslld	(@Tx[1],2);
+	&pxor	(@X[0],@Tx[2]);
+	 eval(shift(@insns));
+	  &movdqa	(@Tx[2],eval(16*(($Xi)/5))."($K_XX_XX)");	# K_XX_XX
+	 eval(shift(@insns));		# rol
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	&pxor	(@X[0],@Tx[1]);		# "X[0]"^=("X[0]">>96)<<<2
+	&pshufd (@Tx[1],@X[-1&7],0xee)	if ($Xi==7);	# was &movdqa	(@Tx[0],@X[-1&7]) in Xupdate_ssse3_32_79
+
+	 foreach (@insns) { eval; }	# remaining instructions [if any]
+
+  $Xi++;	push(@X,shift(@X));	# "rotate" X[]
+		push(@Tx,shift(@Tx));
+}
+
+sub Xupdate_ssse3_32_79()
+{ use integer;
+  my $body = shift;
+  my @insns = (&$body,&$body,&$body,&$body);	# 32 to 44 instructions
+  my ($a,$b,$c,$d,$e);
+
+	 eval(shift(@insns))		if ($Xi==8);
+	&pxor	(@X[0],@X[-4&7]);	# "X[0]"="X[-32]"^"X[-16]"
+	 eval(shift(@insns))		if ($Xi==8);
+	 eval(shift(@insns));		# body_20_39
+	 eval(shift(@insns));
+	 eval(shift(@insns))		if (@insns[1] =~ /_ror/);
+	 eval(shift(@insns))		if (@insns[0] =~ /_ror/);
+	&punpcklqdq(@Tx[0],@X[-1&7]);	# compose "X[-6]", was &palignr(@Tx[0],@X[-2&7],8);
+	 eval(shift(@insns));
+	 eval(shift(@insns));		# rol
+
+	&pxor	(@X[0],@X[-7&7]);	# "X[0]"^="X[-28]"
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	if ($Xi%5) {
+	  &movdqa	(@Tx[2],@Tx[1]);# "perpetuate" K_XX_XX...
+	} else {			# ... or load next one
+	  &movdqa	(@Tx[2],eval(16*($Xi/5))."($K_XX_XX)");
+	}
+	 eval(shift(@insns));		# ror
+	  &paddd	(@Tx[1],@X[-1&7]);
+	 eval(shift(@insns));
+
+	&pxor	(@X[0],@Tx[0]);		# "X[0]"^="X[-6]"
+	 eval(shift(@insns));		# body_20_39
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));		# rol
+	 eval(shift(@insns))		if (@insns[0] =~ /_ror/);
+
+	&movdqa	(@Tx[0],@X[0]);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	  &movdqa	(eval(16*(($Xi-1)&3))."(%rsp)",@Tx[1]);	# X[]+K xfer to IALU
+	 eval(shift(@insns));		# ror
+	 eval(shift(@insns));
+	 eval(shift(@insns));		# body_20_39
+
+	&pslld	(@X[0],2);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&psrld	(@Tx[0],30);
+	 eval(shift(@insns))		if (@insns[0] =~ /_rol/);# rol
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));		# ror
+
+	&por	(@X[0],@Tx[0]);		# "X[0]"<<<=2
+	 eval(shift(@insns));
+	 eval(shift(@insns));		# body_20_39
+	 eval(shift(@insns))		if (@insns[1] =~ /_rol/);
+	 eval(shift(@insns))		if (@insns[0] =~ /_rol/);
+	  &pshufd(@Tx[1],@X[-1&7],0xee)	if ($Xi<19);	# was &movdqa	(@Tx[1],@X[0])
+	 eval(shift(@insns));
+	 eval(shift(@insns));		# rol
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));		# rol
+	 eval(shift(@insns));
+
+	 foreach (@insns) { eval; }	# remaining instructions
+
+  $Xi++;	push(@X,shift(@X));	# "rotate" X[]
+		push(@Tx,shift(@Tx));
+}
+
+sub Xuplast_ssse3_80()
+{ use integer;
+  my $body = shift;
+  my @insns = (&$body,&$body,&$body,&$body);	# 32 instructions
+  my ($a,$b,$c,$d,$e);
+
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	  &paddd	(@Tx[1],@X[-1&7]);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	  &movdqa	(eval(16*(($Xi-1)&3))."(%rsp)",@Tx[1]);	# X[]+K xfer IALU
+
+	 foreach (@insns) { eval; }		# remaining instructions
+
+	&cmp	($inp,$len);
+	&je	(shift);
+
+	unshift(@Tx,pop(@Tx));
+
+	&movdqa	(@Tx[2],"64($K_XX_XX)");	# pbswap mask
+	&movdqa	(@Tx[1],"0($K_XX_XX)");		# K_00_19
+	&movdqu	(@X[-4&7],"0($inp)");		# load input
+	&movdqu	(@X[-3&7],"16($inp)");
+	&movdqu	(@X[-2&7],"32($inp)");
+	&movdqu	(@X[-1&7],"48($inp)");
+	&pshufb	(@X[-4&7],@Tx[2]);		# byte swap
+	&add	($inp,64);
+
+  $Xi=0;
+}
+
+sub Xloop_ssse3()
+{ use integer;
+  my $body = shift;
+  my @insns = (&$body,&$body,&$body,&$body);	# 32 instructions
+  my ($a,$b,$c,$d,$e);
+
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&pshufb	(@X[($Xi-3)&7],@Tx[2]);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&paddd	(@X[($Xi-4)&7],@Tx[1]);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&movdqa	(eval(16*$Xi)."(%rsp)",@X[($Xi-4)&7]);	# X[]+K xfer to IALU
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&psubd	(@X[($Xi-4)&7],@Tx[1]);
+
+	foreach (@insns) { eval; }
+  $Xi++;
+}
+
+sub Xtail_ssse3()
+{ use integer;
+  my $body = shift;
+  my @insns = (&$body,&$body,&$body,&$body);	# 32 instructions
+  my ($a,$b,$c,$d,$e);
+
+	foreach (@insns) { eval; }
+}
+
+my @body_00_19 = (
+	'($a,$b,$c,$d,$e)=@V;'.
+	'&$_ror	($b,$j?7:2);',	# $b>>>2
+	'&xor	(@T[0],$d);',
+	'&mov	(@T[1],$a);',	# $b for next round
+
+	'&add	($e,eval(4*($j&15))."(%rsp)");',# X[]+K xfer
+	'&xor	($b,$c);',	# $c^$d for next round
+
+	'&$_rol	($a,5);',
+	'&add	($e,@T[0]);',
+	'&and	(@T[1],$b);',	# ($b&($c^$d)) for next round
+
+	'&xor	($b,$c);',	# restore $b
+	'&add	($e,$a);'	.'$j++; unshift(@V,pop(@V)); unshift(@T,pop(@T));'
+	);
+
+sub body_00_19 () {	# ((c^d)&b)^d
+    # on start @T[0]=(c^d)&b
+    return &body_20_39() if ($rx==19); $rx++;
+
+    use integer;
+    my ($k,$n);
+    my @r=@body_00_19;
+
+	$n = scalar(@r);
+	$k = (($jj+1)*12/20)*20*$n/12;	# 12 aesencs per these 20 rounds
+	@r[$k%$n].='&$aesenc();'	if ($jj==$k/$n);
+	$jj++;
+
+    return @r;
+}
+
+my @body_20_39 = (
+	'($a,$b,$c,$d,$e)=@V;'.
+	'&add	($e,eval(4*($j&15))."(%rsp)");',# X[]+K xfer
+	'&xor	(@T[0],$d)	if($j==19);'.
+	'&xor	(@T[0],$c)	if($j> 19);',	# ($b^$d^$c)
+	'&mov	(@T[1],$a);',	# $b for next round
+
+	'&$_rol	($a,5);',
+	'&add	($e,@T[0]);',
+	'&xor	(@T[1],$c)	if ($j< 79);',	# $b^$d for next round
+
+	'&$_ror	($b,7);',	# $b>>>2
+	'&add	($e,$a);'	.'$j++; unshift(@V,pop(@V)); unshift(@T,pop(@T));'
+	);
+
+sub body_20_39 () {	# b^d^c
+    # on entry @T[0]=b^d
+    return &body_40_59() if ($rx==39); $rx++;
+
+    use integer;
+    my ($k,$n);
+    my @r=@body_20_39;
+
+	$n = scalar(@r);
+	$k = (($jj+1)*8/20)*20*$n/8;	# 8 aesencs per these 20 rounds
+	@r[$k%$n].='&$aesenc();'	if ($jj==$k/$n && $rx!=20);
+	$jj++;
+
+    return @r;
+}
+
+my @body_40_59 = (
+	'($a,$b,$c,$d,$e)=@V;'.
+	'&add	($e,eval(4*($j&15))."(%rsp)");',# X[]+K xfer
+	'&and	(@T[0],$c)	if ($j>=40);',	# (b^c)&(c^d)
+	'&xor	($c,$d)		if ($j>=40);',	# restore $c
+
+	'&$_ror	($b,7);',	# $b>>>2
+	'&mov	(@T[1],$a);',	# $b for next round
+	'&xor	(@T[0],$c);',
+
+	'&$_rol	($a,5);',
+	'&add	($e,@T[0]);',
+	'&xor	(@T[1],$c)	if ($j==59);'.
+	'&xor	(@T[1],$b)	if ($j< 59);',	# b^c for next round
+
+	'&xor	($b,$c)		if ($j< 59);',	# c^d for next round
+	'&add	($e,$a);'	.'$j++; unshift(@V,pop(@V)); unshift(@T,pop(@T));'
+	);
+
+sub body_40_59 () {	# ((b^c)&(c^d))^c
+    # on entry @T[0]=(b^c), (c^=d)
+    $rx++;
+
+    use integer;
+    my ($k,$n);
+    my @r=@body_40_59;
+
+	$n = scalar(@r);
+	$k=(($jj+1)*12/20)*20*$n/12;	# 12 aesencs per these 20 rounds
+	@r[$k%$n].='&$aesenc();'	if ($jj==$k/$n && $rx!=40);
+	$jj++;
+
+    return @r;
+}
+$code.=<<___;
+.align	32
+.Loop_ssse3:
+___
+	&Xupdate_ssse3_16_31(\&body_00_19);
+	&Xupdate_ssse3_16_31(\&body_00_19);
+	&Xupdate_ssse3_16_31(\&body_00_19);
+	&Xupdate_ssse3_16_31(\&body_00_19);
+	&Xupdate_ssse3_32_79(\&body_00_19);
+	&Xupdate_ssse3_32_79(\&body_20_39);
+	&Xupdate_ssse3_32_79(\&body_20_39);
+	&Xupdate_ssse3_32_79(\&body_20_39);
+	&Xupdate_ssse3_32_79(\&body_20_39);
+	&Xupdate_ssse3_32_79(\&body_20_39);
+	&Xupdate_ssse3_32_79(\&body_40_59);
+	&Xupdate_ssse3_32_79(\&body_40_59);
+	&Xupdate_ssse3_32_79(\&body_40_59);
+	&Xupdate_ssse3_32_79(\&body_40_59);
+	&Xupdate_ssse3_32_79(\&body_40_59);
+	&Xupdate_ssse3_32_79(\&body_20_39);
+	&Xuplast_ssse3_80(\&body_20_39,".Ldone_ssse3");	# can jump to "done"
+
+				$saved_j=$j; @saved_V=@V;
+				$saved_r=$r; @saved_rndkey=@rndkey;
+
+	&Xloop_ssse3(\&body_20_39);
+	&Xloop_ssse3(\&body_20_39);
+	&Xloop_ssse3(\&body_20_39);
+
+$code.=<<___;
+	movups	$iv,48($out,$in0)		# write output
+	lea	64($in0),$in0
+
+	add	0($ctx),$A			# update context
+	add	4($ctx),@T[0]
+	add	8($ctx),$C
+	add	12($ctx),$D
+	mov	$A,0($ctx)
+	add	16($ctx),$E
+	mov	@T[0],4($ctx)
+	mov	@T[0],$B			# magic seed
+	mov	$C,8($ctx)
+	mov	$C,@T[1]
+	mov	$D,12($ctx)
+	xor	$D,@T[1]
+	mov	$E,16($ctx)
+	and	@T[1],@T[0]
+	jmp	.Loop_ssse3
+
+.Ldone_ssse3:
+___
+				$jj=$j=$saved_j; @V=@saved_V;
+				$r=$saved_r;     @rndkey=@saved_rndkey;
+
+	&Xtail_ssse3(\&body_20_39);
+	&Xtail_ssse3(\&body_20_39);
+	&Xtail_ssse3(\&body_20_39);
+
+$code.=<<___;
+	movups	$iv,48($out,$in0)		# write output
+	mov	88(%rsp),$ivp			# restore $ivp
+
+	add	0($ctx),$A			# update context
+	add	4($ctx),@T[0]
+	add	8($ctx),$C
+	mov	$A,0($ctx)
+	add	12($ctx),$D
+	mov	@T[0],4($ctx)
+	add	16($ctx),$E
+	mov	$C,8($ctx)
+	mov	$D,12($ctx)
+	mov	$E,16($ctx)
+	movups	$iv,($ivp)			# write IV
+___
+$code.=<<___ if ($win64);
+	movaps	96+0(%rsp),%xmm6
+	movaps	96+16(%rsp),%xmm7
+	movaps	96+32(%rsp),%xmm8
+	movaps	96+48(%rsp),%xmm9
+	movaps	96+64(%rsp),%xmm10
+	movaps	96+80(%rsp),%xmm11
+	movaps	96+96(%rsp),%xmm12
+	movaps	96+112(%rsp),%xmm13
+	movaps	96+128(%rsp),%xmm14
+	movaps	96+144(%rsp),%xmm15
+___
+$code.=<<___;
+	lea	`104+($win64?10*16:0)`(%rsp),%rsi
+	mov	0(%rsi),%r15
+	mov	8(%rsi),%r14
+	mov	16(%rsi),%r13
+	mov	24(%rsi),%r12
+	mov	32(%rsi),%rbp
+	mov	40(%rsi),%rbx
+	lea	48(%rsi),%rsp
+.Lepilogue_ssse3:
+	ret
+.size	aesni_cbc_sha1_enc_ssse3,.-aesni_cbc_sha1_enc_ssse3
+___
+
+						if ($stitched_decrypt) {{{
+# reset
+($in0,$out,$len,$key,$ivp,$ctx,$inp)=("%rdi","%rsi","%rdx","%rcx","%r8","%r9","%r10");
+$j=$jj=$r=$rx=0;
+$Xi=4;
+
+# reassign for Atom Silvermont (see above)
+($inout0,$inout1,$inout2,$inout3,$rndkey0)=map("%xmm$_",(0..4));
+@X=map("%xmm$_",(8..13,6,7));
+@Tx=map("%xmm$_",(14,15,5));
+
+my @aes256_dec = (
+	'&movdqu($inout0,"0x00($in0)");',
+	'&movdqu($inout1,"0x10($in0)");	&pxor	($inout0,$rndkey0);',
+	'&movdqu($inout2,"0x20($in0)");	&pxor	($inout1,$rndkey0);',
+	'&movdqu($inout3,"0x30($in0)");	&pxor	($inout2,$rndkey0);',
+
+	'&pxor	($inout3,$rndkey0);	&movups	($rndkey0,"16-112($key)");',
+	'&movaps("64(%rsp)",@X[2]);',	# save IV, originally @X[3]
+	undef,undef
+	);
+for ($i=0;$i<13;$i++) {
+    push (@aes256_dec,(
+	'&aesdec	($inout0,$rndkey0);',
+	'&aesdec	($inout1,$rndkey0);',
+	'&aesdec	($inout2,$rndkey0);',
+	'&aesdec	($inout3,$rndkey0);	&movups($rndkey0,"'.(16*($i+2)-112).'($key)");'
+	));
+    push (@aes256_dec,(undef,undef))	if (($i>=3 && $i<=5) || $i>=11);
+    push (@aes256_dec,(undef,undef))	if ($i==5);
+}
+push(@aes256_dec,(
+	'&aesdeclast	($inout0,$rndkey0);	&movups	(@X[0],"0x00($in0)");',
+	'&aesdeclast	($inout1,$rndkey0);	&movups	(@X[1],"0x10($in0)");',
+	'&aesdeclast	($inout2,$rndkey0);	&movups	(@X[2],"0x20($in0)");',
+	'&aesdeclast	($inout3,$rndkey0);	&movups	(@X[3],"0x30($in0)");',
+
+	'&xorps		($inout0,"64(%rsp)");	&movdqu	($rndkey0,"-112($key)");',
+	'&xorps		($inout1,@X[0]);	&movups	("0x00($out,$in0)",$inout0);',
+	'&xorps		($inout2,@X[1]);	&movups	("0x10($out,$in0)",$inout1);',
+	'&xorps		($inout3,@X[2]);	&movups	("0x20($out,$in0)",$inout2);',
+
+	'&movups	("0x30($out,$in0)",$inout3);'
+	));
+
+sub body_00_19_dec () {	# ((c^d)&b)^d
+    # on start @T[0]=(c^d)&b
+    return &body_20_39_dec() if ($rx==19);
+
+    my @r=@body_00_19;
+
+	unshift (@r,@aes256_dec[$rx])	if (@aes256_dec[$rx]);
+	$rx++;
+
+    return @r;
+}
+
+sub body_20_39_dec () {	# b^d^c
+    # on entry @T[0]=b^d
+    return &body_40_59_dec() if ($rx==39);
+  
+    my @r=@body_20_39;
+
+	unshift (@r,@aes256_dec[$rx])	if (@aes256_dec[$rx]);
+	$rx++;
+
+    return @r;
+}
+
+sub body_40_59_dec () {	# ((b^c)&(c^d))^c
+    # on entry @T[0]=(b^c), (c^=d)
+
+    my @r=@body_40_59;
+
+	unshift (@r,@aes256_dec[$rx])	if (@aes256_dec[$rx]);
+	$rx++;
+
+    return @r;
+}
+
+$code.=<<___;
+.globl	aesni256_cbc_sha1_dec
+.type	aesni256_cbc_sha1_dec,\@abi-omnipotent
+.align	32
+aesni256_cbc_sha1_dec:
+	# caller should check for SSSE3 and AES-NI bits
+	mov	OPENSSL_ia32cap_P+0(%rip),%r10d
+	mov	OPENSSL_ia32cap_P+4(%rip),%r11d
+___
+$code.=<<___ if ($avx);
+	and	\$`1<<28`,%r11d		# mask AVX bit
+	and	\$`1<<30`,%r10d		# mask "Intel CPU" bit
+	or	%r11d,%r10d
+	cmp	\$`1<<28|1<<30`,%r10d
+	je	aesni256_cbc_sha1_dec_avx
+___
+$code.=<<___;
+	jmp	aesni256_cbc_sha1_dec_ssse3
+	ret
+.size	aesni256_cbc_sha1_dec,.-aesni256_cbc_sha1_dec
+
+.type	aesni256_cbc_sha1_dec_ssse3,\@function,6
+.align	32
+aesni256_cbc_sha1_dec_ssse3:
+	mov	`($win64?56:8)`(%rsp),$inp	# load 7th argument
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+	lea	`-104-($win64?10*16:0)`(%rsp),%rsp
+___
+$code.=<<___ if ($win64);
+	movaps	%xmm6,96+0(%rsp)
+	movaps	%xmm7,96+16(%rsp)
+	movaps	%xmm8,96+32(%rsp)
+	movaps	%xmm9,96+48(%rsp)
+	movaps	%xmm10,96+64(%rsp)
+	movaps	%xmm11,96+80(%rsp)
+	movaps	%xmm12,96+96(%rsp)
+	movaps	%xmm13,96+112(%rsp)
+	movaps	%xmm14,96+128(%rsp)
+	movaps	%xmm15,96+144(%rsp)
+.Lprologue_dec_ssse3:
+___
+$code.=<<___;
+	mov	$in0,%r12			# reassign arguments
+	mov	$out,%r13
+	mov	$len,%r14
+	lea	112($key),%r15			# size optimization
+	movdqu	($ivp),@X[3]			# load IV
+	#mov	$ivp,88(%rsp)			# save $ivp
+___
+($in0,$out,$len,$key)=map("%r$_",(12..15));	# reassign arguments
+$code.=<<___;
+	shl	\$6,$len
+	sub	$in0,$out
+	add	$inp,$len		# end of input
+
+	lea	K_XX_XX(%rip),$K_XX_XX
+	mov	0($ctx),$A		# load context
+	mov	4($ctx),$B
+	mov	8($ctx),$C
+	mov	12($ctx),$D
+	mov	$B,@T[0]		# magic seed
+	mov	16($ctx),$E
+	mov	$C,@T[1]
+	xor	$D,@T[1]
+	and	@T[1],@T[0]
+
+	movdqa	64($K_XX_XX),@Tx[2]	# pbswap mask
+	movdqa	0($K_XX_XX),@Tx[1]	# K_00_19
+	movdqu	0($inp),@X[-4&7]	# load input to %xmm[0-3]
+	movdqu	16($inp),@X[-3&7]
+	movdqu	32($inp),@X[-2&7]
+	movdqu	48($inp),@X[-1&7]
+	pshufb	@Tx[2],@X[-4&7]		# byte swap
+	add	\$64,$inp
+	pshufb	@Tx[2],@X[-3&7]
+	pshufb	@Tx[2],@X[-2&7]
+	pshufb	@Tx[2],@X[-1&7]
+	paddd	@Tx[1],@X[-4&7]		# add K_00_19
+	paddd	@Tx[1],@X[-3&7]
+	paddd	@Tx[1],@X[-2&7]
+	movdqa	@X[-4&7],0(%rsp)	# X[]+K xfer to IALU
+	psubd	@Tx[1],@X[-4&7]		# restore X[]
+	movdqa	@X[-3&7],16(%rsp)
+	psubd	@Tx[1],@X[-3&7]
+	movdqa	@X[-2&7],32(%rsp)
+	psubd	@Tx[1],@X[-2&7]
+	movdqu	-112($key),$rndkey0	# $key[0]
+	jmp	.Loop_dec_ssse3
+
+.align	32
+.Loop_dec_ssse3:
+___
+	&Xupdate_ssse3_16_31(\&body_00_19_dec);
+	&Xupdate_ssse3_16_31(\&body_00_19_dec);
+	&Xupdate_ssse3_16_31(\&body_00_19_dec);
+	&Xupdate_ssse3_16_31(\&body_00_19_dec);
+	&Xupdate_ssse3_32_79(\&body_00_19_dec);
+	&Xupdate_ssse3_32_79(\&body_20_39_dec);
+	&Xupdate_ssse3_32_79(\&body_20_39_dec);
+	&Xupdate_ssse3_32_79(\&body_20_39_dec);
+	&Xupdate_ssse3_32_79(\&body_20_39_dec);
+	&Xupdate_ssse3_32_79(\&body_20_39_dec);
+	&Xupdate_ssse3_32_79(\&body_40_59_dec);
+	&Xupdate_ssse3_32_79(\&body_40_59_dec);
+	&Xupdate_ssse3_32_79(\&body_40_59_dec);
+	&Xupdate_ssse3_32_79(\&body_40_59_dec);
+	&Xupdate_ssse3_32_79(\&body_40_59_dec);
+	&Xupdate_ssse3_32_79(\&body_20_39_dec);
+	&Xuplast_ssse3_80(\&body_20_39_dec,".Ldone_dec_ssse3");	# can jump to "done"
+
+				$saved_j=$j;   @saved_V=@V;
+				$saved_rx=$rx;
+
+	&Xloop_ssse3(\&body_20_39_dec);
+	&Xloop_ssse3(\&body_20_39_dec);
+	&Xloop_ssse3(\&body_20_39_dec);
+
+	eval(@aes256_dec[-1]);			# last store
+$code.=<<___;
+	lea	64($in0),$in0
+
+	add	0($ctx),$A			# update context
+	add	4($ctx),@T[0]
+	add	8($ctx),$C
+	add	12($ctx),$D
+	mov	$A,0($ctx)
+	add	16($ctx),$E
+	mov	@T[0],4($ctx)
+	mov	@T[0],$B			# magic seed
+	mov	$C,8($ctx)
+	mov	$C,@T[1]
+	mov	$D,12($ctx)
+	xor	$D,@T[1]
+	mov	$E,16($ctx)
+	and	@T[1],@T[0]
+	jmp	.Loop_dec_ssse3
+
+.Ldone_dec_ssse3:
+___
+				$jj=$j=$saved_j; @V=@saved_V;
+				$rx=$saved_rx;
+
+	&Xtail_ssse3(\&body_20_39_dec);
+	&Xtail_ssse3(\&body_20_39_dec);
+	&Xtail_ssse3(\&body_20_39_dec);
+
+	eval(@aes256_dec[-1]);			# last store
+$code.=<<___;
+	add	0($ctx),$A			# update context
+	add	4($ctx),@T[0]
+	add	8($ctx),$C
+	mov	$A,0($ctx)
+	add	12($ctx),$D
+	mov	@T[0],4($ctx)
+	add	16($ctx),$E
+	mov	$C,8($ctx)
+	mov	$D,12($ctx)
+	mov	$E,16($ctx)
+	movups	@X[3],($ivp)			# write IV
+___
+$code.=<<___ if ($win64);
+	movaps	96+0(%rsp),%xmm6
+	movaps	96+16(%rsp),%xmm7
+	movaps	96+32(%rsp),%xmm8
+	movaps	96+48(%rsp),%xmm9
+	movaps	96+64(%rsp),%xmm10
+	movaps	96+80(%rsp),%xmm11
+	movaps	96+96(%rsp),%xmm12
+	movaps	96+112(%rsp),%xmm13
+	movaps	96+128(%rsp),%xmm14
+	movaps	96+144(%rsp),%xmm15
+___
+$code.=<<___;
+	lea	`104+($win64?10*16:0)`(%rsp),%rsi
+	mov	0(%rsi),%r15
+	mov	8(%rsi),%r14
+	mov	16(%rsi),%r13
+	mov	24(%rsi),%r12
+	mov	32(%rsi),%rbp
+	mov	40(%rsi),%rbx
+	lea	48(%rsi),%rsp
+.Lepilogue_dec_ssse3:
+	ret
+.size	aesni256_cbc_sha1_dec_ssse3,.-aesni256_cbc_sha1_dec_ssse3
+___
+						}}}
+$j=$jj=$r=$rx=0;
+
+if ($avx) {
+my ($in0,$out,$len,$key,$ivp,$ctx,$inp)=("%rdi","%rsi","%rdx","%rcx","%r8","%r9","%r10");
+
+my $Xi=4;
+my @X=map("%xmm$_",(4..7,0..3));
+my @Tx=map("%xmm$_",(8..10));
+my @V=($A,$B,$C,$D,$E)=("%eax","%ebx","%ecx","%edx","%ebp");	# size optimization
+my @T=("%esi","%edi");
+my ($rndkey0,$iv,$in)=map("%xmm$_",(11..13));
+my @rndkey=("%xmm14","%xmm15");
+my ($inout0,$inout1,$inout2,$inout3)=map("%xmm$_",(12..15));	# for dec
+my $Kx=@Tx[2];
+
+my $_rol=sub { &shld(@_[0],@_) };
+my $_ror=sub { &shrd(@_[0],@_) };
+
+$code.=<<___;
+.type	aesni_cbc_sha1_enc_avx,\@function,6
+.align	32
+aesni_cbc_sha1_enc_avx:
+	mov	`($win64?56:8)`(%rsp),$inp	# load 7th argument
+	#shr	\$6,$len			# debugging artefact
+	#jz	.Lepilogue_avx			# debugging artefact
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+	lea	`-104-($win64?10*16:0)`(%rsp),%rsp
+	#mov	$in0,$inp			# debugging artefact
+	#lea	64(%rsp),$ctx			# debugging artefact
+___
+$code.=<<___ if ($win64);
+	movaps	%xmm6,96+0(%rsp)
+	movaps	%xmm7,96+16(%rsp)
+	movaps	%xmm8,96+32(%rsp)
+	movaps	%xmm9,96+48(%rsp)
+	movaps	%xmm10,96+64(%rsp)
+	movaps	%xmm11,96+80(%rsp)
+	movaps	%xmm12,96+96(%rsp)
+	movaps	%xmm13,96+112(%rsp)
+	movaps	%xmm14,96+128(%rsp)
+	movaps	%xmm15,96+144(%rsp)
+.Lprologue_avx:
+___
+$code.=<<___;
+	vzeroall
+	mov	$in0,%r12			# reassign arguments
+	mov	$out,%r13
+	mov	$len,%r14
+	lea	112($key),%r15			# size optimization
+	vmovdqu	($ivp),$iv			# load IV
+	mov	$ivp,88(%rsp)			# save $ivp
+___
+($in0,$out,$len,$key)=map("%r$_",(12..15));	# reassign arguments
+my $rounds="${ivp}d";
+$code.=<<___;
+	shl	\$6,$len
+	sub	$in0,$out
+	mov	240-112($key),$rounds
+	add	$inp,$len		# end of input
+
+	lea	K_XX_XX(%rip),$K_XX_XX
+	mov	0($ctx),$A		# load context
+	mov	4($ctx),$B
+	mov	8($ctx),$C
+	mov	12($ctx),$D
+	mov	$B,@T[0]		# magic seed
+	mov	16($ctx),$E
+	mov	$C,@T[1]
+	xor	$D,@T[1]
+	and	@T[1],@T[0]
+
+	vmovdqa	64($K_XX_XX),@X[2]	# pbswap mask
+	vmovdqa	0($K_XX_XX),$Kx		# K_00_19
+	vmovdqu	0($inp),@X[-4&7]	# load input to %xmm[0-3]
+	vmovdqu	16($inp),@X[-3&7]
+	vmovdqu	32($inp),@X[-2&7]
+	vmovdqu	48($inp),@X[-1&7]
+	vpshufb	@X[2],@X[-4&7],@X[-4&7]	# byte swap
+	add	\$64,$inp
+	vpshufb	@X[2],@X[-3&7],@X[-3&7]
+	vpshufb	@X[2],@X[-2&7],@X[-2&7]
+	vpshufb	@X[2],@X[-1&7],@X[-1&7]
+	vpaddd	$Kx,@X[-4&7],@X[0]	# add K_00_19
+	vpaddd	$Kx,@X[-3&7],@X[1]
+	vpaddd	$Kx,@X[-2&7],@X[2]
+	vmovdqa	@X[0],0(%rsp)		# X[]+K xfer to IALU
+	vmovdqa	@X[1],16(%rsp)
+	vmovdqa	@X[2],32(%rsp)
+	vmovups	-112($key),$rndkey[1]	# $key[0]
+	vmovups	16-112($key),$rndkey[0]	# forward reference
+	jmp	.Loop_avx
+___
+
+my $aesenc=sub {
+  use integer;
+  my ($n,$k)=($r/10,$r%10);
+    if ($k==0) {
+      $code.=<<___;
+	vmovdqu		`16*$n`($in0),$in		# load input
+	vpxor		$rndkey[1],$in,$in
+___
+      $code.=<<___ if ($n);
+	vmovups		$iv,`16*($n-1)`($out,$in0)	# write output
+___
+      $code.=<<___;
+	vpxor		$in,$iv,$iv
+	vaesenc		$rndkey[0],$iv,$iv
+	vmovups		`32+16*$k-112`($key),$rndkey[1]
+___
+    } elsif ($k==9) {
+      $sn++;
+      $code.=<<___;
+	cmp		\$11,$rounds
+	jb		.Lvaesenclast$sn
+	vaesenc		$rndkey[0],$iv,$iv
+	vmovups		`32+16*($k+0)-112`($key),$rndkey[1]
+	vaesenc		$rndkey[1],$iv,$iv
+	vmovups		`32+16*($k+1)-112`($key),$rndkey[0]
+	je		.Lvaesenclast$sn
+	vaesenc		$rndkey[0],$iv,$iv
+	vmovups		`32+16*($k+2)-112`($key),$rndkey[1]
+	vaesenc		$rndkey[1],$iv,$iv
+	vmovups		`32+16*($k+3)-112`($key),$rndkey[0]
+.Lvaesenclast$sn:
+	vaesenclast	$rndkey[0],$iv,$iv
+	vmovups		-112($key),$rndkey[0]
+	vmovups		16-112($key),$rndkey[1]		# forward reference
+___
+    } else {
+      $code.=<<___;
+	vaesenc		$rndkey[0],$iv,$iv
+	vmovups		`32+16*$k-112`($key),$rndkey[1]
+___
+    }
+    $r++;	unshift(@rndkey,pop(@rndkey));
+};
+
+sub Xupdate_avx_16_31()		# recall that $Xi starts wtih 4
+{ use integer;
+  my $body = shift;
+  my @insns = (&$body,&$body,&$body,&$body);	# 40 instructions
+  my ($a,$b,$c,$d,$e);
+
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&vpalignr(@X[0],@X[-3&7],@X[-4&7],8);	# compose "X[-14]" in "X[0]"
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	  &vpaddd	(@Tx[1],$Kx,@X[-1&7]);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&vpsrldq(@Tx[0],@X[-1&7],4);		# "X[-3]", 3 dwords
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&vpxor	(@X[0],@X[0],@X[-4&7]);		# "X[0]"^="X[-16]"
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	&vpxor	(@Tx[0],@Tx[0],@X[-2&7]);	# "X[-3]"^"X[-8]"
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	&vpxor	(@X[0],@X[0],@Tx[0]);		# "X[0]"^="X[-3]"^"X[-8]"
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	  &vmovdqa	(eval(16*(($Xi-1)&3))."(%rsp)",@Tx[1]);	# X[]+K xfer to IALU
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	&vpsrld	(@Tx[0],@X[0],31);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	&vpslldq(@Tx[1],@X[0],12);		# "X[0]"<<96, extract one dword
+	&vpaddd	(@X[0],@X[0],@X[0]);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	&vpor	(@X[0],@X[0],@Tx[0]);		# "X[0]"<<<=1
+	&vpsrld	(@Tx[0],@Tx[1],30);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	&vpslld	(@Tx[1],@Tx[1],2);
+	&vpxor	(@X[0],@X[0],@Tx[0]);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	&vpxor	(@X[0],@X[0],@Tx[1]);		# "X[0]"^=("X[0]">>96)<<<2
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	  &vmovdqa	($Kx,eval(16*(($Xi)/5))."($K_XX_XX)")	if ($Xi%5==0);	# K_XX_XX
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+
+	 foreach (@insns) { eval; }	# remaining instructions [if any]
+
+  $Xi++;	push(@X,shift(@X));	# "rotate" X[]
+}
+
+sub Xupdate_avx_32_79()
+{ use integer;
+  my $body = shift;
+  my @insns = (&$body,&$body,&$body,&$body);	# 32 to 48 instructions
+  my ($a,$b,$c,$d,$e);
+
+	&vpalignr(@Tx[0],@X[-1&7],@X[-2&7],8);	# compose "X[-6]"
+	&vpxor	(@X[0],@X[0],@X[-4&7]);		# "X[0]"="X[-32]"^"X[-16]"
+	 eval(shift(@insns));		# body_20_39
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));		# rol
+
+	&vpxor	(@X[0],@X[0],@X[-7&7]);		# "X[0]"^="X[-28]"
+	 eval(shift(@insns));
+	 eval(shift(@insns))	if (@insns[0] !~ /&ro[rl]/);
+	  &vpaddd	(@Tx[1],$Kx,@X[-1&7]);
+	  &vmovdqa	($Kx,eval(16*($Xi/5))."($K_XX_XX)")	if ($Xi%5==0);
+	 eval(shift(@insns));		# ror
+	 eval(shift(@insns));
+
+	&vpxor	(@X[0],@X[0],@Tx[0]);		# "X[0]"^="X[-6]"
+	 eval(shift(@insns));		# body_20_39
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));		# rol
+
+	&vpsrld	(@Tx[0],@X[0],30);
+	  &vmovdqa	(eval(16*(($Xi-1)&3))."(%rsp)",@Tx[1]);	# X[]+K xfer to IALU
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));		# ror
+	 eval(shift(@insns));
+
+	&vpslld	(@X[0],@X[0],2);
+	 eval(shift(@insns));		# body_20_39
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));		# rol
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));		# ror
+	 eval(shift(@insns));
+
+	&vpor	(@X[0],@X[0],@Tx[0]);		# "X[0]"<<<=2
+	 eval(shift(@insns));		# body_20_39
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));		# rol
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));		# rol
+	 eval(shift(@insns));
+
+	 foreach (@insns) { eval; }	# remaining instructions
+
+  $Xi++;	push(@X,shift(@X));	# "rotate" X[]
+}
+
+sub Xuplast_avx_80()
+{ use integer;
+  my $body = shift;
+  my @insns = (&$body,&$body,&$body,&$body);	# 32 instructions
+  my ($a,$b,$c,$d,$e);
+
+	 eval(shift(@insns));
+	  &vpaddd	(@Tx[1],$Kx,@X[-1&7]);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	  &vmovdqa	(eval(16*(($Xi-1)&3))."(%rsp)",@Tx[1]);	# X[]+K xfer IALU
+
+	 foreach (@insns) { eval; }		# remaining instructions
+
+	&cmp	($inp,$len);
+	&je	(shift);
+
+	&vmovdqa(@Tx[1],"64($K_XX_XX)");	# pbswap mask
+	&vmovdqa($Kx,"0($K_XX_XX)");		# K_00_19
+	&vmovdqu(@X[-4&7],"0($inp)");		# load input
+	&vmovdqu(@X[-3&7],"16($inp)");
+	&vmovdqu(@X[-2&7],"32($inp)");
+	&vmovdqu(@X[-1&7],"48($inp)");
+	&vpshufb(@X[-4&7],@X[-4&7],@Tx[1]);	# byte swap
+	&add	($inp,64);
+
+  $Xi=0;
+}
+
+sub Xloop_avx()
+{ use integer;
+  my $body = shift;
+  my @insns = (&$body,&$body,&$body,&$body);	# 32 instructions
+  my ($a,$b,$c,$d,$e);
+
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&vpshufb(@X[($Xi-3)&7],@X[($Xi-3)&7],@Tx[1]);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&vpaddd	(@Tx[0],@X[($Xi-4)&7],$Kx);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&vmovdqa(eval(16*$Xi)."(%rsp)",@Tx[0]);	# X[]+K xfer to IALU
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	foreach (@insns) { eval; }
+  $Xi++;
+}
+
+sub Xtail_avx()
+{ use integer;
+  my $body = shift;
+  my @insns = (&$body,&$body,&$body,&$body);	# 32 instructions
+  my ($a,$b,$c,$d,$e);
+
+	foreach (@insns) { eval; }
+}
+
+$code.=<<___;
+.align	32
+.Loop_avx:
+___
+	&Xupdate_avx_16_31(\&body_00_19);
+	&Xupdate_avx_16_31(\&body_00_19);
+	&Xupdate_avx_16_31(\&body_00_19);
+	&Xupdate_avx_16_31(\&body_00_19);
+	&Xupdate_avx_32_79(\&body_00_19);
+	&Xupdate_avx_32_79(\&body_20_39);
+	&Xupdate_avx_32_79(\&body_20_39);
+	&Xupdate_avx_32_79(\&body_20_39);
+	&Xupdate_avx_32_79(\&body_20_39);
+	&Xupdate_avx_32_79(\&body_20_39);
+	&Xupdate_avx_32_79(\&body_40_59);
+	&Xupdate_avx_32_79(\&body_40_59);
+	&Xupdate_avx_32_79(\&body_40_59);
+	&Xupdate_avx_32_79(\&body_40_59);
+	&Xupdate_avx_32_79(\&body_40_59);
+	&Xupdate_avx_32_79(\&body_20_39);
+	&Xuplast_avx_80(\&body_20_39,".Ldone_avx");	# can jump to "done"
+
+				$saved_j=$j; @saved_V=@V;
+				$saved_r=$r; @saved_rndkey=@rndkey;
+
+	&Xloop_avx(\&body_20_39);
+	&Xloop_avx(\&body_20_39);
+	&Xloop_avx(\&body_20_39);
+
+$code.=<<___;
+	vmovups	$iv,48($out,$in0)		# write output
+	lea	64($in0),$in0
+
+	add	0($ctx),$A			# update context
+	add	4($ctx),@T[0]
+	add	8($ctx),$C
+	add	12($ctx),$D
+	mov	$A,0($ctx)
+	add	16($ctx),$E
+	mov	@T[0],4($ctx)
+	mov	@T[0],$B			# magic seed
+	mov	$C,8($ctx)
+	mov	$C,@T[1]
+	mov	$D,12($ctx)
+	xor	$D,@T[1]
+	mov	$E,16($ctx)
+	and	@T[1],@T[0]
+	jmp	.Loop_avx
+
+.Ldone_avx:
+___
+				$jj=$j=$saved_j; @V=@saved_V;
+				$r=$saved_r;     @rndkey=@saved_rndkey;
+
+	&Xtail_avx(\&body_20_39);
+	&Xtail_avx(\&body_20_39);
+	&Xtail_avx(\&body_20_39);
+
+$code.=<<___;
+	vmovups	$iv,48($out,$in0)		# write output
+	mov	88(%rsp),$ivp			# restore $ivp
+
+	add	0($ctx),$A			# update context
+	add	4($ctx),@T[0]
+	add	8($ctx),$C
+	mov	$A,0($ctx)
+	add	12($ctx),$D
+	mov	@T[0],4($ctx)
+	add	16($ctx),$E
+	mov	$C,8($ctx)
+	mov	$D,12($ctx)
+	mov	$E,16($ctx)
+	vmovups	$iv,($ivp)			# write IV
+	vzeroall
+___
+$code.=<<___ if ($win64);
+	movaps	96+0(%rsp),%xmm6
+	movaps	96+16(%rsp),%xmm7
+	movaps	96+32(%rsp),%xmm8
+	movaps	96+48(%rsp),%xmm9
+	movaps	96+64(%rsp),%xmm10
+	movaps	96+80(%rsp),%xmm11
+	movaps	96+96(%rsp),%xmm12
+	movaps	96+112(%rsp),%xmm13
+	movaps	96+128(%rsp),%xmm14
+	movaps	96+144(%rsp),%xmm15
+___
+$code.=<<___;
+	lea	`104+($win64?10*16:0)`(%rsp),%rsi
+	mov	0(%rsi),%r15
+	mov	8(%rsi),%r14
+	mov	16(%rsi),%r13
+	mov	24(%rsi),%r12
+	mov	32(%rsi),%rbp
+	mov	40(%rsi),%rbx
+	lea	48(%rsi),%rsp
+.Lepilogue_avx:
+	ret
+.size	aesni_cbc_sha1_enc_avx,.-aesni_cbc_sha1_enc_avx
+___
+
+						if ($stitched_decrypt) {{{
+# reset
+($in0,$out,$len,$key,$ivp,$ctx,$inp)=("%rdi","%rsi","%rdx","%rcx","%r8","%r9","%r10");
+
+$j=$jj=$r=$rx=0;
+$Xi=4;
+
+@aes256_dec = (
+	'&vpxor	($inout0,$rndkey0,"0x00($in0)");',
+	'&vpxor	($inout1,$rndkey0,"0x10($in0)");',
+	'&vpxor	($inout2,$rndkey0,"0x20($in0)");',
+	'&vpxor	($inout3,$rndkey0,"0x30($in0)");',
+
+	'&vmovups($rndkey0,"16-112($key)");',
+	'&vmovups("64(%rsp)",@X[2]);',		# save IV, originally @X[3]
+	undef,undef
+	);
+for ($i=0;$i<13;$i++) {
+    push (@aes256_dec,(
+	'&vaesdec	($inout0,$inout0,$rndkey0);',
+	'&vaesdec	($inout1,$inout1,$rndkey0);',
+	'&vaesdec	($inout2,$inout2,$rndkey0);',
+	'&vaesdec	($inout3,$inout3,$rndkey0);	&vmovups($rndkey0,"'.(16*($i+2)-112).'($key)");'
+	));
+    push (@aes256_dec,(undef,undef))	if (($i>=3 && $i<=5) || $i>=11);
+    push (@aes256_dec,(undef,undef))	if ($i==5);
+}
+push(@aes256_dec,(
+	'&vaesdeclast	($inout0,$inout0,$rndkey0);	&vmovups(@X[0],"0x00($in0)");',
+	'&vaesdeclast	($inout1,$inout1,$rndkey0);	&vmovups(@X[1],"0x10($in0)");',
+	'&vaesdeclast	($inout2,$inout2,$rndkey0);	&vmovups(@X[2],"0x20($in0)");',
+	'&vaesdeclast	($inout3,$inout3,$rndkey0);	&vmovups(@X[3],"0x30($in0)");',
+
+	'&vxorps	($inout0,$inout0,"64(%rsp)");	&vmovdqu($rndkey0,"-112($key)");',
+	'&vxorps	($inout1,$inout1,@X[0]);	&vmovups("0x00($out,$in0)",$inout0);',
+	'&vxorps	($inout2,$inout2,@X[1]);	&vmovups("0x10($out,$in0)",$inout1);',
+	'&vxorps	($inout3,$inout3,@X[2]);	&vmovups("0x20($out,$in0)",$inout2);',
+
+	'&vmovups	("0x30($out,$in0)",$inout3);'
+	));
+
+$code.=<<___;
+.type	aesni256_cbc_sha1_dec_avx,\@function,6
+.align	32
+aesni256_cbc_sha1_dec_avx:
+	mov	`($win64?56:8)`(%rsp),$inp	# load 7th argument
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+	lea	`-104-($win64?10*16:0)`(%rsp),%rsp
+___
+$code.=<<___ if ($win64);
+	movaps	%xmm6,96+0(%rsp)
+	movaps	%xmm7,96+16(%rsp)
+	movaps	%xmm8,96+32(%rsp)
+	movaps	%xmm9,96+48(%rsp)
+	movaps	%xmm10,96+64(%rsp)
+	movaps	%xmm11,96+80(%rsp)
+	movaps	%xmm12,96+96(%rsp)
+	movaps	%xmm13,96+112(%rsp)
+	movaps	%xmm14,96+128(%rsp)
+	movaps	%xmm15,96+144(%rsp)
+.Lprologue_dec_avx:
+___
+$code.=<<___;
+	vzeroall
+	mov	$in0,%r12			# reassign arguments
+	mov	$out,%r13
+	mov	$len,%r14
+	lea	112($key),%r15			# size optimization
+	vmovdqu	($ivp),@X[3]			# load IV
+___
+($in0,$out,$len,$key)=map("%r$_",(12..15));	# reassign arguments
+$code.=<<___;
+	shl	\$6,$len
+	sub	$in0,$out
+	add	$inp,$len		# end of input
+
+	lea	K_XX_XX(%rip),$K_XX_XX
+	mov	0($ctx),$A		# load context
+	mov	4($ctx),$B
+	mov	8($ctx),$C
+	mov	12($ctx),$D
+	mov	$B,@T[0]		# magic seed
+	mov	16($ctx),$E
+	mov	$C,@T[1]
+	xor	$D,@T[1]
+	and	@T[1],@T[0]
+
+	vmovdqa	64($K_XX_XX),@X[2]	# pbswap mask
+	vmovdqa	0($K_XX_XX),$Kx		# K_00_19
+	vmovdqu	0($inp),@X[-4&7]	# load input to %xmm[0-3]
+	vmovdqu	16($inp),@X[-3&7]
+	vmovdqu	32($inp),@X[-2&7]
+	vmovdqu	48($inp),@X[-1&7]
+	vpshufb	@X[2],@X[-4&7],@X[-4&7]	# byte swap
+	add	\$64,$inp
+	vpshufb	@X[2],@X[-3&7],@X[-3&7]
+	vpshufb	@X[2],@X[-2&7],@X[-2&7]
+	vpshufb	@X[2],@X[-1&7],@X[-1&7]
+	vpaddd	$Kx,@X[-4&7],@X[0]	# add K_00_19
+	vpaddd	$Kx,@X[-3&7],@X[1]
+	vpaddd	$Kx,@X[-2&7],@X[2]
+	vmovdqa	@X[0],0(%rsp)		# X[]+K xfer to IALU
+	vmovdqa	@X[1],16(%rsp)
+	vmovdqa	@X[2],32(%rsp)
+	vmovups	-112($key),$rndkey0	# $key[0]
+	jmp	.Loop_dec_avx
+
+.align	32
+.Loop_dec_avx:
+___
+	&Xupdate_avx_16_31(\&body_00_19_dec);
+	&Xupdate_avx_16_31(\&body_00_19_dec);
+	&Xupdate_avx_16_31(\&body_00_19_dec);
+	&Xupdate_avx_16_31(\&body_00_19_dec);
+	&Xupdate_avx_32_79(\&body_00_19_dec);
+	&Xupdate_avx_32_79(\&body_20_39_dec);
+	&Xupdate_avx_32_79(\&body_20_39_dec);
+	&Xupdate_avx_32_79(\&body_20_39_dec);
+	&Xupdate_avx_32_79(\&body_20_39_dec);
+	&Xupdate_avx_32_79(\&body_20_39_dec);
+	&Xupdate_avx_32_79(\&body_40_59_dec);
+	&Xupdate_avx_32_79(\&body_40_59_dec);
+	&Xupdate_avx_32_79(\&body_40_59_dec);
+	&Xupdate_avx_32_79(\&body_40_59_dec);
+	&Xupdate_avx_32_79(\&body_40_59_dec);
+	&Xupdate_avx_32_79(\&body_20_39_dec);
+	&Xuplast_avx_80(\&body_20_39_dec,".Ldone_dec_avx");	# can jump to "done"
+
+				$saved_j=$j; @saved_V=@V;
+				$saved_rx=$rx;
+
+	&Xloop_avx(\&body_20_39_dec);
+	&Xloop_avx(\&body_20_39_dec);
+	&Xloop_avx(\&body_20_39_dec);
+
+	eval(@aes256_dec[-1]);			# last store
+$code.=<<___;
+	lea	64($in0),$in0
+
+	add	0($ctx),$A			# update context
+	add	4($ctx),@T[0]
+	add	8($ctx),$C
+	add	12($ctx),$D
+	mov	$A,0($ctx)
+	add	16($ctx),$E
+	mov	@T[0],4($ctx)
+	mov	@T[0],$B			# magic seed
+	mov	$C,8($ctx)
+	mov	$C,@T[1]
+	mov	$D,12($ctx)
+	xor	$D,@T[1]
+	mov	$E,16($ctx)
+	and	@T[1],@T[0]
+	jmp	.Loop_dec_avx
+
+.Ldone_dec_avx:
+___
+				$jj=$j=$saved_j; @V=@saved_V;
+				$rx=$saved_rx;
+
+	&Xtail_avx(\&body_20_39_dec);
+	&Xtail_avx(\&body_20_39_dec);
+	&Xtail_avx(\&body_20_39_dec);
+
+	eval(@aes256_dec[-1]);			# last store
+$code.=<<___;
+
+	add	0($ctx),$A			# update context
+	add	4($ctx),@T[0]
+	add	8($ctx),$C
+	mov	$A,0($ctx)
+	add	12($ctx),$D
+	mov	@T[0],4($ctx)
+	add	16($ctx),$E
+	mov	$C,8($ctx)
+	mov	$D,12($ctx)
+	mov	$E,16($ctx)
+	vmovups	@X[3],($ivp)			# write IV
+	vzeroall
+___
+$code.=<<___ if ($win64);
+	movaps	96+0(%rsp),%xmm6
+	movaps	96+16(%rsp),%xmm7
+	movaps	96+32(%rsp),%xmm8
+	movaps	96+48(%rsp),%xmm9
+	movaps	96+64(%rsp),%xmm10
+	movaps	96+80(%rsp),%xmm11
+	movaps	96+96(%rsp),%xmm12
+	movaps	96+112(%rsp),%xmm13
+	movaps	96+128(%rsp),%xmm14
+	movaps	96+144(%rsp),%xmm15
+___
+$code.=<<___;
+	lea	`104+($win64?10*16:0)`(%rsp),%rsi
+	mov	0(%rsi),%r15
+	mov	8(%rsi),%r14
+	mov	16(%rsi),%r13
+	mov	24(%rsi),%r12
+	mov	32(%rsi),%rbp
+	mov	40(%rsi),%rbx
+	lea	48(%rsi),%rsp
+.Lepilogue_dec_avx:
+	ret
+.size	aesni256_cbc_sha1_dec_avx,.-aesni256_cbc_sha1_dec_avx
+___
+						}}}
+}
+$code.=<<___;
+.align	64
+K_XX_XX:
+.long	0x5a827999,0x5a827999,0x5a827999,0x5a827999	# K_00_19
+.long	0x6ed9eba1,0x6ed9eba1,0x6ed9eba1,0x6ed9eba1	# K_20_39
+.long	0x8f1bbcdc,0x8f1bbcdc,0x8f1bbcdc,0x8f1bbcdc	# K_40_59
+.long	0xca62c1d6,0xca62c1d6,0xca62c1d6,0xca62c1d6	# K_60_79
+.long	0x00010203,0x04050607,0x08090a0b,0x0c0d0e0f	# pbswap mask
+.byte	0xf,0xe,0xd,0xc,0xb,0xa,0x9,0x8,0x7,0x6,0x5,0x4,0x3,0x2,0x1,0x0
+
+.asciz	"AESNI-CBC+SHA1 stitch for x86_64, CRYPTOGAMS by "
+.align	64
+___
+						if ($shaext) {{{
+($in0,$out,$len,$key,$ivp,$ctx,$inp)=("%rdi","%rsi","%rdx","%rcx","%r8","%r9","%r10");
+
+$rounds="%r11d";
+
+($iv,$in,$rndkey0)=map("%xmm$_",(2,14,15));
+@rndkey=("%xmm0","%xmm1");
+$r=0;
+
+my ($BSWAP,$ABCD,$E,$E_,$ABCD_SAVE,$E_SAVE)=map("%xmm$_",(7..12));
+my @MSG=map("%xmm$_",(3..6));
+
+$code.=<<___;
+.type	aesni_cbc_sha1_enc_shaext,\@function,6
+.align	32
+aesni_cbc_sha1_enc_shaext:
+	mov	`($win64?56:8)`(%rsp),$inp	# load 7th argument
+___
+$code.=<<___ if ($win64);
+	lea	`-8-10*16`(%rsp),%rsp
+	movaps	%xmm6,-8-10*16(%rax)
+	movaps	%xmm7,-8-9*16(%rax)
+	movaps	%xmm8,-8-8*16(%rax)
+	movaps	%xmm9,-8-7*16(%rax)
+	movaps	%xmm10,-8-6*16(%rax)
+	movaps	%xmm11,-8-5*16(%rax)
+	movaps	%xmm12,-8-4*16(%rax)
+	movaps	%xmm13,-8-3*16(%rax)
+	movaps	%xmm14,-8-2*16(%rax)
+	movaps	%xmm15,-8-1*16(%rax)
+.Lprologue_shaext:
+___
+$code.=<<___;
+	movdqu	($ctx),$ABCD
+	movd	16($ctx),$E
+	movdqa	K_XX_XX+0x50(%rip),$BSWAP	# byte-n-word swap
+
+	mov	240($key),$rounds
+	sub	$in0,$out
+	movups	($key),$rndkey0			# $key[0]
+	movups	16($key),$rndkey[0]		# forward reference
+	lea	112($key),$key			# size optimization
+
+	pshufd	\$0b00011011,$ABCD,$ABCD	# flip word order
+	pshufd	\$0b00011011,$E,$E		# flip word order
+	jmp	.Loop_shaext
+
+.align	16
+.Loop_shaext:
+___
+	&$aesenc();
+$code.=<<___;
+	movdqu		($inp),@MSG[0]
+	movdqa		$E,$E_SAVE		# offload $E
+	pshufb		$BSWAP,@MSG[0]
+	movdqu		0x10($inp),@MSG[1]
+	movdqa		$ABCD,$ABCD_SAVE	# offload $ABCD
+___
+	&$aesenc();
+$code.=<<___;
+	pshufb		$BSWAP,@MSG[1]
+
+	paddd		@MSG[0],$E
+	movdqu		0x20($inp),@MSG[2]
+	lea		0x40($inp),$inp
+	pxor		$E_SAVE,@MSG[0]		# black magic
+___
+	&$aesenc();
+$code.=<<___;
+	pxor		$E_SAVE,@MSG[0]		# black magic
+	movdqa		$ABCD,$E_
+	pshufb		$BSWAP,@MSG[2]
+	sha1rnds4	\$0,$E,$ABCD		# 0-3
+	sha1nexte	@MSG[1],$E_
+___
+	&$aesenc();
+$code.=<<___;
+	sha1msg1	@MSG[1],@MSG[0]
+	movdqu		-0x10($inp),@MSG[3]
+	movdqa		$ABCD,$E
+	pshufb		$BSWAP,@MSG[3]
+___
+	&$aesenc();
+$code.=<<___;
+	sha1rnds4	\$0,$E_,$ABCD		# 4-7
+	sha1nexte	@MSG[2],$E
+	pxor		@MSG[2],@MSG[0]
+	sha1msg1	@MSG[2],@MSG[1]
+___
+	&$aesenc();
+
+for($i=2;$i<20-4;$i++) {
+$code.=<<___;
+	movdqa		$ABCD,$E_
+	sha1rnds4	\$`int($i/5)`,$E,$ABCD	# 8-11
+	sha1nexte	@MSG[3],$E_
+___
+	&$aesenc();
+$code.=<<___;
+	sha1msg2	@MSG[3],@MSG[0]
+	pxor		@MSG[3],@MSG[1]
+	sha1msg1	@MSG[3],@MSG[2]
+___
+	($E,$E_)=($E_,$E);
+	push(@MSG,shift(@MSG));
+
+	&$aesenc();
+}
+$code.=<<___;
+	movdqa		$ABCD,$E_
+	sha1rnds4	\$3,$E,$ABCD		# 64-67
+	sha1nexte	@MSG[3],$E_
+	sha1msg2	@MSG[3],@MSG[0]
+	pxor		@MSG[3],@MSG[1]
+___
+	&$aesenc();
+$code.=<<___;
+	movdqa		$ABCD,$E
+	sha1rnds4	\$3,$E_,$ABCD		# 68-71
+	sha1nexte	@MSG[0],$E
+	sha1msg2	@MSG[0],@MSG[1]
+___
+	&$aesenc();
+$code.=<<___;
+	movdqa		$E_SAVE,@MSG[0]
+	movdqa		$ABCD,$E_
+	sha1rnds4	\$3,$E,$ABCD		# 72-75
+	sha1nexte	@MSG[1],$E_
+___
+	&$aesenc();
+$code.=<<___;
+	movdqa		$ABCD,$E
+	sha1rnds4	\$3,$E_,$ABCD		# 76-79
+	sha1nexte	$MSG[0],$E
+___
+	while($r<40)	{ &$aesenc(); }		# remaining aesenc's
+$code.=<<___;
+	dec		$len
+
+	paddd		$ABCD_SAVE,$ABCD
+	movups		$iv,48($out,$in0)	# write output
+	lea		64($in0),$in0
+	jnz		.Loop_shaext
+
+	pshufd	\$0b00011011,$ABCD,$ABCD
+	pshufd	\$0b00011011,$E,$E
+	movups	$iv,($ivp)			# write IV
+	movdqu	$ABCD,($ctx)
+	movd	$E,16($ctx)
+___
+$code.=<<___ if ($win64);
+	movaps	-8-10*16(%rax),%xmm6
+	movaps	-8-9*16(%rax),%xmm7
+	movaps	-8-8*16(%rax),%xmm8
+	movaps	-8-7*16(%rax),%xmm9
+	movaps	-8-6*16(%rax),%xmm10
+	movaps	-8-5*16(%rax),%xmm11
+	movaps	-8-4*16(%rax),%xmm12
+	movaps	-8-3*16(%rax),%xmm13
+	movaps	-8-2*16(%rax),%xmm14
+	movaps	-8-1*16(%rax),%xmm15
+	mov	%rax,%rsp
+.Lepilogue_shaext:
+___
+$code.=<<___;
+	ret
+.size	aesni_cbc_sha1_enc_shaext,.-aesni_cbc_sha1_enc_shaext
+___
+						}}}
+# EXCEPTION_DISPOSITION handler (EXCEPTION_RECORD *rec,ULONG64 frame,
+#		CONTEXT *context,DISPATCHER_CONTEXT *disp)
+if ($win64) {
+$rec="%rcx";
+$frame="%rdx";
+$context="%r8";
+$disp="%r9";
+
+$code.=<<___;
+.extern	__imp_RtlVirtualUnwind
+.type	ssse3_handler,\@abi-omnipotent
+.align	16
+ssse3_handler:
+	push	%rsi
+	push	%rdi
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+	pushfq
+	sub	\$64,%rsp
+
+	mov	120($context),%rax	# pull context->Rax
+	mov	248($context),%rbx	# pull context->Rip
+
+	mov	8($disp),%rsi		# disp->ImageBase
+	mov	56($disp),%r11		# disp->HandlerData
+
+	mov	0(%r11),%r10d		# HandlerData[0]
+	lea	(%rsi,%r10),%r10	# prologue label
+	cmp	%r10,%rbx		# context->RipRsp
+
+	mov	4(%r11),%r10d		# HandlerData[1]
+	lea	(%rsi,%r10),%r10	# epilogue label
+	cmp	%r10,%rbx		# context->Rip>=epilogue label
+	jae	.Lcommon_seh_tail
+___
+$code.=<<___ if ($shaext);
+	lea	aesni_cbc_sha1_enc_shaext(%rip),%r10
+	cmp	%r10,%rbx
+	jb	.Lseh_no_shaext
+
+	lea	(%rax),%rsi
+	lea	512($context),%rdi	# &context.Xmm6
+	mov	\$20,%ecx
+	.long	0xa548f3fc		# cld; rep movsq
+	lea	168(%rax),%rax		# adjust stack pointer
+	jmp	.Lcommon_seh_tail
+.Lseh_no_shaext:
+___
+$code.=<<___;
+	lea	96(%rax),%rsi
+	lea	512($context),%rdi	# &context.Xmm6
+	mov	\$20,%ecx
+	.long	0xa548f3fc		# cld; rep movsq
+	lea	`104+10*16`(%rax),%rax	# adjust stack pointer
+
+	mov	0(%rax),%r15
+	mov	8(%rax),%r14
+	mov	16(%rax),%r13
+	mov	24(%rax),%r12
+	mov	32(%rax),%rbp
+	mov	40(%rax),%rbx
+	lea	48(%rax),%rax
+	mov	%rbx,144($context)	# restore context->Rbx
+	mov	%rbp,160($context)	# restore context->Rbp
+	mov	%r12,216($context)	# restore context->R12
+	mov	%r13,224($context)	# restore context->R13
+	mov	%r14,232($context)	# restore context->R14
+	mov	%r15,240($context)	# restore context->R15
+
+.Lcommon_seh_tail:
+	mov	8(%rax),%rdi
+	mov	16(%rax),%rsi
+	mov	%rax,152($context)	# restore context->Rsp
+	mov	%rsi,168($context)	# restore context->Rsi
+	mov	%rdi,176($context)	# restore context->Rdi
+
+	mov	40($disp),%rdi		# disp->ContextRecord
+	mov	$context,%rsi		# context
+	mov	\$154,%ecx		# sizeof(CONTEXT)
+	.long	0xa548f3fc		# cld; rep movsq
+
+	mov	$disp,%rsi
+	xor	%rcx,%rcx		# arg1, UNW_FLAG_NHANDLER
+	mov	8(%rsi),%rdx		# arg2, disp->ImageBase
+	mov	0(%rsi),%r8		# arg3, disp->ControlPc
+	mov	16(%rsi),%r9		# arg4, disp->FunctionEntry
+	mov	40(%rsi),%r10		# disp->ContextRecord
+	lea	56(%rsi),%r11		# &disp->HandlerData
+	lea	24(%rsi),%r12		# &disp->EstablisherFrame
+	mov	%r10,32(%rsp)		# arg5
+	mov	%r11,40(%rsp)		# arg6
+	mov	%r12,48(%rsp)		# arg7
+	mov	%rcx,56(%rsp)		# arg8, (NULL)
+	call	*__imp_RtlVirtualUnwind(%rip)
+
+	mov	\$1,%eax		# ExceptionContinueSearch
+	add	\$64,%rsp
+	popfq
+	pop	%r15
+	pop	%r14
+	pop	%r13
+	pop	%r12
+	pop	%rbp
+	pop	%rbx
+	pop	%rdi
+	pop	%rsi
+	ret
+.size	ssse3_handler,.-ssse3_handler
+
+.section	.pdata
+.align	4
+	.rva	.LSEH_begin_aesni_cbc_sha1_enc_ssse3
+	.rva	.LSEH_end_aesni_cbc_sha1_enc_ssse3
+	.rva	.LSEH_info_aesni_cbc_sha1_enc_ssse3
+___
+$code.=<<___ if ($avx);
+	.rva	.LSEH_begin_aesni_cbc_sha1_enc_avx
+	.rva	.LSEH_end_aesni_cbc_sha1_enc_avx
+	.rva	.LSEH_info_aesni_cbc_sha1_enc_avx
+___
+$code.=<<___ if ($shaext);
+	.rva	.LSEH_begin_aesni_cbc_sha1_enc_shaext
+	.rva	.LSEH_end_aesni_cbc_sha1_enc_shaext
+	.rva	.LSEH_info_aesni_cbc_sha1_enc_shaext
+___
+$code.=<<___;
+.section	.xdata
+.align	8
+.LSEH_info_aesni_cbc_sha1_enc_ssse3:
+	.byte	9,0,0,0
+	.rva	ssse3_handler
+	.rva	.Lprologue_ssse3,.Lepilogue_ssse3	# HandlerData[]
+___
+$code.=<<___ if ($avx);
+.LSEH_info_aesni_cbc_sha1_enc_avx:
+	.byte	9,0,0,0
+	.rva	ssse3_handler
+	.rva	.Lprologue_avx,.Lepilogue_avx		# HandlerData[]
+___
+$code.=<<___ if ($shaext);
+.LSEH_info_aesni_cbc_sha1_enc_shaext:
+	.byte	9,0,0,0
+	.rva	ssse3_handler
+	.rva	.Lprologue_shaext,.Lepilogue_shaext	# HandlerData[]
+___
+}
+
+####################################################################
+sub rex {
+  local *opcode=shift;
+  my ($dst,$src)=@_;
+  my $rex=0;
+
+    $rex|=0x04			if($dst>=8);
+    $rex|=0x01			if($src>=8);
+    unshift @opcode,$rex|0x40	if($rex);
+}
+
+sub sha1rnds4 {
+    if (@_[0] =~ /\$([x0-9a-f]+),\s*%xmm([0-9]+),\s*%xmm([0-9]+)/) {
+      my @opcode=(0x0f,0x3a,0xcc);
+	rex(\@opcode,$3,$2);
+	push @opcode,0xc0|($2&7)|(($3&7)<<3);		# ModR/M
+	my $c=$1;
+	push @opcode,$c=~/^0/?oct($c):$c;
+	return ".byte\t".join(',',@opcode);
+    } else {
+	return "sha1rnds4\t".@_[0];
+    }
+}
+
+sub sha1op38 {
+    my $instr = shift;
+    my %opcodelet = (
+		"sha1nexte" => 0xc8,
+  		"sha1msg1"  => 0xc9,
+		"sha1msg2"  => 0xca	);
+
+    if (defined($opcodelet{$instr}) && @_[0] =~ /%xmm([0-9]+),\s*%xmm([0-9]+)/) {
+      my @opcode=(0x0f,0x38);
+	rex(\@opcode,$2,$1);
+	push @opcode,$opcodelet{$instr};
+	push @opcode,0xc0|($1&7)|(($2&7)<<3);		# ModR/M
+	return ".byte\t".join(',',@opcode);
+    } else {
+	return $instr."\t".@_[0];
+    }
+}
+
+sub aesni {
+  my $line=shift;
+  my @opcode=(0x0f,0x38);
+
+    if ($line=~/(aes[a-z]+)\s+%xmm([0-9]+),\s*%xmm([0-9]+)/) {
+	my %opcodelet = (
+		"aesenc" => 0xdc,	"aesenclast" => 0xdd,
+		"aesdec" => 0xde,	"aesdeclast" => 0xdf
+	);
+	return undef if (!defined($opcodelet{$1}));
+	rex(\@opcode,$3,$2);
+	push @opcode,$opcodelet{$1},0xc0|($2&7)|(($3&7)<<3);	# ModR/M
+	unshift @opcode,0x66;
+	return ".byte\t".join(',',@opcode);
+    }
+    return $line;
+}
+
+foreach (split("\n",$code)) {
+        s/\`([^\`]*)\`/eval $1/geo;
+
+	s/\b(sha1rnds4)\s+(.*)/sha1rnds4($2)/geo		or
+	s/\b(sha1[^\s]*)\s+(.*)/sha1op38($1,$2)/geo		or
+	s/\b(aes.*%xmm[0-9]+).*$/aesni($1)/geo;
+
+	print $_,"\n";
+}
+close STDOUT;
diff --git a/crypto/aes/asm/aesni-sha256-x86_64.pl b/crypto/aes/asm/aesni-sha256-x86_64.pl
new file mode 100644
index 000000000..a5fde2e4d
--- /dev/null
+++ b/crypto/aes/asm/aesni-sha256-x86_64.pl
@@ -0,0 +1,1713 @@
+#! /usr/bin/env perl
+# Copyright 2013-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+#
+# ====================================================================
+# Written by Andy Polyakov  for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+#
+# January 2013
+#
+# This is AESNI-CBC+SHA256 stitch implementation. The idea, as spelled
+# in http://download.intel.com/design/intarch/papers/323686.pdf, is
+# that since AESNI-CBC encrypt exhibit *very* low instruction-level
+# parallelism, interleaving it with another algorithm would allow to
+# utilize processor resources better and achieve better performance.
+# SHA256 instruction sequences(*) are taken from sha512-x86_64.pl and
+# AESNI code is weaved into it. As SHA256 dominates execution time,
+# stitch performance does not depend on AES key length. Below are
+# performance numbers in cycles per processed byte, less is better,
+# for standalone AESNI-CBC encrypt, standalone SHA256, and stitched
+# subroutine:
+#
+#		 AES-128/-192/-256+SHA256	this(**)gain
+# Sandy Bridge	    5.05/6.05/7.05+11.6		13.0	+28%/36%/43%
+# Ivy Bridge	    5.05/6.05/7.05+10.3		11.6	+32%/41%/50%
+# Haswell	    4.43/5.29/6.19+7.80		8.79	+39%/49%/59%
+# Skylake	    2.62/3.14/3.62+7.70		8.10	+27%/34%/40%
+# Bulldozer	    5.77/6.89/8.00+13.7		13.7	+42%/50%/58%
+#
+# (*)	there are XOP, AVX1 and AVX2 code paths, meaning that
+#	Westmere is omitted from loop, this is because gain was not
+#	estimated high enough to justify the effort;
+# (**)	these are EVP-free results, results obtained with 'speed
+#	-evp aes-256-cbc-hmac-sha256' will vary by percent or two;
+
+$flavour = shift;
+$output  = shift;
+if ($flavour =~ /\./) { $output = $flavour; undef $flavour; }
+
+$win64=0; $win64=1 if ($flavour =~ /[nm]asm|mingw64/ || $output =~ /\.asm$/);
+
+$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+( $xlate="${dir}x86_64-xlate.pl" and -f $xlate ) or
+( $xlate="${dir}../../perlasm/x86_64-xlate.pl" and -f $xlate) or
+die "can't locate x86_64-xlate.pl";
+
+if (`$ENV{CC} -Wa,-v -c -o /dev/null -x assembler /dev/null 2>&1`
+		=~ /GNU assembler version ([2-9]\.[0-9]+)/) {
+	$avx = ($1>=2.19) + ($1>=2.22);
+}
+
+if (!$avx && $win64 && ($flavour =~ /nasm/ || $ENV{ASM} =~ /nasm/) &&
+	   `nasm -v 2>&1` =~ /NASM version ([2-9]\.[0-9]+)/) {
+	$avx = ($1>=2.09) + ($1>=2.10);
+}
+
+if (!$avx && $win64 && ($flavour =~ /masm/ || $ENV{ASM} =~ /ml64/) &&
+	   `ml64 2>&1` =~ /Version ([0-9]+)\./) {
+	$avx = ($1>=10) + ($1>=12);
+}
+
+if (!$avx && `$ENV{CC} -v 2>&1` =~ /((?:^clang|LLVM) version|.*based on LLVM) ([3-9]\.[0-9]+)/) {
+	$avx = ($2>=3.0) + ($2>3.0);
+}
+
+$shaext=$avx;	### set to zero if compiling for 1.0.1
+$avx=1		if (!$shaext && $avx);
+
+open OUT,"| \"$^X\" \"$xlate\" $flavour \"$output\"";
+*STDOUT=*OUT;
+
+$func="aesni_cbc_sha256_enc";
+$TABLE="K256";
+$SZ=4;
+@ROT=($A,$B,$C,$D,$E,$F,$G,$H)=("%eax","%ebx","%ecx","%edx",
+				"%r8d","%r9d","%r10d","%r11d");
+($T1,$a0,$a1,$a2,$a3)=("%r12d","%r13d","%r14d","%r15d","%esi");
+@Sigma0=( 2,13,22);
+@Sigma1=( 6,11,25);
+@sigma0=( 7,18, 3);
+@sigma1=(17,19,10);
+$rounds=64;
+
+########################################################################
+# void aesni_cbc_sha256_enc(const void *inp,
+#			void *out,
+#			size_t length,
+#			const AES_KEY *key,
+#			unsigned char *iv,
+#			SHA256_CTX *ctx,
+#			const void *in0);
+($inp,  $out,  $len,  $key,  $ivp, $ctx, $in0) =
+("%rdi","%rsi","%rdx","%rcx","%r8","%r9","%r10");
+
+$Tbl="%rbp";
+
+$_inp="16*$SZ+0*8(%rsp)";
+$_out="16*$SZ+1*8(%rsp)";
+$_end="16*$SZ+2*8(%rsp)";
+$_key="16*$SZ+3*8(%rsp)";
+$_ivp="16*$SZ+4*8(%rsp)";
+$_ctx="16*$SZ+5*8(%rsp)";
+$_in0="16*$SZ+6*8(%rsp)";
+$_rsp="16*$SZ+7*8(%rsp)";
+$framesz=16*$SZ+8*8;
+
+$code=<<___;
+.text
+
+.extern	OPENSSL_ia32cap_P
+.globl	$func
+.type	$func,\@abi-omnipotent
+.align	16
+$func:
+___
+						if ($avx) {
+$code.=<<___;
+	lea	OPENSSL_ia32cap_P(%rip),%r11
+	mov	\$1,%eax
+	cmp	\$0,`$win64?"%rcx":"%rdi"`
+	je	.Lprobe
+	mov	0(%r11),%eax
+	mov	4(%r11),%r10
+___
+$code.=<<___ if ($shaext);
+	bt	\$61,%r10			# check for SHA
+	jc	${func}_shaext
+___
+$code.=<<___;
+	mov	%r10,%r11
+	shr	\$32,%r11
+
+	test	\$`1<<11`,%r10d			# check for XOP
+	jnz	${func}_xop
+___
+$code.=<<___ if ($avx>1);
+	and	\$`1<<8|1<<5|1<<3`,%r11d	# check for BMI2+AVX2+BMI1
+	cmp	\$`1<<8|1<<5|1<<3`,%r11d
+	je	${func}_avx2
+___
+$code.=<<___;
+	and	\$`1<<28`,%r10d			# check for AVX
+	jnz	${func}_avx
+	ud2
+___
+						}
+$code.=<<___;
+	xor	%eax,%eax
+	cmp	\$0,`$win64?"%rcx":"%rdi"`
+	je	.Lprobe
+	ud2
+.Lprobe:
+	ret
+.size	$func,.-$func
+
+.align	64
+.type	$TABLE,\@object
+$TABLE:
+	.long	0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5
+	.long	0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5
+	.long	0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5
+	.long	0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5
+	.long	0xd807aa98,0x12835b01,0x243185be,0x550c7dc3
+	.long	0xd807aa98,0x12835b01,0x243185be,0x550c7dc3
+	.long	0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174
+	.long	0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174
+	.long	0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc
+	.long	0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc
+	.long	0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da
+	.long	0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da
+	.long	0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7
+	.long	0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7
+	.long	0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967
+	.long	0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967
+	.long	0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13
+	.long	0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13
+	.long	0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85
+	.long	0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85
+	.long	0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3
+	.long	0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3
+	.long	0xd192e819,0xd6990624,0xf40e3585,0x106aa070
+	.long	0xd192e819,0xd6990624,0xf40e3585,0x106aa070
+	.long	0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5
+	.long	0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5
+	.long	0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3
+	.long	0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3
+	.long	0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208
+	.long	0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208
+	.long	0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2
+	.long	0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2
+
+	.long	0x00010203,0x04050607,0x08090a0b,0x0c0d0e0f
+	.long	0x00010203,0x04050607,0x08090a0b,0x0c0d0e0f
+	.long	0,0,0,0,   0,0,0,0,   -1,-1,-1,-1
+	.long	0,0,0,0,   0,0,0,0
+	.asciz	"AESNI-CBC+SHA256 stitch for x86_64, CRYPTOGAMS by "
+.align	64
+___
+
+######################################################################
+# SIMD code paths
+#
+{{{
+($iv,$inout,$roundkey,$temp,
+ $mask10,$mask12,$mask14,$offload)=map("%xmm$_",(8..15));
+
+$aesni_cbc_idx=0;
+@aesni_cbc_block = (
+##	&vmovdqu	($roundkey,"0x00-0x80($inp)");'
+##	&vmovdqu	($inout,($inp));
+##	&mov		($_inp,$inp);
+
+	'&vpxor		($inout,$inout,$roundkey);'.
+	' &vmovdqu	($roundkey,"0x10-0x80($inp)");',
+
+	'&vpxor		($inout,$inout,$iv);',
+
+	'&vaesenc	($inout,$inout,$roundkey);'.
+	' &vmovdqu	($roundkey,"0x20-0x80($inp)");',
+
+	'&vaesenc	($inout,$inout,$roundkey);'.
+	' &vmovdqu	($roundkey,"0x30-0x80($inp)");',
+
+	'&vaesenc	($inout,$inout,$roundkey);'.
+	' &vmovdqu	($roundkey,"0x40-0x80($inp)");',
+
+	'&vaesenc	($inout,$inout,$roundkey);'.
+	' &vmovdqu	($roundkey,"0x50-0x80($inp)");',
+
+	'&vaesenc	($inout,$inout,$roundkey);'.
+	' &vmovdqu	($roundkey,"0x60-0x80($inp)");',
+
+	'&vaesenc	($inout,$inout,$roundkey);'.
+	' &vmovdqu	($roundkey,"0x70-0x80($inp)");',
+
+	'&vaesenc	($inout,$inout,$roundkey);'.
+	' &vmovdqu	($roundkey,"0x80-0x80($inp)");',
+
+	'&vaesenc	($inout,$inout,$roundkey);'.
+	' &vmovdqu	($roundkey,"0x90-0x80($inp)");',
+
+	'&vaesenc	($inout,$inout,$roundkey);'.
+	' &vmovdqu	($roundkey,"0xa0-0x80($inp)");',
+
+	'&vaesenclast	($temp,$inout,$roundkey);'.
+	' &vaesenc	($inout,$inout,$roundkey);'.
+	' &vmovdqu	($roundkey,"0xb0-0x80($inp)");',
+
+	'&vpand		($iv,$temp,$mask10);'.
+	' &vaesenc	($inout,$inout,$roundkey);'.
+	' &vmovdqu	($roundkey,"0xc0-0x80($inp)");',
+
+	'&vaesenclast	($temp,$inout,$roundkey);'.
+	' &vaesenc	($inout,$inout,$roundkey);'.
+	' &vmovdqu	($roundkey,"0xd0-0x80($inp)");',
+
+	'&vpand		($temp,$temp,$mask12);'.
+	' &vaesenc	($inout,$inout,$roundkey);'.
+	 '&vmovdqu	($roundkey,"0xe0-0x80($inp)");',
+
+	'&vpor		($iv,$iv,$temp);'.
+	' &vaesenclast	($temp,$inout,$roundkey);'.
+	' &vmovdqu	($roundkey,"0x00-0x80($inp)");'
+
+##	&mov		($inp,$_inp);
+##	&mov		($out,$_out);
+##	&vpand		($temp,$temp,$mask14);
+##	&vpor		($iv,$iv,$temp);
+##	&vmovdqu	($iv,($out,$inp);
+##	&lea		(inp,16($inp));
+);
+
+my $a4=$T1;
+my ($a,$b,$c,$d,$e,$f,$g,$h);
+
+sub AUTOLOAD()		# thunk [simplified] 32-bit style perlasm
+{ my $opcode = $AUTOLOAD; $opcode =~ s/.*:://;
+  my $arg = pop;
+    $arg = "\$$arg" if ($arg*1 eq $arg);
+    $code .= "\t$opcode\t".join(',',$arg,reverse @_)."\n";
+}
+
+sub body_00_15 () {
+	(
+	'($a,$b,$c,$d,$e,$f,$g,$h)=@ROT;'.
+
+	'&ror	($a0,$Sigma1[2]-$Sigma1[1])',
+	'&mov	($a,$a1)',
+	'&mov	($a4,$f)',
+
+	'&xor	($a0,$e)',
+	'&ror	($a1,$Sigma0[2]-$Sigma0[1])',
+	'&xor	($a4,$g)',			# f^g
+
+	'&ror	($a0,$Sigma1[1]-$Sigma1[0])',
+	'&xor	($a1,$a)',
+	'&and	($a4,$e)',			# (f^g)&e
+
+	@aesni_cbc_block[$aesni_cbc_idx++].
+	'&xor	($a0,$e)',
+	'&add	($h,$SZ*($i&15)."(%rsp)")',	# h+=X[i]+K[i]
+	'&mov	($a2,$a)',
+
+	'&ror	($a1,$Sigma0[1]-$Sigma0[0])',
+	'&xor	($a4,$g)',			# Ch(e,f,g)=((f^g)&e)^g
+	'&xor	($a2,$b)',			# a^b, b^c in next round
+
+	'&ror	($a0,$Sigma1[0])',		# Sigma1(e)
+	'&add	($h,$a4)',			# h+=Ch(e,f,g)
+	'&and	($a3,$a2)',			# (b^c)&(a^b)
+
+	'&xor	($a1,$a)',
+	'&add	($h,$a0)',			# h+=Sigma1(e)
+	'&xor	($a3,$b)',			# Maj(a,b,c)=Ch(a^b,c,b)
+
+	'&add	($d,$h)',			# d+=h
+	'&ror	($a1,$Sigma0[0])',		# Sigma0(a)
+	'&add	($h,$a3)',			# h+=Maj(a,b,c)
+
+	'&mov	($a0,$d)',
+	'&add	($a1,$h);'.			# h+=Sigma0(a)
+	'($a2,$a3) = ($a3,$a2); unshift(@ROT,pop(@ROT)); $i++;'
+	);
+}
+
+if ($avx) {{
+######################################################################
+# XOP code path
+#
+$code.=<<___;
+.type	${func}_xop,\@function,6
+.align	64
+${func}_xop:
+.Lxop_shortcut:
+	mov	`($win64?56:8)`(%rsp),$in0	# load 7th parameter
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+	mov	%rsp,%r11		# copy %rsp
+	sub	\$`$framesz+$win64*16*10`,%rsp
+	and	\$-64,%rsp		# align stack frame
+
+	shl	\$6,$len
+	sub	$inp,$out		# re-bias
+	sub	$inp,$in0
+	add	$inp,$len		# end of input
+
+	#mov	$inp,$_inp		# saved later
+	mov	$out,$_out
+	mov	$len,$_end
+	#mov	$key,$_key		# remains resident in $inp register
+	mov	$ivp,$_ivp
+	mov	$ctx,$_ctx
+	mov	$in0,$_in0
+	mov	%r11,$_rsp
+___
+$code.=<<___ if ($win64);
+	movaps	%xmm6,`$framesz+16*0`(%rsp)
+	movaps	%xmm7,`$framesz+16*1`(%rsp)
+	movaps	%xmm8,`$framesz+16*2`(%rsp)
+	movaps	%xmm9,`$framesz+16*3`(%rsp)
+	movaps	%xmm10,`$framesz+16*4`(%rsp)
+	movaps	%xmm11,`$framesz+16*5`(%rsp)
+	movaps	%xmm12,`$framesz+16*6`(%rsp)
+	movaps	%xmm13,`$framesz+16*7`(%rsp)
+	movaps	%xmm14,`$framesz+16*8`(%rsp)
+	movaps	%xmm15,`$framesz+16*9`(%rsp)
+___
+$code.=<<___;
+.Lprologue_xop:
+	vzeroall
+
+	mov	$inp,%r12		# borrow $a4
+	lea	0x80($key),$inp		# size optimization, reassign
+	lea	$TABLE+`$SZ*2*$rounds+32`(%rip),%r13	# borrow $a0
+	mov	0xf0-0x80($inp),%r14d	# rounds, borrow $a1
+	mov	$ctx,%r15		# borrow $a2
+	mov	$in0,%rsi		# borrow $a3
+	vmovdqu	($ivp),$iv		# load IV
+	sub	\$9,%r14
+
+	mov	$SZ*0(%r15),$A
+	mov	$SZ*1(%r15),$B
+	mov	$SZ*2(%r15),$C
+	mov	$SZ*3(%r15),$D
+	mov	$SZ*4(%r15),$E
+	mov	$SZ*5(%r15),$F
+	mov	$SZ*6(%r15),$G
+	mov	$SZ*7(%r15),$H
+
+	vmovdqa	0x00(%r13,%r14,8),$mask14
+	vmovdqa	0x10(%r13,%r14,8),$mask12
+	vmovdqa	0x20(%r13,%r14,8),$mask10
+	vmovdqu	0x00-0x80($inp),$roundkey
+	jmp	.Lloop_xop
+___
+					if ($SZ==4) {	# SHA256
+    my @X = map("%xmm$_",(0..3));
+    my ($t0,$t1,$t2,$t3) = map("%xmm$_",(4..7));
+
+$code.=<<___;
+.align	16
+.Lloop_xop:
+	vmovdqa	$TABLE+`$SZ*2*$rounds`(%rip),$t3
+	vmovdqu	0x00(%rsi,%r12),@X[0]
+	vmovdqu	0x10(%rsi,%r12),@X[1]
+	vmovdqu	0x20(%rsi,%r12),@X[2]
+	vmovdqu	0x30(%rsi,%r12),@X[3]
+	vpshufb	$t3,@X[0],@X[0]
+	lea	$TABLE(%rip),$Tbl
+	vpshufb	$t3,@X[1],@X[1]
+	vpshufb	$t3,@X[2],@X[2]
+	vpaddd	0x00($Tbl),@X[0],$t0
+	vpshufb	$t3,@X[3],@X[3]
+	vpaddd	0x20($Tbl),@X[1],$t1
+	vpaddd	0x40($Tbl),@X[2],$t2
+	vpaddd	0x60($Tbl),@X[3],$t3
+	vmovdqa	$t0,0x00(%rsp)
+	mov	$A,$a1
+	vmovdqa	$t1,0x10(%rsp)
+	mov	$B,$a3
+	vmovdqa	$t2,0x20(%rsp)
+	xor	$C,$a3			# magic
+	vmovdqa	$t3,0x30(%rsp)
+	mov	$E,$a0
+	jmp	.Lxop_00_47
+
+.align	16
+.Lxop_00_47:
+	sub	\$-16*2*$SZ,$Tbl	# size optimization
+	vmovdqu	(%r12),$inout		# $a4
+	mov	%r12,$_inp		# $a4
+___
+sub XOP_256_00_47 () {
+my $j = shift;
+my $body = shift;
+my @X = @_;
+my @insns = (&$body,&$body,&$body,&$body);	# 104 instructions
+
+	&vpalignr	($t0,@X[1],@X[0],$SZ);	# X[1..4]
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	 &vpalignr	($t3,@X[3],@X[2],$SZ);	# X[9..12]
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	&vprotd		($t1,$t0,8*$SZ-$sigma0[1]);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	&vpsrld		($t0,$t0,$sigma0[2]);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	 &vpaddd	(@X[0],@X[0],$t3);	# X[0..3] += X[9..12]
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	&vprotd		($t2,$t1,$sigma0[1]-$sigma0[0]);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	&vpxor		($t0,$t0,$t1);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	 &vprotd	($t3,@X[3],8*$SZ-$sigma1[1]);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	&vpxor		($t0,$t0,$t2);		# sigma0(X[1..4])
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	 &vpsrld	($t2,@X[3],$sigma1[2]);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	&vpaddd		(@X[0],@X[0],$t0);	# X[0..3] += sigma0(X[1..4])
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	 &vprotd	($t1,$t3,$sigma1[1]-$sigma1[0]);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	 &vpxor		($t3,$t3,$t2);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	 &vpxor		($t3,$t3,$t1);		# sigma1(X[14..15])
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	&vpsrldq	($t3,$t3,8);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	&vpaddd		(@X[0],@X[0],$t3);	# X[0..1] += sigma1(X[14..15])
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	 &vprotd	($t3,@X[0],8*$SZ-$sigma1[1]);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	 &vpsrld	($t2,@X[0],$sigma1[2]);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	 &vprotd	($t1,$t3,$sigma1[1]-$sigma1[0]);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	 &vpxor		($t3,$t3,$t2);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	 &vpxor		($t3,$t3,$t1);		# sigma1(X[16..17])
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	&vpslldq	($t3,$t3,8);		# 22 instructions
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	&vpaddd		(@X[0],@X[0],$t3);	# X[2..3] += sigma1(X[16..17])
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	&vpaddd		($t2,@X[0],16*2*$j."($Tbl)");
+	  foreach (@insns) { eval; }		# remaining instructions
+	&vmovdqa	(16*$j."(%rsp)",$t2);
+}
+
+    $aesni_cbc_idx=0;
+    for ($i=0,$j=0; $j<4; $j++) {
+	&XOP_256_00_47($j,\&body_00_15,@X);
+	push(@X,shift(@X));			# rotate(@X)
+    }
+    	&mov		("%r12",$_inp);		# borrow $a4
+	&vpand		($temp,$temp,$mask14);
+	&mov		("%r15",$_out);		# borrow $a2
+	&vpor		($iv,$iv,$temp);
+	&vmovdqu	("(%r15,%r12)",$iv);	# write output
+	&lea		("%r12","16(%r12)");	# inp++
+
+	&cmpb	($SZ-1+16*2*$SZ."($Tbl)",0);
+	&jne	(".Lxop_00_47");
+
+	&vmovdqu	($inout,"(%r12)");
+	&mov		($_inp,"%r12");
+
+    $aesni_cbc_idx=0;
+    for ($i=0; $i<16; ) {
+	foreach(body_00_15()) { eval; }
+    }
+					}
+$code.=<<___;
+	mov	$_inp,%r12		# borrow $a4
+	mov	$_out,%r13		# borrow $a0
+	mov	$_ctx,%r15		# borrow $a2
+	mov	$_in0,%rsi		# borrow $a3
+
+	vpand	$mask14,$temp,$temp
+	mov	$a1,$A
+	vpor	$temp,$iv,$iv
+	vmovdqu	$iv,(%r13,%r12)		# write output
+	lea	16(%r12),%r12		# inp++
+
+	add	$SZ*0(%r15),$A
+	add	$SZ*1(%r15),$B
+	add	$SZ*2(%r15),$C
+	add	$SZ*3(%r15),$D
+	add	$SZ*4(%r15),$E
+	add	$SZ*5(%r15),$F
+	add	$SZ*6(%r15),$G
+	add	$SZ*7(%r15),$H
+
+	cmp	$_end,%r12
+
+	mov	$A,$SZ*0(%r15)
+	mov	$B,$SZ*1(%r15)
+	mov	$C,$SZ*2(%r15)
+	mov	$D,$SZ*3(%r15)
+	mov	$E,$SZ*4(%r15)
+	mov	$F,$SZ*5(%r15)
+	mov	$G,$SZ*6(%r15)
+	mov	$H,$SZ*7(%r15)
+
+	jb	.Lloop_xop
+
+	mov	$_ivp,$ivp
+	mov	$_rsp,%rsi
+	vmovdqu	$iv,($ivp)		# output IV
+	vzeroall
+___
+$code.=<<___ if ($win64);
+	movaps	`$framesz+16*0`(%rsp),%xmm6
+	movaps	`$framesz+16*1`(%rsp),%xmm7
+	movaps	`$framesz+16*2`(%rsp),%xmm8
+	movaps	`$framesz+16*3`(%rsp),%xmm9
+	movaps	`$framesz+16*4`(%rsp),%xmm10
+	movaps	`$framesz+16*5`(%rsp),%xmm11
+	movaps	`$framesz+16*6`(%rsp),%xmm12
+	movaps	`$framesz+16*7`(%rsp),%xmm13
+	movaps	`$framesz+16*8`(%rsp),%xmm14
+	movaps	`$framesz+16*9`(%rsp),%xmm15
+___
+$code.=<<___;
+	mov	(%rsi),%r15
+	mov	8(%rsi),%r14
+	mov	16(%rsi),%r13
+	mov	24(%rsi),%r12
+	mov	32(%rsi),%rbp
+	mov	40(%rsi),%rbx
+	lea	48(%rsi),%rsp
+.Lepilogue_xop:
+	ret
+.size	${func}_xop,.-${func}_xop
+___
+######################################################################
+# AVX+shrd code path
+#
+local *ror = sub { &shrd(@_[0],@_) };
+
+$code.=<<___;
+.type	${func}_avx,\@function,6
+.align	64
+${func}_avx:
+.Lavx_shortcut:
+	mov	`($win64?56:8)`(%rsp),$in0	# load 7th parameter
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+	mov	%rsp,%r11		# copy %rsp
+	sub	\$`$framesz+$win64*16*10`,%rsp
+	and	\$-64,%rsp		# align stack frame
+
+	shl	\$6,$len
+	sub	$inp,$out		# re-bias
+	sub	$inp,$in0
+	add	$inp,$len		# end of input
+
+	#mov	$inp,$_inp		# saved later
+	mov	$out,$_out
+	mov	$len,$_end
+	#mov	$key,$_key		# remains resident in $inp register
+	mov	$ivp,$_ivp
+	mov	$ctx,$_ctx
+	mov	$in0,$_in0
+	mov	%r11,$_rsp
+___
+$code.=<<___ if ($win64);
+	movaps	%xmm6,`$framesz+16*0`(%rsp)
+	movaps	%xmm7,`$framesz+16*1`(%rsp)
+	movaps	%xmm8,`$framesz+16*2`(%rsp)
+	movaps	%xmm9,`$framesz+16*3`(%rsp)
+	movaps	%xmm10,`$framesz+16*4`(%rsp)
+	movaps	%xmm11,`$framesz+16*5`(%rsp)
+	movaps	%xmm12,`$framesz+16*6`(%rsp)
+	movaps	%xmm13,`$framesz+16*7`(%rsp)
+	movaps	%xmm14,`$framesz+16*8`(%rsp)
+	movaps	%xmm15,`$framesz+16*9`(%rsp)
+___
+$code.=<<___;
+.Lprologue_avx:
+	vzeroall
+
+	mov	$inp,%r12		# borrow $a4
+	lea	0x80($key),$inp		# size optimization, reassign
+	lea	$TABLE+`$SZ*2*$rounds+32`(%rip),%r13	# borrow $a0
+	mov	0xf0-0x80($inp),%r14d	# rounds, borrow $a1
+	mov	$ctx,%r15		# borrow $a2
+	mov	$in0,%rsi		# borrow $a3
+	vmovdqu	($ivp),$iv		# load IV
+	sub	\$9,%r14
+
+	mov	$SZ*0(%r15),$A
+	mov	$SZ*1(%r15),$B
+	mov	$SZ*2(%r15),$C
+	mov	$SZ*3(%r15),$D
+	mov	$SZ*4(%r15),$E
+	mov	$SZ*5(%r15),$F
+	mov	$SZ*6(%r15),$G
+	mov	$SZ*7(%r15),$H
+
+	vmovdqa	0x00(%r13,%r14,8),$mask14
+	vmovdqa	0x10(%r13,%r14,8),$mask12
+	vmovdqa	0x20(%r13,%r14,8),$mask10
+	vmovdqu	0x00-0x80($inp),$roundkey
+___
+					if ($SZ==4) {	# SHA256
+    my @X = map("%xmm$_",(0..3));
+    my ($t0,$t1,$t2,$t3) = map("%xmm$_",(4..7));
+
+$code.=<<___;
+	jmp	.Lloop_avx
+.align	16
+.Lloop_avx:
+	vmovdqa	$TABLE+`$SZ*2*$rounds`(%rip),$t3
+	vmovdqu	0x00(%rsi,%r12),@X[0]
+	vmovdqu	0x10(%rsi,%r12),@X[1]
+	vmovdqu	0x20(%rsi,%r12),@X[2]
+	vmovdqu	0x30(%rsi,%r12),@X[3]
+	vpshufb	$t3,@X[0],@X[0]
+	lea	$TABLE(%rip),$Tbl
+	vpshufb	$t3,@X[1],@X[1]
+	vpshufb	$t3,@X[2],@X[2]
+	vpaddd	0x00($Tbl),@X[0],$t0
+	vpshufb	$t3,@X[3],@X[3]
+	vpaddd	0x20($Tbl),@X[1],$t1
+	vpaddd	0x40($Tbl),@X[2],$t2
+	vpaddd	0x60($Tbl),@X[3],$t3
+	vmovdqa	$t0,0x00(%rsp)
+	mov	$A,$a1
+	vmovdqa	$t1,0x10(%rsp)
+	mov	$B,$a3
+	vmovdqa	$t2,0x20(%rsp)
+	xor	$C,$a3			# magic
+	vmovdqa	$t3,0x30(%rsp)
+	mov	$E,$a0
+	jmp	.Lavx_00_47
+
+.align	16
+.Lavx_00_47:
+	sub	\$-16*2*$SZ,$Tbl	# size optimization
+	vmovdqu	(%r12),$inout		# $a4
+	mov	%r12,$_inp		# $a4
+___
+sub Xupdate_256_AVX () {
+	(
+	'&vpalignr	($t0,@X[1],@X[0],$SZ)',	# X[1..4]
+	 '&vpalignr	($t3,@X[3],@X[2],$SZ)',	# X[9..12]
+	'&vpsrld	($t2,$t0,$sigma0[0]);',
+	 '&vpaddd	(@X[0],@X[0],$t3)',	# X[0..3] += X[9..12]
+	'&vpsrld	($t3,$t0,$sigma0[2])',
+	'&vpslld	($t1,$t0,8*$SZ-$sigma0[1]);',
+	'&vpxor		($t0,$t3,$t2)',
+	 '&vpshufd	($t3,@X[3],0b11111010)',# X[14..15]
+	'&vpsrld	($t2,$t2,$sigma0[1]-$sigma0[0]);',
+	'&vpxor		($t0,$t0,$t1)',
+	'&vpslld	($t1,$t1,$sigma0[1]-$sigma0[0]);',
+	'&vpxor		($t0,$t0,$t2)',
+	 '&vpsrld	($t2,$t3,$sigma1[2]);',
+	'&vpxor		($t0,$t0,$t1)',		# sigma0(X[1..4])
+	 '&vpsrlq	($t3,$t3,$sigma1[0]);',
+	'&vpaddd	(@X[0],@X[0],$t0)',	# X[0..3] += sigma0(X[1..4])
+	 '&vpxor	($t2,$t2,$t3);',
+	 '&vpsrlq	($t3,$t3,$sigma1[1]-$sigma1[0])',
+	 '&vpxor	($t2,$t2,$t3)',		# sigma1(X[14..15])
+	 '&vpshufd	($t2,$t2,0b10000100)',
+	 '&vpsrldq	($t2,$t2,8)',
+	'&vpaddd	(@X[0],@X[0],$t2)',	# X[0..1] += sigma1(X[14..15])
+	 '&vpshufd	($t3,@X[0],0b01010000)',# X[16..17]
+	 '&vpsrld	($t2,$t3,$sigma1[2])',
+	 '&vpsrlq	($t3,$t3,$sigma1[0])',
+	 '&vpxor	($t2,$t2,$t3);',
+	 '&vpsrlq	($t3,$t3,$sigma1[1]-$sigma1[0])',
+	 '&vpxor	($t2,$t2,$t3)',
+	 '&vpshufd	($t2,$t2,0b11101000)',
+	 '&vpslldq	($t2,$t2,8)',
+	'&vpaddd	(@X[0],@X[0],$t2)'	# X[2..3] += sigma1(X[16..17])
+	);
+}
+
+sub AVX_256_00_47 () {
+my $j = shift;
+my $body = shift;
+my @X = @_;
+my @insns = (&$body,&$body,&$body,&$body);	# 104 instructions
+
+	foreach (Xupdate_256_AVX()) {		# 29 instructions
+	    eval;
+	    eval(shift(@insns));
+	    eval(shift(@insns));
+	    eval(shift(@insns));
+	}
+	&vpaddd		($t2,@X[0],16*2*$j."($Tbl)");
+	  foreach (@insns) { eval; }		# remaining instructions
+	&vmovdqa	(16*$j."(%rsp)",$t2);
+}
+
+    $aesni_cbc_idx=0;
+    for ($i=0,$j=0; $j<4; $j++) {
+	&AVX_256_00_47($j,\&body_00_15,@X);
+	push(@X,shift(@X));			# rotate(@X)
+    }
+    	&mov		("%r12",$_inp);		# borrow $a4
+	&vpand		($temp,$temp,$mask14);
+	&mov		("%r15",$_out);		# borrow $a2
+	&vpor		($iv,$iv,$temp);
+	&vmovdqu	("(%r15,%r12)",$iv);	# write output
+	&lea		("%r12","16(%r12)");	# inp++
+
+	&cmpb	($SZ-1+16*2*$SZ."($Tbl)",0);
+	&jne	(".Lavx_00_47");
+
+	&vmovdqu	($inout,"(%r12)");
+	&mov		($_inp,"%r12");
+
+    $aesni_cbc_idx=0;
+    for ($i=0; $i<16; ) {
+	foreach(body_00_15()) { eval; }
+    }
+
+					}
+$code.=<<___;
+	mov	$_inp,%r12		# borrow $a4
+	mov	$_out,%r13		# borrow $a0
+	mov	$_ctx,%r15		# borrow $a2
+	mov	$_in0,%rsi		# borrow $a3
+
+	vpand	$mask14,$temp,$temp
+	mov	$a1,$A
+	vpor	$temp,$iv,$iv
+	vmovdqu	$iv,(%r13,%r12)		# write output
+	lea	16(%r12),%r12		# inp++
+
+	add	$SZ*0(%r15),$A
+	add	$SZ*1(%r15),$B
+	add	$SZ*2(%r15),$C
+	add	$SZ*3(%r15),$D
+	add	$SZ*4(%r15),$E
+	add	$SZ*5(%r15),$F
+	add	$SZ*6(%r15),$G
+	add	$SZ*7(%r15),$H
+
+	cmp	$_end,%r12
+
+	mov	$A,$SZ*0(%r15)
+	mov	$B,$SZ*1(%r15)
+	mov	$C,$SZ*2(%r15)
+	mov	$D,$SZ*3(%r15)
+	mov	$E,$SZ*4(%r15)
+	mov	$F,$SZ*5(%r15)
+	mov	$G,$SZ*6(%r15)
+	mov	$H,$SZ*7(%r15)
+	jb	.Lloop_avx
+
+	mov	$_ivp,$ivp
+	mov	$_rsp,%rsi
+	vmovdqu	$iv,($ivp)		# output IV
+	vzeroall
+___
+$code.=<<___ if ($win64);
+	movaps	`$framesz+16*0`(%rsp),%xmm6
+	movaps	`$framesz+16*1`(%rsp),%xmm7
+	movaps	`$framesz+16*2`(%rsp),%xmm8
+	movaps	`$framesz+16*3`(%rsp),%xmm9
+	movaps	`$framesz+16*4`(%rsp),%xmm10
+	movaps	`$framesz+16*5`(%rsp),%xmm11
+	movaps	`$framesz+16*6`(%rsp),%xmm12
+	movaps	`$framesz+16*7`(%rsp),%xmm13
+	movaps	`$framesz+16*8`(%rsp),%xmm14
+	movaps	`$framesz+16*9`(%rsp),%xmm15
+___
+$code.=<<___;
+	mov	(%rsi),%r15
+	mov	8(%rsi),%r14
+	mov	16(%rsi),%r13
+	mov	24(%rsi),%r12
+	mov	32(%rsi),%rbp
+	mov	40(%rsi),%rbx
+	lea	48(%rsi),%rsp
+.Lepilogue_avx:
+	ret
+.size	${func}_avx,.-${func}_avx
+___
+
+if ($avx>1) {{
+######################################################################
+# AVX2+BMI code path
+#
+my $a5=$SZ==4?"%esi":"%rsi";	# zap $inp 
+my $PUSH8=8*2*$SZ;
+use integer;
+
+sub bodyx_00_15 () {
+	# at start $a1 should be zero, $a3 - $b^$c and $a4 copy of $f
+	(
+	'($a,$b,$c,$d,$e,$f,$g,$h)=@ROT;'.
+
+	'&add	($h,(32*($i/(16/$SZ))+$SZ*($i%(16/$SZ)))%$PUSH8.$base)',    # h+=X[i]+K[i]
+	'&and	($a4,$e)',		# f&e
+	'&rorx	($a0,$e,$Sigma1[2])',
+	'&rorx	($a2,$e,$Sigma1[1])',
+
+	'&lea	($a,"($a,$a1)")',	# h+=Sigma0(a) from the past
+	'&lea	($h,"($h,$a4)")',
+	'&andn	($a4,$e,$g)',		# ~e&g
+	'&xor	($a0,$a2)',
+
+	'&rorx	($a1,$e,$Sigma1[0])',
+	'&lea	($h,"($h,$a4)")',	# h+=Ch(e,f,g)=(e&f)+(~e&g)
+	'&xor	($a0,$a1)',		# Sigma1(e)
+	'&mov	($a2,$a)',
+
+	'&rorx	($a4,$a,$Sigma0[2])',
+	'&lea	($h,"($h,$a0)")',	# h+=Sigma1(e)
+	'&xor	($a2,$b)',		# a^b, b^c in next round
+	'&rorx	($a1,$a,$Sigma0[1])',
+
+	'&rorx	($a0,$a,$Sigma0[0])',
+	'&lea	($d,"($d,$h)")',	# d+=h
+	'&and	($a3,$a2)',		# (b^c)&(a^b)
+	@aesni_cbc_block[$aesni_cbc_idx++].
+	'&xor	($a1,$a4)',
+
+	'&xor	($a3,$b)',		# Maj(a,b,c)=Ch(a^b,c,b)
+	'&xor	($a1,$a0)',		# Sigma0(a)
+	'&lea	($h,"($h,$a3)");'.	# h+=Maj(a,b,c)
+	'&mov	($a4,$e)',		# copy of f in future
+
+	'($a2,$a3) = ($a3,$a2); unshift(@ROT,pop(@ROT)); $i++;'
+	);
+	# and at the finish one has to $a+=$a1
+}
+
+$code.=<<___;
+.type	${func}_avx2,\@function,6
+.align	64
+${func}_avx2:
+.Lavx2_shortcut:
+	mov	`($win64?56:8)`(%rsp),$in0	# load 7th parameter
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+	mov	%rsp,%r11		# copy %rsp
+	sub	\$`2*$SZ*$rounds+8*8+$win64*16*10`,%rsp
+	and	\$-256*$SZ,%rsp		# align stack frame
+	add	\$`2*$SZ*($rounds-8)`,%rsp
+
+	shl	\$6,$len
+	sub	$inp,$out		# re-bias
+	sub	$inp,$in0
+	add	$inp,$len		# end of input
+
+	#mov	$inp,$_inp		# saved later
+	#mov	$out,$_out		# kept in $offload
+	mov	$len,$_end
+	#mov	$key,$_key		# remains resident in $inp register
+	mov	$ivp,$_ivp
+	mov	$ctx,$_ctx
+	mov	$in0,$_in0
+	mov	%r11,$_rsp
+___
+$code.=<<___ if ($win64);
+	movaps	%xmm6,`$framesz+16*0`(%rsp)
+	movaps	%xmm7,`$framesz+16*1`(%rsp)
+	movaps	%xmm8,`$framesz+16*2`(%rsp)
+	movaps	%xmm9,`$framesz+16*3`(%rsp)
+	movaps	%xmm10,`$framesz+16*4`(%rsp)
+	movaps	%xmm11,`$framesz+16*5`(%rsp)
+	movaps	%xmm12,`$framesz+16*6`(%rsp)
+	movaps	%xmm13,`$framesz+16*7`(%rsp)
+	movaps	%xmm14,`$framesz+16*8`(%rsp)
+	movaps	%xmm15,`$framesz+16*9`(%rsp)
+___
+$code.=<<___;
+.Lprologue_avx2:
+	vzeroall
+
+	mov	$inp,%r13		# borrow $a0
+	vpinsrq	\$1,$out,$offload,$offload
+	lea	0x80($key),$inp		# size optimization, reassign
+	lea	$TABLE+`$SZ*2*$rounds+32`(%rip),%r12	# borrow $a4
+	mov	0xf0-0x80($inp),%r14d	# rounds, borrow $a1
+	mov	$ctx,%r15		# borrow $a2
+	mov	$in0,%rsi		# borrow $a3
+	vmovdqu	($ivp),$iv		# load IV
+	lea	-9(%r14),%r14
+
+	vmovdqa	0x00(%r12,%r14,8),$mask14
+	vmovdqa	0x10(%r12,%r14,8),$mask12
+	vmovdqa	0x20(%r12,%r14,8),$mask10
+
+	sub	\$-16*$SZ,%r13		# inp++, size optimization
+	mov	$SZ*0(%r15),$A
+	lea	(%rsi,%r13),%r12	# borrow $a0
+	mov	$SZ*1(%r15),$B
+	cmp	$len,%r13		# $_end
+	mov	$SZ*2(%r15),$C
+	cmove	%rsp,%r12		# next block or random data
+	mov	$SZ*3(%r15),$D
+	mov	$SZ*4(%r15),$E
+	mov	$SZ*5(%r15),$F
+	mov	$SZ*6(%r15),$G
+	mov	$SZ*7(%r15),$H
+	vmovdqu	0x00-0x80($inp),$roundkey
+___
+					if ($SZ==4) {	# SHA256
+    my @X = map("%ymm$_",(0..3));
+    my ($t0,$t1,$t2,$t3) = map("%ymm$_",(4..7));
+
+$code.=<<___;
+	jmp	.Loop_avx2
+.align	16
+.Loop_avx2:
+	vmovdqa	$TABLE+`$SZ*2*$rounds`(%rip),$t3
+	vmovdqu	-16*$SZ+0(%rsi,%r13),%xmm0
+	vmovdqu	-16*$SZ+16(%rsi,%r13),%xmm1
+	vmovdqu	-16*$SZ+32(%rsi,%r13),%xmm2
+	vmovdqu	-16*$SZ+48(%rsi,%r13),%xmm3
+
+	vinserti128	\$1,(%r12),@X[0],@X[0]
+	vinserti128	\$1,16(%r12),@X[1],@X[1]
+	 vpshufb	$t3,@X[0],@X[0]
+	vinserti128	\$1,32(%r12),@X[2],@X[2]
+	 vpshufb	$t3,@X[1],@X[1]
+	vinserti128	\$1,48(%r12),@X[3],@X[3]
+
+	lea	$TABLE(%rip),$Tbl
+	vpshufb	$t3,@X[2],@X[2]
+	lea	-16*$SZ(%r13),%r13
+	vpaddd	0x00($Tbl),@X[0],$t0
+	vpshufb	$t3,@X[3],@X[3]
+	vpaddd	0x20($Tbl),@X[1],$t1
+	vpaddd	0x40($Tbl),@X[2],$t2
+	vpaddd	0x60($Tbl),@X[3],$t3
+	vmovdqa	$t0,0x00(%rsp)
+	xor	$a1,$a1
+	vmovdqa	$t1,0x20(%rsp)
+	lea	-$PUSH8(%rsp),%rsp
+	mov	$B,$a3
+	vmovdqa	$t2,0x00(%rsp)
+	xor	$C,$a3			# magic
+	vmovdqa	$t3,0x20(%rsp)
+	mov	$F,$a4
+	sub	\$-16*2*$SZ,$Tbl	# size optimization
+	jmp	.Lavx2_00_47
+
+.align	16
+.Lavx2_00_47:
+	vmovdqu	(%r13),$inout
+	vpinsrq	\$0,%r13,$offload,$offload
+___
+
+sub AVX2_256_00_47 () {
+my $j = shift;
+my $body = shift;
+my @X = @_;
+my @insns = (&$body,&$body,&$body,&$body);	# 96 instructions
+my $base = "+2*$PUSH8(%rsp)";
+
+	&lea	("%rsp","-$PUSH8(%rsp)")	if (($j%2)==0);
+	foreach (Xupdate_256_AVX()) {		# 29 instructions
+	    eval;
+	    eval(shift(@insns));
+	    eval(shift(@insns));
+	    eval(shift(@insns));
+	}
+	&vpaddd		($t2,@X[0],16*2*$j."($Tbl)");
+	  foreach (@insns) { eval; }		# remaining instructions
+	&vmovdqa	((32*$j)%$PUSH8."(%rsp)",$t2);
+}
+    $aesni_cbc_idx=0;
+    for ($i=0,$j=0; $j<4; $j++) {
+	&AVX2_256_00_47($j,\&bodyx_00_15,@X);
+	push(@X,shift(@X));			# rotate(@X)
+    }
+	&vmovq		("%r13",$offload);	# borrow $a0
+	&vpextrq	("%r15",$offload,1);	# borrow $a2
+	&vpand		($temp,$temp,$mask14);
+	&vpor		($iv,$iv,$temp);
+	&vmovdqu	("(%r15,%r13)",$iv);	# write output
+	&lea		("%r13","16(%r13)");	# inp++
+
+	&lea	($Tbl,16*2*$SZ."($Tbl)");
+	&cmpb	(($SZ-1)."($Tbl)",0);
+	&jne	(".Lavx2_00_47");
+
+	&vmovdqu	($inout,"(%r13)");
+	&vpinsrq	($offload,$offload,"%r13",0);
+
+    $aesni_cbc_idx=0;
+    for ($i=0; $i<16; ) {
+	my $base=$i<8?"+$PUSH8(%rsp)":"(%rsp)";
+	foreach(bodyx_00_15()) { eval; }
+    }
+					}
+$code.=<<___;
+	vpextrq	\$1,$offload,%r12		# $_out, borrow $a4
+	vmovq	$offload,%r13			# $_inp, borrow $a0
+	mov	`2*$SZ*$rounds+5*8`(%rsp),%r15	# $_ctx, borrow $a2
+	add	$a1,$A
+	lea	`2*$SZ*($rounds-8)`(%rsp),$Tbl
+
+	vpand	$mask14,$temp,$temp
+	vpor	$temp,$iv,$iv
+	vmovdqu	$iv,(%r12,%r13)			# write output
+	lea	16(%r13),%r13
+
+	add	$SZ*0(%r15),$A
+	add	$SZ*1(%r15),$B
+	add	$SZ*2(%r15),$C
+	add	$SZ*3(%r15),$D
+	add	$SZ*4(%r15),$E
+	add	$SZ*5(%r15),$F
+	add	$SZ*6(%r15),$G
+	add	$SZ*7(%r15),$H
+
+	mov	$A,$SZ*0(%r15)
+	mov	$B,$SZ*1(%r15)
+	mov	$C,$SZ*2(%r15)
+	mov	$D,$SZ*3(%r15)
+	mov	$E,$SZ*4(%r15)
+	mov	$F,$SZ*5(%r15)
+	mov	$G,$SZ*6(%r15)
+	mov	$H,$SZ*7(%r15)
+
+	cmp	`$PUSH8+2*8`($Tbl),%r13		# $_end
+	je	.Ldone_avx2
+
+	xor	$a1,$a1
+	mov	$B,$a3
+	mov	$F,$a4
+	xor	$C,$a3			# magic
+	jmp	.Lower_avx2
+.align	16
+.Lower_avx2:
+	vmovdqu	(%r13),$inout
+	vpinsrq	\$0,%r13,$offload,$offload
+___
+    $aesni_cbc_idx=0;
+    for ($i=0; $i<16; ) {
+	my $base="+16($Tbl)";
+	foreach(bodyx_00_15()) { eval; }
+	&lea	($Tbl,"-$PUSH8($Tbl)")	if ($i==8);
+    }
+$code.=<<___;
+	vmovq	$offload,%r13			# borrow $a0
+	vpextrq	\$1,$offload,%r15		# borrow $a2
+	vpand	$mask14,$temp,$temp
+	vpor	$temp,$iv,$iv
+	lea	-$PUSH8($Tbl),$Tbl
+	vmovdqu	$iv,(%r15,%r13)			# write output
+	lea	16(%r13),%r13			# inp++
+	cmp	%rsp,$Tbl
+	jae	.Lower_avx2
+
+	mov	`2*$SZ*$rounds+5*8`(%rsp),%r15	# $_ctx, borrow $a2
+	lea	16*$SZ(%r13),%r13
+	mov	`2*$SZ*$rounds+6*8`(%rsp),%rsi	# $_in0, borrow $a3
+	add	$a1,$A
+	lea	`2*$SZ*($rounds-8)`(%rsp),%rsp
+
+	add	$SZ*0(%r15),$A
+	add	$SZ*1(%r15),$B
+	add	$SZ*2(%r15),$C
+	add	$SZ*3(%r15),$D
+	add	$SZ*4(%r15),$E
+	add	$SZ*5(%r15),$F
+	add	$SZ*6(%r15),$G
+	lea	(%rsi,%r13),%r12
+	add	$SZ*7(%r15),$H
+
+	cmp	$_end,%r13
+
+	mov	$A,$SZ*0(%r15)
+	cmove	%rsp,%r12		# next block or stale data
+	mov	$B,$SZ*1(%r15)
+	mov	$C,$SZ*2(%r15)
+	mov	$D,$SZ*3(%r15)
+	mov	$E,$SZ*4(%r15)
+	mov	$F,$SZ*5(%r15)
+	mov	$G,$SZ*6(%r15)
+	mov	$H,$SZ*7(%r15)
+
+	jbe	.Loop_avx2
+	lea	(%rsp),$Tbl
+
+.Ldone_avx2:
+	lea	($Tbl),%rsp
+	mov	$_ivp,$ivp
+	mov	$_rsp,%rsi
+	vmovdqu	$iv,($ivp)		# output IV
+	vzeroall
+___
+$code.=<<___ if ($win64);
+	movaps	`$framesz+16*0`(%rsp),%xmm6
+	movaps	`$framesz+16*1`(%rsp),%xmm7
+	movaps	`$framesz+16*2`(%rsp),%xmm8
+	movaps	`$framesz+16*3`(%rsp),%xmm9
+	movaps	`$framesz+16*4`(%rsp),%xmm10
+	movaps	`$framesz+16*5`(%rsp),%xmm11
+	movaps	`$framesz+16*6`(%rsp),%xmm12
+	movaps	`$framesz+16*7`(%rsp),%xmm13
+	movaps	`$framesz+16*8`(%rsp),%xmm14
+	movaps	`$framesz+16*9`(%rsp),%xmm15
+___
+$code.=<<___;
+	mov	(%rsi),%r15
+	mov	8(%rsi),%r14
+	mov	16(%rsi),%r13
+	mov	24(%rsi),%r12
+	mov	32(%rsi),%rbp
+	mov	40(%rsi),%rbx
+	lea	48(%rsi),%rsp
+.Lepilogue_avx2:
+	ret
+.size	${func}_avx2,.-${func}_avx2
+___
+}}
+}}
+{{
+my ($in0,$out,$len,$key,$ivp,$ctx,$inp)=("%rdi","%rsi","%rdx","%rcx","%r8","%r9","%r10");
+
+my ($rounds,$Tbl)=("%r11d","%rbx");
+
+my ($iv,$in,$rndkey0)=map("%xmm$_",(6,14,15));
+my @rndkey=("%xmm4","%xmm5");
+my $r=0;
+my $sn=0;
+
+my ($Wi,$ABEF,$CDGH,$TMP,$BSWAP,$ABEF_SAVE,$CDGH_SAVE)=map("%xmm$_",(0..3,7..9));
+my @MSG=map("%xmm$_",(10..13));
+
+my $aesenc=sub {
+  use integer;
+  my ($n,$k)=($r/10,$r%10);
+    if ($k==0) {
+      $code.=<<___;
+	movups		`16*$n`($in0),$in		# load input
+	xorps		$rndkey0,$in
+___
+      $code.=<<___ if ($n);
+	movups		$iv,`16*($n-1)`($out,$in0)	# write output
+___
+      $code.=<<___;
+	xorps		$in,$iv
+	movups		`32+16*$k-112`($key),$rndkey[1]
+	aesenc		$rndkey[0],$iv
+___
+    } elsif ($k==9) {
+      $sn++;
+      $code.=<<___;
+	cmp		\$11,$rounds
+	jb		.Laesenclast$sn
+	movups		`32+16*($k+0)-112`($key),$rndkey[1]
+	aesenc		$rndkey[0],$iv
+	movups		`32+16*($k+1)-112`($key),$rndkey[0]
+	aesenc		$rndkey[1],$iv
+	je		.Laesenclast$sn
+	movups		`32+16*($k+2)-112`($key),$rndkey[1]
+	aesenc		$rndkey[0],$iv
+	movups		`32+16*($k+3)-112`($key),$rndkey[0]
+	aesenc		$rndkey[1],$iv
+.Laesenclast$sn:
+	aesenclast	$rndkey[0],$iv
+	movups		16-112($key),$rndkey[1]		# forward reference
+	nop
+___
+    } else {
+      $code.=<<___;
+	movups		`32+16*$k-112`($key),$rndkey[1]
+	aesenc		$rndkey[0],$iv
+___
+    }
+    $r++;	unshift(@rndkey,pop(@rndkey));
+};
+
+if ($shaext) {
+my $Tbl="%rax";
+
+$code.=<<___;
+.type	${func}_shaext,\@function,6
+.align	32
+${func}_shaext:
+	mov	`($win64?56:8)`(%rsp),$inp	# load 7th argument
+___
+$code.=<<___ if ($win64);
+	lea	`-8-10*16`(%rsp),%rsp
+	movaps	%xmm6,-8-10*16(%rax)
+	movaps	%xmm7,-8-9*16(%rax)
+	movaps	%xmm8,-8-8*16(%rax)
+	movaps	%xmm9,-8-7*16(%rax)
+	movaps	%xmm10,-8-6*16(%rax)
+	movaps	%xmm11,-8-5*16(%rax)
+	movaps	%xmm12,-8-4*16(%rax)
+	movaps	%xmm13,-8-3*16(%rax)
+	movaps	%xmm14,-8-2*16(%rax)
+	movaps	%xmm15,-8-1*16(%rax)
+.Lprologue_shaext:
+___
+$code.=<<___;
+	lea		K256+0x80(%rip),$Tbl
+	movdqu		($ctx),$ABEF		# DCBA
+	movdqu		16($ctx),$CDGH		# HGFE
+	movdqa		0x200-0x80($Tbl),$TMP	# byte swap mask
+
+	mov		240($key),$rounds
+	sub		$in0,$out
+	movups		($key),$rndkey0		# $key[0]
+	movups		16($key),$rndkey[0]	# forward reference
+	lea		112($key),$key		# size optimization
+
+	pshufd		\$0x1b,$ABEF,$Wi	# ABCD
+	pshufd		\$0xb1,$ABEF,$ABEF	# CDAB
+	pshufd		\$0x1b,$CDGH,$CDGH	# EFGH
+	movdqa		$TMP,$BSWAP		# offload
+	palignr		\$8,$CDGH,$ABEF		# ABEF
+	punpcklqdq	$Wi,$CDGH		# CDGH
+
+	jmp	.Loop_shaext
+
+.align	16
+.Loop_shaext:
+	movdqu		($inp),@MSG[0]
+	movdqu		0x10($inp),@MSG[1]
+	movdqu		0x20($inp),@MSG[2]
+	pshufb		$TMP,@MSG[0]
+	movdqu		0x30($inp),@MSG[3]
+
+	movdqa		0*32-0x80($Tbl),$Wi
+	paddd		@MSG[0],$Wi
+	pshufb		$TMP,@MSG[1]
+	movdqa		$CDGH,$CDGH_SAVE	# offload
+	movdqa		$ABEF,$ABEF_SAVE	# offload
+___
+	&$aesenc();
+$code.=<<___;
+	sha256rnds2	$ABEF,$CDGH		# 0-3
+	pshufd		\$0x0e,$Wi,$Wi
+___
+	&$aesenc();
+$code.=<<___;
+	sha256rnds2	$CDGH,$ABEF
+
+	movdqa		1*32-0x80($Tbl),$Wi
+	paddd		@MSG[1],$Wi
+	pshufb		$TMP,@MSG[2]
+	lea		0x40($inp),$inp
+___
+	&$aesenc();
+$code.=<<___;
+	sha256rnds2	$ABEF,$CDGH		# 4-7
+	pshufd		\$0x0e,$Wi,$Wi
+___
+	&$aesenc();
+$code.=<<___;
+	sha256rnds2	$CDGH,$ABEF
+
+	movdqa		2*32-0x80($Tbl),$Wi
+	paddd		@MSG[2],$Wi
+	pshufb		$TMP,@MSG[3]
+	sha256msg1	@MSG[1],@MSG[0]
+___
+	&$aesenc();
+$code.=<<___;
+	sha256rnds2	$ABEF,$CDGH		# 8-11
+	pshufd		\$0x0e,$Wi,$Wi
+	movdqa		@MSG[3],$TMP
+	palignr		\$4,@MSG[2],$TMP
+	paddd		$TMP,@MSG[0]
+___
+	&$aesenc();
+$code.=<<___;
+	sha256rnds2	$CDGH,$ABEF
+
+	movdqa		3*32-0x80($Tbl),$Wi
+	paddd		@MSG[3],$Wi
+	sha256msg2	@MSG[3],@MSG[0]
+	sha256msg1	@MSG[2],@MSG[1]
+___
+	&$aesenc();
+$code.=<<___;
+	sha256rnds2	$ABEF,$CDGH		# 12-15
+	pshufd		\$0x0e,$Wi,$Wi
+___
+	&$aesenc();
+$code.=<<___;
+	movdqa		@MSG[0],$TMP
+	palignr		\$4,@MSG[3],$TMP
+	paddd		$TMP,@MSG[1]
+	sha256rnds2	$CDGH,$ABEF
+___
+for($i=4;$i<16-3;$i++) {
+	&$aesenc()	if (($r%10)==0);
+$code.=<<___;
+	movdqa		$i*32-0x80($Tbl),$Wi
+	paddd		@MSG[0],$Wi
+	sha256msg2	@MSG[0],@MSG[1]
+	sha256msg1	@MSG[3],@MSG[2]
+___
+	&$aesenc();
+$code.=<<___;
+	sha256rnds2	$ABEF,$CDGH		# 16-19...
+	pshufd		\$0x0e,$Wi,$Wi
+	movdqa		@MSG[1],$TMP
+	palignr		\$4,@MSG[0],$TMP
+	paddd		$TMP,@MSG[2]
+___
+	&$aesenc();
+	&$aesenc()	if ($r==19);
+$code.=<<___;
+	sha256rnds2	$CDGH,$ABEF
+___
+	push(@MSG,shift(@MSG));
+}
+$code.=<<___;
+	movdqa		13*32-0x80($Tbl),$Wi
+	paddd		@MSG[0],$Wi
+	sha256msg2	@MSG[0],@MSG[1]
+	sha256msg1	@MSG[3],@MSG[2]
+___
+	&$aesenc();
+$code.=<<___;
+	sha256rnds2	$ABEF,$CDGH		# 52-55
+	pshufd		\$0x0e,$Wi,$Wi
+	movdqa		@MSG[1],$TMP
+	palignr		\$4,@MSG[0],$TMP
+	paddd		$TMP,@MSG[2]
+___
+	&$aesenc();
+	&$aesenc();
+$code.=<<___;
+	sha256rnds2	$CDGH,$ABEF
+
+	movdqa		14*32-0x80($Tbl),$Wi
+	paddd		@MSG[1],$Wi
+	sha256msg2	@MSG[1],@MSG[2]
+	movdqa		$BSWAP,$TMP
+___
+	&$aesenc();
+$code.=<<___;
+	sha256rnds2	$ABEF,$CDGH		# 56-59
+	pshufd		\$0x0e,$Wi,$Wi
+___
+	&$aesenc();
+$code.=<<___;
+	sha256rnds2	$CDGH,$ABEF
+
+	movdqa		15*32-0x80($Tbl),$Wi
+	paddd		@MSG[2],$Wi
+___
+	&$aesenc();
+	&$aesenc();
+$code.=<<___;
+	sha256rnds2	$ABEF,$CDGH		# 60-63
+	pshufd		\$0x0e,$Wi,$Wi
+___
+	&$aesenc();
+$code.=<<___;
+	sha256rnds2	$CDGH,$ABEF
+	#pxor		$CDGH,$rndkey0		# black magic
+___
+	while ($r<40)	{ &$aesenc(); }		# remaining aesenc's
+$code.=<<___;
+	#xorps		$CDGH,$rndkey0		# black magic
+	paddd		$CDGH_SAVE,$CDGH
+	paddd		$ABEF_SAVE,$ABEF
+
+	dec		$len
+	movups		$iv,48($out,$in0)	# write output
+	lea		64($in0),$in0
+	jnz		.Loop_shaext
+
+	pshufd		\$0xb1,$CDGH,$CDGH	# DCHG
+	pshufd		\$0x1b,$ABEF,$TMP	# FEBA
+	pshufd		\$0xb1,$ABEF,$ABEF	# BAFE
+	punpckhqdq	$CDGH,$ABEF		# DCBA
+	palignr		\$8,$TMP,$CDGH		# HGFE
+
+	movups		$iv,($ivp)		# write IV
+	movdqu		$ABEF,($ctx)
+	movdqu		$CDGH,16($ctx)
+___
+$code.=<<___ if ($win64);
+	movaps	0*16(%rsp),%xmm6
+	movaps	1*16(%rsp),%xmm7
+	movaps	2*16(%rsp),%xmm8
+	movaps	3*16(%rsp),%xmm9
+	movaps	4*16(%rsp),%xmm10
+	movaps	5*16(%rsp),%xmm11
+	movaps	6*16(%rsp),%xmm12
+	movaps	7*16(%rsp),%xmm13
+	movaps	8*16(%rsp),%xmm14
+	movaps	9*16(%rsp),%xmm15
+	lea	8+10*16(%rsp),%rsp
+.Lepilogue_shaext:
+___
+$code.=<<___;
+	ret
+.size	${func}_shaext,.-${func}_shaext
+___
+}
+}}}}}
+
+# EXCEPTION_DISPOSITION handler (EXCEPTION_RECORD *rec,ULONG64 frame,
+#		CONTEXT *context,DISPATCHER_CONTEXT *disp)
+if ($win64 && $avx) {
+$rec="%rcx";
+$frame="%rdx";
+$context="%r8";
+$disp="%r9";
+
+$code.=<<___;
+.extern	__imp_RtlVirtualUnwind
+.type	se_handler,\@abi-omnipotent
+.align	16
+se_handler:
+	push	%rsi
+	push	%rdi
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+	pushfq
+	sub	\$64,%rsp
+
+	mov	120($context),%rax	# pull context->Rax
+	mov	248($context),%rbx	# pull context->Rip
+
+	mov	8($disp),%rsi		# disp->ImageBase
+	mov	56($disp),%r11		# disp->HanderlData
+
+	mov	0(%r11),%r10d		# HandlerData[0]
+	lea	(%rsi,%r10),%r10	# prologue label
+	cmp	%r10,%rbx		# context->RipRsp
+
+	mov	4(%r11),%r10d		# HandlerData[1]
+	lea	(%rsi,%r10),%r10	# epilogue label
+	cmp	%r10,%rbx		# context->Rip>=epilogue label
+	jae	.Lin_prologue
+___
+$code.=<<___ if ($shaext);
+	lea	aesni_cbc_sha256_enc_shaext(%rip),%r10
+	cmp	%r10,%rbx
+	jb	.Lnot_in_shaext
+
+	lea	(%rax),%rsi
+	lea	512($context),%rdi	# &context.Xmm6
+	mov	\$20,%ecx
+	.long	0xa548f3fc		# cld; rep movsq
+	lea	168(%rax),%rax		# adjust stack pointer
+	jmp	.Lin_prologue
+.Lnot_in_shaext:
+___
+$code.=<<___ if ($avx>1);
+	lea	.Lavx2_shortcut(%rip),%r10
+	cmp	%r10,%rbx		# context->RipRbx
+	mov	%rbp,160($context)	# restore context->Rbp
+	mov	%r12,216($context)	# restore context->R12
+	mov	%r13,224($context)	# restore context->R13
+	mov	%r14,232($context)	# restore context->R14
+	mov	%r15,240($context)	# restore context->R15
+
+	lea	16*$SZ+8*8(%rsi),%rsi	# Xmm6- save area
+	lea	512($context),%rdi	# &context.Xmm6
+	mov	\$20,%ecx
+	.long	0xa548f3fc		# cld; rep movsq
+
+.Lin_prologue:
+	mov	8(%rax),%rdi
+	mov	16(%rax),%rsi
+	mov	%rax,152($context)	# restore context->Rsp
+	mov	%rsi,168($context)	# restore context->Rsi
+	mov	%rdi,176($context)	# restore context->Rdi
+
+	mov	40($disp),%rdi		# disp->ContextRecord
+	mov	$context,%rsi		# context
+	mov	\$154,%ecx		# sizeof(CONTEXT)
+	.long	0xa548f3fc		# cld; rep movsq
+
+	mov	$disp,%rsi
+	xor	%rcx,%rcx		# arg1, UNW_FLAG_NHANDLER
+	mov	8(%rsi),%rdx		# arg2, disp->ImageBase
+	mov	0(%rsi),%r8		# arg3, disp->ControlPc
+	mov	16(%rsi),%r9		# arg4, disp->FunctionEntry
+	mov	40(%rsi),%r10		# disp->ContextRecord
+	lea	56(%rsi),%r11		# &disp->HandlerData
+	lea	24(%rsi),%r12		# &disp->EstablisherFrame
+	mov	%r10,32(%rsp)		# arg5
+	mov	%r11,40(%rsp)		# arg6
+	mov	%r12,48(%rsp)		# arg7
+	mov	%rcx,56(%rsp)		# arg8, (NULL)
+	call	*__imp_RtlVirtualUnwind(%rip)
+
+	mov	\$1,%eax		# ExceptionContinueSearch
+	add	\$64,%rsp
+	popfq
+	pop	%r15
+	pop	%r14
+	pop	%r13
+	pop	%r12
+	pop	%rbp
+	pop	%rbx
+	pop	%rdi
+	pop	%rsi
+	ret
+.size	se_handler,.-se_handler
+
+.section	.pdata
+	.rva	.LSEH_begin_${func}_xop
+	.rva	.LSEH_end_${func}_xop
+	.rva	.LSEH_info_${func}_xop
+
+	.rva	.LSEH_begin_${func}_avx
+	.rva	.LSEH_end_${func}_avx
+	.rva	.LSEH_info_${func}_avx
+___
+$code.=<<___ if ($avx>1);
+	.rva	.LSEH_begin_${func}_avx2
+	.rva	.LSEH_end_${func}_avx2
+	.rva	.LSEH_info_${func}_avx2
+___
+$code.=<<___ if ($shaext);
+	.rva	.LSEH_begin_${func}_shaext
+	.rva	.LSEH_end_${func}_shaext
+	.rva	.LSEH_info_${func}_shaext
+___
+$code.=<<___;
+.section	.xdata
+.align	8
+.LSEH_info_${func}_xop:
+	.byte	9,0,0,0
+	.rva	se_handler
+	.rva	.Lprologue_xop,.Lepilogue_xop		# HandlerData[]
+
+.LSEH_info_${func}_avx:
+	.byte	9,0,0,0
+	.rva	se_handler
+	.rva	.Lprologue_avx,.Lepilogue_avx		# HandlerData[]
+___
+$code.=<<___ if ($avx>1);
+.LSEH_info_${func}_avx2:
+	.byte	9,0,0,0
+	.rva	se_handler
+	.rva	.Lprologue_avx2,.Lepilogue_avx2		# HandlerData[]
+___
+$code.=<<___ if ($shaext);
+.LSEH_info_${func}_shaext:
+	.byte	9,0,0,0
+	.rva	se_handler
+	.rva	.Lprologue_shaext,.Lepilogue_shaext	# HandlerData[]
+___
+}
+
+####################################################################
+sub rex {
+  local *opcode=shift;
+  my ($dst,$src)=@_;
+  my $rex=0;
+
+    $rex|=0x04			if($dst>=8);
+    $rex|=0x01			if($src>=8);
+    unshift @opcode,$rex|0x40	if($rex);
+}
+
+{
+  my %opcodelet = (
+		"sha256rnds2" => 0xcb,
+  		"sha256msg1"  => 0xcc,
+		"sha256msg2"  => 0xcd	);
+
+  sub sha256op38 {
+    my $instr = shift;
+
+    if (defined($opcodelet{$instr}) && @_[0] =~ /%xmm([0-9]+),\s*%xmm([0-9]+)/) {
+      my @opcode=(0x0f,0x38);
+	rex(\@opcode,$2,$1);
+	push @opcode,$opcodelet{$instr};
+	push @opcode,0xc0|($1&7)|(($2&7)<<3);		# ModR/M
+	return ".byte\t".join(',',@opcode);
+    } else {
+	return $instr."\t".@_[0];
+    }
+  }
+}
+
+$code =~ s/\`([^\`]*)\`/eval $1/gem;
+$code =~ s/\b(sha256[^\s]*)\s+(.*)/sha256op38($1,$2)/gem;
+print $code;
+close STDOUT;
diff --git a/crypto/aes/asm/aesni-x86.pl b/crypto/aes/asm/aesni-x86.pl
new file mode 100644
index 000000000..ed1a47c30
--- /dev/null
+++ b/crypto/aes/asm/aesni-x86.pl
@@ -0,0 +1,3413 @@
+#! /usr/bin/env perl
+# Copyright 2009-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+
+# ====================================================================
+# Written by Andy Polyakov  for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+#
+# This module implements support for Intel AES-NI extension. In
+# OpenSSL context it's used with Intel engine, but can also be used as
+# drop-in replacement for crypto/aes/asm/aes-586.pl [see below for
+# details].
+#
+# Performance.
+#
+# To start with see corresponding paragraph in aesni-x86_64.pl...
+# Instead of filling table similar to one found there I've chosen to
+# summarize *comparison* results for raw ECB, CTR and CBC benchmarks.
+# The simplified table below represents 32-bit performance relative
+# to 64-bit one in every given point. Ratios vary for different
+# encryption modes, therefore interval values.
+#
+#	16-byte     64-byte     256-byte    1-KB        8-KB
+#	53-67%      67-84%      91-94%      95-98%      97-99.5%
+#
+# Lower ratios for smaller block sizes are perfectly understandable,
+# because function call overhead is higher in 32-bit mode. Largest
+# 8-KB block performance is virtually same: 32-bit code is less than
+# 1% slower for ECB, CBC and CCM, and ~3% slower otherwise.
+
+# January 2011
+#
+# See aesni-x86_64.pl for details. Unlike x86_64 version this module
+# interleaves at most 6 aes[enc|dec] instructions, because there are
+# not enough registers for 8x interleave [which should be optimal for
+# Sandy Bridge]. Actually, performance results for 6x interleave
+# factor presented in aesni-x86_64.pl (except for CTR) are for this
+# module.
+
+# April 2011
+#
+# Add aesni_xts_[en|de]crypt. Westmere spends 1.50 cycles processing
+# one byte out of 8KB with 128-bit key, Sandy Bridge - 1.09.
+
+# November 2015
+#
+# Add aesni_ocb_[en|de]crypt.
+
+######################################################################
+# Current large-block performance in cycles per byte processed with
+# 128-bit key (less is better).
+#
+#		CBC en-/decrypt	CTR	XTS	ECB	OCB
+# Westmere	3.77/1.37	1.37	1.52	1.27
+# * Bridge	5.07/0.98	0.99	1.09	0.91	1.10
+# Haswell	4.44/0.80	0.97	1.03	0.72	0.76
+# Silvermont	5.77/3.56	3.67	4.03	3.46	4.03
+# Bulldozer	5.80/0.98	1.05	1.24	0.93	1.23
+
+$PREFIX="aesni";	# if $PREFIX is set to "AES", the script
+			# generates drop-in replacement for
+			# crypto/aes/asm/aes-586.pl:-)
+$inline=1;		# inline _aesni_[en|de]crypt
+
+$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+push(@INC,"${dir}","${dir}../../perlasm");
+require "x86asm.pl";
+
+$output = pop;
+open OUT,">$output";
+*STDOUT=*OUT;
+
+&asm_init($ARGV[0],$0);
+
+&external_label("OPENSSL_ia32cap_P");
+&static_label("key_const");
+
+if ($PREFIX eq "aesni")	{ $movekey=\&movups; }
+else			{ $movekey=\&movups; }
+
+$len="eax";
+$rounds="ecx";
+$key="edx";
+$inp="esi";
+$out="edi";
+$rounds_="ebx";	# backup copy for $rounds
+$key_="ebp";	# backup copy for $key
+
+$rndkey0="xmm0";
+$rndkey1="xmm1";
+$inout0="xmm2";
+$inout1="xmm3";
+$inout2="xmm4";
+$inout3="xmm5";	$in1="xmm5";
+$inout4="xmm6";	$in0="xmm6";
+$inout5="xmm7";	$ivec="xmm7";
+
+# AESNI extension
+sub aeskeygenassist
+{ my($dst,$src,$imm)=@_;
+    if ("$dst:$src" =~ /xmm([0-7]):xmm([0-7])/)
+    {	&data_byte(0x66,0x0f,0x3a,0xdf,0xc0|($1<<3)|$2,$imm);	}
+}
+sub aescommon
+{ my($opcodelet,$dst,$src)=@_;
+    if ("$dst:$src" =~ /xmm([0-7]):xmm([0-7])/)
+    {	&data_byte(0x66,0x0f,0x38,$opcodelet,0xc0|($1<<3)|$2);}
+}
+sub aesimc	{ aescommon(0xdb,@_); }
+sub aesenc	{ aescommon(0xdc,@_); }
+sub aesenclast	{ aescommon(0xdd,@_); }
+sub aesdec	{ aescommon(0xde,@_); }
+sub aesdeclast	{ aescommon(0xdf,@_); }
+
+# Inline version of internal aesni_[en|de]crypt1
+{ my $sn;
+sub aesni_inline_generate1
+{ my ($p,$inout,$ivec)=@_; $inout=$inout0 if (!defined($inout));
+  $sn++;
+
+    &$movekey		($rndkey0,&QWP(0,$key));
+    &$movekey		($rndkey1,&QWP(16,$key));
+    &xorps		($ivec,$rndkey0)	if (defined($ivec));
+    &lea		($key,&DWP(32,$key));
+    &xorps		($inout,$ivec)		if (defined($ivec));
+    &xorps		($inout,$rndkey0)	if (!defined($ivec));
+    &set_label("${p}1_loop_$sn");
+	eval"&aes${p}	($inout,$rndkey1)";
+	&dec		($rounds);
+	&$movekey	($rndkey1,&QWP(0,$key));
+	&lea		($key,&DWP(16,$key));
+    &jnz		(&label("${p}1_loop_$sn"));
+    eval"&aes${p}last	($inout,$rndkey1)";
+}}
+
+sub aesni_generate1	# fully unrolled loop
+{ my ($p,$inout)=@_; $inout=$inout0 if (!defined($inout));
+
+    &function_begin_B("_aesni_${p}rypt1");
+	&movups		($rndkey0,&QWP(0,$key));
+	&$movekey	($rndkey1,&QWP(0x10,$key));
+	&xorps		($inout,$rndkey0);
+	&$movekey	($rndkey0,&QWP(0x20,$key));
+	&lea		($key,&DWP(0x30,$key));
+	&cmp		($rounds,11);
+	&jb		(&label("${p}128"));
+	&lea		($key,&DWP(0x20,$key));
+	&je		(&label("${p}192"));
+	&lea		($key,&DWP(0x20,$key));
+	eval"&aes${p}	($inout,$rndkey1)";
+	&$movekey	($rndkey1,&QWP(-0x40,$key));
+	eval"&aes${p}	($inout,$rndkey0)";
+	&$movekey	($rndkey0,&QWP(-0x30,$key));
+    &set_label("${p}192");
+	eval"&aes${p}	($inout,$rndkey1)";
+	&$movekey	($rndkey1,&QWP(-0x20,$key));
+	eval"&aes${p}	($inout,$rndkey0)";
+	&$movekey	($rndkey0,&QWP(-0x10,$key));
+    &set_label("${p}128");
+	eval"&aes${p}	($inout,$rndkey1)";
+	&$movekey	($rndkey1,&QWP(0,$key));
+	eval"&aes${p}	($inout,$rndkey0)";
+	&$movekey	($rndkey0,&QWP(0x10,$key));
+	eval"&aes${p}	($inout,$rndkey1)";
+	&$movekey	($rndkey1,&QWP(0x20,$key));
+	eval"&aes${p}	($inout,$rndkey0)";
+	&$movekey	($rndkey0,&QWP(0x30,$key));
+	eval"&aes${p}	($inout,$rndkey1)";
+	&$movekey	($rndkey1,&QWP(0x40,$key));
+	eval"&aes${p}	($inout,$rndkey0)";
+	&$movekey	($rndkey0,&QWP(0x50,$key));
+	eval"&aes${p}	($inout,$rndkey1)";
+	&$movekey	($rndkey1,&QWP(0x60,$key));
+	eval"&aes${p}	($inout,$rndkey0)";
+	&$movekey	($rndkey0,&QWP(0x70,$key));
+	eval"&aes${p}	($inout,$rndkey1)";
+    eval"&aes${p}last	($inout,$rndkey0)";
+    &ret();
+    &function_end_B("_aesni_${p}rypt1");
+}
+
+# void $PREFIX_encrypt (const void *inp,void *out,const AES_KEY *key);
+&aesni_generate1("enc") if (!$inline);
+&function_begin_B("${PREFIX}_encrypt");
+	&mov	("eax",&wparam(0));
+	&mov	($key,&wparam(2));
+	&movups	($inout0,&QWP(0,"eax"));
+	&mov	($rounds,&DWP(240,$key));
+	&mov	("eax",&wparam(1));
+	if ($inline)
+	{   &aesni_inline_generate1("enc");	}
+	else
+	{   &call	("_aesni_encrypt1");	}
+	&pxor	($rndkey0,$rndkey0);		# clear register bank
+	&pxor	($rndkey1,$rndkey1);
+	&movups	(&QWP(0,"eax"),$inout0);
+	&pxor	($inout0,$inout0);
+	&ret	();
+&function_end_B("${PREFIX}_encrypt");
+
+# void $PREFIX_decrypt (const void *inp,void *out,const AES_KEY *key);
+&aesni_generate1("dec") if(!$inline);
+&function_begin_B("${PREFIX}_decrypt");
+	&mov	("eax",&wparam(0));
+	&mov	($key,&wparam(2));
+	&movups	($inout0,&QWP(0,"eax"));
+	&mov	($rounds,&DWP(240,$key));
+	&mov	("eax",&wparam(1));
+	if ($inline)
+	{   &aesni_inline_generate1("dec");	}
+	else
+	{   &call	("_aesni_decrypt1");	}
+	&pxor	($rndkey0,$rndkey0);		# clear register bank
+	&pxor	($rndkey1,$rndkey1);
+	&movups	(&QWP(0,"eax"),$inout0);
+	&pxor	($inout0,$inout0);
+	&ret	();
+&function_end_B("${PREFIX}_decrypt");
+
+# _aesni_[en|de]cryptN are private interfaces, N denotes interleave
+# factor. Why 3x subroutine were originally used in loops? Even though
+# aes[enc|dec] latency was originally 6, it could be scheduled only
+# every *2nd* cycle. Thus 3x interleave was the one providing optimal
+# utilization, i.e. when subroutine's throughput is virtually same as
+# of non-interleaved subroutine [for number of input blocks up to 3].
+# This is why it originally made no sense to implement 2x subroutine.
+# But times change and it became appropriate to spend extra 192 bytes
+# on 2x subroutine on Atom Silvermont account. For processors that
+# can schedule aes[enc|dec] every cycle optimal interleave factor
+# equals to corresponding instructions latency. 8x is optimal for
+# * Bridge, but it's unfeasible to accommodate such implementation
+# in XMM registers addreassable in 32-bit mode and therefore maximum
+# of 6x is used instead...
+
+sub aesni_generate2
+{ my $p=shift;
+
+    &function_begin_B("_aesni_${p}rypt2");
+	&$movekey	($rndkey0,&QWP(0,$key));
+	&shl		($rounds,4);
+	&$movekey	($rndkey1,&QWP(16,$key));
+	&xorps		($inout0,$rndkey0);
+	&pxor		($inout1,$rndkey0);
+	&$movekey	($rndkey0,&QWP(32,$key));
+	&lea		($key,&DWP(32,$key,$rounds));
+	&neg		($rounds);
+	&add		($rounds,16);
+
+    &set_label("${p}2_loop");
+	eval"&aes${p}	($inout0,$rndkey1)";
+	eval"&aes${p}	($inout1,$rndkey1)";
+	&$movekey	($rndkey1,&QWP(0,$key,$rounds));
+	&add		($rounds,32);
+	eval"&aes${p}	($inout0,$rndkey0)";
+	eval"&aes${p}	($inout1,$rndkey0)";
+	&$movekey	($rndkey0,&QWP(-16,$key,$rounds));
+	&jnz		(&label("${p}2_loop"));
+    eval"&aes${p}	($inout0,$rndkey1)";
+    eval"&aes${p}	($inout1,$rndkey1)";
+    eval"&aes${p}last	($inout0,$rndkey0)";
+    eval"&aes${p}last	($inout1,$rndkey0)";
+    &ret();
+    &function_end_B("_aesni_${p}rypt2");
+}
+
+sub aesni_generate3
+{ my $p=shift;
+
+    &function_begin_B("_aesni_${p}rypt3");
+	&$movekey	($rndkey0,&QWP(0,$key));
+	&shl		($rounds,4);
+	&$movekey	($rndkey1,&QWP(16,$key));
+	&xorps		($inout0,$rndkey0);
+	&pxor		($inout1,$rndkey0);
+	&pxor		($inout2,$rndkey0);
+	&$movekey	($rndkey0,&QWP(32,$key));
+	&lea		($key,&DWP(32,$key,$rounds));
+	&neg		($rounds);
+	&add		($rounds,16);
+
+    &set_label("${p}3_loop");
+	eval"&aes${p}	($inout0,$rndkey1)";
+	eval"&aes${p}	($inout1,$rndkey1)";
+	eval"&aes${p}	($inout2,$rndkey1)";
+	&$movekey	($rndkey1,&QWP(0,$key,$rounds));
+	&add		($rounds,32);
+	eval"&aes${p}	($inout0,$rndkey0)";
+	eval"&aes${p}	($inout1,$rndkey0)";
+	eval"&aes${p}	($inout2,$rndkey0)";
+	&$movekey	($rndkey0,&QWP(-16,$key,$rounds));
+	&jnz		(&label("${p}3_loop"));
+    eval"&aes${p}	($inout0,$rndkey1)";
+    eval"&aes${p}	($inout1,$rndkey1)";
+    eval"&aes${p}	($inout2,$rndkey1)";
+    eval"&aes${p}last	($inout0,$rndkey0)";
+    eval"&aes${p}last	($inout1,$rndkey0)";
+    eval"&aes${p}last	($inout2,$rndkey0)";
+    &ret();
+    &function_end_B("_aesni_${p}rypt3");
+}
+
+# 4x interleave is implemented to improve small block performance,
+# most notably [and naturally] 4 block by ~30%. One can argue that one
+# should have implemented 5x as well, but improvement  would be <20%,
+# so it's not worth it...
+sub aesni_generate4
+{ my $p=shift;
+
+    &function_begin_B("_aesni_${p}rypt4");
+	&$movekey	($rndkey0,&QWP(0,$key));
+	&$movekey	($rndkey1,&QWP(16,$key));
+	&shl		($rounds,4);
+	&xorps		($inout0,$rndkey0);
+	&pxor		($inout1,$rndkey0);
+	&pxor		($inout2,$rndkey0);
+	&pxor		($inout3,$rndkey0);
+	&$movekey	($rndkey0,&QWP(32,$key));
+	&lea		($key,&DWP(32,$key,$rounds));
+	&neg		($rounds);
+	&data_byte	(0x0f,0x1f,0x40,0x00);
+	&add		($rounds,16);
+
+    &set_label("${p}4_loop");
+	eval"&aes${p}	($inout0,$rndkey1)";
+	eval"&aes${p}	($inout1,$rndkey1)";
+	eval"&aes${p}	($inout2,$rndkey1)";
+	eval"&aes${p}	($inout3,$rndkey1)";
+	&$movekey	($rndkey1,&QWP(0,$key,$rounds));
+	&add		($rounds,32);
+	eval"&aes${p}	($inout0,$rndkey0)";
+	eval"&aes${p}	($inout1,$rndkey0)";
+	eval"&aes${p}	($inout2,$rndkey0)";
+	eval"&aes${p}	($inout3,$rndkey0)";
+	&$movekey	($rndkey0,&QWP(-16,$key,$rounds));
+    &jnz		(&label("${p}4_loop"));
+
+    eval"&aes${p}	($inout0,$rndkey1)";
+    eval"&aes${p}	($inout1,$rndkey1)";
+    eval"&aes${p}	($inout2,$rndkey1)";
+    eval"&aes${p}	($inout3,$rndkey1)";
+    eval"&aes${p}last	($inout0,$rndkey0)";
+    eval"&aes${p}last	($inout1,$rndkey0)";
+    eval"&aes${p}last	($inout2,$rndkey0)";
+    eval"&aes${p}last	($inout3,$rndkey0)";
+    &ret();
+    &function_end_B("_aesni_${p}rypt4");
+}
+
+sub aesni_generate6
+{ my $p=shift;
+
+    &function_begin_B("_aesni_${p}rypt6");
+    &static_label("_aesni_${p}rypt6_enter");
+	&$movekey	($rndkey0,&QWP(0,$key));
+	&shl		($rounds,4);
+	&$movekey	($rndkey1,&QWP(16,$key));
+	&xorps		($inout0,$rndkey0);
+	&pxor		($inout1,$rndkey0);	# pxor does better here
+	&pxor		($inout2,$rndkey0);
+	eval"&aes${p}	($inout0,$rndkey1)";
+	&pxor		($inout3,$rndkey0);
+	&pxor		($inout4,$rndkey0);
+	eval"&aes${p}	($inout1,$rndkey1)";
+	&lea		($key,&DWP(32,$key,$rounds));
+	&neg		($rounds);
+	eval"&aes${p}	($inout2,$rndkey1)";
+	&pxor		($inout5,$rndkey0);
+	&$movekey	($rndkey0,&QWP(0,$key,$rounds));
+	&add		($rounds,16);
+	&jmp		(&label("_aesni_${p}rypt6_inner"));
+
+    &set_label("${p}6_loop",16);
+	eval"&aes${p}	($inout0,$rndkey1)";
+	eval"&aes${p}	($inout1,$rndkey1)";
+	eval"&aes${p}	($inout2,$rndkey1)";
+    &set_label("_aesni_${p}rypt6_inner");
+	eval"&aes${p}	($inout3,$rndkey1)";
+	eval"&aes${p}	($inout4,$rndkey1)";
+	eval"&aes${p}	($inout5,$rndkey1)";
+    &set_label("_aesni_${p}rypt6_enter");
+	&$movekey	($rndkey1,&QWP(0,$key,$rounds));
+	&add		($rounds,32);
+	eval"&aes${p}	($inout0,$rndkey0)";
+	eval"&aes${p}	($inout1,$rndkey0)";
+	eval"&aes${p}	($inout2,$rndkey0)";
+	eval"&aes${p}	($inout3,$rndkey0)";
+	eval"&aes${p}	($inout4,$rndkey0)";
+	eval"&aes${p}	($inout5,$rndkey0)";
+	&$movekey	($rndkey0,&QWP(-16,$key,$rounds));
+    &jnz		(&label("${p}6_loop"));
+
+    eval"&aes${p}	($inout0,$rndkey1)";
+    eval"&aes${p}	($inout1,$rndkey1)";
+    eval"&aes${p}	($inout2,$rndkey1)";
+    eval"&aes${p}	($inout3,$rndkey1)";
+    eval"&aes${p}	($inout4,$rndkey1)";
+    eval"&aes${p}	($inout5,$rndkey1)";
+    eval"&aes${p}last	($inout0,$rndkey0)";
+    eval"&aes${p}last	($inout1,$rndkey0)";
+    eval"&aes${p}last	($inout2,$rndkey0)";
+    eval"&aes${p}last	($inout3,$rndkey0)";
+    eval"&aes${p}last	($inout4,$rndkey0)";
+    eval"&aes${p}last	($inout5,$rndkey0)";
+    &ret();
+    &function_end_B("_aesni_${p}rypt6");
+}
+&aesni_generate2("enc") if ($PREFIX eq "aesni");
+&aesni_generate2("dec");
+&aesni_generate3("enc") if ($PREFIX eq "aesni");
+&aesni_generate3("dec");
+&aesni_generate4("enc") if ($PREFIX eq "aesni");
+&aesni_generate4("dec");
+&aesni_generate6("enc") if ($PREFIX eq "aesni");
+&aesni_generate6("dec");
+
+if ($PREFIX eq "aesni") {
+######################################################################
+# void aesni_ecb_encrypt (const void *in, void *out,
+#                         size_t length, const AES_KEY *key,
+#                         int enc);
+&function_begin("aesni_ecb_encrypt");
+	&mov	($inp,&wparam(0));
+	&mov	($out,&wparam(1));
+	&mov	($len,&wparam(2));
+	&mov	($key,&wparam(3));
+	&mov	($rounds_,&wparam(4));
+	&and	($len,-16);
+	&jz	(&label("ecb_ret"));
+	&mov	($rounds,&DWP(240,$key));
+	&test	($rounds_,$rounds_);
+	&jz	(&label("ecb_decrypt"));
+
+	&mov	($key_,$key);		# backup $key
+	&mov	($rounds_,$rounds);	# backup $rounds
+	&cmp	($len,0x60);
+	&jb	(&label("ecb_enc_tail"));
+
+	&movdqu	($inout0,&QWP(0,$inp));
+	&movdqu	($inout1,&QWP(0x10,$inp));
+	&movdqu	($inout2,&QWP(0x20,$inp));
+	&movdqu	($inout3,&QWP(0x30,$inp));
+	&movdqu	($inout4,&QWP(0x40,$inp));
+	&movdqu	($inout5,&QWP(0x50,$inp));
+	&lea	($inp,&DWP(0x60,$inp));
+	&sub	($len,0x60);
+	&jmp	(&label("ecb_enc_loop6_enter"));
+
+&set_label("ecb_enc_loop6",16);
+	&movups	(&QWP(0,$out),$inout0);
+	&movdqu	($inout0,&QWP(0,$inp));
+	&movups	(&QWP(0x10,$out),$inout1);
+	&movdqu	($inout1,&QWP(0x10,$inp));
+	&movups	(&QWP(0x20,$out),$inout2);
+	&movdqu	($inout2,&QWP(0x20,$inp));
+	&movups	(&QWP(0x30,$out),$inout3);
+	&movdqu	($inout3,&QWP(0x30,$inp));
+	&movups	(&QWP(0x40,$out),$inout4);
+	&movdqu	($inout4,&QWP(0x40,$inp));
+	&movups	(&QWP(0x50,$out),$inout5);
+	&lea	($out,&DWP(0x60,$out));
+	&movdqu	($inout5,&QWP(0x50,$inp));
+	&lea	($inp,&DWP(0x60,$inp));
+&set_label("ecb_enc_loop6_enter");
+
+	&call	("_aesni_encrypt6");
+
+	&mov	($key,$key_);		# restore $key
+	&mov	($rounds,$rounds_);	# restore $rounds
+	&sub	($len,0x60);
+	&jnc	(&label("ecb_enc_loop6"));
+
+	&movups	(&QWP(0,$out),$inout0);
+	&movups	(&QWP(0x10,$out),$inout1);
+	&movups	(&QWP(0x20,$out),$inout2);
+	&movups	(&QWP(0x30,$out),$inout3);
+	&movups	(&QWP(0x40,$out),$inout4);
+	&movups	(&QWP(0x50,$out),$inout5);
+	&lea	($out,&DWP(0x60,$out));
+	&add	($len,0x60);
+	&jz	(&label("ecb_ret"));
+
+&set_label("ecb_enc_tail");
+	&movups	($inout0,&QWP(0,$inp));
+	&cmp	($len,0x20);
+	&jb	(&label("ecb_enc_one"));
+	&movups	($inout1,&QWP(0x10,$inp));
+	&je	(&label("ecb_enc_two"));
+	&movups	($inout2,&QWP(0x20,$inp));
+	&cmp	($len,0x40);
+	&jb	(&label("ecb_enc_three"));
+	&movups	($inout3,&QWP(0x30,$inp));
+	&je	(&label("ecb_enc_four"));
+	&movups	($inout4,&QWP(0x40,$inp));
+	&xorps	($inout5,$inout5);
+	&call	("_aesni_encrypt6");
+	&movups	(&QWP(0,$out),$inout0);
+	&movups	(&QWP(0x10,$out),$inout1);
+	&movups	(&QWP(0x20,$out),$inout2);
+	&movups	(&QWP(0x30,$out),$inout3);
+	&movups	(&QWP(0x40,$out),$inout4);
+	jmp	(&label("ecb_ret"));
+
+&set_label("ecb_enc_one",16);
+	if ($inline)
+	{   &aesni_inline_generate1("enc");	}
+	else
+	{   &call	("_aesni_encrypt1");	}
+	&movups	(&QWP(0,$out),$inout0);
+	&jmp	(&label("ecb_ret"));
+
+&set_label("ecb_enc_two",16);
+	&call	("_aesni_encrypt2");
+	&movups	(&QWP(0,$out),$inout0);
+	&movups	(&QWP(0x10,$out),$inout1);
+	&jmp	(&label("ecb_ret"));
+
+&set_label("ecb_enc_three",16);
+	&call	("_aesni_encrypt3");
+	&movups	(&QWP(0,$out),$inout0);
+	&movups	(&QWP(0x10,$out),$inout1);
+	&movups	(&QWP(0x20,$out),$inout2);
+	&jmp	(&label("ecb_ret"));
+
+&set_label("ecb_enc_four",16);
+	&call	("_aesni_encrypt4");
+	&movups	(&QWP(0,$out),$inout0);
+	&movups	(&QWP(0x10,$out),$inout1);
+	&movups	(&QWP(0x20,$out),$inout2);
+	&movups	(&QWP(0x30,$out),$inout3);
+	&jmp	(&label("ecb_ret"));
+######################################################################
+&set_label("ecb_decrypt",16);
+	&mov	($key_,$key);		# backup $key
+	&mov	($rounds_,$rounds);	# backup $rounds
+	&cmp	($len,0x60);
+	&jb	(&label("ecb_dec_tail"));
+
+	&movdqu	($inout0,&QWP(0,$inp));
+	&movdqu	($inout1,&QWP(0x10,$inp));
+	&movdqu	($inout2,&QWP(0x20,$inp));
+	&movdqu	($inout3,&QWP(0x30,$inp));
+	&movdqu	($inout4,&QWP(0x40,$inp));
+	&movdqu	($inout5,&QWP(0x50,$inp));
+	&lea	($inp,&DWP(0x60,$inp));
+	&sub	($len,0x60);
+	&jmp	(&label("ecb_dec_loop6_enter"));
+
+&set_label("ecb_dec_loop6",16);
+	&movups	(&QWP(0,$out),$inout0);
+	&movdqu	($inout0,&QWP(0,$inp));
+	&movups	(&QWP(0x10,$out),$inout1);
+	&movdqu	($inout1,&QWP(0x10,$inp));
+	&movups	(&QWP(0x20,$out),$inout2);
+	&movdqu	($inout2,&QWP(0x20,$inp));
+	&movups	(&QWP(0x30,$out),$inout3);
+	&movdqu	($inout3,&QWP(0x30,$inp));
+	&movups	(&QWP(0x40,$out),$inout4);
+	&movdqu	($inout4,&QWP(0x40,$inp));
+	&movups	(&QWP(0x50,$out),$inout5);
+	&lea	($out,&DWP(0x60,$out));
+	&movdqu	($inout5,&QWP(0x50,$inp));
+	&lea	($inp,&DWP(0x60,$inp));
+&set_label("ecb_dec_loop6_enter");
+
+	&call	("_aesni_decrypt6");
+
+	&mov	($key,$key_);		# restore $key
+	&mov	($rounds,$rounds_);	# restore $rounds
+	&sub	($len,0x60);
+	&jnc	(&label("ecb_dec_loop6"));
+
+	&movups	(&QWP(0,$out),$inout0);
+	&movups	(&QWP(0x10,$out),$inout1);
+	&movups	(&QWP(0x20,$out),$inout2);
+	&movups	(&QWP(0x30,$out),$inout3);
+	&movups	(&QWP(0x40,$out),$inout4);
+	&movups	(&QWP(0x50,$out),$inout5);
+	&lea	($out,&DWP(0x60,$out));
+	&add	($len,0x60);
+	&jz	(&label("ecb_ret"));
+
+&set_label("ecb_dec_tail");
+	&movups	($inout0,&QWP(0,$inp));
+	&cmp	($len,0x20);
+	&jb	(&label("ecb_dec_one"));
+	&movups	($inout1,&QWP(0x10,$inp));
+	&je	(&label("ecb_dec_two"));
+	&movups	($inout2,&QWP(0x20,$inp));
+	&cmp	($len,0x40);
+	&jb	(&label("ecb_dec_three"));
+	&movups	($inout3,&QWP(0x30,$inp));
+	&je	(&label("ecb_dec_four"));
+	&movups	($inout4,&QWP(0x40,$inp));
+	&xorps	($inout5,$inout5);
+	&call	("_aesni_decrypt6");
+	&movups	(&QWP(0,$out),$inout0);
+	&movups	(&QWP(0x10,$out),$inout1);
+	&movups	(&QWP(0x20,$out),$inout2);
+	&movups	(&QWP(0x30,$out),$inout3);
+	&movups	(&QWP(0x40,$out),$inout4);
+	&jmp	(&label("ecb_ret"));
+
+&set_label("ecb_dec_one",16);
+	if ($inline)
+	{   &aesni_inline_generate1("dec");	}
+	else
+	{   &call	("_aesni_decrypt1");	}
+	&movups	(&QWP(0,$out),$inout0);
+	&jmp	(&label("ecb_ret"));
+
+&set_label("ecb_dec_two",16);
+	&call	("_aesni_decrypt2");
+	&movups	(&QWP(0,$out),$inout0);
+	&movups	(&QWP(0x10,$out),$inout1);
+	&jmp	(&label("ecb_ret"));
+
+&set_label("ecb_dec_three",16);
+	&call	("_aesni_decrypt3");
+	&movups	(&QWP(0,$out),$inout0);
+	&movups	(&QWP(0x10,$out),$inout1);
+	&movups	(&QWP(0x20,$out),$inout2);
+	&jmp	(&label("ecb_ret"));
+
+&set_label("ecb_dec_four",16);
+	&call	("_aesni_decrypt4");
+	&movups	(&QWP(0,$out),$inout0);
+	&movups	(&QWP(0x10,$out),$inout1);
+	&movups	(&QWP(0x20,$out),$inout2);
+	&movups	(&QWP(0x30,$out),$inout3);
+
+&set_label("ecb_ret");
+	&pxor	("xmm0","xmm0");		# clear register bank
+	&pxor	("xmm1","xmm1");
+	&pxor	("xmm2","xmm2");
+	&pxor	("xmm3","xmm3");
+	&pxor	("xmm4","xmm4");
+	&pxor	("xmm5","xmm5");
+	&pxor	("xmm6","xmm6");
+	&pxor	("xmm7","xmm7");
+&function_end("aesni_ecb_encrypt");
+
+######################################################################
+# void aesni_ccm64_[en|de]crypt_blocks (const void *in, void *out,
+#                         size_t blocks, const AES_KEY *key,
+#                         const char *ivec,char *cmac);
+#
+# Handles only complete blocks, operates on 64-bit counter and
+# does not update *ivec! Nor does it finalize CMAC value
+# (see engine/eng_aesni.c for details)
+#
+{ my $cmac=$inout1;
+&function_begin("aesni_ccm64_encrypt_blocks");
+	&mov	($inp,&wparam(0));
+	&mov	($out,&wparam(1));
+	&mov	($len,&wparam(2));
+	&mov	($key,&wparam(3));
+	&mov	($rounds_,&wparam(4));
+	&mov	($rounds,&wparam(5));
+	&mov	($key_,"esp");
+	&sub	("esp",60);
+	&and	("esp",-16);			# align stack
+	&mov	(&DWP(48,"esp"),$key_);
+
+	&movdqu	($ivec,&QWP(0,$rounds_));	# load ivec
+	&movdqu	($cmac,&QWP(0,$rounds));	# load cmac
+	&mov	($rounds,&DWP(240,$key));
+
+	# compose byte-swap control mask for pshufb on stack
+	&mov	(&DWP(0,"esp"),0x0c0d0e0f);
+	&mov	(&DWP(4,"esp"),0x08090a0b);
+	&mov	(&DWP(8,"esp"),0x04050607);
+	&mov	(&DWP(12,"esp"),0x00010203);
+
+	# compose counter increment vector on stack
+	&mov	($rounds_,1);
+	&xor	($key_,$key_);
+	&mov	(&DWP(16,"esp"),$rounds_);
+	&mov	(&DWP(20,"esp"),$key_);
+	&mov	(&DWP(24,"esp"),$key_);
+	&mov	(&DWP(28,"esp"),$key_);
+
+	&shl	($rounds,4);
+	&mov	($rounds_,16);
+	&lea	($key_,&DWP(0,$key));
+	&movdqa	($inout3,&QWP(0,"esp"));
+	&movdqa	($inout0,$ivec);
+	&lea	($key,&DWP(32,$key,$rounds));
+	&sub	($rounds_,$rounds);
+	&pshufb	($ivec,$inout3);
+
+&set_label("ccm64_enc_outer");
+	&$movekey	($rndkey0,&QWP(0,$key_));
+	&mov		($rounds,$rounds_);
+	&movups		($in0,&QWP(0,$inp));
+
+	&xorps		($inout0,$rndkey0);
+	&$movekey	($rndkey1,&QWP(16,$key_));
+	&xorps		($rndkey0,$in0);
+	&xorps		($cmac,$rndkey0);		# cmac^=inp
+	&$movekey	($rndkey0,&QWP(32,$key_));
+
+&set_label("ccm64_enc2_loop");
+	&aesenc		($inout0,$rndkey1);
+	&aesenc		($cmac,$rndkey1);
+	&$movekey	($rndkey1,&QWP(0,$key,$rounds));
+	&add		($rounds,32);
+	&aesenc		($inout0,$rndkey0);
+	&aesenc		($cmac,$rndkey0);
+	&$movekey	($rndkey0,&QWP(-16,$key,$rounds));
+	&jnz		(&label("ccm64_enc2_loop"));
+	&aesenc		($inout0,$rndkey1);
+	&aesenc		($cmac,$rndkey1);
+	&paddq		($ivec,&QWP(16,"esp"));
+	&dec		($len);
+	&aesenclast	($inout0,$rndkey0);
+	&aesenclast	($cmac,$rndkey0);
+
+	&lea	($inp,&DWP(16,$inp));
+	&xorps	($in0,$inout0);			# inp^=E(ivec)
+	&movdqa	($inout0,$ivec);
+	&movups	(&QWP(0,$out),$in0);		# save output
+	&pshufb	($inout0,$inout3);
+	&lea	($out,&DWP(16,$out));
+	&jnz	(&label("ccm64_enc_outer"));
+
+	&mov	("esp",&DWP(48,"esp"));
+	&mov	($out,&wparam(5));
+	&movups	(&QWP(0,$out),$cmac);
+
+	&pxor	("xmm0","xmm0");		# clear register bank
+	&pxor	("xmm1","xmm1");
+	&pxor	("xmm2","xmm2");
+	&pxor	("xmm3","xmm3");
+	&pxor	("xmm4","xmm4");
+	&pxor	("xmm5","xmm5");
+	&pxor	("xmm6","xmm6");
+	&pxor	("xmm7","xmm7");
+&function_end("aesni_ccm64_encrypt_blocks");
+
+&function_begin("aesni_ccm64_decrypt_blocks");
+	&mov	($inp,&wparam(0));
+	&mov	($out,&wparam(1));
+	&mov	($len,&wparam(2));
+	&mov	($key,&wparam(3));
+	&mov	($rounds_,&wparam(4));
+	&mov	($rounds,&wparam(5));
+	&mov	($key_,"esp");
+	&sub	("esp",60);
+	&and	("esp",-16);			# align stack
+	&mov	(&DWP(48,"esp"),$key_);
+
+	&movdqu	($ivec,&QWP(0,$rounds_));	# load ivec
+	&movdqu	($cmac,&QWP(0,$rounds));	# load cmac
+	&mov	($rounds,&DWP(240,$key));
+
+	# compose byte-swap control mask for pshufb on stack
+	&mov	(&DWP(0,"esp"),0x0c0d0e0f);
+	&mov	(&DWP(4,"esp"),0x08090a0b);
+	&mov	(&DWP(8,"esp"),0x04050607);
+	&mov	(&DWP(12,"esp"),0x00010203);
+
+	# compose counter increment vector on stack
+	&mov	($rounds_,1);
+	&xor	($key_,$key_);
+	&mov	(&DWP(16,"esp"),$rounds_);
+	&mov	(&DWP(20,"esp"),$key_);
+	&mov	(&DWP(24,"esp"),$key_);
+	&mov	(&DWP(28,"esp"),$key_);
+
+	&movdqa	($inout3,&QWP(0,"esp"));	# bswap mask
+	&movdqa	($inout0,$ivec);
+
+	&mov	($key_,$key);
+	&mov	($rounds_,$rounds);
+
+	&pshufb	($ivec,$inout3);
+	if ($inline)
+	{   &aesni_inline_generate1("enc");	}
+	else
+	{   &call	("_aesni_encrypt1");	}
+	&shl	($rounds_,4);
+	&mov	($rounds,16);
+	&movups	($in0,&QWP(0,$inp));		# load inp
+	&paddq	($ivec,&QWP(16,"esp"));
+	&lea	($inp,&QWP(16,$inp));
+	&sub	($rounds,$rounds_);
+	&lea	($key,&DWP(32,$key_,$rounds_));
+	&mov	($rounds_,$rounds);
+	&jmp	(&label("ccm64_dec_outer"));
+
+&set_label("ccm64_dec_outer",16);
+	&xorps	($in0,$inout0);			# inp ^= E(ivec)
+	&movdqa	($inout0,$ivec);
+	&movups	(&QWP(0,$out),$in0);		# save output
+	&lea	($out,&DWP(16,$out));
+	&pshufb	($inout0,$inout3);
+
+	&sub	($len,1);
+	&jz	(&label("ccm64_dec_break"));
+
+	&$movekey	($rndkey0,&QWP(0,$key_));
+	&mov		($rounds,$rounds_);
+	&$movekey	($rndkey1,&QWP(16,$key_));
+	&xorps		($in0,$rndkey0);
+	&xorps		($inout0,$rndkey0);
+	&xorps		($cmac,$in0);		# cmac^=out
+	&$movekey	($rndkey0,&QWP(32,$key_));
+
+&set_label("ccm64_dec2_loop");
+	&aesenc		($inout0,$rndkey1);
+	&aesenc		($cmac,$rndkey1);
+	&$movekey	($rndkey1,&QWP(0,$key,$rounds));
+	&add		($rounds,32);
+	&aesenc		($inout0,$rndkey0);
+	&aesenc		($cmac,$rndkey0);
+	&$movekey	($rndkey0,&QWP(-16,$key,$rounds));
+	&jnz		(&label("ccm64_dec2_loop"));
+	&movups		($in0,&QWP(0,$inp));	# load inp
+	&paddq		($ivec,&QWP(16,"esp"));
+	&aesenc		($inout0,$rndkey1);
+	&aesenc		($cmac,$rndkey1);
+	&aesenclast	($inout0,$rndkey0);
+	&aesenclast	($cmac,$rndkey0);
+	&lea		($inp,&QWP(16,$inp));
+	&jmp	(&label("ccm64_dec_outer"));
+
+&set_label("ccm64_dec_break",16);
+	&mov	($rounds,&DWP(240,$key_));
+	&mov	($key,$key_);
+	if ($inline)
+	{   &aesni_inline_generate1("enc",$cmac,$in0);	}
+	else
+	{   &call	("_aesni_encrypt1",$cmac);	}
+
+	&mov	("esp",&DWP(48,"esp"));
+	&mov	($out,&wparam(5));
+	&movups	(&QWP(0,$out),$cmac);
+
+	&pxor	("xmm0","xmm0");		# clear register bank
+	&pxor	("xmm1","xmm1");
+	&pxor	("xmm2","xmm2");
+	&pxor	("xmm3","xmm3");
+	&pxor	("xmm4","xmm4");
+	&pxor	("xmm5","xmm5");
+	&pxor	("xmm6","xmm6");
+	&pxor	("xmm7","xmm7");
+&function_end("aesni_ccm64_decrypt_blocks");
+}
+
+######################################################################
+# void aesni_ctr32_encrypt_blocks (const void *in, void *out,
+#                         size_t blocks, const AES_KEY *key,
+#                         const char *ivec);
+#
+# Handles only complete blocks, operates on 32-bit counter and
+# does not update *ivec! (see crypto/modes/ctr128.c for details)
+#
+# stack layout:
+#	0	pshufb mask
+#	16	vector addend: 0,6,6,6
+# 	32	counter-less ivec
+#	48	1st triplet of counter vector
+#	64	2nd triplet of counter vector
+#	80	saved %esp
+
+&function_begin("aesni_ctr32_encrypt_blocks");
+	&mov	($inp,&wparam(0));
+	&mov	($out,&wparam(1));
+	&mov	($len,&wparam(2));
+	&mov	($key,&wparam(3));
+	&mov	($rounds_,&wparam(4));
+	&mov	($key_,"esp");
+	&sub	("esp",88);
+	&and	("esp",-16);			# align stack
+	&mov	(&DWP(80,"esp"),$key_);
+
+	&cmp	($len,1);
+	&je	(&label("ctr32_one_shortcut"));
+
+	&movdqu	($inout5,&QWP(0,$rounds_));	# load ivec
+
+	# compose byte-swap control mask for pshufb on stack
+	&mov	(&DWP(0,"esp"),0x0c0d0e0f);
+	&mov	(&DWP(4,"esp"),0x08090a0b);
+	&mov	(&DWP(8,"esp"),0x04050607);
+	&mov	(&DWP(12,"esp"),0x00010203);
+
+	# compose counter increment vector on stack
+	&mov	($rounds,6);
+	&xor	($key_,$key_);
+	&mov	(&DWP(16,"esp"),$rounds);
+	&mov	(&DWP(20,"esp"),$rounds);
+	&mov	(&DWP(24,"esp"),$rounds);
+	&mov	(&DWP(28,"esp"),$key_);
+
+	&pextrd	($rounds_,$inout5,3);		# pull 32-bit counter
+	&pinsrd	($inout5,$key_,3);		# wipe 32-bit counter
+
+	&mov	($rounds,&DWP(240,$key));	# key->rounds
+
+	# compose 2 vectors of 3x32-bit counters
+	&bswap	($rounds_);
+	&pxor	($rndkey0,$rndkey0);
+	&pxor	($rndkey1,$rndkey1);
+	&movdqa	($inout0,&QWP(0,"esp"));	# load byte-swap mask
+	&pinsrd	($rndkey0,$rounds_,0);
+	&lea	($key_,&DWP(3,$rounds_));
+	&pinsrd	($rndkey1,$key_,0);
+	&inc	($rounds_);
+	&pinsrd	($rndkey0,$rounds_,1);
+	&inc	($key_);
+	&pinsrd	($rndkey1,$key_,1);
+	&inc	($rounds_);
+	&pinsrd	($rndkey0,$rounds_,2);
+	&inc	($key_);
+	&pinsrd	($rndkey1,$key_,2);
+	&movdqa	(&QWP(48,"esp"),$rndkey0);	# save 1st triplet
+	&pshufb	($rndkey0,$inout0);		# byte swap
+	&movdqu	($inout4,&QWP(0,$key));		# key[0]
+	&movdqa	(&QWP(64,"esp"),$rndkey1);	# save 2nd triplet
+	&pshufb	($rndkey1,$inout0);		# byte swap
+
+	&pshufd	($inout0,$rndkey0,3<<6);	# place counter to upper dword
+	&pshufd	($inout1,$rndkey0,2<<6);
+	&cmp	($len,6);
+	&jb	(&label("ctr32_tail"));
+	&pxor	($inout5,$inout4);		# counter-less ivec^key[0]
+	&shl	($rounds,4);
+	&mov	($rounds_,16);
+	&movdqa	(&QWP(32,"esp"),$inout5);	# save counter-less ivec^key[0]
+	&mov	($key_,$key);			# backup $key
+	&sub	($rounds_,$rounds);		# backup twisted $rounds
+	&lea	($key,&DWP(32,$key,$rounds));
+	&sub	($len,6);
+	&jmp	(&label("ctr32_loop6"));
+
+&set_label("ctr32_loop6",16);
+	# inlining _aesni_encrypt6's prologue gives ~6% improvement...
+	&pshufd	($inout2,$rndkey0,1<<6);
+	&movdqa	($rndkey0,&QWP(32,"esp"));	# pull counter-less ivec
+	&pshufd	($inout3,$rndkey1,3<<6);
+	&pxor		($inout0,$rndkey0);	# merge counter-less ivec
+	&pshufd	($inout4,$rndkey1,2<<6);
+	&pxor		($inout1,$rndkey0);
+	&pshufd	($inout5,$rndkey1,1<<6);
+	&$movekey	($rndkey1,&QWP(16,$key_));
+	&pxor		($inout2,$rndkey0);
+	&pxor		($inout3,$rndkey0);
+	&aesenc		($inout0,$rndkey1);
+	&pxor		($inout4,$rndkey0);
+	&pxor		($inout5,$rndkey0);
+	&aesenc		($inout1,$rndkey1);
+	&$movekey	($rndkey0,&QWP(32,$key_));
+	&mov		($rounds,$rounds_);
+	&aesenc		($inout2,$rndkey1);
+	&aesenc		($inout3,$rndkey1);
+	&aesenc		($inout4,$rndkey1);
+	&aesenc		($inout5,$rndkey1);
+
+	&call		(&label("_aesni_encrypt6_enter"));
+
+	&movups	($rndkey1,&QWP(0,$inp));
+	&movups	($rndkey0,&QWP(0x10,$inp));
+	&xorps	($inout0,$rndkey1);
+	&movups	($rndkey1,&QWP(0x20,$inp));
+	&xorps	($inout1,$rndkey0);
+	&movups	(&QWP(0,$out),$inout0);
+	&movdqa	($rndkey0,&QWP(16,"esp"));	# load increment
+	&xorps	($inout2,$rndkey1);
+	&movdqa	($rndkey1,&QWP(64,"esp"));	# load 2nd triplet
+	&movups	(&QWP(0x10,$out),$inout1);
+	&movups	(&QWP(0x20,$out),$inout2);
+
+	&paddd	($rndkey1,$rndkey0);		# 2nd triplet increment
+	&paddd	($rndkey0,&QWP(48,"esp"));	# 1st triplet increment
+	&movdqa	($inout0,&QWP(0,"esp"));	# load byte swap mask
+
+	&movups	($inout1,&QWP(0x30,$inp));
+	&movups	($inout2,&QWP(0x40,$inp));
+	&xorps	($inout3,$inout1);
+	&movups	($inout1,&QWP(0x50,$inp));
+	&lea	($inp,&DWP(0x60,$inp));
+	&movdqa	(&QWP(48,"esp"),$rndkey0);	# save 1st triplet
+	&pshufb	($rndkey0,$inout0);		# byte swap
+	&xorps	($inout4,$inout2);
+	&movups	(&QWP(0x30,$out),$inout3);
+	&xorps	($inout5,$inout1);
+	&movdqa	(&QWP(64,"esp"),$rndkey1);	# save 2nd triplet
+	&pshufb	($rndkey1,$inout0);		# byte swap
+	&movups	(&QWP(0x40,$out),$inout4);
+	&pshufd	($inout0,$rndkey0,3<<6);
+	&movups	(&QWP(0x50,$out),$inout5);
+	&lea	($out,&DWP(0x60,$out));
+
+	&pshufd	($inout1,$rndkey0,2<<6);
+	&sub	($len,6);
+	&jnc	(&label("ctr32_loop6"));
+
+	&add	($len,6);
+	&jz	(&label("ctr32_ret"));
+	&movdqu	($inout5,&QWP(0,$key_));
+	&mov	($key,$key_);
+	&pxor	($inout5,&QWP(32,"esp"));	# restore count-less ivec
+	&mov	($rounds,&DWP(240,$key_));	# restore $rounds
+
+&set_label("ctr32_tail");
+	&por	($inout0,$inout5);
+	&cmp	($len,2);
+	&jb	(&label("ctr32_one"));
+
+	&pshufd	($inout2,$rndkey0,1<<6);
+	&por	($inout1,$inout5);
+	&je	(&label("ctr32_two"));
+
+	&pshufd	($inout3,$rndkey1,3<<6);
+	&por	($inout2,$inout5);
+	&cmp	($len,4);
+	&jb	(&label("ctr32_three"));
+
+	&pshufd	($inout4,$rndkey1,2<<6);
+	&por	($inout3,$inout5);
+	&je	(&label("ctr32_four"));
+
+	&por	($inout4,$inout5);
+	&call	("_aesni_encrypt6");
+	&movups	($rndkey1,&QWP(0,$inp));
+	&movups	($rndkey0,&QWP(0x10,$inp));
+	&xorps	($inout0,$rndkey1);
+	&movups	($rndkey1,&QWP(0x20,$inp));
+	&xorps	($inout1,$rndkey0);
+	&movups	($rndkey0,&QWP(0x30,$inp));
+	&xorps	($inout2,$rndkey1);
+	&movups	($rndkey1,&QWP(0x40,$inp));
+	&xorps	($inout3,$rndkey0);
+	&movups	(&QWP(0,$out),$inout0);
+	&xorps	($inout4,$rndkey1);
+	&movups	(&QWP(0x10,$out),$inout1);
+	&movups	(&QWP(0x20,$out),$inout2);
+	&movups	(&QWP(0x30,$out),$inout3);
+	&movups	(&QWP(0x40,$out),$inout4);
+	&jmp	(&label("ctr32_ret"));
+
+&set_label("ctr32_one_shortcut",16);
+	&movups	($inout0,&QWP(0,$rounds_));	# load ivec
+	&mov	($rounds,&DWP(240,$key));
+	
+&set_label("ctr32_one");
+	if ($inline)
+	{   &aesni_inline_generate1("enc");	}
+	else
+	{   &call	("_aesni_encrypt1");	}
+	&movups	($in0,&QWP(0,$inp));
+	&xorps	($in0,$inout0);
+	&movups	(&QWP(0,$out),$in0);
+	&jmp	(&label("ctr32_ret"));
+
+&set_label("ctr32_two",16);
+	&call	("_aesni_encrypt2");
+	&movups	($inout3,&QWP(0,$inp));
+	&movups	($inout4,&QWP(0x10,$inp));
+	&xorps	($inout0,$inout3);
+	&xorps	($inout1,$inout4);
+	&movups	(&QWP(0,$out),$inout0);
+	&movups	(&QWP(0x10,$out),$inout1);
+	&jmp	(&label("ctr32_ret"));
+
+&set_label("ctr32_three",16);
+	&call	("_aesni_encrypt3");
+	&movups	($inout3,&QWP(0,$inp));
+	&movups	($inout4,&QWP(0x10,$inp));
+	&xorps	($inout0,$inout3);
+	&movups	($inout5,&QWP(0x20,$inp));
+	&xorps	($inout1,$inout4);
+	&movups	(&QWP(0,$out),$inout0);
+	&xorps	($inout2,$inout5);
+	&movups	(&QWP(0x10,$out),$inout1);
+	&movups	(&QWP(0x20,$out),$inout2);
+	&jmp	(&label("ctr32_ret"));
+
+&set_label("ctr32_four",16);
+	&call	("_aesni_encrypt4");
+	&movups	($inout4,&QWP(0,$inp));
+	&movups	($inout5,&QWP(0x10,$inp));
+	&movups	($rndkey1,&QWP(0x20,$inp));
+	&xorps	($inout0,$inout4);
+	&movups	($rndkey0,&QWP(0x30,$inp));
+	&xorps	($inout1,$inout5);
+	&movups	(&QWP(0,$out),$inout0);
+	&xorps	($inout2,$rndkey1);
+	&movups	(&QWP(0x10,$out),$inout1);
+	&xorps	($inout3,$rndkey0);
+	&movups	(&QWP(0x20,$out),$inout2);
+	&movups	(&QWP(0x30,$out),$inout3);
+
+&set_label("ctr32_ret");
+	&pxor	("xmm0","xmm0");		# clear register bank
+	&pxor	("xmm1","xmm1");
+	&pxor	("xmm2","xmm2");
+	&pxor	("xmm3","xmm3");
+	&pxor	("xmm4","xmm4");
+	&movdqa	(&QWP(32,"esp"),"xmm0");	# clear stack
+	&pxor	("xmm5","xmm5");
+	&movdqa	(&QWP(48,"esp"),"xmm0");
+	&pxor	("xmm6","xmm6");
+	&movdqa	(&QWP(64,"esp"),"xmm0");
+	&pxor	("xmm7","xmm7");
+	&mov	("esp",&DWP(80,"esp"));
+&function_end("aesni_ctr32_encrypt_blocks");
+
+######################################################################
+# void aesni_xts_[en|de]crypt(const char *inp,char *out,size_t len,
+#	const AES_KEY *key1, const AES_KEY *key2
+#	const unsigned char iv[16]);
+#
+{ my ($tweak,$twtmp,$twres,$twmask)=($rndkey1,$rndkey0,$inout0,$inout1);
+
+&function_begin("aesni_xts_encrypt");
+	&mov	($key,&wparam(4));		# key2
+	&mov	($inp,&wparam(5));		# clear-text tweak
+
+	&mov	($rounds,&DWP(240,$key));	# key2->rounds
+	&movups	($inout0,&QWP(0,$inp));
+	if ($inline)
+	{   &aesni_inline_generate1("enc");	}
+	else
+	{   &call	("_aesni_encrypt1");	}
+
+	&mov	($inp,&wparam(0));
+	&mov	($out,&wparam(1));
+	&mov	($len,&wparam(2));
+	&mov	($key,&wparam(3));		# key1
+
+	&mov	($key_,"esp");
+	&sub	("esp",16*7+8);
+	&mov	($rounds,&DWP(240,$key));	# key1->rounds
+	&and	("esp",-16);			# align stack
+
+	&mov	(&DWP(16*6+0,"esp"),0x87);	# compose the magic constant
+	&mov	(&DWP(16*6+4,"esp"),0);
+	&mov	(&DWP(16*6+8,"esp"),1);
+	&mov	(&DWP(16*6+12,"esp"),0);
+	&mov	(&DWP(16*7+0,"esp"),$len);	# save original $len
+	&mov	(&DWP(16*7+4,"esp"),$key_);	# save original %esp
+
+	&movdqa	($tweak,$inout0);
+	&pxor	($twtmp,$twtmp);
+	&movdqa	($twmask,&QWP(6*16,"esp"));	# 0x0...010...87
+	&pcmpgtd($twtmp,$tweak);		# broadcast upper bits
+
+	&and	($len,-16);
+	&mov	($key_,$key);			# backup $key
+	&mov	($rounds_,$rounds);		# backup $rounds
+	&sub	($len,16*6);
+	&jc	(&label("xts_enc_short"));
+
+	&shl	($rounds,4);
+	&mov	($rounds_,16);
+	&sub	($rounds_,$rounds);
+	&lea	($key,&DWP(32,$key,$rounds));
+	&jmp	(&label("xts_enc_loop6"));
+
+&set_label("xts_enc_loop6",16);
+	for ($i=0;$i<4;$i++) {
+	    &pshufd	($twres,$twtmp,0x13);
+	    &pxor	($twtmp,$twtmp);
+	    &movdqa	(&QWP(16*$i,"esp"),$tweak);
+	    &paddq	($tweak,$tweak);	# &psllq($tweak,1);
+	    &pand	($twres,$twmask);	# isolate carry and residue
+	    &pcmpgtd	($twtmp,$tweak);	# broadcast upper bits
+	    &pxor	($tweak,$twres);
+	}
+	&pshufd	($inout5,$twtmp,0x13);
+	&movdqa	(&QWP(16*$i++,"esp"),$tweak);
+	&paddq	($tweak,$tweak);		# &psllq($tweak,1);
+	 &$movekey	($rndkey0,&QWP(0,$key_));
+	&pand	($inout5,$twmask);		# isolate carry and residue
+	 &movups	($inout0,&QWP(0,$inp));	# load input
+	&pxor	($inout5,$tweak);
+
+	# inline _aesni_encrypt6 prologue and flip xor with tweak and key[0]
+	&mov	($rounds,$rounds_);		# restore $rounds
+	&movdqu	($inout1,&QWP(16*1,$inp));
+	 &xorps		($inout0,$rndkey0);	# input^=rndkey[0]
+	&movdqu	($inout2,&QWP(16*2,$inp));
+	 &pxor		($inout1,$rndkey0);
+	&movdqu	($inout3,&QWP(16*3,$inp));
+	 &pxor		($inout2,$rndkey0);
+	&movdqu	($inout4,&QWP(16*4,$inp));
+	 &pxor		($inout3,$rndkey0);
+	&movdqu	($rndkey1,&QWP(16*5,$inp));
+	 &pxor		($inout4,$rndkey0);
+	&lea	($inp,&DWP(16*6,$inp));
+	&pxor	($inout0,&QWP(16*0,"esp"));	# input^=tweak
+	&movdqa	(&QWP(16*$i,"esp"),$inout5);	# save last tweak
+	&pxor	($inout5,$rndkey1);
+
+	 &$movekey	($rndkey1,&QWP(16,$key_));
+	&pxor	($inout1,&QWP(16*1,"esp"));
+	&pxor	($inout2,&QWP(16*2,"esp"));
+	 &aesenc	($inout0,$rndkey1);
+	&pxor	($inout3,&QWP(16*3,"esp"));
+	&pxor	($inout4,&QWP(16*4,"esp"));
+	 &aesenc	($inout1,$rndkey1);
+	&pxor		($inout5,$rndkey0);
+	 &$movekey	($rndkey0,&QWP(32,$key_));
+	 &aesenc	($inout2,$rndkey1);
+	 &aesenc	($inout3,$rndkey1);
+	 &aesenc	($inout4,$rndkey1);
+	 &aesenc	($inout5,$rndkey1);
+	&call		(&label("_aesni_encrypt6_enter"));
+
+	&movdqa	($tweak,&QWP(16*5,"esp"));	# last tweak
+       &pxor	($twtmp,$twtmp);
+	&xorps	($inout0,&QWP(16*0,"esp"));	# output^=tweak
+       &pcmpgtd	($twtmp,$tweak);		# broadcast upper bits
+	&xorps	($inout1,&QWP(16*1,"esp"));
+	&movups	(&QWP(16*0,$out),$inout0);	# write output
+	&xorps	($inout2,&QWP(16*2,"esp"));
+	&movups	(&QWP(16*1,$out),$inout1);
+	&xorps	($inout3,&QWP(16*3,"esp"));
+	&movups	(&QWP(16*2,$out),$inout2);
+	&xorps	($inout4,&QWP(16*4,"esp"));
+	&movups	(&QWP(16*3,$out),$inout3);
+	&xorps	($inout5,$tweak);
+	&movups	(&QWP(16*4,$out),$inout4);
+       &pshufd	($twres,$twtmp,0x13);
+	&movups	(&QWP(16*5,$out),$inout5);
+	&lea	($out,&DWP(16*6,$out));
+       &movdqa	($twmask,&QWP(16*6,"esp"));	# 0x0...010...87
+
+	&pxor	($twtmp,$twtmp);
+	&paddq	($tweak,$tweak);		# &psllq($tweak,1);
+	&pand	($twres,$twmask);		# isolate carry and residue
+	&pcmpgtd($twtmp,$tweak);		# broadcast upper bits
+	&pxor	($tweak,$twres);
+
+	&sub	($len,16*6);
+	&jnc	(&label("xts_enc_loop6"));
+
+	&mov	($rounds,&DWP(240,$key_));	# restore $rounds
+	&mov	($key,$key_);			# restore $key
+	&mov	($rounds_,$rounds);
+
+&set_label("xts_enc_short");
+	&add	($len,16*6);
+	&jz	(&label("xts_enc_done6x"));
+
+	&movdqa	($inout3,$tweak);		# put aside previous tweak
+	&cmp	($len,0x20);
+	&jb	(&label("xts_enc_one"));
+
+	&pshufd	($twres,$twtmp,0x13);
+	&pxor	($twtmp,$twtmp);
+	&paddq	($tweak,$tweak);		# &psllq($tweak,1);
+	&pand	($twres,$twmask);		# isolate carry and residue
+	&pcmpgtd($twtmp,$tweak);		# broadcast upper bits
+	&pxor	($tweak,$twres);
+	&je	(&label("xts_enc_two"));
+
+	&pshufd	($twres,$twtmp,0x13);
+	&pxor	($twtmp,$twtmp);
+	&movdqa	($inout4,$tweak);		# put aside previous tweak
+	&paddq	($tweak,$tweak);		# &psllq($tweak,1);
+	&pand	($twres,$twmask);		# isolate carry and residue
+	&pcmpgtd($twtmp,$tweak);		# broadcast upper bits
+	&pxor	($tweak,$twres);
+	&cmp	($len,0x40);
+	&jb	(&label("xts_enc_three"));
+
+	&pshufd	($twres,$twtmp,0x13);
+	&pxor	($twtmp,$twtmp);
+	&movdqa	($inout5,$tweak);		# put aside previous tweak
+	&paddq	($tweak,$tweak);		# &psllq($tweak,1);
+	&pand	($twres,$twmask);		# isolate carry and residue
+	&pcmpgtd($twtmp,$tweak);		# broadcast upper bits
+	&pxor	($tweak,$twres);
+	&movdqa	(&QWP(16*0,"esp"),$inout3);
+	&movdqa	(&QWP(16*1,"esp"),$inout4);
+	&je	(&label("xts_enc_four"));
+
+	&movdqa	(&QWP(16*2,"esp"),$inout5);
+	&pshufd	($inout5,$twtmp,0x13);
+	&movdqa	(&QWP(16*3,"esp"),$tweak);
+	&paddq	($tweak,$tweak);		# &psllq($inout0,1);
+	&pand	($inout5,$twmask);		# isolate carry and residue
+	&pxor	($inout5,$tweak);
+
+	&movdqu	($inout0,&QWP(16*0,$inp));	# load input
+	&movdqu	($inout1,&QWP(16*1,$inp));
+	&movdqu	($inout2,&QWP(16*2,$inp));
+	&pxor	($inout0,&QWP(16*0,"esp"));	# input^=tweak
+	&movdqu	($inout3,&QWP(16*3,$inp));
+	&pxor	($inout1,&QWP(16*1,"esp"));
+	&movdqu	($inout4,&QWP(16*4,$inp));
+	&pxor	($inout2,&QWP(16*2,"esp"));
+	&lea	($inp,&DWP(16*5,$inp));
+	&pxor	($inout3,&QWP(16*3,"esp"));
+	&movdqa	(&QWP(16*4,"esp"),$inout5);	# save last tweak
+	&pxor	($inout4,$inout5);
+
+	&call	("_aesni_encrypt6");
+
+	&movaps	($tweak,&QWP(16*4,"esp"));	# last tweak
+	&xorps	($inout0,&QWP(16*0,"esp"));	# output^=tweak
+	&xorps	($inout1,&QWP(16*1,"esp"));
+	&xorps	($inout2,&QWP(16*2,"esp"));
+	&movups	(&QWP(16*0,$out),$inout0);	# write output
+	&xorps	($inout3,&QWP(16*3,"esp"));
+	&movups	(&QWP(16*1,$out),$inout1);
+	&xorps	($inout4,$tweak);
+	&movups	(&QWP(16*2,$out),$inout2);
+	&movups	(&QWP(16*3,$out),$inout3);
+	&movups	(&QWP(16*4,$out),$inout4);
+	&lea	($out,&DWP(16*5,$out));
+	&jmp	(&label("xts_enc_done"));
+
+&set_label("xts_enc_one",16);
+	&movups	($inout0,&QWP(16*0,$inp));	# load input
+	&lea	($inp,&DWP(16*1,$inp));
+	&xorps	($inout0,$inout3);		# input^=tweak
+	if ($inline)
+	{   &aesni_inline_generate1("enc");	}
+	else
+	{   &call	("_aesni_encrypt1");	}
+	&xorps	($inout0,$inout3);		# output^=tweak
+	&movups	(&QWP(16*0,$out),$inout0);	# write output
+	&lea	($out,&DWP(16*1,$out));
+
+	&movdqa	($tweak,$inout3);		# last tweak
+	&jmp	(&label("xts_enc_done"));
+
+&set_label("xts_enc_two",16);
+	&movaps	($inout4,$tweak);		# put aside last tweak
+
+	&movups	($inout0,&QWP(16*0,$inp));	# load input
+	&movups	($inout1,&QWP(16*1,$inp));
+	&lea	($inp,&DWP(16*2,$inp));
+	&xorps	($inout0,$inout3);		# input^=tweak
+	&xorps	($inout1,$inout4);
+
+	&call	("_aesni_encrypt2");
+
+	&xorps	($inout0,$inout3);		# output^=tweak
+	&xorps	($inout1,$inout4);
+	&movups	(&QWP(16*0,$out),$inout0);	# write output
+	&movups	(&QWP(16*1,$out),$inout1);
+	&lea	($out,&DWP(16*2,$out));
+
+	&movdqa	($tweak,$inout4);		# last tweak
+	&jmp	(&label("xts_enc_done"));
+
+&set_label("xts_enc_three",16);
+	&movaps	($inout5,$tweak);		# put aside last tweak
+	&movups	($inout0,&QWP(16*0,$inp));	# load input
+	&movups	($inout1,&QWP(16*1,$inp));
+	&movups	($inout2,&QWP(16*2,$inp));
+	&lea	($inp,&DWP(16*3,$inp));
+	&xorps	($inout0,$inout3);		# input^=tweak
+	&xorps	($inout1,$inout4);
+	&xorps	($inout2,$inout5);
+
+	&call	("_aesni_encrypt3");
+
+	&xorps	($inout0,$inout3);		# output^=tweak
+	&xorps	($inout1,$inout4);
+	&xorps	($inout2,$inout5);
+	&movups	(&QWP(16*0,$out),$inout0);	# write output
+	&movups	(&QWP(16*1,$out),$inout1);
+	&movups	(&QWP(16*2,$out),$inout2);
+	&lea	($out,&DWP(16*3,$out));
+
+	&movdqa	($tweak,$inout5);		# last tweak
+	&jmp	(&label("xts_enc_done"));
+
+&set_label("xts_enc_four",16);
+	&movaps	($inout4,$tweak);		# put aside last tweak
+
+	&movups	($inout0,&QWP(16*0,$inp));	# load input
+	&movups	($inout1,&QWP(16*1,$inp));
+	&movups	($inout2,&QWP(16*2,$inp));
+	&xorps	($inout0,&QWP(16*0,"esp"));	# input^=tweak
+	&movups	($inout3,&QWP(16*3,$inp));
+	&lea	($inp,&DWP(16*4,$inp));
+	&xorps	($inout1,&QWP(16*1,"esp"));
+	&xorps	($inout2,$inout5);
+	&xorps	($inout3,$inout4);
+
+	&call	("_aesni_encrypt4");
+
+	&xorps	($inout0,&QWP(16*0,"esp"));	# output^=tweak
+	&xorps	($inout1,&QWP(16*1,"esp"));
+	&xorps	($inout2,$inout5);
+	&movups	(&QWP(16*0,$out),$inout0);	# write output
+	&xorps	($inout3,$inout4);
+	&movups	(&QWP(16*1,$out),$inout1);
+	&movups	(&QWP(16*2,$out),$inout2);
+	&movups	(&QWP(16*3,$out),$inout3);
+	&lea	($out,&DWP(16*4,$out));
+
+	&movdqa	($tweak,$inout4);		# last tweak
+	&jmp	(&label("xts_enc_done"));
+
+&set_label("xts_enc_done6x",16);		# $tweak is pre-calculated
+	&mov	($len,&DWP(16*7+0,"esp"));	# restore original $len
+	&and	($len,15);
+	&jz	(&label("xts_enc_ret"));
+	&movdqa	($inout3,$tweak);
+	&mov	(&DWP(16*7+0,"esp"),$len);	# save $len%16
+	&jmp	(&label("xts_enc_steal"));
+
+&set_label("xts_enc_done",16);
+	&mov	($len,&DWP(16*7+0,"esp"));	# restore original $len
+	&pxor	($twtmp,$twtmp);
+	&and	($len,15);
+	&jz	(&label("xts_enc_ret"));
+
+	&pcmpgtd($twtmp,$tweak);		# broadcast upper bits
+	&mov	(&DWP(16*7+0,"esp"),$len);	# save $len%16
+	&pshufd	($inout3,$twtmp,0x13);
+	&paddq	($tweak,$tweak);		# &psllq($tweak,1);
+	&pand	($inout3,&QWP(16*6,"esp"));	# isolate carry and residue
+	&pxor	($inout3,$tweak);
+
+&set_label("xts_enc_steal");
+	&movz	($rounds,&BP(0,$inp));
+	&movz	($key,&BP(-16,$out));
+	&lea	($inp,&DWP(1,$inp));
+	&mov	(&BP(-16,$out),&LB($rounds));
+	&mov	(&BP(0,$out),&LB($key));
+	&lea	($out,&DWP(1,$out));
+	&sub	($len,1);
+	&jnz	(&label("xts_enc_steal"));
+
+	&sub	($out,&DWP(16*7+0,"esp"));	# rewind $out
+	&mov	($key,$key_);			# restore $key
+	&mov	($rounds,$rounds_);		# restore $rounds
+
+	&movups	($inout0,&QWP(-16,$out));	# load input
+	&xorps	($inout0,$inout3);		# input^=tweak
+	if ($inline)
+	{   &aesni_inline_generate1("enc");	}
+	else
+	{   &call	("_aesni_encrypt1");	}
+	&xorps	($inout0,$inout3);		# output^=tweak
+	&movups	(&QWP(-16,$out),$inout0);	# write output
+
+&set_label("xts_enc_ret");
+	&pxor	("xmm0","xmm0");		# clear register bank
+	&pxor	("xmm1","xmm1");
+	&pxor	("xmm2","xmm2");
+	&movdqa	(&QWP(16*0,"esp"),"xmm0");	# clear stack
+	&pxor	("xmm3","xmm3");
+	&movdqa	(&QWP(16*1,"esp"),"xmm0");
+	&pxor	("xmm4","xmm4");
+	&movdqa	(&QWP(16*2,"esp"),"xmm0");
+	&pxor	("xmm5","xmm5");
+	&movdqa	(&QWP(16*3,"esp"),"xmm0");
+	&pxor	("xmm6","xmm6");
+	&movdqa	(&QWP(16*4,"esp"),"xmm0");
+	&pxor	("xmm7","xmm7");
+	&movdqa	(&QWP(16*5,"esp"),"xmm0");
+	&mov	("esp",&DWP(16*7+4,"esp"));	# restore %esp
+&function_end("aesni_xts_encrypt");
+
+&function_begin("aesni_xts_decrypt");
+	&mov	($key,&wparam(4));		# key2
+	&mov	($inp,&wparam(5));		# clear-text tweak
+
+	&mov	($rounds,&DWP(240,$key));	# key2->rounds
+	&movups	($inout0,&QWP(0,$inp));
+	if ($inline)
+	{   &aesni_inline_generate1("enc");	}
+	else
+	{   &call	("_aesni_encrypt1");	}
+
+	&mov	($inp,&wparam(0));
+	&mov	($out,&wparam(1));
+	&mov	($len,&wparam(2));
+	&mov	($key,&wparam(3));		# key1
+
+	&mov	($key_,"esp");
+	&sub	("esp",16*7+8);
+	&and	("esp",-16);			# align stack
+
+	&xor	($rounds_,$rounds_);		# if(len%16) len-=16;
+	&test	($len,15);
+	&setnz	(&LB($rounds_));
+	&shl	($rounds_,4);
+	&sub	($len,$rounds_);
+
+	&mov	(&DWP(16*6+0,"esp"),0x87);	# compose the magic constant
+	&mov	(&DWP(16*6+4,"esp"),0);
+	&mov	(&DWP(16*6+8,"esp"),1);
+	&mov	(&DWP(16*6+12,"esp"),0);
+	&mov	(&DWP(16*7+0,"esp"),$len);	# save original $len
+	&mov	(&DWP(16*7+4,"esp"),$key_);	# save original %esp
+
+	&mov	($rounds,&DWP(240,$key));	# key1->rounds
+	&mov	($key_,$key);			# backup $key
+	&mov	($rounds_,$rounds);		# backup $rounds
+
+	&movdqa	($tweak,$inout0);
+	&pxor	($twtmp,$twtmp);
+	&movdqa	($twmask,&QWP(6*16,"esp"));	# 0x0...010...87
+	&pcmpgtd($twtmp,$tweak);		# broadcast upper bits
+
+	&and	($len,-16);
+	&sub	($len,16*6);
+	&jc	(&label("xts_dec_short"));
+
+	&shl	($rounds,4);
+	&mov	($rounds_,16);
+	&sub	($rounds_,$rounds);
+	&lea	($key,&DWP(32,$key,$rounds));
+	&jmp	(&label("xts_dec_loop6"));
+
+&set_label("xts_dec_loop6",16);
+	for ($i=0;$i<4;$i++) {
+	    &pshufd	($twres,$twtmp,0x13);
+	    &pxor	($twtmp,$twtmp);
+	    &movdqa	(&QWP(16*$i,"esp"),$tweak);
+	    &paddq	($tweak,$tweak);	# &psllq($tweak,1);
+	    &pand	($twres,$twmask);	# isolate carry and residue
+	    &pcmpgtd	($twtmp,$tweak);	# broadcast upper bits
+	    &pxor	($tweak,$twres);
+	}
+	&pshufd	($inout5,$twtmp,0x13);
+	&movdqa	(&QWP(16*$i++,"esp"),$tweak);
+	&paddq	($tweak,$tweak);		# &psllq($tweak,1);
+	 &$movekey	($rndkey0,&QWP(0,$key_));
+	&pand	($inout5,$twmask);		# isolate carry and residue
+	 &movups	($inout0,&QWP(0,$inp));	# load input
+	&pxor	($inout5,$tweak);
+
+	# inline _aesni_encrypt6 prologue and flip xor with tweak and key[0]
+	&mov	($rounds,$rounds_);
+	&movdqu	($inout1,&QWP(16*1,$inp));
+	 &xorps		($inout0,$rndkey0);	# input^=rndkey[0]
+	&movdqu	($inout2,&QWP(16*2,$inp));
+	 &pxor		($inout1,$rndkey0);
+	&movdqu	($inout3,&QWP(16*3,$inp));
+	 &pxor		($inout2,$rndkey0);
+	&movdqu	($inout4,&QWP(16*4,$inp));
+	 &pxor		($inout3,$rndkey0);
+	&movdqu	($rndkey1,&QWP(16*5,$inp));
+	 &pxor		($inout4,$rndkey0);
+	&lea	($inp,&DWP(16*6,$inp));
+	&pxor	($inout0,&QWP(16*0,"esp"));	# input^=tweak
+	&movdqa	(&QWP(16*$i,"esp"),$inout5);	# save last tweak
+	&pxor	($inout5,$rndkey1);
+
+	 &$movekey	($rndkey1,&QWP(16,$key_));
+	&pxor	($inout1,&QWP(16*1,"esp"));
+	&pxor	($inout2,&QWP(16*2,"esp"));
+	 &aesdec	($inout0,$rndkey1);
+	&pxor	($inout3,&QWP(16*3,"esp"));
+	&pxor	($inout4,&QWP(16*4,"esp"));
+	 &aesdec	($inout1,$rndkey1);
+	&pxor		($inout5,$rndkey0);
+	 &$movekey	($rndkey0,&QWP(32,$key_));
+	 &aesdec	($inout2,$rndkey1);
+	 &aesdec	($inout3,$rndkey1);
+	 &aesdec	($inout4,$rndkey1);
+	 &aesdec	($inout5,$rndkey1);
+	&call		(&label("_aesni_decrypt6_enter"));
+
+	&movdqa	($tweak,&QWP(16*5,"esp"));	# last tweak
+       &pxor	($twtmp,$twtmp);
+	&xorps	($inout0,&QWP(16*0,"esp"));	# output^=tweak
+       &pcmpgtd	($twtmp,$tweak);		# broadcast upper bits
+	&xorps	($inout1,&QWP(16*1,"esp"));
+	&movups	(&QWP(16*0,$out),$inout0);	# write output
+	&xorps	($inout2,&QWP(16*2,"esp"));
+	&movups	(&QWP(16*1,$out),$inout1);
+	&xorps	($inout3,&QWP(16*3,"esp"));
+	&movups	(&QWP(16*2,$out),$inout2);
+	&xorps	($inout4,&QWP(16*4,"esp"));
+	&movups	(&QWP(16*3,$out),$inout3);
+	&xorps	($inout5,$tweak);
+	&movups	(&QWP(16*4,$out),$inout4);
+       &pshufd	($twres,$twtmp,0x13);
+	&movups	(&QWP(16*5,$out),$inout5);
+	&lea	($out,&DWP(16*6,$out));
+       &movdqa	($twmask,&QWP(16*6,"esp"));	# 0x0...010...87
+
+	&pxor	($twtmp,$twtmp);
+	&paddq	($tweak,$tweak);		# &psllq($tweak,1);
+	&pand	($twres,$twmask);		# isolate carry and residue
+	&pcmpgtd($twtmp,$tweak);		# broadcast upper bits
+	&pxor	($tweak,$twres);
+
+	&sub	($len,16*6);
+	&jnc	(&label("xts_dec_loop6"));
+
+	&mov	($rounds,&DWP(240,$key_));	# restore $rounds
+	&mov	($key,$key_);			# restore $key
+	&mov	($rounds_,$rounds);
+
+&set_label("xts_dec_short");
+	&add	($len,16*6);
+	&jz	(&label("xts_dec_done6x"));
+
+	&movdqa	($inout3,$tweak);		# put aside previous tweak
+	&cmp	($len,0x20);
+	&jb	(&label("xts_dec_one"));
+
+	&pshufd	($twres,$twtmp,0x13);
+	&pxor	($twtmp,$twtmp);
+	&paddq	($tweak,$tweak);		# &psllq($tweak,1);
+	&pand	($twres,$twmask);		# isolate carry and residue
+	&pcmpgtd($twtmp,$tweak);		# broadcast upper bits
+	&pxor	($tweak,$twres);
+	&je	(&label("xts_dec_two"));
+
+	&pshufd	($twres,$twtmp,0x13);
+	&pxor	($twtmp,$twtmp);
+	&movdqa	($inout4,$tweak);		# put aside previous tweak
+	&paddq	($tweak,$tweak);		# &psllq($tweak,1);
+	&pand	($twres,$twmask);		# isolate carry and residue
+	&pcmpgtd($twtmp,$tweak);		# broadcast upper bits
+	&pxor	($tweak,$twres);
+	&cmp	($len,0x40);
+	&jb	(&label("xts_dec_three"));
+
+	&pshufd	($twres,$twtmp,0x13);
+	&pxor	($twtmp,$twtmp);
+	&movdqa	($inout5,$tweak);		# put aside previous tweak
+	&paddq	($tweak,$tweak);		# &psllq($tweak,1);
+	&pand	($twres,$twmask);		# isolate carry and residue
+	&pcmpgtd($twtmp,$tweak);		# broadcast upper bits
+	&pxor	($tweak,$twres);
+	&movdqa	(&QWP(16*0,"esp"),$inout3);
+	&movdqa	(&QWP(16*1,"esp"),$inout4);
+	&je	(&label("xts_dec_four"));
+
+	&movdqa	(&QWP(16*2,"esp"),$inout5);
+	&pshufd	($inout5,$twtmp,0x13);
+	&movdqa	(&QWP(16*3,"esp"),$tweak);
+	&paddq	($tweak,$tweak);		# &psllq($inout0,1);
+	&pand	($inout5,$twmask);		# isolate carry and residue
+	&pxor	($inout5,$tweak);
+
+	&movdqu	($inout0,&QWP(16*0,$inp));	# load input
+	&movdqu	($inout1,&QWP(16*1,$inp));
+	&movdqu	($inout2,&QWP(16*2,$inp));
+	&pxor	($inout0,&QWP(16*0,"esp"));	# input^=tweak
+	&movdqu	($inout3,&QWP(16*3,$inp));
+	&pxor	($inout1,&QWP(16*1,"esp"));
+	&movdqu	($inout4,&QWP(16*4,$inp));
+	&pxor	($inout2,&QWP(16*2,"esp"));
+	&lea	($inp,&DWP(16*5,$inp));
+	&pxor	($inout3,&QWP(16*3,"esp"));
+	&movdqa	(&QWP(16*4,"esp"),$inout5);	# save last tweak
+	&pxor	($inout4,$inout5);
+
+	&call	("_aesni_decrypt6");
+
+	&movaps	($tweak,&QWP(16*4,"esp"));	# last tweak
+	&xorps	($inout0,&QWP(16*0,"esp"));	# output^=tweak
+	&xorps	($inout1,&QWP(16*1,"esp"));
+	&xorps	($inout2,&QWP(16*2,"esp"));
+	&movups	(&QWP(16*0,$out),$inout0);	# write output
+	&xorps	($inout3,&QWP(16*3,"esp"));
+	&movups	(&QWP(16*1,$out),$inout1);
+	&xorps	($inout4,$tweak);
+	&movups	(&QWP(16*2,$out),$inout2);
+	&movups	(&QWP(16*3,$out),$inout3);
+	&movups	(&QWP(16*4,$out),$inout4);
+	&lea	($out,&DWP(16*5,$out));
+	&jmp	(&label("xts_dec_done"));
+
+&set_label("xts_dec_one",16);
+	&movups	($inout0,&QWP(16*0,$inp));	# load input
+	&lea	($inp,&DWP(16*1,$inp));
+	&xorps	($inout0,$inout3);		# input^=tweak
+	if ($inline)
+	{   &aesni_inline_generate1("dec");	}
+	else
+	{   &call	("_aesni_decrypt1");	}
+	&xorps	($inout0,$inout3);		# output^=tweak
+	&movups	(&QWP(16*0,$out),$inout0);	# write output
+	&lea	($out,&DWP(16*1,$out));
+
+	&movdqa	($tweak,$inout3);		# last tweak
+	&jmp	(&label("xts_dec_done"));
+
+&set_label("xts_dec_two",16);
+	&movaps	($inout4,$tweak);		# put aside last tweak
+
+	&movups	($inout0,&QWP(16*0,$inp));	# load input
+	&movups	($inout1,&QWP(16*1,$inp));
+	&lea	($inp,&DWP(16*2,$inp));
+	&xorps	($inout0,$inout3);		# input^=tweak
+	&xorps	($inout1,$inout4);
+
+	&call	("_aesni_decrypt2");
+
+	&xorps	($inout0,$inout3);		# output^=tweak
+	&xorps	($inout1,$inout4);
+	&movups	(&QWP(16*0,$out),$inout0);	# write output
+	&movups	(&QWP(16*1,$out),$inout1);
+	&lea	($out,&DWP(16*2,$out));
+
+	&movdqa	($tweak,$inout4);		# last tweak
+	&jmp	(&label("xts_dec_done"));
+
+&set_label("xts_dec_three",16);
+	&movaps	($inout5,$tweak);		# put aside last tweak
+	&movups	($inout0,&QWP(16*0,$inp));	# load input
+	&movups	($inout1,&QWP(16*1,$inp));
+	&movups	($inout2,&QWP(16*2,$inp));
+	&lea	($inp,&DWP(16*3,$inp));
+	&xorps	($inout0,$inout3);		# input^=tweak
+	&xorps	($inout1,$inout4);
+	&xorps	($inout2,$inout5);
+
+	&call	("_aesni_decrypt3");
+
+	&xorps	($inout0,$inout3);		# output^=tweak
+	&xorps	($inout1,$inout4);
+	&xorps	($inout2,$inout5);
+	&movups	(&QWP(16*0,$out),$inout0);	# write output
+	&movups	(&QWP(16*1,$out),$inout1);
+	&movups	(&QWP(16*2,$out),$inout2);
+	&lea	($out,&DWP(16*3,$out));
+
+	&movdqa	($tweak,$inout5);		# last tweak
+	&jmp	(&label("xts_dec_done"));
+
+&set_label("xts_dec_four",16);
+	&movaps	($inout4,$tweak);		# put aside last tweak
+
+	&movups	($inout0,&QWP(16*0,$inp));	# load input
+	&movups	($inout1,&QWP(16*1,$inp));
+	&movups	($inout2,&QWP(16*2,$inp));
+	&xorps	($inout0,&QWP(16*0,"esp"));	# input^=tweak
+	&movups	($inout3,&QWP(16*3,$inp));
+	&lea	($inp,&DWP(16*4,$inp));
+	&xorps	($inout1,&QWP(16*1,"esp"));
+	&xorps	($inout2,$inout5);
+	&xorps	($inout3,$inout4);
+
+	&call	("_aesni_decrypt4");
+
+	&xorps	($inout0,&QWP(16*0,"esp"));	# output^=tweak
+	&xorps	($inout1,&QWP(16*1,"esp"));
+	&xorps	($inout2,$inout5);
+	&movups	(&QWP(16*0,$out),$inout0);	# write output
+	&xorps	($inout3,$inout4);
+	&movups	(&QWP(16*1,$out),$inout1);
+	&movups	(&QWP(16*2,$out),$inout2);
+	&movups	(&QWP(16*3,$out),$inout3);
+	&lea	($out,&DWP(16*4,$out));
+
+	&movdqa	($tweak,$inout4);		# last tweak
+	&jmp	(&label("xts_dec_done"));
+
+&set_label("xts_dec_done6x",16);		# $tweak is pre-calculated
+	&mov	($len,&DWP(16*7+0,"esp"));	# restore original $len
+	&and	($len,15);
+	&jz	(&label("xts_dec_ret"));
+	&mov	(&DWP(16*7+0,"esp"),$len);	# save $len%16
+	&jmp	(&label("xts_dec_only_one_more"));
+
+&set_label("xts_dec_done",16);
+	&mov	($len,&DWP(16*7+0,"esp"));	# restore original $len
+	&pxor	($twtmp,$twtmp);
+	&and	($len,15);
+	&jz	(&label("xts_dec_ret"));
+
+	&pcmpgtd($twtmp,$tweak);		# broadcast upper bits
+	&mov	(&DWP(16*7+0,"esp"),$len);	# save $len%16
+	&pshufd	($twres,$twtmp,0x13);
+	&pxor	($twtmp,$twtmp);
+	&movdqa	($twmask,&QWP(16*6,"esp"));
+	&paddq	($tweak,$tweak);		# &psllq($tweak,1);
+	&pand	($twres,$twmask);		# isolate carry and residue
+	&pcmpgtd($twtmp,$tweak);		# broadcast upper bits
+	&pxor	($tweak,$twres);
+
+&set_label("xts_dec_only_one_more");
+	&pshufd	($inout3,$twtmp,0x13);
+	&movdqa	($inout4,$tweak);		# put aside previous tweak
+	&paddq	($tweak,$tweak);		# &psllq($tweak,1);
+	&pand	($inout3,$twmask);		# isolate carry and residue
+	&pxor	($inout3,$tweak);
+
+	&mov	($key,$key_);			# restore $key
+	&mov	($rounds,$rounds_);		# restore $rounds
+
+	&movups	($inout0,&QWP(0,$inp));		# load input
+	&xorps	($inout0,$inout3);		# input^=tweak
+	if ($inline)
+	{   &aesni_inline_generate1("dec");	}
+	else
+	{   &call	("_aesni_decrypt1");	}
+	&xorps	($inout0,$inout3);		# output^=tweak
+	&movups	(&QWP(0,$out),$inout0);		# write output
+
+&set_label("xts_dec_steal");
+	&movz	($rounds,&BP(16,$inp));
+	&movz	($key,&BP(0,$out));
+	&lea	($inp,&DWP(1,$inp));
+	&mov	(&BP(0,$out),&LB($rounds));
+	&mov	(&BP(16,$out),&LB($key));
+	&lea	($out,&DWP(1,$out));
+	&sub	($len,1);
+	&jnz	(&label("xts_dec_steal"));
+
+	&sub	($out,&DWP(16*7+0,"esp"));	# rewind $out
+	&mov	($key,$key_);			# restore $key
+	&mov	($rounds,$rounds_);		# restore $rounds
+
+	&movups	($inout0,&QWP(0,$out));		# load input
+	&xorps	($inout0,$inout4);		# input^=tweak
+	if ($inline)
+	{   &aesni_inline_generate1("dec");	}
+	else
+	{   &call	("_aesni_decrypt1");	}
+	&xorps	($inout0,$inout4);		# output^=tweak
+	&movups	(&QWP(0,$out),$inout0);		# write output
+
+&set_label("xts_dec_ret");
+	&pxor	("xmm0","xmm0");		# clear register bank
+	&pxor	("xmm1","xmm1");
+	&pxor	("xmm2","xmm2");
+	&movdqa	(&QWP(16*0,"esp"),"xmm0");	# clear stack
+	&pxor	("xmm3","xmm3");
+	&movdqa	(&QWP(16*1,"esp"),"xmm0");
+	&pxor	("xmm4","xmm4");
+	&movdqa	(&QWP(16*2,"esp"),"xmm0");
+	&pxor	("xmm5","xmm5");
+	&movdqa	(&QWP(16*3,"esp"),"xmm0");
+	&pxor	("xmm6","xmm6");
+	&movdqa	(&QWP(16*4,"esp"),"xmm0");
+	&pxor	("xmm7","xmm7");
+	&movdqa	(&QWP(16*5,"esp"),"xmm0");
+	&mov	("esp",&DWP(16*7+4,"esp"));	# restore %esp
+&function_end("aesni_xts_decrypt");
+}
+
+######################################################################
+# void aesni_ocb_[en|de]crypt(const char *inp, char *out, size_t blocks,
+#	const AES_KEY *key, unsigned int start_block_num,
+#	unsigned char offset_i[16], const unsigned char L_[][16],
+#	unsigned char checksum[16]);
+#
+{
+# offsets within stack frame
+my $checksum = 16*6;
+my ($key_off,$rounds_off,$out_off,$end_off,$esp_off)=map(16*7+4*$_,(0..4));
+
+# reassigned registers
+my ($l_,$block,$i1,$i3,$i5) = ($rounds_,$key_,$rounds,$len,$out);
+# $l_, $blocks, $inp, $key are permanently allocated in registers;
+# remaining non-volatile ones are offloaded to stack, which even
+# stay invariant after written to stack.
+
+&function_begin("aesni_ocb_encrypt");
+	&mov	($rounds,&wparam(5));		# &offset_i
+	&mov	($rounds_,&wparam(7));		# &checksum
+
+	&mov	($inp,&wparam(0));
+	&mov	($out,&wparam(1));
+	&mov	($len,&wparam(2));
+	&mov	($key,&wparam(3));
+	&movdqu	($rndkey0,&QWP(0,$rounds));	# load offset_i
+	&mov	($block,&wparam(4));		# start_block_num
+	&movdqu	($rndkey1,&QWP(0,$rounds_));	# load checksum
+	&mov	($l_,&wparam(6));		# L_
+
+	&mov	($rounds,"esp");
+	&sub	("esp",$esp_off+4);		# alloca
+	&and	("esp",-16);			# align stack
+
+	&sub	($out,$inp);
+	&shl	($len,4);
+	&lea	($len,&DWP(-16*6,$inp,$len));	# end of input - 16*6
+	&mov	(&DWP($out_off,"esp"),$out);
+	&mov	(&DWP($end_off,"esp"),$len);
+	&mov	(&DWP($esp_off,"esp"),$rounds);
+
+	&mov	($rounds,&DWP(240,$key));
+
+	&test	($block,1);
+	&jnz	(&label("odd"));
+
+	&bsf		($i3,$block);
+	&add		($block,1);
+	&shl		($i3,4);
+	&movdqu		($inout5,&QWP(0,$l_,$i3));
+	&mov		($i3,$key);			# put aside key
+
+	&movdqu		($inout0,&QWP(16*0,$inp));	# load input
+	&lea		($inp,&DWP(16,$inp));
+
+	&pxor		($inout5,$rndkey0);		# ^ last offset_i
+	&pxor		($rndkey1,$inout0);		# checksum
+	&pxor		($inout0,$inout5);		# ^ offset_i
+
+	&movdqa		($inout4,$rndkey1);
+	if ($inline)
+	{   &aesni_inline_generate1("enc");	}
+	else
+	{   &call	("_aesni_encrypt1");	}
+
+	&xorps		($inout0,$inout5);		# ^ offset_i
+	&movdqa		($rndkey0,$inout5);		# pass last offset_i
+	&movdqa		($rndkey1,$inout4);		# pass the checksum
+
+	&movups		(&QWP(-16,$out,$inp),$inout0);	# store output
+
+	&mov		($rounds,&DWP(240,$i3));
+	&mov		($key,$i3);			# restore key
+	&mov		($len,&DWP($end_off,"esp"));
+
+&set_label("odd");
+	&shl		($rounds,4);
+	&mov		($out,16);
+	&sub		($out,$rounds);			# twisted rounds
+	&mov		(&DWP($key_off,"esp"),$key);
+	&lea		($key,&DWP(32,$key,$rounds));	# end of key schedule
+	&mov		(&DWP($rounds_off,"esp"),$out);
+
+	&cmp		($inp,$len);
+	&ja		(&label("short"));
+	&jmp		(&label("grandloop"));
+
+&set_label("grandloop",32);
+	&lea		($i1,&DWP(1,$block));
+	&lea		($i3,&DWP(3,$block));
+	&lea		($i5,&DWP(5,$block));
+	&add		($block,6);
+	&bsf		($i1,$i1);
+	&bsf		($i3,$i3);
+	&bsf		($i5,$i5);
+	&shl		($i1,4);
+	&shl		($i3,4);
+	&shl		($i5,4);
+	&movdqu		($inout0,&QWP(0,$l_));
+	&movdqu		($inout1,&QWP(0,$l_,$i1));
+	&mov		($rounds,&DWP($rounds_off,"esp"));
+	&movdqa		($inout2,$inout0);
+	&movdqu		($inout3,&QWP(0,$l_,$i3));
+	&movdqa		($inout4,$inout0);
+	&movdqu		($inout5,&QWP(0,$l_,$i5));
+
+	&pxor		($inout0,$rndkey0);		# ^ last offset_i
+	&pxor		($inout1,$inout0);
+	&movdqa		(&QWP(16*0,"esp"),$inout0);
+	&pxor		($inout2,$inout1);
+	&movdqa		(&QWP(16*1,"esp"),$inout1);
+	&pxor		($inout3,$inout2);
+	&movdqa		(&QWP(16*2,"esp"),$inout2);
+	&pxor		($inout4,$inout3);
+	&movdqa		(&QWP(16*3,"esp"),$inout3);
+	&pxor		($inout5,$inout4);
+	&movdqa		(&QWP(16*4,"esp"),$inout4);
+	&movdqa		(&QWP(16*5,"esp"),$inout5);
+
+	&$movekey	($rndkey0,&QWP(-48,$key,$rounds));
+	&movdqu		($inout0,&QWP(16*0,$inp));	# load input
+	&movdqu		($inout1,&QWP(16*1,$inp));
+	&movdqu		($inout2,&QWP(16*2,$inp));
+	&movdqu		($inout3,&QWP(16*3,$inp));
+	&movdqu		($inout4,&QWP(16*4,$inp));
+	&movdqu		($inout5,&QWP(16*5,$inp));
+	&lea		($inp,&DWP(16*6,$inp));
+
+	&pxor		($rndkey1,$inout0);		# checksum
+	&pxor		($inout0,$rndkey0);		# ^ roundkey[0]
+	&pxor		($rndkey1,$inout1);
+	&pxor		($inout1,$rndkey0);
+	&pxor		($rndkey1,$inout2);
+	&pxor		($inout2,$rndkey0);
+	&pxor		($rndkey1,$inout3);
+	&pxor		($inout3,$rndkey0);
+	&pxor		($rndkey1,$inout4);
+	&pxor		($inout4,$rndkey0);
+	&pxor		($rndkey1,$inout5);
+	&pxor		($inout5,$rndkey0);
+	&movdqa		(&QWP($checksum,"esp"),$rndkey1);
+
+	&$movekey	($rndkey1,&QWP(-32,$key,$rounds));
+	&pxor		($inout0,&QWP(16*0,"esp"));	# ^ offset_i
+	&pxor		($inout1,&QWP(16*1,"esp"));
+	&pxor		($inout2,&QWP(16*2,"esp"));
+	&pxor		($inout3,&QWP(16*3,"esp"));
+	&pxor		($inout4,&QWP(16*4,"esp"));
+	&pxor		($inout5,&QWP(16*5,"esp"));
+
+	&$movekey	($rndkey0,&QWP(-16,$key,$rounds));
+	&aesenc		($inout0,$rndkey1);
+	&aesenc		($inout1,$rndkey1);
+	&aesenc		($inout2,$rndkey1);
+	&aesenc		($inout3,$rndkey1);
+	&aesenc		($inout4,$rndkey1);
+	&aesenc		($inout5,$rndkey1);
+
+	&mov		($out,&DWP($out_off,"esp"));
+	&mov		($len,&DWP($end_off,"esp"));
+	&call		("_aesni_encrypt6_enter");
+
+	&movdqa		($rndkey0,&QWP(16*5,"esp"));	# pass last offset_i
+	&pxor		($inout0,&QWP(16*0,"esp"));	# ^ offset_i
+	&pxor		($inout1,&QWP(16*1,"esp"));
+	&pxor		($inout2,&QWP(16*2,"esp"));
+	&pxor		($inout3,&QWP(16*3,"esp"));
+	&pxor		($inout4,&QWP(16*4,"esp"));
+	&pxor		($inout5,$rndkey0);
+	&movdqa		($rndkey1,&QWP($checksum,"esp"));# pass the checksum
+
+	&movdqu		(&QWP(-16*6,$out,$inp),$inout0);# store output
+	&movdqu		(&QWP(-16*5,$out,$inp),$inout1);
+	&movdqu		(&QWP(-16*4,$out,$inp),$inout2);
+	&movdqu		(&QWP(-16*3,$out,$inp),$inout3);
+	&movdqu		(&QWP(-16*2,$out,$inp),$inout4);
+	&movdqu		(&QWP(-16*1,$out,$inp),$inout5);
+	&cmp		($inp,$len);			# done yet?
+	&jb		(&label("grandloop"));
+
+&set_label("short");
+	&add		($len,16*6);
+	&sub		($len,$inp);
+	&jz		(&label("done"));
+
+	&cmp		($len,16*2);
+	&jb		(&label("one"));
+	&je		(&label("two"));
+
+	&cmp		($len,16*4);
+	&jb		(&label("three"));
+	&je		(&label("four"));
+
+	&lea		($i1,&DWP(1,$block));
+	&lea		($i3,&DWP(3,$block));
+	&bsf		($i1,$i1);
+	&bsf		($i3,$i3);
+	&shl		($i1,4);
+	&shl		($i3,4);
+	&movdqu		($inout0,&QWP(0,$l_));
+	&movdqu		($inout1,&QWP(0,$l_,$i1));
+	&mov		($rounds,&DWP($rounds_off,"esp"));
+	&movdqa		($inout2,$inout0);
+	&movdqu		($inout3,&QWP(0,$l_,$i3));
+	&movdqa		($inout4,$inout0);
+
+	&pxor		($inout0,$rndkey0);		# ^ last offset_i
+	&pxor		($inout1,$inout0);
+	&movdqa		(&QWP(16*0,"esp"),$inout0);
+	&pxor		($inout2,$inout1);
+	&movdqa		(&QWP(16*1,"esp"),$inout1);
+	&pxor		($inout3,$inout2);
+	&movdqa		(&QWP(16*2,"esp"),$inout2);
+	&pxor		($inout4,$inout3);
+	&movdqa		(&QWP(16*3,"esp"),$inout3);
+	&pxor		($inout5,$inout4);
+	&movdqa		(&QWP(16*4,"esp"),$inout4);
+
+	&$movekey	($rndkey0,&QWP(-48,$key,$rounds));
+	&movdqu		($inout0,&QWP(16*0,$inp));	# load input
+	&movdqu		($inout1,&QWP(16*1,$inp));
+	&movdqu		($inout2,&QWP(16*2,$inp));
+	&movdqu		($inout3,&QWP(16*3,$inp));
+	&movdqu		($inout4,&QWP(16*4,$inp));
+	&pxor		($inout5,$inout5);
+
+	&pxor		($rndkey1,$inout0);		# checksum
+	&pxor		($inout0,$rndkey0);		# ^ roundkey[0]
+	&pxor		($rndkey1,$inout1);
+	&pxor		($inout1,$rndkey0);
+	&pxor		($rndkey1,$inout2);
+	&pxor		($inout2,$rndkey0);
+	&pxor		($rndkey1,$inout3);
+	&pxor		($inout3,$rndkey0);
+	&pxor		($rndkey1,$inout4);
+	&pxor		($inout4,$rndkey0);
+	&movdqa		(&QWP($checksum,"esp"),$rndkey1);
+
+	&$movekey	($rndkey1,&QWP(-32,$key,$rounds));
+	&pxor		($inout0,&QWP(16*0,"esp"));	# ^ offset_i
+	&pxor		($inout1,&QWP(16*1,"esp"));
+	&pxor		($inout2,&QWP(16*2,"esp"));
+	&pxor		($inout3,&QWP(16*3,"esp"));
+	&pxor		($inout4,&QWP(16*4,"esp"));
+
+	&$movekey	($rndkey0,&QWP(-16,$key,$rounds));
+	&aesenc		($inout0,$rndkey1);
+	&aesenc		($inout1,$rndkey1);
+	&aesenc		($inout2,$rndkey1);
+	&aesenc		($inout3,$rndkey1);
+	&aesenc		($inout4,$rndkey1);
+	&aesenc		($inout5,$rndkey1);
+
+	&mov		($out,&DWP($out_off,"esp"));
+	&call		("_aesni_encrypt6_enter");
+
+	&movdqa		($rndkey0,&QWP(16*4,"esp"));	# pass last offset_i
+	&pxor		($inout0,&QWP(16*0,"esp"));	# ^ offset_i
+	&pxor		($inout1,&QWP(16*1,"esp"));
+	&pxor		($inout2,&QWP(16*2,"esp"));
+	&pxor		($inout3,&QWP(16*3,"esp"));
+	&pxor		($inout4,$rndkey0);
+	&movdqa		($rndkey1,&QWP($checksum,"esp"));# pass the checksum
+
+	&movdqu		(&QWP(16*0,$out,$inp),$inout0);	# store output
+	&movdqu		(&QWP(16*1,$out,$inp),$inout1);
+	&movdqu		(&QWP(16*2,$out,$inp),$inout2);
+	&movdqu		(&QWP(16*3,$out,$inp),$inout3);
+	&movdqu		(&QWP(16*4,$out,$inp),$inout4);
+
+	&jmp		(&label("done"));
+
+&set_label("one",16);
+	&movdqu		($inout5,&QWP(0,$l_));
+	&mov		($key,&DWP($key_off,"esp"));	# restore key
+
+	&movdqu		($inout0,&QWP(16*0,$inp));	# load input
+	&mov		($rounds,&DWP(240,$key));
+
+	&pxor		($inout5,$rndkey0);		# ^ last offset_i
+	&pxor		($rndkey1,$inout0);		# checksum
+	&pxor		($inout0,$inout5);		# ^ offset_i
+
+	&movdqa		($inout4,$rndkey1);
+	&mov		($out,&DWP($out_off,"esp"));
+	if ($inline)
+	{   &aesni_inline_generate1("enc");	}
+	else
+	{   &call	("_aesni_encrypt1");	}
+
+	&xorps		($inout0,$inout5);		# ^ offset_i
+	&movdqa		($rndkey0,$inout5);		# pass last offset_i
+	&movdqa		($rndkey1,$inout4);		# pass the checksum
+	&movups		(&QWP(0,$out,$inp),$inout0);
+
+	&jmp		(&label("done"));
+
+&set_label("two",16);
+	&lea		($i1,&DWP(1,$block));
+	&mov		($key,&DWP($key_off,"esp"));	# restore key
+	&bsf		($i1,$i1);
+	&shl		($i1,4);
+	&movdqu		($inout4,&QWP(0,$l_));
+	&movdqu		($inout5,&QWP(0,$l_,$i1));
+
+	&movdqu		($inout0,&QWP(16*0,$inp));	# load input
+	&movdqu		($inout1,&QWP(16*1,$inp));
+	&mov		($rounds,&DWP(240,$key));
+
+	&pxor		($inout4,$rndkey0);		# ^ last offset_i
+	&pxor		($inout5,$inout4);
+
+	&pxor		($rndkey1,$inout0);		# checksum
+	&pxor		($inout0,$inout4);		# ^ offset_i
+	&pxor		($rndkey1,$inout1);
+	&pxor		($inout1,$inout5);
+
+	&movdqa		($inout3,$rndkey1)
+	&mov		($out,&DWP($out_off,"esp"));
+	&call		("_aesni_encrypt2");
+
+	&xorps		($inout0,$inout4);		# ^ offset_i
+	&xorps		($inout1,$inout5);
+	&movdqa		($rndkey0,$inout5);		# pass last offset_i
+	&movdqa		($rndkey1,$inout3);		# pass the checksum
+	&movups		(&QWP(16*0,$out,$inp),$inout0);	# store output
+	&movups		(&QWP(16*1,$out,$inp),$inout1);
+
+	&jmp		(&label("done"));
+
+&set_label("three",16);
+	&lea		($i1,&DWP(1,$block));
+	&mov		($key,&DWP($key_off,"esp"));	# restore key
+	&bsf		($i1,$i1);
+	&shl		($i1,4);
+	&movdqu		($inout3,&QWP(0,$l_));
+	&movdqu		($inout4,&QWP(0,$l_,$i1));
+	&movdqa		($inout5,$inout3);
+
+	&movdqu		($inout0,&QWP(16*0,$inp));	# load input
+	&movdqu		($inout1,&QWP(16*1,$inp));
+	&movdqu		($inout2,&QWP(16*2,$inp));
+	&mov		($rounds,&DWP(240,$key));
+
+	&pxor		($inout3,$rndkey0);		# ^ last offset_i
+	&pxor		($inout4,$inout3);
+	&pxor		($inout5,$inout4);
+
+	&pxor		($rndkey1,$inout0);		# checksum
+	&pxor		($inout0,$inout3);		# ^ offset_i
+	&pxor		($rndkey1,$inout1);
+	&pxor		($inout1,$inout4);
+	&pxor		($rndkey1,$inout2);
+	&pxor		($inout2,$inout5);
+
+	&movdqa		(&QWP($checksum,"esp"),$rndkey1);
+	&mov		($out,&DWP($out_off,"esp"));
+	&call		("_aesni_encrypt3");
+
+	&xorps		($inout0,$inout3);		# ^ offset_i
+	&xorps		($inout1,$inout4);
+	&xorps		($inout2,$inout5);
+	&movdqa		($rndkey0,$inout5);		# pass last offset_i
+	&movdqa		($rndkey1,&QWP($checksum,"esp"));# pass the checksum
+	&movups		(&QWP(16*0,$out,$inp),$inout0);	# store output
+	&movups		(&QWP(16*1,$out,$inp),$inout1);
+	&movups		(&QWP(16*2,$out,$inp),$inout2);
+
+	&jmp		(&label("done"));
+
+&set_label("four",16);
+	&lea		($i1,&DWP(1,$block));
+	&lea		($i3,&DWP(3,$block));
+	&bsf		($i1,$i1);
+	&bsf		($i3,$i3);
+	&mov		($key,&DWP($key_off,"esp"));	# restore key
+	&shl		($i1,4);
+	&shl		($i3,4);
+	&movdqu		($inout2,&QWP(0,$l_));
+	&movdqu		($inout3,&QWP(0,$l_,$i1));
+	&movdqa		($inout4,$inout2);
+	&movdqu		($inout5,&QWP(0,$l_,$i3));
+
+	&pxor		($inout2,$rndkey0);		# ^ last offset_i
+	&movdqu		($inout0,&QWP(16*0,$inp));	# load input
+	&pxor		($inout3,$inout2);
+	&movdqu		($inout1,&QWP(16*1,$inp));
+	&pxor		($inout4,$inout3);
+	&movdqa		(&QWP(16*0,"esp"),$inout2);
+	&pxor		($inout5,$inout4);
+	&movdqa		(&QWP(16*1,"esp"),$inout3);
+	&movdqu		($inout2,&QWP(16*2,$inp));
+	&movdqu		($inout3,&QWP(16*3,$inp));
+	&mov		($rounds,&DWP(240,$key));
+
+	&pxor		($rndkey1,$inout0);		# checksum
+	&pxor		($inout0,&QWP(16*0,"esp"));	# ^ offset_i
+	&pxor		($rndkey1,$inout1);
+	&pxor		($inout1,&QWP(16*1,"esp"));
+	&pxor		($rndkey1,$inout2);
+	&pxor		($inout2,$inout4);
+	&pxor		($rndkey1,$inout3);
+	&pxor		($inout3,$inout5);
+
+	&movdqa		(&QWP($checksum,"esp"),$rndkey1)
+	&mov		($out,&DWP($out_off,"esp"));
+	&call		("_aesni_encrypt4");
+
+	&xorps		($inout0,&QWP(16*0,"esp"));	# ^ offset_i
+	&xorps		($inout1,&QWP(16*1,"esp"));
+	&xorps		($inout2,$inout4);
+	&movups		(&QWP(16*0,$out,$inp),$inout0);	# store output
+	&xorps		($inout3,$inout5);
+	&movups		(&QWP(16*1,$out,$inp),$inout1);
+	&movdqa		($rndkey0,$inout5);		# pass last offset_i
+	&movups		(&QWP(16*2,$out,$inp),$inout2);
+	&movdqa		($rndkey1,&QWP($checksum,"esp"));# pass the checksum
+	&movups		(&QWP(16*3,$out,$inp),$inout3);
+
+&set_label("done");
+	&mov	($key,&DWP($esp_off,"esp"));
+	&pxor	($inout0,$inout0);		# clear register bank
+	&pxor	($inout1,$inout1);
+	&movdqa	(&QWP(16*0,"esp"),$inout0);	# clear stack
+	&pxor	($inout2,$inout2);
+	&movdqa	(&QWP(16*1,"esp"),$inout0);
+	&pxor	($inout3,$inout3);
+	&movdqa	(&QWP(16*2,"esp"),$inout0);
+	&pxor	($inout4,$inout4);
+	&movdqa	(&QWP(16*3,"esp"),$inout0);
+	&pxor	($inout5,$inout5);
+	&movdqa	(&QWP(16*4,"esp"),$inout0);
+	&movdqa	(&QWP(16*5,"esp"),$inout0);
+	&movdqa	(&QWP(16*6,"esp"),$inout0);
+
+	&lea	("esp",&DWP(0,$key));
+	&mov	($rounds,&wparam(5));		# &offset_i
+	&mov	($rounds_,&wparam(7));		# &checksum
+	&movdqu	(&QWP(0,$rounds),$rndkey0);
+	&pxor	($rndkey0,$rndkey0);
+	&movdqu	(&QWP(0,$rounds_),$rndkey1);
+	&pxor	($rndkey1,$rndkey1);
+&function_end("aesni_ocb_encrypt");
+
+&function_begin("aesni_ocb_decrypt");
+	&mov	($rounds,&wparam(5));		# &offset_i
+	&mov	($rounds_,&wparam(7));		# &checksum
+
+	&mov	($inp,&wparam(0));
+	&mov	($out,&wparam(1));
+	&mov	($len,&wparam(2));
+	&mov	($key,&wparam(3));
+	&movdqu	($rndkey0,&QWP(0,$rounds));	# load offset_i
+	&mov	($block,&wparam(4));		# start_block_num
+	&movdqu	($rndkey1,&QWP(0,$rounds_));	# load checksum
+	&mov	($l_,&wparam(6));		# L_
+
+	&mov	($rounds,"esp");
+	&sub	("esp",$esp_off+4);		# alloca
+	&and	("esp",-16);			# align stack
+
+	&sub	($out,$inp);
+	&shl	($len,4);
+	&lea	($len,&DWP(-16*6,$inp,$len));	# end of input - 16*6
+	&mov	(&DWP($out_off,"esp"),$out);
+	&mov	(&DWP($end_off,"esp"),$len);
+	&mov	(&DWP($esp_off,"esp"),$rounds);
+
+	&mov	($rounds,&DWP(240,$key));
+
+	&test	($block,1);
+	&jnz	(&label("odd"));
+
+	&bsf		($i3,$block);
+	&add		($block,1);
+	&shl		($i3,4);
+	&movdqu		($inout5,&QWP(0,$l_,$i3));
+	&mov		($i3,$key);			# put aside key
+
+	&movdqu		($inout0,&QWP(16*0,$inp));	# load input
+	&lea		($inp,&DWP(16,$inp));
+
+	&pxor		($inout5,$rndkey0);		# ^ last offset_i
+	&pxor		($inout0,$inout5);		# ^ offset_i
+
+	&movdqa		($inout4,$rndkey1);
+	if ($inline)
+	{   &aesni_inline_generate1("dec");	}
+	else
+	{   &call	("_aesni_decrypt1");	}
+
+	&xorps		($inout0,$inout5);		# ^ offset_i
+	&movaps		($rndkey1,$inout4);		# pass the checksum
+	&movdqa		($rndkey0,$inout5);		# pass last offset_i
+	&xorps		($rndkey1,$inout0);		# checksum
+	&movups		(&QWP(-16,$out,$inp),$inout0);	# store output
+
+	&mov		($rounds,&DWP(240,$i3));
+	&mov		($key,$i3);			# restore key
+	&mov		($len,&DWP($end_off,"esp"));
+
+&set_label("odd");
+	&shl		($rounds,4);
+	&mov		($out,16);
+	&sub		($out,$rounds);			# twisted rounds
+	&mov		(&DWP($key_off,"esp"),$key);
+	&lea		($key,&DWP(32,$key,$rounds));	# end of key schedule
+	&mov		(&DWP($rounds_off,"esp"),$out);
+
+	&cmp		($inp,$len);
+	&ja		(&label("short"));
+	&jmp		(&label("grandloop"));
+
+&set_label("grandloop",32);
+	&lea		($i1,&DWP(1,$block));
+	&lea		($i3,&DWP(3,$block));
+	&lea		($i5,&DWP(5,$block));
+	&add		($block,6);
+	&bsf		($i1,$i1);
+	&bsf		($i3,$i3);
+	&bsf		($i5,$i5);
+	&shl		($i1,4);
+	&shl		($i3,4);
+	&shl		($i5,4);
+	&movdqu		($inout0,&QWP(0,$l_));
+	&movdqu		($inout1,&QWP(0,$l_,$i1));
+	&mov		($rounds,&DWP($rounds_off,"esp"));
+	&movdqa		($inout2,$inout0);
+	&movdqu		($inout3,&QWP(0,$l_,$i3));
+	&movdqa		($inout4,$inout0);
+	&movdqu		($inout5,&QWP(0,$l_,$i5));
+
+	&pxor		($inout0,$rndkey0);		# ^ last offset_i
+	&pxor		($inout1,$inout0);
+	&movdqa		(&QWP(16*0,"esp"),$inout0);
+	&pxor		($inout2,$inout1);
+	&movdqa		(&QWP(16*1,"esp"),$inout1);
+	&pxor		($inout3,$inout2);
+	&movdqa		(&QWP(16*2,"esp"),$inout2);
+	&pxor		($inout4,$inout3);
+	&movdqa		(&QWP(16*3,"esp"),$inout3);
+	&pxor		($inout5,$inout4);
+	&movdqa		(&QWP(16*4,"esp"),$inout4);
+	&movdqa		(&QWP(16*5,"esp"),$inout5);
+
+	&$movekey	($rndkey0,&QWP(-48,$key,$rounds));
+	&movdqu		($inout0,&QWP(16*0,$inp));	# load input
+	&movdqu		($inout1,&QWP(16*1,$inp));
+	&movdqu		($inout2,&QWP(16*2,$inp));
+	&movdqu		($inout3,&QWP(16*3,$inp));
+	&movdqu		($inout4,&QWP(16*4,$inp));
+	&movdqu		($inout5,&QWP(16*5,$inp));
+	&lea		($inp,&DWP(16*6,$inp));
+
+	&movdqa		(&QWP($checksum,"esp"),$rndkey1);
+	&pxor		($inout0,$rndkey0);		# ^ roundkey[0]
+	&pxor		($inout1,$rndkey0);
+	&pxor		($inout2,$rndkey0);
+	&pxor		($inout3,$rndkey0);
+	&pxor		($inout4,$rndkey0);
+	&pxor		($inout5,$rndkey0);
+
+	&$movekey	($rndkey1,&QWP(-32,$key,$rounds));
+	&pxor		($inout0,&QWP(16*0,"esp"));	# ^ offset_i
+	&pxor		($inout1,&QWP(16*1,"esp"));
+	&pxor		($inout2,&QWP(16*2,"esp"));
+	&pxor		($inout3,&QWP(16*3,"esp"));
+	&pxor		($inout4,&QWP(16*4,"esp"));
+	&pxor		($inout5,&QWP(16*5,"esp"));
+
+	&$movekey	($rndkey0,&QWP(-16,$key,$rounds));
+	&aesdec		($inout0,$rndkey1);
+	&aesdec		($inout1,$rndkey1);
+	&aesdec		($inout2,$rndkey1);
+	&aesdec		($inout3,$rndkey1);
+	&aesdec		($inout4,$rndkey1);
+	&aesdec		($inout5,$rndkey1);
+
+	&mov		($out,&DWP($out_off,"esp"));
+	&mov		($len,&DWP($end_off,"esp"));
+	&call		("_aesni_decrypt6_enter");
+
+	&movdqa		($rndkey0,&QWP(16*5,"esp"));	# pass last offset_i
+	&pxor		($inout0,&QWP(16*0,"esp"));	# ^ offset_i
+	&movdqa		($rndkey1,&QWP($checksum,"esp"));
+	&pxor		($inout1,&QWP(16*1,"esp"));
+	&pxor		($inout2,&QWP(16*2,"esp"));
+	&pxor		($inout3,&QWP(16*3,"esp"));
+	&pxor		($inout4,&QWP(16*4,"esp"));
+	&pxor		($inout5,$rndkey0);
+
+	&pxor		($rndkey1,$inout0);		# checksum
+	&movdqu		(&QWP(-16*6,$out,$inp),$inout0);# store output
+	&pxor		($rndkey1,$inout1);
+	&movdqu		(&QWP(-16*5,$out,$inp),$inout1);
+	&pxor		($rndkey1,$inout2);
+	&movdqu		(&QWP(-16*4,$out,$inp),$inout2);
+	&pxor		($rndkey1,$inout3);
+	&movdqu		(&QWP(-16*3,$out,$inp),$inout3);
+	&pxor		($rndkey1,$inout4);
+	&movdqu		(&QWP(-16*2,$out,$inp),$inout4);
+	&pxor		($rndkey1,$inout5);
+	&movdqu		(&QWP(-16*1,$out,$inp),$inout5);
+	&cmp		($inp,$len);			# done yet?
+	&jb		(&label("grandloop"));
+
+&set_label("short");
+	&add		($len,16*6);
+	&sub		($len,$inp);
+	&jz		(&label("done"));
+
+	&cmp		($len,16*2);
+	&jb		(&label("one"));
+	&je		(&label("two"));
+
+	&cmp		($len,16*4);
+	&jb		(&label("three"));
+	&je		(&label("four"));
+
+	&lea		($i1,&DWP(1,$block));
+	&lea		($i3,&DWP(3,$block));
+	&bsf		($i1,$i1);
+	&bsf		($i3,$i3);
+	&shl		($i1,4);
+	&shl		($i3,4);
+	&movdqu		($inout0,&QWP(0,$l_));
+	&movdqu		($inout1,&QWP(0,$l_,$i1));
+	&mov		($rounds,&DWP($rounds_off,"esp"));
+	&movdqa		($inout2,$inout0);
+	&movdqu		($inout3,&QWP(0,$l_,$i3));
+	&movdqa		($inout4,$inout0);
+
+	&pxor		($inout0,$rndkey0);		# ^ last offset_i
+	&pxor		($inout1,$inout0);
+	&movdqa		(&QWP(16*0,"esp"),$inout0);
+	&pxor		($inout2,$inout1);
+	&movdqa		(&QWP(16*1,"esp"),$inout1);
+	&pxor		($inout3,$inout2);
+	&movdqa		(&QWP(16*2,"esp"),$inout2);
+	&pxor		($inout4,$inout3);
+	&movdqa		(&QWP(16*3,"esp"),$inout3);
+	&pxor		($inout5,$inout4);
+	&movdqa		(&QWP(16*4,"esp"),$inout4);
+
+	&$movekey	($rndkey0,&QWP(-48,$key,$rounds));
+	&movdqu		($inout0,&QWP(16*0,$inp));	# load input
+	&movdqu		($inout1,&QWP(16*1,$inp));
+	&movdqu		($inout2,&QWP(16*2,$inp));
+	&movdqu		($inout3,&QWP(16*3,$inp));
+	&movdqu		($inout4,&QWP(16*4,$inp));
+	&pxor		($inout5,$inout5);
+
+	&movdqa		(&QWP($checksum,"esp"),$rndkey1);
+	&pxor		($inout0,$rndkey0);		# ^ roundkey[0]
+	&pxor		($inout1,$rndkey0);
+	&pxor		($inout2,$rndkey0);
+	&pxor		($inout3,$rndkey0);
+	&pxor		($inout4,$rndkey0);
+
+	&$movekey	($rndkey1,&QWP(-32,$key,$rounds));
+	&pxor		($inout0,&QWP(16*0,"esp"));	# ^ offset_i
+	&pxor		($inout1,&QWP(16*1,"esp"));
+	&pxor		($inout2,&QWP(16*2,"esp"));
+	&pxor		($inout3,&QWP(16*3,"esp"));
+	&pxor		($inout4,&QWP(16*4,"esp"));
+
+	&$movekey	($rndkey0,&QWP(-16,$key,$rounds));
+	&aesdec		($inout0,$rndkey1);
+	&aesdec		($inout1,$rndkey1);
+	&aesdec		($inout2,$rndkey1);
+	&aesdec		($inout3,$rndkey1);
+	&aesdec		($inout4,$rndkey1);
+	&aesdec		($inout5,$rndkey1);
+
+	&mov		($out,&DWP($out_off,"esp"));
+	&call		("_aesni_decrypt6_enter");
+
+	&movdqa		($rndkey0,&QWP(16*4,"esp"));	# pass last offset_i
+	&pxor		($inout0,&QWP(16*0,"esp"));	# ^ offset_i
+	&movdqa		($rndkey1,&QWP($checksum,"esp"));
+	&pxor		($inout1,&QWP(16*1,"esp"));
+	&pxor		($inout2,&QWP(16*2,"esp"));
+	&pxor		($inout3,&QWP(16*3,"esp"));
+	&pxor		($inout4,$rndkey0);
+
+	&pxor		($rndkey1,$inout0);		# checksum
+	&movdqu		(&QWP(16*0,$out,$inp),$inout0);	# store output
+	&pxor		($rndkey1,$inout1);
+	&movdqu		(&QWP(16*1,$out,$inp),$inout1);
+	&pxor		($rndkey1,$inout2);
+	&movdqu		(&QWP(16*2,$out,$inp),$inout2);
+	&pxor		($rndkey1,$inout3);
+	&movdqu		(&QWP(16*3,$out,$inp),$inout3);
+	&pxor		($rndkey1,$inout4);
+	&movdqu		(&QWP(16*4,$out,$inp),$inout4);
+
+	&jmp		(&label("done"));
+
+&set_label("one",16);
+	&movdqu		($inout5,&QWP(0,$l_));
+	&mov		($key,&DWP($key_off,"esp"));	# restore key
+
+	&movdqu		($inout0,&QWP(16*0,$inp));	# load input
+	&mov		($rounds,&DWP(240,$key));
+
+	&pxor		($inout5,$rndkey0);		# ^ last offset_i
+	&pxor		($inout0,$inout5);		# ^ offset_i
+
+	&movdqa		($inout4,$rndkey1);
+	&mov		($out,&DWP($out_off,"esp"));
+	if ($inline)
+	{   &aesni_inline_generate1("dec");	}
+	else
+	{   &call	("_aesni_decrypt1");	}
+
+	&xorps		($inout0,$inout5);		# ^ offset_i
+	&movaps		($rndkey1,$inout4);		# pass the checksum
+	&movdqa		($rndkey0,$inout5);		# pass last offset_i
+	&xorps		($rndkey1,$inout0);		# checksum
+	&movups		(&QWP(0,$out,$inp),$inout0);
+
+	&jmp		(&label("done"));
+
+&set_label("two",16);
+	&lea		($i1,&DWP(1,$block));
+	&mov		($key,&DWP($key_off,"esp"));	# restore key
+	&bsf		($i1,$i1);
+	&shl		($i1,4);
+	&movdqu		($inout4,&QWP(0,$l_));
+	&movdqu		($inout5,&QWP(0,$l_,$i1));
+
+	&movdqu		($inout0,&QWP(16*0,$inp));	# load input
+	&movdqu		($inout1,&QWP(16*1,$inp));
+	&mov		($rounds,&DWP(240,$key));
+
+	&movdqa		($inout3,$rndkey1);
+	&pxor		($inout4,$rndkey0);		# ^ last offset_i
+	&pxor		($inout5,$inout4);
+
+	&pxor		($inout0,$inout4);		# ^ offset_i
+	&pxor		($inout1,$inout5);
+
+	&mov		($out,&DWP($out_off,"esp"));
+	&call		("_aesni_decrypt2");
+
+	&xorps		($inout0,$inout4);		# ^ offset_i
+	&xorps		($inout1,$inout5);
+	&movdqa		($rndkey0,$inout5);		# pass last offset_i
+	&xorps		($inout3,$inout0);		# checksum
+	&movups		(&QWP(16*0,$out,$inp),$inout0);	# store output
+	&xorps		($inout3,$inout1);
+	&movups		(&QWP(16*1,$out,$inp),$inout1);
+	&movaps		($rndkey1,$inout3);		# pass the checksum
+
+	&jmp		(&label("done"));
+
+&set_label("three",16);
+	&lea		($i1,&DWP(1,$block));
+	&mov		($key,&DWP($key_off,"esp"));	# restore key
+	&bsf		($i1,$i1);
+	&shl		($i1,4);
+	&movdqu		($inout3,&QWP(0,$l_));
+	&movdqu		($inout4,&QWP(0,$l_,$i1));
+	&movdqa		($inout5,$inout3);
+
+	&movdqu		($inout0,&QWP(16*0,$inp));	# load input
+	&movdqu		($inout1,&QWP(16*1,$inp));
+	&movdqu		($inout2,&QWP(16*2,$inp));
+	&mov		($rounds,&DWP(240,$key));
+
+	&movdqa		(&QWP($checksum,"esp"),$rndkey1);
+	&pxor		($inout3,$rndkey0);		# ^ last offset_i
+	&pxor		($inout4,$inout3);
+	&pxor		($inout5,$inout4);
+
+	&pxor		($inout0,$inout3);		# ^ offset_i
+	&pxor		($inout1,$inout4);
+	&pxor		($inout2,$inout5);
+
+	&mov		($out,&DWP($out_off,"esp"));
+	&call		("_aesni_decrypt3");
+
+	&movdqa		($rndkey1,&QWP($checksum,"esp"));# pass the checksum
+	&xorps		($inout0,$inout3);		# ^ offset_i
+	&xorps		($inout1,$inout4);
+	&xorps		($inout2,$inout5);
+	&movups		(&QWP(16*0,$out,$inp),$inout0);	# store output
+	&pxor		($rndkey1,$inout0);		# checksum
+	&movdqa		($rndkey0,$inout5);		# pass last offset_i
+	&movups		(&QWP(16*1,$out,$inp),$inout1);
+	&pxor		($rndkey1,$inout1);
+	&movups		(&QWP(16*2,$out,$inp),$inout2);
+	&pxor		($rndkey1,$inout2);
+
+	&jmp		(&label("done"));
+
+&set_label("four",16);
+	&lea		($i1,&DWP(1,$block));
+	&lea		($i3,&DWP(3,$block));
+	&bsf		($i1,$i1);
+	&bsf		($i3,$i3);
+	&mov		($key,&DWP($key_off,"esp"));	# restore key
+	&shl		($i1,4);
+	&shl		($i3,4);
+	&movdqu		($inout2,&QWP(0,$l_));
+	&movdqu		($inout3,&QWP(0,$l_,$i1));
+	&movdqa		($inout4,$inout2);
+	&movdqu		($inout5,&QWP(0,$l_,$i3));
+
+	&pxor		($inout2,$rndkey0);		# ^ last offset_i
+	&movdqu		($inout0,&QWP(16*0,$inp));	# load input
+	&pxor		($inout3,$inout2);
+	&movdqu		($inout1,&QWP(16*1,$inp));
+	&pxor		($inout4,$inout3);
+	&movdqa		(&QWP(16*0,"esp"),$inout2);
+	&pxor		($inout5,$inout4);
+	&movdqa		(&QWP(16*1,"esp"),$inout3);
+	&movdqu		($inout2,&QWP(16*2,$inp));
+	&movdqu		($inout3,&QWP(16*3,$inp));
+	&mov		($rounds,&DWP(240,$key));
+
+	&movdqa		(&QWP($checksum,"esp"),$rndkey1);
+	&pxor		($inout0,&QWP(16*0,"esp"));	# ^ offset_i
+	&pxor		($inout1,&QWP(16*1,"esp"));
+	&pxor		($inout2,$inout4);
+	&pxor		($inout3,$inout5);
+
+	&mov		($out,&DWP($out_off,"esp"));
+	&call		("_aesni_decrypt4");
+
+	&movdqa		($rndkey1,&QWP($checksum,"esp"));# pass the checksum
+	&xorps		($inout0,&QWP(16*0,"esp"));	# ^ offset_i
+	&xorps		($inout1,&QWP(16*1,"esp"));
+	&xorps		($inout2,$inout4);
+	&movups		(&QWP(16*0,$out,$inp),$inout0);	# store output
+	&pxor		($rndkey1,$inout0);		# checksum
+	&xorps		($inout3,$inout5);
+	&movups		(&QWP(16*1,$out,$inp),$inout1);
+	&pxor		($rndkey1,$inout1);
+	&movdqa		($rndkey0,$inout5);		# pass last offset_i
+	&movups		(&QWP(16*2,$out,$inp),$inout2);
+	&pxor		($rndkey1,$inout2);
+	&movups		(&QWP(16*3,$out,$inp),$inout3);
+	&pxor		($rndkey1,$inout3);
+
+&set_label("done");
+	&mov	($key,&DWP($esp_off,"esp"));
+	&pxor	($inout0,$inout0);		# clear register bank
+	&pxor	($inout1,$inout1);
+	&movdqa	(&QWP(16*0,"esp"),$inout0);	# clear stack
+	&pxor	($inout2,$inout2);
+	&movdqa	(&QWP(16*1,"esp"),$inout0);
+	&pxor	($inout3,$inout3);
+	&movdqa	(&QWP(16*2,"esp"),$inout0);
+	&pxor	($inout4,$inout4);
+	&movdqa	(&QWP(16*3,"esp"),$inout0);
+	&pxor	($inout5,$inout5);
+	&movdqa	(&QWP(16*4,"esp"),$inout0);
+	&movdqa	(&QWP(16*5,"esp"),$inout0);
+	&movdqa	(&QWP(16*6,"esp"),$inout0);
+
+	&lea	("esp",&DWP(0,$key));
+	&mov	($rounds,&wparam(5));		# &offset_i
+	&mov	($rounds_,&wparam(7));		# &checksum
+	&movdqu	(&QWP(0,$rounds),$rndkey0);
+	&pxor	($rndkey0,$rndkey0);
+	&movdqu	(&QWP(0,$rounds_),$rndkey1);
+	&pxor	($rndkey1,$rndkey1);
+&function_end("aesni_ocb_decrypt");
+}
+}
+
+######################################################################
+# void $PREFIX_cbc_encrypt (const void *inp, void *out,
+#                           size_t length, const AES_KEY *key,
+#                           unsigned char *ivp,const int enc);
+&function_begin("${PREFIX}_cbc_encrypt");
+	&mov	($inp,&wparam(0));
+	&mov	($rounds_,"esp");
+	&mov	($out,&wparam(1));
+	&sub	($rounds_,24);
+	&mov	($len,&wparam(2));
+	&and	($rounds_,-16);
+	&mov	($key,&wparam(3));
+	&mov	($key_,&wparam(4));
+	&test	($len,$len);
+	&jz	(&label("cbc_abort"));
+
+	&cmp	(&wparam(5),0);
+	&xchg	($rounds_,"esp");		# alloca
+	&movups	($ivec,&QWP(0,$key_));		# load IV
+	&mov	($rounds,&DWP(240,$key));
+	&mov	($key_,$key);			# backup $key
+	&mov	(&DWP(16,"esp"),$rounds_);	# save original %esp
+	&mov	($rounds_,$rounds);		# backup $rounds
+	&je	(&label("cbc_decrypt"));
+
+	&movaps	($inout0,$ivec);
+	&cmp	($len,16);
+	&jb	(&label("cbc_enc_tail"));
+	&sub	($len,16);
+	&jmp	(&label("cbc_enc_loop"));
+
+&set_label("cbc_enc_loop",16);
+	&movups	($ivec,&QWP(0,$inp));		# input actually
+	&lea	($inp,&DWP(16,$inp));
+	if ($inline)
+	{   &aesni_inline_generate1("enc",$inout0,$ivec);	}
+	else
+	{   &xorps($inout0,$ivec); &call("_aesni_encrypt1");	}
+	&mov	($rounds,$rounds_);	# restore $rounds
+	&mov	($key,$key_);		# restore $key
+	&movups	(&QWP(0,$out),$inout0);	# store output
+	&lea	($out,&DWP(16,$out));
+	&sub	($len,16);
+	&jnc	(&label("cbc_enc_loop"));
+	&add	($len,16);
+	&jnz	(&label("cbc_enc_tail"));
+	&movaps	($ivec,$inout0);
+	&pxor	($inout0,$inout0);
+	&jmp	(&label("cbc_ret"));
+
+&set_label("cbc_enc_tail");
+	&mov	("ecx",$len);		# zaps $rounds
+	&data_word(0xA4F3F689);		# rep movsb
+	&mov	("ecx",16);		# zero tail
+	&sub	("ecx",$len);
+	&xor	("eax","eax");		# zaps $len
+	&data_word(0xAAF3F689);		# rep stosb
+	&lea	($out,&DWP(-16,$out));	# rewind $out by 1 block
+	&mov	($rounds,$rounds_);	# restore $rounds
+	&mov	($inp,$out);		# $inp and $out are the same
+	&mov	($key,$key_);		# restore $key
+	&jmp	(&label("cbc_enc_loop"));
+######################################################################
+&set_label("cbc_decrypt",16);
+	&cmp	($len,0x50);
+	&jbe	(&label("cbc_dec_tail"));
+	&movaps	(&QWP(0,"esp"),$ivec);		# save IV
+	&sub	($len,0x50);
+	&jmp	(&label("cbc_dec_loop6_enter"));
+
+&set_label("cbc_dec_loop6",16);
+	&movaps	(&QWP(0,"esp"),$rndkey0);	# save IV
+	&movups	(&QWP(0,$out),$inout5);
+	&lea	($out,&DWP(0x10,$out));
+&set_label("cbc_dec_loop6_enter");
+	&movdqu	($inout0,&QWP(0,$inp));
+	&movdqu	($inout1,&QWP(0x10,$inp));
+	&movdqu	($inout2,&QWP(0x20,$inp));
+	&movdqu	($inout3,&QWP(0x30,$inp));
+	&movdqu	($inout4,&QWP(0x40,$inp));
+	&movdqu	($inout5,&QWP(0x50,$inp));
+
+	&call	("_aesni_decrypt6");
+
+	&movups	($rndkey1,&QWP(0,$inp));
+	&movups	($rndkey0,&QWP(0x10,$inp));
+	&xorps	($inout0,&QWP(0,"esp"));	# ^=IV
+	&xorps	($inout1,$rndkey1);
+	&movups	($rndkey1,&QWP(0x20,$inp));
+	&xorps	($inout2,$rndkey0);
+	&movups	($rndkey0,&QWP(0x30,$inp));
+	&xorps	($inout3,$rndkey1);
+	&movups	($rndkey1,&QWP(0x40,$inp));
+	&xorps	($inout4,$rndkey0);
+	&movups	($rndkey0,&QWP(0x50,$inp));	# IV
+	&xorps	($inout5,$rndkey1);
+	&movups	(&QWP(0,$out),$inout0);
+	&movups	(&QWP(0x10,$out),$inout1);
+	&lea	($inp,&DWP(0x60,$inp));
+	&movups	(&QWP(0x20,$out),$inout2);
+	&mov	($rounds,$rounds_);		# restore $rounds
+	&movups	(&QWP(0x30,$out),$inout3);
+	&mov	($key,$key_);			# restore $key
+	&movups	(&QWP(0x40,$out),$inout4);
+	&lea	($out,&DWP(0x50,$out));
+	&sub	($len,0x60);
+	&ja	(&label("cbc_dec_loop6"));
+
+	&movaps	($inout0,$inout5);
+	&movaps	($ivec,$rndkey0);
+	&add	($len,0x50);
+	&jle	(&label("cbc_dec_clear_tail_collected"));
+	&movups	(&QWP(0,$out),$inout0);
+	&lea	($out,&DWP(0x10,$out));
+&set_label("cbc_dec_tail");
+	&movups	($inout0,&QWP(0,$inp));
+	&movaps	($in0,$inout0);
+	&cmp	($len,0x10);
+	&jbe	(&label("cbc_dec_one"));
+
+	&movups	($inout1,&QWP(0x10,$inp));
+	&movaps	($in1,$inout1);
+	&cmp	($len,0x20);
+	&jbe	(&label("cbc_dec_two"));
+
+	&movups	($inout2,&QWP(0x20,$inp));
+	&cmp	($len,0x30);
+	&jbe	(&label("cbc_dec_three"));
+
+	&movups	($inout3,&QWP(0x30,$inp));
+	&cmp	($len,0x40);
+	&jbe	(&label("cbc_dec_four"));
+
+	&movups	($inout4,&QWP(0x40,$inp));
+	&movaps	(&QWP(0,"esp"),$ivec);		# save IV
+	&movups	($inout0,&QWP(0,$inp));
+	&xorps	($inout5,$inout5);
+	&call	("_aesni_decrypt6");
+	&movups	($rndkey1,&QWP(0,$inp));
+	&movups	($rndkey0,&QWP(0x10,$inp));
+	&xorps	($inout0,&QWP(0,"esp"));	# ^= IV
+	&xorps	($inout1,$rndkey1);
+	&movups	($rndkey1,&QWP(0x20,$inp));
+	&xorps	($inout2,$rndkey0);
+	&movups	($rndkey0,&QWP(0x30,$inp));
+	&xorps	($inout3,$rndkey1);
+	&movups	($ivec,&QWP(0x40,$inp));	# IV
+	&xorps	($inout4,$rndkey0);
+	&movups	(&QWP(0,$out),$inout0);
+	&movups	(&QWP(0x10,$out),$inout1);
+	&pxor	($inout1,$inout1);
+	&movups	(&QWP(0x20,$out),$inout2);
+	&pxor	($inout2,$inout2);
+	&movups	(&QWP(0x30,$out),$inout3);
+	&pxor	($inout3,$inout3);
+	&lea	($out,&DWP(0x40,$out));
+	&movaps	($inout0,$inout4);
+	&pxor	($inout4,$inout4);
+	&sub	($len,0x50);
+	&jmp	(&label("cbc_dec_tail_collected"));
+
+&set_label("cbc_dec_one",16);
+	if ($inline)
+	{   &aesni_inline_generate1("dec");	}
+	else
+	{   &call	("_aesni_decrypt1");	}
+	&xorps	($inout0,$ivec);
+	&movaps	($ivec,$in0);
+	&sub	($len,0x10);
+	&jmp	(&label("cbc_dec_tail_collected"));
+
+&set_label("cbc_dec_two",16);
+	&call	("_aesni_decrypt2");
+	&xorps	($inout0,$ivec);
+	&xorps	($inout1,$in0);
+	&movups	(&QWP(0,$out),$inout0);
+	&movaps	($inout0,$inout1);
+	&pxor	($inout1,$inout1);
+	&lea	($out,&DWP(0x10,$out));
+	&movaps	($ivec,$in1);
+	&sub	($len,0x20);
+	&jmp	(&label("cbc_dec_tail_collected"));
+
+&set_label("cbc_dec_three",16);
+	&call	("_aesni_decrypt3");
+	&xorps	($inout0,$ivec);
+	&xorps	($inout1,$in0);
+	&xorps	($inout2,$in1);
+	&movups	(&QWP(0,$out),$inout0);
+	&movaps	($inout0,$inout2);
+	&pxor	($inout2,$inout2);
+	&movups	(&QWP(0x10,$out),$inout1);
+	&pxor	($inout1,$inout1);
+	&lea	($out,&DWP(0x20,$out));
+	&movups	($ivec,&QWP(0x20,$inp));
+	&sub	($len,0x30);
+	&jmp	(&label("cbc_dec_tail_collected"));
+
+&set_label("cbc_dec_four",16);
+	&call	("_aesni_decrypt4");
+	&movups	($rndkey1,&QWP(0x10,$inp));
+	&movups	($rndkey0,&QWP(0x20,$inp));
+	&xorps	($inout0,$ivec);
+	&movups	($ivec,&QWP(0x30,$inp));
+	&xorps	($inout1,$in0);
+	&movups	(&QWP(0,$out),$inout0);
+	&xorps	($inout2,$rndkey1);
+	&movups	(&QWP(0x10,$out),$inout1);
+	&pxor	($inout1,$inout1);
+	&xorps	($inout3,$rndkey0);
+	&movups	(&QWP(0x20,$out),$inout2);
+	&pxor	($inout2,$inout2);
+	&lea	($out,&DWP(0x30,$out));
+	&movaps	($inout0,$inout3);
+	&pxor	($inout3,$inout3);
+	&sub	($len,0x40);
+	&jmp	(&label("cbc_dec_tail_collected"));
+
+&set_label("cbc_dec_clear_tail_collected",16);
+	&pxor	($inout1,$inout1);
+	&pxor	($inout2,$inout2);
+	&pxor	($inout3,$inout3);
+	&pxor	($inout4,$inout4);
+&set_label("cbc_dec_tail_collected");
+	&and	($len,15);
+	&jnz	(&label("cbc_dec_tail_partial"));
+	&movups	(&QWP(0,$out),$inout0);
+	&pxor	($rndkey0,$rndkey0);
+	&jmp	(&label("cbc_ret"));
+
+&set_label("cbc_dec_tail_partial",16);
+	&movaps	(&QWP(0,"esp"),$inout0);
+	&pxor	($rndkey0,$rndkey0);
+	&mov	("ecx",16);
+	&mov	($inp,"esp");
+	&sub	("ecx",$len);
+	&data_word(0xA4F3F689);		# rep movsb
+	&movdqa	(&QWP(0,"esp"),$inout0);
+
+&set_label("cbc_ret");
+	&mov	("esp",&DWP(16,"esp"));	# pull original %esp
+	&mov	($key_,&wparam(4));
+	&pxor	($inout0,$inout0);
+	&pxor	($rndkey1,$rndkey1);
+	&movups	(&QWP(0,$key_),$ivec);	# output IV
+	&pxor	($ivec,$ivec);
+&set_label("cbc_abort");
+&function_end("${PREFIX}_cbc_encrypt");
+
+######################################################################
+# Mechanical port from aesni-x86_64.pl.
+#
+# _aesni_set_encrypt_key is private interface,
+# input:
+#	"eax"	const unsigned char *userKey
+#	$rounds	int bits
+#	$key	AES_KEY *key
+# output:
+#	"eax"	return code
+#	$round	rounds
+
+&function_begin_B("_aesni_set_encrypt_key");
+	&push	("ebp");
+	&push	("ebx");
+	&test	("eax","eax");
+	&jz	(&label("bad_pointer"));
+	&test	($key,$key);
+	&jz	(&label("bad_pointer"));
+
+	&call	(&label("pic"));
+&set_label("pic");
+	&blindpop("ebx");
+	&lea	("ebx",&DWP(&label("key_const")."-".&label("pic"),"ebx"));
+
+	&picmeup("ebp","OPENSSL_ia32cap_P","ebx",&label("key_const"));
+	&movups	("xmm0",&QWP(0,"eax"));	# pull first 128 bits of *userKey
+	&xorps	("xmm4","xmm4");	# low dword of xmm4 is assumed 0
+	&mov	("ebp",&DWP(4,"ebp"));
+	&lea	($key,&DWP(16,$key));
+	&and	("ebp",1<<28|1<<11);	# AVX and XOP bits
+	&cmp	($rounds,256);
+	&je	(&label("14rounds"));
+	&cmp	($rounds,192);
+	&je	(&label("12rounds"));
+	&cmp	($rounds,128);
+	&jne	(&label("bad_keybits"));
+
+&set_label("10rounds",16);
+	&cmp		("ebp",1<<28);
+	&je		(&label("10rounds_alt"));
+
+	&mov		($rounds,9);
+	&$movekey	(&QWP(-16,$key),"xmm0");	# round 0
+	&aeskeygenassist("xmm1","xmm0",0x01);		# round 1
+	&call		(&label("key_128_cold"));
+	&aeskeygenassist("xmm1","xmm0",0x2);		# round 2
+	&call		(&label("key_128"));
+	&aeskeygenassist("xmm1","xmm0",0x04);		# round 3
+	&call		(&label("key_128"));
+	&aeskeygenassist("xmm1","xmm0",0x08);		# round 4
+	&call		(&label("key_128"));
+	&aeskeygenassist("xmm1","xmm0",0x10);		# round 5
+	&call		(&label("key_128"));
+	&aeskeygenassist("xmm1","xmm0",0x20);		# round 6
+	&call		(&label("key_128"));
+	&aeskeygenassist("xmm1","xmm0",0x40);		# round 7
+	&call		(&label("key_128"));
+	&aeskeygenassist("xmm1","xmm0",0x80);		# round 8
+	&call		(&label("key_128"));
+	&aeskeygenassist("xmm1","xmm0",0x1b);		# round 9
+	&call		(&label("key_128"));
+	&aeskeygenassist("xmm1","xmm0",0x36);		# round 10
+	&call		(&label("key_128"));
+	&$movekey	(&QWP(0,$key),"xmm0");
+	&mov		(&DWP(80,$key),$rounds);
+
+	&jmp	(&label("good_key"));
+
+&set_label("key_128",16);
+	&$movekey	(&QWP(0,$key),"xmm0");
+	&lea		($key,&DWP(16,$key));
+&set_label("key_128_cold");
+	&shufps		("xmm4","xmm0",0b00010000);
+	&xorps		("xmm0","xmm4");
+	&shufps		("xmm4","xmm0",0b10001100);
+	&xorps		("xmm0","xmm4");
+	&shufps		("xmm1","xmm1",0b11111111);	# critical path
+	&xorps		("xmm0","xmm1");
+	&ret();
+
+&set_label("10rounds_alt",16);
+	&movdqa		("xmm5",&QWP(0x00,"ebx"));
+	&mov		($rounds,8);
+	&movdqa		("xmm4",&QWP(0x20,"ebx"));
+	&movdqa		("xmm2","xmm0");
+	&movdqu		(&QWP(-16,$key),"xmm0");
+
+&set_label("loop_key128");
+	&pshufb		("xmm0","xmm5");
+	&aesenclast	("xmm0","xmm4");
+	&pslld		("xmm4",1);
+	&lea		($key,&DWP(16,$key));
+
+	&movdqa		("xmm3","xmm2");
+	&pslldq		("xmm2",4);
+	&pxor		("xmm3","xmm2");
+	&pslldq		("xmm2",4);
+	&pxor		("xmm3","xmm2");
+	&pslldq		("xmm2",4);
+	&pxor		("xmm2","xmm3");
+
+	&pxor		("xmm0","xmm2");
+	&movdqu		(&QWP(-16,$key),"xmm0");
+	&movdqa		("xmm2","xmm0");
+
+	&dec		($rounds);
+	&jnz		(&label("loop_key128"));
+
+	&movdqa		("xmm4",&QWP(0x30,"ebx"));
+
+	&pshufb		("xmm0","xmm5");
+	&aesenclast	("xmm0","xmm4");
+	&pslld		("xmm4",1);
+
+	&movdqa		("xmm3","xmm2");
+	&pslldq		("xmm2",4);
+	&pxor		("xmm3","xmm2");
+	&pslldq		("xmm2",4);
+	&pxor		("xmm3","xmm2");
+	&pslldq		("xmm2",4);
+	&pxor		("xmm2","xmm3");
+
+	&pxor		("xmm0","xmm2");
+	&movdqu		(&QWP(0,$key),"xmm0");
+
+	&movdqa		("xmm2","xmm0");
+	&pshufb		("xmm0","xmm5");
+	&aesenclast	("xmm0","xmm4");
+
+	&movdqa		("xmm3","xmm2");
+	&pslldq		("xmm2",4);
+	&pxor		("xmm3","xmm2");
+	&pslldq		("xmm2",4);
+	&pxor		("xmm3","xmm2");
+	&pslldq		("xmm2",4);
+	&pxor		("xmm2","xmm3");
+
+	&pxor		("xmm0","xmm2");
+	&movdqu		(&QWP(16,$key),"xmm0");
+
+	&mov		($rounds,9);
+	&mov		(&DWP(96,$key),$rounds);
+
+	&jmp	(&label("good_key"));
+
+&set_label("12rounds",16);
+	&movq		("xmm2",&QWP(16,"eax"));	# remaining 1/3 of *userKey
+	&cmp		("ebp",1<<28);
+	&je		(&label("12rounds_alt"));
+
+	&mov		($rounds,11);
+	&$movekey	(&QWP(-16,$key),"xmm0");	# round 0
+	&aeskeygenassist("xmm1","xmm2",0x01);		# round 1,2
+	&call		(&label("key_192a_cold"));
+	&aeskeygenassist("xmm1","xmm2",0x02);		# round 2,3
+	&call		(&label("key_192b"));
+	&aeskeygenassist("xmm1","xmm2",0x04);		# round 4,5
+	&call		(&label("key_192a"));
+	&aeskeygenassist("xmm1","xmm2",0x08);		# round 5,6
+	&call		(&label("key_192b"));
+	&aeskeygenassist("xmm1","xmm2",0x10);		# round 7,8
+	&call		(&label("key_192a"));
+	&aeskeygenassist("xmm1","xmm2",0x20);		# round 8,9
+	&call		(&label("key_192b"));
+	&aeskeygenassist("xmm1","xmm2",0x40);		# round 10,11
+	&call		(&label("key_192a"));
+	&aeskeygenassist("xmm1","xmm2",0x80);		# round 11,12
+	&call		(&label("key_192b"));
+	&$movekey	(&QWP(0,$key),"xmm0");
+	&mov		(&DWP(48,$key),$rounds);
+
+	&jmp	(&label("good_key"));
+
+&set_label("key_192a",16);
+	&$movekey	(&QWP(0,$key),"xmm0");
+	&lea		($key,&DWP(16,$key));
+&set_label("key_192a_cold",16);
+	&movaps		("xmm5","xmm2");
+&set_label("key_192b_warm");
+	&shufps		("xmm4","xmm0",0b00010000);
+	&movdqa		("xmm3","xmm2");
+	&xorps		("xmm0","xmm4");
+	&shufps		("xmm4","xmm0",0b10001100);
+	&pslldq		("xmm3",4);
+	&xorps		("xmm0","xmm4");
+	&pshufd		("xmm1","xmm1",0b01010101);	# critical path
+	&pxor		("xmm2","xmm3");
+	&pxor		("xmm0","xmm1");
+	&pshufd		("xmm3","xmm0",0b11111111);
+	&pxor		("xmm2","xmm3");
+	&ret();
+
+&set_label("key_192b",16);
+	&movaps		("xmm3","xmm0");
+	&shufps		("xmm5","xmm0",0b01000100);
+	&$movekey	(&QWP(0,$key),"xmm5");
+	&shufps		("xmm3","xmm2",0b01001110);
+	&$movekey	(&QWP(16,$key),"xmm3");
+	&lea		($key,&DWP(32,$key));
+	&jmp		(&label("key_192b_warm"));
+
+&set_label("12rounds_alt",16);
+	&movdqa		("xmm5",&QWP(0x10,"ebx"));
+	&movdqa		("xmm4",&QWP(0x20,"ebx"));
+	&mov		($rounds,8);
+	&movdqu		(&QWP(-16,$key),"xmm0");
+
+&set_label("loop_key192");
+	&movq		(&QWP(0,$key),"xmm2");
+	&movdqa		("xmm1","xmm2");
+	&pshufb		("xmm2","xmm5");
+	&aesenclast	("xmm2","xmm4");
+	&pslld		("xmm4",1);
+	&lea		($key,&DWP(24,$key));
+
+	&movdqa		("xmm3","xmm0");
+	&pslldq		("xmm0",4);
+	&pxor		("xmm3","xmm0");
+	&pslldq		("xmm0",4);
+	&pxor		("xmm3","xmm0");
+	&pslldq		("xmm0",4);
+	&pxor		("xmm0","xmm3");
+
+	&pshufd		("xmm3","xmm0",0xff);
+	&pxor		("xmm3","xmm1");
+	&pslldq		("xmm1",4);
+	&pxor		("xmm3","xmm1");
+
+	&pxor		("xmm0","xmm2");
+	&pxor		("xmm2","xmm3");
+	&movdqu		(&QWP(-16,$key),"xmm0");
+
+	&dec		($rounds);
+	&jnz		(&label("loop_key192"));
+
+	&mov	($rounds,11);
+	&mov	(&DWP(32,$key),$rounds);
+
+	&jmp	(&label("good_key"));
+
+&set_label("14rounds",16);
+	&movups		("xmm2",&QWP(16,"eax"));	# remaining half of *userKey
+	&lea		($key,&DWP(16,$key));
+	&cmp		("ebp",1<<28);
+	&je		(&label("14rounds_alt"));
+
+	&mov		($rounds,13);
+	&$movekey	(&QWP(-32,$key),"xmm0");	# round 0
+	&$movekey	(&QWP(-16,$key),"xmm2");	# round 1
+	&aeskeygenassist("xmm1","xmm2",0x01);		# round 2
+	&call		(&label("key_256a_cold"));
+	&aeskeygenassist("xmm1","xmm0",0x01);		# round 3
+	&call		(&label("key_256b"));
+	&aeskeygenassist("xmm1","xmm2",0x02);		# round 4
+	&call		(&label("key_256a"));
+	&aeskeygenassist("xmm1","xmm0",0x02);		# round 5
+	&call		(&label("key_256b"));
+	&aeskeygenassist("xmm1","xmm2",0x04);		# round 6
+	&call		(&label("key_256a"));
+	&aeskeygenassist("xmm1","xmm0",0x04);		# round 7
+	&call		(&label("key_256b"));
+	&aeskeygenassist("xmm1","xmm2",0x08);		# round 8
+	&call		(&label("key_256a"));
+	&aeskeygenassist("xmm1","xmm0",0x08);		# round 9
+	&call		(&label("key_256b"));
+	&aeskeygenassist("xmm1","xmm2",0x10);		# round 10
+	&call		(&label("key_256a"));
+	&aeskeygenassist("xmm1","xmm0",0x10);		# round 11
+	&call		(&label("key_256b"));
+	&aeskeygenassist("xmm1","xmm2",0x20);		# round 12
+	&call		(&label("key_256a"));
+	&aeskeygenassist("xmm1","xmm0",0x20);		# round 13
+	&call		(&label("key_256b"));
+	&aeskeygenassist("xmm1","xmm2",0x40);		# round 14
+	&call		(&label("key_256a"));
+	&$movekey	(&QWP(0,$key),"xmm0");
+	&mov		(&DWP(16,$key),$rounds);
+	&xor		("eax","eax");
+
+	&jmp	(&label("good_key"));
+
+&set_label("key_256a",16);
+	&$movekey	(&QWP(0,$key),"xmm2");
+	&lea		($key,&DWP(16,$key));
+&set_label("key_256a_cold");
+	&shufps		("xmm4","xmm0",0b00010000);
+	&xorps		("xmm0","xmm4");
+	&shufps		("xmm4","xmm0",0b10001100);
+	&xorps		("xmm0","xmm4");
+	&shufps		("xmm1","xmm1",0b11111111);	# critical path
+	&xorps		("xmm0","xmm1");
+	&ret();
+
+&set_label("key_256b",16);
+	&$movekey	(&QWP(0,$key),"xmm0");
+	&lea		($key,&DWP(16,$key));
+
+	&shufps		("xmm4","xmm2",0b00010000);
+	&xorps		("xmm2","xmm4");
+	&shufps		("xmm4","xmm2",0b10001100);
+	&xorps		("xmm2","xmm4");
+	&shufps		("xmm1","xmm1",0b10101010);	# critical path
+	&xorps		("xmm2","xmm1");
+	&ret();
+
+&set_label("14rounds_alt",16);
+	&movdqa		("xmm5",&QWP(0x00,"ebx"));
+	&movdqa		("xmm4",&QWP(0x20,"ebx"));
+	&mov		($rounds,7);
+	&movdqu		(&QWP(-32,$key),"xmm0");
+	&movdqa		("xmm1","xmm2");
+	&movdqu		(&QWP(-16,$key),"xmm2");
+
+&set_label("loop_key256");
+	&pshufb		("xmm2","xmm5");
+	&aesenclast	("xmm2","xmm4");
+
+	&movdqa		("xmm3","xmm0");
+	&pslldq		("xmm0",4);
+	&pxor		("xmm3","xmm0");
+	&pslldq		("xmm0",4);
+	&pxor		("xmm3","xmm0");
+	&pslldq		("xmm0",4);
+	&pxor		("xmm0","xmm3");
+	&pslld		("xmm4",1);
+
+	&pxor		("xmm0","xmm2");
+	&movdqu		(&QWP(0,$key),"xmm0");
+
+	&dec		($rounds);
+	&jz		(&label("done_key256"));
+
+	&pshufd		("xmm2","xmm0",0xff);
+	&pxor		("xmm3","xmm3");
+	&aesenclast	("xmm2","xmm3");
+
+	&movdqa		("xmm3","xmm1");
+	&pslldq		("xmm1",4);
+	&pxor		("xmm3","xmm1");
+	&pslldq		("xmm1",4);
+	&pxor		("xmm3","xmm1");
+	&pslldq		("xmm1",4);
+	&pxor		("xmm1","xmm3");
+
+	&pxor		("xmm2","xmm1");
+	&movdqu		(&QWP(16,$key),"xmm2");
+	&lea		($key,&DWP(32,$key));
+	&movdqa		("xmm1","xmm2");
+	&jmp		(&label("loop_key256"));
+
+&set_label("done_key256");
+	&mov		($rounds,13);
+	&mov		(&DWP(16,$key),$rounds);
+
+&set_label("good_key");
+	&pxor	("xmm0","xmm0");
+	&pxor	("xmm1","xmm1");
+	&pxor	("xmm2","xmm2");
+	&pxor	("xmm3","xmm3");
+	&pxor	("xmm4","xmm4");
+	&pxor	("xmm5","xmm5");
+	&xor	("eax","eax");
+	&pop	("ebx");
+	&pop	("ebp");
+	&ret	();
+
+&set_label("bad_pointer",4);
+	&mov	("eax",-1);
+	&pop	("ebx");
+	&pop	("ebp");
+	&ret	();
+&set_label("bad_keybits",4);
+	&pxor	("xmm0","xmm0");
+	&mov	("eax",-2);
+	&pop	("ebx");
+	&pop	("ebp");
+	&ret	();
+&function_end_B("_aesni_set_encrypt_key");
+
+# int $PREFIX_set_encrypt_key (const unsigned char *userKey, int bits,
+#                              AES_KEY *key)
+&function_begin_B("${PREFIX}_set_encrypt_key");
+	&mov	("eax",&wparam(0));
+	&mov	($rounds,&wparam(1));
+	&mov	($key,&wparam(2));
+	&call	("_aesni_set_encrypt_key");
+	&ret	();
+&function_end_B("${PREFIX}_set_encrypt_key");
+
+# int $PREFIX_set_decrypt_key (const unsigned char *userKey, int bits,
+#                              AES_KEY *key)
+&function_begin_B("${PREFIX}_set_decrypt_key");
+	&mov	("eax",&wparam(0));
+	&mov	($rounds,&wparam(1));
+	&mov	($key,&wparam(2));
+	&call	("_aesni_set_encrypt_key");
+	&mov	($key,&wparam(2));
+	&shl	($rounds,4);	# rounds-1 after _aesni_set_encrypt_key
+	&test	("eax","eax");
+	&jnz	(&label("dec_key_ret"));
+	&lea	("eax",&DWP(16,$key,$rounds));	# end of key schedule
+
+	&$movekey	("xmm0",&QWP(0,$key));	# just swap
+	&$movekey	("xmm1",&QWP(0,"eax"));
+	&$movekey	(&QWP(0,"eax"),"xmm0");
+	&$movekey	(&QWP(0,$key),"xmm1");
+	&lea		($key,&DWP(16,$key));
+	&lea		("eax",&DWP(-16,"eax"));
+
+&set_label("dec_key_inverse");
+	&$movekey	("xmm0",&QWP(0,$key));	# swap and inverse
+	&$movekey	("xmm1",&QWP(0,"eax"));
+	&aesimc		("xmm0","xmm0");
+	&aesimc		("xmm1","xmm1");
+	&lea		($key,&DWP(16,$key));
+	&lea		("eax",&DWP(-16,"eax"));
+	&$movekey	(&QWP(16,"eax"),"xmm0");
+	&$movekey	(&QWP(-16,$key),"xmm1");
+	&cmp		("eax",$key);
+	&ja		(&label("dec_key_inverse"));
+
+	&$movekey	("xmm0",&QWP(0,$key));	# inverse middle
+	&aesimc		("xmm0","xmm0");
+	&$movekey	(&QWP(0,$key),"xmm0");
+
+	&pxor		("xmm0","xmm0");
+	&pxor		("xmm1","xmm1");
+	&xor		("eax","eax");		# return success
+&set_label("dec_key_ret");
+	&ret	();
+&function_end_B("${PREFIX}_set_decrypt_key");
+
+&set_label("key_const",64);
+&data_word(0x0c0f0e0d,0x0c0f0e0d,0x0c0f0e0d,0x0c0f0e0d);
+&data_word(0x04070605,0x04070605,0x04070605,0x04070605);
+&data_word(1,1,1,1);
+&data_word(0x1b,0x1b,0x1b,0x1b);
+&asciz("AES for Intel AES-NI, CRYPTOGAMS by ");
+
+&asm_finish();
+
+close STDOUT;
diff --git a/crypto/aes/asm/aesni-x86_64.pl b/crypto/aes/asm/aesni-x86_64.pl
new file mode 100644
index 000000000..98ca17991
--- /dev/null
+++ b/crypto/aes/asm/aesni-x86_64.pl
@@ -0,0 +1,5060 @@
+#! /usr/bin/env perl
+# Copyright 2009-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+#
+# ====================================================================
+# Written by Andy Polyakov  for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+#
+# This module implements support for Intel AES-NI extension. In
+# OpenSSL context it's used with Intel engine, but can also be used as
+# drop-in replacement for crypto/aes/asm/aes-x86_64.pl [see below for
+# details].
+#
+# Performance.
+#
+# Given aes(enc|dec) instructions' latency asymptotic performance for
+# non-parallelizable modes such as CBC encrypt is 3.75 cycles per byte
+# processed with 128-bit key. And given their throughput asymptotic
+# performance for parallelizable modes is 1.25 cycles per byte. Being
+# asymptotic limit it's not something you commonly achieve in reality,
+# but how close does one get? Below are results collected for
+# different modes and block sized. Pairs of numbers are for en-/
+# decryption.
+#
+#	16-byte     64-byte     256-byte    1-KB        8-KB
+# ECB	4.25/4.25   1.38/1.38   1.28/1.28   1.26/1.26	1.26/1.26
+# CTR	5.42/5.42   1.92/1.92   1.44/1.44   1.28/1.28   1.26/1.26
+# CBC	4.38/4.43   4.15/1.43   4.07/1.32   4.07/1.29   4.06/1.28
+# CCM	5.66/9.42   4.42/5.41   4.16/4.40   4.09/4.15   4.06/4.07   
+# OFB	5.42/5.42   4.64/4.64   4.44/4.44   4.39/4.39   4.38/4.38
+# CFB	5.73/5.85   5.56/5.62   5.48/5.56   5.47/5.55   5.47/5.55
+#
+# ECB, CTR, CBC and CCM results are free from EVP overhead. This means
+# that otherwise used 'openssl speed -evp aes-128-??? -engine aesni
+# [-decrypt]' will exhibit 10-15% worse results for smaller blocks.
+# The results were collected with specially crafted speed.c benchmark
+# in order to compare them with results reported in "Intel Advanced
+# Encryption Standard (AES) New Instruction Set" White Paper Revision
+# 3.0 dated May 2010. All above results are consistently better. This
+# module also provides better performance for block sizes smaller than
+# 128 bytes in points *not* represented in the above table.
+#
+# Looking at the results for 8-KB buffer.
+#
+# CFB and OFB results are far from the limit, because implementation
+# uses "generic" CRYPTO_[c|o]fb128_encrypt interfaces relying on
+# single-block aesni_encrypt, which is not the most optimal way to go.
+# CBC encrypt result is unexpectedly high and there is no documented
+# explanation for it. Seemingly there is a small penalty for feeding
+# the result back to AES unit the way it's done in CBC mode. There is
+# nothing one can do and the result appears optimal. CCM result is
+# identical to CBC, because CBC-MAC is essentially CBC encrypt without
+# saving output. CCM CTR "stays invisible," because it's neatly
+# interleaved wih CBC-MAC. This provides ~30% improvement over
+# "straghtforward" CCM implementation with CTR and CBC-MAC performed
+# disjointly. Parallelizable modes practically achieve the theoretical
+# limit.
+#
+# Looking at how results vary with buffer size.
+#
+# Curves are practically saturated at 1-KB buffer size. In most cases
+# "256-byte" performance is >95%, and "64-byte" is ~90% of "8-KB" one.
+# CTR curve doesn't follow this pattern and is "slowest" changing one
+# with "256-byte" result being 87% of "8-KB." This is because overhead
+# in CTR mode is most computationally intensive. Small-block CCM
+# decrypt is slower than encrypt, because first CTR and last CBC-MAC
+# iterations can't be interleaved.
+#
+# Results for 192- and 256-bit keys.
+#
+# EVP-free results were observed to scale perfectly with number of
+# rounds for larger block sizes, i.e. 192-bit result being 10/12 times
+# lower and 256-bit one - 10/14. Well, in CBC encrypt case differences
+# are a tad smaller, because the above mentioned penalty biases all
+# results by same constant value. In similar way function call
+# overhead affects small-block performance, as well as OFB and CFB
+# results. Differences are not large, most common coefficients are
+# 10/11.7 and 10/13.4 (as opposite to 10/12.0 and 10/14.0), but one
+# observe even 10/11.2 and 10/12.4 (CTR, OFB, CFB)...
+
+# January 2011
+#
+# While Westmere processor features 6 cycles latency for aes[enc|dec]
+# instructions, which can be scheduled every second cycle, Sandy
+# Bridge spends 8 cycles per instruction, but it can schedule them
+# every cycle. This means that code targeting Westmere would perform
+# suboptimally on Sandy Bridge. Therefore this update.
+#
+# In addition, non-parallelizable CBC encrypt (as well as CCM) is
+# optimized. Relative improvement might appear modest, 8% on Westmere,
+# but in absolute terms it's 3.77 cycles per byte encrypted with
+# 128-bit key on Westmere, and 5.07 - on Sandy Bridge. These numbers
+# should be compared to asymptotic limits of 3.75 for Westmere and
+# 5.00 for Sandy Bridge. Actually, the fact that they get this close
+# to asymptotic limits is quite amazing. Indeed, the limit is
+# calculated as latency times number of rounds, 10 for 128-bit key,
+# and divided by 16, the number of bytes in block, or in other words
+# it accounts *solely* for aesenc instructions. But there are extra
+# instructions, and numbers so close to the asymptotic limits mean
+# that it's as if it takes as little as *one* additional cycle to
+# execute all of them. How is it possible? It is possible thanks to
+# out-of-order execution logic, which manages to overlap post-
+# processing of previous block, things like saving the output, with
+# actual encryption of current block, as well as pre-processing of
+# current block, things like fetching input and xor-ing it with
+# 0-round element of the key schedule, with actual encryption of
+# previous block. Keep this in mind...
+#
+# For parallelizable modes, such as ECB, CBC decrypt, CTR, higher
+# performance is achieved by interleaving instructions working on
+# independent blocks. In which case asymptotic limit for such modes
+# can be obtained by dividing above mentioned numbers by AES
+# instructions' interleave factor. Westmere can execute at most 3 
+# instructions at a time, meaning that optimal interleave factor is 3,
+# and that's where the "magic" number of 1.25 come from. "Optimal
+# interleave factor" means that increase of interleave factor does
+# not improve performance. The formula has proven to reflect reality
+# pretty well on Westmere... Sandy Bridge on the other hand can
+# execute up to 8 AES instructions at a time, so how does varying
+# interleave factor affect the performance? Here is table for ECB
+# (numbers are cycles per byte processed with 128-bit key):
+#
+# instruction interleave factor		3x	6x	8x
+# theoretical asymptotic limit		1.67	0.83	0.625
+# measured performance for 8KB block	1.05	0.86	0.84
+#
+# "as if" interleave factor		4.7x	5.8x	6.0x
+#
+# Further data for other parallelizable modes:
+#
+# CBC decrypt				1.16	0.93	0.74
+# CTR					1.14	0.91	0.74
+#
+# Well, given 3x column it's probably inappropriate to call the limit
+# asymptotic, if it can be surpassed, isn't it? What happens there?
+# Rewind to CBC paragraph for the answer. Yes, out-of-order execution
+# magic is responsible for this. Processor overlaps not only the
+# additional instructions with AES ones, but even AES instuctions
+# processing adjacent triplets of independent blocks. In the 6x case
+# additional instructions  still claim disproportionally small amount
+# of additional cycles, but in 8x case number of instructions must be
+# a tad too high for out-of-order logic to cope with, and AES unit
+# remains underutilized... As you can see 8x interleave is hardly
+# justifiable, so there no need to feel bad that 32-bit aesni-x86.pl
+# utilizies 6x interleave because of limited register bank capacity.
+#
+# Higher interleave factors do have negative impact on Westmere
+# performance. While for ECB mode it's negligible ~1.5%, other
+# parallelizables perform ~5% worse, which is outweighed by ~25%
+# improvement on Sandy Bridge. To balance regression on Westmere
+# CTR mode was implemented with 6x aesenc interleave factor.
+
+# April 2011
+#
+# Add aesni_xts_[en|de]crypt. Westmere spends 1.25 cycles processing
+# one byte out of 8KB with 128-bit key, Sandy Bridge - 0.90. Just like
+# in CTR mode AES instruction interleave factor was chosen to be 6x.
+
+# November 2015
+#
+# Add aesni_ocb_[en|de]crypt. AES instruction interleave factor was
+# chosen to be 6x.
+
+######################################################################
+# Current large-block performance in cycles per byte processed with
+# 128-bit key (less is better).
+#
+#		CBC en-/decrypt	CTR	XTS	ECB	OCB
+# Westmere	3.77/1.25	1.25	1.25	1.26
+# * Bridge	5.07/0.74	0.75	0.90	0.85	0.98
+# Haswell	4.44/0.63	0.63	0.73	0.63	0.70
+# Skylake	2.62/0.63	0.63	0.63	0.63
+# Silvermont	5.75/3.54	3.56	4.12	3.87(*)	4.11
+# Goldmont	3.82/1.26	1.26	1.29	1.29	1.50
+# Bulldozer	5.77/0.70	0.72	0.90	0.70	0.95
+#
+# (*)	Atom Silvermont ECB result is suboptimal because of penalties
+#	incurred by operations on %xmm8-15. As ECB is not considered
+#	critical, nothing was done to mitigate the problem.
+
+$PREFIX="aesni";	# if $PREFIX is set to "AES", the script
+			# generates drop-in replacement for
+			# crypto/aes/asm/aes-x86_64.pl:-)
+
+$flavour = shift;
+$output  = shift;
+if ($flavour =~ /\./) { $output = $flavour; undef $flavour; }
+
+$win64=0; $win64=1 if ($flavour =~ /[nm]asm|mingw64/ || $output =~ /\.asm$/);
+
+$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+( $xlate="${dir}x86_64-xlate.pl" and -f $xlate ) or
+( $xlate="${dir}../../perlasm/x86_64-xlate.pl" and -f $xlate) or
+die "can't locate x86_64-xlate.pl";
+
+open OUT,"| \"$^X\" \"$xlate\" $flavour \"$output\"";
+*STDOUT=*OUT;
+
+$movkey = $PREFIX eq "aesni" ? "movups" : "movups";
+@_4args=$win64?	("%rcx","%rdx","%r8", "%r9") :	# Win64 order
+		("%rdi","%rsi","%rdx","%rcx");	# Unix order
+
+$code=".text\n";
+$code.=".extern	OPENSSL_ia32cap_P\n";
+
+$rounds="%eax";	# input to and changed by aesni_[en|de]cryptN !!!
+# this is natural Unix argument order for public $PREFIX_[ecb|cbc]_encrypt ...
+$inp="%rdi";
+$out="%rsi";
+$len="%rdx";
+$key="%rcx";	# input to and changed by aesni_[en|de]cryptN !!!
+$ivp="%r8";	# cbc, ctr, ...
+
+$rnds_="%r10d";	# backup copy for $rounds
+$key_="%r11";	# backup copy for $key
+
+# %xmm register layout
+$rndkey0="%xmm0";	$rndkey1="%xmm1";
+$inout0="%xmm2";	$inout1="%xmm3";
+$inout2="%xmm4";	$inout3="%xmm5";
+$inout4="%xmm6";	$inout5="%xmm7";
+$inout6="%xmm8";	$inout7="%xmm9";
+
+$in2="%xmm6";		$in1="%xmm7";	# used in CBC decrypt, CTR, ...
+$in0="%xmm8";		$iv="%xmm9";
+
+# Inline version of internal aesni_[en|de]crypt1.
+#
+# Why folded loop? Because aes[enc|dec] is slow enough to accommodate
+# cycles which take care of loop variables...
+{ my $sn;
+sub aesni_generate1 {
+my ($p,$key,$rounds,$inout,$ivec)=@_;	$inout=$inout0 if (!defined($inout));
+++$sn;
+$code.=<<___;
+	$movkey	($key),$rndkey0
+	$movkey	16($key),$rndkey1
+___
+$code.=<<___ if (defined($ivec));
+	xorps	$rndkey0,$ivec
+	lea	32($key),$key
+	xorps	$ivec,$inout
+___
+$code.=<<___ if (!defined($ivec));
+	lea	32($key),$key
+	xorps	$rndkey0,$inout
+___
+$code.=<<___;
+.Loop_${p}1_$sn:
+	aes${p}	$rndkey1,$inout
+	dec	$rounds
+	$movkey	($key),$rndkey1
+	lea	16($key),$key
+	jnz	.Loop_${p}1_$sn	# loop body is 16 bytes
+	aes${p}last	$rndkey1,$inout
+___
+}}
+# void $PREFIX_[en|de]crypt (const void *inp,void *out,const AES_KEY *key);
+#
+{ my ($inp,$out,$key) = @_4args;
+
+$code.=<<___;
+.globl	${PREFIX}_encrypt
+.type	${PREFIX}_encrypt,\@abi-omnipotent
+.align	16
+${PREFIX}_encrypt:
+	movups	($inp),$inout0		# load input
+	mov	240($key),$rounds	# key->rounds
+___
+	&aesni_generate1("enc",$key,$rounds);
+$code.=<<___;
+	 pxor	$rndkey0,$rndkey0	# clear register bank
+	 pxor	$rndkey1,$rndkey1
+	movups	$inout0,($out)		# output
+	 pxor	$inout0,$inout0
+	ret
+.size	${PREFIX}_encrypt,.-${PREFIX}_encrypt
+
+.globl	${PREFIX}_decrypt
+.type	${PREFIX}_decrypt,\@abi-omnipotent
+.align	16
+${PREFIX}_decrypt:
+	movups	($inp),$inout0		# load input
+	mov	240($key),$rounds	# key->rounds
+___
+	&aesni_generate1("dec",$key,$rounds);
+$code.=<<___;
+	 pxor	$rndkey0,$rndkey0	# clear register bank
+	 pxor	$rndkey1,$rndkey1
+	movups	$inout0,($out)		# output
+	 pxor	$inout0,$inout0
+	ret
+.size	${PREFIX}_decrypt, .-${PREFIX}_decrypt
+___
+}
+
+# _aesni_[en|de]cryptN are private interfaces, N denotes interleave
+# factor. Why 3x subroutine were originally used in loops? Even though
+# aes[enc|dec] latency was originally 6, it could be scheduled only
+# every *2nd* cycle. Thus 3x interleave was the one providing optimal
+# utilization, i.e. when subroutine's throughput is virtually same as
+# of non-interleaved subroutine [for number of input blocks up to 3].
+# This is why it originally made no sense to implement 2x subroutine.
+# But times change and it became appropriate to spend extra 192 bytes
+# on 2x subroutine on Atom Silvermont account. For processors that
+# can schedule aes[enc|dec] every cycle optimal interleave factor
+# equals to corresponding instructions latency. 8x is optimal for
+# * Bridge and "super-optimal" for other Intel CPUs... 
+
+sub aesni_generate2 {
+my $dir=shift;
+# As already mentioned it takes in $key and $rounds, which are *not*
+# preserved. $inout[0-1] is cipher/clear text...
+$code.=<<___;
+.type	_aesni_${dir}rypt2,\@abi-omnipotent
+.align	16
+_aesni_${dir}rypt2:
+	$movkey	($key),$rndkey0
+	shl	\$4,$rounds
+	$movkey	16($key),$rndkey1
+	xorps	$rndkey0,$inout0
+	xorps	$rndkey0,$inout1
+	$movkey	32($key),$rndkey0
+	lea	32($key,$rounds),$key
+	neg	%rax				# $rounds
+	add	\$16,%rax
+
+.L${dir}_loop2:
+	aes${dir}	$rndkey1,$inout0
+	aes${dir}	$rndkey1,$inout1
+	$movkey		($key,%rax),$rndkey1
+	add		\$32,%rax
+	aes${dir}	$rndkey0,$inout0
+	aes${dir}	$rndkey0,$inout1
+	$movkey		-16($key,%rax),$rndkey0
+	jnz		.L${dir}_loop2
+
+	aes${dir}	$rndkey1,$inout0
+	aes${dir}	$rndkey1,$inout1
+	aes${dir}last	$rndkey0,$inout0
+	aes${dir}last	$rndkey0,$inout1
+	ret
+.size	_aesni_${dir}rypt2,.-_aesni_${dir}rypt2
+___
+}
+sub aesni_generate3 {
+my $dir=shift;
+# As already mentioned it takes in $key and $rounds, which are *not*
+# preserved. $inout[0-2] is cipher/clear text...
+$code.=<<___;
+.type	_aesni_${dir}rypt3,\@abi-omnipotent
+.align	16
+_aesni_${dir}rypt3:
+	$movkey	($key),$rndkey0
+	shl	\$4,$rounds
+	$movkey	16($key),$rndkey1
+	xorps	$rndkey0,$inout0
+	xorps	$rndkey0,$inout1
+	xorps	$rndkey0,$inout2
+	$movkey	32($key),$rndkey0
+	lea	32($key,$rounds),$key
+	neg	%rax				# $rounds
+	add	\$16,%rax
+
+.L${dir}_loop3:
+	aes${dir}	$rndkey1,$inout0
+	aes${dir}	$rndkey1,$inout1
+	aes${dir}	$rndkey1,$inout2
+	$movkey		($key,%rax),$rndkey1
+	add		\$32,%rax
+	aes${dir}	$rndkey0,$inout0
+	aes${dir}	$rndkey0,$inout1
+	aes${dir}	$rndkey0,$inout2
+	$movkey		-16($key,%rax),$rndkey0
+	jnz		.L${dir}_loop3
+
+	aes${dir}	$rndkey1,$inout0
+	aes${dir}	$rndkey1,$inout1
+	aes${dir}	$rndkey1,$inout2
+	aes${dir}last	$rndkey0,$inout0
+	aes${dir}last	$rndkey0,$inout1
+	aes${dir}last	$rndkey0,$inout2
+	ret
+.size	_aesni_${dir}rypt3,.-_aesni_${dir}rypt3
+___
+}
+# 4x interleave is implemented to improve small block performance,
+# most notably [and naturally] 4 block by ~30%. One can argue that one
+# should have implemented 5x as well, but improvement would be <20%,
+# so it's not worth it...
+sub aesni_generate4 {
+my $dir=shift;
+# As already mentioned it takes in $key and $rounds, which are *not*
+# preserved. $inout[0-3] is cipher/clear text...
+$code.=<<___;
+.type	_aesni_${dir}rypt4,\@abi-omnipotent
+.align	16
+_aesni_${dir}rypt4:
+	$movkey	($key),$rndkey0
+	shl	\$4,$rounds
+	$movkey	16($key),$rndkey1
+	xorps	$rndkey0,$inout0
+	xorps	$rndkey0,$inout1
+	xorps	$rndkey0,$inout2
+	xorps	$rndkey0,$inout3
+	$movkey	32($key),$rndkey0
+	lea	32($key,$rounds),$key
+	neg	%rax				# $rounds
+	.byte	0x0f,0x1f,0x00
+	add	\$16,%rax
+
+.L${dir}_loop4:
+	aes${dir}	$rndkey1,$inout0
+	aes${dir}	$rndkey1,$inout1
+	aes${dir}	$rndkey1,$inout2
+	aes${dir}	$rndkey1,$inout3
+	$movkey		($key,%rax),$rndkey1
+	add		\$32,%rax
+	aes${dir}	$rndkey0,$inout0
+	aes${dir}	$rndkey0,$inout1
+	aes${dir}	$rndkey0,$inout2
+	aes${dir}	$rndkey0,$inout3
+	$movkey		-16($key,%rax),$rndkey0
+	jnz		.L${dir}_loop4
+
+	aes${dir}	$rndkey1,$inout0
+	aes${dir}	$rndkey1,$inout1
+	aes${dir}	$rndkey1,$inout2
+	aes${dir}	$rndkey1,$inout3
+	aes${dir}last	$rndkey0,$inout0
+	aes${dir}last	$rndkey0,$inout1
+	aes${dir}last	$rndkey0,$inout2
+	aes${dir}last	$rndkey0,$inout3
+	ret
+.size	_aesni_${dir}rypt4,.-_aesni_${dir}rypt4
+___
+}
+sub aesni_generate6 {
+my $dir=shift;
+# As already mentioned it takes in $key and $rounds, which are *not*
+# preserved. $inout[0-5] is cipher/clear text...
+$code.=<<___;
+.type	_aesni_${dir}rypt6,\@abi-omnipotent
+.align	16
+_aesni_${dir}rypt6:
+	$movkey		($key),$rndkey0
+	shl		\$4,$rounds
+	$movkey		16($key),$rndkey1
+	xorps		$rndkey0,$inout0
+	pxor		$rndkey0,$inout1
+	pxor		$rndkey0,$inout2
+	aes${dir}	$rndkey1,$inout0
+	lea		32($key,$rounds),$key
+	neg		%rax			# $rounds
+	aes${dir}	$rndkey1,$inout1
+	pxor		$rndkey0,$inout3
+	pxor		$rndkey0,$inout4
+	aes${dir}	$rndkey1,$inout2
+	pxor		$rndkey0,$inout5
+	$movkey		($key,%rax),$rndkey0
+	add		\$16,%rax
+	jmp		.L${dir}_loop6_enter
+.align	16
+.L${dir}_loop6:
+	aes${dir}	$rndkey1,$inout0
+	aes${dir}	$rndkey1,$inout1
+	aes${dir}	$rndkey1,$inout2
+.L${dir}_loop6_enter:
+	aes${dir}	$rndkey1,$inout3
+	aes${dir}	$rndkey1,$inout4
+	aes${dir}	$rndkey1,$inout5
+	$movkey		($key,%rax),$rndkey1
+	add		\$32,%rax
+	aes${dir}	$rndkey0,$inout0
+	aes${dir}	$rndkey0,$inout1
+	aes${dir}	$rndkey0,$inout2
+	aes${dir}	$rndkey0,$inout3
+	aes${dir}	$rndkey0,$inout4
+	aes${dir}	$rndkey0,$inout5
+	$movkey		-16($key,%rax),$rndkey0
+	jnz		.L${dir}_loop6
+
+	aes${dir}	$rndkey1,$inout0
+	aes${dir}	$rndkey1,$inout1
+	aes${dir}	$rndkey1,$inout2
+	aes${dir}	$rndkey1,$inout3
+	aes${dir}	$rndkey1,$inout4
+	aes${dir}	$rndkey1,$inout5
+	aes${dir}last	$rndkey0,$inout0
+	aes${dir}last	$rndkey0,$inout1
+	aes${dir}last	$rndkey0,$inout2
+	aes${dir}last	$rndkey0,$inout3
+	aes${dir}last	$rndkey0,$inout4
+	aes${dir}last	$rndkey0,$inout5
+	ret
+.size	_aesni_${dir}rypt6,.-_aesni_${dir}rypt6
+___
+}
+sub aesni_generate8 {
+my $dir=shift;
+# As already mentioned it takes in $key and $rounds, which are *not*
+# preserved. $inout[0-7] is cipher/clear text...
+$code.=<<___;
+.type	_aesni_${dir}rypt8,\@abi-omnipotent
+.align	16
+_aesni_${dir}rypt8:
+	$movkey		($key),$rndkey0
+	shl		\$4,$rounds
+	$movkey		16($key),$rndkey1
+	xorps		$rndkey0,$inout0
+	xorps		$rndkey0,$inout1
+	pxor		$rndkey0,$inout2
+	pxor		$rndkey0,$inout3
+	pxor		$rndkey0,$inout4
+	lea		32($key,$rounds),$key
+	neg		%rax			# $rounds
+	aes${dir}	$rndkey1,$inout0
+	pxor		$rndkey0,$inout5
+	pxor		$rndkey0,$inout6
+	aes${dir}	$rndkey1,$inout1
+	pxor		$rndkey0,$inout7
+	$movkey		($key,%rax),$rndkey0
+	add		\$16,%rax
+	jmp		.L${dir}_loop8_inner
+.align	16
+.L${dir}_loop8:
+	aes${dir}	$rndkey1,$inout0
+	aes${dir}	$rndkey1,$inout1
+.L${dir}_loop8_inner:
+	aes${dir}	$rndkey1,$inout2
+	aes${dir}	$rndkey1,$inout3
+	aes${dir}	$rndkey1,$inout4
+	aes${dir}	$rndkey1,$inout5
+	aes${dir}	$rndkey1,$inout6
+	aes${dir}	$rndkey1,$inout7
+.L${dir}_loop8_enter:
+	$movkey		($key,%rax),$rndkey1
+	add		\$32,%rax
+	aes${dir}	$rndkey0,$inout0
+	aes${dir}	$rndkey0,$inout1
+	aes${dir}	$rndkey0,$inout2
+	aes${dir}	$rndkey0,$inout3
+	aes${dir}	$rndkey0,$inout4
+	aes${dir}	$rndkey0,$inout5
+	aes${dir}	$rndkey0,$inout6
+	aes${dir}	$rndkey0,$inout7
+	$movkey		-16($key,%rax),$rndkey0
+	jnz		.L${dir}_loop8
+
+	aes${dir}	$rndkey1,$inout0
+	aes${dir}	$rndkey1,$inout1
+	aes${dir}	$rndkey1,$inout2
+	aes${dir}	$rndkey1,$inout3
+	aes${dir}	$rndkey1,$inout4
+	aes${dir}	$rndkey1,$inout5
+	aes${dir}	$rndkey1,$inout6
+	aes${dir}	$rndkey1,$inout7
+	aes${dir}last	$rndkey0,$inout0
+	aes${dir}last	$rndkey0,$inout1
+	aes${dir}last	$rndkey0,$inout2
+	aes${dir}last	$rndkey0,$inout3
+	aes${dir}last	$rndkey0,$inout4
+	aes${dir}last	$rndkey0,$inout5
+	aes${dir}last	$rndkey0,$inout6
+	aes${dir}last	$rndkey0,$inout7
+	ret
+.size	_aesni_${dir}rypt8,.-_aesni_${dir}rypt8
+___
+}
+&aesni_generate2("enc") if ($PREFIX eq "aesni");
+&aesni_generate2("dec");
+&aesni_generate3("enc") if ($PREFIX eq "aesni");
+&aesni_generate3("dec");
+&aesni_generate4("enc") if ($PREFIX eq "aesni");
+&aesni_generate4("dec");
+&aesni_generate6("enc") if ($PREFIX eq "aesni");
+&aesni_generate6("dec");
+&aesni_generate8("enc") if ($PREFIX eq "aesni");
+&aesni_generate8("dec");
+
+if ($PREFIX eq "aesni") {
+########################################################################
+# void aesni_ecb_encrypt (const void *in, void *out,
+#			  size_t length, const AES_KEY *key,
+#			  int enc);
+$code.=<<___;
+.globl	aesni_ecb_encrypt
+.type	aesni_ecb_encrypt,\@function,5
+.align	16
+aesni_ecb_encrypt:
+___
+$code.=<<___ if ($win64);
+	lea	-0x58(%rsp),%rsp
+	movaps	%xmm6,(%rsp)		# offload $inout4..7
+	movaps	%xmm7,0x10(%rsp)
+	movaps	%xmm8,0x20(%rsp)
+	movaps	%xmm9,0x30(%rsp)
+.Lecb_enc_body:
+___
+$code.=<<___;
+	and	\$-16,$len		# if ($len<16)
+	jz	.Lecb_ret		# return
+
+	mov	240($key),$rounds	# key->rounds
+	$movkey	($key),$rndkey0
+	mov	$key,$key_		# backup $key
+	mov	$rounds,$rnds_		# backup $rounds
+	test	%r8d,%r8d		# 5th argument
+	jz	.Lecb_decrypt
+#--------------------------- ECB ENCRYPT ------------------------------#
+	cmp	\$0x80,$len		# if ($len<8*16)
+	jb	.Lecb_enc_tail		# short input
+
+	movdqu	($inp),$inout0		# load 8 input blocks
+	movdqu	0x10($inp),$inout1
+	movdqu	0x20($inp),$inout2
+	movdqu	0x30($inp),$inout3
+	movdqu	0x40($inp),$inout4
+	movdqu	0x50($inp),$inout5
+	movdqu	0x60($inp),$inout6
+	movdqu	0x70($inp),$inout7
+	lea	0x80($inp),$inp		# $inp+=8*16
+	sub	\$0x80,$len		# $len-=8*16 (can be zero)
+	jmp	.Lecb_enc_loop8_enter
+.align 16
+.Lecb_enc_loop8:
+	movups	$inout0,($out)		# store 8 output blocks
+	mov	$key_,$key		# restore $key
+	movdqu	($inp),$inout0		# load 8 input blocks
+	mov	$rnds_,$rounds		# restore $rounds
+	movups	$inout1,0x10($out)
+	movdqu	0x10($inp),$inout1
+	movups	$inout2,0x20($out)
+	movdqu	0x20($inp),$inout2
+	movups	$inout3,0x30($out)
+	movdqu	0x30($inp),$inout3
+	movups	$inout4,0x40($out)
+	movdqu	0x40($inp),$inout4
+	movups	$inout5,0x50($out)
+	movdqu	0x50($inp),$inout5
+	movups	$inout6,0x60($out)
+	movdqu	0x60($inp),$inout6
+	movups	$inout7,0x70($out)
+	lea	0x80($out),$out		# $out+=8*16
+	movdqu	0x70($inp),$inout7
+	lea	0x80($inp),$inp		# $inp+=8*16
+.Lecb_enc_loop8_enter:
+
+	call	_aesni_encrypt8
+
+	sub	\$0x80,$len
+	jnc	.Lecb_enc_loop8		# loop if $len-=8*16 didn't borrow
+
+	movups	$inout0,($out)		# store 8 output blocks
+	mov	$key_,$key		# restore $key
+	movups	$inout1,0x10($out)
+	mov	$rnds_,$rounds		# restore $rounds
+	movups	$inout2,0x20($out)
+	movups	$inout3,0x30($out)
+	movups	$inout4,0x40($out)
+	movups	$inout5,0x50($out)
+	movups	$inout6,0x60($out)
+	movups	$inout7,0x70($out)
+	lea	0x80($out),$out		# $out+=8*16
+	add	\$0x80,$len		# restore real remaining $len
+	jz	.Lecb_ret		# done if ($len==0)
+
+.Lecb_enc_tail:				# $len is less than 8*16
+	movups	($inp),$inout0
+	cmp	\$0x20,$len
+	jb	.Lecb_enc_one
+	movups	0x10($inp),$inout1
+	je	.Lecb_enc_two
+	movups	0x20($inp),$inout2
+	cmp	\$0x40,$len
+	jb	.Lecb_enc_three
+	movups	0x30($inp),$inout3
+	je	.Lecb_enc_four
+	movups	0x40($inp),$inout4
+	cmp	\$0x60,$len
+	jb	.Lecb_enc_five
+	movups	0x50($inp),$inout5
+	je	.Lecb_enc_six
+	movdqu	0x60($inp),$inout6
+	xorps	$inout7,$inout7
+	call	_aesni_encrypt8
+	movups	$inout0,($out)		# store 7 output blocks
+	movups	$inout1,0x10($out)
+	movups	$inout2,0x20($out)
+	movups	$inout3,0x30($out)
+	movups	$inout4,0x40($out)
+	movups	$inout5,0x50($out)
+	movups	$inout6,0x60($out)
+	jmp	.Lecb_ret
+.align	16
+.Lecb_enc_one:
+___
+	&aesni_generate1("enc",$key,$rounds);
+$code.=<<___;
+	movups	$inout0,($out)		# store one output block
+	jmp	.Lecb_ret
+.align	16
+.Lecb_enc_two:
+	call	_aesni_encrypt2
+	movups	$inout0,($out)		# store 2 output blocks
+	movups	$inout1,0x10($out)
+	jmp	.Lecb_ret
+.align	16
+.Lecb_enc_three:
+	call	_aesni_encrypt3
+	movups	$inout0,($out)		# store 3 output blocks
+	movups	$inout1,0x10($out)
+	movups	$inout2,0x20($out)
+	jmp	.Lecb_ret
+.align	16
+.Lecb_enc_four:
+	call	_aesni_encrypt4
+	movups	$inout0,($out)		# store 4 output blocks
+	movups	$inout1,0x10($out)
+	movups	$inout2,0x20($out)
+	movups	$inout3,0x30($out)
+	jmp	.Lecb_ret
+.align	16
+.Lecb_enc_five:
+	xorps	$inout5,$inout5
+	call	_aesni_encrypt6
+	movups	$inout0,($out)		# store 5 output blocks
+	movups	$inout1,0x10($out)
+	movups	$inout2,0x20($out)
+	movups	$inout3,0x30($out)
+	movups	$inout4,0x40($out)
+	jmp	.Lecb_ret
+.align	16
+.Lecb_enc_six:
+	call	_aesni_encrypt6
+	movups	$inout0,($out)		# store 6 output blocks
+	movups	$inout1,0x10($out)
+	movups	$inout2,0x20($out)
+	movups	$inout3,0x30($out)
+	movups	$inout4,0x40($out)
+	movups	$inout5,0x50($out)
+	jmp	.Lecb_ret
+#--------------------------- ECB DECRYPT ------------------------------#
+.align	16
+.Lecb_decrypt:
+	cmp	\$0x80,$len		# if ($len<8*16)
+	jb	.Lecb_dec_tail		# short input
+
+	movdqu	($inp),$inout0		# load 8 input blocks
+	movdqu	0x10($inp),$inout1
+	movdqu	0x20($inp),$inout2
+	movdqu	0x30($inp),$inout3
+	movdqu	0x40($inp),$inout4
+	movdqu	0x50($inp),$inout5
+	movdqu	0x60($inp),$inout6
+	movdqu	0x70($inp),$inout7
+	lea	0x80($inp),$inp		# $inp+=8*16
+	sub	\$0x80,$len		# $len-=8*16 (can be zero)
+	jmp	.Lecb_dec_loop8_enter
+.align 16
+.Lecb_dec_loop8:
+	movups	$inout0,($out)		# store 8 output blocks
+	mov	$key_,$key		# restore $key
+	movdqu	($inp),$inout0		# load 8 input blocks
+	mov	$rnds_,$rounds		# restore $rounds
+	movups	$inout1,0x10($out)
+	movdqu	0x10($inp),$inout1
+	movups	$inout2,0x20($out)
+	movdqu	0x20($inp),$inout2
+	movups	$inout3,0x30($out)
+	movdqu	0x30($inp),$inout3
+	movups	$inout4,0x40($out)
+	movdqu	0x40($inp),$inout4
+	movups	$inout5,0x50($out)
+	movdqu	0x50($inp),$inout5
+	movups	$inout6,0x60($out)
+	movdqu	0x60($inp),$inout6
+	movups	$inout7,0x70($out)
+	lea	0x80($out),$out		# $out+=8*16
+	movdqu	0x70($inp),$inout7
+	lea	0x80($inp),$inp		# $inp+=8*16
+.Lecb_dec_loop8_enter:
+
+	call	_aesni_decrypt8
+
+	$movkey	($key_),$rndkey0
+	sub	\$0x80,$len
+	jnc	.Lecb_dec_loop8		# loop if $len-=8*16 didn't borrow
+
+	movups	$inout0,($out)		# store 8 output blocks
+	 pxor	$inout0,$inout0		# clear register bank
+	mov	$key_,$key		# restore $key
+	movups	$inout1,0x10($out)
+	 pxor	$inout1,$inout1
+	mov	$rnds_,$rounds		# restore $rounds
+	movups	$inout2,0x20($out)
+	 pxor	$inout2,$inout2
+	movups	$inout3,0x30($out)
+	 pxor	$inout3,$inout3
+	movups	$inout4,0x40($out)
+	 pxor	$inout4,$inout4
+	movups	$inout5,0x50($out)
+	 pxor	$inout5,$inout5
+	movups	$inout6,0x60($out)
+	 pxor	$inout6,$inout6
+	movups	$inout7,0x70($out)
+	 pxor	$inout7,$inout7
+	lea	0x80($out),$out		# $out+=8*16
+	add	\$0x80,$len		# restore real remaining $len
+	jz	.Lecb_ret		# done if ($len==0)
+
+.Lecb_dec_tail:
+	movups	($inp),$inout0
+	cmp	\$0x20,$len
+	jb	.Lecb_dec_one
+	movups	0x10($inp),$inout1
+	je	.Lecb_dec_two
+	movups	0x20($inp),$inout2
+	cmp	\$0x40,$len
+	jb	.Lecb_dec_three
+	movups	0x30($inp),$inout3
+	je	.Lecb_dec_four
+	movups	0x40($inp),$inout4
+	cmp	\$0x60,$len
+	jb	.Lecb_dec_five
+	movups	0x50($inp),$inout5
+	je	.Lecb_dec_six
+	movups	0x60($inp),$inout6
+	$movkey	($key),$rndkey0
+	xorps	$inout7,$inout7
+	call	_aesni_decrypt8
+	movups	$inout0,($out)		# store 7 output blocks
+	 pxor	$inout0,$inout0		# clear register bank
+	movups	$inout1,0x10($out)
+	 pxor	$inout1,$inout1
+	movups	$inout2,0x20($out)
+	 pxor	$inout2,$inout2
+	movups	$inout3,0x30($out)
+	 pxor	$inout3,$inout3
+	movups	$inout4,0x40($out)
+	 pxor	$inout4,$inout4
+	movups	$inout5,0x50($out)
+	 pxor	$inout5,$inout5
+	movups	$inout6,0x60($out)
+	 pxor	$inout6,$inout6
+	 pxor	$inout7,$inout7
+	jmp	.Lecb_ret
+.align	16
+.Lecb_dec_one:
+___
+	&aesni_generate1("dec",$key,$rounds);
+$code.=<<___;
+	movups	$inout0,($out)		# store one output block
+	 pxor	$inout0,$inout0		# clear register bank
+	jmp	.Lecb_ret
+.align	16
+.Lecb_dec_two:
+	call	_aesni_decrypt2
+	movups	$inout0,($out)		# store 2 output blocks
+	 pxor	$inout0,$inout0		# clear register bank
+	movups	$inout1,0x10($out)
+	 pxor	$inout1,$inout1
+	jmp	.Lecb_ret
+.align	16
+.Lecb_dec_three:
+	call	_aesni_decrypt3
+	movups	$inout0,($out)		# store 3 output blocks
+	 pxor	$inout0,$inout0		# clear register bank
+	movups	$inout1,0x10($out)
+	 pxor	$inout1,$inout1
+	movups	$inout2,0x20($out)
+	 pxor	$inout2,$inout2
+	jmp	.Lecb_ret
+.align	16
+.Lecb_dec_four:
+	call	_aesni_decrypt4
+	movups	$inout0,($out)		# store 4 output blocks
+	 pxor	$inout0,$inout0		# clear register bank
+	movups	$inout1,0x10($out)
+	 pxor	$inout1,$inout1
+	movups	$inout2,0x20($out)
+	 pxor	$inout2,$inout2
+	movups	$inout3,0x30($out)
+	 pxor	$inout3,$inout3
+	jmp	.Lecb_ret
+.align	16
+.Lecb_dec_five:
+	xorps	$inout5,$inout5
+	call	_aesni_decrypt6
+	movups	$inout0,($out)		# store 5 output blocks
+	 pxor	$inout0,$inout0		# clear register bank
+	movups	$inout1,0x10($out)
+	 pxor	$inout1,$inout1
+	movups	$inout2,0x20($out)
+	 pxor	$inout2,$inout2
+	movups	$inout3,0x30($out)
+	 pxor	$inout3,$inout3
+	movups	$inout4,0x40($out)
+	 pxor	$inout4,$inout4
+	 pxor	$inout5,$inout5
+	jmp	.Lecb_ret
+.align	16
+.Lecb_dec_six:
+	call	_aesni_decrypt6
+	movups	$inout0,($out)		# store 6 output blocks
+	 pxor	$inout0,$inout0		# clear register bank
+	movups	$inout1,0x10($out)
+	 pxor	$inout1,$inout1
+	movups	$inout2,0x20($out)
+	 pxor	$inout2,$inout2
+	movups	$inout3,0x30($out)
+	 pxor	$inout3,$inout3
+	movups	$inout4,0x40($out)
+	 pxor	$inout4,$inout4
+	movups	$inout5,0x50($out)
+	 pxor	$inout5,$inout5
+
+.Lecb_ret:
+	xorps	$rndkey0,$rndkey0	# %xmm0
+	pxor	$rndkey1,$rndkey1
+___
+$code.=<<___ if ($win64);
+	movaps	(%rsp),%xmm6
+	movaps	%xmm0,(%rsp)		# clear stack
+	movaps	0x10(%rsp),%xmm7
+	movaps	%xmm0,0x10(%rsp)
+	movaps	0x20(%rsp),%xmm8
+	movaps	%xmm0,0x20(%rsp)
+	movaps	0x30(%rsp),%xmm9
+	movaps	%xmm0,0x30(%rsp)
+	lea	0x58(%rsp),%rsp
+.Lecb_enc_ret:
+___
+$code.=<<___;
+	ret
+.size	aesni_ecb_encrypt,.-aesni_ecb_encrypt
+___
+
+{
+######################################################################
+# void aesni_ccm64_[en|de]crypt_blocks (const void *in, void *out,
+#                         size_t blocks, const AES_KEY *key,
+#                         const char *ivec,char *cmac);
+#
+# Handles only complete blocks, operates on 64-bit counter and
+# does not update *ivec! Nor does it finalize CMAC value
+# (see engine/eng_aesni.c for details)
+#
+{
+my $cmac="%r9";	# 6th argument
+
+my $increment="%xmm9";
+my $iv="%xmm6";
+my $bswap_mask="%xmm7";
+
+$code.=<<___;
+.globl	aesni_ccm64_encrypt_blocks
+.type	aesni_ccm64_encrypt_blocks,\@function,6
+.align	16
+aesni_ccm64_encrypt_blocks:
+___
+$code.=<<___ if ($win64);
+	lea	-0x58(%rsp),%rsp
+	movaps	%xmm6,(%rsp)		# $iv
+	movaps	%xmm7,0x10(%rsp)	# $bswap_mask
+	movaps	%xmm8,0x20(%rsp)	# $in0
+	movaps	%xmm9,0x30(%rsp)	# $increment
+.Lccm64_enc_body:
+___
+$code.=<<___;
+	mov	240($key),$rounds		# key->rounds
+	movdqu	($ivp),$iv
+	movdqa	.Lincrement64(%rip),$increment
+	movdqa	.Lbswap_mask(%rip),$bswap_mask
+
+	shl	\$4,$rounds
+	mov	\$16,$rnds_
+	lea	0($key),$key_
+	movdqu	($cmac),$inout1
+	movdqa	$iv,$inout0
+	lea	32($key,$rounds),$key		# end of key schedule
+	pshufb	$bswap_mask,$iv
+	sub	%rax,%r10			# twisted $rounds
+	jmp	.Lccm64_enc_outer
+.align	16
+.Lccm64_enc_outer:
+	$movkey	($key_),$rndkey0
+	mov	%r10,%rax
+	movups	($inp),$in0			# load inp
+
+	xorps	$rndkey0,$inout0		# counter
+	$movkey	16($key_),$rndkey1
+	xorps	$in0,$rndkey0
+	xorps	$rndkey0,$inout1		# cmac^=inp
+	$movkey	32($key_),$rndkey0
+
+.Lccm64_enc2_loop:
+	aesenc	$rndkey1,$inout0
+	aesenc	$rndkey1,$inout1
+	$movkey	($key,%rax),$rndkey1
+	add	\$32,%rax
+	aesenc	$rndkey0,$inout0
+	aesenc	$rndkey0,$inout1
+	$movkey	-16($key,%rax),$rndkey0
+	jnz	.Lccm64_enc2_loop
+	aesenc	$rndkey1,$inout0
+	aesenc	$rndkey1,$inout1
+	paddq	$increment,$iv
+	dec	$len				# $len-- ($len is in blocks)
+	aesenclast	$rndkey0,$inout0
+	aesenclast	$rndkey0,$inout1
+
+	lea	16($inp),$inp
+	xorps	$inout0,$in0			# inp ^= E(iv)
+	movdqa	$iv,$inout0
+	movups	$in0,($out)			# save output
+	pshufb	$bswap_mask,$inout0
+	lea	16($out),$out			# $out+=16
+	jnz	.Lccm64_enc_outer		# loop if ($len!=0)
+
+	 pxor	$rndkey0,$rndkey0		# clear register bank
+	 pxor	$rndkey1,$rndkey1
+	 pxor	$inout0,$inout0
+	movups	$inout1,($cmac)			# store resulting mac
+	 pxor	$inout1,$inout1
+	 pxor	$in0,$in0
+	 pxor	$iv,$iv
+___
+$code.=<<___ if ($win64);
+	movaps	(%rsp),%xmm6
+	movaps	%xmm0,(%rsp)			# clear stack
+	movaps	0x10(%rsp),%xmm7
+	movaps	%xmm0,0x10(%rsp)
+	movaps	0x20(%rsp),%xmm8
+	movaps	%xmm0,0x20(%rsp)
+	movaps	0x30(%rsp),%xmm9
+	movaps	%xmm0,0x30(%rsp)
+	lea	0x58(%rsp),%rsp
+.Lccm64_enc_ret:
+___
+$code.=<<___;
+	ret
+.size	aesni_ccm64_encrypt_blocks,.-aesni_ccm64_encrypt_blocks
+___
+######################################################################
+$code.=<<___;
+.globl	aesni_ccm64_decrypt_blocks
+.type	aesni_ccm64_decrypt_blocks,\@function,6
+.align	16
+aesni_ccm64_decrypt_blocks:
+___
+$code.=<<___ if ($win64);
+	lea	-0x58(%rsp),%rsp
+	movaps	%xmm6,(%rsp)		# $iv
+	movaps	%xmm7,0x10(%rsp)	# $bswap_mask
+	movaps	%xmm8,0x20(%rsp)	# $in8
+	movaps	%xmm9,0x30(%rsp)	# $increment
+.Lccm64_dec_body:
+___
+$code.=<<___;
+	mov	240($key),$rounds		# key->rounds
+	movups	($ivp),$iv
+	movdqu	($cmac),$inout1
+	movdqa	.Lincrement64(%rip),$increment
+	movdqa	.Lbswap_mask(%rip),$bswap_mask
+
+	movaps	$iv,$inout0
+	mov	$rounds,$rnds_
+	mov	$key,$key_
+	pshufb	$bswap_mask,$iv
+___
+	&aesni_generate1("enc",$key,$rounds);
+$code.=<<___;
+	shl	\$4,$rnds_
+	mov	\$16,$rounds
+	movups	($inp),$in0			# load inp
+	paddq	$increment,$iv
+	lea	16($inp),$inp			# $inp+=16
+	sub	%r10,%rax			# twisted $rounds
+	lea	32($key_,$rnds_),$key		# end of key schedule
+	mov	%rax,%r10
+	jmp	.Lccm64_dec_outer
+.align	16
+.Lccm64_dec_outer:
+	xorps	$inout0,$in0			# inp ^= E(iv)
+	movdqa	$iv,$inout0
+	movups	$in0,($out)			# save output
+	lea	16($out),$out			# $out+=16
+	pshufb	$bswap_mask,$inout0
+
+	sub	\$1,$len			# $len-- ($len is in blocks)
+	jz	.Lccm64_dec_break		# if ($len==0) break
+
+	$movkey	($key_),$rndkey0
+	mov	%r10,%rax
+	$movkey	16($key_),$rndkey1
+	xorps	$rndkey0,$in0
+	xorps	$rndkey0,$inout0
+	xorps	$in0,$inout1			# cmac^=out
+	$movkey	32($key_),$rndkey0
+	jmp	.Lccm64_dec2_loop
+.align	16
+.Lccm64_dec2_loop:
+	aesenc	$rndkey1,$inout0
+	aesenc	$rndkey1,$inout1
+	$movkey	($key,%rax),$rndkey1
+	add	\$32,%rax
+	aesenc	$rndkey0,$inout0
+	aesenc	$rndkey0,$inout1
+	$movkey	-16($key,%rax),$rndkey0
+	jnz	.Lccm64_dec2_loop
+	movups	($inp),$in0			# load input
+	paddq	$increment,$iv
+	aesenc	$rndkey1,$inout0
+	aesenc	$rndkey1,$inout1
+	aesenclast	$rndkey0,$inout0
+	aesenclast	$rndkey0,$inout1
+	lea	16($inp),$inp			# $inp+=16
+	jmp	.Lccm64_dec_outer
+
+.align	16
+.Lccm64_dec_break:
+	#xorps	$in0,$inout1			# cmac^=out
+	mov	240($key_),$rounds
+___
+	&aesni_generate1("enc",$key_,$rounds,$inout1,$in0);
+$code.=<<___;
+	 pxor	$rndkey0,$rndkey0		# clear register bank
+	 pxor	$rndkey1,$rndkey1
+	 pxor	$inout0,$inout0
+	movups	$inout1,($cmac)			# store resulting mac
+	 pxor	$inout1,$inout1
+	 pxor	$in0,$in0
+	 pxor	$iv,$iv
+___
+$code.=<<___ if ($win64);
+	movaps	(%rsp),%xmm6
+	movaps	%xmm0,(%rsp)			# clear stack
+	movaps	0x10(%rsp),%xmm7
+	movaps	%xmm0,0x10(%rsp)
+	movaps	0x20(%rsp),%xmm8
+	movaps	%xmm0,0x20(%rsp)
+	movaps	0x30(%rsp),%xmm9
+	movaps	%xmm0,0x30(%rsp)
+	lea	0x58(%rsp),%rsp
+.Lccm64_dec_ret:
+___
+$code.=<<___;
+	ret
+.size	aesni_ccm64_decrypt_blocks,.-aesni_ccm64_decrypt_blocks
+___
+}
+######################################################################
+# void aesni_ctr32_encrypt_blocks (const void *in, void *out,
+#                         size_t blocks, const AES_KEY *key,
+#                         const char *ivec);
+#
+# Handles only complete blocks, operates on 32-bit counter and
+# does not update *ivec! (see crypto/modes/ctr128.c for details)
+#
+# Overhaul based on suggestions from Shay Gueron and Vlad Krasnov,
+# http://rt.openssl.org/Ticket/Display.html?id=3021&user=guest&pass=guest.
+# Keywords are full unroll and modulo-schedule counter calculations
+# with zero-round key xor.
+{
+my ($in0,$in1,$in2,$in3,$in4,$in5)=map("%xmm$_",(10..15));
+my ($key0,$ctr)=("${key_}d","${ivp}d");
+my $frame_size = 0x80 + ($win64?160:0);
+
+$code.=<<___;
+.globl	aesni_ctr32_encrypt_blocks
+.type	aesni_ctr32_encrypt_blocks,\@function,5
+.align	16
+aesni_ctr32_encrypt_blocks:
+	cmp	\$1,$len
+	jne	.Lctr32_bulk
+
+	# handle single block without allocating stack frame,
+	# useful when handling edges
+	movups	($ivp),$inout0
+	movups	($inp),$inout1
+	mov	240($key),%edx			# key->rounds
+___
+	&aesni_generate1("enc",$key,"%edx");
+$code.=<<___;
+	 pxor	$rndkey0,$rndkey0		# clear register bank
+	 pxor	$rndkey1,$rndkey1
+	xorps	$inout1,$inout0
+	 pxor	$inout1,$inout1
+	movups	$inout0,($out)
+	 xorps	$inout0,$inout0
+	jmp	.Lctr32_epilogue
+
+.align	16
+.Lctr32_bulk:
+	lea	(%rsp),%rax
+	push	%rbp
+	sub	\$$frame_size,%rsp
+	and	\$-16,%rsp	# Linux kernel stack can be incorrectly seeded
+___
+$code.=<<___ if ($win64);
+	movaps	%xmm6,-0xa8(%rax)		# offload everything
+	movaps	%xmm7,-0x98(%rax)
+	movaps	%xmm8,-0x88(%rax)
+	movaps	%xmm9,-0x78(%rax)
+	movaps	%xmm10,-0x68(%rax)
+	movaps	%xmm11,-0x58(%rax)
+	movaps	%xmm12,-0x48(%rax)
+	movaps	%xmm13,-0x38(%rax)
+	movaps	%xmm14,-0x28(%rax)
+	movaps	%xmm15,-0x18(%rax)
+.Lctr32_body:
+___
+$code.=<<___;
+	lea	-8(%rax),%rbp
+
+	# 8 16-byte words on top of stack are counter values
+	# xor-ed with zero-round key
+
+	movdqu	($ivp),$inout0
+	movdqu	($key),$rndkey0
+	mov	12($ivp),$ctr			# counter LSB
+	pxor	$rndkey0,$inout0
+	mov	12($key),$key0			# 0-round key LSB
+	movdqa	$inout0,0x00(%rsp)		# populate counter block
+	bswap	$ctr
+	movdqa	$inout0,$inout1
+	movdqa	$inout0,$inout2
+	movdqa	$inout0,$inout3
+	movdqa	$inout0,0x40(%rsp)
+	movdqa	$inout0,0x50(%rsp)
+	movdqa	$inout0,0x60(%rsp)
+	mov	%rdx,%r10			# about to borrow %rdx
+	movdqa	$inout0,0x70(%rsp)
+
+	lea	1($ctr),%rax
+	 lea	2($ctr),%rdx
+	bswap	%eax
+	 bswap	%edx
+	xor	$key0,%eax
+	 xor	$key0,%edx
+	pinsrd	\$3,%eax,$inout1
+	lea	3($ctr),%rax
+	movdqa	$inout1,0x10(%rsp)
+	 pinsrd	\$3,%edx,$inout2
+	bswap	%eax
+	 mov	%r10,%rdx			# restore %rdx
+	 lea	4($ctr),%r10
+	 movdqa	$inout2,0x20(%rsp)
+	xor	$key0,%eax
+	 bswap	%r10d
+	pinsrd	\$3,%eax,$inout3
+	 xor	$key0,%r10d
+	movdqa	$inout3,0x30(%rsp)
+	lea	5($ctr),%r9
+	 mov	%r10d,0x40+12(%rsp)
+	bswap	%r9d
+	 lea	6($ctr),%r10
+	mov	240($key),$rounds		# key->rounds
+	xor	$key0,%r9d
+	 bswap	%r10d
+	mov	%r9d,0x50+12(%rsp)
+	 xor	$key0,%r10d
+	lea	7($ctr),%r9
+	 mov	%r10d,0x60+12(%rsp)
+	bswap	%r9d
+	 mov	OPENSSL_ia32cap_P+4(%rip),%r10d 
+	xor	$key0,%r9d
+	 and	\$`1<<26|1<<22`,%r10d		# isolate XSAVE+MOVBE
+	mov	%r9d,0x70+12(%rsp)
+
+	$movkey	0x10($key),$rndkey1
+
+	movdqa	0x40(%rsp),$inout4
+	movdqa	0x50(%rsp),$inout5
+
+	cmp	\$8,$len		# $len is in blocks
+	jb	.Lctr32_tail		# short input if ($len<8)
+
+	sub	\$6,$len		# $len is biased by -6
+	cmp	\$`1<<22`,%r10d		# check for MOVBE without XSAVE
+	je	.Lctr32_6x		# [which denotes Atom Silvermont]
+
+	lea	0x80($key),$key		# size optimization
+	sub	\$2,$len		# $len is biased by -8
+	jmp	.Lctr32_loop8
+
+.align	16
+.Lctr32_6x:
+	shl	\$4,$rounds
+	mov	\$48,$rnds_
+	bswap	$key0
+	lea	32($key,$rounds),$key	# end of key schedule
+	sub	%rax,%r10		# twisted $rounds
+	jmp	.Lctr32_loop6
+
+.align	16
+.Lctr32_loop6:
+	 add	\$6,$ctr		# next counter value
+	$movkey	-48($key,$rnds_),$rndkey0
+	aesenc	$rndkey1,$inout0
+	 mov	$ctr,%eax
+	 xor	$key0,%eax
+	aesenc	$rndkey1,$inout1
+	 movbe	%eax,`0x00+12`(%rsp)	# store next counter value
+	 lea	1($ctr),%eax
+	aesenc	$rndkey1,$inout2
+	 xor	$key0,%eax
+	 movbe	%eax,`0x10+12`(%rsp)
+	aesenc	$rndkey1,$inout3
+	 lea	2($ctr),%eax
+	 xor	$key0,%eax
+	aesenc	$rndkey1,$inout4
+	 movbe	%eax,`0x20+12`(%rsp)
+	 lea	3($ctr),%eax
+	aesenc	$rndkey1,$inout5
+	$movkey	-32($key,$rnds_),$rndkey1
+	 xor	$key0,%eax
+
+	aesenc	$rndkey0,$inout0
+	 movbe	%eax,`0x30+12`(%rsp)
+	 lea	4($ctr),%eax
+	aesenc	$rndkey0,$inout1
+	 xor	$key0,%eax
+	 movbe	%eax,`0x40+12`(%rsp)
+	aesenc	$rndkey0,$inout2
+	 lea	5($ctr),%eax
+	 xor	$key0,%eax
+	aesenc	$rndkey0,$inout3
+	 movbe	%eax,`0x50+12`(%rsp)
+	 mov	%r10,%rax		# mov	$rnds_,$rounds
+	aesenc	$rndkey0,$inout4
+	aesenc	$rndkey0,$inout5
+	$movkey	-16($key,$rnds_),$rndkey0
+
+	call	.Lenc_loop6
+
+	movdqu	($inp),$inout6		# load 6 input blocks
+	movdqu	0x10($inp),$inout7
+	movdqu	0x20($inp),$in0
+	movdqu	0x30($inp),$in1
+	movdqu	0x40($inp),$in2
+	movdqu	0x50($inp),$in3
+	lea	0x60($inp),$inp		# $inp+=6*16
+	$movkey	-64($key,$rnds_),$rndkey1
+	pxor	$inout0,$inout6		# inp^=E(ctr)
+	movaps	0x00(%rsp),$inout0	# load next counter [xor-ed with 0 round]
+	pxor	$inout1,$inout7
+	movaps	0x10(%rsp),$inout1
+	pxor	$inout2,$in0
+	movaps	0x20(%rsp),$inout2
+	pxor	$inout3,$in1
+	movaps	0x30(%rsp),$inout3
+	pxor	$inout4,$in2
+	movaps	0x40(%rsp),$inout4
+	pxor	$inout5,$in3
+	movaps	0x50(%rsp),$inout5
+	movdqu	$inout6,($out)		# store 6 output blocks
+	movdqu	$inout7,0x10($out)
+	movdqu	$in0,0x20($out)
+	movdqu	$in1,0x30($out)
+	movdqu	$in2,0x40($out)
+	movdqu	$in3,0x50($out)
+	lea	0x60($out),$out		# $out+=6*16
+
+	sub	\$6,$len
+	jnc	.Lctr32_loop6		# loop if $len-=6 didn't borrow
+
+	add	\$6,$len		# restore real remaining $len
+	jz	.Lctr32_done		# done if ($len==0)
+
+	lea	-48($rnds_),$rounds
+	lea	-80($key,$rnds_),$key	# restore $key
+	neg	$rounds
+	shr	\$4,$rounds		# restore $rounds
+	jmp	.Lctr32_tail
+
+.align	32
+.Lctr32_loop8:
+	 add		\$8,$ctr		# next counter value
+	movdqa		0x60(%rsp),$inout6
+	aesenc		$rndkey1,$inout0
+	 mov		$ctr,%r9d
+	movdqa		0x70(%rsp),$inout7
+	aesenc		$rndkey1,$inout1
+	 bswap		%r9d
+	$movkey		0x20-0x80($key),$rndkey0
+	aesenc		$rndkey1,$inout2
+	 xor		$key0,%r9d
+	 nop
+	aesenc		$rndkey1,$inout3
+	 mov		%r9d,0x00+12(%rsp)	# store next counter value
+	 lea		1($ctr),%r9
+	aesenc		$rndkey1,$inout4
+	aesenc		$rndkey1,$inout5
+	aesenc		$rndkey1,$inout6
+	aesenc		$rndkey1,$inout7
+	$movkey		0x30-0x80($key),$rndkey1
+___
+for($i=2;$i<8;$i++) {
+my $rndkeyx = ($i&1)?$rndkey1:$rndkey0;
+$code.=<<___;
+	 bswap		%r9d
+	aesenc		$rndkeyx,$inout0
+	aesenc		$rndkeyx,$inout1
+	 xor		$key0,%r9d
+	 .byte		0x66,0x90
+	aesenc		$rndkeyx,$inout2
+	aesenc		$rndkeyx,$inout3
+	 mov		%r9d,`0x10*($i-1)`+12(%rsp)
+	 lea		$i($ctr),%r9
+	aesenc		$rndkeyx,$inout4
+	aesenc		$rndkeyx,$inout5
+	aesenc		$rndkeyx,$inout6
+	aesenc		$rndkeyx,$inout7
+	$movkey		`0x20+0x10*$i`-0x80($key),$rndkeyx
+___
+}
+$code.=<<___;
+	 bswap		%r9d
+	aesenc		$rndkey0,$inout0
+	aesenc		$rndkey0,$inout1
+	aesenc		$rndkey0,$inout2
+	 xor		$key0,%r9d
+	 movdqu		0x00($inp),$in0		# start loading input
+	aesenc		$rndkey0,$inout3
+	 mov		%r9d,0x70+12(%rsp)
+	 cmp		\$11,$rounds
+	aesenc		$rndkey0,$inout4
+	aesenc		$rndkey0,$inout5
+	aesenc		$rndkey0,$inout6
+	aesenc		$rndkey0,$inout7
+	$movkey		0xa0-0x80($key),$rndkey0
+
+	jb		.Lctr32_enc_done
+
+	aesenc		$rndkey1,$inout0
+	aesenc		$rndkey1,$inout1
+	aesenc		$rndkey1,$inout2
+	aesenc		$rndkey1,$inout3
+	aesenc		$rndkey1,$inout4
+	aesenc		$rndkey1,$inout5
+	aesenc		$rndkey1,$inout6
+	aesenc		$rndkey1,$inout7
+	$movkey		0xb0-0x80($key),$rndkey1
+
+	aesenc		$rndkey0,$inout0
+	aesenc		$rndkey0,$inout1
+	aesenc		$rndkey0,$inout2
+	aesenc		$rndkey0,$inout3
+	aesenc		$rndkey0,$inout4
+	aesenc		$rndkey0,$inout5
+	aesenc		$rndkey0,$inout6
+	aesenc		$rndkey0,$inout7
+	$movkey		0xc0-0x80($key),$rndkey0
+	je		.Lctr32_enc_done
+
+	aesenc		$rndkey1,$inout0
+	aesenc		$rndkey1,$inout1
+	aesenc		$rndkey1,$inout2
+	aesenc		$rndkey1,$inout3
+	aesenc		$rndkey1,$inout4
+	aesenc		$rndkey1,$inout5
+	aesenc		$rndkey1,$inout6
+	aesenc		$rndkey1,$inout7
+	$movkey		0xd0-0x80($key),$rndkey1
+
+	aesenc		$rndkey0,$inout0
+	aesenc		$rndkey0,$inout1
+	aesenc		$rndkey0,$inout2
+	aesenc		$rndkey0,$inout3
+	aesenc		$rndkey0,$inout4
+	aesenc		$rndkey0,$inout5
+	aesenc		$rndkey0,$inout6
+	aesenc		$rndkey0,$inout7
+	$movkey		0xe0-0x80($key),$rndkey0
+	jmp		.Lctr32_enc_done
+
+.align	16
+.Lctr32_enc_done:
+	movdqu		0x10($inp),$in1
+	pxor		$rndkey0,$in0		# input^=round[last]
+	movdqu		0x20($inp),$in2
+	pxor		$rndkey0,$in1
+	movdqu		0x30($inp),$in3
+	pxor		$rndkey0,$in2
+	movdqu		0x40($inp),$in4
+	pxor		$rndkey0,$in3
+	movdqu		0x50($inp),$in5
+	pxor		$rndkey0,$in4
+	pxor		$rndkey0,$in5
+	aesenc		$rndkey1,$inout0
+	aesenc		$rndkey1,$inout1
+	aesenc		$rndkey1,$inout2
+	aesenc		$rndkey1,$inout3
+	aesenc		$rndkey1,$inout4
+	aesenc		$rndkey1,$inout5
+	aesenc		$rndkey1,$inout6
+	aesenc		$rndkey1,$inout7
+	movdqu		0x60($inp),$rndkey1	# borrow $rndkey1 for inp[6]
+	lea		0x80($inp),$inp		# $inp+=8*16
+
+	aesenclast	$in0,$inout0		# $inN is inp[N]^round[last]
+	pxor		$rndkey0,$rndkey1	# borrowed $rndkey
+	movdqu		0x70-0x80($inp),$in0
+	aesenclast	$in1,$inout1
+	pxor		$rndkey0,$in0
+	movdqa		0x00(%rsp),$in1		# load next counter block
+	aesenclast	$in2,$inout2
+	aesenclast	$in3,$inout3
+	movdqa		0x10(%rsp),$in2
+	movdqa		0x20(%rsp),$in3
+	aesenclast	$in4,$inout4
+	aesenclast	$in5,$inout5
+	movdqa		0x30(%rsp),$in4
+	movdqa		0x40(%rsp),$in5
+	aesenclast	$rndkey1,$inout6
+	movdqa		0x50(%rsp),$rndkey0
+	$movkey		0x10-0x80($key),$rndkey1#real 1st-round key
+	aesenclast	$in0,$inout7
+
+	movups		$inout0,($out)		# store 8 output blocks
+	movdqa		$in1,$inout0
+	movups		$inout1,0x10($out)
+	movdqa		$in2,$inout1
+	movups		$inout2,0x20($out)
+	movdqa		$in3,$inout2
+	movups		$inout3,0x30($out)
+	movdqa		$in4,$inout3
+	movups		$inout4,0x40($out)
+	movdqa		$in5,$inout4
+	movups		$inout5,0x50($out)
+	movdqa		$rndkey0,$inout5
+	movups		$inout6,0x60($out)
+	movups		$inout7,0x70($out)
+	lea		0x80($out),$out		# $out+=8*16
+
+	sub	\$8,$len
+	jnc	.Lctr32_loop8			# loop if $len-=8 didn't borrow
+
+	add	\$8,$len			# restore real remainig $len
+	jz	.Lctr32_done			# done if ($len==0)
+	lea	-0x80($key),$key
+
+.Lctr32_tail:
+	# note that at this point $inout0..5 are populated with
+	# counter values xor-ed with 0-round key 
+	lea	16($key),$key
+	cmp	\$4,$len
+	jb	.Lctr32_loop3
+	je	.Lctr32_loop4
+
+	# if ($len>4) compute 7 E(counter)
+	shl		\$4,$rounds
+	movdqa		0x60(%rsp),$inout6
+	pxor		$inout7,$inout7
+
+	$movkey		16($key),$rndkey0
+	aesenc		$rndkey1,$inout0
+	aesenc		$rndkey1,$inout1
+	lea		32-16($key,$rounds),$key# prepare for .Lenc_loop8_enter
+	neg		%rax
+	aesenc		$rndkey1,$inout2
+	add		\$16,%rax		# prepare for .Lenc_loop8_enter
+	 movups		($inp),$in0
+	aesenc		$rndkey1,$inout3
+	aesenc		$rndkey1,$inout4
+	 movups		0x10($inp),$in1		# pre-load input
+	 movups		0x20($inp),$in2
+	aesenc		$rndkey1,$inout5
+	aesenc		$rndkey1,$inout6
+
+	call            .Lenc_loop8_enter
+
+	movdqu	0x30($inp),$in3
+	pxor	$in0,$inout0
+	movdqu	0x40($inp),$in0
+	pxor	$in1,$inout1
+	movdqu	$inout0,($out)			# store output
+	pxor	$in2,$inout2
+	movdqu	$inout1,0x10($out)
+	pxor	$in3,$inout3
+	movdqu	$inout2,0x20($out)
+	pxor	$in0,$inout4
+	movdqu	$inout3,0x30($out)
+	movdqu	$inout4,0x40($out)
+	cmp	\$6,$len
+	jb	.Lctr32_done			# $len was 5, stop store
+
+	movups	0x50($inp),$in1
+	xorps	$in1,$inout5
+	movups	$inout5,0x50($out)
+	je	.Lctr32_done			# $len was 6, stop store
+
+	movups	0x60($inp),$in2
+	xorps	$in2,$inout6
+	movups	$inout6,0x60($out)
+	jmp	.Lctr32_done			# $len was 7, stop store
+
+.align	32
+.Lctr32_loop4:
+	aesenc		$rndkey1,$inout0
+	lea		16($key),$key
+	dec		$rounds
+	aesenc		$rndkey1,$inout1
+	aesenc		$rndkey1,$inout2
+	aesenc		$rndkey1,$inout3
+	$movkey		($key),$rndkey1
+	jnz		.Lctr32_loop4
+	aesenclast	$rndkey1,$inout0
+	aesenclast	$rndkey1,$inout1
+	 movups		($inp),$in0		# load input
+	 movups		0x10($inp),$in1
+	aesenclast	$rndkey1,$inout2
+	aesenclast	$rndkey1,$inout3
+	 movups		0x20($inp),$in2
+	 movups		0x30($inp),$in3
+
+	xorps	$in0,$inout0
+	movups	$inout0,($out)			# store output
+	xorps	$in1,$inout1
+	movups	$inout1,0x10($out)
+	pxor	$in2,$inout2
+	movdqu	$inout2,0x20($out)
+	pxor	$in3,$inout3
+	movdqu	$inout3,0x30($out)
+	jmp	.Lctr32_done			# $len was 4, stop store
+
+.align	32
+.Lctr32_loop3:
+	aesenc		$rndkey1,$inout0
+	lea		16($key),$key
+	dec		$rounds
+	aesenc		$rndkey1,$inout1
+	aesenc		$rndkey1,$inout2
+	$movkey		($key),$rndkey1
+	jnz		.Lctr32_loop3
+	aesenclast	$rndkey1,$inout0
+	aesenclast	$rndkey1,$inout1
+	aesenclast	$rndkey1,$inout2
+
+	movups	($inp),$in0			# load input
+	xorps	$in0,$inout0
+	movups	$inout0,($out)			# store output
+	cmp	\$2,$len
+	jb	.Lctr32_done			# $len was 1, stop store
+
+	movups	0x10($inp),$in1
+	xorps	$in1,$inout1
+	movups	$inout1,0x10($out)
+	je	.Lctr32_done			# $len was 2, stop store
+
+	movups	0x20($inp),$in2
+	xorps	$in2,$inout2
+	movups	$inout2,0x20($out)		# $len was 3, stop store
+
+.Lctr32_done:
+	xorps	%xmm0,%xmm0			# clear regiser bank
+	xor	$key0,$key0
+	pxor	%xmm1,%xmm1
+	pxor	%xmm2,%xmm2
+	pxor	%xmm3,%xmm3
+	pxor	%xmm4,%xmm4
+	pxor	%xmm5,%xmm5
+___
+$code.=<<___ if (!$win64);
+	pxor	%xmm6,%xmm6
+	pxor	%xmm7,%xmm7
+	movaps	%xmm0,0x00(%rsp)		# clear stack
+	pxor	%xmm8,%xmm8
+	movaps	%xmm0,0x10(%rsp)
+	pxor	%xmm9,%xmm9
+	movaps	%xmm0,0x20(%rsp)
+	pxor	%xmm10,%xmm10
+	movaps	%xmm0,0x30(%rsp)
+	pxor	%xmm11,%xmm11
+	movaps	%xmm0,0x40(%rsp)
+	pxor	%xmm12,%xmm12
+	movaps	%xmm0,0x50(%rsp)
+	pxor	%xmm13,%xmm13
+	movaps	%xmm0,0x60(%rsp)
+	pxor	%xmm14,%xmm14
+	movaps	%xmm0,0x70(%rsp)
+	pxor	%xmm15,%xmm15
+___
+$code.=<<___ if ($win64);
+	movaps	-0xa0(%rbp),%xmm6
+	movaps	%xmm0,-0xa0(%rbp)		# clear stack
+	movaps	-0x90(%rbp),%xmm7
+	movaps	%xmm0,-0x90(%rbp)
+	movaps	-0x80(%rbp),%xmm8
+	movaps	%xmm0,-0x80(%rbp)
+	movaps	-0x70(%rbp),%xmm9
+	movaps	%xmm0,-0x70(%rbp)
+	movaps	-0x60(%rbp),%xmm10
+	movaps	%xmm0,-0x60(%rbp)
+	movaps	-0x50(%rbp),%xmm11
+	movaps	%xmm0,-0x50(%rbp)
+	movaps	-0x40(%rbp),%xmm12
+	movaps	%xmm0,-0x40(%rbp)
+	movaps	-0x30(%rbp),%xmm13
+	movaps	%xmm0,-0x30(%rbp)
+	movaps	-0x20(%rbp),%xmm14
+	movaps	%xmm0,-0x20(%rbp)
+	movaps	-0x10(%rbp),%xmm15
+	movaps	%xmm0,-0x10(%rbp)
+	movaps	%xmm0,0x00(%rsp)
+	movaps	%xmm0,0x10(%rsp)
+	movaps	%xmm0,0x20(%rsp)
+	movaps	%xmm0,0x30(%rsp)
+	movaps	%xmm0,0x40(%rsp)
+	movaps	%xmm0,0x50(%rsp)
+	movaps	%xmm0,0x60(%rsp)
+	movaps	%xmm0,0x70(%rsp)
+___
+$code.=<<___;
+	lea	(%rbp),%rsp
+	pop	%rbp
+.Lctr32_epilogue:
+	ret
+.size	aesni_ctr32_encrypt_blocks,.-aesni_ctr32_encrypt_blocks
+___
+}
+
+######################################################################
+# void aesni_xts_[en|de]crypt(const char *inp,char *out,size_t len,
+#	const AES_KEY *key1, const AES_KEY *key2
+#	const unsigned char iv[16]);
+#
+{
+my @tweak=map("%xmm$_",(10..15));
+my ($twmask,$twres,$twtmp)=("%xmm8","%xmm9",@tweak[4]);
+my ($key2,$ivp,$len_)=("%r8","%r9","%r9");
+my $frame_size = 0x70 + ($win64?160:0);
+
+$code.=<<___;
+.globl	aesni_xts_encrypt
+.type	aesni_xts_encrypt,\@function,6
+.align	16
+aesni_xts_encrypt:
+	lea	(%rsp),%rax
+	push	%rbp
+	sub	\$$frame_size,%rsp
+	and	\$-16,%rsp	# Linux kernel stack can be incorrectly seeded
+___
+$code.=<<___ if ($win64);
+	movaps	%xmm6,-0xa8(%rax)		# offload everything
+	movaps	%xmm7,-0x98(%rax)
+	movaps	%xmm8,-0x88(%rax)
+	movaps	%xmm9,-0x78(%rax)
+	movaps	%xmm10,-0x68(%rax)
+	movaps	%xmm11,-0x58(%rax)
+	movaps	%xmm12,-0x48(%rax)
+	movaps	%xmm13,-0x38(%rax)
+	movaps	%xmm14,-0x28(%rax)
+	movaps	%xmm15,-0x18(%rax)
+.Lxts_enc_body:
+___
+$code.=<<___;
+	lea	-8(%rax),%rbp
+	movups	($ivp),$inout0			# load clear-text tweak
+	mov	240(%r8),$rounds		# key2->rounds
+	mov	240($key),$rnds_		# key1->rounds
+___
+	# generate the tweak
+	&aesni_generate1("enc",$key2,$rounds,$inout0);
+$code.=<<___;
+	$movkey	($key),$rndkey0			# zero round key
+	mov	$key,$key_			# backup $key
+	mov	$rnds_,$rounds			# backup $rounds
+	shl	\$4,$rnds_
+	mov	$len,$len_			# backup $len
+	and	\$-16,$len
+
+	$movkey	16($key,$rnds_),$rndkey1	# last round key
+
+	movdqa	.Lxts_magic(%rip),$twmask
+	movdqa	$inout0,@tweak[5]
+	pshufd	\$0x5f,$inout0,$twres
+	pxor	$rndkey0,$rndkey1
+___
+    # alternative tweak calculation algorithm is based on suggestions
+    # by Shay Gueron. psrad doesn't conflict with AES-NI instructions
+    # and should help in the future...
+    for ($i=0;$i<4;$i++) {
+    $code.=<<___;
+	movdqa	$twres,$twtmp
+	paddd	$twres,$twres
+	movdqa	@tweak[5],@tweak[$i]
+	psrad	\$31,$twtmp			# broadcast upper bits
+	paddq	@tweak[5],@tweak[5]
+	pand	$twmask,$twtmp
+	pxor	$rndkey0,@tweak[$i]
+	pxor	$twtmp,@tweak[5]
+___
+    }
+$code.=<<___;
+	movdqa	@tweak[5],@tweak[4]
+	psrad	\$31,$twres
+	paddq	@tweak[5],@tweak[5]
+	pand	$twmask,$twres
+	pxor	$rndkey0,@tweak[4]
+	pxor	$twres,@tweak[5]
+	movaps	$rndkey1,0x60(%rsp)		# save round[0]^round[last]
+
+	sub	\$16*6,$len
+	jc	.Lxts_enc_short			# if $len-=6*16 borrowed
+
+	mov	\$16+96,$rounds
+	lea	32($key_,$rnds_),$key		# end of key schedule
+	sub	%r10,%rax			# twisted $rounds
+	$movkey	16($key_),$rndkey1
+	mov	%rax,%r10			# backup twisted $rounds
+	lea	.Lxts_magic(%rip),%r8
+	jmp	.Lxts_enc_grandloop
+
+.align	32
+.Lxts_enc_grandloop:
+	movdqu	`16*0`($inp),$inout0		# load input
+	movdqa	$rndkey0,$twmask
+	movdqu	`16*1`($inp),$inout1
+	pxor	@tweak[0],$inout0		# input^=tweak^round[0]
+	movdqu	`16*2`($inp),$inout2
+	pxor	@tweak[1],$inout1
+	 aesenc		$rndkey1,$inout0
+	movdqu	`16*3`($inp),$inout3
+	pxor	@tweak[2],$inout2
+	 aesenc		$rndkey1,$inout1
+	movdqu	`16*4`($inp),$inout4
+	pxor	@tweak[3],$inout3
+	 aesenc		$rndkey1,$inout2
+	movdqu	`16*5`($inp),$inout5
+	pxor	@tweak[5],$twmask		# round[0]^=tweak[5]
+	 movdqa	0x60(%rsp),$twres		# load round[0]^round[last]
+	pxor	@tweak[4],$inout4
+	 aesenc		$rndkey1,$inout3
+	$movkey	32($key_),$rndkey0
+	lea	`16*6`($inp),$inp
+	pxor	$twmask,$inout5
+
+	 pxor	$twres,@tweak[0]		# calclulate tweaks^round[last]
+	aesenc		$rndkey1,$inout4
+	 pxor	$twres,@tweak[1]
+	 movdqa	@tweak[0],`16*0`(%rsp)		# put aside tweaks^round[last]
+	aesenc		$rndkey1,$inout5
+	$movkey		48($key_),$rndkey1
+	 pxor	$twres,@tweak[2]
+
+	aesenc		$rndkey0,$inout0
+	 pxor	$twres,@tweak[3]
+	 movdqa	@tweak[1],`16*1`(%rsp)
+	aesenc		$rndkey0,$inout1
+	 pxor	$twres,@tweak[4]
+	 movdqa	@tweak[2],`16*2`(%rsp)
+	aesenc		$rndkey0,$inout2
+	aesenc		$rndkey0,$inout3
+	 pxor	$twres,$twmask
+	 movdqa	@tweak[4],`16*4`(%rsp)
+	aesenc		$rndkey0,$inout4
+	aesenc		$rndkey0,$inout5
+	$movkey		64($key_),$rndkey0
+	 movdqa	$twmask,`16*5`(%rsp)
+	pshufd	\$0x5f,@tweak[5],$twres
+	jmp	.Lxts_enc_loop6
+.align	32
+.Lxts_enc_loop6:
+	aesenc		$rndkey1,$inout0
+	aesenc		$rndkey1,$inout1
+	aesenc		$rndkey1,$inout2
+	aesenc		$rndkey1,$inout3
+	aesenc		$rndkey1,$inout4
+	aesenc		$rndkey1,$inout5
+	$movkey		-64($key,%rax),$rndkey1
+	add		\$32,%rax
+
+	aesenc		$rndkey0,$inout0
+	aesenc		$rndkey0,$inout1
+	aesenc		$rndkey0,$inout2
+	aesenc		$rndkey0,$inout3
+	aesenc		$rndkey0,$inout4
+	aesenc		$rndkey0,$inout5
+	$movkey		-80($key,%rax),$rndkey0
+	jnz		.Lxts_enc_loop6
+
+	movdqa	(%r8),$twmask			# start calculating next tweak
+	movdqa	$twres,$twtmp
+	paddd	$twres,$twres
+	 aesenc		$rndkey1,$inout0
+	paddq	@tweak[5],@tweak[5]
+	psrad	\$31,$twtmp
+	 aesenc		$rndkey1,$inout1
+	pand	$twmask,$twtmp
+	$movkey	($key_),@tweak[0]		# load round[0]
+	 aesenc		$rndkey1,$inout2
+	 aesenc		$rndkey1,$inout3
+	 aesenc		$rndkey1,$inout4
+	pxor	$twtmp,@tweak[5]
+	movaps	@tweak[0],@tweak[1]		# copy round[0]
+	 aesenc		$rndkey1,$inout5
+	 $movkey	-64($key),$rndkey1
+
+	movdqa	$twres,$twtmp
+	 aesenc		$rndkey0,$inout0
+	paddd	$twres,$twres
+	pxor	@tweak[5],@tweak[0]
+	 aesenc		$rndkey0,$inout1
+	psrad	\$31,$twtmp
+	paddq	@tweak[5],@tweak[5]
+	 aesenc		$rndkey0,$inout2
+	 aesenc		$rndkey0,$inout3
+	pand	$twmask,$twtmp
+	movaps	@tweak[1],@tweak[2]
+	 aesenc		$rndkey0,$inout4
+	pxor	$twtmp,@tweak[5]
+	movdqa	$twres,$twtmp
+	 aesenc		$rndkey0,$inout5
+	 $movkey	-48($key),$rndkey0
+
+	paddd	$twres,$twres
+	 aesenc		$rndkey1,$inout0
+	pxor	@tweak[5],@tweak[1]
+	psrad	\$31,$twtmp
+	 aesenc		$rndkey1,$inout1
+	paddq	@tweak[5],@tweak[5]
+	pand	$twmask,$twtmp
+	 aesenc		$rndkey1,$inout2
+	 aesenc		$rndkey1,$inout3
+	 movdqa	@tweak[3],`16*3`(%rsp)
+	pxor	$twtmp,@tweak[5]
+	 aesenc		$rndkey1,$inout4
+	movaps	@tweak[2],@tweak[3]
+	movdqa	$twres,$twtmp
+	 aesenc		$rndkey1,$inout5
+	 $movkey	-32($key),$rndkey1
+
+	paddd	$twres,$twres
+	 aesenc		$rndkey0,$inout0
+	pxor	@tweak[5],@tweak[2]
+	psrad	\$31,$twtmp
+	 aesenc		$rndkey0,$inout1
+	paddq	@tweak[5],@tweak[5]
+	pand	$twmask,$twtmp
+	 aesenc		$rndkey0,$inout2
+	 aesenc		$rndkey0,$inout3
+	 aesenc		$rndkey0,$inout4
+	pxor	$twtmp,@tweak[5]
+	movaps	@tweak[3],@tweak[4]
+	 aesenc		$rndkey0,$inout5
+
+	movdqa	$twres,$rndkey0
+	paddd	$twres,$twres
+	 aesenc		$rndkey1,$inout0
+	pxor	@tweak[5],@tweak[3]
+	psrad	\$31,$rndkey0
+	 aesenc		$rndkey1,$inout1
+	paddq	@tweak[5],@tweak[5]
+	pand	$twmask,$rndkey0
+	 aesenc		$rndkey1,$inout2
+	 aesenc		$rndkey1,$inout3
+	pxor	$rndkey0,@tweak[5]
+	$movkey		($key_),$rndkey0
+	 aesenc		$rndkey1,$inout4
+	 aesenc		$rndkey1,$inout5
+	$movkey		16($key_),$rndkey1
+
+	pxor	@tweak[5],@tweak[4]
+	 aesenclast	`16*0`(%rsp),$inout0
+	psrad	\$31,$twres
+	paddq	@tweak[5],@tweak[5]
+	 aesenclast	`16*1`(%rsp),$inout1
+	 aesenclast	`16*2`(%rsp),$inout2
+	pand	$twmask,$twres
+	mov	%r10,%rax			# restore $rounds
+	 aesenclast	`16*3`(%rsp),$inout3
+	 aesenclast	`16*4`(%rsp),$inout4
+	 aesenclast	`16*5`(%rsp),$inout5
+	pxor	$twres,@tweak[5]
+
+	lea	`16*6`($out),$out		# $out+=6*16
+	movups	$inout0,`-16*6`($out)		# store 6 output blocks
+	movups	$inout1,`-16*5`($out)
+	movups	$inout2,`-16*4`($out)
+	movups	$inout3,`-16*3`($out)
+	movups	$inout4,`-16*2`($out)
+	movups	$inout5,`-16*1`($out)
+	sub	\$16*6,$len
+	jnc	.Lxts_enc_grandloop		# loop if $len-=6*16 didn't borrow
+
+	mov	\$16+96,$rounds
+	sub	$rnds_,$rounds
+	mov	$key_,$key			# restore $key
+	shr	\$4,$rounds			# restore original value
+
+.Lxts_enc_short:
+	# at the point @tweak[0..5] are populated with tweak values
+	mov	$rounds,$rnds_			# backup $rounds
+	pxor	$rndkey0,@tweak[0]
+	add	\$16*6,$len			# restore real remaining $len
+	jz	.Lxts_enc_done			# done if ($len==0)
+
+	pxor	$rndkey0,@tweak[1]
+	cmp	\$0x20,$len
+	jb	.Lxts_enc_one			# $len is 1*16
+	pxor	$rndkey0,@tweak[2]
+	je	.Lxts_enc_two			# $len is 2*16
+
+	pxor	$rndkey0,@tweak[3]
+	cmp	\$0x40,$len
+	jb	.Lxts_enc_three			# $len is 3*16
+	pxor	$rndkey0,@tweak[4]
+	je	.Lxts_enc_four			# $len is 4*16
+
+	movdqu	($inp),$inout0			# $len is 5*16
+	movdqu	16*1($inp),$inout1
+	movdqu	16*2($inp),$inout2
+	pxor	@tweak[0],$inout0
+	movdqu	16*3($inp),$inout3
+	pxor	@tweak[1],$inout1
+	movdqu	16*4($inp),$inout4
+	lea	16*5($inp),$inp			# $inp+=5*16
+	pxor	@tweak[2],$inout2
+	pxor	@tweak[3],$inout3
+	pxor	@tweak[4],$inout4
+	pxor	$inout5,$inout5
+
+	call	_aesni_encrypt6
+
+	xorps	@tweak[0],$inout0
+	movdqa	@tweak[5],@tweak[0]
+	xorps	@tweak[1],$inout1
+	xorps	@tweak[2],$inout2
+	movdqu	$inout0,($out)			# store 5 output blocks
+	xorps	@tweak[3],$inout3
+	movdqu	$inout1,16*1($out)
+	xorps	@tweak[4],$inout4
+	movdqu	$inout2,16*2($out)
+	movdqu	$inout3,16*3($out)
+	movdqu	$inout4,16*4($out)
+	lea	16*5($out),$out			# $out+=5*16
+	jmp	.Lxts_enc_done
+
+.align	16
+.Lxts_enc_one:
+	movups	($inp),$inout0
+	lea	16*1($inp),$inp			# inp+=1*16
+	xorps	@tweak[0],$inout0
+___
+	&aesni_generate1("enc",$key,$rounds);
+$code.=<<___;
+	xorps	@tweak[0],$inout0
+	movdqa	@tweak[1],@tweak[0]
+	movups	$inout0,($out)			# store one output block
+	lea	16*1($out),$out			# $out+=1*16
+	jmp	.Lxts_enc_done
+
+.align	16
+.Lxts_enc_two:
+	movups	($inp),$inout0
+	movups	16($inp),$inout1
+	lea	32($inp),$inp			# $inp+=2*16
+	xorps	@tweak[0],$inout0
+	xorps	@tweak[1],$inout1
+
+	call	_aesni_encrypt2
+
+	xorps	@tweak[0],$inout0
+	movdqa	@tweak[2],@tweak[0]
+	xorps	@tweak[1],$inout1
+	movups	$inout0,($out)			# store 2 output blocks
+	movups	$inout1,16*1($out)
+	lea	16*2($out),$out			# $out+=2*16
+	jmp	.Lxts_enc_done
+
+.align	16
+.Lxts_enc_three:
+	movups	($inp),$inout0
+	movups	16*1($inp),$inout1
+	movups	16*2($inp),$inout2
+	lea	16*3($inp),$inp			# $inp+=3*16
+	xorps	@tweak[0],$inout0
+	xorps	@tweak[1],$inout1
+	xorps	@tweak[2],$inout2
+
+	call	_aesni_encrypt3
+
+	xorps	@tweak[0],$inout0
+	movdqa	@tweak[3],@tweak[0]
+	xorps	@tweak[1],$inout1
+	xorps	@tweak[2],$inout2
+	movups	$inout0,($out)			# store 3 output blocks
+	movups	$inout1,16*1($out)
+	movups	$inout2,16*2($out)
+	lea	16*3($out),$out			# $out+=3*16
+	jmp	.Lxts_enc_done
+
+.align	16
+.Lxts_enc_four:
+	movups	($inp),$inout0
+	movups	16*1($inp),$inout1
+	movups	16*2($inp),$inout2
+	xorps	@tweak[0],$inout0
+	movups	16*3($inp),$inout3
+	lea	16*4($inp),$inp			# $inp+=4*16
+	xorps	@tweak[1],$inout1
+	xorps	@tweak[2],$inout2
+	xorps	@tweak[3],$inout3
+
+	call	_aesni_encrypt4
+
+	pxor	@tweak[0],$inout0
+	movdqa	@tweak[4],@tweak[0]
+	pxor	@tweak[1],$inout1
+	pxor	@tweak[2],$inout2
+	movdqu	$inout0,($out)			# store 4 output blocks
+	pxor	@tweak[3],$inout3
+	movdqu	$inout1,16*1($out)
+	movdqu	$inout2,16*2($out)
+	movdqu	$inout3,16*3($out)
+	lea	16*4($out),$out			# $out+=4*16
+	jmp	.Lxts_enc_done
+
+.align	16
+.Lxts_enc_done:
+	and	\$15,$len_			# see if $len%16 is 0
+	jz	.Lxts_enc_ret
+	mov	$len_,$len
+
+.Lxts_enc_steal:
+	movzb	($inp),%eax			# borrow $rounds ...
+	movzb	-16($out),%ecx			# ... and $key
+	lea	1($inp),$inp
+	mov	%al,-16($out)
+	mov	%cl,0($out)
+	lea	1($out),$out
+	sub	\$1,$len
+	jnz	.Lxts_enc_steal
+
+	sub	$len_,$out			# rewind $out
+	mov	$key_,$key			# restore $key
+	mov	$rnds_,$rounds			# restore $rounds
+
+	movups	-16($out),$inout0
+	xorps	@tweak[0],$inout0
+___
+	&aesni_generate1("enc",$key,$rounds);
+$code.=<<___;
+	xorps	@tweak[0],$inout0
+	movups	$inout0,-16($out)
+
+.Lxts_enc_ret:
+	xorps	%xmm0,%xmm0			# clear register bank
+	pxor	%xmm1,%xmm1
+	pxor	%xmm2,%xmm2
+	pxor	%xmm3,%xmm3
+	pxor	%xmm4,%xmm4
+	pxor	%xmm5,%xmm5
+___
+$code.=<<___ if (!$win64);
+	pxor	%xmm6,%xmm6
+	pxor	%xmm7,%xmm7
+	movaps	%xmm0,0x00(%rsp)		# clear stack
+	pxor	%xmm8,%xmm8
+	movaps	%xmm0,0x10(%rsp)
+	pxor	%xmm9,%xmm9
+	movaps	%xmm0,0x20(%rsp)
+	pxor	%xmm10,%xmm10
+	movaps	%xmm0,0x30(%rsp)
+	pxor	%xmm11,%xmm11
+	movaps	%xmm0,0x40(%rsp)
+	pxor	%xmm12,%xmm12
+	movaps	%xmm0,0x50(%rsp)
+	pxor	%xmm13,%xmm13
+	movaps	%xmm0,0x60(%rsp)
+	pxor	%xmm14,%xmm14
+	pxor	%xmm15,%xmm15
+___
+$code.=<<___ if ($win64);
+	movaps	-0xa0(%rbp),%xmm6
+	movaps	%xmm0,-0xa0(%rbp)		# clear stack
+	movaps	-0x90(%rbp),%xmm7
+	movaps	%xmm0,-0x90(%rbp)
+	movaps	-0x80(%rbp),%xmm8
+	movaps	%xmm0,-0x80(%rbp)
+	movaps	-0x70(%rbp),%xmm9
+	movaps	%xmm0,-0x70(%rbp)
+	movaps	-0x60(%rbp),%xmm10
+	movaps	%xmm0,-0x60(%rbp)
+	movaps	-0x50(%rbp),%xmm11
+	movaps	%xmm0,-0x50(%rbp)
+	movaps	-0x40(%rbp),%xmm12
+	movaps	%xmm0,-0x40(%rbp)
+	movaps	-0x30(%rbp),%xmm13
+	movaps	%xmm0,-0x30(%rbp)
+	movaps	-0x20(%rbp),%xmm14
+	movaps	%xmm0,-0x20(%rbp)
+	movaps	-0x10(%rbp),%xmm15
+	movaps	%xmm0,-0x10(%rbp)
+	movaps	%xmm0,0x00(%rsp)
+	movaps	%xmm0,0x10(%rsp)
+	movaps	%xmm0,0x20(%rsp)
+	movaps	%xmm0,0x30(%rsp)
+	movaps	%xmm0,0x40(%rsp)
+	movaps	%xmm0,0x50(%rsp)
+	movaps	%xmm0,0x60(%rsp)
+___
+$code.=<<___;
+	lea	(%rbp),%rsp
+	pop	%rbp
+.Lxts_enc_epilogue:
+	ret
+.size	aesni_xts_encrypt,.-aesni_xts_encrypt
+___
+
+$code.=<<___;
+.globl	aesni_xts_decrypt
+.type	aesni_xts_decrypt,\@function,6
+.align	16
+aesni_xts_decrypt:
+	lea	(%rsp),%rax
+	push	%rbp
+	sub	\$$frame_size,%rsp
+	and	\$-16,%rsp	# Linux kernel stack can be incorrectly seeded
+___
+$code.=<<___ if ($win64);
+	movaps	%xmm6,-0xa8(%rax)		# offload everything
+	movaps	%xmm7,-0x98(%rax)
+	movaps	%xmm8,-0x88(%rax)
+	movaps	%xmm9,-0x78(%rax)
+	movaps	%xmm10,-0x68(%rax)
+	movaps	%xmm11,-0x58(%rax)
+	movaps	%xmm12,-0x48(%rax)
+	movaps	%xmm13,-0x38(%rax)
+	movaps	%xmm14,-0x28(%rax)
+	movaps	%xmm15,-0x18(%rax)
+.Lxts_dec_body:
+___
+$code.=<<___;
+	lea	-8(%rax),%rbp
+	movups	($ivp),$inout0			# load clear-text tweak
+	mov	240($key2),$rounds		# key2->rounds
+	mov	240($key),$rnds_		# key1->rounds
+___
+	# generate the tweak
+	&aesni_generate1("enc",$key2,$rounds,$inout0);
+$code.=<<___;
+	xor	%eax,%eax			# if ($len%16) len-=16;
+	test	\$15,$len
+	setnz	%al
+	shl	\$4,%rax
+	sub	%rax,$len
+
+	$movkey	($key),$rndkey0			# zero round key
+	mov	$key,$key_			# backup $key
+	mov	$rnds_,$rounds			# backup $rounds
+	shl	\$4,$rnds_
+	mov	$len,$len_			# backup $len
+	and	\$-16,$len
+
+	$movkey	16($key,$rnds_),$rndkey1	# last round key
+
+	movdqa	.Lxts_magic(%rip),$twmask
+	movdqa	$inout0,@tweak[5]
+	pshufd	\$0x5f,$inout0,$twres
+	pxor	$rndkey0,$rndkey1
+___
+    for ($i=0;$i<4;$i++) {
+    $code.=<<___;
+	movdqa	$twres,$twtmp
+	paddd	$twres,$twres
+	movdqa	@tweak[5],@tweak[$i]
+	psrad	\$31,$twtmp			# broadcast upper bits
+	paddq	@tweak[5],@tweak[5]
+	pand	$twmask,$twtmp
+	pxor	$rndkey0,@tweak[$i]
+	pxor	$twtmp,@tweak[5]
+___
+    }
+$code.=<<___;
+	movdqa	@tweak[5],@tweak[4]
+	psrad	\$31,$twres
+	paddq	@tweak[5],@tweak[5]
+	pand	$twmask,$twres
+	pxor	$rndkey0,@tweak[4]
+	pxor	$twres,@tweak[5]
+	movaps	$rndkey1,0x60(%rsp)		# save round[0]^round[last]
+
+	sub	\$16*6,$len
+	jc	.Lxts_dec_short			# if $len-=6*16 borrowed
+
+	mov	\$16+96,$rounds
+	lea	32($key_,$rnds_),$key		# end of key schedule
+	sub	%r10,%rax			# twisted $rounds
+	$movkey	16($key_),$rndkey1
+	mov	%rax,%r10			# backup twisted $rounds
+	lea	.Lxts_magic(%rip),%r8
+	jmp	.Lxts_dec_grandloop
+
+.align	32
+.Lxts_dec_grandloop:
+	movdqu	`16*0`($inp),$inout0		# load input
+	movdqa	$rndkey0,$twmask
+	movdqu	`16*1`($inp),$inout1
+	pxor	@tweak[0],$inout0		# intput^=tweak^round[0]
+	movdqu	`16*2`($inp),$inout2
+	pxor	@tweak[1],$inout1
+	 aesdec		$rndkey1,$inout0
+	movdqu	`16*3`($inp),$inout3
+	pxor	@tweak[2],$inout2
+	 aesdec		$rndkey1,$inout1
+	movdqu	`16*4`($inp),$inout4
+	pxor	@tweak[3],$inout3
+	 aesdec		$rndkey1,$inout2
+	movdqu	`16*5`($inp),$inout5
+	pxor	@tweak[5],$twmask		# round[0]^=tweak[5]
+	 movdqa	0x60(%rsp),$twres		# load round[0]^round[last]
+	pxor	@tweak[4],$inout4
+	 aesdec		$rndkey1,$inout3
+	$movkey	32($key_),$rndkey0
+	lea	`16*6`($inp),$inp
+	pxor	$twmask,$inout5
+
+	 pxor	$twres,@tweak[0]		# calclulate tweaks^round[last]
+	aesdec		$rndkey1,$inout4
+	 pxor	$twres,@tweak[1]
+	 movdqa	@tweak[0],`16*0`(%rsp)		# put aside tweaks^last round key
+	aesdec		$rndkey1,$inout5
+	$movkey		48($key_),$rndkey1
+	 pxor	$twres,@tweak[2]
+
+	aesdec		$rndkey0,$inout0
+	 pxor	$twres,@tweak[3]
+	 movdqa	@tweak[1],`16*1`(%rsp)
+	aesdec		$rndkey0,$inout1
+	 pxor	$twres,@tweak[4]
+	 movdqa	@tweak[2],`16*2`(%rsp)
+	aesdec		$rndkey0,$inout2
+	aesdec		$rndkey0,$inout3
+	 pxor	$twres,$twmask
+	 movdqa	@tweak[4],`16*4`(%rsp)
+	aesdec		$rndkey0,$inout4
+	aesdec		$rndkey0,$inout5
+	$movkey		64($key_),$rndkey0
+	 movdqa	$twmask,`16*5`(%rsp)
+	pshufd	\$0x5f,@tweak[5],$twres
+	jmp	.Lxts_dec_loop6
+.align	32
+.Lxts_dec_loop6:
+	aesdec		$rndkey1,$inout0
+	aesdec		$rndkey1,$inout1
+	aesdec		$rndkey1,$inout2
+	aesdec		$rndkey1,$inout3
+	aesdec		$rndkey1,$inout4
+	aesdec		$rndkey1,$inout5
+	$movkey		-64($key,%rax),$rndkey1
+	add		\$32,%rax
+
+	aesdec		$rndkey0,$inout0
+	aesdec		$rndkey0,$inout1
+	aesdec		$rndkey0,$inout2
+	aesdec		$rndkey0,$inout3
+	aesdec		$rndkey0,$inout4
+	aesdec		$rndkey0,$inout5
+	$movkey		-80($key,%rax),$rndkey0
+	jnz		.Lxts_dec_loop6
+
+	movdqa	(%r8),$twmask			# start calculating next tweak
+	movdqa	$twres,$twtmp
+	paddd	$twres,$twres
+	 aesdec		$rndkey1,$inout0
+	paddq	@tweak[5],@tweak[5]
+	psrad	\$31,$twtmp
+	 aesdec		$rndkey1,$inout1
+	pand	$twmask,$twtmp
+	$movkey	($key_),@tweak[0]		# load round[0]
+	 aesdec		$rndkey1,$inout2
+	 aesdec		$rndkey1,$inout3
+	 aesdec		$rndkey1,$inout4
+	pxor	$twtmp,@tweak[5]
+	movaps	@tweak[0],@tweak[1]		# copy round[0]
+	 aesdec		$rndkey1,$inout5
+	 $movkey	-64($key),$rndkey1
+
+	movdqa	$twres,$twtmp
+	 aesdec		$rndkey0,$inout0
+	paddd	$twres,$twres
+	pxor	@tweak[5],@tweak[0]
+	 aesdec		$rndkey0,$inout1
+	psrad	\$31,$twtmp
+	paddq	@tweak[5],@tweak[5]
+	 aesdec		$rndkey0,$inout2
+	 aesdec		$rndkey0,$inout3
+	pand	$twmask,$twtmp
+	movaps	@tweak[1],@tweak[2]
+	 aesdec		$rndkey0,$inout4
+	pxor	$twtmp,@tweak[5]
+	movdqa	$twres,$twtmp
+	 aesdec		$rndkey0,$inout5
+	 $movkey	-48($key),$rndkey0
+
+	paddd	$twres,$twres
+	 aesdec		$rndkey1,$inout0
+	pxor	@tweak[5],@tweak[1]
+	psrad	\$31,$twtmp
+	 aesdec		$rndkey1,$inout1
+	paddq	@tweak[5],@tweak[5]
+	pand	$twmask,$twtmp
+	 aesdec		$rndkey1,$inout2
+	 aesdec		$rndkey1,$inout3
+	 movdqa	@tweak[3],`16*3`(%rsp)
+	pxor	$twtmp,@tweak[5]
+	 aesdec		$rndkey1,$inout4
+	movaps	@tweak[2],@tweak[3]
+	movdqa	$twres,$twtmp
+	 aesdec		$rndkey1,$inout5
+	 $movkey	-32($key),$rndkey1
+
+	paddd	$twres,$twres
+	 aesdec		$rndkey0,$inout0
+	pxor	@tweak[5],@tweak[2]
+	psrad	\$31,$twtmp
+	 aesdec		$rndkey0,$inout1
+	paddq	@tweak[5],@tweak[5]
+	pand	$twmask,$twtmp
+	 aesdec		$rndkey0,$inout2
+	 aesdec		$rndkey0,$inout3
+	 aesdec		$rndkey0,$inout4
+	pxor	$twtmp,@tweak[5]
+	movaps	@tweak[3],@tweak[4]
+	 aesdec		$rndkey0,$inout5
+
+	movdqa	$twres,$rndkey0
+	paddd	$twres,$twres
+	 aesdec		$rndkey1,$inout0
+	pxor	@tweak[5],@tweak[3]
+	psrad	\$31,$rndkey0
+	 aesdec		$rndkey1,$inout1
+	paddq	@tweak[5],@tweak[5]
+	pand	$twmask,$rndkey0
+	 aesdec		$rndkey1,$inout2
+	 aesdec		$rndkey1,$inout3
+	pxor	$rndkey0,@tweak[5]
+	$movkey		($key_),$rndkey0
+	 aesdec		$rndkey1,$inout4
+	 aesdec		$rndkey1,$inout5
+	$movkey		16($key_),$rndkey1
+
+	pxor	@tweak[5],@tweak[4]
+	 aesdeclast	`16*0`(%rsp),$inout0
+	psrad	\$31,$twres
+	paddq	@tweak[5],@tweak[5]
+	 aesdeclast	`16*1`(%rsp),$inout1
+	 aesdeclast	`16*2`(%rsp),$inout2
+	pand	$twmask,$twres
+	mov	%r10,%rax			# restore $rounds
+	 aesdeclast	`16*3`(%rsp),$inout3
+	 aesdeclast	`16*4`(%rsp),$inout4
+	 aesdeclast	`16*5`(%rsp),$inout5
+	pxor	$twres,@tweak[5]
+
+	lea	`16*6`($out),$out		# $out+=6*16
+	movups	$inout0,`-16*6`($out)		# store 6 output blocks
+	movups	$inout1,`-16*5`($out)
+	movups	$inout2,`-16*4`($out)
+	movups	$inout3,`-16*3`($out)
+	movups	$inout4,`-16*2`($out)
+	movups	$inout5,`-16*1`($out)
+	sub	\$16*6,$len
+	jnc	.Lxts_dec_grandloop		# loop if $len-=6*16 didn't borrow
+
+	mov	\$16+96,$rounds
+	sub	$rnds_,$rounds
+	mov	$key_,$key			# restore $key
+	shr	\$4,$rounds			# restore original value
+
+.Lxts_dec_short:
+	# at the point @tweak[0..5] are populated with tweak values
+	mov	$rounds,$rnds_			# backup $rounds
+	pxor	$rndkey0,@tweak[0]
+	pxor	$rndkey0,@tweak[1]
+	add	\$16*6,$len			# restore real remaining $len
+	jz	.Lxts_dec_done			# done if ($len==0)
+
+	pxor	$rndkey0,@tweak[2]
+	cmp	\$0x20,$len
+	jb	.Lxts_dec_one			# $len is 1*16
+	pxor	$rndkey0,@tweak[3]
+	je	.Lxts_dec_two			# $len is 2*16
+
+	pxor	$rndkey0,@tweak[4]
+	cmp	\$0x40,$len
+	jb	.Lxts_dec_three			# $len is 3*16
+	je	.Lxts_dec_four			# $len is 4*16
+
+	movdqu	($inp),$inout0			# $len is 5*16
+	movdqu	16*1($inp),$inout1
+	movdqu	16*2($inp),$inout2
+	pxor	@tweak[0],$inout0
+	movdqu	16*3($inp),$inout3
+	pxor	@tweak[1],$inout1
+	movdqu	16*4($inp),$inout4
+	lea	16*5($inp),$inp			# $inp+=5*16
+	pxor	@tweak[2],$inout2
+	pxor	@tweak[3],$inout3
+	pxor	@tweak[4],$inout4
+
+	call	_aesni_decrypt6
+
+	xorps	@tweak[0],$inout0
+	xorps	@tweak[1],$inout1
+	xorps	@tweak[2],$inout2
+	movdqu	$inout0,($out)			# store 5 output blocks
+	xorps	@tweak[3],$inout3
+	movdqu	$inout1,16*1($out)
+	xorps	@tweak[4],$inout4
+	movdqu	$inout2,16*2($out)
+	 pxor		$twtmp,$twtmp
+	movdqu	$inout3,16*3($out)
+	 pcmpgtd	@tweak[5],$twtmp
+	movdqu	$inout4,16*4($out)
+	lea	16*5($out),$out			# $out+=5*16
+	 pshufd		\$0x13,$twtmp,@tweak[1]	# $twres
+	and	\$15,$len_
+	jz	.Lxts_dec_ret
+
+	movdqa	@tweak[5],@tweak[0]
+	paddq	@tweak[5],@tweak[5]		# psllq 1,$tweak
+	pand	$twmask,@tweak[1]		# isolate carry and residue
+	pxor	@tweak[5],@tweak[1]
+	jmp	.Lxts_dec_done2
+
+.align	16
+.Lxts_dec_one:
+	movups	($inp),$inout0
+	lea	16*1($inp),$inp			# $inp+=1*16
+	xorps	@tweak[0],$inout0
+___
+	&aesni_generate1("dec",$key,$rounds);
+$code.=<<___;
+	xorps	@tweak[0],$inout0
+	movdqa	@tweak[1],@tweak[0]
+	movups	$inout0,($out)			# store one output block
+	movdqa	@tweak[2],@tweak[1]
+	lea	16*1($out),$out			# $out+=1*16
+	jmp	.Lxts_dec_done
+
+.align	16
+.Lxts_dec_two:
+	movups	($inp),$inout0
+	movups	16($inp),$inout1
+	lea	32($inp),$inp			# $inp+=2*16
+	xorps	@tweak[0],$inout0
+	xorps	@tweak[1],$inout1
+
+	call	_aesni_decrypt2
+
+	xorps	@tweak[0],$inout0
+	movdqa	@tweak[2],@tweak[0]
+	xorps	@tweak[1],$inout1
+	movdqa	@tweak[3],@tweak[1]
+	movups	$inout0,($out)			# store 2 output blocks
+	movups	$inout1,16*1($out)
+	lea	16*2($out),$out			# $out+=2*16
+	jmp	.Lxts_dec_done
+
+.align	16
+.Lxts_dec_three:
+	movups	($inp),$inout0
+	movups	16*1($inp),$inout1
+	movups	16*2($inp),$inout2
+	lea	16*3($inp),$inp			# $inp+=3*16
+	xorps	@tweak[0],$inout0
+	xorps	@tweak[1],$inout1
+	xorps	@tweak[2],$inout2
+
+	call	_aesni_decrypt3
+
+	xorps	@tweak[0],$inout0
+	movdqa	@tweak[3],@tweak[0]
+	xorps	@tweak[1],$inout1
+	movdqa	@tweak[4],@tweak[1]
+	xorps	@tweak[2],$inout2
+	movups	$inout0,($out)			# store 3 output blocks
+	movups	$inout1,16*1($out)
+	movups	$inout2,16*2($out)
+	lea	16*3($out),$out			# $out+=3*16
+	jmp	.Lxts_dec_done
+
+.align	16
+.Lxts_dec_four:
+	movups	($inp),$inout0
+	movups	16*1($inp),$inout1
+	movups	16*2($inp),$inout2
+	xorps	@tweak[0],$inout0
+	movups	16*3($inp),$inout3
+	lea	16*4($inp),$inp			# $inp+=4*16
+	xorps	@tweak[1],$inout1
+	xorps	@tweak[2],$inout2
+	xorps	@tweak[3],$inout3
+
+	call	_aesni_decrypt4
+
+	pxor	@tweak[0],$inout0
+	movdqa	@tweak[4],@tweak[0]
+	pxor	@tweak[1],$inout1
+	movdqa	@tweak[5],@tweak[1]
+	pxor	@tweak[2],$inout2
+	movdqu	$inout0,($out)			# store 4 output blocks
+	pxor	@tweak[3],$inout3
+	movdqu	$inout1,16*1($out)
+	movdqu	$inout2,16*2($out)
+	movdqu	$inout3,16*3($out)
+	lea	16*4($out),$out			# $out+=4*16
+	jmp	.Lxts_dec_done
+
+.align	16
+.Lxts_dec_done:
+	and	\$15,$len_			# see if $len%16 is 0
+	jz	.Lxts_dec_ret
+.Lxts_dec_done2:
+	mov	$len_,$len
+	mov	$key_,$key			# restore $key
+	mov	$rnds_,$rounds			# restore $rounds
+
+	movups	($inp),$inout0
+	xorps	@tweak[1],$inout0
+___
+	&aesni_generate1("dec",$key,$rounds);
+$code.=<<___;
+	xorps	@tweak[1],$inout0
+	movups	$inout0,($out)
+
+.Lxts_dec_steal:
+	movzb	16($inp),%eax			# borrow $rounds ...
+	movzb	($out),%ecx			# ... and $key
+	lea	1($inp),$inp
+	mov	%al,($out)
+	mov	%cl,16($out)
+	lea	1($out),$out
+	sub	\$1,$len
+	jnz	.Lxts_dec_steal
+
+	sub	$len_,$out			# rewind $out
+	mov	$key_,$key			# restore $key
+	mov	$rnds_,$rounds			# restore $rounds
+
+	movups	($out),$inout0
+	xorps	@tweak[0],$inout0
+___
+	&aesni_generate1("dec",$key,$rounds);
+$code.=<<___;
+	xorps	@tweak[0],$inout0
+	movups	$inout0,($out)
+
+.Lxts_dec_ret:
+	xorps	%xmm0,%xmm0			# clear register bank
+	pxor	%xmm1,%xmm1
+	pxor	%xmm2,%xmm2
+	pxor	%xmm3,%xmm3
+	pxor	%xmm4,%xmm4
+	pxor	%xmm5,%xmm5
+___
+$code.=<<___ if (!$win64);
+	pxor	%xmm6,%xmm6
+	pxor	%xmm7,%xmm7
+	movaps	%xmm0,0x00(%rsp)		# clear stack
+	pxor	%xmm8,%xmm8
+	movaps	%xmm0,0x10(%rsp)
+	pxor	%xmm9,%xmm9
+	movaps	%xmm0,0x20(%rsp)
+	pxor	%xmm10,%xmm10
+	movaps	%xmm0,0x30(%rsp)
+	pxor	%xmm11,%xmm11
+	movaps	%xmm0,0x40(%rsp)
+	pxor	%xmm12,%xmm12
+	movaps	%xmm0,0x50(%rsp)
+	pxor	%xmm13,%xmm13
+	movaps	%xmm0,0x60(%rsp)
+	pxor	%xmm14,%xmm14
+	pxor	%xmm15,%xmm15
+___
+$code.=<<___ if ($win64);
+	movaps	-0xa0(%rbp),%xmm6
+	movaps	%xmm0,-0xa0(%rbp)		# clear stack
+	movaps	-0x90(%rbp),%xmm7
+	movaps	%xmm0,-0x90(%rbp)
+	movaps	-0x80(%rbp),%xmm8
+	movaps	%xmm0,-0x80(%rbp)
+	movaps	-0x70(%rbp),%xmm9
+	movaps	%xmm0,-0x70(%rbp)
+	movaps	-0x60(%rbp),%xmm10
+	movaps	%xmm0,-0x60(%rbp)
+	movaps	-0x50(%rbp),%xmm11
+	movaps	%xmm0,-0x50(%rbp)
+	movaps	-0x40(%rbp),%xmm12
+	movaps	%xmm0,-0x40(%rbp)
+	movaps	-0x30(%rbp),%xmm13
+	movaps	%xmm0,-0x30(%rbp)
+	movaps	-0x20(%rbp),%xmm14
+	movaps	%xmm0,-0x20(%rbp)
+	movaps	-0x10(%rbp),%xmm15
+	movaps	%xmm0,-0x10(%rbp)
+	movaps	%xmm0,0x00(%rsp)
+	movaps	%xmm0,0x10(%rsp)
+	movaps	%xmm0,0x20(%rsp)
+	movaps	%xmm0,0x30(%rsp)
+	movaps	%xmm0,0x40(%rsp)
+	movaps	%xmm0,0x50(%rsp)
+	movaps	%xmm0,0x60(%rsp)
+___
+$code.=<<___;
+	lea	(%rbp),%rsp
+	pop	%rbp
+.Lxts_dec_epilogue:
+	ret
+.size	aesni_xts_decrypt,.-aesni_xts_decrypt
+___
+}
+
+######################################################################
+# void aesni_ocb_[en|de]crypt(const char *inp, char *out, size_t blocks,
+#	const AES_KEY *key, unsigned int start_block_num,
+#	unsigned char offset_i[16], const unsigned char L_[][16],
+#	unsigned char checksum[16]);
+#
+{
+my @offset=map("%xmm$_",(10..15));
+my ($checksum,$rndkey0l)=("%xmm8","%xmm9");
+my ($block_num,$offset_p)=("%r8","%r9");		# 5th and 6th arguments
+my ($L_p,$checksum_p) = ("%rbx","%rbp");
+my ($i1,$i3,$i5) = ("%r12","%r13","%r14");
+my $seventh_arg = $win64 ? 56 : 8;
+my $blocks = $len;
+
+$code.=<<___;
+.globl	aesni_ocb_encrypt
+.type	aesni_ocb_encrypt,\@function,6
+.align	32
+aesni_ocb_encrypt:
+	lea	(%rsp),%rax
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+___
+$code.=<<___ if ($win64);
+	lea	-0xa0(%rsp),%rsp
+	movaps	%xmm6,0x00(%rsp)		# offload everything
+	movaps	%xmm7,0x10(%rsp)
+	movaps	%xmm8,0x20(%rsp)
+	movaps	%xmm9,0x30(%rsp)
+	movaps	%xmm10,0x40(%rsp)
+	movaps	%xmm11,0x50(%rsp)
+	movaps	%xmm12,0x60(%rsp)
+	movaps	%xmm13,0x70(%rsp)
+	movaps	%xmm14,0x80(%rsp)
+	movaps	%xmm15,0x90(%rsp)
+.Locb_enc_body:
+___
+$code.=<<___;
+	mov	$seventh_arg(%rax),$L_p		# 7th argument
+	mov	$seventh_arg+8(%rax),$checksum_p# 8th argument
+
+	mov	240($key),$rnds_
+	mov	$key,$key_
+	shl	\$4,$rnds_
+	$movkey	($key),$rndkey0l		# round[0]
+	$movkey	16($key,$rnds_),$rndkey1	# round[last]
+
+	movdqu	($offset_p),@offset[5]		# load last offset_i
+	pxor	$rndkey1,$rndkey0l		# round[0] ^ round[last]
+	pxor	$rndkey1,@offset[5]		# offset_i ^ round[last]
+
+	mov	\$16+32,$rounds
+	lea	32($key_,$rnds_),$key
+	$movkey	16($key_),$rndkey1		# round[1]
+	sub	%r10,%rax			# twisted $rounds
+	mov	%rax,%r10			# backup twisted $rounds
+
+	movdqu	($L_p),@offset[0]		# L_0 for all odd-numbered blocks
+	movdqu	($checksum_p),$checksum		# load checksum
+
+	test	\$1,$block_num			# is first block number odd?
+	jnz	.Locb_enc_odd
+
+	bsf	$block_num,$i1
+	add	\$1,$block_num
+	shl	\$4,$i1
+	movdqu	($L_p,$i1),$inout5		# borrow
+	movdqu	($inp),$inout0
+	lea	16($inp),$inp
+
+	call	__ocb_encrypt1
+
+	movdqa	$inout5,@offset[5]
+	movups	$inout0,($out)
+	lea	16($out),$out
+	sub	\$1,$blocks
+	jz	.Locb_enc_done
+
+.Locb_enc_odd:
+	lea	1($block_num),$i1		# even-numbered blocks
+	lea	3($block_num),$i3
+	lea	5($block_num),$i5
+	lea	6($block_num),$block_num
+	bsf	$i1,$i1				# ntz(block)
+	bsf	$i3,$i3
+	bsf	$i5,$i5
+	shl	\$4,$i1				# ntz(block) -> table offset
+	shl	\$4,$i3
+	shl	\$4,$i5
+
+	sub	\$6,$blocks
+	jc	.Locb_enc_short
+	jmp	.Locb_enc_grandloop
+
+.align	32
+.Locb_enc_grandloop:
+	movdqu	`16*0`($inp),$inout0		# load input
+	movdqu	`16*1`($inp),$inout1
+	movdqu	`16*2`($inp),$inout2
+	movdqu	`16*3`($inp),$inout3
+	movdqu	`16*4`($inp),$inout4
+	movdqu	`16*5`($inp),$inout5
+	lea	`16*6`($inp),$inp
+
+	call	__ocb_encrypt6
+
+	movups	$inout0,`16*0`($out)		# store output
+	movups	$inout1,`16*1`($out)
+	movups	$inout2,`16*2`($out)
+	movups	$inout3,`16*3`($out)
+	movups	$inout4,`16*4`($out)
+	movups	$inout5,`16*5`($out)
+	lea	`16*6`($out),$out
+	sub	\$6,$blocks
+	jnc	.Locb_enc_grandloop
+
+.Locb_enc_short:
+	add	\$6,$blocks
+	jz	.Locb_enc_done
+
+	movdqu	`16*0`($inp),$inout0
+	cmp	\$2,$blocks
+	jb	.Locb_enc_one
+	movdqu	`16*1`($inp),$inout1
+	je	.Locb_enc_two
+
+	movdqu	`16*2`($inp),$inout2
+	cmp	\$4,$blocks
+	jb	.Locb_enc_three
+	movdqu	`16*3`($inp),$inout3
+	je	.Locb_enc_four
+
+	movdqu	`16*4`($inp),$inout4
+	pxor	$inout5,$inout5
+
+	call	__ocb_encrypt6
+
+	movdqa	@offset[4],@offset[5]
+	movups	$inout0,`16*0`($out)
+	movups	$inout1,`16*1`($out)
+	movups	$inout2,`16*2`($out)
+	movups	$inout3,`16*3`($out)
+	movups	$inout4,`16*4`($out)
+
+	jmp	.Locb_enc_done
+
+.align	16
+.Locb_enc_one:
+	movdqa	@offset[0],$inout5		# borrow
+
+	call	__ocb_encrypt1
+
+	movdqa	$inout5,@offset[5]
+	movups	$inout0,`16*0`($out)
+	jmp	.Locb_enc_done
+
+.align	16
+.Locb_enc_two:
+	pxor	$inout2,$inout2
+	pxor	$inout3,$inout3
+
+	call	__ocb_encrypt4
+
+	movdqa	@offset[1],@offset[5]
+	movups	$inout0,`16*0`($out)
+	movups	$inout1,`16*1`($out)
+
+	jmp	.Locb_enc_done
+
+.align	16
+.Locb_enc_three:
+	pxor	$inout3,$inout3
+
+	call	__ocb_encrypt4
+
+	movdqa	@offset[2],@offset[5]
+	movups	$inout0,`16*0`($out)
+	movups	$inout1,`16*1`($out)
+	movups	$inout2,`16*2`($out)
+
+	jmp	.Locb_enc_done
+
+.align	16
+.Locb_enc_four:
+	call	__ocb_encrypt4
+
+	movdqa	@offset[3],@offset[5]
+	movups	$inout0,`16*0`($out)
+	movups	$inout1,`16*1`($out)
+	movups	$inout2,`16*2`($out)
+	movups	$inout3,`16*3`($out)
+
+.Locb_enc_done:
+	pxor	$rndkey0,@offset[5]		# "remove" round[last]
+	movdqu	$checksum,($checksum_p)		# store checksum
+	movdqu	@offset[5],($offset_p)		# store last offset_i
+
+	xorps	%xmm0,%xmm0			# clear register bank
+	pxor	%xmm1,%xmm1
+	pxor	%xmm2,%xmm2
+	pxor	%xmm3,%xmm3
+	pxor	%xmm4,%xmm4
+	pxor	%xmm5,%xmm5
+___
+$code.=<<___ if (!$win64);
+	pxor	%xmm6,%xmm6
+	pxor	%xmm7,%xmm7
+	pxor	%xmm8,%xmm8
+	pxor	%xmm9,%xmm9
+	pxor	%xmm10,%xmm10
+	pxor	%xmm11,%xmm11
+	pxor	%xmm12,%xmm12
+	pxor	%xmm13,%xmm13
+	pxor	%xmm14,%xmm14
+	pxor	%xmm15,%xmm15
+___
+$code.=<<___ if ($win64);
+	movaps	0x00(%rsp),%xmm6
+	movaps	%xmm0,0x00(%rsp)		# clear stack
+	movaps	0x10(%rsp),%xmm7
+	movaps	%xmm0,0x10(%rsp)
+	movaps	0x20(%rsp),%xmm8
+	movaps	%xmm0,0x20(%rsp)
+	movaps	0x30(%rsp),%xmm9
+	movaps	%xmm0,0x30(%rsp)
+	movaps	0x40(%rsp),%xmm10
+	movaps	%xmm0,0x40(%rsp)
+	movaps	0x50(%rsp),%xmm11
+	movaps	%xmm0,0x50(%rsp)
+	movaps	0x60(%rsp),%xmm12
+	movaps	%xmm0,0x60(%rsp)
+	movaps	0x70(%rsp),%xmm13
+	movaps	%xmm0,0x70(%rsp)
+	movaps	0x80(%rsp),%xmm14
+	movaps	%xmm0,0x80(%rsp)
+	movaps	0x90(%rsp),%xmm15
+	movaps	%xmm0,0x90(%rsp)
+	lea	0xa0+0x28(%rsp),%rax
+.Locb_enc_pop:
+	lea	0xa0(%rsp),%rsp
+___
+$code.=<<___;
+	pop	%r14
+	pop	%r13
+	pop	%r12
+	pop	%rbp
+	pop	%rbx
+.Locb_enc_epilogue:
+	ret
+.size	aesni_ocb_encrypt,.-aesni_ocb_encrypt
+
+.type	__ocb_encrypt6,\@abi-omnipotent
+.align	32
+__ocb_encrypt6:
+	 pxor		$rndkey0l,@offset[5]	# offset_i ^ round[0]
+	 movdqu		($L_p,$i1),@offset[1]
+	 movdqa		@offset[0],@offset[2]
+	 movdqu		($L_p,$i3),@offset[3]
+	 movdqa		@offset[0],@offset[4]
+	 pxor		@offset[5],@offset[0]
+	 movdqu		($L_p,$i5),@offset[5]
+	 pxor		@offset[0],@offset[1]
+	pxor		$inout0,$checksum	# accumulate checksum
+	pxor		@offset[0],$inout0	# input ^ round[0] ^ offset_i
+	 pxor		@offset[1],@offset[2]
+	pxor		$inout1,$checksum
+	pxor		@offset[1],$inout1
+	 pxor		@offset[2],@offset[3]
+	pxor		$inout2,$checksum
+	pxor		@offset[2],$inout2
+	 pxor		@offset[3],@offset[4]
+	pxor		$inout3,$checksum
+	pxor		@offset[3],$inout3
+	 pxor		@offset[4],@offset[5]
+	pxor		$inout4,$checksum
+	pxor		@offset[4],$inout4
+	pxor		$inout5,$checksum
+	pxor		@offset[5],$inout5
+	$movkey		32($key_),$rndkey0
+
+	lea		1($block_num),$i1	# even-numbered blocks
+	lea		3($block_num),$i3
+	lea		5($block_num),$i5
+	add		\$6,$block_num
+	 pxor		$rndkey0l,@offset[0]	# offset_i ^ round[last]
+	bsf		$i1,$i1			# ntz(block)
+	bsf		$i3,$i3
+	bsf		$i5,$i5
+
+	aesenc		$rndkey1,$inout0
+	aesenc		$rndkey1,$inout1
+	aesenc		$rndkey1,$inout2
+	aesenc		$rndkey1,$inout3
+	 pxor		$rndkey0l,@offset[1]
+	 pxor		$rndkey0l,@offset[2]
+	aesenc		$rndkey1,$inout4
+	 pxor		$rndkey0l,@offset[3]
+	 pxor		$rndkey0l,@offset[4]
+	aesenc		$rndkey1,$inout5
+	$movkey		48($key_),$rndkey1
+	 pxor		$rndkey0l,@offset[5]
+
+	aesenc		$rndkey0,$inout0
+	aesenc		$rndkey0,$inout1
+	aesenc		$rndkey0,$inout2
+	aesenc		$rndkey0,$inout3
+	aesenc		$rndkey0,$inout4
+	aesenc		$rndkey0,$inout5
+	$movkey		64($key_),$rndkey0
+	shl		\$4,$i1			# ntz(block) -> table offset
+	shl		\$4,$i3
+	jmp		.Locb_enc_loop6
+
+.align	32
+.Locb_enc_loop6:
+	aesenc		$rndkey1,$inout0
+	aesenc		$rndkey1,$inout1
+	aesenc		$rndkey1,$inout2
+	aesenc		$rndkey1,$inout3
+	aesenc		$rndkey1,$inout4
+	aesenc		$rndkey1,$inout5
+	$movkey		($key,%rax),$rndkey1
+	add		\$32,%rax
+
+	aesenc		$rndkey0,$inout0
+	aesenc		$rndkey0,$inout1
+	aesenc		$rndkey0,$inout2
+	aesenc		$rndkey0,$inout3
+	aesenc		$rndkey0,$inout4
+	aesenc		$rndkey0,$inout5
+	$movkey		-16($key,%rax),$rndkey0
+	jnz		.Locb_enc_loop6
+
+	aesenc		$rndkey1,$inout0
+	aesenc		$rndkey1,$inout1
+	aesenc		$rndkey1,$inout2
+	aesenc		$rndkey1,$inout3
+	aesenc		$rndkey1,$inout4
+	aesenc		$rndkey1,$inout5
+	$movkey		16($key_),$rndkey1
+	shl		\$4,$i5
+
+	aesenclast	@offset[0],$inout0
+	movdqu		($L_p),@offset[0]	# L_0 for all odd-numbered blocks
+	mov		%r10,%rax		# restore twisted rounds
+	aesenclast	@offset[1],$inout1
+	aesenclast	@offset[2],$inout2
+	aesenclast	@offset[3],$inout3
+	aesenclast	@offset[4],$inout4
+	aesenclast	@offset[5],$inout5
+	ret
+.size	__ocb_encrypt6,.-__ocb_encrypt6
+
+.type	__ocb_encrypt4,\@abi-omnipotent
+.align	32
+__ocb_encrypt4:
+	 pxor		$rndkey0l,@offset[5]	# offset_i ^ round[0]
+	 movdqu		($L_p,$i1),@offset[1]
+	 movdqa		@offset[0],@offset[2]
+	 movdqu		($L_p,$i3),@offset[3]
+	 pxor		@offset[5],@offset[0]
+	 pxor		@offset[0],@offset[1]
+	pxor		$inout0,$checksum	# accumulate checksum
+	pxor		@offset[0],$inout0	# input ^ round[0] ^ offset_i
+	 pxor		@offset[1],@offset[2]
+	pxor		$inout1,$checksum
+	pxor		@offset[1],$inout1
+	 pxor		@offset[2],@offset[3]
+	pxor		$inout2,$checksum
+	pxor		@offset[2],$inout2
+	pxor		$inout3,$checksum
+	pxor		@offset[3],$inout3
+	$movkey		32($key_),$rndkey0
+
+	 pxor		$rndkey0l,@offset[0]	# offset_i ^ round[last]
+	 pxor		$rndkey0l,@offset[1]
+	 pxor		$rndkey0l,@offset[2]
+	 pxor		$rndkey0l,@offset[3]
+
+	aesenc		$rndkey1,$inout0
+	aesenc		$rndkey1,$inout1
+	aesenc		$rndkey1,$inout2
+	aesenc		$rndkey1,$inout3
+	$movkey		48($key_),$rndkey1
+
+	aesenc		$rndkey0,$inout0
+	aesenc		$rndkey0,$inout1
+	aesenc		$rndkey0,$inout2
+	aesenc		$rndkey0,$inout3
+	$movkey		64($key_),$rndkey0
+	jmp		.Locb_enc_loop4
+
+.align	32
+.Locb_enc_loop4:
+	aesenc		$rndkey1,$inout0
+	aesenc		$rndkey1,$inout1
+	aesenc		$rndkey1,$inout2
+	aesenc		$rndkey1,$inout3
+	$movkey		($key,%rax),$rndkey1
+	add		\$32,%rax
+
+	aesenc		$rndkey0,$inout0
+	aesenc		$rndkey0,$inout1
+	aesenc		$rndkey0,$inout2
+	aesenc		$rndkey0,$inout3
+	$movkey		-16($key,%rax),$rndkey0
+	jnz		.Locb_enc_loop4
+
+	aesenc		$rndkey1,$inout0
+	aesenc		$rndkey1,$inout1
+	aesenc		$rndkey1,$inout2
+	aesenc		$rndkey1,$inout3
+	$movkey		16($key_),$rndkey1
+	mov		%r10,%rax		# restore twisted rounds
+
+	aesenclast	@offset[0],$inout0
+	aesenclast	@offset[1],$inout1
+	aesenclast	@offset[2],$inout2
+	aesenclast	@offset[3],$inout3
+	ret
+.size	__ocb_encrypt4,.-__ocb_encrypt4
+
+.type	__ocb_encrypt1,\@abi-omnipotent
+.align	32
+__ocb_encrypt1:
+	 pxor		@offset[5],$inout5	# offset_i
+	 pxor		$rndkey0l,$inout5	# offset_i ^ round[0]
+	pxor		$inout0,$checksum	# accumulate checksum
+	pxor		$inout5,$inout0		# input ^ round[0] ^ offset_i
+	$movkey		32($key_),$rndkey0
+
+	aesenc		$rndkey1,$inout0
+	$movkey		48($key_),$rndkey1
+	pxor		$rndkey0l,$inout5	# offset_i ^ round[last]
+
+	aesenc		$rndkey0,$inout0
+	$movkey		64($key_),$rndkey0
+	jmp		.Locb_enc_loop1
+
+.align	32
+.Locb_enc_loop1:
+	aesenc		$rndkey1,$inout0
+	$movkey		($key,%rax),$rndkey1
+	add		\$32,%rax
+
+	aesenc		$rndkey0,$inout0
+	$movkey		-16($key,%rax),$rndkey0
+	jnz		.Locb_enc_loop1
+
+	aesenc		$rndkey1,$inout0
+	$movkey		16($key_),$rndkey1	# redundant in tail
+	mov		%r10,%rax		# restore twisted rounds
+
+	aesenclast	$inout5,$inout0
+	ret
+.size	__ocb_encrypt1,.-__ocb_encrypt1
+
+.globl	aesni_ocb_decrypt
+.type	aesni_ocb_decrypt,\@function,6
+.align	32
+aesni_ocb_decrypt:
+	lea	(%rsp),%rax
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+___
+$code.=<<___ if ($win64);
+	lea	-0xa0(%rsp),%rsp
+	movaps	%xmm6,0x00(%rsp)		# offload everything
+	movaps	%xmm7,0x10(%rsp)
+	movaps	%xmm8,0x20(%rsp)
+	movaps	%xmm9,0x30(%rsp)
+	movaps	%xmm10,0x40(%rsp)
+	movaps	%xmm11,0x50(%rsp)
+	movaps	%xmm12,0x60(%rsp)
+	movaps	%xmm13,0x70(%rsp)
+	movaps	%xmm14,0x80(%rsp)
+	movaps	%xmm15,0x90(%rsp)
+.Locb_dec_body:
+___
+$code.=<<___;
+	mov	$seventh_arg(%rax),$L_p		# 7th argument
+	mov	$seventh_arg+8(%rax),$checksum_p# 8th argument
+
+	mov	240($key),$rnds_
+	mov	$key,$key_
+	shl	\$4,$rnds_
+	$movkey	($key),$rndkey0l		# round[0]
+	$movkey	16($key,$rnds_),$rndkey1	# round[last]
+
+	movdqu	($offset_p),@offset[5]		# load last offset_i
+	pxor	$rndkey1,$rndkey0l		# round[0] ^ round[last]
+	pxor	$rndkey1,@offset[5]		# offset_i ^ round[last]
+
+	mov	\$16+32,$rounds
+	lea	32($key_,$rnds_),$key
+	$movkey	16($key_),$rndkey1		# round[1]
+	sub	%r10,%rax			# twisted $rounds
+	mov	%rax,%r10			# backup twisted $rounds
+
+	movdqu	($L_p),@offset[0]		# L_0 for all odd-numbered blocks
+	movdqu	($checksum_p),$checksum		# load checksum
+
+	test	\$1,$block_num			# is first block number odd?
+	jnz	.Locb_dec_odd
+
+	bsf	$block_num,$i1
+	add	\$1,$block_num
+	shl	\$4,$i1
+	movdqu	($L_p,$i1),$inout5		# borrow
+	movdqu	($inp),$inout0
+	lea	16($inp),$inp
+
+	call	__ocb_decrypt1
+
+	movdqa	$inout5,@offset[5]
+	movups	$inout0,($out)
+	xorps	$inout0,$checksum		# accumulate checksum
+	lea	16($out),$out
+	sub	\$1,$blocks
+	jz	.Locb_dec_done
+
+.Locb_dec_odd:
+	lea	1($block_num),$i1		# even-numbered blocks
+	lea	3($block_num),$i3
+	lea	5($block_num),$i5
+	lea	6($block_num),$block_num
+	bsf	$i1,$i1				# ntz(block)
+	bsf	$i3,$i3
+	bsf	$i5,$i5
+	shl	\$4,$i1				# ntz(block) -> table offset
+	shl	\$4,$i3
+	shl	\$4,$i5
+
+	sub	\$6,$blocks
+	jc	.Locb_dec_short
+	jmp	.Locb_dec_grandloop
+
+.align	32
+.Locb_dec_grandloop:
+	movdqu	`16*0`($inp),$inout0		# load input
+	movdqu	`16*1`($inp),$inout1
+	movdqu	`16*2`($inp),$inout2
+	movdqu	`16*3`($inp),$inout3
+	movdqu	`16*4`($inp),$inout4
+	movdqu	`16*5`($inp),$inout5
+	lea	`16*6`($inp),$inp
+
+	call	__ocb_decrypt6
+
+	movups	$inout0,`16*0`($out)		# store output
+	pxor	$inout0,$checksum		# accumulate checksum
+	movups	$inout1,`16*1`($out)
+	pxor	$inout1,$checksum
+	movups	$inout2,`16*2`($out)
+	pxor	$inout2,$checksum
+	movups	$inout3,`16*3`($out)
+	pxor	$inout3,$checksum
+	movups	$inout4,`16*4`($out)
+	pxor	$inout4,$checksum
+	movups	$inout5,`16*5`($out)
+	pxor	$inout5,$checksum
+	lea	`16*6`($out),$out
+	sub	\$6,$blocks
+	jnc	.Locb_dec_grandloop
+
+.Locb_dec_short:
+	add	\$6,$blocks
+	jz	.Locb_dec_done
+
+	movdqu	`16*0`($inp),$inout0
+	cmp	\$2,$blocks
+	jb	.Locb_dec_one
+	movdqu	`16*1`($inp),$inout1
+	je	.Locb_dec_two
+
+	movdqu	`16*2`($inp),$inout2
+	cmp	\$4,$blocks
+	jb	.Locb_dec_three
+	movdqu	`16*3`($inp),$inout3
+	je	.Locb_dec_four
+
+	movdqu	`16*4`($inp),$inout4
+	pxor	$inout5,$inout5
+
+	call	__ocb_decrypt6
+
+	movdqa	@offset[4],@offset[5]
+	movups	$inout0,`16*0`($out)		# store output
+	pxor	$inout0,$checksum		# accumulate checksum
+	movups	$inout1,`16*1`($out)
+	pxor	$inout1,$checksum
+	movups	$inout2,`16*2`($out)
+	pxor	$inout2,$checksum
+	movups	$inout3,`16*3`($out)
+	pxor	$inout3,$checksum
+	movups	$inout4,`16*4`($out)
+	pxor	$inout4,$checksum
+
+	jmp	.Locb_dec_done
+
+.align	16
+.Locb_dec_one:
+	movdqa	@offset[0],$inout5		# borrow
+
+	call	__ocb_decrypt1
+
+	movdqa	$inout5,@offset[5]
+	movups	$inout0,`16*0`($out)		# store output
+	xorps	$inout0,$checksum		# accumulate checksum
+	jmp	.Locb_dec_done
+
+.align	16
+.Locb_dec_two:
+	pxor	$inout2,$inout2
+	pxor	$inout3,$inout3
+
+	call	__ocb_decrypt4
+
+	movdqa	@offset[1],@offset[5]
+	movups	$inout0,`16*0`($out)		# store output
+	xorps	$inout0,$checksum		# accumulate checksum
+	movups	$inout1,`16*1`($out)
+	xorps	$inout1,$checksum
+
+	jmp	.Locb_dec_done
+
+.align	16
+.Locb_dec_three:
+	pxor	$inout3,$inout3
+
+	call	__ocb_decrypt4
+
+	movdqa	@offset[2],@offset[5]
+	movups	$inout0,`16*0`($out)		# store output
+	xorps	$inout0,$checksum		# accumulate checksum
+	movups	$inout1,`16*1`($out)
+	xorps	$inout1,$checksum
+	movups	$inout2,`16*2`($out)
+	xorps	$inout2,$checksum
+
+	jmp	.Locb_dec_done
+
+.align	16
+.Locb_dec_four:
+	call	__ocb_decrypt4
+
+	movdqa	@offset[3],@offset[5]
+	movups	$inout0,`16*0`($out)		# store output
+	pxor	$inout0,$checksum		# accumulate checksum
+	movups	$inout1,`16*1`($out)
+	pxor	$inout1,$checksum
+	movups	$inout2,`16*2`($out)
+	pxor	$inout2,$checksum
+	movups	$inout3,`16*3`($out)
+	pxor	$inout3,$checksum
+
+.Locb_dec_done:
+	pxor	$rndkey0,@offset[5]		# "remove" round[last]
+	movdqu	$checksum,($checksum_p)		# store checksum
+	movdqu	@offset[5],($offset_p)		# store last offset_i
+
+	xorps	%xmm0,%xmm0			# clear register bank
+	pxor	%xmm1,%xmm1
+	pxor	%xmm2,%xmm2
+	pxor	%xmm3,%xmm3
+	pxor	%xmm4,%xmm4
+	pxor	%xmm5,%xmm5
+___
+$code.=<<___ if (!$win64);
+	pxor	%xmm6,%xmm6
+	pxor	%xmm7,%xmm7
+	pxor	%xmm8,%xmm8
+	pxor	%xmm9,%xmm9
+	pxor	%xmm10,%xmm10
+	pxor	%xmm11,%xmm11
+	pxor	%xmm12,%xmm12
+	pxor	%xmm13,%xmm13
+	pxor	%xmm14,%xmm14
+	pxor	%xmm15,%xmm15
+___
+$code.=<<___ if ($win64);
+	movaps	0x00(%rsp),%xmm6
+	movaps	%xmm0,0x00(%rsp)		# clear stack
+	movaps	0x10(%rsp),%xmm7
+	movaps	%xmm0,0x10(%rsp)
+	movaps	0x20(%rsp),%xmm8
+	movaps	%xmm0,0x20(%rsp)
+	movaps	0x30(%rsp),%xmm9
+	movaps	%xmm0,0x30(%rsp)
+	movaps	0x40(%rsp),%xmm10
+	movaps	%xmm0,0x40(%rsp)
+	movaps	0x50(%rsp),%xmm11
+	movaps	%xmm0,0x50(%rsp)
+	movaps	0x60(%rsp),%xmm12
+	movaps	%xmm0,0x60(%rsp)
+	movaps	0x70(%rsp),%xmm13
+	movaps	%xmm0,0x70(%rsp)
+	movaps	0x80(%rsp),%xmm14
+	movaps	%xmm0,0x80(%rsp)
+	movaps	0x90(%rsp),%xmm15
+	movaps	%xmm0,0x90(%rsp)
+	lea	0xa0+0x28(%rsp),%rax
+.Locb_dec_pop:
+	lea	0xa0(%rsp),%rsp
+___
+$code.=<<___;
+	pop	%r14
+	pop	%r13
+	pop	%r12
+	pop	%rbp
+	pop	%rbx
+.Locb_dec_epilogue:
+	ret
+.size	aesni_ocb_decrypt,.-aesni_ocb_decrypt
+
+.type	__ocb_decrypt6,\@abi-omnipotent
+.align	32
+__ocb_decrypt6:
+	 pxor		$rndkey0l,@offset[5]	# offset_i ^ round[0]
+	 movdqu		($L_p,$i1),@offset[1]
+	 movdqa		@offset[0],@offset[2]
+	 movdqu		($L_p,$i3),@offset[3]
+	 movdqa		@offset[0],@offset[4]
+	 pxor		@offset[5],@offset[0]
+	 movdqu		($L_p,$i5),@offset[5]
+	 pxor		@offset[0],@offset[1]
+	pxor		@offset[0],$inout0	# input ^ round[0] ^ offset_i
+	 pxor		@offset[1],@offset[2]
+	pxor		@offset[1],$inout1
+	 pxor		@offset[2],@offset[3]
+	pxor		@offset[2],$inout2
+	 pxor		@offset[3],@offset[4]
+	pxor		@offset[3],$inout3
+	 pxor		@offset[4],@offset[5]
+	pxor		@offset[4],$inout4
+	pxor		@offset[5],$inout5
+	$movkey		32($key_),$rndkey0
+
+	lea		1($block_num),$i1	# even-numbered blocks
+	lea		3($block_num),$i3
+	lea		5($block_num),$i5
+	add		\$6,$block_num
+	 pxor		$rndkey0l,@offset[0]	# offset_i ^ round[last]
+	bsf		$i1,$i1			# ntz(block)
+	bsf		$i3,$i3
+	bsf		$i5,$i5
+
+	aesdec		$rndkey1,$inout0
+	aesdec		$rndkey1,$inout1
+	aesdec		$rndkey1,$inout2
+	aesdec		$rndkey1,$inout3
+	 pxor		$rndkey0l,@offset[1]
+	 pxor		$rndkey0l,@offset[2]
+	aesdec		$rndkey1,$inout4
+	 pxor		$rndkey0l,@offset[3]
+	 pxor		$rndkey0l,@offset[4]
+	aesdec		$rndkey1,$inout5
+	$movkey		48($key_),$rndkey1
+	 pxor		$rndkey0l,@offset[5]
+
+	aesdec		$rndkey0,$inout0
+	aesdec		$rndkey0,$inout1
+	aesdec		$rndkey0,$inout2
+	aesdec		$rndkey0,$inout3
+	aesdec		$rndkey0,$inout4
+	aesdec		$rndkey0,$inout5
+	$movkey		64($key_),$rndkey0
+	shl		\$4,$i1			# ntz(block) -> table offset
+	shl		\$4,$i3
+	jmp		.Locb_dec_loop6
+
+.align	32
+.Locb_dec_loop6:
+	aesdec		$rndkey1,$inout0
+	aesdec		$rndkey1,$inout1
+	aesdec		$rndkey1,$inout2
+	aesdec		$rndkey1,$inout3
+	aesdec		$rndkey1,$inout4
+	aesdec		$rndkey1,$inout5
+	$movkey		($key,%rax),$rndkey1
+	add		\$32,%rax
+
+	aesdec		$rndkey0,$inout0
+	aesdec		$rndkey0,$inout1
+	aesdec		$rndkey0,$inout2
+	aesdec		$rndkey0,$inout3
+	aesdec		$rndkey0,$inout4
+	aesdec		$rndkey0,$inout5
+	$movkey		-16($key,%rax),$rndkey0
+	jnz		.Locb_dec_loop6
+
+	aesdec		$rndkey1,$inout0
+	aesdec		$rndkey1,$inout1
+	aesdec		$rndkey1,$inout2
+	aesdec		$rndkey1,$inout3
+	aesdec		$rndkey1,$inout4
+	aesdec		$rndkey1,$inout5
+	$movkey		16($key_),$rndkey1
+	shl		\$4,$i5
+
+	aesdeclast	@offset[0],$inout0
+	movdqu		($L_p),@offset[0]	# L_0 for all odd-numbered blocks
+	mov		%r10,%rax		# restore twisted rounds
+	aesdeclast	@offset[1],$inout1
+	aesdeclast	@offset[2],$inout2
+	aesdeclast	@offset[3],$inout3
+	aesdeclast	@offset[4],$inout4
+	aesdeclast	@offset[5],$inout5
+	ret
+.size	__ocb_decrypt6,.-__ocb_decrypt6
+
+.type	__ocb_decrypt4,\@abi-omnipotent
+.align	32
+__ocb_decrypt4:
+	 pxor		$rndkey0l,@offset[5]	# offset_i ^ round[0]
+	 movdqu		($L_p,$i1),@offset[1]
+	 movdqa		@offset[0],@offset[2]
+	 movdqu		($L_p,$i3),@offset[3]
+	 pxor		@offset[5],@offset[0]
+	 pxor		@offset[0],@offset[1]
+	pxor		@offset[0],$inout0	# input ^ round[0] ^ offset_i
+	 pxor		@offset[1],@offset[2]
+	pxor		@offset[1],$inout1
+	 pxor		@offset[2],@offset[3]
+	pxor		@offset[2],$inout2
+	pxor		@offset[3],$inout3
+	$movkey		32($key_),$rndkey0
+
+	 pxor		$rndkey0l,@offset[0]	# offset_i ^ round[last]
+	 pxor		$rndkey0l,@offset[1]
+	 pxor		$rndkey0l,@offset[2]
+	 pxor		$rndkey0l,@offset[3]
+
+	aesdec		$rndkey1,$inout0
+	aesdec		$rndkey1,$inout1
+	aesdec		$rndkey1,$inout2
+	aesdec		$rndkey1,$inout3
+	$movkey		48($key_),$rndkey1
+
+	aesdec		$rndkey0,$inout0
+	aesdec		$rndkey0,$inout1
+	aesdec		$rndkey0,$inout2
+	aesdec		$rndkey0,$inout3
+	$movkey		64($key_),$rndkey0
+	jmp		.Locb_dec_loop4
+
+.align	32
+.Locb_dec_loop4:
+	aesdec		$rndkey1,$inout0
+	aesdec		$rndkey1,$inout1
+	aesdec		$rndkey1,$inout2
+	aesdec		$rndkey1,$inout3
+	$movkey		($key,%rax),$rndkey1
+	add		\$32,%rax
+
+	aesdec		$rndkey0,$inout0
+	aesdec		$rndkey0,$inout1
+	aesdec		$rndkey0,$inout2
+	aesdec		$rndkey0,$inout3
+	$movkey		-16($key,%rax),$rndkey0
+	jnz		.Locb_dec_loop4
+
+	aesdec		$rndkey1,$inout0
+	aesdec		$rndkey1,$inout1
+	aesdec		$rndkey1,$inout2
+	aesdec		$rndkey1,$inout3
+	$movkey		16($key_),$rndkey1
+	mov		%r10,%rax		# restore twisted rounds
+
+	aesdeclast	@offset[0],$inout0
+	aesdeclast	@offset[1],$inout1
+	aesdeclast	@offset[2],$inout2
+	aesdeclast	@offset[3],$inout3
+	ret
+.size	__ocb_decrypt4,.-__ocb_decrypt4
+
+.type	__ocb_decrypt1,\@abi-omnipotent
+.align	32
+__ocb_decrypt1:
+	 pxor		@offset[5],$inout5	# offset_i
+	 pxor		$rndkey0l,$inout5	# offset_i ^ round[0]
+	pxor		$inout5,$inout0		# input ^ round[0] ^ offset_i
+	$movkey		32($key_),$rndkey0
+
+	aesdec		$rndkey1,$inout0
+	$movkey		48($key_),$rndkey1
+	pxor		$rndkey0l,$inout5	# offset_i ^ round[last]
+
+	aesdec		$rndkey0,$inout0
+	$movkey		64($key_),$rndkey0
+	jmp		.Locb_dec_loop1
+
+.align	32
+.Locb_dec_loop1:
+	aesdec		$rndkey1,$inout0
+	$movkey		($key,%rax),$rndkey1
+	add		\$32,%rax
+
+	aesdec		$rndkey0,$inout0
+	$movkey		-16($key,%rax),$rndkey0
+	jnz		.Locb_dec_loop1
+
+	aesdec		$rndkey1,$inout0
+	$movkey		16($key_),$rndkey1	# redundant in tail
+	mov		%r10,%rax		# restore twisted rounds
+
+	aesdeclast	$inout5,$inout0
+	ret
+.size	__ocb_decrypt1,.-__ocb_decrypt1
+___
+} }}
+
+########################################################################
+# void $PREFIX_cbc_encrypt (const void *inp, void *out,
+#			    size_t length, const AES_KEY *key,
+#			    unsigned char *ivp,const int enc);
+{
+my $frame_size = 0x10 + ($win64?0xa0:0);	# used in decrypt
+my ($iv,$in0,$in1,$in2,$in3,$in4)=map("%xmm$_",(10..15));
+my $inp_=$key_;
+
+$code.=<<___;
+.globl	${PREFIX}_cbc_encrypt
+.type	${PREFIX}_cbc_encrypt,\@function,6
+.align	16
+${PREFIX}_cbc_encrypt:
+	test	$len,$len		# check length
+	jz	.Lcbc_ret
+
+	mov	240($key),$rnds_	# key->rounds
+	mov	$key,$key_		# backup $key
+	test	%r9d,%r9d		# 6th argument
+	jz	.Lcbc_decrypt
+#--------------------------- CBC ENCRYPT ------------------------------#
+	movups	($ivp),$inout0		# load iv as initial state
+	mov	$rnds_,$rounds
+	cmp	\$16,$len
+	jb	.Lcbc_enc_tail
+	sub	\$16,$len
+	jmp	.Lcbc_enc_loop
+.align	16
+.Lcbc_enc_loop:
+	movups	($inp),$inout1		# load input
+	lea	16($inp),$inp
+	#xorps	$inout1,$inout0
+___
+	&aesni_generate1("enc",$key,$rounds,$inout0,$inout1);
+$code.=<<___;
+	mov	$rnds_,$rounds		# restore $rounds
+	mov	$key_,$key		# restore $key
+	movups	$inout0,0($out)		# store output
+	lea	16($out),$out
+	sub	\$16,$len
+	jnc	.Lcbc_enc_loop
+	add	\$16,$len
+	jnz	.Lcbc_enc_tail
+	 pxor	$rndkey0,$rndkey0	# clear register bank
+	 pxor	$rndkey1,$rndkey1
+	movups	$inout0,($ivp)
+	 pxor	$inout0,$inout0
+	 pxor	$inout1,$inout1
+	jmp	.Lcbc_ret
+
+.Lcbc_enc_tail:
+	mov	$len,%rcx	# zaps $key
+	xchg	$inp,$out	# $inp is %rsi and $out is %rdi now
+	.long	0x9066A4F3	# rep movsb
+	mov	\$16,%ecx	# zero tail
+	sub	$len,%rcx
+	xor	%eax,%eax
+	.long	0x9066AAF3	# rep stosb
+	lea	-16(%rdi),%rdi	# rewind $out by 1 block
+	mov	$rnds_,$rounds	# restore $rounds
+	mov	%rdi,%rsi	# $inp and $out are the same
+	mov	$key_,$key	# restore $key
+	xor	$len,$len	# len=16
+	jmp	.Lcbc_enc_loop	# one more spin
+#--------------------------- CBC DECRYPT ------------------------------#
+.align	16
+.Lcbc_decrypt:
+	cmp	\$16,$len
+	jne	.Lcbc_decrypt_bulk
+
+	# handle single block without allocating stack frame,
+	# useful in ciphertext stealing mode
+	movdqu	($inp),$inout0		# load input
+	movdqu	($ivp),$inout1		# load iv
+	movdqa	$inout0,$inout2		# future iv
+___
+	&aesni_generate1("dec",$key,$rnds_);
+$code.=<<___;
+	 pxor	$rndkey0,$rndkey0	# clear register bank
+	 pxor	$rndkey1,$rndkey1
+	movdqu	$inout2,($ivp)		# store iv
+	xorps	$inout1,$inout0		# ^=iv
+	 pxor	$inout1,$inout1
+	movups	$inout0,($out)		# store output
+	 pxor	$inout0,$inout0
+	jmp	.Lcbc_ret
+.align	16
+.Lcbc_decrypt_bulk:
+	lea	(%rsp),%rax
+	push	%rbp
+	sub	\$$frame_size,%rsp
+	and	\$-16,%rsp	# Linux kernel stack can be incorrectly seeded
+___
+$code.=<<___ if ($win64);
+	movaps	%xmm6,0x10(%rsp)
+	movaps	%xmm7,0x20(%rsp)
+	movaps	%xmm8,0x30(%rsp)
+	movaps	%xmm9,0x40(%rsp)
+	movaps	%xmm10,0x50(%rsp)
+	movaps	%xmm11,0x60(%rsp)
+	movaps	%xmm12,0x70(%rsp)
+	movaps	%xmm13,0x80(%rsp)
+	movaps	%xmm14,0x90(%rsp)
+	movaps	%xmm15,0xa0(%rsp)
+.Lcbc_decrypt_body:
+___
+$code.=<<___;
+	lea	-8(%rax),%rbp
+	movups	($ivp),$iv
+	mov	$rnds_,$rounds
+	cmp	\$0x50,$len
+	jbe	.Lcbc_dec_tail
+
+	$movkey	($key),$rndkey0
+	movdqu	0x00($inp),$inout0	# load input
+	movdqu	0x10($inp),$inout1
+	movdqa	$inout0,$in0
+	movdqu	0x20($inp),$inout2
+	movdqa	$inout1,$in1
+	movdqu	0x30($inp),$inout3
+	movdqa	$inout2,$in2
+	movdqu	0x40($inp),$inout4
+	movdqa	$inout3,$in3
+	movdqu	0x50($inp),$inout5
+	movdqa	$inout4,$in4
+	mov	OPENSSL_ia32cap_P+4(%rip),%r9d
+	cmp	\$0x70,$len
+	jbe	.Lcbc_dec_six_or_seven
+
+	and	\$`1<<26|1<<22`,%r9d	# isolate XSAVE+MOVBE
+	sub	\$0x50,$len		# $len is biased by -5*16
+	cmp	\$`1<<22`,%r9d		# check for MOVBE without XSAVE
+	je	.Lcbc_dec_loop6_enter	# [which denotes Atom Silvermont]
+	sub	\$0x20,$len		# $len is biased by -7*16
+	lea	0x70($key),$key		# size optimization
+	jmp	.Lcbc_dec_loop8_enter
+.align	16
+.Lcbc_dec_loop8:
+	movups	$inout7,($out)
+	lea	0x10($out),$out
+.Lcbc_dec_loop8_enter:
+	movdqu		0x60($inp),$inout6
+	pxor		$rndkey0,$inout0
+	movdqu		0x70($inp),$inout7
+	pxor		$rndkey0,$inout1
+	$movkey		0x10-0x70($key),$rndkey1
+	pxor		$rndkey0,$inout2
+	xor		$inp_,$inp_
+	cmp		\$0x70,$len	# is there at least 0x60 bytes ahead?
+	pxor		$rndkey0,$inout3
+	pxor		$rndkey0,$inout4
+	pxor		$rndkey0,$inout5
+	pxor		$rndkey0,$inout6
+
+	aesdec		$rndkey1,$inout0
+	pxor		$rndkey0,$inout7
+	$movkey		0x20-0x70($key),$rndkey0
+	aesdec		$rndkey1,$inout1
+	aesdec		$rndkey1,$inout2
+	aesdec		$rndkey1,$inout3
+	aesdec		$rndkey1,$inout4
+	aesdec		$rndkey1,$inout5
+	aesdec		$rndkey1,$inout6
+	setnc		${inp_}b
+	shl		\$7,$inp_
+	aesdec		$rndkey1,$inout7
+	add		$inp,$inp_
+	$movkey		0x30-0x70($key),$rndkey1
+___
+for($i=1;$i<12;$i++) {
+my $rndkeyx = ($i&1)?$rndkey0:$rndkey1;
+$code.=<<___	if ($i==7);
+	cmp		\$11,$rounds
+___
+$code.=<<___;
+	aesdec		$rndkeyx,$inout0
+	aesdec		$rndkeyx,$inout1
+	aesdec		$rndkeyx,$inout2
+	aesdec		$rndkeyx,$inout3
+	aesdec		$rndkeyx,$inout4
+	aesdec		$rndkeyx,$inout5
+	aesdec		$rndkeyx,$inout6
+	aesdec		$rndkeyx,$inout7
+	$movkey		`0x30+0x10*$i`-0x70($key),$rndkeyx
+___
+$code.=<<___	if ($i<6 || (!($i&1) && $i>7));
+	nop
+___
+$code.=<<___	if ($i==7);
+	jb		.Lcbc_dec_done
+___
+$code.=<<___	if ($i==9);
+	je		.Lcbc_dec_done
+___
+$code.=<<___	if ($i==11);
+	jmp		.Lcbc_dec_done
+___
+}
+$code.=<<___;
+.align	16
+.Lcbc_dec_done:
+	aesdec		$rndkey1,$inout0
+	aesdec		$rndkey1,$inout1
+	pxor		$rndkey0,$iv
+	pxor		$rndkey0,$in0
+	aesdec		$rndkey1,$inout2
+	aesdec		$rndkey1,$inout3
+	pxor		$rndkey0,$in1
+	pxor		$rndkey0,$in2
+	aesdec		$rndkey1,$inout4
+	aesdec		$rndkey1,$inout5
+	pxor		$rndkey0,$in3
+	pxor		$rndkey0,$in4
+	aesdec		$rndkey1,$inout6
+	aesdec		$rndkey1,$inout7
+	movdqu		0x50($inp),$rndkey1
+
+	aesdeclast	$iv,$inout0
+	movdqu		0x60($inp),$iv		# borrow $iv
+	pxor		$rndkey0,$rndkey1
+	aesdeclast	$in0,$inout1
+	pxor		$rndkey0,$iv
+	movdqu		0x70($inp),$rndkey0	# next IV
+	aesdeclast	$in1,$inout2
+	lea		0x80($inp),$inp
+	movdqu		0x00($inp_),$in0
+	aesdeclast	$in2,$inout3
+	aesdeclast	$in3,$inout4
+	movdqu		0x10($inp_),$in1
+	movdqu		0x20($inp_),$in2
+	aesdeclast	$in4,$inout5
+	aesdeclast	$rndkey1,$inout6
+	movdqu		0x30($inp_),$in3
+	movdqu		0x40($inp_),$in4
+	aesdeclast	$iv,$inout7
+	movdqa		$rndkey0,$iv		# return $iv
+	movdqu		0x50($inp_),$rndkey1
+	$movkey		-0x70($key),$rndkey0
+
+	movups		$inout0,($out)		# store output
+	movdqa		$in0,$inout0
+	movups		$inout1,0x10($out)
+	movdqa		$in1,$inout1
+	movups		$inout2,0x20($out)
+	movdqa		$in2,$inout2
+	movups		$inout3,0x30($out)
+	movdqa		$in3,$inout3
+	movups		$inout4,0x40($out)
+	movdqa		$in4,$inout4
+	movups		$inout5,0x50($out)
+	movdqa		$rndkey1,$inout5
+	movups		$inout6,0x60($out)
+	lea		0x70($out),$out
+
+	sub	\$0x80,$len
+	ja	.Lcbc_dec_loop8
+
+	movaps	$inout7,$inout0
+	lea	-0x70($key),$key
+	add	\$0x70,$len
+	jle	.Lcbc_dec_clear_tail_collected
+	movups	$inout7,($out)
+	lea	0x10($out),$out
+	cmp	\$0x50,$len
+	jbe	.Lcbc_dec_tail
+
+	movaps	$in0,$inout0
+.Lcbc_dec_six_or_seven:
+	cmp	\$0x60,$len
+	ja	.Lcbc_dec_seven
+
+	movaps	$inout5,$inout6
+	call	_aesni_decrypt6
+	pxor	$iv,$inout0		# ^= IV
+	movaps	$inout6,$iv
+	pxor	$in0,$inout1
+	movdqu	$inout0,($out)
+	pxor	$in1,$inout2
+	movdqu	$inout1,0x10($out)
+	 pxor	$inout1,$inout1		# clear register bank
+	pxor	$in2,$inout3
+	movdqu	$inout2,0x20($out)
+	 pxor	$inout2,$inout2
+	pxor	$in3,$inout4
+	movdqu	$inout3,0x30($out)
+	 pxor	$inout3,$inout3
+	pxor	$in4,$inout5
+	movdqu	$inout4,0x40($out)
+	 pxor	$inout4,$inout4
+	lea	0x50($out),$out
+	movdqa	$inout5,$inout0
+	 pxor	$inout5,$inout5
+	jmp	.Lcbc_dec_tail_collected
+
+.align	16
+.Lcbc_dec_seven:
+	movups	0x60($inp),$inout6
+	xorps	$inout7,$inout7
+	call	_aesni_decrypt8
+	movups	0x50($inp),$inout7
+	pxor	$iv,$inout0		# ^= IV
+	movups	0x60($inp),$iv
+	pxor	$in0,$inout1
+	movdqu	$inout0,($out)
+	pxor	$in1,$inout2
+	movdqu	$inout1,0x10($out)
+	 pxor	$inout1,$inout1		# clear register bank
+	pxor	$in2,$inout3
+	movdqu	$inout2,0x20($out)
+	 pxor	$inout2,$inout2
+	pxor	$in3,$inout4
+	movdqu	$inout3,0x30($out)
+	 pxor	$inout3,$inout3
+	pxor	$in4,$inout5
+	movdqu	$inout4,0x40($out)
+	 pxor	$inout4,$inout4
+	pxor	$inout7,$inout6
+	movdqu	$inout5,0x50($out)
+	 pxor	$inout5,$inout5
+	lea	0x60($out),$out
+	movdqa	$inout6,$inout0
+	 pxor	$inout6,$inout6
+	 pxor	$inout7,$inout7
+	jmp	.Lcbc_dec_tail_collected
+
+.align	16
+.Lcbc_dec_loop6:
+	movups	$inout5,($out)
+	lea	0x10($out),$out
+	movdqu	0x00($inp),$inout0	# load input
+	movdqu	0x10($inp),$inout1
+	movdqa	$inout0,$in0
+	movdqu	0x20($inp),$inout2
+	movdqa	$inout1,$in1
+	movdqu	0x30($inp),$inout3
+	movdqa	$inout2,$in2
+	movdqu	0x40($inp),$inout4
+	movdqa	$inout3,$in3
+	movdqu	0x50($inp),$inout5
+	movdqa	$inout4,$in4
+.Lcbc_dec_loop6_enter:
+	lea	0x60($inp),$inp
+	movdqa	$inout5,$inout6
+
+	call	_aesni_decrypt6
+
+	pxor	$iv,$inout0		# ^= IV
+	movdqa	$inout6,$iv
+	pxor	$in0,$inout1
+	movdqu	$inout0,($out)
+	pxor	$in1,$inout2
+	movdqu	$inout1,0x10($out)
+	pxor	$in2,$inout3
+	movdqu	$inout2,0x20($out)
+	pxor	$in3,$inout4
+	mov	$key_,$key
+	movdqu	$inout3,0x30($out)
+	pxor	$in4,$inout5
+	mov	$rnds_,$rounds
+	movdqu	$inout4,0x40($out)
+	lea	0x50($out),$out
+	sub	\$0x60,$len
+	ja	.Lcbc_dec_loop6
+
+	movdqa	$inout5,$inout0
+	add	\$0x50,$len
+	jle	.Lcbc_dec_clear_tail_collected
+	movups	$inout5,($out)
+	lea	0x10($out),$out
+
+.Lcbc_dec_tail:
+	movups	($inp),$inout0
+	sub	\$0x10,$len
+	jbe	.Lcbc_dec_one		# $len is 1*16 or less
+
+	movups	0x10($inp),$inout1
+	movaps	$inout0,$in0
+	sub	\$0x10,$len
+	jbe	.Lcbc_dec_two		# $len is 2*16 or less
+
+	movups	0x20($inp),$inout2
+	movaps	$inout1,$in1
+	sub	\$0x10,$len
+	jbe	.Lcbc_dec_three		# $len is 3*16 or less
+
+	movups	0x30($inp),$inout3
+	movaps	$inout2,$in2
+	sub	\$0x10,$len
+	jbe	.Lcbc_dec_four		# $len is 4*16 or less
+
+	movups	0x40($inp),$inout4	# $len is 5*16 or less
+	movaps	$inout3,$in3
+	movaps	$inout4,$in4
+	xorps	$inout5,$inout5
+	call	_aesni_decrypt6
+	pxor	$iv,$inout0
+	movaps	$in4,$iv
+	pxor	$in0,$inout1
+	movdqu	$inout0,($out)
+	pxor	$in1,$inout2
+	movdqu	$inout1,0x10($out)
+	 pxor	$inout1,$inout1		# clear register bank
+	pxor	$in2,$inout3
+	movdqu	$inout2,0x20($out)
+	 pxor	$inout2,$inout2
+	pxor	$in3,$inout4
+	movdqu	$inout3,0x30($out)
+	 pxor	$inout3,$inout3
+	lea	0x40($out),$out
+	movdqa	$inout4,$inout0
+	 pxor	$inout4,$inout4
+	 pxor	$inout5,$inout5
+	sub	\$0x10,$len
+	jmp	.Lcbc_dec_tail_collected
+
+.align	16
+.Lcbc_dec_one:
+	movaps	$inout0,$in0
+___
+	&aesni_generate1("dec",$key,$rounds);
+$code.=<<___;
+	xorps	$iv,$inout0
+	movaps	$in0,$iv
+	jmp	.Lcbc_dec_tail_collected
+.align	16
+.Lcbc_dec_two:
+	movaps	$inout1,$in1
+	call	_aesni_decrypt2
+	pxor	$iv,$inout0
+	movaps	$in1,$iv
+	pxor	$in0,$inout1
+	movdqu	$inout0,($out)
+	movdqa	$inout1,$inout0
+	 pxor	$inout1,$inout1		# clear register bank
+	lea	0x10($out),$out
+	jmp	.Lcbc_dec_tail_collected
+.align	16
+.Lcbc_dec_three:
+	movaps	$inout2,$in2
+	call	_aesni_decrypt3
+	pxor	$iv,$inout0
+	movaps	$in2,$iv
+	pxor	$in0,$inout1
+	movdqu	$inout0,($out)
+	pxor	$in1,$inout2
+	movdqu	$inout1,0x10($out)
+	 pxor	$inout1,$inout1		# clear register bank
+	movdqa	$inout2,$inout0
+	 pxor	$inout2,$inout2
+	lea	0x20($out),$out
+	jmp	.Lcbc_dec_tail_collected
+.align	16
+.Lcbc_dec_four:
+	movaps	$inout3,$in3
+	call	_aesni_decrypt4
+	pxor	$iv,$inout0
+	movaps	$in3,$iv
+	pxor	$in0,$inout1
+	movdqu	$inout0,($out)
+	pxor	$in1,$inout2
+	movdqu	$inout1,0x10($out)
+	 pxor	$inout1,$inout1		# clear register bank
+	pxor	$in2,$inout3
+	movdqu	$inout2,0x20($out)
+	 pxor	$inout2,$inout2
+	movdqa	$inout3,$inout0
+	 pxor	$inout3,$inout3
+	lea	0x30($out),$out
+	jmp	.Lcbc_dec_tail_collected
+
+.align	16
+.Lcbc_dec_clear_tail_collected:
+	pxor	$inout1,$inout1		# clear register bank
+	pxor	$inout2,$inout2
+	pxor	$inout3,$inout3
+___
+$code.=<<___ if (!$win64);
+	pxor	$inout4,$inout4		# %xmm6..9
+	pxor	$inout5,$inout5
+	pxor	$inout6,$inout6
+	pxor	$inout7,$inout7
+___
+$code.=<<___;
+.Lcbc_dec_tail_collected:
+	movups	$iv,($ivp)
+	and	\$15,$len
+	jnz	.Lcbc_dec_tail_partial
+	movups	$inout0,($out)
+	pxor	$inout0,$inout0
+	jmp	.Lcbc_dec_ret
+.align	16
+.Lcbc_dec_tail_partial:
+	movaps	$inout0,(%rsp)
+	pxor	$inout0,$inout0
+	mov	\$16,%rcx
+	mov	$out,%rdi
+	sub	$len,%rcx
+	lea	(%rsp),%rsi
+	.long	0x9066A4F3		# rep movsb
+	movdqa	$inout0,(%rsp)
+
+.Lcbc_dec_ret:
+	xorps	$rndkey0,$rndkey0	# %xmm0
+	pxor	$rndkey1,$rndkey1
+___
+$code.=<<___ if ($win64);
+	movaps	0x10(%rsp),%xmm6
+	movaps	%xmm0,0x10(%rsp)	# clear stack
+	movaps	0x20(%rsp),%xmm7
+	movaps	%xmm0,0x20(%rsp)
+	movaps	0x30(%rsp),%xmm8
+	movaps	%xmm0,0x30(%rsp)
+	movaps	0x40(%rsp),%xmm9
+	movaps	%xmm0,0x40(%rsp)
+	movaps	0x50(%rsp),%xmm10
+	movaps	%xmm0,0x50(%rsp)
+	movaps	0x60(%rsp),%xmm11
+	movaps	%xmm0,0x60(%rsp)
+	movaps	0x70(%rsp),%xmm12
+	movaps	%xmm0,0x70(%rsp)
+	movaps	0x80(%rsp),%xmm13
+	movaps	%xmm0,0x80(%rsp)
+	movaps	0x90(%rsp),%xmm14
+	movaps	%xmm0,0x90(%rsp)
+	movaps	0xa0(%rsp),%xmm15
+	movaps	%xmm0,0xa0(%rsp)
+___
+$code.=<<___;
+	lea	(%rbp),%rsp
+	pop	%rbp
+.Lcbc_ret:
+	ret
+.size	${PREFIX}_cbc_encrypt,.-${PREFIX}_cbc_encrypt
+___
+} 
+# int ${PREFIX}_set_decrypt_key(const unsigned char *inp,
+#				int bits, AES_KEY *key)
+#
+# input:	$inp	user-supplied key
+#		$bits	$inp length in bits
+#		$key	pointer to key schedule
+# output:	%eax	0 denoting success, -1 or -2 - failure (see C)
+#		*$key	key schedule
+#
+{ my ($inp,$bits,$key) = @_4args;
+  $bits =~ s/%r/%e/;
+
+$code.=<<___;
+.globl	${PREFIX}_set_decrypt_key
+.type	${PREFIX}_set_decrypt_key,\@abi-omnipotent
+.align	16
+${PREFIX}_set_decrypt_key:
+	.byte	0x48,0x83,0xEC,0x08	# sub rsp,8
+	call	__aesni_set_encrypt_key
+	shl	\$4,$bits		# rounds-1 after _aesni_set_encrypt_key
+	test	%eax,%eax
+	jnz	.Ldec_key_ret
+	lea	16($key,$bits),$inp	# points at the end of key schedule
+
+	$movkey	($key),%xmm0		# just swap
+	$movkey	($inp),%xmm1
+	$movkey	%xmm0,($inp)
+	$movkey	%xmm1,($key)
+	lea	16($key),$key
+	lea	-16($inp),$inp
+
+.Ldec_key_inverse:
+	$movkey	($key),%xmm0		# swap and inverse
+	$movkey	($inp),%xmm1
+	aesimc	%xmm0,%xmm0
+	aesimc	%xmm1,%xmm1
+	lea	16($key),$key
+	lea	-16($inp),$inp
+	$movkey	%xmm0,16($inp)
+	$movkey	%xmm1,-16($key)
+	cmp	$key,$inp
+	ja	.Ldec_key_inverse
+
+	$movkey	($key),%xmm0		# inverse middle
+	aesimc	%xmm0,%xmm0
+	pxor	%xmm1,%xmm1
+	$movkey	%xmm0,($inp)
+	pxor	%xmm0,%xmm0
+.Ldec_key_ret:
+	add	\$8,%rsp
+	ret
+.LSEH_end_set_decrypt_key:
+.size	${PREFIX}_set_decrypt_key,.-${PREFIX}_set_decrypt_key
+___
+
+# This is based on submission by
+#
+#	Huang Ying 
+#	Vinodh Gopal 
+#	Kahraman Akdemir
+#
+# Aggressively optimized in respect to aeskeygenassist's critical path
+# and is contained in %xmm0-5 to meet Win64 ABI requirement.
+#
+# int ${PREFIX}_set_encrypt_key(const unsigned char *inp,
+#				int bits, AES_KEY * const key);
+#
+# input:	$inp	user-supplied key
+#		$bits	$inp length in bits
+#		$key	pointer to key schedule
+# output:	%eax	0 denoting success, -1 or -2 - failure (see C)
+#		$bits	rounds-1 (used in aesni_set_decrypt_key)
+#		*$key	key schedule
+#		$key	pointer to key schedule (used in
+#			aesni_set_decrypt_key)
+#
+# Subroutine is frame-less, which means that only volatile registers
+# are used. Note that it's declared "abi-omnipotent", which means that
+# amount of volatile registers is smaller on Windows.
+#
+$code.=<<___;
+.globl	${PREFIX}_set_encrypt_key
+.type	${PREFIX}_set_encrypt_key,\@abi-omnipotent
+.align	16
+${PREFIX}_set_encrypt_key:
+__aesni_set_encrypt_key:
+	.byte	0x48,0x83,0xEC,0x08	# sub rsp,8
+	mov	\$-1,%rax
+	test	$inp,$inp
+	jz	.Lenc_key_ret
+	test	$key,$key
+	jz	.Lenc_key_ret
+
+	mov	\$`1<<28|1<<11`,%r10d	# AVX and XOP bits
+	movups	($inp),%xmm0		# pull first 128 bits of *userKey
+	xorps	%xmm4,%xmm4		# low dword of xmm4 is assumed 0
+	and	OPENSSL_ia32cap_P+4(%rip),%r10d
+	lea	16($key),%rax		# %rax is used as modifiable copy of $key
+	cmp	\$256,$bits
+	je	.L14rounds
+	cmp	\$192,$bits
+	je	.L12rounds
+	cmp	\$128,$bits
+	jne	.Lbad_keybits
+
+.L10rounds:
+	mov	\$9,$bits			# 10 rounds for 128-bit key
+	cmp	\$`1<<28`,%r10d			# AVX, bit no XOP
+	je	.L10rounds_alt
+
+	$movkey	%xmm0,($key)			# round 0
+	aeskeygenassist	\$0x1,%xmm0,%xmm1	# round 1
+	call		.Lkey_expansion_128_cold
+	aeskeygenassist	\$0x2,%xmm0,%xmm1	# round 2
+	call		.Lkey_expansion_128
+	aeskeygenassist	\$0x4,%xmm0,%xmm1	# round 3
+	call		.Lkey_expansion_128
+	aeskeygenassist	\$0x8,%xmm0,%xmm1	# round 4
+	call		.Lkey_expansion_128
+	aeskeygenassist	\$0x10,%xmm0,%xmm1	# round 5
+	call		.Lkey_expansion_128
+	aeskeygenassist	\$0x20,%xmm0,%xmm1	# round 6
+	call		.Lkey_expansion_128
+	aeskeygenassist	\$0x40,%xmm0,%xmm1	# round 7
+	call		.Lkey_expansion_128
+	aeskeygenassist	\$0x80,%xmm0,%xmm1	# round 8
+	call		.Lkey_expansion_128
+	aeskeygenassist	\$0x1b,%xmm0,%xmm1	# round 9
+	call		.Lkey_expansion_128
+	aeskeygenassist	\$0x36,%xmm0,%xmm1	# round 10
+	call		.Lkey_expansion_128
+	$movkey	%xmm0,(%rax)
+	mov	$bits,80(%rax)	# 240(%rdx)
+	xor	%eax,%eax
+	jmp	.Lenc_key_ret
+
+.align	16
+.L10rounds_alt:
+	movdqa	.Lkey_rotate(%rip),%xmm5
+	mov	\$8,%r10d
+	movdqa	.Lkey_rcon1(%rip),%xmm4
+	movdqa	%xmm0,%xmm2
+	movdqu	%xmm0,($key)
+	jmp	.Loop_key128
+
+.align	16
+.Loop_key128:
+	pshufb		%xmm5,%xmm0
+	aesenclast	%xmm4,%xmm0
+	pslld		\$1,%xmm4
+	lea		16(%rax),%rax
+
+	movdqa		%xmm2,%xmm3
+	pslldq		\$4,%xmm2
+	pxor		%xmm2,%xmm3
+	pslldq		\$4,%xmm2
+	pxor		%xmm2,%xmm3
+	pslldq		\$4,%xmm2
+	pxor		%xmm3,%xmm2
+
+	pxor		%xmm2,%xmm0
+	movdqu		%xmm0,-16(%rax)
+	movdqa		%xmm0,%xmm2
+
+	dec	%r10d
+	jnz	.Loop_key128
+
+	movdqa		.Lkey_rcon1b(%rip),%xmm4
+
+	pshufb		%xmm5,%xmm0
+	aesenclast	%xmm4,%xmm0
+	pslld		\$1,%xmm4
+
+	movdqa		%xmm2,%xmm3
+	pslldq		\$4,%xmm2
+	pxor		%xmm2,%xmm3
+	pslldq		\$4,%xmm2
+	pxor		%xmm2,%xmm3
+	pslldq		\$4,%xmm2
+	pxor		%xmm3,%xmm2
+
+	pxor		%xmm2,%xmm0
+	movdqu		%xmm0,(%rax)
+
+	movdqa		%xmm0,%xmm2
+	pshufb		%xmm5,%xmm0
+	aesenclast	%xmm4,%xmm0
+
+	movdqa		%xmm2,%xmm3
+	pslldq		\$4,%xmm2
+	pxor		%xmm2,%xmm3
+	pslldq		\$4,%xmm2
+	pxor		%xmm2,%xmm3
+	pslldq		\$4,%xmm2
+	pxor		%xmm3,%xmm2
+
+	pxor		%xmm2,%xmm0
+	movdqu		%xmm0,16(%rax)
+
+	mov	$bits,96(%rax)	# 240($key)
+	xor	%eax,%eax
+	jmp	.Lenc_key_ret
+
+.align	16
+.L12rounds:
+	movq	16($inp),%xmm2			# remaining 1/3 of *userKey
+	mov	\$11,$bits			# 12 rounds for 192
+	cmp	\$`1<<28`,%r10d			# AVX, but no XOP
+	je	.L12rounds_alt
+
+	$movkey	%xmm0,($key)			# round 0
+	aeskeygenassist	\$0x1,%xmm2,%xmm1	# round 1,2
+	call		.Lkey_expansion_192a_cold
+	aeskeygenassist	\$0x2,%xmm2,%xmm1	# round 2,3
+	call		.Lkey_expansion_192b
+	aeskeygenassist	\$0x4,%xmm2,%xmm1	# round 4,5
+	call		.Lkey_expansion_192a
+	aeskeygenassist	\$0x8,%xmm2,%xmm1	# round 5,6
+	call		.Lkey_expansion_192b
+	aeskeygenassist	\$0x10,%xmm2,%xmm1	# round 7,8
+	call		.Lkey_expansion_192a
+	aeskeygenassist	\$0x20,%xmm2,%xmm1	# round 8,9
+	call		.Lkey_expansion_192b
+	aeskeygenassist	\$0x40,%xmm2,%xmm1	# round 10,11
+	call		.Lkey_expansion_192a
+	aeskeygenassist	\$0x80,%xmm2,%xmm1	# round 11,12
+	call		.Lkey_expansion_192b
+	$movkey	%xmm0,(%rax)
+	mov	$bits,48(%rax)	# 240(%rdx)
+	xor	%rax, %rax
+	jmp	.Lenc_key_ret
+
+.align	16
+.L12rounds_alt:
+	movdqa	.Lkey_rotate192(%rip),%xmm5
+	movdqa	.Lkey_rcon1(%rip),%xmm4
+	mov	\$8,%r10d
+	movdqu	%xmm0,($key)
+	jmp	.Loop_key192
+
+.align	16
+.Loop_key192:
+	movq		%xmm2,0(%rax)
+	movdqa		%xmm2,%xmm1
+	pshufb		%xmm5,%xmm2
+	aesenclast	%xmm4,%xmm2
+	pslld		\$1, %xmm4
+	lea		24(%rax),%rax
+
+	movdqa		%xmm0,%xmm3
+	pslldq		\$4,%xmm0
+	pxor		%xmm0,%xmm3
+	pslldq		\$4,%xmm0
+	pxor		%xmm0,%xmm3
+	pslldq		\$4,%xmm0
+	pxor		%xmm3,%xmm0
+
+	pshufd		\$0xff,%xmm0,%xmm3
+	pxor		%xmm1,%xmm3
+	pslldq		\$4,%xmm1
+	pxor		%xmm1,%xmm3
+
+	pxor		%xmm2,%xmm0
+	pxor		%xmm3,%xmm2
+	movdqu		%xmm0,-16(%rax)
+
+	dec	%r10d
+	jnz	.Loop_key192
+
+	mov	$bits,32(%rax)	# 240($key)
+	xor	%eax,%eax
+	jmp	.Lenc_key_ret
+
+.align	16
+.L14rounds:
+	movups	16($inp),%xmm2			# remaning half of *userKey
+	mov	\$13,$bits			# 14 rounds for 256
+	lea	16(%rax),%rax
+	cmp	\$`1<<28`,%r10d			# AVX, but no XOP
+	je	.L14rounds_alt
+
+	$movkey	%xmm0,($key)			# round 0
+	$movkey	%xmm2,16($key)			# round 1
+	aeskeygenassist	\$0x1,%xmm2,%xmm1	# round 2
+	call		.Lkey_expansion_256a_cold
+	aeskeygenassist	\$0x1,%xmm0,%xmm1	# round 3
+	call		.Lkey_expansion_256b
+	aeskeygenassist	\$0x2,%xmm2,%xmm1	# round 4
+	call		.Lkey_expansion_256a
+	aeskeygenassist	\$0x2,%xmm0,%xmm1	# round 5
+	call		.Lkey_expansion_256b
+	aeskeygenassist	\$0x4,%xmm2,%xmm1	# round 6
+	call		.Lkey_expansion_256a
+	aeskeygenassist	\$0x4,%xmm0,%xmm1	# round 7
+	call		.Lkey_expansion_256b
+	aeskeygenassist	\$0x8,%xmm2,%xmm1	# round 8
+	call		.Lkey_expansion_256a
+	aeskeygenassist	\$0x8,%xmm0,%xmm1	# round 9
+	call		.Lkey_expansion_256b
+	aeskeygenassist	\$0x10,%xmm2,%xmm1	# round 10
+	call		.Lkey_expansion_256a
+	aeskeygenassist	\$0x10,%xmm0,%xmm1	# round 11
+	call		.Lkey_expansion_256b
+	aeskeygenassist	\$0x20,%xmm2,%xmm1	# round 12
+	call		.Lkey_expansion_256a
+	aeskeygenassist	\$0x20,%xmm0,%xmm1	# round 13
+	call		.Lkey_expansion_256b
+	aeskeygenassist	\$0x40,%xmm2,%xmm1	# round 14
+	call		.Lkey_expansion_256a
+	$movkey	%xmm0,(%rax)
+	mov	$bits,16(%rax)	# 240(%rdx)
+	xor	%rax,%rax
+	jmp	.Lenc_key_ret
+
+.align	16
+.L14rounds_alt:
+	movdqa	.Lkey_rotate(%rip),%xmm5
+	movdqa	.Lkey_rcon1(%rip),%xmm4
+	mov	\$7,%r10d
+	movdqu	%xmm0,0($key)
+	movdqa	%xmm2,%xmm1
+	movdqu	%xmm2,16($key)
+	jmp	.Loop_key256
+
+.align	16
+.Loop_key256:
+	pshufb		%xmm5,%xmm2
+	aesenclast	%xmm4,%xmm2
+
+	movdqa		%xmm0,%xmm3
+	pslldq		\$4,%xmm0
+	pxor		%xmm0,%xmm3
+	pslldq		\$4,%xmm0
+	pxor		%xmm0,%xmm3
+	pslldq		\$4,%xmm0
+	pxor		%xmm3,%xmm0
+	pslld		\$1,%xmm4
+
+	pxor		%xmm2,%xmm0
+	movdqu		%xmm0,(%rax)
+
+	dec	%r10d
+	jz	.Ldone_key256
+
+	pshufd		\$0xff,%xmm0,%xmm2
+	pxor		%xmm3,%xmm3
+	aesenclast	%xmm3,%xmm2
+
+	movdqa		%xmm1,%xmm3
+	pslldq		\$4,%xmm1
+	pxor		%xmm1,%xmm3
+	pslldq		\$4,%xmm1
+	pxor		%xmm1,%xmm3
+	pslldq		\$4,%xmm1
+	pxor		%xmm3,%xmm1
+
+	pxor		%xmm1,%xmm2
+	movdqu		%xmm2,16(%rax)
+	lea		32(%rax),%rax
+	movdqa		%xmm2,%xmm1
+
+	jmp	.Loop_key256
+
+.Ldone_key256:
+	mov	$bits,16(%rax)	# 240($key)
+	xor	%eax,%eax
+	jmp	.Lenc_key_ret
+
+.align	16
+.Lbad_keybits:
+	mov	\$-2,%rax
+.Lenc_key_ret:
+	pxor	%xmm0,%xmm0
+	pxor	%xmm1,%xmm1
+	pxor	%xmm2,%xmm2
+	pxor	%xmm3,%xmm3
+	pxor	%xmm4,%xmm4
+	pxor	%xmm5,%xmm5
+	add	\$8,%rsp
+	ret
+.LSEH_end_set_encrypt_key:
+
+.align	16
+.Lkey_expansion_128:
+	$movkey	%xmm0,(%rax)
+	lea	16(%rax),%rax
+.Lkey_expansion_128_cold:
+	shufps	\$0b00010000,%xmm0,%xmm4
+	xorps	%xmm4, %xmm0
+	shufps	\$0b10001100,%xmm0,%xmm4
+	xorps	%xmm4, %xmm0
+	shufps	\$0b11111111,%xmm1,%xmm1	# critical path
+	xorps	%xmm1,%xmm0
+	ret
+
+.align 16
+.Lkey_expansion_192a:
+	$movkey	%xmm0,(%rax)
+	lea	16(%rax),%rax
+.Lkey_expansion_192a_cold:
+	movaps	%xmm2, %xmm5
+.Lkey_expansion_192b_warm:
+	shufps	\$0b00010000,%xmm0,%xmm4
+	movdqa	%xmm2,%xmm3
+	xorps	%xmm4,%xmm0
+	shufps	\$0b10001100,%xmm0,%xmm4
+	pslldq	\$4,%xmm3
+	xorps	%xmm4,%xmm0
+	pshufd	\$0b01010101,%xmm1,%xmm1	# critical path
+	pxor	%xmm3,%xmm2
+	pxor	%xmm1,%xmm0
+	pshufd	\$0b11111111,%xmm0,%xmm3
+	pxor	%xmm3,%xmm2
+	ret
+
+.align 16
+.Lkey_expansion_192b:
+	movaps	%xmm0,%xmm3
+	shufps	\$0b01000100,%xmm0,%xmm5
+	$movkey	%xmm5,(%rax)
+	shufps	\$0b01001110,%xmm2,%xmm3
+	$movkey	%xmm3,16(%rax)
+	lea	32(%rax),%rax
+	jmp	.Lkey_expansion_192b_warm
+
+.align	16
+.Lkey_expansion_256a:
+	$movkey	%xmm2,(%rax)
+	lea	16(%rax),%rax
+.Lkey_expansion_256a_cold:
+	shufps	\$0b00010000,%xmm0,%xmm4
+	xorps	%xmm4,%xmm0
+	shufps	\$0b10001100,%xmm0,%xmm4
+	xorps	%xmm4,%xmm0
+	shufps	\$0b11111111,%xmm1,%xmm1	# critical path
+	xorps	%xmm1,%xmm0
+	ret
+
+.align 16
+.Lkey_expansion_256b:
+	$movkey	%xmm0,(%rax)
+	lea	16(%rax),%rax
+
+	shufps	\$0b00010000,%xmm2,%xmm4
+	xorps	%xmm4,%xmm2
+	shufps	\$0b10001100,%xmm2,%xmm4
+	xorps	%xmm4,%xmm2
+	shufps	\$0b10101010,%xmm1,%xmm1	# critical path
+	xorps	%xmm1,%xmm2
+	ret
+.size	${PREFIX}_set_encrypt_key,.-${PREFIX}_set_encrypt_key
+.size	__aesni_set_encrypt_key,.-__aesni_set_encrypt_key
+___
+}
+
+$code.=<<___;
+.align	64
+.Lbswap_mask:
+	.byte	15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0
+.Lincrement32:
+	.long	6,6,6,0
+.Lincrement64:
+	.long	1,0,0,0
+.Lxts_magic:
+	.long	0x87,0,1,0
+.Lincrement1:
+	.byte	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1
+.Lkey_rotate:
+	.long	0x0c0f0e0d,0x0c0f0e0d,0x0c0f0e0d,0x0c0f0e0d
+.Lkey_rotate192:
+	.long	0x04070605,0x04070605,0x04070605,0x04070605
+.Lkey_rcon1:
+	.long	1,1,1,1
+.Lkey_rcon1b:
+	.long	0x1b,0x1b,0x1b,0x1b
+
+.asciz  "AES for Intel AES-NI, CRYPTOGAMS by "
+.align	64
+___
+
+# EXCEPTION_DISPOSITION handler (EXCEPTION_RECORD *rec,ULONG64 frame,
+#		CONTEXT *context,DISPATCHER_CONTEXT *disp)
+if ($win64) {
+$rec="%rcx";
+$frame="%rdx";
+$context="%r8";
+$disp="%r9";
+
+$code.=<<___;
+.extern	__imp_RtlVirtualUnwind
+___
+$code.=<<___ if ($PREFIX eq "aesni");
+.type	ecb_ccm64_se_handler,\@abi-omnipotent
+.align	16
+ecb_ccm64_se_handler:
+	push	%rsi
+	push	%rdi
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+	pushfq
+	sub	\$64,%rsp
+
+	mov	120($context),%rax	# pull context->Rax
+	mov	248($context),%rbx	# pull context->Rip
+
+	mov	8($disp),%rsi		# disp->ImageBase
+	mov	56($disp),%r11		# disp->HandlerData
+
+	mov	0(%r11),%r10d		# HandlerData[0]
+	lea	(%rsi,%r10),%r10	# prologue label
+	cmp	%r10,%rbx		# context->RipRsp
+
+	mov	4(%r11),%r10d		# HandlerData[1]
+	lea	(%rsi,%r10),%r10	# epilogue label
+	cmp	%r10,%rbx		# context->Rip>=epilogue label
+	jae	.Lcommon_seh_tail
+
+	lea	0(%rax),%rsi		# %xmm save area
+	lea	512($context),%rdi	# &context.Xmm6
+	mov	\$8,%ecx		# 4*sizeof(%xmm0)/sizeof(%rax)
+	.long	0xa548f3fc		# cld; rep movsq
+	lea	0x58(%rax),%rax		# adjust stack pointer
+
+	jmp	.Lcommon_seh_tail
+.size	ecb_ccm64_se_handler,.-ecb_ccm64_se_handler
+
+.type	ctr_xts_se_handler,\@abi-omnipotent
+.align	16
+ctr_xts_se_handler:
+	push	%rsi
+	push	%rdi
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+	pushfq
+	sub	\$64,%rsp
+
+	mov	120($context),%rax	# pull context->Rax
+	mov	248($context),%rbx	# pull context->Rip
+
+	mov	8($disp),%rsi		# disp->ImageBase
+	mov	56($disp),%r11		# disp->HandlerData
+
+	mov	0(%r11),%r10d		# HandlerData[0]
+	lea	(%rsi,%r10),%r10	# prologue lable
+	cmp	%r10,%rbx		# context->RipRsp
+
+	mov	4(%r11),%r10d		# HandlerData[1]
+	lea	(%rsi,%r10),%r10	# epilogue label
+	cmp	%r10,%rbx		# context->Rip>=epilogue label
+	jae	.Lcommon_seh_tail
+
+	mov	160($context),%rax	# pull context->Rbp
+	lea	-0xa0(%rax),%rsi	# %xmm save area
+	lea	512($context),%rdi	# & context.Xmm6
+	mov	\$20,%ecx		# 10*sizeof(%xmm0)/sizeof(%rax)
+	.long	0xa548f3fc		# cld; rep movsq
+
+	jmp	.Lcommon_rbp_tail
+.size	ctr_xts_se_handler,.-ctr_xts_se_handler
+
+.type	ocb_se_handler,\@abi-omnipotent
+.align	16
+ocb_se_handler:
+	push	%rsi
+	push	%rdi
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+	pushfq
+	sub	\$64,%rsp
+
+	mov	120($context),%rax	# pull context->Rax
+	mov	248($context),%rbx	# pull context->Rip
+
+	mov	8($disp),%rsi		# disp->ImageBase
+	mov	56($disp),%r11		# disp->HandlerData
+
+	mov	0(%r11),%r10d		# HandlerData[0]
+	lea	(%rsi,%r10),%r10	# prologue lable
+	cmp	%r10,%rbx		# context->RipRip>=epilogue label
+	jae	.Lcommon_seh_tail
+
+	mov	8(%r11),%r10d		# HandlerData[2]
+	lea	(%rsi,%r10),%r10
+	cmp	%r10,%rbx		# context->Rip>=pop label
+	jae	.Locb_no_xmm
+
+	mov	152($context),%rax	# pull context->Rsp
+
+	lea	(%rax),%rsi		# %xmm save area
+	lea	512($context),%rdi	# & context.Xmm6
+	mov	\$20,%ecx		# 10*sizeof(%xmm0)/sizeof(%rax)
+	.long	0xa548f3fc		# cld; rep movsq
+	lea	0xa0+0x28(%rax),%rax
+
+.Locb_no_xmm:
+	mov	-8(%rax),%rbx
+	mov	-16(%rax),%rbp
+	mov	-24(%rax),%r12
+	mov	-32(%rax),%r13
+	mov	-40(%rax),%r14
+
+	mov	%rbx,144($context)	# restore context->Rbx
+	mov	%rbp,160($context)	# restore context->Rbp
+	mov	%r12,216($context)	# restore context->R12
+	mov	%r13,224($context)	# restore context->R13
+	mov	%r14,232($context)	# restore context->R14
+
+	jmp	.Lcommon_seh_tail
+.size	ocb_se_handler,.-ocb_se_handler
+___
+$code.=<<___;
+.type	cbc_se_handler,\@abi-omnipotent
+.align	16
+cbc_se_handler:
+	push	%rsi
+	push	%rdi
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+	pushfq
+	sub	\$64,%rsp
+
+	mov	152($context),%rax	# pull context->Rsp
+	mov	248($context),%rbx	# pull context->Rip
+
+	lea	.Lcbc_decrypt_bulk(%rip),%r10
+	cmp	%r10,%rbx		# context->Rip<"prologue" label
+	jb	.Lcommon_seh_tail
+
+	lea	.Lcbc_decrypt_body(%rip),%r10
+	cmp	%r10,%rbx		# context->RipRip>="epilogue" label
+	jae	.Lcommon_seh_tail
+
+	lea	16(%rax),%rsi		# %xmm save area
+	lea	512($context),%rdi	# &context.Xmm6
+	mov	\$20,%ecx		# 10*sizeof(%xmm0)/sizeof(%rax)
+	.long	0xa548f3fc		# cld; rep movsq
+
+.Lcommon_rbp_tail:
+	mov	160($context),%rax	# pull context->Rbp
+	mov	(%rax),%rbp		# restore saved %rbp
+	lea	8(%rax),%rax		# adjust stack pointer
+	mov	%rbp,160($context)	# restore context->Rbp
+	jmp	.Lcommon_seh_tail
+
+.Lrestore_cbc_rax:
+	mov	120($context),%rax
+
+.Lcommon_seh_tail:
+	mov	8(%rax),%rdi
+	mov	16(%rax),%rsi
+	mov	%rax,152($context)	# restore context->Rsp
+	mov	%rsi,168($context)	# restore context->Rsi
+	mov	%rdi,176($context)	# restore context->Rdi
+
+	mov	40($disp),%rdi		# disp->ContextRecord
+	mov	$context,%rsi		# context
+	mov	\$154,%ecx		# sizeof(CONTEXT)
+	.long	0xa548f3fc		# cld; rep movsq
+
+	mov	$disp,%rsi
+	xor	%rcx,%rcx		# arg1, UNW_FLAG_NHANDLER
+	mov	8(%rsi),%rdx		# arg2, disp->ImageBase
+	mov	0(%rsi),%r8		# arg3, disp->ControlPc
+	mov	16(%rsi),%r9		# arg4, disp->FunctionEntry
+	mov	40(%rsi),%r10		# disp->ContextRecord
+	lea	56(%rsi),%r11		# &disp->HandlerData
+	lea	24(%rsi),%r12		# &disp->EstablisherFrame
+	mov	%r10,32(%rsp)		# arg5
+	mov	%r11,40(%rsp)		# arg6
+	mov	%r12,48(%rsp)		# arg7
+	mov	%rcx,56(%rsp)		# arg8, (NULL)
+	call	*__imp_RtlVirtualUnwind(%rip)
+
+	mov	\$1,%eax		# ExceptionContinueSearch
+	add	\$64,%rsp
+	popfq
+	pop	%r15
+	pop	%r14
+	pop	%r13
+	pop	%r12
+	pop	%rbp
+	pop	%rbx
+	pop	%rdi
+	pop	%rsi
+	ret
+.size	cbc_se_handler,.-cbc_se_handler
+
+.section	.pdata
+.align	4
+___
+$code.=<<___ if ($PREFIX eq "aesni");
+	.rva	.LSEH_begin_aesni_ecb_encrypt
+	.rva	.LSEH_end_aesni_ecb_encrypt
+	.rva	.LSEH_info_ecb
+
+	.rva	.LSEH_begin_aesni_ccm64_encrypt_blocks
+	.rva	.LSEH_end_aesni_ccm64_encrypt_blocks
+	.rva	.LSEH_info_ccm64_enc
+
+	.rva	.LSEH_begin_aesni_ccm64_decrypt_blocks
+	.rva	.LSEH_end_aesni_ccm64_decrypt_blocks
+	.rva	.LSEH_info_ccm64_dec
+
+	.rva	.LSEH_begin_aesni_ctr32_encrypt_blocks
+	.rva	.LSEH_end_aesni_ctr32_encrypt_blocks
+	.rva	.LSEH_info_ctr32
+
+	.rva	.LSEH_begin_aesni_xts_encrypt
+	.rva	.LSEH_end_aesni_xts_encrypt
+	.rva	.LSEH_info_xts_enc
+
+	.rva	.LSEH_begin_aesni_xts_decrypt
+	.rva	.LSEH_end_aesni_xts_decrypt
+	.rva	.LSEH_info_xts_dec
+
+	.rva	.LSEH_begin_aesni_ocb_encrypt
+	.rva	.LSEH_end_aesni_ocb_encrypt
+	.rva	.LSEH_info_ocb_enc
+
+	.rva	.LSEH_begin_aesni_ocb_decrypt
+	.rva	.LSEH_end_aesni_ocb_decrypt
+	.rva	.LSEH_info_ocb_dec
+___
+$code.=<<___;
+	.rva	.LSEH_begin_${PREFIX}_cbc_encrypt
+	.rva	.LSEH_end_${PREFIX}_cbc_encrypt
+	.rva	.LSEH_info_cbc
+
+	.rva	${PREFIX}_set_decrypt_key
+	.rva	.LSEH_end_set_decrypt_key
+	.rva	.LSEH_info_key
+
+	.rva	${PREFIX}_set_encrypt_key
+	.rva	.LSEH_end_set_encrypt_key
+	.rva	.LSEH_info_key
+.section	.xdata
+.align	8
+___
+$code.=<<___ if ($PREFIX eq "aesni");
+.LSEH_info_ecb:
+	.byte	9,0,0,0
+	.rva	ecb_ccm64_se_handler
+	.rva	.Lecb_enc_body,.Lecb_enc_ret		# HandlerData[]
+.LSEH_info_ccm64_enc:
+	.byte	9,0,0,0
+	.rva	ecb_ccm64_se_handler
+	.rva	.Lccm64_enc_body,.Lccm64_enc_ret	# HandlerData[]
+.LSEH_info_ccm64_dec:
+	.byte	9,0,0,0
+	.rva	ecb_ccm64_se_handler
+	.rva	.Lccm64_dec_body,.Lccm64_dec_ret	# HandlerData[]
+.LSEH_info_ctr32:
+	.byte	9,0,0,0
+	.rva	ctr_xts_se_handler
+	.rva	.Lctr32_body,.Lctr32_epilogue		# HandlerData[]
+.LSEH_info_xts_enc:
+	.byte	9,0,0,0
+	.rva	ctr_xts_se_handler
+	.rva	.Lxts_enc_body,.Lxts_enc_epilogue	# HandlerData[]
+.LSEH_info_xts_dec:
+	.byte	9,0,0,0
+	.rva	ctr_xts_se_handler
+	.rva	.Lxts_dec_body,.Lxts_dec_epilogue	# HandlerData[]
+.LSEH_info_ocb_enc:
+	.byte	9,0,0,0
+	.rva	ocb_se_handler
+	.rva	.Locb_enc_body,.Locb_enc_epilogue	# HandlerData[]
+	.rva	.Locb_enc_pop
+	.long	0
+.LSEH_info_ocb_dec:
+	.byte	9,0,0,0
+	.rva	ocb_se_handler
+	.rva	.Locb_dec_body,.Locb_dec_epilogue	# HandlerData[]
+	.rva	.Locb_dec_pop
+	.long	0
+___
+$code.=<<___;
+.LSEH_info_cbc:
+	.byte	9,0,0,0
+	.rva	cbc_se_handler
+.LSEH_info_key:
+	.byte	0x01,0x04,0x01,0x00
+	.byte	0x04,0x02,0x00,0x00	# sub rsp,8
+___
+}
+
+sub rex {
+  local *opcode=shift;
+  my ($dst,$src)=@_;
+  my $rex=0;
+
+    $rex|=0x04			if($dst>=8);
+    $rex|=0x01			if($src>=8);
+    push @opcode,$rex|0x40	if($rex);
+}
+
+sub aesni {
+  my $line=shift;
+  my @opcode=(0x66);
+
+    if ($line=~/(aeskeygenassist)\s+\$([x0-9a-f]+),\s*%xmm([0-9]+),\s*%xmm([0-9]+)/) {
+	rex(\@opcode,$4,$3);
+	push @opcode,0x0f,0x3a,0xdf;
+	push @opcode,0xc0|($3&7)|(($4&7)<<3);	# ModR/M
+	my $c=$2;
+	push @opcode,$c=~/^0/?oct($c):$c;
+	return ".byte\t".join(',',@opcode);
+    }
+    elsif ($line=~/(aes[a-z]+)\s+%xmm([0-9]+),\s*%xmm([0-9]+)/) {
+	my %opcodelet = (
+		"aesimc" => 0xdb,
+		"aesenc" => 0xdc,	"aesenclast" => 0xdd,
+		"aesdec" => 0xde,	"aesdeclast" => 0xdf
+	);
+	return undef if (!defined($opcodelet{$1}));
+	rex(\@opcode,$3,$2);
+	push @opcode,0x0f,0x38,$opcodelet{$1};
+	push @opcode,0xc0|($2&7)|(($3&7)<<3);	# ModR/M
+	return ".byte\t".join(',',@opcode);
+    }
+    elsif ($line=~/(aes[a-z]+)\s+([0x1-9a-fA-F]*)\(%rsp\),\s*%xmm([0-9]+)/) {
+	my %opcodelet = (
+		"aesenc" => 0xdc,	"aesenclast" => 0xdd,
+		"aesdec" => 0xde,	"aesdeclast" => 0xdf
+	);
+	return undef if (!defined($opcodelet{$1}));
+	my $off = $2;
+	push @opcode,0x44 if ($3>=8);
+	push @opcode,0x0f,0x38,$opcodelet{$1};
+	push @opcode,0x44|(($3&7)<<3),0x24;	# ModR/M
+	push @opcode,($off=~/^0/?oct($off):$off)&0xff;
+	return ".byte\t".join(',',@opcode);
+    }
+    return $line;
+}
+
+sub movbe {
+	".byte	0x0f,0x38,0xf1,0x44,0x24,".shift;
+}
+
+$code =~ s/\`([^\`]*)\`/eval($1)/gem;
+$code =~ s/\b(aes.*%xmm[0-9]+).*$/aesni($1)/gem;
+#$code =~ s/\bmovbe\s+%eax/bswap %eax; mov %eax/gm;	# debugging artefact
+$code =~ s/\bmovbe\s+%eax,\s*([0-9]+)\(%rsp\)/movbe($1)/gem;
+
+print $code;
+
+close STDOUT;
diff --git a/crypto/aes/asm/aesp8-ppc.pl b/crypto/aes/asm/aesp8-ppc.pl
new file mode 100755
index 000000000..b7e92f653
--- /dev/null
+++ b/crypto/aes/asm/aesp8-ppc.pl
@@ -0,0 +1,3805 @@
+#! /usr/bin/env perl
+# Copyright 2014-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+#
+# ====================================================================
+# Written by Andy Polyakov  for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+#
+# This module implements support for AES instructions as per PowerISA
+# specification version 2.07, first implemented by POWER8 processor.
+# The module is endian-agnostic in sense that it supports both big-
+# and little-endian cases. Data alignment in parallelizable modes is
+# handled with VSX loads and stores, which implies MSR.VSX flag being
+# set. It should also be noted that ISA specification doesn't prohibit
+# alignment exceptions for these instructions on page boundaries.
+# Initially alignment was handled in pure AltiVec/VMX way [when data
+# is aligned programmatically, which in turn guarantees exception-
+# free execution], but it turned to hamper performance when vcipher
+# instructions are interleaved. It's reckoned that eventual
+# misalignment penalties at page boundaries are in average lower
+# than additional overhead in pure AltiVec approach.
+#
+# May 2016
+#
+# Add XTS subroutine, 9x on little- and 12x improvement on big-endian
+# systems were measured.
+#
+######################################################################
+# Current large-block performance in cycles per byte processed with
+# 128-bit key (less is better).
+#
+#		CBC en-/decrypt	CTR	XTS
+# POWER8[le]	3.96/0.72	0.74	1.1
+# POWER8[be]	3.75/0.65	0.66	1.0
+
+$flavour = shift;
+
+if ($flavour =~ /64/) {
+	$SIZE_T	=8;
+	$LRSAVE	=2*$SIZE_T;
+	$STU	="stdu";
+	$POP	="ld";
+	$PUSH	="std";
+	$UCMP	="cmpld";
+	$SHL	="sldi";
+} elsif ($flavour =~ /32/) {
+	$SIZE_T	=4;
+	$LRSAVE	=$SIZE_T;
+	$STU	="stwu";
+	$POP	="lwz";
+	$PUSH	="stw";
+	$UCMP	="cmplw";
+	$SHL	="slwi";
+} else { die "nonsense $flavour"; }
+
+$LITTLE_ENDIAN = ($flavour=~/le$/) ? $SIZE_T : 0;
+
+$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+( $xlate="${dir}ppc-xlate.pl" and -f $xlate ) or
+( $xlate="${dir}../../perlasm/ppc-xlate.pl" and -f $xlate) or
+die "can't locate ppc-xlate.pl";
+
+open STDOUT,"| $^X $xlate $flavour ".shift || die "can't call $xlate: $!";
+
+$FRAME=8*$SIZE_T;
+$prefix="aes_p8";
+
+$sp="r1";
+$vrsave="r12";
+
+#########################################################################
+{{{	# Key setup procedures						#
+my ($inp,$bits,$out,$ptr,$cnt,$rounds)=map("r$_",(3..8));
+my ($zero,$in0,$in1,$key,$rcon,$mask,$tmp)=map("v$_",(0..6));
+my ($stage,$outperm,$outmask,$outhead,$outtail)=map("v$_",(7..11));
+
+$code.=<<___;
+.machine	"any"
+
+.text
+
+.align	7
+rcon:
+.long	0x01000000, 0x01000000, 0x01000000, 0x01000000	?rev
+.long	0x1b000000, 0x1b000000, 0x1b000000, 0x1b000000	?rev
+.long	0x0d0e0f0c, 0x0d0e0f0c, 0x0d0e0f0c, 0x0d0e0f0c	?rev
+.long	0,0,0,0						?asis
+Lconsts:
+	mflr	r0
+	bcl	20,31,\$+4
+	mflr	$ptr	 #vvvvv "distance between . and rcon
+	addi	$ptr,$ptr,-0x48
+	mtlr	r0
+	blr
+	.long	0
+	.byte	0,12,0x14,0,0,0,0,0
+.asciz	"AES for PowerISA 2.07, CRYPTOGAMS by "
+
+.globl	.${prefix}_set_encrypt_key
+.align	5
+.${prefix}_set_encrypt_key:
+Lset_encrypt_key:
+	mflr		r11
+	$PUSH		r11,$LRSAVE($sp)
+
+	li		$ptr,-1
+	${UCMP}i	$inp,0
+	beq-		Lenc_key_abort		# if ($inp==0) return -1;
+	${UCMP}i	$out,0
+	beq-		Lenc_key_abort		# if ($out==0) return -1;
+	li		$ptr,-2
+	cmpwi		$bits,128
+	blt-		Lenc_key_abort
+	cmpwi		$bits,256
+	bgt-		Lenc_key_abort
+	andi.		r0,$bits,0x3f
+	bne-		Lenc_key_abort
+
+	lis		r0,0xfff0
+	mfspr		$vrsave,256
+	mtspr		256,r0
+
+	bl		Lconsts
+	mtlr		r11
+
+	neg		r9,$inp
+	lvx		$in0,0,$inp
+	addi		$inp,$inp,15		# 15 is not typo
+	lvsr		$key,0,r9		# borrow $key
+	li		r8,0x20
+	cmpwi		$bits,192
+	lvx		$in1,0,$inp
+	le?vspltisb	$mask,0x0f		# borrow $mask
+	lvx		$rcon,0,$ptr
+	le?vxor		$key,$key,$mask		# adjust for byte swap
+	lvx		$mask,r8,$ptr
+	addi		$ptr,$ptr,0x10
+	vperm		$in0,$in0,$in1,$key	# align [and byte swap in LE]
+	li		$cnt,8
+	vxor		$zero,$zero,$zero
+	mtctr		$cnt
+
+	?lvsr		$outperm,0,$out
+	vspltisb	$outmask,-1
+	lvx		$outhead,0,$out
+	?vperm		$outmask,$zero,$outmask,$outperm
+
+	blt		Loop128
+	addi		$inp,$inp,8
+	beq		L192
+	addi		$inp,$inp,8
+	b		L256
+
+.align	4
+Loop128:
+	vperm		$key,$in0,$in0,$mask	# rotate-n-splat
+	vsldoi		$tmp,$zero,$in0,12	# >>32
+	 vperm		$outtail,$in0,$in0,$outperm	# rotate
+	 vsel		$stage,$outhead,$outtail,$outmask
+	 vmr		$outhead,$outtail
+	vcipherlast	$key,$key,$rcon
+	 stvx		$stage,0,$out
+	 addi		$out,$out,16
+
+	vxor		$in0,$in0,$tmp
+	vsldoi		$tmp,$zero,$tmp,12	# >>32
+	vxor		$in0,$in0,$tmp
+	vsldoi		$tmp,$zero,$tmp,12	# >>32
+	vxor		$in0,$in0,$tmp
+	 vadduwm	$rcon,$rcon,$rcon
+	vxor		$in0,$in0,$key
+	bdnz		Loop128
+
+	lvx		$rcon,0,$ptr		# last two round keys
+
+	vperm		$key,$in0,$in0,$mask	# rotate-n-splat
+	vsldoi		$tmp,$zero,$in0,12	# >>32
+	 vperm		$outtail,$in0,$in0,$outperm	# rotate
+	 vsel		$stage,$outhead,$outtail,$outmask
+	 vmr		$outhead,$outtail
+	vcipherlast	$key,$key,$rcon
+	 stvx		$stage,0,$out
+	 addi		$out,$out,16
+
+	vxor		$in0,$in0,$tmp
+	vsldoi		$tmp,$zero,$tmp,12	# >>32
+	vxor		$in0,$in0,$tmp
+	vsldoi		$tmp,$zero,$tmp,12	# >>32
+	vxor		$in0,$in0,$tmp
+	 vadduwm	$rcon,$rcon,$rcon
+	vxor		$in0,$in0,$key
+
+	vperm		$key,$in0,$in0,$mask	# rotate-n-splat
+	vsldoi		$tmp,$zero,$in0,12	# >>32
+	 vperm		$outtail,$in0,$in0,$outperm	# rotate
+	 vsel		$stage,$outhead,$outtail,$outmask
+	 vmr		$outhead,$outtail
+	vcipherlast	$key,$key,$rcon
+	 stvx		$stage,0,$out
+	 addi		$out,$out,16
+
+	vxor		$in0,$in0,$tmp
+	vsldoi		$tmp,$zero,$tmp,12	# >>32
+	vxor		$in0,$in0,$tmp
+	vsldoi		$tmp,$zero,$tmp,12	# >>32
+	vxor		$in0,$in0,$tmp
+	vxor		$in0,$in0,$key
+	 vperm		$outtail,$in0,$in0,$outperm	# rotate
+	 vsel		$stage,$outhead,$outtail,$outmask
+	 vmr		$outhead,$outtail
+	 stvx		$stage,0,$out
+
+	addi		$inp,$out,15		# 15 is not typo
+	addi		$out,$out,0x50
+
+	li		$rounds,10
+	b		Ldone
+
+.align	4
+L192:
+	lvx		$tmp,0,$inp
+	li		$cnt,4
+	 vperm		$outtail,$in0,$in0,$outperm	# rotate
+	 vsel		$stage,$outhead,$outtail,$outmask
+	 vmr		$outhead,$outtail
+	 stvx		$stage,0,$out
+	 addi		$out,$out,16
+	vperm		$in1,$in1,$tmp,$key	# align [and byte swap in LE]
+	vspltisb	$key,8			# borrow $key
+	mtctr		$cnt
+	vsububm		$mask,$mask,$key	# adjust the mask
+
+Loop192:
+	vperm		$key,$in1,$in1,$mask	# roate-n-splat
+	vsldoi		$tmp,$zero,$in0,12	# >>32
+	vcipherlast	$key,$key,$rcon
+
+	vxor		$in0,$in0,$tmp
+	vsldoi		$tmp,$zero,$tmp,12	# >>32
+	vxor		$in0,$in0,$tmp
+	vsldoi		$tmp,$zero,$tmp,12	# >>32
+	vxor		$in0,$in0,$tmp
+
+	 vsldoi		$stage,$zero,$in1,8
+	vspltw		$tmp,$in0,3
+	vxor		$tmp,$tmp,$in1
+	vsldoi		$in1,$zero,$in1,12	# >>32
+	 vadduwm	$rcon,$rcon,$rcon
+	vxor		$in1,$in1,$tmp
+	vxor		$in0,$in0,$key
+	vxor		$in1,$in1,$key
+	 vsldoi		$stage,$stage,$in0,8
+
+	vperm		$key,$in1,$in1,$mask	# rotate-n-splat
+	vsldoi		$tmp,$zero,$in0,12	# >>32
+	 vperm		$outtail,$stage,$stage,$outperm	# rotate
+	 vsel		$stage,$outhead,$outtail,$outmask
+	 vmr		$outhead,$outtail
+	vcipherlast	$key,$key,$rcon
+	 stvx		$stage,0,$out
+	 addi		$out,$out,16
+
+	 vsldoi		$stage,$in0,$in1,8
+	vxor		$in0,$in0,$tmp
+	vsldoi		$tmp,$zero,$tmp,12	# >>32
+	 vperm		$outtail,$stage,$stage,$outperm	# rotate
+	 vsel		$stage,$outhead,$outtail,$outmask
+	 vmr		$outhead,$outtail
+	vxor		$in0,$in0,$tmp
+	vsldoi		$tmp,$zero,$tmp,12	# >>32
+	vxor		$in0,$in0,$tmp
+	 stvx		$stage,0,$out
+	 addi		$out,$out,16
+
+	vspltw		$tmp,$in0,3
+	vxor		$tmp,$tmp,$in1
+	vsldoi		$in1,$zero,$in1,12	# >>32
+	 vadduwm	$rcon,$rcon,$rcon
+	vxor		$in1,$in1,$tmp
+	vxor		$in0,$in0,$key
+	vxor		$in1,$in1,$key
+	 vperm		$outtail,$in0,$in0,$outperm	# rotate
+	 vsel		$stage,$outhead,$outtail,$outmask
+	 vmr		$outhead,$outtail
+	 stvx		$stage,0,$out
+	 addi		$inp,$out,15		# 15 is not typo
+	 addi		$out,$out,16
+	bdnz		Loop192
+
+	li		$rounds,12
+	addi		$out,$out,0x20
+	b		Ldone
+
+.align	4
+L256:
+	lvx		$tmp,0,$inp
+	li		$cnt,7
+	li		$rounds,14
+	 vperm		$outtail,$in0,$in0,$outperm	# rotate
+	 vsel		$stage,$outhead,$outtail,$outmask
+	 vmr		$outhead,$outtail
+	 stvx		$stage,0,$out
+	 addi		$out,$out,16
+	vperm		$in1,$in1,$tmp,$key	# align [and byte swap in LE]
+	mtctr		$cnt
+
+Loop256:
+	vperm		$key,$in1,$in1,$mask	# rotate-n-splat
+	vsldoi		$tmp,$zero,$in0,12	# >>32
+	 vperm		$outtail,$in1,$in1,$outperm	# rotate
+	 vsel		$stage,$outhead,$outtail,$outmask
+	 vmr		$outhead,$outtail
+	vcipherlast	$key,$key,$rcon
+	 stvx		$stage,0,$out
+	 addi		$out,$out,16
+
+	vxor		$in0,$in0,$tmp
+	vsldoi		$tmp,$zero,$tmp,12	# >>32
+	vxor		$in0,$in0,$tmp
+	vsldoi		$tmp,$zero,$tmp,12	# >>32
+	vxor		$in0,$in0,$tmp
+	 vadduwm	$rcon,$rcon,$rcon
+	vxor		$in0,$in0,$key
+	 vperm		$outtail,$in0,$in0,$outperm	# rotate
+	 vsel		$stage,$outhead,$outtail,$outmask
+	 vmr		$outhead,$outtail
+	 stvx		$stage,0,$out
+	 addi		$inp,$out,15		# 15 is not typo
+	 addi		$out,$out,16
+	bdz		Ldone
+
+	vspltw		$key,$in0,3		# just splat
+	vsldoi		$tmp,$zero,$in1,12	# >>32
+	vsbox		$key,$key
+
+	vxor		$in1,$in1,$tmp
+	vsldoi		$tmp,$zero,$tmp,12	# >>32
+	vxor		$in1,$in1,$tmp
+	vsldoi		$tmp,$zero,$tmp,12	# >>32
+	vxor		$in1,$in1,$tmp
+
+	vxor		$in1,$in1,$key
+	b		Loop256
+
+.align	4
+Ldone:
+	lvx		$in1,0,$inp		# redundant in aligned case
+	vsel		$in1,$outhead,$in1,$outmask
+	stvx		$in1,0,$inp
+	li		$ptr,0
+	mtspr		256,$vrsave
+	stw		$rounds,0($out)
+
+Lenc_key_abort:
+	mr		r3,$ptr
+	blr
+	.long		0
+	.byte		0,12,0x14,1,0,0,3,0
+	.long		0
+.size	.${prefix}_set_encrypt_key,.-.${prefix}_set_encrypt_key
+
+.globl	.${prefix}_set_decrypt_key
+.align	5
+.${prefix}_set_decrypt_key:
+	$STU		$sp,-$FRAME($sp)
+	mflr		r10
+	$PUSH		r10,$FRAME+$LRSAVE($sp)
+	bl		Lset_encrypt_key
+	mtlr		r10
+
+	cmpwi		r3,0
+	bne-		Ldec_key_abort
+
+	slwi		$cnt,$rounds,4
+	subi		$inp,$out,240		# first round key
+	srwi		$rounds,$rounds,1
+	add		$out,$inp,$cnt		# last round key
+	mtctr		$rounds
+
+Ldeckey:
+	lwz		r0, 0($inp)
+	lwz		r6, 4($inp)
+	lwz		r7, 8($inp)
+	lwz		r8, 12($inp)
+	addi		$inp,$inp,16
+	lwz		r9, 0($out)
+	lwz		r10,4($out)
+	lwz		r11,8($out)
+	lwz		r12,12($out)
+	stw		r0, 0($out)
+	stw		r6, 4($out)
+	stw		r7, 8($out)
+	stw		r8, 12($out)
+	subi		$out,$out,16
+	stw		r9, -16($inp)
+	stw		r10,-12($inp)
+	stw		r11,-8($inp)
+	stw		r12,-4($inp)
+	bdnz		Ldeckey
+
+	xor		r3,r3,r3		# return value
+Ldec_key_abort:
+	addi		$sp,$sp,$FRAME
+	blr
+	.long		0
+	.byte		0,12,4,1,0x80,0,3,0
+	.long		0
+.size	.${prefix}_set_decrypt_key,.-.${prefix}_set_decrypt_key
+___
+}}}
+#########################################################################
+{{{	# Single block en- and decrypt procedures			#
+sub gen_block () {
+my $dir = shift;
+my $n   = $dir eq "de" ? "n" : "";
+my ($inp,$out,$key,$rounds,$idx)=map("r$_",(3..7));
+
+$code.=<<___;
+.globl	.${prefix}_${dir}crypt
+.align	5
+.${prefix}_${dir}crypt:
+	lwz		$rounds,240($key)
+	lis		r0,0xfc00
+	mfspr		$vrsave,256
+	li		$idx,15			# 15 is not typo
+	mtspr		256,r0
+
+	lvx		v0,0,$inp
+	neg		r11,$out
+	lvx		v1,$idx,$inp
+	lvsl		v2,0,$inp		# inpperm
+	le?vspltisb	v4,0x0f
+	?lvsl		v3,0,r11		# outperm
+	le?vxor		v2,v2,v4
+	li		$idx,16
+	vperm		v0,v0,v1,v2		# align [and byte swap in LE]
+	lvx		v1,0,$key
+	?lvsl		v5,0,$key		# keyperm
+	srwi		$rounds,$rounds,1
+	lvx		v2,$idx,$key
+	addi		$idx,$idx,16
+	subi		$rounds,$rounds,1
+	?vperm		v1,v1,v2,v5		# align round key
+
+	vxor		v0,v0,v1
+	lvx		v1,$idx,$key
+	addi		$idx,$idx,16
+	mtctr		$rounds
+
+Loop_${dir}c:
+	?vperm		v2,v2,v1,v5
+	v${n}cipher	v0,v0,v2
+	lvx		v2,$idx,$key
+	addi		$idx,$idx,16
+	?vperm		v1,v1,v2,v5
+	v${n}cipher	v0,v0,v1
+	lvx		v1,$idx,$key
+	addi		$idx,$idx,16
+	bdnz		Loop_${dir}c
+
+	?vperm		v2,v2,v1,v5
+	v${n}cipher	v0,v0,v2
+	lvx		v2,$idx,$key
+	?vperm		v1,v1,v2,v5
+	v${n}cipherlast	v0,v0,v1
+
+	vspltisb	v2,-1
+	vxor		v1,v1,v1
+	li		$idx,15			# 15 is not typo
+	?vperm		v2,v1,v2,v3		# outmask
+	le?vxor		v3,v3,v4
+	lvx		v1,0,$out		# outhead
+	vperm		v0,v0,v0,v3		# rotate [and byte swap in LE]
+	vsel		v1,v1,v0,v2
+	lvx		v4,$idx,$out
+	stvx		v1,0,$out
+	vsel		v0,v0,v4,v2
+	stvx		v0,$idx,$out
+
+	mtspr		256,$vrsave
+	blr
+	.long		0
+	.byte		0,12,0x14,0,0,0,3,0
+	.long		0
+.size	.${prefix}_${dir}crypt,.-.${prefix}_${dir}crypt
+___
+}
+&gen_block("en");
+&gen_block("de");
+}}}
+#########################################################################
+{{{	# CBC en- and decrypt procedures				#
+my ($inp,$out,$len,$key,$ivp,$enc,$rounds,$idx)=map("r$_",(3..10));
+my ($rndkey0,$rndkey1,$inout,$tmp)=		map("v$_",(0..3));
+my ($ivec,$inptail,$inpperm,$outhead,$outperm,$outmask,$keyperm)=
+						map("v$_",(4..10));
+$code.=<<___;
+.globl	.${prefix}_cbc_encrypt
+.align	5
+.${prefix}_cbc_encrypt:
+	${UCMP}i	$len,16
+	bltlr-
+
+	cmpwi		$enc,0			# test direction
+	lis		r0,0xffe0
+	mfspr		$vrsave,256
+	mtspr		256,r0
+
+	li		$idx,15
+	vxor		$rndkey0,$rndkey0,$rndkey0
+	le?vspltisb	$tmp,0x0f
+
+	lvx		$ivec,0,$ivp		# load [unaligned] iv
+	lvsl		$inpperm,0,$ivp
+	lvx		$inptail,$idx,$ivp
+	le?vxor		$inpperm,$inpperm,$tmp
+	vperm		$ivec,$ivec,$inptail,$inpperm
+
+	neg		r11,$inp
+	?lvsl		$keyperm,0,$key		# prepare for unaligned key
+	lwz		$rounds,240($key)
+
+	lvsr		$inpperm,0,r11		# prepare for unaligned load
+	lvx		$inptail,0,$inp
+	addi		$inp,$inp,15		# 15 is not typo
+	le?vxor		$inpperm,$inpperm,$tmp
+
+	?lvsr		$outperm,0,$out		# prepare for unaligned store
+	vspltisb	$outmask,-1
+	lvx		$outhead,0,$out
+	?vperm		$outmask,$rndkey0,$outmask,$outperm
+	le?vxor		$outperm,$outperm,$tmp
+
+	srwi		$rounds,$rounds,1
+	li		$idx,16
+	subi		$rounds,$rounds,1
+	beq		Lcbc_dec
+
+Lcbc_enc:
+	vmr		$inout,$inptail
+	lvx		$inptail,0,$inp
+	addi		$inp,$inp,16
+	mtctr		$rounds
+	subi		$len,$len,16		# len-=16
+
+	lvx		$rndkey0,0,$key
+	 vperm		$inout,$inout,$inptail,$inpperm
+	lvx		$rndkey1,$idx,$key
+	addi		$idx,$idx,16
+	?vperm		$rndkey0,$rndkey0,$rndkey1,$keyperm
+	vxor		$inout,$inout,$rndkey0
+	lvx		$rndkey0,$idx,$key
+	addi		$idx,$idx,16
+	vxor		$inout,$inout,$ivec
+
+Loop_cbc_enc:
+	?vperm		$rndkey1,$rndkey1,$rndkey0,$keyperm
+	vcipher		$inout,$inout,$rndkey1
+	lvx		$rndkey1,$idx,$key
+	addi		$idx,$idx,16
+	?vperm		$rndkey0,$rndkey0,$rndkey1,$keyperm
+	vcipher		$inout,$inout,$rndkey0
+	lvx		$rndkey0,$idx,$key
+	addi		$idx,$idx,16
+	bdnz		Loop_cbc_enc
+
+	?vperm		$rndkey1,$rndkey1,$rndkey0,$keyperm
+	vcipher		$inout,$inout,$rndkey1
+	lvx		$rndkey1,$idx,$key
+	li		$idx,16
+	?vperm		$rndkey0,$rndkey0,$rndkey1,$keyperm
+	vcipherlast	$ivec,$inout,$rndkey0
+	${UCMP}i	$len,16
+
+	vperm		$tmp,$ivec,$ivec,$outperm
+	vsel		$inout,$outhead,$tmp,$outmask
+	vmr		$outhead,$tmp
+	stvx		$inout,0,$out
+	addi		$out,$out,16
+	bge		Lcbc_enc
+
+	b		Lcbc_done
+
+.align	4
+Lcbc_dec:
+	${UCMP}i	$len,128
+	bge		_aesp8_cbc_decrypt8x
+	vmr		$tmp,$inptail
+	lvx		$inptail,0,$inp
+	addi		$inp,$inp,16
+	mtctr		$rounds
+	subi		$len,$len,16		# len-=16
+
+	lvx		$rndkey0,0,$key
+	 vperm		$tmp,$tmp,$inptail,$inpperm
+	lvx		$rndkey1,$idx,$key
+	addi		$idx,$idx,16
+	?vperm		$rndkey0,$rndkey0,$rndkey1,$keyperm
+	vxor		$inout,$tmp,$rndkey0
+	lvx		$rndkey0,$idx,$key
+	addi		$idx,$idx,16
+
+Loop_cbc_dec:
+	?vperm		$rndkey1,$rndkey1,$rndkey0,$keyperm
+	vncipher	$inout,$inout,$rndkey1
+	lvx		$rndkey1,$idx,$key
+	addi		$idx,$idx,16
+	?vperm		$rndkey0,$rndkey0,$rndkey1,$keyperm
+	vncipher	$inout,$inout,$rndkey0
+	lvx		$rndkey0,$idx,$key
+	addi		$idx,$idx,16
+	bdnz		Loop_cbc_dec
+
+	?vperm		$rndkey1,$rndkey1,$rndkey0,$keyperm
+	vncipher	$inout,$inout,$rndkey1
+	lvx		$rndkey1,$idx,$key
+	li		$idx,16
+	?vperm		$rndkey0,$rndkey0,$rndkey1,$keyperm
+	vncipherlast	$inout,$inout,$rndkey0
+	${UCMP}i	$len,16
+
+	vxor		$inout,$inout,$ivec
+	vmr		$ivec,$tmp
+	vperm		$tmp,$inout,$inout,$outperm
+	vsel		$inout,$outhead,$tmp,$outmask
+	vmr		$outhead,$tmp
+	stvx		$inout,0,$out
+	addi		$out,$out,16
+	bge		Lcbc_dec
+
+Lcbc_done:
+	addi		$out,$out,-1
+	lvx		$inout,0,$out		# redundant in aligned case
+	vsel		$inout,$outhead,$inout,$outmask
+	stvx		$inout,0,$out
+
+	neg		$enc,$ivp		# write [unaligned] iv
+	li		$idx,15			# 15 is not typo
+	vxor		$rndkey0,$rndkey0,$rndkey0
+	vspltisb	$outmask,-1
+	le?vspltisb	$tmp,0x0f
+	?lvsl		$outperm,0,$enc
+	?vperm		$outmask,$rndkey0,$outmask,$outperm
+	le?vxor		$outperm,$outperm,$tmp
+	lvx		$outhead,0,$ivp
+	vperm		$ivec,$ivec,$ivec,$outperm
+	vsel		$inout,$outhead,$ivec,$outmask
+	lvx		$inptail,$idx,$ivp
+	stvx		$inout,0,$ivp
+	vsel		$inout,$ivec,$inptail,$outmask
+	stvx		$inout,$idx,$ivp
+
+	mtspr		256,$vrsave
+	blr
+	.long		0
+	.byte		0,12,0x14,0,0,0,6,0
+	.long		0
+___
+#########################################################################
+{{	# Optimized CBC decrypt procedure				#
+my $key_="r11";
+my ($x00,$x10,$x20,$x30,$x40,$x50,$x60,$x70)=map("r$_",(0,8,26..31));
+    $x00=0 if ($flavour =~ /osx/);
+my ($in0, $in1, $in2, $in3, $in4, $in5, $in6, $in7 )=map("v$_",(0..3,10..13));
+my ($out0,$out1,$out2,$out3,$out4,$out5,$out6,$out7)=map("v$_",(14..21));
+my $rndkey0="v23";	# v24-v25 rotating buffer for first found keys
+			# v26-v31 last 6 round keys
+my ($tmp,$keyperm)=($in3,$in4);	# aliases with "caller", redundant assignment
+
+$code.=<<___;
+.align	5
+_aesp8_cbc_decrypt8x:
+	$STU		$sp,-`($FRAME+21*16+6*$SIZE_T)`($sp)
+	li		r10,`$FRAME+8*16+15`
+	li		r11,`$FRAME+8*16+31`
+	stvx		v20,r10,$sp		# ABI says so
+	addi		r10,r10,32
+	stvx		v21,r11,$sp
+	addi		r11,r11,32
+	stvx		v22,r10,$sp
+	addi		r10,r10,32
+	stvx		v23,r11,$sp
+	addi		r11,r11,32
+	stvx		v24,r10,$sp
+	addi		r10,r10,32
+	stvx		v25,r11,$sp
+	addi		r11,r11,32
+	stvx		v26,r10,$sp
+	addi		r10,r10,32
+	stvx		v27,r11,$sp
+	addi		r11,r11,32
+	stvx		v28,r10,$sp
+	addi		r10,r10,32
+	stvx		v29,r11,$sp
+	addi		r11,r11,32
+	stvx		v30,r10,$sp
+	stvx		v31,r11,$sp
+	li		r0,-1
+	stw		$vrsave,`$FRAME+21*16-4`($sp)	# save vrsave
+	li		$x10,0x10
+	$PUSH		r26,`$FRAME+21*16+0*$SIZE_T`($sp)
+	li		$x20,0x20
+	$PUSH		r27,`$FRAME+21*16+1*$SIZE_T`($sp)
+	li		$x30,0x30
+	$PUSH		r28,`$FRAME+21*16+2*$SIZE_T`($sp)
+	li		$x40,0x40
+	$PUSH		r29,`$FRAME+21*16+3*$SIZE_T`($sp)
+	li		$x50,0x50
+	$PUSH		r30,`$FRAME+21*16+4*$SIZE_T`($sp)
+	li		$x60,0x60
+	$PUSH		r31,`$FRAME+21*16+5*$SIZE_T`($sp)
+	li		$x70,0x70
+	mtspr		256,r0
+
+	subi		$rounds,$rounds,3	# -4 in total
+	subi		$len,$len,128		# bias
+
+	lvx		$rndkey0,$x00,$key	# load key schedule
+	lvx		v30,$x10,$key
+	addi		$key,$key,0x20
+	lvx		v31,$x00,$key
+	?vperm		$rndkey0,$rndkey0,v30,$keyperm
+	addi		$key_,$sp,$FRAME+15
+	mtctr		$rounds
+
+Load_cbc_dec_key:
+	?vperm		v24,v30,v31,$keyperm
+	lvx		v30,$x10,$key
+	addi		$key,$key,0x20
+	stvx		v24,$x00,$key_		# off-load round[1]
+	?vperm		v25,v31,v30,$keyperm
+	lvx		v31,$x00,$key
+	stvx		v25,$x10,$key_		# off-load round[2]
+	addi		$key_,$key_,0x20
+	bdnz		Load_cbc_dec_key
+
+	lvx		v26,$x10,$key
+	?vperm		v24,v30,v31,$keyperm
+	lvx		v27,$x20,$key
+	stvx		v24,$x00,$key_		# off-load round[3]
+	?vperm		v25,v31,v26,$keyperm
+	lvx		v28,$x30,$key
+	stvx		v25,$x10,$key_		# off-load round[4]
+	addi		$key_,$sp,$FRAME+15	# rewind $key_
+	?vperm		v26,v26,v27,$keyperm
+	lvx		v29,$x40,$key
+	?vperm		v27,v27,v28,$keyperm
+	lvx		v30,$x50,$key
+	?vperm		v28,v28,v29,$keyperm
+	lvx		v31,$x60,$key
+	?vperm		v29,v29,v30,$keyperm
+	lvx		$out0,$x70,$key		# borrow $out0
+	?vperm		v30,v30,v31,$keyperm
+	lvx		v24,$x00,$key_		# pre-load round[1]
+	?vperm		v31,v31,$out0,$keyperm
+	lvx		v25,$x10,$key_		# pre-load round[2]
+
+	#lvx		$inptail,0,$inp		# "caller" already did this
+	#addi		$inp,$inp,15		# 15 is not typo
+	subi		$inp,$inp,15		# undo "caller"
+
+	 le?li		$idx,8
+	lvx_u		$in0,$x00,$inp		# load first 8 "words"
+	 le?lvsl	$inpperm,0,$idx
+	 le?vspltisb	$tmp,0x0f
+	lvx_u		$in1,$x10,$inp
+	 le?vxor	$inpperm,$inpperm,$tmp	# transform for lvx_u/stvx_u
+	lvx_u		$in2,$x20,$inp
+	 le?vperm	$in0,$in0,$in0,$inpperm
+	lvx_u		$in3,$x30,$inp
+	 le?vperm	$in1,$in1,$in1,$inpperm
+	lvx_u		$in4,$x40,$inp
+	 le?vperm	$in2,$in2,$in2,$inpperm
+	vxor		$out0,$in0,$rndkey0
+	lvx_u		$in5,$x50,$inp
+	 le?vperm	$in3,$in3,$in3,$inpperm
+	vxor		$out1,$in1,$rndkey0
+	lvx_u		$in6,$x60,$inp
+	 le?vperm	$in4,$in4,$in4,$inpperm
+	vxor		$out2,$in2,$rndkey0
+	lvx_u		$in7,$x70,$inp
+	addi		$inp,$inp,0x80
+	 le?vperm	$in5,$in5,$in5,$inpperm
+	vxor		$out3,$in3,$rndkey0
+	 le?vperm	$in6,$in6,$in6,$inpperm
+	vxor		$out4,$in4,$rndkey0
+	 le?vperm	$in7,$in7,$in7,$inpperm
+	vxor		$out5,$in5,$rndkey0
+	vxor		$out6,$in6,$rndkey0
+	vxor		$out7,$in7,$rndkey0
+
+	mtctr		$rounds
+	b		Loop_cbc_dec8x
+.align	5
+Loop_cbc_dec8x:
+	vncipher	$out0,$out0,v24
+	vncipher	$out1,$out1,v24
+	vncipher	$out2,$out2,v24
+	vncipher	$out3,$out3,v24
+	vncipher	$out4,$out4,v24
+	vncipher	$out5,$out5,v24
+	vncipher	$out6,$out6,v24
+	vncipher	$out7,$out7,v24
+	lvx		v24,$x20,$key_		# round[3]
+	addi		$key_,$key_,0x20
+
+	vncipher	$out0,$out0,v25
+	vncipher	$out1,$out1,v25
+	vncipher	$out2,$out2,v25
+	vncipher	$out3,$out3,v25
+	vncipher	$out4,$out4,v25
+	vncipher	$out5,$out5,v25
+	vncipher	$out6,$out6,v25
+	vncipher	$out7,$out7,v25
+	lvx		v25,$x10,$key_		# round[4]
+	bdnz		Loop_cbc_dec8x
+
+	subic		$len,$len,128		# $len-=128
+	vncipher	$out0,$out0,v24
+	vncipher	$out1,$out1,v24
+	vncipher	$out2,$out2,v24
+	vncipher	$out3,$out3,v24
+	vncipher	$out4,$out4,v24
+	vncipher	$out5,$out5,v24
+	vncipher	$out6,$out6,v24
+	vncipher	$out7,$out7,v24
+
+	subfe.		r0,r0,r0		# borrow?-1:0
+	vncipher	$out0,$out0,v25
+	vncipher	$out1,$out1,v25
+	vncipher	$out2,$out2,v25
+	vncipher	$out3,$out3,v25
+	vncipher	$out4,$out4,v25
+	vncipher	$out5,$out5,v25
+	vncipher	$out6,$out6,v25
+	vncipher	$out7,$out7,v25
+
+	and		r0,r0,$len
+	vncipher	$out0,$out0,v26
+	vncipher	$out1,$out1,v26
+	vncipher	$out2,$out2,v26
+	vncipher	$out3,$out3,v26
+	vncipher	$out4,$out4,v26
+	vncipher	$out5,$out5,v26
+	vncipher	$out6,$out6,v26
+	vncipher	$out7,$out7,v26
+
+	add		$inp,$inp,r0		# $inp is adjusted in such
+						# way that at exit from the
+						# loop inX-in7 are loaded
+						# with last "words"
+	vncipher	$out0,$out0,v27
+	vncipher	$out1,$out1,v27
+	vncipher	$out2,$out2,v27
+	vncipher	$out3,$out3,v27
+	vncipher	$out4,$out4,v27
+	vncipher	$out5,$out5,v27
+	vncipher	$out6,$out6,v27
+	vncipher	$out7,$out7,v27
+
+	addi		$key_,$sp,$FRAME+15	# rewind $key_
+	vncipher	$out0,$out0,v28
+	vncipher	$out1,$out1,v28
+	vncipher	$out2,$out2,v28
+	vncipher	$out3,$out3,v28
+	vncipher	$out4,$out4,v28
+	vncipher	$out5,$out5,v28
+	vncipher	$out6,$out6,v28
+	vncipher	$out7,$out7,v28
+	lvx		v24,$x00,$key_		# re-pre-load round[1]
+
+	vncipher	$out0,$out0,v29
+	vncipher	$out1,$out1,v29
+	vncipher	$out2,$out2,v29
+	vncipher	$out3,$out3,v29
+	vncipher	$out4,$out4,v29
+	vncipher	$out5,$out5,v29
+	vncipher	$out6,$out6,v29
+	vncipher	$out7,$out7,v29
+	lvx		v25,$x10,$key_		# re-pre-load round[2]
+
+	vncipher	$out0,$out0,v30
+	 vxor		$ivec,$ivec,v31		# xor with last round key
+	vncipher	$out1,$out1,v30
+	 vxor		$in0,$in0,v31
+	vncipher	$out2,$out2,v30
+	 vxor		$in1,$in1,v31
+	vncipher	$out3,$out3,v30
+	 vxor		$in2,$in2,v31
+	vncipher	$out4,$out4,v30
+	 vxor		$in3,$in3,v31
+	vncipher	$out5,$out5,v30
+	 vxor		$in4,$in4,v31
+	vncipher	$out6,$out6,v30
+	 vxor		$in5,$in5,v31
+	vncipher	$out7,$out7,v30
+	 vxor		$in6,$in6,v31
+
+	vncipherlast	$out0,$out0,$ivec
+	vncipherlast	$out1,$out1,$in0
+	 lvx_u		$in0,$x00,$inp		# load next input block
+	vncipherlast	$out2,$out2,$in1
+	 lvx_u		$in1,$x10,$inp
+	vncipherlast	$out3,$out3,$in2
+	 le?vperm	$in0,$in0,$in0,$inpperm
+	 lvx_u		$in2,$x20,$inp
+	vncipherlast	$out4,$out4,$in3
+	 le?vperm	$in1,$in1,$in1,$inpperm
+	 lvx_u		$in3,$x30,$inp
+	vncipherlast	$out5,$out5,$in4
+	 le?vperm	$in2,$in2,$in2,$inpperm
+	 lvx_u		$in4,$x40,$inp
+	vncipherlast	$out6,$out6,$in5
+	 le?vperm	$in3,$in3,$in3,$inpperm
+	 lvx_u		$in5,$x50,$inp
+	vncipherlast	$out7,$out7,$in6
+	 le?vperm	$in4,$in4,$in4,$inpperm
+	 lvx_u		$in6,$x60,$inp
+	vmr		$ivec,$in7
+	 le?vperm	$in5,$in5,$in5,$inpperm
+	 lvx_u		$in7,$x70,$inp
+	 addi		$inp,$inp,0x80
+
+	le?vperm	$out0,$out0,$out0,$inpperm
+	le?vperm	$out1,$out1,$out1,$inpperm
+	stvx_u		$out0,$x00,$out
+	 le?vperm	$in6,$in6,$in6,$inpperm
+	 vxor		$out0,$in0,$rndkey0
+	le?vperm	$out2,$out2,$out2,$inpperm
+	stvx_u		$out1,$x10,$out
+	 le?vperm	$in7,$in7,$in7,$inpperm
+	 vxor		$out1,$in1,$rndkey0
+	le?vperm	$out3,$out3,$out3,$inpperm
+	stvx_u		$out2,$x20,$out
+	 vxor		$out2,$in2,$rndkey0
+	le?vperm	$out4,$out4,$out4,$inpperm
+	stvx_u		$out3,$x30,$out
+	 vxor		$out3,$in3,$rndkey0
+	le?vperm	$out5,$out5,$out5,$inpperm
+	stvx_u		$out4,$x40,$out
+	 vxor		$out4,$in4,$rndkey0
+	le?vperm	$out6,$out6,$out6,$inpperm
+	stvx_u		$out5,$x50,$out
+	 vxor		$out5,$in5,$rndkey0
+	le?vperm	$out7,$out7,$out7,$inpperm
+	stvx_u		$out6,$x60,$out
+	 vxor		$out6,$in6,$rndkey0
+	stvx_u		$out7,$x70,$out
+	addi		$out,$out,0x80
+	 vxor		$out7,$in7,$rndkey0
+
+	mtctr		$rounds
+	beq		Loop_cbc_dec8x		# did $len-=128 borrow?
+
+	addic.		$len,$len,128
+	beq		Lcbc_dec8x_done
+	nop
+	nop
+
+Loop_cbc_dec8x_tail:				# up to 7 "words" tail...
+	vncipher	$out1,$out1,v24
+	vncipher	$out2,$out2,v24
+	vncipher	$out3,$out3,v24
+	vncipher	$out4,$out4,v24
+	vncipher	$out5,$out5,v24
+	vncipher	$out6,$out6,v24
+	vncipher	$out7,$out7,v24
+	lvx		v24,$x20,$key_		# round[3]
+	addi		$key_,$key_,0x20
+
+	vncipher	$out1,$out1,v25
+	vncipher	$out2,$out2,v25
+	vncipher	$out3,$out3,v25
+	vncipher	$out4,$out4,v25
+	vncipher	$out5,$out5,v25
+	vncipher	$out6,$out6,v25
+	vncipher	$out7,$out7,v25
+	lvx		v25,$x10,$key_		# round[4]
+	bdnz		Loop_cbc_dec8x_tail
+
+	vncipher	$out1,$out1,v24
+	vncipher	$out2,$out2,v24
+	vncipher	$out3,$out3,v24
+	vncipher	$out4,$out4,v24
+	vncipher	$out5,$out5,v24
+	vncipher	$out6,$out6,v24
+	vncipher	$out7,$out7,v24
+
+	vncipher	$out1,$out1,v25
+	vncipher	$out2,$out2,v25
+	vncipher	$out3,$out3,v25
+	vncipher	$out4,$out4,v25
+	vncipher	$out5,$out5,v25
+	vncipher	$out6,$out6,v25
+	vncipher	$out7,$out7,v25
+
+	vncipher	$out1,$out1,v26
+	vncipher	$out2,$out2,v26
+	vncipher	$out3,$out3,v26
+	vncipher	$out4,$out4,v26
+	vncipher	$out5,$out5,v26
+	vncipher	$out6,$out6,v26
+	vncipher	$out7,$out7,v26
+
+	vncipher	$out1,$out1,v27
+	vncipher	$out2,$out2,v27
+	vncipher	$out3,$out3,v27
+	vncipher	$out4,$out4,v27
+	vncipher	$out5,$out5,v27
+	vncipher	$out6,$out6,v27
+	vncipher	$out7,$out7,v27
+
+	vncipher	$out1,$out1,v28
+	vncipher	$out2,$out2,v28
+	vncipher	$out3,$out3,v28
+	vncipher	$out4,$out4,v28
+	vncipher	$out5,$out5,v28
+	vncipher	$out6,$out6,v28
+	vncipher	$out7,$out7,v28
+
+	vncipher	$out1,$out1,v29
+	vncipher	$out2,$out2,v29
+	vncipher	$out3,$out3,v29
+	vncipher	$out4,$out4,v29
+	vncipher	$out5,$out5,v29
+	vncipher	$out6,$out6,v29
+	vncipher	$out7,$out7,v29
+
+	vncipher	$out1,$out1,v30
+	 vxor		$ivec,$ivec,v31		# last round key
+	vncipher	$out2,$out2,v30
+	 vxor		$in1,$in1,v31
+	vncipher	$out3,$out3,v30
+	 vxor		$in2,$in2,v31
+	vncipher	$out4,$out4,v30
+	 vxor		$in3,$in3,v31
+	vncipher	$out5,$out5,v30
+	 vxor		$in4,$in4,v31
+	vncipher	$out6,$out6,v30
+	 vxor		$in5,$in5,v31
+	vncipher	$out7,$out7,v30
+	 vxor		$in6,$in6,v31
+
+	cmplwi		$len,32			# switch($len)
+	blt		Lcbc_dec8x_one
+	nop
+	beq		Lcbc_dec8x_two
+	cmplwi		$len,64
+	blt		Lcbc_dec8x_three
+	nop
+	beq		Lcbc_dec8x_four
+	cmplwi		$len,96
+	blt		Lcbc_dec8x_five
+	nop
+	beq		Lcbc_dec8x_six
+
+Lcbc_dec8x_seven:
+	vncipherlast	$out1,$out1,$ivec
+	vncipherlast	$out2,$out2,$in1
+	vncipherlast	$out3,$out3,$in2
+	vncipherlast	$out4,$out4,$in3
+	vncipherlast	$out5,$out5,$in4
+	vncipherlast	$out6,$out6,$in5
+	vncipherlast	$out7,$out7,$in6
+	vmr		$ivec,$in7
+
+	le?vperm	$out1,$out1,$out1,$inpperm
+	le?vperm	$out2,$out2,$out2,$inpperm
+	stvx_u		$out1,$x00,$out
+	le?vperm	$out3,$out3,$out3,$inpperm
+	stvx_u		$out2,$x10,$out
+	le?vperm	$out4,$out4,$out4,$inpperm
+	stvx_u		$out3,$x20,$out
+	le?vperm	$out5,$out5,$out5,$inpperm
+	stvx_u		$out4,$x30,$out
+	le?vperm	$out6,$out6,$out6,$inpperm
+	stvx_u		$out5,$x40,$out
+	le?vperm	$out7,$out7,$out7,$inpperm
+	stvx_u		$out6,$x50,$out
+	stvx_u		$out7,$x60,$out
+	addi		$out,$out,0x70
+	b		Lcbc_dec8x_done
+
+.align	5
+Lcbc_dec8x_six:
+	vncipherlast	$out2,$out2,$ivec
+	vncipherlast	$out3,$out3,$in2
+	vncipherlast	$out4,$out4,$in3
+	vncipherlast	$out5,$out5,$in4
+	vncipherlast	$out6,$out6,$in5
+	vncipherlast	$out7,$out7,$in6
+	vmr		$ivec,$in7
+
+	le?vperm	$out2,$out2,$out2,$inpperm
+	le?vperm	$out3,$out3,$out3,$inpperm
+	stvx_u		$out2,$x00,$out
+	le?vperm	$out4,$out4,$out4,$inpperm
+	stvx_u		$out3,$x10,$out
+	le?vperm	$out5,$out5,$out5,$inpperm
+	stvx_u		$out4,$x20,$out
+	le?vperm	$out6,$out6,$out6,$inpperm
+	stvx_u		$out5,$x30,$out
+	le?vperm	$out7,$out7,$out7,$inpperm
+	stvx_u		$out6,$x40,$out
+	stvx_u		$out7,$x50,$out
+	addi		$out,$out,0x60
+	b		Lcbc_dec8x_done
+
+.align	5
+Lcbc_dec8x_five:
+	vncipherlast	$out3,$out3,$ivec
+	vncipherlast	$out4,$out4,$in3
+	vncipherlast	$out5,$out5,$in4
+	vncipherlast	$out6,$out6,$in5
+	vncipherlast	$out7,$out7,$in6
+	vmr		$ivec,$in7
+
+	le?vperm	$out3,$out3,$out3,$inpperm
+	le?vperm	$out4,$out4,$out4,$inpperm
+	stvx_u		$out3,$x00,$out
+	le?vperm	$out5,$out5,$out5,$inpperm
+	stvx_u		$out4,$x10,$out
+	le?vperm	$out6,$out6,$out6,$inpperm
+	stvx_u		$out5,$x20,$out
+	le?vperm	$out7,$out7,$out7,$inpperm
+	stvx_u		$out6,$x30,$out
+	stvx_u		$out7,$x40,$out
+	addi		$out,$out,0x50
+	b		Lcbc_dec8x_done
+
+.align	5
+Lcbc_dec8x_four:
+	vncipherlast	$out4,$out4,$ivec
+	vncipherlast	$out5,$out5,$in4
+	vncipherlast	$out6,$out6,$in5
+	vncipherlast	$out7,$out7,$in6
+	vmr		$ivec,$in7
+
+	le?vperm	$out4,$out4,$out4,$inpperm
+	le?vperm	$out5,$out5,$out5,$inpperm
+	stvx_u		$out4,$x00,$out
+	le?vperm	$out6,$out6,$out6,$inpperm
+	stvx_u		$out5,$x10,$out
+	le?vperm	$out7,$out7,$out7,$inpperm
+	stvx_u		$out6,$x20,$out
+	stvx_u		$out7,$x30,$out
+	addi		$out,$out,0x40
+	b		Lcbc_dec8x_done
+
+.align	5
+Lcbc_dec8x_three:
+	vncipherlast	$out5,$out5,$ivec
+	vncipherlast	$out6,$out6,$in5
+	vncipherlast	$out7,$out7,$in6
+	vmr		$ivec,$in7
+
+	le?vperm	$out5,$out5,$out5,$inpperm
+	le?vperm	$out6,$out6,$out6,$inpperm
+	stvx_u		$out5,$x00,$out
+	le?vperm	$out7,$out7,$out7,$inpperm
+	stvx_u		$out6,$x10,$out
+	stvx_u		$out7,$x20,$out
+	addi		$out,$out,0x30
+	b		Lcbc_dec8x_done
+
+.align	5
+Lcbc_dec8x_two:
+	vncipherlast	$out6,$out6,$ivec
+	vncipherlast	$out7,$out7,$in6
+	vmr		$ivec,$in7
+
+	le?vperm	$out6,$out6,$out6,$inpperm
+	le?vperm	$out7,$out7,$out7,$inpperm
+	stvx_u		$out6,$x00,$out
+	stvx_u		$out7,$x10,$out
+	addi		$out,$out,0x20
+	b		Lcbc_dec8x_done
+
+.align	5
+Lcbc_dec8x_one:
+	vncipherlast	$out7,$out7,$ivec
+	vmr		$ivec,$in7
+
+	le?vperm	$out7,$out7,$out7,$inpperm
+	stvx_u		$out7,0,$out
+	addi		$out,$out,0x10
+
+Lcbc_dec8x_done:
+	le?vperm	$ivec,$ivec,$ivec,$inpperm
+	stvx_u		$ivec,0,$ivp		# write [unaligned] iv
+
+	li		r10,`$FRAME+15`
+	li		r11,`$FRAME+31`
+	stvx		$inpperm,r10,$sp	# wipe copies of round keys
+	addi		r10,r10,32
+	stvx		$inpperm,r11,$sp
+	addi		r11,r11,32
+	stvx		$inpperm,r10,$sp
+	addi		r10,r10,32
+	stvx		$inpperm,r11,$sp
+	addi		r11,r11,32
+	stvx		$inpperm,r10,$sp
+	addi		r10,r10,32
+	stvx		$inpperm,r11,$sp
+	addi		r11,r11,32
+	stvx		$inpperm,r10,$sp
+	addi		r10,r10,32
+	stvx		$inpperm,r11,$sp
+	addi		r11,r11,32
+
+	mtspr		256,$vrsave
+	lvx		v20,r10,$sp		# ABI says so
+	addi		r10,r10,32
+	lvx		v21,r11,$sp
+	addi		r11,r11,32
+	lvx		v22,r10,$sp
+	addi		r10,r10,32
+	lvx		v23,r11,$sp
+	addi		r11,r11,32
+	lvx		v24,r10,$sp
+	addi		r10,r10,32
+	lvx		v25,r11,$sp
+	addi		r11,r11,32
+	lvx		v26,r10,$sp
+	addi		r10,r10,32
+	lvx		v27,r11,$sp
+	addi		r11,r11,32
+	lvx		v28,r10,$sp
+	addi		r10,r10,32
+	lvx		v29,r11,$sp
+	addi		r11,r11,32
+	lvx		v30,r10,$sp
+	lvx		v31,r11,$sp
+	$POP		r26,`$FRAME+21*16+0*$SIZE_T`($sp)
+	$POP		r27,`$FRAME+21*16+1*$SIZE_T`($sp)
+	$POP		r28,`$FRAME+21*16+2*$SIZE_T`($sp)
+	$POP		r29,`$FRAME+21*16+3*$SIZE_T`($sp)
+	$POP		r30,`$FRAME+21*16+4*$SIZE_T`($sp)
+	$POP		r31,`$FRAME+21*16+5*$SIZE_T`($sp)
+	addi		$sp,$sp,`$FRAME+21*16+6*$SIZE_T`
+	blr
+	.long		0
+	.byte		0,12,0x04,0,0x80,6,6,0
+	.long		0
+.size	.${prefix}_cbc_encrypt,.-.${prefix}_cbc_encrypt
+___
+}}	}}}
+
+#########################################################################
+{{{	# CTR procedure[s]						#
+my ($inp,$out,$len,$key,$ivp,$x10,$rounds,$idx)=map("r$_",(3..10));
+my ($rndkey0,$rndkey1,$inout,$tmp)=		map("v$_",(0..3));
+my ($ivec,$inptail,$inpperm,$outhead,$outperm,$outmask,$keyperm,$one)=
+						map("v$_",(4..11));
+my $dat=$tmp;
+
+$code.=<<___;
+.globl	.${prefix}_ctr32_encrypt_blocks
+.align	5
+.${prefix}_ctr32_encrypt_blocks:
+	${UCMP}i	$len,1
+	bltlr-
+
+	lis		r0,0xfff0
+	mfspr		$vrsave,256
+	mtspr		256,r0
+
+	li		$idx,15
+	vxor		$rndkey0,$rndkey0,$rndkey0
+	le?vspltisb	$tmp,0x0f
+
+	lvx		$ivec,0,$ivp		# load [unaligned] iv
+	lvsl		$inpperm,0,$ivp
+	lvx		$inptail,$idx,$ivp
+	 vspltisb	$one,1
+	le?vxor		$inpperm,$inpperm,$tmp
+	vperm		$ivec,$ivec,$inptail,$inpperm
+	 vsldoi		$one,$rndkey0,$one,1
+
+	neg		r11,$inp
+	?lvsl		$keyperm,0,$key		# prepare for unaligned key
+	lwz		$rounds,240($key)
+
+	lvsr		$inpperm,0,r11		# prepare for unaligned load
+	lvx		$inptail,0,$inp
+	addi		$inp,$inp,15		# 15 is not typo
+	le?vxor		$inpperm,$inpperm,$tmp
+
+	srwi		$rounds,$rounds,1
+	li		$idx,16
+	subi		$rounds,$rounds,1
+
+	${UCMP}i	$len,8
+	bge		_aesp8_ctr32_encrypt8x
+
+	?lvsr		$outperm,0,$out		# prepare for unaligned store
+	vspltisb	$outmask,-1
+	lvx		$outhead,0,$out
+	?vperm		$outmask,$rndkey0,$outmask,$outperm
+	le?vxor		$outperm,$outperm,$tmp
+
+	lvx		$rndkey0,0,$key
+	mtctr		$rounds
+	lvx		$rndkey1,$idx,$key
+	addi		$idx,$idx,16
+	?vperm		$rndkey0,$rndkey0,$rndkey1,$keyperm
+	vxor		$inout,$ivec,$rndkey0
+	lvx		$rndkey0,$idx,$key
+	addi		$idx,$idx,16
+	b		Loop_ctr32_enc
+
+.align	5
+Loop_ctr32_enc:
+	?vperm		$rndkey1,$rndkey1,$rndkey0,$keyperm
+	vcipher		$inout,$inout,$rndkey1
+	lvx		$rndkey1,$idx,$key
+	addi		$idx,$idx,16
+	?vperm		$rndkey0,$rndkey0,$rndkey1,$keyperm
+	vcipher		$inout,$inout,$rndkey0
+	lvx		$rndkey0,$idx,$key
+	addi		$idx,$idx,16
+	bdnz		Loop_ctr32_enc
+
+	vadduwm		$ivec,$ivec,$one
+	 vmr		$dat,$inptail
+	 lvx		$inptail,0,$inp
+	 addi		$inp,$inp,16
+	 subic.		$len,$len,1		# blocks--
+
+	?vperm		$rndkey1,$rndkey1,$rndkey0,$keyperm
+	vcipher		$inout,$inout,$rndkey1
+	lvx		$rndkey1,$idx,$key
+	 vperm		$dat,$dat,$inptail,$inpperm
+	 li		$idx,16
+	?vperm		$rndkey1,$rndkey0,$rndkey1,$keyperm
+	 lvx		$rndkey0,0,$key
+	vxor		$dat,$dat,$rndkey1	# last round key
+	vcipherlast	$inout,$inout,$dat
+
+	 lvx		$rndkey1,$idx,$key
+	 addi		$idx,$idx,16
+	vperm		$inout,$inout,$inout,$outperm
+	vsel		$dat,$outhead,$inout,$outmask
+	 mtctr		$rounds
+	 ?vperm		$rndkey0,$rndkey0,$rndkey1,$keyperm
+	vmr		$outhead,$inout
+	 vxor		$inout,$ivec,$rndkey0
+	 lvx		$rndkey0,$idx,$key
+	 addi		$idx,$idx,16
+	stvx		$dat,0,$out
+	addi		$out,$out,16
+	bne		Loop_ctr32_enc
+
+	addi		$out,$out,-1
+	lvx		$inout,0,$out		# redundant in aligned case
+	vsel		$inout,$outhead,$inout,$outmask
+	stvx		$inout,0,$out
+
+	mtspr		256,$vrsave
+	blr
+	.long		0
+	.byte		0,12,0x14,0,0,0,6,0
+	.long		0
+___
+#########################################################################
+{{	# Optimized CTR procedure					#
+my $key_="r11";
+my ($x00,$x10,$x20,$x30,$x40,$x50,$x60,$x70)=map("r$_",(0,8,26..31));
+    $x00=0 if ($flavour =~ /osx/);
+my ($in0, $in1, $in2, $in3, $in4, $in5, $in6, $in7 )=map("v$_",(0..3,10,12..14));
+my ($out0,$out1,$out2,$out3,$out4,$out5,$out6,$out7)=map("v$_",(15..22));
+my $rndkey0="v23";	# v24-v25 rotating buffer for first found keys
+			# v26-v31 last 6 round keys
+my ($tmp,$keyperm)=($in3,$in4);	# aliases with "caller", redundant assignment
+my ($two,$three,$four)=($outhead,$outperm,$outmask);
+
+$code.=<<___;
+.align	5
+_aesp8_ctr32_encrypt8x:
+	$STU		$sp,-`($FRAME+21*16+6*$SIZE_T)`($sp)
+	li		r10,`$FRAME+8*16+15`
+	li		r11,`$FRAME+8*16+31`
+	stvx		v20,r10,$sp		# ABI says so
+	addi		r10,r10,32
+	stvx		v21,r11,$sp
+	addi		r11,r11,32
+	stvx		v22,r10,$sp
+	addi		r10,r10,32
+	stvx		v23,r11,$sp
+	addi		r11,r11,32
+	stvx		v24,r10,$sp
+	addi		r10,r10,32
+	stvx		v25,r11,$sp
+	addi		r11,r11,32
+	stvx		v26,r10,$sp
+	addi		r10,r10,32
+	stvx		v27,r11,$sp
+	addi		r11,r11,32
+	stvx		v28,r10,$sp
+	addi		r10,r10,32
+	stvx		v29,r11,$sp
+	addi		r11,r11,32
+	stvx		v30,r10,$sp
+	stvx		v31,r11,$sp
+	li		r0,-1
+	stw		$vrsave,`$FRAME+21*16-4`($sp)	# save vrsave
+	li		$x10,0x10
+	$PUSH		r26,`$FRAME+21*16+0*$SIZE_T`($sp)
+	li		$x20,0x20
+	$PUSH		r27,`$FRAME+21*16+1*$SIZE_T`($sp)
+	li		$x30,0x30
+	$PUSH		r28,`$FRAME+21*16+2*$SIZE_T`($sp)
+	li		$x40,0x40
+	$PUSH		r29,`$FRAME+21*16+3*$SIZE_T`($sp)
+	li		$x50,0x50
+	$PUSH		r30,`$FRAME+21*16+4*$SIZE_T`($sp)
+	li		$x60,0x60
+	$PUSH		r31,`$FRAME+21*16+5*$SIZE_T`($sp)
+	li		$x70,0x70
+	mtspr		256,r0
+
+	subi		$rounds,$rounds,3	# -4 in total
+
+	lvx		$rndkey0,$x00,$key	# load key schedule
+	lvx		v30,$x10,$key
+	addi		$key,$key,0x20
+	lvx		v31,$x00,$key
+	?vperm		$rndkey0,$rndkey0,v30,$keyperm
+	addi		$key_,$sp,$FRAME+15
+	mtctr		$rounds
+
+Load_ctr32_enc_key:
+	?vperm		v24,v30,v31,$keyperm
+	lvx		v30,$x10,$key
+	addi		$key,$key,0x20
+	stvx		v24,$x00,$key_		# off-load round[1]
+	?vperm		v25,v31,v30,$keyperm
+	lvx		v31,$x00,$key
+	stvx		v25,$x10,$key_		# off-load round[2]
+	addi		$key_,$key_,0x20
+	bdnz		Load_ctr32_enc_key
+
+	lvx		v26,$x10,$key
+	?vperm		v24,v30,v31,$keyperm
+	lvx		v27,$x20,$key
+	stvx		v24,$x00,$key_		# off-load round[3]
+	?vperm		v25,v31,v26,$keyperm
+	lvx		v28,$x30,$key
+	stvx		v25,$x10,$key_		# off-load round[4]
+	addi		$key_,$sp,$FRAME+15	# rewind $key_
+	?vperm		v26,v26,v27,$keyperm
+	lvx		v29,$x40,$key
+	?vperm		v27,v27,v28,$keyperm
+	lvx		v30,$x50,$key
+	?vperm		v28,v28,v29,$keyperm
+	lvx		v31,$x60,$key
+	?vperm		v29,v29,v30,$keyperm
+	lvx		$out0,$x70,$key		# borrow $out0
+	?vperm		v30,v30,v31,$keyperm
+	lvx		v24,$x00,$key_		# pre-load round[1]
+	?vperm		v31,v31,$out0,$keyperm
+	lvx		v25,$x10,$key_		# pre-load round[2]
+
+	vadduwm		$two,$one,$one
+	subi		$inp,$inp,15		# undo "caller"
+	$SHL		$len,$len,4
+
+	vadduwm		$out1,$ivec,$one	# counter values ...
+	vadduwm		$out2,$ivec,$two
+	vxor		$out0,$ivec,$rndkey0	# ... xored with rndkey[0]
+	 le?li		$idx,8
+	vadduwm		$out3,$out1,$two
+	vxor		$out1,$out1,$rndkey0
+	 le?lvsl	$inpperm,0,$idx
+	vadduwm		$out4,$out2,$two
+	vxor		$out2,$out2,$rndkey0
+	 le?vspltisb	$tmp,0x0f
+	vadduwm		$out5,$out3,$two
+	vxor		$out3,$out3,$rndkey0
+	 le?vxor	$inpperm,$inpperm,$tmp	# transform for lvx_u/stvx_u
+	vadduwm		$out6,$out4,$two
+	vxor		$out4,$out4,$rndkey0
+	vadduwm		$out7,$out5,$two
+	vxor		$out5,$out5,$rndkey0
+	vadduwm		$ivec,$out6,$two	# next counter value
+	vxor		$out6,$out6,$rndkey0
+	vxor		$out7,$out7,$rndkey0
+
+	mtctr		$rounds
+	b		Loop_ctr32_enc8x
+.align	5
+Loop_ctr32_enc8x:
+	vcipher 	$out0,$out0,v24
+	vcipher 	$out1,$out1,v24
+	vcipher 	$out2,$out2,v24
+	vcipher 	$out3,$out3,v24
+	vcipher 	$out4,$out4,v24
+	vcipher 	$out5,$out5,v24
+	vcipher 	$out6,$out6,v24
+	vcipher 	$out7,$out7,v24
+Loop_ctr32_enc8x_middle:
+	lvx		v24,$x20,$key_		# round[3]
+	addi		$key_,$key_,0x20
+
+	vcipher 	$out0,$out0,v25
+	vcipher 	$out1,$out1,v25
+	vcipher 	$out2,$out2,v25
+	vcipher 	$out3,$out3,v25
+	vcipher 	$out4,$out4,v25
+	vcipher 	$out5,$out5,v25
+	vcipher 	$out6,$out6,v25
+	vcipher 	$out7,$out7,v25
+	lvx		v25,$x10,$key_		# round[4]
+	bdnz		Loop_ctr32_enc8x
+
+	subic		r11,$len,256		# $len-256, borrow $key_
+	vcipher 	$out0,$out0,v24
+	vcipher 	$out1,$out1,v24
+	vcipher 	$out2,$out2,v24
+	vcipher 	$out3,$out3,v24
+	vcipher 	$out4,$out4,v24
+	vcipher 	$out5,$out5,v24
+	vcipher 	$out6,$out6,v24
+	vcipher 	$out7,$out7,v24
+
+	subfe		r0,r0,r0		# borrow?-1:0
+	vcipher 	$out0,$out0,v25
+	vcipher 	$out1,$out1,v25
+	vcipher 	$out2,$out2,v25
+	vcipher 	$out3,$out3,v25
+	vcipher 	$out4,$out4,v25
+	vcipher		$out5,$out5,v25
+	vcipher		$out6,$out6,v25
+	vcipher		$out7,$out7,v25
+
+	and		r0,r0,r11
+	addi		$key_,$sp,$FRAME+15	# rewind $key_
+	vcipher		$out0,$out0,v26
+	vcipher		$out1,$out1,v26
+	vcipher		$out2,$out2,v26
+	vcipher		$out3,$out3,v26
+	vcipher		$out4,$out4,v26
+	vcipher		$out5,$out5,v26
+	vcipher		$out6,$out6,v26
+	vcipher		$out7,$out7,v26
+	lvx		v24,$x00,$key_		# re-pre-load round[1]
+
+	subic		$len,$len,129		# $len-=129
+	vcipher		$out0,$out0,v27
+	addi		$len,$len,1		# $len-=128 really
+	vcipher		$out1,$out1,v27
+	vcipher		$out2,$out2,v27
+	vcipher		$out3,$out3,v27
+	vcipher		$out4,$out4,v27
+	vcipher		$out5,$out5,v27
+	vcipher		$out6,$out6,v27
+	vcipher		$out7,$out7,v27
+	lvx		v25,$x10,$key_		# re-pre-load round[2]
+
+	vcipher		$out0,$out0,v28
+	 lvx_u		$in0,$x00,$inp		# load input
+	vcipher		$out1,$out1,v28
+	 lvx_u		$in1,$x10,$inp
+	vcipher		$out2,$out2,v28
+	 lvx_u		$in2,$x20,$inp
+	vcipher		$out3,$out3,v28
+	 lvx_u		$in3,$x30,$inp
+	vcipher		$out4,$out4,v28
+	 lvx_u		$in4,$x40,$inp
+	vcipher		$out5,$out5,v28
+	 lvx_u		$in5,$x50,$inp
+	vcipher		$out6,$out6,v28
+	 lvx_u		$in6,$x60,$inp
+	vcipher		$out7,$out7,v28
+	 lvx_u		$in7,$x70,$inp
+	 addi		$inp,$inp,0x80
+
+	vcipher		$out0,$out0,v29
+	 le?vperm	$in0,$in0,$in0,$inpperm
+	vcipher		$out1,$out1,v29
+	 le?vperm	$in1,$in1,$in1,$inpperm
+	vcipher		$out2,$out2,v29
+	 le?vperm	$in2,$in2,$in2,$inpperm
+	vcipher		$out3,$out3,v29
+	 le?vperm	$in3,$in3,$in3,$inpperm
+	vcipher		$out4,$out4,v29
+	 le?vperm	$in4,$in4,$in4,$inpperm
+	vcipher		$out5,$out5,v29
+	 le?vperm	$in5,$in5,$in5,$inpperm
+	vcipher		$out6,$out6,v29
+	 le?vperm	$in6,$in6,$in6,$inpperm
+	vcipher		$out7,$out7,v29
+	 le?vperm	$in7,$in7,$in7,$inpperm
+
+	add		$inp,$inp,r0		# $inp is adjusted in such
+						# way that at exit from the
+						# loop inX-in7 are loaded
+						# with last "words"
+	subfe.		r0,r0,r0		# borrow?-1:0
+	vcipher		$out0,$out0,v30
+	 vxor		$in0,$in0,v31		# xor with last round key
+	vcipher		$out1,$out1,v30
+	 vxor		$in1,$in1,v31
+	vcipher		$out2,$out2,v30
+	 vxor		$in2,$in2,v31
+	vcipher		$out3,$out3,v30
+	 vxor		$in3,$in3,v31
+	vcipher		$out4,$out4,v30
+	 vxor		$in4,$in4,v31
+	vcipher		$out5,$out5,v30
+	 vxor		$in5,$in5,v31
+	vcipher		$out6,$out6,v30
+	 vxor		$in6,$in6,v31
+	vcipher		$out7,$out7,v30
+	 vxor		$in7,$in7,v31
+
+	bne		Lctr32_enc8x_break	# did $len-129 borrow?
+
+	vcipherlast	$in0,$out0,$in0
+	vcipherlast	$in1,$out1,$in1
+	 vadduwm	$out1,$ivec,$one	# counter values ...
+	vcipherlast	$in2,$out2,$in2
+	 vadduwm	$out2,$ivec,$two
+	 vxor		$out0,$ivec,$rndkey0	# ... xored with rndkey[0]
+	vcipherlast	$in3,$out3,$in3
+	 vadduwm	$out3,$out1,$two
+	 vxor		$out1,$out1,$rndkey0
+	vcipherlast	$in4,$out4,$in4
+	 vadduwm	$out4,$out2,$two
+	 vxor		$out2,$out2,$rndkey0
+	vcipherlast	$in5,$out5,$in5
+	 vadduwm	$out5,$out3,$two
+	 vxor		$out3,$out3,$rndkey0
+	vcipherlast	$in6,$out6,$in6
+	 vadduwm	$out6,$out4,$two
+	 vxor		$out4,$out4,$rndkey0
+	vcipherlast	$in7,$out7,$in7
+	 vadduwm	$out7,$out5,$two
+	 vxor		$out5,$out5,$rndkey0
+	le?vperm	$in0,$in0,$in0,$inpperm
+	 vadduwm	$ivec,$out6,$two	# next counter value
+	 vxor		$out6,$out6,$rndkey0
+	le?vperm	$in1,$in1,$in1,$inpperm
+	 vxor		$out7,$out7,$rndkey0
+	mtctr		$rounds
+
+	 vcipher	$out0,$out0,v24
+	stvx_u		$in0,$x00,$out
+	le?vperm	$in2,$in2,$in2,$inpperm
+	 vcipher	$out1,$out1,v24
+	stvx_u		$in1,$x10,$out
+	le?vperm	$in3,$in3,$in3,$inpperm
+	 vcipher	$out2,$out2,v24
+	stvx_u		$in2,$x20,$out
+	le?vperm	$in4,$in4,$in4,$inpperm
+	 vcipher	$out3,$out3,v24
+	stvx_u		$in3,$x30,$out
+	le?vperm	$in5,$in5,$in5,$inpperm
+	 vcipher	$out4,$out4,v24
+	stvx_u		$in4,$x40,$out
+	le?vperm	$in6,$in6,$in6,$inpperm
+	 vcipher	$out5,$out5,v24
+	stvx_u		$in5,$x50,$out
+	le?vperm	$in7,$in7,$in7,$inpperm
+	 vcipher	$out6,$out6,v24
+	stvx_u		$in6,$x60,$out
+	 vcipher	$out7,$out7,v24
+	stvx_u		$in7,$x70,$out
+	addi		$out,$out,0x80
+
+	b		Loop_ctr32_enc8x_middle
+
+.align	5
+Lctr32_enc8x_break:
+	cmpwi		$len,-0x60
+	blt		Lctr32_enc8x_one
+	nop
+	beq		Lctr32_enc8x_two
+	cmpwi		$len,-0x40
+	blt		Lctr32_enc8x_three
+	nop
+	beq		Lctr32_enc8x_four
+	cmpwi		$len,-0x20
+	blt		Lctr32_enc8x_five
+	nop
+	beq		Lctr32_enc8x_six
+	cmpwi		$len,0x00
+	blt		Lctr32_enc8x_seven
+
+Lctr32_enc8x_eight:
+	vcipherlast	$out0,$out0,$in0
+	vcipherlast	$out1,$out1,$in1
+	vcipherlast	$out2,$out2,$in2
+	vcipherlast	$out3,$out3,$in3
+	vcipherlast	$out4,$out4,$in4
+	vcipherlast	$out5,$out5,$in5
+	vcipherlast	$out6,$out6,$in6
+	vcipherlast	$out7,$out7,$in7
+
+	le?vperm	$out0,$out0,$out0,$inpperm
+	le?vperm	$out1,$out1,$out1,$inpperm
+	stvx_u		$out0,$x00,$out
+	le?vperm	$out2,$out2,$out2,$inpperm
+	stvx_u		$out1,$x10,$out
+	le?vperm	$out3,$out3,$out3,$inpperm
+	stvx_u		$out2,$x20,$out
+	le?vperm	$out4,$out4,$out4,$inpperm
+	stvx_u		$out3,$x30,$out
+	le?vperm	$out5,$out5,$out5,$inpperm
+	stvx_u		$out4,$x40,$out
+	le?vperm	$out6,$out6,$out6,$inpperm
+	stvx_u		$out5,$x50,$out
+	le?vperm	$out7,$out7,$out7,$inpperm
+	stvx_u		$out6,$x60,$out
+	stvx_u		$out7,$x70,$out
+	addi		$out,$out,0x80
+	b		Lctr32_enc8x_done
+
+.align	5
+Lctr32_enc8x_seven:
+	vcipherlast	$out0,$out0,$in1
+	vcipherlast	$out1,$out1,$in2
+	vcipherlast	$out2,$out2,$in3
+	vcipherlast	$out3,$out3,$in4
+	vcipherlast	$out4,$out4,$in5
+	vcipherlast	$out5,$out5,$in6
+	vcipherlast	$out6,$out6,$in7
+
+	le?vperm	$out0,$out0,$out0,$inpperm
+	le?vperm	$out1,$out1,$out1,$inpperm
+	stvx_u		$out0,$x00,$out
+	le?vperm	$out2,$out2,$out2,$inpperm
+	stvx_u		$out1,$x10,$out
+	le?vperm	$out3,$out3,$out3,$inpperm
+	stvx_u		$out2,$x20,$out
+	le?vperm	$out4,$out4,$out4,$inpperm
+	stvx_u		$out3,$x30,$out
+	le?vperm	$out5,$out5,$out5,$inpperm
+	stvx_u		$out4,$x40,$out
+	le?vperm	$out6,$out6,$out6,$inpperm
+	stvx_u		$out5,$x50,$out
+	stvx_u		$out6,$x60,$out
+	addi		$out,$out,0x70
+	b		Lctr32_enc8x_done
+
+.align	5
+Lctr32_enc8x_six:
+	vcipherlast	$out0,$out0,$in2
+	vcipherlast	$out1,$out1,$in3
+	vcipherlast	$out2,$out2,$in4
+	vcipherlast	$out3,$out3,$in5
+	vcipherlast	$out4,$out4,$in6
+	vcipherlast	$out5,$out5,$in7
+
+	le?vperm	$out0,$out0,$out0,$inpperm
+	le?vperm	$out1,$out1,$out1,$inpperm
+	stvx_u		$out0,$x00,$out
+	le?vperm	$out2,$out2,$out2,$inpperm
+	stvx_u		$out1,$x10,$out
+	le?vperm	$out3,$out3,$out3,$inpperm
+	stvx_u		$out2,$x20,$out
+	le?vperm	$out4,$out4,$out4,$inpperm
+	stvx_u		$out3,$x30,$out
+	le?vperm	$out5,$out5,$out5,$inpperm
+	stvx_u		$out4,$x40,$out
+	stvx_u		$out5,$x50,$out
+	addi		$out,$out,0x60
+	b		Lctr32_enc8x_done
+
+.align	5
+Lctr32_enc8x_five:
+	vcipherlast	$out0,$out0,$in3
+	vcipherlast	$out1,$out1,$in4
+	vcipherlast	$out2,$out2,$in5
+	vcipherlast	$out3,$out3,$in6
+	vcipherlast	$out4,$out4,$in7
+
+	le?vperm	$out0,$out0,$out0,$inpperm
+	le?vperm	$out1,$out1,$out1,$inpperm
+	stvx_u		$out0,$x00,$out
+	le?vperm	$out2,$out2,$out2,$inpperm
+	stvx_u		$out1,$x10,$out
+	le?vperm	$out3,$out3,$out3,$inpperm
+	stvx_u		$out2,$x20,$out
+	le?vperm	$out4,$out4,$out4,$inpperm
+	stvx_u		$out3,$x30,$out
+	stvx_u		$out4,$x40,$out
+	addi		$out,$out,0x50
+	b		Lctr32_enc8x_done
+
+.align	5
+Lctr32_enc8x_four:
+	vcipherlast	$out0,$out0,$in4
+	vcipherlast	$out1,$out1,$in5
+	vcipherlast	$out2,$out2,$in6
+	vcipherlast	$out3,$out3,$in7
+
+	le?vperm	$out0,$out0,$out0,$inpperm
+	le?vperm	$out1,$out1,$out1,$inpperm
+	stvx_u		$out0,$x00,$out
+	le?vperm	$out2,$out2,$out2,$inpperm
+	stvx_u		$out1,$x10,$out
+	le?vperm	$out3,$out3,$out3,$inpperm
+	stvx_u		$out2,$x20,$out
+	stvx_u		$out3,$x30,$out
+	addi		$out,$out,0x40
+	b		Lctr32_enc8x_done
+
+.align	5
+Lctr32_enc8x_three:
+	vcipherlast	$out0,$out0,$in5
+	vcipherlast	$out1,$out1,$in6
+	vcipherlast	$out2,$out2,$in7
+
+	le?vperm	$out0,$out0,$out0,$inpperm
+	le?vperm	$out1,$out1,$out1,$inpperm
+	stvx_u		$out0,$x00,$out
+	le?vperm	$out2,$out2,$out2,$inpperm
+	stvx_u		$out1,$x10,$out
+	stvx_u		$out2,$x20,$out
+	addi		$out,$out,0x30
+	b		Lcbc_dec8x_done
+
+.align	5
+Lctr32_enc8x_two:
+	vcipherlast	$out0,$out0,$in6
+	vcipherlast	$out1,$out1,$in7
+
+	le?vperm	$out0,$out0,$out0,$inpperm
+	le?vperm	$out1,$out1,$out1,$inpperm
+	stvx_u		$out0,$x00,$out
+	stvx_u		$out1,$x10,$out
+	addi		$out,$out,0x20
+	b		Lcbc_dec8x_done
+
+.align	5
+Lctr32_enc8x_one:
+	vcipherlast	$out0,$out0,$in7
+
+	le?vperm	$out0,$out0,$out0,$inpperm
+	stvx_u		$out0,0,$out
+	addi		$out,$out,0x10
+
+Lctr32_enc8x_done:
+	li		r10,`$FRAME+15`
+	li		r11,`$FRAME+31`
+	stvx		$inpperm,r10,$sp	# wipe copies of round keys
+	addi		r10,r10,32
+	stvx		$inpperm,r11,$sp
+	addi		r11,r11,32
+	stvx		$inpperm,r10,$sp
+	addi		r10,r10,32
+	stvx		$inpperm,r11,$sp
+	addi		r11,r11,32
+	stvx		$inpperm,r10,$sp
+	addi		r10,r10,32
+	stvx		$inpperm,r11,$sp
+	addi		r11,r11,32
+	stvx		$inpperm,r10,$sp
+	addi		r10,r10,32
+	stvx		$inpperm,r11,$sp
+	addi		r11,r11,32
+
+	mtspr		256,$vrsave
+	lvx		v20,r10,$sp		# ABI says so
+	addi		r10,r10,32
+	lvx		v21,r11,$sp
+	addi		r11,r11,32
+	lvx		v22,r10,$sp
+	addi		r10,r10,32
+	lvx		v23,r11,$sp
+	addi		r11,r11,32
+	lvx		v24,r10,$sp
+	addi		r10,r10,32
+	lvx		v25,r11,$sp
+	addi		r11,r11,32
+	lvx		v26,r10,$sp
+	addi		r10,r10,32
+	lvx		v27,r11,$sp
+	addi		r11,r11,32
+	lvx		v28,r10,$sp
+	addi		r10,r10,32
+	lvx		v29,r11,$sp
+	addi		r11,r11,32
+	lvx		v30,r10,$sp
+	lvx		v31,r11,$sp
+	$POP		r26,`$FRAME+21*16+0*$SIZE_T`($sp)
+	$POP		r27,`$FRAME+21*16+1*$SIZE_T`($sp)
+	$POP		r28,`$FRAME+21*16+2*$SIZE_T`($sp)
+	$POP		r29,`$FRAME+21*16+3*$SIZE_T`($sp)
+	$POP		r30,`$FRAME+21*16+4*$SIZE_T`($sp)
+	$POP		r31,`$FRAME+21*16+5*$SIZE_T`($sp)
+	addi		$sp,$sp,`$FRAME+21*16+6*$SIZE_T`
+	blr
+	.long		0
+	.byte		0,12,0x04,0,0x80,6,6,0
+	.long		0
+.size	.${prefix}_ctr32_encrypt_blocks,.-.${prefix}_ctr32_encrypt_blocks
+___
+}}	}}}
+
+#########################################################################
+{{{	# XTS procedures						#
+# int aes_p8_xts_[en|de]crypt(const char *inp, char *out, size_t len,	#
+#                             const AES_KEY *key1, const AES_KEY *key2,	#
+#                             [const] unsigned char iv[16]);		#
+# If $key2 is NULL, then a "tweak chaining" mode is engaged, in which	#
+# input tweak value is assumed to be encrypted already, and last tweak	#
+# value, one suitable for consecutive call on same chunk of data, is	#
+# written back to original buffer. In addition, in "tweak chaining"	#
+# mode only complete input blocks are processed.			#
+
+my ($inp,$out,$len,$key1,$key2,$ivp,$rounds,$idx) =	map("r$_",(3..10));
+my ($rndkey0,$rndkey1,$inout) =				map("v$_",(0..2));
+my ($output,$inptail,$inpperm,$leperm,$keyperm) =	map("v$_",(3..7));
+my ($tweak,$seven,$eighty7,$tmp,$tweak1) =		map("v$_",(8..12));
+my $taillen = $key2;
+
+   ($inp,$idx) = ($idx,$inp);				# reassign
+
+$code.=<<___;
+.globl	.${prefix}_xts_encrypt
+.align	5
+.${prefix}_xts_encrypt:
+	mr		$inp,r3				# reassign
+	li		r3,-1
+	${UCMP}i	$len,16
+	bltlr-
+
+	lis		r0,0xfff0
+	mfspr		r12,256				# save vrsave
+	li		r11,0
+	mtspr		256,r0
+
+	vspltisb	$seven,0x07			# 0x070707..07
+	le?lvsl		$leperm,r11,r11
+	le?vspltisb	$tmp,0x0f
+	le?vxor		$leperm,$leperm,$seven
+
+	li		$idx,15
+	lvx		$tweak,0,$ivp			# load [unaligned] iv
+	lvsl		$inpperm,0,$ivp
+	lvx		$inptail,$idx,$ivp
+	le?vxor		$inpperm,$inpperm,$tmp
+	vperm		$tweak,$tweak,$inptail,$inpperm
+
+	neg		r11,$inp
+	lvsr		$inpperm,0,r11			# prepare for unaligned load
+	lvx		$inout,0,$inp
+	addi		$inp,$inp,15			# 15 is not typo
+	le?vxor		$inpperm,$inpperm,$tmp
+
+	${UCMP}i	$key2,0				# key2==NULL?
+	beq		Lxts_enc_no_key2
+
+	?lvsl		$keyperm,0,$key2		# prepare for unaligned key
+	lwz		$rounds,240($key2)
+	srwi		$rounds,$rounds,1
+	subi		$rounds,$rounds,1
+	li		$idx,16
+
+	lvx		$rndkey0,0,$key2
+	lvx		$rndkey1,$idx,$key2
+	addi		$idx,$idx,16
+	?vperm		$rndkey0,$rndkey0,$rndkey1,$keyperm
+	vxor		$tweak,$tweak,$rndkey0
+	lvx		$rndkey0,$idx,$key2
+	addi		$idx,$idx,16
+	mtctr		$rounds
+
+Ltweak_xts_enc:
+	?vperm		$rndkey1,$rndkey1,$rndkey0,$keyperm
+	vcipher		$tweak,$tweak,$rndkey1
+	lvx		$rndkey1,$idx,$key2
+	addi		$idx,$idx,16
+	?vperm		$rndkey0,$rndkey0,$rndkey1,$keyperm
+	vcipher		$tweak,$tweak,$rndkey0
+	lvx		$rndkey0,$idx,$key2
+	addi		$idx,$idx,16
+	bdnz		Ltweak_xts_enc
+
+	?vperm		$rndkey1,$rndkey1,$rndkey0,$keyperm
+	vcipher		$tweak,$tweak,$rndkey1
+	lvx		$rndkey1,$idx,$key2
+	?vperm		$rndkey0,$rndkey0,$rndkey1,$keyperm
+	vcipherlast	$tweak,$tweak,$rndkey0
+
+	li		$ivp,0				# don't chain the tweak
+	b		Lxts_enc
+
+Lxts_enc_no_key2:
+	li		$idx,-16
+	and		$len,$len,$idx			# in "tweak chaining"
+							# mode only complete
+							# blocks are processed
+Lxts_enc:
+	lvx		$inptail,0,$inp
+	addi		$inp,$inp,16
+
+	?lvsl		$keyperm,0,$key1		# prepare for unaligned key
+	lwz		$rounds,240($key1)
+	srwi		$rounds,$rounds,1
+	subi		$rounds,$rounds,1
+	li		$idx,16
+
+	vslb		$eighty7,$seven,$seven		# 0x808080..80
+	vor		$eighty7,$eighty7,$seven	# 0x878787..87
+	vspltisb	$tmp,1				# 0x010101..01
+	vsldoi		$eighty7,$eighty7,$tmp,15	# 0x870101..01
+
+	${UCMP}i	$len,96
+	bge		_aesp8_xts_encrypt6x
+
+	andi.		$taillen,$len,15
+	subic		r0,$len,32
+	subi		$taillen,$taillen,16
+	subfe		r0,r0,r0
+	and		r0,r0,$taillen
+	add		$inp,$inp,r0
+
+	lvx		$rndkey0,0,$key1
+	lvx		$rndkey1,$idx,$key1
+	addi		$idx,$idx,16
+	vperm		$inout,$inout,$inptail,$inpperm
+	?vperm		$rndkey0,$rndkey0,$rndkey1,$keyperm
+	vxor		$inout,$inout,$tweak
+	vxor		$inout,$inout,$rndkey0
+	lvx		$rndkey0,$idx,$key1
+	addi		$idx,$idx,16
+	mtctr		$rounds
+	b		Loop_xts_enc
+
+.align	5
+Loop_xts_enc:
+	?vperm		$rndkey1,$rndkey1,$rndkey0,$keyperm
+	vcipher		$inout,$inout,$rndkey1
+	lvx		$rndkey1,$idx,$key1
+	addi		$idx,$idx,16
+	?vperm		$rndkey0,$rndkey0,$rndkey1,$keyperm
+	vcipher		$inout,$inout,$rndkey0
+	lvx		$rndkey0,$idx,$key1
+	addi		$idx,$idx,16
+	bdnz		Loop_xts_enc
+
+	?vperm		$rndkey1,$rndkey1,$rndkey0,$keyperm
+	vcipher		$inout,$inout,$rndkey1
+	lvx		$rndkey1,$idx,$key1
+	li		$idx,16
+	?vperm		$rndkey0,$rndkey0,$rndkey1,$keyperm
+	vxor		$rndkey0,$rndkey0,$tweak
+	vcipherlast	$output,$inout,$rndkey0
+
+	le?vperm	$tmp,$output,$output,$leperm
+	be?nop
+	le?stvx_u	$tmp,0,$out
+	be?stvx_u	$output,0,$out
+	addi		$out,$out,16
+
+	subic.		$len,$len,16
+	beq		Lxts_enc_done
+
+	vmr		$inout,$inptail
+	lvx		$inptail,0,$inp
+	addi		$inp,$inp,16
+	lvx		$rndkey0,0,$key1
+	lvx		$rndkey1,$idx,$key1
+	addi		$idx,$idx,16
+
+	subic		r0,$len,32
+	subfe		r0,r0,r0
+	and		r0,r0,$taillen
+	add		$inp,$inp,r0
+
+	vsrab		$tmp,$tweak,$seven		# next tweak value
+	vaddubm		$tweak,$tweak,$tweak
+	vsldoi		$tmp,$tmp,$tmp,15
+	vand		$tmp,$tmp,$eighty7
+	vxor		$tweak,$tweak,$tmp
+
+	vperm		$inout,$inout,$inptail,$inpperm
+	?vperm		$rndkey0,$rndkey0,$rndkey1,$keyperm
+	vxor		$inout,$inout,$tweak
+	vxor		$output,$output,$rndkey0	# just in case $len<16
+	vxor		$inout,$inout,$rndkey0
+	lvx		$rndkey0,$idx,$key1
+	addi		$idx,$idx,16
+
+	mtctr		$rounds
+	${UCMP}i	$len,16
+	bge		Loop_xts_enc
+
+	vxor		$output,$output,$tweak
+	lvsr		$inpperm,0,$len			# $inpperm is no longer needed
+	vxor		$inptail,$inptail,$inptail	# $inptail is no longer needed
+	vspltisb	$tmp,-1
+	vperm		$inptail,$inptail,$tmp,$inpperm
+	vsel		$inout,$inout,$output,$inptail
+
+	subi		r11,$out,17
+	subi		$out,$out,16
+	mtctr		$len
+	li		$len,16
+Loop_xts_enc_steal:
+	lbzu		r0,1(r11)
+	stb		r0,16(r11)
+	bdnz		Loop_xts_enc_steal
+
+	mtctr		$rounds
+	b		Loop_xts_enc			# one more time...
+
+Lxts_enc_done:
+	${UCMP}i	$ivp,0
+	beq		Lxts_enc_ret
+
+	vsrab		$tmp,$tweak,$seven		# next tweak value
+	vaddubm		$tweak,$tweak,$tweak
+	vsldoi		$tmp,$tmp,$tmp,15
+	vand		$tmp,$tmp,$eighty7
+	vxor		$tweak,$tweak,$tmp
+
+	le?vperm	$tweak,$tweak,$tweak,$leperm
+	stvx_u		$tweak,0,$ivp
+
+Lxts_enc_ret:
+	mtspr		256,r12				# restore vrsave
+	li		r3,0
+	blr
+	.long		0
+	.byte		0,12,0x04,0,0x80,6,6,0
+	.long		0
+.size	.${prefix}_xts_encrypt,.-.${prefix}_xts_encrypt
+
+.globl	.${prefix}_xts_decrypt
+.align	5
+.${prefix}_xts_decrypt:
+	mr		$inp,r3				# reassign
+	li		r3,-1
+	${UCMP}i	$len,16
+	bltlr-
+
+	lis		r0,0xfff8
+	mfspr		r12,256				# save vrsave
+	li		r11,0
+	mtspr		256,r0
+
+	andi.		r0,$len,15
+	neg		r0,r0
+	andi.		r0,r0,16
+	sub		$len,$len,r0
+
+	vspltisb	$seven,0x07			# 0x070707..07
+	le?lvsl		$leperm,r11,r11
+	le?vspltisb	$tmp,0x0f
+	le?vxor		$leperm,$leperm,$seven
+
+	li		$idx,15
+	lvx		$tweak,0,$ivp			# load [unaligned] iv
+	lvsl		$inpperm,0,$ivp
+	lvx		$inptail,$idx,$ivp
+	le?vxor		$inpperm,$inpperm,$tmp
+	vperm		$tweak,$tweak,$inptail,$inpperm
+
+	neg		r11,$inp
+	lvsr		$inpperm,0,r11			# prepare for unaligned load
+	lvx		$inout,0,$inp
+	addi		$inp,$inp,15			# 15 is not typo
+	le?vxor		$inpperm,$inpperm,$tmp
+
+	${UCMP}i	$key2,0				# key2==NULL?
+	beq		Lxts_dec_no_key2
+
+	?lvsl		$keyperm,0,$key2		# prepare for unaligned key
+	lwz		$rounds,240($key2)
+	srwi		$rounds,$rounds,1
+	subi		$rounds,$rounds,1
+	li		$idx,16
+
+	lvx		$rndkey0,0,$key2
+	lvx		$rndkey1,$idx,$key2
+	addi		$idx,$idx,16
+	?vperm		$rndkey0,$rndkey0,$rndkey1,$keyperm
+	vxor		$tweak,$tweak,$rndkey0
+	lvx		$rndkey0,$idx,$key2
+	addi		$idx,$idx,16
+	mtctr		$rounds
+
+Ltweak_xts_dec:
+	?vperm		$rndkey1,$rndkey1,$rndkey0,$keyperm
+	vcipher		$tweak,$tweak,$rndkey1
+	lvx		$rndkey1,$idx,$key2
+	addi		$idx,$idx,16
+	?vperm		$rndkey0,$rndkey0,$rndkey1,$keyperm
+	vcipher		$tweak,$tweak,$rndkey0
+	lvx		$rndkey0,$idx,$key2
+	addi		$idx,$idx,16
+	bdnz		Ltweak_xts_dec
+
+	?vperm		$rndkey1,$rndkey1,$rndkey0,$keyperm
+	vcipher		$tweak,$tweak,$rndkey1
+	lvx		$rndkey1,$idx,$key2
+	?vperm		$rndkey0,$rndkey0,$rndkey1,$keyperm
+	vcipherlast	$tweak,$tweak,$rndkey0
+
+	li		$ivp,0				# don't chain the tweak
+	b		Lxts_dec
+
+Lxts_dec_no_key2:
+	neg		$idx,$len
+	andi.		$idx,$idx,15
+	add		$len,$len,$idx			# in "tweak chaining"
+							# mode only complete
+							# blocks are processed
+Lxts_dec:
+	lvx		$inptail,0,$inp
+	addi		$inp,$inp,16
+
+	?lvsl		$keyperm,0,$key1		# prepare for unaligned key
+	lwz		$rounds,240($key1)
+	srwi		$rounds,$rounds,1
+	subi		$rounds,$rounds,1
+	li		$idx,16
+
+	vslb		$eighty7,$seven,$seven		# 0x808080..80
+	vor		$eighty7,$eighty7,$seven	# 0x878787..87
+	vspltisb	$tmp,1				# 0x010101..01
+	vsldoi		$eighty7,$eighty7,$tmp,15	# 0x870101..01
+
+	${UCMP}i	$len,96
+	bge		_aesp8_xts_decrypt6x
+
+	lvx		$rndkey0,0,$key1
+	lvx		$rndkey1,$idx,$key1
+	addi		$idx,$idx,16
+	vperm		$inout,$inout,$inptail,$inpperm
+	?vperm		$rndkey0,$rndkey0,$rndkey1,$keyperm
+	vxor		$inout,$inout,$tweak
+	vxor		$inout,$inout,$rndkey0
+	lvx		$rndkey0,$idx,$key1
+	addi		$idx,$idx,16
+	mtctr		$rounds
+
+	${UCMP}i	$len,16
+	blt		Ltail_xts_dec
+	be?b		Loop_xts_dec
+
+.align	5
+Loop_xts_dec:
+	?vperm		$rndkey1,$rndkey1,$rndkey0,$keyperm
+	vncipher	$inout,$inout,$rndkey1
+	lvx		$rndkey1,$idx,$key1
+	addi		$idx,$idx,16
+	?vperm		$rndkey0,$rndkey0,$rndkey1,$keyperm
+	vncipher	$inout,$inout,$rndkey0
+	lvx		$rndkey0,$idx,$key1
+	addi		$idx,$idx,16
+	bdnz		Loop_xts_dec
+
+	?vperm		$rndkey1,$rndkey1,$rndkey0,$keyperm
+	vncipher	$inout,$inout,$rndkey1
+	lvx		$rndkey1,$idx,$key1
+	li		$idx,16
+	?vperm		$rndkey0,$rndkey0,$rndkey1,$keyperm
+	vxor		$rndkey0,$rndkey0,$tweak
+	vncipherlast	$output,$inout,$rndkey0
+
+	le?vperm	$tmp,$output,$output,$leperm
+	be?nop
+	le?stvx_u	$tmp,0,$out
+	be?stvx_u	$output,0,$out
+	addi		$out,$out,16
+
+	subic.		$len,$len,16
+	beq		Lxts_dec_done
+
+	vmr		$inout,$inptail
+	lvx		$inptail,0,$inp
+	addi		$inp,$inp,16
+	lvx		$rndkey0,0,$key1
+	lvx		$rndkey1,$idx,$key1
+	addi		$idx,$idx,16
+
+	vsrab		$tmp,$tweak,$seven		# next tweak value
+	vaddubm		$tweak,$tweak,$tweak
+	vsldoi		$tmp,$tmp,$tmp,15
+	vand		$tmp,$tmp,$eighty7
+	vxor		$tweak,$tweak,$tmp
+
+	vperm		$inout,$inout,$inptail,$inpperm
+	?vperm		$rndkey0,$rndkey0,$rndkey1,$keyperm
+	vxor		$inout,$inout,$tweak
+	vxor		$inout,$inout,$rndkey0
+	lvx		$rndkey0,$idx,$key1
+	addi		$idx,$idx,16
+
+	mtctr		$rounds
+	${UCMP}i	$len,16
+	bge		Loop_xts_dec
+
+Ltail_xts_dec:
+	vsrab		$tmp,$tweak,$seven		# next tweak value
+	vaddubm		$tweak1,$tweak,$tweak
+	vsldoi		$tmp,$tmp,$tmp,15
+	vand		$tmp,$tmp,$eighty7
+	vxor		$tweak1,$tweak1,$tmp
+
+	subi		$inp,$inp,16
+	add		$inp,$inp,$len
+
+	vxor		$inout,$inout,$tweak		# :-(
+	vxor		$inout,$inout,$tweak1		# :-)
+
+Loop_xts_dec_short:
+	?vperm		$rndkey1,$rndkey1,$rndkey0,$keyperm
+	vncipher	$inout,$inout,$rndkey1
+	lvx		$rndkey1,$idx,$key1
+	addi		$idx,$idx,16
+	?vperm		$rndkey0,$rndkey0,$rndkey1,$keyperm
+	vncipher	$inout,$inout,$rndkey0
+	lvx		$rndkey0,$idx,$key1
+	addi		$idx,$idx,16
+	bdnz		Loop_xts_dec_short
+
+	?vperm		$rndkey1,$rndkey1,$rndkey0,$keyperm
+	vncipher	$inout,$inout,$rndkey1
+	lvx		$rndkey1,$idx,$key1
+	li		$idx,16
+	?vperm		$rndkey0,$rndkey0,$rndkey1,$keyperm
+	vxor		$rndkey0,$rndkey0,$tweak1
+	vncipherlast	$output,$inout,$rndkey0
+
+	le?vperm	$tmp,$output,$output,$leperm
+	be?nop
+	le?stvx_u	$tmp,0,$out
+	be?stvx_u	$output,0,$out
+
+	vmr		$inout,$inptail
+	lvx		$inptail,0,$inp
+	#addi		$inp,$inp,16
+	lvx		$rndkey0,0,$key1
+	lvx		$rndkey1,$idx,$key1
+	addi		$idx,$idx,16
+	vperm		$inout,$inout,$inptail,$inpperm
+	?vperm		$rndkey0,$rndkey0,$rndkey1,$keyperm
+
+	lvsr		$inpperm,0,$len			# $inpperm is no longer needed
+	vxor		$inptail,$inptail,$inptail	# $inptail is no longer needed
+	vspltisb	$tmp,-1
+	vperm		$inptail,$inptail,$tmp,$inpperm
+	vsel		$inout,$inout,$output,$inptail
+
+	vxor		$rndkey0,$rndkey0,$tweak
+	vxor		$inout,$inout,$rndkey0
+	lvx		$rndkey0,$idx,$key1
+	addi		$idx,$idx,16
+
+	subi		r11,$out,1
+	mtctr		$len
+	li		$len,16
+Loop_xts_dec_steal:
+	lbzu		r0,1(r11)
+	stb		r0,16(r11)
+	bdnz		Loop_xts_dec_steal
+
+	mtctr		$rounds
+	b		Loop_xts_dec			# one more time...
+
+Lxts_dec_done:
+	${UCMP}i	$ivp,0
+	beq		Lxts_dec_ret
+
+	vsrab		$tmp,$tweak,$seven		# next tweak value
+	vaddubm		$tweak,$tweak,$tweak
+	vsldoi		$tmp,$tmp,$tmp,15
+	vand		$tmp,$tmp,$eighty7
+	vxor		$tweak,$tweak,$tmp
+
+	le?vperm	$tweak,$tweak,$tweak,$leperm
+	stvx_u		$tweak,0,$ivp
+
+Lxts_dec_ret:
+	mtspr		256,r12				# restore vrsave
+	li		r3,0
+	blr
+	.long		0
+	.byte		0,12,0x04,0,0x80,6,6,0
+	.long		0
+.size	.${prefix}_xts_decrypt,.-.${prefix}_xts_decrypt
+___
+#########################################################################
+{{	# Optimized XTS procedures					#
+my $key_=$key2;
+my ($x00,$x10,$x20,$x30,$x40,$x50,$x60,$x70)=map("r$_",(0,3,26..31));
+    $x00=0 if ($flavour =~ /osx/);
+my ($in0,  $in1,  $in2,  $in3,  $in4,  $in5 )=map("v$_",(0..5));
+my ($out0, $out1, $out2, $out3, $out4, $out5)=map("v$_",(7,12..16));
+my ($twk0, $twk1, $twk2, $twk3, $twk4, $twk5)=map("v$_",(17..22));
+my $rndkey0="v23";	# v24-v25 rotating buffer for first found keys
+			# v26-v31 last 6 round keys
+my ($keyperm)=($out0);	# aliases with "caller", redundant assignment
+my $taillen=$x70;
+
+$code.=<<___;
+.align	5
+_aesp8_xts_encrypt6x:
+	$STU		$sp,-`($FRAME+21*16+6*$SIZE_T)`($sp)
+	mflr		r11
+	li		r7,`$FRAME+8*16+15`
+	li		r3,`$FRAME+8*16+31`
+	$PUSH		r11,`$FRAME+21*16+6*$SIZE_T+$LRSAVE`($sp)
+	stvx		v20,r7,$sp		# ABI says so
+	addi		r7,r7,32
+	stvx		v21,r3,$sp
+	addi		r3,r3,32
+	stvx		v22,r7,$sp
+	addi		r7,r7,32
+	stvx		v23,r3,$sp
+	addi		r3,r3,32
+	stvx		v24,r7,$sp
+	addi		r7,r7,32
+	stvx		v25,r3,$sp
+	addi		r3,r3,32
+	stvx		v26,r7,$sp
+	addi		r7,r7,32
+	stvx		v27,r3,$sp
+	addi		r3,r3,32
+	stvx		v28,r7,$sp
+	addi		r7,r7,32
+	stvx		v29,r3,$sp
+	addi		r3,r3,32
+	stvx		v30,r7,$sp
+	stvx		v31,r3,$sp
+	li		r0,-1
+	stw		$vrsave,`$FRAME+21*16-4`($sp)	# save vrsave
+	li		$x10,0x10
+	$PUSH		r26,`$FRAME+21*16+0*$SIZE_T`($sp)
+	li		$x20,0x20
+	$PUSH		r27,`$FRAME+21*16+1*$SIZE_T`($sp)
+	li		$x30,0x30
+	$PUSH		r28,`$FRAME+21*16+2*$SIZE_T`($sp)
+	li		$x40,0x40
+	$PUSH		r29,`$FRAME+21*16+3*$SIZE_T`($sp)
+	li		$x50,0x50
+	$PUSH		r30,`$FRAME+21*16+4*$SIZE_T`($sp)
+	li		$x60,0x60
+	$PUSH		r31,`$FRAME+21*16+5*$SIZE_T`($sp)
+	li		$x70,0x70
+	mtspr		256,r0
+
+	subi		$rounds,$rounds,3	# -4 in total
+
+	lvx		$rndkey0,$x00,$key1	# load key schedule
+	lvx		v30,$x10,$key1
+	addi		$key1,$key1,0x20
+	lvx		v31,$x00,$key1
+	?vperm		$rndkey0,$rndkey0,v30,$keyperm
+	addi		$key_,$sp,$FRAME+15
+	mtctr		$rounds
+
+Load_xts_enc_key:
+	?vperm		v24,v30,v31,$keyperm
+	lvx		v30,$x10,$key1
+	addi		$key1,$key1,0x20
+	stvx		v24,$x00,$key_		# off-load round[1]
+	?vperm		v25,v31,v30,$keyperm
+	lvx		v31,$x00,$key1
+	stvx		v25,$x10,$key_		# off-load round[2]
+	addi		$key_,$key_,0x20
+	bdnz		Load_xts_enc_key
+
+	lvx		v26,$x10,$key1
+	?vperm		v24,v30,v31,$keyperm
+	lvx		v27,$x20,$key1
+	stvx		v24,$x00,$key_		# off-load round[3]
+	?vperm		v25,v31,v26,$keyperm
+	lvx		v28,$x30,$key1
+	stvx		v25,$x10,$key_		# off-load round[4]
+	addi		$key_,$sp,$FRAME+15	# rewind $key_
+	?vperm		v26,v26,v27,$keyperm
+	lvx		v29,$x40,$key1
+	?vperm		v27,v27,v28,$keyperm
+	lvx		v30,$x50,$key1
+	?vperm		v28,v28,v29,$keyperm
+	lvx		v31,$x60,$key1
+	?vperm		v29,v29,v30,$keyperm
+	lvx		$twk5,$x70,$key1	# borrow $twk5
+	?vperm		v30,v30,v31,$keyperm
+	lvx		v24,$x00,$key_		# pre-load round[1]
+	?vperm		v31,v31,$twk5,$keyperm
+	lvx		v25,$x10,$key_		# pre-load round[2]
+
+	 vperm		$in0,$inout,$inptail,$inpperm
+	 subi		$inp,$inp,31		# undo "caller"
+	vxor		$twk0,$tweak,$rndkey0
+	vsrab		$tmp,$tweak,$seven	# next tweak value
+	vaddubm		$tweak,$tweak,$tweak
+	vsldoi		$tmp,$tmp,$tmp,15
+	vand		$tmp,$tmp,$eighty7
+	 vxor		$out0,$in0,$twk0
+	vxor		$tweak,$tweak,$tmp
+
+	 lvx_u		$in1,$x10,$inp
+	vxor		$twk1,$tweak,$rndkey0
+	vsrab		$tmp,$tweak,$seven	# next tweak value
+	vaddubm		$tweak,$tweak,$tweak
+	vsldoi		$tmp,$tmp,$tmp,15
+	 le?vperm	$in1,$in1,$in1,$leperm
+	vand		$tmp,$tmp,$eighty7
+	 vxor		$out1,$in1,$twk1
+	vxor		$tweak,$tweak,$tmp
+
+	 lvx_u		$in2,$x20,$inp
+	 andi.		$taillen,$len,15
+	vxor		$twk2,$tweak,$rndkey0
+	vsrab		$tmp,$tweak,$seven	# next tweak value
+	vaddubm		$tweak,$tweak,$tweak
+	vsldoi		$tmp,$tmp,$tmp,15
+	 le?vperm	$in2,$in2,$in2,$leperm
+	vand		$tmp,$tmp,$eighty7
+	 vxor		$out2,$in2,$twk2
+	vxor		$tweak,$tweak,$tmp
+
+	 lvx_u		$in3,$x30,$inp
+	 sub		$len,$len,$taillen
+	vxor		$twk3,$tweak,$rndkey0
+	vsrab		$tmp,$tweak,$seven	# next tweak value
+	vaddubm		$tweak,$tweak,$tweak
+	vsldoi		$tmp,$tmp,$tmp,15
+	 le?vperm	$in3,$in3,$in3,$leperm
+	vand		$tmp,$tmp,$eighty7
+	 vxor		$out3,$in3,$twk3
+	vxor		$tweak,$tweak,$tmp
+
+	 lvx_u		$in4,$x40,$inp
+	 subi		$len,$len,0x60
+	vxor		$twk4,$tweak,$rndkey0
+	vsrab		$tmp,$tweak,$seven	# next tweak value
+	vaddubm		$tweak,$tweak,$tweak
+	vsldoi		$tmp,$tmp,$tmp,15
+	 le?vperm	$in4,$in4,$in4,$leperm
+	vand		$tmp,$tmp,$eighty7
+	 vxor		$out4,$in4,$twk4
+	vxor		$tweak,$tweak,$tmp
+
+	 lvx_u		$in5,$x50,$inp
+	 addi		$inp,$inp,0x60
+	vxor		$twk5,$tweak,$rndkey0
+	vsrab		$tmp,$tweak,$seven	# next tweak value
+	vaddubm		$tweak,$tweak,$tweak
+	vsldoi		$tmp,$tmp,$tmp,15
+	 le?vperm	$in5,$in5,$in5,$leperm
+	vand		$tmp,$tmp,$eighty7
+	 vxor		$out5,$in5,$twk5
+	vxor		$tweak,$tweak,$tmp
+
+	vxor		v31,v31,$rndkey0
+	mtctr		$rounds
+	b		Loop_xts_enc6x
+
+.align	5
+Loop_xts_enc6x:
+	vcipher		$out0,$out0,v24
+	vcipher		$out1,$out1,v24
+	vcipher		$out2,$out2,v24
+	vcipher		$out3,$out3,v24
+	vcipher		$out4,$out4,v24
+	vcipher		$out5,$out5,v24
+	lvx		v24,$x20,$key_		# round[3]
+	addi		$key_,$key_,0x20
+
+	vcipher		$out0,$out0,v25
+	vcipher		$out1,$out1,v25
+	vcipher		$out2,$out2,v25
+	vcipher		$out3,$out3,v25
+	vcipher		$out4,$out4,v25
+	vcipher		$out5,$out5,v25
+	lvx		v25,$x10,$key_		# round[4]
+	bdnz		Loop_xts_enc6x
+
+	subic		$len,$len,96		# $len-=96
+	 vxor		$in0,$twk0,v31		# xor with last round key
+	vcipher		$out0,$out0,v24
+	vcipher		$out1,$out1,v24
+	 vsrab		$tmp,$tweak,$seven	# next tweak value
+	 vxor		$twk0,$tweak,$rndkey0
+	 vaddubm	$tweak,$tweak,$tweak
+	vcipher		$out2,$out2,v24
+	vcipher		$out3,$out3,v24
+	 vsldoi		$tmp,$tmp,$tmp,15
+	vcipher		$out4,$out4,v24
+	vcipher		$out5,$out5,v24
+
+	subfe.		r0,r0,r0		# borrow?-1:0
+	 vand		$tmp,$tmp,$eighty7
+	vcipher		$out0,$out0,v25
+	vcipher		$out1,$out1,v25
+	 vxor		$tweak,$tweak,$tmp
+	vcipher		$out2,$out2,v25
+	vcipher		$out3,$out3,v25
+	 vxor		$in1,$twk1,v31
+	 vsrab		$tmp,$tweak,$seven	# next tweak value
+	 vxor		$twk1,$tweak,$rndkey0
+	vcipher		$out4,$out4,v25
+	vcipher		$out5,$out5,v25
+
+	and		r0,r0,$len
+	 vaddubm	$tweak,$tweak,$tweak
+	 vsldoi		$tmp,$tmp,$tmp,15
+	vcipher		$out0,$out0,v26
+	vcipher		$out1,$out1,v26
+	 vand		$tmp,$tmp,$eighty7
+	vcipher		$out2,$out2,v26
+	vcipher		$out3,$out3,v26
+	 vxor		$tweak,$tweak,$tmp
+	vcipher		$out4,$out4,v26
+	vcipher		$out5,$out5,v26
+
+	add		$inp,$inp,r0		# $inp is adjusted in such
+						# way that at exit from the
+						# loop inX-in5 are loaded
+						# with last "words"
+	 vxor		$in2,$twk2,v31
+	 vsrab		$tmp,$tweak,$seven	# next tweak value
+	 vxor		$twk2,$tweak,$rndkey0
+	 vaddubm	$tweak,$tweak,$tweak
+	vcipher		$out0,$out0,v27
+	vcipher		$out1,$out1,v27
+	 vsldoi		$tmp,$tmp,$tmp,15
+	vcipher		$out2,$out2,v27
+	vcipher		$out3,$out3,v27
+	 vand		$tmp,$tmp,$eighty7
+	vcipher		$out4,$out4,v27
+	vcipher		$out5,$out5,v27
+
+	addi		$key_,$sp,$FRAME+15	# rewind $key_
+	 vxor		$tweak,$tweak,$tmp
+	vcipher		$out0,$out0,v28
+	vcipher		$out1,$out1,v28
+	 vxor		$in3,$twk3,v31
+	 vsrab		$tmp,$tweak,$seven	# next tweak value
+	 vxor		$twk3,$tweak,$rndkey0
+	vcipher		$out2,$out2,v28
+	vcipher		$out3,$out3,v28
+	 vaddubm	$tweak,$tweak,$tweak
+	 vsldoi		$tmp,$tmp,$tmp,15
+	vcipher		$out4,$out4,v28
+	vcipher		$out5,$out5,v28
+	lvx		v24,$x00,$key_		# re-pre-load round[1]
+	 vand		$tmp,$tmp,$eighty7
+
+	vcipher		$out0,$out0,v29
+	vcipher		$out1,$out1,v29
+	 vxor		$tweak,$tweak,$tmp
+	vcipher		$out2,$out2,v29
+	vcipher		$out3,$out3,v29
+	 vxor		$in4,$twk4,v31
+	 vsrab		$tmp,$tweak,$seven	# next tweak value
+	 vxor		$twk4,$tweak,$rndkey0
+	vcipher		$out4,$out4,v29
+	vcipher		$out5,$out5,v29
+	lvx		v25,$x10,$key_		# re-pre-load round[2]
+	 vaddubm	$tweak,$tweak,$tweak
+	 vsldoi		$tmp,$tmp,$tmp,15
+
+	vcipher		$out0,$out0,v30
+	vcipher		$out1,$out1,v30
+	 vand		$tmp,$tmp,$eighty7
+	vcipher		$out2,$out2,v30
+	vcipher		$out3,$out3,v30
+	 vxor		$tweak,$tweak,$tmp
+	vcipher		$out4,$out4,v30
+	vcipher		$out5,$out5,v30
+	 vxor		$in5,$twk5,v31
+	 vsrab		$tmp,$tweak,$seven	# next tweak value
+	 vxor		$twk5,$tweak,$rndkey0
+
+	vcipherlast	$out0,$out0,$in0
+	 lvx_u		$in0,$x00,$inp		# load next input block
+	 vaddubm	$tweak,$tweak,$tweak
+	 vsldoi		$tmp,$tmp,$tmp,15
+	vcipherlast	$out1,$out1,$in1
+	 lvx_u		$in1,$x10,$inp
+	vcipherlast	$out2,$out2,$in2
+	 le?vperm	$in0,$in0,$in0,$leperm
+	 lvx_u		$in2,$x20,$inp
+	 vand		$tmp,$tmp,$eighty7
+	vcipherlast	$out3,$out3,$in3
+	 le?vperm	$in1,$in1,$in1,$leperm
+	 lvx_u		$in3,$x30,$inp
+	vcipherlast	$out4,$out4,$in4
+	 le?vperm	$in2,$in2,$in2,$leperm
+	 lvx_u		$in4,$x40,$inp
+	 vxor		$tweak,$tweak,$tmp
+	vcipherlast	$tmp,$out5,$in5		# last block might be needed
+						# in stealing mode
+	 le?vperm	$in3,$in3,$in3,$leperm
+	 lvx_u		$in5,$x50,$inp
+	 addi		$inp,$inp,0x60
+	 le?vperm	$in4,$in4,$in4,$leperm
+	 le?vperm	$in5,$in5,$in5,$leperm
+
+	le?vperm	$out0,$out0,$out0,$leperm
+	le?vperm	$out1,$out1,$out1,$leperm
+	stvx_u		$out0,$x00,$out		# store output
+	 vxor		$out0,$in0,$twk0
+	le?vperm	$out2,$out2,$out2,$leperm
+	stvx_u		$out1,$x10,$out
+	 vxor		$out1,$in1,$twk1
+	le?vperm	$out3,$out3,$out3,$leperm
+	stvx_u		$out2,$x20,$out
+	 vxor		$out2,$in2,$twk2
+	le?vperm	$out4,$out4,$out4,$leperm
+	stvx_u		$out3,$x30,$out
+	 vxor		$out3,$in3,$twk3
+	le?vperm	$out5,$tmp,$tmp,$leperm
+	stvx_u		$out4,$x40,$out
+	 vxor		$out4,$in4,$twk4
+	le?stvx_u	$out5,$x50,$out
+	be?stvx_u	$tmp, $x50,$out
+	 vxor		$out5,$in5,$twk5
+	addi		$out,$out,0x60
+
+	mtctr		$rounds
+	beq		Loop_xts_enc6x		# did $len-=96 borrow?
+
+	addic.		$len,$len,0x60
+	beq		Lxts_enc6x_zero
+	cmpwi		$len,0x20
+	blt		Lxts_enc6x_one
+	nop
+	beq		Lxts_enc6x_two
+	cmpwi		$len,0x40
+	blt		Lxts_enc6x_three
+	nop
+	beq		Lxts_enc6x_four
+
+Lxts_enc6x_five:
+	vxor		$out0,$in1,$twk0
+	vxor		$out1,$in2,$twk1
+	vxor		$out2,$in3,$twk2
+	vxor		$out3,$in4,$twk3
+	vxor		$out4,$in5,$twk4
+
+	bl		_aesp8_xts_enc5x
+
+	le?vperm	$out0,$out0,$out0,$leperm
+	vmr		$twk0,$twk5		# unused tweak
+	le?vperm	$out1,$out1,$out1,$leperm
+	stvx_u		$out0,$x00,$out		# store output
+	le?vperm	$out2,$out2,$out2,$leperm
+	stvx_u		$out1,$x10,$out
+	le?vperm	$out3,$out3,$out3,$leperm
+	stvx_u		$out2,$x20,$out
+	vxor		$tmp,$out4,$twk5	# last block prep for stealing
+	le?vperm	$out4,$out4,$out4,$leperm
+	stvx_u		$out3,$x30,$out
+	stvx_u		$out4,$x40,$out
+	addi		$out,$out,0x50
+	bne		Lxts_enc6x_steal
+	b		Lxts_enc6x_done
+
+.align	4
+Lxts_enc6x_four:
+	vxor		$out0,$in2,$twk0
+	vxor		$out1,$in3,$twk1
+	vxor		$out2,$in4,$twk2
+	vxor		$out3,$in5,$twk3
+	vxor		$out4,$out4,$out4
+
+	bl		_aesp8_xts_enc5x
+
+	le?vperm	$out0,$out0,$out0,$leperm
+	vmr		$twk0,$twk4		# unused tweak
+	le?vperm	$out1,$out1,$out1,$leperm
+	stvx_u		$out0,$x00,$out		# store output
+	le?vperm	$out2,$out2,$out2,$leperm
+	stvx_u		$out1,$x10,$out
+	vxor		$tmp,$out3,$twk4	# last block prep for stealing
+	le?vperm	$out3,$out3,$out3,$leperm
+	stvx_u		$out2,$x20,$out
+	stvx_u		$out3,$x30,$out
+	addi		$out,$out,0x40
+	bne		Lxts_enc6x_steal
+	b		Lxts_enc6x_done
+
+.align	4
+Lxts_enc6x_three:
+	vxor		$out0,$in3,$twk0
+	vxor		$out1,$in4,$twk1
+	vxor		$out2,$in5,$twk2
+	vxor		$out3,$out3,$out3
+	vxor		$out4,$out4,$out4
+
+	bl		_aesp8_xts_enc5x
+
+	le?vperm	$out0,$out0,$out0,$leperm
+	vmr		$twk0,$twk3		# unused tweak
+	le?vperm	$out1,$out1,$out1,$leperm
+	stvx_u		$out0,$x00,$out		# store output
+	vxor		$tmp,$out2,$twk3	# last block prep for stealing
+	le?vperm	$out2,$out2,$out2,$leperm
+	stvx_u		$out1,$x10,$out
+	stvx_u		$out2,$x20,$out
+	addi		$out,$out,0x30
+	bne		Lxts_enc6x_steal
+	b		Lxts_enc6x_done
+
+.align	4
+Lxts_enc6x_two:
+	vxor		$out0,$in4,$twk0
+	vxor		$out1,$in5,$twk1
+	vxor		$out2,$out2,$out2
+	vxor		$out3,$out3,$out3
+	vxor		$out4,$out4,$out4
+
+	bl		_aesp8_xts_enc5x
+
+	le?vperm	$out0,$out0,$out0,$leperm
+	vmr		$twk0,$twk2		# unused tweak
+	vxor		$tmp,$out1,$twk2	# last block prep for stealing
+	le?vperm	$out1,$out1,$out1,$leperm
+	stvx_u		$out0,$x00,$out		# store output
+	stvx_u		$out1,$x10,$out
+	addi		$out,$out,0x20
+	bne		Lxts_enc6x_steal
+	b		Lxts_enc6x_done
+
+.align	4
+Lxts_enc6x_one:
+	vxor		$out0,$in5,$twk0
+	nop
+Loop_xts_enc1x:
+	vcipher		$out0,$out0,v24
+	lvx		v24,$x20,$key_		# round[3]
+	addi		$key_,$key_,0x20
+
+	vcipher		$out0,$out0,v25
+	lvx		v25,$x10,$key_		# round[4]
+	bdnz		Loop_xts_enc1x
+
+	add		$inp,$inp,$taillen
+	cmpwi		$taillen,0
+	vcipher		$out0,$out0,v24
+
+	subi		$inp,$inp,16
+	vcipher		$out0,$out0,v25
+
+	lvsr		$inpperm,0,$taillen
+	vcipher		$out0,$out0,v26
+
+	lvx_u		$in0,0,$inp
+	vcipher		$out0,$out0,v27
+
+	addi		$key_,$sp,$FRAME+15	# rewind $key_
+	vcipher		$out0,$out0,v28
+	lvx		v24,$x00,$key_		# re-pre-load round[1]
+
+	vcipher		$out0,$out0,v29
+	lvx		v25,$x10,$key_		# re-pre-load round[2]
+	 vxor		$twk0,$twk0,v31
+
+	le?vperm	$in0,$in0,$in0,$leperm
+	vcipher		$out0,$out0,v30
+
+	vperm		$in0,$in0,$in0,$inpperm
+	vcipherlast	$out0,$out0,$twk0
+
+	vmr		$twk0,$twk1		# unused tweak
+	vxor		$tmp,$out0,$twk1	# last block prep for stealing
+	le?vperm	$out0,$out0,$out0,$leperm
+	stvx_u		$out0,$x00,$out		# store output
+	addi		$out,$out,0x10
+	bne		Lxts_enc6x_steal
+	b		Lxts_enc6x_done
+
+.align	4
+Lxts_enc6x_zero:
+	cmpwi		$taillen,0
+	beq		Lxts_enc6x_done
+
+	add		$inp,$inp,$taillen
+	subi		$inp,$inp,16
+	lvx_u		$in0,0,$inp
+	lvsr		$inpperm,0,$taillen	# $in5 is no more
+	le?vperm	$in0,$in0,$in0,$leperm
+	vperm		$in0,$in0,$in0,$inpperm
+	vxor		$tmp,$tmp,$twk0
+Lxts_enc6x_steal:
+	vxor		$in0,$in0,$twk0
+	vxor		$out0,$out0,$out0
+	vspltisb	$out1,-1
+	vperm		$out0,$out0,$out1,$inpperm
+	vsel		$out0,$in0,$tmp,$out0	# $tmp is last block, remember?
+
+	subi		r30,$out,17
+	subi		$out,$out,16
+	mtctr		$taillen
+Loop_xts_enc6x_steal:
+	lbzu		r0,1(r30)
+	stb		r0,16(r30)
+	bdnz		Loop_xts_enc6x_steal
+
+	li		$taillen,0
+	mtctr		$rounds
+	b		Loop_xts_enc1x		# one more time...
+
+.align	4
+Lxts_enc6x_done:
+	${UCMP}i	$ivp,0
+	beq		Lxts_enc6x_ret
+
+	vxor		$tweak,$twk0,$rndkey0
+	le?vperm	$tweak,$tweak,$tweak,$leperm
+	stvx_u		$tweak,0,$ivp
+
+Lxts_enc6x_ret:
+	mtlr		r11
+	li		r10,`$FRAME+15`
+	li		r11,`$FRAME+31`
+	stvx		$seven,r10,$sp		# wipe copies of round keys
+	addi		r10,r10,32
+	stvx		$seven,r11,$sp
+	addi		r11,r11,32
+	stvx		$seven,r10,$sp
+	addi		r10,r10,32
+	stvx		$seven,r11,$sp
+	addi		r11,r11,32
+	stvx		$seven,r10,$sp
+	addi		r10,r10,32
+	stvx		$seven,r11,$sp
+	addi		r11,r11,32
+	stvx		$seven,r10,$sp
+	addi		r10,r10,32
+	stvx		$seven,r11,$sp
+	addi		r11,r11,32
+
+	mtspr		256,$vrsave
+	lvx		v20,r10,$sp		# ABI says so
+	addi		r10,r10,32
+	lvx		v21,r11,$sp
+	addi		r11,r11,32
+	lvx		v22,r10,$sp
+	addi		r10,r10,32
+	lvx		v23,r11,$sp
+	addi		r11,r11,32
+	lvx		v24,r10,$sp
+	addi		r10,r10,32
+	lvx		v25,r11,$sp
+	addi		r11,r11,32
+	lvx		v26,r10,$sp
+	addi		r10,r10,32
+	lvx		v27,r11,$sp
+	addi		r11,r11,32
+	lvx		v28,r10,$sp
+	addi		r10,r10,32
+	lvx		v29,r11,$sp
+	addi		r11,r11,32
+	lvx		v30,r10,$sp
+	lvx		v31,r11,$sp
+	$POP		r26,`$FRAME+21*16+0*$SIZE_T`($sp)
+	$POP		r27,`$FRAME+21*16+1*$SIZE_T`($sp)
+	$POP		r28,`$FRAME+21*16+2*$SIZE_T`($sp)
+	$POP		r29,`$FRAME+21*16+3*$SIZE_T`($sp)
+	$POP		r30,`$FRAME+21*16+4*$SIZE_T`($sp)
+	$POP		r31,`$FRAME+21*16+5*$SIZE_T`($sp)
+	addi		$sp,$sp,`$FRAME+21*16+6*$SIZE_T`
+	blr
+	.long		0
+	.byte		0,12,0x04,1,0x80,6,6,0
+	.long		0
+
+.align	5
+_aesp8_xts_enc5x:
+	vcipher		$out0,$out0,v24
+	vcipher		$out1,$out1,v24
+	vcipher		$out2,$out2,v24
+	vcipher		$out3,$out3,v24
+	vcipher		$out4,$out4,v24
+	lvx		v24,$x20,$key_		# round[3]
+	addi		$key_,$key_,0x20
+
+	vcipher		$out0,$out0,v25
+	vcipher		$out1,$out1,v25
+	vcipher		$out2,$out2,v25
+	vcipher		$out3,$out3,v25
+	vcipher		$out4,$out4,v25
+	lvx		v25,$x10,$key_		# round[4]
+	bdnz		_aesp8_xts_enc5x
+
+	add		$inp,$inp,$taillen
+	cmpwi		$taillen,0
+	vcipher		$out0,$out0,v24
+	vcipher		$out1,$out1,v24
+	vcipher		$out2,$out2,v24
+	vcipher		$out3,$out3,v24
+	vcipher		$out4,$out4,v24
+
+	subi		$inp,$inp,16
+	vcipher		$out0,$out0,v25
+	vcipher		$out1,$out1,v25
+	vcipher		$out2,$out2,v25
+	vcipher		$out3,$out3,v25
+	vcipher		$out4,$out4,v25
+	 vxor		$twk0,$twk0,v31
+
+	vcipher		$out0,$out0,v26
+	lvsr		$inpperm,0,$taillen	# $in5 is no more
+	vcipher		$out1,$out1,v26
+	vcipher		$out2,$out2,v26
+	vcipher		$out3,$out3,v26
+	vcipher		$out4,$out4,v26
+	 vxor		$in1,$twk1,v31
+
+	vcipher		$out0,$out0,v27
+	lvx_u		$in0,0,$inp
+	vcipher		$out1,$out1,v27
+	vcipher		$out2,$out2,v27
+	vcipher		$out3,$out3,v27
+	vcipher		$out4,$out4,v27
+	 vxor		$in2,$twk2,v31
+
+	addi		$key_,$sp,$FRAME+15	# rewind $key_
+	vcipher		$out0,$out0,v28
+	vcipher		$out1,$out1,v28
+	vcipher		$out2,$out2,v28
+	vcipher		$out3,$out3,v28
+	vcipher		$out4,$out4,v28
+	lvx		v24,$x00,$key_		# re-pre-load round[1]
+	 vxor		$in3,$twk3,v31
+
+	vcipher		$out0,$out0,v29
+	le?vperm	$in0,$in0,$in0,$leperm
+	vcipher		$out1,$out1,v29
+	vcipher		$out2,$out2,v29
+	vcipher		$out3,$out3,v29
+	vcipher		$out4,$out4,v29
+	lvx		v25,$x10,$key_		# re-pre-load round[2]
+	 vxor		$in4,$twk4,v31
+
+	vcipher		$out0,$out0,v30
+	vperm		$in0,$in0,$in0,$inpperm
+	vcipher		$out1,$out1,v30
+	vcipher		$out2,$out2,v30
+	vcipher		$out3,$out3,v30
+	vcipher		$out4,$out4,v30
+
+	vcipherlast	$out0,$out0,$twk0
+	vcipherlast	$out1,$out1,$in1
+	vcipherlast	$out2,$out2,$in2
+	vcipherlast	$out3,$out3,$in3
+	vcipherlast	$out4,$out4,$in4
+	blr
+        .long   	0
+        .byte   	0,12,0x14,0,0,0,0,0
+
+.align	5
+_aesp8_xts_decrypt6x:
+	$STU		$sp,-`($FRAME+21*16+6*$SIZE_T)`($sp)
+	mflr		r11
+	li		r7,`$FRAME+8*16+15`
+	li		r3,`$FRAME+8*16+31`
+	$PUSH		r11,`$FRAME+21*16+6*$SIZE_T+$LRSAVE`($sp)
+	stvx		v20,r7,$sp		# ABI says so
+	addi		r7,r7,32
+	stvx		v21,r3,$sp
+	addi		r3,r3,32
+	stvx		v22,r7,$sp
+	addi		r7,r7,32
+	stvx		v23,r3,$sp
+	addi		r3,r3,32
+	stvx		v24,r7,$sp
+	addi		r7,r7,32
+	stvx		v25,r3,$sp
+	addi		r3,r3,32
+	stvx		v26,r7,$sp
+	addi		r7,r7,32
+	stvx		v27,r3,$sp
+	addi		r3,r3,32
+	stvx		v28,r7,$sp
+	addi		r7,r7,32
+	stvx		v29,r3,$sp
+	addi		r3,r3,32
+	stvx		v30,r7,$sp
+	stvx		v31,r3,$sp
+	li		r0,-1
+	stw		$vrsave,`$FRAME+21*16-4`($sp)	# save vrsave
+	li		$x10,0x10
+	$PUSH		r26,`$FRAME+21*16+0*$SIZE_T`($sp)
+	li		$x20,0x20
+	$PUSH		r27,`$FRAME+21*16+1*$SIZE_T`($sp)
+	li		$x30,0x30
+	$PUSH		r28,`$FRAME+21*16+2*$SIZE_T`($sp)
+	li		$x40,0x40
+	$PUSH		r29,`$FRAME+21*16+3*$SIZE_T`($sp)
+	li		$x50,0x50
+	$PUSH		r30,`$FRAME+21*16+4*$SIZE_T`($sp)
+	li		$x60,0x60
+	$PUSH		r31,`$FRAME+21*16+5*$SIZE_T`($sp)
+	li		$x70,0x70
+	mtspr		256,r0
+
+	subi		$rounds,$rounds,3	# -4 in total
+
+	lvx		$rndkey0,$x00,$key1	# load key schedule
+	lvx		v30,$x10,$key1
+	addi		$key1,$key1,0x20
+	lvx		v31,$x00,$key1
+	?vperm		$rndkey0,$rndkey0,v30,$keyperm
+	addi		$key_,$sp,$FRAME+15
+	mtctr		$rounds
+
+Load_xts_dec_key:
+	?vperm		v24,v30,v31,$keyperm
+	lvx		v30,$x10,$key1
+	addi		$key1,$key1,0x20
+	stvx		v24,$x00,$key_		# off-load round[1]
+	?vperm		v25,v31,v30,$keyperm
+	lvx		v31,$x00,$key1
+	stvx		v25,$x10,$key_		# off-load round[2]
+	addi		$key_,$key_,0x20
+	bdnz		Load_xts_dec_key
+
+	lvx		v26,$x10,$key1
+	?vperm		v24,v30,v31,$keyperm
+	lvx		v27,$x20,$key1
+	stvx		v24,$x00,$key_		# off-load round[3]
+	?vperm		v25,v31,v26,$keyperm
+	lvx		v28,$x30,$key1
+	stvx		v25,$x10,$key_		# off-load round[4]
+	addi		$key_,$sp,$FRAME+15	# rewind $key_
+	?vperm		v26,v26,v27,$keyperm
+	lvx		v29,$x40,$key1
+	?vperm		v27,v27,v28,$keyperm
+	lvx		v30,$x50,$key1
+	?vperm		v28,v28,v29,$keyperm
+	lvx		v31,$x60,$key1
+	?vperm		v29,v29,v30,$keyperm
+	lvx		$twk5,$x70,$key1	# borrow $twk5
+	?vperm		v30,v30,v31,$keyperm
+	lvx		v24,$x00,$key_		# pre-load round[1]
+	?vperm		v31,v31,$twk5,$keyperm
+	lvx		v25,$x10,$key_		# pre-load round[2]
+
+	 vperm		$in0,$inout,$inptail,$inpperm
+	 subi		$inp,$inp,31		# undo "caller"
+	vxor		$twk0,$tweak,$rndkey0
+	vsrab		$tmp,$tweak,$seven	# next tweak value
+	vaddubm		$tweak,$tweak,$tweak
+	vsldoi		$tmp,$tmp,$tmp,15
+	vand		$tmp,$tmp,$eighty7
+	 vxor		$out0,$in0,$twk0
+	vxor		$tweak,$tweak,$tmp
+
+	 lvx_u		$in1,$x10,$inp
+	vxor		$twk1,$tweak,$rndkey0
+	vsrab		$tmp,$tweak,$seven	# next tweak value
+	vaddubm		$tweak,$tweak,$tweak
+	vsldoi		$tmp,$tmp,$tmp,15
+	 le?vperm	$in1,$in1,$in1,$leperm
+	vand		$tmp,$tmp,$eighty7
+	 vxor		$out1,$in1,$twk1
+	vxor		$tweak,$tweak,$tmp
+
+	 lvx_u		$in2,$x20,$inp
+	 andi.		$taillen,$len,15
+	vxor		$twk2,$tweak,$rndkey0
+	vsrab		$tmp,$tweak,$seven	# next tweak value
+	vaddubm		$tweak,$tweak,$tweak
+	vsldoi		$tmp,$tmp,$tmp,15
+	 le?vperm	$in2,$in2,$in2,$leperm
+	vand		$tmp,$tmp,$eighty7
+	 vxor		$out2,$in2,$twk2
+	vxor		$tweak,$tweak,$tmp
+
+	 lvx_u		$in3,$x30,$inp
+	 sub		$len,$len,$taillen
+	vxor		$twk3,$tweak,$rndkey0
+	vsrab		$tmp,$tweak,$seven	# next tweak value
+	vaddubm		$tweak,$tweak,$tweak
+	vsldoi		$tmp,$tmp,$tmp,15
+	 le?vperm	$in3,$in3,$in3,$leperm
+	vand		$tmp,$tmp,$eighty7
+	 vxor		$out3,$in3,$twk3
+	vxor		$tweak,$tweak,$tmp
+
+	 lvx_u		$in4,$x40,$inp
+	 subi		$len,$len,0x60
+	vxor		$twk4,$tweak,$rndkey0
+	vsrab		$tmp,$tweak,$seven	# next tweak value
+	vaddubm		$tweak,$tweak,$tweak
+	vsldoi		$tmp,$tmp,$tmp,15
+	 le?vperm	$in4,$in4,$in4,$leperm
+	vand		$tmp,$tmp,$eighty7
+	 vxor		$out4,$in4,$twk4
+	vxor		$tweak,$tweak,$tmp
+
+	 lvx_u		$in5,$x50,$inp
+	 addi		$inp,$inp,0x60
+	vxor		$twk5,$tweak,$rndkey0
+	vsrab		$tmp,$tweak,$seven	# next tweak value
+	vaddubm		$tweak,$tweak,$tweak
+	vsldoi		$tmp,$tmp,$tmp,15
+	 le?vperm	$in5,$in5,$in5,$leperm
+	vand		$tmp,$tmp,$eighty7
+	 vxor		$out5,$in5,$twk5
+	vxor		$tweak,$tweak,$tmp
+
+	vxor		v31,v31,$rndkey0
+	mtctr		$rounds
+	b		Loop_xts_dec6x
+
+.align	5
+Loop_xts_dec6x:
+	vncipher	$out0,$out0,v24
+	vncipher	$out1,$out1,v24
+	vncipher	$out2,$out2,v24
+	vncipher	$out3,$out3,v24
+	vncipher	$out4,$out4,v24
+	vncipher	$out5,$out5,v24
+	lvx		v24,$x20,$key_		# round[3]
+	addi		$key_,$key_,0x20
+
+	vncipher	$out0,$out0,v25
+	vncipher	$out1,$out1,v25
+	vncipher	$out2,$out2,v25
+	vncipher	$out3,$out3,v25
+	vncipher	$out4,$out4,v25
+	vncipher	$out5,$out5,v25
+	lvx		v25,$x10,$key_		# round[4]
+	bdnz		Loop_xts_dec6x
+
+	subic		$len,$len,96		# $len-=96
+	 vxor		$in0,$twk0,v31		# xor with last round key
+	vncipher	$out0,$out0,v24
+	vncipher	$out1,$out1,v24
+	 vsrab		$tmp,$tweak,$seven	# next tweak value
+	 vxor		$twk0,$tweak,$rndkey0
+	 vaddubm	$tweak,$tweak,$tweak
+	vncipher	$out2,$out2,v24
+	vncipher	$out3,$out3,v24
+	 vsldoi		$tmp,$tmp,$tmp,15
+	vncipher	$out4,$out4,v24
+	vncipher	$out5,$out5,v24
+
+	subfe.		r0,r0,r0		# borrow?-1:0
+	 vand		$tmp,$tmp,$eighty7
+	vncipher	$out0,$out0,v25
+	vncipher	$out1,$out1,v25
+	 vxor		$tweak,$tweak,$tmp
+	vncipher	$out2,$out2,v25
+	vncipher	$out3,$out3,v25
+	 vxor		$in1,$twk1,v31
+	 vsrab		$tmp,$tweak,$seven	# next tweak value
+	 vxor		$twk1,$tweak,$rndkey0
+	vncipher	$out4,$out4,v25
+	vncipher	$out5,$out5,v25
+
+	and		r0,r0,$len
+	 vaddubm	$tweak,$tweak,$tweak
+	 vsldoi		$tmp,$tmp,$tmp,15
+	vncipher	$out0,$out0,v26
+	vncipher	$out1,$out1,v26
+	 vand		$tmp,$tmp,$eighty7
+	vncipher	$out2,$out2,v26
+	vncipher	$out3,$out3,v26
+	 vxor		$tweak,$tweak,$tmp
+	vncipher	$out4,$out4,v26
+	vncipher	$out5,$out5,v26
+
+	add		$inp,$inp,r0		# $inp is adjusted in such
+						# way that at exit from the
+						# loop inX-in5 are loaded
+						# with last "words"
+	 vxor		$in2,$twk2,v31
+	 vsrab		$tmp,$tweak,$seven	# next tweak value
+	 vxor		$twk2,$tweak,$rndkey0
+	 vaddubm	$tweak,$tweak,$tweak
+	vncipher	$out0,$out0,v27
+	vncipher	$out1,$out1,v27
+	 vsldoi		$tmp,$tmp,$tmp,15
+	vncipher	$out2,$out2,v27
+	vncipher	$out3,$out3,v27
+	 vand		$tmp,$tmp,$eighty7
+	vncipher	$out4,$out4,v27
+	vncipher	$out5,$out5,v27
+
+	addi		$key_,$sp,$FRAME+15	# rewind $key_
+	 vxor		$tweak,$tweak,$tmp
+	vncipher	$out0,$out0,v28
+	vncipher	$out1,$out1,v28
+	 vxor		$in3,$twk3,v31
+	 vsrab		$tmp,$tweak,$seven	# next tweak value
+	 vxor		$twk3,$tweak,$rndkey0
+	vncipher	$out2,$out2,v28
+	vncipher	$out3,$out3,v28
+	 vaddubm	$tweak,$tweak,$tweak
+	 vsldoi		$tmp,$tmp,$tmp,15
+	vncipher	$out4,$out4,v28
+	vncipher	$out5,$out5,v28
+	lvx		v24,$x00,$key_		# re-pre-load round[1]
+	 vand		$tmp,$tmp,$eighty7
+
+	vncipher	$out0,$out0,v29
+	vncipher	$out1,$out1,v29
+	 vxor		$tweak,$tweak,$tmp
+	vncipher	$out2,$out2,v29
+	vncipher	$out3,$out3,v29
+	 vxor		$in4,$twk4,v31
+	 vsrab		$tmp,$tweak,$seven	# next tweak value
+	 vxor		$twk4,$tweak,$rndkey0
+	vncipher	$out4,$out4,v29
+	vncipher	$out5,$out5,v29
+	lvx		v25,$x10,$key_		# re-pre-load round[2]
+	 vaddubm	$tweak,$tweak,$tweak
+	 vsldoi		$tmp,$tmp,$tmp,15
+
+	vncipher	$out0,$out0,v30
+	vncipher	$out1,$out1,v30
+	 vand		$tmp,$tmp,$eighty7
+	vncipher	$out2,$out2,v30
+	vncipher	$out3,$out3,v30
+	 vxor		$tweak,$tweak,$tmp
+	vncipher	$out4,$out4,v30
+	vncipher	$out5,$out5,v30
+	 vxor		$in5,$twk5,v31
+	 vsrab		$tmp,$tweak,$seven	# next tweak value
+	 vxor		$twk5,$tweak,$rndkey0
+
+	vncipherlast	$out0,$out0,$in0
+	 lvx_u		$in0,$x00,$inp		# load next input block
+	 vaddubm	$tweak,$tweak,$tweak
+	 vsldoi		$tmp,$tmp,$tmp,15
+	vncipherlast	$out1,$out1,$in1
+	 lvx_u		$in1,$x10,$inp
+	vncipherlast	$out2,$out2,$in2
+	 le?vperm	$in0,$in0,$in0,$leperm
+	 lvx_u		$in2,$x20,$inp
+	 vand		$tmp,$tmp,$eighty7
+	vncipherlast	$out3,$out3,$in3
+	 le?vperm	$in1,$in1,$in1,$leperm
+	 lvx_u		$in3,$x30,$inp
+	vncipherlast	$out4,$out4,$in4
+	 le?vperm	$in2,$in2,$in2,$leperm
+	 lvx_u		$in4,$x40,$inp
+	 vxor		$tweak,$tweak,$tmp
+	vncipherlast	$out5,$out5,$in5
+	 le?vperm	$in3,$in3,$in3,$leperm
+	 lvx_u		$in5,$x50,$inp
+	 addi		$inp,$inp,0x60
+	 le?vperm	$in4,$in4,$in4,$leperm
+	 le?vperm	$in5,$in5,$in5,$leperm
+
+	le?vperm	$out0,$out0,$out0,$leperm
+	le?vperm	$out1,$out1,$out1,$leperm
+	stvx_u		$out0,$x00,$out		# store output
+	 vxor		$out0,$in0,$twk0
+	le?vperm	$out2,$out2,$out2,$leperm
+	stvx_u		$out1,$x10,$out
+	 vxor		$out1,$in1,$twk1
+	le?vperm	$out3,$out3,$out3,$leperm
+	stvx_u		$out2,$x20,$out
+	 vxor		$out2,$in2,$twk2
+	le?vperm	$out4,$out4,$out4,$leperm
+	stvx_u		$out3,$x30,$out
+	 vxor		$out3,$in3,$twk3
+	le?vperm	$out5,$out5,$out5,$leperm
+	stvx_u		$out4,$x40,$out
+	 vxor		$out4,$in4,$twk4
+	stvx_u		$out5,$x50,$out
+	 vxor		$out5,$in5,$twk5
+	addi		$out,$out,0x60
+
+	mtctr		$rounds
+	beq		Loop_xts_dec6x		# did $len-=96 borrow?
+
+	addic.		$len,$len,0x60
+	beq		Lxts_dec6x_zero
+	cmpwi		$len,0x20
+	blt		Lxts_dec6x_one
+	nop
+	beq		Lxts_dec6x_two
+	cmpwi		$len,0x40
+	blt		Lxts_dec6x_three
+	nop
+	beq		Lxts_dec6x_four
+
+Lxts_dec6x_five:
+	vxor		$out0,$in1,$twk0
+	vxor		$out1,$in2,$twk1
+	vxor		$out2,$in3,$twk2
+	vxor		$out3,$in4,$twk3
+	vxor		$out4,$in5,$twk4
+
+	bl		_aesp8_xts_dec5x
+
+	le?vperm	$out0,$out0,$out0,$leperm
+	vmr		$twk0,$twk5		# unused tweak
+	vxor		$twk1,$tweak,$rndkey0
+	le?vperm	$out1,$out1,$out1,$leperm
+	stvx_u		$out0,$x00,$out		# store output
+	vxor		$out0,$in0,$twk1
+	le?vperm	$out2,$out2,$out2,$leperm
+	stvx_u		$out1,$x10,$out
+	le?vperm	$out3,$out3,$out3,$leperm
+	stvx_u		$out2,$x20,$out
+	le?vperm	$out4,$out4,$out4,$leperm
+	stvx_u		$out3,$x30,$out
+	stvx_u		$out4,$x40,$out
+	addi		$out,$out,0x50
+	bne		Lxts_dec6x_steal
+	b		Lxts_dec6x_done
+
+.align	4
+Lxts_dec6x_four:
+	vxor		$out0,$in2,$twk0
+	vxor		$out1,$in3,$twk1
+	vxor		$out2,$in4,$twk2
+	vxor		$out3,$in5,$twk3
+	vxor		$out4,$out4,$out4
+
+	bl		_aesp8_xts_dec5x
+
+	le?vperm	$out0,$out0,$out0,$leperm
+	vmr		$twk0,$twk4		# unused tweak
+	vmr		$twk1,$twk5
+	le?vperm	$out1,$out1,$out1,$leperm
+	stvx_u		$out0,$x00,$out		# store output
+	vxor		$out0,$in0,$twk5
+	le?vperm	$out2,$out2,$out2,$leperm
+	stvx_u		$out1,$x10,$out
+	le?vperm	$out3,$out3,$out3,$leperm
+	stvx_u		$out2,$x20,$out
+	stvx_u		$out3,$x30,$out
+	addi		$out,$out,0x40
+	bne		Lxts_dec6x_steal
+	b		Lxts_dec6x_done
+
+.align	4
+Lxts_dec6x_three:
+	vxor		$out0,$in3,$twk0
+	vxor		$out1,$in4,$twk1
+	vxor		$out2,$in5,$twk2
+	vxor		$out3,$out3,$out3
+	vxor		$out4,$out4,$out4
+
+	bl		_aesp8_xts_dec5x
+
+	le?vperm	$out0,$out0,$out0,$leperm
+	vmr		$twk0,$twk3		# unused tweak
+	vmr		$twk1,$twk4
+	le?vperm	$out1,$out1,$out1,$leperm
+	stvx_u		$out0,$x00,$out		# store output
+	vxor		$out0,$in0,$twk4
+	le?vperm	$out2,$out2,$out2,$leperm
+	stvx_u		$out1,$x10,$out
+	stvx_u		$out2,$x20,$out
+	addi		$out,$out,0x30
+	bne		Lxts_dec6x_steal
+	b		Lxts_dec6x_done
+
+.align	4
+Lxts_dec6x_two:
+	vxor		$out0,$in4,$twk0
+	vxor		$out1,$in5,$twk1
+	vxor		$out2,$out2,$out2
+	vxor		$out3,$out3,$out3
+	vxor		$out4,$out4,$out4
+
+	bl		_aesp8_xts_dec5x
+
+	le?vperm	$out0,$out0,$out0,$leperm
+	vmr		$twk0,$twk2		# unused tweak
+	vmr		$twk1,$twk3
+	le?vperm	$out1,$out1,$out1,$leperm
+	stvx_u		$out0,$x00,$out		# store output
+	vxor		$out0,$in0,$twk3
+	stvx_u		$out1,$x10,$out
+	addi		$out,$out,0x20
+	bne		Lxts_dec6x_steal
+	b		Lxts_dec6x_done
+
+.align	4
+Lxts_dec6x_one:
+	vxor		$out0,$in5,$twk0
+	nop
+Loop_xts_dec1x:
+	vncipher	$out0,$out0,v24
+	lvx		v24,$x20,$key_		# round[3]
+	addi		$key_,$key_,0x20
+
+	vncipher	$out0,$out0,v25
+	lvx		v25,$x10,$key_		# round[4]
+	bdnz		Loop_xts_dec1x
+
+	subi		r0,$taillen,1
+	vncipher	$out0,$out0,v24
+
+	andi.		r0,r0,16
+	cmpwi		$taillen,0
+	vncipher	$out0,$out0,v25
+
+	sub		$inp,$inp,r0
+	vncipher	$out0,$out0,v26
+
+	lvx_u		$in0,0,$inp
+	vncipher	$out0,$out0,v27
+
+	addi		$key_,$sp,$FRAME+15	# rewind $key_
+	vncipher	$out0,$out0,v28
+	lvx		v24,$x00,$key_		# re-pre-load round[1]
+
+	vncipher	$out0,$out0,v29
+	lvx		v25,$x10,$key_		# re-pre-load round[2]
+	 vxor		$twk0,$twk0,v31
+
+	le?vperm	$in0,$in0,$in0,$leperm
+	vncipher	$out0,$out0,v30
+
+	mtctr		$rounds
+	vncipherlast	$out0,$out0,$twk0
+
+	vmr		$twk0,$twk1		# unused tweak
+	vmr		$twk1,$twk2
+	le?vperm	$out0,$out0,$out0,$leperm
+	stvx_u		$out0,$x00,$out		# store output
+	addi		$out,$out,0x10
+	vxor		$out0,$in0,$twk2
+	bne		Lxts_dec6x_steal
+	b		Lxts_dec6x_done
+
+.align	4
+Lxts_dec6x_zero:
+	cmpwi		$taillen,0
+	beq		Lxts_dec6x_done
+
+	lvx_u		$in0,0,$inp
+	le?vperm	$in0,$in0,$in0,$leperm
+	vxor		$out0,$in0,$twk1
+Lxts_dec6x_steal:
+	vncipher	$out0,$out0,v24
+	lvx		v24,$x20,$key_		# round[3]
+	addi		$key_,$key_,0x20
+
+	vncipher	$out0,$out0,v25
+	lvx		v25,$x10,$key_		# round[4]
+	bdnz		Lxts_dec6x_steal
+
+	add		$inp,$inp,$taillen
+	vncipher	$out0,$out0,v24
+
+	cmpwi		$taillen,0
+	vncipher	$out0,$out0,v25
+
+	lvx_u		$in0,0,$inp
+	vncipher	$out0,$out0,v26
+
+	lvsr		$inpperm,0,$taillen	# $in5 is no more
+	vncipher	$out0,$out0,v27
+
+	addi		$key_,$sp,$FRAME+15	# rewind $key_
+	vncipher	$out0,$out0,v28
+	lvx		v24,$x00,$key_		# re-pre-load round[1]
+
+	vncipher	$out0,$out0,v29
+	lvx		v25,$x10,$key_		# re-pre-load round[2]
+	 vxor		$twk1,$twk1,v31
+
+	le?vperm	$in0,$in0,$in0,$leperm
+	vncipher	$out0,$out0,v30
+
+	vperm		$in0,$in0,$in0,$inpperm
+	vncipherlast	$tmp,$out0,$twk1
+
+	le?vperm	$out0,$tmp,$tmp,$leperm
+	le?stvx_u	$out0,0,$out
+	be?stvx_u	$tmp,0,$out
+
+	vxor		$out0,$out0,$out0
+	vspltisb	$out1,-1
+	vperm		$out0,$out0,$out1,$inpperm
+	vsel		$out0,$in0,$tmp,$out0
+	vxor		$out0,$out0,$twk0
+
+	subi		r30,$out,1
+	mtctr		$taillen
+Loop_xts_dec6x_steal:
+	lbzu		r0,1(r30)
+	stb		r0,16(r30)
+	bdnz		Loop_xts_dec6x_steal
+
+	li		$taillen,0
+	mtctr		$rounds
+	b		Loop_xts_dec1x		# one more time...
+
+.align	4
+Lxts_dec6x_done:
+	${UCMP}i	$ivp,0
+	beq		Lxts_dec6x_ret
+
+	vxor		$tweak,$twk0,$rndkey0
+	le?vperm	$tweak,$tweak,$tweak,$leperm
+	stvx_u		$tweak,0,$ivp
+
+Lxts_dec6x_ret:
+	mtlr		r11
+	li		r10,`$FRAME+15`
+	li		r11,`$FRAME+31`
+	stvx		$seven,r10,$sp		# wipe copies of round keys
+	addi		r10,r10,32
+	stvx		$seven,r11,$sp
+	addi		r11,r11,32
+	stvx		$seven,r10,$sp
+	addi		r10,r10,32
+	stvx		$seven,r11,$sp
+	addi		r11,r11,32
+	stvx		$seven,r10,$sp
+	addi		r10,r10,32
+	stvx		$seven,r11,$sp
+	addi		r11,r11,32
+	stvx		$seven,r10,$sp
+	addi		r10,r10,32
+	stvx		$seven,r11,$sp
+	addi		r11,r11,32
+
+	mtspr		256,$vrsave
+	lvx		v20,r10,$sp		# ABI says so
+	addi		r10,r10,32
+	lvx		v21,r11,$sp
+	addi		r11,r11,32
+	lvx		v22,r10,$sp
+	addi		r10,r10,32
+	lvx		v23,r11,$sp
+	addi		r11,r11,32
+	lvx		v24,r10,$sp
+	addi		r10,r10,32
+	lvx		v25,r11,$sp
+	addi		r11,r11,32
+	lvx		v26,r10,$sp
+	addi		r10,r10,32
+	lvx		v27,r11,$sp
+	addi		r11,r11,32
+	lvx		v28,r10,$sp
+	addi		r10,r10,32
+	lvx		v29,r11,$sp
+	addi		r11,r11,32
+	lvx		v30,r10,$sp
+	lvx		v31,r11,$sp
+	$POP		r26,`$FRAME+21*16+0*$SIZE_T`($sp)
+	$POP		r27,`$FRAME+21*16+1*$SIZE_T`($sp)
+	$POP		r28,`$FRAME+21*16+2*$SIZE_T`($sp)
+	$POP		r29,`$FRAME+21*16+3*$SIZE_T`($sp)
+	$POP		r30,`$FRAME+21*16+4*$SIZE_T`($sp)
+	$POP		r31,`$FRAME+21*16+5*$SIZE_T`($sp)
+	addi		$sp,$sp,`$FRAME+21*16+6*$SIZE_T`
+	blr
+	.long		0
+	.byte		0,12,0x04,1,0x80,6,6,0
+	.long		0
+
+.align	5
+_aesp8_xts_dec5x:
+	vncipher	$out0,$out0,v24
+	vncipher	$out1,$out1,v24
+	vncipher	$out2,$out2,v24
+	vncipher	$out3,$out3,v24
+	vncipher	$out4,$out4,v24
+	lvx		v24,$x20,$key_		# round[3]
+	addi		$key_,$key_,0x20
+
+	vncipher	$out0,$out0,v25
+	vncipher	$out1,$out1,v25
+	vncipher	$out2,$out2,v25
+	vncipher	$out3,$out3,v25
+	vncipher	$out4,$out4,v25
+	lvx		v25,$x10,$key_		# round[4]
+	bdnz		_aesp8_xts_dec5x
+
+	subi		r0,$taillen,1
+	vncipher	$out0,$out0,v24
+	vncipher	$out1,$out1,v24
+	vncipher	$out2,$out2,v24
+	vncipher	$out3,$out3,v24
+	vncipher	$out4,$out4,v24
+
+	andi.		r0,r0,16
+	cmpwi		$taillen,0
+	vncipher	$out0,$out0,v25
+	vncipher	$out1,$out1,v25
+	vncipher	$out2,$out2,v25
+	vncipher	$out3,$out3,v25
+	vncipher	$out4,$out4,v25
+	 vxor		$twk0,$twk0,v31
+
+	sub		$inp,$inp,r0
+	vncipher	$out0,$out0,v26
+	vncipher	$out1,$out1,v26
+	vncipher	$out2,$out2,v26
+	vncipher	$out3,$out3,v26
+	vncipher	$out4,$out4,v26
+	 vxor		$in1,$twk1,v31
+
+	vncipher	$out0,$out0,v27
+	lvx_u		$in0,0,$inp
+	vncipher	$out1,$out1,v27
+	vncipher	$out2,$out2,v27
+	vncipher	$out3,$out3,v27
+	vncipher	$out4,$out4,v27
+	 vxor		$in2,$twk2,v31
+
+	addi		$key_,$sp,$FRAME+15	# rewind $key_
+	vncipher	$out0,$out0,v28
+	vncipher	$out1,$out1,v28
+	vncipher	$out2,$out2,v28
+	vncipher	$out3,$out3,v28
+	vncipher	$out4,$out4,v28
+	lvx		v24,$x00,$key_		# re-pre-load round[1]
+	 vxor		$in3,$twk3,v31
+
+	vncipher	$out0,$out0,v29
+	le?vperm	$in0,$in0,$in0,$leperm
+	vncipher	$out1,$out1,v29
+	vncipher	$out2,$out2,v29
+	vncipher	$out3,$out3,v29
+	vncipher	$out4,$out4,v29
+	lvx		v25,$x10,$key_		# re-pre-load round[2]
+	 vxor		$in4,$twk4,v31
+
+	vncipher	$out0,$out0,v30
+	vncipher	$out1,$out1,v30
+	vncipher	$out2,$out2,v30
+	vncipher	$out3,$out3,v30
+	vncipher	$out4,$out4,v30
+
+	vncipherlast	$out0,$out0,$twk0
+	vncipherlast	$out1,$out1,$in1
+	vncipherlast	$out2,$out2,$in2
+	vncipherlast	$out3,$out3,$in3
+	vncipherlast	$out4,$out4,$in4
+	mtctr		$rounds
+	blr
+        .long   	0
+        .byte   	0,12,0x14,0,0,0,0,0
+___
+}}	}}}
+
+my $consts=1;
+foreach(split("\n",$code)) {
+        s/\`([^\`]*)\`/eval($1)/geo;
+
+	# constants table endian-specific conversion
+	if ($consts && m/\.(long|byte)\s+(.+)\s+(\?[a-z]*)$/o) {
+	    my $conv=$3;
+	    my @bytes=();
+
+	    # convert to endian-agnostic format
+	    if ($1 eq "long") {
+	      foreach (split(/,\s*/,$2)) {
+		my $l = /^0/?oct:int;
+		push @bytes,($l>>24)&0xff,($l>>16)&0xff,($l>>8)&0xff,$l&0xff;
+	      }
+	    } else {
+		@bytes = map(/^0/?oct:int,split(/,\s*/,$2));
+	    }
+
+	    # little-endian conversion
+	    if ($flavour =~ /le$/o) {
+		SWITCH: for($conv)  {
+		    /\?inv/ && do   { @bytes=map($_^0xf,@bytes); last; };
+		    /\?rev/ && do   { @bytes=reverse(@bytes);    last; }; 
+		}
+	    }
+
+	    #emit
+	    print ".byte\t",join(',',map (sprintf("0x%02x",$_),@bytes)),"\n";
+	    next;
+	}
+	$consts=0 if (m/Lconsts:/o);	# end of table
+
+	# instructions prefixed with '?' are endian-specific and need
+	# to be adjusted accordingly...
+	if ($flavour =~ /le$/o) {	# little-endian
+	    s/le\?//o		or
+	    s/be\?/#be#/o	or
+	    s/\?lvsr/lvsl/o	or
+	    s/\?lvsl/lvsr/o	or
+	    s/\?(vperm\s+v[0-9]+,\s*)(v[0-9]+,\s*)(v[0-9]+,\s*)(v[0-9]+)/$1$3$2$4/o or
+	    s/\?(vsldoi\s+v[0-9]+,\s*)(v[0-9]+,)\s*(v[0-9]+,\s*)([0-9]+)/$1$3$2 16-$4/o or
+	    s/\?(vspltw\s+v[0-9]+,\s*)(v[0-9]+,)\s*([0-9])/$1$2 3-$3/o;
+	} else {			# big-endian
+	    s/le\?/#le#/o	or
+	    s/be\?//o		or
+	    s/\?([a-z]+)/$1/o;
+	}
+
+        print $_,"\n";
+}
+
+close STDOUT;
diff --git a/crypto/aes/asm/aest4-sparcv9.pl b/crypto/aes/asm/aest4-sparcv9.pl
new file mode 100644
index 000000000..bf479c60a
--- /dev/null
+++ b/crypto/aes/asm/aest4-sparcv9.pl
@@ -0,0 +1,929 @@
+#! /usr/bin/env perl
+# Copyright 2012-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+
+# ====================================================================
+# Written by David S. Miller  and Andy Polyakov
+# . The module is licensed under 2-clause BSD
+# license. October 2012. All rights reserved.
+# ====================================================================
+
+######################################################################
+# AES for SPARC T4.
+#
+# AES round instructions complete in 3 cycles and can be issued every
+# cycle. It means that round calculations should take 4*rounds cycles,
+# because any given round instruction depends on result of *both*
+# previous instructions:
+#
+#	|0 |1 |2 |3 |4
+#	|01|01|01|
+#	   |23|23|23|
+#	            |01|01|...
+#	               |23|...
+#
+# Provided that fxor [with IV] takes 3 cycles to complete, critical
+# path length for CBC encrypt would be 3+4*rounds, or in other words
+# it should process one byte in at least (3+4*rounds)/16 cycles. This
+# estimate doesn't account for "collateral" instructions, such as
+# fetching input from memory, xor-ing it with zero-round key and
+# storing the result. Yet, *measured* performance [for data aligned
+# at 64-bit boundary!] deviates from this equation by less than 0.5%:
+#
+#		128-bit key	192-		256-
+# CBC encrypt	2.70/2.90(*)	3.20/3.40	3.70/3.90
+#			 (*) numbers after slash are for
+#			     misaligned data.
+#
+# Out-of-order execution logic managed to fully overlap "collateral"
+# instructions with those on critical path. Amazing!
+#
+# As with Intel AES-NI, question is if it's possible to improve
+# performance of parallelizeable modes by interleaving round
+# instructions. Provided round instruction latency and throughput
+# optimal interleave factor is 2. But can we expect 2x performance
+# improvement? Well, as round instructions can be issued one per
+# cycle, they don't saturate the 2-way issue pipeline and therefore
+# there is room for "collateral" calculations... Yet, 2x speed-up
+# over CBC encrypt remains unattaintable:
+#
+#		128-bit key	192-		256-
+# CBC decrypt	1.64/2.11	1.89/2.37	2.23/2.61
+# CTR		1.64/2.08(*)	1.89/2.33	2.23/2.61
+#			 (*) numbers after slash are for
+#			     misaligned data.
+#
+# Estimates based on amount of instructions under assumption that
+# round instructions are not pairable with any other instruction
+# suggest that latter is the actual case and pipeline runs
+# underutilized. It should be noted that T4 out-of-order execution
+# logic is so capable that performance gain from 2x interleave is
+# not even impressive, ~7-13% over non-interleaved code, largest
+# for 256-bit keys.
+
+# To anchor to something else, software implementation processes
+# one byte in 29 cycles with 128-bit key on same processor. Intel
+# Sandy Bridge encrypts byte in 5.07 cycles in CBC mode and decrypts
+# in 0.93, naturally with AES-NI.
+
+$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+push(@INC,"${dir}","${dir}../../perlasm");
+require "sparcv9_modes.pl";
+
+$output = pop;
+open STDOUT,">$output";
+
+$::evp=1;	# if $evp is set to 0, script generates module with
+# AES_[en|de]crypt, AES_set_[en|de]crypt_key and AES_cbc_encrypt entry
+# points. These however are not fully compatible with openssl/aes.h,
+# because they expect AES_KEY to be aligned at 64-bit boundary. When
+# used through EVP, alignment is arranged at EVP layer. Second thing
+# that is arranged by EVP is at least 32-bit alignment of IV.
+
+######################################################################
+# single-round subroutines
+#
+{
+my ($inp,$out,$key,$rounds,$tmp,$mask)=map("%o$_",(0..5));
+
+$code.=<<___;
+#include "sparc_arch.h"
+
+#ifdef	__arch64__
+.register	%g2,#scratch
+.register	%g3,#scratch
+#endif
+
+.text
+
+.globl	aes_t4_encrypt
+.align	32
+aes_t4_encrypt:
+	andcc		$inp, 7, %g1		! is input aligned?
+	andn		$inp, 7, $inp
+
+	ldx		[$key + 0], %g4
+	ldx		[$key + 8], %g5
+
+	ldx		[$inp + 0], %o4
+	bz,pt		%icc, 1f
+	ldx		[$inp + 8], %o5
+	ldx		[$inp + 16], $inp
+	sll		%g1, 3, %g1
+	sub		%g0, %g1, %o3
+	sllx		%o4, %g1, %o4
+	sllx		%o5, %g1, %g1
+	srlx		%o5, %o3, %o5
+	srlx		$inp, %o3, %o3
+	or		%o5, %o4, %o4
+	or		%o3, %g1, %o5
+1:
+	ld		[$key + 240], $rounds
+	ldd		[$key + 16], %f12
+	ldd		[$key + 24], %f14
+	xor		%g4, %o4, %o4
+	xor		%g5, %o5, %o5
+	movxtod		%o4, %f0
+	movxtod		%o5, %f2
+	srl		$rounds, 1, $rounds
+	ldd		[$key + 32], %f16
+	sub		$rounds, 1, $rounds
+	ldd		[$key + 40], %f18
+	add		$key, 48, $key
+
+.Lenc:
+	aes_eround01	%f12, %f0, %f2, %f4
+	aes_eround23	%f14, %f0, %f2, %f2
+	ldd		[$key + 0], %f12
+	ldd		[$key + 8], %f14
+	sub		$rounds,1,$rounds
+	aes_eround01	%f16, %f4, %f2, %f0
+	aes_eround23	%f18, %f4, %f2, %f2
+	ldd		[$key + 16], %f16
+	ldd		[$key + 24], %f18
+	brnz,pt		$rounds, .Lenc
+	add		$key, 32, $key
+
+	andcc		$out, 7, $tmp		! is output aligned?
+	aes_eround01	%f12, %f0, %f2, %f4
+	aes_eround23	%f14, %f0, %f2, %f2
+	aes_eround01_l	%f16, %f4, %f2, %f0
+	aes_eround23_l	%f18, %f4, %f2, %f2
+
+	bnz,pn		%icc, 2f
+	nop
+
+	std		%f0, [$out + 0]
+	retl
+	std		%f2, [$out + 8]
+
+2:	alignaddrl	$out, %g0, $out
+	mov		0xff, $mask
+	srl		$mask, $tmp, $mask
+
+	faligndata	%f0, %f0, %f4
+	faligndata	%f0, %f2, %f6
+	faligndata	%f2, %f2, %f8
+
+	stda		%f4, [$out + $mask]0xc0	! partial store
+	std		%f6, [$out + 8]
+	add		$out, 16, $out
+	orn		%g0, $mask, $mask
+	retl
+	stda		%f8, [$out + $mask]0xc0	! partial store
+.type	aes_t4_encrypt,#function
+.size	aes_t4_encrypt,.-aes_t4_encrypt
+
+.globl	aes_t4_decrypt
+.align	32
+aes_t4_decrypt:
+	andcc		$inp, 7, %g1		! is input aligned?
+	andn		$inp, 7, $inp
+
+	ldx		[$key + 0], %g4
+	ldx		[$key + 8], %g5
+
+	ldx		[$inp + 0], %o4
+	bz,pt		%icc, 1f
+	ldx		[$inp + 8], %o5
+	ldx		[$inp + 16], $inp
+	sll		%g1, 3, %g1
+	sub		%g0, %g1, %o3
+	sllx		%o4, %g1, %o4
+	sllx		%o5, %g1, %g1
+	srlx		%o5, %o3, %o5
+	srlx		$inp, %o3, %o3
+	or		%o5, %o4, %o4
+	or		%o3, %g1, %o5
+1:
+	ld		[$key + 240], $rounds
+	ldd		[$key + 16], %f12
+	ldd		[$key + 24], %f14
+	xor		%g4, %o4, %o4
+	xor		%g5, %o5, %o5
+	movxtod		%o4, %f0
+	movxtod		%o5, %f2
+	srl		$rounds, 1, $rounds
+	ldd		[$key + 32], %f16
+	sub		$rounds, 1, $rounds
+	ldd		[$key + 40], %f18
+	add		$key, 48, $key
+
+.Ldec:
+	aes_dround01	%f12, %f0, %f2, %f4
+	aes_dround23	%f14, %f0, %f2, %f2
+	ldd		[$key + 0], %f12
+	ldd		[$key + 8], %f14
+	sub		$rounds,1,$rounds
+	aes_dround01	%f16, %f4, %f2, %f0
+	aes_dround23	%f18, %f4, %f2, %f2
+	ldd		[$key + 16], %f16
+	ldd		[$key + 24], %f18
+	brnz,pt		$rounds, .Ldec
+	add		$key, 32, $key
+
+	andcc		$out, 7, $tmp		! is output aligned?
+	aes_dround01	%f12, %f0, %f2, %f4
+	aes_dround23	%f14, %f0, %f2, %f2
+	aes_dround01_l	%f16, %f4, %f2, %f0
+	aes_dround23_l	%f18, %f4, %f2, %f2
+
+	bnz,pn		%icc, 2f
+	nop
+
+	std		%f0, [$out + 0]
+	retl
+	std		%f2, [$out + 8]
+
+2:	alignaddrl	$out, %g0, $out
+	mov		0xff, $mask
+	srl		$mask, $tmp, $mask
+
+	faligndata	%f0, %f0, %f4
+	faligndata	%f0, %f2, %f6
+	faligndata	%f2, %f2, %f8
+
+	stda		%f4, [$out + $mask]0xc0	! partial store
+	std		%f6, [$out + 8]
+	add		$out, 16, $out
+	orn		%g0, $mask, $mask
+	retl
+	stda		%f8, [$out + $mask]0xc0	! partial store
+.type	aes_t4_decrypt,#function
+.size	aes_t4_decrypt,.-aes_t4_decrypt
+___
+}
+
+######################################################################
+# key setup subroutines
+#
+{
+my ($inp,$bits,$out,$tmp)=map("%o$_",(0..5));
+$code.=<<___;
+.globl	aes_t4_set_encrypt_key
+.align	32
+aes_t4_set_encrypt_key:
+.Lset_encrypt_key:
+	and		$inp, 7, $tmp
+	alignaddr	$inp, %g0, $inp
+	cmp		$bits, 192
+	ldd		[$inp + 0], %f0
+	bl,pt		%icc,.L128
+	ldd		[$inp + 8], %f2
+
+	be,pt		%icc,.L192
+	ldd		[$inp + 16], %f4
+	brz,pt		$tmp, .L256aligned
+	ldd		[$inp + 24], %f6
+
+	ldd		[$inp + 32], %f8
+	faligndata	%f0, %f2, %f0
+	faligndata	%f2, %f4, %f2
+	faligndata	%f4, %f6, %f4
+	faligndata	%f6, %f8, %f6
+.L256aligned:
+___
+for ($i=0; $i<6; $i++) {
+    $code.=<<___;
+	std		%f0, [$out + `32*$i+0`]
+	aes_kexpand1	%f0, %f6, $i, %f0
+	std		%f2, [$out + `32*$i+8`]
+	aes_kexpand2	%f2, %f0, %f2
+	std		%f4, [$out + `32*$i+16`]
+	aes_kexpand0	%f4, %f2, %f4
+	std		%f6, [$out + `32*$i+24`]
+	aes_kexpand2	%f6, %f4, %f6
+___
+}
+$code.=<<___;
+	std		%f0, [$out + `32*$i+0`]
+	aes_kexpand1	%f0, %f6, $i, %f0
+	std		%f2, [$out + `32*$i+8`]
+	aes_kexpand2	%f2, %f0, %f2
+	std		%f4, [$out + `32*$i+16`]
+	std		%f6, [$out + `32*$i+24`]
+	std		%f0, [$out + `32*$i+32`]
+	std		%f2, [$out + `32*$i+40`]
+
+	mov		14, $tmp
+	st		$tmp, [$out + 240]
+	retl
+	xor		%o0, %o0, %o0
+
+.align	16
+.L192:
+	brz,pt		$tmp, .L192aligned
+	nop
+
+	ldd		[$inp + 24], %f6
+	faligndata	%f0, %f2, %f0
+	faligndata	%f2, %f4, %f2
+	faligndata	%f4, %f6, %f4
+.L192aligned:
+___
+for ($i=0; $i<7; $i++) {
+    $code.=<<___;
+	std		%f0, [$out + `24*$i+0`]
+	aes_kexpand1	%f0, %f4, $i, %f0
+	std		%f2, [$out + `24*$i+8`]
+	aes_kexpand2	%f2, %f0, %f2
+	std		%f4, [$out + `24*$i+16`]
+	aes_kexpand2	%f4, %f2, %f4
+___
+}
+$code.=<<___;
+	std		%f0, [$out + `24*$i+0`]
+	aes_kexpand1	%f0, %f4, $i, %f0
+	std		%f2, [$out + `24*$i+8`]
+	aes_kexpand2	%f2, %f0, %f2
+	std		%f4, [$out + `24*$i+16`]
+	std		%f0, [$out + `24*$i+24`]
+	std		%f2, [$out + `24*$i+32`]
+
+	mov		12, $tmp
+	st		$tmp, [$out + 240]
+	retl
+	xor		%o0, %o0, %o0
+
+.align	16
+.L128:
+	brz,pt		$tmp, .L128aligned
+	nop
+
+	ldd		[$inp + 16], %f4
+	faligndata	%f0, %f2, %f0
+	faligndata	%f2, %f4, %f2
+.L128aligned:
+___
+for ($i=0; $i<10; $i++) {
+    $code.=<<___;
+	std		%f0, [$out + `16*$i+0`]
+	aes_kexpand1	%f0, %f2, $i, %f0
+	std		%f2, [$out + `16*$i+8`]
+	aes_kexpand2	%f2, %f0, %f2
+___
+}
+$code.=<<___;
+	std		%f0, [$out + `16*$i+0`]
+	std		%f2, [$out + `16*$i+8`]
+
+	mov		10, $tmp
+	st		$tmp, [$out + 240]
+	retl
+	xor		%o0, %o0, %o0
+.type	aes_t4_set_encrypt_key,#function
+.size	aes_t4_set_encrypt_key,.-aes_t4_set_encrypt_key
+
+.globl	aes_t4_set_decrypt_key
+.align	32
+aes_t4_set_decrypt_key:
+	mov		%o7, %o5
+	call		.Lset_encrypt_key
+	nop
+
+	mov		%o5, %o7
+	sll		$tmp, 4, $inp		! $tmp is number of rounds
+	add		$tmp, 2, $tmp
+	add		$out, $inp, $inp	! $inp=$out+16*rounds
+	srl		$tmp, 2, $tmp		! $tmp=(rounds+2)/4
+
+.Lkey_flip:
+	ldd		[$out + 0],  %f0
+	ldd		[$out + 8],  %f2
+	ldd		[$out + 16], %f4
+	ldd		[$out + 24], %f6
+	ldd		[$inp + 0],  %f8
+	ldd		[$inp + 8],  %f10
+	ldd		[$inp - 16], %f12
+	ldd		[$inp - 8],  %f14
+	sub		$tmp, 1, $tmp
+	std		%f0, [$inp + 0]
+	std		%f2, [$inp + 8]
+	std		%f4, [$inp - 16]
+	std		%f6, [$inp - 8]
+	std		%f8, [$out + 0]
+	std		%f10, [$out + 8]
+	std		%f12, [$out + 16]
+	std		%f14, [$out + 24]
+	add		$out, 32, $out
+	brnz		$tmp, .Lkey_flip
+	sub		$inp, 32, $inp
+
+	retl
+	xor		%o0, %o0, %o0
+.type	aes_t4_set_decrypt_key,#function
+.size	aes_t4_set_decrypt_key,.-aes_t4_set_decrypt_key
+___
+}
+
+{{{
+my ($inp,$out,$len,$key,$ivec,$enc)=map("%i$_",(0..5));
+my ($ileft,$iright,$ooff,$omask,$ivoff)=map("%l$_",(1..7));
+
+$code.=<<___;
+.align	32
+_aes128_encrypt_1x:
+___
+for ($i=0; $i<4; $i++) {
+    $code.=<<___;
+	aes_eround01	%f`16+8*$i+0`, %f0, %f2, %f4
+	aes_eround23	%f`16+8*$i+2`, %f0, %f2, %f2
+	aes_eround01	%f`16+8*$i+4`, %f4, %f2, %f0
+	aes_eround23	%f`16+8*$i+6`, %f4, %f2, %f2
+___
+}
+$code.=<<___;
+	aes_eround01	%f48, %f0, %f2, %f4
+	aes_eround23	%f50, %f0, %f2, %f2
+	aes_eround01_l	%f52, %f4, %f2, %f0
+	retl
+	aes_eround23_l	%f54, %f4, %f2, %f2
+.type	_aes128_encrypt_1x,#function
+.size	_aes128_encrypt_1x,.-_aes128_encrypt_1x
+
+.align	32
+_aes128_encrypt_2x:
+___
+for ($i=0; $i<4; $i++) {
+    $code.=<<___;
+	aes_eround01	%f`16+8*$i+0`, %f0, %f2, %f8
+	aes_eround23	%f`16+8*$i+2`, %f0, %f2, %f2
+	aes_eround01	%f`16+8*$i+0`, %f4, %f6, %f10
+	aes_eround23	%f`16+8*$i+2`, %f4, %f6, %f6
+	aes_eround01	%f`16+8*$i+4`, %f8, %f2, %f0
+	aes_eround23	%f`16+8*$i+6`, %f8, %f2, %f2
+	aes_eround01	%f`16+8*$i+4`, %f10, %f6, %f4
+	aes_eround23	%f`16+8*$i+6`, %f10, %f6, %f6
+___
+}
+$code.=<<___;
+	aes_eround01	%f48, %f0, %f2, %f8
+	aes_eround23	%f50, %f0, %f2, %f2
+	aes_eround01	%f48, %f4, %f6, %f10
+	aes_eround23	%f50, %f4, %f6, %f6
+	aes_eround01_l	%f52, %f8, %f2, %f0
+	aes_eround23_l	%f54, %f8, %f2, %f2
+	aes_eround01_l	%f52, %f10, %f6, %f4
+	retl
+	aes_eround23_l	%f54, %f10, %f6, %f6
+.type	_aes128_encrypt_2x,#function
+.size	_aes128_encrypt_2x,.-_aes128_encrypt_2x
+
+.align	32
+_aes128_loadkey:
+	ldx		[$key + 0], %g4
+	ldx		[$key + 8], %g5
+___
+for ($i=2; $i<22;$i++) {			# load key schedule
+    $code.=<<___;
+	ldd		[$key + `8*$i`], %f`12+2*$i`
+___
+}
+$code.=<<___;
+	retl
+	nop
+.type	_aes128_loadkey,#function
+.size	_aes128_loadkey,.-_aes128_loadkey
+_aes128_load_enckey=_aes128_loadkey
+_aes128_load_deckey=_aes128_loadkey
+
+___
+
+&alg_cbc_encrypt_implement("aes",128);
+if ($::evp) {
+    &alg_ctr32_implement("aes",128);
+    &alg_xts_implement("aes",128,"en");
+    &alg_xts_implement("aes",128,"de");
+}
+&alg_cbc_decrypt_implement("aes",128);
+
+$code.=<<___;
+.align	32
+_aes128_decrypt_1x:
+___
+for ($i=0; $i<4; $i++) {
+    $code.=<<___;
+	aes_dround01	%f`16+8*$i+0`, %f0, %f2, %f4
+	aes_dround23	%f`16+8*$i+2`, %f0, %f2, %f2
+	aes_dround01	%f`16+8*$i+4`, %f4, %f2, %f0
+	aes_dround23	%f`16+8*$i+6`, %f4, %f2, %f2
+___
+}
+$code.=<<___;
+	aes_dround01	%f48, %f0, %f2, %f4
+	aes_dround23	%f50, %f0, %f2, %f2
+	aes_dround01_l	%f52, %f4, %f2, %f0
+	retl
+	aes_dround23_l	%f54, %f4, %f2, %f2
+.type	_aes128_decrypt_1x,#function
+.size	_aes128_decrypt_1x,.-_aes128_decrypt_1x
+
+.align	32
+_aes128_decrypt_2x:
+___
+for ($i=0; $i<4; $i++) {
+    $code.=<<___;
+	aes_dround01	%f`16+8*$i+0`, %f0, %f2, %f8
+	aes_dround23	%f`16+8*$i+2`, %f0, %f2, %f2
+	aes_dround01	%f`16+8*$i+0`, %f4, %f6, %f10
+	aes_dround23	%f`16+8*$i+2`, %f4, %f6, %f6
+	aes_dround01	%f`16+8*$i+4`, %f8, %f2, %f0
+	aes_dround23	%f`16+8*$i+6`, %f8, %f2, %f2
+	aes_dround01	%f`16+8*$i+4`, %f10, %f6, %f4
+	aes_dround23	%f`16+8*$i+6`, %f10, %f6, %f6
+___
+}
+$code.=<<___;
+	aes_dround01	%f48, %f0, %f2, %f8
+	aes_dround23	%f50, %f0, %f2, %f2
+	aes_dround01	%f48, %f4, %f6, %f10
+	aes_dround23	%f50, %f4, %f6, %f6
+	aes_dround01_l	%f52, %f8, %f2, %f0
+	aes_dround23_l	%f54, %f8, %f2, %f2
+	aes_dround01_l	%f52, %f10, %f6, %f4
+	retl
+	aes_dround23_l	%f54, %f10, %f6, %f6
+.type	_aes128_decrypt_2x,#function
+.size	_aes128_decrypt_2x,.-_aes128_decrypt_2x
+___
+
+$code.=<<___;
+.align	32
+_aes192_encrypt_1x:
+___
+for ($i=0; $i<5; $i++) {
+    $code.=<<___;
+	aes_eround01	%f`16+8*$i+0`, %f0, %f2, %f4
+	aes_eround23	%f`16+8*$i+2`, %f0, %f2, %f2
+	aes_eround01	%f`16+8*$i+4`, %f4, %f2, %f0
+	aes_eround23	%f`16+8*$i+6`, %f4, %f2, %f2
+___
+}
+$code.=<<___;
+	aes_eround01	%f56, %f0, %f2, %f4
+	aes_eround23	%f58, %f0, %f2, %f2
+	aes_eround01_l	%f60, %f4, %f2, %f0
+	retl
+	aes_eround23_l	%f62, %f4, %f2, %f2
+.type	_aes192_encrypt_1x,#function
+.size	_aes192_encrypt_1x,.-_aes192_encrypt_1x
+
+.align	32
+_aes192_encrypt_2x:
+___
+for ($i=0; $i<5; $i++) {
+    $code.=<<___;
+	aes_eround01	%f`16+8*$i+0`, %f0, %f2, %f8
+	aes_eround23	%f`16+8*$i+2`, %f0, %f2, %f2
+	aes_eround01	%f`16+8*$i+0`, %f4, %f6, %f10
+	aes_eround23	%f`16+8*$i+2`, %f4, %f6, %f6
+	aes_eround01	%f`16+8*$i+4`, %f8, %f2, %f0
+	aes_eround23	%f`16+8*$i+6`, %f8, %f2, %f2
+	aes_eround01	%f`16+8*$i+4`, %f10, %f6, %f4
+	aes_eround23	%f`16+8*$i+6`, %f10, %f6, %f6
+___
+}
+$code.=<<___;
+	aes_eround01	%f56, %f0, %f2, %f8
+	aes_eround23	%f58, %f0, %f2, %f2
+	aes_eround01	%f56, %f4, %f6, %f10
+	aes_eround23	%f58, %f4, %f6, %f6
+	aes_eround01_l	%f60, %f8, %f2, %f0
+	aes_eround23_l	%f62, %f8, %f2, %f2
+	aes_eround01_l	%f60, %f10, %f6, %f4
+	retl
+	aes_eround23_l	%f62, %f10, %f6, %f6
+.type	_aes192_encrypt_2x,#function
+.size	_aes192_encrypt_2x,.-_aes192_encrypt_2x
+
+.align	32
+_aes256_encrypt_1x:
+	aes_eround01	%f16, %f0, %f2, %f4
+	aes_eround23	%f18, %f0, %f2, %f2
+	ldd		[$key + 208], %f16
+	ldd		[$key + 216], %f18
+	aes_eround01	%f20, %f4, %f2, %f0
+	aes_eround23	%f22, %f4, %f2, %f2
+	ldd		[$key + 224], %f20
+	ldd		[$key + 232], %f22
+___
+for ($i=1; $i<6; $i++) {
+    $code.=<<___;
+	aes_eround01	%f`16+8*$i+0`, %f0, %f2, %f4
+	aes_eround23	%f`16+8*$i+2`, %f0, %f2, %f2
+	aes_eround01	%f`16+8*$i+4`, %f4, %f2, %f0
+	aes_eround23	%f`16+8*$i+6`, %f4, %f2, %f2
+___
+}
+$code.=<<___;
+	aes_eround01	%f16, %f0, %f2, %f4
+	aes_eround23	%f18, %f0, %f2, %f2
+	ldd		[$key + 16], %f16
+	ldd		[$key + 24], %f18
+	aes_eround01_l	%f20, %f4, %f2, %f0
+	aes_eround23_l	%f22, %f4, %f2, %f2
+	ldd		[$key + 32], %f20
+	retl
+	ldd		[$key + 40], %f22
+.type	_aes256_encrypt_1x,#function
+.size	_aes256_encrypt_1x,.-_aes256_encrypt_1x
+
+.align	32
+_aes256_encrypt_2x:
+	aes_eround01	%f16, %f0, %f2, %f8
+	aes_eround23	%f18, %f0, %f2, %f2
+	aes_eround01	%f16, %f4, %f6, %f10
+	aes_eround23	%f18, %f4, %f6, %f6
+	ldd		[$key + 208], %f16
+	ldd		[$key + 216], %f18
+	aes_eround01	%f20, %f8, %f2, %f0
+	aes_eround23	%f22, %f8, %f2, %f2
+	aes_eround01	%f20, %f10, %f6, %f4
+	aes_eround23	%f22, %f10, %f6, %f6
+	ldd		[$key + 224], %f20
+	ldd		[$key + 232], %f22
+___
+for ($i=1; $i<6; $i++) {
+    $code.=<<___;
+	aes_eround01	%f`16+8*$i+0`, %f0, %f2, %f8
+	aes_eround23	%f`16+8*$i+2`, %f0, %f2, %f2
+	aes_eround01	%f`16+8*$i+0`, %f4, %f6, %f10
+	aes_eround23	%f`16+8*$i+2`, %f4, %f6, %f6
+	aes_eround01	%f`16+8*$i+4`, %f8, %f2, %f0
+	aes_eround23	%f`16+8*$i+6`, %f8, %f2, %f2
+	aes_eround01	%f`16+8*$i+4`, %f10, %f6, %f4
+	aes_eround23	%f`16+8*$i+6`, %f10, %f6, %f6
+___
+}
+$code.=<<___;
+	aes_eround01	%f16, %f0, %f2, %f8
+	aes_eround23	%f18, %f0, %f2, %f2
+	aes_eround01	%f16, %f4, %f6, %f10
+	aes_eround23	%f18, %f4, %f6, %f6
+	ldd		[$key + 16], %f16
+	ldd		[$key + 24], %f18
+	aes_eround01_l	%f20, %f8, %f2, %f0
+	aes_eround23_l	%f22, %f8, %f2, %f2
+	aes_eround01_l	%f20, %f10, %f6, %f4
+	aes_eround23_l	%f22, %f10, %f6, %f6
+	ldd		[$key + 32], %f20
+	retl
+	ldd		[$key + 40], %f22
+.type	_aes256_encrypt_2x,#function
+.size	_aes256_encrypt_2x,.-_aes256_encrypt_2x
+
+.align	32
+_aes192_loadkey:
+	ldx		[$key + 0], %g4
+	ldx		[$key + 8], %g5
+___
+for ($i=2; $i<26;$i++) {			# load key schedule
+    $code.=<<___;
+	ldd		[$key + `8*$i`], %f`12+2*$i`
+___
+}
+$code.=<<___;
+	retl
+	nop
+.type	_aes192_loadkey,#function
+.size	_aes192_loadkey,.-_aes192_loadkey
+_aes256_loadkey=_aes192_loadkey
+_aes192_load_enckey=_aes192_loadkey
+_aes192_load_deckey=_aes192_loadkey
+_aes256_load_enckey=_aes192_loadkey
+_aes256_load_deckey=_aes192_loadkey
+___
+
+&alg_cbc_encrypt_implement("aes",256);
+&alg_cbc_encrypt_implement("aes",192);
+if ($::evp) {
+    &alg_ctr32_implement("aes",256);
+    &alg_xts_implement("aes",256,"en");
+    &alg_xts_implement("aes",256,"de");
+    &alg_ctr32_implement("aes",192);
+}
+&alg_cbc_decrypt_implement("aes",192);
+&alg_cbc_decrypt_implement("aes",256);
+
+$code.=<<___;
+.align	32
+_aes256_decrypt_1x:
+	aes_dround01	%f16, %f0, %f2, %f4
+	aes_dround23	%f18, %f0, %f2, %f2
+	ldd		[$key + 208], %f16
+	ldd		[$key + 216], %f18
+	aes_dround01	%f20, %f4, %f2, %f0
+	aes_dround23	%f22, %f4, %f2, %f2
+	ldd		[$key + 224], %f20
+	ldd		[$key + 232], %f22
+___
+for ($i=1; $i<6; $i++) {
+    $code.=<<___;
+	aes_dround01	%f`16+8*$i+0`, %f0, %f2, %f4
+	aes_dround23	%f`16+8*$i+2`, %f0, %f2, %f2
+	aes_dround01	%f`16+8*$i+4`, %f4, %f2, %f0
+	aes_dround23	%f`16+8*$i+6`, %f4, %f2, %f2
+___
+}
+$code.=<<___;
+	aes_dround01	%f16, %f0, %f2, %f4
+	aes_dround23	%f18, %f0, %f2, %f2
+	ldd		[$key + 16], %f16
+	ldd		[$key + 24], %f18
+	aes_dround01_l	%f20, %f4, %f2, %f0
+	aes_dround23_l	%f22, %f4, %f2, %f2
+	ldd		[$key + 32], %f20
+	retl
+	ldd		[$key + 40], %f22
+.type	_aes256_decrypt_1x,#function
+.size	_aes256_decrypt_1x,.-_aes256_decrypt_1x
+
+.align	32
+_aes256_decrypt_2x:
+	aes_dround01	%f16, %f0, %f2, %f8
+	aes_dround23	%f18, %f0, %f2, %f2
+	aes_dround01	%f16, %f4, %f6, %f10
+	aes_dround23	%f18, %f4, %f6, %f6
+	ldd		[$key + 208], %f16
+	ldd		[$key + 216], %f18
+	aes_dround01	%f20, %f8, %f2, %f0
+	aes_dround23	%f22, %f8, %f2, %f2
+	aes_dround01	%f20, %f10, %f6, %f4
+	aes_dround23	%f22, %f10, %f6, %f6
+	ldd		[$key + 224], %f20
+	ldd		[$key + 232], %f22
+___
+for ($i=1; $i<6; $i++) {
+    $code.=<<___;
+	aes_dround01	%f`16+8*$i+0`, %f0, %f2, %f8
+	aes_dround23	%f`16+8*$i+2`, %f0, %f2, %f2
+	aes_dround01	%f`16+8*$i+0`, %f4, %f6, %f10
+	aes_dround23	%f`16+8*$i+2`, %f4, %f6, %f6
+	aes_dround01	%f`16+8*$i+4`, %f8, %f2, %f0
+	aes_dround23	%f`16+8*$i+6`, %f8, %f2, %f2
+	aes_dround01	%f`16+8*$i+4`, %f10, %f6, %f4
+	aes_dround23	%f`16+8*$i+6`, %f10, %f6, %f6
+___
+}
+$code.=<<___;
+	aes_dround01	%f16, %f0, %f2, %f8
+	aes_dround23	%f18, %f0, %f2, %f2
+	aes_dround01	%f16, %f4, %f6, %f10
+	aes_dround23	%f18, %f4, %f6, %f6
+	ldd		[$key + 16], %f16
+	ldd		[$key + 24], %f18
+	aes_dround01_l	%f20, %f8, %f2, %f0
+	aes_dround23_l	%f22, %f8, %f2, %f2
+	aes_dround01_l	%f20, %f10, %f6, %f4
+	aes_dround23_l	%f22, %f10, %f6, %f6
+	ldd		[$key + 32], %f20
+	retl
+	ldd		[$key + 40], %f22
+.type	_aes256_decrypt_2x,#function
+.size	_aes256_decrypt_2x,.-_aes256_decrypt_2x
+
+.align	32
+_aes192_decrypt_1x:
+___
+for ($i=0; $i<5; $i++) {
+    $code.=<<___;
+	aes_dround01	%f`16+8*$i+0`, %f0, %f2, %f4
+	aes_dround23	%f`16+8*$i+2`, %f0, %f2, %f2
+	aes_dround01	%f`16+8*$i+4`, %f4, %f2, %f0
+	aes_dround23	%f`16+8*$i+6`, %f4, %f2, %f2
+___
+}
+$code.=<<___;
+	aes_dround01	%f56, %f0, %f2, %f4
+	aes_dround23	%f58, %f0, %f2, %f2
+	aes_dround01_l	%f60, %f4, %f2, %f0
+	retl
+	aes_dround23_l	%f62, %f4, %f2, %f2
+.type	_aes192_decrypt_1x,#function
+.size	_aes192_decrypt_1x,.-_aes192_decrypt_1x
+
+.align	32
+_aes192_decrypt_2x:
+___
+for ($i=0; $i<5; $i++) {
+    $code.=<<___;
+	aes_dround01	%f`16+8*$i+0`, %f0, %f2, %f8
+	aes_dround23	%f`16+8*$i+2`, %f0, %f2, %f2
+	aes_dround01	%f`16+8*$i+0`, %f4, %f6, %f10
+	aes_dround23	%f`16+8*$i+2`, %f4, %f6, %f6
+	aes_dround01	%f`16+8*$i+4`, %f8, %f2, %f0
+	aes_dround23	%f`16+8*$i+6`, %f8, %f2, %f2
+	aes_dround01	%f`16+8*$i+4`, %f10, %f6, %f4
+	aes_dround23	%f`16+8*$i+6`, %f10, %f6, %f6
+___
+}
+$code.=<<___;
+	aes_dround01	%f56, %f0, %f2, %f8
+	aes_dround23	%f58, %f0, %f2, %f2
+	aes_dround01	%f56, %f4, %f6, %f10
+	aes_dround23	%f58, %f4, %f6, %f6
+	aes_dround01_l	%f60, %f8, %f2, %f0
+	aes_dround23_l	%f62, %f8, %f2, %f2
+	aes_dround01_l	%f60, %f10, %f6, %f4
+	retl
+	aes_dround23_l	%f62, %f10, %f6, %f6
+.type	_aes192_decrypt_2x,#function
+.size	_aes192_decrypt_2x,.-_aes192_decrypt_2x
+___
+}}}
+
+if (!$::evp) {
+$code.=<<___;
+.global	AES_encrypt
+AES_encrypt=aes_t4_encrypt
+.global	AES_decrypt
+AES_decrypt=aes_t4_decrypt
+.global	AES_set_encrypt_key
+.align	32
+AES_set_encrypt_key:
+	andcc		%o2, 7, %g0		! check alignment
+	bnz,a,pn	%icc, 1f
+	mov		-1, %o0
+	brz,a,pn	%o0, 1f
+	mov		-1, %o0
+	brz,a,pn	%o2, 1f
+	mov		-1, %o0
+	andncc		%o1, 0x1c0, %g0
+	bnz,a,pn	%icc, 1f
+	mov		-2, %o0
+	cmp		%o1, 128
+	bl,a,pn		%icc, 1f
+	mov		-2, %o0
+	b		aes_t4_set_encrypt_key
+	nop
+1:	retl
+	nop
+.type	AES_set_encrypt_key,#function
+.size	AES_set_encrypt_key,.-AES_set_encrypt_key
+
+.global	AES_set_decrypt_key
+.align	32
+AES_set_decrypt_key:
+	andcc		%o2, 7, %g0		! check alignment
+	bnz,a,pn	%icc, 1f
+	mov		-1, %o0
+	brz,a,pn	%o0, 1f
+	mov		-1, %o0
+	brz,a,pn	%o2, 1f
+	mov		-1, %o0
+	andncc		%o1, 0x1c0, %g0
+	bnz,a,pn	%icc, 1f
+	mov		-2, %o0
+	cmp		%o1, 128
+	bl,a,pn		%icc, 1f
+	mov		-2, %o0
+	b		aes_t4_set_decrypt_key
+	nop
+1:	retl
+	nop
+.type	AES_set_decrypt_key,#function
+.size	AES_set_decrypt_key,.-AES_set_decrypt_key
+___
+
+my ($inp,$out,$len,$key,$ivec,$enc)=map("%o$_",(0..5));
+
+$code.=<<___;
+.globl	AES_cbc_encrypt
+.align	32
+AES_cbc_encrypt:
+	ld		[$key + 240], %g1
+	nop
+	brz		$enc, .Lcbc_decrypt
+	cmp		%g1, 12
+
+	bl,pt		%icc, aes128_t4_cbc_encrypt
+	nop
+	be,pn		%icc, aes192_t4_cbc_encrypt
+	nop
+	ba		aes256_t4_cbc_encrypt
+	nop
+
+.Lcbc_decrypt:
+	bl,pt		%icc, aes128_t4_cbc_decrypt
+	nop
+	be,pn		%icc, aes192_t4_cbc_decrypt
+	nop
+	ba		aes256_t4_cbc_decrypt
+	nop
+.type	AES_cbc_encrypt,#function
+.size	AES_cbc_encrypt,.-AES_cbc_encrypt
+___
+}
+$code.=<<___;
+.asciz	"AES for SPARC T4, David S. Miller, Andy Polyakov"
+.align	4
+___
+
+&emit_assembler();
+
+close STDOUT;
diff --git a/crypto/aes/asm/aesv8-armx.pl b/crypto/aes/asm/aesv8-armx.pl
new file mode 100755
index 000000000..9246dbb43
--- /dev/null
+++ b/crypto/aes/asm/aesv8-armx.pl
@@ -0,0 +1,1005 @@
+#! /usr/bin/env perl
+# Copyright 2014-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+#
+# ====================================================================
+# Written by Andy Polyakov  for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+#
+# This module implements support for ARMv8 AES instructions. The
+# module is endian-agnostic in sense that it supports both big- and
+# little-endian cases. As does it support both 32- and 64-bit modes
+# of operation. Latter is achieved by limiting amount of utilized
+# registers to 16, which implies additional NEON load and integer
+# instructions. This has no effect on mighty Apple A7, where results
+# are literally equal to the theoretical estimates based on AES
+# instruction latencies and issue rates. On Cortex-A53, an in-order
+# execution core, this costs up to 10-15%, which is partially
+# compensated by implementing dedicated code path for 128-bit
+# CBC encrypt case. On Cortex-A57 parallelizable mode performance
+# seems to be limited by sheer amount of NEON instructions...
+#
+# Performance in cycles per byte processed with 128-bit key:
+#
+#		CBC enc		CBC dec		CTR
+# Apple A7	2.39		1.20		1.20
+# Cortex-A53	1.32		1.29		1.46
+# Cortex-A57(*)	1.95		0.85		0.93
+# Denver	1.96		0.86		0.80
+# Mongoose	1.33		1.20		1.20
+#
+# (*)	original 3.64/1.34/1.32 results were for r0p0 revision
+#	and are still same even for updated module;
+
+$flavour = shift;
+$output  = shift;
+
+$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+( $xlate="${dir}arm-xlate.pl" and -f $xlate ) or
+( $xlate="${dir}../../perlasm/arm-xlate.pl" and -f $xlate) or
+die "can't locate arm-xlate.pl";
+
+open OUT,"| \"$^X\" $xlate $flavour $output";
+*STDOUT=*OUT;
+
+$prefix="aes_v8";
+
+$code=<<___;
+#include "arm_arch.h"
+
+#if __ARM_MAX_ARCH__>=7
+.text
+___
+$code.=".arch	armv8-a+crypto\n"			if ($flavour =~ /64/);
+$code.=".arch	armv7-a\n.fpu	neon\n.code	32\n"	if ($flavour !~ /64/);
+		#^^^^^^ this is done to simplify adoption by not depending
+		#	on latest binutils.
+
+# Assembler mnemonics are an eclectic mix of 32- and 64-bit syntax,
+# NEON is mostly 32-bit mnemonics, integer - mostly 64. Goal is to
+# maintain both 32- and 64-bit codes within single module and
+# transliterate common code to either flavour with regex vodoo.
+#
+{{{
+my ($inp,$bits,$out,$ptr,$rounds)=("x0","w1","x2","x3","w12");
+my ($zero,$rcon,$mask,$in0,$in1,$tmp,$key)=
+	$flavour=~/64/? map("q$_",(0..6)) : map("q$_",(0..3,8..10));
+
+
+$code.=<<___;
+.align	5
+.Lrcon:
+.long	0x01,0x01,0x01,0x01
+.long	0x0c0f0e0d,0x0c0f0e0d,0x0c0f0e0d,0x0c0f0e0d	// rotate-n-splat
+.long	0x1b,0x1b,0x1b,0x1b
+
+.globl	${prefix}_set_encrypt_key
+.type	${prefix}_set_encrypt_key,%function
+.align	5
+${prefix}_set_encrypt_key:
+.Lenc_key:
+___
+$code.=<<___	if ($flavour =~ /64/);
+	stp	x29,x30,[sp,#-16]!
+	add	x29,sp,#0
+___
+$code.=<<___;
+	mov	$ptr,#-1
+	cmp	$inp,#0
+	b.eq	.Lenc_key_abort
+	cmp	$out,#0
+	b.eq	.Lenc_key_abort
+	mov	$ptr,#-2
+	cmp	$bits,#128
+	b.lt	.Lenc_key_abort
+	cmp	$bits,#256
+	b.gt	.Lenc_key_abort
+	tst	$bits,#0x3f
+	b.ne	.Lenc_key_abort
+
+	adr	$ptr,.Lrcon
+	cmp	$bits,#192
+
+	veor	$zero,$zero,$zero
+	vld1.8	{$in0},[$inp],#16
+	mov	$bits,#8		// reuse $bits
+	vld1.32	{$rcon,$mask},[$ptr],#32
+
+	b.lt	.Loop128
+	b.eq	.L192
+	b	.L256
+
+.align	4
+.Loop128:
+	vtbl.8	$key,{$in0},$mask
+	vext.8	$tmp,$zero,$in0,#12
+	vst1.32	{$in0},[$out],#16
+	aese	$key,$zero
+	subs	$bits,$bits,#1
+
+	veor	$in0,$in0,$tmp
+	vext.8	$tmp,$zero,$tmp,#12
+	veor	$in0,$in0,$tmp
+	vext.8	$tmp,$zero,$tmp,#12
+	 veor	$key,$key,$rcon
+	veor	$in0,$in0,$tmp
+	vshl.u8	$rcon,$rcon,#1
+	veor	$in0,$in0,$key
+	b.ne	.Loop128
+
+	vld1.32	{$rcon},[$ptr]
+
+	vtbl.8	$key,{$in0},$mask
+	vext.8	$tmp,$zero,$in0,#12
+	vst1.32	{$in0},[$out],#16
+	aese	$key,$zero
+
+	veor	$in0,$in0,$tmp
+	vext.8	$tmp,$zero,$tmp,#12
+	veor	$in0,$in0,$tmp
+	vext.8	$tmp,$zero,$tmp,#12
+	 veor	$key,$key,$rcon
+	veor	$in0,$in0,$tmp
+	vshl.u8	$rcon,$rcon,#1
+	veor	$in0,$in0,$key
+
+	vtbl.8	$key,{$in0},$mask
+	vext.8	$tmp,$zero,$in0,#12
+	vst1.32	{$in0},[$out],#16
+	aese	$key,$zero
+
+	veor	$in0,$in0,$tmp
+	vext.8	$tmp,$zero,$tmp,#12
+	veor	$in0,$in0,$tmp
+	vext.8	$tmp,$zero,$tmp,#12
+	 veor	$key,$key,$rcon
+	veor	$in0,$in0,$tmp
+	veor	$in0,$in0,$key
+	vst1.32	{$in0},[$out]
+	add	$out,$out,#0x50
+
+	mov	$rounds,#10
+	b	.Ldone
+
+.align	4
+.L192:
+	vld1.8	{$in1},[$inp],#8
+	vmov.i8	$key,#8			// borrow $key
+	vst1.32	{$in0},[$out],#16
+	vsub.i8	$mask,$mask,$key	// adjust the mask
+
+.Loop192:
+	vtbl.8	$key,{$in1},$mask
+	vext.8	$tmp,$zero,$in0,#12
+	vst1.32	{$in1},[$out],#8
+	aese	$key,$zero
+	subs	$bits,$bits,#1
+
+	veor	$in0,$in0,$tmp
+	vext.8	$tmp,$zero,$tmp,#12
+	veor	$in0,$in0,$tmp
+	vext.8	$tmp,$zero,$tmp,#12
+	veor	$in0,$in0,$tmp
+
+	vdup.32	$tmp,${in0}[3]
+	veor	$tmp,$tmp,$in1
+	 veor	$key,$key,$rcon
+	vext.8	$in1,$zero,$in1,#12
+	vshl.u8	$rcon,$rcon,#1
+	veor	$in1,$in1,$tmp
+	veor	$in0,$in0,$key
+	veor	$in1,$in1,$key
+	vst1.32	{$in0},[$out],#16
+	b.ne	.Loop192
+
+	mov	$rounds,#12
+	add	$out,$out,#0x20
+	b	.Ldone
+
+.align	4
+.L256:
+	vld1.8	{$in1},[$inp]
+	mov	$bits,#7
+	mov	$rounds,#14
+	vst1.32	{$in0},[$out],#16
+
+.Loop256:
+	vtbl.8	$key,{$in1},$mask
+	vext.8	$tmp,$zero,$in0,#12
+	vst1.32	{$in1},[$out],#16
+	aese	$key,$zero
+	subs	$bits,$bits,#1
+
+	veor	$in0,$in0,$tmp
+	vext.8	$tmp,$zero,$tmp,#12
+	veor	$in0,$in0,$tmp
+	vext.8	$tmp,$zero,$tmp,#12
+	 veor	$key,$key,$rcon
+	veor	$in0,$in0,$tmp
+	vshl.u8	$rcon,$rcon,#1
+	veor	$in0,$in0,$key
+	vst1.32	{$in0},[$out],#16
+	b.eq	.Ldone
+
+	vdup.32	$key,${in0}[3]		// just splat
+	vext.8	$tmp,$zero,$in1,#12
+	aese	$key,$zero
+
+	veor	$in1,$in1,$tmp
+	vext.8	$tmp,$zero,$tmp,#12
+	veor	$in1,$in1,$tmp
+	vext.8	$tmp,$zero,$tmp,#12
+	veor	$in1,$in1,$tmp
+
+	veor	$in1,$in1,$key
+	b	.Loop256
+
+.Ldone:
+	str	$rounds,[$out]
+	mov	$ptr,#0
+
+.Lenc_key_abort:
+	mov	x0,$ptr			// return value
+	`"ldr	x29,[sp],#16"		if ($flavour =~ /64/)`
+	ret
+.size	${prefix}_set_encrypt_key,.-${prefix}_set_encrypt_key
+
+.globl	${prefix}_set_decrypt_key
+.type	${prefix}_set_decrypt_key,%function
+.align	5
+${prefix}_set_decrypt_key:
+___
+$code.=<<___	if ($flavour =~ /64/);
+	stp	x29,x30,[sp,#-16]!
+	add	x29,sp,#0
+___
+$code.=<<___	if ($flavour !~ /64/);
+	stmdb	sp!,{r4,lr}
+___
+$code.=<<___;
+	bl	.Lenc_key
+
+	cmp	x0,#0
+	b.ne	.Ldec_key_abort
+
+	sub	$out,$out,#240		// restore original $out
+	mov	x4,#-16
+	add	$inp,$out,x12,lsl#4	// end of key schedule
+
+	vld1.32	{v0.16b},[$out]
+	vld1.32	{v1.16b},[$inp]
+	vst1.32	{v0.16b},[$inp],x4
+	vst1.32	{v1.16b},[$out],#16
+
+.Loop_imc:
+	vld1.32	{v0.16b},[$out]
+	vld1.32	{v1.16b},[$inp]
+	aesimc	v0.16b,v0.16b
+	aesimc	v1.16b,v1.16b
+	vst1.32	{v0.16b},[$inp],x4
+	vst1.32	{v1.16b},[$out],#16
+	cmp	$inp,$out
+	b.hi	.Loop_imc
+
+	vld1.32	{v0.16b},[$out]
+	aesimc	v0.16b,v0.16b
+	vst1.32	{v0.16b},[$inp]
+
+	eor	x0,x0,x0		// return value
+.Ldec_key_abort:
+___
+$code.=<<___	if ($flavour !~ /64/);
+	ldmia	sp!,{r4,pc}
+___
+$code.=<<___	if ($flavour =~ /64/);
+	ldp	x29,x30,[sp],#16
+	ret
+___
+$code.=<<___;
+.size	${prefix}_set_decrypt_key,.-${prefix}_set_decrypt_key
+___
+}}}
+{{{
+sub gen_block () {
+my $dir = shift;
+my ($e,$mc) = $dir eq "en" ? ("e","mc") : ("d","imc");
+my ($inp,$out,$key)=map("x$_",(0..2));
+my $rounds="w3";
+my ($rndkey0,$rndkey1,$inout)=map("q$_",(0..3));
+
+$code.=<<___;
+.globl	${prefix}_${dir}crypt
+.type	${prefix}_${dir}crypt,%function
+.align	5
+${prefix}_${dir}crypt:
+	ldr	$rounds,[$key,#240]
+	vld1.32	{$rndkey0},[$key],#16
+	vld1.8	{$inout},[$inp]
+	sub	$rounds,$rounds,#2
+	vld1.32	{$rndkey1},[$key],#16
+
+.Loop_${dir}c:
+	aes$e	$inout,$rndkey0
+	aes$mc	$inout,$inout
+	vld1.32	{$rndkey0},[$key],#16
+	subs	$rounds,$rounds,#2
+	aes$e	$inout,$rndkey1
+	aes$mc	$inout,$inout
+	vld1.32	{$rndkey1},[$key],#16
+	b.gt	.Loop_${dir}c
+
+	aes$e	$inout,$rndkey0
+	aes$mc	$inout,$inout
+	vld1.32	{$rndkey0},[$key]
+	aes$e	$inout,$rndkey1
+	veor	$inout,$inout,$rndkey0
+
+	vst1.8	{$inout},[$out]
+	ret
+.size	${prefix}_${dir}crypt,.-${prefix}_${dir}crypt
+___
+}
+&gen_block("en");
+&gen_block("de");
+}}}
+{{{
+my ($inp,$out,$len,$key,$ivp)=map("x$_",(0..4)); my $enc="w5";
+my ($rounds,$cnt,$key_,$step,$step1)=($enc,"w6","x7","x8","x12");
+my ($dat0,$dat1,$in0,$in1,$tmp0,$tmp1,$ivec,$rndlast)=map("q$_",(0..7));
+
+my ($dat,$tmp,$rndzero_n_last)=($dat0,$tmp0,$tmp1);
+my ($key4,$key5,$key6,$key7)=("x6","x12","x14",$key);
+
+### q8-q15	preloaded key schedule
+
+$code.=<<___;
+.globl	${prefix}_cbc_encrypt
+.type	${prefix}_cbc_encrypt,%function
+.align	5
+${prefix}_cbc_encrypt:
+___
+$code.=<<___	if ($flavour =~ /64/);
+	stp	x29,x30,[sp,#-16]!
+	add	x29,sp,#0
+___
+$code.=<<___	if ($flavour !~ /64/);
+	mov	ip,sp
+	stmdb	sp!,{r4-r8,lr}
+	vstmdb	sp!,{d8-d15}            @ ABI specification says so
+	ldmia	ip,{r4-r5}		@ load remaining args
+___
+$code.=<<___;
+	subs	$len,$len,#16
+	mov	$step,#16
+	b.lo	.Lcbc_abort
+	cclr	$step,eq
+
+	cmp	$enc,#0			// en- or decrypting?
+	ldr	$rounds,[$key,#240]
+	and	$len,$len,#-16
+	vld1.8	{$ivec},[$ivp]
+	vld1.8	{$dat},[$inp],$step
+
+	vld1.32	{q8-q9},[$key]		// load key schedule...
+	sub	$rounds,$rounds,#6
+	add	$key_,$key,x5,lsl#4	// pointer to last 7 round keys
+	sub	$rounds,$rounds,#2
+	vld1.32	{q10-q11},[$key_],#32
+	vld1.32	{q12-q13},[$key_],#32
+	vld1.32	{q14-q15},[$key_],#32
+	vld1.32	{$rndlast},[$key_]
+
+	add	$key_,$key,#32
+	mov	$cnt,$rounds
+	b.eq	.Lcbc_dec
+
+	cmp	$rounds,#2
+	veor	$dat,$dat,$ivec
+	veor	$rndzero_n_last,q8,$rndlast
+	b.eq	.Lcbc_enc128
+
+	vld1.32	{$in0-$in1},[$key_]
+	add	$key_,$key,#16
+	add	$key4,$key,#16*4
+	add	$key5,$key,#16*5
+	aese	$dat,q8
+	aesmc	$dat,$dat
+	add	$key6,$key,#16*6
+	add	$key7,$key,#16*7
+	b	.Lenter_cbc_enc
+
+.align	4
+.Loop_cbc_enc:
+	aese	$dat,q8
+	aesmc	$dat,$dat
+	 vst1.8	{$ivec},[$out],#16
+.Lenter_cbc_enc:
+	aese	$dat,q9
+	aesmc	$dat,$dat
+	aese	$dat,$in0
+	aesmc	$dat,$dat
+	vld1.32	{q8},[$key4]
+	cmp	$rounds,#4
+	aese	$dat,$in1
+	aesmc	$dat,$dat
+	vld1.32	{q9},[$key5]
+	b.eq	.Lcbc_enc192
+
+	aese	$dat,q8
+	aesmc	$dat,$dat
+	vld1.32	{q8},[$key6]
+	aese	$dat,q9
+	aesmc	$dat,$dat
+	vld1.32	{q9},[$key7]
+	nop
+
+.Lcbc_enc192:
+	aese	$dat,q8
+	aesmc	$dat,$dat
+	 subs	$len,$len,#16
+	aese	$dat,q9
+	aesmc	$dat,$dat
+	 cclr	$step,eq
+	aese	$dat,q10
+	aesmc	$dat,$dat
+	aese	$dat,q11
+	aesmc	$dat,$dat
+	 vld1.8	{q8},[$inp],$step
+	aese	$dat,q12
+	aesmc	$dat,$dat
+	 veor	q8,q8,$rndzero_n_last
+	aese	$dat,q13
+	aesmc	$dat,$dat
+	 vld1.32 {q9},[$key_]		// re-pre-load rndkey[1]
+	aese	$dat,q14
+	aesmc	$dat,$dat
+	aese	$dat,q15
+	veor	$ivec,$dat,$rndlast
+	b.hs	.Loop_cbc_enc
+
+	vst1.8	{$ivec},[$out],#16
+	b	.Lcbc_done
+
+.align	5
+.Lcbc_enc128:
+	vld1.32	{$in0-$in1},[$key_]
+	aese	$dat,q8
+	aesmc	$dat,$dat
+	b	.Lenter_cbc_enc128
+.Loop_cbc_enc128:
+	aese	$dat,q8
+	aesmc	$dat,$dat
+	 vst1.8	{$ivec},[$out],#16
+.Lenter_cbc_enc128:
+	aese	$dat,q9
+	aesmc	$dat,$dat
+	 subs	$len,$len,#16
+	aese	$dat,$in0
+	aesmc	$dat,$dat
+	 cclr	$step,eq
+	aese	$dat,$in1
+	aesmc	$dat,$dat
+	aese	$dat,q10
+	aesmc	$dat,$dat
+	aese	$dat,q11
+	aesmc	$dat,$dat
+	 vld1.8	{q8},[$inp],$step
+	aese	$dat,q12
+	aesmc	$dat,$dat
+	aese	$dat,q13
+	aesmc	$dat,$dat
+	aese	$dat,q14
+	aesmc	$dat,$dat
+	 veor	q8,q8,$rndzero_n_last
+	aese	$dat,q15
+	veor	$ivec,$dat,$rndlast
+	b.hs	.Loop_cbc_enc128
+
+	vst1.8	{$ivec},[$out],#16
+	b	.Lcbc_done
+___
+{
+my ($dat2,$in2,$tmp2)=map("q$_",(10,11,9));
+$code.=<<___;
+.align	5
+.Lcbc_dec:
+	vld1.8	{$dat2},[$inp],#16
+	subs	$len,$len,#32		// bias
+	add	$cnt,$rounds,#2
+	vorr	$in1,$dat,$dat
+	vorr	$dat1,$dat,$dat
+	vorr	$in2,$dat2,$dat2
+	b.lo	.Lcbc_dec_tail
+
+	vorr	$dat1,$dat2,$dat2
+	vld1.8	{$dat2},[$inp],#16
+	vorr	$in0,$dat,$dat
+	vorr	$in1,$dat1,$dat1
+	vorr	$in2,$dat2,$dat2
+
+.Loop3x_cbc_dec:
+	aesd	$dat0,q8
+	aesimc	$dat0,$dat0
+	aesd	$dat1,q8
+	aesimc	$dat1,$dat1
+	aesd	$dat2,q8
+	aesimc	$dat2,$dat2
+	vld1.32	{q8},[$key_],#16
+	subs	$cnt,$cnt,#2
+	aesd	$dat0,q9
+	aesimc	$dat0,$dat0
+	aesd	$dat1,q9
+	aesimc	$dat1,$dat1
+	aesd	$dat2,q9
+	aesimc	$dat2,$dat2
+	vld1.32	{q9},[$key_],#16
+	b.gt	.Loop3x_cbc_dec
+
+	aesd	$dat0,q8
+	aesimc	$dat0,$dat0
+	aesd	$dat1,q8
+	aesimc	$dat1,$dat1
+	aesd	$dat2,q8
+	aesimc	$dat2,$dat2
+	 veor	$tmp0,$ivec,$rndlast
+	 subs	$len,$len,#0x30
+	 veor	$tmp1,$in0,$rndlast
+	 mov.lo	x6,$len			// x6, $cnt, is zero at this point
+	aesd	$dat0,q9
+	aesimc	$dat0,$dat0
+	aesd	$dat1,q9
+	aesimc	$dat1,$dat1
+	aesd	$dat2,q9
+	aesimc	$dat2,$dat2
+	 veor	$tmp2,$in1,$rndlast
+	 add	$inp,$inp,x6		// $inp is adjusted in such way that
+					// at exit from the loop $dat1-$dat2
+					// are loaded with last "words"
+	 vorr	$ivec,$in2,$in2
+	 mov	$key_,$key
+	aesd	$dat0,q12
+	aesimc	$dat0,$dat0
+	aesd	$dat1,q12
+	aesimc	$dat1,$dat1
+	aesd	$dat2,q12
+	aesimc	$dat2,$dat2
+	 vld1.8	{$in0},[$inp],#16
+	aesd	$dat0,q13
+	aesimc	$dat0,$dat0
+	aesd	$dat1,q13
+	aesimc	$dat1,$dat1
+	aesd	$dat2,q13
+	aesimc	$dat2,$dat2
+	 vld1.8	{$in1},[$inp],#16
+	aesd	$dat0,q14
+	aesimc	$dat0,$dat0
+	aesd	$dat1,q14
+	aesimc	$dat1,$dat1
+	aesd	$dat2,q14
+	aesimc	$dat2,$dat2
+	 vld1.8	{$in2},[$inp],#16
+	aesd	$dat0,q15
+	aesd	$dat1,q15
+	aesd	$dat2,q15
+	 vld1.32 {q8},[$key_],#16	// re-pre-load rndkey[0]
+	 add	$cnt,$rounds,#2
+	veor	$tmp0,$tmp0,$dat0
+	veor	$tmp1,$tmp1,$dat1
+	veor	$dat2,$dat2,$tmp2
+	 vld1.32 {q9},[$key_],#16	// re-pre-load rndkey[1]
+	vst1.8	{$tmp0},[$out],#16
+	 vorr	$dat0,$in0,$in0
+	vst1.8	{$tmp1},[$out],#16
+	 vorr	$dat1,$in1,$in1
+	vst1.8	{$dat2},[$out],#16
+	 vorr	$dat2,$in2,$in2
+	b.hs	.Loop3x_cbc_dec
+
+	cmn	$len,#0x30
+	b.eq	.Lcbc_done
+	nop
+
+.Lcbc_dec_tail:
+	aesd	$dat1,q8
+	aesimc	$dat1,$dat1
+	aesd	$dat2,q8
+	aesimc	$dat2,$dat2
+	vld1.32	{q8},[$key_],#16
+	subs	$cnt,$cnt,#2
+	aesd	$dat1,q9
+	aesimc	$dat1,$dat1
+	aesd	$dat2,q9
+	aesimc	$dat2,$dat2
+	vld1.32	{q9},[$key_],#16
+	b.gt	.Lcbc_dec_tail
+
+	aesd	$dat1,q8
+	aesimc	$dat1,$dat1
+	aesd	$dat2,q8
+	aesimc	$dat2,$dat2
+	aesd	$dat1,q9
+	aesimc	$dat1,$dat1
+	aesd	$dat2,q9
+	aesimc	$dat2,$dat2
+	aesd	$dat1,q12
+	aesimc	$dat1,$dat1
+	aesd	$dat2,q12
+	aesimc	$dat2,$dat2
+	 cmn	$len,#0x20
+	aesd	$dat1,q13
+	aesimc	$dat1,$dat1
+	aesd	$dat2,q13
+	aesimc	$dat2,$dat2
+	 veor	$tmp1,$ivec,$rndlast
+	aesd	$dat1,q14
+	aesimc	$dat1,$dat1
+	aesd	$dat2,q14
+	aesimc	$dat2,$dat2
+	 veor	$tmp2,$in1,$rndlast
+	aesd	$dat1,q15
+	aesd	$dat2,q15
+	b.eq	.Lcbc_dec_one
+	veor	$tmp1,$tmp1,$dat1
+	veor	$tmp2,$tmp2,$dat2
+	 vorr	$ivec,$in2,$in2
+	vst1.8	{$tmp1},[$out],#16
+	vst1.8	{$tmp2},[$out],#16
+	b	.Lcbc_done
+
+.Lcbc_dec_one:
+	veor	$tmp1,$tmp1,$dat2
+	 vorr	$ivec,$in2,$in2
+	vst1.8	{$tmp1},[$out],#16
+
+.Lcbc_done:
+	vst1.8	{$ivec},[$ivp]
+.Lcbc_abort:
+___
+}
+$code.=<<___	if ($flavour !~ /64/);
+	vldmia	sp!,{d8-d15}
+	ldmia	sp!,{r4-r8,pc}
+___
+$code.=<<___	if ($flavour =~ /64/);
+	ldr	x29,[sp],#16
+	ret
+___
+$code.=<<___;
+.size	${prefix}_cbc_encrypt,.-${prefix}_cbc_encrypt
+___
+}}}
+{{{
+my ($inp,$out,$len,$key,$ivp)=map("x$_",(0..4));
+my ($rounds,$cnt,$key_)=("w5","w6","x7");
+my ($ctr,$tctr0,$tctr1,$tctr2)=map("w$_",(8..10,12));
+my $step="x12";		# aliases with $tctr2
+
+my ($dat0,$dat1,$in0,$in1,$tmp0,$tmp1,$ivec,$rndlast)=map("q$_",(0..7));
+my ($dat2,$in2,$tmp2)=map("q$_",(10,11,9));
+
+my ($dat,$tmp)=($dat0,$tmp0);
+
+### q8-q15	preloaded key schedule
+
+$code.=<<___;
+.globl	${prefix}_ctr32_encrypt_blocks
+.type	${prefix}_ctr32_encrypt_blocks,%function
+.align	5
+${prefix}_ctr32_encrypt_blocks:
+___
+$code.=<<___	if ($flavour =~ /64/);
+	stp		x29,x30,[sp,#-16]!
+	add		x29,sp,#0
+___
+$code.=<<___	if ($flavour !~ /64/);
+	mov		ip,sp
+	stmdb		sp!,{r4-r10,lr}
+	vstmdb		sp!,{d8-d15}            @ ABI specification says so
+	ldr		r4, [ip]		@ load remaining arg
+___
+$code.=<<___;
+	ldr		$rounds,[$key,#240]
+
+	ldr		$ctr, [$ivp, #12]
+	vld1.32		{$dat0},[$ivp]
+
+	vld1.32		{q8-q9},[$key]		// load key schedule...
+	sub		$rounds,$rounds,#4
+	mov		$step,#16
+	cmp		$len,#2
+	add		$key_,$key,x5,lsl#4	// pointer to last 5 round keys
+	sub		$rounds,$rounds,#2
+	vld1.32		{q12-q13},[$key_],#32
+	vld1.32		{q14-q15},[$key_],#32
+	vld1.32		{$rndlast},[$key_]
+	add		$key_,$key,#32
+	mov		$cnt,$rounds
+	cclr		$step,lo
+#ifndef __ARMEB__
+	rev		$ctr, $ctr
+#endif
+	vorr		$dat1,$dat0,$dat0
+	add		$tctr1, $ctr, #1
+	vorr		$dat2,$dat0,$dat0
+	add		$ctr, $ctr, #2
+	vorr		$ivec,$dat0,$dat0
+	rev		$tctr1, $tctr1
+	vmov.32		${dat1}[3],$tctr1
+	b.ls		.Lctr32_tail
+	rev		$tctr2, $ctr
+	sub		$len,$len,#3		// bias
+	vmov.32		${dat2}[3],$tctr2
+	b		.Loop3x_ctr32
+
+.align	4
+.Loop3x_ctr32:
+	aese		$dat0,q8
+	aesmc		$dat0,$dat0
+	aese		$dat1,q8
+	aesmc		$dat1,$dat1
+	aese		$dat2,q8
+	aesmc		$dat2,$dat2
+	vld1.32		{q8},[$key_],#16
+	subs		$cnt,$cnt,#2
+	aese		$dat0,q9
+	aesmc		$dat0,$dat0
+	aese		$dat1,q9
+	aesmc		$dat1,$dat1
+	aese		$dat2,q9
+	aesmc		$dat2,$dat2
+	vld1.32		{q9},[$key_],#16
+	b.gt		.Loop3x_ctr32
+
+	aese		$dat0,q8
+	aesmc		$tmp0,$dat0
+	aese		$dat1,q8
+	aesmc		$tmp1,$dat1
+	 vld1.8		{$in0},[$inp],#16
+	 vorr		$dat0,$ivec,$ivec
+	aese		$dat2,q8
+	aesmc		$dat2,$dat2
+	 vld1.8		{$in1},[$inp],#16
+	 vorr		$dat1,$ivec,$ivec
+	aese		$tmp0,q9
+	aesmc		$tmp0,$tmp0
+	aese		$tmp1,q9
+	aesmc		$tmp1,$tmp1
+	 vld1.8		{$in2},[$inp],#16
+	 mov		$key_,$key
+	aese		$dat2,q9
+	aesmc		$tmp2,$dat2
+	 vorr		$dat2,$ivec,$ivec
+	 add		$tctr0,$ctr,#1
+	aese		$tmp0,q12
+	aesmc		$tmp0,$tmp0
+	aese		$tmp1,q12
+	aesmc		$tmp1,$tmp1
+	 veor		$in0,$in0,$rndlast
+	 add		$tctr1,$ctr,#2
+	aese		$tmp2,q12
+	aesmc		$tmp2,$tmp2
+	 veor		$in1,$in1,$rndlast
+	 add		$ctr,$ctr,#3
+	aese		$tmp0,q13
+	aesmc		$tmp0,$tmp0
+	aese		$tmp1,q13
+	aesmc		$tmp1,$tmp1
+	 veor		$in2,$in2,$rndlast
+	 rev		$tctr0,$tctr0
+	aese		$tmp2,q13
+	aesmc		$tmp2,$tmp2
+	 vmov.32	${dat0}[3], $tctr0
+	 rev		$tctr1,$tctr1
+	aese		$tmp0,q14
+	aesmc		$tmp0,$tmp0
+	aese		$tmp1,q14
+	aesmc		$tmp1,$tmp1
+	 vmov.32	${dat1}[3], $tctr1
+	 rev		$tctr2,$ctr
+	aese		$tmp2,q14
+	aesmc		$tmp2,$tmp2
+	 vmov.32	${dat2}[3], $tctr2
+	 subs		$len,$len,#3
+	aese		$tmp0,q15
+	aese		$tmp1,q15
+	aese		$tmp2,q15
+
+	veor		$in0,$in0,$tmp0
+	 vld1.32	 {q8},[$key_],#16	// re-pre-load rndkey[0]
+	vst1.8		{$in0},[$out],#16
+	veor		$in1,$in1,$tmp1
+	 mov		$cnt,$rounds
+	vst1.8		{$in1},[$out],#16
+	veor		$in2,$in2,$tmp2
+	 vld1.32	 {q9},[$key_],#16	// re-pre-load rndkey[1]
+	vst1.8		{$in2},[$out],#16
+	b.hs		.Loop3x_ctr32
+
+	adds		$len,$len,#3
+	b.eq		.Lctr32_done
+	cmp		$len,#1
+	mov		$step,#16
+	cclr		$step,eq
+
+.Lctr32_tail:
+	aese		$dat0,q8
+	aesmc		$dat0,$dat0
+	aese		$dat1,q8
+	aesmc		$dat1,$dat1
+	vld1.32		{q8},[$key_],#16
+	subs		$cnt,$cnt,#2
+	aese		$dat0,q9
+	aesmc		$dat0,$dat0
+	aese		$dat1,q9
+	aesmc		$dat1,$dat1
+	vld1.32		{q9},[$key_],#16
+	b.gt		.Lctr32_tail
+
+	aese		$dat0,q8
+	aesmc		$dat0,$dat0
+	aese		$dat1,q8
+	aesmc		$dat1,$dat1
+	aese		$dat0,q9
+	aesmc		$dat0,$dat0
+	aese		$dat1,q9
+	aesmc		$dat1,$dat1
+	 vld1.8		{$in0},[$inp],$step
+	aese		$dat0,q12
+	aesmc		$dat0,$dat0
+	aese		$dat1,q12
+	aesmc		$dat1,$dat1
+	 vld1.8		{$in1},[$inp]
+	aese		$dat0,q13
+	aesmc		$dat0,$dat0
+	aese		$dat1,q13
+	aesmc		$dat1,$dat1
+	 veor		$in0,$in0,$rndlast
+	aese		$dat0,q14
+	aesmc		$dat0,$dat0
+	aese		$dat1,q14
+	aesmc		$dat1,$dat1
+	 veor		$in1,$in1,$rndlast
+	aese		$dat0,q15
+	aese		$dat1,q15
+
+	cmp		$len,#1
+	veor		$in0,$in0,$dat0
+	veor		$in1,$in1,$dat1
+	vst1.8		{$in0},[$out],#16
+	b.eq		.Lctr32_done
+	vst1.8		{$in1},[$out]
+
+.Lctr32_done:
+___
+$code.=<<___	if ($flavour !~ /64/);
+	vldmia		sp!,{d8-d15}
+	ldmia		sp!,{r4-r10,pc}
+___
+$code.=<<___	if ($flavour =~ /64/);
+	ldr		x29,[sp],#16
+	ret
+___
+$code.=<<___;
+.size	${prefix}_ctr32_encrypt_blocks,.-${prefix}_ctr32_encrypt_blocks
+___
+}}}
+$code.=<<___;
+#endif
+___
+########################################
+if ($flavour =~ /64/) {			######## 64-bit code
+    my %opcode = (
+	"aesd"	=>	0x4e285800,	"aese"	=>	0x4e284800,
+	"aesimc"=>	0x4e287800,	"aesmc"	=>	0x4e286800	);
+
+    local *unaes = sub {
+	my ($mnemonic,$arg)=@_;
+
+	$arg =~ m/[qv]([0-9]+)[^,]*,\s*[qv]([0-9]+)/o	&&
+	sprintf ".inst\t0x%08x\t//%s %s",
+			$opcode{$mnemonic}|$1|($2<<5),
+			$mnemonic,$arg;
+    };
+
+    foreach(split("\n",$code)) {
+	s/\`([^\`]*)\`/eval($1)/geo;
+
+	s/\bq([0-9]+)\b/"v".($1<8?$1:$1+8).".16b"/geo;	# old->new registers
+	s/@\s/\/\//o;			# old->new style commentary
+
+	#s/[v]?(aes\w+)\s+([qv].*)/unaes($1,$2)/geo	or
+	s/cclr\s+([wx])([^,]+),\s*([a-z]+)/csel	$1$2,$1zr,$1$2,$3/o	or
+	s/mov\.([a-z]+)\s+([wx][0-9]+),\s*([wx][0-9]+)/csel	$2,$3,$2,$1/o	or
+	s/vmov\.i8/movi/o	or	# fix up legacy mnemonics
+	s/vext\.8/ext/o		or
+	s/vrev32\.8/rev32/o	or
+	s/vtst\.8/cmtst/o	or
+	s/vshr/ushr/o		or
+	s/^(\s+)v/$1/o		or	# strip off v prefix
+	s/\bbx\s+lr\b/ret/o;
+
+	# fix up remainig legacy suffixes
+	s/\.[ui]?8//o;
+	m/\],#8/o and s/\.16b/\.8b/go;
+	s/\.[ui]?32//o and s/\.16b/\.4s/go;
+	s/\.[ui]?64//o and s/\.16b/\.2d/go;
+	s/\.[42]([sd])\[([0-3])\]/\.$1\[$2\]/o;
+
+	print $_,"\n";
+    }
+} else {				######## 32-bit code
+    my %opcode = (
+	"aesd"	=>	0xf3b00340,	"aese"	=>	0xf3b00300,
+	"aesimc"=>	0xf3b003c0,	"aesmc"	=>	0xf3b00380	);
+
+    local *unaes = sub {
+	my ($mnemonic,$arg)=@_;
+
+	if ($arg =~ m/[qv]([0-9]+)[^,]*,\s*[qv]([0-9]+)/o) {
+	    my $word = $opcode{$mnemonic}|(($1&7)<<13)|(($1&8)<<19)
+					 |(($2&7)<<1) |(($2&8)<<2);
+	    # since ARMv7 instructions are always encoded little-endian.
+	    # correct solution is to use .inst directive, but older
+	    # assemblers don't implement it:-(
+	    sprintf ".byte\t0x%02x,0x%02x,0x%02x,0x%02x\t@ %s %s",
+			$word&0xff,($word>>8)&0xff,
+			($word>>16)&0xff,($word>>24)&0xff,
+			$mnemonic,$arg;
+	}
+    };
+
+    sub unvtbl {
+	my $arg=shift;
+
+	$arg =~ m/q([0-9]+),\s*\{q([0-9]+)\},\s*q([0-9]+)/o &&
+	sprintf	"vtbl.8	d%d,{q%d},d%d\n\t".
+		"vtbl.8	d%d,{q%d},d%d", 2*$1,$2,2*$3, 2*$1+1,$2,2*$3+1;	
+    }
+
+    sub unvdup32 {
+	my $arg=shift;
+
+	$arg =~ m/q([0-9]+),\s*q([0-9]+)\[([0-3])\]/o &&
+	sprintf	"vdup.32	q%d,d%d[%d]",$1,2*$2+($3>>1),$3&1;	
+    }
+
+    sub unvmov32 {
+	my $arg=shift;
+
+	$arg =~ m/q([0-9]+)\[([0-3])\],(.*)/o &&
+	sprintf	"vmov.32	d%d[%d],%s",2*$1+($2>>1),$2&1,$3;	
+    }
+
+    foreach(split("\n",$code)) {
+	s/\`([^\`]*)\`/eval($1)/geo;
+
+	s/\b[wx]([0-9]+)\b/r$1/go;		# new->old registers
+	s/\bv([0-9])\.[12468]+[bsd]\b/q$1/go;	# new->old registers
+	s/\/\/\s?/@ /o;				# new->old style commentary
+
+	# fix up remainig new-style suffixes
+	s/\{q([0-9]+)\},\s*\[(.+)\],#8/sprintf "{d%d},[$2]!",2*$1/eo	or
+	s/\],#[0-9]+/]!/o;
+
+	s/[v]?(aes\w+)\s+([qv].*)/unaes($1,$2)/geo	or
+	s/cclr\s+([^,]+),\s*([a-z]+)/mov$2	$1,#0/o	or
+	s/vtbl\.8\s+(.*)/unvtbl($1)/geo			or
+	s/vdup\.32\s+(.*)/unvdup32($1)/geo		or
+	s/vmov\.32\s+(.*)/unvmov32($1)/geo		or
+	s/^(\s+)b\./$1b/o				or
+	s/^(\s+)mov\./$1mov/o				or
+	s/^(\s+)ret/$1bx\tlr/o;
+
+	print $_,"\n";
+    }
+}
+
+close STDOUT;
diff --git a/crypto/aes/asm/bsaes-armv7.pl b/crypto/aes/asm/bsaes-armv7.pl
new file mode 100644
index 000000000..12091ef9c
--- /dev/null
+++ b/crypto/aes/asm/bsaes-armv7.pl
@@ -0,0 +1,2495 @@
+#! /usr/bin/env perl
+# Copyright 2012-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+
+# ====================================================================
+# Written by Andy Polyakov  for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+#
+# Specific modes and adaptation for Linux kernel by Ard Biesheuvel
+# . Permission to use under GPL terms is
+# granted.
+# ====================================================================
+
+# Bit-sliced AES for ARM NEON
+#
+# February 2012.
+#
+# This implementation is direct adaptation of bsaes-x86_64 module for
+# ARM NEON. Except that this module is endian-neutral [in sense that
+# it can be compiled for either endianness] by courtesy of vld1.8's
+# neutrality. Initial version doesn't implement interface to OpenSSL,
+# only low-level primitives and unsupported entry points, just enough
+# to collect performance results, which for Cortex-A8 core are:
+#
+# encrypt	19.5 cycles per byte processed with 128-bit key
+# decrypt	22.1 cycles per byte processed with 128-bit key
+# key conv.	440  cycles per 128-bit key/0.18 of 8x block
+#
+# Snapdragon S4 encrypts byte in 17.6 cycles and decrypts in 19.7,
+# which is [much] worse than anticipated (for further details see
+# http://www.openssl.org/~appro/Snapdragon-S4.html).
+#
+# Cortex-A15 manages in 14.2/16.1 cycles [when integer-only code
+# manages in 20.0 cycles].
+#
+# When comparing to x86_64 results keep in mind that NEON unit is
+# [mostly] single-issue and thus can't [fully] benefit from
+# instruction-level parallelism. And when comparing to aes-armv4
+# results keep in mind key schedule conversion overhead (see
+# bsaes-x86_64.pl for further details)...
+#
+#						
+
+# April-August 2013
+#
+# Add CBC, CTR and XTS subroutines, adapt for kernel use.
+#
+#					
+
+$flavour = shift;
+if ($flavour=~/\w[\w\-]*\.\w+$/) { $output=$flavour; undef $flavour; }
+else { while (($output=shift) && ($output!~/\w[\w\-]*\.\w+$/)) {} }
+
+if ($flavour && $flavour ne "void") {
+    $0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+    ( $xlate="${dir}arm-xlate.pl" and -f $xlate ) or
+    ( $xlate="${dir}../../perlasm/arm-xlate.pl" and -f $xlate) or
+    die "can't locate arm-xlate.pl";
+
+    open STDOUT,"| \"$^X\" $xlate $flavour $output";
+} else {
+    open STDOUT,">$output";
+}
+
+my ($inp,$out,$len,$key)=("r0","r1","r2","r3");
+my @XMM=map("q$_",(0..15));
+
+{
+my ($key,$rounds,$const)=("r4","r5","r6");
+
+sub Dlo()   { shift=~m|q([1]?[0-9])|?"d".($1*2):"";     }
+sub Dhi()   { shift=~m|q([1]?[0-9])|?"d".($1*2+1):"";   }
+
+sub Sbox {
+# input in  lsb > [b0, b1, b2, b3, b4, b5, b6, b7] < msb
+# output in lsb > [b0, b1, b4, b6, b3, b7, b2, b5] < msb
+my @b=@_[0..7];
+my @t=@_[8..11];
+my @s=@_[12..15];
+	&InBasisChange	(@b);
+	&Inv_GF256	(@b[6,5,0,3,7,1,4,2],@t,@s);
+	&OutBasisChange	(@b[7,1,4,2,6,5,0,3]);
+}
+
+sub InBasisChange {
+# input in  lsb > [b0, b1, b2, b3, b4, b5, b6, b7] < msb
+# output in lsb > [b6, b5, b0, b3, b7, b1, b4, b2] < msb 
+my @b=@_[0..7];
+$code.=<<___;
+	veor	@b[2], @b[2], @b[1]
+	veor	@b[5], @b[5], @b[6]
+	veor	@b[3], @b[3], @b[0]
+	veor	@b[6], @b[6], @b[2]
+	veor	@b[5], @b[5], @b[0]
+
+	veor	@b[6], @b[6], @b[3]
+	veor	@b[3], @b[3], @b[7]
+	veor	@b[7], @b[7], @b[5]
+	veor	@b[3], @b[3], @b[4]
+	veor	@b[4], @b[4], @b[5]
+
+	veor	@b[2], @b[2], @b[7]
+	veor	@b[3], @b[3], @b[1]
+	veor	@b[1], @b[1], @b[5]
+___
+}
+
+sub OutBasisChange {
+# input in  lsb > [b0, b1, b2, b3, b4, b5, b6, b7] < msb
+# output in lsb > [b6, b1, b2, b4, b7, b0, b3, b5] < msb
+my @b=@_[0..7];
+$code.=<<___;
+	veor	@b[0], @b[0], @b[6]
+	veor	@b[1], @b[1], @b[4]
+	veor	@b[4], @b[4], @b[6]
+	veor	@b[2], @b[2], @b[0]
+	veor	@b[6], @b[6], @b[1]
+
+	veor	@b[1], @b[1], @b[5]
+	veor	@b[5], @b[5], @b[3]
+	veor	@b[3], @b[3], @b[7]
+	veor	@b[7], @b[7], @b[5]
+	veor	@b[2], @b[2], @b[5]
+
+	veor	@b[4], @b[4], @b[7]
+___
+}
+
+sub InvSbox {
+# input in lsb 	> [b0, b1, b2, b3, b4, b5, b6, b7] < msb
+# output in lsb	> [b0, b1, b6, b4, b2, b7, b3, b5] < msb
+my @b=@_[0..7];
+my @t=@_[8..11];
+my @s=@_[12..15];
+	&InvInBasisChange	(@b);
+	&Inv_GF256		(@b[5,1,2,6,3,7,0,4],@t,@s);
+	&InvOutBasisChange	(@b[3,7,0,4,5,1,2,6]);
+}
+
+sub InvInBasisChange {		# OutBasisChange in reverse (with twist)
+my @b=@_[5,1,2,6,3,7,0,4];
+$code.=<<___
+	 veor	@b[1], @b[1], @b[7]
+	veor	@b[4], @b[4], @b[7]
+
+	veor	@b[7], @b[7], @b[5]
+	 veor	@b[1], @b[1], @b[3]
+	veor	@b[2], @b[2], @b[5]
+	veor	@b[3], @b[3], @b[7]
+
+	veor	@b[6], @b[6], @b[1]
+	veor	@b[2], @b[2], @b[0]
+	 veor	@b[5], @b[5], @b[3]
+	veor	@b[4], @b[4], @b[6]
+	veor	@b[0], @b[0], @b[6]
+	veor	@b[1], @b[1], @b[4]
+___
+}
+
+sub InvOutBasisChange {		# InBasisChange in reverse
+my @b=@_[2,5,7,3,6,1,0,4];
+$code.=<<___;
+	veor	@b[1], @b[1], @b[5]
+	veor	@b[2], @b[2], @b[7]
+
+	veor	@b[3], @b[3], @b[1]
+	veor	@b[4], @b[4], @b[5]
+	veor	@b[7], @b[7], @b[5]
+	veor	@b[3], @b[3], @b[4]
+	 veor 	@b[5], @b[5], @b[0]
+	veor	@b[3], @b[3], @b[7]
+	 veor	@b[6], @b[6], @b[2]
+	 veor	@b[2], @b[2], @b[1]
+	veor	@b[6], @b[6], @b[3]
+
+	veor	@b[3], @b[3], @b[0]
+	veor	@b[5], @b[5], @b[6]
+___
+}
+
+sub Mul_GF4 {
+#;*************************************************************
+#;* Mul_GF4: Input x0-x1,y0-y1 Output x0-x1 Temp t0 (8) *
+#;*************************************************************
+my ($x0,$x1,$y0,$y1,$t0,$t1)=@_;
+$code.=<<___;
+	veor 	$t0, $y0, $y1
+	vand	$t0, $t0, $x0
+	veor	$x0, $x0, $x1
+	vand	$t1, $x1, $y0
+	vand	$x0, $x0, $y1
+	veor	$x1, $t1, $t0
+	veor	$x0, $x0, $t1
+___
+}
+
+sub Mul_GF4_N {				# not used, see next subroutine
+# multiply and scale by N
+my ($x0,$x1,$y0,$y1,$t0)=@_;
+$code.=<<___;
+	veor	$t0, $y0, $y1
+	vand	$t0, $t0, $x0
+	veor	$x0, $x0, $x1
+	vand	$x1, $x1, $y0
+	vand	$x0, $x0, $y1
+	veor	$x1, $x1, $x0
+	veor	$x0, $x0, $t0
+___
+}
+
+sub Mul_GF4_N_GF4 {
+# interleaved Mul_GF4_N and Mul_GF4
+my ($x0,$x1,$y0,$y1,$t0,
+    $x2,$x3,$y2,$y3,$t1)=@_;
+$code.=<<___;
+	veor	$t0, $y0, $y1
+	 veor 	$t1, $y2, $y3
+	vand	$t0, $t0, $x0
+	 vand	$t1, $t1, $x2
+	veor	$x0, $x0, $x1
+	 veor	$x2, $x2, $x3
+	vand	$x1, $x1, $y0
+	 vand	$x3, $x3, $y2
+	vand	$x0, $x0, $y1
+	 vand	$x2, $x2, $y3
+	veor	$x1, $x1, $x0
+	 veor	$x2, $x2, $x3
+	veor	$x0, $x0, $t0
+	 veor	$x3, $x3, $t1
+___
+}
+sub Mul_GF16_2 {
+my @x=@_[0..7];
+my @y=@_[8..11];
+my @t=@_[12..15];
+$code.=<<___;
+	veor	@t[0], @x[0], @x[2]
+	veor	@t[1], @x[1], @x[3]
+___
+	&Mul_GF4  	(@x[0], @x[1], @y[0], @y[1], @t[2..3]);
+$code.=<<___;
+	veor	@y[0], @y[0], @y[2]
+	veor	@y[1], @y[1], @y[3]
+___
+	Mul_GF4_N_GF4	(@t[0], @t[1], @y[0], @y[1], @t[3],
+			 @x[2], @x[3], @y[2], @y[3], @t[2]);
+$code.=<<___;
+	veor	@x[0], @x[0], @t[0]
+	veor	@x[2], @x[2], @t[0]
+	veor	@x[1], @x[1], @t[1]
+	veor	@x[3], @x[3], @t[1]
+
+	veor	@t[0], @x[4], @x[6]
+	veor	@t[1], @x[5], @x[7]
+___
+	&Mul_GF4_N_GF4	(@t[0], @t[1], @y[0], @y[1], @t[3],
+			 @x[6], @x[7], @y[2], @y[3], @t[2]);
+$code.=<<___;
+	veor	@y[0], @y[0], @y[2]
+	veor	@y[1], @y[1], @y[3]
+___
+	&Mul_GF4  	(@x[4], @x[5], @y[0], @y[1], @t[2..3]);
+$code.=<<___;
+	veor	@x[4], @x[4], @t[0]
+	veor	@x[6], @x[6], @t[0]
+	veor	@x[5], @x[5], @t[1]
+	veor	@x[7], @x[7], @t[1]
+___
+}
+sub Inv_GF256 {
+#;********************************************************************
+#;* Inv_GF256: Input x0-x7 Output x0-x7 Temp t0-t3,s0-s3 (144)       *
+#;********************************************************************
+my @x=@_[0..7];
+my @t=@_[8..11];
+my @s=@_[12..15];
+# direct optimizations from hardware
+$code.=<<___;
+	veor	@t[3], @x[4], @x[6]
+	veor	@t[2], @x[5], @x[7]
+	veor	@t[1], @x[1], @x[3]
+	veor	@s[1], @x[7], @x[6]
+	 vmov	@t[0], @t[2]
+	veor	@s[0], @x[0], @x[2]
+
+	vorr	@t[2], @t[2], @t[1]
+	veor	@s[3], @t[3], @t[0]
+	vand	@s[2], @t[3], @s[0]
+	vorr	@t[3], @t[3], @s[0]
+	veor	@s[0], @s[0], @t[1]
+	vand	@t[0], @t[0], @t[1]
+	veor	@t[1], @x[3], @x[2]
+	vand	@s[3], @s[3], @s[0]
+	vand	@s[1], @s[1], @t[1]
+	veor	@t[1], @x[4], @x[5]
+	veor	@s[0], @x[1], @x[0]
+	veor	@t[3], @t[3], @s[1]
+	veor	@t[2], @t[2], @s[1]
+	vand	@s[1], @t[1], @s[0]
+	vorr	@t[1], @t[1], @s[0]
+	veor	@t[3], @t[3], @s[3]
+	veor	@t[0], @t[0], @s[1]
+	veor	@t[2], @t[2], @s[2]
+	veor	@t[1], @t[1], @s[3]
+	veor	@t[0], @t[0], @s[2]
+	vand	@s[0], @x[7], @x[3]
+	veor	@t[1], @t[1], @s[2]
+	vand	@s[1], @x[6], @x[2]
+	vand	@s[2], @x[5], @x[1]
+	vorr	@s[3], @x[4], @x[0]
+	veor	@t[3], @t[3], @s[0]
+	veor	@t[1], @t[1], @s[2]
+	veor	@t[0], @t[0], @s[3]
+	veor	@t[2], @t[2], @s[1]
+
+	@ Inv_GF16 \t0, \t1, \t2, \t3, \s0, \s1, \s2, \s3
+
+	@ new smaller inversion
+
+	vand	@s[2], @t[3], @t[1]
+	vmov	@s[0], @t[0]
+
+	veor	@s[1], @t[2], @s[2]
+	veor	@s[3], @t[0], @s[2]
+	veor	@s[2], @t[0], @s[2]	@ @s[2]=@s[3]
+
+	vbsl	@s[1], @t[1], @t[0]
+	vbsl	@s[3], @t[3], @t[2]
+	veor	@t[3], @t[3], @t[2]
+
+	vbsl	@s[0], @s[1], @s[2]
+	vbsl	@t[0], @s[2], @s[1]
+
+	vand	@s[2], @s[0], @s[3]
+	veor	@t[1], @t[1], @t[0]
+
+	veor	@s[2], @s[2], @t[3]
+___
+# output in s3, s2, s1, t1
+
+# Mul_GF16_2 \x0, \x1, \x2, \x3, \x4, \x5, \x6, \x7, \t2, \t3, \t0, \t1, \s0, \s1, \s2, \s3
+
+# Mul_GF16_2 \x0, \x1, \x2, \x3, \x4, \x5, \x6, \x7, \s3, \s2, \s1, \t1, \s0, \t0, \t2, \t3
+	&Mul_GF16_2(@x,@s[3,2,1],@t[1],@s[0],@t[0,2,3]);
+
+### output msb > [x3,x2,x1,x0,x7,x6,x5,x4] < lsb
+}
+
+# AES linear components
+
+sub ShiftRows {
+my @x=@_[0..7];
+my @t=@_[8..11];
+my $mask=pop;
+$code.=<<___;
+	vldmia	$key!, {@t[0]-@t[3]}
+	veor	@t[0], @t[0], @x[0]
+	veor	@t[1], @t[1], @x[1]
+	vtbl.8	`&Dlo(@x[0])`, {@t[0]}, `&Dlo($mask)`
+	vtbl.8	`&Dhi(@x[0])`, {@t[0]}, `&Dhi($mask)`
+	vldmia	$key!, {@t[0]}
+	veor	@t[2], @t[2], @x[2]
+	vtbl.8	`&Dlo(@x[1])`, {@t[1]}, `&Dlo($mask)`
+	vtbl.8	`&Dhi(@x[1])`, {@t[1]}, `&Dhi($mask)`
+	vldmia	$key!, {@t[1]}
+	veor	@t[3], @t[3], @x[3]
+	vtbl.8	`&Dlo(@x[2])`, {@t[2]}, `&Dlo($mask)`
+	vtbl.8	`&Dhi(@x[2])`, {@t[2]}, `&Dhi($mask)`
+	vldmia	$key!, {@t[2]}
+	vtbl.8	`&Dlo(@x[3])`, {@t[3]}, `&Dlo($mask)`
+	vtbl.8	`&Dhi(@x[3])`, {@t[3]}, `&Dhi($mask)`
+	vldmia	$key!, {@t[3]}
+	veor	@t[0], @t[0], @x[4]
+	veor	@t[1], @t[1], @x[5]
+	vtbl.8	`&Dlo(@x[4])`, {@t[0]}, `&Dlo($mask)`
+	vtbl.8	`&Dhi(@x[4])`, {@t[0]}, `&Dhi($mask)`
+	veor	@t[2], @t[2], @x[6]
+	vtbl.8	`&Dlo(@x[5])`, {@t[1]}, `&Dlo($mask)`
+	vtbl.8	`&Dhi(@x[5])`, {@t[1]}, `&Dhi($mask)`
+	veor	@t[3], @t[3], @x[7]
+	vtbl.8	`&Dlo(@x[6])`, {@t[2]}, `&Dlo($mask)`
+	vtbl.8	`&Dhi(@x[6])`, {@t[2]}, `&Dhi($mask)`
+	vtbl.8	`&Dlo(@x[7])`, {@t[3]}, `&Dlo($mask)`
+	vtbl.8	`&Dhi(@x[7])`, {@t[3]}, `&Dhi($mask)`
+___
+}
+
+sub MixColumns {
+# modified to emit output in order suitable for feeding back to aesenc[last]
+my @x=@_[0..7];
+my @t=@_[8..15];
+my $inv=@_[16];	# optional
+$code.=<<___;
+	vext.8	@t[0], @x[0], @x[0], #12	@ x0 <<< 32
+	vext.8	@t[1], @x[1], @x[1], #12
+	 veor	@x[0], @x[0], @t[0]		@ x0 ^ (x0 <<< 32)
+	vext.8	@t[2], @x[2], @x[2], #12
+	 veor	@x[1], @x[1], @t[1]
+	vext.8	@t[3], @x[3], @x[3], #12
+	 veor	@x[2], @x[2], @t[2]
+	vext.8	@t[4], @x[4], @x[4], #12
+	 veor	@x[3], @x[3], @t[3]
+	vext.8	@t[5], @x[5], @x[5], #12
+	 veor	@x[4], @x[4], @t[4]
+	vext.8	@t[6], @x[6], @x[6], #12
+	 veor	@x[5], @x[5], @t[5]
+	vext.8	@t[7], @x[7], @x[7], #12
+	 veor	@x[6], @x[6], @t[6]
+
+	veor	@t[1], @t[1], @x[0]
+	 veor	@x[7], @x[7], @t[7]
+	 vext.8	@x[0], @x[0], @x[0], #8		@ (x0 ^ (x0 <<< 32)) <<< 64)
+	veor	@t[2], @t[2], @x[1]
+	veor	@t[0], @t[0], @x[7]
+	veor	@t[1], @t[1], @x[7]
+	 vext.8	@x[1], @x[1], @x[1], #8
+	veor	@t[5], @t[5], @x[4]
+	 veor	@x[0], @x[0], @t[0]
+	veor	@t[6], @t[6], @x[5]
+	 veor	@x[1], @x[1], @t[1]
+	 vext.8	@t[0], @x[4], @x[4], #8
+	veor	@t[4], @t[4], @x[3]
+	 vext.8	@t[1], @x[5], @x[5], #8
+	veor	@t[7], @t[7], @x[6]
+	 vext.8	@x[4], @x[3], @x[3], #8
+	veor	@t[3], @t[3], @x[2]
+	 vext.8	@x[5], @x[7], @x[7], #8
+	veor	@t[4], @t[4], @x[7]
+	 vext.8	@x[3], @x[6], @x[6], #8
+	veor	@t[3], @t[3], @x[7]
+	 vext.8	@x[6], @x[2], @x[2], #8
+	veor	@x[7], @t[1], @t[5]
+___
+$code.=<<___ if (!$inv);
+	veor	@x[2], @t[0], @t[4]
+	veor	@x[4], @x[4], @t[3]
+	veor	@x[5], @x[5], @t[7]
+	veor	@x[3], @x[3], @t[6]
+	 @ vmov	@x[2], @t[0]
+	veor	@x[6], @x[6], @t[2]
+	 @ vmov	@x[7], @t[1]
+___
+$code.=<<___ if ($inv);
+	veor	@t[3], @t[3], @x[4]
+	veor	@x[5], @x[5], @t[7]
+	veor	@x[2], @x[3], @t[6]
+	veor	@x[3], @t[0], @t[4]
+	veor	@x[4], @x[6], @t[2]
+	vmov	@x[6], @t[3]
+	 @ vmov	@x[7], @t[1]
+___
+}
+
+sub InvMixColumns_orig {
+my @x=@_[0..7];
+my @t=@_[8..15];
+
+$code.=<<___;
+	@ multiplication by 0x0e
+	vext.8	@t[7], @x[7], @x[7], #12
+	vmov	@t[2], @x[2]
+	veor	@x[2], @x[2], @x[5]		@ 2 5
+	veor	@x[7], @x[7], @x[5]		@ 7 5
+	vext.8	@t[0], @x[0], @x[0], #12
+	vmov	@t[5], @x[5]
+	veor	@x[5], @x[5], @x[0]		@ 5 0		[1]
+	veor	@x[0], @x[0], @x[1]		@ 0 1
+	vext.8	@t[1], @x[1], @x[1], #12
+	veor	@x[1], @x[1], @x[2]		@ 1 25
+	veor	@x[0], @x[0], @x[6]		@ 01 6		[2]
+	vext.8	@t[3], @x[3], @x[3], #12
+	veor	@x[1], @x[1], @x[3]		@ 125 3		[4]
+	veor	@x[2], @x[2], @x[0]		@ 25 016	[3]
+	veor	@x[3], @x[3], @x[7]		@ 3 75
+	veor	@x[7], @x[7], @x[6]		@ 75 6		[0]
+	vext.8	@t[6], @x[6], @x[6], #12
+	vmov	@t[4], @x[4]
+	veor	@x[6], @x[6], @x[4]		@ 6 4
+	veor	@x[4], @x[4], @x[3]		@ 4 375		[6]
+	veor	@x[3], @x[3], @x[7]		@ 375 756=36
+	veor	@x[6], @x[6], @t[5]		@ 64 5		[7]
+	veor	@x[3], @x[3], @t[2]		@ 36 2
+	vext.8	@t[5], @t[5], @t[5], #12
+	veor	@x[3], @x[3], @t[4]		@ 362 4		[5]
+___
+					my @y = @x[7,5,0,2,1,3,4,6];
+$code.=<<___;
+	@ multiplication by 0x0b
+	veor	@y[1], @y[1], @y[0]
+	veor	@y[0], @y[0], @t[0]
+	vext.8	@t[2], @t[2], @t[2], #12
+	veor	@y[1], @y[1], @t[1]
+	veor	@y[0], @y[0], @t[5]
+	vext.8	@t[4], @t[4], @t[4], #12
+	veor	@y[1], @y[1], @t[6]
+	veor	@y[0], @y[0], @t[7]
+	veor	@t[7], @t[7], @t[6]		@ clobber t[7]
+
+	veor	@y[3], @y[3], @t[0]
+	 veor	@y[1], @y[1], @y[0]
+	vext.8	@t[0], @t[0], @t[0], #12
+	veor	@y[2], @y[2], @t[1]
+	veor	@y[4], @y[4], @t[1]
+	vext.8	@t[1], @t[1], @t[1], #12
+	veor	@y[2], @y[2], @t[2]
+	veor	@y[3], @y[3], @t[2]
+	veor	@y[5], @y[5], @t[2]
+	veor	@y[2], @y[2], @t[7]
+	vext.8	@t[2], @t[2], @t[2], #12
+	veor	@y[3], @y[3], @t[3]
+	veor	@y[6], @y[6], @t[3]
+	veor	@y[4], @y[4], @t[3]
+	veor	@y[7], @y[7], @t[4]
+	vext.8	@t[3], @t[3], @t[3], #12
+	veor	@y[5], @y[5], @t[4]
+	veor	@y[7], @y[7], @t[7]
+	veor	@t[7], @t[7], @t[5]		@ clobber t[7] even more
+	veor	@y[3], @y[3], @t[5]
+	veor	@y[4], @y[4], @t[4]
+
+	veor	@y[5], @y[5], @t[7]
+	vext.8	@t[4], @t[4], @t[4], #12
+	veor	@y[6], @y[6], @t[7]
+	veor	@y[4], @y[4], @t[7]
+
+	veor	@t[7], @t[7], @t[5]
+	vext.8	@t[5], @t[5], @t[5], #12
+
+	@ multiplication by 0x0d
+	veor	@y[4], @y[4], @y[7]
+	 veor	@t[7], @t[7], @t[6]		@ restore t[7]
+	veor	@y[7], @y[7], @t[4]
+	vext.8	@t[6], @t[6], @t[6], #12
+	veor	@y[2], @y[2], @t[0]
+	veor	@y[7], @y[7], @t[5]
+	vext.8	@t[7], @t[7], @t[7], #12
+	veor	@y[2], @y[2], @t[2]
+
+	veor	@y[3], @y[3], @y[1]
+	veor	@y[1], @y[1], @t[1]
+	veor	@y[0], @y[0], @t[0]
+	veor	@y[3], @y[3], @t[0]
+	veor	@y[1], @y[1], @t[5]
+	veor	@y[0], @y[0], @t[5]
+	vext.8	@t[0], @t[0], @t[0], #12
+	veor	@y[1], @y[1], @t[7]
+	veor	@y[0], @y[0], @t[6]
+	veor	@y[3], @y[3], @y[1]
+	veor	@y[4], @y[4], @t[1]
+	vext.8	@t[1], @t[1], @t[1], #12
+
+	veor	@y[7], @y[7], @t[7]
+	veor	@y[4], @y[4], @t[2]
+	veor	@y[5], @y[5], @t[2]
+	veor	@y[2], @y[2], @t[6]
+	veor	@t[6], @t[6], @t[3]		@ clobber t[6]
+	vext.8	@t[2], @t[2], @t[2], #12
+	veor	@y[4], @y[4], @y[7]
+	veor	@y[3], @y[3], @t[6]
+
+	veor	@y[6], @y[6], @t[6]
+	veor	@y[5], @y[5], @t[5]
+	vext.8	@t[5], @t[5], @t[5], #12
+	veor	@y[6], @y[6], @t[4]
+	vext.8	@t[4], @t[4], @t[4], #12
+	veor	@y[5], @y[5], @t[6]
+	veor	@y[6], @y[6], @t[7]
+	vext.8	@t[7], @t[7], @t[7], #12
+	veor	@t[6], @t[6], @t[3]		@ restore t[6]
+	vext.8	@t[3], @t[3], @t[3], #12
+
+	@ multiplication by 0x09
+	veor	@y[4], @y[4], @y[1]
+	veor	@t[1], @t[1], @y[1]		@ t[1]=y[1]
+	veor	@t[0], @t[0], @t[5]		@ clobber t[0]
+	vext.8	@t[6], @t[6], @t[6], #12
+	veor	@t[1], @t[1], @t[5]
+	veor	@y[3], @y[3], @t[0]
+	veor	@t[0], @t[0], @y[0]		@ t[0]=y[0]
+	veor	@t[1], @t[1], @t[6]
+	veor	@t[6], @t[6], @t[7]		@ clobber t[6]
+	veor	@y[4], @y[4], @t[1]
+	veor	@y[7], @y[7], @t[4]
+	veor	@y[6], @y[6], @t[3]
+	veor	@y[5], @y[5], @t[2]
+	veor	@t[4], @t[4], @y[4]		@ t[4]=y[4]
+	veor	@t[3], @t[3], @y[3]		@ t[3]=y[3]
+	veor	@t[5], @t[5], @y[5]		@ t[5]=y[5]
+	veor	@t[2], @t[2], @y[2]		@ t[2]=y[2]
+	veor	@t[3], @t[3], @t[7]
+	veor	@XMM[5], @t[5], @t[6]
+	veor	@XMM[6], @t[6], @y[6]		@ t[6]=y[6]
+	veor	@XMM[2], @t[2], @t[6]
+	veor	@XMM[7], @t[7], @y[7]		@ t[7]=y[7]
+
+	vmov	@XMM[0], @t[0]
+	vmov	@XMM[1], @t[1]
+	@ vmov	@XMM[2], @t[2]
+	vmov	@XMM[3], @t[3]
+	vmov	@XMM[4], @t[4]
+	@ vmov	@XMM[5], @t[5]
+	@ vmov	@XMM[6], @t[6]
+	@ vmov	@XMM[7], @t[7]
+___
+}
+
+sub InvMixColumns {
+my @x=@_[0..7];
+my @t=@_[8..15];
+
+# Thanks to Jussi Kivilinna for providing pointer to
+#
+# | 0e 0b 0d 09 |   | 02 03 01 01 |   | 05 00 04 00 |
+# | 09 0e 0b 0d | = | 01 02 03 01 | x | 00 05 00 04 |
+# | 0d 09 0e 0b |   | 01 01 02 03 |   | 04 00 05 00 |
+# | 0b 0d 09 0e |   | 03 01 01 02 |   | 00 04 00 05 |
+
+$code.=<<___;
+	@ multiplication by 0x05-0x00-0x04-0x00
+	vext.8	@t[0], @x[0], @x[0], #8
+	vext.8	@t[6], @x[6], @x[6], #8
+	vext.8	@t[7], @x[7], @x[7], #8
+	veor	@t[0], @t[0], @x[0]
+	vext.8	@t[1], @x[1], @x[1], #8
+	veor	@t[6], @t[6], @x[6]
+	vext.8	@t[2], @x[2], @x[2], #8
+	veor	@t[7], @t[7], @x[7]
+	vext.8	@t[3], @x[3], @x[3], #8
+	veor	@t[1], @t[1], @x[1]
+	vext.8	@t[4], @x[4], @x[4], #8
+	veor	@t[2], @t[2], @x[2]
+	vext.8	@t[5], @x[5], @x[5], #8
+	veor	@t[3], @t[3], @x[3]
+	veor	@t[4], @t[4], @x[4]
+	veor	@t[5], @t[5], @x[5]
+
+	 veor	@x[0], @x[0], @t[6]
+	 veor	@x[1], @x[1], @t[6]
+	 veor	@x[2], @x[2], @t[0]
+	 veor	@x[4], @x[4], @t[2]
+	 veor	@x[3], @x[3], @t[1]
+	 veor	@x[1], @x[1], @t[7]
+	 veor	@x[2], @x[2], @t[7]
+	 veor	@x[4], @x[4], @t[6]
+	 veor	@x[5], @x[5], @t[3]
+	 veor	@x[3], @x[3], @t[6]
+	 veor	@x[6], @x[6], @t[4]
+	 veor	@x[4], @x[4], @t[7]
+	 veor	@x[5], @x[5], @t[7]
+	 veor	@x[7], @x[7], @t[5]
+___
+	&MixColumns	(@x,@t,1);	# flipped 2<->3 and 4<->6
+}
+
+sub swapmove {
+my ($a,$b,$n,$mask,$t)=@_;
+$code.=<<___;
+	vshr.u64	$t, $b, #$n
+	veor		$t, $t, $a
+	vand		$t, $t, $mask
+	veor		$a, $a, $t
+	vshl.u64	$t, $t, #$n
+	veor		$b, $b, $t
+___
+}
+sub swapmove2x {
+my ($a0,$b0,$a1,$b1,$n,$mask,$t0,$t1)=@_;
+$code.=<<___;
+	vshr.u64	$t0, $b0, #$n
+	 vshr.u64	$t1, $b1, #$n
+	veor		$t0, $t0, $a0
+	 veor		$t1, $t1, $a1
+	vand		$t0, $t0, $mask
+	 vand		$t1, $t1, $mask
+	veor		$a0, $a0, $t0
+	vshl.u64	$t0, $t0, #$n
+	 veor		$a1, $a1, $t1
+	 vshl.u64	$t1, $t1, #$n
+	veor		$b0, $b0, $t0
+	 veor		$b1, $b1, $t1
+___
+}
+
+sub bitslice {
+my @x=reverse(@_[0..7]);
+my ($t0,$t1,$t2,$t3)=@_[8..11];
+$code.=<<___;
+	vmov.i8	$t0,#0x55			@ compose .LBS0
+	vmov.i8	$t1,#0x33			@ compose .LBS1
+___
+	&swapmove2x(@x[0,1,2,3],1,$t0,$t2,$t3);
+	&swapmove2x(@x[4,5,6,7],1,$t0,$t2,$t3);
+$code.=<<___;
+	vmov.i8	$t0,#0x0f			@ compose .LBS2
+___
+	&swapmove2x(@x[0,2,1,3],2,$t1,$t2,$t3);
+	&swapmove2x(@x[4,6,5,7],2,$t1,$t2,$t3);
+
+	&swapmove2x(@x[0,4,1,5],4,$t0,$t2,$t3);
+	&swapmove2x(@x[2,6,3,7],4,$t0,$t2,$t3);
+}
+
+$code.=<<___;
+#ifndef __KERNEL__
+# include "arm_arch.h"
+
+# define VFP_ABI_PUSH	vstmdb	sp!,{d8-d15}
+# define VFP_ABI_POP	vldmia	sp!,{d8-d15}
+# define VFP_ABI_FRAME	0x40
+#else
+# define VFP_ABI_PUSH
+# define VFP_ABI_POP
+# define VFP_ABI_FRAME	0
+# define BSAES_ASM_EXTENDED_KEY
+# define XTS_CHAIN_TWEAK
+# define __ARM_ARCH__ __LINUX_ARM_ARCH__
+# define __ARM_MAX_ARCH__ 7
+#endif
+
+#ifdef __thumb__
+# define adrl adr
+#endif
+
+#if __ARM_MAX_ARCH__>=7
+.arch	armv7-a
+.fpu	neon
+
+.text
+.syntax	unified 	@ ARMv7-capable assembler is expected to handle this
+#if defined(__thumb2__) && !defined(__APPLE__)
+.thumb
+#else
+.code   32
+# undef __thumb2__
+#endif
+
+.type	_bsaes_decrypt8,%function
+.align	4
+_bsaes_decrypt8:
+	adr	$const,_bsaes_decrypt8
+	vldmia	$key!, {@XMM[9]}		@ round 0 key
+#ifdef	__APPLE__
+	adr	$const,.LM0ISR
+#else
+	add	$const,$const,#.LM0ISR-_bsaes_decrypt8
+#endif
+
+	vldmia	$const!, {@XMM[8]}		@ .LM0ISR
+	veor	@XMM[10], @XMM[0], @XMM[9]	@ xor with round0 key
+	veor	@XMM[11], @XMM[1], @XMM[9]
+	 vtbl.8	`&Dlo(@XMM[0])`, {@XMM[10]}, `&Dlo(@XMM[8])`
+	 vtbl.8	`&Dhi(@XMM[0])`, {@XMM[10]}, `&Dhi(@XMM[8])`
+	veor	@XMM[12], @XMM[2], @XMM[9]
+	 vtbl.8	`&Dlo(@XMM[1])`, {@XMM[11]}, `&Dlo(@XMM[8])`
+	 vtbl.8	`&Dhi(@XMM[1])`, {@XMM[11]}, `&Dhi(@XMM[8])`
+	veor	@XMM[13], @XMM[3], @XMM[9]
+	 vtbl.8	`&Dlo(@XMM[2])`, {@XMM[12]}, `&Dlo(@XMM[8])`
+	 vtbl.8	`&Dhi(@XMM[2])`, {@XMM[12]}, `&Dhi(@XMM[8])`
+	veor	@XMM[14], @XMM[4], @XMM[9]
+	 vtbl.8	`&Dlo(@XMM[3])`, {@XMM[13]}, `&Dlo(@XMM[8])`
+	 vtbl.8	`&Dhi(@XMM[3])`, {@XMM[13]}, `&Dhi(@XMM[8])`
+	veor	@XMM[15], @XMM[5], @XMM[9]
+	 vtbl.8	`&Dlo(@XMM[4])`, {@XMM[14]}, `&Dlo(@XMM[8])`
+	 vtbl.8	`&Dhi(@XMM[4])`, {@XMM[14]}, `&Dhi(@XMM[8])`
+	veor	@XMM[10], @XMM[6], @XMM[9]
+	 vtbl.8	`&Dlo(@XMM[5])`, {@XMM[15]}, `&Dlo(@XMM[8])`
+	 vtbl.8	`&Dhi(@XMM[5])`, {@XMM[15]}, `&Dhi(@XMM[8])`
+	veor	@XMM[11], @XMM[7], @XMM[9]
+	 vtbl.8	`&Dlo(@XMM[6])`, {@XMM[10]}, `&Dlo(@XMM[8])`
+	 vtbl.8	`&Dhi(@XMM[6])`, {@XMM[10]}, `&Dhi(@XMM[8])`
+	 vtbl.8	`&Dlo(@XMM[7])`, {@XMM[11]}, `&Dlo(@XMM[8])`
+	 vtbl.8	`&Dhi(@XMM[7])`, {@XMM[11]}, `&Dhi(@XMM[8])`
+___
+	&bitslice	(@XMM[0..7, 8..11]);
+$code.=<<___;
+	sub	$rounds,$rounds,#1
+	b	.Ldec_sbox
+.align	4
+.Ldec_loop:
+___
+	&ShiftRows	(@XMM[0..7, 8..12]);
+$code.=".Ldec_sbox:\n";
+	&InvSbox	(@XMM[0..7, 8..15]);
+$code.=<<___;
+	subs	$rounds,$rounds,#1
+	bcc	.Ldec_done
+___
+	&InvMixColumns	(@XMM[0,1,6,4,2,7,3,5, 8..15]);
+$code.=<<___;
+	vldmia	$const, {@XMM[12]}		@ .LISR
+	ite	eq				@ Thumb2 thing, sanity check in ARM
+	addeq	$const,$const,#0x10
+	bne	.Ldec_loop
+	vldmia	$const, {@XMM[12]}		@ .LISRM0
+	b	.Ldec_loop
+.align	4
+.Ldec_done:
+___
+	&bitslice	(@XMM[0,1,6,4,2,7,3,5, 8..11]);
+$code.=<<___;
+	vldmia	$key, {@XMM[8]}			@ last round key
+	veor	@XMM[6], @XMM[6], @XMM[8]
+	veor	@XMM[4], @XMM[4], @XMM[8]
+	veor	@XMM[2], @XMM[2], @XMM[8]
+	veor	@XMM[7], @XMM[7], @XMM[8]
+	veor	@XMM[3], @XMM[3], @XMM[8]
+	veor	@XMM[5], @XMM[5], @XMM[8]
+	veor	@XMM[0], @XMM[0], @XMM[8]
+	veor	@XMM[1], @XMM[1], @XMM[8]
+	bx	lr
+.size	_bsaes_decrypt8,.-_bsaes_decrypt8
+
+.type	_bsaes_const,%object
+.align	6
+_bsaes_const:
+.LM0ISR:	@ InvShiftRows constants
+	.quad	0x0a0e0206070b0f03, 0x0004080c0d010509
+.LISR:
+	.quad	0x0504070602010003, 0x0f0e0d0c080b0a09
+.LISRM0:
+	.quad	0x01040b0e0205080f, 0x0306090c00070a0d
+.LM0SR:		@ ShiftRows constants
+	.quad	0x0a0e02060f03070b, 0x0004080c05090d01
+.LSR:
+	.quad	0x0504070600030201, 0x0f0e0d0c0a09080b
+.LSRM0:
+	.quad	0x0304090e00050a0f, 0x01060b0c0207080d
+.LM0:
+	.quad	0x02060a0e03070b0f, 0x0004080c0105090d
+.LREVM0SR:
+	.quad	0x090d01050c000408, 0x03070b0f060a0e02
+.asciz	"Bit-sliced AES for NEON, CRYPTOGAMS by "
+.align	6
+.size	_bsaes_const,.-_bsaes_const
+
+.type	_bsaes_encrypt8,%function
+.align	4
+_bsaes_encrypt8:
+	adr	$const,_bsaes_encrypt8
+	vldmia	$key!, {@XMM[9]}		@ round 0 key
+#ifdef	__APPLE__
+	adr	$const,.LM0SR
+#else
+	sub	$const,$const,#_bsaes_encrypt8-.LM0SR
+#endif
+
+	vldmia	$const!, {@XMM[8]}		@ .LM0SR
+_bsaes_encrypt8_alt:
+	veor	@XMM[10], @XMM[0], @XMM[9]	@ xor with round0 key
+	veor	@XMM[11], @XMM[1], @XMM[9]
+	 vtbl.8	`&Dlo(@XMM[0])`, {@XMM[10]}, `&Dlo(@XMM[8])`
+	 vtbl.8	`&Dhi(@XMM[0])`, {@XMM[10]}, `&Dhi(@XMM[8])`
+	veor	@XMM[12], @XMM[2], @XMM[9]
+	 vtbl.8	`&Dlo(@XMM[1])`, {@XMM[11]}, `&Dlo(@XMM[8])`
+	 vtbl.8	`&Dhi(@XMM[1])`, {@XMM[11]}, `&Dhi(@XMM[8])`
+	veor	@XMM[13], @XMM[3], @XMM[9]
+	 vtbl.8	`&Dlo(@XMM[2])`, {@XMM[12]}, `&Dlo(@XMM[8])`
+	 vtbl.8	`&Dhi(@XMM[2])`, {@XMM[12]}, `&Dhi(@XMM[8])`
+	veor	@XMM[14], @XMM[4], @XMM[9]
+	 vtbl.8	`&Dlo(@XMM[3])`, {@XMM[13]}, `&Dlo(@XMM[8])`
+	 vtbl.8	`&Dhi(@XMM[3])`, {@XMM[13]}, `&Dhi(@XMM[8])`
+	veor	@XMM[15], @XMM[5], @XMM[9]
+	 vtbl.8	`&Dlo(@XMM[4])`, {@XMM[14]}, `&Dlo(@XMM[8])`
+	 vtbl.8	`&Dhi(@XMM[4])`, {@XMM[14]}, `&Dhi(@XMM[8])`
+	veor	@XMM[10], @XMM[6], @XMM[9]
+	 vtbl.8	`&Dlo(@XMM[5])`, {@XMM[15]}, `&Dlo(@XMM[8])`
+	 vtbl.8	`&Dhi(@XMM[5])`, {@XMM[15]}, `&Dhi(@XMM[8])`
+	veor	@XMM[11], @XMM[7], @XMM[9]
+	 vtbl.8	`&Dlo(@XMM[6])`, {@XMM[10]}, `&Dlo(@XMM[8])`
+	 vtbl.8	`&Dhi(@XMM[6])`, {@XMM[10]}, `&Dhi(@XMM[8])`
+	 vtbl.8	`&Dlo(@XMM[7])`, {@XMM[11]}, `&Dlo(@XMM[8])`
+	 vtbl.8	`&Dhi(@XMM[7])`, {@XMM[11]}, `&Dhi(@XMM[8])`
+_bsaes_encrypt8_bitslice:
+___
+	&bitslice	(@XMM[0..7, 8..11]);
+$code.=<<___;
+	sub	$rounds,$rounds,#1
+	b	.Lenc_sbox
+.align	4
+.Lenc_loop:
+___
+	&ShiftRows	(@XMM[0..7, 8..12]);
+$code.=".Lenc_sbox:\n";
+	&Sbox		(@XMM[0..7, 8..15]);
+$code.=<<___;
+	subs	$rounds,$rounds,#1
+	bcc	.Lenc_done
+___
+	&MixColumns	(@XMM[0,1,4,6,3,7,2,5, 8..15]);
+$code.=<<___;
+	vldmia	$const, {@XMM[12]}		@ .LSR
+	ite	eq				@ Thumb2 thing, samity check in ARM
+	addeq	$const,$const,#0x10
+	bne	.Lenc_loop
+	vldmia	$const, {@XMM[12]}		@ .LSRM0
+	b	.Lenc_loop
+.align	4
+.Lenc_done:
+___
+	# output in lsb > [t0, t1, t4, t6, t3, t7, t2, t5] < msb
+	&bitslice	(@XMM[0,1,4,6,3,7,2,5, 8..11]);
+$code.=<<___;
+	vldmia	$key, {@XMM[8]}			@ last round key
+	veor	@XMM[4], @XMM[4], @XMM[8]
+	veor	@XMM[6], @XMM[6], @XMM[8]
+	veor	@XMM[3], @XMM[3], @XMM[8]
+	veor	@XMM[7], @XMM[7], @XMM[8]
+	veor	@XMM[2], @XMM[2], @XMM[8]
+	veor	@XMM[5], @XMM[5], @XMM[8]
+	veor	@XMM[0], @XMM[0], @XMM[8]
+	veor	@XMM[1], @XMM[1], @XMM[8]
+	bx	lr
+.size	_bsaes_encrypt8,.-_bsaes_encrypt8
+___
+}
+{
+my ($out,$inp,$rounds,$const)=("r12","r4","r5","r6");
+
+sub bitslice_key {
+my @x=reverse(@_[0..7]);
+my ($bs0,$bs1,$bs2,$t2,$t3)=@_[8..12];
+
+	&swapmove	(@x[0,1],1,$bs0,$t2,$t3);
+$code.=<<___;
+	@ &swapmove(@x[2,3],1,$t0,$t2,$t3);
+	vmov	@x[2], @x[0]
+	vmov	@x[3], @x[1]
+___
+	#&swapmove2x(@x[4,5,6,7],1,$t0,$t2,$t3);
+
+	&swapmove2x	(@x[0,2,1,3],2,$bs1,$t2,$t3);
+$code.=<<___;
+	@ &swapmove2x(@x[4,6,5,7],2,$t1,$t2,$t3);
+	vmov	@x[4], @x[0]
+	vmov	@x[6], @x[2]
+	vmov	@x[5], @x[1]
+	vmov	@x[7], @x[3]
+___
+	&swapmove2x	(@x[0,4,1,5],4,$bs2,$t2,$t3);
+	&swapmove2x	(@x[2,6,3,7],4,$bs2,$t2,$t3);
+}
+
+$code.=<<___;
+.type	_bsaes_key_convert,%function
+.align	4
+_bsaes_key_convert:
+	adr	$const,_bsaes_key_convert
+	vld1.8	{@XMM[7]},  [$inp]!		@ load round 0 key
+#ifdef	__APPLE__
+	adr	$const,.LM0
+#else
+	sub	$const,$const,#_bsaes_key_convert-.LM0
+#endif
+	vld1.8	{@XMM[15]}, [$inp]!		@ load round 1 key
+
+	vmov.i8	@XMM[8],  #0x01			@ bit masks
+	vmov.i8	@XMM[9],  #0x02
+	vmov.i8	@XMM[10], #0x04
+	vmov.i8	@XMM[11], #0x08
+	vmov.i8	@XMM[12], #0x10
+	vmov.i8	@XMM[13], #0x20
+	vldmia	$const, {@XMM[14]}		@ .LM0
+
+#ifdef __ARMEL__
+	vrev32.8	@XMM[7],  @XMM[7]
+	vrev32.8	@XMM[15], @XMM[15]
+#endif
+	sub	$rounds,$rounds,#1
+	vstmia	$out!, {@XMM[7]}		@ save round 0 key
+	b	.Lkey_loop
+
+.align	4
+.Lkey_loop:
+	vtbl.8	`&Dlo(@XMM[7])`,{@XMM[15]},`&Dlo(@XMM[14])`
+	vtbl.8	`&Dhi(@XMM[7])`,{@XMM[15]},`&Dhi(@XMM[14])`
+	vmov.i8	@XMM[6],  #0x40
+	vmov.i8	@XMM[15], #0x80
+
+	vtst.8	@XMM[0], @XMM[7], @XMM[8]
+	vtst.8	@XMM[1], @XMM[7], @XMM[9]
+	vtst.8	@XMM[2], @XMM[7], @XMM[10]
+	vtst.8	@XMM[3], @XMM[7], @XMM[11]
+	vtst.8	@XMM[4], @XMM[7], @XMM[12]
+	vtst.8	@XMM[5], @XMM[7], @XMM[13]
+	vtst.8	@XMM[6], @XMM[7], @XMM[6]
+	vtst.8	@XMM[7], @XMM[7], @XMM[15]
+	vld1.8	{@XMM[15]}, [$inp]!		@ load next round key
+	vmvn	@XMM[0], @XMM[0]		@ "pnot"
+	vmvn	@XMM[1], @XMM[1]
+	vmvn	@XMM[5], @XMM[5]
+	vmvn	@XMM[6], @XMM[6]
+#ifdef __ARMEL__
+	vrev32.8	@XMM[15], @XMM[15]
+#endif
+	subs	$rounds,$rounds,#1
+	vstmia	$out!,{@XMM[0]-@XMM[7]}		@ write bit-sliced round key
+	bne	.Lkey_loop
+
+	vmov.i8	@XMM[7],#0x63			@ compose .L63
+	@ don't save last round key
+	bx	lr
+.size	_bsaes_key_convert,.-_bsaes_key_convert
+___
+}
+
+if (0) {		# following four functions are unsupported interface
+			# used for benchmarking...
+$code.=<<___;
+.globl	bsaes_enc_key_convert
+.type	bsaes_enc_key_convert,%function
+.align	4
+bsaes_enc_key_convert:
+	stmdb	sp!,{r4-r6,lr}
+	vstmdb	sp!,{d8-d15}		@ ABI specification says so
+
+	ldr	r5,[$inp,#240]			@ pass rounds
+	mov	r4,$inp				@ pass key
+	mov	r12,$out			@ pass key schedule
+	bl	_bsaes_key_convert
+	veor	@XMM[7],@XMM[7],@XMM[15]	@ fix up last round key
+	vstmia	r12, {@XMM[7]}			@ save last round key
+
+	vldmia	sp!,{d8-d15}
+	ldmia	sp!,{r4-r6,pc}
+.size	bsaes_enc_key_convert,.-bsaes_enc_key_convert
+
+.globl	bsaes_encrypt_128
+.type	bsaes_encrypt_128,%function
+.align	4
+bsaes_encrypt_128:
+	stmdb	sp!,{r4-r6,lr}
+	vstmdb	sp!,{d8-d15}		@ ABI specification says so
+.Lenc128_loop:
+	vld1.8	{@XMM[0]-@XMM[1]}, [$inp]!	@ load input
+	vld1.8	{@XMM[2]-@XMM[3]}, [$inp]!
+	mov	r4,$key				@ pass the key
+	vld1.8	{@XMM[4]-@XMM[5]}, [$inp]!
+	mov	r5,#10				@ pass rounds
+	vld1.8	{@XMM[6]-@XMM[7]}, [$inp]!
+
+	bl	_bsaes_encrypt8
+
+	vst1.8	{@XMM[0]-@XMM[1]}, [$out]!	@ write output
+	vst1.8	{@XMM[4]}, [$out]!
+	vst1.8	{@XMM[6]}, [$out]!
+	vst1.8	{@XMM[3]}, [$out]!
+	vst1.8	{@XMM[7]}, [$out]!
+	vst1.8	{@XMM[2]}, [$out]!
+	subs	$len,$len,#0x80
+	vst1.8	{@XMM[5]}, [$out]!
+	bhi	.Lenc128_loop
+
+	vldmia	sp!,{d8-d15}
+	ldmia	sp!,{r4-r6,pc}
+.size	bsaes_encrypt_128,.-bsaes_encrypt_128
+
+.globl	bsaes_dec_key_convert
+.type	bsaes_dec_key_convert,%function
+.align	4
+bsaes_dec_key_convert:
+	stmdb	sp!,{r4-r6,lr}
+	vstmdb	sp!,{d8-d15}		@ ABI specification says so
+
+	ldr	r5,[$inp,#240]			@ pass rounds
+	mov	r4,$inp				@ pass key
+	mov	r12,$out			@ pass key schedule
+	bl	_bsaes_key_convert
+	vldmia	$out, {@XMM[6]}
+	vstmia	r12,  {@XMM[15]}		@ save last round key
+	veor	@XMM[7], @XMM[7], @XMM[6]	@ fix up round 0 key
+	vstmia	$out, {@XMM[7]}
+
+	vldmia	sp!,{d8-d15}
+	ldmia	sp!,{r4-r6,pc}
+.size	bsaes_dec_key_convert,.-bsaes_dec_key_convert
+
+.globl	bsaes_decrypt_128
+.type	bsaes_decrypt_128,%function
+.align	4
+bsaes_decrypt_128:
+	stmdb	sp!,{r4-r6,lr}
+	vstmdb	sp!,{d8-d15}		@ ABI specification says so
+.Ldec128_loop:
+	vld1.8	{@XMM[0]-@XMM[1]}, [$inp]!	@ load input
+	vld1.8	{@XMM[2]-@XMM[3]}, [$inp]!
+	mov	r4,$key				@ pass the key
+	vld1.8	{@XMM[4]-@XMM[5]}, [$inp]!
+	mov	r5,#10				@ pass rounds
+	vld1.8	{@XMM[6]-@XMM[7]}, [$inp]!
+
+	bl	_bsaes_decrypt8
+
+	vst1.8	{@XMM[0]-@XMM[1]}, [$out]!	@ write output
+	vst1.8	{@XMM[6]}, [$out]!
+	vst1.8	{@XMM[4]}, [$out]!
+	vst1.8	{@XMM[2]}, [$out]!
+	vst1.8	{@XMM[7]}, [$out]!
+	vst1.8	{@XMM[3]}, [$out]!
+	subs	$len,$len,#0x80
+	vst1.8	{@XMM[5]}, [$out]!
+	bhi	.Ldec128_loop
+
+	vldmia	sp!,{d8-d15}
+	ldmia	sp!,{r4-r6,pc}
+.size	bsaes_decrypt_128,.-bsaes_decrypt_128
+___
+}
+{
+my ($inp,$out,$len,$key, $ivp,$fp,$rounds)=map("r$_",(0..3,8..10));
+my ($keysched)=("sp");
+
+$code.=<<___;
+.extern AES_cbc_encrypt
+.extern AES_decrypt
+
+.global	bsaes_cbc_encrypt
+.type	bsaes_cbc_encrypt,%function
+.align	5
+bsaes_cbc_encrypt:
+#ifndef	__KERNEL__
+	cmp	$len, #128
+#ifndef	__thumb__
+	blo	AES_cbc_encrypt
+#else
+	bhs	1f
+	b	AES_cbc_encrypt
+1:
+#endif
+#endif
+
+	@ it is up to the caller to make sure we are called with enc == 0
+
+	mov	ip, sp
+	stmdb	sp!, {r4-r10, lr}
+	VFP_ABI_PUSH
+	ldr	$ivp, [ip]			@ IV is 1st arg on the stack
+	mov	$len, $len, lsr#4		@ len in 16 byte blocks
+	sub	sp, #0x10			@ scratch space to carry over the IV
+	mov	$fp, sp				@ save sp
+
+	ldr	$rounds, [$key, #240]		@ get # of rounds
+#ifndef	BSAES_ASM_EXTENDED_KEY
+	@ allocate the key schedule on the stack
+	sub	r12, sp, $rounds, lsl#7		@ 128 bytes per inner round key
+	add	r12, #`128-32`			@ sifze of bit-slices key schedule
+
+	@ populate the key schedule
+	mov	r4, $key			@ pass key
+	mov	r5, $rounds			@ pass # of rounds
+	mov	sp, r12				@ sp is $keysched
+	bl	_bsaes_key_convert
+	vldmia	$keysched, {@XMM[6]}
+	vstmia	r12,  {@XMM[15]}		@ save last round key
+	veor	@XMM[7], @XMM[7], @XMM[6]	@ fix up round 0 key
+	vstmia	$keysched, {@XMM[7]}
+#else
+	ldr	r12, [$key, #244]
+	eors	r12, #1
+	beq	0f
+
+	@ populate the key schedule
+	str	r12, [$key, #244]
+	mov	r4, $key			@ pass key
+	mov	r5, $rounds			@ pass # of rounds
+	add	r12, $key, #248			@ pass key schedule
+	bl	_bsaes_key_convert
+	add	r4, $key, #248
+	vldmia	r4, {@XMM[6]}
+	vstmia	r12, {@XMM[15]}			@ save last round key
+	veor	@XMM[7], @XMM[7], @XMM[6]	@ fix up round 0 key
+	vstmia	r4, {@XMM[7]}
+
+.align	2
+0:
+#endif
+
+	vld1.8	{@XMM[15]}, [$ivp]		@ load IV
+	b	.Lcbc_dec_loop
+
+.align	4
+.Lcbc_dec_loop:
+	subs	$len, $len, #0x8
+	bmi	.Lcbc_dec_loop_finish
+
+	vld1.8	{@XMM[0]-@XMM[1]}, [$inp]!	@ load input
+	vld1.8	{@XMM[2]-@XMM[3]}, [$inp]!
+#ifndef	BSAES_ASM_EXTENDED_KEY
+	mov	r4, $keysched			@ pass the key
+#else
+	add	r4, $key, #248
+#endif
+	vld1.8	{@XMM[4]-@XMM[5]}, [$inp]!
+	mov	r5, $rounds
+	vld1.8	{@XMM[6]-@XMM[7]}, [$inp]
+	sub	$inp, $inp, #0x60
+	vstmia	$fp, {@XMM[15]}			@ put aside IV
+
+	bl	_bsaes_decrypt8
+
+	vldmia	$fp, {@XMM[14]}			@ reload IV
+	vld1.8	{@XMM[8]-@XMM[9]}, [$inp]!	@ reload input
+	veor	@XMM[0], @XMM[0], @XMM[14]	@ ^= IV
+	vld1.8	{@XMM[10]-@XMM[11]}, [$inp]!
+	veor	@XMM[1], @XMM[1], @XMM[8]
+	veor	@XMM[6], @XMM[6], @XMM[9]
+	vld1.8	{@XMM[12]-@XMM[13]}, [$inp]!
+	veor	@XMM[4], @XMM[4], @XMM[10]
+	veor	@XMM[2], @XMM[2], @XMM[11]
+	vld1.8	{@XMM[14]-@XMM[15]}, [$inp]!
+	veor	@XMM[7], @XMM[7], @XMM[12]
+	vst1.8	{@XMM[0]-@XMM[1]}, [$out]!	@ write output
+	veor	@XMM[3], @XMM[3], @XMM[13]
+	vst1.8	{@XMM[6]}, [$out]!
+	veor	@XMM[5], @XMM[5], @XMM[14]
+	vst1.8	{@XMM[4]}, [$out]!
+	vst1.8	{@XMM[2]}, [$out]!
+	vst1.8	{@XMM[7]}, [$out]!
+	vst1.8	{@XMM[3]}, [$out]!
+	vst1.8	{@XMM[5]}, [$out]!
+
+	b	.Lcbc_dec_loop
+
+.Lcbc_dec_loop_finish:
+	adds	$len, $len, #8
+	beq	.Lcbc_dec_done
+
+	vld1.8	{@XMM[0]}, [$inp]!		@ load input
+	cmp	$len, #2
+	blo	.Lcbc_dec_one
+	vld1.8	{@XMM[1]}, [$inp]!
+#ifndef	BSAES_ASM_EXTENDED_KEY
+	mov	r4, $keysched			@ pass the key
+#else
+	add	r4, $key, #248
+#endif
+	mov	r5, $rounds
+	vstmia	$fp, {@XMM[15]}			@ put aside IV
+	beq	.Lcbc_dec_two
+	vld1.8	{@XMM[2]}, [$inp]!
+	cmp	$len, #4
+	blo	.Lcbc_dec_three
+	vld1.8	{@XMM[3]}, [$inp]!
+	beq	.Lcbc_dec_four
+	vld1.8	{@XMM[4]}, [$inp]!
+	cmp	$len, #6
+	blo	.Lcbc_dec_five
+	vld1.8	{@XMM[5]}, [$inp]!
+	beq	.Lcbc_dec_six
+	vld1.8	{@XMM[6]}, [$inp]!
+	sub	$inp, $inp, #0x70
+
+	bl	_bsaes_decrypt8
+
+	vldmia	$fp, {@XMM[14]}			@ reload IV
+	vld1.8	{@XMM[8]-@XMM[9]}, [$inp]!	@ reload input
+	veor	@XMM[0], @XMM[0], @XMM[14]	@ ^= IV
+	vld1.8	{@XMM[10]-@XMM[11]}, [$inp]!
+	veor	@XMM[1], @XMM[1], @XMM[8]
+	veor	@XMM[6], @XMM[6], @XMM[9]
+	vld1.8	{@XMM[12]-@XMM[13]}, [$inp]!
+	veor	@XMM[4], @XMM[4], @XMM[10]
+	veor	@XMM[2], @XMM[2], @XMM[11]
+	vld1.8	{@XMM[15]}, [$inp]!
+	veor	@XMM[7], @XMM[7], @XMM[12]
+	vst1.8	{@XMM[0]-@XMM[1]}, [$out]!	@ write output
+	veor	@XMM[3], @XMM[3], @XMM[13]
+	vst1.8	{@XMM[6]}, [$out]!
+	vst1.8	{@XMM[4]}, [$out]!
+	vst1.8	{@XMM[2]}, [$out]!
+	vst1.8	{@XMM[7]}, [$out]!
+	vst1.8	{@XMM[3]}, [$out]!
+	b	.Lcbc_dec_done
+.align	4
+.Lcbc_dec_six:
+	sub	$inp, $inp, #0x60
+	bl	_bsaes_decrypt8
+	vldmia	$fp,{@XMM[14]}			@ reload IV
+	vld1.8	{@XMM[8]-@XMM[9]}, [$inp]!	@ reload input
+	veor	@XMM[0], @XMM[0], @XMM[14]	@ ^= IV
+	vld1.8	{@XMM[10]-@XMM[11]}, [$inp]!
+	veor	@XMM[1], @XMM[1], @XMM[8]
+	veor	@XMM[6], @XMM[6], @XMM[9]
+	vld1.8	{@XMM[12]}, [$inp]!
+	veor	@XMM[4], @XMM[4], @XMM[10]
+	veor	@XMM[2], @XMM[2], @XMM[11]
+	vld1.8	{@XMM[15]}, [$inp]!
+	veor	@XMM[7], @XMM[7], @XMM[12]
+	vst1.8	{@XMM[0]-@XMM[1]}, [$out]!	@ write output
+	vst1.8	{@XMM[6]}, [$out]!
+	vst1.8	{@XMM[4]}, [$out]!
+	vst1.8	{@XMM[2]}, [$out]!
+	vst1.8	{@XMM[7]}, [$out]!
+	b	.Lcbc_dec_done
+.align	4
+.Lcbc_dec_five:
+	sub	$inp, $inp, #0x50
+	bl	_bsaes_decrypt8
+	vldmia	$fp, {@XMM[14]}			@ reload IV
+	vld1.8	{@XMM[8]-@XMM[9]}, [$inp]!	@ reload input
+	veor	@XMM[0], @XMM[0], @XMM[14]	@ ^= IV
+	vld1.8	{@XMM[10]-@XMM[11]}, [$inp]!
+	veor	@XMM[1], @XMM[1], @XMM[8]
+	veor	@XMM[6], @XMM[6], @XMM[9]
+	vld1.8	{@XMM[15]}, [$inp]!
+	veor	@XMM[4], @XMM[4], @XMM[10]
+	vst1.8	{@XMM[0]-@XMM[1]}, [$out]!	@ write output
+	veor	@XMM[2], @XMM[2], @XMM[11]
+	vst1.8	{@XMM[6]}, [$out]!
+	vst1.8	{@XMM[4]}, [$out]!
+	vst1.8	{@XMM[2]}, [$out]!
+	b	.Lcbc_dec_done
+.align	4
+.Lcbc_dec_four:
+	sub	$inp, $inp, #0x40
+	bl	_bsaes_decrypt8
+	vldmia	$fp, {@XMM[14]}			@ reload IV
+	vld1.8	{@XMM[8]-@XMM[9]}, [$inp]!	@ reload input
+	veor	@XMM[0], @XMM[0], @XMM[14]	@ ^= IV
+	vld1.8	{@XMM[10]}, [$inp]!
+	veor	@XMM[1], @XMM[1], @XMM[8]
+	veor	@XMM[6], @XMM[6], @XMM[9]
+	vld1.8	{@XMM[15]}, [$inp]!
+	veor	@XMM[4], @XMM[4], @XMM[10]
+	vst1.8	{@XMM[0]-@XMM[1]}, [$out]!	@ write output
+	vst1.8	{@XMM[6]}, [$out]!
+	vst1.8	{@XMM[4]}, [$out]!
+	b	.Lcbc_dec_done
+.align	4
+.Lcbc_dec_three:
+	sub	$inp, $inp, #0x30
+	bl	_bsaes_decrypt8
+	vldmia	$fp, {@XMM[14]}			@ reload IV
+	vld1.8	{@XMM[8]-@XMM[9]}, [$inp]!	@ reload input
+	veor	@XMM[0], @XMM[0], @XMM[14]	@ ^= IV
+	vld1.8	{@XMM[15]}, [$inp]!
+	veor	@XMM[1], @XMM[1], @XMM[8]
+	veor	@XMM[6], @XMM[6], @XMM[9]
+	vst1.8	{@XMM[0]-@XMM[1]}, [$out]!	@ write output
+	vst1.8	{@XMM[6]}, [$out]!
+	b	.Lcbc_dec_done
+.align	4
+.Lcbc_dec_two:
+	sub	$inp, $inp, #0x20
+	bl	_bsaes_decrypt8
+	vldmia	$fp, {@XMM[14]}			@ reload IV
+	vld1.8	{@XMM[8]}, [$inp]!		@ reload input
+	veor	@XMM[0], @XMM[0], @XMM[14]	@ ^= IV
+	vld1.8	{@XMM[15]}, [$inp]!		@ reload input
+	veor	@XMM[1], @XMM[1], @XMM[8]
+	vst1.8	{@XMM[0]-@XMM[1]}, [$out]!	@ write output
+	b	.Lcbc_dec_done
+.align	4
+.Lcbc_dec_one:
+	sub	$inp, $inp, #0x10
+	mov	$rounds, $out			@ save original out pointer
+	mov	$out, $fp			@ use the iv scratch space as out buffer
+	mov	r2, $key
+	vmov	@XMM[4],@XMM[15]		@ just in case ensure that IV
+	vmov	@XMM[5],@XMM[0]			@ and input are preserved
+	bl	AES_decrypt
+	vld1.8	{@XMM[0]}, [$fp,:64]		@ load result
+	veor	@XMM[0], @XMM[0], @XMM[4]	@ ^= IV
+	vmov	@XMM[15], @XMM[5]		@ @XMM[5] holds input
+	vst1.8	{@XMM[0]}, [$rounds]		@ write output
+
+.Lcbc_dec_done:
+#ifndef	BSAES_ASM_EXTENDED_KEY
+	vmov.i32	q0, #0
+	vmov.i32	q1, #0
+.Lcbc_dec_bzero:				@ wipe key schedule [if any]
+	vstmia		$keysched!, {q0-q1}
+	cmp		$keysched, $fp
+	bne		.Lcbc_dec_bzero
+#endif
+
+	mov	sp, $fp
+	add	sp, #0x10			@ add sp,$fp,#0x10 is no good for thumb
+	vst1.8	{@XMM[15]}, [$ivp]		@ return IV
+	VFP_ABI_POP
+	ldmia	sp!, {r4-r10, pc}
+.size	bsaes_cbc_encrypt,.-bsaes_cbc_encrypt
+___
+}
+{
+my ($inp,$out,$len,$key, $ctr,$fp,$rounds)=(map("r$_",(0..3,8..10)));
+my $const = "r6";	# shared with _bsaes_encrypt8_alt
+my $keysched = "sp";
+
+$code.=<<___;
+.extern	AES_encrypt
+.global	bsaes_ctr32_encrypt_blocks
+.type	bsaes_ctr32_encrypt_blocks,%function
+.align	5
+bsaes_ctr32_encrypt_blocks:
+	cmp	$len, #8			@ use plain AES for
+	blo	.Lctr_enc_short			@ small sizes
+
+	mov	ip, sp
+	stmdb	sp!, {r4-r10, lr}
+	VFP_ABI_PUSH
+	ldr	$ctr, [ip]			@ ctr is 1st arg on the stack
+	sub	sp, sp, #0x10			@ scratch space to carry over the ctr
+	mov	$fp, sp				@ save sp
+
+	ldr	$rounds, [$key, #240]		@ get # of rounds
+#ifndef	BSAES_ASM_EXTENDED_KEY
+	@ allocate the key schedule on the stack
+	sub	r12, sp, $rounds, lsl#7		@ 128 bytes per inner round key
+	add	r12, #`128-32`			@ size of bit-sliced key schedule
+
+	@ populate the key schedule
+	mov	r4, $key			@ pass key
+	mov	r5, $rounds			@ pass # of rounds
+	mov	sp, r12				@ sp is $keysched
+	bl	_bsaes_key_convert
+	veor	@XMM[7],@XMM[7],@XMM[15]	@ fix up last round key
+	vstmia	r12, {@XMM[7]}			@ save last round key
+
+	vld1.8	{@XMM[0]}, [$ctr]		@ load counter
+#ifdef	__APPLE__
+	mov	$ctr, #:lower16:(.LREVM0SR-.LM0)
+	add	$ctr, $const, $ctr
+#else
+	add	$ctr, $const, #.LREVM0SR-.LM0	@ borrow $ctr
+#endif
+	vldmia	$keysched, {@XMM[4]}		@ load round0 key
+#else
+	ldr	r12, [$key, #244]
+	eors	r12, #1
+	beq	0f
+
+	@ populate the key schedule
+	str	r12, [$key, #244]
+	mov	r4, $key			@ pass key
+	mov	r5, $rounds			@ pass # of rounds
+	add	r12, $key, #248			@ pass key schedule
+	bl	_bsaes_key_convert
+	veor	@XMM[7],@XMM[7],@XMM[15]	@ fix up last round key
+	vstmia	r12, {@XMM[7]}			@ save last round key
+
+.align	2
+0:	add	r12, $key, #248
+	vld1.8	{@XMM[0]}, [$ctr]		@ load counter
+	adrl	$ctr, .LREVM0SR			@ borrow $ctr
+	vldmia	r12, {@XMM[4]}			@ load round0 key
+	sub	sp, #0x10			@ place for adjusted round0 key
+#endif
+
+	vmov.i32	@XMM[8],#1		@ compose 1<<96
+	veor		@XMM[9],@XMM[9],@XMM[9]
+	vrev32.8	@XMM[0],@XMM[0]
+	vext.8		@XMM[8],@XMM[9],@XMM[8],#4
+	vrev32.8	@XMM[4],@XMM[4]
+	vadd.u32	@XMM[9],@XMM[8],@XMM[8]	@ compose 2<<96
+	vstmia	$keysched, {@XMM[4]}		@ save adjusted round0 key
+	b	.Lctr_enc_loop
+
+.align	4
+.Lctr_enc_loop:
+	vadd.u32	@XMM[10], @XMM[8], @XMM[9]	@ compose 3<<96
+	vadd.u32	@XMM[1], @XMM[0], @XMM[8]	@ +1
+	vadd.u32	@XMM[2], @XMM[0], @XMM[9]	@ +2
+	vadd.u32	@XMM[3], @XMM[0], @XMM[10]	@ +3
+	vadd.u32	@XMM[4], @XMM[1], @XMM[10]
+	vadd.u32	@XMM[5], @XMM[2], @XMM[10]
+	vadd.u32	@XMM[6], @XMM[3], @XMM[10]
+	vadd.u32	@XMM[7], @XMM[4], @XMM[10]
+	vadd.u32	@XMM[10], @XMM[5], @XMM[10]	@ next counter
+
+	@ Borrow prologue from _bsaes_encrypt8 to use the opportunity
+	@ to flip byte order in 32-bit counter
+
+	vldmia		$keysched, {@XMM[9]}		@ load round0 key
+#ifndef	BSAES_ASM_EXTENDED_KEY
+	add		r4, $keysched, #0x10		@ pass next round key
+#else
+	add		r4, $key, #`248+16`
+#endif
+	vldmia		$ctr, {@XMM[8]}			@ .LREVM0SR
+	mov		r5, $rounds			@ pass rounds
+	vstmia		$fp, {@XMM[10]}			@ save next counter
+#ifdef	__APPLE__
+	mov		$const, #:lower16:(.LREVM0SR-.LSR)
+	sub		$const, $ctr, $const
+#else
+	sub		$const, $ctr, #.LREVM0SR-.LSR	@ pass constants
+#endif
+
+	bl		_bsaes_encrypt8_alt
+
+	subs		$len, $len, #8
+	blo		.Lctr_enc_loop_done
+
+	vld1.8		{@XMM[8]-@XMM[9]}, [$inp]!	@ load input
+	vld1.8		{@XMM[10]-@XMM[11]}, [$inp]!
+	veor		@XMM[0], @XMM[8]
+	veor		@XMM[1], @XMM[9]
+	vld1.8		{@XMM[12]-@XMM[13]}, [$inp]!
+	veor		@XMM[4], @XMM[10]
+	veor		@XMM[6], @XMM[11]
+	vld1.8		{@XMM[14]-@XMM[15]}, [$inp]!
+	veor		@XMM[3], @XMM[12]
+	vst1.8		{@XMM[0]-@XMM[1]}, [$out]!	@ write output
+	veor		@XMM[7], @XMM[13]
+	veor		@XMM[2], @XMM[14]
+	vst1.8		{@XMM[4]}, [$out]!
+	veor		@XMM[5], @XMM[15]
+	vst1.8		{@XMM[6]}, [$out]!
+	vmov.i32	@XMM[8], #1			@ compose 1<<96
+	vst1.8		{@XMM[3]}, [$out]!
+	veor		@XMM[9], @XMM[9], @XMM[9]
+	vst1.8		{@XMM[7]}, [$out]!
+	vext.8		@XMM[8], @XMM[9], @XMM[8], #4
+	vst1.8		{@XMM[2]}, [$out]!
+	vadd.u32	@XMM[9],@XMM[8],@XMM[8]		@ compose 2<<96
+	vst1.8		{@XMM[5]}, [$out]!
+	vldmia		$fp, {@XMM[0]}			@ load counter
+
+	bne		.Lctr_enc_loop
+	b		.Lctr_enc_done
+
+.align	4
+.Lctr_enc_loop_done:
+	add		$len, $len, #8
+	vld1.8		{@XMM[8]}, [$inp]!	@ load input
+	veor		@XMM[0], @XMM[8]
+	vst1.8		{@XMM[0]}, [$out]!	@ write output
+	cmp		$len, #2
+	blo		.Lctr_enc_done
+	vld1.8		{@XMM[9]}, [$inp]!
+	veor		@XMM[1], @XMM[9]
+	vst1.8		{@XMM[1]}, [$out]!
+	beq		.Lctr_enc_done
+	vld1.8		{@XMM[10]}, [$inp]!
+	veor		@XMM[4], @XMM[10]
+	vst1.8		{@XMM[4]}, [$out]!
+	cmp		$len, #4
+	blo		.Lctr_enc_done
+	vld1.8		{@XMM[11]}, [$inp]!
+	veor		@XMM[6], @XMM[11]
+	vst1.8		{@XMM[6]}, [$out]!
+	beq		.Lctr_enc_done
+	vld1.8		{@XMM[12]}, [$inp]!
+	veor		@XMM[3], @XMM[12]
+	vst1.8		{@XMM[3]}, [$out]!
+	cmp		$len, #6
+	blo		.Lctr_enc_done
+	vld1.8		{@XMM[13]}, [$inp]!
+	veor		@XMM[7], @XMM[13]
+	vst1.8		{@XMM[7]}, [$out]!
+	beq		.Lctr_enc_done
+	vld1.8		{@XMM[14]}, [$inp]
+	veor		@XMM[2], @XMM[14]
+	vst1.8		{@XMM[2]}, [$out]!
+
+.Lctr_enc_done:
+	vmov.i32	q0, #0
+	vmov.i32	q1, #0
+#ifndef	BSAES_ASM_EXTENDED_KEY
+.Lctr_enc_bzero:			@ wipe key schedule [if any]
+	vstmia		$keysched!, {q0-q1}
+	cmp		$keysched, $fp
+	bne		.Lctr_enc_bzero
+#else
+	vstmia		$keysched, {q0-q1}
+#endif
+
+	mov	sp, $fp
+	add	sp, #0x10		@ add sp,$fp,#0x10 is no good for thumb
+	VFP_ABI_POP
+	ldmia	sp!, {r4-r10, pc}	@ return
+
+.align	4
+.Lctr_enc_short:
+	ldr	ip, [sp]		@ ctr pointer is passed on stack
+	stmdb	sp!, {r4-r8, lr}
+
+	mov	r4, $inp		@ copy arguments
+	mov	r5, $out
+	mov	r6, $len
+	mov	r7, $key
+	ldr	r8, [ip, #12]		@ load counter LSW
+	vld1.8	{@XMM[1]}, [ip]		@ load whole counter value
+#ifdef __ARMEL__
+	rev	r8, r8
+#endif
+	sub	sp, sp, #0x10
+	vst1.8	{@XMM[1]}, [sp]		@ copy counter value
+	sub	sp, sp, #0x10
+
+.Lctr_enc_short_loop:
+	add	r0, sp, #0x10		@ input counter value
+	mov	r1, sp			@ output on the stack
+	mov	r2, r7			@ key
+
+	bl	AES_encrypt
+
+	vld1.8	{@XMM[0]}, [r4]!	@ load input
+	vld1.8	{@XMM[1]}, [sp]		@ load encrypted counter
+	add	r8, r8, #1
+#ifdef __ARMEL__
+	rev	r0, r8
+	str	r0, [sp, #0x1c]		@ next counter value
+#else
+	str	r8, [sp, #0x1c]		@ next counter value
+#endif
+	veor	@XMM[0],@XMM[0],@XMM[1]
+	vst1.8	{@XMM[0]}, [r5]!	@ store output
+	subs	r6, r6, #1
+	bne	.Lctr_enc_short_loop
+
+	vmov.i32	q0, #0
+	vmov.i32	q1, #0
+	vstmia		sp!, {q0-q1}
+
+	ldmia	sp!, {r4-r8, pc}
+.size	bsaes_ctr32_encrypt_blocks,.-bsaes_ctr32_encrypt_blocks
+___
+}
+{
+######################################################################
+# void bsaes_xts_[en|de]crypt(const char *inp,char *out,size_t len,
+#	const AES_KEY *key1, const AES_KEY *key2,
+#	const unsigned char iv[16]);
+#
+my ($inp,$out,$len,$key,$rounds,$magic,$fp)=(map("r$_",(7..10,1..3)));
+my $const="r6";		# returned by _bsaes_key_convert
+my $twmask=@XMM[5];
+my @T=@XMM[6..7];
+
+$code.=<<___;
+.globl	bsaes_xts_encrypt
+.type	bsaes_xts_encrypt,%function
+.align	4
+bsaes_xts_encrypt:
+	mov	ip, sp
+	stmdb	sp!, {r4-r10, lr}		@ 0x20
+	VFP_ABI_PUSH
+	mov	r6, sp				@ future $fp
+
+	mov	$inp, r0
+	mov	$out, r1
+	mov	$len, r2
+	mov	$key, r3
+
+	sub	r0, sp, #0x10			@ 0x10
+	bic	r0, #0xf			@ align at 16 bytes
+	mov	sp, r0
+
+#ifdef	XTS_CHAIN_TWEAK
+	ldr	r0, [ip]			@ pointer to input tweak
+#else
+	@ generate initial tweak
+	ldr	r0, [ip, #4]			@ iv[]
+	mov	r1, sp
+	ldr	r2, [ip, #0]			@ key2
+	bl	AES_encrypt
+	mov	r0,sp				@ pointer to initial tweak
+#endif
+
+	ldr	$rounds, [$key, #240]		@ get # of rounds
+	mov	$fp, r6
+#ifndef	BSAES_ASM_EXTENDED_KEY
+	@ allocate the key schedule on the stack
+	sub	r12, sp, $rounds, lsl#7		@ 128 bytes per inner round key
+	@ add	r12, #`128-32`			@ size of bit-sliced key schedule
+	sub	r12, #`32+16`			@ place for tweak[9]
+
+	@ populate the key schedule
+	mov	r4, $key			@ pass key
+	mov	r5, $rounds			@ pass # of rounds
+	mov	sp, r12
+	add	r12, #0x90			@ pass key schedule
+	bl	_bsaes_key_convert
+	veor	@XMM[7], @XMM[7], @XMM[15]	@ fix up last round key
+	vstmia	r12, {@XMM[7]}			@ save last round key
+#else
+	ldr	r12, [$key, #244]
+	eors	r12, #1
+	beq	0f
+
+	str	r12, [$key, #244]
+	mov	r4, $key			@ pass key
+	mov	r5, $rounds			@ pass # of rounds
+	add	r12, $key, #248			@ pass key schedule
+	bl	_bsaes_key_convert
+	veor	@XMM[7], @XMM[7], @XMM[15]	@ fix up last round key
+	vstmia	r12, {@XMM[7]}
+
+.align	2
+0:	sub	sp, #0x90			@ place for tweak[9]
+#endif
+
+	vld1.8	{@XMM[8]}, [r0]			@ initial tweak
+	adr	$magic, .Lxts_magic
+
+	subs	$len, #0x80
+	blo	.Lxts_enc_short
+	b	.Lxts_enc_loop
+
+.align	4
+.Lxts_enc_loop:
+	vldmia		$magic, {$twmask}	@ load XTS magic
+	vshr.s64	@T[0], @XMM[8], #63
+	mov		r0, sp
+	vand		@T[0], @T[0], $twmask
+___
+for($i=9;$i<16;$i++) {
+$code.=<<___;
+	vadd.u64	@XMM[$i], @XMM[$i-1], @XMM[$i-1]
+	vst1.64		{@XMM[$i-1]}, [r0,:128]!
+	vswp		`&Dhi("@T[0]")`,`&Dlo("@T[0]")`
+	vshr.s64	@T[1], @XMM[$i], #63
+	veor		@XMM[$i], @XMM[$i], @T[0]
+	vand		@T[1], @T[1], $twmask
+___
+	@T=reverse(@T);
+
+$code.=<<___ if ($i>=10);
+	vld1.8		{@XMM[$i-10]}, [$inp]!
+___
+$code.=<<___ if ($i>=11);
+	veor		@XMM[$i-11], @XMM[$i-11], @XMM[$i-3]
+___
+}
+$code.=<<___;
+	vadd.u64	@XMM[8], @XMM[15], @XMM[15]
+	vst1.64		{@XMM[15]}, [r0,:128]!
+	vswp		`&Dhi("@T[0]")`,`&Dlo("@T[0]")`
+	veor		@XMM[8], @XMM[8], @T[0]
+	vst1.64		{@XMM[8]}, [r0,:128]		@ next round tweak
+
+	vld1.8		{@XMM[6]-@XMM[7]}, [$inp]!
+	veor		@XMM[5], @XMM[5], @XMM[13]
+#ifndef	BSAES_ASM_EXTENDED_KEY
+	add		r4, sp, #0x90			@ pass key schedule
+#else
+	add		r4, $key, #248			@ pass key schedule
+#endif
+	veor		@XMM[6], @XMM[6], @XMM[14]
+	mov		r5, $rounds			@ pass rounds
+	veor		@XMM[7], @XMM[7], @XMM[15]
+	mov		r0, sp
+
+	bl		_bsaes_encrypt8
+
+	vld1.64		{@XMM[ 8]-@XMM[ 9]}, [r0,:128]!
+	vld1.64		{@XMM[10]-@XMM[11]}, [r0,:128]!
+	veor		@XMM[0], @XMM[0], @XMM[ 8]
+	vld1.64		{@XMM[12]-@XMM[13]}, [r0,:128]!
+	veor		@XMM[1], @XMM[1], @XMM[ 9]
+	veor		@XMM[8], @XMM[4], @XMM[10]
+	vst1.8		{@XMM[0]-@XMM[1]}, [$out]!
+	veor		@XMM[9], @XMM[6], @XMM[11]
+	vld1.64		{@XMM[14]-@XMM[15]}, [r0,:128]!
+	veor		@XMM[10], @XMM[3], @XMM[12]
+	vst1.8		{@XMM[8]-@XMM[9]}, [$out]!
+	veor		@XMM[11], @XMM[7], @XMM[13]
+	veor		@XMM[12], @XMM[2], @XMM[14]
+	vst1.8		{@XMM[10]-@XMM[11]}, [$out]!
+	veor		@XMM[13], @XMM[5], @XMM[15]
+	vst1.8		{@XMM[12]-@XMM[13]}, [$out]!
+
+	vld1.64		{@XMM[8]}, [r0,:128]		@ next round tweak
+
+	subs		$len, #0x80
+	bpl		.Lxts_enc_loop
+
+.Lxts_enc_short:
+	adds		$len, #0x70
+	bmi		.Lxts_enc_done
+
+	vldmia		$magic, {$twmask}	@ load XTS magic
+	vshr.s64	@T[0], @XMM[8], #63
+	mov		r0, sp
+	vand		@T[0], @T[0], $twmask
+___
+for($i=9;$i<16;$i++) {
+$code.=<<___;
+	vadd.u64	@XMM[$i], @XMM[$i-1], @XMM[$i-1]
+	vst1.64		{@XMM[$i-1]}, [r0,:128]!
+	vswp		`&Dhi("@T[0]")`,`&Dlo("@T[0]")`
+	vshr.s64	@T[1], @XMM[$i], #63
+	veor		@XMM[$i], @XMM[$i], @T[0]
+	vand		@T[1], @T[1], $twmask
+___
+	@T=reverse(@T);
+
+$code.=<<___ if ($i>=10);
+	vld1.8		{@XMM[$i-10]}, [$inp]!
+	subs		$len, #0x10
+	bmi		.Lxts_enc_`$i-9`
+___
+$code.=<<___ if ($i>=11);
+	veor		@XMM[$i-11], @XMM[$i-11], @XMM[$i-3]
+___
+}
+$code.=<<___;
+	sub		$len, #0x10
+	vst1.64		{@XMM[15]}, [r0,:128]		@ next round tweak
+
+	vld1.8		{@XMM[6]}, [$inp]!
+	veor		@XMM[5], @XMM[5], @XMM[13]
+#ifndef	BSAES_ASM_EXTENDED_KEY
+	add		r4, sp, #0x90			@ pass key schedule
+#else
+	add		r4, $key, #248			@ pass key schedule
+#endif
+	veor		@XMM[6], @XMM[6], @XMM[14]
+	mov		r5, $rounds			@ pass rounds
+	mov		r0, sp
+
+	bl		_bsaes_encrypt8
+
+	vld1.64		{@XMM[ 8]-@XMM[ 9]}, [r0,:128]!
+	vld1.64		{@XMM[10]-@XMM[11]}, [r0,:128]!
+	veor		@XMM[0], @XMM[0], @XMM[ 8]
+	vld1.64		{@XMM[12]-@XMM[13]}, [r0,:128]!
+	veor		@XMM[1], @XMM[1], @XMM[ 9]
+	veor		@XMM[8], @XMM[4], @XMM[10]
+	vst1.8		{@XMM[0]-@XMM[1]}, [$out]!
+	veor		@XMM[9], @XMM[6], @XMM[11]
+	vld1.64		{@XMM[14]}, [r0,:128]!
+	veor		@XMM[10], @XMM[3], @XMM[12]
+	vst1.8		{@XMM[8]-@XMM[9]}, [$out]!
+	veor		@XMM[11], @XMM[7], @XMM[13]
+	veor		@XMM[12], @XMM[2], @XMM[14]
+	vst1.8		{@XMM[10]-@XMM[11]}, [$out]!
+	vst1.8		{@XMM[12]}, [$out]!
+
+	vld1.64		{@XMM[8]}, [r0,:128]		@ next round tweak
+	b		.Lxts_enc_done
+.align	4
+.Lxts_enc_6:
+	veor		@XMM[4], @XMM[4], @XMM[12]
+#ifndef	BSAES_ASM_EXTENDED_KEY
+	add		r4, sp, #0x90			@ pass key schedule
+#else
+	add		r4, $key, #248			@ pass key schedule
+#endif
+	veor		@XMM[5], @XMM[5], @XMM[13]
+	mov		r5, $rounds			@ pass rounds
+	mov		r0, sp
+
+	bl		_bsaes_encrypt8
+
+	vld1.64		{@XMM[ 8]-@XMM[ 9]}, [r0,:128]!
+	vld1.64		{@XMM[10]-@XMM[11]}, [r0,:128]!
+	veor		@XMM[0], @XMM[0], @XMM[ 8]
+	vld1.64		{@XMM[12]-@XMM[13]}, [r0,:128]!
+	veor		@XMM[1], @XMM[1], @XMM[ 9]
+	veor		@XMM[8], @XMM[4], @XMM[10]
+	vst1.8		{@XMM[0]-@XMM[1]}, [$out]!
+	veor		@XMM[9], @XMM[6], @XMM[11]
+	veor		@XMM[10], @XMM[3], @XMM[12]
+	vst1.8		{@XMM[8]-@XMM[9]}, [$out]!
+	veor		@XMM[11], @XMM[7], @XMM[13]
+	vst1.8		{@XMM[10]-@XMM[11]}, [$out]!
+
+	vld1.64		{@XMM[8]}, [r0,:128]		@ next round tweak
+	b		.Lxts_enc_done
+
+@ put this in range for both ARM and Thumb mode adr instructions
+.align	5
+.Lxts_magic:
+	.quad	1, 0x87
+
+.align	5
+.Lxts_enc_5:
+	veor		@XMM[3], @XMM[3], @XMM[11]
+#ifndef	BSAES_ASM_EXTENDED_KEY
+	add		r4, sp, #0x90			@ pass key schedule
+#else
+	add		r4, $key, #248			@ pass key schedule
+#endif
+	veor		@XMM[4], @XMM[4], @XMM[12]
+	mov		r5, $rounds			@ pass rounds
+	mov		r0, sp
+
+	bl		_bsaes_encrypt8
+
+	vld1.64		{@XMM[ 8]-@XMM[ 9]}, [r0,:128]!
+	vld1.64		{@XMM[10]-@XMM[11]}, [r0,:128]!
+	veor		@XMM[0], @XMM[0], @XMM[ 8]
+	vld1.64		{@XMM[12]}, [r0,:128]!
+	veor		@XMM[1], @XMM[1], @XMM[ 9]
+	veor		@XMM[8], @XMM[4], @XMM[10]
+	vst1.8		{@XMM[0]-@XMM[1]}, [$out]!
+	veor		@XMM[9], @XMM[6], @XMM[11]
+	veor		@XMM[10], @XMM[3], @XMM[12]
+	vst1.8		{@XMM[8]-@XMM[9]}, [$out]!
+	vst1.8		{@XMM[10]}, [$out]!
+
+	vld1.64		{@XMM[8]}, [r0,:128]		@ next round tweak
+	b		.Lxts_enc_done
+.align	4
+.Lxts_enc_4:
+	veor		@XMM[2], @XMM[2], @XMM[10]
+#ifndef	BSAES_ASM_EXTENDED_KEY
+	add		r4, sp, #0x90			@ pass key schedule
+#else
+	add		r4, $key, #248			@ pass key schedule
+#endif
+	veor		@XMM[3], @XMM[3], @XMM[11]
+	mov		r5, $rounds			@ pass rounds
+	mov		r0, sp
+
+	bl		_bsaes_encrypt8
+
+	vld1.64		{@XMM[ 8]-@XMM[ 9]}, [r0,:128]!
+	vld1.64		{@XMM[10]-@XMM[11]}, [r0,:128]!
+	veor		@XMM[0], @XMM[0], @XMM[ 8]
+	veor		@XMM[1], @XMM[1], @XMM[ 9]
+	veor		@XMM[8], @XMM[4], @XMM[10]
+	vst1.8		{@XMM[0]-@XMM[1]}, [$out]!
+	veor		@XMM[9], @XMM[6], @XMM[11]
+	vst1.8		{@XMM[8]-@XMM[9]}, [$out]!
+
+	vld1.64		{@XMM[8]}, [r0,:128]		@ next round tweak
+	b		.Lxts_enc_done
+.align	4
+.Lxts_enc_3:
+	veor		@XMM[1], @XMM[1], @XMM[9]
+#ifndef	BSAES_ASM_EXTENDED_KEY
+	add		r4, sp, #0x90			@ pass key schedule
+#else
+	add		r4, $key, #248			@ pass key schedule
+#endif
+	veor		@XMM[2], @XMM[2], @XMM[10]
+	mov		r5, $rounds			@ pass rounds
+	mov		r0, sp
+
+	bl		_bsaes_encrypt8
+
+	vld1.64		{@XMM[8]-@XMM[9]}, [r0,:128]!
+	vld1.64		{@XMM[10]}, [r0,:128]!
+	veor		@XMM[0], @XMM[0], @XMM[ 8]
+	veor		@XMM[1], @XMM[1], @XMM[ 9]
+	veor		@XMM[8], @XMM[4], @XMM[10]
+	vst1.8		{@XMM[0]-@XMM[1]}, [$out]!
+	vst1.8		{@XMM[8]}, [$out]!
+
+	vld1.64		{@XMM[8]}, [r0,:128]		@ next round tweak
+	b		.Lxts_enc_done
+.align	4
+.Lxts_enc_2:
+	veor		@XMM[0], @XMM[0], @XMM[8]
+#ifndef	BSAES_ASM_EXTENDED_KEY
+	add		r4, sp, #0x90			@ pass key schedule
+#else
+	add		r4, $key, #248			@ pass key schedule
+#endif
+	veor		@XMM[1], @XMM[1], @XMM[9]
+	mov		r5, $rounds			@ pass rounds
+	mov		r0, sp
+
+	bl		_bsaes_encrypt8
+
+	vld1.64		{@XMM[8]-@XMM[9]}, [r0,:128]!
+	veor		@XMM[0], @XMM[0], @XMM[ 8]
+	veor		@XMM[1], @XMM[1], @XMM[ 9]
+	vst1.8		{@XMM[0]-@XMM[1]}, [$out]!
+
+	vld1.64		{@XMM[8]}, [r0,:128]		@ next round tweak
+	b		.Lxts_enc_done
+.align	4
+.Lxts_enc_1:
+	mov		r0, sp
+	veor		@XMM[0], @XMM[0], @XMM[8]
+	mov		r1, sp
+	vst1.8		{@XMM[0]}, [sp,:128]
+	mov		r2, $key
+	mov		r4, $fp				@ preserve fp
+
+	bl		AES_encrypt
+
+	vld1.8		{@XMM[0]}, [sp,:128]
+	veor		@XMM[0], @XMM[0], @XMM[8]
+	vst1.8		{@XMM[0]}, [$out]!
+	mov		$fp, r4
+
+	vmov		@XMM[8], @XMM[9]		@ next round tweak
+
+.Lxts_enc_done:
+#ifndef	XTS_CHAIN_TWEAK
+	adds		$len, #0x10
+	beq		.Lxts_enc_ret
+	sub		r6, $out, #0x10
+
+.Lxts_enc_steal:
+	ldrb		r0, [$inp], #1
+	ldrb		r1, [$out, #-0x10]
+	strb		r0, [$out, #-0x10]
+	strb		r1, [$out], #1
+
+	subs		$len, #1
+	bhi		.Lxts_enc_steal
+
+	vld1.8		{@XMM[0]}, [r6]
+	mov		r0, sp
+	veor		@XMM[0], @XMM[0], @XMM[8]
+	mov		r1, sp
+	vst1.8		{@XMM[0]}, [sp,:128]
+	mov		r2, $key
+	mov		r4, $fp			@ preserve fp
+
+	bl		AES_encrypt
+
+	vld1.8		{@XMM[0]}, [sp,:128]
+	veor		@XMM[0], @XMM[0], @XMM[8]
+	vst1.8		{@XMM[0]}, [r6]
+	mov		$fp, r4
+#endif
+
+.Lxts_enc_ret:
+	bic		r0, $fp, #0xf
+	vmov.i32	q0, #0
+	vmov.i32	q1, #0
+#ifdef	XTS_CHAIN_TWEAK
+	ldr		r1, [$fp, #0x20+VFP_ABI_FRAME]	@ chain tweak
+#endif
+.Lxts_enc_bzero:				@ wipe key schedule [if any]
+	vstmia		sp!, {q0-q1}
+	cmp		sp, r0
+	bne		.Lxts_enc_bzero
+
+	mov		sp, $fp
+#ifdef	XTS_CHAIN_TWEAK
+	vst1.8		{@XMM[8]}, [r1]
+#endif
+	VFP_ABI_POP
+	ldmia		sp!, {r4-r10, pc}	@ return
+
+.size	bsaes_xts_encrypt,.-bsaes_xts_encrypt
+
+.globl	bsaes_xts_decrypt
+.type	bsaes_xts_decrypt,%function
+.align	4
+bsaes_xts_decrypt:
+	mov	ip, sp
+	stmdb	sp!, {r4-r10, lr}		@ 0x20
+	VFP_ABI_PUSH
+	mov	r6, sp				@ future $fp
+
+	mov	$inp, r0
+	mov	$out, r1
+	mov	$len, r2
+	mov	$key, r3
+
+	sub	r0, sp, #0x10			@ 0x10
+	bic	r0, #0xf			@ align at 16 bytes
+	mov	sp, r0
+
+#ifdef	XTS_CHAIN_TWEAK
+	ldr	r0, [ip]			@ pointer to input tweak
+#else
+	@ generate initial tweak
+	ldr	r0, [ip, #4]			@ iv[]
+	mov	r1, sp
+	ldr	r2, [ip, #0]			@ key2
+	bl	AES_encrypt
+	mov	r0, sp				@ pointer to initial tweak
+#endif
+
+	ldr	$rounds, [$key, #240]		@ get # of rounds
+	mov	$fp, r6
+#ifndef	BSAES_ASM_EXTENDED_KEY
+	@ allocate the key schedule on the stack
+	sub	r12, sp, $rounds, lsl#7		@ 128 bytes per inner round key
+	@ add	r12, #`128-32`			@ size of bit-sliced key schedule
+	sub	r12, #`32+16`			@ place for tweak[9]
+
+	@ populate the key schedule
+	mov	r4, $key			@ pass key
+	mov	r5, $rounds			@ pass # of rounds
+	mov	sp, r12
+	add	r12, #0x90			@ pass key schedule
+	bl	_bsaes_key_convert
+	add	r4, sp, #0x90
+	vldmia	r4, {@XMM[6]}
+	vstmia	r12,  {@XMM[15]}		@ save last round key
+	veor	@XMM[7], @XMM[7], @XMM[6]	@ fix up round 0 key
+	vstmia	r4, {@XMM[7]}
+#else
+	ldr	r12, [$key, #244]
+	eors	r12, #1
+	beq	0f
+
+	str	r12, [$key, #244]
+	mov	r4, $key			@ pass key
+	mov	r5, $rounds			@ pass # of rounds
+	add	r12, $key, #248			@ pass key schedule
+	bl	_bsaes_key_convert
+	add	r4, $key, #248
+	vldmia	r4, {@XMM[6]}
+	vstmia	r12,  {@XMM[15]}		@ save last round key
+	veor	@XMM[7], @XMM[7], @XMM[6]	@ fix up round 0 key
+	vstmia	r4, {@XMM[7]}
+
+.align	2
+0:	sub	sp, #0x90			@ place for tweak[9]
+#endif
+	vld1.8	{@XMM[8]}, [r0]			@ initial tweak
+	adr	$magic, .Lxts_magic
+
+#ifndef	XTS_CHAIN_TWEAK
+	tst	$len, #0xf			@ if not multiple of 16
+	it	ne				@ Thumb2 thing, sanity check in ARM
+	subne	$len, #0x10			@ subtract another 16 bytes
+#endif
+	subs	$len, #0x80
+
+	blo	.Lxts_dec_short
+	b	.Lxts_dec_loop
+
+.align	4
+.Lxts_dec_loop:
+	vldmia		$magic, {$twmask}	@ load XTS magic
+	vshr.s64	@T[0], @XMM[8], #63
+	mov		r0, sp
+	vand		@T[0], @T[0], $twmask
+___
+for($i=9;$i<16;$i++) {
+$code.=<<___;
+	vadd.u64	@XMM[$i], @XMM[$i-1], @XMM[$i-1]
+	vst1.64		{@XMM[$i-1]}, [r0,:128]!
+	vswp		`&Dhi("@T[0]")`,`&Dlo("@T[0]")`
+	vshr.s64	@T[1], @XMM[$i], #63
+	veor		@XMM[$i], @XMM[$i], @T[0]
+	vand		@T[1], @T[1], $twmask
+___
+	@T=reverse(@T);
+
+$code.=<<___ if ($i>=10);
+	vld1.8		{@XMM[$i-10]}, [$inp]!
+___
+$code.=<<___ if ($i>=11);
+	veor		@XMM[$i-11], @XMM[$i-11], @XMM[$i-3]
+___
+}
+$code.=<<___;
+	vadd.u64	@XMM[8], @XMM[15], @XMM[15]
+	vst1.64		{@XMM[15]}, [r0,:128]!
+	vswp		`&Dhi("@T[0]")`,`&Dlo("@T[0]")`
+	veor		@XMM[8], @XMM[8], @T[0]
+	vst1.64		{@XMM[8]}, [r0,:128]		@ next round tweak
+
+	vld1.8		{@XMM[6]-@XMM[7]}, [$inp]!
+	veor		@XMM[5], @XMM[5], @XMM[13]
+#ifndef	BSAES_ASM_EXTENDED_KEY
+	add		r4, sp, #0x90			@ pass key schedule
+#else
+	add		r4, $key, #248			@ pass key schedule
+#endif
+	veor		@XMM[6], @XMM[6], @XMM[14]
+	mov		r5, $rounds			@ pass rounds
+	veor		@XMM[7], @XMM[7], @XMM[15]
+	mov		r0, sp
+
+	bl		_bsaes_decrypt8
+
+	vld1.64		{@XMM[ 8]-@XMM[ 9]}, [r0,:128]!
+	vld1.64		{@XMM[10]-@XMM[11]}, [r0,:128]!
+	veor		@XMM[0], @XMM[0], @XMM[ 8]
+	vld1.64		{@XMM[12]-@XMM[13]}, [r0,:128]!
+	veor		@XMM[1], @XMM[1], @XMM[ 9]
+	veor		@XMM[8], @XMM[6], @XMM[10]
+	vst1.8		{@XMM[0]-@XMM[1]}, [$out]!
+	veor		@XMM[9], @XMM[4], @XMM[11]
+	vld1.64		{@XMM[14]-@XMM[15]}, [r0,:128]!
+	veor		@XMM[10], @XMM[2], @XMM[12]
+	vst1.8		{@XMM[8]-@XMM[9]}, [$out]!
+	veor		@XMM[11], @XMM[7], @XMM[13]
+	veor		@XMM[12], @XMM[3], @XMM[14]
+	vst1.8		{@XMM[10]-@XMM[11]}, [$out]!
+	veor		@XMM[13], @XMM[5], @XMM[15]
+	vst1.8		{@XMM[12]-@XMM[13]}, [$out]!
+
+	vld1.64		{@XMM[8]}, [r0,:128]		@ next round tweak
+
+	subs		$len, #0x80
+	bpl		.Lxts_dec_loop
+
+.Lxts_dec_short:
+	adds		$len, #0x70
+	bmi		.Lxts_dec_done
+
+	vldmia		$magic, {$twmask}	@ load XTS magic
+	vshr.s64	@T[0], @XMM[8], #63
+	mov		r0, sp
+	vand		@T[0], @T[0], $twmask
+___
+for($i=9;$i<16;$i++) {
+$code.=<<___;
+	vadd.u64	@XMM[$i], @XMM[$i-1], @XMM[$i-1]
+	vst1.64		{@XMM[$i-1]}, [r0,:128]!
+	vswp		`&Dhi("@T[0]")`,`&Dlo("@T[0]")`
+	vshr.s64	@T[1], @XMM[$i], #63
+	veor		@XMM[$i], @XMM[$i], @T[0]
+	vand		@T[1], @T[1], $twmask
+___
+	@T=reverse(@T);
+
+$code.=<<___ if ($i>=10);
+	vld1.8		{@XMM[$i-10]}, [$inp]!
+	subs		$len, #0x10
+	bmi		.Lxts_dec_`$i-9`
+___
+$code.=<<___ if ($i>=11);
+	veor		@XMM[$i-11], @XMM[$i-11], @XMM[$i-3]
+___
+}
+$code.=<<___;
+	sub		$len, #0x10
+	vst1.64		{@XMM[15]}, [r0,:128]		@ next round tweak
+
+	vld1.8		{@XMM[6]}, [$inp]!
+	veor		@XMM[5], @XMM[5], @XMM[13]
+#ifndef	BSAES_ASM_EXTENDED_KEY
+	add		r4, sp, #0x90			@ pass key schedule
+#else
+	add		r4, $key, #248			@ pass key schedule
+#endif
+	veor		@XMM[6], @XMM[6], @XMM[14]
+	mov		r5, $rounds			@ pass rounds
+	mov		r0, sp
+
+	bl		_bsaes_decrypt8
+
+	vld1.64		{@XMM[ 8]-@XMM[ 9]}, [r0,:128]!
+	vld1.64		{@XMM[10]-@XMM[11]}, [r0,:128]!
+	veor		@XMM[0], @XMM[0], @XMM[ 8]
+	vld1.64		{@XMM[12]-@XMM[13]}, [r0,:128]!
+	veor		@XMM[1], @XMM[1], @XMM[ 9]
+	veor		@XMM[8], @XMM[6], @XMM[10]
+	vst1.8		{@XMM[0]-@XMM[1]}, [$out]!
+	veor		@XMM[9], @XMM[4], @XMM[11]
+	vld1.64		{@XMM[14]}, [r0,:128]!
+	veor		@XMM[10], @XMM[2], @XMM[12]
+	vst1.8		{@XMM[8]-@XMM[9]}, [$out]!
+	veor		@XMM[11], @XMM[7], @XMM[13]
+	veor		@XMM[12], @XMM[3], @XMM[14]
+	vst1.8		{@XMM[10]-@XMM[11]}, [$out]!
+	vst1.8		{@XMM[12]}, [$out]!
+
+	vld1.64		{@XMM[8]}, [r0,:128]		@ next round tweak
+	b		.Lxts_dec_done
+.align	4
+.Lxts_dec_6:
+	vst1.64		{@XMM[14]}, [r0,:128]		@ next round tweak
+
+	veor		@XMM[4], @XMM[4], @XMM[12]
+#ifndef	BSAES_ASM_EXTENDED_KEY
+	add		r4, sp, #0x90			@ pass key schedule
+#else
+	add		r4, $key, #248			@ pass key schedule
+#endif
+	veor		@XMM[5], @XMM[5], @XMM[13]
+	mov		r5, $rounds			@ pass rounds
+	mov		r0, sp
+
+	bl		_bsaes_decrypt8
+
+	vld1.64		{@XMM[ 8]-@XMM[ 9]}, [r0,:128]!
+	vld1.64		{@XMM[10]-@XMM[11]}, [r0,:128]!
+	veor		@XMM[0], @XMM[0], @XMM[ 8]
+	vld1.64		{@XMM[12]-@XMM[13]}, [r0,:128]!
+	veor		@XMM[1], @XMM[1], @XMM[ 9]
+	veor		@XMM[8], @XMM[6], @XMM[10]
+	vst1.8		{@XMM[0]-@XMM[1]}, [$out]!
+	veor		@XMM[9], @XMM[4], @XMM[11]
+	veor		@XMM[10], @XMM[2], @XMM[12]
+	vst1.8		{@XMM[8]-@XMM[9]}, [$out]!
+	veor		@XMM[11], @XMM[7], @XMM[13]
+	vst1.8		{@XMM[10]-@XMM[11]}, [$out]!
+
+	vld1.64		{@XMM[8]}, [r0,:128]		@ next round tweak
+	b		.Lxts_dec_done
+.align	4
+.Lxts_dec_5:
+	veor		@XMM[3], @XMM[3], @XMM[11]
+#ifndef	BSAES_ASM_EXTENDED_KEY
+	add		r4, sp, #0x90			@ pass key schedule
+#else
+	add		r4, $key, #248			@ pass key schedule
+#endif
+	veor		@XMM[4], @XMM[4], @XMM[12]
+	mov		r5, $rounds			@ pass rounds
+	mov		r0, sp
+
+	bl		_bsaes_decrypt8
+
+	vld1.64		{@XMM[ 8]-@XMM[ 9]}, [r0,:128]!
+	vld1.64		{@XMM[10]-@XMM[11]}, [r0,:128]!
+	veor		@XMM[0], @XMM[0], @XMM[ 8]
+	vld1.64		{@XMM[12]}, [r0,:128]!
+	veor		@XMM[1], @XMM[1], @XMM[ 9]
+	veor		@XMM[8], @XMM[6], @XMM[10]
+	vst1.8		{@XMM[0]-@XMM[1]}, [$out]!
+	veor		@XMM[9], @XMM[4], @XMM[11]
+	veor		@XMM[10], @XMM[2], @XMM[12]
+	vst1.8		{@XMM[8]-@XMM[9]}, [$out]!
+	vst1.8		{@XMM[10]}, [$out]!
+
+	vld1.64		{@XMM[8]}, [r0,:128]		@ next round tweak
+	b		.Lxts_dec_done
+.align	4
+.Lxts_dec_4:
+	veor		@XMM[2], @XMM[2], @XMM[10]
+#ifndef	BSAES_ASM_EXTENDED_KEY
+	add		r4, sp, #0x90			@ pass key schedule
+#else
+	add		r4, $key, #248			@ pass key schedule
+#endif
+	veor		@XMM[3], @XMM[3], @XMM[11]
+	mov		r5, $rounds			@ pass rounds
+	mov		r0, sp
+
+	bl		_bsaes_decrypt8
+
+	vld1.64		{@XMM[ 8]-@XMM[ 9]}, [r0,:128]!
+	vld1.64		{@XMM[10]-@XMM[11]}, [r0,:128]!
+	veor		@XMM[0], @XMM[0], @XMM[ 8]
+	veor		@XMM[1], @XMM[1], @XMM[ 9]
+	veor		@XMM[8], @XMM[6], @XMM[10]
+	vst1.8		{@XMM[0]-@XMM[1]}, [$out]!
+	veor		@XMM[9], @XMM[4], @XMM[11]
+	vst1.8		{@XMM[8]-@XMM[9]}, [$out]!
+
+	vld1.64		{@XMM[8]}, [r0,:128]		@ next round tweak
+	b		.Lxts_dec_done
+.align	4
+.Lxts_dec_3:
+	veor		@XMM[1], @XMM[1], @XMM[9]
+#ifndef	BSAES_ASM_EXTENDED_KEY
+	add		r4, sp, #0x90			@ pass key schedule
+#else
+	add		r4, $key, #248			@ pass key schedule
+#endif
+	veor		@XMM[2], @XMM[2], @XMM[10]
+	mov		r5, $rounds			@ pass rounds
+	mov		r0, sp
+
+	bl		_bsaes_decrypt8
+
+	vld1.64		{@XMM[8]-@XMM[9]}, [r0,:128]!
+	vld1.64		{@XMM[10]}, [r0,:128]!
+	veor		@XMM[0], @XMM[0], @XMM[ 8]
+	veor		@XMM[1], @XMM[1], @XMM[ 9]
+	veor		@XMM[8], @XMM[6], @XMM[10]
+	vst1.8		{@XMM[0]-@XMM[1]}, [$out]!
+	vst1.8		{@XMM[8]}, [$out]!
+
+	vld1.64		{@XMM[8]}, [r0,:128]		@ next round tweak
+	b		.Lxts_dec_done
+.align	4
+.Lxts_dec_2:
+	veor		@XMM[0], @XMM[0], @XMM[8]
+#ifndef	BSAES_ASM_EXTENDED_KEY
+	add		r4, sp, #0x90			@ pass key schedule
+#else
+	add		r4, $key, #248			@ pass key schedule
+#endif
+	veor		@XMM[1], @XMM[1], @XMM[9]
+	mov		r5, $rounds			@ pass rounds
+	mov		r0, sp
+
+	bl		_bsaes_decrypt8
+
+	vld1.64		{@XMM[8]-@XMM[9]}, [r0,:128]!
+	veor		@XMM[0], @XMM[0], @XMM[ 8]
+	veor		@XMM[1], @XMM[1], @XMM[ 9]
+	vst1.8		{@XMM[0]-@XMM[1]}, [$out]!
+
+	vld1.64		{@XMM[8]}, [r0,:128]		@ next round tweak
+	b		.Lxts_dec_done
+.align	4
+.Lxts_dec_1:
+	mov		r0, sp
+	veor		@XMM[0], @XMM[0], @XMM[8]
+	mov		r1, sp
+	vst1.8		{@XMM[0]}, [sp,:128]
+	mov		r5, $magic			@ preserve magic
+	mov		r2, $key
+	mov		r4, $fp				@ preserve fp
+
+	bl		AES_decrypt
+
+	vld1.8		{@XMM[0]}, [sp,:128]
+	veor		@XMM[0], @XMM[0], @XMM[8]
+	vst1.8		{@XMM[0]}, [$out]!
+	mov		$fp, r4
+	mov		$magic, r5
+
+	vmov		@XMM[8], @XMM[9]		@ next round tweak
+
+.Lxts_dec_done:
+#ifndef	XTS_CHAIN_TWEAK
+	adds		$len, #0x10
+	beq		.Lxts_dec_ret
+
+	@ calculate one round of extra tweak for the stolen ciphertext
+	vldmia		$magic, {$twmask}
+	vshr.s64	@XMM[6], @XMM[8], #63
+	vand		@XMM[6], @XMM[6], $twmask
+	vadd.u64	@XMM[9], @XMM[8], @XMM[8]
+	vswp		`&Dhi("@XMM[6]")`,`&Dlo("@XMM[6]")`
+	veor		@XMM[9], @XMM[9], @XMM[6]
+
+	@ perform the final decryption with the last tweak value
+	vld1.8		{@XMM[0]}, [$inp]!
+	mov		r0, sp
+	veor		@XMM[0], @XMM[0], @XMM[9]
+	mov		r1, sp
+	vst1.8		{@XMM[0]}, [sp,:128]
+	mov		r2, $key
+	mov		r4, $fp			@ preserve fp
+
+	bl		AES_decrypt
+
+	vld1.8		{@XMM[0]}, [sp,:128]
+	veor		@XMM[0], @XMM[0], @XMM[9]
+	vst1.8		{@XMM[0]}, [$out]
+
+	mov		r6, $out
+.Lxts_dec_steal:
+	ldrb		r1, [$out]
+	ldrb		r0, [$inp], #1
+	strb		r1, [$out, #0x10]
+	strb		r0, [$out], #1
+
+	subs		$len, #1
+	bhi		.Lxts_dec_steal
+
+	vld1.8		{@XMM[0]}, [r6]
+	mov		r0, sp
+	veor		@XMM[0], @XMM[8]
+	mov		r1, sp
+	vst1.8		{@XMM[0]}, [sp,:128]
+	mov		r2, $key
+
+	bl		AES_decrypt
+
+	vld1.8		{@XMM[0]}, [sp,:128]
+	veor		@XMM[0], @XMM[0], @XMM[8]
+	vst1.8		{@XMM[0]}, [r6]
+	mov		$fp, r4
+#endif
+
+.Lxts_dec_ret:
+	bic		r0, $fp, #0xf
+	vmov.i32	q0, #0
+	vmov.i32	q1, #0
+#ifdef	XTS_CHAIN_TWEAK
+	ldr		r1, [$fp, #0x20+VFP_ABI_FRAME]	@ chain tweak
+#endif
+.Lxts_dec_bzero:				@ wipe key schedule [if any]
+	vstmia		sp!, {q0-q1}
+	cmp		sp, r0
+	bne		.Lxts_dec_bzero
+
+	mov		sp, $fp
+#ifdef	XTS_CHAIN_TWEAK
+	vst1.8		{@XMM[8]}, [r1]
+#endif
+	VFP_ABI_POP
+	ldmia		sp!, {r4-r10, pc}	@ return
+
+.size	bsaes_xts_decrypt,.-bsaes_xts_decrypt
+___
+}
+$code.=<<___;
+#endif
+___
+
+$code =~ s/\`([^\`]*)\`/eval($1)/gem;
+
+open SELF,$0;
+while() {
+	next if (/^#!/);
+        last if (!s/^#/@/ and !/^$/);
+        print;
+}
+close SELF;
+
+print $code;
+
+close STDOUT;
diff --git a/crypto/aes/asm/bsaes-x86_64.pl b/crypto/aes/asm/bsaes-x86_64.pl
new file mode 100644
index 000000000..921d870e9
--- /dev/null
+++ b/crypto/aes/asm/bsaes-x86_64.pl
@@ -0,0 +1,3111 @@
+#! /usr/bin/env perl
+# Copyright 2011-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+
+###################################################################
+### AES-128 [originally in CTR mode]				###
+### bitsliced implementation for Intel Core 2 processors	###
+### requires support of SSE extensions up to SSSE3		###
+### Author: Emilia Käsper and Peter Schwabe			###
+### Date: 2009-03-19						###
+### Public domain						###
+###								###
+### See http://homes.esat.kuleuven.be/~ekasper/#software for	###
+### further information.					###
+###################################################################
+#
+# September 2011.
+#
+# Started as transliteration to "perlasm" the original code has
+# undergone following changes:
+#
+# - code was made position-independent;
+# - rounds were folded into a loop resulting in >5x size reduction
+#   from 12.5KB to 2.2KB;
+# - above was possibile thanks to mixcolumns() modification that
+#   allowed to feed its output back to aesenc[last], this was
+#   achieved at cost of two additional inter-registers moves;
+# - some instruction reordering and interleaving;
+# - this module doesn't implement key setup subroutine, instead it
+#   relies on conversion of "conventional" key schedule as returned
+#   by AES_set_encrypt_key (see discussion below);
+# - first and last round keys are treated differently, which allowed
+#   to skip one shiftrows(), reduce bit-sliced key schedule and
+#   speed-up conversion by 22%;
+# - support for 192- and 256-bit keys was added;
+#
+# Resulting performance in CPU cycles spent to encrypt one byte out
+# of 4096-byte buffer with 128-bit key is:
+#
+#		Emilia's	this(*)		difference
+#
+# Core 2    	9.30		8.69		+7%
+# Nehalem(**) 	7.63		6.88		+11%
+# Atom	    	17.1		16.4		+4%
+# Silvermont	-		12.9
+# Goldmont	-		8.85
+#
+# (*)	Comparison is not completely fair, because "this" is ECB,
+#	i.e. no extra processing such as counter values calculation
+#	and xor-ing input as in Emilia's CTR implementation is
+#	performed. However, the CTR calculations stand for not more
+#	than 1% of total time, so comparison is *rather* fair.
+#
+# (**)	Results were collected on Westmere, which is considered to
+#	be equivalent to Nehalem for this code.
+#
+# As for key schedule conversion subroutine. Interface to OpenSSL
+# relies on per-invocation on-the-fly conversion. This naturally
+# has impact on performance, especially for short inputs. Conversion
+# time in CPU cycles and its ratio to CPU cycles spent in 8x block
+# function is:
+#
+# 		conversion	conversion/8x block
+# Core 2	240		0.22
+# Nehalem	180		0.20
+# Atom		430		0.20
+#
+# The ratio values mean that 128-byte blocks will be processed
+# 16-18% slower, 256-byte blocks - 9-10%, 384-byte blocks - 6-7%,
+# etc. Then keep in mind that input sizes not divisible by 128 are
+# *effectively* slower, especially shortest ones, e.g. consecutive
+# 144-byte blocks are processed 44% slower than one would expect,
+# 272 - 29%, 400 - 22%, etc. Yet, despite all these "shortcomings"
+# it's still faster than ["hyper-threading-safe" code path in]
+# aes-x86_64.pl on all lengths above 64 bytes...
+#
+# October 2011.
+#
+# Add decryption procedure. Performance in CPU cycles spent to decrypt
+# one byte out of 4096-byte buffer with 128-bit key is:
+#
+# Core 2	9.98
+# Nehalem	7.80
+# Atom		17.9
+# Silvermont	14.0
+# Goldmont	10.2
+#
+# November 2011.
+#
+# Add bsaes_xts_[en|de]crypt. Less-than-80-bytes-block performance is
+# suboptimal, but XTS is meant to be used with larger blocks...
+#
+#						
+
+$flavour = shift;
+$output  = shift;
+if ($flavour =~ /\./) { $output = $flavour; undef $flavour; }
+
+$win64=0; $win64=1 if ($flavour =~ /[nm]asm|mingw64/ || $output =~ /\.asm$/);
+
+$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+( $xlate="${dir}x86_64-xlate.pl" and -f $xlate ) or
+( $xlate="${dir}../../perlasm/x86_64-xlate.pl" and -f $xlate) or
+die "can't locate x86_64-xlate.pl";
+
+open OUT,"| \"$^X\" \"$xlate\" $flavour \"$output\"";
+*STDOUT=*OUT;
+
+my ($inp,$out,$len,$key,$ivp)=("%rdi","%rsi","%rdx","%rcx");
+my @XMM=map("%xmm$_",(15,0..14));	# best on Atom, +10% over (0..15)
+my $ecb=0;	# suppress unreferenced ECB subroutines, spare some space...
+
+{
+my ($key,$rounds,$const)=("%rax","%r10d","%r11");
+
+sub Sbox {
+# input in  lsb > [b0, b1, b2, b3, b4, b5, b6, b7] < msb
+# output in lsb > [b0, b1, b4, b6, b3, b7, b2, b5] < msb
+my @b=@_[0..7];
+my @t=@_[8..11];
+my @s=@_[12..15];
+	&InBasisChange	(@b);
+	&Inv_GF256	(@b[6,5,0,3,7,1,4,2],@t,@s);
+	&OutBasisChange	(@b[7,1,4,2,6,5,0,3]);
+}
+
+sub InBasisChange {
+# input in  lsb > [b0, b1, b2, b3, b4, b5, b6, b7] < msb
+# output in lsb > [b6, b5, b0, b3, b7, b1, b4, b2] < msb 
+my @b=@_[0..7];
+$code.=<<___;
+	pxor	@b[6], @b[5]
+	pxor	@b[1], @b[2]
+	pxor	@b[0], @b[3]
+	pxor	@b[2], @b[6]
+	pxor 	@b[0], @b[5]
+
+	pxor	@b[3], @b[6]
+	pxor	@b[7], @b[3]
+	pxor	@b[5], @b[7]
+	pxor	@b[4], @b[3]
+	pxor	@b[5], @b[4]
+	pxor	@b[1], @b[3]
+
+	pxor	@b[7], @b[2]
+	pxor	@b[5], @b[1]
+___
+}
+
+sub OutBasisChange {
+# input in  lsb > [b0, b1, b2, b3, b4, b5, b6, b7] < msb
+# output in lsb > [b6, b1, b2, b4, b7, b0, b3, b5] < msb
+my @b=@_[0..7];
+$code.=<<___;
+	pxor	@b[6], @b[0]
+	pxor	@b[4], @b[1]
+	pxor	@b[0], @b[2]
+	pxor	@b[6], @b[4]
+	pxor	@b[1], @b[6]
+
+	pxor	@b[5], @b[1]
+	pxor	@b[3], @b[5]
+	pxor	@b[7], @b[3]
+	pxor	@b[5], @b[7]
+	pxor	@b[5], @b[2]
+
+	pxor	@b[7], @b[4]
+___
+}
+
+sub InvSbox {
+# input in lsb 	> [b0, b1, b2, b3, b4, b5, b6, b7] < msb
+# output in lsb	> [b0, b1, b6, b4, b2, b7, b3, b5] < msb
+my @b=@_[0..7];
+my @t=@_[8..11];
+my @s=@_[12..15];
+	&InvInBasisChange	(@b);
+	&Inv_GF256		(@b[5,1,2,6,3,7,0,4],@t,@s);
+	&InvOutBasisChange	(@b[3,7,0,4,5,1,2,6]);
+}
+
+sub InvInBasisChange {		# OutBasisChange in reverse
+my @b=@_[5,1,2,6,3,7,0,4];
+$code.=<<___
+	pxor	@b[7], @b[4]
+
+	pxor	@b[5], @b[7]
+	pxor	@b[5], @b[2]
+	pxor	@b[7], @b[3]
+	pxor	@b[3], @b[5]
+	pxor	@b[5], @b[1]
+
+	pxor	@b[1], @b[6]
+	pxor	@b[0], @b[2]
+	pxor	@b[6], @b[4]
+	pxor	@b[6], @b[0]
+	pxor	@b[4], @b[1]
+___
+}
+
+sub InvOutBasisChange {		# InBasisChange in reverse
+my @b=@_[2,5,7,3,6,1,0,4];
+$code.=<<___;
+	pxor	@b[5], @b[1]
+	pxor	@b[7], @b[2]
+
+	pxor	@b[1], @b[3]
+	pxor	@b[5], @b[4]
+	pxor	@b[5], @b[7]
+	pxor	@b[4], @b[3]
+	 pxor 	@b[0], @b[5]
+	pxor	@b[7], @b[3]
+	 pxor	@b[2], @b[6]
+	 pxor	@b[1], @b[2]
+	pxor	@b[3], @b[6]
+
+	pxor	@b[0], @b[3]
+	pxor	@b[6], @b[5]
+___
+}
+
+sub Mul_GF4 {
+#;*************************************************************
+#;* Mul_GF4: Input x0-x1,y0-y1 Output x0-x1 Temp t0 (8) *
+#;*************************************************************
+my ($x0,$x1,$y0,$y1,$t0)=@_;
+$code.=<<___;
+	movdqa	$y0, $t0
+	pxor 	$y1, $t0
+	pand	$x0, $t0
+	pxor	$x1, $x0
+	pand	$y0, $x1
+	pand	$y1, $x0
+	pxor	$x1, $x0
+	pxor	$t0, $x1
+___
+}
+
+sub Mul_GF4_N {				# not used, see next subroutine
+# multiply and scale by N
+my ($x0,$x1,$y0,$y1,$t0)=@_;
+$code.=<<___;
+	movdqa	$y0, $t0
+	pxor	$y1, $t0
+	pand	$x0, $t0
+	pxor	$x1, $x0
+	pand	$y0, $x1
+	pand	$y1, $x0
+	pxor	$x0, $x1
+	pxor	$t0, $x0
+___
+}
+
+sub Mul_GF4_N_GF4 {
+# interleaved Mul_GF4_N and Mul_GF4
+my ($x0,$x1,$y0,$y1,$t0,
+    $x2,$x3,$y2,$y3,$t1)=@_;
+$code.=<<___;
+	movdqa	$y0, $t0
+	 movdqa	$y2, $t1
+	pxor	$y1, $t0
+	 pxor 	$y3, $t1
+	pand	$x0, $t0
+	 pand	$x2, $t1
+	pxor	$x1, $x0
+	 pxor	$x3, $x2
+	pand	$y0, $x1
+	 pand	$y2, $x3
+	pand	$y1, $x0
+	 pand	$y3, $x2
+	pxor	$x0, $x1
+	 pxor	$x3, $x2
+	pxor	$t0, $x0
+	 pxor	$t1, $x3
+___
+}
+sub Mul_GF16_2 {
+my @x=@_[0..7];
+my @y=@_[8..11];
+my @t=@_[12..15];
+$code.=<<___;
+	movdqa	@x[0], @t[0]
+	movdqa	@x[1], @t[1]
+___
+	&Mul_GF4  	(@x[0], @x[1], @y[0], @y[1], @t[2]);
+$code.=<<___;
+	pxor	@x[2], @t[0]
+	pxor	@x[3], @t[1]
+	pxor	@y[2], @y[0]
+	pxor	@y[3], @y[1]
+___
+	Mul_GF4_N_GF4	(@t[0], @t[1], @y[0], @y[1], @t[3],
+			 @x[2], @x[3], @y[2], @y[3], @t[2]);
+$code.=<<___;
+	pxor	@t[0], @x[0]
+	pxor	@t[0], @x[2]
+	pxor	@t[1], @x[1]
+	pxor	@t[1], @x[3]
+
+	movdqa	@x[4], @t[0]
+	movdqa	@x[5], @t[1]
+	pxor	@x[6], @t[0]
+	pxor	@x[7], @t[1]
+___
+	&Mul_GF4_N_GF4	(@t[0], @t[1], @y[0], @y[1], @t[3],
+			 @x[6], @x[7], @y[2], @y[3], @t[2]);
+$code.=<<___;
+	pxor	@y[2], @y[0]
+	pxor	@y[3], @y[1]
+___
+	&Mul_GF4  	(@x[4], @x[5], @y[0], @y[1], @t[3]);
+$code.=<<___;
+	pxor	@t[0], @x[4]
+	pxor	@t[0], @x[6]
+	pxor	@t[1], @x[5]
+	pxor	@t[1], @x[7]
+___
+}
+sub Inv_GF256 {
+#;********************************************************************
+#;* Inv_GF256: Input x0-x7 Output x0-x7 Temp t0-t3,s0-s3 (144)       *
+#;********************************************************************
+my @x=@_[0..7];
+my @t=@_[8..11];
+my @s=@_[12..15];
+# direct optimizations from hardware
+$code.=<<___;
+	movdqa	@x[4], @t[3]
+	movdqa	@x[5], @t[2]
+	movdqa	@x[1], @t[1]
+	movdqa	@x[7], @s[1]
+	movdqa	@x[0], @s[0]
+
+	pxor	@x[6], @t[3]
+	pxor	@x[7], @t[2]
+	pxor	@x[3], @t[1]
+	 movdqa	@t[3], @s[2]
+	pxor	@x[6], @s[1]
+	 movdqa	@t[2], @t[0]
+	pxor	@x[2], @s[0]
+	 movdqa	@t[3], @s[3]
+
+	por	@t[1], @t[2]
+	por	@s[0], @t[3]
+	pxor	@t[0], @s[3]
+	pand	@s[0], @s[2]
+	pxor	@t[1], @s[0]
+	pand	@t[1], @t[0]
+	pand	@s[0], @s[3]
+	movdqa	@x[3], @s[0]
+	pxor	@x[2], @s[0]
+	pand	@s[0], @s[1]
+	pxor	@s[1], @t[3]
+	pxor	@s[1], @t[2]
+	movdqa	@x[4], @s[1]
+	movdqa	@x[1], @s[0]
+	pxor	@x[5], @s[1]
+	pxor	@x[0], @s[0]
+	movdqa	@s[1], @t[1]
+	pand	@s[0], @s[1]
+	por	@s[0], @t[1]
+	pxor	@s[1], @t[0]
+	pxor	@s[3], @t[3]
+	pxor	@s[2], @t[2]
+	pxor	@s[3], @t[1]
+	movdqa	@x[7], @s[0]
+	pxor	@s[2], @t[0]
+	movdqa	@x[6], @s[1]
+	pxor	@s[2], @t[1]
+	movdqa	@x[5], @s[2]
+	pand	@x[3], @s[0]
+	movdqa	@x[4], @s[3]
+	pand	@x[2], @s[1]
+	pand	@x[1], @s[2]
+	por	@x[0], @s[3]
+	pxor	@s[0], @t[3]
+	pxor	@s[1], @t[2]
+	pxor	@s[2], @t[1]
+	pxor	@s[3], @t[0] 
+
+	#Inv_GF16 \t0, \t1, \t2, \t3, \s0, \s1, \s2, \s3
+
+	# new smaller inversion
+
+	movdqa	@t[3], @s[0]
+	pand	@t[1], @t[3]
+	pxor	@t[2], @s[0]
+
+	movdqa	@t[0], @s[2]
+	movdqa	@s[0], @s[3]
+	pxor	@t[3], @s[2]
+	pand	@s[2], @s[3]
+
+	movdqa	@t[1], @s[1]
+	pxor	@t[2], @s[3]
+	pxor	@t[0], @s[1]
+
+	pxor	@t[2], @t[3]
+
+	pand	@t[3], @s[1]
+
+	movdqa	@s[2], @t[2]
+	pxor	@t[0], @s[1]
+
+	pxor	@s[1], @t[2]
+	pxor	@s[1], @t[1]
+
+	pand	@t[0], @t[2]
+
+	pxor	@t[2], @s[2]
+	pxor	@t[2], @t[1]
+
+	pand	@s[3], @s[2]
+
+	pxor	@s[0], @s[2]
+___
+# output in s3, s2, s1, t1
+
+# Mul_GF16_2 \x0, \x1, \x2, \x3, \x4, \x5, \x6, \x7, \t2, \t3, \t0, \t1, \s0, \s1, \s2, \s3
+
+# Mul_GF16_2 \x0, \x1, \x2, \x3, \x4, \x5, \x6, \x7, \s3, \s2, \s1, \t1, \s0, \t0, \t2, \t3
+	&Mul_GF16_2(@x,@s[3,2,1],@t[1],@s[0],@t[0,2,3]);
+
+### output msb > [x3,x2,x1,x0,x7,x6,x5,x4] < lsb
+}
+
+# AES linear components
+
+sub ShiftRows {
+my @x=@_[0..7];
+my $mask=pop;
+$code.=<<___;
+	pxor	0x00($key),@x[0]
+	pxor	0x10($key),@x[1]
+	pxor	0x20($key),@x[2]
+	pxor	0x30($key),@x[3]
+	pshufb	$mask,@x[0]
+	pshufb	$mask,@x[1]
+	pxor	0x40($key),@x[4]
+	pxor	0x50($key),@x[5]
+	pshufb	$mask,@x[2]
+	pshufb	$mask,@x[3]
+	pxor	0x60($key),@x[6]
+	pxor	0x70($key),@x[7]
+	pshufb	$mask,@x[4]
+	pshufb	$mask,@x[5]
+	pshufb	$mask,@x[6]
+	pshufb	$mask,@x[7]
+	lea	0x80($key),$key
+___
+}
+
+sub MixColumns {
+# modified to emit output in order suitable for feeding back to aesenc[last]
+my @x=@_[0..7];
+my @t=@_[8..15];
+my $inv=@_[16];	# optional
+$code.=<<___;
+	pshufd	\$0x93, @x[0], @t[0]	# x0 <<< 32
+	pshufd	\$0x93, @x[1], @t[1]
+	 pxor	@t[0], @x[0]		# x0 ^ (x0 <<< 32)
+	pshufd	\$0x93, @x[2], @t[2]
+	 pxor	@t[1], @x[1]
+	pshufd	\$0x93, @x[3], @t[3]
+	 pxor	@t[2], @x[2]
+	pshufd	\$0x93, @x[4], @t[4]
+	 pxor	@t[3], @x[3]
+	pshufd	\$0x93, @x[5], @t[5]
+	 pxor	@t[4], @x[4]
+	pshufd	\$0x93, @x[6], @t[6]
+	 pxor	@t[5], @x[5]
+	pshufd	\$0x93, @x[7], @t[7]
+	 pxor	@t[6], @x[6]
+	 pxor	@t[7], @x[7]
+
+	pxor	@x[0], @t[1]
+	pxor	@x[7], @t[0]
+	pxor	@x[7], @t[1]
+	 pshufd	\$0x4E, @x[0], @x[0] 	# (x0 ^ (x0 <<< 32)) <<< 64)
+	pxor	@x[1], @t[2]
+	 pshufd	\$0x4E, @x[1], @x[1]
+	pxor	@x[4], @t[5]
+	 pxor	@t[0], @x[0]
+	pxor	@x[5], @t[6]
+	 pxor	@t[1], @x[1]
+	pxor	@x[3], @t[4]
+	 pshufd	\$0x4E, @x[4], @t[0]
+	pxor	@x[6], @t[7]
+	 pshufd	\$0x4E, @x[5], @t[1]
+	pxor	@x[2], @t[3]
+	 pshufd	\$0x4E, @x[3], @x[4]
+	pxor	@x[7], @t[3]
+	 pshufd	\$0x4E, @x[7], @x[5]
+	pxor	@x[7], @t[4]
+	 pshufd	\$0x4E, @x[6], @x[3]
+	pxor	@t[4], @t[0]
+	 pshufd	\$0x4E, @x[2], @x[6]
+	pxor	@t[5], @t[1]
+___
+$code.=<<___ if (!$inv);
+	pxor	@t[3], @x[4]
+	pxor	@t[7], @x[5]
+	pxor	@t[6], @x[3]
+	 movdqa	@t[0], @x[2]
+	pxor	@t[2], @x[6]
+	 movdqa	@t[1], @x[7]
+___
+$code.=<<___ if ($inv);
+	pxor	@x[4], @t[3]
+	pxor	@t[7], @x[5]
+	pxor	@x[3], @t[6]
+	 movdqa	@t[0], @x[3]
+	pxor	@t[2], @x[6]
+	 movdqa	@t[6], @x[2]
+	 movdqa	@t[1], @x[7]
+	 movdqa	@x[6], @x[4]
+	 movdqa	@t[3], @x[6]
+___
+}
+
+sub InvMixColumns_orig {
+my @x=@_[0..7];
+my @t=@_[8..15];
+
+$code.=<<___;
+	# multiplication by 0x0e
+	pshufd	\$0x93, @x[7], @t[7]
+	movdqa	@x[2], @t[2]
+	pxor	@x[5], @x[7]		# 7 5
+	pxor	@x[5], @x[2]		# 2 5
+	pshufd	\$0x93, @x[0], @t[0]
+	movdqa	@x[5], @t[5]
+	pxor	@x[0], @x[5]		# 5 0		[1]
+	pxor	@x[1], @x[0]		# 0 1
+	pshufd	\$0x93, @x[1], @t[1]
+	pxor	@x[2], @x[1]		# 1 25
+	pxor	@x[6], @x[0]		# 01 6		[2]
+	pxor	@x[3], @x[1]		# 125 3		[4]
+	pshufd	\$0x93, @x[3], @t[3]
+	pxor	@x[0], @x[2]		# 25 016	[3]
+	pxor	@x[7], @x[3]		# 3 75
+	pxor	@x[6], @x[7]		# 75 6		[0]
+	pshufd	\$0x93, @x[6], @t[6]
+	movdqa	@x[4], @t[4]
+	pxor	@x[4], @x[6]		# 6 4
+	pxor	@x[3], @x[4]		# 4 375		[6]
+	pxor	@x[7], @x[3]		# 375 756=36
+	pxor	@t[5], @x[6]		# 64 5		[7]
+	pxor	@t[2], @x[3]		# 36 2
+	pxor	@t[4], @x[3]		# 362 4		[5]
+	pshufd	\$0x93, @t[5], @t[5]
+___
+					my @y = @x[7,5,0,2,1,3,4,6];
+$code.=<<___;
+	# multiplication by 0x0b
+	pxor	@y[0], @y[1]
+	pxor	@t[0], @y[0]
+	pxor	@t[1], @y[1]
+	pshufd	\$0x93, @t[2], @t[2]
+	pxor	@t[5], @y[0]
+	pxor	@t[6], @y[1]
+	pxor	@t[7], @y[0]
+	pshufd	\$0x93, @t[4], @t[4]
+	pxor	@t[6], @t[7]		# clobber t[7]
+	pxor	@y[0], @y[1]
+
+	pxor	@t[0], @y[3]
+	pshufd	\$0x93, @t[0], @t[0]
+	pxor	@t[1], @y[2]
+	pxor	@t[1], @y[4]
+	pxor	@t[2], @y[2]
+	pshufd	\$0x93, @t[1], @t[1]
+	pxor	@t[2], @y[3]
+	pxor	@t[2], @y[5]
+	pxor	@t[7], @y[2]
+	pshufd	\$0x93, @t[2], @t[2]
+	pxor	@t[3], @y[3]
+	pxor	@t[3], @y[6]
+	pxor	@t[3], @y[4]
+	pshufd	\$0x93, @t[3], @t[3]
+	pxor	@t[4], @y[7]
+	pxor	@t[4], @y[5]
+	pxor	@t[7], @y[7]
+	pxor	@t[5], @y[3]
+	pxor	@t[4], @y[4]
+	pxor	@t[5], @t[7]		# clobber t[7] even more
+
+	pxor	@t[7], @y[5]
+	pshufd	\$0x93, @t[4], @t[4]
+	pxor	@t[7], @y[6]
+	pxor	@t[7], @y[4]
+
+	pxor	@t[5], @t[7]
+	pshufd	\$0x93, @t[5], @t[5]
+	pxor	@t[6], @t[7]		# restore t[7]
+
+	# multiplication by 0x0d
+	pxor	@y[7], @y[4]
+	pxor	@t[4], @y[7]
+	pshufd	\$0x93, @t[6], @t[6]
+	pxor	@t[0], @y[2]
+	pxor	@t[5], @y[7]
+	pxor	@t[2], @y[2]
+	pshufd	\$0x93, @t[7], @t[7]
+
+	pxor	@y[1], @y[3]
+	pxor	@t[1], @y[1]
+	pxor	@t[0], @y[0]
+	pxor	@t[0], @y[3]
+	pxor	@t[5], @y[1]
+	pxor	@t[5], @y[0]
+	pxor	@t[7], @y[1]
+	pshufd	\$0x93, @t[0], @t[0]
+	pxor	@t[6], @y[0]
+	pxor	@y[1], @y[3]
+	pxor	@t[1], @y[4]
+	pshufd	\$0x93, @t[1], @t[1]
+
+	pxor	@t[7], @y[7]
+	pxor	@t[2], @y[4]
+	pxor	@t[2], @y[5]
+	pshufd	\$0x93, @t[2], @t[2]
+	pxor	@t[6], @y[2]
+	pxor	@t[3], @t[6]		# clobber t[6]
+	pxor	@y[7], @y[4]
+	pxor	@t[6], @y[3]
+
+	pxor	@t[6], @y[6]
+	pxor	@t[5], @y[5]
+	pxor	@t[4], @y[6]
+	pshufd	\$0x93, @t[4], @t[4]
+	pxor	@t[6], @y[5]
+	pxor	@t[7], @y[6]
+	pxor	@t[3], @t[6]		# restore t[6]
+
+	pshufd	\$0x93, @t[5], @t[5]
+	pshufd	\$0x93, @t[6], @t[6]
+	pshufd	\$0x93, @t[7], @t[7]
+	pshufd	\$0x93, @t[3], @t[3]
+
+	# multiplication by 0x09
+	pxor	@y[1], @y[4]
+	pxor	@y[1], @t[1]		# t[1]=y[1]
+	pxor	@t[5], @t[0]		# clobber t[0]
+	pxor	@t[5], @t[1]
+	pxor	@t[0], @y[3]
+	pxor	@y[0], @t[0]		# t[0]=y[0]
+	pxor	@t[6], @t[1]
+	pxor	@t[7], @t[6]		# clobber t[6]
+	pxor	@t[1], @y[4]
+	pxor	@t[4], @y[7]
+	pxor	@y[4], @t[4]		# t[4]=y[4]
+	pxor	@t[3], @y[6]
+	pxor	@y[3], @t[3]		# t[3]=y[3]
+	pxor	@t[2], @y[5]
+	pxor	@y[2], @t[2]		# t[2]=y[2]
+	pxor	@t[7], @t[3]
+	pxor	@y[5], @t[5]		# t[5]=y[5]
+	pxor	@t[6], @t[2]
+	pxor	@t[6], @t[5]
+	pxor	@y[6], @t[6]		# t[6]=y[6]
+	pxor	@y[7], @t[7]		# t[7]=y[7]
+
+	movdqa	@t[0],@XMM[0]
+	movdqa	@t[1],@XMM[1]
+	movdqa	@t[2],@XMM[2]
+	movdqa	@t[3],@XMM[3]
+	movdqa	@t[4],@XMM[4]
+	movdqa	@t[5],@XMM[5]
+	movdqa	@t[6],@XMM[6]
+	movdqa	@t[7],@XMM[7]
+___
+}
+
+sub InvMixColumns {
+my @x=@_[0..7];
+my @t=@_[8..15];
+
+# Thanks to Jussi Kivilinna for providing pointer to
+#
+# | 0e 0b 0d 09 |   | 02 03 01 01 |   | 05 00 04 00 |
+# | 09 0e 0b 0d | = | 01 02 03 01 | x | 00 05 00 04 |
+# | 0d 09 0e 0b |   | 01 01 02 03 |   | 04 00 05 00 |
+# | 0b 0d 09 0e |   | 03 01 01 02 |   | 00 04 00 05 |
+
+$code.=<<___;
+	# multiplication by 0x05-0x00-0x04-0x00
+	pshufd	\$0x4E, @x[0], @t[0]
+	pshufd	\$0x4E, @x[6], @t[6]
+	pxor	@x[0], @t[0]
+	pshufd	\$0x4E, @x[7], @t[7]
+	pxor	@x[6], @t[6]
+	pshufd	\$0x4E, @x[1], @t[1]
+	pxor	@x[7], @t[7]
+	pshufd	\$0x4E, @x[2], @t[2]
+	pxor	@x[1], @t[1]
+	pshufd	\$0x4E, @x[3], @t[3]
+	pxor	@x[2], @t[2]
+	 pxor	@t[6], @x[0]
+	 pxor	@t[6], @x[1]
+	pshufd	\$0x4E, @x[4], @t[4]
+	pxor	@x[3], @t[3]
+	 pxor	@t[0], @x[2]
+	 pxor	@t[1], @x[3]
+	pshufd	\$0x4E, @x[5], @t[5]
+	pxor	@x[4], @t[4]
+	 pxor	@t[7], @x[1]
+	 pxor	@t[2], @x[4]
+	pxor	@x[5], @t[5]
+
+	 pxor	@t[7], @x[2]
+	 pxor	@t[6], @x[3]
+	 pxor	@t[6], @x[4]
+	 pxor	@t[3], @x[5]
+	 pxor	@t[4], @x[6]
+	 pxor	@t[7], @x[4]
+	 pxor	@t[7], @x[5]
+	 pxor	@t[5], @x[7]
+___
+	&MixColumns	(@x,@t,1);	# flipped 2<->3 and 4<->6
+}
+
+sub aesenc {				# not used
+my @b=@_[0..7];
+my @t=@_[8..15];
+$code.=<<___;
+	movdqa	0x30($const),@t[0]	# .LSR
+___
+	&ShiftRows	(@b,@t[0]);
+	&Sbox		(@b,@t);
+	&MixColumns	(@b[0,1,4,6,3,7,2,5],@t);
+}
+
+sub aesenclast {			# not used
+my @b=@_[0..7];
+my @t=@_[8..15];
+$code.=<<___;
+	movdqa	0x40($const),@t[0]	# .LSRM0
+___
+	&ShiftRows	(@b,@t[0]);
+	&Sbox		(@b,@t);
+$code.=<<___
+	pxor	0x00($key),@b[0]
+	pxor	0x10($key),@b[1]
+	pxor	0x20($key),@b[4]
+	pxor	0x30($key),@b[6]
+	pxor	0x40($key),@b[3]
+	pxor	0x50($key),@b[7]
+	pxor	0x60($key),@b[2]
+	pxor	0x70($key),@b[5]
+___
+}
+
+sub swapmove {
+my ($a,$b,$n,$mask,$t)=@_;
+$code.=<<___;
+	movdqa	$b,$t
+	psrlq	\$$n,$b
+	pxor  	$a,$b
+	pand	$mask,$b
+	pxor	$b,$a
+	psllq	\$$n,$b
+	pxor	$t,$b
+___
+}
+sub swapmove2x {
+my ($a0,$b0,$a1,$b1,$n,$mask,$t0,$t1)=@_;
+$code.=<<___;
+	movdqa	$b0,$t0
+	psrlq	\$$n,$b0
+	 movdqa	$b1,$t1
+	 psrlq	\$$n,$b1
+	pxor  	$a0,$b0
+	 pxor  	$a1,$b1
+	pand	$mask,$b0
+	 pand	$mask,$b1
+	pxor	$b0,$a0
+	psllq	\$$n,$b0
+	 pxor	$b1,$a1
+	 psllq	\$$n,$b1
+	pxor	$t0,$b0
+	 pxor	$t1,$b1
+___
+}
+
+sub bitslice {
+my @x=reverse(@_[0..7]);
+my ($t0,$t1,$t2,$t3)=@_[8..11];
+$code.=<<___;
+	movdqa	0x00($const),$t0	# .LBS0
+	movdqa	0x10($const),$t1	# .LBS1
+___
+	&swapmove2x(@x[0,1,2,3],1,$t0,$t2,$t3);
+	&swapmove2x(@x[4,5,6,7],1,$t0,$t2,$t3);
+$code.=<<___;
+	movdqa	0x20($const),$t0	# .LBS2
+___
+	&swapmove2x(@x[0,2,1,3],2,$t1,$t2,$t3);
+	&swapmove2x(@x[4,6,5,7],2,$t1,$t2,$t3);
+
+	&swapmove2x(@x[0,4,1,5],4,$t0,$t2,$t3);
+	&swapmove2x(@x[2,6,3,7],4,$t0,$t2,$t3);
+}
+
+$code.=<<___;
+.text
+
+.extern	asm_AES_encrypt
+.extern	asm_AES_decrypt
+
+.type	_bsaes_encrypt8,\@abi-omnipotent
+.align	64
+_bsaes_encrypt8:
+	lea	.LBS0(%rip), $const	# constants table
+
+	movdqa	($key), @XMM[9]		# round 0 key
+	lea	0x10($key), $key
+	movdqa	0x50($const), @XMM[8]	# .LM0SR
+	pxor	@XMM[9], @XMM[0]	# xor with round0 key
+	pxor	@XMM[9], @XMM[1]
+	pxor	@XMM[9], @XMM[2]
+	pxor	@XMM[9], @XMM[3]
+	 pshufb	@XMM[8], @XMM[0]
+	 pshufb	@XMM[8], @XMM[1]
+	pxor	@XMM[9], @XMM[4]
+	pxor	@XMM[9], @XMM[5]
+	 pshufb	@XMM[8], @XMM[2]
+	 pshufb	@XMM[8], @XMM[3]
+	pxor	@XMM[9], @XMM[6]
+	pxor	@XMM[9], @XMM[7]
+	 pshufb	@XMM[8], @XMM[4]
+	 pshufb	@XMM[8], @XMM[5]
+	 pshufb	@XMM[8], @XMM[6]
+	 pshufb	@XMM[8], @XMM[7]
+_bsaes_encrypt8_bitslice:
+___
+	&bitslice	(@XMM[0..7, 8..11]);
+$code.=<<___;
+	dec	$rounds
+	jmp	.Lenc_sbox
+.align	16
+.Lenc_loop:
+___
+	&ShiftRows	(@XMM[0..7, 8]);
+$code.=".Lenc_sbox:\n";
+	&Sbox		(@XMM[0..7, 8..15]);
+$code.=<<___;
+	dec	$rounds
+	jl	.Lenc_done
+___
+	&MixColumns	(@XMM[0,1,4,6,3,7,2,5, 8..15]);
+$code.=<<___;
+	movdqa	0x30($const), @XMM[8]	# .LSR
+	jnz	.Lenc_loop
+	movdqa	0x40($const), @XMM[8]	# .LSRM0
+	jmp	.Lenc_loop
+.align	16
+.Lenc_done:
+___
+	# output in lsb > [t0, t1, t4, t6, t3, t7, t2, t5] < msb
+	&bitslice	(@XMM[0,1,4,6,3,7,2,5, 8..11]);
+$code.=<<___;
+	movdqa	($key), @XMM[8]		# last round key
+	pxor	@XMM[8], @XMM[4]
+	pxor	@XMM[8], @XMM[6]
+	pxor	@XMM[8], @XMM[3]
+	pxor	@XMM[8], @XMM[7]
+	pxor	@XMM[8], @XMM[2]
+	pxor	@XMM[8], @XMM[5]
+	pxor	@XMM[8], @XMM[0]
+	pxor	@XMM[8], @XMM[1]
+	ret
+.size	_bsaes_encrypt8,.-_bsaes_encrypt8
+
+.type	_bsaes_decrypt8,\@abi-omnipotent
+.align	64
+_bsaes_decrypt8:
+	lea	.LBS0(%rip), $const	# constants table
+
+	movdqa	($key), @XMM[9]		# round 0 key
+	lea	0x10($key), $key
+	movdqa	-0x30($const), @XMM[8]	# .LM0ISR
+	pxor	@XMM[9], @XMM[0]	# xor with round0 key
+	pxor	@XMM[9], @XMM[1]
+	pxor	@XMM[9], @XMM[2]
+	pxor	@XMM[9], @XMM[3]
+	 pshufb	@XMM[8], @XMM[0]
+	 pshufb	@XMM[8], @XMM[1]
+	pxor	@XMM[9], @XMM[4]
+	pxor	@XMM[9], @XMM[5]
+	 pshufb	@XMM[8], @XMM[2]
+	 pshufb	@XMM[8], @XMM[3]
+	pxor	@XMM[9], @XMM[6]
+	pxor	@XMM[9], @XMM[7]
+	 pshufb	@XMM[8], @XMM[4]
+	 pshufb	@XMM[8], @XMM[5]
+	 pshufb	@XMM[8], @XMM[6]
+	 pshufb	@XMM[8], @XMM[7]
+___
+	&bitslice	(@XMM[0..7, 8..11]);
+$code.=<<___;
+	dec	$rounds
+	jmp	.Ldec_sbox
+.align	16
+.Ldec_loop:
+___
+	&ShiftRows	(@XMM[0..7, 8]);
+$code.=".Ldec_sbox:\n";
+	&InvSbox	(@XMM[0..7, 8..15]);
+$code.=<<___;
+	dec	$rounds
+	jl	.Ldec_done
+___
+	&InvMixColumns	(@XMM[0,1,6,4,2,7,3,5, 8..15]);
+$code.=<<___;
+	movdqa	-0x10($const), @XMM[8]	# .LISR
+	jnz	.Ldec_loop
+	movdqa	-0x20($const), @XMM[8]	# .LISRM0
+	jmp	.Ldec_loop
+.align	16
+.Ldec_done:
+___
+	&bitslice	(@XMM[0,1,6,4,2,7,3,5, 8..11]);
+$code.=<<___;
+	movdqa	($key), @XMM[8]		# last round key
+	pxor	@XMM[8], @XMM[6]
+	pxor	@XMM[8], @XMM[4]
+	pxor	@XMM[8], @XMM[2]
+	pxor	@XMM[8], @XMM[7]
+	pxor	@XMM[8], @XMM[3]
+	pxor	@XMM[8], @XMM[5]
+	pxor	@XMM[8], @XMM[0]
+	pxor	@XMM[8], @XMM[1]
+	ret
+.size	_bsaes_decrypt8,.-_bsaes_decrypt8
+___
+}
+{
+my ($out,$inp,$rounds,$const)=("%rax","%rcx","%r10d","%r11");
+
+sub bitslice_key {
+my @x=reverse(@_[0..7]);
+my ($bs0,$bs1,$bs2,$t2,$t3)=@_[8..12];
+
+	&swapmove	(@x[0,1],1,$bs0,$t2,$t3);
+$code.=<<___;
+	#&swapmove(@x[2,3],1,$t0,$t2,$t3);
+	movdqa	@x[0], @x[2]
+	movdqa	@x[1], @x[3]
+___
+	#&swapmove2x(@x[4,5,6,7],1,$t0,$t2,$t3);
+
+	&swapmove2x	(@x[0,2,1,3],2,$bs1,$t2,$t3);
+$code.=<<___;
+	#&swapmove2x(@x[4,6,5,7],2,$t1,$t2,$t3);
+	movdqa	@x[0], @x[4]
+	movdqa	@x[2], @x[6]
+	movdqa	@x[1], @x[5]
+	movdqa	@x[3], @x[7]
+___
+	&swapmove2x	(@x[0,4,1,5],4,$bs2,$t2,$t3);
+	&swapmove2x	(@x[2,6,3,7],4,$bs2,$t2,$t3);
+}
+
+$code.=<<___;
+.type	_bsaes_key_convert,\@abi-omnipotent
+.align	16
+_bsaes_key_convert:
+	lea	.Lmasks(%rip), $const
+	movdqu	($inp), %xmm7		# load round 0 key
+	lea	0x10($inp), $inp
+	movdqa	0x00($const), %xmm0	# 0x01...
+	movdqa	0x10($const), %xmm1	# 0x02...
+	movdqa	0x20($const), %xmm2	# 0x04...
+	movdqa	0x30($const), %xmm3	# 0x08...
+	movdqa	0x40($const), %xmm4	# .LM0
+	pcmpeqd	%xmm5, %xmm5		# .LNOT
+
+	movdqu	($inp), %xmm6		# load round 1 key
+	movdqa	%xmm7, ($out)		# save round 0 key
+	lea	0x10($out), $out
+	dec	$rounds
+	jmp	.Lkey_loop
+.align	16
+.Lkey_loop:
+	pshufb	%xmm4, %xmm6		# .LM0
+
+	movdqa	%xmm0,	%xmm8
+	movdqa	%xmm1,	%xmm9
+
+	pand	%xmm6,	%xmm8
+	pand	%xmm6,	%xmm9
+	movdqa	%xmm2,	%xmm10
+	pcmpeqb	%xmm0,	%xmm8
+	psllq	\$4,	%xmm0		# 0x10...
+	movdqa	%xmm3,	%xmm11
+	pcmpeqb	%xmm1,	%xmm9
+	psllq	\$4,	%xmm1		# 0x20...
+
+	pand	%xmm6,	%xmm10
+	pand	%xmm6,	%xmm11
+	movdqa	%xmm0,	%xmm12
+	pcmpeqb	%xmm2,	%xmm10
+	psllq	\$4,	%xmm2		# 0x40...
+	movdqa	%xmm1,	%xmm13
+	pcmpeqb	%xmm3,	%xmm11
+	psllq	\$4,	%xmm3		# 0x80...
+
+	movdqa	%xmm2,	%xmm14
+	movdqa	%xmm3,	%xmm15
+	 pxor	%xmm5,	%xmm8		# "pnot"
+	 pxor	%xmm5,	%xmm9
+
+	pand	%xmm6,	%xmm12
+	pand	%xmm6,	%xmm13
+	 movdqa	%xmm8, 0x00($out)	# write bit-sliced round key
+	pcmpeqb	%xmm0,	%xmm12
+	psrlq	\$4,	%xmm0		# 0x01...
+	 movdqa	%xmm9, 0x10($out)
+	pcmpeqb	%xmm1,	%xmm13
+	psrlq	\$4,	%xmm1		# 0x02...
+	 lea	0x10($inp), $inp
+
+	pand	%xmm6,	%xmm14
+	pand	%xmm6,	%xmm15
+	 movdqa	%xmm10, 0x20($out)
+	pcmpeqb	%xmm2,	%xmm14
+	psrlq	\$4,	%xmm2		# 0x04...
+	 movdqa	%xmm11, 0x30($out)
+	pcmpeqb	%xmm3,	%xmm15
+	psrlq	\$4,	%xmm3		# 0x08...
+	 movdqu	($inp), %xmm6		# load next round key
+
+	pxor	%xmm5, %xmm13		# "pnot"
+	pxor	%xmm5, %xmm14
+	movdqa	%xmm12, 0x40($out)
+	movdqa	%xmm13, 0x50($out)
+	movdqa	%xmm14, 0x60($out)
+	movdqa	%xmm15, 0x70($out)
+	lea	0x80($out),$out
+	dec	$rounds
+	jnz	.Lkey_loop
+
+	movdqa	0x50($const), %xmm7	# .L63
+	#movdqa	%xmm6, ($out)		# don't save last round key
+	ret
+.size	_bsaes_key_convert,.-_bsaes_key_convert
+___
+}
+
+if (0 && !$win64) {	# following four functions are unsupported interface
+			# used for benchmarking...
+$code.=<<___;
+.globl	bsaes_enc_key_convert
+.type	bsaes_enc_key_convert,\@function,2
+.align	16
+bsaes_enc_key_convert:
+	mov	240($inp),%r10d		# pass rounds
+	mov	$inp,%rcx		# pass key
+	mov	$out,%rax		# pass key schedule
+	call	_bsaes_key_convert
+	pxor	%xmm6,%xmm7		# fix up last round key
+	movdqa	%xmm7,(%rax)		# save last round key
+	ret
+.size	bsaes_enc_key_convert,.-bsaes_enc_key_convert
+
+.globl	bsaes_encrypt_128
+.type	bsaes_encrypt_128,\@function,4
+.align	16
+bsaes_encrypt_128:
+.Lenc128_loop:
+	movdqu	0x00($inp), @XMM[0]	# load input
+	movdqu	0x10($inp), @XMM[1]
+	movdqu	0x20($inp), @XMM[2]
+	movdqu	0x30($inp), @XMM[3]
+	movdqu	0x40($inp), @XMM[4]
+	movdqu	0x50($inp), @XMM[5]
+	movdqu	0x60($inp), @XMM[6]
+	movdqu	0x70($inp), @XMM[7]
+	mov	$key, %rax		# pass the $key
+	lea	0x80($inp), $inp
+	mov	\$10,%r10d
+
+	call	_bsaes_encrypt8
+
+	movdqu	@XMM[0], 0x00($out)	# write output
+	movdqu	@XMM[1], 0x10($out)
+	movdqu	@XMM[4], 0x20($out)
+	movdqu	@XMM[6], 0x30($out)
+	movdqu	@XMM[3], 0x40($out)
+	movdqu	@XMM[7], 0x50($out)
+	movdqu	@XMM[2], 0x60($out)
+	movdqu	@XMM[5], 0x70($out)
+	lea	0x80($out), $out
+	sub	\$0x80,$len
+	ja	.Lenc128_loop
+	ret
+.size	bsaes_encrypt_128,.-bsaes_encrypt_128
+
+.globl	bsaes_dec_key_convert
+.type	bsaes_dec_key_convert,\@function,2
+.align	16
+bsaes_dec_key_convert:
+	mov	240($inp),%r10d		# pass rounds
+	mov	$inp,%rcx		# pass key
+	mov	$out,%rax		# pass key schedule
+	call	_bsaes_key_convert
+	pxor	($out),%xmm7		# fix up round 0 key
+	movdqa	%xmm6,(%rax)		# save last round key
+	movdqa	%xmm7,($out)
+	ret
+.size	bsaes_dec_key_convert,.-bsaes_dec_key_convert
+
+.globl	bsaes_decrypt_128
+.type	bsaes_decrypt_128,\@function,4
+.align	16
+bsaes_decrypt_128:
+.Ldec128_loop:
+	movdqu	0x00($inp), @XMM[0]	# load input
+	movdqu	0x10($inp), @XMM[1]
+	movdqu	0x20($inp), @XMM[2]
+	movdqu	0x30($inp), @XMM[3]
+	movdqu	0x40($inp), @XMM[4]
+	movdqu	0x50($inp), @XMM[5]
+	movdqu	0x60($inp), @XMM[6]
+	movdqu	0x70($inp), @XMM[7]
+	mov	$key, %rax		# pass the $key
+	lea	0x80($inp), $inp
+	mov	\$10,%r10d
+
+	call	_bsaes_decrypt8
+
+	movdqu	@XMM[0], 0x00($out)	# write output
+	movdqu	@XMM[1], 0x10($out)
+	movdqu	@XMM[6], 0x20($out)
+	movdqu	@XMM[4], 0x30($out)
+	movdqu	@XMM[2], 0x40($out)
+	movdqu	@XMM[7], 0x50($out)
+	movdqu	@XMM[3], 0x60($out)
+	movdqu	@XMM[5], 0x70($out)
+	lea	0x80($out), $out
+	sub	\$0x80,$len
+	ja	.Ldec128_loop
+	ret
+.size	bsaes_decrypt_128,.-bsaes_decrypt_128
+___
+}
+{
+######################################################################
+#
+# OpenSSL interface
+#
+my ($arg1,$arg2,$arg3,$arg4,$arg5,$arg6)=$win64	? ("%rcx","%rdx","%r8","%r9","%r10","%r11d")
+						: ("%rdi","%rsi","%rdx","%rcx","%r8","%r9d");
+my ($inp,$out,$len,$key)=("%r12","%r13","%r14","%r15");
+
+if ($ecb) {
+$code.=<<___;
+.globl	bsaes_ecb_encrypt_blocks
+.type	bsaes_ecb_encrypt_blocks,\@abi-omnipotent
+.align	16
+bsaes_ecb_encrypt_blocks:
+	mov	%rsp, %rax
+.Lecb_enc_prologue:
+	push	%rbp
+	push	%rbx
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+	lea	-0x48(%rsp),%rsp
+___
+$code.=<<___ if ($win64);
+	lea	-0xa0(%rsp), %rsp
+	movaps	%xmm6, 0x40(%rsp)
+	movaps	%xmm7, 0x50(%rsp)
+	movaps	%xmm8, 0x60(%rsp)
+	movaps	%xmm9, 0x70(%rsp)
+	movaps	%xmm10, 0x80(%rsp)
+	movaps	%xmm11, 0x90(%rsp)
+	movaps	%xmm12, 0xa0(%rsp)
+	movaps	%xmm13, 0xb0(%rsp)
+	movaps	%xmm14, 0xc0(%rsp)
+	movaps	%xmm15, 0xd0(%rsp)
+.Lecb_enc_body:
+___
+$code.=<<___;
+	mov	%rsp,%rbp		# backup %rsp
+	mov	240($arg4),%eax		# rounds
+	mov	$arg1,$inp		# backup arguments
+	mov	$arg2,$out
+	mov	$arg3,$len
+	mov	$arg4,$key
+	cmp	\$8,$arg3
+	jb	.Lecb_enc_short
+
+	mov	%eax,%ebx		# backup rounds
+	shl	\$7,%rax		# 128 bytes per inner round key
+	sub	\$`128-32`,%rax		# size of bit-sliced key schedule
+	sub	%rax,%rsp
+	mov	%rsp,%rax		# pass key schedule
+	mov	$key,%rcx		# pass key
+	mov	%ebx,%r10d		# pass rounds
+	call	_bsaes_key_convert
+	pxor	%xmm6,%xmm7		# fix up last round key
+	movdqa	%xmm7,(%rax)		# save last round key
+
+	sub	\$8,$len
+.Lecb_enc_loop:
+	movdqu	0x00($inp), @XMM[0]	# load input
+	movdqu	0x10($inp), @XMM[1]
+	movdqu	0x20($inp), @XMM[2]
+	movdqu	0x30($inp), @XMM[3]
+	movdqu	0x40($inp), @XMM[4]
+	movdqu	0x50($inp), @XMM[5]
+	mov	%rsp, %rax		# pass key schedule
+	movdqu	0x60($inp), @XMM[6]
+	mov	%ebx,%r10d		# pass rounds
+	movdqu	0x70($inp), @XMM[7]
+	lea	0x80($inp), $inp
+
+	call	_bsaes_encrypt8
+
+	movdqu	@XMM[0], 0x00($out)	# write output
+	movdqu	@XMM[1], 0x10($out)
+	movdqu	@XMM[4], 0x20($out)
+	movdqu	@XMM[6], 0x30($out)
+	movdqu	@XMM[3], 0x40($out)
+	movdqu	@XMM[7], 0x50($out)
+	movdqu	@XMM[2], 0x60($out)
+	movdqu	@XMM[5], 0x70($out)
+	lea	0x80($out), $out
+	sub	\$8,$len
+	jnc	.Lecb_enc_loop
+
+	add	\$8,$len
+	jz	.Lecb_enc_done
+
+	movdqu	0x00($inp), @XMM[0]	# load input
+	mov	%rsp, %rax		# pass key schedule
+	mov	%ebx,%r10d		# pass rounds
+	cmp	\$2,$len
+	jb	.Lecb_enc_one
+	movdqu	0x10($inp), @XMM[1]
+	je	.Lecb_enc_two
+	movdqu	0x20($inp), @XMM[2]
+	cmp	\$4,$len
+	jb	.Lecb_enc_three
+	movdqu	0x30($inp), @XMM[3]
+	je	.Lecb_enc_four
+	movdqu	0x40($inp), @XMM[4]
+	cmp	\$6,$len
+	jb	.Lecb_enc_five
+	movdqu	0x50($inp), @XMM[5]
+	je	.Lecb_enc_six
+	movdqu	0x60($inp), @XMM[6]
+	call	_bsaes_encrypt8
+	movdqu	@XMM[0], 0x00($out)	# write output
+	movdqu	@XMM[1], 0x10($out)
+	movdqu	@XMM[4], 0x20($out)
+	movdqu	@XMM[6], 0x30($out)
+	movdqu	@XMM[3], 0x40($out)
+	movdqu	@XMM[7], 0x50($out)
+	movdqu	@XMM[2], 0x60($out)
+	jmp	.Lecb_enc_done
+.align	16
+.Lecb_enc_six:
+	call	_bsaes_encrypt8
+	movdqu	@XMM[0], 0x00($out)	# write output
+	movdqu	@XMM[1], 0x10($out)
+	movdqu	@XMM[4], 0x20($out)
+	movdqu	@XMM[6], 0x30($out)
+	movdqu	@XMM[3], 0x40($out)
+	movdqu	@XMM[7], 0x50($out)
+	jmp	.Lecb_enc_done
+.align	16
+.Lecb_enc_five:
+	call	_bsaes_encrypt8
+	movdqu	@XMM[0], 0x00($out)	# write output
+	movdqu	@XMM[1], 0x10($out)
+	movdqu	@XMM[4], 0x20($out)
+	movdqu	@XMM[6], 0x30($out)
+	movdqu	@XMM[3], 0x40($out)
+	jmp	.Lecb_enc_done
+.align	16
+.Lecb_enc_four:
+	call	_bsaes_encrypt8
+	movdqu	@XMM[0], 0x00($out)	# write output
+	movdqu	@XMM[1], 0x10($out)
+	movdqu	@XMM[4], 0x20($out)
+	movdqu	@XMM[6], 0x30($out)
+	jmp	.Lecb_enc_done
+.align	16
+.Lecb_enc_three:
+	call	_bsaes_encrypt8
+	movdqu	@XMM[0], 0x00($out)	# write output
+	movdqu	@XMM[1], 0x10($out)
+	movdqu	@XMM[4], 0x20($out)
+	jmp	.Lecb_enc_done
+.align	16
+.Lecb_enc_two:
+	call	_bsaes_encrypt8
+	movdqu	@XMM[0], 0x00($out)	# write output
+	movdqu	@XMM[1], 0x10($out)
+	jmp	.Lecb_enc_done
+.align	16
+.Lecb_enc_one:
+	call	_bsaes_encrypt8
+	movdqu	@XMM[0], 0x00($out)	# write output
+	jmp	.Lecb_enc_done
+.align	16
+.Lecb_enc_short:
+	lea	($inp), $arg1
+	lea	($out), $arg2
+	lea	($key), $arg3
+	call	asm_AES_encrypt
+	lea	16($inp), $inp
+	lea	16($out), $out
+	dec	$len
+	jnz	.Lecb_enc_short
+
+.Lecb_enc_done:
+	lea	(%rsp),%rax
+	pxor	%xmm0, %xmm0
+.Lecb_enc_bzero:			# wipe key schedule [if any]
+	movdqa	%xmm0, 0x00(%rax)
+	movdqa	%xmm0, 0x10(%rax)
+	lea	0x20(%rax), %rax
+	cmp	%rax, %rbp
+	jb	.Lecb_enc_bzero
+
+	lea	(%rbp),%rsp		# restore %rsp
+___
+$code.=<<___ if ($win64);
+	movaps	0x40(%rbp), %xmm6
+	movaps	0x50(%rbp), %xmm7
+	movaps	0x60(%rbp), %xmm8
+	movaps	0x70(%rbp), %xmm9
+	movaps	0x80(%rbp), %xmm10
+	movaps	0x90(%rbp), %xmm11
+	movaps	0xa0(%rbp), %xmm12
+	movaps	0xb0(%rbp), %xmm13
+	movaps	0xc0(%rbp), %xmm14
+	movaps	0xd0(%rbp), %xmm15
+	lea	0xa0(%rbp), %rsp
+___
+$code.=<<___;
+	mov	0x48(%rsp), %r15
+	mov	0x50(%rsp), %r14
+	mov	0x58(%rsp), %r13
+	mov	0x60(%rsp), %r12
+	mov	0x68(%rsp), %rbx
+	mov	0x70(%rsp), %rax
+	lea	0x78(%rsp), %rsp
+	mov	%rax, %rbp
+.Lecb_enc_epilogue:
+	ret
+.size	bsaes_ecb_encrypt_blocks,.-bsaes_ecb_encrypt_blocks
+
+.globl	bsaes_ecb_decrypt_blocks
+.type	bsaes_ecb_decrypt_blocks,\@abi-omnipotent
+.align	16
+bsaes_ecb_decrypt_blocks:
+	mov	%rsp, %rax
+.Lecb_dec_prologue:
+	push	%rbp
+	push	%rbx
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+	lea	-0x48(%rsp),%rsp
+___
+$code.=<<___ if ($win64);
+	lea	-0xa0(%rsp), %rsp
+	movaps	%xmm6, 0x40(%rsp)
+	movaps	%xmm7, 0x50(%rsp)
+	movaps	%xmm8, 0x60(%rsp)
+	movaps	%xmm9, 0x70(%rsp)
+	movaps	%xmm10, 0x80(%rsp)
+	movaps	%xmm11, 0x90(%rsp)
+	movaps	%xmm12, 0xa0(%rsp)
+	movaps	%xmm13, 0xb0(%rsp)
+	movaps	%xmm14, 0xc0(%rsp)
+	movaps	%xmm15, 0xd0(%rsp)
+.Lecb_dec_body:
+___
+$code.=<<___;
+	mov	%rsp,%rbp		# backup %rsp
+	mov	240($arg4),%eax		# rounds
+	mov	$arg1,$inp		# backup arguments
+	mov	$arg2,$out
+	mov	$arg3,$len
+	mov	$arg4,$key
+	cmp	\$8,$arg3
+	jb	.Lecb_dec_short
+
+	mov	%eax,%ebx		# backup rounds
+	shl	\$7,%rax		# 128 bytes per inner round key
+	sub	\$`128-32`,%rax		# size of bit-sliced key schedule
+	sub	%rax,%rsp
+	mov	%rsp,%rax		# pass key schedule
+	mov	$key,%rcx		# pass key
+	mov	%ebx,%r10d		# pass rounds
+	call	_bsaes_key_convert
+	pxor	(%rsp),%xmm7		# fix up 0 round key
+	movdqa	%xmm6,(%rax)		# save last round key
+	movdqa	%xmm7,(%rsp)
+
+	sub	\$8,$len
+.Lecb_dec_loop:
+	movdqu	0x00($inp), @XMM[0]	# load input
+	movdqu	0x10($inp), @XMM[1]
+	movdqu	0x20($inp), @XMM[2]
+	movdqu	0x30($inp), @XMM[3]
+	movdqu	0x40($inp), @XMM[4]
+	movdqu	0x50($inp), @XMM[5]
+	mov	%rsp, %rax		# pass key schedule
+	movdqu	0x60($inp), @XMM[6]
+	mov	%ebx,%r10d		# pass rounds
+	movdqu	0x70($inp), @XMM[7]
+	lea	0x80($inp), $inp
+
+	call	_bsaes_decrypt8
+
+	movdqu	@XMM[0], 0x00($out)	# write output
+	movdqu	@XMM[1], 0x10($out)
+	movdqu	@XMM[6], 0x20($out)
+	movdqu	@XMM[4], 0x30($out)
+	movdqu	@XMM[2], 0x40($out)
+	movdqu	@XMM[7], 0x50($out)
+	movdqu	@XMM[3], 0x60($out)
+	movdqu	@XMM[5], 0x70($out)
+	lea	0x80($out), $out
+	sub	\$8,$len
+	jnc	.Lecb_dec_loop
+
+	add	\$8,$len
+	jz	.Lecb_dec_done
+
+	movdqu	0x00($inp), @XMM[0]	# load input
+	mov	%rsp, %rax		# pass key schedule
+	mov	%ebx,%r10d		# pass rounds
+	cmp	\$2,$len
+	jb	.Lecb_dec_one
+	movdqu	0x10($inp), @XMM[1]
+	je	.Lecb_dec_two
+	movdqu	0x20($inp), @XMM[2]
+	cmp	\$4,$len
+	jb	.Lecb_dec_three
+	movdqu	0x30($inp), @XMM[3]
+	je	.Lecb_dec_four
+	movdqu	0x40($inp), @XMM[4]
+	cmp	\$6,$len
+	jb	.Lecb_dec_five
+	movdqu	0x50($inp), @XMM[5]
+	je	.Lecb_dec_six
+	movdqu	0x60($inp), @XMM[6]
+	call	_bsaes_decrypt8
+	movdqu	@XMM[0], 0x00($out)	# write output
+	movdqu	@XMM[1], 0x10($out)
+	movdqu	@XMM[6], 0x20($out)
+	movdqu	@XMM[4], 0x30($out)
+	movdqu	@XMM[2], 0x40($out)
+	movdqu	@XMM[7], 0x50($out)
+	movdqu	@XMM[3], 0x60($out)
+	jmp	.Lecb_dec_done
+.align	16
+.Lecb_dec_six:
+	call	_bsaes_decrypt8
+	movdqu	@XMM[0], 0x00($out)	# write output
+	movdqu	@XMM[1], 0x10($out)
+	movdqu	@XMM[6], 0x20($out)
+	movdqu	@XMM[4], 0x30($out)
+	movdqu	@XMM[2], 0x40($out)
+	movdqu	@XMM[7], 0x50($out)
+	jmp	.Lecb_dec_done
+.align	16
+.Lecb_dec_five:
+	call	_bsaes_decrypt8
+	movdqu	@XMM[0], 0x00($out)	# write output
+	movdqu	@XMM[1], 0x10($out)
+	movdqu	@XMM[6], 0x20($out)
+	movdqu	@XMM[4], 0x30($out)
+	movdqu	@XMM[2], 0x40($out)
+	jmp	.Lecb_dec_done
+.align	16
+.Lecb_dec_four:
+	call	_bsaes_decrypt8
+	movdqu	@XMM[0], 0x00($out)	# write output
+	movdqu	@XMM[1], 0x10($out)
+	movdqu	@XMM[6], 0x20($out)
+	movdqu	@XMM[4], 0x30($out)
+	jmp	.Lecb_dec_done
+.align	16
+.Lecb_dec_three:
+	call	_bsaes_decrypt8
+	movdqu	@XMM[0], 0x00($out)	# write output
+	movdqu	@XMM[1], 0x10($out)
+	movdqu	@XMM[6], 0x20($out)
+	jmp	.Lecb_dec_done
+.align	16
+.Lecb_dec_two:
+	call	_bsaes_decrypt8
+	movdqu	@XMM[0], 0x00($out)	# write output
+	movdqu	@XMM[1], 0x10($out)
+	jmp	.Lecb_dec_done
+.align	16
+.Lecb_dec_one:
+	call	_bsaes_decrypt8
+	movdqu	@XMM[0], 0x00($out)	# write output
+	jmp	.Lecb_dec_done
+.align	16
+.Lecb_dec_short:
+	lea	($inp), $arg1
+	lea	($out), $arg2
+	lea	($key), $arg3
+	call	asm_AES_decrypt
+	lea	16($inp), $inp
+	lea	16($out), $out
+	dec	$len
+	jnz	.Lecb_dec_short
+
+.Lecb_dec_done:
+	lea	(%rsp),%rax
+	pxor	%xmm0, %xmm0
+.Lecb_dec_bzero:			# wipe key schedule [if any]
+	movdqa	%xmm0, 0x00(%rax)
+	movdqa	%xmm0, 0x10(%rax)
+	lea	0x20(%rax), %rax
+	cmp	%rax, %rbp
+	jb	.Lecb_dec_bzero
+
+	lea	(%rbp),%rsp		# restore %rsp
+___
+$code.=<<___ if ($win64);
+	movaps	0x40(%rbp), %xmm6
+	movaps	0x50(%rbp), %xmm7
+	movaps	0x60(%rbp), %xmm8
+	movaps	0x70(%rbp), %xmm9
+	movaps	0x80(%rbp), %xmm10
+	movaps	0x90(%rbp), %xmm11
+	movaps	0xa0(%rbp), %xmm12
+	movaps	0xb0(%rbp), %xmm13
+	movaps	0xc0(%rbp), %xmm14
+	movaps	0xd0(%rbp), %xmm15
+	lea	0xa0(%rbp), %rsp
+___
+$code.=<<___;
+	mov	0x48(%rsp), %r15
+	mov	0x50(%rsp), %r14
+	mov	0x58(%rsp), %r13
+	mov	0x60(%rsp), %r12
+	mov	0x68(%rsp), %rbx
+	mov	0x70(%rsp), %rax
+	lea	0x78(%rsp), %rsp
+	mov	%rax, %rbp
+.Lecb_dec_epilogue:
+	ret
+.size	bsaes_ecb_decrypt_blocks,.-bsaes_ecb_decrypt_blocks
+___
+}
+$code.=<<___;
+.extern	asm_AES_cbc_encrypt
+.globl	bsaes_cbc_encrypt
+.type	bsaes_cbc_encrypt,\@abi-omnipotent
+.align	16
+bsaes_cbc_encrypt:
+___
+$code.=<<___ if ($win64);
+	mov	48(%rsp),$arg6		# pull direction flag
+___
+$code.=<<___;
+	cmp	\$0,$arg6
+	jne	asm_AES_cbc_encrypt
+	cmp	\$128,$arg3
+	jb	asm_AES_cbc_encrypt
+
+	mov	%rsp, %rax
+.Lcbc_dec_prologue:
+	push	%rbp
+	push	%rbx
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+	lea	-0x48(%rsp), %rsp
+___
+$code.=<<___ if ($win64);
+	mov	0xa0(%rsp),$arg5	# pull ivp
+	lea	-0xa0(%rsp), %rsp
+	movaps	%xmm6, 0x40(%rsp)
+	movaps	%xmm7, 0x50(%rsp)
+	movaps	%xmm8, 0x60(%rsp)
+	movaps	%xmm9, 0x70(%rsp)
+	movaps	%xmm10, 0x80(%rsp)
+	movaps	%xmm11, 0x90(%rsp)
+	movaps	%xmm12, 0xa0(%rsp)
+	movaps	%xmm13, 0xb0(%rsp)
+	movaps	%xmm14, 0xc0(%rsp)
+	movaps	%xmm15, 0xd0(%rsp)
+.Lcbc_dec_body:
+___
+$code.=<<___;
+	mov	%rsp, %rbp		# backup %rsp
+	mov	240($arg4), %eax	# rounds
+	mov	$arg1, $inp		# backup arguments
+	mov	$arg2, $out
+	mov	$arg3, $len
+	mov	$arg4, $key
+	mov	$arg5, %rbx
+	shr	\$4, $len		# bytes to blocks
+
+	mov	%eax, %edx		# rounds
+	shl	\$7, %rax		# 128 bytes per inner round key
+	sub	\$`128-32`, %rax	# size of bit-sliced key schedule
+	sub	%rax, %rsp
+
+	mov	%rsp, %rax		# pass key schedule
+	mov	$key, %rcx		# pass key
+	mov	%edx, %r10d		# pass rounds
+	call	_bsaes_key_convert
+	pxor	(%rsp),%xmm7		# fix up 0 round key
+	movdqa	%xmm6,(%rax)		# save last round key
+	movdqa	%xmm7,(%rsp)
+
+	movdqu	(%rbx), @XMM[15]	# load IV
+	sub	\$8,$len
+.Lcbc_dec_loop:
+	movdqu	0x00($inp), @XMM[0]	# load input
+	movdqu	0x10($inp), @XMM[1]
+	movdqu	0x20($inp), @XMM[2]
+	movdqu	0x30($inp), @XMM[3]
+	movdqu	0x40($inp), @XMM[4]
+	movdqu	0x50($inp), @XMM[5]
+	mov	%rsp, %rax		# pass key schedule
+	movdqu	0x60($inp), @XMM[6]
+	mov	%edx,%r10d		# pass rounds
+	movdqu	0x70($inp), @XMM[7]
+	movdqa	@XMM[15], 0x20(%rbp)	# put aside IV
+
+	call	_bsaes_decrypt8
+
+	pxor	0x20(%rbp), @XMM[0]	# ^= IV
+	movdqu	0x00($inp), @XMM[8]	# re-load input
+	movdqu	0x10($inp), @XMM[9]
+	pxor	@XMM[8], @XMM[1]
+	movdqu	0x20($inp), @XMM[10]
+	pxor	@XMM[9], @XMM[6]
+	movdqu	0x30($inp), @XMM[11]
+	pxor	@XMM[10], @XMM[4]
+	movdqu	0x40($inp), @XMM[12]
+	pxor	@XMM[11], @XMM[2]
+	movdqu	0x50($inp), @XMM[13]
+	pxor	@XMM[12], @XMM[7]
+	movdqu	0x60($inp), @XMM[14]
+	pxor	@XMM[13], @XMM[3]
+	movdqu	0x70($inp), @XMM[15]	# IV
+	pxor	@XMM[14], @XMM[5]
+	movdqu	@XMM[0], 0x00($out)	# write output
+	lea	0x80($inp), $inp
+	movdqu	@XMM[1], 0x10($out)
+	movdqu	@XMM[6], 0x20($out)
+	movdqu	@XMM[4], 0x30($out)
+	movdqu	@XMM[2], 0x40($out)
+	movdqu	@XMM[7], 0x50($out)
+	movdqu	@XMM[3], 0x60($out)
+	movdqu	@XMM[5], 0x70($out)
+	lea	0x80($out), $out
+	sub	\$8,$len
+	jnc	.Lcbc_dec_loop
+
+	add	\$8,$len
+	jz	.Lcbc_dec_done
+
+	movdqu	0x00($inp), @XMM[0]	# load input
+	mov	%rsp, %rax		# pass key schedule
+	mov	%edx, %r10d		# pass rounds
+	cmp	\$2,$len
+	jb	.Lcbc_dec_one
+	movdqu	0x10($inp), @XMM[1]
+	je	.Lcbc_dec_two
+	movdqu	0x20($inp), @XMM[2]
+	cmp	\$4,$len
+	jb	.Lcbc_dec_three
+	movdqu	0x30($inp), @XMM[3]
+	je	.Lcbc_dec_four
+	movdqu	0x40($inp), @XMM[4]
+	cmp	\$6,$len
+	jb	.Lcbc_dec_five
+	movdqu	0x50($inp), @XMM[5]
+	je	.Lcbc_dec_six
+	movdqu	0x60($inp), @XMM[6]
+	movdqa	@XMM[15], 0x20(%rbp)	# put aside IV
+	call	_bsaes_decrypt8
+	pxor	0x20(%rbp), @XMM[0]	# ^= IV
+	movdqu	0x00($inp), @XMM[8]	# re-load input
+	movdqu	0x10($inp), @XMM[9]
+	pxor	@XMM[8], @XMM[1]
+	movdqu	0x20($inp), @XMM[10]
+	pxor	@XMM[9], @XMM[6]
+	movdqu	0x30($inp), @XMM[11]
+	pxor	@XMM[10], @XMM[4]
+	movdqu	0x40($inp), @XMM[12]
+	pxor	@XMM[11], @XMM[2]
+	movdqu	0x50($inp), @XMM[13]
+	pxor	@XMM[12], @XMM[7]
+	movdqu	0x60($inp), @XMM[15]	# IV
+	pxor	@XMM[13], @XMM[3]
+	movdqu	@XMM[0], 0x00($out)	# write output
+	movdqu	@XMM[1], 0x10($out)
+	movdqu	@XMM[6], 0x20($out)
+	movdqu	@XMM[4], 0x30($out)
+	movdqu	@XMM[2], 0x40($out)
+	movdqu	@XMM[7], 0x50($out)
+	movdqu	@XMM[3], 0x60($out)
+	jmp	.Lcbc_dec_done
+.align	16
+.Lcbc_dec_six:
+	movdqa	@XMM[15], 0x20(%rbp)	# put aside IV
+	call	_bsaes_decrypt8
+	pxor	0x20(%rbp), @XMM[0]	# ^= IV
+	movdqu	0x00($inp), @XMM[8]	# re-load input
+	movdqu	0x10($inp), @XMM[9]
+	pxor	@XMM[8], @XMM[1]
+	movdqu	0x20($inp), @XMM[10]
+	pxor	@XMM[9], @XMM[6]
+	movdqu	0x30($inp), @XMM[11]
+	pxor	@XMM[10], @XMM[4]
+	movdqu	0x40($inp), @XMM[12]
+	pxor	@XMM[11], @XMM[2]
+	movdqu	0x50($inp), @XMM[15]	# IV
+	pxor	@XMM[12], @XMM[7]
+	movdqu	@XMM[0], 0x00($out)	# write output
+	movdqu	@XMM[1], 0x10($out)
+	movdqu	@XMM[6], 0x20($out)
+	movdqu	@XMM[4], 0x30($out)
+	movdqu	@XMM[2], 0x40($out)
+	movdqu	@XMM[7], 0x50($out)
+	jmp	.Lcbc_dec_done
+.align	16
+.Lcbc_dec_five:
+	movdqa	@XMM[15], 0x20(%rbp)	# put aside IV
+	call	_bsaes_decrypt8
+	pxor	0x20(%rbp), @XMM[0]	# ^= IV
+	movdqu	0x00($inp), @XMM[8]	# re-load input
+	movdqu	0x10($inp), @XMM[9]
+	pxor	@XMM[8], @XMM[1]
+	movdqu	0x20($inp), @XMM[10]
+	pxor	@XMM[9], @XMM[6]
+	movdqu	0x30($inp), @XMM[11]
+	pxor	@XMM[10], @XMM[4]
+	movdqu	0x40($inp), @XMM[15]	# IV
+	pxor	@XMM[11], @XMM[2]
+	movdqu	@XMM[0], 0x00($out)	# write output
+	movdqu	@XMM[1], 0x10($out)
+	movdqu	@XMM[6], 0x20($out)
+	movdqu	@XMM[4], 0x30($out)
+	movdqu	@XMM[2], 0x40($out)
+	jmp	.Lcbc_dec_done
+.align	16
+.Lcbc_dec_four:
+	movdqa	@XMM[15], 0x20(%rbp)	# put aside IV
+	call	_bsaes_decrypt8
+	pxor	0x20(%rbp), @XMM[0]	# ^= IV
+	movdqu	0x00($inp), @XMM[8]	# re-load input
+	movdqu	0x10($inp), @XMM[9]
+	pxor	@XMM[8], @XMM[1]
+	movdqu	0x20($inp), @XMM[10]
+	pxor	@XMM[9], @XMM[6]
+	movdqu	0x30($inp), @XMM[15]	# IV
+	pxor	@XMM[10], @XMM[4]
+	movdqu	@XMM[0], 0x00($out)	# write output
+	movdqu	@XMM[1], 0x10($out)
+	movdqu	@XMM[6], 0x20($out)
+	movdqu	@XMM[4], 0x30($out)
+	jmp	.Lcbc_dec_done
+.align	16
+.Lcbc_dec_three:
+	movdqa	@XMM[15], 0x20(%rbp)	# put aside IV
+	call	_bsaes_decrypt8
+	pxor	0x20(%rbp), @XMM[0]	# ^= IV
+	movdqu	0x00($inp), @XMM[8]	# re-load input
+	movdqu	0x10($inp), @XMM[9]
+	pxor	@XMM[8], @XMM[1]
+	movdqu	0x20($inp), @XMM[15]	# IV
+	pxor	@XMM[9], @XMM[6]
+	movdqu	@XMM[0], 0x00($out)	# write output
+	movdqu	@XMM[1], 0x10($out)
+	movdqu	@XMM[6], 0x20($out)
+	jmp	.Lcbc_dec_done
+.align	16
+.Lcbc_dec_two:
+	movdqa	@XMM[15], 0x20(%rbp)	# put aside IV
+	call	_bsaes_decrypt8
+	pxor	0x20(%rbp), @XMM[0]	# ^= IV
+	movdqu	0x00($inp), @XMM[8]	# re-load input
+	movdqu	0x10($inp), @XMM[15]	# IV
+	pxor	@XMM[8], @XMM[1]
+	movdqu	@XMM[0], 0x00($out)	# write output
+	movdqu	@XMM[1], 0x10($out)
+	jmp	.Lcbc_dec_done
+.align	16
+.Lcbc_dec_one:
+	lea	($inp), $arg1
+	lea	0x20(%rbp), $arg2	# buffer output
+	lea	($key), $arg3
+	call	asm_AES_decrypt		# doesn't touch %xmm
+	pxor	0x20(%rbp), @XMM[15]	# ^= IV
+	movdqu	@XMM[15], ($out)	# write output
+	movdqa	@XMM[0], @XMM[15]	# IV
+
+.Lcbc_dec_done:
+	movdqu	@XMM[15], (%rbx)	# return IV
+	lea	(%rsp), %rax
+	pxor	%xmm0, %xmm0
+.Lcbc_dec_bzero:			# wipe key schedule [if any]
+	movdqa	%xmm0, 0x00(%rax)
+	movdqa	%xmm0, 0x10(%rax)
+	lea	0x20(%rax), %rax
+	cmp	%rax, %rbp
+	ja	.Lcbc_dec_bzero
+
+	lea	(%rbp),%rsp		# restore %rsp
+___
+$code.=<<___ if ($win64);
+	movaps	0x40(%rbp), %xmm6
+	movaps	0x50(%rbp), %xmm7
+	movaps	0x60(%rbp), %xmm8
+	movaps	0x70(%rbp), %xmm9
+	movaps	0x80(%rbp), %xmm10
+	movaps	0x90(%rbp), %xmm11
+	movaps	0xa0(%rbp), %xmm12
+	movaps	0xb0(%rbp), %xmm13
+	movaps	0xc0(%rbp), %xmm14
+	movaps	0xd0(%rbp), %xmm15
+	lea	0xa0(%rbp), %rsp
+___
+$code.=<<___;
+	mov	0x48(%rsp), %r15
+	mov	0x50(%rsp), %r14
+	mov	0x58(%rsp), %r13
+	mov	0x60(%rsp), %r12
+	mov	0x68(%rsp), %rbx
+	mov	0x70(%rsp), %rax
+	lea	0x78(%rsp), %rsp
+	mov	%rax, %rbp
+.Lcbc_dec_epilogue:
+	ret
+.size	bsaes_cbc_encrypt,.-bsaes_cbc_encrypt
+
+.globl	bsaes_ctr32_encrypt_blocks
+.type	bsaes_ctr32_encrypt_blocks,\@abi-omnipotent
+.align	16
+bsaes_ctr32_encrypt_blocks:
+	mov	%rsp, %rax
+.Lctr_enc_prologue:
+	push	%rbp
+	push	%rbx
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+	lea	-0x48(%rsp), %rsp
+___
+$code.=<<___ if ($win64);
+	mov	0xa0(%rsp),$arg5	# pull ivp
+	lea	-0xa0(%rsp), %rsp
+	movaps	%xmm6, 0x40(%rsp)
+	movaps	%xmm7, 0x50(%rsp)
+	movaps	%xmm8, 0x60(%rsp)
+	movaps	%xmm9, 0x70(%rsp)
+	movaps	%xmm10, 0x80(%rsp)
+	movaps	%xmm11, 0x90(%rsp)
+	movaps	%xmm12, 0xa0(%rsp)
+	movaps	%xmm13, 0xb0(%rsp)
+	movaps	%xmm14, 0xc0(%rsp)
+	movaps	%xmm15, 0xd0(%rsp)
+.Lctr_enc_body:
+___
+$code.=<<___;
+	mov	%rsp, %rbp		# backup %rsp
+	movdqu	($arg5), %xmm0		# load counter
+	mov	240($arg4), %eax	# rounds
+	mov	$arg1, $inp		# backup arguments
+	mov	$arg2, $out
+	mov	$arg3, $len
+	mov	$arg4, $key
+	movdqa	%xmm0, 0x20(%rbp)	# copy counter
+	cmp	\$8, $arg3
+	jb	.Lctr_enc_short
+
+	mov	%eax, %ebx		# rounds
+	shl	\$7, %rax		# 128 bytes per inner round key
+	sub	\$`128-32`, %rax	# size of bit-sliced key schedule
+	sub	%rax, %rsp
+
+	mov	%rsp, %rax		# pass key schedule
+	mov	$key, %rcx		# pass key
+	mov	%ebx, %r10d		# pass rounds
+	call	_bsaes_key_convert
+	pxor	%xmm6,%xmm7		# fix up last round key
+	movdqa	%xmm7,(%rax)		# save last round key
+
+	movdqa	(%rsp), @XMM[9]		# load round0 key
+	lea	.LADD1(%rip), %r11
+	movdqa	0x20(%rbp), @XMM[0]	# counter copy
+	movdqa	-0x20(%r11), @XMM[8]	# .LSWPUP
+	pshufb	@XMM[8], @XMM[9]	# byte swap upper part
+	pshufb	@XMM[8], @XMM[0]
+	movdqa	@XMM[9], (%rsp)		# save adjusted round0 key
+	jmp	.Lctr_enc_loop
+.align	16
+.Lctr_enc_loop:
+	movdqa	@XMM[0], 0x20(%rbp)	# save counter
+	movdqa	@XMM[0], @XMM[1]	# prepare 8 counter values
+	movdqa	@XMM[0], @XMM[2]
+	paddd	0x00(%r11), @XMM[1]	# .LADD1
+	movdqa	@XMM[0], @XMM[3]
+	paddd	0x10(%r11), @XMM[2]	# .LADD2
+	movdqa	@XMM[0], @XMM[4]
+	paddd	0x20(%r11), @XMM[3]	# .LADD3
+	movdqa	@XMM[0], @XMM[5]
+	paddd	0x30(%r11), @XMM[4]	# .LADD4
+	movdqa	@XMM[0], @XMM[6]
+	paddd	0x40(%r11), @XMM[5]	# .LADD5
+	movdqa	@XMM[0], @XMM[7]
+	paddd	0x50(%r11), @XMM[6]	# .LADD6
+	paddd	0x60(%r11), @XMM[7]	# .LADD7
+
+	# Borrow prologue from _bsaes_encrypt8 to use the opportunity
+	# to flip byte order in 32-bit counter
+	movdqa	(%rsp), @XMM[9]		# round 0 key
+	lea	0x10(%rsp), %rax	# pass key schedule
+	movdqa	-0x10(%r11), @XMM[8]	# .LSWPUPM0SR
+	pxor	@XMM[9], @XMM[0]	# xor with round0 key
+	pxor	@XMM[9], @XMM[1]
+	pxor	@XMM[9], @XMM[2]
+	pxor	@XMM[9], @XMM[3]
+	 pshufb	@XMM[8], @XMM[0]
+	 pshufb	@XMM[8], @XMM[1]
+	pxor	@XMM[9], @XMM[4]
+	pxor	@XMM[9], @XMM[5]
+	 pshufb	@XMM[8], @XMM[2]
+	 pshufb	@XMM[8], @XMM[3]
+	pxor	@XMM[9], @XMM[6]
+	pxor	@XMM[9], @XMM[7]
+	 pshufb	@XMM[8], @XMM[4]
+	 pshufb	@XMM[8], @XMM[5]
+	 pshufb	@XMM[8], @XMM[6]
+	 pshufb	@XMM[8], @XMM[7]
+	lea	.LBS0(%rip), %r11	# constants table
+	mov	%ebx,%r10d		# pass rounds
+
+	call	_bsaes_encrypt8_bitslice
+
+	sub	\$8,$len
+	jc	.Lctr_enc_loop_done
+
+	movdqu	0x00($inp), @XMM[8]	# load input
+	movdqu	0x10($inp), @XMM[9]
+	movdqu	0x20($inp), @XMM[10]
+	movdqu	0x30($inp), @XMM[11]
+	movdqu	0x40($inp), @XMM[12]
+	movdqu	0x50($inp), @XMM[13]
+	movdqu	0x60($inp), @XMM[14]
+	movdqu	0x70($inp), @XMM[15]
+	lea	0x80($inp),$inp
+	pxor	@XMM[0], @XMM[8]
+	movdqa	0x20(%rbp), @XMM[0]	# load counter
+	pxor	@XMM[9], @XMM[1]
+	movdqu	@XMM[8], 0x00($out)	# write output
+	pxor	@XMM[10], @XMM[4]
+	movdqu	@XMM[1], 0x10($out)
+	pxor	@XMM[11], @XMM[6]
+	movdqu	@XMM[4], 0x20($out)
+	pxor	@XMM[12], @XMM[3]
+	movdqu	@XMM[6], 0x30($out)
+	pxor	@XMM[13], @XMM[7]
+	movdqu	@XMM[3], 0x40($out)
+	pxor	@XMM[14], @XMM[2]
+	movdqu	@XMM[7], 0x50($out)
+	pxor	@XMM[15], @XMM[5]
+	movdqu	@XMM[2], 0x60($out)
+	lea	.LADD1(%rip), %r11
+	movdqu	@XMM[5], 0x70($out)
+	lea	0x80($out), $out
+	paddd	0x70(%r11), @XMM[0]	# .LADD8
+	jnz	.Lctr_enc_loop
+
+	jmp	.Lctr_enc_done
+.align	16
+.Lctr_enc_loop_done:
+	add	\$8, $len
+	movdqu	0x00($inp), @XMM[8]	# load input
+	pxor	@XMM[8], @XMM[0]
+	movdqu	@XMM[0], 0x00($out)	# write output
+	cmp	\$2,$len
+	jb	.Lctr_enc_done
+	movdqu	0x10($inp), @XMM[9]
+	pxor	@XMM[9], @XMM[1]
+	movdqu	@XMM[1], 0x10($out)
+	je	.Lctr_enc_done
+	movdqu	0x20($inp), @XMM[10]
+	pxor	@XMM[10], @XMM[4]
+	movdqu	@XMM[4], 0x20($out)
+	cmp	\$4,$len
+	jb	.Lctr_enc_done
+	movdqu	0x30($inp), @XMM[11]
+	pxor	@XMM[11], @XMM[6]
+	movdqu	@XMM[6], 0x30($out)
+	je	.Lctr_enc_done
+	movdqu	0x40($inp), @XMM[12]
+	pxor	@XMM[12], @XMM[3]
+	movdqu	@XMM[3], 0x40($out)
+	cmp	\$6,$len
+	jb	.Lctr_enc_done
+	movdqu	0x50($inp), @XMM[13]
+	pxor	@XMM[13], @XMM[7]
+	movdqu	@XMM[7], 0x50($out)
+	je	.Lctr_enc_done
+	movdqu	0x60($inp), @XMM[14]
+	pxor	@XMM[14], @XMM[2]
+	movdqu	@XMM[2], 0x60($out)
+	jmp	.Lctr_enc_done
+
+.align	16
+.Lctr_enc_short:
+	lea	0x20(%rbp), $arg1
+	lea	0x30(%rbp), $arg2
+	lea	($key), $arg3
+	call	asm_AES_encrypt
+	movdqu	($inp), @XMM[1]
+	lea	16($inp), $inp
+	mov	0x2c(%rbp), %eax	# load 32-bit counter
+	bswap	%eax
+	pxor	0x30(%rbp), @XMM[1]
+	inc	%eax			# increment
+	movdqu	@XMM[1], ($out)
+	bswap	%eax
+	lea	16($out), $out
+	mov	%eax, 0x2c(%rsp)	# save 32-bit counter
+	dec	$len
+	jnz	.Lctr_enc_short
+
+.Lctr_enc_done:
+	lea	(%rsp), %rax
+	pxor	%xmm0, %xmm0
+.Lctr_enc_bzero:			# wipe key schedule [if any]
+	movdqa	%xmm0, 0x00(%rax)
+	movdqa	%xmm0, 0x10(%rax)
+	lea	0x20(%rax), %rax
+	cmp	%rax, %rbp
+	ja	.Lctr_enc_bzero
+
+	lea	(%rbp),%rsp		# restore %rsp
+___
+$code.=<<___ if ($win64);
+	movaps	0x40(%rbp), %xmm6
+	movaps	0x50(%rbp), %xmm7
+	movaps	0x60(%rbp), %xmm8
+	movaps	0x70(%rbp), %xmm9
+	movaps	0x80(%rbp), %xmm10
+	movaps	0x90(%rbp), %xmm11
+	movaps	0xa0(%rbp), %xmm12
+	movaps	0xb0(%rbp), %xmm13
+	movaps	0xc0(%rbp), %xmm14
+	movaps	0xd0(%rbp), %xmm15
+	lea	0xa0(%rbp), %rsp
+___
+$code.=<<___;
+	mov	0x48(%rsp), %r15
+	mov	0x50(%rsp), %r14
+	mov	0x58(%rsp), %r13
+	mov	0x60(%rsp), %r12
+	mov	0x68(%rsp), %rbx
+	mov	0x70(%rsp), %rax
+	lea	0x78(%rsp), %rsp
+	mov	%rax, %rbp
+.Lctr_enc_epilogue:
+	ret
+.size	bsaes_ctr32_encrypt_blocks,.-bsaes_ctr32_encrypt_blocks
+___
+######################################################################
+# void bsaes_xts_[en|de]crypt(const char *inp,char *out,size_t len,
+#	const AES_KEY *key1, const AES_KEY *key2,
+#	const unsigned char iv[16]);
+#
+my ($twmask,$twres,$twtmp)=@XMM[13..15];
+$arg6=~s/d$//;
+
+$code.=<<___;
+.globl	bsaes_xts_encrypt
+.type	bsaes_xts_encrypt,\@abi-omnipotent
+.align	16
+bsaes_xts_encrypt:
+	mov	%rsp, %rax
+.Lxts_enc_prologue:
+	push	%rbp
+	push	%rbx
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+	lea	-0x48(%rsp), %rsp
+___
+$code.=<<___ if ($win64);
+	mov	0xa0(%rsp),$arg5	# pull key2
+	mov	0xa8(%rsp),$arg6	# pull ivp
+	lea	-0xa0(%rsp), %rsp
+	movaps	%xmm6, 0x40(%rsp)
+	movaps	%xmm7, 0x50(%rsp)
+	movaps	%xmm8, 0x60(%rsp)
+	movaps	%xmm9, 0x70(%rsp)
+	movaps	%xmm10, 0x80(%rsp)
+	movaps	%xmm11, 0x90(%rsp)
+	movaps	%xmm12, 0xa0(%rsp)
+	movaps	%xmm13, 0xb0(%rsp)
+	movaps	%xmm14, 0xc0(%rsp)
+	movaps	%xmm15, 0xd0(%rsp)
+.Lxts_enc_body:
+___
+$code.=<<___;
+	mov	%rsp, %rbp		# backup %rsp
+	mov	$arg1, $inp		# backup arguments
+	mov	$arg2, $out
+	mov	$arg3, $len
+	mov	$arg4, $key
+
+	lea	($arg6), $arg1
+	lea	0x20(%rbp), $arg2
+	lea	($arg5), $arg3
+	call	asm_AES_encrypt		# generate initial tweak
+
+	mov	240($key), %eax		# rounds
+	mov	$len, %rbx		# backup $len
+
+	mov	%eax, %edx		# rounds
+	shl	\$7, %rax		# 128 bytes per inner round key
+	sub	\$`128-32`, %rax	# size of bit-sliced key schedule
+	sub	%rax, %rsp
+
+	mov	%rsp, %rax		# pass key schedule
+	mov	$key, %rcx		# pass key
+	mov	%edx, %r10d		# pass rounds
+	call	_bsaes_key_convert
+	pxor	%xmm6, %xmm7		# fix up last round key
+	movdqa	%xmm7, (%rax)		# save last round key
+
+	and	\$-16, $len
+	sub	\$0x80, %rsp		# place for tweak[8]
+	movdqa	0x20(%rbp), @XMM[7]	# initial tweak
+
+	pxor	$twtmp, $twtmp
+	movdqa	.Lxts_magic(%rip), $twmask
+	pcmpgtd	@XMM[7], $twtmp		# broadcast upper bits
+
+	sub	\$0x80, $len
+	jc	.Lxts_enc_short
+	jmp	.Lxts_enc_loop
+
+.align	16
+.Lxts_enc_loop:
+___
+    for ($i=0;$i<7;$i++) {
+    $code.=<<___;
+	pshufd	\$0x13, $twtmp, $twres
+	pxor	$twtmp, $twtmp
+	movdqa	@XMM[7], @XMM[$i]
+	movdqa	@XMM[7], `0x10*$i`(%rsp)# save tweak[$i]
+	paddq	@XMM[7], @XMM[7]	# psllq	1,$tweak
+	pand	$twmask, $twres		# isolate carry and residue
+	pcmpgtd	@XMM[7], $twtmp		# broadcast upper bits
+	pxor	$twres, @XMM[7]
+___
+    $code.=<<___ if ($i>=1);
+	movdqu	`0x10*($i-1)`($inp), @XMM[8+$i-1]
+___
+    $code.=<<___ if ($i>=2);
+	pxor	@XMM[8+$i-2], @XMM[$i-2]# input[] ^ tweak[]
+___
+    }
+$code.=<<___;
+	movdqu	0x60($inp), @XMM[8+6]
+	pxor	@XMM[8+5], @XMM[5]
+	movdqu	0x70($inp), @XMM[8+7]
+	lea	0x80($inp), $inp
+	movdqa	@XMM[7], 0x70(%rsp)
+	pxor	@XMM[8+6], @XMM[6]
+	lea	0x80(%rsp), %rax	# pass key schedule
+	pxor	@XMM[8+7], @XMM[7]
+	mov	%edx, %r10d		# pass rounds
+
+	call	_bsaes_encrypt8
+
+	pxor	0x00(%rsp), @XMM[0]	# ^= tweak[]
+	pxor	0x10(%rsp), @XMM[1]
+	movdqu	@XMM[0], 0x00($out)	# write output
+	pxor	0x20(%rsp), @XMM[4]
+	movdqu	@XMM[1], 0x10($out)
+	pxor	0x30(%rsp), @XMM[6]
+	movdqu	@XMM[4], 0x20($out)
+	pxor	0x40(%rsp), @XMM[3]
+	movdqu	@XMM[6], 0x30($out)
+	pxor	0x50(%rsp), @XMM[7]
+	movdqu	@XMM[3], 0x40($out)
+	pxor	0x60(%rsp), @XMM[2]
+	movdqu	@XMM[7], 0x50($out)
+	pxor	0x70(%rsp), @XMM[5]
+	movdqu	@XMM[2], 0x60($out)
+	movdqu	@XMM[5], 0x70($out)
+	lea	0x80($out), $out
+
+	movdqa	0x70(%rsp), @XMM[7]	# prepare next iteration tweak
+	pxor	$twtmp, $twtmp
+	movdqa	.Lxts_magic(%rip), $twmask
+	pcmpgtd	@XMM[7], $twtmp
+	pshufd	\$0x13, $twtmp, $twres
+	pxor	$twtmp, $twtmp
+	paddq	@XMM[7], @XMM[7]	# psllq	1,$tweak
+	pand	$twmask, $twres		# isolate carry and residue
+	pcmpgtd	@XMM[7], $twtmp		# broadcast upper bits
+	pxor	$twres, @XMM[7]
+
+	sub	\$0x80,$len
+	jnc	.Lxts_enc_loop
+
+.Lxts_enc_short:
+	add	\$0x80, $len
+	jz	.Lxts_enc_done
+___
+    for ($i=0;$i<7;$i++) {
+    $code.=<<___;
+	pshufd	\$0x13, $twtmp, $twres
+	pxor	$twtmp, $twtmp
+	movdqa	@XMM[7], @XMM[$i]
+	movdqa	@XMM[7], `0x10*$i`(%rsp)# save tweak[$i]
+	paddq	@XMM[7], @XMM[7]	# psllq	1,$tweak
+	pand	$twmask, $twres		# isolate carry and residue
+	pcmpgtd	@XMM[7], $twtmp		# broadcast upper bits
+	pxor	$twres, @XMM[7]
+___
+    $code.=<<___ if ($i>=1);
+	movdqu	`0x10*($i-1)`($inp), @XMM[8+$i-1]
+	cmp	\$`0x10*$i`,$len
+	je	.Lxts_enc_$i
+___
+    $code.=<<___ if ($i>=2);
+	pxor	@XMM[8+$i-2], @XMM[$i-2]# input[] ^ tweak[]
+___
+    }
+$code.=<<___;
+	movdqu	0x60($inp), @XMM[8+6]
+	pxor	@XMM[8+5], @XMM[5]
+	movdqa	@XMM[7], 0x70(%rsp)
+	lea	0x70($inp), $inp
+	pxor	@XMM[8+6], @XMM[6]
+	lea	0x80(%rsp), %rax	# pass key schedule
+	mov	%edx, %r10d		# pass rounds
+
+	call	_bsaes_encrypt8
+
+	pxor	0x00(%rsp), @XMM[0]	# ^= tweak[]
+	pxor	0x10(%rsp), @XMM[1]
+	movdqu	@XMM[0], 0x00($out)	# write output
+	pxor	0x20(%rsp), @XMM[4]
+	movdqu	@XMM[1], 0x10($out)
+	pxor	0x30(%rsp), @XMM[6]
+	movdqu	@XMM[4], 0x20($out)
+	pxor	0x40(%rsp), @XMM[3]
+	movdqu	@XMM[6], 0x30($out)
+	pxor	0x50(%rsp), @XMM[7]
+	movdqu	@XMM[3], 0x40($out)
+	pxor	0x60(%rsp), @XMM[2]
+	movdqu	@XMM[7], 0x50($out)
+	movdqu	@XMM[2], 0x60($out)
+	lea	0x70($out), $out
+
+	movdqa	0x70(%rsp), @XMM[7]	# next iteration tweak
+	jmp	.Lxts_enc_done
+.align	16
+.Lxts_enc_6:
+	pxor	@XMM[8+4], @XMM[4]
+	lea	0x60($inp), $inp
+	pxor	@XMM[8+5], @XMM[5]
+	lea	0x80(%rsp), %rax	# pass key schedule
+	mov	%edx, %r10d		# pass rounds
+
+	call	_bsaes_encrypt8
+
+	pxor	0x00(%rsp), @XMM[0]	# ^= tweak[]
+	pxor	0x10(%rsp), @XMM[1]
+	movdqu	@XMM[0], 0x00($out)	# write output
+	pxor	0x20(%rsp), @XMM[4]
+	movdqu	@XMM[1], 0x10($out)
+	pxor	0x30(%rsp), @XMM[6]
+	movdqu	@XMM[4], 0x20($out)
+	pxor	0x40(%rsp), @XMM[3]
+	movdqu	@XMM[6], 0x30($out)
+	pxor	0x50(%rsp), @XMM[7]
+	movdqu	@XMM[3], 0x40($out)
+	movdqu	@XMM[7], 0x50($out)
+	lea	0x60($out), $out
+
+	movdqa	0x60(%rsp), @XMM[7]	# next iteration tweak
+	jmp	.Lxts_enc_done
+.align	16
+.Lxts_enc_5:
+	pxor	@XMM[8+3], @XMM[3]
+	lea	0x50($inp), $inp
+	pxor	@XMM[8+4], @XMM[4]
+	lea	0x80(%rsp), %rax	# pass key schedule
+	mov	%edx, %r10d		# pass rounds
+
+	call	_bsaes_encrypt8
+
+	pxor	0x00(%rsp), @XMM[0]	# ^= tweak[]
+	pxor	0x10(%rsp), @XMM[1]
+	movdqu	@XMM[0], 0x00($out)	# write output
+	pxor	0x20(%rsp), @XMM[4]
+	movdqu	@XMM[1], 0x10($out)
+	pxor	0x30(%rsp), @XMM[6]
+	movdqu	@XMM[4], 0x20($out)
+	pxor	0x40(%rsp), @XMM[3]
+	movdqu	@XMM[6], 0x30($out)
+	movdqu	@XMM[3], 0x40($out)
+	lea	0x50($out), $out
+
+	movdqa	0x50(%rsp), @XMM[7]	# next iteration tweak
+	jmp	.Lxts_enc_done
+.align	16
+.Lxts_enc_4:
+	pxor	@XMM[8+2], @XMM[2]
+	lea	0x40($inp), $inp
+	pxor	@XMM[8+3], @XMM[3]
+	lea	0x80(%rsp), %rax	# pass key schedule
+	mov	%edx, %r10d		# pass rounds
+
+	call	_bsaes_encrypt8
+
+	pxor	0x00(%rsp), @XMM[0]	# ^= tweak[]
+	pxor	0x10(%rsp), @XMM[1]
+	movdqu	@XMM[0], 0x00($out)	# write output
+	pxor	0x20(%rsp), @XMM[4]
+	movdqu	@XMM[1], 0x10($out)
+	pxor	0x30(%rsp), @XMM[6]
+	movdqu	@XMM[4], 0x20($out)
+	movdqu	@XMM[6], 0x30($out)
+	lea	0x40($out), $out
+
+	movdqa	0x40(%rsp), @XMM[7]	# next iteration tweak
+	jmp	.Lxts_enc_done
+.align	16
+.Lxts_enc_3:
+	pxor	@XMM[8+1], @XMM[1]
+	lea	0x30($inp), $inp
+	pxor	@XMM[8+2], @XMM[2]
+	lea	0x80(%rsp), %rax	# pass key schedule
+	mov	%edx, %r10d		# pass rounds
+
+	call	_bsaes_encrypt8
+
+	pxor	0x00(%rsp), @XMM[0]	# ^= tweak[]
+	pxor	0x10(%rsp), @XMM[1]
+	movdqu	@XMM[0], 0x00($out)	# write output
+	pxor	0x20(%rsp), @XMM[4]
+	movdqu	@XMM[1], 0x10($out)
+	movdqu	@XMM[4], 0x20($out)
+	lea	0x30($out), $out
+
+	movdqa	0x30(%rsp), @XMM[7]	# next iteration tweak
+	jmp	.Lxts_enc_done
+.align	16
+.Lxts_enc_2:
+	pxor	@XMM[8+0], @XMM[0]
+	lea	0x20($inp), $inp
+	pxor	@XMM[8+1], @XMM[1]
+	lea	0x80(%rsp), %rax	# pass key schedule
+	mov	%edx, %r10d		# pass rounds
+
+	call	_bsaes_encrypt8
+
+	pxor	0x00(%rsp), @XMM[0]	# ^= tweak[]
+	pxor	0x10(%rsp), @XMM[1]
+	movdqu	@XMM[0], 0x00($out)	# write output
+	movdqu	@XMM[1], 0x10($out)
+	lea	0x20($out), $out
+
+	movdqa	0x20(%rsp), @XMM[7]	# next iteration tweak
+	jmp	.Lxts_enc_done
+.align	16
+.Lxts_enc_1:
+	pxor	@XMM[0], @XMM[8]
+	lea	0x10($inp), $inp
+	movdqa	@XMM[8], 0x20(%rbp)
+	lea	0x20(%rbp), $arg1
+	lea	0x20(%rbp), $arg2
+	lea	($key), $arg3
+	call	asm_AES_encrypt		# doesn't touch %xmm
+	pxor	0x20(%rbp), @XMM[0]	# ^= tweak[]
+	#pxor	@XMM[8], @XMM[0]
+	#lea	0x80(%rsp), %rax	# pass key schedule
+	#mov	%edx, %r10d		# pass rounds
+	#call	_bsaes_encrypt8
+	#pxor	0x00(%rsp), @XMM[0]	# ^= tweak[]
+	movdqu	@XMM[0], 0x00($out)	# write output
+	lea	0x10($out), $out
+
+	movdqa	0x10(%rsp), @XMM[7]	# next iteration tweak
+
+.Lxts_enc_done:
+	and	\$15, %ebx
+	jz	.Lxts_enc_ret
+	mov	$out, %rdx
+
+.Lxts_enc_steal:
+	movzb	($inp), %eax
+	movzb	-16(%rdx), %ecx
+	lea	1($inp), $inp
+	mov	%al, -16(%rdx)
+	mov	%cl, 0(%rdx)
+	lea	1(%rdx), %rdx
+	sub	\$1,%ebx
+	jnz	.Lxts_enc_steal
+
+	movdqu	-16($out), @XMM[0]
+	lea	0x20(%rbp), $arg1
+	pxor	@XMM[7], @XMM[0]
+	lea	0x20(%rbp), $arg2
+	movdqa	@XMM[0], 0x20(%rbp)
+	lea	($key), $arg3
+	call	asm_AES_encrypt		# doesn't touch %xmm
+	pxor	0x20(%rbp), @XMM[7]
+	movdqu	@XMM[7], -16($out)
+
+.Lxts_enc_ret:
+	lea	(%rsp), %rax
+	pxor	%xmm0, %xmm0
+.Lxts_enc_bzero:			# wipe key schedule [if any]
+	movdqa	%xmm0, 0x00(%rax)
+	movdqa	%xmm0, 0x10(%rax)
+	lea	0x20(%rax), %rax
+	cmp	%rax, %rbp
+	ja	.Lxts_enc_bzero
+
+	lea	(%rbp),%rsp		# restore %rsp
+___
+$code.=<<___ if ($win64);
+	movaps	0x40(%rbp), %xmm6
+	movaps	0x50(%rbp), %xmm7
+	movaps	0x60(%rbp), %xmm8
+	movaps	0x70(%rbp), %xmm9
+	movaps	0x80(%rbp), %xmm10
+	movaps	0x90(%rbp), %xmm11
+	movaps	0xa0(%rbp), %xmm12
+	movaps	0xb0(%rbp), %xmm13
+	movaps	0xc0(%rbp), %xmm14
+	movaps	0xd0(%rbp), %xmm15
+	lea	0xa0(%rbp), %rsp
+___
+$code.=<<___;
+	mov	0x48(%rsp), %r15
+	mov	0x50(%rsp), %r14
+	mov	0x58(%rsp), %r13
+	mov	0x60(%rsp), %r12
+	mov	0x68(%rsp), %rbx
+	mov	0x70(%rsp), %rax
+	lea	0x78(%rsp), %rsp
+	mov	%rax, %rbp
+.Lxts_enc_epilogue:
+	ret
+.size	bsaes_xts_encrypt,.-bsaes_xts_encrypt
+
+.globl	bsaes_xts_decrypt
+.type	bsaes_xts_decrypt,\@abi-omnipotent
+.align	16
+bsaes_xts_decrypt:
+	mov	%rsp, %rax
+.Lxts_dec_prologue:
+	push	%rbp
+	push	%rbx
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+	lea	-0x48(%rsp), %rsp
+___
+$code.=<<___ if ($win64);
+	mov	0xa0(%rsp),$arg5	# pull key2
+	mov	0xa8(%rsp),$arg6	# pull ivp
+	lea	-0xa0(%rsp), %rsp
+	movaps	%xmm6, 0x40(%rsp)
+	movaps	%xmm7, 0x50(%rsp)
+	movaps	%xmm8, 0x60(%rsp)
+	movaps	%xmm9, 0x70(%rsp)
+	movaps	%xmm10, 0x80(%rsp)
+	movaps	%xmm11, 0x90(%rsp)
+	movaps	%xmm12, 0xa0(%rsp)
+	movaps	%xmm13, 0xb0(%rsp)
+	movaps	%xmm14, 0xc0(%rsp)
+	movaps	%xmm15, 0xd0(%rsp)
+.Lxts_dec_body:
+___
+$code.=<<___;
+	mov	%rsp, %rbp		# backup %rsp
+	mov	$arg1, $inp		# backup arguments
+	mov	$arg2, $out
+	mov	$arg3, $len
+	mov	$arg4, $key
+
+	lea	($arg6), $arg1
+	lea	0x20(%rbp), $arg2
+	lea	($arg5), $arg3
+	call	asm_AES_encrypt		# generate initial tweak
+
+	mov	240($key), %eax		# rounds
+	mov	$len, %rbx		# backup $len
+
+	mov	%eax, %edx		# rounds
+	shl	\$7, %rax		# 128 bytes per inner round key
+	sub	\$`128-32`, %rax	# size of bit-sliced key schedule
+	sub	%rax, %rsp
+
+	mov	%rsp, %rax		# pass key schedule
+	mov	$key, %rcx		# pass key
+	mov	%edx, %r10d		# pass rounds
+	call	_bsaes_key_convert
+	pxor	(%rsp), %xmm7		# fix up round 0 key
+	movdqa	%xmm6, (%rax)		# save last round key
+	movdqa	%xmm7, (%rsp)
+
+	xor	%eax, %eax		# if ($len%16) len-=16;
+	and	\$-16, $len
+	test	\$15, %ebx
+	setnz	%al
+	shl	\$4, %rax
+	sub	%rax, $len
+
+	sub	\$0x80, %rsp		# place for tweak[8]
+	movdqa	0x20(%rbp), @XMM[7]	# initial tweak
+
+	pxor	$twtmp, $twtmp
+	movdqa	.Lxts_magic(%rip), $twmask
+	pcmpgtd	@XMM[7], $twtmp		# broadcast upper bits
+
+	sub	\$0x80, $len
+	jc	.Lxts_dec_short
+	jmp	.Lxts_dec_loop
+
+.align	16
+.Lxts_dec_loop:
+___
+    for ($i=0;$i<7;$i++) {
+    $code.=<<___;
+	pshufd	\$0x13, $twtmp, $twres
+	pxor	$twtmp, $twtmp
+	movdqa	@XMM[7], @XMM[$i]
+	movdqa	@XMM[7], `0x10*$i`(%rsp)# save tweak[$i]
+	paddq	@XMM[7], @XMM[7]	# psllq	1,$tweak
+	pand	$twmask, $twres		# isolate carry and residue
+	pcmpgtd	@XMM[7], $twtmp		# broadcast upper bits
+	pxor	$twres, @XMM[7]
+___
+    $code.=<<___ if ($i>=1);
+	movdqu	`0x10*($i-1)`($inp), @XMM[8+$i-1]
+___
+    $code.=<<___ if ($i>=2);
+	pxor	@XMM[8+$i-2], @XMM[$i-2]# input[] ^ tweak[]
+___
+    }
+$code.=<<___;
+	movdqu	0x60($inp), @XMM[8+6]
+	pxor	@XMM[8+5], @XMM[5]
+	movdqu	0x70($inp), @XMM[8+7]
+	lea	0x80($inp), $inp
+	movdqa	@XMM[7], 0x70(%rsp)
+	pxor	@XMM[8+6], @XMM[6]
+	lea	0x80(%rsp), %rax	# pass key schedule
+	pxor	@XMM[8+7], @XMM[7]
+	mov	%edx, %r10d		# pass rounds
+
+	call	_bsaes_decrypt8
+
+	pxor	0x00(%rsp), @XMM[0]	# ^= tweak[]
+	pxor	0x10(%rsp), @XMM[1]
+	movdqu	@XMM[0], 0x00($out)	# write output
+	pxor	0x20(%rsp), @XMM[6]
+	movdqu	@XMM[1], 0x10($out)
+	pxor	0x30(%rsp), @XMM[4]
+	movdqu	@XMM[6], 0x20($out)
+	pxor	0x40(%rsp), @XMM[2]
+	movdqu	@XMM[4], 0x30($out)
+	pxor	0x50(%rsp), @XMM[7]
+	movdqu	@XMM[2], 0x40($out)
+	pxor	0x60(%rsp), @XMM[3]
+	movdqu	@XMM[7], 0x50($out)
+	pxor	0x70(%rsp), @XMM[5]
+	movdqu	@XMM[3], 0x60($out)
+	movdqu	@XMM[5], 0x70($out)
+	lea	0x80($out), $out
+
+	movdqa	0x70(%rsp), @XMM[7]	# prepare next iteration tweak
+	pxor	$twtmp, $twtmp
+	movdqa	.Lxts_magic(%rip), $twmask
+	pcmpgtd	@XMM[7], $twtmp
+	pshufd	\$0x13, $twtmp, $twres
+	pxor	$twtmp, $twtmp
+	paddq	@XMM[7], @XMM[7]	# psllq	1,$tweak
+	pand	$twmask, $twres		# isolate carry and residue
+	pcmpgtd	@XMM[7], $twtmp		# broadcast upper bits
+	pxor	$twres, @XMM[7]
+
+	sub	\$0x80,$len
+	jnc	.Lxts_dec_loop
+
+.Lxts_dec_short:
+	add	\$0x80, $len
+	jz	.Lxts_dec_done
+___
+    for ($i=0;$i<7;$i++) {
+    $code.=<<___;
+	pshufd	\$0x13, $twtmp, $twres
+	pxor	$twtmp, $twtmp
+	movdqa	@XMM[7], @XMM[$i]
+	movdqa	@XMM[7], `0x10*$i`(%rsp)# save tweak[$i]
+	paddq	@XMM[7], @XMM[7]	# psllq	1,$tweak
+	pand	$twmask, $twres		# isolate carry and residue
+	pcmpgtd	@XMM[7], $twtmp		# broadcast upper bits
+	pxor	$twres, @XMM[7]
+___
+    $code.=<<___ if ($i>=1);
+	movdqu	`0x10*($i-1)`($inp), @XMM[8+$i-1]
+	cmp	\$`0x10*$i`,$len
+	je	.Lxts_dec_$i
+___
+    $code.=<<___ if ($i>=2);
+	pxor	@XMM[8+$i-2], @XMM[$i-2]# input[] ^ tweak[]
+___
+    }
+$code.=<<___;
+	movdqu	0x60($inp), @XMM[8+6]
+	pxor	@XMM[8+5], @XMM[5]
+	movdqa	@XMM[7], 0x70(%rsp)
+	lea	0x70($inp), $inp
+	pxor	@XMM[8+6], @XMM[6]
+	lea	0x80(%rsp), %rax	# pass key schedule
+	mov	%edx, %r10d		# pass rounds
+
+	call	_bsaes_decrypt8
+
+	pxor	0x00(%rsp), @XMM[0]	# ^= tweak[]
+	pxor	0x10(%rsp), @XMM[1]
+	movdqu	@XMM[0], 0x00($out)	# write output
+	pxor	0x20(%rsp), @XMM[6]
+	movdqu	@XMM[1], 0x10($out)
+	pxor	0x30(%rsp), @XMM[4]
+	movdqu	@XMM[6], 0x20($out)
+	pxor	0x40(%rsp), @XMM[2]
+	movdqu	@XMM[4], 0x30($out)
+	pxor	0x50(%rsp), @XMM[7]
+	movdqu	@XMM[2], 0x40($out)
+	pxor	0x60(%rsp), @XMM[3]
+	movdqu	@XMM[7], 0x50($out)
+	movdqu	@XMM[3], 0x60($out)
+	lea	0x70($out), $out
+
+	movdqa	0x70(%rsp), @XMM[7]	# next iteration tweak
+	jmp	.Lxts_dec_done
+.align	16
+.Lxts_dec_6:
+	pxor	@XMM[8+4], @XMM[4]
+	lea	0x60($inp), $inp
+	pxor	@XMM[8+5], @XMM[5]
+	lea	0x80(%rsp), %rax	# pass key schedule
+	mov	%edx, %r10d		# pass rounds
+
+	call	_bsaes_decrypt8
+
+	pxor	0x00(%rsp), @XMM[0]	# ^= tweak[]
+	pxor	0x10(%rsp), @XMM[1]
+	movdqu	@XMM[0], 0x00($out)	# write output
+	pxor	0x20(%rsp), @XMM[6]
+	movdqu	@XMM[1], 0x10($out)
+	pxor	0x30(%rsp), @XMM[4]
+	movdqu	@XMM[6], 0x20($out)
+	pxor	0x40(%rsp), @XMM[2]
+	movdqu	@XMM[4], 0x30($out)
+	pxor	0x50(%rsp), @XMM[7]
+	movdqu	@XMM[2], 0x40($out)
+	movdqu	@XMM[7], 0x50($out)
+	lea	0x60($out), $out
+
+	movdqa	0x60(%rsp), @XMM[7]	# next iteration tweak
+	jmp	.Lxts_dec_done
+.align	16
+.Lxts_dec_5:
+	pxor	@XMM[8+3], @XMM[3]
+	lea	0x50($inp), $inp
+	pxor	@XMM[8+4], @XMM[4]
+	lea	0x80(%rsp), %rax	# pass key schedule
+	mov	%edx, %r10d		# pass rounds
+
+	call	_bsaes_decrypt8
+
+	pxor	0x00(%rsp), @XMM[0]	# ^= tweak[]
+	pxor	0x10(%rsp), @XMM[1]
+	movdqu	@XMM[0], 0x00($out)	# write output
+	pxor	0x20(%rsp), @XMM[6]
+	movdqu	@XMM[1], 0x10($out)
+	pxor	0x30(%rsp), @XMM[4]
+	movdqu	@XMM[6], 0x20($out)
+	pxor	0x40(%rsp), @XMM[2]
+	movdqu	@XMM[4], 0x30($out)
+	movdqu	@XMM[2], 0x40($out)
+	lea	0x50($out), $out
+
+	movdqa	0x50(%rsp), @XMM[7]	# next iteration tweak
+	jmp	.Lxts_dec_done
+.align	16
+.Lxts_dec_4:
+	pxor	@XMM[8+2], @XMM[2]
+	lea	0x40($inp), $inp
+	pxor	@XMM[8+3], @XMM[3]
+	lea	0x80(%rsp), %rax	# pass key schedule
+	mov	%edx, %r10d		# pass rounds
+
+	call	_bsaes_decrypt8
+
+	pxor	0x00(%rsp), @XMM[0]	# ^= tweak[]
+	pxor	0x10(%rsp), @XMM[1]
+	movdqu	@XMM[0], 0x00($out)	# write output
+	pxor	0x20(%rsp), @XMM[6]
+	movdqu	@XMM[1], 0x10($out)
+	pxor	0x30(%rsp), @XMM[4]
+	movdqu	@XMM[6], 0x20($out)
+	movdqu	@XMM[4], 0x30($out)
+	lea	0x40($out), $out
+
+	movdqa	0x40(%rsp), @XMM[7]	# next iteration tweak
+	jmp	.Lxts_dec_done
+.align	16
+.Lxts_dec_3:
+	pxor	@XMM[8+1], @XMM[1]
+	lea	0x30($inp), $inp
+	pxor	@XMM[8+2], @XMM[2]
+	lea	0x80(%rsp), %rax	# pass key schedule
+	mov	%edx, %r10d		# pass rounds
+
+	call	_bsaes_decrypt8
+
+	pxor	0x00(%rsp), @XMM[0]	# ^= tweak[]
+	pxor	0x10(%rsp), @XMM[1]
+	movdqu	@XMM[0], 0x00($out)	# write output
+	pxor	0x20(%rsp), @XMM[6]
+	movdqu	@XMM[1], 0x10($out)
+	movdqu	@XMM[6], 0x20($out)
+	lea	0x30($out), $out
+
+	movdqa	0x30(%rsp), @XMM[7]	# next iteration tweak
+	jmp	.Lxts_dec_done
+.align	16
+.Lxts_dec_2:
+	pxor	@XMM[8+0], @XMM[0]
+	lea	0x20($inp), $inp
+	pxor	@XMM[8+1], @XMM[1]
+	lea	0x80(%rsp), %rax	# pass key schedule
+	mov	%edx, %r10d		# pass rounds
+
+	call	_bsaes_decrypt8
+
+	pxor	0x00(%rsp), @XMM[0]	# ^= tweak[]
+	pxor	0x10(%rsp), @XMM[1]
+	movdqu	@XMM[0], 0x00($out)	# write output
+	movdqu	@XMM[1], 0x10($out)
+	lea	0x20($out), $out
+
+	movdqa	0x20(%rsp), @XMM[7]	# next iteration tweak
+	jmp	.Lxts_dec_done
+.align	16
+.Lxts_dec_1:
+	pxor	@XMM[0], @XMM[8]
+	lea	0x10($inp), $inp
+	movdqa	@XMM[8], 0x20(%rbp)
+	lea	0x20(%rbp), $arg1
+	lea	0x20(%rbp), $arg2
+	lea	($key), $arg3
+	call	asm_AES_decrypt		# doesn't touch %xmm
+	pxor	0x20(%rbp), @XMM[0]	# ^= tweak[]
+	#pxor	@XMM[8], @XMM[0]
+	#lea	0x80(%rsp), %rax	# pass key schedule
+	#mov	%edx, %r10d		# pass rounds
+	#call	_bsaes_decrypt8
+	#pxor	0x00(%rsp), @XMM[0]	# ^= tweak[]
+	movdqu	@XMM[0], 0x00($out)	# write output
+	lea	0x10($out), $out
+
+	movdqa	0x10(%rsp), @XMM[7]	# next iteration tweak
+
+.Lxts_dec_done:
+	and	\$15, %ebx
+	jz	.Lxts_dec_ret
+
+	pxor	$twtmp, $twtmp
+	movdqa	.Lxts_magic(%rip), $twmask
+	pcmpgtd	@XMM[7], $twtmp
+	pshufd	\$0x13, $twtmp, $twres
+	movdqa	@XMM[7], @XMM[6]
+	paddq	@XMM[7], @XMM[7]	# psllq 1,$tweak
+	pand	$twmask, $twres		# isolate carry and residue
+	movdqu	($inp), @XMM[0]
+	pxor	$twres, @XMM[7]
+
+	lea	0x20(%rbp), $arg1
+	pxor	@XMM[7], @XMM[0]
+	lea	0x20(%rbp), $arg2
+	movdqa	@XMM[0], 0x20(%rbp)
+	lea	($key), $arg3
+	call	asm_AES_decrypt		# doesn't touch %xmm
+	pxor	0x20(%rbp), @XMM[7]
+	mov	$out, %rdx
+	movdqu	@XMM[7], ($out)
+
+.Lxts_dec_steal:
+	movzb	16($inp), %eax
+	movzb	(%rdx), %ecx
+	lea	1($inp), $inp
+	mov	%al, (%rdx)
+	mov	%cl, 16(%rdx)
+	lea	1(%rdx), %rdx
+	sub	\$1,%ebx
+	jnz	.Lxts_dec_steal
+
+	movdqu	($out), @XMM[0]
+	lea	0x20(%rbp), $arg1
+	pxor	@XMM[6], @XMM[0]
+	lea	0x20(%rbp), $arg2
+	movdqa	@XMM[0], 0x20(%rbp)
+	lea	($key), $arg3
+	call	asm_AES_decrypt		# doesn't touch %xmm
+	pxor	0x20(%rbp), @XMM[6]
+	movdqu	@XMM[6], ($out)
+
+.Lxts_dec_ret:
+	lea	(%rsp), %rax
+	pxor	%xmm0, %xmm0
+.Lxts_dec_bzero:			# wipe key schedule [if any]
+	movdqa	%xmm0, 0x00(%rax)
+	movdqa	%xmm0, 0x10(%rax)
+	lea	0x20(%rax), %rax
+	cmp	%rax, %rbp
+	ja	.Lxts_dec_bzero
+
+	lea	(%rbp),%rsp		# restore %rsp
+___
+$code.=<<___ if ($win64);
+	movaps	0x40(%rbp), %xmm6
+	movaps	0x50(%rbp), %xmm7
+	movaps	0x60(%rbp), %xmm8
+	movaps	0x70(%rbp), %xmm9
+	movaps	0x80(%rbp), %xmm10
+	movaps	0x90(%rbp), %xmm11
+	movaps	0xa0(%rbp), %xmm12
+	movaps	0xb0(%rbp), %xmm13
+	movaps	0xc0(%rbp), %xmm14
+	movaps	0xd0(%rbp), %xmm15
+	lea	0xa0(%rbp), %rsp
+___
+$code.=<<___;
+	mov	0x48(%rsp), %r15
+	mov	0x50(%rsp), %r14
+	mov	0x58(%rsp), %r13
+	mov	0x60(%rsp), %r12
+	mov	0x68(%rsp), %rbx
+	mov	0x70(%rsp), %rax
+	lea	0x78(%rsp), %rsp
+	mov	%rax, %rbp
+.Lxts_dec_epilogue:
+	ret
+.size	bsaes_xts_decrypt,.-bsaes_xts_decrypt
+___
+}
+$code.=<<___;
+.type	_bsaes_const,\@object
+.align	64
+_bsaes_const:
+.LM0ISR:	# InvShiftRows constants
+	.quad	0x0a0e0206070b0f03, 0x0004080c0d010509
+.LISRM0:
+	.quad	0x01040b0e0205080f, 0x0306090c00070a0d
+.LISR:
+	.quad	0x0504070602010003, 0x0f0e0d0c080b0a09
+.LBS0:		# bit-slice constants
+	.quad	0x5555555555555555, 0x5555555555555555
+.LBS1:
+	.quad	0x3333333333333333, 0x3333333333333333
+.LBS2:
+	.quad	0x0f0f0f0f0f0f0f0f, 0x0f0f0f0f0f0f0f0f
+.LSR:		# shiftrows constants
+	.quad	0x0504070600030201, 0x0f0e0d0c0a09080b
+.LSRM0:
+	.quad	0x0304090e00050a0f, 0x01060b0c0207080d
+.LM0SR:
+	.quad	0x0a0e02060f03070b, 0x0004080c05090d01
+.LSWPUP:	# byte-swap upper dword
+	.quad	0x0706050403020100, 0x0c0d0e0f0b0a0908
+.LSWPUPM0SR:
+	.quad	0x0a0d02060c03070b, 0x0004080f05090e01
+.LADD1:		# counter increment constants
+	.quad	0x0000000000000000, 0x0000000100000000
+.LADD2:
+	.quad	0x0000000000000000, 0x0000000200000000
+.LADD3:
+	.quad	0x0000000000000000, 0x0000000300000000
+.LADD4:
+	.quad	0x0000000000000000, 0x0000000400000000
+.LADD5:
+	.quad	0x0000000000000000, 0x0000000500000000
+.LADD6:
+	.quad	0x0000000000000000, 0x0000000600000000
+.LADD7:
+	.quad	0x0000000000000000, 0x0000000700000000
+.LADD8:
+	.quad	0x0000000000000000, 0x0000000800000000
+.Lxts_magic:
+	.long	0x87,0,1,0
+.Lmasks:
+	.quad	0x0101010101010101, 0x0101010101010101
+	.quad	0x0202020202020202, 0x0202020202020202
+	.quad	0x0404040404040404, 0x0404040404040404
+	.quad	0x0808080808080808, 0x0808080808080808
+.LM0:
+	.quad	0x02060a0e03070b0f, 0x0004080c0105090d
+.L63:
+	.quad	0x6363636363636363, 0x6363636363636363
+.asciz	"Bit-sliced AES for x86_64/SSSE3, Emilia Käsper, Peter Schwabe, Andy Polyakov"
+.align	64
+.size	_bsaes_const,.-_bsaes_const
+___
+
+# EXCEPTION_DISPOSITION handler (EXCEPTION_RECORD *rec,ULONG64 frame,
+#		CONTEXT *context,DISPATCHER_CONTEXT *disp)
+if ($win64) {
+$rec="%rcx";
+$frame="%rdx";
+$context="%r8";
+$disp="%r9";
+
+$code.=<<___;
+.extern	__imp_RtlVirtualUnwind
+.type	se_handler,\@abi-omnipotent
+.align	16
+se_handler:
+	push	%rsi
+	push	%rdi
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+	pushfq
+	sub	\$64,%rsp
+
+	mov	120($context),%rax	# pull context->Rax
+	mov	248($context),%rbx	# pull context->Rip
+
+	mov	8($disp),%rsi		# disp->ImageBase
+	mov	56($disp),%r11		# disp->HandlerData
+
+	mov	0(%r11),%r10d		# HandlerData[0]
+	lea	(%rsi,%r10),%r10	# prologue label
+	cmp	%r10,%rbx		# context->RipRsp
+
+	mov	4(%r11),%r10d		# HandlerData[1]
+	lea	(%rsi,%r10),%r10	# epilogue label
+	cmp	%r10,%rbx		# context->Rip>=epilogue label
+	jae	.Lin_prologue
+
+	mov	160($context),%rax	# pull context->Rbp
+
+	lea	0x40(%rax),%rsi		# %xmm save area
+	lea	512($context),%rdi	# &context.Xmm6
+	mov	\$20,%ecx		# 10*sizeof(%xmm0)/sizeof(%rax)
+	.long	0xa548f3fc		# cld; rep movsq
+	lea	0xa0(%rax),%rax		# adjust stack pointer
+
+	mov	0x70(%rax),%rbp
+	mov	0x68(%rax),%rbx
+	mov	0x60(%rax),%r12
+	mov	0x58(%rax),%r13
+	mov	0x50(%rax),%r14
+	mov	0x48(%rax),%r15
+	lea	0x78(%rax),%rax		# adjust stack pointer
+	mov	%rbx,144($context)	# restore context->Rbx
+	mov	%rbp,160($context)	# restore context->Rbp
+	mov	%r12,216($context)	# restore context->R12
+	mov	%r13,224($context)	# restore context->R13
+	mov	%r14,232($context)	# restore context->R14
+	mov	%r15,240($context)	# restore context->R15
+
+.Lin_prologue:
+	mov	%rax,152($context)	# restore context->Rsp
+
+	mov	40($disp),%rdi		# disp->ContextRecord
+	mov	$context,%rsi		# context
+	mov	\$`1232/8`,%ecx		# sizeof(CONTEXT)
+	.long	0xa548f3fc		# cld; rep movsq
+
+	mov	$disp,%rsi
+	xor	%rcx,%rcx		# arg1, UNW_FLAG_NHANDLER
+	mov	8(%rsi),%rdx		# arg2, disp->ImageBase
+	mov	0(%rsi),%r8		# arg3, disp->ControlPc
+	mov	16(%rsi),%r9		# arg4, disp->FunctionEntry
+	mov	40(%rsi),%r10		# disp->ContextRecord
+	lea	56(%rsi),%r11		# &disp->HandlerData
+	lea	24(%rsi),%r12		# &disp->EstablisherFrame
+	mov	%r10,32(%rsp)		# arg5
+	mov	%r11,40(%rsp)		# arg6
+	mov	%r12,48(%rsp)		# arg7
+	mov	%rcx,56(%rsp)		# arg8, (NULL)
+	call	*__imp_RtlVirtualUnwind(%rip)
+
+	mov	\$1,%eax		# ExceptionContinueSearch
+	add	\$64,%rsp
+	popfq
+	pop	%r15
+	pop	%r14
+	pop	%r13
+	pop	%r12
+	pop	%rbp
+	pop	%rbx
+	pop	%rdi
+	pop	%rsi
+	ret
+.size	se_handler,.-se_handler
+
+.section	.pdata
+.align	4
+___
+$code.=<<___ if ($ecb);
+	.rva	.Lecb_enc_prologue
+	.rva	.Lecb_enc_epilogue
+	.rva	.Lecb_enc_info
+
+	.rva	.Lecb_dec_prologue
+	.rva	.Lecb_dec_epilogue
+	.rva	.Lecb_dec_info
+___
+$code.=<<___;
+	.rva	.Lcbc_dec_prologue
+	.rva	.Lcbc_dec_epilogue
+	.rva	.Lcbc_dec_info
+
+	.rva	.Lctr_enc_prologue
+	.rva	.Lctr_enc_epilogue
+	.rva	.Lctr_enc_info
+
+	.rva	.Lxts_enc_prologue
+	.rva	.Lxts_enc_epilogue
+	.rva	.Lxts_enc_info
+
+	.rva	.Lxts_dec_prologue
+	.rva	.Lxts_dec_epilogue
+	.rva	.Lxts_dec_info
+
+.section	.xdata
+.align	8
+___
+$code.=<<___ if ($ecb);
+.Lecb_enc_info:
+	.byte	9,0,0,0
+	.rva	se_handler
+	.rva	.Lecb_enc_body,.Lecb_enc_epilogue	# HandlerData[]
+.Lecb_dec_info:
+	.byte	9,0,0,0
+	.rva	se_handler
+	.rva	.Lecb_dec_body,.Lecb_dec_epilogue	# HandlerData[]
+___
+$code.=<<___;
+.Lcbc_dec_info:
+	.byte	9,0,0,0
+	.rva	se_handler
+	.rva	.Lcbc_dec_body,.Lcbc_dec_epilogue	# HandlerData[]
+.Lctr_enc_info:
+	.byte	9,0,0,0
+	.rva	se_handler
+	.rva	.Lctr_enc_body,.Lctr_enc_epilogue	# HandlerData[]
+.Lxts_enc_info:
+	.byte	9,0,0,0
+	.rva	se_handler
+	.rva	.Lxts_enc_body,.Lxts_enc_epilogue	# HandlerData[]
+.Lxts_dec_info:
+	.byte	9,0,0,0
+	.rva	se_handler
+	.rva	.Lxts_dec_body,.Lxts_dec_epilogue	# HandlerData[]
+___
+}
+
+$code =~ s/\`([^\`]*)\`/eval($1)/gem;
+
+print $code;
+
+close STDOUT;
diff --git a/crypto/aes/asm/vpaes-armv8.pl b/crypto/aes/asm/vpaes-armv8.pl
new file mode 100755
index 000000000..d6b5f561c
--- /dev/null
+++ b/crypto/aes/asm/vpaes-armv8.pl
@@ -0,0 +1,1259 @@
+#! /usr/bin/env perl
+# Copyright 2015-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+
+######################################################################
+## Constant-time SSSE3 AES core implementation.
+## version 0.1
+##
+## By Mike Hamburg (Stanford University), 2009
+## Public domain.
+##
+## For details see http://shiftleft.org/papers/vector_aes/ and
+## http://crypto.stanford.edu/vpaes/.
+##
+######################################################################
+# ARMv8 NEON adaptation by 
+#
+# Reason for undertaken effort is that there is at least one popular
+# SoC based on Cortex-A53 that doesn't have crypto extensions.
+#
+#                   CBC enc     ECB enc/dec(*)   [bit-sliced enc/dec]
+# Cortex-A53        21.5        18.1/20.6        [17.5/19.8         ]
+# Cortex-A57        36.0(**)    20.4/24.9(**)    [14.4/16.6         ]
+# X-Gene            45.9(**)    45.8/57.7(**)    [33.1/37.6(**)     ]
+# Denver(***)       16.6(**)    15.1/17.8(**)    [8.80/9.93         ]
+# Apple A7(***)     22.7(**)    10.9/14.3        [8.45/10.0         ]
+# Mongoose(***)     26.3(**)    21.0/25.0(**)    [13.3/16.8         ]
+#
+# (*)	ECB denotes approximate result for parallelizeable modes
+#	such as CBC decrypt, CTR, etc.;
+# (**)	these results are worse than scalar compiler-generated
+#	code, but it's constant-time and therefore preferred;
+# (***)	presented for reference/comparison purposes;
+
+$flavour = shift;
+while (($output=shift) && ($output!~/\w[\w\-]*\.\w+$/)) {}
+
+$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+( $xlate="${dir}arm-xlate.pl" and -f $xlate ) or
+( $xlate="${dir}../../perlasm/arm-xlate.pl" and -f $xlate) or
+die "can't locate arm-xlate.pl";
+
+open OUT,"| \"$^X\" $xlate $flavour $output";
+*STDOUT=*OUT;
+
+$code.=<<___;
+.text
+
+.type	_vpaes_consts,%object
+.align	7	// totally strategic alignment
+_vpaes_consts:
+.Lk_mc_forward:	// mc_forward
+	.quad	0x0407060500030201, 0x0C0F0E0D080B0A09
+	.quad	0x080B0A0904070605, 0x000302010C0F0E0D
+	.quad	0x0C0F0E0D080B0A09, 0x0407060500030201
+	.quad	0x000302010C0F0E0D, 0x080B0A0904070605
+.Lk_mc_backward:// mc_backward
+	.quad	0x0605040702010003, 0x0E0D0C0F0A09080B
+	.quad	0x020100030E0D0C0F, 0x0A09080B06050407
+	.quad	0x0E0D0C0F0A09080B, 0x0605040702010003
+	.quad	0x0A09080B06050407, 0x020100030E0D0C0F
+.Lk_sr:		// sr
+	.quad	0x0706050403020100, 0x0F0E0D0C0B0A0908
+	.quad	0x030E09040F0A0500, 0x0B06010C07020D08
+	.quad	0x0F060D040B020900, 0x070E050C030A0108
+	.quad	0x0B0E0104070A0D00, 0x0306090C0F020508
+
+//
+// "Hot" constants
+//
+.Lk_inv:	// inv, inva
+	.quad	0x0E05060F0D080180, 0x040703090A0B0C02
+	.quad	0x01040A060F0B0780, 0x030D0E0C02050809
+.Lk_ipt:	// input transform (lo, hi)
+	.quad	0xC2B2E8985A2A7000, 0xCABAE09052227808
+	.quad	0x4C01307D317C4D00, 0xCD80B1FCB0FDCC81
+.Lk_sbo:	// sbou, sbot
+	.quad	0xD0D26D176FBDC700, 0x15AABF7AC502A878
+	.quad	0xCFE474A55FBB6A00, 0x8E1E90D1412B35FA
+.Lk_sb1:	// sb1u, sb1t
+	.quad	0x3618D415FAE22300, 0x3BF7CCC10D2ED9EF
+	.quad	0xB19BE18FCB503E00, 0xA5DF7A6E142AF544
+.Lk_sb2:	// sb2u, sb2t
+	.quad	0x69EB88400AE12900, 0xC2A163C8AB82234A
+	.quad	0xE27A93C60B712400, 0x5EB7E955BC982FCD
+
+//
+//  Decryption stuff
+//
+.Lk_dipt:	// decryption input transform
+	.quad	0x0F505B040B545F00, 0x154A411E114E451A
+	.quad	0x86E383E660056500, 0x12771772F491F194
+.Lk_dsbo:	// decryption sbox final output
+	.quad	0x1387EA537EF94000, 0xC7AA6DB9D4943E2D
+	.quad	0x12D7560F93441D00, 0xCA4B8159D8C58E9C
+.Lk_dsb9:	// decryption sbox output *9*u, *9*t
+	.quad	0x851C03539A86D600, 0xCAD51F504F994CC9
+	.quad	0xC03B1789ECD74900, 0x725E2C9EB2FBA565
+.Lk_dsbd:	// decryption sbox output *D*u, *D*t
+	.quad	0x7D57CCDFE6B1A200, 0xF56E9B13882A4439
+	.quad	0x3CE2FAF724C6CB00, 0x2931180D15DEEFD3
+.Lk_dsbb:	// decryption sbox output *B*u, *B*t
+	.quad	0xD022649296B44200, 0x602646F6B0F2D404
+	.quad	0xC19498A6CD596700, 0xF3FF0C3E3255AA6B
+.Lk_dsbe:	// decryption sbox output *E*u, *E*t
+	.quad	0x46F2929626D4D000, 0x2242600464B4F6B0
+	.quad	0x0C55A6CDFFAAC100, 0x9467F36B98593E32
+
+//
+//  Key schedule constants
+//
+.Lk_dksd:	// decryption key schedule: invskew x*D
+	.quad	0xFEB91A5DA3E44700, 0x0740E3A45A1DBEF9
+	.quad	0x41C277F4B5368300, 0x5FDC69EAAB289D1E
+.Lk_dksb:	// decryption key schedule: invskew x*B
+	.quad	0x9A4FCA1F8550D500, 0x03D653861CC94C99
+	.quad	0x115BEDA7B6FC4A00, 0xD993256F7E3482C8
+.Lk_dkse:	// decryption key schedule: invskew x*E + 0x63
+	.quad	0xD5031CCA1FC9D600, 0x53859A4C994F5086
+	.quad	0xA23196054FDC7BE8, 0xCD5EF96A20B31487
+.Lk_dks9:	// decryption key schedule: invskew x*9
+	.quad	0xB6116FC87ED9A700, 0x4AED933482255BFC
+	.quad	0x4576516227143300, 0x8BB89FACE9DAFDCE
+
+.Lk_rcon:	// rcon
+	.quad	0x1F8391B9AF9DEEB6, 0x702A98084D7C7D81
+
+.Lk_opt:	// output transform
+	.quad	0xFF9F4929D6B66000, 0xF7974121DEBE6808
+	.quad	0x01EDBD5150BCEC00, 0xE10D5DB1B05C0CE0
+.Lk_deskew:	// deskew tables: inverts the sbox's "skew"
+	.quad	0x07E4A34047A4E300, 0x1DFEB95A5DBEF91A
+	.quad	0x5F36B5DC83EA6900, 0x2841C2ABF49D1E77
+
+.asciz  "Vector Permutaion AES for ARMv8, Mike Hamburg (Stanford University)"
+.size	_vpaes_consts,.-_vpaes_consts
+.align	6
+___
+
+{
+my ($inp,$out,$key) = map("x$_",(0..2));
+
+my ($invlo,$invhi,$iptlo,$ipthi,$sbou,$sbot) = map("v$_.16b",(18..23));
+my ($sb1u,$sb1t,$sb2u,$sb2t) = map("v$_.16b",(24..27));
+my ($sb9u,$sb9t,$sbdu,$sbdt,$sbbu,$sbbt,$sbeu,$sbet)=map("v$_.16b",(24..31));
+
+$code.=<<___;
+##
+##  _aes_preheat
+##
+##  Fills register %r10 -> .aes_consts (so you can -fPIC)
+##  and %xmm9-%xmm15 as specified below.
+##
+.type	_vpaes_encrypt_preheat,%function
+.align	4
+_vpaes_encrypt_preheat:
+	adr	x10, .Lk_inv
+	movi	v17.16b, #0x0f
+	ld1	{v18.2d-v19.2d}, [x10],#32	// .Lk_inv
+	ld1	{v20.2d-v23.2d}, [x10],#64	// .Lk_ipt, .Lk_sbo
+	ld1	{v24.2d-v27.2d}, [x10]		// .Lk_sb1, .Lk_sb2
+	ret
+.size	_vpaes_encrypt_preheat,.-_vpaes_encrypt_preheat
+
+##
+##  _aes_encrypt_core
+##
+##  AES-encrypt %xmm0.
+##
+##  Inputs:
+##     %xmm0 = input
+##     %xmm9-%xmm15 as in _vpaes_preheat
+##    (%rdx) = scheduled keys
+##
+##  Output in %xmm0
+##  Clobbers  %xmm1-%xmm5, %r9, %r10, %r11, %rax
+##  Preserves %xmm6 - %xmm8 so you get some local vectors
+##
+##
+.type	_vpaes_encrypt_core,%function
+.align 4
+_vpaes_encrypt_core:
+	mov	x9, $key
+	ldr	w8, [$key,#240]			// pull rounds
+	adr	x11, .Lk_mc_forward+16
+						// vmovdqa	.Lk_ipt(%rip),	%xmm2	# iptlo
+	ld1	{v16.2d}, [x9], #16		// vmovdqu	(%r9),	%xmm5		# round0 key
+	and	v1.16b, v7.16b, v17.16b		// vpand	%xmm9,	%xmm0,	%xmm1
+	ushr	v0.16b, v7.16b, #4		// vpsrlb	\$4,	%xmm0,	%xmm0
+	tbl	v1.16b, {$iptlo}, v1.16b	// vpshufb	%xmm1,	%xmm2,	%xmm1
+						// vmovdqa	.Lk_ipt+16(%rip), %xmm3	# ipthi
+	tbl	v2.16b, {$ipthi}, v0.16b	// vpshufb	%xmm0,	%xmm3,	%xmm2
+	eor	v0.16b, v1.16b, v16.16b		// vpxor	%xmm5,	%xmm1,	%xmm0
+	eor	v0.16b, v0.16b, v2.16b		// vpxor	%xmm2,	%xmm0,	%xmm0
+	b	.Lenc_entry
+
+.align 4
+.Lenc_loop:
+	// middle of middle round
+	add	x10, x11, #0x40
+	tbl	v4.16b, {$sb1t}, v2.16b		// vpshufb	%xmm2,	%xmm13,	%xmm4	# 4 = sb1u
+	ld1	{v1.2d}, [x11], #16		// vmovdqa	-0x40(%r11,%r10), %xmm1	# .Lk_mc_forward[]
+	tbl	v0.16b, {$sb1u}, v3.16b		// vpshufb	%xmm3,	%xmm12,	%xmm0	# 0 = sb1t
+	eor	v4.16b, v4.16b, v16.16b		// vpxor	%xmm5,	%xmm4,	%xmm4	# 4 = sb1u + k
+	tbl	v5.16b,	{$sb2t}, v2.16b		// vpshufb	%xmm2,	%xmm15,	%xmm5	# 4 = sb2u
+	eor	v0.16b, v0.16b, v4.16b		// vpxor	%xmm4,	%xmm0,	%xmm0	# 0 = A
+	tbl	v2.16b, {$sb2u}, v3.16b		// vpshufb	%xmm3,	%xmm14,	%xmm2	# 2 = sb2t
+	ld1	{v4.2d}, [x10]			// vmovdqa	(%r11,%r10), %xmm4	# .Lk_mc_backward[]
+	tbl	v3.16b, {v0.16b}, v1.16b	// vpshufb	%xmm1,	%xmm0,	%xmm3	# 0 = B
+	eor	v2.16b, v2.16b, v5.16b		// vpxor	%xmm5,	%xmm2,	%xmm2	# 2 = 2A
+	tbl	v0.16b, {v0.16b}, v4.16b	// vpshufb	%xmm4,	%xmm0,	%xmm0	# 3 = D
+	eor	v3.16b, v3.16b, v2.16b		// vpxor	%xmm2,	%xmm3,	%xmm3	# 0 = 2A+B
+	tbl	v4.16b, {v3.16b}, v1.16b	// vpshufb	%xmm1,	%xmm3,	%xmm4	# 0 = 2B+C
+	eor	v0.16b, v0.16b, v3.16b		// vpxor	%xmm3,	%xmm0,	%xmm0	# 3 = 2A+B+D
+	and	x11, x11, #~(1<<6)		// and		\$0x30,	%r11		# ... mod 4
+	eor	v0.16b, v0.16b, v4.16b		// vpxor	%xmm4,	%xmm0, %xmm0	# 0 = 2A+3B+C+D
+	sub	w8, w8, #1			// nr--
+
+.Lenc_entry:
+	// top of round
+	and	v1.16b, v0.16b, v17.16b		// vpand	%xmm0,	%xmm9,	%xmm1   # 0 = k
+	ushr	v0.16b, v0.16b, #4		// vpsrlb	\$4,	%xmm0,	%xmm0	# 1 = i
+	tbl	v5.16b, {$invhi}, v1.16b	// vpshufb	%xmm1,	%xmm11,	%xmm5	# 2 = a/k
+	eor	v1.16b, v1.16b, v0.16b		// vpxor	%xmm0,	%xmm1,	%xmm1	# 0 = j
+	tbl	v3.16b, {$invlo}, v0.16b	// vpshufb	%xmm0, 	%xmm10,	%xmm3  	# 3 = 1/i
+	tbl	v4.16b, {$invlo}, v1.16b	// vpshufb	%xmm1, 	%xmm10,	%xmm4  	# 4 = 1/j
+	eor	v3.16b, v3.16b, v5.16b		// vpxor	%xmm5,	%xmm3,	%xmm3	# 3 = iak = 1/i + a/k
+	eor	v4.16b, v4.16b, v5.16b		// vpxor	%xmm5,	%xmm4,	%xmm4  	# 4 = jak = 1/j + a/k
+	tbl	v2.16b, {$invlo}, v3.16b	// vpshufb	%xmm3,	%xmm10,	%xmm2  	# 2 = 1/iak
+	tbl	v3.16b, {$invlo}, v4.16b	// vpshufb	%xmm4,	%xmm10,	%xmm3	# 3 = 1/jak
+	eor	v2.16b, v2.16b, v1.16b		// vpxor	%xmm1,	%xmm2,	%xmm2  	# 2 = io
+	eor	v3.16b, v3.16b, v0.16b		// vpxor	%xmm0,	%xmm3,	%xmm3	# 3 = jo
+	ld1	{v16.2d}, [x9],#16		// vmovdqu	(%r9),	%xmm5
+	cbnz	w8, .Lenc_loop
+
+	// middle of last round
+	add	x10, x11, #0x80
+						// vmovdqa	-0x60(%r10), %xmm4	# 3 : sbou	.Lk_sbo
+						// vmovdqa	-0x50(%r10), %xmm0	# 0 : sbot	.Lk_sbo+16
+	tbl	v4.16b, {$sbou}, v2.16b		// vpshufb	%xmm2,	%xmm4,	%xmm4	# 4 = sbou
+	ld1	{v1.2d}, [x10]			// vmovdqa	0x40(%r11,%r10), %xmm1	# .Lk_sr[]
+	tbl	v0.16b, {$sbot}, v3.16b		// vpshufb	%xmm3,	%xmm0,	%xmm0	# 0 = sb1t
+	eor	v4.16b, v4.16b, v16.16b		// vpxor	%xmm5,	%xmm4,	%xmm4	# 4 = sb1u + k
+	eor	v0.16b, v0.16b, v4.16b		// vpxor	%xmm4,	%xmm0,	%xmm0	# 0 = A
+	tbl	v0.16b, {v0.16b}, v1.16b	// vpshufb	%xmm1,	%xmm0,	%xmm0
+	ret
+.size	_vpaes_encrypt_core,.-_vpaes_encrypt_core
+
+.globl	vpaes_encrypt
+.type	vpaes_encrypt,%function
+.align	4
+vpaes_encrypt:
+	stp	x29,x30,[sp,#-16]!
+	add	x29,sp,#0
+
+	ld1	{v7.16b}, [$inp]
+	bl	_vpaes_encrypt_preheat
+	bl	_vpaes_encrypt_core
+	st1	{v0.16b}, [$out]
+
+	ldp	x29,x30,[sp],#16
+	ret
+.size	vpaes_encrypt,.-vpaes_encrypt
+
+.type	_vpaes_encrypt_2x,%function
+.align 4
+_vpaes_encrypt_2x:
+	mov	x9, $key
+	ldr	w8, [$key,#240]			// pull rounds
+	adr	x11, .Lk_mc_forward+16
+						// vmovdqa	.Lk_ipt(%rip),	%xmm2	# iptlo
+	ld1	{v16.2d}, [x9], #16		// vmovdqu	(%r9),	%xmm5		# round0 key
+	and	v1.16b,  v14.16b,  v17.16b	// vpand	%xmm9,	%xmm0,	%xmm1
+	ushr	v0.16b,  v14.16b,  #4		// vpsrlb	\$4,	%xmm0,	%xmm0
+	 and	v9.16b,  v15.16b,  v17.16b
+	 ushr	v8.16b,  v15.16b,  #4
+	tbl	v1.16b,  {$iptlo}, v1.16b	// vpshufb	%xmm1,	%xmm2,	%xmm1
+	 tbl	v9.16b,  {$iptlo}, v9.16b
+						// vmovdqa	.Lk_ipt+16(%rip), %xmm3	# ipthi
+	tbl	v2.16b,  {$ipthi}, v0.16b	// vpshufb	%xmm0,	%xmm3,	%xmm2
+	 tbl	v10.16b, {$ipthi}, v8.16b
+	eor	v0.16b,  v1.16b,   v16.16b	// vpxor	%xmm5,	%xmm1,	%xmm0
+	 eor	v8.16b,  v9.16b,   v16.16b
+	eor	v0.16b,  v0.16b,   v2.16b	// vpxor	%xmm2,	%xmm0,	%xmm0
+	 eor	v8.16b,  v8.16b,   v10.16b
+	b	.Lenc_2x_entry
+
+.align 4
+.Lenc_2x_loop:
+	// middle of middle round
+	add	x10, x11, #0x40
+	tbl	v4.16b,  {$sb1t}, v2.16b	// vpshufb	%xmm2,	%xmm13,	%xmm4	# 4 = sb1u
+	 tbl	v12.16b, {$sb1t}, v10.16b
+	ld1	{v1.2d}, [x11], #16		// vmovdqa	-0x40(%r11,%r10), %xmm1	# .Lk_mc_forward[]
+	tbl	v0.16b,  {$sb1u}, v3.16b	// vpshufb	%xmm3,	%xmm12,	%xmm0	# 0 = sb1t
+	 tbl	v8.16b,  {$sb1u}, v11.16b
+	eor	v4.16b,  v4.16b,  v16.16b	// vpxor	%xmm5,	%xmm4,	%xmm4	# 4 = sb1u + k
+	 eor	v12.16b, v12.16b, v16.16b
+	tbl	v5.16b,	 {$sb2t}, v2.16b	// vpshufb	%xmm2,	%xmm15,	%xmm5	# 4 = sb2u
+	 tbl	v13.16b, {$sb2t}, v10.16b
+	eor	v0.16b,  v0.16b,  v4.16b	// vpxor	%xmm4,	%xmm0,	%xmm0	# 0 = A
+	 eor	v8.16b,  v8.16b,  v12.16b
+	tbl	v2.16b,  {$sb2u}, v3.16b	// vpshufb	%xmm3,	%xmm14,	%xmm2	# 2 = sb2t
+	 tbl	v10.16b, {$sb2u}, v11.16b
+	ld1	{v4.2d}, [x10]			// vmovdqa	(%r11,%r10), %xmm4	# .Lk_mc_backward[]
+	tbl	v3.16b,  {v0.16b}, v1.16b	// vpshufb	%xmm1,	%xmm0,	%xmm3	# 0 = B
+	 tbl	v11.16b, {v8.16b}, v1.16b
+	eor	v2.16b,  v2.16b,  v5.16b	// vpxor	%xmm5,	%xmm2,	%xmm2	# 2 = 2A
+	 eor	v10.16b, v10.16b, v13.16b
+	tbl	v0.16b,  {v0.16b}, v4.16b	// vpshufb	%xmm4,	%xmm0,	%xmm0	# 3 = D
+	 tbl	v8.16b,  {v8.16b}, v4.16b
+	eor	v3.16b,  v3.16b,  v2.16b	// vpxor	%xmm2,	%xmm3,	%xmm3	# 0 = 2A+B
+	 eor	v11.16b, v11.16b, v10.16b
+	tbl	v4.16b,  {v3.16b}, v1.16b	// vpshufb	%xmm1,	%xmm3,	%xmm4	# 0 = 2B+C
+	 tbl	v12.16b, {v11.16b},v1.16b
+	eor	v0.16b,  v0.16b,  v3.16b	// vpxor	%xmm3,	%xmm0,	%xmm0	# 3 = 2A+B+D
+	 eor	v8.16b,  v8.16b,  v11.16b
+	and	x11, x11, #~(1<<6)		// and		\$0x30,	%r11		# ... mod 4
+	eor	v0.16b,  v0.16b,  v4.16b	// vpxor	%xmm4,	%xmm0, %xmm0	# 0 = 2A+3B+C+D
+	 eor	v8.16b,  v8.16b,  v12.16b
+	sub	w8, w8, #1			// nr--
+
+.Lenc_2x_entry:
+	// top of round
+	and	v1.16b,  v0.16b, v17.16b	// vpand	%xmm0,	%xmm9,	%xmm1   # 0 = k
+	ushr	v0.16b,  v0.16b, #4		// vpsrlb	\$4,	%xmm0,	%xmm0	# 1 = i
+	 and	v9.16b,  v8.16b, v17.16b
+	 ushr	v8.16b,  v8.16b, #4
+	tbl	v5.16b,  {$invhi},v1.16b	// vpshufb	%xmm1,	%xmm11,	%xmm5	# 2 = a/k
+	 tbl	v13.16b, {$invhi},v9.16b
+	eor	v1.16b,  v1.16b,  v0.16b	// vpxor	%xmm0,	%xmm1,	%xmm1	# 0 = j
+	 eor	v9.16b,  v9.16b,  v8.16b
+	tbl	v3.16b,  {$invlo},v0.16b	// vpshufb	%xmm0, 	%xmm10,	%xmm3  	# 3 = 1/i
+	 tbl	v11.16b, {$invlo},v8.16b
+	tbl	v4.16b,  {$invlo},v1.16b	// vpshufb	%xmm1, 	%xmm10,	%xmm4  	# 4 = 1/j
+	 tbl	v12.16b, {$invlo},v9.16b
+	eor	v3.16b,  v3.16b,  v5.16b	// vpxor	%xmm5,	%xmm3,	%xmm3	# 3 = iak = 1/i + a/k
+	 eor	v11.16b, v11.16b, v13.16b
+	eor	v4.16b,  v4.16b,  v5.16b	// vpxor	%xmm5,	%xmm4,	%xmm4  	# 4 = jak = 1/j + a/k
+	 eor	v12.16b, v12.16b, v13.16b
+	tbl	v2.16b,  {$invlo},v3.16b	// vpshufb	%xmm3,	%xmm10,	%xmm2  	# 2 = 1/iak
+	 tbl	v10.16b, {$invlo},v11.16b
+	tbl	v3.16b,  {$invlo},v4.16b	// vpshufb	%xmm4,	%xmm10,	%xmm3	# 3 = 1/jak
+	 tbl	v11.16b, {$invlo},v12.16b
+	eor	v2.16b,  v2.16b,  v1.16b	// vpxor	%xmm1,	%xmm2,	%xmm2  	# 2 = io
+	 eor	v10.16b, v10.16b, v9.16b
+	eor	v3.16b,  v3.16b,  v0.16b	// vpxor	%xmm0,	%xmm3,	%xmm3	# 3 = jo
+	 eor	v11.16b, v11.16b, v8.16b
+	ld1	{v16.2d}, [x9],#16		// vmovdqu	(%r9),	%xmm5
+	cbnz	w8, .Lenc_2x_loop
+
+	// middle of last round
+	add	x10, x11, #0x80
+						// vmovdqa	-0x60(%r10), %xmm4	# 3 : sbou	.Lk_sbo
+						// vmovdqa	-0x50(%r10), %xmm0	# 0 : sbot	.Lk_sbo+16
+	tbl	v4.16b,  {$sbou}, v2.16b	// vpshufb	%xmm2,	%xmm4,	%xmm4	# 4 = sbou
+	 tbl	v12.16b, {$sbou}, v10.16b
+	ld1	{v1.2d}, [x10]			// vmovdqa	0x40(%r11,%r10), %xmm1	# .Lk_sr[]
+	tbl	v0.16b,  {$sbot}, v3.16b	// vpshufb	%xmm3,	%xmm0,	%xmm0	# 0 = sb1t
+	 tbl	v8.16b,  {$sbot}, v11.16b
+	eor	v4.16b,  v4.16b,  v16.16b	// vpxor	%xmm5,	%xmm4,	%xmm4	# 4 = sb1u + k
+	 eor	v12.16b, v12.16b, v16.16b
+	eor	v0.16b,  v0.16b,  v4.16b	// vpxor	%xmm4,	%xmm0,	%xmm0	# 0 = A
+	 eor	v8.16b,  v8.16b,  v12.16b
+	tbl	v0.16b,  {v0.16b},v1.16b	// vpshufb	%xmm1,	%xmm0,	%xmm0
+	 tbl	v1.16b,  {v8.16b},v1.16b
+	ret
+.size	_vpaes_encrypt_2x,.-_vpaes_encrypt_2x
+
+.type	_vpaes_decrypt_preheat,%function
+.align	4
+_vpaes_decrypt_preheat:
+	adr	x10, .Lk_inv
+	movi	v17.16b, #0x0f
+	adr	x11, .Lk_dipt
+	ld1	{v18.2d-v19.2d}, [x10],#32	// .Lk_inv
+	ld1	{v20.2d-v23.2d}, [x11],#64	// .Lk_dipt, .Lk_dsbo
+	ld1	{v24.2d-v27.2d}, [x11],#64	// .Lk_dsb9, .Lk_dsbd
+	ld1	{v28.2d-v31.2d}, [x11]		// .Lk_dsbb, .Lk_dsbe
+	ret
+.size	_vpaes_decrypt_preheat,.-_vpaes_decrypt_preheat
+
+##
+##  Decryption core
+##
+##  Same API as encryption core.
+##
+.type	_vpaes_decrypt_core,%function
+.align	4
+_vpaes_decrypt_core:
+	mov	x9, $key
+	ldr	w8, [$key,#240]			// pull rounds
+
+						// vmovdqa	.Lk_dipt(%rip), %xmm2	# iptlo
+	lsl	x11, x8, #4			// mov	%rax,	%r11;	shl	\$4, %r11
+	eor	x11, x11, #0x30			// xor		\$0x30,	%r11
+	adr	x10, .Lk_sr
+	and	x11, x11, #0x30			// and		\$0x30,	%r11
+	add	x11, x11, x10
+	adr	x10, .Lk_mc_forward+48
+
+	ld1	{v16.2d}, [x9],#16		// vmovdqu	(%r9),	%xmm4		# round0 key
+	and	v1.16b, v7.16b, v17.16b		// vpand	%xmm9,	%xmm0,	%xmm1
+	ushr	v0.16b, v7.16b, #4		// vpsrlb	\$4,	%xmm0,	%xmm0
+	tbl	v2.16b, {$iptlo}, v1.16b	// vpshufb	%xmm1,	%xmm2,	%xmm2
+	ld1	{v5.2d}, [x10]			// vmovdqa	.Lk_mc_forward+48(%rip), %xmm5
+						// vmovdqa	.Lk_dipt+16(%rip), %xmm1 # ipthi
+	tbl	v0.16b, {$ipthi}, v0.16b	// vpshufb	%xmm0,	%xmm1,	%xmm0
+	eor	v2.16b, v2.16b, v16.16b		// vpxor	%xmm4,	%xmm2,	%xmm2
+	eor	v0.16b, v0.16b, v2.16b		// vpxor	%xmm2,	%xmm0,	%xmm0
+	b	.Ldec_entry
+
+.align 4
+.Ldec_loop:
+//
+//  Inverse mix columns
+//
+						// vmovdqa	-0x20(%r10),%xmm4		# 4 : sb9u
+						// vmovdqa	-0x10(%r10),%xmm1		# 0 : sb9t
+	tbl	v4.16b, {$sb9u}, v2.16b		// vpshufb	%xmm2,	%xmm4,	%xmm4		# 4 = sb9u
+	tbl	v1.16b, {$sb9t}, v3.16b		// vpshufb	%xmm3,	%xmm1,	%xmm1		# 0 = sb9t
+	eor	v0.16b, v4.16b, v16.16b		// vpxor	%xmm4,	%xmm0,	%xmm0
+						// vmovdqa	0x00(%r10),%xmm4		# 4 : sbdu
+	eor	v0.16b, v0.16b, v1.16b		// vpxor	%xmm1,	%xmm0,	%xmm0		# 0 = ch
+						// vmovdqa	0x10(%r10),%xmm1		# 0 : sbdt
+
+	tbl	v4.16b, {$sbdu}, v2.16b		// vpshufb	%xmm2,	%xmm4,	%xmm4		# 4 = sbdu
+	tbl 	v0.16b, {v0.16b}, v5.16b	// vpshufb	%xmm5,	%xmm0,	%xmm0		# MC ch
+	tbl	v1.16b, {$sbdt}, v3.16b		// vpshufb	%xmm3,	%xmm1,	%xmm1		# 0 = sbdt
+	eor	v0.16b, v0.16b, v4.16b		// vpxor	%xmm4,	%xmm0,	%xmm0		# 4 = ch
+						// vmovdqa	0x20(%r10),	%xmm4		# 4 : sbbu
+	eor	v0.16b, v0.16b, v1.16b		// vpxor	%xmm1,	%xmm0,	%xmm0		# 0 = ch
+						// vmovdqa	0x30(%r10),	%xmm1		# 0 : sbbt
+
+	tbl	v4.16b, {$sbbu}, v2.16b		// vpshufb	%xmm2,	%xmm4,	%xmm4		# 4 = sbbu
+	tbl	v0.16b, {v0.16b}, v5.16b	// vpshufb	%xmm5,	%xmm0,	%xmm0		# MC ch
+	tbl	v1.16b, {$sbbt}, v3.16b		// vpshufb	%xmm3,	%xmm1,	%xmm1		# 0 = sbbt
+	eor	v0.16b, v0.16b, v4.16b		// vpxor	%xmm4,	%xmm0,	%xmm0		# 4 = ch
+						// vmovdqa	0x40(%r10),	%xmm4		# 4 : sbeu
+	eor	v0.16b, v0.16b, v1.16b		// vpxor	%xmm1,	%xmm0,	%xmm0		# 0 = ch
+						// vmovdqa	0x50(%r10),	%xmm1		# 0 : sbet
+
+	tbl	v4.16b, {$sbeu}, v2.16b		// vpshufb	%xmm2,	%xmm4,	%xmm4		# 4 = sbeu
+	tbl	v0.16b, {v0.16b}, v5.16b	// vpshufb	%xmm5,	%xmm0,	%xmm0		# MC ch
+	tbl	v1.16b, {$sbet}, v3.16b		// vpshufb	%xmm3,	%xmm1,	%xmm1		# 0 = sbet
+	eor	v0.16b, v0.16b, v4.16b		// vpxor	%xmm4,	%xmm0,	%xmm0		# 4 = ch
+	ext	v5.16b, v5.16b, v5.16b, #12	// vpalignr \$12,	%xmm5,	%xmm5,	%xmm5
+	eor	v0.16b, v0.16b, v1.16b		// vpxor	%xmm1,	%xmm0,	%xmm0		# 0 = ch
+	sub	w8, w8, #1			// sub		\$1,%rax			# nr--
+
+.Ldec_entry:
+	// top of round
+	and	v1.16b, v0.16b, v17.16b		// vpand	%xmm9,	%xmm0,	%xmm1	# 0 = k
+	ushr	v0.16b, v0.16b, #4		// vpsrlb	\$4,	%xmm0,	%xmm0	# 1 = i
+	tbl	v2.16b, {$invhi}, v1.16b	// vpshufb	%xmm1,	%xmm11,	%xmm2	# 2 = a/k
+	eor	v1.16b,	v1.16b, v0.16b		// vpxor	%xmm0,	%xmm1,	%xmm1	# 0 = j
+	tbl	v3.16b, {$invlo}, v0.16b	// vpshufb	%xmm0, 	%xmm10,	%xmm3	# 3 = 1/i
+	tbl	v4.16b, {$invlo}, v1.16b	// vpshufb	%xmm1,	%xmm10,	%xmm4	# 4 = 1/j
+	eor	v3.16b, v3.16b, v2.16b		// vpxor	%xmm2,	%xmm3,	%xmm3	# 3 = iak = 1/i + a/k
+	eor	v4.16b, v4.16b, v2.16b		// vpxor	%xmm2, 	%xmm4,	%xmm4	# 4 = jak = 1/j + a/k
+	tbl	v2.16b, {$invlo}, v3.16b	// vpshufb	%xmm3,	%xmm10,	%xmm2	# 2 = 1/iak
+	tbl	v3.16b, {$invlo}, v4.16b	// vpshufb	%xmm4,  %xmm10,	%xmm3	# 3 = 1/jak
+	eor	v2.16b, v2.16b, v1.16b		// vpxor	%xmm1,	%xmm2,	%xmm2	# 2 = io
+	eor	v3.16b, v3.16b, v0.16b		// vpxor	%xmm0,  %xmm3,	%xmm3	# 3 = jo
+	ld1	{v16.2d}, [x9],#16		// vmovdqu	(%r9),	%xmm0
+	cbnz	w8, .Ldec_loop
+
+	// middle of last round
+						// vmovdqa	0x60(%r10),	%xmm4	# 3 : sbou
+	tbl	v4.16b, {$sbou}, v2.16b		// vpshufb	%xmm2,	%xmm4,	%xmm4	# 4 = sbou
+						// vmovdqa	0x70(%r10),	%xmm1	# 0 : sbot
+	ld1	{v2.2d}, [x11]			// vmovdqa	-0x160(%r11),	%xmm2	# .Lk_sr-.Lk_dsbd=-0x160
+	tbl	v1.16b, {$sbot}, v3.16b		// vpshufb	%xmm3,	%xmm1,	%xmm1	# 0 = sb1t
+	eor	v4.16b, v4.16b, v16.16b		// vpxor	%xmm0,	%xmm4,	%xmm4	# 4 = sb1u + k
+	eor	v0.16b, v1.16b, v4.16b		// vpxor	%xmm4,	%xmm1,	%xmm0	# 0 = A
+	tbl	v0.16b, {v0.16b}, v2.16b	// vpshufb	%xmm2,	%xmm0,	%xmm0
+	ret
+.size	_vpaes_decrypt_core,.-_vpaes_decrypt_core
+
+.globl	vpaes_decrypt
+.type	vpaes_decrypt,%function
+.align	4
+vpaes_decrypt:
+	stp	x29,x30,[sp,#-16]!
+	add	x29,sp,#0
+
+	ld1	{v7.16b}, [$inp]
+	bl	_vpaes_decrypt_preheat
+	bl	_vpaes_decrypt_core
+	st1	{v0.16b}, [$out]
+
+	ldp	x29,x30,[sp],#16
+	ret
+.size	vpaes_decrypt,.-vpaes_decrypt
+
+// v14-v15 input, v0-v1 output
+.type	_vpaes_decrypt_2x,%function
+.align	4
+_vpaes_decrypt_2x:
+	mov	x9, $key
+	ldr	w8, [$key,#240]			// pull rounds
+
+						// vmovdqa	.Lk_dipt(%rip), %xmm2	# iptlo
+	lsl	x11, x8, #4			// mov	%rax,	%r11;	shl	\$4, %r11
+	eor	x11, x11, #0x30			// xor		\$0x30,	%r11
+	adr	x10, .Lk_sr
+	and	x11, x11, #0x30			// and		\$0x30,	%r11
+	add	x11, x11, x10
+	adr	x10, .Lk_mc_forward+48
+
+	ld1	{v16.2d}, [x9],#16		// vmovdqu	(%r9),	%xmm4		# round0 key
+	and	v1.16b,  v14.16b, v17.16b	// vpand	%xmm9,	%xmm0,	%xmm1
+	ushr	v0.16b,  v14.16b, #4		// vpsrlb	\$4,	%xmm0,	%xmm0
+	 and	v9.16b,  v15.16b, v17.16b
+	 ushr	v8.16b,  v15.16b, #4
+	tbl	v2.16b,  {$iptlo},v1.16b	// vpshufb	%xmm1,	%xmm2,	%xmm2
+	 tbl	v10.16b, {$iptlo},v9.16b
+	ld1	{v5.2d}, [x10]			// vmovdqa	.Lk_mc_forward+48(%rip), %xmm5
+						// vmovdqa	.Lk_dipt+16(%rip), %xmm1 # ipthi
+	tbl	v0.16b,  {$ipthi},v0.16b	// vpshufb	%xmm0,	%xmm1,	%xmm0
+	 tbl	v8.16b,  {$ipthi},v8.16b
+	eor	v2.16b,  v2.16b,  v16.16b	// vpxor	%xmm4,	%xmm2,	%xmm2
+	 eor	v10.16b, v10.16b, v16.16b
+	eor	v0.16b,  v0.16b,  v2.16b	// vpxor	%xmm2,	%xmm0,	%xmm0
+	 eor	v8.16b,  v8.16b,  v10.16b
+	b	.Ldec_2x_entry
+
+.align 4
+.Ldec_2x_loop:
+//
+//  Inverse mix columns
+//
+						// vmovdqa	-0x20(%r10),%xmm4		# 4 : sb9u
+						// vmovdqa	-0x10(%r10),%xmm1		# 0 : sb9t
+	tbl	v4.16b,  {$sb9u}, v2.16b	// vpshufb	%xmm2,	%xmm4,	%xmm4		# 4 = sb9u
+	 tbl	v12.16b, {$sb9u}, v10.16b
+	tbl	v1.16b,  {$sb9t}, v3.16b	// vpshufb	%xmm3,	%xmm1,	%xmm1		# 0 = sb9t
+	 tbl	v9.16b,  {$sb9t}, v11.16b
+	eor	v0.16b,  v4.16b,  v16.16b	// vpxor	%xmm4,	%xmm0,	%xmm0
+	 eor	v8.16b,  v12.16b, v16.16b
+						// vmovdqa	0x00(%r10),%xmm4		# 4 : sbdu
+	eor	v0.16b,  v0.16b,  v1.16b	// vpxor	%xmm1,	%xmm0,	%xmm0		# 0 = ch
+	 eor	v8.16b,  v8.16b,  v9.16b	// vpxor	%xmm1,	%xmm0,	%xmm0		# 0 = ch
+						// vmovdqa	0x10(%r10),%xmm1		# 0 : sbdt
+
+	tbl	v4.16b,  {$sbdu}, v2.16b	// vpshufb	%xmm2,	%xmm4,	%xmm4		# 4 = sbdu
+	 tbl	v12.16b, {$sbdu}, v10.16b
+	tbl 	v0.16b,  {v0.16b},v5.16b	// vpshufb	%xmm5,	%xmm0,	%xmm0		# MC ch
+	 tbl 	v8.16b,  {v8.16b},v5.16b
+	tbl	v1.16b,  {$sbdt}, v3.16b	// vpshufb	%xmm3,	%xmm1,	%xmm1		# 0 = sbdt
+	 tbl	v9.16b,  {$sbdt}, v11.16b
+	eor	v0.16b,  v0.16b,  v4.16b	// vpxor	%xmm4,	%xmm0,	%xmm0		# 4 = ch
+	 eor	v8.16b,  v8.16b,  v12.16b
+						// vmovdqa	0x20(%r10),	%xmm4		# 4 : sbbu
+	eor	v0.16b,  v0.16b,  v1.16b	// vpxor	%xmm1,	%xmm0,	%xmm0		# 0 = ch
+	 eor	v8.16b,  v8.16b,  v9.16b
+						// vmovdqa	0x30(%r10),	%xmm1		# 0 : sbbt
+
+	tbl	v4.16b,  {$sbbu}, v2.16b	// vpshufb	%xmm2,	%xmm4,	%xmm4		# 4 = sbbu
+	 tbl	v12.16b, {$sbbu}, v10.16b
+	tbl	v0.16b,  {v0.16b},v5.16b	// vpshufb	%xmm5,	%xmm0,	%xmm0		# MC ch
+	 tbl	v8.16b,  {v8.16b},v5.16b
+	tbl	v1.16b,  {$sbbt}, v3.16b	// vpshufb	%xmm3,	%xmm1,	%xmm1		# 0 = sbbt
+	 tbl	v9.16b,  {$sbbt}, v11.16b
+	eor	v0.16b,  v0.16b,  v4.16b	// vpxor	%xmm4,	%xmm0,	%xmm0		# 4 = ch
+	 eor	v8.16b,  v8.16b,  v12.16b
+						// vmovdqa	0x40(%r10),	%xmm4		# 4 : sbeu
+	eor	v0.16b,  v0.16b,  v1.16b	// vpxor	%xmm1,	%xmm0,	%xmm0		# 0 = ch
+	 eor	v8.16b,  v8.16b,  v9.16b
+						// vmovdqa	0x50(%r10),	%xmm1		# 0 : sbet
+
+	tbl	v4.16b,  {$sbeu}, v2.16b	// vpshufb	%xmm2,	%xmm4,	%xmm4		# 4 = sbeu
+	 tbl	v12.16b, {$sbeu}, v10.16b
+	tbl	v0.16b,  {v0.16b},v5.16b	// vpshufb	%xmm5,	%xmm0,	%xmm0		# MC ch
+	 tbl	v8.16b,  {v8.16b},v5.16b
+	tbl	v1.16b,  {$sbet}, v3.16b	// vpshufb	%xmm3,	%xmm1,	%xmm1		# 0 = sbet
+	 tbl	v9.16b,  {$sbet}, v11.16b
+	eor	v0.16b,  v0.16b,  v4.16b	// vpxor	%xmm4,	%xmm0,	%xmm0		# 4 = ch
+	 eor	v8.16b,  v8.16b,  v12.16b
+	ext	v5.16b,  v5.16b,  v5.16b, #12	// vpalignr \$12,	%xmm5,	%xmm5,	%xmm5
+	eor	v0.16b,  v0.16b,  v1.16b	// vpxor	%xmm1,	%xmm0,	%xmm0		# 0 = ch
+	 eor	v8.16b,  v8.16b,  v9.16b
+	sub	w8, w8, #1			// sub		\$1,%rax			# nr--
+
+.Ldec_2x_entry:
+	// top of round
+	and	v1.16b,  v0.16b,  v17.16b	// vpand	%xmm9,	%xmm0,	%xmm1	# 0 = k
+	ushr	v0.16b,  v0.16b,  #4		// vpsrlb	\$4,	%xmm0,	%xmm0	# 1 = i
+	 and	v9.16b,  v8.16b,  v17.16b
+	 ushr	v8.16b,  v8.16b,  #4
+	tbl	v2.16b,  {$invhi},v1.16b	// vpshufb	%xmm1,	%xmm11,	%xmm2	# 2 = a/k
+	 tbl	v10.16b, {$invhi},v9.16b
+	eor	v1.16b,	 v1.16b,  v0.16b	// vpxor	%xmm0,	%xmm1,	%xmm1	# 0 = j
+	 eor	v9.16b,	 v9.16b,  v8.16b
+	tbl	v3.16b,  {$invlo},v0.16b	// vpshufb	%xmm0, 	%xmm10,	%xmm3	# 3 = 1/i
+	 tbl	v11.16b, {$invlo},v8.16b
+	tbl	v4.16b,  {$invlo},v1.16b	// vpshufb	%xmm1,	%xmm10,	%xmm4	# 4 = 1/j
+	 tbl	v12.16b, {$invlo},v9.16b
+	eor	v3.16b,  v3.16b,  v2.16b	// vpxor	%xmm2,	%xmm3,	%xmm3	# 3 = iak = 1/i + a/k
+	 eor	v11.16b, v11.16b, v10.16b
+	eor	v4.16b,  v4.16b,  v2.16b	// vpxor	%xmm2, 	%xmm4,	%xmm4	# 4 = jak = 1/j + a/k
+	 eor	v12.16b, v12.16b, v10.16b
+	tbl	v2.16b,  {$invlo},v3.16b	// vpshufb	%xmm3,	%xmm10,	%xmm2	# 2 = 1/iak
+	 tbl	v10.16b, {$invlo},v11.16b
+	tbl	v3.16b,  {$invlo},v4.16b	// vpshufb	%xmm4,  %xmm10,	%xmm3	# 3 = 1/jak
+	 tbl	v11.16b, {$invlo},v12.16b
+	eor	v2.16b,  v2.16b,  v1.16b	// vpxor	%xmm1,	%xmm2,	%xmm2	# 2 = io
+	 eor	v10.16b, v10.16b, v9.16b
+	eor	v3.16b,  v3.16b,  v0.16b	// vpxor	%xmm0,  %xmm3,	%xmm3	# 3 = jo
+	 eor	v11.16b, v11.16b, v8.16b
+	ld1	{v16.2d}, [x9],#16		// vmovdqu	(%r9),	%xmm0
+	cbnz	w8, .Ldec_2x_loop
+
+	// middle of last round
+						// vmovdqa	0x60(%r10),	%xmm4	# 3 : sbou
+	tbl	v4.16b,  {$sbou}, v2.16b	// vpshufb	%xmm2,	%xmm4,	%xmm4	# 4 = sbou
+	 tbl	v12.16b, {$sbou}, v10.16b
+						// vmovdqa	0x70(%r10),	%xmm1	# 0 : sbot
+	tbl	v1.16b,  {$sbot}, v3.16b	// vpshufb	%xmm3,	%xmm1,	%xmm1	# 0 = sb1t
+	 tbl	v9.16b,  {$sbot}, v11.16b
+	ld1	{v2.2d}, [x11]			// vmovdqa	-0x160(%r11),	%xmm2	# .Lk_sr-.Lk_dsbd=-0x160
+	eor	v4.16b,  v4.16b,  v16.16b	// vpxor	%xmm0,	%xmm4,	%xmm4	# 4 = sb1u + k
+	 eor	v12.16b, v12.16b, v16.16b
+	eor	v0.16b,  v1.16b,  v4.16b	// vpxor	%xmm4,	%xmm1,	%xmm0	# 0 = A
+	 eor	v8.16b,  v9.16b,  v12.16b
+	tbl	v0.16b,  {v0.16b},v2.16b	// vpshufb	%xmm2,	%xmm0,	%xmm0
+	 tbl	v1.16b,  {v8.16b},v2.16b
+	ret
+.size	_vpaes_decrypt_2x,.-_vpaes_decrypt_2x
+___
+}
+{
+my ($inp,$bits,$out,$dir)=("x0","w1","x2","w3");
+my ($invlo,$invhi,$iptlo,$ipthi,$rcon) = map("v$_.16b",(18..21,8));
+
+$code.=<<___;
+########################################################
+##                                                    ##
+##                  AES key schedule                  ##
+##                                                    ##
+########################################################
+.type	_vpaes_key_preheat,%function
+.align	4
+_vpaes_key_preheat:
+	adr	x10, .Lk_inv
+	movi	v16.16b, #0x5b			// .Lk_s63
+	adr	x11, .Lk_sb1
+	movi	v17.16b, #0x0f			// .Lk_s0F
+	ld1	{v18.2d-v21.2d}, [x10]		// .Lk_inv, .Lk_ipt
+	adr	x10, .Lk_dksd
+	ld1	{v22.2d-v23.2d}, [x11]		// .Lk_sb1
+	adr	x11, .Lk_mc_forward
+	ld1	{v24.2d-v27.2d}, [x10],#64	// .Lk_dksd, .Lk_dksb
+	ld1	{v28.2d-v31.2d}, [x10],#64	// .Lk_dkse, .Lk_dks9
+	ld1	{v8.2d}, [x10]			// .Lk_rcon
+	ld1	{v9.2d}, [x11]			// .Lk_mc_forward[0]
+	ret
+.size	_vpaes_key_preheat,.-_vpaes_key_preheat
+
+.type	_vpaes_schedule_core,%function
+.align	4
+_vpaes_schedule_core:
+	stp	x29, x30, [sp,#-16]!
+	add	x29,sp,#0
+
+	bl	_vpaes_key_preheat		// load the tables
+
+	ld1	{v0.16b}, [$inp],#16		// vmovdqu	(%rdi),	%xmm0		# load key (unaligned)
+
+	// input transform
+	mov	v3.16b, v0.16b			// vmovdqa	%xmm0,	%xmm3
+	bl	_vpaes_schedule_transform
+	mov	v7.16b, v0.16b			// vmovdqa	%xmm0,	%xmm7
+
+	adr	x10, .Lk_sr			// lea	.Lk_sr(%rip),%r10
+	add	x8, x8, x10
+	cbnz	$dir, .Lschedule_am_decrypting
+
+	// encrypting, output zeroth round key after transform
+	st1	{v0.2d}, [$out]			// vmovdqu	%xmm0,	(%rdx)
+	b	.Lschedule_go
+
+.Lschedule_am_decrypting:
+	// decrypting, output zeroth round key after shiftrows
+	ld1	{v1.2d}, [x8]			// vmovdqa	(%r8,%r10),	%xmm1
+	tbl	v3.16b, {v3.16b}, v1.16b	// vpshufb  %xmm1,	%xmm3,	%xmm3
+	st1	{v3.2d}, [$out]			// vmovdqu	%xmm3,	(%rdx)
+	eor	x8, x8, #0x30			// xor	\$0x30, %r8
+
+.Lschedule_go:
+	cmp	$bits, #192			// cmp	\$192,	%esi
+	b.hi	.Lschedule_256
+	b.eq	.Lschedule_192
+	// 128: fall though
+
+##
+##  .schedule_128
+##
+##  128-bit specific part of key schedule.
+##
+##  This schedule is really simple, because all its parts
+##  are accomplished by the subroutines.
+##
+.Lschedule_128:
+	mov	$inp, #10			// mov	\$10, %esi
+
+.Loop_schedule_128:
+	sub	$inp, $inp, #1			// dec	%esi
+	bl 	_vpaes_schedule_round
+	cbz 	$inp, .Lschedule_mangle_last
+	bl	_vpaes_schedule_mangle		// write output
+	b 	.Loop_schedule_128
+
+##
+##  .aes_schedule_192
+##
+##  192-bit specific part of key schedule.
+##
+##  The main body of this schedule is the same as the 128-bit
+##  schedule, but with more smearing.  The long, high side is
+##  stored in %xmm7 as before, and the short, low side is in
+##  the high bits of %xmm6.
+##
+##  This schedule is somewhat nastier, however, because each
+##  round produces 192 bits of key material, or 1.5 round keys.
+##  Therefore, on each cycle we do 2 rounds and produce 3 round
+##  keys.
+##
+.align	4
+.Lschedule_192:
+	sub	$inp, $inp, #8
+	ld1	{v0.16b}, [$inp]		// vmovdqu	8(%rdi),%xmm0		# load key part 2 (very unaligned)
+	bl	_vpaes_schedule_transform	// input transform
+	mov	v6.16b, v0.16b			// vmovdqa	%xmm0,	%xmm6		# save short part
+	eor	v4.16b, v4.16b, v4.16b		// vpxor	%xmm4,	%xmm4, %xmm4	# clear 4
+	ins	v6.d[0], v4.d[0]		// vmovhlps	%xmm4,	%xmm6,	%xmm6		# clobber low side with zeros
+	mov	$inp, #4			// mov	\$4,	%esi
+
+.Loop_schedule_192:
+	sub	$inp, $inp, #1			// dec	%esi
+	bl	_vpaes_schedule_round
+	ext	v0.16b, v6.16b, v0.16b, #8	// vpalignr	\$8,%xmm6,%xmm0,%xmm0
+	bl	_vpaes_schedule_mangle		// save key n
+	bl	_vpaes_schedule_192_smear
+	bl	_vpaes_schedule_mangle		// save key n+1
+	bl	_vpaes_schedule_round
+	cbz 	$inp, .Lschedule_mangle_last
+	bl	_vpaes_schedule_mangle		// save key n+2
+	bl	_vpaes_schedule_192_smear
+	b	.Loop_schedule_192
+
+##
+##  .aes_schedule_256
+##
+##  256-bit specific part of key schedule.
+##
+##  The structure here is very similar to the 128-bit
+##  schedule, but with an additional "low side" in
+##  %xmm6.  The low side's rounds are the same as the
+##  high side's, except no rcon and no rotation.
+##
+.align	4
+.Lschedule_256:
+	ld1	{v0.16b}, [$inp]		// vmovdqu	16(%rdi),%xmm0		# load key part 2 (unaligned)
+	bl	_vpaes_schedule_transform	// input transform
+	mov	$inp, #7			// mov	\$7, %esi
+	
+.Loop_schedule_256:
+	sub	$inp, $inp, #1			// dec	%esi
+	bl	_vpaes_schedule_mangle		// output low result
+	mov	v6.16b, v0.16b			// vmovdqa	%xmm0,	%xmm6		# save cur_lo in xmm6
+
+	// high round
+	bl	_vpaes_schedule_round
+	cbz 	$inp, .Lschedule_mangle_last
+	bl	_vpaes_schedule_mangle	
+
+	// low round. swap xmm7 and xmm6
+	dup	v0.4s, v0.s[3]			// vpshufd	\$0xFF,	%xmm0,	%xmm0
+	movi	v4.16b, #0
+	mov	v5.16b, v7.16b			// vmovdqa	%xmm7,	%xmm5
+	mov	v7.16b, v6.16b			// vmovdqa	%xmm6,	%xmm7
+	bl	_vpaes_schedule_low_round
+	mov	v7.16b, v5.16b			// vmovdqa	%xmm5,	%xmm7
+	
+	b	.Loop_schedule_256
+
+##
+##  .aes_schedule_mangle_last
+##
+##  Mangler for last round of key schedule
+##  Mangles %xmm0
+##    when encrypting, outputs out(%xmm0) ^ 63
+##    when decrypting, outputs unskew(%xmm0)
+##
+##  Always called right before return... jumps to cleanup and exits
+##
+.align	4
+.Lschedule_mangle_last:
+	// schedule last round key from xmm0
+	adr	x11, .Lk_deskew			// lea	.Lk_deskew(%rip),%r11	# prepare to deskew
+	cbnz	$dir, .Lschedule_mangle_last_dec
+
+	// encrypting
+	ld1	{v1.2d}, [x8]			// vmovdqa	(%r8,%r10),%xmm1
+	adr	x11, .Lk_opt			// lea	.Lk_opt(%rip),	%r11		# prepare to output transform
+	add	$out, $out, #32			// add	\$32,	%rdx
+	tbl	v0.16b, {v0.16b}, v1.16b	// vpshufb	%xmm1,	%xmm0,	%xmm0		# output permute
+
+.Lschedule_mangle_last_dec:
+	ld1	{v20.2d-v21.2d}, [x11]		// reload constants
+	sub	$out, $out, #16			// add	\$-16,	%rdx 
+	eor	v0.16b, v0.16b, v16.16b		// vpxor	.Lk_s63(%rip),	%xmm0,	%xmm0
+	bl	_vpaes_schedule_transform	// output transform
+	st1	{v0.2d}, [$out]			// vmovdqu	%xmm0,	(%rdx)		# save last key
+
+	// cleanup
+	eor	v0.16b, v0.16b, v0.16b		// vpxor	%xmm0,	%xmm0,	%xmm0
+	eor	v1.16b, v1.16b, v1.16b		// vpxor	%xmm1,	%xmm1,	%xmm1
+	eor	v2.16b, v2.16b, v2.16b		// vpxor	%xmm2,	%xmm2,	%xmm2
+	eor	v3.16b, v3.16b, v3.16b		// vpxor	%xmm3,	%xmm3,	%xmm3
+	eor	v4.16b, v4.16b, v4.16b		// vpxor	%xmm4,	%xmm4,	%xmm4
+	eor	v5.16b, v5.16b, v5.16b		// vpxor	%xmm5,	%xmm5,	%xmm5
+	eor	v6.16b, v6.16b, v6.16b		// vpxor	%xmm6,	%xmm6,	%xmm6
+	eor	v7.16b, v7.16b, v7.16b		// vpxor	%xmm7,	%xmm7,	%xmm7
+	ldp	x29, x30, [sp],#16
+	ret
+.size	_vpaes_schedule_core,.-_vpaes_schedule_core
+
+##
+##  .aes_schedule_192_smear
+##
+##  Smear the short, low side in the 192-bit key schedule.
+##
+##  Inputs:
+##    %xmm7: high side, b  a  x  y
+##    %xmm6:  low side, d  c  0  0
+##    %xmm13: 0
+##
+##  Outputs:
+##    %xmm6: b+c+d  b+c  0  0
+##    %xmm0: b+c+d  b+c  b  a
+##
+.type	_vpaes_schedule_192_smear,%function
+.align	4
+_vpaes_schedule_192_smear:
+	movi	v1.16b, #0
+	dup	v0.4s, v7.s[3]
+	ins	v1.s[3], v6.s[2]	// vpshufd	\$0x80,	%xmm6,	%xmm1	# d c 0 0 -> c 0 0 0
+	ins	v0.s[0], v7.s[2]	// vpshufd	\$0xFE,	%xmm7,	%xmm0	# b a _ _ -> b b b a
+	eor	v6.16b, v6.16b, v1.16b	// vpxor	%xmm1,	%xmm6,	%xmm6	# -> c+d c 0 0
+	eor	v1.16b, v1.16b, v1.16b	// vpxor	%xmm1,	%xmm1,	%xmm1
+	eor	v6.16b, v6.16b, v0.16b	// vpxor	%xmm0,	%xmm6,	%xmm6	# -> b+c+d b+c b a
+	mov	v0.16b, v6.16b		// vmovdqa	%xmm6,	%xmm0
+	ins	v6.d[0], v1.d[0]	// vmovhlps	%xmm1,	%xmm6,	%xmm6	# clobber low side with zeros
+	ret
+.size	_vpaes_schedule_192_smear,.-_vpaes_schedule_192_smear
+
+##
+##  .aes_schedule_round
+##
+##  Runs one main round of the key schedule on %xmm0, %xmm7
+##
+##  Specifically, runs subbytes on the high dword of %xmm0
+##  then rotates it by one byte and xors into the low dword of
+##  %xmm7.
+##
+##  Adds rcon from low byte of %xmm8, then rotates %xmm8 for
+##  next rcon.
+##
+##  Smears the dwords of %xmm7 by xoring the low into the
+##  second low, result into third, result into highest.
+##
+##  Returns results in %xmm7 = %xmm0.
+##  Clobbers %xmm1-%xmm4, %r11.
+##
+.type	_vpaes_schedule_round,%function
+.align	4
+_vpaes_schedule_round:
+	// extract rcon from xmm8
+	movi	v4.16b, #0			// vpxor	%xmm4,	%xmm4,	%xmm4
+	ext	v1.16b, $rcon, v4.16b, #15	// vpalignr	\$15,	%xmm8,	%xmm4,	%xmm1
+	ext	$rcon, $rcon, $rcon, #15	// vpalignr	\$15,	%xmm8,	%xmm8,	%xmm8
+	eor	v7.16b, v7.16b, v1.16b		// vpxor	%xmm1,	%xmm7,	%xmm7
+
+	// rotate
+	dup	v0.4s, v0.s[3]			// vpshufd	\$0xFF,	%xmm0,	%xmm0
+	ext	v0.16b, v0.16b, v0.16b, #1	// vpalignr	\$1,	%xmm0,	%xmm0,	%xmm0
+
+	// fall through...
+
+	// low round: same as high round, but no rotation and no rcon.
+_vpaes_schedule_low_round:
+	// smear xmm7
+	ext	v1.16b, v4.16b, v7.16b, #12	// vpslldq	\$4,	%xmm7,	%xmm1
+	eor	v7.16b, v7.16b, v1.16b		// vpxor	%xmm1,	%xmm7,	%xmm7
+	ext	v4.16b, v4.16b, v7.16b, #8	// vpslldq	\$8,	%xmm7,	%xmm4
+
+	// subbytes
+	and	v1.16b, v0.16b, v17.16b		// vpand	%xmm9,	%xmm0,	%xmm1		# 0 = k
+	ushr	v0.16b, v0.16b, #4		// vpsrlb	\$4,	%xmm0,	%xmm0		# 1 = i
+	 eor	v7.16b, v7.16b, v4.16b		// vpxor	%xmm4,	%xmm7,	%xmm7
+	tbl	v2.16b, {$invhi}, v1.16b	// vpshufb	%xmm1,	%xmm11,	%xmm2		# 2 = a/k
+	eor	v1.16b, v1.16b, v0.16b		// vpxor	%xmm0,	%xmm1,	%xmm1		# 0 = j
+	tbl	v3.16b, {$invlo}, v0.16b	// vpshufb	%xmm0, 	%xmm10,	%xmm3		# 3 = 1/i
+	eor	v3.16b, v3.16b, v2.16b		// vpxor	%xmm2,	%xmm3,	%xmm3		# 3 = iak = 1/i + a/k
+	tbl	v4.16b, {$invlo}, v1.16b	// vpshufb	%xmm1,	%xmm10,	%xmm4		# 4 = 1/j
+	 eor	v7.16b, v7.16b, v16.16b		// vpxor	.Lk_s63(%rip),	%xmm7,	%xmm7
+	tbl	v3.16b, {$invlo}, v3.16b	// vpshufb	%xmm3,	%xmm10,	%xmm3		# 2 = 1/iak
+	eor	v4.16b, v4.16b, v2.16b		// vpxor	%xmm2,	%xmm4,	%xmm4		# 4 = jak = 1/j + a/k
+	tbl	v2.16b, {$invlo}, v4.16b	// vpshufb	%xmm4,	%xmm10,	%xmm2		# 3 = 1/jak
+	eor	v3.16b, v3.16b, v1.16b		// vpxor	%xmm1,	%xmm3,	%xmm3		# 2 = io
+	eor	v2.16b, v2.16b, v0.16b		// vpxor	%xmm0,	%xmm2,	%xmm2		# 3 = jo
+	tbl	v4.16b, {v23.16b}, v3.16b	// vpshufb	%xmm3,	%xmm13,	%xmm4		# 4 = sbou
+	tbl	v1.16b, {v22.16b}, v2.16b	// vpshufb	%xmm2,	%xmm12,	%xmm1		# 0 = sb1t
+	eor	v1.16b, v1.16b, v4.16b		// vpxor	%xmm4,	%xmm1,	%xmm1		# 0 = sbox output
+
+	// add in smeared stuff
+	eor	v0.16b, v1.16b, v7.16b		// vpxor	%xmm7,	%xmm1,	%xmm0
+	eor	v7.16b, v1.16b, v7.16b		// vmovdqa	%xmm0,	%xmm7
+	ret
+.size	_vpaes_schedule_round,.-_vpaes_schedule_round
+
+##
+##  .aes_schedule_transform
+##
+##  Linear-transform %xmm0 according to tables at (%r11)
+##
+##  Requires that %xmm9 = 0x0F0F... as in preheat
+##  Output in %xmm0
+##  Clobbers %xmm1, %xmm2
+##
+.type	_vpaes_schedule_transform,%function
+.align	4
+_vpaes_schedule_transform:
+	and	v1.16b, v0.16b, v17.16b		// vpand	%xmm9,	%xmm0,	%xmm1
+	ushr	v0.16b, v0.16b, #4		// vpsrlb	\$4,	%xmm0,	%xmm0
+						// vmovdqa	(%r11),	%xmm2 	# lo
+	tbl	v2.16b, {$iptlo}, v1.16b	// vpshufb	%xmm1,	%xmm2,	%xmm2
+						// vmovdqa	16(%r11),	%xmm1 # hi
+	tbl	v0.16b, {$ipthi}, v0.16b	// vpshufb	%xmm0,	%xmm1,	%xmm0
+	eor	v0.16b, v0.16b, v2.16b		// vpxor	%xmm2,	%xmm0,	%xmm0
+	ret
+.size	_vpaes_schedule_transform,.-_vpaes_schedule_transform
+
+##
+##  .aes_schedule_mangle
+##
+##  Mangle xmm0 from (basis-transformed) standard version
+##  to our version.
+##
+##  On encrypt,
+##    xor with 0x63
+##    multiply by circulant 0,1,1,1
+##    apply shiftrows transform
+##
+##  On decrypt,
+##    xor with 0x63
+##    multiply by "inverse mixcolumns" circulant E,B,D,9
+##    deskew
+##    apply shiftrows transform
+##
+##
+##  Writes out to (%rdx), and increments or decrements it
+##  Keeps track of round number mod 4 in %r8
+##  Preserves xmm0
+##  Clobbers xmm1-xmm5
+##
+.type	_vpaes_schedule_mangle,%function
+.align	4
+_vpaes_schedule_mangle:
+	mov	v4.16b, v0.16b			// vmovdqa	%xmm0,	%xmm4	# save xmm0 for later
+						// vmovdqa	.Lk_mc_forward(%rip),%xmm5
+	cbnz	$dir, .Lschedule_mangle_dec
+
+	// encrypting
+	eor	v4.16b, v0.16b, v16.16b		// vpxor	.Lk_s63(%rip),	%xmm0,	%xmm4
+	add	$out, $out, #16			// add	\$16,	%rdx
+	tbl	v4.16b, {v4.16b}, v9.16b	// vpshufb	%xmm5,	%xmm4,	%xmm4
+	tbl	v1.16b, {v4.16b}, v9.16b	// vpshufb	%xmm5,	%xmm4,	%xmm1
+	tbl	v3.16b, {v1.16b}, v9.16b	// vpshufb	%xmm5,	%xmm1,	%xmm3
+	eor	v4.16b, v4.16b, v1.16b		// vpxor	%xmm1,	%xmm4,	%xmm4
+	ld1	{v1.2d}, [x8]			// vmovdqa	(%r8,%r10),	%xmm1
+	eor	v3.16b, v3.16b, v4.16b		// vpxor	%xmm4,	%xmm3,	%xmm3
+
+	b	.Lschedule_mangle_both
+.align	4
+.Lschedule_mangle_dec:
+	// inverse mix columns
+						// lea	.Lk_dksd(%rip),%r11
+	ushr	v1.16b, v4.16b, #4		// vpsrlb	\$4,	%xmm4,	%xmm1	# 1 = hi
+	and	v4.16b, v4.16b, v17.16b		// vpand	%xmm9,	%xmm4,	%xmm4	# 4 = lo
+
+						// vmovdqa	0x00(%r11),	%xmm2
+	tbl	v2.16b, {v24.16b}, v4.16b	// vpshufb	%xmm4,	%xmm2,	%xmm2
+						// vmovdqa	0x10(%r11),	%xmm3
+	tbl	v3.16b,	{v25.16b}, v1.16b	// vpshufb	%xmm1,	%xmm3,	%xmm3
+	eor	v3.16b, v3.16b, v2.16b		// vpxor	%xmm2,	%xmm3,	%xmm3
+	tbl	v3.16b, {v3.16b}, v9.16b	// vpshufb	%xmm5,	%xmm3,	%xmm3
+
+						// vmovdqa	0x20(%r11),	%xmm2
+	tbl	v2.16b, {v26.16b}, v4.16b	// vpshufb	%xmm4,	%xmm2,	%xmm2
+	eor	v2.16b, v2.16b, v3.16b		// vpxor	%xmm3,	%xmm2,	%xmm2
+						// vmovdqa	0x30(%r11),	%xmm3
+	tbl	v3.16b, {v27.16b}, v1.16b	// vpshufb	%xmm1,	%xmm3,	%xmm3
+	eor	v3.16b, v3.16b, v2.16b		// vpxor	%xmm2,	%xmm3,	%xmm3
+	tbl	v3.16b, {v3.16b}, v9.16b	// vpshufb	%xmm5,	%xmm3,	%xmm3
+
+						// vmovdqa	0x40(%r11),	%xmm2
+	tbl	v2.16b, {v28.16b}, v4.16b	// vpshufb	%xmm4,	%xmm2,	%xmm2
+	eor	v2.16b, v2.16b, v3.16b		// vpxor	%xmm3,	%xmm2,	%xmm2
+						// vmovdqa	0x50(%r11),	%xmm3
+	tbl	v3.16b, {v29.16b}, v1.16b	// vpshufb	%xmm1,	%xmm3,	%xmm3
+	eor	v3.16b, v3.16b, v2.16b		// vpxor	%xmm2,	%xmm3,	%xmm3
+
+						// vmovdqa	0x60(%r11),	%xmm2
+	tbl	v2.16b, {v30.16b}, v4.16b	// vpshufb	%xmm4,	%xmm2,	%xmm2
+	tbl	v3.16b, {v3.16b}, v9.16b	// vpshufb	%xmm5,	%xmm3,	%xmm3
+						// vmovdqa	0x70(%r11),	%xmm4
+	tbl	v4.16b, {v31.16b}, v1.16b	// vpshufb	%xmm1,	%xmm4,	%xmm4
+	ld1	{v1.2d}, [x8]			// vmovdqa	(%r8,%r10),	%xmm1
+	eor	v2.16b, v2.16b, v3.16b		// vpxor	%xmm3,	%xmm2,	%xmm2
+	eor	v3.16b, v4.16b, v2.16b		// vpxor	%xmm2,	%xmm4,	%xmm3
+
+	sub	$out, $out, #16			// add	\$-16,	%rdx
+
+.Lschedule_mangle_both:
+	tbl	v3.16b, {v3.16b}, v1.16b	// vpshufb	%xmm1,	%xmm3,	%xmm3
+	add	x8, x8, #64-16			// add	\$-16,	%r8
+	and	x8, x8, #~(1<<6)		// and	\$0x30,	%r8
+	st1	{v3.2d}, [$out]			// vmovdqu	%xmm3,	(%rdx)
+	ret
+.size	_vpaes_schedule_mangle,.-_vpaes_schedule_mangle
+
+.globl	vpaes_set_encrypt_key
+.type	vpaes_set_encrypt_key,%function
+.align	4
+vpaes_set_encrypt_key:
+	stp	x29,x30,[sp,#-16]!
+	add	x29,sp,#0
+	stp	d8,d9,[sp,#-16]!	// ABI spec says so
+
+	lsr	w9, $bits, #5		// shr	\$5,%eax
+	add	w9, w9, #5		// \$5,%eax
+	str	w9, [$out,#240]		// mov	%eax,240(%rdx)	# AES_KEY->rounds = nbits/32+5;
+
+	mov	$dir, #0		// mov	\$0,%ecx
+	mov	x8, #0x30		// mov	\$0x30,%r8d
+	bl	_vpaes_schedule_core
+	eor	x0, x0, x0
+
+	ldp	d8,d9,[sp],#16
+	ldp	x29,x30,[sp],#16
+	ret
+.size	vpaes_set_encrypt_key,.-vpaes_set_encrypt_key
+
+.globl	vpaes_set_decrypt_key
+.type	vpaes_set_decrypt_key,%function
+.align	4
+vpaes_set_decrypt_key:
+	stp	x29,x30,[sp,#-16]!
+	add	x29,sp,#0
+	stp	d8,d9,[sp,#-16]!	// ABI spec says so
+
+	lsr	w9, $bits, #5		// shr	\$5,%eax
+	add	w9, w9, #5		// \$5,%eax
+	str	w9, [$out,#240]		// mov	%eax,240(%rdx)	# AES_KEY->rounds = nbits/32+5;
+	lsl	w9, w9, #4		// shl	\$4,%eax
+	add	$out, $out, #16		// lea	16(%rdx,%rax),%rdx
+	add	$out, $out, x9
+
+	mov	$dir, #1		// mov	\$1,%ecx
+	lsr	w8, $bits, #1		// shr	\$1,%r8d
+	and	x8, x8, #32		// and	\$32,%r8d
+	eor	x8, x8, #32		// xor	\$32,%r8d	# nbits==192?0:32
+	bl	_vpaes_schedule_core
+
+	ldp	d8,d9,[sp],#16
+	ldp	x29,x30,[sp],#16
+	ret
+.size	vpaes_set_decrypt_key,.-vpaes_set_decrypt_key
+___
+}
+{
+my ($inp,$out,$len,$key,$ivec,$dir) = map("x$_",(0..5));
+
+$code.=<<___;
+.globl	vpaes_cbc_encrypt
+.type	vpaes_cbc_encrypt,%function
+.align	4
+vpaes_cbc_encrypt:
+	cbz	$len, .Lcbc_abort
+	cmp	w5, #0			// check direction
+	b.eq	vpaes_cbc_decrypt
+
+	stp	x29,x30,[sp,#-16]!
+	add	x29,sp,#0
+
+	mov	x17, $len		// reassign
+	mov	x2,  $key		// reassign
+
+	ld1	{v0.16b}, [$ivec]	// load ivec
+	bl	_vpaes_encrypt_preheat
+	b	.Lcbc_enc_loop
+
+.align	4
+.Lcbc_enc_loop:
+	ld1	{v7.16b}, [$inp],#16	// load input
+	eor	v7.16b, v7.16b, v0.16b	// xor with ivec
+	bl	_vpaes_encrypt_core
+	st1	{v0.16b}, [$out],#16	// save output
+	subs	x17, x17, #16
+	b.hi	.Lcbc_enc_loop
+
+	st1	{v0.16b}, [$ivec]	// write ivec
+
+	ldp	x29,x30,[sp],#16
+.Lcbc_abort:
+	ret
+.size	vpaes_cbc_encrypt,.-vpaes_cbc_encrypt
+
+.type	vpaes_cbc_decrypt,%function
+.align	4
+vpaes_cbc_decrypt:
+	stp	x29,x30,[sp,#-16]!
+	add	x29,sp,#0
+	stp	d8,d9,[sp,#-16]!	// ABI spec says so
+	stp	d10,d11,[sp,#-16]!
+	stp	d12,d13,[sp,#-16]!
+	stp	d14,d15,[sp,#-16]!
+
+	mov	x17, $len		// reassign
+	mov	x2,  $key		// reassign
+	ld1	{v6.16b}, [$ivec]	// load ivec
+	bl	_vpaes_decrypt_preheat
+	tst	x17, #16
+	b.eq	.Lcbc_dec_loop2x
+
+	ld1	{v7.16b}, [$inp], #16	// load input
+	bl	_vpaes_decrypt_core
+	eor	v0.16b, v0.16b, v6.16b	// xor with ivec
+	orr	v6.16b, v7.16b, v7.16b	// next ivec value
+	st1	{v0.16b}, [$out], #16
+	subs	x17, x17, #16
+	b.ls	.Lcbc_dec_done
+
+.align	4
+.Lcbc_dec_loop2x:
+	ld1	{v14.16b,v15.16b}, [$inp], #32
+	bl	_vpaes_decrypt_2x
+	eor	v0.16b, v0.16b, v6.16b	// xor with ivec
+	eor	v1.16b, v1.16b, v14.16b
+	orr	v6.16b, v15.16b, v15.16b
+	st1	{v0.16b,v1.16b}, [$out], #32
+	subs	x17, x17, #32
+	b.hi	.Lcbc_dec_loop2x
+
+.Lcbc_dec_done:
+	st1	{v6.16b}, [$ivec]
+
+	ldp	d14,d15,[sp],#16
+	ldp	d12,d13,[sp],#16
+	ldp	d10,d11,[sp],#16
+	ldp	d8,d9,[sp],#16
+	ldp	x29,x30,[sp],#16
+	ret
+.size	vpaes_cbc_decrypt,.-vpaes_cbc_decrypt
+___
+if (1) {
+$code.=<<___;
+.globl	vpaes_ecb_encrypt
+.type	vpaes_ecb_encrypt,%function
+.align	4
+vpaes_ecb_encrypt:
+	stp	x29,x30,[sp,#-16]!
+	add	x29,sp,#0
+	stp	d8,d9,[sp,#-16]!	// ABI spec says so
+	stp	d10,d11,[sp,#-16]!
+	stp	d12,d13,[sp,#-16]!
+	stp	d14,d15,[sp,#-16]!
+
+	mov	x17, $len
+	mov	x2,  $key
+	bl	_vpaes_encrypt_preheat
+	tst	x17, #16
+	b.eq	.Lecb_enc_loop
+
+	ld1	{v7.16b}, [$inp],#16
+	bl	_vpaes_encrypt_core
+	st1	{v0.16b}, [$out],#16
+	subs	x17, x17, #16
+	b.ls	.Lecb_enc_done
+
+.align	4
+.Lecb_enc_loop:
+	ld1	{v14.16b,v15.16b}, [$inp], #32
+	bl	_vpaes_encrypt_2x
+	st1	{v0.16b,v1.16b}, [$out], #32
+	subs	x17, x17, #32
+	b.hi	.Lecb_enc_loop
+
+.Lecb_enc_done:
+	ldp	d14,d15,[sp],#16
+	ldp	d12,d13,[sp],#16
+	ldp	d10,d11,[sp],#16
+	ldp	d8,d9,[sp],#16
+	ldp	x29,x30,[sp],#16
+	ret
+.size	vpaes_ecb_encrypt,.-vpaes_ecb_encrypt
+
+.globl	vpaes_ecb_decrypt
+.type	vpaes_ecb_decrypt,%function
+.align	4
+vpaes_ecb_decrypt:
+	stp	x29,x30,[sp,#-16]!
+	add	x29,sp,#0
+	stp	d8,d9,[sp,#-16]!	// ABI spec says so
+	stp	d10,d11,[sp,#-16]!
+	stp	d12,d13,[sp,#-16]!
+	stp	d14,d15,[sp,#-16]!
+
+	mov	x17, $len
+	mov	x2,  $key
+	bl	_vpaes_decrypt_preheat
+	tst	x17, #16
+	b.eq	.Lecb_dec_loop
+
+	ld1	{v7.16b}, [$inp],#16
+	bl	_vpaes_encrypt_core
+	st1	{v0.16b}, [$out],#16
+	subs	x17, x17, #16
+	b.ls	.Lecb_dec_done
+
+.align	4
+.Lecb_dec_loop:
+	ld1	{v14.16b,v15.16b}, [$inp], #32
+	bl	_vpaes_decrypt_2x
+	st1	{v0.16b,v1.16b}, [$out], #32
+	subs	x17, x17, #32
+	b.hi	.Lecb_dec_loop
+
+.Lecb_dec_done:
+	ldp	d14,d15,[sp],#16
+	ldp	d12,d13,[sp],#16
+	ldp	d10,d11,[sp],#16
+	ldp	d8,d9,[sp],#16
+	ldp	x29,x30,[sp],#16
+	ret
+.size	vpaes_ecb_decrypt,.-vpaes_ecb_decrypt
+___
+}	}
+print $code;
+
+close STDOUT;
diff --git a/crypto/aes/asm/vpaes-ppc.pl b/crypto/aes/asm/vpaes-ppc.pl
new file mode 100644
index 000000000..bb38fbe60
--- /dev/null
+++ b/crypto/aes/asm/vpaes-ppc.pl
@@ -0,0 +1,1594 @@
+#! /usr/bin/env perl
+# Copyright 2013-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+
+######################################################################
+## Constant-time SSSE3 AES core implementation.
+## version 0.1
+##
+## By Mike Hamburg (Stanford University), 2009
+## Public domain.
+##
+## For details see http://shiftleft.org/papers/vector_aes/ and
+## http://crypto.stanford.edu/vpaes/.
+
+# CBC encrypt/decrypt performance in cycles per byte processed with
+# 128-bit key.
+#
+#		aes-ppc.pl		this
+# PPC74x0/G4e	35.5/52.1/(23.8)	11.9(*)/15.4
+# PPC970/G5	37.9/55.0/(28.5)	22.2/28.5
+# POWER6	42.7/54.3/(28.2)	63.0/92.8(**)
+# POWER7	32.3/42.9/(18.4)	18.5/23.3
+#
+# (*)	This is ~10% worse than reported in paper. The reason is
+#	twofold. This module doesn't make any assumption about
+#	key schedule (or data for that matter) alignment and handles
+#	it in-line. Secondly it, being transliterated from
+#	vpaes-x86_64.pl, relies on "nested inversion" better suited
+#	for Intel CPUs.
+# (**)	Inadequate POWER6 performance is due to astronomic AltiVec
+#	latency, 9 cycles per simple logical operation.
+
+$flavour = shift;
+
+if ($flavour =~ /64/) {
+	$SIZE_T	=8;
+	$LRSAVE	=2*$SIZE_T;
+	$STU	="stdu";
+	$POP	="ld";
+	$PUSH	="std";
+	$UCMP	="cmpld";
+} elsif ($flavour =~ /32/) {
+	$SIZE_T	=4;
+	$LRSAVE	=$SIZE_T;
+	$STU	="stwu";
+	$POP	="lwz";
+	$PUSH	="stw";
+	$UCMP	="cmplw";
+} else { die "nonsense $flavour"; }
+
+$sp="r1";
+$FRAME=6*$SIZE_T+13*16;	# 13*16 is for v20-v31 offload
+
+$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+( $xlate="${dir}ppc-xlate.pl" and -f $xlate ) or
+( $xlate="${dir}../../perlasm/ppc-xlate.pl" and -f $xlate) or
+die "can't locate ppc-xlate.pl";
+
+open STDOUT,"| $^X $xlate $flavour ".shift || die "can't call $xlate: $!";
+
+$code.=<<___;
+.machine	"any"
+
+.text
+
+.align	7	# totally strategic alignment
+_vpaes_consts:
+Lk_mc_forward:	# mc_forward
+	.long	0x01020300, 0x05060704, 0x090a0b08, 0x0d0e0f0c	?inv
+	.long	0x05060704, 0x090a0b08, 0x0d0e0f0c, 0x01020300	?inv
+	.long	0x090a0b08, 0x0d0e0f0c, 0x01020300, 0x05060704	?inv
+	.long	0x0d0e0f0c, 0x01020300, 0x05060704, 0x090a0b08	?inv
+Lk_mc_backward:	# mc_backward
+	.long	0x03000102, 0x07040506, 0x0b08090a, 0x0f0c0d0e	?inv
+	.long	0x0f0c0d0e, 0x03000102, 0x07040506, 0x0b08090a	?inv
+	.long	0x0b08090a, 0x0f0c0d0e, 0x03000102, 0x07040506	?inv
+	.long	0x07040506, 0x0b08090a, 0x0f0c0d0e, 0x03000102	?inv
+Lk_sr:		# sr
+	.long	0x00010203, 0x04050607, 0x08090a0b, 0x0c0d0e0f	?inv
+	.long	0x00050a0f, 0x04090e03, 0x080d0207, 0x0c01060b	?inv
+	.long	0x0009020b, 0x040d060f, 0x08010a03, 0x0c050e07	?inv
+	.long	0x000d0a07, 0x04010e0b, 0x0805020f, 0x0c090603	?inv
+
+##
+## "Hot" constants
+##
+Lk_inv:		# inv, inva
+	.long	0xf001080d, 0x0f06050e, 0x020c0b0a, 0x09030704	?rev
+	.long	0xf0070b0f, 0x060a0401, 0x09080502, 0x0c0e0d03	?rev
+Lk_ipt:		# input transform (lo, hi)
+	.long	0x00702a5a, 0x98e8b2c2, 0x08782252, 0x90e0baca	?rev
+	.long	0x004d7c31, 0x7d30014c, 0x81ccfdb0, 0xfcb180cd	?rev
+Lk_sbo:		# sbou, sbot
+	.long	0x00c7bd6f, 0x176dd2d0, 0x78a802c5, 0x7abfaa15	?rev
+	.long	0x006abb5f, 0xa574e4cf, 0xfa352b41, 0xd1901e8e	?rev
+Lk_sb1:		# sb1u, sb1t
+	.long	0x0023e2fa, 0x15d41836, 0xefd92e0d, 0xc1ccf73b	?rev
+	.long	0x003e50cb, 0x8fe19bb1, 0x44f52a14, 0x6e7adfa5	?rev
+Lk_sb2:		# sb2u, sb2t
+	.long	0x0029e10a, 0x4088eb69, 0x4a2382ab, 0xc863a1c2	?rev
+	.long	0x0024710b, 0xc6937ae2, 0xcd2f98bc, 0x55e9b75e	?rev
+
+##
+##  Decryption stuff
+##
+Lk_dipt:	# decryption input transform
+	.long	0x005f540b, 0x045b500f, 0x1a454e11, 0x1e414a15	?rev
+	.long	0x00650560, 0xe683e386, 0x94f191f4, 0x72177712	?rev
+Lk_dsbo:	# decryption sbox final output
+	.long	0x0040f97e, 0x53ea8713, 0x2d3e94d4, 0xb96daac7	?rev
+	.long	0x001d4493, 0x0f56d712, 0x9c8ec5d8, 0x59814bca	?rev
+Lk_dsb9:	# decryption sbox output *9*u, *9*t
+	.long	0x00d6869a, 0x53031c85, 0xc94c994f, 0x501fd5ca	?rev
+	.long	0x0049d7ec, 0x89173bc0, 0x65a5fbb2, 0x9e2c5e72	?rev
+Lk_dsbd:	# decryption sbox output *D*u, *D*t
+	.long	0x00a2b1e6, 0xdfcc577d, 0x39442a88, 0x139b6ef5	?rev
+	.long	0x00cbc624, 0xf7fae23c, 0xd3efde15, 0x0d183129	?rev
+Lk_dsbb:	# decryption sbox output *B*u, *B*t
+	.long	0x0042b496, 0x926422d0, 0x04d4f2b0, 0xf6462660	?rev
+	.long	0x006759cd, 0xa69894c1, 0x6baa5532, 0x3e0cfff3	?rev
+Lk_dsbe:	# decryption sbox output *E*u, *E*t
+	.long	0x00d0d426, 0x9692f246, 0xb0f6b464, 0x04604222	?rev
+	.long	0x00c1aaff, 0xcda6550c, 0x323e5998, 0x6bf36794	?rev
+
+##
+##  Key schedule constants
+##
+Lk_dksd:	# decryption key schedule: invskew x*D
+	.long	0x0047e4a3, 0x5d1ab9fe, 0xf9be1d5a, 0xa4e34007	?rev
+	.long	0x008336b5, 0xf477c241, 0x1e9d28ab, 0xea69dc5f	?rev
+Lk_dksb:	# decryption key schedule: invskew x*B
+	.long	0x00d55085, 0x1fca4f9a, 0x994cc91c, 0x8653d603	?rev
+	.long	0x004afcb6, 0xa7ed5b11, 0xc882347e, 0x6f2593d9	?rev
+Lk_dkse:	# decryption key schedule: invskew x*E + 0x63
+	.long	0x00d6c91f, 0xca1c03d5, 0x86504f99, 0x4c9a8553	?rev
+	.long	0xe87bdc4f, 0x059631a2, 0x8714b320, 0x6af95ecd	?rev
+Lk_dks9:	# decryption key schedule: invskew x*9
+	.long	0x00a7d97e, 0xc86f11b6, 0xfc5b2582, 0x3493ed4a	?rev
+	.long	0x00331427, 0x62517645, 0xcefddae9, 0xac9fb88b	?rev
+
+Lk_rcon:	# rcon
+	.long	0xb6ee9daf, 0xb991831f, 0x817d7c4d, 0x08982a70	?asis
+Lk_s63:
+	.long	0x5b5b5b5b, 0x5b5b5b5b, 0x5b5b5b5b, 0x5b5b5b5b	?asis
+
+Lk_opt:		# output transform
+	.long	0x0060b6d6, 0x29499fff, 0x0868bede, 0x214197f7	?rev
+	.long	0x00ecbc50, 0x51bded01, 0xe00c5cb0, 0xb15d0de1	?rev
+Lk_deskew:	# deskew tables: inverts the sbox's "skew"
+	.long	0x00e3a447, 0x40a3e407, 0x1af9be5d, 0x5ab9fe1d	?rev
+	.long	0x0069ea83, 0xdcb5365f, 0x771e9df4, 0xabc24128	?rev
+.align	5
+Lconsts:
+	mflr	r0
+	bcl	20,31,\$+4
+	mflr	r12	#vvvvv "distance between . and _vpaes_consts
+	addi	r12,r12,-0x308
+	mtlr	r0
+	blr
+	.long	0
+	.byte	0,12,0x14,0,0,0,0,0
+.asciz  "Vector Permutation AES for AltiVec, Mike Hamburg (Stanford University)"
+.align	6
+___
+
+my ($inptail,$inpperm,$outhead,$outperm,$outmask,$keyperm) = map("v$_",(26..31));
+{
+my ($inp,$out,$key) = map("r$_",(3..5));
+
+my ($invlo,$invhi,$iptlo,$ipthi,$sbou,$sbot) = map("v$_",(10..15));
+my ($sb1u,$sb1t,$sb2u,$sb2t) = map("v$_",(16..19));
+my ($sb9u,$sb9t,$sbdu,$sbdt,$sbbu,$sbbt,$sbeu,$sbet)=map("v$_",(16..23));
+
+$code.=<<___;
+##
+##  _aes_preheat
+##
+##  Fills register %r10 -> .aes_consts (so you can -fPIC)
+##  and %xmm9-%xmm15 as specified below.
+##
+.align	4
+_vpaes_encrypt_preheat:
+	mflr	r8
+	bl	Lconsts
+	mtlr	r8
+	li	r11, 0xc0		# Lk_inv
+	li	r10, 0xd0
+	li	r9,  0xe0		# Lk_ipt
+	li	r8,  0xf0
+	vxor	v7, v7, v7		# 0x00..00
+	vspltisb	v8,4		# 0x04..04
+	vspltisb	v9,0x0f		# 0x0f..0f
+	lvx	$invlo, r12, r11
+	li	r11, 0x100
+	lvx	$invhi, r12, r10
+	li	r10, 0x110
+	lvx	$iptlo, r12, r9
+	li	r9,  0x120
+	lvx	$ipthi, r12, r8
+	li	r8,  0x130
+	lvx	$sbou, r12, r11
+	li	r11, 0x140
+	lvx	$sbot, r12, r10
+	li	r10, 0x150
+	lvx	$sb1u, r12, r9
+	lvx	$sb1t, r12, r8
+	lvx	$sb2u, r12, r11
+	lvx	$sb2t, r12, r10
+	blr
+	.long	0
+	.byte	0,12,0x14,0,0,0,0,0
+
+##
+##  _aes_encrypt_core
+##
+##  AES-encrypt %xmm0.
+##
+##  Inputs:
+##     %xmm0 = input
+##     %xmm9-%xmm15 as in _vpaes_preheat
+##    (%rdx) = scheduled keys
+##
+##  Output in %xmm0
+##  Clobbers  %xmm1-%xmm6, %r9, %r10, %r11, %rax
+##
+##
+.align 5
+_vpaes_encrypt_core:
+	lwz	r8, 240($key)		# pull rounds
+	li	r9, 16
+	lvx	v5, 0, $key		# vmovdqu	(%r9),	%xmm5		# round0 key
+	li	r11, 0x10
+	lvx	v6, r9, $key
+	addi	r9, r9, 16
+	?vperm	v5, v5, v6, $keyperm	# align round key
+	addi	r10, r11, 0x40
+	vsrb	v1, v0, v8		# vpsrlb	\$4,	%xmm0,	%xmm0
+	vperm	v0, $iptlo, $iptlo, v0	# vpshufb	%xmm1,	%xmm2,	%xmm1
+	vperm	v1, $ipthi, $ipthi, v1	# vpshufb	%xmm0,	%xmm3,	%xmm2
+	vxor	v0, v0, v5		# vpxor	%xmm5,	%xmm1,	%xmm0
+	vxor	v0, v0, v1		# vpxor	%xmm2,	%xmm0,	%xmm0
+	mtctr	r8
+	b	Lenc_entry
+
+.align 4
+Lenc_loop:
+	# middle of middle round
+	vperm	v4, $sb1t, v7, v2	# vpshufb	%xmm2,	%xmm13,	%xmm4	# 4 = sb1u
+	lvx	v1, r12, r11		# vmovdqa	-0x40(%r11,%r10), %xmm1	# .Lk_mc_forward[]
+	addi	r11, r11, 16
+	vperm	v0, $sb1u, v7, v3	# vpshufb	%xmm3,	%xmm12,	%xmm0	# 0 = sb1t
+	vxor	v4, v4, v5		# vpxor		%xmm5,	%xmm4,	%xmm4	# 4 = sb1u + k
+	andi.	r11, r11, 0x30		# and		\$0x30, %r11	# ... mod 4
+	vperm	v5, $sb2t, v7, v2	# vpshufb	%xmm2,	%xmm15,	%xmm5	# 4 = sb2u
+	vxor	v0, v0, v4		# vpxor		%xmm4,	%xmm0,	%xmm0	# 0 = A
+	vperm	v2, $sb2u, v7, v3	# vpshufb	%xmm3,	%xmm14,	%xmm2	# 2 = sb2t
+	lvx	v4, r12, r10		# vmovdqa	(%r11,%r10), %xmm4	# .Lk_mc_backward[]
+	addi	r10, r11, 0x40
+	vperm	v3, v0, v7, v1		# vpshufb	%xmm1,	%xmm0,	%xmm3	# 0 = B
+	vxor	v2, v2, v5		# vpxor		%xmm5,	%xmm2,	%xmm2	# 2 = 2A
+	vperm	v0, v0, v7, v4		# vpshufb	%xmm4,	%xmm0,	%xmm0	# 3 = D
+	vxor	v3, v3, v2		# vpxor		%xmm2,	%xmm3,	%xmm3	# 0 = 2A+B
+	vperm	v4, v3, v7, v1		# vpshufb	%xmm1,	%xmm3,	%xmm4	# 0 = 2B+C
+	vxor	v0, v0, v3		# vpxor		%xmm3,	%xmm0,	%xmm0	# 3 = 2A+B+D
+	vxor	v0, v0, v4		# vpxor		%xmm4,	%xmm0, %xmm0	# 0 = 2A+3B+C+D
+
+Lenc_entry:
+	# top of round
+	vsrb	v1, v0, v8		# vpsrlb	\$4,	%xmm0,	%xmm0	# 1 = i
+	vperm	v5, $invhi, $invhi, v0	# vpshufb	%xmm1,	%xmm11,	%xmm5	# 2 = a/k
+	vxor	v0, v0, v1		# vpxor		%xmm0,	%xmm1,	%xmm1	# 0 = j
+	vperm	v3, $invlo, $invlo, v1	# vpshufb	%xmm0, 	%xmm10,	%xmm3  	# 3 = 1/i
+	vperm	v4, $invlo, $invlo, v0	# vpshufb	%xmm1, 	%xmm10,	%xmm4  	# 4 = 1/j
+	vand	v0, v0, v9
+	vxor	v3, v3, v5		# vpxor		%xmm5,	%xmm3,	%xmm3	# 3 = iak = 1/i + a/k
+	vxor	v4, v4, v5		# vpxor		%xmm5,	%xmm4,	%xmm4  	# 4 = jak = 1/j + a/k
+	vperm	v2, $invlo, v7, v3	# vpshufb	%xmm3,	%xmm10,	%xmm2  	# 2 = 1/iak
+	vmr	v5, v6
+	lvx	v6, r9, $key		# vmovdqu	(%r9), %xmm5
+	vperm	v3, $invlo, v7, v4	# vpshufb	%xmm4,	%xmm10,	%xmm3	# 3 = 1/jak
+	addi	r9, r9, 16
+	vxor	v2, v2, v0		# vpxor		%xmm1,	%xmm2,	%xmm2  	# 2 = io
+	?vperm	v5, v5, v6, $keyperm	# align round key
+	vxor	v3, v3, v1		# vpxor		%xmm0,	%xmm3,	%xmm3	# 3 = jo
+	bdnz	Lenc_loop
+
+	# middle of last round
+	addi	r10, r11, 0x80
+					# vmovdqa	-0x60(%r10), %xmm4	# 3 : sbou	.Lk_sbo
+					# vmovdqa	-0x50(%r10), %xmm0	# 0 : sbot	.Lk_sbo+16
+	vperm	v4, $sbou, v7, v2	# vpshufb	%xmm2,	%xmm4,	%xmm4	# 4 = sbou
+	lvx	v1, r12, r10		# vmovdqa	0x40(%r11,%r10), %xmm1	# .Lk_sr[]
+	vperm	v0, $sbot, v7, v3	# vpshufb	%xmm3,	%xmm0,	%xmm0	# 0 = sb1t
+	vxor	v4, v4, v5		# vpxor		%xmm5,	%xmm4,	%xmm4	# 4 = sb1u + k
+	vxor	v0, v0, v4		# vpxor		%xmm4,	%xmm0,	%xmm0	# 0 = A
+	vperm	v0, v0, v7, v1		# vpshufb	%xmm1,	%xmm0,	%xmm0
+	blr
+	.long	0
+	.byte	0,12,0x14,0,0,0,0,0
+
+.globl	.vpaes_encrypt
+.align	5
+.vpaes_encrypt:
+	$STU	$sp,-$FRAME($sp)
+	li	r10,`15+6*$SIZE_T`
+	li	r11,`31+6*$SIZE_T`
+	mflr	r6
+	mfspr	r7, 256			# save vrsave
+	stvx	v20,r10,$sp
+	addi	r10,r10,32
+	stvx	v21,r11,$sp
+	addi	r11,r11,32
+	stvx	v22,r10,$sp
+	addi	r10,r10,32
+	stvx	v23,r11,$sp
+	addi	r11,r11,32
+	stvx	v24,r10,$sp
+	addi	r10,r10,32
+	stvx	v25,r11,$sp
+	addi	r11,r11,32
+	stvx	v26,r10,$sp
+	addi	r10,r10,32
+	stvx	v27,r11,$sp
+	addi	r11,r11,32
+	stvx	v28,r10,$sp
+	addi	r10,r10,32
+	stvx	v29,r11,$sp
+	addi	r11,r11,32
+	stvx	v30,r10,$sp
+	stvx	v31,r11,$sp
+	stw	r7,`$FRAME-4`($sp)	# save vrsave
+	li	r0, -1
+	$PUSH	r6,`$FRAME+$LRSAVE`($sp)
+	mtspr	256, r0			# preserve all AltiVec registers
+
+	bl	_vpaes_encrypt_preheat
+
+	?lvsl	$inpperm, 0, $inp	# prepare for unaligned access
+	lvx	v0, 0, $inp
+	addi	$inp, $inp, 15		# 15 is not a typo
+	 ?lvsr	$outperm, 0, $out
+	?lvsl	$keyperm, 0, $key	# prepare for unaligned access
+	lvx	$inptail, 0, $inp	# redundant in aligned case
+	?vperm	v0, v0, $inptail, $inpperm
+
+	bl	_vpaes_encrypt_core
+
+	andi.	r8, $out, 15
+	li	r9, 16
+	beq	Lenc_out_aligned
+
+	vperm	v0, v0, v0, $outperm	# rotate right/left
+	mtctr	r9
+Lenc_out_unaligned:
+	stvebx	v0, 0, $out
+	addi	$out, $out, 1
+	bdnz	Lenc_out_unaligned
+	b	Lenc_done
+
+.align	4
+Lenc_out_aligned:
+	stvx	v0, 0, $out
+Lenc_done:
+
+	li	r10,`15+6*$SIZE_T`
+	li	r11,`31+6*$SIZE_T`
+	mtlr	r6
+	mtspr	256, r7			# restore vrsave
+	lvx	v20,r10,$sp
+	addi	r10,r10,32
+	lvx	v21,r11,$sp
+	addi	r11,r11,32
+	lvx	v22,r10,$sp
+	addi	r10,r10,32
+	lvx	v23,r11,$sp
+	addi	r11,r11,32
+	lvx	v24,r10,$sp
+	addi	r10,r10,32
+	lvx	v25,r11,$sp
+	addi	r11,r11,32
+	lvx	v26,r10,$sp
+	addi	r10,r10,32
+	lvx	v27,r11,$sp
+	addi	r11,r11,32
+	lvx	v28,r10,$sp
+	addi	r10,r10,32
+	lvx	v29,r11,$sp
+	addi	r11,r11,32
+	lvx	v30,r10,$sp
+	lvx	v31,r11,$sp
+	addi	$sp,$sp,$FRAME
+	blr
+	.long	0
+	.byte	0,12,0x04,1,0x80,0,3,0
+	.long	0
+.size	.vpaes_encrypt,.-.vpaes_encrypt
+
+.align	4
+_vpaes_decrypt_preheat:
+	mflr	r8
+	bl	Lconsts
+	mtlr	r8
+	li	r11, 0xc0		# Lk_inv
+	li	r10, 0xd0
+	li	r9,  0x160		# Ldipt
+	li	r8,  0x170
+	vxor	v7, v7, v7		# 0x00..00
+	vspltisb	v8,4		# 0x04..04
+	vspltisb	v9,0x0f		# 0x0f..0f
+	lvx	$invlo, r12, r11
+	li	r11, 0x180
+	lvx	$invhi, r12, r10
+	li	r10, 0x190
+	lvx	$iptlo, r12, r9
+	li	r9,  0x1a0
+	lvx	$ipthi, r12, r8
+	li	r8,  0x1b0
+	lvx	$sbou, r12, r11
+	li	r11, 0x1c0
+	lvx	$sbot, r12, r10
+	li	r10, 0x1d0
+	lvx	$sb9u, r12, r9
+	li	r9,  0x1e0
+	lvx	$sb9t, r12, r8
+	li	r8,  0x1f0
+	lvx	$sbdu, r12, r11
+	li	r11, 0x200
+	lvx	$sbdt, r12, r10
+	li	r10, 0x210
+	lvx	$sbbu, r12, r9
+	lvx	$sbbt, r12, r8
+	lvx	$sbeu, r12, r11
+	lvx	$sbet, r12, r10
+	blr
+	.long	0
+	.byte	0,12,0x14,0,0,0,0,0
+
+##
+##  Decryption core
+##
+##  Same API as encryption core.
+##
+.align	4
+_vpaes_decrypt_core:
+	lwz	r8, 240($key)		# pull rounds
+	li	r9, 16
+	lvx	v5, 0, $key		# vmovdqu	(%r9),	%xmm4		# round0 key
+	li	r11, 0x30
+	lvx	v6, r9, $key
+	addi	r9, r9, 16
+	?vperm	v5, v5, v6, $keyperm	# align round key
+	vsrb	v1, v0, v8		# vpsrlb	\$4,	%xmm0,	%xmm0
+	vperm	v0, $iptlo, $iptlo, v0	# vpshufb	%xmm1,	%xmm2,	%xmm2
+	vperm	v1, $ipthi, $ipthi, v1	# vpshufb	%xmm0,	%xmm1,	%xmm0
+	vxor	v0, v0, v5		# vpxor	%xmm4,	%xmm2,	%xmm2
+	vxor	v0, v0, v1		# vpxor	%xmm2,	%xmm0,	%xmm0
+	mtctr	r8
+	b	Ldec_entry
+
+.align 4
+Ldec_loop:
+#
+#  Inverse mix columns
+#
+	lvx	v0, r12, r11		# v5 and v0 are flipped
+					# vmovdqa	-0x20(%r10),%xmm4		# 4 : sb9u
+					# vmovdqa	-0x10(%r10),%xmm1		# 0 : sb9t
+	vperm	v4, $sb9u, v7, v2	# vpshufb	%xmm2,	%xmm4,	%xmm4		# 4 = sb9u
+	subi	r11, r11, 16
+	vperm	v1, $sb9t, v7, v3	# vpshufb	%xmm3,	%xmm1,	%xmm1		# 0 = sb9t
+	andi.	r11, r11, 0x30
+	vxor	v5, v5, v4		# vpxor		%xmm4,	%xmm0,	%xmm0
+					# vmovdqa	0x00(%r10),%xmm4		# 4 : sbdu
+	vxor	v5, v5, v1		# vpxor		%xmm1,	%xmm0,	%xmm0		# 0 = ch
+					# vmovdqa	0x10(%r10),%xmm1		# 0 : sbdt
+
+	vperm	v4, $sbdu, v7, v2	# vpshufb	%xmm2,	%xmm4,	%xmm4		# 4 = sbdu
+	vperm 	v5, v5, v7, v0		# vpshufb	%xmm5,	%xmm0,	%xmm0		# MC ch
+	vperm	v1, $sbdt, v7, v3	# vpshufb	%xmm3,	%xmm1,	%xmm1		# 0 = sbdt
+	vxor	v5, v5, v4		# vpxor		%xmm4,	%xmm0,	%xmm0		# 4 = ch
+					# vmovdqa	0x20(%r10),	%xmm4		# 4 : sbbu
+	vxor	v5, v5, v1		# vpxor		%xmm1,	%xmm0,	%xmm0		# 0 = ch
+					# vmovdqa	0x30(%r10),	%xmm1		# 0 : sbbt
+
+	vperm	v4, $sbbu, v7, v2	# vpshufb	%xmm2,	%xmm4,	%xmm4		# 4 = sbbu
+	vperm	v5, v5, v7, v0		# vpshufb	%xmm5,	%xmm0,	%xmm0		# MC ch
+	vperm	v1, $sbbt, v7, v3	# vpshufb	%xmm3,	%xmm1,	%xmm1		# 0 = sbbt
+	vxor	v5, v5, v4		# vpxor		%xmm4,	%xmm0,	%xmm0		# 4 = ch
+					# vmovdqa	0x40(%r10),	%xmm4		# 4 : sbeu
+	vxor	v5, v5, v1		# vpxor		%xmm1,	%xmm0,	%xmm0		# 0 = ch
+					# vmovdqa	0x50(%r10),	%xmm1		# 0 : sbet
+
+	vperm	v4, $sbeu, v7, v2	# vpshufb	%xmm2,	%xmm4,	%xmm4		# 4 = sbeu
+	vperm	v5, v5, v7, v0		# vpshufb	%xmm5,	%xmm0,	%xmm0		# MC ch
+	vperm	v1, $sbet, v7, v3	# vpshufb	%xmm3,	%xmm1,	%xmm1		# 0 = sbet
+	vxor	v0, v5, v4		# vpxor		%xmm4,	%xmm0,	%xmm0		# 4 = ch
+	vxor	v0, v0, v1		# vpxor		%xmm1,	%xmm0,	%xmm0		# 0 = ch
+
+Ldec_entry:
+	# top of round
+	vsrb	v1, v0, v8		# vpsrlb	\$4,	%xmm0,	%xmm0	# 1 = i
+	vperm	v2, $invhi, $invhi, v0	# vpshufb	%xmm1,	%xmm11,	%xmm2	# 2 = a/k
+	vxor	v0, v0, v1		# vpxor		%xmm0,	%xmm1,	%xmm1	# 0 = j
+	vperm	v3, $invlo, $invlo, v1	# vpshufb	%xmm0, 	%xmm10,	%xmm3	# 3 = 1/i
+	vperm	v4, $invlo, $invlo, v0	# vpshufb	%xmm1,	%xmm10,	%xmm4	# 4 = 1/j
+	vand	v0, v0, v9
+	vxor	v3, v3, v2		# vpxor		%xmm2,	%xmm3,	%xmm3	# 3 = iak = 1/i + a/k
+	vxor	v4, v4, v2		# vpxor		%xmm2, 	%xmm4,	%xmm4	# 4 = jak = 1/j + a/k
+	vperm	v2, $invlo, v7, v3	# vpshufb	%xmm3,	%xmm10,	%xmm2	# 2 = 1/iak
+	vmr	v5, v6
+	lvx	v6, r9, $key		# vmovdqu	(%r9),	%xmm0
+	vperm	v3, $invlo, v7, v4	# vpshufb	%xmm4,  %xmm10,	%xmm3	# 3 = 1/jak
+	addi	r9, r9, 16
+	vxor	v2, v2, v0		# vpxor		%xmm1,	%xmm2,	%xmm2	# 2 = io
+	?vperm	v5, v5, v6, $keyperm	# align round key
+	vxor	v3, v3, v1		# vpxor		%xmm0,  %xmm3,	%xmm3	# 3 = jo
+	bdnz	Ldec_loop
+
+	# middle of last round
+	addi	r10, r11, 0x80
+					# vmovdqa	0x60(%r10),	%xmm4	# 3 : sbou
+	vperm	v4, $sbou, v7, v2	# vpshufb	%xmm2,	%xmm4,	%xmm4	# 4 = sbou
+					# vmovdqa	0x70(%r10),	%xmm1	# 0 : sbot
+	lvx	v2, r12, r10		# vmovdqa	-0x160(%r11),	%xmm2	# .Lk_sr-.Lk_dsbd=-0x160
+	vperm	v1, $sbot, v7, v3	# vpshufb	%xmm3,	%xmm1,	%xmm1	# 0 = sb1t
+	vxor	v4, v4, v5		# vpxor		%xmm0,	%xmm4,	%xmm4	# 4 = sb1u + k
+	vxor	v0, v1, v4		# vpxor		%xmm4,	%xmm1,	%xmm0	# 0 = A
+	vperm	v0, v0, v7, v2		# vpshufb	%xmm2,	%xmm0,	%xmm0
+	blr
+	.long	0
+	.byte	0,12,0x14,0,0,0,0,0
+
+.globl	.vpaes_decrypt
+.align	5
+.vpaes_decrypt:
+	$STU	$sp,-$FRAME($sp)
+	li	r10,`15+6*$SIZE_T`
+	li	r11,`31+6*$SIZE_T`
+	mflr	r6
+	mfspr	r7, 256			# save vrsave
+	stvx	v20,r10,$sp
+	addi	r10,r10,32
+	stvx	v21,r11,$sp
+	addi	r11,r11,32
+	stvx	v22,r10,$sp
+	addi	r10,r10,32
+	stvx	v23,r11,$sp
+	addi	r11,r11,32
+	stvx	v24,r10,$sp
+	addi	r10,r10,32
+	stvx	v25,r11,$sp
+	addi	r11,r11,32
+	stvx	v26,r10,$sp
+	addi	r10,r10,32
+	stvx	v27,r11,$sp
+	addi	r11,r11,32
+	stvx	v28,r10,$sp
+	addi	r10,r10,32
+	stvx	v29,r11,$sp
+	addi	r11,r11,32
+	stvx	v30,r10,$sp
+	stvx	v31,r11,$sp
+	stw	r7,`$FRAME-4`($sp)	# save vrsave
+	li	r0, -1
+	$PUSH	r6,`$FRAME+$LRSAVE`($sp)
+	mtspr	256, r0			# preserve all AltiVec registers
+
+	bl	_vpaes_decrypt_preheat
+
+	?lvsl	$inpperm, 0, $inp	# prepare for unaligned access
+	lvx	v0, 0, $inp
+	addi	$inp, $inp, 15		# 15 is not a typo
+	 ?lvsr	$outperm, 0, $out
+	?lvsl	$keyperm, 0, $key
+	lvx	$inptail, 0, $inp	# redundant in aligned case
+	?vperm	v0, v0, $inptail, $inpperm
+
+	bl	_vpaes_decrypt_core
+
+	andi.	r8, $out, 15
+	li	r9, 16
+	beq	Ldec_out_aligned
+
+	vperm	v0, v0, v0, $outperm	# rotate right/left
+	mtctr	r9
+Ldec_out_unaligned:
+	stvebx	v0, 0, $out
+	addi	$out, $out, 1
+	bdnz	Ldec_out_unaligned
+	b	Ldec_done
+
+.align	4
+Ldec_out_aligned:
+	stvx	v0, 0, $out
+Ldec_done:
+
+	li	r10,`15+6*$SIZE_T`
+	li	r11,`31+6*$SIZE_T`
+	mtlr	r6
+	mtspr	256, r7			# restore vrsave
+	lvx	v20,r10,$sp
+	addi	r10,r10,32
+	lvx	v21,r11,$sp
+	addi	r11,r11,32
+	lvx	v22,r10,$sp
+	addi	r10,r10,32
+	lvx	v23,r11,$sp
+	addi	r11,r11,32
+	lvx	v24,r10,$sp
+	addi	r10,r10,32
+	lvx	v25,r11,$sp
+	addi	r11,r11,32
+	lvx	v26,r10,$sp
+	addi	r10,r10,32
+	lvx	v27,r11,$sp
+	addi	r11,r11,32
+	lvx	v28,r10,$sp
+	addi	r10,r10,32
+	lvx	v29,r11,$sp
+	addi	r11,r11,32
+	lvx	v30,r10,$sp
+	lvx	v31,r11,$sp
+	addi	$sp,$sp,$FRAME
+	blr
+	.long	0
+	.byte	0,12,0x04,1,0x80,0,3,0
+	.long	0
+.size	.vpaes_decrypt,.-.vpaes_decrypt
+
+.globl	.vpaes_cbc_encrypt
+.align	5
+.vpaes_cbc_encrypt:
+	${UCMP}i r5,16
+	bltlr-
+
+	$STU	$sp,-`($FRAME+2*$SIZE_T)`($sp)
+	mflr	r0
+	li	r10,`15+6*$SIZE_T`
+	li	r11,`31+6*$SIZE_T`
+	mfspr	r12, 256
+	stvx	v20,r10,$sp
+	addi	r10,r10,32
+	stvx	v21,r11,$sp
+	addi	r11,r11,32
+	stvx	v22,r10,$sp
+	addi	r10,r10,32
+	stvx	v23,r11,$sp
+	addi	r11,r11,32
+	stvx	v24,r10,$sp
+	addi	r10,r10,32
+	stvx	v25,r11,$sp
+	addi	r11,r11,32
+	stvx	v26,r10,$sp
+	addi	r10,r10,32
+	stvx	v27,r11,$sp
+	addi	r11,r11,32
+	stvx	v28,r10,$sp
+	addi	r10,r10,32
+	stvx	v29,r11,$sp
+	addi	r11,r11,32
+	stvx	v30,r10,$sp
+	stvx	v31,r11,$sp
+	stw	r12,`$FRAME-4`($sp)	# save vrsave
+	$PUSH	r30,`$FRAME+$SIZE_T*0`($sp)
+	$PUSH	r31,`$FRAME+$SIZE_T*1`($sp)
+	li	r9, -16
+	$PUSH	r0, `$FRAME+$SIZE_T*2+$LRSAVE`($sp)
+
+	and	r30, r5, r9		# copy length&-16
+	andi.	r9, $out, 15		# is $out aligned?
+	mr	r5, r6			# copy pointer to key
+	mr	r31, r7			# copy pointer to iv
+	li	r6, -1
+	mcrf	cr1, cr0		# put aside $out alignment flag
+	mr	r7, r12			# copy vrsave
+	mtspr	256, r6			# preserve all AltiVec registers
+
+	lvx	v24, 0, r31		# load [potentially unaligned] iv
+	li	r9, 15
+	?lvsl	$inpperm, 0, r31
+	lvx	v25, r9, r31
+	?vperm	v24, v24, v25, $inpperm
+
+	cmpwi	r8, 0			# test direction
+	neg	r8, $inp		# prepare for unaligned access
+	 vxor	v7, v7, v7
+	?lvsl	$keyperm, 0, $key
+	 ?lvsr	$outperm, 0, $out
+	?lvsr	$inpperm, 0, r8		# -$inp
+	 vnor	$outmask, v7, v7	# 0xff..ff
+	lvx	$inptail, 0, $inp
+	 ?vperm	$outmask, v7, $outmask, $outperm
+	addi	$inp, $inp, 15		# 15 is not a typo
+
+	beq	Lcbc_decrypt
+
+	bl	_vpaes_encrypt_preheat
+	li	r0, 16
+
+	beq	cr1, Lcbc_enc_loop	# $out is aligned
+
+	vmr	v0, $inptail
+	lvx	$inptail, 0, $inp
+	addi	$inp, $inp, 16
+	?vperm	v0, v0, $inptail, $inpperm
+	vxor	v0, v0, v24		# ^= iv
+
+	bl	_vpaes_encrypt_core
+
+	andi.	r8, $out, 15
+	vmr	v24, v0			# put aside iv
+	sub	r9, $out, r8
+	vperm	$outhead, v0, v0, $outperm	# rotate right/left
+
+Lcbc_enc_head:
+	stvebx	$outhead, r8, r9
+	cmpwi	r8, 15
+	addi	r8, r8, 1
+	bne	Lcbc_enc_head
+
+	sub.	r30, r30, r0		# len -= 16
+	addi	$out, $out, 16
+	beq	Lcbc_unaligned_done
+
+Lcbc_enc_loop:
+	vmr	v0, $inptail
+	lvx	$inptail, 0, $inp
+	addi	$inp, $inp, 16
+	?vperm	v0, v0, $inptail, $inpperm
+	vxor	v0, v0, v24		# ^= iv
+
+	bl	_vpaes_encrypt_core
+
+	vmr	v24, v0			# put aside iv
+	sub.	r30, r30, r0		# len -= 16
+	vperm	v0, v0, v0, $outperm	# rotate right/left
+	vsel	v1, $outhead, v0, $outmask
+	vmr	$outhead, v0
+	stvx	v1, 0, $out
+	addi	$out, $out, 16
+	bne	Lcbc_enc_loop
+
+	b	Lcbc_done
+
+.align	5
+Lcbc_decrypt:
+	bl	_vpaes_decrypt_preheat
+	li	r0, 16
+
+	beq	cr1, Lcbc_dec_loop	# $out is aligned
+
+	vmr	v0, $inptail
+	lvx	$inptail, 0, $inp
+	addi	$inp, $inp, 16
+	?vperm	v0, v0, $inptail, $inpperm
+	vmr	v25, v0			# put aside input
+
+	bl	_vpaes_decrypt_core
+
+	andi.	r8, $out, 15
+	vxor	v0, v0, v24		# ^= iv
+	vmr	v24, v25
+	sub	r9, $out, r8
+	vperm	$outhead, v0, v0, $outperm	# rotate right/left
+
+Lcbc_dec_head:
+	stvebx	$outhead, r8, r9
+	cmpwi	r8, 15
+	addi	r8, r8, 1
+	bne	Lcbc_dec_head
+
+	sub.	r30, r30, r0		# len -= 16
+	addi	$out, $out, 16
+	beq	Lcbc_unaligned_done
+
+Lcbc_dec_loop:
+	vmr	v0, $inptail
+	lvx	$inptail, 0, $inp
+	addi	$inp, $inp, 16
+	?vperm	v0, v0, $inptail, $inpperm
+	vmr	v25, v0			# put aside input
+
+	bl	_vpaes_decrypt_core
+
+	vxor	v0, v0, v24		# ^= iv
+	vmr	v24, v25
+	sub.	r30, r30, r0		# len -= 16
+	vperm	v0, v0, v0, $outperm	# rotate right/left
+	vsel	v1, $outhead, v0, $outmask
+	vmr	$outhead, v0
+	stvx	v1, 0, $out
+	addi	$out, $out, 16
+	bne	Lcbc_dec_loop
+
+Lcbc_done:
+	beq	cr1, Lcbc_write_iv	# $out is aligned
+
+Lcbc_unaligned_done:
+	andi.	r8, $out, 15
+	sub	$out, $out, r8
+	li	r9, 0
+Lcbc_tail:
+	stvebx	$outhead, r9, $out
+	addi	r9, r9, 1
+	cmpw	r9, r8
+	bne	Lcbc_tail
+
+Lcbc_write_iv:
+	neg	r8, r31			# write [potentially unaligned] iv
+	li	r10, 4
+	?lvsl	$outperm, 0, r8
+	li	r11, 8
+	li	r12, 12
+	vperm	v24, v24, v24, $outperm	# rotate right/left
+	stvewx	v24, 0, r31		# ivp is at least 32-bit aligned
+	stvewx	v24, r10, r31
+	stvewx	v24, r11, r31
+	stvewx	v24, r12, r31
+
+	mtspr	256, r7			# restore vrsave
+	li	r10,`15+6*$SIZE_T`
+	li	r11,`31+6*$SIZE_T`
+	lvx	v20,r10,$sp
+	addi	r10,r10,32
+	lvx	v21,r11,$sp
+	addi	r11,r11,32
+	lvx	v22,r10,$sp
+	addi	r10,r10,32
+	lvx	v23,r11,$sp
+	addi	r11,r11,32
+	lvx	v24,r10,$sp
+	addi	r10,r10,32
+	lvx	v25,r11,$sp
+	addi	r11,r11,32
+	lvx	v26,r10,$sp
+	addi	r10,r10,32
+	lvx	v27,r11,$sp
+	addi	r11,r11,32
+	lvx	v28,r10,$sp
+	addi	r10,r10,32
+	lvx	v29,r11,$sp
+	addi	r11,r11,32
+	lvx	v30,r10,$sp
+	lvx	v31,r11,$sp
+Lcbc_abort:
+	$POP	r0, `$FRAME+$SIZE_T*2+$LRSAVE`($sp)
+	$POP	r30,`$FRAME+$SIZE_T*0`($sp)
+	$POP	r31,`$FRAME+$SIZE_T*1`($sp)
+	mtlr	r0
+	addi	$sp,$sp,`$FRAME+$SIZE_T*2`
+	blr
+	.long	0
+	.byte	0,12,0x04,1,0x80,2,6,0
+	.long	0
+.size	.vpaes_cbc_encrypt,.-.vpaes_cbc_encrypt
+___
+}
+{
+my ($inp,$bits,$out)=map("r$_",(3..5));
+my $dir="cr1";
+my ($invlo,$invhi,$iptlo,$ipthi,$rcon) = map("v$_",(10..13,24));
+
+$code.=<<___;
+########################################################
+##                                                    ##
+##                  AES key schedule                  ##
+##                                                    ##
+########################################################
+.align	4
+_vpaes_key_preheat:
+	mflr	r8
+	bl	Lconsts
+	mtlr	r8
+	li	r11, 0xc0		# Lk_inv
+	li	r10, 0xd0
+	li	r9,  0xe0		# L_ipt
+	li	r8,  0xf0
+
+	vspltisb	v8,4		# 0x04..04
+	vxor	v9,v9,v9		# 0x00..00
+	lvx	$invlo, r12, r11	# Lk_inv
+	li	r11, 0x120
+	lvx	$invhi, r12, r10
+	li	r10, 0x130
+	lvx	$iptlo, r12, r9		# Lk_ipt
+	li	r9, 0x220
+	lvx	$ipthi, r12, r8
+	li	r8, 0x230
+
+	lvx	v14, r12, r11		# Lk_sb1
+	li	r11, 0x240
+	lvx	v15, r12, r10
+	li	r10, 0x250
+
+	lvx	v16, r12, r9		# Lk_dksd
+	li	r9, 0x260
+	lvx	v17, r12, r8
+	li	r8, 0x270
+	lvx	v18, r12, r11		# Lk_dksb
+	li	r11, 0x280
+	lvx	v19, r12, r10
+	li	r10, 0x290
+	lvx	v20, r12, r9		# Lk_dkse
+	li	r9, 0x2a0
+	lvx	v21, r12, r8
+	li	r8, 0x2b0
+	lvx	v22, r12, r11		# Lk_dks9
+	lvx	v23, r12, r10
+
+	lvx	v24, r12, r9		# Lk_rcon
+	lvx	v25, 0, r12		# Lk_mc_forward[0]
+	lvx	v26, r12, r8		# Lks63
+	blr
+	.long	0
+	.byte	0,12,0x14,0,0,0,0,0
+
+.align	4
+_vpaes_schedule_core:
+	mflr	r7
+
+	bl	_vpaes_key_preheat	# load the tables
+
+	#lvx	v0, 0, $inp		# vmovdqu	(%rdi),	%xmm0		# load key (unaligned)
+	neg	r8, $inp		# prepare for unaligned access
+	lvx	v0, 0, $inp
+	addi	$inp, $inp, 15		# 15 is not typo
+	?lvsr	$inpperm, 0, r8		# -$inp
+	lvx	v6, 0, $inp		# v6 serves as inptail
+	addi	$inp, $inp, 8
+	?vperm	v0, v0, v6, $inpperm
+
+	# input transform
+	vmr	v3, v0			# vmovdqa	%xmm0,	%xmm3
+	bl	_vpaes_schedule_transform
+	vmr	v7, v0			# vmovdqa	%xmm0,	%xmm7
+
+	bne	$dir, Lschedule_am_decrypting
+
+	# encrypting, output zeroth round key after transform
+	li	r8, 0x30		# mov	\$0x30,%r8d
+	li	r9, 4
+	li	r10, 8
+	li	r11, 12
+
+	?lvsr	$outperm, 0, $out	# prepare for unaligned access
+	vnor	$outmask, v9, v9	# 0xff..ff
+	?vperm	$outmask, v9, $outmask, $outperm
+
+	#stvx	v0, 0, $out		# vmovdqu	%xmm0,	(%rdx)
+	vperm	$outhead, v0, v0, $outperm	# rotate right/left
+	stvewx	$outhead, 0, $out	# some are superfluous
+	stvewx	$outhead, r9, $out
+	stvewx	$outhead, r10, $out
+	addi	r10, r12, 0x80		# lea	.Lk_sr(%rip),%r10
+	stvewx	$outhead, r11, $out
+	b	Lschedule_go
+
+Lschedule_am_decrypting:
+	srwi	r8, $bits, 1		# shr	\$1,%r8d
+	andi.	r8, r8, 32		# and	\$32,%r8d
+	xori	r8, r8, 32		# xor	\$32,%r8d	# nbits==192?0:32
+	addi	r10, r12, 0x80		# lea	.Lk_sr(%rip),%r10
+	# decrypting, output zeroth round key after shiftrows
+	lvx	v1, r8, r10		# vmovdqa	(%r8,%r10),	%xmm1
+	li	r9, 4
+	li	r10, 8
+	li	r11, 12
+	vperm	v4, v3, v3, v1		# vpshufb	%xmm1,	%xmm3,	%xmm3
+
+	neg	r0, $out		# prepare for unaligned access
+	?lvsl	$outperm, 0, r0
+	vnor	$outmask, v9, v9	# 0xff..ff
+	?vperm	$outmask, $outmask, v9, $outperm
+
+	#stvx	v4, 0, $out		# vmovdqu	%xmm3,	(%rdx)
+	vperm	$outhead, v4, v4, $outperm	# rotate right/left
+	stvewx	$outhead, 0, $out	# some are superfluous
+	stvewx	$outhead, r9, $out
+	stvewx	$outhead, r10, $out
+	addi	r10, r12, 0x80		# lea	.Lk_sr(%rip),%r10
+	stvewx	$outhead, r11, $out
+	addi	$out, $out, 15		# 15 is not typo
+	xori	r8, r8, 0x30		# xor	\$0x30, %r8
+
+Lschedule_go:
+	cmplwi	$bits, 192		# cmp	\$192,	%esi
+	bgt	Lschedule_256
+	beq	Lschedule_192
+	# 128: fall though
+
+##
+##  .schedule_128
+##
+##  128-bit specific part of key schedule.
+##
+##  This schedule is really simple, because all its parts
+##  are accomplished by the subroutines.
+##
+Lschedule_128:
+	li	r0, 10			# mov	\$10, %esi
+	mtctr	r0
+
+Loop_schedule_128:
+	bl 	_vpaes_schedule_round
+	bdz 	Lschedule_mangle_last	# dec	%esi
+	bl	_vpaes_schedule_mangle	# write output
+	b 	Loop_schedule_128
+
+##
+##  .aes_schedule_192
+##
+##  192-bit specific part of key schedule.
+##
+##  The main body of this schedule is the same as the 128-bit
+##  schedule, but with more smearing.  The long, high side is
+##  stored in %xmm7 as before, and the short, low side is in
+##  the high bits of %xmm6.
+##
+##  This schedule is somewhat nastier, however, because each
+##  round produces 192 bits of key material, or 1.5 round keys.
+##  Therefore, on each cycle we do 2 rounds and produce 3 round
+##  keys.
+##
+.align	4
+Lschedule_192:
+	li	r0, 4			# mov	\$4,	%esi
+	lvx	v0, 0, $inp
+	?vperm	v0, v6, v0, $inpperm
+	?vsldoi	v0, v3, v0, 8		# vmovdqu	8(%rdi),%xmm0		# load key part 2 (very unaligned)
+	bl	_vpaes_schedule_transform	# input transform
+	?vsldoi	v6, v0, v9, 8
+	?vsldoi	v6, v9, v6, 8		# clobber "low" side with zeros
+	mtctr	r0
+
+Loop_schedule_192:
+	bl	_vpaes_schedule_round
+	?vsldoi	v0, v6, v0, 8		# vpalignr	\$8,%xmm6,%xmm0,%xmm0
+	bl	_vpaes_schedule_mangle	# save key n
+	bl	_vpaes_schedule_192_smear
+	bl	_vpaes_schedule_mangle	# save key n+1
+	bl	_vpaes_schedule_round
+	bdz 	Lschedule_mangle_last	# dec	%esi
+	bl	_vpaes_schedule_mangle	# save key n+2
+	bl	_vpaes_schedule_192_smear
+	b	Loop_schedule_192
+
+##
+##  .aes_schedule_256
+##
+##  256-bit specific part of key schedule.
+##
+##  The structure here is very similar to the 128-bit
+##  schedule, but with an additional "low side" in
+##  %xmm6.  The low side's rounds are the same as the
+##  high side's, except no rcon and no rotation.
+##
+.align	4
+Lschedule_256:
+	li	r0, 7			# mov	\$7, %esi
+	addi	$inp, $inp, 8
+	lvx	v0, 0, $inp		# vmovdqu	16(%rdi),%xmm0		# load key part 2 (unaligned)
+	?vperm	v0, v6, v0, $inpperm
+	bl	_vpaes_schedule_transform	# input transform
+	mtctr	r0
+
+Loop_schedule_256:
+	bl	_vpaes_schedule_mangle	# output low result
+	vmr	v6, v0			# vmovdqa	%xmm0,	%xmm6		# save cur_lo in xmm6
+
+	# high round
+	bl	_vpaes_schedule_round
+	bdz 	Lschedule_mangle_last	# dec	%esi
+	bl	_vpaes_schedule_mangle	
+
+	# low round. swap xmm7 and xmm6
+	?vspltw	v0, v0, 3		# vpshufd	\$0xFF,	%xmm0,	%xmm0
+	vmr	v5, v7			# vmovdqa	%xmm7,	%xmm5
+	vmr	v7, v6			# vmovdqa	%xmm6,	%xmm7
+	bl	_vpaes_schedule_low_round
+	vmr	v7, v5			# vmovdqa	%xmm5,	%xmm7
+	
+	b	Loop_schedule_256
+##
+##  .aes_schedule_mangle_last
+##
+##  Mangler for last round of key schedule
+##  Mangles %xmm0
+##    when encrypting, outputs out(%xmm0) ^ 63
+##    when decrypting, outputs unskew(%xmm0)
+##
+##  Always called right before return... jumps to cleanup and exits
+##
+.align	4
+Lschedule_mangle_last:
+	# schedule last round key from xmm0
+	li	r11, 0x2e0		# lea	.Lk_deskew(%rip),%r11
+	li	r9,  0x2f0
+	bne	$dir, Lschedule_mangle_last_dec
+
+	# encrypting
+	lvx	v1, r8, r10		# vmovdqa	(%r8,%r10),%xmm1
+	li	r11, 0x2c0		# lea		.Lk_opt(%rip),	%r11	# prepare to output transform
+	li	r9,  0x2d0		# prepare to output transform
+	vperm	v0, v0, v0, v1		# vpshufb	%xmm1,	%xmm0,	%xmm0	# output permute
+
+	lvx	$iptlo, r11, r12	# reload $ipt
+	lvx	$ipthi, r9, r12
+	addi	$out, $out, 16		# add	\$16,	%rdx
+	vxor	v0, v0, v26		# vpxor		.Lk_s63(%rip),	%xmm0,	%xmm0
+	bl	_vpaes_schedule_transform	# output transform
+
+	#stvx	v0, r0, $out		# vmovdqu	%xmm0,	(%rdx)		# save last key
+	vperm	v0, v0, v0, $outperm	# rotate right/left
+	li	r10, 4
+	vsel	v2, $outhead, v0, $outmask
+	li	r11, 8
+	stvx	v2, 0, $out
+	li	r12, 12
+	stvewx	v0, 0, $out		# some (or all) are redundant
+	stvewx	v0, r10, $out
+	stvewx	v0, r11, $out
+	stvewx	v0, r12, $out
+	b	Lschedule_mangle_done
+
+.align	4
+Lschedule_mangle_last_dec:
+	lvx	$iptlo, r11, r12	# reload $ipt
+	lvx	$ipthi, r9,  r12
+	addi	$out, $out, -16		# add	\$-16,	%rdx 
+	vxor	v0, v0, v26		# vpxor	.Lk_s63(%rip),	%xmm0,	%xmm0
+	bl	_vpaes_schedule_transform	# output transform
+
+	#stvx	v0, r0, $out		# vmovdqu	%xmm0,	(%rdx)		# save last key
+	addi	r9, $out, -15		# -15 is not typo
+	vperm	v0, v0, v0, $outperm	# rotate right/left
+	li	r10, 4
+	vsel	v2, $outhead, v0, $outmask
+	li	r11, 8
+	stvx	v2, 0, $out
+	li	r12, 12
+	stvewx	v0, 0, r9		# some (or all) are redundant
+	stvewx	v0, r10, r9
+	stvewx	v0, r11, r9
+	stvewx	v0, r12, r9
+
+
+Lschedule_mangle_done:
+	mtlr	r7
+	# cleanup
+	vxor	v0, v0, v0		# vpxor		%xmm0,	%xmm0,	%xmm0
+	vxor	v1, v1, v1		# vpxor		%xmm1,	%xmm1,	%xmm1
+	vxor	v2, v2, v2		# vpxor		%xmm2,	%xmm2,	%xmm2
+	vxor	v3, v3, v3		# vpxor		%xmm3,	%xmm3,	%xmm3
+	vxor	v4, v4, v4		# vpxor		%xmm4,	%xmm4,	%xmm4
+	vxor	v5, v5, v5		# vpxor		%xmm5,	%xmm5,	%xmm5
+	vxor	v6, v6, v6		# vpxor		%xmm6,	%xmm6,	%xmm6
+	vxor	v7, v7, v7		# vpxor		%xmm7,	%xmm7,	%xmm7
+
+	blr
+	.long	0
+	.byte	0,12,0x14,0,0,0,0,0
+
+##
+##  .aes_schedule_192_smear
+##
+##  Smear the short, low side in the 192-bit key schedule.
+##
+##  Inputs:
+##    %xmm7: high side, b  a  x  y
+##    %xmm6:  low side, d  c  0  0
+##    %xmm13: 0
+##
+##  Outputs:
+##    %xmm6: b+c+d  b+c  0  0
+##    %xmm0: b+c+d  b+c  b  a
+##
+.align	4
+_vpaes_schedule_192_smear:
+	?vspltw	v0, v7, 3
+	?vsldoi	v1, v9, v6, 12		# vpshufd	\$0x80,	%xmm6,	%xmm1	# d c 0 0 -> c 0 0 0
+	?vsldoi	v0, v7, v0, 8		# vpshufd	\$0xFE,	%xmm7,	%xmm0	# b a _ _ -> b b b a
+	vxor	v6, v6, v1		# vpxor		%xmm1,	%xmm6,	%xmm6	# -> c+d c 0 0
+	vxor	v6, v6, v0		# vpxor		%xmm0,	%xmm6,	%xmm6	# -> b+c+d b+c b a
+	vmr	v0, v6
+	?vsldoi	v6, v6, v9, 8
+	?vsldoi	v6, v9, v6, 8		# clobber low side with zeros
+	blr
+	.long	0
+	.byte	0,12,0x14,0,0,0,0,0
+
+##
+##  .aes_schedule_round
+##
+##  Runs one main round of the key schedule on %xmm0, %xmm7
+##
+##  Specifically, runs subbytes on the high dword of %xmm0
+##  then rotates it by one byte and xors into the low dword of
+##  %xmm7.
+##
+##  Adds rcon from low byte of %xmm8, then rotates %xmm8 for
+##  next rcon.
+##
+##  Smears the dwords of %xmm7 by xoring the low into the
+##  second low, result into third, result into highest.
+##
+##  Returns results in %xmm7 = %xmm0.
+##  Clobbers %xmm1-%xmm4, %r11.
+##
+.align	4
+_vpaes_schedule_round:
+	# extract rcon from xmm8
+	#vxor	v4, v4, v4		# vpxor		%xmm4,	%xmm4,	%xmm4
+	?vsldoi	v1, $rcon, v9, 15	# vpalignr	\$15,	%xmm8,	%xmm4,	%xmm1
+	?vsldoi	$rcon, $rcon, $rcon, 15	# vpalignr	\$15,	%xmm8,	%xmm8,	%xmm8
+	vxor	v7, v7, v1		# vpxor		%xmm1,	%xmm7,	%xmm7
+
+	# rotate
+	?vspltw	v0, v0, 3		# vpshufd	\$0xFF,	%xmm0,	%xmm0
+	?vsldoi	v0, v0, v0, 1		# vpalignr	\$1,	%xmm0,	%xmm0,	%xmm0
+
+	# fall through...
+
+	# low round: same as high round, but no rotation and no rcon.
+_vpaes_schedule_low_round:
+	# smear xmm7
+	?vsldoi	v1, v9, v7, 12		# vpslldq	\$4,	%xmm7,	%xmm1
+	vxor	v7, v7, v1		# vpxor		%xmm1,	%xmm7,	%xmm7
+	vspltisb	v1, 0x0f	# 0x0f..0f
+	?vsldoi	v4, v9, v7, 8		# vpslldq	\$8,	%xmm7,	%xmm4
+
+	# subbytes
+	vand	v1, v1, v0		# vpand		%xmm9,	%xmm0,	%xmm1		# 0 = k
+	vsrb	v0, v0, v8		# vpsrlb	\$4,	%xmm0,	%xmm0		# 1 = i
+	 vxor	v7, v7, v4		# vpxor		%xmm4,	%xmm7,	%xmm7
+	vperm	v2, $invhi, v9, v1	# vpshufb	%xmm1,	%xmm11,	%xmm2		# 2 = a/k
+	vxor	v1, v1, v0		# vpxor		%xmm0,	%xmm1,	%xmm1		# 0 = j
+	vperm	v3, $invlo, v9, v0	# vpshufb	%xmm0, 	%xmm10,	%xmm3		# 3 = 1/i
+	vxor	v3, v3, v2		# vpxor		%xmm2,	%xmm3,	%xmm3		# 3 = iak = 1/i + a/k
+	vperm	v4, $invlo, v9, v1	# vpshufb	%xmm1,	%xmm10,	%xmm4		# 4 = 1/j
+	 vxor	v7, v7, v26		# vpxor		.Lk_s63(%rip),	%xmm7,	%xmm7
+	vperm	v3, $invlo, v9, v3	# vpshufb	%xmm3,	%xmm10,	%xmm3		# 2 = 1/iak
+	vxor	v4, v4, v2		# vpxor		%xmm2,	%xmm4,	%xmm4		# 4 = jak = 1/j + a/k
+	vperm	v2, $invlo, v9, v4	# vpshufb	%xmm4,	%xmm10,	%xmm2		# 3 = 1/jak
+	vxor	v3, v3, v1		# vpxor		%xmm1,	%xmm3,	%xmm3		# 2 = io
+	vxor	v2, v2, v0		# vpxor		%xmm0,	%xmm2,	%xmm2		# 3 = jo
+	vperm	v4, v15, v9, v3		# vpshufb	%xmm3,	%xmm13,	%xmm4		# 4 = sbou
+	vperm	v1, v14, v9, v2		# vpshufb	%xmm2,	%xmm12,	%xmm1		# 0 = sb1t
+	vxor	v1, v1, v4		# vpxor		%xmm4,	%xmm1,	%xmm1		# 0 = sbox output
+
+	# add in smeared stuff
+	vxor	v0, v1, v7		# vpxor		%xmm7,	%xmm1,	%xmm0
+	vxor	v7, v1, v7		# vmovdqa	%xmm0,	%xmm7
+	blr
+	.long	0
+	.byte	0,12,0x14,0,0,0,0,0
+
+##
+##  .aes_schedule_transform
+##
+##  Linear-transform %xmm0 according to tables at (%r11)
+##
+##  Requires that %xmm9 = 0x0F0F... as in preheat
+##  Output in %xmm0
+##  Clobbers %xmm2
+##
+.align	4
+_vpaes_schedule_transform:
+	#vand	v1, v0, v9		# vpand		%xmm9,	%xmm0,	%xmm1
+	vsrb	v2, v0, v8		# vpsrlb	\$4,	%xmm0,	%xmm0
+					# vmovdqa	(%r11),	%xmm2 	# lo
+	vperm	v0, $iptlo, $iptlo, v0	# vpshufb	%xmm1,	%xmm2,	%xmm2
+					# vmovdqa	16(%r11),	%xmm1 # hi
+	vperm	v2, $ipthi, $ipthi, v2	# vpshufb	%xmm0,	%xmm1,	%xmm0
+	vxor	v0, v0, v2		# vpxor		%xmm2,	%xmm0,	%xmm0
+	blr
+	.long	0
+	.byte	0,12,0x14,0,0,0,0,0
+
+##
+##  .aes_schedule_mangle
+##
+##  Mangle xmm0 from (basis-transformed) standard version
+##  to our version.
+##
+##  On encrypt,
+##    xor with 0x63
+##    multiply by circulant 0,1,1,1
+##    apply shiftrows transform
+##
+##  On decrypt,
+##    xor with 0x63
+##    multiply by "inverse mixcolumns" circulant E,B,D,9
+##    deskew
+##    apply shiftrows transform
+##
+##
+##  Writes out to (%rdx), and increments or decrements it
+##  Keeps track of round number mod 4 in %r8
+##  Preserves xmm0
+##  Clobbers xmm1-xmm5
+##
+.align	4
+_vpaes_schedule_mangle:
+	#vmr	v4, v0			# vmovdqa	%xmm0,	%xmm4	# save xmm0 for later
+					# vmovdqa	.Lk_mc_forward(%rip),%xmm5
+	bne	$dir, Lschedule_mangle_dec
+
+	# encrypting
+	vxor	v4, v0, v26		# vpxor	.Lk_s63(%rip),	%xmm0,	%xmm4
+	addi	$out, $out, 16		# add	\$16,	%rdx
+	vperm	v4, v4, v4, v25		# vpshufb	%xmm5,	%xmm4,	%xmm4
+	vperm	v1, v4, v4, v25		# vpshufb	%xmm5,	%xmm4,	%xmm1
+	vperm	v3, v1, v1, v25		# vpshufb	%xmm5,	%xmm1,	%xmm3
+	vxor	v4, v4, v1		# vpxor		%xmm1,	%xmm4,	%xmm4
+	lvx	v1, r8, r10		# vmovdqa	(%r8,%r10),	%xmm1
+	vxor	v3, v3, v4		# vpxor		%xmm4,	%xmm3,	%xmm3
+
+	vperm	v3, v3, v3, v1		# vpshufb	%xmm1,	%xmm3,	%xmm3
+	addi	r8, r8, -16		# add	\$-16,	%r8
+	andi.	r8, r8, 0x30		# and	\$0x30,	%r8
+
+	#stvx	v3, 0, $out		# vmovdqu	%xmm3,	(%rdx)
+	vperm	v1, v3, v3, $outperm	# rotate right/left
+	vsel	v2, $outhead, v1, $outmask
+	vmr	$outhead, v1
+	stvx	v2, 0, $out
+	blr
+
+.align	4
+Lschedule_mangle_dec:
+	# inverse mix columns
+					# lea	.Lk_dksd(%rip),%r11
+	vsrb	v1, v0, v8		# vpsrlb	\$4,	%xmm4,	%xmm1	# 1 = hi
+	#and	v4, v0, v9		# vpand		%xmm9,	%xmm4,	%xmm4	# 4 = lo
+
+					# vmovdqa	0x00(%r11),	%xmm2
+	vperm	v2, v16, v16, v0	# vpshufb	%xmm4,	%xmm2,	%xmm2
+					# vmovdqa	0x10(%r11),	%xmm3
+	vperm	v3, v17, v17, v1	# vpshufb	%xmm1,	%xmm3,	%xmm3
+	vxor	v3, v3, v2		# vpxor		%xmm2,	%xmm3,	%xmm3
+	vperm	v3, v3, v9, v25		# vpshufb	%xmm5,	%xmm3,	%xmm3
+
+					# vmovdqa	0x20(%r11),	%xmm2
+	vperm	v2, v18, v18, v0	# vpshufb	%xmm4,	%xmm2,	%xmm2
+	vxor	v2, v2, v3		# vpxor		%xmm3,	%xmm2,	%xmm2
+					# vmovdqa	0x30(%r11),	%xmm3
+	vperm	v3, v19, v19, v1	# vpshufb	%xmm1,	%xmm3,	%xmm3
+	vxor	v3, v3, v2		# vpxor		%xmm2,	%xmm3,	%xmm3
+	vperm	v3, v3, v9, v25		# vpshufb	%xmm5,	%xmm3,	%xmm3
+
+					# vmovdqa	0x40(%r11),	%xmm2
+	vperm	v2, v20, v20, v0	# vpshufb	%xmm4,	%xmm2,	%xmm2
+	vxor	v2, v2, v3		# vpxor		%xmm3,	%xmm2,	%xmm2
+					# vmovdqa	0x50(%r11),	%xmm3
+	vperm	v3, v21, v21, v1	# vpshufb	%xmm1,	%xmm3,	%xmm3
+	vxor	v3, v3, v2		# vpxor		%xmm2,	%xmm3,	%xmm3
+
+					# vmovdqa	0x60(%r11),	%xmm2
+	vperm	v2, v22, v22, v0	# vpshufb	%xmm4,	%xmm2,	%xmm2
+	vperm	v3, v3, v9, v25		# vpshufb	%xmm5,	%xmm3,	%xmm3
+					# vmovdqa	0x70(%r11),	%xmm4
+	vperm	v4, v23, v23, v1	# vpshufb	%xmm1,	%xmm4,	%xmm4
+	lvx	v1, r8, r10		# vmovdqa	(%r8,%r10),	%xmm1
+	vxor	v2, v2, v3		# vpxor		%xmm3,	%xmm2,	%xmm2
+	vxor	v3, v4, v2		# vpxor		%xmm2,	%xmm4,	%xmm3
+
+	addi	$out, $out, -16		# add	\$-16,	%rdx
+
+	vperm	v3, v3, v3, v1		# vpshufb	%xmm1,	%xmm3,	%xmm3
+	addi	r8, r8, -16		# add	\$-16,	%r8
+	andi.	r8, r8, 0x30		# and	\$0x30,	%r8
+
+	#stvx	v3, 0, $out		# vmovdqu	%xmm3,	(%rdx)
+	vperm	v1, v3, v3, $outperm	# rotate right/left
+	vsel	v2, $outhead, v1, $outmask
+	vmr	$outhead, v1
+	stvx	v2, 0, $out
+	blr
+	.long	0
+	.byte	0,12,0x14,0,0,0,0,0
+
+.globl	.vpaes_set_encrypt_key
+.align	5
+.vpaes_set_encrypt_key:
+	$STU	$sp,-$FRAME($sp)
+	li	r10,`15+6*$SIZE_T`
+	li	r11,`31+6*$SIZE_T`
+	mflr	r0
+	mfspr	r6, 256			# save vrsave
+	stvx	v20,r10,$sp
+	addi	r10,r10,32
+	stvx	v21,r11,$sp
+	addi	r11,r11,32
+	stvx	v22,r10,$sp
+	addi	r10,r10,32
+	stvx	v23,r11,$sp
+	addi	r11,r11,32
+	stvx	v24,r10,$sp
+	addi	r10,r10,32
+	stvx	v25,r11,$sp
+	addi	r11,r11,32
+	stvx	v26,r10,$sp
+	addi	r10,r10,32
+	stvx	v27,r11,$sp
+	addi	r11,r11,32
+	stvx	v28,r10,$sp
+	addi	r10,r10,32
+	stvx	v29,r11,$sp
+	addi	r11,r11,32
+	stvx	v30,r10,$sp
+	stvx	v31,r11,$sp
+	stw	r6,`$FRAME-4`($sp)	# save vrsave
+	li	r7, -1
+	$PUSH	r0, `$FRAME+$LRSAVE`($sp)
+	mtspr	256, r7			# preserve all AltiVec registers
+
+	srwi	r9, $bits, 5		# shr	\$5,%eax
+	addi	r9, r9, 6		# add	\$5,%eax
+	stw	r9, 240($out)		# mov	%eax,240(%rdx)	# AES_KEY->rounds = nbits/32+5;
+
+	cmplw	$dir, $bits, $bits	# set encrypt direction
+	li	r8, 0x30		# mov	\$0x30,%r8d
+	bl	_vpaes_schedule_core
+
+	$POP	r0, `$FRAME+$LRSAVE`($sp)
+	li	r10,`15+6*$SIZE_T`
+	li	r11,`31+6*$SIZE_T`
+	mtspr	256, r6			# restore vrsave
+	mtlr	r0
+	xor	r3, r3, r3
+	lvx	v20,r10,$sp
+	addi	r10,r10,32
+	lvx	v21,r11,$sp
+	addi	r11,r11,32
+	lvx	v22,r10,$sp
+	addi	r10,r10,32
+	lvx	v23,r11,$sp
+	addi	r11,r11,32
+	lvx	v24,r10,$sp
+	addi	r10,r10,32
+	lvx	v25,r11,$sp
+	addi	r11,r11,32
+	lvx	v26,r10,$sp
+	addi	r10,r10,32
+	lvx	v27,r11,$sp
+	addi	r11,r11,32
+	lvx	v28,r10,$sp
+	addi	r10,r10,32
+	lvx	v29,r11,$sp
+	addi	r11,r11,32
+	lvx	v30,r10,$sp
+	lvx	v31,r11,$sp
+	addi	$sp,$sp,$FRAME
+	blr
+	.long	0
+	.byte	0,12,0x04,1,0x80,0,3,0
+	.long	0
+.size	.vpaes_set_encrypt_key,.-.vpaes_set_encrypt_key
+
+.globl	.vpaes_set_decrypt_key
+.align	4
+.vpaes_set_decrypt_key:
+	$STU	$sp,-$FRAME($sp)
+	li	r10,`15+6*$SIZE_T`
+	li	r11,`31+6*$SIZE_T`
+	mflr	r0
+	mfspr	r6, 256			# save vrsave
+	stvx	v20,r10,$sp
+	addi	r10,r10,32
+	stvx	v21,r11,$sp
+	addi	r11,r11,32
+	stvx	v22,r10,$sp
+	addi	r10,r10,32
+	stvx	v23,r11,$sp
+	addi	r11,r11,32
+	stvx	v24,r10,$sp
+	addi	r10,r10,32
+	stvx	v25,r11,$sp
+	addi	r11,r11,32
+	stvx	v26,r10,$sp
+	addi	r10,r10,32
+	stvx	v27,r11,$sp
+	addi	r11,r11,32
+	stvx	v28,r10,$sp
+	addi	r10,r10,32
+	stvx	v29,r11,$sp
+	addi	r11,r11,32
+	stvx	v30,r10,$sp
+	stvx	v31,r11,$sp
+	stw	r6,`$FRAME-4`($sp)	# save vrsave
+	li	r7, -1
+	$PUSH	r0, `$FRAME+$LRSAVE`($sp)
+	mtspr	256, r7			# preserve all AltiVec registers
+
+	srwi	r9, $bits, 5		# shr	\$5,%eax
+	addi	r9, r9, 6		# add	\$5,%eax
+	stw	r9, 240($out)		# mov	%eax,240(%rdx)	# AES_KEY->rounds = nbits/32+5;
+
+	slwi	r9, r9, 4		# shl	\$4,%eax
+	add	$out, $out, r9		# lea	(%rdx,%rax),%rdx
+
+	cmplwi	$dir, $bits, 0		# set decrypt direction
+	srwi	r8, $bits, 1		# shr	\$1,%r8d
+	andi.	r8, r8, 32		# and	\$32,%r8d
+	xori	r8, r8, 32		# xor	\$32,%r8d	# nbits==192?0:32
+	bl	_vpaes_schedule_core
+
+	$POP	r0,  `$FRAME+$LRSAVE`($sp)
+	li	r10,`15+6*$SIZE_T`
+	li	r11,`31+6*$SIZE_T`
+	mtspr	256, r6			# restore vrsave
+	mtlr	r0
+	xor	r3, r3, r3
+	lvx	v20,r10,$sp
+	addi	r10,r10,32
+	lvx	v21,r11,$sp
+	addi	r11,r11,32
+	lvx	v22,r10,$sp
+	addi	r10,r10,32
+	lvx	v23,r11,$sp
+	addi	r11,r11,32
+	lvx	v24,r10,$sp
+	addi	r10,r10,32
+	lvx	v25,r11,$sp
+	addi	r11,r11,32
+	lvx	v26,r10,$sp
+	addi	r10,r10,32
+	lvx	v27,r11,$sp
+	addi	r11,r11,32
+	lvx	v28,r10,$sp
+	addi	r10,r10,32
+	lvx	v29,r11,$sp
+	addi	r11,r11,32
+	lvx	v30,r10,$sp
+	lvx	v31,r11,$sp
+	addi	$sp,$sp,$FRAME
+	blr
+	.long	0
+	.byte	0,12,0x04,1,0x80,0,3,0
+	.long	0
+.size	.vpaes_set_decrypt_key,.-.vpaes_set_decrypt_key
+___
+}
+
+my $consts=1;
+foreach  (split("\n",$code)) {
+	s/\`([^\`]*)\`/eval $1/geo;
+
+	# constants table endian-specific conversion
+	if ($consts && m/\.long\s+(.+)\s+(\?[a-z]*)$/o) {
+	    my $conv=$2;
+	    my @bytes=();
+
+	    # convert to endian-agnostic format
+	    foreach (split(/,\s+/,$1)) {
+		my $l = /^0/?oct:int;
+		push @bytes,($l>>24)&0xff,($l>>16)&0xff,($l>>8)&0xff,$l&0xff;
+	    }
+
+	    # little-endian conversion
+	    if ($flavour =~ /le$/o) {
+		SWITCH: for($conv)  {
+		    /\?inv/ && do   { @bytes=map($_^0xf,@bytes); last; };
+		    /\?rev/ && do   { @bytes=reverse(@bytes);    last; }; 
+		}
+	    }
+
+	    #emit
+	    print ".byte\t",join(',',map (sprintf("0x%02x",$_),@bytes)),"\n";
+	    next;
+	}
+	$consts=0 if (m/Lconsts:/o);	# end of table
+
+	# instructions prefixed with '?' are endian-specific and need
+	# to be adjusted accordingly...
+	if ($flavour =~ /le$/o) {	# little-endian
+	    s/\?lvsr/lvsl/o or
+	    s/\?lvsl/lvsr/o or
+	    s/\?(vperm\s+v[0-9]+,\s*)(v[0-9]+,\s*)(v[0-9]+,\s*)(v[0-9]+)/$1$3$2$4/o or
+	    s/\?(vsldoi\s+v[0-9]+,\s*)(v[0-9]+,)\s*(v[0-9]+,\s*)([0-9]+)/$1$3$2 16-$4/o or
+	    s/\?(vspltw\s+v[0-9]+,\s*)(v[0-9]+,)\s*([0-9])/$1$2 3-$3/o;
+	} else {			# big-endian
+	    s/\?([a-z]+)/$1/o;
+	}
+
+	print $_,"\n";
+}
+
+close STDOUT;
diff --git a/crypto/aes/asm/vpaes-x86.pl b/crypto/aes/asm/vpaes-x86.pl
new file mode 100644
index 000000000..47615c079
--- /dev/null
+++ b/crypto/aes/asm/vpaes-x86.pl
@@ -0,0 +1,916 @@
+#! /usr/bin/env perl
+# Copyright 2011-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+
+######################################################################
+## Constant-time SSSE3 AES core implementation.
+## version 0.1
+##
+## By Mike Hamburg (Stanford University), 2009
+## Public domain.
+##
+## For details see http://shiftleft.org/papers/vector_aes/ and
+## http://crypto.stanford.edu/vpaes/.
+
+######################################################################
+# September 2011.
+#
+# Port vpaes-x86_64.pl as 32-bit "almost" drop-in replacement for
+# aes-586.pl. "Almost" refers to the fact that AES_cbc_encrypt
+# doesn't handle partial vectors (doesn't have to if called from
+# EVP only). "Drop-in" implies that this module doesn't share key
+# schedule structure with the original nor does it make assumption
+# about its alignment...
+#
+# Performance summary. aes-586.pl column lists large-block CBC
+# encrypt/decrypt/with-hyper-threading-off(*) results in cycles per
+# byte processed with 128-bit key, and vpaes-x86.pl column - [also
+# large-block CBC] encrypt/decrypt.
+#
+#		aes-586.pl		vpaes-x86.pl
+#
+# Core 2(**)	28.1/41.4/18.3		21.9/25.2(***)
+# Nehalem	27.9/40.4/18.1		10.2/11.9
+# Atom		70.7/92.1/60.1		61.1/75.4(***)
+# Silvermont	45.4/62.9/24.1		49.2/61.1(***)
+#
+# (*)	"Hyper-threading" in the context refers rather to cache shared
+#	among multiple cores, than to specifically Intel HTT. As vast
+#	majority of contemporary cores share cache, slower code path
+#	is common place. In other words "with-hyper-threading-off"
+#	results are presented mostly for reference purposes.
+#
+# (**)	"Core 2" refers to initial 65nm design, a.k.a. Conroe.
+#
+# (***)	Less impressive improvement on Core 2 and Atom is due to slow
+#	pshufb,	yet it's respectable +28%/64%  improvement on Core 2
+#	and +15% on Atom (as implied, over "hyper-threading-safe"
+#	code path).
+#
+#						
+
+$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+push(@INC,"${dir}","${dir}../../perlasm");
+require "x86asm.pl";
+
+$output = pop;
+open OUT,">$output";
+*STDOUT=*OUT;
+
+&asm_init($ARGV[0],"vpaes-x86.pl",$x86only = $ARGV[$#ARGV] eq "386");
+
+$PREFIX="vpaes";
+
+my  ($round, $base, $magic, $key, $const, $inp, $out)=
+    ("eax",  "ebx", "ecx",  "edx","ebp",  "esi","edi");
+
+&static_label("_vpaes_consts");
+&static_label("_vpaes_schedule_low_round");
+
+&set_label("_vpaes_consts",64);
+$k_inv=-0x30;		# inv, inva
+	&data_word(0x0D080180,0x0E05060F,0x0A0B0C02,0x04070309);
+	&data_word(0x0F0B0780,0x01040A06,0x02050809,0x030D0E0C);
+
+$k_s0F=-0x10;		# s0F
+	&data_word(0x0F0F0F0F,0x0F0F0F0F,0x0F0F0F0F,0x0F0F0F0F);
+
+$k_ipt=0x00;		# input transform (lo, hi)
+	&data_word(0x5A2A7000,0xC2B2E898,0x52227808,0xCABAE090);
+	&data_word(0x317C4D00,0x4C01307D,0xB0FDCC81,0xCD80B1FC);
+
+$k_sb1=0x20;		# sb1u, sb1t
+	&data_word(0xCB503E00,0xB19BE18F,0x142AF544,0xA5DF7A6E);
+	&data_word(0xFAE22300,0x3618D415,0x0D2ED9EF,0x3BF7CCC1);
+$k_sb2=0x40;		# sb2u, sb2t
+	&data_word(0x0B712400,0xE27A93C6,0xBC982FCD,0x5EB7E955);
+	&data_word(0x0AE12900,0x69EB8840,0xAB82234A,0xC2A163C8);
+$k_sbo=0x60;		# sbou, sbot
+	&data_word(0x6FBDC700,0xD0D26D17,0xC502A878,0x15AABF7A);
+	&data_word(0x5FBB6A00,0xCFE474A5,0x412B35FA,0x8E1E90D1);
+
+$k_mc_forward=0x80;	# mc_forward
+	&data_word(0x00030201,0x04070605,0x080B0A09,0x0C0F0E0D);
+	&data_word(0x04070605,0x080B0A09,0x0C0F0E0D,0x00030201);
+	&data_word(0x080B0A09,0x0C0F0E0D,0x00030201,0x04070605);
+	&data_word(0x0C0F0E0D,0x00030201,0x04070605,0x080B0A09);
+
+$k_mc_backward=0xc0;	# mc_backward
+	&data_word(0x02010003,0x06050407,0x0A09080B,0x0E0D0C0F);
+	&data_word(0x0E0D0C0F,0x02010003,0x06050407,0x0A09080B);
+	&data_word(0x0A09080B,0x0E0D0C0F,0x02010003,0x06050407);
+	&data_word(0x06050407,0x0A09080B,0x0E0D0C0F,0x02010003);
+
+$k_sr=0x100;		# sr
+	&data_word(0x03020100,0x07060504,0x0B0A0908,0x0F0E0D0C);
+	&data_word(0x0F0A0500,0x030E0904,0x07020D08,0x0B06010C);
+	&data_word(0x0B020900,0x0F060D04,0x030A0108,0x070E050C);
+	&data_word(0x070A0D00,0x0B0E0104,0x0F020508,0x0306090C);
+
+$k_rcon=0x140;		# rcon
+	&data_word(0xAF9DEEB6,0x1F8391B9,0x4D7C7D81,0x702A9808);
+
+$k_s63=0x150;		# s63: all equal to 0x63 transformed
+	&data_word(0x5B5B5B5B,0x5B5B5B5B,0x5B5B5B5B,0x5B5B5B5B);
+
+$k_opt=0x160;		# output transform
+	&data_word(0xD6B66000,0xFF9F4929,0xDEBE6808,0xF7974121);
+	&data_word(0x50BCEC00,0x01EDBD51,0xB05C0CE0,0xE10D5DB1);
+
+$k_deskew=0x180;	# deskew tables: inverts the sbox's "skew"
+	&data_word(0x47A4E300,0x07E4A340,0x5DBEF91A,0x1DFEB95A);
+	&data_word(0x83EA6900,0x5F36B5DC,0xF49D1E77,0x2841C2AB);
+##
+##  Decryption stuff
+##  Key schedule constants
+##
+$k_dksd=0x1a0;		# decryption key schedule: invskew x*D
+	&data_word(0xA3E44700,0xFEB91A5D,0x5A1DBEF9,0x0740E3A4);
+	&data_word(0xB5368300,0x41C277F4,0xAB289D1E,0x5FDC69EA);
+$k_dksb=0x1c0;		# decryption key schedule: invskew x*B
+	&data_word(0x8550D500,0x9A4FCA1F,0x1CC94C99,0x03D65386);
+	&data_word(0xB6FC4A00,0x115BEDA7,0x7E3482C8,0xD993256F);
+$k_dkse=0x1e0;		# decryption key schedule: invskew x*E + 0x63
+	&data_word(0x1FC9D600,0xD5031CCA,0x994F5086,0x53859A4C);
+	&data_word(0x4FDC7BE8,0xA2319605,0x20B31487,0xCD5EF96A);
+$k_dks9=0x200;		# decryption key schedule: invskew x*9
+	&data_word(0x7ED9A700,0xB6116FC8,0x82255BFC,0x4AED9334);
+	&data_word(0x27143300,0x45765162,0xE9DAFDCE,0x8BB89FAC);
+
+##
+##  Decryption stuff
+##  Round function constants
+##
+$k_dipt=0x220;		# decryption input transform
+	&data_word(0x0B545F00,0x0F505B04,0x114E451A,0x154A411E);
+	&data_word(0x60056500,0x86E383E6,0xF491F194,0x12771772);
+
+$k_dsb9=0x240;		# decryption sbox output *9*u, *9*t
+	&data_word(0x9A86D600,0x851C0353,0x4F994CC9,0xCAD51F50);
+	&data_word(0xECD74900,0xC03B1789,0xB2FBA565,0x725E2C9E);
+$k_dsbd=0x260;		# decryption sbox output *D*u, *D*t
+	&data_word(0xE6B1A200,0x7D57CCDF,0x882A4439,0xF56E9B13);
+	&data_word(0x24C6CB00,0x3CE2FAF7,0x15DEEFD3,0x2931180D);
+$k_dsbb=0x280;		# decryption sbox output *B*u, *B*t
+	&data_word(0x96B44200,0xD0226492,0xB0F2D404,0x602646F6);
+	&data_word(0xCD596700,0xC19498A6,0x3255AA6B,0xF3FF0C3E);
+$k_dsbe=0x2a0;		# decryption sbox output *E*u, *E*t
+	&data_word(0x26D4D000,0x46F29296,0x64B4F6B0,0x22426004);
+	&data_word(0xFFAAC100,0x0C55A6CD,0x98593E32,0x9467F36B);
+$k_dsbo=0x2c0;		# decryption sbox final output
+	&data_word(0x7EF94000,0x1387EA53,0xD4943E2D,0xC7AA6DB9);
+	&data_word(0x93441D00,0x12D7560F,0xD8C58E9C,0xCA4B8159);
+&asciz	("Vector Permutation AES for x86/SSSE3, Mike Hamburg (Stanford University)");
+&align	(64);
+
+&function_begin_B("_vpaes_preheat");
+	&add	($const,&DWP(0,"esp"));
+	&movdqa	("xmm7",&QWP($k_inv,$const));
+	&movdqa	("xmm6",&QWP($k_s0F,$const));
+	&ret	();
+&function_end_B("_vpaes_preheat");
+
+##
+##  _aes_encrypt_core
+##
+##  AES-encrypt %xmm0.
+##
+##  Inputs:
+##     %xmm0 = input
+##     %xmm6-%xmm7 as in _vpaes_preheat
+##    (%edx) = scheduled keys
+##
+##  Output in %xmm0
+##  Clobbers  %xmm1-%xmm5, %eax, %ebx, %ecx, %edx
+##
+##
+&function_begin_B("_vpaes_encrypt_core");
+	&mov	($magic,16);
+	&mov	($round,&DWP(240,$key));
+	&movdqa	("xmm1","xmm6")
+	&movdqa	("xmm2",&QWP($k_ipt,$const));
+	&pandn	("xmm1","xmm0");
+	&pand	("xmm0","xmm6");
+	&movdqu	("xmm5",&QWP(0,$key));
+	&pshufb	("xmm2","xmm0");
+	&movdqa	("xmm0",&QWP($k_ipt+16,$const));
+	&pxor	("xmm2","xmm5");
+	&psrld	("xmm1",4);
+	&add	($key,16);
+	&pshufb	("xmm0","xmm1");
+	&lea	($base,&DWP($k_mc_backward,$const));
+	&pxor	("xmm0","xmm2");
+	&jmp	(&label("enc_entry"));
+
+
+&set_label("enc_loop",16);
+	# middle of middle round
+	&movdqa	("xmm4",&QWP($k_sb1,$const));	# 4 : sb1u
+	&movdqa	("xmm0",&QWP($k_sb1+16,$const));# 0 : sb1t
+	&pshufb	("xmm4","xmm2");		# 4 = sb1u
+	&pshufb	("xmm0","xmm3");		# 0 = sb1t
+	&pxor	("xmm4","xmm5");		# 4 = sb1u + k
+	&movdqa	("xmm5",&QWP($k_sb2,$const));	# 4 : sb2u
+	&pxor	("xmm0","xmm4");		# 0 = A
+	&movdqa	("xmm1",&QWP(-0x40,$base,$magic));# .Lk_mc_forward[]
+	&pshufb	("xmm5","xmm2");		# 4 = sb2u
+	&movdqa	("xmm2",&QWP($k_sb2+16,$const));# 2 : sb2t
+	&movdqa	("xmm4",&QWP(0,$base,$magic));	# .Lk_mc_backward[]
+	&pshufb	("xmm2","xmm3");		# 2 = sb2t
+	&movdqa	("xmm3","xmm0");		# 3 = A
+	&pxor	("xmm2","xmm5");		# 2 = 2A
+	&pshufb	("xmm0","xmm1");		# 0 = B
+	&add	($key,16);			# next key
+	&pxor	("xmm0","xmm2");		# 0 = 2A+B
+	&pshufb	("xmm3","xmm4");		# 3 = D
+	&add	($magic,16);			# next mc
+	&pxor	("xmm3","xmm0");		# 3 = 2A+B+D
+	&pshufb	("xmm0","xmm1");		# 0 = 2B+C
+	&and	($magic,0x30);			# ... mod 4
+	&sub	($round,1);			# nr--
+	&pxor	("xmm0","xmm3");		# 0 = 2A+3B+C+D
+
+&set_label("enc_entry");
+	# top of round
+	&movdqa	("xmm1","xmm6");		# 1 : i
+	&movdqa	("xmm5",&QWP($k_inv+16,$const));# 2 : a/k
+	&pandn	("xmm1","xmm0");		# 1 = i<<4
+	&psrld	("xmm1",4);			# 1 = i
+	&pand	("xmm0","xmm6");		# 0 = k
+	&pshufb	("xmm5","xmm0");		# 2 = a/k
+	&movdqa	("xmm3","xmm7");		# 3 : 1/i
+	&pxor	("xmm0","xmm1");		# 0 = j
+	&pshufb	("xmm3","xmm1");		# 3 = 1/i
+	&movdqa	("xmm4","xmm7");		# 4 : 1/j
+	&pxor	("xmm3","xmm5");		# 3 = iak = 1/i + a/k
+	&pshufb	("xmm4","xmm0");		# 4 = 1/j
+	&movdqa	("xmm2","xmm7");		# 2 : 1/iak
+	&pxor	("xmm4","xmm5");		# 4 = jak = 1/j + a/k
+	&pshufb	("xmm2","xmm3");		# 2 = 1/iak
+	&movdqa	("xmm3","xmm7");		# 3 : 1/jak
+	&pxor	("xmm2","xmm0");		# 2 = io
+	&pshufb	("xmm3","xmm4");		# 3 = 1/jak
+	&movdqu	("xmm5",&QWP(0,$key));
+	&pxor	("xmm3","xmm1");		# 3 = jo
+	&jnz	(&label("enc_loop"));
+
+	# middle of last round
+	&movdqa	("xmm4",&QWP($k_sbo,$const));	# 3 : sbou      .Lk_sbo
+	&movdqa	("xmm0",&QWP($k_sbo+16,$const));# 3 : sbot      .Lk_sbo+16
+	&pshufb	("xmm4","xmm2");		# 4 = sbou
+	&pxor	("xmm4","xmm5");		# 4 = sb1u + k
+	&pshufb	("xmm0","xmm3");		# 0 = sb1t
+	&movdqa	("xmm1",&QWP(0x40,$base,$magic));# .Lk_sr[]
+	&pxor	("xmm0","xmm4");		# 0 = A
+	&pshufb	("xmm0","xmm1");
+	&ret	();
+&function_end_B("_vpaes_encrypt_core");
+
+##
+##  Decryption core
+##
+##  Same API as encryption core.
+##
+&function_begin_B("_vpaes_decrypt_core");
+	&lea	($base,&DWP($k_dsbd,$const));
+	&mov	($round,&DWP(240,$key));
+	&movdqa	("xmm1","xmm6");
+	&movdqa	("xmm2",&QWP($k_dipt-$k_dsbd,$base));
+	&pandn	("xmm1","xmm0");
+	&mov	($magic,$round);
+	&psrld	("xmm1",4)
+	&movdqu	("xmm5",&QWP(0,$key));
+	&shl	($magic,4);
+	&pand	("xmm0","xmm6");
+	&pshufb	("xmm2","xmm0");
+	&movdqa	("xmm0",&QWP($k_dipt-$k_dsbd+16,$base));
+	&xor	($magic,0x30);
+	&pshufb	("xmm0","xmm1");
+	&and	($magic,0x30);
+	&pxor	("xmm2","xmm5");
+	&movdqa	("xmm5",&QWP($k_mc_forward+48,$const));
+	&pxor	("xmm0","xmm2");
+	&add	($key,16);
+	&lea	($magic,&DWP($k_sr-$k_dsbd,$base,$magic));
+	&jmp	(&label("dec_entry"));
+
+&set_label("dec_loop",16);
+##
+##  Inverse mix columns
+##
+	&movdqa	("xmm4",&QWP(-0x20,$base));	# 4 : sb9u
+	&movdqa	("xmm1",&QWP(-0x10,$base));	# 0 : sb9t
+	&pshufb	("xmm4","xmm2");		# 4 = sb9u
+	&pshufb	("xmm1","xmm3");		# 0 = sb9t
+	&pxor	("xmm0","xmm4");
+	&movdqa	("xmm4",&QWP(0,$base));		# 4 : sbdu
+	&pxor	("xmm0","xmm1");		# 0 = ch
+	&movdqa	("xmm1",&QWP(0x10,$base));	# 0 : sbdt
+
+	&pshufb	("xmm4","xmm2");		# 4 = sbdu
+	&pshufb	("xmm0","xmm5");		# MC ch
+	&pshufb	("xmm1","xmm3");		# 0 = sbdt
+	&pxor	("xmm0","xmm4");		# 4 = ch
+	&movdqa	("xmm4",&QWP(0x20,$base));	# 4 : sbbu
+	&pxor	("xmm0","xmm1");		# 0 = ch
+	&movdqa	("xmm1",&QWP(0x30,$base));	# 0 : sbbt
+
+	&pshufb	("xmm4","xmm2");		# 4 = sbbu
+	&pshufb	("xmm0","xmm5");		# MC ch
+	&pshufb	("xmm1","xmm3");		# 0 = sbbt
+	&pxor	("xmm0","xmm4");		# 4 = ch
+	&movdqa	("xmm4",&QWP(0x40,$base));	# 4 : sbeu
+	&pxor	("xmm0","xmm1");		# 0 = ch
+	&movdqa	("xmm1",&QWP(0x50,$base));	# 0 : sbet
+
+	&pshufb	("xmm4","xmm2");		# 4 = sbeu
+	&pshufb	("xmm0","xmm5");		# MC ch
+	&pshufb	("xmm1","xmm3");		# 0 = sbet
+	&pxor	("xmm0","xmm4");		# 4 = ch
+	&add	($key,16);			# next round key
+	&palignr("xmm5","xmm5",12);
+	&pxor	("xmm0","xmm1");		# 0 = ch
+	&sub	($round,1);			# nr--
+
+&set_label("dec_entry");
+	# top of round
+	&movdqa	("xmm1","xmm6");		# 1 : i
+	&movdqa	("xmm2",&QWP($k_inv+16,$const));# 2 : a/k
+	&pandn	("xmm1","xmm0");		# 1 = i<<4
+	&pand	("xmm0","xmm6");		# 0 = k
+	&psrld	("xmm1",4);			# 1 = i
+	&pshufb	("xmm2","xmm0");		# 2 = a/k
+	&movdqa	("xmm3","xmm7");		# 3 : 1/i
+	&pxor	("xmm0","xmm1");		# 0 = j
+	&pshufb	("xmm3","xmm1");		# 3 = 1/i
+	&movdqa	("xmm4","xmm7");		# 4 : 1/j
+	&pxor	("xmm3","xmm2");		# 3 = iak = 1/i + a/k
+	&pshufb	("xmm4","xmm0");		# 4 = 1/j
+	&pxor	("xmm4","xmm2");		# 4 = jak = 1/j + a/k
+	&movdqa	("xmm2","xmm7");		# 2 : 1/iak
+	&pshufb	("xmm2","xmm3");		# 2 = 1/iak
+	&movdqa	("xmm3","xmm7");		# 3 : 1/jak
+	&pxor	("xmm2","xmm0");		# 2 = io
+	&pshufb	("xmm3","xmm4");		# 3 = 1/jak
+	&movdqu	("xmm0",&QWP(0,$key));
+	&pxor	("xmm3","xmm1");		# 3 = jo
+	&jnz	(&label("dec_loop"));
+
+	# middle of last round
+	&movdqa	("xmm4",&QWP(0x60,$base));	# 3 : sbou
+	&pshufb	("xmm4","xmm2");		# 4 = sbou
+	&pxor	("xmm4","xmm0");		# 4 = sb1u + k
+	&movdqa	("xmm0",&QWP(0x70,$base));	# 0 : sbot
+	&movdqa	("xmm2",&QWP(0,$magic));
+	&pshufb	("xmm0","xmm3");		# 0 = sb1t
+	&pxor	("xmm0","xmm4");		# 0 = A
+	&pshufb	("xmm0","xmm2");
+	&ret	();
+&function_end_B("_vpaes_decrypt_core");
+
+########################################################
+##                                                    ##
+##                  AES key schedule                  ##
+##                                                    ##
+########################################################
+&function_begin_B("_vpaes_schedule_core");
+	&add	($const,&DWP(0,"esp"));
+	&movdqu	("xmm0",&QWP(0,$inp));		# load key (unaligned)
+	&movdqa	("xmm2",&QWP($k_rcon,$const));	# load rcon
+
+	# input transform
+	&movdqa	("xmm3","xmm0");
+	&lea	($base,&DWP($k_ipt,$const));
+	&movdqa	(&QWP(4,"esp"),"xmm2");		# xmm8
+	&call	("_vpaes_schedule_transform");
+	&movdqa	("xmm7","xmm0");
+
+	&test	($out,$out);
+	&jnz	(&label("schedule_am_decrypting"));
+
+	# encrypting, output zeroth round key after transform
+	&movdqu	(&QWP(0,$key),"xmm0");
+	&jmp	(&label("schedule_go"));
+
+&set_label("schedule_am_decrypting");
+	# decrypting, output zeroth round key after shiftrows
+	&movdqa	("xmm1",&QWP($k_sr,$const,$magic));
+	&pshufb	("xmm3","xmm1");
+	&movdqu	(&QWP(0,$key),"xmm3");
+	&xor	($magic,0x30);
+
+&set_label("schedule_go");
+	&cmp	($round,192);
+	&ja	(&label("schedule_256"));
+	&je	(&label("schedule_192"));
+	# 128: fall though
+
+##
+##  .schedule_128
+##
+##  128-bit specific part of key schedule.
+##
+##  This schedule is really simple, because all its parts
+##  are accomplished by the subroutines.
+##
+&set_label("schedule_128");
+	&mov	($round,10);
+
+&set_label("loop_schedule_128");
+	&call	("_vpaes_schedule_round");
+	&dec	($round);
+	&jz	(&label("schedule_mangle_last"));
+	&call	("_vpaes_schedule_mangle");	# write output
+	&jmp	(&label("loop_schedule_128"));
+
+##
+##  .aes_schedule_192
+##
+##  192-bit specific part of key schedule.
+##
+##  The main body of this schedule is the same as the 128-bit
+##  schedule, but with more smearing.  The long, high side is
+##  stored in %xmm7 as before, and the short, low side is in
+##  the high bits of %xmm6.
+##
+##  This schedule is somewhat nastier, however, because each
+##  round produces 192 bits of key material, or 1.5 round keys.
+##  Therefore, on each cycle we do 2 rounds and produce 3 round
+##  keys.
+##
+&set_label("schedule_192",16);
+	&movdqu	("xmm0",&QWP(8,$inp));		# load key part 2 (very unaligned)
+	&call	("_vpaes_schedule_transform");	# input transform	
+	&movdqa	("xmm6","xmm0");		# save short part
+	&pxor	("xmm4","xmm4");		# clear 4
+	&movhlps("xmm6","xmm4");		# clobber low side with zeros
+	&mov	($round,4);
+
+&set_label("loop_schedule_192");
+	&call	("_vpaes_schedule_round");
+	&palignr("xmm0","xmm6",8);
+	&call	("_vpaes_schedule_mangle");	# save key n
+	&call	("_vpaes_schedule_192_smear");
+	&call	("_vpaes_schedule_mangle");	# save key n+1
+	&call	("_vpaes_schedule_round");
+	&dec	($round);
+	&jz	(&label("schedule_mangle_last"));
+	&call	("_vpaes_schedule_mangle");	# save key n+2
+	&call	("_vpaes_schedule_192_smear");
+	&jmp	(&label("loop_schedule_192"));
+
+##
+##  .aes_schedule_256
+##
+##  256-bit specific part of key schedule.
+##
+##  The structure here is very similar to the 128-bit
+##  schedule, but with an additional "low side" in
+##  %xmm6.  The low side's rounds are the same as the
+##  high side's, except no rcon and no rotation.
+##
+&set_label("schedule_256",16);
+	&movdqu	("xmm0",&QWP(16,$inp));		# load key part 2 (unaligned)
+	&call	("_vpaes_schedule_transform");	# input transform	
+	&mov	($round,7);
+
+&set_label("loop_schedule_256");
+	&call	("_vpaes_schedule_mangle");	# output low result
+	&movdqa	("xmm6","xmm0");		# save cur_lo in xmm6
+
+	# high round
+	&call	("_vpaes_schedule_round");
+	&dec	($round);
+	&jz	(&label("schedule_mangle_last"));
+	&call	("_vpaes_schedule_mangle");	
+
+	# low round. swap xmm7 and xmm6
+	&pshufd	("xmm0","xmm0",0xFF);
+	&movdqa	(&QWP(20,"esp"),"xmm7");
+	&movdqa	("xmm7","xmm6");
+	&call	("_vpaes_schedule_low_round");
+	&movdqa	("xmm7",&QWP(20,"esp"));
+
+	&jmp	(&label("loop_schedule_256"));
+
+##
+##  .aes_schedule_mangle_last
+##
+##  Mangler for last round of key schedule
+##  Mangles %xmm0
+##    when encrypting, outputs out(%xmm0) ^ 63
+##    when decrypting, outputs unskew(%xmm0)
+##
+##  Always called right before return... jumps to cleanup and exits
+##
+&set_label("schedule_mangle_last",16);
+	# schedule last round key from xmm0
+	&lea	($base,&DWP($k_deskew,$const));
+	&test	($out,$out);
+	&jnz	(&label("schedule_mangle_last_dec"));
+
+	# encrypting
+	&movdqa	("xmm1",&QWP($k_sr,$const,$magic));
+	&pshufb	("xmm0","xmm1");		# output permute
+	&lea	($base,&DWP($k_opt,$const));	# prepare to output transform
+	&add	($key,32);
+
+&set_label("schedule_mangle_last_dec");
+	&add	($key,-16);
+	&pxor	("xmm0",&QWP($k_s63,$const));
+	&call	("_vpaes_schedule_transform");	# output transform
+	&movdqu	(&QWP(0,$key),"xmm0");		# save last key
+
+	# cleanup
+	&pxor	("xmm0","xmm0");
+	&pxor	("xmm1","xmm1");
+	&pxor	("xmm2","xmm2");
+	&pxor	("xmm3","xmm3");
+	&pxor	("xmm4","xmm4");
+	&pxor	("xmm5","xmm5");
+	&pxor	("xmm6","xmm6");
+	&pxor	("xmm7","xmm7");
+	&ret	();
+&function_end_B("_vpaes_schedule_core");
+
+##
+##  .aes_schedule_192_smear
+##
+##  Smear the short, low side in the 192-bit key schedule.
+##
+##  Inputs:
+##    %xmm7: high side, b  a  x  y
+##    %xmm6:  low side, d  c  0  0
+##    %xmm13: 0
+##
+##  Outputs:
+##    %xmm6: b+c+d  b+c  0  0
+##    %xmm0: b+c+d  b+c  b  a
+##
+&function_begin_B("_vpaes_schedule_192_smear");
+	&pshufd	("xmm1","xmm6",0x80);		# d c 0 0 -> c 0 0 0
+	&pshufd	("xmm0","xmm7",0xFE);		# b a _ _ -> b b b a
+	&pxor	("xmm6","xmm1");		# -> c+d c 0 0
+	&pxor	("xmm1","xmm1");
+	&pxor	("xmm6","xmm0");		# -> b+c+d b+c b a
+	&movdqa	("xmm0","xmm6");
+	&movhlps("xmm6","xmm1");		# clobber low side with zeros
+	&ret	();
+&function_end_B("_vpaes_schedule_192_smear");
+
+##
+##  .aes_schedule_round
+##
+##  Runs one main round of the key schedule on %xmm0, %xmm7
+##
+##  Specifically, runs subbytes on the high dword of %xmm0
+##  then rotates it by one byte and xors into the low dword of
+##  %xmm7.
+##
+##  Adds rcon from low byte of %xmm8, then rotates %xmm8 for
+##  next rcon.
+##
+##  Smears the dwords of %xmm7 by xoring the low into the
+##  second low, result into third, result into highest.
+##
+##  Returns results in %xmm7 = %xmm0.
+##  Clobbers %xmm1-%xmm5.
+##
+&function_begin_B("_vpaes_schedule_round");
+	# extract rcon from xmm8
+	&movdqa	("xmm2",&QWP(8,"esp"));		# xmm8
+	&pxor	("xmm1","xmm1");
+	&palignr("xmm1","xmm2",15);
+	&palignr("xmm2","xmm2",15);
+	&pxor	("xmm7","xmm1");
+
+	# rotate
+	&pshufd	("xmm0","xmm0",0xFF);
+	&palignr("xmm0","xmm0",1);
+
+	# fall through...
+	&movdqa	(&QWP(8,"esp"),"xmm2");		# xmm8
+
+	# low round: same as high round, but no rotation and no rcon.
+&set_label("_vpaes_schedule_low_round");
+	# smear xmm7
+	&movdqa	("xmm1","xmm7");
+	&pslldq	("xmm7",4);
+	&pxor	("xmm7","xmm1");
+	&movdqa	("xmm1","xmm7");
+	&pslldq	("xmm7",8);
+	&pxor	("xmm7","xmm1");
+	&pxor	("xmm7",&QWP($k_s63,$const));
+
+	# subbyte
+	&movdqa	("xmm4",&QWP($k_s0F,$const));
+	&movdqa	("xmm5",&QWP($k_inv,$const));	# 4 : 1/j
+	&movdqa	("xmm1","xmm4");	
+	&pandn	("xmm1","xmm0");
+	&psrld	("xmm1",4);			# 1 = i
+	&pand	("xmm0","xmm4");		# 0 = k
+	&movdqa	("xmm2",&QWP($k_inv+16,$const));# 2 : a/k
+	&pshufb	("xmm2","xmm0");		# 2 = a/k
+	&pxor	("xmm0","xmm1");		# 0 = j
+	&movdqa	("xmm3","xmm5");		# 3 : 1/i
+	&pshufb	("xmm3","xmm1");		# 3 = 1/i
+	&pxor	("xmm3","xmm2");		# 3 = iak = 1/i + a/k
+	&movdqa	("xmm4","xmm5");		# 4 : 1/j
+	&pshufb	("xmm4","xmm0");		# 4 = 1/j
+	&pxor	("xmm4","xmm2");		# 4 = jak = 1/j + a/k
+	&movdqa	("xmm2","xmm5");		# 2 : 1/iak
+	&pshufb	("xmm2","xmm3");		# 2 = 1/iak
+	&pxor	("xmm2","xmm0");		# 2 = io
+	&movdqa	("xmm3","xmm5");		# 3 : 1/jak
+	&pshufb	("xmm3","xmm4");		# 3 = 1/jak
+	&pxor	("xmm3","xmm1");		# 3 = jo
+	&movdqa	("xmm4",&QWP($k_sb1,$const));	# 4 : sbou
+	&pshufb	("xmm4","xmm2");		# 4 = sbou
+	&movdqa	("xmm0",&QWP($k_sb1+16,$const));# 0 : sbot
+	&pshufb	("xmm0","xmm3");		# 0 = sb1t
+	&pxor	("xmm0","xmm4");		# 0 = sbox output
+
+	# add in smeared stuff
+	&pxor	("xmm0","xmm7");
+	&movdqa	("xmm7","xmm0");
+	&ret	();
+&function_end_B("_vpaes_schedule_round");
+
+##
+##  .aes_schedule_transform
+##
+##  Linear-transform %xmm0 according to tables at (%ebx)
+##
+##  Output in %xmm0
+##  Clobbers %xmm1, %xmm2
+##
+&function_begin_B("_vpaes_schedule_transform");
+	&movdqa	("xmm2",&QWP($k_s0F,$const));
+	&movdqa	("xmm1","xmm2");
+	&pandn	("xmm1","xmm0");
+	&psrld	("xmm1",4);
+	&pand	("xmm0","xmm2");
+	&movdqa	("xmm2",&QWP(0,$base));
+	&pshufb	("xmm2","xmm0");
+	&movdqa	("xmm0",&QWP(16,$base));
+	&pshufb	("xmm0","xmm1");
+	&pxor	("xmm0","xmm2");
+	&ret	();
+&function_end_B("_vpaes_schedule_transform");
+
+##
+##  .aes_schedule_mangle
+##
+##  Mangle xmm0 from (basis-transformed) standard version
+##  to our version.
+##
+##  On encrypt,
+##    xor with 0x63
+##    multiply by circulant 0,1,1,1
+##    apply shiftrows transform
+##
+##  On decrypt,
+##    xor with 0x63
+##    multiply by "inverse mixcolumns" circulant E,B,D,9
+##    deskew
+##    apply shiftrows transform
+##
+##
+##  Writes out to (%edx), and increments or decrements it
+##  Keeps track of round number mod 4 in %ecx
+##  Preserves xmm0
+##  Clobbers xmm1-xmm5
+##
+&function_begin_B("_vpaes_schedule_mangle");
+	&movdqa	("xmm4","xmm0");	# save xmm0 for later
+	&movdqa	("xmm5",&QWP($k_mc_forward,$const));
+	&test	($out,$out);
+	&jnz	(&label("schedule_mangle_dec"));
+
+	# encrypting
+	&add	($key,16);
+	&pxor	("xmm4",&QWP($k_s63,$const));
+	&pshufb	("xmm4","xmm5");
+	&movdqa	("xmm3","xmm4");
+	&pshufb	("xmm4","xmm5");
+	&pxor	("xmm3","xmm4");
+	&pshufb	("xmm4","xmm5");
+	&pxor	("xmm3","xmm4");
+
+	&jmp	(&label("schedule_mangle_both"));
+
+&set_label("schedule_mangle_dec",16);
+	# inverse mix columns
+	&movdqa	("xmm2",&QWP($k_s0F,$const));
+	&lea	($inp,&DWP($k_dksd,$const));
+	&movdqa	("xmm1","xmm2");
+	&pandn	("xmm1","xmm4");
+	&psrld	("xmm1",4);			# 1 = hi
+	&pand	("xmm4","xmm2");		# 4 = lo
+
+	&movdqa	("xmm2",&QWP(0,$inp));
+	&pshufb	("xmm2","xmm4");
+	&movdqa	("xmm3",&QWP(0x10,$inp));
+	&pshufb	("xmm3","xmm1");
+	&pxor	("xmm3","xmm2");
+	&pshufb	("xmm3","xmm5");
+
+	&movdqa	("xmm2",&QWP(0x20,$inp));
+	&pshufb	("xmm2","xmm4");
+	&pxor	("xmm2","xmm3");
+	&movdqa	("xmm3",&QWP(0x30,$inp));
+	&pshufb	("xmm3","xmm1");
+	&pxor	("xmm3","xmm2");
+	&pshufb	("xmm3","xmm5");
+
+	&movdqa	("xmm2",&QWP(0x40,$inp));
+	&pshufb	("xmm2","xmm4");
+	&pxor	("xmm2","xmm3");
+	&movdqa	("xmm3",&QWP(0x50,$inp));
+	&pshufb	("xmm3","xmm1");
+	&pxor	("xmm3","xmm2");
+	&pshufb	("xmm3","xmm5");
+
+	&movdqa	("xmm2",&QWP(0x60,$inp));
+	&pshufb	("xmm2","xmm4");
+	&pxor	("xmm2","xmm3");
+	&movdqa	("xmm3",&QWP(0x70,$inp));
+	&pshufb	("xmm3","xmm1");
+	&pxor	("xmm3","xmm2");
+
+	&add	($key,-16);
+
+&set_label("schedule_mangle_both");
+	&movdqa	("xmm1",&QWP($k_sr,$const,$magic));
+	&pshufb	("xmm3","xmm1");
+	&add	($magic,-16);
+	&and	($magic,0x30);
+	&movdqu	(&QWP(0,$key),"xmm3");
+	&ret	();
+&function_end_B("_vpaes_schedule_mangle");
+
+#
+# Interface to OpenSSL
+#
+&function_begin("${PREFIX}_set_encrypt_key");
+	&mov	($inp,&wparam(0));		# inp
+	&lea	($base,&DWP(-56,"esp"));
+	&mov	($round,&wparam(1));		# bits
+	&and	($base,-16);
+	&mov	($key,&wparam(2));		# key
+	&xchg	($base,"esp");			# alloca
+	&mov	(&DWP(48,"esp"),$base);
+
+	&mov	($base,$round);
+	&shr	($base,5);
+	&add	($base,5);
+	&mov	(&DWP(240,$key),$base);		# AES_KEY->rounds = nbits/32+5;
+	&mov	($magic,0x30);
+	&mov	($out,0);
+
+	&lea	($const,&DWP(&label("_vpaes_consts")."+0x30-".&label("pic_point")));
+	&call	("_vpaes_schedule_core");
+&set_label("pic_point");
+
+	&mov	("esp",&DWP(48,"esp"));
+	&xor	("eax","eax");
+&function_end("${PREFIX}_set_encrypt_key");
+
+&function_begin("${PREFIX}_set_decrypt_key");
+	&mov	($inp,&wparam(0));		# inp
+	&lea	($base,&DWP(-56,"esp"));
+	&mov	($round,&wparam(1));		# bits
+	&and	($base,-16);
+	&mov	($key,&wparam(2));		# key
+	&xchg	($base,"esp");			# alloca
+	&mov	(&DWP(48,"esp"),$base);
+
+	&mov	($base,$round);
+	&shr	($base,5);
+	&add	($base,5);
+	&mov	(&DWP(240,$key),$base);	# AES_KEY->rounds = nbits/32+5;
+	&shl	($base,4);
+	&lea	($key,&DWP(16,$key,$base));
+
+	&mov	($out,1);
+	&mov	($magic,$round);
+	&shr	($magic,1);
+	&and	($magic,32);
+	&xor	($magic,32);			# nbist==192?0:32;
+
+	&lea	($const,&DWP(&label("_vpaes_consts")."+0x30-".&label("pic_point")));
+	&call	("_vpaes_schedule_core");
+&set_label("pic_point");
+
+	&mov	("esp",&DWP(48,"esp"));
+	&xor	("eax","eax");
+&function_end("${PREFIX}_set_decrypt_key");
+
+&function_begin("${PREFIX}_encrypt");
+	&lea	($const,&DWP(&label("_vpaes_consts")."+0x30-".&label("pic_point")));
+	&call	("_vpaes_preheat");
+&set_label("pic_point");
+	&mov	($inp,&wparam(0));		# inp
+	&lea	($base,&DWP(-56,"esp"));
+	&mov	($out,&wparam(1));		# out
+	&and	($base,-16);
+	&mov	($key,&wparam(2));		# key
+	&xchg	($base,"esp");			# alloca
+	&mov	(&DWP(48,"esp"),$base);
+
+	&movdqu	("xmm0",&QWP(0,$inp));
+	&call	("_vpaes_encrypt_core");
+	&movdqu	(&QWP(0,$out),"xmm0");
+
+	&mov	("esp",&DWP(48,"esp"));
+&function_end("${PREFIX}_encrypt");
+
+&function_begin("${PREFIX}_decrypt");
+	&lea	($const,&DWP(&label("_vpaes_consts")."+0x30-".&label("pic_point")));
+	&call	("_vpaes_preheat");
+&set_label("pic_point");
+	&mov	($inp,&wparam(0));		# inp
+	&lea	($base,&DWP(-56,"esp"));
+	&mov	($out,&wparam(1));		# out
+	&and	($base,-16);
+	&mov	($key,&wparam(2));		# key
+	&xchg	($base,"esp");			# alloca
+	&mov	(&DWP(48,"esp"),$base);
+
+	&movdqu	("xmm0",&QWP(0,$inp));
+	&call	("_vpaes_decrypt_core");
+	&movdqu	(&QWP(0,$out),"xmm0");
+
+	&mov	("esp",&DWP(48,"esp"));
+&function_end("${PREFIX}_decrypt");
+
+&function_begin("${PREFIX}_cbc_encrypt");
+	&mov	($inp,&wparam(0));		# inp
+	&mov	($out,&wparam(1));		# out
+	&mov	($round,&wparam(2));		# len
+	&mov	($key,&wparam(3));		# key
+	&sub	($round,16);
+	&jc	(&label("cbc_abort"));
+	&lea	($base,&DWP(-56,"esp"));
+	&mov	($const,&wparam(4));		# ivp
+	&and	($base,-16);
+	&mov	($magic,&wparam(5));		# enc
+	&xchg	($base,"esp");			# alloca
+	&movdqu	("xmm1",&QWP(0,$const));	# load IV
+	&sub	($out,$inp);
+	&mov	(&DWP(48,"esp"),$base);
+
+	&mov	(&DWP(0,"esp"),$out);		# save out
+	&mov	(&DWP(4,"esp"),$key)		# save key
+	&mov	(&DWP(8,"esp"),$const);		# save ivp
+	&mov	($out,$round);			# $out works as $len
+
+	&lea	($const,&DWP(&label("_vpaes_consts")."+0x30-".&label("pic_point")));
+	&call	("_vpaes_preheat");
+&set_label("pic_point");
+	&cmp	($magic,0);
+	&je	(&label("cbc_dec_loop"));
+	&jmp	(&label("cbc_enc_loop"));
+
+&set_label("cbc_enc_loop",16);
+	&movdqu	("xmm0",&QWP(0,$inp));		# load input
+	&pxor	("xmm0","xmm1");		# inp^=iv
+	&call	("_vpaes_encrypt_core");
+	&mov	($base,&DWP(0,"esp"));		# restore out
+	&mov	($key,&DWP(4,"esp"));		# restore key
+	&movdqa	("xmm1","xmm0");
+	&movdqu	(&QWP(0,$base,$inp),"xmm0");	# write output
+	&lea	($inp,&DWP(16,$inp));
+	&sub	($out,16);
+	&jnc	(&label("cbc_enc_loop"));
+	&jmp	(&label("cbc_done"));
+
+&set_label("cbc_dec_loop",16);
+	&movdqu	("xmm0",&QWP(0,$inp));		# load input
+	&movdqa	(&QWP(16,"esp"),"xmm1");	# save IV
+	&movdqa	(&QWP(32,"esp"),"xmm0");	# save future IV
+	&call	("_vpaes_decrypt_core");
+	&mov	($base,&DWP(0,"esp"));		# restore out
+	&mov	($key,&DWP(4,"esp"));		# restore key
+	&pxor	("xmm0",&QWP(16,"esp"));	# out^=iv
+	&movdqa	("xmm1",&QWP(32,"esp"));	# load next IV
+	&movdqu	(&QWP(0,$base,$inp),"xmm0");	# write output
+	&lea	($inp,&DWP(16,$inp));
+	&sub	($out,16);
+	&jnc	(&label("cbc_dec_loop"));
+
+&set_label("cbc_done");
+	&mov	($base,&DWP(8,"esp"));		# restore ivp
+	&mov	("esp",&DWP(48,"esp"));
+	&movdqu	(&QWP(0,$base),"xmm1");		# write IV
+&set_label("cbc_abort");
+&function_end("${PREFIX}_cbc_encrypt");
+
+&asm_finish();
+
+close STDOUT;
diff --git a/crypto/aes/asm/vpaes-x86_64.pl b/crypto/aes/asm/vpaes-x86_64.pl
new file mode 100644
index 000000000..422e8ee44
--- /dev/null
+++ b/crypto/aes/asm/vpaes-x86_64.pl
@@ -0,0 +1,1215 @@
+#! /usr/bin/env perl
+# Copyright 2011-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+
+######################################################################
+## Constant-time SSSE3 AES core implementation.
+## version 0.1
+##
+## By Mike Hamburg (Stanford University), 2009
+## Public domain.
+##
+## For details see http://shiftleft.org/papers/vector_aes/ and
+## http://crypto.stanford.edu/vpaes/.
+
+######################################################################
+# September 2011.
+#
+# Interface to OpenSSL as "almost" drop-in replacement for
+# aes-x86_64.pl. "Almost" refers to the fact that AES_cbc_encrypt
+# doesn't handle partial vectors (doesn't have to if called from
+# EVP only). "Drop-in" implies that this module doesn't share key
+# schedule structure with the original nor does it make assumption
+# about its alignment...
+#
+# Performance summary. aes-x86_64.pl column lists large-block CBC
+# encrypt/decrypt/with-hyper-threading-off(*) results in cycles per
+# byte processed with 128-bit key, and vpaes-x86_64.pl column -
+# [also large-block CBC] encrypt/decrypt.
+#
+#		aes-x86_64.pl		vpaes-x86_64.pl
+#
+# Core 2(**)	29.6/41.1/14.3		21.9/25.2(***)
+# Nehalem	29.6/40.3/14.6		10.0/11.8
+# Atom		57.3/74.2/32.1		60.9/77.2(***)
+# Silvermont	52.7/64.0/19.5		48.8/60.8(***)
+# Goldmont	38.9/49.0/17.8		10.6/12.6
+#
+# (*)	"Hyper-threading" in the context refers rather to cache shared
+#	among multiple cores, than to specifically Intel HTT. As vast
+#	majority of contemporary cores share cache, slower code path
+#	is common place. In other words "with-hyper-threading-off"
+#	results are presented mostly for reference purposes.
+#
+# (**)	"Core 2" refers to initial 65nm design, a.k.a. Conroe.
+#
+# (***)	Less impressive improvement on Core 2 and Atom is due to slow
+#	pshufb,	yet it's respectable +36%/62% improvement on Core 2
+#	(as implied, over "hyper-threading-safe" code path).
+#
+#						
+
+$flavour = shift;
+$output  = shift;
+if ($flavour =~ /\./) { $output = $flavour; undef $flavour; }
+
+$win64=0; $win64=1 if ($flavour =~ /[nm]asm|mingw64/ || $output =~ /\.asm$/);
+
+$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+( $xlate="${dir}x86_64-xlate.pl" and -f $xlate ) or
+( $xlate="${dir}../../perlasm/x86_64-xlate.pl" and -f $xlate) or
+die "can't locate x86_64-xlate.pl";
+
+open OUT,"| \"$^X\" \"$xlate\" $flavour \"$output\"";
+*STDOUT=*OUT;
+
+$PREFIX="vpaes";
+
+$code.=<<___;
+.text
+
+##
+##  _aes_encrypt_core
+##
+##  AES-encrypt %xmm0.
+##
+##  Inputs:
+##     %xmm0 = input
+##     %xmm9-%xmm15 as in _vpaes_preheat
+##    (%rdx) = scheduled keys
+##
+##  Output in %xmm0
+##  Clobbers  %xmm1-%xmm5, %r9, %r10, %r11, %rax
+##  Preserves %xmm6 - %xmm8 so you get some local vectors
+##
+##
+.type	_vpaes_encrypt_core,\@abi-omnipotent
+.align 16
+_vpaes_encrypt_core:
+	mov	%rdx,	%r9
+	mov	\$16,	%r11
+	mov	240(%rdx),%eax
+	movdqa	%xmm9,	%xmm1
+	movdqa	.Lk_ipt(%rip), %xmm2	# iptlo
+	pandn	%xmm0,	%xmm1
+	movdqu	(%r9),	%xmm5		# round0 key
+	psrld	\$4,	%xmm1
+	pand	%xmm9,	%xmm0
+	pshufb	%xmm0,	%xmm2
+	movdqa	.Lk_ipt+16(%rip), %xmm0	# ipthi
+	pshufb	%xmm1,	%xmm0
+	pxor	%xmm5,	%xmm2
+	add	\$16,	%r9
+	pxor	%xmm2,	%xmm0
+	lea	.Lk_mc_backward(%rip),%r10
+	jmp	.Lenc_entry
+
+.align 16
+.Lenc_loop:
+	# middle of middle round
+	movdqa  %xmm13,	%xmm4	# 4 : sb1u
+	movdqa  %xmm12,	%xmm0	# 0 : sb1t
+	pshufb  %xmm2,	%xmm4	# 4 = sb1u
+	pshufb  %xmm3,	%xmm0	# 0 = sb1t
+	pxor	%xmm5,	%xmm4	# 4 = sb1u + k
+	movdqa  %xmm15,	%xmm5	# 4 : sb2u
+	pxor	%xmm4,	%xmm0	# 0 = A
+	movdqa	-0x40(%r11,%r10), %xmm1		# .Lk_mc_forward[]
+	pshufb	%xmm2,	%xmm5	# 4 = sb2u
+	movdqa	(%r11,%r10), %xmm4		# .Lk_mc_backward[]
+	movdqa	%xmm14, %xmm2	# 2 : sb2t
+	pshufb	%xmm3,  %xmm2	# 2 = sb2t
+	movdqa	%xmm0,  %xmm3	# 3 = A
+	pxor	%xmm5,	%xmm2	# 2 = 2A
+	pshufb  %xmm1,  %xmm0	# 0 = B
+	add	\$16,	%r9	# next key
+	pxor	%xmm2,  %xmm0	# 0 = 2A+B
+	pshufb	%xmm4,	%xmm3	# 3 = D
+	add	\$16,	%r11	# next mc
+	pxor	%xmm0,	%xmm3	# 3 = 2A+B+D
+	pshufb  %xmm1,	%xmm0	# 0 = 2B+C
+	and	\$0x30,	%r11	# ... mod 4
+	sub	\$1,%rax	# nr--
+	pxor	%xmm3,	%xmm0	# 0 = 2A+3B+C+D
+
+.Lenc_entry:
+	# top of round
+	movdqa  %xmm9, 	%xmm1	# 1 : i
+	movdqa	%xmm11, %xmm5	# 2 : a/k
+	pandn	%xmm0, 	%xmm1	# 1 = i<<4
+	psrld	\$4,   	%xmm1   # 1 = i
+	pand	%xmm9, 	%xmm0   # 0 = k
+	pshufb  %xmm0,  %xmm5	# 2 = a/k
+	movdqa	%xmm10,	%xmm3  	# 3 : 1/i
+	pxor	%xmm1,	%xmm0	# 0 = j
+	pshufb  %xmm1, 	%xmm3  	# 3 = 1/i
+	movdqa	%xmm10,	%xmm4  	# 4 : 1/j
+	pxor	%xmm5, 	%xmm3  	# 3 = iak = 1/i + a/k
+	pshufb	%xmm0, 	%xmm4  	# 4 = 1/j
+	movdqa	%xmm10,	%xmm2  	# 2 : 1/iak
+	pxor	%xmm5, 	%xmm4  	# 4 = jak = 1/j + a/k
+	pshufb  %xmm3,	%xmm2  	# 2 = 1/iak
+	movdqa	%xmm10, %xmm3   # 3 : 1/jak
+	pxor	%xmm0, 	%xmm2  	# 2 = io
+	pshufb  %xmm4,  %xmm3   # 3 = 1/jak
+	movdqu	(%r9),	%xmm5
+	pxor	%xmm1,  %xmm3   # 3 = jo
+	jnz	.Lenc_loop
+
+	# middle of last round
+	movdqa	-0x60(%r10), %xmm4	# 3 : sbou	.Lk_sbo
+	movdqa	-0x50(%r10), %xmm0	# 0 : sbot	.Lk_sbo+16
+	pshufb  %xmm2,  %xmm4	# 4 = sbou
+	pxor	%xmm5,  %xmm4	# 4 = sb1u + k
+	pshufb  %xmm3,	%xmm0	# 0 = sb1t
+	movdqa	0x40(%r11,%r10), %xmm1		# .Lk_sr[]
+	pxor	%xmm4,	%xmm0	# 0 = A
+	pshufb	%xmm1,	%xmm0
+	ret
+.size	_vpaes_encrypt_core,.-_vpaes_encrypt_core
+	
+##
+##  Decryption core
+##
+##  Same API as encryption core.
+##
+.type	_vpaes_decrypt_core,\@abi-omnipotent
+.align	16
+_vpaes_decrypt_core:
+	mov	%rdx,	%r9		# load key
+	mov	240(%rdx),%eax
+	movdqa	%xmm9,	%xmm1
+	movdqa	.Lk_dipt(%rip), %xmm2	# iptlo
+	pandn	%xmm0,	%xmm1
+	mov	%rax,	%r11
+	psrld	\$4,	%xmm1
+	movdqu	(%r9),	%xmm5		# round0 key
+	shl	\$4,	%r11
+	pand	%xmm9,	%xmm0
+	pshufb	%xmm0,	%xmm2
+	movdqa	.Lk_dipt+16(%rip), %xmm0 # ipthi
+	xor	\$0x30,	%r11
+	lea	.Lk_dsbd(%rip),%r10
+	pshufb	%xmm1,	%xmm0
+	and	\$0x30,	%r11
+	pxor	%xmm5,	%xmm2
+	movdqa	.Lk_mc_forward+48(%rip), %xmm5
+	pxor	%xmm2,	%xmm0
+	add	\$16,	%r9
+	add	%r10,	%r11
+	jmp	.Ldec_entry
+
+.align 16
+.Ldec_loop:
+##
+##  Inverse mix columns
+##
+	movdqa  -0x20(%r10),%xmm4	# 4 : sb9u
+	movdqa  -0x10(%r10),%xmm1	# 0 : sb9t
+	pshufb	%xmm2,	%xmm4		# 4 = sb9u
+	pshufb	%xmm3,	%xmm1		# 0 = sb9t
+	pxor	%xmm4,	%xmm0
+	movdqa  0x00(%r10),%xmm4	# 4 : sbdu
+	pxor	%xmm1,	%xmm0		# 0 = ch
+	movdqa  0x10(%r10),%xmm1	# 0 : sbdt
+
+	pshufb	%xmm2,	%xmm4		# 4 = sbdu
+	pshufb	%xmm5,	%xmm0		# MC ch
+	pshufb	%xmm3,	%xmm1		# 0 = sbdt
+	pxor	%xmm4,	%xmm0		# 4 = ch
+	movdqa  0x20(%r10),%xmm4	# 4 : sbbu
+	pxor	%xmm1,	%xmm0		# 0 = ch
+	movdqa  0x30(%r10),%xmm1	# 0 : sbbt
+
+	pshufb	%xmm2,	%xmm4		# 4 = sbbu
+	pshufb	%xmm5,	%xmm0		# MC ch
+	pshufb	%xmm3,	%xmm1		# 0 = sbbt
+	pxor	%xmm4,	%xmm0		# 4 = ch
+	movdqa  0x40(%r10),%xmm4	# 4 : sbeu
+	pxor	%xmm1,	%xmm0		# 0 = ch
+	movdqa  0x50(%r10),%xmm1	# 0 : sbet
+
+	pshufb	%xmm2,	%xmm4		# 4 = sbeu
+	pshufb	%xmm5,	%xmm0		# MC ch
+	pshufb	%xmm3,	%xmm1		# 0 = sbet
+	pxor	%xmm4,	%xmm0		# 4 = ch
+	add	\$16, %r9		# next round key
+	palignr	\$12,	%xmm5,	%xmm5
+	pxor	%xmm1,	%xmm0		# 0 = ch
+	sub	\$1,%rax		# nr--
+
+.Ldec_entry:
+	# top of round
+	movdqa  %xmm9, 	%xmm1	# 1 : i
+	pandn	%xmm0, 	%xmm1	# 1 = i<<4
+	movdqa	%xmm11, %xmm2	# 2 : a/k
+	psrld	\$4,    %xmm1	# 1 = i
+	pand	%xmm9, 	%xmm0	# 0 = k
+	pshufb  %xmm0,  %xmm2	# 2 = a/k
+	movdqa	%xmm10,	%xmm3	# 3 : 1/i
+	pxor	%xmm1,	%xmm0	# 0 = j
+	pshufb  %xmm1, 	%xmm3	# 3 = 1/i
+	movdqa	%xmm10,	%xmm4	# 4 : 1/j
+	pxor	%xmm2, 	%xmm3	# 3 = iak = 1/i + a/k
+	pshufb	%xmm0, 	%xmm4	# 4 = 1/j
+	pxor	%xmm2, 	%xmm4	# 4 = jak = 1/j + a/k
+	movdqa	%xmm10,	%xmm2	# 2 : 1/iak
+	pshufb  %xmm3,	%xmm2	# 2 = 1/iak
+	movdqa	%xmm10, %xmm3	# 3 : 1/jak
+	pxor	%xmm0, 	%xmm2	# 2 = io
+	pshufb  %xmm4,  %xmm3	# 3 = 1/jak
+	movdqu	(%r9),	%xmm0
+	pxor	%xmm1,  %xmm3	# 3 = jo
+	jnz	.Ldec_loop
+
+	# middle of last round
+	movdqa	0x60(%r10), %xmm4	# 3 : sbou
+	pshufb  %xmm2,  %xmm4	# 4 = sbou
+	pxor	%xmm0,  %xmm4	# 4 = sb1u + k
+	movdqa	0x70(%r10), %xmm0	# 0 : sbot
+	movdqa	-0x160(%r11), %xmm2	# .Lk_sr-.Lk_dsbd=-0x160
+	pshufb  %xmm3,	%xmm0	# 0 = sb1t
+	pxor	%xmm4,	%xmm0	# 0 = A
+	pshufb	%xmm2,	%xmm0
+	ret
+.size	_vpaes_decrypt_core,.-_vpaes_decrypt_core
+
+########################################################
+##                                                    ##
+##                  AES key schedule                  ##
+##                                                    ##
+########################################################
+.type	_vpaes_schedule_core,\@abi-omnipotent
+.align	16
+_vpaes_schedule_core:
+	# rdi = key
+	# rsi = size in bits
+	# rdx = buffer
+	# rcx = direction.  0=encrypt, 1=decrypt
+
+	call	_vpaes_preheat		# load the tables
+	movdqa	.Lk_rcon(%rip), %xmm8	# load rcon
+	movdqu	(%rdi),	%xmm0		# load key (unaligned)
+
+	# input transform
+	movdqa	%xmm0,	%xmm3
+	lea	.Lk_ipt(%rip), %r11
+	call	_vpaes_schedule_transform
+	movdqa	%xmm0,	%xmm7
+
+	lea	.Lk_sr(%rip),%r10
+	test	%rcx,	%rcx
+	jnz	.Lschedule_am_decrypting
+
+	# encrypting, output zeroth round key after transform
+	movdqu	%xmm0,	(%rdx)
+	jmp	.Lschedule_go
+
+.Lschedule_am_decrypting:
+	# decrypting, output zeroth round key after shiftrows
+	movdqa	(%r8,%r10),%xmm1
+	pshufb  %xmm1,	%xmm3
+	movdqu	%xmm3,	(%rdx)
+	xor	\$0x30, %r8
+
+.Lschedule_go:
+	cmp	\$192,	%esi
+	ja	.Lschedule_256
+	je	.Lschedule_192
+	# 128: fall though
+
+##
+##  .schedule_128
+##
+##  128-bit specific part of key schedule.
+##
+##  This schedule is really simple, because all its parts
+##  are accomplished by the subroutines.
+##
+.Lschedule_128:
+	mov	\$10, %esi
+	
+.Loop_schedule_128:
+	call 	_vpaes_schedule_round
+	dec	%rsi
+	jz 	.Lschedule_mangle_last
+	call	_vpaes_schedule_mangle	# write output
+	jmp 	.Loop_schedule_128
+
+##
+##  .aes_schedule_192
+##
+##  192-bit specific part of key schedule.
+##
+##  The main body of this schedule is the same as the 128-bit
+##  schedule, but with more smearing.  The long, high side is
+##  stored in %xmm7 as before, and the short, low side is in
+##  the high bits of %xmm6.
+##
+##  This schedule is somewhat nastier, however, because each
+##  round produces 192 bits of key material, or 1.5 round keys.
+##  Therefore, on each cycle we do 2 rounds and produce 3 round
+##  keys.
+##
+.align	16
+.Lschedule_192:
+	movdqu	8(%rdi),%xmm0		# load key part 2 (very unaligned)
+	call	_vpaes_schedule_transform	# input transform
+	movdqa	%xmm0,	%xmm6		# save short part
+	pxor	%xmm4,	%xmm4		# clear 4
+	movhlps	%xmm4,	%xmm6		# clobber low side with zeros
+	mov	\$4,	%esi
+
+.Loop_schedule_192:
+	call	_vpaes_schedule_round
+	palignr	\$8,%xmm6,%xmm0	
+	call	_vpaes_schedule_mangle	# save key n
+	call	_vpaes_schedule_192_smear
+	call	_vpaes_schedule_mangle	# save key n+1
+	call	_vpaes_schedule_round
+	dec	%rsi
+	jz 	.Lschedule_mangle_last
+	call	_vpaes_schedule_mangle	# save key n+2
+	call	_vpaes_schedule_192_smear
+	jmp	.Loop_schedule_192
+
+##
+##  .aes_schedule_256
+##
+##  256-bit specific part of key schedule.
+##
+##  The structure here is very similar to the 128-bit
+##  schedule, but with an additional "low side" in
+##  %xmm6.  The low side's rounds are the same as the
+##  high side's, except no rcon and no rotation.
+##
+.align	16
+.Lschedule_256:
+	movdqu	16(%rdi),%xmm0		# load key part 2 (unaligned)
+	call	_vpaes_schedule_transform	# input transform
+	mov	\$7, %esi
+	
+.Loop_schedule_256:
+	call	_vpaes_schedule_mangle	# output low result
+	movdqa	%xmm0,	%xmm6		# save cur_lo in xmm6
+
+	# high round
+	call	_vpaes_schedule_round
+	dec	%rsi
+	jz 	.Lschedule_mangle_last
+	call	_vpaes_schedule_mangle	
+
+	# low round. swap xmm7 and xmm6
+	pshufd	\$0xFF,	%xmm0,	%xmm0
+	movdqa	%xmm7,	%xmm5
+	movdqa	%xmm6,	%xmm7
+	call	_vpaes_schedule_low_round
+	movdqa	%xmm5,	%xmm7
+	
+	jmp	.Loop_schedule_256
+
+	
+##
+##  .aes_schedule_mangle_last
+##
+##  Mangler for last round of key schedule
+##  Mangles %xmm0
+##    when encrypting, outputs out(%xmm0) ^ 63
+##    when decrypting, outputs unskew(%xmm0)
+##
+##  Always called right before return... jumps to cleanup and exits
+##
+.align	16
+.Lschedule_mangle_last:
+	# schedule last round key from xmm0
+	lea	.Lk_deskew(%rip),%r11	# prepare to deskew
+	test	%rcx, 	%rcx
+	jnz	.Lschedule_mangle_last_dec
+
+	# encrypting
+	movdqa	(%r8,%r10),%xmm1
+	pshufb	%xmm1,	%xmm0		# output permute
+	lea	.Lk_opt(%rip),	%r11	# prepare to output transform
+	add	\$32,	%rdx
+
+.Lschedule_mangle_last_dec:
+	add	\$-16,	%rdx
+	pxor	.Lk_s63(%rip),	%xmm0
+	call	_vpaes_schedule_transform # output transform
+	movdqu	%xmm0,	(%rdx)		# save last key
+
+	# cleanup
+	pxor	%xmm0,  %xmm0
+	pxor	%xmm1,  %xmm1
+	pxor	%xmm2,  %xmm2
+	pxor	%xmm3,  %xmm3
+	pxor	%xmm4,  %xmm4
+	pxor	%xmm5,  %xmm5
+	pxor	%xmm6,  %xmm6
+	pxor	%xmm7,  %xmm7
+	ret
+.size	_vpaes_schedule_core,.-_vpaes_schedule_core
+
+##
+##  .aes_schedule_192_smear
+##
+##  Smear the short, low side in the 192-bit key schedule.
+##
+##  Inputs:
+##    %xmm7: high side, b  a  x  y
+##    %xmm6:  low side, d  c  0  0
+##    %xmm13: 0
+##
+##  Outputs:
+##    %xmm6: b+c+d  b+c  0  0
+##    %xmm0: b+c+d  b+c  b  a
+##
+.type	_vpaes_schedule_192_smear,\@abi-omnipotent
+.align	16
+_vpaes_schedule_192_smear:
+	pshufd	\$0x80,	%xmm6,	%xmm1	# d c 0 0 -> c 0 0 0
+	pshufd	\$0xFE,	%xmm7,	%xmm0	# b a _ _ -> b b b a
+	pxor	%xmm1,	%xmm6		# -> c+d c 0 0
+	pxor	%xmm1,	%xmm1
+	pxor	%xmm0,	%xmm6		# -> b+c+d b+c b a
+	movdqa	%xmm6,	%xmm0
+	movhlps	%xmm1,	%xmm6		# clobber low side with zeros
+	ret
+.size	_vpaes_schedule_192_smear,.-_vpaes_schedule_192_smear
+
+##
+##  .aes_schedule_round
+##
+##  Runs one main round of the key schedule on %xmm0, %xmm7
+##
+##  Specifically, runs subbytes on the high dword of %xmm0
+##  then rotates it by one byte and xors into the low dword of
+##  %xmm7.
+##
+##  Adds rcon from low byte of %xmm8, then rotates %xmm8 for
+##  next rcon.
+##
+##  Smears the dwords of %xmm7 by xoring the low into the
+##  second low, result into third, result into highest.
+##
+##  Returns results in %xmm7 = %xmm0.
+##  Clobbers %xmm1-%xmm4, %r11.
+##
+.type	_vpaes_schedule_round,\@abi-omnipotent
+.align	16
+_vpaes_schedule_round:
+	# extract rcon from xmm8
+	pxor	%xmm1,	%xmm1
+	palignr	\$15,	%xmm8,	%xmm1
+	palignr	\$15,	%xmm8,	%xmm8
+	pxor	%xmm1,	%xmm7
+
+	# rotate
+	pshufd	\$0xFF,	%xmm0,	%xmm0
+	palignr	\$1,	%xmm0,	%xmm0
+	
+	# fall through...
+	
+	# low round: same as high round, but no rotation and no rcon.
+_vpaes_schedule_low_round:
+	# smear xmm7
+	movdqa	%xmm7,	%xmm1
+	pslldq	\$4,	%xmm7
+	pxor	%xmm1,	%xmm7
+	movdqa	%xmm7,	%xmm1
+	pslldq	\$8,	%xmm7
+	pxor	%xmm1,	%xmm7
+	pxor	.Lk_s63(%rip), %xmm7
+
+	# subbytes
+	movdqa  %xmm9, 	%xmm1
+	pandn	%xmm0, 	%xmm1
+	psrld	\$4,    %xmm1		# 1 = i
+	pand	%xmm9, 	%xmm0		# 0 = k
+	movdqa	%xmm11, %xmm2		# 2 : a/k
+	pshufb  %xmm0,  %xmm2		# 2 = a/k
+	pxor	%xmm1,	%xmm0		# 0 = j
+	movdqa	%xmm10,	%xmm3		# 3 : 1/i
+	pshufb  %xmm1, 	%xmm3		# 3 = 1/i
+	pxor	%xmm2, 	%xmm3		# 3 = iak = 1/i + a/k
+	movdqa	%xmm10,	%xmm4		# 4 : 1/j
+	pshufb	%xmm0, 	%xmm4		# 4 = 1/j
+	pxor	%xmm2, 	%xmm4		# 4 = jak = 1/j + a/k
+	movdqa	%xmm10,	%xmm2		# 2 : 1/iak
+	pshufb  %xmm3,	%xmm2		# 2 = 1/iak
+	pxor	%xmm0, 	%xmm2		# 2 = io
+	movdqa	%xmm10, %xmm3		# 3 : 1/jak
+	pshufb  %xmm4,  %xmm3		# 3 = 1/jak
+	pxor	%xmm1,  %xmm3		# 3 = jo
+	movdqa	%xmm13, %xmm4		# 4 : sbou
+	pshufb  %xmm2,  %xmm4		# 4 = sbou
+	movdqa	%xmm12, %xmm0		# 0 : sbot
+	pshufb  %xmm3,	%xmm0		# 0 = sb1t
+	pxor	%xmm4, 	%xmm0		# 0 = sbox output
+
+	# add in smeared stuff
+	pxor	%xmm7,	%xmm0	
+	movdqa	%xmm0,	%xmm7
+	ret
+.size	_vpaes_schedule_round,.-_vpaes_schedule_round
+
+##
+##  .aes_schedule_transform
+##
+##  Linear-transform %xmm0 according to tables at (%r11)
+##
+##  Requires that %xmm9 = 0x0F0F... as in preheat
+##  Output in %xmm0
+##  Clobbers %xmm1, %xmm2
+##
+.type	_vpaes_schedule_transform,\@abi-omnipotent
+.align	16
+_vpaes_schedule_transform:
+	movdqa	%xmm9,	%xmm1
+	pandn	%xmm0,	%xmm1
+	psrld	\$4,	%xmm1
+	pand	%xmm9,	%xmm0
+	movdqa	(%r11), %xmm2 	# lo
+	pshufb	%xmm0,	%xmm2
+	movdqa	16(%r11), %xmm0 # hi
+	pshufb	%xmm1,	%xmm0
+	pxor	%xmm2,	%xmm0
+	ret
+.size	_vpaes_schedule_transform,.-_vpaes_schedule_transform
+
+##
+##  .aes_schedule_mangle
+##
+##  Mangle xmm0 from (basis-transformed) standard version
+##  to our version.
+##
+##  On encrypt,
+##    xor with 0x63
+##    multiply by circulant 0,1,1,1
+##    apply shiftrows transform
+##
+##  On decrypt,
+##    xor with 0x63
+##    multiply by "inverse mixcolumns" circulant E,B,D,9
+##    deskew
+##    apply shiftrows transform
+##
+##
+##  Writes out to (%rdx), and increments or decrements it
+##  Keeps track of round number mod 4 in %r8
+##  Preserves xmm0
+##  Clobbers xmm1-xmm5
+##
+.type	_vpaes_schedule_mangle,\@abi-omnipotent
+.align	16
+_vpaes_schedule_mangle:
+	movdqa	%xmm0,	%xmm4	# save xmm0 for later
+	movdqa	.Lk_mc_forward(%rip),%xmm5
+	test	%rcx, 	%rcx
+	jnz	.Lschedule_mangle_dec
+
+	# encrypting
+	add	\$16,	%rdx
+	pxor	.Lk_s63(%rip),%xmm4
+	pshufb	%xmm5,	%xmm4
+	movdqa	%xmm4,	%xmm3
+	pshufb	%xmm5,	%xmm4
+	pxor	%xmm4,	%xmm3
+	pshufb	%xmm5,	%xmm4
+	pxor	%xmm4,	%xmm3
+
+	jmp	.Lschedule_mangle_both
+.align	16
+.Lschedule_mangle_dec:
+	# inverse mix columns
+	lea	.Lk_dksd(%rip),%r11
+	movdqa	%xmm9,	%xmm1
+	pandn	%xmm4,	%xmm1
+	psrld	\$4,	%xmm1	# 1 = hi
+	pand	%xmm9,	%xmm4	# 4 = lo
+
+	movdqa	0x00(%r11), %xmm2
+	pshufb	%xmm4,	%xmm2
+	movdqa	0x10(%r11), %xmm3
+	pshufb	%xmm1,	%xmm3
+	pxor	%xmm2,	%xmm3
+	pshufb	%xmm5,	%xmm3
+
+	movdqa	0x20(%r11), %xmm2
+	pshufb	%xmm4,	%xmm2
+	pxor	%xmm3,	%xmm2
+	movdqa	0x30(%r11), %xmm3
+	pshufb	%xmm1,	%xmm3
+	pxor	%xmm2,	%xmm3
+	pshufb	%xmm5,	%xmm3
+
+	movdqa	0x40(%r11), %xmm2
+	pshufb	%xmm4,	%xmm2
+	pxor	%xmm3,	%xmm2
+	movdqa	0x50(%r11), %xmm3
+	pshufb	%xmm1,	%xmm3
+	pxor	%xmm2,	%xmm3
+	pshufb	%xmm5,	%xmm3
+
+	movdqa	0x60(%r11), %xmm2
+	pshufb	%xmm4,	%xmm2
+	pxor	%xmm3,	%xmm2
+	movdqa	0x70(%r11), %xmm3
+	pshufb	%xmm1,	%xmm3
+	pxor	%xmm2,	%xmm3
+
+	add	\$-16,	%rdx
+
+.Lschedule_mangle_both:
+	movdqa	(%r8,%r10),%xmm1
+	pshufb	%xmm1,%xmm3
+	add	\$-16,	%r8
+	and	\$0x30,	%r8
+	movdqu	%xmm3,	(%rdx)
+	ret
+.size	_vpaes_schedule_mangle,.-_vpaes_schedule_mangle
+
+#
+# Interface to OpenSSL
+#
+.globl	${PREFIX}_set_encrypt_key
+.type	${PREFIX}_set_encrypt_key,\@function,3
+.align	16
+${PREFIX}_set_encrypt_key:
+___
+$code.=<<___ if ($win64);
+	lea	-0xb8(%rsp),%rsp
+	movaps	%xmm6,0x10(%rsp)
+	movaps	%xmm7,0x20(%rsp)
+	movaps	%xmm8,0x30(%rsp)
+	movaps	%xmm9,0x40(%rsp)
+	movaps	%xmm10,0x50(%rsp)
+	movaps	%xmm11,0x60(%rsp)
+	movaps	%xmm12,0x70(%rsp)
+	movaps	%xmm13,0x80(%rsp)
+	movaps	%xmm14,0x90(%rsp)
+	movaps	%xmm15,0xa0(%rsp)
+.Lenc_key_body:
+___
+$code.=<<___;
+	mov	%esi,%eax
+	shr	\$5,%eax
+	add	\$5,%eax
+	mov	%eax,240(%rdx)	# AES_KEY->rounds = nbits/32+5;
+
+	mov	\$0,%ecx
+	mov	\$0x30,%r8d
+	call	_vpaes_schedule_core
+___
+$code.=<<___ if ($win64);
+	movaps	0x10(%rsp),%xmm6
+	movaps	0x20(%rsp),%xmm7
+	movaps	0x30(%rsp),%xmm8
+	movaps	0x40(%rsp),%xmm9
+	movaps	0x50(%rsp),%xmm10
+	movaps	0x60(%rsp),%xmm11
+	movaps	0x70(%rsp),%xmm12
+	movaps	0x80(%rsp),%xmm13
+	movaps	0x90(%rsp),%xmm14
+	movaps	0xa0(%rsp),%xmm15
+	lea	0xb8(%rsp),%rsp
+.Lenc_key_epilogue:
+___
+$code.=<<___;
+	xor	%eax,%eax
+	ret
+.size	${PREFIX}_set_encrypt_key,.-${PREFIX}_set_encrypt_key
+
+.globl	${PREFIX}_set_decrypt_key
+.type	${PREFIX}_set_decrypt_key,\@function,3
+.align	16
+${PREFIX}_set_decrypt_key:
+___
+$code.=<<___ if ($win64);
+	lea	-0xb8(%rsp),%rsp
+	movaps	%xmm6,0x10(%rsp)
+	movaps	%xmm7,0x20(%rsp)
+	movaps	%xmm8,0x30(%rsp)
+	movaps	%xmm9,0x40(%rsp)
+	movaps	%xmm10,0x50(%rsp)
+	movaps	%xmm11,0x60(%rsp)
+	movaps	%xmm12,0x70(%rsp)
+	movaps	%xmm13,0x80(%rsp)
+	movaps	%xmm14,0x90(%rsp)
+	movaps	%xmm15,0xa0(%rsp)
+.Ldec_key_body:
+___
+$code.=<<___;
+	mov	%esi,%eax
+	shr	\$5,%eax
+	add	\$5,%eax
+	mov	%eax,240(%rdx)	# AES_KEY->rounds = nbits/32+5;
+	shl	\$4,%eax
+	lea	16(%rdx,%rax),%rdx
+
+	mov	\$1,%ecx
+	mov	%esi,%r8d
+	shr	\$1,%r8d
+	and	\$32,%r8d
+	xor	\$32,%r8d	# nbits==192?0:32
+	call	_vpaes_schedule_core
+___
+$code.=<<___ if ($win64);
+	movaps	0x10(%rsp),%xmm6
+	movaps	0x20(%rsp),%xmm7
+	movaps	0x30(%rsp),%xmm8
+	movaps	0x40(%rsp),%xmm9
+	movaps	0x50(%rsp),%xmm10
+	movaps	0x60(%rsp),%xmm11
+	movaps	0x70(%rsp),%xmm12
+	movaps	0x80(%rsp),%xmm13
+	movaps	0x90(%rsp),%xmm14
+	movaps	0xa0(%rsp),%xmm15
+	lea	0xb8(%rsp),%rsp
+.Ldec_key_epilogue:
+___
+$code.=<<___;
+	xor	%eax,%eax
+	ret
+.size	${PREFIX}_set_decrypt_key,.-${PREFIX}_set_decrypt_key
+
+.globl	${PREFIX}_encrypt
+.type	${PREFIX}_encrypt,\@function,3
+.align	16
+${PREFIX}_encrypt:
+___
+$code.=<<___ if ($win64);
+	lea	-0xb8(%rsp),%rsp
+	movaps	%xmm6,0x10(%rsp)
+	movaps	%xmm7,0x20(%rsp)
+	movaps	%xmm8,0x30(%rsp)
+	movaps	%xmm9,0x40(%rsp)
+	movaps	%xmm10,0x50(%rsp)
+	movaps	%xmm11,0x60(%rsp)
+	movaps	%xmm12,0x70(%rsp)
+	movaps	%xmm13,0x80(%rsp)
+	movaps	%xmm14,0x90(%rsp)
+	movaps	%xmm15,0xa0(%rsp)
+.Lenc_body:
+___
+$code.=<<___;
+	movdqu	(%rdi),%xmm0
+	call	_vpaes_preheat
+	call	_vpaes_encrypt_core
+	movdqu	%xmm0,(%rsi)
+___
+$code.=<<___ if ($win64);
+	movaps	0x10(%rsp),%xmm6
+	movaps	0x20(%rsp),%xmm7
+	movaps	0x30(%rsp),%xmm8
+	movaps	0x40(%rsp),%xmm9
+	movaps	0x50(%rsp),%xmm10
+	movaps	0x60(%rsp),%xmm11
+	movaps	0x70(%rsp),%xmm12
+	movaps	0x80(%rsp),%xmm13
+	movaps	0x90(%rsp),%xmm14
+	movaps	0xa0(%rsp),%xmm15
+	lea	0xb8(%rsp),%rsp
+.Lenc_epilogue:
+___
+$code.=<<___;
+	ret
+.size	${PREFIX}_encrypt,.-${PREFIX}_encrypt
+
+.globl	${PREFIX}_decrypt
+.type	${PREFIX}_decrypt,\@function,3
+.align	16
+${PREFIX}_decrypt:
+___
+$code.=<<___ if ($win64);
+	lea	-0xb8(%rsp),%rsp
+	movaps	%xmm6,0x10(%rsp)
+	movaps	%xmm7,0x20(%rsp)
+	movaps	%xmm8,0x30(%rsp)
+	movaps	%xmm9,0x40(%rsp)
+	movaps	%xmm10,0x50(%rsp)
+	movaps	%xmm11,0x60(%rsp)
+	movaps	%xmm12,0x70(%rsp)
+	movaps	%xmm13,0x80(%rsp)
+	movaps	%xmm14,0x90(%rsp)
+	movaps	%xmm15,0xa0(%rsp)
+.Ldec_body:
+___
+$code.=<<___;
+	movdqu	(%rdi),%xmm0
+	call	_vpaes_preheat
+	call	_vpaes_decrypt_core
+	movdqu	%xmm0,(%rsi)
+___
+$code.=<<___ if ($win64);
+	movaps	0x10(%rsp),%xmm6
+	movaps	0x20(%rsp),%xmm7
+	movaps	0x30(%rsp),%xmm8
+	movaps	0x40(%rsp),%xmm9
+	movaps	0x50(%rsp),%xmm10
+	movaps	0x60(%rsp),%xmm11
+	movaps	0x70(%rsp),%xmm12
+	movaps	0x80(%rsp),%xmm13
+	movaps	0x90(%rsp),%xmm14
+	movaps	0xa0(%rsp),%xmm15
+	lea	0xb8(%rsp),%rsp
+.Ldec_epilogue:
+___
+$code.=<<___;
+	ret
+.size	${PREFIX}_decrypt,.-${PREFIX}_decrypt
+___
+{
+my ($inp,$out,$len,$key,$ivp,$enc)=("%rdi","%rsi","%rdx","%rcx","%r8","%r9");
+# void AES_cbc_encrypt (const void char *inp, unsigned char *out,
+#                       size_t length, const AES_KEY *key,
+#                       unsigned char *ivp,const int enc);
+$code.=<<___;
+.globl	${PREFIX}_cbc_encrypt
+.type	${PREFIX}_cbc_encrypt,\@function,6
+.align	16
+${PREFIX}_cbc_encrypt:
+	xchg	$key,$len
+___
+($len,$key)=($key,$len);
+$code.=<<___;
+	sub	\$16,$len
+	jc	.Lcbc_abort
+___
+$code.=<<___ if ($win64);
+	lea	-0xb8(%rsp),%rsp
+	movaps	%xmm6,0x10(%rsp)
+	movaps	%xmm7,0x20(%rsp)
+	movaps	%xmm8,0x30(%rsp)
+	movaps	%xmm9,0x40(%rsp)
+	movaps	%xmm10,0x50(%rsp)
+	movaps	%xmm11,0x60(%rsp)
+	movaps	%xmm12,0x70(%rsp)
+	movaps	%xmm13,0x80(%rsp)
+	movaps	%xmm14,0x90(%rsp)
+	movaps	%xmm15,0xa0(%rsp)
+.Lcbc_body:
+___
+$code.=<<___;
+	movdqu	($ivp),%xmm6		# load IV
+	sub	$inp,$out
+	call	_vpaes_preheat
+	cmp	\$0,${enc}d
+	je	.Lcbc_dec_loop
+	jmp	.Lcbc_enc_loop
+.align	16
+.Lcbc_enc_loop:
+	movdqu	($inp),%xmm0
+	pxor	%xmm6,%xmm0
+	call	_vpaes_encrypt_core
+	movdqa	%xmm0,%xmm6
+	movdqu	%xmm0,($out,$inp)
+	lea	16($inp),$inp
+	sub	\$16,$len
+	jnc	.Lcbc_enc_loop
+	jmp	.Lcbc_done
+.align	16
+.Lcbc_dec_loop:
+	movdqu	($inp),%xmm0
+	movdqa	%xmm0,%xmm7
+	call	_vpaes_decrypt_core
+	pxor	%xmm6,%xmm0
+	movdqa	%xmm7,%xmm6
+	movdqu	%xmm0,($out,$inp)
+	lea	16($inp),$inp
+	sub	\$16,$len
+	jnc	.Lcbc_dec_loop
+.Lcbc_done:
+	movdqu	%xmm6,($ivp)		# save IV
+___
+$code.=<<___ if ($win64);
+	movaps	0x10(%rsp),%xmm6
+	movaps	0x20(%rsp),%xmm7
+	movaps	0x30(%rsp),%xmm8
+	movaps	0x40(%rsp),%xmm9
+	movaps	0x50(%rsp),%xmm10
+	movaps	0x60(%rsp),%xmm11
+	movaps	0x70(%rsp),%xmm12
+	movaps	0x80(%rsp),%xmm13
+	movaps	0x90(%rsp),%xmm14
+	movaps	0xa0(%rsp),%xmm15
+	lea	0xb8(%rsp),%rsp
+.Lcbc_epilogue:
+___
+$code.=<<___;
+.Lcbc_abort:
+	ret
+.size	${PREFIX}_cbc_encrypt,.-${PREFIX}_cbc_encrypt
+___
+}
+$code.=<<___;
+##
+##  _aes_preheat
+##
+##  Fills register %r10 -> .aes_consts (so you can -fPIC)
+##  and %xmm9-%xmm15 as specified below.
+##
+.type	_vpaes_preheat,\@abi-omnipotent
+.align	16
+_vpaes_preheat:
+	lea	.Lk_s0F(%rip), %r10
+	movdqa	-0x20(%r10), %xmm10	# .Lk_inv
+	movdqa	-0x10(%r10), %xmm11	# .Lk_inv+16
+	movdqa	0x00(%r10), %xmm9	# .Lk_s0F
+	movdqa	0x30(%r10), %xmm13	# .Lk_sb1
+	movdqa	0x40(%r10), %xmm12	# .Lk_sb1+16
+	movdqa	0x50(%r10), %xmm15	# .Lk_sb2
+	movdqa	0x60(%r10), %xmm14	# .Lk_sb2+16
+	ret
+.size	_vpaes_preheat,.-_vpaes_preheat
+########################################################
+##                                                    ##
+##                     Constants                      ##
+##                                                    ##
+########################################################
+.type	_vpaes_consts,\@object
+.align	64
+_vpaes_consts:
+.Lk_inv:	# inv, inva
+	.quad	0x0E05060F0D080180, 0x040703090A0B0C02
+	.quad	0x01040A060F0B0780, 0x030D0E0C02050809
+
+.Lk_s0F:	# s0F
+	.quad	0x0F0F0F0F0F0F0F0F, 0x0F0F0F0F0F0F0F0F
+
+.Lk_ipt:	# input transform (lo, hi)
+	.quad	0xC2B2E8985A2A7000, 0xCABAE09052227808
+	.quad	0x4C01307D317C4D00, 0xCD80B1FCB0FDCC81
+
+.Lk_sb1:	# sb1u, sb1t
+	.quad	0xB19BE18FCB503E00, 0xA5DF7A6E142AF544
+	.quad	0x3618D415FAE22300, 0x3BF7CCC10D2ED9EF
+.Lk_sb2:	# sb2u, sb2t
+	.quad	0xE27A93C60B712400, 0x5EB7E955BC982FCD
+	.quad	0x69EB88400AE12900, 0xC2A163C8AB82234A
+.Lk_sbo:	# sbou, sbot
+	.quad	0xD0D26D176FBDC700, 0x15AABF7AC502A878
+	.quad	0xCFE474A55FBB6A00, 0x8E1E90D1412B35FA
+
+.Lk_mc_forward:	# mc_forward
+	.quad	0x0407060500030201, 0x0C0F0E0D080B0A09
+	.quad	0x080B0A0904070605, 0x000302010C0F0E0D
+	.quad	0x0C0F0E0D080B0A09, 0x0407060500030201
+	.quad	0x000302010C0F0E0D, 0x080B0A0904070605
+
+.Lk_mc_backward:# mc_backward
+	.quad	0x0605040702010003, 0x0E0D0C0F0A09080B
+	.quad	0x020100030E0D0C0F, 0x0A09080B06050407
+	.quad	0x0E0D0C0F0A09080B, 0x0605040702010003
+	.quad	0x0A09080B06050407, 0x020100030E0D0C0F
+
+.Lk_sr:		# sr
+	.quad	0x0706050403020100, 0x0F0E0D0C0B0A0908
+	.quad	0x030E09040F0A0500, 0x0B06010C07020D08
+	.quad	0x0F060D040B020900, 0x070E050C030A0108
+	.quad	0x0B0E0104070A0D00, 0x0306090C0F020508
+
+.Lk_rcon:	# rcon
+	.quad	0x1F8391B9AF9DEEB6, 0x702A98084D7C7D81
+
+.Lk_s63:	# s63: all equal to 0x63 transformed
+	.quad	0x5B5B5B5B5B5B5B5B, 0x5B5B5B5B5B5B5B5B
+
+.Lk_opt:	# output transform
+	.quad	0xFF9F4929D6B66000, 0xF7974121DEBE6808
+	.quad	0x01EDBD5150BCEC00, 0xE10D5DB1B05C0CE0
+
+.Lk_deskew:	# deskew tables: inverts the sbox's "skew"
+	.quad	0x07E4A34047A4E300, 0x1DFEB95A5DBEF91A
+	.quad	0x5F36B5DC83EA6900, 0x2841C2ABF49D1E77
+
+##
+##  Decryption stuff
+##  Key schedule constants
+##
+.Lk_dksd:	# decryption key schedule: invskew x*D
+	.quad	0xFEB91A5DA3E44700, 0x0740E3A45A1DBEF9
+	.quad	0x41C277F4B5368300, 0x5FDC69EAAB289D1E
+.Lk_dksb:	# decryption key schedule: invskew x*B
+	.quad	0x9A4FCA1F8550D500, 0x03D653861CC94C99
+	.quad	0x115BEDA7B6FC4A00, 0xD993256F7E3482C8
+.Lk_dkse:	# decryption key schedule: invskew x*E + 0x63
+	.quad	0xD5031CCA1FC9D600, 0x53859A4C994F5086
+	.quad	0xA23196054FDC7BE8, 0xCD5EF96A20B31487
+.Lk_dks9:	# decryption key schedule: invskew x*9
+	.quad	0xB6116FC87ED9A700, 0x4AED933482255BFC
+	.quad	0x4576516227143300, 0x8BB89FACE9DAFDCE
+
+##
+##  Decryption stuff
+##  Round function constants
+##
+.Lk_dipt:	# decryption input transform
+	.quad	0x0F505B040B545F00, 0x154A411E114E451A
+	.quad	0x86E383E660056500, 0x12771772F491F194
+
+.Lk_dsb9:	# decryption sbox output *9*u, *9*t
+	.quad	0x851C03539A86D600, 0xCAD51F504F994CC9
+	.quad	0xC03B1789ECD74900, 0x725E2C9EB2FBA565
+.Lk_dsbd:	# decryption sbox output *D*u, *D*t
+	.quad	0x7D57CCDFE6B1A200, 0xF56E9B13882A4439
+	.quad	0x3CE2FAF724C6CB00, 0x2931180D15DEEFD3
+.Lk_dsbb:	# decryption sbox output *B*u, *B*t
+	.quad	0xD022649296B44200, 0x602646F6B0F2D404
+	.quad	0xC19498A6CD596700, 0xF3FF0C3E3255AA6B
+.Lk_dsbe:	# decryption sbox output *E*u, *E*t
+	.quad	0x46F2929626D4D000, 0x2242600464B4F6B0
+	.quad	0x0C55A6CDFFAAC100, 0x9467F36B98593E32
+.Lk_dsbo:	# decryption sbox final output
+	.quad	0x1387EA537EF94000, 0xC7AA6DB9D4943E2D
+	.quad	0x12D7560F93441D00, 0xCA4B8159D8C58E9C
+.asciz	"Vector Permutation AES for x86_64/SSSE3, Mike Hamburg (Stanford University)"
+.align	64
+.size	_vpaes_consts,.-_vpaes_consts
+___
+
+if ($win64) {
+# EXCEPTION_DISPOSITION handler (EXCEPTION_RECORD *rec,ULONG64 frame,
+#		CONTEXT *context,DISPATCHER_CONTEXT *disp)
+$rec="%rcx";
+$frame="%rdx";
+$context="%r8";
+$disp="%r9";
+
+$code.=<<___;
+.extern	__imp_RtlVirtualUnwind
+.type	se_handler,\@abi-omnipotent
+.align	16
+se_handler:
+	push	%rsi
+	push	%rdi
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+	pushfq
+	sub	\$64,%rsp
+
+	mov	120($context),%rax	# pull context->Rax
+	mov	248($context),%rbx	# pull context->Rip
+
+	mov	8($disp),%rsi		# disp->ImageBase
+	mov	56($disp),%r11		# disp->HandlerData
+
+	mov	0(%r11),%r10d		# HandlerData[0]
+	lea	(%rsi,%r10),%r10	# prologue label
+	cmp	%r10,%rbx		# context->RipRsp
+
+	mov	4(%r11),%r10d		# HandlerData[1]
+	lea	(%rsi,%r10),%r10	# epilogue label
+	cmp	%r10,%rbx		# context->Rip>=epilogue label
+	jae	.Lin_prologue
+
+	lea	16(%rax),%rsi		# %xmm save area
+	lea	512($context),%rdi	# &context.Xmm6
+	mov	\$20,%ecx		# 10*sizeof(%xmm0)/sizeof(%rax)
+	.long	0xa548f3fc		# cld; rep movsq
+	lea	0xb8(%rax),%rax		# adjust stack pointer
+
+.Lin_prologue:
+	mov	8(%rax),%rdi
+	mov	16(%rax),%rsi
+	mov	%rax,152($context)	# restore context->Rsp
+	mov	%rsi,168($context)	# restore context->Rsi
+	mov	%rdi,176($context)	# restore context->Rdi
+
+	mov	40($disp),%rdi		# disp->ContextRecord
+	mov	$context,%rsi		# context
+	mov	\$`1232/8`,%ecx		# sizeof(CONTEXT)
+	.long	0xa548f3fc		# cld; rep movsq
+
+	mov	$disp,%rsi
+	xor	%rcx,%rcx		# arg1, UNW_FLAG_NHANDLER
+	mov	8(%rsi),%rdx		# arg2, disp->ImageBase
+	mov	0(%rsi),%r8		# arg3, disp->ControlPc
+	mov	16(%rsi),%r9		# arg4, disp->FunctionEntry
+	mov	40(%rsi),%r10		# disp->ContextRecord
+	lea	56(%rsi),%r11		# &disp->HandlerData
+	lea	24(%rsi),%r12		# &disp->EstablisherFrame
+	mov	%r10,32(%rsp)		# arg5
+	mov	%r11,40(%rsp)		# arg6
+	mov	%r12,48(%rsp)		# arg7
+	mov	%rcx,56(%rsp)		# arg8, (NULL)
+	call	*__imp_RtlVirtualUnwind(%rip)
+
+	mov	\$1,%eax		# ExceptionContinueSearch
+	add	\$64,%rsp
+	popfq
+	pop	%r15
+	pop	%r14
+	pop	%r13
+	pop	%r12
+	pop	%rbp
+	pop	%rbx
+	pop	%rdi
+	pop	%rsi
+	ret
+.size	se_handler,.-se_handler
+
+.section	.pdata
+.align	4
+	.rva	.LSEH_begin_${PREFIX}_set_encrypt_key
+	.rva	.LSEH_end_${PREFIX}_set_encrypt_key
+	.rva	.LSEH_info_${PREFIX}_set_encrypt_key
+
+	.rva	.LSEH_begin_${PREFIX}_set_decrypt_key
+	.rva	.LSEH_end_${PREFIX}_set_decrypt_key
+	.rva	.LSEH_info_${PREFIX}_set_decrypt_key
+
+	.rva	.LSEH_begin_${PREFIX}_encrypt
+	.rva	.LSEH_end_${PREFIX}_encrypt
+	.rva	.LSEH_info_${PREFIX}_encrypt
+
+	.rva	.LSEH_begin_${PREFIX}_decrypt
+	.rva	.LSEH_end_${PREFIX}_decrypt
+	.rva	.LSEH_info_${PREFIX}_decrypt
+
+	.rva	.LSEH_begin_${PREFIX}_cbc_encrypt
+	.rva	.LSEH_end_${PREFIX}_cbc_encrypt
+	.rva	.LSEH_info_${PREFIX}_cbc_encrypt
+
+.section	.xdata
+.align	8
+.LSEH_info_${PREFIX}_set_encrypt_key:
+	.byte	9,0,0,0
+	.rva	se_handler
+	.rva	.Lenc_key_body,.Lenc_key_epilogue	# HandlerData[]
+.LSEH_info_${PREFIX}_set_decrypt_key:
+	.byte	9,0,0,0
+	.rva	se_handler
+	.rva	.Ldec_key_body,.Ldec_key_epilogue	# HandlerData[]
+.LSEH_info_${PREFIX}_encrypt:
+	.byte	9,0,0,0
+	.rva	se_handler
+	.rva	.Lenc_body,.Lenc_epilogue		# HandlerData[]
+.LSEH_info_${PREFIX}_decrypt:
+	.byte	9,0,0,0
+	.rva	se_handler
+	.rva	.Ldec_body,.Ldec_epilogue		# HandlerData[]
+.LSEH_info_${PREFIX}_cbc_encrypt:
+	.byte	9,0,0,0
+	.rva	se_handler
+	.rva	.Lcbc_body,.Lcbc_epilogue		# HandlerData[]
+___
+}
+
+$code =~ s/\`([^\`]*)\`/eval($1)/gem;
+
+print $code;
+
+close STDOUT;
diff --git a/crypto/aes/build.info b/crypto/aes/build.info
new file mode 100644
index 000000000..cf6cb5ec2
--- /dev/null
+++ b/crypto/aes/build.info
@@ -0,0 +1,57 @@
+LIBS=../../libcrypto
+SOURCE[../../libcrypto]=\
+        aes_misc.c aes_ecb.c aes_cfb.c aes_ofb.c \
+        aes_ige.c aes_wrap.c {- $target{aes_asm_src} -}
+
+GENERATE[aes-ia64.s]=asm/aes-ia64.S
+
+GENERATE[aes-586.s]=asm/aes-586.pl $(PERLASM_SCHEME) $(CFLAGS) $(LIB_CFLAGS) $(PROCESSOR)
+DEPEND[aes-586.s]=../perlasm/x86asm.pl
+GENERATE[vpaes-x86.s]=asm/vpaes-x86.pl $(PERLASM_SCHEME) $(CFLAGS) $(LIB_CFLAGS) $(PROCESSOR)
+DEPEND[vpaes-586.s]=../perlasm/x86asm.pl
+GENERATE[aesni-x86.s]=asm/aesni-x86.pl $(PERLASM_SCHEME) $(CFLAGS) $(LIB_CFLAGS) $(PROCESSOR)
+DEPEND[aesni-586.s]=../perlasm/x86asm.pl
+
+GENERATE[aes-x86_64.s]=asm/aes-x86_64.pl $(PERLASM_SCHEME)
+GENERATE[vpaes-x86_64.s]=asm/vpaes-x86_64.pl $(PERLASM_SCHEME)
+GENERATE[bsaes-x86_64.s]=asm/bsaes-x86_64.pl $(PERLASM_SCHEME)
+GENERATE[aesni-x86_64.s]=asm/aesni-x86_64.pl $(PERLASM_SCHEME)
+GENERATE[aesni-sha1-x86_64.s]=asm/aesni-sha1-x86_64.pl $(PERLASM_SCHEME)
+GENERATE[aesni-sha256-x86_64.s]=asm/aesni-sha256-x86_64.pl $(PERLASM_SCHEME)
+GENERATE[aesni-mb-x86_64.s]=asm/aesni-mb-x86_64.pl $(PERLASM_SCHEME)
+
+GENERATE[aes-sparcv9.S]=asm/aes-sparcv9.pl $(PERLASM_SCHEME)
+INCLUDE[aes-sparcv9.o]=..
+GENERATE[aest4-sparcv9.S]=asm/aest4-sparcv9.pl $(PERLASM_SCHEME)
+INCLUDE[aest4-sparcv9.o]=..
+DEPEND[aest4-sparcv9.S]=../perlasm/sparcv9_modes.pl
+GENERATE[aesfx-sparcv9.S]=asm/aesfx-sparcv9.pl $(PERLASM_SCHEME)
+INCLUDE[aesfx-sparcv9.o]=..
+
+GENERATE[aes-ppc.s]=asm/aes-ppc.pl $(PERLASM_SCHEME)
+GENERATE[vpaes-ppc.s]=asm/vpaes-ppc.pl $(PERLASM_SCHEME)
+GENERATE[aesp8-ppc.s]=asm/aesp8-ppc.pl $(PERLASM_SCHEME)
+
+GENERATE[aes-parisc.s]=asm/aes-parisc.pl $(PERLASM_SCHEME)
+
+GENERATE[aes-mips.S]=asm/aes-mips.pl $(PERLASM_SCHEME)
+
+GENERATE[aesv8-armx.S]=asm/aesv8-armx.pl $(PERLASM_SCHEME)
+INCLUDE[aesv8-armx.o]=..
+GENERATE[vpaes-armv8.S]=asm/vpaes-armv8.pl $(PERLASM_SCHEME)
+
+GENERATE[aes-armv4.S]=asm/aes-armv4.pl $(PERLASM_SCHEME)
+INCLUDE[aes-armv4.o]=..
+GENERATE[bsaes-armv7.S]=asm/bsaes-armv7.pl $(PERLASM_SCHEME)
+INCLUDE[bsaes-armv7.o]=..
+
+BEGINRAW[Makefile]
+##### AES assembler implementations
+
+# GNU make "catch all"
+{- $builddir -}/aes-%.S:	{- $sourcedir -}/asm/aes-%.pl
+	CC="$(CC)" $(PERL) $< $(PERLASM_SCHEME) $@
+{- $builddir -}/bsaes-%.S:	{- $sourcedir -}/asm/bsaes-%.pl
+	CC="$(CC)" $(PERL) $< $(PERLASM_SCHEME) $@
+
+ENDRAW[Makefile]
diff --git a/crypto/alphacpuid.pl b/crypto/alphacpuid.pl
new file mode 100644
index 000000000..6c7fd4c9d
--- /dev/null
+++ b/crypto/alphacpuid.pl
@@ -0,0 +1,257 @@
+#! /usr/bin/env perl
+# Copyright 2010-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+
+$output = pop;
+open STDOUT,">$output";
+
+print <<'___';
+.text
+
+.set	noat
+
+.globl	OPENSSL_cpuid_setup
+.ent	OPENSSL_cpuid_setup
+OPENSSL_cpuid_setup:
+	.frame	$30,0,$26
+	.prologue 0
+	ret	($26)
+.end	OPENSSL_cpuid_setup
+
+.globl	OPENSSL_wipe_cpu
+.ent	OPENSSL_wipe_cpu
+OPENSSL_wipe_cpu:
+	.frame	$30,0,$26
+	.prologue 0
+	clr	$1
+	clr	$2
+	clr	$3
+	clr	$4
+	clr	$5
+	clr	$6
+	clr	$7
+	clr	$8
+	clr	$16
+	clr	$17
+	clr	$18
+	clr	$19
+	clr	$20
+	clr	$21
+	clr	$22
+	clr	$23
+	clr	$24
+	clr	$25
+	clr	$27
+	clr	$at
+	clr	$29
+	fclr	$f0
+	fclr	$f1
+	fclr	$f10
+	fclr	$f11
+	fclr	$f12
+	fclr	$f13
+	fclr	$f14
+	fclr	$f15
+	fclr	$f16
+	fclr	$f17
+	fclr	$f18
+	fclr	$f19
+	fclr	$f20
+	fclr	$f21
+	fclr	$f22
+	fclr	$f23
+	fclr	$f24
+	fclr	$f25
+	fclr	$f26
+	fclr	$f27
+	fclr	$f28
+	fclr	$f29
+	fclr	$f30
+	mov	$sp,$0
+	ret	($26)
+.end	OPENSSL_wipe_cpu
+
+.globl	OPENSSL_atomic_add
+.ent	OPENSSL_atomic_add
+OPENSSL_atomic_add:
+	.frame	$30,0,$26
+	.prologue 0
+1:	ldl_l	$0,0($16)
+	addl	$0,$17,$1
+	stl_c	$1,0($16)
+	beq	$1,1b
+	addl	$0,$17,$0
+	ret	($26)
+.end	OPENSSL_atomic_add
+
+.globl	OPENSSL_rdtsc
+.ent	OPENSSL_rdtsc
+OPENSSL_rdtsc:
+	.frame	$30,0,$26
+	.prologue 0
+	rpcc	$0
+	ret	($26)
+.end	OPENSSL_rdtsc
+
+.globl	OPENSSL_cleanse
+.ent	OPENSSL_cleanse
+OPENSSL_cleanse:
+	.frame	$30,0,$26
+	.prologue 0
+	beq	$17,.Ldone
+	and	$16,7,$0
+	bic	$17,7,$at
+	beq	$at,.Little
+	beq	$0,.Laligned
+
+.Little:
+	subq	$0,8,$0
+	ldq_u	$1,0($16)
+	mov	$16,$2
+.Lalign:
+	mskbl	$1,$16,$1
+	lda	$16,1($16)
+	subq	$17,1,$17
+	addq	$0,1,$0
+	beq	$17,.Lout
+	bne	$0,.Lalign
+.Lout:	stq_u	$1,0($2)
+	beq	$17,.Ldone
+	bic	$17,7,$at
+	beq	$at,.Little
+
+.Laligned:
+	stq	$31,0($16)
+	subq	$17,8,$17
+	lda	$16,8($16)
+	bic	$17,7,$at
+	bne	$at,.Laligned
+	bne	$17,.Little
+.Ldone: ret	($26)
+.end	OPENSSL_cleanse
+
+.globl	CRYPTO_memcmp
+.ent	CRYPTO_memcmp
+CRYPTO_memcmp:
+	.frame	$30,0,$26
+	.prologue 0
+	xor	$0,$0,$0
+	beq	$18,.Lno_data
+
+	xor	$1,$1,$1
+	nop
+.Loop_cmp:
+	ldq_u	$2,0($16)
+	subq	$18,1,$18
+	ldq_u	$3,0($17)
+	extbl	$2,$16,$2
+	lda	$16,1($16)
+	extbl	$3,$17,$3
+	lda	$17,1($17)
+	xor	$3,$2,$2
+	or	$2,$0,$0
+	bne	$18,.Loop_cmp
+
+	subq	$31,$0,$0
+	srl	$0,63,$0
+.Lno_data:
+	ret	($26)
+.end	CRYPTO_memcmp
+___
+{
+my ($out,$cnt,$max)=("\$16","\$17","\$18");
+my ($tick,$lasttick)=("\$19","\$20");
+my ($diff,$lastdiff)=("\$21","\$22");
+my ($v0,$ra,$sp,$zero)=("\$0","\$26","\$30","\$31");
+
+print <<___;
+.globl	OPENSSL_instrument_bus
+.ent	OPENSSL_instrument_bus
+OPENSSL_instrument_bus:
+	.frame	$sp,0,$ra
+	.prologue 0
+	mov	$cnt,$v0
+
+	rpcc	$lasttick
+	mov	0,$diff
+
+	ecb	($out)
+	ldl_l	$tick,0($out)
+	addl	$diff,$tick,$tick
+	mov	$tick,$diff
+	stl_c	$tick,0($out)
+	stl	$diff,0($out)
+
+.Loop:	rpcc	$tick
+	subq	$tick,$lasttick,$diff
+	mov	$tick,$lasttick
+
+	ecb	($out)
+	ldl_l	$tick,0($out)
+	addl	$diff,$tick,$tick
+	mov	$tick,$diff
+	stl_c	$tick,0($out)
+	stl	$diff,0($out)
+
+	subl	$cnt,1,$cnt
+	lda	$out,4($out)
+	bne	$cnt,.Loop
+
+	ret	($ra)
+.end	OPENSSL_instrument_bus
+
+.globl	OPENSSL_instrument_bus2
+.ent	OPENSSL_instrument_bus2
+OPENSSL_instrument_bus2:
+	.frame	$sp,0,$ra
+	.prologue 0
+	mov	$cnt,$v0
+
+	rpcc	$lasttick
+	mov	0,$diff
+
+	ecb	($out)
+	ldl_l	$tick,0($out)
+	addl	$diff,$tick,$tick
+	mov	$tick,$diff
+	stl_c	$tick,0($out)
+	stl	$diff,0($out)
+
+	rpcc	$tick
+	subq	$tick,$lasttick,$diff
+	mov	$tick,$lasttick
+	mov	$diff,$lastdiff
+.Loop2:
+	ecb	($out)
+	ldl_l	$tick,0($out)
+	addl	$diff,$tick,$tick
+	mov	$tick,$diff
+	stl_c	$tick,0($out)
+	stl	$diff,0($out)
+
+	subl	$max,1,$max
+	beq	$max,.Ldone2
+
+	rpcc	$tick
+	subq	$tick,$lasttick,$diff
+	mov	$tick,$lasttick
+	subq	$lastdiff,$diff,$tick
+	mov	$diff,$lastdiff
+	cmovne	$tick,1,$tick
+	subl	$cnt,$tick,$cnt
+	s4addq	$tick,$out,$out
+	bne	$cnt,.Loop2
+
+.Ldone2:
+	subl	$v0,$cnt,$v0
+	ret	($ra)
+.end	OPENSSL_instrument_bus2
+___
+}
+
+close STDOUT;
diff --git a/crypto/arm64cpuid.pl b/crypto/arm64cpuid.pl
new file mode 100755
index 000000000..caa33875c
--- /dev/null
+++ b/crypto/arm64cpuid.pl
@@ -0,0 +1,126 @@
+#! /usr/bin/env perl
+# Copyright 2015-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+
+$flavour = shift;
+$output  = shift;
+
+$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+( $xlate="${dir}arm-xlate.pl" and -f $xlate ) or
+( $xlate="${dir}perlasm/arm-xlate.pl" and -f $xlate) or
+die "can't locate arm-xlate.pl";
+
+open OUT,"| \"$^X\" $xlate $flavour $output";
+*STDOUT=*OUT;
+
+$code.=<<___;
+#include "arm_arch.h"
+
+.text
+.arch	armv8-a+crypto
+
+.align	5
+.globl	_armv7_neon_probe
+.type	_armv7_neon_probe,%function
+_armv7_neon_probe:
+	orr	v15.16b, v15.16b, v15.16b
+	ret
+.size	_armv7_neon_probe,.-_armv7_neon_probe
+
+.globl	_armv7_tick
+.type	_armv7_tick,%function
+_armv7_tick:
+#ifdef	__APPLE__
+	mrs	x0, CNTPCT_EL0
+#else
+	mrs	x0, CNTVCT_EL0
+#endif
+	ret
+.size	_armv7_tick,.-_armv7_tick
+
+.globl	_armv8_aes_probe
+.type	_armv8_aes_probe,%function
+_armv8_aes_probe:
+	aese	v0.16b, v0.16b
+	ret
+.size	_armv8_aes_probe,.-_armv8_aes_probe
+
+.globl	_armv8_sha1_probe
+.type	_armv8_sha1_probe,%function
+_armv8_sha1_probe:
+	sha1h	s0, s0
+	ret
+.size	_armv8_sha1_probe,.-_armv8_sha1_probe
+
+.globl	_armv8_sha256_probe
+.type	_armv8_sha256_probe,%function
+_armv8_sha256_probe:
+	sha256su0	v0.4s, v0.4s
+	ret
+.size	_armv8_sha256_probe,.-_armv8_sha256_probe
+.globl	_armv8_pmull_probe
+.type	_armv8_pmull_probe,%function
+_armv8_pmull_probe:
+	pmull	v0.1q, v0.1d, v0.1d
+	ret
+.size	_armv8_pmull_probe,.-_armv8_pmull_probe
+
+.globl	OPENSSL_cleanse
+.type	OPENSSL_cleanse,%function
+.align	5
+OPENSSL_cleanse:
+	cbz	x1,.Lret	// len==0?
+	cmp	x1,#15
+	b.hi	.Lot		// len>15
+	nop
+.Little:
+	strb	wzr,[x0],#1	// store byte-by-byte
+	subs	x1,x1,#1
+	b.ne	.Little
+.Lret:	ret
+
+.align	4
+.Lot:	tst	x0,#7
+	b.eq	.Laligned	// inp is aligned
+	strb	wzr,[x0],#1	// store byte-by-byte
+	sub	x1,x1,#1
+	b	.Lot
+
+.align	4
+.Laligned:
+	str	xzr,[x0],#8	// store word-by-word
+	sub	x1,x1,#8
+	tst	x1,#-8
+	b.ne	.Laligned	// len>=8
+	cbnz	x1,.Little	// len!=0?
+	ret
+.size	OPENSSL_cleanse,.-OPENSSL_cleanse
+
+.globl	CRYPTO_memcmp
+.type	CRYPTO_memcmp,%function
+.align	4
+CRYPTO_memcmp:
+	eor	w3,w3,w3
+	cbz	x2,.Lno_data	// len==0?
+.Loop_cmp:
+	ldrb	w4,[x0],#1
+	ldrb	w5,[x1],#1
+	eor	w4,w4,w5
+	orr	w3,w3,w4
+	subs	x2,x2,#1
+	b.ne	.Loop_cmp
+
+.Lno_data:
+	neg	w0,w3
+	lsr	w0,w0,#31
+	ret
+.size	CRYPTO_memcmp,.-CRYPTO_memcmp
+___
+
+print $code;
+close STDOUT;
diff --git a/crypto/arm_arch.h b/crypto/arm_arch.h
new file mode 100644
index 000000000..3fc9e69b1
--- /dev/null
+++ b/crypto/arm_arch.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2011-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#ifndef __ARM_ARCH_H__
+# define __ARM_ARCH_H__
+
+# if !defined(__ARM_ARCH__)
+#  if defined(__CC_ARM)
+#   define __ARM_ARCH__ __TARGET_ARCH_ARM
+#   if defined(__BIG_ENDIAN)
+#    define __ARMEB__
+#   else
+#    define __ARMEL__
+#   endif
+#  elif defined(__GNUC__)
+#   if   defined(__aarch64__)
+#    define __ARM_ARCH__ 8
+#    if __BYTE_ORDER__==__ORDER_BIG_ENDIAN__
+#     define __ARMEB__
+#    else
+#     define __ARMEL__
+#    endif
+  /*
+   * Why doesn't gcc define __ARM_ARCH__? Instead it defines
+   * bunch of below macros. See all_architectires[] table in
+   * gcc/config/arm/arm.c. On a side note it defines
+   * __ARMEL__/__ARMEB__ for little-/big-endian.
+   */
+#   elif defined(__ARM_ARCH)
+#    define __ARM_ARCH__ __ARM_ARCH
+#   elif defined(__ARM_ARCH_8A__)
+#    define __ARM_ARCH__ 8
+#   elif defined(__ARM_ARCH_7__) || defined(__ARM_ARCH_7A__)     || \
+        defined(__ARM_ARCH_7R__)|| defined(__ARM_ARCH_7M__)     || \
+        defined(__ARM_ARCH_7EM__)
+#    define __ARM_ARCH__ 7
+#   elif defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_6J__)     || \
+        defined(__ARM_ARCH_6K__)|| defined(__ARM_ARCH_6M__)     || \
+        defined(__ARM_ARCH_6Z__)|| defined(__ARM_ARCH_6ZK__)    || \
+        defined(__ARM_ARCH_6T2__)
+#    define __ARM_ARCH__ 6
+#   elif defined(__ARM_ARCH_5__) || defined(__ARM_ARCH_5T__)     || \
+        defined(__ARM_ARCH_5E__)|| defined(__ARM_ARCH_5TE__)    || \
+        defined(__ARM_ARCH_5TEJ__)
+#    define __ARM_ARCH__ 5
+#   elif defined(__ARM_ARCH_4__) || defined(__ARM_ARCH_4T__)
+#    define __ARM_ARCH__ 4
+#   else
+#    error "unsupported ARM architecture"
+#   endif
+#  endif
+# endif
+
+# if !defined(__ARM_MAX_ARCH__)
+#  define __ARM_MAX_ARCH__ __ARM_ARCH__
+# endif
+
+# if __ARM_MAX_ARCH__<__ARM_ARCH__
+#  error "__ARM_MAX_ARCH__ can't be less than __ARM_ARCH__"
+# elif __ARM_MAX_ARCH__!=__ARM_ARCH__
+#  if __ARM_ARCH__<7 && __ARM_MAX_ARCH__>=7 && defined(__ARMEB__)
+#   error "can't build universal big-endian binary"
+#  endif
+# endif
+
+# if !__ASSEMBLER__
+extern unsigned int OPENSSL_armcap_P;
+# endif
+
+# define ARMV7_NEON      (1<<0)
+# define ARMV7_TICK      (1<<1)
+# define ARMV8_AES       (1<<2)
+# define ARMV8_SHA1      (1<<3)
+# define ARMV8_SHA256    (1<<4)
+# define ARMV8_PMULL     (1<<5)
+
+#endif
diff --git a/crypto/armcap.c b/crypto/armcap.c
new file mode 100644
index 000000000..4215766bf
--- /dev/null
+++ b/crypto/armcap.c
@@ -0,0 +1,175 @@
+/*
+ * Copyright 2011-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#include "arm_arch.h"
+
+unsigned int OPENSSL_armcap_P = 0;
+
+#if __ARM_MAX_ARCH__<7
+void OPENSSL_cpuid_setup(void)
+{
+}
+
+unsigned long OPENSSL_rdtsc(void)
+{
+    return 0;
+}
+#else
+static sigset_t all_masked;
+
+static sigjmp_buf ill_jmp;
+static void ill_handler(int sig)
+{
+    siglongjmp(ill_jmp, sig);
+}
+
+/*
+ * Following subroutines could have been inlined, but it's not all
+ * ARM compilers support inline assembler...
+ */
+void _armv7_neon_probe(void);
+void _armv8_aes_probe(void);
+void _armv8_sha1_probe(void);
+void _armv8_sha256_probe(void);
+void _armv8_pmull_probe(void);
+unsigned long _armv7_tick(void);
+
+unsigned long OPENSSL_rdtsc(void)
+{
+    if (OPENSSL_armcap_P & ARMV7_TICK)
+        return _armv7_tick();
+    else
+        return 0;
+}
+
+# if defined(__GNUC__) && __GNUC__>=2
+void OPENSSL_cpuid_setup(void) __attribute__ ((constructor));
+# endif
+/*
+ * Use a weak reference to getauxval() so we can use it if it is available but
+ * don't break the build if it is not.
+ */
+# if defined(__GNUC__) && __GNUC__>=2 && defined(__ELF__)
+extern unsigned long getauxval(unsigned long type) __attribute__ ((weak));
+# else
+static unsigned long (*getauxval) (unsigned long) = NULL;
+# endif
+
+/*
+ * ARM puts the the feature bits for Crypto Extensions in AT_HWCAP2, whereas
+ * AArch64 used AT_HWCAP.
+ */
+# if defined(__arm__) || defined (__arm)
+#  define HWCAP                  16
+                                  /* AT_HWCAP */
+#  define HWCAP_NEON             (1 << 12)
+
+#  define HWCAP_CE               26
+                                  /* AT_HWCAP2 */
+#  define HWCAP_CE_AES           (1 << 0)
+#  define HWCAP_CE_PMULL         (1 << 1)
+#  define HWCAP_CE_SHA1          (1 << 2)
+#  define HWCAP_CE_SHA256        (1 << 3)
+# elif defined(__aarch64__)
+#  define HWCAP                  16
+                                  /* AT_HWCAP */
+#  define HWCAP_NEON             (1 << 1)
+
+#  define HWCAP_CE               HWCAP
+#  define HWCAP_CE_AES           (1 << 3)
+#  define HWCAP_CE_PMULL         (1 << 4)
+#  define HWCAP_CE_SHA1          (1 << 5)
+#  define HWCAP_CE_SHA256        (1 << 6)
+# endif
+
+void OPENSSL_cpuid_setup(void)
+{
+    char *e;
+    struct sigaction ill_oact, ill_act;
+    sigset_t oset;
+    static int trigger = 0;
+
+    if (trigger)
+        return;
+    trigger = 1;
+
+    if ((e = getenv("OPENSSL_armcap"))) {
+        OPENSSL_armcap_P = (unsigned int)strtoul(e, NULL, 0);
+        return;
+    }
+
+    sigfillset(&all_masked);
+    sigdelset(&all_masked, SIGILL);
+    sigdelset(&all_masked, SIGTRAP);
+    sigdelset(&all_masked, SIGFPE);
+    sigdelset(&all_masked, SIGBUS);
+    sigdelset(&all_masked, SIGSEGV);
+
+    OPENSSL_armcap_P = 0;
+
+    memset(&ill_act, 0, sizeof(ill_act));
+    ill_act.sa_handler = ill_handler;
+    ill_act.sa_mask = all_masked;
+
+    sigprocmask(SIG_SETMASK, &ill_act.sa_mask, &oset);
+    sigaction(SIGILL, &ill_act, &ill_oact);
+
+    if (getauxval != NULL) {
+        if (getauxval(HWCAP) & HWCAP_NEON) {
+            unsigned long hwcap = getauxval(HWCAP_CE);
+
+            OPENSSL_armcap_P |= ARMV7_NEON;
+
+            if (hwcap & HWCAP_CE_AES)
+                OPENSSL_armcap_P |= ARMV8_AES;
+
+            if (hwcap & HWCAP_CE_PMULL)
+                OPENSSL_armcap_P |= ARMV8_PMULL;
+
+            if (hwcap & HWCAP_CE_SHA1)
+                OPENSSL_armcap_P |= ARMV8_SHA1;
+
+            if (hwcap & HWCAP_CE_SHA256)
+                OPENSSL_armcap_P |= ARMV8_SHA256;
+        }
+    } else if (sigsetjmp(ill_jmp, 1) == 0) {
+        _armv7_neon_probe();
+        OPENSSL_armcap_P |= ARMV7_NEON;
+        if (sigsetjmp(ill_jmp, 1) == 0) {
+            _armv8_pmull_probe();
+            OPENSSL_armcap_P |= ARMV8_PMULL | ARMV8_AES;
+        } else if (sigsetjmp(ill_jmp, 1) == 0) {
+            _armv8_aes_probe();
+            OPENSSL_armcap_P |= ARMV8_AES;
+        }
+        if (sigsetjmp(ill_jmp, 1) == 0) {
+            _armv8_sha1_probe();
+            OPENSSL_armcap_P |= ARMV8_SHA1;
+        }
+        if (sigsetjmp(ill_jmp, 1) == 0) {
+            _armv8_sha256_probe();
+            OPENSSL_armcap_P |= ARMV8_SHA256;
+        }
+    }
+    if (sigsetjmp(ill_jmp, 1) == 0) {
+        _armv7_tick();
+        OPENSSL_armcap_P |= ARMV7_TICK;
+    }
+
+    sigaction(SIGILL, &ill_oact, NULL);
+    sigprocmask(SIG_SETMASK, &oset, NULL);
+}
+#endif
diff --git a/crypto/armv4cpuid.pl b/crypto/armv4cpuid.pl
new file mode 100644
index 000000000..33c893d0e
--- /dev/null
+++ b/crypto/armv4cpuid.pl
@@ -0,0 +1,295 @@
+#! /usr/bin/env perl
+# Copyright 2015-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+
+$flavour = shift;
+$output  = shift;
+
+$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+( $xlate="${dir}arm-xlate.pl" and -f $xlate ) or
+( $xlate="${dir}perlasm/arm-xlate.pl" and -f $xlate) or
+die "can't locate arm-xlate.pl";
+
+open OUT,"| \"$^X\" $xlate $flavour $output";
+*STDOUT=*OUT;
+
+$code.=<<___;
+#include "arm_arch.h"
+
+.text
+#if defined(__thumb2__) && !defined(__APPLE__)
+.syntax	unified
+.thumb
+#else
+.code	32
+#endif
+
+.align	5
+.global	OPENSSL_atomic_add
+.type	OPENSSL_atomic_add,%function
+OPENSSL_atomic_add:
+#if __ARM_ARCH__>=6
+.Ladd:	ldrex	r2,[r0]
+	add	r3,r2,r1
+	strex	r2,r3,[r0]
+	cmp	r2,#0
+	bne	.Ladd
+	mov	r0,r3
+	bx	lr
+#else
+	stmdb	sp!,{r4-r6,lr}
+	ldr	r2,.Lspinlock
+	adr	r3,.Lspinlock
+	mov	r4,r0
+	mov	r5,r1
+	add	r6,r3,r2	@ &spinlock
+	b	.+8
+.Lspin:	bl	sched_yield
+	mov	r0,#-1
+	swp	r0,r0,[r6]
+	cmp	r0,#0
+	bne	.Lspin
+
+	ldr	r2,[r4]
+	add	r2,r2,r5
+	str	r2,[r4]
+	str	r0,[r6]		@ release spinlock
+	ldmia	sp!,{r4-r6,lr}
+	tst	lr,#1
+	moveq	pc,lr
+	.word	0xe12fff1e	@ bx	lr
+#endif
+.size	OPENSSL_atomic_add,.-OPENSSL_atomic_add
+
+.global	OPENSSL_cleanse
+.type	OPENSSL_cleanse,%function
+OPENSSL_cleanse:
+	eor	ip,ip,ip
+	cmp	r1,#7
+#ifdef	__thumb2__
+	itt	hs
+#endif
+	subhs	r1,r1,#4
+	bhs	.Lot
+	cmp	r1,#0
+	beq	.Lcleanse_done
+.Little:
+	strb	ip,[r0],#1
+	subs	r1,r1,#1
+	bhi	.Little
+	b	.Lcleanse_done
+
+.Lot:	tst	r0,#3
+	beq	.Laligned
+	strb	ip,[r0],#1
+	sub	r1,r1,#1
+	b	.Lot
+.Laligned:
+	str	ip,[r0],#4
+	subs	r1,r1,#4
+	bhs	.Laligned
+	adds	r1,r1,#4
+	bne	.Little
+.Lcleanse_done:
+#if __ARM_ARCH__>=5
+	bx	lr
+#else
+	tst	lr,#1
+	moveq	pc,lr
+	.word	0xe12fff1e	@ bx	lr
+#endif
+.size	OPENSSL_cleanse,.-OPENSSL_cleanse
+
+.global	CRYPTO_memcmp
+.type	CRYPTO_memcmp,%function
+.align	4
+CRYPTO_memcmp:
+	eor	ip,ip,ip
+	cmp	r2,#0
+	beq	.Lno_data
+	stmdb	sp!,{r4,r5}
+
+.Loop_cmp:
+	ldrb	r4,[r0],#1
+	ldrb	r5,[r1],#1
+	eor	r4,r4,r5
+	orr	ip,ip,r4
+	subs	r2,r2,#1
+	bne	.Loop_cmp
+
+	ldmia	sp!,{r4,r5}
+.Lno_data:
+	neg	r0,ip
+	mov	r0,r0,lsr#31
+#if __ARM_ARCH__>=5
+	bx	lr
+#else
+	tst	lr,#1
+	moveq	pc,lr
+	.word	0xe12fff1e	@ bx	lr
+#endif
+.size	CRYPTO_memcmp,.-CRYPTO_memcmp
+
+#if __ARM_MAX_ARCH__>=7
+.arch	armv7-a
+.fpu	neon
+
+.align	5
+.global	_armv7_neon_probe
+.type	_armv7_neon_probe,%function
+_armv7_neon_probe:
+	vorr	q0,q0,q0
+	bx	lr
+.size	_armv7_neon_probe,.-_armv7_neon_probe
+
+.global	_armv7_tick
+.type	_armv7_tick,%function
+_armv7_tick:
+#ifdef	__APPLE__
+	mrrc	p15,0,r0,r1,c14		@ CNTPCT
+#else
+	mrrc	p15,1,r0,r1,c14		@ CNTVCT
+#endif
+	bx	lr
+.size	_armv7_tick,.-_armv7_tick
+
+.global	_armv8_aes_probe
+.type	_armv8_aes_probe,%function
+_armv8_aes_probe:
+#if defined(__thumb2__) && !defined(__APPLE__)
+	.byte	0xb0,0xff,0x00,0x03	@ aese.8	q0,q0
+#else
+	.byte	0x00,0x03,0xb0,0xf3	@ aese.8	q0,q0
+#endif
+	bx	lr
+.size	_armv8_aes_probe,.-_armv8_aes_probe
+
+.global	_armv8_sha1_probe
+.type	_armv8_sha1_probe,%function
+_armv8_sha1_probe:
+#if defined(__thumb2__) && !defined(__APPLE__)
+	.byte	0x00,0xef,0x40,0x0c	@ sha1c.32	q0,q0,q0
+#else
+	.byte	0x40,0x0c,0x00,0xf2	@ sha1c.32	q0,q0,q0
+#endif
+	bx	lr
+.size	_armv8_sha1_probe,.-_armv8_sha1_probe
+
+.global	_armv8_sha256_probe
+.type	_armv8_sha256_probe,%function
+_armv8_sha256_probe:
+#if defined(__thumb2__) && !defined(__APPLE__)
+	.byte	0x00,0xff,0x40,0x0c	@ sha256h.32	q0,q0,q0
+#else
+	.byte	0x40,0x0c,0x00,0xf3	@ sha256h.32	q0,q0,q0
+#endif
+	bx	lr
+.size	_armv8_sha256_probe,.-_armv8_sha256_probe
+.global	_armv8_pmull_probe
+.type	_armv8_pmull_probe,%function
+_armv8_pmull_probe:
+#if defined(__thumb2__) && !defined(__APPLE__)
+	.byte	0xa0,0xef,0x00,0x0e	@ vmull.p64	q0,d0,d0
+#else
+	.byte	0x00,0x0e,0xa0,0xf2	@ vmull.p64	q0,d0,d0
+#endif
+	bx	lr
+.size	_armv8_pmull_probe,.-_armv8_pmull_probe
+#endif
+
+.global	OPENSSL_wipe_cpu
+.type	OPENSSL_wipe_cpu,%function
+OPENSSL_wipe_cpu:
+#if __ARM_MAX_ARCH__>=7
+	ldr	r0,.LOPENSSL_armcap
+	adr	r1,.LOPENSSL_armcap
+	ldr	r0,[r1,r0]
+#ifdef	__APPLE__
+	ldr	r0,[r0]
+#endif
+#endif
+	eor	r2,r2,r2
+	eor	r3,r3,r3
+	eor	ip,ip,ip
+#if __ARM_MAX_ARCH__>=7
+	tst	r0,#1
+	beq	.Lwipe_done
+	veor	q0, q0, q0
+	veor	q1, q1, q1
+	veor	q2, q2, q2
+	veor	q3, q3, q3
+	veor	q8, q8, q8
+	veor	q9, q9, q9
+	veor	q10, q10, q10
+	veor	q11, q11, q11
+	veor	q12, q12, q12
+	veor	q13, q13, q13
+	veor	q14, q14, q14
+	veor	q15, q15, q15
+.Lwipe_done:
+#endif
+	mov	r0,sp
+#if __ARM_ARCH__>=5
+	bx	lr
+#else
+	tst	lr,#1
+	moveq	pc,lr
+	.word	0xe12fff1e	@ bx	lr
+#endif
+.size	OPENSSL_wipe_cpu,.-OPENSSL_wipe_cpu
+
+.global	OPENSSL_instrument_bus
+.type	OPENSSL_instrument_bus,%function
+OPENSSL_instrument_bus:
+	eor	r0,r0,r0
+#if __ARM_ARCH__>=5
+	bx	lr
+#else
+	tst	lr,#1
+	moveq	pc,lr
+	.word	0xe12fff1e	@ bx	lr
+#endif
+.size	OPENSSL_instrument_bus,.-OPENSSL_instrument_bus
+
+.global	OPENSSL_instrument_bus2
+.type	OPENSSL_instrument_bus2,%function
+OPENSSL_instrument_bus2:
+	eor	r0,r0,r0
+#if __ARM_ARCH__>=5
+	bx	lr
+#else
+	tst	lr,#1
+	moveq	pc,lr
+	.word	0xe12fff1e	@ bx	lr
+#endif
+.size	OPENSSL_instrument_bus2,.-OPENSSL_instrument_bus2
+
+.align	5
+#if __ARM_MAX_ARCH__>=7
+.LOPENSSL_armcap:
+.word	OPENSSL_armcap_P-.
+#endif
+#if __ARM_ARCH__>=6
+.align	5
+#else
+.Lspinlock:
+.word	atomic_add_spinlock-.Lspinlock
+.align	5
+
+.data
+.align	2
+atomic_add_spinlock:
+.word	0
+#endif
+
+.comm	OPENSSL_armcap_P,4,4
+.hidden	OPENSSL_armcap_P
+___
+
+print $code;
+close STDOUT;
diff --git a/crypto/asn1/a_bitstr.c b/crypto/asn1/a_bitstr.c
new file mode 100644
index 000000000..33be907f9
--- /dev/null
+++ b/crypto/asn1/a_bitstr.c
@@ -0,0 +1,210 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include "asn1_locl.h"
+
+int ASN1_BIT_STRING_set(ASN1_BIT_STRING *x, unsigned char *d, int len)
+{
+    return ASN1_STRING_set(x, d, len);
+}
+
+int i2c_ASN1_BIT_STRING(ASN1_BIT_STRING *a, unsigned char **pp)
+{
+    int ret, j, bits, len;
+    unsigned char *p, *d;
+
+    if (a == NULL)
+        return (0);
+
+    len = a->length;
+
+    if (len > 0) {
+        if (a->flags & ASN1_STRING_FLAG_BITS_LEFT) {
+            bits = (int)a->flags & 0x07;
+        } else {
+            for (; len > 0; len--) {
+                if (a->data[len - 1])
+                    break;
+            }
+            j = a->data[len - 1];
+            if (j & 0x01)
+                bits = 0;
+            else if (j & 0x02)
+                bits = 1;
+            else if (j & 0x04)
+                bits = 2;
+            else if (j & 0x08)
+                bits = 3;
+            else if (j & 0x10)
+                bits = 4;
+            else if (j & 0x20)
+                bits = 5;
+            else if (j & 0x40)
+                bits = 6;
+            else if (j & 0x80)
+                bits = 7;
+            else
+                bits = 0;       /* should not happen */
+        }
+    } else
+        bits = 0;
+
+    ret = 1 + len;
+    if (pp == NULL)
+        return (ret);
+
+    p = *pp;
+
+    *(p++) = (unsigned char)bits;
+    d = a->data;
+    if (len > 0) {
+        memcpy(p, d, len);
+        p += len;
+        p[-1] &= (0xff << bits);
+    }
+    *pp = p;
+    return (ret);
+}
+
+ASN1_BIT_STRING *c2i_ASN1_BIT_STRING(ASN1_BIT_STRING **a,
+                                     const unsigned char **pp, long len)
+{
+    ASN1_BIT_STRING *ret = NULL;
+    const unsigned char *p;
+    unsigned char *s;
+    int i;
+
+    if (len < 1) {
+        i = ASN1_R_STRING_TOO_SHORT;
+        goto err;
+    }
+
+    if ((a == NULL) || ((*a) == NULL)) {
+        if ((ret = ASN1_BIT_STRING_new()) == NULL)
+            return (NULL);
+    } else
+        ret = (*a);
+
+    p = *pp;
+    i = *(p++);
+    if (i > 7) {
+        i = ASN1_R_INVALID_BIT_STRING_BITS_LEFT;
+        goto err;
+    }
+    /*
+     * We do this to preserve the settings.  If we modify the settings, via
+     * the _set_bit function, we will recalculate on output
+     */
+    ret->flags &= ~(ASN1_STRING_FLAG_BITS_LEFT | 0x07); /* clear */
+    ret->flags |= (ASN1_STRING_FLAG_BITS_LEFT | i); /* set */
+
+    if (len-- > 1) {            /* using one because of the bits left byte */
+        s = OPENSSL_malloc((int)len);
+        if (s == NULL) {
+            i = ERR_R_MALLOC_FAILURE;
+            goto err;
+        }
+        memcpy(s, p, (int)len);
+        s[len - 1] &= (0xff << i);
+        p += len;
+    } else
+        s = NULL;
+
+    ret->length = (int)len;
+    OPENSSL_free(ret->data);
+    ret->data = s;
+    ret->type = V_ASN1_BIT_STRING;
+    if (a != NULL)
+        (*a) = ret;
+    *pp = p;
+    return (ret);
+ err:
+    ASN1err(ASN1_F_C2I_ASN1_BIT_STRING, i);
+    if ((a == NULL) || (*a != ret))
+        ASN1_BIT_STRING_free(ret);
+    return (NULL);
+}
+
+/*
+ * These next 2 functions from Goetz Babin-Ebell 
+ */
+int ASN1_BIT_STRING_set_bit(ASN1_BIT_STRING *a, int n, int value)
+{
+    int w, v, iv;
+    unsigned char *c;
+
+    w = n / 8;
+    v = 1 << (7 - (n & 0x07));
+    iv = ~v;
+    if (!value)
+        v = 0;
+
+    if (a == NULL)
+        return 0;
+
+    a->flags &= ~(ASN1_STRING_FLAG_BITS_LEFT | 0x07); /* clear, set on write */
+
+    if ((a->length < (w + 1)) || (a->data == NULL)) {
+        if (!value)
+            return (1);         /* Don't need to set */
+        c = OPENSSL_clear_realloc(a->data, a->length, w + 1);
+        if (c == NULL) {
+            ASN1err(ASN1_F_ASN1_BIT_STRING_SET_BIT, ERR_R_MALLOC_FAILURE);
+            return 0;
+        }
+        if (w + 1 - a->length > 0)
+            memset(c + a->length, 0, w + 1 - a->length);
+        a->data = c;
+        a->length = w + 1;
+    }
+    a->data[w] = ((a->data[w]) & iv) | v;
+    while ((a->length > 0) && (a->data[a->length - 1] == 0))
+        a->length--;
+    return (1);
+}
+
+int ASN1_BIT_STRING_get_bit(const ASN1_BIT_STRING *a, int n)
+{
+    int w, v;
+
+    w = n / 8;
+    v = 1 << (7 - (n & 0x07));
+    if ((a == NULL) || (a->length < (w + 1)) || (a->data == NULL))
+        return (0);
+    return ((a->data[w] & v) != 0);
+}
+
+/*
+ * Checks if the given bit string contains only bits specified by
+ * the flags vector. Returns 0 if there is at least one bit set in 'a'
+ * which is not specified in 'flags', 1 otherwise.
+ * 'len' is the length of 'flags'.
+ */
+int ASN1_BIT_STRING_check(const ASN1_BIT_STRING *a,
+                          const unsigned char *flags, int flags_len)
+{
+    int i, ok;
+    /* Check if there is one bit set at all. */
+    if (!a || !a->data)
+        return 1;
+
+    /*
+     * Check each byte of the internal representation of the bit string.
+     */
+    ok = 1;
+    for (i = 0; i < a->length && ok; ++i) {
+        unsigned char mask = i < flags_len ? ~flags[i] : 0xff;
+        /* We are done if there is an unneeded bit set. */
+        ok = (a->data[i] & mask) == 0;
+    }
+    return ok;
+}
diff --git a/crypto/asn1/a_d2i_fp.c b/crypto/asn1/a_d2i_fp.c
new file mode 100644
index 000000000..e5c1d0ed7
--- /dev/null
+++ b/crypto/asn1/a_d2i_fp.c
@@ -0,0 +1,235 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include "internal/cryptlib.h"
+#include "internal/numbers.h"
+#include 
+#include 
+
+static int asn1_d2i_read_bio(BIO *in, BUF_MEM **pb);
+
+#ifndef NO_OLD_ASN1
+# ifndef OPENSSL_NO_STDIO
+
+void *ASN1_d2i_fp(void *(*xnew) (void), d2i_of_void *d2i, FILE *in, void **x)
+{
+    BIO *b;
+    void *ret;
+
+    if ((b = BIO_new(BIO_s_file())) == NULL) {
+        ASN1err(ASN1_F_ASN1_D2I_FP, ERR_R_BUF_LIB);
+        return (NULL);
+    }
+    BIO_set_fp(b, in, BIO_NOCLOSE);
+    ret = ASN1_d2i_bio(xnew, d2i, b, x);
+    BIO_free(b);
+    return (ret);
+}
+# endif
+
+void *ASN1_d2i_bio(void *(*xnew) (void), d2i_of_void *d2i, BIO *in, void **x)
+{
+    BUF_MEM *b = NULL;
+    const unsigned char *p;
+    void *ret = NULL;
+    int len;
+
+    len = asn1_d2i_read_bio(in, &b);
+    if (len < 0)
+        goto err;
+
+    p = (unsigned char *)b->data;
+    ret = d2i(x, &p, len);
+ err:
+    BUF_MEM_free(b);
+    return (ret);
+}
+
+#endif
+
+void *ASN1_item_d2i_bio(const ASN1_ITEM *it, BIO *in, void *x)
+{
+    BUF_MEM *b = NULL;
+    const unsigned char *p;
+    void *ret = NULL;
+    int len;
+
+    len = asn1_d2i_read_bio(in, &b);
+    if (len < 0)
+        goto err;
+
+    p = (const unsigned char *)b->data;
+    ret = ASN1_item_d2i(x, &p, len, it);
+ err:
+    BUF_MEM_free(b);
+    return (ret);
+}
+
+#ifndef OPENSSL_NO_STDIO
+void *ASN1_item_d2i_fp(const ASN1_ITEM *it, FILE *in, void *x)
+{
+    BIO *b;
+    char *ret;
+
+    if ((b = BIO_new(BIO_s_file())) == NULL) {
+        ASN1err(ASN1_F_ASN1_ITEM_D2I_FP, ERR_R_BUF_LIB);
+        return (NULL);
+    }
+    BIO_set_fp(b, in, BIO_NOCLOSE);
+    ret = ASN1_item_d2i_bio(it, b, x);
+    BIO_free(b);
+    return (ret);
+}
+#endif
+
+#define HEADER_SIZE   8
+#define ASN1_CHUNK_INITIAL_SIZE (16 * 1024)
+static int asn1_d2i_read_bio(BIO *in, BUF_MEM **pb)
+{
+    BUF_MEM *b;
+    unsigned char *p;
+    int i;
+    size_t want = HEADER_SIZE;
+    uint32_t eos = 0;
+    size_t off = 0;
+    size_t len = 0;
+
+    const unsigned char *q;
+    long slen;
+    int inf, tag, xclass;
+
+    b = BUF_MEM_new();
+    if (b == NULL) {
+        ASN1err(ASN1_F_ASN1_D2I_READ_BIO, ERR_R_MALLOC_FAILURE);
+        return -1;
+    }
+
+    ERR_clear_error();
+    for (;;) {
+        if (want >= (len - off)) {
+            want -= (len - off);
+
+            if (len + want < len || !BUF_MEM_grow_clean(b, len + want)) {
+                ASN1err(ASN1_F_ASN1_D2I_READ_BIO, ERR_R_MALLOC_FAILURE);
+                goto err;
+            }
+            i = BIO_read(in, &(b->data[len]), want);
+            if ((i < 0) && ((len - off) == 0)) {
+                ASN1err(ASN1_F_ASN1_D2I_READ_BIO, ASN1_R_NOT_ENOUGH_DATA);
+                goto err;
+            }
+            if (i > 0) {
+                if (len + i < len) {
+                    ASN1err(ASN1_F_ASN1_D2I_READ_BIO, ASN1_R_TOO_LONG);
+                    goto err;
+                }
+                len += i;
+            }
+        }
+        /* else data already loaded */
+
+        p = (unsigned char *)&(b->data[off]);
+        q = p;
+        inf = ASN1_get_object(&q, &slen, &tag, &xclass, len - off);
+        if (inf & 0x80) {
+            unsigned long e;
+
+            e = ERR_GET_REASON(ERR_peek_error());
+            if (e != ASN1_R_TOO_LONG)
+                goto err;
+            else
+                ERR_clear_error(); /* clear error */
+        }
+        i = q - p;            /* header length */
+        off += i;               /* end of data */
+
+        if (inf & 1) {
+            /* no data body so go round again */
+            if (eos == UINT32_MAX) {
+                ASN1err(ASN1_F_ASN1_D2I_READ_BIO, ASN1_R_HEADER_TOO_LONG);
+                goto err;
+            }
+            eos++;
+            want = HEADER_SIZE;
+        } else if (eos && (slen == 0) && (tag == V_ASN1_EOC)) {
+            /* eos value, so go back and read another header */
+            eos--;
+            if (eos == 0)
+                break;
+            else
+                want = HEADER_SIZE;
+        } else {
+            /* suck in slen bytes of data */
+            want = slen;
+            if (want > (len - off)) {
+                size_t chunk_max = ASN1_CHUNK_INITIAL_SIZE;
+
+                want -= (len - off);
+                if (want > INT_MAX /* BIO_read takes an int length */  ||
+                    len + want < len) {
+                    ASN1err(ASN1_F_ASN1_D2I_READ_BIO, ASN1_R_TOO_LONG);
+                    goto err;
+                }
+                while (want > 0) {
+                    /*
+                     * Read content in chunks of increasing size
+                     * so we can return an error for EOF without
+                     * having to allocate the entire content length
+                     * in one go.
+                     */
+                    size_t chunk = want > chunk_max ? chunk_max : want;
+
+                    if (!BUF_MEM_grow_clean(b, len + chunk)) {
+                        ASN1err(ASN1_F_ASN1_D2I_READ_BIO, ERR_R_MALLOC_FAILURE);
+                        goto err;
+                    }
+                    want -= chunk;
+                    while (chunk > 0) {
+                        i = BIO_read(in, &(b->data[len]), chunk);
+                        if (i <= 0) {
+                            ASN1err(ASN1_F_ASN1_D2I_READ_BIO,
+                                    ASN1_R_NOT_ENOUGH_DATA);
+                            goto err;
+                        }
+                    /*
+                     * This can't overflow because |len+want| didn't
+                     * overflow.
+                     */
+                        len += i;
+                        chunk -= i;
+                    }
+                    if (chunk_max < INT_MAX/2)
+                        chunk_max *= 2;
+                }
+            }
+            if (off + slen < off) {
+                ASN1err(ASN1_F_ASN1_D2I_READ_BIO, ASN1_R_TOO_LONG);
+                goto err;
+            }
+            off += slen;
+            if (eos == 0) {
+                break;
+            } else
+                want = HEADER_SIZE;
+        }
+    }
+
+    if (off > INT_MAX) {
+        ASN1err(ASN1_F_ASN1_D2I_READ_BIO, ASN1_R_TOO_LONG);
+        goto err;
+    }
+
+    *pb = b;
+    return off;
+ err:
+    BUF_MEM_free(b);
+    return -1;
+}
diff --git a/crypto/asn1/a_digest.c b/crypto/asn1/a_digest.c
new file mode 100644
index 000000000..2f9b63b2a
--- /dev/null
+++ b/crypto/asn1/a_digest.c
@@ -0,0 +1,62 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+
+#include "internal/cryptlib.h"
+
+#ifndef NO_SYS_TYPES_H
+# include 
+#endif
+
+#include 
+#include 
+#include 
+#include 
+
+#ifndef NO_ASN1_OLD
+
+int ASN1_digest(i2d_of_void *i2d, const EVP_MD *type, char *data,
+                unsigned char *md, unsigned int *len)
+{
+    int i;
+    unsigned char *str, *p;
+
+    i = i2d(data, NULL);
+    if ((str = OPENSSL_malloc(i)) == NULL) {
+        ASN1err(ASN1_F_ASN1_DIGEST, ERR_R_MALLOC_FAILURE);
+        return (0);
+    }
+    p = str;
+    i2d(data, &p);
+
+    if (!EVP_Digest(str, i, md, len, type, NULL))
+        return 0;
+    OPENSSL_free(str);
+    return (1);
+}
+
+#endif
+
+int ASN1_item_digest(const ASN1_ITEM *it, const EVP_MD *type, void *asn,
+                     unsigned char *md, unsigned int *len)
+{
+    int i;
+    unsigned char *str = NULL;
+
+    i = ASN1_item_i2d(asn, &str, it);
+    if (!str)
+        return (0);
+
+    if (!EVP_Digest(str, i, md, len, type, NULL))
+        return 0;
+    OPENSSL_free(str);
+    return (1);
+}
diff --git a/crypto/asn1/a_dup.c b/crypto/asn1/a_dup.c
new file mode 100644
index 000000000..d9a57b2c6
--- /dev/null
+++ b/crypto/asn1/a_dup.c
@@ -0,0 +1,68 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+
+#ifndef NO_OLD_ASN1
+
+void *ASN1_dup(i2d_of_void *i2d, d2i_of_void *d2i, void *x)
+{
+    unsigned char *b, *p;
+    const unsigned char *p2;
+    int i;
+    char *ret;
+
+    if (x == NULL)
+        return (NULL);
+
+    i = i2d(x, NULL);
+    b = OPENSSL_malloc(i + 10);
+    if (b == NULL) {
+        ASN1err(ASN1_F_ASN1_DUP, ERR_R_MALLOC_FAILURE);
+        return (NULL);
+    }
+    p = b;
+    i = i2d(x, &p);
+    p2 = b;
+    ret = d2i(NULL, &p2, i);
+    OPENSSL_free(b);
+    return (ret);
+}
+
+#endif
+
+/*
+ * ASN1_ITEM version of dup: this follows the model above except we don't
+ * need to allocate the buffer. At some point this could be rewritten to
+ * directly dup the underlying structure instead of doing and encode and
+ * decode.
+ */
+
+void *ASN1_item_dup(const ASN1_ITEM *it, void *x)
+{
+    unsigned char *b = NULL;
+    const unsigned char *p;
+    long i;
+    void *ret;
+
+    if (x == NULL)
+        return (NULL);
+
+    i = ASN1_item_i2d(x, &b, it);
+    if (b == NULL) {
+        ASN1err(ASN1_F_ASN1_ITEM_DUP, ERR_R_MALLOC_FAILURE);
+        return (NULL);
+    }
+    p = b;
+    ret = ASN1_item_d2i(NULL, &p, i, it);
+    OPENSSL_free(b);
+    return (ret);
+}
diff --git a/crypto/asn1/a_gentm.c b/crypto/asn1/a_gentm.c
new file mode 100644
index 000000000..c02c8d9ac
--- /dev/null
+++ b/crypto/asn1/a_gentm.c
@@ -0,0 +1,273 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * GENERALIZEDTIME implementation. Based on UTCTIME
+ */
+
+#include 
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include "asn1_locl.h"
+
+int asn1_generalizedtime_to_tm(struct tm *tm, const ASN1_GENERALIZEDTIME *d)
+{
+    static const int min[9] = { 0, 0, 1, 1, 0, 0, 0, 0, 0 };
+    static const int max[9] = { 99, 99, 12, 31, 23, 59, 59, 12, 59 };
+    char *a;
+    int n, i, l, o;
+
+    if (d->type != V_ASN1_GENERALIZEDTIME)
+        return (0);
+    l = d->length;
+    a = (char *)d->data;
+    o = 0;
+    /*
+     * GENERALIZEDTIME is similar to UTCTIME except the year is represented
+     * as YYYY. This stuff treats everything as a two digit field so make
+     * first two fields 00 to 99
+     */
+    if (l < 13)
+        goto err;
+    for (i = 0; i < 7; i++) {
+        if ((i == 6) && ((a[o] == 'Z') || (a[o] == '+') || (a[o] == '-'))) {
+            i++;
+            if (tm)
+                tm->tm_sec = 0;
+            break;
+        }
+        if ((a[o] < '0') || (a[o] > '9'))
+            goto err;
+        n = a[o] - '0';
+        if (++o > l)
+            goto err;
+
+        if ((a[o] < '0') || (a[o] > '9'))
+            goto err;
+        n = (n * 10) + a[o] - '0';
+        if (++o > l)
+            goto err;
+
+        if ((n < min[i]) || (n > max[i]))
+            goto err;
+        if (tm) {
+            switch (i) {
+            case 0:
+                tm->tm_year = n * 100 - 1900;
+                break;
+            case 1:
+                tm->tm_year += n;
+                break;
+            case 2:
+                tm->tm_mon = n - 1;
+                break;
+            case 3:
+                tm->tm_mday = n;
+                break;
+            case 4:
+                tm->tm_hour = n;
+                break;
+            case 5:
+                tm->tm_min = n;
+                break;
+            case 6:
+                tm->tm_sec = n;
+                break;
+            }
+        }
+    }
+    /*
+     * Optional fractional seconds: decimal point followed by one or more
+     * digits.
+     */
+    if (a[o] == '.') {
+        if (++o > l)
+            goto err;
+        i = o;
+        while ((a[o] >= '0') && (a[o] <= '9') && (o <= l))
+            o++;
+        /* Must have at least one digit after decimal point */
+        if (i == o)
+            goto err;
+    }
+
+    if (a[o] == 'Z')
+        o++;
+    else if ((a[o] == '+') || (a[o] == '-')) {
+        int offsign = a[o] == '-' ? -1 : 1, offset = 0;
+        o++;
+        if (o + 4 > l)
+            goto err;
+        for (i = 7; i < 9; i++) {
+            if ((a[o] < '0') || (a[o] > '9'))
+                goto err;
+            n = a[o] - '0';
+            o++;
+            if ((a[o] < '0') || (a[o] > '9'))
+                goto err;
+            n = (n * 10) + a[o] - '0';
+            if ((n < min[i]) || (n > max[i]))
+                goto err;
+            if (tm) {
+                if (i == 7)
+                    offset = n * 3600;
+                else if (i == 8)
+                    offset += n * 60;
+            }
+            o++;
+        }
+        if (offset && !OPENSSL_gmtime_adj(tm, 0, offset * offsign))
+            return 0;
+    } else if (a[o]) {
+        /* Missing time zone information. */
+        goto err;
+    }
+    return (o == l);
+ err:
+    return (0);
+}
+
+int ASN1_GENERALIZEDTIME_check(const ASN1_GENERALIZEDTIME *d)
+{
+    return asn1_generalizedtime_to_tm(NULL, d);
+}
+
+int ASN1_GENERALIZEDTIME_set_string(ASN1_GENERALIZEDTIME *s, const char *str)
+{
+    ASN1_GENERALIZEDTIME t;
+
+    t.type = V_ASN1_GENERALIZEDTIME;
+    t.length = strlen(str);
+    t.data = (unsigned char *)str;
+    if (ASN1_GENERALIZEDTIME_check(&t)) {
+        if (s != NULL) {
+            if (!ASN1_STRING_set((ASN1_STRING *)s, str, t.length))
+                return 0;
+            s->type = V_ASN1_GENERALIZEDTIME;
+        }
+        return (1);
+    } else
+        return (0);
+}
+
+ASN1_GENERALIZEDTIME *ASN1_GENERALIZEDTIME_set(ASN1_GENERALIZEDTIME *s,
+                                               time_t t)
+{
+    return ASN1_GENERALIZEDTIME_adj(s, t, 0, 0);
+}
+
+ASN1_GENERALIZEDTIME *ASN1_GENERALIZEDTIME_adj(ASN1_GENERALIZEDTIME *s,
+                                               time_t t, int offset_day,
+                                               long offset_sec)
+{
+    char *p;
+    struct tm *ts;
+    struct tm data;
+    size_t len = 20;
+    ASN1_GENERALIZEDTIME *tmps = NULL;
+
+    if (s == NULL)
+        tmps = ASN1_GENERALIZEDTIME_new();
+    else
+        tmps = s;
+    if (tmps == NULL)
+        return NULL;
+
+    ts = OPENSSL_gmtime(&t, &data);
+    if (ts == NULL)
+        goto err;
+
+    if (offset_day || offset_sec) {
+        if (!OPENSSL_gmtime_adj(ts, offset_day, offset_sec))
+            goto err;
+    }
+
+    p = (char *)tmps->data;
+    if ((p == NULL) || ((size_t)tmps->length < len)) {
+        p = OPENSSL_malloc(len);
+        if (p == NULL) {
+            ASN1err(ASN1_F_ASN1_GENERALIZEDTIME_ADJ, ERR_R_MALLOC_FAILURE);
+            goto err;
+        }
+        OPENSSL_free(tmps->data);
+        tmps->data = (unsigned char *)p;
+    }
+
+    BIO_snprintf(p, len, "%04d%02d%02d%02d%02d%02dZ", ts->tm_year + 1900,
+                 ts->tm_mon + 1, ts->tm_mday, ts->tm_hour, ts->tm_min,
+                 ts->tm_sec);
+    tmps->length = strlen(p);
+    tmps->type = V_ASN1_GENERALIZEDTIME;
+#ifdef CHARSET_EBCDIC_not
+    ebcdic2ascii(tmps->data, tmps->data, tmps->length);
+#endif
+    return tmps;
+ err:
+    if (s == NULL)
+        ASN1_GENERALIZEDTIME_free(tmps);
+    return NULL;
+}
+
+const char *_asn1_mon[12] = {
+    "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+    "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
+};
+
+int ASN1_GENERALIZEDTIME_print(BIO *bp, const ASN1_GENERALIZEDTIME *tm)
+{
+    char *v;
+    int gmt = 0;
+    int i;
+    int y = 0, M = 0, d = 0, h = 0, m = 0, s = 0;
+    char *f = NULL;
+    int f_len = 0;
+
+    i = tm->length;
+    v = (char *)tm->data;
+
+    if (i < 12)
+        goto err;
+    if (v[i - 1] == 'Z')
+        gmt = 1;
+    for (i = 0; i < 12; i++)
+        if ((v[i] > '9') || (v[i] < '0'))
+            goto err;
+    y = (v[0] - '0') * 1000 + (v[1] - '0') * 100
+        + (v[2] - '0') * 10 + (v[3] - '0');
+    M = (v[4] - '0') * 10 + (v[5] - '0');
+    if ((M > 12) || (M < 1))
+        goto err;
+    d = (v[6] - '0') * 10 + (v[7] - '0');
+    h = (v[8] - '0') * 10 + (v[9] - '0');
+    m = (v[10] - '0') * 10 + (v[11] - '0');
+    if (tm->length >= 14 &&
+        (v[12] >= '0') && (v[12] <= '9') &&
+        (v[13] >= '0') && (v[13] <= '9')) {
+        s = (v[12] - '0') * 10 + (v[13] - '0');
+        /* Check for fractions of seconds. */
+        if (tm->length >= 15 && v[14] == '.') {
+            int l = tm->length;
+            f = &v[14];         /* The decimal point. */
+            f_len = 1;
+            while (14 + f_len < l && f[f_len] >= '0' && f[f_len] <= '9')
+                ++f_len;
+        }
+    }
+
+    if (BIO_printf(bp, "%s %2d %02d:%02d:%02d%.*s %d%s",
+                   _asn1_mon[M - 1], d, h, m, s, f_len, f, y,
+                   (gmt) ? " GMT" : "") <= 0)
+        return (0);
+    else
+        return (1);
+ err:
+    BIO_write(bp, "Bad time value", 14);
+    return (0);
+}
diff --git a/crypto/asn1/a_i2d_fp.c b/crypto/asn1/a_i2d_fp.c
new file mode 100644
index 000000000..1514ede4f
--- /dev/null
+++ b/crypto/asn1/a_i2d_fp.c
@@ -0,0 +1,108 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+
+#ifndef NO_OLD_ASN1
+
+# ifndef OPENSSL_NO_STDIO
+int ASN1_i2d_fp(i2d_of_void *i2d, FILE *out, void *x)
+{
+    BIO *b;
+    int ret;
+
+    if ((b = BIO_new(BIO_s_file())) == NULL) {
+        ASN1err(ASN1_F_ASN1_I2D_FP, ERR_R_BUF_LIB);
+        return (0);
+    }
+    BIO_set_fp(b, out, BIO_NOCLOSE);
+    ret = ASN1_i2d_bio(i2d, b, x);
+    BIO_free(b);
+    return (ret);
+}
+# endif
+
+int ASN1_i2d_bio(i2d_of_void *i2d, BIO *out, unsigned char *x)
+{
+    char *b;
+    unsigned char *p;
+    int i, j = 0, n, ret = 1;
+
+    n = i2d(x, NULL);
+    b = OPENSSL_malloc(n);
+    if (b == NULL) {
+        ASN1err(ASN1_F_ASN1_I2D_BIO, ERR_R_MALLOC_FAILURE);
+        return (0);
+    }
+
+    p = (unsigned char *)b;
+    i2d(x, &p);
+
+    for (;;) {
+        i = BIO_write(out, &(b[j]), n);
+        if (i == n)
+            break;
+        if (i <= 0) {
+            ret = 0;
+            break;
+        }
+        j += i;
+        n -= i;
+    }
+    OPENSSL_free(b);
+    return (ret);
+}
+
+#endif
+
+#ifndef OPENSSL_NO_STDIO
+int ASN1_item_i2d_fp(const ASN1_ITEM *it, FILE *out, void *x)
+{
+    BIO *b;
+    int ret;
+
+    if ((b = BIO_new(BIO_s_file())) == NULL) {
+        ASN1err(ASN1_F_ASN1_ITEM_I2D_FP, ERR_R_BUF_LIB);
+        return (0);
+    }
+    BIO_set_fp(b, out, BIO_NOCLOSE);
+    ret = ASN1_item_i2d_bio(it, b, x);
+    BIO_free(b);
+    return (ret);
+}
+#endif
+
+int ASN1_item_i2d_bio(const ASN1_ITEM *it, BIO *out, void *x)
+{
+    unsigned char *b = NULL;
+    int i, j = 0, n, ret = 1;
+
+    n = ASN1_item_i2d(x, &b, it);
+    if (b == NULL) {
+        ASN1err(ASN1_F_ASN1_ITEM_I2D_BIO, ERR_R_MALLOC_FAILURE);
+        return (0);
+    }
+
+    for (;;) {
+        i = BIO_write(out, &(b[j]), n);
+        if (i == n)
+            break;
+        if (i <= 0) {
+            ret = 0;
+            break;
+        }
+        j += i;
+        n -= i;
+    }
+    OPENSSL_free(b);
+    return (ret);
+}
diff --git a/crypto/asn1/a_int.c b/crypto/asn1/a_int.c
new file mode 100644
index 000000000..e0bcd6e50
--- /dev/null
+++ b/crypto/asn1/a_int.c
@@ -0,0 +1,624 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include "internal/numbers.h"
+#include 
+#include 
+#include 
+#include "asn1_locl.h"
+
+ASN1_INTEGER *ASN1_INTEGER_dup(const ASN1_INTEGER *x)
+{
+    return ASN1_STRING_dup(x);
+}
+
+int ASN1_INTEGER_cmp(const ASN1_INTEGER *x, const ASN1_INTEGER *y)
+{
+    int neg, ret;
+    /* Compare signs */
+    neg = x->type & V_ASN1_NEG;
+    if (neg != (y->type & V_ASN1_NEG)) {
+        if (neg)
+            return -1;
+        else
+            return 1;
+    }
+
+    ret = ASN1_STRING_cmp(x, y);
+
+    if (neg)
+        return -ret;
+    else
+        return ret;
+}
+
+/*-
+ * This converts a big endian buffer and sign into its content encoding.
+ * This is used for INTEGER and ENUMERATED types.
+ * The internal representation is an ASN1_STRING whose data is a big endian
+ * representation of the value, ignoring the sign. The sign is determined by
+ * the type: if type & V_ASN1_NEG is true it is negative, otherwise positive.
+ *
+ * Positive integers are no problem: they are almost the same as the DER
+ * encoding, except if the first byte is >= 0x80 we need to add a zero pad.
+ *
+ * Negative integers are a bit trickier...
+ * The DER representation of negative integers is in 2s complement form.
+ * The internal form is converted by complementing each octet and finally
+ * adding one to the result. This can be done less messily with a little trick.
+ * If the internal form has trailing zeroes then they will become FF by the
+ * complement and 0 by the add one (due to carry) so just copy as many trailing
+ * zeros to the destination as there are in the source. The carry will add one
+ * to the last none zero octet: so complement this octet and add one and finally
+ * complement any left over until you get to the start of the string.
+ *
+ * Padding is a little trickier too. If the first bytes is > 0x80 then we pad
+ * with 0xff. However if the first byte is 0x80 and one of the following bytes
+ * is non-zero we pad with 0xff. The reason for this distinction is that 0x80
+ * followed by optional zeros isn't padded.
+ */
+
+static size_t i2c_ibuf(const unsigned char *b, size_t blen, int neg,
+                       unsigned char **pp)
+{
+    int pad = 0;
+    size_t ret, i;
+    unsigned char *p, pb = 0;
+    const unsigned char *n;
+
+    if (b == NULL || blen == 0)
+        ret = 1;
+    else {
+        ret = blen;
+        i = b[0];
+        if (ret == 1 && i == 0)
+            neg = 0;
+        if (!neg && (i > 127)) {
+            pad = 1;
+            pb = 0;
+        } else if (neg) {
+            if (i > 128) {
+                pad = 1;
+                pb = 0xFF;
+            } else if (i == 128) {
+                /*
+                 * Special case: if any other bytes non zero we pad:
+                 * otherwise we don't.
+                 */
+                for (i = 1; i < blen; i++)
+                    if (b[i]) {
+                        pad = 1;
+                        pb = 0xFF;
+                        break;
+                    }
+            }
+        }
+        ret += pad;
+    }
+    if (pp == NULL)
+        return ret;
+    p = *pp;
+
+    if (pad)
+        *(p++) = pb;
+    if (b == NULL || blen == 0)
+        *p = 0;
+    else if (!neg)
+        memcpy(p, b, blen);
+    else {
+        /* Begin at the end of the encoding */
+        n = b + blen;
+        p += blen;
+        i = blen;
+        /* Copy zeros to destination as long as source is zero */
+        while (!n[-1] && i > 1) {
+            *(--p) = 0;
+            n--;
+            i--;
+        }
+        /* Complement and increment next octet */
+        *(--p) = ((*(--n)) ^ 0xff) + 1;
+        i--;
+        /* Complement any octets left */
+        for (; i > 0; i--)
+            *(--p) = *(--n) ^ 0xff;
+    }
+
+    *pp += ret;
+    return ret;
+}
+
+/*
+ * convert content octets into a big endian buffer. Returns the length
+ * of buffer or 0 on error: for malformed INTEGER. If output buffer is
+ * NULL just return length.
+ */
+
+static size_t c2i_ibuf(unsigned char *b, int *pneg,
+                       const unsigned char *p, size_t plen)
+{
+    size_t i;
+    int neg, pad;
+    /* Zero content length is illegal */
+    if (plen == 0) {
+        ASN1err(ASN1_F_C2I_IBUF, ASN1_R_ILLEGAL_ZERO_CONTENT);
+        return 0;
+    }
+    neg = p[0] & 0x80;
+    if (pneg)
+        *pneg = neg;
+    /* Handle common case where length is 1 octet separately */
+    if (plen == 1) {
+        if (b) {
+            if (neg)
+                b[0] = (p[0] ^ 0xFF) + 1;
+            else
+                b[0] = p[0];
+        }
+        return 1;
+    }
+    if (p[0] == 0 || p[0] == 0xFF)
+        pad = 1;
+    else
+        pad = 0;
+    /* reject illegal padding: first two octets MSB can't match */
+    if (pad && (neg == (p[1] & 0x80))) {
+        ASN1err(ASN1_F_C2I_IBUF, ASN1_R_ILLEGAL_PADDING);
+        return 0;
+    }
+    /* If positive just copy across */
+    if (neg == 0) {
+        if (b)
+            memcpy(b, p + pad, plen - pad);
+        return plen - pad;
+    }
+
+    if (neg && pad) {
+        /* check is any following octets are non zero */
+        for (i = 1; i < plen; i++) {
+            if (p[i] != 0)
+                break;
+        }
+        /* if all bytes are zero handle as special case */
+        if (i == plen) {
+            if (b) {
+                b[0] = 1;
+                memset(b + 1, 0, plen - 1);
+            }
+            return plen;
+        }
+    }
+
+    plen -= pad;
+    /* Must be negative: calculate twos complement */
+    if (b) {
+        const unsigned char *from = p + plen - 1 + pad;
+        unsigned char *to = b + plen;
+        i = plen;
+        while (*from == 0 && i) {
+            *--to = 0;
+            i--;
+            from--;
+        }
+        *--to = (*from-- ^ 0xff) + 1;
+        OPENSSL_assert(i != 0);
+        i--;
+        for (; i > 0; i--)
+            *--to = *from-- ^ 0xff;
+    }
+    return plen;
+}
+
+int i2c_ASN1_INTEGER(ASN1_INTEGER *a, unsigned char **pp)
+{
+    return i2c_ibuf(a->data, a->length, a->type & V_ASN1_NEG, pp);
+}
+
+/* Convert big endian buffer into uint64_t, return 0 on error */
+static int asn1_get_uint64(uint64_t *pr, const unsigned char *b, size_t blen)
+{
+    size_t i;
+    if (blen > sizeof(*pr)) {
+        ASN1err(ASN1_F_ASN1_GET_UINT64, ASN1_R_TOO_LARGE);
+        return 0;
+    }
+    *pr = 0;
+    if (b == NULL)
+        return 0;
+    for (i = 0; i < blen; i++) {
+        *pr <<= 8;
+        *pr |= b[i];
+    }
+    return 1;
+}
+
+static size_t asn1_put_uint64(unsigned char *b, uint64_t r)
+{
+    if (r >= 0x100) {
+        unsigned char *p;
+        uint64_t rtmp = r;
+        size_t i = 0;
+
+        /* Work out how many bytes we need */
+        while (rtmp) {
+            rtmp >>= 8;
+            i++;
+        }
+
+        /* Copy from end to beginning */
+        p = b + i - 1;
+
+        do {
+            *p-- = r & 0xFF;
+            r >>= 8;
+        } while (p >= b);
+
+        return i;
+    }
+
+    b[0] = (unsigned char)r;
+    return 1;
+
+}
+
+/*
+ * Absolute value of INT64_MIN: we can't just use -INT64_MIN as it produces
+ * overflow warnings.
+ */
+
+#define ABS_INT64_MIN \
+    ((uint64_t)INT64_MAX + (uint64_t)(-(INT64_MIN + INT64_MAX)))
+
+/* signed version of asn1_get_uint64 */
+static int asn1_get_int64(int64_t *pr, const unsigned char *b, size_t blen,
+                          int neg)
+{
+    uint64_t r;
+    if (asn1_get_uint64(&r, b, blen) == 0)
+        return 0;
+    if (neg) {
+        if (r > ABS_INT64_MIN) {
+            ASN1err(ASN1_F_ASN1_GET_INT64, ASN1_R_TOO_SMALL);
+            return 0;
+        }
+        *pr = 0 - (uint64_t)r;
+    } else {
+        if (r > INT64_MAX) {
+            ASN1err(ASN1_F_ASN1_GET_INT64, ASN1_R_TOO_LARGE);
+            return 0;
+        }
+        *pr = (int64_t)r;
+    }
+    return 1;
+}
+
+/* Convert ASN1 INTEGER content octets to ASN1_INTEGER structure */
+ASN1_INTEGER *c2i_ASN1_INTEGER(ASN1_INTEGER **a, const unsigned char **pp,
+                               long len)
+{
+    ASN1_INTEGER *ret = NULL;
+    size_t r;
+    int neg;
+
+    r = c2i_ibuf(NULL, NULL, *pp, len);
+
+    if (r == 0)
+        return NULL;
+
+    if ((a == NULL) || ((*a) == NULL)) {
+        ret = ASN1_INTEGER_new();
+        if (ret == NULL)
+            return NULL;
+        ret->type = V_ASN1_INTEGER;
+    } else
+        ret = *a;
+
+    if (ASN1_STRING_set(ret, NULL, r) == 0)
+        goto err;
+
+    c2i_ibuf(ret->data, &neg, *pp, len);
+
+    if (neg)
+        ret->type |= V_ASN1_NEG;
+
+    *pp += len;
+    if (a != NULL)
+        (*a) = ret;
+    return ret;
+ err:
+    ASN1err(ASN1_F_C2I_ASN1_INTEGER, ERR_R_MALLOC_FAILURE);
+    if ((a == NULL) || (*a != ret))
+        ASN1_INTEGER_free(ret);
+    return NULL;
+}
+
+static int asn1_string_get_int64(int64_t *pr, const ASN1_STRING *a, int itype)
+{
+    if (a == NULL) {
+        ASN1err(ASN1_F_ASN1_STRING_GET_INT64, ERR_R_PASSED_NULL_PARAMETER);
+        return 0;
+    }
+    if ((a->type & ~V_ASN1_NEG) != itype) {
+        ASN1err(ASN1_F_ASN1_STRING_GET_INT64, ASN1_R_WRONG_INTEGER_TYPE);
+        return 0;
+    }
+    return asn1_get_int64(pr, a->data, a->length, a->type & V_ASN1_NEG);
+}
+
+static int asn1_string_set_int64(ASN1_STRING *a, int64_t r, int itype)
+{
+    unsigned char tbuf[sizeof(r)];
+    size_t l;
+    a->type = itype;
+    if (r < 0) {
+        l = asn1_put_uint64(tbuf, -r);
+        a->type |= V_ASN1_NEG;
+    } else {
+        l = asn1_put_uint64(tbuf, r);
+        a->type &= ~V_ASN1_NEG;
+    }
+    if (l == 0)
+        return 0;
+    return ASN1_STRING_set(a, tbuf, l);
+}
+
+static int asn1_string_get_uint64(uint64_t *pr, const ASN1_STRING *a,
+                                  int itype)
+{
+    if (a == NULL) {
+        ASN1err(ASN1_F_ASN1_STRING_GET_UINT64, ERR_R_PASSED_NULL_PARAMETER);
+        return 0;
+    }
+    if ((a->type & ~V_ASN1_NEG) != itype) {
+        ASN1err(ASN1_F_ASN1_STRING_GET_UINT64, ASN1_R_WRONG_INTEGER_TYPE);
+        return 0;
+    }
+    if (a->type & V_ASN1_NEG) {
+        ASN1err(ASN1_F_ASN1_STRING_GET_UINT64, ASN1_R_ILLEGAL_NEGATIVE_VALUE);
+        return 0;
+    }
+    return asn1_get_uint64(pr, a->data, a->length);
+}
+
+static int asn1_string_set_uint64(ASN1_STRING *a, uint64_t r, int itype)
+{
+    unsigned char tbuf[sizeof(r)];
+    size_t l;
+    a->type = itype;
+    l = asn1_put_uint64(tbuf, r);
+    if (l == 0)
+        return 0;
+    return ASN1_STRING_set(a, tbuf, l);
+}
+
+/*
+ * This is a version of d2i_ASN1_INTEGER that ignores the sign bit of ASN1
+ * integers: some broken software can encode a positive INTEGER with its MSB
+ * set as negative (it doesn't add a padding zero).
+ */
+
+ASN1_INTEGER *d2i_ASN1_UINTEGER(ASN1_INTEGER **a, const unsigned char **pp,
+                                long length)
+{
+    ASN1_INTEGER *ret = NULL;
+    const unsigned char *p;
+    unsigned char *s;
+    long len;
+    int inf, tag, xclass;
+    int i;
+
+    if ((a == NULL) || ((*a) == NULL)) {
+        if ((ret = ASN1_INTEGER_new()) == NULL)
+            return (NULL);
+        ret->type = V_ASN1_INTEGER;
+    } else
+        ret = (*a);
+
+    p = *pp;
+    inf = ASN1_get_object(&p, &len, &tag, &xclass, length);
+    if (inf & 0x80) {
+        i = ASN1_R_BAD_OBJECT_HEADER;
+        goto err;
+    }
+
+    if (tag != V_ASN1_INTEGER) {
+        i = ASN1_R_EXPECTING_AN_INTEGER;
+        goto err;
+    }
+
+    /*
+     * We must OPENSSL_malloc stuff, even for 0 bytes otherwise it signifies
+     * a missing NULL parameter.
+     */
+    s = OPENSSL_malloc((int)len + 1);
+    if (s == NULL) {
+        i = ERR_R_MALLOC_FAILURE;
+        goto err;
+    }
+    ret->type = V_ASN1_INTEGER;
+    if (len) {
+        if ((*p == 0) && (len != 1)) {
+            p++;
+            len--;
+        }
+        memcpy(s, p, (int)len);
+        p += len;
+    }
+
+    OPENSSL_free(ret->data);
+    ret->data = s;
+    ret->length = (int)len;
+    if (a != NULL)
+        (*a) = ret;
+    *pp = p;
+    return (ret);
+ err:
+    ASN1err(ASN1_F_D2I_ASN1_UINTEGER, i);
+    if ((a == NULL) || (*a != ret))
+        ASN1_INTEGER_free(ret);
+    return (NULL);
+}
+
+static ASN1_STRING *bn_to_asn1_string(const BIGNUM *bn, ASN1_STRING *ai,
+                                      int atype)
+{
+    ASN1_INTEGER *ret;
+    int len;
+
+    if (ai == NULL) {
+        ret = ASN1_STRING_type_new(atype);
+    } else {
+        ret = ai;
+        ret->type = atype;
+    }
+
+    if (ret == NULL) {
+        ASN1err(ASN1_F_BN_TO_ASN1_STRING, ERR_R_NESTED_ASN1_ERROR);
+        goto err;
+    }
+
+    if (BN_is_negative(bn) && !BN_is_zero(bn))
+        ret->type |= V_ASN1_NEG_INTEGER;
+
+    len = BN_num_bytes(bn);
+
+    if (len == 0)
+        len = 1;
+
+    if (ASN1_STRING_set(ret, NULL, len) == 0) {
+        ASN1err(ASN1_F_BN_TO_ASN1_STRING, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+
+    /* Correct zero case */
+    if (BN_is_zero(bn))
+        ret->data[0] = 0;
+    else
+        len = BN_bn2bin(bn, ret->data);
+    ret->length = len;
+    return ret;
+ err:
+    if (ret != ai)
+        ASN1_INTEGER_free(ret);
+    return (NULL);
+}
+
+static BIGNUM *asn1_string_to_bn(const ASN1_INTEGER *ai, BIGNUM *bn,
+                                 int itype)
+{
+    BIGNUM *ret;
+
+    if ((ai->type & ~V_ASN1_NEG) != itype) {
+        ASN1err(ASN1_F_ASN1_STRING_TO_BN, ASN1_R_WRONG_INTEGER_TYPE);
+        return NULL;
+    }
+
+    ret = BN_bin2bn(ai->data, ai->length, bn);
+    if (ret == 0) {
+        ASN1err(ASN1_F_ASN1_STRING_TO_BN, ASN1_R_BN_LIB);
+        return NULL;
+    }
+    if (ai->type & V_ASN1_NEG)
+        BN_set_negative(ret, 1);
+    return ret;
+}
+
+int ASN1_INTEGER_get_int64(int64_t *pr, const ASN1_INTEGER *a)
+{
+    return asn1_string_get_int64(pr, a, V_ASN1_INTEGER);
+}
+
+int ASN1_INTEGER_set_int64(ASN1_INTEGER *a, int64_t r)
+{
+    return asn1_string_set_int64(a, r, V_ASN1_INTEGER);
+}
+
+int ASN1_INTEGER_get_uint64(uint64_t *pr, const ASN1_INTEGER *a)
+{
+    return asn1_string_get_uint64(pr, a, V_ASN1_INTEGER);
+}
+
+int ASN1_INTEGER_set_uint64(ASN1_INTEGER *a, uint64_t r)
+{
+    return asn1_string_set_uint64(a, r, V_ASN1_INTEGER);
+}
+
+int ASN1_INTEGER_set(ASN1_INTEGER *a, long v)
+{
+    return ASN1_INTEGER_set_int64(a, v);
+}
+
+long ASN1_INTEGER_get(const ASN1_INTEGER *a)
+{
+    int i;
+    int64_t r;
+    if (a == NULL)
+        return 0;
+    i = ASN1_INTEGER_get_int64(&r, a);
+    if (i == 0)
+        return -1;
+    if (r > LONG_MAX || r < LONG_MIN)
+        return -1;
+    return (long)r;
+}
+
+ASN1_INTEGER *BN_to_ASN1_INTEGER(const BIGNUM *bn, ASN1_INTEGER *ai)
+{
+    return bn_to_asn1_string(bn, ai, V_ASN1_INTEGER);
+}
+
+BIGNUM *ASN1_INTEGER_to_BN(const ASN1_INTEGER *ai, BIGNUM *bn)
+{
+    return asn1_string_to_bn(ai, bn, V_ASN1_INTEGER);
+}
+
+int ASN1_ENUMERATED_get_int64(int64_t *pr, const ASN1_ENUMERATED *a)
+{
+    return asn1_string_get_int64(pr, a, V_ASN1_ENUMERATED);
+}
+
+int ASN1_ENUMERATED_set_int64(ASN1_ENUMERATED *a, int64_t r)
+{
+    return asn1_string_set_int64(a, r, V_ASN1_ENUMERATED);
+}
+
+int ASN1_ENUMERATED_set(ASN1_ENUMERATED *a, long v)
+{
+    return ASN1_ENUMERATED_set_int64(a, v);
+}
+
+long ASN1_ENUMERATED_get(const ASN1_ENUMERATED *a)
+{
+    int i;
+    int64_t r;
+    if (a == NULL)
+        return 0;
+    if ((a->type & ~V_ASN1_NEG) != V_ASN1_ENUMERATED)
+        return -1;
+    if (a->length > (int)sizeof(long))
+        return 0xffffffffL;
+    i = ASN1_ENUMERATED_get_int64(&r, a);
+    if (i == 0)
+        return -1;
+    if (r > LONG_MAX || r < LONG_MIN)
+        return -1;
+    return (long)r;
+}
+
+ASN1_ENUMERATED *BN_to_ASN1_ENUMERATED(const BIGNUM *bn, ASN1_ENUMERATED *ai)
+{
+    return bn_to_asn1_string(bn, ai, V_ASN1_ENUMERATED);
+}
+
+BIGNUM *ASN1_ENUMERATED_to_BN(const ASN1_ENUMERATED *ai, BIGNUM *bn)
+{
+    return asn1_string_to_bn(ai, bn, V_ASN1_ENUMERATED);
+}
diff --git a/crypto/asn1/a_mbstr.c b/crypto/asn1/a_mbstr.c
new file mode 100644
index 000000000..5578e9239
--- /dev/null
+++ b/crypto/asn1/a_mbstr.c
@@ -0,0 +1,395 @@
+/*
+ * Copyright 1999-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include "internal/cryptlib.h"
+#include 
+
+static int traverse_string(const unsigned char *p, int len, int inform,
+                           int (*rfunc) (unsigned long value, void *in),
+                           void *arg);
+static int in_utf8(unsigned long value, void *arg);
+static int out_utf8(unsigned long value, void *arg);
+static int type_str(unsigned long value, void *arg);
+static int cpy_asc(unsigned long value, void *arg);
+static int cpy_bmp(unsigned long value, void *arg);
+static int cpy_univ(unsigned long value, void *arg);
+static int cpy_utf8(unsigned long value, void *arg);
+static int is_numeric(unsigned long value);
+static int is_printable(unsigned long value);
+
+/*
+ * These functions take a string in UTF8, ASCII or multibyte form and a mask
+ * of permissible ASN1 string types. It then works out the minimal type
+ * (using the order Numeric < Printable < IA5 < T61 < BMP < Universal < UTF8)
+ * and creates a string of the correct type with the supplied data. Yes this is
+ * horrible: it has to be :-( The 'ncopy' form checks minimum and maximum
+ * size limits too.
+ */
+
+int ASN1_mbstring_copy(ASN1_STRING **out, const unsigned char *in, int len,
+                       int inform, unsigned long mask)
+{
+    return ASN1_mbstring_ncopy(out, in, len, inform, mask, 0, 0);
+}
+
+int ASN1_mbstring_ncopy(ASN1_STRING **out, const unsigned char *in, int len,
+                        int inform, unsigned long mask,
+                        long minsize, long maxsize)
+{
+    int str_type;
+    int ret;
+    char free_out;
+    int outform, outlen = 0;
+    ASN1_STRING *dest;
+    unsigned char *p;
+    int nchar;
+    char strbuf[32];
+    int (*cpyfunc) (unsigned long, void *) = NULL;
+    if (len == -1)
+        len = strlen((const char *)in);
+    if (!mask)
+        mask = DIRSTRING_TYPE;
+
+    /* First do a string check and work out the number of characters */
+    switch (inform) {
+
+    case MBSTRING_BMP:
+        if (len & 1) {
+            ASN1err(ASN1_F_ASN1_MBSTRING_NCOPY,
+                    ASN1_R_INVALID_BMPSTRING_LENGTH);
+            return -1;
+        }
+        nchar = len >> 1;
+        break;
+
+    case MBSTRING_UNIV:
+        if (len & 3) {
+            ASN1err(ASN1_F_ASN1_MBSTRING_NCOPY,
+                    ASN1_R_INVALID_UNIVERSALSTRING_LENGTH);
+            return -1;
+        }
+        nchar = len >> 2;
+        break;
+
+    case MBSTRING_UTF8:
+        nchar = 0;
+        /* This counts the characters and does utf8 syntax checking */
+        ret = traverse_string(in, len, MBSTRING_UTF8, in_utf8, &nchar);
+        if (ret < 0) {
+            ASN1err(ASN1_F_ASN1_MBSTRING_NCOPY, ASN1_R_INVALID_UTF8STRING);
+            return -1;
+        }
+        break;
+
+    case MBSTRING_ASC:
+        nchar = len;
+        break;
+
+    default:
+        ASN1err(ASN1_F_ASN1_MBSTRING_NCOPY, ASN1_R_UNKNOWN_FORMAT);
+        return -1;
+    }
+
+    if ((minsize > 0) && (nchar < minsize)) {
+        ASN1err(ASN1_F_ASN1_MBSTRING_NCOPY, ASN1_R_STRING_TOO_SHORT);
+        BIO_snprintf(strbuf, sizeof strbuf, "%ld", minsize);
+        ERR_add_error_data(2, "minsize=", strbuf);
+        return -1;
+    }
+
+    if ((maxsize > 0) && (nchar > maxsize)) {
+        ASN1err(ASN1_F_ASN1_MBSTRING_NCOPY, ASN1_R_STRING_TOO_LONG);
+        BIO_snprintf(strbuf, sizeof strbuf, "%ld", maxsize);
+        ERR_add_error_data(2, "maxsize=", strbuf);
+        return -1;
+    }
+
+    /* Now work out minimal type (if any) */
+    if (traverse_string(in, len, inform, type_str, &mask) < 0) {
+        ASN1err(ASN1_F_ASN1_MBSTRING_NCOPY, ASN1_R_ILLEGAL_CHARACTERS);
+        return -1;
+    }
+
+    /* Now work out output format and string type */
+    outform = MBSTRING_ASC;
+    if (mask & B_ASN1_NUMERICSTRING)
+        str_type = V_ASN1_NUMERICSTRING;
+    else if (mask & B_ASN1_PRINTABLESTRING)
+        str_type = V_ASN1_PRINTABLESTRING;
+    else if (mask & B_ASN1_IA5STRING)
+        str_type = V_ASN1_IA5STRING;
+    else if (mask & B_ASN1_T61STRING)
+        str_type = V_ASN1_T61STRING;
+    else if (mask & B_ASN1_BMPSTRING) {
+        str_type = V_ASN1_BMPSTRING;
+        outform = MBSTRING_BMP;
+    } else if (mask & B_ASN1_UNIVERSALSTRING) {
+        str_type = V_ASN1_UNIVERSALSTRING;
+        outform = MBSTRING_UNIV;
+    } else {
+        str_type = V_ASN1_UTF8STRING;
+        outform = MBSTRING_UTF8;
+    }
+    if (!out)
+        return str_type;
+    if (*out) {
+        free_out = 0;
+        dest = *out;
+        OPENSSL_free(dest->data);
+        dest->data = NULL;
+        dest->length = 0;
+        dest->type = str_type;
+    } else {
+        free_out = 1;
+        dest = ASN1_STRING_type_new(str_type);
+        if (dest == NULL) {
+            ASN1err(ASN1_F_ASN1_MBSTRING_NCOPY, ERR_R_MALLOC_FAILURE);
+            return -1;
+        }
+        *out = dest;
+    }
+    /* If both the same type just copy across */
+    if (inform == outform) {
+        if (!ASN1_STRING_set(dest, in, len)) {
+            ASN1err(ASN1_F_ASN1_MBSTRING_NCOPY, ERR_R_MALLOC_FAILURE);
+            return -1;
+        }
+        return str_type;
+    }
+
+    /* Work out how much space the destination will need */
+    switch (outform) {
+    case MBSTRING_ASC:
+        outlen = nchar;
+        cpyfunc = cpy_asc;
+        break;
+
+    case MBSTRING_BMP:
+        outlen = nchar << 1;
+        cpyfunc = cpy_bmp;
+        break;
+
+    case MBSTRING_UNIV:
+        outlen = nchar << 2;
+        cpyfunc = cpy_univ;
+        break;
+
+    case MBSTRING_UTF8:
+        outlen = 0;
+        traverse_string(in, len, inform, out_utf8, &outlen);
+        cpyfunc = cpy_utf8;
+        break;
+    }
+    if ((p = OPENSSL_malloc(outlen + 1)) == NULL) {
+        if (free_out)
+            ASN1_STRING_free(dest);
+        ASN1err(ASN1_F_ASN1_MBSTRING_NCOPY, ERR_R_MALLOC_FAILURE);
+        return -1;
+    }
+    dest->length = outlen;
+    dest->data = p;
+    p[outlen] = 0;
+    traverse_string(in, len, inform, cpyfunc, &p);
+    return str_type;
+}
+
+/*
+ * This function traverses a string and passes the value of each character to
+ * an optional function along with a void * argument.
+ */
+
+static int traverse_string(const unsigned char *p, int len, int inform,
+                           int (*rfunc) (unsigned long value, void *in),
+                           void *arg)
+{
+    unsigned long value;
+    int ret;
+    while (len) {
+        if (inform == MBSTRING_ASC) {
+            value = *p++;
+            len--;
+        } else if (inform == MBSTRING_BMP) {
+            value = *p++ << 8;
+            value |= *p++;
+            len -= 2;
+        } else if (inform == MBSTRING_UNIV) {
+            value = ((unsigned long)*p++) << 24;
+            value |= ((unsigned long)*p++) << 16;
+            value |= *p++ << 8;
+            value |= *p++;
+            len -= 4;
+        } else {
+            ret = UTF8_getc(p, len, &value);
+            if (ret < 0)
+                return -1;
+            len -= ret;
+            p += ret;
+        }
+        if (rfunc) {
+            ret = rfunc(value, arg);
+            if (ret <= 0)
+                return ret;
+        }
+    }
+    return 1;
+}
+
+/* Various utility functions for traverse_string */
+
+/* Just count number of characters */
+
+static int in_utf8(unsigned long value, void *arg)
+{
+    int *nchar;
+    nchar = arg;
+    (*nchar)++;
+    return 1;
+}
+
+/* Determine size of output as a UTF8 String */
+
+static int out_utf8(unsigned long value, void *arg)
+{
+    int *outlen;
+    outlen = arg;
+    *outlen += UTF8_putc(NULL, -1, value);
+    return 1;
+}
+
+/*
+ * Determine the "type" of a string: check each character against a supplied
+ * "mask".
+ */
+
+static int type_str(unsigned long value, void *arg)
+{
+    unsigned long types;
+    types = *((unsigned long *)arg);
+    if ((types & B_ASN1_NUMERICSTRING) && !is_numeric(value))
+        types &= ~B_ASN1_NUMERICSTRING;
+    if ((types & B_ASN1_PRINTABLESTRING) && !is_printable(value))
+        types &= ~B_ASN1_PRINTABLESTRING;
+    if ((types & B_ASN1_IA5STRING) && (value > 127))
+        types &= ~B_ASN1_IA5STRING;
+    if ((types & B_ASN1_T61STRING) && (value > 0xff))
+        types &= ~B_ASN1_T61STRING;
+    if ((types & B_ASN1_BMPSTRING) && (value > 0xffff))
+        types &= ~B_ASN1_BMPSTRING;
+    if (!types)
+        return -1;
+    *((unsigned long *)arg) = types;
+    return 1;
+}
+
+/* Copy one byte per character ASCII like strings */
+
+static int cpy_asc(unsigned long value, void *arg)
+{
+    unsigned char **p, *q;
+    p = arg;
+    q = *p;
+    *q = (unsigned char)value;
+    (*p)++;
+    return 1;
+}
+
+/* Copy two byte per character BMPStrings */
+
+static int cpy_bmp(unsigned long value, void *arg)
+{
+    unsigned char **p, *q;
+    p = arg;
+    q = *p;
+    *q++ = (unsigned char)((value >> 8) & 0xff);
+    *q = (unsigned char)(value & 0xff);
+    *p += 2;
+    return 1;
+}
+
+/* Copy four byte per character UniversalStrings */
+
+static int cpy_univ(unsigned long value, void *arg)
+{
+    unsigned char **p, *q;
+    p = arg;
+    q = *p;
+    *q++ = (unsigned char)((value >> 24) & 0xff);
+    *q++ = (unsigned char)((value >> 16) & 0xff);
+    *q++ = (unsigned char)((value >> 8) & 0xff);
+    *q = (unsigned char)(value & 0xff);
+    *p += 4;
+    return 1;
+}
+
+/* Copy to a UTF8String */
+
+static int cpy_utf8(unsigned long value, void *arg)
+{
+    unsigned char **p;
+    int ret;
+    p = arg;
+    /* We already know there is enough room so pass 0xff as the length */
+    ret = UTF8_putc(*p, 0xff, value);
+    *p += ret;
+    return 1;
+}
+
+/* Return 1 if the character is permitted in a PrintableString */
+static int is_printable(unsigned long value)
+{
+    int ch;
+    if (value > 0x7f)
+        return 0;
+    ch = (int)value;
+    /*
+     * Note: we can't use 'isalnum' because certain accented characters may
+     * count as alphanumeric in some environments.
+     */
+#ifndef CHARSET_EBCDIC
+    if ((ch >= 'a') && (ch <= 'z'))
+        return 1;
+    if ((ch >= 'A') && (ch <= 'Z'))
+        return 1;
+    if ((ch >= '0') && (ch <= '9'))
+        return 1;
+    if ((ch == ' ') || strchr("'()+,-./:=?", ch))
+        return 1;
+#else                           /* CHARSET_EBCDIC */
+    if ((ch >= os_toascii['a']) && (ch <= os_toascii['z']))
+        return 1;
+    if ((ch >= os_toascii['A']) && (ch <= os_toascii['Z']))
+        return 1;
+    if ((ch >= os_toascii['0']) && (ch <= os_toascii['9']))
+        return 1;
+    if ((ch == os_toascii[' ']) || strchr("'()+,-./:=?", os_toebcdic[ch]))
+        return 1;
+#endif                          /* CHARSET_EBCDIC */
+    return 0;
+}
+
+/* Return 1 if the character is a digit or space */
+static int is_numeric(unsigned long value)
+{
+    int ch;
+    if (value > 0x7f)
+        return 0;
+    ch = (int)value;
+#ifndef CHARSET_EBCDIC
+    if (!isdigit(ch) && ch != ' ')
+        return 0;
+#else
+    if (ch > os_toascii['9'])
+        return 0;
+    if (ch < os_toascii['0'] && ch != os_toascii[' '])
+        return 0;
+#endif
+    return 1;
+}
diff --git a/crypto/asn1/a_object.c b/crypto/asn1/a_object.c
new file mode 100644
index 000000000..79f0ecd2a
--- /dev/null
+++ b/crypto/asn1/a_object.c
@@ -0,0 +1,370 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+#include 
+#include 
+#include "internal/asn1_int.h"
+#include "asn1_locl.h"
+
+int i2d_ASN1_OBJECT(const ASN1_OBJECT *a, unsigned char **pp)
+{
+    unsigned char *p;
+    int objsize;
+
+    if ((a == NULL) || (a->data == NULL))
+        return (0);
+
+    objsize = ASN1_object_size(0, a->length, V_ASN1_OBJECT);
+    if (pp == NULL || objsize == -1)
+        return objsize;
+
+    p = *pp;
+    ASN1_put_object(&p, 0, a->length, V_ASN1_OBJECT, V_ASN1_UNIVERSAL);
+    memcpy(p, a->data, a->length);
+    p += a->length;
+
+    *pp = p;
+    return (objsize);
+}
+
+int a2d_ASN1_OBJECT(unsigned char *out, int olen, const char *buf, int num)
+{
+    int i, first, len = 0, c, use_bn;
+    char ftmp[24], *tmp = ftmp;
+    int tmpsize = sizeof ftmp;
+    const char *p;
+    unsigned long l;
+    BIGNUM *bl = NULL;
+
+    if (num == 0)
+        return (0);
+    else if (num == -1)
+        num = strlen(buf);
+
+    p = buf;
+    c = *(p++);
+    num--;
+    if ((c >= '0') && (c <= '2')) {
+        first = c - '0';
+    } else {
+        ASN1err(ASN1_F_A2D_ASN1_OBJECT, ASN1_R_FIRST_NUM_TOO_LARGE);
+        goto err;
+    }
+
+    if (num <= 0) {
+        ASN1err(ASN1_F_A2D_ASN1_OBJECT, ASN1_R_MISSING_SECOND_NUMBER);
+        goto err;
+    }
+    c = *(p++);
+    num--;
+    for (;;) {
+        if (num <= 0)
+            break;
+        if ((c != '.') && (c != ' ')) {
+            ASN1err(ASN1_F_A2D_ASN1_OBJECT, ASN1_R_INVALID_SEPARATOR);
+            goto err;
+        }
+        l = 0;
+        use_bn = 0;
+        for (;;) {
+            if (num <= 0)
+                break;
+            num--;
+            c = *(p++);
+            if ((c == ' ') || (c == '.'))
+                break;
+            if ((c < '0') || (c > '9')) {
+                ASN1err(ASN1_F_A2D_ASN1_OBJECT, ASN1_R_INVALID_DIGIT);
+                goto err;
+            }
+            if (!use_bn && l >= ((ULONG_MAX - 80) / 10L)) {
+                use_bn = 1;
+                if (bl == NULL)
+                    bl = BN_new();
+                if (bl == NULL || !BN_set_word(bl, l))
+                    goto err;
+            }
+            if (use_bn) {
+                if (!BN_mul_word(bl, 10L)
+                    || !BN_add_word(bl, c - '0'))
+                    goto err;
+            } else
+                l = l * 10L + (long)(c - '0');
+        }
+        if (len == 0) {
+            if ((first < 2) && (l >= 40)) {
+                ASN1err(ASN1_F_A2D_ASN1_OBJECT,
+                        ASN1_R_SECOND_NUMBER_TOO_LARGE);
+                goto err;
+            }
+            if (use_bn) {
+                if (!BN_add_word(bl, first * 40))
+                    goto err;
+            } else
+                l += (long)first *40;
+        }
+        i = 0;
+        if (use_bn) {
+            int blsize;
+            blsize = BN_num_bits(bl);
+            blsize = (blsize + 6) / 7;
+            if (blsize > tmpsize) {
+                if (tmp != ftmp)
+                    OPENSSL_free(tmp);
+                tmpsize = blsize + 32;
+                tmp = OPENSSL_malloc(tmpsize);
+                if (tmp == NULL)
+                    goto err;
+            }
+            while (blsize--) {
+                BN_ULONG t = BN_div_word(bl, 0x80L);
+                if (t == (BN_ULONG)-1)
+                    goto err;
+                tmp[i++] = (unsigned char)t;
+            }
+        } else {
+
+            for (;;) {
+                tmp[i++] = (unsigned char)l & 0x7f;
+                l >>= 7L;
+                if (l == 0L)
+                    break;
+            }
+
+        }
+        if (out != NULL) {
+            if (len + i > olen) {
+                ASN1err(ASN1_F_A2D_ASN1_OBJECT, ASN1_R_BUFFER_TOO_SMALL);
+                goto err;
+            }
+            while (--i > 0)
+                out[len++] = tmp[i] | 0x80;
+            out[len++] = tmp[0];
+        } else
+            len += i;
+    }
+    if (tmp != ftmp)
+        OPENSSL_free(tmp);
+    BN_free(bl);
+    return (len);
+ err:
+    if (tmp != ftmp)
+        OPENSSL_free(tmp);
+    BN_free(bl);
+    return (0);
+}
+
+int i2t_ASN1_OBJECT(char *buf, int buf_len, const ASN1_OBJECT *a)
+{
+    return OBJ_obj2txt(buf, buf_len, a, 0);
+}
+
+int i2a_ASN1_OBJECT(BIO *bp, const ASN1_OBJECT *a)
+{
+    char buf[80], *p = buf;
+    int i;
+
+    if ((a == NULL) || (a->data == NULL))
+        return (BIO_write(bp, "NULL", 4));
+    i = i2t_ASN1_OBJECT(buf, sizeof buf, a);
+    if (i > (int)(sizeof(buf) - 1)) {
+        p = OPENSSL_malloc(i + 1);
+        if (p == NULL)
+            return -1;
+        i2t_ASN1_OBJECT(p, i + 1, a);
+    }
+    if (i <= 0) {
+        i = BIO_write(bp, "", 9);
+        i += BIO_dump(bp, (const char *)a->data, a->length);
+        return i;
+    }
+    BIO_write(bp, p, i);
+    if (p != buf)
+        OPENSSL_free(p);
+    return (i);
+}
+
+ASN1_OBJECT *d2i_ASN1_OBJECT(ASN1_OBJECT **a, const unsigned char **pp,
+                             long length)
+{
+    const unsigned char *p;
+    long len;
+    int tag, xclass;
+    int inf, i;
+    ASN1_OBJECT *ret = NULL;
+    p = *pp;
+    inf = ASN1_get_object(&p, &len, &tag, &xclass, length);
+    if (inf & 0x80) {
+        i = ASN1_R_BAD_OBJECT_HEADER;
+        goto err;
+    }
+
+    if (tag != V_ASN1_OBJECT) {
+        i = ASN1_R_EXPECTING_AN_OBJECT;
+        goto err;
+    }
+    ret = c2i_ASN1_OBJECT(a, &p, len);
+    if (ret)
+        *pp = p;
+    return ret;
+ err:
+    ASN1err(ASN1_F_D2I_ASN1_OBJECT, i);
+    return (NULL);
+}
+
+ASN1_OBJECT *c2i_ASN1_OBJECT(ASN1_OBJECT **a, const unsigned char **pp,
+                             long len)
+{
+    ASN1_OBJECT *ret = NULL, tobj;
+    const unsigned char *p;
+    unsigned char *data;
+    int i, length;
+
+    /*
+     * Sanity check OID encoding. Need at least one content octet. MSB must
+     * be clear in the last octet. can't have leading 0x80 in subidentifiers,
+     * see: X.690 8.19.2
+     */
+    if (len <= 0 || len > INT_MAX || pp == NULL || (p = *pp) == NULL ||
+        p[len - 1] & 0x80) {
+        ASN1err(ASN1_F_C2I_ASN1_OBJECT, ASN1_R_INVALID_OBJECT_ENCODING);
+        return NULL;
+    }
+    /* Now 0 < len <= INT_MAX, so the cast is safe. */
+    length = (int)len;
+    /*
+     * Try to lookup OID in table: these are all valid encodings so if we get
+     * a match we know the OID is valid.
+     */
+    tobj.nid = NID_undef;
+    tobj.data = p;
+    tobj.length = length;
+    tobj.flags = 0;
+    i = OBJ_obj2nid(&tobj);
+    if (i != NID_undef) {
+        /*
+         * Return shared registered OID object: this improves efficiency
+         * because we don't have to return a dynamically allocated OID
+         * and NID lookups can use the cached value.
+         */
+        ret = OBJ_nid2obj(i);
+        if (a) {
+            ASN1_OBJECT_free(*a);
+            *a = ret;
+        }
+        *pp += len;
+        return ret;
+    }
+    for (i = 0; i < length; i++, p++) {
+        if (*p == 0x80 && (!i || !(p[-1] & 0x80))) {
+            ASN1err(ASN1_F_C2I_ASN1_OBJECT, ASN1_R_INVALID_OBJECT_ENCODING);
+            return NULL;
+        }
+    }
+
+    /*
+     * only the ASN1_OBJECTs from the 'table' will have values for ->sn or
+     * ->ln
+     */
+    if ((a == NULL) || ((*a) == NULL) ||
+        !((*a)->flags & ASN1_OBJECT_FLAG_DYNAMIC)) {
+        if ((ret = ASN1_OBJECT_new()) == NULL)
+            return (NULL);
+    } else
+        ret = (*a);
+
+    p = *pp;
+    /* detach data from object */
+    data = (unsigned char *)ret->data;
+    ret->data = NULL;
+    /* once detached we can change it */
+    if ((data == NULL) || (ret->length < length)) {
+        ret->length = 0;
+        OPENSSL_free(data);
+        data = OPENSSL_malloc(length);
+        if (data == NULL) {
+            i = ERR_R_MALLOC_FAILURE;
+            goto err;
+        }
+        ret->flags |= ASN1_OBJECT_FLAG_DYNAMIC_DATA;
+    }
+    memcpy(data, p, length);
+    /* reattach data to object, after which it remains const */
+    ret->data = data;
+    ret->length = length;
+    ret->sn = NULL;
+    ret->ln = NULL;
+    /* ret->flags=ASN1_OBJECT_FLAG_DYNAMIC; we know it is dynamic */
+    p += length;
+
+    if (a != NULL)
+        (*a) = ret;
+    *pp = p;
+    return (ret);
+ err:
+    ASN1err(ASN1_F_C2I_ASN1_OBJECT, i);
+    if ((a == NULL) || (*a != ret))
+        ASN1_OBJECT_free(ret);
+    return (NULL);
+}
+
+ASN1_OBJECT *ASN1_OBJECT_new(void)
+{
+    ASN1_OBJECT *ret;
+
+    ret = OPENSSL_zalloc(sizeof(*ret));
+    if (ret == NULL) {
+        ASN1err(ASN1_F_ASN1_OBJECT_NEW, ERR_R_MALLOC_FAILURE);
+        return (NULL);
+    }
+    ret->flags = ASN1_OBJECT_FLAG_DYNAMIC;
+    return (ret);
+}
+
+void ASN1_OBJECT_free(ASN1_OBJECT *a)
+{
+    if (a == NULL)
+        return;
+    if (a->flags & ASN1_OBJECT_FLAG_DYNAMIC_STRINGS) {
+#ifndef CONST_STRICT            /* disable purely for compile-time strict
+                                 * const checking. Doing this on a "real"
+                                 * compile will cause memory leaks */
+        OPENSSL_free((void*)a->sn);
+        OPENSSL_free((void*)a->ln);
+#endif
+        a->sn = a->ln = NULL;
+    }
+    if (a->flags & ASN1_OBJECT_FLAG_DYNAMIC_DATA) {
+        OPENSSL_free((void*)a->data);
+        a->data = NULL;
+        a->length = 0;
+    }
+    if (a->flags & ASN1_OBJECT_FLAG_DYNAMIC)
+        OPENSSL_free(a);
+}
+
+ASN1_OBJECT *ASN1_OBJECT_create(int nid, unsigned char *data, int len,
+                                const char *sn, const char *ln)
+{
+    ASN1_OBJECT o;
+
+    o.sn = sn;
+    o.ln = ln;
+    o.data = data;
+    o.nid = nid;
+    o.length = len;
+    o.flags = ASN1_OBJECT_FLAG_DYNAMIC | ASN1_OBJECT_FLAG_DYNAMIC_STRINGS |
+        ASN1_OBJECT_FLAG_DYNAMIC_DATA;
+    return (OBJ_dup(&o));
+}
diff --git a/crypto/asn1/a_octet.c b/crypto/asn1/a_octet.c
new file mode 100644
index 000000000..7ce054129
--- /dev/null
+++ b/crypto/asn1/a_octet.c
@@ -0,0 +1,42 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+
+ASN1_OCTET_STRING *ASN1_OCTET_STRING_dup(const ASN1_OCTET_STRING *x)
+{
+    return ASN1_STRING_dup(x);
+}
+
+int ASN1_OCTET_STRING_cmp(const ASN1_OCTET_STRING *a,
+                          const ASN1_OCTET_STRING *b)
+{
+    return ASN1_STRING_cmp(a, b);
+}
+
+int ASN1_OCTET_STRING_set(ASN1_OCTET_STRING *x, const unsigned char *d,
+                          int len)
+{
+    return ASN1_STRING_set(x, d, len);
+}
+
+#ifndef OPENSSL_NO_SM2
+int ASN1_OCTET_STRING_is_zero(const ASN1_OCTET_STRING *a)
+{
+    int i;
+    for (i = 0; i < a->length; i++) {
+        if (a->data[i] != 0) {
+            return 0;
+        }
+    }
+    return 1;
+}
+#endif
diff --git a/crypto/asn1/a_print.c b/crypto/asn1/a_print.c
new file mode 100644
index 000000000..1aafe7c83
--- /dev/null
+++ b/crypto/asn1/a_print.c
@@ -0,0 +1,109 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include "internal/cryptlib.h"
+#include 
+
+int ASN1_PRINTABLE_type(const unsigned char *s, int len)
+{
+    int c;
+    int ia5 = 0;
+    int t61 = 0;
+
+    if (len <= 0)
+        len = -1;
+    if (s == NULL)
+        return (V_ASN1_PRINTABLESTRING);
+
+    while ((*s) && (len-- != 0)) {
+        c = *(s++);
+#ifndef CHARSET_EBCDIC
+        if (!(((c >= 'a') && (c <= 'z')) ||
+              ((c >= 'A') && (c <= 'Z')) ||
+              ((c >= '0') && (c <= '9')) ||
+              (c == ' ') || (c == '\'') ||
+              (c == '(') || (c == ')') ||
+              (c == '+') || (c == ',') ||
+              (c == '-') || (c == '.') ||
+              (c == '/') || (c == ':') || (c == '=') || (c == '?')))
+            ia5 = 1;
+        if (c & 0x80)
+            t61 = 1;
+#else
+        if (!isalnum(c) && (c != ' ') && strchr("'()+,-./:=?", c) == NULL)
+            ia5 = 1;
+        if (os_toascii[c] & 0x80)
+            t61 = 1;
+#endif
+    }
+    if (t61)
+        return (V_ASN1_T61STRING);
+    if (ia5)
+        return (V_ASN1_IA5STRING);
+    return (V_ASN1_PRINTABLESTRING);
+}
+
+int ASN1_UNIVERSALSTRING_to_string(ASN1_UNIVERSALSTRING *s)
+{
+    int i;
+    unsigned char *p;
+
+    if (s->type != V_ASN1_UNIVERSALSTRING)
+        return (0);
+    if ((s->length % 4) != 0)
+        return (0);
+    p = s->data;
+    for (i = 0; i < s->length; i += 4) {
+        if ((p[0] != '\0') || (p[1] != '\0') || (p[2] != '\0'))
+            break;
+        else
+            p += 4;
+    }
+    if (i < s->length)
+        return (0);
+    p = s->data;
+    for (i = 3; i < s->length; i += 4) {
+        *(p++) = s->data[i];
+    }
+    *(p) = '\0';
+    s->length /= 4;
+    s->type = ASN1_PRINTABLE_type(s->data, s->length);
+    return (1);
+}
+
+int ASN1_STRING_print(BIO *bp, const ASN1_STRING *v)
+{
+    int i, n;
+    char buf[80];
+    const char *p;
+
+    if (v == NULL)
+        return (0);
+    n = 0;
+    p = (const char *)v->data;
+    for (i = 0; i < v->length; i++) {
+        if ((p[i] > '~') || ((p[i] < ' ') &&
+                             (p[i] != '\n') && (p[i] != '\r')))
+            buf[n] = '.';
+        else
+            buf[n] = p[i];
+        n++;
+        if (n >= 80) {
+            if (BIO_write(bp, buf, n) <= 0)
+                return (0);
+            n = 0;
+        }
+    }
+    if (n > 0)
+        if (BIO_write(bp, buf, n) <= 0)
+            return (0);
+    return (1);
+}
diff --git a/crypto/asn1/a_sign.c b/crypto/asn1/a_sign.c
new file mode 100644
index 000000000..1a02e8004
--- /dev/null
+++ b/crypto/asn1/a_sign.c
@@ -0,0 +1,241 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+
+#include "internal/cryptlib.h"
+
+#ifndef NO_SYS_TYPES_H
+# include 
+#endif
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#ifndef OPENSSL_NO_SM2
+# include 
+#endif
+#include "internal/asn1_int.h"
+#include "internal/evp_int.h"
+
+#ifndef NO_ASN1_OLD
+
+int ASN1_sign(i2d_of_void *i2d, X509_ALGOR *algor1, X509_ALGOR *algor2,
+              ASN1_BIT_STRING *signature, char *data, EVP_PKEY *pkey,
+              const EVP_MD *type)
+{
+    EVP_MD_CTX *ctx = EVP_MD_CTX_new();
+    unsigned char *p, *buf_in = NULL, *buf_out = NULL;
+    int i, inl = 0, outl = 0, outll = 0;
+    X509_ALGOR *a;
+
+    if (ctx == NULL) {
+        ASN1err(ASN1_F_ASN1_SIGN, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+    for (i = 0; i < 2; i++) {
+        if (i == 0)
+            a = algor1;
+        else
+            a = algor2;
+        if (a == NULL)
+            continue;
+        if (type->pkey_type == NID_dsaWithSHA1) {
+            /*
+             * special case: RFC 2459 tells us to omit 'parameters' with
+             * id-dsa-with-sha1
+             */
+            ASN1_TYPE_free(a->parameter);
+            a->parameter = NULL;
+        } else if ((a->parameter == NULL) ||
+                   (a->parameter->type != V_ASN1_NULL)) {
+            ASN1_TYPE_free(a->parameter);
+            if ((a->parameter = ASN1_TYPE_new()) == NULL)
+                goto err;
+            a->parameter->type = V_ASN1_NULL;
+        }
+        ASN1_OBJECT_free(a->algorithm);
+        a->algorithm = OBJ_nid2obj(type->pkey_type);
+        if (a->algorithm == NULL) {
+            ASN1err(ASN1_F_ASN1_SIGN, ASN1_R_UNKNOWN_OBJECT_TYPE);
+            goto err;
+        }
+        if (a->algorithm->length == 0) {
+            ASN1err(ASN1_F_ASN1_SIGN,
+                    ASN1_R_THE_ASN1_OBJECT_IDENTIFIER_IS_NOT_KNOWN_FOR_THIS_MD);
+            goto err;
+        }
+    }
+    inl = i2d(data, NULL);
+    buf_in = OPENSSL_malloc((unsigned int)inl);
+    outll = outl = EVP_PKEY_size(pkey);
+    buf_out = OPENSSL_malloc((unsigned int)outl);
+    if ((buf_in == NULL) || (buf_out == NULL)) {
+        outl = 0;
+        ASN1err(ASN1_F_ASN1_SIGN, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+    p = buf_in;
+
+    i2d(data, &p);
+    if (!EVP_SignInit_ex(ctx, type, NULL)
+        || !EVP_SignUpdate(ctx, (unsigned char *)buf_in, inl)
+        || !EVP_SignFinal(ctx, (unsigned char *)buf_out,
+                          (unsigned int *)&outl, pkey)) {
+        outl = 0;
+        ASN1err(ASN1_F_ASN1_SIGN, ERR_R_EVP_LIB);
+        goto err;
+    }
+    OPENSSL_free(signature->data);
+    signature->data = buf_out;
+    buf_out = NULL;
+    signature->length = outl;
+    /*
+     * In the interests of compatibility, I'll make sure that the bit string
+     * has a 'not-used bits' value of 0
+     */
+    signature->flags &= ~(ASN1_STRING_FLAG_BITS_LEFT | 0x07);
+    signature->flags |= ASN1_STRING_FLAG_BITS_LEFT;
+ err:
+    EVP_MD_CTX_free(ctx);
+    OPENSSL_clear_free((char *)buf_in, (unsigned int)inl);
+    OPENSSL_clear_free((char *)buf_out, outll);
+    return (outl);
+}
+
+#endif
+
+int ASN1_item_sign(const ASN1_ITEM *it, X509_ALGOR *algor1,
+                   X509_ALGOR *algor2, ASN1_BIT_STRING *signature, void *asn,
+                   EVP_PKEY *pkey, const EVP_MD *type)
+{
+    int rv;
+    EVP_MD_CTX *ctx = EVP_MD_CTX_new();
+
+    if (ctx == NULL) {
+        ASN1err(ASN1_F_ASN1_ITEM_SIGN, ERR_R_MALLOC_FAILURE);
+        return 0;
+    }
+    if (!EVP_DigestSignInit(ctx, NULL, type, NULL, pkey)) {
+        EVP_MD_CTX_free(ctx);
+        return 0;
+    }
+
+    rv = ASN1_item_sign_ctx(it, algor1, algor2, signature, asn, ctx);
+
+    EVP_MD_CTX_free(ctx);
+    return rv;
+}
+
+int ASN1_item_sign_ctx(const ASN1_ITEM *it,
+                       X509_ALGOR *algor1, X509_ALGOR *algor2,
+                       ASN1_BIT_STRING *signature, void *asn, EVP_MD_CTX *ctx)
+{
+    const EVP_MD *type;
+    EVP_PKEY *pkey;
+    unsigned char *buf_in = NULL, *buf_out = NULL;
+    size_t inl = 0, outl = 0, outll = 0;
+    int signid, paramtype;
+    int rv;
+
+    type = EVP_MD_CTX_md(ctx);
+    pkey = EVP_PKEY_CTX_get0_pkey(EVP_MD_CTX_pkey_ctx(ctx));
+
+    if (type == NULL || pkey == NULL) {
+        ASN1err(ASN1_F_ASN1_ITEM_SIGN_CTX, ASN1_R_CONTEXT_NOT_INITIALISED);
+        goto err;
+    }
+
+    if (pkey->ameth == NULL) {
+        ASN1err(ASN1_F_ASN1_ITEM_SIGN_CTX, ASN1_R_DIGEST_AND_KEY_TYPE_NOT_SUPPORTED);
+        goto err;
+    }
+
+    if (pkey->ameth->item_sign) {
+        rv = pkey->ameth->item_sign(ctx, it, asn, algor1, algor2, signature);
+        if (rv == 1)
+            outl = signature->length;
+        /*-
+         * Return value meanings:
+         * <=0: error.
+         *   1: method does everything.
+         *   2: carry on as normal.
+         *   3: ASN1 method sets algorithm identifiers: just sign.
+         */
+        if (rv <= 0)
+            ASN1err(ASN1_F_ASN1_ITEM_SIGN_CTX, ERR_R_EVP_LIB);
+        if (rv <= 1)
+            goto err;
+    } else
+        rv = 2;
+
+    if (rv == 2) {
+        if (!OBJ_find_sigid_by_algs(&signid,
+                                    EVP_MD_nid(type),
+                                    pkey->ameth->pkey_id)) {
+            ASN1err(ASN1_F_ASN1_ITEM_SIGN_CTX,
+                    ASN1_R_DIGEST_AND_KEY_TYPE_NOT_SUPPORTED);
+            goto err;
+        }
+
+        if (pkey->ameth->pkey_flags & ASN1_PKEY_SIGPARAM_NULL)
+            paramtype = V_ASN1_NULL;
+        else
+            paramtype = V_ASN1_UNDEF;
+
+        if (algor1)
+            X509_ALGOR_set0(algor1, OBJ_nid2obj(signid), paramtype, NULL);
+        if (algor2)
+            X509_ALGOR_set0(algor2, OBJ_nid2obj(signid), paramtype, NULL);
+
+    }
+
+    inl = ASN1_item_i2d(asn, &buf_in, it);
+    outll = outl = EVP_PKEY_size(pkey);
+    buf_out = OPENSSL_malloc((unsigned int)outl);
+    if ((buf_in == NULL) || (buf_out == NULL)) {
+        outl = 0;
+        ASN1err(ASN1_F_ASN1_ITEM_SIGN_CTX, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+
+#ifndef OPENSSL_NO_SM2
+    if (OBJ_obj2nid(algor1->algorithm) == NID_sm2sign_with_sm3) {
+        if (!EVP_PKEY_CTX_set_ec_scheme(EVP_MD_CTX_pkey_ctx(ctx), NID_sm_scheme)) {
+            outl = 0;
+            ASN1err(ASN1_F_ASN1_ITEM_SIGN_CTX, ERR_R_EC_LIB);
+            goto err;
+        }
+    }
+#endif
+
+    if (!EVP_DigestSignUpdate(ctx, buf_in, inl)
+        || !EVP_DigestSignFinal(ctx, buf_out, &outl)) {
+        outl = 0;
+        ASN1err(ASN1_F_ASN1_ITEM_SIGN_CTX, ERR_R_EVP_LIB);
+        goto err;
+    }
+    OPENSSL_free(signature->data);
+    signature->data = buf_out;
+    buf_out = NULL;
+    signature->length = outl;
+    /*
+     * In the interests of compatibility, I'll make sure that the bit string
+     * has a 'not-used bits' value of 0
+     */
+    signature->flags &= ~(ASN1_STRING_FLAG_BITS_LEFT | 0x07);
+    signature->flags |= ASN1_STRING_FLAG_BITS_LEFT;
+ err:
+    OPENSSL_clear_free((char *)buf_in, (unsigned int)inl);
+    OPENSSL_clear_free((char *)buf_out, outll);
+    return (outl);
+}
diff --git a/crypto/asn1/a_strex.c b/crypto/asn1/a_strex.c
new file mode 100644
index 000000000..9839f5c76
--- /dev/null
+++ b/crypto/asn1/a_strex.c
@@ -0,0 +1,645 @@
+/*
+ * Copyright 2000-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include "internal/cryptlib.h"
+#include "internal/asn1_int.h"
+#include 
+#include 
+#include 
+
+#include "charmap.h"
+
+/*
+ * ASN1_STRING_print_ex() and X509_NAME_print_ex(). Enhanced string and name
+ * printing routines handling multibyte characters, RFC2253 and a host of
+ * other options.
+ */
+
+#define CHARTYPE_BS_ESC         (ASN1_STRFLGS_ESC_2253 | CHARTYPE_FIRST_ESC_2253 | CHARTYPE_LAST_ESC_2253)
+
+#define ESC_FLAGS (ASN1_STRFLGS_ESC_2253 | \
+                  ASN1_STRFLGS_ESC_2254 | \
+                  ASN1_STRFLGS_ESC_QUOTE | \
+                  ASN1_STRFLGS_ESC_CTRL | \
+                  ASN1_STRFLGS_ESC_MSB)
+
+/*
+ * Three IO functions for sending data to memory, a BIO and and a FILE
+ * pointer.
+ */
+static int send_bio_chars(void *arg, const void *buf, int len)
+{
+    if (!arg)
+        return 1;
+    if (BIO_write(arg, buf, len) != len)
+        return 0;
+    return 1;
+}
+
+#ifndef OPENSSL_NO_STDIO
+static int send_fp_chars(void *arg, const void *buf, int len)
+{
+    if (!arg)
+        return 1;
+    if (fwrite(buf, 1, len, arg) != (unsigned int)len)
+        return 0;
+    return 1;
+}
+#endif
+
+typedef int char_io (void *arg, const void *buf, int len);
+
+/*
+ * This function handles display of strings, one character at a time. It is
+ * passed an unsigned long for each character because it could come from 2 or
+ * even 4 byte forms.
+ */
+
+static int do_esc_char(unsigned long c, unsigned char flags, char *do_quotes,
+                       char_io *io_ch, void *arg)
+{
+    unsigned short chflgs;
+    unsigned char chtmp;
+    char tmphex[HEX_SIZE(long) + 3];
+
+    if (c > 0xffffffffL)
+        return -1;
+    if (c > 0xffff) {
+        BIO_snprintf(tmphex, sizeof tmphex, "\\W%08lX", c);
+        if (!io_ch(arg, tmphex, 10))
+            return -1;
+        return 10;
+    }
+    if (c > 0xff) {
+        BIO_snprintf(tmphex, sizeof tmphex, "\\U%04lX", c);
+        if (!io_ch(arg, tmphex, 6))
+            return -1;
+        return 6;
+    }
+    chtmp = (unsigned char)c;
+    if (chtmp > 0x7f)
+        chflgs = flags & ASN1_STRFLGS_ESC_MSB;
+    else
+        chflgs = char_type[chtmp] & flags;
+    if (chflgs & CHARTYPE_BS_ESC) {
+        /* If we don't escape with quotes, signal we need quotes */
+        if (chflgs & ASN1_STRFLGS_ESC_QUOTE) {
+            if (do_quotes)
+                *do_quotes = 1;
+            if (!io_ch(arg, &chtmp, 1))
+                return -1;
+            return 1;
+        }
+        if (!io_ch(arg, "\\", 1))
+            return -1;
+        if (!io_ch(arg, &chtmp, 1))
+            return -1;
+        return 2;
+    }
+    if (chflgs & (ASN1_STRFLGS_ESC_CTRL
+                  | ASN1_STRFLGS_ESC_MSB
+                  | ASN1_STRFLGS_ESC_2254)) {
+        BIO_snprintf(tmphex, 11, "\\%02X", chtmp);
+        if (!io_ch(arg, tmphex, 3))
+            return -1;
+        return 3;
+    }
+    /*
+     * If we get this far and do any escaping at all must escape the escape
+     * character itself: backslash.
+     */
+    if (chtmp == '\\' && flags & ESC_FLAGS) {
+        if (!io_ch(arg, "\\\\", 2))
+            return -1;
+        return 2;
+    }
+    if (!io_ch(arg, &chtmp, 1))
+        return -1;
+    return 1;
+}
+
+#define BUF_TYPE_WIDTH_MASK     0x7
+#define BUF_TYPE_CONVUTF8       0x8
+
+/*
+ * This function sends each character in a buffer to do_esc_char(). It
+ * interprets the content formats and converts to or from UTF8 as
+ * appropriate.
+ */
+
+static int do_buf(unsigned char *buf, int buflen,
+                  int type, unsigned short flags, char *quotes, char_io *io_ch,
+                  void *arg)
+{
+    int i, outlen, len;
+    unsigned short orflags;
+    unsigned char *p, *q;
+    unsigned long c;
+    p = buf;
+    q = buf + buflen;
+    outlen = 0;
+    while (p != q) {
+        if (p == buf && flags & ASN1_STRFLGS_ESC_2253)
+            orflags = CHARTYPE_FIRST_ESC_2253;
+        else
+            orflags = 0;
+        switch (type & BUF_TYPE_WIDTH_MASK) {
+        case 4:
+            c = ((unsigned long)*p++) << 24;
+            c |= ((unsigned long)*p++) << 16;
+            c |= ((unsigned long)*p++) << 8;
+            c |= *p++;
+            break;
+
+        case 2:
+            c = ((unsigned long)*p++) << 8;
+            c |= *p++;
+            break;
+
+        case 1:
+            c = *p++;
+            break;
+
+        case 0:
+            i = UTF8_getc(p, buflen, &c);
+            if (i < 0)
+                return -1;      /* Invalid UTF8String */
+            p += i;
+            break;
+        default:
+            return -1;          /* invalid width */
+        }
+        if (p == q && flags & ASN1_STRFLGS_ESC_2253)
+            orflags = CHARTYPE_LAST_ESC_2253;
+        if (type & BUF_TYPE_CONVUTF8) {
+            unsigned char utfbuf[6];
+            int utflen;
+            utflen = UTF8_putc(utfbuf, sizeof utfbuf, c);
+            for (i = 0; i < utflen; i++) {
+                /*
+                 * We don't need to worry about setting orflags correctly
+                 * because if utflen==1 its value will be correct anyway
+                 * otherwise each character will be > 0x7f and so the
+                 * character will never be escaped on first and last.
+                 */
+                len =
+                    do_esc_char(utfbuf[i], (unsigned short)(flags | orflags),
+                                quotes, io_ch, arg);
+                if (len < 0)
+                    return -1;
+                outlen += len;
+            }
+        } else {
+            len =
+                do_esc_char(c, (unsigned short)(flags | orflags), quotes,
+                            io_ch, arg);
+            if (len < 0)
+                return -1;
+            outlen += len;
+        }
+    }
+    return outlen;
+}
+
+/* This function hex dumps a buffer of characters */
+
+static int do_hex_dump(char_io *io_ch, void *arg, unsigned char *buf,
+                       int buflen)
+{
+    static const char hexdig[] = "0123456789ABCDEF";
+    unsigned char *p, *q;
+    char hextmp[2];
+    if (arg) {
+        p = buf;
+        q = buf + buflen;
+        while (p != q) {
+            hextmp[0] = hexdig[*p >> 4];
+            hextmp[1] = hexdig[*p & 0xf];
+            if (!io_ch(arg, hextmp, 2))
+                return -1;
+            p++;
+        }
+    }
+    return buflen << 1;
+}
+
+/*
+ * "dump" a string. This is done when the type is unknown, or the flags
+ * request it. We can either dump the content octets or the entire DER
+ * encoding. This uses the RFC2253 #01234 format.
+ */
+
+static int do_dump(unsigned long lflags, char_io *io_ch, void *arg,
+                   const ASN1_STRING *str)
+{
+    /*
+     * Placing the ASN1_STRING in a temp ASN1_TYPE allows the DER encoding to
+     * readily obtained
+     */
+    ASN1_TYPE t;
+    unsigned char *der_buf, *p;
+    int outlen, der_len;
+
+    if (!io_ch(arg, "#", 1))
+        return -1;
+    /* If we don't dump DER encoding just dump content octets */
+    if (!(lflags & ASN1_STRFLGS_DUMP_DER)) {
+        outlen = do_hex_dump(io_ch, arg, str->data, str->length);
+        if (outlen < 0)
+            return -1;
+        return outlen + 1;
+    }
+    t.type = str->type;
+    t.value.ptr = (char *)str;
+    der_len = i2d_ASN1_TYPE(&t, NULL);
+    der_buf = OPENSSL_malloc(der_len);
+    if (der_buf == NULL)
+        return -1;
+    p = der_buf;
+    i2d_ASN1_TYPE(&t, &p);
+    outlen = do_hex_dump(io_ch, arg, der_buf, der_len);
+    OPENSSL_free(der_buf);
+    if (outlen < 0)
+        return -1;
+    return outlen + 1;
+}
+
+/*
+ * Lookup table to convert tags to character widths, 0 = UTF8 encoded, -1 is
+ * used for non string types otherwise it is the number of bytes per
+ * character
+ */
+
+static const signed char tag2nbyte[] = {
+    -1, -1, -1, -1, -1,         /* 0-4 */
+    -1, -1, -1, -1, -1,         /* 5-9 */
+    -1, -1, 0, -1,              /* 10-13 */
+    -1, -1, -1, -1,             /* 15-17 */
+    1, 1, 1,                    /* 18-20 */
+    -1, 1, 1, 1,                /* 21-24 */
+    -1, 1, -1,                  /* 25-27 */
+    4, -1, 2                    /* 28-30 */
+};
+
+/*
+ * This is the main function, print out an ASN1_STRING taking note of various
+ * escape and display options. Returns number of characters written or -1 if
+ * an error occurred.
+ */
+
+static int do_print_ex(char_io *io_ch, void *arg, unsigned long lflags,
+                       const ASN1_STRING *str)
+{
+    int outlen, len;
+    int type;
+    char quotes;
+    unsigned short flags;
+    quotes = 0;
+    /* Keep a copy of escape flags */
+    flags = (unsigned short)(lflags & ESC_FLAGS);
+
+    type = str->type;
+
+    outlen = 0;
+
+    if (lflags & ASN1_STRFLGS_SHOW_TYPE) {
+        const char *tagname;
+        tagname = ASN1_tag2str(type);
+        outlen += strlen(tagname);
+        if (!io_ch(arg, tagname, outlen) || !io_ch(arg, ":", 1))
+            return -1;
+        outlen++;
+    }
+
+    /* Decide what to do with type, either dump content or display it */
+
+    /* Dump everything */
+    if (lflags & ASN1_STRFLGS_DUMP_ALL)
+        type = -1;
+    /* Ignore the string type */
+    else if (lflags & ASN1_STRFLGS_IGNORE_TYPE)
+        type = 1;
+    else {
+        /* Else determine width based on type */
+        if ((type > 0) && (type < 31))
+            type = tag2nbyte[type];
+        else
+            type = -1;
+        if ((type == -1) && !(lflags & ASN1_STRFLGS_DUMP_UNKNOWN))
+            type = 1;
+    }
+
+    if (type == -1) {
+        len = do_dump(lflags, io_ch, arg, str);
+        if (len < 0)
+            return -1;
+        outlen += len;
+        return outlen;
+    }
+
+    if (lflags & ASN1_STRFLGS_UTF8_CONVERT) {
+        /*
+         * Note: if string is UTF8 and we want to convert to UTF8 then we
+         * just interpret it as 1 byte per character to avoid converting
+         * twice.
+         */
+        if (!type)
+            type = 1;
+        else
+            type |= BUF_TYPE_CONVUTF8;
+    }
+
+    len = do_buf(str->data, str->length, type, flags, "es, io_ch, NULL);
+    if (len < 0)
+        return -1;
+    outlen += len;
+    if (quotes)
+        outlen += 2;
+    if (!arg)
+        return outlen;
+    if (quotes && !io_ch(arg, "\"", 1))
+        return -1;
+    if (do_buf(str->data, str->length, type, flags, NULL, io_ch, arg) < 0)
+        return -1;
+    if (quotes && !io_ch(arg, "\"", 1))
+        return -1;
+    return outlen;
+}
+
+/* Used for line indenting: print 'indent' spaces */
+
+static int do_indent(char_io *io_ch, void *arg, int indent)
+{
+    int i;
+    for (i = 0; i < indent; i++)
+        if (!io_ch(arg, " ", 1))
+            return 0;
+    return 1;
+}
+
+#define FN_WIDTH_LN     25
+#define FN_WIDTH_SN     10
+
+static int do_name_ex(char_io *io_ch, void *arg, const X509_NAME *n,
+                      int indent, unsigned long flags)
+{
+    int i, prev = -1, orflags, cnt;
+    int fn_opt, fn_nid;
+    ASN1_OBJECT *fn;
+    const ASN1_STRING *val;
+    const X509_NAME_ENTRY *ent;
+    char objtmp[80];
+    const char *objbuf;
+    int outlen, len;
+    char *sep_dn, *sep_mv, *sep_eq;
+    int sep_dn_len, sep_mv_len, sep_eq_len;
+    if (indent < 0)
+        indent = 0;
+    outlen = indent;
+    if (!do_indent(io_ch, arg, indent))
+        return -1;
+    switch (flags & XN_FLAG_SEP_MASK) {
+    case XN_FLAG_SEP_MULTILINE:
+        sep_dn = "\n";
+        sep_dn_len = 1;
+        sep_mv = " + ";
+        sep_mv_len = 3;
+        break;
+
+    case XN_FLAG_SEP_COMMA_PLUS:
+        sep_dn = ",";
+        sep_dn_len = 1;
+        sep_mv = "+";
+        sep_mv_len = 1;
+        indent = 0;
+        break;
+
+    case XN_FLAG_SEP_CPLUS_SPC:
+        sep_dn = ", ";
+        sep_dn_len = 2;
+        sep_mv = " + ";
+        sep_mv_len = 3;
+        indent = 0;
+        break;
+
+    case XN_FLAG_SEP_SPLUS_SPC:
+        sep_dn = "; ";
+        sep_dn_len = 2;
+        sep_mv = " + ";
+        sep_mv_len = 3;
+        indent = 0;
+        break;
+
+    default:
+        return -1;
+    }
+
+    if (flags & XN_FLAG_SPC_EQ) {
+        sep_eq = " = ";
+        sep_eq_len = 3;
+    } else {
+        sep_eq = "=";
+        sep_eq_len = 1;
+    }
+
+    fn_opt = flags & XN_FLAG_FN_MASK;
+
+    cnt = X509_NAME_entry_count(n);
+    for (i = 0; i < cnt; i++) {
+        if (flags & XN_FLAG_DN_REV)
+            ent = X509_NAME_get_entry(n, cnt - i - 1);
+        else
+            ent = X509_NAME_get_entry(n, i);
+        if (prev != -1) {
+            if (prev == X509_NAME_ENTRY_set(ent)) {
+                if (!io_ch(arg, sep_mv, sep_mv_len))
+                    return -1;
+                outlen += sep_mv_len;
+            } else {
+                if (!io_ch(arg, sep_dn, sep_dn_len))
+                    return -1;
+                outlen += sep_dn_len;
+                if (!do_indent(io_ch, arg, indent))
+                    return -1;
+                outlen += indent;
+            }
+        }
+        prev = X509_NAME_ENTRY_set(ent);
+        fn = X509_NAME_ENTRY_get_object(ent);
+        val = X509_NAME_ENTRY_get_data(ent);
+        fn_nid = OBJ_obj2nid(fn);
+        if (fn_opt != XN_FLAG_FN_NONE) {
+            int objlen, fld_len;
+            if ((fn_opt == XN_FLAG_FN_OID) || (fn_nid == NID_undef)) {
+                OBJ_obj2txt(objtmp, sizeof objtmp, fn, 1);
+                fld_len = 0;    /* XXX: what should this be? */
+                objbuf = objtmp;
+            } else {
+                if (fn_opt == XN_FLAG_FN_SN) {
+                    fld_len = FN_WIDTH_SN;
+                    objbuf = OBJ_nid2sn(fn_nid);
+                } else if (fn_opt == XN_FLAG_FN_LN) {
+                    fld_len = FN_WIDTH_LN;
+                    objbuf = OBJ_nid2ln(fn_nid);
+                } else {
+                    fld_len = 0; /* XXX: what should this be? */
+                    objbuf = "";
+                }
+            }
+            objlen = strlen(objbuf);
+            if (!io_ch(arg, objbuf, objlen))
+                return -1;
+            if ((objlen < fld_len) && (flags & XN_FLAG_FN_ALIGN)) {
+                if (!do_indent(io_ch, arg, fld_len - objlen))
+                    return -1;
+                outlen += fld_len - objlen;
+            }
+            if (!io_ch(arg, sep_eq, sep_eq_len))
+                return -1;
+            outlen += objlen + sep_eq_len;
+        }
+        /*
+         * If the field name is unknown then fix up the DER dump flag. We
+         * might want to limit this further so it will DER dump on anything
+         * other than a few 'standard' fields.
+         */
+        if ((fn_nid == NID_undef) && (flags & XN_FLAG_DUMP_UNKNOWN_FIELDS))
+            orflags = ASN1_STRFLGS_DUMP_ALL;
+        else
+            orflags = 0;
+
+        len = do_print_ex(io_ch, arg, flags | orflags, val);
+        if (len < 0)
+            return -1;
+        outlen += len;
+    }
+    return outlen;
+}
+
+/* Wrappers round the main functions */
+
+int X509_NAME_print_ex(BIO *out, const X509_NAME *nm, int indent,
+                       unsigned long flags)
+{
+    if (flags == XN_FLAG_COMPAT)
+        return X509_NAME_print(out, nm, indent);
+    return do_name_ex(send_bio_chars, out, nm, indent, flags);
+}
+
+#ifndef OPENSSL_NO_STDIO
+int X509_NAME_print_ex_fp(FILE *fp, const X509_NAME *nm, int indent,
+                          unsigned long flags)
+{
+    if (flags == XN_FLAG_COMPAT) {
+        BIO *btmp;
+        int ret;
+        btmp = BIO_new_fp(fp, BIO_NOCLOSE);
+        if (!btmp)
+            return -1;
+        ret = X509_NAME_print(btmp, nm, indent);
+        BIO_free(btmp);
+        return ret;
+    }
+    return do_name_ex(send_fp_chars, fp, nm, indent, flags);
+}
+#endif
+
+int ASN1_STRING_print_ex(BIO *out, const ASN1_STRING *str, unsigned long flags)
+{
+    return do_print_ex(send_bio_chars, out, flags, str);
+}
+
+#ifndef OPENSSL_NO_STDIO
+int ASN1_STRING_print_ex_fp(FILE *fp, const ASN1_STRING *str, unsigned long flags)
+{
+    return do_print_ex(send_fp_chars, fp, flags, str);
+}
+#endif
+
+/*
+ * Utility function: convert any string type to UTF8, returns number of bytes
+ * in output string or a negative error code
+ */
+
+int ASN1_STRING_to_UTF8(unsigned char **out, const ASN1_STRING *in)
+{
+    ASN1_STRING stmp, *str = &stmp;
+    int mbflag, type, ret;
+    if (!in)
+        return -1;
+    type = in->type;
+    if ((type < 0) || (type > 30))
+        return -1;
+    mbflag = tag2nbyte[type];
+    if (mbflag == -1)
+        return -1;
+    mbflag |= MBSTRING_FLAG;
+    stmp.data = NULL;
+    stmp.length = 0;
+    stmp.flags = 0;
+    ret =
+        ASN1_mbstring_copy(&str, in->data, in->length, mbflag,
+                           B_ASN1_UTF8STRING);
+    if (ret < 0)
+        return ret;
+    *out = stmp.data;
+    return stmp.length;
+}
+
+/* Return 1 if host is a valid hostname and 0 otherwise */
+int asn1_valid_host(const ASN1_STRING *host)
+{
+    int hostlen = host->length;
+    const unsigned char *hostptr = host->data;
+    int type = host->type;
+    int i;
+    char width = -1;
+    unsigned short chflags = 0, prevchflags;
+
+    if (type > 0 && type < 31)
+        width = tag2nbyte[type];
+    if (width == -1 || hostlen == 0)
+        return 0;
+    /* Treat UTF8String as width 1 as any MSB set is invalid */
+    if (width == 0)
+        width = 1;
+    for (i = 0 ; i < hostlen; i+= width) {
+        prevchflags = chflags;
+        /* Value must be <= 0x7F: check upper bytes are all zeroes */
+        if (width == 4) {
+            if (*hostptr++ != 0 || *hostptr++ != 0 || *hostptr++ != 0)
+                return 0;
+        } else if (width == 2) {
+            if (*hostptr++ != 0)
+                return 0;
+        }
+        if (*hostptr > 0x7f)
+            return 0;
+        chflags = char_type[*hostptr++];
+        if (!(chflags & (CHARTYPE_HOST_ANY | CHARTYPE_HOST_WILD))) {
+            /* Nothing else allowed at start or end of string */
+            if (i == 0 || i == hostlen - 1)
+                return 0;
+            /* Otherwise invalid if not dot or hyphen */
+            if (!(chflags & (CHARTYPE_HOST_DOT | CHARTYPE_HOST_HYPHEN)))
+                return 0;
+            /*
+             * If previous is dot or hyphen then illegal unless both
+             * are hyphens: as .- -. .. are all illegal
+             */
+            if (prevchflags & (CHARTYPE_HOST_DOT | CHARTYPE_HOST_HYPHEN)
+                && ((prevchflags & CHARTYPE_HOST_DOT)
+                    || (chflags & CHARTYPE_HOST_DOT)))
+                return 0;
+        }
+    }
+    return 1;
+}
diff --git a/crypto/asn1/a_strnid.c b/crypto/asn1/a_strnid.c
new file mode 100644
index 000000000..53832c8a2
--- /dev/null
+++ b/crypto/asn1/a_strnid.c
@@ -0,0 +1,287 @@
+/*
+ * Copyright 1999-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+
+static STACK_OF(ASN1_STRING_TABLE) *stable = NULL;
+static void st_free(ASN1_STRING_TABLE *tbl);
+static int sk_table_cmp(const ASN1_STRING_TABLE *const *a,
+                        const ASN1_STRING_TABLE *const *b);
+
+/*
+ * This is the global mask for the mbstring functions: this is use to mask
+ * out certain types (such as BMPString and UTF8String) because certain
+ * software (e.g. Netscape) has problems with them.
+ */
+
+static unsigned long global_mask = B_ASN1_UTF8STRING;
+
+void ASN1_STRING_set_default_mask(unsigned long mask)
+{
+    global_mask = mask;
+}
+
+unsigned long ASN1_STRING_get_default_mask(void)
+{
+    return global_mask;
+}
+
+/*-
+ * This function sets the default to various "flavours" of configuration.
+ * based on an ASCII string. Currently this is:
+ * MASK:XXXX : a numerical mask value.
+ * nobmp : Don't use BMPStrings (just Printable, T61).
+ * pkix : PKIX recommendation in RFC2459.
+ * utf8only : only use UTF8Strings (RFC2459 recommendation for 2004).
+ * default:   the default value, Printable, T61, BMP.
+ */
+
+int ASN1_STRING_set_default_mask_asc(const char *p)
+{
+    unsigned long mask;
+    char *end;
+    if (strncmp(p, "MASK:", 5) == 0) {
+        if (!p[5])
+            return 0;
+        mask = strtoul(p + 5, &end, 0);
+        if (*end)
+            return 0;
+    } else if (strcmp(p, "nombstr") == 0)
+        mask = ~((unsigned long)(B_ASN1_BMPSTRING | B_ASN1_UTF8STRING));
+    else if (strcmp(p, "pkix") == 0)
+        mask = ~((unsigned long)B_ASN1_T61STRING);
+    else if (strcmp(p, "utf8only") == 0)
+        mask = B_ASN1_UTF8STRING;
+    else if (strcmp(p, "default") == 0)
+        mask = 0xFFFFFFFFL;
+    else
+        return 0;
+    ASN1_STRING_set_default_mask(mask);
+    return 1;
+}
+
+/*
+ * The following function generates an ASN1_STRING based on limits in a
+ * table. Frequently the types and length of an ASN1_STRING are restricted by
+ * a corresponding OID. For example certificates and certificate requests.
+ */
+
+ASN1_STRING *ASN1_STRING_set_by_NID(ASN1_STRING **out,
+                                    const unsigned char *in, int inlen,
+                                    int inform, int nid)
+{
+    ASN1_STRING_TABLE *tbl;
+    ASN1_STRING *str = NULL;
+    unsigned long mask;
+    int ret;
+    if (!out)
+        out = &str;
+    tbl = ASN1_STRING_TABLE_get(nid);
+    if (tbl) {
+        mask = tbl->mask;
+        if (!(tbl->flags & STABLE_NO_MASK))
+            mask &= global_mask;
+        ret = ASN1_mbstring_ncopy(out, in, inlen, inform, mask,
+                                  tbl->minsize, tbl->maxsize);
+    } else
+        ret =
+            ASN1_mbstring_copy(out, in, inlen, inform,
+                               DIRSTRING_TYPE & global_mask);
+    if (ret <= 0)
+        return NULL;
+    return *out;
+}
+
+/*
+ * Now the tables and helper functions for the string table:
+ */
+
+/* size limits: this stuff is taken straight from RFC3280 */
+
+#define ub_name                         32768
+#define ub_common_name                  64
+#define ub_locality_name                128
+#define ub_state_name                   128
+#define ub_organization_name            64
+#define ub_organization_unit_name       64
+#define ub_title                        64
+#define ub_email_address                128
+#define ub_serial_number                64
+
+/* From RFC4524 */
+
+#define ub_rfc822_mailbox               256
+
+/* This table must be kept in NID order */
+
+static const ASN1_STRING_TABLE tbl_standard[] = {
+    {NID_commonName, 1, ub_common_name, DIRSTRING_TYPE, 0},
+    {NID_countryName, 2, 2, B_ASN1_PRINTABLESTRING, STABLE_NO_MASK},
+    {NID_localityName, 1, ub_locality_name, DIRSTRING_TYPE, 0},
+    {NID_stateOrProvinceName, 1, ub_state_name, DIRSTRING_TYPE, 0},
+    {NID_organizationName, 1, ub_organization_name, DIRSTRING_TYPE, 0},
+    {NID_organizationalUnitName, 1, ub_organization_unit_name, DIRSTRING_TYPE,
+     0},
+    {NID_pkcs9_emailAddress, 1, ub_email_address, B_ASN1_IA5STRING,
+     STABLE_NO_MASK},
+    {NID_pkcs9_unstructuredName, 1, -1, PKCS9STRING_TYPE, 0},
+    {NID_pkcs9_challengePassword, 1, -1, PKCS9STRING_TYPE, 0},
+    {NID_pkcs9_unstructuredAddress, 1, -1, DIRSTRING_TYPE, 0},
+    {NID_givenName, 1, ub_name, DIRSTRING_TYPE, 0},
+    {NID_surname, 1, ub_name, DIRSTRING_TYPE, 0},
+    {NID_initials, 1, ub_name, DIRSTRING_TYPE, 0},
+    {NID_serialNumber, 1, ub_serial_number, B_ASN1_PRINTABLESTRING,
+     STABLE_NO_MASK},
+    {NID_friendlyName, -1, -1, B_ASN1_BMPSTRING, STABLE_NO_MASK},
+    {NID_name, 1, ub_name, DIRSTRING_TYPE, 0},
+    {NID_dnQualifier, -1, -1, B_ASN1_PRINTABLESTRING, STABLE_NO_MASK},
+    {NID_domainComponent, 1, -1, B_ASN1_IA5STRING, STABLE_NO_MASK},
+    {NID_ms_csp_name, -1, -1, B_ASN1_BMPSTRING, STABLE_NO_MASK},
+    {NID_rfc822Mailbox, 1, ub_rfc822_mailbox, B_ASN1_IA5STRING,
+     STABLE_NO_MASK},
+    {NID_INN, 1, 12, B_ASN1_NUMERICSTRING, STABLE_NO_MASK},
+    {NID_OGRN, 1, 13, B_ASN1_NUMERICSTRING, STABLE_NO_MASK},
+    {NID_SNILS, 1, 11, B_ASN1_NUMERICSTRING, STABLE_NO_MASK}
+};
+
+static int sk_table_cmp(const ASN1_STRING_TABLE *const *a,
+                        const ASN1_STRING_TABLE *const *b)
+{
+    return (*a)->nid - (*b)->nid;
+}
+
+DECLARE_OBJ_BSEARCH_CMP_FN(ASN1_STRING_TABLE, ASN1_STRING_TABLE, table);
+
+static int table_cmp(const ASN1_STRING_TABLE *a, const ASN1_STRING_TABLE *b)
+{
+    return a->nid - b->nid;
+}
+
+IMPLEMENT_OBJ_BSEARCH_CMP_FN(ASN1_STRING_TABLE, ASN1_STRING_TABLE, table);
+
+ASN1_STRING_TABLE *ASN1_STRING_TABLE_get(int nid)
+{
+    int idx;
+    ASN1_STRING_TABLE fnd;
+    fnd.nid = nid;
+    if (stable) {
+        idx = sk_ASN1_STRING_TABLE_find(stable, &fnd);
+        if (idx >= 0)
+            return sk_ASN1_STRING_TABLE_value(stable, idx);
+    }
+    return OBJ_bsearch_table(&fnd, tbl_standard, OSSL_NELEM(tbl_standard));
+}
+
+/*
+ * Return a string table pointer which can be modified: either directly from
+ * table or a copy of an internal value added to the table.
+ */
+
+static ASN1_STRING_TABLE *stable_get(int nid)
+{
+    ASN1_STRING_TABLE *tmp, *rv;
+    /* Always need a string table so allocate one if NULL */
+    if (stable == NULL) {
+        stable = sk_ASN1_STRING_TABLE_new(sk_table_cmp);
+        if (stable == NULL)
+            return NULL;
+    }
+    tmp = ASN1_STRING_TABLE_get(nid);
+    if (tmp && tmp->flags & STABLE_FLAGS_MALLOC)
+        return tmp;
+    rv = OPENSSL_zalloc(sizeof(*rv));
+    if (rv == NULL)
+        return NULL;
+    if (!sk_ASN1_STRING_TABLE_push(stable, rv)) {
+        OPENSSL_free(rv);
+        return NULL;
+    }
+    if (tmp) {
+        rv->nid = tmp->nid;
+        rv->minsize = tmp->minsize;
+        rv->maxsize = tmp->maxsize;
+        rv->mask = tmp->mask;
+        rv->flags = tmp->flags | STABLE_FLAGS_MALLOC;
+    } else {
+        rv->minsize = -1;
+        rv->maxsize = -1;
+        rv->flags = STABLE_FLAGS_MALLOC;
+    }
+    return rv;
+}
+
+int ASN1_STRING_TABLE_add(int nid,
+                          long minsize, long maxsize, unsigned long mask,
+                          unsigned long flags)
+{
+    ASN1_STRING_TABLE *tmp;
+    tmp = stable_get(nid);
+    if (!tmp) {
+        ASN1err(ASN1_F_ASN1_STRING_TABLE_ADD, ERR_R_MALLOC_FAILURE);
+        return 0;
+    }
+    if (minsize >= 0)
+        tmp->minsize = minsize;
+    if (maxsize >= 0)
+        tmp->maxsize = maxsize;
+    if (mask)
+        tmp->mask = mask;
+    if (flags)
+        tmp->flags = STABLE_FLAGS_MALLOC | flags;
+    return 1;
+}
+
+void ASN1_STRING_TABLE_cleanup(void)
+{
+    STACK_OF(ASN1_STRING_TABLE) *tmp;
+    tmp = stable;
+    if (!tmp)
+        return;
+    stable = NULL;
+    sk_ASN1_STRING_TABLE_pop_free(tmp, st_free);
+}
+
+static void st_free(ASN1_STRING_TABLE *tbl)
+{
+    if (tbl->flags & STABLE_FLAGS_MALLOC)
+        OPENSSL_free(tbl);
+}
+
+
+#ifdef STRING_TABLE_TEST
+
+main()
+{
+    ASN1_STRING_TABLE *tmp;
+    int i, last_nid = -1;
+
+    for (tmp = tbl_standard, i = 0; i < OSSL_NELEM(tbl_standard); i++, tmp++) {
+        if (tmp->nid < last_nid) {
+            last_nid = 0;
+            break;
+        }
+        last_nid = tmp->nid;
+    }
+
+    if (last_nid != 0) {
+        printf("Table order OK\n");
+        exit(0);
+    }
+
+    for (tmp = tbl_standard, i = 0; i < OSSL_NELEM(tbl_standard); i++, tmp++)
+        printf("Index %d, NID %d, Name=%s\n", i, tmp->nid,
+               OBJ_nid2ln(tmp->nid));
+
+}
+
+#endif
diff --git a/crypto/asn1/a_time.c b/crypto/asn1/a_time.c
new file mode 100644
index 000000000..3f82c2bc3
--- /dev/null
+++ b/crypto/asn1/a_time.c
@@ -0,0 +1,163 @@
+/*
+ * Copyright 1999-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*-
+ * This is an implementation of the ASN1 Time structure which is:
+ *    Time ::= CHOICE {
+ *      utcTime        UTCTime,
+ *      generalTime    GeneralizedTime }
+ */
+
+#include 
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include "asn1_locl.h"
+
+IMPLEMENT_ASN1_MSTRING(ASN1_TIME, B_ASN1_TIME)
+
+IMPLEMENT_ASN1_FUNCTIONS(ASN1_TIME)
+
+ASN1_TIME *ASN1_TIME_set(ASN1_TIME *s, time_t t)
+{
+    return ASN1_TIME_adj(s, t, 0, 0);
+}
+
+ASN1_TIME *ASN1_TIME_adj(ASN1_TIME *s, time_t t,
+                         int offset_day, long offset_sec)
+{
+    struct tm *ts;
+    struct tm data;
+
+    ts = OPENSSL_gmtime(&t, &data);
+    if (ts == NULL) {
+        ASN1err(ASN1_F_ASN1_TIME_ADJ, ASN1_R_ERROR_GETTING_TIME);
+        return NULL;
+    }
+    if (offset_day || offset_sec) {
+        if (!OPENSSL_gmtime_adj(ts, offset_day, offset_sec))
+            return NULL;
+    }
+    if ((ts->tm_year >= 50) && (ts->tm_year < 150))
+        return ASN1_UTCTIME_adj(s, t, offset_day, offset_sec);
+    return ASN1_GENERALIZEDTIME_adj(s, t, offset_day, offset_sec);
+}
+
+int ASN1_TIME_check(const ASN1_TIME *t)
+{
+    if (t->type == V_ASN1_GENERALIZEDTIME)
+        return ASN1_GENERALIZEDTIME_check(t);
+    else if (t->type == V_ASN1_UTCTIME)
+        return ASN1_UTCTIME_check(t);
+    return 0;
+}
+
+/* Convert an ASN1_TIME structure to GeneralizedTime */
+ASN1_GENERALIZEDTIME *ASN1_TIME_to_generalizedtime(ASN1_TIME *t,
+                                                   ASN1_GENERALIZEDTIME **out)
+{
+    ASN1_GENERALIZEDTIME *ret;
+    char *str;
+    int newlen;
+
+    if (!ASN1_TIME_check(t))
+        return NULL;
+
+    if (out == NULL || *out == NULL) {
+        if ((ret = ASN1_GENERALIZEDTIME_new()) == NULL)
+            return NULL;
+        if (out)
+            *out = ret;
+    } else
+        ret = *out;
+
+    /* If already GeneralizedTime just copy across */
+    if (t->type == V_ASN1_GENERALIZEDTIME) {
+        if (!ASN1_STRING_set(ret, t->data, t->length))
+            return NULL;
+        return ret;
+    }
+
+    /* grow the string */
+    if (!ASN1_STRING_set(ret, NULL, t->length + 2))
+        return NULL;
+    /* ASN1_STRING_set() allocated 'len + 1' bytes. */
+    newlen = t->length + 2 + 1;
+    str = (char *)ret->data;
+    /* Work out the century and prepend */
+    if (t->data[0] >= '5')
+        OPENSSL_strlcpy(str, "19", newlen);
+    else
+        OPENSSL_strlcpy(str, "20", newlen);
+
+    OPENSSL_strlcat(str, (char *)t->data, newlen);
+
+    return ret;
+}
+
+int ASN1_TIME_set_string(ASN1_TIME *s, const char *str)
+{
+    ASN1_TIME t;
+
+    t.length = strlen(str);
+    t.data = (unsigned char *)str;
+    t.flags = 0;
+
+    t.type = V_ASN1_UTCTIME;
+
+    if (!ASN1_TIME_check(&t)) {
+        t.type = V_ASN1_GENERALIZEDTIME;
+        if (!ASN1_TIME_check(&t))
+            return 0;
+    }
+
+    if (s && !ASN1_STRING_copy((ASN1_STRING *)s, (ASN1_STRING *)&t))
+        return 0;
+
+    return 1;
+}
+
+static int asn1_time_to_tm(struct tm *tm, const ASN1_TIME *t)
+{
+    if (t == NULL) {
+        time_t now_t;
+        time(&now_t);
+        if (OPENSSL_gmtime(&now_t, tm))
+            return 1;
+        return 0;
+    }
+
+    if (t->type == V_ASN1_UTCTIME)
+        return asn1_utctime_to_tm(tm, t);
+    else if (t->type == V_ASN1_GENERALIZEDTIME)
+        return asn1_generalizedtime_to_tm(tm, t);
+
+    return 0;
+}
+
+int ASN1_TIME_diff(int *pday, int *psec,
+                   const ASN1_TIME *from, const ASN1_TIME *to)
+{
+    struct tm tm_from, tm_to;
+    if (!asn1_time_to_tm(&tm_from, from))
+        return 0;
+    if (!asn1_time_to_tm(&tm_to, to))
+        return 0;
+    return OPENSSL_gmtime_diff(pday, psec, &tm_from, &tm_to);
+}
+
+int ASN1_TIME_print(BIO *bp, const ASN1_TIME *tm)
+{
+    if (tm->type == V_ASN1_UTCTIME)
+        return ASN1_UTCTIME_print(bp, tm);
+    if (tm->type == V_ASN1_GENERALIZEDTIME)
+        return ASN1_GENERALIZEDTIME_print(bp, tm);
+    BIO_write(bp, "Bad time value", 14);
+    return (0);
+}
diff --git a/crypto/asn1/a_type.c b/crypto/asn1/a_type.c
new file mode 100644
index 000000000..df42360e7
--- /dev/null
+++ b/crypto/asn1/a_type.c
@@ -0,0 +1,134 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+#include "asn1_locl.h"
+
+int ASN1_TYPE_get(const ASN1_TYPE *a)
+{
+    if ((a->value.ptr != NULL) || (a->type == V_ASN1_NULL))
+        return (a->type);
+    else
+        return (0);
+}
+
+void ASN1_TYPE_set(ASN1_TYPE *a, int type, void *value)
+{
+    if (a->value.ptr != NULL) {
+        ASN1_TYPE **tmp_a = &a;
+        asn1_primitive_free((ASN1_VALUE **)tmp_a, NULL, 0);
+    }
+    a->type = type;
+    if (type == V_ASN1_BOOLEAN)
+        a->value.boolean = value ? 0xff : 0;
+    else
+        a->value.ptr = value;
+}
+
+int ASN1_TYPE_set1(ASN1_TYPE *a, int type, const void *value)
+{
+    if (!value || (type == V_ASN1_BOOLEAN)) {
+        void *p = (void *)value;
+        ASN1_TYPE_set(a, type, p);
+    } else if (type == V_ASN1_OBJECT) {
+        ASN1_OBJECT *odup;
+        odup = OBJ_dup(value);
+        if (!odup)
+            return 0;
+        ASN1_TYPE_set(a, type, odup);
+    } else {
+        ASN1_STRING *sdup;
+        sdup = ASN1_STRING_dup(value);
+        if (!sdup)
+            return 0;
+        ASN1_TYPE_set(a, type, sdup);
+    }
+    return 1;
+}
+
+/* Returns 0 if they are equal, != 0 otherwise. */
+int ASN1_TYPE_cmp(const ASN1_TYPE *a, const ASN1_TYPE *b)
+{
+    int result = -1;
+
+    if (!a || !b || a->type != b->type)
+        return -1;
+
+    switch (a->type) {
+    case V_ASN1_OBJECT:
+        result = OBJ_cmp(a->value.object, b->value.object);
+        break;
+    case V_ASN1_BOOLEAN:
+        result = a->value.boolean - b->value.boolean;
+        break;
+    case V_ASN1_NULL:
+        result = 0;             /* They do not have content. */
+        break;
+    case V_ASN1_INTEGER:
+    case V_ASN1_ENUMERATED:
+    case V_ASN1_BIT_STRING:
+    case V_ASN1_OCTET_STRING:
+    case V_ASN1_SEQUENCE:
+    case V_ASN1_SET:
+    case V_ASN1_NUMERICSTRING:
+    case V_ASN1_PRINTABLESTRING:
+    case V_ASN1_T61STRING:
+    case V_ASN1_VIDEOTEXSTRING:
+    case V_ASN1_IA5STRING:
+    case V_ASN1_UTCTIME:
+    case V_ASN1_GENERALIZEDTIME:
+    case V_ASN1_GRAPHICSTRING:
+    case V_ASN1_VISIBLESTRING:
+    case V_ASN1_GENERALSTRING:
+    case V_ASN1_UNIVERSALSTRING:
+    case V_ASN1_BMPSTRING:
+    case V_ASN1_UTF8STRING:
+    case V_ASN1_OTHER:
+    default:
+        result = ASN1_STRING_cmp((ASN1_STRING *)a->value.ptr,
+                                 (ASN1_STRING *)b->value.ptr);
+        break;
+    }
+
+    return result;
+}
+
+ASN1_TYPE *ASN1_TYPE_pack_sequence(const ASN1_ITEM *it, void *s, ASN1_TYPE **t)
+{
+    ASN1_OCTET_STRING *oct;
+    ASN1_TYPE *rt;
+
+    oct = ASN1_item_pack(s, it, NULL);
+    if (oct == NULL)
+        return NULL;
+
+    if (t && *t) {
+        rt = *t;
+    } else {
+        rt = ASN1_TYPE_new();
+        if (rt == NULL) {
+            ASN1_OCTET_STRING_free(oct);
+            return NULL;
+        }
+        if (t)
+            *t = rt;
+    }
+    ASN1_TYPE_set(rt, V_ASN1_SEQUENCE, oct);
+    return rt;
+}
+
+void *ASN1_TYPE_unpack_sequence(const ASN1_ITEM *it, const ASN1_TYPE *t)
+{
+    if (t == NULL || t->type != V_ASN1_SEQUENCE || t->value.sequence == NULL)
+        return NULL;
+    return ASN1_item_unpack(t->value.sequence, it);
+}
diff --git a/crypto/asn1/a_utctm.c b/crypto/asn1/a_utctm.c
new file mode 100644
index 000000000..7916e300e
--- /dev/null
+++ b/crypto/asn1/a_utctm.c
@@ -0,0 +1,254 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include "asn1_locl.h"
+
+int asn1_utctime_to_tm(struct tm *tm, const ASN1_UTCTIME *d)
+{
+    static const int min[8] = { 0, 1, 1, 0, 0, 0, 0, 0 };
+    static const int max[8] = { 99, 12, 31, 23, 59, 59, 12, 59 };
+    char *a;
+    int n, i, l, o;
+
+    if (d->type != V_ASN1_UTCTIME)
+        return (0);
+    l = d->length;
+    a = (char *)d->data;
+    o = 0;
+
+    if (l < 11)
+        goto err;
+    for (i = 0; i < 6; i++) {
+        if ((i == 5) && ((a[o] == 'Z') || (a[o] == '+') || (a[o] == '-'))) {
+            i++;
+            if (tm)
+                tm->tm_sec = 0;
+            break;
+        }
+        if ((a[o] < '0') || (a[o] > '9'))
+            goto err;
+        n = a[o] - '0';
+        if (++o > l)
+            goto err;
+
+        if ((a[o] < '0') || (a[o] > '9'))
+            goto err;
+        n = (n * 10) + a[o] - '0';
+        if (++o > l)
+            goto err;
+
+        if ((n < min[i]) || (n > max[i]))
+            goto err;
+        if (tm) {
+            switch (i) {
+            case 0:
+                tm->tm_year = n < 50 ? n + 100 : n;
+                break;
+            case 1:
+                tm->tm_mon = n - 1;
+                break;
+            case 2:
+                tm->tm_mday = n;
+                break;
+            case 3:
+                tm->tm_hour = n;
+                break;
+            case 4:
+                tm->tm_min = n;
+                break;
+            case 5:
+                tm->tm_sec = n;
+                break;
+            }
+        }
+    }
+    if (a[o] == 'Z')
+        o++;
+    else if ((a[o] == '+') || (a[o] == '-')) {
+        int offsign = a[o] == '-' ? -1 : 1, offset = 0;
+        o++;
+        if (o + 4 > l)
+            goto err;
+        for (i = 6; i < 8; i++) {
+            if ((a[o] < '0') || (a[o] > '9'))
+                goto err;
+            n = a[o] - '0';
+            o++;
+            if ((a[o] < '0') || (a[o] > '9'))
+                goto err;
+            n = (n * 10) + a[o] - '0';
+            if ((n < min[i]) || (n > max[i]))
+                goto err;
+            if (tm) {
+                if (i == 6)
+                    offset = n * 3600;
+                else if (i == 7)
+                    offset += n * 60;
+            }
+            o++;
+        }
+        if (offset && !OPENSSL_gmtime_adj(tm, 0, offset * offsign))
+            return 0;
+    }
+    return o == l;
+ err:
+    return 0;
+}
+
+int ASN1_UTCTIME_check(const ASN1_UTCTIME *d)
+{
+    return asn1_utctime_to_tm(NULL, d);
+}
+
+int ASN1_UTCTIME_set_string(ASN1_UTCTIME *s, const char *str)
+{
+    ASN1_UTCTIME t;
+
+    t.type = V_ASN1_UTCTIME;
+    t.length = strlen(str);
+    t.data = (unsigned char *)str;
+    if (ASN1_UTCTIME_check(&t)) {
+        if (s != NULL) {
+            if (!ASN1_STRING_set((ASN1_STRING *)s, str, t.length))
+                return 0;
+            s->type = V_ASN1_UTCTIME;
+        }
+        return (1);
+    } else
+        return (0);
+}
+
+ASN1_UTCTIME *ASN1_UTCTIME_set(ASN1_UTCTIME *s, time_t t)
+{
+    return ASN1_UTCTIME_adj(s, t, 0, 0);
+}
+
+ASN1_UTCTIME *ASN1_UTCTIME_adj(ASN1_UTCTIME *s, time_t t,
+                               int offset_day, long offset_sec)
+{
+    char *p;
+    struct tm *ts;
+    struct tm data;
+    size_t len = 20;
+    int free_s = 0;
+
+    if (s == NULL) {
+        s = ASN1_UTCTIME_new();
+        if (s == NULL)
+            goto err;
+        free_s = 1;
+    }
+
+    ts = OPENSSL_gmtime(&t, &data);
+    if (ts == NULL)
+        goto err;
+
+    if (offset_day || offset_sec) {
+        if (!OPENSSL_gmtime_adj(ts, offset_day, offset_sec))
+            goto err;
+    }
+
+    if ((ts->tm_year < 50) || (ts->tm_year >= 150))
+        goto err;
+
+    p = (char *)s->data;
+    if ((p == NULL) || ((size_t)s->length < len)) {
+        p = OPENSSL_malloc(len);
+        if (p == NULL) {
+            ASN1err(ASN1_F_ASN1_UTCTIME_ADJ, ERR_R_MALLOC_FAILURE);
+            goto err;
+        }
+        OPENSSL_free(s->data);
+        s->data = (unsigned char *)p;
+    }
+
+    BIO_snprintf(p, len, "%02d%02d%02d%02d%02d%02dZ", ts->tm_year % 100,
+                 ts->tm_mon + 1, ts->tm_mday, ts->tm_hour, ts->tm_min,
+                 ts->tm_sec);
+    s->length = strlen(p);
+    s->type = V_ASN1_UTCTIME;
+#ifdef CHARSET_EBCDIC_not
+    ebcdic2ascii(s->data, s->data, s->length);
+#endif
+    return (s);
+ err:
+    if (free_s)
+        ASN1_UTCTIME_free(s);
+    return NULL;
+}
+
+int ASN1_UTCTIME_cmp_time_t(const ASN1_UTCTIME *s, time_t t)
+{
+    struct tm stm, ttm;
+    int day, sec;
+
+    if (!asn1_utctime_to_tm(&stm, s))
+        return -2;
+
+    if (!OPENSSL_gmtime(&t, &ttm))
+        return -2;
+
+    if (!OPENSSL_gmtime_diff(&day, &sec, &ttm, &stm))
+        return -2;
+
+    if (day > 0)
+        return 1;
+    if (day < 0)
+        return -1;
+    if (sec > 0)
+        return 1;
+    if (sec < 0)
+        return -1;
+    return 0;
+}
+
+int ASN1_UTCTIME_print(BIO *bp, const ASN1_UTCTIME *tm)
+{
+    const char *v;
+    int gmt = 0;
+    int i;
+    int y = 0, M = 0, d = 0, h = 0, m = 0, s = 0;
+
+    i = tm->length;
+    v = (const char *)tm->data;
+
+    if (i < 10)
+        goto err;
+    if (v[i - 1] == 'Z')
+        gmt = 1;
+    for (i = 0; i < 10; i++)
+        if ((v[i] > '9') || (v[i] < '0'))
+            goto err;
+    y = (v[0] - '0') * 10 + (v[1] - '0');
+    if (y < 50)
+        y += 100;
+    M = (v[2] - '0') * 10 + (v[3] - '0');
+    if ((M > 12) || (M < 1))
+        goto err;
+    d = (v[4] - '0') * 10 + (v[5] - '0');
+    h = (v[6] - '0') * 10 + (v[7] - '0');
+    m = (v[8] - '0') * 10 + (v[9] - '0');
+    if (tm->length >= 12 &&
+        (v[10] >= '0') && (v[10] <= '9') && (v[11] >= '0') && (v[11] <= '9'))
+        s = (v[10] - '0') * 10 + (v[11] - '0');
+
+    if (BIO_printf(bp, "%s %2d %02d:%02d:%02d %d%s",
+                   _asn1_mon[M - 1], d, h, m, s, y + 1900,
+                   (gmt) ? " GMT" : "") <= 0)
+        return (0);
+    else
+        return (1);
+ err:
+    BIO_write(bp, "Bad time value", 14);
+    return (0);
+}
diff --git a/crypto/asn1/a_utf8.c b/crypto/asn1/a_utf8.c
new file mode 100644
index 000000000..e2dc09f6a
--- /dev/null
+++ b/crypto/asn1/a_utf8.c
@@ -0,0 +1,188 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+
+/* UTF8 utilities */
+
+/*-
+ * This parses a UTF8 string one character at a time. It is passed a pointer
+ * to the string and the length of the string. It sets 'value' to the value of
+ * the current character. It returns the number of characters read or a
+ * negative error code:
+ * -1 = string too short
+ * -2 = illegal character
+ * -3 = subsequent characters not of the form 10xxxxxx
+ * -4 = character encoded incorrectly (not minimal length).
+ */
+
+int UTF8_getc(const unsigned char *str, int len, unsigned long *val)
+{
+    const unsigned char *p;
+    unsigned long value;
+    int ret;
+    if (len <= 0)
+        return 0;
+    p = str;
+
+    /* Check syntax and work out the encoded value (if correct) */
+    if ((*p & 0x80) == 0) {
+        value = *p++ & 0x7f;
+        ret = 1;
+    } else if ((*p & 0xe0) == 0xc0) {
+        if (len < 2)
+            return -1;
+        if ((p[1] & 0xc0) != 0x80)
+            return -3;
+        value = (*p++ & 0x1f) << 6;
+        value |= *p++ & 0x3f;
+        if (value < 0x80)
+            return -4;
+        ret = 2;
+    } else if ((*p & 0xf0) == 0xe0) {
+        if (len < 3)
+            return -1;
+        if (((p[1] & 0xc0) != 0x80)
+            || ((p[2] & 0xc0) != 0x80))
+            return -3;
+        value = (*p++ & 0xf) << 12;
+        value |= (*p++ & 0x3f) << 6;
+        value |= *p++ & 0x3f;
+        if (value < 0x800)
+            return -4;
+        ret = 3;
+    } else if ((*p & 0xf8) == 0xf0) {
+        if (len < 4)
+            return -1;
+        if (((p[1] & 0xc0) != 0x80)
+            || ((p[2] & 0xc0) != 0x80)
+            || ((p[3] & 0xc0) != 0x80))
+            return -3;
+        value = ((unsigned long)(*p++ & 0x7)) << 18;
+        value |= (*p++ & 0x3f) << 12;
+        value |= (*p++ & 0x3f) << 6;
+        value |= *p++ & 0x3f;
+        if (value < 0x10000)
+            return -4;
+        ret = 4;
+    } else if ((*p & 0xfc) == 0xf8) {
+        if (len < 5)
+            return -1;
+        if (((p[1] & 0xc0) != 0x80)
+            || ((p[2] & 0xc0) != 0x80)
+            || ((p[3] & 0xc0) != 0x80)
+            || ((p[4] & 0xc0) != 0x80))
+            return -3;
+        value = ((unsigned long)(*p++ & 0x3)) << 24;
+        value |= ((unsigned long)(*p++ & 0x3f)) << 18;
+        value |= ((unsigned long)(*p++ & 0x3f)) << 12;
+        value |= (*p++ & 0x3f) << 6;
+        value |= *p++ & 0x3f;
+        if (value < 0x200000)
+            return -4;
+        ret = 5;
+    } else if ((*p & 0xfe) == 0xfc) {
+        if (len < 6)
+            return -1;
+        if (((p[1] & 0xc0) != 0x80)
+            || ((p[2] & 0xc0) != 0x80)
+            || ((p[3] & 0xc0) != 0x80)
+            || ((p[4] & 0xc0) != 0x80)
+            || ((p[5] & 0xc0) != 0x80))
+            return -3;
+        value = ((unsigned long)(*p++ & 0x1)) << 30;
+        value |= ((unsigned long)(*p++ & 0x3f)) << 24;
+        value |= ((unsigned long)(*p++ & 0x3f)) << 18;
+        value |= ((unsigned long)(*p++ & 0x3f)) << 12;
+        value |= (*p++ & 0x3f) << 6;
+        value |= *p++ & 0x3f;
+        if (value < 0x4000000)
+            return -4;
+        ret = 6;
+    } else
+        return -2;
+    *val = value;
+    return ret;
+}
+
+/*
+ * This takes a character 'value' and writes the UTF8 encoded value in 'str'
+ * where 'str' is a buffer containing 'len' characters. Returns the number of
+ * characters written or -1 if 'len' is too small. 'str' can be set to NULL
+ * in which case it just returns the number of characters. It will need at
+ * most 6 characters.
+ */
+
+int UTF8_putc(unsigned char *str, int len, unsigned long value)
+{
+    if (!str)
+        len = 6;                /* Maximum we will need */
+    else if (len <= 0)
+        return -1;
+    if (value < 0x80) {
+        if (str)
+            *str = (unsigned char)value;
+        return 1;
+    }
+    if (value < 0x800) {
+        if (len < 2)
+            return -1;
+        if (str) {
+            *str++ = (unsigned char)(((value >> 6) & 0x1f) | 0xc0);
+            *str = (unsigned char)((value & 0x3f) | 0x80);
+        }
+        return 2;
+    }
+    if (value < 0x10000) {
+        if (len < 3)
+            return -1;
+        if (str) {
+            *str++ = (unsigned char)(((value >> 12) & 0xf) | 0xe0);
+            *str++ = (unsigned char)(((value >> 6) & 0x3f) | 0x80);
+            *str = (unsigned char)((value & 0x3f) | 0x80);
+        }
+        return 3;
+    }
+    if (value < 0x200000) {
+        if (len < 4)
+            return -1;
+        if (str) {
+            *str++ = (unsigned char)(((value >> 18) & 0x7) | 0xf0);
+            *str++ = (unsigned char)(((value >> 12) & 0x3f) | 0x80);
+            *str++ = (unsigned char)(((value >> 6) & 0x3f) | 0x80);
+            *str = (unsigned char)((value & 0x3f) | 0x80);
+        }
+        return 4;
+    }
+    if (value < 0x4000000) {
+        if (len < 5)
+            return -1;
+        if (str) {
+            *str++ = (unsigned char)(((value >> 24) & 0x3) | 0xf8);
+            *str++ = (unsigned char)(((value >> 18) & 0x3f) | 0x80);
+            *str++ = (unsigned char)(((value >> 12) & 0x3f) | 0x80);
+            *str++ = (unsigned char)(((value >> 6) & 0x3f) | 0x80);
+            *str = (unsigned char)((value & 0x3f) | 0x80);
+        }
+        return 5;
+    }
+    if (len < 6)
+        return -1;
+    if (str) {
+        *str++ = (unsigned char)(((value >> 30) & 0x1) | 0xfc);
+        *str++ = (unsigned char)(((value >> 24) & 0x3f) | 0x80);
+        *str++ = (unsigned char)(((value >> 18) & 0x3f) | 0x80);
+        *str++ = (unsigned char)(((value >> 12) & 0x3f) | 0x80);
+        *str++ = (unsigned char)(((value >> 6) & 0x3f) | 0x80);
+        *str = (unsigned char)((value & 0x3f) | 0x80);
+    }
+    return 6;
+}
diff --git a/crypto/asn1/a_verify.c b/crypto/asn1/a_verify.c
new file mode 100644
index 000000000..e00030b05
--- /dev/null
+++ b/crypto/asn1/a_verify.c
@@ -0,0 +1,196 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+
+#include "internal/cryptlib.h"
+
+#ifndef NO_SYS_TYPES_H
+# include 
+#endif
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#ifndef OPENSSL_NO_SM2
+#include 
+#endif
+#include "internal/asn1_int.h"
+#include "internal/evp_int.h"
+
+
+#ifndef NO_ASN1_OLD
+
+int ASN1_verify(i2d_of_void *i2d, X509_ALGOR *a, ASN1_BIT_STRING *signature,
+                char *data, EVP_PKEY *pkey)
+{
+    EVP_MD_CTX *ctx = EVP_MD_CTX_new();
+    const EVP_MD *type;
+    unsigned char *p, *buf_in = NULL;
+    int ret = -1, i, inl;
+
+    if (ctx == NULL) {
+        ASN1err(ASN1_F_ASN1_VERIFY, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+    i = OBJ_obj2nid(a->algorithm);
+    type = EVP_get_digestbyname(OBJ_nid2sn(i));
+    if (type == NULL) {
+        ASN1err(ASN1_F_ASN1_VERIFY, ASN1_R_UNKNOWN_MESSAGE_DIGEST_ALGORITHM);
+        goto err;
+    }
+
+    if (signature->type == V_ASN1_BIT_STRING && signature->flags & 0x7) {
+        ASN1err(ASN1_F_ASN1_VERIFY, ASN1_R_INVALID_BIT_STRING_BITS_LEFT);
+        goto err;
+    }
+
+    inl = i2d(data, NULL);
+    buf_in = OPENSSL_malloc((unsigned int)inl);
+    if (buf_in == NULL) {
+        ASN1err(ASN1_F_ASN1_VERIFY, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+    p = buf_in;
+
+    i2d(data, &p);
+    ret = EVP_VerifyInit_ex(ctx, type, NULL)
+        && EVP_VerifyUpdate(ctx, (unsigned char *)buf_in, inl);
+
+    OPENSSL_clear_free(buf_in, (unsigned int)inl);
+
+    if (!ret) {
+        ASN1err(ASN1_F_ASN1_VERIFY, ERR_R_EVP_LIB);
+        goto err;
+    }
+    ret = -1;
+
+    if (EVP_VerifyFinal(ctx, (unsigned char *)signature->data,
+                        (unsigned int)signature->length, pkey) <= 0) {
+        ASN1err(ASN1_F_ASN1_VERIFY, ERR_R_EVP_LIB);
+        ret = 0;
+        goto err;
+    }
+    ret = 1;
+ err:
+    EVP_MD_CTX_free(ctx);
+    return (ret);
+}
+
+#endif
+
+int ASN1_item_verify(const ASN1_ITEM *it, X509_ALGOR *a,
+                     ASN1_BIT_STRING *signature, void *asn, EVP_PKEY *pkey)
+{
+    EVP_MD_CTX *ctx = NULL;
+    unsigned char *buf_in = NULL;
+    int ret = -1, inl;
+
+    int mdnid, pknid;
+
+    if (!pkey) {
+        ASN1err(ASN1_F_ASN1_ITEM_VERIFY, ERR_R_PASSED_NULL_PARAMETER);
+        return -1;
+    }
+
+    if (signature->type == V_ASN1_BIT_STRING && signature->flags & 0x7) {
+        ASN1err(ASN1_F_ASN1_ITEM_VERIFY, ASN1_R_INVALID_BIT_STRING_BITS_LEFT);
+        return -1;
+    }
+
+    ctx = EVP_MD_CTX_new();
+    if (ctx == NULL) {
+        ASN1err(ASN1_F_ASN1_ITEM_VERIFY, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+
+    /* Convert signature OID into digest and public key OIDs */
+    if (!OBJ_find_sigid_algs(OBJ_obj2nid(a->algorithm), &mdnid, &pknid)) {
+        ASN1err(ASN1_F_ASN1_ITEM_VERIFY, ASN1_R_UNKNOWN_SIGNATURE_ALGORITHM);
+        goto err;
+    }
+    if (mdnid == NID_undef) {
+        if (!pkey->ameth || !pkey->ameth->item_verify) {
+            ASN1err(ASN1_F_ASN1_ITEM_VERIFY,
+                    ASN1_R_UNKNOWN_SIGNATURE_ALGORITHM);
+            goto err;
+        }
+        ret = pkey->ameth->item_verify(ctx, it, asn, a, signature, pkey);
+        /*
+         * Return value of 2 means carry on, anything else means we exit
+         * straight away: either a fatal error of the underlying verification
+         * routine handles all verification.
+         */
+        if (ret != 2)
+            goto err;
+        ret = -1;
+    } else {
+        const EVP_MD *type;
+        EVP_PKEY_CTX *pctx;
+        type = EVP_get_digestbynid(mdnid);
+        if (type == NULL) {
+            ASN1err(ASN1_F_ASN1_ITEM_VERIFY,
+                    ASN1_R_UNKNOWN_MESSAGE_DIGEST_ALGORITHM);
+            goto err;
+        }
+
+        /* Check public key OID matches public key type */
+        if (EVP_PKEY_type(pknid) != pkey->ameth->pkey_id) {
+            ASN1err(ASN1_F_ASN1_ITEM_VERIFY, ASN1_R_WRONG_PUBLIC_KEY_TYPE);
+            goto err;
+        }
+
+        if (!EVP_DigestVerifyInit(ctx, &pctx, type, NULL, pkey)) {
+            ASN1err(ASN1_F_ASN1_ITEM_VERIFY, ERR_R_EVP_LIB);
+            ret = 0;
+            goto err;
+        }
+
+#ifndef OPENSSL_NO_SM2
+        if (OBJ_obj2nid(a->algorithm) == NID_sm2sign_with_sm3) {
+            if (!EVP_PKEY_CTX_set_ec_scheme(pctx, NID_sm_scheme)) {
+                ASN1err(ASN1_F_ASN1_ITEM_VERIFY, ERR_R_EC_LIB);
+                ret = 0;
+                goto err;
+            }
+        }
+#endif
+    }
+
+    inl = ASN1_item_i2d(asn, &buf_in, it);
+
+    if (buf_in == NULL) {
+        ASN1err(ASN1_F_ASN1_ITEM_VERIFY, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+
+    ret = EVP_DigestVerifyUpdate(ctx, buf_in, inl);
+
+    OPENSSL_clear_free(buf_in, (unsigned int)inl);
+
+    if (!ret) {
+        ASN1err(ASN1_F_ASN1_ITEM_VERIFY, ERR_R_EVP_LIB);
+        goto err;
+    }
+    ret = -1;
+
+    if (EVP_DigestVerifyFinal(ctx, signature->data,
+                              (size_t)signature->length) <= 0) {
+        ASN1err(ASN1_F_ASN1_ITEM_VERIFY, ERR_R_EVP_LIB);
+        ret = 0;
+        goto err;
+    }
+    ret = 1;
+ err:
+    EVP_MD_CTX_free(ctx);
+    return (ret);
+}
diff --git a/crypto/asn1/ameth_lib.c b/crypto/asn1/ameth_lib.c
new file mode 100644
index 000000000..7d5b29680
--- /dev/null
+++ b/crypto/asn1/ameth_lib.c
@@ -0,0 +1,409 @@
+/*
+ * Copyright 2006-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+#ifndef OPENSSL_NO_ENGINE
+# include 
+#endif
+#include "internal/asn1_int.h"
+#include "internal/evp_int.h"
+
+/* Keep this sorted in type order !! */
+static const EVP_PKEY_ASN1_METHOD *standard_methods[] = {
+#ifndef OPENSSL_NO_RSA
+    &rsa_asn1_meths[0],
+    &rsa_asn1_meths[1],
+#endif
+#ifndef OPENSSL_NO_DH
+    &dh_asn1_meth,
+#endif
+#ifndef OPENSSL_NO_DSA
+    &dsa_asn1_meths[0],
+    &dsa_asn1_meths[1],
+    &dsa_asn1_meths[2],
+    &dsa_asn1_meths[3],
+    &dsa_asn1_meths[4],
+#endif
+#ifndef OPENSSL_NO_EC
+    &eckey_asn1_meth,
+#endif
+    &hmac_asn1_meth,
+#ifndef OPENSSL_NO_CMAC
+    &cmac_asn1_meth,
+#endif
+#ifndef OPENSSL_NO_DH
+    &dhx_asn1_meth,
+#endif
+#ifndef OPENSSL_NO_EC
+    &ecx25519_asn1_meth,
+#endif
+#ifndef OPENSSL_NO_SM9
+    &sm9_asn1_meth,
+    &sm9_master_asn1_meth,
+#endif
+#ifndef OPENSSL_NO_PAILLIER
+    &paillier_asn1_meth,
+#endif
+};
+
+typedef int sk_cmp_fn_type(const char *const *a, const char *const *b);
+static STACK_OF(EVP_PKEY_ASN1_METHOD) *app_methods = NULL;
+
+#ifdef TEST
+void main()
+{
+    int i;
+    for (i = 0; i < OSSL_NELEM(standard_methods); i++)
+        fprintf(stderr, "Number %d id=%d (%s)\n", i,
+                standard_methods[i]->pkey_id,
+                OBJ_nid2sn(standard_methods[i]->pkey_id));
+}
+#endif
+
+DECLARE_OBJ_BSEARCH_CMP_FN(const EVP_PKEY_ASN1_METHOD *,
+                           const EVP_PKEY_ASN1_METHOD *, ameth);
+
+static int ameth_cmp(const EVP_PKEY_ASN1_METHOD *const *a,
+                     const EVP_PKEY_ASN1_METHOD *const *b)
+{
+    return ((*a)->pkey_id - (*b)->pkey_id);
+}
+
+IMPLEMENT_OBJ_BSEARCH_CMP_FN(const EVP_PKEY_ASN1_METHOD *,
+                             const EVP_PKEY_ASN1_METHOD *, ameth);
+
+int EVP_PKEY_asn1_get_count(void)
+{
+    int num = OSSL_NELEM(standard_methods);
+    if (app_methods)
+        num += sk_EVP_PKEY_ASN1_METHOD_num(app_methods);
+    return num;
+}
+
+const EVP_PKEY_ASN1_METHOD *EVP_PKEY_asn1_get0(int idx)
+{
+    int num = OSSL_NELEM(standard_methods);
+    if (idx < 0)
+        return NULL;
+    if (idx < num)
+        return standard_methods[idx];
+    idx -= num;
+    return sk_EVP_PKEY_ASN1_METHOD_value(app_methods, idx);
+}
+
+static const EVP_PKEY_ASN1_METHOD *pkey_asn1_find(int type)
+{
+    EVP_PKEY_ASN1_METHOD tmp;
+    const EVP_PKEY_ASN1_METHOD *t = &tmp, **ret;
+    tmp.pkey_id = type;
+    if (app_methods) {
+        int idx;
+        idx = sk_EVP_PKEY_ASN1_METHOD_find(app_methods, &tmp);
+        if (idx >= 0)
+            return sk_EVP_PKEY_ASN1_METHOD_value(app_methods, idx);
+    }
+    ret = OBJ_bsearch_ameth(&t, standard_methods, OSSL_NELEM(standard_methods));
+    if (!ret || !*ret)
+        return NULL;
+    return *ret;
+}
+
+/*
+ * Find an implementation of an ASN1 algorithm. If 'pe' is not NULL also
+ * search through engines and set *pe to a functional reference to the engine
+ * implementing 'type' or NULL if no engine implements it.
+ */
+
+const EVP_PKEY_ASN1_METHOD *EVP_PKEY_asn1_find(ENGINE **pe, int type)
+{
+    const EVP_PKEY_ASN1_METHOD *t;
+
+    for (;;) {
+        t = pkey_asn1_find(type);
+        if (!t || !(t->pkey_flags & ASN1_PKEY_ALIAS))
+            break;
+        type = t->pkey_base_id;
+    }
+    if (pe) {
+#ifndef OPENSSL_NO_ENGINE
+        ENGINE *e;
+        /* type will contain the final unaliased type */
+        e = ENGINE_get_pkey_asn1_meth_engine(type);
+        if (e) {
+            *pe = e;
+            return ENGINE_get_pkey_asn1_meth(e, type);
+        }
+#endif
+        *pe = NULL;
+    }
+    return t;
+}
+
+const EVP_PKEY_ASN1_METHOD *EVP_PKEY_asn1_find_str(ENGINE **pe,
+                                                   const char *str, int len)
+{
+    int i;
+    const EVP_PKEY_ASN1_METHOD *ameth;
+    if (len == -1)
+        len = strlen(str);
+    if (pe) {
+#ifndef OPENSSL_NO_ENGINE
+        ENGINE *e;
+        ameth = ENGINE_pkey_asn1_find_str(&e, str, len);
+        if (ameth) {
+            /*
+             * Convert structural into functional reference
+             */
+            if (!ENGINE_init(e))
+                ameth = NULL;
+            ENGINE_free(e);
+            *pe = e;
+            return ameth;
+        }
+#endif
+        *pe = NULL;
+    }
+    for (i = 0; i < EVP_PKEY_asn1_get_count(); i++) {
+        ameth = EVP_PKEY_asn1_get0(i);
+        if (ameth->pkey_flags & ASN1_PKEY_ALIAS)
+            continue;
+        if (((int)strlen(ameth->pem_str) == len)
+            && (strncasecmp(ameth->pem_str, str, len) == 0))
+            return ameth;
+    }
+    return NULL;
+}
+
+int EVP_PKEY_asn1_add0(const EVP_PKEY_ASN1_METHOD *ameth)
+{
+    if (app_methods == NULL) {
+        app_methods = sk_EVP_PKEY_ASN1_METHOD_new(ameth_cmp);
+        if (app_methods == NULL)
+            return 0;
+    }
+    if (!sk_EVP_PKEY_ASN1_METHOD_push(app_methods, ameth))
+        return 0;
+    sk_EVP_PKEY_ASN1_METHOD_sort(app_methods);
+    return 1;
+}
+
+int EVP_PKEY_asn1_add_alias(int to, int from)
+{
+    EVP_PKEY_ASN1_METHOD *ameth;
+    ameth = EVP_PKEY_asn1_new(from, ASN1_PKEY_ALIAS, NULL, NULL);
+    if (ameth == NULL)
+        return 0;
+    ameth->pkey_base_id = to;
+    if (!EVP_PKEY_asn1_add0(ameth)) {
+        EVP_PKEY_asn1_free(ameth);
+        return 0;
+    }
+    return 1;
+}
+
+int EVP_PKEY_asn1_get0_info(int *ppkey_id, int *ppkey_base_id,
+                            int *ppkey_flags, const char **pinfo,
+                            const char **ppem_str,
+                            const EVP_PKEY_ASN1_METHOD *ameth)
+{
+    if (!ameth)
+        return 0;
+    if (ppkey_id)
+        *ppkey_id = ameth->pkey_id;
+    if (ppkey_base_id)
+        *ppkey_base_id = ameth->pkey_base_id;
+    if (ppkey_flags)
+        *ppkey_flags = ameth->pkey_flags;
+    if (pinfo)
+        *pinfo = ameth->info;
+    if (ppem_str)
+        *ppem_str = ameth->pem_str;
+    return 1;
+}
+
+const EVP_PKEY_ASN1_METHOD *EVP_PKEY_get0_asn1(const EVP_PKEY *pkey)
+{
+    return pkey->ameth;
+}
+
+EVP_PKEY_ASN1_METHOD *EVP_PKEY_asn1_new(int id, int flags,
+                                        const char *pem_str, const char *info)
+{
+    EVP_PKEY_ASN1_METHOD *ameth = OPENSSL_zalloc(sizeof(*ameth));
+
+    if (ameth == NULL)
+        return NULL;
+
+    ameth->pkey_id = id;
+    ameth->pkey_base_id = id;
+    ameth->pkey_flags = flags | ASN1_PKEY_DYNAMIC;
+
+    if (info) {
+        ameth->info = OPENSSL_strdup(info);
+        if (!ameth->info)
+            goto err;
+    }
+
+    if (pem_str) {
+        ameth->pem_str = OPENSSL_strdup(pem_str);
+        if (!ameth->pem_str)
+            goto err;
+    }
+
+    return ameth;
+
+ err:
+    EVP_PKEY_asn1_free(ameth);
+    return NULL;
+
+}
+
+void EVP_PKEY_asn1_copy(EVP_PKEY_ASN1_METHOD *dst,
+                        const EVP_PKEY_ASN1_METHOD *src)
+{
+
+    dst->pub_decode = src->pub_decode;
+    dst->pub_encode = src->pub_encode;
+    dst->pub_cmp = src->pub_cmp;
+    dst->pub_print = src->pub_print;
+
+    dst->priv_decode = src->priv_decode;
+    dst->priv_encode = src->priv_encode;
+    dst->priv_print = src->priv_print;
+
+    dst->old_priv_encode = src->old_priv_encode;
+    dst->old_priv_decode = src->old_priv_decode;
+
+    dst->pkey_size = src->pkey_size;
+    dst->pkey_bits = src->pkey_bits;
+
+    dst->param_decode = src->param_decode;
+    dst->param_encode = src->param_encode;
+    dst->param_missing = src->param_missing;
+    dst->param_copy = src->param_copy;
+    dst->param_cmp = src->param_cmp;
+    dst->param_print = src->param_print;
+
+    dst->pkey_free = src->pkey_free;
+    dst->pkey_ctrl = src->pkey_ctrl;
+
+    dst->item_sign = src->item_sign;
+    dst->item_verify = src->item_verify;
+
+}
+
+void EVP_PKEY_asn1_free(EVP_PKEY_ASN1_METHOD *ameth)
+{
+    if (ameth && (ameth->pkey_flags & ASN1_PKEY_DYNAMIC)) {
+        OPENSSL_free(ameth->pem_str);
+        OPENSSL_free(ameth->info);
+        OPENSSL_free(ameth);
+    }
+}
+
+void EVP_PKEY_asn1_set_public(EVP_PKEY_ASN1_METHOD *ameth,
+                              int (*pub_decode) (EVP_PKEY *pk,
+                                                 X509_PUBKEY *pub),
+                              int (*pub_encode) (X509_PUBKEY *pub,
+                                                 const EVP_PKEY *pk),
+                              int (*pub_cmp) (const EVP_PKEY *a,
+                                              const EVP_PKEY *b),
+                              int (*pub_print) (BIO *out,
+                                                const EVP_PKEY *pkey,
+                                                int indent, ASN1_PCTX *pctx),
+                              int (*pkey_size) (const EVP_PKEY *pk),
+                              int (*pkey_bits) (const EVP_PKEY *pk))
+{
+    ameth->pub_decode = pub_decode;
+    ameth->pub_encode = pub_encode;
+    ameth->pub_cmp = pub_cmp;
+    ameth->pub_print = pub_print;
+    ameth->pkey_size = pkey_size;
+    ameth->pkey_bits = pkey_bits;
+}
+
+void EVP_PKEY_asn1_set_private(EVP_PKEY_ASN1_METHOD *ameth,
+                               int (*priv_decode) (EVP_PKEY *pk,
+                                                   const PKCS8_PRIV_KEY_INFO
+                                                   *p8inf),
+                               int (*priv_encode) (PKCS8_PRIV_KEY_INFO *p8,
+                                                   const EVP_PKEY *pk),
+                               int (*priv_print) (BIO *out,
+                                                  const EVP_PKEY *pkey,
+                                                  int indent,
+                                                  ASN1_PCTX *pctx))
+{
+    ameth->priv_decode = priv_decode;
+    ameth->priv_encode = priv_encode;
+    ameth->priv_print = priv_print;
+}
+
+void EVP_PKEY_asn1_set_param(EVP_PKEY_ASN1_METHOD *ameth,
+                             int (*param_decode) (EVP_PKEY *pkey,
+                                                  const unsigned char **pder,
+                                                  int derlen),
+                             int (*param_encode) (const EVP_PKEY *pkey,
+                                                  unsigned char **pder),
+                             int (*param_missing) (const EVP_PKEY *pk),
+                             int (*param_copy) (EVP_PKEY *to,
+                                                const EVP_PKEY *from),
+                             int (*param_cmp) (const EVP_PKEY *a,
+                                               const EVP_PKEY *b),
+                             int (*param_print) (BIO *out,
+                                                 const EVP_PKEY *pkey,
+                                                 int indent, ASN1_PCTX *pctx))
+{
+    ameth->param_decode = param_decode;
+    ameth->param_encode = param_encode;
+    ameth->param_missing = param_missing;
+    ameth->param_copy = param_copy;
+    ameth->param_cmp = param_cmp;
+    ameth->param_print = param_print;
+}
+
+void EVP_PKEY_asn1_set_free(EVP_PKEY_ASN1_METHOD *ameth,
+                            void (*pkey_free) (EVP_PKEY *pkey))
+{
+    ameth->pkey_free = pkey_free;
+}
+
+void EVP_PKEY_asn1_set_ctrl(EVP_PKEY_ASN1_METHOD *ameth,
+                            int (*pkey_ctrl) (EVP_PKEY *pkey, int op,
+                                              long arg1, void *arg2))
+{
+    ameth->pkey_ctrl = pkey_ctrl;
+}
+
+void EVP_PKEY_asn1_set_security_bits(EVP_PKEY_ASN1_METHOD *ameth,
+                                     int (*pkey_security_bits) (const EVP_PKEY
+                                                                *pk))
+{
+    ameth->pkey_security_bits = pkey_security_bits;
+}
+
+void EVP_PKEY_asn1_set_item(EVP_PKEY_ASN1_METHOD *ameth,
+                            int (*item_verify) (EVP_MD_CTX *ctx,
+                                                const ASN1_ITEM *it,
+                                                void *asn,
+                                                X509_ALGOR *a,
+                                                ASN1_BIT_STRING *sig,
+                                                EVP_PKEY *pkey),
+                            int (*item_sign) (EVP_MD_CTX *ctx,
+                                              const ASN1_ITEM *it,
+                                              void *asn,
+                                              X509_ALGOR *alg1,
+                                              X509_ALGOR *alg2,
+                                              ASN1_BIT_STRING *sig))
+{
+    ameth->item_sign = item_sign;
+    ameth->item_verify = item_verify;
+}
diff --git a/crypto/asn1/asn1_err.c b/crypto/asn1/asn1_err.c
new file mode 100644
index 000000000..fd67e6ddf
--- /dev/null
+++ b/crypto/asn1/asn1_err.c
@@ -0,0 +1,270 @@
+/*
+ * Generated by util/mkerr.pl DO NOT EDIT
+ * Copyright 1995-2018 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include 
+
+/* BEGIN ERROR CODES */
+#ifndef OPENSSL_NO_ERR
+
+# define ERR_FUNC(func) ERR_PACK(ERR_LIB_ASN1,func,0)
+# define ERR_REASON(reason) ERR_PACK(ERR_LIB_ASN1,0,reason)
+
+static ERR_STRING_DATA ASN1_str_functs[] = {
+    {ERR_FUNC(ASN1_F_A2D_ASN1_OBJECT), "a2d_ASN1_OBJECT"},
+    {ERR_FUNC(ASN1_F_A2I_ASN1_INTEGER), "a2i_ASN1_INTEGER"},
+    {ERR_FUNC(ASN1_F_A2I_ASN1_STRING), "a2i_ASN1_STRING"},
+    {ERR_FUNC(ASN1_F_APPEND_EXP), "append_exp"},
+    {ERR_FUNC(ASN1_F_ASN1_BIT_STRING_SET_BIT), "ASN1_BIT_STRING_set_bit"},
+    {ERR_FUNC(ASN1_F_ASN1_CB), "asn1_cb"},
+    {ERR_FUNC(ASN1_F_ASN1_CHECK_TLEN), "asn1_check_tlen"},
+    {ERR_FUNC(ASN1_F_ASN1_COLLECT), "asn1_collect"},
+    {ERR_FUNC(ASN1_F_ASN1_D2I_EX_PRIMITIVE), "asn1_d2i_ex_primitive"},
+    {ERR_FUNC(ASN1_F_ASN1_D2I_FP), "ASN1_d2i_fp"},
+    {ERR_FUNC(ASN1_F_ASN1_D2I_READ_BIO), "asn1_d2i_read_bio"},
+    {ERR_FUNC(ASN1_F_ASN1_DIGEST), "ASN1_digest"},
+    {ERR_FUNC(ASN1_F_ASN1_DO_ADB), "asn1_do_adb"},
+    {ERR_FUNC(ASN1_F_ASN1_DO_LOCK), "asn1_do_lock"},
+    {ERR_FUNC(ASN1_F_ASN1_DUP), "ASN1_dup"},
+    {ERR_FUNC(ASN1_F_ASN1_EX_C2I), "asn1_ex_c2i"},
+    {ERR_FUNC(ASN1_F_ASN1_FIND_END), "asn1_find_end"},
+    {ERR_FUNC(ASN1_F_ASN1_GENERALIZEDTIME_ADJ), "ASN1_GENERALIZEDTIME_adj"},
+    {ERR_FUNC(ASN1_F_ASN1_GENERATE_V3), "ASN1_generate_v3"},
+    {ERR_FUNC(ASN1_F_ASN1_GET_INT64), "asn1_get_int64"},
+    {ERR_FUNC(ASN1_F_ASN1_GET_OBJECT), "ASN1_get_object"},
+    {ERR_FUNC(ASN1_F_ASN1_GET_UINT64), "asn1_get_uint64"},
+    {ERR_FUNC(ASN1_F_ASN1_I2D_BIO), "ASN1_i2d_bio"},
+    {ERR_FUNC(ASN1_F_ASN1_I2D_FP), "ASN1_i2d_fp"},
+    {ERR_FUNC(ASN1_F_ASN1_ITEM_D2I_FP), "ASN1_item_d2i_fp"},
+    {ERR_FUNC(ASN1_F_ASN1_ITEM_DUP), "ASN1_item_dup"},
+    {ERR_FUNC(ASN1_F_ASN1_ITEM_EMBED_D2I), "asn1_item_embed_d2i"},
+    {ERR_FUNC(ASN1_F_ASN1_ITEM_EMBED_NEW), "asn1_item_embed_new"},
+    {ERR_FUNC(ASN1_F_ASN1_ITEM_I2D_BIO), "ASN1_item_i2d_bio"},
+    {ERR_FUNC(ASN1_F_ASN1_ITEM_I2D_FP), "ASN1_item_i2d_fp"},
+    {ERR_FUNC(ASN1_F_ASN1_ITEM_PACK), "ASN1_item_pack"},
+    {ERR_FUNC(ASN1_F_ASN1_ITEM_SIGN), "ASN1_item_sign"},
+    {ERR_FUNC(ASN1_F_ASN1_ITEM_SIGN_CTX), "ASN1_item_sign_ctx"},
+    {ERR_FUNC(ASN1_F_ASN1_ITEM_UNPACK), "ASN1_item_unpack"},
+    {ERR_FUNC(ASN1_F_ASN1_ITEM_VERIFY), "ASN1_item_verify"},
+    {ERR_FUNC(ASN1_F_ASN1_MBSTRING_NCOPY), "ASN1_mbstring_ncopy"},
+    {ERR_FUNC(ASN1_F_ASN1_OBJECT_NEW), "ASN1_OBJECT_new"},
+    {ERR_FUNC(ASN1_F_ASN1_OUTPUT_DATA), "asn1_output_data"},
+    {ERR_FUNC(ASN1_F_ASN1_PCTX_NEW), "ASN1_PCTX_new"},
+    {ERR_FUNC(ASN1_F_ASN1_SCTX_NEW), "ASN1_SCTX_new"},
+    {ERR_FUNC(ASN1_F_ASN1_SIGN), "ASN1_sign"},
+    {ERR_FUNC(ASN1_F_ASN1_STR2TYPE), "asn1_str2type"},
+    {ERR_FUNC(ASN1_F_ASN1_STRING_GET_INT64), "asn1_string_get_int64"},
+    {ERR_FUNC(ASN1_F_ASN1_STRING_GET_UINT64), "asn1_string_get_uint64"},
+    {ERR_FUNC(ASN1_F_ASN1_STRING_SET), "ASN1_STRING_set"},
+    {ERR_FUNC(ASN1_F_ASN1_STRING_TABLE_ADD), "ASN1_STRING_TABLE_add"},
+    {ERR_FUNC(ASN1_F_ASN1_STRING_TO_BN), "asn1_string_to_bn"},
+    {ERR_FUNC(ASN1_F_ASN1_STRING_TYPE_NEW), "ASN1_STRING_type_new"},
+    {ERR_FUNC(ASN1_F_ASN1_TEMPLATE_EX_D2I), "asn1_template_ex_d2i"},
+    {ERR_FUNC(ASN1_F_ASN1_TEMPLATE_NEW), "asn1_template_new"},
+    {ERR_FUNC(ASN1_F_ASN1_TEMPLATE_NOEXP_D2I), "asn1_template_noexp_d2i"},
+    {ERR_FUNC(ASN1_F_ASN1_TIME_ADJ), "ASN1_TIME_adj"},
+    {ERR_FUNC(ASN1_F_ASN1_TYPE_GET_INT_OCTETSTRING),
+     "ASN1_TYPE_get_int_octetstring"},
+    {ERR_FUNC(ASN1_F_ASN1_TYPE_GET_OCTETSTRING), "ASN1_TYPE_get_octetstring"},
+    {ERR_FUNC(ASN1_F_ASN1_UTCTIME_ADJ), "ASN1_UTCTIME_adj"},
+    {ERR_FUNC(ASN1_F_ASN1_VERIFY), "ASN1_verify"},
+    {ERR_FUNC(ASN1_F_B64_READ_ASN1), "b64_read_asn1"},
+    {ERR_FUNC(ASN1_F_B64_WRITE_ASN1), "B64_write_ASN1"},
+    {ERR_FUNC(ASN1_F_BIO_NEW_NDEF), "BIO_new_NDEF"},
+    {ERR_FUNC(ASN1_F_BITSTR_CB), "bitstr_cb"},
+    {ERR_FUNC(ASN1_F_BN_TO_ASN1_STRING), "bn_to_asn1_string"},
+    {ERR_FUNC(ASN1_F_C2I_ASN1_BIT_STRING), "c2i_ASN1_BIT_STRING"},
+    {ERR_FUNC(ASN1_F_C2I_ASN1_INTEGER), "c2i_ASN1_INTEGER"},
+    {ERR_FUNC(ASN1_F_C2I_ASN1_OBJECT), "c2i_ASN1_OBJECT"},
+    {ERR_FUNC(ASN1_F_C2I_IBUF), "c2i_ibuf"},
+    {ERR_FUNC(ASN1_F_COLLECT_DATA), "collect_data"},
+    {ERR_FUNC(ASN1_F_D2I_ASN1_OBJECT), "d2i_ASN1_OBJECT"},
+    {ERR_FUNC(ASN1_F_D2I_ASN1_UINTEGER), "d2i_ASN1_UINTEGER"},
+    {ERR_FUNC(ASN1_F_D2I_AUTOPRIVATEKEY), "d2i_AutoPrivateKey"},
+    {ERR_FUNC(ASN1_F_D2I_PRIVATEKEY), "d2i_PrivateKey"},
+    {ERR_FUNC(ASN1_F_D2I_PUBLICKEY), "d2i_PublicKey"},
+    {ERR_FUNC(ASN1_F_DO_TCREATE), "do_tcreate"},
+    {ERR_FUNC(ASN1_F_I2D_ASN1_BIO_STREAM), "i2d_ASN1_bio_stream"},
+    {ERR_FUNC(ASN1_F_I2D_DSA_PUBKEY), "i2d_DSA_PUBKEY"},
+    {ERR_FUNC(ASN1_F_I2D_EC_PUBKEY), "i2d_EC_PUBKEY"},
+    {ERR_FUNC(ASN1_F_I2D_PAILLIER_PUBKEY), "i2d_PAILLIER_PUBKEY"},
+    {ERR_FUNC(ASN1_F_I2D_PRIVATEKEY), "i2d_PrivateKey"},
+    {ERR_FUNC(ASN1_F_I2D_PUBLICKEY), "i2d_PublicKey"},
+    {ERR_FUNC(ASN1_F_I2D_RSA_PUBKEY), "i2d_RSA_PUBKEY"},
+    {ERR_FUNC(ASN1_F_I2D_SM9_MASTER_PUBKEY), "i2d_SM9_MASTER_PUBKEY"},
+    {ERR_FUNC(ASN1_F_I2D_SM9_PUBKEY), "i2d_SM9_PUBKEY"},
+    {ERR_FUNC(ASN1_F_LONG_C2I), "long_c2i"},
+    {ERR_FUNC(ASN1_F_OID_MODULE_INIT), "oid_module_init"},
+    {ERR_FUNC(ASN1_F_PARSE_TAGGING), "parse_tagging"},
+    {ERR_FUNC(ASN1_F_PKCS5_PBE2_SET_IV), "PKCS5_pbe2_set_iv"},
+    {ERR_FUNC(ASN1_F_PKCS5_PBE2_SET_SCRYPT), "PKCS5_pbe2_set_scrypt"},
+    {ERR_FUNC(ASN1_F_PKCS5_PBE_SET), "PKCS5_pbe_set"},
+    {ERR_FUNC(ASN1_F_PKCS5_PBE_SET0_ALGOR), "PKCS5_pbe_set0_algor"},
+    {ERR_FUNC(ASN1_F_PKCS5_PBKDF2_SET), "PKCS5_pbkdf2_set"},
+    {ERR_FUNC(ASN1_F_PKCS5_SCRYPT_SET), "pkcs5_scrypt_set"},
+    {ERR_FUNC(ASN1_F_SMIME_READ_ASN1), "SMIME_read_ASN1"},
+    {ERR_FUNC(ASN1_F_SMIME_TEXT), "SMIME_text"},
+    {ERR_FUNC(ASN1_F_STBL_MODULE_INIT), "stbl_module_init"},
+    {ERR_FUNC(ASN1_F_X509_CRL_ADD0_REVOKED), "X509_CRL_add0_revoked"},
+    {ERR_FUNC(ASN1_F_X509_INFO_NEW), "X509_INFO_new"},
+    {ERR_FUNC(ASN1_F_X509_NAME_ENCODE), "x509_name_encode"},
+    {ERR_FUNC(ASN1_F_X509_NAME_EX_D2I), "x509_name_ex_d2i"},
+    {ERR_FUNC(ASN1_F_X509_NAME_EX_NEW), "x509_name_ex_new"},
+    {ERR_FUNC(ASN1_F_X509_PKEY_NEW), "X509_PKEY_new"},
+    {0, NULL}
+};
+
+static ERR_STRING_DATA ASN1_str_reasons[] = {
+    {ERR_REASON(ASN1_R_ADDING_OBJECT), "adding object"},
+    {ERR_REASON(ASN1_R_ASN1_PARSE_ERROR), "asn1 parse error"},
+    {ERR_REASON(ASN1_R_ASN1_SIG_PARSE_ERROR), "asn1 sig parse error"},
+    {ERR_REASON(ASN1_R_AUX_ERROR), "aux error"},
+    {ERR_REASON(ASN1_R_BAD_OBJECT_HEADER), "bad object header"},
+    {ERR_REASON(ASN1_R_BMPSTRING_IS_WRONG_LENGTH),
+     "bmpstring is wrong length"},
+    {ERR_REASON(ASN1_R_BN_LIB), "bn lib"},
+    {ERR_REASON(ASN1_R_BOOLEAN_IS_WRONG_LENGTH), "boolean is wrong length"},
+    {ERR_REASON(ASN1_R_BUFFER_TOO_SMALL), "buffer too small"},
+    {ERR_REASON(ASN1_R_CIPHER_HAS_NO_OBJECT_IDENTIFIER),
+     "cipher has no object identifier"},
+    {ERR_REASON(ASN1_R_CONTEXT_NOT_INITIALISED), "context not initialised"},
+    {ERR_REASON(ASN1_R_DATA_IS_WRONG), "data is wrong"},
+    {ERR_REASON(ASN1_R_DECODE_ERROR), "decode error"},
+    {ERR_REASON(ASN1_R_DEPTH_EXCEEDED), "depth exceeded"},
+    {ERR_REASON(ASN1_R_DIGEST_AND_KEY_TYPE_NOT_SUPPORTED),
+     "digest and key type not supported"},
+    {ERR_REASON(ASN1_R_ENCODE_ERROR), "encode error"},
+    {ERR_REASON(ASN1_R_ERROR_GETTING_TIME), "error getting time"},
+    {ERR_REASON(ASN1_R_ERROR_LOADING_SECTION), "error loading section"},
+    {ERR_REASON(ASN1_R_ERROR_SETTING_CIPHER_PARAMS),
+     "error setting cipher params"},
+    {ERR_REASON(ASN1_R_EXPECTING_AN_INTEGER), "expecting an integer"},
+    {ERR_REASON(ASN1_R_EXPECTING_AN_OBJECT), "expecting an object"},
+    {ERR_REASON(ASN1_R_EXPLICIT_LENGTH_MISMATCH), "explicit length mismatch"},
+    {ERR_REASON(ASN1_R_EXPLICIT_TAG_NOT_CONSTRUCTED),
+     "explicit tag not constructed"},
+    {ERR_REASON(ASN1_R_FIELD_MISSING), "field missing"},
+    {ERR_REASON(ASN1_R_FIRST_NUM_TOO_LARGE), "first num too large"},
+    {ERR_REASON(ASN1_R_HEADER_TOO_LONG), "header too long"},
+    {ERR_REASON(ASN1_R_ILLEGAL_BITSTRING_FORMAT), "illegal bitstring format"},
+    {ERR_REASON(ASN1_R_ILLEGAL_BOOLEAN), "illegal boolean"},
+    {ERR_REASON(ASN1_R_ILLEGAL_CHARACTERS), "illegal characters"},
+    {ERR_REASON(ASN1_R_ILLEGAL_FORMAT), "illegal format"},
+    {ERR_REASON(ASN1_R_ILLEGAL_HEX), "illegal hex"},
+    {ERR_REASON(ASN1_R_ILLEGAL_IMPLICIT_TAG), "illegal implicit tag"},
+    {ERR_REASON(ASN1_R_ILLEGAL_INTEGER), "illegal integer"},
+    {ERR_REASON(ASN1_R_ILLEGAL_NEGATIVE_VALUE), "illegal negative value"},
+    {ERR_REASON(ASN1_R_ILLEGAL_NESTED_TAGGING), "illegal nested tagging"},
+    {ERR_REASON(ASN1_R_ILLEGAL_NULL), "illegal null"},
+    {ERR_REASON(ASN1_R_ILLEGAL_NULL_VALUE), "illegal null value"},
+    {ERR_REASON(ASN1_R_ILLEGAL_OBJECT), "illegal object"},
+    {ERR_REASON(ASN1_R_ILLEGAL_OPTIONAL_ANY), "illegal optional any"},
+    {ERR_REASON(ASN1_R_ILLEGAL_OPTIONS_ON_ITEM_TEMPLATE),
+     "illegal options on item template"},
+    {ERR_REASON(ASN1_R_ILLEGAL_PADDING), "illegal padding"},
+    {ERR_REASON(ASN1_R_ILLEGAL_TAGGED_ANY), "illegal tagged any"},
+    {ERR_REASON(ASN1_R_ILLEGAL_TIME_VALUE), "illegal time value"},
+    {ERR_REASON(ASN1_R_ILLEGAL_ZERO_CONTENT), "illegal zero content"},
+    {ERR_REASON(ASN1_R_INTEGER_NOT_ASCII_FORMAT), "integer not ascii format"},
+    {ERR_REASON(ASN1_R_INTEGER_TOO_LARGE_FOR_LONG),
+     "integer too large for long"},
+    {ERR_REASON(ASN1_R_INVALID_BIT_STRING_BITS_LEFT),
+     "invalid bit string bits left"},
+    {ERR_REASON(ASN1_R_INVALID_BMPSTRING_LENGTH), "invalid bmpstring length"},
+    {ERR_REASON(ASN1_R_INVALID_DIGIT), "invalid digit"},
+    {ERR_REASON(ASN1_R_INVALID_MIME_TYPE), "invalid mime type"},
+    {ERR_REASON(ASN1_R_INVALID_MODIFIER), "invalid modifier"},
+    {ERR_REASON(ASN1_R_INVALID_NUMBER), "invalid number"},
+    {ERR_REASON(ASN1_R_INVALID_OBJECT_ENCODING), "invalid object encoding"},
+    {ERR_REASON(ASN1_R_INVALID_SCRYPT_PARAMETERS),
+     "invalid scrypt parameters"},
+    {ERR_REASON(ASN1_R_INVALID_SEPARATOR), "invalid separator"},
+    {ERR_REASON(ASN1_R_INVALID_STRING_TABLE_VALUE),
+     "invalid string table value"},
+    {ERR_REASON(ASN1_R_INVALID_UNIVERSALSTRING_LENGTH),
+     "invalid universalstring length"},
+    {ERR_REASON(ASN1_R_INVALID_UTF8STRING), "invalid utf8string"},
+    {ERR_REASON(ASN1_R_INVALID_VALUE), "invalid value"},
+    {ERR_REASON(ASN1_R_LIST_ERROR), "list error"},
+    {ERR_REASON(ASN1_R_MIME_NO_CONTENT_TYPE), "mime no content type"},
+    {ERR_REASON(ASN1_R_MIME_PARSE_ERROR), "mime parse error"},
+    {ERR_REASON(ASN1_R_MIME_SIG_PARSE_ERROR), "mime sig parse error"},
+    {ERR_REASON(ASN1_R_MISSING_EOC), "missing eoc"},
+    {ERR_REASON(ASN1_R_MISSING_SECOND_NUMBER), "missing second number"},
+    {ERR_REASON(ASN1_R_MISSING_VALUE), "missing value"},
+    {ERR_REASON(ASN1_R_MSTRING_NOT_UNIVERSAL), "mstring not universal"},
+    {ERR_REASON(ASN1_R_MSTRING_WRONG_TAG), "mstring wrong tag"},
+    {ERR_REASON(ASN1_R_NESTED_ASN1_STRING), "nested asn1 string"},
+    {ERR_REASON(ASN1_R_NON_HEX_CHARACTERS), "non hex characters"},
+    {ERR_REASON(ASN1_R_NOT_ASCII_FORMAT), "not ascii format"},
+    {ERR_REASON(ASN1_R_NOT_ENOUGH_DATA), "not enough data"},
+    {ERR_REASON(ASN1_R_NO_CONTENT_TYPE), "no content type"},
+    {ERR_REASON(ASN1_R_NO_MATCHING_CHOICE_TYPE), "no matching choice type"},
+    {ERR_REASON(ASN1_R_NO_MULTIPART_BODY_FAILURE),
+     "no multipart body failure"},
+    {ERR_REASON(ASN1_R_NO_MULTIPART_BOUNDARY), "no multipart boundary"},
+    {ERR_REASON(ASN1_R_NO_SIG_CONTENT_TYPE), "no sig content type"},
+    {ERR_REASON(ASN1_R_NULL_IS_WRONG_LENGTH), "null is wrong length"},
+    {ERR_REASON(ASN1_R_OBJECT_NOT_ASCII_FORMAT), "object not ascii format"},
+    {ERR_REASON(ASN1_R_ODD_NUMBER_OF_CHARS), "odd number of chars"},
+    {ERR_REASON(ASN1_R_SECOND_NUMBER_TOO_LARGE), "second number too large"},
+    {ERR_REASON(ASN1_R_SEQUENCE_LENGTH_MISMATCH), "sequence length mismatch"},
+    {ERR_REASON(ASN1_R_SEQUENCE_NOT_CONSTRUCTED), "sequence not constructed"},
+    {ERR_REASON(ASN1_R_SEQUENCE_OR_SET_NEEDS_CONFIG),
+     "sequence or set needs config"},
+    {ERR_REASON(ASN1_R_SHORT_LINE), "short line"},
+    {ERR_REASON(ASN1_R_SIG_INVALID_MIME_TYPE), "sig invalid mime type"},
+    {ERR_REASON(ASN1_R_STREAMING_NOT_SUPPORTED), "streaming not supported"},
+    {ERR_REASON(ASN1_R_STRING_TOO_LONG), "string too long"},
+    {ERR_REASON(ASN1_R_STRING_TOO_SHORT), "string too short"},
+    {ERR_REASON(ASN1_R_THE_ASN1_OBJECT_IDENTIFIER_IS_NOT_KNOWN_FOR_THIS_MD),
+     "the asn1 object identifier is not known for this md"},
+    {ERR_REASON(ASN1_R_TIME_NOT_ASCII_FORMAT), "time not ascii format"},
+    {ERR_REASON(ASN1_R_TOO_LARGE), "too large"},
+    {ERR_REASON(ASN1_R_TOO_LONG), "too long"},
+    {ERR_REASON(ASN1_R_TOO_SMALL), "too small"},
+    {ERR_REASON(ASN1_R_TYPE_NOT_CONSTRUCTED), "type not constructed"},
+    {ERR_REASON(ASN1_R_TYPE_NOT_PRIMITIVE), "type not primitive"},
+    {ERR_REASON(ASN1_R_UNEXPECTED_EOC), "unexpected eoc"},
+    {ERR_REASON(ASN1_R_UNIVERSALSTRING_IS_WRONG_LENGTH),
+     "universalstring is wrong length"},
+    {ERR_REASON(ASN1_R_UNKNOWN_FORMAT), "unknown format"},
+    {ERR_REASON(ASN1_R_UNKNOWN_MESSAGE_DIGEST_ALGORITHM),
+     "unknown message digest algorithm"},
+    {ERR_REASON(ASN1_R_UNKNOWN_OBJECT_TYPE), "unknown object type"},
+    {ERR_REASON(ASN1_R_UNKNOWN_PUBLIC_KEY_TYPE), "unknown public key type"},
+    {ERR_REASON(ASN1_R_UNKNOWN_SIGNATURE_ALGORITHM),
+     "unknown signature algorithm"},
+    {ERR_REASON(ASN1_R_UNKNOWN_TAG), "unknown tag"},
+    {ERR_REASON(ASN1_R_UNSUPPORTED_ANY_DEFINED_BY_TYPE),
+     "unsupported any defined by type"},
+    {ERR_REASON(ASN1_R_UNSUPPORTED_PUBLIC_KEY_TYPE),
+     "unsupported public key type"},
+    {ERR_REASON(ASN1_R_UNSUPPORTED_TYPE), "unsupported type"},
+    {ERR_REASON(ASN1_R_WRONG_INTEGER_TYPE), "wrong integer type"},
+    {ERR_REASON(ASN1_R_WRONG_PUBLIC_KEY_TYPE), "wrong public key type"},
+    {ERR_REASON(ASN1_R_WRONG_TAG), "wrong tag"},
+    {0, NULL}
+};
+
+#endif
+
+int ERR_load_ASN1_strings(void)
+{
+#ifndef OPENSSL_NO_ERR
+
+    if (ERR_func_error_string(ASN1_str_functs[0].error) == NULL) {
+        ERR_load_strings(0, ASN1_str_functs);
+        ERR_load_strings(0, ASN1_str_reasons);
+    }
+#endif
+    return 1;
+}
diff --git a/crypto/asn1/asn1_gen.c b/crypto/asn1/asn1_gen.c
new file mode 100644
index 000000000..493a693aa
--- /dev/null
+++ b/crypto/asn1/asn1_gen.c
@@ -0,0 +1,789 @@
+/*
+ * Copyright 2002-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "internal/cryptlib.h"
+#include 
+#include 
+
+#define ASN1_GEN_FLAG           0x10000
+#define ASN1_GEN_FLAG_IMP       (ASN1_GEN_FLAG|1)
+#define ASN1_GEN_FLAG_EXP       (ASN1_GEN_FLAG|2)
+#define ASN1_GEN_FLAG_TAG       (ASN1_GEN_FLAG|3)
+#define ASN1_GEN_FLAG_BITWRAP   (ASN1_GEN_FLAG|4)
+#define ASN1_GEN_FLAG_OCTWRAP   (ASN1_GEN_FLAG|5)
+#define ASN1_GEN_FLAG_SEQWRAP   (ASN1_GEN_FLAG|6)
+#define ASN1_GEN_FLAG_SETWRAP   (ASN1_GEN_FLAG|7)
+#define ASN1_GEN_FLAG_FORMAT    (ASN1_GEN_FLAG|8)
+
+#define ASN1_GEN_STR(str,val)   {str, sizeof(str) - 1, val}
+
+#define ASN1_FLAG_EXP_MAX       20
+/* Maximum number of nested sequences */
+#define ASN1_GEN_SEQ_MAX_DEPTH  50
+
+/* Input formats */
+
+/* ASCII: default */
+#define ASN1_GEN_FORMAT_ASCII   1
+/* UTF8 */
+#define ASN1_GEN_FORMAT_UTF8    2
+/* Hex */
+#define ASN1_GEN_FORMAT_HEX     3
+/* List of bits */
+#define ASN1_GEN_FORMAT_BITLIST 4
+
+struct tag_name_st {
+    const char *strnam;
+    int len;
+    int tag;
+};
+
+typedef struct {
+    int exp_tag;
+    int exp_class;
+    int exp_constructed;
+    int exp_pad;
+    long exp_len;
+} tag_exp_type;
+
+typedef struct {
+    int imp_tag;
+    int imp_class;
+    int utype;
+    int format;
+    const char *str;
+    tag_exp_type exp_list[ASN1_FLAG_EXP_MAX];
+    int exp_count;
+} tag_exp_arg;
+
+static ASN1_TYPE *generate_v3(const char *str, X509V3_CTX *cnf, int depth,
+                              int *perr);
+static int bitstr_cb(const char *elem, int len, void *bitstr);
+static int asn1_cb(const char *elem, int len, void *bitstr);
+static int append_exp(tag_exp_arg *arg, int exp_tag, int exp_class,
+                      int exp_constructed, int exp_pad, int imp_ok);
+static int parse_tagging(const char *vstart, int vlen, int *ptag,
+                         int *pclass);
+static ASN1_TYPE *asn1_multi(int utype, const char *section, X509V3_CTX *cnf,
+                             int depth, int *perr);
+static ASN1_TYPE *asn1_str2type(const char *str, int format, int utype);
+static int asn1_str2tag(const char *tagstr, int len);
+
+ASN1_TYPE *ASN1_generate_nconf(const char *str, CONF *nconf)
+{
+    X509V3_CTX cnf;
+
+    if (!nconf)
+        return ASN1_generate_v3(str, NULL);
+
+    X509V3_set_nconf(&cnf, nconf);
+    return ASN1_generate_v3(str, &cnf);
+}
+
+ASN1_TYPE *ASN1_generate_v3(const char *str, X509V3_CTX *cnf)
+{
+    int err = 0;
+    ASN1_TYPE *ret = generate_v3(str, cnf, 0, &err);
+    if (err)
+        ASN1err(ASN1_F_ASN1_GENERATE_V3, err);
+    return ret;
+}
+
+static ASN1_TYPE *generate_v3(const char *str, X509V3_CTX *cnf, int depth,
+                              int *perr)
+{
+    ASN1_TYPE *ret;
+    tag_exp_arg asn1_tags;
+    tag_exp_type *etmp;
+
+    int i, len;
+
+    unsigned char *orig_der = NULL, *new_der = NULL;
+    const unsigned char *cpy_start;
+    unsigned char *p;
+    const unsigned char *cp;
+    int cpy_len;
+    long hdr_len = 0;
+    int hdr_constructed = 0, hdr_tag, hdr_class;
+    int r;
+
+    asn1_tags.imp_tag = -1;
+    asn1_tags.imp_class = -1;
+    asn1_tags.format = ASN1_GEN_FORMAT_ASCII;
+    asn1_tags.exp_count = 0;
+    if (CONF_parse_list(str, ',', 1, asn1_cb, &asn1_tags) != 0) {
+        *perr = ASN1_R_UNKNOWN_TAG;
+        return NULL;
+    }
+
+    if ((asn1_tags.utype == V_ASN1_SEQUENCE)
+        || (asn1_tags.utype == V_ASN1_SET)) {
+        if (!cnf) {
+            *perr = ASN1_R_SEQUENCE_OR_SET_NEEDS_CONFIG;
+            return NULL;
+        }
+        if (depth >= ASN1_GEN_SEQ_MAX_DEPTH) {
+            *perr = ASN1_R_ILLEGAL_NESTED_TAGGING;
+            return NULL;
+        }
+        ret = asn1_multi(asn1_tags.utype, asn1_tags.str, cnf, depth, perr);
+    } else
+        ret = asn1_str2type(asn1_tags.str, asn1_tags.format, asn1_tags.utype);
+
+    if (!ret)
+        return NULL;
+
+    /* If no tagging return base type */
+    if ((asn1_tags.imp_tag == -1) && (asn1_tags.exp_count == 0))
+        return ret;
+
+    /* Generate the encoding */
+    cpy_len = i2d_ASN1_TYPE(ret, &orig_der);
+    ASN1_TYPE_free(ret);
+    ret = NULL;
+    /* Set point to start copying for modified encoding */
+    cpy_start = orig_der;
+
+    /* Do we need IMPLICIT tagging? */
+    if (asn1_tags.imp_tag != -1) {
+        /* If IMPLICIT we will replace the underlying tag */
+        /* Skip existing tag+len */
+        r = ASN1_get_object(&cpy_start, &hdr_len, &hdr_tag, &hdr_class,
+                            cpy_len);
+        if (r & 0x80)
+            goto err;
+        /* Update copy length */
+        cpy_len -= cpy_start - orig_der;
+        /*
+         * For IMPLICIT tagging the length should match the original length
+         * and constructed flag should be consistent.
+         */
+        if (r & 0x1) {
+            /* Indefinite length constructed */
+            hdr_constructed = 2;
+            hdr_len = 0;
+        } else
+            /* Just retain constructed flag */
+            hdr_constructed = r & V_ASN1_CONSTRUCTED;
+        /*
+         * Work out new length with IMPLICIT tag: ignore constructed because
+         * it will mess up if indefinite length
+         */
+        len = ASN1_object_size(0, hdr_len, asn1_tags.imp_tag);
+    } else
+        len = cpy_len;
+
+    /* Work out length in any EXPLICIT, starting from end */
+
+    for (i = 0, etmp = asn1_tags.exp_list + asn1_tags.exp_count - 1;
+         i < asn1_tags.exp_count; i++, etmp--) {
+        /* Content length: number of content octets + any padding */
+        len += etmp->exp_pad;
+        etmp->exp_len = len;
+        /* Total object length: length including new header */
+        len = ASN1_object_size(0, len, etmp->exp_tag);
+    }
+
+    /* Allocate buffer for new encoding */
+
+    new_der = OPENSSL_malloc(len);
+    if (new_der == NULL)
+        goto err;
+
+    /* Generate tagged encoding */
+
+    p = new_der;
+
+    /* Output explicit tags first */
+
+    for (i = 0, etmp = asn1_tags.exp_list; i < asn1_tags.exp_count;
+         i++, etmp++) {
+        ASN1_put_object(&p, etmp->exp_constructed, etmp->exp_len,
+                        etmp->exp_tag, etmp->exp_class);
+        if (etmp->exp_pad)
+            *p++ = 0;
+    }
+
+    /* If IMPLICIT, output tag */
+
+    if (asn1_tags.imp_tag != -1) {
+        if (asn1_tags.imp_class == V_ASN1_UNIVERSAL
+            && (asn1_tags.imp_tag == V_ASN1_SEQUENCE
+                || asn1_tags.imp_tag == V_ASN1_SET))
+            hdr_constructed = V_ASN1_CONSTRUCTED;
+        ASN1_put_object(&p, hdr_constructed, hdr_len,
+                        asn1_tags.imp_tag, asn1_tags.imp_class);
+    }
+
+    /* Copy across original encoding */
+    memcpy(p, cpy_start, cpy_len);
+
+    cp = new_der;
+
+    /* Obtain new ASN1_TYPE structure */
+    ret = d2i_ASN1_TYPE(NULL, &cp, len);
+
+ err:
+    OPENSSL_free(orig_der);
+    OPENSSL_free(new_der);
+
+    return ret;
+
+}
+
+static int asn1_cb(const char *elem, int len, void *bitstr)
+{
+    tag_exp_arg *arg = bitstr;
+    int i;
+    int utype;
+    int vlen = 0;
+    const char *p, *vstart = NULL;
+
+    int tmp_tag, tmp_class;
+
+    if (elem == NULL)
+        return -1;
+
+    for (i = 0, p = elem; i < len; p++, i++) {
+        /* Look for the ':' in name value pairs */
+        if (*p == ':') {
+            vstart = p + 1;
+            vlen = len - (vstart - elem);
+            len = p - elem;
+            break;
+        }
+    }
+
+    utype = asn1_str2tag(elem, len);
+
+    if (utype == -1) {
+        ASN1err(ASN1_F_ASN1_CB, ASN1_R_UNKNOWN_TAG);
+        ERR_add_error_data(2, "tag=", elem);
+        return -1;
+    }
+
+    /* If this is not a modifier mark end of string and exit */
+    if (!(utype & ASN1_GEN_FLAG)) {
+        arg->utype = utype;
+        arg->str = vstart;
+        /* If no value and not end of string, error */
+        if (!vstart && elem[len]) {
+            ASN1err(ASN1_F_ASN1_CB, ASN1_R_MISSING_VALUE);
+            return -1;
+        }
+        return 0;
+    }
+
+    switch (utype) {
+
+    case ASN1_GEN_FLAG_IMP:
+        /* Check for illegal multiple IMPLICIT tagging */
+        if (arg->imp_tag != -1) {
+            ASN1err(ASN1_F_ASN1_CB, ASN1_R_ILLEGAL_NESTED_TAGGING);
+            return -1;
+        }
+        if (!parse_tagging(vstart, vlen, &arg->imp_tag, &arg->imp_class))
+            return -1;
+        break;
+
+    case ASN1_GEN_FLAG_EXP:
+
+        if (!parse_tagging(vstart, vlen, &tmp_tag, &tmp_class))
+            return -1;
+        if (!append_exp(arg, tmp_tag, tmp_class, 1, 0, 0))
+            return -1;
+        break;
+
+    case ASN1_GEN_FLAG_SEQWRAP:
+        if (!append_exp(arg, V_ASN1_SEQUENCE, V_ASN1_UNIVERSAL, 1, 0, 1))
+            return -1;
+        break;
+
+    case ASN1_GEN_FLAG_SETWRAP:
+        if (!append_exp(arg, V_ASN1_SET, V_ASN1_UNIVERSAL, 1, 0, 1))
+            return -1;
+        break;
+
+    case ASN1_GEN_FLAG_BITWRAP:
+        if (!append_exp(arg, V_ASN1_BIT_STRING, V_ASN1_UNIVERSAL, 0, 1, 1))
+            return -1;
+        break;
+
+    case ASN1_GEN_FLAG_OCTWRAP:
+        if (!append_exp(arg, V_ASN1_OCTET_STRING, V_ASN1_UNIVERSAL, 0, 0, 1))
+            return -1;
+        break;
+
+    case ASN1_GEN_FLAG_FORMAT:
+        if (!vstart) {
+            ASN1err(ASN1_F_ASN1_CB, ASN1_R_UNKNOWN_FORMAT);
+            return -1;
+        }
+        if (strncmp(vstart, "ASCII", 5) == 0)
+            arg->format = ASN1_GEN_FORMAT_ASCII;
+        else if (strncmp(vstart, "UTF8", 4) == 0)
+            arg->format = ASN1_GEN_FORMAT_UTF8;
+        else if (strncmp(vstart, "HEX", 3) == 0)
+            arg->format = ASN1_GEN_FORMAT_HEX;
+        else if (strncmp(vstart, "BITLIST", 7) == 0)
+            arg->format = ASN1_GEN_FORMAT_BITLIST;
+        else {
+            ASN1err(ASN1_F_ASN1_CB, ASN1_R_UNKNOWN_FORMAT);
+            return -1;
+        }
+        break;
+
+    }
+
+    return 1;
+
+}
+
+static int parse_tagging(const char *vstart, int vlen, int *ptag, int *pclass)
+{
+    char erch[2];
+    long tag_num;
+    char *eptr;
+    if (!vstart)
+        return 0;
+    tag_num = strtoul(vstart, &eptr, 10);
+    /* Check we haven't gone past max length: should be impossible */
+    if (eptr && *eptr && (eptr > vstart + vlen))
+        return 0;
+    if (tag_num < 0) {
+        ASN1err(ASN1_F_PARSE_TAGGING, ASN1_R_INVALID_NUMBER);
+        return 0;
+    }
+    *ptag = tag_num;
+    /* If we have non numeric characters, parse them */
+    if (eptr)
+        vlen -= eptr - vstart;
+    else
+        vlen = 0;
+    if (vlen) {
+        switch (*eptr) {
+
+        case 'U':
+            *pclass = V_ASN1_UNIVERSAL;
+            break;
+
+        case 'A':
+            *pclass = V_ASN1_APPLICATION;
+            break;
+
+        case 'P':
+            *pclass = V_ASN1_PRIVATE;
+            break;
+
+        case 'C':
+            *pclass = V_ASN1_CONTEXT_SPECIFIC;
+            break;
+
+        default:
+            erch[0] = *eptr;
+            erch[1] = 0;
+            ASN1err(ASN1_F_PARSE_TAGGING, ASN1_R_INVALID_MODIFIER);
+            ERR_add_error_data(2, "Char=", erch);
+            return 0;
+
+        }
+    } else
+        *pclass = V_ASN1_CONTEXT_SPECIFIC;
+
+    return 1;
+
+}
+
+/* Handle multiple types: SET and SEQUENCE */
+
+static ASN1_TYPE *asn1_multi(int utype, const char *section, X509V3_CTX *cnf,
+                             int depth, int *perr)
+{
+    ASN1_TYPE *ret = NULL;
+    STACK_OF(ASN1_TYPE) *sk = NULL;
+    STACK_OF(CONF_VALUE) *sect = NULL;
+    unsigned char *der = NULL;
+    int derlen;
+    int i;
+    sk = sk_ASN1_TYPE_new_null();
+    if (!sk)
+        goto bad;
+    if (section) {
+        if (!cnf)
+            goto bad;
+        sect = X509V3_get_section(cnf, (char *)section);
+        if (!sect)
+            goto bad;
+        for (i = 0; i < sk_CONF_VALUE_num(sect); i++) {
+            ASN1_TYPE *typ =
+                generate_v3(sk_CONF_VALUE_value(sect, i)->value, cnf,
+                            depth + 1, perr);
+            if (!typ)
+                goto bad;
+            if (!sk_ASN1_TYPE_push(sk, typ))
+                goto bad;
+        }
+    }
+
+    /*
+     * Now we has a STACK of the components, convert to the correct form
+     */
+
+    if (utype == V_ASN1_SET)
+        derlen = i2d_ASN1_SET_ANY(sk, &der);
+    else
+        derlen = i2d_ASN1_SEQUENCE_ANY(sk, &der);
+
+    if (derlen < 0)
+        goto bad;
+    if ((ret = ASN1_TYPE_new()) == NULL)
+        goto bad;
+    if ((ret->value.asn1_string = ASN1_STRING_type_new(utype)) == NULL)
+        goto bad;
+
+    ret->type = utype;
+    ret->value.asn1_string->data = der;
+    ret->value.asn1_string->length = derlen;
+
+    der = NULL;
+
+ bad:
+
+    OPENSSL_free(der);
+
+    sk_ASN1_TYPE_pop_free(sk, ASN1_TYPE_free);
+    X509V3_section_free(cnf, sect);
+
+    return ret;
+}
+
+static int append_exp(tag_exp_arg *arg, int exp_tag, int exp_class,
+                      int exp_constructed, int exp_pad, int imp_ok)
+{
+    tag_exp_type *exp_tmp;
+    /* Can only have IMPLICIT if permitted */
+    if ((arg->imp_tag != -1) && !imp_ok) {
+        ASN1err(ASN1_F_APPEND_EXP, ASN1_R_ILLEGAL_IMPLICIT_TAG);
+        return 0;
+    }
+
+    if (arg->exp_count == ASN1_FLAG_EXP_MAX) {
+        ASN1err(ASN1_F_APPEND_EXP, ASN1_R_DEPTH_EXCEEDED);
+        return 0;
+    }
+
+    exp_tmp = &arg->exp_list[arg->exp_count++];
+
+    /*
+     * If IMPLICIT set tag to implicit value then reset implicit tag since it
+     * has been used.
+     */
+    if (arg->imp_tag != -1) {
+        exp_tmp->exp_tag = arg->imp_tag;
+        exp_tmp->exp_class = arg->imp_class;
+        arg->imp_tag = -1;
+        arg->imp_class = -1;
+    } else {
+        exp_tmp->exp_tag = exp_tag;
+        exp_tmp->exp_class = exp_class;
+    }
+    exp_tmp->exp_constructed = exp_constructed;
+    exp_tmp->exp_pad = exp_pad;
+
+    return 1;
+}
+
+static int asn1_str2tag(const char *tagstr, int len)
+{
+    unsigned int i;
+    static const struct tag_name_st *tntmp, tnst[] = {
+        ASN1_GEN_STR("BOOL", V_ASN1_BOOLEAN),
+        ASN1_GEN_STR("BOOLEAN", V_ASN1_BOOLEAN),
+        ASN1_GEN_STR("NULL", V_ASN1_NULL),
+        ASN1_GEN_STR("INT", V_ASN1_INTEGER),
+        ASN1_GEN_STR("INTEGER", V_ASN1_INTEGER),
+        ASN1_GEN_STR("ENUM", V_ASN1_ENUMERATED),
+        ASN1_GEN_STR("ENUMERATED", V_ASN1_ENUMERATED),
+        ASN1_GEN_STR("OID", V_ASN1_OBJECT),
+        ASN1_GEN_STR("OBJECT", V_ASN1_OBJECT),
+        ASN1_GEN_STR("UTCTIME", V_ASN1_UTCTIME),
+        ASN1_GEN_STR("UTC", V_ASN1_UTCTIME),
+        ASN1_GEN_STR("GENERALIZEDTIME", V_ASN1_GENERALIZEDTIME),
+        ASN1_GEN_STR("GENTIME", V_ASN1_GENERALIZEDTIME),
+        ASN1_GEN_STR("OCT", V_ASN1_OCTET_STRING),
+        ASN1_GEN_STR("OCTETSTRING", V_ASN1_OCTET_STRING),
+        ASN1_GEN_STR("BITSTR", V_ASN1_BIT_STRING),
+        ASN1_GEN_STR("BITSTRING", V_ASN1_BIT_STRING),
+        ASN1_GEN_STR("UNIVERSALSTRING", V_ASN1_UNIVERSALSTRING),
+        ASN1_GEN_STR("UNIV", V_ASN1_UNIVERSALSTRING),
+        ASN1_GEN_STR("IA5", V_ASN1_IA5STRING),
+        ASN1_GEN_STR("IA5STRING", V_ASN1_IA5STRING),
+        ASN1_GEN_STR("UTF8", V_ASN1_UTF8STRING),
+        ASN1_GEN_STR("UTF8String", V_ASN1_UTF8STRING),
+        ASN1_GEN_STR("BMP", V_ASN1_BMPSTRING),
+        ASN1_GEN_STR("BMPSTRING", V_ASN1_BMPSTRING),
+        ASN1_GEN_STR("VISIBLESTRING", V_ASN1_VISIBLESTRING),
+        ASN1_GEN_STR("VISIBLE", V_ASN1_VISIBLESTRING),
+        ASN1_GEN_STR("PRINTABLESTRING", V_ASN1_PRINTABLESTRING),
+        ASN1_GEN_STR("PRINTABLE", V_ASN1_PRINTABLESTRING),
+        ASN1_GEN_STR("T61", V_ASN1_T61STRING),
+        ASN1_GEN_STR("T61STRING", V_ASN1_T61STRING),
+        ASN1_GEN_STR("TELETEXSTRING", V_ASN1_T61STRING),
+        ASN1_GEN_STR("GeneralString", V_ASN1_GENERALSTRING),
+        ASN1_GEN_STR("GENSTR", V_ASN1_GENERALSTRING),
+        ASN1_GEN_STR("NUMERIC", V_ASN1_NUMERICSTRING),
+        ASN1_GEN_STR("NUMERICSTRING", V_ASN1_NUMERICSTRING),
+
+        /* Special cases */
+        ASN1_GEN_STR("SEQUENCE", V_ASN1_SEQUENCE),
+        ASN1_GEN_STR("SEQ", V_ASN1_SEQUENCE),
+        ASN1_GEN_STR("SET", V_ASN1_SET),
+        /* type modifiers */
+        /* Explicit tag */
+        ASN1_GEN_STR("EXP", ASN1_GEN_FLAG_EXP),
+        ASN1_GEN_STR("EXPLICIT", ASN1_GEN_FLAG_EXP),
+        /* Implicit tag */
+        ASN1_GEN_STR("IMP", ASN1_GEN_FLAG_IMP),
+        ASN1_GEN_STR("IMPLICIT", ASN1_GEN_FLAG_IMP),
+        /* OCTET STRING wrapper */
+        ASN1_GEN_STR("OCTWRAP", ASN1_GEN_FLAG_OCTWRAP),
+        /* SEQUENCE wrapper */
+        ASN1_GEN_STR("SEQWRAP", ASN1_GEN_FLAG_SEQWRAP),
+        /* SET wrapper */
+        ASN1_GEN_STR("SETWRAP", ASN1_GEN_FLAG_SETWRAP),
+        /* BIT STRING wrapper */
+        ASN1_GEN_STR("BITWRAP", ASN1_GEN_FLAG_BITWRAP),
+        ASN1_GEN_STR("FORM", ASN1_GEN_FLAG_FORMAT),
+        ASN1_GEN_STR("FORMAT", ASN1_GEN_FLAG_FORMAT),
+    };
+
+    if (len == -1)
+        len = strlen(tagstr);
+
+    tntmp = tnst;
+    for (i = 0; i < OSSL_NELEM(tnst); i++, tntmp++) {
+        if ((len == tntmp->len) && (strncmp(tntmp->strnam, tagstr, len) == 0))
+            return tntmp->tag;
+    }
+
+    return -1;
+}
+
+static ASN1_TYPE *asn1_str2type(const char *str, int format, int utype)
+{
+    ASN1_TYPE *atmp = NULL;
+    CONF_VALUE vtmp;
+    unsigned char *rdata;
+    long rdlen;
+    int no_unused = 1;
+
+    if ((atmp = ASN1_TYPE_new()) == NULL) {
+        ASN1err(ASN1_F_ASN1_STR2TYPE, ERR_R_MALLOC_FAILURE);
+        return NULL;
+    }
+
+    if (!str)
+        str = "";
+
+    switch (utype) {
+
+    case V_ASN1_NULL:
+        if (str && *str) {
+            ASN1err(ASN1_F_ASN1_STR2TYPE, ASN1_R_ILLEGAL_NULL_VALUE);
+            goto bad_form;
+        }
+        break;
+
+    case V_ASN1_BOOLEAN:
+        if (format != ASN1_GEN_FORMAT_ASCII) {
+            ASN1err(ASN1_F_ASN1_STR2TYPE, ASN1_R_NOT_ASCII_FORMAT);
+            goto bad_form;
+        }
+        vtmp.name = NULL;
+        vtmp.section = NULL;
+        vtmp.value = (char *)str;
+        if (!X509V3_get_value_bool(&vtmp, &atmp->value.boolean)) {
+            ASN1err(ASN1_F_ASN1_STR2TYPE, ASN1_R_ILLEGAL_BOOLEAN);
+            goto bad_str;
+        }
+        break;
+
+    case V_ASN1_INTEGER:
+    case V_ASN1_ENUMERATED:
+        if (format != ASN1_GEN_FORMAT_ASCII) {
+            ASN1err(ASN1_F_ASN1_STR2TYPE, ASN1_R_INTEGER_NOT_ASCII_FORMAT);
+            goto bad_form;
+        }
+        if ((atmp->value.integer
+                    = s2i_ASN1_INTEGER(NULL, str)) == NULL) {
+            ASN1err(ASN1_F_ASN1_STR2TYPE, ASN1_R_ILLEGAL_INTEGER);
+            goto bad_str;
+        }
+        break;
+
+    case V_ASN1_OBJECT:
+        if (format != ASN1_GEN_FORMAT_ASCII) {
+            ASN1err(ASN1_F_ASN1_STR2TYPE, ASN1_R_OBJECT_NOT_ASCII_FORMAT);
+            goto bad_form;
+        }
+        if ((atmp->value.object = OBJ_txt2obj(str, 0)) == NULL) {
+            ASN1err(ASN1_F_ASN1_STR2TYPE, ASN1_R_ILLEGAL_OBJECT);
+            goto bad_str;
+        }
+        break;
+
+    case V_ASN1_UTCTIME:
+    case V_ASN1_GENERALIZEDTIME:
+        if (format != ASN1_GEN_FORMAT_ASCII) {
+            ASN1err(ASN1_F_ASN1_STR2TYPE, ASN1_R_TIME_NOT_ASCII_FORMAT);
+            goto bad_form;
+        }
+        if ((atmp->value.asn1_string = ASN1_STRING_new()) == NULL) {
+            ASN1err(ASN1_F_ASN1_STR2TYPE, ERR_R_MALLOC_FAILURE);
+            goto bad_str;
+        }
+        if (!ASN1_STRING_set(atmp->value.asn1_string, str, -1)) {
+            ASN1err(ASN1_F_ASN1_STR2TYPE, ERR_R_MALLOC_FAILURE);
+            goto bad_str;
+        }
+        atmp->value.asn1_string->type = utype;
+        if (!ASN1_TIME_check(atmp->value.asn1_string)) {
+            ASN1err(ASN1_F_ASN1_STR2TYPE, ASN1_R_ILLEGAL_TIME_VALUE);
+            goto bad_str;
+        }
+
+        break;
+
+    case V_ASN1_BMPSTRING:
+    case V_ASN1_PRINTABLESTRING:
+    case V_ASN1_IA5STRING:
+    case V_ASN1_T61STRING:
+    case V_ASN1_UTF8STRING:
+    case V_ASN1_VISIBLESTRING:
+    case V_ASN1_UNIVERSALSTRING:
+    case V_ASN1_GENERALSTRING:
+    case V_ASN1_NUMERICSTRING:
+        if (format == ASN1_GEN_FORMAT_ASCII)
+            format = MBSTRING_ASC;
+        else if (format == ASN1_GEN_FORMAT_UTF8)
+            format = MBSTRING_UTF8;
+        else {
+            ASN1err(ASN1_F_ASN1_STR2TYPE, ASN1_R_ILLEGAL_FORMAT);
+            goto bad_form;
+        }
+
+        if (ASN1_mbstring_copy(&atmp->value.asn1_string, (unsigned char *)str,
+                               -1, format, ASN1_tag2bit(utype)) <= 0) {
+            ASN1err(ASN1_F_ASN1_STR2TYPE, ERR_R_MALLOC_FAILURE);
+            goto bad_str;
+        }
+
+        break;
+
+    case V_ASN1_BIT_STRING:
+    case V_ASN1_OCTET_STRING:
+        if ((atmp->value.asn1_string = ASN1_STRING_new()) == NULL) {
+            ASN1err(ASN1_F_ASN1_STR2TYPE, ERR_R_MALLOC_FAILURE);
+            goto bad_form;
+        }
+
+        if (format == ASN1_GEN_FORMAT_HEX) {
+            if ((rdata = OPENSSL_hexstr2buf(str, &rdlen)) == NULL) {
+                ASN1err(ASN1_F_ASN1_STR2TYPE, ASN1_R_ILLEGAL_HEX);
+                goto bad_str;
+            }
+            atmp->value.asn1_string->data = rdata;
+            atmp->value.asn1_string->length = rdlen;
+            atmp->value.asn1_string->type = utype;
+        } else if (format == ASN1_GEN_FORMAT_ASCII)
+            ASN1_STRING_set(atmp->value.asn1_string, str, -1);
+        else if ((format == ASN1_GEN_FORMAT_BITLIST)
+                 && (utype == V_ASN1_BIT_STRING)) {
+            if (!CONF_parse_list
+                (str, ',', 1, bitstr_cb, atmp->value.bit_string)) {
+                ASN1err(ASN1_F_ASN1_STR2TYPE, ASN1_R_LIST_ERROR);
+                goto bad_str;
+            }
+            no_unused = 0;
+
+        } else {
+            ASN1err(ASN1_F_ASN1_STR2TYPE, ASN1_R_ILLEGAL_BITSTRING_FORMAT);
+            goto bad_form;
+        }
+
+        if ((utype == V_ASN1_BIT_STRING) && no_unused) {
+            atmp->value.asn1_string->flags
+                &= ~(ASN1_STRING_FLAG_BITS_LEFT | 0x07);
+            atmp->value.asn1_string->flags |= ASN1_STRING_FLAG_BITS_LEFT;
+        }
+
+        break;
+
+    default:
+        ASN1err(ASN1_F_ASN1_STR2TYPE, ASN1_R_UNSUPPORTED_TYPE);
+        goto bad_str;
+    }
+
+    atmp->type = utype;
+    return atmp;
+
+ bad_str:
+    ERR_add_error_data(2, "string=", str);
+ bad_form:
+
+    ASN1_TYPE_free(atmp);
+    return NULL;
+
+}
+
+static int bitstr_cb(const char *elem, int len, void *bitstr)
+{
+    long bitnum;
+    char *eptr;
+    if (!elem)
+        return 0;
+    bitnum = strtoul(elem, &eptr, 10);
+    if (eptr && *eptr && (eptr != elem + len))
+        return 0;
+    if (bitnum < 0) {
+        ASN1err(ASN1_F_BITSTR_CB, ASN1_R_INVALID_NUMBER);
+        return 0;
+    }
+    if (!ASN1_BIT_STRING_set_bit(bitstr, bitnum, 1)) {
+        ASN1err(ASN1_F_BITSTR_CB, ERR_R_MALLOC_FAILURE);
+        return 0;
+    }
+    return 1;
+}
+
+static int mask_cb(const char *elem, int len, void *arg)
+{
+    unsigned long *pmask = arg, tmpmask;
+    int tag;
+    if (elem == NULL)
+        return 0;
+    if ((len == 3) && (strncmp(elem, "DIR", 3) == 0)) {
+        *pmask |= B_ASN1_DIRECTORYSTRING;
+        return 1;
+    }
+    tag = asn1_str2tag(elem, len);
+    if (!tag || (tag & ASN1_GEN_FLAG))
+        return 0;
+    tmpmask = ASN1_tag2bit(tag);
+    if (!tmpmask)
+        return 0;
+    *pmask |= tmpmask;
+    return 1;
+}
+
+int ASN1_str2mask(const char *str, unsigned long *pmask)
+{
+    *pmask = 0;
+    return CONF_parse_list(str, '|', 1, mask_cb, pmask);
+}
diff --git a/crypto/asn1/asn1_lib.c b/crypto/asn1/asn1_lib.c
new file mode 100644
index 000000000..8ca53b4ce
--- /dev/null
+++ b/crypto/asn1/asn1_lib.c
@@ -0,0 +1,384 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include "asn1_locl.h"
+
+static int asn1_get_length(const unsigned char **pp, int *inf, long *rl,
+                           long max);
+static void asn1_put_length(unsigned char **pp, int length);
+
+static int _asn1_check_infinite_end(const unsigned char **p, long len)
+{
+    /*
+     * If there is 0 or 1 byte left, the length check should pick things up
+     */
+    if (len <= 0)
+        return (1);
+    else if ((len >= 2) && ((*p)[0] == 0) && ((*p)[1] == 0)) {
+        (*p) += 2;
+        return (1);
+    }
+    return (0);
+}
+
+int ASN1_check_infinite_end(unsigned char **p, long len)
+{
+    return _asn1_check_infinite_end((const unsigned char **)p, len);
+}
+
+int ASN1_const_check_infinite_end(const unsigned char **p, long len)
+{
+    return _asn1_check_infinite_end(p, len);
+}
+
+int ASN1_get_object(const unsigned char **pp, long *plength, int *ptag,
+                    int *pclass, long omax)
+{
+    int i, ret;
+    long l;
+    const unsigned char *p = *pp;
+    int tag, xclass, inf;
+    long max = omax;
+
+    if (!max)
+        goto err;
+    ret = (*p & V_ASN1_CONSTRUCTED);
+    xclass = (*p & V_ASN1_PRIVATE);
+    i = *p & V_ASN1_PRIMITIVE_TAG;
+    if (i == V_ASN1_PRIMITIVE_TAG) { /* high-tag */
+        p++;
+        if (--max == 0)
+            goto err;
+        l = 0;
+        while (*p & 0x80) {
+            l <<= 7L;
+            l |= *(p++) & 0x7f;
+            if (--max == 0)
+                goto err;
+            if (l > (INT_MAX >> 7L))
+                goto err;
+        }
+        l <<= 7L;
+        l |= *(p++) & 0x7f;
+        tag = (int)l;
+        if (--max == 0)
+            goto err;
+    } else {
+        tag = i;
+        p++;
+        if (--max == 0)
+            goto err;
+    }
+    *ptag = tag;
+    *pclass = xclass;
+    if (!asn1_get_length(&p, &inf, plength, max))
+        goto err;
+
+    if (inf && !(ret & V_ASN1_CONSTRUCTED))
+        goto err;
+
+    if (*plength > (omax - (p - *pp))) {
+        ASN1err(ASN1_F_ASN1_GET_OBJECT, ASN1_R_TOO_LONG);
+        /*
+         * Set this so that even if things are not long enough the values are
+         * set correctly
+         */
+        ret |= 0x80;
+    }
+    *pp = p;
+    return (ret | inf);
+ err:
+    ASN1err(ASN1_F_ASN1_GET_OBJECT, ASN1_R_HEADER_TOO_LONG);
+    return (0x80);
+}
+
+static int asn1_get_length(const unsigned char **pp, int *inf, long *rl,
+                           long max)
+{
+    const unsigned char *p = *pp;
+    unsigned long ret = 0;
+    unsigned long i;
+
+    if (max-- < 1)
+        return 0;
+    if (*p == 0x80) {
+        *inf = 1;
+        ret = 0;
+        p++;
+    } else {
+        *inf = 0;
+        i = *p & 0x7f;
+        if (*(p++) & 0x80) {
+            if (max < (long)i + 1)
+                return 0;
+            /* Skip leading zeroes */
+            while (i && *p == 0) {
+                p++;
+                i--;
+            }
+            if (i > sizeof(long))
+                return 0;
+            while (i-- > 0) {
+                ret <<= 8L;
+                ret |= *(p++);
+            }
+        } else
+            ret = i;
+    }
+    if (ret > LONG_MAX)
+        return 0;
+    *pp = p;
+    *rl = (long)ret;
+    return 1;
+}
+
+/*
+ * class 0 is constructed constructed == 2 for indefinite length constructed
+ */
+void ASN1_put_object(unsigned char **pp, int constructed, int length, int tag,
+                     int xclass)
+{
+    unsigned char *p = *pp;
+    int i, ttag;
+
+    i = (constructed) ? V_ASN1_CONSTRUCTED : 0;
+    i |= (xclass & V_ASN1_PRIVATE);
+    if (tag < 31)
+        *(p++) = i | (tag & V_ASN1_PRIMITIVE_TAG);
+    else {
+        *(p++) = i | V_ASN1_PRIMITIVE_TAG;
+        for (i = 0, ttag = tag; ttag > 0; i++)
+            ttag >>= 7;
+        ttag = i;
+        while (i-- > 0) {
+            p[i] = tag & 0x7f;
+            if (i != (ttag - 1))
+                p[i] |= 0x80;
+            tag >>= 7;
+        }
+        p += ttag;
+    }
+    if (constructed == 2)
+        *(p++) = 0x80;
+    else
+        asn1_put_length(&p, length);
+    *pp = p;
+}
+
+int ASN1_put_eoc(unsigned char **pp)
+{
+    unsigned char *p = *pp;
+    *p++ = 0;
+    *p++ = 0;
+    *pp = p;
+    return 2;
+}
+
+static void asn1_put_length(unsigned char **pp, int length)
+{
+    unsigned char *p = *pp;
+    int i, l;
+    if (length <= 127)
+        *(p++) = (unsigned char)length;
+    else {
+        l = length;
+        for (i = 0; l > 0; i++)
+            l >>= 8;
+        *(p++) = i | 0x80;
+        l = i;
+        while (i-- > 0) {
+            p[i] = length & 0xff;
+            length >>= 8;
+        }
+        p += l;
+    }
+    *pp = p;
+}
+
+int ASN1_object_size(int constructed, int length, int tag)
+{
+    int ret = 1;
+    if (length < 0)
+        return -1;
+    if (tag >= 31) {
+        while (tag > 0) {
+            tag >>= 7;
+            ret++;
+        }
+    }
+    if (constructed == 2) {
+        ret += 3;
+    } else {
+        ret++;
+        if (length > 127) {
+            int tmplen = length;
+            while (tmplen > 0) {
+                tmplen >>= 8;
+                ret++;
+            }
+        }
+    }
+    if (ret >= INT_MAX - length)
+        return -1;
+    return ret + length;
+}
+
+int ASN1_STRING_copy(ASN1_STRING *dst, const ASN1_STRING *str)
+{
+    if (str == NULL)
+        return 0;
+    dst->type = str->type;
+    if (!ASN1_STRING_set(dst, str->data, str->length))
+        return 0;
+    /* Copy flags but preserve embed value */
+    dst->flags &= ASN1_STRING_FLAG_EMBED;
+    dst->flags |= str->flags & ~ASN1_STRING_FLAG_EMBED;
+    return 1;
+}
+
+ASN1_STRING *ASN1_STRING_dup(const ASN1_STRING *str)
+{
+    ASN1_STRING *ret;
+    if (!str)
+        return NULL;
+    ret = ASN1_STRING_new();
+    if (ret == NULL)
+        return NULL;
+    if (!ASN1_STRING_copy(ret, str)) {
+        ASN1_STRING_free(ret);
+        return NULL;
+    }
+    return ret;
+}
+
+int ASN1_STRING_set(ASN1_STRING *str, const void *_data, int len)
+{
+    unsigned char *c;
+    const char *data = _data;
+
+    if (len < 0) {
+        if (data == NULL)
+            return (0);
+        else
+            len = strlen(data);
+    }
+    if ((str->length <= len) || (str->data == NULL)) {
+        c = str->data;
+        str->data = OPENSSL_realloc(c, len + 1);
+        if (str->data == NULL) {
+            ASN1err(ASN1_F_ASN1_STRING_SET, ERR_R_MALLOC_FAILURE);
+            str->data = c;
+            return (0);
+        }
+    }
+    str->length = len;
+    if (data != NULL) {
+        memcpy(str->data, data, len);
+        /* an allowance for strings :-) */
+        str->data[len] = '\0';
+    }
+    return (1);
+}
+
+void ASN1_STRING_set0(ASN1_STRING *str, void *data, int len)
+{
+    OPENSSL_free(str->data);
+    str->data = data;
+    str->length = len;
+}
+
+ASN1_STRING *ASN1_STRING_new(void)
+{
+    return (ASN1_STRING_type_new(V_ASN1_OCTET_STRING));
+}
+
+ASN1_STRING *ASN1_STRING_type_new(int type)
+{
+    ASN1_STRING *ret;
+
+    ret = OPENSSL_zalloc(sizeof(*ret));
+    if (ret == NULL) {
+        ASN1err(ASN1_F_ASN1_STRING_TYPE_NEW, ERR_R_MALLOC_FAILURE);
+        return (NULL);
+    }
+    ret->type = type;
+    return (ret);
+}
+
+void asn1_string_embed_free(ASN1_STRING *a, int embed)
+{
+    if (a == NULL)
+        return;
+    if (!(a->flags & ASN1_STRING_FLAG_NDEF))
+        OPENSSL_free(a->data);
+    if (embed == 0)
+        OPENSSL_free(a);
+}
+
+void ASN1_STRING_free(ASN1_STRING *a)
+{
+    if (a == NULL)
+        return;
+    asn1_string_embed_free(a, a->flags & ASN1_STRING_FLAG_EMBED);
+}
+
+void ASN1_STRING_clear_free(ASN1_STRING *a)
+{
+    if (a == NULL)
+        return;
+    if (a->data && !(a->flags & ASN1_STRING_FLAG_NDEF))
+        OPENSSL_cleanse(a->data, a->length);
+    ASN1_STRING_free(a);
+}
+
+int ASN1_STRING_cmp(const ASN1_STRING *a, const ASN1_STRING *b)
+{
+    int i;
+
+    i = (a->length - b->length);
+    if (i == 0) {
+        i = memcmp(a->data, b->data, a->length);
+        if (i == 0)
+            return (a->type - b->type);
+        else
+            return (i);
+    } else
+        return (i);
+}
+
+int ASN1_STRING_length(const ASN1_STRING *x)
+{
+    return x->length;
+}
+
+void ASN1_STRING_length_set(ASN1_STRING *x, int len)
+{
+    x->length = len;
+}
+
+int ASN1_STRING_type(const ASN1_STRING *x)
+{
+    return x->type;
+}
+
+const unsigned char *ASN1_STRING_get0_data(const ASN1_STRING *x)
+{
+    return x->data;
+}
+
+# if OPENSSL_API_COMPAT < 0x10100000L
+unsigned char *ASN1_STRING_data(ASN1_STRING *x)
+{
+    return x->data;
+}
+#endif
diff --git a/crypto/asn1/asn1_locl.h b/crypto/asn1/asn1_locl.h
new file mode 100644
index 000000000..5f597bd98
--- /dev/null
+++ b/crypto/asn1/asn1_locl.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2005-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/* Internal ASN1 structures and functions: not for application use */
+
+int asn1_utctime_to_tm(struct tm *tm, const ASN1_UTCTIME *d);
+int asn1_generalizedtime_to_tm(struct tm *tm, const ASN1_GENERALIZEDTIME *d);
+
+/* ASN1 scan context structure */
+
+struct asn1_sctx_st {
+    /* The ASN1_ITEM associated with this field */
+    const ASN1_ITEM *it;
+    /* If ASN1_TEMPLATE associated with this field */
+    const ASN1_TEMPLATE *tt;
+    /* Various flags associated with field and context */
+    unsigned long flags;
+    /* If SEQUENCE OF or SET OF, field index */
+    int skidx;
+    /* ASN1 depth of field */
+    int depth;
+    /* Structure and field name */
+    const char *sname, *fname;
+    /* If a primitive type the type of underlying field */
+    int prim_type;
+    /* The field value itself */
+    ASN1_VALUE **field;
+    /* Callback to pass information to */
+    int (*scan_cb) (ASN1_SCTX *ctx);
+    /* Context specific application data */
+    void *app_data;
+} /* ASN1_SCTX */ ;
+
+typedef struct mime_param_st MIME_PARAM;
+DEFINE_STACK_OF(MIME_PARAM)
+typedef struct mime_header_st MIME_HEADER;
+DEFINE_STACK_OF(MIME_HEADER)
+
+/* Month values for printing out times */
+extern const char *_asn1_mon[12];
+
+void asn1_string_embed_free(ASN1_STRING *a, int embed);
+
+int asn1_get_choice_selector(ASN1_VALUE **pval, const ASN1_ITEM *it);
+int asn1_set_choice_selector(ASN1_VALUE **pval, int value,
+                             const ASN1_ITEM *it);
+
+ASN1_VALUE **asn1_get_field_ptr(ASN1_VALUE **pval, const ASN1_TEMPLATE *tt);
+
+const ASN1_TEMPLATE *asn1_do_adb(ASN1_VALUE **pval, const ASN1_TEMPLATE *tt,
+                                 int nullerr);
+
+int asn1_do_lock(ASN1_VALUE **pval, int op, const ASN1_ITEM *it);
+
+void asn1_enc_init(ASN1_VALUE **pval, const ASN1_ITEM *it);
+void asn1_enc_free(ASN1_VALUE **pval, const ASN1_ITEM *it);
+int asn1_enc_restore(int *len, unsigned char **out, ASN1_VALUE **pval,
+                     const ASN1_ITEM *it);
+int asn1_enc_save(ASN1_VALUE **pval, const unsigned char *in, int inlen,
+                  const ASN1_ITEM *it);
+
+void asn1_primitive_free(ASN1_VALUE **pval, const ASN1_ITEM *it, int embed);
+void asn1_template_free(ASN1_VALUE **pval, const ASN1_TEMPLATE *tt);
+
+ASN1_OBJECT *c2i_ASN1_OBJECT(ASN1_OBJECT **a, const unsigned char **pp,
+                             long length);
+int i2c_ASN1_BIT_STRING(ASN1_BIT_STRING *a, unsigned char **pp);
+ASN1_BIT_STRING *c2i_ASN1_BIT_STRING(ASN1_BIT_STRING **a,
+                                     const unsigned char **pp, long length);
+int i2c_ASN1_INTEGER(ASN1_INTEGER *a, unsigned char **pp);
+ASN1_INTEGER *c2i_ASN1_INTEGER(ASN1_INTEGER **a, const unsigned char **pp,
+                               long length);
diff --git a/crypto/asn1/asn1_par.c b/crypto/asn1/asn1_par.c
new file mode 100644
index 000000000..4db3df9b8
--- /dev/null
+++ b/crypto/asn1/asn1_par.c
@@ -0,0 +1,375 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+#include 
+
+#ifndef ASN1_PARSE_MAXDEPTH
+#define ASN1_PARSE_MAXDEPTH 128
+#endif
+
+static int asn1_print_info(BIO *bp, int tag, int xclass, int constructed,
+                           int indent);
+static int asn1_parse2(BIO *bp, const unsigned char **pp, long length,
+                       int offset, int depth, int indent, int dump);
+static int asn1_print_info(BIO *bp, int tag, int xclass, int constructed,
+                           int indent)
+{
+    static const char fmt[] = "%-18s";
+    char str[128];
+    const char *p;
+
+    if (constructed & V_ASN1_CONSTRUCTED)
+        p = "cons: ";
+    else
+        p = "prim: ";
+    if (BIO_write(bp, p, 6) < 6)
+        goto err;
+    BIO_indent(bp, indent, 128);
+
+    p = str;
+    if ((xclass & V_ASN1_PRIVATE) == V_ASN1_PRIVATE)
+        BIO_snprintf(str, sizeof str, "priv [ %d ] ", tag);
+    else if ((xclass & V_ASN1_CONTEXT_SPECIFIC) == V_ASN1_CONTEXT_SPECIFIC)
+        BIO_snprintf(str, sizeof str, "cont [ %d ]", tag);
+    else if ((xclass & V_ASN1_APPLICATION) == V_ASN1_APPLICATION)
+        BIO_snprintf(str, sizeof str, "appl [ %d ]", tag);
+    else if (tag > 30)
+        BIO_snprintf(str, sizeof str, "", tag);
+    else
+        p = ASN1_tag2str(tag);
+
+    if (BIO_printf(bp, fmt, p) <= 0)
+        goto err;
+    return (1);
+ err:
+    return (0);
+}
+
+int ASN1_parse(BIO *bp, const unsigned char *pp, long len, int indent)
+{
+    return (asn1_parse2(bp, &pp, len, 0, 0, indent, 0));
+}
+
+int ASN1_parse_dump(BIO *bp, const unsigned char *pp, long len, int indent,
+                    int dump)
+{
+    return (asn1_parse2(bp, &pp, len, 0, 0, indent, dump));
+}
+
+static int asn1_parse2(BIO *bp, const unsigned char **pp, long length,
+                       int offset, int depth, int indent, int dump)
+{
+    const unsigned char *p, *ep, *tot, *op, *opp;
+    long len;
+    int tag, xclass, ret = 0;
+    int nl, hl, j, r;
+    ASN1_OBJECT *o = NULL;
+    ASN1_OCTET_STRING *os = NULL;
+    /* ASN1_BMPSTRING *bmp=NULL; */
+    int dump_indent, dump_cont = 0;
+
+    if (depth > ASN1_PARSE_MAXDEPTH) {
+            BIO_puts(bp, "BAD RECURSION DEPTH\n");
+            return 0;
+    }
+
+    dump_indent = 6;            /* Because we know BIO_dump_indent() */
+    p = *pp;
+    tot = p + length;
+    while (length > 0) {
+        op = p;
+        j = ASN1_get_object(&p, &len, &tag, &xclass, length);
+        if (j & 0x80) {
+            if (BIO_write(bp, "Error in encoding\n", 18) <= 0)
+                goto end;
+            ret = 0;
+            goto end;
+        }
+        hl = (p - op);
+        length -= hl;
+        /*
+         * if j == 0x21 it is a constructed indefinite length object
+         */
+        if (BIO_printf(bp, "%5ld:", (long)offset + (long)(op - *pp))
+            <= 0)
+            goto end;
+
+        if (j != (V_ASN1_CONSTRUCTED | 1)) {
+            if (BIO_printf(bp, "d=%-2d hl=%ld l=%4ld ",
+                           depth, (long)hl, len) <= 0)
+                goto end;
+        } else {
+            if (BIO_printf(bp, "d=%-2d hl=%ld l=inf  ", depth, (long)hl) <= 0)
+                goto end;
+        }
+        if (!asn1_print_info(bp, tag, xclass, j, (indent) ? depth : 0))
+            goto end;
+        if (j & V_ASN1_CONSTRUCTED) {
+            const unsigned char *sp = p;
+
+            ep = p + len;
+            if (BIO_write(bp, "\n", 1) <= 0)
+                goto end;
+            if (len > length) {
+                BIO_printf(bp, "length is greater than %ld\n", length);
+                ret = 0;
+                goto end;
+            }
+            if ((j == 0x21) && (len == 0)) {
+                for (;;) {
+                    r = asn1_parse2(bp, &p, (long)(tot - p),
+                                    offset + (p - *pp), depth + 1,
+                                    indent, dump);
+                    if (r == 0) {
+                        ret = 0;
+                        goto end;
+                    }
+                    if ((r == 2) || (p >= tot)) {
+                        len = p - sp;
+                        break;
+                    }
+                }
+            } else {
+                long tmp = len;
+
+                while (p < ep) {
+                    sp = p;
+                    r = asn1_parse2(bp, &p, tmp,
+                                    offset + (p - *pp), depth + 1,
+                                    indent, dump);
+                    if (r == 0) {
+                        ret = 0;
+                        goto end;
+                    }
+                    tmp -= p - sp;
+                }
+            }
+        } else if (xclass != 0) {
+            p += len;
+            if (BIO_write(bp, "\n", 1) <= 0)
+                goto end;
+        } else {
+            nl = 0;
+            if ((tag == V_ASN1_PRINTABLESTRING) ||
+                (tag == V_ASN1_T61STRING) ||
+                (tag == V_ASN1_IA5STRING) ||
+                (tag == V_ASN1_VISIBLESTRING) ||
+                (tag == V_ASN1_NUMERICSTRING) ||
+                (tag == V_ASN1_UTF8STRING) ||
+                (tag == V_ASN1_UTCTIME) || (tag == V_ASN1_GENERALIZEDTIME)) {
+                if (BIO_write(bp, ":", 1) <= 0)
+                    goto end;
+                if ((len > 0) && BIO_write(bp, (const char *)p, (int)len)
+                    != (int)len)
+                    goto end;
+            } else if (tag == V_ASN1_OBJECT) {
+                opp = op;
+                if (d2i_ASN1_OBJECT(&o, &opp, len + hl) != NULL) {
+                    if (BIO_write(bp, ":", 1) <= 0)
+                        goto end;
+                    i2a_ASN1_OBJECT(bp, o);
+                } else {
+                    if (BIO_puts(bp, ":BAD OBJECT") <= 0)
+                        goto end;
+                    dump_cont = 1;
+                }
+            } else if (tag == V_ASN1_BOOLEAN) {
+                if (len != 1) {
+                    if (BIO_puts(bp, ":BAD BOOLEAN") <= 0)
+                        goto end;
+                    dump_cont = 1;
+                }
+                if (len > 0)
+                    BIO_printf(bp, ":%u", p[0]);
+            } else if (tag == V_ASN1_BMPSTRING) {
+                /* do the BMP thang */
+            } else if (tag == V_ASN1_OCTET_STRING) {
+                int i, printable = 1;
+
+                opp = op;
+                os = d2i_ASN1_OCTET_STRING(NULL, &opp, len + hl);
+                if (os != NULL && os->length > 0) {
+                    opp = os->data;
+                    /*
+                     * testing whether the octet string is printable
+                     */
+                    for (i = 0; i < os->length; i++) {
+                        if (((opp[i] < ' ') &&
+                             (opp[i] != '\n') &&
+                             (opp[i] != '\r') &&
+                             (opp[i] != '\t')) || (opp[i] > '~')) {
+                            printable = 0;
+                            break;
+                        }
+                    }
+                    if (printable)
+                        /* printable string */
+                    {
+                        if (BIO_write(bp, ":", 1) <= 0)
+                            goto end;
+                        if (BIO_write(bp, (const char *)opp, os->length) <= 0)
+                            goto end;
+                    } else if (!dump)
+                        /*
+                         * not printable => print octet string as hex dump
+                         */
+                    {
+                        if (BIO_write(bp, "[HEX DUMP]:", 11) <= 0)
+                            goto end;
+                        for (i = 0; i < os->length; i++) {
+                            if (BIO_printf(bp, "%02X", opp[i]) <= 0)
+                                goto end;
+                        }
+                    } else
+                        /* print the normal dump */
+                    {
+                        if (!nl) {
+                            if (BIO_write(bp, "\n", 1) <= 0)
+                                goto end;
+                        }
+                        if (BIO_dump_indent(bp,
+                                            (const char *)opp,
+                                            ((dump == -1 || dump >
+                                              os->
+                                              length) ? os->length : dump),
+                                            dump_indent) <= 0)
+                            goto end;
+                        nl = 1;
+                    }
+                }
+                ASN1_OCTET_STRING_free(os);
+                os = NULL;
+            } else if (tag == V_ASN1_INTEGER) {
+                ASN1_INTEGER *bs;
+                int i;
+
+                opp = op;
+                bs = d2i_ASN1_INTEGER(NULL, &opp, len + hl);
+                if (bs != NULL) {
+                    if (BIO_write(bp, ":", 1) <= 0)
+                        goto end;
+                    if (bs->type == V_ASN1_NEG_INTEGER)
+                        if (BIO_write(bp, "-", 1) <= 0)
+                            goto end;
+                    for (i = 0; i < bs->length; i++) {
+                        if (BIO_printf(bp, "%02X", bs->data[i]) <= 0)
+                            goto end;
+                    }
+                    if (bs->length == 0) {
+                        if (BIO_write(bp, "00", 2) <= 0)
+                            goto end;
+                    }
+                } else {
+                    if (BIO_puts(bp, ":BAD INTEGER") <= 0)
+                        goto end;
+                    dump_cont = 1;
+                }
+                ASN1_INTEGER_free(bs);
+            } else if (tag == V_ASN1_ENUMERATED) {
+                ASN1_ENUMERATED *bs;
+                int i;
+
+                opp = op;
+                bs = d2i_ASN1_ENUMERATED(NULL, &opp, len + hl);
+                if (bs != NULL) {
+                    if (BIO_write(bp, ":", 1) <= 0)
+                        goto end;
+                    if (bs->type == V_ASN1_NEG_ENUMERATED)
+                        if (BIO_write(bp, "-", 1) <= 0)
+                            goto end;
+                    for (i = 0; i < bs->length; i++) {
+                        if (BIO_printf(bp, "%02X", bs->data[i]) <= 0)
+                            goto end;
+                    }
+                    if (bs->length == 0) {
+                        if (BIO_write(bp, "00", 2) <= 0)
+                            goto end;
+                    }
+                } else {
+                    if (BIO_puts(bp, ":BAD ENUMERATED") <= 0)
+                        goto end;
+                    dump_cont = 1;
+                }
+                ASN1_ENUMERATED_free(bs);
+            } else if (len > 0 && dump) {
+                if (!nl) {
+                    if (BIO_write(bp, "\n", 1) <= 0)
+                        goto end;
+                }
+                if (BIO_dump_indent(bp, (const char *)p,
+                                    ((dump == -1 || dump > len) ? len : dump),
+                                    dump_indent) <= 0)
+                    goto end;
+                nl = 1;
+            }
+            if (dump_cont) {
+                int i;
+                const unsigned char *tmp = op + hl;
+                if (BIO_puts(bp, ":[") <= 0)
+                    goto end;
+                for (i = 0; i < len; i++) {
+                    if (BIO_printf(bp, "%02X", tmp[i]) <= 0)
+                        goto end;
+                }
+                if (BIO_puts(bp, "]") <= 0)
+                    goto end;
+            }
+
+            if (!nl) {
+                if (BIO_write(bp, "\n", 1) <= 0)
+                    goto end;
+            }
+            p += len;
+            if ((tag == V_ASN1_EOC) && (xclass == 0)) {
+                ret = 2;        /* End of sequence */
+                goto end;
+            }
+        }
+        length -= len;
+    }
+    ret = 1;
+ end:
+    ASN1_OBJECT_free(o);
+    ASN1_OCTET_STRING_free(os);
+    *pp = p;
+    return (ret);
+}
+
+const char *ASN1_tag2str(int tag)
+{
+    static const char *const tag2str[] = {
+        /* 0-4 */
+        "EOC", "BOOLEAN", "INTEGER", "BIT STRING", "OCTET STRING",
+        /* 5-9 */
+        "NULL", "OBJECT", "OBJECT DESCRIPTOR", "EXTERNAL", "REAL",
+        /* 10-13 */
+        "ENUMERATED", "", "UTF8STRING", "",
+        /* 15-17 */
+        "", "", "SEQUENCE", "SET",
+        /* 18-20 */
+        "NUMERICSTRING", "PRINTABLESTRING", "T61STRING",
+        /* 21-24 */
+        "VIDEOTEXSTRING", "IA5STRING", "UTCTIME", "GENERALIZEDTIME",
+        /* 25-27 */
+        "GRAPHICSTRING", "VISIBLESTRING", "GENERALSTRING",
+        /* 28-30 */
+        "UNIVERSALSTRING", "", "BMPSTRING"
+    };
+
+    if ((tag == V_ASN1_NEG_INTEGER) || (tag == V_ASN1_NEG_ENUMERATED))
+        tag &= ~0x100;
+
+    if (tag < 0 || tag > 30)
+        return "(unknown)";
+    return tag2str[tag];
+}
diff --git a/crypto/asn1/asn_mime.c b/crypto/asn1/asn_mime.c
new file mode 100644
index 000000000..d7ec801b1
--- /dev/null
+++ b/crypto/asn1/asn_mime.c
@@ -0,0 +1,980 @@
+/*
+ * Copyright 2008-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+#include 
+#include 
+#include "internal/evp_int.h"
+#include "internal/bio.h"
+#include "asn1_locl.h"
+
+/*
+ * Generalised MIME like utilities for streaming ASN1. Although many have a
+ * PKCS7/CMS like flavour others are more general purpose.
+ */
+
+/*
+ * MIME format structures Note that all are translated to lower case apart
+ * from parameter values. Quotes are stripped off
+ */
+
+struct mime_param_st {
+    char *param_name;           /* Param name e.g. "micalg" */
+    char *param_value;          /* Param value e.g. "sha1" */
+};
+
+struct mime_header_st {
+    char *name;                 /* Name of line e.g. "content-type" */
+    char *value;                /* Value of line e.g. "text/plain" */
+    STACK_OF(MIME_PARAM) *params; /* Zero or more parameters */
+};
+
+static int asn1_output_data(BIO *out, BIO *data, ASN1_VALUE *val, int flags,
+                            const ASN1_ITEM *it);
+static char *strip_ends(char *name);
+static char *strip_start(char *name);
+static char *strip_end(char *name);
+static MIME_HEADER *mime_hdr_new(const char *name, const char *value);
+static int mime_hdr_addparam(MIME_HEADER *mhdr, const char *name, const char *value);
+static STACK_OF(MIME_HEADER) *mime_parse_hdr(BIO *bio);
+static int mime_hdr_cmp(const MIME_HEADER *const *a,
+                        const MIME_HEADER *const *b);
+static int mime_param_cmp(const MIME_PARAM *const *a,
+                          const MIME_PARAM *const *b);
+static void mime_param_free(MIME_PARAM *param);
+static int mime_bound_check(char *line, int linelen, const char *bound, int blen);
+static int multi_split(BIO *bio, const char *bound, STACK_OF(BIO) **ret);
+static int strip_eol(char *linebuf, int *plen, int flags);
+static MIME_HEADER *mime_hdr_find(STACK_OF(MIME_HEADER) *hdrs, const char *name);
+static MIME_PARAM *mime_param_find(MIME_HEADER *hdr, const char *name);
+static void mime_hdr_free(MIME_HEADER *hdr);
+
+#define MAX_SMLEN 1024
+#define mime_debug(x)           /* x */
+
+/* Output an ASN1 structure in BER format streaming if necessary */
+
+int i2d_ASN1_bio_stream(BIO *out, ASN1_VALUE *val, BIO *in, int flags,
+                        const ASN1_ITEM *it)
+{
+    /* If streaming create stream BIO and copy all content through it */
+    if (flags & SMIME_STREAM) {
+        BIO *bio, *tbio;
+        bio = BIO_new_NDEF(out, val, it);
+        if (!bio) {
+            ASN1err(ASN1_F_I2D_ASN1_BIO_STREAM, ERR_R_MALLOC_FAILURE);
+            return 0;
+        }
+        SMIME_crlf_copy(in, bio, flags);
+        (void)BIO_flush(bio);
+        /* Free up successive BIOs until we hit the old output BIO */
+        do {
+            tbio = BIO_pop(bio);
+            BIO_free(bio);
+            bio = tbio;
+        } while (bio != out);
+    }
+    /*
+     * else just write out ASN1 structure which will have all content stored
+     * internally
+     */
+    else
+        ASN1_item_i2d_bio(it, out, val);
+    return 1;
+}
+
+/* Base 64 read and write of ASN1 structure */
+
+static int B64_write_ASN1(BIO *out, ASN1_VALUE *val, BIO *in, int flags,
+                          const ASN1_ITEM *it)
+{
+    BIO *b64;
+    int r;
+    b64 = BIO_new(BIO_f_base64());
+    if (b64 == NULL) {
+        ASN1err(ASN1_F_B64_WRITE_ASN1, ERR_R_MALLOC_FAILURE);
+        return 0;
+    }
+    /*
+     * prepend the b64 BIO so all data is base64 encoded.
+     */
+    out = BIO_push(b64, out);
+    r = i2d_ASN1_bio_stream(out, val, in, flags, it);
+    (void)BIO_flush(out);
+    BIO_pop(out);
+    BIO_free(b64);
+    return r;
+}
+
+/* Streaming ASN1 PEM write */
+
+int PEM_write_bio_ASN1_stream(BIO *out, ASN1_VALUE *val, BIO *in, int flags,
+                              const char *hdr, const ASN1_ITEM *it)
+{
+    int r;
+    BIO_printf(out, "-----BEGIN %s-----\n", hdr);
+    r = B64_write_ASN1(out, val, in, flags, it);
+    BIO_printf(out, "-----END %s-----\n", hdr);
+    return r;
+}
+
+static ASN1_VALUE *b64_read_asn1(BIO *bio, const ASN1_ITEM *it)
+{
+    BIO *b64;
+    ASN1_VALUE *val;
+
+    if ((b64 = BIO_new(BIO_f_base64())) == NULL) {
+        ASN1err(ASN1_F_B64_READ_ASN1, ERR_R_MALLOC_FAILURE);
+        return 0;
+    }
+    bio = BIO_push(b64, bio);
+    val = ASN1_item_d2i_bio(it, bio, NULL);
+    if (!val)
+        ASN1err(ASN1_F_B64_READ_ASN1, ASN1_R_DECODE_ERROR);
+    (void)BIO_flush(bio);
+    BIO_pop(bio);
+    BIO_free(b64);
+    return val;
+}
+
+/* Generate the MIME "micalg" parameter from RFC3851, RFC4490 */
+
+static int asn1_write_micalg(BIO *out, STACK_OF(X509_ALGOR) *mdalgs)
+{
+    const EVP_MD *md;
+    int i, have_unknown = 0, write_comma, ret = 0, md_nid;
+    have_unknown = 0;
+    write_comma = 0;
+    for (i = 0; i < sk_X509_ALGOR_num(mdalgs); i++) {
+        if (write_comma)
+            BIO_write(out, ",", 1);
+        write_comma = 1;
+        md_nid = OBJ_obj2nid(sk_X509_ALGOR_value(mdalgs, i)->algorithm);
+        md = EVP_get_digestbynid(md_nid);
+        if (md && md->md_ctrl) {
+            int rv;
+            char *micstr;
+            rv = md->md_ctrl(NULL, EVP_MD_CTRL_MICALG, 0, &micstr);
+            if (rv > 0) {
+                BIO_puts(out, micstr);
+                OPENSSL_free(micstr);
+                continue;
+            }
+            if (rv != -2)
+                goto err;
+        }
+        switch (md_nid) {
+        case NID_sha1:
+            BIO_puts(out, "sha1");
+            break;
+
+        case NID_md5:
+            BIO_puts(out, "md5");
+            break;
+
+        case NID_sha256:
+            BIO_puts(out, "sha-256");
+            break;
+
+        case NID_sha384:
+            BIO_puts(out, "sha-384");
+            break;
+
+        case NID_sha512:
+            BIO_puts(out, "sha-512");
+            break;
+
+        case NID_id_GostR3411_94:
+            BIO_puts(out, "gostr3411-94");
+            goto err;
+
+        default:
+            if (have_unknown)
+                write_comma = 0;
+            else {
+                BIO_puts(out, "unknown");
+                have_unknown = 1;
+            }
+            break;
+
+        }
+    }
+
+    ret = 1;
+ err:
+
+    return ret;
+
+}
+
+/* SMIME sender */
+
+int SMIME_write_ASN1(BIO *bio, ASN1_VALUE *val, BIO *data, int flags,
+                     int ctype_nid, int econt_nid,
+                     STACK_OF(X509_ALGOR) *mdalgs, const ASN1_ITEM *it)
+{
+    char bound[33], c;
+    int i;
+    const char *mime_prefix, *mime_eol, *cname = "smime.p7m";
+    const char *msg_type = NULL;
+    if (flags & SMIME_OLDMIME)
+        mime_prefix = "application/x-pkcs7-";
+    else
+        mime_prefix = "application/pkcs7-";
+
+    if (flags & SMIME_CRLFEOL)
+        mime_eol = "\r\n";
+    else
+        mime_eol = "\n";
+    if ((flags & SMIME_DETACHED) && data) {
+        /* We want multipart/signed */
+        /* Generate a random boundary */
+        if (RAND_bytes((unsigned char *)bound, 32) <= 0)
+            return 0;
+        for (i = 0; i < 32; i++) {
+            c = bound[i] & 0xf;
+            if (c < 10)
+                c += '0';
+            else
+                c += 'A' - 10;
+            bound[i] = c;
+        }
+        bound[32] = 0;
+        BIO_printf(bio, "MIME-Version: 1.0%s", mime_eol);
+        BIO_printf(bio, "Content-Type: multipart/signed;");
+        BIO_printf(bio, " protocol=\"%ssignature\";", mime_prefix);
+        BIO_puts(bio, " micalg=\"");
+        asn1_write_micalg(bio, mdalgs);
+        BIO_printf(bio, "\"; boundary=\"----%s\"%s%s",
+                   bound, mime_eol, mime_eol);
+        BIO_printf(bio, "This is an S/MIME signed message%s%s",
+                   mime_eol, mime_eol);
+        /* Now write out the first part */
+        BIO_printf(bio, "------%s%s", bound, mime_eol);
+        if (!asn1_output_data(bio, data, val, flags, it))
+            return 0;
+        BIO_printf(bio, "%s------%s%s", mime_eol, bound, mime_eol);
+
+        /* Headers for signature */
+
+        BIO_printf(bio, "Content-Type: %ssignature;", mime_prefix);
+        BIO_printf(bio, " name=\"smime.p7s\"%s", mime_eol);
+        BIO_printf(bio, "Content-Transfer-Encoding: base64%s", mime_eol);
+        BIO_printf(bio, "Content-Disposition: attachment;");
+        BIO_printf(bio, " filename=\"smime.p7s\"%s%s", mime_eol, mime_eol);
+        B64_write_ASN1(bio, val, NULL, 0, it);
+        BIO_printf(bio, "%s------%s--%s%s", mime_eol, bound,
+                   mime_eol, mime_eol);
+        return 1;
+    }
+
+    /* Determine smime-type header */
+
+    if (ctype_nid == NID_pkcs7_enveloped)
+        msg_type = "enveloped-data";
+    else if (ctype_nid == NID_pkcs7_signed) {
+        if (econt_nid == NID_id_smime_ct_receipt)
+            msg_type = "signed-receipt";
+        else if (sk_X509_ALGOR_num(mdalgs) >= 0)
+            msg_type = "signed-data";
+        else
+            msg_type = "certs-only";
+    } else if (ctype_nid == NID_id_smime_ct_compressedData) {
+        msg_type = "compressed-data";
+        cname = "smime.p7z";
+    }
+    /* MIME headers */
+    BIO_printf(bio, "MIME-Version: 1.0%s", mime_eol);
+    BIO_printf(bio, "Content-Disposition: attachment;");
+    BIO_printf(bio, " filename=\"%s\"%s", cname, mime_eol);
+    BIO_printf(bio, "Content-Type: %smime;", mime_prefix);
+    if (msg_type)
+        BIO_printf(bio, " smime-type=%s;", msg_type);
+    BIO_printf(bio, " name=\"%s\"%s", cname, mime_eol);
+    BIO_printf(bio, "Content-Transfer-Encoding: base64%s%s",
+               mime_eol, mime_eol);
+    if (!B64_write_ASN1(bio, val, data, flags, it))
+        return 0;
+    BIO_printf(bio, "%s", mime_eol);
+    return 1;
+}
+
+/* Handle output of ASN1 data */
+
+static int asn1_output_data(BIO *out, BIO *data, ASN1_VALUE *val, int flags,
+                            const ASN1_ITEM *it)
+{
+    BIO *tmpbio;
+    const ASN1_AUX *aux = it->funcs;
+    ASN1_STREAM_ARG sarg;
+    int rv = 1;
+
+    /*
+     * If data is not detached or resigning then the output BIO is already
+     * set up to finalise when it is written through.
+     */
+    if (!(flags & SMIME_DETACHED) || (flags & PKCS7_REUSE_DIGEST)) {
+        SMIME_crlf_copy(data, out, flags);
+        return 1;
+    }
+
+    if (!aux || !aux->asn1_cb) {
+        ASN1err(ASN1_F_ASN1_OUTPUT_DATA, ASN1_R_STREAMING_NOT_SUPPORTED);
+        return 0;
+    }
+
+    sarg.out = out;
+    sarg.ndef_bio = NULL;
+    sarg.boundary = NULL;
+
+    /* Let ASN1 code prepend any needed BIOs */
+
+    if (aux->asn1_cb(ASN1_OP_DETACHED_PRE, &val, it, &sarg) <= 0)
+        return 0;
+
+    /* Copy data across, passing through filter BIOs for processing */
+    SMIME_crlf_copy(data, sarg.ndef_bio, flags);
+
+    /* Finalize structure */
+    if (aux->asn1_cb(ASN1_OP_DETACHED_POST, &val, it, &sarg) <= 0)
+        rv = 0;
+
+    /* Now remove any digests prepended to the BIO */
+
+    while (sarg.ndef_bio != out) {
+        tmpbio = BIO_pop(sarg.ndef_bio);
+        BIO_free(sarg.ndef_bio);
+        sarg.ndef_bio = tmpbio;
+    }
+
+    return rv;
+
+}
+
+/*
+ * SMIME reader: handle multipart/signed and opaque signing. in multipart
+ * case the content is placed in a memory BIO pointed to by "bcont". In
+ * opaque this is set to NULL
+ */
+
+ASN1_VALUE *SMIME_read_ASN1(BIO *bio, BIO **bcont, const ASN1_ITEM *it)
+{
+    BIO *asnin;
+    STACK_OF(MIME_HEADER) *headers = NULL;
+    STACK_OF(BIO) *parts = NULL;
+    MIME_HEADER *hdr;
+    MIME_PARAM *prm;
+    ASN1_VALUE *val;
+    int ret;
+
+    if (bcont)
+        *bcont = NULL;
+
+    if ((headers = mime_parse_hdr(bio)) == NULL) {
+        ASN1err(ASN1_F_SMIME_READ_ASN1, ASN1_R_MIME_PARSE_ERROR);
+        return NULL;
+    }
+
+    if ((hdr = mime_hdr_find(headers, "content-type")) == NULL
+        || hdr->value == NULL) {
+        sk_MIME_HEADER_pop_free(headers, mime_hdr_free);
+        ASN1err(ASN1_F_SMIME_READ_ASN1, ASN1_R_NO_CONTENT_TYPE);
+        return NULL;
+    }
+
+    /* Handle multipart/signed */
+
+    if (strcmp(hdr->value, "multipart/signed") == 0) {
+        /* Split into two parts */
+        prm = mime_param_find(hdr, "boundary");
+        if (!prm || !prm->param_value) {
+            sk_MIME_HEADER_pop_free(headers, mime_hdr_free);
+            ASN1err(ASN1_F_SMIME_READ_ASN1, ASN1_R_NO_MULTIPART_BOUNDARY);
+            return NULL;
+        }
+        ret = multi_split(bio, prm->param_value, &parts);
+        sk_MIME_HEADER_pop_free(headers, mime_hdr_free);
+        if (!ret || (sk_BIO_num(parts) != 2)) {
+            ASN1err(ASN1_F_SMIME_READ_ASN1, ASN1_R_NO_MULTIPART_BODY_FAILURE);
+            sk_BIO_pop_free(parts, BIO_vfree);
+            return NULL;
+        }
+
+        /* Parse the signature piece */
+        asnin = sk_BIO_value(parts, 1);
+
+        if ((headers = mime_parse_hdr(asnin)) == NULL) {
+            ASN1err(ASN1_F_SMIME_READ_ASN1, ASN1_R_MIME_SIG_PARSE_ERROR);
+            sk_BIO_pop_free(parts, BIO_vfree);
+            return NULL;
+        }
+
+        /* Get content type */
+
+        if ((hdr = mime_hdr_find(headers, "content-type")) == NULL
+            || hdr->value == NULL) {
+            sk_MIME_HEADER_pop_free(headers, mime_hdr_free);
+            ASN1err(ASN1_F_SMIME_READ_ASN1, ASN1_R_NO_SIG_CONTENT_TYPE);
+            return NULL;
+        }
+
+        if (strcmp(hdr->value, "application/x-pkcs7-signature") &&
+            strcmp(hdr->value, "application/pkcs7-signature")) {
+            ASN1err(ASN1_F_SMIME_READ_ASN1, ASN1_R_SIG_INVALID_MIME_TYPE);
+            ERR_add_error_data(2, "type: ", hdr->value);
+            sk_MIME_HEADER_pop_free(headers, mime_hdr_free);
+            sk_BIO_pop_free(parts, BIO_vfree);
+            return NULL;
+        }
+        sk_MIME_HEADER_pop_free(headers, mime_hdr_free);
+        /* Read in ASN1 */
+        if ((val = b64_read_asn1(asnin, it)) == NULL) {
+            ASN1err(ASN1_F_SMIME_READ_ASN1, ASN1_R_ASN1_SIG_PARSE_ERROR);
+            sk_BIO_pop_free(parts, BIO_vfree);
+            return NULL;
+        }
+
+        if (bcont) {
+            *bcont = sk_BIO_value(parts, 0);
+            BIO_free(asnin);
+            sk_BIO_free(parts);
+        } else
+            sk_BIO_pop_free(parts, BIO_vfree);
+        return val;
+    }
+
+    /* OK, if not multipart/signed try opaque signature */
+
+    if (strcmp(hdr->value, "application/x-pkcs7-mime") &&
+        strcmp(hdr->value, "application/pkcs7-mime")) {
+        ASN1err(ASN1_F_SMIME_READ_ASN1, ASN1_R_INVALID_MIME_TYPE);
+        ERR_add_error_data(2, "type: ", hdr->value);
+        sk_MIME_HEADER_pop_free(headers, mime_hdr_free);
+        return NULL;
+    }
+
+    sk_MIME_HEADER_pop_free(headers, mime_hdr_free);
+
+    if ((val = b64_read_asn1(bio, it)) == NULL) {
+        ASN1err(ASN1_F_SMIME_READ_ASN1, ASN1_R_ASN1_PARSE_ERROR);
+        return NULL;
+    }
+    return val;
+
+}
+
+/* Copy text from one BIO to another making the output CRLF at EOL */
+int SMIME_crlf_copy(BIO *in, BIO *out, int flags)
+{
+    BIO *bf;
+    char eol;
+    int len;
+    char linebuf[MAX_SMLEN];
+    /*
+     * Buffer output so we don't write one line at a time. This is useful
+     * when streaming as we don't end up with one OCTET STRING per line.
+     */
+    bf = BIO_new(BIO_f_buffer());
+    if (bf == NULL)
+        return 0;
+    out = BIO_push(bf, out);
+    if (flags & SMIME_BINARY) {
+        while ((len = BIO_read(in, linebuf, MAX_SMLEN)) > 0)
+            BIO_write(out, linebuf, len);
+    } else {
+        int eolcnt = 0;
+        if (flags & SMIME_TEXT)
+            BIO_printf(out, "Content-Type: text/plain\r\n\r\n");
+        while ((len = BIO_gets(in, linebuf, MAX_SMLEN)) > 0) {
+            eol = strip_eol(linebuf, &len, flags);
+            if (len) {
+                /* Not EOF: write out all CRLF */
+                if (flags & SMIME_ASCIICRLF) {
+                    int i;
+                    for (i = 0; i < eolcnt; i++)
+                        BIO_write(out, "\r\n", 2);
+                    eolcnt = 0;
+                }
+                BIO_write(out, linebuf, len);
+                if (eol)
+                    BIO_write(out, "\r\n", 2);
+            } else if (flags & SMIME_ASCIICRLF)
+                eolcnt++;
+            else if (eol)
+                BIO_write(out, "\r\n", 2);
+        }
+    }
+    (void)BIO_flush(out);
+    BIO_pop(out);
+    BIO_free(bf);
+    return 1;
+}
+
+/* Strip off headers if they are text/plain */
+int SMIME_text(BIO *in, BIO *out)
+{
+    char iobuf[4096];
+    int len;
+    STACK_OF(MIME_HEADER) *headers;
+    MIME_HEADER *hdr;
+
+    if ((headers = mime_parse_hdr(in)) == NULL) {
+        ASN1err(ASN1_F_SMIME_TEXT, ASN1_R_MIME_PARSE_ERROR);
+        return 0;
+    }
+    if ((hdr = mime_hdr_find(headers, "content-type")) == NULL
+        || hdr->value == NULL) {
+        ASN1err(ASN1_F_SMIME_TEXT, ASN1_R_MIME_NO_CONTENT_TYPE);
+        sk_MIME_HEADER_pop_free(headers, mime_hdr_free);
+        return 0;
+    }
+    if (strcmp(hdr->value, "text/plain")) {
+        ASN1err(ASN1_F_SMIME_TEXT, ASN1_R_INVALID_MIME_TYPE);
+        ERR_add_error_data(2, "type: ", hdr->value);
+        sk_MIME_HEADER_pop_free(headers, mime_hdr_free);
+        return 0;
+    }
+    sk_MIME_HEADER_pop_free(headers, mime_hdr_free);
+    while ((len = BIO_read(in, iobuf, sizeof(iobuf))) > 0)
+        BIO_write(out, iobuf, len);
+    if (len < 0)
+        return 0;
+    return 1;
+}
+
+/*
+ * Split a multipart/XXX message body into component parts: result is
+ * canonical parts in a STACK of bios
+ */
+
+static int multi_split(BIO *bio, const char *bound, STACK_OF(BIO) **ret)
+{
+    char linebuf[MAX_SMLEN];
+    int len, blen;
+    int eol = 0, next_eol = 0;
+    BIO *bpart = NULL;
+    STACK_OF(BIO) *parts;
+    char state, part, first;
+
+    blen = strlen(bound);
+    part = 0;
+    state = 0;
+    first = 1;
+    parts = sk_BIO_new_null();
+    *ret = parts;
+    if (*ret == NULL)
+        return 0;
+    while ((len = BIO_gets(bio, linebuf, MAX_SMLEN)) > 0) {
+        state = mime_bound_check(linebuf, len, bound, blen);
+        if (state == 1) {
+            first = 1;
+            part++;
+        } else if (state == 2) {
+            if (!sk_BIO_push(parts, bpart)) {
+                BIO_free(bpart);
+                return 0;
+            }
+            return 1;
+        } else if (part) {
+            /* Strip CR+LF from linebuf */
+            next_eol = strip_eol(linebuf, &len, 0);
+            if (first) {
+                first = 0;
+                if (bpart)
+                    if (!sk_BIO_push(parts, bpart)) {
+                        BIO_free(bpart);
+                        return 0;
+                    }
+                bpart = BIO_new(BIO_s_mem());
+                if (bpart == NULL)
+                    return 0;
+                BIO_set_mem_eof_return(bpart, 0);
+            } else if (eol)
+                BIO_write(bpart, "\r\n", 2);
+            eol = next_eol;
+            if (len)
+                BIO_write(bpart, linebuf, len);
+        }
+    }
+    BIO_free(bpart);
+    return 0;
+}
+
+/* This is the big one: parse MIME header lines up to message body */
+
+#define MIME_INVALID    0
+#define MIME_START      1
+#define MIME_TYPE       2
+#define MIME_NAME       3
+#define MIME_VALUE      4
+#define MIME_QUOTE      5
+#define MIME_COMMENT    6
+
+static STACK_OF(MIME_HEADER) *mime_parse_hdr(BIO *bio)
+{
+    char *p, *q, c;
+    char *ntmp;
+    char linebuf[MAX_SMLEN];
+    MIME_HEADER *mhdr = NULL, *new_hdr = NULL;
+    STACK_OF(MIME_HEADER) *headers;
+    int len, state, save_state = 0;
+
+    headers = sk_MIME_HEADER_new(mime_hdr_cmp);
+    if (headers == NULL)
+        return NULL;
+    while ((len = BIO_gets(bio, linebuf, MAX_SMLEN)) > 0) {
+        /* If whitespace at line start then continuation line */
+        if (mhdr && isspace((unsigned char)linebuf[0]))
+            state = MIME_NAME;
+        else
+            state = MIME_START;
+        ntmp = NULL;
+        /* Go through all characters */
+        for (p = linebuf, q = linebuf; (c = *p) && (c != '\r') && (c != '\n');
+             p++) {
+
+            /*
+             * State machine to handle MIME headers if this looks horrible
+             * that's because it *is*
+             */
+
+            switch (state) {
+            case MIME_START:
+                if (c == ':') {
+                    state = MIME_TYPE;
+                    *p = 0;
+                    ntmp = strip_ends(q);
+                    q = p + 1;
+                }
+                break;
+
+            case MIME_TYPE:
+                if (c == ';') {
+                    mime_debug("Found End Value\n");
+                    *p = 0;
+                    new_hdr = mime_hdr_new(ntmp, strip_ends(q));
+                    if (new_hdr == NULL)
+                        goto err;
+                    if (!sk_MIME_HEADER_push(headers, new_hdr))
+                        goto err;
+                    mhdr = new_hdr;
+                    new_hdr = NULL;
+                    ntmp = NULL;
+                    q = p + 1;
+                    state = MIME_NAME;
+                } else if (c == '(') {
+                    save_state = state;
+                    state = MIME_COMMENT;
+                }
+                break;
+
+            case MIME_COMMENT:
+                if (c == ')') {
+                    state = save_state;
+                }
+                break;
+
+            case MIME_NAME:
+                if (c == '=') {
+                    state = MIME_VALUE;
+                    *p = 0;
+                    ntmp = strip_ends(q);
+                    q = p + 1;
+                }
+                break;
+
+            case MIME_VALUE:
+                if (c == ';') {
+                    state = MIME_NAME;
+                    *p = 0;
+                    mime_hdr_addparam(mhdr, ntmp, strip_ends(q));
+                    ntmp = NULL;
+                    q = p + 1;
+                } else if (c == '"') {
+                    mime_debug("Found Quote\n");
+                    state = MIME_QUOTE;
+                } else if (c == '(') {
+                    save_state = state;
+                    state = MIME_COMMENT;
+                }
+                break;
+
+            case MIME_QUOTE:
+                if (c == '"') {
+                    mime_debug("Found Match Quote\n");
+                    state = MIME_VALUE;
+                }
+                break;
+            }
+        }
+
+        if (state == MIME_TYPE) {
+            new_hdr = mime_hdr_new(ntmp, strip_ends(q));
+            if (new_hdr == NULL)
+                goto err;
+            if (!sk_MIME_HEADER_push(headers, new_hdr))
+                goto err;
+            mhdr = new_hdr;
+            new_hdr = NULL;
+        } else if (state == MIME_VALUE)
+            mime_hdr_addparam(mhdr, ntmp, strip_ends(q));
+        if (p == linebuf)
+            break;              /* Blank line means end of headers */
+    }
+
+    return headers;
+
+err:
+    mime_hdr_free(new_hdr);
+    sk_MIME_HEADER_pop_free(headers, mime_hdr_free);
+    return NULL;
+}
+
+static char *strip_ends(char *name)
+{
+    return strip_end(strip_start(name));
+}
+
+/* Strip a parameter of whitespace from start of param */
+static char *strip_start(char *name)
+{
+    char *p, c;
+    /* Look for first non white space or quote */
+    for (p = name; (c = *p); p++) {
+        if (c == '"') {
+            /* Next char is start of string if non null */
+            if (p[1])
+                return p + 1;
+            /* Else null string */
+            return NULL;
+        }
+        if (!isspace((unsigned char)c))
+            return p;
+    }
+    return NULL;
+}
+
+/* As above but strip from end of string : maybe should handle brackets? */
+static char *strip_end(char *name)
+{
+    char *p, c;
+    if (!name)
+        return NULL;
+    /* Look for first non white space or quote */
+    for (p = name + strlen(name) - 1; p >= name; p--) {
+        c = *p;
+        if (c == '"') {
+            if (p - 1 == name)
+                return NULL;
+            *p = 0;
+            return name;
+        }
+        if (isspace((unsigned char)c))
+            *p = 0;
+        else
+            return name;
+    }
+    return NULL;
+}
+
+static MIME_HEADER *mime_hdr_new(const char *name, const char *value)
+{
+    MIME_HEADER *mhdr = NULL;
+    char *tmpname = NULL, *tmpval = NULL, *p;
+    int c;
+
+    if (name) {
+        if ((tmpname = OPENSSL_strdup(name)) == NULL)
+            return NULL;
+        for (p = tmpname; *p; p++) {
+            c = (unsigned char)*p;
+            if (isupper(c)) {
+                c = tolower(c);
+                *p = c;
+            }
+        }
+    }
+    if (value) {
+        if ((tmpval = OPENSSL_strdup(value)) == NULL)
+            goto err;
+        for (p = tmpval; *p; p++) {
+            c = (unsigned char)*p;
+            if (isupper(c)) {
+                c = tolower(c);
+                *p = c;
+            }
+        }
+    }
+    mhdr = OPENSSL_malloc(sizeof(*mhdr));
+    if (mhdr == NULL)
+        goto err;
+    mhdr->name = tmpname;
+    mhdr->value = tmpval;
+    if ((mhdr->params = sk_MIME_PARAM_new(mime_param_cmp)) == NULL)
+        goto err;
+    return mhdr;
+
+ err:
+    OPENSSL_free(tmpname);
+    OPENSSL_free(tmpval);
+    OPENSSL_free(mhdr);
+    return NULL;
+}
+
+static int mime_hdr_addparam(MIME_HEADER *mhdr, const char *name, const char *value)
+{
+    char *tmpname = NULL, *tmpval = NULL, *p;
+    int c;
+    MIME_PARAM *mparam = NULL;
+    if (name) {
+        tmpname = OPENSSL_strdup(name);
+        if (!tmpname)
+            goto err;
+        for (p = tmpname; *p; p++) {
+            c = (unsigned char)*p;
+            if (isupper(c)) {
+                c = tolower(c);
+                *p = c;
+            }
+        }
+    }
+    if (value) {
+        tmpval = OPENSSL_strdup(value);
+        if (!tmpval)
+            goto err;
+    }
+    /* Parameter values are case sensitive so leave as is */
+    mparam = OPENSSL_malloc(sizeof(*mparam));
+    if (mparam == NULL)
+        goto err;
+    mparam->param_name = tmpname;
+    mparam->param_value = tmpval;
+    if (!sk_MIME_PARAM_push(mhdr->params, mparam))
+        goto err;
+    return 1;
+ err:
+    OPENSSL_free(tmpname);
+    OPENSSL_free(tmpval);
+    OPENSSL_free(mparam);
+    return 0;
+}
+
+static int mime_hdr_cmp(const MIME_HEADER *const *a,
+                        const MIME_HEADER *const *b)
+{
+    if (!(*a)->name || !(*b)->name)
+        return ! !(*a)->name - ! !(*b)->name;
+
+    return (strcmp((*a)->name, (*b)->name));
+}
+
+static int mime_param_cmp(const MIME_PARAM *const *a,
+                          const MIME_PARAM *const *b)
+{
+    if (!(*a)->param_name || !(*b)->param_name)
+        return ! !(*a)->param_name - ! !(*b)->param_name;
+    return (strcmp((*a)->param_name, (*b)->param_name));
+}
+
+/* Find a header with a given name (if possible) */
+
+static MIME_HEADER *mime_hdr_find(STACK_OF(MIME_HEADER) *hdrs, const char *name)
+{
+    MIME_HEADER htmp;
+    int idx;
+
+    htmp.name = (char *)name;
+    htmp.value = NULL;
+    htmp.params = NULL;
+
+    idx = sk_MIME_HEADER_find(hdrs, &htmp);
+    if (idx < 0)
+        return NULL;
+    return sk_MIME_HEADER_value(hdrs, idx);
+}
+
+static MIME_PARAM *mime_param_find(MIME_HEADER *hdr, const char *name)
+{
+    MIME_PARAM param;
+    int idx;
+
+    param.param_name = (char *)name;
+    param.param_value = NULL;
+    idx = sk_MIME_PARAM_find(hdr->params, ¶m);
+    if (idx < 0)
+        return NULL;
+    return sk_MIME_PARAM_value(hdr->params, idx);
+}
+
+static void mime_hdr_free(MIME_HEADER *hdr)
+{
+    if (hdr == NULL)
+        return;
+    OPENSSL_free(hdr->name);
+    OPENSSL_free(hdr->value);
+    if (hdr->params)
+        sk_MIME_PARAM_pop_free(hdr->params, mime_param_free);
+    OPENSSL_free(hdr);
+}
+
+static void mime_param_free(MIME_PARAM *param)
+{
+    OPENSSL_free(param->param_name);
+    OPENSSL_free(param->param_value);
+    OPENSSL_free(param);
+}
+
+/*-
+ * Check for a multipart boundary. Returns:
+ * 0 : no boundary
+ * 1 : part boundary
+ * 2 : final boundary
+ */
+static int mime_bound_check(char *line, int linelen, const char *bound, int blen)
+{
+    if (linelen == -1)
+        linelen = strlen(line);
+    if (blen == -1)
+        blen = strlen(bound);
+    /* Quickly eliminate if line length too short */
+    if (blen + 2 > linelen)
+        return 0;
+    /* Check for part boundary */
+    if ((strncmp(line, "--", 2) == 0)
+        && strncmp(line + 2, bound, blen) == 0) {
+        if (strncmp(line + blen + 2, "--", 2) == 0)
+            return 2;
+        else
+            return 1;
+    }
+    return 0;
+}
+
+static int strip_eol(char *linebuf, int *plen, int flags)
+{
+    int len = *plen;
+    char *p, c;
+    int is_eol = 0;
+    p = linebuf + len - 1;
+    for (p = linebuf + len - 1; len > 0; len--, p--) {
+        c = *p;
+        if (c == '\n')
+            is_eol = 1;
+        else if (is_eol && flags & SMIME_ASCIICRLF && c < 33)
+            continue;
+        else if (c != '\r')
+            break;
+    }
+    *plen = len;
+    return is_eol;
+}
diff --git a/crypto/asn1/asn_moid.c b/crypto/asn1/asn_moid.c
new file mode 100644
index 000000000..8176b7600
--- /dev/null
+++ b/crypto/asn1/asn_moid.c
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2002-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+#include "internal/asn1_int.h"
+#include "internal/objects.h"
+
+/* Simple ASN1 OID module: add all objects in a given section */
+
+static int do_create(const char *value, const char *name);
+
+static int oid_module_init(CONF_IMODULE *md, const CONF *cnf)
+{
+    int i;
+    const char *oid_section;
+    STACK_OF(CONF_VALUE) *sktmp;
+    CONF_VALUE *oval;
+
+    oid_section = CONF_imodule_get_value(md);
+    if ((sktmp = NCONF_get_section(cnf, oid_section)) == NULL) {
+        ASN1err(ASN1_F_OID_MODULE_INIT, ASN1_R_ERROR_LOADING_SECTION);
+        return 0;
+    }
+    for (i = 0; i < sk_CONF_VALUE_num(sktmp); i++) {
+        oval = sk_CONF_VALUE_value(sktmp, i);
+        if (!do_create(oval->value, oval->name)) {
+            ASN1err(ASN1_F_OID_MODULE_INIT, ASN1_R_ADDING_OBJECT);
+            return 0;
+        }
+    }
+    return 1;
+}
+
+static void oid_module_finish(CONF_IMODULE *md)
+{
+}
+
+void ASN1_add_oid_module(void)
+{
+    CONF_module_add("oid_section", oid_module_init, oid_module_finish);
+}
+
+/*-
+ * Create an OID based on a name value pair. Accept two formats.
+ * shortname = 1.2.3.4
+ * shortname = some long name, 1.2.3.4
+ */
+
+static int do_create(const char *value, const char *name)
+{
+    int nid;
+    ASN1_OBJECT *oid;
+    const char *ln, *ostr, *p;
+    char *lntmp;
+    p = strrchr(value, ',');
+    if (!p) {
+        ln = name;
+        ostr = value;
+    } else {
+        ln = NULL;
+        ostr = p + 1;
+        if (!*ostr)
+            return 0;
+        while (isspace((unsigned char)*ostr))
+            ostr++;
+    }
+
+    nid = OBJ_create(ostr, name, ln);
+
+    if (nid == NID_undef)
+        return 0;
+
+    if (p) {
+        ln = value;
+        while (isspace((unsigned char)*ln))
+            ln++;
+        p--;
+        while (isspace((unsigned char)*p)) {
+            if (p == ln)
+                return 0;
+            p--;
+        }
+        p++;
+        lntmp = OPENSSL_malloc((p - ln) + 1);
+        if (lntmp == NULL)
+            return 0;
+        memcpy(lntmp, ln, p - ln);
+        lntmp[p - ln] = 0;
+        oid = OBJ_nid2obj(nid);
+        oid->ln = lntmp;
+    }
+
+    return 1;
+}
diff --git a/crypto/asn1/asn_mstbl.c b/crypto/asn1/asn_mstbl.c
new file mode 100644
index 000000000..826093900
--- /dev/null
+++ b/crypto/asn1/asn_mstbl.c
@@ -0,0 +1,114 @@
+/*
+ * Copyright 2012-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+
+/* Multi string module: add table entries from a given section */
+
+static int do_tcreate(const char *value, const char *name);
+
+static int stbl_module_init(CONF_IMODULE *md, const CONF *cnf)
+{
+    int i;
+    const char *stbl_section;
+    STACK_OF(CONF_VALUE) *sktmp;
+    CONF_VALUE *mval;
+
+    stbl_section = CONF_imodule_get_value(md);
+    if ((sktmp = NCONF_get_section(cnf, stbl_section)) == NULL) {
+        ASN1err(ASN1_F_STBL_MODULE_INIT, ASN1_R_ERROR_LOADING_SECTION);
+        return 0;
+    }
+    for (i = 0; i < sk_CONF_VALUE_num(sktmp); i++) {
+        mval = sk_CONF_VALUE_value(sktmp, i);
+        if (!do_tcreate(mval->value, mval->name)) {
+            ASN1err(ASN1_F_STBL_MODULE_INIT, ASN1_R_INVALID_VALUE);
+            return 0;
+        }
+    }
+    return 1;
+}
+
+static void stbl_module_finish(CONF_IMODULE *md)
+{
+    ASN1_STRING_TABLE_cleanup();
+}
+
+void ASN1_add_stable_module(void)
+{
+    CONF_module_add("stbl_section", stbl_module_init, stbl_module_finish);
+}
+
+/*
+ * Create an table entry based on a name value pair. format is oid_name =
+ * n1:v1, n2:v2,... where name is "min", "max", "mask" or "flags".
+ */
+
+static int do_tcreate(const char *value, const char *name)
+{
+    char *eptr;
+    int nid, i, rv = 0;
+    long tbl_min = -1, tbl_max = -1;
+    unsigned long tbl_mask = 0, tbl_flags = 0;
+    STACK_OF(CONF_VALUE) *lst = NULL;
+    CONF_VALUE *cnf = NULL;
+    nid = OBJ_sn2nid(name);
+    if (nid == NID_undef)
+        nid = OBJ_ln2nid(name);
+    if (nid == NID_undef)
+        goto err;
+    lst = X509V3_parse_list(value);
+    if (!lst)
+        goto err;
+    for (i = 0; i < sk_CONF_VALUE_num(lst); i++) {
+        cnf = sk_CONF_VALUE_value(lst, i);
+        if (strcmp(cnf->name, "min") == 0) {
+            tbl_min = strtoul(cnf->value, &eptr, 0);
+            if (*eptr)
+                goto err;
+        } else if (strcmp(cnf->name, "max") == 0) {
+            tbl_max = strtoul(cnf->value, &eptr, 0);
+            if (*eptr)
+                goto err;
+        } else if (strcmp(cnf->name, "mask") == 0) {
+            if (!ASN1_str2mask(cnf->value, &tbl_mask) || !tbl_mask)
+                goto err;
+        } else if (strcmp(cnf->name, "flags") == 0) {
+            if (strcmp(cnf->value, "nomask") == 0)
+                tbl_flags = STABLE_NO_MASK;
+            else if (strcmp(cnf->value, "none") == 0)
+                tbl_flags = STABLE_FLAGS_CLEAR;
+            else
+                goto err;
+        } else
+            goto err;
+    }
+    rv = 1;
+ err:
+    if (rv == 0) {
+        ASN1err(ASN1_F_DO_TCREATE, ASN1_R_INVALID_STRING_TABLE_VALUE);
+        if (cnf)
+            ERR_add_error_data(4, "field=", cnf->name,
+                               ", value=", cnf->value);
+        else
+            ERR_add_error_data(4, "name=", name, ", value=", value);
+    } else {
+        rv = ASN1_STRING_TABLE_add(nid, tbl_min, tbl_max,
+                                   tbl_mask, tbl_flags);
+        if (!rv)
+            ASN1err(ASN1_F_DO_TCREATE, ERR_R_MALLOC_FAILURE);
+    }
+    sk_CONF_VALUE_pop_free(lst, X509V3_conf_free);
+    return rv;
+}
diff --git a/crypto/asn1/asn_pack.c b/crypto/asn1/asn_pack.c
new file mode 100644
index 000000000..63bc30675
--- /dev/null
+++ b/crypto/asn1/asn_pack.c
@@ -0,0 +1,62 @@
+/*
+ * Copyright 1999-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+
+/* ASN1 packing and unpacking functions */
+
+ASN1_STRING *ASN1_item_pack(void *obj, const ASN1_ITEM *it, ASN1_STRING **oct)
+{
+    ASN1_STRING *octmp;
+
+     if (oct == NULL || *oct == NULL) {
+        if ((octmp = ASN1_STRING_new()) == NULL) {
+            ASN1err(ASN1_F_ASN1_ITEM_PACK, ERR_R_MALLOC_FAILURE);
+            return NULL;
+        }
+    } else {
+        octmp = *oct;
+    }
+
+    OPENSSL_free(octmp->data);
+    octmp->data = NULL;
+
+    if ((octmp->length = ASN1_item_i2d(obj, &octmp->data, it)) == 0) {
+        ASN1err(ASN1_F_ASN1_ITEM_PACK, ASN1_R_ENCODE_ERROR);
+        goto err;
+    }
+    if (octmp->data == NULL) {
+        ASN1err(ASN1_F_ASN1_ITEM_PACK, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+
+    if (oct != NULL && *oct == NULL)
+        *oct = octmp;
+
+    return octmp;
+ err:
+    if (oct == NULL || *oct == NULL)
+        ASN1_STRING_free(octmp);
+    return NULL;
+}
+
+/* Extract an ASN1 object from an ASN1_STRING */
+
+void *ASN1_item_unpack(const ASN1_STRING *oct, const ASN1_ITEM *it)
+{
+    const unsigned char *p;
+    void *ret;
+
+    p = oct->data;
+    if ((ret = ASN1_item_d2i(NULL, &p, oct->length, it)) == NULL)
+        ASN1err(ASN1_F_ASN1_ITEM_UNPACK, ASN1_R_DECODE_ERROR);
+    return ret;
+}
diff --git a/crypto/asn1/bio_asn1.c b/crypto/asn1/bio_asn1.c
new file mode 100644
index 000000000..400effa12
--- /dev/null
+++ b/crypto/asn1/bio_asn1.c
@@ -0,0 +1,437 @@
+/*
+ * Copyright 2006-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * Experimental ASN1 BIO. When written through the data is converted to an
+ * ASN1 string type: default is OCTET STRING. Additional functions can be
+ * provided to add prefix and suffix data.
+ */
+
+#include 
+#include 
+#include 
+
+/* Must be large enough for biggest tag+length */
+#define DEFAULT_ASN1_BUF_SIZE 20
+
+typedef enum {
+    ASN1_STATE_START,
+    ASN1_STATE_PRE_COPY,
+    ASN1_STATE_HEADER,
+    ASN1_STATE_HEADER_COPY,
+    ASN1_STATE_DATA_COPY,
+    ASN1_STATE_POST_COPY,
+    ASN1_STATE_DONE
+} asn1_bio_state_t;
+
+typedef struct BIO_ASN1_EX_FUNCS_st {
+    asn1_ps_func *ex_func;
+    asn1_ps_func *ex_free_func;
+} BIO_ASN1_EX_FUNCS;
+
+typedef struct BIO_ASN1_BUF_CTX_t {
+    /* Internal state */
+    asn1_bio_state_t state;
+    /* Internal buffer */
+    unsigned char *buf;
+    /* Size of buffer */
+    int bufsize;
+    /* Current position in buffer */
+    int bufpos;
+    /* Current buffer length */
+    int buflen;
+    /* Amount of data to copy */
+    int copylen;
+    /* Class and tag to use */
+    int asn1_class, asn1_tag;
+    asn1_ps_func *prefix, *prefix_free, *suffix, *suffix_free;
+    /* Extra buffer for prefix and suffix data */
+    unsigned char *ex_buf;
+    int ex_len;
+    int ex_pos;
+    void *ex_arg;
+} BIO_ASN1_BUF_CTX;
+
+static int asn1_bio_write(BIO *h, const char *buf, int num);
+static int asn1_bio_read(BIO *h, char *buf, int size);
+static int asn1_bio_puts(BIO *h, const char *str);
+static int asn1_bio_gets(BIO *h, char *str, int size);
+static long asn1_bio_ctrl(BIO *h, int cmd, long arg1, void *arg2);
+static int asn1_bio_new(BIO *h);
+static int asn1_bio_free(BIO *data);
+static long asn1_bio_callback_ctrl(BIO *h, int cmd, bio_info_cb *fp);
+
+static int asn1_bio_init(BIO_ASN1_BUF_CTX *ctx, int size);
+static int asn1_bio_flush_ex(BIO *b, BIO_ASN1_BUF_CTX *ctx,
+                             asn1_ps_func *cleanup, asn1_bio_state_t next);
+static int asn1_bio_setup_ex(BIO *b, BIO_ASN1_BUF_CTX *ctx,
+                             asn1_ps_func *setup,
+                             asn1_bio_state_t ex_state,
+                             asn1_bio_state_t other_state);
+
+static const BIO_METHOD methods_asn1 = {
+    BIO_TYPE_ASN1,
+    "asn1",
+    asn1_bio_write,
+    asn1_bio_read,
+    asn1_bio_puts,
+    asn1_bio_gets,
+    asn1_bio_ctrl,
+    asn1_bio_new,
+    asn1_bio_free,
+    asn1_bio_callback_ctrl,
+};
+
+const BIO_METHOD *BIO_f_asn1(void)
+{
+    return (&methods_asn1);
+}
+
+static int asn1_bio_new(BIO *b)
+{
+    BIO_ASN1_BUF_CTX *ctx = OPENSSL_zalloc(sizeof(*ctx));
+
+    if (ctx == NULL)
+        return 0;
+    if (!asn1_bio_init(ctx, DEFAULT_ASN1_BUF_SIZE)) {
+        OPENSSL_free(ctx);
+        return 0;
+    }
+    BIO_set_data(b, ctx);
+    BIO_set_init(b, 1);
+
+    return 1;
+}
+
+static int asn1_bio_init(BIO_ASN1_BUF_CTX *ctx, int size)
+{
+    ctx->buf = OPENSSL_malloc(size);
+    if (ctx->buf == NULL)
+        return 0;
+    ctx->bufsize = size;
+    ctx->asn1_class = V_ASN1_UNIVERSAL;
+    ctx->asn1_tag = V_ASN1_OCTET_STRING;
+    ctx->state = ASN1_STATE_START;
+    return 1;
+}
+
+static int asn1_bio_free(BIO *b)
+{
+    BIO_ASN1_BUF_CTX *ctx;
+
+    if (b == NULL)
+        return 0;
+
+    ctx = BIO_get_data(b);
+    if (ctx == NULL)
+        return 0;
+
+    OPENSSL_free(ctx->buf);
+    OPENSSL_free(ctx);
+    BIO_set_data(b, NULL);
+    BIO_set_init(b, 0);
+
+    return 1;
+}
+
+static int asn1_bio_write(BIO *b, const char *in, int inl)
+{
+    BIO_ASN1_BUF_CTX *ctx;
+    int wrmax, wrlen, ret;
+    unsigned char *p;
+    BIO *next;
+
+    ctx = BIO_get_data(b);
+    next = BIO_next(b);
+    if (in == NULL || inl < 0 || ctx == NULL || next == NULL)
+        return 0;
+
+    wrlen = 0;
+    ret = -1;
+
+    for (;;) {
+        switch (ctx->state) {
+
+            /* Setup prefix data, call it */
+        case ASN1_STATE_START:
+            if (!asn1_bio_setup_ex(b, ctx, ctx->prefix,
+                                   ASN1_STATE_PRE_COPY, ASN1_STATE_HEADER))
+                return 0;
+            break;
+
+            /* Copy any pre data first */
+        case ASN1_STATE_PRE_COPY:
+
+            ret = asn1_bio_flush_ex(b, ctx, ctx->prefix_free,
+                                    ASN1_STATE_HEADER);
+
+            if (ret <= 0)
+                goto done;
+
+            break;
+
+        case ASN1_STATE_HEADER:
+            ctx->buflen = ASN1_object_size(0, inl, ctx->asn1_tag) - inl;
+            OPENSSL_assert(ctx->buflen <= ctx->bufsize);
+            p = ctx->buf;
+            ASN1_put_object(&p, 0, inl, ctx->asn1_tag, ctx->asn1_class);
+            ctx->copylen = inl;
+            ctx->state = ASN1_STATE_HEADER_COPY;
+
+            break;
+
+        case ASN1_STATE_HEADER_COPY:
+            ret = BIO_write(next, ctx->buf + ctx->bufpos, ctx->buflen);
+            if (ret <= 0)
+                goto done;
+
+            ctx->buflen -= ret;
+            if (ctx->buflen)
+                ctx->bufpos += ret;
+            else {
+                ctx->bufpos = 0;
+                ctx->state = ASN1_STATE_DATA_COPY;
+            }
+
+            break;
+
+        case ASN1_STATE_DATA_COPY:
+
+            if (inl > ctx->copylen)
+                wrmax = ctx->copylen;
+            else
+                wrmax = inl;
+            ret = BIO_write(next, in, wrmax);
+            if (ret <= 0)
+                break;
+            wrlen += ret;
+            ctx->copylen -= ret;
+            in += ret;
+            inl -= ret;
+
+            if (ctx->copylen == 0)
+                ctx->state = ASN1_STATE_HEADER;
+
+            if (inl == 0)
+                goto done;
+
+            break;
+
+        default:
+            BIO_clear_retry_flags(b);
+            return 0;
+
+        }
+
+    }
+
+ done:
+    BIO_clear_retry_flags(b);
+    BIO_copy_next_retry(b);
+
+    return (wrlen > 0) ? wrlen : ret;
+
+}
+
+static int asn1_bio_flush_ex(BIO *b, BIO_ASN1_BUF_CTX *ctx,
+                             asn1_ps_func *cleanup, asn1_bio_state_t next)
+{
+    int ret;
+
+    if (ctx->ex_len <= 0)
+        return 1;
+    for (;;) {
+        ret = BIO_write(BIO_next(b), ctx->ex_buf + ctx->ex_pos, ctx->ex_len);
+        if (ret <= 0)
+            break;
+        ctx->ex_len -= ret;
+        if (ctx->ex_len > 0)
+            ctx->ex_pos += ret;
+        else {
+            if (cleanup)
+                cleanup(b, &ctx->ex_buf, &ctx->ex_len, &ctx->ex_arg);
+            ctx->state = next;
+            ctx->ex_pos = 0;
+            break;
+        }
+    }
+    return ret;
+}
+
+static int asn1_bio_setup_ex(BIO *b, BIO_ASN1_BUF_CTX *ctx,
+                             asn1_ps_func *setup,
+                             asn1_bio_state_t ex_state,
+                             asn1_bio_state_t other_state)
+{
+    if (setup && !setup(b, &ctx->ex_buf, &ctx->ex_len, &ctx->ex_arg)) {
+        BIO_clear_retry_flags(b);
+        return 0;
+    }
+    if (ctx->ex_len > 0)
+        ctx->state = ex_state;
+    else
+        ctx->state = other_state;
+    return 1;
+}
+
+static int asn1_bio_read(BIO *b, char *in, int inl)
+{
+    BIO *next = BIO_next(b);
+    if (next == NULL)
+        return 0;
+    return BIO_read(next, in, inl);
+}
+
+static int asn1_bio_puts(BIO *b, const char *str)
+{
+    return asn1_bio_write(b, str, strlen(str));
+}
+
+static int asn1_bio_gets(BIO *b, char *str, int size)
+{
+    BIO *next = BIO_next(b);
+    if (next == NULL)
+        return 0;
+    return BIO_gets(next, str, size);
+}
+
+static long asn1_bio_callback_ctrl(BIO *b, int cmd, bio_info_cb *fp)
+{
+    BIO *next = BIO_next(b);
+    if (next == NULL)
+        return 0;
+    return BIO_callback_ctrl(next, cmd, fp);
+}
+
+static long asn1_bio_ctrl(BIO *b, int cmd, long arg1, void *arg2)
+{
+    BIO_ASN1_BUF_CTX *ctx;
+    BIO_ASN1_EX_FUNCS *ex_func;
+    long ret = 1;
+    BIO *next;
+
+    ctx = BIO_get_data(b);
+    if (ctx == NULL)
+        return 0;
+    next = BIO_next(b);
+    switch (cmd) {
+
+    case BIO_C_SET_PREFIX:
+        ex_func = arg2;
+        ctx->prefix = ex_func->ex_func;
+        ctx->prefix_free = ex_func->ex_free_func;
+        break;
+
+    case BIO_C_GET_PREFIX:
+        ex_func = arg2;
+        ex_func->ex_func = ctx->prefix;
+        ex_func->ex_free_func = ctx->prefix_free;
+        break;
+
+    case BIO_C_SET_SUFFIX:
+        ex_func = arg2;
+        ctx->suffix = ex_func->ex_func;
+        ctx->suffix_free = ex_func->ex_free_func;
+        break;
+
+    case BIO_C_GET_SUFFIX:
+        ex_func = arg2;
+        ex_func->ex_func = ctx->suffix;
+        ex_func->ex_free_func = ctx->suffix_free;
+        break;
+
+    case BIO_C_SET_EX_ARG:
+        ctx->ex_arg = arg2;
+        break;
+
+    case BIO_C_GET_EX_ARG:
+        *(void **)arg2 = ctx->ex_arg;
+        break;
+
+    case BIO_CTRL_FLUSH:
+        if (next == NULL)
+            return 0;
+
+        /* Call post function if possible */
+        if (ctx->state == ASN1_STATE_HEADER) {
+            if (!asn1_bio_setup_ex(b, ctx, ctx->suffix,
+                                   ASN1_STATE_POST_COPY, ASN1_STATE_DONE))
+                return 0;
+        }
+
+        if (ctx->state == ASN1_STATE_POST_COPY) {
+            ret = asn1_bio_flush_ex(b, ctx, ctx->suffix_free,
+                                    ASN1_STATE_DONE);
+            if (ret <= 0)
+                return ret;
+        }
+
+        if (ctx->state == ASN1_STATE_DONE)
+            return BIO_ctrl(next, cmd, arg1, arg2);
+        else {
+            BIO_clear_retry_flags(b);
+            return 0;
+        }
+
+    default:
+        if (next == NULL)
+            return 0;
+        return BIO_ctrl(next, cmd, arg1, arg2);
+
+    }
+
+    return ret;
+}
+
+static int asn1_bio_set_ex(BIO *b, int cmd,
+                           asn1_ps_func *ex_func, asn1_ps_func *ex_free_func)
+{
+    BIO_ASN1_EX_FUNCS extmp;
+    extmp.ex_func = ex_func;
+    extmp.ex_free_func = ex_free_func;
+    return BIO_ctrl(b, cmd, 0, &extmp);
+}
+
+static int asn1_bio_get_ex(BIO *b, int cmd,
+                           asn1_ps_func **ex_func,
+                           asn1_ps_func **ex_free_func)
+{
+    BIO_ASN1_EX_FUNCS extmp;
+    int ret;
+    ret = BIO_ctrl(b, cmd, 0, &extmp);
+    if (ret > 0) {
+        *ex_func = extmp.ex_func;
+        *ex_free_func = extmp.ex_free_func;
+    }
+    return ret;
+}
+
+int BIO_asn1_set_prefix(BIO *b, asn1_ps_func *prefix,
+                        asn1_ps_func *prefix_free)
+{
+    return asn1_bio_set_ex(b, BIO_C_SET_PREFIX, prefix, prefix_free);
+}
+
+int BIO_asn1_get_prefix(BIO *b, asn1_ps_func **pprefix,
+                        asn1_ps_func **pprefix_free)
+{
+    return asn1_bio_get_ex(b, BIO_C_GET_PREFIX, pprefix, pprefix_free);
+}
+
+int BIO_asn1_set_suffix(BIO *b, asn1_ps_func *suffix,
+                        asn1_ps_func *suffix_free)
+{
+    return asn1_bio_set_ex(b, BIO_C_SET_SUFFIX, suffix, suffix_free);
+}
+
+int BIO_asn1_get_suffix(BIO *b, asn1_ps_func **psuffix,
+                        asn1_ps_func **psuffix_free)
+{
+    return asn1_bio_get_ex(b, BIO_C_GET_SUFFIX, psuffix, psuffix_free);
+}
diff --git a/crypto/asn1/bio_ndef.c b/crypto/asn1/bio_ndef.c
new file mode 100644
index 000000000..0f206b249
--- /dev/null
+++ b/crypto/asn1/bio_ndef.c
@@ -0,0 +1,199 @@
+/*
+ * Copyright 2008-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include 
+#include 
+
+#include 
+
+/* Experimental NDEF ASN1 BIO support routines */
+
+/*
+ * The usage is quite simple, initialize an ASN1 structure, get a BIO from it
+ * then any data written through the BIO will end up translated to
+ * appropriate format on the fly. The data is streamed out and does *not*
+ * need to be all held in memory at once. When the BIO is flushed the output
+ * is finalized and any signatures etc written out. The BIO is a 'proper'
+ * BIO and can handle non blocking I/O correctly. The usage is simple. The
+ * implementation is *not*...
+ */
+
+/* BIO support data stored in the ASN1 BIO ex_arg */
+
+typedef struct ndef_aux_st {
+    /* ASN1 structure this BIO refers to */
+    ASN1_VALUE *val;
+    const ASN1_ITEM *it;
+    /* Top of the BIO chain */
+    BIO *ndef_bio;
+    /* Output BIO */
+    BIO *out;
+    /* Boundary where content is inserted */
+    unsigned char **boundary;
+    /* DER buffer start */
+    unsigned char *derbuf;
+} NDEF_SUPPORT;
+
+static int ndef_prefix(BIO *b, unsigned char **pbuf, int *plen, void *parg);
+static int ndef_prefix_free(BIO *b, unsigned char **pbuf, int *plen,
+                            void *parg);
+static int ndef_suffix(BIO *b, unsigned char **pbuf, int *plen, void *parg);
+static int ndef_suffix_free(BIO *b, unsigned char **pbuf, int *plen,
+                            void *parg);
+
+BIO *BIO_new_NDEF(BIO *out, ASN1_VALUE *val, const ASN1_ITEM *it)
+{
+    NDEF_SUPPORT *ndef_aux = NULL;
+    BIO *asn_bio = NULL;
+    const ASN1_AUX *aux = it->funcs;
+    ASN1_STREAM_ARG sarg;
+
+    if (!aux || !aux->asn1_cb) {
+        ASN1err(ASN1_F_BIO_NEW_NDEF, ASN1_R_STREAMING_NOT_SUPPORTED);
+        return NULL;
+    }
+    ndef_aux = OPENSSL_zalloc(sizeof(*ndef_aux));
+    asn_bio = BIO_new(BIO_f_asn1());
+    if (ndef_aux == NULL || asn_bio == NULL)
+        goto err;
+
+    /* ASN1 bio needs to be next to output BIO */
+    out = BIO_push(asn_bio, out);
+    if (out == NULL)
+        goto err;
+
+    BIO_asn1_set_prefix(asn_bio, ndef_prefix, ndef_prefix_free);
+    BIO_asn1_set_suffix(asn_bio, ndef_suffix, ndef_suffix_free);
+
+    /*
+     * Now let callback prepends any digest, cipher etc BIOs ASN1 structure
+     * needs.
+     */
+
+    sarg.out = out;
+    sarg.ndef_bio = NULL;
+    sarg.boundary = NULL;
+
+    if (aux->asn1_cb(ASN1_OP_STREAM_PRE, &val, it, &sarg) <= 0)
+        goto err;
+
+    ndef_aux->val = val;
+    ndef_aux->it = it;
+    ndef_aux->ndef_bio = sarg.ndef_bio;
+    ndef_aux->boundary = sarg.boundary;
+    ndef_aux->out = out;
+
+    BIO_ctrl(asn_bio, BIO_C_SET_EX_ARG, 0, ndef_aux);
+
+    return sarg.ndef_bio;
+
+ err:
+    BIO_free(asn_bio);
+    OPENSSL_free(ndef_aux);
+    return NULL;
+}
+
+static int ndef_prefix(BIO *b, unsigned char **pbuf, int *plen, void *parg)
+{
+    NDEF_SUPPORT *ndef_aux;
+    unsigned char *p;
+    int derlen;
+
+    if (!parg)
+        return 0;
+
+    ndef_aux = *(NDEF_SUPPORT **)parg;
+
+    derlen = ASN1_item_ndef_i2d(ndef_aux->val, NULL, ndef_aux->it);
+    p = OPENSSL_malloc(derlen);
+    if (p == NULL)
+        return 0;
+
+    ndef_aux->derbuf = p;
+    *pbuf = p;
+    derlen = ASN1_item_ndef_i2d(ndef_aux->val, &p, ndef_aux->it);
+
+    if (!*ndef_aux->boundary)
+        return 0;
+
+    *plen = *ndef_aux->boundary - *pbuf;
+
+    return 1;
+}
+
+static int ndef_prefix_free(BIO *b, unsigned char **pbuf, int *plen,
+                            void *parg)
+{
+    NDEF_SUPPORT *ndef_aux;
+
+    if (!parg)
+        return 0;
+
+    ndef_aux = *(NDEF_SUPPORT **)parg;
+
+    OPENSSL_free(ndef_aux->derbuf);
+
+    ndef_aux->derbuf = NULL;
+    *pbuf = NULL;
+    *plen = 0;
+    return 1;
+}
+
+static int ndef_suffix_free(BIO *b, unsigned char **pbuf, int *plen,
+                            void *parg)
+{
+    NDEF_SUPPORT **pndef_aux = (NDEF_SUPPORT **)parg;
+    if (!ndef_prefix_free(b, pbuf, plen, parg))
+        return 0;
+    OPENSSL_free(*pndef_aux);
+    *pndef_aux = NULL;
+    return 1;
+}
+
+static int ndef_suffix(BIO *b, unsigned char **pbuf, int *plen, void *parg)
+{
+    NDEF_SUPPORT *ndef_aux;
+    unsigned char *p;
+    int derlen;
+    const ASN1_AUX *aux;
+    ASN1_STREAM_ARG sarg;
+
+    if (!parg)
+        return 0;
+
+    ndef_aux = *(NDEF_SUPPORT **)parg;
+
+    aux = ndef_aux->it->funcs;
+
+    /* Finalize structures */
+    sarg.ndef_bio = ndef_aux->ndef_bio;
+    sarg.out = ndef_aux->out;
+    sarg.boundary = ndef_aux->boundary;
+    if (aux->asn1_cb(ASN1_OP_STREAM_POST,
+                     &ndef_aux->val, ndef_aux->it, &sarg) <= 0)
+        return 0;
+
+    derlen = ASN1_item_ndef_i2d(ndef_aux->val, NULL, ndef_aux->it);
+    p = OPENSSL_malloc(derlen);
+    if (p == NULL)
+        return 0;
+
+    ndef_aux->derbuf = p;
+    *pbuf = p;
+    derlen = ASN1_item_ndef_i2d(ndef_aux->val, &p, ndef_aux->it);
+
+    if (!*ndef_aux->boundary)
+        return 0;
+    *pbuf = *ndef_aux->boundary;
+    *plen = derlen - (*ndef_aux->boundary - ndef_aux->derbuf);
+
+    return 1;
+}
diff --git a/crypto/asn1/build.info b/crypto/asn1/build.info
new file mode 100644
index 000000000..02d1120b8
--- /dev/null
+++ b/crypto/asn1/build.info
@@ -0,0 +1,16 @@
+LIBS=../../libcrypto
+SOURCE[../../libcrypto]=\
+        a_object.c a_bitstr.c a_utctm.c a_gentm.c a_time.c a_int.c a_octet.c \
+        a_print.c a_type.c a_dup.c a_d2i_fp.c a_i2d_fp.c \
+        a_utf8.c a_sign.c a_digest.c a_verify.c a_mbstr.c a_strex.c \
+        x_algor.c x_val.c x_sig.c x_bignum.c \
+        x_long.c x_info.c x_spki.c nsseq.c \
+        d2i_pu.c d2i_pr.c i2d_pu.c i2d_pr.c\
+        t_pkey.c t_spki.c t_bitst.c \
+        tasn_new.c tasn_fre.c tasn_enc.c tasn_dec.c tasn_utl.c tasn_typ.c \
+        tasn_prn.c tasn_scn.c ameth_lib.c \
+        f_int.c f_string.c n_pkey.c \
+        x_pkey.c bio_asn1.c bio_ndef.c asn_mime.c \
+        asn1_gen.c asn1_par.c asn1_lib.c asn1_err.c a_strnid.c \
+        evp_asn1.c asn_pack.c p5_pbe.c p5_pbev2.c p5_scrypt.c p8_pkey.c \
+        asn_moid.c asn_mstbl.c
diff --git a/crypto/asn1/charmap.h b/crypto/asn1/charmap.h
new file mode 100644
index 000000000..2a75925c3
--- /dev/null
+++ b/crypto/asn1/charmap.h
@@ -0,0 +1,34 @@
+/*
+ * WARNING: do not edit!
+ * Generated by crypto/asn1/charmap.pl
+ *
+ * Copyright 2000-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#define CHARTYPE_HOST_ANY 4096
+#define CHARTYPE_HOST_DOT 8192
+#define CHARTYPE_HOST_HYPHEN 16384
+#define CHARTYPE_HOST_WILD 32768
+
+/*
+ * Mask of various character properties
+ */
+
+static const unsigned short char_type[] = {
+    1026,    2,    2,    2,    2,    2,    2,    2,    2,    2,    2,    2,
+       2,    2,    2,    2,    2,    2,    2,    2,    2,    2,    2,    2,
+       2,    2,    2,    2,    2,    2,    2,    2,  120,    0,    1,   40,
+       0,    0,    0,   16, 1040, 1040, 33792,   25,   25, 16400, 8208,   16,
+    4112, 4112, 4112, 4112, 4112, 4112, 4112, 4112, 4112, 4112,   16,    9,
+       9,   16,    9,   16,    0, 4112, 4112, 4112, 4112, 4112, 4112, 4112,
+    4112, 4112, 4112, 4112, 4112, 4112, 4112, 4112, 4112, 4112, 4112, 4112,
+    4112, 4112, 4112, 4112, 4112, 4112, 4112,    0, 1025,    0,    0,    0,
+       0, 4112, 4112, 4112, 4112, 4112, 4112, 4112, 4112, 4112, 4112, 4112,
+    4112, 4112, 4112, 4112, 4112, 4112, 4112, 4112, 4112, 4112, 4112, 4112,
+    4112, 4112, 4112,    0,    0,    0,    0,    2
+};
diff --git a/crypto/asn1/charmap.pl b/crypto/asn1/charmap.pl
new file mode 100644
index 000000000..26ca32522
--- /dev/null
+++ b/crypto/asn1/charmap.pl
@@ -0,0 +1,117 @@
+#! /usr/bin/env perl
+# Copyright 2000-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+use strict;
+
+my ($i, @arr);
+
+# Set up an array with the type of ASCII characters
+# Each set bit represents a character property.
+
+# RFC2253 character properties
+my $RFC2253_ESC = 1;	# Character escaped with \
+my $ESC_CTRL	= 2;	# Escaped control character
+# These are used with RFC1779 quoting using "
+my $NOESC_QUOTE	= 8;	# Not escaped if quoted
+my $PSTRING_CHAR = 0x10;	# Valid PrintableString character
+my $RFC2253_FIRST_ESC = 0x20; # Escaped with \ if first character
+my $RFC2253_LAST_ESC = 0x40;  # Escaped with \ if last character
+my $RFC2254_ESC = 0x400;	# Character escaped \XX
+my $HOST_ANY = 0x1000;      # Valid hostname character anywhere in label
+my $HOST_DOT = 0x2000;  # Dot: hostname label separator
+my $HOST_HYPHEN = 0x4000; # Hyphen: not valid at start or end.
+my $HOST_WILD = 0x8000; # Wildcard character
+
+for($i = 0; $i < 128; $i++) {
+	# Set the RFC2253 escape characters (control)
+	$arr[$i] = 0;
+	if(($i < 32) || ($i > 126)) {
+		$arr[$i] |= $ESC_CTRL;
+	}
+
+	# Some PrintableString characters
+	if(		   ( ( $i >= ord("a")) && ( $i <= ord("z")) )
+			|| (  ( $i >= ord("A")) && ( $i <= ord("Z")) )
+			|| (  ( $i >= ord("0")) && ( $i <= ord("9")) )  ) {
+		$arr[$i] |= $PSTRING_CHAR | $HOST_ANY;
+	}
+}
+
+# Now setup the rest
+
+# Remaining RFC2253 escaped characters
+
+$arr[ord(" ")] |= $NOESC_QUOTE | $RFC2253_FIRST_ESC | $RFC2253_LAST_ESC;
+$arr[ord("#")] |= $NOESC_QUOTE | $RFC2253_FIRST_ESC;
+
+$arr[ord(",")] |= $NOESC_QUOTE | $RFC2253_ESC;
+$arr[ord("+")] |= $NOESC_QUOTE | $RFC2253_ESC;
+$arr[ord("\"")] |= $RFC2253_ESC;
+$arr[ord("\\")] |= $RFC2253_ESC;
+$arr[ord("<")] |= $NOESC_QUOTE | $RFC2253_ESC;
+$arr[ord(">")] |= $NOESC_QUOTE | $RFC2253_ESC;
+$arr[ord(";")] |= $NOESC_QUOTE | $RFC2253_ESC;
+
+# Remaining RFC2254 characters
+
+$arr[0] |= $RFC2254_ESC;
+$arr[ord("(")] |= $RFC2254_ESC;
+$arr[ord(")")] |= $RFC2254_ESC;
+$arr[ord("*")] |= $RFC2254_ESC | $HOST_WILD;
+$arr[ord("\\")] |= $RFC2254_ESC;
+
+# Remaining PrintableString characters
+
+$arr[ord(" ")] |= $PSTRING_CHAR;
+$arr[ord("'")] |= $PSTRING_CHAR;
+$arr[ord("(")] |= $PSTRING_CHAR;
+$arr[ord(")")] |= $PSTRING_CHAR;
+$arr[ord("+")] |= $PSTRING_CHAR;
+$arr[ord(",")] |= $PSTRING_CHAR;
+$arr[ord("-")] |= $PSTRING_CHAR | $HOST_HYPHEN;
+$arr[ord(".")] |= $PSTRING_CHAR | $HOST_DOT;
+$arr[ord("/")] |= $PSTRING_CHAR;
+$arr[ord(":")] |= $PSTRING_CHAR;
+$arr[ord("=")] |= $PSTRING_CHAR;
+$arr[ord("?")] |= $PSTRING_CHAR;
+
+# Now generate the C code
+
+print <
+#include "internal/cryptlib.h"
+#include 
+#include 
+#include 
+#ifndef OPENSSL_NO_ENGINE
+# include 
+#endif
+#include 
+#include 
+#include "internal/asn1_int.h"
+#include "internal/evp_int.h"
+
+EVP_PKEY *d2i_PrivateKey(int type, EVP_PKEY **a, const unsigned char **pp,
+                         long length)
+{
+    EVP_PKEY *ret;
+    const unsigned char *p = *pp;
+
+    if ((a == NULL) || (*a == NULL)) {
+        if ((ret = EVP_PKEY_new()) == NULL) {
+            ASN1err(ASN1_F_D2I_PRIVATEKEY, ERR_R_EVP_LIB);
+            return (NULL);
+        }
+    } else {
+        ret = *a;
+#ifndef OPENSSL_NO_ENGINE
+        ENGINE_finish(ret->engine);
+        ret->engine = NULL;
+#endif
+    }
+
+    if (!EVP_PKEY_set_type(ret, type)) {
+        ASN1err(ASN1_F_D2I_PRIVATEKEY, ASN1_R_UNKNOWN_PUBLIC_KEY_TYPE);
+        goto err;
+    }
+
+    if (!ret->ameth->old_priv_decode ||
+        !ret->ameth->old_priv_decode(ret, &p, length)) {
+        if (ret->ameth->priv_decode) {
+            EVP_PKEY *tmp;
+            PKCS8_PRIV_KEY_INFO *p8 = NULL;
+            p8 = d2i_PKCS8_PRIV_KEY_INFO(NULL, &p, length);
+            if (!p8)
+                goto err;
+            tmp = EVP_PKCS82PKEY(p8);
+            PKCS8_PRIV_KEY_INFO_free(p8);
+            if (tmp == NULL)
+                goto err;
+            EVP_PKEY_free(ret);
+            ret = tmp;
+        } else {
+            ASN1err(ASN1_F_D2I_PRIVATEKEY, ERR_R_ASN1_LIB);
+            goto err;
+        }
+    }
+    *pp = p;
+    if (a != NULL)
+        (*a) = ret;
+    return (ret);
+ err:
+    if (a == NULL || *a != ret)
+        EVP_PKEY_free(ret);
+    return (NULL);
+}
+
+/*
+ * This works like d2i_PrivateKey() except it automatically works out the
+ * type
+ */
+
+EVP_PKEY *d2i_AutoPrivateKey(EVP_PKEY **a, const unsigned char **pp,
+                             long length)
+{
+    STACK_OF(ASN1_TYPE) *inkey;
+    const unsigned char *p;
+    int keytype;
+    p = *pp;
+    /*
+     * Dirty trick: read in the ASN1 data into a STACK_OF(ASN1_TYPE): by
+     * analyzing it we can determine the passed structure: this assumes the
+     * input is surrounded by an ASN1 SEQUENCE.
+     */
+    inkey = d2i_ASN1_SEQUENCE_ANY(NULL, &p, length);
+    p = *pp;
+    /*
+     * Since we only need to discern "traditional format" RSA and DSA keys we
+     * can just count the elements.
+     */
+    if (sk_ASN1_TYPE_num(inkey) == 6)
+        keytype = EVP_PKEY_DSA;
+    else if (sk_ASN1_TYPE_num(inkey) == 4)
+        keytype = EVP_PKEY_EC;
+    else if (sk_ASN1_TYPE_num(inkey) == 3) { /* This seems to be PKCS8, not
+                                              * traditional format */
+        PKCS8_PRIV_KEY_INFO *p8 = d2i_PKCS8_PRIV_KEY_INFO(NULL, &p, length);
+        EVP_PKEY *ret;
+
+        sk_ASN1_TYPE_pop_free(inkey, ASN1_TYPE_free);
+        if (!p8) {
+            ASN1err(ASN1_F_D2I_AUTOPRIVATEKEY,
+                    ASN1_R_UNSUPPORTED_PUBLIC_KEY_TYPE);
+            return NULL;
+        }
+        ret = EVP_PKCS82PKEY(p8);
+        PKCS8_PRIV_KEY_INFO_free(p8);
+        if (ret == NULL)
+            return NULL;
+        *pp = p;
+        if (a) {
+            *a = ret;
+        }
+        return ret;
+    } else
+        keytype = EVP_PKEY_RSA;
+    sk_ASN1_TYPE_pop_free(inkey, ASN1_TYPE_free);
+    return d2i_PrivateKey(keytype, a, pp, length);
+}
diff --git a/crypto/asn1/d2i_pu.c b/crypto/asn1/d2i_pu.c
new file mode 100644
index 000000000..442db2d26
--- /dev/null
+++ b/crypto/asn1/d2i_pu.c
@@ -0,0 +1,82 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+#include 
+#include 
+#ifndef OPENSSL_NO_RSA
+# include 
+#endif
+#ifndef OPENSSL_NO_DSA
+# include 
+#endif
+#include 
+
+#include "internal/evp_int.h"
+
+EVP_PKEY *d2i_PublicKey(int type, EVP_PKEY **a, const unsigned char **pp,
+                        long length)
+{
+    EVP_PKEY *ret;
+
+    if ((a == NULL) || (*a == NULL)) {
+        if ((ret = EVP_PKEY_new()) == NULL) {
+            ASN1err(ASN1_F_D2I_PUBLICKEY, ERR_R_EVP_LIB);
+            return (NULL);
+        }
+    } else
+        ret = *a;
+
+    if (!EVP_PKEY_set_type(ret, type)) {
+        ASN1err(ASN1_F_D2I_PUBLICKEY, ERR_R_EVP_LIB);
+        goto err;
+    }
+
+    switch (EVP_PKEY_id(ret)) {
+#ifndef OPENSSL_NO_RSA
+    case EVP_PKEY_RSA:
+        if ((ret->pkey.rsa = d2i_RSAPublicKey(NULL, pp, length)) == NULL) {
+            ASN1err(ASN1_F_D2I_PUBLICKEY, ERR_R_ASN1_LIB);
+            goto err;
+        }
+        break;
+#endif
+#ifndef OPENSSL_NO_DSA
+    case EVP_PKEY_DSA:
+        /* TMP UGLY CAST */
+        if (!d2i_DSAPublicKey(&ret->pkey.dsa, pp, length)) {
+            ASN1err(ASN1_F_D2I_PUBLICKEY, ERR_R_ASN1_LIB);
+            goto err;
+        }
+        break;
+#endif
+#ifndef OPENSSL_NO_EC
+    case EVP_PKEY_EC:
+        if (!o2i_ECPublicKey(&ret->pkey.ec, pp, length)) {
+            ASN1err(ASN1_F_D2I_PUBLICKEY, ERR_R_ASN1_LIB);
+            goto err;
+        }
+        break;
+#endif
+    default:
+        ASN1err(ASN1_F_D2I_PUBLICKEY, ASN1_R_UNKNOWN_PUBLIC_KEY_TYPE);
+        goto err;
+        /* break; */
+    }
+    if (a != NULL)
+        (*a) = ret;
+    return (ret);
+ err:
+    if (a == NULL || *a != ret)
+        EVP_PKEY_free(ret);
+    return (NULL);
+}
diff --git a/crypto/asn1/evp_asn1.c b/crypto/asn1/evp_asn1.c
new file mode 100644
index 000000000..a458367eb
--- /dev/null
+++ b/crypto/asn1/evp_asn1.c
@@ -0,0 +1,115 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+
+int ASN1_TYPE_set_octetstring(ASN1_TYPE *a, unsigned char *data, int len)
+{
+    ASN1_STRING *os;
+
+    if ((os = ASN1_OCTET_STRING_new()) == NULL)
+        return (0);
+    if (!ASN1_OCTET_STRING_set(os, data, len)) {
+        ASN1_OCTET_STRING_free(os);
+        return 0;
+    }
+    ASN1_TYPE_set(a, V_ASN1_OCTET_STRING, os);
+    return (1);
+}
+
+/* int max_len:  for returned value    */
+int ASN1_TYPE_get_octetstring(const ASN1_TYPE *a, unsigned char *data, int max_len)
+{
+    int ret, num;
+    const unsigned char *p;
+
+    if ((a->type != V_ASN1_OCTET_STRING) || (a->value.octet_string == NULL)) {
+        ASN1err(ASN1_F_ASN1_TYPE_GET_OCTETSTRING, ASN1_R_DATA_IS_WRONG);
+        return (-1);
+    }
+    p = ASN1_STRING_get0_data(a->value.octet_string);
+    ret = ASN1_STRING_length(a->value.octet_string);
+    if (ret < max_len)
+        num = ret;
+    else
+        num = max_len;
+    memcpy(data, p, num);
+    return (ret);
+}
+
+typedef struct {
+    long num;
+    ASN1_OCTET_STRING *oct;
+} asn1_int_oct;
+
+ASN1_SEQUENCE(asn1_int_oct) = {
+        ASN1_SIMPLE(asn1_int_oct, num, LONG),
+        ASN1_SIMPLE(asn1_int_oct, oct, ASN1_OCTET_STRING)
+} static_ASN1_SEQUENCE_END(asn1_int_oct)
+
+DECLARE_ASN1_ITEM(asn1_int_oct)
+
+int ASN1_TYPE_set_int_octetstring(ASN1_TYPE *a, long num, unsigned char *data,
+                                  int len)
+{
+    asn1_int_oct atmp;
+    ASN1_OCTET_STRING oct;
+
+    atmp.num = num;
+    atmp.oct = &oct;
+    oct.data = data;
+    oct.type = V_ASN1_OCTET_STRING;
+    oct.length = len;
+    oct.flags = 0;
+
+    if (ASN1_TYPE_pack_sequence(ASN1_ITEM_rptr(asn1_int_oct), &atmp, &a))
+        return 1;
+    return 0;
+}
+
+/*
+ * we return the actual length...
+ */
+/* int max_len:  for returned value    */
+int ASN1_TYPE_get_int_octetstring(const ASN1_TYPE *a, long *num,
+                                  unsigned char *data, int max_len)
+{
+    asn1_int_oct *atmp = NULL;
+    int ret = -1, n;
+
+    if ((a->type != V_ASN1_SEQUENCE) || (a->value.sequence == NULL)) {
+        goto err;
+    }
+
+    atmp = ASN1_TYPE_unpack_sequence(ASN1_ITEM_rptr(asn1_int_oct), a);
+
+    if (atmp == NULL)
+        goto err;
+
+    if (num != NULL)
+        *num = atmp->num;
+
+    ret = ASN1_STRING_length(atmp->oct);
+    if (max_len > ret)
+        n = ret;
+    else
+        n = max_len;
+
+    if (data != NULL)
+        memcpy(data, ASN1_STRING_get0_data(atmp->oct), n);
+    if (ret == -1) {
+ err:
+        ASN1err(ASN1_F_ASN1_TYPE_GET_INT_OCTETSTRING, ASN1_R_DATA_IS_WRONG);
+    }
+    M_ASN1_free_of(atmp, asn1_int_oct);
+    return ret;
+}
diff --git a/crypto/asn1/f_int.c b/crypto/asn1/f_int.c
new file mode 100644
index 000000000..51fc884b5
--- /dev/null
+++ b/crypto/asn1/f_int.c
@@ -0,0 +1,167 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+
+int i2a_ASN1_INTEGER(BIO *bp, const ASN1_INTEGER *a)
+{
+    int i, n = 0;
+    static const char *h = "0123456789ABCDEF";
+    char buf[2];
+
+    if (a == NULL)
+        return (0);
+
+    if (a->type & V_ASN1_NEG) {
+        if (BIO_write(bp, "-", 1) != 1)
+            goto err;
+        n = 1;
+    }
+
+    if (a->length == 0) {
+        if (BIO_write(bp, "00", 2) != 2)
+            goto err;
+        n += 2;
+    } else {
+        for (i = 0; i < a->length; i++) {
+            if ((i != 0) && (i % 35 == 0)) {
+                if (BIO_write(bp, "\\\n", 2) != 2)
+                    goto err;
+                n += 2;
+            }
+            buf[0] = h[((unsigned char)a->data[i] >> 4) & 0x0f];
+            buf[1] = h[((unsigned char)a->data[i]) & 0x0f];
+            if (BIO_write(bp, buf, 2) != 2)
+                goto err;
+            n += 2;
+        }
+    }
+    return (n);
+ err:
+    return (-1);
+}
+
+int a2i_ASN1_INTEGER(BIO *bp, ASN1_INTEGER *bs, char *buf, int size)
+{
+    int i, j, k, m, n, again, bufsize;
+    unsigned char *s = NULL, *sp;
+    unsigned char *bufp;
+    int num = 0, slen = 0, first = 1;
+
+    bs->type = V_ASN1_INTEGER;
+
+    bufsize = BIO_gets(bp, buf, size);
+    for (;;) {
+        if (bufsize < 1)
+            goto err;
+        i = bufsize;
+        if (buf[i - 1] == '\n')
+            buf[--i] = '\0';
+        if (i == 0)
+            goto err;
+        if (buf[i - 1] == '\r')
+            buf[--i] = '\0';
+        if (i == 0)
+            goto err;
+        again = (buf[i - 1] == '\\');
+
+        for (j = 0; j < i; j++) {
+#ifndef CHARSET_EBCDIC
+            if (!(((buf[j] >= '0') && (buf[j] <= '9')) ||
+                  ((buf[j] >= 'a') && (buf[j] <= 'f')) ||
+                  ((buf[j] >= 'A') && (buf[j] <= 'F'))))
+#else
+            /*
+             * This #ifdef is not strictly necessary, since the characters
+             * A...F a...f 0...9 are contiguous (yes, even in EBCDIC - but
+             * not the whole alphabet). Nevertheless, isxdigit() is faster.
+             */
+            if (!isxdigit(buf[j]))
+#endif
+            {
+                i = j;
+                break;
+            }
+        }
+        buf[i] = '\0';
+        /*
+         * We have now cleared all the crap off the end of the line
+         */
+        if (i < 2)
+            goto err;
+
+        bufp = (unsigned char *)buf;
+        if (first) {
+            first = 0;
+            if ((bufp[0] == '0') && (buf[1] == '0')) {
+                bufp += 2;
+                i -= 2;
+            }
+        }
+        k = 0;
+        i -= again;
+        if (i % 2 != 0) {
+            ASN1err(ASN1_F_A2I_ASN1_INTEGER, ASN1_R_ODD_NUMBER_OF_CHARS);
+            OPENSSL_free(s);
+            return 0;
+        }
+        i /= 2;
+        if (num + i > slen) {
+            sp = OPENSSL_clear_realloc(s, slen, num + i * 2);
+            if (sp == NULL) {
+                ASN1err(ASN1_F_A2I_ASN1_INTEGER, ERR_R_MALLOC_FAILURE);
+                OPENSSL_free(s);
+                return 0;
+            }
+            s = sp;
+            slen = num + i * 2;
+        }
+        for (j = 0; j < i; j++, k += 2) {
+            for (n = 0; n < 2; n++) {
+                m = OPENSSL_hexchar2int(bufp[k + n]);
+                if (m < 0) {
+                    ASN1err(ASN1_F_A2I_ASN1_INTEGER,
+                            ASN1_R_NON_HEX_CHARACTERS);
+                    goto err;
+                }
+                s[num + j] <<= 4;
+                s[num + j] |= m;
+            }
+        }
+        num += i;
+        if (again)
+            bufsize = BIO_gets(bp, buf, size);
+        else
+            break;
+    }
+    bs->length = num;
+    bs->data = s;
+    return 1;
+ err:
+    ASN1err(ASN1_F_A2I_ASN1_INTEGER, ASN1_R_SHORT_LINE);
+    OPENSSL_free(s);
+    return 0;
+}
+
+int i2a_ASN1_ENUMERATED(BIO *bp, const ASN1_ENUMERATED *a)
+{
+    return i2a_ASN1_INTEGER(bp, a);
+}
+
+int a2i_ASN1_ENUMERATED(BIO *bp, ASN1_ENUMERATED *bs, char *buf, int size)
+{
+    int rv = a2i_ASN1_INTEGER(bp, bs, buf, size);
+    if (rv == 1)
+        bs->type = V_ASN1_INTEGER | (bs->type & V_ASN1_NEG);
+    return rv;
+}
diff --git a/crypto/asn1/f_string.c b/crypto/asn1/f_string.c
new file mode 100644
index 000000000..b9258bba8
--- /dev/null
+++ b/crypto/asn1/f_string.c
@@ -0,0 +1,148 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+
+int i2a_ASN1_STRING(BIO *bp, const ASN1_STRING *a, int type)
+{
+    int i, n = 0;
+    static const char *h = "0123456789ABCDEF";
+    char buf[2];
+
+    if (a == NULL)
+        return (0);
+
+    if (a->length == 0) {
+        if (BIO_write(bp, "0", 1) != 1)
+            goto err;
+        n = 1;
+    } else {
+        for (i = 0; i < a->length; i++) {
+            if ((i != 0) && (i % 35 == 0)) {
+                if (BIO_write(bp, "\\\n", 2) != 2)
+                    goto err;
+                n += 2;
+            }
+            buf[0] = h[((unsigned char)a->data[i] >> 4) & 0x0f];
+            buf[1] = h[((unsigned char)a->data[i]) & 0x0f];
+            if (BIO_write(bp, buf, 2) != 2)
+                goto err;
+            n += 2;
+        }
+    }
+    return (n);
+ err:
+    return (-1);
+}
+
+int a2i_ASN1_STRING(BIO *bp, ASN1_STRING *bs, char *buf, int size)
+{
+    int i, j, k, m, n, again, bufsize, spec_char;
+    unsigned char *s = NULL, *sp;
+    unsigned char *bufp;
+    int num = 0, slen = 0, first = 1;
+
+    bufsize = BIO_gets(bp, buf, size);
+    for (;;) {
+        if (bufsize < 1) {
+            if (first)
+                break;
+            else
+                goto err;
+        }
+        first = 0;
+
+        i = bufsize;
+        if (buf[i - 1] == '\n')
+            buf[--i] = '\0';
+        if (i == 0)
+            goto err;
+        if (buf[i - 1] == '\r')
+            buf[--i] = '\0';
+        if (i == 0)
+            goto err;
+        again = (buf[i - 1] == '\\');
+
+        for (j = i - 1; j > 0; j--) {
+#ifndef CHARSET_EBCDIC
+            spec_char = (!(((buf[j] >= '0') && (buf[j] <= '9')) ||
+                  ((buf[j] >= 'a') && (buf[j] <= 'f')) ||
+                  ((buf[j] >= 'A') && (buf[j] <= 'F'))));
+#else
+            /*
+             * This #ifdef is not strictly necessary, since the characters
+             * A...F a...f 0...9 are contiguous (yes, even in EBCDIC - but
+             * not the whole alphabet). Nevertheless, isxdigit() is faster.
+             */
+            spec_char = (!isxdigit(buf[j]));
+#endif
+            if (spec_char) {
+                i = j;
+                break;
+            }
+        }
+        buf[i] = '\0';
+        /*
+         * We have now cleared all the crap off the end of the line
+         */
+        if (i < 2)
+            goto err;
+
+        bufp = (unsigned char *)buf;
+
+        k = 0;
+        i -= again;
+        if (i % 2 != 0) {
+            ASN1err(ASN1_F_A2I_ASN1_STRING, ASN1_R_ODD_NUMBER_OF_CHARS);
+            OPENSSL_free(s);
+            return 0;
+        }
+        i /= 2;
+        if (num + i > slen) {
+            sp = OPENSSL_realloc(s, (unsigned int)num + i * 2);
+            if (sp == NULL) {
+                ASN1err(ASN1_F_A2I_ASN1_STRING, ERR_R_MALLOC_FAILURE);
+                OPENSSL_free(s);
+                return 0;
+            }
+            s = sp;
+            slen = num + i * 2;
+        }
+        for (j = 0; j < i; j++, k += 2) {
+            for (n = 0; n < 2; n++) {
+                m = OPENSSL_hexchar2int(bufp[k + n]);
+                if (m < 0) {
+                    ASN1err(ASN1_F_A2I_ASN1_STRING,
+                            ASN1_R_NON_HEX_CHARACTERS);
+                    OPENSSL_free(s);
+                    return 0;
+                }
+                s[num + j] <<= 4;
+                s[num + j] |= m;
+            }
+        }
+        num += i;
+        if (again)
+            bufsize = BIO_gets(bp, buf, size);
+        else
+            break;
+    }
+    bs->length = num;
+    bs->data = s;
+    return 1;
+
+ err:
+    ASN1err(ASN1_F_A2I_ASN1_STRING, ASN1_R_SHORT_LINE);
+    OPENSSL_free(s);
+    return 0;
+}
diff --git a/crypto/asn1/i2d_pr.c b/crypto/asn1/i2d_pr.c
new file mode 100644
index 000000000..445b0c842
--- /dev/null
+++ b/crypto/asn1/i2d_pr.c
@@ -0,0 +1,33 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+#include "internal/asn1_int.h"
+#include "internal/evp_int.h"
+
+int i2d_PrivateKey(EVP_PKEY *a, unsigned char **pp)
+{
+    if (a->ameth && a->ameth->old_priv_encode) {
+        return a->ameth->old_priv_encode(a, pp);
+    }
+    if (a->ameth && a->ameth->priv_encode) {
+        PKCS8_PRIV_KEY_INFO *p8 = EVP_PKEY2PKCS8(a);
+        int ret = 0;
+        if (p8 != NULL) {
+            ret = i2d_PKCS8_PRIV_KEY_INFO(p8, pp);
+            PKCS8_PRIV_KEY_INFO_free(p8);
+        }
+        return ret;
+    }
+    ASN1err(ASN1_F_I2D_PRIVATEKEY, ASN1_R_UNSUPPORTED_PUBLIC_KEY_TYPE);
+    return -1;
+}
diff --git a/crypto/asn1/i2d_pu.c b/crypto/asn1/i2d_pu.c
new file mode 100644
index 000000000..3f9631a6c
--- /dev/null
+++ b/crypto/asn1/i2d_pu.c
@@ -0,0 +1,42 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+#include 
+#ifndef OPENSSL_NO_RSA
+# include 
+#endif
+#ifndef OPENSSL_NO_DSA
+# include 
+#endif
+#include 
+
+int i2d_PublicKey(EVP_PKEY *a, unsigned char **pp)
+{
+    switch (EVP_PKEY_id(a)) {
+#ifndef OPENSSL_NO_RSA
+    case EVP_PKEY_RSA:
+        return i2d_RSAPublicKey(EVP_PKEY_get0_RSA(a), pp);
+#endif
+#ifndef OPENSSL_NO_DSA
+    case EVP_PKEY_DSA:
+        return i2d_DSAPublicKey(EVP_PKEY_get0_DSA(a), pp);
+#endif
+#ifndef OPENSSL_NO_EC
+    case EVP_PKEY_EC:
+        return i2o_ECPublicKey(EVP_PKEY_get0_EC_KEY(a), pp);
+#endif
+    default:
+        ASN1err(ASN1_F_I2D_PUBLICKEY, ASN1_R_UNSUPPORTED_PUBLIC_KEY_TYPE);
+        return -1;
+    }
+}
diff --git a/crypto/asn1/n_pkey.c b/crypto/asn1/n_pkey.c
new file mode 100644
index 000000000..267ce6011
--- /dev/null
+++ b/crypto/asn1/n_pkey.c
@@ -0,0 +1,62 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "openssl/opensslconf.h"
+#ifdef OPENSSL_NO_RSA
+NON_EMPTY_TRANSLATION_UNIT
+#else
+
+# include "internal/cryptlib.h"
+# include 
+# include 
+# include 
+# include 
+# include 
+# include 
+
+# ifndef OPENSSL_NO_RC4
+
+typedef struct netscape_pkey_st {
+    long version;
+    X509_ALGOR *algor;
+    ASN1_OCTET_STRING *private_key;
+} NETSCAPE_PKEY;
+
+typedef struct netscape_encrypted_pkey_st {
+    ASN1_OCTET_STRING *os;
+    /*
+     * This is the same structure as DigestInfo so use it: although this
+     * isn't really anything to do with digests.
+     */
+    X509_SIG *enckey;
+} NETSCAPE_ENCRYPTED_PKEY;
+
+
+ASN1_BROKEN_SEQUENCE(NETSCAPE_ENCRYPTED_PKEY) = {
+        ASN1_SIMPLE(NETSCAPE_ENCRYPTED_PKEY, os, ASN1_OCTET_STRING),
+        ASN1_SIMPLE(NETSCAPE_ENCRYPTED_PKEY, enckey, X509_SIG)
+} static_ASN1_BROKEN_SEQUENCE_END(NETSCAPE_ENCRYPTED_PKEY)
+
+DECLARE_ASN1_FUNCTIONS_const(NETSCAPE_ENCRYPTED_PKEY)
+DECLARE_ASN1_ENCODE_FUNCTIONS_const(NETSCAPE_ENCRYPTED_PKEY,NETSCAPE_ENCRYPTED_PKEY)
+IMPLEMENT_ASN1_FUNCTIONS_const(NETSCAPE_ENCRYPTED_PKEY)
+
+ASN1_SEQUENCE(NETSCAPE_PKEY) = {
+        ASN1_SIMPLE(NETSCAPE_PKEY, version, LONG),
+        ASN1_SIMPLE(NETSCAPE_PKEY, algor, X509_ALGOR),
+        ASN1_SIMPLE(NETSCAPE_PKEY, private_key, ASN1_OCTET_STRING)
+} static_ASN1_SEQUENCE_END(NETSCAPE_PKEY)
+
+DECLARE_ASN1_FUNCTIONS_const(NETSCAPE_PKEY)
+DECLARE_ASN1_ENCODE_FUNCTIONS_const(NETSCAPE_PKEY,NETSCAPE_PKEY)
+IMPLEMENT_ASN1_FUNCTIONS_const(NETSCAPE_PKEY)
+
+# endif                         /* OPENSSL_NO_RC4 */
+
+#endif
diff --git a/crypto/asn1/nsseq.c b/crypto/asn1/nsseq.c
new file mode 100644
index 000000000..c7baf40d3
--- /dev/null
+++ b/crypto/asn1/nsseq.c
@@ -0,0 +1,34 @@
+/*
+ * Copyright 1999-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+
+static int nsseq_cb(int operation, ASN1_VALUE **pval, const ASN1_ITEM *it,
+                    void *exarg)
+{
+    if (operation == ASN1_OP_NEW_POST) {
+        NETSCAPE_CERT_SEQUENCE *nsseq;
+        nsseq = (NETSCAPE_CERT_SEQUENCE *)*pval;
+        nsseq->type = OBJ_nid2obj(NID_netscape_cert_sequence);
+    }
+    return 1;
+}
+
+/* Netscape certificate sequence structure */
+
+ASN1_SEQUENCE_cb(NETSCAPE_CERT_SEQUENCE, nsseq_cb) = {
+        ASN1_SIMPLE(NETSCAPE_CERT_SEQUENCE, type, ASN1_OBJECT),
+        ASN1_EXP_SEQUENCE_OF_OPT(NETSCAPE_CERT_SEQUENCE, certs, X509, 0)
+} ASN1_SEQUENCE_END_cb(NETSCAPE_CERT_SEQUENCE, NETSCAPE_CERT_SEQUENCE)
+
+IMPLEMENT_ASN1_FUNCTIONS(NETSCAPE_CERT_SEQUENCE)
diff --git a/crypto/asn1/p5_pbe.c b/crypto/asn1/p5_pbe.c
new file mode 100644
index 000000000..ab7e16898
--- /dev/null
+++ b/crypto/asn1/p5_pbe.c
@@ -0,0 +1,96 @@
+/*
+ * Copyright 1999-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+#include 
+
+/* PKCS#5 password based encryption structure */
+
+ASN1_SEQUENCE(PBEPARAM) = {
+        ASN1_SIMPLE(PBEPARAM, salt, ASN1_OCTET_STRING),
+        ASN1_SIMPLE(PBEPARAM, iter, ASN1_INTEGER)
+} ASN1_SEQUENCE_END(PBEPARAM)
+
+IMPLEMENT_ASN1_FUNCTIONS(PBEPARAM)
+
+/* Set an algorithm identifier for a PKCS#5 PBE algorithm */
+
+int PKCS5_pbe_set0_algor(X509_ALGOR *algor, int alg, int iter,
+                         const unsigned char *salt, int saltlen)
+{
+    PBEPARAM *pbe = NULL;
+    ASN1_STRING *pbe_str = NULL;
+    unsigned char *sstr = NULL;
+
+    pbe = PBEPARAM_new();
+    if (pbe == NULL) {
+        ASN1err(ASN1_F_PKCS5_PBE_SET0_ALGOR, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+    if (iter <= 0)
+        iter = PKCS5_DEFAULT_ITER;
+    if (!ASN1_INTEGER_set(pbe->iter, iter)) {
+        ASN1err(ASN1_F_PKCS5_PBE_SET0_ALGOR, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+    if (!saltlen)
+        saltlen = PKCS5_SALT_LEN;
+
+    sstr = OPENSSL_malloc(saltlen);
+    if (sstr == NULL) {
+        ASN1err(ASN1_F_PKCS5_PBE_SET0_ALGOR, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+    if (salt)
+        memcpy(sstr, salt, saltlen);
+    else if (RAND_bytes(sstr, saltlen) <= 0)
+        goto err;
+
+    ASN1_STRING_set0(pbe->salt, sstr, saltlen);
+    sstr = NULL;
+
+    if (!ASN1_item_pack(pbe, ASN1_ITEM_rptr(PBEPARAM), &pbe_str)) {
+        ASN1err(ASN1_F_PKCS5_PBE_SET0_ALGOR, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+
+    PBEPARAM_free(pbe);
+    pbe = NULL;
+
+    if (X509_ALGOR_set0(algor, OBJ_nid2obj(alg), V_ASN1_SEQUENCE, pbe_str))
+        return 1;
+
+ err:
+    OPENSSL_free(sstr);
+    PBEPARAM_free(pbe);
+    ASN1_STRING_free(pbe_str);
+    return 0;
+}
+
+/* Return an algorithm identifier for a PKCS#5 PBE algorithm */
+
+X509_ALGOR *PKCS5_pbe_set(int alg, int iter,
+                          const unsigned char *salt, int saltlen)
+{
+    X509_ALGOR *ret;
+    ret = X509_ALGOR_new();
+    if (ret == NULL) {
+        ASN1err(ASN1_F_PKCS5_PBE_SET, ERR_R_MALLOC_FAILURE);
+        return NULL;
+    }
+
+    if (PKCS5_pbe_set0_algor(ret, alg, iter, salt, saltlen))
+        return ret;
+
+    X509_ALGOR_free(ret);
+    return NULL;
+}
diff --git a/crypto/asn1/p5_pbev2.c b/crypto/asn1/p5_pbev2.c
new file mode 100644
index 000000000..14e8700b7
--- /dev/null
+++ b/crypto/asn1/p5_pbev2.c
@@ -0,0 +1,221 @@
+/*
+ * Copyright 1999-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+#include 
+
+/* PKCS#5 v2.0 password based encryption structures */
+
+ASN1_SEQUENCE(PBE2PARAM) = {
+        ASN1_SIMPLE(PBE2PARAM, keyfunc, X509_ALGOR),
+        ASN1_SIMPLE(PBE2PARAM, encryption, X509_ALGOR)
+} ASN1_SEQUENCE_END(PBE2PARAM)
+
+IMPLEMENT_ASN1_FUNCTIONS(PBE2PARAM)
+
+ASN1_SEQUENCE(PBKDF2PARAM) = {
+        ASN1_SIMPLE(PBKDF2PARAM, salt, ASN1_ANY),
+        ASN1_SIMPLE(PBKDF2PARAM, iter, ASN1_INTEGER),
+        ASN1_OPT(PBKDF2PARAM, keylength, ASN1_INTEGER),
+        ASN1_OPT(PBKDF2PARAM, prf, X509_ALGOR)
+} ASN1_SEQUENCE_END(PBKDF2PARAM)
+
+IMPLEMENT_ASN1_FUNCTIONS(PBKDF2PARAM)
+
+/*
+ * Return an algorithm identifier for a PKCS#5 v2.0 PBE algorithm: yes I know
+ * this is horrible! Extended version to allow application supplied PRF NID
+ * and IV.
+ */
+
+X509_ALGOR *PKCS5_pbe2_set_iv(const EVP_CIPHER *cipher, int iter,
+                              unsigned char *salt, int saltlen,
+                              unsigned char *aiv, int prf_nid)
+{
+    X509_ALGOR *scheme = NULL, *ret = NULL;
+    int alg_nid, keylen;
+    EVP_CIPHER_CTX *ctx = NULL;
+    unsigned char iv[EVP_MAX_IV_LENGTH];
+    PBE2PARAM *pbe2 = NULL;
+
+    alg_nid = EVP_CIPHER_type(cipher);
+    if (alg_nid == NID_undef) {
+        ASN1err(ASN1_F_PKCS5_PBE2_SET_IV,
+                ASN1_R_CIPHER_HAS_NO_OBJECT_IDENTIFIER);
+        goto err;
+    }
+
+    if ((pbe2 = PBE2PARAM_new()) == NULL)
+        goto merr;
+
+    /* Setup the AlgorithmIdentifier for the encryption scheme */
+    scheme = pbe2->encryption;
+    scheme->algorithm = OBJ_nid2obj(alg_nid);
+    if ((scheme->parameter = ASN1_TYPE_new()) == NULL)
+        goto merr;
+
+    /* Create random IV */
+    if (EVP_CIPHER_iv_length(cipher)) {
+        if (aiv)
+            memcpy(iv, aiv, EVP_CIPHER_iv_length(cipher));
+        else if (RAND_bytes(iv, EVP_CIPHER_iv_length(cipher)) <= 0)
+            goto err;
+    }
+
+    ctx = EVP_CIPHER_CTX_new();
+    if (ctx == NULL)
+        goto merr;
+
+    /* Dummy cipherinit to just setup the IV, and PRF */
+    if (!EVP_CipherInit_ex(ctx, cipher, NULL, NULL, iv, 0))
+        goto err;
+    if (EVP_CIPHER_param_to_asn1(ctx, scheme->parameter) < 0) {
+        ASN1err(ASN1_F_PKCS5_PBE2_SET_IV, ASN1_R_ERROR_SETTING_CIPHER_PARAMS);
+        goto err;
+    }
+    /*
+     * If prf NID unspecified see if cipher has a preference. An error is OK
+     * here: just means use default PRF.
+     */
+    if ((prf_nid == -1) &&
+        EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_PBE_PRF_NID, 0, &prf_nid) <= 0) {
+        ERR_clear_error();
+        prf_nid = NID_hmacWithSHA256;
+    }
+    EVP_CIPHER_CTX_free(ctx);
+    ctx = NULL;
+
+    /* If its RC2 then we'd better setup the key length */
+
+    if (alg_nid == NID_rc2_cbc)
+        keylen = EVP_CIPHER_key_length(cipher);
+    else
+        keylen = -1;
+
+    /* Setup keyfunc */
+
+    X509_ALGOR_free(pbe2->keyfunc);
+
+    pbe2->keyfunc = PKCS5_pbkdf2_set(iter, salt, saltlen, prf_nid, keylen);
+
+    if (!pbe2->keyfunc)
+        goto merr;
+
+    /* Now set up top level AlgorithmIdentifier */
+
+    if ((ret = X509_ALGOR_new()) == NULL)
+        goto merr;
+
+    ret->algorithm = OBJ_nid2obj(NID_pbes2);
+
+    /* Encode PBE2PARAM into parameter */
+
+    if (!ASN1_TYPE_pack_sequence(ASN1_ITEM_rptr(PBE2PARAM), pbe2,
+                                 &ret->parameter))
+         goto merr;
+
+    PBE2PARAM_free(pbe2);
+    pbe2 = NULL;
+
+    return ret;
+
+ merr:
+    ASN1err(ASN1_F_PKCS5_PBE2_SET_IV, ERR_R_MALLOC_FAILURE);
+
+ err:
+    EVP_CIPHER_CTX_free(ctx);
+    PBE2PARAM_free(pbe2);
+    /* Note 'scheme' is freed as part of pbe2 */
+    X509_ALGOR_free(ret);
+
+    return NULL;
+}
+
+X509_ALGOR *PKCS5_pbe2_set(const EVP_CIPHER *cipher, int iter,
+                           unsigned char *salt, int saltlen)
+{
+    return PKCS5_pbe2_set_iv(cipher, iter, salt, saltlen, NULL, -1);
+}
+
+X509_ALGOR *PKCS5_pbkdf2_set(int iter, unsigned char *salt, int saltlen,
+                             int prf_nid, int keylen)
+{
+    X509_ALGOR *keyfunc = NULL;
+    PBKDF2PARAM *kdf = NULL;
+    ASN1_OCTET_STRING *osalt = NULL;
+
+    if ((kdf = PBKDF2PARAM_new()) == NULL)
+        goto merr;
+    if ((osalt = ASN1_OCTET_STRING_new()) == NULL)
+        goto merr;
+
+    kdf->salt->value.octet_string = osalt;
+    kdf->salt->type = V_ASN1_OCTET_STRING;
+
+    if (saltlen == 0)
+        saltlen = PKCS5_SALT_LEN;
+    if ((osalt->data = OPENSSL_malloc(saltlen)) == NULL)
+        goto merr;
+
+    osalt->length = saltlen;
+
+    if (salt)
+        memcpy(osalt->data, salt, saltlen);
+    else if (RAND_bytes(osalt->data, saltlen) <= 0)
+        goto merr;
+
+    if (iter <= 0)
+        iter = PKCS5_DEFAULT_ITER;
+
+    if (!ASN1_INTEGER_set(kdf->iter, iter))
+        goto merr;
+
+    /* If have a key len set it up */
+
+    if (keylen > 0) {
+        if ((kdf->keylength = ASN1_INTEGER_new()) == NULL)
+            goto merr;
+        if (!ASN1_INTEGER_set(kdf->keylength, keylen))
+            goto merr;
+    }
+
+    /* prf can stay NULL if we are using hmacWithSHA1 */
+    if (prf_nid > 0 && prf_nid != NID_hmacWithSHA1) {
+        kdf->prf = X509_ALGOR_new();
+        if (kdf->prf == NULL)
+            goto merr;
+        X509_ALGOR_set0(kdf->prf, OBJ_nid2obj(prf_nid), V_ASN1_NULL, NULL);
+    }
+
+    /* Finally setup the keyfunc structure */
+
+    keyfunc = X509_ALGOR_new();
+    if (keyfunc == NULL)
+        goto merr;
+
+    keyfunc->algorithm = OBJ_nid2obj(NID_id_pbkdf2);
+
+    /* Encode PBKDF2PARAM into parameter of pbe2 */
+
+    if (!ASN1_TYPE_pack_sequence(ASN1_ITEM_rptr(PBKDF2PARAM), kdf,
+                                 &keyfunc->parameter))
+         goto merr;
+
+    PBKDF2PARAM_free(kdf);
+    return keyfunc;
+
+ merr:
+    ASN1err(ASN1_F_PKCS5_PBKDF2_SET, ERR_R_MALLOC_FAILURE);
+    PBKDF2PARAM_free(kdf);
+    X509_ALGOR_free(keyfunc);
+    return NULL;
+}
diff --git a/crypto/asn1/p5_scrypt.c b/crypto/asn1/p5_scrypt.c
new file mode 100644
index 000000000..4cb783749
--- /dev/null
+++ b/crypto/asn1/p5_scrypt.c
@@ -0,0 +1,283 @@
+/*
+ * Copyright 2015-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#ifndef OPENSSL_NO_SCRYPT
+/* PKCS#5 scrypt password based encryption structures */
+
+typedef struct {
+    ASN1_OCTET_STRING *salt;
+    ASN1_INTEGER *costParameter;
+    ASN1_INTEGER *blockSize;
+    ASN1_INTEGER *parallelizationParameter;
+    ASN1_INTEGER *keyLength;
+} SCRYPT_PARAMS;
+
+ASN1_SEQUENCE(SCRYPT_PARAMS) = {
+        ASN1_SIMPLE(SCRYPT_PARAMS, salt, ASN1_OCTET_STRING),
+        ASN1_SIMPLE(SCRYPT_PARAMS, costParameter, ASN1_INTEGER),
+        ASN1_SIMPLE(SCRYPT_PARAMS, blockSize, ASN1_INTEGER),
+        ASN1_SIMPLE(SCRYPT_PARAMS, parallelizationParameter, ASN1_INTEGER),
+        ASN1_OPT(SCRYPT_PARAMS, keyLength, ASN1_INTEGER),
+} static_ASN1_SEQUENCE_END(SCRYPT_PARAMS)
+
+DECLARE_ASN1_ALLOC_FUNCTIONS(SCRYPT_PARAMS)
+IMPLEMENT_ASN1_ALLOC_FUNCTIONS(SCRYPT_PARAMS)
+
+static X509_ALGOR *pkcs5_scrypt_set(const unsigned char *salt, size_t saltlen,
+                                    size_t keylen, uint64_t N, uint64_t r,
+                                    uint64_t p);
+
+/*
+ * Return an algorithm identifier for a PKCS#5 v2.0 PBE algorithm using scrypt
+ */
+
+X509_ALGOR *PKCS5_pbe2_set_scrypt(const EVP_CIPHER *cipher,
+                                  const unsigned char *salt, int saltlen,
+                                  unsigned char *aiv, uint64_t N, uint64_t r,
+                                  uint64_t p)
+{
+    X509_ALGOR *scheme = NULL, *ret = NULL;
+    int alg_nid;
+    size_t keylen = 0;
+    EVP_CIPHER_CTX *ctx = NULL;
+    unsigned char iv[EVP_MAX_IV_LENGTH];
+    PBE2PARAM *pbe2 = NULL;
+
+    if (!cipher) {
+        ASN1err(ASN1_F_PKCS5_PBE2_SET_SCRYPT, ERR_R_PASSED_NULL_PARAMETER);
+        goto err;
+    }
+
+    if (EVP_PBE_scrypt(NULL, 0, NULL, 0, N, r, p, 0, NULL, 0) == 0) {
+        ASN1err(ASN1_F_PKCS5_PBE2_SET_SCRYPT,
+                ASN1_R_INVALID_SCRYPT_PARAMETERS);
+        goto err;
+    }
+
+    alg_nid = EVP_CIPHER_type(cipher);
+    if (alg_nid == NID_undef) {
+        ASN1err(ASN1_F_PKCS5_PBE2_SET_SCRYPT,
+                ASN1_R_CIPHER_HAS_NO_OBJECT_IDENTIFIER);
+        goto err;
+    }
+
+    pbe2 = PBE2PARAM_new();
+    if (pbe2 == NULL)
+        goto merr;
+
+    /* Setup the AlgorithmIdentifier for the encryption scheme */
+    scheme = pbe2->encryption;
+
+    scheme->algorithm = OBJ_nid2obj(alg_nid);
+    scheme->parameter = ASN1_TYPE_new();
+    if (scheme->parameter == NULL)
+        goto merr;
+
+    /* Create random IV */
+    if (EVP_CIPHER_iv_length(cipher)) {
+        if (aiv)
+            memcpy(iv, aiv, EVP_CIPHER_iv_length(cipher));
+        else if (RAND_bytes(iv, EVP_CIPHER_iv_length(cipher)) < 0)
+            goto err;
+    }
+
+    ctx = EVP_CIPHER_CTX_new();
+    if (ctx == NULL)
+        goto merr;
+
+    /* Dummy cipherinit to just setup the IV */
+    if (EVP_CipherInit_ex(ctx, cipher, NULL, NULL, iv, 0) == 0)
+        goto err;
+    if (EVP_CIPHER_param_to_asn1(ctx, scheme->parameter) < 0) {
+        ASN1err(ASN1_F_PKCS5_PBE2_SET_SCRYPT,
+                ASN1_R_ERROR_SETTING_CIPHER_PARAMS);
+        goto err;
+    }
+    EVP_CIPHER_CTX_free(ctx);
+    ctx = NULL;
+
+    /* If its RC2 then we'd better setup the key length */
+
+    if (alg_nid == NID_rc2_cbc)
+        keylen = EVP_CIPHER_key_length(cipher);
+
+    /* Setup keyfunc */
+
+    X509_ALGOR_free(pbe2->keyfunc);
+
+    pbe2->keyfunc = pkcs5_scrypt_set(salt, saltlen, keylen, N, r, p);
+
+    if (pbe2->keyfunc == NULL)
+        goto merr;
+
+    /* Now set up top level AlgorithmIdentifier */
+
+    ret = X509_ALGOR_new();
+    if (ret == NULL)
+        goto merr;
+
+    ret->algorithm = OBJ_nid2obj(NID_pbes2);
+
+    /* Encode PBE2PARAM into parameter */
+
+    if (ASN1_TYPE_pack_sequence(ASN1_ITEM_rptr(PBE2PARAM), pbe2,
+                                &ret->parameter) == NULL)
+        goto merr;
+
+    PBE2PARAM_free(pbe2);
+    pbe2 = NULL;
+
+    return ret;
+
+ merr:
+    ASN1err(ASN1_F_PKCS5_PBE2_SET_SCRYPT, ERR_R_MALLOC_FAILURE);
+
+ err:
+    PBE2PARAM_free(pbe2);
+    X509_ALGOR_free(ret);
+    EVP_CIPHER_CTX_free(ctx);
+
+    return NULL;
+}
+
+static X509_ALGOR *pkcs5_scrypt_set(const unsigned char *salt, size_t saltlen,
+                                    size_t keylen, uint64_t N, uint64_t r,
+                                    uint64_t p)
+{
+    X509_ALGOR *keyfunc = NULL;
+    SCRYPT_PARAMS *sparam = SCRYPT_PARAMS_new();
+
+    if (sparam == NULL)
+        goto merr;
+
+    if (!saltlen)
+        saltlen = PKCS5_SALT_LEN;
+
+    /* This will either copy salt or grow the buffer */
+    if (ASN1_STRING_set(sparam->salt, salt, saltlen) == 0)
+        goto merr;
+
+    if (salt == NULL && RAND_bytes(sparam->salt->data, saltlen) <= 0)
+        goto err;
+
+    if (ASN1_INTEGER_set_uint64(sparam->costParameter, N) == 0)
+        goto merr;
+
+    if (ASN1_INTEGER_set_uint64(sparam->blockSize, r) == 0)
+        goto merr;
+
+    if (ASN1_INTEGER_set_uint64(sparam->parallelizationParameter, p) == 0)
+        goto merr;
+
+    /* If have a key len set it up */
+
+    if (keylen > 0) {
+        sparam->keyLength = ASN1_INTEGER_new();
+        if (sparam->keyLength == NULL)
+            goto merr;
+        if (ASN1_INTEGER_set_int64(sparam->keyLength, keylen) == 0)
+            goto merr;
+    }
+
+    /* Finally setup the keyfunc structure */
+
+    keyfunc = X509_ALGOR_new();
+    if (keyfunc == NULL)
+        goto merr;
+
+    keyfunc->algorithm = OBJ_nid2obj(NID_id_scrypt);
+
+    /* Encode SCRYPT_PARAMS into parameter of pbe2 */
+
+    if (ASN1_TYPE_pack_sequence(ASN1_ITEM_rptr(SCRYPT_PARAMS), sparam,
+                                &keyfunc->parameter) == NULL)
+        goto merr;
+
+    SCRYPT_PARAMS_free(sparam);
+    return keyfunc;
+
+ merr:
+    ASN1err(ASN1_F_PKCS5_SCRYPT_SET, ERR_R_MALLOC_FAILURE);
+ err:
+    SCRYPT_PARAMS_free(sparam);
+    X509_ALGOR_free(keyfunc);
+    return NULL;
+}
+
+int PKCS5_v2_scrypt_keyivgen(EVP_CIPHER_CTX *ctx, const char *pass,
+                             int passlen, ASN1_TYPE *param,
+                             const EVP_CIPHER *c, const EVP_MD *md, int en_de)
+{
+    unsigned char *salt, key[EVP_MAX_KEY_LENGTH];
+    uint64_t p, r, N;
+    size_t saltlen;
+    size_t keylen = 0;
+    int rv = 0;
+    SCRYPT_PARAMS *sparam = NULL;
+
+    if (EVP_CIPHER_CTX_cipher(ctx) == NULL) {
+        EVPerr(EVP_F_PKCS5_V2_SCRYPT_KEYIVGEN, EVP_R_NO_CIPHER_SET);
+        goto err;
+    }
+
+    /* Decode parameter */
+
+    sparam = ASN1_TYPE_unpack_sequence(ASN1_ITEM_rptr(SCRYPT_PARAMS), param);
+
+    if (sparam == NULL) {
+        EVPerr(EVP_F_PKCS5_V2_SCRYPT_KEYIVGEN, EVP_R_DECODE_ERROR);
+        goto err;
+    }
+
+    keylen = EVP_CIPHER_CTX_key_length(ctx);
+
+    /* Now check the parameters of sparam */
+
+    if (sparam->keyLength) {
+        uint64_t spkeylen;
+        if ((ASN1_INTEGER_get_uint64(&spkeylen, sparam->keyLength) == 0)
+            || (spkeylen != keylen)) {
+            EVPerr(EVP_F_PKCS5_V2_SCRYPT_KEYIVGEN,
+                   EVP_R_UNSUPPORTED_KEYLENGTH);
+            goto err;
+        }
+    }
+    /* Check all parameters fit in uint64_t and are acceptable to scrypt */
+    if (ASN1_INTEGER_get_uint64(&N, sparam->costParameter) == 0
+        || ASN1_INTEGER_get_uint64(&r, sparam->blockSize) == 0
+        || ASN1_INTEGER_get_uint64(&p, sparam->parallelizationParameter) == 0
+        || EVP_PBE_scrypt(NULL, 0, NULL, 0, N, r, p, 0, NULL, 0) == 0) {
+        EVPerr(EVP_F_PKCS5_V2_SCRYPT_KEYIVGEN,
+               EVP_R_ILLEGAL_SCRYPT_PARAMETERS);
+        goto err;
+    }
+
+    /* it seems that its all OK */
+
+    salt = sparam->salt->data;
+    saltlen = sparam->salt->length;
+    if (EVP_PBE_scrypt(pass, passlen, salt, saltlen, N, r, p, 0, key, keylen)
+        == 0)
+        goto err;
+    rv = EVP_CipherInit_ex(ctx, NULL, NULL, key, NULL, en_de);
+ err:
+    if (keylen)
+        OPENSSL_cleanse(key, keylen);
+    SCRYPT_PARAMS_free(sparam);
+    return rv;
+}
+#endif /* OPENSSL_NO_SCRYPT */
diff --git a/crypto/asn1/p8_pkey.c b/crypto/asn1/p8_pkey.c
new file mode 100644
index 000000000..dbee827e0
--- /dev/null
+++ b/crypto/asn1/p8_pkey.c
@@ -0,0 +1,80 @@
+/*
+ * Copyright 1999-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+#include "internal/x509_int.h"
+
+/* Minor tweak to operation: zero private key data */
+static int pkey_cb(int operation, ASN1_VALUE **pval, const ASN1_ITEM *it,
+                   void *exarg)
+{
+    /* Since the structure must still be valid use ASN1_OP_FREE_PRE */
+    if (operation == ASN1_OP_FREE_PRE) {
+        PKCS8_PRIV_KEY_INFO *key = (PKCS8_PRIV_KEY_INFO *)*pval;
+        if (key->pkey)
+            OPENSSL_cleanse(key->pkey->data, key->pkey->length);
+    }
+    return 1;
+}
+
+ASN1_SEQUENCE_cb(PKCS8_PRIV_KEY_INFO, pkey_cb) = {
+        ASN1_SIMPLE(PKCS8_PRIV_KEY_INFO, version, ASN1_INTEGER),
+        ASN1_SIMPLE(PKCS8_PRIV_KEY_INFO, pkeyalg, X509_ALGOR),
+        ASN1_SIMPLE(PKCS8_PRIV_KEY_INFO, pkey, ASN1_OCTET_STRING),
+        ASN1_IMP_SET_OF_OPT(PKCS8_PRIV_KEY_INFO, attributes, X509_ATTRIBUTE, 0)
+} ASN1_SEQUENCE_END_cb(PKCS8_PRIV_KEY_INFO, PKCS8_PRIV_KEY_INFO)
+
+IMPLEMENT_ASN1_FUNCTIONS(PKCS8_PRIV_KEY_INFO)
+
+int PKCS8_pkey_set0(PKCS8_PRIV_KEY_INFO *priv, ASN1_OBJECT *aobj,
+                    int version,
+                    int ptype, void *pval, unsigned char *penc, int penclen)
+{
+    if (version >= 0) {
+        if (!ASN1_INTEGER_set(priv->version, version))
+            return 0;
+    }
+    if (!X509_ALGOR_set0(priv->pkeyalg, aobj, ptype, pval))
+        return 0;
+    if (penc)
+        ASN1_STRING_set0(priv->pkey, penc, penclen);
+    return 1;
+}
+
+int PKCS8_pkey_get0(const ASN1_OBJECT **ppkalg,
+                    const unsigned char **pk, int *ppklen,
+                    const X509_ALGOR **pa, const PKCS8_PRIV_KEY_INFO *p8)
+{
+    if (ppkalg)
+        *ppkalg = p8->pkeyalg->algorithm;
+    if (pk) {
+        *pk = ASN1_STRING_get0_data(p8->pkey);
+        *ppklen = ASN1_STRING_length(p8->pkey);
+    }
+    if (pa)
+        *pa = p8->pkeyalg;
+    return 1;
+}
+
+const STACK_OF(X509_ATTRIBUTE) *
+PKCS8_pkey_get0_attrs(const PKCS8_PRIV_KEY_INFO *p8)
+{
+    return p8->attributes;
+}
+
+int PKCS8_pkey_add1_attr_by_NID(PKCS8_PRIV_KEY_INFO *p8, int nid, int type,
+                                const unsigned char *bytes, int len)
+{
+    if (X509at_add1_attr_by_NID(&p8->attributes, nid, type, bytes, len) != NULL)
+        return 1;
+    return 0;
+}
diff --git a/crypto/asn1/t_bitst.c b/crypto/asn1/t_bitst.c
new file mode 100644
index 000000000..c0aeca4c7
--- /dev/null
+++ b/crypto/asn1/t_bitst.c
@@ -0,0 +1,56 @@
+/*
+ * Copyright 1999-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+
+int ASN1_BIT_STRING_name_print(BIO *out, ASN1_BIT_STRING *bs,
+                               BIT_STRING_BITNAME *tbl, int indent)
+{
+    BIT_STRING_BITNAME *bnam;
+    char first = 1;
+    BIO_printf(out, "%*s", indent, "");
+    for (bnam = tbl; bnam->lname; bnam++) {
+        if (ASN1_BIT_STRING_get_bit(bs, bnam->bitnum)) {
+            if (!first)
+                BIO_puts(out, ", ");
+            BIO_puts(out, bnam->lname);
+            first = 0;
+        }
+    }
+    BIO_puts(out, "\n");
+    return 1;
+}
+
+int ASN1_BIT_STRING_set_asc(ASN1_BIT_STRING *bs, const char *name, int value,
+                            BIT_STRING_BITNAME *tbl)
+{
+    int bitnum;
+    bitnum = ASN1_BIT_STRING_num_asc(name, tbl);
+    if (bitnum < 0)
+        return 0;
+    if (bs) {
+        if (!ASN1_BIT_STRING_set_bit(bs, bitnum, value))
+            return 0;
+    }
+    return 1;
+}
+
+int ASN1_BIT_STRING_num_asc(const char *name, BIT_STRING_BITNAME *tbl)
+{
+    BIT_STRING_BITNAME *bnam;
+    for (bnam = tbl; bnam->lname; bnam++) {
+        if ((strcmp(bnam->sname, name) == 0)
+            || (strcmp(bnam->lname, name) == 0))
+            return bnam->bitnum;
+    }
+    return -1;
+}
diff --git a/crypto/asn1/t_pkey.c b/crypto/asn1/t_pkey.c
new file mode 100644
index 000000000..3b2c9df89
--- /dev/null
+++ b/crypto/asn1/t_pkey.c
@@ -0,0 +1,93 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+#include "internal/bn_int.h"
+
+/* Number of octets per line */
+#define ASN1_BUF_PRINT_WIDTH    15
+/* Maximum indent */
+#define ASN1_PRINT_MAX_INDENT 128
+
+int ASN1_buf_print(BIO *bp, const unsigned char *buf, size_t buflen, int indent)
+{
+    size_t i;
+
+    for (i = 0; i < buflen; i++) {
+        if ((i % ASN1_BUF_PRINT_WIDTH) == 0) {
+            if (i > 0 && BIO_puts(bp, "\n") <= 0)
+                return 0;
+            if (!BIO_indent(bp, indent, ASN1_PRINT_MAX_INDENT))
+                return 0;
+        }
+        /*
+         * Use colon separators for each octet for compatibility as
+         * this function is used to print out key components.
+         */
+        if (BIO_printf(bp, "%02x%s", buf[i],
+                       (i == buflen - 1) ? "" : ":") <= 0)
+                return 0;
+    }
+    if (BIO_write(bp, "\n", 1) <= 0)
+        return 0;
+    return 1;
+}
+
+int ASN1_bn_print(BIO *bp, const char *number, const BIGNUM *num,
+                  unsigned char *ign, int indent)
+{
+    int n, rv = 0;
+    const char *neg;
+    unsigned char *buf = NULL, *tmp = NULL;
+    int buflen;
+
+    if (num == NULL)
+        return 1;
+    neg = BN_is_negative(num) ? "-" : "";
+    if (!BIO_indent(bp, indent, ASN1_PRINT_MAX_INDENT))
+        return 0;
+    if (BN_is_zero(num)) {
+        if (BIO_printf(bp, "%s 0\n", number) <= 0)
+            return 0;
+        return 1;
+    }
+
+    if (BN_num_bytes(num) <= BN_BYTES) {
+        if (BIO_printf(bp, "%s %s%lu (%s0x%lx)\n", number, neg,
+                       (unsigned long)bn_get_words(num)[0], neg,
+                       (unsigned long)bn_get_words(num)[0]) <= 0)
+            return 0;
+        return 1;
+    }
+
+    buflen = BN_num_bytes(num) + 1;
+    buf = tmp = OPENSSL_malloc(buflen);
+    if (buf == NULL)
+        goto err;
+    buf[0] = 0;
+    if (BIO_printf(bp, "%s%s\n", number,
+                   (neg[0] == '-') ? " (Negative)" : "") <= 0)
+        goto err;
+    n = BN_bn2bin(num, buf + 1);
+
+    if (buf[1] & 0x80)
+        n++;
+    else
+        tmp++;
+
+    if (ASN1_buf_print(bp, tmp, n, indent + 4) == 0)
+        goto err;
+    rv = 1;
+    err:
+    OPENSSL_clear_free(buf, buflen);
+    return rv;
+}
diff --git a/crypto/asn1/t_spki.c b/crypto/asn1/t_spki.c
new file mode 100644
index 000000000..f871bd237
--- /dev/null
+++ b/crypto/asn1/t_spki.c
@@ -0,0 +1,60 @@
+/*
+ * Copyright 1999-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+#ifndef OPENSSL_NO_RSA
+# include 
+#endif
+#ifndef OPENSSL_NO_DSA
+# include 
+#endif
+#include 
+
+/* Print out an SPKI */
+
+int NETSCAPE_SPKI_print(BIO *out, NETSCAPE_SPKI *spki)
+{
+    EVP_PKEY *pkey;
+    ASN1_IA5STRING *chal;
+    ASN1_OBJECT *spkioid;
+    int i, n;
+    char *s;
+    BIO_printf(out, "Netscape SPKI:\n");
+    X509_PUBKEY_get0_param(&spkioid, NULL, NULL, NULL, spki->spkac->pubkey);
+    i = OBJ_obj2nid(spkioid);
+    BIO_printf(out, "  Public Key Algorithm: %s\n",
+               (i == NID_undef) ? "UNKNOWN" : OBJ_nid2ln(i));
+    pkey = X509_PUBKEY_get(spki->spkac->pubkey);
+    if (!pkey)
+        BIO_printf(out, "  Unable to load public key\n");
+    else {
+        EVP_PKEY_print_public(out, pkey, 4, NULL);
+        EVP_PKEY_free(pkey);
+    }
+    chal = spki->spkac->challenge;
+    if (chal->length)
+        BIO_printf(out, "  Challenge String: %s\n", chal->data);
+    i = OBJ_obj2nid(spki->sig_algor.algorithm);
+    BIO_printf(out, "  Signature Algorithm: %s",
+               (i == NID_undef) ? "UNKNOWN" : OBJ_nid2ln(i));
+
+    n = spki->signature->length;
+    s = (char *)spki->signature->data;
+    for (i = 0; i < n; i++) {
+        if ((i % 18) == 0)
+            BIO_write(out, "\n      ", 7);
+        BIO_printf(out, "%02x%s", (unsigned char)s[i],
+                   ((i + 1) == n) ? "" : ":");
+    }
+    BIO_write(out, "\n", 1);
+    return 1;
+}
diff --git a/crypto/asn1/tasn_dec.c b/crypto/asn1/tasn_dec.c
new file mode 100644
index 000000000..c9b637516
--- /dev/null
+++ b/crypto/asn1/tasn_dec.c
@@ -0,0 +1,1142 @@
+/*
+ * Copyright 2000-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include "internal/numbers.h"
+#include "asn1_locl.h"
+
+static int asn1_item_embed_d2i(ASN1_VALUE **pval, const unsigned char **in,
+                               long len, const ASN1_ITEM *it,
+                               int tag, int aclass, char opt, ASN1_TLC *ctx);
+
+static int asn1_check_eoc(const unsigned char **in, long len);
+static int asn1_find_end(const unsigned char **in, long len, char inf);
+
+static int asn1_collect(BUF_MEM *buf, const unsigned char **in, long len,
+                        char inf, int tag, int aclass, int depth);
+
+static int collect_data(BUF_MEM *buf, const unsigned char **p, long plen);
+
+static int asn1_check_tlen(long *olen, int *otag, unsigned char *oclass,
+                           char *inf, char *cst,
+                           const unsigned char **in, long len,
+                           int exptag, int expclass, char opt, ASN1_TLC *ctx);
+
+static int asn1_template_ex_d2i(ASN1_VALUE **pval,
+                                const unsigned char **in, long len,
+                                const ASN1_TEMPLATE *tt, char opt,
+                                ASN1_TLC *ctx);
+static int asn1_template_noexp_d2i(ASN1_VALUE **val,
+                                   const unsigned char **in, long len,
+                                   const ASN1_TEMPLATE *tt, char opt,
+                                   ASN1_TLC *ctx);
+static int asn1_d2i_ex_primitive(ASN1_VALUE **pval,
+                                 const unsigned char **in, long len,
+                                 const ASN1_ITEM *it,
+                                 int tag, int aclass, char opt,
+                                 ASN1_TLC *ctx);
+static int asn1_ex_c2i(ASN1_VALUE **pval, const unsigned char *cont, int len,
+                       int utype, char *free_cont, const ASN1_ITEM *it);
+
+/* Table to convert tags to bit values, used for MSTRING type */
+static const unsigned long tag2bit[32] = {
+    /* tags  0 -  3 */
+    0, 0, 0, B_ASN1_BIT_STRING,
+    /* tags  4- 7 */
+    B_ASN1_OCTET_STRING, 0, 0, B_ASN1_UNKNOWN,
+    /* tags  8-11 */
+    B_ASN1_UNKNOWN, B_ASN1_UNKNOWN, B_ASN1_UNKNOWN, B_ASN1_UNKNOWN,
+    /* tags 12-15 */
+    B_ASN1_UTF8STRING, B_ASN1_UNKNOWN, B_ASN1_UNKNOWN, B_ASN1_UNKNOWN,
+    /* tags 16-19 */
+    B_ASN1_SEQUENCE, 0, B_ASN1_NUMERICSTRING, B_ASN1_PRINTABLESTRING,
+    /* tags 20-22 */
+    B_ASN1_T61STRING, B_ASN1_VIDEOTEXSTRING, B_ASN1_IA5STRING,
+    /* tags 23-24 */
+    B_ASN1_UTCTIME, B_ASN1_GENERALIZEDTIME,
+    /* tags 25-27 */
+    B_ASN1_GRAPHICSTRING, B_ASN1_ISO64STRING, B_ASN1_GENERALSTRING,
+    /* tags 28-31 */
+    B_ASN1_UNIVERSALSTRING, B_ASN1_UNKNOWN, B_ASN1_BMPSTRING, B_ASN1_UNKNOWN,
+};
+
+unsigned long ASN1_tag2bit(int tag)
+{
+    if ((tag < 0) || (tag > 30))
+        return 0;
+    return tag2bit[tag];
+}
+
+/* Macro to initialize and invalidate the cache */
+
+#define asn1_tlc_clear(c)       if (c) (c)->valid = 0
+/* Version to avoid compiler warning about 'c' always non-NULL */
+#define asn1_tlc_clear_nc(c)    (c)->valid = 0
+
+/*
+ * Decode an ASN1 item, this currently behaves just like a standard 'd2i'
+ * function. 'in' points to a buffer to read the data from, in future we
+ * will have more advanced versions that can input data a piece at a time and
+ * this will simply be a special case.
+ */
+
+ASN1_VALUE *ASN1_item_d2i(ASN1_VALUE **pval,
+                          const unsigned char **in, long len,
+                          const ASN1_ITEM *it)
+{
+    ASN1_TLC c;
+    ASN1_VALUE *ptmpval = NULL;
+    if (!pval)
+        pval = &ptmpval;
+    asn1_tlc_clear_nc(&c);
+    if (ASN1_item_ex_d2i(pval, in, len, it, -1, 0, 0, &c) > 0)
+        return *pval;
+    return NULL;
+}
+
+int ASN1_item_ex_d2i(ASN1_VALUE **pval, const unsigned char **in, long len,
+                     const ASN1_ITEM *it,
+                     int tag, int aclass, char opt, ASN1_TLC *ctx)
+{
+    int rv;
+    rv = asn1_item_embed_d2i(pval, in, len, it, tag, aclass, opt, ctx);
+    if (rv <= 0)
+        ASN1_item_ex_free(pval, it);
+    return rv;
+}
+
+/*
+ * Decode an item, taking care of IMPLICIT tagging, if any. If 'opt' set and
+ * tag mismatch return -1 to handle OPTIONAL
+ */
+
+static int asn1_item_embed_d2i(ASN1_VALUE **pval, const unsigned char **in,
+                               long len, const ASN1_ITEM *it,
+                               int tag, int aclass, char opt, ASN1_TLC *ctx)
+{
+    const ASN1_TEMPLATE *tt, *errtt = NULL;
+    const ASN1_EXTERN_FUNCS *ef;
+    const ASN1_AUX *aux = it->funcs;
+    ASN1_aux_cb *asn1_cb;
+    const unsigned char *p = NULL, *q;
+    unsigned char oclass;
+    char seq_eoc, seq_nolen, cst, isopt;
+    long tmplen;
+    int i;
+    int otag;
+    int ret = 0;
+    ASN1_VALUE **pchptr;
+    if (!pval)
+        return 0;
+    if (aux && aux->asn1_cb)
+        asn1_cb = aux->asn1_cb;
+    else
+        asn1_cb = 0;
+
+    switch (it->itype) {
+    case ASN1_ITYPE_PRIMITIVE:
+        if (it->templates) {
+            /*
+             * tagging or OPTIONAL is currently illegal on an item template
+             * because the flags can't get passed down. In practice this
+             * isn't a problem: we include the relevant flags from the item
+             * template in the template itself.
+             */
+            if ((tag != -1) || opt) {
+                ASN1err(ASN1_F_ASN1_ITEM_EMBED_D2I,
+                        ASN1_R_ILLEGAL_OPTIONS_ON_ITEM_TEMPLATE);
+                goto err;
+            }
+            return asn1_template_ex_d2i(pval, in, len,
+                                        it->templates, opt, ctx);
+        }
+        return asn1_d2i_ex_primitive(pval, in, len, it,
+                                     tag, aclass, opt, ctx);
+
+    case ASN1_ITYPE_MSTRING:
+        p = *in;
+        /* Just read in tag and class */
+        ret = asn1_check_tlen(NULL, &otag, &oclass, NULL, NULL,
+                              &p, len, -1, 0, 1, ctx);
+        if (!ret) {
+            ASN1err(ASN1_F_ASN1_ITEM_EMBED_D2I, ERR_R_NESTED_ASN1_ERROR);
+            goto err;
+        }
+
+        /* Must be UNIVERSAL class */
+        if (oclass != V_ASN1_UNIVERSAL) {
+            /* If OPTIONAL, assume this is OK */
+            if (opt)
+                return -1;
+            ASN1err(ASN1_F_ASN1_ITEM_EMBED_D2I, ASN1_R_MSTRING_NOT_UNIVERSAL);
+            goto err;
+        }
+        /* Check tag matches bit map */
+        if (!(ASN1_tag2bit(otag) & it->utype)) {
+            /* If OPTIONAL, assume this is OK */
+            if (opt)
+                return -1;
+            ASN1err(ASN1_F_ASN1_ITEM_EMBED_D2I, ASN1_R_MSTRING_WRONG_TAG);
+            goto err;
+        }
+        return asn1_d2i_ex_primitive(pval, in, len, it, otag, 0, 0, ctx);
+
+    case ASN1_ITYPE_EXTERN:
+        /* Use new style d2i */
+        ef = it->funcs;
+        return ef->asn1_ex_d2i(pval, in, len, it, tag, aclass, opt, ctx);
+
+    case ASN1_ITYPE_CHOICE:
+        if (asn1_cb && !asn1_cb(ASN1_OP_D2I_PRE, pval, it, NULL))
+            goto auxerr;
+        if (*pval) {
+            /* Free up and zero CHOICE value if initialised */
+            i = asn1_get_choice_selector(pval, it);
+            if ((i >= 0) && (i < it->tcount)) {
+                tt = it->templates + i;
+                pchptr = asn1_get_field_ptr(pval, tt);
+                asn1_template_free(pchptr, tt);
+                asn1_set_choice_selector(pval, -1, it);
+            }
+        } else if (!ASN1_item_ex_new(pval, it)) {
+            ASN1err(ASN1_F_ASN1_ITEM_EMBED_D2I, ERR_R_NESTED_ASN1_ERROR);
+            goto err;
+        }
+        /* CHOICE type, try each possibility in turn */
+        p = *in;
+        for (i = 0, tt = it->templates; i < it->tcount; i++, tt++) {
+            pchptr = asn1_get_field_ptr(pval, tt);
+            /*
+             * We mark field as OPTIONAL so its absence can be recognised.
+             */
+            ret = asn1_template_ex_d2i(pchptr, &p, len, tt, 1, ctx);
+            /* If field not present, try the next one */
+            if (ret == -1)
+                continue;
+            /* If positive return, read OK, break loop */
+            if (ret > 0)
+                break;
+            /*
+             * Must be an ASN1 parsing error.
+             * Free up any partial choice value
+             */
+            asn1_template_free(pchptr, tt);
+            errtt = tt;
+            ASN1err(ASN1_F_ASN1_ITEM_EMBED_D2I, ERR_R_NESTED_ASN1_ERROR);
+            goto err;
+        }
+
+        /* Did we fall off the end without reading anything? */
+        if (i == it->tcount) {
+            /* If OPTIONAL, this is OK */
+            if (opt) {
+                /* Free and zero it */
+                ASN1_item_ex_free(pval, it);
+                return -1;
+            }
+            ASN1err(ASN1_F_ASN1_ITEM_EMBED_D2I, ASN1_R_NO_MATCHING_CHOICE_TYPE);
+            goto err;
+        }
+
+        asn1_set_choice_selector(pval, i, it);
+
+        if (asn1_cb && !asn1_cb(ASN1_OP_D2I_POST, pval, it, NULL))
+            goto auxerr;
+        *in = p;
+        return 1;
+
+    case ASN1_ITYPE_NDEF_SEQUENCE:
+    case ASN1_ITYPE_SEQUENCE:
+        p = *in;
+        tmplen = len;
+
+        /* If no IMPLICIT tagging set to SEQUENCE, UNIVERSAL */
+        if (tag == -1) {
+            tag = V_ASN1_SEQUENCE;
+            aclass = V_ASN1_UNIVERSAL;
+        }
+        /* Get SEQUENCE length and update len, p */
+        ret = asn1_check_tlen(&len, NULL, NULL, &seq_eoc, &cst,
+                              &p, len, tag, aclass, opt, ctx);
+        if (!ret) {
+            ASN1err(ASN1_F_ASN1_ITEM_EMBED_D2I, ERR_R_NESTED_ASN1_ERROR);
+            goto err;
+        } else if (ret == -1)
+            return -1;
+        if (aux && (aux->flags & ASN1_AFLG_BROKEN)) {
+            len = tmplen - (p - *in);
+            seq_nolen = 1;
+        }
+        /* If indefinite we don't do a length check */
+        else
+            seq_nolen = seq_eoc;
+        if (!cst) {
+            ASN1err(ASN1_F_ASN1_ITEM_EMBED_D2I, ASN1_R_SEQUENCE_NOT_CONSTRUCTED);
+            goto err;
+        }
+
+        if (!*pval && !ASN1_item_ex_new(pval, it)) {
+            ASN1err(ASN1_F_ASN1_ITEM_EMBED_D2I, ERR_R_NESTED_ASN1_ERROR);
+            goto err;
+        }
+
+        if (asn1_cb && !asn1_cb(ASN1_OP_D2I_PRE, pval, it, NULL))
+            goto auxerr;
+
+        /* Free up and zero any ADB found */
+        for (i = 0, tt = it->templates; i < it->tcount; i++, tt++) {
+            if (tt->flags & ASN1_TFLG_ADB_MASK) {
+                const ASN1_TEMPLATE *seqtt;
+                ASN1_VALUE **pseqval;
+                seqtt = asn1_do_adb(pval, tt, 0);
+                if (seqtt == NULL)
+                    continue;
+                pseqval = asn1_get_field_ptr(pval, seqtt);
+                asn1_template_free(pseqval, seqtt);
+            }
+        }
+
+        /* Get each field entry */
+        for (i = 0, tt = it->templates; i < it->tcount; i++, tt++) {
+            const ASN1_TEMPLATE *seqtt;
+            ASN1_VALUE **pseqval;
+            seqtt = asn1_do_adb(pval, tt, 1);
+            if (seqtt == NULL)
+                goto err;
+            pseqval = asn1_get_field_ptr(pval, seqtt);
+            /* Have we ran out of data? */
+            if (!len)
+                break;
+            q = p;
+            if (asn1_check_eoc(&p, len)) {
+                if (!seq_eoc) {
+                    ASN1err(ASN1_F_ASN1_ITEM_EMBED_D2I, ASN1_R_UNEXPECTED_EOC);
+                    goto err;
+                }
+                len -= p - q;
+                seq_eoc = 0;
+                q = p;
+                break;
+            }
+            /*
+             * This determines the OPTIONAL flag value. The field cannot be
+             * omitted if it is the last of a SEQUENCE and there is still
+             * data to be read. This isn't strictly necessary but it
+             * increases efficiency in some cases.
+             */
+            if (i == (it->tcount - 1))
+                isopt = 0;
+            else
+                isopt = (char)(seqtt->flags & ASN1_TFLG_OPTIONAL);
+            /*
+             * attempt to read in field, allowing each to be OPTIONAL
+             */
+
+            ret = asn1_template_ex_d2i(pseqval, &p, len, seqtt, isopt, ctx);
+            if (!ret) {
+                errtt = seqtt;
+                goto err;
+            } else if (ret == -1) {
+                /*
+                 * OPTIONAL component absent. Free and zero the field.
+                 */
+                asn1_template_free(pseqval, seqtt);
+                continue;
+            }
+            /* Update length */
+            len -= p - q;
+        }
+
+        /* Check for EOC if expecting one */
+        if (seq_eoc && !asn1_check_eoc(&p, len)) {
+            ASN1err(ASN1_F_ASN1_ITEM_EMBED_D2I, ASN1_R_MISSING_EOC);
+            goto err;
+        }
+        /* Check all data read */
+        if (!seq_nolen && len) {
+            ASN1err(ASN1_F_ASN1_ITEM_EMBED_D2I, ASN1_R_SEQUENCE_LENGTH_MISMATCH);
+            goto err;
+        }
+
+        /*
+         * If we get here we've got no more data in the SEQUENCE, however we
+         * may not have read all fields so check all remaining are OPTIONAL
+         * and clear any that are.
+         */
+        for (; i < it->tcount; tt++, i++) {
+            const ASN1_TEMPLATE *seqtt;
+            seqtt = asn1_do_adb(pval, tt, 1);
+            if (seqtt == NULL)
+                goto err;
+            if (seqtt->flags & ASN1_TFLG_OPTIONAL) {
+                ASN1_VALUE **pseqval;
+                pseqval = asn1_get_field_ptr(pval, seqtt);
+                asn1_template_free(pseqval, seqtt);
+            } else {
+                errtt = seqtt;
+                ASN1err(ASN1_F_ASN1_ITEM_EMBED_D2I, ASN1_R_FIELD_MISSING);
+                goto err;
+            }
+        }
+        /* Save encoding */
+        if (!asn1_enc_save(pval, *in, p - *in, it))
+            goto auxerr;
+        if (asn1_cb && !asn1_cb(ASN1_OP_D2I_POST, pval, it, NULL))
+            goto auxerr;
+        *in = p;
+        return 1;
+
+    default:
+        return 0;
+    }
+ auxerr:
+    ASN1err(ASN1_F_ASN1_ITEM_EMBED_D2I, ASN1_R_AUX_ERROR);
+ err:
+    if (errtt)
+        ERR_add_error_data(4, "Field=", errtt->field_name,
+                           ", Type=", it->sname);
+    else
+        ERR_add_error_data(2, "Type=", it->sname);
+    return 0;
+}
+
+/*
+ * Templates are handled with two separate functions. One handles any
+ * EXPLICIT tag and the other handles the rest.
+ */
+
+static int asn1_template_ex_d2i(ASN1_VALUE **val,
+                                const unsigned char **in, long inlen,
+                                const ASN1_TEMPLATE *tt, char opt,
+                                ASN1_TLC *ctx)
+{
+    int flags, aclass;
+    int ret;
+    long len;
+    const unsigned char *p, *q;
+    char exp_eoc;
+    if (!val)
+        return 0;
+    flags = tt->flags;
+    aclass = flags & ASN1_TFLG_TAG_CLASS;
+
+    p = *in;
+
+    /* Check if EXPLICIT tag expected */
+    if (flags & ASN1_TFLG_EXPTAG) {
+        char cst;
+        /*
+         * Need to work out amount of data available to the inner content and
+         * where it starts: so read in EXPLICIT header to get the info.
+         */
+        ret = asn1_check_tlen(&len, NULL, NULL, &exp_eoc, &cst,
+                              &p, inlen, tt->tag, aclass, opt, ctx);
+        q = p;
+        if (!ret) {
+            ASN1err(ASN1_F_ASN1_TEMPLATE_EX_D2I, ERR_R_NESTED_ASN1_ERROR);
+            return 0;
+        } else if (ret == -1)
+            return -1;
+        if (!cst) {
+            ASN1err(ASN1_F_ASN1_TEMPLATE_EX_D2I,
+                    ASN1_R_EXPLICIT_TAG_NOT_CONSTRUCTED);
+            return 0;
+        }
+        /* We've found the field so it can't be OPTIONAL now */
+        ret = asn1_template_noexp_d2i(val, &p, len, tt, 0, ctx);
+        if (!ret) {
+            ASN1err(ASN1_F_ASN1_TEMPLATE_EX_D2I, ERR_R_NESTED_ASN1_ERROR);
+            return 0;
+        }
+        /* We read the field in OK so update length */
+        len -= p - q;
+        if (exp_eoc) {
+            /* If NDEF we must have an EOC here */
+            if (!asn1_check_eoc(&p, len)) {
+                ASN1err(ASN1_F_ASN1_TEMPLATE_EX_D2I, ASN1_R_MISSING_EOC);
+                goto err;
+            }
+        } else {
+            /*
+             * Otherwise we must hit the EXPLICIT tag end or its an error
+             */
+            if (len) {
+                ASN1err(ASN1_F_ASN1_TEMPLATE_EX_D2I,
+                        ASN1_R_EXPLICIT_LENGTH_MISMATCH);
+                goto err;
+            }
+        }
+    } else
+        return asn1_template_noexp_d2i(val, in, inlen, tt, opt, ctx);
+
+    *in = p;
+    return 1;
+
+ err:
+    return 0;
+}
+
+static int asn1_template_noexp_d2i(ASN1_VALUE **val,
+                                   const unsigned char **in, long len,
+                                   const ASN1_TEMPLATE *tt, char opt,
+                                   ASN1_TLC *ctx)
+{
+    int flags, aclass;
+    int ret;
+    ASN1_VALUE *tval;
+    const unsigned char *p, *q;
+    if (!val)
+        return 0;
+    flags = tt->flags;
+    aclass = flags & ASN1_TFLG_TAG_CLASS;
+
+    p = *in;
+    q = p;
+
+    /*
+     * If field is embedded then val needs fixing so it is a pointer to
+     * a pointer to a field.
+     */
+    if (tt->flags & ASN1_TFLG_EMBED) {
+        tval = (ASN1_VALUE *)val;
+        val = &tval;
+    }
+
+    if (flags & ASN1_TFLG_SK_MASK) {
+        /* SET OF, SEQUENCE OF */
+        int sktag, skaclass;
+        char sk_eoc;
+        /* First work out expected inner tag value */
+        if (flags & ASN1_TFLG_IMPTAG) {
+            sktag = tt->tag;
+            skaclass = aclass;
+        } else {
+            skaclass = V_ASN1_UNIVERSAL;
+            if (flags & ASN1_TFLG_SET_OF)
+                sktag = V_ASN1_SET;
+            else
+                sktag = V_ASN1_SEQUENCE;
+        }
+        /* Get the tag */
+        ret = asn1_check_tlen(&len, NULL, NULL, &sk_eoc, NULL,
+                              &p, len, sktag, skaclass, opt, ctx);
+        if (!ret) {
+            ASN1err(ASN1_F_ASN1_TEMPLATE_NOEXP_D2I, ERR_R_NESTED_ASN1_ERROR);
+            return 0;
+        } else if (ret == -1)
+            return -1;
+        if (!*val)
+            *val = (ASN1_VALUE *)OPENSSL_sk_new_null();
+        else {
+            /*
+             * We've got a valid STACK: free up any items present
+             */
+            STACK_OF(ASN1_VALUE) *sktmp = (STACK_OF(ASN1_VALUE) *)*val;
+            ASN1_VALUE *vtmp;
+            while (sk_ASN1_VALUE_num(sktmp) > 0) {
+                vtmp = sk_ASN1_VALUE_pop(sktmp);
+                ASN1_item_ex_free(&vtmp, ASN1_ITEM_ptr(tt->item));
+            }
+        }
+
+        if (!*val) {
+            ASN1err(ASN1_F_ASN1_TEMPLATE_NOEXP_D2I, ERR_R_MALLOC_FAILURE);
+            goto err;
+        }
+
+        /* Read as many items as we can */
+        while (len > 0) {
+            ASN1_VALUE *skfield;
+            q = p;
+            /* See if EOC found */
+            if (asn1_check_eoc(&p, len)) {
+                if (!sk_eoc) {
+                    ASN1err(ASN1_F_ASN1_TEMPLATE_NOEXP_D2I,
+                            ASN1_R_UNEXPECTED_EOC);
+                    goto err;
+                }
+                len -= p - q;
+                sk_eoc = 0;
+                break;
+            }
+            skfield = NULL;
+            if (!asn1_item_embed_d2i(&skfield, &p, len,
+                                     ASN1_ITEM_ptr(tt->item), -1, 0, 0, ctx)) {
+                ASN1err(ASN1_F_ASN1_TEMPLATE_NOEXP_D2I,
+                        ERR_R_NESTED_ASN1_ERROR);
+                /* |skfield| may be partially allocated despite failure. */
+                ASN1_item_free(skfield, ASN1_ITEM_ptr(tt->item));
+                goto err;
+            }
+            len -= p - q;
+            if (!sk_ASN1_VALUE_push((STACK_OF(ASN1_VALUE) *)*val, skfield)) {
+                ASN1err(ASN1_F_ASN1_TEMPLATE_NOEXP_D2I, ERR_R_MALLOC_FAILURE);
+                ASN1_item_free(skfield, ASN1_ITEM_ptr(tt->item));
+                goto err;
+            }
+        }
+        if (sk_eoc) {
+            ASN1err(ASN1_F_ASN1_TEMPLATE_NOEXP_D2I, ASN1_R_MISSING_EOC);
+            goto err;
+        }
+    } else if (flags & ASN1_TFLG_IMPTAG) {
+        /* IMPLICIT tagging */
+        ret = asn1_item_embed_d2i(val, &p, len,
+                                  ASN1_ITEM_ptr(tt->item), tt->tag, aclass, opt,
+                                  ctx);
+        if (!ret) {
+            ASN1err(ASN1_F_ASN1_TEMPLATE_NOEXP_D2I, ERR_R_NESTED_ASN1_ERROR);
+            goto err;
+        } else if (ret == -1)
+            return -1;
+    } else {
+        /* Nothing special */
+        ret = asn1_item_embed_d2i(val, &p, len, ASN1_ITEM_ptr(tt->item),
+                                  -1, 0, opt, ctx);
+        if (!ret) {
+            ASN1err(ASN1_F_ASN1_TEMPLATE_NOEXP_D2I, ERR_R_NESTED_ASN1_ERROR);
+            goto err;
+        } else if (ret == -1)
+            return -1;
+    }
+
+    *in = p;
+    return 1;
+
+ err:
+    return 0;
+}
+
+static int asn1_d2i_ex_primitive(ASN1_VALUE **pval,
+                                 const unsigned char **in, long inlen,
+                                 const ASN1_ITEM *it,
+                                 int tag, int aclass, char opt, ASN1_TLC *ctx)
+{
+    int ret = 0, utype;
+    long plen;
+    char cst, inf, free_cont = 0;
+    const unsigned char *p;
+    BUF_MEM buf = { 0, NULL, 0, 0 };
+    const unsigned char *cont = NULL;
+    long len;
+    if (!pval) {
+        ASN1err(ASN1_F_ASN1_D2I_EX_PRIMITIVE, ASN1_R_ILLEGAL_NULL);
+        return 0;               /* Should never happen */
+    }
+
+    if (it->itype == ASN1_ITYPE_MSTRING) {
+        utype = tag;
+        tag = -1;
+    } else
+        utype = it->utype;
+
+    if (utype == V_ASN1_ANY) {
+        /* If type is ANY need to figure out type from tag */
+        unsigned char oclass;
+        if (tag >= 0) {
+            ASN1err(ASN1_F_ASN1_D2I_EX_PRIMITIVE, ASN1_R_ILLEGAL_TAGGED_ANY);
+            return 0;
+        }
+        if (opt) {
+            ASN1err(ASN1_F_ASN1_D2I_EX_PRIMITIVE,
+                    ASN1_R_ILLEGAL_OPTIONAL_ANY);
+            return 0;
+        }
+        p = *in;
+        ret = asn1_check_tlen(NULL, &utype, &oclass, NULL, NULL,
+                              &p, inlen, -1, 0, 0, ctx);
+        if (!ret) {
+            ASN1err(ASN1_F_ASN1_D2I_EX_PRIMITIVE, ERR_R_NESTED_ASN1_ERROR);
+            return 0;
+        }
+        if (oclass != V_ASN1_UNIVERSAL)
+            utype = V_ASN1_OTHER;
+    }
+    if (tag == -1) {
+        tag = utype;
+        aclass = V_ASN1_UNIVERSAL;
+    }
+    p = *in;
+    /* Check header */
+    ret = asn1_check_tlen(&plen, NULL, NULL, &inf, &cst,
+                          &p, inlen, tag, aclass, opt, ctx);
+    if (!ret) {
+        ASN1err(ASN1_F_ASN1_D2I_EX_PRIMITIVE, ERR_R_NESTED_ASN1_ERROR);
+        return 0;
+    } else if (ret == -1)
+        return -1;
+    ret = 0;
+    /* SEQUENCE, SET and "OTHER" are left in encoded form */
+    if ((utype == V_ASN1_SEQUENCE)
+        || (utype == V_ASN1_SET) || (utype == V_ASN1_OTHER)) {
+        /*
+         * Clear context cache for type OTHER because the auto clear when we
+         * have a exact match won't work
+         */
+        if (utype == V_ASN1_OTHER) {
+            asn1_tlc_clear(ctx);
+        }
+        /* SEQUENCE and SET must be constructed */
+        else if (!cst) {
+            ASN1err(ASN1_F_ASN1_D2I_EX_PRIMITIVE,
+                    ASN1_R_TYPE_NOT_CONSTRUCTED);
+            return 0;
+        }
+
+        cont = *in;
+        /* If indefinite length constructed find the real end */
+        if (inf) {
+            if (!asn1_find_end(&p, plen, inf))
+                goto err;
+            len = p - cont;
+        } else {
+            len = p - cont + plen;
+            p += plen;
+        }
+    } else if (cst) {
+        if (utype == V_ASN1_NULL || utype == V_ASN1_BOOLEAN
+            || utype == V_ASN1_OBJECT || utype == V_ASN1_INTEGER
+            || utype == V_ASN1_ENUMERATED) {
+            ASN1err(ASN1_F_ASN1_D2I_EX_PRIMITIVE, ASN1_R_TYPE_NOT_PRIMITIVE);
+            return 0;
+        }
+
+        /* Free any returned 'buf' content */
+        free_cont = 1;
+        /*
+         * Should really check the internal tags are correct but some things
+         * may get this wrong. The relevant specs say that constructed string
+         * types should be OCTET STRINGs internally irrespective of the type.
+         * So instead just check for UNIVERSAL class and ignore the tag.
+         */
+        if (!asn1_collect(&buf, &p, plen, inf, -1, V_ASN1_UNIVERSAL, 0)) {
+            goto err;
+        }
+        len = buf.length;
+        /* Append a final null to string */
+        if (!BUF_MEM_grow_clean(&buf, len + 1)) {
+            ASN1err(ASN1_F_ASN1_D2I_EX_PRIMITIVE, ERR_R_MALLOC_FAILURE);
+            goto err;
+        }
+        buf.data[len] = 0;
+        cont = (const unsigned char *)buf.data;
+    } else {
+        cont = p;
+        len = plen;
+        p += plen;
+    }
+
+    /* We now have content length and type: translate into a structure */
+    /* asn1_ex_c2i may reuse allocated buffer, and so sets free_cont to 0 */
+    if (!asn1_ex_c2i(pval, cont, len, utype, &free_cont, it))
+        goto err;
+
+    *in = p;
+    ret = 1;
+ err:
+    if (free_cont)
+        OPENSSL_free(buf.data);
+    return ret;
+}
+
+/* Translate ASN1 content octets into a structure */
+
+static int asn1_ex_c2i(ASN1_VALUE **pval, const unsigned char *cont, int len,
+                       int utype, char *free_cont, const ASN1_ITEM *it)
+{
+    ASN1_VALUE **opval = NULL;
+    ASN1_STRING *stmp;
+    ASN1_TYPE *typ = NULL;
+    int ret = 0;
+    const ASN1_PRIMITIVE_FUNCS *pf;
+    ASN1_INTEGER **tint;
+    pf = it->funcs;
+
+    if (pf && pf->prim_c2i)
+        return pf->prim_c2i(pval, cont, len, utype, free_cont, it);
+    /* If ANY type clear type and set pointer to internal value */
+    if (it->utype == V_ASN1_ANY) {
+        if (!*pval) {
+            typ = ASN1_TYPE_new();
+            if (typ == NULL)
+                goto err;
+            *pval = (ASN1_VALUE *)typ;
+        } else
+            typ = (ASN1_TYPE *)*pval;
+
+        if (utype != typ->type)
+            ASN1_TYPE_set(typ, utype, NULL);
+        opval = pval;
+        pval = &typ->value.asn1_value;
+    }
+    switch (utype) {
+    case V_ASN1_OBJECT:
+        if (!c2i_ASN1_OBJECT((ASN1_OBJECT **)pval, &cont, len))
+            goto err;
+        break;
+
+    case V_ASN1_NULL:
+        if (len) {
+            ASN1err(ASN1_F_ASN1_EX_C2I, ASN1_R_NULL_IS_WRONG_LENGTH);
+            goto err;
+        }
+        *pval = (ASN1_VALUE *)1;
+        break;
+
+    case V_ASN1_BOOLEAN:
+        if (len != 1) {
+            ASN1err(ASN1_F_ASN1_EX_C2I, ASN1_R_BOOLEAN_IS_WRONG_LENGTH);
+            goto err;
+        } else {
+            ASN1_BOOLEAN *tbool;
+            tbool = (ASN1_BOOLEAN *)pval;
+            *tbool = *cont;
+        }
+        break;
+
+    case V_ASN1_BIT_STRING:
+        if (!c2i_ASN1_BIT_STRING((ASN1_BIT_STRING **)pval, &cont, len))
+            goto err;
+        break;
+
+    case V_ASN1_INTEGER:
+    case V_ASN1_ENUMERATED:
+        tint = (ASN1_INTEGER **)pval;
+        if (!c2i_ASN1_INTEGER(tint, &cont, len))
+            goto err;
+        /* Fixup type to match the expected form */
+        (*tint)->type = utype | ((*tint)->type & V_ASN1_NEG);
+        break;
+
+    case V_ASN1_OCTET_STRING:
+    case V_ASN1_NUMERICSTRING:
+    case V_ASN1_PRINTABLESTRING:
+    case V_ASN1_T61STRING:
+    case V_ASN1_VIDEOTEXSTRING:
+    case V_ASN1_IA5STRING:
+    case V_ASN1_UTCTIME:
+    case V_ASN1_GENERALIZEDTIME:
+    case V_ASN1_GRAPHICSTRING:
+    case V_ASN1_VISIBLESTRING:
+    case V_ASN1_GENERALSTRING:
+    case V_ASN1_UNIVERSALSTRING:
+    case V_ASN1_BMPSTRING:
+    case V_ASN1_UTF8STRING:
+    case V_ASN1_OTHER:
+    case V_ASN1_SET:
+    case V_ASN1_SEQUENCE:
+    default:
+        if (utype == V_ASN1_BMPSTRING && (len & 1)) {
+            ASN1err(ASN1_F_ASN1_EX_C2I, ASN1_R_BMPSTRING_IS_WRONG_LENGTH);
+            goto err;
+        }
+        if (utype == V_ASN1_UNIVERSALSTRING && (len & 3)) {
+            ASN1err(ASN1_F_ASN1_EX_C2I,
+                    ASN1_R_UNIVERSALSTRING_IS_WRONG_LENGTH);
+            goto err;
+        }
+        /* All based on ASN1_STRING and handled the same */
+        if (!*pval) {
+            stmp = ASN1_STRING_type_new(utype);
+            if (stmp == NULL) {
+                ASN1err(ASN1_F_ASN1_EX_C2I, ERR_R_MALLOC_FAILURE);
+                goto err;
+            }
+            *pval = (ASN1_VALUE *)stmp;
+        } else {
+            stmp = (ASN1_STRING *)*pval;
+            stmp->type = utype;
+        }
+        /* If we've already allocated a buffer use it */
+        if (*free_cont) {
+            OPENSSL_free(stmp->data);
+            stmp->data = (unsigned char *)cont; /* UGLY CAST! RL */
+            stmp->length = len;
+            *free_cont = 0;
+        } else {
+            if (!ASN1_STRING_set(stmp, cont, len)) {
+                ASN1err(ASN1_F_ASN1_EX_C2I, ERR_R_MALLOC_FAILURE);
+                ASN1_STRING_free(stmp);
+                *pval = NULL;
+                goto err;
+            }
+        }
+        break;
+    }
+    /* If ASN1_ANY and NULL type fix up value */
+    if (typ && (utype == V_ASN1_NULL))
+        typ->value.ptr = NULL;
+
+    ret = 1;
+ err:
+    if (!ret) {
+        ASN1_TYPE_free(typ);
+        if (opval)
+            *opval = NULL;
+    }
+    return ret;
+}
+
+/*
+ * This function finds the end of an ASN1 structure when passed its maximum
+ * length, whether it is indefinite length and a pointer to the content. This
+ * is more efficient than calling asn1_collect because it does not recurse on
+ * each indefinite length header.
+ */
+
+static int asn1_find_end(const unsigned char **in, long len, char inf)
+{
+    uint32_t expected_eoc;
+    long plen;
+    const unsigned char *p = *in, *q;
+    /* If not indefinite length constructed just add length */
+    if (inf == 0) {
+        *in += len;
+        return 1;
+    }
+    expected_eoc = 1;
+    /*
+     * Indefinite length constructed form. Find the end when enough EOCs are
+     * found. If more indefinite length constructed headers are encountered
+     * increment the expected eoc count otherwise just skip to the end of the
+     * data.
+     */
+    while (len > 0) {
+        if (asn1_check_eoc(&p, len)) {
+            expected_eoc--;
+            if (expected_eoc == 0)
+                break;
+            len -= 2;
+            continue;
+        }
+        q = p;
+        /* Just read in a header: only care about the length */
+        if (!asn1_check_tlen(&plen, NULL, NULL, &inf, NULL, &p, len,
+                             -1, 0, 0, NULL)) {
+            ASN1err(ASN1_F_ASN1_FIND_END, ERR_R_NESTED_ASN1_ERROR);
+            return 0;
+        }
+        if (inf) {
+            if (expected_eoc == UINT32_MAX) {
+                ASN1err(ASN1_F_ASN1_FIND_END, ERR_R_NESTED_ASN1_ERROR);
+                return 0;
+            }
+            expected_eoc++;
+        } else {
+            p += plen;
+        }
+        len -= p - q;
+    }
+    if (expected_eoc) {
+        ASN1err(ASN1_F_ASN1_FIND_END, ASN1_R_MISSING_EOC);
+        return 0;
+    }
+    *in = p;
+    return 1;
+}
+
+/*
+ * This function collects the asn1 data from a constructed string type into
+ * a buffer. The values of 'in' and 'len' should refer to the contents of the
+ * constructed type and 'inf' should be set if it is indefinite length.
+ */
+
+#ifndef ASN1_MAX_STRING_NEST
+/*
+ * This determines how many levels of recursion are permitted in ASN1 string
+ * types. If it is not limited stack overflows can occur. If set to zero no
+ * recursion is allowed at all. Although zero should be adequate examples
+ * exist that require a value of 1. So 5 should be more than enough.
+ */
+# define ASN1_MAX_STRING_NEST 5
+#endif
+
+static int asn1_collect(BUF_MEM *buf, const unsigned char **in, long len,
+                        char inf, int tag, int aclass, int depth)
+{
+    const unsigned char *p, *q;
+    long plen;
+    char cst, ininf;
+    p = *in;
+    inf &= 1;
+    /*
+     * If no buffer and not indefinite length constructed just pass over the
+     * encoded data
+     */
+    if (!buf && !inf) {
+        *in += len;
+        return 1;
+    }
+    while (len > 0) {
+        q = p;
+        /* Check for EOC */
+        if (asn1_check_eoc(&p, len)) {
+            /*
+             * EOC is illegal outside indefinite length constructed form
+             */
+            if (!inf) {
+                ASN1err(ASN1_F_ASN1_COLLECT, ASN1_R_UNEXPECTED_EOC);
+                return 0;
+            }
+            inf = 0;
+            break;
+        }
+
+        if (!asn1_check_tlen(&plen, NULL, NULL, &ininf, &cst, &p,
+                             len, tag, aclass, 0, NULL)) {
+            ASN1err(ASN1_F_ASN1_COLLECT, ERR_R_NESTED_ASN1_ERROR);
+            return 0;
+        }
+
+        /* If indefinite length constructed update max length */
+        if (cst) {
+            if (depth >= ASN1_MAX_STRING_NEST) {
+                ASN1err(ASN1_F_ASN1_COLLECT, ASN1_R_NESTED_ASN1_STRING);
+                return 0;
+            }
+            if (!asn1_collect(buf, &p, plen, ininf, tag, aclass, depth + 1))
+                return 0;
+        } else if (plen && !collect_data(buf, &p, plen))
+            return 0;
+        len -= p - q;
+    }
+    if (inf) {
+        ASN1err(ASN1_F_ASN1_COLLECT, ASN1_R_MISSING_EOC);
+        return 0;
+    }
+    *in = p;
+    return 1;
+}
+
+static int collect_data(BUF_MEM *buf, const unsigned char **p, long plen)
+{
+    int len;
+    if (buf) {
+        len = buf->length;
+        if (!BUF_MEM_grow_clean(buf, len + plen)) {
+            ASN1err(ASN1_F_COLLECT_DATA, ERR_R_MALLOC_FAILURE);
+            return 0;
+        }
+        memcpy(buf->data + len, *p, plen);
+    }
+    *p += plen;
+    return 1;
+}
+
+/* Check for ASN1 EOC and swallow it if found */
+
+static int asn1_check_eoc(const unsigned char **in, long len)
+{
+    const unsigned char *p;
+    if (len < 2)
+        return 0;
+    p = *in;
+    if (!p[0] && !p[1]) {
+        *in += 2;
+        return 1;
+    }
+    return 0;
+}
+
+/*
+ * Check an ASN1 tag and length: a bit like ASN1_get_object but it sets the
+ * length for indefinite length constructed form, we don't know the exact
+ * length but we can set an upper bound to the amount of data available minus
+ * the header length just read.
+ */
+
+static int asn1_check_tlen(long *olen, int *otag, unsigned char *oclass,
+                           char *inf, char *cst,
+                           const unsigned char **in, long len,
+                           int exptag, int expclass, char opt, ASN1_TLC *ctx)
+{
+    int i;
+    int ptag, pclass;
+    long plen;
+    const unsigned char *p, *q;
+    p = *in;
+    q = p;
+
+    if (ctx && ctx->valid) {
+        i = ctx->ret;
+        plen = ctx->plen;
+        pclass = ctx->pclass;
+        ptag = ctx->ptag;
+        p += ctx->hdrlen;
+    } else {
+        i = ASN1_get_object(&p, &plen, &ptag, &pclass, len);
+        if (ctx) {
+            ctx->ret = i;
+            ctx->plen = plen;
+            ctx->pclass = pclass;
+            ctx->ptag = ptag;
+            ctx->hdrlen = p - q;
+            ctx->valid = 1;
+            /*
+             * If definite length, and no error, length + header can't exceed
+             * total amount of data available.
+             */
+            if (!(i & 0x81) && ((plen + ctx->hdrlen) > len)) {
+                ASN1err(ASN1_F_ASN1_CHECK_TLEN, ASN1_R_TOO_LONG);
+                asn1_tlc_clear(ctx);
+                return 0;
+            }
+        }
+    }
+
+    if (i & 0x80) {
+        ASN1err(ASN1_F_ASN1_CHECK_TLEN, ASN1_R_BAD_OBJECT_HEADER);
+        asn1_tlc_clear(ctx);
+        return 0;
+    }
+    if (exptag >= 0) {
+        if ((exptag != ptag) || (expclass != pclass)) {
+            /*
+             * If type is OPTIONAL, not an error: indicate missing type.
+             */
+            if (opt)
+                return -1;
+            asn1_tlc_clear(ctx);
+            ASN1err(ASN1_F_ASN1_CHECK_TLEN, ASN1_R_WRONG_TAG);
+            return 0;
+        }
+        /*
+         * We have a tag and class match: assume we are going to do something
+         * with it
+         */
+        asn1_tlc_clear(ctx);
+    }
+
+    if (i & 1)
+        plen = len - (p - q);
+
+    if (inf)
+        *inf = i & 1;
+
+    if (cst)
+        *cst = i & V_ASN1_CONSTRUCTED;
+
+    if (olen)
+        *olen = plen;
+
+    if (oclass)
+        *oclass = pclass;
+
+    if (otag)
+        *otag = ptag;
+
+    *in = p;
+    return 1;
+}
diff --git a/crypto/asn1/tasn_enc.c b/crypto/asn1/tasn_enc.c
new file mode 100644
index 000000000..caa48696d
--- /dev/null
+++ b/crypto/asn1/tasn_enc.c
@@ -0,0 +1,605 @@
+/*
+ * Copyright 2000-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+#include 
+#include "internal/asn1_int.h"
+#include "asn1_locl.h"
+
+static int asn1_i2d_ex_primitive(ASN1_VALUE **pval, unsigned char **out,
+                                 const ASN1_ITEM *it, int tag, int aclass);
+static int asn1_set_seq_out(STACK_OF(ASN1_VALUE) *sk, unsigned char **out,
+                            int skcontlen, const ASN1_ITEM *item,
+                            int do_sort, int iclass);
+static int asn1_template_ex_i2d(ASN1_VALUE **pval, unsigned char **out,
+                                const ASN1_TEMPLATE *tt, int tag, int aclass);
+static int asn1_item_flags_i2d(ASN1_VALUE *val, unsigned char **out,
+                               const ASN1_ITEM *it, int flags);
+static int asn1_ex_i2c(ASN1_VALUE **pval, unsigned char *cout, int *putype,
+                       const ASN1_ITEM *it);
+
+/*
+ * Top level i2d equivalents: the 'ndef' variant instructs the encoder to use
+ * indefinite length constructed encoding, where appropriate
+ */
+
+int ASN1_item_ndef_i2d(ASN1_VALUE *val, unsigned char **out,
+                       const ASN1_ITEM *it)
+{
+    return asn1_item_flags_i2d(val, out, it, ASN1_TFLG_NDEF);
+}
+
+int ASN1_item_i2d(ASN1_VALUE *val, unsigned char **out, const ASN1_ITEM *it)
+{
+    return asn1_item_flags_i2d(val, out, it, 0);
+}
+
+/*
+ * Encode an ASN1 item, this is use by the standard 'i2d' function. 'out'
+ * points to a buffer to output the data to. The new i2d has one additional
+ * feature. If the output buffer is NULL (i.e. *out == NULL) then a buffer is
+ * allocated and populated with the encoding.
+ */
+
+static int asn1_item_flags_i2d(ASN1_VALUE *val, unsigned char **out,
+                               const ASN1_ITEM *it, int flags)
+{
+    if (out && !*out) {
+        unsigned char *p, *buf;
+        int len;
+        len = ASN1_item_ex_i2d(&val, NULL, it, -1, flags);
+        if (len <= 0)
+            return len;
+        buf = OPENSSL_malloc(len);
+        if (buf == NULL)
+            return -1;
+        p = buf;
+        ASN1_item_ex_i2d(&val, &p, it, -1, flags);
+        *out = buf;
+        return len;
+    }
+
+    return ASN1_item_ex_i2d(&val, out, it, -1, flags);
+}
+
+/*
+ * Encode an item, taking care of IMPLICIT tagging (if any). This function
+ * performs the normal item handling: it can be used in external types.
+ */
+
+int ASN1_item_ex_i2d(ASN1_VALUE **pval, unsigned char **out,
+                     const ASN1_ITEM *it, int tag, int aclass)
+{
+    const ASN1_TEMPLATE *tt = NULL;
+    int i, seqcontlen, seqlen, ndef = 1;
+    const ASN1_EXTERN_FUNCS *ef;
+    const ASN1_AUX *aux = it->funcs;
+    ASN1_aux_cb *asn1_cb = 0;
+
+    if ((it->itype != ASN1_ITYPE_PRIMITIVE) && !*pval)
+        return 0;
+
+    if (aux && aux->asn1_cb)
+        asn1_cb = aux->asn1_cb;
+
+    switch (it->itype) {
+
+    case ASN1_ITYPE_PRIMITIVE:
+        if (it->templates)
+            return asn1_template_ex_i2d(pval, out, it->templates,
+                                        tag, aclass);
+        return asn1_i2d_ex_primitive(pval, out, it, tag, aclass);
+
+    case ASN1_ITYPE_MSTRING:
+        return asn1_i2d_ex_primitive(pval, out, it, -1, aclass);
+
+    case ASN1_ITYPE_CHOICE:
+        if (asn1_cb && !asn1_cb(ASN1_OP_I2D_PRE, pval, it, NULL))
+            return 0;
+        i = asn1_get_choice_selector(pval, it);
+        if ((i >= 0) && (i < it->tcount)) {
+            ASN1_VALUE **pchval;
+            const ASN1_TEMPLATE *chtt;
+            chtt = it->templates + i;
+            pchval = asn1_get_field_ptr(pval, chtt);
+            return asn1_template_ex_i2d(pchval, out, chtt, -1, aclass);
+        }
+        /* Fixme: error condition if selector out of range */
+        if (asn1_cb && !asn1_cb(ASN1_OP_I2D_POST, pval, it, NULL))
+            return 0;
+        break;
+
+    case ASN1_ITYPE_EXTERN:
+        /* If new style i2d it does all the work */
+        ef = it->funcs;
+        return ef->asn1_ex_i2d(pval, out, it, tag, aclass);
+
+    case ASN1_ITYPE_NDEF_SEQUENCE:
+        /* Use indefinite length constructed if requested */
+        if (aclass & ASN1_TFLG_NDEF)
+            ndef = 2;
+        /* fall through */
+
+    case ASN1_ITYPE_SEQUENCE:
+        i = asn1_enc_restore(&seqcontlen, out, pval, it);
+        /* An error occurred */
+        if (i < 0)
+            return 0;
+        /* We have a valid cached encoding... */
+        if (i > 0)
+            return seqcontlen;
+        /* Otherwise carry on */
+        seqcontlen = 0;
+        /* If no IMPLICIT tagging set to SEQUENCE, UNIVERSAL */
+        if (tag == -1) {
+            tag = V_ASN1_SEQUENCE;
+            /* Retain any other flags in aclass */
+            aclass = (aclass & ~ASN1_TFLG_TAG_CLASS)
+                | V_ASN1_UNIVERSAL;
+        }
+        if (asn1_cb && !asn1_cb(ASN1_OP_I2D_PRE, pval, it, NULL))
+            return 0;
+        /* First work out sequence content length */
+        for (i = 0, tt = it->templates; i < it->tcount; tt++, i++) {
+            const ASN1_TEMPLATE *seqtt;
+            ASN1_VALUE **pseqval;
+            int tmplen;
+            seqtt = asn1_do_adb(pval, tt, 1);
+            if (!seqtt)
+                return 0;
+            pseqval = asn1_get_field_ptr(pval, seqtt);
+            tmplen = asn1_template_ex_i2d(pseqval, NULL, seqtt, -1, aclass);
+            if (tmplen == -1 || (tmplen > INT_MAX - seqcontlen))
+                return -1;
+            seqcontlen += tmplen;
+        }
+
+        seqlen = ASN1_object_size(ndef, seqcontlen, tag);
+        if (!out || seqlen == -1)
+            return seqlen;
+        /* Output SEQUENCE header */
+        ASN1_put_object(out, ndef, seqcontlen, tag, aclass);
+        for (i = 0, tt = it->templates; i < it->tcount; tt++, i++) {
+            const ASN1_TEMPLATE *seqtt;
+            ASN1_VALUE **pseqval;
+            seqtt = asn1_do_adb(pval, tt, 1);
+            if (!seqtt)
+                return 0;
+            pseqval = asn1_get_field_ptr(pval, seqtt);
+            /* FIXME: check for errors in enhanced version */
+            asn1_template_ex_i2d(pseqval, out, seqtt, -1, aclass);
+        }
+        if (ndef == 2)
+            ASN1_put_eoc(out);
+        if (asn1_cb && !asn1_cb(ASN1_OP_I2D_POST, pval, it, NULL))
+            return 0;
+        return seqlen;
+
+    default:
+        return 0;
+
+    }
+    return 0;
+}
+
+static int asn1_template_ex_i2d(ASN1_VALUE **pval, unsigned char **out,
+                                const ASN1_TEMPLATE *tt, int tag, int iclass)
+{
+    int i, ret, flags, ttag, tclass, ndef;
+    ASN1_VALUE *tval;
+    flags = tt->flags;
+
+    /*
+     * If field is embedded then val needs fixing so it is a pointer to
+     * a pointer to a field.
+     */
+    if (flags & ASN1_TFLG_EMBED) {
+        tval = (ASN1_VALUE *)pval;
+        pval = &tval;
+    }
+    /*
+     * Work out tag and class to use: tagging may come either from the
+     * template or the arguments, not both because this would create
+     * ambiguity. Additionally the iclass argument may contain some
+     * additional flags which should be noted and passed down to other
+     * levels.
+     */
+    if (flags & ASN1_TFLG_TAG_MASK) {
+        /* Error if argument and template tagging */
+        if (tag != -1)
+            /* FIXME: error code here */
+            return -1;
+        /* Get tagging from template */
+        ttag = tt->tag;
+        tclass = flags & ASN1_TFLG_TAG_CLASS;
+    } else if (tag != -1) {
+        /* No template tagging, get from arguments */
+        ttag = tag;
+        tclass = iclass & ASN1_TFLG_TAG_CLASS;
+    } else {
+        ttag = -1;
+        tclass = 0;
+    }
+    /*
+     * Remove any class mask from iflag.
+     */
+    iclass &= ~ASN1_TFLG_TAG_CLASS;
+
+    /*
+     * At this point 'ttag' contains the outer tag to use, 'tclass' is the
+     * class and iclass is any flags passed to this function.
+     */
+
+    /* if template and arguments require ndef, use it */
+    if ((flags & ASN1_TFLG_NDEF) && (iclass & ASN1_TFLG_NDEF))
+        ndef = 2;
+    else
+        ndef = 1;
+
+    if (flags & ASN1_TFLG_SK_MASK) {
+        /* SET OF, SEQUENCE OF */
+        STACK_OF(ASN1_VALUE) *sk = (STACK_OF(ASN1_VALUE) *)*pval;
+        int isset, sktag, skaclass;
+        int skcontlen, sklen;
+        ASN1_VALUE *skitem;
+
+        if (!*pval)
+            return 0;
+
+        if (flags & ASN1_TFLG_SET_OF) {
+            isset = 1;
+            /* 2 means we reorder */
+            if (flags & ASN1_TFLG_SEQUENCE_OF)
+                isset = 2;
+        } else
+            isset = 0;
+
+        /*
+         * Work out inner tag value: if EXPLICIT or no tagging use underlying
+         * type.
+         */
+        if ((ttag != -1) && !(flags & ASN1_TFLG_EXPTAG)) {
+            sktag = ttag;
+            skaclass = tclass;
+        } else {
+            skaclass = V_ASN1_UNIVERSAL;
+            if (isset)
+                sktag = V_ASN1_SET;
+            else
+                sktag = V_ASN1_SEQUENCE;
+        }
+
+        /* Determine total length of items */
+        skcontlen = 0;
+        for (i = 0; i < sk_ASN1_VALUE_num(sk); i++) {
+            int tmplen;
+            skitem = sk_ASN1_VALUE_value(sk, i);
+            tmplen = ASN1_item_ex_i2d(&skitem, NULL, ASN1_ITEM_ptr(tt->item),
+                                      -1, iclass);
+            if (tmplen == -1 || (skcontlen > INT_MAX - tmplen))
+                return -1;
+            skcontlen += tmplen;
+        }
+        sklen = ASN1_object_size(ndef, skcontlen, sktag);
+        if (sklen == -1)
+            return -1;
+        /* If EXPLICIT need length of surrounding tag */
+        if (flags & ASN1_TFLG_EXPTAG)
+            ret = ASN1_object_size(ndef, sklen, ttag);
+        else
+            ret = sklen;
+
+        if (!out || ret == -1)
+            return ret;
+
+        /* Now encode this lot... */
+        /* EXPLICIT tag */
+        if (flags & ASN1_TFLG_EXPTAG)
+            ASN1_put_object(out, ndef, sklen, ttag, tclass);
+        /* SET or SEQUENCE and IMPLICIT tag */
+        ASN1_put_object(out, ndef, skcontlen, sktag, skaclass);
+        /* And the stuff itself */
+        asn1_set_seq_out(sk, out, skcontlen, ASN1_ITEM_ptr(tt->item),
+                         isset, iclass);
+        if (ndef == 2) {
+            ASN1_put_eoc(out);
+            if (flags & ASN1_TFLG_EXPTAG)
+                ASN1_put_eoc(out);
+        }
+
+        return ret;
+    }
+
+    if (flags & ASN1_TFLG_EXPTAG) {
+        /* EXPLICIT tagging */
+        /* Find length of tagged item */
+        i = ASN1_item_ex_i2d(pval, NULL, ASN1_ITEM_ptr(tt->item), -1, iclass);
+        if (!i)
+            return 0;
+        /* Find length of EXPLICIT tag */
+        ret = ASN1_object_size(ndef, i, ttag);
+        if (out && ret != -1) {
+            /* Output tag and item */
+            ASN1_put_object(out, ndef, i, ttag, tclass);
+            ASN1_item_ex_i2d(pval, out, ASN1_ITEM_ptr(tt->item), -1, iclass);
+            if (ndef == 2)
+                ASN1_put_eoc(out);
+        }
+        return ret;
+    }
+
+    /* Either normal or IMPLICIT tagging: combine class and flags */
+    return ASN1_item_ex_i2d(pval, out, ASN1_ITEM_ptr(tt->item),
+                            ttag, tclass | iclass);
+
+}
+
+/* Temporary structure used to hold DER encoding of items for SET OF */
+
+typedef struct {
+    unsigned char *data;
+    int length;
+    ASN1_VALUE *field;
+} DER_ENC;
+
+static int der_cmp(const void *a, const void *b)
+{
+    const DER_ENC *d1 = a, *d2 = b;
+    int cmplen, i;
+    cmplen = (d1->length < d2->length) ? d1->length : d2->length;
+    i = memcmp(d1->data, d2->data, cmplen);
+    if (i)
+        return i;
+    return d1->length - d2->length;
+}
+
+/* Output the content octets of SET OF or SEQUENCE OF */
+
+static int asn1_set_seq_out(STACK_OF(ASN1_VALUE) *sk, unsigned char **out,
+                            int skcontlen, const ASN1_ITEM *item,
+                            int do_sort, int iclass)
+{
+    int i;
+    ASN1_VALUE *skitem;
+    unsigned char *tmpdat = NULL, *p = NULL;
+    DER_ENC *derlst = NULL, *tder;
+    if (do_sort) {
+        /* Don't need to sort less than 2 items */
+        if (sk_ASN1_VALUE_num(sk) < 2)
+            do_sort = 0;
+        else {
+            derlst = OPENSSL_malloc(sk_ASN1_VALUE_num(sk)
+                                    * sizeof(*derlst));
+            if (derlst == NULL)
+                return 0;
+            tmpdat = OPENSSL_malloc(skcontlen);
+            if (tmpdat == NULL) {
+                OPENSSL_free(derlst);
+                return 0;
+            }
+        }
+    }
+    /* If not sorting just output each item */
+    if (!do_sort) {
+        for (i = 0; i < sk_ASN1_VALUE_num(sk); i++) {
+            skitem = sk_ASN1_VALUE_value(sk, i);
+            ASN1_item_ex_i2d(&skitem, out, item, -1, iclass);
+        }
+        return 1;
+    }
+    p = tmpdat;
+
+    /* Doing sort: build up a list of each member's DER encoding */
+    for (i = 0, tder = derlst; i < sk_ASN1_VALUE_num(sk); i++, tder++) {
+        skitem = sk_ASN1_VALUE_value(sk, i);
+        tder->data = p;
+        tder->length = ASN1_item_ex_i2d(&skitem, &p, item, -1, iclass);
+        tder->field = skitem;
+    }
+
+    /* Now sort them */
+    qsort(derlst, sk_ASN1_VALUE_num(sk), sizeof(*derlst), der_cmp);
+    /* Output sorted DER encoding */
+    p = *out;
+    for (i = 0, tder = derlst; i < sk_ASN1_VALUE_num(sk); i++, tder++) {
+        memcpy(p, tder->data, tder->length);
+        p += tder->length;
+    }
+    *out = p;
+    /* If do_sort is 2 then reorder the STACK */
+    if (do_sort == 2) {
+        for (i = 0, tder = derlst; i < sk_ASN1_VALUE_num(sk); i++, tder++)
+            (void)sk_ASN1_VALUE_set(sk, i, tder->field);
+    }
+    OPENSSL_free(derlst);
+    OPENSSL_free(tmpdat);
+    return 1;
+}
+
+static int asn1_i2d_ex_primitive(ASN1_VALUE **pval, unsigned char **out,
+                                 const ASN1_ITEM *it, int tag, int aclass)
+{
+    int len;
+    int utype;
+    int usetag;
+    int ndef = 0;
+
+    utype = it->utype;
+
+    /*
+     * Get length of content octets and maybe find out the underlying type.
+     */
+
+    len = asn1_ex_i2c(pval, NULL, &utype, it);
+
+    /*
+     * If SEQUENCE, SET or OTHER then header is included in pseudo content
+     * octets so don't include tag+length. We need to check here because the
+     * call to asn1_ex_i2c() could change utype.
+     */
+    if ((utype == V_ASN1_SEQUENCE) || (utype == V_ASN1_SET) ||
+        (utype == V_ASN1_OTHER))
+        usetag = 0;
+    else
+        usetag = 1;
+
+    /* -1 means omit type */
+
+    if (len == -1)
+        return 0;
+
+    /* -2 return is special meaning use ndef */
+    if (len == -2) {
+        ndef = 2;
+        len = 0;
+    }
+
+    /* If not implicitly tagged get tag from underlying type */
+    if (tag == -1)
+        tag = utype;
+
+    /* Output tag+length followed by content octets */
+    if (out) {
+        if (usetag)
+            ASN1_put_object(out, ndef, len, tag, aclass);
+        asn1_ex_i2c(pval, *out, &utype, it);
+        if (ndef)
+            ASN1_put_eoc(out);
+        else
+            *out += len;
+    }
+
+    if (usetag)
+        return ASN1_object_size(ndef, len, tag);
+    return len;
+}
+
+/* Produce content octets from a structure */
+
+static int asn1_ex_i2c(ASN1_VALUE **pval, unsigned char *cout, int *putype,
+                       const ASN1_ITEM *it)
+{
+    ASN1_BOOLEAN *tbool = NULL;
+    ASN1_STRING *strtmp;
+    ASN1_OBJECT *otmp;
+    int utype;
+    const unsigned char *cont;
+    unsigned char c;
+    int len;
+    const ASN1_PRIMITIVE_FUNCS *pf;
+    pf = it->funcs;
+    if (pf && pf->prim_i2c)
+        return pf->prim_i2c(pval, cout, putype, it);
+
+    /* Should type be omitted? */
+    if ((it->itype != ASN1_ITYPE_PRIMITIVE)
+        || (it->utype != V_ASN1_BOOLEAN)) {
+        if (!*pval)
+            return -1;
+    }
+
+    if (it->itype == ASN1_ITYPE_MSTRING) {
+        /* If MSTRING type set the underlying type */
+        strtmp = (ASN1_STRING *)*pval;
+        utype = strtmp->type;
+        *putype = utype;
+    } else if (it->utype == V_ASN1_ANY) {
+        /* If ANY set type and pointer to value */
+        ASN1_TYPE *typ;
+        typ = (ASN1_TYPE *)*pval;
+        utype = typ->type;
+        *putype = utype;
+        pval = &typ->value.asn1_value;
+    } else
+        utype = *putype;
+
+    switch (utype) {
+    case V_ASN1_OBJECT:
+        otmp = (ASN1_OBJECT *)*pval;
+        cont = otmp->data;
+        len = otmp->length;
+        break;
+
+    case V_ASN1_NULL:
+        cont = NULL;
+        len = 0;
+        break;
+
+    case V_ASN1_BOOLEAN:
+        tbool = (ASN1_BOOLEAN *)pval;
+        if (*tbool == -1)
+            return -1;
+        if (it->utype != V_ASN1_ANY) {
+            /*
+             * Default handling if value == size field then omit
+             */
+            if (*tbool && (it->size > 0))
+                return -1;
+            if (!*tbool && !it->size)
+                return -1;
+        }
+        c = (unsigned char)*tbool;
+        cont = &c;
+        len = 1;
+        break;
+
+    case V_ASN1_BIT_STRING:
+        return i2c_ASN1_BIT_STRING((ASN1_BIT_STRING *)*pval,
+                                   cout ? &cout : NULL);
+
+    case V_ASN1_INTEGER:
+    case V_ASN1_ENUMERATED:
+        /*
+         * These are all have the same content format as ASN1_INTEGER
+         */
+        return i2c_ASN1_INTEGER((ASN1_INTEGER *)*pval, cout ? &cout : NULL);
+
+    case V_ASN1_OCTET_STRING:
+    case V_ASN1_NUMERICSTRING:
+    case V_ASN1_PRINTABLESTRING:
+    case V_ASN1_T61STRING:
+    case V_ASN1_VIDEOTEXSTRING:
+    case V_ASN1_IA5STRING:
+    case V_ASN1_UTCTIME:
+    case V_ASN1_GENERALIZEDTIME:
+    case V_ASN1_GRAPHICSTRING:
+    case V_ASN1_VISIBLESTRING:
+    case V_ASN1_GENERALSTRING:
+    case V_ASN1_UNIVERSALSTRING:
+    case V_ASN1_BMPSTRING:
+    case V_ASN1_UTF8STRING:
+    case V_ASN1_SEQUENCE:
+    case V_ASN1_SET:
+    default:
+        /* All based on ASN1_STRING and handled the same */
+        strtmp = (ASN1_STRING *)*pval;
+        /* Special handling for NDEF */
+        if ((it->size == ASN1_TFLG_NDEF)
+            && (strtmp->flags & ASN1_STRING_FLAG_NDEF)) {
+            if (cout) {
+                strtmp->data = cout;
+                strtmp->length = 0;
+            }
+            /* Special return code */
+            return -2;
+        }
+        cont = strtmp->data;
+        len = strtmp->length;
+
+        break;
+
+    }
+    if (cout && len)
+        memcpy(cout, cont, len);
+    return len;
+}
diff --git a/crypto/asn1/tasn_fre.c b/crypto/asn1/tasn_fre.c
new file mode 100644
index 000000000..3c98efb36
--- /dev/null
+++ b/crypto/asn1/tasn_fre.c
@@ -0,0 +1,207 @@
+/*
+ * Copyright 2000-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include "asn1_locl.h"
+
+static void asn1_item_embed_free(ASN1_VALUE **pval, const ASN1_ITEM *it,
+                                 int embed);
+
+/* Free up an ASN1 structure */
+
+void ASN1_item_free(ASN1_VALUE *val, const ASN1_ITEM *it)
+{
+    asn1_item_embed_free(&val, it, 0);
+}
+
+void ASN1_item_ex_free(ASN1_VALUE **pval, const ASN1_ITEM *it)
+{
+    asn1_item_embed_free(pval, it, 0);
+}
+
+static void asn1_item_embed_free(ASN1_VALUE **pval, const ASN1_ITEM *it,
+                                 int embed)
+{
+    const ASN1_TEMPLATE *tt = NULL, *seqtt;
+    const ASN1_EXTERN_FUNCS *ef;
+    const ASN1_AUX *aux = it->funcs;
+    ASN1_aux_cb *asn1_cb;
+    int i;
+
+    if (!pval)
+        return;
+    if ((it->itype != ASN1_ITYPE_PRIMITIVE) && !*pval)
+        return;
+    if (aux && aux->asn1_cb)
+        asn1_cb = aux->asn1_cb;
+    else
+        asn1_cb = 0;
+
+    switch (it->itype) {
+
+    case ASN1_ITYPE_PRIMITIVE:
+        if (it->templates)
+            asn1_template_free(pval, it->templates);
+        else
+            asn1_primitive_free(pval, it, embed);
+        break;
+
+    case ASN1_ITYPE_MSTRING:
+        asn1_primitive_free(pval, it, embed);
+        break;
+
+    case ASN1_ITYPE_CHOICE:
+        if (asn1_cb) {
+            i = asn1_cb(ASN1_OP_FREE_PRE, pval, it, NULL);
+            if (i == 2)
+                return;
+        }
+        i = asn1_get_choice_selector(pval, it);
+        if ((i >= 0) && (i < it->tcount)) {
+            ASN1_VALUE **pchval;
+
+            tt = it->templates + i;
+            pchval = asn1_get_field_ptr(pval, tt);
+            asn1_template_free(pchval, tt);
+        }
+        if (asn1_cb)
+            asn1_cb(ASN1_OP_FREE_POST, pval, it, NULL);
+        if (embed == 0) {
+            OPENSSL_free(*pval);
+            *pval = NULL;
+        }
+        break;
+
+    case ASN1_ITYPE_EXTERN:
+        ef = it->funcs;
+        if (ef && ef->asn1_ex_free)
+            ef->asn1_ex_free(pval, it);
+        break;
+
+    case ASN1_ITYPE_NDEF_SEQUENCE:
+    case ASN1_ITYPE_SEQUENCE:
+        if (asn1_do_lock(pval, -1, it) != 0) /* if error or ref-counter > 0 */
+            return;
+        if (asn1_cb) {
+            i = asn1_cb(ASN1_OP_FREE_PRE, pval, it, NULL);
+            if (i == 2)
+                return;
+        }
+        asn1_enc_free(pval, it);
+        /*
+         * If we free up as normal we will invalidate any ANY DEFINED BY
+         * field and we won't be able to determine the type of the field it
+         * defines. So free up in reverse order.
+         */
+        tt = it->templates + it->tcount;
+        for (i = 0; i < it->tcount; i++) {
+            ASN1_VALUE **pseqval;
+
+            tt--;
+            seqtt = asn1_do_adb(pval, tt, 0);
+            if (!seqtt)
+                continue;
+            pseqval = asn1_get_field_ptr(pval, seqtt);
+            asn1_template_free(pseqval, seqtt);
+        }
+        if (asn1_cb)
+            asn1_cb(ASN1_OP_FREE_POST, pval, it, NULL);
+        if (embed == 0) {
+            OPENSSL_free(*pval);
+            *pval = NULL;
+        }
+        break;
+    }
+}
+
+void asn1_template_free(ASN1_VALUE **pval, const ASN1_TEMPLATE *tt)
+{
+    int embed = tt->flags & ASN1_TFLG_EMBED;
+    ASN1_VALUE *tval;
+    if (embed) {
+        tval = (ASN1_VALUE *)pval;
+        pval = &tval;
+    }
+    if (tt->flags & ASN1_TFLG_SK_MASK) {
+        STACK_OF(ASN1_VALUE) *sk = (STACK_OF(ASN1_VALUE) *)*pval;
+        int i;
+
+        for (i = 0; i < sk_ASN1_VALUE_num(sk); i++) {
+            ASN1_VALUE *vtmp = sk_ASN1_VALUE_value(sk, i);
+
+            asn1_item_embed_free(&vtmp, ASN1_ITEM_ptr(tt->item), embed);
+        }
+        sk_ASN1_VALUE_free(sk);
+        *pval = NULL;
+    } else {
+        asn1_item_embed_free(pval, ASN1_ITEM_ptr(tt->item), embed);
+    }
+}
+
+void asn1_primitive_free(ASN1_VALUE **pval, const ASN1_ITEM *it, int embed)
+{
+    int utype;
+
+    /* Special case: if 'it' is a primitive with a free_func, use that. */
+    if (it) {
+        const ASN1_PRIMITIVE_FUNCS *pf = it->funcs;
+
+        if (pf && pf->prim_free) {
+            pf->prim_free(pval, it);
+            return;
+        }
+    }
+
+    /* Special case: if 'it' is NULL, free contents of ASN1_TYPE */
+    if (!it) {
+        ASN1_TYPE *typ = (ASN1_TYPE *)*pval;
+
+        utype = typ->type;
+        pval = &typ->value.asn1_value;
+        if (!*pval)
+            return;
+    } else if (it->itype == ASN1_ITYPE_MSTRING) {
+        utype = -1;
+        if (!*pval)
+            return;
+    } else {
+        utype = it->utype;
+        if ((utype != V_ASN1_BOOLEAN) && !*pval)
+            return;
+    }
+
+    switch (utype) {
+    case V_ASN1_OBJECT:
+        ASN1_OBJECT_free((ASN1_OBJECT *)*pval);
+        break;
+
+    case V_ASN1_BOOLEAN:
+        if (it)
+            *(ASN1_BOOLEAN *)pval = it->size;
+        else
+            *(ASN1_BOOLEAN *)pval = -1;
+        return;
+
+    case V_ASN1_NULL:
+        break;
+
+    case V_ASN1_ANY:
+        asn1_primitive_free(pval, NULL, 0);
+        OPENSSL_free(*pval);
+        break;
+
+    default:
+        asn1_string_embed_free((ASN1_STRING *)*pval, embed);
+        break;
+    }
+    *pval = NULL;
+}
diff --git a/crypto/asn1/tasn_new.c b/crypto/asn1/tasn_new.c
new file mode 100644
index 000000000..897120d26
--- /dev/null
+++ b/crypto/asn1/tasn_new.c
@@ -0,0 +1,334 @@
+/*
+ * Copyright 2000-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include "asn1_locl.h"
+
+static int asn1_item_embed_new(ASN1_VALUE **pval, const ASN1_ITEM *it,
+                               int embed);
+static int asn1_primitive_new(ASN1_VALUE **pval, const ASN1_ITEM *it,
+                              int embed);
+static void asn1_item_clear(ASN1_VALUE **pval, const ASN1_ITEM *it);
+static int asn1_template_new(ASN1_VALUE **pval, const ASN1_TEMPLATE *tt);
+static void asn1_template_clear(ASN1_VALUE **pval, const ASN1_TEMPLATE *tt);
+static void asn1_primitive_clear(ASN1_VALUE **pval, const ASN1_ITEM *it);
+
+ASN1_VALUE *ASN1_item_new(const ASN1_ITEM *it)
+{
+    ASN1_VALUE *ret = NULL;
+    if (ASN1_item_ex_new(&ret, it) > 0)
+        return ret;
+    return NULL;
+}
+
+/* Allocate an ASN1 structure */
+
+int ASN1_item_ex_new(ASN1_VALUE **pval, const ASN1_ITEM *it)
+{
+    return asn1_item_embed_new(pval, it, 0);
+}
+
+int asn1_item_embed_new(ASN1_VALUE **pval, const ASN1_ITEM *it, int embed)
+{
+    const ASN1_TEMPLATE *tt = NULL;
+    const ASN1_EXTERN_FUNCS *ef;
+    const ASN1_AUX *aux = it->funcs;
+    ASN1_aux_cb *asn1_cb;
+    ASN1_VALUE **pseqval;
+    int i;
+    if (aux && aux->asn1_cb)
+        asn1_cb = aux->asn1_cb;
+    else
+        asn1_cb = 0;
+
+#ifndef OPENSSL_NO_CRYPTO_MDEBUG
+    OPENSSL_mem_debug_push(it->sname ? it->sname : "asn1_item_embed_new");
+#endif
+
+    switch (it->itype) {
+
+    case ASN1_ITYPE_EXTERN:
+        ef = it->funcs;
+        if (ef && ef->asn1_ex_new) {
+            if (!ef->asn1_ex_new(pval, it))
+                goto memerr;
+        }
+        break;
+
+    case ASN1_ITYPE_PRIMITIVE:
+        if (it->templates) {
+            if (!asn1_template_new(pval, it->templates))
+                goto memerr;
+        } else if (!asn1_primitive_new(pval, it, embed))
+            goto memerr;
+        break;
+
+    case ASN1_ITYPE_MSTRING:
+        if (!asn1_primitive_new(pval, it, embed))
+            goto memerr;
+        break;
+
+    case ASN1_ITYPE_CHOICE:
+        if (asn1_cb) {
+            i = asn1_cb(ASN1_OP_NEW_PRE, pval, it, NULL);
+            if (!i)
+                goto auxerr;
+            if (i == 2) {
+#ifndef OPENSSL_NO_CRYPTO_MDEBUG
+                OPENSSL_mem_debug_pop();
+#endif
+                return 1;
+            }
+        }
+        if (embed) {
+            memset(*pval, 0, it->size);
+        } else {
+            *pval = OPENSSL_zalloc(it->size);
+            if (*pval == NULL)
+                goto memerr;
+        }
+        asn1_set_choice_selector(pval, -1, it);
+        if (asn1_cb && !asn1_cb(ASN1_OP_NEW_POST, pval, it, NULL))
+            goto auxerr;
+        break;
+
+    case ASN1_ITYPE_NDEF_SEQUENCE:
+    case ASN1_ITYPE_SEQUENCE:
+        if (asn1_cb) {
+            i = asn1_cb(ASN1_OP_NEW_PRE, pval, it, NULL);
+            if (!i)
+                goto auxerr;
+            if (i == 2) {
+#ifndef OPENSSL_NO_CRYPTO_MDEBUG
+                OPENSSL_mem_debug_pop();
+#endif
+                return 1;
+            }
+        }
+        if (embed) {
+            memset(*pval, 0, it->size);
+        } else {
+            *pval = OPENSSL_zalloc(it->size);
+            if (*pval == NULL)
+                goto memerr;
+        }
+        /* 0 : init. lock */
+        if (asn1_do_lock(pval, 0, it) < 0)
+            goto memerr;
+        asn1_enc_init(pval, it);
+        for (i = 0, tt = it->templates; i < it->tcount; tt++, i++) {
+            pseqval = asn1_get_field_ptr(pval, tt);
+            if (!asn1_template_new(pseqval, tt))
+                goto memerr;
+        }
+        if (asn1_cb && !asn1_cb(ASN1_OP_NEW_POST, pval, it, NULL))
+            goto auxerr;
+        break;
+    }
+#ifndef OPENSSL_NO_CRYPTO_MDEBUG
+    OPENSSL_mem_debug_pop();
+#endif
+    return 1;
+
+ memerr:
+    ASN1err(ASN1_F_ASN1_ITEM_EMBED_NEW, ERR_R_MALLOC_FAILURE);
+#ifndef OPENSSL_NO_CRYPTO_MDEBUG
+    OPENSSL_mem_debug_pop();
+#endif
+    return 0;
+
+ auxerr:
+    ASN1err(ASN1_F_ASN1_ITEM_EMBED_NEW, ASN1_R_AUX_ERROR);
+    ASN1_item_ex_free(pval, it);
+#ifndef OPENSSL_NO_CRYPTO_MDEBUG
+    OPENSSL_mem_debug_pop();
+#endif
+    return 0;
+
+}
+
+static void asn1_item_clear(ASN1_VALUE **pval, const ASN1_ITEM *it)
+{
+    const ASN1_EXTERN_FUNCS *ef;
+
+    switch (it->itype) {
+
+    case ASN1_ITYPE_EXTERN:
+        ef = it->funcs;
+        if (ef && ef->asn1_ex_clear)
+            ef->asn1_ex_clear(pval, it);
+        else
+            *pval = NULL;
+        break;
+
+    case ASN1_ITYPE_PRIMITIVE:
+        if (it->templates)
+            asn1_template_clear(pval, it->templates);
+        else
+            asn1_primitive_clear(pval, it);
+        break;
+
+    case ASN1_ITYPE_MSTRING:
+        asn1_primitive_clear(pval, it);
+        break;
+
+    case ASN1_ITYPE_CHOICE:
+    case ASN1_ITYPE_SEQUENCE:
+    case ASN1_ITYPE_NDEF_SEQUENCE:
+        *pval = NULL;
+        break;
+    }
+}
+
+static int asn1_template_new(ASN1_VALUE **pval, const ASN1_TEMPLATE *tt)
+{
+    const ASN1_ITEM *it = ASN1_ITEM_ptr(tt->item);
+    int embed = tt->flags & ASN1_TFLG_EMBED;
+    ASN1_VALUE *tval;
+    int ret;
+    if (embed) {
+        tval = (ASN1_VALUE *)pval;
+        pval = &tval;
+    }
+    if (tt->flags & ASN1_TFLG_OPTIONAL) {
+        asn1_template_clear(pval, tt);
+        return 1;
+    }
+    /* If ANY DEFINED BY nothing to do */
+
+    if (tt->flags & ASN1_TFLG_ADB_MASK) {
+        *pval = NULL;
+        return 1;
+    }
+#ifndef OPENSSL_NO_CRYPTO_MDEBUG
+    OPENSSL_mem_debug_push(tt->field_name
+            ? tt->field_name : "asn1_template_new");
+#endif
+    /* If SET OF or SEQUENCE OF, its a STACK */
+    if (tt->flags & ASN1_TFLG_SK_MASK) {
+        STACK_OF(ASN1_VALUE) *skval;
+        skval = sk_ASN1_VALUE_new_null();
+        if (!skval) {
+            ASN1err(ASN1_F_ASN1_TEMPLATE_NEW, ERR_R_MALLOC_FAILURE);
+            ret = 0;
+            goto done;
+        }
+        *pval = (ASN1_VALUE *)skval;
+        ret = 1;
+        goto done;
+    }
+    /* Otherwise pass it back to the item routine */
+    ret = asn1_item_embed_new(pval, it, embed);
+ done:
+#ifndef OPENSSL_NO_CRYPTO_MDEBUG
+    OPENSSL_mem_debug_pop();
+#endif
+    return ret;
+}
+
+static void asn1_template_clear(ASN1_VALUE **pval, const ASN1_TEMPLATE *tt)
+{
+    /* If ADB or STACK just NULL the field */
+    if (tt->flags & (ASN1_TFLG_ADB_MASK | ASN1_TFLG_SK_MASK))
+        *pval = NULL;
+    else
+        asn1_item_clear(pval, ASN1_ITEM_ptr(tt->item));
+}
+
+/*
+ * NB: could probably combine most of the real XXX_new() behaviour and junk
+ * all the old functions.
+ */
+
+static int asn1_primitive_new(ASN1_VALUE **pval, const ASN1_ITEM *it,
+                              int embed)
+{
+    ASN1_TYPE *typ;
+    ASN1_STRING *str;
+    int utype;
+
+    if (!it)
+        return 0;
+
+    if (it->funcs) {
+        const ASN1_PRIMITIVE_FUNCS *pf = it->funcs;
+        if (pf->prim_new)
+            return pf->prim_new(pval, it);
+    }
+
+    if (it->itype == ASN1_ITYPE_MSTRING)
+        utype = -1;
+    else
+        utype = it->utype;
+    switch (utype) {
+    case V_ASN1_OBJECT:
+        *pval = (ASN1_VALUE *)OBJ_nid2obj(NID_undef);
+        return 1;
+
+    case V_ASN1_BOOLEAN:
+        *(ASN1_BOOLEAN *)pval = it->size;
+        return 1;
+
+    case V_ASN1_NULL:
+        *pval = (ASN1_VALUE *)1;
+        return 1;
+
+    case V_ASN1_ANY:
+        typ = OPENSSL_malloc(sizeof(*typ));
+        if (typ == NULL)
+            return 0;
+        typ->value.ptr = NULL;
+        typ->type = -1;
+        *pval = (ASN1_VALUE *)typ;
+        break;
+
+    default:
+        if (embed) {
+            str = *(ASN1_STRING **)pval;
+            memset(str, 0, sizeof(*str));
+            str->type = utype;
+            str->flags = ASN1_STRING_FLAG_EMBED;
+        } else {
+            str = ASN1_STRING_type_new(utype);
+            *pval = (ASN1_VALUE *)str;
+        }
+        if (it->itype == ASN1_ITYPE_MSTRING && str)
+            str->flags |= ASN1_STRING_FLAG_MSTRING;
+        break;
+    }
+    if (*pval)
+        return 1;
+    return 0;
+}
+
+static void asn1_primitive_clear(ASN1_VALUE **pval, const ASN1_ITEM *it)
+{
+    int utype;
+    if (it && it->funcs) {
+        const ASN1_PRIMITIVE_FUNCS *pf = it->funcs;
+        if (pf->prim_clear)
+            pf->prim_clear(pval, it);
+        else
+            *pval = NULL;
+        return;
+    }
+    if (!it || (it->itype == ASN1_ITYPE_MSTRING))
+        utype = -1;
+    else
+        utype = it->utype;
+    if (utype == V_ASN1_BOOLEAN)
+        *(ASN1_BOOLEAN *)pval = it->size;
+    else
+        *pval = NULL;
+}
diff --git a/crypto/asn1/tasn_prn.c b/crypto/asn1/tasn_prn.c
new file mode 100644
index 000000000..f53e9056a
--- /dev/null
+++ b/crypto/asn1/tasn_prn.c
@@ -0,0 +1,538 @@
+/*
+ * Copyright 2000-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include "internal/asn1_int.h"
+#include "asn1_locl.h"
+
+/*
+ * Print routines.
+ */
+
+/* ASN1_PCTX routines */
+
+static ASN1_PCTX default_pctx = {
+    ASN1_PCTX_FLAGS_SHOW_ABSENT, /* flags */
+    0,                          /* nm_flags */
+    0,                          /* cert_flags */
+    0,                          /* oid_flags */
+    0                           /* str_flags */
+};
+
+ASN1_PCTX *ASN1_PCTX_new(void)
+{
+    ASN1_PCTX *ret;
+
+    ret = OPENSSL_zalloc(sizeof(*ret));
+    if (ret == NULL) {
+        ASN1err(ASN1_F_ASN1_PCTX_NEW, ERR_R_MALLOC_FAILURE);
+        return NULL;
+    }
+    return ret;
+}
+
+void ASN1_PCTX_free(ASN1_PCTX *p)
+{
+    OPENSSL_free(p);
+}
+
+unsigned long ASN1_PCTX_get_flags(const ASN1_PCTX *p)
+{
+    return p->flags;
+}
+
+void ASN1_PCTX_set_flags(ASN1_PCTX *p, unsigned long flags)
+{
+    p->flags = flags;
+}
+
+unsigned long ASN1_PCTX_get_nm_flags(const ASN1_PCTX *p)
+{
+    return p->nm_flags;
+}
+
+void ASN1_PCTX_set_nm_flags(ASN1_PCTX *p, unsigned long flags)
+{
+    p->nm_flags = flags;
+}
+
+unsigned long ASN1_PCTX_get_cert_flags(const ASN1_PCTX *p)
+{
+    return p->cert_flags;
+}
+
+void ASN1_PCTX_set_cert_flags(ASN1_PCTX *p, unsigned long flags)
+{
+    p->cert_flags = flags;
+}
+
+unsigned long ASN1_PCTX_get_oid_flags(const ASN1_PCTX *p)
+{
+    return p->oid_flags;
+}
+
+void ASN1_PCTX_set_oid_flags(ASN1_PCTX *p, unsigned long flags)
+{
+    p->oid_flags = flags;
+}
+
+unsigned long ASN1_PCTX_get_str_flags(const ASN1_PCTX *p)
+{
+    return p->str_flags;
+}
+
+void ASN1_PCTX_set_str_flags(ASN1_PCTX *p, unsigned long flags)
+{
+    p->str_flags = flags;
+}
+
+/* Main print routines */
+
+static int asn1_item_print_ctx(BIO *out, ASN1_VALUE **fld, int indent,
+                               const ASN1_ITEM *it,
+                               const char *fname, const char *sname,
+                               int nohdr, const ASN1_PCTX *pctx);
+
+static int asn1_template_print_ctx(BIO *out, ASN1_VALUE **fld, int indent,
+                            const ASN1_TEMPLATE *tt, const ASN1_PCTX *pctx);
+
+static int asn1_primitive_print(BIO *out, ASN1_VALUE **fld,
+                                const ASN1_ITEM *it, int indent,
+                                const char *fname, const char *sname,
+                                const ASN1_PCTX *pctx);
+
+static int asn1_print_fsname(BIO *out, int indent,
+                             const char *fname, const char *sname,
+                             const ASN1_PCTX *pctx);
+
+int ASN1_item_print(BIO *out, ASN1_VALUE *ifld, int indent,
+                    const ASN1_ITEM *it, const ASN1_PCTX *pctx)
+{
+    const char *sname;
+    if (pctx == NULL)
+        pctx = &default_pctx;
+    if (pctx->flags & ASN1_PCTX_FLAGS_NO_STRUCT_NAME)
+        sname = NULL;
+    else
+        sname = it->sname;
+    return asn1_item_print_ctx(out, &ifld, indent, it, NULL, sname, 0, pctx);
+}
+
+static int asn1_item_print_ctx(BIO *out, ASN1_VALUE **fld, int indent,
+                               const ASN1_ITEM *it,
+                               const char *fname, const char *sname,
+                               int nohdr, const ASN1_PCTX *pctx)
+{
+    const ASN1_TEMPLATE *tt;
+    const ASN1_EXTERN_FUNCS *ef;
+    ASN1_VALUE **tmpfld;
+    const ASN1_AUX *aux = it->funcs;
+    ASN1_aux_cb *asn1_cb;
+    ASN1_PRINT_ARG parg;
+    int i;
+    if (aux && aux->asn1_cb) {
+        parg.out = out;
+        parg.indent = indent;
+        parg.pctx = pctx;
+        asn1_cb = aux->asn1_cb;
+    } else
+        asn1_cb = 0;
+
+   if (((it->itype != ASN1_ITYPE_PRIMITIVE)
+       || (it->utype != V_ASN1_BOOLEAN)) && *fld == NULL) {
+        if (pctx->flags & ASN1_PCTX_FLAGS_SHOW_ABSENT) {
+            if (!nohdr && !asn1_print_fsname(out, indent, fname, sname, pctx))
+                return 0;
+            if (BIO_puts(out, "\n") <= 0)
+                return 0;
+        }
+        return 1;
+    }
+
+    switch (it->itype) {
+    case ASN1_ITYPE_PRIMITIVE:
+        if (it->templates) {
+            if (!asn1_template_print_ctx(out, fld, indent,
+                                         it->templates, pctx))
+                return 0;
+            break;
+        }
+        /* fall through */
+    case ASN1_ITYPE_MSTRING:
+        if (!asn1_primitive_print(out, fld, it, indent, fname, sname, pctx))
+            return 0;
+        break;
+
+    case ASN1_ITYPE_EXTERN:
+        if (!nohdr && !asn1_print_fsname(out, indent, fname, sname, pctx))
+            return 0;
+        /* Use new style print routine if possible */
+        ef = it->funcs;
+        if (ef && ef->asn1_ex_print) {
+            i = ef->asn1_ex_print(out, fld, indent, "", pctx);
+            if (!i)
+                return 0;
+            if ((i == 2) && (BIO_puts(out, "\n") <= 0))
+                return 0;
+            return 1;
+        } else if (sname &&
+                   BIO_printf(out, ":EXTERNAL TYPE %s\n", sname) <= 0)
+            return 0;
+        break;
+
+    case ASN1_ITYPE_CHOICE:
+        /* CHOICE type, get selector */
+        i = asn1_get_choice_selector(fld, it);
+        /* This should never happen... */
+        if ((i < 0) || (i >= it->tcount)) {
+            if (BIO_printf(out, "ERROR: selector [%d] invalid\n", i) <= 0)
+                return 0;
+            return 1;
+        }
+        tt = it->templates + i;
+        tmpfld = asn1_get_field_ptr(fld, tt);
+        if (!asn1_template_print_ctx(out, tmpfld, indent, tt, pctx))
+            return 0;
+        break;
+
+    case ASN1_ITYPE_SEQUENCE:
+    case ASN1_ITYPE_NDEF_SEQUENCE:
+        if (!nohdr && !asn1_print_fsname(out, indent, fname, sname, pctx))
+            return 0;
+        if (fname || sname) {
+            if (pctx->flags & ASN1_PCTX_FLAGS_SHOW_SEQUENCE) {
+                if (BIO_puts(out, " {\n") <= 0)
+                    return 0;
+            } else {
+                if (BIO_puts(out, "\n") <= 0)
+                    return 0;
+            }
+        }
+
+        if (asn1_cb) {
+            i = asn1_cb(ASN1_OP_PRINT_PRE, fld, it, &parg);
+            if (i == 0)
+                return 0;
+            if (i == 2)
+                return 1;
+        }
+
+        /* Print each field entry */
+        for (i = 0, tt = it->templates; i < it->tcount; i++, tt++) {
+            const ASN1_TEMPLATE *seqtt;
+            seqtt = asn1_do_adb(fld, tt, 1);
+            if (!seqtt)
+                return 0;
+            tmpfld = asn1_get_field_ptr(fld, seqtt);
+            if (!asn1_template_print_ctx(out, tmpfld,
+                                         indent + 2, seqtt, pctx))
+                return 0;
+        }
+        if (pctx->flags & ASN1_PCTX_FLAGS_SHOW_SEQUENCE) {
+            if (BIO_printf(out, "%*s}\n", indent, "") < 0)
+                return 0;
+        }
+
+        if (asn1_cb) {
+            i = asn1_cb(ASN1_OP_PRINT_POST, fld, it, &parg);
+            if (i == 0)
+                return 0;
+        }
+        break;
+
+    default:
+        BIO_printf(out, "Unprocessed type %d\n", it->itype);
+        return 0;
+    }
+
+    return 1;
+}
+
+static int asn1_template_print_ctx(BIO *out, ASN1_VALUE **fld, int indent,
+                            const ASN1_TEMPLATE *tt, const ASN1_PCTX *pctx)
+{
+    int i, flags;
+    const char *sname, *fname;
+    ASN1_VALUE *tfld;
+    flags = tt->flags;
+    if (pctx->flags & ASN1_PCTX_FLAGS_SHOW_FIELD_STRUCT_NAME)
+        sname = ASN1_ITEM_ptr(tt->item)->sname;
+    else
+        sname = NULL;
+    if (pctx->flags & ASN1_PCTX_FLAGS_NO_FIELD_NAME)
+        fname = NULL;
+    else
+        fname = tt->field_name;
+
+    /*
+     * If field is embedded then fld needs fixing so it is a pointer to
+     * a pointer to a field.
+     */
+    if (flags & ASN1_TFLG_EMBED) {
+        tfld = (ASN1_VALUE *)fld;
+        fld = &tfld;
+    }
+
+    if (flags & ASN1_TFLG_SK_MASK) {
+        char *tname;
+        ASN1_VALUE *skitem;
+        STACK_OF(ASN1_VALUE) *stack;
+
+        /* SET OF, SEQUENCE OF */
+        if (fname) {
+            if (pctx->flags & ASN1_PCTX_FLAGS_SHOW_SSOF) {
+                if (flags & ASN1_TFLG_SET_OF)
+                    tname = "SET";
+                else
+                    tname = "SEQUENCE";
+                if (BIO_printf(out, "%*s%s OF %s {\n",
+                               indent, "", tname, tt->field_name) <= 0)
+                    return 0;
+            } else if (BIO_printf(out, "%*s%s:\n", indent, "", fname) <= 0)
+                return 0;
+        }
+        stack = (STACK_OF(ASN1_VALUE) *)*fld;
+        for (i = 0; i < sk_ASN1_VALUE_num(stack); i++) {
+            if ((i > 0) && (BIO_puts(out, "\n") <= 0))
+                return 0;
+
+            skitem = sk_ASN1_VALUE_value(stack, i);
+            if (!asn1_item_print_ctx(out, &skitem, indent + 2,
+                                     ASN1_ITEM_ptr(tt->item), NULL, NULL, 1,
+                                     pctx))
+                return 0;
+        }
+        if (!i && BIO_printf(out, "%*s\n", indent + 2, "") <= 0)
+            return 0;
+        if (pctx->flags & ASN1_PCTX_FLAGS_SHOW_SEQUENCE) {
+            if (BIO_printf(out, "%*s}\n", indent, "") <= 0)
+                return 0;
+        }
+        return 1;
+    }
+    return asn1_item_print_ctx(out, fld, indent, ASN1_ITEM_ptr(tt->item),
+                               fname, sname, 0, pctx);
+}
+
+static int asn1_print_fsname(BIO *out, int indent,
+                             const char *fname, const char *sname,
+                             const ASN1_PCTX *pctx)
+{
+    static const char spaces[] = "                    ";
+    static const int nspaces = sizeof(spaces) - 1;
+
+    while (indent > nspaces) {
+        if (BIO_write(out, spaces, nspaces) != nspaces)
+            return 0;
+        indent -= nspaces;
+    }
+    if (BIO_write(out, spaces, indent) != indent)
+        return 0;
+    if (pctx->flags & ASN1_PCTX_FLAGS_NO_STRUCT_NAME)
+        sname = NULL;
+    if (pctx->flags & ASN1_PCTX_FLAGS_NO_FIELD_NAME)
+        fname = NULL;
+    if (!sname && !fname)
+        return 1;
+    if (fname) {
+        if (BIO_puts(out, fname) <= 0)
+            return 0;
+    }
+    if (sname) {
+        if (fname) {
+            if (BIO_printf(out, " (%s)", sname) <= 0)
+                return 0;
+        } else {
+            if (BIO_puts(out, sname) <= 0)
+                return 0;
+        }
+    }
+    if (BIO_write(out, ": ", 2) != 2)
+        return 0;
+    return 1;
+}
+
+static int asn1_print_boolean(BIO *out, int boolval)
+{
+    const char *str;
+    switch (boolval) {
+    case -1:
+        str = "BOOL ABSENT";
+        break;
+
+    case 0:
+        str = "FALSE";
+        break;
+
+    default:
+        str = "TRUE";
+        break;
+
+    }
+
+    if (BIO_puts(out, str) <= 0)
+        return 0;
+    return 1;
+
+}
+
+static int asn1_print_integer(BIO *out, const ASN1_INTEGER *str)
+{
+    char *s;
+    int ret = 1;
+    s = i2s_ASN1_INTEGER(NULL, str);
+    if (s == NULL)
+        return 0;
+    if (BIO_puts(out, s) <= 0)
+        ret = 0;
+    OPENSSL_free(s);
+    return ret;
+}
+
+static int asn1_print_oid(BIO *out, const ASN1_OBJECT *oid)
+{
+    char objbuf[80];
+    const char *ln;
+    ln = OBJ_nid2ln(OBJ_obj2nid(oid));
+    if (!ln)
+        ln = "";
+    OBJ_obj2txt(objbuf, sizeof objbuf, oid, 1);
+    if (BIO_printf(out, "%s (%s)", ln, objbuf) <= 0)
+        return 0;
+    return 1;
+}
+
+static int asn1_print_obstring(BIO *out, const ASN1_STRING *str, int indent)
+{
+    if (str->type == V_ASN1_BIT_STRING) {
+        if (BIO_printf(out, " (%ld unused bits)\n", str->flags & 0x7) <= 0)
+            return 0;
+    } else if (BIO_puts(out, "\n") <= 0)
+        return 0;
+    if ((str->length > 0)
+        && BIO_dump_indent(out, (const char *)str->data, str->length,
+                           indent + 2) <= 0)
+        return 0;
+    return 1;
+}
+
+static int asn1_primitive_print(BIO *out, ASN1_VALUE **fld,
+                                const ASN1_ITEM *it, int indent,
+                                const char *fname, const char *sname,
+                                const ASN1_PCTX *pctx)
+{
+    long utype;
+    ASN1_STRING *str;
+    int ret = 1, needlf = 1;
+    const char *pname;
+    const ASN1_PRIMITIVE_FUNCS *pf;
+    pf = it->funcs;
+    if (!asn1_print_fsname(out, indent, fname, sname, pctx))
+        return 0;
+    if (pf && pf->prim_print)
+        return pf->prim_print(out, fld, it, indent, pctx);
+    if (it->itype == ASN1_ITYPE_MSTRING) {
+        str = (ASN1_STRING *)*fld;
+        utype = str->type & ~V_ASN1_NEG;
+    } else {
+        utype = it->utype;
+        if (utype == V_ASN1_BOOLEAN)
+            str = NULL;
+        else
+            str = (ASN1_STRING *)*fld;
+    }
+    if (utype == V_ASN1_ANY) {
+        ASN1_TYPE *atype = (ASN1_TYPE *)*fld;
+        utype = atype->type;
+        fld = &atype->value.asn1_value;
+        str = (ASN1_STRING *)*fld;
+        if (pctx->flags & ASN1_PCTX_FLAGS_NO_ANY_TYPE)
+            pname = NULL;
+        else
+            pname = ASN1_tag2str(utype);
+    } else {
+        if (pctx->flags & ASN1_PCTX_FLAGS_SHOW_TYPE)
+            pname = ASN1_tag2str(utype);
+        else
+            pname = NULL;
+    }
+
+    if (utype == V_ASN1_NULL) {
+        if (BIO_puts(out, "NULL\n") <= 0)
+            return 0;
+        return 1;
+    }
+
+    if (pname) {
+        if (BIO_puts(out, pname) <= 0)
+            return 0;
+        if (BIO_puts(out, ":") <= 0)
+            return 0;
+    }
+
+    switch (utype) {
+    case V_ASN1_BOOLEAN:
+        {
+            int boolval = *(int *)fld;
+            if (boolval == -1)
+                boolval = it->size;
+            ret = asn1_print_boolean(out, boolval);
+        }
+        break;
+
+    case V_ASN1_INTEGER:
+    case V_ASN1_ENUMERATED:
+        ret = asn1_print_integer(out, str);
+        break;
+
+    case V_ASN1_UTCTIME:
+        ret = ASN1_UTCTIME_print(out, str);
+        break;
+
+    case V_ASN1_GENERALIZEDTIME:
+        ret = ASN1_GENERALIZEDTIME_print(out, str);
+        break;
+
+    case V_ASN1_OBJECT:
+        ret = asn1_print_oid(out, (const ASN1_OBJECT *)*fld);
+        break;
+
+    case V_ASN1_OCTET_STRING:
+    case V_ASN1_BIT_STRING:
+        ret = asn1_print_obstring(out, str, indent);
+        needlf = 0;
+        break;
+
+    case V_ASN1_SEQUENCE:
+    case V_ASN1_SET:
+    case V_ASN1_OTHER:
+        if (BIO_puts(out, "\n") <= 0)
+            return 0;
+        if (ASN1_parse_dump(out, str->data, str->length, indent, 0) <= 0)
+            ret = 0;
+        needlf = 0;
+        break;
+
+    default:
+        ret = ASN1_STRING_print_ex(out, str, pctx->str_flags);
+
+    }
+    if (!ret)
+        return 0;
+    if (needlf && BIO_puts(out, "\n") <= 0)
+        return 0;
+    return 1;
+}
diff --git a/crypto/asn1/tasn_scn.c b/crypto/asn1/tasn_scn.c
new file mode 100644
index 000000000..e1df2cfca
--- /dev/null
+++ b/crypto/asn1/tasn_scn.c
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2010-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include "asn1_locl.h"
+
+/*
+ * General ASN1 structure recursive scanner: iterate through all fields
+ * passing details to a callback.
+ */
+
+ASN1_SCTX *ASN1_SCTX_new(int (*scan_cb) (ASN1_SCTX *ctx))
+{
+    ASN1_SCTX *ret = OPENSSL_zalloc(sizeof(*ret));
+
+    if (ret == NULL) {
+        ASN1err(ASN1_F_ASN1_SCTX_NEW, ERR_R_MALLOC_FAILURE);
+        return NULL;
+    }
+    ret->scan_cb = scan_cb;
+    return ret;
+}
+
+void ASN1_SCTX_free(ASN1_SCTX *p)
+{
+    OPENSSL_free(p);
+}
+
+const ASN1_ITEM *ASN1_SCTX_get_item(ASN1_SCTX *p)
+{
+    return p->it;
+}
+
+const ASN1_TEMPLATE *ASN1_SCTX_get_template(ASN1_SCTX *p)
+{
+    return p->tt;
+}
+
+unsigned long ASN1_SCTX_get_flags(ASN1_SCTX *p)
+{
+    return p->flags;
+}
+
+void ASN1_SCTX_set_app_data(ASN1_SCTX *p, void *data)
+{
+    p->app_data = data;
+}
+
+void *ASN1_SCTX_get_app_data(ASN1_SCTX *p)
+{
+    return p->app_data;
+}
diff --git a/crypto/asn1/tasn_typ.c b/crypto/asn1/tasn_typ.c
new file mode 100644
index 000000000..98d987901
--- /dev/null
+++ b/crypto/asn1/tasn_typ.c
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2000-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include 
+
+/* Declarations for string types */
+
+#define IMPLEMENT_ASN1_STRING_FUNCTIONS(sname) \
+    IMPLEMENT_ASN1_TYPE(sname) \
+    IMPLEMENT_ASN1_ENCODE_FUNCTIONS_fname(sname, sname, sname) \
+sname *sname##_new(void) \
+{ \
+    return ASN1_STRING_type_new(V_##sname); \
+} \
+void sname##_free(sname *x) \
+{ \
+    ASN1_STRING_free(x); \
+}
+
+IMPLEMENT_ASN1_STRING_FUNCTIONS(ASN1_OCTET_STRING)
+IMPLEMENT_ASN1_STRING_FUNCTIONS(ASN1_INTEGER)
+IMPLEMENT_ASN1_STRING_FUNCTIONS(ASN1_ENUMERATED)
+IMPLEMENT_ASN1_STRING_FUNCTIONS(ASN1_BIT_STRING)
+IMPLEMENT_ASN1_STRING_FUNCTIONS(ASN1_UTF8STRING)
+IMPLEMENT_ASN1_STRING_FUNCTIONS(ASN1_PRINTABLESTRING)
+IMPLEMENT_ASN1_STRING_FUNCTIONS(ASN1_T61STRING)
+IMPLEMENT_ASN1_STRING_FUNCTIONS(ASN1_IA5STRING)
+IMPLEMENT_ASN1_STRING_FUNCTIONS(ASN1_GENERALSTRING)
+IMPLEMENT_ASN1_STRING_FUNCTIONS(ASN1_UTCTIME)
+IMPLEMENT_ASN1_STRING_FUNCTIONS(ASN1_GENERALIZEDTIME)
+IMPLEMENT_ASN1_STRING_FUNCTIONS(ASN1_VISIBLESTRING)
+IMPLEMENT_ASN1_STRING_FUNCTIONS(ASN1_UNIVERSALSTRING)
+IMPLEMENT_ASN1_STRING_FUNCTIONS(ASN1_BMPSTRING)
+
+IMPLEMENT_ASN1_TYPE(ASN1_NULL)
+IMPLEMENT_ASN1_FUNCTIONS(ASN1_NULL)
+
+IMPLEMENT_ASN1_TYPE(ASN1_OBJECT)
+
+IMPLEMENT_ASN1_TYPE(ASN1_ANY)
+
+/* Just swallow an ASN1_SEQUENCE in an ASN1_STRING */
+IMPLEMENT_ASN1_TYPE(ASN1_SEQUENCE)
+
+IMPLEMENT_ASN1_FUNCTIONS_fname(ASN1_TYPE, ASN1_ANY, ASN1_TYPE)
+
+/* Multistring types */
+
+IMPLEMENT_ASN1_MSTRING(ASN1_PRINTABLE, B_ASN1_PRINTABLE)
+IMPLEMENT_ASN1_FUNCTIONS_name(ASN1_STRING, ASN1_PRINTABLE)
+
+IMPLEMENT_ASN1_MSTRING(DISPLAYTEXT, B_ASN1_DISPLAYTEXT)
+IMPLEMENT_ASN1_FUNCTIONS_name(ASN1_STRING, DISPLAYTEXT)
+
+IMPLEMENT_ASN1_MSTRING(DIRECTORYSTRING, B_ASN1_DIRECTORYSTRING)
+IMPLEMENT_ASN1_FUNCTIONS_name(ASN1_STRING, DIRECTORYSTRING)
+
+/* Three separate BOOLEAN type: normal, DEFAULT TRUE and DEFAULT FALSE */
+IMPLEMENT_ASN1_TYPE_ex(ASN1_BOOLEAN, ASN1_BOOLEAN, -1)
+IMPLEMENT_ASN1_TYPE_ex(ASN1_TBOOLEAN, ASN1_BOOLEAN, 1)
+IMPLEMENT_ASN1_TYPE_ex(ASN1_FBOOLEAN, ASN1_BOOLEAN, 0)
+
+/* Special, OCTET STRING with indefinite length constructed support */
+
+IMPLEMENT_ASN1_TYPE_ex(ASN1_OCTET_STRING_NDEF, ASN1_OCTET_STRING, ASN1_TFLG_NDEF)
+
+ASN1_ITEM_TEMPLATE(ASN1_SEQUENCE_ANY) =
+        ASN1_EX_TEMPLATE_TYPE(ASN1_TFLG_SEQUENCE_OF, 0, ASN1_SEQUENCE_ANY, ASN1_ANY)
+ASN1_ITEM_TEMPLATE_END(ASN1_SEQUENCE_ANY)
+
+ASN1_ITEM_TEMPLATE(ASN1_SET_ANY) =
+        ASN1_EX_TEMPLATE_TYPE(ASN1_TFLG_SET_OF, 0, ASN1_SET_ANY, ASN1_ANY)
+ASN1_ITEM_TEMPLATE_END(ASN1_SET_ANY)
+
+IMPLEMENT_ASN1_ENCODE_FUNCTIONS_const_fname(ASN1_SEQUENCE_ANY, ASN1_SEQUENCE_ANY, ASN1_SEQUENCE_ANY)
+IMPLEMENT_ASN1_ENCODE_FUNCTIONS_const_fname(ASN1_SEQUENCE_ANY, ASN1_SET_ANY, ASN1_SET_ANY)
diff --git a/crypto/asn1/tasn_utl.c b/crypto/asn1/tasn_utl.c
new file mode 100644
index 000000000..f79d7d6b4
--- /dev/null
+++ b/crypto/asn1/tasn_utl.c
@@ -0,0 +1,240 @@
+/*
+ * Copyright 2000-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include "asn1_locl.h"
+
+/* Utility functions for manipulating fields and offsets */
+
+/* Add 'offset' to 'addr' */
+#define offset2ptr(addr, offset) (void *)(((char *) addr) + offset)
+
+/*
+ * Given an ASN1_ITEM CHOICE type return the selector value
+ */
+
+int asn1_get_choice_selector(ASN1_VALUE **pval, const ASN1_ITEM *it)
+{
+    int *sel = offset2ptr(*pval, it->utype);
+    return *sel;
+}
+
+/*
+ * Given an ASN1_ITEM CHOICE type set the selector value, return old value.
+ */
+
+int asn1_set_choice_selector(ASN1_VALUE **pval, int value,
+                             const ASN1_ITEM *it)
+{
+    int *sel, ret;
+    sel = offset2ptr(*pval, it->utype);
+    ret = *sel;
+    *sel = value;
+    return ret;
+}
+
+/*
+ * Do atomic reference counting. The value 'op' decides what to do.
+ * If it is +1 then the count is incremented.
+ * If |op| is 0, lock is initialised and count is set to 1.
+ * If |op| is -1, count is decremented and the return value is the current
+ * reference count or 0 if no reference count is active.
+ * It returns -1 on initialisation error.
+ * Used by ASN1_SEQUENCE construct of X509, X509_REQ, X509_CRL objects
+ */
+int asn1_do_lock(ASN1_VALUE **pval, int op, const ASN1_ITEM *it)
+{
+    const ASN1_AUX *aux;
+    int *lck, ret;
+    CRYPTO_RWLOCK **lock;
+    if ((it->itype != ASN1_ITYPE_SEQUENCE)
+        && (it->itype != ASN1_ITYPE_NDEF_SEQUENCE))
+        return 0;
+    aux = it->funcs;
+    if (!aux || !(aux->flags & ASN1_AFLG_REFCOUNT))
+        return 0;
+    lck = offset2ptr(*pval, aux->ref_offset);
+    lock = offset2ptr(*pval, aux->ref_lock);
+    if (op == 0) {
+        *lck = 1;
+        *lock = CRYPTO_THREAD_lock_new();
+        if (*lock == NULL) {
+            ASN1err(ASN1_F_ASN1_DO_LOCK, ERR_R_MALLOC_FAILURE);
+            return -1;
+        }
+        return 1;
+    }
+    if (CRYPTO_atomic_add(lck, op, &ret, *lock) < 0)
+        return -1;  /* failed */
+#ifdef REF_PRINT
+    fprintf(stderr, "%p:%4d:%s\n", it, *lck, it->sname);
+#endif
+    REF_ASSERT_ISNT(ret < 0);
+    if (ret == 0) {
+        CRYPTO_THREAD_lock_free(*lock);
+        *lock = NULL;
+    }
+    return ret;
+}
+
+static ASN1_ENCODING *asn1_get_enc_ptr(ASN1_VALUE **pval, const ASN1_ITEM *it)
+{
+    const ASN1_AUX *aux;
+    if (!pval || !*pval)
+        return NULL;
+    aux = it->funcs;
+    if (!aux || !(aux->flags & ASN1_AFLG_ENCODING))
+        return NULL;
+    return offset2ptr(*pval, aux->enc_offset);
+}
+
+void asn1_enc_init(ASN1_VALUE **pval, const ASN1_ITEM *it)
+{
+    ASN1_ENCODING *enc;
+    enc = asn1_get_enc_ptr(pval, it);
+    if (enc) {
+        enc->enc = NULL;
+        enc->len = 0;
+        enc->modified = 1;
+    }
+}
+
+void asn1_enc_free(ASN1_VALUE **pval, const ASN1_ITEM *it)
+{
+    ASN1_ENCODING *enc;
+    enc = asn1_get_enc_ptr(pval, it);
+    if (enc) {
+        OPENSSL_free(enc->enc);
+        enc->enc = NULL;
+        enc->len = 0;
+        enc->modified = 1;
+    }
+}
+
+int asn1_enc_save(ASN1_VALUE **pval, const unsigned char *in, int inlen,
+                  const ASN1_ITEM *it)
+{
+    ASN1_ENCODING *enc;
+    enc = asn1_get_enc_ptr(pval, it);
+    if (!enc)
+        return 1;
+
+    OPENSSL_free(enc->enc);
+    enc->enc = OPENSSL_malloc(inlen);
+    if (enc->enc == NULL)
+        return 0;
+    memcpy(enc->enc, in, inlen);
+    enc->len = inlen;
+    enc->modified = 0;
+
+    return 1;
+}
+
+int asn1_enc_restore(int *len, unsigned char **out, ASN1_VALUE **pval,
+                     const ASN1_ITEM *it)
+{
+    ASN1_ENCODING *enc;
+    enc = asn1_get_enc_ptr(pval, it);
+    if (!enc || enc->modified)
+        return 0;
+    if (out) {
+        memcpy(*out, enc->enc, enc->len);
+        *out += enc->len;
+    }
+    if (len)
+        *len = enc->len;
+    return 1;
+}
+
+/* Given an ASN1_TEMPLATE get a pointer to a field */
+ASN1_VALUE **asn1_get_field_ptr(ASN1_VALUE **pval, const ASN1_TEMPLATE *tt)
+{
+    ASN1_VALUE **pvaltmp;
+    pvaltmp = offset2ptr(*pval, tt->offset);
+    /*
+     * NOTE for BOOLEAN types the field is just a plain int so we can't
+     * return int **, so settle for (int *).
+     */
+    return pvaltmp;
+}
+
+/*
+ * Handle ANY DEFINED BY template, find the selector, look up the relevant
+ * ASN1_TEMPLATE in the table and return it.
+ */
+
+const ASN1_TEMPLATE *asn1_do_adb(ASN1_VALUE **pval, const ASN1_TEMPLATE *tt,
+                                 int nullerr)
+{
+    const ASN1_ADB *adb;
+    const ASN1_ADB_TABLE *atbl;
+    long selector;
+    ASN1_VALUE **sfld;
+    int i;
+    if (!(tt->flags & ASN1_TFLG_ADB_MASK))
+        return tt;
+
+    /* Else ANY DEFINED BY ... get the table */
+    adb = ASN1_ADB_ptr(tt->item);
+
+    /* Get the selector field */
+    sfld = offset2ptr(*pval, adb->offset);
+
+    /* Check if NULL */
+    if (*sfld == NULL) {
+        if (!adb->null_tt)
+            goto err;
+        return adb->null_tt;
+    }
+
+    /*
+     * Convert type to a long: NB: don't check for NID_undef here because it
+     * might be a legitimate value in the table
+     */
+    if (tt->flags & ASN1_TFLG_ADB_OID)
+        selector = OBJ_obj2nid((ASN1_OBJECT *)*sfld);
+    else
+        selector = ASN1_INTEGER_get((ASN1_INTEGER *)*sfld);
+
+    /* Let application callback translate value */
+    if (adb->adb_cb != NULL && adb->adb_cb(&selector) == 0) {
+        ASN1err(ASN1_F_ASN1_DO_ADB, ASN1_R_UNSUPPORTED_ANY_DEFINED_BY_TYPE);
+        return NULL;
+    }
+
+    /*
+     * Try to find matching entry in table Maybe should check application
+     * types first to allow application override? Might also be useful to
+     * have a flag which indicates table is sorted and we can do a binary
+     * search. For now stick to a linear search.
+     */
+
+    for (atbl = adb->tbl, i = 0; i < adb->tblcount; i++, atbl++)
+        if (atbl->value == selector)
+            return &atbl->tt;
+
+    /* FIXME: need to search application table too */
+
+    /* No match, return default type */
+    if (!adb->default_tt)
+        goto err;
+    return adb->default_tt;
+
+ err:
+    /* FIXME: should log the value or OID of unsupported type */
+    if (nullerr)
+        ASN1err(ASN1_F_ASN1_DO_ADB, ASN1_R_UNSUPPORTED_ANY_DEFINED_BY_TYPE);
+    return NULL;
+}
diff --git a/crypto/asn1/x_algor.c b/crypto/asn1/x_algor.c
new file mode 100644
index 000000000..72378db92
--- /dev/null
+++ b/crypto/asn1/x_algor.c
@@ -0,0 +1,93 @@
+/*
+ * Copyright 1998-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include "internal/evp_int.h"
+
+ASN1_SEQUENCE(X509_ALGOR) = {
+        ASN1_SIMPLE(X509_ALGOR, algorithm, ASN1_OBJECT),
+        ASN1_OPT(X509_ALGOR, parameter, ASN1_ANY)
+} ASN1_SEQUENCE_END(X509_ALGOR)
+
+ASN1_ITEM_TEMPLATE(X509_ALGORS) =
+        ASN1_EX_TEMPLATE_TYPE(ASN1_TFLG_SEQUENCE_OF, 0, algorithms, X509_ALGOR)
+ASN1_ITEM_TEMPLATE_END(X509_ALGORS)
+
+IMPLEMENT_ASN1_FUNCTIONS(X509_ALGOR)
+IMPLEMENT_ASN1_ENCODE_FUNCTIONS_fname(X509_ALGORS, X509_ALGORS, X509_ALGORS)
+IMPLEMENT_ASN1_DUP_FUNCTION(X509_ALGOR)
+
+int X509_ALGOR_set0(X509_ALGOR *alg, ASN1_OBJECT *aobj, int ptype, void *pval)
+{
+    if (!alg)
+        return 0;
+    if (ptype != V_ASN1_UNDEF) {
+        if (alg->parameter == NULL)
+            alg->parameter = ASN1_TYPE_new();
+        if (alg->parameter == NULL)
+            return 0;
+    }
+    if (alg) {
+        ASN1_OBJECT_free(alg->algorithm);
+        alg->algorithm = aobj;
+    }
+    if (ptype == 0)
+        return 1;
+    if (ptype == V_ASN1_UNDEF) {
+        ASN1_TYPE_free(alg->parameter);
+        alg->parameter = NULL;
+    } else
+        ASN1_TYPE_set(alg->parameter, ptype, pval);
+    return 1;
+}
+
+void X509_ALGOR_get0(const ASN1_OBJECT **paobj, int *pptype,
+                     const void **ppval, const X509_ALGOR *algor)
+{
+    if (paobj)
+        *paobj = algor->algorithm;
+    if (pptype) {
+        if (algor->parameter == NULL) {
+            *pptype = V_ASN1_UNDEF;
+            return;
+        } else
+            *pptype = algor->parameter->type;
+        if (ppval)
+            *ppval = algor->parameter->value.ptr;
+    }
+}
+
+/* Set up an X509_ALGOR DigestAlgorithmIdentifier from an EVP_MD */
+
+void X509_ALGOR_set_md(X509_ALGOR *alg, const EVP_MD *md)
+{
+    int param_type;
+
+    if (md->flags & EVP_MD_FLAG_DIGALGID_ABSENT)
+        param_type = V_ASN1_UNDEF;
+    else
+        param_type = V_ASN1_NULL;
+
+    X509_ALGOR_set0(alg, OBJ_nid2obj(EVP_MD_type(md)), param_type, NULL);
+
+}
+
+int X509_ALGOR_cmp(const X509_ALGOR *a, const X509_ALGOR *b)
+{
+    int rv;
+    rv = OBJ_cmp(a->algorithm, b->algorithm);
+    if (rv)
+        return rv;
+    if (!a->parameter && !b->parameter)
+        return 0;
+    return ASN1_TYPE_cmp(a->parameter, b->parameter);
+}
diff --git a/crypto/asn1/x_bignum.c b/crypto/asn1/x_bignum.c
new file mode 100644
index 000000000..da57e77a7
--- /dev/null
+++ b/crypto/asn1/x_bignum.c
@@ -0,0 +1,146 @@
+/*
+ * Copyright 2000-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+
+/*
+ * Custom primitive type for BIGNUM handling. This reads in an ASN1_INTEGER
+ * as a BIGNUM directly. Currently it ignores the sign which isn't a problem
+ * since all BIGNUMs used are non negative and anything that looks negative
+ * is normally due to an encoding error.
+ */
+
+#define BN_SENSITIVE    1
+
+static int bn_new(ASN1_VALUE **pval, const ASN1_ITEM *it);
+static int bn_secure_new(ASN1_VALUE **pval, const ASN1_ITEM *it);
+static void bn_free(ASN1_VALUE **pval, const ASN1_ITEM *it);
+
+static int bn_i2c(ASN1_VALUE **pval, unsigned char *cont, int *putype,
+                  const ASN1_ITEM *it);
+static int bn_c2i(ASN1_VALUE **pval, const unsigned char *cont, int len,
+                  int utype, char *free_cont, const ASN1_ITEM *it);
+static int bn_secure_c2i(ASN1_VALUE **pval, const unsigned char *cont, int len,
+                         int utype, char *free_cont, const ASN1_ITEM *it);
+static int bn_print(BIO *out, ASN1_VALUE **pval, const ASN1_ITEM *it,
+                    int indent, const ASN1_PCTX *pctx);
+
+static ASN1_PRIMITIVE_FUNCS bignum_pf = {
+    NULL, 0,
+    bn_new,
+    bn_free,
+    0,
+    bn_c2i,
+    bn_i2c,
+    bn_print
+};
+
+static ASN1_PRIMITIVE_FUNCS cbignum_pf = {
+    NULL, 0,
+    bn_secure_new,
+    bn_free,
+    0,
+    bn_secure_c2i,
+    bn_i2c,
+    bn_print
+};
+
+ASN1_ITEM_start(BIGNUM)
+        ASN1_ITYPE_PRIMITIVE, V_ASN1_INTEGER, NULL, 0, &bignum_pf, 0, "BIGNUM"
+ASN1_ITEM_end(BIGNUM)
+
+ASN1_ITEM_start(CBIGNUM)
+        ASN1_ITYPE_PRIMITIVE, V_ASN1_INTEGER, NULL, 0, &cbignum_pf, BN_SENSITIVE, "CBIGNUM"
+ASN1_ITEM_end(CBIGNUM)
+
+static int bn_new(ASN1_VALUE **pval, const ASN1_ITEM *it)
+{
+    *pval = (ASN1_VALUE *)BN_new();
+    if (*pval != NULL)
+        return 1;
+    else
+        return 0;
+}
+
+static int bn_secure_new(ASN1_VALUE **pval, const ASN1_ITEM *it)
+{
+    *pval = (ASN1_VALUE *)BN_secure_new();
+    if (*pval != NULL)
+        return 1;
+    else
+        return 0;
+}
+
+static void bn_free(ASN1_VALUE **pval, const ASN1_ITEM *it)
+{
+    if (!*pval)
+        return;
+    if (it->size & BN_SENSITIVE)
+        BN_clear_free((BIGNUM *)*pval);
+    else
+        BN_free((BIGNUM *)*pval);
+    *pval = NULL;
+}
+
+static int bn_i2c(ASN1_VALUE **pval, unsigned char *cont, int *putype,
+                  const ASN1_ITEM *it)
+{
+    BIGNUM *bn;
+    int pad;
+    if (!*pval)
+        return -1;
+    bn = (BIGNUM *)*pval;
+    /* If MSB set in an octet we need a padding byte */
+    if (BN_num_bits(bn) & 0x7)
+        pad = 0;
+    else
+        pad = 1;
+    if (cont) {
+        if (pad)
+            *cont++ = 0;
+        BN_bn2bin(bn, cont);
+    }
+    return pad + BN_num_bytes(bn);
+}
+
+static int bn_c2i(ASN1_VALUE **pval, const unsigned char *cont, int len,
+                  int utype, char *free_cont, const ASN1_ITEM *it)
+{
+    BIGNUM *bn;
+
+    if (*pval == NULL && !bn_new(pval, it))
+        return 0;
+    bn = (BIGNUM *)*pval;
+    if (!BN_bin2bn(cont, len, bn)) {
+        bn_free(pval, it);
+        return 0;
+    }
+    return 1;
+}
+
+static int bn_secure_c2i(ASN1_VALUE **pval, const unsigned char *cont, int len,
+                         int utype, char *free_cont, const ASN1_ITEM *it)
+{
+    if (!*pval)
+        bn_secure_new(pval, it);
+    return bn_c2i(pval, cont, len, utype, free_cont, it);
+}
+
+static int bn_print(BIO *out, ASN1_VALUE **pval, const ASN1_ITEM *it,
+                    int indent, const ASN1_PCTX *pctx)
+{
+    if (!BN_print(out, *(BIGNUM **)pval))
+        return 0;
+    if (BIO_puts(out, "\n") <= 0)
+        return 0;
+    return 1;
+}
diff --git a/crypto/asn1/x_info.c b/crypto/asn1/x_info.c
new file mode 100644
index 000000000..8d99f07c6
--- /dev/null
+++ b/crypto/asn1/x_info.c
@@ -0,0 +1,39 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+#include 
+
+X509_INFO *X509_INFO_new(void)
+{
+    X509_INFO *ret;
+
+    ret = OPENSSL_zalloc(sizeof(*ret));
+    if (ret == NULL) {
+        ASN1err(ASN1_F_X509_INFO_NEW, ERR_R_MALLOC_FAILURE);
+        return NULL;
+    }
+
+    return ret;
+}
+
+void X509_INFO_free(X509_INFO *x)
+{
+    if (x == NULL)
+        return;
+
+    X509_free(x->x509);
+    X509_CRL_free(x->crl);
+    X509_PKEY_free(x->x_pkey);
+    OPENSSL_free(x->enc_data);
+    OPENSSL_free(x);
+}
diff --git a/crypto/asn1/x_long.c b/crypto/asn1/x_long.c
new file mode 100644
index 000000000..c2844713c
--- /dev/null
+++ b/crypto/asn1/x_long.c
@@ -0,0 +1,146 @@
+/*
+ * Copyright 2000-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+
+/*
+ * Custom primitive type for long handling. This converts between an
+ * ASN1_INTEGER and a long directly.
+ */
+
+static int long_new(ASN1_VALUE **pval, const ASN1_ITEM *it);
+static void long_free(ASN1_VALUE **pval, const ASN1_ITEM *it);
+
+static int long_i2c(ASN1_VALUE **pval, unsigned char *cont, int *putype,
+                    const ASN1_ITEM *it);
+static int long_c2i(ASN1_VALUE **pval, const unsigned char *cont, int len,
+                    int utype, char *free_cont, const ASN1_ITEM *it);
+static int long_print(BIO *out, ASN1_VALUE **pval, const ASN1_ITEM *it,
+                      int indent, const ASN1_PCTX *pctx);
+
+static ASN1_PRIMITIVE_FUNCS long_pf = {
+    NULL, 0,
+    long_new,
+    long_free,
+    long_free,                  /* Clear should set to initial value */
+    long_c2i,
+    long_i2c,
+    long_print
+};
+
+ASN1_ITEM_start(LONG)
+        ASN1_ITYPE_PRIMITIVE, V_ASN1_INTEGER, NULL, 0, &long_pf, ASN1_LONG_UNDEF, "LONG"
+ASN1_ITEM_end(LONG)
+
+ASN1_ITEM_start(ZLONG)
+        ASN1_ITYPE_PRIMITIVE, V_ASN1_INTEGER, NULL, 0, &long_pf, 0, "ZLONG"
+ASN1_ITEM_end(ZLONG)
+
+static int long_new(ASN1_VALUE **pval, const ASN1_ITEM *it)
+{
+    *(long *)pval = it->size;
+    return 1;
+}
+
+static void long_free(ASN1_VALUE **pval, const ASN1_ITEM *it)
+{
+    *(long *)pval = it->size;
+}
+
+static int long_i2c(ASN1_VALUE **pval, unsigned char *cont, int *putype,
+                    const ASN1_ITEM *it)
+{
+    long ltmp;
+    unsigned long utmp;
+    int clen, pad, i;
+    /* this exists to bypass broken gcc optimization */
+    char *cp = (char *)pval;
+
+    /* use memcpy, because we may not be long aligned */
+    memcpy(<mp, cp, sizeof(long));
+
+    if (ltmp == it->size)
+        return -1;
+    /*
+     * Convert the long to positive: we subtract one if negative so we can
+     * cleanly handle the padding if only the MSB of the leading octet is
+     * set.
+     */
+    if (ltmp < 0)
+        utmp = 0 - (unsigned long)ltmp - 1;
+    else
+        utmp = ltmp;
+    clen = BN_num_bits_word(utmp);
+    /* If MSB of leading octet set we need to pad */
+    if (!(clen & 0x7))
+        pad = 1;
+    else
+        pad = 0;
+
+    /* Convert number of bits to number of octets */
+    clen = (clen + 7) >> 3;
+
+    if (cont) {
+        if (pad)
+            *cont++ = (ltmp < 0) ? 0xff : 0;
+        for (i = clen - 1; i >= 0; i--) {
+            cont[i] = (unsigned char)(utmp & 0xff);
+            if (ltmp < 0)
+                cont[i] ^= 0xff;
+            utmp >>= 8;
+        }
+    }
+    return clen + pad;
+}
+
+static int long_c2i(ASN1_VALUE **pval, const unsigned char *cont, int len,
+                    int utype, char *free_cont, const ASN1_ITEM *it)
+{
+    int neg, i;
+    long ltmp;
+    unsigned long utmp = 0;
+    char *cp = (char *)pval;
+    if (len > (int)sizeof(long)) {
+        ASN1err(ASN1_F_LONG_C2I, ASN1_R_INTEGER_TOO_LARGE_FOR_LONG);
+        return 0;
+    }
+    /* Is it negative? */
+    if (len && (cont[0] & 0x80))
+        neg = 1;
+    else
+        neg = 0;
+    utmp = 0;
+    for (i = 0; i < len; i++) {
+        utmp <<= 8;
+        if (neg)
+            utmp |= cont[i] ^ 0xff;
+        else
+            utmp |= cont[i];
+    }
+    ltmp = (long)utmp;
+    if (neg) {
+        ltmp = -ltmp;
+        ltmp--;
+    }
+    if (ltmp == it->size) {
+        ASN1err(ASN1_F_LONG_C2I, ASN1_R_INTEGER_TOO_LARGE_FOR_LONG);
+        return 0;
+    }
+    memcpy(cp, <mp, sizeof(long));
+    return 1;
+}
+
+static int long_print(BIO *out, ASN1_VALUE **pval, const ASN1_ITEM *it,
+                      int indent, const ASN1_PCTX *pctx)
+{
+    return BIO_printf(out, "%ld\n", *(long *)pval);
+}
diff --git a/crypto/asn1/x_pkey.c b/crypto/asn1/x_pkey.c
new file mode 100644
index 000000000..593049f0f
--- /dev/null
+++ b/crypto/asn1/x_pkey.c
@@ -0,0 +1,47 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+#include 
+
+X509_PKEY *X509_PKEY_new(void)
+{
+    X509_PKEY *ret = NULL;
+
+    ret = OPENSSL_zalloc(sizeof(*ret));
+    if (ret == NULL)
+        goto err;
+
+    ret->enc_algor = X509_ALGOR_new();
+    ret->enc_pkey = ASN1_OCTET_STRING_new();
+    if (ret->enc_algor == NULL || ret->enc_pkey == NULL)
+        goto err;
+
+    return ret;
+err:
+    X509_PKEY_free(ret);
+    ASN1err(ASN1_F_X509_PKEY_NEW, ERR_R_MALLOC_FAILURE);
+    return NULL;
+}
+
+void X509_PKEY_free(X509_PKEY *x)
+{
+    if (x == NULL)
+        return;
+
+    X509_ALGOR_free(x->enc_algor);
+    ASN1_OCTET_STRING_free(x->enc_pkey);
+    EVP_PKEY_free(x->dec_pkey);
+    if (x->key_free)
+        OPENSSL_free(x->key_data);
+    OPENSSL_free(x);
+}
diff --git a/crypto/asn1/x_sig.c b/crypto/asn1/x_sig.c
new file mode 100644
index 000000000..e465cf2d4
--- /dev/null
+++ b/crypto/asn1/x_sig.c
@@ -0,0 +1,39 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+#include "internal/x509_int.h"
+
+ASN1_SEQUENCE(X509_SIG) = {
+        ASN1_SIMPLE(X509_SIG, algor, X509_ALGOR),
+        ASN1_SIMPLE(X509_SIG, digest, ASN1_OCTET_STRING)
+} ASN1_SEQUENCE_END(X509_SIG)
+
+IMPLEMENT_ASN1_FUNCTIONS(X509_SIG)
+
+void X509_SIG_get0(const X509_SIG *sig, const X509_ALGOR **palg,
+                   const ASN1_OCTET_STRING **pdigest)
+{
+    if (palg)
+        *palg = sig->algor;
+    if (pdigest)
+        *pdigest = sig->digest;
+}
+
+void X509_SIG_getm(X509_SIG *sig, X509_ALGOR **palg,
+                   ASN1_OCTET_STRING **pdigest)
+{
+    if (palg)
+        *palg = sig->algor;
+    if (pdigest)
+        *pdigest = sig->digest;
+}
diff --git a/crypto/asn1/x_spki.c b/crypto/asn1/x_spki.c
new file mode 100644
index 000000000..c45400b42
--- /dev/null
+++ b/crypto/asn1/x_spki.c
@@ -0,0 +1,33 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+ /*
+  * This module was send to me my Pat Richards  who wrote it.
+  * It is under my Copyright with his permission
+  */
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+
+ASN1_SEQUENCE(NETSCAPE_SPKAC) = {
+        ASN1_SIMPLE(NETSCAPE_SPKAC, pubkey, X509_PUBKEY),
+        ASN1_SIMPLE(NETSCAPE_SPKAC, challenge, ASN1_IA5STRING)
+} ASN1_SEQUENCE_END(NETSCAPE_SPKAC)
+
+IMPLEMENT_ASN1_FUNCTIONS(NETSCAPE_SPKAC)
+
+ASN1_SEQUENCE(NETSCAPE_SPKI) = {
+        ASN1_SIMPLE(NETSCAPE_SPKI, spkac, NETSCAPE_SPKAC),
+        ASN1_EMBED(NETSCAPE_SPKI, sig_algor, X509_ALGOR),
+        ASN1_SIMPLE(NETSCAPE_SPKI, signature, ASN1_BIT_STRING)
+} ASN1_SEQUENCE_END(NETSCAPE_SPKI)
+
+IMPLEMENT_ASN1_FUNCTIONS(NETSCAPE_SPKI)
diff --git a/crypto/asn1/x_val.c b/crypto/asn1/x_val.c
new file mode 100644
index 000000000..d1f1d3bff
--- /dev/null
+++ b/crypto/asn1/x_val.c
@@ -0,0 +1,20 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+
+ASN1_SEQUENCE(X509_VAL) = {
+        ASN1_SIMPLE(X509_VAL, notBefore, ASN1_TIME),
+        ASN1_SIMPLE(X509_VAL, notAfter, ASN1_TIME)
+} ASN1_SEQUENCE_END(X509_VAL)
+
+IMPLEMENT_ASN1_FUNCTIONS(X509_VAL)
diff --git a/crypto/async/arch/async_null.c b/crypto/async/arch/async_null.c
new file mode 100644
index 000000000..3eaf170f2
--- /dev/null
+++ b/crypto/async/arch/async_null.c
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2015-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/* This must be the first #include file */
+#include "../async_locl.h"
+
+#ifdef ASYNC_NULL
+int ASYNC_is_capable(void)
+{
+    return 0;
+}
+
+void async_local_cleanup(void)
+{
+}
+#endif
+
diff --git a/crypto/async/arch/async_null.h b/crypto/async/arch/async_null.h
new file mode 100644
index 000000000..aef40b5d9
--- /dev/null
+++ b/crypto/async/arch/async_null.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2015-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+
+/*
+ * If we haven't managed to detect any other async architecture then we default
+ * to NULL.
+ */
+#ifndef ASYNC_ARCH
+# define ASYNC_NULL
+# define ASYNC_ARCH
+
+typedef struct async_fibre_st {
+    int dummy;
+} async_fibre;
+
+
+# define async_fibre_swapcontext(o,n,r)         0
+# define async_fibre_makecontext(c)             0
+# define async_fibre_free(f)
+# define async_fibre_init_dispatcher(f)
+
+#endif
diff --git a/crypto/async/arch/async_posix.c b/crypto/async/arch/async_posix.c
new file mode 100644
index 000000000..02c342d3d
--- /dev/null
+++ b/crypto/async/arch/async_posix.c
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2015-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/* This must be the first #include file */
+#include "../async_locl.h"
+
+#ifdef ASYNC_POSIX
+
+# include 
+# include 
+
+#define STACKSIZE       32768
+
+int ASYNC_is_capable(void)
+{
+    ucontext_t ctx;
+
+    /*
+     * Some platforms provide getcontext() but it does not work (notably
+     * MacOSX PPC64). Check for a working getcontext();
+     */
+    return getcontext(&ctx) == 0;
+}
+
+void async_local_cleanup(void)
+{
+}
+
+int async_fibre_makecontext(async_fibre *fibre)
+{
+    fibre->env_init = 0;
+    if (getcontext(&fibre->fibre) == 0) {
+        fibre->fibre.uc_stack.ss_sp = OPENSSL_malloc(STACKSIZE);
+        if (fibre->fibre.uc_stack.ss_sp != NULL) {
+            fibre->fibre.uc_stack.ss_size = STACKSIZE;
+            fibre->fibre.uc_link = NULL;
+            makecontext(&fibre->fibre, async_start_func, 0);
+            return 1;
+        }
+    } else {
+        fibre->fibre.uc_stack.ss_sp = NULL;
+    }
+    return 0;
+}
+
+void async_fibre_free(async_fibre *fibre)
+{
+    OPENSSL_free(fibre->fibre.uc_stack.ss_sp);
+    fibre->fibre.uc_stack.ss_sp = NULL;
+}
+
+#endif
diff --git a/crypto/async/arch/async_posix.h b/crypto/async/arch/async_posix.h
new file mode 100644
index 000000000..3c61f7f7b
--- /dev/null
+++ b/crypto/async/arch/async_posix.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2015-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#ifndef OPENSSL_ASYNC_ARCH_ASYNC_POSIX_H
+#define OPENSSL_ASYNC_ARCH_ASYNC_POSIX_H
+#include 
+
+#if (defined(OPENSSL_SYS_UNIX) || defined(OPENSSL_SYS_CYGWIN)) \
+    && defined(OPENSSL_THREADS) && !defined(OPENSSL_NO_ASYNC) \
+    && !defined(__ANDROID__) && !defined(__OpenBSD__)
+
+# include 
+
+# if _POSIX_VERSION >= 200112L
+
+# include 
+
+#  define ASYNC_POSIX
+#  define ASYNC_ARCH
+
+#  include 
+#  include 
+#  include "e_os.h"
+
+typedef struct async_fibre_st {
+    ucontext_t fibre;
+    jmp_buf env;
+    int env_init;
+} async_fibre;
+
+static ossl_inline int async_fibre_swapcontext(async_fibre *o, async_fibre *n, int r)
+{
+    o->env_init = 1;
+
+    if (!r || !_setjmp(o->env)) {
+        if (n->env_init)
+            _longjmp(n->env, 1);
+        else
+            setcontext(&n->fibre);
+    }
+
+    return 1;
+}
+
+#  define async_fibre_init_dispatcher(d)
+
+int async_fibre_makecontext(async_fibre *fibre);
+void async_fibre_free(async_fibre *fibre);
+
+# endif
+#endif
+#endif /* OPENSSL_ASYNC_ARCH_ASYNC_POSIX_H */
diff --git a/crypto/async/arch/async_win.c b/crypto/async/arch/async_win.c
new file mode 100644
index 000000000..077d56ced
--- /dev/null
+++ b/crypto/async/arch/async_win.c
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2015-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/* This must be the first #include file */
+#include "../async_locl.h"
+
+#ifdef ASYNC_WIN
+
+# include 
+# include "internal/cryptlib.h"
+
+int ASYNC_is_capable(void)
+{
+    return 1;
+}
+
+void async_local_cleanup(void)
+{
+    async_ctx *ctx = async_get_ctx();
+    if (ctx != NULL) {
+        async_fibre *fibre = &ctx->dispatcher;
+        if (fibre != NULL && fibre->fibre != NULL && fibre->converted) {
+            ConvertFiberToThread();
+            fibre->fibre = NULL;
+        }
+    }
+}
+
+int async_fibre_init_dispatcher(async_fibre *fibre)
+{
+    fibre->fibre = ConvertThreadToFiber(NULL);
+    if (fibre->fibre == NULL) {
+        fibre->converted = 0;
+        fibre->fibre = GetCurrentFiber();
+        if (fibre->fibre == NULL)
+            return 0;
+    } else {
+        fibre->converted = 1;
+    }
+
+    return 1;
+}
+
+VOID CALLBACK async_start_func_win(PVOID unused)
+{
+    async_start_func();
+}
+
+#endif
diff --git a/crypto/async/arch/async_win.h b/crypto/async/arch/async_win.h
new file mode 100644
index 000000000..61cfdd72d
--- /dev/null
+++ b/crypto/async/arch/async_win.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2015-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * This is the same detection used in cryptlib to set up the thread local
+ * storage that we depend on, so just copy that
+ */
+#if defined(_WIN32) && !defined(OPENSSL_NO_ASYNC)
+#include 
+# define ASYNC_WIN
+# define ASYNC_ARCH
+
+# include 
+# include "internal/cryptlib.h"
+
+typedef struct async_fibre_st {
+    LPVOID fibre;
+    int converted;
+} async_fibre;
+
+# define async_fibre_swapcontext(o,n,r) \
+        (SwitchToFiber((n)->fibre), 1)
+# define async_fibre_makecontext(c) \
+        ((c)->fibre = CreateFiber(0, async_start_func_win, 0))
+# define async_fibre_free(f)             (DeleteFiber((f)->fibre))
+
+int async_fibre_init_dispatcher(async_fibre *fibre);
+VOID CALLBACK async_start_func_win(PVOID unused);
+
+#endif
diff --git a/crypto/async/async.c b/crypto/async/async.c
new file mode 100644
index 000000000..8c699af78
--- /dev/null
+++ b/crypto/async/async.c
@@ -0,0 +1,433 @@
+/*
+ * Copyright 2015-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * Without this we start getting longjmp crashes because it thinks we're jumping
+ * up the stack when in fact we are jumping to an entirely different stack. The
+ * cost of this is not having certain buffer overrun/underrun checks etc for
+ * this source file :-(
+ */
+#undef _FORTIFY_SOURCE
+
+/* This must be the first #include file */
+#include "async_locl.h"
+
+#include 
+#include 
+#include 
+
+#define ASYNC_JOB_RUNNING   0
+#define ASYNC_JOB_PAUSING   1
+#define ASYNC_JOB_PAUSED    2
+#define ASYNC_JOB_STOPPING  3
+
+static CRYPTO_THREAD_LOCAL ctxkey;
+static CRYPTO_THREAD_LOCAL poolkey;
+
+static void async_free_pool_internal(async_pool *pool);
+
+static async_ctx *async_ctx_new(void)
+{
+    async_ctx *nctx = NULL;
+
+    nctx = OPENSSL_malloc(sizeof (async_ctx));
+    if (nctx == NULL) {
+        ASYNCerr(ASYNC_F_ASYNC_CTX_NEW, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+
+    async_fibre_init_dispatcher(&nctx->dispatcher);
+    nctx->currjob = NULL;
+    nctx->blocked = 0;
+    if (!CRYPTO_THREAD_set_local(&ctxkey, nctx))
+        goto err;
+
+    return nctx;
+err:
+    OPENSSL_free(nctx);
+
+    return NULL;
+}
+
+async_ctx *async_get_ctx(void)
+{
+    if (!OPENSSL_init_crypto(OPENSSL_INIT_ASYNC, NULL))
+        return NULL;
+
+    return (async_ctx *)CRYPTO_THREAD_get_local(&ctxkey);
+}
+
+static int async_ctx_free(void)
+{
+    async_ctx *ctx;
+
+    ctx = async_get_ctx();
+
+    if (!CRYPTO_THREAD_set_local(&ctxkey, NULL))
+        return 0;
+
+    OPENSSL_free(ctx);
+
+    return 1;
+}
+
+static ASYNC_JOB *async_job_new(void)
+{
+    ASYNC_JOB *job = NULL;
+
+    job = OPENSSL_zalloc(sizeof (ASYNC_JOB));
+    if (job == NULL) {
+        ASYNCerr(ASYNC_F_ASYNC_JOB_NEW, ERR_R_MALLOC_FAILURE);
+        return NULL;
+    }
+
+    job->status = ASYNC_JOB_RUNNING;
+
+    return job;
+}
+
+static void async_job_free(ASYNC_JOB *job)
+{
+    if (job != NULL) {
+        OPENSSL_free(job->funcargs);
+        async_fibre_free(&job->fibrectx);
+        OPENSSL_free(job);
+    }
+}
+
+static ASYNC_JOB *async_get_pool_job(void) {
+    ASYNC_JOB *job;
+    async_pool *pool;
+
+    pool = (async_pool *)CRYPTO_THREAD_get_local(&poolkey);
+    if (pool == NULL) {
+        /*
+         * Pool has not been initialised, so init with the defaults, i.e.
+         * no max size and no pre-created jobs
+         */
+        if (ASYNC_init_thread(0, 0) == 0)
+            return NULL;
+        pool = (async_pool *)CRYPTO_THREAD_get_local(&poolkey);
+    }
+
+    job = sk_ASYNC_JOB_pop(pool->jobs);
+    if (job == NULL) {
+        /* Pool is empty */
+        if ((pool->max_size != 0) && (pool->curr_size >= pool->max_size))
+            return NULL;
+
+        job = async_job_new();
+        if (job != NULL) {
+            if (! async_fibre_makecontext(&job->fibrectx)) {
+                async_job_free(job);
+                return NULL;
+            }
+            pool->curr_size++;
+        }
+    }
+    return job;
+}
+
+static void async_release_job(ASYNC_JOB *job) {
+    async_pool *pool;
+
+    pool = (async_pool *)CRYPTO_THREAD_get_local(&poolkey);
+    OPENSSL_free(job->funcargs);
+    job->funcargs = NULL;
+    sk_ASYNC_JOB_push(pool->jobs, job);
+}
+
+void async_start_func(void)
+{
+    ASYNC_JOB *job;
+    async_ctx *ctx = async_get_ctx();
+
+    while (1) {
+        /* Run the job */
+        job = ctx->currjob;
+        job->ret = job->func(job->funcargs);
+
+        /* Stop the job */
+        job->status = ASYNC_JOB_STOPPING;
+        if (!async_fibre_swapcontext(&job->fibrectx,
+                                     &ctx->dispatcher, 1)) {
+            /*
+             * Should not happen. Getting here will close the thread...can't do
+             * much about it
+             */
+            ASYNCerr(ASYNC_F_ASYNC_START_FUNC, ASYNC_R_FAILED_TO_SWAP_CONTEXT);
+        }
+    }
+}
+
+int ASYNC_start_job(ASYNC_JOB **job, ASYNC_WAIT_CTX *wctx, int *ret,
+                    int (*func)(void *), void *args, size_t size)
+{
+    async_ctx *ctx = async_get_ctx();
+    if (ctx == NULL)
+        ctx = async_ctx_new();
+    if (ctx == NULL) {
+        return ASYNC_ERR;
+    }
+
+    if (*job) {
+        ctx->currjob = *job;
+    }
+
+    for (;;) {
+        if (ctx->currjob != NULL) {
+            if (ctx->currjob->status == ASYNC_JOB_STOPPING) {
+                *ret = ctx->currjob->ret;
+                ctx->currjob->waitctx = NULL;
+                async_release_job(ctx->currjob);
+                ctx->currjob = NULL;
+                *job = NULL;
+                return ASYNC_FINISH;
+            }
+
+            if (ctx->currjob->status == ASYNC_JOB_PAUSING) {
+                *job = ctx->currjob;
+                ctx->currjob->status = ASYNC_JOB_PAUSED;
+                ctx->currjob = NULL;
+                return ASYNC_PAUSE;
+            }
+
+            if (ctx->currjob->status == ASYNC_JOB_PAUSED) {
+                ctx->currjob = *job;
+                /* Resume previous job */
+                if (!async_fibre_swapcontext(&ctx->dispatcher,
+                        &ctx->currjob->fibrectx, 1)) {
+                    ASYNCerr(ASYNC_F_ASYNC_START_JOB,
+                             ASYNC_R_FAILED_TO_SWAP_CONTEXT);
+                    goto err;
+                }
+                continue;
+            }
+
+            /* Should not happen */
+            ASYNCerr(ASYNC_F_ASYNC_START_JOB, ERR_R_INTERNAL_ERROR);
+            async_release_job(ctx->currjob);
+            ctx->currjob = NULL;
+            *job = NULL;
+            return ASYNC_ERR;
+        }
+
+        /* Start a new job */
+        if ((ctx->currjob = async_get_pool_job()) == NULL) {
+            return ASYNC_NO_JOBS;
+        }
+
+        if (args != NULL) {
+            ctx->currjob->funcargs = OPENSSL_malloc(size);
+            if (ctx->currjob->funcargs == NULL) {
+                ASYNCerr(ASYNC_F_ASYNC_START_JOB, ERR_R_MALLOC_FAILURE);
+                async_release_job(ctx->currjob);
+                ctx->currjob = NULL;
+                return ASYNC_ERR;
+            }
+            memcpy(ctx->currjob->funcargs, args, size);
+        } else {
+            ctx->currjob->funcargs = NULL;
+        }
+
+        ctx->currjob->func = func;
+        ctx->currjob->waitctx = wctx;
+        if (!async_fibre_swapcontext(&ctx->dispatcher,
+                &ctx->currjob->fibrectx, 1)) {
+            ASYNCerr(ASYNC_F_ASYNC_START_JOB, ASYNC_R_FAILED_TO_SWAP_CONTEXT);
+            goto err;
+        }
+    }
+
+err:
+    async_release_job(ctx->currjob);
+    ctx->currjob = NULL;
+    *job = NULL;
+    return ASYNC_ERR;
+}
+
+int ASYNC_pause_job(void)
+{
+    ASYNC_JOB *job;
+    async_ctx *ctx = async_get_ctx();
+
+    if (ctx == NULL
+            || ctx->currjob == NULL
+            || ctx->blocked) {
+        /*
+         * Could be we've deliberately not been started within a job so this is
+         * counted as success.
+         */
+        return 1;
+    }
+
+    job = ctx->currjob;
+    job->status = ASYNC_JOB_PAUSING;
+
+    if (!async_fibre_swapcontext(&job->fibrectx,
+                                 &ctx->dispatcher, 1)) {
+        ASYNCerr(ASYNC_F_ASYNC_PAUSE_JOB, ASYNC_R_FAILED_TO_SWAP_CONTEXT);
+        return 0;
+    }
+    /* Reset counts of added and deleted fds */
+    async_wait_ctx_reset_counts(job->waitctx);
+
+    return 1;
+}
+
+static void async_empty_pool(async_pool *pool)
+{
+    ASYNC_JOB *job;
+
+    if (!pool || !pool->jobs)
+        return;
+
+    do {
+        job = sk_ASYNC_JOB_pop(pool->jobs);
+        async_job_free(job);
+    } while (job);
+}
+
+int async_init(void)
+{
+    if (!CRYPTO_THREAD_init_local(&ctxkey, NULL))
+        return 0;
+
+    if (!CRYPTO_THREAD_init_local(&poolkey, NULL)) {
+        CRYPTO_THREAD_cleanup_local(&ctxkey);
+        return 0;
+    }
+
+    return 1;
+}
+
+void async_deinit(void)
+{
+    CRYPTO_THREAD_cleanup_local(&ctxkey);
+    CRYPTO_THREAD_cleanup_local(&poolkey);
+}
+
+int ASYNC_init_thread(size_t max_size, size_t init_size)
+{
+    async_pool *pool;
+    size_t curr_size = 0;
+
+    if (init_size > max_size) {
+        ASYNCerr(ASYNC_F_ASYNC_INIT_THREAD, ASYNC_R_INVALID_POOL_SIZE);
+        return 0;
+    }
+
+    if (!OPENSSL_init_crypto(OPENSSL_INIT_ASYNC, NULL)) {
+        return 0;
+    }
+    if (!ossl_init_thread_start(OPENSSL_INIT_THREAD_ASYNC)) {
+        return 0;
+    }
+
+    pool = OPENSSL_zalloc(sizeof *pool);
+    if (pool == NULL) {
+        ASYNCerr(ASYNC_F_ASYNC_INIT_THREAD, ERR_R_MALLOC_FAILURE);
+        return 0;
+    }
+
+    pool->jobs = sk_ASYNC_JOB_new_null();
+    if (pool->jobs == NULL) {
+        ASYNCerr(ASYNC_F_ASYNC_INIT_THREAD, ERR_R_MALLOC_FAILURE);
+        OPENSSL_free(pool);
+        return 0;
+    }
+
+    pool->max_size = max_size;
+
+    /* Pre-create jobs as required */
+    while (init_size--) {
+        ASYNC_JOB *job;
+        job = async_job_new();
+        if (job == NULL || !async_fibre_makecontext(&job->fibrectx)) {
+            /*
+             * Not actually fatal because we already created the pool, just
+             * skip creation of any more jobs
+             */
+            async_job_free(job);
+            break;
+        }
+        job->funcargs = NULL;
+        sk_ASYNC_JOB_push(pool->jobs, job);
+        curr_size++;
+    }
+    pool->curr_size = curr_size;
+    if (!CRYPTO_THREAD_set_local(&poolkey, pool)) {
+        ASYNCerr(ASYNC_F_ASYNC_INIT_THREAD, ASYNC_R_FAILED_TO_SET_POOL);
+        goto err;
+    }
+
+    return 1;
+err:
+    async_free_pool_internal(pool);
+    return 0;
+}
+
+static void async_free_pool_internal(async_pool *pool)
+{
+    if (pool == NULL)
+        return;
+
+    async_empty_pool(pool);
+    sk_ASYNC_JOB_free(pool->jobs);
+    OPENSSL_free(pool);
+    CRYPTO_THREAD_set_local(&poolkey, NULL);
+    async_local_cleanup();
+    async_ctx_free();
+}
+
+void ASYNC_cleanup_thread(void)
+{
+    async_free_pool_internal((async_pool *)CRYPTO_THREAD_get_local(&poolkey));
+}
+
+ASYNC_JOB *ASYNC_get_current_job(void)
+{
+    async_ctx *ctx;
+
+    ctx = async_get_ctx();
+    if (ctx == NULL)
+        return NULL;
+
+    return ctx->currjob;
+}
+
+ASYNC_WAIT_CTX *ASYNC_get_wait_ctx(ASYNC_JOB *job)
+{
+    return job->waitctx;
+}
+
+void ASYNC_block_pause(void)
+{
+    async_ctx *ctx = async_get_ctx();
+    if (ctx == NULL || ctx->currjob == NULL) {
+        /*
+         * We're not in a job anyway so ignore this
+         */
+        return;
+    }
+    ctx->blocked++;
+}
+
+void ASYNC_unblock_pause(void)
+{
+    async_ctx *ctx = async_get_ctx();
+    if (ctx == NULL || ctx->currjob == NULL) {
+        /*
+         * We're not in a job anyway so ignore this
+         */
+        return;
+    }
+    if (ctx->blocked > 0)
+        ctx->blocked--;
+}
diff --git a/crypto/async/async_err.c b/crypto/async/async_err.c
new file mode 100644
index 000000000..ae97e9653
--- /dev/null
+++ b/crypto/async/async_err.c
@@ -0,0 +1,51 @@
+/*
+ * Generated by util/mkerr.pl DO NOT EDIT
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include 
+
+/* BEGIN ERROR CODES */
+#ifndef OPENSSL_NO_ERR
+
+# define ERR_FUNC(func) ERR_PACK(ERR_LIB_ASYNC,func,0)
+# define ERR_REASON(reason) ERR_PACK(ERR_LIB_ASYNC,0,reason)
+
+static ERR_STRING_DATA ASYNC_str_functs[] = {
+    {ERR_FUNC(ASYNC_F_ASYNC_CTX_NEW), "async_ctx_new"},
+    {ERR_FUNC(ASYNC_F_ASYNC_INIT_THREAD), "ASYNC_init_thread"},
+    {ERR_FUNC(ASYNC_F_ASYNC_JOB_NEW), "async_job_new"},
+    {ERR_FUNC(ASYNC_F_ASYNC_PAUSE_JOB), "ASYNC_pause_job"},
+    {ERR_FUNC(ASYNC_F_ASYNC_START_FUNC), "async_start_func"},
+    {ERR_FUNC(ASYNC_F_ASYNC_START_JOB), "ASYNC_start_job"},
+    {0, NULL}
+};
+
+static ERR_STRING_DATA ASYNC_str_reasons[] = {
+    {ERR_REASON(ASYNC_R_FAILED_TO_SET_POOL), "failed to set pool"},
+    {ERR_REASON(ASYNC_R_FAILED_TO_SWAP_CONTEXT), "failed to swap context"},
+    {ERR_REASON(ASYNC_R_INIT_FAILED), "init failed"},
+    {ERR_REASON(ASYNC_R_INVALID_POOL_SIZE), "invalid pool size"},
+    {0, NULL}
+};
+
+#endif
+
+int ERR_load_ASYNC_strings(void)
+{
+#ifndef OPENSSL_NO_ERR
+
+    if (ERR_func_error_string(ASYNC_str_functs[0].error) == NULL) {
+        ERR_load_strings(0, ASYNC_str_functs);
+        ERR_load_strings(0, ASYNC_str_reasons);
+    }
+#endif
+    return 1;
+}
diff --git a/crypto/async/async_locl.h b/crypto/async/async_locl.h
new file mode 100644
index 000000000..f0ac05a3d
--- /dev/null
+++ b/crypto/async/async_locl.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2015-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * Must do this before including any header files, because on MacOS/X 
+ * includes  which includes 
+ */
+#if defined(__APPLE__) && defined(__MACH__) && !defined(_XOPEN_SOURCE)
+# define _XOPEN_SOURCE          /* Otherwise incomplete ucontext_t structure */
+# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#endif
+
+#if defined(_WIN32)
+# include 
+#endif
+
+#include 
+#include 
+
+typedef struct async_ctx_st async_ctx;
+typedef struct async_pool_st async_pool;
+
+#include "arch/async_win.h"
+#include "arch/async_posix.h"
+#include "arch/async_null.h"
+
+struct async_ctx_st {
+    async_fibre dispatcher;
+    ASYNC_JOB *currjob;
+    unsigned int blocked;
+};
+
+struct async_job_st {
+    async_fibre fibrectx;
+    int (*func) (void *);
+    void *funcargs;
+    int ret;
+    int status;
+    ASYNC_WAIT_CTX *waitctx;
+};
+
+struct fd_lookup_st {
+    const void *key;
+    OSSL_ASYNC_FD fd;
+    void *custom_data;
+    void (*cleanup)(ASYNC_WAIT_CTX *, const void *, OSSL_ASYNC_FD, void *);
+    int add;
+    int del;
+    struct fd_lookup_st *next;
+};
+
+struct async_wait_ctx_st {
+    struct fd_lookup_st *fds;
+    size_t numadd;
+    size_t numdel;
+};
+
+DEFINE_STACK_OF(ASYNC_JOB)
+
+struct async_pool_st {
+    STACK_OF(ASYNC_JOB) *jobs;
+    size_t curr_size;
+    size_t max_size;
+};
+
+void async_local_cleanup(void);
+void async_start_func(void);
+async_ctx *async_get_ctx(void);
+
+void async_wait_ctx_reset_counts(ASYNC_WAIT_CTX *ctx);
+
diff --git a/crypto/async/async_wait.c b/crypto/async/async_wait.c
new file mode 100644
index 000000000..e5ecaeb5e
--- /dev/null
+++ b/crypto/async/async_wait.c
@@ -0,0 +1,193 @@
+/*
+ * Copyright 2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/* This must be the first #include file */
+#include "async_locl.h"
+
+#include 
+
+ASYNC_WAIT_CTX *ASYNC_WAIT_CTX_new(void)
+{
+    return OPENSSL_zalloc(sizeof(ASYNC_WAIT_CTX));
+}
+
+void ASYNC_WAIT_CTX_free(ASYNC_WAIT_CTX *ctx)
+{
+    struct fd_lookup_st *curr;
+    struct fd_lookup_st *next;
+
+    if (ctx == NULL)
+        return;
+
+    curr = ctx->fds;
+    while (curr != NULL) {
+        if (!curr->del) {
+            /* Only try and cleanup if it hasn't been marked deleted */
+            if (curr->cleanup != NULL)
+                curr->cleanup(ctx, curr->key, curr->fd, curr->custom_data);
+        }
+        /* Always free the fd_lookup_st */
+        next = curr->next;
+        OPENSSL_free(curr);
+        curr = next;
+    }
+
+    OPENSSL_free(ctx);
+}
+int ASYNC_WAIT_CTX_set_wait_fd(ASYNC_WAIT_CTX *ctx, const void *key,
+                               OSSL_ASYNC_FD fd, void *custom_data,
+                               void (*cleanup)(ASYNC_WAIT_CTX *, const void *,
+                                               OSSL_ASYNC_FD, void *))
+{
+    struct fd_lookup_st *fdlookup;
+
+    fdlookup = OPENSSL_zalloc(sizeof *fdlookup);
+    if (fdlookup == NULL)
+        return 0;
+
+    fdlookup->key = key;
+    fdlookup->fd = fd;
+    fdlookup->custom_data = custom_data;
+    fdlookup->cleanup = cleanup;
+    fdlookup->add = 1;
+    fdlookup->next = ctx->fds;
+    ctx->fds = fdlookup;
+    ctx->numadd++;
+    return 1;
+}
+
+int ASYNC_WAIT_CTX_get_fd(ASYNC_WAIT_CTX *ctx, const void *key,
+                          OSSL_ASYNC_FD *fd, void **custom_data)
+{
+    struct fd_lookup_st *curr;
+
+    curr = ctx->fds;
+    while (curr != NULL) {
+        if (curr->del) {
+            /* This one has been marked deleted so do nothing */
+            curr = curr->next;
+            continue;
+        }
+        if (curr->key == key) {
+            *fd = curr->fd;
+            *custom_data = curr->custom_data;
+            return 1;
+        }
+        curr = curr->next;
+    }
+    return 0;
+}
+
+int ASYNC_WAIT_CTX_get_all_fds(ASYNC_WAIT_CTX *ctx, OSSL_ASYNC_FD *fd,
+                               size_t *numfds)
+{
+    struct fd_lookup_st *curr;
+
+    curr = ctx->fds;
+    *numfds = 0;
+    while (curr != NULL) {
+        if (curr->del) {
+            /* This one has been marked deleted so do nothing */
+            curr = curr->next;
+            continue;
+        }
+        if (fd != NULL) {
+            *fd = curr->fd;
+            fd++;
+        }
+        (*numfds)++;
+        curr = curr->next;
+    }
+    return 1;
+}
+
+int ASYNC_WAIT_CTX_get_changed_fds(ASYNC_WAIT_CTX *ctx, OSSL_ASYNC_FD *addfd,
+                                   size_t *numaddfds, OSSL_ASYNC_FD *delfd,
+                                   size_t *numdelfds)
+{
+    struct fd_lookup_st *curr;
+
+    *numaddfds = ctx->numadd;
+    *numdelfds = ctx->numdel;
+    if (addfd == NULL && delfd == NULL)
+        return 1;
+
+    curr = ctx->fds;
+
+    while (curr != NULL) {
+        /* We ignore fds that have been marked as both added and deleted */
+        if (curr->del && !curr->add && (delfd != NULL)) {
+            *delfd = curr->fd;
+            delfd++;
+        }
+        if (curr->add && !curr->del && (addfd != NULL)) {
+            *addfd = curr->fd;
+            addfd++;
+        }
+        curr = curr->next;
+    }
+
+    return 1;
+}
+
+int ASYNC_WAIT_CTX_clear_fd(ASYNC_WAIT_CTX *ctx, const void *key)
+{
+    struct fd_lookup_st *curr;
+
+    curr = ctx->fds;
+    while (curr != NULL) {
+        if (curr->del) {
+            /* This one has been marked deleted already so do nothing */
+            curr = curr->next;
+            continue;
+        }
+        if (curr->key == key) {
+            /*
+             * Mark it as deleted. We don't call cleanup if explicitly asked
+             * to clear an fd. We assume the caller is going to do that (if
+             * appropriate).
+             */
+            curr->del = 1;
+            ctx->numdel++;
+            return 1;
+        }
+        curr = curr->next;
+    }
+    return 0;
+}
+
+void async_wait_ctx_reset_counts(ASYNC_WAIT_CTX *ctx)
+{
+    struct fd_lookup_st *curr, *prev = NULL;
+
+    ctx->numadd = 0;
+    ctx->numdel = 0;
+
+    curr = ctx->fds;
+
+    while (curr != NULL) {
+        if (curr->del) {
+            if (prev == NULL)
+                ctx->fds = curr->next;
+            else
+                prev->next = curr->next;
+            OPENSSL_free(curr);
+            if (prev == NULL)
+                curr = ctx->fds;
+            else
+                curr = prev->next;
+            continue;
+        }
+        if (curr->add) {
+            curr->add = 0;
+        }
+        prev = curr;
+        curr = curr->next;
+    }
+}
diff --git a/crypto/async/build.info b/crypto/async/build.info
new file mode 100644
index 000000000..278e3e9f8
--- /dev/null
+++ b/crypto/async/build.info
@@ -0,0 +1,4 @@
+LIBS=../../libcrypto
+SOURCE[../../libcrypto]=\
+        async.c async_wait.c async_err.c arch/async_posix.c arch/async_win.c \
+        arch/async_null.c
diff --git a/crypto/bf/asm/bf-586.pl b/crypto/bf/asm/bf-586.pl
new file mode 100644
index 000000000..ebc24f48a
--- /dev/null
+++ b/crypto/bf/asm/bf-586.pl
@@ -0,0 +1,149 @@
+#! /usr/bin/env perl
+# Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+
+$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+push(@INC,"${dir}","${dir}../../perlasm");
+require "x86asm.pl";
+require "cbc.pl";
+
+$output = pop;
+open STDOUT,">$output";
+
+&asm_init($ARGV[0],"bf-586.pl",$ARGV[$#ARGV] eq "386");
+
+$BF_ROUNDS=16;
+$BF_OFF=($BF_ROUNDS+2)*4;
+$L="edi";
+$R="esi";
+$P="ebp";
+$tmp1="eax";
+$tmp2="ebx";
+$tmp3="ecx";
+$tmp4="edx";
+
+&BF_encrypt("BF_encrypt",1);
+&BF_encrypt("BF_decrypt",0);
+&cbc("BF_cbc_encrypt","BF_encrypt","BF_decrypt",1,4,5,3,-1,-1);
+&asm_finish();
+
+close STDOUT;
+
+sub BF_encrypt
+	{
+	local($name,$enc)=@_;
+
+	&function_begin_B($name,"");
+
+	&comment("");
+
+	&push("ebp");
+	&push("ebx");
+	&mov($tmp2,&wparam(0));
+	&mov($P,&wparam(1));
+	&push("esi");
+	&push("edi");
+
+	&comment("Load the 2 words");
+	&mov($L,&DWP(0,$tmp2,"",0));
+	&mov($R,&DWP(4,$tmp2,"",0));
+
+	&xor(	$tmp1,	$tmp1);
+
+	# encrypting part
+
+	if ($enc)
+		{
+		 &mov($tmp2,&DWP(0,$P,"",0));
+		&xor(	$tmp3,	$tmp3);
+
+		&xor($L,$tmp2);
+		for ($i=0; $i<$BF_ROUNDS; $i+=2)
+			{
+			&comment("");
+			&comment("Round $i");
+			&BF_ENCRYPT($i+1,$R,$L,$P,$tmp1,$tmp2,$tmp3,$tmp4,1);
+
+			&comment("");
+			&comment("Round ".sprintf("%d",$i+1));
+			&BF_ENCRYPT($i+2,$L,$R,$P,$tmp1,$tmp2,$tmp3,$tmp4,1);
+			}
+		# &mov($tmp1,&wparam(0)); In last loop
+		&mov($tmp4,&DWP(($BF_ROUNDS+1)*4,$P,"",0));
+		}
+	else
+		{
+		 &mov($tmp2,&DWP(($BF_ROUNDS+1)*4,$P,"",0));
+		&xor(	$tmp3,	$tmp3);
+
+		&xor($L,$tmp2);
+		for ($i=$BF_ROUNDS; $i>0; $i-=2)
+			{
+			&comment("");
+			&comment("Round $i");
+			&BF_ENCRYPT($i,$R,$L,$P,$tmp1,$tmp2,$tmp3,$tmp4,0);
+			&comment("");
+			&comment("Round ".sprintf("%d",$i-1));
+			&BF_ENCRYPT($i-1,$L,$R,$P,$tmp1,$tmp2,$tmp3,$tmp4,0);
+			}
+		# &mov($tmp1,&wparam(0)); In last loop
+		&mov($tmp4,&DWP(0,$P,"",0));
+		}
+
+	&xor($R,$tmp4);
+	&mov(&DWP(4,$tmp1,"",0),$L);
+
+	&mov(&DWP(0,$tmp1,"",0),$R);
+	&function_end($name);
+	}
+
+sub BF_ENCRYPT
+	{
+	local($i,$L,$R,$P,$tmp1,$tmp2,$tmp3,$tmp4,$enc)=@_;
+
+	&mov(	$tmp4,		&DWP(&n2a($i*4),$P,"",0)); # for next round
+
+	&mov(	$tmp2,		$R);
+	&xor(	$L,		$tmp4);
+
+	&shr(	$tmp2,		16);
+	&mov(	$tmp4,		$R);
+
+	&movb(	&LB($tmp1),	&HB($tmp2));	# A
+	&and(	$tmp2,		0xff);		# B
+
+	&movb(	&LB($tmp3),	&HB($tmp4));	# C
+	&and(	$tmp4,		0xff);		# D
+
+	&mov(	$tmp1,		&DWP(&n2a($BF_OFF+0x0000),$P,$tmp1,4));
+	&mov(	$tmp2,		&DWP(&n2a($BF_OFF+0x0400),$P,$tmp2,4));
+
+	&add(	$tmp2,		$tmp1);
+	&mov(	$tmp1,		&DWP(&n2a($BF_OFF+0x0800),$P,$tmp3,4));
+
+	&xor(	$tmp2,		$tmp1);
+	&mov(	$tmp4,		&DWP(&n2a($BF_OFF+0x0C00),$P,$tmp4,4));
+
+	&add(	$tmp2,		$tmp4);
+	if (($enc && ($i != 16)) || ((!$enc) && ($i != 1)))
+		{ &xor(	$tmp1,		$tmp1); }
+	else
+		{
+		&comment("Load parameter 0 ($i) enc=$enc");
+		&mov($tmp1,&wparam(0));
+		} # In last loop
+
+	&xor(	$L,		$tmp2);
+	# delay
+	}
+
+sub n2a
+	{
+	sprintf("%d",$_[0]);
+	}
+
diff --git a/crypto/bf/bf_cbc.c b/crypto/bf/bf_cbc.c
new file mode 100644
index 000000000..6ed62578a
--- /dev/null
+++ b/crypto/bf/bf_cbc.c
@@ -0,0 +1,86 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "bf_locl.h"
+
+void BF_cbc_encrypt(const unsigned char *in, unsigned char *out, long length,
+                    const BF_KEY *schedule, unsigned char *ivec, int encrypt)
+{
+    register BF_LONG tin0, tin1;
+    register BF_LONG tout0, tout1, xor0, xor1;
+    register long l = length;
+    BF_LONG tin[2];
+
+    if (encrypt) {
+        n2l(ivec, tout0);
+        n2l(ivec, tout1);
+        ivec -= 8;
+        for (l -= 8; l >= 0; l -= 8) {
+            n2l(in, tin0);
+            n2l(in, tin1);
+            tin0 ^= tout0;
+            tin1 ^= tout1;
+            tin[0] = tin0;
+            tin[1] = tin1;
+            BF_encrypt(tin, schedule);
+            tout0 = tin[0];
+            tout1 = tin[1];
+            l2n(tout0, out);
+            l2n(tout1, out);
+        }
+        if (l != -8) {
+            n2ln(in, tin0, tin1, l + 8);
+            tin0 ^= tout0;
+            tin1 ^= tout1;
+            tin[0] = tin0;
+            tin[1] = tin1;
+            BF_encrypt(tin, schedule);
+            tout0 = tin[0];
+            tout1 = tin[1];
+            l2n(tout0, out);
+            l2n(tout1, out);
+        }
+        l2n(tout0, ivec);
+        l2n(tout1, ivec);
+    } else {
+        n2l(ivec, xor0);
+        n2l(ivec, xor1);
+        ivec -= 8;
+        for (l -= 8; l >= 0; l -= 8) {
+            n2l(in, tin0);
+            n2l(in, tin1);
+            tin[0] = tin0;
+            tin[1] = tin1;
+            BF_decrypt(tin, schedule);
+            tout0 = tin[0] ^ xor0;
+            tout1 = tin[1] ^ xor1;
+            l2n(tout0, out);
+            l2n(tout1, out);
+            xor0 = tin0;
+            xor1 = tin1;
+        }
+        if (l != -8) {
+            n2l(in, tin0);
+            n2l(in, tin1);
+            tin[0] = tin0;
+            tin[1] = tin1;
+            BF_decrypt(tin, schedule);
+            tout0 = tin[0] ^ xor0;
+            tout1 = tin[1] ^ xor1;
+            l2nn(tout0, tout1, out, l + 8);
+            xor0 = tin0;
+            xor1 = tin1;
+        }
+        l2n(xor0, ivec);
+        l2n(xor1, ivec);
+    }
+    tin0 = tin1 = tout0 = tout1 = xor0 = xor1 = 0;
+    tin[0] = tin[1] = 0;
+}
diff --git a/crypto/bf/bf_cfb64.c b/crypto/bf/bf_cfb64.c
new file mode 100644
index 000000000..ce6e13b5a
--- /dev/null
+++ b/crypto/bf/bf_cfb64.c
@@ -0,0 +1,74 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "bf_locl.h"
+
+/*
+ * The input and output encrypted as though 64bit cfb mode is being used.
+ * The extra state information to record how much of the 64bit block we have
+ * used is contained in *num;
+ */
+
+void BF_cfb64_encrypt(const unsigned char *in, unsigned char *out,
+                      long length, const BF_KEY *schedule,
+                      unsigned char *ivec, int *num, int encrypt)
+{
+    register BF_LONG v0, v1, t;
+    register int n = *num;
+    register long l = length;
+    BF_LONG ti[2];
+    unsigned char *iv, c, cc;
+
+    iv = (unsigned char *)ivec;
+    if (encrypt) {
+        while (l--) {
+            if (n == 0) {
+                n2l(iv, v0);
+                ti[0] = v0;
+                n2l(iv, v1);
+                ti[1] = v1;
+                BF_encrypt((BF_LONG *)ti, schedule);
+                iv = (unsigned char *)ivec;
+                t = ti[0];
+                l2n(t, iv);
+                t = ti[1];
+                l2n(t, iv);
+                iv = (unsigned char *)ivec;
+            }
+            c = *(in++) ^ iv[n];
+            *(out++) = c;
+            iv[n] = c;
+            n = (n + 1) & 0x07;
+        }
+    } else {
+        while (l--) {
+            if (n == 0) {
+                n2l(iv, v0);
+                ti[0] = v0;
+                n2l(iv, v1);
+                ti[1] = v1;
+                BF_encrypt((BF_LONG *)ti, schedule);
+                iv = (unsigned char *)ivec;
+                t = ti[0];
+                l2n(t, iv);
+                t = ti[1];
+                l2n(t, iv);
+                iv = (unsigned char *)ivec;
+            }
+            cc = *(in++);
+            c = iv[n];
+            iv[n] = cc;
+            *(out++) = c ^ cc;
+            n = (n + 1) & 0x07;
+        }
+    }
+    v0 = v1 = ti[0] = ti[1] = t = c = cc = 0;
+    *num = n;
+}
diff --git a/crypto/bf/bf_ecb.c b/crypto/bf/bf_ecb.c
new file mode 100644
index 000000000..aa73540f3
--- /dev/null
+++ b/crypto/bf/bf_ecb.c
@@ -0,0 +1,43 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "bf_locl.h"
+#include 
+
+/*
+ * Blowfish as implemented from 'Blowfish: Springer-Verlag paper' (From
+ * LECTURE NOTES IN COMPUTER SCIENCE 809, FAST SOFTWARE ENCRYPTION, CAMBRIDGE
+ * SECURITY WORKSHOP, CAMBRIDGE, U.K., DECEMBER 9-11, 1993)
+ */
+
+const char *BF_options(void)
+{
+    return ("blowfish(ptr)");
+}
+
+void BF_ecb_encrypt(const unsigned char *in, unsigned char *out,
+                    const BF_KEY *key, int encrypt)
+{
+    BF_LONG l, d[2];
+
+    n2l(in, l);
+    d[0] = l;
+    n2l(in, l);
+    d[1] = l;
+    if (encrypt)
+        BF_encrypt(d, key);
+    else
+        BF_decrypt(d, key);
+    l = d[0];
+    l2n(l, out);
+    l = d[1];
+    l2n(l, out);
+    l = d[0] = d[1] = 0;
+}
diff --git a/crypto/bf/bf_enc.c b/crypto/bf/bf_enc.c
new file mode 100644
index 000000000..9f80c56d5
--- /dev/null
+++ b/crypto/bf/bf_enc.c
@@ -0,0 +1,179 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "bf_locl.h"
+
+/*
+ * Blowfish as implemented from 'Blowfish: Springer-Verlag paper' (From
+ * LECTURE NOTES IN COMPUTER SCIENCE 809, FAST SOFTWARE ENCRYPTION, CAMBRIDGE
+ * SECURITY WORKSHOP, CAMBRIDGE, U.K., DECEMBER 9-11, 1993)
+ */
+
+#if (BF_ROUNDS != 16) && (BF_ROUNDS != 20)
+# error If you set BF_ROUNDS to some value other than 16 or 20, you will have \
+to modify the code.
+#endif
+
+void BF_encrypt(BF_LONG *data, const BF_KEY *key)
+{
+    register BF_LONG l, r;
+    register const BF_LONG *p, *s;
+
+    p = key->P;
+    s = &(key->S[0]);
+    l = data[0];
+    r = data[1];
+
+    l ^= p[0];
+    BF_ENC(r, l, s, p[1]);
+    BF_ENC(l, r, s, p[2]);
+    BF_ENC(r, l, s, p[3]);
+    BF_ENC(l, r, s, p[4]);
+    BF_ENC(r, l, s, p[5]);
+    BF_ENC(l, r, s, p[6]);
+    BF_ENC(r, l, s, p[7]);
+    BF_ENC(l, r, s, p[8]);
+    BF_ENC(r, l, s, p[9]);
+    BF_ENC(l, r, s, p[10]);
+    BF_ENC(r, l, s, p[11]);
+    BF_ENC(l, r, s, p[12]);
+    BF_ENC(r, l, s, p[13]);
+    BF_ENC(l, r, s, p[14]);
+    BF_ENC(r, l, s, p[15]);
+    BF_ENC(l, r, s, p[16]);
+# if BF_ROUNDS == 20
+    BF_ENC(r, l, s, p[17]);
+    BF_ENC(l, r, s, p[18]);
+    BF_ENC(r, l, s, p[19]);
+    BF_ENC(l, r, s, p[20]);
+# endif
+    r ^= p[BF_ROUNDS + 1];
+
+    data[1] = l & 0xffffffffU;
+    data[0] = r & 0xffffffffU;
+}
+
+#ifndef BF_DEFAULT_OPTIONS
+
+void BF_decrypt(BF_LONG *data, const BF_KEY *key)
+{
+    register BF_LONG l, r;
+    register const BF_LONG *p, *s;
+
+    p = key->P;
+    s = &(key->S[0]);
+    l = data[0];
+    r = data[1];
+
+    l ^= p[BF_ROUNDS + 1];
+#  if BF_ROUNDS == 20
+    BF_ENC(r, l, s, p[20]);
+    BF_ENC(l, r, s, p[19]);
+    BF_ENC(r, l, s, p[18]);
+    BF_ENC(l, r, s, p[17]);
+#  endif
+    BF_ENC(r, l, s, p[16]);
+    BF_ENC(l, r, s, p[15]);
+    BF_ENC(r, l, s, p[14]);
+    BF_ENC(l, r, s, p[13]);
+    BF_ENC(r, l, s, p[12]);
+    BF_ENC(l, r, s, p[11]);
+    BF_ENC(r, l, s, p[10]);
+    BF_ENC(l, r, s, p[9]);
+    BF_ENC(r, l, s, p[8]);
+    BF_ENC(l, r, s, p[7]);
+    BF_ENC(r, l, s, p[6]);
+    BF_ENC(l, r, s, p[5]);
+    BF_ENC(r, l, s, p[4]);
+    BF_ENC(l, r, s, p[3]);
+    BF_ENC(r, l, s, p[2]);
+    BF_ENC(l, r, s, p[1]);
+    r ^= p[0];
+
+    data[1] = l & 0xffffffffU;
+    data[0] = r & 0xffffffffU;
+}
+
+void BF_cbc_encrypt(const unsigned char *in, unsigned char *out, long length,
+                    const BF_KEY *schedule, unsigned char *ivec, int encrypt)
+{
+    register BF_LONG tin0, tin1;
+    register BF_LONG tout0, tout1, xor0, xor1;
+    register long l = length;
+    BF_LONG tin[2];
+
+    if (encrypt) {
+        n2l(ivec, tout0);
+        n2l(ivec, tout1);
+        ivec -= 8;
+        for (l -= 8; l >= 0; l -= 8) {
+            n2l(in, tin0);
+            n2l(in, tin1);
+            tin0 ^= tout0;
+            tin1 ^= tout1;
+            tin[0] = tin0;
+            tin[1] = tin1;
+            BF_encrypt(tin, schedule);
+            tout0 = tin[0];
+            tout1 = tin[1];
+            l2n(tout0, out);
+            l2n(tout1, out);
+        }
+        if (l != -8) {
+            n2ln(in, tin0, tin1, l + 8);
+            tin0 ^= tout0;
+            tin1 ^= tout1;
+            tin[0] = tin0;
+            tin[1] = tin1;
+            BF_encrypt(tin, schedule);
+            tout0 = tin[0];
+            tout1 = tin[1];
+            l2n(tout0, out);
+            l2n(tout1, out);
+        }
+        l2n(tout0, ivec);
+        l2n(tout1, ivec);
+    } else {
+        n2l(ivec, xor0);
+        n2l(ivec, xor1);
+        ivec -= 8;
+        for (l -= 8; l >= 0; l -= 8) {
+            n2l(in, tin0);
+            n2l(in, tin1);
+            tin[0] = tin0;
+            tin[1] = tin1;
+            BF_decrypt(tin, schedule);
+            tout0 = tin[0] ^ xor0;
+            tout1 = tin[1] ^ xor1;
+            l2n(tout0, out);
+            l2n(tout1, out);
+            xor0 = tin0;
+            xor1 = tin1;
+        }
+        if (l != -8) {
+            n2l(in, tin0);
+            n2l(in, tin1);
+            tin[0] = tin0;
+            tin[1] = tin1;
+            BF_decrypt(tin, schedule);
+            tout0 = tin[0] ^ xor0;
+            tout1 = tin[1] ^ xor1;
+            l2nn(tout0, tout1, out, l + 8);
+            xor0 = tin0;
+            xor1 = tin1;
+        }
+        l2n(xor0, ivec);
+        l2n(xor1, ivec);
+    }
+    tin0 = tin1 = tout0 = tout1 = xor0 = xor1 = 0;
+    tin[0] = tin[1] = 0;
+}
+
+#endif
diff --git a/crypto/bf/bf_locl.h b/crypto/bf/bf_locl.h
new file mode 100644
index 000000000..7e5f92c26
--- /dev/null
+++ b/crypto/bf/bf_locl.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#ifndef HEADER_BF_LOCL_H
+# define HEADER_BF_LOCL_H
+# include 
+
+/* NOTE - c is not incremented as per n2l */
+# define n2ln(c,l1,l2,n) { \
+                        c+=n; \
+                        l1=l2=0; \
+                        switch (n) { \
+                        case 8: l2 =((unsigned long)(*(--(c))))    ; \
+                        case 7: l2|=((unsigned long)(*(--(c))))<< 8; \
+                        case 6: l2|=((unsigned long)(*(--(c))))<<16; \
+                        case 5: l2|=((unsigned long)(*(--(c))))<<24; \
+                        case 4: l1 =((unsigned long)(*(--(c))))    ; \
+                        case 3: l1|=((unsigned long)(*(--(c))))<< 8; \
+                        case 2: l1|=((unsigned long)(*(--(c))))<<16; \
+                        case 1: l1|=((unsigned long)(*(--(c))))<<24; \
+                                } \
+                        }
+
+/* NOTE - c is not incremented as per l2n */
+# define l2nn(l1,l2,c,n) { \
+                        c+=n; \
+                        switch (n) { \
+                        case 8: *(--(c))=(unsigned char)(((l2)    )&0xff); \
+                        case 7: *(--(c))=(unsigned char)(((l2)>> 8)&0xff); \
+                        case 6: *(--(c))=(unsigned char)(((l2)>>16)&0xff); \
+                        case 5: *(--(c))=(unsigned char)(((l2)>>24)&0xff); \
+                        case 4: *(--(c))=(unsigned char)(((l1)    )&0xff); \
+                        case 3: *(--(c))=(unsigned char)(((l1)>> 8)&0xff); \
+                        case 2: *(--(c))=(unsigned char)(((l1)>>16)&0xff); \
+                        case 1: *(--(c))=(unsigned char)(((l1)>>24)&0xff); \
+                                } \
+                        }
+
+# undef n2l
+# define n2l(c,l)        (l =((unsigned long)(*((c)++)))<<24L, \
+                         l|=((unsigned long)(*((c)++)))<<16L, \
+                         l|=((unsigned long)(*((c)++)))<< 8L, \
+                         l|=((unsigned long)(*((c)++))))
+
+# undef l2n
+# define l2n(l,c)        (*((c)++)=(unsigned char)(((l)>>24L)&0xff), \
+                         *((c)++)=(unsigned char)(((l)>>16L)&0xff), \
+                         *((c)++)=(unsigned char)(((l)>> 8L)&0xff), \
+                         *((c)++)=(unsigned char)(((l)     )&0xff))
+
+/*
+ * This is actually a big endian algorithm, the most significant byte is used
+ * to lookup array 0
+ */
+
+# define BF_ENC(LL,R,S,P) ( \
+        LL^=P, \
+        LL^=((( S[       ((R>>24)&0xff)] + \
+                S[0x0100+((R>>16)&0xff)])^ \
+                S[0x0200+((R>> 8)&0xff)])+ \
+                S[0x0300+((R    )&0xff)])&0xffffffffU \
+        )
+
+#endif
diff --git a/crypto/bf/bf_ofb64.c b/crypto/bf/bf_ofb64.c
new file mode 100644
index 000000000..6418217b3
--- /dev/null
+++ b/crypto/bf/bf_ofb64.c
@@ -0,0 +1,61 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "bf_locl.h"
+
+/*
+ * The input and output encrypted as though 64bit ofb mode is being used.
+ * The extra state information to record how much of the 64bit block we have
+ * used is contained in *num;
+ */
+void BF_ofb64_encrypt(const unsigned char *in, unsigned char *out,
+                      long length, const BF_KEY *schedule,
+                      unsigned char *ivec, int *num)
+{
+    register BF_LONG v0, v1, t;
+    register int n = *num;
+    register long l = length;
+    unsigned char d[8];
+    register char *dp;
+    BF_LONG ti[2];
+    unsigned char *iv;
+    int save = 0;
+
+    iv = (unsigned char *)ivec;
+    n2l(iv, v0);
+    n2l(iv, v1);
+    ti[0] = v0;
+    ti[1] = v1;
+    dp = (char *)d;
+    l2n(v0, dp);
+    l2n(v1, dp);
+    while (l--) {
+        if (n == 0) {
+            BF_encrypt((BF_LONG *)ti, schedule);
+            dp = (char *)d;
+            t = ti[0];
+            l2n(t, dp);
+            t = ti[1];
+            l2n(t, dp);
+            save++;
+        }
+        *(out++) = *(in++) ^ d[n];
+        n = (n + 1) & 0x07;
+    }
+    if (save) {
+        v0 = ti[0];
+        v1 = ti[1];
+        iv = (unsigned char *)ivec;
+        l2n(v0, iv);
+        l2n(v1, iv);
+    }
+    t = v0 = v1 = ti[0] = ti[1] = 0;
+    *num = n;
+}
diff --git a/crypto/bf/bf_pi.h b/crypto/bf/bf_pi.h
new file mode 100644
index 000000000..a054b03f8
--- /dev/null
+++ b/crypto/bf/bf_pi.h
@@ -0,0 +1,530 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+static const BF_KEY bf_init = {
+    {
+     0x243f6a88L, 0x85a308d3L, 0x13198a2eL, 0x03707344L,
+     0xa4093822L, 0x299f31d0L, 0x082efa98L, 0xec4e6c89L,
+     0x452821e6L, 0x38d01377L, 0xbe5466cfL, 0x34e90c6cL,
+     0xc0ac29b7L, 0xc97c50ddL, 0x3f84d5b5L, 0xb5470917L,
+     0x9216d5d9L, 0x8979fb1b}, {
+                                0xd1310ba6L, 0x98dfb5acL, 0x2ffd72dbL,
+                                0xd01adfb7L,
+                                0xb8e1afedL, 0x6a267e96L, 0xba7c9045L,
+                                0xf12c7f99L,
+                                0x24a19947L, 0xb3916cf7L, 0x0801f2e2L,
+                                0x858efc16L,
+                                0x636920d8L, 0x71574e69L, 0xa458fea3L,
+                                0xf4933d7eL,
+                                0x0d95748fL, 0x728eb658L, 0x718bcd58L,
+                                0x82154aeeL,
+                                0x7b54a41dL, 0xc25a59b5L, 0x9c30d539L,
+                                0x2af26013L,
+                                0xc5d1b023L, 0x286085f0L, 0xca417918L,
+                                0xb8db38efL,
+                                0x8e79dcb0L, 0x603a180eL, 0x6c9e0e8bL,
+                                0xb01e8a3eL,
+                                0xd71577c1L, 0xbd314b27L, 0x78af2fdaL,
+                                0x55605c60L,
+                                0xe65525f3L, 0xaa55ab94L, 0x57489862L,
+                                0x63e81440L,
+                                0x55ca396aL, 0x2aab10b6L, 0xb4cc5c34L,
+                                0x1141e8ceL,
+                                0xa15486afL, 0x7c72e993L, 0xb3ee1411L,
+                                0x636fbc2aL,
+                                0x2ba9c55dL, 0x741831f6L, 0xce5c3e16L,
+                                0x9b87931eL,
+                                0xafd6ba33L, 0x6c24cf5cL, 0x7a325381L,
+                                0x28958677L,
+                                0x3b8f4898L, 0x6b4bb9afL, 0xc4bfe81bL,
+                                0x66282193L,
+                                0x61d809ccL, 0xfb21a991L, 0x487cac60L,
+                                0x5dec8032L,
+                                0xef845d5dL, 0xe98575b1L, 0xdc262302L,
+                                0xeb651b88L,
+                                0x23893e81L, 0xd396acc5L, 0x0f6d6ff3L,
+                                0x83f44239L,
+                                0x2e0b4482L, 0xa4842004L, 0x69c8f04aL,
+                                0x9e1f9b5eL,
+                                0x21c66842L, 0xf6e96c9aL, 0x670c9c61L,
+                                0xabd388f0L,
+                                0x6a51a0d2L, 0xd8542f68L, 0x960fa728L,
+                                0xab5133a3L,
+                                0x6eef0b6cL, 0x137a3be4L, 0xba3bf050L,
+                                0x7efb2a98L,
+                                0xa1f1651dL, 0x39af0176L, 0x66ca593eL,
+                                0x82430e88L,
+                                0x8cee8619L, 0x456f9fb4L, 0x7d84a5c3L,
+                                0x3b8b5ebeL,
+                                0xe06f75d8L, 0x85c12073L, 0x401a449fL,
+                                0x56c16aa6L,
+                                0x4ed3aa62L, 0x363f7706L, 0x1bfedf72L,
+                                0x429b023dL,
+                                0x37d0d724L, 0xd00a1248L, 0xdb0fead3L,
+                                0x49f1c09bL,
+                                0x075372c9L, 0x80991b7bL, 0x25d479d8L,
+                                0xf6e8def7L,
+                                0xe3fe501aL, 0xb6794c3bL, 0x976ce0bdL,
+                                0x04c006baL,
+                                0xc1a94fb6L, 0x409f60c4L, 0x5e5c9ec2L,
+                                0x196a2463L,
+                                0x68fb6fafL, 0x3e6c53b5L, 0x1339b2ebL,
+                                0x3b52ec6fL,
+                                0x6dfc511fL, 0x9b30952cL, 0xcc814544L,
+                                0xaf5ebd09L,
+                                0xbee3d004L, 0xde334afdL, 0x660f2807L,
+                                0x192e4bb3L,
+                                0xc0cba857L, 0x45c8740fL, 0xd20b5f39L,
+                                0xb9d3fbdbL,
+                                0x5579c0bdL, 0x1a60320aL, 0xd6a100c6L,
+                                0x402c7279L,
+                                0x679f25feL, 0xfb1fa3ccL, 0x8ea5e9f8L,
+                                0xdb3222f8L,
+                                0x3c7516dfL, 0xfd616b15L, 0x2f501ec8L,
+                                0xad0552abL,
+                                0x323db5faL, 0xfd238760L, 0x53317b48L,
+                                0x3e00df82L,
+                                0x9e5c57bbL, 0xca6f8ca0L, 0x1a87562eL,
+                                0xdf1769dbL,
+                                0xd542a8f6L, 0x287effc3L, 0xac6732c6L,
+                                0x8c4f5573L,
+                                0x695b27b0L, 0xbbca58c8L, 0xe1ffa35dL,
+                                0xb8f011a0L,
+                                0x10fa3d98L, 0xfd2183b8L, 0x4afcb56cL,
+                                0x2dd1d35bL,
+                                0x9a53e479L, 0xb6f84565L, 0xd28e49bcL,
+                                0x4bfb9790L,
+                                0xe1ddf2daL, 0xa4cb7e33L, 0x62fb1341L,
+                                0xcee4c6e8L,
+                                0xef20cadaL, 0x36774c01L, 0xd07e9efeL,
+                                0x2bf11fb4L,
+                                0x95dbda4dL, 0xae909198L, 0xeaad8e71L,
+                                0x6b93d5a0L,
+                                0xd08ed1d0L, 0xafc725e0L, 0x8e3c5b2fL,
+                                0x8e7594b7L,
+                                0x8ff6e2fbL, 0xf2122b64L, 0x8888b812L,
+                                0x900df01cL,
+                                0x4fad5ea0L, 0x688fc31cL, 0xd1cff191L,
+                                0xb3a8c1adL,
+                                0x2f2f2218L, 0xbe0e1777L, 0xea752dfeL,
+                                0x8b021fa1L,
+                                0xe5a0cc0fL, 0xb56f74e8L, 0x18acf3d6L,
+                                0xce89e299L,
+                                0xb4a84fe0L, 0xfd13e0b7L, 0x7cc43b81L,
+                                0xd2ada8d9L,
+                                0x165fa266L, 0x80957705L, 0x93cc7314L,
+                                0x211a1477L,
+                                0xe6ad2065L, 0x77b5fa86L, 0xc75442f5L,
+                                0xfb9d35cfL,
+                                0xebcdaf0cL, 0x7b3e89a0L, 0xd6411bd3L,
+                                0xae1e7e49L,
+                                0x00250e2dL, 0x2071b35eL, 0x226800bbL,
+                                0x57b8e0afL,
+                                0x2464369bL, 0xf009b91eL, 0x5563911dL,
+                                0x59dfa6aaL,
+                                0x78c14389L, 0xd95a537fL, 0x207d5ba2L,
+                                0x02e5b9c5L,
+                                0x83260376L, 0x6295cfa9L, 0x11c81968L,
+                                0x4e734a41L,
+                                0xb3472dcaL, 0x7b14a94aL, 0x1b510052L,
+                                0x9a532915L,
+                                0xd60f573fL, 0xbc9bc6e4L, 0x2b60a476L,
+                                0x81e67400L,
+                                0x08ba6fb5L, 0x571be91fL, 0xf296ec6bL,
+                                0x2a0dd915L,
+                                0xb6636521L, 0xe7b9f9b6L, 0xff34052eL,
+                                0xc5855664L,
+                                0x53b02d5dL, 0xa99f8fa1L, 0x08ba4799L,
+                                0x6e85076aL,
+                                0x4b7a70e9L, 0xb5b32944L, 0xdb75092eL,
+                                0xc4192623L,
+                                0xad6ea6b0L, 0x49a7df7dL, 0x9cee60b8L,
+                                0x8fedb266L,
+                                0xecaa8c71L, 0x699a17ffL, 0x5664526cL,
+                                0xc2b19ee1L,
+                                0x193602a5L, 0x75094c29L, 0xa0591340L,
+                                0xe4183a3eL,
+                                0x3f54989aL, 0x5b429d65L, 0x6b8fe4d6L,
+                                0x99f73fd6L,
+                                0xa1d29c07L, 0xefe830f5L, 0x4d2d38e6L,
+                                0xf0255dc1L,
+                                0x4cdd2086L, 0x8470eb26L, 0x6382e9c6L,
+                                0x021ecc5eL,
+                                0x09686b3fL, 0x3ebaefc9L, 0x3c971814L,
+                                0x6b6a70a1L,
+                                0x687f3584L, 0x52a0e286L, 0xb79c5305L,
+                                0xaa500737L,
+                                0x3e07841cL, 0x7fdeae5cL, 0x8e7d44ecL,
+                                0x5716f2b8L,
+                                0xb03ada37L, 0xf0500c0dL, 0xf01c1f04L,
+                                0x0200b3ffL,
+                                0xae0cf51aL, 0x3cb574b2L, 0x25837a58L,
+                                0xdc0921bdL,
+                                0xd19113f9L, 0x7ca92ff6L, 0x94324773L,
+                                0x22f54701L,
+                                0x3ae5e581L, 0x37c2dadcL, 0xc8b57634L,
+                                0x9af3dda7L,
+                                0xa9446146L, 0x0fd0030eL, 0xecc8c73eL,
+                                0xa4751e41L,
+                                0xe238cd99L, 0x3bea0e2fL, 0x3280bba1L,
+                                0x183eb331L,
+                                0x4e548b38L, 0x4f6db908L, 0x6f420d03L,
+                                0xf60a04bfL,
+                                0x2cb81290L, 0x24977c79L, 0x5679b072L,
+                                0xbcaf89afL,
+                                0xde9a771fL, 0xd9930810L, 0xb38bae12L,
+                                0xdccf3f2eL,
+                                0x5512721fL, 0x2e6b7124L, 0x501adde6L,
+                                0x9f84cd87L,
+                                0x7a584718L, 0x7408da17L, 0xbc9f9abcL,
+                                0xe94b7d8cL,
+                                0xec7aec3aL, 0xdb851dfaL, 0x63094366L,
+                                0xc464c3d2L,
+                                0xef1c1847L, 0x3215d908L, 0xdd433b37L,
+                                0x24c2ba16L,
+                                0x12a14d43L, 0x2a65c451L, 0x50940002L,
+                                0x133ae4ddL,
+                                0x71dff89eL, 0x10314e55L, 0x81ac77d6L,
+                                0x5f11199bL,
+                                0x043556f1L, 0xd7a3c76bL, 0x3c11183bL,
+                                0x5924a509L,
+                                0xf28fe6edL, 0x97f1fbfaL, 0x9ebabf2cL,
+                                0x1e153c6eL,
+                                0x86e34570L, 0xeae96fb1L, 0x860e5e0aL,
+                                0x5a3e2ab3L,
+                                0x771fe71cL, 0x4e3d06faL, 0x2965dcb9L,
+                                0x99e71d0fL,
+                                0x803e89d6L, 0x5266c825L, 0x2e4cc978L,
+                                0x9c10b36aL,
+                                0xc6150ebaL, 0x94e2ea78L, 0xa5fc3c53L,
+                                0x1e0a2df4L,
+                                0xf2f74ea7L, 0x361d2b3dL, 0x1939260fL,
+                                0x19c27960L,
+                                0x5223a708L, 0xf71312b6L, 0xebadfe6eL,
+                                0xeac31f66L,
+                                0xe3bc4595L, 0xa67bc883L, 0xb17f37d1L,
+                                0x018cff28L,
+                                0xc332ddefL, 0xbe6c5aa5L, 0x65582185L,
+                                0x68ab9802L,
+                                0xeecea50fL, 0xdb2f953bL, 0x2aef7dadL,
+                                0x5b6e2f84L,
+                                0x1521b628L, 0x29076170L, 0xecdd4775L,
+                                0x619f1510L,
+                                0x13cca830L, 0xeb61bd96L, 0x0334fe1eL,
+                                0xaa0363cfL,
+                                0xb5735c90L, 0x4c70a239L, 0xd59e9e0bL,
+                                0xcbaade14L,
+                                0xeecc86bcL, 0x60622ca7L, 0x9cab5cabL,
+                                0xb2f3846eL,
+                                0x648b1eafL, 0x19bdf0caL, 0xa02369b9L,
+                                0x655abb50L,
+                                0x40685a32L, 0x3c2ab4b3L, 0x319ee9d5L,
+                                0xc021b8f7L,
+                                0x9b540b19L, 0x875fa099L, 0x95f7997eL,
+                                0x623d7da8L,
+                                0xf837889aL, 0x97e32d77L, 0x11ed935fL,
+                                0x16681281L,
+                                0x0e358829L, 0xc7e61fd6L, 0x96dedfa1L,
+                                0x7858ba99L,
+                                0x57f584a5L, 0x1b227263L, 0x9b83c3ffL,
+                                0x1ac24696L,
+                                0xcdb30aebL, 0x532e3054L, 0x8fd948e4L,
+                                0x6dbc3128L,
+                                0x58ebf2efL, 0x34c6ffeaL, 0xfe28ed61L,
+                                0xee7c3c73L,
+                                0x5d4a14d9L, 0xe864b7e3L, 0x42105d14L,
+                                0x203e13e0L,
+                                0x45eee2b6L, 0xa3aaabeaL, 0xdb6c4f15L,
+                                0xfacb4fd0L,
+                                0xc742f442L, 0xef6abbb5L, 0x654f3b1dL,
+                                0x41cd2105L,
+                                0xd81e799eL, 0x86854dc7L, 0xe44b476aL,
+                                0x3d816250L,
+                                0xcf62a1f2L, 0x5b8d2646L, 0xfc8883a0L,
+                                0xc1c7b6a3L,
+                                0x7f1524c3L, 0x69cb7492L, 0x47848a0bL,
+                                0x5692b285L,
+                                0x095bbf00L, 0xad19489dL, 0x1462b174L,
+                                0x23820e00L,
+                                0x58428d2aL, 0x0c55f5eaL, 0x1dadf43eL,
+                                0x233f7061L,
+                                0x3372f092L, 0x8d937e41L, 0xd65fecf1L,
+                                0x6c223bdbL,
+                                0x7cde3759L, 0xcbee7460L, 0x4085f2a7L,
+                                0xce77326eL,
+                                0xa6078084L, 0x19f8509eL, 0xe8efd855L,
+                                0x61d99735L,
+                                0xa969a7aaL, 0xc50c06c2L, 0x5a04abfcL,
+                                0x800bcadcL,
+                                0x9e447a2eL, 0xc3453484L, 0xfdd56705L,
+                                0x0e1e9ec9L,
+                                0xdb73dbd3L, 0x105588cdL, 0x675fda79L,
+                                0xe3674340L,
+                                0xc5c43465L, 0x713e38d8L, 0x3d28f89eL,
+                                0xf16dff20L,
+                                0x153e21e7L, 0x8fb03d4aL, 0xe6e39f2bL,
+                                0xdb83adf7L,
+                                0xe93d5a68L, 0x948140f7L, 0xf64c261cL,
+                                0x94692934L,
+                                0x411520f7L, 0x7602d4f7L, 0xbcf46b2eL,
+                                0xd4a20068L,
+                                0xd4082471L, 0x3320f46aL, 0x43b7d4b7L,
+                                0x500061afL,
+                                0x1e39f62eL, 0x97244546L, 0x14214f74L,
+                                0xbf8b8840L,
+                                0x4d95fc1dL, 0x96b591afL, 0x70f4ddd3L,
+                                0x66a02f45L,
+                                0xbfbc09ecL, 0x03bd9785L, 0x7fac6dd0L,
+                                0x31cb8504L,
+                                0x96eb27b3L, 0x55fd3941L, 0xda2547e6L,
+                                0xabca0a9aL,
+                                0x28507825L, 0x530429f4L, 0x0a2c86daL,
+                                0xe9b66dfbL,
+                                0x68dc1462L, 0xd7486900L, 0x680ec0a4L,
+                                0x27a18deeL,
+                                0x4f3ffea2L, 0xe887ad8cL, 0xb58ce006L,
+                                0x7af4d6b6L,
+                                0xaace1e7cL, 0xd3375fecL, 0xce78a399L,
+                                0x406b2a42L,
+                                0x20fe9e35L, 0xd9f385b9L, 0xee39d7abL,
+                                0x3b124e8bL,
+                                0x1dc9faf7L, 0x4b6d1856L, 0x26a36631L,
+                                0xeae397b2L,
+                                0x3a6efa74L, 0xdd5b4332L, 0x6841e7f7L,
+                                0xca7820fbL,
+                                0xfb0af54eL, 0xd8feb397L, 0x454056acL,
+                                0xba489527L,
+                                0x55533a3aL, 0x20838d87L, 0xfe6ba9b7L,
+                                0xd096954bL,
+                                0x55a867bcL, 0xa1159a58L, 0xcca92963L,
+                                0x99e1db33L,
+                                0xa62a4a56L, 0x3f3125f9L, 0x5ef47e1cL,
+                                0x9029317cL,
+                                0xfdf8e802L, 0x04272f70L, 0x80bb155cL,
+                                0x05282ce3L,
+                                0x95c11548L, 0xe4c66d22L, 0x48c1133fL,
+                                0xc70f86dcL,
+                                0x07f9c9eeL, 0x41041f0fL, 0x404779a4L,
+                                0x5d886e17L,
+                                0x325f51ebL, 0xd59bc0d1L, 0xf2bcc18fL,
+                                0x41113564L,
+                                0x257b7834L, 0x602a9c60L, 0xdff8e8a3L,
+                                0x1f636c1bL,
+                                0x0e12b4c2L, 0x02e1329eL, 0xaf664fd1L,
+                                0xcad18115L,
+                                0x6b2395e0L, 0x333e92e1L, 0x3b240b62L,
+                                0xeebeb922L,
+                                0x85b2a20eL, 0xe6ba0d99L, 0xde720c8cL,
+                                0x2da2f728L,
+                                0xd0127845L, 0x95b794fdL, 0x647d0862L,
+                                0xe7ccf5f0L,
+                                0x5449a36fL, 0x877d48faL, 0xc39dfd27L,
+                                0xf33e8d1eL,
+                                0x0a476341L, 0x992eff74L, 0x3a6f6eabL,
+                                0xf4f8fd37L,
+                                0xa812dc60L, 0xa1ebddf8L, 0x991be14cL,
+                                0xdb6e6b0dL,
+                                0xc67b5510L, 0x6d672c37L, 0x2765d43bL,
+                                0xdcd0e804L,
+                                0xf1290dc7L, 0xcc00ffa3L, 0xb5390f92L,
+                                0x690fed0bL,
+                                0x667b9ffbL, 0xcedb7d9cL, 0xa091cf0bL,
+                                0xd9155ea3L,
+                                0xbb132f88L, 0x515bad24L, 0x7b9479bfL,
+                                0x763bd6ebL,
+                                0x37392eb3L, 0xcc115979L, 0x8026e297L,
+                                0xf42e312dL,
+                                0x6842ada7L, 0xc66a2b3bL, 0x12754cccL,
+                                0x782ef11cL,
+                                0x6a124237L, 0xb79251e7L, 0x06a1bbe6L,
+                                0x4bfb6350L,
+                                0x1a6b1018L, 0x11caedfaL, 0x3d25bdd8L,
+                                0xe2e1c3c9L,
+                                0x44421659L, 0x0a121386L, 0xd90cec6eL,
+                                0xd5abea2aL,
+                                0x64af674eL, 0xda86a85fL, 0xbebfe988L,
+                                0x64e4c3feL,
+                                0x9dbc8057L, 0xf0f7c086L, 0x60787bf8L,
+                                0x6003604dL,
+                                0xd1fd8346L, 0xf6381fb0L, 0x7745ae04L,
+                                0xd736fcccL,
+                                0x83426b33L, 0xf01eab71L, 0xb0804187L,
+                                0x3c005e5fL,
+                                0x77a057beL, 0xbde8ae24L, 0x55464299L,
+                                0xbf582e61L,
+                                0x4e58f48fL, 0xf2ddfda2L, 0xf474ef38L,
+                                0x8789bdc2L,
+                                0x5366f9c3L, 0xc8b38e74L, 0xb475f255L,
+                                0x46fcd9b9L,
+                                0x7aeb2661L, 0x8b1ddf84L, 0x846a0e79L,
+                                0x915f95e2L,
+                                0x466e598eL, 0x20b45770L, 0x8cd55591L,
+                                0xc902de4cL,
+                                0xb90bace1L, 0xbb8205d0L, 0x11a86248L,
+                                0x7574a99eL,
+                                0xb77f19b6L, 0xe0a9dc09L, 0x662d09a1L,
+                                0xc4324633L,
+                                0xe85a1f02L, 0x09f0be8cL, 0x4a99a025L,
+                                0x1d6efe10L,
+                                0x1ab93d1dL, 0x0ba5a4dfL, 0xa186f20fL,
+                                0x2868f169L,
+                                0xdcb7da83L, 0x573906feL, 0xa1e2ce9bL,
+                                0x4fcd7f52L,
+                                0x50115e01L, 0xa70683faL, 0xa002b5c4L,
+                                0x0de6d027L,
+                                0x9af88c27L, 0x773f8641L, 0xc3604c06L,
+                                0x61a806b5L,
+                                0xf0177a28L, 0xc0f586e0L, 0x006058aaL,
+                                0x30dc7d62L,
+                                0x11e69ed7L, 0x2338ea63L, 0x53c2dd94L,
+                                0xc2c21634L,
+                                0xbbcbee56L, 0x90bcb6deL, 0xebfc7da1L,
+                                0xce591d76L,
+                                0x6f05e409L, 0x4b7c0188L, 0x39720a3dL,
+                                0x7c927c24L,
+                                0x86e3725fL, 0x724d9db9L, 0x1ac15bb4L,
+                                0xd39eb8fcL,
+                                0xed545578L, 0x08fca5b5L, 0xd83d7cd3L,
+                                0x4dad0fc4L,
+                                0x1e50ef5eL, 0xb161e6f8L, 0xa28514d9L,
+                                0x6c51133cL,
+                                0x6fd5c7e7L, 0x56e14ec4L, 0x362abfceL,
+                                0xddc6c837L,
+                                0xd79a3234L, 0x92638212L, 0x670efa8eL,
+                                0x406000e0L,
+                                0x3a39ce37L, 0xd3faf5cfL, 0xabc27737L,
+                                0x5ac52d1bL,
+                                0x5cb0679eL, 0x4fa33742L, 0xd3822740L,
+                                0x99bc9bbeL,
+                                0xd5118e9dL, 0xbf0f7315L, 0xd62d1c7eL,
+                                0xc700c47bL,
+                                0xb78c1b6bL, 0x21a19045L, 0xb26eb1beL,
+                                0x6a366eb4L,
+                                0x5748ab2fL, 0xbc946e79L, 0xc6a376d2L,
+                                0x6549c2c8L,
+                                0x530ff8eeL, 0x468dde7dL, 0xd5730a1dL,
+                                0x4cd04dc6L,
+                                0x2939bbdbL, 0xa9ba4650L, 0xac9526e8L,
+                                0xbe5ee304L,
+                                0xa1fad5f0L, 0x6a2d519aL, 0x63ef8ce2L,
+                                0x9a86ee22L,
+                                0xc089c2b8L, 0x43242ef6L, 0xa51e03aaL,
+                                0x9cf2d0a4L,
+                                0x83c061baL, 0x9be96a4dL, 0x8fe51550L,
+                                0xba645bd6L,
+                                0x2826a2f9L, 0xa73a3ae1L, 0x4ba99586L,
+                                0xef5562e9L,
+                                0xc72fefd3L, 0xf752f7daL, 0x3f046f69L,
+                                0x77fa0a59L,
+                                0x80e4a915L, 0x87b08601L, 0x9b09e6adL,
+                                0x3b3ee593L,
+                                0xe990fd5aL, 0x9e34d797L, 0x2cf0b7d9L,
+                                0x022b8b51L,
+                                0x96d5ac3aL, 0x017da67dL, 0xd1cf3ed6L,
+                                0x7c7d2d28L,
+                                0x1f9f25cfL, 0xadf2b89bL, 0x5ad6b472L,
+                                0x5a88f54cL,
+                                0xe029ac71L, 0xe019a5e6L, 0x47b0acfdL,
+                                0xed93fa9bL,
+                                0xe8d3c48dL, 0x283b57ccL, 0xf8d56629L,
+                                0x79132e28L,
+                                0x785f0191L, 0xed756055L, 0xf7960e44L,
+                                0xe3d35e8cL,
+                                0x15056dd4L, 0x88f46dbaL, 0x03a16125L,
+                                0x0564f0bdL,
+                                0xc3eb9e15L, 0x3c9057a2L, 0x97271aecL,
+                                0xa93a072aL,
+                                0x1b3f6d9bL, 0x1e6321f5L, 0xf59c66fbL,
+                                0x26dcf319L,
+                                0x7533d928L, 0xb155fdf5L, 0x03563482L,
+                                0x8aba3cbbL,
+                                0x28517711L, 0xc20ad9f8L, 0xabcc5167L,
+                                0xccad925fL,
+                                0x4de81751L, 0x3830dc8eL, 0x379d5862L,
+                                0x9320f991L,
+                                0xea7a90c2L, 0xfb3e7bceL, 0x5121ce64L,
+                                0x774fbe32L,
+                                0xa8b6e37eL, 0xc3293d46L, 0x48de5369L,
+                                0x6413e680L,
+                                0xa2ae0810L, 0xdd6db224L, 0x69852dfdL,
+                                0x09072166L,
+                                0xb39a460aL, 0x6445c0ddL, 0x586cdecfL,
+                                0x1c20c8aeL,
+                                0x5bbef7ddL, 0x1b588d40L, 0xccd2017fL,
+                                0x6bb4e3bbL,
+                                0xdda26a7eL, 0x3a59ff45L, 0x3e350a44L,
+                                0xbcb4cdd5L,
+                                0x72eacea8L, 0xfa6484bbL, 0x8d6612aeL,
+                                0xbf3c6f47L,
+                                0xd29be463L, 0x542f5d9eL, 0xaec2771bL,
+                                0xf64e6370L,
+                                0x740e0d8dL, 0xe75b1357L, 0xf8721671L,
+                                0xaf537d5dL,
+                                0x4040cb08L, 0x4eb4e2ccL, 0x34d2466aL,
+                                0x0115af84L,
+                                0xe1b00428L, 0x95983a1dL, 0x06b89fb4L,
+                                0xce6ea048L,
+                                0x6f3f3b82L, 0x3520ab82L, 0x011a1d4bL,
+                                0x277227f8L,
+                                0x611560b1L, 0xe7933fdcL, 0xbb3a792bL,
+                                0x344525bdL,
+                                0xa08839e1L, 0x51ce794bL, 0x2f32c9b7L,
+                                0xa01fbac9L,
+                                0xe01cc87eL, 0xbcc7d1f6L, 0xcf0111c3L,
+                                0xa1e8aac7L,
+                                0x1a908749L, 0xd44fbd9aL, 0xd0dadecbL,
+                                0xd50ada38L,
+                                0x0339c32aL, 0xc6913667L, 0x8df9317cL,
+                                0xe0b12b4fL,
+                                0xf79e59b7L, 0x43f5bb3aL, 0xf2d519ffL,
+                                0x27d9459cL,
+                                0xbf97222cL, 0x15e6fc2aL, 0x0f91fc71L,
+                                0x9b941525L,
+                                0xfae59361L, 0xceb69cebL, 0xc2a86459L,
+                                0x12baa8d1L,
+                                0xb6c1075eL, 0xe3056a0cL, 0x10d25065L,
+                                0xcb03a442L,
+                                0xe0ec6e0eL, 0x1698db3bL, 0x4c98a0beL,
+                                0x3278e964L,
+                                0x9f1f9532L, 0xe0d392dfL, 0xd3a0342bL,
+                                0x8971f21eL,
+                                0x1b0a7441L, 0x4ba3348cL, 0xc5be7120L,
+                                0xc37632d8L,
+                                0xdf359f8dL, 0x9b992f2eL, 0xe60b6f47L,
+                                0x0fe3f11dL,
+                                0xe54cda54L, 0x1edad891L, 0xce6279cfL,
+                                0xcd3e7e6fL,
+                                0x1618b166L, 0xfd2c1d05L, 0x848fd2c5L,
+                                0xf6fb2299L,
+                                0xf523f357L, 0xa6327623L, 0x93a83531L,
+                                0x56cccd02L,
+                                0xacf08162L, 0x5a75ebb5L, 0x6e163697L,
+                                0x88d273ccL,
+                                0xde966292L, 0x81b949d0L, 0x4c50901bL,
+                                0x71c65614L,
+                                0xe6c6c7bdL, 0x327a140aL, 0x45e1d006L,
+                                0xc3f27b9aL,
+                                0xc9aa53fdL, 0x62a80f00L, 0xbb25bfe2L,
+                                0x35bdd2f6L,
+                                0x71126905L, 0xb2040222L, 0xb6cbcf7cL,
+                                0xcd769c2bL,
+                                0x53113ec0L, 0x1640e3d3L, 0x38abbd60L,
+                                0x2547adf0L,
+                                0xba38209cL, 0xf746ce76L, 0x77afa1c5L,
+                                0x20756060L,
+                                0x85cbfe4eL, 0x8ae88dd8L, 0x7aaaf9b0L,
+                                0x4cf9aa7eL,
+                                0x1948c25cL, 0x02fb8a8cL, 0x01c36ae4L,
+                                0xd6ebe1f9L,
+                                0x90d4f869L, 0xa65cdea0L, 0x3f09252dL,
+                                0xc208e69fL,
+                                0xb74e6132L, 0xce77e25bL, 0x578fdfe3L,
+                                0x3ac372e6L,
+                                }
+};
diff --git a/crypto/bf/bf_skey.c b/crypto/bf/bf_skey.c
new file mode 100644
index 000000000..a4903a2a7
--- /dev/null
+++ b/crypto/bf/bf_skey.c
@@ -0,0 +1,67 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include 
+#include "bf_locl.h"
+#include "bf_pi.h"
+
+void BF_set_key(BF_KEY *key, int len, const unsigned char *data)
+{
+    int i;
+    BF_LONG *p, ri, in[2];
+    const unsigned char *d, *end;
+
+    memcpy(key, &bf_init, sizeof(BF_KEY));
+    p = key->P;
+
+    if (len > ((BF_ROUNDS + 2) * 4))
+        len = (BF_ROUNDS + 2) * 4;
+
+    d = data;
+    end = &(data[len]);
+    for (i = 0; i < (BF_ROUNDS + 2); i++) {
+        ri = *(d++);
+        if (d >= end)
+            d = data;
+
+        ri <<= 8;
+        ri |= *(d++);
+        if (d >= end)
+            d = data;
+
+        ri <<= 8;
+        ri |= *(d++);
+        if (d >= end)
+            d = data;
+
+        ri <<= 8;
+        ri |= *(d++);
+        if (d >= end)
+            d = data;
+
+        p[i] ^= ri;
+    }
+
+    in[0] = 0L;
+    in[1] = 0L;
+    for (i = 0; i < (BF_ROUNDS + 2); i += 2) {
+        BF_encrypt(in, key);
+        p[i] = in[0];
+        p[i + 1] = in[1];
+    }
+
+    p = key->S;
+    for (i = 0; i < 4 * 256; i += 2) {
+        BF_encrypt(in, key);
+        p[i] = in[0];
+        p[i + 1] = in[1];
+    }
+}
diff --git a/crypto/bf/build.info b/crypto/bf/build.info
new file mode 100644
index 000000000..37a004ea5
--- /dev/null
+++ b/crypto/bf/build.info
@@ -0,0 +1,6 @@
+LIBS=../../libcrypto
+SOURCE[../../libcrypto]=bf_skey.c bf_ecb.c bf_cfb64.c bf_ofb64.c \
+        {- $target{bf_asm_src} -}
+
+GENERATE[bf-586.s]=asm/bf-586.pl $(PERLASM_SCHEME) $(CFLAGS) $(LIB_CFLAGS) $(PROCESSOR)
+DEPEND[bf-586.s]=../perlasm/x86asm.pl ../perlasm/cbc.pl
diff --git a/crypto/bio/b_addr.c b/crypto/bio/b_addr.c
new file mode 100644
index 000000000..0f1900db9
--- /dev/null
+++ b/crypto/bio/b_addr.c
@@ -0,0 +1,897 @@
+/*
+ * Copyright 2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+
+#include "bio_lcl.h"
+#include 
+
+#ifndef OPENSSL_NO_SOCK
+#include 
+#include 
+#include 
+#include 
+
+#ifdef _HPUX_SOURCE
+static const char *ossl_hstrerror(int herr)
+{
+    switch (herr) {
+    case -1:
+        return strerror(errno);
+    case 0:
+        return "No error";
+    case HOST_NOT_FOUND:
+        return "Host not found";
+    case NO_DATA:                /* NO_ADDRESS is a synonym */
+        return "No data";
+    case NO_RECOVERY:
+        return "Non recoverable error";
+    case TRY_AGAIN:
+        return "Try again";
+    default:
+        break;
+    }
+    return "unknown error";
+}
+# define hstrerror(e) ossl_hstrerror(e)
+#endif
+
+CRYPTO_RWLOCK *bio_lookup_lock;
+static CRYPTO_ONCE bio_lookup_init = CRYPTO_ONCE_STATIC_INIT;
+
+/*
+ * Throughout this file and bio_lcl.h, the existence of the macro
+ * AI_PASSIVE is used to detect the availability of struct addrinfo,
+ * getnameinfo() and getaddrinfo().  If that macro doesn't exist,
+ * we use our own implementation instead, using gethostbyname,
+ * getservbyname and a few other.
+ */
+
+/**********************************************************************
+ *
+ * Address structure
+ *
+ */
+
+BIO_ADDR *BIO_ADDR_new(void)
+{
+    BIO_ADDR *ret = OPENSSL_zalloc(sizeof(*ret));
+
+    if (ret == NULL) {
+        BIOerr(BIO_F_BIO_ADDR_NEW, ERR_R_MALLOC_FAILURE);
+        return NULL;
+    }
+
+    ret->sa.sa_family = AF_UNSPEC;
+    return ret;
+}
+
+void BIO_ADDR_free(BIO_ADDR *ap)
+{
+    OPENSSL_free(ap);
+}
+
+void BIO_ADDR_clear(BIO_ADDR *ap)
+{
+    memset(ap, 0, sizeof(*ap));
+    ap->sa.sa_family = AF_UNSPEC;
+}
+
+/*
+ * BIO_ADDR_make - non-public routine to fill a BIO_ADDR with the contents
+ * of a struct sockaddr.
+ */
+int BIO_ADDR_make(BIO_ADDR *ap, const struct sockaddr *sa)
+{
+    if (sa->sa_family == AF_INET) {
+        ap->s_in = *(const struct sockaddr_in *)sa;
+        return 1;
+    }
+#ifdef AF_INET6
+    if (sa->sa_family == AF_INET6) {
+        ap->s_in6 = *(const struct sockaddr_in6 *)sa;
+        return 1;
+    }
+#endif
+#ifdef AF_UNIX
+    if (ap->sa.sa_family == AF_UNIX) {
+        ap->s_un = *(const struct sockaddr_un *)sa;
+        return 1;
+    }
+#endif
+
+    return 0;
+}
+
+int BIO_ADDR_rawmake(BIO_ADDR *ap, int family,
+                     const void *where, size_t wherelen,
+                     unsigned short port)
+{
+#ifdef AF_UNIX
+    if (family == AF_UNIX) {
+        if (wherelen + 1 > sizeof(ap->s_un.sun_path))
+            return 0;
+        memset(&ap->s_un, 0, sizeof(ap->s_un));
+        ap->s_un.sun_family = family;
+        strncpy(ap->s_un.sun_path, where, sizeof(ap->s_un.sun_path) - 1);
+        return 1;
+    }
+#endif
+    if (family == AF_INET) {
+        if (wherelen != sizeof(struct in_addr))
+            return 0;
+        memset(&ap->s_in, 0, sizeof(ap->s_in));
+        ap->s_in.sin_family = family;
+        ap->s_in.sin_port = port;
+        ap->s_in.sin_addr = *(struct in_addr *)where;
+        return 1;
+    }
+#ifdef AF_INET6
+    if (family == AF_INET6) {
+        if (wherelen != sizeof(struct in6_addr))
+            return 0;
+        memset(&ap->s_in6, 0, sizeof(ap->s_in6));
+        ap->s_in6.sin6_family = family;
+        ap->s_in6.sin6_port = port;
+        ap->s_in6.sin6_addr = *(struct in6_addr *)where;
+        return 1;
+    }
+#endif
+
+    return 0;
+}
+
+int BIO_ADDR_family(const BIO_ADDR *ap)
+{
+    return ap->sa.sa_family;
+}
+
+int BIO_ADDR_rawaddress(const BIO_ADDR *ap, void *p, size_t *l)
+{
+    size_t len = 0;
+    const void *addrptr = NULL;
+
+    if (ap->sa.sa_family == AF_INET) {
+        len = sizeof(ap->s_in.sin_addr);
+        addrptr = &ap->s_in.sin_addr;
+    }
+#ifdef AF_INET6
+    else if (ap->sa.sa_family == AF_INET6) {
+        len = sizeof(ap->s_in6.sin6_addr);
+        addrptr = &ap->s_in6.sin6_addr;
+    }
+#endif
+#ifdef AF_UNIX
+    else if (ap->sa.sa_family == AF_UNIX) {
+        len = strlen(ap->s_un.sun_path);
+        addrptr = &ap->s_un.sun_path;
+    }
+#endif
+
+    if (addrptr == NULL)
+        return 0;
+
+    if (p != NULL) {
+        memcpy(p, addrptr, len);
+    }
+    if (l != NULL)
+        *l = len;
+
+    return 1;
+}
+
+unsigned short BIO_ADDR_rawport(const BIO_ADDR *ap)
+{
+    if (ap->sa.sa_family == AF_INET)
+        return ap->s_in.sin_port;
+#ifdef AF_INET6
+    if (ap->sa.sa_family == AF_INET6)
+        return ap->s_in6.sin6_port;
+#endif
+    return 0;
+}
+
+/*-
+ * addr_strings - helper function to get host and service names
+ * @ap: the BIO_ADDR that has the input info
+ * @numeric: 0 if actual names should be returned, 1 if the numeric
+ * representation should be returned.
+ * @hostname: a pointer to a pointer to a memory area to store the
+ * host name or numeric representation.  Unused if NULL.
+ * @service: a pointer to a pointer to a memory area to store the
+ * service name or numeric representation.  Unused if NULL.
+ *
+ * The return value is 0 on failure, with the error code in the error
+ * stack, and 1 on success.
+ */
+static int addr_strings(const BIO_ADDR *ap, int numeric,
+                        char **hostname, char **service)
+{
+    if (BIO_sock_init() != 1)
+        return 0;
+
+    if (1) {
+#ifdef AI_PASSIVE
+        int ret = 0;
+        char host[NI_MAXHOST] = "", serv[NI_MAXSERV] = "";
+        int flags = 0;
+
+        if (numeric)
+            flags |= NI_NUMERICHOST | NI_NUMERICSERV;
+
+        if ((ret = getnameinfo(BIO_ADDR_sockaddr(ap),
+                               BIO_ADDR_sockaddr_size(ap),
+                               host, sizeof(host), serv, sizeof(serv),
+                               flags)) != 0) {
+# ifdef EAI_SYSTEM
+            if (ret == EAI_SYSTEM) {
+                SYSerr(SYS_F_GETNAMEINFO, get_last_socket_error());
+                BIOerr(BIO_F_ADDR_STRINGS, ERR_R_SYS_LIB);
+            } else
+# endif
+            {
+                BIOerr(BIO_F_ADDR_STRINGS, ERR_R_SYS_LIB);
+                ERR_add_error_data(1, gai_strerror(ret));
+            }
+            return 0;
+        }
+
+        /* VMS getnameinfo() has a bug, it doesn't fill in serv, which
+         * leaves it with whatever garbage that happens to be there.
+         * However, we initialise serv with the empty string (serv[0]
+         * is therefore NUL), so it gets real easy to detect when things
+         * didn't go the way one might expect.
+         */
+        if (serv[0] == '\0') {
+            BIO_snprintf(serv, sizeof(serv), "%d",
+                         ntohs(BIO_ADDR_rawport(ap)));
+        }
+
+        if (hostname != NULL)
+            *hostname = OPENSSL_strdup(host);
+        if (service != NULL)
+            *service = OPENSSL_strdup(serv);
+    } else {
+#endif
+        if (hostname != NULL)
+            *hostname = OPENSSL_strdup(inet_ntoa(ap->s_in.sin_addr));
+        if (service != NULL) {
+            char serv[6];        /* port is 16 bits => max 5 decimal digits */
+            BIO_snprintf(serv, sizeof(serv), "%d", ntohs(ap->s_in.sin_port));
+            *service = OPENSSL_strdup(serv);
+        }
+    }
+
+    if ((hostname != NULL && *hostname == NULL)
+            || (service != NULL && *service == NULL)) {
+        if (hostname != NULL) {
+            OPENSSL_free(*hostname);
+            *hostname = NULL;
+        }
+        if (service != NULL) {
+            OPENSSL_free(*service);
+            *service = NULL;
+        }
+        BIOerr(BIO_F_ADDR_STRINGS, ERR_R_MALLOC_FAILURE);
+        return 0;
+    }
+
+    return 1;
+}
+
+char *BIO_ADDR_hostname_string(const BIO_ADDR *ap, int numeric)
+{
+    char *hostname = NULL;
+
+    if (addr_strings(ap, numeric, &hostname, NULL))
+        return hostname;
+
+    return NULL;
+}
+
+char *BIO_ADDR_service_string(const BIO_ADDR *ap, int numeric)
+{
+    char *service = NULL;
+
+    if (addr_strings(ap, numeric, NULL, &service))
+        return service;
+
+    return NULL;
+}
+
+char *BIO_ADDR_path_string(const BIO_ADDR *ap)
+{
+#ifdef AF_UNIX
+    if (ap->sa.sa_family == AF_UNIX)
+        return OPENSSL_strdup(ap->s_un.sun_path);
+#endif
+    return NULL;
+}
+
+/*
+ * BIO_ADDR_sockaddr - non-public routine to return the struct sockaddr
+ * for a given BIO_ADDR.  In reality, this is simply a type safe cast.
+ * The returned struct sockaddr is const, so it can't be tampered with.
+ */
+const struct sockaddr *BIO_ADDR_sockaddr(const BIO_ADDR *ap)
+{
+    return &(ap->sa);
+}
+
+/*
+ * BIO_ADDR_sockaddr_noconst - non-public function that does the same
+ * as BIO_ADDR_sockaddr, but returns a non-const.  USE WITH CARE, as
+ * it allows you to tamper with the data (and thereby the contents
+ * of the input BIO_ADDR).
+ */
+struct sockaddr *BIO_ADDR_sockaddr_noconst(BIO_ADDR *ap)
+{
+    return &(ap->sa);
+}
+
+/*
+ * BIO_ADDR_sockaddr_size - non-public function that returns the size
+ * of the struct sockaddr the BIO_ADDR is using.  If the protocol family
+ * isn't set or is something other than AF_INET, AF_INET6 or AF_UNIX,
+ * the size of the BIO_ADDR type is returned.
+ */
+socklen_t BIO_ADDR_sockaddr_size(const BIO_ADDR *ap)
+{
+    if (ap->sa.sa_family == AF_INET)
+        return sizeof(ap->s_in);
+#ifdef AF_INET6
+    if (ap->sa.sa_family == AF_INET6)
+        return sizeof(ap->s_in6);
+#endif
+#ifdef AF_UNIX
+    if (ap->sa.sa_family == AF_UNIX)
+        return sizeof(ap->s_un);
+#endif
+    return sizeof(*ap);
+}
+
+/**********************************************************************
+ *
+ * Address info database
+ *
+ */
+
+const BIO_ADDRINFO *BIO_ADDRINFO_next(const BIO_ADDRINFO *bai)
+{
+    if (bai != NULL)
+        return bai->bai_next;
+    return NULL;
+}
+
+int BIO_ADDRINFO_family(const BIO_ADDRINFO *bai)
+{
+    if (bai != NULL)
+        return bai->bai_family;
+    return 0;
+}
+
+int BIO_ADDRINFO_socktype(const BIO_ADDRINFO *bai)
+{
+    if (bai != NULL)
+        return bai->bai_socktype;
+    return 0;
+}
+
+int BIO_ADDRINFO_protocol(const BIO_ADDRINFO *bai)
+{
+    if (bai != NULL) {
+        if (bai->bai_protocol != 0)
+            return bai->bai_protocol;
+
+#ifdef AF_UNIX
+        if (bai->bai_family == AF_UNIX)
+            return 0;
+#endif
+
+        switch (bai->bai_socktype) {
+        case SOCK_STREAM:
+            return IPPROTO_TCP;
+        case SOCK_DGRAM:
+            return IPPROTO_UDP;
+        default:
+            break;
+        }
+    }
+    return 0;
+}
+
+/*
+ * BIO_ADDRINFO_sockaddr_size - non-public function that returns the size
+ * of the struct sockaddr inside the BIO_ADDRINFO.
+ */
+socklen_t BIO_ADDRINFO_sockaddr_size(const BIO_ADDRINFO *bai)
+{
+    if (bai != NULL)
+        return bai->bai_addrlen;
+    return 0;
+}
+
+/*
+ * BIO_ADDRINFO_sockaddr - non-public function that returns bai_addr
+ * as the struct sockaddr it is.
+ */
+const struct sockaddr *BIO_ADDRINFO_sockaddr(const BIO_ADDRINFO *bai)
+{
+    if (bai != NULL)
+        return bai->bai_addr;
+    return NULL;
+}
+
+const BIO_ADDR *BIO_ADDRINFO_address(const BIO_ADDRINFO *bai)
+{
+    if (bai != NULL)
+        return (BIO_ADDR *)bai->bai_addr;
+    return NULL;
+}
+
+void BIO_ADDRINFO_free(BIO_ADDRINFO *bai)
+{
+    if (bai == NULL)
+        return;
+
+#ifdef AI_PASSIVE
+# ifdef AF_UNIX
+#  define _cond bai->bai_family != AF_UNIX
+# else
+#  define _cond 1
+# endif
+    if (_cond) {
+        freeaddrinfo(bai);
+        return;
+    }
+#endif
+
+    /* Free manually when we know that addrinfo_wrap() was used.
+     * See further comment above addrinfo_wrap()
+     */
+    while (bai != NULL) {
+        BIO_ADDRINFO *next = bai->bai_next;
+        OPENSSL_free(bai->bai_addr);
+        OPENSSL_free(bai);
+        bai = next;
+    }
+}
+
+/**********************************************************************
+ *
+ * Service functions
+ *
+ */
+
+/*-
+ * The specs in hostserv can take these forms:
+ *
+ * host:service         => *host = "host", *service = "service"
+ * host:*               => *host = "host", *service = NULL
+ * host:                => *host = "host", *service = NULL
+ * :service             => *host = NULL, *service = "service"
+ * *:service            => *host = NULL, *service = "service"
+ *
+ * in case no : is present in the string, the result depends on
+ * hostserv_prio, as follows:
+ *
+ * when hostserv_prio == BIO_PARSE_PRIO_HOST
+ * host                 => *host = "host", *service untouched
+ *
+ * when hostserv_prio == BIO_PARSE_PRIO_SERV
+ * service              => *host untouched, *service = "service"
+ *
+ */
+int BIO_parse_hostserv(const char *hostserv, char **host, char **service,
+                       enum BIO_hostserv_priorities hostserv_prio)
+{
+    const char *h = NULL; size_t hl = 0;
+    const char *p = NULL; size_t pl = 0;
+
+    if (*hostserv == '[') {
+        if ((p = strchr(hostserv, ']')) == NULL)
+            goto spec_err;
+        h = hostserv + 1;
+        hl = p - h;
+        p++;
+        if (*p == '\0')
+            p = NULL;
+        else if (*p != ':')
+            goto spec_err;
+        else {
+            p++;
+            pl = strlen(p);
+        }
+    } else {
+        const char *p2 = strrchr(hostserv, ':');
+        p = strchr(hostserv, ':');
+
+        /*-
+         * Check for more than one colon.  There are three possible
+         * interpretations:
+         * 1. IPv6 address with port number, last colon being separator.
+         * 2. IPv6 address only.
+         * 3. IPv6 address only if hostserv_prio == BIO_PARSE_PRIO_HOST,
+         *    IPv6 address and port number if hostserv_prio == BIO_PARSE_PRIO_SERV
+         * Because of this ambiguity, we currently choose to make it an
+         * error.
+         */
+        if (p != p2)
+            goto amb_err;
+
+        if (p != NULL) {
+            h = hostserv;
+            hl = p - h;
+            p++;
+            pl = strlen(p);
+        } else if (hostserv_prio == BIO_PARSE_PRIO_HOST) {
+            h = hostserv;
+            hl = strlen(h);
+        } else {
+            p = hostserv;
+            pl = strlen(p);
+        }
+    }
+
+    if (p != NULL && strchr(p, ':'))
+        goto spec_err;
+
+    if (h != NULL && host != NULL) {
+        if (hl == 0
+            || (hl == 1 && h[0] == '*')) {
+            *host = NULL;
+        } else {
+            *host = OPENSSL_strndup(h, hl);
+            if (*host == NULL)
+                goto memerr;
+        }
+    }
+    if (p != NULL && service != NULL) {
+        if (pl == 0
+            || (pl == 1 && p[0] == '*')) {
+            *service = NULL;
+        } else {
+            *service = OPENSSL_strndup(p, pl);
+            if (*service == NULL)
+                goto memerr;
+        }
+    }
+
+    return 1;
+ amb_err:
+    BIOerr(BIO_F_BIO_PARSE_HOSTSERV, BIO_R_AMBIGUOUS_HOST_OR_SERVICE);
+    return 0;
+ spec_err:
+    BIOerr(BIO_F_BIO_PARSE_HOSTSERV, BIO_R_MALFORMED_HOST_OR_SERVICE);
+    return 0;
+ memerr:
+    BIOerr(BIO_F_BIO_PARSE_HOSTSERV, ERR_R_MALLOC_FAILURE);
+    return 0;
+}
+
+/* addrinfo_wrap is used to build our own addrinfo "chain".
+ * (it has only one entry, so calling it a chain may be a stretch)
+ * It should ONLY be called when getaddrinfo() and friends
+ * aren't available, OR when dealing with a non IP protocol
+ * family, such as AF_UNIX
+ *
+ * the return value is 1 on success, or 0 on failure, which
+ * only happens if a memory allocation error occurred.
+ */
+static int addrinfo_wrap(int family, int socktype,
+                         const void *where, size_t wherelen,
+                         unsigned short port,
+                         BIO_ADDRINFO **bai)
+{
+    OPENSSL_assert(bai != NULL);
+
+    *bai = OPENSSL_zalloc(sizeof(**bai));
+    if (*bai == NULL)
+        return 0;
+
+    (*bai)->bai_family = family;
+    (*bai)->bai_socktype = socktype;
+    if (socktype == SOCK_STREAM)
+        (*bai)->bai_protocol = IPPROTO_TCP;
+    if (socktype == SOCK_DGRAM)
+        (*bai)->bai_protocol = IPPROTO_UDP;
+#ifdef AF_UNIX
+    if (family == AF_UNIX)
+        (*bai)->bai_protocol = 0;
+#endif
+    {
+        /* Magic: We know that BIO_ADDR_sockaddr_noconst is really
+           just an advanced cast of BIO_ADDR* to struct sockaddr *
+           by the power of union, so while it may seem that we're
+           creating a memory leak here, we are not.  It will be
+           all right. */
+        BIO_ADDR *addr = BIO_ADDR_new();
+        if (addr != NULL) {
+            BIO_ADDR_rawmake(addr, family, where, wherelen, port);
+            (*bai)->bai_addr = BIO_ADDR_sockaddr_noconst(addr);
+        }
+    }
+    (*bai)->bai_next = NULL;
+    if ((*bai)->bai_addr == NULL) {
+        BIO_ADDRINFO_free(*bai);
+        *bai = NULL;
+        return 0;
+    }
+    return 1;
+}
+
+DEFINE_RUN_ONCE_STATIC(do_bio_lookup_init)
+{
+    OPENSSL_init_crypto(0, NULL);
+    bio_lookup_lock = CRYPTO_THREAD_lock_new();
+    return bio_lookup_lock != NULL;
+}
+
+/*-
+ * BIO_lookup - look up the node and service you want to connect to.
+ * @node: the node you want to connect to.
+ * @service: the service you want to connect to.
+ * @lookup_type: declare intent with the result, client or server.
+ * @family: the address family you want to use.  Use AF_UNSPEC for any, or
+ *  AF_INET, AF_INET6 or AF_UNIX.
+ * @socktype: The socket type you want to use.  Can be SOCK_STREAM, SOCK_DGRAM
+ *  or 0 for all.
+ * @res: Storage place for the resulting list of returned addresses
+ *
+ * This will do a lookup of the node and service that you want to connect to.
+ * It returns a linked list of different addresses you can try to connect to.
+ *
+ * When no longer needed you should call BIO_ADDRINFO_free() to free the result.
+ *
+ * The return value is 1 on success or 0 in case of error.
+ */
+int BIO_lookup(const char *host, const char *service,
+               enum BIO_lookup_type lookup_type,
+               int family, int socktype, BIO_ADDRINFO **res)
+{
+    int ret = 0;                 /* Assume failure */
+
+    switch(family) {
+    case AF_INET:
+#ifdef AF_INET6
+    case AF_INET6:
+#endif
+#ifdef AF_UNIX
+    case AF_UNIX:
+#endif
+#ifdef AF_UNSPEC
+    case AF_UNSPEC:
+#endif
+        break;
+    default:
+        BIOerr(BIO_F_BIO_LOOKUP, BIO_R_UNSUPPORTED_PROTOCOL_FAMILY);
+        return 0;
+    }
+
+#ifdef AF_UNIX
+    if (family == AF_UNIX) {
+        if (addrinfo_wrap(family, socktype, host, strlen(host), 0, res))
+            return 1;
+        else
+            BIOerr(BIO_F_BIO_LOOKUP, ERR_R_MALLOC_FAILURE);
+        return 0;
+    }
+#endif
+
+    if (BIO_sock_init() != 1)
+        return 0;
+
+    if (1) {
+        int gai_ret = 0;
+#ifdef AI_PASSIVE
+        struct addrinfo hints;
+        memset(&hints, 0, sizeof hints);
+
+        hints.ai_family = family;
+        hints.ai_socktype = socktype;
+
+        if (lookup_type == BIO_LOOKUP_SERVER)
+            hints.ai_flags |= AI_PASSIVE;
+
+        /* Note that |res| SHOULD be a 'struct addrinfo **' thanks to
+         * macro magic in bio_lcl.h
+         */
+        switch ((gai_ret = getaddrinfo(host, service, &hints, res))) {
+# ifdef EAI_SYSTEM
+        case EAI_SYSTEM:
+            SYSerr(SYS_F_GETADDRINFO, get_last_socket_error());
+            BIOerr(BIO_F_BIO_LOOKUP, ERR_R_SYS_LIB);
+            break;
+# endif
+        case 0:
+            ret = 1;             /* Success */
+            break;
+        default:
+            BIOerr(BIO_F_BIO_LOOKUP, ERR_R_SYS_LIB);
+            ERR_add_error_data(1, gai_strerror(gai_ret));
+            break;
+        }
+    } else {
+#endif
+        const struct hostent *he;
+/*
+ * Because struct hostent is defined for 32-bit pointers only with
+ * VMS C, we need to make sure that '&he_fallback_address' and
+ * '&he_fallback_addresses' are 32-bit pointers
+ */
+#if defined(OPENSSL_SYS_VMS) && defined(__DECC)
+# pragma pointer_size save
+# pragma pointer_size 32
+#endif
+        /* Windows doesn't seem to have in_addr_t */
+#ifdef OPENSSL_SYS_WINDOWS
+        static uint32_t he_fallback_address;
+        static const char *he_fallback_addresses[] =
+            { (char *)&he_fallback_address, NULL };
+#else
+        static in_addr_t he_fallback_address;
+        static const char *he_fallback_addresses[] =
+            { (char *)&he_fallback_address, NULL };
+#endif
+        static const struct hostent he_fallback =
+            { NULL, NULL, AF_INET, sizeof(he_fallback_address),
+              (char **)&he_fallback_addresses };
+#if defined(OPENSSL_SYS_VMS) && defined(__DECC)
+# pragma pointer_size restore
+#endif
+
+        struct servent *se;
+        /* Apparently, on WIN64, s_proto and s_port have traded places... */
+#ifdef _WIN64
+        struct servent se_fallback = { NULL, NULL, NULL, 0 };
+#else
+        struct servent se_fallback = { NULL, NULL, 0, NULL };
+#endif
+
+        if (!RUN_ONCE(&bio_lookup_init, do_bio_lookup_init)) {
+            BIOerr(BIO_F_BIO_LOOKUP, ERR_R_MALLOC_FAILURE);
+            ret = 0;
+            goto err;
+        }
+
+        CRYPTO_THREAD_write_lock(bio_lookup_lock);
+        he_fallback_address = INADDR_ANY;
+        if (host == NULL) {
+            he = &he_fallback;
+            switch(lookup_type) {
+            case BIO_LOOKUP_CLIENT:
+                he_fallback_address = INADDR_LOOPBACK;
+                break;
+            case BIO_LOOKUP_SERVER:
+                he_fallback_address = INADDR_ANY;
+                break;
+            default:
+                OPENSSL_assert(("We forgot to handle a lookup type!" == 0));
+                break;
+            }
+        } else {
+            he = gethostbyname(host);
+
+            if (he == NULL) {
+#ifndef OPENSSL_SYS_WINDOWS
+                BIOerr(BIO_F_BIO_LOOKUP, ERR_R_SYS_LIB);
+                ERR_add_error_data(1, hstrerror(h_errno));
+#else
+                SYSerr(SYS_F_GETHOSTBYNAME, WSAGetLastError());
+#endif
+                ret = 0;
+                goto err;
+            }
+        }
+
+        if (service == NULL) {
+            se_fallback.s_port = 0;
+            se_fallback.s_proto = NULL;
+            se = &se_fallback;
+        } else {
+            char *endp = NULL;
+            long portnum = strtol(service, &endp, 10);
+
+/*
+ * Because struct servent is defined for 32-bit pointers only with
+ * VMS C, we need to make sure that 'proto' is a 32-bit pointer.
+ */
+#if defined(OPENSSL_SYS_VMS) && defined(__DECC)
+# pragma pointer_size save
+# pragma pointer_size 32
+#endif
+            char *proto = NULL;
+#if defined(OPENSSL_SYS_VMS) && defined(__DECC)
+# pragma pointer_size restore
+#endif
+
+            switch (socktype) {
+            case SOCK_STREAM:
+                proto = "tcp";
+                break;
+            case SOCK_DGRAM:
+                proto = "udp";
+                break;
+            }
+
+            if (endp != service && *endp == '\0'
+                    && portnum > 0 && portnum < 65536) {
+                se_fallback.s_port = htons(portnum);
+                se_fallback.s_proto = proto;
+                se = &se_fallback;
+            } else if (endp == service) {
+                se = getservbyname(service, proto);
+
+                if (se == NULL) {
+#ifndef OPENSSL_SYS_WINDOWS
+                    BIOerr(BIO_F_BIO_LOOKUP, ERR_R_SYS_LIB);
+                    ERR_add_error_data(1, hstrerror(h_errno));
+#else
+                    SYSerr(SYS_F_GETSERVBYNAME, WSAGetLastError());
+#endif
+                    goto err;
+                }
+            } else {
+                BIOerr(BIO_F_BIO_LOOKUP, BIO_R_MALFORMED_HOST_OR_SERVICE);
+                goto err;
+            }
+        }
+
+        *res = NULL;
+
+        {
+/*
+ * Because hostent::h_addr_list is an array of 32-bit pointers with VMS C,
+ * we must make sure our iterator designates the same element type, hence
+ * the pointer size dance.
+ */
+#if defined(OPENSSL_SYS_VMS) && defined(__DECC)
+# pragma pointer_size save
+# pragma pointer_size 32
+#endif
+            char **addrlistp;
+#if defined(OPENSSL_SYS_VMS) && defined(__DECC)
+# pragma pointer_size restore
+#endif
+            size_t addresses;
+            BIO_ADDRINFO *tmp_bai = NULL;
+
+            /* The easiest way to create a linked list from an
+               array is to start from the back */
+            for(addrlistp = he->h_addr_list; *addrlistp != NULL;
+                addrlistp++)
+                ;
+
+            for(addresses = addrlistp - he->h_addr_list;
+                addrlistp--, addresses-- > 0; ) {
+                if (!addrinfo_wrap(he->h_addrtype, socktype,
+                                   *addrlistp, he->h_length,
+                                   se->s_port, &tmp_bai))
+                    goto addrinfo_malloc_err;
+                tmp_bai->bai_next = *res;
+                *res = tmp_bai;
+                continue;
+             addrinfo_malloc_err:
+                BIO_ADDRINFO_free(*res);
+                *res = NULL;
+                BIOerr(BIO_F_BIO_LOOKUP, ERR_R_MALLOC_FAILURE);
+                ret = 0;
+                goto err;
+            }
+
+            ret = 1;
+        }
+     err:
+        CRYPTO_THREAD_unlock(bio_lookup_lock);
+    }
+
+    return ret;
+}
+
+#endif /* OPENSSL_NO_SOCK */
diff --git a/crypto/bio/b_dump.c b/crypto/bio/b_dump.c
new file mode 100644
index 000000000..a27954fa3
--- /dev/null
+++ b/crypto/bio/b_dump.c
@@ -0,0 +1,158 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * Stolen from tjh's ssl/ssl_trc.c stuff.
+ */
+
+#include 
+#include "bio_lcl.h"
+
+#define TRUNCATE
+#define DUMP_WIDTH      16
+#define DUMP_WIDTH_LESS_INDENT(i) (DUMP_WIDTH-((i-(i>6?6:i)+3)/4))
+
+int BIO_dump_cb(int (*cb) (const void *data, size_t len, void *u),
+                void *u, const char *s, int len)
+{
+    return BIO_dump_indent_cb(cb, u, s, len, 0);
+}
+
+int BIO_dump_indent_cb(int (*cb) (const void *data, size_t len, void *u),
+                       void *u, const char *s, int len, int indent)
+{
+    int ret = 0;
+    char buf[288 + 1], tmp[20], str[128 + 1];
+    int i, j, rows, trc;
+    unsigned char ch;
+    int dump_width;
+
+    trc = 0;
+
+#ifdef TRUNCATE
+    for (; (len > 0) && ((s[len - 1] == ' ') || (s[len - 1] == '\0')); len--)
+        trc++;
+#endif
+
+    if (indent < 0)
+        indent = 0;
+    if (indent) {
+        if (indent > 128)
+            indent = 128;
+        memset(str, ' ', indent);
+    }
+    str[indent] = '\0';
+
+    dump_width = DUMP_WIDTH_LESS_INDENT(indent);
+    rows = (len / dump_width);
+    if ((rows * dump_width) < len)
+        rows++;
+    for (i = 0; i < rows; i++) {
+        OPENSSL_strlcpy(buf, str, sizeof buf);
+        BIO_snprintf(tmp, sizeof tmp, "%04x - ", i * dump_width);
+        OPENSSL_strlcat(buf, tmp, sizeof buf);
+        for (j = 0; j < dump_width; j++) {
+            if (((i * dump_width) + j) >= len) {
+                OPENSSL_strlcat(buf, "   ", sizeof buf);
+            } else {
+                ch = ((unsigned char)*(s + i * dump_width + j)) & 0xff;
+                BIO_snprintf(tmp, sizeof tmp, "%02x%c", ch,
+                             j == 7 ? '-' : ' ');
+                OPENSSL_strlcat(buf, tmp, sizeof buf);
+            }
+        }
+        OPENSSL_strlcat(buf, "  ", sizeof buf);
+        for (j = 0; j < dump_width; j++) {
+            if (((i * dump_width) + j) >= len)
+                break;
+            ch = ((unsigned char)*(s + i * dump_width + j)) & 0xff;
+#ifndef CHARSET_EBCDIC
+            BIO_snprintf(tmp, sizeof tmp, "%c",
+                         ((ch >= ' ') && (ch <= '~')) ? ch : '.');
+#else
+            BIO_snprintf(tmp, sizeof tmp, "%c",
+                         ((ch >= os_toascii[' ']) && (ch <= os_toascii['~']))
+                         ? os_toebcdic[ch]
+                         : '.');
+#endif
+            OPENSSL_strlcat(buf, tmp, sizeof buf);
+        }
+        OPENSSL_strlcat(buf, "\n", sizeof buf);
+        /*
+         * if this is the last call then update the ddt_dump thing so that we
+         * will move the selection point in the debug window
+         */
+        ret += cb((void *)buf, strlen(buf), u);
+    }
+#ifdef TRUNCATE
+    if (trc > 0) {
+        BIO_snprintf(buf, sizeof buf, "%s%04x - \n", str,
+                     len + trc);
+        ret += cb((void *)buf, strlen(buf), u);
+    }
+#endif
+    return (ret);
+}
+
+#ifndef OPENSSL_NO_STDIO
+static int write_fp(const void *data, size_t len, void *fp)
+{
+    return UP_fwrite(data, len, 1, fp);
+}
+
+int BIO_dump_fp(FILE *fp, const char *s, int len)
+{
+    return BIO_dump_cb(write_fp, fp, s, len);
+}
+
+int BIO_dump_indent_fp(FILE *fp, const char *s, int len, int indent)
+{
+    return BIO_dump_indent_cb(write_fp, fp, s, len, indent);
+}
+#endif
+
+static int write_bio(const void *data, size_t len, void *bp)
+{
+    return BIO_write((BIO *)bp, (const char *)data, len);
+}
+
+int BIO_dump(BIO *bp, const char *s, int len)
+{
+    return BIO_dump_cb(write_bio, bp, s, len);
+}
+
+int BIO_dump_indent(BIO *bp, const char *s, int len, int indent)
+{
+    return BIO_dump_indent_cb(write_bio, bp, s, len, indent);
+}
+
+int BIO_hex_string(BIO *out, int indent, int width, unsigned char *data,
+                   int datalen)
+{
+    int i, j = 0;
+
+    if (datalen < 1)
+        return 1;
+
+    for (i = 0; i < datalen - 1; i++) {
+        if (i && !j)
+            BIO_printf(out, "%*s", indent, "");
+
+        BIO_printf(out, "%02X:", data[i]);
+
+        j = (j + 1) % width;
+        if (!j)
+            BIO_printf(out, "\n");
+    }
+
+    if (i && !j)
+        BIO_printf(out, "%*s", indent, "");
+    BIO_printf(out, "%02X", data[datalen - 1]);
+    return 1;
+}
diff --git a/crypto/bio/b_print.c b/crypto/bio/b_print.c
new file mode 100644
index 000000000..e91ab6de2
--- /dev/null
+++ b/crypto/bio/b_print.c
@@ -0,0 +1,944 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include 
+#include "internal/numbers.h"
+#include "internal/cryptlib.h"
+#ifndef NO_SYS_TYPES_H
+# include 
+#endif
+#include          /* To get BN_LLONG properly defined */
+#include 
+
+#if defined(BN_LLONG) || defined(SIXTY_FOUR_BIT)
+# ifndef HAVE_LONG_LONG
+#  define HAVE_LONG_LONG 1
+# endif
+#endif
+
+/*
+ * Copyright Patrick Powell 1995
+ * This code is based on code written by Patrick Powell 
+ * It may be used for any purpose as long as this notice remains intact
+ * on all source code distributions.
+ */
+
+#ifdef HAVE_LONG_DOUBLE
+# define LDOUBLE long double
+#else
+# define LDOUBLE double
+#endif
+
+#ifdef HAVE_LONG_LONG
+# if defined(_WIN32) && !defined(__GNUC__)
+#  define LLONG __int64
+# else
+#  define LLONG long long
+# endif
+#else
+# define LLONG long
+#endif
+
+static int fmtstr(char **, char **, size_t *, size_t *,
+                  const char *, int, int, int);
+static int fmtint(char **, char **, size_t *, size_t *,
+                  LLONG, int, int, int, int);
+static int fmtfp(char **, char **, size_t *, size_t *,
+                 LDOUBLE, int, int, int, int);
+static int doapr_outch(char **, char **, size_t *, size_t *, int);
+static int _dopr(char **sbuffer, char **buffer,
+                 size_t *maxlen, size_t *retlen, int *truncated,
+                 const char *format, va_list args);
+
+/* format read states */
+#define DP_S_DEFAULT    0
+#define DP_S_FLAGS      1
+#define DP_S_MIN        2
+#define DP_S_DOT        3
+#define DP_S_MAX        4
+#define DP_S_MOD        5
+#define DP_S_CONV       6
+#define DP_S_DONE       7
+
+/* format flags - Bits */
+/* left-aligned padding */
+#define DP_F_MINUS      (1 << 0)
+/* print an explicit '+' for a value with positive sign */
+#define DP_F_PLUS       (1 << 1)
+/* print an explicit ' ' for a value with positive sign */
+#define DP_F_SPACE      (1 << 2)
+/* print 0/0x prefix for octal/hex and decimal point for floating point */
+#define DP_F_NUM        (1 << 3)
+/* print leading zeroes */
+#define DP_F_ZERO       (1 << 4)
+/* print HEX in UPPPERcase */
+#define DP_F_UP         (1 << 5)
+/* treat value as unsigned */
+#define DP_F_UNSIGNED   (1 << 6)
+
+/* conversion flags */
+#define DP_C_SHORT      1
+#define DP_C_LONG       2
+#define DP_C_LDOUBLE    3
+#define DP_C_LLONG      4
+
+/* Floating point formats */
+#define F_FORMAT        0
+#define E_FORMAT        1
+#define G_FORMAT        2
+
+/* some handy macros */
+#define char_to_int(p) (p - '0')
+#define OSSL_MAX(p,q) ((p >= q) ? p : q)
+
+static int
+_dopr(char **sbuffer,
+      char **buffer,
+      size_t *maxlen,
+      size_t *retlen, int *truncated, const char *format, va_list args)
+{
+    char ch;
+    LLONG value;
+    LDOUBLE fvalue;
+    char *strvalue;
+    int min;
+    int max;
+    int state;
+    int flags;
+    int cflags;
+    size_t currlen;
+
+    state = DP_S_DEFAULT;
+    flags = currlen = cflags = min = 0;
+    max = -1;
+    ch = *format++;
+
+    while (state != DP_S_DONE) {
+        if (ch == '\0' || (buffer == NULL && currlen >= *maxlen))
+            state = DP_S_DONE;
+
+        switch (state) {
+        case DP_S_DEFAULT:
+            if (ch == '%')
+                state = DP_S_FLAGS;
+            else
+                if(!doapr_outch(sbuffer, buffer, &currlen, maxlen, ch))
+                    return 0;
+            ch = *format++;
+            break;
+        case DP_S_FLAGS:
+            switch (ch) {
+            case '-':
+                flags |= DP_F_MINUS;
+                ch = *format++;
+                break;
+            case '+':
+                flags |= DP_F_PLUS;
+                ch = *format++;
+                break;
+            case ' ':
+                flags |= DP_F_SPACE;
+                ch = *format++;
+                break;
+            case '#':
+                flags |= DP_F_NUM;
+                ch = *format++;
+                break;
+            case '0':
+                flags |= DP_F_ZERO;
+                ch = *format++;
+                break;
+            default:
+                state = DP_S_MIN;
+                break;
+            }
+            break;
+        case DP_S_MIN:
+            if (isdigit((unsigned char)ch)) {
+                min = 10 * min + char_to_int(ch);
+                ch = *format++;
+            } else if (ch == '*') {
+                min = va_arg(args, int);
+                ch = *format++;
+                state = DP_S_DOT;
+            } else
+                state = DP_S_DOT;
+            break;
+        case DP_S_DOT:
+            if (ch == '.') {
+                state = DP_S_MAX;
+                ch = *format++;
+            } else
+                state = DP_S_MOD;
+            break;
+        case DP_S_MAX:
+            if (isdigit((unsigned char)ch)) {
+                if (max < 0)
+                    max = 0;
+                max = 10 * max + char_to_int(ch);
+                ch = *format++;
+            } else if (ch == '*') {
+                max = va_arg(args, int);
+                ch = *format++;
+                state = DP_S_MOD;
+            } else
+                state = DP_S_MOD;
+            break;
+        case DP_S_MOD:
+            switch (ch) {
+            case 'h':
+                cflags = DP_C_SHORT;
+                ch = *format++;
+                break;
+            case 'l':
+                if (*format == 'l') {
+                    cflags = DP_C_LLONG;
+                    format++;
+                } else
+                    cflags = DP_C_LONG;
+                ch = *format++;
+                break;
+            case 'q':
+                cflags = DP_C_LLONG;
+                ch = *format++;
+                break;
+            case 'L':
+                cflags = DP_C_LDOUBLE;
+                ch = *format++;
+                break;
+            default:
+                break;
+            }
+            state = DP_S_CONV;
+            break;
+        case DP_S_CONV:
+            switch (ch) {
+            case 'd':
+            case 'i':
+                switch (cflags) {
+                case DP_C_SHORT:
+                    value = (short int)va_arg(args, int);
+                    break;
+                case DP_C_LONG:
+                    value = va_arg(args, long int);
+                    break;
+                case DP_C_LLONG:
+                    value = va_arg(args, LLONG);
+                    break;
+                default:
+                    value = va_arg(args, int);
+                    break;
+                }
+                if (!fmtint(sbuffer, buffer, &currlen, maxlen, value, 10, min,
+                            max, flags))
+                    return 0;
+                break;
+            case 'X':
+                flags |= DP_F_UP;
+                /* FALLTHROUGH */
+            case 'x':
+            case 'o':
+            case 'u':
+                flags |= DP_F_UNSIGNED;
+                switch (cflags) {
+                case DP_C_SHORT:
+                    value = (unsigned short int)va_arg(args, unsigned int);
+                    break;
+                case DP_C_LONG:
+                    value = (LLONG) va_arg(args, unsigned long int);
+                    break;
+                case DP_C_LLONG:
+                    value = va_arg(args, unsigned LLONG);
+                    break;
+                default:
+                    value = (LLONG) va_arg(args, unsigned int);
+                    break;
+                }
+                if (!fmtint(sbuffer, buffer, &currlen, maxlen, value,
+                            ch == 'o' ? 8 : (ch == 'u' ? 10 : 16),
+                            min, max, flags))
+                    return 0;
+                break;
+            case 'f':
+                if (cflags == DP_C_LDOUBLE)
+                    fvalue = va_arg(args, LDOUBLE);
+                else
+                    fvalue = va_arg(args, double);
+                if (!fmtfp(sbuffer, buffer, &currlen, maxlen, fvalue, min, max,
+                           flags, F_FORMAT))
+                    return 0;
+                break;
+            case 'E':
+                flags |= DP_F_UP;
+            case 'e':
+                if (cflags == DP_C_LDOUBLE)
+                    fvalue = va_arg(args, LDOUBLE);
+                else
+                    fvalue = va_arg(args, double);
+                if (!fmtfp(sbuffer, buffer, &currlen, maxlen, fvalue, min, max,
+                           flags, E_FORMAT))
+                    return 0;
+                break;
+            case 'G':
+                flags |= DP_F_UP;
+            case 'g':
+                if (cflags == DP_C_LDOUBLE)
+                    fvalue = va_arg(args, LDOUBLE);
+                else
+                    fvalue = va_arg(args, double);
+                if (!fmtfp(sbuffer, buffer, &currlen, maxlen, fvalue, min, max,
+                           flags, G_FORMAT))
+                    return 0;
+                break;
+            case 'c':
+                if(!doapr_outch(sbuffer, buffer, &currlen, maxlen,
+                            va_arg(args, int)))
+                    return 0;
+                break;
+            case 's':
+                strvalue = va_arg(args, char *);
+                if (max < 0) {
+                    if (buffer)
+                        max = INT_MAX;
+                    else
+                        max = *maxlen;
+                }
+                if (!fmtstr(sbuffer, buffer, &currlen, maxlen, strvalue,
+                            flags, min, max))
+                    return 0;
+                break;
+            case 'p':
+                value = (size_t)va_arg(args, void *);
+                if (!fmtint(sbuffer, buffer, &currlen, maxlen,
+                            value, 16, min, max, flags | DP_F_NUM))
+                    return 0;
+                break;
+            case 'n':          /* XXX */
+                if (cflags == DP_C_SHORT) {
+                    short int *num;
+                    num = va_arg(args, short int *);
+                    *num = currlen;
+                } else if (cflags == DP_C_LONG) { /* XXX */
+                    long int *num;
+                    num = va_arg(args, long int *);
+                    *num = (long int)currlen;
+                } else if (cflags == DP_C_LLONG) { /* XXX */
+                    LLONG *num;
+                    num = va_arg(args, LLONG *);
+                    *num = (LLONG) currlen;
+                } else {
+                    int *num;
+                    num = va_arg(args, int *);
+                    *num = currlen;
+                }
+                break;
+            case '%':
+                if(!doapr_outch(sbuffer, buffer, &currlen, maxlen, ch))
+                    return 0;
+                break;
+            case 'w':
+                /* not supported yet, treat as next char */
+                ch = *format++;
+                break;
+            default:
+                /* unknown, skip */
+                break;
+            }
+            ch = *format++;
+            state = DP_S_DEFAULT;
+            flags = cflags = min = 0;
+            max = -1;
+            break;
+        case DP_S_DONE:
+            break;
+        default:
+            break;
+        }
+    }
+    /*
+     * We have to truncate if there is no dynamic buffer and we have filled the
+     * static buffer.
+     */
+    if (buffer == NULL) {
+        *truncated = (currlen > *maxlen - 1);
+        if (*truncated)
+            currlen = *maxlen - 1;
+    }
+    if(!doapr_outch(sbuffer, buffer, &currlen, maxlen, '\0'))
+        return 0;
+    *retlen = currlen - 1;
+    return 1;
+}
+
+static int
+fmtstr(char **sbuffer,
+       char **buffer,
+       size_t *currlen,
+       size_t *maxlen, const char *value, int flags, int min, int max)
+{
+    int padlen;
+    size_t strln;
+    int cnt = 0;
+
+    if (value == 0)
+        value = "";
+
+    strln = OPENSSL_strnlen(value, max < 0 ? SIZE_MAX : (size_t)max);
+
+    padlen = min - strln;
+    if (min < 0 || padlen < 0)
+        padlen = 0;
+    if (max >= 0) {
+        /*
+         * Calculate the maximum output including padding.
+         * Make sure max doesn't overflow into negativity
+         */
+        if (max < INT_MAX - padlen)
+            max += padlen;
+        else
+            max = INT_MAX;
+    }
+    if (flags & DP_F_MINUS)
+        padlen = -padlen;
+
+    while ((padlen > 0) && (max < 0 || cnt < max)) {
+        if(!doapr_outch(sbuffer, buffer, currlen, maxlen, ' '))
+            return 0;
+        --padlen;
+        ++cnt;
+    }
+    while (strln > 0 && (max < 0 || cnt < max)) {
+        if(!doapr_outch(sbuffer, buffer, currlen, maxlen, *value++))
+            return 0;
+        --strln;
+        ++cnt;
+    }
+    while ((padlen < 0) && (max < 0 || cnt < max)) {
+        if(!doapr_outch(sbuffer, buffer, currlen, maxlen, ' '))
+            return 0;
+        ++padlen;
+        ++cnt;
+    }
+    return 1;
+}
+
+static int
+fmtint(char **sbuffer,
+       char **buffer,
+       size_t *currlen,
+       size_t *maxlen, LLONG value, int base, int min, int max, int flags)
+{
+    int signvalue = 0;
+    const char *prefix = "";
+    unsigned LLONG uvalue;
+    char convert[DECIMAL_SIZE(value) + 3];
+    int place = 0;
+    int spadlen = 0;
+    int zpadlen = 0;
+    int caps = 0;
+
+    if (max < 0)
+        max = 0;
+    uvalue = value;
+    if (!(flags & DP_F_UNSIGNED)) {
+        if (value < 0) {
+            signvalue = '-';
+            uvalue = 0 - (unsigned LLONG)value;
+        } else if (flags & DP_F_PLUS)
+            signvalue = '+';
+        else if (flags & DP_F_SPACE)
+            signvalue = ' ';
+    }
+    if (flags & DP_F_NUM) {
+        if (base == 8)
+            prefix = "0";
+        if (base == 16)
+            prefix = "0x";
+    }
+    if (flags & DP_F_UP)
+        caps = 1;
+    do {
+        convert[place++] = (caps ? "0123456789ABCDEF" : "0123456789abcdef")
+            [uvalue % (unsigned)base];
+        uvalue = (uvalue / (unsigned)base);
+    } while (uvalue && (place < (int)sizeof(convert)));
+    if (place == sizeof(convert))
+        place--;
+    convert[place] = 0;
+
+    zpadlen = max - place;
+    spadlen =
+        min - OSSL_MAX(max, place) - (signvalue ? 1 : 0) - strlen(prefix);
+    if (zpadlen < 0)
+        zpadlen = 0;
+    if (spadlen < 0)
+        spadlen = 0;
+    if (flags & DP_F_ZERO) {
+        zpadlen = OSSL_MAX(zpadlen, spadlen);
+        spadlen = 0;
+    }
+    if (flags & DP_F_MINUS)
+        spadlen = -spadlen;
+
+    /* spaces */
+    while (spadlen > 0) {
+        if(!doapr_outch(sbuffer, buffer, currlen, maxlen, ' '))
+            return 0;
+        --spadlen;
+    }
+
+    /* sign */
+    if (signvalue)
+        if(!doapr_outch(sbuffer, buffer, currlen, maxlen, signvalue))
+            return 0;
+
+    /* prefix */
+    while (*prefix) {
+        if(!doapr_outch(sbuffer, buffer, currlen, maxlen, *prefix))
+            return 0;
+        prefix++;
+    }
+
+    /* zeros */
+    if (zpadlen > 0) {
+        while (zpadlen > 0) {
+            if(!doapr_outch(sbuffer, buffer, currlen, maxlen, '0'))
+                return 0;
+            --zpadlen;
+        }
+    }
+    /* digits */
+    while (place > 0) {
+        if (!doapr_outch(sbuffer, buffer, currlen, maxlen, convert[--place]))
+            return 0;
+    }
+
+    /* left justified spaces */
+    while (spadlen < 0) {
+        if (!doapr_outch(sbuffer, buffer, currlen, maxlen, ' '))
+            return 0;
+        ++spadlen;
+    }
+    return 1;
+}
+
+static LDOUBLE abs_val(LDOUBLE value)
+{
+    LDOUBLE result = value;
+    if (value < 0)
+        result = -value;
+    return result;
+}
+
+static LDOUBLE pow_10(int in_exp)
+{
+    LDOUBLE result = 1;
+    while (in_exp) {
+        result *= 10;
+        in_exp--;
+    }
+    return result;
+}
+
+static long roundv(LDOUBLE value)
+{
+    long intpart;
+    intpart = (long)value;
+    value = value - intpart;
+    if (value >= 0.5)
+        intpart++;
+    return intpart;
+}
+
+static int
+fmtfp(char **sbuffer,
+      char **buffer,
+      size_t *currlen,
+      size_t *maxlen, LDOUBLE fvalue, int min, int max, int flags, int style)
+{
+    int signvalue = 0;
+    LDOUBLE ufvalue;
+    LDOUBLE tmpvalue;
+    char iconvert[20];
+    char fconvert[20];
+    char econvert[20];
+    int iplace = 0;
+    int fplace = 0;
+    int eplace = 0;
+    int padlen = 0;
+    int zpadlen = 0;
+    long exp = 0;
+    unsigned long intpart;
+    unsigned long fracpart;
+    unsigned long max10;
+    int realstyle;
+
+    if (max < 0)
+        max = 6;
+
+    if (fvalue < 0)
+        signvalue = '-';
+    else if (flags & DP_F_PLUS)
+        signvalue = '+';
+    else if (flags & DP_F_SPACE)
+        signvalue = ' ';
+
+    /*
+     * G_FORMAT sometimes prints like E_FORMAT and sometimes like F_FORMAT
+     * depending on the number to be printed. Work out which one it is and use
+     * that from here on.
+     */
+    if (style == G_FORMAT) {
+        if (fvalue == 0.0) {
+            realstyle = F_FORMAT;
+        } else if (fvalue < 0.0001) {
+            realstyle = E_FORMAT;
+        } else if ((max == 0 && fvalue >= 10)
+                    || (max > 0 && fvalue >= pow_10(max))) {
+            realstyle = E_FORMAT;
+        } else {
+            realstyle = F_FORMAT;
+        }
+    } else {
+        realstyle = style;
+    }
+
+    if (style != F_FORMAT) {
+        tmpvalue = fvalue;
+        /* Calculate the exponent */
+        if (fvalue != 0.0) {
+            while (tmpvalue < 1) {
+                tmpvalue *= 10;
+                exp--;
+            }
+            while (tmpvalue > 10) {
+                tmpvalue /= 10;
+                exp++;
+            }
+        }
+        if (style == G_FORMAT) {
+            /*
+             * In G_FORMAT the "precision" represents significant digits. We
+             * always have at least 1 significant digit.
+             */
+            if (max == 0)
+                max = 1;
+            /* Now convert significant digits to decimal places */
+            if (realstyle == F_FORMAT) {
+                max -= (exp + 1);
+                if (max < 0) {
+                    /*
+                     * Should not happen. If we're in F_FORMAT then exp < max?
+                     */
+                    return 0;
+                }
+            } else {
+                /*
+                 * In E_FORMAT there is always one significant digit in front
+                 * of the decimal point, so:
+                 * significant digits == 1 + decimal places
+                 */
+                max--;
+            }
+        }
+        if (realstyle == E_FORMAT)
+            fvalue = tmpvalue;
+    }
+    ufvalue = abs_val(fvalue);
+    if (ufvalue > ULONG_MAX) {
+        /* Number too big */
+        return 0;
+    }
+    intpart = (unsigned long)ufvalue;
+
+    /*
+     * sorry, we only support 9 digits past the decimal because of our
+     * conversion method
+     */
+    if (max > 9)
+        max = 9;
+
+    /*
+     * we "cheat" by converting the fractional part to integer by multiplying
+     * by a factor of 10
+     */
+    max10 = roundv(pow_10(max));
+    fracpart = roundv(pow_10(max) * (ufvalue - intpart));
+
+    if (fracpart >= max10) {
+        intpart++;
+        fracpart -= max10;
+    }
+
+    /* convert integer part */
+    do {
+        iconvert[iplace++] = "0123456789"[intpart % 10];
+        intpart = (intpart / 10);
+    } while (intpart && (iplace < (int)sizeof(iconvert)));
+    if (iplace == sizeof iconvert)
+        iplace--;
+    iconvert[iplace] = 0;
+
+    /* convert fractional part */
+    while (fplace < max) {
+        if (style == G_FORMAT && fplace == 0 && (fracpart % 10) == 0) {
+            /* We strip trailing zeros in G_FORMAT */
+            max--;
+            fracpart = fracpart / 10;
+            if (fplace < max)
+                continue;
+            break;
+        }
+        fconvert[fplace++] = "0123456789"[fracpart % 10];
+        fracpart = (fracpart / 10);
+    }
+
+    if (fplace == sizeof fconvert)
+        fplace--;
+    fconvert[fplace] = 0;
+
+    /* convert exponent part */
+    if (realstyle == E_FORMAT) {
+        int tmpexp;
+        if (exp < 0)
+            tmpexp = -exp;
+        else
+            tmpexp = exp;
+
+        do {
+            econvert[eplace++] = "0123456789"[tmpexp % 10];
+            tmpexp = (tmpexp / 10);
+        } while (tmpexp > 0 && eplace < (int)sizeof(econvert));
+        /* Exponent is huge!! Too big to print */
+        if (tmpexp > 0)
+            return 0;
+        /* Add a leading 0 for single digit exponents */
+        if (eplace == 1)
+            econvert[eplace++] = '0';
+    }
+
+    /*
+     * -1 for decimal point (if we have one, i.e. max > 0),
+     * another -1 if we are printing a sign
+     */
+    padlen = min - iplace - max - (max > 0 ? 1 : 0) - ((signvalue) ? 1 : 0);
+    /* Take some off for exponent prefix "+e" and exponent */
+    if (realstyle == E_FORMAT)
+        padlen -= 2 + eplace;
+    zpadlen = max - fplace;
+    if (zpadlen < 0)
+        zpadlen = 0;
+    if (padlen < 0)
+        padlen = 0;
+    if (flags & DP_F_MINUS)
+        padlen = -padlen;
+
+    if ((flags & DP_F_ZERO) && (padlen > 0)) {
+        if (signvalue) {
+            if (!doapr_outch(sbuffer, buffer, currlen, maxlen, signvalue))
+                return 0;
+            --padlen;
+            signvalue = 0;
+        }
+        while (padlen > 0) {
+            if (!doapr_outch(sbuffer, buffer, currlen, maxlen, '0'))
+                return 0;
+            --padlen;
+        }
+    }
+    while (padlen > 0) {
+        if (!doapr_outch(sbuffer, buffer, currlen, maxlen, ' '))
+            return 0;
+        --padlen;
+    }
+    if (signvalue && !doapr_outch(sbuffer, buffer, currlen, maxlen, signvalue))
+        return 0;
+
+    while (iplace > 0) {
+        if (!doapr_outch(sbuffer, buffer, currlen, maxlen, iconvert[--iplace]))
+            return 0;
+    }
+
+    /*
+     * Decimal point. This should probably use locale to find the correct
+     * char to print out.
+     */
+    if (max > 0 || (flags & DP_F_NUM)) {
+        if (!doapr_outch(sbuffer, buffer, currlen, maxlen, '.'))
+            return 0;
+
+        while (fplace > 0) {
+            if(!doapr_outch(sbuffer, buffer, currlen, maxlen,
+                            fconvert[--fplace]))
+                return 0;
+        }
+    }
+    while (zpadlen > 0) {
+        if (!doapr_outch(sbuffer, buffer, currlen, maxlen, '0'))
+            return 0;
+        --zpadlen;
+    }
+    if (realstyle == E_FORMAT) {
+        char ech;
+
+        if ((flags & DP_F_UP) == 0)
+            ech = 'e';
+        else
+            ech = 'E';
+        if (!doapr_outch(sbuffer, buffer, currlen, maxlen, ech))
+                return 0;
+        if (exp < 0) {
+            if (!doapr_outch(sbuffer, buffer, currlen, maxlen, '-'))
+                    return 0;
+        } else {
+            if (!doapr_outch(sbuffer, buffer, currlen, maxlen, '+'))
+                    return 0;
+        }
+        while (eplace > 0) {
+            if (!doapr_outch(sbuffer, buffer, currlen, maxlen,
+                             econvert[--eplace]))
+                return 0;
+        }
+    }
+
+    while (padlen < 0) {
+        if (!doapr_outch(sbuffer, buffer, currlen, maxlen, ' '))
+            return 0;
+        ++padlen;
+    }
+    return 1;
+}
+
+#define BUFFER_INC  1024
+
+static int
+doapr_outch(char **sbuffer,
+            char **buffer, size_t *currlen, size_t *maxlen, int c)
+{
+    /* If we haven't at least one buffer, someone has doe a big booboo */
+    OPENSSL_assert(*sbuffer != NULL || buffer != NULL);
+
+    /* |currlen| must always be <= |*maxlen| */
+    OPENSSL_assert(*currlen <= *maxlen);
+
+    if (buffer && *currlen == *maxlen) {
+        if (*maxlen > INT_MAX - BUFFER_INC)
+            return 0;
+
+        *maxlen += BUFFER_INC;
+        if (*buffer == NULL) {
+            *buffer = OPENSSL_malloc(*maxlen);
+            if (*buffer == NULL)
+                return 0;
+            if (*currlen > 0) {
+                OPENSSL_assert(*sbuffer != NULL);
+                memcpy(*buffer, *sbuffer, *currlen);
+            }
+            *sbuffer = NULL;
+        } else {
+            char *tmpbuf;
+            tmpbuf = OPENSSL_realloc(*buffer, *maxlen);
+            if (tmpbuf == NULL)
+                return 0;
+            *buffer = tmpbuf;
+        }
+    }
+
+    if (*currlen < *maxlen) {
+        if (*sbuffer)
+            (*sbuffer)[(*currlen)++] = (char)c;
+        else
+            (*buffer)[(*currlen)++] = (char)c;
+    }
+
+    return 1;
+}
+
+/***************************************************************************/
+
+int BIO_printf(BIO *bio, const char *format, ...)
+{
+    va_list args;
+    int ret;
+
+    va_start(args, format);
+
+    ret = BIO_vprintf(bio, format, args);
+
+    va_end(args);
+    return (ret);
+}
+
+int BIO_vprintf(BIO *bio, const char *format, va_list args)
+{
+    int ret;
+    size_t retlen;
+    char hugebuf[1024 * 2];     /* Was previously 10k, which is unreasonable
+                                 * in small-stack environments, like threads
+                                 * or DOS programs. */
+    char *hugebufp = hugebuf;
+    size_t hugebufsize = sizeof(hugebuf);
+    char *dynbuf = NULL;
+    int ignored;
+
+    dynbuf = NULL;
+    if (!_dopr(&hugebufp, &dynbuf, &hugebufsize, &retlen, &ignored, format,
+                args)) {
+        OPENSSL_free(dynbuf);
+        return -1;
+    }
+    if (dynbuf) {
+        ret = BIO_write(bio, dynbuf, (int)retlen);
+        OPENSSL_free(dynbuf);
+    } else {
+        ret = BIO_write(bio, hugebuf, (int)retlen);
+    }
+    return (ret);
+}
+
+/*
+ * As snprintf is not available everywhere, we provide our own
+ * implementation. This function has nothing to do with BIOs, but it's
+ * closely related to BIO_printf, and we need *some* name prefix ... (XXX the
+ * function should be renamed, but to what?)
+ */
+int BIO_snprintf(char *buf, size_t n, const char *format, ...)
+{
+    va_list args;
+    int ret;
+
+    va_start(args, format);
+
+    ret = BIO_vsnprintf(buf, n, format, args);
+
+    va_end(args);
+    return (ret);
+}
+
+int BIO_vsnprintf(char *buf, size_t n, const char *format, va_list args)
+{
+    size_t retlen;
+    int truncated;
+
+    if(!_dopr(&buf, NULL, &n, &retlen, &truncated, format, args))
+        return -1;
+
+    if (truncated)
+        /*
+         * In case of truncation, return -1 like traditional snprintf.
+         * (Current drafts for ISO/IEC 9899 say snprintf should return the
+         * number of characters that would have been written, had the buffer
+         * been large enough.)
+         */
+        return -1;
+    else
+        return (retlen <= INT_MAX) ? (int)retlen : -1;
+}
diff --git a/crypto/bio/b_sock.c b/crypto/bio/b_sock.c
new file mode 100644
index 000000000..ac2c2d161
--- /dev/null
+++ b/crypto/bio/b_sock.c
@@ -0,0 +1,381 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include 
+#include "bio_lcl.h"
+#if defined(NETWARE_CLIB)
+# include 
+NETDB_DEFINE_CONTEXT
+#endif
+#ifndef OPENSSL_NO_SOCK
+# define SOCKET_PROTOCOL IPPROTO_TCP
+# ifdef SO_MAXCONN
+#  define MAX_LISTEN  SO_MAXCONN
+# elif defined(SOMAXCONN)
+#  define MAX_LISTEN  SOMAXCONN
+# else
+#  define MAX_LISTEN  32
+# endif
+# if defined(OPENSSL_SYS_WINDOWS)
+static int wsa_init_done = 0;
+# endif
+
+# if OPENSSL_API_COMPAT < 0x10100000L
+int BIO_get_host_ip(const char *str, unsigned char *ip)
+{
+    BIO_ADDRINFO *res = NULL;
+    int ret = 0;
+
+    if (BIO_sock_init() != 1)
+        return 0;               /* don't generate another error code here */
+
+    if (BIO_lookup(str, NULL, BIO_LOOKUP_CLIENT, AF_INET, SOCK_STREAM, &res)) {
+        size_t l;
+
+        if (BIO_ADDRINFO_family(res) != AF_INET) {
+            BIOerr(BIO_F_BIO_GET_HOST_IP,
+                   BIO_R_GETHOSTBYNAME_ADDR_IS_NOT_AF_INET);
+        } else {
+            BIO_ADDR_rawaddress(BIO_ADDRINFO_address(res), NULL, &l);
+            /* Because only AF_INET addresses will reach this far,
+               we can assert that l should be 4 */
+            OPENSSL_assert(l == 4);
+
+            BIO_ADDR_rawaddress(BIO_ADDRINFO_address(res), ip, &l);
+            ret = 1;
+        }
+        BIO_ADDRINFO_free(res);
+    } else {
+        ERR_add_error_data(2, "host=", str);
+    }
+
+    return ret;
+}
+
+int BIO_get_port(const char *str, unsigned short *port_ptr)
+{
+    BIO_ADDRINFO *res = NULL;
+    int ret = 0;
+
+    if (str == NULL) {
+        BIOerr(BIO_F_BIO_GET_PORT, BIO_R_NO_PORT_DEFINED);
+        return (0);
+    }
+
+    if (BIO_sock_init() != 1)
+        return 0;               /* don't generate another error code here */
+
+    if (BIO_lookup(NULL, str, BIO_LOOKUP_CLIENT, AF_INET, SOCK_STREAM, &res)) {
+        if (BIO_ADDRINFO_family(res) != AF_INET) {
+            BIOerr(BIO_F_BIO_GET_PORT,
+                   BIO_R_ADDRINFO_ADDR_IS_NOT_AF_INET);
+        } else {
+            *port_ptr = ntohs(BIO_ADDR_rawport(BIO_ADDRINFO_address(res)));
+            ret = 1;
+        }
+        BIO_ADDRINFO_free(res);
+    } else {
+        ERR_add_error_data(2, "host=", str);
+    }
+
+    return ret;
+}
+# endif
+
+int BIO_sock_error(int sock)
+{
+    int j = 0, i;
+    socklen_t size = sizeof(j);
+
+    /*
+     * Note: under Windows the third parameter is of type (char *) whereas
+     * under other systems it is (void *) if you don't have a cast it will
+     * choke the compiler: if you do have a cast then you can either go for
+     * (char *) or (void *).
+     */
+    i = getsockopt(sock, SOL_SOCKET, SO_ERROR, (void *)&j, &size);
+    if (i < 0)
+        return (get_last_socket_error());
+    else
+        return (j);
+}
+
+# if OPENSSL_API_COMPAT < 0x10100000L
+struct hostent *BIO_gethostbyname(const char *name)
+{
+    /*
+     * Caching gethostbyname() results forever is wrong, so we have to let
+     * the true gethostbyname() worry about this
+     */
+#  if (defined(NETWARE_BSDSOCK) && !defined(__NOVELL_LIBC__))
+    return gethostbyname((char *)name);
+#  else
+    return gethostbyname(name);
+#  endif
+}
+# endif
+
+int BIO_sock_init(void)
+{
+# ifdef OPENSSL_SYS_WINDOWS
+    static struct WSAData wsa_state;
+
+    if (!wsa_init_done) {
+        int err;
+
+        wsa_init_done = 1;
+        memset(&wsa_state, 0, sizeof(wsa_state));
+        /*
+         * Not making wsa_state available to the rest of the code is formally
+         * wrong. But the structures we use are [believed to be] invariable
+         * among Winsock DLLs, while API availability is [expected to be]
+         * probed at run-time with DSO_global_lookup.
+         */
+        if (WSAStartup(0x0202, &wsa_state) != 0) {
+            err = WSAGetLastError();
+            SYSerr(SYS_F_WSASTARTUP, err);
+            BIOerr(BIO_F_BIO_SOCK_INIT, BIO_R_WSASTARTUP);
+            return (-1);
+        }
+    }
+# endif                         /* OPENSSL_SYS_WINDOWS */
+# ifdef WATT32
+    extern int _watt_do_exit;
+    _watt_do_exit = 0;          /* don't make sock_init() call exit() */
+    if (sock_init())
+        return (-1);
+# endif
+
+    return (1);
+}
+
+void bio_sock_cleanup_int(void)
+{
+# ifdef OPENSSL_SYS_WINDOWS
+    if (wsa_init_done) {
+        wsa_init_done = 0;
+        WSACleanup();
+    }
+# endif
+}
+
+# if !defined(OPENSSL_SYS_VMS) || __VMS_VER >= 70000000
+
+int BIO_socket_ioctl(int fd, long type, void *arg)
+{
+    int i;
+
+#  ifdef __DJGPP__
+    i = ioctlsocket(fd, type, (char *)arg);
+#  else
+#   if defined(OPENSSL_SYS_VMS)
+    /*-
+     * 2011-02-18 SMS.
+     * VMS ioctl() can't tolerate a 64-bit "void *arg", but we
+     * observe that all the consumers pass in an "unsigned long *",
+     * so we arrange a local copy with a short pointer, and use
+     * that, instead.
+     */
+#    if __INITIAL_POINTER_SIZE == 64
+#     define ARG arg_32p
+#     pragma pointer_size save
+#     pragma pointer_size 32
+    unsigned long arg_32;
+    unsigned long *arg_32p;
+#     pragma pointer_size restore
+    arg_32p = &arg_32;
+    arg_32 = *((unsigned long *)arg);
+#    else                       /* __INITIAL_POINTER_SIZE == 64 */
+#     define ARG arg
+#    endif                      /* __INITIAL_POINTER_SIZE == 64 [else] */
+#   else                        /* defined(OPENSSL_SYS_VMS) */
+#    define ARG arg
+#   endif                       /* defined(OPENSSL_SYS_VMS) [else] */
+
+    i = ioctlsocket(fd, type, ARG);
+#  endif                        /* __DJGPP__ */
+    if (i < 0)
+        SYSerr(SYS_F_IOCTLSOCKET, get_last_socket_error());
+    return (i);
+}
+# endif                         /* __VMS_VER */
+
+# if OPENSSL_API_COMPAT < 0x10100000L
+int BIO_get_accept_socket(char *host, int bind_mode)
+{
+    int s = INVALID_SOCKET;
+    char *h = NULL, *p = NULL;
+    BIO_ADDRINFO *res = NULL;
+
+    if (!BIO_parse_hostserv(host, &h, &p, BIO_PARSE_PRIO_SERV))
+        return INVALID_SOCKET;
+
+    if (BIO_sock_init() != 1)
+        return INVALID_SOCKET;
+
+    if (BIO_lookup(h, p, BIO_LOOKUP_SERVER, AF_UNSPEC, SOCK_STREAM, &res) != 0)
+        goto err;
+
+    if ((s = BIO_socket(BIO_ADDRINFO_family(res), BIO_ADDRINFO_socktype(res),
+                        BIO_ADDRINFO_protocol(res), 0)) == INVALID_SOCKET) {
+        s = INVALID_SOCKET;
+        goto err;
+    }
+
+    if (!BIO_listen(s, BIO_ADDRINFO_address(res),
+                    bind_mode ? BIO_SOCK_REUSEADDR : 0)) {
+        BIO_closesocket(s);
+        s = INVALID_SOCKET;
+    }
+
+ err:
+    BIO_ADDRINFO_free(res);
+    OPENSSL_free(h);
+    OPENSSL_free(p);
+
+    return s;
+}
+
+int BIO_accept(int sock, char **ip_port)
+{
+    BIO_ADDR res;
+    int ret = -1;
+
+    ret = BIO_accept_ex(sock, &res, 0);
+    if (ret == (int)INVALID_SOCKET) {
+        if (BIO_sock_should_retry(ret)) {
+            ret = -2;
+            goto end;
+        }
+        SYSerr(SYS_F_ACCEPT, get_last_socket_error());
+        BIOerr(BIO_F_BIO_ACCEPT, BIO_R_ACCEPT_ERROR);
+        goto end;
+    }
+
+    if (ip_port != NULL) {
+        char *host = BIO_ADDR_hostname_string(&res, 1);
+        char *port = BIO_ADDR_service_string(&res, 1);
+        if (host != NULL && port != NULL)
+            *ip_port = OPENSSL_zalloc(strlen(host) + strlen(port) + 2);
+        else
+            *ip_port = NULL;
+
+        if (*ip_port == NULL) {
+            BIOerr(BIO_F_BIO_ACCEPT, ERR_R_MALLOC_FAILURE);
+            BIO_closesocket(ret);
+            ret = (int)INVALID_SOCKET;
+        } else {
+            strcpy(*ip_port, host);
+            strcat(*ip_port, ":");
+            strcat(*ip_port, port);
+        }
+        OPENSSL_free(host);
+        OPENSSL_free(port);
+    }
+
+ end:
+    return ret;
+}
+# endif
+
+int BIO_set_tcp_ndelay(int s, int on)
+{
+    int ret = 0;
+# if defined(TCP_NODELAY) && (defined(IPPROTO_TCP) || defined(SOL_TCP))
+    int opt;
+
+#  ifdef SOL_TCP
+    opt = SOL_TCP;
+#  else
+#   ifdef IPPROTO_TCP
+    opt = IPPROTO_TCP;
+#   endif
+#  endif
+
+    ret = setsockopt(s, opt, TCP_NODELAY, (char *)&on, sizeof(on));
+# endif
+    return (ret == 0);
+}
+
+int BIO_socket_nbio(int s, int mode)
+{
+    int ret = -1;
+    int l;
+
+    l = mode;
+# ifdef FIONBIO
+    l = mode;
+
+    ret = BIO_socket_ioctl(s, FIONBIO, &l);
+# elif defined(F_GETFL) && defined(F_SETFL) && (defined(O_NONBLOCK) || defined(FNDELAY))
+    /* make sure this call always pushes an error level; BIO_socket_ioctl() does so, so we do too. */
+
+    l = fcntl(s, F_GETFL, 0);
+    if (l == -1) {
+        SYSerr(SYS_F_FCNTL, get_last_rtl_error());
+        ret = -1;
+    } else {
+#  if defined(O_NONBLOCK)
+        l &= ~O_NONBLOCK;
+#  else
+        l &= ~FNDELAY; /* BSD4.x */
+#  endif
+        if (mode) {
+#  if defined(O_NONBLOCK)
+            l |= O_NONBLOCK;
+#  else
+            l |= FNDELAY; /* BSD4.x */
+#  endif
+        }
+        ret = fcntl(s, F_SETFL, l);
+
+        if (ret < 0) {
+            SYSerr(SYS_F_FCNTL, get_last_rtl_error());
+        }
+    }
+# else
+    /* make sure this call always pushes an error level; BIO_socket_ioctl() does so, so we do too. */
+    BIOerr(BIO_F_BIO_SOCKET_NBIO, ERR_R_PASSED_INVALID_ARGUMENT);
+# endif
+
+    return (ret == 0);
+}
+
+int BIO_sock_info(int sock,
+                  enum BIO_sock_info_type type, union BIO_sock_info_u *info)
+{
+    switch (type) {
+    case BIO_SOCK_INFO_ADDRESS:
+        {
+            socklen_t addr_len;
+            int ret = 0;
+            addr_len = sizeof(*info->addr);
+            ret = getsockname(sock, BIO_ADDR_sockaddr_noconst(info->addr),
+                              &addr_len);
+            if (ret == -1) {
+                SYSerr(SYS_F_GETSOCKNAME, get_last_socket_error());
+                BIOerr(BIO_F_BIO_SOCK_INFO, BIO_R_GETSOCKNAME_ERROR);
+                return 0;
+            }
+            if ((size_t)addr_len > sizeof(*info->addr)) {
+                BIOerr(BIO_F_BIO_SOCK_INFO, BIO_R_GETSOCKNAME_TRUNCATED_ADDRESS);
+                return 0;
+            }
+        }
+        break;
+    default:
+        BIOerr(BIO_F_BIO_SOCK_INFO, BIO_R_UNKNOWN_INFO_TYPE);
+        return 0;
+    }
+    return 1;
+}
+
+#endif
diff --git a/crypto/bio/b_sock2.c b/crypto/bio/b_sock2.c
new file mode 100644
index 000000000..7f4d89e55
--- /dev/null
+++ b/crypto/bio/b_sock2.c
@@ -0,0 +1,270 @@
+/*
+ * Copyright 2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include 
+
+#include "bio_lcl.h"
+
+#include 
+
+#ifndef OPENSSL_NO_SOCK
+# ifdef SO_MAXCONN
+#  define MAX_LISTEN  SO_MAXCONN
+# elif defined(SOMAXCONN)
+#  define MAX_LISTEN  SOMAXCONN
+# else
+#  define MAX_LISTEN  32
+# endif
+
+/*-
+ * BIO_socket - create a socket
+ * @domain: the socket domain (AF_INET, AF_INET6, AF_UNIX, ...)
+ * @socktype: the socket type (SOCK_STEAM, SOCK_DGRAM)
+ * @protocol: the protocol to use (IPPROTO_TCP, IPPROTO_UDP)
+ * @options: BIO socket options (currently unused)
+ *
+ * Creates a socket.  This should be called before calling any
+ * of BIO_connect and BIO_listen.
+ *
+ * Returns the file descriptor on success or INVALID_SOCKET on failure.  On
+ * failure errno is set, and a status is added to the OpenSSL error stack.
+ */
+int BIO_socket(int domain, int socktype, int protocol, int options)
+{
+    int sock = -1;
+
+    if (BIO_sock_init() != 1)
+        return INVALID_SOCKET;
+
+    sock = socket(domain, socktype, protocol);
+    if (sock == -1) {
+        SYSerr(SYS_F_SOCKET, get_last_socket_error());
+        BIOerr(BIO_F_BIO_SOCKET, BIO_R_UNABLE_TO_CREATE_SOCKET);
+        return INVALID_SOCKET;
+    }
+
+    return sock;
+}
+
+/*-
+ * BIO_connect - connect to an address
+ * @sock: the socket to connect with
+ * @addr: the address to connect to
+ * @options: BIO socket options
+ *
+ * Connects to the address using the given socket and options.
+ *
+ * Options can be a combination of the following:
+ * - BIO_SOCK_KEEPALIVE: enable regularly sending keep-alive messages.
+ * - BIO_SOCK_NONBLOCK: Make the socket non-blocking.
+ * - BIO_SOCK_NODELAY: don't delay small messages.
+ *
+ * options holds BIO socket options that can be used
+ * You should call this for every address returned by BIO_lookup
+ * until the connection is successful.
+ *
+ * Returns 1 on success or 0 on failure.  On failure errno is set
+ * and an error status is added to the OpenSSL error stack.
+ */
+int BIO_connect(int sock, const BIO_ADDR *addr, int options)
+{
+    int on = 1;
+
+    if (sock == -1) {
+        BIOerr(BIO_F_BIO_CONNECT, BIO_R_INVALID_SOCKET);
+        return 0;
+    }
+
+    if (!BIO_socket_nbio(sock, (options & BIO_SOCK_NONBLOCK) != 0))
+        return 0;
+
+    if (options & BIO_SOCK_KEEPALIVE) {
+        if (setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(on)) != 0) {
+            SYSerr(SYS_F_SETSOCKOPT, get_last_socket_error());
+            BIOerr(BIO_F_BIO_CONNECT, BIO_R_UNABLE_TO_KEEPALIVE);
+            return 0;
+        }
+    }
+
+    if (options & BIO_SOCK_NODELAY) {
+        if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)) != 0) {
+            SYSerr(SYS_F_SETSOCKOPT, get_last_socket_error());
+            BIOerr(BIO_F_BIO_CONNECT, BIO_R_UNABLE_TO_NODELAY);
+            return 0;
+        }
+    }
+
+    if (connect(sock, BIO_ADDR_sockaddr(addr),
+                BIO_ADDR_sockaddr_size(addr)) == -1) {
+        if (!BIO_sock_should_retry(-1)) {
+            SYSerr(SYS_F_CONNECT, get_last_socket_error());
+            BIOerr(BIO_F_BIO_CONNECT, BIO_R_CONNECT_ERROR);
+        }
+        return 0;
+    }
+    return 1;
+}
+
+/*-
+ * BIO_listen - Creates a listen socket
+ * @sock: the socket to listen with
+ * @addr: local address to bind to
+ * @options: BIO socket options
+ *
+ * Binds to the address using the given socket and options, then
+ * starts listening for incoming connections.
+ *
+ * Options can be a combination of the following:
+ * - BIO_SOCK_KEEPALIVE: enable regularly sending keep-alive messages.
+ * - BIO_SOCK_NONBLOCK: Make the socket non-blocking.
+ * - BIO_SOCK_NODELAY: don't delay small messages.
+ * - BIO_SOCK_REUSEADDR: Try to reuse the address and port combination
+ *   for a recently closed port.
+ * - BIO_SOCK_V6_ONLY: When creating an IPv6 socket, make it listen only
+ *   for IPv6 addresses and not IPv4 addresses mapped to IPv6.
+ *
+ * It's recommended that you set up both an IPv6 and IPv4 listen socket, and
+ * then check both for new clients that connect to it.  You want to set up
+ * the socket as non-blocking in that case since else it could hang.
+ *
+ * Not all operating systems support IPv4 addresses on an IPv6 socket, and for
+ * others it's an option.  If you pass the BIO_LISTEN_V6_ONLY it will try to
+ * create the IPv6 sockets to only listen for IPv6 connection.
+ *
+ * It could be that the first BIO_listen() call will listen to all the IPv6
+ * and IPv4 addresses and that then trying to bind to the IPv4 address will
+ * fail.  We can't tell the difference between already listening ourself to
+ * it and someone else listening to it when failing and errno is EADDRINUSE, so
+ * it's recommended to not give an error in that case if the first call was
+ * successful.
+ *
+ * When restarting the program it could be that the port is still in use.  If
+ * you set to BIO_SOCK_REUSEADDR option it will try to reuse the port anyway.
+ * It's recommended that you use this.
+ */
+int BIO_listen(int sock, const BIO_ADDR *addr, int options)
+{
+    int on = 1;
+    int socktype;
+    socklen_t socktype_len = sizeof(socktype);
+
+    if (sock == -1) {
+        BIOerr(BIO_F_BIO_LISTEN, BIO_R_INVALID_SOCKET);
+        return 0;
+    }
+
+    if (getsockopt(sock, SOL_SOCKET, SO_TYPE, &socktype, &socktype_len) != 0
+        || socktype_len != sizeof(socktype)) {
+        SYSerr(SYS_F_GETSOCKOPT, get_last_socket_error());
+        BIOerr(BIO_F_BIO_LISTEN, BIO_R_GETTING_SOCKTYPE);
+        return 0;
+    }
+
+    if (!BIO_socket_nbio(sock, (options & BIO_SOCK_NONBLOCK) != 0))
+        return 0;
+
+# ifndef OPENSSL_SYS_WINDOWS
+    /* SO_REUSEADDR has different behavior on Windows than on
+     * other operating systems, don't set it there. */
+    if (options & BIO_SOCK_REUSEADDR) {
+        if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) != 0) {
+            SYSerr(SYS_F_SETSOCKOPT, get_last_socket_error());
+            BIOerr(BIO_F_BIO_LISTEN, BIO_R_UNABLE_TO_REUSEADDR);
+            return 0;
+        }
+    }
+# endif
+
+    if (options & BIO_SOCK_KEEPALIVE) {
+        if (setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(on)) != 0) {
+            SYSerr(SYS_F_SETSOCKOPT, get_last_socket_error());
+            BIOerr(BIO_F_BIO_LISTEN, BIO_R_UNABLE_TO_KEEPALIVE);
+            return 0;
+        }
+    }
+
+    if (options & BIO_SOCK_NODELAY) {
+        if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)) != 0) {
+            SYSerr(SYS_F_SETSOCKOPT, get_last_socket_error());
+            BIOerr(BIO_F_BIO_LISTEN, BIO_R_UNABLE_TO_NODELAY);
+            return 0;
+        }
+    }
+
+# ifdef IPV6_V6ONLY
+    if ((options & BIO_SOCK_V6_ONLY) && BIO_ADDR_family(addr) == AF_INET6) {
+        if (setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)) != 0) {
+            SYSerr(SYS_F_SETSOCKOPT, get_last_socket_error());
+            BIOerr(BIO_F_BIO_LISTEN, BIO_R_LISTEN_V6_ONLY);
+            return 0;
+        }
+    }
+# endif
+
+    if (bind(sock, BIO_ADDR_sockaddr(addr), BIO_ADDR_sockaddr_size(addr)) != 0) {
+        SYSerr(SYS_F_BIND, get_last_socket_error());
+        BIOerr(BIO_F_BIO_LISTEN, BIO_R_UNABLE_TO_BIND_SOCKET);
+        return 0;
+    }
+
+    if (socktype != SOCK_DGRAM && listen(sock, MAX_LISTEN) == -1) {
+        SYSerr(SYS_F_LISTEN, get_last_socket_error());
+        BIOerr(BIO_F_BIO_LISTEN, BIO_R_UNABLE_TO_LISTEN_SOCKET);
+        return 0;
+    }
+
+    return 1;
+}
+
+/*-
+ * BIO_accept_ex - Accept new incoming connections
+ * @sock: the listening socket
+ * @addr: the BIO_ADDR to store the peer address in
+ * @options: BIO socket options, applied on the accepted socket.
+ *
+ */
+int BIO_accept_ex(int accept_sock, BIO_ADDR *addr_, int options)
+{
+    socklen_t len;
+    int accepted_sock;
+    BIO_ADDR locaddr;
+    BIO_ADDR *addr = addr_ == NULL ? &locaddr : addr_;
+
+    len = sizeof(*addr);
+    accepted_sock = accept(accept_sock,
+                           BIO_ADDR_sockaddr_noconst(addr), &len);
+    if (accepted_sock == -1) {
+        if (!BIO_sock_should_retry(accepted_sock)) {
+            SYSerr(SYS_F_ACCEPT, get_last_socket_error());
+            BIOerr(BIO_F_BIO_ACCEPT_EX, BIO_R_ACCEPT_ERROR);
+        }
+        return INVALID_SOCKET;
+    }
+
+    if (!BIO_socket_nbio(accepted_sock, (options & BIO_SOCK_NONBLOCK) != 0)) {
+        closesocket(accepted_sock);
+        return INVALID_SOCKET;
+    }
+
+    return accepted_sock;
+}
+
+/*-
+ * BIO_closesocket - Close a socket
+ * @sock: the socket to close
+ */
+int BIO_closesocket(int sock)
+{
+    if (closesocket(sock) < 0)
+        return 0;
+    return 1;
+}
+#endif
diff --git a/crypto/bio/bf_buff.c b/crypto/bio/bf_buff.c
new file mode 100644
index 000000000..b2a387b53
--- /dev/null
+++ b/crypto/bio/bf_buff.c
@@ -0,0 +1,455 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include "bio_lcl.h"
+#include "internal/cryptlib.h"
+
+static int buffer_write(BIO *h, const char *buf, int num);
+static int buffer_read(BIO *h, char *buf, int size);
+static int buffer_puts(BIO *h, const char *str);
+static int buffer_gets(BIO *h, char *str, int size);
+static long buffer_ctrl(BIO *h, int cmd, long arg1, void *arg2);
+static int buffer_new(BIO *h);
+static int buffer_free(BIO *data);
+static long buffer_callback_ctrl(BIO *h, int cmd, bio_info_cb *fp);
+#define DEFAULT_BUFFER_SIZE     4096
+
+static const BIO_METHOD methods_buffer = {
+    BIO_TYPE_BUFFER,
+    "buffer",
+    buffer_write,
+    buffer_read,
+    buffer_puts,
+    buffer_gets,
+    buffer_ctrl,
+    buffer_new,
+    buffer_free,
+    buffer_callback_ctrl,
+};
+
+const BIO_METHOD *BIO_f_buffer(void)
+{
+    return (&methods_buffer);
+}
+
+static int buffer_new(BIO *bi)
+{
+    BIO_F_BUFFER_CTX *ctx = OPENSSL_zalloc(sizeof(*ctx));
+
+    if (ctx == NULL)
+        return (0);
+    ctx->ibuf_size = DEFAULT_BUFFER_SIZE;
+    ctx->ibuf = OPENSSL_malloc(DEFAULT_BUFFER_SIZE);
+    if (ctx->ibuf == NULL) {
+        OPENSSL_free(ctx);
+        return (0);
+    }
+    ctx->obuf_size = DEFAULT_BUFFER_SIZE;
+    ctx->obuf = OPENSSL_malloc(DEFAULT_BUFFER_SIZE);
+    if (ctx->obuf == NULL) {
+        OPENSSL_free(ctx->ibuf);
+        OPENSSL_free(ctx);
+        return (0);
+    }
+
+    bi->init = 1;
+    bi->ptr = (char *)ctx;
+    bi->flags = 0;
+    return (1);
+}
+
+static int buffer_free(BIO *a)
+{
+    BIO_F_BUFFER_CTX *b;
+
+    if (a == NULL)
+        return (0);
+    b = (BIO_F_BUFFER_CTX *)a->ptr;
+    OPENSSL_free(b->ibuf);
+    OPENSSL_free(b->obuf);
+    OPENSSL_free(a->ptr);
+    a->ptr = NULL;
+    a->init = 0;
+    a->flags = 0;
+    return (1);
+}
+
+static int buffer_read(BIO *b, char *out, int outl)
+{
+    int i, num = 0;
+    BIO_F_BUFFER_CTX *ctx;
+
+    if (out == NULL)
+        return (0);
+    ctx = (BIO_F_BUFFER_CTX *)b->ptr;
+
+    if ((ctx == NULL) || (b->next_bio == NULL))
+        return (0);
+    num = 0;
+    BIO_clear_retry_flags(b);
+
+ start:
+    i = ctx->ibuf_len;
+    /* If there is stuff left over, grab it */
+    if (i != 0) {
+        if (i > outl)
+            i = outl;
+        memcpy(out, &(ctx->ibuf[ctx->ibuf_off]), i);
+        ctx->ibuf_off += i;
+        ctx->ibuf_len -= i;
+        num += i;
+        if (outl == i)
+            return (num);
+        outl -= i;
+        out += i;
+    }
+
+    /*
+     * We may have done a partial read. try to do more. We have nothing in
+     * the buffer. If we get an error and have read some data, just return it
+     * and let them retry to get the error again. copy direct to parent
+     * address space
+     */
+    if (outl > ctx->ibuf_size) {
+        for (;;) {
+            i = BIO_read(b->next_bio, out, outl);
+            if (i <= 0) {
+                BIO_copy_next_retry(b);
+                if (i < 0)
+                    return ((num > 0) ? num : i);
+                if (i == 0)
+                    return (num);
+            }
+            num += i;
+            if (outl == i)
+                return (num);
+            out += i;
+            outl -= i;
+        }
+    }
+    /* else */
+
+    /* we are going to be doing some buffering */
+    i = BIO_read(b->next_bio, ctx->ibuf, ctx->ibuf_size);
+    if (i <= 0) {
+        BIO_copy_next_retry(b);
+        if (i < 0)
+            return ((num > 0) ? num : i);
+        if (i == 0)
+            return (num);
+    }
+    ctx->ibuf_off = 0;
+    ctx->ibuf_len = i;
+
+    /* Lets re-read using ourselves :-) */
+    goto start;
+}
+
+static int buffer_write(BIO *b, const char *in, int inl)
+{
+    int i, num = 0;
+    BIO_F_BUFFER_CTX *ctx;
+
+    if ((in == NULL) || (inl <= 0))
+        return (0);
+    ctx = (BIO_F_BUFFER_CTX *)b->ptr;
+    if ((ctx == NULL) || (b->next_bio == NULL))
+        return (0);
+
+    BIO_clear_retry_flags(b);
+ start:
+    i = ctx->obuf_size - (ctx->obuf_len + ctx->obuf_off);
+    /* add to buffer and return */
+    if (i >= inl) {
+        memcpy(&(ctx->obuf[ctx->obuf_off + ctx->obuf_len]), in, inl);
+        ctx->obuf_len += inl;
+        return (num + inl);
+    }
+    /* else */
+    /* stuff already in buffer, so add to it first, then flush */
+    if (ctx->obuf_len != 0) {
+        if (i > 0) {            /* lets fill it up if we can */
+            memcpy(&(ctx->obuf[ctx->obuf_off + ctx->obuf_len]), in, i);
+            in += i;
+            inl -= i;
+            num += i;
+            ctx->obuf_len += i;
+        }
+        /* we now have a full buffer needing flushing */
+        for (;;) {
+            i = BIO_write(b->next_bio, &(ctx->obuf[ctx->obuf_off]),
+                          ctx->obuf_len);
+            if (i <= 0) {
+                BIO_copy_next_retry(b);
+
+                if (i < 0)
+                    return ((num > 0) ? num : i);
+                if (i == 0)
+                    return (num);
+            }
+            ctx->obuf_off += i;
+            ctx->obuf_len -= i;
+            if (ctx->obuf_len == 0)
+                break;
+        }
+    }
+    /*
+     * we only get here if the buffer has been flushed and we still have
+     * stuff to write
+     */
+    ctx->obuf_off = 0;
+
+    /* we now have inl bytes to write */
+    while (inl >= ctx->obuf_size) {
+        i = BIO_write(b->next_bio, in, inl);
+        if (i <= 0) {
+            BIO_copy_next_retry(b);
+            if (i < 0)
+                return ((num > 0) ? num : i);
+            if (i == 0)
+                return (num);
+        }
+        num += i;
+        in += i;
+        inl -= i;
+        if (inl == 0)
+            return (num);
+    }
+
+    /*
+     * copy the rest into the buffer since we have only a small amount left
+     */
+    goto start;
+}
+
+static long buffer_ctrl(BIO *b, int cmd, long num, void *ptr)
+{
+    BIO *dbio;
+    BIO_F_BUFFER_CTX *ctx;
+    long ret = 1;
+    char *p1, *p2;
+    int r, i, *ip;
+    int ibs, obs;
+
+    ctx = (BIO_F_BUFFER_CTX *)b->ptr;
+
+    switch (cmd) {
+    case BIO_CTRL_RESET:
+        ctx->ibuf_off = 0;
+        ctx->ibuf_len = 0;
+        ctx->obuf_off = 0;
+        ctx->obuf_len = 0;
+        if (b->next_bio == NULL)
+            return (0);
+        ret = BIO_ctrl(b->next_bio, cmd, num, ptr);
+        break;
+    case BIO_CTRL_INFO:
+        ret = (long)ctx->obuf_len;
+        break;
+    case BIO_C_GET_BUFF_NUM_LINES:
+        ret = 0;
+        p1 = ctx->ibuf;
+        for (i = 0; i < ctx->ibuf_len; i++) {
+            if (p1[ctx->ibuf_off + i] == '\n')
+                ret++;
+        }
+        break;
+    case BIO_CTRL_WPENDING:
+        ret = (long)ctx->obuf_len;
+        if (ret == 0) {
+            if (b->next_bio == NULL)
+                return (0);
+            ret = BIO_ctrl(b->next_bio, cmd, num, ptr);
+        }
+        break;
+    case BIO_CTRL_PENDING:
+        ret = (long)ctx->ibuf_len;
+        if (ret == 0) {
+            if (b->next_bio == NULL)
+                return (0);
+            ret = BIO_ctrl(b->next_bio, cmd, num, ptr);
+        }
+        break;
+    case BIO_C_SET_BUFF_READ_DATA:
+        if (num > ctx->ibuf_size) {
+            p1 = OPENSSL_malloc((int)num);
+            if (p1 == NULL)
+                goto malloc_error;
+            OPENSSL_free(ctx->ibuf);
+            ctx->ibuf = p1;
+        }
+        ctx->ibuf_off = 0;
+        ctx->ibuf_len = (int)num;
+        memcpy(ctx->ibuf, ptr, (int)num);
+        ret = 1;
+        break;
+    case BIO_C_SET_BUFF_SIZE:
+        if (ptr != NULL) {
+            ip = (int *)ptr;
+            if (*ip == 0) {
+                ibs = (int)num;
+                obs = ctx->obuf_size;
+            } else {            /* if (*ip == 1) */
+
+                ibs = ctx->ibuf_size;
+                obs = (int)num;
+            }
+        } else {
+            ibs = (int)num;
+            obs = (int)num;
+        }
+        p1 = ctx->ibuf;
+        p2 = ctx->obuf;
+        if ((ibs > DEFAULT_BUFFER_SIZE) && (ibs != ctx->ibuf_size)) {
+            p1 = OPENSSL_malloc((int)num);
+            if (p1 == NULL)
+                goto malloc_error;
+        }
+        if ((obs > DEFAULT_BUFFER_SIZE) && (obs != ctx->obuf_size)) {
+            p2 = OPENSSL_malloc((int)num);
+            if (p2 == NULL) {
+                if (p1 != ctx->ibuf)
+                    OPENSSL_free(p1);
+                goto malloc_error;
+            }
+        }
+        if (ctx->ibuf != p1) {
+            OPENSSL_free(ctx->ibuf);
+            ctx->ibuf = p1;
+            ctx->ibuf_off = 0;
+            ctx->ibuf_len = 0;
+            ctx->ibuf_size = ibs;
+        }
+        if (ctx->obuf != p2) {
+            OPENSSL_free(ctx->obuf);
+            ctx->obuf = p2;
+            ctx->obuf_off = 0;
+            ctx->obuf_len = 0;
+            ctx->obuf_size = obs;
+        }
+        break;
+    case BIO_C_DO_STATE_MACHINE:
+        if (b->next_bio == NULL)
+            return (0);
+        BIO_clear_retry_flags(b);
+        ret = BIO_ctrl(b->next_bio, cmd, num, ptr);
+        BIO_copy_next_retry(b);
+        break;
+
+    case BIO_CTRL_FLUSH:
+        if (b->next_bio == NULL)
+            return (0);
+        if (ctx->obuf_len <= 0) {
+            ret = BIO_ctrl(b->next_bio, cmd, num, ptr);
+            break;
+        }
+
+        for (;;) {
+            BIO_clear_retry_flags(b);
+            if (ctx->obuf_len > 0) {
+                r = BIO_write(b->next_bio,
+                              &(ctx->obuf[ctx->obuf_off]), ctx->obuf_len);
+                BIO_copy_next_retry(b);
+                if (r <= 0)
+                    return ((long)r);
+                ctx->obuf_off += r;
+                ctx->obuf_len -= r;
+            } else {
+                ctx->obuf_len = 0;
+                ctx->obuf_off = 0;
+                break;
+            }
+        }
+        ret = BIO_ctrl(b->next_bio, cmd, num, ptr);
+        break;
+    case BIO_CTRL_DUP:
+        dbio = (BIO *)ptr;
+        if (!BIO_set_read_buffer_size(dbio, ctx->ibuf_size) ||
+            !BIO_set_write_buffer_size(dbio, ctx->obuf_size))
+            ret = 0;
+        break;
+    default:
+        if (b->next_bio == NULL)
+            return (0);
+        ret = BIO_ctrl(b->next_bio, cmd, num, ptr);
+        break;
+    }
+    return (ret);
+ malloc_error:
+    BIOerr(BIO_F_BUFFER_CTRL, ERR_R_MALLOC_FAILURE);
+    return (0);
+}
+
+static long buffer_callback_ctrl(BIO *b, int cmd, bio_info_cb *fp)
+{
+    long ret = 1;
+
+    if (b->next_bio == NULL)
+        return (0);
+    switch (cmd) {
+    default:
+        ret = BIO_callback_ctrl(b->next_bio, cmd, fp);
+        break;
+    }
+    return (ret);
+}
+
+static int buffer_gets(BIO *b, char *buf, int size)
+{
+    BIO_F_BUFFER_CTX *ctx;
+    int num = 0, i, flag;
+    char *p;
+
+    ctx = (BIO_F_BUFFER_CTX *)b->ptr;
+    size--;                     /* reserve space for a '\0' */
+    BIO_clear_retry_flags(b);
+
+    for (;;) {
+        if (ctx->ibuf_len > 0) {
+            p = &(ctx->ibuf[ctx->ibuf_off]);
+            flag = 0;
+            for (i = 0; (i < ctx->ibuf_len) && (i < size); i++) {
+                *(buf++) = p[i];
+                if (p[i] == '\n') {
+                    flag = 1;
+                    i++;
+                    break;
+                }
+            }
+            num += i;
+            size -= i;
+            ctx->ibuf_len -= i;
+            ctx->ibuf_off += i;
+            if (flag || size == 0) {
+                *buf = '\0';
+                return (num);
+            }
+        } else {                /* read another chunk */
+
+            i = BIO_read(b->next_bio, ctx->ibuf, ctx->ibuf_size);
+            if (i <= 0) {
+                BIO_copy_next_retry(b);
+                *buf = '\0';
+                if (i < 0)
+                    return ((num > 0) ? num : i);
+                if (i == 0)
+                    return (num);
+            }
+            ctx->ibuf_len = i;
+            ctx->ibuf_off = 0;
+        }
+    }
+}
+
+static int buffer_puts(BIO *b, const char *str)
+{
+    return (buffer_write(b, str, strlen(str)));
+}
diff --git a/crypto/bio/bf_lbuf.c b/crypto/bio/bf_lbuf.c
new file mode 100644
index 000000000..b3c2b5eeb
--- /dev/null
+++ b/crypto/bio/bf_lbuf.c
@@ -0,0 +1,319 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include "bio_lcl.h"
+#include "internal/cryptlib.h"
+#include 
+
+static int linebuffer_write(BIO *h, const char *buf, int num);
+static int linebuffer_read(BIO *h, char *buf, int size);
+static int linebuffer_puts(BIO *h, const char *str);
+static int linebuffer_gets(BIO *h, char *str, int size);
+static long linebuffer_ctrl(BIO *h, int cmd, long arg1, void *arg2);
+static int linebuffer_new(BIO *h);
+static int linebuffer_free(BIO *data);
+static long linebuffer_callback_ctrl(BIO *h, int cmd, bio_info_cb *fp);
+
+/* A 10k maximum should be enough for most purposes */
+#define DEFAULT_LINEBUFFER_SIZE 1024*10
+
+/* #define DEBUG */
+
+static const BIO_METHOD methods_linebuffer = {
+    BIO_TYPE_LINEBUFFER,
+    "linebuffer",
+    linebuffer_write,
+    linebuffer_read,
+    linebuffer_puts,
+    linebuffer_gets,
+    linebuffer_ctrl,
+    linebuffer_new,
+    linebuffer_free,
+    linebuffer_callback_ctrl,
+};
+
+const BIO_METHOD *BIO_f_linebuffer(void)
+{
+    return (&methods_linebuffer);
+}
+
+typedef struct bio_linebuffer_ctx_struct {
+    char *obuf;                 /* the output char array */
+    int obuf_size;              /* how big is the output buffer */
+    int obuf_len;               /* how many bytes are in it */
+} BIO_LINEBUFFER_CTX;
+
+static int linebuffer_new(BIO *bi)
+{
+    BIO_LINEBUFFER_CTX *ctx;
+
+    ctx = OPENSSL_malloc(sizeof(*ctx));
+    if (ctx == NULL)
+        return (0);
+    ctx->obuf = OPENSSL_malloc(DEFAULT_LINEBUFFER_SIZE);
+    if (ctx->obuf == NULL) {
+        OPENSSL_free(ctx);
+        return (0);
+    }
+    ctx->obuf_size = DEFAULT_LINEBUFFER_SIZE;
+    ctx->obuf_len = 0;
+
+    bi->init = 1;
+    bi->ptr = (char *)ctx;
+    bi->flags = 0;
+    return (1);
+}
+
+static int linebuffer_free(BIO *a)
+{
+    BIO_LINEBUFFER_CTX *b;
+
+    if (a == NULL)
+        return (0);
+    b = (BIO_LINEBUFFER_CTX *)a->ptr;
+    OPENSSL_free(b->obuf);
+    OPENSSL_free(a->ptr);
+    a->ptr = NULL;
+    a->init = 0;
+    a->flags = 0;
+    return (1);
+}
+
+static int linebuffer_read(BIO *b, char *out, int outl)
+{
+    int ret = 0;
+
+    if (out == NULL)
+        return (0);
+    if (b->next_bio == NULL)
+        return (0);
+    ret = BIO_read(b->next_bio, out, outl);
+    BIO_clear_retry_flags(b);
+    BIO_copy_next_retry(b);
+    return (ret);
+}
+
+static int linebuffer_write(BIO *b, const char *in, int inl)
+{
+    int i, num = 0, foundnl;
+    BIO_LINEBUFFER_CTX *ctx;
+
+    if ((in == NULL) || (inl <= 0))
+        return (0);
+    ctx = (BIO_LINEBUFFER_CTX *)b->ptr;
+    if ((ctx == NULL) || (b->next_bio == NULL))
+        return (0);
+
+    BIO_clear_retry_flags(b);
+
+    do {
+        const char *p;
+
+        for (p = in; p < in + inl && *p != '\n'; p++) ;
+        if (*p == '\n') {
+            p++;
+            foundnl = 1;
+        } else
+            foundnl = 0;
+
+        /*
+         * If a NL was found and we already have text in the save buffer,
+         * concatenate them and write
+         */
+        while ((foundnl || p - in > ctx->obuf_size - ctx->obuf_len)
+               && ctx->obuf_len > 0) {
+            int orig_olen = ctx->obuf_len;
+
+            i = ctx->obuf_size - ctx->obuf_len;
+            if (p - in > 0) {
+                if (i >= p - in) {
+                    memcpy(&(ctx->obuf[ctx->obuf_len]), in, p - in);
+                    ctx->obuf_len += p - in;
+                    inl -= p - in;
+                    num += p - in;
+                    in = p;
+                } else {
+                    memcpy(&(ctx->obuf[ctx->obuf_len]), in, i);
+                    ctx->obuf_len += i;
+                    inl -= i;
+                    in += i;
+                    num += i;
+                }
+            }
+            i = BIO_write(b->next_bio, ctx->obuf, ctx->obuf_len);
+            if (i <= 0) {
+                ctx->obuf_len = orig_olen;
+                BIO_copy_next_retry(b);
+
+                if (i < 0)
+                    return ((num > 0) ? num : i);
+                if (i == 0)
+                    return (num);
+            }
+            if (i < ctx->obuf_len)
+                memmove(ctx->obuf, ctx->obuf + i, ctx->obuf_len - i);
+            ctx->obuf_len -= i;
+        }
+
+        /*
+         * Now that the save buffer is emptied, let's write the input buffer
+         * if a NL was found and there is anything to write.
+         */
+        if ((foundnl || p - in > ctx->obuf_size) && p - in > 0) {
+            i = BIO_write(b->next_bio, in, p - in);
+            if (i <= 0) {
+                BIO_copy_next_retry(b);
+                if (i < 0)
+                    return ((num > 0) ? num : i);
+                if (i == 0)
+                    return (num);
+            }
+            num += i;
+            in += i;
+            inl -= i;
+        }
+    }
+    while (foundnl && inl > 0);
+    /*
+     * We've written as much as we can.  The rest of the input buffer, if
+     * any, is text that doesn't and with a NL and therefore needs to be
+     * saved for the next trip.
+     */
+    if (inl > 0) {
+        memcpy(&(ctx->obuf[ctx->obuf_len]), in, inl);
+        ctx->obuf_len += inl;
+        num += inl;
+    }
+    return num;
+}
+
+static long linebuffer_ctrl(BIO *b, int cmd, long num, void *ptr)
+{
+    BIO *dbio;
+    BIO_LINEBUFFER_CTX *ctx;
+    long ret = 1;
+    char *p;
+    int r;
+    int obs;
+
+    ctx = (BIO_LINEBUFFER_CTX *)b->ptr;
+
+    switch (cmd) {
+    case BIO_CTRL_RESET:
+        ctx->obuf_len = 0;
+        if (b->next_bio == NULL)
+            return (0);
+        ret = BIO_ctrl(b->next_bio, cmd, num, ptr);
+        break;
+    case BIO_CTRL_INFO:
+        ret = (long)ctx->obuf_len;
+        break;
+    case BIO_CTRL_WPENDING:
+        ret = (long)ctx->obuf_len;
+        if (ret == 0) {
+            if (b->next_bio == NULL)
+                return (0);
+            ret = BIO_ctrl(b->next_bio, cmd, num, ptr);
+        }
+        break;
+    case BIO_C_SET_BUFF_SIZE:
+        obs = (int)num;
+        p = ctx->obuf;
+        if ((obs > DEFAULT_LINEBUFFER_SIZE) && (obs != ctx->obuf_size)) {
+            p = OPENSSL_malloc((int)num);
+            if (p == NULL)
+                goto malloc_error;
+        }
+        if (ctx->obuf != p) {
+            if (ctx->obuf_len > obs) {
+                ctx->obuf_len = obs;
+            }
+            memcpy(p, ctx->obuf, ctx->obuf_len);
+            OPENSSL_free(ctx->obuf);
+            ctx->obuf = p;
+            ctx->obuf_size = obs;
+        }
+        break;
+    case BIO_C_DO_STATE_MACHINE:
+        if (b->next_bio == NULL)
+            return (0);
+        BIO_clear_retry_flags(b);
+        ret = BIO_ctrl(b->next_bio, cmd, num, ptr);
+        BIO_copy_next_retry(b);
+        break;
+
+    case BIO_CTRL_FLUSH:
+        if (b->next_bio == NULL)
+            return (0);
+        if (ctx->obuf_len <= 0) {
+            ret = BIO_ctrl(b->next_bio, cmd, num, ptr);
+            break;
+        }
+
+        for (;;) {
+            BIO_clear_retry_flags(b);
+            if (ctx->obuf_len > 0) {
+                r = BIO_write(b->next_bio, ctx->obuf, ctx->obuf_len);
+                BIO_copy_next_retry(b);
+                if (r <= 0)
+                    return ((long)r);
+                if (r < ctx->obuf_len)
+                    memmove(ctx->obuf, ctx->obuf + r, ctx->obuf_len - r);
+                ctx->obuf_len -= r;
+            } else {
+                ctx->obuf_len = 0;
+                break;
+            }
+        }
+        ret = BIO_ctrl(b->next_bio, cmd, num, ptr);
+        break;
+    case BIO_CTRL_DUP:
+        dbio = (BIO *)ptr;
+        if (!BIO_set_write_buffer_size(dbio, ctx->obuf_size))
+            ret = 0;
+        break;
+    default:
+        if (b->next_bio == NULL)
+            return (0);
+        ret = BIO_ctrl(b->next_bio, cmd, num, ptr);
+        break;
+    }
+    return (ret);
+ malloc_error:
+    BIOerr(BIO_F_LINEBUFFER_CTRL, ERR_R_MALLOC_FAILURE);
+    return (0);
+}
+
+static long linebuffer_callback_ctrl(BIO *b, int cmd, bio_info_cb *fp)
+{
+    long ret = 1;
+
+    if (b->next_bio == NULL)
+        return (0);
+    switch (cmd) {
+    default:
+        ret = BIO_callback_ctrl(b->next_bio, cmd, fp);
+        break;
+    }
+    return (ret);
+}
+
+static int linebuffer_gets(BIO *b, char *buf, int size)
+{
+    if (b->next_bio == NULL)
+        return (0);
+    return (BIO_gets(b->next_bio, buf, size));
+}
+
+static int linebuffer_puts(BIO *b, const char *str)
+{
+    return (linebuffer_write(b, str, strlen(str)));
+}
diff --git a/crypto/bio/bf_nbio.c b/crypto/bio/bf_nbio.c
new file mode 100644
index 000000000..364d9fb5a
--- /dev/null
+++ b/crypto/bio/bf_nbio.c
@@ -0,0 +1,194 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include "bio_lcl.h"
+#include "internal/cryptlib.h"
+#include 
+
+/*
+ * BIO_put and BIO_get both add to the digest, BIO_gets returns the digest
+ */
+
+static int nbiof_write(BIO *h, const char *buf, int num);
+static int nbiof_read(BIO *h, char *buf, int size);
+static int nbiof_puts(BIO *h, const char *str);
+static int nbiof_gets(BIO *h, char *str, int size);
+static long nbiof_ctrl(BIO *h, int cmd, long arg1, void *arg2);
+static int nbiof_new(BIO *h);
+static int nbiof_free(BIO *data);
+static long nbiof_callback_ctrl(BIO *h, int cmd, bio_info_cb *fp);
+typedef struct nbio_test_st {
+    /* only set if we sent a 'should retry' error */
+    int lrn;
+    int lwn;
+} NBIO_TEST;
+
+static const BIO_METHOD methods_nbiof = {
+    BIO_TYPE_NBIO_TEST,
+    "non-blocking IO test filter",
+    nbiof_write,
+    nbiof_read,
+    nbiof_puts,
+    nbiof_gets,
+    nbiof_ctrl,
+    nbiof_new,
+    nbiof_free,
+    nbiof_callback_ctrl,
+};
+
+const BIO_METHOD *BIO_f_nbio_test(void)
+{
+    return (&methods_nbiof);
+}
+
+static int nbiof_new(BIO *bi)
+{
+    NBIO_TEST *nt;
+
+    if ((nt = OPENSSL_zalloc(sizeof(*nt))) == NULL)
+        return (0);
+    nt->lrn = -1;
+    nt->lwn = -1;
+    bi->ptr = (char *)nt;
+    bi->init = 1;
+    return (1);
+}
+
+static int nbiof_free(BIO *a)
+{
+    if (a == NULL)
+        return (0);
+    OPENSSL_free(a->ptr);
+    a->ptr = NULL;
+    a->init = 0;
+    a->flags = 0;
+    return (1);
+}
+
+static int nbiof_read(BIO *b, char *out, int outl)
+{
+    int ret = 0;
+    int num;
+    unsigned char n;
+
+    if (out == NULL)
+        return (0);
+    if (b->next_bio == NULL)
+        return (0);
+
+    BIO_clear_retry_flags(b);
+    if (RAND_bytes(&n, 1) <= 0)
+        return -1;
+    num = (n & 0x07);
+
+    if (outl > num)
+        outl = num;
+
+    if (num == 0) {
+        ret = -1;
+        BIO_set_retry_read(b);
+    } else {
+        ret = BIO_read(b->next_bio, out, outl);
+        if (ret < 0)
+            BIO_copy_next_retry(b);
+    }
+    return (ret);
+}
+
+static int nbiof_write(BIO *b, const char *in, int inl)
+{
+    NBIO_TEST *nt;
+    int ret = 0;
+    int num;
+    unsigned char n;
+
+    if ((in == NULL) || (inl <= 0))
+        return (0);
+    if (b->next_bio == NULL)
+        return (0);
+    nt = (NBIO_TEST *)b->ptr;
+
+    BIO_clear_retry_flags(b);
+
+    if (nt->lwn > 0) {
+        num = nt->lwn;
+        nt->lwn = 0;
+    } else {
+        if (RAND_bytes(&n, 1) <= 0)
+            return -1;
+        num = (n & 7);
+    }
+
+    if (inl > num)
+        inl = num;
+
+    if (num == 0) {
+        ret = -1;
+        BIO_set_retry_write(b);
+    } else {
+        ret = BIO_write(b->next_bio, in, inl);
+        if (ret < 0) {
+            BIO_copy_next_retry(b);
+            nt->lwn = inl;
+        }
+    }
+    return (ret);
+}
+
+static long nbiof_ctrl(BIO *b, int cmd, long num, void *ptr)
+{
+    long ret;
+
+    if (b->next_bio == NULL)
+        return (0);
+    switch (cmd) {
+    case BIO_C_DO_STATE_MACHINE:
+        BIO_clear_retry_flags(b);
+        ret = BIO_ctrl(b->next_bio, cmd, num, ptr);
+        BIO_copy_next_retry(b);
+        break;
+    case BIO_CTRL_DUP:
+        ret = 0L;
+        break;
+    default:
+        ret = BIO_ctrl(b->next_bio, cmd, num, ptr);
+        break;
+    }
+    return (ret);
+}
+
+static long nbiof_callback_ctrl(BIO *b, int cmd, bio_info_cb *fp)
+{
+    long ret = 1;
+
+    if (b->next_bio == NULL)
+        return (0);
+    switch (cmd) {
+    default:
+        ret = BIO_callback_ctrl(b->next_bio, cmd, fp);
+        break;
+    }
+    return (ret);
+}
+
+static int nbiof_gets(BIO *bp, char *buf, int size)
+{
+    if (bp->next_bio == NULL)
+        return (0);
+    return (BIO_gets(bp->next_bio, buf, size));
+}
+
+static int nbiof_puts(BIO *bp, const char *str)
+{
+    if (bp->next_bio == NULL)
+        return (0);
+    return (BIO_puts(bp->next_bio, str));
+}
diff --git a/crypto/bio/bf_null.c b/crypto/bio/bf_null.c
new file mode 100644
index 000000000..0736b3f2f
--- /dev/null
+++ b/crypto/bio/bf_null.c
@@ -0,0 +1,140 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include "bio_lcl.h"
+#include "internal/cryptlib.h"
+
+/*
+ * BIO_put and BIO_get both add to the digest, BIO_gets returns the digest
+ */
+
+static int nullf_write(BIO *h, const char *buf, int num);
+static int nullf_read(BIO *h, char *buf, int size);
+static int nullf_puts(BIO *h, const char *str);
+static int nullf_gets(BIO *h, char *str, int size);
+static long nullf_ctrl(BIO *h, int cmd, long arg1, void *arg2);
+static int nullf_new(BIO *h);
+static int nullf_free(BIO *data);
+static long nullf_callback_ctrl(BIO *h, int cmd, bio_info_cb *fp);
+static const BIO_METHOD methods_nullf = {
+    BIO_TYPE_NULL_FILTER,
+    "NULL filter",
+    nullf_write,
+    nullf_read,
+    nullf_puts,
+    nullf_gets,
+    nullf_ctrl,
+    nullf_new,
+    nullf_free,
+    nullf_callback_ctrl,
+};
+
+const BIO_METHOD *BIO_f_null(void)
+{
+    return (&methods_nullf);
+}
+
+static int nullf_new(BIO *bi)
+{
+    bi->init = 1;
+    bi->ptr = NULL;
+    bi->flags = 0;
+    return (1);
+}
+
+static int nullf_free(BIO *a)
+{
+    if (a == NULL)
+        return (0);
+    /*-
+    a->ptr=NULL;
+    a->init=0;
+    a->flags=0;
+    */
+    return (1);
+}
+
+static int nullf_read(BIO *b, char *out, int outl)
+{
+    int ret = 0;
+
+    if (out == NULL)
+        return (0);
+    if (b->next_bio == NULL)
+        return (0);
+    ret = BIO_read(b->next_bio, out, outl);
+    BIO_clear_retry_flags(b);
+    BIO_copy_next_retry(b);
+    return (ret);
+}
+
+static int nullf_write(BIO *b, const char *in, int inl)
+{
+    int ret = 0;
+
+    if ((in == NULL) || (inl <= 0))
+        return (0);
+    if (b->next_bio == NULL)
+        return (0);
+    ret = BIO_write(b->next_bio, in, inl);
+    BIO_clear_retry_flags(b);
+    BIO_copy_next_retry(b);
+    return (ret);
+}
+
+static long nullf_ctrl(BIO *b, int cmd, long num, void *ptr)
+{
+    long ret;
+
+    if (b->next_bio == NULL)
+        return (0);
+    switch (cmd) {
+    case BIO_C_DO_STATE_MACHINE:
+        BIO_clear_retry_flags(b);
+        ret = BIO_ctrl(b->next_bio, cmd, num, ptr);
+        BIO_copy_next_retry(b);
+        break;
+    case BIO_CTRL_DUP:
+        ret = 0L;
+        break;
+    default:
+        ret = BIO_ctrl(b->next_bio, cmd, num, ptr);
+    }
+    return (ret);
+}
+
+static long nullf_callback_ctrl(BIO *b, int cmd, bio_info_cb *fp)
+{
+    long ret = 1;
+
+    if (b->next_bio == NULL)
+        return (0);
+    switch (cmd) {
+    default:
+        ret = BIO_callback_ctrl(b->next_bio, cmd, fp);
+        break;
+    }
+    return (ret);
+}
+
+static int nullf_gets(BIO *bp, char *buf, int size)
+{
+    if (bp->next_bio == NULL)
+        return (0);
+    return (BIO_gets(bp->next_bio, buf, size));
+}
+
+static int nullf_puts(BIO *bp, const char *str)
+{
+    if (bp->next_bio == NULL)
+        return (0);
+    return (BIO_puts(bp->next_bio, str));
+}
diff --git a/crypto/bio/bio_cb.c b/crypto/bio/bio_cb.c
new file mode 100644
index 000000000..69ea3d067
--- /dev/null
+++ b/crypto/bio/bio_cb.c
@@ -0,0 +1,99 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include 
+#include "bio_lcl.h"
+#include "internal/cryptlib.h"
+#include 
+
+long BIO_debug_callback(BIO *bio, int cmd, const char *argp,
+                        int argi, long argl, long ret)
+{
+    BIO *b;
+    char buf[256];
+    char *p;
+    long r = 1;
+    int len;
+    size_t p_maxlen;
+
+    if (BIO_CB_RETURN & cmd)
+        r = ret;
+
+    len = BIO_snprintf(buf, sizeof buf, "BIO[%p]: ", (void *)bio);
+
+    /* Ignore errors and continue printing the other information. */
+    if (len < 0)
+        len = 0;
+    p = buf + len;
+    p_maxlen = sizeof(buf) - len;
+
+    switch (cmd) {
+    case BIO_CB_FREE:
+        BIO_snprintf(p, p_maxlen, "Free - %s\n", bio->method->name);
+        break;
+    case BIO_CB_READ:
+        if (bio->method->type & BIO_TYPE_DESCRIPTOR)
+            BIO_snprintf(p, p_maxlen, "read(%d,%lu) - %s fd=%d\n",
+                         bio->num, (unsigned long)argi,
+                         bio->method->name, bio->num);
+        else
+            BIO_snprintf(p, p_maxlen, "read(%d,%lu) - %s\n",
+                         bio->num, (unsigned long)argi, bio->method->name);
+        break;
+    case BIO_CB_WRITE:
+        if (bio->method->type & BIO_TYPE_DESCRIPTOR)
+            BIO_snprintf(p, p_maxlen, "write(%d,%lu) - %s fd=%d\n",
+                         bio->num, (unsigned long)argi,
+                         bio->method->name, bio->num);
+        else
+            BIO_snprintf(p, p_maxlen, "write(%d,%lu) - %s\n",
+                         bio->num, (unsigned long)argi, bio->method->name);
+        break;
+    case BIO_CB_PUTS:
+        BIO_snprintf(p, p_maxlen, "puts() - %s\n", bio->method->name);
+        break;
+    case BIO_CB_GETS:
+        BIO_snprintf(p, p_maxlen, "gets(%lu) - %s\n", (unsigned long)argi,
+                     bio->method->name);
+        break;
+    case BIO_CB_CTRL:
+        BIO_snprintf(p, p_maxlen, "ctrl(%lu) - %s\n", (unsigned long)argi,
+                     bio->method->name);
+        break;
+    case BIO_CB_RETURN | BIO_CB_READ:
+        BIO_snprintf(p, p_maxlen, "read return %ld\n", ret);
+        break;
+    case BIO_CB_RETURN | BIO_CB_WRITE:
+        BIO_snprintf(p, p_maxlen, "write return %ld\n", ret);
+        break;
+    case BIO_CB_RETURN | BIO_CB_GETS:
+        BIO_snprintf(p, p_maxlen, "gets return %ld\n", ret);
+        break;
+    case BIO_CB_RETURN | BIO_CB_PUTS:
+        BIO_snprintf(p, p_maxlen, "puts return %ld\n", ret);
+        break;
+    case BIO_CB_RETURN | BIO_CB_CTRL:
+        BIO_snprintf(p, p_maxlen, "ctrl return %ld\n", ret);
+        break;
+    default:
+        BIO_snprintf(p, p_maxlen, "bio callback - unknown type (%d)\n", cmd);
+        break;
+    }
+
+    b = (BIO *)bio->cb_arg;
+    if (b != NULL)
+        BIO_write(b, buf, strlen(buf));
+#if !defined(OPENSSL_NO_STDIO)
+    else
+        fputs(buf, stderr);
+#endif
+    return (r);
+}
diff --git a/crypto/bio/bio_err.c b/crypto/bio/bio_err.c
new file mode 100644
index 000000000..98c90d6e5
--- /dev/null
+++ b/crypto/bio/bio_err.c
@@ -0,0 +1,125 @@
+/*
+ * Generated by util/mkerr.pl DO NOT EDIT
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include 
+
+/* BEGIN ERROR CODES */
+#ifndef OPENSSL_NO_ERR
+
+# define ERR_FUNC(func) ERR_PACK(ERR_LIB_BIO,func,0)
+# define ERR_REASON(reason) ERR_PACK(ERR_LIB_BIO,0,reason)
+
+static ERR_STRING_DATA BIO_str_functs[] = {
+    {ERR_FUNC(BIO_F_ACPT_STATE), "acpt_state"},
+    {ERR_FUNC(BIO_F_ADDR_STRINGS), "addr_strings"},
+    {ERR_FUNC(BIO_F_BIO_ACCEPT), "BIO_accept"},
+    {ERR_FUNC(BIO_F_BIO_ACCEPT_EX), "BIO_accept_ex"},
+    {ERR_FUNC(BIO_F_BIO_ADDR_NEW), "BIO_ADDR_new"},
+    {ERR_FUNC(BIO_F_BIO_CALLBACK_CTRL), "BIO_callback_ctrl"},
+    {ERR_FUNC(BIO_F_BIO_CONNECT), "BIO_connect"},
+    {ERR_FUNC(BIO_F_BIO_CTRL), "BIO_ctrl"},
+    {ERR_FUNC(BIO_F_BIO_GETS), "BIO_gets"},
+    {ERR_FUNC(BIO_F_BIO_GET_HOST_IP), "BIO_get_host_ip"},
+    {ERR_FUNC(BIO_F_BIO_GET_NEW_INDEX), "BIO_get_new_index"},
+    {ERR_FUNC(BIO_F_BIO_GET_PORT), "BIO_get_port"},
+    {ERR_FUNC(BIO_F_BIO_LISTEN), "BIO_listen"},
+    {ERR_FUNC(BIO_F_BIO_LOOKUP), "BIO_lookup"},
+    {ERR_FUNC(BIO_F_BIO_MAKE_PAIR), "bio_make_pair"},
+    {ERR_FUNC(BIO_F_BIO_NEW), "BIO_new"},
+    {ERR_FUNC(BIO_F_BIO_NEW_FILE), "BIO_new_file"},
+    {ERR_FUNC(BIO_F_BIO_NEW_MEM_BUF), "BIO_new_mem_buf"},
+    {ERR_FUNC(BIO_F_BIO_NREAD), "BIO_nread"},
+    {ERR_FUNC(BIO_F_BIO_NREAD0), "BIO_nread0"},
+    {ERR_FUNC(BIO_F_BIO_NWRITE), "BIO_nwrite"},
+    {ERR_FUNC(BIO_F_BIO_NWRITE0), "BIO_nwrite0"},
+    {ERR_FUNC(BIO_F_BIO_PARSE_HOSTSERV), "BIO_parse_hostserv"},
+    {ERR_FUNC(BIO_F_BIO_PUTS), "BIO_puts"},
+    {ERR_FUNC(BIO_F_BIO_READ), "BIO_read"},
+    {ERR_FUNC(BIO_F_BIO_SOCKET), "BIO_socket"},
+    {ERR_FUNC(BIO_F_BIO_SOCKET_NBIO), "BIO_socket_nbio"},
+    {ERR_FUNC(BIO_F_BIO_SOCK_INFO), "BIO_sock_info"},
+    {ERR_FUNC(BIO_F_BIO_SOCK_INIT), "BIO_sock_init"},
+    {ERR_FUNC(BIO_F_BIO_WRITE), "BIO_write"},
+    {ERR_FUNC(BIO_F_BUFFER_CTRL), "buffer_ctrl"},
+    {ERR_FUNC(BIO_F_CONN_CTRL), "conn_ctrl"},
+    {ERR_FUNC(BIO_F_CONN_STATE), "conn_state"},
+    {ERR_FUNC(BIO_F_DGRAM_SCTP_READ), "dgram_sctp_read"},
+    {ERR_FUNC(BIO_F_DGRAM_SCTP_WRITE), "dgram_sctp_write"},
+    {ERR_FUNC(BIO_F_FILE_CTRL), "file_ctrl"},
+    {ERR_FUNC(BIO_F_FILE_READ), "file_read"},
+    {ERR_FUNC(BIO_F_LINEBUFFER_CTRL), "linebuffer_ctrl"},
+    {ERR_FUNC(BIO_F_MEM_WRITE), "mem_write"},
+    {ERR_FUNC(BIO_F_SSL_NEW), "SSL_new"},
+    {0, NULL}
+};
+
+static ERR_STRING_DATA BIO_str_reasons[] = {
+    {ERR_REASON(BIO_R_ACCEPT_ERROR), "accept error"},
+    {ERR_REASON(BIO_R_ADDRINFO_ADDR_IS_NOT_AF_INET),
+     "addrinfo addr is not af inet"},
+    {ERR_REASON(BIO_R_AMBIGUOUS_HOST_OR_SERVICE),
+     "ambiguous host or service"},
+    {ERR_REASON(BIO_R_BAD_FOPEN_MODE), "bad fopen mode"},
+    {ERR_REASON(BIO_R_BROKEN_PIPE), "broken pipe"},
+    {ERR_REASON(BIO_R_CONNECT_ERROR), "connect error"},
+    {ERR_REASON(BIO_R_GETHOSTBYNAME_ADDR_IS_NOT_AF_INET),
+     "gethostbyname addr is not af inet"},
+    {ERR_REASON(BIO_R_GETSOCKNAME_ERROR), "getsockname error"},
+    {ERR_REASON(BIO_R_GETSOCKNAME_TRUNCATED_ADDRESS),
+     "getsockname truncated address"},
+    {ERR_REASON(BIO_R_GETTING_SOCKTYPE), "getting socktype"},
+    {ERR_REASON(BIO_R_INVALID_ARGUMENT), "invalid argument"},
+    {ERR_REASON(BIO_R_INVALID_SOCKET), "invalid socket"},
+    {ERR_REASON(BIO_R_IN_USE), "in use"},
+    {ERR_REASON(BIO_R_LISTEN_V6_ONLY), "listen v6 only"},
+    {ERR_REASON(BIO_R_LOOKUP_RETURNED_NOTHING), "lookup returned nothing"},
+    {ERR_REASON(BIO_R_MALFORMED_HOST_OR_SERVICE),
+     "malformed host or service"},
+    {ERR_REASON(BIO_R_NBIO_CONNECT_ERROR), "nbio connect error"},
+    {ERR_REASON(BIO_R_NO_ACCEPT_ADDR_OR_SERVICE_SPECIFIED),
+     "no accept addr or service specified"},
+    {ERR_REASON(BIO_R_NO_HOSTNAME_OR_SERVICE_SPECIFIED),
+     "no hostname or service specified"},
+    {ERR_REASON(BIO_R_NO_PORT_DEFINED), "no port defined"},
+    {ERR_REASON(BIO_R_NO_SUCH_FILE), "no such file"},
+    {ERR_REASON(BIO_R_NULL_PARAMETER), "null parameter"},
+    {ERR_REASON(BIO_R_UNABLE_TO_BIND_SOCKET), "unable to bind socket"},
+    {ERR_REASON(BIO_R_UNABLE_TO_CREATE_SOCKET), "unable to create socket"},
+    {ERR_REASON(BIO_R_UNABLE_TO_KEEPALIVE), "unable to keepalive"},
+    {ERR_REASON(BIO_R_UNABLE_TO_LISTEN_SOCKET), "unable to listen socket"},
+    {ERR_REASON(BIO_R_UNABLE_TO_NODELAY), "unable to nodelay"},
+    {ERR_REASON(BIO_R_UNABLE_TO_REUSEADDR), "unable to reuseaddr"},
+    {ERR_REASON(BIO_R_UNAVAILABLE_IP_FAMILY), "unavailable ip family"},
+    {ERR_REASON(BIO_R_UNINITIALIZED), "uninitialized"},
+    {ERR_REASON(BIO_R_UNKNOWN_INFO_TYPE), "unknown info type"},
+    {ERR_REASON(BIO_R_UNSUPPORTED_IP_FAMILY), "unsupported ip family"},
+    {ERR_REASON(BIO_R_UNSUPPORTED_METHOD), "unsupported method"},
+    {ERR_REASON(BIO_R_UNSUPPORTED_PROTOCOL_FAMILY),
+     "unsupported protocol family"},
+    {ERR_REASON(BIO_R_WRITE_TO_READ_ONLY_BIO), "write to read only BIO"},
+    {ERR_REASON(BIO_R_WSASTARTUP), "WSAStartup"},
+    {0, NULL}
+};
+
+#endif
+
+int ERR_load_BIO_strings(void)
+{
+#ifndef OPENSSL_NO_ERR
+
+    if (ERR_func_error_string(BIO_str_functs[0].error) == NULL) {
+        ERR_load_strings(0, BIO_str_functs);
+        ERR_load_strings(0, BIO_str_reasons);
+    }
+#endif
+    return 1;
+}
diff --git a/crypto/bio/bio_lcl.h b/crypto/bio/bio_lcl.h
new file mode 100644
index 000000000..39178cf50
--- /dev/null
+++ b/crypto/bio/bio_lcl.h
@@ -0,0 +1,188 @@
+/*
+ * Copyright 2005-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#define USE_SOCKETS
+#include "e_os.h"
+
+/* BEGIN BIO_ADDRINFO/BIO_ADDR stuff. */
+
+#ifndef OPENSSL_NO_SOCK
+/*
+ * Throughout this file and b_addr.c, the existence of the macro
+ * AI_PASSIVE is used to detect the availability of struct addrinfo,
+ * getnameinfo() and getaddrinfo().  If that macro doesn't exist,
+ * we use our own implementation instead.
+ */
+
+/*
+ * It's imperative that these macros get defined before openssl/bio.h gets
+ * included.  Otherwise, the AI_PASSIVE hack will not work properly.
+ * For clarity, we check for internal/cryptlib.h since it's a common header
+ * that also includes bio.h.
+ */
+# ifdef HEADER_CRYPTLIB_H
+#  error internal/cryptlib.h included before bio_lcl.h
+# endif
+# ifdef HEADER_BIO_H
+#  error openssl/bio.h included before bio_lcl.h
+# endif
+
+/*
+ * Undefine AF_UNIX on systems that define it but don't support it.
+ */
+# if defined(OPENSSL_SYS_WINDOWS) || defined(OPENSSL_SYS_VMS)
+#  undef AF_UNIX
+# endif
+
+# ifdef AI_PASSIVE
+
+/*
+ * There's a bug in VMS C header file netdb.h, where struct addrinfo
+ * always is the P32 variant, but the functions that handle that structure,
+ * such as getaddrinfo() and freeaddrinfo() adapt to the initial pointer
+ * size.  The easiest workaround is to force struct addrinfo to be the
+ * 64-bit variant when compiling in P64 mode.
+ */
+#  if defined(OPENSSL_SYS_VMS) && __INITIAL_POINTER_SIZE == 64
+#   define addrinfo __addrinfo64
+#  endif
+
+#  define bio_addrinfo_st addrinfo
+#  define bai_family      ai_family
+#  define bai_socktype    ai_socktype
+#  define bai_protocol    ai_protocol
+#  define bai_addrlen     ai_addrlen
+#  define bai_addr        ai_addr
+#  define bai_next        ai_next
+# else
+struct bio_addrinfo_st {
+    int bai_family;
+    int bai_socktype;
+    int bai_protocol;
+    size_t bai_addrlen;
+    struct sockaddr *bai_addr;
+    struct bio_addrinfo_st *bai_next;
+};
+# endif
+
+union bio_addr_st {
+    struct sockaddr sa;
+# ifdef AF_INET6
+    struct sockaddr_in6 s_in6;
+# endif
+    struct sockaddr_in s_in;
+# ifdef AF_UNIX
+    struct sockaddr_un s_un;
+# endif
+};
+#endif
+
+/* END BIO_ADDRINFO/BIO_ADDR stuff. */
+
+#include "internal/cryptlib.h"
+#include 
+
+typedef struct bio_f_buffer_ctx_struct {
+    /*-
+     * Buffers are setup like this:
+     *
+     * <---------------------- size ----------------------->
+     * +---------------------------------------------------+
+     * | consumed | remaining          | free space        |
+     * +---------------------------------------------------+
+     * <-- off --><------- len ------->
+     */
+    /*- BIO *bio; *//*
+     * this is now in the BIO struct
+     */
+    int ibuf_size;              /* how big is the input buffer */
+    int obuf_size;              /* how big is the output buffer */
+    char *ibuf;                 /* the char array */
+    int ibuf_len;               /* how many bytes are in it */
+    int ibuf_off;               /* write/read offset */
+    char *obuf;                 /* the char array */
+    int obuf_len;               /* how many bytes are in it */
+    int obuf_off;               /* write/read offset */
+} BIO_F_BUFFER_CTX;
+
+struct bio_st {
+    const BIO_METHOD *method;
+    /* bio, mode, argp, argi, argl, ret */
+    long (*callback) (struct bio_st *, int, const char *, int, long, long);
+    char *cb_arg;               /* first argument for the callback */
+    int init;
+    int shutdown;
+    int flags;                  /* extra storage */
+    int retry_reason;
+    int num;
+    void *ptr;
+    struct bio_st *next_bio;    /* used by filter BIOs */
+    struct bio_st *prev_bio;    /* used by filter BIOs */
+    int references;
+    uint64_t num_read;
+    uint64_t num_write;
+    CRYPTO_EX_DATA ex_data;
+    CRYPTO_RWLOCK *lock;
+};
+
+#ifndef OPENSSL_NO_SOCK
+# ifdef OPENSSL_SYS_VMS
+typedef unsigned int socklen_t;
+# endif
+
+extern CRYPTO_RWLOCK *bio_lookup_lock;
+
+int BIO_ADDR_make(BIO_ADDR *ap, const struct sockaddr *sa);
+const struct sockaddr *BIO_ADDR_sockaddr(const BIO_ADDR *ap);
+struct sockaddr *BIO_ADDR_sockaddr_noconst(BIO_ADDR *ap);
+socklen_t BIO_ADDR_sockaddr_size(const BIO_ADDR *ap);
+socklen_t BIO_ADDRINFO_sockaddr_size(const BIO_ADDRINFO *bai);
+const struct sockaddr *BIO_ADDRINFO_sockaddr(const BIO_ADDRINFO *bai);
+#endif
+
+extern CRYPTO_RWLOCK *bio_type_lock;
+
+void bio_sock_cleanup_int(void);
+
+#if BIO_FLAGS_UPLINK==0
+/* Shortcut UPLINK calls on most platforms... */
+# define UP_stdin        stdin
+# define UP_stdout       stdout
+# define UP_stderr       stderr
+# define UP_fprintf      fprintf
+# define UP_fgets        fgets
+# define UP_fread        fread
+# define UP_fwrite       fwrite
+# undef  UP_fsetmod
+# define UP_feof         feof
+# define UP_fclose       fclose
+
+# define UP_fopen        fopen
+# define UP_fseek        fseek
+# define UP_ftell        ftell
+# define UP_fflush       fflush
+# define UP_ferror       ferror
+# ifdef _WIN32
+#  define UP_fileno       _fileno
+#  define UP_open         _open
+#  define UP_read         _read
+#  define UP_write        _write
+#  define UP_lseek        _lseek
+#  define UP_close        _close
+# else
+#  define UP_fileno       fileno
+#  define UP_open         open
+#  define UP_read         read
+#  define UP_write        write
+#  define UP_lseek        lseek
+#  define UP_close        close
+# endif
+
+#endif
+
diff --git a/crypto/bio/bio_lib.c b/crypto/bio/bio_lib.c
new file mode 100644
index 000000000..62392c3a5
--- /dev/null
+++ b/crypto/bio/bio_lib.c
@@ -0,0 +1,600 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include 
+#include "bio_lcl.h"
+#include "internal/cryptlib.h"
+
+BIO *BIO_new(const BIO_METHOD *method)
+{
+    BIO *bio = OPENSSL_zalloc(sizeof(*bio));
+
+    if (bio == NULL) {
+        BIOerr(BIO_F_BIO_NEW, ERR_R_MALLOC_FAILURE);
+        return (NULL);
+    }
+
+    bio->method = method;
+    bio->shutdown = 1;
+    bio->references = 1;
+
+    if (!CRYPTO_new_ex_data(CRYPTO_EX_INDEX_BIO, bio, &bio->ex_data))
+        goto err;
+
+    bio->lock = CRYPTO_THREAD_lock_new();
+    if (bio->lock == NULL) {
+        BIOerr(BIO_F_BIO_NEW, ERR_R_MALLOC_FAILURE);
+        CRYPTO_free_ex_data(CRYPTO_EX_INDEX_BIO, bio, &bio->ex_data);
+        goto err;
+    }
+
+    if (method->create != NULL && !method->create(bio)) {
+        BIOerr(BIO_F_BIO_NEW, ERR_R_INIT_FAIL);
+        CRYPTO_free_ex_data(CRYPTO_EX_INDEX_BIO, bio, &bio->ex_data);
+        CRYPTO_THREAD_lock_free(bio->lock);
+        goto err;
+    }
+
+    return bio;
+
+err:
+    OPENSSL_free(bio);
+    return NULL;
+}
+
+int BIO_free(BIO *a)
+{
+    int i;
+
+    if (a == NULL)
+        return 0;
+
+    if (CRYPTO_atomic_add(&a->references, -1, &i, a->lock) <= 0)
+        return 0;
+
+    REF_PRINT_COUNT("BIO", a);
+    if (i > 0)
+        return 1;
+    REF_ASSERT_ISNT(i < 0);
+    if ((a->callback != NULL) &&
+        ((i = (int)a->callback(a, BIO_CB_FREE, NULL, 0, 0L, 1L)) <= 0))
+        return i;
+
+    if ((a->method != NULL) && (a->method->destroy != NULL))
+        a->method->destroy(a);
+
+    CRYPTO_free_ex_data(CRYPTO_EX_INDEX_BIO, a, &a->ex_data);
+
+    CRYPTO_THREAD_lock_free(a->lock);
+
+    OPENSSL_free(a);
+
+    return 1;
+}
+
+void BIO_set_data(BIO *a, void *ptr)
+{
+    a->ptr = ptr;
+}
+
+void *BIO_get_data(BIO *a)
+{
+    return a->ptr;
+}
+
+void BIO_set_init(BIO *a, int init)
+{
+    a->init = init;
+}
+
+int BIO_get_init(BIO *a)
+{
+    return a->init;
+}
+
+void BIO_set_shutdown(BIO *a, int shut)
+{
+    a->shutdown = shut;
+}
+
+int BIO_get_shutdown(BIO *a)
+{
+    return a->shutdown;
+}
+
+void BIO_vfree(BIO *a)
+{
+    BIO_free(a);
+}
+
+int BIO_up_ref(BIO *a)
+{
+    int i;
+
+    if (CRYPTO_atomic_add(&a->references, 1, &i, a->lock) <= 0)
+        return 0;
+
+    REF_PRINT_COUNT("BIO", a);
+    REF_ASSERT_ISNT(i < 2);
+    return ((i > 1) ? 1 : 0);
+}
+
+void BIO_clear_flags(BIO *b, int flags)
+{
+    b->flags &= ~flags;
+}
+
+int BIO_test_flags(const BIO *b, int flags)
+{
+    return (b->flags & flags);
+}
+
+void BIO_set_flags(BIO *b, int flags)
+{
+    b->flags |= flags;
+}
+
+long (*BIO_get_callback(const BIO *b)) (struct bio_st *, int, const char *,
+                                        int, long, long) {
+    return b->callback;
+}
+
+void BIO_set_callback(BIO *b,
+                      long (*cb) (struct bio_st *, int, const char *, int,
+                                  long, long))
+{
+    b->callback = cb;
+}
+
+void BIO_set_callback_arg(BIO *b, char *arg)
+{
+    b->cb_arg = arg;
+}
+
+char *BIO_get_callback_arg(const BIO *b)
+{
+    return b->cb_arg;
+}
+
+const char *BIO_method_name(const BIO *b)
+{
+    return b->method->name;
+}
+
+int BIO_method_type(const BIO *b)
+{
+    return b->method->type;
+}
+
+int BIO_read(BIO *b, void *out, int outl)
+{
+    int i;
+    long (*cb) (BIO *, int, const char *, int, long, long);
+
+    if ((b == NULL) || (b->method == NULL) || (b->method->bread == NULL)) {
+        BIOerr(BIO_F_BIO_READ, BIO_R_UNSUPPORTED_METHOD);
+        return (-2);
+    }
+
+    cb = b->callback;
+    if ((cb != NULL) &&
+        ((i = (int)cb(b, BIO_CB_READ, out, outl, 0L, 1L)) <= 0))
+        return (i);
+
+    if (!b->init) {
+        BIOerr(BIO_F_BIO_READ, BIO_R_UNINITIALIZED);
+        return (-2);
+    }
+
+    i = b->method->bread(b, out, outl);
+
+    if (i > 0)
+        b->num_read += (uint64_t)i;
+
+    if (cb != NULL)
+        i = (int)cb(b, BIO_CB_READ | BIO_CB_RETURN, out, outl, 0L, (long)i);
+    return (i);
+}
+
+int BIO_write(BIO *b, const void *in, int inl)
+{
+    int i;
+    long (*cb) (BIO *, int, const char *, int, long, long);
+
+    if (b == NULL)
+        return (0);
+
+    cb = b->callback;
+    if ((b->method == NULL) || (b->method->bwrite == NULL)) {
+        BIOerr(BIO_F_BIO_WRITE, BIO_R_UNSUPPORTED_METHOD);
+        return (-2);
+    }
+
+    if ((cb != NULL) &&
+        ((i = (int)cb(b, BIO_CB_WRITE, in, inl, 0L, 1L)) <= 0))
+        return (i);
+
+    if (!b->init) {
+        BIOerr(BIO_F_BIO_WRITE, BIO_R_UNINITIALIZED);
+        return (-2);
+    }
+
+    i = b->method->bwrite(b, in, inl);
+
+    if (i > 0)
+        b->num_write += (uint64_t)i;
+
+    if (cb != NULL)
+        i = (int)cb(b, BIO_CB_WRITE | BIO_CB_RETURN, in, inl, 0L, (long)i);
+    return (i);
+}
+
+int BIO_puts(BIO *b, const char *in)
+{
+    int i;
+    long (*cb) (BIO *, int, const char *, int, long, long);
+
+    if ((b == NULL) || (b->method == NULL) || (b->method->bputs == NULL)) {
+        BIOerr(BIO_F_BIO_PUTS, BIO_R_UNSUPPORTED_METHOD);
+        return (-2);
+    }
+
+    cb = b->callback;
+
+    if ((cb != NULL) && ((i = (int)cb(b, BIO_CB_PUTS, in, 0, 0L, 1L)) <= 0))
+        return (i);
+
+    if (!b->init) {
+        BIOerr(BIO_F_BIO_PUTS, BIO_R_UNINITIALIZED);
+        return (-2);
+    }
+
+    i = b->method->bputs(b, in);
+
+    if (i > 0)
+        b->num_write += (uint64_t)i;
+
+    if (cb != NULL)
+        i = (int)cb(b, BIO_CB_PUTS | BIO_CB_RETURN, in, 0, 0L, (long)i);
+    return (i);
+}
+
+int BIO_gets(BIO *b, char *in, int inl)
+{
+    int i;
+    long (*cb) (BIO *, int, const char *, int, long, long);
+
+    if ((b == NULL) || (b->method == NULL) || (b->method->bgets == NULL)) {
+        BIOerr(BIO_F_BIO_GETS, BIO_R_UNSUPPORTED_METHOD);
+        return (-2);
+    }
+
+    cb = b->callback;
+
+    if ((cb != NULL) && ((i = (int)cb(b, BIO_CB_GETS, in, inl, 0L, 1L)) <= 0))
+        return (i);
+
+    if (!b->init) {
+        BIOerr(BIO_F_BIO_GETS, BIO_R_UNINITIALIZED);
+        return (-2);
+    }
+
+    i = b->method->bgets(b, in, inl);
+
+    if (cb != NULL)
+        i = (int)cb(b, BIO_CB_GETS | BIO_CB_RETURN, in, inl, 0L, (long)i);
+    return (i);
+}
+
+int BIO_indent(BIO *b, int indent, int max)
+{
+    if (indent < 0)
+        indent = 0;
+    if (indent > max)
+        indent = max;
+    while (indent--)
+        if (BIO_puts(b, " ") != 1)
+            return 0;
+    return 1;
+}
+
+long BIO_int_ctrl(BIO *b, int cmd, long larg, int iarg)
+{
+    int i;
+
+    i = iarg;
+    return (BIO_ctrl(b, cmd, larg, (char *)&i));
+}
+
+void *BIO_ptr_ctrl(BIO *b, int cmd, long larg)
+{
+    void *p = NULL;
+
+    if (BIO_ctrl(b, cmd, larg, (char *)&p) <= 0)
+        return (NULL);
+    else
+        return (p);
+}
+
+long BIO_ctrl(BIO *b, int cmd, long larg, void *parg)
+{
+    long ret;
+    long (*cb) (BIO *, int, const char *, int, long, long);
+
+    if (b == NULL)
+        return (0);
+
+    if ((b->method == NULL) || (b->method->ctrl == NULL)) {
+        BIOerr(BIO_F_BIO_CTRL, BIO_R_UNSUPPORTED_METHOD);
+        return (-2);
+    }
+
+    cb = b->callback;
+
+    if ((cb != NULL) &&
+        ((ret = cb(b, BIO_CB_CTRL, parg, cmd, larg, 1L)) <= 0))
+        return (ret);
+
+    ret = b->method->ctrl(b, cmd, larg, parg);
+
+    if (cb != NULL)
+        ret = cb(b, BIO_CB_CTRL | BIO_CB_RETURN, parg, cmd, larg, ret);
+    return (ret);
+}
+
+long BIO_callback_ctrl(BIO *b, int cmd,
+                       void (*fp) (struct bio_st *, int, const char *, int,
+                                   long, long))
+{
+    long ret;
+    long (*cb) (BIO *, int, const char *, int, long, long);
+
+    if (b == NULL)
+        return (0);
+
+    if ((b->method == NULL) || (b->method->callback_ctrl == NULL)) {
+        BIOerr(BIO_F_BIO_CALLBACK_CTRL, BIO_R_UNSUPPORTED_METHOD);
+        return (-2);
+    }
+
+    cb = b->callback;
+
+    if ((cb != NULL) &&
+        ((ret = cb(b, BIO_CB_CTRL, (void *)&fp, cmd, 0, 1L)) <= 0))
+        return (ret);
+
+    ret = b->method->callback_ctrl(b, cmd, fp);
+
+    if (cb != NULL)
+        ret = cb(b, BIO_CB_CTRL | BIO_CB_RETURN, (void *)&fp, cmd, 0, ret);
+    return (ret);
+}
+
+/*
+ * It is unfortunate to duplicate in functions what the BIO_(w)pending macros
+ * do; but those macros have inappropriate return type, and for interfacing
+ * from other programming languages, C macros aren't much of a help anyway.
+ */
+size_t BIO_ctrl_pending(BIO *bio)
+{
+    return BIO_ctrl(bio, BIO_CTRL_PENDING, 0, NULL);
+}
+
+size_t BIO_ctrl_wpending(BIO *bio)
+{
+    return BIO_ctrl(bio, BIO_CTRL_WPENDING, 0, NULL);
+}
+
+/* put the 'bio' on the end of b's list of operators */
+BIO *BIO_push(BIO *b, BIO *bio)
+{
+    BIO *lb;
+
+    if (b == NULL)
+        return (bio);
+    lb = b;
+    while (lb->next_bio != NULL)
+        lb = lb->next_bio;
+    lb->next_bio = bio;
+    if (bio != NULL)
+        bio->prev_bio = lb;
+    /* called to do internal processing */
+    BIO_ctrl(b, BIO_CTRL_PUSH, 0, lb);
+    return (b);
+}
+
+/* Remove the first and return the rest */
+BIO *BIO_pop(BIO *b)
+{
+    BIO *ret;
+
+    if (b == NULL)
+        return (NULL);
+    ret = b->next_bio;
+
+    BIO_ctrl(b, BIO_CTRL_POP, 0, b);
+
+    if (b->prev_bio != NULL)
+        b->prev_bio->next_bio = b->next_bio;
+    if (b->next_bio != NULL)
+        b->next_bio->prev_bio = b->prev_bio;
+
+    b->next_bio = NULL;
+    b->prev_bio = NULL;
+    return (ret);
+}
+
+BIO *BIO_get_retry_BIO(BIO *bio, int *reason)
+{
+    BIO *b, *last;
+
+    b = last = bio;
+    for (;;) {
+        if (!BIO_should_retry(b))
+            break;
+        last = b;
+        b = b->next_bio;
+        if (b == NULL)
+            break;
+    }
+    if (reason != NULL)
+        *reason = last->retry_reason;
+    return (last);
+}
+
+int BIO_get_retry_reason(BIO *bio)
+{
+    return (bio->retry_reason);
+}
+
+void BIO_set_retry_reason(BIO *bio, int reason)
+{
+    bio->retry_reason = reason;
+}
+
+BIO *BIO_find_type(BIO *bio, int type)
+{
+    int mt, mask;
+
+    if (bio == NULL)
+        return NULL;
+    mask = type & 0xff;
+    do {
+        if (bio->method != NULL) {
+            mt = bio->method->type;
+
+            if (!mask) {
+                if (mt & type)
+                    return (bio);
+            } else if (mt == type)
+                return (bio);
+        }
+        bio = bio->next_bio;
+    } while (bio != NULL);
+    return (NULL);
+}
+
+BIO *BIO_next(BIO *b)
+{
+    if (b == NULL)
+        return NULL;
+    return b->next_bio;
+}
+
+void BIO_set_next(BIO *b, BIO *next)
+{
+    b->next_bio = next;
+}
+
+void BIO_free_all(BIO *bio)
+{
+    BIO *b;
+    int ref;
+
+    while (bio != NULL) {
+        b = bio;
+        ref = b->references;
+        bio = bio->next_bio;
+        BIO_free(b);
+        /* Since ref count > 1, don't free anyone else. */
+        if (ref > 1)
+            break;
+    }
+}
+
+BIO *BIO_dup_chain(BIO *in)
+{
+    BIO *ret = NULL, *eoc = NULL, *bio, *new_bio;
+
+    for (bio = in; bio != NULL; bio = bio->next_bio) {
+        if ((new_bio = BIO_new(bio->method)) == NULL)
+            goto err;
+        new_bio->callback = bio->callback;
+        new_bio->cb_arg = bio->cb_arg;
+        new_bio->init = bio->init;
+        new_bio->shutdown = bio->shutdown;
+        new_bio->flags = bio->flags;
+
+        /* This will let SSL_s_sock() work with stdin/stdout */
+        new_bio->num = bio->num;
+
+        if (!BIO_dup_state(bio, (char *)new_bio)) {
+            BIO_free(new_bio);
+            goto err;
+        }
+
+        /* copy app data */
+        if (!CRYPTO_dup_ex_data(CRYPTO_EX_INDEX_BIO, &new_bio->ex_data,
+                                &bio->ex_data)) {
+            BIO_free(new_bio);
+            goto err;
+        }
+
+        if (ret == NULL) {
+            eoc = new_bio;
+            ret = eoc;
+        } else {
+            BIO_push(eoc, new_bio);
+            eoc = new_bio;
+        }
+    }
+    return (ret);
+ err:
+    BIO_free_all(ret);
+
+    return (NULL);
+}
+
+void BIO_copy_next_retry(BIO *b)
+{
+    BIO_set_flags(b, BIO_get_retry_flags(b->next_bio));
+    b->retry_reason = b->next_bio->retry_reason;
+}
+
+int BIO_set_ex_data(BIO *bio, int idx, void *data)
+{
+    return (CRYPTO_set_ex_data(&(bio->ex_data), idx, data));
+}
+
+void *BIO_get_ex_data(BIO *bio, int idx)
+{
+    return (CRYPTO_get_ex_data(&(bio->ex_data), idx));
+}
+
+uint64_t BIO_number_read(BIO *bio)
+{
+    if (bio)
+        return bio->num_read;
+    return 0;
+}
+
+uint64_t BIO_number_written(BIO *bio)
+{
+    if (bio)
+        return bio->num_write;
+    return 0;
+}
+
+void bio_free_ex_data(BIO *bio)
+{
+    CRYPTO_free_ex_data(CRYPTO_EX_INDEX_BIO, bio, &bio->ex_data);
+}
+
+void bio_cleanup(void)
+{
+#ifndef OPENSSL_NO_SOCK
+    bio_sock_cleanup_int();
+    CRYPTO_THREAD_lock_free(bio_lookup_lock);
+    bio_lookup_lock = NULL;
+#endif
+    CRYPTO_THREAD_lock_free(bio_type_lock);
+    bio_type_lock = NULL;
+}
diff --git a/crypto/bio/bio_meth.c b/crypto/bio/bio_meth.c
new file mode 100644
index 000000000..c5f9f7e8e
--- /dev/null
+++ b/crypto/bio/bio_meth.c
@@ -0,0 +1,145 @@
+/*
+ * Copyright 2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "bio_lcl.h"
+#include 
+
+CRYPTO_RWLOCK *bio_type_lock = NULL;
+static CRYPTO_ONCE bio_type_init = CRYPTO_ONCE_STATIC_INIT;
+
+DEFINE_RUN_ONCE_STATIC(do_bio_type_init)
+{
+    bio_type_lock = CRYPTO_THREAD_lock_new();
+    return bio_type_lock != NULL;
+}
+
+int BIO_get_new_index()
+{
+    static int bio_count = BIO_TYPE_START;
+    int newval;
+
+    if (!RUN_ONCE(&bio_type_init, do_bio_type_init)) {
+        BIOerr(BIO_F_BIO_GET_NEW_INDEX, ERR_R_MALLOC_FAILURE);
+        return -1;
+    }
+    if (!CRYPTO_atomic_add(&bio_count, 1, &newval, bio_type_lock))
+        return -1;
+    return newval;
+}
+
+BIO_METHOD *BIO_meth_new(int type, const char *name)
+{
+    BIO_METHOD *biom = OPENSSL_zalloc(sizeof(BIO_METHOD));
+
+    if (biom != NULL) {
+        biom->type = type;
+        biom->name = name;
+    }
+    return biom;
+}
+
+void BIO_meth_free(BIO_METHOD *biom)
+{
+    OPENSSL_free(biom);
+}
+
+int (*BIO_meth_get_write(BIO_METHOD *biom)) (BIO *, const char *, int)
+{
+    return biom->bwrite;
+}
+
+int BIO_meth_set_write(BIO_METHOD *biom,
+                       int (*bwrite) (BIO *, const char *, int))
+{
+    biom->bwrite = bwrite;
+    return 1;
+}
+
+int (*BIO_meth_get_read(BIO_METHOD *biom)) (BIO *, char *, int)
+{
+    return biom->bread;
+}
+
+int BIO_meth_set_read(BIO_METHOD *biom,
+                      int (*bread) (BIO *, char *, int))
+{
+    biom->bread = bread;
+    return 1;
+}
+
+int (*BIO_meth_get_puts(BIO_METHOD *biom)) (BIO *, const char *)
+{
+    return biom->bputs;
+}
+
+int BIO_meth_set_puts(BIO_METHOD *biom,
+                      int (*bputs) (BIO *, const char *))
+{
+    biom->bputs = bputs;
+    return 1;
+}
+
+int (*BIO_meth_get_gets(BIO_METHOD *biom)) (BIO *, char *, int)
+{
+    return biom->bgets;
+}
+
+int BIO_meth_set_gets(BIO_METHOD *biom,
+                      int (*bgets) (BIO *, char *, int))
+{
+    biom->bgets = bgets;
+    return 1;
+}
+
+long (*BIO_meth_get_ctrl(BIO_METHOD *biom)) (BIO *, int, long, void *)
+{
+    return biom->ctrl;
+}
+
+int BIO_meth_set_ctrl(BIO_METHOD *biom,
+                      long (*ctrl) (BIO *, int, long, void *))
+{
+    biom->ctrl = ctrl;
+    return 1;
+}
+
+int (*BIO_meth_get_create(BIO_METHOD *biom)) (BIO *)
+{
+    return biom->create;
+}
+
+int BIO_meth_set_create(BIO_METHOD *biom, int (*create) (BIO *))
+{
+    biom->create = create;
+    return 1;
+}
+
+int (*BIO_meth_get_destroy(BIO_METHOD *biom)) (BIO *)
+{
+    return biom->destroy;
+}
+
+int BIO_meth_set_destroy(BIO_METHOD *biom, int (*destroy) (BIO *))
+{
+    biom->destroy = destroy;
+    return 1;
+}
+
+long (*BIO_meth_get_callback_ctrl(BIO_METHOD *biom)) (BIO *, int, bio_info_cb *)
+{
+    return biom->callback_ctrl;
+}
+
+int BIO_meth_set_callback_ctrl(BIO_METHOD *biom,
+                               long (*callback_ctrl) (BIO *, int,
+                                                      bio_info_cb *))
+{
+    biom->callback_ctrl = callback_ctrl;
+    return 1;
+}
diff --git a/crypto/bio/bss_acpt.c b/crypto/bio/bss_acpt.c
new file mode 100644
index 000000000..6fb971a50
--- /dev/null
+++ b/crypto/bio/bss_acpt.c
@@ -0,0 +1,557 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include "bio_lcl.h"
+
+#ifndef OPENSSL_NO_SOCK
+
+typedef struct bio_accept_st {
+    int state;
+    int accept_family;
+    int bind_mode;     /* Socket mode for BIO_listen */
+    int accepted_mode; /* Socket mode for BIO_accept (set on accepted sock) */
+    char *param_addr;
+    char *param_serv;
+
+    int accept_sock;
+
+    BIO_ADDRINFO *addr_first;
+    const BIO_ADDRINFO *addr_iter;
+    BIO_ADDR cache_accepting_addr;   /* Useful if we asked for port 0 */
+    char *cache_accepting_name, *cache_accepting_serv;
+    BIO_ADDR cache_peer_addr;
+    char *cache_peer_name, *cache_peer_serv;
+
+    BIO *bio_chain;
+} BIO_ACCEPT;
+
+static int acpt_write(BIO *h, const char *buf, int num);
+static int acpt_read(BIO *h, char *buf, int size);
+static int acpt_puts(BIO *h, const char *str);
+static long acpt_ctrl(BIO *h, int cmd, long arg1, void *arg2);
+static int acpt_new(BIO *h);
+static int acpt_free(BIO *data);
+static int acpt_state(BIO *b, BIO_ACCEPT *c);
+static void acpt_close_socket(BIO *data);
+static BIO_ACCEPT *BIO_ACCEPT_new(void);
+static void BIO_ACCEPT_free(BIO_ACCEPT *a);
+
+# define ACPT_S_BEFORE                   1
+# define ACPT_S_GET_ADDR                 2
+# define ACPT_S_CREATE_SOCKET            3
+# define ACPT_S_LISTEN                   4
+# define ACPT_S_ACCEPT                   5
+# define ACPT_S_OK                       6
+
+static const BIO_METHOD methods_acceptp = {
+    BIO_TYPE_ACCEPT,
+    "socket accept",
+    acpt_write,
+    acpt_read,
+    acpt_puts,
+    NULL,                       /* connect_gets, */
+    acpt_ctrl,
+    acpt_new,
+    acpt_free,
+    NULL,
+};
+
+const BIO_METHOD *BIO_s_accept(void)
+{
+    return (&methods_acceptp);
+}
+
+static int acpt_new(BIO *bi)
+{
+    BIO_ACCEPT *ba;
+
+    bi->init = 0;
+    bi->num = (int)INVALID_SOCKET;
+    bi->flags = 0;
+    if ((ba = BIO_ACCEPT_new()) == NULL)
+        return (0);
+    bi->ptr = (char *)ba;
+    ba->state = ACPT_S_BEFORE;
+    bi->shutdown = 1;
+    return (1);
+}
+
+static BIO_ACCEPT *BIO_ACCEPT_new(void)
+{
+    BIO_ACCEPT *ret;
+
+    if ((ret = OPENSSL_zalloc(sizeof(*ret))) == NULL)
+        return (NULL);
+    ret->accept_family = BIO_FAMILY_IPANY;
+    ret->accept_sock = (int)INVALID_SOCKET;
+    return (ret);
+}
+
+static void BIO_ACCEPT_free(BIO_ACCEPT *a)
+{
+    if (a == NULL)
+        return;
+
+    OPENSSL_free(a->param_addr);
+    OPENSSL_free(a->param_serv);
+    BIO_ADDRINFO_free(a->addr_first);
+    OPENSSL_free(a->cache_accepting_name);
+    OPENSSL_free(a->cache_accepting_serv);
+    OPENSSL_free(a->cache_peer_name);
+    OPENSSL_free(a->cache_peer_serv);
+    BIO_free(a->bio_chain);
+    OPENSSL_free(a);
+}
+
+static void acpt_close_socket(BIO *bio)
+{
+    BIO_ACCEPT *c;
+
+    c = (BIO_ACCEPT *)bio->ptr;
+    if (c->accept_sock != (int)INVALID_SOCKET) {
+        shutdown(c->accept_sock, 2);
+        closesocket(c->accept_sock);
+        c->accept_sock = (int)INVALID_SOCKET;
+        bio->num = (int)INVALID_SOCKET;
+    }
+}
+
+static int acpt_free(BIO *a)
+{
+    BIO_ACCEPT *data;
+
+    if (a == NULL)
+        return (0);
+    data = (BIO_ACCEPT *)a->ptr;
+
+    if (a->shutdown) {
+        acpt_close_socket(a);
+        BIO_ACCEPT_free(data);
+        a->ptr = NULL;
+        a->flags = 0;
+        a->init = 0;
+    }
+    return (1);
+}
+
+static int acpt_state(BIO *b, BIO_ACCEPT *c)
+{
+    BIO *bio = NULL, *dbio;
+    int s = -1, ret = -1;
+
+    for (;;) {
+        switch (c->state) {
+        case ACPT_S_BEFORE:
+            if (c->param_addr == NULL && c->param_serv == NULL) {
+                BIOerr(BIO_F_ACPT_STATE, BIO_R_NO_ACCEPT_ADDR_OR_SERVICE_SPECIFIED);
+                ERR_add_error_data(4,
+                                   "hostname=", c->param_addr,
+                                   " service=", c->param_serv);
+                goto exit_loop;
+            }
+
+            /* Because we're starting a new bind, any cached name and serv
+             * are now obsolete and need to be cleaned out.
+             * QUESTION: should this be done in acpt_close_socket() instead?
+             */
+            OPENSSL_free(c->cache_accepting_name);
+            c->cache_accepting_name = NULL;
+            OPENSSL_free(c->cache_accepting_serv);
+            c->cache_accepting_serv = NULL;
+            OPENSSL_free(c->cache_peer_name);
+            c->cache_peer_name = NULL;
+            OPENSSL_free(c->cache_peer_serv);
+            c->cache_peer_serv = NULL;
+
+            c->state = ACPT_S_GET_ADDR;
+            break;
+
+        case ACPT_S_GET_ADDR:
+            {
+                int family = AF_UNSPEC;
+                switch (c->accept_family) {
+                case BIO_FAMILY_IPV6:
+                    if (1) { /* This is a trick we use to avoid bit rot.
+                              * at least the "else" part will always be
+                              * compiled.
+                              */
+#ifdef AF_INET6
+                        family = AF_INET6;
+                    } else {
+#endif
+                        BIOerr(BIO_F_ACPT_STATE, BIO_R_UNAVAILABLE_IP_FAMILY);
+                        goto exit_loop;
+                    }
+                    break;
+                case BIO_FAMILY_IPV4:
+                    family = AF_INET;
+                    break;
+                case BIO_FAMILY_IPANY:
+                    family = AF_UNSPEC;
+                    break;
+                default:
+                    BIOerr(BIO_F_ACPT_STATE, BIO_R_UNSUPPORTED_IP_FAMILY);
+                    goto exit_loop;
+                }
+                if (BIO_lookup(c->param_addr, c->param_serv, BIO_LOOKUP_SERVER,
+                               family, SOCK_STREAM, &c->addr_first) == 0)
+                    goto exit_loop;
+            }
+            if (c->addr_first == NULL) {
+                BIOerr(BIO_F_ACPT_STATE, BIO_R_LOOKUP_RETURNED_NOTHING);
+                goto exit_loop;
+            }
+            /* We're currently not iterating, but set this as preparation
+             * for possible future development in that regard
+             */
+            c->addr_iter = c->addr_first;
+            c->state = ACPT_S_CREATE_SOCKET;
+            break;
+
+        case ACPT_S_CREATE_SOCKET:
+            ret = BIO_socket(BIO_ADDRINFO_family(c->addr_iter),
+                             BIO_ADDRINFO_socktype(c->addr_iter),
+                             BIO_ADDRINFO_protocol(c->addr_iter), 0);
+            if (ret == (int)INVALID_SOCKET) {
+                SYSerr(SYS_F_SOCKET, get_last_socket_error());
+                ERR_add_error_data(4,
+                                   "hostname=", c->param_addr,
+                                   " service=", c->param_serv);
+                BIOerr(BIO_F_ACPT_STATE, BIO_R_UNABLE_TO_CREATE_SOCKET);
+                goto exit_loop;
+            }
+            c->accept_sock = ret;
+            b->num = ret;
+            c->state = ACPT_S_LISTEN;
+            break;
+
+        case ACPT_S_LISTEN:
+            {
+                if (!BIO_listen(c->accept_sock,
+                                BIO_ADDRINFO_address(c->addr_iter),
+                                c->bind_mode)) {
+                    BIO_closesocket(c->accept_sock);
+                    goto exit_loop;
+                }
+            }
+
+            {
+                union BIO_sock_info_u info;
+
+                info.addr = &c->cache_accepting_addr;
+                if (!BIO_sock_info(c->accept_sock, BIO_SOCK_INFO_ADDRESS,
+                                   &info)) {
+                    BIO_closesocket(c->accept_sock);
+                    goto exit_loop;
+                }
+            }
+
+            c->cache_accepting_name =
+                BIO_ADDR_hostname_string(&c->cache_accepting_addr, 1);
+            c->cache_accepting_serv =
+                BIO_ADDR_service_string(&c->cache_accepting_addr, 1);
+            c->state = ACPT_S_ACCEPT;
+            s = -1;
+            ret = 1;
+            goto end;
+
+        case ACPT_S_ACCEPT:
+            if (b->next_bio != NULL) {
+                c->state = ACPT_S_OK;
+                break;
+            }
+            BIO_clear_retry_flags(b);
+            b->retry_reason = 0;
+
+            s = BIO_accept_ex(c->accept_sock, &c->cache_peer_addr,
+                              c->accepted_mode);
+
+            /* If the returned socket is invalid, this might still be
+             * retryable
+             */
+            if (s < 0) {
+                if (BIO_sock_should_retry(s)) {
+                    BIO_set_retry_special(b);
+                    b->retry_reason = BIO_RR_ACCEPT;
+                    goto end;
+                }
+            }
+
+            /* If it wasn't retryable, we fail */
+            if (s < 0) {
+                ret = s;
+                goto exit_loop;
+            }
+
+            bio = BIO_new_socket(s, BIO_CLOSE);
+            if (bio == NULL)
+                goto exit_loop;
+
+            BIO_set_callback(bio, BIO_get_callback(b));
+            BIO_set_callback_arg(bio, BIO_get_callback_arg(b));
+
+            /*
+             * If the accept BIO has an bio_chain, we dup it and put the new
+             * socket at the end.
+             */
+            if (c->bio_chain != NULL) {
+                if ((dbio = BIO_dup_chain(c->bio_chain)) == NULL)
+                    goto exit_loop;
+                if (!BIO_push(dbio, bio))
+                    goto exit_loop;
+                bio = dbio;
+            }
+            if (BIO_push(b, bio) == NULL)
+                goto exit_loop;
+
+            c->cache_peer_name =
+                BIO_ADDR_hostname_string(&c->cache_peer_addr, 1);
+            c->cache_peer_serv =
+                BIO_ADDR_service_string(&c->cache_peer_addr, 1);
+            c->state = ACPT_S_OK;
+            bio = NULL;
+            ret = 1;
+            goto end;
+
+        case ACPT_S_OK:
+            if (b->next_bio == NULL) {
+                c->state = ACPT_S_ACCEPT;
+                break;
+            }
+            ret = 1;
+            goto end;
+
+        default:
+            ret = 0;
+            goto end;
+        }
+    }
+
+  exit_loop:
+    if (bio != NULL)
+        BIO_free(bio);
+    else if (s >= 0)
+        BIO_closesocket(s);
+  end:
+    return ret;
+}
+
+static int acpt_read(BIO *b, char *out, int outl)
+{
+    int ret = 0;
+    BIO_ACCEPT *data;
+
+    BIO_clear_retry_flags(b);
+    data = (BIO_ACCEPT *)b->ptr;
+
+    while (b->next_bio == NULL) {
+        ret = acpt_state(b, data);
+        if (ret <= 0)
+            return (ret);
+    }
+
+    ret = BIO_read(b->next_bio, out, outl);
+    BIO_copy_next_retry(b);
+    return (ret);
+}
+
+static int acpt_write(BIO *b, const char *in, int inl)
+{
+    int ret;
+    BIO_ACCEPT *data;
+
+    BIO_clear_retry_flags(b);
+    data = (BIO_ACCEPT *)b->ptr;
+
+    while (b->next_bio == NULL) {
+        ret = acpt_state(b, data);
+        if (ret <= 0)
+            return (ret);
+    }
+
+    ret = BIO_write(b->next_bio, in, inl);
+    BIO_copy_next_retry(b);
+    return (ret);
+}
+
+static long acpt_ctrl(BIO *b, int cmd, long num, void *ptr)
+{
+    int *ip;
+    long ret = 1;
+    BIO_ACCEPT *data;
+    char **pp;
+
+    data = (BIO_ACCEPT *)b->ptr;
+
+    switch (cmd) {
+    case BIO_CTRL_RESET:
+        ret = 0;
+        data->state = ACPT_S_BEFORE;
+        acpt_close_socket(b);
+        BIO_ADDRINFO_free(data->addr_first);
+        data->addr_first = NULL;
+        b->flags = 0;
+        break;
+    case BIO_C_DO_STATE_MACHINE:
+        /* use this one to start the connection */
+        ret = (long)acpt_state(b, data);
+        break;
+    case BIO_C_SET_ACCEPT:
+        if (ptr != NULL) {
+            if (num == 0) {
+                char *hold_serv = data->param_serv;
+                /* We affect the hostname regardless.  However, the input
+                 * string might contain a host:service spec, so we must
+                 * parse it, which might or might not affect the service
+                 */
+                OPENSSL_free(data->param_addr);
+                data->param_addr = NULL;
+                ret = BIO_parse_hostserv(ptr,
+                                         &data->param_addr,
+                                         &data->param_serv,
+                                         BIO_PARSE_PRIO_SERV);
+                if (hold_serv != data->param_serv)
+                    OPENSSL_free(hold_serv);
+                b->init = 1;
+            } else if (num == 1) {
+                OPENSSL_free(data->param_serv);
+                data->param_serv = BUF_strdup(ptr);
+                b->init = 1;
+            } else if (num == 2) {
+                data->bind_mode |= BIO_SOCK_NONBLOCK;
+            } else if (num == 3) {
+                BIO_free(data->bio_chain);
+                data->bio_chain = (BIO *)ptr;
+            } else if (num == 4) {
+                data->accept_family = *(int *)ptr;
+            }
+        } else {
+            if (num == 2) {
+                data->bind_mode &= ~BIO_SOCK_NONBLOCK;
+            }
+        }
+        break;
+    case BIO_C_SET_NBIO:
+        if (num != 0)
+            data->accepted_mode |= BIO_SOCK_NONBLOCK;
+        else
+            data->accepted_mode &= ~BIO_SOCK_NONBLOCK;
+        break;
+    case BIO_C_SET_FD:
+        b->init = 1;
+        b->num = *((int *)ptr);
+        data->accept_sock = b->num;
+        data->state = ACPT_S_ACCEPT;
+        b->shutdown = (int)num;
+        b->init = 1;
+        break;
+    case BIO_C_GET_FD:
+        if (b->init) {
+            ip = (int *)ptr;
+            if (ip != NULL)
+                *ip = data->accept_sock;
+            ret = data->accept_sock;
+        } else
+            ret = -1;
+        break;
+    case BIO_C_GET_ACCEPT:
+        if (b->init) {
+            if (num == 0 && ptr != NULL) {
+                pp = (char **)ptr;
+                *pp = data->cache_accepting_name;
+            } else if (num == 1 && ptr != NULL) {
+                pp = (char **)ptr;
+                *pp = data->cache_accepting_serv;
+            } else if (num == 2 && ptr != NULL) {
+                pp = (char **)ptr;
+                *pp = data->cache_peer_name;
+            } else if (num == 3 && ptr != NULL) {
+                pp = (char **)ptr;
+                *pp = data->cache_peer_serv;
+            } else if (num == 4) {
+                switch (BIO_ADDRINFO_family(data->addr_iter)) {
+#ifdef AF_INET6
+                case AF_INET6:
+                    ret = BIO_FAMILY_IPV6;
+                    break;
+#endif
+                case AF_INET:
+                    ret = BIO_FAMILY_IPV4;
+                    break;
+                case 0:
+                    ret = data->accept_family;
+                    break;
+                default:
+                    ret = -1;
+                    break;
+                }
+            } else
+                ret = -1;
+        } else
+            ret = -1;
+        break;
+    case BIO_CTRL_GET_CLOSE:
+        ret = b->shutdown;
+        break;
+    case BIO_CTRL_SET_CLOSE:
+        b->shutdown = (int)num;
+        break;
+    case BIO_CTRL_PENDING:
+    case BIO_CTRL_WPENDING:
+        ret = 0;
+        break;
+    case BIO_CTRL_FLUSH:
+        break;
+    case BIO_C_SET_BIND_MODE:
+        data->bind_mode = (int)num;
+        break;
+    case BIO_C_GET_BIND_MODE:
+        ret = (long)data->bind_mode;
+        break;
+    case BIO_CTRL_DUP:
+/*-     dbio=(BIO *)ptr;
+        if (data->param_port) EAY EAY
+                BIO_set_port(dbio,data->param_port);
+        if (data->param_hostname)
+                BIO_set_hostname(dbio,data->param_hostname);
+        BIO_set_nbio(dbio,data->nbio); */
+        break;
+
+    default:
+        ret = 0;
+        break;
+    }
+    return (ret);
+}
+
+static int acpt_puts(BIO *bp, const char *str)
+{
+    int n, ret;
+
+    n = strlen(str);
+    ret = acpt_write(bp, str, n);
+    return (ret);
+}
+
+BIO *BIO_new_accept(const char *str)
+{
+    BIO *ret;
+
+    ret = BIO_new(BIO_s_accept());
+    if (ret == NULL)
+        return (NULL);
+    if (BIO_set_accept_name(ret, str))
+        return (ret);
+    BIO_free(ret);
+    return (NULL);
+}
+
+#endif
diff --git a/crypto/bio/bss_bio.c b/crypto/bio/bss_bio.c
new file mode 100644
index 000000000..de34f6bf3
--- /dev/null
+++ b/crypto/bio/bss_bio.c
@@ -0,0 +1,805 @@
+/*
+ * Copyright 1999-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * Special method for a BIO where the other endpoint is also a BIO of this
+ * kind, handled by the same thread (i.e. the "peer" is actually ourselves,
+ * wearing a different hat). Such "BIO pairs" are mainly for using the SSL
+ * library with I/O interfaces for which no specific BIO method is available.
+ * See ssl/ssltest.c for some hints on how this can be used.
+ */
+
+#include 
+#include 
+#include 
+#include 
+
+#include "bio_lcl.h"
+#include 
+#include 
+
+#include "e_os.h"
+
+static int bio_new(BIO *bio);
+static int bio_free(BIO *bio);
+static int bio_read(BIO *bio, char *buf, int size);
+static int bio_write(BIO *bio, const char *buf, int num);
+static long bio_ctrl(BIO *bio, int cmd, long num, void *ptr);
+static int bio_puts(BIO *bio, const char *str);
+
+static int bio_make_pair(BIO *bio1, BIO *bio2);
+static void bio_destroy_pair(BIO *bio);
+
+static const BIO_METHOD methods_biop = {
+    BIO_TYPE_BIO,
+    "BIO pair",
+    bio_write,
+    bio_read,
+    bio_puts,
+    NULL /* no bio_gets */ ,
+    bio_ctrl,
+    bio_new,
+    bio_free,
+    NULL                        /* no bio_callback_ctrl */
+};
+
+const BIO_METHOD *BIO_s_bio(void)
+{
+    return &methods_biop;
+}
+
+struct bio_bio_st {
+    BIO *peer;                  /* NULL if buf == NULL. If peer != NULL, then
+                                 * peer->ptr is also a bio_bio_st, and its
+                                 * "peer" member points back to us. peer !=
+                                 * NULL iff init != 0 in the BIO. */
+    /* This is for what we write (i.e. reading uses peer's struct): */
+    int closed;                 /* valid iff peer != NULL */
+    size_t len;                 /* valid iff buf != NULL; 0 if peer == NULL */
+    size_t offset;              /* valid iff buf != NULL; 0 if len == 0 */
+    size_t size;
+    char *buf;                  /* "size" elements (if != NULL) */
+    size_t request;             /* valid iff peer != NULL; 0 if len != 0,
+                                 * otherwise set by peer to number of bytes
+                                 * it (unsuccessfully) tried to read, never
+                                 * more than buffer space (size-len)
+                                 * warrants. */
+};
+
+static int bio_new(BIO *bio)
+{
+    struct bio_bio_st *b = OPENSSL_zalloc(sizeof(*b));
+
+    if (b == NULL)
+        return 0;
+
+    /* enough for one TLS record (just a default) */
+    b->size = 17 * 1024;
+
+    bio->ptr = b;
+    return 1;
+}
+
+static int bio_free(BIO *bio)
+{
+    struct bio_bio_st *b;
+
+    if (bio == NULL)
+        return 0;
+    b = bio->ptr;
+
+    assert(b != NULL);
+
+    if (b->peer)
+        bio_destroy_pair(bio);
+
+    OPENSSL_free(b->buf);
+    OPENSSL_free(b);
+
+    return 1;
+}
+
+static int bio_read(BIO *bio, char *buf, int size_)
+{
+    size_t size = size_;
+    size_t rest;
+    struct bio_bio_st *b, *peer_b;
+
+    BIO_clear_retry_flags(bio);
+
+    if (!bio->init)
+        return 0;
+
+    b = bio->ptr;
+    assert(b != NULL);
+    assert(b->peer != NULL);
+    peer_b = b->peer->ptr;
+    assert(peer_b != NULL);
+    assert(peer_b->buf != NULL);
+
+    peer_b->request = 0;        /* will be set in "retry_read" situation */
+
+    if (buf == NULL || size == 0)
+        return 0;
+
+    if (peer_b->len == 0) {
+        if (peer_b->closed)
+            return 0;           /* writer has closed, and no data is left */
+        else {
+            BIO_set_retry_read(bio); /* buffer is empty */
+            if (size <= peer_b->size)
+                peer_b->request = size;
+            else
+                /*
+                 * don't ask for more than the peer can deliver in one write
+                 */
+                peer_b->request = peer_b->size;
+            return -1;
+        }
+    }
+
+    /* we can read */
+    if (peer_b->len < size)
+        size = peer_b->len;
+
+    /* now read "size" bytes */
+
+    rest = size;
+
+    assert(rest > 0);
+    do {                        /* one or two iterations */
+        size_t chunk;
+
+        assert(rest <= peer_b->len);
+        if (peer_b->offset + rest <= peer_b->size)
+            chunk = rest;
+        else
+            /* wrap around ring buffer */
+            chunk = peer_b->size - peer_b->offset;
+        assert(peer_b->offset + chunk <= peer_b->size);
+
+        memcpy(buf, peer_b->buf + peer_b->offset, chunk);
+
+        peer_b->len -= chunk;
+        if (peer_b->len) {
+            peer_b->offset += chunk;
+            assert(peer_b->offset <= peer_b->size);
+            if (peer_b->offset == peer_b->size)
+                peer_b->offset = 0;
+            buf += chunk;
+        } else {
+            /* buffer now empty, no need to advance "buf" */
+            assert(chunk == rest);
+            peer_b->offset = 0;
+        }
+        rest -= chunk;
+    }
+    while (rest);
+
+    return size;
+}
+
+/*-
+ * non-copying interface: provide pointer to available data in buffer
+ *    bio_nread0:  return number of available bytes
+ *    bio_nread:   also advance index
+ * (example usage:  bio_nread0(), read from buffer, bio_nread()
+ *  or just         bio_nread(), read from buffer)
+ */
+/*
+ * WARNING: The non-copying interface is largely untested as of yet and may
+ * contain bugs.
+ */
+static ossl_ssize_t bio_nread0(BIO *bio, char **buf)
+{
+    struct bio_bio_st *b, *peer_b;
+    ossl_ssize_t num;
+
+    BIO_clear_retry_flags(bio);
+
+    if (!bio->init)
+        return 0;
+
+    b = bio->ptr;
+    assert(b != NULL);
+    assert(b->peer != NULL);
+    peer_b = b->peer->ptr;
+    assert(peer_b != NULL);
+    assert(peer_b->buf != NULL);
+
+    peer_b->request = 0;
+
+    if (peer_b->len == 0) {
+        char dummy;
+
+        /* avoid code duplication -- nothing available for reading */
+        return bio_read(bio, &dummy, 1); /* returns 0 or -1 */
+    }
+
+    num = peer_b->len;
+    if (peer_b->size < peer_b->offset + num)
+        /* no ring buffer wrap-around for non-copying interface */
+        num = peer_b->size - peer_b->offset;
+    assert(num > 0);
+
+    if (buf != NULL)
+        *buf = peer_b->buf + peer_b->offset;
+    return num;
+}
+
+static ossl_ssize_t bio_nread(BIO *bio, char **buf, size_t num_)
+{
+    struct bio_bio_st *b, *peer_b;
+    ossl_ssize_t num, available;
+
+    if (num_ > OSSL_SSIZE_MAX)
+        num = OSSL_SSIZE_MAX;
+    else
+        num = (ossl_ssize_t) num_;
+
+    available = bio_nread0(bio, buf);
+    if (num > available)
+        num = available;
+    if (num <= 0)
+        return num;
+
+    b = bio->ptr;
+    peer_b = b->peer->ptr;
+
+    peer_b->len -= num;
+    if (peer_b->len) {
+        peer_b->offset += num;
+        assert(peer_b->offset <= peer_b->size);
+        if (peer_b->offset == peer_b->size)
+            peer_b->offset = 0;
+    } else
+        peer_b->offset = 0;
+
+    return num;
+}
+
+static int bio_write(BIO *bio, const char *buf, int num_)
+{
+    size_t num = num_;
+    size_t rest;
+    struct bio_bio_st *b;
+
+    BIO_clear_retry_flags(bio);
+
+    if (!bio->init || buf == NULL || num == 0)
+        return 0;
+
+    b = bio->ptr;
+    assert(b != NULL);
+    assert(b->peer != NULL);
+    assert(b->buf != NULL);
+
+    b->request = 0;
+    if (b->closed) {
+        /* we already closed */
+        BIOerr(BIO_F_BIO_WRITE, BIO_R_BROKEN_PIPE);
+        return -1;
+    }
+
+    assert(b->len <= b->size);
+
+    if (b->len == b->size) {
+        BIO_set_retry_write(bio); /* buffer is full */
+        return -1;
+    }
+
+    /* we can write */
+    if (num > b->size - b->len)
+        num = b->size - b->len;
+
+    /* now write "num" bytes */
+
+    rest = num;
+
+    assert(rest > 0);
+    do {                        /* one or two iterations */
+        size_t write_offset;
+        size_t chunk;
+
+        assert(b->len + rest <= b->size);
+
+        write_offset = b->offset + b->len;
+        if (write_offset >= b->size)
+            write_offset -= b->size;
+        /* b->buf[write_offset] is the first byte we can write to. */
+
+        if (write_offset + rest <= b->size)
+            chunk = rest;
+        else
+            /* wrap around ring buffer */
+            chunk = b->size - write_offset;
+
+        memcpy(b->buf + write_offset, buf, chunk);
+
+        b->len += chunk;
+
+        assert(b->len <= b->size);
+
+        rest -= chunk;
+        buf += chunk;
+    }
+    while (rest);
+
+    return num;
+}
+
+/*-
+ * non-copying interface: provide pointer to region to write to
+ *   bio_nwrite0:  check how much space is available
+ *   bio_nwrite:   also increase length
+ * (example usage:  bio_nwrite0(), write to buffer, bio_nwrite()
+ *  or just         bio_nwrite(), write to buffer)
+ */
+static ossl_ssize_t bio_nwrite0(BIO *bio, char **buf)
+{
+    struct bio_bio_st *b;
+    size_t num;
+    size_t write_offset;
+
+    BIO_clear_retry_flags(bio);
+
+    if (!bio->init)
+        return 0;
+
+    b = bio->ptr;
+    assert(b != NULL);
+    assert(b->peer != NULL);
+    assert(b->buf != NULL);
+
+    b->request = 0;
+    if (b->closed) {
+        BIOerr(BIO_F_BIO_NWRITE0, BIO_R_BROKEN_PIPE);
+        return -1;
+    }
+
+    assert(b->len <= b->size);
+
+    if (b->len == b->size) {
+        BIO_set_retry_write(bio);
+        return -1;
+    }
+
+    num = b->size - b->len;
+    write_offset = b->offset + b->len;
+    if (write_offset >= b->size)
+        write_offset -= b->size;
+    if (write_offset + num > b->size)
+        /*
+         * no ring buffer wrap-around for non-copying interface (to fulfil
+         * the promise by BIO_ctrl_get_write_guarantee, BIO_nwrite may have
+         * to be called twice)
+         */
+        num = b->size - write_offset;
+
+    if (buf != NULL)
+        *buf = b->buf + write_offset;
+    assert(write_offset + num <= b->size);
+
+    return num;
+}
+
+static ossl_ssize_t bio_nwrite(BIO *bio, char **buf, size_t num_)
+{
+    struct bio_bio_st *b;
+    ossl_ssize_t num, space;
+
+    if (num_ > OSSL_SSIZE_MAX)
+        num = OSSL_SSIZE_MAX;
+    else
+        num = (ossl_ssize_t) num_;
+
+    space = bio_nwrite0(bio, buf);
+    if (num > space)
+        num = space;
+    if (num <= 0)
+        return num;
+    b = bio->ptr;
+    assert(b != NULL);
+    b->len += num;
+    assert(b->len <= b->size);
+
+    return num;
+}
+
+static long bio_ctrl(BIO *bio, int cmd, long num, void *ptr)
+{
+    long ret;
+    struct bio_bio_st *b = bio->ptr;
+
+    assert(b != NULL);
+
+    switch (cmd) {
+        /* specific CTRL codes */
+
+    case BIO_C_SET_WRITE_BUF_SIZE:
+        if (b->peer) {
+            BIOerr(BIO_F_BIO_CTRL, BIO_R_IN_USE);
+            ret = 0;
+        } else if (num == 0) {
+            BIOerr(BIO_F_BIO_CTRL, BIO_R_INVALID_ARGUMENT);
+            ret = 0;
+        } else {
+            size_t new_size = num;
+
+            if (b->size != new_size) {
+                OPENSSL_free(b->buf);
+                b->buf = NULL;
+                b->size = new_size;
+            }
+            ret = 1;
+        }
+        break;
+
+    case BIO_C_GET_WRITE_BUF_SIZE:
+        ret = (long)b->size;
+        break;
+
+    case BIO_C_MAKE_BIO_PAIR:
+        {
+            BIO *other_bio = ptr;
+
+            if (bio_make_pair(bio, other_bio))
+                ret = 1;
+            else
+                ret = 0;
+        }
+        break;
+
+    case BIO_C_DESTROY_BIO_PAIR:
+        /*
+         * Affects both BIOs in the pair -- call just once! Or let
+         * BIO_free(bio1); BIO_free(bio2); do the job.
+         */
+        bio_destroy_pair(bio);
+        ret = 1;
+        break;
+
+    case BIO_C_GET_WRITE_GUARANTEE:
+        /*
+         * How many bytes can the caller feed to the next write without
+         * having to keep any?
+         */
+        if (b->peer == NULL || b->closed)
+            ret = 0;
+        else
+            ret = (long)b->size - b->len;
+        break;
+
+    case BIO_C_GET_READ_REQUEST:
+        /*
+         * If the peer unsuccessfully tried to read, how many bytes were
+         * requested? (As with BIO_CTRL_PENDING, that number can usually be
+         * treated as boolean.)
+         */
+        ret = (long)b->request;
+        break;
+
+    case BIO_C_RESET_READ_REQUEST:
+        /*
+         * Reset request.  (Can be useful after read attempts at the other
+         * side that are meant to be non-blocking, e.g. when probing SSL_read
+         * to see if any data is available.)
+         */
+        b->request = 0;
+        ret = 1;
+        break;
+
+    case BIO_C_SHUTDOWN_WR:
+        /* similar to shutdown(..., SHUT_WR) */
+        b->closed = 1;
+        ret = 1;
+        break;
+
+    case BIO_C_NREAD0:
+        /* prepare for non-copying read */
+        ret = (long)bio_nread0(bio, ptr);
+        break;
+
+    case BIO_C_NREAD:
+        /* non-copying read */
+        ret = (long)bio_nread(bio, ptr, (size_t)num);
+        break;
+
+    case BIO_C_NWRITE0:
+        /* prepare for non-copying write */
+        ret = (long)bio_nwrite0(bio, ptr);
+        break;
+
+    case BIO_C_NWRITE:
+        /* non-copying write */
+        ret = (long)bio_nwrite(bio, ptr, (size_t)num);
+        break;
+
+        /* standard CTRL codes follow */
+
+    case BIO_CTRL_RESET:
+        if (b->buf != NULL) {
+            b->len = 0;
+            b->offset = 0;
+        }
+        ret = 0;
+        break;
+
+    case BIO_CTRL_GET_CLOSE:
+        ret = bio->shutdown;
+        break;
+
+    case BIO_CTRL_SET_CLOSE:
+        bio->shutdown = (int)num;
+        ret = 1;
+        break;
+
+    case BIO_CTRL_PENDING:
+        if (b->peer != NULL) {
+            struct bio_bio_st *peer_b = b->peer->ptr;
+
+            ret = (long)peer_b->len;
+        } else
+            ret = 0;
+        break;
+
+    case BIO_CTRL_WPENDING:
+        if (b->buf != NULL)
+            ret = (long)b->len;
+        else
+            ret = 0;
+        break;
+
+    case BIO_CTRL_DUP:
+        /* See BIO_dup_chain for circumstances we have to expect. */
+        {
+            BIO *other_bio = ptr;
+            struct bio_bio_st *other_b;
+
+            assert(other_bio != NULL);
+            other_b = other_bio->ptr;
+            assert(other_b != NULL);
+
+            assert(other_b->buf == NULL); /* other_bio is always fresh */
+
+            other_b->size = b->size;
+        }
+
+        ret = 1;
+        break;
+
+    case BIO_CTRL_FLUSH:
+        ret = 1;
+        break;
+
+    case BIO_CTRL_EOF:
+        if (b->peer != NULL) {
+            struct bio_bio_st *peer_b = b->peer->ptr;
+
+            if (peer_b->len == 0 && peer_b->closed)
+                ret = 1;
+            else
+                ret = 0;
+        } else {
+            ret = 1;
+        }
+        break;
+
+    default:
+        ret = 0;
+    }
+    return ret;
+}
+
+static int bio_puts(BIO *bio, const char *str)
+{
+    return bio_write(bio, str, strlen(str));
+}
+
+static int bio_make_pair(BIO *bio1, BIO *bio2)
+{
+    struct bio_bio_st *b1, *b2;
+
+    assert(bio1 != NULL);
+    assert(bio2 != NULL);
+
+    b1 = bio1->ptr;
+    b2 = bio2->ptr;
+
+    if (b1->peer != NULL || b2->peer != NULL) {
+        BIOerr(BIO_F_BIO_MAKE_PAIR, BIO_R_IN_USE);
+        return 0;
+    }
+
+    if (b1->buf == NULL) {
+        b1->buf = OPENSSL_malloc(b1->size);
+        if (b1->buf == NULL) {
+            BIOerr(BIO_F_BIO_MAKE_PAIR, ERR_R_MALLOC_FAILURE);
+            return 0;
+        }
+        b1->len = 0;
+        b1->offset = 0;
+    }
+
+    if (b2->buf == NULL) {
+        b2->buf = OPENSSL_malloc(b2->size);
+        if (b2->buf == NULL) {
+            BIOerr(BIO_F_BIO_MAKE_PAIR, ERR_R_MALLOC_FAILURE);
+            return 0;
+        }
+        b2->len = 0;
+        b2->offset = 0;
+    }
+
+    b1->peer = bio2;
+    b1->closed = 0;
+    b1->request = 0;
+    b2->peer = bio1;
+    b2->closed = 0;
+    b2->request = 0;
+
+    bio1->init = 1;
+    bio2->init = 1;
+
+    return 1;
+}
+
+static void bio_destroy_pair(BIO *bio)
+{
+    struct bio_bio_st *b = bio->ptr;
+
+    if (b != NULL) {
+        BIO *peer_bio = b->peer;
+
+        if (peer_bio != NULL) {
+            struct bio_bio_st *peer_b = peer_bio->ptr;
+
+            assert(peer_b != NULL);
+            assert(peer_b->peer == bio);
+
+            peer_b->peer = NULL;
+            peer_bio->init = 0;
+            assert(peer_b->buf != NULL);
+            peer_b->len = 0;
+            peer_b->offset = 0;
+
+            b->peer = NULL;
+            bio->init = 0;
+            assert(b->buf != NULL);
+            b->len = 0;
+            b->offset = 0;
+        }
+    }
+}
+
+/* Exported convenience functions */
+int BIO_new_bio_pair(BIO **bio1_p, size_t writebuf1,
+                     BIO **bio2_p, size_t writebuf2)
+{
+    BIO *bio1 = NULL, *bio2 = NULL;
+    long r;
+    int ret = 0;
+
+    bio1 = BIO_new(BIO_s_bio());
+    if (bio1 == NULL)
+        goto err;
+    bio2 = BIO_new(BIO_s_bio());
+    if (bio2 == NULL)
+        goto err;
+
+    if (writebuf1) {
+        r = BIO_set_write_buf_size(bio1, writebuf1);
+        if (!r)
+            goto err;
+    }
+    if (writebuf2) {
+        r = BIO_set_write_buf_size(bio2, writebuf2);
+        if (!r)
+            goto err;
+    }
+
+    r = BIO_make_bio_pair(bio1, bio2);
+    if (!r)
+        goto err;
+    ret = 1;
+
+ err:
+    if (ret == 0) {
+        BIO_free(bio1);
+        bio1 = NULL;
+        BIO_free(bio2);
+        bio2 = NULL;
+    }
+
+    *bio1_p = bio1;
+    *bio2_p = bio2;
+    return ret;
+}
+
+size_t BIO_ctrl_get_write_guarantee(BIO *bio)
+{
+    return BIO_ctrl(bio, BIO_C_GET_WRITE_GUARANTEE, 0, NULL);
+}
+
+size_t BIO_ctrl_get_read_request(BIO *bio)
+{
+    return BIO_ctrl(bio, BIO_C_GET_READ_REQUEST, 0, NULL);
+}
+
+int BIO_ctrl_reset_read_request(BIO *bio)
+{
+    return (BIO_ctrl(bio, BIO_C_RESET_READ_REQUEST, 0, NULL) != 0);
+}
+
+/*
+ * BIO_nread0/nread/nwrite0/nwrite are available only for BIO pairs for now
+ * (conceivably some other BIOs could allow non-copying reads and writes
+ * too.)
+ */
+int BIO_nread0(BIO *bio, char **buf)
+{
+    long ret;
+
+    if (!bio->init) {
+        BIOerr(BIO_F_BIO_NREAD0, BIO_R_UNINITIALIZED);
+        return -2;
+    }
+
+    ret = BIO_ctrl(bio, BIO_C_NREAD0, 0, buf);
+    if (ret > INT_MAX)
+        return INT_MAX;
+    else
+        return (int)ret;
+}
+
+int BIO_nread(BIO *bio, char **buf, int num)
+{
+    int ret;
+
+    if (!bio->init) {
+        BIOerr(BIO_F_BIO_NREAD, BIO_R_UNINITIALIZED);
+        return -2;
+    }
+
+    ret = (int)BIO_ctrl(bio, BIO_C_NREAD, num, buf);
+    if (ret > 0)
+        bio->num_read += ret;
+    return ret;
+}
+
+int BIO_nwrite0(BIO *bio, char **buf)
+{
+    long ret;
+
+    if (!bio->init) {
+        BIOerr(BIO_F_BIO_NWRITE0, BIO_R_UNINITIALIZED);
+        return -2;
+    }
+
+    ret = BIO_ctrl(bio, BIO_C_NWRITE0, 0, buf);
+    if (ret > INT_MAX)
+        return INT_MAX;
+    else
+        return (int)ret;
+}
+
+int BIO_nwrite(BIO *bio, char **buf, int num)
+{
+    int ret;
+
+    if (!bio->init) {
+        BIOerr(BIO_F_BIO_NWRITE, BIO_R_UNINITIALIZED);
+        return -2;
+    }
+
+    ret = BIO_ctrl(bio, BIO_C_NWRITE, num, buf);
+    if (ret > 0)
+        bio->num_write += ret;
+    return ret;
+}
diff --git a/crypto/bio/bss_conn.c b/crypto/bio/bss_conn.c
new file mode 100644
index 000000000..dfd0988df
--- /dev/null
+++ b/crypto/bio/bss_conn.c
@@ -0,0 +1,545 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+
+#include "bio_lcl.h"
+
+#ifndef OPENSSL_NO_SOCK
+
+typedef struct bio_connect_st {
+    int state;
+    int connect_family;
+    char *param_hostname;
+    char *param_service;
+    int connect_mode;
+
+    BIO_ADDRINFO *addr_first;
+    const BIO_ADDRINFO *addr_iter;
+    /*
+     * int socket; this will be kept in bio->num so that it is compatible
+     * with the bss_sock bio
+     */
+    /*
+     * called when the connection is initially made callback(BIO,state,ret);
+     * The callback should return 'ret'.  state is for compatibility with the
+     * ssl info_callback
+     */
+    int (*info_callback) (const BIO *bio, int state, int ret);
+} BIO_CONNECT;
+
+static int conn_write(BIO *h, const char *buf, int num);
+static int conn_read(BIO *h, char *buf, int size);
+static int conn_puts(BIO *h, const char *str);
+static long conn_ctrl(BIO *h, int cmd, long arg1, void *arg2);
+static int conn_new(BIO *h);
+static int conn_free(BIO *data);
+static long conn_callback_ctrl(BIO *h, int cmd, bio_info_cb *);
+
+static int conn_state(BIO *b, BIO_CONNECT *c);
+static void conn_close_socket(BIO *data);
+BIO_CONNECT *BIO_CONNECT_new(void);
+void BIO_CONNECT_free(BIO_CONNECT *a);
+
+#define BIO_CONN_S_BEFORE                1
+#define BIO_CONN_S_GET_ADDR              2
+#define BIO_CONN_S_CREATE_SOCKET         3
+#define BIO_CONN_S_CONNECT               4
+#define BIO_CONN_S_OK                    5
+#define BIO_CONN_S_BLOCKED_CONNECT       6
+
+static const BIO_METHOD methods_connectp = {
+    BIO_TYPE_CONNECT,
+    "socket connect",
+    conn_write,
+    conn_read,
+    conn_puts,
+    NULL,                       /* connect_gets, */
+    conn_ctrl,
+    conn_new,
+    conn_free,
+    conn_callback_ctrl,
+};
+
+static int conn_state(BIO *b, BIO_CONNECT *c)
+{
+    int ret = -1, i;
+    int (*cb) (const BIO *, int, int) = NULL;
+
+    if (c->info_callback != NULL)
+        cb = c->info_callback;
+
+    for (;;) {
+        switch (c->state) {
+        case BIO_CONN_S_BEFORE:
+            if (c->param_hostname == NULL && c->param_service == NULL) {
+                BIOerr(BIO_F_CONN_STATE, BIO_R_NO_HOSTNAME_OR_SERVICE_SPECIFIED);
+                ERR_add_error_data(4,
+                                   "hostname=", c->param_hostname,
+                                   " service=", c->param_service);
+                goto exit_loop;
+            }
+            c->state = BIO_CONN_S_GET_ADDR;
+            break;
+
+        case BIO_CONN_S_GET_ADDR:
+            {
+                int family = AF_UNSPEC;
+                switch (c->connect_family) {
+                case BIO_FAMILY_IPV6:
+                    if (1) { /* This is a trick we use to avoid bit rot.
+                              * at least the "else" part will always be
+                              * compiled.
+                              */
+#ifdef AF_INET6
+                        family = AF_INET6;
+                    } else {
+#endif
+                        BIOerr(BIO_F_CONN_STATE, BIO_R_UNAVAILABLE_IP_FAMILY);
+                        goto exit_loop;
+                    }
+                    break;
+                case BIO_FAMILY_IPV4:
+                    family = AF_INET;
+                    break;
+                case BIO_FAMILY_IPANY:
+                    family = AF_UNSPEC;
+                    break;
+                default:
+                    BIOerr(BIO_F_CONN_STATE, BIO_R_UNSUPPORTED_IP_FAMILY);
+                    goto exit_loop;
+                }
+                if (BIO_lookup(c->param_hostname, c->param_service,
+                               BIO_LOOKUP_CLIENT,
+                               family, SOCK_STREAM, &c->addr_first) == 0)
+                    goto exit_loop;
+            }
+            if (c->addr_first == NULL) {
+                BIOerr(BIO_F_CONN_STATE, BIO_R_LOOKUP_RETURNED_NOTHING);
+                goto exit_loop;
+            }
+            c->addr_iter = c->addr_first;
+            c->state = BIO_CONN_S_CREATE_SOCKET;
+            break;
+
+        case BIO_CONN_S_CREATE_SOCKET:
+            ret = BIO_socket(BIO_ADDRINFO_family(c->addr_iter),
+                             BIO_ADDRINFO_socktype(c->addr_iter),
+                             BIO_ADDRINFO_protocol(c->addr_iter), 0);
+            if (ret == (int)INVALID_SOCKET) {
+                SYSerr(SYS_F_SOCKET, get_last_socket_error());
+                ERR_add_error_data(4,
+                                   "hostname=", c->param_hostname,
+                                   " service=", c->param_service);
+                BIOerr(BIO_F_CONN_STATE, BIO_R_UNABLE_TO_CREATE_SOCKET);
+                goto exit_loop;
+            }
+            b->num = ret;
+            c->state = BIO_CONN_S_CONNECT;
+            break;
+
+        case BIO_CONN_S_CONNECT:
+            BIO_clear_retry_flags(b);
+            ret = BIO_connect(b->num, BIO_ADDRINFO_address(c->addr_iter),
+                              BIO_SOCK_KEEPALIVE | c->connect_mode);
+            b->retry_reason = 0;
+            if (ret == 0) {
+                if (BIO_sock_should_retry(ret)) {
+                    BIO_set_retry_special(b);
+                    c->state = BIO_CONN_S_BLOCKED_CONNECT;
+                    b->retry_reason = BIO_RR_CONNECT;
+                    ERR_clear_error();
+                } else if ((c->addr_iter = BIO_ADDRINFO_next(c->addr_iter))
+                           != NULL) {
+                    /*
+                     * if there are more addresses to try, do that first
+                     */
+                    BIO_closesocket(b->num);
+                    c->state = BIO_CONN_S_CREATE_SOCKET;
+                    ERR_clear_error();
+                    break;
+                } else {
+                    SYSerr(SYS_F_CONNECT, get_last_socket_error());
+                    ERR_add_error_data(4,
+                                       "hostname=", c->param_hostname,
+                                       " service=", c->param_service);
+                    BIOerr(BIO_F_CONN_STATE, BIO_R_CONNECT_ERROR);
+                }
+                goto exit_loop;
+            } else {
+                c->state = BIO_CONN_S_OK;
+            }
+            break;
+
+        case BIO_CONN_S_BLOCKED_CONNECT:
+            i = BIO_sock_error(b->num);
+            if (i) {
+                BIO_clear_retry_flags(b);
+                SYSerr(SYS_F_CONNECT, i);
+                ERR_add_error_data(4,
+                                   "hostname=", c->param_hostname,
+                                   " service=", c->param_service);
+                BIOerr(BIO_F_CONN_STATE, BIO_R_NBIO_CONNECT_ERROR);
+                ret = 0;
+                goto exit_loop;
+            } else
+                c->state = BIO_CONN_S_OK;
+            break;
+
+        case BIO_CONN_S_OK:
+            ret = 1;
+            goto exit_loop;
+        default:
+            /* abort(); */
+            goto exit_loop;
+        }
+
+        if (cb != NULL) {
+            if ((ret = cb((BIO *)b, c->state, ret)) == 0)
+                goto end;
+        }
+    }
+
+    /* Loop does not exit */
+ exit_loop:
+    if (cb != NULL)
+        ret = cb((BIO *)b, c->state, ret);
+ end:
+    return (ret);
+}
+
+BIO_CONNECT *BIO_CONNECT_new(void)
+{
+    BIO_CONNECT *ret;
+
+    if ((ret = OPENSSL_zalloc(sizeof(*ret))) == NULL)
+        return (NULL);
+    ret->state = BIO_CONN_S_BEFORE;
+    ret->connect_family = BIO_FAMILY_IPANY;
+    return (ret);
+}
+
+void BIO_CONNECT_free(BIO_CONNECT *a)
+{
+    if (a == NULL)
+        return;
+
+    OPENSSL_free(a->param_hostname);
+    OPENSSL_free(a->param_service);
+    BIO_ADDRINFO_free(a->addr_first);
+    OPENSSL_free(a);
+}
+
+const BIO_METHOD *BIO_s_connect(void)
+{
+    return (&methods_connectp);
+}
+
+static int conn_new(BIO *bi)
+{
+    bi->init = 0;
+    bi->num = (int)INVALID_SOCKET;
+    bi->flags = 0;
+    if ((bi->ptr = (char *)BIO_CONNECT_new()) == NULL)
+        return (0);
+    else
+        return (1);
+}
+
+static void conn_close_socket(BIO *bio)
+{
+    BIO_CONNECT *c;
+
+    c = (BIO_CONNECT *)bio->ptr;
+    if (bio->num != (int)INVALID_SOCKET) {
+        /* Only do a shutdown if things were established */
+        if (c->state == BIO_CONN_S_OK)
+            shutdown(bio->num, 2);
+        BIO_closesocket(bio->num);
+        bio->num = (int)INVALID_SOCKET;
+    }
+}
+
+static int conn_free(BIO *a)
+{
+    BIO_CONNECT *data;
+
+    if (a == NULL)
+        return (0);
+    data = (BIO_CONNECT *)a->ptr;
+
+    if (a->shutdown) {
+        conn_close_socket(a);
+        BIO_CONNECT_free(data);
+        a->ptr = NULL;
+        a->flags = 0;
+        a->init = 0;
+    }
+    return (1);
+}
+
+static int conn_read(BIO *b, char *out, int outl)
+{
+    int ret = 0;
+    BIO_CONNECT *data;
+
+    data = (BIO_CONNECT *)b->ptr;
+    if (data->state != BIO_CONN_S_OK) {
+        ret = conn_state(b, data);
+        if (ret <= 0)
+            return (ret);
+    }
+
+    if (out != NULL) {
+        clear_socket_error();
+        ret = readsocket(b->num, out, outl);
+        BIO_clear_retry_flags(b);
+        if (ret <= 0) {
+            if (BIO_sock_should_retry(ret))
+                BIO_set_retry_read(b);
+        }
+    }
+    return (ret);
+}
+
+static int conn_write(BIO *b, const char *in, int inl)
+{
+    int ret;
+    BIO_CONNECT *data;
+
+    data = (BIO_CONNECT *)b->ptr;
+    if (data->state != BIO_CONN_S_OK) {
+        ret = conn_state(b, data);
+        if (ret <= 0)
+            return (ret);
+    }
+
+    clear_socket_error();
+    ret = writesocket(b->num, in, inl);
+    BIO_clear_retry_flags(b);
+    if (ret <= 0) {
+        if (BIO_sock_should_retry(ret))
+            BIO_set_retry_write(b);
+    }
+    return (ret);
+}
+
+static long conn_ctrl(BIO *b, int cmd, long num, void *ptr)
+{
+    BIO *dbio;
+    int *ip;
+    const char **pptr = NULL;
+    long ret = 1;
+    BIO_CONNECT *data;
+
+    data = (BIO_CONNECT *)b->ptr;
+
+    switch (cmd) {
+    case BIO_CTRL_RESET:
+        ret = 0;
+        data->state = BIO_CONN_S_BEFORE;
+        conn_close_socket(b);
+        BIO_ADDRINFO_free(data->addr_first);
+        data->addr_first = NULL;
+        b->flags = 0;
+        break;
+    case BIO_C_DO_STATE_MACHINE:
+        /* use this one to start the connection */
+        if (data->state != BIO_CONN_S_OK)
+            ret = (long)conn_state(b, data);
+        else
+            ret = 1;
+        break;
+    case BIO_C_GET_CONNECT:
+        if (ptr != NULL) {
+            pptr = (const char **)ptr;
+            if (num == 0) {
+                *pptr = data->param_hostname;
+            } else if (num == 1) {
+                *pptr = data->param_service;
+            } else if (num == 2) {
+                *pptr = (const char *)BIO_ADDRINFO_address(data->addr_iter);
+            } else if (num == 3) {
+                switch (BIO_ADDRINFO_family(data->addr_iter)) {
+# ifdef AF_INET6
+                case AF_INET6:
+                    ret = BIO_FAMILY_IPV6;
+                    break;
+# endif
+                case AF_INET:
+                    ret = BIO_FAMILY_IPV4;
+                    break;
+                case 0:
+                    ret = data->connect_family;
+                    break;
+                default:
+                    ret = -1;
+                    break;
+                }
+            } else {
+                ret = 0;
+            }
+        } else {
+            ret = 0;
+        }
+        break;
+    case BIO_C_SET_CONNECT:
+        if (ptr != NULL) {
+            b->init = 1;
+            if (num == 0) {
+                char *hold_service = data->param_service;
+                /* We affect the hostname regardless.  However, the input
+                 * string might contain a host:service spec, so we must
+                 * parse it, which might or might not affect the service
+                 */
+                OPENSSL_free(data->param_hostname);
+                data->param_hostname = NULL;
+                ret = BIO_parse_hostserv(ptr,
+                                         &data->param_hostname,
+                                         &data->param_service,
+                                         BIO_PARSE_PRIO_HOST);
+                if (hold_service != data->param_service)
+                    OPENSSL_free(hold_service);
+            } else if (num == 1) {
+                OPENSSL_free(data->param_service);
+                data->param_service = BUF_strdup(ptr);
+            } else if (num == 2) {
+                const BIO_ADDR *addr = (const BIO_ADDR *)ptr;
+                if (ret) {
+                    data->param_hostname = BIO_ADDR_hostname_string(addr, 1);
+                    data->param_service = BIO_ADDR_service_string(addr, 1);
+                    BIO_ADDRINFO_free(data->addr_first);
+                    data->addr_first = NULL;
+                    data->addr_iter = NULL;
+                }
+            } else if (num == 3) {
+                data->connect_family = *(int *)ptr;
+            } else {
+                ret = 0;
+            }
+        }
+        break;
+    case BIO_C_SET_NBIO:
+        if (num != 0)
+            data->connect_mode |= BIO_SOCK_NONBLOCK;
+        else
+            data->connect_mode &= ~BIO_SOCK_NONBLOCK;
+        break;
+    case BIO_C_SET_CONNECT_MODE:
+        data->connect_mode = (int)num;
+        break;
+    case BIO_C_GET_FD:
+        if (b->init) {
+            ip = (int *)ptr;
+            if (ip != NULL)
+                *ip = b->num;
+            ret = b->num;
+        } else
+            ret = -1;
+        break;
+    case BIO_CTRL_GET_CLOSE:
+        ret = b->shutdown;
+        break;
+    case BIO_CTRL_SET_CLOSE:
+        b->shutdown = (int)num;
+        break;
+    case BIO_CTRL_PENDING:
+    case BIO_CTRL_WPENDING:
+        ret = 0;
+        break;
+    case BIO_CTRL_FLUSH:
+        break;
+    case BIO_CTRL_DUP:
+        {
+            dbio = (BIO *)ptr;
+            if (data->param_hostname)
+                BIO_set_conn_hostname(dbio, data->param_hostname);
+            if (data->param_service)
+                BIO_set_conn_port(dbio, data->param_service);
+            BIO_set_conn_ip_family(dbio, data->connect_family);
+            BIO_set_conn_mode(dbio, data->connect_mode);
+            /*
+             * FIXME: the cast of the function seems unlikely to be a good
+             * idea
+             */
+            (void)BIO_set_info_callback(dbio,
+                                        (bio_info_cb *)data->info_callback);
+        }
+        break;
+    case BIO_CTRL_SET_CALLBACK:
+        {
+# if 0                          /* FIXME: Should this be used? -- Richard
+                                 * Levitte */
+            BIOerr(BIO_F_CONN_CTRL, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
+            ret = -1;
+# else
+            ret = 0;
+# endif
+        }
+        break;
+    case BIO_CTRL_GET_CALLBACK:
+        {
+            int (**fptr) (const BIO *bio, int state, int xret);
+
+            fptr = (int (**)(const BIO *bio, int state, int xret))ptr;
+            *fptr = data->info_callback;
+        }
+        break;
+    default:
+        ret = 0;
+        break;
+    }
+    return (ret);
+}
+
+static long conn_callback_ctrl(BIO *b, int cmd, bio_info_cb *fp)
+{
+    long ret = 1;
+    BIO_CONNECT *data;
+
+    data = (BIO_CONNECT *)b->ptr;
+
+    switch (cmd) {
+    case BIO_CTRL_SET_CALLBACK:
+        {
+            data->info_callback =
+                (int (*)(const struct bio_st *, int, int))fp;
+        }
+        break;
+    default:
+        ret = 0;
+        break;
+    }
+    return (ret);
+}
+
+static int conn_puts(BIO *bp, const char *str)
+{
+    int n, ret;
+
+    n = strlen(str);
+    ret = conn_write(bp, str, n);
+    return (ret);
+}
+
+BIO *BIO_new_connect(const char *str)
+{
+    BIO *ret;
+
+    ret = BIO_new(BIO_s_connect());
+    if (ret == NULL)
+        return (NULL);
+    if (BIO_set_conn_hostname(ret, str))
+        return (ret);
+    BIO_free(ret);
+    return (NULL);
+}
+
+#endif
diff --git a/crypto/bio/bss_dgram.c b/crypto/bio/bss_dgram.c
new file mode 100644
index 000000000..6dfcc9ba7
--- /dev/null
+++ b/crypto/bio/bss_dgram.c
@@ -0,0 +1,1944 @@
+/*
+ * Copyright 2005-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+
+#include "bio_lcl.h"
+#ifndef OPENSSL_NO_DGRAM
+
+# if !(defined(_WIN32) || defined(OPENSSL_SYS_VMS))
+#  include 
+# endif
+# if defined(OPENSSL_SYS_VMS)
+#  include 
+# endif
+
+# ifndef OPENSSL_NO_SCTP
+#  include 
+#  include 
+#  define OPENSSL_SCTP_DATA_CHUNK_TYPE            0x00
+#  define OPENSSL_SCTP_FORWARD_CUM_TSN_CHUNK_TYPE 0xc0
+# endif
+
+# if defined(OPENSSL_SYS_LINUX) && !defined(IP_MTU)
+#  define IP_MTU      14        /* linux is lame */
+# endif
+
+# if OPENSSL_USE_IPV6 && !defined(IPPROTO_IPV6)
+#  define IPPROTO_IPV6 41       /* windows is lame */
+# endif
+
+# if defined(__FreeBSD__) && defined(IN6_IS_ADDR_V4MAPPED)
+/* Standard definition causes type-punning problems. */
+#  undef IN6_IS_ADDR_V4MAPPED
+#  define s6_addr32 __u6_addr.__u6_addr32
+#  define IN6_IS_ADDR_V4MAPPED(a)               \
+        (((a)->s6_addr32[0] == 0) &&          \
+         ((a)->s6_addr32[1] == 0) &&          \
+         ((a)->s6_addr32[2] == htonl(0x0000ffff)))
+# endif
+
+static int dgram_write(BIO *h, const char *buf, int num);
+static int dgram_read(BIO *h, char *buf, int size);
+static int dgram_puts(BIO *h, const char *str);
+static long dgram_ctrl(BIO *h, int cmd, long arg1, void *arg2);
+static int dgram_new(BIO *h);
+static int dgram_free(BIO *data);
+static int dgram_clear(BIO *bio);
+
+# ifndef OPENSSL_NO_SCTP
+static int dgram_sctp_write(BIO *h, const char *buf, int num);
+static int dgram_sctp_read(BIO *h, char *buf, int size);
+static int dgram_sctp_puts(BIO *h, const char *str);
+static long dgram_sctp_ctrl(BIO *h, int cmd, long arg1, void *arg2);
+static int dgram_sctp_new(BIO *h);
+static int dgram_sctp_free(BIO *data);
+#  ifdef SCTP_AUTHENTICATION_EVENT
+static void dgram_sctp_handle_auth_free_key_event(BIO *b, union sctp_notification
+                                                  *snp);
+#  endif
+# endif
+
+static int BIO_dgram_should_retry(int s);
+
+static void get_current_time(struct timeval *t);
+
+static const BIO_METHOD methods_dgramp = {
+    BIO_TYPE_DGRAM,
+    "datagram socket",
+    dgram_write,
+    dgram_read,
+    dgram_puts,
+    NULL,                       /* dgram_gets, */
+    dgram_ctrl,
+    dgram_new,
+    dgram_free,
+    NULL,
+};
+
+# ifndef OPENSSL_NO_SCTP
+static const BIO_METHOD methods_dgramp_sctp = {
+    BIO_TYPE_DGRAM_SCTP,
+    "datagram sctp socket",
+    dgram_sctp_write,
+    dgram_sctp_read,
+    dgram_sctp_puts,
+    NULL,                       /* dgram_gets, */
+    dgram_sctp_ctrl,
+    dgram_sctp_new,
+    dgram_sctp_free,
+    NULL,
+};
+# endif
+
+typedef struct bio_dgram_data_st {
+    BIO_ADDR peer;
+    unsigned int connected;
+    unsigned int _errno;
+    unsigned int mtu;
+    struct timeval next_timeout;
+    struct timeval socket_timeout;
+    unsigned int peekmode;
+} bio_dgram_data;
+
+# ifndef OPENSSL_NO_SCTP
+typedef struct bio_dgram_sctp_save_message_st {
+    BIO *bio;
+    char *data;
+    int length;
+} bio_dgram_sctp_save_message;
+
+typedef struct bio_dgram_sctp_data_st {
+    BIO_ADDR peer;
+    unsigned int connected;
+    unsigned int _errno;
+    unsigned int mtu;
+    struct bio_dgram_sctp_sndinfo sndinfo;
+    struct bio_dgram_sctp_rcvinfo rcvinfo;
+    struct bio_dgram_sctp_prinfo prinfo;
+    void (*handle_notifications) (BIO *bio, void *context, void *buf);
+    void *notification_context;
+    int in_handshake;
+    int ccs_rcvd;
+    int ccs_sent;
+    int save_shutdown;
+    int peer_auth_tested;
+    bio_dgram_sctp_save_message saved_message;
+} bio_dgram_sctp_data;
+# endif
+
+const BIO_METHOD *BIO_s_datagram(void)
+{
+    return (&methods_dgramp);
+}
+
+BIO *BIO_new_dgram(int fd, int close_flag)
+{
+    BIO *ret;
+
+    ret = BIO_new(BIO_s_datagram());
+    if (ret == NULL)
+        return (NULL);
+    BIO_set_fd(ret, fd, close_flag);
+    return (ret);
+}
+
+static int dgram_new(BIO *bi)
+{
+    bio_dgram_data *data = OPENSSL_zalloc(sizeof(*data));
+
+    if (data == NULL)
+        return 0;
+    bi->ptr = data;
+    return (1);
+}
+
+static int dgram_free(BIO *a)
+{
+    bio_dgram_data *data;
+
+    if (a == NULL)
+        return (0);
+    if (!dgram_clear(a))
+        return 0;
+
+    data = (bio_dgram_data *)a->ptr;
+    OPENSSL_free(data);
+
+    return (1);
+}
+
+static int dgram_clear(BIO *a)
+{
+    if (a == NULL)
+        return (0);
+    if (a->shutdown) {
+        if (a->init) {
+            BIO_closesocket(a->num);
+        }
+        a->init = 0;
+        a->flags = 0;
+    }
+    return (1);
+}
+
+static void dgram_adjust_rcv_timeout(BIO *b)
+{
+# if defined(SO_RCVTIMEO)
+    bio_dgram_data *data = (bio_dgram_data *)b->ptr;
+    union {
+        size_t s;
+        int i;
+    } sz = {
+        0
+    };
+
+    /* Is a timer active? */
+    if (data->next_timeout.tv_sec > 0 || data->next_timeout.tv_usec > 0) {
+        struct timeval timenow, timeleft;
+
+        /* Read current socket timeout */
+#  ifdef OPENSSL_SYS_WINDOWS
+        int timeout;
+
+        sz.i = sizeof(timeout);
+        if (getsockopt(b->num, SOL_SOCKET, SO_RCVTIMEO,
+                       (void *)&timeout, &sz.i) < 0) {
+            perror("getsockopt");
+        } else {
+            data->socket_timeout.tv_sec = timeout / 1000;
+            data->socket_timeout.tv_usec = (timeout % 1000) * 1000;
+        }
+#  else
+        sz.i = sizeof(data->socket_timeout);
+        if (getsockopt(b->num, SOL_SOCKET, SO_RCVTIMEO,
+                       &(data->socket_timeout), (void *)&sz) < 0) {
+            perror("getsockopt");
+        } else if (sizeof(sz.s) != sizeof(sz.i) && sz.i == 0)
+            OPENSSL_assert(sz.s <= sizeof(data->socket_timeout));
+#  endif
+
+        /* Get current time */
+        get_current_time(&timenow);
+
+        /* Calculate time left until timer expires */
+        memcpy(&timeleft, &(data->next_timeout), sizeof(struct timeval));
+        if (timeleft.tv_usec < timenow.tv_usec) {
+            timeleft.tv_usec = 1000000 - timenow.tv_usec + timeleft.tv_usec;
+            timeleft.tv_sec--;
+        } else {
+            timeleft.tv_usec -= timenow.tv_usec;
+        }
+        if (timeleft.tv_sec < timenow.tv_sec) {
+            timeleft.tv_sec = 0;
+            timeleft.tv_usec = 1;
+        } else {
+            timeleft.tv_sec -= timenow.tv_sec;
+        }
+
+        /*
+         * Adjust socket timeout if next handshake message timer will expire
+         * earlier.
+         */
+        if ((data->socket_timeout.tv_sec == 0
+             && data->socket_timeout.tv_usec == 0)
+            || (data->socket_timeout.tv_sec > timeleft.tv_sec)
+            || (data->socket_timeout.tv_sec == timeleft.tv_sec
+                && data->socket_timeout.tv_usec >= timeleft.tv_usec)) {
+#  ifdef OPENSSL_SYS_WINDOWS
+            timeout = timeleft.tv_sec * 1000 + timeleft.tv_usec / 1000;
+            if (setsockopt(b->num, SOL_SOCKET, SO_RCVTIMEO,
+                           (void *)&timeout, sizeof(timeout)) < 0) {
+                perror("setsockopt");
+            }
+#  else
+            if (setsockopt(b->num, SOL_SOCKET, SO_RCVTIMEO, &timeleft,
+                           sizeof(struct timeval)) < 0) {
+                perror("setsockopt");
+            }
+#  endif
+        }
+    }
+# endif
+}
+
+static void dgram_reset_rcv_timeout(BIO *b)
+{
+# if defined(SO_RCVTIMEO)
+    bio_dgram_data *data = (bio_dgram_data *)b->ptr;
+
+    /* Is a timer active? */
+    if (data->next_timeout.tv_sec > 0 || data->next_timeout.tv_usec > 0) {
+#  ifdef OPENSSL_SYS_WINDOWS
+        int timeout = data->socket_timeout.tv_sec * 1000 +
+            data->socket_timeout.tv_usec / 1000;
+        if (setsockopt(b->num, SOL_SOCKET, SO_RCVTIMEO,
+                       (void *)&timeout, sizeof(timeout)) < 0) {
+            perror("setsockopt");
+        }
+#  else
+        if (setsockopt
+            (b->num, SOL_SOCKET, SO_RCVTIMEO, &(data->socket_timeout),
+             sizeof(struct timeval)) < 0) {
+            perror("setsockopt");
+        }
+#  endif
+    }
+# endif
+}
+
+static int dgram_read(BIO *b, char *out, int outl)
+{
+    int ret = 0;
+    bio_dgram_data *data = (bio_dgram_data *)b->ptr;
+    int flags = 0;
+
+    BIO_ADDR peer;
+    socklen_t len = sizeof(peer);
+
+    if (out != NULL) {
+        clear_socket_error();
+        memset(&peer, 0, sizeof(peer));
+        dgram_adjust_rcv_timeout(b);
+        if (data->peekmode)
+            flags = MSG_PEEK;
+        ret = recvfrom(b->num, out, outl, flags,
+                       BIO_ADDR_sockaddr_noconst(&peer), &len);
+
+        if (!data->connected && ret >= 0)
+            BIO_ctrl(b, BIO_CTRL_DGRAM_SET_PEER, 0, &peer);
+
+        BIO_clear_retry_flags(b);
+        if (ret < 0) {
+            if (BIO_dgram_should_retry(ret)) {
+                BIO_set_retry_read(b);
+                data->_errno = get_last_socket_error();
+            }
+        }
+
+        dgram_reset_rcv_timeout(b);
+    }
+    return (ret);
+}
+
+static int dgram_write(BIO *b, const char *in, int inl)
+{
+    int ret;
+    bio_dgram_data *data = (bio_dgram_data *)b->ptr;
+    clear_socket_error();
+
+    if (data->connected)
+        ret = writesocket(b->num, in, inl);
+    else {
+        int peerlen = BIO_ADDR_sockaddr_size(&data->peer);
+
+# if defined(NETWARE_CLIB) && defined(NETWARE_BSDSOCK)
+        ret = sendto(b->num, (char *)in, inl, 0,
+                     BIO_ADDR_sockaddr(&data->peer), peerlen);
+# else
+        ret = sendto(b->num, in, inl, 0,
+                     BIO_ADDR_sockaddr(&data->peer), peerlen);
+# endif
+    }
+
+    BIO_clear_retry_flags(b);
+    if (ret <= 0) {
+        if (BIO_dgram_should_retry(ret)) {
+            BIO_set_retry_write(b);
+            data->_errno = get_last_socket_error();
+        }
+    }
+    return (ret);
+}
+
+static long dgram_get_mtu_overhead(bio_dgram_data *data)
+{
+    long ret;
+
+    switch (BIO_ADDR_family(&data->peer)) {
+    case AF_INET:
+        /*
+         * Assume this is UDP - 20 bytes for IP, 8 bytes for UDP
+         */
+        ret = 28;
+        break;
+# ifdef AF_INET6
+    case AF_INET6:
+        {
+#  ifdef IN6_IS_ADDR_V4MAPPED
+            struct in6_addr tmp_addr;
+            if (BIO_ADDR_rawaddress(&data->peer, &tmp_addr, NULL)
+                && IN6_IS_ADDR_V4MAPPED(&tmp_addr))
+                /*
+                 * Assume this is UDP - 20 bytes for IP, 8 bytes for UDP
+                 */
+                ret = 28;
+            else
+#  endif
+            /*
+             * Assume this is UDP - 40 bytes for IP, 8 bytes for UDP
+             */
+            ret = 48;
+        }
+        break;
+# endif
+    default:
+        /* We don't know. Go with the historical default */
+        ret = 28;
+        break;
+    }
+    return ret;
+}
+
+static long dgram_ctrl(BIO *b, int cmd, long num, void *ptr)
+{
+    long ret = 1;
+    int *ip;
+    bio_dgram_data *data = NULL;
+    int sockopt_val = 0;
+    int d_errno;
+# if defined(OPENSSL_SYS_LINUX) && (defined(IP_MTU_DISCOVER) || defined(IP_MTU))
+    socklen_t sockopt_len;      /* assume that system supporting IP_MTU is
+                                 * modern enough to define socklen_t */
+    socklen_t addr_len;
+    BIO_ADDR addr;
+# endif
+
+    data = (bio_dgram_data *)b->ptr;
+
+    switch (cmd) {
+    case BIO_CTRL_RESET:
+        num = 0;
+        ret = 0;
+        break;
+    case BIO_CTRL_INFO:
+        ret = 0;
+        break;
+    case BIO_C_SET_FD:
+        dgram_clear(b);
+        b->num = *((int *)ptr);
+        b->shutdown = (int)num;
+        b->init = 1;
+        break;
+    case BIO_C_GET_FD:
+        if (b->init) {
+            ip = (int *)ptr;
+            if (ip != NULL)
+                *ip = b->num;
+            ret = b->num;
+        } else
+            ret = -1;
+        break;
+    case BIO_CTRL_GET_CLOSE:
+        ret = b->shutdown;
+        break;
+    case BIO_CTRL_SET_CLOSE:
+        b->shutdown = (int)num;
+        break;
+    case BIO_CTRL_PENDING:
+    case BIO_CTRL_WPENDING:
+        ret = 0;
+        break;
+    case BIO_CTRL_DUP:
+    case BIO_CTRL_FLUSH:
+        ret = 1;
+        break;
+    case BIO_CTRL_DGRAM_CONNECT:
+        BIO_ADDR_make(&data->peer, BIO_ADDR_sockaddr((BIO_ADDR *)ptr));
+        break;
+        /* (Linux)kernel sets DF bit on outgoing IP packets */
+    case BIO_CTRL_DGRAM_MTU_DISCOVER:
+# if defined(OPENSSL_SYS_LINUX) && defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DO)
+        addr_len = (socklen_t) sizeof(addr);
+        memset(&addr, 0, sizeof(addr));
+        if (getsockname(b->num, &addr.sa, &addr_len) < 0) {
+            ret = 0;
+            break;
+        }
+        switch (addr.sa.sa_family) {
+        case AF_INET:
+            sockopt_val = IP_PMTUDISC_DO;
+            if ((ret = setsockopt(b->num, IPPROTO_IP, IP_MTU_DISCOVER,
+                                  &sockopt_val, sizeof(sockopt_val))) < 0)
+                perror("setsockopt");
+            break;
+#  if OPENSSL_USE_IPV6 && defined(IPV6_MTU_DISCOVER) && defined(IPV6_PMTUDISC_DO)
+        case AF_INET6:
+            sockopt_val = IPV6_PMTUDISC_DO;
+            if ((ret = setsockopt(b->num, IPPROTO_IPV6, IPV6_MTU_DISCOVER,
+                                  &sockopt_val, sizeof(sockopt_val))) < 0)
+                perror("setsockopt");
+            break;
+#  endif
+        default:
+            ret = -1;
+            break;
+        }
+# else
+        ret = -1;
+# endif
+        break;
+    case BIO_CTRL_DGRAM_QUERY_MTU:
+# if defined(OPENSSL_SYS_LINUX) && defined(IP_MTU)
+        addr_len = (socklen_t) sizeof(addr);
+        memset(&addr, 0, sizeof(addr));
+        if (getsockname(b->num, &addr.sa, &addr_len) < 0) {
+            ret = 0;
+            break;
+        }
+        sockopt_len = sizeof(sockopt_val);
+        switch (addr.sa.sa_family) {
+        case AF_INET:
+            if ((ret =
+                 getsockopt(b->num, IPPROTO_IP, IP_MTU, (void *)&sockopt_val,
+                            &sockopt_len)) < 0 || sockopt_val < 0) {
+                ret = 0;
+            } else {
+                /*
+                 * we assume that the transport protocol is UDP and no IP
+                 * options are used.
+                 */
+                data->mtu = sockopt_val - 8 - 20;
+                ret = data->mtu;
+            }
+            break;
+#  if OPENSSL_USE_IPV6 && defined(IPV6_MTU)
+        case AF_INET6:
+            if ((ret =
+                 getsockopt(b->num, IPPROTO_IPV6, IPV6_MTU,
+                            (void *)&sockopt_val, &sockopt_len)) < 0
+                || sockopt_val < 0) {
+                ret = 0;
+            } else {
+                /*
+                 * we assume that the transport protocol is UDP and no IPV6
+                 * options are used.
+                 */
+                data->mtu = sockopt_val - 8 - 40;
+                ret = data->mtu;
+            }
+            break;
+#  endif
+        default:
+            ret = 0;
+            break;
+        }
+# else
+        ret = 0;
+# endif
+        break;
+    case BIO_CTRL_DGRAM_GET_FALLBACK_MTU:
+        ret = -dgram_get_mtu_overhead(data);
+        switch (BIO_ADDR_family(&data->peer)) {
+        case AF_INET:
+            ret += 576;
+            break;
+# if OPENSSL_USE_IPV6
+        case AF_INET6:
+            {
+#  ifdef IN6_IS_ADDR_V4MAPPED
+                struct in6_addr tmp_addr;
+                if (BIO_ADDR_rawaddress(&data->peer, &tmp_addr, NULL)
+                    && IN6_IS_ADDR_V4MAPPED(&tmp_addr))
+                    ret += 576;
+                else
+#  endif
+                    ret += 1280;
+            }
+            break;
+# endif
+        default:
+            ret += 576;
+            break;
+        }
+        break;
+    case BIO_CTRL_DGRAM_GET_MTU:
+        return data->mtu;
+    case BIO_CTRL_DGRAM_SET_MTU:
+        data->mtu = num;
+        ret = num;
+        break;
+    case BIO_CTRL_DGRAM_SET_CONNECTED:
+        if (ptr != NULL) {
+            data->connected = 1;
+            BIO_ADDR_make(&data->peer, BIO_ADDR_sockaddr((BIO_ADDR *)ptr));
+        } else {
+            data->connected = 0;
+            memset(&data->peer, 0, sizeof(data->peer));
+        }
+        break;
+    case BIO_CTRL_DGRAM_GET_PEER:
+        ret = BIO_ADDR_sockaddr_size(&data->peer);
+        /* FIXME: if num < ret, we will only return part of an address.
+           That should bee an error, no? */
+        if (num == 0 || num > ret)
+            num = ret;
+        memcpy(ptr, &data->peer, (ret = num));
+        break;
+    case BIO_CTRL_DGRAM_SET_PEER:
+        BIO_ADDR_make(&data->peer, BIO_ADDR_sockaddr((BIO_ADDR *)ptr));
+        break;
+    case BIO_CTRL_DGRAM_SET_NEXT_TIMEOUT:
+        memcpy(&(data->next_timeout), ptr, sizeof(struct timeval));
+        break;
+# if defined(SO_RCVTIMEO)
+    case BIO_CTRL_DGRAM_SET_RECV_TIMEOUT:
+#  ifdef OPENSSL_SYS_WINDOWS
+        {
+            struct timeval *tv = (struct timeval *)ptr;
+            int timeout = tv->tv_sec * 1000 + tv->tv_usec / 1000;
+            if (setsockopt(b->num, SOL_SOCKET, SO_RCVTIMEO,
+                           (void *)&timeout, sizeof(timeout)) < 0) {
+                perror("setsockopt");
+                ret = -1;
+            }
+        }
+#  else
+        if (setsockopt(b->num, SOL_SOCKET, SO_RCVTIMEO, ptr,
+                       sizeof(struct timeval)) < 0) {
+            perror("setsockopt");
+            ret = -1;
+        }
+#  endif
+        break;
+    case BIO_CTRL_DGRAM_GET_RECV_TIMEOUT:
+        {
+            union {
+                size_t s;
+                int i;
+            } sz = {
+                0
+            };
+#  ifdef OPENSSL_SYS_WINDOWS
+            int timeout;
+            struct timeval *tv = (struct timeval *)ptr;
+
+            sz.i = sizeof(timeout);
+            if (getsockopt(b->num, SOL_SOCKET, SO_RCVTIMEO,
+                           (void *)&timeout, &sz.i) < 0) {
+                perror("getsockopt");
+                ret = -1;
+            } else {
+                tv->tv_sec = timeout / 1000;
+                tv->tv_usec = (timeout % 1000) * 1000;
+                ret = sizeof(*tv);
+            }
+#  else
+            sz.i = sizeof(struct timeval);
+            if (getsockopt(b->num, SOL_SOCKET, SO_RCVTIMEO,
+                           ptr, (void *)&sz) < 0) {
+                perror("getsockopt");
+                ret = -1;
+            } else if (sizeof(sz.s) != sizeof(sz.i) && sz.i == 0) {
+                OPENSSL_assert(sz.s <= sizeof(struct timeval));
+                ret = (int)sz.s;
+            } else
+                ret = sz.i;
+#  endif
+        }
+        break;
+# endif
+# if defined(SO_SNDTIMEO)
+    case BIO_CTRL_DGRAM_SET_SEND_TIMEOUT:
+#  ifdef OPENSSL_SYS_WINDOWS
+        {
+            struct timeval *tv = (struct timeval *)ptr;
+            int timeout = tv->tv_sec * 1000 + tv->tv_usec / 1000;
+            if (setsockopt(b->num, SOL_SOCKET, SO_SNDTIMEO,
+                           (void *)&timeout, sizeof(timeout)) < 0) {
+                perror("setsockopt");
+                ret = -1;
+            }
+        }
+#  else
+        if (setsockopt(b->num, SOL_SOCKET, SO_SNDTIMEO, ptr,
+                       sizeof(struct timeval)) < 0) {
+            perror("setsockopt");
+            ret = -1;
+        }
+#  endif
+        break;
+    case BIO_CTRL_DGRAM_GET_SEND_TIMEOUT:
+        {
+            union {
+                size_t s;
+                int i;
+            } sz = {
+                0
+            };
+#  ifdef OPENSSL_SYS_WINDOWS
+            int timeout;
+            struct timeval *tv = (struct timeval *)ptr;
+
+            sz.i = sizeof(timeout);
+            if (getsockopt(b->num, SOL_SOCKET, SO_SNDTIMEO,
+                           (void *)&timeout, &sz.i) < 0) {
+                perror("getsockopt");
+                ret = -1;
+            } else {
+                tv->tv_sec = timeout / 1000;
+                tv->tv_usec = (timeout % 1000) * 1000;
+                ret = sizeof(*tv);
+            }
+#  else
+            sz.i = sizeof(struct timeval);
+            if (getsockopt(b->num, SOL_SOCKET, SO_SNDTIMEO,
+                           ptr, (void *)&sz) < 0) {
+                perror("getsockopt");
+                ret = -1;
+            } else if (sizeof(sz.s) != sizeof(sz.i) && sz.i == 0) {
+                OPENSSL_assert(sz.s <= sizeof(struct timeval));
+                ret = (int)sz.s;
+            } else
+                ret = sz.i;
+#  endif
+        }
+        break;
+# endif
+    case BIO_CTRL_DGRAM_GET_SEND_TIMER_EXP:
+        /* fall-through */
+    case BIO_CTRL_DGRAM_GET_RECV_TIMER_EXP:
+# ifdef OPENSSL_SYS_WINDOWS
+        d_errno = (data->_errno == WSAETIMEDOUT);
+# else
+        d_errno = (data->_errno == EAGAIN);
+# endif
+        if (d_errno) {
+            ret = 1;
+            data->_errno = 0;
+        } else
+            ret = 0;
+        break;
+# ifdef EMSGSIZE
+    case BIO_CTRL_DGRAM_MTU_EXCEEDED:
+        if (data->_errno == EMSGSIZE) {
+            ret = 1;
+            data->_errno = 0;
+        } else
+            ret = 0;
+        break;
+# endif
+    case BIO_CTRL_DGRAM_SET_DONT_FRAG:
+        sockopt_val = num ? 1 : 0;
+
+        switch (data->peer.sa.sa_family) {
+        case AF_INET:
+# if defined(IP_DONTFRAG)
+            if ((ret = setsockopt(b->num, IPPROTO_IP, IP_DONTFRAG,
+                                  &sockopt_val, sizeof(sockopt_val))) < 0) {
+                perror("setsockopt");
+                ret = -1;
+            }
+# elif defined(OPENSSL_SYS_LINUX) && defined(IP_MTU_DISCOVER) && defined (IP_PMTUDISC_PROBE)
+            if ((sockopt_val = num ? IP_PMTUDISC_PROBE : IP_PMTUDISC_DONT),
+                (ret = setsockopt(b->num, IPPROTO_IP, IP_MTU_DISCOVER,
+                                  &sockopt_val, sizeof(sockopt_val))) < 0) {
+                perror("setsockopt");
+                ret = -1;
+            }
+# elif defined(OPENSSL_SYS_WINDOWS) && defined(IP_DONTFRAGMENT)
+            if ((ret = setsockopt(b->num, IPPROTO_IP, IP_DONTFRAGMENT,
+                                  (const char *)&sockopt_val,
+                                  sizeof(sockopt_val))) < 0) {
+                perror("setsockopt");
+                ret = -1;
+            }
+# else
+            ret = -1;
+# endif
+            break;
+# if OPENSSL_USE_IPV6
+        case AF_INET6:
+#  if defined(IPV6_DONTFRAG)
+            if ((ret = setsockopt(b->num, IPPROTO_IPV6, IPV6_DONTFRAG,
+                                  (const void *)&sockopt_val,
+                                  sizeof(sockopt_val))) < 0) {
+                perror("setsockopt");
+                ret = -1;
+            }
+#  elif defined(OPENSSL_SYS_LINUX) && defined(IPV6_MTUDISCOVER)
+            if ((sockopt_val = num ? IP_PMTUDISC_PROBE : IP_PMTUDISC_DONT),
+                (ret = setsockopt(b->num, IPPROTO_IPV6, IPV6_MTU_DISCOVER,
+                                  &sockopt_val, sizeof(sockopt_val))) < 0) {
+                perror("setsockopt");
+                ret = -1;
+            }
+#  else
+            ret = -1;
+#  endif
+            break;
+# endif
+        default:
+            ret = -1;
+            break;
+        }
+        break;
+    case BIO_CTRL_DGRAM_GET_MTU_OVERHEAD:
+        ret = dgram_get_mtu_overhead(data);
+        break;
+    case BIO_CTRL_DGRAM_SET_PEEK_MODE:
+        data->peekmode = (unsigned int)num;
+        break;
+    default:
+        ret = 0;
+        break;
+    }
+    return (ret);
+}
+
+static int dgram_puts(BIO *bp, const char *str)
+{
+    int n, ret;
+
+    n = strlen(str);
+    ret = dgram_write(bp, str, n);
+    return (ret);
+}
+
+# ifndef OPENSSL_NO_SCTP
+const BIO_METHOD *BIO_s_datagram_sctp(void)
+{
+    return (&methods_dgramp_sctp);
+}
+
+BIO *BIO_new_dgram_sctp(int fd, int close_flag)
+{
+    BIO *bio;
+    int ret, optval = 20000;
+    int auth_data = 0, auth_forward = 0;
+    unsigned char *p;
+    struct sctp_authchunk auth;
+    struct sctp_authchunks *authchunks;
+    socklen_t sockopt_len;
+#  ifdef SCTP_AUTHENTICATION_EVENT
+#   ifdef SCTP_EVENT
+    struct sctp_event event;
+#   else
+    struct sctp_event_subscribe event;
+#   endif
+#  endif
+
+    bio = BIO_new(BIO_s_datagram_sctp());
+    if (bio == NULL)
+        return (NULL);
+    BIO_set_fd(bio, fd, close_flag);
+
+    /* Activate SCTP-AUTH for DATA and FORWARD-TSN chunks */
+    auth.sauth_chunk = OPENSSL_SCTP_DATA_CHUNK_TYPE;
+    ret =
+        setsockopt(fd, IPPROTO_SCTP, SCTP_AUTH_CHUNK, &auth,
+                   sizeof(struct sctp_authchunk));
+    if (ret < 0) {
+        BIO_vfree(bio);
+        return (NULL);
+    }
+    auth.sauth_chunk = OPENSSL_SCTP_FORWARD_CUM_TSN_CHUNK_TYPE;
+    ret =
+        setsockopt(fd, IPPROTO_SCTP, SCTP_AUTH_CHUNK, &auth,
+                   sizeof(struct sctp_authchunk));
+    if (ret < 0) {
+        BIO_vfree(bio);
+        return (NULL);
+    }
+
+    /*
+     * Test if activation was successful. When using accept(), SCTP-AUTH has
+     * to be activated for the listening socket already, otherwise the
+     * connected socket won't use it.
+     */
+    sockopt_len = (socklen_t) (sizeof(sctp_assoc_t) + 256 * sizeof(uint8_t));
+    authchunks = OPENSSL_zalloc(sockopt_len);
+    if (authchunks == NULL) {
+        BIO_vfree(bio);
+        return (NULL);
+    }
+    ret = getsockopt(fd, IPPROTO_SCTP, SCTP_LOCAL_AUTH_CHUNKS, authchunks,
+                   &sockopt_len);
+    if (ret < 0) {
+        OPENSSL_free(authchunks);
+        BIO_vfree(bio);
+        return (NULL);
+    }
+
+    for (p = (unsigned char *)authchunks->gauth_chunks;
+         p < (unsigned char *)authchunks + sockopt_len;
+         p += sizeof(uint8_t)) {
+        if (*p == OPENSSL_SCTP_DATA_CHUNK_TYPE)
+            auth_data = 1;
+        if (*p == OPENSSL_SCTP_FORWARD_CUM_TSN_CHUNK_TYPE)
+            auth_forward = 1;
+    }
+
+    OPENSSL_free(authchunks);
+
+    OPENSSL_assert(auth_data);
+    OPENSSL_assert(auth_forward);
+
+#  ifdef SCTP_AUTHENTICATION_EVENT
+#   ifdef SCTP_EVENT
+    memset(&event, 0, sizeof(event));
+    event.se_assoc_id = 0;
+    event.se_type = SCTP_AUTHENTICATION_EVENT;
+    event.se_on = 1;
+    ret =
+        setsockopt(fd, IPPROTO_SCTP, SCTP_EVENT, &event,
+                   sizeof(struct sctp_event));
+    if (ret < 0) {
+        BIO_vfree(bio);
+        return (NULL);
+    }
+#   else
+    sockopt_len = (socklen_t) sizeof(struct sctp_event_subscribe);
+    ret = getsockopt(fd, IPPROTO_SCTP, SCTP_EVENTS, &event, &sockopt_len);
+    if (ret < 0) {
+        BIO_vfree(bio);
+        return (NULL);
+    }
+
+    event.sctp_authentication_event = 1;
+
+    ret =
+        setsockopt(fd, IPPROTO_SCTP, SCTP_EVENTS, &event,
+                   sizeof(struct sctp_event_subscribe));
+    if (ret < 0) {
+        BIO_vfree(bio);
+        return (NULL);
+    }
+#   endif
+#  endif
+
+    /*
+     * Disable partial delivery by setting the min size larger than the max
+     * record size of 2^14 + 2048 + 13
+     */
+    ret =
+        setsockopt(fd, IPPROTO_SCTP, SCTP_PARTIAL_DELIVERY_POINT, &optval,
+                   sizeof(optval));
+    if (ret < 0) {
+        BIO_vfree(bio);
+        return (NULL);
+    }
+
+    return (bio);
+}
+
+int BIO_dgram_is_sctp(BIO *bio)
+{
+    return (BIO_method_type(bio) == BIO_TYPE_DGRAM_SCTP);
+}
+
+static int dgram_sctp_new(BIO *bi)
+{
+    bio_dgram_sctp_data *data = NULL;
+
+    bi->init = 0;
+    bi->num = 0;
+    data = OPENSSL_zalloc(sizeof(*data));
+    if (data == NULL)
+        return 0;
+#  ifdef SCTP_PR_SCTP_NONE
+    data->prinfo.pr_policy = SCTP_PR_SCTP_NONE;
+#  endif
+    bi->ptr = data;
+
+    bi->flags = 0;
+    return (1);
+}
+
+static int dgram_sctp_free(BIO *a)
+{
+    bio_dgram_sctp_data *data;
+
+    if (a == NULL)
+        return (0);
+    if (!dgram_clear(a))
+        return 0;
+
+    data = (bio_dgram_sctp_data *) a->ptr;
+    if (data != NULL) {
+        OPENSSL_free(data->saved_message.data);
+        OPENSSL_free(data);
+    }
+
+    return (1);
+}
+
+#  ifdef SCTP_AUTHENTICATION_EVENT
+void dgram_sctp_handle_auth_free_key_event(BIO *b,
+                                           union sctp_notification *snp)
+{
+    int ret;
+    struct sctp_authkey_event *authkeyevent = &snp->sn_auth_event;
+
+    if (authkeyevent->auth_indication == SCTP_AUTH_FREE_KEY) {
+        struct sctp_authkeyid authkeyid;
+
+        /* delete key */
+        authkeyid.scact_keynumber = authkeyevent->auth_keynumber;
+        ret = setsockopt(b->num, IPPROTO_SCTP, SCTP_AUTH_DELETE_KEY,
+                         &authkeyid, sizeof(struct sctp_authkeyid));
+    }
+}
+#  endif
+
+static int dgram_sctp_read(BIO *b, char *out, int outl)
+{
+    int ret = 0, n = 0, i, optval;
+    socklen_t optlen;
+    bio_dgram_sctp_data *data = (bio_dgram_sctp_data *) b->ptr;
+    union sctp_notification *snp;
+    struct msghdr msg;
+    struct iovec iov;
+    struct cmsghdr *cmsg;
+    char cmsgbuf[512];
+
+    if (out != NULL) {
+        clear_socket_error();
+
+        do {
+            memset(&data->rcvinfo, 0, sizeof(data->rcvinfo));
+            iov.iov_base = out;
+            iov.iov_len = outl;
+            msg.msg_name = NULL;
+            msg.msg_namelen = 0;
+            msg.msg_iov = &iov;
+            msg.msg_iovlen = 1;
+            msg.msg_control = cmsgbuf;
+            msg.msg_controllen = 512;
+            msg.msg_flags = 0;
+            n = recvmsg(b->num, &msg, 0);
+
+            if (n <= 0) {
+                if (n < 0)
+                    ret = n;
+                break;
+            }
+
+            if (msg.msg_controllen > 0) {
+                for (cmsg = CMSG_FIRSTHDR(&msg); cmsg;
+                     cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+                    if (cmsg->cmsg_level != IPPROTO_SCTP)
+                        continue;
+#  ifdef SCTP_RCVINFO
+                    if (cmsg->cmsg_type == SCTP_RCVINFO) {
+                        struct sctp_rcvinfo *rcvinfo;
+
+                        rcvinfo = (struct sctp_rcvinfo *)CMSG_DATA(cmsg);
+                        data->rcvinfo.rcv_sid = rcvinfo->rcv_sid;
+                        data->rcvinfo.rcv_ssn = rcvinfo->rcv_ssn;
+                        data->rcvinfo.rcv_flags = rcvinfo->rcv_flags;
+                        data->rcvinfo.rcv_ppid = rcvinfo->rcv_ppid;
+                        data->rcvinfo.rcv_tsn = rcvinfo->rcv_tsn;
+                        data->rcvinfo.rcv_cumtsn = rcvinfo->rcv_cumtsn;
+                        data->rcvinfo.rcv_context = rcvinfo->rcv_context;
+                    }
+#  endif
+#  ifdef SCTP_SNDRCV
+                    if (cmsg->cmsg_type == SCTP_SNDRCV) {
+                        struct sctp_sndrcvinfo *sndrcvinfo;
+
+                        sndrcvinfo =
+                            (struct sctp_sndrcvinfo *)CMSG_DATA(cmsg);
+                        data->rcvinfo.rcv_sid = sndrcvinfo->sinfo_stream;
+                        data->rcvinfo.rcv_ssn = sndrcvinfo->sinfo_ssn;
+                        data->rcvinfo.rcv_flags = sndrcvinfo->sinfo_flags;
+                        data->rcvinfo.rcv_ppid = sndrcvinfo->sinfo_ppid;
+                        data->rcvinfo.rcv_tsn = sndrcvinfo->sinfo_tsn;
+                        data->rcvinfo.rcv_cumtsn = sndrcvinfo->sinfo_cumtsn;
+                        data->rcvinfo.rcv_context = sndrcvinfo->sinfo_context;
+                    }
+#  endif
+                }
+            }
+
+            if (msg.msg_flags & MSG_NOTIFICATION) {
+                snp = (union sctp_notification *)out;
+                if (snp->sn_header.sn_type == SCTP_SENDER_DRY_EVENT) {
+#  ifdef SCTP_EVENT
+                    struct sctp_event event;
+#  else
+                    struct sctp_event_subscribe event;
+                    socklen_t eventsize;
+#  endif
+                    /*
+                     * If a message has been delayed until the socket is dry,
+                     * it can be sent now.
+                     */
+                    if (data->saved_message.length > 0) {
+                        i = dgram_sctp_write(data->saved_message.bio,
+                                         data->saved_message.data,
+                                         data->saved_message.length);
+                        if (i < 0) {
+                            ret = i;
+                            break;
+                        }
+                        OPENSSL_free(data->saved_message.data);
+                        data->saved_message.data = NULL;
+                        data->saved_message.length = 0;
+                    }
+
+                    /* disable sender dry event */
+#  ifdef SCTP_EVENT
+                    memset(&event, 0, sizeof(event));
+                    event.se_assoc_id = 0;
+                    event.se_type = SCTP_SENDER_DRY_EVENT;
+                    event.se_on = 0;
+                    i = setsockopt(b->num, IPPROTO_SCTP, SCTP_EVENT, &event,
+                                   sizeof(struct sctp_event));
+                    if (i < 0) {
+                        ret = i;
+                        break;
+                    }
+#  else
+                    eventsize = sizeof(struct sctp_event_subscribe);
+                    i = getsockopt(b->num, IPPROTO_SCTP, SCTP_EVENTS, &event,
+                                   &eventsize);
+                    if (i < 0) {
+                        ret = i;
+                        break;
+                    }
+
+                    event.sctp_sender_dry_event = 0;
+
+                    i = setsockopt(b->num, IPPROTO_SCTP, SCTP_EVENTS, &event,
+                                   sizeof(struct sctp_event_subscribe));
+                    if (i < 0) {
+                        ret = i;
+                        break;
+                    }
+#  endif
+                }
+#  ifdef SCTP_AUTHENTICATION_EVENT
+                if (snp->sn_header.sn_type == SCTP_AUTHENTICATION_EVENT)
+                    dgram_sctp_handle_auth_free_key_event(b, snp);
+#  endif
+
+                if (data->handle_notifications != NULL)
+                    data->handle_notifications(b, data->notification_context,
+                                               (void *)out);
+
+                memset(out, 0, outl);
+            } else
+                ret += n;
+        }
+        while ((msg.msg_flags & MSG_NOTIFICATION) && (msg.msg_flags & MSG_EOR)
+               && (ret < outl));
+
+        if (ret > 0 && !(msg.msg_flags & MSG_EOR)) {
+            /* Partial message read, this should never happen! */
+
+            /*
+             * The buffer was too small, this means the peer sent a message
+             * that was larger than allowed.
+             */
+            if (ret == outl)
+                return -1;
+
+            /*
+             * Test if socket buffer can handle max record size (2^14 + 2048
+             * + 13)
+             */
+            optlen = (socklen_t) sizeof(int);
+            ret = getsockopt(b->num, SOL_SOCKET, SO_RCVBUF, &optval, &optlen);
+            if (ret >= 0)
+                OPENSSL_assert(optval >= 18445);
+
+            /*
+             * Test if SCTP doesn't partially deliver below max record size
+             * (2^14 + 2048 + 13)
+             */
+            optlen = (socklen_t) sizeof(int);
+            ret =
+                getsockopt(b->num, IPPROTO_SCTP, SCTP_PARTIAL_DELIVERY_POINT,
+                           &optval, &optlen);
+            if (ret >= 0)
+                OPENSSL_assert(optval >= 18445);
+
+            /*
+             * Partially delivered notification??? Probably a bug....
+             */
+            OPENSSL_assert(!(msg.msg_flags & MSG_NOTIFICATION));
+
+            /*
+             * Everything seems ok till now, so it's most likely a message
+             * dropped by PR-SCTP.
+             */
+            memset(out, 0, outl);
+            BIO_set_retry_read(b);
+            return -1;
+        }
+
+        BIO_clear_retry_flags(b);
+        if (ret < 0) {
+            if (BIO_dgram_should_retry(ret)) {
+                BIO_set_retry_read(b);
+                data->_errno = get_last_socket_error();
+            }
+        }
+
+        /* Test if peer uses SCTP-AUTH before continuing */
+        if (!data->peer_auth_tested) {
+            int ii, auth_data = 0, auth_forward = 0;
+            unsigned char *p;
+            struct sctp_authchunks *authchunks;
+
+            optlen =
+                (socklen_t) (sizeof(sctp_assoc_t) + 256 * sizeof(uint8_t));
+            authchunks = OPENSSL_malloc(optlen);
+            if (authchunks == NULL) {
+                BIOerr(BIO_F_DGRAM_SCTP_READ, ERR_R_MALLOC_FAILURE);
+                return -1;
+            }
+            memset(authchunks, 0, optlen);
+            ii = getsockopt(b->num, IPPROTO_SCTP, SCTP_PEER_AUTH_CHUNKS,
+                            authchunks, &optlen);
+
+            if (ii >= 0)
+                for (p = (unsigned char *)authchunks->gauth_chunks;
+                     p < (unsigned char *)authchunks + optlen;
+                     p += sizeof(uint8_t)) {
+                    if (*p == OPENSSL_SCTP_DATA_CHUNK_TYPE)
+                        auth_data = 1;
+                    if (*p == OPENSSL_SCTP_FORWARD_CUM_TSN_CHUNK_TYPE)
+                        auth_forward = 1;
+                }
+
+            OPENSSL_free(authchunks);
+
+            if (!auth_data || !auth_forward) {
+                BIOerr(BIO_F_DGRAM_SCTP_READ, BIO_R_CONNECT_ERROR);
+                return -1;
+            }
+
+            data->peer_auth_tested = 1;
+        }
+    }
+    return (ret);
+}
+
+/*
+ * dgram_sctp_write - send message on SCTP socket
+ * @b: BIO to write to
+ * @in: data to send
+ * @inl: amount of bytes in @in to send
+ *
+ * Returns -1 on error or the sent amount of bytes on success
+ */
+static int dgram_sctp_write(BIO *b, const char *in, int inl)
+{
+    int ret;
+    bio_dgram_sctp_data *data = (bio_dgram_sctp_data *) b->ptr;
+    struct bio_dgram_sctp_sndinfo *sinfo = &(data->sndinfo);
+    struct bio_dgram_sctp_prinfo *pinfo = &(data->prinfo);
+    struct bio_dgram_sctp_sndinfo handshake_sinfo;
+    struct iovec iov[1];
+    struct msghdr msg;
+    struct cmsghdr *cmsg;
+#  if defined(SCTP_SNDINFO) && defined(SCTP_PRINFO)
+    char cmsgbuf[CMSG_SPACE(sizeof(struct sctp_sndinfo)) +
+                 CMSG_SPACE(sizeof(struct sctp_prinfo))];
+    struct sctp_sndinfo *sndinfo;
+    struct sctp_prinfo *prinfo;
+#  else
+    char cmsgbuf[CMSG_SPACE(sizeof(struct sctp_sndrcvinfo))];
+    struct sctp_sndrcvinfo *sndrcvinfo;
+#  endif
+
+    clear_socket_error();
+
+    /*
+     * If we're send anything else than application data, disable all user
+     * parameters and flags.
+     */
+    if (in[0] != 23) {
+        memset(&handshake_sinfo, 0, sizeof(handshake_sinfo));
+#  ifdef SCTP_SACK_IMMEDIATELY
+        handshake_sinfo.snd_flags = SCTP_SACK_IMMEDIATELY;
+#  endif
+        sinfo = &handshake_sinfo;
+    }
+
+    /*
+     * If we have to send a shutdown alert message and the socket is not dry
+     * yet, we have to save it and send it as soon as the socket gets dry.
+     */
+    if (data->save_shutdown) {
+        ret = BIO_dgram_sctp_wait_for_dry(b);
+        if (ret < 0) {
+            return -1;
+        }
+        if (ret == 0) {
+            char *tmp;
+            data->saved_message.bio = b;
+            if ((tmp = OPENSSL_malloc(inl)) == NULL) {
+                BIOerr(BIO_F_DGRAM_SCTP_WRITE, ERR_R_MALLOC_FAILURE);
+                return -1;
+            }
+            OPENSSL_free(data->saved_message.data);
+            data->saved_message.data = tmp;
+            memcpy(data->saved_message.data, in, inl);
+            data->saved_message.length = inl;
+            return inl;
+        }
+    }
+
+    iov[0].iov_base = (char *)in;
+    iov[0].iov_len = inl;
+    msg.msg_name = NULL;
+    msg.msg_namelen = 0;
+    msg.msg_iov = iov;
+    msg.msg_iovlen = 1;
+    msg.msg_control = (caddr_t) cmsgbuf;
+    msg.msg_controllen = 0;
+    msg.msg_flags = 0;
+#  if defined(SCTP_SNDINFO) && defined(SCTP_PRINFO)
+    cmsg = (struct cmsghdr *)cmsgbuf;
+    cmsg->cmsg_level = IPPROTO_SCTP;
+    cmsg->cmsg_type = SCTP_SNDINFO;
+    cmsg->cmsg_len = CMSG_LEN(sizeof(struct sctp_sndinfo));
+    sndinfo = (struct sctp_sndinfo *)CMSG_DATA(cmsg);
+    memset(sndinfo, 0, sizeof(*sndinfo));
+    sndinfo->snd_sid = sinfo->snd_sid;
+    sndinfo->snd_flags = sinfo->snd_flags;
+    sndinfo->snd_ppid = sinfo->snd_ppid;
+    sndinfo->snd_context = sinfo->snd_context;
+    msg.msg_controllen += CMSG_SPACE(sizeof(struct sctp_sndinfo));
+
+    cmsg =
+        (struct cmsghdr *)&cmsgbuf[CMSG_SPACE(sizeof(struct sctp_sndinfo))];
+    cmsg->cmsg_level = IPPROTO_SCTP;
+    cmsg->cmsg_type = SCTP_PRINFO;
+    cmsg->cmsg_len = CMSG_LEN(sizeof(struct sctp_prinfo));
+    prinfo = (struct sctp_prinfo *)CMSG_DATA(cmsg);
+    memset(prinfo, 0, sizeof(*prinfo));
+    prinfo->pr_policy = pinfo->pr_policy;
+    prinfo->pr_value = pinfo->pr_value;
+    msg.msg_controllen += CMSG_SPACE(sizeof(struct sctp_prinfo));
+#  else
+    cmsg = (struct cmsghdr *)cmsgbuf;
+    cmsg->cmsg_level = IPPROTO_SCTP;
+    cmsg->cmsg_type = SCTP_SNDRCV;
+    cmsg->cmsg_len = CMSG_LEN(sizeof(struct sctp_sndrcvinfo));
+    sndrcvinfo = (struct sctp_sndrcvinfo *)CMSG_DATA(cmsg);
+    memset(sndrcvinfo, 0, sizeof(*sndrcvinfo));
+    sndrcvinfo->sinfo_stream = sinfo->snd_sid;
+    sndrcvinfo->sinfo_flags = sinfo->snd_flags;
+#   ifdef __FreeBSD__
+    sndrcvinfo->sinfo_flags |= pinfo->pr_policy;
+#   endif
+    sndrcvinfo->sinfo_ppid = sinfo->snd_ppid;
+    sndrcvinfo->sinfo_context = sinfo->snd_context;
+    sndrcvinfo->sinfo_timetolive = pinfo->pr_value;
+    msg.msg_controllen += CMSG_SPACE(sizeof(struct sctp_sndrcvinfo));
+#  endif
+
+    ret = sendmsg(b->num, &msg, 0);
+
+    BIO_clear_retry_flags(b);
+    if (ret <= 0) {
+        if (BIO_dgram_should_retry(ret)) {
+            BIO_set_retry_write(b);
+            data->_errno = get_last_socket_error();
+        }
+    }
+    return (ret);
+}
+
+static long dgram_sctp_ctrl(BIO *b, int cmd, long num, void *ptr)
+{
+    long ret = 1;
+    bio_dgram_sctp_data *data = NULL;
+    socklen_t sockopt_len = 0;
+    struct sctp_authkeyid authkeyid;
+    struct sctp_authkey *authkey = NULL;
+
+    data = (bio_dgram_sctp_data *) b->ptr;
+
+    switch (cmd) {
+    case BIO_CTRL_DGRAM_QUERY_MTU:
+        /*
+         * Set to maximum (2^14) and ignore user input to enable transport
+         * protocol fragmentation. Returns always 2^14.
+         */
+        data->mtu = 16384;
+        ret = data->mtu;
+        break;
+    case BIO_CTRL_DGRAM_SET_MTU:
+        /*
+         * Set to maximum (2^14) and ignore input to enable transport
+         * protocol fragmentation. Returns always 2^14.
+         */
+        data->mtu = 16384;
+        ret = data->mtu;
+        break;
+    case BIO_CTRL_DGRAM_SET_CONNECTED:
+    case BIO_CTRL_DGRAM_CONNECT:
+        /* Returns always -1. */
+        ret = -1;
+        break;
+    case BIO_CTRL_DGRAM_SET_NEXT_TIMEOUT:
+        /*
+         * SCTP doesn't need the DTLS timer Returns always 1.
+         */
+        break;
+    case BIO_CTRL_DGRAM_GET_MTU_OVERHEAD:
+        /*
+         * We allow transport protocol fragmentation so this is irrelevant
+         */
+        ret = 0;
+        break;
+    case BIO_CTRL_DGRAM_SCTP_SET_IN_HANDSHAKE:
+        if (num > 0)
+            data->in_handshake = 1;
+        else
+            data->in_handshake = 0;
+
+        ret =
+            setsockopt(b->num, IPPROTO_SCTP, SCTP_NODELAY,
+                       &data->in_handshake, sizeof(int));
+        break;
+    case BIO_CTRL_DGRAM_SCTP_ADD_AUTH_KEY:
+        /*
+         * New shared key for SCTP AUTH. Returns 0 on success, -1 otherwise.
+         */
+
+        /* Get active key */
+        sockopt_len = sizeof(struct sctp_authkeyid);
+        ret =
+            getsockopt(b->num, IPPROTO_SCTP, SCTP_AUTH_ACTIVE_KEY, &authkeyid,
+                       &sockopt_len);
+        if (ret < 0)
+            break;
+
+        /* Add new key */
+        sockopt_len = sizeof(struct sctp_authkey) + 64 * sizeof(uint8_t);
+        authkey = OPENSSL_malloc(sockopt_len);
+        if (authkey == NULL) {
+            ret = -1;
+            break;
+        }
+        memset(authkey, 0, sockopt_len);
+        authkey->sca_keynumber = authkeyid.scact_keynumber + 1;
+#  ifndef __FreeBSD__
+        /*
+         * This field is missing in FreeBSD 8.2 and earlier, and FreeBSD 8.3
+         * and higher work without it.
+         */
+        authkey->sca_keylength = 64;
+#  endif
+        memcpy(&authkey->sca_key[0], ptr, 64 * sizeof(uint8_t));
+
+        ret =
+            setsockopt(b->num, IPPROTO_SCTP, SCTP_AUTH_KEY, authkey,
+                       sockopt_len);
+        OPENSSL_free(authkey);
+        authkey = NULL;
+        if (ret < 0)
+            break;
+
+        /* Reset active key */
+        ret = setsockopt(b->num, IPPROTO_SCTP, SCTP_AUTH_ACTIVE_KEY,
+                         &authkeyid, sizeof(struct sctp_authkeyid));
+        if (ret < 0)
+            break;
+
+        break;
+    case BIO_CTRL_DGRAM_SCTP_NEXT_AUTH_KEY:
+        /* Returns 0 on success, -1 otherwise. */
+
+        /* Get active key */
+        sockopt_len = sizeof(struct sctp_authkeyid);
+        ret =
+            getsockopt(b->num, IPPROTO_SCTP, SCTP_AUTH_ACTIVE_KEY, &authkeyid,
+                       &sockopt_len);
+        if (ret < 0)
+            break;
+
+        /* Set active key */
+        authkeyid.scact_keynumber = authkeyid.scact_keynumber + 1;
+        ret = setsockopt(b->num, IPPROTO_SCTP, SCTP_AUTH_ACTIVE_KEY,
+                         &authkeyid, sizeof(struct sctp_authkeyid));
+        if (ret < 0)
+            break;
+
+        /*
+         * CCS has been sent, so remember that and fall through to check if
+         * we need to deactivate an old key
+         */
+        data->ccs_sent = 1;
+
+    case BIO_CTRL_DGRAM_SCTP_AUTH_CCS_RCVD:
+        /* Returns 0 on success, -1 otherwise. */
+
+        /*
+         * Has this command really been called or is this just a
+         * fall-through?
+         */
+        if (cmd == BIO_CTRL_DGRAM_SCTP_AUTH_CCS_RCVD)
+            data->ccs_rcvd = 1;
+
+        /*
+         * CSS has been both, received and sent, so deactivate an old key
+         */
+        if (data->ccs_rcvd == 1 && data->ccs_sent == 1) {
+            /* Get active key */
+            sockopt_len = sizeof(struct sctp_authkeyid);
+            ret =
+                getsockopt(b->num, IPPROTO_SCTP, SCTP_AUTH_ACTIVE_KEY,
+                           &authkeyid, &sockopt_len);
+            if (ret < 0)
+                break;
+
+            /*
+             * Deactivate key or delete second last key if
+             * SCTP_AUTHENTICATION_EVENT is not available.
+             */
+            authkeyid.scact_keynumber = authkeyid.scact_keynumber - 1;
+#  ifdef SCTP_AUTH_DEACTIVATE_KEY
+            sockopt_len = sizeof(struct sctp_authkeyid);
+            ret = setsockopt(b->num, IPPROTO_SCTP, SCTP_AUTH_DEACTIVATE_KEY,
+                             &authkeyid, sockopt_len);
+            if (ret < 0)
+                break;
+#  endif
+#  ifndef SCTP_AUTHENTICATION_EVENT
+            if (authkeyid.scact_keynumber > 0) {
+                authkeyid.scact_keynumber = authkeyid.scact_keynumber - 1;
+                ret = setsockopt(b->num, IPPROTO_SCTP, SCTP_AUTH_DELETE_KEY,
+                                 &authkeyid, sizeof(struct sctp_authkeyid));
+                if (ret < 0)
+                    break;
+            }
+#  endif
+
+            data->ccs_rcvd = 0;
+            data->ccs_sent = 0;
+        }
+        break;
+    case BIO_CTRL_DGRAM_SCTP_GET_SNDINFO:
+        /* Returns the size of the copied struct. */
+        if (num > (long)sizeof(struct bio_dgram_sctp_sndinfo))
+            num = sizeof(struct bio_dgram_sctp_sndinfo);
+
+        memcpy(ptr, &(data->sndinfo), num);
+        ret = num;
+        break;
+    case BIO_CTRL_DGRAM_SCTP_SET_SNDINFO:
+        /* Returns the size of the copied struct. */
+        if (num > (long)sizeof(struct bio_dgram_sctp_sndinfo))
+            num = sizeof(struct bio_dgram_sctp_sndinfo);
+
+        memcpy(&(data->sndinfo), ptr, num);
+        break;
+    case BIO_CTRL_DGRAM_SCTP_GET_RCVINFO:
+        /* Returns the size of the copied struct. */
+        if (num > (long)sizeof(struct bio_dgram_sctp_rcvinfo))
+            num = sizeof(struct bio_dgram_sctp_rcvinfo);
+
+        memcpy(ptr, &data->rcvinfo, num);
+
+        ret = num;
+        break;
+    case BIO_CTRL_DGRAM_SCTP_SET_RCVINFO:
+        /* Returns the size of the copied struct. */
+        if (num > (long)sizeof(struct bio_dgram_sctp_rcvinfo))
+            num = sizeof(struct bio_dgram_sctp_rcvinfo);
+
+        memcpy(&(data->rcvinfo), ptr, num);
+        break;
+    case BIO_CTRL_DGRAM_SCTP_GET_PRINFO:
+        /* Returns the size of the copied struct. */
+        if (num > (long)sizeof(struct bio_dgram_sctp_prinfo))
+            num = sizeof(struct bio_dgram_sctp_prinfo);
+
+        memcpy(ptr, &(data->prinfo), num);
+        ret = num;
+        break;
+    case BIO_CTRL_DGRAM_SCTP_SET_PRINFO:
+        /* Returns the size of the copied struct. */
+        if (num > (long)sizeof(struct bio_dgram_sctp_prinfo))
+            num = sizeof(struct bio_dgram_sctp_prinfo);
+
+        memcpy(&(data->prinfo), ptr, num);
+        break;
+    case BIO_CTRL_DGRAM_SCTP_SAVE_SHUTDOWN:
+        /* Returns always 1. */
+        if (num > 0)
+            data->save_shutdown = 1;
+        else
+            data->save_shutdown = 0;
+        break;
+
+    default:
+        /*
+         * Pass to default ctrl function to process SCTP unspecific commands
+         */
+        ret = dgram_ctrl(b, cmd, num, ptr);
+        break;
+    }
+    return (ret);
+}
+
+int BIO_dgram_sctp_notification_cb(BIO *b,
+                                   void (*handle_notifications) (BIO *bio,
+                                                                 void
+                                                                 *context,
+                                                                 void *buf),
+                                   void *context)
+{
+    bio_dgram_sctp_data *data = (bio_dgram_sctp_data *) b->ptr;
+
+    if (handle_notifications != NULL) {
+        data->handle_notifications = handle_notifications;
+        data->notification_context = context;
+    } else
+        return -1;
+
+    return 0;
+}
+
+/*
+ * BIO_dgram_sctp_wait_for_dry - Wait for SCTP SENDER_DRY event
+ * @b: The BIO to check for the dry event
+ *
+ * Wait until the peer confirms all packets have been received, and so that
+ * our kernel doesn't have anything to send anymore.  This is only received by
+ * the peer's kernel, not the application.
+ *
+ * Returns:
+ * -1 on error
+ *  0 when not dry yet
+ *  1 when dry
+ */
+int BIO_dgram_sctp_wait_for_dry(BIO *b)
+{
+    int is_dry = 0;
+    int sockflags = 0;
+    int n, ret;
+    union sctp_notification snp;
+    struct msghdr msg;
+    struct iovec iov;
+#  ifdef SCTP_EVENT
+    struct sctp_event event;
+#  else
+    struct sctp_event_subscribe event;
+    socklen_t eventsize;
+#  endif
+    bio_dgram_sctp_data *data = (bio_dgram_sctp_data *) b->ptr;
+
+    /* set sender dry event */
+#  ifdef SCTP_EVENT
+    memset(&event, 0, sizeof(event));
+    event.se_assoc_id = 0;
+    event.se_type = SCTP_SENDER_DRY_EVENT;
+    event.se_on = 1;
+    ret =
+        setsockopt(b->num, IPPROTO_SCTP, SCTP_EVENT, &event,
+                   sizeof(struct sctp_event));
+#  else
+    eventsize = sizeof(struct sctp_event_subscribe);
+    ret = getsockopt(b->num, IPPROTO_SCTP, SCTP_EVENTS, &event, &eventsize);
+    if (ret < 0)
+        return -1;
+
+    event.sctp_sender_dry_event = 1;
+
+    ret =
+        setsockopt(b->num, IPPROTO_SCTP, SCTP_EVENTS, &event,
+                   sizeof(struct sctp_event_subscribe));
+#  endif
+    if (ret < 0)
+        return -1;
+
+    /* peek for notification */
+    memset(&snp, 0, sizeof(snp));
+    iov.iov_base = (char *)&snp;
+    iov.iov_len = sizeof(union sctp_notification);
+    msg.msg_name = NULL;
+    msg.msg_namelen = 0;
+    msg.msg_iov = &iov;
+    msg.msg_iovlen = 1;
+    msg.msg_control = NULL;
+    msg.msg_controllen = 0;
+    msg.msg_flags = 0;
+
+    n = recvmsg(b->num, &msg, MSG_PEEK);
+    if (n <= 0) {
+        if ((n < 0) && (get_last_socket_error() != EAGAIN)
+            && (get_last_socket_error() != EWOULDBLOCK))
+            return -1;
+        else
+            return 0;
+    }
+
+    /* if we find a notification, process it and try again if necessary */
+    while (msg.msg_flags & MSG_NOTIFICATION) {
+        memset(&snp, 0, sizeof(snp));
+        iov.iov_base = (char *)&snp;
+        iov.iov_len = sizeof(union sctp_notification);
+        msg.msg_name = NULL;
+        msg.msg_namelen = 0;
+        msg.msg_iov = &iov;
+        msg.msg_iovlen = 1;
+        msg.msg_control = NULL;
+        msg.msg_controllen = 0;
+        msg.msg_flags = 0;
+
+        n = recvmsg(b->num, &msg, 0);
+        if (n <= 0) {
+            if ((n < 0) && (get_last_socket_error() != EAGAIN)
+                && (get_last_socket_error() != EWOULDBLOCK))
+                return -1;
+            else
+                return is_dry;
+        }
+
+        if (snp.sn_header.sn_type == SCTP_SENDER_DRY_EVENT) {
+            is_dry = 1;
+
+            /* disable sender dry event */
+#  ifdef SCTP_EVENT
+            memset(&event, 0, sizeof(event));
+            event.se_assoc_id = 0;
+            event.se_type = SCTP_SENDER_DRY_EVENT;
+            event.se_on = 0;
+            ret =
+                setsockopt(b->num, IPPROTO_SCTP, SCTP_EVENT, &event,
+                           sizeof(struct sctp_event));
+#  else
+            eventsize = (socklen_t) sizeof(struct sctp_event_subscribe);
+            ret =
+                getsockopt(b->num, IPPROTO_SCTP, SCTP_EVENTS, &event,
+                           &eventsize);
+            if (ret < 0)
+                return -1;
+
+            event.sctp_sender_dry_event = 0;
+
+            ret =
+                setsockopt(b->num, IPPROTO_SCTP, SCTP_EVENTS, &event,
+                           sizeof(struct sctp_event_subscribe));
+#  endif
+            if (ret < 0)
+                return -1;
+        }
+#  ifdef SCTP_AUTHENTICATION_EVENT
+        if (snp.sn_header.sn_type == SCTP_AUTHENTICATION_EVENT)
+            dgram_sctp_handle_auth_free_key_event(b, &snp);
+#  endif
+
+        if (data->handle_notifications != NULL)
+            data->handle_notifications(b, data->notification_context,
+                                       (void *)&snp);
+
+        /* found notification, peek again */
+        memset(&snp, 0, sizeof(snp));
+        iov.iov_base = (char *)&snp;
+        iov.iov_len = sizeof(union sctp_notification);
+        msg.msg_name = NULL;
+        msg.msg_namelen = 0;
+        msg.msg_iov = &iov;
+        msg.msg_iovlen = 1;
+        msg.msg_control = NULL;
+        msg.msg_controllen = 0;
+        msg.msg_flags = 0;
+
+        /* if we have seen the dry already, don't wait */
+        if (is_dry) {
+            sockflags = fcntl(b->num, F_GETFL, 0);
+            fcntl(b->num, F_SETFL, O_NONBLOCK);
+        }
+
+        n = recvmsg(b->num, &msg, MSG_PEEK);
+
+        if (is_dry) {
+            fcntl(b->num, F_SETFL, sockflags);
+        }
+
+        if (n <= 0) {
+            if ((n < 0) && (get_last_socket_error() != EAGAIN)
+                && (get_last_socket_error() != EWOULDBLOCK))
+                return -1;
+            else
+                return is_dry;
+        }
+    }
+
+    /* read anything else */
+    return is_dry;
+}
+
+int BIO_dgram_sctp_msg_waiting(BIO *b)
+{
+    int n, sockflags;
+    union sctp_notification snp;
+    struct msghdr msg;
+    struct iovec iov;
+    bio_dgram_sctp_data *data = (bio_dgram_sctp_data *) b->ptr;
+
+    /* Check if there are any messages waiting to be read */
+    do {
+        memset(&snp, 0, sizeof(snp));
+        iov.iov_base = (char *)&snp;
+        iov.iov_len = sizeof(union sctp_notification);
+        msg.msg_name = NULL;
+        msg.msg_namelen = 0;
+        msg.msg_iov = &iov;
+        msg.msg_iovlen = 1;
+        msg.msg_control = NULL;
+        msg.msg_controllen = 0;
+        msg.msg_flags = 0;
+
+        sockflags = fcntl(b->num, F_GETFL, 0);
+        fcntl(b->num, F_SETFL, O_NONBLOCK);
+        n = recvmsg(b->num, &msg, MSG_PEEK);
+        fcntl(b->num, F_SETFL, sockflags);
+
+        /* if notification, process and try again */
+        if (n > 0 && (msg.msg_flags & MSG_NOTIFICATION)) {
+#  ifdef SCTP_AUTHENTICATION_EVENT
+            if (snp.sn_header.sn_type == SCTP_AUTHENTICATION_EVENT)
+                dgram_sctp_handle_auth_free_key_event(b, &snp);
+#  endif
+
+            memset(&snp, 0, sizeof(snp));
+            iov.iov_base = (char *)&snp;
+            iov.iov_len = sizeof(union sctp_notification);
+            msg.msg_name = NULL;
+            msg.msg_namelen = 0;
+            msg.msg_iov = &iov;
+            msg.msg_iovlen = 1;
+            msg.msg_control = NULL;
+            msg.msg_controllen = 0;
+            msg.msg_flags = 0;
+            n = recvmsg(b->num, &msg, 0);
+
+            if (data->handle_notifications != NULL)
+                data->handle_notifications(b, data->notification_context,
+                                           (void *)&snp);
+        }
+
+    } while (n > 0 && (msg.msg_flags & MSG_NOTIFICATION));
+
+    /* Return 1 if there is a message to be read, return 0 otherwise. */
+    if (n > 0)
+        return 1;
+    else
+        return 0;
+}
+
+static int dgram_sctp_puts(BIO *bp, const char *str)
+{
+    int n, ret;
+
+    n = strlen(str);
+    ret = dgram_sctp_write(bp, str, n);
+    return (ret);
+}
+# endif
+
+static int BIO_dgram_should_retry(int i)
+{
+    int err;
+
+    if ((i == 0) || (i == -1)) {
+        err = get_last_socket_error();
+
+# if defined(OPENSSL_SYS_WINDOWS)
+        /*
+         * If the socket return value (i) is -1 and err is unexpectedly 0 at
+         * this point, the error code was overwritten by another system call
+         * before this error handling is called.
+         */
+# endif
+
+        return (BIO_dgram_non_fatal_error(err));
+    }
+    return (0);
+}
+
+int BIO_dgram_non_fatal_error(int err)
+{
+    switch (err) {
+# if defined(OPENSSL_SYS_WINDOWS)
+#  if defined(WSAEWOULDBLOCK)
+    case WSAEWOULDBLOCK:
+#  endif
+# endif
+
+# ifdef EWOULDBLOCK
+#  ifdef WSAEWOULDBLOCK
+#   if WSAEWOULDBLOCK != EWOULDBLOCK
+    case EWOULDBLOCK:
+#   endif
+#  else
+    case EWOULDBLOCK:
+#  endif
+# endif
+
+# ifdef EINTR
+    case EINTR:
+# endif
+
+# ifdef EAGAIN
+#  if EWOULDBLOCK != EAGAIN
+    case EAGAIN:
+#  endif
+# endif
+
+# ifdef EPROTO
+    case EPROTO:
+# endif
+
+# ifdef EINPROGRESS
+    case EINPROGRESS:
+# endif
+
+# ifdef EALREADY
+    case EALREADY:
+# endif
+
+        return (1);
+        /* break; */
+    default:
+        break;
+    }
+    return (0);
+}
+
+static void get_current_time(struct timeval *t)
+{
+# if defined(_WIN32)
+    SYSTEMTIME st;
+    union {
+        unsigned __int64 ul;
+        FILETIME ft;
+    } now;
+
+    GetSystemTime(&st);
+    SystemTimeToFileTime(&st, &now.ft);
+#  ifdef  __MINGW32__
+    now.ul -= 116444736000000000ULL;
+#  else
+    now.ul -= 116444736000000000UI64; /* re-bias to 1/1/1970 */
+#  endif
+    t->tv_sec = (long)(now.ul / 10000000);
+    t->tv_usec = ((int)(now.ul % 10000000)) / 10;
+# elif defined(OPENSSL_SYS_VMS)
+    struct timeb tb;
+    ftime(&tb);
+    t->tv_sec = (long)tb.time;
+    t->tv_usec = (long)tb.millitm * 1000;
+# else
+    gettimeofday(t, NULL);
+# endif
+}
+
+#endif
diff --git a/crypto/bio/bss_fd.c b/crypto/bio/bss_fd.c
new file mode 100644
index 000000000..1e56cb62d
--- /dev/null
+++ b/crypto/bio/bss_fd.c
@@ -0,0 +1,273 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+
+#include "bio_lcl.h"
+
+#if defined(OPENSSL_NO_POSIX_IO)
+/*
+ * Dummy placeholder for BIO_s_fd...
+ */
+BIO *BIO_new_fd(int fd, int close_flag)
+{
+    return NULL;
+}
+
+int BIO_fd_non_fatal_error(int err)
+{
+    return 0;
+}
+
+int BIO_fd_should_retry(int i)
+{
+    return 0;
+}
+
+const BIO_METHOD *BIO_s_fd(void)
+{
+    return NULL;
+}
+#else
+/*
+ * As for unconditional usage of "UPLINK" interface in this module.
+ * Trouble is that unlike Unix file descriptors [which are indexes
+ * in kernel-side per-process table], corresponding descriptors on
+ * platforms which require "UPLINK" interface seem to be indexes
+ * in a user-land, non-global table. Well, in fact they are indexes
+ * in stdio _iob[], and recall that _iob[] was the very reason why
+ * "UPLINK" interface was introduced in first place. But one way on
+ * another. Neither libcrypto or libssl use this BIO meaning that
+ * file descriptors can only be provided by application. Therefore
+ * "UPLINK" calls are due...
+ */
+static int fd_write(BIO *h, const char *buf, int num);
+static int fd_read(BIO *h, char *buf, int size);
+static int fd_puts(BIO *h, const char *str);
+static int fd_gets(BIO *h, char *buf, int size);
+static long fd_ctrl(BIO *h, int cmd, long arg1, void *arg2);
+static int fd_new(BIO *h);
+static int fd_free(BIO *data);
+int BIO_fd_should_retry(int s);
+
+static const BIO_METHOD methods_fdp = {
+    BIO_TYPE_FD, "file descriptor",
+    fd_write,
+    fd_read,
+    fd_puts,
+    fd_gets,
+    fd_ctrl,
+    fd_new,
+    fd_free,
+    NULL,
+};
+
+const BIO_METHOD *BIO_s_fd(void)
+{
+    return (&methods_fdp);
+}
+
+BIO *BIO_new_fd(int fd, int close_flag)
+{
+    BIO *ret;
+    ret = BIO_new(BIO_s_fd());
+    if (ret == NULL)
+        return (NULL);
+    BIO_set_fd(ret, fd, close_flag);
+    return (ret);
+}
+
+static int fd_new(BIO *bi)
+{
+    bi->init = 0;
+    bi->num = -1;
+    bi->ptr = NULL;
+    bi->flags = BIO_FLAGS_UPLINK; /* essentially redundant */
+    return (1);
+}
+
+static int fd_free(BIO *a)
+{
+    if (a == NULL)
+        return (0);
+    if (a->shutdown) {
+        if (a->init) {
+            UP_close(a->num);
+        }
+        a->init = 0;
+        a->flags = BIO_FLAGS_UPLINK;
+    }
+    return (1);
+}
+
+static int fd_read(BIO *b, char *out, int outl)
+{
+    int ret = 0;
+
+    if (out != NULL) {
+        clear_sys_error();
+        ret = UP_read(b->num, out, outl);
+        BIO_clear_retry_flags(b);
+        if (ret <= 0) {
+            if (BIO_fd_should_retry(ret))
+                BIO_set_retry_read(b);
+        }
+    }
+    return (ret);
+}
+
+static int fd_write(BIO *b, const char *in, int inl)
+{
+    int ret;
+    clear_sys_error();
+    ret = UP_write(b->num, in, inl);
+    BIO_clear_retry_flags(b);
+    if (ret <= 0) {
+        if (BIO_fd_should_retry(ret))
+            BIO_set_retry_write(b);
+    }
+    return (ret);
+}
+
+static long fd_ctrl(BIO *b, int cmd, long num, void *ptr)
+{
+    long ret = 1;
+    int *ip;
+
+    switch (cmd) {
+    case BIO_CTRL_RESET:
+        num = 0;
+    case BIO_C_FILE_SEEK:
+        ret = (long)UP_lseek(b->num, num, 0);
+        break;
+    case BIO_C_FILE_TELL:
+    case BIO_CTRL_INFO:
+        ret = (long)UP_lseek(b->num, 0, 1);
+        break;
+    case BIO_C_SET_FD:
+        fd_free(b);
+        b->num = *((int *)ptr);
+        b->shutdown = (int)num;
+        b->init = 1;
+        break;
+    case BIO_C_GET_FD:
+        if (b->init) {
+            ip = (int *)ptr;
+            if (ip != NULL)
+                *ip = b->num;
+            ret = b->num;
+        } else
+            ret = -1;
+        break;
+    case BIO_CTRL_GET_CLOSE:
+        ret = b->shutdown;
+        break;
+    case BIO_CTRL_SET_CLOSE:
+        b->shutdown = (int)num;
+        break;
+    case BIO_CTRL_PENDING:
+    case BIO_CTRL_WPENDING:
+        ret = 0;
+        break;
+    case BIO_CTRL_DUP:
+    case BIO_CTRL_FLUSH:
+        ret = 1;
+        break;
+    default:
+        ret = 0;
+        break;
+    }
+    return (ret);
+}
+
+static int fd_puts(BIO *bp, const char *str)
+{
+    int n, ret;
+
+    n = strlen(str);
+    ret = fd_write(bp, str, n);
+    return (ret);
+}
+
+static int fd_gets(BIO *bp, char *buf, int size)
+{
+    int ret = 0;
+    char *ptr = buf;
+    char *end = buf + size - 1;
+
+    while ((ptr < end) && (fd_read(bp, ptr, 1) > 0) && (ptr[0] != '\n'))
+        ptr++;
+
+    ptr[0] = '\0';
+
+    if (buf[0] != '\0')
+        ret = strlen(buf);
+    return (ret);
+}
+
+int BIO_fd_should_retry(int i)
+{
+    int err;
+
+    if ((i == 0) || (i == -1)) {
+        err = get_last_sys_error();
+
+        return (BIO_fd_non_fatal_error(err));
+    }
+    return (0);
+}
+
+int BIO_fd_non_fatal_error(int err)
+{
+    switch (err) {
+
+# ifdef EWOULDBLOCK
+#  ifdef WSAEWOULDBLOCK
+#   if WSAEWOULDBLOCK != EWOULDBLOCK
+    case EWOULDBLOCK:
+#   endif
+#  else
+    case EWOULDBLOCK:
+#  endif
+# endif
+
+# if defined(ENOTCONN)
+    case ENOTCONN:
+# endif
+
+# ifdef EINTR
+    case EINTR:
+# endif
+
+# ifdef EAGAIN
+#  if EWOULDBLOCK != EAGAIN
+    case EAGAIN:
+#  endif
+# endif
+
+# ifdef EPROTO
+    case EPROTO:
+# endif
+
+# ifdef EINPROGRESS
+    case EINPROGRESS:
+# endif
+
+# ifdef EALREADY
+    case EALREADY:
+# endif
+        return (1);
+        /* break; */
+    default:
+        break;
+    }
+    return (0);
+}
+#endif
diff --git a/crypto/bio/bss_file.c b/crypto/bio/bss_file.c
new file mode 100644
index 000000000..6af2d9cb8
--- /dev/null
+++ b/crypto/bio/bss_file.c
@@ -0,0 +1,419 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*-
+ * 03-Dec-1997  rdenny@dc3.com  Fix bug preventing use of stdin/stdout
+ *              with binary data (e.g. asn1parse -inform DER < xxx) under
+ *              Windows
+ */
+
+#ifndef HEADER_BSS_FILE_C
+# define HEADER_BSS_FILE_C
+
+# if defined(__linux) || defined(__sun) || defined(__hpux)
+/*
+ * Following definition aliases fopen to fopen64 on above mentioned
+ * platforms. This makes it possible to open and sequentially access files
+ * larger than 2GB from 32-bit application. It does not allow to traverse
+ * them beyond 2GB with fseek/ftell, but on the other hand *no* 32-bit
+ * platform permits that, not with fseek/ftell. Not to mention that breaking
+ * 2GB limit for seeking would require surgery to *our* API. But sequential
+ * access suffices for practical cases when you can run into large files,
+ * such as fingerprinting, so we can let API alone. For reference, the list
+ * of 32-bit platforms which allow for sequential access of large files
+ * without extra "magic" comprise *BSD, Darwin, IRIX...
+ */
+#  ifndef _FILE_OFFSET_BITS
+#   define _FILE_OFFSET_BITS 64
+#  endif
+# endif
+
+# include 
+# include 
+# include "bio_lcl.h"
+# include 
+
+# if !defined(OPENSSL_NO_STDIO)
+
+static int file_write(BIO *h, const char *buf, int num);
+static int file_read(BIO *h, char *buf, int size);
+static int file_puts(BIO *h, const char *str);
+static int file_gets(BIO *h, char *str, int size);
+static long file_ctrl(BIO *h, int cmd, long arg1, void *arg2);
+static int file_new(BIO *h);
+static int file_free(BIO *data);
+static const BIO_METHOD methods_filep = {
+    BIO_TYPE_FILE,
+    "FILE pointer",
+    file_write,
+    file_read,
+    file_puts,
+    file_gets,
+    file_ctrl,
+    file_new,
+    file_free,
+    NULL,
+};
+
+BIO *BIO_new_file(const char *filename, const char *mode)
+{
+    BIO  *ret;
+    FILE *file = openssl_fopen(filename, mode);
+    int fp_flags = BIO_CLOSE;
+
+    if (strchr(mode, 'b') == NULL)
+        fp_flags |= BIO_FP_TEXT;
+
+    if (file == NULL) {
+        SYSerr(SYS_F_FOPEN, get_last_sys_error());
+        ERR_add_error_data(5, "fopen('", filename, "','", mode, "')");
+        if (errno == ENOENT
+# ifdef ENXIO
+            || errno == ENXIO
+# endif
+            )
+            BIOerr(BIO_F_BIO_NEW_FILE, BIO_R_NO_SUCH_FILE);
+        else
+            BIOerr(BIO_F_BIO_NEW_FILE, ERR_R_SYS_LIB);
+        return (NULL);
+    }
+    if ((ret = BIO_new(BIO_s_file())) == NULL) {
+        fclose(file);
+        return (NULL);
+    }
+
+    BIO_clear_flags(ret, BIO_FLAGS_UPLINK); /* we did fopen -> we disengage
+                                             * UPLINK */
+    BIO_set_fp(ret, file, fp_flags);
+    return (ret);
+}
+
+BIO *BIO_new_fp(FILE *stream, int close_flag)
+{
+    BIO *ret;
+
+    if ((ret = BIO_new(BIO_s_file())) == NULL)
+        return (NULL);
+
+    /* redundant flag, left for documentation purposes */
+    BIO_set_flags(ret, BIO_FLAGS_UPLINK);
+    BIO_set_fp(ret, stream, close_flag);
+    return (ret);
+}
+
+const BIO_METHOD *BIO_s_file(void)
+{
+    return (&methods_filep);
+}
+
+static int file_new(BIO *bi)
+{
+    bi->init = 0;
+    bi->num = 0;
+    bi->ptr = NULL;
+    bi->flags = BIO_FLAGS_UPLINK; /* default to UPLINK */
+    return (1);
+}
+
+static int file_free(BIO *a)
+{
+    if (a == NULL)
+        return (0);
+    if (a->shutdown) {
+        if ((a->init) && (a->ptr != NULL)) {
+            if (a->flags & BIO_FLAGS_UPLINK)
+                UP_fclose(a->ptr);
+            else
+                fclose(a->ptr);
+            a->ptr = NULL;
+            a->flags = BIO_FLAGS_UPLINK;
+        }
+        a->init = 0;
+    }
+    return (1);
+}
+
+static int file_read(BIO *b, char *out, int outl)
+{
+    int ret = 0;
+
+    if (b->init && (out != NULL)) {
+        if (b->flags & BIO_FLAGS_UPLINK)
+            ret = UP_fread(out, 1, (int)outl, b->ptr);
+        else
+            ret = fread(out, 1, (int)outl, (FILE *)b->ptr);
+        if (ret == 0
+            && (b->flags & BIO_FLAGS_UPLINK) ? UP_ferror((FILE *)b->ptr) :
+                                               ferror((FILE *)b->ptr)) {
+            SYSerr(SYS_F_FREAD, get_last_sys_error());
+            BIOerr(BIO_F_FILE_READ, ERR_R_SYS_LIB);
+            ret = -1;
+        }
+    }
+    return (ret);
+}
+
+static int file_write(BIO *b, const char *in, int inl)
+{
+    int ret = 0;
+
+    if (b->init && (in != NULL)) {
+        if (b->flags & BIO_FLAGS_UPLINK)
+            ret = UP_fwrite(in, (int)inl, 1, b->ptr);
+        else
+            ret = fwrite(in, (int)inl, 1, (FILE *)b->ptr);
+        if (ret)
+            ret = inl;
+        /* ret=fwrite(in,1,(int)inl,(FILE *)b->ptr); */
+        /*
+         * according to Tim Hudson , the commented out
+         * version above can cause 'inl' write calls under some stupid stdio
+         * implementations (VMS)
+         */
+    }
+    return (ret);
+}
+
+static long file_ctrl(BIO *b, int cmd, long num, void *ptr)
+{
+    long ret = 1;
+    FILE *fp = (FILE *)b->ptr;
+    FILE **fpp;
+    char p[4];
+
+    switch (cmd) {
+    case BIO_C_FILE_SEEK:
+    case BIO_CTRL_RESET:
+        if (b->flags & BIO_FLAGS_UPLINK)
+            ret = (long)UP_fseek(b->ptr, num, 0);
+        else
+            ret = (long)fseek(fp, num, 0);
+        break;
+    case BIO_CTRL_EOF:
+        if (b->flags & BIO_FLAGS_UPLINK)
+            ret = (long)UP_feof(fp);
+        else
+            ret = (long)feof(fp);
+        break;
+    case BIO_C_FILE_TELL:
+    case BIO_CTRL_INFO:
+        if (b->flags & BIO_FLAGS_UPLINK)
+            ret = UP_ftell(b->ptr);
+        else
+            ret = ftell(fp);
+        break;
+    case BIO_C_SET_FILE_PTR:
+        file_free(b);
+        b->shutdown = (int)num & BIO_CLOSE;
+        b->ptr = ptr;
+        b->init = 1;
+#  if BIO_FLAGS_UPLINK!=0
+#   if defined(__MINGW32__) && defined(__MSVCRT__) && !defined(_IOB_ENTRIES)
+#    define _IOB_ENTRIES 20
+#   endif
+        /* Safety net to catch purely internal BIO_set_fp calls */
+#   if defined(_MSC_VER) && _MSC_VER>=1900
+        if (ptr == stdin || ptr == stdout || ptr == stderr)
+            BIO_clear_flags(b, BIO_FLAGS_UPLINK);
+#   elif defined(_IOB_ENTRIES)
+        if ((size_t)ptr >= (size_t)stdin &&
+            (size_t)ptr < (size_t)(stdin + _IOB_ENTRIES))
+            BIO_clear_flags(b, BIO_FLAGS_UPLINK);
+#   endif
+#  endif
+#  ifdef UP_fsetmod
+        if (b->flags & BIO_FLAGS_UPLINK)
+            UP_fsetmod(b->ptr, (char)((num & BIO_FP_TEXT) ? 't' : 'b'));
+        else
+#  endif
+        {
+#  if defined(OPENSSL_SYS_WINDOWS)
+            int fd = _fileno((FILE *)ptr);
+            if (num & BIO_FP_TEXT)
+                _setmode(fd, _O_TEXT);
+            else
+                _setmode(fd, _O_BINARY);
+#  elif defined(OPENSSL_SYS_MSDOS)
+            int fd = fileno((FILE *)ptr);
+            /* Set correct text/binary mode */
+            if (num & BIO_FP_TEXT)
+                _setmode(fd, _O_TEXT);
+            /* Dangerous to set stdin/stdout to raw (unless redirected) */
+            else {
+                if (fd == STDIN_FILENO || fd == STDOUT_FILENO) {
+                    if (isatty(fd) <= 0)
+                        _setmode(fd, _O_BINARY);
+                } else
+                    _setmode(fd, _O_BINARY);
+            }
+#  elif defined(OPENSSL_SYS_WIN32_CYGWIN)
+            int fd = fileno((FILE *)ptr);
+            if (num & BIO_FP_TEXT)
+                setmode(fd, O_TEXT);
+            else
+                setmode(fd, O_BINARY);
+#  endif
+        }
+        break;
+    case BIO_C_SET_FILENAME:
+        file_free(b);
+        b->shutdown = (int)num & BIO_CLOSE;
+        if (num & BIO_FP_APPEND) {
+            if (num & BIO_FP_READ)
+                OPENSSL_strlcpy(p, "a+", sizeof p);
+            else
+                OPENSSL_strlcpy(p, "a", sizeof p);
+        } else if ((num & BIO_FP_READ) && (num & BIO_FP_WRITE))
+            OPENSSL_strlcpy(p, "r+", sizeof p);
+        else if (num & BIO_FP_WRITE)
+            OPENSSL_strlcpy(p, "w", sizeof p);
+        else if (num & BIO_FP_READ)
+            OPENSSL_strlcpy(p, "r", sizeof p);
+        else {
+            BIOerr(BIO_F_FILE_CTRL, BIO_R_BAD_FOPEN_MODE);
+            ret = 0;
+            break;
+        }
+#  if defined(OPENSSL_SYS_MSDOS) || defined(OPENSSL_SYS_WINDOWS) || defined(OPENSSL_SYS_WIN32_CYGWIN)
+        if (!(num & BIO_FP_TEXT))
+            strcat(p, "b");
+        else
+            strcat(p, "t");
+#  endif
+        fp = openssl_fopen(ptr, p);
+        if (fp == NULL) {
+            SYSerr(SYS_F_FOPEN, get_last_sys_error());
+            ERR_add_error_data(5, "fopen('", ptr, "','", p, "')");
+            BIOerr(BIO_F_FILE_CTRL, ERR_R_SYS_LIB);
+            ret = 0;
+            break;
+        }
+        b->ptr = fp;
+        b->init = 1;
+        BIO_clear_flags(b, BIO_FLAGS_UPLINK); /* we did fopen -> we disengage
+                                               * UPLINK */
+        break;
+    case BIO_C_GET_FILE_PTR:
+        /* the ptr parameter is actually a FILE ** in this case. */
+        if (ptr != NULL) {
+            fpp = (FILE **)ptr;
+            *fpp = (FILE *)b->ptr;
+        }
+        break;
+    case BIO_CTRL_GET_CLOSE:
+        ret = (long)b->shutdown;
+        break;
+    case BIO_CTRL_SET_CLOSE:
+        b->shutdown = (int)num;
+        break;
+    case BIO_CTRL_FLUSH:
+        if (b->flags & BIO_FLAGS_UPLINK)
+            UP_fflush(b->ptr);
+        else
+            fflush((FILE *)b->ptr);
+        break;
+    case BIO_CTRL_DUP:
+        ret = 1;
+        break;
+
+    case BIO_CTRL_WPENDING:
+    case BIO_CTRL_PENDING:
+    case BIO_CTRL_PUSH:
+    case BIO_CTRL_POP:
+    default:
+        ret = 0;
+        break;
+    }
+    return (ret);
+}
+
+static int file_gets(BIO *bp, char *buf, int size)
+{
+    int ret = 0;
+
+    buf[0] = '\0';
+    if (bp->flags & BIO_FLAGS_UPLINK) {
+        if (!UP_fgets(buf, size, bp->ptr))
+            goto err;
+    } else {
+        if (!fgets(buf, size, (FILE *)bp->ptr))
+            goto err;
+    }
+    if (buf[0] != '\0')
+        ret = strlen(buf);
+ err:
+    return (ret);
+}
+
+static int file_puts(BIO *bp, const char *str)
+{
+    int n, ret;
+
+    n = strlen(str);
+    ret = file_write(bp, str, n);
+    return (ret);
+}
+
+#else
+
+static int file_write(BIO *b, const char *in, int inl)
+{
+    return -1;
+}
+static int file_read(BIO *b, char *out, int outl)
+{
+    return -1;
+}
+static int file_puts(BIO *bp, const char *str)
+{
+    return -1;
+}
+static int file_gets(BIO *bp, char *buf, int size)
+{
+    return 0;
+}
+static long file_ctrl(BIO *b, int cmd, long num, void *ptr)
+{
+    return 0;
+}
+static int file_new(BIO *bi)
+{
+    return 0;
+}
+static int file_free(BIO *a)
+{
+    return 0;
+}
+
+static const BIO_METHOD methods_filep = {
+    BIO_TYPE_FILE,
+    "FILE pointer",
+    file_write,
+    file_read,
+    file_puts,
+    file_gets,
+    file_ctrl,
+    file_new,
+    file_free,
+    NULL,
+};
+
+const BIO_METHOD *BIO_s_file(void)
+{
+    return (&methods_filep);
+}
+
+BIO *BIO_new_file(const char *filename, const char *mode)
+{
+    return NULL;
+}
+
+# endif                         /* OPENSSL_NO_STDIO */
+
+#endif                          /* HEADER_BSS_FILE_C */
diff --git a/crypto/bio/bss_log.c b/crypto/bio/bss_log.c
new file mode 100644
index 000000000..6cbde4d2f
--- /dev/null
+++ b/crypto/bio/bss_log.c
@@ -0,0 +1,406 @@
+/*
+ * Copyright 1999-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * Why BIO_s_log?
+ *
+ * BIO_s_log is useful for system daemons (or services under NT). It is
+ * one-way BIO, it sends all stuff to syslogd (on system that commonly use
+ * that), or event log (on NT), or OPCOM (on OpenVMS).
+ *
+ */
+
+#include 
+#include 
+
+#include "bio_lcl.h"
+#include "internal/cryptlib.h"
+
+#if defined(OPENSSL_SYS_WINCE)
+#elif defined(OPENSSL_SYS_WIN32)
+#elif defined(OPENSSL_SYS_VMS)
+# include 
+# include 
+# include 
+# include 
+/* Some compiler options may mask the declaration of "_malloc32". */
+# if __INITIAL_POINTER_SIZE && defined _ANSI_C_SOURCE
+#  if __INITIAL_POINTER_SIZE == 64
+#   pragma pointer_size save
+#   pragma pointer_size 32
+void *_malloc32(__size_t);
+#   pragma pointer_size restore
+#  endif                        /* __INITIAL_POINTER_SIZE == 64 */
+# endif                         /* __INITIAL_POINTER_SIZE && defined
+                                 * _ANSI_C_SOURCE */
+#elif defined(OPENSSL_SYS_NETWARE)
+# define NO_SYSLOG
+#elif (!defined(MSDOS) || defined(WATT32)) && !defined(OPENSSL_SYS_VXWORKS) && !defined(NO_SYSLOG)
+# include 
+#endif
+
+#include 
+#include 
+
+#ifndef NO_SYSLOG
+
+# if defined(OPENSSL_SYS_WIN32)
+#  define LOG_EMERG       0
+#  define LOG_ALERT       1
+#  define LOG_CRIT        2
+#  define LOG_ERR         3
+#  define LOG_WARNING     4
+#  define LOG_NOTICE      5
+#  define LOG_INFO        6
+#  define LOG_DEBUG       7
+
+#  define LOG_DAEMON      (3<<3)
+# elif defined(OPENSSL_SYS_VMS)
+/* On VMS, we don't really care about these, but we need them to compile */
+#  define LOG_EMERG       0
+#  define LOG_ALERT       1
+#  define LOG_CRIT        2
+#  define LOG_ERR         3
+#  define LOG_WARNING     4
+#  define LOG_NOTICE      5
+#  define LOG_INFO        6
+#  define LOG_DEBUG       7
+
+#  define LOG_DAEMON      OPC$M_NM_NTWORK
+# endif
+
+static int slg_write(BIO *h, const char *buf, int num);
+static int slg_puts(BIO *h, const char *str);
+static long slg_ctrl(BIO *h, int cmd, long arg1, void *arg2);
+static int slg_new(BIO *h);
+static int slg_free(BIO *data);
+static void xopenlog(BIO *bp, char *name, int level);
+static void xsyslog(BIO *bp, int priority, const char *string);
+static void xcloselog(BIO *bp);
+
+static const BIO_METHOD methods_slg = {
+    BIO_TYPE_MEM, "syslog",
+    slg_write,
+    NULL,
+    slg_puts,
+    NULL,
+    slg_ctrl,
+    slg_new,
+    slg_free,
+    NULL,
+};
+
+const BIO_METHOD *BIO_s_log(void)
+{
+    return (&methods_slg);
+}
+
+static int slg_new(BIO *bi)
+{
+    bi->init = 1;
+    bi->num = 0;
+    bi->ptr = NULL;
+    xopenlog(bi, "application", LOG_DAEMON);
+    return (1);
+}
+
+static int slg_free(BIO *a)
+{
+    if (a == NULL)
+        return (0);
+    xcloselog(a);
+    return (1);
+}
+
+static int slg_write(BIO *b, const char *in, int inl)
+{
+    int ret = inl;
+    char *buf;
+    char *pp;
+    int priority, i;
+    static const struct {
+        int strl;
+        char str[10];
+        int log_level;
+    } mapping[] = {
+        {
+            6, "PANIC ", LOG_EMERG
+        },
+        {
+            6, "EMERG ", LOG_EMERG
+        },
+        {
+            4, "EMR ", LOG_EMERG
+        },
+        {
+            6, "ALERT ", LOG_ALERT
+        },
+        {
+            4, "ALR ", LOG_ALERT
+        },
+        {
+            5, "CRIT ", LOG_CRIT
+        },
+        {
+            4, "CRI ", LOG_CRIT
+        },
+        {
+            6, "ERROR ", LOG_ERR
+        },
+        {
+            4, "ERR ", LOG_ERR
+        },
+        {
+            8, "WARNING ", LOG_WARNING
+        },
+        {
+            5, "WARN ", LOG_WARNING
+        },
+        {
+            4, "WAR ", LOG_WARNING
+        },
+        {
+            7, "NOTICE ", LOG_NOTICE
+        },
+        {
+            5, "NOTE ", LOG_NOTICE
+        },
+        {
+            4, "NOT ", LOG_NOTICE
+        },
+        {
+            5, "INFO ", LOG_INFO
+        },
+        {
+            4, "INF ", LOG_INFO
+        },
+        {
+            6, "DEBUG ", LOG_DEBUG
+        },
+        {
+            4, "DBG ", LOG_DEBUG
+        },
+        {
+            0, "", LOG_ERR
+        }
+        /* The default */
+    };
+
+    if ((buf = OPENSSL_malloc(inl + 1)) == NULL) {
+        return (0);
+    }
+    strncpy(buf, in, inl);
+    buf[inl] = '\0';
+
+    i = 0;
+    while (strncmp(buf, mapping[i].str, mapping[i].strl) != 0)
+        i++;
+    priority = mapping[i].log_level;
+    pp = buf + mapping[i].strl;
+
+    xsyslog(b, priority, pp);
+
+    OPENSSL_free(buf);
+    return (ret);
+}
+
+static long slg_ctrl(BIO *b, int cmd, long num, void *ptr)
+{
+    switch (cmd) {
+    case BIO_CTRL_SET:
+        xcloselog(b);
+        xopenlog(b, ptr, num);
+        break;
+    default:
+        break;
+    }
+    return (0);
+}
+
+static int slg_puts(BIO *bp, const char *str)
+{
+    int n, ret;
+
+    n = strlen(str);
+    ret = slg_write(bp, str, n);
+    return (ret);
+}
+
+# if defined(OPENSSL_SYS_WIN32)
+
+static void xopenlog(BIO *bp, char *name, int level)
+{
+    if (check_winnt())
+        bp->ptr = RegisterEventSourceA(NULL, name);
+    else
+        bp->ptr = NULL;
+}
+
+static void xsyslog(BIO *bp, int priority, const char *string)
+{
+    LPCSTR lpszStrings[2];
+    WORD evtype = EVENTLOG_ERROR_TYPE;
+    char pidbuf[DECIMAL_SIZE(DWORD) + 4];
+
+    if (bp->ptr == NULL)
+        return;
+
+    switch (priority) {
+    case LOG_EMERG:
+    case LOG_ALERT:
+    case LOG_CRIT:
+    case LOG_ERR:
+        evtype = EVENTLOG_ERROR_TYPE;
+        break;
+    case LOG_WARNING:
+        evtype = EVENTLOG_WARNING_TYPE;
+        break;
+    case LOG_NOTICE:
+    case LOG_INFO:
+    case LOG_DEBUG:
+        evtype = EVENTLOG_INFORMATION_TYPE;
+        break;
+    default:
+        /*
+         * Should never happen, but set it
+         * as error anyway.
+         */
+        evtype = EVENTLOG_ERROR_TYPE;
+        break;
+    }
+
+    sprintf(pidbuf, "[%lu] ", GetCurrentProcessId());
+    lpszStrings[0] = pidbuf;
+    lpszStrings[1] = string;
+
+    ReportEventA(bp->ptr, evtype, 0, 1024, NULL, 2, 0, lpszStrings, NULL);
+}
+
+static void xcloselog(BIO *bp)
+{
+    if (bp->ptr)
+        DeregisterEventSource((HANDLE) (bp->ptr));
+    bp->ptr = NULL;
+}
+
+# elif defined(OPENSSL_SYS_VMS)
+
+static int VMS_OPC_target = LOG_DAEMON;
+
+static void xopenlog(BIO *bp, char *name, int level)
+{
+    VMS_OPC_target = level;
+}
+
+static void xsyslog(BIO *bp, int priority, const char *string)
+{
+    struct dsc$descriptor_s opc_dsc;
+
+/* Arrange 32-bit pointer to opcdef buffer and malloc(), if needed. */
+#  if __INITIAL_POINTER_SIZE == 64
+#   pragma pointer_size save
+#   pragma pointer_size 32
+#   define OPCDEF_TYPE __char_ptr32
+#   define OPCDEF_MALLOC _malloc32
+#  else                         /* __INITIAL_POINTER_SIZE == 64 */
+#   define OPCDEF_TYPE char *
+#   define OPCDEF_MALLOC OPENSSL_malloc
+#  endif                        /* __INITIAL_POINTER_SIZE == 64 [else] */
+
+    struct opcdef *opcdef_p;
+
+#  if __INITIAL_POINTER_SIZE == 64
+#   pragma pointer_size restore
+#  endif                        /* __INITIAL_POINTER_SIZE == 64 */
+
+    char buf[10240];
+    unsigned int len;
+    struct dsc$descriptor_s buf_dsc;
+    $DESCRIPTOR(fao_cmd, "!AZ: !AZ");
+    char *priority_tag;
+
+    switch (priority) {
+    case LOG_EMERG:
+        priority_tag = "Emergency";
+        break;
+    case LOG_ALERT:
+        priority_tag = "Alert";
+        break;
+    case LOG_CRIT:
+        priority_tag = "Critical";
+        break;
+    case LOG_ERR:
+        priority_tag = "Error";
+        break;
+    case LOG_WARNING:
+        priority_tag = "Warning";
+        break;
+    case LOG_NOTICE:
+        priority_tag = "Notice";
+        break;
+    case LOG_INFO:
+        priority_tag = "Info";
+        break;
+    case LOG_DEBUG:
+        priority_tag = "DEBUG";
+        break;
+    }
+
+    buf_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
+    buf_dsc.dsc$b_class = DSC$K_CLASS_S;
+    buf_dsc.dsc$a_pointer = buf;
+    buf_dsc.dsc$w_length = sizeof(buf) - 1;
+
+    lib$sys_fao(&fao_cmd, &len, &buf_dsc, priority_tag, string);
+
+    /* We know there's an 8-byte header.  That's documented. */
+    opcdef_p = OPCDEF_MALLOC(8 + len);
+    opcdef_p->opc$b_ms_type = OPC$_RQ_RQST;
+    memcpy(opcdef_p->opc$z_ms_target_classes, &VMS_OPC_target, 3);
+    opcdef_p->opc$l_ms_rqstid = 0;
+    memcpy(&opcdef_p->opc$l_ms_text, buf, len);
+
+    opc_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
+    opc_dsc.dsc$b_class = DSC$K_CLASS_S;
+    opc_dsc.dsc$a_pointer = (OPCDEF_TYPE) opcdef_p;
+    opc_dsc.dsc$w_length = len + 8;
+
+    sys$sndopr(opc_dsc, 0);
+
+    OPENSSL_free(opcdef_p);
+}
+
+static void xcloselog(BIO *bp)
+{
+}
+
+# else                          /* Unix/Watt32 */
+
+static void xopenlog(BIO *bp, char *name, int level)
+{
+#  ifdef WATT32                 /* djgpp/DOS */
+    openlog(name, LOG_PID | LOG_CONS | LOG_NDELAY, level);
+#  else
+    openlog(name, LOG_PID | LOG_CONS, level);
+#  endif
+}
+
+static void xsyslog(BIO *bp, int priority, const char *string)
+{
+    syslog(priority, "%s", string);
+}
+
+static void xcloselog(BIO *bp)
+{
+    closelog();
+}
+
+# endif                         /* Unix */
+
+#endif                          /* NO_SYSLOG */
diff --git a/crypto/bio/bss_mem.c b/crypto/bio/bss_mem.c
new file mode 100644
index 000000000..6dc075dc6
--- /dev/null
+++ b/crypto/bio/bss_mem.c
@@ -0,0 +1,347 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include "bio_lcl.h"
+#include "internal/cryptlib.h"
+
+static int mem_write(BIO *h, const char *buf, int num);
+static int mem_read(BIO *h, char *buf, int size);
+static int mem_puts(BIO *h, const char *str);
+static int mem_gets(BIO *h, char *str, int size);
+static long mem_ctrl(BIO *h, int cmd, long arg1, void *arg2);
+static int mem_new(BIO *h);
+static int secmem_new(BIO *h);
+static int mem_free(BIO *data);
+static int mem_buf_free(BIO *data, int free_all);
+static int mem_buf_sync(BIO *h);
+
+static const BIO_METHOD mem_method = {
+    BIO_TYPE_MEM,
+    "memory buffer",
+    mem_write,
+    mem_read,
+    mem_puts,
+    mem_gets,
+    mem_ctrl,
+    mem_new,
+    mem_free,
+    NULL,
+};
+
+static const BIO_METHOD secmem_method = {
+    BIO_TYPE_MEM,
+    "secure memory buffer",
+    mem_write,
+    mem_read,
+    mem_puts,
+    mem_gets,
+    mem_ctrl,
+    secmem_new,
+    mem_free,
+    NULL,
+};
+
+/* BIO memory stores buffer and read pointer  */
+typedef struct bio_buf_mem_st {
+    struct buf_mem_st *buf;   /* allocated buffer */
+    struct buf_mem_st *readp; /* read pointer */
+} BIO_BUF_MEM;
+
+/*
+ * bio->num is used to hold the value to return on 'empty', if it is 0,
+ * should_retry is not set
+ */
+
+const BIO_METHOD *BIO_s_mem(void)
+{
+    return (&mem_method);
+}
+
+const BIO_METHOD *BIO_s_secmem(void)
+{
+    return(&secmem_method);
+}
+
+BIO *BIO_new_mem_buf(const void *buf, int len)
+{
+    BIO *ret;
+    BUF_MEM *b;
+    BIO_BUF_MEM *bb;
+    size_t sz;
+
+    if (buf == NULL) {
+        BIOerr(BIO_F_BIO_NEW_MEM_BUF, BIO_R_NULL_PARAMETER);
+        return NULL;
+    }
+    sz = (len < 0) ? strlen(buf) : (size_t)len;
+    if ((ret = BIO_new(BIO_s_mem())) == NULL)
+        return NULL;
+    bb = (BIO_BUF_MEM *)ret->ptr;
+    b = bb->buf;
+    /* Cast away const and trust in the MEM_RDONLY flag. */
+    b->data = (void *)buf;
+    b->length = sz;
+    b->max = sz;
+    *bb->readp = *bb->buf;
+    ret->flags |= BIO_FLAGS_MEM_RDONLY;
+    /* Since this is static data retrying won't help */
+    ret->num = 0;
+    return ret;
+}
+
+static int mem_init(BIO *bi, unsigned long flags)
+{
+    BIO_BUF_MEM *bb = OPENSSL_zalloc(sizeof(*bb));
+
+    if (bb == NULL)
+        return 0;
+    if ((bb->buf = BUF_MEM_new_ex(flags)) == NULL) {
+        OPENSSL_free(bb);
+        return 0;
+    }
+    if ((bb->readp = OPENSSL_zalloc(sizeof(*bb->readp))) == NULL) {
+        BUF_MEM_free(bb->buf);
+        OPENSSL_free(bb);
+        return 0;
+    }
+    *bb->readp = *bb->buf;
+    bi->shutdown = 1;
+    bi->init = 1;
+    bi->num = -1;
+    bi->ptr = (char *)bb;
+    return 1;
+}
+
+static int mem_new(BIO *bi)
+{
+    return (mem_init(bi, 0L));
+}
+
+static int secmem_new(BIO *bi)
+{
+    return (mem_init(bi, BUF_MEM_FLAG_SECURE));
+}
+
+static int mem_free(BIO *a)
+{
+    return (mem_buf_free(a, 1));
+}
+
+static int mem_buf_free(BIO *a, int free_all)
+{
+    if (a == NULL)
+        return (0);
+    if (a->shutdown) {
+        if ((a->init) && (a->ptr != NULL)) {
+            BUF_MEM *b;
+            BIO_BUF_MEM *bb = (BIO_BUF_MEM *)a->ptr;
+
+            if (bb != NULL) {
+                b = bb->buf;
+                if (a->flags & BIO_FLAGS_MEM_RDONLY)
+                    b->data = NULL;
+                BUF_MEM_free(b);
+                if (free_all) {
+                    OPENSSL_free(bb->readp);
+                    OPENSSL_free(bb);
+                }
+            }
+            a->ptr = NULL;
+        }
+    }
+    return (1);
+}
+
+/*
+ * Reallocate memory buffer if read pointer differs
+ */
+static int mem_buf_sync(BIO *b)
+{
+    if (b != NULL && b->init != 0 && b->ptr != NULL) {
+        BIO_BUF_MEM *bbm = (BIO_BUF_MEM *)b->ptr;
+
+        if (bbm->readp->data != bbm->buf->data) {
+            memmove(bbm->buf->data, bbm->readp->data, bbm->readp->length);
+            bbm->buf->length = bbm->readp->length;
+            bbm->readp->data = bbm->buf->data;
+        }
+    }
+    return (0);
+}
+
+static int mem_read(BIO *b, char *out, int outl)
+{
+    int ret = -1;
+    BIO_BUF_MEM *bbm = (BIO_BUF_MEM *)b->ptr;
+    BUF_MEM *bm = bbm->readp;
+
+    BIO_clear_retry_flags(b);
+    ret = (outl >= 0 && (size_t)outl > bm->length) ? (int)bm->length : outl;
+    if ((out != NULL) && (ret > 0)) {
+        memcpy(out, bm->data, ret);
+        bm->length -= ret;
+        bm->data += ret;
+    } else if (bm->length == 0) {
+        ret = b->num;
+        if (ret != 0)
+            BIO_set_retry_read(b);
+    }
+    return (ret);
+}
+
+static int mem_write(BIO *b, const char *in, int inl)
+{
+    int ret = -1;
+    int blen;
+    BIO_BUF_MEM *bbm = (BIO_BUF_MEM *)b->ptr;
+
+    if (in == NULL) {
+        BIOerr(BIO_F_MEM_WRITE, BIO_R_NULL_PARAMETER);
+        goto end;
+    }
+    if (b->flags & BIO_FLAGS_MEM_RDONLY) {
+        BIOerr(BIO_F_MEM_WRITE, BIO_R_WRITE_TO_READ_ONLY_BIO);
+        goto end;
+    }
+    BIO_clear_retry_flags(b);
+    blen = bbm->readp->length;
+    mem_buf_sync(b);
+    if (BUF_MEM_grow_clean(bbm->buf, blen + inl) == 0)
+        goto end;
+    memcpy(bbm->buf->data + blen, in, inl);
+    *bbm->readp = *bbm->buf;
+    ret = inl;
+ end:
+    return (ret);
+}
+
+static long mem_ctrl(BIO *b, int cmd, long num, void *ptr)
+{
+    long ret = 1;
+    char **pptr;
+    BIO_BUF_MEM *bbm = (BIO_BUF_MEM *)b->ptr;
+    BUF_MEM *bm;
+
+    switch (cmd) {
+    case BIO_CTRL_RESET:
+        bm = bbm->buf;
+        if (bm->data != NULL) {
+            /* For read only case reset to the start again */
+            if ((b->flags & BIO_FLAGS_MEM_RDONLY) || (b->flags & BIO_FLAGS_NONCLEAR_RST)) {
+                bm->length = bm->max;
+            } else {
+                memset(bm->data, 0, bm->max);
+                bm->length = 0;
+            }
+            *bbm->readp = *bbm->buf;
+        }
+        break;
+    case BIO_CTRL_EOF:
+        bm = bbm->readp;
+        ret = (long)(bm->length == 0);
+        break;
+    case BIO_C_SET_BUF_MEM_EOF_RETURN:
+        b->num = (int)num;
+        break;
+    case BIO_CTRL_INFO:
+        bm = bbm->readp;
+        ret = (long)bm->length;
+        if (ptr != NULL) {
+            pptr = (char **)ptr;
+            *pptr = (char *)&(bm->data[0]);
+        }
+        break;
+    case BIO_C_SET_BUF_MEM:
+        mem_buf_free(b, 0);
+        b->shutdown = (int)num;
+        bbm->buf = ptr;
+        *bbm->readp = *bbm->buf;
+        b->ptr = bbm;
+        break;
+    case BIO_C_GET_BUF_MEM_PTR:
+        if (ptr != NULL) {
+            mem_buf_sync(b);
+            bm = bbm->readp;
+            pptr = (char **)ptr;
+            *pptr = (char *)bm;
+        }
+        break;
+    case BIO_CTRL_GET_CLOSE:
+        ret = (long)b->shutdown;
+        break;
+    case BIO_CTRL_SET_CLOSE:
+        b->shutdown = (int)num;
+        break;
+    case BIO_CTRL_WPENDING:
+        ret = 0L;
+        break;
+    case BIO_CTRL_PENDING:
+        bm = bbm->readp;
+        ret = (long)bm->length;
+        break;
+    case BIO_CTRL_DUP:
+    case BIO_CTRL_FLUSH:
+        ret = 1;
+        break;
+    case BIO_CTRL_PUSH:
+    case BIO_CTRL_POP:
+    default:
+        ret = 0;
+        break;
+    }
+    return (ret);
+}
+
+static int mem_gets(BIO *bp, char *buf, int size)
+{
+    int i, j;
+    int ret = -1;
+    char *p;
+    BIO_BUF_MEM *bbm = (BIO_BUF_MEM *)bp->ptr;
+    BUF_MEM *bm = bbm->readp;
+
+    BIO_clear_retry_flags(bp);
+    j = bm->length;
+    if ((size - 1) < j)
+        j = size - 1;
+    if (j <= 0) {
+        *buf = '\0';
+        return 0;
+    }
+    p = bm->data;
+    for (i = 0; i < j; i++) {
+        if (p[i] == '\n') {
+            i++;
+            break;
+        }
+    }
+
+    /*
+     * i is now the max num of bytes to copy, either j or up to
+     * and including the first newline
+     */
+
+    i = mem_read(bp, buf, i);
+    if (i > 0)
+        buf[i] = '\0';
+    ret = i;
+    return (ret);
+}
+
+static int mem_puts(BIO *bp, const char *str)
+{
+    int n, ret;
+
+    n = strlen(str);
+    ret = mem_write(bp, str, n);
+    /* memory semantics is that it will always work */
+    return (ret);
+}
diff --git a/crypto/bio/bss_null.c b/crypto/bio/bss_null.c
new file mode 100644
index 000000000..e5c4adc87
--- /dev/null
+++ b/crypto/bio/bss_null.c
@@ -0,0 +1,100 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include "bio_lcl.h"
+#include "internal/cryptlib.h"
+
+static int null_write(BIO *h, const char *buf, int num);
+static int null_read(BIO *h, char *buf, int size);
+static int null_puts(BIO *h, const char *str);
+static int null_gets(BIO *h, char *str, int size);
+static long null_ctrl(BIO *h, int cmd, long arg1, void *arg2);
+static int null_new(BIO *h);
+static int null_free(BIO *data);
+static const BIO_METHOD null_method = {
+    BIO_TYPE_NULL,
+    "NULL",
+    null_write,
+    null_read,
+    null_puts,
+    null_gets,
+    null_ctrl,
+    null_new,
+    null_free,
+    NULL,
+};
+
+const BIO_METHOD *BIO_s_null(void)
+{
+    return (&null_method);
+}
+
+static int null_new(BIO *bi)
+{
+    bi->init = 1;
+    bi->num = 0;
+    bi->ptr = (NULL);
+    return (1);
+}
+
+static int null_free(BIO *a)
+{
+    if (a == NULL)
+        return (0);
+    return (1);
+}
+
+static int null_read(BIO *b, char *out, int outl)
+{
+    return (0);
+}
+
+static int null_write(BIO *b, const char *in, int inl)
+{
+    return (inl);
+}
+
+static long null_ctrl(BIO *b, int cmd, long num, void *ptr)
+{
+    long ret = 1;
+
+    switch (cmd) {
+    case BIO_CTRL_RESET:
+    case BIO_CTRL_EOF:
+    case BIO_CTRL_SET:
+    case BIO_CTRL_SET_CLOSE:
+    case BIO_CTRL_FLUSH:
+    case BIO_CTRL_DUP:
+        ret = 1;
+        break;
+    case BIO_CTRL_GET_CLOSE:
+    case BIO_CTRL_INFO:
+    case BIO_CTRL_GET:
+    case BIO_CTRL_PENDING:
+    case BIO_CTRL_WPENDING:
+    default:
+        ret = 0;
+        break;
+    }
+    return (ret);
+}
+
+static int null_gets(BIO *bp, char *buf, int size)
+{
+    return (0);
+}
+
+static int null_puts(BIO *bp, const char *str)
+{
+    if (str == NULL)
+        return (0);
+    return (strlen(str));
+}
diff --git a/crypto/bio/bss_sock.c b/crypto/bio/bss_sock.c
new file mode 100644
index 000000000..570e8985a
--- /dev/null
+++ b/crypto/bio/bss_sock.c
@@ -0,0 +1,231 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#define USE_SOCKETS
+#include "bio_lcl.h"
+#include "internal/cryptlib.h"
+
+#ifndef OPENSSL_NO_SOCK
+
+# include 
+
+# ifdef WATT32
+/* Watt-32 uses same names */
+#  undef sock_write
+#  undef sock_read
+#  undef sock_puts
+#  define sock_write SockWrite
+#  define sock_read  SockRead
+#  define sock_puts  SockPuts
+# endif
+
+static int sock_write(BIO *h, const char *buf, int num);
+static int sock_read(BIO *h, char *buf, int size);
+static int sock_puts(BIO *h, const char *str);
+static long sock_ctrl(BIO *h, int cmd, long arg1, void *arg2);
+static int sock_new(BIO *h);
+static int sock_free(BIO *data);
+int BIO_sock_should_retry(int s);
+
+static const BIO_METHOD methods_sockp = {
+    BIO_TYPE_SOCKET,
+    "socket",
+    sock_write,
+    sock_read,
+    sock_puts,
+    NULL,                       /* sock_gets, */
+    sock_ctrl,
+    sock_new,
+    sock_free,
+    NULL,
+};
+
+const BIO_METHOD *BIO_s_socket(void)
+{
+    return (&methods_sockp);
+}
+
+BIO *BIO_new_socket(int fd, int close_flag)
+{
+    BIO *ret;
+
+    ret = BIO_new(BIO_s_socket());
+    if (ret == NULL)
+        return (NULL);
+    BIO_set_fd(ret, fd, close_flag);
+    return (ret);
+}
+
+static int sock_new(BIO *bi)
+{
+    bi->init = 0;
+    bi->num = 0;
+    bi->ptr = NULL;
+    bi->flags = 0;
+    return (1);
+}
+
+static int sock_free(BIO *a)
+{
+    if (a == NULL)
+        return (0);
+    if (a->shutdown) {
+        if (a->init) {
+            BIO_closesocket(a->num);
+        }
+        a->init = 0;
+        a->flags = 0;
+    }
+    return (1);
+}
+
+static int sock_read(BIO *b, char *out, int outl)
+{
+    int ret = 0;
+
+    if (out != NULL) {
+        clear_socket_error();
+        ret = readsocket(b->num, out, outl);
+        BIO_clear_retry_flags(b);
+        if (ret <= 0) {
+            if (BIO_sock_should_retry(ret))
+                BIO_set_retry_read(b);
+        }
+    }
+    return (ret);
+}
+
+static int sock_write(BIO *b, const char *in, int inl)
+{
+    int ret;
+
+    clear_socket_error();
+    ret = writesocket(b->num, in, inl);
+    BIO_clear_retry_flags(b);
+    if (ret <= 0) {
+        if (BIO_sock_should_retry(ret))
+            BIO_set_retry_write(b);
+    }
+    return (ret);
+}
+
+static long sock_ctrl(BIO *b, int cmd, long num, void *ptr)
+{
+    long ret = 1;
+    int *ip;
+
+    switch (cmd) {
+    case BIO_C_SET_FD:
+        sock_free(b);
+        b->num = *((int *)ptr);
+        b->shutdown = (int)num;
+        b->init = 1;
+        break;
+    case BIO_C_GET_FD:
+        if (b->init) {
+            ip = (int *)ptr;
+            if (ip != NULL)
+                *ip = b->num;
+            ret = b->num;
+        } else
+            ret = -1;
+        break;
+    case BIO_CTRL_GET_CLOSE:
+        ret = b->shutdown;
+        break;
+    case BIO_CTRL_SET_CLOSE:
+        b->shutdown = (int)num;
+        break;
+    case BIO_CTRL_DUP:
+    case BIO_CTRL_FLUSH:
+        ret = 1;
+        break;
+    default:
+        ret = 0;
+        break;
+    }
+    return (ret);
+}
+
+static int sock_puts(BIO *bp, const char *str)
+{
+    int n, ret;
+
+    n = strlen(str);
+    ret = sock_write(bp, str, n);
+    return (ret);
+}
+
+int BIO_sock_should_retry(int i)
+{
+    int err;
+
+    if ((i == 0) || (i == -1)) {
+        err = get_last_socket_error();
+
+        return (BIO_sock_non_fatal_error(err));
+    }
+    return (0);
+}
+
+int BIO_sock_non_fatal_error(int err)
+{
+    switch (err) {
+# if defined(OPENSSL_SYS_WINDOWS)
+#  if defined(WSAEWOULDBLOCK)
+    case WSAEWOULDBLOCK:
+#  endif
+# endif
+
+# ifdef EWOULDBLOCK
+#  ifdef WSAEWOULDBLOCK
+#   if WSAEWOULDBLOCK != EWOULDBLOCK
+    case EWOULDBLOCK:
+#   endif
+#  else
+    case EWOULDBLOCK:
+#  endif
+# endif
+
+# if defined(ENOTCONN)
+    case ENOTCONN:
+# endif
+
+# ifdef EINTR
+    case EINTR:
+# endif
+
+# ifdef EAGAIN
+#  if EWOULDBLOCK != EAGAIN
+    case EAGAIN:
+#  endif
+# endif
+
+# ifdef EPROTO
+    case EPROTO:
+# endif
+
+# ifdef EINPROGRESS
+    case EINPROGRESS:
+# endif
+
+# ifdef EALREADY
+    case EALREADY:
+# endif
+        return (1);
+        /* break; */
+    default:
+        break;
+    }
+    return (0);
+}
+
+#endif                          /* #ifndef OPENSSL_NO_SOCK */
diff --git a/crypto/bio/build.info b/crypto/bio/build.info
new file mode 100644
index 000000000..d1e7d73c5
--- /dev/null
+++ b/crypto/bio/build.info
@@ -0,0 +1,8 @@
+LIBS=../../libcrypto
+SOURCE[../../libcrypto]=\
+        bio_lib.c bio_cb.c bio_err.c \
+        bss_mem.c bss_null.c bss_fd.c \
+        bss_file.c bss_sock.c bss_conn.c \
+        bf_null.c bf_buff.c b_print.c b_dump.c b_addr.c \
+        b_sock.c b_sock2.c bss_acpt.c bf_nbio.c bss_log.c bss_bio.c \
+        bss_dgram.c bio_meth.c bf_lbuf.c
diff --git a/crypto/blake2/blake2_impl.h b/crypto/blake2/blake2_impl.h
new file mode 100644
index 000000000..8fe5c9591
--- /dev/null
+++ b/crypto/blake2/blake2_impl.h
@@ -0,0 +1,130 @@
+/*
+ * Copyright 2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * Derived from the BLAKE2 reference implementation written by Samuel Neves.
+ * Copyright 2012, Samuel Neves 
+ * More information about the BLAKE2 hash function and its implementations
+ * can be found at https://blake2.net.
+ */
+
+#include 
+#include "e_os.h"
+
+static ossl_inline uint32_t load32(const uint8_t *src)
+{
+    const union {
+        long one;
+        char little;
+    } is_endian = { 1 };
+
+    if (is_endian.little) {
+        uint32_t w;
+        memcpy(&w, src, sizeof(w));
+        return w;
+    } else {
+        uint32_t w = ((uint32_t)src[0])
+                   | ((uint32_t)src[1] <<  8)
+                   | ((uint32_t)src[2] << 16)
+                   | ((uint32_t)src[3] << 24);
+        return w;
+    }
+}
+
+static ossl_inline uint64_t load64(const uint8_t *src)
+{
+    const union {
+        long one;
+        char little;
+    } is_endian = { 1 };
+
+    if (is_endian.little) {
+        uint64_t w;
+        memcpy(&w, src, sizeof(w));
+        return w;
+    } else {
+        uint64_t w = ((uint64_t)src[0])
+                   | ((uint64_t)src[1] <<  8)
+                   | ((uint64_t)src[2] << 16)
+                   | ((uint64_t)src[3] << 24)
+                   | ((uint64_t)src[4] << 32)
+                   | ((uint64_t)src[5] << 40)
+                   | ((uint64_t)src[6] << 48)
+                   | ((uint64_t)src[7] << 56);
+        return w;
+    }
+}
+
+static ossl_inline void store32(uint8_t *dst, uint32_t w)
+{
+    const union {
+        long one;
+        char little;
+    } is_endian = { 1 };
+
+    if (is_endian.little) {
+        memcpy(dst, &w, sizeof(w));
+    } else {
+        uint8_t *p = (uint8_t *)dst;
+        int i;
+
+        for (i = 0; i < 4; i++)
+            p[i] = (uint8_t)(w >> (8 * i));
+    }
+}
+
+static ossl_inline void store64(uint8_t *dst, uint64_t w)
+{
+    const union {
+        long one;
+        char little;
+    } is_endian = { 1 };
+
+    if (is_endian.little) {
+        memcpy(dst, &w, sizeof(w));
+    } else {
+        uint8_t *p = (uint8_t *)dst;
+        int i;
+
+        for (i = 0; i < 8; i++)
+            p[i] = (uint8_t)(w >> (8 * i));
+    }
+}
+
+static ossl_inline uint64_t load48(const uint8_t *src)
+{
+    uint64_t w = ((uint64_t)src[0])
+               | ((uint64_t)src[1] <<  8)
+               | ((uint64_t)src[2] << 16)
+               | ((uint64_t)src[3] << 24)
+               | ((uint64_t)src[4] << 32)
+               | ((uint64_t)src[5] << 40);
+    return w;
+}
+
+static ossl_inline void store48(uint8_t *dst, uint64_t w)
+{
+    uint8_t *p = (uint8_t *)dst;
+    p[0] = (uint8_t)w;
+    p[1] = (uint8_t)(w>>8);
+    p[2] = (uint8_t)(w>>16);
+    p[3] = (uint8_t)(w>>24);
+    p[4] = (uint8_t)(w>>32);
+    p[5] = (uint8_t)(w>>40);
+}
+
+static ossl_inline uint32_t rotr32(const uint32_t w, const unsigned int c)
+{
+    return (w >> c) | (w << (32 - c));
+}
+
+static ossl_inline uint64_t rotr64(const uint64_t w, const unsigned int c)
+{
+    return (w >> c) | (w << (64 - c));
+}
diff --git a/crypto/blake2/blake2_locl.h b/crypto/blake2/blake2_locl.h
new file mode 100644
index 000000000..fb7beb976
--- /dev/null
+++ b/crypto/blake2/blake2_locl.h
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * Derived from the BLAKE2 reference implementation written by Samuel Neves.
+ * Copyright 2012, Samuel Neves 
+ * More information about the BLAKE2 hash function and its implementations
+ * can be found at https://blake2.net.
+ */
+
+#include 
+#include "e_os.h"
+
+#define BLAKE2S_BLOCKBYTES    64
+#define BLAKE2S_OUTBYTES      32
+#define BLAKE2S_KEYBYTES      32
+#define BLAKE2S_SALTBYTES     8
+#define BLAKE2S_PERSONALBYTES 8
+
+#define BLAKE2B_BLOCKBYTES    128
+#define BLAKE2B_OUTBYTES      64
+#define BLAKE2B_KEYBYTES      64
+#define BLAKE2B_SALTBYTES     16
+#define BLAKE2B_PERSONALBYTES 16
+
+struct blake2s_param_st {
+    uint8_t  digest_length; /* 1 */
+    uint8_t  key_length;    /* 2 */
+    uint8_t  fanout;        /* 3 */
+    uint8_t  depth;         /* 4 */
+    uint8_t  leaf_length[4];/* 8 */
+    uint8_t  node_offset[6];/* 14 */
+    uint8_t  node_depth;    /* 15 */
+    uint8_t  inner_length;  /* 16 */
+    uint8_t  salt[BLAKE2S_SALTBYTES]; /* 24 */
+    uint8_t  personal[BLAKE2S_PERSONALBYTES];  /* 32 */
+};
+
+typedef struct blake2s_param_st BLAKE2S_PARAM;
+
+struct blake2s_ctx_st {
+    uint32_t h[8];
+    uint32_t t[2];
+    uint32_t f[2];
+    uint8_t  buf[BLAKE2S_BLOCKBYTES];
+    size_t   buflen;
+};
+
+struct blake2b_param_st {
+    uint8_t  digest_length; /* 1 */
+    uint8_t  key_length;    /* 2 */
+    uint8_t  fanout;        /* 3 */
+    uint8_t  depth;         /* 4 */
+    uint8_t  leaf_length[4];/* 8 */
+    uint8_t  node_offset[8];/* 16 */
+    uint8_t  node_depth;    /* 17 */
+    uint8_t  inner_length;  /* 18 */
+    uint8_t  reserved[14];  /* 32 */
+    uint8_t  salt[BLAKE2B_SALTBYTES]; /* 48 */
+    uint8_t  personal[BLAKE2B_PERSONALBYTES];  /* 64 */
+};
+
+typedef struct blake2b_param_st BLAKE2B_PARAM;
+
+struct blake2b_ctx_st {
+    uint64_t h[8];
+    uint64_t t[2];
+    uint64_t f[2];
+    uint8_t  buf[BLAKE2B_BLOCKBYTES];
+    size_t   buflen;
+};
+
+#define BLAKE2B_DIGEST_LENGTH 64
+#define BLAKE2S_DIGEST_LENGTH 32
+
+typedef struct blake2s_ctx_st BLAKE2S_CTX;
+typedef struct blake2b_ctx_st BLAKE2B_CTX;
+
+int BLAKE2b_Init(BLAKE2B_CTX *c);
+int BLAKE2b_Update(BLAKE2B_CTX *c, const void *data, size_t datalen);
+int BLAKE2b_Final(unsigned char *md, BLAKE2B_CTX *c);
+
+int BLAKE2s_Init(BLAKE2S_CTX *c);
+int BLAKE2s_Update(BLAKE2S_CTX *c, const void *data, size_t datalen);
+int BLAKE2s_Final(unsigned char *md, BLAKE2S_CTX *c);
diff --git a/crypto/blake2/blake2b.c b/crypto/blake2/blake2b.c
new file mode 100644
index 000000000..e77bd9ac1
--- /dev/null
+++ b/crypto/blake2/blake2b.c
@@ -0,0 +1,270 @@
+/*
+ * Copyright 2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * Derived from the BLAKE2 reference implementation written by Samuel Neves.
+ * Copyright 2012, Samuel Neves 
+ * More information about the BLAKE2 hash function and its implementations
+ * can be found at https://blake2.net.
+ */
+
+#include 
+#include 
+#include 
+#include "e_os.h"
+
+#include "blake2_locl.h"
+#include "blake2_impl.h"
+
+static const uint64_t blake2b_IV[8] =
+{
+    0x6a09e667f3bcc908U, 0xbb67ae8584caa73bU,
+    0x3c6ef372fe94f82bU, 0xa54ff53a5f1d36f1U,
+    0x510e527fade682d1U, 0x9b05688c2b3e6c1fU,
+    0x1f83d9abfb41bd6bU, 0x5be0cd19137e2179U
+};
+
+static const uint8_t blake2b_sigma[12][16] =
+{
+    {  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15 } ,
+    { 14, 10,  4,  8,  9, 15, 13,  6,  1, 12,  0,  2, 11,  7,  5,  3 } ,
+    { 11,  8, 12,  0,  5,  2, 15, 13, 10, 14,  3,  6,  7,  1,  9,  4 } ,
+    {  7,  9,  3,  1, 13, 12, 11, 14,  2,  6,  5, 10,  4,  0, 15,  8 } ,
+    {  9,  0,  5,  7,  2,  4, 10, 15, 14,  1, 11, 12,  6,  8,  3, 13 } ,
+    {  2, 12,  6, 10,  0, 11,  8,  3,  4, 13,  7,  5, 15, 14,  1,  9 } ,
+    { 12,  5,  1, 15, 14, 13,  4, 10,  0,  7,  6,  3,  9,  2,  8, 11 } ,
+    { 13, 11,  7, 14, 12,  1,  3,  9,  5,  0, 15,  4,  8,  6,  2, 10 } ,
+    {  6, 15, 14,  9, 11,  3,  0,  8, 12,  2, 13,  7,  1,  4, 10,  5 } ,
+    { 10,  2,  8,  4,  7,  6,  1,  5, 15, 11,  9, 14,  3, 12, 13 , 0 } ,
+    {  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15 } ,
+    { 14, 10,  4,  8,  9, 15, 13,  6,  1, 12,  0,  2, 11,  7,  5,  3 }
+};
+
+/* Set that it's the last block we'll compress */
+static ossl_inline void blake2b_set_lastblock(BLAKE2B_CTX *S)
+{
+    S->f[0] = -1;
+}
+
+/* Initialize the hashing state. */
+static ossl_inline void blake2b_init0(BLAKE2B_CTX *S)
+{
+    int i;
+
+    memset(S, 0, sizeof(BLAKE2B_CTX));
+    for (i = 0; i < 8; ++i) {
+        S->h[i] = blake2b_IV[i];
+    }
+}
+
+/* init xors IV with input parameter block */
+static void blake2b_init_param(BLAKE2B_CTX *S, const BLAKE2B_PARAM *P)
+{
+    size_t i;
+    const uint8_t *p = (const uint8_t *)(P);
+    blake2b_init0(S);
+
+    /* The param struct is carefully hand packed, and should be 64 bytes on
+     * every platform. */
+    assert(sizeof(BLAKE2B_PARAM) == 64);
+    /* IV XOR ParamBlock */
+    for (i = 0; i < 8; ++i) {
+        S->h[i] ^= load64(p + sizeof(S->h[i]) * i);
+    }
+}
+
+/* Initialize the hashing context.  Always returns 1. */
+int BLAKE2b_Init(BLAKE2B_CTX *c)
+{
+    BLAKE2B_PARAM P[1];
+    P->digest_length = BLAKE2B_DIGEST_LENGTH;
+    P->key_length    = 0;
+    P->fanout        = 1;
+    P->depth         = 1;
+    store32(P->leaf_length, 0);
+    store64(P->node_offset, 0);
+    P->node_depth    = 0;
+    P->inner_length  = 0;
+    memset(P->reserved, 0, sizeof(P->reserved));
+    memset(P->salt,     0, sizeof(P->salt));
+    memset(P->personal, 0, sizeof(P->personal));
+    blake2b_init_param(c, P);
+    return 1;
+}
+
+/* Permute the state while xoring in the block of data. */
+static void blake2b_compress(BLAKE2B_CTX *S,
+                            const uint8_t *blocks,
+                            size_t len)
+{
+    uint64_t m[16];
+    uint64_t v[16];
+    int i;
+    size_t increment;
+
+    /*
+     * There are two distinct usage vectors for this function:
+     *
+     * a) BLAKE2b_Update uses it to process complete blocks,
+     *    possibly more than one at a time;
+     *
+     * b) BLAK2b_Final uses it to process last block, always
+     *    single but possibly incomplete, in which case caller
+     *    pads input with zeros.
+     */
+    assert(len < BLAKE2B_BLOCKBYTES || len % BLAKE2B_BLOCKBYTES == 0);
+
+    /*
+     * Since last block is always processed with separate call,
+     * |len| not being multiple of complete blocks can be observed
+     * only with |len| being less than BLAKE2B_BLOCKBYTES ("less"
+     * including even zero), which is why following assignment doesn't
+     * have to reside inside the main loop below.
+     */
+    increment = len < BLAKE2B_BLOCKBYTES ? len : BLAKE2B_BLOCKBYTES;
+
+    for (i = 0; i < 8; ++i) {
+        v[i] = S->h[i];
+    }
+
+    do {
+        for (i = 0; i < 16; ++i) {
+            m[i] = load64(blocks + i * sizeof(m[i]));
+        }
+
+        /* blake2b_increment_counter */
+        S->t[0] += increment;
+        S->t[1] += (S->t[0] < increment);
+
+        v[8]  = blake2b_IV[0];
+        v[9]  = blake2b_IV[1];
+        v[10] = blake2b_IV[2];
+        v[11] = blake2b_IV[3];
+        v[12] = S->t[0] ^ blake2b_IV[4];
+        v[13] = S->t[1] ^ blake2b_IV[5];
+        v[14] = S->f[0] ^ blake2b_IV[6];
+        v[15] = S->f[1] ^ blake2b_IV[7];
+#define G(r,i,a,b,c,d) \
+        do { \
+            a = a + b + m[blake2b_sigma[r][2*i+0]]; \
+            d = rotr64(d ^ a, 32); \
+            c = c + d; \
+            b = rotr64(b ^ c, 24); \
+            a = a + b + m[blake2b_sigma[r][2*i+1]]; \
+            d = rotr64(d ^ a, 16); \
+            c = c + d; \
+            b = rotr64(b ^ c, 63); \
+        } while (0)
+#define ROUND(r)  \
+        do { \
+            G(r,0,v[ 0],v[ 4],v[ 8],v[12]); \
+            G(r,1,v[ 1],v[ 5],v[ 9],v[13]); \
+            G(r,2,v[ 2],v[ 6],v[10],v[14]); \
+            G(r,3,v[ 3],v[ 7],v[11],v[15]); \
+            G(r,4,v[ 0],v[ 5],v[10],v[15]); \
+            G(r,5,v[ 1],v[ 6],v[11],v[12]); \
+            G(r,6,v[ 2],v[ 7],v[ 8],v[13]); \
+            G(r,7,v[ 3],v[ 4],v[ 9],v[14]); \
+        } while (0)
+#if defined(OPENSSL_SMALL_FOOTPRINT)
+        /* 3x size reduction on x86_64, almost 7x on ARMv8, 9x on ARMv4 */
+        for (i = 0; i < 12; i++) {
+            ROUND(i);
+        }
+#else
+        ROUND(0);
+        ROUND(1);
+        ROUND(2);
+        ROUND(3);
+        ROUND(4);
+        ROUND(5);
+        ROUND(6);
+        ROUND(7);
+        ROUND(8);
+        ROUND(9);
+        ROUND(10);
+        ROUND(11);
+#endif
+
+        for (i = 0; i < 8; ++i) {
+            S->h[i] = v[i] ^= v[i + 8] ^ S->h[i];
+        }
+#undef G
+#undef ROUND
+        blocks += increment;
+        len -= increment;
+    } while (len);
+}
+
+/* Absorb the input data into the hash state.  Always returns 1. */
+int BLAKE2b_Update(BLAKE2B_CTX *c, const void *data, size_t datalen)
+{
+    const uint8_t *in = data;
+    size_t fill;
+
+    /*
+     * Intuitively one would expect intermediate buffer, c->buf, to
+     * store incomplete blocks. But in this case we are interested to
+     * temporarily stash even complete blocks, because last one in the
+     * stream has to be treated in special way, and at this point we
+     * don't know if last block in *this* call is last one "ever". This
+     * is the reason for why |datalen| is compared as >, and not >=.
+     */
+    fill = sizeof(c->buf) - c->buflen;
+    if (datalen > fill) {
+        if (c->buflen) {
+            memcpy(c->buf + c->buflen, in, fill); /* Fill buffer */
+            blake2b_compress(c, c->buf, BLAKE2B_BLOCKBYTES);
+            c->buflen = 0;
+            in += fill;
+            datalen -= fill;
+        }
+        if (datalen > BLAKE2B_BLOCKBYTES) {
+            size_t stashlen = datalen % BLAKE2B_BLOCKBYTES;
+            /*
+             * If |datalen| is a multiple of the blocksize, stash
+             * last complete block, it can be final one...
+             */
+            stashlen = stashlen ? stashlen : BLAKE2B_BLOCKBYTES;
+            datalen -= stashlen;
+            blake2b_compress(c, in, datalen);
+            in += datalen;
+            datalen = stashlen;
+        }
+    }
+
+    assert(datalen <= BLAKE2B_BLOCKBYTES);
+
+    memcpy(c->buf + c->buflen, in, datalen);
+    c->buflen += datalen; /* Be lazy, do not compress */
+
+    return 1;
+}
+
+/*
+ * Calculate the final hash and save it in md.
+ * Always returns 1.
+ */
+int BLAKE2b_Final(unsigned char *md, BLAKE2B_CTX *c)
+{
+    int i;
+
+    blake2b_set_lastblock(c);
+    /* Padding */
+    memset(c->buf + c->buflen, 0, sizeof(c->buf) - c->buflen);
+    blake2b_compress(c, c->buf, c->buflen);
+
+    /* Output full hash to message digest */
+    for (i = 0; i < 8; ++i) {
+        store64(md + sizeof(c->h[i]) * i, c->h[i]);
+    }
+
+    OPENSSL_cleanse(c, sizeof(BLAKE2B_CTX));
+    return 1;
+}
diff --git a/crypto/blake2/blake2s.c b/crypto/blake2/blake2s.c
new file mode 100644
index 000000000..0b3503e4f
--- /dev/null
+++ b/crypto/blake2/blake2s.c
@@ -0,0 +1,264 @@
+/*
+ * Copyright 2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * Derived from the BLAKE2 reference implementation written by Samuel Neves.
+ * Copyright 2012, Samuel Neves 
+ * More information about the BLAKE2 hash function and its implementations
+ * can be found at https://blake2.net.
+ */
+
+#include 
+#include 
+#include 
+#include "e_os.h"
+
+#include "blake2_locl.h"
+#include "blake2_impl.h"
+
+static const uint32_t blake2s_IV[8] =
+{
+    0x6A09E667U, 0xBB67AE85U, 0x3C6EF372U, 0xA54FF53AU,
+    0x510E527FU, 0x9B05688CU, 0x1F83D9ABU, 0x5BE0CD19U
+};
+
+static const uint8_t blake2s_sigma[10][16] =
+{
+    {  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15 } ,
+    { 14, 10,  4,  8,  9, 15, 13,  6,  1, 12,  0,  2, 11,  7,  5,  3 } ,
+    { 11,  8, 12,  0,  5,  2, 15, 13, 10, 14,  3,  6,  7,  1,  9,  4 } ,
+    {  7,  9,  3,  1, 13, 12, 11, 14,  2,  6,  5, 10,  4,  0, 15,  8 } ,
+    {  9,  0,  5,  7,  2,  4, 10, 15, 14,  1, 11, 12,  6,  8,  3, 13 } ,
+    {  2, 12,  6, 10,  0, 11,  8,  3,  4, 13,  7,  5, 15, 14,  1,  9 } ,
+    { 12,  5,  1, 15, 14, 13,  4, 10,  0,  7,  6,  3,  9,  2,  8, 11 } ,
+    { 13, 11,  7, 14, 12,  1,  3,  9,  5,  0, 15,  4,  8,  6,  2, 10 } ,
+    {  6, 15, 14,  9, 11,  3,  0,  8, 12,  2, 13,  7,  1,  4, 10,  5 } ,
+    { 10,  2,  8,  4,  7,  6,  1,  5, 15, 11,  9, 14,  3, 12, 13 , 0 } ,
+};
+
+/* Set that it's the last block we'll compress */
+static ossl_inline void blake2s_set_lastblock(BLAKE2S_CTX *S)
+{
+    S->f[0] = -1;
+}
+
+/* Initialize the hashing state. */
+static ossl_inline void blake2s_init0(BLAKE2S_CTX *S)
+{
+    int i;
+
+    memset(S, 0, sizeof(BLAKE2S_CTX));
+    for (i = 0; i < 8; ++i) {
+        S->h[i] = blake2s_IV[i];
+    }
+}
+
+/* init2 xors IV with input parameter block */
+static void blake2s_init_param(BLAKE2S_CTX *S, const BLAKE2S_PARAM *P)
+{
+    const uint8_t *p = (const uint8_t *)(P);
+    size_t i;
+
+    /* The param struct is carefully hand packed, and should be 32 bytes on
+     * every platform. */
+    assert(sizeof(BLAKE2S_PARAM) == 32);
+    blake2s_init0(S);
+    /* IV XOR ParamBlock */
+    for (i = 0; i < 8; ++i) {
+        S->h[i] ^= load32(&p[i*4]);
+    }
+}
+
+/* Initialize the hashing context.  Always returns 1. */
+int BLAKE2s_Init(BLAKE2S_CTX *c)
+{
+    BLAKE2S_PARAM P[1];
+
+    P->digest_length = BLAKE2S_DIGEST_LENGTH;
+    P->key_length    = 0;
+    P->fanout        = 1;
+    P->depth         = 1;
+    store32(P->leaf_length, 0);
+    store48(P->node_offset, 0);
+    P->node_depth    = 0;
+    P->inner_length  = 0;
+    memset(P->salt,     0, sizeof(P->salt));
+    memset(P->personal, 0, sizeof(P->personal));
+    blake2s_init_param(c, P);
+    return 1;
+}
+
+/* Permute the state while xoring in the block of data. */
+static void blake2s_compress(BLAKE2S_CTX *S,
+                            const uint8_t *blocks,
+                            size_t len)
+{
+    uint32_t m[16];
+    uint32_t v[16];
+    size_t i;
+    size_t increment;
+
+    /*
+     * There are two distinct usage vectors for this function:
+     *
+     * a) BLAKE2s_Update uses it to process complete blocks,
+     *    possibly more than one at a time;
+     *
+     * b) BLAK2s_Final uses it to process last block, always
+     *    single but possibly incomplete, in which case caller
+     *    pads input with zeros.
+     */
+    assert(len < BLAKE2S_BLOCKBYTES || len % BLAKE2S_BLOCKBYTES == 0);
+
+    /*
+     * Since last block is always processed with separate call,
+     * |len| not being multiple of complete blocks can be observed
+     * only with |len| being less than BLAKE2S_BLOCKBYTES ("less"
+     * including even zero), which is why following assignment doesn't
+     * have to reside inside the main loop below.
+     */
+    increment = len < BLAKE2S_BLOCKBYTES ? len : BLAKE2S_BLOCKBYTES;
+
+    for (i = 0; i < 8; ++i) {
+        v[i] = S->h[i];
+    }
+
+    do {
+        for (i = 0; i < 16; ++i) {
+            m[i] = load32(blocks + i * sizeof(m[i]));
+        }
+
+        /* blake2s_increment_counter */
+        S->t[0] += increment;
+        S->t[1] += (S->t[0] < increment);
+
+        v[ 8] = blake2s_IV[0];
+        v[ 9] = blake2s_IV[1];
+        v[10] = blake2s_IV[2];
+        v[11] = blake2s_IV[3];
+        v[12] = S->t[0] ^ blake2s_IV[4];
+        v[13] = S->t[1] ^ blake2s_IV[5];
+        v[14] = S->f[0] ^ blake2s_IV[6];
+        v[15] = S->f[1] ^ blake2s_IV[7];
+#define G(r,i,a,b,c,d) \
+        do { \
+            a = a + b + m[blake2s_sigma[r][2*i+0]]; \
+            d = rotr32(d ^ a, 16); \
+            c = c + d; \
+            b = rotr32(b ^ c, 12); \
+            a = a + b + m[blake2s_sigma[r][2*i+1]]; \
+            d = rotr32(d ^ a, 8); \
+            c = c + d; \
+            b = rotr32(b ^ c, 7); \
+        } while (0)
+#define ROUND(r)  \
+        do { \
+            G(r,0,v[ 0],v[ 4],v[ 8],v[12]); \
+            G(r,1,v[ 1],v[ 5],v[ 9],v[13]); \
+            G(r,2,v[ 2],v[ 6],v[10],v[14]); \
+            G(r,3,v[ 3],v[ 7],v[11],v[15]); \
+            G(r,4,v[ 0],v[ 5],v[10],v[15]); \
+            G(r,5,v[ 1],v[ 6],v[11],v[12]); \
+            G(r,6,v[ 2],v[ 7],v[ 8],v[13]); \
+            G(r,7,v[ 3],v[ 4],v[ 9],v[14]); \
+        } while (0)
+#if defined(OPENSSL_SMALL_FOOTPRINT)
+        /* almost 3x reduction on x86_64, 4.5x on ARMv8, 4x on ARMv4 */
+        for (i = 0; i < 10; i++) {
+            ROUND(i);
+        }
+#else
+        ROUND(0);
+        ROUND(1);
+        ROUND(2);
+        ROUND(3);
+        ROUND(4);
+        ROUND(5);
+        ROUND(6);
+        ROUND(7);
+        ROUND(8);
+        ROUND(9);
+#endif
+
+        for (i = 0; i < 8; ++i) {
+            S->h[i] = v[i] ^= v[i + 8] ^ S->h[i];
+        }
+#undef G
+#undef ROUND
+        blocks += increment;
+        len -= increment;
+    } while (len);
+}
+
+/* Absorb the input data into the hash state.  Always returns 1. */
+int BLAKE2s_Update(BLAKE2S_CTX *c, const void *data, size_t datalen)
+{
+    const uint8_t *in = data;
+    size_t fill;
+
+    /*
+     * Intuitively one would expect intermediate buffer, c->buf, to
+     * store incomplete blocks. But in this case we are interested to
+     * temporarily stash even complete blocks, because last one in the
+     * stream has to be treated in special way, and at this point we
+     * don't know if last block in *this* call is last one "ever". This
+     * is the reason for why |datalen| is compared as >, and not >=.
+     */
+    fill = sizeof(c->buf) - c->buflen;
+    if (datalen > fill) {
+        if (c->buflen) {
+            memcpy(c->buf + c->buflen, in, fill); /* Fill buffer */
+            blake2s_compress(c, c->buf, BLAKE2S_BLOCKBYTES);
+            c->buflen = 0;
+            in += fill;
+            datalen -= fill;
+        }
+        if (datalen > BLAKE2S_BLOCKBYTES)  {
+            size_t stashlen = datalen % BLAKE2S_BLOCKBYTES;
+            /*
+             * If |datalen| is a multiple of the blocksize, stash
+             * last complete block, it can be final one...
+             */
+            stashlen = stashlen ? stashlen : BLAKE2S_BLOCKBYTES;
+            datalen -= stashlen;
+            blake2s_compress(c, in, datalen);
+            in += datalen;
+            datalen = stashlen;
+        }
+    }
+
+    assert(datalen <= BLAKE2S_BLOCKBYTES);
+
+    memcpy(c->buf + c->buflen, in, datalen);
+    c->buflen += datalen; /* Be lazy, do not compress */
+
+    return 1;
+}
+
+/*
+ * Calculate the final hash and save it in md.
+ * Always returns 1.
+ */
+int BLAKE2s_Final(unsigned char *md, BLAKE2S_CTX *c)
+{
+    int i;
+
+    blake2s_set_lastblock(c);
+    /* Padding */
+    memset(c->buf + c->buflen, 0, sizeof(c->buf) - c->buflen);
+    blake2s_compress(c, c->buf, c->buflen);
+
+    /* Output full hash to temp buffer */
+    for (i = 0; i < 8; ++i) {
+        store32(md + sizeof(c->h[i]) * i, c->h[i]);
+    }
+
+    OPENSSL_cleanse(c, sizeof(BLAKE2S_CTX));
+    return 1;
+}
diff --git a/crypto/blake2/build.info b/crypto/blake2/build.info
new file mode 100644
index 000000000..0036f0848
--- /dev/null
+++ b/crypto/blake2/build.info
@@ -0,0 +1,3 @@
+LIBS=../../libcrypto
+SOURCE[../../libcrypto]=\
+        blake2b.c blake2s.c m_blake2b.c m_blake2s.c
diff --git a/crypto/blake2/m_blake2b.c b/crypto/blake2/m_blake2b.c
new file mode 100644
index 000000000..82c6f6bd8
--- /dev/null
+++ b/crypto/blake2/m_blake2b.c
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * Derived from the BLAKE2 reference implementation written by Samuel Neves.
+ * Copyright 2012, Samuel Neves 
+ * More information about the BLAKE2 hash function and its implementations
+ * can be found at https://blake2.net.
+ */
+
+#include "internal/cryptlib.h"
+
+#ifndef OPENSSL_NO_BLAKE2
+
+# include 
+# include 
+# include "blake2_locl.h"
+# include "internal/evp_int.h"
+
+static int init(EVP_MD_CTX *ctx)
+{
+    return BLAKE2b_Init(EVP_MD_CTX_md_data(ctx));
+}
+
+static int update(EVP_MD_CTX *ctx, const void *data, size_t count)
+{
+    return BLAKE2b_Update(EVP_MD_CTX_md_data(ctx), data, count);
+}
+
+static int final(EVP_MD_CTX *ctx, unsigned char *md)
+{
+    return BLAKE2b_Final(md, EVP_MD_CTX_md_data(ctx));
+}
+
+static const EVP_MD blake2b_md = {
+    NID_blake2b512,
+    0,
+    BLAKE2B_DIGEST_LENGTH,
+    0,
+    init,
+    update,
+    final,
+    NULL,
+    NULL,
+    BLAKE2B_BLOCKBYTES,
+    sizeof(EVP_MD *) + sizeof(BLAKE2B_CTX),
+};
+
+const EVP_MD *EVP_blake2b512(void)
+{
+    return (&blake2b_md);
+}
+#endif
diff --git a/crypto/blake2/m_blake2s.c b/crypto/blake2/m_blake2s.c
new file mode 100644
index 000000000..467e91a87
--- /dev/null
+++ b/crypto/blake2/m_blake2s.c
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * Derived from the BLAKE2 reference implementation written by Samuel Neves.
+ * Copyright 2012, Samuel Neves 
+ * More information about the BLAKE2 hash function and its implementations
+ * can be found at https://blake2.net.
+ */
+
+#include "internal/cryptlib.h"
+
+#ifndef OPENSSL_NO_BLAKE2
+
+# include 
+# include 
+# include "blake2_locl.h"
+# include "internal/evp_int.h"
+
+static int init(EVP_MD_CTX *ctx)
+{
+    return BLAKE2s_Init(EVP_MD_CTX_md_data(ctx));
+}
+
+static int update(EVP_MD_CTX *ctx, const void *data, size_t count)
+{
+    return BLAKE2s_Update(EVP_MD_CTX_md_data(ctx), data, count);
+}
+
+static int final(EVP_MD_CTX *ctx, unsigned char *md)
+{
+    return BLAKE2s_Final(md, EVP_MD_CTX_md_data(ctx));
+}
+
+static const EVP_MD blake2s_md = {
+    NID_blake2s256,
+    0,
+    BLAKE2S_DIGEST_LENGTH,
+    0,
+    init,
+    update,
+    final,
+    NULL,
+    NULL,
+    BLAKE2S_BLOCKBYTES,
+    sizeof(EVP_MD *) + sizeof(BLAKE2S_CTX),
+};
+
+const EVP_MD *EVP_blake2s256(void)
+{
+    return (&blake2s_md);
+}
+#endif
diff --git a/crypto/bn/asm/alpha-mont.pl b/crypto/bn/asm/alpha-mont.pl
new file mode 100644
index 000000000..1d68d6d07
--- /dev/null
+++ b/crypto/bn/asm/alpha-mont.pl
@@ -0,0 +1,331 @@
+#! /usr/bin/env perl
+# Copyright 2006-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+#
+# ====================================================================
+# Written by Andy Polyakov  for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+#
+# On 21264 RSA sign performance improves by 70/35/20/15 percent for
+# 512/1024/2048/4096 bit key lengths. This is against vendor compiler
+# instructed to '-tune host' code with in-line assembler. Other
+# benchmarks improve by 15-20%. To anchor it to something else, the
+# code provides approximately the same performance per GHz as AMD64.
+# I.e. if you compare 1GHz 21264 and 2GHz Opteron, you'll observe ~2x
+# difference.
+
+$output=pop;
+open STDOUT,">$output";
+
+# int bn_mul_mont(
+$rp="a0";	# BN_ULONG *rp,
+$ap="a1";	# const BN_ULONG *ap,
+$bp="a2";	# const BN_ULONG *bp,
+$np="a3";	# const BN_ULONG *np,
+$n0="a4";	# const BN_ULONG *n0,
+$num="a5";	# int num);
+
+$lo0="t0";
+$hi0="t1";
+$lo1="t2";
+$hi1="t3";
+$aj="t4";
+$bi="t5";
+$nj="t6";
+$tp="t7";
+$alo="t8";
+$ahi="t9";
+$nlo="t10";
+$nhi="t11";
+$tj="t12";
+$i="s3";
+$j="s4";
+$m1="s5";
+
+$code=<<___;
+#ifdef __linux__
+#include 
+#else
+#include 
+#include 
+#endif
+
+.text
+
+.set	noat
+.set	noreorder
+
+.globl	bn_mul_mont
+.align	5
+.ent	bn_mul_mont
+bn_mul_mont:
+	lda	sp,-48(sp)
+	stq	ra,0(sp)
+	stq	s3,8(sp)
+	stq	s4,16(sp)
+	stq	s5,24(sp)
+	stq	fp,32(sp)
+	mov	sp,fp
+	.mask	0x0400f000,-48
+	.frame	fp,48,ra
+	.prologue 0
+
+	.align	4
+	.set	reorder
+	sextl	$num,$num
+	mov	0,v0
+	cmplt	$num,4,AT
+	bne	AT,.Lexit
+
+	ldq	$hi0,0($ap)	# ap[0]
+	s8addq	$num,16,AT
+	ldq	$aj,8($ap)
+	subq	sp,AT,sp
+	ldq	$bi,0($bp)	# bp[0]
+	lda	AT,-4096(zero)	# mov	-4096,AT
+	ldq	$n0,0($n0)
+	and	sp,AT,sp
+
+	mulq	$hi0,$bi,$lo0
+	ldq	$hi1,0($np)	# np[0]
+	umulh	$hi0,$bi,$hi0
+	ldq	$nj,8($np)
+
+	mulq	$lo0,$n0,$m1
+
+	mulq	$hi1,$m1,$lo1
+	umulh	$hi1,$m1,$hi1
+
+	addq	$lo1,$lo0,$lo1
+	cmpult	$lo1,$lo0,AT
+	addq	$hi1,AT,$hi1
+
+	mulq	$aj,$bi,$alo
+	mov	2,$j
+	umulh	$aj,$bi,$ahi
+	mov	sp,$tp
+
+	mulq	$nj,$m1,$nlo
+	s8addq	$j,$ap,$aj
+	umulh	$nj,$m1,$nhi
+	s8addq	$j,$np,$nj
+.align	4
+.L1st:
+	.set	noreorder
+	ldq	$aj,0($aj)
+	addl	$j,1,$j
+	ldq	$nj,0($nj)
+	lda	$tp,8($tp)
+
+	addq	$alo,$hi0,$lo0
+	mulq	$aj,$bi,$alo
+	cmpult	$lo0,$hi0,AT
+	addq	$nlo,$hi1,$lo1
+
+	mulq	$nj,$m1,$nlo
+	addq	$ahi,AT,$hi0
+	cmpult	$lo1,$hi1,v0
+	cmplt	$j,$num,$tj
+
+	umulh	$aj,$bi,$ahi
+	addq	$nhi,v0,$hi1
+	addq	$lo1,$lo0,$lo1
+	s8addq	$j,$ap,$aj
+
+	umulh	$nj,$m1,$nhi
+	cmpult	$lo1,$lo0,v0
+	addq	$hi1,v0,$hi1
+	s8addq	$j,$np,$nj
+
+	stq	$lo1,-8($tp)
+	nop
+	unop
+	bne	$tj,.L1st
+	.set	reorder
+
+	addq	$alo,$hi0,$lo0
+	addq	$nlo,$hi1,$lo1
+	cmpult	$lo0,$hi0,AT
+	cmpult	$lo1,$hi1,v0
+	addq	$ahi,AT,$hi0
+	addq	$nhi,v0,$hi1
+
+	addq	$lo1,$lo0,$lo1
+	cmpult	$lo1,$lo0,v0
+	addq	$hi1,v0,$hi1
+
+	stq	$lo1,0($tp)
+
+	addq	$hi1,$hi0,$hi1
+	cmpult	$hi1,$hi0,AT
+	stq	$hi1,8($tp)
+	stq	AT,16($tp)
+
+	mov	1,$i
+.align	4
+.Louter:
+	s8addq	$i,$bp,$bi
+	ldq	$hi0,0($ap)
+	ldq	$aj,8($ap)
+	ldq	$bi,0($bi)
+	ldq	$hi1,0($np)
+	ldq	$nj,8($np)
+	ldq	$tj,0(sp)
+
+	mulq	$hi0,$bi,$lo0
+	umulh	$hi0,$bi,$hi0
+
+	addq	$lo0,$tj,$lo0
+	cmpult	$lo0,$tj,AT
+	addq	$hi0,AT,$hi0
+
+	mulq	$lo0,$n0,$m1
+
+	mulq	$hi1,$m1,$lo1
+	umulh	$hi1,$m1,$hi1
+
+	addq	$lo1,$lo0,$lo1
+	cmpult	$lo1,$lo0,AT
+	mov	2,$j
+	addq	$hi1,AT,$hi1
+
+	mulq	$aj,$bi,$alo
+	mov	sp,$tp
+	umulh	$aj,$bi,$ahi
+
+	mulq	$nj,$m1,$nlo
+	s8addq	$j,$ap,$aj
+	umulh	$nj,$m1,$nhi
+.align	4
+.Linner:
+	.set	noreorder
+	ldq	$tj,8($tp)	#L0
+	nop			#U1
+	ldq	$aj,0($aj)	#L1
+	s8addq	$j,$np,$nj	#U0
+
+	ldq	$nj,0($nj)	#L0
+	nop			#U1
+	addq	$alo,$hi0,$lo0	#L1
+	lda	$tp,8($tp)
+
+	mulq	$aj,$bi,$alo	#U1
+	cmpult	$lo0,$hi0,AT	#L0
+	addq	$nlo,$hi1,$lo1	#L1
+	addl	$j,1,$j
+
+	mulq	$nj,$m1,$nlo	#U1
+	addq	$ahi,AT,$hi0	#L0
+	addq	$lo0,$tj,$lo0	#L1
+	cmpult	$lo1,$hi1,v0	#U0
+
+	umulh	$aj,$bi,$ahi	#U1
+	cmpult	$lo0,$tj,AT	#L0
+	addq	$lo1,$lo0,$lo1	#L1
+	addq	$nhi,v0,$hi1	#U0
+
+	umulh	$nj,$m1,$nhi	#U1
+	s8addq	$j,$ap,$aj	#L0
+	cmpult	$lo1,$lo0,v0	#L1
+	cmplt	$j,$num,$tj	#U0	# borrow $tj
+
+	addq	$hi0,AT,$hi0	#L0
+	addq	$hi1,v0,$hi1	#U1
+	stq	$lo1,-8($tp)	#L1
+	bne	$tj,.Linner	#U0
+	.set	reorder
+
+	ldq	$tj,8($tp)
+	addq	$alo,$hi0,$lo0
+	addq	$nlo,$hi1,$lo1
+	cmpult	$lo0,$hi0,AT
+	cmpult	$lo1,$hi1,v0
+	addq	$ahi,AT,$hi0
+	addq	$nhi,v0,$hi1
+
+	addq	$lo0,$tj,$lo0
+	cmpult	$lo0,$tj,AT
+	addq	$hi0,AT,$hi0
+
+	ldq	$tj,16($tp)
+	addq	$lo1,$lo0,$j
+	cmpult	$j,$lo0,v0
+	addq	$hi1,v0,$hi1
+
+	addq	$hi1,$hi0,$lo1
+	stq	$j,0($tp)
+	cmpult	$lo1,$hi0,$hi1
+	addq	$lo1,$tj,$lo1
+	cmpult	$lo1,$tj,AT
+	addl	$i,1,$i
+	addq	$hi1,AT,$hi1
+	stq	$lo1,8($tp)
+	cmplt	$i,$num,$tj	# borrow $tj
+	stq	$hi1,16($tp)
+	bne	$tj,.Louter
+
+	s8addq	$num,sp,$tj	# &tp[num]
+	mov	$rp,$bp		# put rp aside
+	mov	sp,$tp
+	mov	sp,$ap
+	mov	0,$hi0		# clear borrow bit
+
+.align	4
+.Lsub:	ldq	$lo0,0($tp)
+	ldq	$lo1,0($np)
+	lda	$tp,8($tp)
+	lda	$np,8($np)
+	subq	$lo0,$lo1,$lo1	# tp[i]-np[i]
+	cmpult	$lo0,$lo1,AT
+	subq	$lo1,$hi0,$lo0
+	cmpult	$lo1,$lo0,$hi0
+	or	$hi0,AT,$hi0
+	stq	$lo0,0($rp)
+	cmpult	$tp,$tj,v0
+	lda	$rp,8($rp)
+	bne	v0,.Lsub
+
+	subq	$hi1,$hi0,$hi0	# handle upmost overflow bit
+	mov	sp,$tp
+	mov	$bp,$rp		# restore rp
+
+	and	sp,$hi0,$ap
+	bic	$bp,$hi0,$bp
+	bis	$bp,$ap,$ap	# ap=borrow?tp:rp
+
+.align	4
+.Lcopy:	ldq	$aj,0($ap)	# copy or in-place refresh
+	lda	$tp,8($tp)
+	lda	$rp,8($rp)
+	lda	$ap,8($ap)
+	stq	zero,-8($tp)	# zap tp
+	cmpult	$tp,$tj,AT
+	stq	$aj,-8($rp)
+	bne	AT,.Lcopy
+	mov	1,v0
+
+.Lexit:
+	.set	noreorder
+	mov	fp,sp
+	/*ldq	ra,0(sp)*/
+	ldq	s3,8(sp)
+	ldq	s4,16(sp)
+	ldq	s5,24(sp)
+	ldq	fp,32(sp)
+	lda	sp,48(sp)
+	ret	(ra)
+.end	bn_mul_mont
+.ascii	"Montgomery Multiplication for Alpha, CRYPTOGAMS by "
+.align	2
+___
+
+print $code;
+close STDOUT;
diff --git a/crypto/bn/asm/armv4-gf2m.pl b/crypto/bn/asm/armv4-gf2m.pl
new file mode 100644
index 000000000..0bb543307
--- /dev/null
+++ b/crypto/bn/asm/armv4-gf2m.pl
@@ -0,0 +1,332 @@
+#! /usr/bin/env perl
+# Copyright 2011-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+#
+# ====================================================================
+# Written by Andy Polyakov  for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+#
+# May 2011
+#
+# The module implements bn_GF2m_mul_2x2 polynomial multiplication
+# used in bn_gf2m.c. It's kind of low-hanging mechanical port from
+# C for the time being... Except that it has two code paths: pure
+# integer code suitable for any ARMv4 and later CPU and NEON code
+# suitable for ARMv7. Pure integer 1x1 multiplication subroutine runs
+# in ~45 cycles on dual-issue core such as Cortex A8, which is ~50%
+# faster than compiler-generated code. For ECDH and ECDSA verify (but
+# not for ECDSA sign) it means 25%-45% improvement depending on key
+# length, more for longer keys. Even though NEON 1x1 multiplication
+# runs in even less cycles, ~30, improvement is measurable only on
+# longer keys. One has to optimize code elsewhere to get NEON glow...
+#
+# April 2014
+#
+# Double bn_GF2m_mul_2x2 performance by using algorithm from paper
+# referred below, which improves ECDH and ECDSA verify benchmarks
+# by 18-40%.
+#
+# Câmara, D.; Gouvêa, C. P. L.; López, J. & Dahab, R.: Fast Software
+# Polynomial Multiplication on ARM Processors using the NEON Engine.
+# 
+# http://conradoplg.cryptoland.net/files/2010/12/mocrysen13.pdf
+
+$flavour = shift;
+if ($flavour=~/\w[\w\-]*\.\w+$/) { $output=$flavour; undef $flavour; }
+else { while (($output=shift) && ($output!~/\w[\w\-]*\.\w+$/)) {} }
+
+if ($flavour && $flavour ne "void") {
+    $0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+    ( $xlate="${dir}arm-xlate.pl" and -f $xlate ) or
+    ( $xlate="${dir}../../perlasm/arm-xlate.pl" and -f $xlate) or
+    die "can't locate arm-xlate.pl";
+
+    open STDOUT,"| \"$^X\" $xlate $flavour $output";
+} else {
+    open STDOUT,">$output";
+}
+
+$code=<<___;
+#include "arm_arch.h"
+
+.text
+#if defined(__thumb2__)
+.syntax	unified
+.thumb
+#else
+.code	32
+#endif
+___
+################
+# private interface to mul_1x1_ialu
+#
+$a="r1";
+$b="r0";
+
+($a0,$a1,$a2,$a12,$a4,$a14)=
+($hi,$lo,$t0,$t1, $i0,$i1 )=map("r$_",(4..9),12);
+
+$mask="r12";
+
+$code.=<<___;
+.type	mul_1x1_ialu,%function
+.align	5
+mul_1x1_ialu:
+	mov	$a0,#0
+	bic	$a1,$a,#3<<30		@ a1=a&0x3fffffff
+	str	$a0,[sp,#0]		@ tab[0]=0
+	add	$a2,$a1,$a1		@ a2=a1<<1
+	str	$a1,[sp,#4]		@ tab[1]=a1
+	eor	$a12,$a1,$a2		@ a1^a2
+	str	$a2,[sp,#8]		@ tab[2]=a2
+	mov	$a4,$a1,lsl#2		@ a4=a1<<2
+	str	$a12,[sp,#12]		@ tab[3]=a1^a2
+	eor	$a14,$a1,$a4		@ a1^a4
+	str	$a4,[sp,#16]		@ tab[4]=a4
+	eor	$a0,$a2,$a4		@ a2^a4
+	str	$a14,[sp,#20]		@ tab[5]=a1^a4
+	eor	$a12,$a12,$a4		@ a1^a2^a4
+	str	$a0,[sp,#24]		@ tab[6]=a2^a4
+	and	$i0,$mask,$b,lsl#2
+	str	$a12,[sp,#28]		@ tab[7]=a1^a2^a4
+
+	and	$i1,$mask,$b,lsr#1
+	ldr	$lo,[sp,$i0]		@ tab[b       & 0x7]
+	and	$i0,$mask,$b,lsr#4
+	ldr	$t1,[sp,$i1]		@ tab[b >>  3 & 0x7]
+	and	$i1,$mask,$b,lsr#7
+	ldr	$t0,[sp,$i0]		@ tab[b >>  6 & 0x7]
+	eor	$lo,$lo,$t1,lsl#3	@ stall
+	mov	$hi,$t1,lsr#29
+	ldr	$t1,[sp,$i1]		@ tab[b >>  9 & 0x7]
+
+	and	$i0,$mask,$b,lsr#10
+	eor	$lo,$lo,$t0,lsl#6
+	eor	$hi,$hi,$t0,lsr#26
+	ldr	$t0,[sp,$i0]		@ tab[b >> 12 & 0x7]
+
+	and	$i1,$mask,$b,lsr#13
+	eor	$lo,$lo,$t1,lsl#9
+	eor	$hi,$hi,$t1,lsr#23
+	ldr	$t1,[sp,$i1]		@ tab[b >> 15 & 0x7]
+
+	and	$i0,$mask,$b,lsr#16
+	eor	$lo,$lo,$t0,lsl#12
+	eor	$hi,$hi,$t0,lsr#20
+	ldr	$t0,[sp,$i0]		@ tab[b >> 18 & 0x7]
+
+	and	$i1,$mask,$b,lsr#19
+	eor	$lo,$lo,$t1,lsl#15
+	eor	$hi,$hi,$t1,lsr#17
+	ldr	$t1,[sp,$i1]		@ tab[b >> 21 & 0x7]
+
+	and	$i0,$mask,$b,lsr#22
+	eor	$lo,$lo,$t0,lsl#18
+	eor	$hi,$hi,$t0,lsr#14
+	ldr	$t0,[sp,$i0]		@ tab[b >> 24 & 0x7]
+
+	and	$i1,$mask,$b,lsr#25
+	eor	$lo,$lo,$t1,lsl#21
+	eor	$hi,$hi,$t1,lsr#11
+	ldr	$t1,[sp,$i1]		@ tab[b >> 27 & 0x7]
+
+	tst	$a,#1<<30
+	and	$i0,$mask,$b,lsr#28
+	eor	$lo,$lo,$t0,lsl#24
+	eor	$hi,$hi,$t0,lsr#8
+	ldr	$t0,[sp,$i0]		@ tab[b >> 30      ]
+
+#ifdef	__thumb2__
+	itt	ne
+#endif
+	eorne	$lo,$lo,$b,lsl#30
+	eorne	$hi,$hi,$b,lsr#2
+	tst	$a,#1<<31
+	eor	$lo,$lo,$t1,lsl#27
+	eor	$hi,$hi,$t1,lsr#5
+#ifdef	__thumb2__
+	itt	ne
+#endif
+	eorne	$lo,$lo,$b,lsl#31
+	eorne	$hi,$hi,$b,lsr#1
+	eor	$lo,$lo,$t0,lsl#30
+	eor	$hi,$hi,$t0,lsr#2
+
+	mov	pc,lr
+.size	mul_1x1_ialu,.-mul_1x1_ialu
+___
+################
+# void	bn_GF2m_mul_2x2(BN_ULONG *r,
+#	BN_ULONG a1,BN_ULONG a0,
+#	BN_ULONG b1,BN_ULONG b0);	# r[3..0]=a1a0·b1b0
+{
+$code.=<<___;
+.global	bn_GF2m_mul_2x2
+.type	bn_GF2m_mul_2x2,%function
+.align	5
+bn_GF2m_mul_2x2:
+#if __ARM_MAX_ARCH__>=7
+	stmdb	sp!,{r10,lr}
+	ldr	r12,.LOPENSSL_armcap
+	adr	r10,.LOPENSSL_armcap
+	ldr	r12,[r12,r10]
+#ifdef	__APPLE__
+	ldr	r12,[r12]
+#endif
+	tst	r12,#ARMV7_NEON
+	itt	ne
+	ldrne	r10,[sp],#8
+	bne	.LNEON
+	stmdb	sp!,{r4-r9}
+#else
+	stmdb	sp!,{r4-r10,lr}
+#endif
+___
+$ret="r10";	# reassigned 1st argument
+$code.=<<___;
+	mov	$ret,r0			@ reassign 1st argument
+	mov	$b,r3			@ $b=b1
+	sub	r7,sp,#36
+	mov	r8,sp
+	and	r7,r7,#-32
+	ldr	r3,[sp,#32]		@ load b0
+	mov	$mask,#7<<2
+	mov	sp,r7			@ allocate tab[8]
+	str	r8,[r7,#32]
+
+	bl	mul_1x1_ialu		@ a1·b1
+	str	$lo,[$ret,#8]
+	str	$hi,[$ret,#12]
+
+	eor	$b,$b,r3		@ flip b0 and b1
+	 eor	$a,$a,r2		@ flip a0 and a1
+	eor	r3,r3,$b
+	 eor	r2,r2,$a
+	eor	$b,$b,r3
+	 eor	$a,$a,r2
+	bl	mul_1x1_ialu		@ a0·b0
+	str	$lo,[$ret]
+	str	$hi,[$ret,#4]
+
+	eor	$a,$a,r2
+	eor	$b,$b,r3
+	bl	mul_1x1_ialu		@ (a1+a0)·(b1+b0)
+___
+@r=map("r$_",(6..9));
+$code.=<<___;
+	ldmia	$ret,{@r[0]-@r[3]}
+	eor	$lo,$lo,$hi
+	ldr	sp,[sp,#32]		@ destroy tab[8]
+	eor	$hi,$hi,@r[1]
+	eor	$lo,$lo,@r[0]
+	eor	$hi,$hi,@r[2]
+	eor	$lo,$lo,@r[3]
+	eor	$hi,$hi,@r[3]
+	str	$hi,[$ret,#8]
+	eor	$lo,$lo,$hi
+	str	$lo,[$ret,#4]
+
+#if __ARM_ARCH__>=5
+	ldmia	sp!,{r4-r10,pc}
+#else
+	ldmia	sp!,{r4-r10,lr}
+	tst	lr,#1
+	moveq	pc,lr			@ be binary compatible with V4, yet
+	bx	lr			@ interoperable with Thumb ISA:-)
+#endif
+___
+}
+{
+my ($r,$t0,$t1,$t2,$t3)=map("q$_",(0..3,8..12));
+my ($a,$b,$k48,$k32,$k16)=map("d$_",(26..31));
+
+$code.=<<___;
+#if __ARM_MAX_ARCH__>=7
+.arch	armv7-a
+.fpu	neon
+
+.align	5
+.LNEON:
+	ldr		r12, [sp]		@ 5th argument
+	vmov		$a, r2, r1
+	vmov		$b, r12, r3
+	vmov.i64	$k48, #0x0000ffffffffffff
+	vmov.i64	$k32, #0x00000000ffffffff
+	vmov.i64	$k16, #0x000000000000ffff
+
+	vext.8		$t0#lo, $a, $a, #1	@ A1
+	vmull.p8	$t0, $t0#lo, $b		@ F = A1*B
+	vext.8		$r#lo, $b, $b, #1	@ B1
+	vmull.p8	$r, $a, $r#lo		@ E = A*B1
+	vext.8		$t1#lo, $a, $a, #2	@ A2
+	vmull.p8	$t1, $t1#lo, $b		@ H = A2*B
+	vext.8		$t3#lo, $b, $b, #2	@ B2
+	vmull.p8	$t3, $a, $t3#lo		@ G = A*B2
+	vext.8		$t2#lo, $a, $a, #3	@ A3
+	veor		$t0, $t0, $r		@ L = E + F
+	vmull.p8	$t2, $t2#lo, $b		@ J = A3*B
+	vext.8		$r#lo, $b, $b, #3	@ B3
+	veor		$t1, $t1, $t3		@ M = G + H
+	vmull.p8	$r, $a, $r#lo		@ I = A*B3
+	veor		$t0#lo, $t0#lo, $t0#hi	@ t0 = (L) (P0 + P1) << 8
+	vand		$t0#hi, $t0#hi, $k48
+	vext.8		$t3#lo, $b, $b, #4	@ B4
+	veor		$t1#lo, $t1#lo, $t1#hi	@ t1 = (M) (P2 + P3) << 16
+	vand		$t1#hi, $t1#hi, $k32
+	vmull.p8	$t3, $a, $t3#lo		@ K = A*B4
+	veor		$t2, $t2, $r		@ N = I + J
+	veor		$t0#lo, $t0#lo, $t0#hi
+	veor		$t1#lo, $t1#lo, $t1#hi
+	veor		$t2#lo, $t2#lo, $t2#hi	@ t2 = (N) (P4 + P5) << 24
+	vand		$t2#hi, $t2#hi, $k16
+	vext.8		$t0, $t0, $t0, #15
+	veor		$t3#lo, $t3#lo, $t3#hi	@ t3 = (K) (P6 + P7) << 32
+	vmov.i64	$t3#hi, #0
+	vext.8		$t1, $t1, $t1, #14
+	veor		$t2#lo, $t2#lo, $t2#hi
+	vmull.p8	$r, $a, $b		@ D = A*B
+	vext.8		$t3, $t3, $t3, #12
+	vext.8		$t2, $t2, $t2, #13
+	veor		$t0, $t0, $t1
+	veor		$t2, $t2, $t3
+	veor		$r, $r, $t0
+	veor		$r, $r, $t2
+
+	vst1.32		{$r}, [r0]
+	ret		@ bx lr
+#endif
+___
+}
+$code.=<<___;
+.size	bn_GF2m_mul_2x2,.-bn_GF2m_mul_2x2
+#if __ARM_MAX_ARCH__>=7
+.align	5
+.LOPENSSL_armcap:
+.word	OPENSSL_armcap_P-.
+#endif
+.asciz	"GF(2^m) Multiplication for ARMv4/NEON, CRYPTOGAMS by "
+.align	5
+
+#if __ARM_MAX_ARCH__>=7
+.comm	OPENSSL_armcap_P,4,4
+#endif
+___
+
+foreach (split("\n",$code)) {
+	s/\`([^\`]*)\`/eval $1/geo;
+
+	s/\bq([0-9]+)#(lo|hi)/sprintf "d%d",2*$1+($2 eq "hi")/geo	or
+	s/\bret\b/bx	lr/go		or
+	s/\bbx\s+lr\b/.word\t0xe12fff1e/go;    # make it possible to compile with -march=armv4
+
+	print $_,"\n";
+}
+close STDOUT;   # enforce flush
diff --git a/crypto/bn/asm/armv4-mont.pl b/crypto/bn/asm/armv4-mont.pl
new file mode 100644
index 000000000..0dc4fe95e
--- /dev/null
+++ b/crypto/bn/asm/armv4-mont.pl
@@ -0,0 +1,756 @@
+#! /usr/bin/env perl
+# Copyright 2007-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+
+# ====================================================================
+# Written by Andy Polyakov  for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+
+# January 2007.
+
+# Montgomery multiplication for ARMv4.
+#
+# Performance improvement naturally varies among CPU implementations
+# and compilers. The code was observed to provide +65-35% improvement
+# [depending on key length, less for longer keys] on ARM920T, and
+# +115-80% on Intel IXP425. This is compared to pre-bn_mul_mont code
+# base and compiler generated code with in-lined umull and even umlal
+# instructions. The latter means that this code didn't really have an 
+# "advantage" of utilizing some "secret" instruction.
+#
+# The code is interoperable with Thumb ISA and is rather compact, less
+# than 1/2KB. Windows CE port would be trivial, as it's exclusively
+# about decorations, ABI and instruction syntax are identical.
+
+# November 2013
+#
+# Add NEON code path, which handles lengths divisible by 8. RSA/DSA
+# performance improvement on Cortex-A8 is ~45-100% depending on key
+# length, more for longer keys. On Cortex-A15 the span is ~10-105%.
+# On Snapdragon S4 improvement was measured to vary from ~70% to
+# incredible ~380%, yes, 4.8x faster, for RSA4096 sign. But this is
+# rather because original integer-only code seems to perform
+# suboptimally on S4. Situation on Cortex-A9 is unfortunately
+# different. It's being looked into, but the trouble is that
+# performance for vectors longer than 256 bits is actually couple
+# of percent worse than for integer-only code. The code is chosen
+# for execution on all NEON-capable processors, because gain on
+# others outweighs the marginal loss on Cortex-A9.
+
+# September 2015
+#
+# Align Cortex-A9 performance with November 2013 improvements, i.e.
+# NEON code is now ~20-105% faster than integer-only one on this
+# processor. But this optimization further improved performance even
+# on other processors: NEON code path is ~45-180% faster than original
+# integer-only on Cortex-A8, ~10-210% on Cortex-A15, ~70-450% on
+# Snapdragon S4.
+
+$flavour = shift;
+if ($flavour=~/\w[\w\-]*\.\w+$/) { $output=$flavour; undef $flavour; }
+else { while (($output=shift) && ($output!~/\w[\w\-]*\.\w+$/)) {} }
+
+if ($flavour && $flavour ne "void") {
+    $0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+    ( $xlate="${dir}arm-xlate.pl" and -f $xlate ) or
+    ( $xlate="${dir}../../perlasm/arm-xlate.pl" and -f $xlate) or
+    die "can't locate arm-xlate.pl";
+
+    open STDOUT,"| \"$^X\" $xlate $flavour $output";
+} else {
+    open STDOUT,">$output";
+}
+
+$num="r0";	# starts as num argument, but holds &tp[num-1]
+$ap="r1";
+$bp="r2"; $bi="r2"; $rp="r2";
+$np="r3";
+$tp="r4";
+$aj="r5";
+$nj="r6";
+$tj="r7";
+$n0="r8";
+###########	# r9 is reserved by ELF as platform specific, e.g. TLS pointer
+$alo="r10";	# sl, gcc uses it to keep @GOT
+$ahi="r11";	# fp
+$nlo="r12";	# ip
+###########	# r13 is stack pointer
+$nhi="r14";	# lr
+###########	# r15 is program counter
+
+#### argument block layout relative to &tp[num-1], a.k.a. $num
+$_rp="$num,#12*4";
+# ap permanently resides in r1
+$_bp="$num,#13*4";
+# np permanently resides in r3
+$_n0="$num,#14*4";
+$_num="$num,#15*4";	$_bpend=$_num;
+
+$code=<<___;
+#include "arm_arch.h"
+
+.text
+#if defined(__thumb2__)
+.syntax	unified
+.thumb
+#else
+.code	32
+#endif
+
+#if __ARM_MAX_ARCH__>=7
+.align	5
+.LOPENSSL_armcap:
+.word	OPENSSL_armcap_P-.Lbn_mul_mont
+#endif
+
+.global	bn_mul_mont
+.type	bn_mul_mont,%function
+
+.align	5
+bn_mul_mont:
+.Lbn_mul_mont:
+	ldr	ip,[sp,#4]		@ load num
+	stmdb	sp!,{r0,r2}		@ sp points at argument block
+#if __ARM_MAX_ARCH__>=7
+	tst	ip,#7
+	bne	.Lialu
+	adr	r0,.Lbn_mul_mont
+	ldr	r2,.LOPENSSL_armcap
+	ldr	r0,[r0,r2]
+#ifdef	__APPLE__
+	ldr	r0,[r0]
+#endif
+	tst	r0,#ARMV7_NEON		@ NEON available?
+	ldmia	sp, {r0,r2}
+	beq	.Lialu
+	add	sp,sp,#8
+	b	bn_mul8x_mont_neon
+.align	4
+.Lialu:
+#endif
+	cmp	ip,#2
+	mov	$num,ip			@ load num
+#ifdef	__thumb2__
+	ittt	lt
+#endif
+	movlt	r0,#0
+	addlt	sp,sp,#2*4
+	blt	.Labrt
+
+	stmdb	sp!,{r4-r12,lr}		@ save 10 registers
+
+	mov	$num,$num,lsl#2		@ rescale $num for byte count
+	sub	sp,sp,$num		@ alloca(4*num)
+	sub	sp,sp,#4		@ +extra dword
+	sub	$num,$num,#4		@ "num=num-1"
+	add	$tp,$bp,$num		@ &bp[num-1]
+
+	add	$num,sp,$num		@ $num to point at &tp[num-1]
+	ldr	$n0,[$_n0]		@ &n0
+	ldr	$bi,[$bp]		@ bp[0]
+	ldr	$aj,[$ap],#4		@ ap[0],ap++
+	ldr	$nj,[$np],#4		@ np[0],np++
+	ldr	$n0,[$n0]		@ *n0
+	str	$tp,[$_bpend]		@ save &bp[num]
+
+	umull	$alo,$ahi,$aj,$bi	@ ap[0]*bp[0]
+	str	$n0,[$_n0]		@ save n0 value
+	mul	$n0,$alo,$n0		@ "tp[0]"*n0
+	mov	$nlo,#0
+	umlal	$alo,$nlo,$nj,$n0	@ np[0]*n0+"t[0]"
+	mov	$tp,sp
+
+.L1st:
+	ldr	$aj,[$ap],#4		@ ap[j],ap++
+	mov	$alo,$ahi
+	ldr	$nj,[$np],#4		@ np[j],np++
+	mov	$ahi,#0
+	umlal	$alo,$ahi,$aj,$bi	@ ap[j]*bp[0]
+	mov	$nhi,#0
+	umlal	$nlo,$nhi,$nj,$n0	@ np[j]*n0
+	adds	$nlo,$nlo,$alo
+	str	$nlo,[$tp],#4		@ tp[j-1]=,tp++
+	adc	$nlo,$nhi,#0
+	cmp	$tp,$num
+	bne	.L1st
+
+	adds	$nlo,$nlo,$ahi
+	ldr	$tp,[$_bp]		@ restore bp
+	mov	$nhi,#0
+	ldr	$n0,[$_n0]		@ restore n0
+	adc	$nhi,$nhi,#0
+	str	$nlo,[$num]		@ tp[num-1]=
+	mov	$tj,sp
+	str	$nhi,[$num,#4]		@ tp[num]=
+
+.Louter:
+	sub	$tj,$num,$tj		@ "original" $num-1 value
+	sub	$ap,$ap,$tj		@ "rewind" ap to &ap[1]
+	ldr	$bi,[$tp,#4]!		@ *(++bp)
+	sub	$np,$np,$tj		@ "rewind" np to &np[1]
+	ldr	$aj,[$ap,#-4]		@ ap[0]
+	ldr	$alo,[sp]		@ tp[0]
+	ldr	$nj,[$np,#-4]		@ np[0]
+	ldr	$tj,[sp,#4]		@ tp[1]
+
+	mov	$ahi,#0
+	umlal	$alo,$ahi,$aj,$bi	@ ap[0]*bp[i]+tp[0]
+	str	$tp,[$_bp]		@ save bp
+	mul	$n0,$alo,$n0
+	mov	$nlo,#0
+	umlal	$alo,$nlo,$nj,$n0	@ np[0]*n0+"tp[0]"
+	mov	$tp,sp
+
+.Linner:
+	ldr	$aj,[$ap],#4		@ ap[j],ap++
+	adds	$alo,$ahi,$tj		@ +=tp[j]
+	ldr	$nj,[$np],#4		@ np[j],np++
+	mov	$ahi,#0
+	umlal	$alo,$ahi,$aj,$bi	@ ap[j]*bp[i]
+	mov	$nhi,#0
+	umlal	$nlo,$nhi,$nj,$n0	@ np[j]*n0
+	adc	$ahi,$ahi,#0
+	ldr	$tj,[$tp,#8]		@ tp[j+1]
+	adds	$nlo,$nlo,$alo
+	str	$nlo,[$tp],#4		@ tp[j-1]=,tp++
+	adc	$nlo,$nhi,#0
+	cmp	$tp,$num
+	bne	.Linner
+
+	adds	$nlo,$nlo,$ahi
+	mov	$nhi,#0
+	ldr	$tp,[$_bp]		@ restore bp
+	adc	$nhi,$nhi,#0
+	ldr	$n0,[$_n0]		@ restore n0
+	adds	$nlo,$nlo,$tj
+	ldr	$tj,[$_bpend]		@ restore &bp[num]
+	adc	$nhi,$nhi,#0
+	str	$nlo,[$num]		@ tp[num-1]=
+	str	$nhi,[$num,#4]		@ tp[num]=
+
+	cmp	$tp,$tj
+#ifdef	__thumb2__
+	itt	ne
+#endif
+	movne	$tj,sp
+	bne	.Louter
+
+	ldr	$rp,[$_rp]		@ pull rp
+	mov	$aj,sp
+	add	$num,$num,#4		@ $num to point at &tp[num]
+	sub	$aj,$num,$aj		@ "original" num value
+	mov	$tp,sp			@ "rewind" $tp
+	mov	$ap,$tp			@ "borrow" $ap
+	sub	$np,$np,$aj		@ "rewind" $np to &np[0]
+
+	subs	$tj,$tj,$tj		@ "clear" carry flag
+.Lsub:	ldr	$tj,[$tp],#4
+	ldr	$nj,[$np],#4
+	sbcs	$tj,$tj,$nj		@ tp[j]-np[j]
+	str	$tj,[$rp],#4		@ rp[j]=
+	teq	$tp,$num		@ preserve carry
+	bne	.Lsub
+	sbcs	$nhi,$nhi,#0		@ upmost carry
+	mov	$tp,sp			@ "rewind" $tp
+	sub	$rp,$rp,$aj		@ "rewind" $rp
+
+	and	$ap,$tp,$nhi
+	bic	$np,$rp,$nhi
+	orr	$ap,$ap,$np		@ ap=borrow?tp:rp
+
+.Lcopy:	ldr	$tj,[$ap],#4		@ copy or in-place refresh
+	str	sp,[$tp],#4		@ zap tp
+	str	$tj,[$rp],#4
+	cmp	$tp,$num
+	bne	.Lcopy
+
+	mov	sp,$num
+	add	sp,sp,#4		@ skip over tp[num+1]
+	ldmia	sp!,{r4-r12,lr}		@ restore registers
+	add	sp,sp,#2*4		@ skip over {r0,r2}
+	mov	r0,#1
+.Labrt:
+#if __ARM_ARCH__>=5
+	ret				@ bx lr
+#else
+	tst	lr,#1
+	moveq	pc,lr			@ be binary compatible with V4, yet
+	bx	lr			@ interoperable with Thumb ISA:-)
+#endif
+.size	bn_mul_mont,.-bn_mul_mont
+___
+{
+my ($A0,$A1,$A2,$A3)=map("d$_",(0..3));
+my ($N0,$N1,$N2,$N3)=map("d$_",(4..7));
+my ($Z,$Temp)=("q4","q5");
+my @ACC=map("q$_",(6..13));
+my ($Bi,$Ni,$M0)=map("d$_",(28..31));
+my $zero="$Z#lo";
+my $temp="$Temp#lo";
+
+my ($rptr,$aptr,$bptr,$nptr,$n0,$num)=map("r$_",(0..5));
+my ($tinptr,$toutptr,$inner,$outer,$bnptr)=map("r$_",(6..11));
+
+$code.=<<___;
+#if __ARM_MAX_ARCH__>=7
+.arch	armv7-a
+.fpu	neon
+
+.type	bn_mul8x_mont_neon,%function
+.align	5
+bn_mul8x_mont_neon:
+	mov	ip,sp
+	stmdb	sp!,{r4-r11}
+	vstmdb	sp!,{d8-d15}		@ ABI specification says so
+	ldmia	ip,{r4-r5}		@ load rest of parameter block
+	mov	ip,sp
+
+	cmp	$num,#8
+	bhi	.LNEON_8n
+
+	@ special case for $num==8, everything is in register bank...
+
+	vld1.32		{${Bi}[0]}, [$bptr,:32]!
+	veor		$zero,$zero,$zero
+	sub		$toutptr,sp,$num,lsl#4
+	vld1.32		{$A0-$A3},  [$aptr]!		@ can't specify :32 :-(
+	and		$toutptr,$toutptr,#-64
+	vld1.32		{${M0}[0]}, [$n0,:32]
+	mov		sp,$toutptr			@ alloca
+	vzip.16		$Bi,$zero
+
+	vmull.u32	@ACC[0],$Bi,${A0}[0]
+	vmull.u32	@ACC[1],$Bi,${A0}[1]
+	vmull.u32	@ACC[2],$Bi,${A1}[0]
+	vshl.i64	$Ni,@ACC[0]#hi,#16
+	vmull.u32	@ACC[3],$Bi,${A1}[1]
+
+	vadd.u64	$Ni,$Ni,@ACC[0]#lo
+	veor		$zero,$zero,$zero
+	vmul.u32	$Ni,$Ni,$M0
+
+	vmull.u32	@ACC[4],$Bi,${A2}[0]
+	 vld1.32	{$N0-$N3}, [$nptr]!
+	vmull.u32	@ACC[5],$Bi,${A2}[1]
+	vmull.u32	@ACC[6],$Bi,${A3}[0]
+	vzip.16		$Ni,$zero
+	vmull.u32	@ACC[7],$Bi,${A3}[1]
+
+	vmlal.u32	@ACC[0],$Ni,${N0}[0]
+	sub		$outer,$num,#1
+	vmlal.u32	@ACC[1],$Ni,${N0}[1]
+	vmlal.u32	@ACC[2],$Ni,${N1}[0]
+	vmlal.u32	@ACC[3],$Ni,${N1}[1]
+
+	vmlal.u32	@ACC[4],$Ni,${N2}[0]
+	vmov		$Temp,@ACC[0]
+	vmlal.u32	@ACC[5],$Ni,${N2}[1]
+	vmov		@ACC[0],@ACC[1]
+	vmlal.u32	@ACC[6],$Ni,${N3}[0]
+	vmov		@ACC[1],@ACC[2]
+	vmlal.u32	@ACC[7],$Ni,${N3}[1]
+	vmov		@ACC[2],@ACC[3]
+	vmov		@ACC[3],@ACC[4]
+	vshr.u64	$temp,$temp,#16
+	vmov		@ACC[4],@ACC[5]
+	vmov		@ACC[5],@ACC[6]
+	vadd.u64	$temp,$temp,$Temp#hi
+	vmov		@ACC[6],@ACC[7]
+	veor		@ACC[7],@ACC[7]
+	vshr.u64	$temp,$temp,#16
+
+	b	.LNEON_outer8
+
+.align	4
+.LNEON_outer8:
+	vld1.32		{${Bi}[0]}, [$bptr,:32]!
+	veor		$zero,$zero,$zero
+	vzip.16		$Bi,$zero
+	vadd.u64	@ACC[0]#lo,@ACC[0]#lo,$temp
+
+	vmlal.u32	@ACC[0],$Bi,${A0}[0]
+	vmlal.u32	@ACC[1],$Bi,${A0}[1]
+	vmlal.u32	@ACC[2],$Bi,${A1}[0]
+	vshl.i64	$Ni,@ACC[0]#hi,#16
+	vmlal.u32	@ACC[3],$Bi,${A1}[1]
+
+	vadd.u64	$Ni,$Ni,@ACC[0]#lo
+	veor		$zero,$zero,$zero
+	subs		$outer,$outer,#1
+	vmul.u32	$Ni,$Ni,$M0
+
+	vmlal.u32	@ACC[4],$Bi,${A2}[0]
+	vmlal.u32	@ACC[5],$Bi,${A2}[1]
+	vmlal.u32	@ACC[6],$Bi,${A3}[0]
+	vzip.16		$Ni,$zero
+	vmlal.u32	@ACC[7],$Bi,${A3}[1]
+
+	vmlal.u32	@ACC[0],$Ni,${N0}[0]
+	vmlal.u32	@ACC[1],$Ni,${N0}[1]
+	vmlal.u32	@ACC[2],$Ni,${N1}[0]
+	vmlal.u32	@ACC[3],$Ni,${N1}[1]
+
+	vmlal.u32	@ACC[4],$Ni,${N2}[0]
+	vmov		$Temp,@ACC[0]
+	vmlal.u32	@ACC[5],$Ni,${N2}[1]
+	vmov		@ACC[0],@ACC[1]
+	vmlal.u32	@ACC[6],$Ni,${N3}[0]
+	vmov		@ACC[1],@ACC[2]
+	vmlal.u32	@ACC[7],$Ni,${N3}[1]
+	vmov		@ACC[2],@ACC[3]
+	vmov		@ACC[3],@ACC[4]
+	vshr.u64	$temp,$temp,#16
+	vmov		@ACC[4],@ACC[5]
+	vmov		@ACC[5],@ACC[6]
+	vadd.u64	$temp,$temp,$Temp#hi
+	vmov		@ACC[6],@ACC[7]
+	veor		@ACC[7],@ACC[7]
+	vshr.u64	$temp,$temp,#16
+
+	bne	.LNEON_outer8
+
+	vadd.u64	@ACC[0]#lo,@ACC[0]#lo,$temp
+	mov		$toutptr,sp
+	vshr.u64	$temp,@ACC[0]#lo,#16
+	mov		$inner,$num
+	vadd.u64	@ACC[0]#hi,@ACC[0]#hi,$temp
+	add		$tinptr,sp,#96
+	vshr.u64	$temp,@ACC[0]#hi,#16
+	vzip.16		@ACC[0]#lo,@ACC[0]#hi
+
+	b	.LNEON_tail_entry
+
+.align	4
+.LNEON_8n:
+	veor		@ACC[0],@ACC[0],@ACC[0]
+	 sub		$toutptr,sp,#128
+	veor		@ACC[1],@ACC[1],@ACC[1]
+	 sub		$toutptr,$toutptr,$num,lsl#4
+	veor		@ACC[2],@ACC[2],@ACC[2]
+	 and		$toutptr,$toutptr,#-64
+	veor		@ACC[3],@ACC[3],@ACC[3]
+	 mov		sp,$toutptr			@ alloca
+	veor		@ACC[4],@ACC[4],@ACC[4]
+	 add		$toutptr,$toutptr,#256
+	veor		@ACC[5],@ACC[5],@ACC[5]
+	 sub		$inner,$num,#8
+	veor		@ACC[6],@ACC[6],@ACC[6]
+	veor		@ACC[7],@ACC[7],@ACC[7]
+
+.LNEON_8n_init:
+	vst1.64		{@ACC[0]-@ACC[1]},[$toutptr,:256]!
+	subs		$inner,$inner,#8
+	vst1.64		{@ACC[2]-@ACC[3]},[$toutptr,:256]!
+	vst1.64		{@ACC[4]-@ACC[5]},[$toutptr,:256]!
+	vst1.64		{@ACC[6]-@ACC[7]},[$toutptr,:256]!
+	bne		.LNEON_8n_init
+
+	add		$tinptr,sp,#256
+	vld1.32		{$A0-$A3},[$aptr]!
+	add		$bnptr,sp,#8
+	vld1.32		{${M0}[0]},[$n0,:32]
+	mov		$outer,$num
+	b		.LNEON_8n_outer
+
+.align	4
+.LNEON_8n_outer:
+	vld1.32		{${Bi}[0]},[$bptr,:32]!	@ *b++
+	veor		$zero,$zero,$zero
+	vzip.16		$Bi,$zero
+	add		$toutptr,sp,#128
+	vld1.32		{$N0-$N3},[$nptr]!
+
+	vmlal.u32	@ACC[0],$Bi,${A0}[0]
+	vmlal.u32	@ACC[1],$Bi,${A0}[1]
+	 veor		$zero,$zero,$zero
+	vmlal.u32	@ACC[2],$Bi,${A1}[0]
+	 vshl.i64	$Ni,@ACC[0]#hi,#16
+	vmlal.u32	@ACC[3],$Bi,${A1}[1]
+	 vadd.u64	$Ni,$Ni,@ACC[0]#lo
+	vmlal.u32	@ACC[4],$Bi,${A2}[0]
+	 vmul.u32	$Ni,$Ni,$M0
+	vmlal.u32	@ACC[5],$Bi,${A2}[1]
+	vst1.32		{$Bi},[sp,:64]		@ put aside smashed b[8*i+0]
+	vmlal.u32	@ACC[6],$Bi,${A3}[0]
+	 vzip.16	$Ni,$zero
+	vmlal.u32	@ACC[7],$Bi,${A3}[1]
+___
+for ($i=0; $i<7;) {
+$code.=<<___;
+	vld1.32		{${Bi}[0]},[$bptr,:32]!	@ *b++
+	vmlal.u32	@ACC[0],$Ni,${N0}[0]
+	veor		$temp,$temp,$temp
+	vmlal.u32	@ACC[1],$Ni,${N0}[1]
+	vzip.16		$Bi,$temp
+	vmlal.u32	@ACC[2],$Ni,${N1}[0]
+	 vshr.u64	@ACC[0]#lo,@ACC[0]#lo,#16
+	vmlal.u32	@ACC[3],$Ni,${N1}[1]
+	vmlal.u32	@ACC[4],$Ni,${N2}[0]
+	 vadd.u64	@ACC[0]#lo,@ACC[0]#lo,@ACC[0]#hi
+	vmlal.u32	@ACC[5],$Ni,${N2}[1]
+	 vshr.u64	@ACC[0]#lo,@ACC[0]#lo,#16
+	vmlal.u32	@ACC[6],$Ni,${N3}[0]
+	vmlal.u32	@ACC[7],$Ni,${N3}[1]
+	 vadd.u64	@ACC[1]#lo,@ACC[1]#lo,@ACC[0]#lo
+	vst1.32		{$Ni},[$bnptr,:64]!	@ put aside smashed m[8*i+$i]
+___
+	push(@ACC,shift(@ACC));	$i++;
+$code.=<<___;
+	vmlal.u32	@ACC[0],$Bi,${A0}[0]
+	vld1.64		{@ACC[7]},[$tinptr,:128]!
+	vmlal.u32	@ACC[1],$Bi,${A0}[1]
+	 veor		$zero,$zero,$zero
+	vmlal.u32	@ACC[2],$Bi,${A1}[0]
+	 vshl.i64	$Ni,@ACC[0]#hi,#16
+	vmlal.u32	@ACC[3],$Bi,${A1}[1]
+	 vadd.u64	$Ni,$Ni,@ACC[0]#lo
+	vmlal.u32	@ACC[4],$Bi,${A2}[0]
+	 vmul.u32	$Ni,$Ni,$M0
+	vmlal.u32	@ACC[5],$Bi,${A2}[1]
+	vst1.32		{$Bi},[$bnptr,:64]!	@ put aside smashed b[8*i+$i]
+	vmlal.u32	@ACC[6],$Bi,${A3}[0]
+	 vzip.16	$Ni,$zero
+	vmlal.u32	@ACC[7],$Bi,${A3}[1]
+___
+}
+$code.=<<___;
+	vld1.32		{$Bi},[sp,:64]		@ pull smashed b[8*i+0]
+	vmlal.u32	@ACC[0],$Ni,${N0}[0]
+	vld1.32		{$A0-$A3},[$aptr]!
+	vmlal.u32	@ACC[1],$Ni,${N0}[1]
+	vmlal.u32	@ACC[2],$Ni,${N1}[0]
+	 vshr.u64	@ACC[0]#lo,@ACC[0]#lo,#16
+	vmlal.u32	@ACC[3],$Ni,${N1}[1]
+	vmlal.u32	@ACC[4],$Ni,${N2}[0]
+	 vadd.u64	@ACC[0]#lo,@ACC[0]#lo,@ACC[0]#hi
+	vmlal.u32	@ACC[5],$Ni,${N2}[1]
+	 vshr.u64	@ACC[0]#lo,@ACC[0]#lo,#16
+	vmlal.u32	@ACC[6],$Ni,${N3}[0]
+	vmlal.u32	@ACC[7],$Ni,${N3}[1]
+	 vadd.u64	@ACC[1]#lo,@ACC[1]#lo,@ACC[0]#lo
+	vst1.32		{$Ni},[$bnptr,:64]	@ put aside smashed m[8*i+$i]
+	add		$bnptr,sp,#8		@ rewind
+___
+	push(@ACC,shift(@ACC));
+$code.=<<___;
+	sub		$inner,$num,#8
+	b		.LNEON_8n_inner
+
+.align	4
+.LNEON_8n_inner:
+	subs		$inner,$inner,#8
+	vmlal.u32	@ACC[0],$Bi,${A0}[0]
+	vld1.64		{@ACC[7]},[$tinptr,:128]
+	vmlal.u32	@ACC[1],$Bi,${A0}[1]
+	vld1.32		{$Ni},[$bnptr,:64]!	@ pull smashed m[8*i+0]
+	vmlal.u32	@ACC[2],$Bi,${A1}[0]
+	vld1.32		{$N0-$N3},[$nptr]!
+	vmlal.u32	@ACC[3],$Bi,${A1}[1]
+	it		ne
+	addne		$tinptr,$tinptr,#16	@ don't advance in last iteration
+	vmlal.u32	@ACC[4],$Bi,${A2}[0]
+	vmlal.u32	@ACC[5],$Bi,${A2}[1]
+	vmlal.u32	@ACC[6],$Bi,${A3}[0]
+	vmlal.u32	@ACC[7],$Bi,${A3}[1]
+___
+for ($i=1; $i<8; $i++) {
+$code.=<<___;
+	vld1.32		{$Bi},[$bnptr,:64]!	@ pull smashed b[8*i+$i]
+	vmlal.u32	@ACC[0],$Ni,${N0}[0]
+	vmlal.u32	@ACC[1],$Ni,${N0}[1]
+	vmlal.u32	@ACC[2],$Ni,${N1}[0]
+	vmlal.u32	@ACC[3],$Ni,${N1}[1]
+	vmlal.u32	@ACC[4],$Ni,${N2}[0]
+	vmlal.u32	@ACC[5],$Ni,${N2}[1]
+	vmlal.u32	@ACC[6],$Ni,${N3}[0]
+	vmlal.u32	@ACC[7],$Ni,${N3}[1]
+	vst1.64		{@ACC[0]},[$toutptr,:128]!
+___
+	push(@ACC,shift(@ACC));
+$code.=<<___;
+	vmlal.u32	@ACC[0],$Bi,${A0}[0]
+	vld1.64		{@ACC[7]},[$tinptr,:128]
+	vmlal.u32	@ACC[1],$Bi,${A0}[1]
+	vld1.32		{$Ni},[$bnptr,:64]!	@ pull smashed m[8*i+$i]
+	vmlal.u32	@ACC[2],$Bi,${A1}[0]
+	it		ne
+	addne		$tinptr,$tinptr,#16	@ don't advance in last iteration
+	vmlal.u32	@ACC[3],$Bi,${A1}[1]
+	vmlal.u32	@ACC[4],$Bi,${A2}[0]
+	vmlal.u32	@ACC[5],$Bi,${A2}[1]
+	vmlal.u32	@ACC[6],$Bi,${A3}[0]
+	vmlal.u32	@ACC[7],$Bi,${A3}[1]
+___
+}
+$code.=<<___;
+	it		eq
+	subeq		$aptr,$aptr,$num,lsl#2	@ rewind
+	vmlal.u32	@ACC[0],$Ni,${N0}[0]
+	vld1.32		{$Bi},[sp,:64]		@ pull smashed b[8*i+0]
+	vmlal.u32	@ACC[1],$Ni,${N0}[1]
+	vld1.32		{$A0-$A3},[$aptr]!
+	vmlal.u32	@ACC[2],$Ni,${N1}[0]
+	add		$bnptr,sp,#8		@ rewind
+	vmlal.u32	@ACC[3],$Ni,${N1}[1]
+	vmlal.u32	@ACC[4],$Ni,${N2}[0]
+	vmlal.u32	@ACC[5],$Ni,${N2}[1]
+	vmlal.u32	@ACC[6],$Ni,${N3}[0]
+	vst1.64		{@ACC[0]},[$toutptr,:128]!
+	vmlal.u32	@ACC[7],$Ni,${N3}[1]
+
+	bne		.LNEON_8n_inner
+___
+	push(@ACC,shift(@ACC));
+$code.=<<___;
+	add		$tinptr,sp,#128
+	vst1.64		{@ACC[0]-@ACC[1]},[$toutptr,:256]!
+	veor		q2,q2,q2		@ $N0-$N1
+	vst1.64		{@ACC[2]-@ACC[3]},[$toutptr,:256]!
+	veor		q3,q3,q3		@ $N2-$N3
+	vst1.64		{@ACC[4]-@ACC[5]},[$toutptr,:256]!
+	vst1.64		{@ACC[6]},[$toutptr,:128]
+
+	subs		$outer,$outer,#8
+	vld1.64		{@ACC[0]-@ACC[1]},[$tinptr,:256]!
+	vld1.64		{@ACC[2]-@ACC[3]},[$tinptr,:256]!
+	vld1.64		{@ACC[4]-@ACC[5]},[$tinptr,:256]!
+	vld1.64		{@ACC[6]-@ACC[7]},[$tinptr,:256]!
+
+	itt		ne
+	subne		$nptr,$nptr,$num,lsl#2	@ rewind
+	bne		.LNEON_8n_outer
+
+	add		$toutptr,sp,#128
+	vst1.64		{q2-q3}, [sp,:256]!	@ start wiping stack frame
+	vshr.u64	$temp,@ACC[0]#lo,#16
+	vst1.64		{q2-q3},[sp,:256]!
+	vadd.u64	@ACC[0]#hi,@ACC[0]#hi,$temp
+	vst1.64		{q2-q3}, [sp,:256]!
+	vshr.u64	$temp,@ACC[0]#hi,#16
+	vst1.64		{q2-q3}, [sp,:256]!
+	vzip.16		@ACC[0]#lo,@ACC[0]#hi
+
+	mov		$inner,$num
+	b		.LNEON_tail_entry
+
+.align	4
+.LNEON_tail:
+	vadd.u64	@ACC[0]#lo,@ACC[0]#lo,$temp
+	vshr.u64	$temp,@ACC[0]#lo,#16
+	vld1.64		{@ACC[2]-@ACC[3]}, [$tinptr, :256]!
+	vadd.u64	@ACC[0]#hi,@ACC[0]#hi,$temp
+	vld1.64		{@ACC[4]-@ACC[5]}, [$tinptr, :256]!
+	vshr.u64	$temp,@ACC[0]#hi,#16
+	vld1.64		{@ACC[6]-@ACC[7]}, [$tinptr, :256]!
+	vzip.16		@ACC[0]#lo,@ACC[0]#hi
+
+.LNEON_tail_entry:
+___
+for ($i=1; $i<8; $i++) {
+$code.=<<___;
+	vadd.u64	@ACC[1]#lo,@ACC[1]#lo,$temp
+	vst1.32		{@ACC[0]#lo[0]}, [$toutptr, :32]!
+	vshr.u64	$temp,@ACC[1]#lo,#16
+	vadd.u64	@ACC[1]#hi,@ACC[1]#hi,$temp
+	vshr.u64	$temp,@ACC[1]#hi,#16
+	vzip.16		@ACC[1]#lo,@ACC[1]#hi
+___
+	push(@ACC,shift(@ACC));
+}
+	push(@ACC,shift(@ACC));
+$code.=<<___;
+	vld1.64		{@ACC[0]-@ACC[1]}, [$tinptr, :256]!
+	subs		$inner,$inner,#8
+	vst1.32		{@ACC[7]#lo[0]},   [$toutptr, :32]!
+	bne	.LNEON_tail
+
+	vst1.32	{${temp}[0]}, [$toutptr, :32]		@ top-most bit
+	sub	$nptr,$nptr,$num,lsl#2			@ rewind $nptr
+	subs	$aptr,sp,#0				@ clear carry flag
+	add	$bptr,sp,$num,lsl#2
+
+.LNEON_sub:
+	ldmia	$aptr!, {r4-r7}
+	ldmia	$nptr!, {r8-r11}
+	sbcs	r8, r4,r8
+	sbcs	r9, r5,r9
+	sbcs	r10,r6,r10
+	sbcs	r11,r7,r11
+	teq	$aptr,$bptr				@ preserves carry
+	stmia	$rptr!, {r8-r11}
+	bne	.LNEON_sub
+
+	ldr	r10, [$aptr]				@ load top-most bit
+	mov	r11,sp
+	veor	q0,q0,q0
+	sub	r11,$bptr,r11				@ this is num*4
+	veor	q1,q1,q1
+	mov	$aptr,sp
+	sub	$rptr,$rptr,r11				@ rewind $rptr
+	mov	$nptr,$bptr				@ second 3/4th of frame
+	sbcs	r10,r10,#0				@ result is carry flag
+
+.LNEON_copy_n_zap:
+	ldmia	$aptr!, {r4-r7}
+	ldmia	$rptr,  {r8-r11}
+	it	cc
+	movcc	r8, r4
+	vst1.64	{q0-q1}, [$nptr,:256]!			@ wipe
+	itt	cc
+	movcc	r9, r5
+	movcc	r10,r6
+	vst1.64	{q0-q1}, [$nptr,:256]!			@ wipe
+	it	cc
+	movcc	r11,r7
+	ldmia	$aptr, {r4-r7}
+	stmia	$rptr!, {r8-r11}
+	sub	$aptr,$aptr,#16
+	ldmia	$rptr, {r8-r11}
+	it	cc
+	movcc	r8, r4
+	vst1.64	{q0-q1}, [$aptr,:256]!			@ wipe
+	itt	cc
+	movcc	r9, r5
+	movcc	r10,r6
+	vst1.64	{q0-q1}, [$nptr,:256]!			@ wipe
+	it	cc
+	movcc	r11,r7
+	teq	$aptr,$bptr				@ preserves carry
+	stmia	$rptr!, {r8-r11}
+	bne	.LNEON_copy_n_zap
+
+	mov	sp,ip
+        vldmia  sp!,{d8-d15}
+        ldmia   sp!,{r4-r11}
+	ret						@ bx lr
+.size	bn_mul8x_mont_neon,.-bn_mul8x_mont_neon
+#endif
+___
+}
+$code.=<<___;
+.asciz	"Montgomery multiplication for ARMv4/NEON, CRYPTOGAMS by "
+.align	2
+#if __ARM_MAX_ARCH__>=7
+.comm	OPENSSL_armcap_P,4,4
+#endif
+___
+
+foreach (split("\n",$code)) {
+	s/\`([^\`]*)\`/eval $1/ge;
+
+	s/\bq([0-9]+)#(lo|hi)/sprintf "d%d",2*$1+($2 eq "hi")/ge	or
+	s/\bret\b/bx    lr/g						or
+	s/\bbx\s+lr\b/.word\t0xe12fff1e/g;	# make it possible to compile with -march=armv4
+
+	print $_,"\n";
+}
+
+close STDOUT;
diff --git a/crypto/bn/asm/armv8-mont.pl b/crypto/bn/asm/armv8-mont.pl
new file mode 100755
index 000000000..5d5af1b6b
--- /dev/null
+++ b/crypto/bn/asm/armv8-mont.pl
@@ -0,0 +1,1510 @@
+#! /usr/bin/env perl
+# Copyright 2015-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+
+# ====================================================================
+# Written by Andy Polyakov  for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+
+# March 2015
+#
+# "Teaser" Montgomery multiplication module for ARMv8. Needs more
+# work. While it does improve RSA sign performance by 20-30% (less for
+# longer keys) on most processors, for some reason RSA2048 is not
+# faster and RSA4096 goes 15-20% slower on Cortex-A57. Multiplication
+# instruction issue rate is limited on processor in question, meaning
+# that dedicated squaring procedure is a must. Well, actually all
+# contemporary AArch64 processors seem to have limited multiplication
+# issue rate, i.e. they can't issue multiplication every cycle, which
+# explains moderate improvement coefficients in comparison to
+# compiler-generated code. Recall that compiler is instructed to use
+# umulh and therefore uses same amount of multiplication instructions
+# to do the job. Assembly's edge is to minimize number of "collateral"
+# instructions and of course instruction scheduling.
+#
+# April 2015
+#
+# Squaring procedure that handles lengths divisible by 8 improves
+# RSA/DSA performance by 25-40-60% depending on processor and key
+# length. Overall improvement coefficients are always positive in
+# comparison to compiler-generated code. On Cortex-A57 improvement
+# is still modest on longest key lengths, while others exhibit e.g.
+# 50-70% improvement for RSA4096 sign. RSA2048 sign is ~25% faster
+# on Cortex-A57 and ~60-100% faster on others.
+
+$flavour = shift;
+$output  = shift;
+
+$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+( $xlate="${dir}arm-xlate.pl" and -f $xlate ) or
+( $xlate="${dir}../../perlasm/arm-xlate.pl" and -f $xlate) or
+die "can't locate arm-xlate.pl";
+
+open OUT,"| \"$^X\" $xlate $flavour $output";
+*STDOUT=*OUT;
+
+($lo0,$hi0,$aj,$m0,$alo,$ahi,
+ $lo1,$hi1,$nj,$m1,$nlo,$nhi,
+ $ovf, $i,$j,$tp,$tj) = map("x$_",6..17,19..24);
+
+# int bn_mul_mont(
+$rp="x0";	# BN_ULONG *rp,
+$ap="x1";	# const BN_ULONG *ap,
+$bp="x2";	# const BN_ULONG *bp,
+$np="x3";	# const BN_ULONG *np,
+$n0="x4";	# const BN_ULONG *n0,
+$num="x5";	# int num);
+
+$code.=<<___;
+.text
+
+.globl	bn_mul_mont
+.type	bn_mul_mont,%function
+.align	5
+bn_mul_mont:
+	tst	$num,#7
+	b.eq	__bn_sqr8x_mont
+	tst	$num,#3
+	b.eq	__bn_mul4x_mont
+.Lmul_mont:
+	stp	x29,x30,[sp,#-64]!
+	add	x29,sp,#0
+	stp	x19,x20,[sp,#16]
+	stp	x21,x22,[sp,#32]
+	stp	x23,x24,[sp,#48]
+
+	ldr	$m0,[$bp],#8		// bp[0]
+	sub	$tp,sp,$num,lsl#3
+	ldp	$hi0,$aj,[$ap],#16	// ap[0..1]
+	lsl	$num,$num,#3
+	ldr	$n0,[$n0]		// *n0
+	and	$tp,$tp,#-16		// ABI says so
+	ldp	$hi1,$nj,[$np],#16	// np[0..1]
+
+	mul	$lo0,$hi0,$m0		// ap[0]*bp[0]
+	sub	$j,$num,#16		// j=num-2
+	umulh	$hi0,$hi0,$m0
+	mul	$alo,$aj,$m0		// ap[1]*bp[0]
+	umulh	$ahi,$aj,$m0
+
+	mul	$m1,$lo0,$n0		// "tp[0]"*n0
+	mov	sp,$tp			// alloca
+
+	// (*)	mul	$lo1,$hi1,$m1	// np[0]*m1
+	umulh	$hi1,$hi1,$m1
+	mul	$nlo,$nj,$m1		// np[1]*m1
+	// (*)	adds	$lo1,$lo1,$lo0	// discarded
+	// (*)	As for removal of first multiplication and addition
+	//	instructions. The outcome of first addition is
+	//	guaranteed to be zero, which leaves two computationally
+	//	significant outcomes: it either carries or not. Then
+	//	question is when does it carry? Is there alternative
+	//	way to deduce it? If you follow operations, you can
+	//	observe that condition for carry is quite simple:
+	//	$lo0 being non-zero. So that carry can be calculated
+	//	by adding -1 to $lo0. That's what next instruction does.
+	subs	xzr,$lo0,#1		// (*)
+	umulh	$nhi,$nj,$m1
+	adc	$hi1,$hi1,xzr
+	cbz	$j,.L1st_skip
+
+.L1st:
+	ldr	$aj,[$ap],#8
+	adds	$lo0,$alo,$hi0
+	sub	$j,$j,#8		// j--
+	adc	$hi0,$ahi,xzr
+
+	ldr	$nj,[$np],#8
+	adds	$lo1,$nlo,$hi1
+	mul	$alo,$aj,$m0		// ap[j]*bp[0]
+	adc	$hi1,$nhi,xzr
+	umulh	$ahi,$aj,$m0
+
+	adds	$lo1,$lo1,$lo0
+	mul	$nlo,$nj,$m1		// np[j]*m1
+	adc	$hi1,$hi1,xzr
+	umulh	$nhi,$nj,$m1
+	str	$lo1,[$tp],#8		// tp[j-1]
+	cbnz	$j,.L1st
+
+.L1st_skip:
+	adds	$lo0,$alo,$hi0
+	sub	$ap,$ap,$num		// rewind $ap
+	adc	$hi0,$ahi,xzr
+
+	adds	$lo1,$nlo,$hi1
+	sub	$np,$np,$num		// rewind $np
+	adc	$hi1,$nhi,xzr
+
+	adds	$lo1,$lo1,$lo0
+	sub	$i,$num,#8		// i=num-1
+	adcs	$hi1,$hi1,$hi0
+
+	adc	$ovf,xzr,xzr		// upmost overflow bit
+	stp	$lo1,$hi1,[$tp]
+
+.Louter:
+	ldr	$m0,[$bp],#8		// bp[i]
+	ldp	$hi0,$aj,[$ap],#16
+	ldr	$tj,[sp]		// tp[0]
+	add	$tp,sp,#8
+
+	mul	$lo0,$hi0,$m0		// ap[0]*bp[i]
+	sub	$j,$num,#16		// j=num-2
+	umulh	$hi0,$hi0,$m0
+	ldp	$hi1,$nj,[$np],#16
+	mul	$alo,$aj,$m0		// ap[1]*bp[i]
+	adds	$lo0,$lo0,$tj
+	umulh	$ahi,$aj,$m0
+	adc	$hi0,$hi0,xzr
+
+	mul	$m1,$lo0,$n0
+	sub	$i,$i,#8		// i--
+
+	// (*)	mul	$lo1,$hi1,$m1	// np[0]*m1
+	umulh	$hi1,$hi1,$m1
+	mul	$nlo,$nj,$m1		// np[1]*m1
+	// (*)	adds	$lo1,$lo1,$lo0
+	subs	xzr,$lo0,#1		// (*)
+	umulh	$nhi,$nj,$m1
+	cbz	$j,.Linner_skip
+
+.Linner:
+	ldr	$aj,[$ap],#8
+	adc	$hi1,$hi1,xzr
+	ldr	$tj,[$tp],#8		// tp[j]
+	adds	$lo0,$alo,$hi0
+	sub	$j,$j,#8		// j--
+	adc	$hi0,$ahi,xzr
+
+	adds	$lo1,$nlo,$hi1
+	ldr	$nj,[$np],#8
+	adc	$hi1,$nhi,xzr
+
+	mul	$alo,$aj,$m0		// ap[j]*bp[i]
+	adds	$lo0,$lo0,$tj
+	umulh	$ahi,$aj,$m0
+	adc	$hi0,$hi0,xzr
+
+	mul	$nlo,$nj,$m1		// np[j]*m1
+	adds	$lo1,$lo1,$lo0
+	umulh	$nhi,$nj,$m1
+	str	$lo1,[$tp,#-16]		// tp[j-1]
+	cbnz	$j,.Linner
+
+.Linner_skip:
+	ldr	$tj,[$tp],#8		// tp[j]
+	adc	$hi1,$hi1,xzr
+	adds	$lo0,$alo,$hi0
+	sub	$ap,$ap,$num		// rewind $ap
+	adc	$hi0,$ahi,xzr
+
+	adds	$lo1,$nlo,$hi1
+	sub	$np,$np,$num		// rewind $np
+	adcs	$hi1,$nhi,$ovf
+	adc	$ovf,xzr,xzr
+
+	adds	$lo0,$lo0,$tj
+	adc	$hi0,$hi0,xzr
+
+	adds	$lo1,$lo1,$lo0
+	adcs	$hi1,$hi1,$hi0
+	adc	$ovf,$ovf,xzr		// upmost overflow bit
+	stp	$lo1,$hi1,[$tp,#-16]
+
+	cbnz	$i,.Louter
+
+	// Final step. We see if result is larger than modulus, and
+	// if it is, subtract the modulus. But comparison implies
+	// subtraction. So we subtract modulus, see if it borrowed,
+	// and conditionally copy original value.
+	ldr	$tj,[sp]		// tp[0]
+	add	$tp,sp,#8
+	ldr	$nj,[$np],#8		// np[0]
+	subs	$j,$num,#8		// j=num-1 and clear borrow
+	mov	$ap,$rp
+.Lsub:
+	sbcs	$aj,$tj,$nj		// tp[j]-np[j]
+	ldr	$tj,[$tp],#8
+	sub	$j,$j,#8		// j--
+	ldr	$nj,[$np],#8
+	str	$aj,[$ap],#8		// rp[j]=tp[j]-np[j]
+	cbnz	$j,.Lsub
+
+	sbcs	$aj,$tj,$nj
+	sbcs	$ovf,$ovf,xzr		// did it borrow?
+	str	$aj,[$ap],#8		// rp[num-1]
+
+	ldr	$tj,[sp]		// tp[0]
+	add	$tp,sp,#8
+	ldr	$aj,[$rp],#8		// rp[0]
+	sub	$num,$num,#8		// num--
+	nop
+.Lcond_copy:
+	sub	$num,$num,#8		// num--
+	csel	$nj,$tj,$aj,lo		// did it borrow?
+	ldr	$tj,[$tp],#8
+	ldr	$aj,[$rp],#8
+	str	xzr,[$tp,#-16]		// wipe tp
+	str	$nj,[$rp,#-16]
+	cbnz	$num,.Lcond_copy
+
+	csel	$nj,$tj,$aj,lo
+	str	xzr,[$tp,#-8]		// wipe tp
+	str	$nj,[$rp,#-8]
+
+	ldp	x19,x20,[x29,#16]
+	mov	sp,x29
+	ldp	x21,x22,[x29,#32]
+	mov	x0,#1
+	ldp	x23,x24,[x29,#48]
+	ldr	x29,[sp],#64
+	ret
+.size	bn_mul_mont,.-bn_mul_mont
+___
+{
+########################################################################
+# Following is ARMv8 adaptation of sqrx8x_mont from x86_64-mont5 module.
+
+my ($a0,$a1,$a2,$a3,$a4,$a5,$a6,$a7)=map("x$_",(6..13));
+my ($t0,$t1,$t2,$t3)=map("x$_",(14..17));
+my ($acc0,$acc1,$acc2,$acc3,$acc4,$acc5,$acc6,$acc7)=map("x$_",(19..26));
+my ($cnt,$carry,$topmost)=("x27","x28","x30");
+my ($tp,$ap_end,$na0)=($bp,$np,$carry);
+
+$code.=<<___;
+.type	__bn_sqr8x_mont,%function
+.align	5
+__bn_sqr8x_mont:
+	cmp	$ap,$bp
+	b.ne	__bn_mul4x_mont
+.Lsqr8x_mont:
+	stp	x29,x30,[sp,#-128]!
+	add	x29,sp,#0
+	stp	x19,x20,[sp,#16]
+	stp	x21,x22,[sp,#32]
+	stp	x23,x24,[sp,#48]
+	stp	x25,x26,[sp,#64]
+	stp	x27,x28,[sp,#80]
+	stp	$rp,$np,[sp,#96]	// offload rp and np
+
+	ldp	$a0,$a1,[$ap,#8*0]
+	ldp	$a2,$a3,[$ap,#8*2]
+	ldp	$a4,$a5,[$ap,#8*4]
+	ldp	$a6,$a7,[$ap,#8*6]
+
+	sub	$tp,sp,$num,lsl#4
+	lsl	$num,$num,#3
+	ldr	$n0,[$n0]		// *n0
+	mov	sp,$tp			// alloca
+	sub	$cnt,$num,#8*8
+	b	.Lsqr8x_zero_start
+
+.Lsqr8x_zero:
+	sub	$cnt,$cnt,#8*8
+	stp	xzr,xzr,[$tp,#8*0]
+	stp	xzr,xzr,[$tp,#8*2]
+	stp	xzr,xzr,[$tp,#8*4]
+	stp	xzr,xzr,[$tp,#8*6]
+.Lsqr8x_zero_start:
+	stp	xzr,xzr,[$tp,#8*8]
+	stp	xzr,xzr,[$tp,#8*10]
+	stp	xzr,xzr,[$tp,#8*12]
+	stp	xzr,xzr,[$tp,#8*14]
+	add	$tp,$tp,#8*16
+	cbnz	$cnt,.Lsqr8x_zero
+
+	add	$ap_end,$ap,$num
+	add	$ap,$ap,#8*8
+	mov	$acc0,xzr
+	mov	$acc1,xzr
+	mov	$acc2,xzr
+	mov	$acc3,xzr
+	mov	$acc4,xzr
+	mov	$acc5,xzr
+	mov	$acc6,xzr
+	mov	$acc7,xzr
+	mov	$tp,sp
+	str	$n0,[x29,#112]		// offload n0
+
+	// Multiply everything but a[i]*a[i]
+.align	4
+.Lsqr8x_outer_loop:
+        //                                                 a[1]a[0]	(i)
+        //                                             a[2]a[0]
+        //                                         a[3]a[0]
+        //                                     a[4]a[0]
+        //                                 a[5]a[0]
+        //                             a[6]a[0]
+        //                         a[7]a[0]
+        //                                         a[2]a[1]		(ii)
+        //                                     a[3]a[1]
+        //                                 a[4]a[1]
+        //                             a[5]a[1]
+        //                         a[6]a[1]
+        //                     a[7]a[1]
+        //                                 a[3]a[2]			(iii)
+        //                             a[4]a[2]
+        //                         a[5]a[2]
+        //                     a[6]a[2]
+        //                 a[7]a[2]
+        //                         a[4]a[3]				(iv)
+        //                     a[5]a[3]
+        //                 a[6]a[3]
+        //             a[7]a[3]
+        //                 a[5]a[4]					(v)
+        //             a[6]a[4]
+        //         a[7]a[4]
+        //         a[6]a[5]						(vi)
+        //     a[7]a[5]
+        // a[7]a[6]							(vii)
+
+	mul	$t0,$a1,$a0		// lo(a[1..7]*a[0])		(i)
+	mul	$t1,$a2,$a0
+	mul	$t2,$a3,$a0
+	mul	$t3,$a4,$a0
+	adds	$acc1,$acc1,$t0		// t[1]+lo(a[1]*a[0])
+	mul	$t0,$a5,$a0
+	adcs	$acc2,$acc2,$t1
+	mul	$t1,$a6,$a0
+	adcs	$acc3,$acc3,$t2
+	mul	$t2,$a7,$a0
+	adcs	$acc4,$acc4,$t3
+	umulh	$t3,$a1,$a0		// hi(a[1..7]*a[0])
+	adcs	$acc5,$acc5,$t0
+	umulh	$t0,$a2,$a0
+	adcs	$acc6,$acc6,$t1
+	umulh	$t1,$a3,$a0
+	adcs	$acc7,$acc7,$t2
+	umulh	$t2,$a4,$a0
+	stp	$acc0,$acc1,[$tp],#8*2	// t[0..1]
+	adc	$acc0,xzr,xzr		// t[8]
+	adds	$acc2,$acc2,$t3		// t[2]+lo(a[1]*a[0])
+	umulh	$t3,$a5,$a0
+	adcs	$acc3,$acc3,$t0
+	umulh	$t0,$a6,$a0
+	adcs	$acc4,$acc4,$t1
+	umulh	$t1,$a7,$a0
+	adcs	$acc5,$acc5,$t2
+	 mul	$t2,$a2,$a1		// lo(a[2..7]*a[1])		(ii)
+	adcs	$acc6,$acc6,$t3
+	 mul	$t3,$a3,$a1
+	adcs	$acc7,$acc7,$t0
+	 mul	$t0,$a4,$a1
+	adc	$acc0,$acc0,$t1
+
+	mul	$t1,$a5,$a1
+	adds	$acc3,$acc3,$t2
+	mul	$t2,$a6,$a1
+	adcs	$acc4,$acc4,$t3
+	mul	$t3,$a7,$a1
+	adcs	$acc5,$acc5,$t0
+	umulh	$t0,$a2,$a1		// hi(a[2..7]*a[1])
+	adcs	$acc6,$acc6,$t1
+	umulh	$t1,$a3,$a1
+	adcs	$acc7,$acc7,$t2
+	umulh	$t2,$a4,$a1
+	adcs	$acc0,$acc0,$t3
+	umulh	$t3,$a5,$a1
+	stp	$acc2,$acc3,[$tp],#8*2	// t[2..3]
+	adc	$acc1,xzr,xzr		// t[9]
+	adds	$acc4,$acc4,$t0
+	umulh	$t0,$a6,$a1
+	adcs	$acc5,$acc5,$t1
+	umulh	$t1,$a7,$a1
+	adcs	$acc6,$acc6,$t2
+	 mul	$t2,$a3,$a2		// lo(a[3..7]*a[2])		(iii)
+	adcs	$acc7,$acc7,$t3
+	 mul	$t3,$a4,$a2
+	adcs	$acc0,$acc0,$t0
+	 mul	$t0,$a5,$a2
+	adc	$acc1,$acc1,$t1
+
+	mul	$t1,$a6,$a2
+	adds	$acc5,$acc5,$t2
+	mul	$t2,$a7,$a2
+	adcs	$acc6,$acc6,$t3
+	umulh	$t3,$a3,$a2		// hi(a[3..7]*a[2])
+	adcs	$acc7,$acc7,$t0
+	umulh	$t0,$a4,$a2
+	adcs	$acc0,$acc0,$t1
+	umulh	$t1,$a5,$a2
+	adcs	$acc1,$acc1,$t2
+	umulh	$t2,$a6,$a2
+	stp	$acc4,$acc5,[$tp],#8*2	// t[4..5]
+	adc	$acc2,xzr,xzr		// t[10]
+	adds	$acc6,$acc6,$t3
+	umulh	$t3,$a7,$a2
+	adcs	$acc7,$acc7,$t0
+	 mul	$t0,$a4,$a3		// lo(a[4..7]*a[3])		(iv)
+	adcs	$acc0,$acc0,$t1
+	 mul	$t1,$a5,$a3
+	adcs	$acc1,$acc1,$t2
+	 mul	$t2,$a6,$a3
+	adc	$acc2,$acc2,$t3
+
+	mul	$t3,$a7,$a3
+	adds	$acc7,$acc7,$t0
+	umulh	$t0,$a4,$a3		// hi(a[4..7]*a[3])
+	adcs	$acc0,$acc0,$t1
+	umulh	$t1,$a5,$a3
+	adcs	$acc1,$acc1,$t2
+	umulh	$t2,$a6,$a3
+	adcs	$acc2,$acc2,$t3
+	umulh	$t3,$a7,$a3
+	stp	$acc6,$acc7,[$tp],#8*2	// t[6..7]
+	adc	$acc3,xzr,xzr		// t[11]
+	adds	$acc0,$acc0,$t0
+	 mul	$t0,$a5,$a4		// lo(a[5..7]*a[4])		(v)
+	adcs	$acc1,$acc1,$t1
+	 mul	$t1,$a6,$a4
+	adcs	$acc2,$acc2,$t2
+	 mul	$t2,$a7,$a4
+	adc	$acc3,$acc3,$t3
+
+	umulh	$t3,$a5,$a4		// hi(a[5..7]*a[4])
+	adds	$acc1,$acc1,$t0
+	umulh	$t0,$a6,$a4
+	adcs	$acc2,$acc2,$t1
+	umulh	$t1,$a7,$a4
+	adcs	$acc3,$acc3,$t2
+	 mul	$t2,$a6,$a5		// lo(a[6..7]*a[5])		(vi)
+	adc	$acc4,xzr,xzr		// t[12]
+	adds	$acc2,$acc2,$t3
+	 mul	$t3,$a7,$a5
+	adcs	$acc3,$acc3,$t0
+	 umulh	$t0,$a6,$a5		// hi(a[6..7]*a[5])
+	adc	$acc4,$acc4,$t1
+
+	umulh	$t1,$a7,$a5
+	adds	$acc3,$acc3,$t2
+	 mul	$t2,$a7,$a6		// lo(a[7]*a[6])		(vii)
+	adcs	$acc4,$acc4,$t3
+	 umulh	$t3,$a7,$a6		// hi(a[7]*a[6])
+	adc	$acc5,xzr,xzr		// t[13]
+	adds	$acc4,$acc4,$t0
+	sub	$cnt,$ap_end,$ap	// done yet?
+	adc	$acc5,$acc5,$t1
+
+	adds	$acc5,$acc5,$t2
+	sub	$t0,$ap_end,$num	// rewinded ap
+	adc	$acc6,xzr,xzr		// t[14]
+	add	$acc6,$acc6,$t3
+
+	cbz	$cnt,.Lsqr8x_outer_break
+
+	mov	$n0,$a0
+	ldp	$a0,$a1,[$tp,#8*0]
+	ldp	$a2,$a3,[$tp,#8*2]
+	ldp	$a4,$a5,[$tp,#8*4]
+	ldp	$a6,$a7,[$tp,#8*6]
+	adds	$acc0,$acc0,$a0
+	adcs	$acc1,$acc1,$a1
+	ldp	$a0,$a1,[$ap,#8*0]
+	adcs	$acc2,$acc2,$a2
+	adcs	$acc3,$acc3,$a3
+	ldp	$a2,$a3,[$ap,#8*2]
+	adcs	$acc4,$acc4,$a4
+	adcs	$acc5,$acc5,$a5
+	ldp	$a4,$a5,[$ap,#8*4]
+	adcs	$acc6,$acc6,$a6
+	mov	$rp,$ap
+	adcs	$acc7,xzr,$a7
+	ldp	$a6,$a7,[$ap,#8*6]
+	add	$ap,$ap,#8*8
+	//adc	$carry,xzr,xzr		// moved below
+	mov	$cnt,#-8*8
+
+	//                                                         a[8]a[0]
+	//                                                     a[9]a[0]
+	//                                                 a[a]a[0]
+	//                                             a[b]a[0]
+	//                                         a[c]a[0]
+	//                                     a[d]a[0]
+	//                                 a[e]a[0]
+	//                             a[f]a[0]
+	//                                                     a[8]a[1]
+	//                         a[f]a[1]........................
+	//                                                 a[8]a[2]
+	//                     a[f]a[2]........................
+	//                                             a[8]a[3]
+	//                 a[f]a[3]........................
+	//                                         a[8]a[4]
+	//             a[f]a[4]........................
+	//                                     a[8]a[5]
+	//         a[f]a[5]........................
+	//                                 a[8]a[6]
+	//     a[f]a[6]........................
+	//                             a[8]a[7]
+	// a[f]a[7]........................
+.Lsqr8x_mul:
+	mul	$t0,$a0,$n0
+	adc	$carry,xzr,xzr		// carry bit, modulo-scheduled
+	mul	$t1,$a1,$n0
+	add	$cnt,$cnt,#8
+	mul	$t2,$a2,$n0
+	mul	$t3,$a3,$n0
+	adds	$acc0,$acc0,$t0
+	mul	$t0,$a4,$n0
+	adcs	$acc1,$acc1,$t1
+	mul	$t1,$a5,$n0
+	adcs	$acc2,$acc2,$t2
+	mul	$t2,$a6,$n0
+	adcs	$acc3,$acc3,$t3
+	mul	$t3,$a7,$n0
+	adcs	$acc4,$acc4,$t0
+	umulh	$t0,$a0,$n0
+	adcs	$acc5,$acc5,$t1
+	umulh	$t1,$a1,$n0
+	adcs	$acc6,$acc6,$t2
+	umulh	$t2,$a2,$n0
+	adcs	$acc7,$acc7,$t3
+	umulh	$t3,$a3,$n0
+	adc	$carry,$carry,xzr
+	str	$acc0,[$tp],#8
+	adds	$acc0,$acc1,$t0
+	umulh	$t0,$a4,$n0
+	adcs	$acc1,$acc2,$t1
+	umulh	$t1,$a5,$n0
+	adcs	$acc2,$acc3,$t2
+	umulh	$t2,$a6,$n0
+	adcs	$acc3,$acc4,$t3
+	umulh	$t3,$a7,$n0
+	ldr	$n0,[$rp,$cnt]
+	adcs	$acc4,$acc5,$t0
+	adcs	$acc5,$acc6,$t1
+	adcs	$acc6,$acc7,$t2
+	adcs	$acc7,$carry,$t3
+	//adc	$carry,xzr,xzr		// moved above
+	cbnz	$cnt,.Lsqr8x_mul
+					// note that carry flag is guaranteed
+					// to be zero at this point
+	cmp	$ap,$ap_end		// done yet?
+	b.eq	.Lsqr8x_break
+
+	ldp	$a0,$a1,[$tp,#8*0]
+	ldp	$a2,$a3,[$tp,#8*2]
+	ldp	$a4,$a5,[$tp,#8*4]
+	ldp	$a6,$a7,[$tp,#8*6]
+	adds	$acc0,$acc0,$a0
+	ldr	$n0,[$rp,#-8*8]
+	adcs	$acc1,$acc1,$a1
+	ldp	$a0,$a1,[$ap,#8*0]
+	adcs	$acc2,$acc2,$a2
+	adcs	$acc3,$acc3,$a3
+	ldp	$a2,$a3,[$ap,#8*2]
+	adcs	$acc4,$acc4,$a4
+	adcs	$acc5,$acc5,$a5
+	ldp	$a4,$a5,[$ap,#8*4]
+	adcs	$acc6,$acc6,$a6
+	mov	$cnt,#-8*8
+	adcs	$acc7,$acc7,$a7
+	ldp	$a6,$a7,[$ap,#8*6]
+	add	$ap,$ap,#8*8
+	//adc	$carry,xzr,xzr		// moved above
+	b	.Lsqr8x_mul
+
+.align	4
+.Lsqr8x_break:
+	ldp	$a0,$a1,[$rp,#8*0]
+	add	$ap,$rp,#8*8
+	ldp	$a2,$a3,[$rp,#8*2]
+	sub	$t0,$ap_end,$ap		// is it last iteration?
+	ldp	$a4,$a5,[$rp,#8*4]
+	sub	$t1,$tp,$t0
+	ldp	$a6,$a7,[$rp,#8*6]
+	cbz	$t0,.Lsqr8x_outer_loop
+
+	stp	$acc0,$acc1,[$tp,#8*0]
+	ldp	$acc0,$acc1,[$t1,#8*0]
+	stp	$acc2,$acc3,[$tp,#8*2]
+	ldp	$acc2,$acc3,[$t1,#8*2]
+	stp	$acc4,$acc5,[$tp,#8*4]
+	ldp	$acc4,$acc5,[$t1,#8*4]
+	stp	$acc6,$acc7,[$tp,#8*6]
+	mov	$tp,$t1
+	ldp	$acc6,$acc7,[$t1,#8*6]
+	b	.Lsqr8x_outer_loop
+
+.align	4
+.Lsqr8x_outer_break:
+	// Now multiply above result by 2 and add a[n-1]*a[n-1]|...|a[0]*a[0]
+	ldp	$a1,$a3,[$t0,#8*0]	// recall that $t0 is &a[0]
+	ldp	$t1,$t2,[sp,#8*1]
+	ldp	$a5,$a7,[$t0,#8*2]
+	add	$ap,$t0,#8*4
+	ldp	$t3,$t0,[sp,#8*3]
+
+	stp	$acc0,$acc1,[$tp,#8*0]
+	mul	$acc0,$a1,$a1
+	stp	$acc2,$acc3,[$tp,#8*2]
+	umulh	$a1,$a1,$a1
+	stp	$acc4,$acc5,[$tp,#8*4]
+	mul	$a2,$a3,$a3
+	stp	$acc6,$acc7,[$tp,#8*6]
+	mov	$tp,sp
+	umulh	$a3,$a3,$a3
+	adds	$acc1,$a1,$t1,lsl#1
+	extr	$t1,$t2,$t1,#63
+	sub	$cnt,$num,#8*4
+
+.Lsqr4x_shift_n_add:
+	adcs	$acc2,$a2,$t1
+	extr	$t2,$t3,$t2,#63
+	sub	$cnt,$cnt,#8*4
+	adcs	$acc3,$a3,$t2
+	ldp	$t1,$t2,[$tp,#8*5]
+	mul	$a4,$a5,$a5
+	ldp	$a1,$a3,[$ap],#8*2
+	umulh	$a5,$a5,$a5
+	mul	$a6,$a7,$a7
+	umulh	$a7,$a7,$a7
+	extr	$t3,$t0,$t3,#63
+	stp	$acc0,$acc1,[$tp,#8*0]
+	adcs	$acc4,$a4,$t3
+	extr	$t0,$t1,$t0,#63
+	stp	$acc2,$acc3,[$tp,#8*2]
+	adcs	$acc5,$a5,$t0
+	ldp	$t3,$t0,[$tp,#8*7]
+	extr	$t1,$t2,$t1,#63
+	adcs	$acc6,$a6,$t1
+	extr	$t2,$t3,$t2,#63
+	adcs	$acc7,$a7,$t2
+	ldp	$t1,$t2,[$tp,#8*9]
+	mul	$a0,$a1,$a1
+	ldp	$a5,$a7,[$ap],#8*2
+	umulh	$a1,$a1,$a1
+	mul	$a2,$a3,$a3
+	umulh	$a3,$a3,$a3
+	stp	$acc4,$acc5,[$tp,#8*4]
+	extr	$t3,$t0,$t3,#63
+	stp	$acc6,$acc7,[$tp,#8*6]
+	add	$tp,$tp,#8*8
+	adcs	$acc0,$a0,$t3
+	extr	$t0,$t1,$t0,#63
+	adcs	$acc1,$a1,$t0
+	ldp	$t3,$t0,[$tp,#8*3]
+	extr	$t1,$t2,$t1,#63
+	cbnz	$cnt,.Lsqr4x_shift_n_add
+___
+my ($np,$np_end)=($ap,$ap_end);
+$code.=<<___;
+	 ldp	$np,$n0,[x29,#104]	// pull np and n0
+
+	adcs	$acc2,$a2,$t1
+	extr	$t2,$t3,$t2,#63
+	adcs	$acc3,$a3,$t2
+	ldp	$t1,$t2,[$tp,#8*5]
+	mul	$a4,$a5,$a5
+	umulh	$a5,$a5,$a5
+	stp	$acc0,$acc1,[$tp,#8*0]
+	mul	$a6,$a7,$a7
+	umulh	$a7,$a7,$a7
+	stp	$acc2,$acc3,[$tp,#8*2]
+	extr	$t3,$t0,$t3,#63
+	adcs	$acc4,$a4,$t3
+	extr	$t0,$t1,$t0,#63
+	 ldp	$acc0,$acc1,[sp,#8*0]
+	adcs	$acc5,$a5,$t0
+	extr	$t1,$t2,$t1,#63
+	 ldp	$a0,$a1,[$np,#8*0]
+	adcs	$acc6,$a6,$t1
+	extr	$t2,xzr,$t2,#63
+	 ldp	$a2,$a3,[$np,#8*2]
+	adc	$acc7,$a7,$t2
+	 ldp	$a4,$a5,[$np,#8*4]
+
+	// Reduce by 512 bits per iteration
+	mul	$na0,$n0,$acc0		// t[0]*n0
+	ldp	$a6,$a7,[$np,#8*6]
+	add	$np_end,$np,$num
+	ldp	$acc2,$acc3,[sp,#8*2]
+	stp	$acc4,$acc5,[$tp,#8*4]
+	ldp	$acc4,$acc5,[sp,#8*4]
+	stp	$acc6,$acc7,[$tp,#8*6]
+	ldp	$acc6,$acc7,[sp,#8*6]
+	add	$np,$np,#8*8
+	mov	$topmost,xzr		// initial top-most carry
+	mov	$tp,sp
+	mov	$cnt,#8
+
+.Lsqr8x_reduction:
+	// (*)	mul	$t0,$a0,$na0	// lo(n[0-7])*lo(t[0]*n0)
+	mul	$t1,$a1,$na0
+	sub	$cnt,$cnt,#1
+	mul	$t2,$a2,$na0
+	str	$na0,[$tp],#8		// put aside t[0]*n0 for tail processing
+	mul	$t3,$a3,$na0
+	// (*)	adds	xzr,$acc0,$t0
+	subs	xzr,$acc0,#1		// (*)
+	mul	$t0,$a4,$na0
+	adcs	$acc0,$acc1,$t1
+	mul	$t1,$a5,$na0
+	adcs	$acc1,$acc2,$t2
+	mul	$t2,$a6,$na0
+	adcs	$acc2,$acc3,$t3
+	mul	$t3,$a7,$na0
+	adcs	$acc3,$acc4,$t0
+	umulh	$t0,$a0,$na0		// hi(n[0-7])*lo(t[0]*n0)
+	adcs	$acc4,$acc5,$t1
+	umulh	$t1,$a1,$na0
+	adcs	$acc5,$acc6,$t2
+	umulh	$t2,$a2,$na0
+	adcs	$acc6,$acc7,$t3
+	umulh	$t3,$a3,$na0
+	adc	$acc7,xzr,xzr
+	adds	$acc0,$acc0,$t0
+	umulh	$t0,$a4,$na0
+	adcs	$acc1,$acc1,$t1
+	umulh	$t1,$a5,$na0
+	adcs	$acc2,$acc2,$t2
+	umulh	$t2,$a6,$na0
+	adcs	$acc3,$acc3,$t3
+	umulh	$t3,$a7,$na0
+	mul	$na0,$n0,$acc0		// next t[0]*n0
+	adcs	$acc4,$acc4,$t0
+	adcs	$acc5,$acc5,$t1
+	adcs	$acc6,$acc6,$t2
+	adc	$acc7,$acc7,$t3
+	cbnz	$cnt,.Lsqr8x_reduction
+
+	ldp	$t0,$t1,[$tp,#8*0]
+	ldp	$t2,$t3,[$tp,#8*2]
+	mov	$rp,$tp
+	sub	$cnt,$np_end,$np	// done yet?
+	adds	$acc0,$acc0,$t0
+	adcs	$acc1,$acc1,$t1
+	ldp	$t0,$t1,[$tp,#8*4]
+	adcs	$acc2,$acc2,$t2
+	adcs	$acc3,$acc3,$t3
+	ldp	$t2,$t3,[$tp,#8*6]
+	adcs	$acc4,$acc4,$t0
+	adcs	$acc5,$acc5,$t1
+	adcs	$acc6,$acc6,$t2
+	adcs	$acc7,$acc7,$t3
+	//adc	$carry,xzr,xzr		// moved below
+	cbz	$cnt,.Lsqr8x8_post_condition
+
+	ldr	$n0,[$tp,#-8*8]
+	ldp	$a0,$a1,[$np,#8*0]
+	ldp	$a2,$a3,[$np,#8*2]
+	ldp	$a4,$a5,[$np,#8*4]
+	mov	$cnt,#-8*8
+	ldp	$a6,$a7,[$np,#8*6]
+	add	$np,$np,#8*8
+
+.Lsqr8x_tail:
+	mul	$t0,$a0,$n0
+	adc	$carry,xzr,xzr		// carry bit, modulo-scheduled
+	mul	$t1,$a1,$n0
+	add	$cnt,$cnt,#8
+	mul	$t2,$a2,$n0
+	mul	$t3,$a3,$n0
+	adds	$acc0,$acc0,$t0
+	mul	$t0,$a4,$n0
+	adcs	$acc1,$acc1,$t1
+	mul	$t1,$a5,$n0
+	adcs	$acc2,$acc2,$t2
+	mul	$t2,$a6,$n0
+	adcs	$acc3,$acc3,$t3
+	mul	$t3,$a7,$n0
+	adcs	$acc4,$acc4,$t0
+	umulh	$t0,$a0,$n0
+	adcs	$acc5,$acc5,$t1
+	umulh	$t1,$a1,$n0
+	adcs	$acc6,$acc6,$t2
+	umulh	$t2,$a2,$n0
+	adcs	$acc7,$acc7,$t3
+	umulh	$t3,$a3,$n0
+	adc	$carry,$carry,xzr
+	str	$acc0,[$tp],#8
+	adds	$acc0,$acc1,$t0
+	umulh	$t0,$a4,$n0
+	adcs	$acc1,$acc2,$t1
+	umulh	$t1,$a5,$n0
+	adcs	$acc2,$acc3,$t2
+	umulh	$t2,$a6,$n0
+	adcs	$acc3,$acc4,$t3
+	umulh	$t3,$a7,$n0
+	ldr	$n0,[$rp,$cnt]
+	adcs	$acc4,$acc5,$t0
+	adcs	$acc5,$acc6,$t1
+	adcs	$acc6,$acc7,$t2
+	adcs	$acc7,$carry,$t3
+	//adc	$carry,xzr,xzr		// moved above
+	cbnz	$cnt,.Lsqr8x_tail
+					// note that carry flag is guaranteed
+					// to be zero at this point
+	ldp	$a0,$a1,[$tp,#8*0]
+	sub	$cnt,$np_end,$np	// done yet?
+	sub	$t2,$np_end,$num	// rewinded np
+	ldp	$a2,$a3,[$tp,#8*2]
+	ldp	$a4,$a5,[$tp,#8*4]
+	ldp	$a6,$a7,[$tp,#8*6]
+	cbz	$cnt,.Lsqr8x_tail_break
+
+	ldr	$n0,[$rp,#-8*8]
+	adds	$acc0,$acc0,$a0
+	adcs	$acc1,$acc1,$a1
+	ldp	$a0,$a1,[$np,#8*0]
+	adcs	$acc2,$acc2,$a2
+	adcs	$acc3,$acc3,$a3
+	ldp	$a2,$a3,[$np,#8*2]
+	adcs	$acc4,$acc4,$a4
+	adcs	$acc5,$acc5,$a5
+	ldp	$a4,$a5,[$np,#8*4]
+	adcs	$acc6,$acc6,$a6
+	mov	$cnt,#-8*8
+	adcs	$acc7,$acc7,$a7
+	ldp	$a6,$a7,[$np,#8*6]
+	add	$np,$np,#8*8
+	//adc	$carry,xzr,xzr		// moved above
+	b	.Lsqr8x_tail
+
+.align	4
+.Lsqr8x_tail_break:
+	ldr	$n0,[x29,#112]		// pull n0
+	add	$cnt,$tp,#8*8		// end of current t[num] window
+
+	subs	xzr,$topmost,#1		// "move" top-most carry to carry bit
+	adcs	$t0,$acc0,$a0
+	adcs	$t1,$acc1,$a1
+	ldp	$acc0,$acc1,[$rp,#8*0]
+	adcs	$acc2,$acc2,$a2
+	ldp	$a0,$a1,[$t2,#8*0]	// recall that $t2 is &n[0]
+	adcs	$acc3,$acc3,$a3
+	ldp	$a2,$a3,[$t2,#8*2]
+	adcs	$acc4,$acc4,$a4
+	adcs	$acc5,$acc5,$a5
+	ldp	$a4,$a5,[$t2,#8*4]
+	adcs	$acc6,$acc6,$a6
+	adcs	$acc7,$acc7,$a7
+	ldp	$a6,$a7,[$t2,#8*6]
+	add	$np,$t2,#8*8
+	adc	$topmost,xzr,xzr	// top-most carry
+	mul	$na0,$n0,$acc0
+	stp	$t0,$t1,[$tp,#8*0]
+	stp	$acc2,$acc3,[$tp,#8*2]
+	ldp	$acc2,$acc3,[$rp,#8*2]
+	stp	$acc4,$acc5,[$tp,#8*4]
+	ldp	$acc4,$acc5,[$rp,#8*4]
+	cmp	$cnt,x29		// did we hit the bottom?
+	stp	$acc6,$acc7,[$tp,#8*6]
+	mov	$tp,$rp			// slide the window
+	ldp	$acc6,$acc7,[$rp,#8*6]
+	mov	$cnt,#8
+	b.ne	.Lsqr8x_reduction
+
+	// Final step. We see if result is larger than modulus, and
+	// if it is, subtract the modulus. But comparison implies
+	// subtraction. So we subtract modulus, see if it borrowed,
+	// and conditionally copy original value.
+	ldr	$rp,[x29,#96]		// pull rp
+	add	$tp,$tp,#8*8
+	subs	$t0,$acc0,$a0
+	sbcs	$t1,$acc1,$a1
+	sub	$cnt,$num,#8*8
+	mov	$ap_end,$rp		// $rp copy
+
+.Lsqr8x_sub:
+	sbcs	$t2,$acc2,$a2
+	ldp	$a0,$a1,[$np,#8*0]
+	sbcs	$t3,$acc3,$a3
+	stp	$t0,$t1,[$rp,#8*0]
+	sbcs	$t0,$acc4,$a4
+	ldp	$a2,$a3,[$np,#8*2]
+	sbcs	$t1,$acc5,$a5
+	stp	$t2,$t3,[$rp,#8*2]
+	sbcs	$t2,$acc6,$a6
+	ldp	$a4,$a5,[$np,#8*4]
+	sbcs	$t3,$acc7,$a7
+	ldp	$a6,$a7,[$np,#8*6]
+	add	$np,$np,#8*8
+	ldp	$acc0,$acc1,[$tp,#8*0]
+	sub	$cnt,$cnt,#8*8
+	ldp	$acc2,$acc3,[$tp,#8*2]
+	ldp	$acc4,$acc5,[$tp,#8*4]
+	ldp	$acc6,$acc7,[$tp,#8*6]
+	add	$tp,$tp,#8*8
+	stp	$t0,$t1,[$rp,#8*4]
+	sbcs	$t0,$acc0,$a0
+	stp	$t2,$t3,[$rp,#8*6]
+	add	$rp,$rp,#8*8
+	sbcs	$t1,$acc1,$a1
+	cbnz	$cnt,.Lsqr8x_sub
+
+	sbcs	$t2,$acc2,$a2
+	 mov	$tp,sp
+	 add	$ap,sp,$num
+	 ldp	$a0,$a1,[$ap_end,#8*0]
+	sbcs	$t3,$acc3,$a3
+	stp	$t0,$t1,[$rp,#8*0]
+	sbcs	$t0,$acc4,$a4
+	 ldp	$a2,$a3,[$ap_end,#8*2]
+	sbcs	$t1,$acc5,$a5
+	stp	$t2,$t3,[$rp,#8*2]
+	sbcs	$t2,$acc6,$a6
+	 ldp	$acc0,$acc1,[$ap,#8*0]
+	sbcs	$t3,$acc7,$a7
+	 ldp	$acc2,$acc3,[$ap,#8*2]
+	sbcs	xzr,$topmost,xzr	// did it borrow?
+	ldr	x30,[x29,#8]		// pull return address
+	stp	$t0,$t1,[$rp,#8*4]
+	stp	$t2,$t3,[$rp,#8*6]
+
+	sub	$cnt,$num,#8*4
+.Lsqr4x_cond_copy:
+	sub	$cnt,$cnt,#8*4
+	csel	$t0,$acc0,$a0,lo
+	 stp	xzr,xzr,[$tp,#8*0]
+	csel	$t1,$acc1,$a1,lo
+	ldp	$a0,$a1,[$ap_end,#8*4]
+	ldp	$acc0,$acc1,[$ap,#8*4]
+	csel	$t2,$acc2,$a2,lo
+	 stp	xzr,xzr,[$tp,#8*2]
+	 add	$tp,$tp,#8*4
+	csel	$t3,$acc3,$a3,lo
+	ldp	$a2,$a3,[$ap_end,#8*6]
+	ldp	$acc2,$acc3,[$ap,#8*6]
+	add	$ap,$ap,#8*4
+	stp	$t0,$t1,[$ap_end,#8*0]
+	stp	$t2,$t3,[$ap_end,#8*2]
+	add	$ap_end,$ap_end,#8*4
+	 stp	xzr,xzr,[$ap,#8*0]
+	 stp	xzr,xzr,[$ap,#8*2]
+	cbnz	$cnt,.Lsqr4x_cond_copy
+
+	csel	$t0,$acc0,$a0,lo
+	 stp	xzr,xzr,[$tp,#8*0]
+	csel	$t1,$acc1,$a1,lo
+	 stp	xzr,xzr,[$tp,#8*2]
+	csel	$t2,$acc2,$a2,lo
+	csel	$t3,$acc3,$a3,lo
+	stp	$t0,$t1,[$ap_end,#8*0]
+	stp	$t2,$t3,[$ap_end,#8*2]
+
+	b	.Lsqr8x_done
+
+.align	4
+.Lsqr8x8_post_condition:
+	adc	$carry,xzr,xzr
+	ldr	x30,[x29,#8]		// pull return address
+	// $acc0-7,$carry hold result, $a0-7 hold modulus
+	subs	$a0,$acc0,$a0
+	ldr	$ap,[x29,#96]		// pull rp
+	sbcs	$a1,$acc1,$a1
+	 stp	xzr,xzr,[sp,#8*0]
+	sbcs	$a2,$acc2,$a2
+	 stp	xzr,xzr,[sp,#8*2]
+	sbcs	$a3,$acc3,$a3
+	 stp	xzr,xzr,[sp,#8*4]
+	sbcs	$a4,$acc4,$a4
+	 stp	xzr,xzr,[sp,#8*6]
+	sbcs	$a5,$acc5,$a5
+	 stp	xzr,xzr,[sp,#8*8]
+	sbcs	$a6,$acc6,$a6
+	 stp	xzr,xzr,[sp,#8*10]
+	sbcs	$a7,$acc7,$a7
+	 stp	xzr,xzr,[sp,#8*12]
+	sbcs	$carry,$carry,xzr	// did it borrow?
+	 stp	xzr,xzr,[sp,#8*14]
+
+	// $a0-7 hold result-modulus
+	csel	$a0,$acc0,$a0,lo
+	csel	$a1,$acc1,$a1,lo
+	csel	$a2,$acc2,$a2,lo
+	csel	$a3,$acc3,$a3,lo
+	stp	$a0,$a1,[$ap,#8*0]
+	csel	$a4,$acc4,$a4,lo
+	csel	$a5,$acc5,$a5,lo
+	stp	$a2,$a3,[$ap,#8*2]
+	csel	$a6,$acc6,$a6,lo
+	csel	$a7,$acc7,$a7,lo
+	stp	$a4,$a5,[$ap,#8*4]
+	stp	$a6,$a7,[$ap,#8*6]
+
+.Lsqr8x_done:
+	ldp	x19,x20,[x29,#16]
+	mov	sp,x29
+	ldp	x21,x22,[x29,#32]
+	mov	x0,#1
+	ldp	x23,x24,[x29,#48]
+	ldp	x25,x26,[x29,#64]
+	ldp	x27,x28,[x29,#80]
+	ldr	x29,[sp],#128
+	ret
+.size	__bn_sqr8x_mont,.-__bn_sqr8x_mont
+___
+}
+
+{
+########################################################################
+# Even though this might look as ARMv8 adaptation of mulx4x_mont from
+# x86_64-mont5 module, it's different in sense that it performs
+# reduction 256 bits at a time.
+
+my ($a0,$a1,$a2,$a3,
+    $t0,$t1,$t2,$t3,
+    $m0,$m1,$m2,$m3,
+    $acc0,$acc1,$acc2,$acc3,$acc4,
+    $bi,$mi,$tp,$ap_end,$cnt) = map("x$_",(6..17,19..28));
+my  $bp_end=$rp;
+my  ($carry,$topmost) = ($rp,"x30");
+
+$code.=<<___;
+.type	__bn_mul4x_mont,%function
+.align	5
+__bn_mul4x_mont:
+	stp	x29,x30,[sp,#-128]!
+	add	x29,sp,#0
+	stp	x19,x20,[sp,#16]
+	stp	x21,x22,[sp,#32]
+	stp	x23,x24,[sp,#48]
+	stp	x25,x26,[sp,#64]
+	stp	x27,x28,[sp,#80]
+
+	sub	$tp,sp,$num,lsl#3
+	lsl	$num,$num,#3
+	ldr	$n0,[$n0]		// *n0
+	sub	sp,$tp,#8*4		// alloca
+
+	add	$t0,$bp,$num
+	add	$ap_end,$ap,$num
+	stp	$rp,$t0,[x29,#96]	// offload rp and &b[num]
+
+	ldr	$bi,[$bp,#8*0]		// b[0]
+	ldp	$a0,$a1,[$ap,#8*0]	// a[0..3]
+	ldp	$a2,$a3,[$ap,#8*2]
+	add	$ap,$ap,#8*4
+	mov	$acc0,xzr
+	mov	$acc1,xzr
+	mov	$acc2,xzr
+	mov	$acc3,xzr
+	ldp	$m0,$m1,[$np,#8*0]	// n[0..3]
+	ldp	$m2,$m3,[$np,#8*2]
+	adds	$np,$np,#8*4		// clear carry bit
+	mov	$carry,xzr
+	mov	$cnt,#0
+	mov	$tp,sp
+
+.Loop_mul4x_1st_reduction:
+	mul	$t0,$a0,$bi		// lo(a[0..3]*b[0])
+	adc	$carry,$carry,xzr	// modulo-scheduled
+	mul	$t1,$a1,$bi
+	add	$cnt,$cnt,#8
+	mul	$t2,$a2,$bi
+	and	$cnt,$cnt,#31
+	mul	$t3,$a3,$bi
+	adds	$acc0,$acc0,$t0
+	umulh	$t0,$a0,$bi		// hi(a[0..3]*b[0])
+	adcs	$acc1,$acc1,$t1
+	mul	$mi,$acc0,$n0		// t[0]*n0
+	adcs	$acc2,$acc2,$t2
+	umulh	$t1,$a1,$bi
+	adcs	$acc3,$acc3,$t3
+	umulh	$t2,$a2,$bi
+	adc	$acc4,xzr,xzr
+	umulh	$t3,$a3,$bi
+	ldr	$bi,[$bp,$cnt]		// next b[i] (or b[0])
+	adds	$acc1,$acc1,$t0
+	// (*)	mul	$t0,$m0,$mi	// lo(n[0..3]*t[0]*n0)
+	str	$mi,[$tp],#8		// put aside t[0]*n0 for tail processing
+	adcs	$acc2,$acc2,$t1
+	mul	$t1,$m1,$mi
+	adcs	$acc3,$acc3,$t2
+	mul	$t2,$m2,$mi
+	adc	$acc4,$acc4,$t3		// can't overflow
+	mul	$t3,$m3,$mi
+	// (*)	adds	xzr,$acc0,$t0
+	subs	xzr,$acc0,#1		// (*)
+	umulh	$t0,$m0,$mi		// hi(n[0..3]*t[0]*n0)
+	adcs	$acc0,$acc1,$t1
+	umulh	$t1,$m1,$mi
+	adcs	$acc1,$acc2,$t2
+	umulh	$t2,$m2,$mi
+	adcs	$acc2,$acc3,$t3
+	umulh	$t3,$m3,$mi
+	adcs	$acc3,$acc4,$carry
+	adc	$carry,xzr,xzr
+	adds	$acc0,$acc0,$t0
+	sub	$t0,$ap_end,$ap
+	adcs	$acc1,$acc1,$t1
+	adcs	$acc2,$acc2,$t2
+	adcs	$acc3,$acc3,$t3
+	//adc	$carry,$carry,xzr
+	cbnz	$cnt,.Loop_mul4x_1st_reduction
+
+	cbz	$t0,.Lmul4x4_post_condition
+
+	ldp	$a0,$a1,[$ap,#8*0]	// a[4..7]
+	ldp	$a2,$a3,[$ap,#8*2]
+	add	$ap,$ap,#8*4
+	ldr	$mi,[sp]		// a[0]*n0
+	ldp	$m0,$m1,[$np,#8*0]	// n[4..7]
+	ldp	$m2,$m3,[$np,#8*2]
+	add	$np,$np,#8*4
+
+.Loop_mul4x_1st_tail:
+	mul	$t0,$a0,$bi		// lo(a[4..7]*b[i])
+	adc	$carry,$carry,xzr	// modulo-scheduled
+	mul	$t1,$a1,$bi
+	add	$cnt,$cnt,#8
+	mul	$t2,$a2,$bi
+	and	$cnt,$cnt,#31
+	mul	$t3,$a3,$bi
+	adds	$acc0,$acc0,$t0
+	umulh	$t0,$a0,$bi		// hi(a[4..7]*b[i])
+	adcs	$acc1,$acc1,$t1
+	umulh	$t1,$a1,$bi
+	adcs	$acc2,$acc2,$t2
+	umulh	$t2,$a2,$bi
+	adcs	$acc3,$acc3,$t3
+	umulh	$t3,$a3,$bi
+	adc	$acc4,xzr,xzr
+	ldr	$bi,[$bp,$cnt]		// next b[i] (or b[0])
+	adds	$acc1,$acc1,$t0
+	mul	$t0,$m0,$mi		// lo(n[4..7]*a[0]*n0)
+	adcs	$acc2,$acc2,$t1
+	mul	$t1,$m1,$mi
+	adcs	$acc3,$acc3,$t2
+	mul	$t2,$m2,$mi
+	adc	$acc4,$acc4,$t3		// can't overflow
+	mul	$t3,$m3,$mi
+	adds	$acc0,$acc0,$t0
+	umulh	$t0,$m0,$mi		// hi(n[4..7]*a[0]*n0)
+	adcs	$acc1,$acc1,$t1
+	umulh	$t1,$m1,$mi
+	adcs	$acc2,$acc2,$t2
+	umulh	$t2,$m2,$mi
+	adcs	$acc3,$acc3,$t3
+	adcs	$acc4,$acc4,$carry
+	umulh	$t3,$m3,$mi
+	adc	$carry,xzr,xzr
+	ldr	$mi,[sp,$cnt]		// next t[0]*n0
+	str	$acc0,[$tp],#8		// result!!!
+	adds	$acc0,$acc1,$t0
+	sub	$t0,$ap_end,$ap		// done yet?
+	adcs	$acc1,$acc2,$t1
+	adcs	$acc2,$acc3,$t2
+	adcs	$acc3,$acc4,$t3
+	//adc	$carry,$carry,xzr
+	cbnz	$cnt,.Loop_mul4x_1st_tail
+
+	sub	$t1,$ap_end,$num	// rewinded $ap
+	cbz	$t0,.Lmul4x_proceed
+
+	ldp	$a0,$a1,[$ap,#8*0]
+	ldp	$a2,$a3,[$ap,#8*2]
+	add	$ap,$ap,#8*4
+	ldp	$m0,$m1,[$np,#8*0]
+	ldp	$m2,$m3,[$np,#8*2]
+	add	$np,$np,#8*4
+	b	.Loop_mul4x_1st_tail
+
+.align	5
+.Lmul4x_proceed:
+	ldr	$bi,[$bp,#8*4]!		// *++b
+	adc	$topmost,$carry,xzr
+	ldp	$a0,$a1,[$t1,#8*0]	// a[0..3]
+	sub	$np,$np,$num		// rewind np
+	ldp	$a2,$a3,[$t1,#8*2]
+	add	$ap,$t1,#8*4
+
+	stp	$acc0,$acc1,[$tp,#8*0]	// result!!!
+	ldp	$acc0,$acc1,[sp,#8*4]	// t[0..3]
+	stp	$acc2,$acc3,[$tp,#8*2]	// result!!!
+	ldp	$acc2,$acc3,[sp,#8*6]
+
+	ldp	$m0,$m1,[$np,#8*0]	// n[0..3]
+	mov	$tp,sp
+	ldp	$m2,$m3,[$np,#8*2]
+	adds	$np,$np,#8*4		// clear carry bit
+	mov	$carry,xzr
+
+.align	4
+.Loop_mul4x_reduction:
+	mul	$t0,$a0,$bi		// lo(a[0..3]*b[4])
+	adc	$carry,$carry,xzr	// modulo-scheduled
+	mul	$t1,$a1,$bi
+	add	$cnt,$cnt,#8
+	mul	$t2,$a2,$bi
+	and	$cnt,$cnt,#31
+	mul	$t3,$a3,$bi
+	adds	$acc0,$acc0,$t0
+	umulh	$t0,$a0,$bi		// hi(a[0..3]*b[4])
+	adcs	$acc1,$acc1,$t1
+	mul	$mi,$acc0,$n0		// t[0]*n0
+	adcs	$acc2,$acc2,$t2
+	umulh	$t1,$a1,$bi
+	adcs	$acc3,$acc3,$t3
+	umulh	$t2,$a2,$bi
+	adc	$acc4,xzr,xzr
+	umulh	$t3,$a3,$bi
+	ldr	$bi,[$bp,$cnt]		// next b[i]
+	adds	$acc1,$acc1,$t0
+	// (*)	mul	$t0,$m0,$mi
+	str	$mi,[$tp],#8		// put aside t[0]*n0 for tail processing
+	adcs	$acc2,$acc2,$t1
+	mul	$t1,$m1,$mi		// lo(n[0..3]*t[0]*n0
+	adcs	$acc3,$acc3,$t2
+	mul	$t2,$m2,$mi
+	adc	$acc4,$acc4,$t3		// can't overflow
+	mul	$t3,$m3,$mi
+	// (*)	adds	xzr,$acc0,$t0
+	subs	xzr,$acc0,#1		// (*)
+	umulh	$t0,$m0,$mi		// hi(n[0..3]*t[0]*n0
+	adcs	$acc0,$acc1,$t1
+	umulh	$t1,$m1,$mi
+	adcs	$acc1,$acc2,$t2
+	umulh	$t2,$m2,$mi
+	adcs	$acc2,$acc3,$t3
+	umulh	$t3,$m3,$mi
+	adcs	$acc3,$acc4,$carry
+	adc	$carry,xzr,xzr
+	adds	$acc0,$acc0,$t0
+	adcs	$acc1,$acc1,$t1
+	adcs	$acc2,$acc2,$t2
+	adcs	$acc3,$acc3,$t3
+	//adc	$carry,$carry,xzr
+	cbnz	$cnt,.Loop_mul4x_reduction
+
+	adc	$carry,$carry,xzr
+	ldp	$t0,$t1,[$tp,#8*4]	// t[4..7]
+	ldp	$t2,$t3,[$tp,#8*6]
+	ldp	$a0,$a1,[$ap,#8*0]	// a[4..7]
+	ldp	$a2,$a3,[$ap,#8*2]
+	add	$ap,$ap,#8*4
+	adds	$acc0,$acc0,$t0
+	adcs	$acc1,$acc1,$t1
+	adcs	$acc2,$acc2,$t2
+	adcs	$acc3,$acc3,$t3
+	//adc	$carry,$carry,xzr
+
+	ldr	$mi,[sp]		// t[0]*n0
+	ldp	$m0,$m1,[$np,#8*0]	// n[4..7]
+	ldp	$m2,$m3,[$np,#8*2]
+	add	$np,$np,#8*4
+
+.align	4
+.Loop_mul4x_tail:
+	mul	$t0,$a0,$bi		// lo(a[4..7]*b[4])
+	adc	$carry,$carry,xzr	// modulo-scheduled
+	mul	$t1,$a1,$bi
+	add	$cnt,$cnt,#8
+	mul	$t2,$a2,$bi
+	and	$cnt,$cnt,#31
+	mul	$t3,$a3,$bi
+	adds	$acc0,$acc0,$t0
+	umulh	$t0,$a0,$bi		// hi(a[4..7]*b[4])
+	adcs	$acc1,$acc1,$t1
+	umulh	$t1,$a1,$bi
+	adcs	$acc2,$acc2,$t2
+	umulh	$t2,$a2,$bi
+	adcs	$acc3,$acc3,$t3
+	umulh	$t3,$a3,$bi
+	adc	$acc4,xzr,xzr
+	ldr	$bi,[$bp,$cnt]		// next b[i]
+	adds	$acc1,$acc1,$t0
+	mul	$t0,$m0,$mi		// lo(n[4..7]*t[0]*n0)
+	adcs	$acc2,$acc2,$t1
+	mul	$t1,$m1,$mi
+	adcs	$acc3,$acc3,$t2
+	mul	$t2,$m2,$mi
+	adc	$acc4,$acc4,$t3		// can't overflow
+	mul	$t3,$m3,$mi
+	adds	$acc0,$acc0,$t0
+	umulh	$t0,$m0,$mi		// hi(n[4..7]*t[0]*n0)
+	adcs	$acc1,$acc1,$t1
+	umulh	$t1,$m1,$mi
+	adcs	$acc2,$acc2,$t2
+	umulh	$t2,$m2,$mi
+	adcs	$acc3,$acc3,$t3
+	umulh	$t3,$m3,$mi
+	adcs	$acc4,$acc4,$carry
+	ldr	$mi,[sp,$cnt]		// next a[0]*n0
+	adc	$carry,xzr,xzr
+	str	$acc0,[$tp],#8		// result!!!
+	adds	$acc0,$acc1,$t0
+	sub	$t0,$ap_end,$ap		// done yet?
+	adcs	$acc1,$acc2,$t1
+	adcs	$acc2,$acc3,$t2
+	adcs	$acc3,$acc4,$t3
+	//adc	$carry,$carry,xzr
+	cbnz	$cnt,.Loop_mul4x_tail
+
+	sub	$t1,$np,$num		// rewinded np?
+	adc	$carry,$carry,xzr
+	cbz	$t0,.Loop_mul4x_break
+
+	ldp	$t0,$t1,[$tp,#8*4]
+	ldp	$t2,$t3,[$tp,#8*6]
+	ldp	$a0,$a1,[$ap,#8*0]
+	ldp	$a2,$a3,[$ap,#8*2]
+	add	$ap,$ap,#8*4
+	adds	$acc0,$acc0,$t0
+	adcs	$acc1,$acc1,$t1
+	adcs	$acc2,$acc2,$t2
+	adcs	$acc3,$acc3,$t3
+	//adc	$carry,$carry,xzr
+	ldp	$m0,$m1,[$np,#8*0]
+	ldp	$m2,$m3,[$np,#8*2]
+	add	$np,$np,#8*4
+	b	.Loop_mul4x_tail
+
+.align	4
+.Loop_mul4x_break:
+	ldp	$t2,$t3,[x29,#96]	// pull rp and &b[num]
+	adds	$acc0,$acc0,$topmost
+	add	$bp,$bp,#8*4		// bp++
+	adcs	$acc1,$acc1,xzr
+	sub	$ap,$ap,$num		// rewind ap
+	adcs	$acc2,$acc2,xzr
+	stp	$acc0,$acc1,[$tp,#8*0]	// result!!!
+	adcs	$acc3,$acc3,xzr
+	ldp	$acc0,$acc1,[sp,#8*4]	// t[0..3]
+	adc	$topmost,$carry,xzr
+	stp	$acc2,$acc3,[$tp,#8*2]	// result!!!
+	cmp	$bp,$t3			// done yet?
+	ldp	$acc2,$acc3,[sp,#8*6]
+	ldp	$m0,$m1,[$t1,#8*0]	// n[0..3]
+	ldp	$m2,$m3,[$t1,#8*2]
+	add	$np,$t1,#8*4
+	b.eq	.Lmul4x_post
+
+	ldr	$bi,[$bp]
+	ldp	$a0,$a1,[$ap,#8*0]	// a[0..3]
+	ldp	$a2,$a3,[$ap,#8*2]
+	adds	$ap,$ap,#8*4		// clear carry bit
+	mov	$carry,xzr
+	mov	$tp,sp
+	b	.Loop_mul4x_reduction
+
+.align	4
+.Lmul4x_post:
+	// Final step. We see if result is larger than modulus, and
+	// if it is, subtract the modulus. But comparison implies
+	// subtraction. So we subtract modulus, see if it borrowed,
+	// and conditionally copy original value.
+	mov	$rp,$t2
+	mov	$ap_end,$t2		// $rp copy
+	subs	$t0,$acc0,$m0
+	add	$tp,sp,#8*8
+	sbcs	$t1,$acc1,$m1
+	sub	$cnt,$num,#8*4
+
+.Lmul4x_sub:
+	sbcs	$t2,$acc2,$m2
+	ldp	$m0,$m1,[$np,#8*0]
+	sub	$cnt,$cnt,#8*4
+	ldp	$acc0,$acc1,[$tp,#8*0]
+	sbcs	$t3,$acc3,$m3
+	ldp	$m2,$m3,[$np,#8*2]
+	add	$np,$np,#8*4
+	ldp	$acc2,$acc3,[$tp,#8*2]
+	add	$tp,$tp,#8*4
+	stp	$t0,$t1,[$rp,#8*0]
+	sbcs	$t0,$acc0,$m0
+	stp	$t2,$t3,[$rp,#8*2]
+	add	$rp,$rp,#8*4
+	sbcs	$t1,$acc1,$m1
+	cbnz	$cnt,.Lmul4x_sub
+
+	sbcs	$t2,$acc2,$m2
+	 mov	$tp,sp
+	 add	$ap,sp,#8*4
+	 ldp	$a0,$a1,[$ap_end,#8*0]
+	sbcs	$t3,$acc3,$m3
+	stp	$t0,$t1,[$rp,#8*0]
+	 ldp	$a2,$a3,[$ap_end,#8*2]
+	stp	$t2,$t3,[$rp,#8*2]
+	 ldp	$acc0,$acc1,[$ap,#8*0]
+	 ldp	$acc2,$acc3,[$ap,#8*2]
+	sbcs	xzr,$topmost,xzr	// did it borrow?
+	ldr	x30,[x29,#8]		// pull return address
+
+	sub	$cnt,$num,#8*4
+.Lmul4x_cond_copy:
+	sub	$cnt,$cnt,#8*4
+	csel	$t0,$acc0,$a0,lo
+	 stp	xzr,xzr,[$tp,#8*0]
+	csel	$t1,$acc1,$a1,lo
+	ldp	$a0,$a1,[$ap_end,#8*4]
+	ldp	$acc0,$acc1,[$ap,#8*4]
+	csel	$t2,$acc2,$a2,lo
+	 stp	xzr,xzr,[$tp,#8*2]
+	 add	$tp,$tp,#8*4
+	csel	$t3,$acc3,$a3,lo
+	ldp	$a2,$a3,[$ap_end,#8*6]
+	ldp	$acc2,$acc3,[$ap,#8*6]
+	add	$ap,$ap,#8*4
+	stp	$t0,$t1,[$ap_end,#8*0]
+	stp	$t2,$t3,[$ap_end,#8*2]
+	add	$ap_end,$ap_end,#8*4
+	cbnz	$cnt,.Lmul4x_cond_copy
+
+	csel	$t0,$acc0,$a0,lo
+	 stp	xzr,xzr,[$tp,#8*0]
+	csel	$t1,$acc1,$a1,lo
+	 stp	xzr,xzr,[$tp,#8*2]
+	csel	$t2,$acc2,$a2,lo
+	 stp	xzr,xzr,[$tp,#8*3]
+	csel	$t3,$acc3,$a3,lo
+	 stp	xzr,xzr,[$tp,#8*4]
+	stp	$t0,$t1,[$ap_end,#8*0]
+	stp	$t2,$t3,[$ap_end,#8*2]
+
+	b	.Lmul4x_done
+
+.align	4
+.Lmul4x4_post_condition:
+	adc	$carry,$carry,xzr
+	ldr	$ap,[x29,#96]		// pull rp
+	// $acc0-3,$carry hold result, $m0-7 hold modulus
+	subs	$a0,$acc0,$m0
+	ldr	x30,[x29,#8]		// pull return address
+	sbcs	$a1,$acc1,$m1
+	 stp	xzr,xzr,[sp,#8*0]
+	sbcs	$a2,$acc2,$m2
+	 stp	xzr,xzr,[sp,#8*2]
+	sbcs	$a3,$acc3,$m3
+	 stp	xzr,xzr,[sp,#8*4]
+	sbcs	xzr,$carry,xzr		// did it borrow?
+	 stp	xzr,xzr,[sp,#8*6]
+
+	// $a0-3 hold result-modulus
+	csel	$a0,$acc0,$a0,lo
+	csel	$a1,$acc1,$a1,lo
+	csel	$a2,$acc2,$a2,lo
+	csel	$a3,$acc3,$a3,lo
+	stp	$a0,$a1,[$ap,#8*0]
+	stp	$a2,$a3,[$ap,#8*2]
+
+.Lmul4x_done:
+	ldp	x19,x20,[x29,#16]
+	mov	sp,x29
+	ldp	x21,x22,[x29,#32]
+	mov	x0,#1
+	ldp	x23,x24,[x29,#48]
+	ldp	x25,x26,[x29,#64]
+	ldp	x27,x28,[x29,#80]
+	ldr	x29,[sp],#128
+	ret
+.size	__bn_mul4x_mont,.-__bn_mul4x_mont
+___
+}
+$code.=<<___;
+.asciz	"Montgomery Multiplication for ARMv8, CRYPTOGAMS by "
+.align	4
+___
+
+print $code;
+
+close STDOUT;
diff --git a/crypto/bn/asm/bn-586.pl b/crypto/bn/asm/bn-586.pl
new file mode 100644
index 000000000..1ca1bbf7d
--- /dev/null
+++ b/crypto/bn/asm/bn-586.pl
@@ -0,0 +1,785 @@
+#! /usr/bin/env perl
+# Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+
+$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+push(@INC,"${dir}","${dir}../../perlasm");
+require "x86asm.pl";
+
+$output = pop;
+open STDOUT,">$output";
+
+&asm_init($ARGV[0],$0);
+
+$sse2=0;
+for (@ARGV) { $sse2=1 if (/-DOPENSSL_IA32_SSE2/); }
+
+&external_label("OPENSSL_ia32cap_P") if ($sse2);
+
+&bn_mul_add_words("bn_mul_add_words");
+&bn_mul_words("bn_mul_words");
+&bn_sqr_words("bn_sqr_words");
+&bn_div_words("bn_div_words");
+&bn_add_words("bn_add_words");
+&bn_sub_words("bn_sub_words");
+&bn_sub_part_words("bn_sub_part_words");
+
+&asm_finish();
+
+close STDOUT;
+
+sub bn_mul_add_words
+	{
+	local($name)=@_;
+
+	&function_begin_B($name,$sse2?"EXTRN\t_OPENSSL_ia32cap_P:DWORD":"");
+
+	$r="eax";
+	$a="edx";
+	$c="ecx";
+
+	if ($sse2) {
+		&picmeup("eax","OPENSSL_ia32cap_P");
+		&bt(&DWP(0,"eax"),26);
+		&jnc(&label("maw_non_sse2"));
+
+		&mov($r,&wparam(0));
+		&mov($a,&wparam(1));
+		&mov($c,&wparam(2));
+		&movd("mm0",&wparam(3));	# mm0 = w
+		&pxor("mm1","mm1");		# mm1 = carry_in
+		&jmp(&label("maw_sse2_entry"));
+		
+	&set_label("maw_sse2_unrolled",16);
+		&movd("mm3",&DWP(0,$r,"",0));	# mm3 = r[0]
+		&paddq("mm1","mm3");		# mm1 = carry_in + r[0]
+		&movd("mm2",&DWP(0,$a,"",0));	# mm2 = a[0]
+		&pmuludq("mm2","mm0");		# mm2 = w*a[0]
+		&movd("mm4",&DWP(4,$a,"",0));	# mm4 = a[1]
+		&pmuludq("mm4","mm0");		# mm4 = w*a[1]
+		&movd("mm6",&DWP(8,$a,"",0));	# mm6 = a[2]
+		&pmuludq("mm6","mm0");		# mm6 = w*a[2]
+		&movd("mm7",&DWP(12,$a,"",0));	# mm7 = a[3]
+		&pmuludq("mm7","mm0");		# mm7 = w*a[3]
+		&paddq("mm1","mm2");		# mm1 = carry_in + r[0] + w*a[0]
+		&movd("mm3",&DWP(4,$r,"",0));	# mm3 = r[1]
+		&paddq("mm3","mm4");		# mm3 = r[1] + w*a[1]
+		&movd("mm5",&DWP(8,$r,"",0));	# mm5 = r[2]
+		&paddq("mm5","mm6");		# mm5 = r[2] + w*a[2]
+		&movd("mm4",&DWP(12,$r,"",0));	# mm4 = r[3]
+		&paddq("mm7","mm4");		# mm7 = r[3] + w*a[3]
+		&movd(&DWP(0,$r,"",0),"mm1");
+		&movd("mm2",&DWP(16,$a,"",0));	# mm2 = a[4]
+		&pmuludq("mm2","mm0");		# mm2 = w*a[4]
+		&psrlq("mm1",32);		# mm1 = carry0
+		&movd("mm4",&DWP(20,$a,"",0));	# mm4 = a[5]
+		&pmuludq("mm4","mm0");		# mm4 = w*a[5]
+		&paddq("mm1","mm3");		# mm1 = carry0 + r[1] + w*a[1]
+		&movd("mm6",&DWP(24,$a,"",0));	# mm6 = a[6]
+		&pmuludq("mm6","mm0");		# mm6 = w*a[6]
+		&movd(&DWP(4,$r,"",0),"mm1");
+		&psrlq("mm1",32);		# mm1 = carry1
+		&movd("mm3",&DWP(28,$a,"",0));	# mm3 = a[7]
+		&add($a,32);
+		&pmuludq("mm3","mm0");		# mm3 = w*a[7]
+		&paddq("mm1","mm5");		# mm1 = carry1 + r[2] + w*a[2]
+		&movd("mm5",&DWP(16,$r,"",0));	# mm5 = r[4]
+		&paddq("mm2","mm5");		# mm2 = r[4] + w*a[4]
+		&movd(&DWP(8,$r,"",0),"mm1");
+		&psrlq("mm1",32);		# mm1 = carry2
+		&paddq("mm1","mm7");		# mm1 = carry2 + r[3] + w*a[3]
+		&movd("mm5",&DWP(20,$r,"",0));	# mm5 = r[5]
+		&paddq("mm4","mm5");		# mm4 = r[5] + w*a[5]
+		&movd(&DWP(12,$r,"",0),"mm1");
+		&psrlq("mm1",32);		# mm1 = carry3
+		&paddq("mm1","mm2");		# mm1 = carry3 + r[4] + w*a[4]
+		&movd("mm5",&DWP(24,$r,"",0));	# mm5 = r[6]
+		&paddq("mm6","mm5");		# mm6 = r[6] + w*a[6]
+		&movd(&DWP(16,$r,"",0),"mm1");
+		&psrlq("mm1",32);		# mm1 = carry4
+		&paddq("mm1","mm4");		# mm1 = carry4 + r[5] + w*a[5]
+		&movd("mm5",&DWP(28,$r,"",0));	# mm5 = r[7]
+		&paddq("mm3","mm5");		# mm3 = r[7] + w*a[7]
+		&movd(&DWP(20,$r,"",0),"mm1");
+		&psrlq("mm1",32);		# mm1 = carry5
+		&paddq("mm1","mm6");		# mm1 = carry5 + r[6] + w*a[6]
+		&movd(&DWP(24,$r,"",0),"mm1");
+		&psrlq("mm1",32);		# mm1 = carry6
+		&paddq("mm1","mm3");		# mm1 = carry6 + r[7] + w*a[7]
+		&movd(&DWP(28,$r,"",0),"mm1");
+		&lea($r,&DWP(32,$r));
+		&psrlq("mm1",32);		# mm1 = carry_out
+
+		&sub($c,8);
+		&jz(&label("maw_sse2_exit"));
+	&set_label("maw_sse2_entry");
+		&test($c,0xfffffff8);
+		&jnz(&label("maw_sse2_unrolled"));
+
+	&set_label("maw_sse2_loop",4);
+		&movd("mm2",&DWP(0,$a));	# mm2 = a[i]
+		&movd("mm3",&DWP(0,$r));	# mm3 = r[i]
+		&pmuludq("mm2","mm0");		# a[i] *= w
+		&lea($a,&DWP(4,$a));
+		&paddq("mm1","mm3");		# carry += r[i]
+		&paddq("mm1","mm2");		# carry += a[i]*w
+		&movd(&DWP(0,$r),"mm1");	# r[i] = carry_low
+		&sub($c,1);
+		&psrlq("mm1",32);		# carry = carry_high
+		&lea($r,&DWP(4,$r));
+		&jnz(&label("maw_sse2_loop"));
+	&set_label("maw_sse2_exit");
+		&movd("eax","mm1");		# c = carry_out
+		&emms();
+		&ret();
+
+	&set_label("maw_non_sse2",16);
+	}
+
+	# function_begin prologue
+	&push("ebp");
+	&push("ebx");
+	&push("esi");
+	&push("edi");
+
+	&comment("");
+	$Low="eax";
+	$High="edx";
+	$a="ebx";
+	$w="ebp";
+	$r="edi";
+	$c="esi";
+
+	&xor($c,$c);		# clear carry
+	&mov($r,&wparam(0));	#
+
+	&mov("ecx",&wparam(2));	#
+	&mov($a,&wparam(1));	#
+
+	&and("ecx",0xfffffff8);	# num / 8
+	&mov($w,&wparam(3));	#
+
+	&push("ecx");		# Up the stack for a tmp variable
+
+	&jz(&label("maw_finish"));
+
+	&set_label("maw_loop",16);
+
+	for ($i=0; $i<32; $i+=4)
+		{
+		&comment("Round $i");
+
+		 &mov("eax",&DWP($i,$a)); 	# *a
+		&mul($w);			# *a * w
+		&add("eax",$c);			# L(t)+= c
+		&adc("edx",0);			# H(t)+=carry
+		 &add("eax",&DWP($i,$r));	# L(t)+= *r
+		&adc("edx",0);			# H(t)+=carry
+		 &mov(&DWP($i,$r),"eax");	# *r= L(t);
+		&mov($c,"edx");			# c=  H(t);
+		}
+
+	&comment("");
+	&sub("ecx",8);
+	&lea($a,&DWP(32,$a));
+	&lea($r,&DWP(32,$r));
+	&jnz(&label("maw_loop"));
+
+	&set_label("maw_finish",0);
+	&mov("ecx",&wparam(2));	# get num
+	&and("ecx",7);
+	&jnz(&label("maw_finish2"));	# helps branch prediction
+	&jmp(&label("maw_end"));
+
+	&set_label("maw_finish2",1);
+	for ($i=0; $i<7; $i++)
+		{
+		&comment("Tail Round $i");
+		 &mov("eax",&DWP($i*4,$a));	# *a
+		&mul($w);			# *a * w
+		&add("eax",$c);			# L(t)+=c
+		&adc("edx",0);			# H(t)+=carry
+		 &add("eax",&DWP($i*4,$r));	# L(t)+= *r
+		&adc("edx",0);			# H(t)+=carry
+		 &dec("ecx") if ($i != 7-1);
+		&mov(&DWP($i*4,$r),"eax");	# *r= L(t);
+		 &mov($c,"edx");		# c=  H(t);
+		&jz(&label("maw_end")) if ($i != 7-1);
+		}
+	&set_label("maw_end",0);
+	&mov("eax",$c);
+
+	&pop("ecx");	# clear variable from
+
+	&function_end($name);
+	}
+
+sub bn_mul_words
+	{
+	local($name)=@_;
+
+	&function_begin_B($name,$sse2?"EXTRN\t_OPENSSL_ia32cap_P:DWORD":"");
+
+	$r="eax";
+	$a="edx";
+	$c="ecx";
+
+	if ($sse2) {
+		&picmeup("eax","OPENSSL_ia32cap_P");
+		&bt(&DWP(0,"eax"),26);
+		&jnc(&label("mw_non_sse2"));
+
+		&mov($r,&wparam(0));
+		&mov($a,&wparam(1));
+		&mov($c,&wparam(2));
+		&movd("mm0",&wparam(3));	# mm0 = w
+		&pxor("mm1","mm1");		# mm1 = carry = 0
+
+	&set_label("mw_sse2_loop",16);
+		&movd("mm2",&DWP(0,$a));	# mm2 = a[i]
+		&pmuludq("mm2","mm0");		# a[i] *= w
+		&lea($a,&DWP(4,$a));
+		&paddq("mm1","mm2");		# carry += a[i]*w
+		&movd(&DWP(0,$r),"mm1");	# r[i] = carry_low
+		&sub($c,1);
+		&psrlq("mm1",32);		# carry = carry_high
+		&lea($r,&DWP(4,$r));
+		&jnz(&label("mw_sse2_loop"));
+
+		&movd("eax","mm1");		# return carry
+		&emms();
+		&ret();
+	&set_label("mw_non_sse2",16);
+	}
+
+	# function_begin prologue
+	&push("ebp");
+	&push("ebx");
+	&push("esi");
+	&push("edi");
+
+	&comment("");
+	$Low="eax";
+	$High="edx";
+	$a="ebx";
+	$w="ecx";
+	$r="edi";
+	$c="esi";
+	$num="ebp";
+
+	&xor($c,$c);		# clear carry
+	&mov($r,&wparam(0));	#
+	&mov($a,&wparam(1));	#
+	&mov($num,&wparam(2));	#
+	&mov($w,&wparam(3));	#
+
+	&and($num,0xfffffff8);	# num / 8
+	&jz(&label("mw_finish"));
+
+	&set_label("mw_loop",0);
+	for ($i=0; $i<32; $i+=4)
+		{
+		&comment("Round $i");
+
+		 &mov("eax",&DWP($i,$a,"",0)); 	# *a
+		&mul($w);			# *a * w
+		&add("eax",$c);			# L(t)+=c
+		 # XXX
+
+		&adc("edx",0);			# H(t)+=carry
+		 &mov(&DWP($i,$r,"",0),"eax");	# *r= L(t);
+
+		&mov($c,"edx");			# c=  H(t);
+		}
+
+	&comment("");
+	&add($a,32);
+	&add($r,32);
+	&sub($num,8);
+	&jz(&label("mw_finish"));
+	&jmp(&label("mw_loop"));
+
+	&set_label("mw_finish",0);
+	&mov($num,&wparam(2));	# get num
+	&and($num,7);
+	&jnz(&label("mw_finish2"));
+	&jmp(&label("mw_end"));
+
+	&set_label("mw_finish2",1);
+	for ($i=0; $i<7; $i++)
+		{
+		&comment("Tail Round $i");
+		 &mov("eax",&DWP($i*4,$a,"",0));# *a
+		&mul($w);			# *a * w
+		&add("eax",$c);			# L(t)+=c
+		 # XXX
+		&adc("edx",0);			# H(t)+=carry
+		 &mov(&DWP($i*4,$r,"",0),"eax");# *r= L(t);
+		&mov($c,"edx");			# c=  H(t);
+		 &dec($num) if ($i != 7-1);
+		&jz(&label("mw_end")) if ($i != 7-1);
+		}
+	&set_label("mw_end",0);
+	&mov("eax",$c);
+
+	&function_end($name);
+	}
+
+sub bn_sqr_words
+	{
+	local($name)=@_;
+
+	&function_begin_B($name,$sse2?"EXTRN\t_OPENSSL_ia32cap_P:DWORD":"");
+
+	$r="eax";
+	$a="edx";
+	$c="ecx";
+
+	if ($sse2) {
+		&picmeup("eax","OPENSSL_ia32cap_P");
+		&bt(&DWP(0,"eax"),26);
+		&jnc(&label("sqr_non_sse2"));
+
+		&mov($r,&wparam(0));
+		&mov($a,&wparam(1));
+		&mov($c,&wparam(2));
+
+	&set_label("sqr_sse2_loop",16);
+		&movd("mm0",&DWP(0,$a));	# mm0 = a[i]
+		&pmuludq("mm0","mm0");		# a[i] *= a[i]
+		&lea($a,&DWP(4,$a));		# a++
+		&movq(&QWP(0,$r),"mm0");	# r[i] = a[i]*a[i]
+		&sub($c,1);
+		&lea($r,&DWP(8,$r));		# r += 2
+		&jnz(&label("sqr_sse2_loop"));
+
+		&emms();
+		&ret();
+	&set_label("sqr_non_sse2",16);
+	}
+
+	# function_begin prologue
+	&push("ebp");
+	&push("ebx");
+	&push("esi");
+	&push("edi");
+
+	&comment("");
+	$r="esi";
+	$a="edi";
+	$num="ebx";
+
+	&mov($r,&wparam(0));	#
+	&mov($a,&wparam(1));	#
+	&mov($num,&wparam(2));	#
+
+	&and($num,0xfffffff8);	# num / 8
+	&jz(&label("sw_finish"));
+
+	&set_label("sw_loop",0);
+	for ($i=0; $i<32; $i+=4)
+		{
+		&comment("Round $i");
+		&mov("eax",&DWP($i,$a,"",0)); 	# *a
+		 # XXX
+		&mul("eax");			# *a * *a
+		&mov(&DWP($i*2,$r,"",0),"eax");	#
+		 &mov(&DWP($i*2+4,$r,"",0),"edx");#
+		}
+
+	&comment("");
+	&add($a,32);
+	&add($r,64);
+	&sub($num,8);
+	&jnz(&label("sw_loop"));
+
+	&set_label("sw_finish",0);
+	&mov($num,&wparam(2));	# get num
+	&and($num,7);
+	&jz(&label("sw_end"));
+
+	for ($i=0; $i<7; $i++)
+		{
+		&comment("Tail Round $i");
+		&mov("eax",&DWP($i*4,$a,"",0));	# *a
+		 # XXX
+		&mul("eax");			# *a * *a
+		&mov(&DWP($i*8,$r,"",0),"eax");	#
+		 &dec($num) if ($i != 7-1);
+		&mov(&DWP($i*8+4,$r,"",0),"edx");
+		 &jz(&label("sw_end")) if ($i != 7-1);
+		}
+	&set_label("sw_end",0);
+
+	&function_end($name);
+	}
+
+sub bn_div_words
+	{
+	local($name)=@_;
+
+	&function_begin_B($name,"");
+	&mov("edx",&wparam(0));	#
+	&mov("eax",&wparam(1));	#
+	&mov("ecx",&wparam(2));	#
+	&div("ecx");
+	&ret();
+	&function_end_B($name);
+	}
+
+sub bn_add_words
+	{
+	local($name)=@_;
+
+	&function_begin($name,"");
+
+	&comment("");
+	$a="esi";
+	$b="edi";
+	$c="eax";
+	$r="ebx";
+	$tmp1="ecx";
+	$tmp2="edx";
+	$num="ebp";
+
+	&mov($r,&wparam(0));	# get r
+	 &mov($a,&wparam(1));	# get a
+	&mov($b,&wparam(2));	# get b
+	 &mov($num,&wparam(3));	# get num
+	&xor($c,$c);		# clear carry
+	 &and($num,0xfffffff8);	# num / 8
+
+	&jz(&label("aw_finish"));
+
+	&set_label("aw_loop",0);
+	for ($i=0; $i<8; $i++)
+		{
+		&comment("Round $i");
+
+		&mov($tmp1,&DWP($i*4,$a,"",0)); 	# *a
+		 &mov($tmp2,&DWP($i*4,$b,"",0)); 	# *b
+		&add($tmp1,$c);
+		 &mov($c,0);
+		&adc($c,$c);
+		 &add($tmp1,$tmp2);
+		&adc($c,0);
+		 &mov(&DWP($i*4,$r,"",0),$tmp1); 	# *r
+		}
+
+	&comment("");
+	&add($a,32);
+	 &add($b,32);
+	&add($r,32);
+	 &sub($num,8);
+	&jnz(&label("aw_loop"));
+
+	&set_label("aw_finish",0);
+	&mov($num,&wparam(3));	# get num
+	&and($num,7);
+	 &jz(&label("aw_end"));
+
+	for ($i=0; $i<7; $i++)
+		{
+		&comment("Tail Round $i");
+		&mov($tmp1,&DWP($i*4,$a,"",0));	# *a
+		 &mov($tmp2,&DWP($i*4,$b,"",0));# *b
+		&add($tmp1,$c);
+		 &mov($c,0);
+		&adc($c,$c);
+		 &add($tmp1,$tmp2);
+		&adc($c,0);
+		 &dec($num) if ($i != 6);
+		&mov(&DWP($i*4,$r,"",0),$tmp1);	# *r
+		 &jz(&label("aw_end")) if ($i != 6);
+		}
+	&set_label("aw_end",0);
+
+#	&mov("eax",$c);		# $c is "eax"
+
+	&function_end($name);
+	}
+
+sub bn_sub_words
+	{
+	local($name)=@_;
+
+	&function_begin($name,"");
+
+	&comment("");
+	$a="esi";
+	$b="edi";
+	$c="eax";
+	$r="ebx";
+	$tmp1="ecx";
+	$tmp2="edx";
+	$num="ebp";
+
+	&mov($r,&wparam(0));	# get r
+	 &mov($a,&wparam(1));	# get a
+	&mov($b,&wparam(2));	# get b
+	 &mov($num,&wparam(3));	# get num
+	&xor($c,$c);		# clear carry
+	 &and($num,0xfffffff8);	# num / 8
+
+	&jz(&label("aw_finish"));
+
+	&set_label("aw_loop",0);
+	for ($i=0; $i<8; $i++)
+		{
+		&comment("Round $i");
+
+		&mov($tmp1,&DWP($i*4,$a,"",0)); 	# *a
+		 &mov($tmp2,&DWP($i*4,$b,"",0)); 	# *b
+		&sub($tmp1,$c);
+		 &mov($c,0);
+		&adc($c,$c);
+		 &sub($tmp1,$tmp2);
+		&adc($c,0);
+		 &mov(&DWP($i*4,$r,"",0),$tmp1); 	# *r
+		}
+
+	&comment("");
+	&add($a,32);
+	 &add($b,32);
+	&add($r,32);
+	 &sub($num,8);
+	&jnz(&label("aw_loop"));
+
+	&set_label("aw_finish",0);
+	&mov($num,&wparam(3));	# get num
+	&and($num,7);
+	 &jz(&label("aw_end"));
+
+	for ($i=0; $i<7; $i++)
+		{
+		&comment("Tail Round $i");
+		&mov($tmp1,&DWP($i*4,$a,"",0));	# *a
+		 &mov($tmp2,&DWP($i*4,$b,"",0));# *b
+		&sub($tmp1,$c);
+		 &mov($c,0);
+		&adc($c,$c);
+		 &sub($tmp1,$tmp2);
+		&adc($c,0);
+		 &dec($num) if ($i != 6);
+		&mov(&DWP($i*4,$r,"",0),$tmp1);	# *r
+		 &jz(&label("aw_end")) if ($i != 6);
+		}
+	&set_label("aw_end",0);
+
+#	&mov("eax",$c);		# $c is "eax"
+
+	&function_end($name);
+	}
+
+sub bn_sub_part_words
+	{
+	local($name)=@_;
+
+	&function_begin($name,"");
+
+	&comment("");
+	$a="esi";
+	$b="edi";
+	$c="eax";
+	$r="ebx";
+	$tmp1="ecx";
+	$tmp2="edx";
+	$num="ebp";
+
+	&mov($r,&wparam(0));	# get r
+	 &mov($a,&wparam(1));	# get a
+	&mov($b,&wparam(2));	# get b
+	 &mov($num,&wparam(3));	# get num
+	&xor($c,$c);		# clear carry
+	 &and($num,0xfffffff8);	# num / 8
+
+	&jz(&label("aw_finish"));
+
+	&set_label("aw_loop",0);
+	for ($i=0; $i<8; $i++)
+		{
+		&comment("Round $i");
+
+		&mov($tmp1,&DWP($i*4,$a,"",0)); 	# *a
+		 &mov($tmp2,&DWP($i*4,$b,"",0)); 	# *b
+		&sub($tmp1,$c);
+		 &mov($c,0);
+		&adc($c,$c);
+		 &sub($tmp1,$tmp2);
+		&adc($c,0);
+		 &mov(&DWP($i*4,$r,"",0),$tmp1); 	# *r
+		}
+
+	&comment("");
+	&add($a,32);
+	 &add($b,32);
+	&add($r,32);
+	 &sub($num,8);
+	&jnz(&label("aw_loop"));
+
+	&set_label("aw_finish",0);
+	&mov($num,&wparam(3));	# get num
+	&and($num,7);
+	 &jz(&label("aw_end"));
+
+	for ($i=0; $i<7; $i++)
+		{
+		&comment("Tail Round $i");
+		&mov($tmp1,&DWP(0,$a,"",0));	# *a
+		 &mov($tmp2,&DWP(0,$b,"",0));# *b
+		&sub($tmp1,$c);
+		 &mov($c,0);
+		&adc($c,$c);
+		 &sub($tmp1,$tmp2);
+		&adc($c,0);
+		&mov(&DWP(0,$r,"",0),$tmp1);	# *r
+		&add($a, 4);
+		&add($b, 4);
+		&add($r, 4);
+		 &dec($num) if ($i != 6);
+		 &jz(&label("aw_end")) if ($i != 6);
+		}
+	&set_label("aw_end",0);
+
+	&cmp(&wparam(4),0);
+	&je(&label("pw_end"));
+
+	&mov($num,&wparam(4));	# get dl
+	&cmp($num,0);
+	&je(&label("pw_end"));
+	&jge(&label("pw_pos"));
+
+	&comment("pw_neg");
+	&mov($tmp2,0);
+	&sub($tmp2,$num);
+	&mov($num,$tmp2);
+	&and($num,0xfffffff8);	# num / 8
+	&jz(&label("pw_neg_finish"));
+
+	&set_label("pw_neg_loop",0);
+	for ($i=0; $i<8; $i++)
+	{
+	    &comment("dl<0 Round $i");
+
+	    &mov($tmp1,0);
+	    &mov($tmp2,&DWP($i*4,$b,"",0)); 	# *b
+	    &sub($tmp1,$c);
+	    &mov($c,0);
+	    &adc($c,$c);
+	    &sub($tmp1,$tmp2);
+	    &adc($c,0);
+	    &mov(&DWP($i*4,$r,"",0),$tmp1); 	# *r
+	}
+	    
+	&comment("");
+	&add($b,32);
+	&add($r,32);
+	&sub($num,8);
+	&jnz(&label("pw_neg_loop"));
+	    
+	&set_label("pw_neg_finish",0);
+	&mov($tmp2,&wparam(4));	# get dl
+	&mov($num,0);
+	&sub($num,$tmp2);
+	&and($num,7);
+	&jz(&label("pw_end"));
+	    
+	for ($i=0; $i<7; $i++)
+	{
+	    &comment("dl<0 Tail Round $i");
+	    &mov($tmp1,0);
+	    &mov($tmp2,&DWP($i*4,$b,"",0));# *b
+	    &sub($tmp1,$c);
+	    &mov($c,0);
+	    &adc($c,$c);
+	    &sub($tmp1,$tmp2);
+	    &adc($c,0);
+	    &dec($num) if ($i != 6);
+	    &mov(&DWP($i*4,$r,"",0),$tmp1);	# *r
+	    &jz(&label("pw_end")) if ($i != 6);
+	}
+
+	&jmp(&label("pw_end"));
+	
+	&set_label("pw_pos",0);
+	
+	&and($num,0xfffffff8);	# num / 8
+	&jz(&label("pw_pos_finish"));
+
+	&set_label("pw_pos_loop",0);
+
+	for ($i=0; $i<8; $i++)
+	{
+	    &comment("dl>0 Round $i");
+
+	    &mov($tmp1,&DWP($i*4,$a,"",0));	# *a
+	    &sub($tmp1,$c);
+	    &mov(&DWP($i*4,$r,"",0),$tmp1);	# *r
+	    &jnc(&label("pw_nc".$i));
+	}
+	    
+	&comment("");
+	&add($a,32);
+	&add($r,32);
+	&sub($num,8);
+	&jnz(&label("pw_pos_loop"));
+	    
+	&set_label("pw_pos_finish",0);
+	&mov($num,&wparam(4));	# get dl
+	&and($num,7);
+	&jz(&label("pw_end"));
+	    
+	for ($i=0; $i<7; $i++)
+	{
+	    &comment("dl>0 Tail Round $i");
+	    &mov($tmp1,&DWP($i*4,$a,"",0));	# *a
+	    &sub($tmp1,$c);
+	    &mov(&DWP($i*4,$r,"",0),$tmp1);	# *r
+	    &jnc(&label("pw_tail_nc".$i));
+	    &dec($num) if ($i != 6);
+	    &jz(&label("pw_end")) if ($i != 6);
+	}
+	&mov($c,1);
+	&jmp(&label("pw_end"));
+
+	&set_label("pw_nc_loop",0);
+	for ($i=0; $i<8; $i++)
+	{
+	    &mov($tmp1,&DWP($i*4,$a,"",0));	# *a
+	    &mov(&DWP($i*4,$r,"",0),$tmp1);	# *r
+	    &set_label("pw_nc".$i,0);
+	}
+	    
+	&comment("");
+	&add($a,32);
+	&add($r,32);
+	&sub($num,8);
+	&jnz(&label("pw_nc_loop"));
+	    
+	&mov($num,&wparam(4));	# get dl
+	&and($num,7);
+	&jz(&label("pw_nc_end"));
+	    
+	for ($i=0; $i<7; $i++)
+	{
+	    &mov($tmp1,&DWP($i*4,$a,"",0));	# *a
+	    &mov(&DWP($i*4,$r,"",0),$tmp1);	# *r
+	    &set_label("pw_tail_nc".$i,0);
+	    &dec($num) if ($i != 6);
+	    &jz(&label("pw_nc_end")) if ($i != 6);
+	}
+
+	&set_label("pw_nc_end",0);
+	&mov($c,0);
+
+	&set_label("pw_end",0);
+
+#	&mov("eax",$c);		# $c is "eax"
+
+	&function_end($name);
+	}
diff --git a/crypto/bn/asm/c64xplus-gf2m.pl b/crypto/bn/asm/c64xplus-gf2m.pl
new file mode 100644
index 000000000..c0e540080
--- /dev/null
+++ b/crypto/bn/asm/c64xplus-gf2m.pl
@@ -0,0 +1,160 @@
+#! /usr/bin/env perl
+# Copyright 2012-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+#
+# ====================================================================
+# Written by Andy Polyakov  for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+#
+# February 2012
+#
+# The module implements bn_GF2m_mul_2x2 polynomial multiplication
+# used in bn_gf2m.c. It's kind of low-hanging mechanical port from
+# C for the time being... The subroutine runs in 37 cycles, which is
+# 4.5x faster than compiler-generated code. Though comparison is
+# totally unfair, because this module utilizes Galois Field Multiply
+# instruction.
+
+while (($output=shift) && ($output!~/\w[\w\-]*\.\w+$/)) {}
+open STDOUT,">$output";
+
+($rp,$a1,$a0,$b1,$b0)=("A4","B4","A6","B6","A8");   # argument vector
+
+($Alo,$Alox0,$Alox1,$Alox2,$Alox3)=map("A$_",(16..20));
+($Ahi,$Ahix0,$Ahix1,$Ahix2,$Ahix3)=map("B$_",(16..20));
+($B_0,$B_1,$B_2,$B_3)=("B5","A5","A7","B7");
+($A,$B)=($Alo,$B_1);
+$xFF="B1";
+
+sub mul_1x1_upper {
+my ($A,$B)=@_;
+$code.=<<___;
+	EXTU	$B,8,24,$B_2		; smash $B to 4 bytes
+||	AND	$B,$xFF,$B_0
+||	SHRU	$B,24,$B_3
+	SHRU	$A,16,   $Ahi		; smash $A to two halfwords
+||	EXTU	$A,16,16,$Alo
+
+	XORMPY	$Alo,$B_2,$Alox2	; 16x8 bits muliplication
+||	XORMPY	$Ahi,$B_2,$Ahix2
+||	EXTU	$B,16,24,$B_1
+	XORMPY	$Alo,$B_0,$Alox0
+||	XORMPY	$Ahi,$B_0,$Ahix0
+	XORMPY	$Alo,$B_3,$Alox3
+||	XORMPY	$Ahi,$B_3,$Ahix3
+	XORMPY	$Alo,$B_1,$Alox1
+||	XORMPY	$Ahi,$B_1,$Ahix1
+___
+}
+sub mul_1x1_merged {
+my ($OUTlo,$OUThi,$A,$B)=@_;
+$code.=<<___;
+	 EXTU	$B,8,24,$B_2		; smash $B to 4 bytes
+||	 AND	$B,$xFF,$B_0
+||	 SHRU	$B,24,$B_3
+	 SHRU	$A,16,   $Ahi		; smash $A to two halfwords
+||	 EXTU	$A,16,16,$Alo
+
+	XOR	$Ahix0,$Alox2,$Ahix0
+||	MV	$Ahix2,$OUThi
+||	 XORMPY	$Alo,$B_2,$Alox2
+	 XORMPY	$Ahi,$B_2,$Ahix2
+||	 EXTU	$B,16,24,$B_1
+||	 XORMPY	$Alo,$B_0,A1		; $Alox0
+	XOR	$Ahix1,$Alox3,$Ahix1
+||	SHL	$Ahix0,16,$OUTlo
+||	SHRU	$Ahix0,16,$Ahix0
+	XOR	$Alox0,$OUTlo,$OUTlo
+||	XOR	$Ahix0,$OUThi,$OUThi
+||	 XORMPY	$Ahi,$B_0,$Ahix0
+||	 XORMPY	$Alo,$B_3,$Alox3
+||	SHL	$Alox1,8,$Alox1
+||	SHL	$Ahix3,8,$Ahix3
+	XOR	$Alox1,$OUTlo,$OUTlo
+||	XOR	$Ahix3,$OUThi,$OUThi
+||	 XORMPY	$Ahi,$B_3,$Ahix3
+||	SHL	$Ahix1,24,$Alox1
+||	SHRU	$Ahix1,8, $Ahix1
+	XOR	$Alox1,$OUTlo,$OUTlo
+||	XOR	$Ahix1,$OUThi,$OUThi
+||	 XORMPY	$Alo,$B_1,$Alox1
+||	 XORMPY	$Ahi,$B_1,$Ahix1
+||	 MV	A1,$Alox0
+___
+}
+sub mul_1x1_lower {
+my ($OUTlo,$OUThi)=@_;
+$code.=<<___;
+	;NOP
+	XOR	$Ahix0,$Alox2,$Ahix0
+||	MV	$Ahix2,$OUThi
+	NOP
+	XOR	$Ahix1,$Alox3,$Ahix1
+||	SHL	$Ahix0,16,$OUTlo
+||	SHRU	$Ahix0,16,$Ahix0
+	XOR	$Alox0,$OUTlo,$OUTlo
+||	XOR	$Ahix0,$OUThi,$OUThi
+||	SHL	$Alox1,8,$Alox1
+||	SHL	$Ahix3,8,$Ahix3
+	XOR	$Alox1,$OUTlo,$OUTlo
+||	XOR	$Ahix3,$OUThi,$OUThi
+||	SHL	$Ahix1,24,$Alox1
+||	SHRU	$Ahix1,8, $Ahix1
+	XOR	$Alox1,$OUTlo,$OUTlo
+||	XOR	$Ahix1,$OUThi,$OUThi
+___
+}
+$code.=<<___;
+	.text
+
+	.if	.ASSEMBLER_VERSION<7000000
+	.asg	0,__TI_EABI__
+	.endif
+	.if	__TI_EABI__
+	.asg	bn_GF2m_mul_2x2,_bn_GF2m_mul_2x2
+	.endif
+
+	.global	_bn_GF2m_mul_2x2
+_bn_GF2m_mul_2x2:
+	.asmfunc
+	MVK	0xFF,$xFF
+___
+	&mul_1x1_upper($a0,$b0);		# a0·b0
+$code.=<<___;
+||	MV	$b1,$B
+	MV	$a1,$A
+___
+	&mul_1x1_merged("A28","B28",$A,$B);	# a0·b0/a1·b1
+$code.=<<___;
+||	XOR	$b0,$b1,$B
+	XOR	$a0,$a1,$A
+___
+	&mul_1x1_merged("A31","B31",$A,$B);	# a1·b1/(a0+a1)·(b0+b1)
+$code.=<<___;
+	XOR	A28,A31,A29
+||	XOR	B28,B31,B29			; a0·b0+a1·b1
+___
+	&mul_1x1_lower("A30","B30");		# (a0+a1)·(b0+b1)
+$code.=<<___;
+||	BNOP	B3
+	XOR	A29,A30,A30
+||	XOR	B29,B30,B30			; (a0+a1)·(b0+b1)-a0·b0-a1·b1
+	XOR	B28,A30,A30
+||	STW	A28,*${rp}[0]
+	XOR	B30,A31,A31
+||	STW	A30,*${rp}[1]
+	STW	A31,*${rp}[2]
+	STW	B31,*${rp}[3]
+	.endasmfunc
+___
+
+print $code;
+close STDOUT;
diff --git a/crypto/bn/asm/co-586.pl b/crypto/bn/asm/co-586.pl
new file mode 100644
index 000000000..60d036366
--- /dev/null
+++ b/crypto/bn/asm/co-586.pl
@@ -0,0 +1,298 @@
+#! /usr/bin/env perl
+# Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+push(@INC,"${dir}","${dir}../../perlasm");
+require "x86asm.pl";
+
+$output = pop;
+open STDOUT,">$output";
+
+&asm_init($ARGV[0],$0);
+
+&bn_mul_comba("bn_mul_comba8",8);
+&bn_mul_comba("bn_mul_comba4",4);
+&bn_sqr_comba("bn_sqr_comba8",8);
+&bn_sqr_comba("bn_sqr_comba4",4);
+
+&asm_finish();
+
+close STDOUT;
+
+sub mul_add_c
+	{
+	local($a,$ai,$b,$bi,$c0,$c1,$c2,$pos,$i,$na,$nb)=@_;
+
+	# pos == -1 if eax and edx are pre-loaded, 0 to load from next
+	# words, and 1 if load return value
+
+	&comment("mul a[$ai]*b[$bi]");
+
+	# "eax" and "edx" will always be pre-loaded.
+	# &mov("eax",&DWP($ai*4,$a,"",0)) ;
+	# &mov("edx",&DWP($bi*4,$b,"",0));
+
+	&mul("edx");
+	&add($c0,"eax");
+	 &mov("eax",&DWP(($na)*4,$a,"",0)) if $pos == 0;	# laod next a
+	 &mov("eax",&wparam(0)) if $pos > 0;			# load r[]
+	 ###
+	&adc($c1,"edx");
+	 &mov("edx",&DWP(($nb)*4,$b,"",0)) if $pos == 0;	# laod next b
+	 &mov("edx",&DWP(($nb)*4,$b,"",0)) if $pos == 1;	# laod next b
+	 ###
+	&adc($c2,0);
+	 # is pos > 1, it means it is the last loop 
+	 &mov(&DWP($i*4,"eax","",0),$c0) if $pos > 0;		# save r[];
+	&mov("eax",&DWP(($na)*4,$a,"",0)) if $pos == 1;		# laod next a
+	}
+
+sub sqr_add_c
+	{
+	local($r,$a,$ai,$bi,$c0,$c1,$c2,$pos,$i,$na,$nb)=@_;
+
+	# pos == -1 if eax and edx are pre-loaded, 0 to load from next
+	# words, and 1 if load return value
+
+	&comment("sqr a[$ai]*a[$bi]");
+
+	# "eax" and "edx" will always be pre-loaded.
+	# &mov("eax",&DWP($ai*4,$a,"",0)) ;
+	# &mov("edx",&DWP($bi*4,$b,"",0));
+
+	if ($ai == $bi)
+		{ &mul("eax");}
+	else
+		{ &mul("edx");}
+	&add($c0,"eax");
+	 &mov("eax",&DWP(($na)*4,$a,"",0)) if $pos == 0;	# load next a
+	 ###
+	&adc($c1,"edx");
+	 &mov("edx",&DWP(($nb)*4,$a,"",0)) if ($pos == 1) && ($na != $nb);
+	 ###
+	&adc($c2,0);
+	 # is pos > 1, it means it is the last loop 
+	 &mov(&DWP($i*4,$r,"",0),$c0) if $pos > 0;		# save r[];
+	&mov("eax",&DWP(($na)*4,$a,"",0)) if $pos == 1;		# load next b
+	}
+
+sub sqr_add_c2
+	{
+	local($r,$a,$ai,$bi,$c0,$c1,$c2,$pos,$i,$na,$nb)=@_;
+
+	# pos == -1 if eax and edx are pre-loaded, 0 to load from next
+	# words, and 1 if load return value
+
+	&comment("sqr a[$ai]*a[$bi]");
+
+	# "eax" and "edx" will always be pre-loaded.
+	# &mov("eax",&DWP($ai*4,$a,"",0)) ;
+	# &mov("edx",&DWP($bi*4,$a,"",0));
+
+	if ($ai == $bi)
+		{ &mul("eax");}
+	else
+		{ &mul("edx");}
+	&add("eax","eax");
+	 ###
+	&adc("edx","edx");
+	 ###
+	&adc($c2,0);
+	 &add($c0,"eax");
+	&adc($c1,"edx");
+	 &mov("eax",&DWP(($na)*4,$a,"",0)) if $pos == 0;	# load next a
+	 &mov("eax",&DWP(($na)*4,$a,"",0)) if $pos == 1;	# load next b
+	&adc($c2,0);
+	&mov(&DWP($i*4,$r,"",0),$c0) if $pos > 0;		# save r[];
+	 &mov("edx",&DWP(($nb)*4,$a,"",0)) if ($pos <= 1) && ($na != $nb);
+	 ###
+	}
+
+sub bn_mul_comba
+	{
+	local($name,$num)=@_;
+	local($a,$b,$c0,$c1,$c2);
+	local($i,$as,$ae,$bs,$be,$ai,$bi);
+	local($tot,$end);
+
+	&function_begin_B($name,"");
+
+	$c0="ebx";
+	$c1="ecx";
+	$c2="ebp";
+	$a="esi";
+	$b="edi";
+	
+	$as=0;
+	$ae=0;
+	$bs=0;
+	$be=0;
+	$tot=$num+$num-1;
+
+	&push("esi");
+	 &mov($a,&wparam(1));
+	&push("edi");
+	 &mov($b,&wparam(2));
+	&push("ebp");
+	 &push("ebx");
+
+	&xor($c0,$c0);
+	 &mov("eax",&DWP(0,$a,"",0));	# load the first word 
+	&xor($c1,$c1);
+	 &mov("edx",&DWP(0,$b,"",0));	# load the first second 
+
+	for ($i=0; $i<$tot; $i++)
+		{
+		$ai=$as;
+		$bi=$bs;
+		$end=$be+1;
+
+		&comment("################## Calculate word $i"); 
+
+		for ($j=$bs; $j<$end; $j++)
+			{
+			&xor($c2,$c2) if ($j == $bs);
+			if (($j+1) == $end)
+				{
+				$v=1;
+				$v=2 if (($i+1) == $tot);
+				}
+			else
+				{ $v=0; }
+			if (($j+1) != $end)
+				{
+				$na=($ai-1);
+				$nb=($bi+1);
+				}
+			else
+				{
+				$na=$as+($i < ($num-1));
+				$nb=$bs+($i >= ($num-1));
+				}
+#printf STDERR "[$ai,$bi] -> [$na,$nb]\n";
+			&mul_add_c($a,$ai,$b,$bi,$c0,$c1,$c2,$v,$i,$na,$nb);
+			if ($v)
+				{
+				&comment("saved r[$i]");
+				# &mov("eax",&wparam(0));
+				# &mov(&DWP($i*4,"eax","",0),$c0);
+				($c0,$c1,$c2)=($c1,$c2,$c0);
+				}
+			$ai--;
+			$bi++;
+			}
+		$as++ if ($i < ($num-1));
+		$ae++ if ($i >= ($num-1));
+
+		$bs++ if ($i >= ($num-1));
+		$be++ if ($i < ($num-1));
+		}
+	&comment("save r[$i]");
+	# &mov("eax",&wparam(0));
+	&mov(&DWP($i*4,"eax","",0),$c0);
+
+	&pop("ebx");
+	&pop("ebp");
+	&pop("edi");
+	&pop("esi");
+	&ret();
+	&function_end_B($name);
+	}
+
+sub bn_sqr_comba
+	{
+	local($name,$num)=@_;
+	local($r,$a,$c0,$c1,$c2)=@_;
+	local($i,$as,$ae,$bs,$be,$ai,$bi);
+	local($b,$tot,$end,$half);
+
+	&function_begin_B($name,"");
+
+	$c0="ebx";
+	$c1="ecx";
+	$c2="ebp";
+	$a="esi";
+	$r="edi";
+
+	&push("esi");
+	 &push("edi");
+	&push("ebp");
+	 &push("ebx");
+	&mov($r,&wparam(0));
+	 &mov($a,&wparam(1));
+	&xor($c0,$c0);
+	 &xor($c1,$c1);
+	&mov("eax",&DWP(0,$a,"",0)); # load the first word
+
+	$as=0;
+	$ae=0;
+	$bs=0;
+	$be=0;
+	$tot=$num+$num-1;
+
+	for ($i=0; $i<$tot; $i++)
+		{
+		$ai=$as;
+		$bi=$bs;
+		$end=$be+1;
+
+		&comment("############### Calculate word $i");
+		for ($j=$bs; $j<$end; $j++)
+			{
+			&xor($c2,$c2) if ($j == $bs);
+			if (($ai-1) < ($bi+1))
+				{
+				$v=1;
+				$v=2 if ($i+1) == $tot;
+				}
+			else
+				{ $v=0; }
+			if (!$v)
+				{
+				$na=$ai-1;
+				$nb=$bi+1;
+				}
+			else
+				{
+				$na=$as+($i < ($num-1));
+				$nb=$bs+($i >= ($num-1));
+				}
+			if ($ai == $bi)
+				{
+				&sqr_add_c($r,$a,$ai,$bi,
+					$c0,$c1,$c2,$v,$i,$na,$nb);
+				}
+			else
+				{
+				&sqr_add_c2($r,$a,$ai,$bi,
+					$c0,$c1,$c2,$v,$i,$na,$nb);
+				}
+			if ($v)
+				{
+				&comment("saved r[$i]");
+				#&mov(&DWP($i*4,$r,"",0),$c0);
+				($c0,$c1,$c2)=($c1,$c2,$c0);
+				last;
+				}
+			$ai--;
+			$bi++;
+			}
+		$as++ if ($i < ($num-1));
+		$ae++ if ($i >= ($num-1));
+
+		$bs++ if ($i >= ($num-1));
+		$be++ if ($i < ($num-1));
+		}
+	&mov(&DWP($i*4,$r,"",0),$c0);
+	&pop("ebx");
+	&pop("ebp");
+	&pop("edi");
+	&pop("esi");
+	&ret();
+	&function_end_B($name);
+	}
diff --git a/crypto/bn/asm/ia64-mont.pl b/crypto/bn/asm/ia64-mont.pl
new file mode 100644
index 000000000..5cc5c599f
--- /dev/null
+++ b/crypto/bn/asm/ia64-mont.pl
@@ -0,0 +1,860 @@
+#! /usr/bin/env perl
+# Copyright 2010-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+#
+# ====================================================================
+# Written by Andy Polyakov  for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+
+# January 2010
+#
+# "Teaser" Montgomery multiplication module for IA-64. There are
+# several possibilities for improvement:
+#
+# - modulo-scheduling outer loop would eliminate quite a number of
+#   stalls after ldf8, xma and getf.sig outside inner loop and
+#   improve shorter key performance;
+# - shorter vector support [with input vectors being fetched only
+#   once] should be added;
+# - 2x unroll with help of n0[1] would make the code scalable on
+#   "wider" IA-64, "wider" than Itanium 2 that is, which is not of
+#   acute interest, because upcoming Tukwila's individual cores are
+#   reportedly based on Itanium 2 design;
+# - dedicated squaring procedure(?);
+#
+# January 2010
+#
+# Shorter vector support is implemented by zero-padding ap and np
+# vectors up to 8 elements, or 512 bits. This means that 256-bit
+# inputs will be processed only 2 times faster than 512-bit inputs,
+# not 4 [as one would expect, because algorithm complexity is n^2].
+# The reason for padding is that inputs shorter than 512 bits won't
+# be processed faster anyway, because minimal critical path of the
+# core loop happens to match 512-bit timing. Either way, it resulted
+# in >100% improvement of 512-bit RSA sign benchmark and 50% - of
+# 1024-bit one [in comparison to original version of *this* module].
+#
+# So far 'openssl speed rsa dsa' output on 900MHz Itanium 2 *with*
+# this module is:
+#                   sign    verify    sign/s verify/s
+# rsa  512 bits 0.000290s 0.000024s   3452.8  42031.4
+# rsa 1024 bits 0.000793s 0.000058s   1261.7  17172.0
+# rsa 2048 bits 0.005908s 0.000148s    169.3   6754.0
+# rsa 4096 bits 0.033456s 0.000469s     29.9   2133.6
+# dsa  512 bits 0.000253s 0.000198s   3949.9   5057.0
+# dsa 1024 bits 0.000585s 0.000607s   1708.4   1647.4
+# dsa 2048 bits 0.001453s 0.001703s    688.1    587.4
+#
+# ... and *without* (but still with ia64.S):
+#
+# rsa  512 bits 0.000670s 0.000041s   1491.8  24145.5
+# rsa 1024 bits 0.001988s 0.000080s    502.9  12499.3
+# rsa 2048 bits 0.008702s 0.000189s    114.9   5293.9
+# rsa 4096 bits 0.043860s 0.000533s     22.8   1875.9
+# dsa  512 bits 0.000441s 0.000427s   2265.3   2340.6
+# dsa 1024 bits 0.000823s 0.000867s   1215.6   1153.2
+# dsa 2048 bits 0.001894s 0.002179s    528.1    458.9
+#
+# As it can be seen, RSA sign performance improves by 130-30%,
+# hereafter less for longer keys, while verify - by 74-13%.
+# DSA performance improves by 115-30%.
+
+$output=pop;
+
+if ($^O eq "hpux") {
+    $ADDP="addp4";
+    for (@ARGV) { $ADDP="add" if (/[\+DD|\-mlp]64/); }
+} else { $ADDP="add"; }
+
+$code=<<___;
+.explicit
+.text
+
+// int bn_mul_mont (BN_ULONG *rp,const BN_ULONG *ap,
+//		    const BN_ULONG *bp,const BN_ULONG *np,
+//		    const BN_ULONG *n0p,int num);			
+.align	64
+.global	bn_mul_mont#
+.proc	bn_mul_mont#
+bn_mul_mont:
+	.prologue
+	.body
+{ .mmi;	cmp4.le		p6,p7=2,r37;;
+(p6)	cmp4.lt.unc	p8,p9=8,r37
+	mov		ret0=r0		};;
+{ .bbb;
+(p9)	br.cond.dptk.many	bn_mul_mont_8
+(p8)	br.cond.dpnt.many	bn_mul_mont_general
+(p7)	br.ret.spnt.many	b0	};;
+.endp	bn_mul_mont#
+
+prevfs=r2;	prevpr=r3;	prevlc=r10;	prevsp=r11;
+
+rptr=r8;	aptr=r9;	bptr=r14;	nptr=r15;
+tptr=r16;	// &tp[0]
+tp_1=r17;	// &tp[-1]
+num=r18;	len=r19;	lc=r20;
+topbit=r21;	// carry bit from tmp[num]
+
+n0=f6;
+m0=f7;
+bi=f8;
+
+.align	64
+.local	bn_mul_mont_general#
+.proc	bn_mul_mont_general#
+bn_mul_mont_general:
+	.prologue
+{ .mmi;	.save	ar.pfs,prevfs
+	alloc	prevfs=ar.pfs,6,2,0,8
+	$ADDP	aptr=0,in1
+	.save	ar.lc,prevlc
+	mov	prevlc=ar.lc		}
+{ .mmi;	.vframe	prevsp
+	mov	prevsp=sp
+	$ADDP	bptr=0,in2
+	.save	pr,prevpr
+	mov	prevpr=pr		};;
+
+	.body
+	.rotf		alo[6],nlo[4],ahi[8],nhi[6]
+	.rotr		a[3],n[3],t[2]
+
+{ .mmi;	ldf8		bi=[bptr],8		// (*bp++)
+	ldf8		alo[4]=[aptr],16	// ap[0]
+	$ADDP		r30=8,in1	};;
+{ .mmi;	ldf8		alo[3]=[r30],16		// ap[1]
+	ldf8		alo[2]=[aptr],16	// ap[2]
+	$ADDP		in4=0,in4	};;
+{ .mmi;	ldf8		alo[1]=[r30]		// ap[3]
+	ldf8		n0=[in4]		// n0
+	$ADDP		rptr=0,in0		}
+{ .mmi;	$ADDP		nptr=0,in3
+	mov		r31=16
+	zxt4		num=in5		};;
+{ .mmi;	ldf8		nlo[2]=[nptr],8		// np[0]
+	shladd		len=num,3,r0
+	shladd		r31=num,3,r31	};;
+{ .mmi;	ldf8		nlo[1]=[nptr],8		// np[1]
+	add		lc=-5,num
+	sub		r31=sp,r31	};;
+{ .mfb;	and		sp=-16,r31		// alloca
+	xmpy.hu		ahi[2]=alo[4],bi	// ap[0]*bp[0]
+	nop.b		0		}
+{ .mfb;	nop.m		0
+	xmpy.lu		alo[4]=alo[4],bi
+	brp.loop.imp	.L1st_ctop,.L1st_cend-16
+					};;
+{ .mfi;	nop.m		0
+	xma.hu		ahi[1]=alo[3],bi,ahi[2]	// ap[1]*bp[0]
+	add		tp_1=8,sp	}
+{ .mfi;	nop.m		0
+	xma.lu		alo[3]=alo[3],bi,ahi[2]
+	mov		pr.rot=0x20001f<<16
+			// ------^----- (p40) at first (p23)
+			// ----------^^ p[16:20]=1
+					};;
+{ .mfi;	nop.m		0
+	xmpy.lu		m0=alo[4],n0		// (ap[0]*bp[0])*n0
+	mov		ar.lc=lc	}
+{ .mfi;	nop.m		0
+	fcvt.fxu.s1	nhi[1]=f0
+	mov		ar.ec=8		};;
+
+.align	32
+.L1st_ctop:
+.pred.rel	"mutex",p40,p42
+{ .mfi;	(p16)	ldf8		alo[0]=[aptr],8		    // *(aptr++)
+	(p18)	xma.hu		ahi[0]=alo[2],bi,ahi[1]
+	(p40)	add		n[2]=n[2],a[2]		}   // (p23)					}
+{ .mfi;	(p18)	ldf8		nlo[0]=[nptr],8		    // *(nptr++)(p16)
+	(p18)	xma.lu		alo[2]=alo[2],bi,ahi[1]
+	(p42)	add		n[2]=n[2],a[2],1	};; // (p23)
+{ .mfi;	(p21)	getf.sig	a[0]=alo[5]
+	(p20)	xma.hu		nhi[0]=nlo[2],m0,nhi[1]
+	(p42)	cmp.leu		p41,p39=n[2],a[2]   	}   // (p23)
+{ .mfi;	(p23)	st8		[tp_1]=n[2],8
+	(p20)	xma.lu		nlo[2]=nlo[2],m0,nhi[1]
+	(p40)	cmp.ltu		p41,p39=n[2],a[2]	}   // (p23)
+{ .mmb;	(p21)	getf.sig	n[0]=nlo[3]
+	(p16)	nop.m		0
+	br.ctop.sptk	.L1st_ctop			};;
+.L1st_cend:
+
+{ .mmi;	getf.sig	a[0]=ahi[6]		// (p24)
+	getf.sig	n[0]=nhi[4]
+	add		num=-1,num	};;	// num--
+{ .mmi;	.pred.rel	"mutex",p40,p42
+(p40)	add		n[0]=n[0],a[0]
+(p42)	add		n[0]=n[0],a[0],1
+	sub		aptr=aptr,len	};;	// rewind
+{ .mmi;	.pred.rel	"mutex",p40,p42
+(p40)	cmp.ltu		p41,p39=n[0],a[0]
+(p42)	cmp.leu		p41,p39=n[0],a[0]
+	sub		nptr=nptr,len	};;
+{ .mmi;	.pred.rel	"mutex",p39,p41
+(p39)	add		topbit=r0,r0
+(p41)	add		topbit=r0,r0,1
+	nop.i		0		}	
+{ .mmi;	st8		[tp_1]=n[0]
+	add		tptr=16,sp
+	add		tp_1=8,sp	};;
+
+.Louter:
+{ .mmi;	ldf8		bi=[bptr],8		// (*bp++)
+	ldf8		ahi[3]=[tptr]		// tp[0]
+	add		r30=8,aptr	};;
+{ .mmi;	ldf8		alo[4]=[aptr],16	// ap[0]
+	ldf8		alo[3]=[r30],16		// ap[1]
+	add		r31=8,nptr	};;
+{ .mfb;	ldf8		alo[2]=[aptr],16	// ap[2]
+	xma.hu		ahi[2]=alo[4],bi,ahi[3]	// ap[0]*bp[i]+tp[0]
+	brp.loop.imp	.Linner_ctop,.Linner_cend-16
+					}
+{ .mfb;	ldf8		alo[1]=[r30]		// ap[3]
+	xma.lu		alo[4]=alo[4],bi,ahi[3]
+	clrrrb.pr			};;
+{ .mfi;	ldf8		nlo[2]=[nptr],16	// np[0]
+	xma.hu		ahi[1]=alo[3],bi,ahi[2]	// ap[1]*bp[i]
+	nop.i		0		}
+{ .mfi;	ldf8		nlo[1]=[r31]		// np[1]
+	xma.lu		alo[3]=alo[3],bi,ahi[2]
+	mov		pr.rot=0x20101f<<16
+			// ------^----- (p40) at first (p23)
+			// --------^--- (p30) at first (p22)
+			// ----------^^ p[16:20]=1
+					};;
+{ .mfi;	st8		[tptr]=r0		// tp[0] is already accounted
+	xmpy.lu		m0=alo[4],n0		// (ap[0]*bp[i]+tp[0])*n0
+	mov		ar.lc=lc	}
+{ .mfi;
+	fcvt.fxu.s1	nhi[1]=f0
+	mov		ar.ec=8		};;
+
+// This loop spins in 4*(n+7) ticks on Itanium 2 and should spin in
+// 7*(n+7) ticks on Itanium (the one codenamed Merced). Factor of 7
+// in latter case accounts for two-tick pipeline stall, which means
+// that its performance would be ~20% lower than optimal one. No
+// attempt was made to address this, because original Itanium is
+// hardly represented out in the wild...
+.align	32
+.Linner_ctop:
+.pred.rel	"mutex",p40,p42
+.pred.rel	"mutex",p30,p32
+{ .mfi;	(p16)	ldf8		alo[0]=[aptr],8		    // *(aptr++)
+	(p18)	xma.hu		ahi[0]=alo[2],bi,ahi[1]
+	(p40)	add		n[2]=n[2],a[2]		}   // (p23)
+{ .mfi;	(p16)	nop.m		0
+	(p18)	xma.lu		alo[2]=alo[2],bi,ahi[1]
+	(p42)	add		n[2]=n[2],a[2],1	};; // (p23)
+{ .mfi;	(p21)	getf.sig	a[0]=alo[5]
+	(p16)	nop.f		0
+	(p40)	cmp.ltu		p41,p39=n[2],a[2]	}   // (p23)
+{ .mfi;	(p21)	ld8		t[0]=[tptr],8
+	(p16)	nop.f		0
+	(p42)	cmp.leu		p41,p39=n[2],a[2]	};; // (p23)
+{ .mfi;	(p18)	ldf8		nlo[0]=[nptr],8		    // *(nptr++)
+	(p20)	xma.hu		nhi[0]=nlo[2],m0,nhi[1]
+	(p30)	add		a[1]=a[1],t[1]		}   // (p22)
+{ .mfi;	(p16)	nop.m		0
+	(p20)	xma.lu		nlo[2]=nlo[2],m0,nhi[1]
+	(p32)	add		a[1]=a[1],t[1],1	};; // (p22)
+{ .mmi;	(p21)	getf.sig	n[0]=nlo[3]
+	(p16)	nop.m		0
+	(p30)	cmp.ltu		p31,p29=a[1],t[1]	}   // (p22)
+{ .mmb;	(p23)	st8		[tp_1]=n[2],8
+	(p32)	cmp.leu		p31,p29=a[1],t[1]	    // (p22)
+	br.ctop.sptk	.Linner_ctop			};;
+.Linner_cend:
+
+{ .mmi;	getf.sig	a[0]=ahi[6]		// (p24)
+	getf.sig	n[0]=nhi[4]
+	nop.i		0		};;
+
+{ .mmi;	.pred.rel	"mutex",p31,p33
+(p31)	add		a[0]=a[0],topbit
+(p33)	add		a[0]=a[0],topbit,1
+	mov		topbit=r0	};;
+{ .mfi; .pred.rel	"mutex",p31,p33
+(p31)	cmp.ltu		p32,p30=a[0],topbit
+(p33)	cmp.leu		p32,p30=a[0],topbit
+					}
+{ .mfi;	.pred.rel	"mutex",p40,p42
+(p40)	add		n[0]=n[0],a[0]
+(p42)	add		n[0]=n[0],a[0],1
+					};;
+{ .mmi;	.pred.rel	"mutex",p44,p46
+(p40)	cmp.ltu		p41,p39=n[0],a[0]
+(p42)	cmp.leu		p41,p39=n[0],a[0]
+(p32)	add		topbit=r0,r0,1	}
+
+{ .mmi;	st8		[tp_1]=n[0],8
+	cmp4.ne		p6,p0=1,num
+	sub		aptr=aptr,len	};;	// rewind
+{ .mmi;	sub		nptr=nptr,len
+(p41)	add		topbit=r0,r0,1
+	add		tptr=16,sp	}
+{ .mmb;	add		tp_1=8,sp
+	add		num=-1,num		// num--
+(p6)	br.cond.sptk.many	.Louter	};;
+
+{ .mbb;	add		lc=4,lc
+	brp.loop.imp	.Lsub_ctop,.Lsub_cend-16
+	clrrrb.pr			};;
+{ .mii;	nop.m		0
+	mov		pr.rot=0x10001<<16
+			// ------^---- (p33) at first (p17)
+	mov		ar.lc=lc	}
+{ .mii;	nop.m		0
+	mov		ar.ec=3
+	nop.i		0		};;
+
+.Lsub_ctop:
+.pred.rel	"mutex",p33,p35
+{ .mfi;	(p16)	ld8		t[0]=[tptr],8		    // t=*(tp++)
+	(p16)	nop.f		0
+	(p33)	sub		n[1]=t[1],n[1]		}   // (p17)
+{ .mfi;	(p16)	ld8		n[0]=[nptr],8		    // n=*(np++)
+	(p16)	nop.f		0
+	(p35)	sub		n[1]=t[1],n[1],1	};; // (p17)
+{ .mib;	(p18)	st8		[rptr]=n[2],8		    // *(rp++)=r
+	(p33)	cmp.gtu		p34,p32=n[1],t[1]	    // (p17)
+	(p18)	nop.b		0			}
+{ .mib;	(p18)	nop.m		0
+	(p35)	cmp.geu		p34,p32=n[1],t[1]	    // (p17)
+	br.ctop.sptk	.Lsub_ctop			};;
+.Lsub_cend:
+
+{ .mmb;	.pred.rel	"mutex",p34,p36
+(p34)	sub	topbit=topbit,r0	// (p19)
+(p36)	sub	topbit=topbit,r0,1
+	brp.loop.imp	.Lcopy_ctop,.Lcopy_cend-16
+					}
+{ .mmb;	sub	rptr=rptr,len		// rewind
+	sub	tptr=tptr,len
+	clrrrb.pr			};;
+{ .mmi;	and	aptr=tptr,topbit
+	andcm	bptr=rptr,topbit
+	mov	pr.rot=1<<16		};;
+{ .mii;	or	nptr=aptr,bptr
+	mov	ar.lc=lc
+	mov	ar.ec=3			};;
+
+.Lcopy_ctop:
+{ .mmb;	(p16)	ld8	n[0]=[nptr],8
+	(p18)	st8	[tptr]=r0,8
+	(p16)	nop.b	0		}
+{ .mmb;	(p16)	nop.m	0
+	(p18)	st8	[rptr]=n[2],8
+	br.ctop.sptk	.Lcopy_ctop	};;
+.Lcopy_cend:
+
+{ .mmi;	mov		ret0=1			// signal "handled"
+	rum		1<<5			// clear um.mfh
+	mov		ar.lc=prevlc	}
+{ .mib;	.restore	sp
+	mov		sp=prevsp
+	mov		pr=prevpr,0x1ffff
+	br.ret.sptk.many	b0	};;
+.endp	bn_mul_mont_general#
+
+a1=r16;  a2=r17;  a3=r18;  a4=r19;  a5=r20;  a6=r21;  a7=r22;  a8=r23;
+n1=r24;  n2=r25;  n3=r26;  n4=r27;  n5=r28;  n6=r29;  n7=r30;  n8=r31;
+t0=r15;
+
+ai0=f8;  ai1=f9;  ai2=f10; ai3=f11; ai4=f12; ai5=f13; ai6=f14; ai7=f15;
+ni0=f16; ni1=f17; ni2=f18; ni3=f19; ni4=f20; ni5=f21; ni6=f22; ni7=f23;
+
+.align	64
+.skip	48		// aligns loop body
+.local	bn_mul_mont_8#
+.proc	bn_mul_mont_8#
+bn_mul_mont_8:
+	.prologue
+{ .mmi;	.save		ar.pfs,prevfs
+	alloc		prevfs=ar.pfs,6,2,0,8
+	.vframe		prevsp
+	mov		prevsp=sp
+	.save		ar.lc,prevlc
+	mov		prevlc=ar.lc	}
+{ .mmi;	add		r17=-6*16,sp
+	add		sp=-7*16,sp
+	.save		pr,prevpr
+	mov		prevpr=pr	};;
+
+{ .mmi;	.save.gf	0,0x10
+	stf.spill	[sp]=f16,-16
+	.save.gf	0,0x20
+	stf.spill	[r17]=f17,32
+	add		r16=-5*16,prevsp};;
+{ .mmi;	.save.gf	0,0x40
+	stf.spill	[r16]=f18,32
+	.save.gf	0,0x80
+	stf.spill	[r17]=f19,32
+	$ADDP		aptr=0,in1	};;
+{ .mmi;	.save.gf	0,0x100
+	stf.spill	[r16]=f20,32
+	.save.gf	0,0x200
+	stf.spill	[r17]=f21,32
+	$ADDP		r29=8,in1	};;
+{ .mmi;	.save.gf	0,0x400
+	stf.spill	[r16]=f22
+	.save.gf	0,0x800
+	stf.spill	[r17]=f23
+	$ADDP		rptr=0,in0	};;
+
+	.body
+	.rotf		bj[8],mj[2],tf[2],alo[10],ahi[10],nlo[10],nhi[10]
+	.rotr		t[8]
+
+// load input vectors padding them to 8 elements
+{ .mmi;	ldf8		ai0=[aptr],16		// ap[0]
+	ldf8		ai1=[r29],16		// ap[1]
+	$ADDP		bptr=0,in2	}
+{ .mmi;	$ADDP		r30=8,in2
+	$ADDP		nptr=0,in3
+	$ADDP		r31=8,in3	};;
+{ .mmi;	ldf8		bj[7]=[bptr],16		// bp[0]
+	ldf8		bj[6]=[r30],16		// bp[1]
+	cmp4.le		p4,p5=3,in5	}
+{ .mmi;	ldf8		ni0=[nptr],16		// np[0]
+	ldf8		ni1=[r31],16		// np[1]
+	cmp4.le		p6,p7=4,in5	};;
+
+{ .mfi;	(p4)ldf8	ai2=[aptr],16		// ap[2]
+	(p5)fcvt.fxu	ai2=f0
+	cmp4.le		p8,p9=5,in5	}
+{ .mfi;	(p6)ldf8	ai3=[r29],16		// ap[3]
+	(p7)fcvt.fxu	ai3=f0
+	cmp4.le		p10,p11=6,in5	}
+{ .mfi;	(p4)ldf8	bj[5]=[bptr],16		// bp[2]
+	(p5)fcvt.fxu	bj[5]=f0
+	cmp4.le		p12,p13=7,in5	}
+{ .mfi;	(p6)ldf8	bj[4]=[r30],16		// bp[3]
+	(p7)fcvt.fxu	bj[4]=f0
+	cmp4.le		p14,p15=8,in5	}
+{ .mfi;	(p4)ldf8	ni2=[nptr],16		// np[2]
+	(p5)fcvt.fxu	ni2=f0
+	addp4		r28=-1,in5	}
+{ .mfi;	(p6)ldf8	ni3=[r31],16		// np[3]
+	(p7)fcvt.fxu	ni3=f0
+	$ADDP		in4=0,in4	};;
+
+{ .mfi;	ldf8		n0=[in4]
+	fcvt.fxu	tf[1]=f0
+	nop.i		0		}
+
+{ .mfi;	(p8)ldf8	ai4=[aptr],16		// ap[4]
+	(p9)fcvt.fxu	ai4=f0
+	mov		t[0]=r0		}
+{ .mfi;	(p10)ldf8	ai5=[r29],16		// ap[5]
+	(p11)fcvt.fxu	ai5=f0
+	mov		t[1]=r0		}
+{ .mfi;	(p8)ldf8	bj[3]=[bptr],16		// bp[4]
+	(p9)fcvt.fxu	bj[3]=f0
+	mov		t[2]=r0		}
+{ .mfi;	(p10)ldf8	bj[2]=[r30],16		// bp[5]
+	(p11)fcvt.fxu	bj[2]=f0
+	mov		t[3]=r0		}
+{ .mfi;	(p8)ldf8	ni4=[nptr],16		// np[4]
+	(p9)fcvt.fxu	ni4=f0
+	mov		t[4]=r0		}
+{ .mfi;	(p10)ldf8	ni5=[r31],16		// np[5]
+	(p11)fcvt.fxu	ni5=f0
+	mov		t[5]=r0		};;
+
+{ .mfi;	(p12)ldf8	ai6=[aptr],16		// ap[6]
+	(p13)fcvt.fxu	ai6=f0
+	mov		t[6]=r0		}
+{ .mfi;	(p14)ldf8	ai7=[r29],16		// ap[7]
+	(p15)fcvt.fxu	ai7=f0
+	mov		t[7]=r0		}
+{ .mfi;	(p12)ldf8	bj[1]=[bptr],16		// bp[6]
+	(p13)fcvt.fxu	bj[1]=f0
+	mov		ar.lc=r28	}
+{ .mfi;	(p14)ldf8	bj[0]=[r30],16		// bp[7]
+	(p15)fcvt.fxu	bj[0]=f0
+	mov		ar.ec=1		}
+{ .mfi;	(p12)ldf8	ni6=[nptr],16		// np[6]
+	(p13)fcvt.fxu	ni6=f0
+	mov		pr.rot=1<<16	}
+{ .mfb;	(p14)ldf8	ni7=[r31],16		// np[7]
+	(p15)fcvt.fxu	ni7=f0
+	brp.loop.imp	.Louter_8_ctop,.Louter_8_cend-16
+					};;
+
+// The loop is scheduled for 32*n ticks on Itanium 2. Actual attempt
+// to measure with help of Interval Time Counter indicated that the
+// factor is a tad higher: 33 or 34, if not 35. Exact measurement and
+// addressing the issue is problematic, because I don't have access
+// to platform-specific instruction-level profiler. On Itanium it
+// should run in 56*n ticks, because of higher xma latency...
+.Louter_8_ctop:
+	.pred.rel		"mutex",p40,p42
+	.pred.rel		"mutex",p48,p50
+{ .mfi;	(p16)	nop.m		0			// 0:
+	(p16)	xma.hu		ahi[0]=ai0,bj[7],tf[1]	//	ap[0]*b[i]+t[0]
+	(p40)	add		a3=a3,n3	}	//	(p17) a3+=n3
+{ .mfi;	(p42)	add		a3=a3,n3,1
+	(p16)	xma.lu		alo[0]=ai0,bj[7],tf[1]
+	(p16)	nop.i		0		};;
+{ .mii;	(p17)	getf.sig	a7=alo[8]		// 1:
+	(p48)	add		t[6]=t[6],a3		//	(p17) t[6]+=a3
+	(p50)	add		t[6]=t[6],a3,1	};;
+{ .mfi;	(p17)	getf.sig	a8=ahi[8]		// 2:
+	(p17)	xma.hu		nhi[7]=ni6,mj[1],nhi[6]	//	np[6]*m0
+	(p40)	cmp.ltu		p43,p41=a3,n3	}
+{ .mfi;	(p42)	cmp.leu		p43,p41=a3,n3
+	(p17)	xma.lu		nlo[7]=ni6,mj[1],nhi[6]
+	(p16)	nop.i		0		};;
+{ .mii;	(p17)	getf.sig	n5=nlo[6]		// 3:
+	(p48)	cmp.ltu		p51,p49=t[6],a3
+	(p50)	cmp.leu		p51,p49=t[6],a3	};;
+	.pred.rel		"mutex",p41,p43
+	.pred.rel		"mutex",p49,p51
+{ .mfi;	(p16)	nop.m		0			// 4:
+	(p16)	xma.hu		ahi[1]=ai1,bj[7],ahi[0]	//	ap[1]*b[i]
+	(p41)	add		a4=a4,n4	}	//	(p17) a4+=n4
+{ .mfi;	(p43)	add		a4=a4,n4,1
+	(p16)	xma.lu		alo[1]=ai1,bj[7],ahi[0]
+	(p16)	nop.i		0		};;
+{ .mfi;	(p49)	add		t[5]=t[5],a4		// 5:	(p17) t[5]+=a4
+	(p16)	xmpy.lu		mj[0]=alo[0],n0		//	(ap[0]*b[i]+t[0])*n0
+	(p51)	add		t[5]=t[5],a4,1	};;
+{ .mfi;	(p16)	nop.m		0			// 6:
+	(p17)	xma.hu		nhi[8]=ni7,mj[1],nhi[7]	//	np[7]*m0
+	(p41)	cmp.ltu		p42,p40=a4,n4	}
+{ .mfi;	(p43)	cmp.leu		p42,p40=a4,n4
+	(p17)	xma.lu		nlo[8]=ni7,mj[1],nhi[7]
+	(p16)	nop.i		0		};;
+{ .mii;	(p17)	getf.sig	n6=nlo[7]		// 7:
+	(p49)	cmp.ltu		p50,p48=t[5],a4
+	(p51)	cmp.leu		p50,p48=t[5],a4	};;
+	.pred.rel		"mutex",p40,p42
+	.pred.rel		"mutex",p48,p50
+{ .mfi;	(p16)	nop.m		0			// 8:
+	(p16)	xma.hu		ahi[2]=ai2,bj[7],ahi[1]	//	ap[2]*b[i]
+	(p40)	add		a5=a5,n5	}	//	(p17) a5+=n5
+{ .mfi;	(p42)	add		a5=a5,n5,1
+	(p16)	xma.lu		alo[2]=ai2,bj[7],ahi[1]
+	(p16)	nop.i		0		};;
+{ .mii;	(p16)	getf.sig	a1=alo[1]		// 9:
+	(p48)	add		t[4]=t[4],a5		//	p(17) t[4]+=a5
+	(p50)	add		t[4]=t[4],a5,1	};;
+{ .mfi;	(p16)	nop.m		0			// 10:
+	(p16)	xma.hu		nhi[0]=ni0,mj[0],alo[0]	//	np[0]*m0
+	(p40)	cmp.ltu		p43,p41=a5,n5	}
+{ .mfi;	(p42)	cmp.leu		p43,p41=a5,n5
+	(p16)	xma.lu		nlo[0]=ni0,mj[0],alo[0]
+	(p16)	nop.i		0		};;
+{ .mii;	(p17)	getf.sig	n7=nlo[8]		// 11:
+	(p48)	cmp.ltu		p51,p49=t[4],a5
+	(p50)	cmp.leu		p51,p49=t[4],a5	};;
+	.pred.rel		"mutex",p41,p43
+	.pred.rel		"mutex",p49,p51
+{ .mfi;	(p17)	getf.sig	n8=nhi[8]		// 12:
+	(p16)	xma.hu		ahi[3]=ai3,bj[7],ahi[2]	//	ap[3]*b[i]
+	(p41)	add		a6=a6,n6	}	//	(p17) a6+=n6
+{ .mfi;	(p43)	add		a6=a6,n6,1
+	(p16)	xma.lu		alo[3]=ai3,bj[7],ahi[2]
+	(p16)	nop.i		0		};;
+{ .mii;	(p16)	getf.sig	a2=alo[2]		// 13:
+	(p49)	add		t[3]=t[3],a6		//	(p17) t[3]+=a6
+	(p51)	add		t[3]=t[3],a6,1	};;
+{ .mfi;	(p16)	nop.m		0			// 14:
+	(p16)	xma.hu		nhi[1]=ni1,mj[0],nhi[0]	//	np[1]*m0
+	(p41)	cmp.ltu		p42,p40=a6,n6	}
+{ .mfi;	(p43)	cmp.leu		p42,p40=a6,n6
+	(p16)	xma.lu		nlo[1]=ni1,mj[0],nhi[0]
+	(p16)	nop.i		0		};;
+{ .mii;	(p16)	nop.m		0			// 15:
+	(p49)	cmp.ltu		p50,p48=t[3],a6
+	(p51)	cmp.leu		p50,p48=t[3],a6	};;
+	.pred.rel		"mutex",p40,p42
+	.pred.rel		"mutex",p48,p50
+{ .mfi;	(p16)	nop.m		0			// 16:
+	(p16)	xma.hu		ahi[4]=ai4,bj[7],ahi[3]	//	ap[4]*b[i]
+	(p40)	add		a7=a7,n7	}	//	(p17) a7+=n7
+{ .mfi;	(p42)	add		a7=a7,n7,1
+	(p16)	xma.lu		alo[4]=ai4,bj[7],ahi[3]
+	(p16)	nop.i		0		};;
+{ .mii;	(p16)	getf.sig	a3=alo[3]		// 17:
+	(p48)	add		t[2]=t[2],a7		//	(p17) t[2]+=a7
+	(p50)	add		t[2]=t[2],a7,1	};;
+{ .mfi;	(p16)	nop.m		0			// 18:
+	(p16)	xma.hu		nhi[2]=ni2,mj[0],nhi[1]	//	np[2]*m0
+	(p40)	cmp.ltu		p43,p41=a7,n7	}
+{ .mfi;	(p42)	cmp.leu		p43,p41=a7,n7
+	(p16)	xma.lu		nlo[2]=ni2,mj[0],nhi[1]
+	(p16)	nop.i		0		};;
+{ .mii;	(p16)	getf.sig	n1=nlo[1]		// 19:
+	(p48)	cmp.ltu		p51,p49=t[2],a7
+	(p50)	cmp.leu		p51,p49=t[2],a7	};;
+	.pred.rel		"mutex",p41,p43
+	.pred.rel		"mutex",p49,p51
+{ .mfi;	(p16)	nop.m		0			// 20:
+	(p16)	xma.hu		ahi[5]=ai5,bj[7],ahi[4]	//	ap[5]*b[i]
+	(p41)	add		a8=a8,n8	}	//	(p17) a8+=n8
+{ .mfi;	(p43)	add		a8=a8,n8,1
+	(p16)	xma.lu		alo[5]=ai5,bj[7],ahi[4]
+	(p16)	nop.i		0		};;
+{ .mii;	(p16)	getf.sig	a4=alo[4]		// 21:
+	(p49)	add		t[1]=t[1],a8		//	(p17) t[1]+=a8
+	(p51)	add		t[1]=t[1],a8,1	};;
+{ .mfi;	(p16)	nop.m		0			// 22:
+	(p16)	xma.hu		nhi[3]=ni3,mj[0],nhi[2]	//	np[3]*m0
+	(p41)	cmp.ltu		p42,p40=a8,n8	}
+{ .mfi;	(p43)	cmp.leu		p42,p40=a8,n8
+	(p16)	xma.lu		nlo[3]=ni3,mj[0],nhi[2]
+	(p16)	nop.i		0		};;
+{ .mii;	(p16)	getf.sig	n2=nlo[2]		// 23:
+	(p49)	cmp.ltu		p50,p48=t[1],a8
+	(p51)	cmp.leu		p50,p48=t[1],a8	};;
+{ .mfi;	(p16)	nop.m		0			// 24:
+	(p16)	xma.hu		ahi[6]=ai6,bj[7],ahi[5]	//	ap[6]*b[i]
+	(p16)	add		a1=a1,n1	}	//	(p16) a1+=n1
+{ .mfi;	(p16)	nop.m		0
+	(p16)	xma.lu		alo[6]=ai6,bj[7],ahi[5]
+	(p17)	mov		t[0]=r0		};;
+{ .mii;	(p16)	getf.sig	a5=alo[5]		// 25:
+	(p16)	add		t0=t[7],a1		//	(p16) t[7]+=a1
+	(p42)	add		t[0]=t[0],r0,1	};;
+{ .mfi;	(p16)	setf.sig	tf[0]=t0		// 26:
+	(p16)	xma.hu		nhi[4]=ni4,mj[0],nhi[3]	//	np[4]*m0
+	(p50)	add		t[0]=t[0],r0,1	}
+{ .mfi;	(p16)	cmp.ltu.unc	p42,p40=a1,n1
+	(p16)	xma.lu		nlo[4]=ni4,mj[0],nhi[3]
+	(p16)	nop.i		0		};;
+{ .mii;	(p16)	getf.sig	n3=nlo[3]		// 27:
+	(p16)	cmp.ltu.unc	p50,p48=t0,a1
+	(p16)	nop.i		0		};;
+	.pred.rel		"mutex",p40,p42
+	.pred.rel		"mutex",p48,p50
+{ .mfi;	(p16)	nop.m		0			// 28:
+	(p16)	xma.hu		ahi[7]=ai7,bj[7],ahi[6]	//	ap[7]*b[i]
+	(p40)	add		a2=a2,n2	}	//	(p16) a2+=n2
+{ .mfi;	(p42)	add		a2=a2,n2,1
+	(p16)	xma.lu		alo[7]=ai7,bj[7],ahi[6]
+	(p16)	nop.i		0		};;
+{ .mii;	(p16)	getf.sig	a6=alo[6]		// 29:
+	(p48)	add		t[6]=t[6],a2		//	(p16) t[6]+=a2
+	(p50)	add		t[6]=t[6],a2,1	};;
+{ .mfi;	(p16)	nop.m		0			// 30:
+	(p16)	xma.hu		nhi[5]=ni5,mj[0],nhi[4]	//	np[5]*m0
+	(p40)	cmp.ltu		p41,p39=a2,n2	}
+{ .mfi;	(p42)	cmp.leu		p41,p39=a2,n2
+	(p16)	xma.lu		nlo[5]=ni5,mj[0],nhi[4]
+	(p16)	nop.i		0		};;
+{ .mfi;	(p16)	getf.sig	n4=nlo[4]		// 31:
+	(p16)	nop.f		0
+	(p48)	cmp.ltu		p49,p47=t[6],a2	}
+{ .mfb;	(p50)	cmp.leu		p49,p47=t[6],a2
+	(p16)	nop.f		0
+	br.ctop.sptk.many	.Louter_8_ctop	};;
+.Louter_8_cend:
+
+// above loop has to execute one more time, without (p16), which is
+// replaced with merged move of np[8] to GPR bank
+	.pred.rel		"mutex",p40,p42
+	.pred.rel		"mutex",p48,p50
+{ .mmi;	(p0)	getf.sig	n1=ni0			// 0:
+	(p40)	add		a3=a3,n3		//	(p17) a3+=n3
+	(p42)	add		a3=a3,n3,1	};;
+{ .mii;	(p17)	getf.sig	a7=alo[8]		// 1:
+	(p48)	add		t[6]=t[6],a3		//	(p17) t[6]+=a3
+	(p50)	add		t[6]=t[6],a3,1	};;
+{ .mfi;	(p17)	getf.sig	a8=ahi[8]		// 2:
+	(p17)	xma.hu		nhi[7]=ni6,mj[1],nhi[6]	//	np[6]*m0
+	(p40)	cmp.ltu		p43,p41=a3,n3	}
+{ .mfi;	(p42)	cmp.leu		p43,p41=a3,n3
+	(p17)	xma.lu		nlo[7]=ni6,mj[1],nhi[6]
+	(p0)	nop.i		0		};;
+{ .mii;	(p17)	getf.sig	n5=nlo[6]		// 3:
+	(p48)	cmp.ltu		p51,p49=t[6],a3
+	(p50)	cmp.leu		p51,p49=t[6],a3	};;
+	.pred.rel		"mutex",p41,p43
+	.pred.rel		"mutex",p49,p51
+{ .mmi;	(p0)	getf.sig	n2=ni1			// 4:
+	(p41)	add		a4=a4,n4		//	(p17) a4+=n4
+	(p43)	add		a4=a4,n4,1	};;
+{ .mfi;	(p49)	add		t[5]=t[5],a4		// 5:	(p17) t[5]+=a4
+	(p0)	nop.f		0
+	(p51)	add		t[5]=t[5],a4,1	};;
+{ .mfi;	(p0)	getf.sig	n3=ni2			// 6:
+	(p17)	xma.hu		nhi[8]=ni7,mj[1],nhi[7]	//	np[7]*m0
+	(p41)	cmp.ltu		p42,p40=a4,n4	}
+{ .mfi;	(p43)	cmp.leu		p42,p40=a4,n4
+	(p17)	xma.lu		nlo[8]=ni7,mj[1],nhi[7]
+	(p0)	nop.i		0		};;
+{ .mii;	(p17)	getf.sig	n6=nlo[7]		// 7:
+	(p49)	cmp.ltu		p50,p48=t[5],a4
+	(p51)	cmp.leu		p50,p48=t[5],a4	};;
+	.pred.rel		"mutex",p40,p42
+	.pred.rel		"mutex",p48,p50
+{ .mii;	(p0)	getf.sig	n4=ni3			// 8:
+	(p40)	add		a5=a5,n5		//	(p17) a5+=n5
+	(p42)	add		a5=a5,n5,1	};;
+{ .mii;	(p0)	nop.m		0			// 9:
+	(p48)	add		t[4]=t[4],a5		//	p(17) t[4]+=a5
+	(p50)	add		t[4]=t[4],a5,1	};;
+{ .mii;	(p0)	nop.m		0			// 10:
+	(p40)	cmp.ltu		p43,p41=a5,n5
+	(p42)	cmp.leu		p43,p41=a5,n5	};;
+{ .mii;	(p17)	getf.sig	n7=nlo[8]		// 11:
+	(p48)	cmp.ltu		p51,p49=t[4],a5
+	(p50)	cmp.leu		p51,p49=t[4],a5	};;
+	.pred.rel		"mutex",p41,p43
+	.pred.rel		"mutex",p49,p51
+{ .mii;	(p17)	getf.sig	n8=nhi[8]		// 12:
+	(p41)	add		a6=a6,n6		//	(p17) a6+=n6
+	(p43)	add		a6=a6,n6,1	};;
+{ .mii;	(p0)	getf.sig	n5=ni4			// 13:
+	(p49)	add		t[3]=t[3],a6		//	(p17) t[3]+=a6
+	(p51)	add		t[3]=t[3],a6,1	};;
+{ .mii;	(p0)	nop.m		0			// 14:
+	(p41)	cmp.ltu		p42,p40=a6,n6
+	(p43)	cmp.leu		p42,p40=a6,n6	};;
+{ .mii;	(p0)	getf.sig	n6=ni5			// 15:
+	(p49)	cmp.ltu		p50,p48=t[3],a6
+	(p51)	cmp.leu		p50,p48=t[3],a6	};;
+	.pred.rel		"mutex",p40,p42
+	.pred.rel		"mutex",p48,p50
+{ .mii;	(p0)	nop.m		0			// 16:
+	(p40)	add		a7=a7,n7		//	(p17) a7+=n7
+	(p42)	add		a7=a7,n7,1	};;
+{ .mii;	(p0)	nop.m		0			// 17:
+	(p48)	add		t[2]=t[2],a7		//	(p17) t[2]+=a7
+	(p50)	add		t[2]=t[2],a7,1	};;
+{ .mii;	(p0)	nop.m		0			// 18:
+	(p40)	cmp.ltu		p43,p41=a7,n7
+	(p42)	cmp.leu		p43,p41=a7,n7	};;
+{ .mii;	(p0)	getf.sig	n7=ni6			// 19:
+	(p48)	cmp.ltu		p51,p49=t[2],a7
+	(p50)	cmp.leu		p51,p49=t[2],a7	};;
+	.pred.rel		"mutex",p41,p43
+	.pred.rel		"mutex",p49,p51
+{ .mii;	(p0)	nop.m		0			// 20:
+	(p41)	add		a8=a8,n8		//	(p17) a8+=n8
+	(p43)	add		a8=a8,n8,1	};;
+{ .mmi;	(p0)	nop.m		0			// 21:
+	(p49)	add		t[1]=t[1],a8		//	(p17) t[1]+=a8
+	(p51)	add		t[1]=t[1],a8,1	}
+{ .mmi;	(p17)	mov		t[0]=r0
+	(p41)	cmp.ltu		p42,p40=a8,n8
+	(p43)	cmp.leu		p42,p40=a8,n8	};;
+{ .mmi;	(p0)	getf.sig	n8=ni7			// 22:
+	(p49)	cmp.ltu		p50,p48=t[1],a8
+	(p51)	cmp.leu		p50,p48=t[1],a8	}
+{ .mmi;	(p42)	add		t[0]=t[0],r0,1
+	(p0)	add		r16=-7*16,prevsp
+	(p0)	add		r17=-6*16,prevsp	};;
+
+// subtract np[8] from carrybit|tmp[8]
+// carrybit|tmp[8] layout upon exit from above loop is:
+//	t[0]|t[1]|t[2]|t[3]|t[4]|t[5]|t[6]|t[7]|t0 (least significant)
+{ .mmi;	(p50)add	t[0]=t[0],r0,1
+	add		r18=-5*16,prevsp
+	sub		n1=t0,n1	};;
+{ .mmi;	cmp.gtu		p34,p32=n1,t0;;
+	.pred.rel	"mutex",p32,p34
+	(p32)sub	n2=t[7],n2
+	(p34)sub	n2=t[7],n2,1	};;
+{ .mii;	(p32)cmp.gtu	p35,p33=n2,t[7]
+	(p34)cmp.geu	p35,p33=n2,t[7];;
+	.pred.rel	"mutex",p33,p35
+	(p33)sub	n3=t[6],n3	}
+{ .mmi;	(p35)sub	n3=t[6],n3,1;;
+	(p33)cmp.gtu	p34,p32=n3,t[6]
+	(p35)cmp.geu	p34,p32=n3,t[6]	};;
+	.pred.rel	"mutex",p32,p34
+{ .mii;	(p32)sub	n4=t[5],n4
+	(p34)sub	n4=t[5],n4,1;;
+	(p32)cmp.gtu	p35,p33=n4,t[5]	}
+{ .mmi;	(p34)cmp.geu	p35,p33=n4,t[5];;
+	.pred.rel	"mutex",p33,p35
+	(p33)sub	n5=t[4],n5
+	(p35)sub	n5=t[4],n5,1	};;
+{ .mii;	(p33)cmp.gtu	p34,p32=n5,t[4]
+	(p35)cmp.geu	p34,p32=n5,t[4];;
+	.pred.rel	"mutex",p32,p34
+	(p32)sub	n6=t[3],n6	}
+{ .mmi;	(p34)sub	n6=t[3],n6,1;;
+	(p32)cmp.gtu	p35,p33=n6,t[3]
+	(p34)cmp.geu	p35,p33=n6,t[3]	};;
+	.pred.rel	"mutex",p33,p35
+{ .mii;	(p33)sub	n7=t[2],n7
+	(p35)sub	n7=t[2],n7,1;;
+	(p33)cmp.gtu	p34,p32=n7,t[2]	}
+{ .mmi;	(p35)cmp.geu	p34,p32=n7,t[2];;
+	.pred.rel	"mutex",p32,p34
+	(p32)sub	n8=t[1],n8
+	(p34)sub	n8=t[1],n8,1	};;
+{ .mii;	(p32)cmp.gtu	p35,p33=n8,t[1]
+	(p34)cmp.geu	p35,p33=n8,t[1];;
+	.pred.rel	"mutex",p33,p35
+	(p33)sub	a8=t[0],r0	}
+{ .mmi;	(p35)sub	a8=t[0],r0,1;;
+	(p33)cmp.gtu	p34,p32=a8,t[0]
+	(p35)cmp.geu	p34,p32=a8,t[0]	};;
+
+// save the result, either tmp[num] or tmp[num]-np[num]
+	.pred.rel	"mutex",p32,p34
+{ .mmi;	(p32)st8	[rptr]=n1,8
+	(p34)st8	[rptr]=t0,8
+	add		r19=-4*16,prevsp};;
+{ .mmb;	(p32)st8	[rptr]=n2,8
+	(p34)st8	[rptr]=t[7],8
+	(p5)br.cond.dpnt.few	.Ldone	};;
+{ .mmb;	(p32)st8	[rptr]=n3,8
+	(p34)st8	[rptr]=t[6],8
+	(p7)br.cond.dpnt.few	.Ldone	};;
+{ .mmb;	(p32)st8	[rptr]=n4,8
+	(p34)st8	[rptr]=t[5],8
+	(p9)br.cond.dpnt.few	.Ldone	};;
+{ .mmb;	(p32)st8	[rptr]=n5,8
+	(p34)st8	[rptr]=t[4],8
+	(p11)br.cond.dpnt.few	.Ldone	};;
+{ .mmb;	(p32)st8	[rptr]=n6,8
+	(p34)st8	[rptr]=t[3],8
+	(p13)br.cond.dpnt.few	.Ldone	};;
+{ .mmb;	(p32)st8	[rptr]=n7,8
+	(p34)st8	[rptr]=t[2],8
+	(p15)br.cond.dpnt.few	.Ldone	};;
+{ .mmb;	(p32)st8	[rptr]=n8,8
+	(p34)st8	[rptr]=t[1],8
+	nop.b		0		};;
+.Ldone:						// epilogue
+{ .mmi;	ldf.fill	f16=[r16],64
+	ldf.fill	f17=[r17],64
+	nop.i		0		}
+{ .mmi;	ldf.fill	f18=[r18],64
+	ldf.fill	f19=[r19],64
+	mov		pr=prevpr,0x1ffff	};;
+{ .mmi;	ldf.fill	f20=[r16]
+	ldf.fill	f21=[r17]
+	mov		ar.lc=prevlc	}
+{ .mmi;	ldf.fill	f22=[r18]
+	ldf.fill	f23=[r19]
+	mov		ret0=1		}	// signal "handled"
+{ .mib;	rum		1<<5
+	.restore	sp
+	mov		sp=prevsp
+	br.ret.sptk.many	b0	};;
+.endp	bn_mul_mont_8#
+
+.type	copyright#,\@object
+copyright:
+stringz	"Montgomery multiplication for IA-64, CRYPTOGAMS by "
+___
+
+open STDOUT,">$output" if $output;
+print $code;
+close STDOUT;
diff --git a/crypto/bn/asm/ia64.S b/crypto/bn/asm/ia64.S
new file mode 100644
index 000000000..f2404a3c1
--- /dev/null
+++ b/crypto/bn/asm/ia64.S
@@ -0,0 +1,1562 @@
+.explicit
+.text
+.ident	"ia64.S, Version 2.1"
+.ident	"IA-64 ISA artwork by Andy Polyakov "
+
+// Copyright 2001-2016 The OpenSSL Project Authors. All Rights Reserved.
+//
+// Licensed under the OpenSSL license (the "License").  You may not use
+// this file except in compliance with the License.  You can obtain a copy
+// in the file LICENSE in the source distribution or at
+// https://www.openssl.org/source/license.html
+
+//
+// ====================================================================
+// Written by Andy Polyakov  for the OpenSSL
+// project.
+//
+// Rights for redistribution and usage in source and binary forms are
+// granted according to the OpenSSL license. Warranty of any kind is
+// disclaimed.
+// ====================================================================
+//
+// Version 2.x is Itanium2 re-tune. Few words about how Itanum2 is
+// different from Itanium to this module viewpoint. Most notably, is it
+// "wider" than Itanium? Can you experience loop scalability as
+// discussed in commentary sections? Not really:-( Itanium2 has 6
+// integer ALU ports, i.e. it's 2 ports wider, but it's not enough to
+// spin twice as fast, as I need 8 IALU ports. Amount of floating point
+// ports is the same, i.e. 2, while I need 4. In other words, to this
+// module Itanium2 remains effectively as "wide" as Itanium. Yet it's
+// essentially different in respect to this module, and a re-tune was
+// required. Well, because some instruction latencies has changed. Most
+// noticeably those intensively used:
+//
+//			Itanium	Itanium2
+//	ldf8		9	6		L2 hit
+//	ld8		2	1		L1 hit
+//	getf		2	5
+//	xma[->getf]	7[+1]	4[+0]
+//	add[->st8]	1[+1]	1[+0]
+//
+// What does it mean? You might ratiocinate that the original code
+// should run just faster... Because sum of latencies is smaller...
+// Wrong! Note that getf latency increased. This means that if a loop is
+// scheduled for lower latency (as they were), then it will suffer from
+// stall condition and the code will therefore turn anti-scalable, e.g.
+// original bn_mul_words spun at 5*n or 2.5 times slower than expected
+// on Itanium2! What to do? Reschedule loops for Itanium2? But then
+// Itanium would exhibit anti-scalability. So I've chosen to reschedule
+// for worst latency for every instruction aiming for best *all-round*
+// performance.  
+
+// Q.	How much faster does it get?
+// A.	Here is the output from 'openssl speed rsa dsa' for vanilla
+//	0.9.6a compiled with gcc version 2.96 20000731 (Red Hat
+//	Linux 7.1 2.96-81):
+//
+//	                  sign    verify    sign/s verify/s
+//	rsa  512 bits   0.0036s   0.0003s    275.3   2999.2
+//	rsa 1024 bits   0.0203s   0.0011s     49.3    894.1
+//	rsa 2048 bits   0.1331s   0.0040s      7.5    250.9
+//	rsa 4096 bits   0.9270s   0.0147s      1.1     68.1
+//	                  sign    verify    sign/s verify/s
+//	dsa  512 bits   0.0035s   0.0043s    288.3    234.8
+//	dsa 1024 bits   0.0111s   0.0135s     90.0     74.2
+//
+//	And here is similar output but for this assembler
+//	implementation:-)
+//
+//	                  sign    verify    sign/s verify/s
+//	rsa  512 bits   0.0021s   0.0001s    549.4   9638.5
+//	rsa 1024 bits   0.0055s   0.0002s    183.8   4481.1
+//	rsa 2048 bits   0.0244s   0.0006s     41.4   1726.3
+//	rsa 4096 bits   0.1295s   0.0018s      7.7    561.5
+//	                  sign    verify    sign/s verify/s
+//	dsa  512 bits   0.0012s   0.0013s    891.9    756.6
+//	dsa 1024 bits   0.0023s   0.0028s    440.4    376.2
+//	
+//	Yes, you may argue that it's not fair comparison as it's
+//	possible to craft the C implementation with BN_UMULT_HIGH
+//	inline assembler macro. But of course! Here is the output
+//	with the macro:
+//
+//	                  sign    verify    sign/s verify/s
+//	rsa  512 bits   0.0020s   0.0002s    495.0   6561.0
+//	rsa 1024 bits   0.0086s   0.0004s    116.2   2235.7
+//	rsa 2048 bits   0.0519s   0.0015s     19.3    667.3
+//	rsa 4096 bits   0.3464s   0.0053s      2.9    187.7
+//	                  sign    verify    sign/s verify/s
+//	dsa  512 bits   0.0016s   0.0020s    613.1    510.5
+//	dsa 1024 bits   0.0045s   0.0054s    221.0    183.9
+//
+//	My code is still way faster, huh:-) And I believe that even
+//	higher performance can be achieved. Note that as keys get
+//	longer, performance gain is larger. Why? According to the
+//	profiler there is another player in the field, namely
+//	BN_from_montgomery consuming larger and larger portion of CPU
+//	time as keysize decreases. I therefore consider putting effort
+//	to assembler implementation of the following routine:
+//
+//	void bn_mul_add_mont (BN_ULONG *rp,BN_ULONG *np,int nl,BN_ULONG n0)
+//	{
+//	int      i,j;
+//	BN_ULONG v;
+//
+//	for (i=0; i for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+
+# This module doesn't present direct interest for OpenSSL, because it
+# doesn't provide better performance for longer keys, at least not on
+# in-order-execution cores. While 512-bit RSA sign operations can be
+# 65% faster in 64-bit mode, 1024-bit ones are only 15% faster, and
+# 4096-bit ones are up to 15% slower. In 32-bit mode it varies from
+# 16% improvement for 512-bit RSA sign to -33% for 4096-bit RSA
+# verify:-( All comparisons are against bn_mul_mont-free assembler.
+# The module might be of interest to embedded system developers, as
+# the code is smaller than 1KB, yet offers >3x improvement on MIPS64
+# and 75-30% [less for longer keys] on MIPS32 over compiler-generated
+# code.
+
+######################################################################
+# There is a number of MIPS ABI in use, O32 and N32/64 are most
+# widely used. Then there is a new contender: NUBI. It appears that if
+# one picks the latter, it's possible to arrange code in ABI neutral
+# manner. Therefore let's stick to NUBI register layout:
+#
+($zero,$at,$t0,$t1,$t2)=map("\$$_",(0..2,24,25));
+($a0,$a1,$a2,$a3,$a4,$a5,$a6,$a7)=map("\$$_",(4..11));
+($s0,$s1,$s2,$s3,$s4,$s5,$s6,$s7,$s8,$s9,$s10,$s11)=map("\$$_",(12..23));
+($gp,$tp,$sp,$fp,$ra)=map("\$$_",(3,28..31));
+#
+# The return value is placed in $a0. Following coding rules facilitate
+# interoperability:
+#
+# - never ever touch $tp, "thread pointer", former $gp;
+# - copy return value to $t0, former $v0 [or to $a0 if you're adapting
+#   old code];
+# - on O32 populate $a4-$a7 with 'lw $aN,4*N($sp)' if necessary;
+#
+# For reference here is register layout for N32/64 MIPS ABIs:
+#
+# ($zero,$at,$v0,$v1)=map("\$$_",(0..3));
+# ($a0,$a1,$a2,$a3,$a4,$a5,$a6,$a7)=map("\$$_",(4..11));
+# ($t0,$t1,$t2,$t3,$t8,$t9)=map("\$$_",(12..15,24,25));
+# ($s0,$s1,$s2,$s3,$s4,$s5,$s6,$s7)=map("\$$_",(16..23));
+# ($gp,$sp,$fp,$ra)=map("\$$_",(28..31));
+#
+$flavour = shift || "o32"; # supported flavours are o32,n32,64,nubi32,nubi64
+
+if ($flavour =~ /64|n32/i) {
+	$PTR_ADD="dadd";	# incidentally works even on n32
+	$PTR_SUB="dsub";	# incidentally works even on n32
+	$REG_S="sd";
+	$REG_L="ld";
+	$SZREG=8;
+} else {
+	$PTR_ADD="add";
+	$PTR_SUB="sub";
+	$REG_S="sw";
+	$REG_L="lw";
+	$SZREG=4;
+}
+$SAVED_REGS_MASK = ($flavour =~ /nubi/i) ? 0x00fff000 : 0x00ff0000;
+#
+# 
+#
+######################################################################
+
+while (($output=shift) && ($output!~/\w[\w\-]*\.\w+$/)) {}
+open STDOUT,">$output";
+
+if ($flavour =~ /64|n32/i) {
+	$LD="ld";
+	$ST="sd";
+	$MULTU="dmultu";
+	$ADDU="daddu";
+	$SUBU="dsubu";
+	$BNSZ=8;
+} else {
+	$LD="lw";
+	$ST="sw";
+	$MULTU="multu";
+	$ADDU="addu";
+	$SUBU="subu";
+	$BNSZ=4;
+}
+
+# int bn_mul_mont(
+$rp=$a0;	# BN_ULONG *rp,
+$ap=$a1;	# const BN_ULONG *ap,
+$bp=$a2;	# const BN_ULONG *bp,
+$np=$a3;	# const BN_ULONG *np,
+$n0=$a4;	# const BN_ULONG *n0,
+$num=$a5;	# int num);
+
+$lo0=$a6;
+$hi0=$a7;
+$lo1=$t1;
+$hi1=$t2;
+$aj=$s0;
+$bi=$s1;
+$nj=$s2;
+$tp=$s3;
+$alo=$s4;
+$ahi=$s5;
+$nlo=$s6;
+$nhi=$s7;
+$tj=$s8;
+$i=$s9;
+$j=$s10;
+$m1=$s11;
+
+$FRAMESIZE=14;
+
+$code=<<___;
+.text
+
+.set	noat
+.set	noreorder
+
+.align	5
+.globl	bn_mul_mont
+.ent	bn_mul_mont
+bn_mul_mont:
+___
+$code.=<<___ if ($flavour =~ /o32/i);
+	lw	$n0,16($sp)
+	lw	$num,20($sp)
+___
+$code.=<<___;
+	slt	$at,$num,4
+	bnez	$at,1f
+	li	$t0,0
+	slt	$at,$num,17	# on in-order CPU
+	bnez	$at,bn_mul_mont_internal
+	nop
+1:	jr	$ra
+	li	$a0,0
+.end	bn_mul_mont
+
+.align	5
+.ent	bn_mul_mont_internal
+bn_mul_mont_internal:
+	.frame	$fp,$FRAMESIZE*$SZREG,$ra
+	.mask	0x40000000|$SAVED_REGS_MASK,-$SZREG
+	$PTR_SUB $sp,$FRAMESIZE*$SZREG
+	$REG_S	$fp,($FRAMESIZE-1)*$SZREG($sp)
+	$REG_S	$s11,($FRAMESIZE-2)*$SZREG($sp)
+	$REG_S	$s10,($FRAMESIZE-3)*$SZREG($sp)
+	$REG_S	$s9,($FRAMESIZE-4)*$SZREG($sp)
+	$REG_S	$s8,($FRAMESIZE-5)*$SZREG($sp)
+	$REG_S	$s7,($FRAMESIZE-6)*$SZREG($sp)
+	$REG_S	$s6,($FRAMESIZE-7)*$SZREG($sp)
+	$REG_S	$s5,($FRAMESIZE-8)*$SZREG($sp)
+	$REG_S	$s4,($FRAMESIZE-9)*$SZREG($sp)
+___
+$code.=<<___ if ($flavour =~ /nubi/i);
+	$REG_S	$s3,($FRAMESIZE-10)*$SZREG($sp)
+	$REG_S	$s2,($FRAMESIZE-11)*$SZREG($sp)
+	$REG_S	$s1,($FRAMESIZE-12)*$SZREG($sp)
+	$REG_S	$s0,($FRAMESIZE-13)*$SZREG($sp)
+___
+$code.=<<___;
+	move	$fp,$sp
+
+	.set	reorder
+	$LD	$n0,0($n0)
+	$LD	$bi,0($bp)	# bp[0]
+	$LD	$aj,0($ap)	# ap[0]
+	$LD	$nj,0($np)	# np[0]
+
+	$PTR_SUB $sp,2*$BNSZ	# place for two extra words
+	sll	$num,`log($BNSZ)/log(2)`
+	li	$at,-4096
+	$PTR_SUB $sp,$num
+	and	$sp,$at
+
+	$MULTU	$aj,$bi
+	$LD	$alo,$BNSZ($ap)
+	$LD	$nlo,$BNSZ($np)
+	mflo	$lo0
+	mfhi	$hi0
+	$MULTU	$lo0,$n0
+	mflo	$m1
+
+	$MULTU	$alo,$bi
+	mflo	$alo
+	mfhi	$ahi
+
+	$MULTU	$nj,$m1
+	mflo	$lo1
+	mfhi	$hi1
+	$MULTU	$nlo,$m1
+	$ADDU	$lo1,$lo0
+	sltu	$at,$lo1,$lo0
+	$ADDU	$hi1,$at
+	mflo	$nlo
+	mfhi	$nhi
+
+	move	$tp,$sp
+	li	$j,2*$BNSZ
+.align	4
+.L1st:
+	.set	noreorder
+	$PTR_ADD $aj,$ap,$j
+	$PTR_ADD $nj,$np,$j
+	$LD	$aj,($aj)
+	$LD	$nj,($nj)
+
+	$MULTU	$aj,$bi
+	$ADDU	$lo0,$alo,$hi0
+	$ADDU	$lo1,$nlo,$hi1
+	sltu	$at,$lo0,$hi0
+	sltu	$t0,$lo1,$hi1
+	$ADDU	$hi0,$ahi,$at
+	$ADDU	$hi1,$nhi,$t0
+	mflo	$alo
+	mfhi	$ahi
+
+	$ADDU	$lo1,$lo0
+	sltu	$at,$lo1,$lo0
+	$MULTU	$nj,$m1
+	$ADDU	$hi1,$at
+	addu	$j,$BNSZ
+	$ST	$lo1,($tp)
+	sltu	$t0,$j,$num
+	mflo	$nlo
+	mfhi	$nhi
+
+	bnez	$t0,.L1st
+	$PTR_ADD $tp,$BNSZ
+	.set	reorder
+
+	$ADDU	$lo0,$alo,$hi0
+	sltu	$at,$lo0,$hi0
+	$ADDU	$hi0,$ahi,$at
+
+	$ADDU	$lo1,$nlo,$hi1
+	sltu	$t0,$lo1,$hi1
+	$ADDU	$hi1,$nhi,$t0
+	$ADDU	$lo1,$lo0
+	sltu	$at,$lo1,$lo0
+	$ADDU	$hi1,$at
+
+	$ST	$lo1,($tp)
+
+	$ADDU	$hi1,$hi0
+	sltu	$at,$hi1,$hi0
+	$ST	$hi1,$BNSZ($tp)
+	$ST	$at,2*$BNSZ($tp)
+
+	li	$i,$BNSZ
+.align	4
+.Louter:
+	$PTR_ADD $bi,$bp,$i
+	$LD	$bi,($bi)
+	$LD	$aj,($ap)
+	$LD	$alo,$BNSZ($ap)
+	$LD	$tj,($sp)
+
+	$MULTU	$aj,$bi
+	$LD	$nj,($np)
+	$LD	$nlo,$BNSZ($np)
+	mflo	$lo0
+	mfhi	$hi0
+	$ADDU	$lo0,$tj
+	$MULTU	$lo0,$n0
+	sltu	$at,$lo0,$tj
+	$ADDU	$hi0,$at
+	mflo	$m1
+
+	$MULTU	$alo,$bi
+	mflo	$alo
+	mfhi	$ahi
+
+	$MULTU	$nj,$m1
+	mflo	$lo1
+	mfhi	$hi1
+
+	$MULTU	$nlo,$m1
+	$ADDU	$lo1,$lo0
+	sltu	$at,$lo1,$lo0
+	$ADDU	$hi1,$at
+	mflo	$nlo
+	mfhi	$nhi
+
+	move	$tp,$sp
+	li	$j,2*$BNSZ
+	$LD	$tj,$BNSZ($tp)
+.align	4
+.Linner:
+	.set	noreorder
+	$PTR_ADD $aj,$ap,$j
+	$PTR_ADD $nj,$np,$j
+	$LD	$aj,($aj)
+	$LD	$nj,($nj)
+
+	$MULTU	$aj,$bi
+	$ADDU	$lo0,$alo,$hi0
+	$ADDU	$lo1,$nlo,$hi1
+	sltu	$at,$lo0,$hi0
+	sltu	$t0,$lo1,$hi1
+	$ADDU	$hi0,$ahi,$at
+	$ADDU	$hi1,$nhi,$t0
+	mflo	$alo
+	mfhi	$ahi
+
+	$ADDU	$lo0,$tj
+	addu	$j,$BNSZ
+	$MULTU	$nj,$m1
+	sltu	$at,$lo0,$tj
+	$ADDU	$lo1,$lo0
+	$ADDU	$hi0,$at
+	sltu	$t0,$lo1,$lo0
+	$LD	$tj,2*$BNSZ($tp)
+	$ADDU	$hi1,$t0
+	sltu	$at,$j,$num
+	mflo	$nlo
+	mfhi	$nhi
+	$ST	$lo1,($tp)
+	bnez	$at,.Linner
+	$PTR_ADD $tp,$BNSZ
+	.set	reorder
+
+	$ADDU	$lo0,$alo,$hi0
+	sltu	$at,$lo0,$hi0
+	$ADDU	$hi0,$ahi,$at
+	$ADDU	$lo0,$tj
+	sltu	$t0,$lo0,$tj
+	$ADDU	$hi0,$t0
+
+	$LD	$tj,2*$BNSZ($tp)
+	$ADDU	$lo1,$nlo,$hi1
+	sltu	$at,$lo1,$hi1
+	$ADDU	$hi1,$nhi,$at
+	$ADDU	$lo1,$lo0
+	sltu	$t0,$lo1,$lo0
+	$ADDU	$hi1,$t0
+	$ST	$lo1,($tp)
+
+	$ADDU	$lo1,$hi1,$hi0
+	sltu	$hi1,$lo1,$hi0
+	$ADDU	$lo1,$tj
+	sltu	$at,$lo1,$tj
+	$ADDU	$hi1,$at
+	$ST	$lo1,$BNSZ($tp)
+	$ST	$hi1,2*$BNSZ($tp)
+
+	addu	$i,$BNSZ
+	sltu	$t0,$i,$num
+	bnez	$t0,.Louter
+
+	.set	noreorder
+	$PTR_ADD $tj,$sp,$num	# &tp[num]
+	move	$tp,$sp
+	move	$ap,$sp
+	li	$hi0,0		# clear borrow bit
+
+.align	4
+.Lsub:	$LD	$lo0,($tp)
+	$LD	$lo1,($np)
+	$PTR_ADD $tp,$BNSZ
+	$PTR_ADD $np,$BNSZ
+	$SUBU	$lo1,$lo0,$lo1	# tp[i]-np[i]
+	sgtu	$at,$lo1,$lo0
+	$SUBU	$lo0,$lo1,$hi0
+	sgtu	$hi0,$lo0,$lo1
+	$ST	$lo0,($rp)
+	or	$hi0,$at
+	sltu	$at,$tp,$tj
+	bnez	$at,.Lsub
+	$PTR_ADD $rp,$BNSZ
+
+	$SUBU	$hi0,$hi1,$hi0	# handle upmost overflow bit
+	move	$tp,$sp
+	$PTR_SUB $rp,$num	# restore rp
+	not	$hi1,$hi0
+
+	and	$ap,$hi0,$sp
+	and	$bp,$hi1,$rp
+	or	$ap,$ap,$bp	# ap=borrow?tp:rp
+
+.align	4
+.Lcopy:	$LD	$aj,($ap)
+	$PTR_ADD $ap,$BNSZ
+	$ST	$zero,($tp)
+	$PTR_ADD $tp,$BNSZ
+	sltu	$at,$tp,$tj
+	$ST	$aj,($rp)
+	bnez	$at,.Lcopy
+	$PTR_ADD $rp,$BNSZ
+
+	li	$a0,1
+	li	$t0,1
+
+	.set	noreorder
+	move	$sp,$fp
+	$REG_L	$fp,($FRAMESIZE-1)*$SZREG($sp)
+	$REG_L	$s11,($FRAMESIZE-2)*$SZREG($sp)
+	$REG_L	$s10,($FRAMESIZE-3)*$SZREG($sp)
+	$REG_L	$s9,($FRAMESIZE-4)*$SZREG($sp)
+	$REG_L	$s8,($FRAMESIZE-5)*$SZREG($sp)
+	$REG_L	$s7,($FRAMESIZE-6)*$SZREG($sp)
+	$REG_L	$s6,($FRAMESIZE-7)*$SZREG($sp)
+	$REG_L	$s5,($FRAMESIZE-8)*$SZREG($sp)
+	$REG_L	$s4,($FRAMESIZE-9)*$SZREG($sp)
+___
+$code.=<<___ if ($flavour =~ /nubi/i);
+	$REG_L	$s3,($FRAMESIZE-10)*$SZREG($sp)
+	$REG_L	$s2,($FRAMESIZE-11)*$SZREG($sp)
+	$REG_L	$s1,($FRAMESIZE-12)*$SZREG($sp)
+	$REG_L	$s0,($FRAMESIZE-13)*$SZREG($sp)
+___
+$code.=<<___;
+	jr	$ra
+	$PTR_ADD $sp,$FRAMESIZE*$SZREG
+.end	bn_mul_mont_internal
+.rdata
+.asciiz	"Montgomery Multiplication for MIPS, CRYPTOGAMS by "
+___
+
+$code =~ s/\`([^\`]*)\`/eval $1/gem;
+
+print $code;
+close STDOUT;
diff --git a/crypto/bn/asm/mips.pl b/crypto/bn/asm/mips.pl
new file mode 100644
index 000000000..420f01f3a
--- /dev/null
+++ b/crypto/bn/asm/mips.pl
@@ -0,0 +1,2241 @@
+#! /usr/bin/env perl
+# Copyright 2010-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+#
+# ====================================================================
+# Written by Andy Polyakov  for the OpenSSL
+# project.
+#
+# Rights for redistribution and usage in source and binary forms are
+# granted according to the OpenSSL license. Warranty of any kind is
+# disclaimed.
+# ====================================================================
+
+
+# July 1999
+#
+# This is drop-in MIPS III/IV ISA replacement for crypto/bn/bn_asm.c.
+#
+# The module is designed to work with either of the "new" MIPS ABI(5),
+# namely N32 or N64, offered by IRIX 6.x. It's not meant to work under
+# IRIX 5.x not only because it doesn't support new ABIs but also
+# because 5.x kernels put R4x00 CPU into 32-bit mode and all those
+# 64-bit instructions (daddu, dmultu, etc.) found below gonna only
+# cause illegal instruction exception:-(
+#
+# In addition the code depends on preprocessor flags set up by MIPSpro
+# compiler driver (either as or cc) and therefore (probably?) can't be
+# compiled by the GNU assembler. GNU C driver manages fine though...
+# I mean as long as -mmips-as is specified or is the default option,
+# because then it simply invokes /usr/bin/as which in turn takes
+# perfect care of the preprocessor definitions. Another neat feature
+# offered by the MIPSpro assembler is an optimization pass. This gave
+# me the opportunity to have the code looking more regular as all those
+# architecture dependent instruction rescheduling details were left to
+# the assembler. Cool, huh?
+#
+# Performance improvement is astonishing! 'apps/openssl speed rsa dsa'
+# goes way over 3 times faster!
+#
+#					
+
+# October 2010
+#
+# Adapt the module even for 32-bit ABIs and other OSes. The former was
+# achieved by mechanical replacement of 64-bit arithmetic instructions
+# such as dmultu, daddu, etc. with their 32-bit counterparts and
+# adjusting offsets denoting multiples of BN_ULONG. Above mentioned
+# >3x performance improvement naturally does not apply to 32-bit code
+# [because there is no instruction 32-bit compiler can't use], one
+# has to content with 40-85% improvement depending on benchmark and
+# key length, more for longer keys.
+
+$flavour = shift || "o32";
+while (($output=shift) && ($output!~/\w[\w\-]*\.\w+$/)) {}
+open STDOUT,">$output";
+
+if ($flavour =~ /64|n32/i) {
+	$LD="ld";
+	$ST="sd";
+	$MULTU="dmultu";
+	$DIVU="ddivu";
+	$ADDU="daddu";
+	$SUBU="dsubu";
+	$SRL="dsrl";
+	$SLL="dsll";
+	$BNSZ=8;
+	$PTR_ADD="daddu";
+	$PTR_SUB="dsubu";
+	$SZREG=8;
+	$REG_S="sd";
+	$REG_L="ld";
+} else {
+	$LD="lw";
+	$ST="sw";
+	$MULTU="multu";
+	$DIVU="divu";
+	$ADDU="addu";
+	$SUBU="subu";
+	$SRL="srl";
+	$SLL="sll";
+	$BNSZ=4;
+	$PTR_ADD="addu";
+	$PTR_SUB="subu";
+	$SZREG=4;
+	$REG_S="sw";
+	$REG_L="lw";
+	$code=".set	mips2\n";
+}
+
+# Below is N32/64 register layout used in the original module.
+#
+($zero,$at,$v0,$v1)=map("\$$_",(0..3));
+($a0,$a1,$a2,$a3,$a4,$a5,$a6,$a7)=map("\$$_",(4..11));
+($t0,$t1,$t2,$t3,$t8,$t9)=map("\$$_",(12..15,24,25));
+($s0,$s1,$s2,$s3,$s4,$s5,$s6,$s7)=map("\$$_",(16..23));
+($gp,$sp,$fp,$ra)=map("\$$_",(28..31));
+($ta0,$ta1,$ta2,$ta3)=($a4,$a5,$a6,$a7);
+#
+# No special adaptation is required for O32. NUBI on the other hand
+# is treated by saving/restoring ($v1,$t0..$t3).
+
+$gp=$v1 if ($flavour =~ /nubi/i);
+
+$minus4=$v1;
+
+$code.=<<___;
+.rdata
+.asciiz	"mips3.s, Version 1.2"
+.asciiz	"MIPS II/III/IV ISA artwork by Andy Polyakov "
+
+.text
+.set	noat
+
+.align	5
+.globl	bn_mul_add_words
+.ent	bn_mul_add_words
+bn_mul_add_words:
+	.set	noreorder
+	bgtz	$a2,bn_mul_add_words_internal
+	move	$v0,$zero
+	jr	$ra
+	move	$a0,$v0
+.end	bn_mul_add_words
+
+.align	5
+.ent	bn_mul_add_words_internal
+bn_mul_add_words_internal:
+___
+$code.=<<___ if ($flavour =~ /nubi/i);
+	.frame	$sp,6*$SZREG,$ra
+	.mask	0x8000f008,-$SZREG
+	.set	noreorder
+	$PTR_SUB $sp,6*$SZREG
+	$REG_S	$ra,5*$SZREG($sp)
+	$REG_S	$t3,4*$SZREG($sp)
+	$REG_S	$t2,3*$SZREG($sp)
+	$REG_S	$t1,2*$SZREG($sp)
+	$REG_S	$t0,1*$SZREG($sp)
+	$REG_S	$gp,0*$SZREG($sp)
+___
+$code.=<<___;
+	.set	reorder
+	li	$minus4,-4
+	and	$ta0,$a2,$minus4
+	beqz	$ta0,.L_bn_mul_add_words_tail
+
+.L_bn_mul_add_words_loop:
+	$LD	$t0,0($a1)
+	$MULTU	$t0,$a3
+	$LD	$t1,0($a0)
+	$LD	$t2,$BNSZ($a1)
+	$LD	$t3,$BNSZ($a0)
+	$LD	$ta0,2*$BNSZ($a1)
+	$LD	$ta1,2*$BNSZ($a0)
+	$ADDU	$t1,$v0
+	sltu	$v0,$t1,$v0	# All manuals say it "compares 32-bit
+				# values", but it seems to work fine
+				# even on 64-bit registers.
+	mflo	$at
+	mfhi	$t0
+	$ADDU	$t1,$at
+	$ADDU	$v0,$t0
+	 $MULTU	$t2,$a3
+	sltu	$at,$t1,$at
+	$ST	$t1,0($a0)
+	$ADDU	$v0,$at
+
+	$LD	$ta2,3*$BNSZ($a1)
+	$LD	$ta3,3*$BNSZ($a0)
+	$ADDU	$t3,$v0
+	sltu	$v0,$t3,$v0
+	mflo	$at
+	mfhi	$t2
+	$ADDU	$t3,$at
+	$ADDU	$v0,$t2
+	 $MULTU	$ta0,$a3
+	sltu	$at,$t3,$at
+	$ST	$t3,$BNSZ($a0)
+	$ADDU	$v0,$at
+
+	subu	$a2,4
+	$PTR_ADD $a0,4*$BNSZ
+	$PTR_ADD $a1,4*$BNSZ
+	$ADDU	$ta1,$v0
+	sltu	$v0,$ta1,$v0
+	mflo	$at
+	mfhi	$ta0
+	$ADDU	$ta1,$at
+	$ADDU	$v0,$ta0
+	 $MULTU	$ta2,$a3
+	sltu	$at,$ta1,$at
+	$ST	$ta1,-2*$BNSZ($a0)
+	$ADDU	$v0,$at
+
+
+	and	$ta0,$a2,$minus4
+	$ADDU	$ta3,$v0
+	sltu	$v0,$ta3,$v0
+	mflo	$at
+	mfhi	$ta2
+	$ADDU	$ta3,$at
+	$ADDU	$v0,$ta2
+	sltu	$at,$ta3,$at
+	$ST	$ta3,-$BNSZ($a0)
+	.set	noreorder
+	bgtz	$ta0,.L_bn_mul_add_words_loop
+	$ADDU	$v0,$at
+
+	beqz	$a2,.L_bn_mul_add_words_return
+	nop
+
+.L_bn_mul_add_words_tail:
+	.set	reorder
+	$LD	$t0,0($a1)
+	$MULTU	$t0,$a3
+	$LD	$t1,0($a0)
+	subu	$a2,1
+	$ADDU	$t1,$v0
+	sltu	$v0,$t1,$v0
+	mflo	$at
+	mfhi	$t0
+	$ADDU	$t1,$at
+	$ADDU	$v0,$t0
+	sltu	$at,$t1,$at
+	$ST	$t1,0($a0)
+	$ADDU	$v0,$at
+	beqz	$a2,.L_bn_mul_add_words_return
+
+	$LD	$t0,$BNSZ($a1)
+	$MULTU	$t0,$a3
+	$LD	$t1,$BNSZ($a0)
+	subu	$a2,1
+	$ADDU	$t1,$v0
+	sltu	$v0,$t1,$v0
+	mflo	$at
+	mfhi	$t0
+	$ADDU	$t1,$at
+	$ADDU	$v0,$t0
+	sltu	$at,$t1,$at
+	$ST	$t1,$BNSZ($a0)
+	$ADDU	$v0,$at
+	beqz	$a2,.L_bn_mul_add_words_return
+
+	$LD	$t0,2*$BNSZ($a1)
+	$MULTU	$t0,$a3
+	$LD	$t1,2*$BNSZ($a0)
+	$ADDU	$t1,$v0
+	sltu	$v0,$t1,$v0
+	mflo	$at
+	mfhi	$t0
+	$ADDU	$t1,$at
+	$ADDU	$v0,$t0
+	sltu	$at,$t1,$at
+	$ST	$t1,2*$BNSZ($a0)
+	$ADDU	$v0,$at
+
+.L_bn_mul_add_words_return:
+	.set	noreorder
+___
+$code.=<<___ if ($flavour =~ /nubi/i);
+	$REG_L	$t3,4*$SZREG($sp)
+	$REG_L	$t2,3*$SZREG($sp)
+	$REG_L	$t1,2*$SZREG($sp)
+	$REG_L	$t0,1*$SZREG($sp)
+	$REG_L	$gp,0*$SZREG($sp)
+	$PTR_ADD $sp,6*$SZREG
+___
+$code.=<<___;
+	jr	$ra
+	move	$a0,$v0
+.end	bn_mul_add_words_internal
+
+.align	5
+.globl	bn_mul_words
+.ent	bn_mul_words
+bn_mul_words:
+	.set	noreorder
+	bgtz	$a2,bn_mul_words_internal
+	move	$v0,$zero
+	jr	$ra
+	move	$a0,$v0
+.end	bn_mul_words
+
+.align	5
+.ent	bn_mul_words_internal
+bn_mul_words_internal:
+___
+$code.=<<___ if ($flavour =~ /nubi/i);
+	.frame	$sp,6*$SZREG,$ra
+	.mask	0x8000f008,-$SZREG
+	.set	noreorder
+	$PTR_SUB $sp,6*$SZREG
+	$REG_S	$ra,5*$SZREG($sp)
+	$REG_S	$t3,4*$SZREG($sp)
+	$REG_S	$t2,3*$SZREG($sp)
+	$REG_S	$t1,2*$SZREG($sp)
+	$REG_S	$t0,1*$SZREG($sp)
+	$REG_S	$gp,0*$SZREG($sp)
+___
+$code.=<<___;
+	.set	reorder
+	li	$minus4,-4
+	and	$ta0,$a2,$minus4
+	beqz	$ta0,.L_bn_mul_words_tail
+
+.L_bn_mul_words_loop:
+	$LD	$t0,0($a1)
+	$MULTU	$t0,$a3
+	$LD	$t2,$BNSZ($a1)
+	$LD	$ta0,2*$BNSZ($a1)
+	$LD	$ta2,3*$BNSZ($a1)
+	mflo	$at
+	mfhi	$t0
+	$ADDU	$v0,$at
+	sltu	$t1,$v0,$at
+	 $MULTU	$t2,$a3
+	$ST	$v0,0($a0)
+	$ADDU	$v0,$t1,$t0
+
+	subu	$a2,4
+	$PTR_ADD $a0,4*$BNSZ
+	$PTR_ADD $a1,4*$BNSZ
+	mflo	$at
+	mfhi	$t2
+	$ADDU	$v0,$at
+	sltu	$t3,$v0,$at
+	 $MULTU	$ta0,$a3
+	$ST	$v0,-3*$BNSZ($a0)
+	$ADDU	$v0,$t3,$t2
+
+	mflo	$at
+	mfhi	$ta0
+	$ADDU	$v0,$at
+	sltu	$ta1,$v0,$at
+	 $MULTU	$ta2,$a3
+	$ST	$v0,-2*$BNSZ($a0)
+	$ADDU	$v0,$ta1,$ta0
+
+	and	$ta0,$a2,$minus4
+	mflo	$at
+	mfhi	$ta2
+	$ADDU	$v0,$at
+	sltu	$ta3,$v0,$at
+	$ST	$v0,-$BNSZ($a0)
+	.set	noreorder
+	bgtz	$ta0,.L_bn_mul_words_loop
+	$ADDU	$v0,$ta3,$ta2
+
+	beqz	$a2,.L_bn_mul_words_return
+	nop
+
+.L_bn_mul_words_tail:
+	.set	reorder
+	$LD	$t0,0($a1)
+	$MULTU	$t0,$a3
+	subu	$a2,1
+	mflo	$at
+	mfhi	$t0
+	$ADDU	$v0,$at
+	sltu	$t1,$v0,$at
+	$ST	$v0,0($a0)
+	$ADDU	$v0,$t1,$t0
+	beqz	$a2,.L_bn_mul_words_return
+
+	$LD	$t0,$BNSZ($a1)
+	$MULTU	$t0,$a3
+	subu	$a2,1
+	mflo	$at
+	mfhi	$t0
+	$ADDU	$v0,$at
+	sltu	$t1,$v0,$at
+	$ST	$v0,$BNSZ($a0)
+	$ADDU	$v0,$t1,$t0
+	beqz	$a2,.L_bn_mul_words_return
+
+	$LD	$t0,2*$BNSZ($a1)
+	$MULTU	$t0,$a3
+	mflo	$at
+	mfhi	$t0
+	$ADDU	$v0,$at
+	sltu	$t1,$v0,$at
+	$ST	$v0,2*$BNSZ($a0)
+	$ADDU	$v0,$t1,$t0
+
+.L_bn_mul_words_return:
+	.set	noreorder
+___
+$code.=<<___ if ($flavour =~ /nubi/i);
+	$REG_L	$t3,4*$SZREG($sp)
+	$REG_L	$t2,3*$SZREG($sp)
+	$REG_L	$t1,2*$SZREG($sp)
+	$REG_L	$t0,1*$SZREG($sp)
+	$REG_L	$gp,0*$SZREG($sp)
+	$PTR_ADD $sp,6*$SZREG
+___
+$code.=<<___;
+	jr	$ra
+	move	$a0,$v0
+.end	bn_mul_words_internal
+
+.align	5
+.globl	bn_sqr_words
+.ent	bn_sqr_words
+bn_sqr_words:
+	.set	noreorder
+	bgtz	$a2,bn_sqr_words_internal
+	move	$v0,$zero
+	jr	$ra
+	move	$a0,$v0
+.end	bn_sqr_words
+
+.align	5
+.ent	bn_sqr_words_internal
+bn_sqr_words_internal:
+___
+$code.=<<___ if ($flavour =~ /nubi/i);
+	.frame	$sp,6*$SZREG,$ra
+	.mask	0x8000f008,-$SZREG
+	.set	noreorder
+	$PTR_SUB $sp,6*$SZREG
+	$REG_S	$ra,5*$SZREG($sp)
+	$REG_S	$t3,4*$SZREG($sp)
+	$REG_S	$t2,3*$SZREG($sp)
+	$REG_S	$t1,2*$SZREG($sp)
+	$REG_S	$t0,1*$SZREG($sp)
+	$REG_S	$gp,0*$SZREG($sp)
+___
+$code.=<<___;
+	.set	reorder
+	li	$minus4,-4
+	and	$ta0,$a2,$minus4
+	beqz	$ta0,.L_bn_sqr_words_tail
+
+.L_bn_sqr_words_loop:
+	$LD	$t0,0($a1)
+	$MULTU	$t0,$t0
+	$LD	$t2,$BNSZ($a1)
+	$LD	$ta0,2*$BNSZ($a1)
+	$LD	$ta2,3*$BNSZ($a1)
+	mflo	$t1
+	mfhi	$t0
+	$ST	$t1,0($a0)
+	$ST	$t0,$BNSZ($a0)
+
+	$MULTU	$t2,$t2
+	subu	$a2,4
+	$PTR_ADD $a0,8*$BNSZ
+	$PTR_ADD $a1,4*$BNSZ
+	mflo	$t3
+	mfhi	$t2
+	$ST	$t3,-6*$BNSZ($a0)
+	$ST	$t2,-5*$BNSZ($a0)
+
+	$MULTU	$ta0,$ta0
+	mflo	$ta1
+	mfhi	$ta0
+	$ST	$ta1,-4*$BNSZ($a0)
+	$ST	$ta0,-3*$BNSZ($a0)
+
+
+	$MULTU	$ta2,$ta2
+	and	$ta0,$a2,$minus4
+	mflo	$ta3
+	mfhi	$ta2
+	$ST	$ta3,-2*$BNSZ($a0)
+
+	.set	noreorder
+	bgtz	$ta0,.L_bn_sqr_words_loop
+	$ST	$ta2,-$BNSZ($a0)
+
+	beqz	$a2,.L_bn_sqr_words_return
+	nop
+
+.L_bn_sqr_words_tail:
+	.set	reorder
+	$LD	$t0,0($a1)
+	$MULTU	$t0,$t0
+	subu	$a2,1
+	mflo	$t1
+	mfhi	$t0
+	$ST	$t1,0($a0)
+	$ST	$t0,$BNSZ($a0)
+	beqz	$a2,.L_bn_sqr_words_return
+
+	$LD	$t0,$BNSZ($a1)
+	$MULTU	$t0,$t0
+	subu	$a2,1
+	mflo	$t1
+	mfhi	$t0
+	$ST	$t1,2*$BNSZ($a0)
+	$ST	$t0,3*$BNSZ($a0)
+	beqz	$a2,.L_bn_sqr_words_return
+
+	$LD	$t0,2*$BNSZ($a1)
+	$MULTU	$t0,$t0
+	mflo	$t1
+	mfhi	$t0
+	$ST	$t1,4*$BNSZ($a0)
+	$ST	$t0,5*$BNSZ($a0)
+
+.L_bn_sqr_words_return:
+	.set	noreorder
+___
+$code.=<<___ if ($flavour =~ /nubi/i);
+	$REG_L	$t3,4*$SZREG($sp)
+	$REG_L	$t2,3*$SZREG($sp)
+	$REG_L	$t1,2*$SZREG($sp)
+	$REG_L	$t0,1*$SZREG($sp)
+	$REG_L	$gp,0*$SZREG($sp)
+	$PTR_ADD $sp,6*$SZREG
+___
+$code.=<<___;
+	jr	$ra
+	move	$a0,$v0
+
+.end	bn_sqr_words_internal
+
+.align	5
+.globl	bn_add_words
+.ent	bn_add_words
+bn_add_words:
+	.set	noreorder
+	bgtz	$a3,bn_add_words_internal
+	move	$v0,$zero
+	jr	$ra
+	move	$a0,$v0
+.end	bn_add_words
+
+.align	5
+.ent	bn_add_words_internal
+bn_add_words_internal:
+___
+$code.=<<___ if ($flavour =~ /nubi/i);
+	.frame	$sp,6*$SZREG,$ra
+	.mask	0x8000f008,-$SZREG
+	.set	noreorder
+	$PTR_SUB $sp,6*$SZREG
+	$REG_S	$ra,5*$SZREG($sp)
+	$REG_S	$t3,4*$SZREG($sp)
+	$REG_S	$t2,3*$SZREG($sp)
+	$REG_S	$t1,2*$SZREG($sp)
+	$REG_S	$t0,1*$SZREG($sp)
+	$REG_S	$gp,0*$SZREG($sp)
+___
+$code.=<<___;
+	.set	reorder
+	li	$minus4,-4
+	and	$at,$a3,$minus4
+	beqz	$at,.L_bn_add_words_tail
+
+.L_bn_add_words_loop:
+	$LD	$t0,0($a1)
+	$LD	$ta0,0($a2)
+	subu	$a3,4
+	$LD	$t1,$BNSZ($a1)
+	and	$at,$a3,$minus4
+	$LD	$t2,2*$BNSZ($a1)
+	$PTR_ADD $a2,4*$BNSZ
+	$LD	$t3,3*$BNSZ($a1)
+	$PTR_ADD $a0,4*$BNSZ
+	$LD	$ta1,-3*$BNSZ($a2)
+	$PTR_ADD $a1,4*$BNSZ
+	$LD	$ta2,-2*$BNSZ($a2)
+	$LD	$ta3,-$BNSZ($a2)
+	$ADDU	$ta0,$t0
+	sltu	$t8,$ta0,$t0
+	$ADDU	$t0,$ta0,$v0
+	sltu	$v0,$t0,$ta0
+	$ST	$t0,-4*$BNSZ($a0)
+	$ADDU	$v0,$t8
+
+	$ADDU	$ta1,$t1
+	sltu	$t9,$ta1,$t1
+	$ADDU	$t1,$ta1,$v0
+	sltu	$v0,$t1,$ta1
+	$ST	$t1,-3*$BNSZ($a0)
+	$ADDU	$v0,$t9
+
+	$ADDU	$ta2,$t2
+	sltu	$t8,$ta2,$t2
+	$ADDU	$t2,$ta2,$v0
+	sltu	$v0,$t2,$ta2
+	$ST	$t2,-2*$BNSZ($a0)
+	$ADDU	$v0,$t8
+	
+	$ADDU	$ta3,$t3
+	sltu	$t9,$ta3,$t3
+	$ADDU	$t3,$ta3,$v0
+	sltu	$v0,$t3,$ta3
+	$ST	$t3,-$BNSZ($a0)
+	
+	.set	noreorder
+	bgtz	$at,.L_bn_add_words_loop
+	$ADDU	$v0,$t9
+
+	beqz	$a3,.L_bn_add_words_return
+	nop
+
+.L_bn_add_words_tail:
+	.set	reorder
+	$LD	$t0,0($a1)
+	$LD	$ta0,0($a2)
+	$ADDU	$ta0,$t0
+	subu	$a3,1
+	sltu	$t8,$ta0,$t0
+	$ADDU	$t0,$ta0,$v0
+	sltu	$v0,$t0,$ta0
+	$ST	$t0,0($a0)
+	$ADDU	$v0,$t8
+	beqz	$a3,.L_bn_add_words_return
+
+	$LD	$t1,$BNSZ($a1)
+	$LD	$ta1,$BNSZ($a2)
+	$ADDU	$ta1,$t1
+	subu	$a3,1
+	sltu	$t9,$ta1,$t1
+	$ADDU	$t1,$ta1,$v0
+	sltu	$v0,$t1,$ta1
+	$ST	$t1,$BNSZ($a0)
+	$ADDU	$v0,$t9
+	beqz	$a3,.L_bn_add_words_return
+
+	$LD	$t2,2*$BNSZ($a1)
+	$LD	$ta2,2*$BNSZ($a2)
+	$ADDU	$ta2,$t2
+	sltu	$t8,$ta2,$t2
+	$ADDU	$t2,$ta2,$v0
+	sltu	$v0,$t2,$ta2
+	$ST	$t2,2*$BNSZ($a0)
+	$ADDU	$v0,$t8
+
+.L_bn_add_words_return:
+	.set	noreorder
+___
+$code.=<<___ if ($flavour =~ /nubi/i);
+	$REG_L	$t3,4*$SZREG($sp)
+	$REG_L	$t2,3*$SZREG($sp)
+	$REG_L	$t1,2*$SZREG($sp)
+	$REG_L	$t0,1*$SZREG($sp)
+	$REG_L	$gp,0*$SZREG($sp)
+	$PTR_ADD $sp,6*$SZREG
+___
+$code.=<<___;
+	jr	$ra
+	move	$a0,$v0
+
+.end	bn_add_words_internal
+
+.align	5
+.globl	bn_sub_words
+.ent	bn_sub_words
+bn_sub_words:
+	.set	noreorder
+	bgtz	$a3,bn_sub_words_internal
+	move	$v0,$zero
+	jr	$ra
+	move	$a0,$zero
+.end	bn_sub_words
+
+.align	5
+.ent	bn_sub_words_internal
+bn_sub_words_internal:
+___
+$code.=<<___ if ($flavour =~ /nubi/i);
+	.frame	$sp,6*$SZREG,$ra
+	.mask	0x8000f008,-$SZREG
+	.set	noreorder
+	$PTR_SUB $sp,6*$SZREG
+	$REG_S	$ra,5*$SZREG($sp)
+	$REG_S	$t3,4*$SZREG($sp)
+	$REG_S	$t2,3*$SZREG($sp)
+	$REG_S	$t1,2*$SZREG($sp)
+	$REG_S	$t0,1*$SZREG($sp)
+	$REG_S	$gp,0*$SZREG($sp)
+___
+$code.=<<___;
+	.set	reorder
+	li	$minus4,-4
+	and	$at,$a3,$minus4
+	beqz	$at,.L_bn_sub_words_tail
+
+.L_bn_sub_words_loop:
+	$LD	$t0,0($a1)
+	$LD	$ta0,0($a2)
+	subu	$a3,4
+	$LD	$t1,$BNSZ($a1)
+	and	$at,$a3,$minus4
+	$LD	$t2,2*$BNSZ($a1)
+	$PTR_ADD $a2,4*$BNSZ
+	$LD	$t3,3*$BNSZ($a1)
+	$PTR_ADD $a0,4*$BNSZ
+	$LD	$ta1,-3*$BNSZ($a2)
+	$PTR_ADD $a1,4*$BNSZ
+	$LD	$ta2,-2*$BNSZ($a2)
+	$LD	$ta3,-$BNSZ($a2)
+	sltu	$t8,$t0,$ta0
+	$SUBU	$ta0,$t0,$ta0
+	$SUBU	$t0,$ta0,$v0
+	sgtu	$v0,$t0,$ta0
+	$ST	$t0,-4*$BNSZ($a0)
+	$ADDU	$v0,$t8
+
+	sltu	$t9,$t1,$ta1
+	$SUBU	$ta1,$t1,$ta1
+	$SUBU	$t1,$ta1,$v0
+	sgtu	$v0,$t1,$ta1
+	$ST	$t1,-3*$BNSZ($a0)
+	$ADDU	$v0,$t9
+
+
+	sltu	$t8,$t2,$ta2
+	$SUBU	$ta2,$t2,$ta2
+	$SUBU	$t2,$ta2,$v0
+	sgtu	$v0,$t2,$ta2
+	$ST	$t2,-2*$BNSZ($a0)
+	$ADDU	$v0,$t8
+
+	sltu	$t9,$t3,$ta3
+	$SUBU	$ta3,$t3,$ta3
+	$SUBU	$t3,$ta3,$v0
+	sgtu	$v0,$t3,$ta3
+	$ST	$t3,-$BNSZ($a0)
+
+	.set	noreorder
+	bgtz	$at,.L_bn_sub_words_loop
+	$ADDU	$v0,$t9
+
+	beqz	$a3,.L_bn_sub_words_return
+	nop
+
+.L_bn_sub_words_tail:
+	.set	reorder
+	$LD	$t0,0($a1)
+	$LD	$ta0,0($a2)
+	subu	$a3,1
+	sltu	$t8,$t0,$ta0
+	$SUBU	$ta0,$t0,$ta0
+	$SUBU	$t0,$ta0,$v0
+	sgtu	$v0,$t0,$ta0
+	$ST	$t0,0($a0)
+	$ADDU	$v0,$t8
+	beqz	$a3,.L_bn_sub_words_return
+
+	$LD	$t1,$BNSZ($a1)
+	subu	$a3,1
+	$LD	$ta1,$BNSZ($a2)
+	sltu	$t9,$t1,$ta1
+	$SUBU	$ta1,$t1,$ta1
+	$SUBU	$t1,$ta1,$v0
+	sgtu	$v0,$t1,$ta1
+	$ST	$t1,$BNSZ($a0)
+	$ADDU	$v0,$t9
+	beqz	$a3,.L_bn_sub_words_return
+
+	$LD	$t2,2*$BNSZ($a1)
+	$LD	$ta2,2*$BNSZ($a2)
+	sltu	$t8,$t2,$ta2
+	$SUBU	$ta2,$t2,$ta2
+	$SUBU	$t2,$ta2,$v0
+	sgtu	$v0,$t2,$ta2
+	$ST	$t2,2*$BNSZ($a0)
+	$ADDU	$v0,$t8
+
+.L_bn_sub_words_return:
+	.set	noreorder
+___
+$code.=<<___ if ($flavour =~ /nubi/i);
+	$REG_L	$t3,4*$SZREG($sp)
+	$REG_L	$t2,3*$SZREG($sp)
+	$REG_L	$t1,2*$SZREG($sp)
+	$REG_L	$t0,1*$SZREG($sp)
+	$REG_L	$gp,0*$SZREG($sp)
+	$PTR_ADD $sp,6*$SZREG
+___
+$code.=<<___;
+	jr	$ra
+	move	$a0,$v0
+.end	bn_sub_words_internal
+
+.align 5
+.globl	bn_div_3_words
+.ent	bn_div_3_words
+bn_div_3_words:
+	.set	noreorder
+	move	$a3,$a0		# we know that bn_div_words does not
+				# touch $a3, $ta2, $ta3 and preserves $a2
+				# so that we can save two arguments
+				# and return address in registers
+				# instead of stack:-)
+				
+	$LD	$a0,($a3)
+	move	$ta2,$a1
+	bne	$a0,$a2,bn_div_3_words_internal
+	$LD	$a1,-$BNSZ($a3)
+	li	$v0,-1
+	jr	$ra
+	move	$a0,$v0
+.end	bn_div_3_words
+
+.align	5
+.ent	bn_div_3_words_internal
+bn_div_3_words_internal:
+___
+$code.=<<___ if ($flavour =~ /nubi/i);
+	.frame	$sp,6*$SZREG,$ra
+	.mask	0x8000f008,-$SZREG
+	.set	noreorder
+	$PTR_SUB $sp,6*$SZREG
+	$REG_S	$ra,5*$SZREG($sp)
+	$REG_S	$t3,4*$SZREG($sp)
+	$REG_S	$t2,3*$SZREG($sp)
+	$REG_S	$t1,2*$SZREG($sp)
+	$REG_S	$t0,1*$SZREG($sp)
+	$REG_S	$gp,0*$SZREG($sp)
+___
+$code.=<<___;
+	.set	reorder
+	move	$ta3,$ra
+	bal	bn_div_words_internal
+	move	$ra,$ta3
+	$MULTU	$ta2,$v0
+	$LD	$t2,-2*$BNSZ($a3)
+	move	$ta0,$zero
+	mfhi	$t1
+	mflo	$t0
+	sltu	$t8,$t1,$a1
+.L_bn_div_3_words_inner_loop:
+	bnez	$t8,.L_bn_div_3_words_inner_loop_done
+	sgeu	$at,$t2,$t0
+	seq	$t9,$t1,$a1
+	and	$at,$t9
+	sltu	$t3,$t0,$ta2
+	$ADDU	$a1,$a2
+	$SUBU	$t1,$t3
+	$SUBU	$t0,$ta2
+	sltu	$t8,$t1,$a1
+	sltu	$ta0,$a1,$a2
+	or	$t8,$ta0
+	.set	noreorder
+	beqz	$at,.L_bn_div_3_words_inner_loop
+	$SUBU	$v0,1
+	$ADDU	$v0,1
+	.set	reorder
+.L_bn_div_3_words_inner_loop_done:
+	.set	noreorder
+___
+$code.=<<___ if ($flavour =~ /nubi/i);
+	$REG_L	$t3,4*$SZREG($sp)
+	$REG_L	$t2,3*$SZREG($sp)
+	$REG_L	$t1,2*$SZREG($sp)
+	$REG_L	$t0,1*$SZREG($sp)
+	$REG_L	$gp,0*$SZREG($sp)
+	$PTR_ADD $sp,6*$SZREG
+___
+$code.=<<___;
+	jr	$ra
+	move	$a0,$v0
+.end	bn_div_3_words_internal
+
+.align	5
+.globl	bn_div_words
+.ent	bn_div_words
+bn_div_words:
+	.set	noreorder
+	bnez	$a2,bn_div_words_internal
+	li	$v0,-1		# I would rather signal div-by-zero
+				# which can be done with 'break 7'
+	jr	$ra
+	move	$a0,$v0
+.end	bn_div_words
+
+.align	5
+.ent	bn_div_words_internal
+bn_div_words_internal:
+___
+$code.=<<___ if ($flavour =~ /nubi/i);
+	.frame	$sp,6*$SZREG,$ra
+	.mask	0x8000f008,-$SZREG
+	.set	noreorder
+	$PTR_SUB $sp,6*$SZREG
+	$REG_S	$ra,5*$SZREG($sp)
+	$REG_S	$t3,4*$SZREG($sp)
+	$REG_S	$t2,3*$SZREG($sp)
+	$REG_S	$t1,2*$SZREG($sp)
+	$REG_S	$t0,1*$SZREG($sp)
+	$REG_S	$gp,0*$SZREG($sp)
+___
+$code.=<<___;
+	move	$v1,$zero
+	bltz	$a2,.L_bn_div_words_body
+	move	$t9,$v1
+	$SLL	$a2,1
+	bgtz	$a2,.-4
+	addu	$t9,1
+
+	.set	reorder
+	negu	$t1,$t9
+	li	$t2,-1
+	$SLL	$t2,$t1
+	and	$t2,$a0
+	$SRL	$at,$a1,$t1
+	.set	noreorder
+	beqz	$t2,.+12
+	nop
+	break	6		# signal overflow
+	.set	reorder
+	$SLL	$a0,$t9
+	$SLL	$a1,$t9
+	or	$a0,$at
+___
+$QT=$ta0;
+$HH=$ta1;
+$DH=$v1;
+$code.=<<___;
+.L_bn_div_words_body:
+	$SRL	$DH,$a2,4*$BNSZ	# bits
+	sgeu	$at,$a0,$a2
+	.set	noreorder
+	beqz	$at,.+12
+	nop
+	$SUBU	$a0,$a2
+	.set	reorder
+
+	li	$QT,-1
+	$SRL	$HH,$a0,4*$BNSZ	# bits
+	$SRL	$QT,4*$BNSZ	# q=0xffffffff
+	beq	$DH,$HH,.L_bn_div_words_skip_div1
+	$DIVU	$zero,$a0,$DH
+	mflo	$QT
+.L_bn_div_words_skip_div1:
+	$MULTU	$a2,$QT
+	$SLL	$t3,$a0,4*$BNSZ	# bits
+	$SRL	$at,$a1,4*$BNSZ	# bits
+	or	$t3,$at
+	mflo	$t0
+	mfhi	$t1
+.L_bn_div_words_inner_loop1:
+	sltu	$t2,$t3,$t0
+	seq	$t8,$HH,$t1
+	sltu	$at,$HH,$t1
+	and	$t2,$t8
+	sltu	$v0,$t0,$a2
+	or	$at,$t2
+	.set	noreorder
+	beqz	$at,.L_bn_div_words_inner_loop1_done
+	$SUBU	$t1,$v0
+	$SUBU	$t0,$a2
+	b	.L_bn_div_words_inner_loop1
+	$SUBU	$QT,1
+	.set	reorder
+.L_bn_div_words_inner_loop1_done:
+
+	$SLL	$a1,4*$BNSZ	# bits
+	$SUBU	$a0,$t3,$t0
+	$SLL	$v0,$QT,4*$BNSZ	# bits
+
+	li	$QT,-1
+	$SRL	$HH,$a0,4*$BNSZ	# bits
+	$SRL	$QT,4*$BNSZ	# q=0xffffffff
+	beq	$DH,$HH,.L_bn_div_words_skip_div2
+	$DIVU	$zero,$a0,$DH
+	mflo	$QT
+.L_bn_div_words_skip_div2:
+	$MULTU	$a2,$QT
+	$SLL	$t3,$a0,4*$BNSZ	# bits
+	$SRL	$at,$a1,4*$BNSZ	# bits
+	or	$t3,$at
+	mflo	$t0
+	mfhi	$t1
+.L_bn_div_words_inner_loop2:
+	sltu	$t2,$t3,$t0
+	seq	$t8,$HH,$t1
+	sltu	$at,$HH,$t1
+	and	$t2,$t8
+	sltu	$v1,$t0,$a2
+	or	$at,$t2
+	.set	noreorder
+	beqz	$at,.L_bn_div_words_inner_loop2_done
+	$SUBU	$t1,$v1
+	$SUBU	$t0,$a2
+	b	.L_bn_div_words_inner_loop2
+	$SUBU	$QT,1
+	.set	reorder
+.L_bn_div_words_inner_loop2_done:
+
+	$SUBU	$a0,$t3,$t0
+	or	$v0,$QT
+	$SRL	$v1,$a0,$t9	# $v1 contains remainder if anybody wants it
+	$SRL	$a2,$t9		# restore $a2
+
+	.set	noreorder
+	move	$a1,$v1
+___
+$code.=<<___ if ($flavour =~ /nubi/i);
+	$REG_L	$t3,4*$SZREG($sp)
+	$REG_L	$t2,3*$SZREG($sp)
+	$REG_L	$t1,2*$SZREG($sp)
+	$REG_L	$t0,1*$SZREG($sp)
+	$REG_L	$gp,0*$SZREG($sp)
+	$PTR_ADD $sp,6*$SZREG
+___
+$code.=<<___;
+	jr	$ra
+	move	$a0,$v0
+.end	bn_div_words_internal
+___
+undef $HH; undef $QT; undef $DH;
+
+($a_0,$a_1,$a_2,$a_3)=($t0,$t1,$t2,$t3);
+($b_0,$b_1,$b_2,$b_3)=($ta0,$ta1,$ta2,$ta3);
+
+($a_4,$a_5,$a_6,$a_7)=($s0,$s2,$s4,$a1); # once we load a[7], no use for $a1
+($b_4,$b_5,$b_6,$b_7)=($s1,$s3,$s5,$a2); # once we load b[7], no use for $a2
+
+($t_1,$t_2,$c_1,$c_2,$c_3)=($t8,$t9,$v0,$v1,$a3);
+
+$code.=<<___;
+
+.align	5
+.globl	bn_mul_comba8
+.ent	bn_mul_comba8
+bn_mul_comba8:
+	.set	noreorder
+___
+$code.=<<___ if ($flavour =~ /nubi/i);
+	.frame	$sp,12*$SZREG,$ra
+	.mask	0x803ff008,-$SZREG
+	$PTR_SUB $sp,12*$SZREG
+	$REG_S	$ra,11*$SZREG($sp)
+	$REG_S	$s5,10*$SZREG($sp)
+	$REG_S	$s4,9*$SZREG($sp)
+	$REG_S	$s3,8*$SZREG($sp)
+	$REG_S	$s2,7*$SZREG($sp)
+	$REG_S	$s1,6*$SZREG($sp)
+	$REG_S	$s0,5*$SZREG($sp)
+	$REG_S	$t3,4*$SZREG($sp)
+	$REG_S	$t2,3*$SZREG($sp)
+	$REG_S	$t1,2*$SZREG($sp)
+	$REG_S	$t0,1*$SZREG($sp)
+	$REG_S	$gp,0*$SZREG($sp)
+___
+$code.=<<___ if ($flavour !~ /nubi/i);
+	.frame	$sp,6*$SZREG,$ra
+	.mask	0x003f0000,-$SZREG
+	$PTR_SUB $sp,6*$SZREG
+	$REG_S	$s5,5*$SZREG($sp)
+	$REG_S	$s4,4*$SZREG($sp)
+	$REG_S	$s3,3*$SZREG($sp)
+	$REG_S	$s2,2*$SZREG($sp)
+	$REG_S	$s1,1*$SZREG($sp)
+	$REG_S	$s0,0*$SZREG($sp)
+___
+$code.=<<___;
+
+	.set	reorder
+	$LD	$a_0,0($a1)	# If compiled with -mips3 option on
+				# R5000 box assembler barks on this
+				# 1ine with "should not have mult/div
+				# as last instruction in bb (R10K
+				# bug)" warning. If anybody out there
+				# has a clue about how to circumvent
+				# this do send me a note.
+				#		
+
+	$LD	$b_0,0($a2)
+	$LD	$a_1,$BNSZ($a1)
+	$LD	$a_2,2*$BNSZ($a1)
+	$MULTU	$a_0,$b_0		# mul_add_c(a[0],b[0],c1,c2,c3);
+	$LD	$a_3,3*$BNSZ($a1)
+	$LD	$b_1,$BNSZ($a2)
+	$LD	$b_2,2*$BNSZ($a2)
+	$LD	$b_3,3*$BNSZ($a2)
+	mflo	$c_1
+	mfhi	$c_2
+
+	$LD	$a_4,4*$BNSZ($a1)
+	$LD	$a_5,5*$BNSZ($a1)
+	$MULTU	$a_0,$b_1		# mul_add_c(a[0],b[1],c2,c3,c1);
+	$LD	$a_6,6*$BNSZ($a1)
+	$LD	$a_7,7*$BNSZ($a1)
+	$LD	$b_4,4*$BNSZ($a2)
+	$LD	$b_5,5*$BNSZ($a2)
+	mflo	$t_1
+	mfhi	$t_2
+	$ADDU	$c_2,$t_1
+	sltu	$at,$c_2,$t_1
+	$MULTU	$a_1,$b_0		# mul_add_c(a[1],b[0],c2,c3,c1);
+	$ADDU	$c_3,$t_2,$at
+	$LD	$b_6,6*$BNSZ($a2)
+	$LD	$b_7,7*$BNSZ($a2)
+	$ST	$c_1,0($a0)	# r[0]=c1;
+	mflo	$t_1
+	mfhi	$t_2
+	$ADDU	$c_2,$t_1
+	sltu	$at,$c_2,$t_1
+	 $MULTU	$a_2,$b_0		# mul_add_c(a[2],b[0],c3,c1,c2);
+	$ADDU	$t_2,$at
+	$ADDU	$c_3,$t_2
+	sltu	$c_1,$c_3,$t_2
+	$ST	$c_2,$BNSZ($a0)	# r[1]=c2;
+
+	mflo	$t_1
+	mfhi	$t_2
+	$ADDU	$c_3,$t_1
+	sltu	$at,$c_3,$t_1
+	$MULTU	$a_1,$b_1		# mul_add_c(a[1],b[1],c3,c1,c2);
+	$ADDU	$t_2,$at
+	$ADDU	$c_1,$t_2
+	mflo	$t_1
+	mfhi	$t_2
+	$ADDU	$c_3,$t_1
+	sltu	$at,$c_3,$t_1
+	$MULTU	$a_0,$b_2		# mul_add_c(a[0],b[2],c3,c1,c2);
+	$ADDU	$t_2,$at
+	$ADDU	$c_1,$t_2
+	sltu	$c_2,$c_1,$t_2
+	mflo	$t_1
+	mfhi	$t_2
+	$ADDU	$c_3,$t_1
+	sltu	$at,$c_3,$t_1
+	 $MULTU	$a_0,$b_3		# mul_add_c(a[0],b[3],c1,c2,c3);
+	$ADDU	$t_2,$at
+	$ADDU	$c_1,$t_2
+	sltu	$at,$c_1,$t_2
+	$ADDU	$c_2,$at
+	$ST	$c_3,2*$BNSZ($a0)	# r[2]=c3;
+
+	mflo	$t_1
+	mfhi	$t_2
+	$ADDU	$c_1,$t_1
+	sltu	$at,$c_1,$t_1
+	$MULTU	$a_1,$b_2		# mul_add_c(a[1],b[2],c1,c2,c3);
+	$ADDU	$t_2,$at
+	$ADDU	$c_2,$t_2
+	sltu	$c_3,$c_2,$t_2
+	mflo	$t_1
+	mfhi	$t_2
+	$ADDU	$c_1,$t_1
+	sltu	$at,$c_1,$t_1
+	$MULTU	$a_2,$b_1		# mul_add_c(a[2],b[1],c1,c2,c3);
+	$ADDU	$t_2,$at
+	$ADDU	$c_2,$t_2
+	sltu	$at,$c_2,$t_2
+	$ADDU	$c_3,$at
+	mflo	$t_1
+	mfhi	$t_2
+	$ADDU	$c_1,$t_1
+	sltu	$at,$c_1,$t_1
+	$MULTU	$a_3,$b_0		# mul_add_c(a[3],b[0],c1,c2,c3);
+	$ADDU	$t_2,$at
+	$ADDU	$c_2,$t_2
+	sltu	$at,$c_2,$t_2
+	$ADDU	$c_3,$at
+	mflo	$t_1
+	mfhi	$t_2
+	$ADDU	$c_1,$t_1
+	sltu	$at,$c_1,$t_1
+	 $MULTU	$a_4,$b_0		# mul_add_c(a[4],b[0],c2,c3,c1);
+	$ADDU	$t_2,$at
+	$ADDU	$c_2,$t_2
+	sltu	$at,$c_2,$t_2
+	$ADDU	$c_3,$at
+	$ST	$c_1,3*$BNSZ($a0)	# r[3]=c1;
+
+	mflo	$t_1
+	mfhi	$t_2
+	$ADDU	$c_2,$t_1
+	sltu	$at,$c_2,$t_1
+	$MULTU	$a_3,$b_1		# mul_add_c(a[3],b[1],c2,c3,c1);
+	$ADDU	$t_2,$at
+	$ADDU	$c_3,$t_2
+	sltu	$c_1,$c_3,$t_2
+	mflo	$t_1
+	mfhi	$t_2
+	$ADDU	$c_2,$t_1
+	sltu	$at,$c_2,$t_1
+	$MULTU	$a_2,$b_2		# mul_add_c(a[2],b[2],c2,c3,c1);
+	$ADDU	$t_2,$at
+	$ADDU	$c_3,$t_2
+	sltu	$at,$c_3,$t_2
+	$ADDU	$c_1,$at
+	mflo	$t_1
+	mfhi	$t_2
+	$ADDU	$c_2,$t_1
+	sltu	$at,$c_2,$t_1
+	$MULTU	$a_1,$b_3		# mul_add_c(a[1],b[3],c2,c3,c1);
+	$ADDU	$t_2,$at
+	$ADDU	$c_3,$t_2
+	sltu	$at,$c_3,$t_2
+	$ADDU	$c_1,$at
+	mflo	$t_1
+	mfhi	$t_2
+	$ADDU	$c_2,$t_1
+	sltu	$at,$c_2,$t_1
+	$MULTU	$a_0,$b_4		# mul_add_c(a[0],b[4],c2,c3,c1);
+	$ADDU	$t_2,$at
+	$ADDU	$c_3,$t_2
+	sltu	$at,$c_3,$t_2
+	$ADDU	$c_1,$at
+	mflo	$t_1
+	mfhi	$t_2
+	$ADDU	$c_2,$t_1
+	sltu	$at,$c_2,$t_1
+	 $MULTU	$a_0,$b_5		# mul_add_c(a[0],b[5],c3,c1,c2);
+	$ADDU	$t_2,$at
+	$ADDU	$c_3,$t_2
+	sltu	$at,$c_3,$t_2
+	$ADDU	$c_1,$at
+	$ST	$c_2,4*$BNSZ($a0)	# r[4]=c2;
+
+	mflo	$t_1
+	mfhi	$t_2
+	$ADDU	$c_3,$t_1
+	sltu	$at,$c_3,$t_1
+	$MULTU	$a_1,$b_4		# mul_add_c(a[1],b[4],c3,c1,c2);
+	$ADDU	$t_2,$at
+	$ADDU	$c_1,$t_2
+	sltu	$c_2,$c_1,$t_2
+	mflo	$t_1
+	mfhi	$t_2
+	$ADDU	$c_3,$t_1
+	sltu	$at,$c_3,$t_1
+	$MULTU	$a_2,$b_3		# mul_add_c(a[2],b[3],c3,c1,c2);
+	$ADDU	$t_2,$at
+	$ADDU	$c_1,$t_2
+	sltu	$at,$c_1,$t_2
+	$ADDU	$c_2,$at
+	mflo	$t_1
+	mfhi	$t_2
+	$ADDU	$c_3,$t_1
+	sltu	$at,$c_3,$t_1
+	$MULTU	$a_3,$b_2		# mul_add_c(a[3],b[2],c3,c1,c2);
+	$ADDU	$t_2,$at
+	$ADDU	$c_1,$t_2
+	sltu	$at,$c_1,$t_2
+	$ADDU	$c_2,$at
+	mflo	$t_1
+	mfhi	$t_2
+	$ADDU	$c_3,$t_1
+	sltu	$at,$c_3,$t_1
+	$MULTU	$a_4,$b_1		# mul_add_c(a[4],b[1],c3,c1,c2);
+	$ADDU	$t_2,$at
+	$ADDU	$c_1,$t_2
+	sltu	$at,$c_1,$t_2
+	$ADDU	$c_2,$at
+	mflo	$t_1
+	mfhi	$t_2
+	$ADDU	$c_3,$t_1
+	sltu	$at,$c_3,$t_1
+	$MULTU	$a_5,$b_0		# mul_add_c(a[5],b[0],c3,c1,c2);
+	$ADDU	$t_2,$at
+	$ADDU	$c_1,$t_2
+	sltu	$at,$c_1,$t_2
+	$ADDU	$c_2,$at
+	mflo	$t_1
+	mfhi	$t_2
+	$ADDU	$c_3,$t_1
+	sltu	$at,$c_3,$t_1
+	 $MULTU	$a_6,$b_0		# mul_add_c(a[6],b[0],c1,c2,c3);
+	$ADDU	$t_2,$at
+	$ADDU	$c_1,$t_2
+	sltu	$at,$c_1,$t_2
+	$ADDU	$c_2,$at
+	$ST	$c_3,5*$BNSZ($a0)	# r[5]=c3;
+
+	mflo	$t_1
+	mfhi	$t_2
+	$ADDU	$c_1,$t_1
+	sltu	$at,$c_1,$t_1
+	$MULTU	$a_5,$b_1		# mul_add_c(a[5],b[1],c1,c2,c3);
+	$ADDU	$t_2,$at
+	$ADDU	$c_2,$t_2
+	sltu	$c_3,$c_2,$t_2
+	mflo	$t_1
+	mfhi	$t_2
+	$ADDU	$c_1,$t_1
+	sltu	$at,$c_1,$t_1
+	$MULTU	$a_4,$b_2		# mul_add_c(a[4],b[2],c1,c2,c3);
+	$ADDU	$t_2,$at
+	$ADDU	$c_2,$t_2
+	sltu	$at,$c_2,$t_2
+	$ADDU	$c_3,$at
+	mflo	$t_1
+	mfhi	$t_2
+	$ADDU	$c_1,$t_1
+	sltu	$at,$c_1,$t_1
+	$MULTU	$a_3,$b_3		# mul_add_c(a[3],b[3],c1,c2,c3);
+	$ADDU	$t_2,$at
+	$ADDU	$c_2,$t_2
+	sltu	$at,$c_2,$t_2
+	$ADDU	$c_3,$at
+	mflo	$t_1
+	mfhi	$t_2
+	$ADDU	$c_1,$t_1
+	sltu	$at,$c_1,$t_1
+	$MULTU	$a_2,$b_4		# mul_add_c(a[2],b[4],c1,c2,c3);
+	$ADDU	$t_2,$at
+	$ADDU	$c_2,$t_2
+	sltu	$at,$c_2,$t_2
+	$ADDU	$c_3,$at
+	mflo	$t_1
+	mfhi	$t_2
+	$ADDU	$c_1,$t_1
+	sltu	$at,$c_1,$t_1
+	$MULTU	$a_1,$b_5		# mul_add_c(a[1],b[5],c1,c2,c3);
+	$ADDU	$t_2,$at
+	$ADDU	$c_2,$t_2
+	sltu	$at,$c_2,$t_2
+	$ADDU	$c_3,$at
+	mflo	$t_1
+	mfhi	$t_2
+	$ADDU	$c_1,$t_1
+	sltu	$at,$c_1,$t_1
+	$MULTU	$a_0,$b_6		# mul_add_c(a[0],b[6],c1,c2,c3);
+	$ADDU	$t_2,$at
+	$ADDU	$c_2,$t_2
+	sltu	$at,$c_2,$t_2
+	$ADDU	$c_3,$at
+	mflo	$t_1
+	mfhi	$t_2
+	$ADDU	$c_1,$t_1
+	sltu	$at,$c_1,$t_1
+	 $MULTU	$a_0,$b_7		# mul_add_c(a[0],b[7],c2,c3,c1);
+	$ADDU	$t_2,$at
+	$ADDU	$c_2,$t_2
+	sltu	$at,$c_2,$t_2
+	$ADDU	$c_3,$at
+	$ST	$c_1,6*$BNSZ($a0)	# r[6]=c1;
+
+	mflo	$t_1
+	mfhi	$t_2
+	$ADDU	$c_2,$t_1
+	sltu	$at,$c_2,$t_1
+	$MULTU	$a_1,$b_6		# mul_add_c(a[1],b[6],c2,c3,c1);
+	$ADDU	$t_2,$at
+	$ADDU	$c_3,$t_2
+	sltu	$c_1,$c_3,$t_2
+	mflo	$t_1
+	mfhi	$t_2
+	$ADDU	$c_2,$t_1
+	sltu	$at,$c_2,$t_1
+	$MULTU	$a_2,$b_5		# mul_add_c(a[2],b[5],c2,c3,c1);
+	$ADDU	$t_2,$at
+	$ADDU	$c_3,$t_2
+	sltu	$at,$c_3,$t_2
+	$ADDU	$c_1,$at
+	mflo	$t_1
+	mfhi	$t_2
+	$ADDU	$c_2,$t_1
+	sltu	$at,$c_2,$t_1
+	$MULTU	$a_3,$b_4		# mul_add_c(a[3],b[4],c2,c3,c1);
+	$ADDU	$t_2,$at
+	$ADDU	$c_3,$t_2
+	sltu	$at,$c_3,$t_2
+	$ADDU	$c_1,$at
+	mflo	$t_1
+	mfhi	$t_2
+	$ADDU	$c_2,$t_1
+	sltu	$at,$c_2,$t_1
+	$MULTU	$a_4,$b_3		# mul_add_c(a[4],b[3],c2,c3,c1);
+	$ADDU	$t_2,$at
+	$ADDU	$c_3,$t_2
+	sltu	$at,$c_3,$t_2
+	$ADDU	$c_1,$at
+	mflo	$t_1
+	mfhi	$t_2
+	$ADDU	$c_2,$t_1
+	sltu	$at,$c_2,$t_1
+	$MULTU	$a_5,$b_2		# mul_add_c(a[5],b[2],c2,c3,c1);
+	$ADDU	$t_2,$at
+	$ADDU	$c_3,$t_2
+	sltu	$at,$c_3,$t_2
+	$ADDU	$c_1,$at
+	mflo	$t_1
+	mfhi	$t_2
+	$ADDU	$c_2,$t_1
+	sltu	$at,$c_2,$t_1
+	$MULTU	$a_6,$b_1		# mul_add_c(a[6],b[1],c2,c3,c1);
+	$ADDU	$t_2,$at
+	$ADDU	$c_3,$t_2
+	sltu	$at,$c_3,$t_2
+	$ADDU	$c_1,$at
+	mflo	$t_1
+	mfhi	$t_2
+	$ADDU	$c_2,$t_1
+	sltu	$at,$c_2,$t_1
+	$MULTU	$a_7,$b_0		# mul_add_c(a[7],b[0],c2,c3,c1);
+	$ADDU	$t_2,$at
+	$ADDU	$c_3,$t_2
+	sltu	$at,$c_3,$t_2
+	$ADDU	$c_1,$at
+	mflo	$t_1
+	mfhi	$t_2
+	$ADDU	$c_2,$t_1
+	sltu	$at,$c_2,$t_1
+	 $MULTU	$a_7,$b_1		# mul_add_c(a[7],b[1],c3,c1,c2);
+	$ADDU	$t_2,$at
+	$ADDU	$c_3,$t_2
+	sltu	$at,$c_3,$t_2
+	$ADDU	$c_1,$at
+	$ST	$c_2,7*$BNSZ($a0)	# r[7]=c2;
+
+	mflo	$t_1
+	mfhi	$t_2
+	$ADDU	$c_3,$t_1
+	sltu	$at,$c_3,$t_1
+	$MULTU	$a_6,$b_2		# mul_add_c(a[6],b[2],c3,c1,c2);
+	$ADDU	$t_2,$at
+	$ADDU	$c_1,$t_2
+	sltu	$c_2,$c_1,$t_2
+	mflo	$t_1
+	mfhi	$t_2
+	$ADDU	$c_3,$t_1
+	sltu	$at,$c_3,$t_1
+	$MULTU	$a_5,$b_3		# mul_add_c(a[5],b[3],c3,c1,c2);
+	$ADDU	$t_2,$at
+	$ADDU	$c_1,$t_2
+	sltu	$at,$c_1,$t_2
+	$ADDU	$c_2,$at
+	mflo	$t_1
+	mfhi	$t_2
+	$ADDU	$c_3,$t_1
+	sltu	$at,$c_3,$t_1
+	$MULTU	$a_4,$b_4		# mul_add_c(a[4],b[4],c3,c1,c2);
+	$ADDU	$t_2,$at
+	$ADDU	$c_1,$t_2
+	sltu	$at,$c_1,$t_2
+	$ADDU	$c_2,$at
+	mflo	$t_1
+	mfhi	$t_2
+	$ADDU	$c_3,$t_1
+	sltu	$at,$c_3,$t_1
+	$MULTU	$a_3,$b_5		# mul_add_c(a[3],b[5],c3,c1,c2);
+	$ADDU	$t_2,$at
+	$ADDU	$c_1,$t_2
+	sltu	$at,$c_1,$t_2
+	$ADDU	$c_2,$at
+	mflo	$t_1
+	mfhi	$t_2
+	$ADDU	$c_3,$t_1
+	sltu	$at,$c_3,$t_1
+	$MULTU	$a_2,$b_6		# mul_add_c(a[2],b[6],c3,c1,c2);
+	$ADDU	$t_2,$at
+	$ADDU	$c_1,$t_2
+	sltu	$at,$c_1,$t_2
+	$ADDU	$c_2,$at
+	mflo	$t_1
+	mfhi	$t_2
+	$ADDU	$c_3,$t_1
+	sltu	$at,$c_3,$t_1
+	$MULTU	$a_1,$b_7		# mul_add_c(a[1],b[7],c3,c1,c2);
+	$ADDU	$t_2,$at
+	$ADDU	$c_1,$t_2
+	sltu	$at,$c_1,$t_2
+	$ADDU	$c_2,$at
+	mflo	$t_1
+	mfhi	$t_2
+	$ADDU	$c_3,$t_1
+	sltu	$at,$c_3,$t_1
+	 $MULTU	$a_2,$b_7		# mul_add_c(a[2],b[7],c1,c2,c3);
+	$ADDU	$t_2,$at
+	$ADDU	$c_1,$t_2
+	sltu	$at,$c_1,$t_2
+	$ADDU	$c_2,$at
+	$ST	$c_3,8*$BNSZ($a0)	# r[8]=c3;
+
+	mflo	$t_1
+	mfhi	$t_2
+	$ADDU	$c_1,$t_1
+	sltu	$at,$c_1,$t_1
+	$MULTU	$a_3,$b_6		# mul_add_c(a[3],b[6],c1,c2,c3);
+	$ADDU	$t_2,$at
+	$ADDU	$c_2,$t_2
+	sltu	$c_3,$c_2,$t_2
+	mflo	$t_1
+	mfhi	$t_2
+	$ADDU	$c_1,$t_1
+	sltu	$at,$c_1,$t_1
+	$MULTU	$a_4,$b_5		# mul_add_c(a[4],b[5],c1,c2,c3);
+	$ADDU	$t_2,$at
+	$ADDU	$c_2,$t_2
+	sltu	$at,$c_2,$t_2
+	$ADDU	$c_3,$at
+	mflo	$t_1
+	mfhi	$t_2
+	$ADDU	$c_1,$t_1
+	sltu	$at,$c_1,$t_1
+	$MULTU	$a_5,$b_4		# mul_add_c(a[5],b[4],c1,c2,c3);
+	$ADDU	$t_2,$at
+	$ADDU	$c_2,$t_2
+	sltu	$at,$c_2,$t_2
+	$ADDU	$c_3,$at
+	mflo	$t_1
+	mfhi	$t_2
+	$ADDU	$c_1,$t_1
+	sltu	$at,$c_1,$t_1
+	$MULTU	$a_6,$b_3		# mul_add_c(a[6],b[3],c1,c2,c3);
+	$ADDU	$t_2,$at
+	$ADDU	$c_2,$t_2
+	sltu	$at,$c_2,$t_2
+	$ADDU	$c_3,$at
+	mflo	$t_1
+	mfhi	$t_2
+	$ADDU	$c_1,$t_1
+	sltu	$at,$c_1,$t_1
+	$MULTU	$a_7,$b_2		# mul_add_c(a[7],b[2],c1,c2,c3);
+	$ADDU	$t_2,$at
+	$ADDU	$c_2,$t_2
+	sltu	$at,$c_2,$t_2
+	$ADDU	$c_3,$at
+	mflo	$t_1
+	mfhi	$t_2
+	$ADDU	$c_1,$t_1
+	sltu	$at,$c_1,$t_1
+	 $MULTU	$a_7,$b_3		# mul_add_c(a[7],b[3],c2,c3,c1);
+	$ADDU	$t_2,$at
+	$ADDU	$c_2,$t_2
+	sltu	$at,$c_2,$t_2
+	$ADDU	$c_3,$at
+	$ST	$c_1,9*$BNSZ($a0)	# r[9]=c1;
+
+	mflo	$t_1
+	mfhi	$t_2
+	$ADDU	$c_2,$t_1
+	sltu	$at,$c_2,$t_1
+	$MULTU	$a_6,$b_4		# mul_add_c(a[6],b[4],c2,c3,c1);
+	$ADDU	$t_2,$at
+	$ADDU	$c_3,$t_2
+	sltu	$c_1,$c_3,$t_2
+	mflo	$t_1
+	mfhi	$t_2
+	$ADDU	$c_2,$t_1
+	sltu	$at,$c_2,$t_1
+	$MULTU	$a_5,$b_5		# mul_add_c(a[5],b[5],c2,c3,c1);
+	$ADDU	$t_2,$at
+	$ADDU	$c_3,$t_2
+	sltu	$at,$c_3,$t_2
+	$ADDU	$c_1,$at
+	mflo	$t_1
+	mfhi	$t_2
+	$ADDU	$c_2,$t_1
+	sltu	$at,$c_2,$t_1
+	$MULTU	$a_4,$b_6		# mul_add_c(a[4],b[6],c2,c3,c1);
+	$ADDU	$t_2,$at
+	$ADDU	$c_3,$t_2
+	sltu	$at,$c_3,$t_2
+	$ADDU	$c_1,$at
+	mflo	$t_1
+	mfhi	$t_2
+	$ADDU	$c_2,$t_1
+	sltu	$at,$c_2,$t_1
+	$MULTU	$a_3,$b_7		# mul_add_c(a[3],b[7],c2,c3,c1);
+	$ADDU	$t_2,$at
+	$ADDU	$c_3,$t_2
+	sltu	$at,$c_3,$t_2
+	$ADDU	$c_1,$at
+	mflo	$t_1
+	mfhi	$t_2
+	$ADDU	$c_2,$t_1
+	sltu	$at,$c_2,$t_1
+	$MULTU	$a_4,$b_7		# mul_add_c(a[4],b[7],c3,c1,c2);
+	$ADDU	$t_2,$at
+	$ADDU	$c_3,$t_2
+	sltu	$at,$c_3,$t_2
+	$ADDU	$c_1,$at
+	$ST	$c_2,10*$BNSZ($a0)	# r[10]=c2;
+
+	mflo	$t_1
+	mfhi	$t_2
+	$ADDU	$c_3,$t_1
+	sltu	$at,$c_3,$t_1
+	$MULTU	$a_5,$b_6		# mul_add_c(a[5],b[6],c3,c1,c2);
+	$ADDU	$t_2,$at
+	$ADDU	$c_1,$t_2
+	sltu	$c_2,$c_1,$t_2
+	mflo	$t_1
+	mfhi	$t_2
+	$ADDU	$c_3,$t_1
+	sltu	$at,$c_3,$t_1
+	$MULTU	$a_6,$b_5		# mul_add_c(a[6],b[5],c3,c1,c2);
+	$ADDU	$t_2,$at
+	$ADDU	$c_1,$t_2
+	sltu	$at,$c_1,$t_2
+	$ADDU	$c_2,$at
+	mflo	$t_1
+	mfhi	$t_2
+	$ADDU	$c_3,$t_1
+	sltu	$at,$c_3,$t_1
+	$MULTU	$a_7,$b_4		# mul_add_c(a[7],b[4],c3,c1,c2);
+	$ADDU	$t_2,$at
+	$ADDU	$c_1,$t_2
+	sltu	$at,$c_1,$t_2
+	$ADDU	$c_2,$at
+	mflo	$t_1
+	mfhi	$t_2
+	$ADDU	$c_3,$t_1
+	sltu	$at,$c_3,$t_1
+	 $MULTU	$a_7,$b_5		# mul_add_c(a[7],b[5],c1,c2,c3);
+	$ADDU	$t_2,$at
+	$ADDU	$c_1,$t_2
+	sltu	$at,$c_1,$t_2
+	$ADDU	$c_2,$at
+	$ST	$c_3,11*$BNSZ($a0)	# r[11]=c3;
+
+	mflo	$t_1
+	mfhi	$t_2
+	$ADDU	$c_1,$t_1
+	sltu	$at,$c_1,$t_1
+	$MULTU	$a_6,$b_6		# mul_add_c(a[6],b[6],c1,c2,c3);
+	$ADDU	$t_2,$at
+	$ADDU	$c_2,$t_2
+	sltu	$c_3,$c_2,$t_2
+	mflo	$t_1
+	mfhi	$t_2
+	$ADDU	$c_1,$t_1
+	sltu	$at,$c_1,$t_1
+	$MULTU	$a_5,$b_7		# mul_add_c(a[5],b[7],c1,c2,c3);
+	$ADDU	$t_2,$at
+	$ADDU	$c_2,$t_2
+	sltu	$at,$c_2,$t_2
+	$ADDU	$c_3,$at
+	mflo	$t_1
+	mfhi	$t_2
+	$ADDU	$c_1,$t_1
+	sltu	$at,$c_1,$t_1
+	 $MULTU	$a_6,$b_7		# mul_add_c(a[6],b[7],c2,c3,c1);
+	$ADDU	$t_2,$at
+	$ADDU	$c_2,$t_2
+	sltu	$at,$c_2,$t_2
+	$ADDU	$c_3,$at
+	$ST	$c_1,12*$BNSZ($a0)	# r[12]=c1;
+
+	mflo	$t_1
+	mfhi	$t_2
+	$ADDU	$c_2,$t_1
+	sltu	$at,$c_2,$t_1
+	$MULTU	$a_7,$b_6		# mul_add_c(a[7],b[6],c2,c3,c1);
+	$ADDU	$t_2,$at
+	$ADDU	$c_3,$t_2
+	sltu	$c_1,$c_3,$t_2
+	mflo	$t_1
+	mfhi	$t_2
+	$ADDU	$c_2,$t_1
+	sltu	$at,$c_2,$t_1
+	$MULTU	$a_7,$b_7		# mul_add_c(a[7],b[7],c3,c1,c2);
+	$ADDU	$t_2,$at
+	$ADDU	$c_3,$t_2
+	sltu	$at,$c_3,$t_2
+	$ADDU	$c_1,$at
+	$ST	$c_2,13*$BNSZ($a0)	# r[13]=c2;
+
+	mflo	$t_1
+	mfhi	$t_2
+	$ADDU	$c_3,$t_1
+	sltu	$at,$c_3,$t_1
+	$ADDU	$t_2,$at
+	$ADDU	$c_1,$t_2
+	$ST	$c_3,14*$BNSZ($a0)	# r[14]=c3;
+	$ST	$c_1,15*$BNSZ($a0)	# r[15]=c1;
+
+	.set	noreorder
+___
+$code.=<<___ if ($flavour =~ /nubi/i);
+	$REG_L	$s5,10*$SZREG($sp)
+	$REG_L	$s4,9*$SZREG($sp)
+	$REG_L	$s3,8*$SZREG($sp)
+	$REG_L	$s2,7*$SZREG($sp)
+	$REG_L	$s1,6*$SZREG($sp)
+	$REG_L	$s0,5*$SZREG($sp)
+	$REG_L	$t3,4*$SZREG($sp)
+	$REG_L	$t2,3*$SZREG($sp)
+	$REG_L	$t1,2*$SZREG($sp)
+	$REG_L	$t0,1*$SZREG($sp)
+	$REG_L	$gp,0*$SZREG($sp)
+	jr	$ra
+	$PTR_ADD $sp,12*$SZREG
+___
+$code.=<<___ if ($flavour !~ /nubi/i);
+	$REG_L	$s5,5*$SZREG($sp)
+	$REG_L	$s4,4*$SZREG($sp)
+	$REG_L	$s3,3*$SZREG($sp)
+	$REG_L	$s2,2*$SZREG($sp)
+	$REG_L	$s1,1*$SZREG($sp)
+	$REG_L	$s0,0*$SZREG($sp)
+	jr	$ra
+	$PTR_ADD $sp,6*$SZREG
+___
+$code.=<<___;
+.end	bn_mul_comba8
+
+.align	5
+.globl	bn_mul_comba4
+.ent	bn_mul_comba4
+bn_mul_comba4:
+___
+$code.=<<___ if ($flavour =~ /nubi/i);
+	.frame	$sp,6*$SZREG,$ra
+	.mask	0x8000f008,-$SZREG
+	.set	noreorder
+	$PTR_SUB $sp,6*$SZREG
+	$REG_S	$ra,5*$SZREG($sp)
+	$REG_S	$t3,4*$SZREG($sp)
+	$REG_S	$t2,3*$SZREG($sp)
+	$REG_S	$t1,2*$SZREG($sp)
+	$REG_S	$t0,1*$SZREG($sp)
+	$REG_S	$gp,0*$SZREG($sp)
+___
+$code.=<<___;
+	.set	reorder
+	$LD	$a_0,0($a1)
+	$LD	$b_0,0($a2)
+	$LD	$a_1,$BNSZ($a1)
+	$LD	$a_2,2*$BNSZ($a1)
+	$MULTU	$a_0,$b_0		# mul_add_c(a[0],b[0],c1,c2,c3);
+	$LD	$a_3,3*$BNSZ($a1)
+	$LD	$b_1,$BNSZ($a2)
+	$LD	$b_2,2*$BNSZ($a2)
+	$LD	$b_3,3*$BNSZ($a2)
+	mflo	$c_1
+	mfhi	$c_2
+	$ST	$c_1,0($a0)
+
+	$MULTU	$a_0,$b_1		# mul_add_c(a[0],b[1],c2,c3,c1);
+	mflo	$t_1
+	mfhi	$t_2
+	$ADDU	$c_2,$t_1
+	sltu	$at,$c_2,$t_1
+	$MULTU	$a_1,$b_0		# mul_add_c(a[1],b[0],c2,c3,c1);
+	$ADDU	$c_3,$t_2,$at
+	mflo	$t_1
+	mfhi	$t_2
+	$ADDU	$c_2,$t_1
+	sltu	$at,$c_2,$t_1
+	 $MULTU	$a_2,$b_0		# mul_add_c(a[2],b[0],c3,c1,c2);
+	$ADDU	$t_2,$at
+	$ADDU	$c_3,$t_2
+	sltu	$c_1,$c_3,$t_2
+	$ST	$c_2,$BNSZ($a0)
+
+	mflo	$t_1
+	mfhi	$t_2
+	$ADDU	$c_3,$t_1
+	sltu	$at,$c_3,$t_1
+	$MULTU	$a_1,$b_1		# mul_add_c(a[1],b[1],c3,c1,c2);
+	$ADDU	$t_2,$at
+	$ADDU	$c_1,$t_2
+	mflo	$t_1
+	mfhi	$t_2
+	$ADDU	$c_3,$t_1
+	sltu	$at,$c_3,$t_1
+	$MULTU	$a_0,$b_2		# mul_add_c(a[0],b[2],c3,c1,c2);
+	$ADDU	$t_2,$at
+	$ADDU	$c_1,$t_2
+	sltu	$c_2,$c_1,$t_2
+	mflo	$t_1
+	mfhi	$t_2
+	$ADDU	$c_3,$t_1
+	sltu	$at,$c_3,$t_1
+	 $MULTU	$a_0,$b_3		# mul_add_c(a[0],b[3],c1,c2,c3);
+	$ADDU	$t_2,$at
+	$ADDU	$c_1,$t_2
+	sltu	$at,$c_1,$t_2
+	$ADDU	$c_2,$at
+	$ST	$c_3,2*$BNSZ($a0)
+
+	mflo	$t_1
+	mfhi	$t_2
+	$ADDU	$c_1,$t_1
+	sltu	$at,$c_1,$t_1
+	$MULTU	$a_1,$b_2		# mul_add_c(a[1],b[2],c1,c2,c3);
+	$ADDU	$t_2,$at
+	$ADDU	$c_2,$t_2
+	sltu	$c_3,$c_2,$t_2
+	mflo	$t_1
+	mfhi	$t_2
+	$ADDU	$c_1,$t_1
+	sltu	$at,$c_1,$t_1
+	$MULTU	$a_2,$b_1		# mul_add_c(a[2],b[1],c1,c2,c3);
+	$ADDU	$t_2,$at
+	$ADDU	$c_2,$t_2
+	sltu	$at,$c_2,$t_2
+	$ADDU	$c_3,$at
+	mflo	$t_1
+	mfhi	$t_2
+	$ADDU	$c_1,$t_1
+	sltu	$at,$c_1,$t_1
+	$MULTU	$a_3,$b_0		# mul_add_c(a[3],b[0],c1,c2,c3);
+	$ADDU	$t_2,$at
+	$ADDU	$c_2,$t_2
+	sltu	$at,$c_2,$t_2
+	$ADDU	$c_3,$at
+	mflo	$t_1
+	mfhi	$t_2
+	$ADDU	$c_1,$t_1
+	sltu	$at,$c_1,$t_1
+	 $MULTU	$a_3,$b_1		# mul_add_c(a[3],b[1],c2,c3,c1);
+	$ADDU	$t_2,$at
+	$ADDU	$c_2,$t_2
+	sltu	$at,$c_2,$t_2
+	$ADDU	$c_3,$at
+	$ST	$c_1,3*$BNSZ($a0)
+
+	mflo	$t_1
+	mfhi	$t_2
+	$ADDU	$c_2,$t_1
+	sltu	$at,$c_2,$t_1
+	$MULTU	$a_2,$b_2		# mul_add_c(a[2],b[2],c2,c3,c1);
+	$ADDU	$t_2,$at
+	$ADDU	$c_3,$t_2
+	sltu	$c_1,$c_3,$t_2
+	mflo	$t_1
+	mfhi	$t_2
+	$ADDU	$c_2,$t_1
+	sltu	$at,$c_2,$t_1
+	$MULTU	$a_1,$b_3		# mul_add_c(a[1],b[3],c2,c3,c1);
+	$ADDU	$t_2,$at
+	$ADDU	$c_3,$t_2
+	sltu	$at,$c_3,$t_2
+	$ADDU	$c_1,$at
+	mflo	$t_1
+	mfhi	$t_2
+	$ADDU	$c_2,$t_1
+	sltu	$at,$c_2,$t_1
+	 $MULTU	$a_2,$b_3		# mul_add_c(a[2],b[3],c3,c1,c2);
+	$ADDU	$t_2,$at
+	$ADDU	$c_3,$t_2
+	sltu	$at,$c_3,$t_2
+	$ADDU	$c_1,$at
+	$ST	$c_2,4*$BNSZ($a0)
+
+	mflo	$t_1
+	mfhi	$t_2
+	$ADDU	$c_3,$t_1
+	sltu	$at,$c_3,$t_1
+	$MULTU	$a_3,$b_2		# mul_add_c(a[3],b[2],c3,c1,c2);
+	$ADDU	$t_2,$at
+	$ADDU	$c_1,$t_2
+	sltu	$c_2,$c_1,$t_2
+	mflo	$t_1
+	mfhi	$t_2
+	$ADDU	$c_3,$t_1
+	sltu	$at,$c_3,$t_1
+	 $MULTU	$a_3,$b_3		# mul_add_c(a[3],b[3],c1,c2,c3);
+	$ADDU	$t_2,$at
+	$ADDU	$c_1,$t_2
+	sltu	$at,$c_1,$t_2
+	$ADDU	$c_2,$at
+	$ST	$c_3,5*$BNSZ($a0)
+
+	mflo	$t_1
+	mfhi	$t_2
+	$ADDU	$c_1,$t_1
+	sltu	$at,$c_1,$t_1
+	$ADDU	$t_2,$at
+	$ADDU	$c_2,$t_2
+	$ST	$c_1,6*$BNSZ($a0)
+	$ST	$c_2,7*$BNSZ($a0)
+
+	.set	noreorder
+___
+$code.=<<___ if ($flavour =~ /nubi/i);
+	$REG_L	$t3,4*$SZREG($sp)
+	$REG_L	$t2,3*$SZREG($sp)
+	$REG_L	$t1,2*$SZREG($sp)
+	$REG_L	$t0,1*$SZREG($sp)
+	$REG_L	$gp,0*$SZREG($sp)
+	$PTR_ADD $sp,6*$SZREG
+___
+$code.=<<___;
+	jr	$ra
+	nop
+.end	bn_mul_comba4
+___
+
+($a_4,$a_5,$a_6,$a_7)=($b_0,$b_1,$b_2,$b_3);
+
+sub add_c2 () {
+my ($hi,$lo,$c0,$c1,$c2,
+    $warm,      # !$warm denotes first call with specific sequence of
+                # $c_[XYZ] when there is no Z-carry to accumulate yet;
+    $an,$bn     # these two are arguments for multiplication which
+                # result is used in *next* step [which is why it's
+                # commented as "forward multiplication" below];
+    )=@_;
+$code.=<<___;
+	mflo	$lo
+	mfhi	$hi
+	$ADDU	$c0,$lo
+	sltu	$at,$c0,$lo
+	 $MULTU	$an,$bn			# forward multiplication
+	$ADDU	$c0,$lo
+	$ADDU	$at,$hi
+	sltu	$lo,$c0,$lo
+	$ADDU	$c1,$at
+	$ADDU	$hi,$lo
+___
+$code.=<<___	if (!$warm);
+	sltu	$c2,$c1,$at
+	$ADDU	$c1,$hi
+	sltu	$hi,$c1,$hi
+	$ADDU	$c2,$hi
+___
+$code.=<<___	if ($warm);
+	sltu	$at,$c1,$at
+	$ADDU	$c1,$hi
+	$ADDU	$c2,$at
+	sltu	$hi,$c1,$hi
+	$ADDU	$c2,$hi
+___
+}
+
+$code.=<<___;
+
+.align	5
+.globl	bn_sqr_comba8
+.ent	bn_sqr_comba8
+bn_sqr_comba8:
+___
+$code.=<<___ if ($flavour =~ /nubi/i);
+	.frame	$sp,6*$SZREG,$ra
+	.mask	0x8000f008,-$SZREG
+	.set	noreorder
+	$PTR_SUB $sp,6*$SZREG
+	$REG_S	$ra,5*$SZREG($sp)
+	$REG_S	$t3,4*$SZREG($sp)
+	$REG_S	$t2,3*$SZREG($sp)
+	$REG_S	$t1,2*$SZREG($sp)
+	$REG_S	$t0,1*$SZREG($sp)
+	$REG_S	$gp,0*$SZREG($sp)
+___
+$code.=<<___;
+	.set	reorder
+	$LD	$a_0,0($a1)
+	$LD	$a_1,$BNSZ($a1)
+	$LD	$a_2,2*$BNSZ($a1)
+	$LD	$a_3,3*$BNSZ($a1)
+
+	$MULTU	$a_0,$a_0		# mul_add_c(a[0],b[0],c1,c2,c3);
+	$LD	$a_4,4*$BNSZ($a1)
+	$LD	$a_5,5*$BNSZ($a1)
+	$LD	$a_6,6*$BNSZ($a1)
+	$LD	$a_7,7*$BNSZ($a1)
+	mflo	$c_1
+	mfhi	$c_2
+	$ST	$c_1,0($a0)
+
+	$MULTU	$a_0,$a_1		# mul_add_c2(a[0],b[1],c2,c3,c1);
+	mflo	$t_1
+	mfhi	$t_2
+	slt	$c_1,$t_2,$zero
+	$SLL	$t_2,1
+	 $MULTU	$a_2,$a_0		# mul_add_c2(a[2],b[0],c3,c1,c2);
+	slt	$a2,$t_1,$zero
+	$ADDU	$t_2,$a2
+	$SLL	$t_1,1
+	$ADDU	$c_2,$t_1
+	sltu	$at,$c_2,$t_1
+	$ADDU	$c_3,$t_2,$at
+	$ST	$c_2,$BNSZ($a0)
+___
+	&add_c2($t_2,$t_1,$c_3,$c_1,$c_2,0,
+		$a_1,$a_1);		# mul_add_c(a[1],b[1],c3,c1,c2);
+$code.=<<___;
+	mflo	$t_1
+	mfhi	$t_2
+	$ADDU	$c_3,$t_1
+	sltu	$at,$c_3,$t_1
+	 $MULTU	$a_0,$a_3		# mul_add_c2(a[0],b[3],c1,c2,c3);
+	$ADDU	$t_2,$at
+	$ADDU	$c_1,$t_2
+	sltu	$at,$c_1,$t_2
+	$ADDU	$c_2,$at
+	$ST	$c_3,2*$BNSZ($a0)
+___
+	&add_c2($t_2,$t_1,$c_1,$c_2,$c_3,0,
+		$a_1,$a_2);		# mul_add_c2(a[1],b[2],c1,c2,c3);
+	&add_c2($t_2,$t_1,$c_1,$c_2,$c_3,1,
+		$a_4,$a_0);		# mul_add_c2(a[4],b[0],c2,c3,c1);
+$code.=<<___;
+	$ST	$c_1,3*$BNSZ($a0)
+___
+	&add_c2($t_2,$t_1,$c_2,$c_3,$c_1,0,
+		$a_3,$a_1);		# mul_add_c2(a[3],b[1],c2,c3,c1);
+	&add_c2($t_2,$t_1,$c_2,$c_3,$c_1,1,
+		$a_2,$a_2);		# mul_add_c(a[2],b[2],c2,c3,c1);
+$code.=<<___;
+	mflo	$t_1
+	mfhi	$t_2
+	$ADDU	$c_2,$t_1
+	sltu	$at,$c_2,$t_1
+	 $MULTU	$a_0,$a_5		# mul_add_c2(a[0],b[5],c3,c1,c2);
+	$ADDU	$t_2,$at
+	$ADDU	$c_3,$t_2
+	sltu	$at,$c_3,$t_2
+	$ADDU	$c_1,$at
+	$ST	$c_2,4*$BNSZ($a0)
+___
+	&add_c2($t_2,$t_1,$c_3,$c_1,$c_2,0,
+		$a_1,$a_4);		# mul_add_c2(a[1],b[4],c3,c1,c2);
+	&add_c2($t_2,$t_1,$c_3,$c_1,$c_2,1,
+		$a_2,$a_3);		# mul_add_c2(a[2],b[3],c3,c1,c2);
+	&add_c2($t_2,$t_1,$c_3,$c_1,$c_2,1,
+		$a_6,$a_0);		# mul_add_c2(a[6],b[0],c1,c2,c3);
+$code.=<<___;
+	$ST	$c_3,5*$BNSZ($a0)
+___
+	&add_c2($t_2,$t_1,$c_1,$c_2,$c_3,0,
+		$a_5,$a_1);		# mul_add_c2(a[5],b[1],c1,c2,c3);
+	&add_c2($t_2,$t_1,$c_1,$c_2,$c_3,1,
+		$a_4,$a_2);		# mul_add_c2(a[4],b[2],c1,c2,c3);
+	&add_c2($t_2,$t_1,$c_1,$c_2,$c_3,1,
+		$a_3,$a_3);		# mul_add_c(a[3],b[3],c1,c2,c3);
+$code.=<<___;
+	mflo	$t_1
+	mfhi	$t_2
+	$ADDU	$c_1,$t_1
+	sltu	$at,$c_1,$t_1
+	 $MULTU	$a_0,$a_7		# mul_add_c2(a[0],b[7],c2,c3,c1);
+	$ADDU	$t_2,$at
+	$ADDU	$c_2,$t_2
+	sltu	$at,$c_2,$t_2
+	$ADDU	$c_3,$at
+	$ST	$c_1,6*$BNSZ($a0)
+___
+	&add_c2($t_2,$t_1,$c_2,$c_3,$c_1,0,
+		$a_1,$a_6);		# mul_add_c2(a[1],b[6],c2,c3,c1);
+	&add_c2($t_2,$t_1,$c_2,$c_3,$c_1,1,
+		$a_2,$a_5);		# mul_add_c2(a[2],b[5],c2,c3,c1);
+	&add_c2($t_2,$t_1,$c_2,$c_3,$c_1,1,
+		$a_3,$a_4);		# mul_add_c2(a[3],b[4],c2,c3,c1);
+	&add_c2($t_2,$t_1,$c_2,$c_3,$c_1,1,
+		$a_7,$a_1);		# mul_add_c2(a[7],b[1],c3,c1,c2);
+$code.=<<___;
+	$ST	$c_2,7*$BNSZ($a0)
+___
+	&add_c2($t_2,$t_1,$c_3,$c_1,$c_2,0,
+		$a_6,$a_2);		# mul_add_c2(a[6],b[2],c3,c1,c2);
+	&add_c2($t_2,$t_1,$c_3,$c_1,$c_2,1,
+		$a_5,$a_3);		# mul_add_c2(a[5],b[3],c3,c1,c2);
+	&add_c2($t_2,$t_1,$c_3,$c_1,$c_2,1,
+		$a_4,$a_4);		# mul_add_c(a[4],b[4],c3,c1,c2);
+$code.=<<___;
+	mflo	$t_1
+	mfhi	$t_2
+	$ADDU	$c_3,$t_1
+	sltu	$at,$c_3,$t_1
+	 $MULTU	$a_2,$a_7		# mul_add_c2(a[2],b[7],c1,c2,c3);
+	$ADDU	$t_2,$at
+	$ADDU	$c_1,$t_2
+	sltu	$at,$c_1,$t_2
+	$ADDU	$c_2,$at
+	$ST	$c_3,8*$BNSZ($a0)
+___
+	&add_c2($t_2,$t_1,$c_1,$c_2,$c_3,0,
+		$a_3,$a_6);		# mul_add_c2(a[3],b[6],c1,c2,c3);
+	&add_c2($t_2,$t_1,$c_1,$c_2,$c_3,1,
+		$a_4,$a_5);		# mul_add_c2(a[4],b[5],c1,c2,c3);
+	&add_c2($t_2,$t_1,$c_1,$c_2,$c_3,1,
+		$a_7,$a_3);		# mul_add_c2(a[7],b[3],c2,c3,c1);
+$code.=<<___;
+	$ST	$c_1,9*$BNSZ($a0)
+___
+	&add_c2($t_2,$t_1,$c_2,$c_3,$c_1,0,
+		$a_6,$a_4);		# mul_add_c2(a[6],b[4],c2,c3,c1);
+	&add_c2($t_2,$t_1,$c_2,$c_3,$c_1,1,
+		$a_5,$a_5);		# mul_add_c(a[5],b[5],c2,c3,c1);
+$code.=<<___;
+	mflo	$t_1
+	mfhi	$t_2
+	$ADDU	$c_2,$t_1
+	sltu	$at,$c_2,$t_1
+	 $MULTU	$a_4,$a_7		# mul_add_c2(a[4],b[7],c3,c1,c2);
+	$ADDU	$t_2,$at
+	$ADDU	$c_3,$t_2
+	sltu	$at,$c_3,$t_2
+	$ADDU	$c_1,$at
+	$ST	$c_2,10*$BNSZ($a0)
+___
+	&add_c2($t_2,$t_1,$c_3,$c_1,$c_2,0,
+		$a_5,$a_6);		# mul_add_c2(a[5],b[6],c3,c1,c2);
+	&add_c2($t_2,$t_1,$c_3,$c_1,$c_2,1,
+		$a_7,$a_5);		# mul_add_c2(a[7],b[5],c1,c2,c3);
+$code.=<<___;
+	$ST	$c_3,11*$BNSZ($a0)
+___
+	&add_c2($t_2,$t_1,$c_1,$c_2,$c_3,0,
+		$a_6,$a_6);		# mul_add_c(a[6],b[6],c1,c2,c3);
+$code.=<<___;
+	mflo	$t_1
+	mfhi	$t_2
+	$ADDU	$c_1,$t_1
+	sltu	$at,$c_1,$t_1
+	 $MULTU	$a_6,$a_7		# mul_add_c2(a[6],b[7],c2,c3,c1);
+	$ADDU	$t_2,$at
+	$ADDU	$c_2,$t_2
+	sltu	$at,$c_2,$t_2
+	$ADDU	$c_3,$at
+	$ST	$c_1,12*$BNSZ($a0)
+___
+	&add_c2($t_2,$t_1,$c_2,$c_3,$c_1,0,
+		$a_7,$a_7);		# mul_add_c(a[7],b[7],c3,c1,c2);
+$code.=<<___;
+	$ST	$c_2,13*$BNSZ($a0)
+
+	mflo	$t_1
+	mfhi	$t_2
+	$ADDU	$c_3,$t_1
+	sltu	$at,$c_3,$t_1
+	$ADDU	$t_2,$at
+	$ADDU	$c_1,$t_2
+	$ST	$c_3,14*$BNSZ($a0)
+	$ST	$c_1,15*$BNSZ($a0)
+
+	.set	noreorder
+___
+$code.=<<___ if ($flavour =~ /nubi/i);
+	$REG_L	$t3,4*$SZREG($sp)
+	$REG_L	$t2,3*$SZREG($sp)
+	$REG_L	$t1,2*$SZREG($sp)
+	$REG_L	$t0,1*$SZREG($sp)
+	$REG_L	$gp,0*$SZREG($sp)
+	$PTR_ADD $sp,6*$SZREG
+___
+$code.=<<___;
+	jr	$ra
+	nop
+.end	bn_sqr_comba8
+
+.align	5
+.globl	bn_sqr_comba4
+.ent	bn_sqr_comba4
+bn_sqr_comba4:
+___
+$code.=<<___ if ($flavour =~ /nubi/i);
+	.frame	$sp,6*$SZREG,$ra
+	.mask	0x8000f008,-$SZREG
+	.set	noreorder
+	$PTR_SUB $sp,6*$SZREG
+	$REG_S	$ra,5*$SZREG($sp)
+	$REG_S	$t3,4*$SZREG($sp)
+	$REG_S	$t2,3*$SZREG($sp)
+	$REG_S	$t1,2*$SZREG($sp)
+	$REG_S	$t0,1*$SZREG($sp)
+	$REG_S	$gp,0*$SZREG($sp)
+___
+$code.=<<___;
+	.set	reorder
+	$LD	$a_0,0($a1)
+	$LD	$a_1,$BNSZ($a1)
+	$MULTU	$a_0,$a_0		# mul_add_c(a[0],b[0],c1,c2,c3);
+	$LD	$a_2,2*$BNSZ($a1)
+	$LD	$a_3,3*$BNSZ($a1)
+	mflo	$c_1
+	mfhi	$c_2
+	$ST	$c_1,0($a0)
+
+	$MULTU	$a_0,$a_1		# mul_add_c2(a[0],b[1],c2,c3,c1);
+	mflo	$t_1
+	mfhi	$t_2
+	slt	$c_1,$t_2,$zero
+	$SLL	$t_2,1
+	 $MULTU	$a_2,$a_0		# mul_add_c2(a[2],b[0],c3,c1,c2);
+	slt	$a2,$t_1,$zero
+	$ADDU	$t_2,$a2
+	$SLL	$t_1,1
+	$ADDU	$c_2,$t_1
+	sltu	$at,$c_2,$t_1
+	$ADDU	$c_3,$t_2,$at
+	$ST	$c_2,$BNSZ($a0)
+___
+	&add_c2($t_2,$t_1,$c_3,$c_1,$c_2,0,
+		$a_1,$a_1);		# mul_add_c(a[1],b[1],c3,c1,c2);
+$code.=<<___;
+	mflo	$t_1
+	mfhi	$t_2
+	$ADDU	$c_3,$t_1
+	sltu	$at,$c_3,$t_1
+	 $MULTU	$a_0,$a_3		# mul_add_c2(a[0],b[3],c1,c2,c3);
+	$ADDU	$t_2,$at
+	$ADDU	$c_1,$t_2
+	sltu	$at,$c_1,$t_2
+	$ADDU	$c_2,$at
+	$ST	$c_3,2*$BNSZ($a0)
+___
+	&add_c2($t_2,$t_1,$c_1,$c_2,$c_3,0,
+		$a_1,$a_2);		# mul_add_c2(a2[1],b[2],c1,c2,c3);
+	&add_c2($t_2,$t_1,$c_1,$c_2,$c_3,1,
+		$a_3,$a_1);		# mul_add_c2(a[3],b[1],c2,c3,c1);
+$code.=<<___;
+	$ST	$c_1,3*$BNSZ($a0)
+___
+	&add_c2($t_2,$t_1,$c_2,$c_3,$c_1,0,
+		$a_2,$a_2);		# mul_add_c(a[2],b[2],c2,c3,c1);
+$code.=<<___;
+	mflo	$t_1
+	mfhi	$t_2
+	$ADDU	$c_2,$t_1
+	sltu	$at,$c_2,$t_1
+	 $MULTU	$a_2,$a_3		# mul_add_c2(a[2],b[3],c3,c1,c2);
+	$ADDU	$t_2,$at
+	$ADDU	$c_3,$t_2
+	sltu	$at,$c_3,$t_2
+	$ADDU	$c_1,$at
+	$ST	$c_2,4*$BNSZ($a0)
+___
+	&add_c2($t_2,$t_1,$c_3,$c_1,$c_2,0,
+		$a_3,$a_3);		# mul_add_c(a[3],b[3],c1,c2,c3);
+$code.=<<___;
+	$ST	$c_3,5*$BNSZ($a0)
+
+	mflo	$t_1
+	mfhi	$t_2
+	$ADDU	$c_1,$t_1
+	sltu	$at,$c_1,$t_1
+	$ADDU	$t_2,$at
+	$ADDU	$c_2,$t_2
+	$ST	$c_1,6*$BNSZ($a0)
+	$ST	$c_2,7*$BNSZ($a0)
+
+	.set	noreorder
+___
+$code.=<<___ if ($flavour =~ /nubi/i);
+	$REG_L	$t3,4*$SZREG($sp)
+	$REG_L	$t2,3*$SZREG($sp)
+	$REG_L	$t1,2*$SZREG($sp)
+	$REG_L	$t0,1*$SZREG($sp)
+	$REG_L	$gp,0*$SZREG($sp)
+	$PTR_ADD $sp,6*$SZREG
+___
+$code.=<<___;
+	jr	$ra
+	nop
+.end	bn_sqr_comba4
+___
+print $code;
+close STDOUT;
diff --git a/crypto/bn/asm/parisc-mont.pl b/crypto/bn/asm/parisc-mont.pl
new file mode 100644
index 000000000..8aa94e851
--- /dev/null
+++ b/crypto/bn/asm/parisc-mont.pl
@@ -0,0 +1,1002 @@
+#! /usr/bin/env perl
+# Copyright 2009-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+
+# ====================================================================
+# Written by Andy Polyakov  for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+
+# On PA-7100LC this module performs ~90-50% better, less for longer
+# keys, than code generated by gcc 3.2 for PA-RISC 1.1. Latter means
+# that compiler utilized xmpyu instruction to perform 32x32=64-bit
+# multiplication, which in turn means that "baseline" performance was
+# optimal in respect to instruction set capabilities. Fair comparison
+# with vendor compiler is problematic, because OpenSSL doesn't define
+# BN_LLONG [presumably] for historical reasons, which drives compiler
+# toward 4 times 16x16=32-bit multiplicatons [plus complementary
+# shifts and additions] instead. This means that you should observe
+# several times improvement over code generated by vendor compiler
+# for PA-RISC 1.1, but the "baseline" is far from optimal. The actual
+# improvement coefficient was never collected on PA-7100LC, or any
+# other 1.1 CPU, because I don't have access to such machine with
+# vendor compiler. But to give you a taste, PA-RISC 1.1 code path
+# reportedly outperformed code generated by cc +DA1.1 +O3 by factor
+# of ~5x on PA-8600.
+#
+# On PA-RISC 2.0 it has to compete with pa-risc2[W].s, which is
+# reportedly ~2x faster than vendor compiler generated code [according
+# to comment in pa-risc2[W].s]. Here comes a catch. Execution core of
+# this implementation is actually 32-bit one, in the sense that it
+# operates on 32-bit values. But pa-risc2[W].s operates on arrays of
+# 64-bit BN_LONGs... How do they interoperate then? No problem. This
+# module picks halves of 64-bit values in reverse order and pretends
+# they were 32-bit BN_LONGs. But can 32-bit core compete with "pure"
+# 64-bit code such as pa-risc2[W].s then? Well, the thing is that
+# 32x32=64-bit multiplication is the best even PA-RISC 2.0 can do,
+# i.e. there is no "wider" multiplication like on most other 64-bit
+# platforms. This means that even being effectively 32-bit, this
+# implementation performs "64-bit" computational task in same amount
+# of arithmetic operations, most notably multiplications. It requires
+# more memory references, most notably to tp[num], but this doesn't
+# seem to exhaust memory port capacity. And indeed, dedicated PA-RISC
+# 2.0 code path provides virtually same performance as pa-risc2[W].s:
+# it's ~10% better for shortest key length and ~10% worse for longest
+# one.
+#
+# In case it wasn't clear. The module has two distinct code paths:
+# PA-RISC 1.1 and PA-RISC 2.0 ones. Latter features carry-free 64-bit
+# additions and 64-bit integer loads, not to mention specific
+# instruction scheduling. In 64-bit build naturally only 2.0 code path
+# is assembled. In 32-bit application context both code paths are
+# assembled, PA-RISC 2.0 CPU is detected at run-time and proper path
+# is taken automatically. Also, in 32-bit build the module imposes
+# couple of limitations: vector lengths has to be even and vector
+# addresses has to be 64-bit aligned. Normally neither is a problem:
+# most common key lengths are even and vectors are commonly malloc-ed,
+# which ensures alignment.
+#
+# Special thanks to polarhome.com for providing HP-UX account on
+# PA-RISC 1.1 machine, and to correspondent who chose to remain
+# anonymous for testing the code on PA-RISC 2.0 machine.
+
+$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+
+$flavour = shift;
+$output = shift;
+
+open STDOUT,">$output";
+
+if ($flavour =~ /64/) {
+	$LEVEL		="2.0W";
+	$SIZE_T		=8;
+	$FRAME_MARKER	=80;
+	$SAVED_RP	=16;
+	$PUSH		="std";
+	$PUSHMA		="std,ma";
+	$POP		="ldd";
+	$POPMB		="ldd,mb";
+	$BN_SZ		=$SIZE_T;
+} else {
+	$LEVEL		="1.1";	#$LEVEL.="\n\t.ALLOW\t2.0";
+	$SIZE_T		=4;
+	$FRAME_MARKER	=48;
+	$SAVED_RP	=20;
+	$PUSH		="stw";
+	$PUSHMA		="stwm";
+	$POP		="ldw";
+	$POPMB		="ldwm";
+	$BN_SZ		=$SIZE_T;
+	if (open CONF,"<${dir}../../opensslconf.h") {
+	    while() {
+		if (m/#\s*define\s+SIXTY_FOUR_BIT/) {
+		    $BN_SZ=8;
+		    $LEVEL="2.0";
+		    last;
+		}
+	    }
+	    close CONF;
+	}
+}
+
+$FRAME=8*$SIZE_T+$FRAME_MARKER;	# 8 saved regs + frame marker
+				#                [+ argument transfer]
+$LOCALS=$FRAME-$FRAME_MARKER;
+$FRAME+=32;			# local variables
+
+$tp="%r31";
+$ti1="%r29";
+$ti0="%r28";
+
+$rp="%r26";
+$ap="%r25";
+$bp="%r24";
+$np="%r23";
+$n0="%r22";	# passed through stack in 32-bit
+$num="%r21";	# passed through stack in 32-bit
+$idx="%r20";
+$arrsz="%r19";
+
+$nm1="%r7";
+$nm0="%r6";
+$ab1="%r5";
+$ab0="%r4";
+
+$fp="%r3";
+$hi1="%r2";
+$hi0="%r1";
+
+$xfer=$n0;	# accommodates [-16..15] offset in fld[dw]s
+
+$fm0="%fr4";	$fti=$fm0;
+$fbi="%fr5L";
+$fn0="%fr5R";
+$fai="%fr6";	$fab0="%fr7";	$fab1="%fr8";
+$fni="%fr9";	$fnm0="%fr10";	$fnm1="%fr11";
+
+$code=<<___;
+	.LEVEL	$LEVEL
+	.SPACE	\$TEXT\$
+	.SUBSPA	\$CODE\$,QUAD=0,ALIGN=8,ACCESS=0x2C,CODE_ONLY
+
+	.EXPORT	bn_mul_mont,ENTRY,ARGW0=GR,ARGW1=GR,ARGW2=GR,ARGW3=GR
+	.ALIGN	64
+bn_mul_mont
+	.PROC
+	.CALLINFO	FRAME=`$FRAME-8*$SIZE_T`,NO_CALLS,SAVE_RP,SAVE_SP,ENTRY_GR=6
+	.ENTRY
+	$PUSH	%r2,-$SAVED_RP(%sp)		; standard prologue
+	$PUSHMA	%r3,$FRAME(%sp)
+	$PUSH	%r4,`-$FRAME+1*$SIZE_T`(%sp)
+	$PUSH	%r5,`-$FRAME+2*$SIZE_T`(%sp)
+	$PUSH	%r6,`-$FRAME+3*$SIZE_T`(%sp)
+	$PUSH	%r7,`-$FRAME+4*$SIZE_T`(%sp)
+	$PUSH	%r8,`-$FRAME+5*$SIZE_T`(%sp)
+	$PUSH	%r9,`-$FRAME+6*$SIZE_T`(%sp)
+	$PUSH	%r10,`-$FRAME+7*$SIZE_T`(%sp)
+	ldo	-$FRAME(%sp),$fp
+___
+$code.=<<___ if ($SIZE_T==4);
+	ldw	`-$FRAME_MARKER-4`($fp),$n0
+	ldw	`-$FRAME_MARKER-8`($fp),$num
+	nop
+	nop					; alignment
+___
+$code.=<<___ if ($BN_SZ==4);
+	comiclr,<=	6,$num,%r0		; are vectors long enough?
+	b		L\$abort
+	ldi		0,%r28			; signal "unhandled"
+	add,ev		%r0,$num,$num		; is $num even?
+	b		L\$abort
+	nop
+	or		$ap,$np,$ti1
+	extru,=		$ti1,31,3,%r0		; are ap and np 64-bit aligned?
+	b		L\$abort
+	nop
+	nop					; alignment
+	nop
+
+	fldws		0($n0),${fn0}
+	fldws,ma	4($bp),${fbi}		; bp[0]
+___
+$code.=<<___ if ($BN_SZ==8);
+	comib,>		3,$num,L\$abort		; are vectors long enough?
+	ldi		0,%r28			; signal "unhandled"
+	addl		$num,$num,$num		; I operate on 32-bit values
+
+	fldws		4($n0),${fn0}		; only low part of n0
+	fldws		4($bp),${fbi}		; bp[0] in flipped word order
+___
+$code.=<<___;
+	fldds		0($ap),${fai}		; ap[0,1]
+	fldds		0($np),${fni}		; np[0,1]
+
+	sh2addl		$num,%r0,$arrsz
+	ldi		31,$hi0
+	ldo		36($arrsz),$hi1		; space for tp[num+1]
+	andcm		$hi1,$hi0,$hi1		; align
+	addl		$hi1,%sp,%sp
+	$PUSH		$fp,-$SIZE_T(%sp)
+
+	ldo		`$LOCALS+16`($fp),$xfer
+	ldo		`$LOCALS+32+4`($fp),$tp
+
+	xmpyu		${fai}L,${fbi},${fab0}	; ap[0]*bp[0]
+	xmpyu		${fai}R,${fbi},${fab1}	; ap[1]*bp[0]
+	xmpyu		${fn0},${fab0}R,${fm0}
+
+	addl		$arrsz,$ap,$ap		; point at the end
+	addl		$arrsz,$np,$np
+	subi		0,$arrsz,$idx		; j=0
+	ldo		8($idx),$idx		; j++++
+
+	xmpyu		${fni}L,${fm0}R,${fnm0}	; np[0]*m
+	xmpyu		${fni}R,${fm0}R,${fnm1}	; np[1]*m
+	fstds		${fab0},-16($xfer)
+	fstds		${fnm0},-8($xfer)
+	fstds		${fab1},0($xfer)
+	fstds		${fnm1},8($xfer)
+	 flddx		$idx($ap),${fai}	; ap[2,3]
+	 flddx		$idx($np),${fni}	; np[2,3]
+___
+$code.=<<___ if ($BN_SZ==4);
+	mtctl		$hi0,%cr11		; $hi0 still holds 31
+	extrd,u,*=	$hi0,%sar,1,$hi0	; executes on PA-RISC 1.0
+	b		L\$parisc11
+	nop
+___
+$code.=<<___;					# PA-RISC 2.0 code-path
+	xmpyu		${fai}L,${fbi},${fab0}	; ap[j]*bp[0]
+	xmpyu		${fni}L,${fm0}R,${fnm0}	; np[j]*m
+	ldd		-16($xfer),$ab0
+	fstds		${fab0},-16($xfer)
+
+	extrd,u		$ab0,31,32,$hi0
+	extrd,u		$ab0,63,32,$ab0
+	ldd		-8($xfer),$nm0
+	fstds		${fnm0},-8($xfer)
+	 ldo		8($idx),$idx		; j++++
+	 addl		$ab0,$nm0,$nm0		; low part is discarded
+	 extrd,u	$nm0,31,32,$hi1
+
+L\$1st
+	xmpyu		${fai}R,${fbi},${fab1}	; ap[j+1]*bp[0]
+	xmpyu		${fni}R,${fm0}R,${fnm1}	; np[j+1]*m
+	ldd		0($xfer),$ab1
+	fstds		${fab1},0($xfer)
+	 addl		$hi0,$ab1,$ab1
+	 extrd,u	$ab1,31,32,$hi0
+	ldd		8($xfer),$nm1
+	fstds		${fnm1},8($xfer)
+	 extrd,u	$ab1,63,32,$ab1
+	 addl		$hi1,$nm1,$nm1
+	flddx		$idx($ap),${fai}	; ap[j,j+1]
+	flddx		$idx($np),${fni}	; np[j,j+1]
+	 addl		$ab1,$nm1,$nm1
+	 extrd,u	$nm1,31,32,$hi1
+
+	xmpyu		${fai}L,${fbi},${fab0}	; ap[j]*bp[0]
+	xmpyu		${fni}L,${fm0}R,${fnm0}	; np[j]*m
+	ldd		-16($xfer),$ab0
+	fstds		${fab0},-16($xfer)
+	 addl		$hi0,$ab0,$ab0
+	 extrd,u	$ab0,31,32,$hi0
+	ldd		-8($xfer),$nm0
+	fstds		${fnm0},-8($xfer)
+	 extrd,u	$ab0,63,32,$ab0
+	 addl		$hi1,$nm0,$nm0
+	stw		$nm1,-4($tp)		; tp[j-1]
+	 addl		$ab0,$nm0,$nm0
+	 stw,ma		$nm0,8($tp)		; tp[j-1]
+	addib,<>	8,$idx,L\$1st		; j++++
+	 extrd,u	$nm0,31,32,$hi1
+
+	xmpyu		${fai}R,${fbi},${fab1}	; ap[j]*bp[0]
+	xmpyu		${fni}R,${fm0}R,${fnm1}	; np[j]*m
+	ldd		0($xfer),$ab1
+	fstds		${fab1},0($xfer)
+	 addl		$hi0,$ab1,$ab1
+	 extrd,u	$ab1,31,32,$hi0
+	ldd		8($xfer),$nm1
+	fstds		${fnm1},8($xfer)
+	 extrd,u	$ab1,63,32,$ab1
+	 addl		$hi1,$nm1,$nm1
+	ldd		-16($xfer),$ab0
+	 addl		$ab1,$nm1,$nm1
+	ldd		-8($xfer),$nm0
+	 extrd,u	$nm1,31,32,$hi1
+
+	 addl		$hi0,$ab0,$ab0
+	 extrd,u	$ab0,31,32,$hi0
+	stw		$nm1,-4($tp)		; tp[j-1]
+	 extrd,u	$ab0,63,32,$ab0
+	 addl		$hi1,$nm0,$nm0
+	ldd		0($xfer),$ab1
+	 addl		$ab0,$nm0,$nm0
+	ldd,mb		8($xfer),$nm1
+	 extrd,u	$nm0,31,32,$hi1
+	stw,ma		$nm0,8($tp)		; tp[j-1]
+
+	ldo		-1($num),$num		; i--
+	subi		0,$arrsz,$idx		; j=0
+___
+$code.=<<___ if ($BN_SZ==4);
+	fldws,ma	4($bp),${fbi}		; bp[1]
+___
+$code.=<<___ if ($BN_SZ==8);
+	fldws		0($bp),${fbi}		; bp[1] in flipped word order
+___
+$code.=<<___;
+	 flddx		$idx($ap),${fai}	; ap[0,1]
+	 flddx		$idx($np),${fni}	; np[0,1]
+	 fldws		8($xfer),${fti}R	; tp[0]
+	addl		$hi0,$ab1,$ab1
+	 extrd,u	$ab1,31,32,$hi0
+	 extrd,u	$ab1,63,32,$ab1
+	 ldo		8($idx),$idx		; j++++
+	 xmpyu		${fai}L,${fbi},${fab0}	; ap[0]*bp[1]
+	 xmpyu		${fai}R,${fbi},${fab1}	; ap[1]*bp[1]
+	addl		$hi1,$nm1,$nm1
+	addl		$ab1,$nm1,$nm1
+	extrd,u		$nm1,31,32,$hi1
+	 fstws,mb	${fab0}L,-8($xfer)	; save high part
+	stw		$nm1,-4($tp)		; tp[j-1]
+
+	 fcpy,sgl	%fr0,${fti}L		; zero high part
+	 fcpy,sgl	%fr0,${fab0}L
+	addl		$hi1,$hi0,$hi0
+	extrd,u		$hi0,31,32,$hi1
+	 fcnvxf,dbl,dbl	${fti},${fti}		; 32-bit unsigned int -> double
+	 fcnvxf,dbl,dbl	${fab0},${fab0}
+	stw		$hi0,0($tp)
+	stw		$hi1,4($tp)
+
+	fadd,dbl	${fti},${fab0},${fab0}	; add tp[0]
+	fcnvfx,dbl,dbl	${fab0},${fab0}		; double -> 33-bit unsigned int
+	xmpyu		${fn0},${fab0}R,${fm0}
+	ldo		`$LOCALS+32+4`($fp),$tp
+L\$outer
+	xmpyu		${fni}L,${fm0}R,${fnm0}	; np[0]*m
+	xmpyu		${fni}R,${fm0}R,${fnm1}	; np[1]*m
+	fstds		${fab0},-16($xfer)	; 33-bit value
+	fstds		${fnm0},-8($xfer)
+	 flddx		$idx($ap),${fai}	; ap[2]
+	 flddx		$idx($np),${fni}	; np[2]
+	 ldo		8($idx),$idx		; j++++
+	ldd		-16($xfer),$ab0		; 33-bit value
+	ldd		-8($xfer),$nm0
+	ldw		0($xfer),$hi0		; high part
+
+	xmpyu		${fai}L,${fbi},${fab0}	; ap[j]*bp[i]
+	xmpyu		${fni}L,${fm0}R,${fnm0}	; np[j]*m
+	 extrd,u	$ab0,31,32,$ti0		; carry bit
+	 extrd,u	$ab0,63,32,$ab0
+	fstds		${fab1},0($xfer)
+	 addl		$ti0,$hi0,$hi0		; account carry bit
+	fstds		${fnm1},8($xfer)
+	 addl		$ab0,$nm0,$nm0		; low part is discarded
+	ldw		0($tp),$ti1		; tp[1]
+	 extrd,u	$nm0,31,32,$hi1
+	fstds		${fab0},-16($xfer)
+	fstds		${fnm0},-8($xfer)
+
+L\$inner
+	xmpyu		${fai}R,${fbi},${fab1}	; ap[j+1]*bp[i]
+	xmpyu		${fni}R,${fm0}R,${fnm1}	; np[j+1]*m
+	ldd		0($xfer),$ab1
+	fstds		${fab1},0($xfer)
+	 addl		$hi0,$ti1,$ti1
+	 addl		$ti1,$ab1,$ab1
+	ldd		8($xfer),$nm1
+	fstds		${fnm1},8($xfer)
+	 extrd,u	$ab1,31,32,$hi0
+	 extrd,u	$ab1,63,32,$ab1
+	flddx		$idx($ap),${fai}	; ap[j,j+1]
+	flddx		$idx($np),${fni}	; np[j,j+1]
+	 addl		$hi1,$nm1,$nm1
+	 addl		$ab1,$nm1,$nm1
+	ldw		4($tp),$ti0		; tp[j]
+	stw		$nm1,-4($tp)		; tp[j-1]
+
+	xmpyu		${fai}L,${fbi},${fab0}	; ap[j]*bp[i]
+	xmpyu		${fni}L,${fm0}R,${fnm0}	; np[j]*m
+	ldd		-16($xfer),$ab0
+	fstds		${fab0},-16($xfer)
+	 addl		$hi0,$ti0,$ti0
+	 addl		$ti0,$ab0,$ab0
+	ldd		-8($xfer),$nm0
+	fstds		${fnm0},-8($xfer)
+	 extrd,u	$ab0,31,32,$hi0
+	 extrd,u	$nm1,31,32,$hi1
+	ldw		8($tp),$ti1		; tp[j]
+	 extrd,u	$ab0,63,32,$ab0
+	 addl		$hi1,$nm0,$nm0
+	 addl		$ab0,$nm0,$nm0
+	 stw,ma		$nm0,8($tp)		; tp[j-1]
+	addib,<>	8,$idx,L\$inner		; j++++
+	 extrd,u	$nm0,31,32,$hi1
+
+	xmpyu		${fai}R,${fbi},${fab1}	; ap[j]*bp[i]
+	xmpyu		${fni}R,${fm0}R,${fnm1}	; np[j]*m
+	ldd		0($xfer),$ab1
+	fstds		${fab1},0($xfer)
+	 addl		$hi0,$ti1,$ti1
+	 addl		$ti1,$ab1,$ab1
+	ldd		8($xfer),$nm1
+	fstds		${fnm1},8($xfer)
+	 extrd,u	$ab1,31,32,$hi0
+	 extrd,u	$ab1,63,32,$ab1
+	ldw		4($tp),$ti0		; tp[j]
+	 addl		$hi1,$nm1,$nm1
+	 addl		$ab1,$nm1,$nm1
+	ldd		-16($xfer),$ab0
+	ldd		-8($xfer),$nm0
+	 extrd,u	$nm1,31,32,$hi1
+
+	addl		$hi0,$ab0,$ab0
+	 addl		$ti0,$ab0,$ab0
+	 stw		$nm1,-4($tp)		; tp[j-1]
+	 extrd,u	$ab0,31,32,$hi0
+	ldw		8($tp),$ti1		; tp[j]
+	 extrd,u	$ab0,63,32,$ab0
+	 addl		$hi1,$nm0,$nm0
+	ldd		0($xfer),$ab1
+	 addl		$ab0,$nm0,$nm0
+	ldd,mb		8($xfer),$nm1
+	 extrd,u	$nm0,31,32,$hi1
+	 stw,ma		$nm0,8($tp)		; tp[j-1]
+
+	addib,=		-1,$num,L\$outerdone	; i--
+	subi		0,$arrsz,$idx		; j=0
+___
+$code.=<<___ if ($BN_SZ==4);
+	fldws,ma	4($bp),${fbi}		; bp[i]
+___
+$code.=<<___ if ($BN_SZ==8);
+	ldi		12,$ti0			; bp[i] in flipped word order
+	addl,ev		%r0,$num,$num
+	ldi		-4,$ti0
+	addl		$ti0,$bp,$bp
+	fldws		0($bp),${fbi}
+___
+$code.=<<___;
+	 flddx		$idx($ap),${fai}	; ap[0]
+	addl		$hi0,$ab1,$ab1
+	 flddx		$idx($np),${fni}	; np[0]
+	 fldws		8($xfer),${fti}R	; tp[0]
+	addl		$ti1,$ab1,$ab1
+	extrd,u		$ab1,31,32,$hi0
+	extrd,u		$ab1,63,32,$ab1
+
+	 ldo		8($idx),$idx		; j++++
+	 xmpyu		${fai}L,${fbi},${fab0}	; ap[0]*bp[i]
+	 xmpyu		${fai}R,${fbi},${fab1}	; ap[1]*bp[i]
+	ldw		4($tp),$ti0		; tp[j]
+
+	addl		$hi1,$nm1,$nm1
+	 fstws,mb	${fab0}L,-8($xfer)	; save high part
+	addl		$ab1,$nm1,$nm1
+	extrd,u		$nm1,31,32,$hi1
+	 fcpy,sgl	%fr0,${fti}L		; zero high part
+	 fcpy,sgl	%fr0,${fab0}L
+	stw		$nm1,-4($tp)		; tp[j-1]
+
+	 fcnvxf,dbl,dbl	${fti},${fti}		; 32-bit unsigned int -> double
+	 fcnvxf,dbl,dbl	${fab0},${fab0}
+	addl		$hi1,$hi0,$hi0
+	 fadd,dbl	${fti},${fab0},${fab0}	; add tp[0]
+	addl		$ti0,$hi0,$hi0
+	extrd,u		$hi0,31,32,$hi1
+	 fcnvfx,dbl,dbl	${fab0},${fab0}		; double -> 33-bit unsigned int
+	stw		$hi0,0($tp)
+	stw		$hi1,4($tp)
+	 xmpyu		${fn0},${fab0}R,${fm0}
+
+	b		L\$outer
+	ldo		`$LOCALS+32+4`($fp),$tp
+
+L\$outerdone
+	addl		$hi0,$ab1,$ab1
+	addl		$ti1,$ab1,$ab1
+	extrd,u		$ab1,31,32,$hi0
+	extrd,u		$ab1,63,32,$ab1
+
+	ldw		4($tp),$ti0		; tp[j]
+
+	addl		$hi1,$nm1,$nm1
+	addl		$ab1,$nm1,$nm1
+	extrd,u		$nm1,31,32,$hi1
+	stw		$nm1,-4($tp)		; tp[j-1]
+
+	addl		$hi1,$hi0,$hi0
+	addl		$ti0,$hi0,$hi0
+	extrd,u		$hi0,31,32,$hi1
+	stw		$hi0,0($tp)
+	stw		$hi1,4($tp)
+
+	ldo		`$LOCALS+32`($fp),$tp
+	sub		%r0,%r0,%r0		; clear borrow
+___
+$code.=<<___ if ($BN_SZ==4);
+	ldws,ma		4($tp),$ti0
+	extru,=		$rp,31,3,%r0		; is rp 64-bit aligned?
+	b		L\$sub_pa11
+	addl		$tp,$arrsz,$tp
+L\$sub
+	ldwx		$idx($np),$hi0
+	subb		$ti0,$hi0,$hi1
+	ldwx		$idx($tp),$ti0
+	addib,<>	4,$idx,L\$sub
+	stws,ma		$hi1,4($rp)
+
+	subb		$ti0,%r0,$hi1
+	ldo		-4($tp),$tp
+___
+$code.=<<___ if ($BN_SZ==8);
+	ldd,ma		8($tp),$ti0
+L\$sub
+	ldd		$idx($np),$hi0
+	shrpd		$ti0,$ti0,32,$ti0	; flip word order
+	std		$ti0,-8($tp)		; save flipped value
+	sub,db		$ti0,$hi0,$hi1
+	ldd,ma		8($tp),$ti0
+	addib,<>	8,$idx,L\$sub
+	std,ma		$hi1,8($rp)
+
+	extrd,u		$ti0,31,32,$ti0		; carry in flipped word order
+	sub,db		$ti0,%r0,$hi1
+	ldo		-8($tp),$tp
+___
+$code.=<<___;
+	and		$tp,$hi1,$ap
+	andcm		$rp,$hi1,$bp
+	or		$ap,$bp,$np
+
+	sub		$rp,$arrsz,$rp		; rewind rp
+	subi		0,$arrsz,$idx
+	ldo		`$LOCALS+32`($fp),$tp
+L\$copy
+	ldd		$idx($np),$hi0
+	std,ma		%r0,8($tp)
+	addib,<>	8,$idx,.-8		; L\$copy
+	std,ma		$hi0,8($rp)	
+___
+
+if ($BN_SZ==4) {				# PA-RISC 1.1 code-path
+$ablo=$ab0;
+$abhi=$ab1;
+$nmlo0=$nm0;
+$nmhi0=$nm1;
+$nmlo1="%r9";
+$nmhi1="%r8";
+
+$code.=<<___;
+	b		L\$done
+	nop
+
+	.ALIGN		8
+L\$parisc11
+	xmpyu		${fai}L,${fbi},${fab0}	; ap[j]*bp[0]
+	xmpyu		${fni}L,${fm0}R,${fnm0}	; np[j]*m
+	ldw		-12($xfer),$ablo
+	ldw		-16($xfer),$hi0
+	ldw		-4($xfer),$nmlo0
+	ldw		-8($xfer),$nmhi0
+	fstds		${fab0},-16($xfer)
+	fstds		${fnm0},-8($xfer)
+
+	 ldo		8($idx),$idx		; j++++
+	 add		$ablo,$nmlo0,$nmlo0	; discarded
+	 addc		%r0,$nmhi0,$hi1
+	ldw		4($xfer),$ablo
+	ldw		0($xfer),$abhi
+	nop
+
+L\$1st_pa11
+	xmpyu		${fai}R,${fbi},${fab1}	; ap[j+1]*bp[0]
+	flddx		$idx($ap),${fai}	; ap[j,j+1]
+	xmpyu		${fni}R,${fm0}R,${fnm1}	; np[j+1]*m
+	flddx		$idx($np),${fni}	; np[j,j+1]
+	 add		$hi0,$ablo,$ablo
+	ldw		12($xfer),$nmlo1
+	 addc		%r0,$abhi,$hi0
+	ldw		8($xfer),$nmhi1
+	 add		$ablo,$nmlo1,$nmlo1
+	fstds		${fab1},0($xfer)
+	 addc		%r0,$nmhi1,$nmhi1
+	fstds		${fnm1},8($xfer)
+	 add		$hi1,$nmlo1,$nmlo1
+	ldw		-12($xfer),$ablo
+	 addc		%r0,$nmhi1,$hi1
+	ldw		-16($xfer),$abhi
+
+	xmpyu		${fai}L,${fbi},${fab0}	; ap[j]*bp[0]
+	ldw		-4($xfer),$nmlo0
+	xmpyu		${fni}L,${fm0}R,${fnm0}	; np[j]*m
+	ldw		-8($xfer),$nmhi0
+	 add		$hi0,$ablo,$ablo
+	stw		$nmlo1,-4($tp)		; tp[j-1]
+	 addc		%r0,$abhi,$hi0
+	fstds		${fab0},-16($xfer)
+	 add		$ablo,$nmlo0,$nmlo0
+	fstds		${fnm0},-8($xfer)
+	 addc		%r0,$nmhi0,$nmhi0
+	ldw		0($xfer),$abhi
+	 add		$hi1,$nmlo0,$nmlo0
+	ldw		4($xfer),$ablo
+	 stws,ma	$nmlo0,8($tp)		; tp[j-1]
+	addib,<>	8,$idx,L\$1st_pa11	; j++++
+	 addc		%r0,$nmhi0,$hi1
+
+	 ldw		8($xfer),$nmhi1
+	 ldw		12($xfer),$nmlo1
+	xmpyu		${fai}R,${fbi},${fab1}	; ap[j]*bp[0]
+	xmpyu		${fni}R,${fm0}R,${fnm1}	; np[j]*m
+	 add		$hi0,$ablo,$ablo
+	fstds		${fab1},0($xfer)
+	 addc		%r0,$abhi,$hi0
+	fstds		${fnm1},8($xfer)
+	 add		$ablo,$nmlo1,$nmlo1
+	ldw		-16($xfer),$abhi
+	 addc		%r0,$nmhi1,$nmhi1
+	ldw		-12($xfer),$ablo
+	 add		$hi1,$nmlo1,$nmlo1
+	ldw		-8($xfer),$nmhi0
+	 addc		%r0,$nmhi1,$hi1
+	ldw		-4($xfer),$nmlo0
+
+	 add		$hi0,$ablo,$ablo
+	stw		$nmlo1,-4($tp)		; tp[j-1]
+	 addc		%r0,$abhi,$hi0
+	ldw		0($xfer),$abhi
+	 add		$ablo,$nmlo0,$nmlo0
+	ldw		4($xfer),$ablo
+	 addc		%r0,$nmhi0,$nmhi0
+	ldws,mb		8($xfer),$nmhi1
+	 add		$hi1,$nmlo0,$nmlo0
+	ldw		4($xfer),$nmlo1
+	 addc		%r0,$nmhi0,$hi1
+	stws,ma		$nmlo0,8($tp)		; tp[j-1]
+
+	ldo		-1($num),$num		; i--
+	subi		0,$arrsz,$idx		; j=0
+
+	 fldws,ma	4($bp),${fbi}		; bp[1]
+	 flddx		$idx($ap),${fai}	; ap[0,1]
+	 flddx		$idx($np),${fni}	; np[0,1]
+	 fldws		8($xfer),${fti}R	; tp[0]
+	add		$hi0,$ablo,$ablo
+	addc		%r0,$abhi,$hi0
+	 ldo		8($idx),$idx		; j++++
+	 xmpyu		${fai}L,${fbi},${fab0}	; ap[0]*bp[1]
+	 xmpyu		${fai}R,${fbi},${fab1}	; ap[1]*bp[1]
+	add		$hi1,$nmlo1,$nmlo1
+	addc		%r0,$nmhi1,$nmhi1
+	add		$ablo,$nmlo1,$nmlo1
+	addc		%r0,$nmhi1,$hi1
+	 fstws,mb	${fab0}L,-8($xfer)	; save high part
+	stw		$nmlo1,-4($tp)		; tp[j-1]
+
+	 fcpy,sgl	%fr0,${fti}L		; zero high part
+	 fcpy,sgl	%fr0,${fab0}L
+	add		$hi1,$hi0,$hi0
+	addc		%r0,%r0,$hi1
+	 fcnvxf,dbl,dbl	${fti},${fti}		; 32-bit unsigned int -> double
+	 fcnvxf,dbl,dbl	${fab0},${fab0}
+	stw		$hi0,0($tp)
+	stw		$hi1,4($tp)
+
+	fadd,dbl	${fti},${fab0},${fab0}	; add tp[0]
+	fcnvfx,dbl,dbl	${fab0},${fab0}		; double -> 33-bit unsigned int
+	xmpyu		${fn0},${fab0}R,${fm0}
+	ldo		`$LOCALS+32+4`($fp),$tp
+L\$outer_pa11
+	xmpyu		${fni}L,${fm0}R,${fnm0}	; np[0]*m
+	xmpyu		${fni}R,${fm0}R,${fnm1}	; np[1]*m
+	fstds		${fab0},-16($xfer)	; 33-bit value
+	fstds		${fnm0},-8($xfer)
+	 flddx		$idx($ap),${fai}	; ap[2,3]
+	 flddx		$idx($np),${fni}	; np[2,3]
+	ldw		-16($xfer),$abhi	; carry bit actually
+	 ldo		8($idx),$idx		; j++++
+	ldw		-12($xfer),$ablo
+	ldw		-8($xfer),$nmhi0
+	ldw		-4($xfer),$nmlo0
+	ldw		0($xfer),$hi0		; high part
+
+	xmpyu		${fai}L,${fbi},${fab0}	; ap[j]*bp[i]
+	xmpyu		${fni}L,${fm0}R,${fnm0}	; np[j]*m
+	fstds		${fab1},0($xfer)
+	 addl		$abhi,$hi0,$hi0		; account carry bit
+	fstds		${fnm1},8($xfer)
+	 add		$ablo,$nmlo0,$nmlo0	; discarded
+	ldw		0($tp),$ti1		; tp[1]
+	 addc		%r0,$nmhi0,$hi1
+	fstds		${fab0},-16($xfer)
+	fstds		${fnm0},-8($xfer)
+	ldw		4($xfer),$ablo
+	ldw		0($xfer),$abhi
+
+L\$inner_pa11
+	xmpyu		${fai}R,${fbi},${fab1}	; ap[j+1]*bp[i]
+	flddx		$idx($ap),${fai}	; ap[j,j+1]
+	xmpyu		${fni}R,${fm0}R,${fnm1}	; np[j+1]*m
+	flddx		$idx($np),${fni}	; np[j,j+1]
+	 add		$hi0,$ablo,$ablo
+	ldw		4($tp),$ti0		; tp[j]
+	 addc		%r0,$abhi,$abhi
+	ldw		12($xfer),$nmlo1
+	 add		$ti1,$ablo,$ablo
+	ldw		8($xfer),$nmhi1
+	 addc		%r0,$abhi,$hi0
+	fstds		${fab1},0($xfer)
+	 add		$ablo,$nmlo1,$nmlo1
+	fstds		${fnm1},8($xfer)
+	 addc		%r0,$nmhi1,$nmhi1
+	ldw		-12($xfer),$ablo
+	 add		$hi1,$nmlo1,$nmlo1
+	ldw		-16($xfer),$abhi
+	 addc		%r0,$nmhi1,$hi1
+
+	xmpyu		${fai}L,${fbi},${fab0}	; ap[j]*bp[i]
+	ldw		8($tp),$ti1		; tp[j]
+	xmpyu		${fni}L,${fm0}R,${fnm0}	; np[j]*m
+	ldw		-4($xfer),$nmlo0
+	 add		$hi0,$ablo,$ablo
+	ldw		-8($xfer),$nmhi0
+	 addc		%r0,$abhi,$abhi
+	stw		$nmlo1,-4($tp)		; tp[j-1]
+	 add		$ti0,$ablo,$ablo
+	fstds		${fab0},-16($xfer)
+	 addc		%r0,$abhi,$hi0
+	fstds		${fnm0},-8($xfer)
+	 add		$ablo,$nmlo0,$nmlo0
+	ldw		4($xfer),$ablo
+	 addc		%r0,$nmhi0,$nmhi0
+	ldw		0($xfer),$abhi
+	 add		$hi1,$nmlo0,$nmlo0
+	 stws,ma	$nmlo0,8($tp)		; tp[j-1]
+	addib,<>	8,$idx,L\$inner_pa11	; j++++
+	 addc		%r0,$nmhi0,$hi1
+
+	xmpyu		${fai}R,${fbi},${fab1}	; ap[j]*bp[i]
+	ldw		12($xfer),$nmlo1
+	xmpyu		${fni}R,${fm0}R,${fnm1}	; np[j]*m
+	ldw		8($xfer),$nmhi1
+	 add		$hi0,$ablo,$ablo
+	ldw		4($tp),$ti0		; tp[j]
+	 addc		%r0,$abhi,$abhi
+	fstds		${fab1},0($xfer)
+	 add		$ti1,$ablo,$ablo
+	fstds		${fnm1},8($xfer)
+	 addc		%r0,$abhi,$hi0
+	ldw		-16($xfer),$abhi
+	 add		$ablo,$nmlo1,$nmlo1
+	ldw		-12($xfer),$ablo
+	 addc		%r0,$nmhi1,$nmhi1
+	ldw		-8($xfer),$nmhi0
+	 add		$hi1,$nmlo1,$nmlo1
+	ldw		-4($xfer),$nmlo0
+	 addc		%r0,$nmhi1,$hi1
+
+	add		$hi0,$ablo,$ablo
+	 stw		$nmlo1,-4($tp)		; tp[j-1]
+	addc		%r0,$abhi,$abhi
+	 add		$ti0,$ablo,$ablo
+	ldw		8($tp),$ti1		; tp[j]
+	 addc		%r0,$abhi,$hi0
+	ldw		0($xfer),$abhi
+	 add		$ablo,$nmlo0,$nmlo0
+	ldw		4($xfer),$ablo
+	 addc		%r0,$nmhi0,$nmhi0
+	ldws,mb		8($xfer),$nmhi1
+	 add		$hi1,$nmlo0,$nmlo0
+	ldw		4($xfer),$nmlo1
+	 addc		%r0,$nmhi0,$hi1
+	 stws,ma	$nmlo0,8($tp)		; tp[j-1]
+
+	addib,=		-1,$num,L\$outerdone_pa11; i--
+	subi		0,$arrsz,$idx		; j=0
+
+	 fldws,ma	4($bp),${fbi}		; bp[i]
+	 flddx		$idx($ap),${fai}	; ap[0]
+	add		$hi0,$ablo,$ablo
+	addc		%r0,$abhi,$abhi
+	 flddx		$idx($np),${fni}	; np[0]
+	 fldws		8($xfer),${fti}R	; tp[0]
+	add		$ti1,$ablo,$ablo
+	addc		%r0,$abhi,$hi0
+
+	 ldo		8($idx),$idx		; j++++
+	 xmpyu		${fai}L,${fbi},${fab0}	; ap[0]*bp[i]
+	 xmpyu		${fai}R,${fbi},${fab1}	; ap[1]*bp[i]
+	ldw		4($tp),$ti0		; tp[j]
+
+	add		$hi1,$nmlo1,$nmlo1
+	addc		%r0,$nmhi1,$nmhi1
+	 fstws,mb	${fab0}L,-8($xfer)	; save high part
+	add		$ablo,$nmlo1,$nmlo1
+	addc		%r0,$nmhi1,$hi1
+	 fcpy,sgl	%fr0,${fti}L		; zero high part
+	 fcpy,sgl	%fr0,${fab0}L
+	stw		$nmlo1,-4($tp)		; tp[j-1]
+
+	 fcnvxf,dbl,dbl	${fti},${fti}		; 32-bit unsigned int -> double
+	 fcnvxf,dbl,dbl	${fab0},${fab0}
+	add		$hi1,$hi0,$hi0
+	addc		%r0,%r0,$hi1
+	 fadd,dbl	${fti},${fab0},${fab0}	; add tp[0]
+	add		$ti0,$hi0,$hi0
+	addc		%r0,$hi1,$hi1
+	 fcnvfx,dbl,dbl	${fab0},${fab0}		; double -> 33-bit unsigned int
+	stw		$hi0,0($tp)
+	stw		$hi1,4($tp)
+	 xmpyu		${fn0},${fab0}R,${fm0}
+
+	b		L\$outer_pa11
+	ldo		`$LOCALS+32+4`($fp),$tp
+
+L\$outerdone_pa11
+	add		$hi0,$ablo,$ablo
+	addc		%r0,$abhi,$abhi
+	add		$ti1,$ablo,$ablo
+	addc		%r0,$abhi,$hi0
+
+	ldw		4($tp),$ti0		; tp[j]
+
+	add		$hi1,$nmlo1,$nmlo1
+	addc		%r0,$nmhi1,$nmhi1
+	add		$ablo,$nmlo1,$nmlo1
+	addc		%r0,$nmhi1,$hi1
+	stw		$nmlo1,-4($tp)		; tp[j-1]
+
+	add		$hi1,$hi0,$hi0
+	addc		%r0,%r0,$hi1
+	add		$ti0,$hi0,$hi0
+	addc		%r0,$hi1,$hi1
+	stw		$hi0,0($tp)
+	stw		$hi1,4($tp)
+
+	ldo		`$LOCALS+32+4`($fp),$tp
+	sub		%r0,%r0,%r0		; clear borrow
+	ldw		-4($tp),$ti0
+	addl		$tp,$arrsz,$tp
+L\$sub_pa11
+	ldwx		$idx($np),$hi0
+	subb		$ti0,$hi0,$hi1
+	ldwx		$idx($tp),$ti0
+	addib,<>	4,$idx,L\$sub_pa11
+	stws,ma		$hi1,4($rp)
+
+	subb		$ti0,%r0,$hi1
+	ldo		-4($tp),$tp
+	and		$tp,$hi1,$ap
+	andcm		$rp,$hi1,$bp
+	or		$ap,$bp,$np
+
+	sub		$rp,$arrsz,$rp		; rewind rp
+	subi		0,$arrsz,$idx
+	ldo		`$LOCALS+32`($fp),$tp
+L\$copy_pa11
+	ldwx		$idx($np),$hi0
+	stws,ma		%r0,4($tp)
+	addib,<>	4,$idx,L\$copy_pa11
+	stws,ma		$hi0,4($rp)	
+
+	nop					; alignment
+L\$done
+___
+}
+
+$code.=<<___;
+	ldi		1,%r28			; signal "handled"
+	ldo		$FRAME($fp),%sp		; destroy tp[num+1]
+
+	$POP	`-$FRAME-$SAVED_RP`(%sp),%r2	; standard epilogue
+	$POP	`-$FRAME+1*$SIZE_T`(%sp),%r4
+	$POP	`-$FRAME+2*$SIZE_T`(%sp),%r5
+	$POP	`-$FRAME+3*$SIZE_T`(%sp),%r6
+	$POP	`-$FRAME+4*$SIZE_T`(%sp),%r7
+	$POP	`-$FRAME+5*$SIZE_T`(%sp),%r8
+	$POP	`-$FRAME+6*$SIZE_T`(%sp),%r9
+	$POP	`-$FRAME+7*$SIZE_T`(%sp),%r10
+L\$abort
+	bv	(%r2)
+	.EXIT
+	$POPMB	-$FRAME(%sp),%r3
+	.PROCEND
+	.STRINGZ "Montgomery Multiplication for PA-RISC, CRYPTOGAMS by "
+___
+
+# Explicitly encode PA-RISC 2.0 instructions used in this module, so
+# that it can be compiled with .LEVEL 1.0. It should be noted that I
+# wouldn't have to do this, if GNU assembler understood .ALLOW 2.0
+# directive...
+
+my $ldd = sub {
+  my ($mod,$args) = @_;
+  my $orig = "ldd$mod\t$args";
+
+    if ($args =~ /%r([0-9]+)\(%r([0-9]+)\),%r([0-9]+)/)		# format 4
+    {	my $opcode=(0x03<<26)|($2<<21)|($1<<16)|(3<<6)|$3;
+	sprintf "\t.WORD\t0x%08x\t; %s",$opcode,$orig;
+    }
+    elsif ($args =~ /(\-?[0-9]+)\(%r([0-9]+)\),%r([0-9]+)/)	# format 5
+    {	my $opcode=(0x03<<26)|($2<<21)|(1<<12)|(3<<6)|$3;
+	$opcode|=(($1&0xF)<<17)|(($1&0x10)<<12);		# encode offset
+	$opcode|=(1<<5)  if ($mod =~ /^,m/);
+	$opcode|=(1<<13) if ($mod =~ /^,mb/);
+	sprintf "\t.WORD\t0x%08x\t; %s",$opcode,$orig;
+    }
+    else { "\t".$orig; }
+};
+
+my $std = sub {
+  my ($mod,$args) = @_;
+  my $orig = "std$mod\t$args";
+
+    if ($args =~ /%r([0-9]+),(\-?[0-9]+)\(%r([0-9]+)\)/)	# format 6
+    {	my $opcode=(0x03<<26)|($3<<21)|($1<<16)|(1<<12)|(0xB<<6);
+	$opcode|=(($2&0xF)<<1)|(($2&0x10)>>4);			# encode offset
+	$opcode|=(1<<5)  if ($mod =~ /^,m/);
+	$opcode|=(1<<13) if ($mod =~ /^,mb/);
+	sprintf "\t.WORD\t0x%08x\t; %s",$opcode,$orig;
+    }
+    else { "\t".$orig; }
+};
+
+my $extrd = sub {
+  my ($mod,$args) = @_;
+  my $orig = "extrd$mod\t$args";
+
+    # I only have ",u" completer, it's implicitly encoded...
+    if ($args =~ /%r([0-9]+),([0-9]+),([0-9]+),%r([0-9]+)/)	# format 15
+    {	my $opcode=(0x36<<26)|($1<<21)|($4<<16);
+	my $len=32-$3;
+	$opcode |= (($2&0x20)<<6)|(($2&0x1f)<<5);		# encode pos
+	$opcode |= (($len&0x20)<<7)|($len&0x1f);		# encode len
+	sprintf "\t.WORD\t0x%08x\t; %s",$opcode,$orig;
+    }
+    elsif ($args =~ /%r([0-9]+),%sar,([0-9]+),%r([0-9]+)/)	# format 12
+    {	my $opcode=(0x34<<26)|($1<<21)|($3<<16)|(2<<11)|(1<<9);
+	my $len=32-$2;
+	$opcode |= (($len&0x20)<<3)|($len&0x1f);		# encode len
+	$opcode |= (1<<13) if ($mod =~ /,\**=/);
+	sprintf "\t.WORD\t0x%08x\t; %s",$opcode,$orig;
+    }
+    else { "\t".$orig; }
+};
+
+my $shrpd = sub {
+  my ($mod,$args) = @_;
+  my $orig = "shrpd$mod\t$args";
+
+    if ($args =~ /%r([0-9]+),%r([0-9]+),([0-9]+),%r([0-9]+)/)	# format 14
+    {	my $opcode=(0x34<<26)|($2<<21)|($1<<16)|(1<<10)|$4;
+	my $cpos=63-$3;
+	$opcode |= (($cpos&0x20)<<6)|(($cpos&0x1f)<<5);		# encode sa
+	sprintf "\t.WORD\t0x%08x\t; %s",$opcode,$orig;
+    }
+    else { "\t".$orig; }
+};
+
+my $sub = sub {
+  my ($mod,$args) = @_;
+  my $orig = "sub$mod\t$args";
+
+    if ($mod eq ",db" && $args =~ /%r([0-9]+),%r([0-9]+),%r([0-9]+)/) {
+	my $opcode=(0x02<<26)|($2<<21)|($1<<16)|$3;
+	$opcode|=(1<<10);	# e1
+	$opcode|=(1<<8);	# e2
+	$opcode|=(1<<5);	# d
+	sprintf "\t.WORD\t0x%08x\t; %s",$opcode,$orig
+    }
+    else { "\t".$orig; }
+};
+
+sub assemble {
+  my ($mnemonic,$mod,$args)=@_;
+  my $opcode = eval("\$$mnemonic");
+
+    ref($opcode) eq 'CODE' ? &$opcode($mod,$args) : "\t$mnemonic$mod\t$args";
+}
+
+foreach (split("\n",$code)) {
+	s/\`([^\`]*)\`/eval $1/ge;
+	# flip word order in 64-bit mode...
+	s/(xmpyu\s+)($fai|$fni)([LR])/$1.$2.($3 eq "L"?"R":"L")/e if ($BN_SZ==8);
+	# assemble 2.0 instructions in 32-bit mode...
+	s/^\s+([a-z]+)([\S]*)\s+([\S]*)/&assemble($1,$2,$3)/e if ($BN_SZ==4);
+
+	s/\bbv\b/bve/gm	if ($SIZE_T==8);
+
+	print $_,"\n";
+}
+close STDOUT;
diff --git a/crypto/bn/asm/ppc-mont.pl b/crypto/bn/asm/ppc-mont.pl
new file mode 100644
index 000000000..5802260ca
--- /dev/null
+++ b/crypto/bn/asm/ppc-mont.pl
@@ -0,0 +1,342 @@
+#! /usr/bin/env perl
+# Copyright 2006-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+
+# ====================================================================
+# Written by Andy Polyakov  for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+
+# April 2006
+
+# "Teaser" Montgomery multiplication module for PowerPC. It's possible
+# to gain a bit more by modulo-scheduling outer loop, then dedicated
+# squaring procedure should give further 20% and code can be adapted
+# for 32-bit application running on 64-bit CPU. As for the latter.
+# It won't be able to achieve "native" 64-bit performance, because in
+# 32-bit application context every addc instruction will have to be
+# expanded as addc, twice right shift by 32 and finally adde, etc.
+# So far RSA *sign* performance improvement over pre-bn_mul_mont asm
+# for 64-bit application running on PPC970/G5 is:
+#
+# 512-bit	+65%	
+# 1024-bit	+35%
+# 2048-bit	+18%
+# 4096-bit	+4%
+
+$flavour = shift;
+
+if ($flavour =~ /32/) {
+	$BITS=	32;
+	$BNSZ=	$BITS/8;
+	$SIZE_T=4;
+	$RZONE=	224;
+
+	$LD=	"lwz";		# load
+	$LDU=	"lwzu";		# load and update
+	$LDX=	"lwzx";		# load indexed
+	$ST=	"stw";		# store
+	$STU=	"stwu";		# store and update
+	$STX=	"stwx";		# store indexed
+	$STUX=	"stwux";	# store indexed and update
+	$UMULL=	"mullw";	# unsigned multiply low
+	$UMULH=	"mulhwu";	# unsigned multiply high
+	$UCMP=	"cmplw";	# unsigned compare
+	$SHRI=	"srwi";		# unsigned shift right by immediate	
+	$PUSH=	$ST;
+	$POP=	$LD;
+} elsif ($flavour =~ /64/) {
+	$BITS=	64;
+	$BNSZ=	$BITS/8;
+	$SIZE_T=8;
+	$RZONE=	288;
+
+	# same as above, but 64-bit mnemonics...
+	$LD=	"ld";		# load
+	$LDU=	"ldu";		# load and update
+	$LDX=	"ldx";		# load indexed
+	$ST=	"std";		# store
+	$STU=	"stdu";		# store and update
+	$STX=	"stdx";		# store indexed
+	$STUX=	"stdux";	# store indexed and update
+	$UMULL=	"mulld";	# unsigned multiply low
+	$UMULH=	"mulhdu";	# unsigned multiply high
+	$UCMP=	"cmpld";	# unsigned compare
+	$SHRI=	"srdi";		# unsigned shift right by immediate	
+	$PUSH=	$ST;
+	$POP=	$LD;
+} else { die "nonsense $flavour"; }
+
+$FRAME=8*$SIZE_T+$RZONE;
+$LOCALS=8*$SIZE_T;
+
+$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+( $xlate="${dir}ppc-xlate.pl" and -f $xlate ) or
+( $xlate="${dir}../../perlasm/ppc-xlate.pl" and -f $xlate) or
+die "can't locate ppc-xlate.pl";
+
+open STDOUT,"| $^X $xlate $flavour ".shift || die "can't call $xlate: $!";
+
+$sp="r1";
+$toc="r2";
+$rp="r3";	$ovf="r3";
+$ap="r4";
+$bp="r5";
+$np="r6";
+$n0="r7";
+$num="r8";
+$rp="r9";	# $rp is reassigned
+$aj="r10";
+$nj="r11";
+$tj="r12";
+# non-volatile registers
+$i="r20";
+$j="r21";
+$tp="r22";
+$m0="r23";
+$m1="r24";
+$lo0="r25";
+$hi0="r26";
+$lo1="r27";
+$hi1="r28";
+$alo="r29";
+$ahi="r30";
+$nlo="r31";
+#
+$nhi="r0";
+
+$code=<<___;
+.machine "any"
+.text
+
+.globl	.bn_mul_mont_int
+.align	4
+.bn_mul_mont_int:
+	cmpwi	$num,4
+	mr	$rp,r3		; $rp is reassigned
+	li	r3,0
+	bltlr
+___
+$code.=<<___ if ($BNSZ==4);
+	cmpwi	$num,32		; longer key performance is not better
+	bgelr
+___
+$code.=<<___;
+	slwi	$num,$num,`log($BNSZ)/log(2)`
+	li	$tj,-4096
+	addi	$ovf,$num,$FRAME
+	subf	$ovf,$ovf,$sp	; $sp-$ovf
+	and	$ovf,$ovf,$tj	; minimize TLB usage
+	subf	$ovf,$sp,$ovf	; $ovf-$sp
+	mr	$tj,$sp
+	srwi	$num,$num,`log($BNSZ)/log(2)`
+	$STUX	$sp,$sp,$ovf
+
+	$PUSH	r20,`-12*$SIZE_T`($tj)
+	$PUSH	r21,`-11*$SIZE_T`($tj)
+	$PUSH	r22,`-10*$SIZE_T`($tj)
+	$PUSH	r23,`-9*$SIZE_T`($tj)
+	$PUSH	r24,`-8*$SIZE_T`($tj)
+	$PUSH	r25,`-7*$SIZE_T`($tj)
+	$PUSH	r26,`-6*$SIZE_T`($tj)
+	$PUSH	r27,`-5*$SIZE_T`($tj)
+	$PUSH	r28,`-4*$SIZE_T`($tj)
+	$PUSH	r29,`-3*$SIZE_T`($tj)
+	$PUSH	r30,`-2*$SIZE_T`($tj)
+	$PUSH	r31,`-1*$SIZE_T`($tj)
+
+	$LD	$n0,0($n0)	; pull n0[0] value
+	addi	$num,$num,-2	; adjust $num for counter register
+
+	$LD	$m0,0($bp)	; m0=bp[0]
+	$LD	$aj,0($ap)	; ap[0]
+	addi	$tp,$sp,$LOCALS
+	$UMULL	$lo0,$aj,$m0	; ap[0]*bp[0]
+	$UMULH	$hi0,$aj,$m0
+
+	$LD	$aj,$BNSZ($ap)	; ap[1]
+	$LD	$nj,0($np)	; np[0]
+
+	$UMULL	$m1,$lo0,$n0	; "tp[0]"*n0
+
+	$UMULL	$alo,$aj,$m0	; ap[1]*bp[0]
+	$UMULH	$ahi,$aj,$m0
+
+	$UMULL	$lo1,$nj,$m1	; np[0]*m1
+	$UMULH	$hi1,$nj,$m1
+	$LD	$nj,$BNSZ($np)	; np[1]
+	addc	$lo1,$lo1,$lo0
+	addze	$hi1,$hi1
+
+	$UMULL	$nlo,$nj,$m1	; np[1]*m1
+	$UMULH	$nhi,$nj,$m1
+
+	mtctr	$num
+	li	$j,`2*$BNSZ`
+.align	4
+L1st:
+	$LDX	$aj,$ap,$j	; ap[j]
+	addc	$lo0,$alo,$hi0
+	$LDX	$nj,$np,$j	; np[j]
+	addze	$hi0,$ahi
+	$UMULL	$alo,$aj,$m0	; ap[j]*bp[0]
+	addc	$lo1,$nlo,$hi1
+	$UMULH	$ahi,$aj,$m0
+	addze	$hi1,$nhi
+	$UMULL	$nlo,$nj,$m1	; np[j]*m1
+	addc	$lo1,$lo1,$lo0	; np[j]*m1+ap[j]*bp[0]
+	$UMULH	$nhi,$nj,$m1
+	addze	$hi1,$hi1
+	$ST	$lo1,0($tp)	; tp[j-1]
+
+	addi	$j,$j,$BNSZ	; j++
+	addi	$tp,$tp,$BNSZ	; tp++
+	bdnz	L1st
+;L1st
+	addc	$lo0,$alo,$hi0
+	addze	$hi0,$ahi
+
+	addc	$lo1,$nlo,$hi1
+	addze	$hi1,$nhi
+	addc	$lo1,$lo1,$lo0	; np[j]*m1+ap[j]*bp[0]
+	addze	$hi1,$hi1
+	$ST	$lo1,0($tp)	; tp[j-1]
+
+	li	$ovf,0
+	addc	$hi1,$hi1,$hi0
+	addze	$ovf,$ovf	; upmost overflow bit
+	$ST	$hi1,$BNSZ($tp)
+
+	li	$i,$BNSZ
+.align	4
+Louter:
+	$LDX	$m0,$bp,$i	; m0=bp[i]
+	$LD	$aj,0($ap)	; ap[0]
+	addi	$tp,$sp,$LOCALS
+	$LD	$tj,$LOCALS($sp); tp[0]
+	$UMULL	$lo0,$aj,$m0	; ap[0]*bp[i]
+	$UMULH	$hi0,$aj,$m0
+	$LD	$aj,$BNSZ($ap)	; ap[1]
+	$LD	$nj,0($np)	; np[0]
+	addc	$lo0,$lo0,$tj	; ap[0]*bp[i]+tp[0]
+	$UMULL	$alo,$aj,$m0	; ap[j]*bp[i]
+	addze	$hi0,$hi0
+	$UMULL	$m1,$lo0,$n0	; tp[0]*n0
+	$UMULH	$ahi,$aj,$m0
+	$UMULL	$lo1,$nj,$m1	; np[0]*m1
+	$UMULH	$hi1,$nj,$m1
+	$LD	$nj,$BNSZ($np)	; np[1]
+	addc	$lo1,$lo1,$lo0
+	$UMULL	$nlo,$nj,$m1	; np[1]*m1
+	addze	$hi1,$hi1
+	$UMULH	$nhi,$nj,$m1
+
+	mtctr	$num
+	li	$j,`2*$BNSZ`
+.align	4
+Linner:
+	$LDX	$aj,$ap,$j	; ap[j]
+	addc	$lo0,$alo,$hi0
+	$LD	$tj,$BNSZ($tp)	; tp[j]
+	addze	$hi0,$ahi
+	$LDX	$nj,$np,$j	; np[j]
+	addc	$lo1,$nlo,$hi1
+	$UMULL	$alo,$aj,$m0	; ap[j]*bp[i]
+	addze	$hi1,$nhi
+	$UMULH	$ahi,$aj,$m0
+	addc	$lo0,$lo0,$tj	; ap[j]*bp[i]+tp[j]
+	$UMULL	$nlo,$nj,$m1	; np[j]*m1
+	addze	$hi0,$hi0
+	$UMULH	$nhi,$nj,$m1
+	addc	$lo1,$lo1,$lo0	; np[j]*m1+ap[j]*bp[i]+tp[j]
+	addi	$j,$j,$BNSZ	; j++
+	addze	$hi1,$hi1
+	$ST	$lo1,0($tp)	; tp[j-1]
+	addi	$tp,$tp,$BNSZ	; tp++
+	bdnz	Linner
+;Linner
+	$LD	$tj,$BNSZ($tp)	; tp[j]
+	addc	$lo0,$alo,$hi0
+	addze	$hi0,$ahi
+	addc	$lo0,$lo0,$tj	; ap[j]*bp[i]+tp[j]
+	addze	$hi0,$hi0
+
+	addc	$lo1,$nlo,$hi1
+	addze	$hi1,$nhi
+	addc	$lo1,$lo1,$lo0	; np[j]*m1+ap[j]*bp[i]+tp[j]
+	addze	$hi1,$hi1
+	$ST	$lo1,0($tp)	; tp[j-1]
+
+	addic	$ovf,$ovf,-1	; move upmost overflow to XER[CA]
+	li	$ovf,0
+	adde	$hi1,$hi1,$hi0
+	addze	$ovf,$ovf
+	$ST	$hi1,$BNSZ($tp)
+;
+	slwi	$tj,$num,`log($BNSZ)/log(2)`
+	$UCMP	$i,$tj
+	addi	$i,$i,$BNSZ
+	ble	Louter
+
+	addi	$num,$num,2	; restore $num
+	subfc	$j,$j,$j	; j=0 and "clear" XER[CA]
+	addi	$tp,$sp,$LOCALS
+	mtctr	$num
+
+.align	4
+Lsub:	$LDX	$tj,$tp,$j
+	$LDX	$nj,$np,$j
+	subfe	$aj,$nj,$tj	; tp[j]-np[j]
+	$STX	$aj,$rp,$j
+	addi	$j,$j,$BNSZ
+	bdnz	Lsub
+
+	li	$j,0
+	mtctr	$num
+	subfe	$ovf,$j,$ovf	; handle upmost overflow bit
+	and	$ap,$tp,$ovf
+	andc	$np,$rp,$ovf
+	or	$ap,$ap,$np	; ap=borrow?tp:rp
+
+.align	4
+Lcopy:				; copy or in-place refresh
+	$LDX	$tj,$ap,$j
+	$STX	$tj,$rp,$j
+	$STX	$j,$tp,$j	; zap at once
+	addi	$j,$j,$BNSZ
+	bdnz	Lcopy
+
+	$POP	$tj,0($sp)
+	li	r3,1
+	$POP	r20,`-12*$SIZE_T`($tj)
+	$POP	r21,`-11*$SIZE_T`($tj)
+	$POP	r22,`-10*$SIZE_T`($tj)
+	$POP	r23,`-9*$SIZE_T`($tj)
+	$POP	r24,`-8*$SIZE_T`($tj)
+	$POP	r25,`-7*$SIZE_T`($tj)
+	$POP	r26,`-6*$SIZE_T`($tj)
+	$POP	r27,`-5*$SIZE_T`($tj)
+	$POP	r28,`-4*$SIZE_T`($tj)
+	$POP	r29,`-3*$SIZE_T`($tj)
+	$POP	r30,`-2*$SIZE_T`($tj)
+	$POP	r31,`-1*$SIZE_T`($tj)
+	mr	$sp,$tj
+	blr
+	.long	0
+	.byte	0,12,4,0,0x80,12,6,0
+	.long	0
+.size	.bn_mul_mont_int,.-.bn_mul_mont_int
+
+.asciz  "Montgomery Multiplication for PPC, CRYPTOGAMS by "
+___
+
+$code =~ s/\`([^\`]*)\`/eval $1/gem;
+print $code;
+close STDOUT;
diff --git a/crypto/bn/asm/ppc.pl b/crypto/bn/asm/ppc.pl
new file mode 100644
index 000000000..4ea534a1c
--- /dev/null
+++ b/crypto/bn/asm/ppc.pl
@@ -0,0 +1,2014 @@
+#! /usr/bin/env perl
+# Copyright 2004-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+# Implemented as a Perl wrapper as we want to support several different
+# architectures with single file. We pick up the target based on the
+# file name we are asked to generate.
+#
+# It should be noted though that this perl code is nothing like
+# /crypto/perlasm/x86*. In this case perl is used pretty much
+# as pre-processor to cover for platform differences in name decoration,
+# linker tables, 32-/64-bit instruction sets...
+#
+# As you might know there're several PowerPC ABI in use. Most notably
+# Linux and AIX use different 32-bit ABIs. Good news are that these ABIs
+# are similar enough to implement leaf(!) functions, which would be ABI
+# neutral. And that's what you find here: ABI neutral leaf functions.
+# In case you wonder what that is...
+#
+#       AIX performance
+#
+#	MEASUREMENTS WITH cc ON a 200 MhZ PowerPC 604e.
+#
+#	The following is the performance of 32-bit compiler
+#	generated code:
+#
+#	OpenSSL 0.9.6c 21 dec 2001
+#	built on: Tue Jun 11 11:06:51 EDT 2002
+#	options:bn(64,32) ...
+#compiler: cc -DTHREADS  -DAIX -DB_ENDIAN -DBN_LLONG -O3
+#                  sign    verify    sign/s verify/s
+#rsa  512 bits   0.0098s   0.0009s    102.0   1170.6
+#rsa 1024 bits   0.0507s   0.0026s     19.7    387.5
+#rsa 2048 bits   0.3036s   0.0085s      3.3    117.1
+#rsa 4096 bits   2.0040s   0.0299s      0.5     33.4
+#dsa  512 bits   0.0087s   0.0106s    114.3     94.5
+#dsa 1024 bits   0.0256s   0.0313s     39.0     32.0	
+#
+#	Same bechmark with this assembler code:
+#
+#rsa  512 bits   0.0056s   0.0005s    178.6   2049.2
+#rsa 1024 bits   0.0283s   0.0015s     35.3    674.1
+#rsa 2048 bits   0.1744s   0.0050s      5.7    201.2
+#rsa 4096 bits   1.1644s   0.0179s      0.9     55.7
+#dsa  512 bits   0.0052s   0.0062s    191.6    162.0
+#dsa 1024 bits   0.0149s   0.0180s     67.0     55.5
+#
+#	Number of operations increases by at almost 75%
+#
+#	Here are performance numbers for 64-bit compiler
+#	generated code:
+#
+#	OpenSSL 0.9.6g [engine] 9 Aug 2002
+#	built on: Fri Apr 18 16:59:20 EDT 2003
+#	options:bn(64,64) ...
+#	compiler: cc -DTHREADS -D_REENTRANT -q64 -DB_ENDIAN -O3
+#                  sign    verify    sign/s verify/s
+#rsa  512 bits   0.0028s   0.0003s    357.1   3844.4
+#rsa 1024 bits   0.0148s   0.0008s     67.5   1239.7
+#rsa 2048 bits   0.0963s   0.0028s     10.4    353.0
+#rsa 4096 bits   0.6538s   0.0102s      1.5     98.1
+#dsa  512 bits   0.0026s   0.0032s    382.5    313.7
+#dsa 1024 bits   0.0081s   0.0099s    122.8    100.6
+#
+#	Same benchmark with this assembler code:
+#
+#rsa  512 bits   0.0020s   0.0002s    510.4   6273.7
+#rsa 1024 bits   0.0088s   0.0005s    114.1   2128.3
+#rsa 2048 bits   0.0540s   0.0016s     18.5    622.5
+#rsa 4096 bits   0.3700s   0.0058s      2.7    171.0
+#dsa  512 bits   0.0016s   0.0020s    610.7    507.1
+#dsa 1024 bits   0.0047s   0.0058s    212.5    173.2
+#	
+#	Again, performance increases by at about 75%
+#
+#       Mac OS X, Apple G5 1.8GHz (Note this is 32 bit code)
+#       OpenSSL 0.9.7c 30 Sep 2003
+#
+#       Original code.
+#
+#rsa  512 bits   0.0011s   0.0001s    906.1  11012.5
+#rsa 1024 bits   0.0060s   0.0003s    166.6   3363.1
+#rsa 2048 bits   0.0370s   0.0010s     27.1    982.4
+#rsa 4096 bits   0.2426s   0.0036s      4.1    280.4
+#dsa  512 bits   0.0010s   0.0012s   1038.1    841.5
+#dsa 1024 bits   0.0030s   0.0037s    329.6    269.7
+#dsa 2048 bits   0.0101s   0.0127s     98.9     78.6
+#
+#       Same benchmark with this assembler code:
+#
+#rsa  512 bits   0.0007s   0.0001s   1416.2  16645.9
+#rsa 1024 bits   0.0036s   0.0002s    274.4   5380.6
+#rsa 2048 bits   0.0222s   0.0006s     45.1   1589.5
+#rsa 4096 bits   0.1469s   0.0022s      6.8    449.6
+#dsa  512 bits   0.0006s   0.0007s   1664.2   1376.2
+#dsa 1024 bits   0.0018s   0.0023s    545.0    442.2
+#dsa 2048 bits   0.0061s   0.0075s    163.5    132.8
+#
+#        Performance increase of ~60%
+#
+#	If you have comments or suggestions to improve code send
+#	me a note at schari@us.ibm.com
+#
+
+$flavour = shift;
+
+if ($flavour =~ /32/) {
+	$BITS=	32;
+	$BNSZ=	$BITS/8;
+	$ISA=	"\"ppc\"";
+
+	$LD=	"lwz";		# load
+	$LDU=	"lwzu";		# load and update
+	$ST=	"stw";		# store
+	$STU=	"stwu";		# store and update
+	$UMULL=	"mullw";	# unsigned multiply low
+	$UMULH=	"mulhwu";	# unsigned multiply high
+	$UDIV=	"divwu";	# unsigned divide
+	$UCMPI=	"cmplwi";	# unsigned compare with immediate
+	$UCMP=	"cmplw";	# unsigned compare
+	$CNTLZ=	"cntlzw";	# count leading zeros
+	$SHL=	"slw";		# shift left
+	$SHR=	"srw";		# unsigned shift right
+	$SHRI=	"srwi";		# unsigned shift right by immediate	
+	$SHLI=	"slwi";		# shift left by immediate
+	$CLRU=	"clrlwi";	# clear upper bits
+	$INSR=	"insrwi";	# insert right
+	$ROTL=	"rotlwi";	# rotate left by immediate
+	$TR=	"tw";		# conditional trap
+} elsif ($flavour =~ /64/) {
+	$BITS=	64;
+	$BNSZ=	$BITS/8;
+	$ISA=	"\"ppc64\"";
+
+	# same as above, but 64-bit mnemonics...
+	$LD=	"ld";		# load
+	$LDU=	"ldu";		# load and update
+	$ST=	"std";		# store
+	$STU=	"stdu";		# store and update
+	$UMULL=	"mulld";	# unsigned multiply low
+	$UMULH=	"mulhdu";	# unsigned multiply high
+	$UDIV=	"divdu";	# unsigned divide
+	$UCMPI=	"cmpldi";	# unsigned compare with immediate
+	$UCMP=	"cmpld";	# unsigned compare
+	$CNTLZ=	"cntlzd";	# count leading zeros
+	$SHL=	"sld";		# shift left
+	$SHR=	"srd";		# unsigned shift right
+	$SHRI=	"srdi";		# unsigned shift right by immediate	
+	$SHLI=	"sldi";		# shift left by immediate
+	$CLRU=	"clrldi";	# clear upper bits
+	$INSR=	"insrdi";	# insert right 
+	$ROTL=	"rotldi";	# rotate left by immediate
+	$TR=	"td";		# conditional trap
+} else { die "nonsense $flavour"; }
+
+$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+( $xlate="${dir}ppc-xlate.pl" and -f $xlate ) or
+( $xlate="${dir}../../perlasm/ppc-xlate.pl" and -f $xlate) or
+die "can't locate ppc-xlate.pl";
+
+open STDOUT,"| $^X $xlate $flavour ".shift || die "can't call $xlate: $!";
+
+$data=< 0 then result !=0
+				# In either case carry bit is set.
+	beq	Lppcasm_sub_adios
+	addi	r4,r4,-$BNSZ
+	addi	r3,r3,-$BNSZ
+	addi	r5,r5,-$BNSZ
+	mtctr	r6
+Lppcasm_sub_mainloop:	
+	$LDU	r7,$BNSZ(r4)
+	$LDU	r8,$BNSZ(r5)
+	subfe	r6,r8,r7	# r6 = r7+carry bit + onescomplement(r8)
+				# if carry = 1 this is r7-r8. Else it
+				# is r7-r8 -1 as we need.
+	$STU	r6,$BNSZ(r3)
+	bdnz	Lppcasm_sub_mainloop
+Lppcasm_sub_adios:	
+	subfze	r3,r0		# if carry bit is set then r3 = 0 else -1
+	andi.	r3,r3,1         # keep only last bit.
+	blr
+	.long	0
+	.byte	0,12,0x14,0,0,0,4,0
+	.long	0
+.size	.bn_sub_words,.-.bn_sub_words
+
+#
+#	NOTE:	The following label name should be changed to
+#		"bn_add_words" i.e. remove the first dot
+#		for the gcc compiler. This should be automatically
+#		done in the build
+#
+
+.align	4
+.bn_add_words:
+#
+#	Handcoded version of bn_add_words
+#
+#BN_ULONG bn_add_words(BN_ULONG *r, BN_ULONG *a, BN_ULONG *b, int n)
+#
+#	r3 = r
+#	r4 = a
+#	r5 = b
+#	r6 = n
+#
+#       Note:	No loop unrolling done since this is not a performance
+#               critical loop.
+
+	xor	r0,r0,r0
+#
+#	check for r6 = 0. Is this needed?
+#
+	addic.	r6,r6,0		#test r6 and clear carry bit.
+	beq	Lppcasm_add_adios
+	addi	r4,r4,-$BNSZ
+	addi	r3,r3,-$BNSZ
+	addi	r5,r5,-$BNSZ
+	mtctr	r6
+Lppcasm_add_mainloop:	
+	$LDU	r7,$BNSZ(r4)
+	$LDU	r8,$BNSZ(r5)
+	adde	r8,r7,r8
+	$STU	r8,$BNSZ(r3)
+	bdnz	Lppcasm_add_mainloop
+Lppcasm_add_adios:	
+	addze	r3,r0			#return carry bit.
+	blr
+	.long	0
+	.byte	0,12,0x14,0,0,0,4,0
+	.long	0
+.size	.bn_add_words,.-.bn_add_words
+
+#
+#	NOTE:	The following label name should be changed to
+#		"bn_div_words" i.e. remove the first dot
+#		for the gcc compiler. This should be automatically
+#		done in the build
+#
+
+.align	4
+.bn_div_words:
+#
+#	This is a cleaned up version of code generated by
+#	the AIX compiler. The only optimization is to use
+#	the PPC instruction to count leading zeros instead
+#	of call to num_bits_word. Since this was compiled
+#	only at level -O2 we can possibly squeeze it more?
+#	
+#	r3 = h
+#	r4 = l
+#	r5 = d
+	
+	$UCMPI	0,r5,0			# compare r5 and 0
+	bne	Lppcasm_div1		# proceed if d!=0
+	li	r3,-1			# d=0 return -1
+	blr
+Lppcasm_div1:
+	xor	r0,r0,r0		#r0=0
+	li	r8,$BITS
+	$CNTLZ.	r7,r5			#r7 = num leading 0s in d.
+	beq	Lppcasm_div2		#proceed if no leading zeros
+	subf	r8,r7,r8		#r8 = BN_num_bits_word(d)
+	$SHR.	r9,r3,r8		#are there any bits above r8'th?
+	$TR	16,r9,r0		#if there're, signal to dump core...
+Lppcasm_div2:
+	$UCMP	0,r3,r5			#h>=d?
+	blt	Lppcasm_div3		#goto Lppcasm_div3 if not
+	subf	r3,r5,r3		#h-=d ; 
+Lppcasm_div3:				#r7 = BN_BITS2-i. so r7=i
+	cmpi	0,0,r7,0		# is (i == 0)?
+	beq	Lppcasm_div4
+	$SHL	r3,r3,r7		# h = (h<< i)
+	$SHR	r8,r4,r8		# r8 = (l >> BN_BITS2 -i)
+	$SHL	r5,r5,r7		# d<<=i
+	or	r3,r3,r8		# h = (h<>(BN_BITS2-i))
+	$SHL	r4,r4,r7		# l <<=i
+Lppcasm_div4:
+	$SHRI	r9,r5,`$BITS/2`		# r9 = dh
+					# dl will be computed when needed
+					# as it saves registers.
+	li	r6,2			#r6=2
+	mtctr	r6			#counter will be in count.
+Lppcasm_divouterloop: 
+	$SHRI	r8,r3,`$BITS/2`		#r8 = (h>>BN_BITS4)
+	$SHRI	r11,r4,`$BITS/2`	#r11= (l&BN_MASK2h)>>BN_BITS4
+					# compute here for innerloop.
+	$UCMP	0,r8,r9			# is (h>>BN_BITS4)==dh
+	bne	Lppcasm_div5		# goto Lppcasm_div5 if not
+
+	li	r8,-1
+	$CLRU	r8,r8,`$BITS/2`		#q = BN_MASK2l 
+	b	Lppcasm_div6
+Lppcasm_div5:
+	$UDIV	r8,r3,r9		#q = h/dh
+Lppcasm_div6:
+	$UMULL	r12,r9,r8		#th = q*dh
+	$CLRU	r10,r5,`$BITS/2`	#r10=dl
+	$UMULL	r6,r8,r10		#tl = q*dl
+	
+Lppcasm_divinnerloop:
+	subf	r10,r12,r3		#t = h -th
+	$SHRI	r7,r10,`$BITS/2`	#r7= (t &BN_MASK2H), sort of...
+	addic.	r7,r7,0			#test if r7 == 0. used below.
+					# now want to compute
+					# r7 = (t<>BN_BITS4)
+					# the following 2 instructions do that
+	$SHLI	r7,r10,`$BITS/2`	# r7 = (t<>BN_BITS4)
+	$UCMP	cr1,r6,r7		# compare (tl <= r7)
+	bne	Lppcasm_divinnerexit
+	ble	cr1,Lppcasm_divinnerexit
+	addi	r8,r8,-1		#q--
+	subf	r12,r9,r12		#th -=dh
+	$CLRU	r10,r5,`$BITS/2`	#r10=dl. t is no longer needed in loop.
+	subf	r6,r10,r6		#tl -=dl
+	b	Lppcasm_divinnerloop
+Lppcasm_divinnerexit:
+	$SHRI	r10,r6,`$BITS/2`	#t=(tl>>BN_BITS4)
+	$SHLI	r11,r6,`$BITS/2`	#tl=(tl<=tl) goto Lppcasm_div7
+	addi	r12,r12,1		# th++
+Lppcasm_div7:
+	subf	r11,r11,r4		#r11=l-tl
+	$UCMP	cr1,r3,r12		#compare h and th
+	bge	cr1,Lppcasm_div8	#if (h>=th) goto Lppcasm_div8
+	addi	r8,r8,-1		# q--
+	add	r3,r5,r3		# h+=d
+Lppcasm_div8:
+	subf	r12,r12,r3		#r12 = h-th
+	$SHLI	r4,r11,`$BITS/2`	#l=(l&BN_MASK2l)<>BN_BITS4))&BN_MASK2
+					# the following 2 instructions will do this.
+	$INSR	r11,r12,`$BITS/2`,`$BITS/2`	# r11 is the value we want rotated $BITS/2.
+	$ROTL	r3,r11,`$BITS/2`	# rotate by $BITS/2 and store in r3
+	bdz	Lppcasm_div9		#if (count==0) break ;
+	$SHLI	r0,r8,`$BITS/2`		#ret =q<> 2
+	beq	Lppcasm_mw_REM
+	mtctr	r7
+Lppcasm_mw_LOOP:	
+					#mul(rp[0],ap[0],w,c1);
+	$LD	r8,`0*$BNSZ`(r4)
+	$UMULL	r9,r6,r8
+	$UMULH  r10,r6,r8
+	addc	r9,r9,r12
+	#addze	r10,r10			#carry is NOT ignored.
+					#will be taken care of
+					#in second spin below
+					#using adde.
+	$ST	r9,`0*$BNSZ`(r3)
+					#mul(rp[1],ap[1],w,c1);
+	$LD	r8,`1*$BNSZ`(r4)	
+	$UMULL	r11,r6,r8
+	$UMULH  r12,r6,r8
+	adde	r11,r11,r10
+	#addze	r12,r12
+	$ST	r11,`1*$BNSZ`(r3)
+					#mul(rp[2],ap[2],w,c1);
+	$LD	r8,`2*$BNSZ`(r4)
+	$UMULL	r9,r6,r8
+	$UMULH  r10,r6,r8
+	adde	r9,r9,r12
+	#addze	r10,r10
+	$ST	r9,`2*$BNSZ`(r3)
+					#mul_add(rp[3],ap[3],w,c1);
+	$LD	r8,`3*$BNSZ`(r4)
+	$UMULL	r11,r6,r8
+	$UMULH  r12,r6,r8
+	adde	r11,r11,r10
+	addze	r12,r12			#this spin we collect carry into
+					#r12
+	$ST	r11,`3*$BNSZ`(r3)
+	
+	addi	r3,r3,`4*$BNSZ`
+	addi	r4,r4,`4*$BNSZ`
+	bdnz	Lppcasm_mw_LOOP
+
+Lppcasm_mw_REM:
+	andi.	r5,r5,0x3
+	beq	Lppcasm_mw_OVER
+					#mul(rp[0],ap[0],w,c1);
+	$LD	r8,`0*$BNSZ`(r4)
+	$UMULL	r9,r6,r8
+	$UMULH  r10,r6,r8
+	addc	r9,r9,r12
+	addze	r10,r10
+	$ST	r9,`0*$BNSZ`(r3)
+	addi	r12,r10,0
+	
+	addi	r5,r5,-1
+	cmpli	0,0,r5,0
+	beq	Lppcasm_mw_OVER
+
+	
+					#mul(rp[1],ap[1],w,c1);
+	$LD	r8,`1*$BNSZ`(r4)	
+	$UMULL	r9,r6,r8
+	$UMULH  r10,r6,r8
+	addc	r9,r9,r12
+	addze	r10,r10
+	$ST	r9,`1*$BNSZ`(r3)
+	addi	r12,r10,0
+	
+	addi	r5,r5,-1
+	cmpli	0,0,r5,0
+	beq	Lppcasm_mw_OVER
+	
+					#mul_add(rp[2],ap[2],w,c1);
+	$LD	r8,`2*$BNSZ`(r4)
+	$UMULL	r9,r6,r8
+	$UMULH  r10,r6,r8
+	addc	r9,r9,r12
+	addze	r10,r10
+	$ST	r9,`2*$BNSZ`(r3)
+	addi	r12,r10,0
+		
+Lppcasm_mw_OVER:	
+	addi	r3,r12,0
+	blr
+	.long	0
+	.byte	0,12,0x14,0,0,0,4,0
+	.long	0
+.size	bn_mul_words,.-bn_mul_words
+
+#
+#	NOTE:	The following label name should be changed to
+#		"bn_mul_add_words" i.e. remove the first dot
+#		for the gcc compiler. This should be automatically
+#		done in the build
+#
+
+.align	4
+.bn_mul_add_words:
+#
+# BN_ULONG bn_mul_add_words(BN_ULONG *rp, BN_ULONG *ap, int num, BN_ULONG w)
+#
+# r3 = rp
+# r4 = ap
+# r5 = num
+# r6 = w
+#
+# empirical evidence suggests that unrolled version performs best!!
+#
+	xor	r0,r0,r0		#r0 = 0
+	xor	r12,r12,r12  		#r12 = 0 . used for carry		
+	rlwinm.	r7,r5,30,2,31		# num >> 2
+	beq	Lppcasm_maw_leftover	# if (num < 4) go LPPCASM_maw_leftover
+	mtctr	r7
+Lppcasm_maw_mainloop:	
+					#mul_add(rp[0],ap[0],w,c1);
+	$LD	r8,`0*$BNSZ`(r4)
+	$LD	r11,`0*$BNSZ`(r3)
+	$UMULL	r9,r6,r8
+	$UMULH  r10,r6,r8
+	addc	r9,r9,r12		#r12 is carry.
+	addze	r10,r10
+	addc	r9,r9,r11
+	#addze	r10,r10
+					#the above instruction addze
+					#is NOT needed. Carry will NOT
+					#be ignored. It's not affected
+					#by multiply and will be collected
+					#in the next spin
+	$ST	r9,`0*$BNSZ`(r3)
+	
+					#mul_add(rp[1],ap[1],w,c1);
+	$LD	r8,`1*$BNSZ`(r4)	
+	$LD	r9,`1*$BNSZ`(r3)
+	$UMULL	r11,r6,r8
+	$UMULH  r12,r6,r8
+	adde	r11,r11,r10		#r10 is carry.
+	addze	r12,r12
+	addc	r11,r11,r9
+	#addze	r12,r12
+	$ST	r11,`1*$BNSZ`(r3)
+	
+					#mul_add(rp[2],ap[2],w,c1);
+	$LD	r8,`2*$BNSZ`(r4)
+	$UMULL	r9,r6,r8
+	$LD	r11,`2*$BNSZ`(r3)
+	$UMULH  r10,r6,r8
+	adde	r9,r9,r12
+	addze	r10,r10
+	addc	r9,r9,r11
+	#addze	r10,r10
+	$ST	r9,`2*$BNSZ`(r3)
+	
+					#mul_add(rp[3],ap[3],w,c1);
+	$LD	r8,`3*$BNSZ`(r4)
+	$UMULL	r11,r6,r8
+	$LD	r9,`3*$BNSZ`(r3)
+	$UMULH  r12,r6,r8
+	adde	r11,r11,r10
+	addze	r12,r12
+	addc	r11,r11,r9
+	addze	r12,r12
+	$ST	r11,`3*$BNSZ`(r3)
+	addi	r3,r3,`4*$BNSZ`
+	addi	r4,r4,`4*$BNSZ`
+	bdnz	Lppcasm_maw_mainloop
+	
+Lppcasm_maw_leftover:
+	andi.	r5,r5,0x3
+	beq	Lppcasm_maw_adios
+	addi	r3,r3,-$BNSZ
+	addi	r4,r4,-$BNSZ
+					#mul_add(rp[0],ap[0],w,c1);
+	mtctr	r5
+	$LDU	r8,$BNSZ(r4)
+	$UMULL	r9,r6,r8
+	$UMULH  r10,r6,r8
+	$LDU	r11,$BNSZ(r3)
+	addc	r9,r9,r11
+	addze	r10,r10
+	addc	r9,r9,r12
+	addze	r12,r10
+	$ST	r9,0(r3)
+	
+	bdz	Lppcasm_maw_adios
+					#mul_add(rp[1],ap[1],w,c1);
+	$LDU	r8,$BNSZ(r4)	
+	$UMULL	r9,r6,r8
+	$UMULH  r10,r6,r8
+	$LDU	r11,$BNSZ(r3)
+	addc	r9,r9,r11
+	addze	r10,r10
+	addc	r9,r9,r12
+	addze	r12,r10
+	$ST	r9,0(r3)
+	
+	bdz	Lppcasm_maw_adios
+					#mul_add(rp[2],ap[2],w,c1);
+	$LDU	r8,$BNSZ(r4)
+	$UMULL	r9,r6,r8
+	$UMULH  r10,r6,r8
+	$LDU	r11,$BNSZ(r3)
+	addc	r9,r9,r11
+	addze	r10,r10
+	addc	r9,r9,r12
+	addze	r12,r10
+	$ST	r9,0(r3)
+		
+Lppcasm_maw_adios:	
+	addi	r3,r12,0
+	blr
+	.long	0
+	.byte	0,12,0x14,0,0,0,4,0
+	.long	0
+.size	.bn_mul_add_words,.-.bn_mul_add_words
+	.align	4
+EOF
+$data =~ s/\`([^\`]*)\`/eval $1/gem;
+print $data;
+close STDOUT;
diff --git a/crypto/bn/asm/ppc64-mont.pl b/crypto/bn/asm/ppc64-mont.pl
new file mode 100644
index 000000000..1e19c958a
--- /dev/null
+++ b/crypto/bn/asm/ppc64-mont.pl
@@ -0,0 +1,1635 @@
+#! /usr/bin/env perl
+# Copyright 2007-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+
+# ====================================================================
+# Written by Andy Polyakov  for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+
+# December 2007
+
+# The reason for undertaken effort is basically following. Even though
+# Power 6 CPU operates at incredible 4.7GHz clock frequency, its PKI
+# performance was observed to be less than impressive, essentially as
+# fast as 1.8GHz PPC970, or 2.6 times(!) slower than one would hope.
+# Well, it's not surprising that IBM had to make some sacrifices to
+# boost the clock frequency that much, but no overall improvement?
+# Having observed how much difference did switching to FPU make on
+# UltraSPARC, playing same stunt on Power 6 appeared appropriate...
+# Unfortunately the resulting performance improvement is not as
+# impressive, ~30%, and in absolute terms is still very far from what
+# one would expect from 4.7GHz CPU. There is a chance that I'm doing
+# something wrong, but in the lack of assembler level micro-profiling
+# data or at least decent platform guide I can't tell... Or better
+# results might be achieved with VMX... Anyway, this module provides
+# *worse* performance on other PowerPC implementations, ~40-15% slower
+# on PPC970 depending on key length and ~40% slower on Power 5 for all
+# key lengths. As it's obviously inappropriate as "best all-round"
+# alternative, it has to be complemented with run-time CPU family
+# detection. Oh! It should also be noted that unlike other PowerPC
+# implementation IALU ppc-mont.pl module performs *suboptimaly* on
+# >=1024-bit key lengths on Power 6. It should also be noted that
+# *everything* said so far applies to 64-bit builds! As far as 32-bit
+# application executed on 64-bit CPU goes, this module is likely to
+# become preferred choice, because it's easy to adapt it for such
+# case and *is* faster than 32-bit ppc-mont.pl on *all* processors.
+
+# February 2008
+
+# Micro-profiling assisted optimization results in ~15% improvement
+# over original ppc64-mont.pl version, or overall ~50% improvement
+# over ppc.pl module on Power 6. If compared to ppc-mont.pl on same
+# Power 6 CPU, this module is 5-150% faster depending on key length,
+# [hereafter] more for longer keys. But if compared to ppc-mont.pl
+# on 1.8GHz PPC970, it's only 5-55% faster. Still far from impressive
+# in absolute terms, but it's apparently the way Power 6 is...
+
+# December 2009
+
+# Adapted for 32-bit build this module delivers 25-120%, yes, more
+# than *twice* for longer keys, performance improvement over 32-bit
+# ppc-mont.pl on 1.8GHz PPC970. However! This implementation utilizes
+# even 64-bit integer operations and the trouble is that most PPC
+# operating systems don't preserve upper halves of general purpose
+# registers upon 32-bit signal delivery. They do preserve them upon
+# context switch, but not signalling:-( This means that asynchronous
+# signals have to be blocked upon entry to this subroutine. Signal
+# masking (and of course complementary unmasking) has quite an impact
+# on performance, naturally larger for shorter keys. It's so severe
+# that 512-bit key performance can be as low as 1/3 of expected one.
+# This is why this routine can be engaged for longer key operations
+# only on these OSes, see crypto/ppccap.c for further details. MacOS X
+# is an exception from this and doesn't require signal masking, and
+# that's where above improvement coefficients were collected. For
+# others alternative would be to break dependence on upper halves of
+# GPRs by sticking to 32-bit integer operations...
+
+# December 2012
+
+# Remove above mentioned dependence on GPRs' upper halves in 32-bit
+# build. No signal masking overhead, but integer instructions are
+# *more* numerous... It's still "universally" faster than 32-bit
+# ppc-mont.pl, but improvement coefficient is not as impressive
+# for longer keys...
+
+$flavour = shift;
+
+if ($flavour =~ /32/) {
+	$SIZE_T=4;
+	$RZONE=	224;
+	$fname=	"bn_mul_mont_fpu64";
+
+	$STUX=	"stwux";	# store indexed and update
+	$PUSH=	"stw";
+	$POP=	"lwz";
+} elsif ($flavour =~ /64/) {
+	$SIZE_T=8;
+	$RZONE=	288;
+	$fname=	"bn_mul_mont_fpu64";
+
+	# same as above, but 64-bit mnemonics...
+	$STUX=	"stdux";	# store indexed and update
+	$PUSH=	"std";
+	$POP=	"ld";
+} else { die "nonsense $flavour"; }
+
+$LITTLE_ENDIAN = ($flavour=~/le$/) ? 4 : 0;
+
+$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+( $xlate="${dir}ppc-xlate.pl" and -f $xlate ) or
+( $xlate="${dir}../../perlasm/ppc-xlate.pl" and -f $xlate) or
+die "can't locate ppc-xlate.pl";
+
+open STDOUT,"| $^X $xlate $flavour ".shift || die "can't call $xlate: $!";
+
+$FRAME=64;	# padded frame header
+$TRANSFER=16*8;
+
+$carry="r0";
+$sp="r1";
+$toc="r2";
+$rp="r3";	$ovf="r3";
+$ap="r4";
+$bp="r5";
+$np="r6";
+$n0="r7";
+$num="r8";
+$rp="r9";	# $rp is reassigned
+$tp="r10";
+$j="r11";
+$i="r12";
+# non-volatile registers
+$c1="r19";
+$n1="r20";
+$a1="r21";
+$nap_d="r22";	# interleaved ap and np in double format
+$a0="r23";	# ap[0]
+$t0="r24";	# temporary registers
+$t1="r25";
+$t2="r26";
+$t3="r27";
+$t4="r28";
+$t5="r29";
+$t6="r30";
+$t7="r31";
+
+# PPC offers enough register bank capacity to unroll inner loops twice
+#
+#     ..A3A2A1A0
+#           dcba
+#    -----------
+#            A0a
+#           A0b
+#          A0c
+#         A0d
+#          A1a
+#         A1b
+#        A1c
+#       A1d
+#        A2a
+#       A2b
+#      A2c
+#     A2d
+#      A3a
+#     A3b
+#    A3c
+#   A3d
+#    ..a
+#   ..b
+#
+$ba="f0";	$bb="f1";	$bc="f2";	$bd="f3";
+$na="f4";	$nb="f5";	$nc="f6";	$nd="f7";
+$dota="f8";	$dotb="f9";
+$A0="f10";	$A1="f11";	$A2="f12";	$A3="f13";
+$N0="f20";	$N1="f21";	$N2="f22";	$N3="f23";
+$T0a="f24";	$T0b="f25";
+$T1a="f26";	$T1b="f27";
+$T2a="f28";	$T2b="f29";
+$T3a="f30";	$T3b="f31";
+
+# sp----------->+-------------------------------+
+#		| saved sp			|
+#		+-------------------------------+
+#		.				.
+#   +64		+-------------------------------+
+#		| 16 gpr<->fpr transfer zone	|
+#		.				.
+#		.				.
+#   +16*8	+-------------------------------+
+#		| __int64 tmp[-1]		|
+#		+-------------------------------+
+#		| __int64 tmp[num]		|
+#		.				.
+#		.				.
+#		.				.
+#   +(num+1)*8	+-------------------------------+
+#		| padding to 64 byte boundary	|
+#		.				.
+#   +X		+-------------------------------+
+#		| double nap_d[4*num]		|
+#		.				.
+#		.				.
+#		.				.
+#		+-------------------------------+
+#		.				.
+#   -13*size_t	+-------------------------------+
+#		| 13 saved gpr, r19-r31		|
+#		.				.
+#		.				.
+#   -12*8	+-------------------------------+
+#		| 12 saved fpr, f20-f31		|
+#		.				.
+#		.				.
+#		+-------------------------------+
+
+$code=<<___;
+.machine "any"
+.text
+
+.globl	.$fname
+.align	5
+.$fname:
+	cmpwi	$num,`3*8/$SIZE_T`
+	mr	$rp,r3		; $rp is reassigned
+	li	r3,0		; possible "not handled" return code
+	bltlr-
+	andi.	r0,$num,`16/$SIZE_T-1`		; $num has to be "even"
+	bnelr-
+
+	slwi	$num,$num,`log($SIZE_T)/log(2)`	; num*=sizeof(BN_LONG)
+	li	$i,-4096
+	slwi	$tp,$num,2	; place for {an}p_{lh}[num], i.e. 4*num
+	add	$tp,$tp,$num	; place for tp[num+1]
+	addi	$tp,$tp,`$FRAME+$TRANSFER+8+64+$RZONE`
+	subf	$tp,$tp,$sp	; $sp-$tp
+	and	$tp,$tp,$i	; minimize TLB usage
+	subf	$tp,$sp,$tp	; $tp-$sp
+	mr	$i,$sp
+	$STUX	$sp,$sp,$tp	; alloca
+
+	$PUSH	r19,`-12*8-13*$SIZE_T`($i)
+	$PUSH	r20,`-12*8-12*$SIZE_T`($i)
+	$PUSH	r21,`-12*8-11*$SIZE_T`($i)
+	$PUSH	r22,`-12*8-10*$SIZE_T`($i)
+	$PUSH	r23,`-12*8-9*$SIZE_T`($i)
+	$PUSH	r24,`-12*8-8*$SIZE_T`($i)
+	$PUSH	r25,`-12*8-7*$SIZE_T`($i)
+	$PUSH	r26,`-12*8-6*$SIZE_T`($i)
+	$PUSH	r27,`-12*8-5*$SIZE_T`($i)
+	$PUSH	r28,`-12*8-4*$SIZE_T`($i)
+	$PUSH	r29,`-12*8-3*$SIZE_T`($i)
+	$PUSH	r30,`-12*8-2*$SIZE_T`($i)
+	$PUSH	r31,`-12*8-1*$SIZE_T`($i)
+	stfd	f20,`-12*8`($i)
+	stfd	f21,`-11*8`($i)
+	stfd	f22,`-10*8`($i)
+	stfd	f23,`-9*8`($i)
+	stfd	f24,`-8*8`($i)
+	stfd	f25,`-7*8`($i)
+	stfd	f26,`-6*8`($i)
+	stfd	f27,`-5*8`($i)
+	stfd	f28,`-4*8`($i)
+	stfd	f29,`-3*8`($i)
+	stfd	f30,`-2*8`($i)
+	stfd	f31,`-1*8`($i)
+
+	addi	$tp,$sp,`$FRAME+$TRANSFER+8+64`
+	li	$i,-64
+	add	$nap_d,$tp,$num
+	and	$nap_d,$nap_d,$i	; align to 64 bytes
+	; nap_d is off by 1, because it's used with stfdu/lfdu
+	addi	$nap_d,$nap_d,-8
+	srwi	$j,$num,`3+1`	; counter register, num/2
+	addi	$j,$j,-1
+	addi	$tp,$sp,`$FRAME+$TRANSFER-8`
+	li	$carry,0
+	mtctr	$j
+___
+
+$code.=<<___ if ($SIZE_T==8);
+	ld	$a0,0($ap)		; pull ap[0] value
+	ld	$t3,0($bp)		; bp[0]
+	ld	$n0,0($n0)		; pull n0[0] value
+
+	mulld	$t7,$a0,$t3		; ap[0]*bp[0]
+	; transfer bp[0] to FPU as 4x16-bit values
+	extrdi	$t0,$t3,16,48
+	extrdi	$t1,$t3,16,32
+	extrdi	$t2,$t3,16,16
+	extrdi	$t3,$t3,16,0
+	std	$t0,`$FRAME+0`($sp)
+	std	$t1,`$FRAME+8`($sp)
+	std	$t2,`$FRAME+16`($sp)
+	std	$t3,`$FRAME+24`($sp)
+
+	mulld	$t7,$t7,$n0		; tp[0]*n0
+	; transfer (ap[0]*bp[0])*n0 to FPU as 4x16-bit values
+	extrdi	$t4,$t7,16,48
+	extrdi	$t5,$t7,16,32
+	extrdi	$t6,$t7,16,16
+	extrdi	$t7,$t7,16,0
+	std	$t4,`$FRAME+32`($sp)
+	std	$t5,`$FRAME+40`($sp)
+	std	$t6,`$FRAME+48`($sp)
+	std	$t7,`$FRAME+56`($sp)
+
+	extrdi	$t0,$a0,32,32		; lwz	$t0,4($ap)
+	extrdi	$t1,$a0,32,0		; lwz	$t1,0($ap)
+	lwz	$t2,`12^$LITTLE_ENDIAN`($ap)	; load a[1] as 32-bit word pair
+	lwz	$t3,`8^$LITTLE_ENDIAN`($ap)
+	lwz	$t4,`4^$LITTLE_ENDIAN`($np)	; load n[0] as 32-bit word pair
+	lwz	$t5,`0^$LITTLE_ENDIAN`($np)
+	lwz	$t6,`12^$LITTLE_ENDIAN`($np)	; load n[1] as 32-bit word pair
+	lwz	$t7,`8^$LITTLE_ENDIAN`($np)
+___
+$code.=<<___ if ($SIZE_T==4);
+	lwz	$a0,0($ap)		; pull ap[0,1] value
+	mr	$n1,$n0
+	lwz	$a1,4($ap)
+	li	$c1,0
+	lwz	$t1,0($bp)		; bp[0,1]
+	lwz	$t3,4($bp)
+	lwz	$n0,0($n1)		; pull n0[0,1] value
+	lwz	$n1,4($n1)
+
+	mullw	$t4,$a0,$t1		; mulld ap[0]*bp[0]
+	mulhwu	$t5,$a0,$t1
+	mullw	$t6,$a1,$t1
+	mullw	$t7,$a0,$t3
+	add	$t5,$t5,$t6
+	add	$t5,$t5,$t7
+	; transfer bp[0] to FPU as 4x16-bit values
+	extrwi	$t0,$t1,16,16
+	extrwi	$t1,$t1,16,0
+	extrwi	$t2,$t3,16,16
+	extrwi	$t3,$t3,16,0
+	std	$t0,`$FRAME+0`($sp)	; yes, std in 32-bit build
+	std	$t1,`$FRAME+8`($sp)
+	std	$t2,`$FRAME+16`($sp)
+	std	$t3,`$FRAME+24`($sp)
+
+	mullw	$t0,$t4,$n0		; mulld tp[0]*n0
+	mulhwu	$t1,$t4,$n0
+	mullw	$t2,$t5,$n0
+	mullw	$t3,$t4,$n1
+	add	$t1,$t1,$t2
+	add	$t1,$t1,$t3
+	; transfer (ap[0]*bp[0])*n0 to FPU as 4x16-bit values
+	extrwi	$t4,$t0,16,16
+	extrwi	$t5,$t0,16,0
+	extrwi	$t6,$t1,16,16
+	extrwi	$t7,$t1,16,0
+	std	$t4,`$FRAME+32`($sp)	; yes, std in 32-bit build
+	std	$t5,`$FRAME+40`($sp)
+	std	$t6,`$FRAME+48`($sp)
+	std	$t7,`$FRAME+56`($sp)
+
+	mr	$t0,$a0			; lwz	$t0,0($ap)
+	mr	$t1,$a1			; lwz	$t1,4($ap)
+	lwz	$t2,8($ap)		; load a[j..j+3] as 32-bit word pairs
+	lwz	$t3,12($ap)
+	lwz	$t4,0($np)		; load n[j..j+3] as 32-bit word pairs
+	lwz	$t5,4($np)
+	lwz	$t6,8($np)
+	lwz	$t7,12($np)
+___
+$code.=<<___;
+	lfd	$ba,`$FRAME+0`($sp)
+	lfd	$bb,`$FRAME+8`($sp)
+	lfd	$bc,`$FRAME+16`($sp)
+	lfd	$bd,`$FRAME+24`($sp)
+	lfd	$na,`$FRAME+32`($sp)
+	lfd	$nb,`$FRAME+40`($sp)
+	lfd	$nc,`$FRAME+48`($sp)
+	lfd	$nd,`$FRAME+56`($sp)
+	std	$t0,`$FRAME+64`($sp)	; yes, std even in 32-bit build
+	std	$t1,`$FRAME+72`($sp)
+	std	$t2,`$FRAME+80`($sp)
+	std	$t3,`$FRAME+88`($sp)
+	std	$t4,`$FRAME+96`($sp)
+	std	$t5,`$FRAME+104`($sp)
+	std	$t6,`$FRAME+112`($sp)
+	std	$t7,`$FRAME+120`($sp)
+	fcfid	$ba,$ba
+	fcfid	$bb,$bb
+	fcfid	$bc,$bc
+	fcfid	$bd,$bd
+	fcfid	$na,$na
+	fcfid	$nb,$nb
+	fcfid	$nc,$nc
+	fcfid	$nd,$nd
+
+	lfd	$A0,`$FRAME+64`($sp)
+	lfd	$A1,`$FRAME+72`($sp)
+	lfd	$A2,`$FRAME+80`($sp)
+	lfd	$A3,`$FRAME+88`($sp)
+	lfd	$N0,`$FRAME+96`($sp)
+	lfd	$N1,`$FRAME+104`($sp)
+	lfd	$N2,`$FRAME+112`($sp)
+	lfd	$N3,`$FRAME+120`($sp)
+	fcfid	$A0,$A0
+	fcfid	$A1,$A1
+	fcfid	$A2,$A2
+	fcfid	$A3,$A3
+	fcfid	$N0,$N0
+	fcfid	$N1,$N1
+	fcfid	$N2,$N2
+	fcfid	$N3,$N3
+	addi	$ap,$ap,16
+	addi	$np,$np,16
+
+	fmul	$T1a,$A1,$ba
+	fmul	$T1b,$A1,$bb
+	stfd	$A0,8($nap_d)		; save a[j] in double format
+	stfd	$A1,16($nap_d)
+	fmul	$T2a,$A2,$ba
+	fmul	$T2b,$A2,$bb
+	stfd	$A2,24($nap_d)		; save a[j+1] in double format
+	stfd	$A3,32($nap_d)
+	fmul	$T3a,$A3,$ba
+	fmul	$T3b,$A3,$bb
+	stfd	$N0,40($nap_d)		; save n[j] in double format
+	stfd	$N1,48($nap_d)
+	fmul	$T0a,$A0,$ba
+	fmul	$T0b,$A0,$bb
+	stfd	$N2,56($nap_d)		; save n[j+1] in double format
+	stfdu	$N3,64($nap_d)
+
+	fmadd	$T1a,$A0,$bc,$T1a
+	fmadd	$T1b,$A0,$bd,$T1b
+	fmadd	$T2a,$A1,$bc,$T2a
+	fmadd	$T2b,$A1,$bd,$T2b
+	fmadd	$T3a,$A2,$bc,$T3a
+	fmadd	$T3b,$A2,$bd,$T3b
+	fmul	$dota,$A3,$bc
+	fmul	$dotb,$A3,$bd
+
+	fmadd	$T1a,$N1,$na,$T1a
+	fmadd	$T1b,$N1,$nb,$T1b
+	fmadd	$T2a,$N2,$na,$T2a
+	fmadd	$T2b,$N2,$nb,$T2b
+	fmadd	$T3a,$N3,$na,$T3a
+	fmadd	$T3b,$N3,$nb,$T3b
+	fmadd	$T0a,$N0,$na,$T0a
+	fmadd	$T0b,$N0,$nb,$T0b
+
+	fmadd	$T1a,$N0,$nc,$T1a
+	fmadd	$T1b,$N0,$nd,$T1b
+	fmadd	$T2a,$N1,$nc,$T2a
+	fmadd	$T2b,$N1,$nd,$T2b
+	fmadd	$T3a,$N2,$nc,$T3a
+	fmadd	$T3b,$N2,$nd,$T3b
+	fmadd	$dota,$N3,$nc,$dota
+	fmadd	$dotb,$N3,$nd,$dotb
+
+	fctid	$T0a,$T0a
+	fctid	$T0b,$T0b
+	fctid	$T1a,$T1a
+	fctid	$T1b,$T1b
+	fctid	$T2a,$T2a
+	fctid	$T2b,$T2b
+	fctid	$T3a,$T3a
+	fctid	$T3b,$T3b
+
+	stfd	$T0a,`$FRAME+0`($sp)
+	stfd	$T0b,`$FRAME+8`($sp)
+	stfd	$T1a,`$FRAME+16`($sp)
+	stfd	$T1b,`$FRAME+24`($sp)
+	stfd	$T2a,`$FRAME+32`($sp)
+	stfd	$T2b,`$FRAME+40`($sp)
+	stfd	$T3a,`$FRAME+48`($sp)
+	stfd	$T3b,`$FRAME+56`($sp)
+
+.align	5
+L1st:
+___
+$code.=<<___ if ($SIZE_T==8);
+	lwz	$t0,`4^$LITTLE_ENDIAN`($ap)	; load a[j] as 32-bit word pair
+	lwz	$t1,`0^$LITTLE_ENDIAN`($ap)
+	lwz	$t2,`12^$LITTLE_ENDIAN`($ap)	; load a[j+1] as 32-bit word pair
+	lwz	$t3,`8^$LITTLE_ENDIAN`($ap)
+	lwz	$t4,`4^$LITTLE_ENDIAN`($np)	; load n[j] as 32-bit word pair
+	lwz	$t5,`0^$LITTLE_ENDIAN`($np)
+	lwz	$t6,`12^$LITTLE_ENDIAN`($np)	; load n[j+1] as 32-bit word pair
+	lwz	$t7,`8^$LITTLE_ENDIAN`($np)
+___
+$code.=<<___ if ($SIZE_T==4);
+	lwz	$t0,0($ap)		; load a[j..j+3] as 32-bit word pairs
+	lwz	$t1,4($ap)
+	lwz	$t2,8($ap)
+	lwz	$t3,12($ap)
+	lwz	$t4,0($np)		; load n[j..j+3] as 32-bit word pairs
+	lwz	$t5,4($np)
+	lwz	$t6,8($np)
+	lwz	$t7,12($np)
+___
+$code.=<<___;
+	std	$t0,`$FRAME+64`($sp)	; yes, std even in 32-bit build
+	std	$t1,`$FRAME+72`($sp)
+	std	$t2,`$FRAME+80`($sp)
+	std	$t3,`$FRAME+88`($sp)
+	std	$t4,`$FRAME+96`($sp)
+	std	$t5,`$FRAME+104`($sp)
+	std	$t6,`$FRAME+112`($sp)
+	std	$t7,`$FRAME+120`($sp)
+___
+if ($SIZE_T==8 or $flavour =~ /osx/) {
+$code.=<<___;
+	ld	$t0,`$FRAME+0`($sp)
+	ld	$t1,`$FRAME+8`($sp)
+	ld	$t2,`$FRAME+16`($sp)
+	ld	$t3,`$FRAME+24`($sp)
+	ld	$t4,`$FRAME+32`($sp)
+	ld	$t5,`$FRAME+40`($sp)
+	ld	$t6,`$FRAME+48`($sp)
+	ld	$t7,`$FRAME+56`($sp)
+___
+} else {
+$code.=<<___;
+	lwz	$t1,`$FRAME+0^$LITTLE_ENDIAN`($sp)
+	lwz	$t0,`$FRAME+4^$LITTLE_ENDIAN`($sp)
+	lwz	$t3,`$FRAME+8^$LITTLE_ENDIAN`($sp)
+	lwz	$t2,`$FRAME+12^$LITTLE_ENDIAN`($sp)
+	lwz	$t5,`$FRAME+16^$LITTLE_ENDIAN`($sp)
+	lwz	$t4,`$FRAME+20^$LITTLE_ENDIAN`($sp)
+	lwz	$t7,`$FRAME+24^$LITTLE_ENDIAN`($sp)
+	lwz	$t6,`$FRAME+28^$LITTLE_ENDIAN`($sp)
+___
+}
+$code.=<<___;
+	lfd	$A0,`$FRAME+64`($sp)
+	lfd	$A1,`$FRAME+72`($sp)
+	lfd	$A2,`$FRAME+80`($sp)
+	lfd	$A3,`$FRAME+88`($sp)
+	lfd	$N0,`$FRAME+96`($sp)
+	lfd	$N1,`$FRAME+104`($sp)
+	lfd	$N2,`$FRAME+112`($sp)
+	lfd	$N3,`$FRAME+120`($sp)
+	fcfid	$A0,$A0
+	fcfid	$A1,$A1
+	fcfid	$A2,$A2
+	fcfid	$A3,$A3
+	fcfid	$N0,$N0
+	fcfid	$N1,$N1
+	fcfid	$N2,$N2
+	fcfid	$N3,$N3
+	addi	$ap,$ap,16
+	addi	$np,$np,16
+
+	fmul	$T1a,$A1,$ba
+	fmul	$T1b,$A1,$bb
+	fmul	$T2a,$A2,$ba
+	fmul	$T2b,$A2,$bb
+	stfd	$A0,8($nap_d)		; save a[j] in double format
+	stfd	$A1,16($nap_d)
+	fmul	$T3a,$A3,$ba
+	fmul	$T3b,$A3,$bb
+	fmadd	$T0a,$A0,$ba,$dota
+	fmadd	$T0b,$A0,$bb,$dotb
+	stfd	$A2,24($nap_d)		; save a[j+1] in double format
+	stfd	$A3,32($nap_d)
+___
+if ($SIZE_T==8 or $flavour =~ /osx/) {
+$code.=<<___;
+	fmadd	$T1a,$A0,$bc,$T1a
+	fmadd	$T1b,$A0,$bd,$T1b
+	fmadd	$T2a,$A1,$bc,$T2a
+	fmadd	$T2b,$A1,$bd,$T2b
+	stfd	$N0,40($nap_d)		; save n[j] in double format
+	stfd	$N1,48($nap_d)
+	fmadd	$T3a,$A2,$bc,$T3a
+	fmadd	$T3b,$A2,$bd,$T3b
+	 add	$t0,$t0,$carry		; can not overflow
+	fmul	$dota,$A3,$bc
+	fmul	$dotb,$A3,$bd
+	stfd	$N2,56($nap_d)		; save n[j+1] in double format
+	stfdu	$N3,64($nap_d)
+	 srdi	$carry,$t0,16
+	 add	$t1,$t1,$carry
+	 srdi	$carry,$t1,16
+
+	fmadd	$T1a,$N1,$na,$T1a
+	fmadd	$T1b,$N1,$nb,$T1b
+	 insrdi	$t0,$t1,16,32
+	fmadd	$T2a,$N2,$na,$T2a
+	fmadd	$T2b,$N2,$nb,$T2b
+	 add	$t2,$t2,$carry
+	fmadd	$T3a,$N3,$na,$T3a
+	fmadd	$T3b,$N3,$nb,$T3b
+	 srdi	$carry,$t2,16
+	fmadd	$T0a,$N0,$na,$T0a
+	fmadd	$T0b,$N0,$nb,$T0b
+	 insrdi	$t0,$t2,16,16
+	 add	$t3,$t3,$carry
+	 srdi	$carry,$t3,16
+
+	fmadd	$T1a,$N0,$nc,$T1a
+	fmadd	$T1b,$N0,$nd,$T1b
+	 insrdi	$t0,$t3,16,0		; 0..63 bits
+	fmadd	$T2a,$N1,$nc,$T2a
+	fmadd	$T2b,$N1,$nd,$T2b
+	 add	$t4,$t4,$carry
+	fmadd	$T3a,$N2,$nc,$T3a
+	fmadd	$T3b,$N2,$nd,$T3b
+	 srdi	$carry,$t4,16
+	fmadd	$dota,$N3,$nc,$dota
+	fmadd	$dotb,$N3,$nd,$dotb
+	 add	$t5,$t5,$carry
+	 srdi	$carry,$t5,16
+	 insrdi	$t4,$t5,16,32
+
+	fctid	$T0a,$T0a
+	fctid	$T0b,$T0b
+	 add	$t6,$t6,$carry
+	fctid	$T1a,$T1a
+	fctid	$T1b,$T1b
+	 srdi	$carry,$t6,16
+	fctid	$T2a,$T2a
+	fctid	$T2b,$T2b
+	 insrdi	$t4,$t6,16,16
+	fctid	$T3a,$T3a
+	fctid	$T3b,$T3b
+	 add	$t7,$t7,$carry
+	 insrdi	$t4,$t7,16,0		; 64..127 bits
+	 srdi	$carry,$t7,16		; upper 33 bits
+
+	stfd	$T0a,`$FRAME+0`($sp)
+	stfd	$T0b,`$FRAME+8`($sp)
+	stfd	$T1a,`$FRAME+16`($sp)
+	stfd	$T1b,`$FRAME+24`($sp)
+	stfd	$T2a,`$FRAME+32`($sp)
+	stfd	$T2b,`$FRAME+40`($sp)
+	stfd	$T3a,`$FRAME+48`($sp)
+	stfd	$T3b,`$FRAME+56`($sp)
+	 std	$t0,8($tp)		; tp[j-1]
+	 stdu	$t4,16($tp)		; tp[j]
+___
+} else {
+$code.=<<___;
+	fmadd	$T1a,$A0,$bc,$T1a
+	fmadd	$T1b,$A0,$bd,$T1b
+	 addc	$t0,$t0,$carry
+	 adde	$t1,$t1,$c1
+	 srwi	$carry,$t0,16
+	fmadd	$T2a,$A1,$bc,$T2a
+	fmadd	$T2b,$A1,$bd,$T2b
+	stfd	$N0,40($nap_d)		; save n[j] in double format
+	stfd	$N1,48($nap_d)
+	 srwi	$c1,$t1,16
+	 insrwi	$carry,$t1,16,0
+	fmadd	$T3a,$A2,$bc,$T3a
+	fmadd	$T3b,$A2,$bd,$T3b
+	 addc	$t2,$t2,$carry
+	 adde	$t3,$t3,$c1
+	 srwi	$carry,$t2,16
+	fmul	$dota,$A3,$bc
+	fmul	$dotb,$A3,$bd
+	stfd	$N2,56($nap_d)		; save n[j+1] in double format
+	stfdu	$N3,64($nap_d)
+	 insrwi	$t0,$t2,16,0		; 0..31 bits
+	 srwi	$c1,$t3,16
+	 insrwi	$carry,$t3,16,0
+
+	fmadd	$T1a,$N1,$na,$T1a
+	fmadd	$T1b,$N1,$nb,$T1b
+	 lwz	$t3,`$FRAME+32^$LITTLE_ENDIAN`($sp)	; permuted $t1
+	 lwz	$t2,`$FRAME+36^$LITTLE_ENDIAN`($sp)	; permuted $t0
+	 addc	$t4,$t4,$carry
+	 adde	$t5,$t5,$c1
+	 srwi	$carry,$t4,16
+	fmadd	$T2a,$N2,$na,$T2a
+	fmadd	$T2b,$N2,$nb,$T2b
+	 srwi	$c1,$t5,16
+	 insrwi	$carry,$t5,16,0
+	fmadd	$T3a,$N3,$na,$T3a
+	fmadd	$T3b,$N3,$nb,$T3b
+	 addc	$t6,$t6,$carry
+	 adde	$t7,$t7,$c1
+	 srwi	$carry,$t6,16
+	fmadd	$T0a,$N0,$na,$T0a
+	fmadd	$T0b,$N0,$nb,$T0b
+	 insrwi	$t4,$t6,16,0		; 32..63 bits
+	 srwi	$c1,$t7,16
+	 insrwi	$carry,$t7,16,0
+
+	fmadd	$T1a,$N0,$nc,$T1a
+	fmadd	$T1b,$N0,$nd,$T1b
+	 lwz	$t7,`$FRAME+40^$LITTLE_ENDIAN`($sp)	; permuted $t3
+	 lwz	$t6,`$FRAME+44^$LITTLE_ENDIAN`($sp)	; permuted $t2
+	 addc	$t2,$t2,$carry
+	 adde	$t3,$t3,$c1
+	 srwi	$carry,$t2,16
+	fmadd	$T2a,$N1,$nc,$T2a
+	fmadd	$T2b,$N1,$nd,$T2b
+	 stw	$t0,12($tp)		; tp[j-1]
+	 stw	$t4,8($tp)
+	 srwi	$c1,$t3,16
+	 insrwi	$carry,$t3,16,0
+	fmadd	$T3a,$N2,$nc,$T3a
+	fmadd	$T3b,$N2,$nd,$T3b
+	 lwz	$t1,`$FRAME+48^$LITTLE_ENDIAN`($sp)	; permuted $t5
+	 lwz	$t0,`$FRAME+52^$LITTLE_ENDIAN`($sp)	; permuted $t4
+	 addc	$t6,$t6,$carry
+	 adde	$t7,$t7,$c1
+	 srwi	$carry,$t6,16
+	fmadd	$dota,$N3,$nc,$dota
+	fmadd	$dotb,$N3,$nd,$dotb
+	 insrwi	$t2,$t6,16,0		; 64..95 bits
+	 srwi	$c1,$t7,16
+	 insrwi	$carry,$t7,16,0
+
+	fctid	$T0a,$T0a
+	fctid	$T0b,$T0b
+	 lwz	$t5,`$FRAME+56^$LITTLE_ENDIAN`($sp)	; permuted $t7
+	 lwz	$t4,`$FRAME+60^$LITTLE_ENDIAN`($sp)	; permuted $t6
+	 addc	$t0,$t0,$carry
+	 adde	$t1,$t1,$c1
+	 srwi	$carry,$t0,16
+	fctid	$T1a,$T1a
+	fctid	$T1b,$T1b
+	 srwi	$c1,$t1,16
+	 insrwi	$carry,$t1,16,0
+	fctid	$T2a,$T2a
+	fctid	$T2b,$T2b
+	 addc	$t4,$t4,$carry
+	 adde	$t5,$t5,$c1
+	 srwi	$carry,$t4,16
+	fctid	$T3a,$T3a
+	fctid	$T3b,$T3b
+	 insrwi	$t0,$t4,16,0		; 96..127 bits
+	 srwi	$c1,$t5,16
+	 insrwi	$carry,$t5,16,0
+
+	stfd	$T0a,`$FRAME+0`($sp)
+	stfd	$T0b,`$FRAME+8`($sp)
+	stfd	$T1a,`$FRAME+16`($sp)
+	stfd	$T1b,`$FRAME+24`($sp)
+	stfd	$T2a,`$FRAME+32`($sp)
+	stfd	$T2b,`$FRAME+40`($sp)
+	stfd	$T3a,`$FRAME+48`($sp)
+	stfd	$T3b,`$FRAME+56`($sp)
+	 stw	$t2,20($tp)		; tp[j]
+	 stwu	$t0,16($tp)
+___
+}
+$code.=<<___;
+	bdnz	L1st
+
+	fctid	$dota,$dota
+	fctid	$dotb,$dotb
+___
+if ($SIZE_T==8 or $flavour =~ /osx/) {
+$code.=<<___;
+	ld	$t0,`$FRAME+0`($sp)
+	ld	$t1,`$FRAME+8`($sp)
+	ld	$t2,`$FRAME+16`($sp)
+	ld	$t3,`$FRAME+24`($sp)
+	ld	$t4,`$FRAME+32`($sp)
+	ld	$t5,`$FRAME+40`($sp)
+	ld	$t6,`$FRAME+48`($sp)
+	ld	$t7,`$FRAME+56`($sp)
+	stfd	$dota,`$FRAME+64`($sp)
+	stfd	$dotb,`$FRAME+72`($sp)
+
+	add	$t0,$t0,$carry		; can not overflow
+	srdi	$carry,$t0,16
+	add	$t1,$t1,$carry
+	srdi	$carry,$t1,16
+	insrdi	$t0,$t1,16,32
+	add	$t2,$t2,$carry
+	srdi	$carry,$t2,16
+	insrdi	$t0,$t2,16,16
+	add	$t3,$t3,$carry
+	srdi	$carry,$t3,16
+	insrdi	$t0,$t3,16,0		; 0..63 bits
+	add	$t4,$t4,$carry
+	srdi	$carry,$t4,16
+	add	$t5,$t5,$carry
+	srdi	$carry,$t5,16
+	insrdi	$t4,$t5,16,32
+	add	$t6,$t6,$carry
+	srdi	$carry,$t6,16
+	insrdi	$t4,$t6,16,16
+	add	$t7,$t7,$carry
+	insrdi	$t4,$t7,16,0		; 64..127 bits
+	srdi	$carry,$t7,16		; upper 33 bits
+	ld	$t6,`$FRAME+64`($sp)
+	ld	$t7,`$FRAME+72`($sp)
+
+	std	$t0,8($tp)		; tp[j-1]
+	stdu	$t4,16($tp)		; tp[j]
+
+	add	$t6,$t6,$carry		; can not overflow
+	srdi	$carry,$t6,16
+	add	$t7,$t7,$carry
+	insrdi	$t6,$t7,48,0
+	srdi	$ovf,$t7,48
+	std	$t6,8($tp)		; tp[num-1]
+___
+} else {
+$code.=<<___;
+	lwz	$t1,`$FRAME+0^$LITTLE_ENDIAN`($sp)
+	lwz	$t0,`$FRAME+4^$LITTLE_ENDIAN`($sp)
+	lwz	$t3,`$FRAME+8^$LITTLE_ENDIAN`($sp)
+	lwz	$t2,`$FRAME+12^$LITTLE_ENDIAN`($sp)
+	lwz	$t5,`$FRAME+16^$LITTLE_ENDIAN`($sp)
+	lwz	$t4,`$FRAME+20^$LITTLE_ENDIAN`($sp)
+	lwz	$t7,`$FRAME+24^$LITTLE_ENDIAN`($sp)
+	lwz	$t6,`$FRAME+28^$LITTLE_ENDIAN`($sp)
+	stfd	$dota,`$FRAME+64`($sp)
+	stfd	$dotb,`$FRAME+72`($sp)
+
+	addc	$t0,$t0,$carry
+	adde	$t1,$t1,$c1
+	srwi	$carry,$t0,16
+	insrwi	$carry,$t1,16,0
+	srwi	$c1,$t1,16
+	addc	$t2,$t2,$carry
+	adde	$t3,$t3,$c1
+	srwi	$carry,$t2,16
+	 insrwi	$t0,$t2,16,0		; 0..31 bits
+	insrwi	$carry,$t3,16,0
+	srwi	$c1,$t3,16
+	addc	$t4,$t4,$carry
+	adde	$t5,$t5,$c1
+	srwi	$carry,$t4,16
+	insrwi	$carry,$t5,16,0
+	srwi	$c1,$t5,16
+	addc	$t6,$t6,$carry
+	adde	$t7,$t7,$c1
+	srwi	$carry,$t6,16
+	 insrwi	$t4,$t6,16,0		; 32..63 bits
+	insrwi	$carry,$t7,16,0
+	srwi	$c1,$t7,16
+	 stw	$t0,12($tp)		; tp[j-1]
+	 stw	$t4,8($tp)
+
+	lwz	$t3,`$FRAME+32^$LITTLE_ENDIAN`($sp)	; permuted $t1
+	lwz	$t2,`$FRAME+36^$LITTLE_ENDIAN`($sp)	; permuted $t0
+	lwz	$t7,`$FRAME+40^$LITTLE_ENDIAN`($sp)	; permuted $t3
+	lwz	$t6,`$FRAME+44^$LITTLE_ENDIAN`($sp)	; permuted $t2
+	lwz	$t1,`$FRAME+48^$LITTLE_ENDIAN`($sp)	; permuted $t5
+	lwz	$t0,`$FRAME+52^$LITTLE_ENDIAN`($sp)	; permuted $t4
+	lwz	$t5,`$FRAME+56^$LITTLE_ENDIAN`($sp)	; permuted $t7
+	lwz	$t4,`$FRAME+60^$LITTLE_ENDIAN`($sp)	; permuted $t6
+
+	addc	$t2,$t2,$carry
+	adde	$t3,$t3,$c1
+	srwi	$carry,$t2,16
+	insrwi	$carry,$t3,16,0
+	srwi	$c1,$t3,16
+	addc	$t6,$t6,$carry
+	adde	$t7,$t7,$c1
+	srwi	$carry,$t6,16
+	 insrwi	$t2,$t6,16,0		; 64..95 bits
+	insrwi	$carry,$t7,16,0
+	srwi	$c1,$t7,16
+	addc	$t0,$t0,$carry
+	adde	$t1,$t1,$c1
+	srwi	$carry,$t0,16
+	insrwi	$carry,$t1,16,0
+	srwi	$c1,$t1,16
+	addc	$t4,$t4,$carry
+	adde	$t5,$t5,$c1
+	srwi	$carry,$t4,16
+	 insrwi	$t0,$t4,16,0		; 96..127 bits
+	insrwi	$carry,$t5,16,0
+	srwi	$c1,$t5,16
+	 stw	$t2,20($tp)		; tp[j]
+	 stwu	$t0,16($tp)
+
+	lwz	$t7,`$FRAME+64^$LITTLE_ENDIAN`($sp)
+	lwz	$t6,`$FRAME+68^$LITTLE_ENDIAN`($sp)
+	lwz	$t5,`$FRAME+72^$LITTLE_ENDIAN`($sp)
+	lwz	$t4,`$FRAME+76^$LITTLE_ENDIAN`($sp)
+
+	addc	$t6,$t6,$carry
+	adde	$t7,$t7,$c1
+	srwi	$carry,$t6,16
+	insrwi	$carry,$t7,16,0
+	srwi	$c1,$t7,16
+	addc	$t4,$t4,$carry
+	adde	$t5,$t5,$c1
+
+	insrwi	$t6,$t4,16,0
+	srwi	$t4,$t4,16
+	insrwi	$t4,$t5,16,0
+	srwi	$ovf,$t5,16
+	stw	$t6,12($tp)		; tp[num-1]
+	stw	$t4,8($tp)
+___
+}
+$code.=<<___;
+	slwi	$t7,$num,2
+	subf	$nap_d,$t7,$nap_d	; rewind pointer
+
+	li	$i,8			; i=1
+.align	5
+Louter:
+	addi	$tp,$sp,`$FRAME+$TRANSFER`
+	li	$carry,0
+	mtctr	$j
+___
+$code.=<<___ if ($SIZE_T==8);
+	ldx	$t3,$bp,$i		; bp[i]
+
+	ld	$t6,`$FRAME+$TRANSFER+8`($sp)	; tp[0]
+	mulld	$t7,$a0,$t3		; ap[0]*bp[i]
+	add	$t7,$t7,$t6		; ap[0]*bp[i]+tp[0]
+	; transfer bp[i] to FPU as 4x16-bit values
+	extrdi	$t0,$t3,16,48
+	extrdi	$t1,$t3,16,32
+	extrdi	$t2,$t3,16,16
+	extrdi	$t3,$t3,16,0
+	std	$t0,`$FRAME+0`($sp)
+	std	$t1,`$FRAME+8`($sp)
+	std	$t2,`$FRAME+16`($sp)
+	std	$t3,`$FRAME+24`($sp)
+
+	mulld	$t7,$t7,$n0		; tp[0]*n0
+	; transfer (ap[0]*bp[i]+tp[0])*n0 to FPU as 4x16-bit values
+	extrdi	$t4,$t7,16,48
+	extrdi	$t5,$t7,16,32
+	extrdi	$t6,$t7,16,16
+	extrdi	$t7,$t7,16,0
+	std	$t4,`$FRAME+32`($sp)
+	std	$t5,`$FRAME+40`($sp)
+	std	$t6,`$FRAME+48`($sp)
+	std	$t7,`$FRAME+56`($sp)
+___
+$code.=<<___ if ($SIZE_T==4);
+	add	$t0,$bp,$i
+	li	$c1,0
+	lwz	$t1,0($t0)		; bp[i,i+1]
+	lwz	$t3,4($t0)
+
+	mullw	$t4,$a0,$t1		; ap[0]*bp[i]
+	lwz	$t0,`$FRAME+$TRANSFER+8+4`($sp)	; tp[0]
+	mulhwu	$t5,$a0,$t1
+	lwz	$t2,`$FRAME+$TRANSFER+8`($sp)	; tp[0]
+	mullw	$t6,$a1,$t1
+	mullw	$t7,$a0,$t3
+	add	$t5,$t5,$t6
+	add	$t5,$t5,$t7
+	addc	$t4,$t4,$t0		; ap[0]*bp[i]+tp[0]
+	adde	$t5,$t5,$t2
+	; transfer bp[i] to FPU as 4x16-bit values
+	extrwi	$t0,$t1,16,16
+	extrwi	$t1,$t1,16,0
+	extrwi	$t2,$t3,16,16
+	extrwi	$t3,$t3,16,0
+	std	$t0,`$FRAME+0`($sp)	; yes, std in 32-bit build
+	std	$t1,`$FRAME+8`($sp)
+	std	$t2,`$FRAME+16`($sp)
+	std	$t3,`$FRAME+24`($sp)
+
+	mullw	$t0,$t4,$n0		; mulld tp[0]*n0
+	mulhwu	$t1,$t4,$n0
+	mullw	$t2,$t5,$n0
+	mullw	$t3,$t4,$n1
+	add	$t1,$t1,$t2
+	add	$t1,$t1,$t3
+	; transfer (ap[0]*bp[i]+tp[0])*n0 to FPU as 4x16-bit values
+	extrwi	$t4,$t0,16,16
+	extrwi	$t5,$t0,16,0
+	extrwi	$t6,$t1,16,16
+	extrwi	$t7,$t1,16,0
+	std	$t4,`$FRAME+32`($sp)	; yes, std in 32-bit build
+	std	$t5,`$FRAME+40`($sp)
+	std	$t6,`$FRAME+48`($sp)
+	std	$t7,`$FRAME+56`($sp)
+___
+$code.=<<___;
+	lfd	$A0,8($nap_d)		; load a[j] in double format
+	lfd	$A1,16($nap_d)
+	lfd	$A2,24($nap_d)		; load a[j+1] in double format
+	lfd	$A3,32($nap_d)
+	lfd	$N0,40($nap_d)		; load n[j] in double format
+	lfd	$N1,48($nap_d)
+	lfd	$N2,56($nap_d)		; load n[j+1] in double format
+	lfdu	$N3,64($nap_d)
+
+	lfd	$ba,`$FRAME+0`($sp)
+	lfd	$bb,`$FRAME+8`($sp)
+	lfd	$bc,`$FRAME+16`($sp)
+	lfd	$bd,`$FRAME+24`($sp)
+	lfd	$na,`$FRAME+32`($sp)
+	lfd	$nb,`$FRAME+40`($sp)
+	lfd	$nc,`$FRAME+48`($sp)
+	lfd	$nd,`$FRAME+56`($sp)
+
+	fcfid	$ba,$ba
+	fcfid	$bb,$bb
+	fcfid	$bc,$bc
+	fcfid	$bd,$bd
+	fcfid	$na,$na
+	fcfid	$nb,$nb
+	fcfid	$nc,$nc
+	fcfid	$nd,$nd
+
+	fmul	$T1a,$A1,$ba
+	fmul	$T1b,$A1,$bb
+	fmul	$T2a,$A2,$ba
+	fmul	$T2b,$A2,$bb
+	fmul	$T3a,$A3,$ba
+	fmul	$T3b,$A3,$bb
+	fmul	$T0a,$A0,$ba
+	fmul	$T0b,$A0,$bb
+
+	fmadd	$T1a,$A0,$bc,$T1a
+	fmadd	$T1b,$A0,$bd,$T1b
+	fmadd	$T2a,$A1,$bc,$T2a
+	fmadd	$T2b,$A1,$bd,$T2b
+	fmadd	$T3a,$A2,$bc,$T3a
+	fmadd	$T3b,$A2,$bd,$T3b
+	fmul	$dota,$A3,$bc
+	fmul	$dotb,$A3,$bd
+
+	fmadd	$T1a,$N1,$na,$T1a
+	fmadd	$T1b,$N1,$nb,$T1b
+	 lfd	$A0,8($nap_d)		; load a[j] in double format
+	 lfd	$A1,16($nap_d)
+	fmadd	$T2a,$N2,$na,$T2a
+	fmadd	$T2b,$N2,$nb,$T2b
+	 lfd	$A2,24($nap_d)		; load a[j+1] in double format
+	 lfd	$A3,32($nap_d)
+	fmadd	$T3a,$N3,$na,$T3a
+	fmadd	$T3b,$N3,$nb,$T3b
+	fmadd	$T0a,$N0,$na,$T0a
+	fmadd	$T0b,$N0,$nb,$T0b
+
+	fmadd	$T1a,$N0,$nc,$T1a
+	fmadd	$T1b,$N0,$nd,$T1b
+	fmadd	$T2a,$N1,$nc,$T2a
+	fmadd	$T2b,$N1,$nd,$T2b
+	fmadd	$T3a,$N2,$nc,$T3a
+	fmadd	$T3b,$N2,$nd,$T3b
+	fmadd	$dota,$N3,$nc,$dota
+	fmadd	$dotb,$N3,$nd,$dotb
+
+	fctid	$T0a,$T0a
+	fctid	$T0b,$T0b
+	fctid	$T1a,$T1a
+	fctid	$T1b,$T1b
+	fctid	$T2a,$T2a
+	fctid	$T2b,$T2b
+	fctid	$T3a,$T3a
+	fctid	$T3b,$T3b
+
+	stfd	$T0a,`$FRAME+0`($sp)
+	stfd	$T0b,`$FRAME+8`($sp)
+	stfd	$T1a,`$FRAME+16`($sp)
+	stfd	$T1b,`$FRAME+24`($sp)
+	stfd	$T2a,`$FRAME+32`($sp)
+	stfd	$T2b,`$FRAME+40`($sp)
+	stfd	$T3a,`$FRAME+48`($sp)
+	stfd	$T3b,`$FRAME+56`($sp)
+
+.align	5
+Linner:
+	fmul	$T1a,$A1,$ba
+	fmul	$T1b,$A1,$bb
+	fmul	$T2a,$A2,$ba
+	fmul	$T2b,$A2,$bb
+	lfd	$N0,40($nap_d)		; load n[j] in double format
+	lfd	$N1,48($nap_d)
+	fmul	$T3a,$A3,$ba
+	fmul	$T3b,$A3,$bb
+	fmadd	$T0a,$A0,$ba,$dota
+	fmadd	$T0b,$A0,$bb,$dotb
+	lfd	$N2,56($nap_d)		; load n[j+1] in double format
+	lfdu	$N3,64($nap_d)
+
+	fmadd	$T1a,$A0,$bc,$T1a
+	fmadd	$T1b,$A0,$bd,$T1b
+	fmadd	$T2a,$A1,$bc,$T2a
+	fmadd	$T2b,$A1,$bd,$T2b
+	 lfd	$A0,8($nap_d)		; load a[j] in double format
+	 lfd	$A1,16($nap_d)
+	fmadd	$T3a,$A2,$bc,$T3a
+	fmadd	$T3b,$A2,$bd,$T3b
+	fmul	$dota,$A3,$bc
+	fmul	$dotb,$A3,$bd
+	 lfd	$A2,24($nap_d)		; load a[j+1] in double format
+	 lfd	$A3,32($nap_d)
+___
+if ($SIZE_T==8 or $flavour =~ /osx/) {
+$code.=<<___;
+	fmadd	$T1a,$N1,$na,$T1a
+	fmadd	$T1b,$N1,$nb,$T1b
+	 ld	$t0,`$FRAME+0`($sp)
+	 ld	$t1,`$FRAME+8`($sp)
+	fmadd	$T2a,$N2,$na,$T2a
+	fmadd	$T2b,$N2,$nb,$T2b
+	 ld	$t2,`$FRAME+16`($sp)
+	 ld	$t3,`$FRAME+24`($sp)
+	fmadd	$T3a,$N3,$na,$T3a
+	fmadd	$T3b,$N3,$nb,$T3b
+	 add	$t0,$t0,$carry		; can not overflow
+	 ld	$t4,`$FRAME+32`($sp)
+	 ld	$t5,`$FRAME+40`($sp)
+	fmadd	$T0a,$N0,$na,$T0a
+	fmadd	$T0b,$N0,$nb,$T0b
+	 srdi	$carry,$t0,16
+	 add	$t1,$t1,$carry
+	 srdi	$carry,$t1,16
+	 ld	$t6,`$FRAME+48`($sp)
+	 ld	$t7,`$FRAME+56`($sp)
+
+	fmadd	$T1a,$N0,$nc,$T1a
+	fmadd	$T1b,$N0,$nd,$T1b
+	 insrdi	$t0,$t1,16,32
+	 ld	$t1,8($tp)		; tp[j]
+	fmadd	$T2a,$N1,$nc,$T2a
+	fmadd	$T2b,$N1,$nd,$T2b
+	 add	$t2,$t2,$carry
+	fmadd	$T3a,$N2,$nc,$T3a
+	fmadd	$T3b,$N2,$nd,$T3b
+	 srdi	$carry,$t2,16
+	 insrdi	$t0,$t2,16,16
+	fmadd	$dota,$N3,$nc,$dota
+	fmadd	$dotb,$N3,$nd,$dotb
+	 add	$t3,$t3,$carry
+	 ldu	$t2,16($tp)		; tp[j+1]
+	 srdi	$carry,$t3,16
+	 insrdi	$t0,$t3,16,0		; 0..63 bits
+	 add	$t4,$t4,$carry
+
+	fctid	$T0a,$T0a
+	fctid	$T0b,$T0b
+	 srdi	$carry,$t4,16
+	fctid	$T1a,$T1a
+	fctid	$T1b,$T1b
+	 add	$t5,$t5,$carry
+	fctid	$T2a,$T2a
+	fctid	$T2b,$T2b
+	 srdi	$carry,$t5,16
+	 insrdi	$t4,$t5,16,32
+	fctid	$T3a,$T3a
+	fctid	$T3b,$T3b
+	 add	$t6,$t6,$carry
+	 srdi	$carry,$t6,16
+	 insrdi	$t4,$t6,16,16
+
+	stfd	$T0a,`$FRAME+0`($sp)
+	stfd	$T0b,`$FRAME+8`($sp)
+	 add	$t7,$t7,$carry
+	 addc	$t3,$t0,$t1
+___
+$code.=<<___ if ($SIZE_T==4);		# adjust XER[CA]
+	extrdi	$t0,$t0,32,0
+	extrdi	$t1,$t1,32,0
+	adde	$t0,$t0,$t1
+___
+$code.=<<___;
+	stfd	$T1a,`$FRAME+16`($sp)
+	stfd	$T1b,`$FRAME+24`($sp)
+	 insrdi	$t4,$t7,16,0		; 64..127 bits
+	 srdi	$carry,$t7,16		; upper 33 bits
+	stfd	$T2a,`$FRAME+32`($sp)
+	stfd	$T2b,`$FRAME+40`($sp)
+	 adde	$t5,$t4,$t2
+___
+$code.=<<___ if ($SIZE_T==4);		# adjust XER[CA]
+	extrdi	$t4,$t4,32,0
+	extrdi	$t2,$t2,32,0
+	adde	$t4,$t4,$t2
+___
+$code.=<<___;
+	stfd	$T3a,`$FRAME+48`($sp)
+	stfd	$T3b,`$FRAME+56`($sp)
+	 addze	$carry,$carry
+	 std	$t3,-16($tp)		; tp[j-1]
+	 std	$t5,-8($tp)		; tp[j]
+___
+} else {
+$code.=<<___;
+	fmadd	$T1a,$N1,$na,$T1a
+	fmadd	$T1b,$N1,$nb,$T1b
+	 lwz	$t1,`$FRAME+0^$LITTLE_ENDIAN`($sp)
+	 lwz	$t0,`$FRAME+4^$LITTLE_ENDIAN`($sp)
+	fmadd	$T2a,$N2,$na,$T2a
+	fmadd	$T2b,$N2,$nb,$T2b
+	 lwz	$t3,`$FRAME+8^$LITTLE_ENDIAN`($sp)
+	 lwz	$t2,`$FRAME+12^$LITTLE_ENDIAN`($sp)
+	fmadd	$T3a,$N3,$na,$T3a
+	fmadd	$T3b,$N3,$nb,$T3b
+	 lwz	$t5,`$FRAME+16^$LITTLE_ENDIAN`($sp)
+	 lwz	$t4,`$FRAME+20^$LITTLE_ENDIAN`($sp)
+	 addc	$t0,$t0,$carry
+	 adde	$t1,$t1,$c1
+	 srwi	$carry,$t0,16
+	fmadd	$T0a,$N0,$na,$T0a
+	fmadd	$T0b,$N0,$nb,$T0b
+	 lwz	$t7,`$FRAME+24^$LITTLE_ENDIAN`($sp)
+	 lwz	$t6,`$FRAME+28^$LITTLE_ENDIAN`($sp)
+	 srwi	$c1,$t1,16
+	 insrwi	$carry,$t1,16,0
+
+	fmadd	$T1a,$N0,$nc,$T1a
+	fmadd	$T1b,$N0,$nd,$T1b
+	 addc	$t2,$t2,$carry
+	 adde	$t3,$t3,$c1
+	 srwi	$carry,$t2,16
+	fmadd	$T2a,$N1,$nc,$T2a
+	fmadd	$T2b,$N1,$nd,$T2b
+	 insrwi	$t0,$t2,16,0		; 0..31 bits
+	 srwi	$c1,$t3,16
+	 insrwi	$carry,$t3,16,0
+	fmadd	$T3a,$N2,$nc,$T3a
+	fmadd	$T3b,$N2,$nd,$T3b
+	 lwz	$t2,12($tp)		; tp[j]
+	 lwz	$t3,8($tp)
+	 addc	$t4,$t4,$carry
+	 adde	$t5,$t5,$c1
+	 srwi	$carry,$t4,16
+	fmadd	$dota,$N3,$nc,$dota
+	fmadd	$dotb,$N3,$nd,$dotb
+	 srwi	$c1,$t5,16
+	 insrwi	$carry,$t5,16,0
+
+	fctid	$T0a,$T0a
+	 addc	$t6,$t6,$carry
+	 adde	$t7,$t7,$c1
+	 srwi	$carry,$t6,16
+	fctid	$T0b,$T0b
+	 insrwi	$t4,$t6,16,0		; 32..63 bits
+	 srwi	$c1,$t7,16
+	 insrwi	$carry,$t7,16,0
+	fctid	$T1a,$T1a
+	 addc	$t0,$t0,$t2
+	 adde	$t4,$t4,$t3
+	 lwz	$t3,`$FRAME+32^$LITTLE_ENDIAN`($sp)	; permuted $t1
+	 lwz	$t2,`$FRAME+36^$LITTLE_ENDIAN`($sp)	; permuted $t0
+	fctid	$T1b,$T1b
+	 addze	$carry,$carry
+	 addze	$c1,$c1
+	 stw	$t0,4($tp)		; tp[j-1]
+	 stw	$t4,0($tp)
+	fctid	$T2a,$T2a
+	 addc	$t2,$t2,$carry
+	 adde	$t3,$t3,$c1
+	 srwi	$carry,$t2,16
+	 lwz	$t7,`$FRAME+40^$LITTLE_ENDIAN`($sp)	; permuted $t3
+	 lwz	$t6,`$FRAME+44^$LITTLE_ENDIAN`($sp)	; permuted $t2
+	fctid	$T2b,$T2b
+	 srwi	$c1,$t3,16
+	 insrwi	$carry,$t3,16,0
+	 lwz	$t1,`$FRAME+48^$LITTLE_ENDIAN`($sp)	; permuted $t5
+	 lwz	$t0,`$FRAME+52^$LITTLE_ENDIAN`($sp)	; permuted $t4
+	fctid	$T3a,$T3a
+	 addc	$t6,$t6,$carry
+	 adde	$t7,$t7,$c1
+	 srwi	$carry,$t6,16
+	 lwz	$t5,`$FRAME+56^$LITTLE_ENDIAN`($sp)	; permuted $t7
+	 lwz	$t4,`$FRAME+60^$LITTLE_ENDIAN`($sp)	; permuted $t6
+	fctid	$T3b,$T3b
+
+	 insrwi	$t2,$t6,16,0		; 64..95 bits
+	insrwi	$carry,$t7,16,0
+	srwi	$c1,$t7,16
+	 lwz	$t6,20($tp)
+	 lwzu	$t7,16($tp)
+	addc	$t0,$t0,$carry
+	 stfd	$T0a,`$FRAME+0`($sp)
+	adde	$t1,$t1,$c1
+	srwi	$carry,$t0,16
+	 stfd	$T0b,`$FRAME+8`($sp)
+	insrwi	$carry,$t1,16,0
+	srwi	$c1,$t1,16
+	addc	$t4,$t4,$carry
+	 stfd	$T1a,`$FRAME+16`($sp)
+	adde	$t5,$t5,$c1
+	srwi	$carry,$t4,16
+	 insrwi	$t0,$t4,16,0		; 96..127 bits
+	 stfd	$T1b,`$FRAME+24`($sp)
+	insrwi	$carry,$t5,16,0
+	srwi	$c1,$t5,16
+
+	addc	$t2,$t2,$t6
+	 stfd	$T2a,`$FRAME+32`($sp)
+	adde	$t0,$t0,$t7
+	 stfd	$T2b,`$FRAME+40`($sp)
+	addze	$carry,$carry
+	 stfd	$T3a,`$FRAME+48`($sp)
+	addze	$c1,$c1
+	 stfd	$T3b,`$FRAME+56`($sp)
+	 stw	$t2,-4($tp)		; tp[j]
+	 stw	$t0,-8($tp)
+___
+}
+$code.=<<___;
+	bdnz	Linner
+
+	fctid	$dota,$dota
+	fctid	$dotb,$dotb
+___
+if ($SIZE_T==8 or $flavour =~ /osx/) {
+$code.=<<___;
+	ld	$t0,`$FRAME+0`($sp)
+	ld	$t1,`$FRAME+8`($sp)
+	ld	$t2,`$FRAME+16`($sp)
+	ld	$t3,`$FRAME+24`($sp)
+	ld	$t4,`$FRAME+32`($sp)
+	ld	$t5,`$FRAME+40`($sp)
+	ld	$t6,`$FRAME+48`($sp)
+	ld	$t7,`$FRAME+56`($sp)
+	stfd	$dota,`$FRAME+64`($sp)
+	stfd	$dotb,`$FRAME+72`($sp)
+
+	add	$t0,$t0,$carry		; can not overflow
+	srdi	$carry,$t0,16
+	add	$t1,$t1,$carry
+	srdi	$carry,$t1,16
+	insrdi	$t0,$t1,16,32
+	add	$t2,$t2,$carry
+	ld	$t1,8($tp)		; tp[j]
+	srdi	$carry,$t2,16
+	insrdi	$t0,$t2,16,16
+	add	$t3,$t3,$carry
+	ldu	$t2,16($tp)		; tp[j+1]
+	srdi	$carry,$t3,16
+	insrdi	$t0,$t3,16,0		; 0..63 bits
+	add	$t4,$t4,$carry
+	srdi	$carry,$t4,16
+	add	$t5,$t5,$carry
+	srdi	$carry,$t5,16
+	insrdi	$t4,$t5,16,32
+	add	$t6,$t6,$carry
+	srdi	$carry,$t6,16
+	insrdi	$t4,$t6,16,16
+	add	$t7,$t7,$carry
+	insrdi	$t4,$t7,16,0		; 64..127 bits
+	srdi	$carry,$t7,16		; upper 33 bits
+	ld	$t6,`$FRAME+64`($sp)
+	ld	$t7,`$FRAME+72`($sp)
+
+	addc	$t3,$t0,$t1
+___
+$code.=<<___ if ($SIZE_T==4);		# adjust XER[CA]
+	extrdi	$t0,$t0,32,0
+	extrdi	$t1,$t1,32,0
+	adde	$t0,$t0,$t1
+___
+$code.=<<___;
+	adde	$t5,$t4,$t2
+___
+$code.=<<___ if ($SIZE_T==4);		# adjust XER[CA]
+	extrdi	$t4,$t4,32,0
+	extrdi	$t2,$t2,32,0
+	adde	$t4,$t4,$t2
+___
+$code.=<<___;
+	addze	$carry,$carry
+
+	std	$t3,-16($tp)		; tp[j-1]
+	std	$t5,-8($tp)		; tp[j]
+
+	add	$carry,$carry,$ovf	; comsume upmost overflow
+	add	$t6,$t6,$carry		; can not overflow
+	srdi	$carry,$t6,16
+	add	$t7,$t7,$carry
+	insrdi	$t6,$t7,48,0
+	srdi	$ovf,$t7,48
+	std	$t6,0($tp)		; tp[num-1]
+___
+} else {
+$code.=<<___;
+	lwz	$t1,`$FRAME+0^$LITTLE_ENDIAN`($sp)
+	lwz	$t0,`$FRAME+4^$LITTLE_ENDIAN`($sp)
+	lwz	$t3,`$FRAME+8^$LITTLE_ENDIAN`($sp)
+	lwz	$t2,`$FRAME+12^$LITTLE_ENDIAN`($sp)
+	lwz	$t5,`$FRAME+16^$LITTLE_ENDIAN`($sp)
+	lwz	$t4,`$FRAME+20^$LITTLE_ENDIAN`($sp)
+	lwz	$t7,`$FRAME+24^$LITTLE_ENDIAN`($sp)
+	lwz	$t6,`$FRAME+28^$LITTLE_ENDIAN`($sp)
+	stfd	$dota,`$FRAME+64`($sp)
+	stfd	$dotb,`$FRAME+72`($sp)
+
+	addc	$t0,$t0,$carry
+	adde	$t1,$t1,$c1
+	srwi	$carry,$t0,16
+	insrwi	$carry,$t1,16,0
+	srwi	$c1,$t1,16
+	addc	$t2,$t2,$carry
+	adde	$t3,$t3,$c1
+	srwi	$carry,$t2,16
+	 insrwi	$t0,$t2,16,0		; 0..31 bits
+	 lwz	$t2,12($tp)		; tp[j]
+	insrwi	$carry,$t3,16,0
+	srwi	$c1,$t3,16
+	 lwz	$t3,8($tp)
+	addc	$t4,$t4,$carry
+	adde	$t5,$t5,$c1
+	srwi	$carry,$t4,16
+	insrwi	$carry,$t5,16,0
+	srwi	$c1,$t5,16
+	addc	$t6,$t6,$carry
+	adde	$t7,$t7,$c1
+	srwi	$carry,$t6,16
+	 insrwi	$t4,$t6,16,0		; 32..63 bits
+	insrwi	$carry,$t7,16,0
+	srwi	$c1,$t7,16
+
+	addc	$t0,$t0,$t2
+	adde	$t4,$t4,$t3
+	addze	$carry,$carry
+	addze	$c1,$c1
+	 stw	$t0,4($tp)		; tp[j-1]
+	 stw	$t4,0($tp)
+
+	lwz	$t3,`$FRAME+32^$LITTLE_ENDIAN`($sp)	; permuted $t1
+	lwz	$t2,`$FRAME+36^$LITTLE_ENDIAN`($sp)	; permuted $t0
+	lwz	$t7,`$FRAME+40^$LITTLE_ENDIAN`($sp)	; permuted $t3
+	lwz	$t6,`$FRAME+44^$LITTLE_ENDIAN`($sp)	; permuted $t2
+	lwz	$t1,`$FRAME+48^$LITTLE_ENDIAN`($sp)	; permuted $t5
+	lwz	$t0,`$FRAME+52^$LITTLE_ENDIAN`($sp)	; permuted $t4
+	lwz	$t5,`$FRAME+56^$LITTLE_ENDIAN`($sp)	; permuted $t7
+	lwz	$t4,`$FRAME+60^$LITTLE_ENDIAN`($sp)	; permuted $t6
+
+	addc	$t2,$t2,$carry
+	adde	$t3,$t3,$c1
+	srwi	$carry,$t2,16
+	insrwi	$carry,$t3,16,0
+	srwi	$c1,$t3,16
+	addc	$t6,$t6,$carry
+	adde	$t7,$t7,$c1
+	srwi	$carry,$t6,16
+	 insrwi	$t2,$t6,16,0		; 64..95 bits
+	 lwz	$t6,20($tp)
+	insrwi	$carry,$t7,16,0
+	srwi	$c1,$t7,16
+	 lwzu	$t7,16($tp)
+	addc	$t0,$t0,$carry
+	adde	$t1,$t1,$c1
+	srwi	$carry,$t0,16
+	insrwi	$carry,$t1,16,0
+	srwi	$c1,$t1,16
+	addc	$t4,$t4,$carry
+	adde	$t5,$t5,$c1
+	srwi	$carry,$t4,16
+	 insrwi	$t0,$t4,16,0		; 96..127 bits
+	insrwi	$carry,$t5,16,0
+	srwi	$c1,$t5,16
+
+	addc	$t2,$t2,$t6
+	adde	$t0,$t0,$t7
+	 lwz	$t7,`$FRAME+64^$LITTLE_ENDIAN`($sp)
+	 lwz	$t6,`$FRAME+68^$LITTLE_ENDIAN`($sp)
+	addze	$carry,$carry
+	addze	$c1,$c1
+	 lwz	$t5,`$FRAME+72^$LITTLE_ENDIAN`($sp)
+	 lwz	$t4,`$FRAME+76^$LITTLE_ENDIAN`($sp)
+
+	addc	$t6,$t6,$carry
+	adde	$t7,$t7,$c1
+	 stw	$t2,-4($tp)		; tp[j]
+	 stw	$t0,-8($tp)
+	addc	$t6,$t6,$ovf
+	addze	$t7,$t7
+	srwi	$carry,$t6,16
+	insrwi	$carry,$t7,16,0
+	srwi	$c1,$t7,16
+	addc	$t4,$t4,$carry
+	adde	$t5,$t5,$c1
+
+	insrwi	$t6,$t4,16,0
+	srwi	$t4,$t4,16
+	insrwi	$t4,$t5,16,0
+	srwi	$ovf,$t5,16
+	stw	$t6,4($tp)		; tp[num-1]
+	stw	$t4,0($tp)
+___
+}
+$code.=<<___;
+	slwi	$t7,$num,2
+	addi	$i,$i,8
+	subf	$nap_d,$t7,$nap_d	; rewind pointer
+	cmpw	$i,$num
+	blt-	Louter
+___
+
+$code.=<<___ if ($SIZE_T==8);
+	subf	$np,$num,$np	; rewind np
+	addi	$j,$j,1		; restore counter
+	subfc	$i,$i,$i	; j=0 and "clear" XER[CA]
+	addi	$tp,$sp,`$FRAME+$TRANSFER+8`
+	addi	$t4,$sp,`$FRAME+$TRANSFER+16`
+	addi	$t5,$np,8
+	addi	$t6,$rp,8
+	mtctr	$j
+
+.align	4
+Lsub:	ldx	$t0,$tp,$i
+	ldx	$t1,$np,$i
+	ldx	$t2,$t4,$i
+	ldx	$t3,$t5,$i
+	subfe	$t0,$t1,$t0	; tp[j]-np[j]
+	subfe	$t2,$t3,$t2	; tp[j+1]-np[j+1]
+	stdx	$t0,$rp,$i
+	stdx	$t2,$t6,$i
+	addi	$i,$i,16
+	bdnz	Lsub
+
+	li	$i,0
+	subfe	$ovf,$i,$ovf	; handle upmost overflow bit
+	and	$ap,$tp,$ovf
+	andc	$np,$rp,$ovf
+	or	$ap,$ap,$np	; ap=borrow?tp:rp
+	addi	$t7,$ap,8
+	mtctr	$j
+
+.align	4
+Lcopy:				; copy or in-place refresh
+	ldx	$t0,$ap,$i
+	ldx	$t1,$t7,$i
+	std	$i,8($nap_d)	; zap nap_d
+	std	$i,16($nap_d)
+	std	$i,24($nap_d)
+	std	$i,32($nap_d)
+	std	$i,40($nap_d)
+	std	$i,48($nap_d)
+	std	$i,56($nap_d)
+	stdu	$i,64($nap_d)
+	stdx	$t0,$rp,$i
+	stdx	$t1,$t6,$i
+	stdx	$i,$tp,$i	; zap tp at once
+	stdx	$i,$t4,$i
+	addi	$i,$i,16
+	bdnz	Lcopy
+___
+$code.=<<___ if ($SIZE_T==4);
+	subf	$np,$num,$np	; rewind np
+	addi	$j,$j,1		; restore counter
+	subfc	$i,$i,$i	; j=0 and "clear" XER[CA]
+	addi	$tp,$sp,`$FRAME+$TRANSFER`
+	addi	$np,$np,-4
+	addi	$rp,$rp,-4
+	addi	$ap,$sp,`$FRAME+$TRANSFER+4`
+	mtctr	$j
+
+.align	4
+Lsub:	lwz	$t0,12($tp)	; load tp[j..j+3] in 64-bit word order
+	lwz	$t1,8($tp)
+	lwz	$t2,20($tp)
+	lwzu	$t3,16($tp)
+	lwz	$t4,4($np)	; load np[j..j+3] in 32-bit word order
+	lwz	$t5,8($np)
+	lwz	$t6,12($np)
+	lwzu	$t7,16($np)
+	subfe	$t4,$t4,$t0	; tp[j]-np[j]
+	 stw	$t0,4($ap)	; save tp[j..j+3] in 32-bit word order
+	subfe	$t5,$t5,$t1	; tp[j+1]-np[j+1]
+	 stw	$t1,8($ap)
+	subfe	$t6,$t6,$t2	; tp[j+2]-np[j+2]
+	 stw	$t2,12($ap)
+	subfe	$t7,$t7,$t3	; tp[j+3]-np[j+3]
+	 stwu	$t3,16($ap)
+	stw	$t4,4($rp)
+	stw	$t5,8($rp)
+	stw	$t6,12($rp)
+	stwu	$t7,16($rp)
+	bdnz	Lsub
+
+	li	$i,0
+	subfe	$ovf,$i,$ovf	; handle upmost overflow bit
+	addi	$tp,$sp,`$FRAME+$TRANSFER+4`
+	subf	$rp,$num,$rp	; rewind rp
+	and	$ap,$tp,$ovf
+	andc	$np,$rp,$ovf
+	or	$ap,$ap,$np	; ap=borrow?tp:rp
+	addi	$tp,$sp,`$FRAME+$TRANSFER`
+	mtctr	$j
+
+.align	4
+Lcopy:				; copy or in-place refresh
+	lwz	$t0,4($ap)
+	lwz	$t1,8($ap)
+	lwz	$t2,12($ap)
+	lwzu	$t3,16($ap)
+	std	$i,8($nap_d)	; zap nap_d
+	std	$i,16($nap_d)
+	std	$i,24($nap_d)
+	std	$i,32($nap_d)
+	std	$i,40($nap_d)
+	std	$i,48($nap_d)
+	std	$i,56($nap_d)
+	stdu	$i,64($nap_d)
+	stw	$t0,4($rp)
+	stw	$t1,8($rp)
+	stw	$t2,12($rp)
+	stwu	$t3,16($rp)
+	std	$i,8($tp)	; zap tp at once
+	stdu	$i,16($tp)
+	bdnz	Lcopy
+___
+
+$code.=<<___;
+	$POP	$i,0($sp)
+	li	r3,1	; signal "handled"
+	$POP	r19,`-12*8-13*$SIZE_T`($i)
+	$POP	r20,`-12*8-12*$SIZE_T`($i)
+	$POP	r21,`-12*8-11*$SIZE_T`($i)
+	$POP	r22,`-12*8-10*$SIZE_T`($i)
+	$POP	r23,`-12*8-9*$SIZE_T`($i)
+	$POP	r24,`-12*8-8*$SIZE_T`($i)
+	$POP	r25,`-12*8-7*$SIZE_T`($i)
+	$POP	r26,`-12*8-6*$SIZE_T`($i)
+	$POP	r27,`-12*8-5*$SIZE_T`($i)
+	$POP	r28,`-12*8-4*$SIZE_T`($i)
+	$POP	r29,`-12*8-3*$SIZE_T`($i)
+	$POP	r30,`-12*8-2*$SIZE_T`($i)
+	$POP	r31,`-12*8-1*$SIZE_T`($i)
+	lfd	f20,`-12*8`($i)
+	lfd	f21,`-11*8`($i)
+	lfd	f22,`-10*8`($i)
+	lfd	f23,`-9*8`($i)
+	lfd	f24,`-8*8`($i)
+	lfd	f25,`-7*8`($i)
+	lfd	f26,`-6*8`($i)
+	lfd	f27,`-5*8`($i)
+	lfd	f28,`-4*8`($i)
+	lfd	f29,`-3*8`($i)
+	lfd	f30,`-2*8`($i)
+	lfd	f31,`-1*8`($i)
+	mr	$sp,$i
+	blr
+	.long	0
+	.byte	0,12,4,0,0x8c,13,6,0
+	.long	0
+.size	.$fname,.-.$fname
+
+.asciz  "Montgomery Multiplication for PPC64, CRYPTOGAMS by "
+___
+
+$code =~ s/\`([^\`]*)\`/eval $1/gem;
+print $code;
+close STDOUT;
diff --git a/crypto/bn/asm/rsaz-avx2.pl b/crypto/bn/asm/rsaz-avx2.pl
new file mode 100755
index 000000000..0c1b236ef
--- /dev/null
+++ b/crypto/bn/asm/rsaz-avx2.pl
@@ -0,0 +1,1968 @@
+#! /usr/bin/env perl
+# Copyright 2013-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+
+##############################################################################
+#                                                                            #
+#  Copyright (c) 2012, Intel Corporation                                     #
+#                                                                            #
+#  All rights reserved.                                                      #
+#                                                                            #
+#  Redistribution and use in source and binary forms, with or without        #
+#  modification, are permitted provided that the following conditions are    #
+#  met:                                                                      #
+#                                                                            #
+#  *  Redistributions of source code must retain the above copyright         #
+#     notice, this list of conditions and the following disclaimer.          #
+#                                                                            #
+#  *  Redistributions in binary form must reproduce the above copyright      #
+#     notice, this list of conditions and the following disclaimer in the    #
+#     documentation and/or other materials provided with the                 #
+#     distribution.                                                          #
+#                                                                            #
+#  *  Neither the name of the Intel Corporation nor the names of its         #
+#     contributors may be used to endorse or promote products derived from   #
+#     this software without specific prior written permission.               #
+#                                                                            #
+#                                                                            #
+#  THIS SOFTWARE IS PROVIDED BY INTEL CORPORATION ""AS IS"" AND ANY          #
+#  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE         #
+#  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR        #
+#  PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL CORPORATION OR            #
+#  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,     #
+#  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,       #
+#  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR        #
+#  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF    #
+#  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING      #
+#  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS        #
+#  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.              #
+#                                                                            #
+##############################################################################
+# Developers and authors:                                                    #
+# Shay Gueron (1, 2), and Vlad Krasnov (1)                                   #
+# (1) Intel Corporation, Israel Development Center, Haifa, Israel            #
+# (2) University of Haifa, Israel                                            #
+##############################################################################
+# Reference:                                                                 #
+# [1] S. Gueron, V. Krasnov: "Software Implementation of Modular             #
+#     Exponentiation,  Using Advanced Vector Instructions Architectures",    #
+#     F. Ozbudak and F. Rodriguez-Henriquez (Eds.): WAIFI 2012, LNCS 7369,   #
+#     pp. 119?135, 2012. Springer-Verlag Berlin Heidelberg 2012              #
+# [2] S. Gueron: "Efficient Software Implementations of Modular              #
+#     Exponentiation", Journal of Cryptographic Engineering 2:31-43 (2012).  #
+# [3] S. Gueron, V. Krasnov: "Speeding up Big-numbers Squaring",IEEE         #
+#     Proceedings of 9th International Conference on Information Technology: #
+#     New Generations (ITNG 2012), pp.821-823 (2012)                         #
+# [4] S. Gueron, V. Krasnov: "[PATCH] Efficient and side channel analysis    #
+#     resistant 1024-bit modular exponentiation, for optimizing RSA2048      #
+#     on AVX2 capable x86_64 platforms",                                     #
+#     http://rt.openssl.org/Ticket/Display.html?id=2850&user=guest&pass=guest#
+##############################################################################
+#
+# +13% improvement over original submission by 
+#
+# rsa2048 sign/sec	OpenSSL 1.0.1	scalar(*)	this
+# 2.3GHz Haswell	621		765/+23%	1113/+79%
+# 2.3GHz Broadwell(**)	688		1200(***)/+74%	1120/+63%
+#
+# (*)	if system doesn't support AVX2, for reference purposes;
+# (**)	scaled to 2.3GHz to simplify comparison;
+# (***)	scalar AD*X code is faster than AVX2 and is preferred code
+#	path for Broadwell;
+
+$flavour = shift;
+$output  = shift;
+if ($flavour =~ /\./) { $output = $flavour; undef $flavour; }
+
+$win64=0; $win64=1 if ($flavour =~ /[nm]asm|mingw64/ || $output =~ /\.asm$/);
+
+$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+( $xlate="${dir}x86_64-xlate.pl" and -f $xlate ) or
+( $xlate="${dir}../../perlasm/x86_64-xlate.pl" and -f $xlate) or
+die "can't locate x86_64-xlate.pl";
+
+if (`$ENV{CC} -Wa,-v -c -o /dev/null -x assembler /dev/null 2>&1`
+		=~ /GNU assembler version ([2-9]\.[0-9]+)/) {
+	$avx = ($1>=2.19) + ($1>=2.22);
+	$addx = ($1>=2.23);
+}
+
+if (!$avx && $win64 && ($flavour =~ /nasm/ || $ENV{ASM} =~ /nasm/) &&
+	    `nasm -v 2>&1` =~ /NASM version ([2-9]\.[0-9]+)/) {
+	$avx = ($1>=2.09) + ($1>=2.10);
+	$addx = ($1>=2.10);
+}
+
+if (!$avx && $win64 && ($flavour =~ /masm/ || $ENV{ASM} =~ /ml64/) &&
+	    `ml64 2>&1` =~ /Version ([0-9]+)\./) {
+	$avx = ($1>=10) + ($1>=11);
+	$addx = ($1>=11);
+}
+
+if (!$avx && `$ENV{CC} -v 2>&1` =~ /(^clang version|based on LLVM) ([3-9])\.([0-9]+)/) {
+	my $ver = $2 + $3/100.0;	# 3.1->3.01, 3.10->3.10
+	$avx = ($ver>=3.0) + ($ver>=3.01);
+	$addx = ($ver>=3.03);
+}
+
+open OUT,"| \"$^X\" \"$xlate\" $flavour \"$output\"";
+*STDOUT = *OUT;
+
+if ($avx>1) {{{
+{ # void AMS_WW(
+my $rp="%rdi";	# BN_ULONG *rp,
+my $ap="%rsi";	# const BN_ULONG *ap,
+my $np="%rdx";	# const BN_ULONG *np,
+my $n0="%ecx";	# const BN_ULONG n0,
+my $rep="%r8d";	# int repeat);
+
+# The registers that hold the accumulated redundant result
+# The AMM works on 1024 bit operands, and redundant word size is 29
+# Therefore: ceil(1024/29)/4 = 9
+my $ACC0="%ymm0";
+my $ACC1="%ymm1";
+my $ACC2="%ymm2";
+my $ACC3="%ymm3";
+my $ACC4="%ymm4";
+my $ACC5="%ymm5";
+my $ACC6="%ymm6";
+my $ACC7="%ymm7";
+my $ACC8="%ymm8";
+my $ACC9="%ymm9";
+# Registers that hold the broadcasted words of bp, currently used
+my $B1="%ymm10";
+my $B2="%ymm11";
+# Registers that hold the broadcasted words of Y, currently used
+my $Y1="%ymm12";
+my $Y2="%ymm13";
+# Helper registers
+my $TEMP1="%ymm14";
+my $AND_MASK="%ymm15";
+# alu registers that hold the first words of the ACC
+my $r0="%r9";
+my $r1="%r10";
+my $r2="%r11";
+my $r3="%r12";
+
+my $i="%r14d";			# loop counter
+my $tmp = "%r15";
+
+my $FrameSize=32*18+32*8;	# place for A^2 and 2*A
+
+my $aap=$r0;
+my $tp0="%rbx";
+my $tp1=$r3;
+my $tpa=$tmp;
+
+$np="%r13";			# reassigned argument
+
+$code.=<<___;
+.text
+
+.globl	rsaz_1024_sqr_avx2
+.type	rsaz_1024_sqr_avx2,\@function,5
+.align	64
+rsaz_1024_sqr_avx2:		# 702 cycles, 14% faster than rsaz_1024_mul_avx2
+	lea	(%rsp), %rax
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+	vzeroupper
+___
+$code.=<<___ if ($win64);
+	lea	-0xa8(%rsp),%rsp
+	vmovaps	%xmm6,-0xd8(%rax)
+	vmovaps	%xmm7,-0xc8(%rax)
+	vmovaps	%xmm8,-0xb8(%rax)
+	vmovaps	%xmm9,-0xa8(%rax)
+	vmovaps	%xmm10,-0x98(%rax)
+	vmovaps	%xmm11,-0x88(%rax)
+	vmovaps	%xmm12,-0x78(%rax)
+	vmovaps	%xmm13,-0x68(%rax)
+	vmovaps	%xmm14,-0x58(%rax)
+	vmovaps	%xmm15,-0x48(%rax)
+.Lsqr_1024_body:
+___
+$code.=<<___;
+	mov	%rax,%rbp
+	mov	%rdx, $np			# reassigned argument
+	sub	\$$FrameSize, %rsp
+	mov	$np, $tmp
+	sub	\$-128, $rp			# size optimization
+	sub	\$-128, $ap
+	sub	\$-128, $np
+
+	and	\$4095, $tmp			# see if $np crosses page
+	add	\$32*10, $tmp
+	shr	\$12, $tmp
+	vpxor	$ACC9,$ACC9,$ACC9
+	jz	.Lsqr_1024_no_n_copy
+
+	# unaligned 256-bit load that crosses page boundary can
+	# cause >2x performance degradation here, so if $np does
+	# cross page boundary, copy it to stack and make sure stack
+	# frame doesn't...
+	sub		\$32*10,%rsp
+	vmovdqu		32*0-128($np), $ACC0
+	and		\$-2048, %rsp
+	vmovdqu		32*1-128($np), $ACC1
+	vmovdqu		32*2-128($np), $ACC2
+	vmovdqu		32*3-128($np), $ACC3
+	vmovdqu		32*4-128($np), $ACC4
+	vmovdqu		32*5-128($np), $ACC5
+	vmovdqu		32*6-128($np), $ACC6
+	vmovdqu		32*7-128($np), $ACC7
+	vmovdqu		32*8-128($np), $ACC8
+	lea		$FrameSize+128(%rsp),$np
+	vmovdqu		$ACC0, 32*0-128($np)
+	vmovdqu		$ACC1, 32*1-128($np)
+	vmovdqu		$ACC2, 32*2-128($np)
+	vmovdqu		$ACC3, 32*3-128($np)
+	vmovdqu		$ACC4, 32*4-128($np)
+	vmovdqu		$ACC5, 32*5-128($np)
+	vmovdqu		$ACC6, 32*6-128($np)
+	vmovdqu		$ACC7, 32*7-128($np)
+	vmovdqu		$ACC8, 32*8-128($np)
+	vmovdqu		$ACC9, 32*9-128($np)	# $ACC9 is zero
+
+.Lsqr_1024_no_n_copy:
+	and		\$-1024, %rsp
+
+	vmovdqu		32*1-128($ap), $ACC1
+	vmovdqu		32*2-128($ap), $ACC2
+	vmovdqu		32*3-128($ap), $ACC3
+	vmovdqu		32*4-128($ap), $ACC4
+	vmovdqu		32*5-128($ap), $ACC5
+	vmovdqu		32*6-128($ap), $ACC6
+	vmovdqu		32*7-128($ap), $ACC7
+	vmovdqu		32*8-128($ap), $ACC8
+
+	lea	192(%rsp), $tp0			# 64+128=192
+	vpbroadcastq	.Land_mask(%rip), $AND_MASK
+	jmp	.LOOP_GRANDE_SQR_1024
+
+.align	32
+.LOOP_GRANDE_SQR_1024:
+	lea	32*18+128(%rsp), $aap		# size optimization
+	lea	448(%rsp), $tp1			# 64+128+256=448
+
+	# the squaring is performed as described in Variant B of
+	# "Speeding up Big-Number Squaring", so start by calculating
+	# the A*2=A+A vector
+	vpaddq		$ACC1, $ACC1, $ACC1
+	 vpbroadcastq	32*0-128($ap), $B1
+	vpaddq		$ACC2, $ACC2, $ACC2
+	vmovdqa		$ACC1, 32*0-128($aap)
+	vpaddq		$ACC3, $ACC3, $ACC3
+	vmovdqa		$ACC2, 32*1-128($aap)
+	vpaddq		$ACC4, $ACC4, $ACC4
+	vmovdqa		$ACC3, 32*2-128($aap)
+	vpaddq		$ACC5, $ACC5, $ACC5
+	vmovdqa		$ACC4, 32*3-128($aap)
+	vpaddq		$ACC6, $ACC6, $ACC6
+	vmovdqa		$ACC5, 32*4-128($aap)
+	vpaddq		$ACC7, $ACC7, $ACC7
+	vmovdqa		$ACC6, 32*5-128($aap)
+	vpaddq		$ACC8, $ACC8, $ACC8
+	vmovdqa		$ACC7, 32*6-128($aap)
+	vpxor		$ACC9, $ACC9, $ACC9
+	vmovdqa		$ACC8, 32*7-128($aap)
+
+	vpmuludq	32*0-128($ap), $B1, $ACC0
+	 vpbroadcastq	32*1-128($ap), $B2
+	 vmovdqu	$ACC9, 32*9-192($tp0)	# zero upper half
+	vpmuludq	$B1, $ACC1, $ACC1
+	 vmovdqu	$ACC9, 32*10-448($tp1)
+	vpmuludq	$B1, $ACC2, $ACC2
+	 vmovdqu	$ACC9, 32*11-448($tp1)
+	vpmuludq	$B1, $ACC3, $ACC3
+	 vmovdqu	$ACC9, 32*12-448($tp1)
+	vpmuludq	$B1, $ACC4, $ACC4
+	 vmovdqu	$ACC9, 32*13-448($tp1)
+	vpmuludq	$B1, $ACC5, $ACC5
+	 vmovdqu	$ACC9, 32*14-448($tp1)
+	vpmuludq	$B1, $ACC6, $ACC6
+	 vmovdqu	$ACC9, 32*15-448($tp1)
+	vpmuludq	$B1, $ACC7, $ACC7
+	 vmovdqu	$ACC9, 32*16-448($tp1)
+	vpmuludq	$B1, $ACC8, $ACC8
+	 vpbroadcastq	32*2-128($ap), $B1
+	 vmovdqu	$ACC9, 32*17-448($tp1)
+
+	mov	$ap, $tpa
+	mov 	\$4, $i
+	jmp	.Lsqr_entry_1024
+___
+$TEMP0=$Y1;
+$TEMP2=$Y2;
+$code.=<<___;
+.align	32
+.LOOP_SQR_1024:
+	 vpbroadcastq	32*1-128($tpa), $B2
+	vpmuludq	32*0-128($ap), $B1, $ACC0
+	vpaddq		32*0-192($tp0), $ACC0, $ACC0
+	vpmuludq	32*0-128($aap), $B1, $ACC1
+	vpaddq		32*1-192($tp0), $ACC1, $ACC1
+	vpmuludq	32*1-128($aap), $B1, $ACC2
+	vpaddq		32*2-192($tp0), $ACC2, $ACC2
+	vpmuludq	32*2-128($aap), $B1, $ACC3
+	vpaddq		32*3-192($tp0), $ACC3, $ACC3
+	vpmuludq	32*3-128($aap), $B1, $ACC4
+	vpaddq		32*4-192($tp0), $ACC4, $ACC4
+	vpmuludq	32*4-128($aap), $B1, $ACC5
+	vpaddq		32*5-192($tp0), $ACC5, $ACC5
+	vpmuludq	32*5-128($aap), $B1, $ACC6
+	vpaddq		32*6-192($tp0), $ACC6, $ACC6
+	vpmuludq	32*6-128($aap), $B1, $ACC7
+	vpaddq		32*7-192($tp0), $ACC7, $ACC7
+	vpmuludq	32*7-128($aap), $B1, $ACC8
+	 vpbroadcastq	32*2-128($tpa), $B1
+	vpaddq		32*8-192($tp0), $ACC8, $ACC8
+.Lsqr_entry_1024:
+	vmovdqu		$ACC0, 32*0-192($tp0)
+	vmovdqu		$ACC1, 32*1-192($tp0)
+
+	vpmuludq	32*1-128($ap), $B2, $TEMP0
+	vpaddq		$TEMP0, $ACC2, $ACC2
+	vpmuludq	32*1-128($aap), $B2, $TEMP1
+	vpaddq		$TEMP1, $ACC3, $ACC3
+	vpmuludq	32*2-128($aap), $B2, $TEMP2
+	vpaddq		$TEMP2, $ACC4, $ACC4
+	vpmuludq	32*3-128($aap), $B2, $TEMP0
+	vpaddq		$TEMP0, $ACC5, $ACC5
+	vpmuludq	32*4-128($aap), $B2, $TEMP1
+	vpaddq		$TEMP1, $ACC6, $ACC6
+	vpmuludq	32*5-128($aap), $B2, $TEMP2
+	vpaddq		$TEMP2, $ACC7, $ACC7
+	vpmuludq	32*6-128($aap), $B2, $TEMP0
+	vpaddq		$TEMP0, $ACC8, $ACC8
+	vpmuludq	32*7-128($aap), $B2, $ACC0
+	 vpbroadcastq	32*3-128($tpa), $B2
+	vpaddq		32*9-192($tp0), $ACC0, $ACC0
+
+	vmovdqu		$ACC2, 32*2-192($tp0)
+	vmovdqu		$ACC3, 32*3-192($tp0)
+
+	vpmuludq	32*2-128($ap), $B1, $TEMP2
+	vpaddq		$TEMP2, $ACC4, $ACC4
+	vpmuludq	32*2-128($aap), $B1, $TEMP0
+	vpaddq		$TEMP0, $ACC5, $ACC5
+	vpmuludq	32*3-128($aap), $B1, $TEMP1
+	vpaddq		$TEMP1, $ACC6, $ACC6
+	vpmuludq	32*4-128($aap), $B1, $TEMP2
+	vpaddq		$TEMP2, $ACC7, $ACC7
+	vpmuludq	32*5-128($aap), $B1, $TEMP0
+	vpaddq		$TEMP0, $ACC8, $ACC8
+	vpmuludq	32*6-128($aap), $B1, $TEMP1
+	vpaddq		$TEMP1, $ACC0, $ACC0
+	vpmuludq	32*7-128($aap), $B1, $ACC1
+	 vpbroadcastq	32*4-128($tpa), $B1
+	vpaddq		32*10-448($tp1), $ACC1, $ACC1
+
+	vmovdqu		$ACC4, 32*4-192($tp0)
+	vmovdqu		$ACC5, 32*5-192($tp0)
+
+	vpmuludq	32*3-128($ap), $B2, $TEMP0
+	vpaddq		$TEMP0, $ACC6, $ACC6
+	vpmuludq	32*3-128($aap), $B2, $TEMP1
+	vpaddq		$TEMP1, $ACC7, $ACC7
+	vpmuludq	32*4-128($aap), $B2, $TEMP2
+	vpaddq		$TEMP2, $ACC8, $ACC8
+	vpmuludq	32*5-128($aap), $B2, $TEMP0
+	vpaddq		$TEMP0, $ACC0, $ACC0
+	vpmuludq	32*6-128($aap), $B2, $TEMP1
+	vpaddq		$TEMP1, $ACC1, $ACC1
+	vpmuludq	32*7-128($aap), $B2, $ACC2
+	 vpbroadcastq	32*5-128($tpa), $B2
+	vpaddq		32*11-448($tp1), $ACC2, $ACC2	
+
+	vmovdqu		$ACC6, 32*6-192($tp0)
+	vmovdqu		$ACC7, 32*7-192($tp0)
+
+	vpmuludq	32*4-128($ap), $B1, $TEMP0
+	vpaddq		$TEMP0, $ACC8, $ACC8
+	vpmuludq	32*4-128($aap), $B1, $TEMP1
+	vpaddq		$TEMP1, $ACC0, $ACC0
+	vpmuludq	32*5-128($aap), $B1, $TEMP2
+	vpaddq		$TEMP2, $ACC1, $ACC1
+	vpmuludq	32*6-128($aap), $B1, $TEMP0
+	vpaddq		$TEMP0, $ACC2, $ACC2
+	vpmuludq	32*7-128($aap), $B1, $ACC3
+	 vpbroadcastq	32*6-128($tpa), $B1
+	vpaddq		32*12-448($tp1), $ACC3, $ACC3
+
+	vmovdqu		$ACC8, 32*8-192($tp0)
+	vmovdqu		$ACC0, 32*9-192($tp0)
+	lea		8($tp0), $tp0
+
+	vpmuludq	32*5-128($ap), $B2, $TEMP2
+	vpaddq		$TEMP2, $ACC1, $ACC1
+	vpmuludq	32*5-128($aap), $B2, $TEMP0
+	vpaddq		$TEMP0, $ACC2, $ACC2
+	vpmuludq	32*6-128($aap), $B2, $TEMP1
+	vpaddq		$TEMP1, $ACC3, $ACC3
+	vpmuludq	32*7-128($aap), $B2, $ACC4
+	 vpbroadcastq	32*7-128($tpa), $B2
+	vpaddq		32*13-448($tp1), $ACC4, $ACC4
+
+	vmovdqu		$ACC1, 32*10-448($tp1)
+	vmovdqu		$ACC2, 32*11-448($tp1)
+
+	vpmuludq	32*6-128($ap), $B1, $TEMP0
+	vpaddq		$TEMP0, $ACC3, $ACC3
+	vpmuludq	32*6-128($aap), $B1, $TEMP1
+	 vpbroadcastq	32*8-128($tpa), $ACC0		# borrow $ACC0 for $B1
+	vpaddq		$TEMP1, $ACC4, $ACC4
+	vpmuludq	32*7-128($aap), $B1, $ACC5
+	 vpbroadcastq	32*0+8-128($tpa), $B1		# for next iteration
+	vpaddq		32*14-448($tp1), $ACC5, $ACC5
+
+	vmovdqu		$ACC3, 32*12-448($tp1)
+	vmovdqu		$ACC4, 32*13-448($tp1)
+	lea		8($tpa), $tpa
+
+	vpmuludq	32*7-128($ap), $B2, $TEMP0
+	vpaddq		$TEMP0, $ACC5, $ACC5
+	vpmuludq	32*7-128($aap), $B2, $ACC6
+	vpaddq		32*15-448($tp1), $ACC6, $ACC6
+
+	vpmuludq	32*8-128($ap), $ACC0, $ACC7
+	vmovdqu		$ACC5, 32*14-448($tp1)
+	vpaddq		32*16-448($tp1), $ACC7, $ACC7
+	vmovdqu		$ACC6, 32*15-448($tp1)
+	vmovdqu		$ACC7, 32*16-448($tp1)
+	lea		8($tp1), $tp1
+
+	dec	$i        
+	jnz	.LOOP_SQR_1024
+___
+$ZERO = $ACC9;
+$TEMP0 = $B1;
+$TEMP2 = $B2;
+$TEMP3 = $Y1;
+$TEMP4 = $Y2;
+$code.=<<___;
+	# we need to fix indices 32-39 to avoid overflow
+	vmovdqu		32*8(%rsp), $ACC8		# 32*8-192($tp0),
+	vmovdqu		32*9(%rsp), $ACC1		# 32*9-192($tp0)
+	vmovdqu		32*10(%rsp), $ACC2		# 32*10-192($tp0)
+	lea		192(%rsp), $tp0			# 64+128=192
+
+	vpsrlq		\$29, $ACC8, $TEMP1
+	vpand		$AND_MASK, $ACC8, $ACC8
+	vpsrlq		\$29, $ACC1, $TEMP2
+	vpand		$AND_MASK, $ACC1, $ACC1
+
+	vpermq		\$0x93, $TEMP1, $TEMP1
+	vpxor		$ZERO, $ZERO, $ZERO
+	vpermq		\$0x93, $TEMP2, $TEMP2
+
+	vpblendd	\$3, $ZERO, $TEMP1, $TEMP0
+	vpblendd	\$3, $TEMP1, $TEMP2, $TEMP1
+	vpaddq		$TEMP0, $ACC8, $ACC8
+	vpblendd	\$3, $TEMP2, $ZERO, $TEMP2
+	vpaddq		$TEMP1, $ACC1, $ACC1
+	vpaddq		$TEMP2, $ACC2, $ACC2
+	vmovdqu		$ACC1, 32*9-192($tp0)
+	vmovdqu		$ACC2, 32*10-192($tp0)
+
+	mov	(%rsp), %rax
+	mov	8(%rsp), $r1
+	mov	16(%rsp), $r2
+	mov	24(%rsp), $r3
+	vmovdqu	32*1(%rsp), $ACC1
+	vmovdqu	32*2-192($tp0), $ACC2
+	vmovdqu	32*3-192($tp0), $ACC3
+	vmovdqu	32*4-192($tp0), $ACC4
+	vmovdqu	32*5-192($tp0), $ACC5
+	vmovdqu	32*6-192($tp0), $ACC6
+	vmovdqu	32*7-192($tp0), $ACC7
+
+	mov	%rax, $r0
+	imull	$n0, %eax
+	and	\$0x1fffffff, %eax
+	vmovd	%eax, $Y1
+
+	mov	%rax, %rdx
+	imulq	-128($np), %rax
+	 vpbroadcastq	$Y1, $Y1
+	add	%rax, $r0
+	mov	%rdx, %rax
+	imulq	8-128($np), %rax
+	shr	\$29, $r0
+	add	%rax, $r1
+	mov	%rdx, %rax
+	imulq	16-128($np), %rax
+	add	$r0, $r1
+	add	%rax, $r2
+	imulq	24-128($np), %rdx
+	add	%rdx, $r3
+
+	mov	$r1, %rax
+	imull	$n0, %eax
+	and	\$0x1fffffff, %eax
+
+	mov \$9, $i
+	jmp .LOOP_REDUCE_1024
+
+.align	32
+.LOOP_REDUCE_1024:
+	vmovd	%eax, $Y2
+	vpbroadcastq	$Y2, $Y2
+
+	vpmuludq	32*1-128($np), $Y1, $TEMP0
+	 mov	%rax, %rdx
+	 imulq	-128($np), %rax
+	vpaddq		$TEMP0, $ACC1, $ACC1
+	 add	%rax, $r1
+	vpmuludq	32*2-128($np), $Y1, $TEMP1
+	 mov	%rdx, %rax
+	 imulq	8-128($np), %rax
+	vpaddq		$TEMP1, $ACC2, $ACC2
+	vpmuludq	32*3-128($np), $Y1, $TEMP2
+	 .byte	0x67
+	 add	%rax, $r2
+	 .byte	0x67
+	 mov	%rdx, %rax
+	 imulq	16-128($np), %rax
+	 shr	\$29, $r1
+	vpaddq		$TEMP2, $ACC3, $ACC3
+	vpmuludq	32*4-128($np), $Y1, $TEMP0
+	 add	%rax, $r3
+	 add	$r1, $r2
+	vpaddq		$TEMP0, $ACC4, $ACC4
+	vpmuludq	32*5-128($np), $Y1, $TEMP1
+	 mov	$r2, %rax
+	 imull	$n0, %eax
+	vpaddq		$TEMP1, $ACC5, $ACC5
+	vpmuludq	32*6-128($np), $Y1, $TEMP2
+	 and	\$0x1fffffff, %eax
+	vpaddq		$TEMP2, $ACC6, $ACC6
+	vpmuludq	32*7-128($np), $Y1, $TEMP0
+	vpaddq		$TEMP0, $ACC7, $ACC7
+	vpmuludq	32*8-128($np), $Y1, $TEMP1
+	 vmovd	%eax, $Y1
+	 #vmovdqu	32*1-8-128($np), $TEMP2		# moved below
+	vpaddq		$TEMP1, $ACC8, $ACC8
+	 #vmovdqu	32*2-8-128($np), $TEMP0		# moved below
+	 vpbroadcastq	$Y1, $Y1
+
+	vpmuludq	32*1-8-128($np), $Y2, $TEMP2	# see above
+	vmovdqu		32*3-8-128($np), $TEMP1
+	 mov	%rax, %rdx
+	 imulq	-128($np), %rax
+	vpaddq		$TEMP2, $ACC1, $ACC1
+	vpmuludq	32*2-8-128($np), $Y2, $TEMP0	# see above
+	vmovdqu		32*4-8-128($np), $TEMP2
+	 add	%rax, $r2
+	 mov	%rdx, %rax
+	 imulq	8-128($np), %rax
+	vpaddq		$TEMP0, $ACC2, $ACC2
+	 add	$r3, %rax
+	 shr	\$29, $r2
+	vpmuludq	$Y2, $TEMP1, $TEMP1
+	vmovdqu		32*5-8-128($np), $TEMP0
+	 add	$r2, %rax
+	vpaddq		$TEMP1, $ACC3, $ACC3
+	vpmuludq	$Y2, $TEMP2, $TEMP2
+	vmovdqu		32*6-8-128($np), $TEMP1
+	 .byte	0x67
+	 mov	%rax, $r3
+	 imull	$n0, %eax
+	vpaddq		$TEMP2, $ACC4, $ACC4
+	vpmuludq	$Y2, $TEMP0, $TEMP0
+	.byte	0xc4,0x41,0x7e,0x6f,0x9d,0x58,0x00,0x00,0x00	# vmovdqu		32*7-8-128($np), $TEMP2
+	 and	\$0x1fffffff, %eax
+	vpaddq		$TEMP0, $ACC5, $ACC5
+	vpmuludq	$Y2, $TEMP1, $TEMP1
+	vmovdqu		32*8-8-128($np), $TEMP0
+	vpaddq		$TEMP1, $ACC6, $ACC6
+	vpmuludq	$Y2, $TEMP2, $TEMP2
+	vmovdqu		32*9-8-128($np), $ACC9
+	 vmovd	%eax, $ACC0			# borrow ACC0 for Y2
+	 imulq	-128($np), %rax
+	vpaddq		$TEMP2, $ACC7, $ACC7
+	vpmuludq	$Y2, $TEMP0, $TEMP0
+	 vmovdqu	32*1-16-128($np), $TEMP1
+	 vpbroadcastq	$ACC0, $ACC0
+	vpaddq		$TEMP0, $ACC8, $ACC8
+	vpmuludq	$Y2, $ACC9, $ACC9
+	 vmovdqu	32*2-16-128($np), $TEMP2
+	 add	%rax, $r3
+
+___
+($ACC0,$Y2)=($Y2,$ACC0);
+$code.=<<___;
+	 vmovdqu	32*1-24-128($np), $ACC0
+	vpmuludq	$Y1, $TEMP1, $TEMP1
+	vmovdqu		32*3-16-128($np), $TEMP0
+	vpaddq		$TEMP1, $ACC1, $ACC1
+	 vpmuludq	$Y2, $ACC0, $ACC0
+	vpmuludq	$Y1, $TEMP2, $TEMP2
+	.byte	0xc4,0x41,0x7e,0x6f,0xb5,0xf0,0xff,0xff,0xff	# vmovdqu		32*4-16-128($np), $TEMP1
+	 vpaddq		$ACC1, $ACC0, $ACC0
+	vpaddq		$TEMP2, $ACC2, $ACC2
+	vpmuludq	$Y1, $TEMP0, $TEMP0
+	vmovdqu		32*5-16-128($np), $TEMP2
+	 .byte	0x67
+	 vmovq		$ACC0, %rax
+	 vmovdqu	$ACC0, (%rsp)		# transfer $r0-$r3
+	vpaddq		$TEMP0, $ACC3, $ACC3
+	vpmuludq	$Y1, $TEMP1, $TEMP1
+	vmovdqu		32*6-16-128($np), $TEMP0
+	vpaddq		$TEMP1, $ACC4, $ACC4
+	vpmuludq	$Y1, $TEMP2, $TEMP2
+	vmovdqu		32*7-16-128($np), $TEMP1
+	vpaddq		$TEMP2, $ACC5, $ACC5
+	vpmuludq	$Y1, $TEMP0, $TEMP0
+	vmovdqu		32*8-16-128($np), $TEMP2
+	vpaddq		$TEMP0, $ACC6, $ACC6
+	vpmuludq	$Y1, $TEMP1, $TEMP1
+	 shr	\$29, $r3
+	vmovdqu		32*9-16-128($np), $TEMP0
+	 add	$r3, %rax
+	vpaddq		$TEMP1, $ACC7, $ACC7
+	vpmuludq	$Y1, $TEMP2, $TEMP2
+	 #vmovdqu	32*2-24-128($np), $TEMP1	# moved below
+	 mov	%rax, $r0
+	 imull	$n0, %eax
+	vpaddq		$TEMP2, $ACC8, $ACC8
+	vpmuludq	$Y1, $TEMP0, $TEMP0
+	 and	\$0x1fffffff, %eax
+	 vmovd	%eax, $Y1
+	 vmovdqu	32*3-24-128($np), $TEMP2
+	.byte	0x67
+	vpaddq		$TEMP0, $ACC9, $ACC9
+	 vpbroadcastq	$Y1, $Y1
+
+	vpmuludq	32*2-24-128($np), $Y2, $TEMP1	# see above
+	vmovdqu		32*4-24-128($np), $TEMP0
+	 mov	%rax, %rdx
+	 imulq	-128($np), %rax
+	 mov	8(%rsp), $r1
+	vpaddq		$TEMP1, $ACC2, $ACC1
+	vpmuludq	$Y2, $TEMP2, $TEMP2
+	vmovdqu		32*5-24-128($np), $TEMP1
+	 add	%rax, $r0
+	 mov	%rdx, %rax
+	 imulq	8-128($np), %rax
+	 .byte	0x67
+	 shr	\$29, $r0
+	 mov	16(%rsp), $r2
+	vpaddq		$TEMP2, $ACC3, $ACC2
+	vpmuludq	$Y2, $TEMP0, $TEMP0
+	vmovdqu		32*6-24-128($np), $TEMP2
+	 add	%rax, $r1
+	 mov	%rdx, %rax
+	 imulq	16-128($np), %rax
+	vpaddq		$TEMP0, $ACC4, $ACC3
+	vpmuludq	$Y2, $TEMP1, $TEMP1
+	vmovdqu		32*7-24-128($np), $TEMP0
+	 imulq	24-128($np), %rdx		# future $r3
+	 add	%rax, $r2
+	 lea	($r0,$r1), %rax
+	vpaddq		$TEMP1, $ACC5, $ACC4
+	vpmuludq	$Y2, $TEMP2, $TEMP2
+	vmovdqu		32*8-24-128($np), $TEMP1
+	 mov	%rax, $r1
+	 imull	$n0, %eax
+	vpmuludq	$Y2, $TEMP0, $TEMP0
+	vpaddq		$TEMP2, $ACC6, $ACC5
+	vmovdqu		32*9-24-128($np), $TEMP2
+	 and	\$0x1fffffff, %eax
+	vpaddq		$TEMP0, $ACC7, $ACC6
+	vpmuludq	$Y2, $TEMP1, $TEMP1
+	 add	24(%rsp), %rdx
+	vpaddq		$TEMP1, $ACC8, $ACC7
+	vpmuludq	$Y2, $TEMP2, $TEMP2
+	vpaddq		$TEMP2, $ACC9, $ACC8
+	 vmovq	$r3, $ACC9
+	 mov	%rdx, $r3
+
+	dec	$i
+	jnz	.LOOP_REDUCE_1024
+___
+($ACC0,$Y2)=($Y2,$ACC0);
+$code.=<<___;
+	lea	448(%rsp), $tp1			# size optimization
+	vpaddq	$ACC9, $Y2, $ACC0
+	vpxor	$ZERO, $ZERO, $ZERO
+
+	vpaddq		32*9-192($tp0), $ACC0, $ACC0
+	vpaddq		32*10-448($tp1), $ACC1, $ACC1
+	vpaddq		32*11-448($tp1), $ACC2, $ACC2
+	vpaddq		32*12-448($tp1), $ACC3, $ACC3
+	vpaddq		32*13-448($tp1), $ACC4, $ACC4
+	vpaddq		32*14-448($tp1), $ACC5, $ACC5
+	vpaddq		32*15-448($tp1), $ACC6, $ACC6
+	vpaddq		32*16-448($tp1), $ACC7, $ACC7
+	vpaddq		32*17-448($tp1), $ACC8, $ACC8
+
+	vpsrlq		\$29, $ACC0, $TEMP1
+	vpand		$AND_MASK, $ACC0, $ACC0
+	vpsrlq		\$29, $ACC1, $TEMP2
+	vpand		$AND_MASK, $ACC1, $ACC1
+	vpsrlq		\$29, $ACC2, $TEMP3
+	vpermq		\$0x93, $TEMP1, $TEMP1
+	vpand		$AND_MASK, $ACC2, $ACC2
+	vpsrlq		\$29, $ACC3, $TEMP4
+	vpermq		\$0x93, $TEMP2, $TEMP2
+	vpand		$AND_MASK, $ACC3, $ACC3
+	vpermq		\$0x93, $TEMP3, $TEMP3
+
+	vpblendd	\$3, $ZERO, $TEMP1, $TEMP0
+	vpermq		\$0x93, $TEMP4, $TEMP4
+	vpblendd	\$3, $TEMP1, $TEMP2, $TEMP1
+	vpaddq		$TEMP0, $ACC0, $ACC0
+	vpblendd	\$3, $TEMP2, $TEMP3, $TEMP2
+	vpaddq		$TEMP1, $ACC1, $ACC1
+	vpblendd	\$3, $TEMP3, $TEMP4, $TEMP3
+	vpaddq		$TEMP2, $ACC2, $ACC2
+	vpblendd	\$3, $TEMP4, $ZERO, $TEMP4
+	vpaddq		$TEMP3, $ACC3, $ACC3
+	vpaddq		$TEMP4, $ACC4, $ACC4
+
+	vpsrlq		\$29, $ACC0, $TEMP1
+	vpand		$AND_MASK, $ACC0, $ACC0
+	vpsrlq		\$29, $ACC1, $TEMP2
+	vpand		$AND_MASK, $ACC1, $ACC1
+	vpsrlq		\$29, $ACC2, $TEMP3
+	vpermq		\$0x93, $TEMP1, $TEMP1
+	vpand		$AND_MASK, $ACC2, $ACC2
+	vpsrlq		\$29, $ACC3, $TEMP4
+	vpermq		\$0x93, $TEMP2, $TEMP2
+	vpand		$AND_MASK, $ACC3, $ACC3
+	vpermq		\$0x93, $TEMP3, $TEMP3
+
+	vpblendd	\$3, $ZERO, $TEMP1, $TEMP0
+	vpermq		\$0x93, $TEMP4, $TEMP4
+	vpblendd	\$3, $TEMP1, $TEMP2, $TEMP1
+	vpaddq		$TEMP0, $ACC0, $ACC0
+	vpblendd	\$3, $TEMP2, $TEMP3, $TEMP2
+	vpaddq		$TEMP1, $ACC1, $ACC1
+	vmovdqu		$ACC0, 32*0-128($rp)
+	vpblendd	\$3, $TEMP3, $TEMP4, $TEMP3
+	vpaddq		$TEMP2, $ACC2, $ACC2
+	vmovdqu		$ACC1, 32*1-128($rp)
+	vpblendd	\$3, $TEMP4, $ZERO, $TEMP4
+	vpaddq		$TEMP3, $ACC3, $ACC3
+	vmovdqu		$ACC2, 32*2-128($rp)
+	vpaddq		$TEMP4, $ACC4, $ACC4
+	vmovdqu		$ACC3, 32*3-128($rp)
+___
+$TEMP5=$ACC0;
+$code.=<<___;
+	vpsrlq		\$29, $ACC4, $TEMP1
+	vpand		$AND_MASK, $ACC4, $ACC4
+	vpsrlq		\$29, $ACC5, $TEMP2
+	vpand		$AND_MASK, $ACC5, $ACC5
+	vpsrlq		\$29, $ACC6, $TEMP3
+	vpermq		\$0x93, $TEMP1, $TEMP1
+	vpand		$AND_MASK, $ACC6, $ACC6
+	vpsrlq		\$29, $ACC7, $TEMP4
+	vpermq		\$0x93, $TEMP2, $TEMP2
+	vpand		$AND_MASK, $ACC7, $ACC7
+	vpsrlq		\$29, $ACC8, $TEMP5
+	vpermq		\$0x93, $TEMP3, $TEMP3
+	vpand		$AND_MASK, $ACC8, $ACC8
+	vpermq		\$0x93, $TEMP4, $TEMP4
+
+	vpblendd	\$3, $ZERO, $TEMP1, $TEMP0
+	vpermq		\$0x93, $TEMP5, $TEMP5
+	vpblendd	\$3, $TEMP1, $TEMP2, $TEMP1
+	vpaddq		$TEMP0, $ACC4, $ACC4
+	vpblendd	\$3, $TEMP2, $TEMP3, $TEMP2
+	vpaddq		$TEMP1, $ACC5, $ACC5
+	vpblendd	\$3, $TEMP3, $TEMP4, $TEMP3
+	vpaddq		$TEMP2, $ACC6, $ACC6
+	vpblendd	\$3, $TEMP4, $TEMP5, $TEMP4
+	vpaddq		$TEMP3, $ACC7, $ACC7
+	vpaddq		$TEMP4, $ACC8, $ACC8
+     
+	vpsrlq		\$29, $ACC4, $TEMP1
+	vpand		$AND_MASK, $ACC4, $ACC4
+	vpsrlq		\$29, $ACC5, $TEMP2
+	vpand		$AND_MASK, $ACC5, $ACC5
+	vpsrlq		\$29, $ACC6, $TEMP3
+	vpermq		\$0x93, $TEMP1, $TEMP1
+	vpand		$AND_MASK, $ACC6, $ACC6
+	vpsrlq		\$29, $ACC7, $TEMP4
+	vpermq		\$0x93, $TEMP2, $TEMP2
+	vpand		$AND_MASK, $ACC7, $ACC7
+	vpsrlq		\$29, $ACC8, $TEMP5
+	vpermq		\$0x93, $TEMP3, $TEMP3
+	vpand		$AND_MASK, $ACC8, $ACC8
+	vpermq		\$0x93, $TEMP4, $TEMP4
+
+	vpblendd	\$3, $ZERO, $TEMP1, $TEMP0
+	vpermq		\$0x93, $TEMP5, $TEMP5
+	vpblendd	\$3, $TEMP1, $TEMP2, $TEMP1
+	vpaddq		$TEMP0, $ACC4, $ACC4
+	vpblendd	\$3, $TEMP2, $TEMP3, $TEMP2
+	vpaddq		$TEMP1, $ACC5, $ACC5
+	vmovdqu		$ACC4, 32*4-128($rp)
+	vpblendd	\$3, $TEMP3, $TEMP4, $TEMP3
+	vpaddq		$TEMP2, $ACC6, $ACC6
+	vmovdqu		$ACC5, 32*5-128($rp)
+	vpblendd	\$3, $TEMP4, $TEMP5, $TEMP4
+	vpaddq		$TEMP3, $ACC7, $ACC7
+	vmovdqu		$ACC6, 32*6-128($rp)
+	vpaddq		$TEMP4, $ACC8, $ACC8
+	vmovdqu		$ACC7, 32*7-128($rp)
+	vmovdqu		$ACC8, 32*8-128($rp)
+
+	mov	$rp, $ap
+	dec	$rep
+	jne	.LOOP_GRANDE_SQR_1024
+
+	vzeroall
+	mov	%rbp, %rax
+___
+$code.=<<___ if ($win64);
+	movaps	-0xd8(%rax),%xmm6
+	movaps	-0xc8(%rax),%xmm7
+	movaps	-0xb8(%rax),%xmm8
+	movaps	-0xa8(%rax),%xmm9
+	movaps	-0x98(%rax),%xmm10
+	movaps	-0x88(%rax),%xmm11
+	movaps	-0x78(%rax),%xmm12
+	movaps	-0x68(%rax),%xmm13
+	movaps	-0x58(%rax),%xmm14
+	movaps	-0x48(%rax),%xmm15
+___
+$code.=<<___;
+	mov	-48(%rax),%r15
+	mov	-40(%rax),%r14
+	mov	-32(%rax),%r13
+	mov	-24(%rax),%r12
+	mov	-16(%rax),%rbp
+	mov	-8(%rax),%rbx
+	lea	(%rax),%rsp		# restore %rsp
+.Lsqr_1024_epilogue:
+	ret
+.size	rsaz_1024_sqr_avx2,.-rsaz_1024_sqr_avx2
+___
+}
+
+{ # void AMM_WW(
+my $rp="%rdi";	# BN_ULONG *rp,
+my $ap="%rsi";	# const BN_ULONG *ap,
+my $bp="%rdx";	# const BN_ULONG *bp,
+my $np="%rcx";	# const BN_ULONG *np,
+my $n0="%r8d";	# unsigned int n0);
+
+# The registers that hold the accumulated redundant result
+# The AMM works on 1024 bit operands, and redundant word size is 29
+# Therefore: ceil(1024/29)/4 = 9
+my $ACC0="%ymm0";
+my $ACC1="%ymm1";
+my $ACC2="%ymm2";
+my $ACC3="%ymm3";
+my $ACC4="%ymm4";
+my $ACC5="%ymm5";
+my $ACC6="%ymm6";
+my $ACC7="%ymm7";
+my $ACC8="%ymm8";
+my $ACC9="%ymm9";
+
+# Registers that hold the broadcasted words of multiplier, currently used
+my $Bi="%ymm10";
+my $Yi="%ymm11";
+
+# Helper registers
+my $TEMP0=$ACC0;
+my $TEMP1="%ymm12";
+my $TEMP2="%ymm13";
+my $ZERO="%ymm14";
+my $AND_MASK="%ymm15";
+
+# alu registers that hold the first words of the ACC
+my $r0="%r9";
+my $r1="%r10";
+my $r2="%r11";
+my $r3="%r12";
+
+my $i="%r14d";
+my $tmp="%r15";
+
+$bp="%r13";	# reassigned argument
+
+$code.=<<___;
+.globl	rsaz_1024_mul_avx2
+.type	rsaz_1024_mul_avx2,\@function,5
+.align	64
+rsaz_1024_mul_avx2:
+	lea	(%rsp), %rax
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+___
+$code.=<<___ if ($win64);
+	vzeroupper
+	lea	-0xa8(%rsp),%rsp
+	vmovaps	%xmm6,-0xd8(%rax)
+	vmovaps	%xmm7,-0xc8(%rax)
+	vmovaps	%xmm8,-0xb8(%rax)
+	vmovaps	%xmm9,-0xa8(%rax)
+	vmovaps	%xmm10,-0x98(%rax)
+	vmovaps	%xmm11,-0x88(%rax)
+	vmovaps	%xmm12,-0x78(%rax)
+	vmovaps	%xmm13,-0x68(%rax)
+	vmovaps	%xmm14,-0x58(%rax)
+	vmovaps	%xmm15,-0x48(%rax)
+.Lmul_1024_body:
+___
+$code.=<<___;
+	mov	%rax,%rbp
+	vzeroall
+	mov	%rdx, $bp	# reassigned argument
+	sub	\$64,%rsp
+
+	# unaligned 256-bit load that crosses page boundary can
+	# cause severe performance degradation here, so if $ap does
+	# cross page boundary, swap it with $bp [meaning that caller
+	# is advised to lay down $ap and $bp next to each other, so
+	# that only one can cross page boundary].
+	.byte	0x67,0x67
+	mov	$ap, $tmp
+	and	\$4095, $tmp
+	add	\$32*10, $tmp
+	shr	\$12, $tmp
+	mov	$ap, $tmp
+	cmovnz	$bp, $ap
+	cmovnz	$tmp, $bp
+
+	mov	$np, $tmp
+	sub	\$-128,$ap	# size optimization
+	sub	\$-128,$np
+	sub	\$-128,$rp
+
+	and	\$4095, $tmp	# see if $np crosses page
+	add	\$32*10, $tmp
+	.byte	0x67,0x67
+	shr	\$12, $tmp
+	jz	.Lmul_1024_no_n_copy
+
+	# unaligned 256-bit load that crosses page boundary can
+	# cause severe performance degradation here, so if $np does
+	# cross page boundary, copy it to stack and make sure stack
+	# frame doesn't...
+	sub		\$32*10,%rsp
+	vmovdqu		32*0-128($np), $ACC0
+	and		\$-512, %rsp
+	vmovdqu		32*1-128($np), $ACC1
+	vmovdqu		32*2-128($np), $ACC2
+	vmovdqu		32*3-128($np), $ACC3
+	vmovdqu		32*4-128($np), $ACC4
+	vmovdqu		32*5-128($np), $ACC5
+	vmovdqu		32*6-128($np), $ACC6
+	vmovdqu		32*7-128($np), $ACC7
+	vmovdqu		32*8-128($np), $ACC8
+	lea		64+128(%rsp),$np
+	vmovdqu		$ACC0, 32*0-128($np)
+	vpxor		$ACC0, $ACC0, $ACC0
+	vmovdqu		$ACC1, 32*1-128($np)
+	vpxor		$ACC1, $ACC1, $ACC1
+	vmovdqu		$ACC2, 32*2-128($np)
+	vpxor		$ACC2, $ACC2, $ACC2
+	vmovdqu		$ACC3, 32*3-128($np)
+	vpxor		$ACC3, $ACC3, $ACC3
+	vmovdqu		$ACC4, 32*4-128($np)
+	vpxor		$ACC4, $ACC4, $ACC4
+	vmovdqu		$ACC5, 32*5-128($np)
+	vpxor		$ACC5, $ACC5, $ACC5
+	vmovdqu		$ACC6, 32*6-128($np)
+	vpxor		$ACC6, $ACC6, $ACC6
+	vmovdqu		$ACC7, 32*7-128($np)
+	vpxor		$ACC7, $ACC7, $ACC7
+	vmovdqu		$ACC8, 32*8-128($np)
+	vmovdqa		$ACC0, $ACC8
+	vmovdqu		$ACC9, 32*9-128($np)	# $ACC9 is zero after vzeroall
+.Lmul_1024_no_n_copy:
+	and	\$-64,%rsp
+
+	mov	($bp), %rbx
+	vpbroadcastq ($bp), $Bi
+	vmovdqu	$ACC0, (%rsp)			# clear top of stack
+	xor	$r0, $r0
+	.byte	0x67
+	xor	$r1, $r1
+	xor	$r2, $r2
+	xor	$r3, $r3
+
+	vmovdqu	.Land_mask(%rip), $AND_MASK
+	mov	\$9, $i
+	vmovdqu	$ACC9, 32*9-128($rp)		# $ACC9 is zero after vzeroall
+	jmp	.Loop_mul_1024
+
+.align	32
+.Loop_mul_1024:
+	 vpsrlq		\$29, $ACC3, $ACC9		# correct $ACC3(*)
+	mov	%rbx, %rax
+	imulq	-128($ap), %rax
+	add	$r0, %rax
+	mov	%rbx, $r1
+	imulq	8-128($ap), $r1
+	add	8(%rsp), $r1
+
+	mov	%rax, $r0
+	imull	$n0, %eax
+	and	\$0x1fffffff, %eax
+
+	 mov	%rbx, $r2
+	 imulq	16-128($ap), $r2
+	 add	16(%rsp), $r2
+
+	 mov	%rbx, $r3
+	 imulq	24-128($ap), $r3
+	 add	24(%rsp), $r3
+	vpmuludq	32*1-128($ap),$Bi,$TEMP0
+	 vmovd		%eax, $Yi
+	vpaddq		$TEMP0,$ACC1,$ACC1
+	vpmuludq	32*2-128($ap),$Bi,$TEMP1
+	 vpbroadcastq	$Yi, $Yi
+	vpaddq		$TEMP1,$ACC2,$ACC2
+	vpmuludq	32*3-128($ap),$Bi,$TEMP2
+	 vpand		$AND_MASK, $ACC3, $ACC3		# correct $ACC3
+	vpaddq		$TEMP2,$ACC3,$ACC3
+	vpmuludq	32*4-128($ap),$Bi,$TEMP0
+	vpaddq		$TEMP0,$ACC4,$ACC4
+	vpmuludq	32*5-128($ap),$Bi,$TEMP1
+	vpaddq		$TEMP1,$ACC5,$ACC5
+	vpmuludq	32*6-128($ap),$Bi,$TEMP2
+	vpaddq		$TEMP2,$ACC6,$ACC6
+	vpmuludq	32*7-128($ap),$Bi,$TEMP0
+	 vpermq		\$0x93, $ACC9, $ACC9		# correct $ACC3
+	vpaddq		$TEMP0,$ACC7,$ACC7
+	vpmuludq	32*8-128($ap),$Bi,$TEMP1
+	 vpbroadcastq	8($bp), $Bi
+	vpaddq		$TEMP1,$ACC8,$ACC8
+
+	mov	%rax,%rdx
+	imulq	-128($np),%rax
+	add	%rax,$r0
+	mov	%rdx,%rax
+	imulq	8-128($np),%rax
+	add	%rax,$r1
+	mov	%rdx,%rax
+	imulq	16-128($np),%rax
+	add	%rax,$r2
+	shr	\$29, $r0
+	imulq	24-128($np),%rdx
+	add	%rdx,$r3
+	add	$r0, $r1
+
+	vpmuludq	32*1-128($np),$Yi,$TEMP2
+	 vmovq		$Bi, %rbx
+	vpaddq		$TEMP2,$ACC1,$ACC1
+	vpmuludq	32*2-128($np),$Yi,$TEMP0
+	vpaddq		$TEMP0,$ACC2,$ACC2
+	vpmuludq	32*3-128($np),$Yi,$TEMP1
+	vpaddq		$TEMP1,$ACC3,$ACC3
+	vpmuludq	32*4-128($np),$Yi,$TEMP2
+	vpaddq		$TEMP2,$ACC4,$ACC4
+	vpmuludq	32*5-128($np),$Yi,$TEMP0
+	vpaddq		$TEMP0,$ACC5,$ACC5
+	vpmuludq	32*6-128($np),$Yi,$TEMP1
+	vpaddq		$TEMP1,$ACC6,$ACC6
+	vpmuludq	32*7-128($np),$Yi,$TEMP2
+	 vpblendd	\$3, $ZERO, $ACC9, $ACC9	# correct $ACC3
+	vpaddq		$TEMP2,$ACC7,$ACC7
+	vpmuludq	32*8-128($np),$Yi,$TEMP0
+	 vpaddq		$ACC9, $ACC3, $ACC3		# correct $ACC3
+	vpaddq		$TEMP0,$ACC8,$ACC8
+
+	mov	%rbx, %rax
+	imulq	-128($ap),%rax
+	add	%rax,$r1
+	 vmovdqu	-8+32*1-128($ap),$TEMP1
+	mov	%rbx, %rax
+	imulq	8-128($ap),%rax
+	add	%rax,$r2
+	 vmovdqu	-8+32*2-128($ap),$TEMP2
+
+	mov	$r1, %rax
+	imull	$n0, %eax
+	and	\$0x1fffffff, %eax
+
+	 imulq	16-128($ap),%rbx
+	 add	%rbx,$r3
+	vpmuludq	$Bi,$TEMP1,$TEMP1
+	 vmovd		%eax, $Yi
+	vmovdqu		-8+32*3-128($ap),$TEMP0
+	vpaddq		$TEMP1,$ACC1,$ACC1
+	vpmuludq	$Bi,$TEMP2,$TEMP2
+	 vpbroadcastq	$Yi, $Yi
+	vmovdqu		-8+32*4-128($ap),$TEMP1
+	vpaddq		$TEMP2,$ACC2,$ACC2
+	vpmuludq	$Bi,$TEMP0,$TEMP0
+	vmovdqu		-8+32*5-128($ap),$TEMP2
+	vpaddq		$TEMP0,$ACC3,$ACC3
+	vpmuludq	$Bi,$TEMP1,$TEMP1
+	vmovdqu		-8+32*6-128($ap),$TEMP0
+	vpaddq		$TEMP1,$ACC4,$ACC4
+	vpmuludq	$Bi,$TEMP2,$TEMP2
+	vmovdqu		-8+32*7-128($ap),$TEMP1
+	vpaddq		$TEMP2,$ACC5,$ACC5
+	vpmuludq	$Bi,$TEMP0,$TEMP0
+	vmovdqu		-8+32*8-128($ap),$TEMP2
+	vpaddq		$TEMP0,$ACC6,$ACC6
+	vpmuludq	$Bi,$TEMP1,$TEMP1
+	vmovdqu		-8+32*9-128($ap),$ACC9
+	vpaddq		$TEMP1,$ACC7,$ACC7
+	vpmuludq	$Bi,$TEMP2,$TEMP2
+	vpaddq		$TEMP2,$ACC8,$ACC8
+	vpmuludq	$Bi,$ACC9,$ACC9
+	 vpbroadcastq	16($bp), $Bi
+
+	mov	%rax,%rdx
+	imulq	-128($np),%rax
+	add	%rax,$r1
+	 vmovdqu	-8+32*1-128($np),$TEMP0
+	mov	%rdx,%rax
+	imulq	8-128($np),%rax
+	add	%rax,$r2
+	 vmovdqu	-8+32*2-128($np),$TEMP1
+	shr	\$29, $r1
+	imulq	16-128($np),%rdx
+	add	%rdx,$r3
+	add	$r1, $r2
+
+	vpmuludq	$Yi,$TEMP0,$TEMP0
+	 vmovq		$Bi, %rbx
+	vmovdqu		-8+32*3-128($np),$TEMP2
+	vpaddq		$TEMP0,$ACC1,$ACC1
+	vpmuludq	$Yi,$TEMP1,$TEMP1
+	vmovdqu		-8+32*4-128($np),$TEMP0
+	vpaddq		$TEMP1,$ACC2,$ACC2
+	vpmuludq	$Yi,$TEMP2,$TEMP2
+	vmovdqu		-8+32*5-128($np),$TEMP1
+	vpaddq		$TEMP2,$ACC3,$ACC3
+	vpmuludq	$Yi,$TEMP0,$TEMP0
+	vmovdqu		-8+32*6-128($np),$TEMP2
+	vpaddq		$TEMP0,$ACC4,$ACC4
+	vpmuludq	$Yi,$TEMP1,$TEMP1
+	vmovdqu		-8+32*7-128($np),$TEMP0
+	vpaddq		$TEMP1,$ACC5,$ACC5
+	vpmuludq	$Yi,$TEMP2,$TEMP2
+	vmovdqu		-8+32*8-128($np),$TEMP1
+	vpaddq		$TEMP2,$ACC6,$ACC6
+	vpmuludq	$Yi,$TEMP0,$TEMP0
+	vmovdqu		-8+32*9-128($np),$TEMP2
+	vpaddq		$TEMP0,$ACC7,$ACC7
+	vpmuludq	$Yi,$TEMP1,$TEMP1
+	vpaddq		$TEMP1,$ACC8,$ACC8
+	vpmuludq	$Yi,$TEMP2,$TEMP2
+	vpaddq		$TEMP2,$ACC9,$ACC9
+
+	 vmovdqu	-16+32*1-128($ap),$TEMP0
+	mov	%rbx,%rax
+	imulq	-128($ap),%rax
+	add	$r2,%rax
+
+	 vmovdqu	-16+32*2-128($ap),$TEMP1
+	mov	%rax,$r2
+	imull	$n0, %eax
+	and	\$0x1fffffff, %eax
+
+	 imulq	8-128($ap),%rbx
+	 add	%rbx,$r3
+	vpmuludq	$Bi,$TEMP0,$TEMP0
+	 vmovd		%eax, $Yi
+	vmovdqu		-16+32*3-128($ap),$TEMP2
+	vpaddq		$TEMP0,$ACC1,$ACC1
+	vpmuludq	$Bi,$TEMP1,$TEMP1
+	 vpbroadcastq	$Yi, $Yi
+	vmovdqu		-16+32*4-128($ap),$TEMP0
+	vpaddq		$TEMP1,$ACC2,$ACC2
+	vpmuludq	$Bi,$TEMP2,$TEMP2
+	vmovdqu		-16+32*5-128($ap),$TEMP1
+	vpaddq		$TEMP2,$ACC3,$ACC3
+	vpmuludq	$Bi,$TEMP0,$TEMP0
+	vmovdqu		-16+32*6-128($ap),$TEMP2
+	vpaddq		$TEMP0,$ACC4,$ACC4
+	vpmuludq	$Bi,$TEMP1,$TEMP1
+	vmovdqu		-16+32*7-128($ap),$TEMP0
+	vpaddq		$TEMP1,$ACC5,$ACC5
+	vpmuludq	$Bi,$TEMP2,$TEMP2
+	vmovdqu		-16+32*8-128($ap),$TEMP1
+	vpaddq		$TEMP2,$ACC6,$ACC6
+	vpmuludq	$Bi,$TEMP0,$TEMP0
+	vmovdqu		-16+32*9-128($ap),$TEMP2
+	vpaddq		$TEMP0,$ACC7,$ACC7
+	vpmuludq	$Bi,$TEMP1,$TEMP1
+	vpaddq		$TEMP1,$ACC8,$ACC8
+	vpmuludq	$Bi,$TEMP2,$TEMP2
+	 vpbroadcastq	24($bp), $Bi
+	vpaddq		$TEMP2,$ACC9,$ACC9
+
+	 vmovdqu	-16+32*1-128($np),$TEMP0
+	mov	%rax,%rdx
+	imulq	-128($np),%rax
+	add	%rax,$r2
+	 vmovdqu	-16+32*2-128($np),$TEMP1
+	imulq	8-128($np),%rdx
+	add	%rdx,$r3
+	shr	\$29, $r2
+
+	vpmuludq	$Yi,$TEMP0,$TEMP0
+	 vmovq		$Bi, %rbx
+	vmovdqu		-16+32*3-128($np),$TEMP2
+	vpaddq		$TEMP0,$ACC1,$ACC1
+	vpmuludq	$Yi,$TEMP1,$TEMP1
+	vmovdqu		-16+32*4-128($np),$TEMP0
+	vpaddq		$TEMP1,$ACC2,$ACC2
+	vpmuludq	$Yi,$TEMP2,$TEMP2
+	vmovdqu		-16+32*5-128($np),$TEMP1
+	vpaddq		$TEMP2,$ACC3,$ACC3
+	vpmuludq	$Yi,$TEMP0,$TEMP0
+	vmovdqu		-16+32*6-128($np),$TEMP2
+	vpaddq		$TEMP0,$ACC4,$ACC4
+	vpmuludq	$Yi,$TEMP1,$TEMP1
+	vmovdqu		-16+32*7-128($np),$TEMP0
+	vpaddq		$TEMP1,$ACC5,$ACC5
+	vpmuludq	$Yi,$TEMP2,$TEMP2
+	vmovdqu		-16+32*8-128($np),$TEMP1
+	vpaddq		$TEMP2,$ACC6,$ACC6
+	vpmuludq	$Yi,$TEMP0,$TEMP0
+	vmovdqu		-16+32*9-128($np),$TEMP2
+	vpaddq		$TEMP0,$ACC7,$ACC7
+	vpmuludq	$Yi,$TEMP1,$TEMP1
+	 vmovdqu	-24+32*1-128($ap),$TEMP0
+	vpaddq		$TEMP1,$ACC8,$ACC8
+	vpmuludq	$Yi,$TEMP2,$TEMP2
+	 vmovdqu	-24+32*2-128($ap),$TEMP1
+	vpaddq		$TEMP2,$ACC9,$ACC9
+
+	add	$r2, $r3
+	imulq	-128($ap),%rbx
+	add	%rbx,$r3
+
+	mov	$r3, %rax
+	imull	$n0, %eax
+	and	\$0x1fffffff, %eax
+
+	vpmuludq	$Bi,$TEMP0,$TEMP0
+	 vmovd		%eax, $Yi
+	vmovdqu		-24+32*3-128($ap),$TEMP2
+	vpaddq		$TEMP0,$ACC1,$ACC1
+	vpmuludq	$Bi,$TEMP1,$TEMP1
+	 vpbroadcastq	$Yi, $Yi
+	vmovdqu		-24+32*4-128($ap),$TEMP0
+	vpaddq		$TEMP1,$ACC2,$ACC2
+	vpmuludq	$Bi,$TEMP2,$TEMP2
+	vmovdqu		-24+32*5-128($ap),$TEMP1
+	vpaddq		$TEMP2,$ACC3,$ACC3
+	vpmuludq	$Bi,$TEMP0,$TEMP0
+	vmovdqu		-24+32*6-128($ap),$TEMP2
+	vpaddq		$TEMP0,$ACC4,$ACC4
+	vpmuludq	$Bi,$TEMP1,$TEMP1
+	vmovdqu		-24+32*7-128($ap),$TEMP0
+	vpaddq		$TEMP1,$ACC5,$ACC5
+	vpmuludq	$Bi,$TEMP2,$TEMP2
+	vmovdqu		-24+32*8-128($ap),$TEMP1
+	vpaddq		$TEMP2,$ACC6,$ACC6
+	vpmuludq	$Bi,$TEMP0,$TEMP0
+	vmovdqu		-24+32*9-128($ap),$TEMP2
+	vpaddq		$TEMP0,$ACC7,$ACC7
+	vpmuludq	$Bi,$TEMP1,$TEMP1
+	vpaddq		$TEMP1,$ACC8,$ACC8
+	vpmuludq	$Bi,$TEMP2,$TEMP2
+	 vpbroadcastq	32($bp), $Bi
+	vpaddq		$TEMP2,$ACC9,$ACC9
+	 add		\$32, $bp			# $bp++
+
+	vmovdqu		-24+32*1-128($np),$TEMP0
+	imulq	-128($np),%rax
+	add	%rax,$r3
+	shr	\$29, $r3
+
+	vmovdqu		-24+32*2-128($np),$TEMP1
+	vpmuludq	$Yi,$TEMP0,$TEMP0
+	 vmovq		$Bi, %rbx
+	vmovdqu		-24+32*3-128($np),$TEMP2
+	vpaddq		$TEMP0,$ACC1,$ACC0		# $ACC0==$TEMP0
+	vpmuludq	$Yi,$TEMP1,$TEMP1
+	 vmovdqu	$ACC0, (%rsp)			# transfer $r0-$r3
+	vpaddq		$TEMP1,$ACC2,$ACC1
+	vmovdqu		-24+32*4-128($np),$TEMP0
+	vpmuludq	$Yi,$TEMP2,$TEMP2
+	vmovdqu		-24+32*5-128($np),$TEMP1
+	vpaddq		$TEMP2,$ACC3,$ACC2
+	vpmuludq	$Yi,$TEMP0,$TEMP0
+	vmovdqu		-24+32*6-128($np),$TEMP2
+	vpaddq		$TEMP0,$ACC4,$ACC3
+	vpmuludq	$Yi,$TEMP1,$TEMP1
+	vmovdqu		-24+32*7-128($np),$TEMP0
+	vpaddq		$TEMP1,$ACC5,$ACC4
+	vpmuludq	$Yi,$TEMP2,$TEMP2
+	vmovdqu		-24+32*8-128($np),$TEMP1
+	vpaddq		$TEMP2,$ACC6,$ACC5
+	vpmuludq	$Yi,$TEMP0,$TEMP0
+	vmovdqu		-24+32*9-128($np),$TEMP2
+	 mov	$r3, $r0
+	vpaddq		$TEMP0,$ACC7,$ACC6
+	vpmuludq	$Yi,$TEMP1,$TEMP1
+	 add	(%rsp), $r0
+	vpaddq		$TEMP1,$ACC8,$ACC7
+	vpmuludq	$Yi,$TEMP2,$TEMP2
+	 vmovq	$r3, $TEMP1
+	vpaddq		$TEMP2,$ACC9,$ACC8
+
+	dec	$i
+	jnz	.Loop_mul_1024
+___
+
+# (*)	Original implementation was correcting ACC1-ACC3 for overflow
+#	after 7 loop runs, or after 28 iterations, or 56 additions.
+#	But as we underutilize resources, it's possible to correct in
+#	each iteration with marginal performance loss. But then, as
+#	we do it in each iteration, we can correct less digits, and
+#	avoid performance penalties completely. Also note that we
+#	correct only three digits out of four. This works because
+#	most significant digit is subjected to less additions.
+
+$TEMP0 = $ACC9;
+$TEMP3 = $Bi;
+$TEMP4 = $Yi;
+$code.=<<___;
+	vpermq		\$0, $AND_MASK, $AND_MASK
+	vpaddq		(%rsp), $TEMP1, $ACC0
+
+	vpsrlq		\$29, $ACC0, $TEMP1
+	vpand		$AND_MASK, $ACC0, $ACC0
+	vpsrlq		\$29, $ACC1, $TEMP2
+	vpand		$AND_MASK, $ACC1, $ACC1
+	vpsrlq		\$29, $ACC2, $TEMP3
+	vpermq		\$0x93, $TEMP1, $TEMP1
+	vpand		$AND_MASK, $ACC2, $ACC2
+	vpsrlq		\$29, $ACC3, $TEMP4
+	vpermq		\$0x93, $TEMP2, $TEMP2
+	vpand		$AND_MASK, $ACC3, $ACC3
+
+	vpblendd	\$3, $ZERO, $TEMP1, $TEMP0
+	vpermq		\$0x93, $TEMP3, $TEMP3
+	vpblendd	\$3, $TEMP1, $TEMP2, $TEMP1
+	vpermq		\$0x93, $TEMP4, $TEMP4
+	vpaddq		$TEMP0, $ACC0, $ACC0
+	vpblendd	\$3, $TEMP2, $TEMP3, $TEMP2
+	vpaddq		$TEMP1, $ACC1, $ACC1
+	vpblendd	\$3, $TEMP3, $TEMP4, $TEMP3
+	vpaddq		$TEMP2, $ACC2, $ACC2
+	vpblendd	\$3, $TEMP4, $ZERO, $TEMP4
+	vpaddq		$TEMP3, $ACC3, $ACC3
+	vpaddq		$TEMP4, $ACC4, $ACC4
+
+	vpsrlq		\$29, $ACC0, $TEMP1
+	vpand		$AND_MASK, $ACC0, $ACC0
+	vpsrlq		\$29, $ACC1, $TEMP2
+	vpand		$AND_MASK, $ACC1, $ACC1
+	vpsrlq		\$29, $ACC2, $TEMP3
+	vpermq		\$0x93, $TEMP1, $TEMP1
+	vpand		$AND_MASK, $ACC2, $ACC2
+	vpsrlq		\$29, $ACC3, $TEMP4
+	vpermq		\$0x93, $TEMP2, $TEMP2
+	vpand		$AND_MASK, $ACC3, $ACC3
+	vpermq		\$0x93, $TEMP3, $TEMP3
+
+	vpblendd	\$3, $ZERO, $TEMP1, $TEMP0
+	vpermq		\$0x93, $TEMP4, $TEMP4
+	vpblendd	\$3, $TEMP1, $TEMP2, $TEMP1
+	vpaddq		$TEMP0, $ACC0, $ACC0
+	vpblendd	\$3, $TEMP2, $TEMP3, $TEMP2
+	vpaddq		$TEMP1, $ACC1, $ACC1
+	vpblendd	\$3, $TEMP3, $TEMP4, $TEMP3
+	vpaddq		$TEMP2, $ACC2, $ACC2
+	vpblendd	\$3, $TEMP4, $ZERO, $TEMP4
+	vpaddq		$TEMP3, $ACC3, $ACC3
+	vpaddq		$TEMP4, $ACC4, $ACC4
+
+	vmovdqu		$ACC0, 0-128($rp)
+	vmovdqu		$ACC1, 32-128($rp)
+	vmovdqu		$ACC2, 64-128($rp)
+	vmovdqu		$ACC3, 96-128($rp)
+___
+
+$TEMP5=$ACC0;
+$code.=<<___;
+	vpsrlq		\$29, $ACC4, $TEMP1
+	vpand		$AND_MASK, $ACC4, $ACC4
+	vpsrlq		\$29, $ACC5, $TEMP2
+	vpand		$AND_MASK, $ACC5, $ACC5
+	vpsrlq		\$29, $ACC6, $TEMP3
+	vpermq		\$0x93, $TEMP1, $TEMP1
+	vpand		$AND_MASK, $ACC6, $ACC6
+	vpsrlq		\$29, $ACC7, $TEMP4
+	vpermq		\$0x93, $TEMP2, $TEMP2
+	vpand		$AND_MASK, $ACC7, $ACC7
+	vpsrlq		\$29, $ACC8, $TEMP5
+	vpermq		\$0x93, $TEMP3, $TEMP3
+	vpand		$AND_MASK, $ACC8, $ACC8
+	vpermq		\$0x93, $TEMP4, $TEMP4
+
+	vpblendd	\$3, $ZERO, $TEMP1, $TEMP0
+	vpermq		\$0x93, $TEMP5, $TEMP5
+	vpblendd	\$3, $TEMP1, $TEMP2, $TEMP1
+	vpaddq		$TEMP0, $ACC4, $ACC4
+	vpblendd	\$3, $TEMP2, $TEMP3, $TEMP2
+	vpaddq		$TEMP1, $ACC5, $ACC5
+	vpblendd	\$3, $TEMP3, $TEMP4, $TEMP3
+	vpaddq		$TEMP2, $ACC6, $ACC6
+	vpblendd	\$3, $TEMP4, $TEMP5, $TEMP4
+	vpaddq		$TEMP3, $ACC7, $ACC7
+	vpaddq		$TEMP4, $ACC8, $ACC8
+
+	vpsrlq		\$29, $ACC4, $TEMP1
+	vpand		$AND_MASK, $ACC4, $ACC4
+	vpsrlq		\$29, $ACC5, $TEMP2
+	vpand		$AND_MASK, $ACC5, $ACC5
+	vpsrlq		\$29, $ACC6, $TEMP3
+	vpermq		\$0x93, $TEMP1, $TEMP1
+	vpand		$AND_MASK, $ACC6, $ACC6
+	vpsrlq		\$29, $ACC7, $TEMP4
+	vpermq		\$0x93, $TEMP2, $TEMP2
+	vpand		$AND_MASK, $ACC7, $ACC7
+	vpsrlq		\$29, $ACC8, $TEMP5
+	vpermq		\$0x93, $TEMP3, $TEMP3
+	vpand		$AND_MASK, $ACC8, $ACC8
+	vpermq		\$0x93, $TEMP4, $TEMP4
+
+	vpblendd	\$3, $ZERO, $TEMP1, $TEMP0
+	vpermq		\$0x93, $TEMP5, $TEMP5
+	vpblendd	\$3, $TEMP1, $TEMP2, $TEMP1
+	vpaddq		$TEMP0, $ACC4, $ACC4
+	vpblendd	\$3, $TEMP2, $TEMP3, $TEMP2
+	vpaddq		$TEMP1, $ACC5, $ACC5
+	vpblendd	\$3, $TEMP3, $TEMP4, $TEMP3
+	vpaddq		$TEMP2, $ACC6, $ACC6
+	vpblendd	\$3, $TEMP4, $TEMP5, $TEMP4
+	vpaddq		$TEMP3, $ACC7, $ACC7
+	vpaddq		$TEMP4, $ACC8, $ACC8
+
+	vmovdqu		$ACC4, 128-128($rp)
+	vmovdqu		$ACC5, 160-128($rp)    
+	vmovdqu		$ACC6, 192-128($rp)
+	vmovdqu		$ACC7, 224-128($rp)
+	vmovdqu		$ACC8, 256-128($rp)
+	vzeroupper
+
+	mov	%rbp, %rax
+___
+$code.=<<___ if ($win64);
+	movaps	-0xd8(%rax),%xmm6
+	movaps	-0xc8(%rax),%xmm7
+	movaps	-0xb8(%rax),%xmm8
+	movaps	-0xa8(%rax),%xmm9
+	movaps	-0x98(%rax),%xmm10
+	movaps	-0x88(%rax),%xmm11
+	movaps	-0x78(%rax),%xmm12
+	movaps	-0x68(%rax),%xmm13
+	movaps	-0x58(%rax),%xmm14
+	movaps	-0x48(%rax),%xmm15
+___
+$code.=<<___;
+	mov	-48(%rax),%r15
+	mov	-40(%rax),%r14
+	mov	-32(%rax),%r13
+	mov	-24(%rax),%r12
+	mov	-16(%rax),%rbp
+	mov	-8(%rax),%rbx
+	lea	(%rax),%rsp		# restore %rsp
+.Lmul_1024_epilogue:
+	ret
+.size	rsaz_1024_mul_avx2,.-rsaz_1024_mul_avx2
+___
+}
+{
+my ($out,$inp) = $win64 ? ("%rcx","%rdx") : ("%rdi","%rsi");
+my @T = map("%r$_",(8..11));
+
+$code.=<<___;
+.globl	rsaz_1024_red2norm_avx2
+.type	rsaz_1024_red2norm_avx2,\@abi-omnipotent
+.align	32
+rsaz_1024_red2norm_avx2:
+	sub	\$-128,$inp	# size optimization
+	xor	%rax,%rax
+___
+
+for ($j=0,$i=0; $i<16; $i++) {
+    my $k=0;
+    while (29*$j<64*($i+1)) {	# load data till boundary
+	$code.="	mov	`8*$j-128`($inp), @T[0]\n";
+	$j++; $k++; push(@T,shift(@T));
+    }
+    $l=$k;
+    while ($k>1) {		# shift loaded data but last value
+	$code.="	shl	\$`29*($j-$k)`,@T[-$k]\n";
+	$k--;
+    }
+    $code.=<<___;		# shift last value
+	mov	@T[-1], @T[0]
+	shl	\$`29*($j-1)`, @T[-1]
+	shr	\$`-29*($j-1)`, @T[0]
+___
+    while ($l) {		# accumulate all values
+	$code.="	add	@T[-$l], %rax\n";
+	$l--;
+    }
+	$code.=<<___;
+	adc	\$0, @T[0]	# consume eventual carry
+	mov	%rax, 8*$i($out)
+	mov	@T[0], %rax
+___
+    push(@T,shift(@T));
+}
+$code.=<<___;
+	ret
+.size	rsaz_1024_red2norm_avx2,.-rsaz_1024_red2norm_avx2
+
+.globl	rsaz_1024_norm2red_avx2
+.type	rsaz_1024_norm2red_avx2,\@abi-omnipotent
+.align	32
+rsaz_1024_norm2red_avx2:
+	sub	\$-128,$out	# size optimization
+	mov	($inp),@T[0]
+	mov	\$0x1fffffff,%eax
+___
+for ($j=0,$i=0; $i<16; $i++) {
+    $code.="	mov	`8*($i+1)`($inp),@T[1]\n"	if ($i<15);
+    $code.="	xor	@T[1],@T[1]\n"			if ($i==15);
+    my $k=1;
+    while (29*($j+1)<64*($i+1)) {
+    	$code.=<<___;
+	mov	@T[0],@T[-$k]
+	shr	\$`29*$j`,@T[-$k]
+	and	%rax,@T[-$k]				# &0x1fffffff
+	mov	@T[-$k],`8*$j-128`($out)
+___
+	$j++; $k++;
+    }
+    $code.=<<___;
+	shrd	\$`29*$j`,@T[1],@T[0]
+	and	%rax,@T[0]
+	mov	@T[0],`8*$j-128`($out)
+___
+    $j++;
+    push(@T,shift(@T));
+}
+$code.=<<___;
+	mov	@T[0],`8*$j-128`($out)			# zero
+	mov	@T[0],`8*($j+1)-128`($out)
+	mov	@T[0],`8*($j+2)-128`($out)
+	mov	@T[0],`8*($j+3)-128`($out)
+	ret
+.size	rsaz_1024_norm2red_avx2,.-rsaz_1024_norm2red_avx2
+___
+}
+{
+my ($out,$inp,$power) = $win64 ? ("%rcx","%rdx","%r8d") : ("%rdi","%rsi","%edx");
+
+$code.=<<___;
+.globl	rsaz_1024_scatter5_avx2
+.type	rsaz_1024_scatter5_avx2,\@abi-omnipotent
+.align	32
+rsaz_1024_scatter5_avx2:
+	vzeroupper
+	vmovdqu	.Lscatter_permd(%rip),%ymm5
+	shl	\$4,$power
+	lea	($out,$power),$out
+	mov	\$9,%eax
+	jmp	.Loop_scatter_1024
+
+.align	32
+.Loop_scatter_1024:
+	vmovdqu		($inp),%ymm0
+	lea		32($inp),$inp
+	vpermd		%ymm0,%ymm5,%ymm0
+	vmovdqu		%xmm0,($out)
+	lea		16*32($out),$out
+	dec	%eax
+	jnz	.Loop_scatter_1024
+
+	vzeroupper
+	ret
+.size	rsaz_1024_scatter5_avx2,.-rsaz_1024_scatter5_avx2
+
+.globl	rsaz_1024_gather5_avx2
+.type	rsaz_1024_gather5_avx2,\@abi-omnipotent
+.align	32
+rsaz_1024_gather5_avx2:
+	vzeroupper
+	mov	%rsp,%r11
+___
+$code.=<<___ if ($win64);
+	lea	-0x88(%rsp),%rax
+.LSEH_begin_rsaz_1024_gather5:
+	# I can't trust assembler to use specific encoding:-(
+	.byte	0x48,0x8d,0x60,0xe0		# lea	-0x20(%rax),%rsp
+	.byte	0xc5,0xf8,0x29,0x70,0xe0	# vmovaps %xmm6,-0x20(%rax)
+	.byte	0xc5,0xf8,0x29,0x78,0xf0	# vmovaps %xmm7,-0x10(%rax)
+	.byte	0xc5,0x78,0x29,0x40,0x00	# vmovaps %xmm8,0(%rax)
+	.byte	0xc5,0x78,0x29,0x48,0x10	# vmovaps %xmm9,0x10(%rax)
+	.byte	0xc5,0x78,0x29,0x50,0x20	# vmovaps %xmm10,0x20(%rax)
+	.byte	0xc5,0x78,0x29,0x58,0x30	# vmovaps %xmm11,0x30(%rax)
+	.byte	0xc5,0x78,0x29,0x60,0x40	# vmovaps %xmm12,0x40(%rax)
+	.byte	0xc5,0x78,0x29,0x68,0x50	# vmovaps %xmm13,0x50(%rax)
+	.byte	0xc5,0x78,0x29,0x70,0x60	# vmovaps %xmm14,0x60(%rax)
+	.byte	0xc5,0x78,0x29,0x78,0x70	# vmovaps %xmm15,0x70(%rax)
+___
+$code.=<<___;
+	lea	-0x100(%rsp),%rsp
+	and	\$-32, %rsp
+	lea	.Linc(%rip), %r10
+	lea	-128(%rsp),%rax			# control u-op density
+
+	vmovd		$power, %xmm4
+	vmovdqa		(%r10),%ymm0
+	vmovdqa		32(%r10),%ymm1
+	vmovdqa		64(%r10),%ymm5
+	vpbroadcastd	%xmm4,%ymm4
+
+	vpaddd		%ymm5, %ymm0, %ymm2
+	vpcmpeqd	%ymm4, %ymm0, %ymm0
+	vpaddd		%ymm5, %ymm1, %ymm3
+	vpcmpeqd	%ymm4, %ymm1, %ymm1
+	vmovdqa		%ymm0, 32*0+128(%rax)
+	vpaddd		%ymm5, %ymm2, %ymm0
+	vpcmpeqd	%ymm4, %ymm2, %ymm2
+	vmovdqa		%ymm1, 32*1+128(%rax)
+	vpaddd		%ymm5, %ymm3, %ymm1
+	vpcmpeqd	%ymm4, %ymm3, %ymm3
+	vmovdqa		%ymm2, 32*2+128(%rax)
+	vpaddd		%ymm5, %ymm0, %ymm2
+	vpcmpeqd	%ymm4, %ymm0, %ymm0
+	vmovdqa		%ymm3, 32*3+128(%rax)
+	vpaddd		%ymm5, %ymm1, %ymm3
+	vpcmpeqd	%ymm4, %ymm1, %ymm1
+	vmovdqa		%ymm0, 32*4+128(%rax)
+	vpaddd		%ymm5, %ymm2, %ymm8
+	vpcmpeqd	%ymm4, %ymm2, %ymm2
+	vmovdqa		%ymm1, 32*5+128(%rax)
+	vpaddd		%ymm5, %ymm3, %ymm9
+	vpcmpeqd	%ymm4, %ymm3, %ymm3
+	vmovdqa		%ymm2, 32*6+128(%rax)
+	vpaddd		%ymm5, %ymm8, %ymm10
+	vpcmpeqd	%ymm4, %ymm8, %ymm8
+	vmovdqa		%ymm3, 32*7+128(%rax)
+	vpaddd		%ymm5, %ymm9, %ymm11
+	vpcmpeqd	%ymm4, %ymm9, %ymm9
+	vpaddd		%ymm5, %ymm10, %ymm12
+	vpcmpeqd	%ymm4, %ymm10, %ymm10
+	vpaddd		%ymm5, %ymm11, %ymm13
+	vpcmpeqd	%ymm4, %ymm11, %ymm11
+	vpaddd		%ymm5, %ymm12, %ymm14
+	vpcmpeqd	%ymm4, %ymm12, %ymm12
+	vpaddd		%ymm5, %ymm13, %ymm15
+	vpcmpeqd	%ymm4, %ymm13, %ymm13
+	vpcmpeqd	%ymm4, %ymm14, %ymm14
+	vpcmpeqd	%ymm4, %ymm15, %ymm15
+
+	vmovdqa	-32(%r10),%ymm7			# .Lgather_permd
+	lea	128($inp), $inp
+	mov	\$9,$power
+
+.Loop_gather_1024:
+	vmovdqa		32*0-128($inp),	%ymm0
+	vmovdqa		32*1-128($inp),	%ymm1
+	vmovdqa		32*2-128($inp),	%ymm2
+	vmovdqa		32*3-128($inp),	%ymm3
+	vpand		32*0+128(%rax),	%ymm0,	%ymm0
+	vpand		32*1+128(%rax),	%ymm1,	%ymm1
+	vpand		32*2+128(%rax),	%ymm2,	%ymm2
+	vpor		%ymm0, %ymm1, %ymm4
+	vpand		32*3+128(%rax),	%ymm3,	%ymm3
+	vmovdqa		32*4-128($inp),	%ymm0
+	vmovdqa		32*5-128($inp),	%ymm1
+	vpor		%ymm2, %ymm3, %ymm5
+	vmovdqa		32*6-128($inp),	%ymm2
+	vmovdqa		32*7-128($inp),	%ymm3
+	vpand		32*4+128(%rax),	%ymm0,	%ymm0
+	vpand		32*5+128(%rax),	%ymm1,	%ymm1
+	vpand		32*6+128(%rax),	%ymm2,	%ymm2
+	vpor		%ymm0, %ymm4, %ymm4
+	vpand		32*7+128(%rax),	%ymm3,	%ymm3
+	vpand		32*8-128($inp),	%ymm8,	%ymm0
+	vpor		%ymm1, %ymm5, %ymm5
+	vpand		32*9-128($inp),	%ymm9,	%ymm1
+	vpor		%ymm2, %ymm4, %ymm4
+	vpand		32*10-128($inp),%ymm10,	%ymm2
+	vpor		%ymm3, %ymm5, %ymm5
+	vpand		32*11-128($inp),%ymm11,	%ymm3
+	vpor		%ymm0, %ymm4, %ymm4
+	vpand		32*12-128($inp),%ymm12,	%ymm0
+	vpor		%ymm1, %ymm5, %ymm5
+	vpand		32*13-128($inp),%ymm13,	%ymm1
+	vpor		%ymm2, %ymm4, %ymm4
+	vpand		32*14-128($inp),%ymm14,	%ymm2
+	vpor		%ymm3, %ymm5, %ymm5
+	vpand		32*15-128($inp),%ymm15,	%ymm3
+	lea		32*16($inp), $inp
+	vpor		%ymm0, %ymm4, %ymm4
+	vpor		%ymm1, %ymm5, %ymm5
+	vpor		%ymm2, %ymm4, %ymm4
+	vpor		%ymm3, %ymm5, %ymm5
+
+	vpor		%ymm5, %ymm4, %ymm4
+	vextracti128	\$1, %ymm4, %xmm5	# upper half is cleared
+	vpor		%xmm4, %xmm5, %xmm5
+	vpermd		%ymm5,%ymm7,%ymm5
+	vmovdqu		%ymm5,($out)
+	lea		32($out),$out
+	dec	$power
+	jnz	.Loop_gather_1024
+
+	vpxor	%ymm0,%ymm0,%ymm0
+	vmovdqu	%ymm0,($out)
+	vzeroupper
+___
+$code.=<<___ if ($win64);
+	movaps	-0xa8(%r11),%xmm6
+	movaps	-0x98(%r11),%xmm7
+	movaps	-0x88(%r11),%xmm8
+	movaps	-0x78(%r11),%xmm9
+	movaps	-0x68(%r11),%xmm10
+	movaps	-0x58(%r11),%xmm11
+	movaps	-0x48(%r11),%xmm12
+	movaps	-0x38(%r11),%xmm13
+	movaps	-0x28(%r11),%xmm14
+	movaps	-0x18(%r11),%xmm15
+.LSEH_end_rsaz_1024_gather5:
+___
+$code.=<<___;
+	lea	(%r11),%rsp
+	ret
+.size	rsaz_1024_gather5_avx2,.-rsaz_1024_gather5_avx2
+___
+}
+
+$code.=<<___;
+.extern	OPENSSL_ia32cap_P
+.globl	rsaz_avx2_eligible
+.type	rsaz_avx2_eligible,\@abi-omnipotent
+.align	32
+rsaz_avx2_eligible:
+	mov	OPENSSL_ia32cap_P+8(%rip),%eax
+___
+$code.=<<___	if ($addx);
+	mov	\$`1<<8|1<<19`,%ecx
+	mov	\$0,%edx
+	and	%eax,%ecx
+	cmp	\$`1<<8|1<<19`,%ecx	# check for BMI2+AD*X
+	cmove	%edx,%eax
+___
+$code.=<<___;
+	and	\$`1<<5`,%eax
+	shr	\$5,%eax
+	ret
+.size	rsaz_avx2_eligible,.-rsaz_avx2_eligible
+
+.align	64
+.Land_mask:
+	.quad	0x1fffffff,0x1fffffff,0x1fffffff,-1
+.Lscatter_permd:
+	.long	0,2,4,6,7,7,7,7
+.Lgather_permd:
+	.long	0,7,1,7,2,7,3,7
+.Linc:
+	.long	0,0,0,0, 1,1,1,1
+	.long	2,2,2,2, 3,3,3,3
+	.long	4,4,4,4, 4,4,4,4
+.align	64
+___
+
+if ($win64) {
+$rec="%rcx";
+$frame="%rdx";
+$context="%r8";
+$disp="%r9";
+
+$code.=<<___
+.extern	__imp_RtlVirtualUnwind
+.type	rsaz_se_handler,\@abi-omnipotent
+.align	16
+rsaz_se_handler:
+	push	%rsi
+	push	%rdi
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+	pushfq
+	sub	\$64,%rsp
+
+	mov	120($context),%rax	# pull context->Rax
+	mov	248($context),%rbx	# pull context->Rip
+
+	mov	8($disp),%rsi		# disp->ImageBase
+	mov	56($disp),%r11		# disp->HandlerData
+
+	mov	0(%r11),%r10d		# HandlerData[0]
+	lea	(%rsi,%r10),%r10	# prologue label
+	cmp	%r10,%rbx		# context->RipRsp
+
+	mov	4(%r11),%r10d		# HandlerData[1]
+	lea	(%rsi,%r10),%r10	# epilogue label
+	cmp	%r10,%rbx		# context->Rip>=epilogue label
+	jae	.Lcommon_seh_tail
+
+	mov	160($context),%rax	# pull context->Rbp
+
+	mov	-48(%rax),%r15
+	mov	-40(%rax),%r14
+	mov	-32(%rax),%r13
+	mov	-24(%rax),%r12
+	mov	-16(%rax),%rbp
+	mov	-8(%rax),%rbx
+	mov	%r15,240($context)
+	mov	%r14,232($context)
+	mov	%r13,224($context)
+	mov	%r12,216($context)
+	mov	%rbp,160($context)
+	mov	%rbx,144($context)
+
+	lea	-0xd8(%rax),%rsi	# %xmm save area
+	lea	512($context),%rdi	# & context.Xmm6
+	mov	\$20,%ecx		# 10*sizeof(%xmm0)/sizeof(%rax)
+	.long	0xa548f3fc		# cld; rep movsq
+
+.Lcommon_seh_tail:
+	mov	8(%rax),%rdi
+	mov	16(%rax),%rsi
+	mov	%rax,152($context)	# restore context->Rsp
+	mov	%rsi,168($context)	# restore context->Rsi
+	mov	%rdi,176($context)	# restore context->Rdi
+
+	mov	40($disp),%rdi		# disp->ContextRecord
+	mov	$context,%rsi		# context
+	mov	\$154,%ecx		# sizeof(CONTEXT)
+	.long	0xa548f3fc		# cld; rep movsq
+
+	mov	$disp,%rsi
+	xor	%rcx,%rcx		# arg1, UNW_FLAG_NHANDLER
+	mov	8(%rsi),%rdx		# arg2, disp->ImageBase
+	mov	0(%rsi),%r8		# arg3, disp->ControlPc
+	mov	16(%rsi),%r9		# arg4, disp->FunctionEntry
+	mov	40(%rsi),%r10		# disp->ContextRecord
+	lea	56(%rsi),%r11		# &disp->HandlerData
+	lea	24(%rsi),%r12		# &disp->EstablisherFrame
+	mov	%r10,32(%rsp)		# arg5
+	mov	%r11,40(%rsp)		# arg6
+	mov	%r12,48(%rsp)		# arg7
+	mov	%rcx,56(%rsp)		# arg8, (NULL)
+	call	*__imp_RtlVirtualUnwind(%rip)
+
+	mov	\$1,%eax		# ExceptionContinueSearch
+	add	\$64,%rsp
+	popfq
+	pop	%r15
+	pop	%r14
+	pop	%r13
+	pop	%r12
+	pop	%rbp
+	pop	%rbx
+	pop	%rdi
+	pop	%rsi
+	ret
+.size	rsaz_se_handler,.-rsaz_se_handler
+
+.section	.pdata
+.align	4
+	.rva	.LSEH_begin_rsaz_1024_sqr_avx2
+	.rva	.LSEH_end_rsaz_1024_sqr_avx2
+	.rva	.LSEH_info_rsaz_1024_sqr_avx2
+
+	.rva	.LSEH_begin_rsaz_1024_mul_avx2
+	.rva	.LSEH_end_rsaz_1024_mul_avx2
+	.rva	.LSEH_info_rsaz_1024_mul_avx2
+
+	.rva	.LSEH_begin_rsaz_1024_gather5
+	.rva	.LSEH_end_rsaz_1024_gather5
+	.rva	.LSEH_info_rsaz_1024_gather5
+.section	.xdata
+.align	8
+.LSEH_info_rsaz_1024_sqr_avx2:
+	.byte	9,0,0,0
+	.rva	rsaz_se_handler
+	.rva	.Lsqr_1024_body,.Lsqr_1024_epilogue
+.LSEH_info_rsaz_1024_mul_avx2:
+	.byte	9,0,0,0
+	.rva	rsaz_se_handler
+	.rva	.Lmul_1024_body,.Lmul_1024_epilogue
+.LSEH_info_rsaz_1024_gather5:
+	.byte	0x01,0x36,0x17,0x0b
+	.byte	0x36,0xf8,0x09,0x00	# vmovaps 0x90(rsp),xmm15
+	.byte	0x31,0xe8,0x08,0x00	# vmovaps 0x80(rsp),xmm14
+	.byte	0x2c,0xd8,0x07,0x00	# vmovaps 0x70(rsp),xmm13
+	.byte	0x27,0xc8,0x06,0x00	# vmovaps 0x60(rsp),xmm12
+	.byte	0x22,0xb8,0x05,0x00	# vmovaps 0x50(rsp),xmm11
+	.byte	0x1d,0xa8,0x04,0x00	# vmovaps 0x40(rsp),xmm10
+	.byte	0x18,0x98,0x03,0x00	# vmovaps 0x30(rsp),xmm9
+	.byte	0x13,0x88,0x02,0x00	# vmovaps 0x20(rsp),xmm8
+	.byte	0x0e,0x78,0x01,0x00	# vmovaps 0x10(rsp),xmm7
+	.byte	0x09,0x68,0x00,0x00	# vmovaps 0x00(rsp),xmm6
+	.byte	0x04,0x01,0x15,0x00	# sub	  rsp,0xa8
+	.byte	0x00,0xb3,0x00,0x00	# set_frame r11
+___
+}
+
+foreach (split("\n",$code)) {
+	s/\`([^\`]*)\`/eval($1)/ge;
+
+	s/\b(sh[rl]d?\s+\$)(-?[0-9]+)/$1.$2%64/ge		or
+
+	s/\b(vmov[dq])\b(.+)%ymm([0-9]+)/$1$2%xmm$3/go		or
+	s/\b(vmovdqu)\b(.+)%x%ymm([0-9]+)/$1$2%xmm$3/go		or
+	s/\b(vpinsr[qd])\b(.+)%ymm([0-9]+)/$1$2%xmm$3/go	or
+	s/\b(vpextr[qd])\b(.+)%ymm([0-9]+)/$1$2%xmm$3/go	or
+	s/\b(vpbroadcast[qd]\s+)%ymm([0-9]+)/$1%xmm$2/go;
+	print $_,"\n";
+}
+
+}}} else {{{
+print <<___;	# assembler is too old
+.text
+
+.globl	rsaz_avx2_eligible
+.type	rsaz_avx2_eligible,\@abi-omnipotent
+rsaz_avx2_eligible:
+	xor	%eax,%eax
+	ret
+.size	rsaz_avx2_eligible,.-rsaz_avx2_eligible
+
+.globl	rsaz_1024_sqr_avx2
+.globl	rsaz_1024_mul_avx2
+.globl	rsaz_1024_norm2red_avx2
+.globl	rsaz_1024_red2norm_avx2
+.globl	rsaz_1024_scatter5_avx2
+.globl	rsaz_1024_gather5_avx2
+.type	rsaz_1024_sqr_avx2,\@abi-omnipotent
+rsaz_1024_sqr_avx2:
+rsaz_1024_mul_avx2:
+rsaz_1024_norm2red_avx2:
+rsaz_1024_red2norm_avx2:
+rsaz_1024_scatter5_avx2:
+rsaz_1024_gather5_avx2:
+	.byte	0x0f,0x0b	# ud2
+	ret
+.size	rsaz_1024_sqr_avx2,.-rsaz_1024_sqr_avx2
+___
+}}}
+
+close STDOUT;
diff --git a/crypto/bn/asm/rsaz-x86_64.pl b/crypto/bn/asm/rsaz-x86_64.pl
new file mode 100755
index 000000000..6f3b664f7
--- /dev/null
+++ b/crypto/bn/asm/rsaz-x86_64.pl
@@ -0,0 +1,2358 @@
+#! /usr/bin/env perl
+# Copyright 2013-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+
+##############################################################################
+#                                                                            #
+#  Copyright (c) 2012, Intel Corporation                                     #
+#                                                                            #
+#  All rights reserved.                                                      #
+#                                                                            #
+#  Redistribution and use in source and binary forms, with or without        #
+#  modification, are permitted provided that the following conditions are    #
+#  met:                                                                      #
+#                                                                            #
+#  *  Redistributions of source code must retain the above copyright         #
+#     notice, this list of conditions and the following disclaimer.          #
+#                                                                            #
+#  *  Redistributions in binary form must reproduce the above copyright      #
+#     notice, this list of conditions and the following disclaimer in the    #
+#     documentation and/or other materials provided with the                 #
+#     distribution.                                                          #
+#                                                                            #
+#  *  Neither the name of the Intel Corporation nor the names of its         #
+#     contributors may be used to endorse or promote products derived from   #
+#     this software without specific prior written permission.               #
+#                                                                            #
+#                                                                            #
+#  THIS SOFTWARE IS PROVIDED BY INTEL CORPORATION ""AS IS"" AND ANY          #
+#  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE         #
+#  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR        #
+#  PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL CORPORATION OR            #
+#  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,     #
+#  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,       #
+#  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR        #
+#  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF    #
+#  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING      #
+#  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS        #
+#  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.              #
+#                                                                            #
+##############################################################################
+# Developers and authors:                                                    #
+# Shay Gueron (1, 2), and Vlad Krasnov (1)                                   #
+# (1) Intel Architecture Group, Microprocessor and Chipset Development,      #
+#     Israel Development Center, Haifa, Israel                               #
+# (2) University of Haifa                                                    #
+##############################################################################
+# Reference:                                                                 #
+# [1] S. Gueron, "Efficient Software Implementations of Modular              #
+#     Exponentiation", http://eprint.iacr.org/2011/239                       #
+# [2] S. Gueron, V. Krasnov. "Speeding up Big-Numbers Squaring".             #
+#     IEEE Proceedings of 9th International Conference on Information        #
+#     Technology: New Generations (ITNG 2012), 821-823 (2012).               #
+# [3] S. Gueron, Efficient Software Implementations of Modular Exponentiation#
+#     Journal of Cryptographic Engineering 2:31-43 (2012).                   #
+# [4] S. Gueron, V. Krasnov: "[PATCH] Efficient and side channel analysis    #
+#     resistant 512-bit and 1024-bit modular exponentiation for optimizing   #
+#     RSA1024 and RSA2048 on x86_64 platforms",                              #
+#     http://rt.openssl.org/Ticket/Display.html?id=2582&user=guest&pass=guest#
+##############################################################################
+
+# While original submission covers 512- and 1024-bit exponentiation,
+# this module is limited to 512-bit version only (and as such
+# accelerates RSA1024 sign). This is because improvement for longer
+# keys is not high enough to justify the effort, highest measured
+# was ~5% on Westmere. [This is relative to OpenSSL 1.0.2, upcoming
+# for the moment of this writing!] Nor does this module implement
+# "monolithic" complete exponentiation jumbo-subroutine, but adheres
+# to more modular mixture of C and assembly. And it's optimized even
+# for processors other than Intel Core family (see table below for
+# improvement coefficients).
+# 						
+#
+# RSA1024 sign/sec	this/original	|this/rsax(*)	this/fips(*)
+#			----------------+---------------------------
+# Opteron		+13%		|+5%		+20%
+# Bulldozer		-0%		|-1%		+10%
+# P4			+11%		|+7%		+8%
+# Westmere		+5%		|+14%		+17%
+# Sandy Bridge		+2%		|+12%		+29%
+# Ivy Bridge		+1%		|+11%		+35%
+# Haswell(**)		-0%		|+12%		+39%
+# Atom			+13%		|+11%		+4%
+# VIA Nano		+70%		|+9%		+25%
+#
+# (*)	rsax engine and fips numbers are presented for reference
+#	purposes;
+# (**)	MULX was attempted, but found to give only marginal improvement;
+
+$flavour = shift;
+$output  = shift;
+if ($flavour =~ /\./) { $output = $flavour; undef $flavour; }
+
+$win64=0; $win64=1 if ($flavour =~ /[nm]asm|mingw64/ || $output =~ /\.asm$/);
+
+$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+( $xlate="${dir}x86_64-xlate.pl" and -f $xlate ) or
+( $xlate="${dir}../../perlasm/x86_64-xlate.pl" and -f $xlate) or
+die "can't locate x86_64-xlate.pl";
+
+open OUT,"| \"$^X\" \"$xlate\" $flavour \"$output\"";
+*STDOUT=*OUT;
+
+if (`$ENV{CC} -Wa,-v -c -o /dev/null -x assembler /dev/null 2>&1`
+		=~ /GNU assembler version ([2-9]\.[0-9]+)/) {
+	$addx = ($1>=2.23);
+}
+
+if (!$addx && $win64 && ($flavour =~ /nasm/ || $ENV{ASM} =~ /nasm/) &&
+	    `nasm -v 2>&1` =~ /NASM version ([2-9]\.[0-9]+)/) {
+	$addx = ($1>=2.10);
+}
+
+if (!$addx && $win64 && ($flavour =~ /masm/ || $ENV{ASM} =~ /ml64/) &&
+	    `ml64 2>&1` =~ /Version ([0-9]+)\./) {
+	$addx = ($1>=12);
+}
+
+if (!$addx && `$ENV{CC} -v 2>&1` =~ /((?:^clang|LLVM) version|.*based on LLVM) ([3-9])\.([0-9]+)/) {
+	my $ver = $2 + $3/100.0;	# 3.1->3.01, 3.10->3.10
+	$addx = ($ver>=3.03);
+}
+
+($out, $inp, $mod) = ("%rdi", "%rsi", "%rbp");	# common internal API
+{
+my ($out,$inp,$mod,$n0,$times) = ("%rdi","%rsi","%rdx","%rcx","%r8d");
+
+$code.=<<___;
+.text
+
+.extern	OPENSSL_ia32cap_P
+
+.globl	rsaz_512_sqr
+.type	rsaz_512_sqr,\@function,5
+.align	32
+rsaz_512_sqr:				# 25-29% faster than rsaz_512_mul
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+
+	subq	\$128+24, %rsp
+.Lsqr_body:
+	movq	$mod, %rbp		# common argument
+	movq	($inp), %rdx
+	movq	8($inp), %rax
+	movq	$n0, 128(%rsp)
+___
+$code.=<<___ if ($addx);
+	movl	\$0x80100,%r11d
+	andl	OPENSSL_ia32cap_P+8(%rip),%r11d
+	cmpl	\$0x80100,%r11d		# check for MULX and ADO/CX
+	je	.Loop_sqrx
+___
+$code.=<<___;
+	jmp	.Loop_sqr
+
+.align	32
+.Loop_sqr:
+	movl	$times,128+8(%rsp)
+#first iteration
+	movq	%rdx, %rbx
+	mulq	%rdx
+	movq	%rax, %r8
+	movq	16($inp), %rax
+	movq	%rdx, %r9
+
+	mulq	%rbx
+	addq	%rax, %r9
+	movq	24($inp), %rax
+	movq	%rdx, %r10
+	adcq	\$0, %r10
+
+	mulq	%rbx
+	addq	%rax, %r10
+	movq	32($inp), %rax
+	movq	%rdx, %r11
+	adcq	\$0, %r11
+
+	mulq	%rbx
+	addq	%rax, %r11
+	movq	40($inp), %rax
+	movq	%rdx, %r12
+	adcq	\$0, %r12
+
+	mulq	%rbx
+	addq	%rax, %r12
+	movq	48($inp), %rax
+	movq	%rdx, %r13
+	adcq	\$0, %r13
+
+	mulq	%rbx
+	addq	%rax, %r13
+	movq	56($inp), %rax
+	movq	%rdx, %r14
+	adcq	\$0, %r14
+
+	mulq	%rbx
+	addq	%rax, %r14
+	movq	%rbx, %rax
+	movq	%rdx, %r15
+	adcq	\$0, %r15
+
+	addq	%r8, %r8		#shlq	\$1, %r8
+	movq	%r9, %rcx
+	adcq	%r9, %r9		#shld	\$1, %r8, %r9
+
+	mulq	%rax
+	movq	%rax, (%rsp)
+	addq	%rdx, %r8
+	adcq	\$0, %r9
+
+	movq	%r8, 8(%rsp)
+	shrq	\$63, %rcx
+
+#second iteration
+	movq	8($inp), %r8
+	movq	16($inp), %rax
+	mulq	%r8
+	addq	%rax, %r10
+	movq	24($inp), %rax
+	movq	%rdx, %rbx
+	adcq	\$0, %rbx
+
+	mulq	%r8
+	addq	%rax, %r11
+	movq	32($inp), %rax
+	adcq	\$0, %rdx
+	addq	%rbx, %r11
+	movq	%rdx, %rbx
+	adcq	\$0, %rbx
+
+	mulq	%r8
+	addq	%rax, %r12
+	movq	40($inp), %rax
+	adcq	\$0, %rdx
+	addq	%rbx, %r12
+	movq	%rdx, %rbx
+	adcq	\$0, %rbx
+
+	mulq	%r8
+	addq	%rax, %r13
+	movq	48($inp), %rax
+	adcq	\$0, %rdx
+	addq	%rbx, %r13
+	movq	%rdx, %rbx
+	adcq	\$0, %rbx
+
+	mulq	%r8
+	addq	%rax, %r14
+	movq	56($inp), %rax
+	adcq	\$0, %rdx
+	addq	%rbx, %r14
+	movq	%rdx, %rbx
+	adcq	\$0, %rbx
+
+	mulq	%r8
+	addq	%rax, %r15
+	movq	%r8, %rax
+	adcq	\$0, %rdx
+	addq	%rbx, %r15
+	movq	%rdx, %r8
+	movq	%r10, %rdx
+	adcq	\$0, %r8
+
+	add	%rdx, %rdx
+	lea	(%rcx,%r10,2), %r10	#shld	\$1, %rcx, %r10
+	movq	%r11, %rbx
+	adcq	%r11, %r11		#shld	\$1, %r10, %r11
+
+	mulq	%rax
+	addq	%rax, %r9
+	adcq	%rdx, %r10
+	adcq	\$0, %r11
+
+	movq	%r9, 16(%rsp)
+	movq	%r10, 24(%rsp)
+	shrq	\$63, %rbx
+	
+#third iteration
+	movq	16($inp), %r9	
+	movq	24($inp), %rax
+	mulq	%r9
+	addq	%rax, %r12
+	movq	32($inp), %rax
+	movq	%rdx, %rcx
+	adcq	\$0, %rcx
+
+	mulq	%r9
+	addq	%rax, %r13
+	movq	40($inp), %rax
+	adcq	\$0, %rdx
+	addq	%rcx, %r13
+	movq	%rdx, %rcx
+	adcq	\$0, %rcx
+
+	mulq	%r9
+	addq	%rax, %r14
+	movq	48($inp), %rax
+	adcq	\$0, %rdx
+	addq	%rcx, %r14
+	movq	%rdx, %rcx
+	adcq	\$0, %rcx
+
+	mulq	%r9
+	 movq	%r12, %r10
+	 lea	(%rbx,%r12,2), %r12	#shld	\$1, %rbx, %r12
+	addq	%rax, %r15
+	movq	56($inp), %rax
+	adcq	\$0, %rdx
+	addq	%rcx, %r15
+	movq	%rdx, %rcx
+	adcq	\$0, %rcx
+
+	mulq	%r9
+	 shrq	\$63, %r10
+	addq	%rax, %r8
+	movq	%r9, %rax
+	adcq	\$0, %rdx
+	addq	%rcx, %r8
+	movq	%rdx, %r9
+	adcq	\$0, %r9
+
+	movq	%r13, %rcx
+	leaq	(%r10,%r13,2), %r13	#shld	\$1, %r12, %r13
+
+	mulq	%rax
+	addq	%rax, %r11
+	adcq	%rdx, %r12
+	adcq	\$0, %r13
+
+	movq	%r11, 32(%rsp)
+	movq	%r12, 40(%rsp)
+	shrq	\$63, %rcx
+
+#fourth iteration
+	movq	24($inp), %r10
+	movq	32($inp), %rax
+	mulq	%r10
+	addq	%rax, %r14
+	movq	40($inp), %rax
+	movq	%rdx, %rbx
+	adcq	\$0, %rbx
+
+	mulq	%r10
+	addq	%rax, %r15
+	movq	48($inp), %rax
+	adcq	\$0, %rdx
+	addq	%rbx, %r15
+	movq	%rdx, %rbx
+	adcq	\$0, %rbx
+
+	mulq	%r10
+	 movq	%r14, %r12
+	 leaq	(%rcx,%r14,2), %r14	#shld	\$1, %rcx, %r14
+	addq	%rax, %r8
+	movq	56($inp), %rax
+	adcq	\$0, %rdx
+	addq	%rbx, %r8
+	movq	%rdx, %rbx
+	adcq	\$0, %rbx
+
+	mulq	%r10
+	 shrq	\$63, %r12
+	addq	%rax, %r9
+	movq	%r10, %rax
+	adcq	\$0, %rdx
+	addq	%rbx, %r9
+	movq	%rdx, %r10
+	adcq	\$0, %r10
+
+	movq	%r15, %rbx
+	leaq	(%r12,%r15,2),%r15	#shld	\$1, %r14, %r15
+
+	mulq	%rax
+	addq	%rax, %r13
+	adcq	%rdx, %r14
+	adcq	\$0, %r15
+
+	movq	%r13, 48(%rsp)
+	movq	%r14, 56(%rsp)
+	shrq	\$63, %rbx
+
+#fifth iteration
+	movq	32($inp), %r11
+	movq	40($inp), %rax
+	mulq	%r11
+	addq	%rax, %r8
+	movq	48($inp), %rax
+	movq	%rdx, %rcx
+	adcq	\$0, %rcx
+
+	mulq	%r11
+	addq	%rax, %r9
+	movq	56($inp), %rax
+	adcq	\$0, %rdx
+	 movq	%r8, %r12
+	 leaq	(%rbx,%r8,2), %r8	#shld	\$1, %rbx, %r8
+	addq	%rcx, %r9
+	movq	%rdx, %rcx
+	adcq	\$0, %rcx
+
+	mulq	%r11
+	 shrq	\$63, %r12
+	addq	%rax, %r10
+	movq	%r11, %rax
+	adcq	\$0, %rdx
+	addq	%rcx, %r10
+	movq	%rdx, %r11
+	adcq	\$0, %r11
+
+	movq	%r9, %rcx
+	leaq	(%r12,%r9,2), %r9	#shld	\$1, %r8, %r9
+
+	mulq	%rax
+	addq	%rax, %r15
+	adcq	%rdx, %r8
+	adcq	\$0, %r9
+
+	movq	%r15, 64(%rsp)
+	movq	%r8, 72(%rsp)
+	shrq	\$63, %rcx
+
+#sixth iteration
+	movq	40($inp), %r12
+	movq	48($inp), %rax
+	mulq	%r12
+	addq	%rax, %r10
+	movq	56($inp), %rax
+	movq	%rdx, %rbx
+	adcq	\$0, %rbx
+
+	mulq	%r12
+	addq	%rax, %r11
+	movq	%r12, %rax
+	 movq	%r10, %r15
+	 leaq	(%rcx,%r10,2), %r10	#shld	\$1, %rcx, %r10
+	adcq	\$0, %rdx
+	 shrq	\$63, %r15
+	addq	%rbx, %r11
+	movq	%rdx, %r12
+	adcq	\$0, %r12
+
+	movq	%r11, %rbx
+	leaq	(%r15,%r11,2), %r11	#shld	\$1, %r10, %r11
+
+	mulq	%rax
+	addq	%rax, %r9
+	adcq	%rdx, %r10
+	adcq	\$0, %r11
+
+	movq	%r9, 80(%rsp)
+	movq	%r10, 88(%rsp)
+
+#seventh iteration
+	movq	48($inp), %r13
+	movq	56($inp), %rax
+	mulq	%r13
+	addq	%rax, %r12
+	movq	%r13, %rax
+	movq	%rdx, %r13
+	adcq	\$0, %r13
+
+	xorq	%r14, %r14
+	shlq	\$1, %rbx
+	adcq	%r12, %r12		#shld	\$1, %rbx, %r12
+	adcq	%r13, %r13		#shld	\$1, %r12, %r13
+	adcq	%r14, %r14		#shld	\$1, %r13, %r14
+
+	mulq	%rax
+	addq	%rax, %r11
+	adcq	%rdx, %r12
+	adcq	\$0, %r13
+
+	movq	%r11, 96(%rsp)
+	movq	%r12, 104(%rsp)
+
+#eighth iteration
+	movq	56($inp), %rax
+	mulq	%rax
+	addq	%rax, %r13
+	adcq	\$0, %rdx
+
+	addq	%rdx, %r14
+
+	movq	%r13, 112(%rsp)
+	movq	%r14, 120(%rsp)
+
+	movq	(%rsp), %r8
+	movq	8(%rsp), %r9
+	movq	16(%rsp), %r10
+	movq	24(%rsp), %r11
+	movq	32(%rsp), %r12
+	movq	40(%rsp), %r13
+	movq	48(%rsp), %r14
+	movq	56(%rsp), %r15
+
+	call	__rsaz_512_reduce
+
+	addq	64(%rsp), %r8
+	adcq	72(%rsp), %r9
+	adcq	80(%rsp), %r10
+	adcq	88(%rsp), %r11
+	adcq	96(%rsp), %r12
+	adcq	104(%rsp), %r13
+	adcq	112(%rsp), %r14
+	adcq	120(%rsp), %r15
+	sbbq	%rcx, %rcx
+
+	call	__rsaz_512_subtract
+
+	movq	%r8, %rdx
+	movq	%r9, %rax
+	movl	128+8(%rsp), $times
+	movq	$out, $inp
+
+	decl	$times
+	jnz	.Loop_sqr
+___
+if ($addx) {
+$code.=<<___;
+	jmp	.Lsqr_tail
+
+.align	32
+.Loop_sqrx:
+	movl	$times,128+8(%rsp)
+	movq	$out, %xmm0		# off-load
+	movq	%rbp, %xmm1		# off-load
+#first iteration	
+	mulx	%rax, %r8, %r9
+
+	mulx	16($inp), %rcx, %r10
+	xor	%rbp, %rbp		# cf=0, of=0
+
+	mulx	24($inp), %rax, %r11
+	adcx	%rcx, %r9
+
+	mulx	32($inp), %rcx, %r12
+	adcx	%rax, %r10
+
+	mulx	40($inp), %rax, %r13
+	adcx	%rcx, %r11
+
+	.byte	0xc4,0x62,0xf3,0xf6,0xb6,0x30,0x00,0x00,0x00	# mulx	48($inp), %rcx, %r14
+	adcx	%rax, %r12
+	adcx	%rcx, %r13
+
+	.byte	0xc4,0x62,0xfb,0xf6,0xbe,0x38,0x00,0x00,0x00	# mulx	56($inp), %rax, %r15
+	adcx	%rax, %r14
+	adcx	%rbp, %r15		# %rbp is 0
+
+	mov	%r9, %rcx
+	shld	\$1, %r8, %r9
+	shl	\$1, %r8
+
+	xor	%ebp, %ebp
+	mulx	%rdx, %rax, %rdx
+	adcx	%rdx, %r8
+	 mov	8($inp), %rdx
+	adcx	%rbp, %r9
+
+	mov	%rax, (%rsp)
+	mov	%r8, 8(%rsp)
+
+#second iteration	
+	mulx	16($inp), %rax, %rbx
+	adox	%rax, %r10
+	adcx	%rbx, %r11
+
+	.byte	0xc4,0x62,0xc3,0xf6,0x86,0x18,0x00,0x00,0x00	# mulx	24($inp), $out, %r8
+	adox	$out, %r11
+	adcx	%r8, %r12
+
+	mulx	32($inp), %rax, %rbx
+	adox	%rax, %r12
+	adcx	%rbx, %r13
+
+	mulx	40($inp), $out, %r8
+	adox	$out, %r13
+	adcx	%r8, %r14
+
+	.byte	0xc4,0xe2,0xfb,0xf6,0x9e,0x30,0x00,0x00,0x00	# mulx	48($inp), %rax, %rbx
+	adox	%rax, %r14
+	adcx	%rbx, %r15
+
+	.byte	0xc4,0x62,0xc3,0xf6,0x86,0x38,0x00,0x00,0x00	# mulx	56($inp), $out, %r8
+	adox	$out, %r15
+	adcx	%rbp, %r8
+	adox	%rbp, %r8
+
+	mov	%r11, %rbx
+	shld	\$1, %r10, %r11
+	shld	\$1, %rcx, %r10
+
+	xor	%ebp,%ebp
+	mulx	%rdx, %rax, %rcx
+	 mov	16($inp), %rdx
+	adcx	%rax, %r9
+	adcx	%rcx, %r10
+	adcx	%rbp, %r11
+
+	mov	%r9, 16(%rsp)
+	.byte	0x4c,0x89,0x94,0x24,0x18,0x00,0x00,0x00		# mov	%r10, 24(%rsp)
+	
+#third iteration	
+	.byte	0xc4,0x62,0xc3,0xf6,0x8e,0x18,0x00,0x00,0x00	# mulx	24($inp), $out, %r9
+	adox	$out, %r12
+	adcx	%r9, %r13
+
+	mulx	32($inp), %rax, %rcx
+	adox	%rax, %r13
+	adcx	%rcx, %r14
+
+	mulx	40($inp), $out, %r9
+	adox	$out, %r14
+	adcx	%r9, %r15
+
+	.byte	0xc4,0xe2,0xfb,0xf6,0x8e,0x30,0x00,0x00,0x00	# mulx	48($inp), %rax, %rcx
+	adox	%rax, %r15
+	adcx	%rcx, %r8
+
+	.byte	0xc4,0x62,0xc3,0xf6,0x8e,0x38,0x00,0x00,0x00	# mulx	56($inp), $out, %r9
+	adox	$out, %r8
+	adcx	%rbp, %r9
+	adox	%rbp, %r9
+
+	mov	%r13, %rcx
+	shld	\$1, %r12, %r13
+	shld	\$1, %rbx, %r12
+
+	xor	%ebp, %ebp
+	mulx	%rdx, %rax, %rdx
+	adcx	%rax, %r11
+	adcx	%rdx, %r12
+	 mov	24($inp), %rdx
+	adcx	%rbp, %r13
+
+	mov	%r11, 32(%rsp)
+	.byte	0x4c,0x89,0xa4,0x24,0x28,0x00,0x00,0x00		# mov	%r12, 40(%rsp)
+	
+#fourth iteration	
+	.byte	0xc4,0xe2,0xfb,0xf6,0x9e,0x20,0x00,0x00,0x00	# mulx	32($inp), %rax, %rbx
+	adox	%rax, %r14
+	adcx	%rbx, %r15
+
+	mulx	40($inp), $out, %r10
+	adox	$out, %r15
+	adcx	%r10, %r8
+
+	mulx	48($inp), %rax, %rbx
+	adox	%rax, %r8
+	adcx	%rbx, %r9
+
+	mulx	56($inp), $out, %r10
+	adox	$out, %r9
+	adcx	%rbp, %r10
+	adox	%rbp, %r10
+
+	.byte	0x66
+	mov	%r15, %rbx
+	shld	\$1, %r14, %r15
+	shld	\$1, %rcx, %r14
+
+	xor	%ebp, %ebp
+	mulx	%rdx, %rax, %rdx
+	adcx	%rax, %r13
+	adcx	%rdx, %r14
+	 mov	32($inp), %rdx
+	adcx	%rbp, %r15
+
+	mov	%r13, 48(%rsp)
+	mov	%r14, 56(%rsp)
+	
+#fifth iteration	
+	.byte	0xc4,0x62,0xc3,0xf6,0x9e,0x28,0x00,0x00,0x00	# mulx	40($inp), $out, %r11
+	adox	$out, %r8
+	adcx	%r11, %r9
+
+	mulx	48($inp), %rax, %rcx
+	adox	%rax, %r9
+	adcx	%rcx, %r10
+
+	mulx	56($inp), $out, %r11
+	adox	$out, %r10
+	adcx	%rbp, %r11
+	adox	%rbp, %r11
+
+	mov	%r9, %rcx
+	shld	\$1, %r8, %r9
+	shld	\$1, %rbx, %r8
+
+	xor	%ebp, %ebp
+	mulx	%rdx, %rax, %rdx
+	adcx	%rax, %r15
+	adcx	%rdx, %r8
+	 mov	40($inp), %rdx
+	adcx	%rbp, %r9
+
+	mov	%r15, 64(%rsp)
+	mov	%r8, 72(%rsp)
+	
+#sixth iteration	
+	.byte	0xc4,0xe2,0xfb,0xf6,0x9e,0x30,0x00,0x00,0x00	# mulx	48($inp), %rax, %rbx
+	adox	%rax, %r10
+	adcx	%rbx, %r11
+
+	.byte	0xc4,0x62,0xc3,0xf6,0xa6,0x38,0x00,0x00,0x00	# mulx	56($inp), $out, %r12
+	adox	$out, %r11
+	adcx	%rbp, %r12
+	adox	%rbp, %r12
+
+	mov	%r11, %rbx
+	shld	\$1, %r10, %r11
+	shld	\$1, %rcx, %r10
+
+	xor	%ebp, %ebp
+	mulx	%rdx, %rax, %rdx
+	adcx	%rax, %r9
+	adcx	%rdx, %r10
+	 mov	48($inp), %rdx
+	adcx	%rbp, %r11
+
+	mov	%r9, 80(%rsp)
+	mov	%r10, 88(%rsp)
+
+#seventh iteration
+	.byte	0xc4,0x62,0xfb,0xf6,0xae,0x38,0x00,0x00,0x00	# mulx	56($inp), %rax, %r13
+	adox	%rax, %r12
+	adox	%rbp, %r13
+
+	xor	%r14, %r14
+	shld	\$1, %r13, %r14
+	shld	\$1, %r12, %r13
+	shld	\$1, %rbx, %r12
+
+	xor	%ebp, %ebp
+	mulx	%rdx, %rax, %rdx
+	adcx	%rax, %r11
+	adcx	%rdx, %r12
+	 mov	56($inp), %rdx
+	adcx	%rbp, %r13
+
+	.byte	0x4c,0x89,0x9c,0x24,0x60,0x00,0x00,0x00		# mov	%r11, 96(%rsp)
+	.byte	0x4c,0x89,0xa4,0x24,0x68,0x00,0x00,0x00		# mov	%r12, 104(%rsp)
+
+#eighth iteration
+	mulx	%rdx, %rax, %rdx
+	adox	%rax, %r13
+	adox	%rbp, %rdx
+
+	.byte	0x66
+	add	%rdx, %r14
+
+	movq	%r13, 112(%rsp)
+	movq	%r14, 120(%rsp)
+	movq	%xmm0, $out
+	movq	%xmm1, %rbp
+
+	movq	128(%rsp), %rdx		# pull $n0
+	movq	(%rsp), %r8
+	movq	8(%rsp), %r9
+	movq	16(%rsp), %r10
+	movq	24(%rsp), %r11
+	movq	32(%rsp), %r12
+	movq	40(%rsp), %r13
+	movq	48(%rsp), %r14
+	movq	56(%rsp), %r15
+
+	call	__rsaz_512_reducex
+
+	addq	64(%rsp), %r8
+	adcq	72(%rsp), %r9
+	adcq	80(%rsp), %r10
+	adcq	88(%rsp), %r11
+	adcq	96(%rsp), %r12
+	adcq	104(%rsp), %r13
+	adcq	112(%rsp), %r14
+	adcq	120(%rsp), %r15
+	sbbq	%rcx, %rcx
+
+	call	__rsaz_512_subtract
+
+	movq	%r8, %rdx
+	movq	%r9, %rax
+	movl	128+8(%rsp), $times
+	movq	$out, $inp
+
+	decl	$times
+	jnz	.Loop_sqrx
+
+.Lsqr_tail:
+___
+}
+$code.=<<___;
+
+	leaq	128+24+48(%rsp), %rax
+	movq	-48(%rax), %r15
+	movq	-40(%rax), %r14
+	movq	-32(%rax), %r13
+	movq	-24(%rax), %r12
+	movq	-16(%rax), %rbp
+	movq	-8(%rax), %rbx
+	leaq	(%rax), %rsp
+.Lsqr_epilogue:
+	ret
+.size	rsaz_512_sqr,.-rsaz_512_sqr
+___
+}
+{
+my ($out,$ap,$bp,$mod,$n0) = ("%rdi","%rsi","%rdx","%rcx","%r8");
+$code.=<<___;
+.globl	rsaz_512_mul
+.type	rsaz_512_mul,\@function,5
+.align	32
+rsaz_512_mul:
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+
+	subq	\$128+24, %rsp
+.Lmul_body:
+	movq	$out, %xmm0		# off-load arguments
+	movq	$mod, %xmm1
+	movq	$n0, 128(%rsp)
+___
+$code.=<<___ if ($addx);
+	movl	\$0x80100,%r11d
+	andl	OPENSSL_ia32cap_P+8(%rip),%r11d
+	cmpl	\$0x80100,%r11d		# check for MULX and ADO/CX
+	je	.Lmulx
+___
+$code.=<<___;
+	movq	($bp), %rbx		# pass b[0]
+	movq	$bp, %rbp		# pass argument
+	call	__rsaz_512_mul
+
+	movq	%xmm0, $out
+	movq	%xmm1, %rbp
+
+	movq	(%rsp), %r8
+	movq	8(%rsp), %r9
+	movq	16(%rsp), %r10
+	movq	24(%rsp), %r11
+	movq	32(%rsp), %r12
+	movq	40(%rsp), %r13
+	movq	48(%rsp), %r14
+	movq	56(%rsp), %r15
+
+	call	__rsaz_512_reduce
+___
+$code.=<<___ if ($addx);
+	jmp	.Lmul_tail
+
+.align	32
+.Lmulx:
+	movq	$bp, %rbp		# pass argument
+	movq	($bp), %rdx		# pass b[0]
+	call	__rsaz_512_mulx
+
+	movq	%xmm0, $out
+	movq	%xmm1, %rbp
+
+	movq	128(%rsp), %rdx		# pull $n0
+	movq	(%rsp), %r8
+	movq	8(%rsp), %r9
+	movq	16(%rsp), %r10
+	movq	24(%rsp), %r11
+	movq	32(%rsp), %r12
+	movq	40(%rsp), %r13
+	movq	48(%rsp), %r14
+	movq	56(%rsp), %r15
+
+	call	__rsaz_512_reducex
+.Lmul_tail:
+___
+$code.=<<___;
+	addq	64(%rsp), %r8
+	adcq	72(%rsp), %r9
+	adcq	80(%rsp), %r10
+	adcq	88(%rsp), %r11
+	adcq	96(%rsp), %r12
+	adcq	104(%rsp), %r13
+	adcq	112(%rsp), %r14
+	adcq	120(%rsp), %r15
+	sbbq	%rcx, %rcx
+
+	call	__rsaz_512_subtract
+
+	leaq	128+24+48(%rsp), %rax
+	movq	-48(%rax), %r15
+	movq	-40(%rax), %r14
+	movq	-32(%rax), %r13
+	movq	-24(%rax), %r12
+	movq	-16(%rax), %rbp
+	movq	-8(%rax), %rbx
+	leaq	(%rax), %rsp
+.Lmul_epilogue:
+	ret
+.size	rsaz_512_mul,.-rsaz_512_mul
+___
+}
+{
+my ($out,$ap,$bp,$mod,$n0,$pwr) = ("%rdi","%rsi","%rdx","%rcx","%r8","%r9d");
+$code.=<<___;
+.globl	rsaz_512_mul_gather4
+.type	rsaz_512_mul_gather4,\@function,6
+.align	32
+rsaz_512_mul_gather4:
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+
+	subq	\$`128+24+($win64?0xb0:0)`, %rsp
+___
+$code.=<<___	if ($win64);
+	movaps	%xmm6,0xa0(%rsp)
+	movaps	%xmm7,0xb0(%rsp)
+	movaps	%xmm8,0xc0(%rsp)
+	movaps	%xmm9,0xd0(%rsp)
+	movaps	%xmm10,0xe0(%rsp)
+	movaps	%xmm11,0xf0(%rsp)
+	movaps	%xmm12,0x100(%rsp)
+	movaps	%xmm13,0x110(%rsp)
+	movaps	%xmm14,0x120(%rsp)
+	movaps	%xmm15,0x130(%rsp)
+___
+$code.=<<___;
+.Lmul_gather4_body:
+	movd	$pwr,%xmm8
+	movdqa	.Linc+16(%rip),%xmm1	# 00000002000000020000000200000002
+	movdqa	.Linc(%rip),%xmm0	# 00000001000000010000000000000000
+
+	pshufd	\$0,%xmm8,%xmm8		# broadcast $power
+	movdqa	%xmm1,%xmm7
+	movdqa	%xmm1,%xmm2
+___
+########################################################################
+# calculate mask by comparing 0..15 to $power
+#
+for($i=0;$i<4;$i++) {
+$code.=<<___;
+	paddd	%xmm`$i`,%xmm`$i+1`
+	pcmpeqd	%xmm8,%xmm`$i`
+	movdqa	%xmm7,%xmm`$i+3`
+___
+}
+for(;$i<7;$i++) {
+$code.=<<___;
+	paddd	%xmm`$i`,%xmm`$i+1`
+	pcmpeqd	%xmm8,%xmm`$i`
+___
+}
+$code.=<<___;
+	pcmpeqd	%xmm8,%xmm7
+
+	movdqa	16*0($bp),%xmm8
+	movdqa	16*1($bp),%xmm9
+	movdqa	16*2($bp),%xmm10
+	movdqa	16*3($bp),%xmm11
+	pand	%xmm0,%xmm8
+	movdqa	16*4($bp),%xmm12
+	pand	%xmm1,%xmm9
+	movdqa	16*5($bp),%xmm13
+	pand	%xmm2,%xmm10
+	movdqa	16*6($bp),%xmm14
+	pand	%xmm3,%xmm11
+	movdqa	16*7($bp),%xmm15
+	leaq	128($bp), %rbp
+	pand	%xmm4,%xmm12
+	pand	%xmm5,%xmm13
+	pand	%xmm6,%xmm14
+	pand	%xmm7,%xmm15
+	por	%xmm10,%xmm8
+	por	%xmm11,%xmm9
+	por	%xmm12,%xmm8
+	por	%xmm13,%xmm9
+	por	%xmm14,%xmm8
+	por	%xmm15,%xmm9
+
+	por	%xmm9,%xmm8
+	pshufd	\$0x4e,%xmm8,%xmm9
+	por	%xmm9,%xmm8
+___
+$code.=<<___ if ($addx);
+	movl	\$0x80100,%r11d
+	andl	OPENSSL_ia32cap_P+8(%rip),%r11d
+	cmpl	\$0x80100,%r11d		# check for MULX and ADO/CX
+	je	.Lmulx_gather
+___
+$code.=<<___;
+	movq	%xmm8,%rbx
+
+	movq	$n0, 128(%rsp)		# off-load arguments
+	movq	$out, 128+8(%rsp)
+	movq	$mod, 128+16(%rsp)
+
+	movq	($ap), %rax
+	 movq	8($ap), %rcx
+	mulq	%rbx			# 0 iteration
+	movq	%rax, (%rsp)
+	movq	%rcx, %rax
+	movq	%rdx, %r8
+
+	mulq	%rbx
+	addq	%rax, %r8
+	movq	16($ap), %rax
+	movq	%rdx, %r9
+	adcq	\$0, %r9
+
+	mulq	%rbx
+	addq	%rax, %r9
+	movq	24($ap), %rax
+	movq	%rdx, %r10
+	adcq	\$0, %r10
+
+	mulq	%rbx
+	addq	%rax, %r10
+	movq	32($ap), %rax
+	movq	%rdx, %r11
+	adcq	\$0, %r11
+
+	mulq	%rbx
+	addq	%rax, %r11
+	movq	40($ap), %rax
+	movq	%rdx, %r12
+	adcq	\$0, %r12
+
+	mulq	%rbx
+	addq	%rax, %r12
+	movq	48($ap), %rax
+	movq	%rdx, %r13
+	adcq	\$0, %r13
+
+	mulq	%rbx
+	addq	%rax, %r13
+	movq	56($ap), %rax
+	movq	%rdx, %r14
+	adcq	\$0, %r14
+	
+	mulq	%rbx
+	addq	%rax, %r14
+	 movq	($ap), %rax
+	movq	%rdx, %r15
+	adcq	\$0, %r15
+
+	leaq	8(%rsp), %rdi
+	movl	\$7, %ecx
+	jmp	.Loop_mul_gather
+
+.align	32
+.Loop_mul_gather:
+	movdqa	16*0(%rbp),%xmm8
+	movdqa	16*1(%rbp),%xmm9
+	movdqa	16*2(%rbp),%xmm10
+	movdqa	16*3(%rbp),%xmm11
+	pand	%xmm0,%xmm8
+	movdqa	16*4(%rbp),%xmm12
+	pand	%xmm1,%xmm9
+	movdqa	16*5(%rbp),%xmm13
+	pand	%xmm2,%xmm10
+	movdqa	16*6(%rbp),%xmm14
+	pand	%xmm3,%xmm11
+	movdqa	16*7(%rbp),%xmm15
+	leaq	128(%rbp), %rbp
+	pand	%xmm4,%xmm12
+	pand	%xmm5,%xmm13
+	pand	%xmm6,%xmm14
+	pand	%xmm7,%xmm15
+	por	%xmm10,%xmm8
+	por	%xmm11,%xmm9
+	por	%xmm12,%xmm8
+	por	%xmm13,%xmm9
+	por	%xmm14,%xmm8
+	por	%xmm15,%xmm9
+
+	por	%xmm9,%xmm8
+	pshufd	\$0x4e,%xmm8,%xmm9
+	por	%xmm9,%xmm8
+	movq	%xmm8,%rbx
+
+	mulq	%rbx
+	addq	%rax, %r8
+	movq	8($ap), %rax
+	movq	%r8, (%rdi)
+	movq	%rdx, %r8
+	adcq	\$0, %r8
+
+	mulq	%rbx
+	addq	%rax, %r9
+	movq	16($ap), %rax
+	adcq	\$0, %rdx
+	addq	%r9, %r8
+	movq	%rdx, %r9
+	adcq	\$0, %r9
+
+	mulq	%rbx
+	addq	%rax, %r10
+	movq	24($ap), %rax
+	adcq	\$0, %rdx
+	addq	%r10, %r9
+	movq	%rdx, %r10
+	adcq	\$0, %r10
+
+	mulq	%rbx
+	addq	%rax, %r11
+	movq	32($ap), %rax
+	adcq	\$0, %rdx
+	addq	%r11, %r10
+	movq	%rdx, %r11
+	adcq	\$0, %r11
+
+	mulq	%rbx
+	addq	%rax, %r12
+	movq	40($ap), %rax
+	adcq	\$0, %rdx
+	addq	%r12, %r11
+	movq	%rdx, %r12
+	adcq	\$0, %r12
+
+	mulq	%rbx
+	addq	%rax, %r13
+	movq	48($ap), %rax
+	adcq	\$0, %rdx
+	addq	%r13, %r12
+	movq	%rdx, %r13
+	adcq	\$0, %r13
+
+	mulq	%rbx
+	addq	%rax, %r14
+	movq	56($ap), %rax
+	adcq	\$0, %rdx
+	addq	%r14, %r13
+	movq	%rdx, %r14
+	adcq	\$0, %r14
+
+	mulq	%rbx
+	addq	%rax, %r15
+	 movq	($ap), %rax
+	adcq	\$0, %rdx
+	addq	%r15, %r14
+	movq	%rdx, %r15	
+	adcq	\$0, %r15
+
+	leaq	8(%rdi), %rdi
+
+	decl	%ecx
+	jnz	.Loop_mul_gather
+
+	movq	%r8, (%rdi)
+	movq	%r9, 8(%rdi)
+	movq	%r10, 16(%rdi)
+	movq	%r11, 24(%rdi)
+	movq	%r12, 32(%rdi)
+	movq	%r13, 40(%rdi)
+	movq	%r14, 48(%rdi)
+	movq	%r15, 56(%rdi)
+
+	movq	128+8(%rsp), $out
+	movq	128+16(%rsp), %rbp
+
+	movq	(%rsp), %r8
+	movq	8(%rsp), %r9
+	movq	16(%rsp), %r10
+	movq	24(%rsp), %r11
+	movq	32(%rsp), %r12
+	movq	40(%rsp), %r13
+	movq	48(%rsp), %r14
+	movq	56(%rsp), %r15
+
+	call	__rsaz_512_reduce
+___
+$code.=<<___ if ($addx);
+	jmp	.Lmul_gather_tail
+
+.align	32
+.Lmulx_gather:
+	movq	%xmm8,%rdx
+
+	mov	$n0, 128(%rsp)		# off-load arguments
+	mov	$out, 128+8(%rsp)
+	mov	$mod, 128+16(%rsp)
+
+	mulx	($ap), %rbx, %r8	# 0 iteration
+	mov	%rbx, (%rsp)
+	xor	%edi, %edi		# cf=0, of=0
+
+	mulx	8($ap), %rax, %r9
+
+	mulx	16($ap), %rbx, %r10
+	adcx	%rax, %r8
+
+	mulx	24($ap), %rax, %r11
+	adcx	%rbx, %r9
+
+	mulx	32($ap), %rbx, %r12
+	adcx	%rax, %r10
+
+	mulx	40($ap), %rax, %r13
+	adcx	%rbx, %r11
+
+	mulx	48($ap), %rbx, %r14
+	adcx	%rax, %r12
+	
+	mulx	56($ap), %rax, %r15
+	adcx	%rbx, %r13
+	adcx	%rax, %r14
+	.byte	0x67
+	mov	%r8, %rbx
+	adcx	%rdi, %r15		# %rdi is 0
+
+	mov	\$-7, %rcx
+	jmp	.Loop_mulx_gather
+
+.align	32
+.Loop_mulx_gather:
+	movdqa	16*0(%rbp),%xmm8
+	movdqa	16*1(%rbp),%xmm9
+	movdqa	16*2(%rbp),%xmm10
+	movdqa	16*3(%rbp),%xmm11
+	pand	%xmm0,%xmm8
+	movdqa	16*4(%rbp),%xmm12
+	pand	%xmm1,%xmm9
+	movdqa	16*5(%rbp),%xmm13
+	pand	%xmm2,%xmm10
+	movdqa	16*6(%rbp),%xmm14
+	pand	%xmm3,%xmm11
+	movdqa	16*7(%rbp),%xmm15
+	leaq	128(%rbp), %rbp
+	pand	%xmm4,%xmm12
+	pand	%xmm5,%xmm13
+	pand	%xmm6,%xmm14
+	pand	%xmm7,%xmm15
+	por	%xmm10,%xmm8
+	por	%xmm11,%xmm9
+	por	%xmm12,%xmm8
+	por	%xmm13,%xmm9
+	por	%xmm14,%xmm8
+	por	%xmm15,%xmm9
+
+	por	%xmm9,%xmm8
+	pshufd	\$0x4e,%xmm8,%xmm9
+	por	%xmm9,%xmm8
+	movq	%xmm8,%rdx
+
+	.byte	0xc4,0x62,0xfb,0xf6,0x86,0x00,0x00,0x00,0x00	# mulx	($ap), %rax, %r8
+	adcx	%rax, %rbx
+	adox	%r9, %r8
+
+	mulx	8($ap), %rax, %r9
+	adcx	%rax, %r8
+	adox	%r10, %r9
+
+	mulx	16($ap), %rax, %r10
+	adcx	%rax, %r9
+	adox	%r11, %r10
+
+	.byte	0xc4,0x62,0xfb,0xf6,0x9e,0x18,0x00,0x00,0x00	# mulx	24($ap), %rax, %r11
+	adcx	%rax, %r10
+	adox	%r12, %r11
+
+	mulx	32($ap), %rax, %r12
+	adcx	%rax, %r11
+	adox	%r13, %r12
+
+	mulx	40($ap), %rax, %r13
+	adcx	%rax, %r12
+	adox	%r14, %r13
+
+	.byte	0xc4,0x62,0xfb,0xf6,0xb6,0x30,0x00,0x00,0x00	# mulx	48($ap), %rax, %r14
+	adcx	%rax, %r13
+	.byte	0x67
+	adox	%r15, %r14
+
+	mulx	56($ap), %rax, %r15
+	 mov	%rbx, 64(%rsp,%rcx,8)
+	adcx	%rax, %r14
+	adox	%rdi, %r15
+	mov	%r8, %rbx
+	adcx	%rdi, %r15		# cf=0
+
+	inc	%rcx			# of=0
+	jnz	.Loop_mulx_gather
+
+	mov	%r8, 64(%rsp)
+	mov	%r9, 64+8(%rsp)
+	mov	%r10, 64+16(%rsp)
+	mov	%r11, 64+24(%rsp)
+	mov	%r12, 64+32(%rsp)
+	mov	%r13, 64+40(%rsp)
+	mov	%r14, 64+48(%rsp)
+	mov	%r15, 64+56(%rsp)
+
+	mov	128(%rsp), %rdx		# pull arguments
+	mov	128+8(%rsp), $out
+	mov	128+16(%rsp), %rbp
+
+	mov	(%rsp), %r8
+	mov	8(%rsp), %r9
+	mov	16(%rsp), %r10
+	mov	24(%rsp), %r11
+	mov	32(%rsp), %r12
+	mov	40(%rsp), %r13
+	mov	48(%rsp), %r14
+	mov	56(%rsp), %r15
+
+	call	__rsaz_512_reducex
+
+.Lmul_gather_tail:
+___
+$code.=<<___;
+	addq	64(%rsp), %r8
+	adcq	72(%rsp), %r9
+	adcq	80(%rsp), %r10
+	adcq	88(%rsp), %r11
+	adcq	96(%rsp), %r12
+	adcq	104(%rsp), %r13
+	adcq	112(%rsp), %r14
+	adcq	120(%rsp), %r15
+	sbbq	%rcx, %rcx
+
+	call	__rsaz_512_subtract
+
+	leaq	128+24+48(%rsp), %rax
+___
+$code.=<<___	if ($win64);
+	movaps	0xa0-0xc8(%rax),%xmm6
+	movaps	0xb0-0xc8(%rax),%xmm7
+	movaps	0xc0-0xc8(%rax),%xmm8
+	movaps	0xd0-0xc8(%rax),%xmm9
+	movaps	0xe0-0xc8(%rax),%xmm10
+	movaps	0xf0-0xc8(%rax),%xmm11
+	movaps	0x100-0xc8(%rax),%xmm12
+	movaps	0x110-0xc8(%rax),%xmm13
+	movaps	0x120-0xc8(%rax),%xmm14
+	movaps	0x130-0xc8(%rax),%xmm15
+	lea	0xb0(%rax),%rax
+___
+$code.=<<___;
+	movq	-48(%rax), %r15
+	movq	-40(%rax), %r14
+	movq	-32(%rax), %r13
+	movq	-24(%rax), %r12
+	movq	-16(%rax), %rbp
+	movq	-8(%rax), %rbx
+	leaq	(%rax), %rsp
+.Lmul_gather4_epilogue:
+	ret
+.size	rsaz_512_mul_gather4,.-rsaz_512_mul_gather4
+___
+}
+{
+my ($out,$ap,$mod,$n0,$tbl,$pwr) = ("%rdi","%rsi","%rdx","%rcx","%r8","%r9d");
+$code.=<<___;
+.globl	rsaz_512_mul_scatter4
+.type	rsaz_512_mul_scatter4,\@function,6
+.align	32
+rsaz_512_mul_scatter4:
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+
+	mov	$pwr, $pwr
+	subq	\$128+24, %rsp
+.Lmul_scatter4_body:
+	leaq	($tbl,$pwr,8), $tbl
+	movq	$out, %xmm0		# off-load arguments
+	movq	$mod, %xmm1
+	movq	$tbl, %xmm2
+	movq	$n0, 128(%rsp)
+
+	movq	$out, %rbp
+___
+$code.=<<___ if ($addx);
+	movl	\$0x80100,%r11d
+	andl	OPENSSL_ia32cap_P+8(%rip),%r11d
+	cmpl	\$0x80100,%r11d		# check for MULX and ADO/CX
+	je	.Lmulx_scatter
+___
+$code.=<<___;
+	movq	($out),%rbx		# pass b[0]
+	call	__rsaz_512_mul
+
+	movq	%xmm0, $out
+	movq	%xmm1, %rbp
+
+	movq	(%rsp), %r8
+	movq	8(%rsp), %r9
+	movq	16(%rsp), %r10
+	movq	24(%rsp), %r11
+	movq	32(%rsp), %r12
+	movq	40(%rsp), %r13
+	movq	48(%rsp), %r14
+	movq	56(%rsp), %r15
+
+	call	__rsaz_512_reduce
+___
+$code.=<<___ if ($addx);
+	jmp	.Lmul_scatter_tail
+	
+.align	32
+.Lmulx_scatter:
+	movq	($out), %rdx		# pass b[0]
+	call	__rsaz_512_mulx
+
+	movq	%xmm0, $out
+	movq	%xmm1, %rbp
+
+	movq	128(%rsp), %rdx		# pull $n0
+	movq	(%rsp), %r8
+	movq	8(%rsp), %r9
+	movq	16(%rsp), %r10
+	movq	24(%rsp), %r11
+	movq	32(%rsp), %r12
+	movq	40(%rsp), %r13
+	movq	48(%rsp), %r14
+	movq	56(%rsp), %r15
+
+	call	__rsaz_512_reducex
+
+.Lmul_scatter_tail:
+___
+$code.=<<___;
+	addq	64(%rsp), %r8
+	adcq	72(%rsp), %r9
+	adcq	80(%rsp), %r10
+	adcq	88(%rsp), %r11
+	adcq	96(%rsp), %r12
+	adcq	104(%rsp), %r13
+	adcq	112(%rsp), %r14
+	adcq	120(%rsp), %r15
+	movq	%xmm2, $inp
+	sbbq	%rcx, %rcx
+
+	call	__rsaz_512_subtract
+
+	movq	%r8, 128*0($inp)	# scatter
+	movq	%r9, 128*1($inp)
+	movq	%r10, 128*2($inp)
+	movq	%r11, 128*3($inp)
+	movq	%r12, 128*4($inp)
+	movq	%r13, 128*5($inp)
+	movq	%r14, 128*6($inp)
+	movq	%r15, 128*7($inp)
+
+	leaq	128+24+48(%rsp), %rax
+	movq	-48(%rax), %r15
+	movq	-40(%rax), %r14
+	movq	-32(%rax), %r13
+	movq	-24(%rax), %r12
+	movq	-16(%rax), %rbp
+	movq	-8(%rax), %rbx
+	leaq	(%rax), %rsp
+.Lmul_scatter4_epilogue:
+	ret
+.size	rsaz_512_mul_scatter4,.-rsaz_512_mul_scatter4
+___
+}
+{
+my ($out,$inp,$mod,$n0) = ("%rdi","%rsi","%rdx","%rcx");
+$code.=<<___;
+.globl	rsaz_512_mul_by_one
+.type	rsaz_512_mul_by_one,\@function,4
+.align	32
+rsaz_512_mul_by_one:
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+
+	subq	\$128+24, %rsp
+.Lmul_by_one_body:
+___
+$code.=<<___ if ($addx);
+	movl	OPENSSL_ia32cap_P+8(%rip),%eax
+___
+$code.=<<___;
+	movq	$mod, %rbp	# reassign argument
+	movq	$n0, 128(%rsp)
+
+	movq	($inp), %r8
+	pxor	%xmm0, %xmm0
+	movq	8($inp), %r9
+	movq	16($inp), %r10
+	movq	24($inp), %r11
+	movq	32($inp), %r12
+	movq	40($inp), %r13
+	movq	48($inp), %r14
+	movq	56($inp), %r15
+
+	movdqa	%xmm0, (%rsp)
+	movdqa	%xmm0, 16(%rsp)
+	movdqa	%xmm0, 32(%rsp)
+	movdqa	%xmm0, 48(%rsp)
+	movdqa	%xmm0, 64(%rsp)
+	movdqa	%xmm0, 80(%rsp)
+	movdqa	%xmm0, 96(%rsp)
+___
+$code.=<<___ if ($addx);
+	andl	\$0x80100,%eax
+	cmpl	\$0x80100,%eax		# check for MULX and ADO/CX
+	je	.Lby_one_callx
+___
+$code.=<<___;
+	call	__rsaz_512_reduce
+___
+$code.=<<___ if ($addx);
+	jmp	.Lby_one_tail
+.align	32
+.Lby_one_callx:
+	movq	128(%rsp), %rdx		# pull $n0
+	call	__rsaz_512_reducex
+.Lby_one_tail:
+___
+$code.=<<___;
+	movq	%r8, ($out)
+	movq	%r9, 8($out)
+	movq	%r10, 16($out)
+	movq	%r11, 24($out)
+	movq	%r12, 32($out)
+	movq	%r13, 40($out)
+	movq	%r14, 48($out)
+	movq	%r15, 56($out)
+
+	leaq	128+24+48(%rsp), %rax
+	movq	-48(%rax), %r15
+	movq	-40(%rax), %r14
+	movq	-32(%rax), %r13
+	movq	-24(%rax), %r12
+	movq	-16(%rax), %rbp
+	movq	-8(%rax), %rbx
+	leaq	(%rax), %rsp
+.Lmul_by_one_epilogue:
+	ret
+.size	rsaz_512_mul_by_one,.-rsaz_512_mul_by_one
+___
+}
+{	# __rsaz_512_reduce
+	#
+	# input:	%r8-%r15, %rbp - mod, 128(%rsp) - n0
+	# output:	%r8-%r15
+	# clobbers:	everything except %rbp and %rdi
+$code.=<<___;
+.type	__rsaz_512_reduce,\@abi-omnipotent
+.align	32
+__rsaz_512_reduce:
+	movq	%r8, %rbx
+	imulq	128+8(%rsp), %rbx
+	movq	0(%rbp), %rax
+	movl	\$8, %ecx
+	jmp	.Lreduction_loop
+
+.align	32
+.Lreduction_loop:
+	mulq	%rbx
+	movq	8(%rbp), %rax
+	negq	%r8
+	movq	%rdx, %r8
+	adcq	\$0, %r8
+
+	mulq	%rbx
+	addq	%rax, %r9
+	movq	16(%rbp), %rax
+	adcq	\$0, %rdx
+	addq	%r9, %r8
+	movq	%rdx, %r9
+	adcq	\$0, %r9
+
+	mulq	%rbx
+	addq	%rax, %r10
+	movq	24(%rbp), %rax
+	adcq	\$0, %rdx
+	addq	%r10, %r9
+	movq	%rdx, %r10
+	adcq	\$0, %r10
+
+	mulq	%rbx
+	addq	%rax, %r11
+	movq	32(%rbp), %rax
+	adcq	\$0, %rdx
+	addq	%r11, %r10
+	 movq	128+8(%rsp), %rsi
+	#movq	%rdx, %r11
+	#adcq	\$0, %r11
+	adcq	\$0, %rdx
+	movq	%rdx, %r11
+
+	mulq	%rbx
+	addq	%rax, %r12
+	movq	40(%rbp), %rax
+	adcq	\$0, %rdx
+	 imulq	%r8, %rsi
+	addq	%r12, %r11
+	movq	%rdx, %r12
+	adcq	\$0, %r12
+
+	mulq	%rbx
+	addq	%rax, %r13
+	movq	48(%rbp), %rax
+	adcq	\$0, %rdx
+	addq	%r13, %r12
+	movq	%rdx, %r13
+	adcq	\$0, %r13
+
+	mulq	%rbx
+	addq	%rax, %r14
+	movq	56(%rbp), %rax
+	adcq	\$0, %rdx
+	addq	%r14, %r13
+	movq	%rdx, %r14
+	adcq	\$0, %r14
+
+	mulq	%rbx
+	 movq	%rsi, %rbx
+	addq	%rax, %r15
+	 movq	0(%rbp), %rax
+	adcq	\$0, %rdx
+	addq	%r15, %r14
+	movq	%rdx, %r15
+	adcq	\$0, %r15
+
+	decl	%ecx
+	jne	.Lreduction_loop
+
+	ret
+.size	__rsaz_512_reduce,.-__rsaz_512_reduce
+___
+}
+if ($addx) {
+	# __rsaz_512_reducex
+	#
+	# input:	%r8-%r15, %rbp - mod, 128(%rsp) - n0
+	# output:	%r8-%r15
+	# clobbers:	everything except %rbp and %rdi
+$code.=<<___;
+.type	__rsaz_512_reducex,\@abi-omnipotent
+.align	32
+__rsaz_512_reducex:
+	#movq	128+8(%rsp), %rdx		# pull $n0
+	imulq	%r8, %rdx
+	xorq	%rsi, %rsi			# cf=0,of=0
+	movl	\$8, %ecx
+	jmp	.Lreduction_loopx
+
+.align	32
+.Lreduction_loopx:
+	mov	%r8, %rbx
+	mulx	0(%rbp), %rax, %r8
+	adcx	%rbx, %rax
+	adox	%r9, %r8
+
+	mulx	8(%rbp), %rax, %r9
+	adcx	%rax, %r8
+	adox	%r10, %r9
+
+	mulx	16(%rbp), %rbx, %r10
+	adcx	%rbx, %r9
+	adox	%r11, %r10
+
+	mulx	24(%rbp), %rbx, %r11
+	adcx	%rbx, %r10
+	adox	%r12, %r11
+
+	.byte	0xc4,0x62,0xe3,0xf6,0xa5,0x20,0x00,0x00,0x00	# mulx	32(%rbp), %rbx, %r12
+	 mov	%rdx, %rax
+	 mov	%r8, %rdx
+	adcx	%rbx, %r11
+	adox	%r13, %r12
+
+	 mulx	128+8(%rsp), %rbx, %rdx
+	 mov	%rax, %rdx
+
+	mulx	40(%rbp), %rax, %r13
+	adcx	%rax, %r12
+	adox	%r14, %r13
+
+	.byte	0xc4,0x62,0xfb,0xf6,0xb5,0x30,0x00,0x00,0x00	# mulx	48(%rbp), %rax, %r14
+	adcx	%rax, %r13
+	adox	%r15, %r14
+
+	mulx	56(%rbp), %rax, %r15
+	 mov	%rbx, %rdx
+	adcx	%rax, %r14
+	adox	%rsi, %r15			# %rsi is 0
+	adcx	%rsi, %r15			# cf=0
+
+	decl	%ecx				# of=0
+	jne	.Lreduction_loopx
+
+	ret
+.size	__rsaz_512_reducex,.-__rsaz_512_reducex
+___
+}
+{	# __rsaz_512_subtract
+	# input: %r8-%r15, %rdi - $out, %rbp - $mod, %rcx - mask
+	# output:
+	# clobbers: everything but %rdi, %rsi and %rbp
+$code.=<<___;
+.type	__rsaz_512_subtract,\@abi-omnipotent
+.align	32
+__rsaz_512_subtract:
+	movq	%r8, ($out)
+	movq	%r9, 8($out)
+	movq	%r10, 16($out)
+	movq	%r11, 24($out)
+	movq	%r12, 32($out)
+	movq	%r13, 40($out)
+	movq	%r14, 48($out)
+	movq	%r15, 56($out)
+
+	movq	0($mod), %r8
+	movq	8($mod), %r9
+	negq	%r8
+	notq	%r9
+	andq	%rcx, %r8
+	movq	16($mod), %r10
+	andq	%rcx, %r9
+	notq	%r10
+	movq	24($mod), %r11
+	andq	%rcx, %r10
+	notq	%r11
+	movq	32($mod), %r12
+	andq	%rcx, %r11
+	notq	%r12
+	movq	40($mod), %r13
+	andq	%rcx, %r12
+	notq	%r13
+	movq	48($mod), %r14
+	andq	%rcx, %r13
+	notq	%r14
+	movq	56($mod), %r15
+	andq	%rcx, %r14
+	notq	%r15
+	andq	%rcx, %r15
+
+	addq	($out), %r8
+	adcq	8($out), %r9
+	adcq	16($out), %r10
+	adcq	24($out), %r11
+	adcq	32($out), %r12
+	adcq	40($out), %r13
+	adcq	48($out), %r14
+	adcq	56($out), %r15
+
+	movq	%r8, ($out)
+	movq	%r9, 8($out)
+	movq	%r10, 16($out)
+	movq	%r11, 24($out)
+	movq	%r12, 32($out)
+	movq	%r13, 40($out)
+	movq	%r14, 48($out)
+	movq	%r15, 56($out)
+
+	ret
+.size	__rsaz_512_subtract,.-__rsaz_512_subtract
+___
+}
+{	# __rsaz_512_mul
+	#
+	# input: %rsi - ap, %rbp - bp
+	# output:
+	# clobbers: everything
+my ($ap,$bp) = ("%rsi","%rbp");
+$code.=<<___;
+.type	__rsaz_512_mul,\@abi-omnipotent
+.align	32
+__rsaz_512_mul:
+	leaq	8(%rsp), %rdi
+
+	movq	($ap), %rax
+	mulq	%rbx
+	movq	%rax, (%rdi)
+	movq	8($ap), %rax
+	movq	%rdx, %r8
+
+	mulq	%rbx
+	addq	%rax, %r8
+	movq	16($ap), %rax
+	movq	%rdx, %r9
+	adcq	\$0, %r9
+
+	mulq	%rbx
+	addq	%rax, %r9
+	movq	24($ap), %rax
+	movq	%rdx, %r10
+	adcq	\$0, %r10
+
+	mulq	%rbx
+	addq	%rax, %r10
+	movq	32($ap), %rax
+	movq	%rdx, %r11
+	adcq	\$0, %r11
+
+	mulq	%rbx
+	addq	%rax, %r11
+	movq	40($ap), %rax
+	movq	%rdx, %r12
+	adcq	\$0, %r12
+
+	mulq	%rbx
+	addq	%rax, %r12
+	movq	48($ap), %rax
+	movq	%rdx, %r13
+	adcq	\$0, %r13
+
+	mulq	%rbx
+	addq	%rax, %r13
+	movq	56($ap), %rax
+	movq	%rdx, %r14
+	adcq	\$0, %r14
+	
+	mulq	%rbx
+	addq	%rax, %r14
+	 movq	($ap), %rax
+	movq	%rdx, %r15
+	adcq	\$0, %r15
+
+	leaq	8($bp), $bp
+	leaq	8(%rdi), %rdi
+
+	movl	\$7, %ecx
+	jmp	.Loop_mul
+
+.align	32
+.Loop_mul:
+	movq	($bp), %rbx
+	mulq	%rbx
+	addq	%rax, %r8
+	movq	8($ap), %rax
+	movq	%r8, (%rdi)
+	movq	%rdx, %r8
+	adcq	\$0, %r8
+
+	mulq	%rbx
+	addq	%rax, %r9
+	movq	16($ap), %rax
+	adcq	\$0, %rdx
+	addq	%r9, %r8
+	movq	%rdx, %r9
+	adcq	\$0, %r9
+
+	mulq	%rbx
+	addq	%rax, %r10
+	movq	24($ap), %rax
+	adcq	\$0, %rdx
+	addq	%r10, %r9
+	movq	%rdx, %r10
+	adcq	\$0, %r10
+
+	mulq	%rbx
+	addq	%rax, %r11
+	movq	32($ap), %rax
+	adcq	\$0, %rdx
+	addq	%r11, %r10
+	movq	%rdx, %r11
+	adcq	\$0, %r11
+
+	mulq	%rbx
+	addq	%rax, %r12
+	movq	40($ap), %rax
+	adcq	\$0, %rdx
+	addq	%r12, %r11
+	movq	%rdx, %r12
+	adcq	\$0, %r12
+
+	mulq	%rbx
+	addq	%rax, %r13
+	movq	48($ap), %rax
+	adcq	\$0, %rdx
+	addq	%r13, %r12
+	movq	%rdx, %r13
+	adcq	\$0, %r13
+
+	mulq	%rbx
+	addq	%rax, %r14
+	movq	56($ap), %rax
+	adcq	\$0, %rdx
+	addq	%r14, %r13
+	movq	%rdx, %r14
+	 leaq	8($bp), $bp
+	adcq	\$0, %r14
+
+	mulq	%rbx
+	addq	%rax, %r15
+	 movq	($ap), %rax
+	adcq	\$0, %rdx
+	addq	%r15, %r14
+	movq	%rdx, %r15	
+	adcq	\$0, %r15
+
+	leaq	8(%rdi), %rdi
+
+	decl	%ecx
+	jnz	.Loop_mul
+
+	movq	%r8, (%rdi)
+	movq	%r9, 8(%rdi)
+	movq	%r10, 16(%rdi)
+	movq	%r11, 24(%rdi)
+	movq	%r12, 32(%rdi)
+	movq	%r13, 40(%rdi)
+	movq	%r14, 48(%rdi)
+	movq	%r15, 56(%rdi)
+
+	ret
+.size	__rsaz_512_mul,.-__rsaz_512_mul
+___
+}
+if ($addx) {
+	# __rsaz_512_mulx
+	#
+	# input: %rsi - ap, %rbp - bp
+	# output:
+	# clobbers: everything
+my ($ap,$bp,$zero) = ("%rsi","%rbp","%rdi");
+$code.=<<___;
+.type	__rsaz_512_mulx,\@abi-omnipotent
+.align	32
+__rsaz_512_mulx:
+	mulx	($ap), %rbx, %r8	# initial %rdx preloaded by caller
+	mov	\$-6, %rcx
+
+	mulx	8($ap), %rax, %r9
+	movq	%rbx, 8(%rsp)
+
+	mulx	16($ap), %rbx, %r10
+	adc	%rax, %r8
+
+	mulx	24($ap), %rax, %r11
+	adc	%rbx, %r9
+
+	mulx	32($ap), %rbx, %r12
+	adc	%rax, %r10
+
+	mulx	40($ap), %rax, %r13
+	adc	%rbx, %r11
+
+	mulx	48($ap), %rbx, %r14
+	adc	%rax, %r12
+
+	mulx	56($ap), %rax, %r15
+	 mov	8($bp), %rdx
+	adc	%rbx, %r13
+	adc	%rax, %r14
+	adc	\$0, %r15
+
+	xor	$zero, $zero		# cf=0,of=0
+	jmp	.Loop_mulx
+
+.align	32
+.Loop_mulx:
+	movq	%r8, %rbx
+	mulx	($ap), %rax, %r8
+	adcx	%rax, %rbx
+	adox	%r9, %r8
+
+	mulx	8($ap), %rax, %r9
+	adcx	%rax, %r8
+	adox	%r10, %r9
+
+	mulx	16($ap), %rax, %r10
+	adcx	%rax, %r9
+	adox	%r11, %r10
+
+	mulx	24($ap), %rax, %r11
+	adcx	%rax, %r10
+	adox	%r12, %r11
+
+	.byte	0x3e,0xc4,0x62,0xfb,0xf6,0xa6,0x20,0x00,0x00,0x00	# mulx	32($ap), %rax, %r12
+	adcx	%rax, %r11
+	adox	%r13, %r12
+
+	mulx	40($ap), %rax, %r13
+	adcx	%rax, %r12
+	adox	%r14, %r13
+
+	mulx	48($ap), %rax, %r14
+	adcx	%rax, %r13
+	adox	%r15, %r14
+
+	mulx	56($ap), %rax, %r15
+	 movq	64($bp,%rcx,8), %rdx
+	 movq	%rbx, 8+64-8(%rsp,%rcx,8)
+	adcx	%rax, %r14
+	adox	$zero, %r15
+	adcx	$zero, %r15		# cf=0
+
+	inc	%rcx			# of=0
+	jnz	.Loop_mulx
+
+	movq	%r8, %rbx
+	mulx	($ap), %rax, %r8
+	adcx	%rax, %rbx
+	adox	%r9, %r8
+
+	.byte	0xc4,0x62,0xfb,0xf6,0x8e,0x08,0x00,0x00,0x00	# mulx	8($ap), %rax, %r9
+	adcx	%rax, %r8
+	adox	%r10, %r9
+
+	.byte	0xc4,0x62,0xfb,0xf6,0x96,0x10,0x00,0x00,0x00	# mulx	16($ap), %rax, %r10
+	adcx	%rax, %r9
+	adox	%r11, %r10
+
+	mulx	24($ap), %rax, %r11
+	adcx	%rax, %r10
+	adox	%r12, %r11
+
+	mulx	32($ap), %rax, %r12
+	adcx	%rax, %r11
+	adox	%r13, %r12
+
+	mulx	40($ap), %rax, %r13
+	adcx	%rax, %r12
+	adox	%r14, %r13
+
+	.byte	0xc4,0x62,0xfb,0xf6,0xb6,0x30,0x00,0x00,0x00	# mulx	48($ap), %rax, %r14
+	adcx	%rax, %r13
+	adox	%r15, %r14
+
+	.byte	0xc4,0x62,0xfb,0xf6,0xbe,0x38,0x00,0x00,0x00	# mulx	56($ap), %rax, %r15
+	adcx	%rax, %r14
+	adox	$zero, %r15
+	adcx	$zero, %r15
+
+	mov	%rbx, 8+64-8(%rsp)
+	mov	%r8, 8+64(%rsp)
+	mov	%r9, 8+64+8(%rsp)
+	mov	%r10, 8+64+16(%rsp)
+	mov	%r11, 8+64+24(%rsp)
+	mov	%r12, 8+64+32(%rsp)
+	mov	%r13, 8+64+40(%rsp)
+	mov	%r14, 8+64+48(%rsp)
+	mov	%r15, 8+64+56(%rsp)
+
+	ret
+.size	__rsaz_512_mulx,.-__rsaz_512_mulx
+___
+}
+{
+my ($out,$inp,$power)= $win64 ? ("%rcx","%rdx","%r8d") : ("%rdi","%rsi","%edx");
+$code.=<<___;
+.globl	rsaz_512_scatter4
+.type	rsaz_512_scatter4,\@abi-omnipotent
+.align	16
+rsaz_512_scatter4:
+	leaq	($out,$power,8), $out
+	movl	\$8, %r9d
+	jmp	.Loop_scatter
+.align	16
+.Loop_scatter:
+	movq	($inp), %rax
+	leaq	8($inp), $inp
+	movq	%rax, ($out)
+	leaq	128($out), $out
+	decl	%r9d
+	jnz	.Loop_scatter
+	ret
+.size	rsaz_512_scatter4,.-rsaz_512_scatter4
+
+.globl	rsaz_512_gather4
+.type	rsaz_512_gather4,\@abi-omnipotent
+.align	16
+rsaz_512_gather4:
+___
+$code.=<<___	if ($win64);
+.LSEH_begin_rsaz_512_gather4:
+	.byte	0x48,0x81,0xec,0xa8,0x00,0x00,0x00	# sub    $0xa8,%rsp
+	.byte	0x0f,0x29,0x34,0x24			# movaps %xmm6,(%rsp)
+	.byte	0x0f,0x29,0x7c,0x24,0x10		# movaps %xmm7,0x10(%rsp)
+	.byte	0x44,0x0f,0x29,0x44,0x24,0x20		# movaps %xmm8,0x20(%rsp)
+	.byte	0x44,0x0f,0x29,0x4c,0x24,0x30		# movaps %xmm9,0x30(%rsp)
+	.byte	0x44,0x0f,0x29,0x54,0x24,0x40		# movaps %xmm10,0x40(%rsp)
+	.byte	0x44,0x0f,0x29,0x5c,0x24,0x50		# movaps %xmm11,0x50(%rsp)
+	.byte	0x44,0x0f,0x29,0x64,0x24,0x60		# movaps %xmm12,0x60(%rsp)
+	.byte	0x44,0x0f,0x29,0x6c,0x24,0x70		# movaps %xmm13,0x70(%rsp)
+	.byte	0x44,0x0f,0x29,0xb4,0x24,0x80,0,0,0	# movaps %xmm14,0x80(%rsp)
+	.byte	0x44,0x0f,0x29,0xbc,0x24,0x90,0,0,0	# movaps %xmm15,0x90(%rsp)
+___
+$code.=<<___;
+	movd	$power,%xmm8
+	movdqa	.Linc+16(%rip),%xmm1	# 00000002000000020000000200000002
+	movdqa	.Linc(%rip),%xmm0	# 00000001000000010000000000000000
+
+	pshufd	\$0,%xmm8,%xmm8		# broadcast $power
+	movdqa	%xmm1,%xmm7
+	movdqa	%xmm1,%xmm2
+___
+########################################################################
+# calculate mask by comparing 0..15 to $power
+#
+for($i=0;$i<4;$i++) {
+$code.=<<___;
+	paddd	%xmm`$i`,%xmm`$i+1`
+	pcmpeqd	%xmm8,%xmm`$i`
+	movdqa	%xmm7,%xmm`$i+3`
+___
+}
+for(;$i<7;$i++) {
+$code.=<<___;
+	paddd	%xmm`$i`,%xmm`$i+1`
+	pcmpeqd	%xmm8,%xmm`$i`
+___
+}
+$code.=<<___;
+	pcmpeqd	%xmm8,%xmm7
+	movl	\$8, %r9d
+	jmp	.Loop_gather
+.align	16
+.Loop_gather:
+	movdqa	16*0($inp),%xmm8
+	movdqa	16*1($inp),%xmm9
+	movdqa	16*2($inp),%xmm10
+	movdqa	16*3($inp),%xmm11
+	pand	%xmm0,%xmm8
+	movdqa	16*4($inp),%xmm12
+	pand	%xmm1,%xmm9
+	movdqa	16*5($inp),%xmm13
+	pand	%xmm2,%xmm10
+	movdqa	16*6($inp),%xmm14
+	pand	%xmm3,%xmm11
+	movdqa	16*7($inp),%xmm15
+	leaq	128($inp), $inp
+	pand	%xmm4,%xmm12
+	pand	%xmm5,%xmm13
+	pand	%xmm6,%xmm14
+	pand	%xmm7,%xmm15
+	por	%xmm10,%xmm8
+	por	%xmm11,%xmm9
+	por	%xmm12,%xmm8
+	por	%xmm13,%xmm9
+	por	%xmm14,%xmm8
+	por	%xmm15,%xmm9
+
+	por	%xmm9,%xmm8
+	pshufd	\$0x4e,%xmm8,%xmm9
+	por	%xmm9,%xmm8
+	movq	%xmm8,($out)
+	leaq	8($out), $out
+	decl	%r9d
+	jnz	.Loop_gather
+___
+$code.=<<___	if ($win64);
+	movaps	0x00(%rsp),%xmm6
+	movaps	0x10(%rsp),%xmm7
+	movaps	0x20(%rsp),%xmm8
+	movaps	0x30(%rsp),%xmm9
+	movaps	0x40(%rsp),%xmm10
+	movaps	0x50(%rsp),%xmm11
+	movaps	0x60(%rsp),%xmm12
+	movaps	0x70(%rsp),%xmm13
+	movaps	0x80(%rsp),%xmm14
+	movaps	0x90(%rsp),%xmm15
+	add	\$0xa8,%rsp
+___
+$code.=<<___;
+	ret
+.LSEH_end_rsaz_512_gather4:
+.size	rsaz_512_gather4,.-rsaz_512_gather4
+
+.align	64
+.Linc:
+	.long	0,0, 1,1
+	.long	2,2, 2,2
+___
+}
+
+# EXCEPTION_DISPOSITION handler (EXCEPTION_RECORD *rec,ULONG64 frame,
+#		CONTEXT *context,DISPATCHER_CONTEXT *disp)
+if ($win64) {
+$rec="%rcx";
+$frame="%rdx";
+$context="%r8";
+$disp="%r9";
+
+$code.=<<___;
+.extern	__imp_RtlVirtualUnwind
+.type	se_handler,\@abi-omnipotent
+.align	16
+se_handler:
+	push	%rsi
+	push	%rdi
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+	pushfq
+	sub	\$64,%rsp
+
+	mov	120($context),%rax	# pull context->Rax
+	mov	248($context),%rbx	# pull context->Rip
+
+	mov	8($disp),%rsi		# disp->ImageBase
+	mov	56($disp),%r11		# disp->HandlerData
+
+	mov	0(%r11),%r10d		# HandlerData[0]
+	lea	(%rsi,%r10),%r10	# end of prologue label
+	cmp	%r10,%rbx		# context->RipRsp
+
+	mov	4(%r11),%r10d		# HandlerData[1]
+	lea	(%rsi,%r10),%r10	# epilogue label
+	cmp	%r10,%rbx		# context->Rip>=epilogue label
+	jae	.Lcommon_seh_tail
+
+	lea	128+24+48(%rax),%rax
+
+	lea	.Lmul_gather4_epilogue(%rip),%rbx
+	cmp	%r10,%rbx
+	jne	.Lse_not_in_mul_gather4
+
+	lea	0xb0(%rax),%rax
+
+	lea	-48-0xa8(%rax),%rsi
+	lea	512($context),%rdi
+	mov	\$20,%ecx
+	.long	0xa548f3fc		# cld; rep movsq
+
+.Lse_not_in_mul_gather4:
+	mov	-8(%rax),%rbx
+	mov	-16(%rax),%rbp
+	mov	-24(%rax),%r12
+	mov	-32(%rax),%r13
+	mov	-40(%rax),%r14
+	mov	-48(%rax),%r15
+	mov	%rbx,144($context)	# restore context->Rbx
+	mov	%rbp,160($context)	# restore context->Rbp
+	mov	%r12,216($context)	# restore context->R12
+	mov	%r13,224($context)	# restore context->R13
+	mov	%r14,232($context)	# restore context->R14
+	mov	%r15,240($context)	# restore context->R15
+
+.Lcommon_seh_tail:
+	mov	8(%rax),%rdi
+	mov	16(%rax),%rsi
+	mov	%rax,152($context)	# restore context->Rsp
+	mov	%rsi,168($context)	# restore context->Rsi
+	mov	%rdi,176($context)	# restore context->Rdi
+
+	mov	40($disp),%rdi		# disp->ContextRecord
+	mov	$context,%rsi		# context
+	mov	\$154,%ecx		# sizeof(CONTEXT)
+	.long	0xa548f3fc		# cld; rep movsq
+
+	mov	$disp,%rsi
+	xor	%rcx,%rcx		# arg1, UNW_FLAG_NHANDLER
+	mov	8(%rsi),%rdx		# arg2, disp->ImageBase
+	mov	0(%rsi),%r8		# arg3, disp->ControlPc
+	mov	16(%rsi),%r9		# arg4, disp->FunctionEntry
+	mov	40(%rsi),%r10		# disp->ContextRecord
+	lea	56(%rsi),%r11		# &disp->HandlerData
+	lea	24(%rsi),%r12		# &disp->EstablisherFrame
+	mov	%r10,32(%rsp)		# arg5
+	mov	%r11,40(%rsp)		# arg6
+	mov	%r12,48(%rsp)		# arg7
+	mov	%rcx,56(%rsp)		# arg8, (NULL)
+	call	*__imp_RtlVirtualUnwind(%rip)
+
+	mov	\$1,%eax		# ExceptionContinueSearch
+	add	\$64,%rsp
+	popfq
+	pop	%r15
+	pop	%r14
+	pop	%r13
+	pop	%r12
+	pop	%rbp
+	pop	%rbx
+	pop	%rdi
+	pop	%rsi
+	ret
+.size	se_handler,.-se_handler
+
+.section	.pdata
+.align	4
+	.rva	.LSEH_begin_rsaz_512_sqr
+	.rva	.LSEH_end_rsaz_512_sqr
+	.rva	.LSEH_info_rsaz_512_sqr
+
+	.rva	.LSEH_begin_rsaz_512_mul
+	.rva	.LSEH_end_rsaz_512_mul
+	.rva	.LSEH_info_rsaz_512_mul
+
+	.rva	.LSEH_begin_rsaz_512_mul_gather4
+	.rva	.LSEH_end_rsaz_512_mul_gather4
+	.rva	.LSEH_info_rsaz_512_mul_gather4
+
+	.rva	.LSEH_begin_rsaz_512_mul_scatter4
+	.rva	.LSEH_end_rsaz_512_mul_scatter4
+	.rva	.LSEH_info_rsaz_512_mul_scatter4
+
+	.rva	.LSEH_begin_rsaz_512_mul_by_one
+	.rva	.LSEH_end_rsaz_512_mul_by_one
+	.rva	.LSEH_info_rsaz_512_mul_by_one
+
+	.rva	.LSEH_begin_rsaz_512_gather4
+	.rva	.LSEH_end_rsaz_512_gather4
+	.rva	.LSEH_info_rsaz_512_gather4
+
+.section	.xdata
+.align	8
+.LSEH_info_rsaz_512_sqr:
+	.byte	9,0,0,0
+	.rva	se_handler
+	.rva	.Lsqr_body,.Lsqr_epilogue			# HandlerData[]
+.LSEH_info_rsaz_512_mul:
+	.byte	9,0,0,0
+	.rva	se_handler
+	.rva	.Lmul_body,.Lmul_epilogue			# HandlerData[]
+.LSEH_info_rsaz_512_mul_gather4:
+	.byte	9,0,0,0
+	.rva	se_handler
+	.rva	.Lmul_gather4_body,.Lmul_gather4_epilogue	# HandlerData[]
+.LSEH_info_rsaz_512_mul_scatter4:
+	.byte	9,0,0,0
+	.rva	se_handler
+	.rva	.Lmul_scatter4_body,.Lmul_scatter4_epilogue	# HandlerData[]
+.LSEH_info_rsaz_512_mul_by_one:
+	.byte	9,0,0,0
+	.rva	se_handler
+	.rva	.Lmul_by_one_body,.Lmul_by_one_epilogue		# HandlerData[]
+.LSEH_info_rsaz_512_gather4:
+	.byte	0x01,0x46,0x16,0x00
+	.byte	0x46,0xf8,0x09,0x00	# vmovaps 0x90(rsp),xmm15
+	.byte	0x3d,0xe8,0x08,0x00	# vmovaps 0x80(rsp),xmm14
+	.byte	0x34,0xd8,0x07,0x00	# vmovaps 0x70(rsp),xmm13
+	.byte	0x2e,0xc8,0x06,0x00	# vmovaps 0x60(rsp),xmm12
+	.byte	0x28,0xb8,0x05,0x00	# vmovaps 0x50(rsp),xmm11
+	.byte	0x22,0xa8,0x04,0x00	# vmovaps 0x40(rsp),xmm10
+	.byte	0x1c,0x98,0x03,0x00	# vmovaps 0x30(rsp),xmm9
+	.byte	0x16,0x88,0x02,0x00	# vmovaps 0x20(rsp),xmm8
+	.byte	0x10,0x78,0x01,0x00	# vmovaps 0x10(rsp),xmm7
+	.byte	0x0b,0x68,0x00,0x00	# vmovaps 0x00(rsp),xmm6
+	.byte	0x07,0x01,0x15,0x00	# sub     rsp,0xa8
+___
+}
+
+$code =~ s/\`([^\`]*)\`/eval $1/gem;
+print $code;
+close STDOUT;
diff --git a/crypto/bn/asm/s390x-gf2m.pl b/crypto/bn/asm/s390x-gf2m.pl
new file mode 100644
index 000000000..cbd16f421
--- /dev/null
+++ b/crypto/bn/asm/s390x-gf2m.pl
@@ -0,0 +1,228 @@
+#! /usr/bin/env perl
+# Copyright 2011-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+#
+# ====================================================================
+# Written by Andy Polyakov  for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+#
+# May 2011
+#
+# The module implements bn_GF2m_mul_2x2 polynomial multiplication used
+# in bn_gf2m.c. It's kind of low-hanging mechanical port from C for
+# the time being... gcc 4.3 appeared to generate poor code, therefore
+# the effort. And indeed, the module delivers 55%-90%(*) improvement
+# on haviest ECDSA verify and ECDH benchmarks for 163- and 571-bit
+# key lengths on z990, 30%-55%(*) - on z10, and 70%-110%(*) - on z196.
+# This is for 64-bit build. In 32-bit "highgprs" case improvement is
+# even higher, for example on z990 it was measured 80%-150%. ECDSA
+# sign is modest 9%-12% faster. Keep in mind that these coefficients
+# are not ones for bn_GF2m_mul_2x2 itself, as not all CPU time is
+# burnt in it...
+#
+# (*)	gcc 4.1 was observed to deliver better results than gcc 4.3,
+#	so that improvement coefficients can vary from one specific
+#	setup to another.
+
+$flavour = shift;
+
+if ($flavour =~ /3[12]/) {
+        $SIZE_T=4;
+        $g="";
+} else {
+        $SIZE_T=8;
+        $g="g";
+}
+
+while (($output=shift) && ($output!~/\w[\w\-]*\.\w+$/)) {}
+open STDOUT,">$output";
+
+$stdframe=16*$SIZE_T+4*8;
+
+$rp="%r2";
+$a1="%r3";
+$a0="%r4";
+$b1="%r5";
+$b0="%r6";
+
+$ra="%r14";
+$sp="%r15";
+
+@T=("%r0","%r1");
+@i=("%r12","%r13");
+
+($a1,$a2,$a4,$a8,$a12,$a48)=map("%r$_",(6..11));
+($lo,$hi,$b)=map("%r$_",(3..5)); $a=$lo; $mask=$a8;
+
+$code.=<<___;
+.text
+
+.type	_mul_1x1,\@function
+.align	16
+_mul_1x1:
+	lgr	$a1,$a
+	sllg	$a2,$a,1
+	sllg	$a4,$a,2
+	sllg	$a8,$a,3
+
+	srag	$lo,$a1,63			# broadcast 63rd bit
+	nihh	$a1,0x1fff
+	srag	@i[0],$a2,63			# broadcast 62nd bit
+	nihh	$a2,0x3fff
+	srag	@i[1],$a4,63			# broadcast 61st bit
+	nihh	$a4,0x7fff
+	ngr	$lo,$b
+	ngr	@i[0],$b
+	ngr	@i[1],$b
+
+	lghi	@T[0],0
+	lgr	$a12,$a1
+	stg	@T[0],`$stdframe+0*8`($sp)	# tab[0]=0
+	xgr	$a12,$a2
+	stg	$a1,`$stdframe+1*8`($sp)	# tab[1]=a1
+	 lgr	$a48,$a4
+	stg	$a2,`$stdframe+2*8`($sp)	# tab[2]=a2
+	 xgr	$a48,$a8
+	stg	$a12,`$stdframe+3*8`($sp)	# tab[3]=a1^a2
+	 xgr	$a1,$a4
+
+	stg	$a4,`$stdframe+4*8`($sp)	# tab[4]=a4
+	xgr	$a2,$a4
+	stg	$a1,`$stdframe+5*8`($sp)	# tab[5]=a1^a4
+	xgr	$a12,$a4
+	stg	$a2,`$stdframe+6*8`($sp)	# tab[6]=a2^a4
+	 xgr	$a1,$a48
+	stg	$a12,`$stdframe+7*8`($sp)	# tab[7]=a1^a2^a4
+	 xgr	$a2,$a48
+
+	stg	$a8,`$stdframe+8*8`($sp)	# tab[8]=a8
+	xgr	$a12,$a48
+	stg	$a1,`$stdframe+9*8`($sp)	# tab[9]=a1^a8
+	 xgr	$a1,$a4
+	stg	$a2,`$stdframe+10*8`($sp)	# tab[10]=a2^a8
+	 xgr	$a2,$a4
+	stg	$a12,`$stdframe+11*8`($sp)	# tab[11]=a1^a2^a8
+
+	xgr	$a12,$a4
+	stg	$a48,`$stdframe+12*8`($sp)	# tab[12]=a4^a8
+	 srlg	$hi,$lo,1
+	stg	$a1,`$stdframe+13*8`($sp)	# tab[13]=a1^a4^a8
+	 sllg	$lo,$lo,63
+	stg	$a2,`$stdframe+14*8`($sp)	# tab[14]=a2^a4^a8
+	 srlg	@T[0],@i[0],2
+	stg	$a12,`$stdframe+15*8`($sp)	# tab[15]=a1^a2^a4^a8
+
+	lghi	$mask,`0xf<<3`
+	sllg	$a1,@i[0],62
+	 sllg	@i[0],$b,3
+	srlg	@T[1],@i[1],3
+	 ngr	@i[0],$mask
+	sllg	$a2,@i[1],61
+	 srlg	@i[1],$b,4-3
+	xgr	$hi,@T[0]
+	 ngr	@i[1],$mask
+	xgr	$lo,$a1
+	xgr	$hi,@T[1]
+	xgr	$lo,$a2
+
+	xg	$lo,$stdframe(@i[0],$sp)
+	srlg	@i[0],$b,8-3
+	ngr	@i[0],$mask
+___
+for($n=1;$n<14;$n++) {
+$code.=<<___;
+	lg	@T[1],$stdframe(@i[1],$sp)
+	srlg	@i[1],$b,`($n+2)*4`-3
+	sllg	@T[0],@T[1],`$n*4`
+	ngr	@i[1],$mask
+	srlg	@T[1],@T[1],`64-$n*4`
+	xgr	$lo,@T[0]
+	xgr	$hi,@T[1]
+___
+	push(@i,shift(@i)); push(@T,shift(@T));
+}
+$code.=<<___;
+	lg	@T[1],$stdframe(@i[1],$sp)
+	sllg	@T[0],@T[1],`$n*4`
+	srlg	@T[1],@T[1],`64-$n*4`
+	xgr	$lo,@T[0]
+	xgr	$hi,@T[1]
+
+	lg	@T[0],$stdframe(@i[0],$sp)
+	sllg	@T[1],@T[0],`($n+1)*4`
+	srlg	@T[0],@T[0],`64-($n+1)*4`
+	xgr	$lo,@T[1]
+	xgr	$hi,@T[0]
+
+	br	$ra
+.size	_mul_1x1,.-_mul_1x1
+
+.globl	bn_GF2m_mul_2x2
+.type	bn_GF2m_mul_2x2,\@function
+.align	16
+bn_GF2m_mul_2x2:
+	stm${g}	%r3,%r15,3*$SIZE_T($sp)
+
+	lghi	%r1,-$stdframe-128
+	la	%r0,0($sp)
+	la	$sp,0(%r1,$sp)			# alloca
+	st${g}	%r0,0($sp)			# back chain
+___
+if ($SIZE_T==8) {
+my @r=map("%r$_",(6..9));
+$code.=<<___;
+	bras	$ra,_mul_1x1			# a1·b1
+	stmg	$lo,$hi,16($rp)
+
+	lg	$a,`$stdframe+128+4*$SIZE_T`($sp)
+	lg	$b,`$stdframe+128+6*$SIZE_T`($sp)
+	bras	$ra,_mul_1x1			# a0·b0
+	stmg	$lo,$hi,0($rp)
+
+	lg	$a,`$stdframe+128+3*$SIZE_T`($sp)
+	lg	$b,`$stdframe+128+5*$SIZE_T`($sp)
+	xg	$a,`$stdframe+128+4*$SIZE_T`($sp)
+	xg	$b,`$stdframe+128+6*$SIZE_T`($sp)
+	bras	$ra,_mul_1x1			# (a0+a1)·(b0+b1)
+	lmg	@r[0],@r[3],0($rp)
+
+	xgr	$lo,$hi
+	xgr	$hi,@r[1]
+	xgr	$lo,@r[0]
+	xgr	$hi,@r[2]
+	xgr	$lo,@r[3]	
+	xgr	$hi,@r[3]
+	xgr	$lo,$hi
+	stg	$hi,16($rp)
+	stg	$lo,8($rp)
+___
+} else {
+$code.=<<___;
+	sllg	%r3,%r3,32
+	sllg	%r5,%r5,32
+	or	%r3,%r4
+	or	%r5,%r6
+	bras	$ra,_mul_1x1
+	rllg	$lo,$lo,32
+	rllg	$hi,$hi,32
+	stmg	$lo,$hi,0($rp)
+___
+}
+$code.=<<___;
+	lm${g}	%r6,%r15,`$stdframe+128+6*$SIZE_T`($sp)
+	br	$ra
+.size	bn_GF2m_mul_2x2,.-bn_GF2m_mul_2x2
+.string	"GF(2^m) Multiplication for s390x, CRYPTOGAMS by "
+___
+
+$code =~ s/\`([^\`]*)\`/eval($1)/gem;
+print $code;
+close STDOUT;
diff --git a/crypto/bn/asm/s390x-mont.pl b/crypto/bn/asm/s390x-mont.pl
new file mode 100644
index 000000000..2205bc2ca
--- /dev/null
+++ b/crypto/bn/asm/s390x-mont.pl
@@ -0,0 +1,284 @@
+#! /usr/bin/env perl
+# Copyright 2007-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+
+# ====================================================================
+# Written by Andy Polyakov  for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+
+# April 2007.
+#
+# Performance improvement over vanilla C code varies from 85% to 45%
+# depending on key length and benchmark. Unfortunately in this context
+# these are not very impressive results [for code that utilizes "wide"
+# 64x64=128-bit multiplication, which is not commonly available to C
+# programmers], at least hand-coded bn_asm.c replacement is known to
+# provide 30-40% better results for longest keys. Well, on a second
+# thought it's not very surprising, because z-CPUs are single-issue
+# and _strictly_ in-order execution, while bn_mul_mont is more or less
+# dependent on CPU ability to pipe-line instructions and have several
+# of them "in-flight" at the same time. I mean while other methods,
+# for example Karatsuba, aim to minimize amount of multiplications at
+# the cost of other operations increase, bn_mul_mont aim to neatly
+# "overlap" multiplications and the other operations [and on most
+# platforms even minimize the amount of the other operations, in
+# particular references to memory]. But it's possible to improve this
+# module performance by implementing dedicated squaring code-path and
+# possibly by unrolling loops...
+
+# January 2009.
+#
+# Reschedule to minimize/avoid Address Generation Interlock hazard,
+# make inner loops counter-based.
+
+# November 2010.
+#
+# Adapt for -m31 build. If kernel supports what's called "highgprs"
+# feature on Linux [see /proc/cpuinfo], it's possible to use 64-bit
+# instructions and achieve "64-bit" performance even in 31-bit legacy
+# application context. The feature is not specific to any particular
+# processor, as long as it's "z-CPU". Latter implies that the code
+# remains z/Architecture specific. Compatibility with 32-bit BN_ULONG
+# is achieved by swapping words after 64-bit loads, follow _dswap-s.
+# On z990 it was measured to perform 2.6-2.2 times better than
+# compiler-generated code, less for longer keys...
+
+$flavour = shift;
+
+if ($flavour =~ /3[12]/) {
+	$SIZE_T=4;
+	$g="";
+} else {
+	$SIZE_T=8;
+	$g="g";
+}
+
+while (($output=shift) && ($output!~/\w[\w\-]*\.\w+$/)) {}
+open STDOUT,">$output";
+
+$stdframe=16*$SIZE_T+4*8;
+
+$mn0="%r0";
+$num="%r1";
+
+# int bn_mul_mont(
+$rp="%r2";		# BN_ULONG *rp,
+$ap="%r3";		# const BN_ULONG *ap,
+$bp="%r4";		# const BN_ULONG *bp,
+$np="%r5";		# const BN_ULONG *np,
+$n0="%r6";		# const BN_ULONG *n0,
+#$num="160(%r15)"	# int num);
+
+$bi="%r2";	# zaps rp
+$j="%r7";
+
+$ahi="%r8";
+$alo="%r9";
+$nhi="%r10";
+$nlo="%r11";
+$AHI="%r12";
+$NHI="%r13";
+$count="%r14";
+$sp="%r15";
+
+$code.=<<___;
+.text
+.globl	bn_mul_mont
+.type	bn_mul_mont,\@function
+bn_mul_mont:
+	lgf	$num,`$stdframe+$SIZE_T-4`($sp)	# pull $num
+	sla	$num,`log($SIZE_T)/log(2)`	# $num to enumerate bytes
+	la	$bp,0($num,$bp)
+
+	st${g}	%r2,2*$SIZE_T($sp)
+
+	cghi	$num,16		#
+	lghi	%r2,0		#
+	blr	%r14		# if($num<16) return 0;
+___
+$code.=<<___ if ($flavour =~ /3[12]/);
+	tmll	$num,4
+	bnzr	%r14		# if ($num&1) return 0;
+___
+$code.=<<___ if ($flavour !~ /3[12]/);
+	cghi	$num,96		#
+	bhr	%r14		# if($num>96) return 0;
+___
+$code.=<<___;
+	stm${g}	%r3,%r15,3*$SIZE_T($sp)
+
+	lghi	$rp,-$stdframe-8	# leave room for carry bit
+	lcgr	$j,$num		# -$num
+	lgr	%r0,$sp
+	la	$rp,0($rp,$sp)
+	la	$sp,0($j,$rp)	# alloca
+	st${g}	%r0,0($sp)	# back chain
+
+	sra	$num,3		# restore $num
+	la	$bp,0($j,$bp)	# restore $bp
+	ahi	$num,-1		# adjust $num for inner loop
+	lg	$n0,0($n0)	# pull n0
+	_dswap	$n0
+
+	lg	$bi,0($bp)
+	_dswap	$bi
+	lg	$alo,0($ap)
+	_dswap	$alo
+	mlgr	$ahi,$bi	# ap[0]*bp[0]
+	lgr	$AHI,$ahi
+
+	lgr	$mn0,$alo	# "tp[0]"*n0
+	msgr	$mn0,$n0
+
+	lg	$nlo,0($np)	#
+	_dswap	$nlo
+	mlgr	$nhi,$mn0	# np[0]*m1
+	algr	$nlo,$alo	# +="tp[0]"
+	lghi	$NHI,0
+	alcgr	$NHI,$nhi
+
+	la	$j,8(%r0)	# j=1
+	lr	$count,$num
+
+.align	16
+.L1st:
+	lg	$alo,0($j,$ap)
+	_dswap	$alo
+	mlgr	$ahi,$bi	# ap[j]*bp[0]
+	algr	$alo,$AHI
+	lghi	$AHI,0
+	alcgr	$AHI,$ahi
+
+	lg	$nlo,0($j,$np)
+	_dswap	$nlo
+	mlgr	$nhi,$mn0	# np[j]*m1
+	algr	$nlo,$NHI
+	lghi	$NHI,0
+	alcgr	$nhi,$NHI	# +="tp[j]"
+	algr	$nlo,$alo
+	alcgr	$NHI,$nhi
+
+	stg	$nlo,$stdframe-8($j,$sp)	# tp[j-1]=
+	la	$j,8($j)	# j++
+	brct	$count,.L1st
+
+	algr	$NHI,$AHI
+	lghi	$AHI,0
+	alcgr	$AHI,$AHI	# upmost overflow bit
+	stg	$NHI,$stdframe-8($j,$sp)
+	stg	$AHI,$stdframe($j,$sp)
+	la	$bp,8($bp)	# bp++
+
+.Louter:
+	lg	$bi,0($bp)	# bp[i]
+	_dswap	$bi
+	lg	$alo,0($ap)
+	_dswap	$alo
+	mlgr	$ahi,$bi	# ap[0]*bp[i]
+	alg	$alo,$stdframe($sp)	# +=tp[0]
+	lghi	$AHI,0
+	alcgr	$AHI,$ahi
+
+	lgr	$mn0,$alo
+	msgr	$mn0,$n0	# tp[0]*n0
+
+	lg	$nlo,0($np)	# np[0]
+	_dswap	$nlo
+	mlgr	$nhi,$mn0	# np[0]*m1
+	algr	$nlo,$alo	# +="tp[0]"
+	lghi	$NHI,0
+	alcgr	$NHI,$nhi
+
+	la	$j,8(%r0)	# j=1
+	lr	$count,$num
+
+.align	16
+.Linner:
+	lg	$alo,0($j,$ap)
+	_dswap	$alo
+	mlgr	$ahi,$bi	# ap[j]*bp[i]
+	algr	$alo,$AHI
+	lghi	$AHI,0
+	alcgr	$ahi,$AHI
+	alg	$alo,$stdframe($j,$sp)# +=tp[j]
+	alcgr	$AHI,$ahi
+
+	lg	$nlo,0($j,$np)
+	_dswap	$nlo
+	mlgr	$nhi,$mn0	# np[j]*m1
+	algr	$nlo,$NHI
+	lghi	$NHI,0
+	alcgr	$nhi,$NHI
+	algr	$nlo,$alo	# +="tp[j]"
+	alcgr	$NHI,$nhi
+
+	stg	$nlo,$stdframe-8($j,$sp)	# tp[j-1]=
+	la	$j,8($j)	# j++
+	brct	$count,.Linner
+
+	algr	$NHI,$AHI
+	lghi	$AHI,0
+	alcgr	$AHI,$AHI
+	alg	$NHI,$stdframe($j,$sp)# accumulate previous upmost overflow bit
+	lghi	$ahi,0
+	alcgr	$AHI,$ahi	# new upmost overflow bit
+	stg	$NHI,$stdframe-8($j,$sp)
+	stg	$AHI,$stdframe($j,$sp)
+
+	la	$bp,8($bp)	# bp++
+	cl${g}	$bp,`$stdframe+8+4*$SIZE_T`($j,$sp)	# compare to &bp[num]
+	jne	.Louter
+
+	l${g}	$rp,`$stdframe+8+2*$SIZE_T`($j,$sp)	# reincarnate rp
+	la	$ap,$stdframe($sp)
+	ahi	$num,1		# restore $num, incidentally clears "borrow"
+
+	la	$j,0(%r0)
+	lr	$count,$num
+.Lsub:	lg	$alo,0($j,$ap)
+	lg	$nlo,0($j,$np)
+	_dswap	$nlo
+	slbgr	$alo,$nlo
+	stg	$alo,0($j,$rp)
+	la	$j,8($j)
+	brct	$count,.Lsub
+	lghi	$ahi,0
+	slbgr	$AHI,$ahi	# handle upmost carry
+
+	ngr	$ap,$AHI
+	lghi	$np,-1
+	xgr	$np,$AHI
+	ngr	$np,$rp
+	ogr	$ap,$np		# ap=borrow?tp:rp
+
+	la	$j,0(%r0)
+	lgr	$count,$num
+.Lcopy:	lg	$alo,0($j,$ap)		# copy or in-place refresh
+	_dswap	$alo
+	stg	$j,$stdframe($j,$sp)	# zap tp
+	stg	$alo,0($j,$rp)
+	la	$j,8($j)
+	brct	$count,.Lcopy
+
+	la	%r1,`$stdframe+8+6*$SIZE_T`($j,$sp)
+	lm${g}	%r6,%r15,0(%r1)
+	lghi	%r2,1		# signal "processed"
+	br	%r14
+.size	bn_mul_mont,.-bn_mul_mont
+.string	"Montgomery Multiplication for s390x, CRYPTOGAMS by "
+___
+
+foreach (split("\n",$code)) {
+	s/\`([^\`]*)\`/eval $1/ge;
+	s/_dswap\s+(%r[0-9]+)/sprintf("rllg\t%s,%s,32",$1,$1) if($SIZE_T==4)/e;
+	print $_,"\n";
+}
+close STDOUT;
diff --git a/crypto/bn/asm/s390x.S b/crypto/bn/asm/s390x.S
new file mode 100755
index 000000000..292a7a999
--- /dev/null
+++ b/crypto/bn/asm/s390x.S
@@ -0,0 +1,713 @@
+.ident "s390x.S, version 1.1"
+// ====================================================================
+// Copyright 2007-2016 The OpenSSL Project Authors. All Rights Reserved.
+//
+// Licensed under the OpenSSL license (the "License").  You may not use
+// this file except in compliance with the License.  You can obtain a copy
+// in the file LICENSE in the source distribution or at
+// https://www.openssl.org/source/license.html
+// ====================================================================
+
+.text
+
+#define zero	%r0
+
+// BN_ULONG bn_mul_add_words(BN_ULONG *r2,BN_ULONG *r3,int r4,BN_ULONG r5);
+.globl	bn_mul_add_words
+.type	bn_mul_add_words,@function
+.align	4
+bn_mul_add_words:
+	lghi	zero,0		// zero = 0
+	la	%r1,0(%r2)	// put rp aside [to give way to]
+	lghi	%r2,0		// return value
+	ltgfr	%r4,%r4
+	bler	%r14		// if (len<=0) return 0;
+
+	stmg	%r6,%r13,48(%r15)
+	lghi	%r2,3
+	lghi	%r12,0		// carry = 0
+	slgr	%r1,%r3		// rp-=ap
+	nr	%r2,%r4		// len%4
+	sra	%r4,2		// cnt=len/4
+	jz	.Loop1_madd	// carry is incidentally cleared if branch taken
+	algr	zero,zero	// clear carry
+
+	lg	%r7,0(%r3)	// ap[0]
+	lg	%r9,8(%r3)	// ap[1]
+	mlgr	%r6,%r5		// *=w
+	brct	%r4,.Loop4_madd
+	j	.Loop4_madd_tail
+
+.Loop4_madd:
+	mlgr	%r8,%r5
+	lg	%r11,16(%r3)	// ap[i+2]
+	alcgr	%r7,%r12	// +=carry
+	alcgr	%r6,zero
+	alg	%r7,0(%r3,%r1)	// +=rp[i]
+	stg	%r7,0(%r3,%r1)	// rp[i]=
+
+	mlgr	%r10,%r5
+	lg	%r13,24(%r3)
+	alcgr	%r9,%r6
+	alcgr	%r8,zero
+	alg	%r9,8(%r3,%r1)
+	stg	%r9,8(%r3,%r1)
+
+	mlgr	%r12,%r5
+	lg	%r7,32(%r3)
+	alcgr	%r11,%r8
+	alcgr	%r10,zero
+	alg	%r11,16(%r3,%r1)
+	stg	%r11,16(%r3,%r1)
+
+	mlgr	%r6,%r5
+	lg	%r9,40(%r3)
+	alcgr	%r13,%r10
+	alcgr	%r12,zero
+	alg	%r13,24(%r3,%r1)
+	stg	%r13,24(%r3,%r1)
+
+	la	%r3,32(%r3)	// i+=4
+	brct	%r4,.Loop4_madd
+
+.Loop4_madd_tail:
+	mlgr	%r8,%r5
+	lg	%r11,16(%r3)
+	alcgr	%r7,%r12	// +=carry
+	alcgr	%r6,zero
+	alg	%r7,0(%r3,%r1)	// +=rp[i]
+	stg	%r7,0(%r3,%r1)	// rp[i]=
+
+	mlgr	%r10,%r5
+	lg	%r13,24(%r3)
+	alcgr	%r9,%r6
+	alcgr	%r8,zero
+	alg	%r9,8(%r3,%r1)
+	stg	%r9,8(%r3,%r1)
+
+	mlgr	%r12,%r5
+	alcgr	%r11,%r8
+	alcgr	%r10,zero
+	alg	%r11,16(%r3,%r1)
+	stg	%r11,16(%r3,%r1)
+
+	alcgr	%r13,%r10
+	alcgr	%r12,zero
+	alg	%r13,24(%r3,%r1)
+	stg	%r13,24(%r3,%r1)
+
+	la	%r3,32(%r3)	// i+=4
+
+	la	%r2,1(%r2)	// see if len%4 is zero ...
+	brct	%r2,.Loop1_madd	// without touching condition code:-)
+
+.Lend_madd:
+	lgr	%r2,zero	// return value
+	alcgr	%r2,%r12	// collect even carry bit
+	lmg	%r6,%r13,48(%r15)
+	br	%r14
+
+.Loop1_madd:
+	lg	%r7,0(%r3)	// ap[i]
+	mlgr	%r6,%r5		// *=w
+	alcgr	%r7,%r12	// +=carry
+	alcgr	%r6,zero
+	alg	%r7,0(%r3,%r1)	// +=rp[i]
+	stg	%r7,0(%r3,%r1)	// rp[i]=
+
+	lgr	%r12,%r6
+	la	%r3,8(%r3)	// i++
+	brct	%r2,.Loop1_madd
+
+	j	.Lend_madd
+.size	bn_mul_add_words,.-bn_mul_add_words
+
+// BN_ULONG bn_mul_words(BN_ULONG *r2,BN_ULONG *r3,int r4,BN_ULONG r5);
+.globl	bn_mul_words
+.type	bn_mul_words,@function
+.align	4
+bn_mul_words:
+	lghi	zero,0		// zero = 0
+	la	%r1,0(%r2)	// put rp aside
+	lghi	%r2,0		// i=0;
+	ltgfr	%r4,%r4
+	bler	%r14		// if (len<=0) return 0;
+
+	stmg	%r6,%r10,48(%r15)
+	lghi	%r10,3
+	lghi	%r8,0		// carry = 0
+	nr	%r10,%r4	// len%4
+	sra	%r4,2		// cnt=len/4
+	jz	.Loop1_mul	// carry is incidentally cleared if branch taken
+	algr	zero,zero	// clear carry
+
+.Loop4_mul:
+	lg	%r7,0(%r2,%r3)	// ap[i]
+	mlgr	%r6,%r5		// *=w
+	alcgr	%r7,%r8		// +=carry
+	stg	%r7,0(%r2,%r1)	// rp[i]=
+
+	lg	%r9,8(%r2,%r3)
+	mlgr	%r8,%r5
+	alcgr	%r9,%r6
+	stg	%r9,8(%r2,%r1)
+
+	lg	%r7,16(%r2,%r3)
+	mlgr	%r6,%r5
+	alcgr	%r7,%r8
+	stg	%r7,16(%r2,%r1)
+
+	lg	%r9,24(%r2,%r3)
+	mlgr	%r8,%r5
+	alcgr	%r9,%r6
+	stg	%r9,24(%r2,%r1)
+
+	la	%r2,32(%r2)	// i+=4
+	brct	%r4,.Loop4_mul
+
+	la	%r10,1(%r10)		// see if len%4 is zero ...
+	brct	%r10,.Loop1_mul		// without touching condition code:-)
+
+.Lend_mul:
+	alcgr	%r8,zero	// collect carry bit
+	lgr	%r2,%r8
+	lmg	%r6,%r10,48(%r15)
+	br	%r14
+
+.Loop1_mul:
+	lg	%r7,0(%r2,%r3)	// ap[i]
+	mlgr	%r6,%r5		// *=w
+	alcgr	%r7,%r8		// +=carry
+	stg	%r7,0(%r2,%r1)	// rp[i]=
+
+	lgr	%r8,%r6
+	la	%r2,8(%r2)	// i++
+	brct	%r10,.Loop1_mul
+
+	j	.Lend_mul
+.size	bn_mul_words,.-bn_mul_words
+
+// void bn_sqr_words(BN_ULONG *r2,BN_ULONG *r2,int r4)
+.globl	bn_sqr_words
+.type	bn_sqr_words,@function
+.align	4
+bn_sqr_words:
+	ltgfr	%r4,%r4
+	bler	%r14
+
+	stmg	%r6,%r7,48(%r15)
+	srag	%r1,%r4,2	// cnt=len/4
+	jz	.Loop1_sqr
+
+.Loop4_sqr:
+	lg	%r7,0(%r3)
+	mlgr	%r6,%r7
+	stg	%r7,0(%r2)
+	stg	%r6,8(%r2)
+
+	lg	%r7,8(%r3)
+	mlgr	%r6,%r7
+	stg	%r7,16(%r2)
+	stg	%r6,24(%r2)
+
+	lg	%r7,16(%r3)
+	mlgr	%r6,%r7
+	stg	%r7,32(%r2)
+	stg	%r6,40(%r2)
+
+	lg	%r7,24(%r3)
+	mlgr	%r6,%r7
+	stg	%r7,48(%r2)
+	stg	%r6,56(%r2)
+
+	la	%r3,32(%r3)
+	la	%r2,64(%r2)
+	brct	%r1,.Loop4_sqr
+
+	lghi	%r1,3
+	nr	%r4,%r1		// cnt=len%4
+	jz	.Lend_sqr
+
+.Loop1_sqr:
+	lg	%r7,0(%r3)
+	mlgr	%r6,%r7
+	stg	%r7,0(%r2)
+	stg	%r6,8(%r2)
+
+	la	%r3,8(%r3)
+	la	%r2,16(%r2)
+	brct	%r4,.Loop1_sqr
+
+.Lend_sqr:
+	lmg	%r6,%r7,48(%r15)
+	br	%r14
+.size	bn_sqr_words,.-bn_sqr_words
+
+// BN_ULONG bn_div_words(BN_ULONG h,BN_ULONG l,BN_ULONG d);
+.globl	bn_div_words
+.type	bn_div_words,@function
+.align	4
+bn_div_words:
+	dlgr	%r2,%r4
+	lgr	%r2,%r3
+	br	%r14
+.size	bn_div_words,.-bn_div_words
+
+// BN_ULONG bn_add_words(BN_ULONG *r2,BN_ULONG *r3,BN_ULONG *r4,int r5);
+.globl	bn_add_words
+.type	bn_add_words,@function
+.align	4
+bn_add_words:
+	la	%r1,0(%r2)	// put rp aside
+	lghi	%r2,0		// i=0
+	ltgfr	%r5,%r5
+	bler	%r14		// if (len<=0) return 0;
+
+	stg	%r6,48(%r15)
+	lghi	%r6,3
+	nr	%r6,%r5		// len%4
+	sra	%r5,2		// len/4, use sra because it sets condition code
+	jz	.Loop1_add	// carry is incidentally cleared if branch taken
+	algr	%r2,%r2		// clear carry
+
+.Loop4_add:
+	lg	%r0,0(%r2,%r3)
+	alcg	%r0,0(%r2,%r4)
+	stg	%r0,0(%r2,%r1)
+	lg	%r0,8(%r2,%r3)
+	alcg	%r0,8(%r2,%r4)
+	stg	%r0,8(%r2,%r1)
+	lg	%r0,16(%r2,%r3)
+	alcg	%r0,16(%r2,%r4)
+	stg	%r0,16(%r2,%r1)
+	lg	%r0,24(%r2,%r3)
+	alcg	%r0,24(%r2,%r4)
+	stg	%r0,24(%r2,%r1)
+
+	la	%r2,32(%r2)	// i+=4
+	brct	%r5,.Loop4_add
+
+	la	%r6,1(%r6)	// see if len%4 is zero ...
+	brct	%r6,.Loop1_add	// without touching condition code:-)
+
+.Lexit_add:
+	lghi	%r2,0
+	alcgr	%r2,%r2
+	lg	%r6,48(%r15)
+	br	%r14
+
+.Loop1_add:
+	lg	%r0,0(%r2,%r3)
+	alcg	%r0,0(%r2,%r4)
+	stg	%r0,0(%r2,%r1)
+
+	la	%r2,8(%r2)	// i++
+	brct	%r6,.Loop1_add
+
+	j	.Lexit_add
+.size	bn_add_words,.-bn_add_words
+
+// BN_ULONG bn_sub_words(BN_ULONG *r2,BN_ULONG *r3,BN_ULONG *r4,int r5);
+.globl	bn_sub_words
+.type	bn_sub_words,@function
+.align	4
+bn_sub_words:
+	la	%r1,0(%r2)	// put rp aside
+	lghi	%r2,0		// i=0
+	ltgfr	%r5,%r5
+	bler	%r14		// if (len<=0) return 0;
+
+	stg	%r6,48(%r15)
+	lghi	%r6,3
+	nr	%r6,%r5		// len%4
+	sra	%r5,2		// len/4, use sra because it sets condition code
+	jnz	.Loop4_sub	// borrow is incidentally cleared if branch taken
+	slgr	%r2,%r2		// clear borrow
+
+.Loop1_sub:
+	lg	%r0,0(%r2,%r3)
+	slbg	%r0,0(%r2,%r4)
+	stg	%r0,0(%r2,%r1)
+
+	la	%r2,8(%r2)	// i++
+	brct	%r6,.Loop1_sub
+	j	.Lexit_sub
+
+.Loop4_sub:
+	lg	%r0,0(%r2,%r3)
+	slbg	%r0,0(%r2,%r4)
+	stg	%r0,0(%r2,%r1)
+	lg	%r0,8(%r2,%r3)
+	slbg	%r0,8(%r2,%r4)
+	stg	%r0,8(%r2,%r1)
+	lg	%r0,16(%r2,%r3)
+	slbg	%r0,16(%r2,%r4)
+	stg	%r0,16(%r2,%r1)
+	lg	%r0,24(%r2,%r3)
+	slbg	%r0,24(%r2,%r4)
+	stg	%r0,24(%r2,%r1)
+
+	la	%r2,32(%r2)	// i+=4
+	brct	%r5,.Loop4_sub
+
+	la	%r6,1(%r6)	// see if len%4 is zero ...
+	brct	%r6,.Loop1_sub	// without touching condition code:-)
+
+.Lexit_sub:
+	lghi	%r2,0
+	slbgr	%r2,%r2
+	lcgr	%r2,%r2
+	lg	%r6,48(%r15)
+	br	%r14
+.size	bn_sub_words,.-bn_sub_words
+
+#define c1	%r1
+#define c2	%r5
+#define c3	%r8
+
+#define mul_add_c(ai,bi,c1,c2,c3)	\
+	lg	%r7,ai*8(%r3);		\
+	mlg	%r6,bi*8(%r4);		\
+	algr	c1,%r7;			\
+	alcgr	c2,%r6;			\
+	alcgr	c3,zero
+
+// void bn_mul_comba8(BN_ULONG *r2,BN_ULONG *r3,BN_ULONG *r4);
+.globl	bn_mul_comba8
+.type	bn_mul_comba8,@function
+.align	4
+bn_mul_comba8:
+	stmg	%r6,%r8,48(%r15)
+
+	lghi	c1,0
+	lghi	c2,0
+	lghi	c3,0
+	lghi	zero,0
+
+	mul_add_c(0,0,c1,c2,c3);
+	stg	c1,0*8(%r2)
+	lghi	c1,0
+
+	mul_add_c(0,1,c2,c3,c1);
+	mul_add_c(1,0,c2,c3,c1);
+	stg	c2,1*8(%r2)
+	lghi	c2,0
+
+	mul_add_c(2,0,c3,c1,c2);
+	mul_add_c(1,1,c3,c1,c2);
+	mul_add_c(0,2,c3,c1,c2);
+	stg	c3,2*8(%r2)
+	lghi	c3,0
+
+	mul_add_c(0,3,c1,c2,c3);
+	mul_add_c(1,2,c1,c2,c3);
+	mul_add_c(2,1,c1,c2,c3);
+	mul_add_c(3,0,c1,c2,c3);
+	stg	c1,3*8(%r2)
+	lghi	c1,0
+
+	mul_add_c(4,0,c2,c3,c1);
+	mul_add_c(3,1,c2,c3,c1);
+	mul_add_c(2,2,c2,c3,c1);
+	mul_add_c(1,3,c2,c3,c1);
+	mul_add_c(0,4,c2,c3,c1);
+	stg	c2,4*8(%r2)
+	lghi	c2,0
+
+	mul_add_c(0,5,c3,c1,c2);
+	mul_add_c(1,4,c3,c1,c2);
+	mul_add_c(2,3,c3,c1,c2);
+	mul_add_c(3,2,c3,c1,c2);
+	mul_add_c(4,1,c3,c1,c2);
+	mul_add_c(5,0,c3,c1,c2);
+	stg	c3,5*8(%r2)
+	lghi	c3,0
+
+	mul_add_c(6,0,c1,c2,c3);
+	mul_add_c(5,1,c1,c2,c3);
+	mul_add_c(4,2,c1,c2,c3);
+	mul_add_c(3,3,c1,c2,c3);
+	mul_add_c(2,4,c1,c2,c3);
+	mul_add_c(1,5,c1,c2,c3);
+	mul_add_c(0,6,c1,c2,c3);
+	stg	c1,6*8(%r2)
+	lghi	c1,0
+
+	mul_add_c(0,7,c2,c3,c1);
+	mul_add_c(1,6,c2,c3,c1);
+	mul_add_c(2,5,c2,c3,c1);
+	mul_add_c(3,4,c2,c3,c1);
+	mul_add_c(4,3,c2,c3,c1);
+	mul_add_c(5,2,c2,c3,c1);
+	mul_add_c(6,1,c2,c3,c1);
+	mul_add_c(7,0,c2,c3,c1);
+	stg	c2,7*8(%r2)
+	lghi	c2,0
+
+	mul_add_c(7,1,c3,c1,c2);
+	mul_add_c(6,2,c3,c1,c2);
+	mul_add_c(5,3,c3,c1,c2);
+	mul_add_c(4,4,c3,c1,c2);
+	mul_add_c(3,5,c3,c1,c2);
+	mul_add_c(2,6,c3,c1,c2);
+	mul_add_c(1,7,c3,c1,c2);
+	stg	c3,8*8(%r2)
+	lghi	c3,0
+
+	mul_add_c(2,7,c1,c2,c3);
+	mul_add_c(3,6,c1,c2,c3);
+	mul_add_c(4,5,c1,c2,c3);
+	mul_add_c(5,4,c1,c2,c3);
+	mul_add_c(6,3,c1,c2,c3);
+	mul_add_c(7,2,c1,c2,c3);
+	stg	c1,9*8(%r2)
+	lghi	c1,0
+
+	mul_add_c(7,3,c2,c3,c1);
+	mul_add_c(6,4,c2,c3,c1);
+	mul_add_c(5,5,c2,c3,c1);
+	mul_add_c(4,6,c2,c3,c1);
+	mul_add_c(3,7,c2,c3,c1);
+	stg	c2,10*8(%r2)
+	lghi	c2,0
+
+	mul_add_c(4,7,c3,c1,c2);
+	mul_add_c(5,6,c3,c1,c2);
+	mul_add_c(6,5,c3,c1,c2);
+	mul_add_c(7,4,c3,c1,c2);
+	stg	c3,11*8(%r2)
+	lghi	c3,0
+
+	mul_add_c(7,5,c1,c2,c3);
+	mul_add_c(6,6,c1,c2,c3);
+	mul_add_c(5,7,c1,c2,c3);
+	stg	c1,12*8(%r2)
+	lghi	c1,0
+
+
+	mul_add_c(6,7,c2,c3,c1);
+	mul_add_c(7,6,c2,c3,c1);
+	stg	c2,13*8(%r2)
+	lghi	c2,0
+
+	mul_add_c(7,7,c3,c1,c2);
+	stg	c3,14*8(%r2)
+	stg	c1,15*8(%r2)
+
+	lmg	%r6,%r8,48(%r15)
+	br	%r14
+.size	bn_mul_comba8,.-bn_mul_comba8
+
+// void bn_mul_comba4(BN_ULONG *r2,BN_ULONG *r3,BN_ULONG *r4);
+.globl	bn_mul_comba4
+.type	bn_mul_comba4,@function
+.align	4
+bn_mul_comba4:
+	stmg	%r6,%r8,48(%r15)
+
+	lghi	c1,0
+	lghi	c2,0
+	lghi	c3,0
+	lghi	zero,0
+
+	mul_add_c(0,0,c1,c2,c3);
+	stg	c1,0*8(%r3)
+	lghi	c1,0
+
+	mul_add_c(0,1,c2,c3,c1);
+	mul_add_c(1,0,c2,c3,c1);
+	stg	c2,1*8(%r2)
+	lghi	c2,0
+
+	mul_add_c(2,0,c3,c1,c2);
+	mul_add_c(1,1,c3,c1,c2);
+	mul_add_c(0,2,c3,c1,c2);
+	stg	c3,2*8(%r2)
+	lghi	c3,0
+
+	mul_add_c(0,3,c1,c2,c3);
+	mul_add_c(1,2,c1,c2,c3);
+	mul_add_c(2,1,c1,c2,c3);
+	mul_add_c(3,0,c1,c2,c3);
+	stg	c1,3*8(%r2)
+	lghi	c1,0
+
+	mul_add_c(3,1,c2,c3,c1);
+	mul_add_c(2,2,c2,c3,c1);
+	mul_add_c(1,3,c2,c3,c1);
+	stg	c2,4*8(%r2)
+	lghi	c2,0
+
+	mul_add_c(2,3,c3,c1,c2);
+	mul_add_c(3,2,c3,c1,c2);
+	stg	c3,5*8(%r2)
+	lghi	c3,0
+
+	mul_add_c(3,3,c1,c2,c3);
+	stg	c1,6*8(%r2)
+	stg	c2,7*8(%r2)
+
+	stmg	%r6,%r8,48(%r15)
+	br	%r14
+.size	bn_mul_comba4,.-bn_mul_comba4
+
+#define sqr_add_c(ai,c1,c2,c3)		\
+	lg	%r7,ai*8(%r3);		\
+	mlgr	%r6,%r7;		\
+	algr	c1,%r7;			\
+	alcgr	c2,%r6;			\
+	alcgr	c3,zero
+
+#define sqr_add_c2(ai,aj,c1,c2,c3)	\
+	lg	%r7,ai*8(%r3);		\
+	mlg	%r6,aj*8(%r3);		\
+	algr	c1,%r7;			\
+	alcgr	c2,%r6;			\
+	alcgr	c3,zero;		\
+	algr	c1,%r7;			\
+	alcgr	c2,%r6;			\
+	alcgr	c3,zero
+
+// void bn_sqr_comba8(BN_ULONG *r2,BN_ULONG *r3);
+.globl	bn_sqr_comba8
+.type	bn_sqr_comba8,@function
+.align	4
+bn_sqr_comba8:
+	stmg	%r6,%r8,48(%r15)
+
+	lghi	c1,0
+	lghi	c2,0
+	lghi	c3,0
+	lghi	zero,0
+
+	sqr_add_c(0,c1,c2,c3);
+	stg	c1,0*8(%r2)
+	lghi	c1,0
+
+	sqr_add_c2(1,0,c2,c3,c1);
+	stg	c2,1*8(%r2)
+	lghi	c2,0
+
+	sqr_add_c(1,c3,c1,c2);
+	sqr_add_c2(2,0,c3,c1,c2);
+	stg	c3,2*8(%r2)
+	lghi	c3,0
+
+	sqr_add_c2(3,0,c1,c2,c3);
+	sqr_add_c2(2,1,c1,c2,c3);
+	stg	c1,3*8(%r2)
+	lghi	c1,0
+
+	sqr_add_c(2,c2,c3,c1);
+	sqr_add_c2(3,1,c2,c3,c1);
+	sqr_add_c2(4,0,c2,c3,c1);
+	stg	c2,4*8(%r2)
+	lghi	c2,0
+
+	sqr_add_c2(5,0,c3,c1,c2);
+	sqr_add_c2(4,1,c3,c1,c2);
+	sqr_add_c2(3,2,c3,c1,c2);
+	stg	c3,5*8(%r2)
+	lghi	c3,0
+
+	sqr_add_c(3,c1,c2,c3);
+	sqr_add_c2(4,2,c1,c2,c3);
+	sqr_add_c2(5,1,c1,c2,c3);
+	sqr_add_c2(6,0,c1,c2,c3);
+	stg	c1,6*8(%r2)
+	lghi	c1,0
+
+	sqr_add_c2(7,0,c2,c3,c1);
+	sqr_add_c2(6,1,c2,c3,c1);
+	sqr_add_c2(5,2,c2,c3,c1);
+	sqr_add_c2(4,3,c2,c3,c1);
+	stg	c2,7*8(%r2)
+	lghi	c2,0
+
+	sqr_add_c(4,c3,c1,c2);
+	sqr_add_c2(5,3,c3,c1,c2);
+	sqr_add_c2(6,2,c3,c1,c2);
+	sqr_add_c2(7,1,c3,c1,c2);
+	stg	c3,8*8(%r2)
+	lghi	c3,0
+
+	sqr_add_c2(7,2,c1,c2,c3);
+	sqr_add_c2(6,3,c1,c2,c3);
+	sqr_add_c2(5,4,c1,c2,c3);
+	stg	c1,9*8(%r2)
+	lghi	c1,0
+
+	sqr_add_c(5,c2,c3,c1);
+	sqr_add_c2(6,4,c2,c3,c1);
+	sqr_add_c2(7,3,c2,c3,c1);
+	stg	c2,10*8(%r2)
+	lghi	c2,0
+
+	sqr_add_c2(7,4,c3,c1,c2);
+	sqr_add_c2(6,5,c3,c1,c2);
+	stg	c3,11*8(%r2)
+	lghi	c3,0
+
+	sqr_add_c(6,c1,c2,c3);
+	sqr_add_c2(7,5,c1,c2,c3);
+	stg	c1,12*8(%r2)
+	lghi	c1,0
+
+	sqr_add_c2(7,6,c2,c3,c1);
+	stg	c2,13*8(%r2)
+	lghi	c2,0
+
+	sqr_add_c(7,c3,c1,c2);
+	stg	c3,14*8(%r2)
+	stg	c1,15*8(%r2)
+
+	lmg	%r6,%r8,48(%r15)
+	br	%r14
+.size	bn_sqr_comba8,.-bn_sqr_comba8
+
+// void bn_sqr_comba4(BN_ULONG *r2,BN_ULONG *r3);
+.globl bn_sqr_comba4
+.type	bn_sqr_comba4,@function
+.align	4
+bn_sqr_comba4:
+	stmg	%r6,%r8,48(%r15)
+
+	lghi	c1,0
+	lghi	c2,0
+	lghi	c3,0
+	lghi	zero,0
+
+	sqr_add_c(0,c1,c2,c3);
+	stg	c1,0*8(%r2)
+	lghi	c1,0
+
+	sqr_add_c2(1,0,c2,c3,c1);
+	stg	c2,1*8(%r2)
+	lghi	c2,0
+
+	sqr_add_c(1,c3,c1,c2);
+	sqr_add_c2(2,0,c3,c1,c2);
+	stg	c3,2*8(%r2)
+	lghi	c3,0
+
+	sqr_add_c2(3,0,c1,c2,c3);
+	sqr_add_c2(2,1,c1,c2,c3);
+	stg	c1,3*8(%r2)
+	lghi	c1,0
+
+	sqr_add_c(2,c2,c3,c1);
+	sqr_add_c2(3,1,c2,c3,c1);
+	stg	c2,4*8(%r2)
+	lghi	c2,0
+
+	sqr_add_c2(3,2,c3,c1,c2);
+	stg	c3,5*8(%r2)
+	lghi	c3,0
+
+	sqr_add_c(3,c1,c2,c3);
+	stg	c1,6*8(%r2)
+	stg	c2,7*8(%r2)
+
+	lmg	%r6,%r8,48(%r15)
+	br	%r14
+.size	bn_sqr_comba4,.-bn_sqr_comba4
diff --git a/crypto/bn/asm/sparct4-mont.pl b/crypto/bn/asm/sparct4-mont.pl
new file mode 100755
index 000000000..4faf66f10
--- /dev/null
+++ b/crypto/bn/asm/sparct4-mont.pl
@@ -0,0 +1,1232 @@
+#! /usr/bin/env perl
+# Copyright 2012-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+
+# ====================================================================
+# Written by David S. Miller  and Andy Polyakov
+# . The module is licensed under 2-clause BSD
+# license. November 2012. All rights reserved.
+# ====================================================================
+
+######################################################################
+# Montgomery squaring-n-multiplication module for SPARC T4.
+#
+# The module consists of three parts:
+#
+# 1) collection of "single-op" subroutines that perform single
+#    operation, Montgomery squaring or multiplication, on 512-,
+#    1024-, 1536- and 2048-bit operands;
+# 2) collection of "multi-op" subroutines that perform 5 squaring and
+#    1 multiplication operations on operands of above lengths;
+# 3) fall-back and helper VIS3 subroutines.
+#
+# RSA sign is dominated by multi-op subroutine, while RSA verify and
+# DSA - by single-op. Special note about 4096-bit RSA verify result.
+# Operands are too long for dedicated hardware and it's handled by
+# VIS3 code, which is why you don't see any improvement. It's surely
+# possible to improve it [by deploying 'mpmul' instruction], maybe in
+# the future...
+#
+# Performance improvement.
+#
+# 64-bit process, VIS3:
+#                   sign    verify    sign/s verify/s
+# rsa 1024 bits 0.000628s 0.000028s   1592.4  35434.4
+# rsa 2048 bits 0.003282s 0.000106s    304.7   9438.3
+# rsa 4096 bits 0.025866s 0.000340s     38.7   2940.9
+# dsa 1024 bits 0.000301s 0.000332s   3323.7   3013.9
+# dsa 2048 bits 0.001056s 0.001233s    946.9    810.8
+#
+# 64-bit process, this module:
+#                   sign    verify    sign/s verify/s
+# rsa 1024 bits 0.000256s 0.000016s   3904.4  61411.9
+# rsa 2048 bits 0.000946s 0.000029s   1056.8  34292.7
+# rsa 4096 bits 0.005061s 0.000340s    197.6   2940.5
+# dsa 1024 bits 0.000176s 0.000195s   5674.7   5130.5
+# dsa 2048 bits 0.000296s 0.000354s   3383.2   2827.6
+#
+######################################################################
+# 32-bit process, VIS3:
+#                   sign    verify    sign/s verify/s
+# rsa 1024 bits 0.000665s 0.000028s   1504.8  35233.3
+# rsa 2048 bits 0.003349s 0.000106s    298.6   9433.4
+# rsa 4096 bits 0.025959s 0.000341s     38.5   2934.8
+# dsa 1024 bits 0.000320s 0.000341s   3123.3   2929.6
+# dsa 2048 bits 0.001101s 0.001260s    908.2    793.4
+#
+# 32-bit process, this module:
+#                   sign    verify    sign/s verify/s
+# rsa 1024 bits 0.000301s 0.000017s   3317.1  60240.0
+# rsa 2048 bits 0.001034s 0.000030s    966.9  33812.7
+# rsa 4096 bits 0.005244s 0.000341s    190.7   2935.4
+# dsa 1024 bits 0.000201s 0.000205s   4976.1   4879.2
+# dsa 2048 bits 0.000328s 0.000360s   3051.1   2774.2
+#
+# 32-bit code is prone to performance degradation as interrupt rate
+# dispatched to CPU executing the code grows. This is because in
+# standard process of handling interrupt in 32-bit process context
+# upper halves of most integer registers used as input or output are
+# zeroed. This renders result invalid, and operation has to be re-run.
+# If CPU is "bothered" with timer interrupts only, the penalty is
+# hardly measurable. But in order to mitigate this problem for higher
+# interrupt rates contemporary Linux kernel recognizes biased stack
+# even in 32-bit process context and preserves full register contents.
+# See http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=517ffce4e1a03aea979fe3a18a3dd1761a24fafb
+# for details.
+
+$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+push(@INC,"${dir}","${dir}../../perlasm");
+require "sparcv9_modes.pl";
+
+$output = pop;
+open STDOUT,">$output";
+
+$code.=<<___;
+#include "sparc_arch.h"
+
+#ifdef	__arch64__
+.register	%g2,#scratch
+.register	%g3,#scratch
+#endif
+
+.section	".text",#alloc,#execinstr
+
+#ifdef	__PIC__
+SPARC_PIC_THUNK(%g1)
+#endif
+___
+
+########################################################################
+# Register layout for mont[mul|sqr] instructions.
+# For details see "Oracle SPARC Architecture 2011" manual at
+# http://www.oracle.com/technetwork/server-storage/sun-sparc-enterprise/documentation/.
+#
+my @R=map("%f".2*$_,(0..11,30,31,12..29));
+my @N=(map("%l$_",(0..7)),map("%o$_",(0..5))); @N=(@N,@N,@N[0..3]);
+my @A=(@N[0..13],@R[14..31]);
+my @B=(map("%i$_",(0..5)),map("%l$_",(0..7))); @B=(@B,@B,map("%o$_",(0..3)));
+
+########################################################################
+# int bn_mul_mont_t4_$NUM(u64 *rp,const u64 *ap,const u64 *bp,
+#			  const u64 *np,const BN_ULONG *n0);
+#
+sub generate_bn_mul_mont_t4() {
+my $NUM=shift;
+my ($rp,$ap,$bp,$np,$sentinel)=map("%g$_",(1..5));
+
+$code.=<<___;
+.globl	bn_mul_mont_t4_$NUM
+.align	32
+bn_mul_mont_t4_$NUM:
+#ifdef	__arch64__
+	mov	0,$sentinel
+	mov	-128,%g4
+#elif defined(SPARCV9_64BIT_STACK)
+	SPARC_LOAD_ADDRESS_LEAF(OPENSSL_sparcv9cap_P,%g1,%g5)
+	ld	[%g1+0],%g1	! OPENSSL_sparcv9_P[0]
+	mov	-2047,%g4
+	and	%g1,SPARCV9_64BIT_STACK,%g1
+	movrz	%g1,0,%g4
+	mov	-1,$sentinel
+	add	%g4,-128,%g4
+#else
+	mov	-1,$sentinel
+	mov	-128,%g4
+#endif
+	sllx	$sentinel,32,$sentinel
+	save	%sp,%g4,%sp
+#ifndef	__arch64__
+	save	%sp,-128,%sp	! warm it up
+	save	%sp,-128,%sp
+	save	%sp,-128,%sp
+	save	%sp,-128,%sp
+	save	%sp,-128,%sp
+	save	%sp,-128,%sp
+	restore
+	restore
+	restore
+	restore
+	restore
+	restore
+#endif
+	and	%sp,1,%g4
+	or	$sentinel,%fp,%fp
+	or	%g4,$sentinel,$sentinel
+
+	! copy arguments to global registers
+	mov	%i0,$rp
+	mov	%i1,$ap
+	mov	%i2,$bp
+	mov	%i3,$np
+	ld	[%i4+0],%f1	! load *n0
+	ld	[%i4+4],%f0
+	fsrc2	%f0,%f60
+___
+
+# load ap[$NUM] ########################################################
+$code.=<<___;
+	save	%sp,-128,%sp;		or	$sentinel,%fp,%fp
+___
+for($i=0; $i<14 && $i<$NUM; $i++) {
+my $lo=$i<13?@A[$i+1]:"%o7";
+$code.=<<___;
+	ld	[$ap+$i*8+0],$lo
+	ld	[$ap+$i*8+4],@A[$i]
+	sllx	@A[$i],32,@A[$i]
+	or	$lo,@A[$i],@A[$i]
+___
+}
+for(; $i<$NUM; $i++) {
+my ($hi,$lo)=("%f".2*($i%4),"%f".(2*($i%4)+1));
+$code.=<<___;
+	ld	[$ap+$i*8+0],$lo
+	ld	[$ap+$i*8+4],$hi
+	fsrc2	$hi,@A[$i]
+___
+}
+# load np[$NUM] ########################################################
+$code.=<<___;
+	save	%sp,-128,%sp;		or	$sentinel,%fp,%fp
+___
+for($i=0; $i<14 && $i<$NUM; $i++) {
+my $lo=$i<13?@N[$i+1]:"%o7";
+$code.=<<___;
+	ld	[$np+$i*8+0],$lo
+	ld	[$np+$i*8+4],@N[$i]
+	sllx	@N[$i],32,@N[$i]
+	or	$lo,@N[$i],@N[$i]
+___
+}
+$code.=<<___;
+	save	%sp,-128,%sp;		or	$sentinel,%fp,%fp
+___
+for(; $i<28 && $i<$NUM; $i++) {
+my $lo=$i<27?@N[$i+1]:"%o7";
+$code.=<<___;
+	ld	[$np+$i*8+0],$lo
+	ld	[$np+$i*8+4],@N[$i]
+	sllx	@N[$i],32,@N[$i]
+	or	$lo,@N[$i],@N[$i]
+___
+}
+$code.=<<___;
+	save	%sp,-128,%sp;		or	$sentinel,%fp,%fp
+___
+for(; $i<$NUM; $i++) {
+my $lo=($i<$NUM-1)?@N[$i+1]:"%o7";
+$code.=<<___;
+	ld	[$np+$i*8+0],$lo
+	ld	[$np+$i*8+4],@N[$i]
+	sllx	@N[$i],32,@N[$i]
+	or	$lo,@N[$i],@N[$i]
+___
+}
+$code.=<<___;
+	cmp	$ap,$bp
+	be	SIZE_T_CC,.Lmsquare_$NUM
+	nop
+___
+
+# load bp[$NUM] ########################################################
+$code.=<<___;
+	save	%sp,-128,%sp;		or	$sentinel,%fp,%fp
+___
+for($i=0; $i<14 && $i<$NUM; $i++) {
+my $lo=$i<13?@B[$i+1]:"%o7";
+$code.=<<___;
+	ld	[$bp+$i*8+0],$lo
+	ld	[$bp+$i*8+4],@B[$i]
+	sllx	@B[$i],32,@B[$i]
+	or	$lo,@B[$i],@B[$i]
+___
+}
+$code.=<<___;
+	save	%sp,-128,%sp;		or	$sentinel,%fp,%fp
+___
+for(; $i<$NUM; $i++) {
+my $lo=($i<$NUM-1)?@B[$i+1]:"%o7";
+$code.=<<___;
+	ld	[$bp+$i*8+0],$lo
+	ld	[$bp+$i*8+4],@B[$i]
+	sllx	@B[$i],32,@B[$i]
+	or	$lo,@B[$i],@B[$i]
+___
+}
+# magic ################################################################
+$code.=<<___;
+	.word	0x81b02920+$NUM-1	! montmul	$NUM-1
+.Lmresume_$NUM:
+	fbu,pn	%fcc3,.Lmabort_$NUM
+#ifndef	__arch64__
+	and	%fp,$sentinel,$sentinel
+	brz,pn	$sentinel,.Lmabort_$NUM
+#endif
+	nop
+#ifdef	__arch64__
+	restore
+	restore
+	restore
+	restore
+	restore
+#else
+	restore;		and	%fp,$sentinel,$sentinel
+	restore;		and	%fp,$sentinel,$sentinel
+	restore;		and	%fp,$sentinel,$sentinel
+	restore;		and	%fp,$sentinel,$sentinel
+	 brz,pn	$sentinel,.Lmabort1_$NUM
+	restore
+#endif
+___
+
+# save tp[$NUM] ########################################################
+for($i=0; $i<14 && $i<$NUM; $i++) {
+$code.=<<___;
+	movxtod	@A[$i],@R[$i]
+___
+}
+$code.=<<___;
+#ifdef	__arch64__
+	restore
+#else
+	 and	%fp,$sentinel,$sentinel
+	restore
+	 and	$sentinel,1,%o7
+	 and	%fp,$sentinel,$sentinel
+	 srl	%fp,0,%fp		! just in case?
+	 or	%o7,$sentinel,$sentinel
+	brz,a,pn $sentinel,.Lmdone_$NUM
+	mov	0,%i0		! return failure
+#endif
+___
+for($i=0; $i<12 && $i<$NUM; $i++) {
+@R[$i] =~ /%f([0-9]+)/;
+my $lo = "%f".($1+1);
+$code.=<<___;
+	st	$lo,[$rp+$i*8+0]
+	st	@R[$i],[$rp+$i*8+4]
+___
+}
+for(; $i<$NUM; $i++) {
+my ($hi,$lo)=("%f".2*($i%4),"%f".(2*($i%4)+1));
+$code.=<<___;
+	fsrc2	@R[$i],$hi
+	st	$lo,[$rp+$i*8+0]
+	st	$hi,[$rp+$i*8+4]
+___
+}
+$code.=<<___;
+	mov	1,%i0		! return success
+.Lmdone_$NUM:
+	ret
+	restore
+
+.Lmabort_$NUM:
+	restore
+	restore
+	restore
+	restore
+	restore
+.Lmabort1_$NUM:
+	restore
+
+	mov	0,%i0		! return failure
+	ret
+	restore
+
+.align	32
+.Lmsquare_$NUM:
+	save	%sp,-128,%sp;		or	$sentinel,%fp,%fp
+	save	%sp,-128,%sp;		or	$sentinel,%fp,%fp
+	.word   0x81b02940+$NUM-1	! montsqr	$NUM-1
+	ba	.Lmresume_$NUM
+	nop
+.type	bn_mul_mont_t4_$NUM, #function
+.size	bn_mul_mont_t4_$NUM, .-bn_mul_mont_t4_$NUM
+___
+}
+
+for ($i=8;$i<=32;$i+=8) {
+	&generate_bn_mul_mont_t4($i);
+}
+
+########################################################################
+#
+sub load_ccr {
+my ($ptbl,$pwr,$ccr,$skip_wr)=@_;
+$code.=<<___;
+	srl	$pwr,	2,	%o4
+	and	$pwr,	3,	%o5
+	and	%o4,	7,	%o4
+	sll	%o5,	3,	%o5	! offset within first cache line
+	add	%o5,	$ptbl,	$ptbl	! of the pwrtbl
+	or	%g0,	1,	%o5
+	sll	%o5,	%o4,	$ccr
+___
+$code.=<<___	if (!$skip_wr);
+	wr	$ccr,	%g0,	%ccr
+___
+}
+sub load_b_pair {
+my ($pwrtbl,$B0,$B1)=@_;
+
+$code.=<<___;
+	ldx	[$pwrtbl+0*32],	$B0
+	ldx	[$pwrtbl+8*32],	$B1
+	ldx	[$pwrtbl+1*32],	%o4
+	ldx	[$pwrtbl+9*32],	%o5
+	movvs	%icc,	%o4,	$B0
+	ldx	[$pwrtbl+2*32],	%o4
+	movvs	%icc,	%o5,	$B1
+	ldx	[$pwrtbl+10*32],%o5
+	move	%icc,	%o4,	$B0
+	ldx	[$pwrtbl+3*32],	%o4
+	move	%icc,	%o5,	$B1
+	ldx	[$pwrtbl+11*32],%o5
+	movneg	%icc,	%o4,	$B0
+	ldx	[$pwrtbl+4*32],	%o4
+	movneg	%icc,	%o5,	$B1
+	ldx	[$pwrtbl+12*32],%o5
+	movcs	%xcc,	%o4,	$B0
+	ldx	[$pwrtbl+5*32],%o4
+	movcs	%xcc,	%o5,	$B1
+	ldx	[$pwrtbl+13*32],%o5
+	movvs	%xcc,	%o4,	$B0
+	ldx	[$pwrtbl+6*32],	%o4
+	movvs	%xcc,	%o5,	$B1
+	ldx	[$pwrtbl+14*32],%o5
+	move	%xcc,	%o4,	$B0
+	ldx	[$pwrtbl+7*32],	%o4
+	move	%xcc,	%o5,	$B1
+	ldx	[$pwrtbl+15*32],%o5
+	movneg	%xcc,	%o4,	$B0
+	add	$pwrtbl,16*32,	$pwrtbl
+	movneg	%xcc,	%o5,	$B1
+___
+}
+sub load_b {
+my ($pwrtbl,$Bi)=@_;
+
+$code.=<<___;
+	ldx	[$pwrtbl+0*32],	$Bi
+	ldx	[$pwrtbl+1*32],	%o4
+	ldx	[$pwrtbl+2*32],	%o5
+	movvs	%icc,	%o4,	$Bi
+	ldx	[$pwrtbl+3*32],	%o4
+	move	%icc,	%o5,	$Bi
+	ldx	[$pwrtbl+4*32],	%o5
+	movneg	%icc,	%o4,	$Bi
+	ldx	[$pwrtbl+5*32],	%o4
+	movcs	%xcc,	%o5,	$Bi
+	ldx	[$pwrtbl+6*32],	%o5
+	movvs	%xcc,	%o4,	$Bi
+	ldx	[$pwrtbl+7*32],	%o4
+	move	%xcc,	%o5,	$Bi
+	add	$pwrtbl,8*32,	$pwrtbl
+	movneg	%xcc,	%o4,	$Bi
+___
+}
+
+########################################################################
+# int bn_pwr5_mont_t4_$NUM(u64 *tp,const u64 *np,const BN_ULONG *n0,
+#			   const u64 *pwrtbl,int pwr,int stride);
+#
+sub generate_bn_pwr5_mont_t4() {
+my $NUM=shift;
+my ($tp,$np,$pwrtbl,$pwr,$sentinel)=map("%g$_",(1..5));
+
+$code.=<<___;
+.globl	bn_pwr5_mont_t4_$NUM
+.align	32
+bn_pwr5_mont_t4_$NUM:
+#ifdef	__arch64__
+	mov	0,$sentinel
+	mov	-128,%g4
+#elif defined(SPARCV9_64BIT_STACK)
+	SPARC_LOAD_ADDRESS_LEAF(OPENSSL_sparcv9cap_P,%g1,%g5)
+	ld	[%g1+0],%g1	! OPENSSL_sparcv9_P[0]
+	mov	-2047,%g4
+	and	%g1,SPARCV9_64BIT_STACK,%g1
+	movrz	%g1,0,%g4
+	mov	-1,$sentinel
+	add	%g4,-128,%g4
+#else
+	mov	-1,$sentinel
+	mov	-128,%g4
+#endif
+	sllx	$sentinel,32,$sentinel
+	save	%sp,%g4,%sp
+#ifndef	__arch64__
+	save	%sp,-128,%sp	! warm it up
+	save	%sp,-128,%sp
+	save	%sp,-128,%sp
+	save	%sp,-128,%sp
+	save	%sp,-128,%sp
+	save	%sp,-128,%sp
+	restore
+	restore
+	restore
+	restore
+	restore
+	restore
+#endif
+	and	%sp,1,%g4
+	or	$sentinel,%fp,%fp
+	or	%g4,$sentinel,$sentinel
+
+	! copy arguments to global registers
+	mov	%i0,$tp
+	mov	%i1,$np
+	ld	[%i2+0],%f1	! load *n0
+	ld	[%i2+4],%f0
+	mov	%i3,$pwrtbl
+	srl	%i4,%g0,%i4	! pack last arguments
+	sllx	%i5,32,$pwr
+	or	%i4,$pwr,$pwr
+	fsrc2	%f0,%f60
+___
+
+# load tp[$NUM] ########################################################
+$code.=<<___;
+	save	%sp,-128,%sp;		or	$sentinel,%fp,%fp
+___
+for($i=0; $i<14 && $i<$NUM; $i++) {
+$code.=<<___;
+	ldx	[$tp+$i*8],@A[$i]
+___
+}
+for(; $i<$NUM; $i++) {
+$code.=<<___;
+	ldd	[$tp+$i*8],@A[$i]
+___
+}
+# load np[$NUM] ########################################################
+$code.=<<___;
+	save	%sp,-128,%sp;		or	$sentinel,%fp,%fp
+___
+for($i=0; $i<14 && $i<$NUM; $i++) {
+$code.=<<___;
+	ldx	[$np+$i*8],@N[$i]
+___
+}
+$code.=<<___;
+	save	%sp,-128,%sp;		or	$sentinel,%fp,%fp
+___
+for(; $i<28 && $i<$NUM; $i++) {
+$code.=<<___;
+	ldx	[$np+$i*8],@N[$i]
+___
+}
+$code.=<<___;
+	save	%sp,-128,%sp;		or	$sentinel,%fp,%fp
+___
+for(; $i<$NUM; $i++) {
+$code.=<<___;
+	ldx	[$np+$i*8],@N[$i]
+___
+}
+# load pwrtbl[pwr] ########################################################
+$code.=<<___;
+	save	%sp,-128,%sp;		or	$sentinel,%fp,%fp
+
+	srlx	$pwr,	32,	%o4		! unpack $pwr
+	srl	$pwr,	%g0,	%o5
+	sub	%o4,	5,	%o4
+	mov	$pwrtbl,	%o7
+	sllx	%o4,	32,	$pwr		! re-pack $pwr
+	or	%o5,	$pwr,	$pwr
+	srl	%o5,	%o4,	%o5
+___
+	&load_ccr("%o7","%o5","%o4");
+$code.=<<___;
+	b	.Lstride_$NUM
+	nop
+.align	16
+.Lstride_$NUM:
+___
+for($i=0; $i<14 && $i<$NUM; $i+=2) {
+	&load_b_pair("%o7",@B[$i],@B[$i+1]);
+}
+$code.=<<___;
+	save	%sp,-128,%sp;		or	$sentinel,%fp,%fp
+___
+for(; $i<$NUM; $i+=2) {
+	&load_b_pair("%i7",@B[$i],@B[$i+1]);
+}
+$code.=<<___;
+	srax	$pwr,	32,	%o4		! unpack $pwr
+	srl	$pwr,	%g0,	%o5
+	sub	%o4,	5,	%o4
+	mov	$pwrtbl,	%i7
+	sllx	%o4,	32,	$pwr		! re-pack $pwr
+	or	%o5,	$pwr,	$pwr
+	srl	%o5,	%o4,	%o5
+___
+	&load_ccr("%i7","%o5","%o4",1);
+
+# magic ################################################################
+for($i=0; $i<5; $i++) {
+$code.=<<___;
+	.word	0x81b02940+$NUM-1	! montsqr	$NUM-1
+	fbu,pn	%fcc3,.Labort_$NUM
+#ifndef	__arch64__
+	and	%fp,$sentinel,$sentinel
+	brz,pn	$sentinel,.Labort_$NUM
+#endif
+	nop
+___
+}
+$code.=<<___;
+	wr	%o4,	%g0,	%ccr
+	.word	0x81b02920+$NUM-1	! montmul	$NUM-1
+	fbu,pn	%fcc3,.Labort_$NUM
+#ifndef	__arch64__
+	and	%fp,$sentinel,$sentinel
+	brz,pn	$sentinel,.Labort_$NUM
+#endif
+
+	srax	$pwr,	32,	%o4
+#ifdef	__arch64__
+	brgez	%o4,.Lstride_$NUM
+	restore
+	restore
+	restore
+	restore
+	restore
+#else
+	brgez	%o4,.Lstride_$NUM
+	restore;		and	%fp,$sentinel,$sentinel
+	restore;		and	%fp,$sentinel,$sentinel
+	restore;		and	%fp,$sentinel,$sentinel
+	restore;		and	%fp,$sentinel,$sentinel
+	 brz,pn	$sentinel,.Labort1_$NUM
+	restore
+#endif
+___
+
+# save tp[$NUM] ########################################################
+for($i=0; $i<14 && $i<$NUM; $i++) {
+$code.=<<___;
+	movxtod	@A[$i],@R[$i]
+___
+}
+$code.=<<___;
+#ifdef	__arch64__
+	restore
+#else
+	 and	%fp,$sentinel,$sentinel
+	restore
+	 and	$sentinel,1,%o7
+	 and	%fp,$sentinel,$sentinel
+	 srl	%fp,0,%fp		! just in case?
+	 or	%o7,$sentinel,$sentinel
+	brz,a,pn $sentinel,.Ldone_$NUM
+	mov	0,%i0		! return failure
+#endif
+___
+for($i=0; $i<$NUM; $i++) {
+$code.=<<___;
+	std	@R[$i],[$tp+$i*8]
+___
+}
+$code.=<<___;
+	mov	1,%i0		! return success
+.Ldone_$NUM:
+	ret
+	restore
+
+.Labort_$NUM:
+	restore
+	restore
+	restore
+	restore
+	restore
+.Labort1_$NUM:
+	restore
+
+	mov	0,%i0		! return failure
+	ret
+	restore
+.type	bn_pwr5_mont_t4_$NUM, #function
+.size	bn_pwr5_mont_t4_$NUM, .-bn_pwr5_mont_t4_$NUM
+___
+}
+
+for ($i=8;$i<=32;$i+=8) {
+	&generate_bn_pwr5_mont_t4($i);
+}
+
+{
+########################################################################
+# Fall-back subroutines
+#
+# copy of bn_mul_mont_vis3 adjusted for vectors of 64-bit values
+#
+($n0,$m0,$m1,$lo0,$hi0, $lo1,$hi1,$aj,$alo,$nj,$nlo,$tj)=
+	(map("%g$_",(1..5)),map("%o$_",(0..5,7)));
+
+# int bn_mul_mont(
+$rp="%o0";	# u64 *rp,
+$ap="%o1";	# const u64 *ap,
+$bp="%o2";	# const u64 *bp,
+$np="%o3";	# const u64 *np,
+$n0p="%o4";	# const BN_ULONG *n0,
+$num="%o5";	# int num);	# caller ensures that num is >=3
+$code.=<<___;
+.globl	bn_mul_mont_t4
+.align	32
+bn_mul_mont_t4:
+	add	%sp,	STACK_BIAS,	%g4	! real top of stack
+	sll	$num,	3,	$num		! size in bytes
+	add	$num,	63,	%g1
+	andn	%g1,	63,	%g1		! buffer size rounded up to 64 bytes
+	sub	%g4,	%g1,	%g1
+	andn	%g1,	63,	%g1		! align at 64 byte
+	sub	%g1,	STACK_FRAME,	%g1	! new top of stack
+	sub	%g1,	%g4,	%g1
+
+	save	%sp,	%g1,	%sp
+___
+#	+-------------------------------+<-----	%sp
+#	.				.
+#	+-------------------------------+<-----	aligned at 64 bytes
+#	| __int64 tmp[0]		|
+#	+-------------------------------+
+#	.				.
+#	.				.
+#	+-------------------------------+<-----	aligned at 64 bytes
+#	.				.
+($rp,$ap,$bp,$np,$n0p,$num)=map("%i$_",(0..5));
+($t0,$t1,$t2,$t3,$cnt,$tp,$bufsz)=map("%l$_",(0..7));
+($ovf,$i)=($t0,$t1);
+$code.=<<___;
+	ld	[$n0p+0],	$t0	! pull n0[0..1] value
+	ld	[$n0p+4],	$t1
+	add	%sp, STACK_BIAS+STACK_FRAME, $tp
+	ldx	[$bp+0],	$m0	! m0=bp[0]
+	sllx	$t1,	32,	$n0
+	add	$bp,	8,	$bp
+	or	$t0,	$n0,	$n0
+
+	ldx	[$ap+0],	$aj	! ap[0]
+
+	mulx	$aj,	$m0,	$lo0	! ap[0]*bp[0]
+	umulxhi	$aj,	$m0,	$hi0
+
+	ldx	[$ap+8],	$aj	! ap[1]
+	add	$ap,	16,	$ap
+	ldx	[$np+0],	$nj	! np[0]
+
+	mulx	$lo0,	$n0,	$m1	! "tp[0]"*n0
+
+	mulx	$aj,	$m0,	$alo	! ap[1]*bp[0]
+	umulxhi	$aj,	$m0,	$aj	! ahi=aj
+
+	mulx	$nj,	$m1,	$lo1	! np[0]*m1
+	umulxhi	$nj,	$m1,	$hi1
+
+	ldx	[$np+8],	$nj	! np[1]
+
+	addcc	$lo0,	$lo1,	$lo1
+	add	$np,	16,	$np
+	addxc	%g0,	$hi1,	$hi1
+
+	mulx	$nj,	$m1,	$nlo	! np[1]*m1
+	umulxhi	$nj,	$m1,	$nj	! nhi=nj
+
+	ba	.L1st
+	sub	$num,	24,	$cnt	! cnt=num-3
+
+.align	16
+.L1st:
+	addcc	$alo,	$hi0,	$lo0
+	addxc	$aj,	%g0,	$hi0
+
+	ldx	[$ap+0],	$aj	! ap[j]
+	addcc	$nlo,	$hi1,	$lo1
+	add	$ap,	8,	$ap
+	addxc	$nj,	%g0,	$hi1	! nhi=nj
+
+	ldx	[$np+0],	$nj	! np[j]
+	mulx	$aj,	$m0,	$alo	! ap[j]*bp[0]
+	add	$np,	8,	$np
+	umulxhi	$aj,	$m0,	$aj	! ahi=aj
+
+	mulx	$nj,	$m1,	$nlo	! np[j]*m1
+	addcc	$lo0,	$lo1,	$lo1	! np[j]*m1+ap[j]*bp[0]
+	umulxhi	$nj,	$m1,	$nj	! nhi=nj
+	addxc	%g0,	$hi1,	$hi1
+	stxa	$lo1,	[$tp]0xe2	! tp[j-1]
+	add	$tp,	8,	$tp	! tp++
+
+	brnz,pt	$cnt,	.L1st
+	sub	$cnt,	8,	$cnt	! j--
+!.L1st
+	addcc	$alo,	$hi0,	$lo0
+	addxc	$aj,	%g0,	$hi0	! ahi=aj
+
+	addcc	$nlo,	$hi1,	$lo1
+	addxc	$nj,	%g0,	$hi1
+	addcc	$lo0,	$lo1,	$lo1	! np[j]*m1+ap[j]*bp[0]
+	addxc	%g0,	$hi1,	$hi1
+	stxa	$lo1,	[$tp]0xe2	! tp[j-1]
+	add	$tp,	8,	$tp
+
+	addcc	$hi0,	$hi1,	$hi1
+	addxc	%g0,	%g0,	$ovf	! upmost overflow bit
+	stxa	$hi1,	[$tp]0xe2
+	add	$tp,	8,	$tp
+
+	ba	.Louter
+	sub	$num,	16,	$i	! i=num-2
+
+.align	16
+.Louter:
+	ldx	[$bp+0],	$m0	! m0=bp[i]
+	add	$bp,	8,	$bp
+
+	sub	$ap,	$num,	$ap	! rewind
+	sub	$np,	$num,	$np
+	sub	$tp,	$num,	$tp
+
+	ldx	[$ap+0],	$aj	! ap[0]
+	ldx	[$np+0],	$nj	! np[0]
+
+	mulx	$aj,	$m0,	$lo0	! ap[0]*bp[i]
+	ldx	[$tp],		$tj	! tp[0]
+	umulxhi	$aj,	$m0,	$hi0
+	ldx	[$ap+8],	$aj	! ap[1]
+	addcc	$lo0,	$tj,	$lo0	! ap[0]*bp[i]+tp[0]
+	mulx	$aj,	$m0,	$alo	! ap[1]*bp[i]
+	addxc	%g0,	$hi0,	$hi0
+	mulx	$lo0,	$n0,	$m1	! tp[0]*n0
+	umulxhi	$aj,	$m0,	$aj	! ahi=aj
+	mulx	$nj,	$m1,	$lo1	! np[0]*m1
+	add	$ap,	16,	$ap
+	umulxhi	$nj,	$m1,	$hi1
+	ldx	[$np+8],	$nj	! np[1]
+	add	$np,	16,	$np
+	addcc	$lo1,	$lo0,	$lo1
+	mulx	$nj,	$m1,	$nlo	! np[1]*m1
+	addxc	%g0,	$hi1,	$hi1
+	umulxhi	$nj,	$m1,	$nj	! nhi=nj
+
+	ba	.Linner
+	sub	$num,	24,	$cnt	! cnt=num-3
+.align	16
+.Linner:
+	addcc	$alo,	$hi0,	$lo0
+	ldx	[$tp+8],	$tj	! tp[j]
+	addxc	$aj,	%g0,	$hi0	! ahi=aj
+	ldx	[$ap+0],	$aj	! ap[j]
+	add	$ap,	8,	$ap
+	addcc	$nlo,	$hi1,	$lo1
+	mulx	$aj,	$m0,	$alo	! ap[j]*bp[i]
+	addxc	$nj,	%g0,	$hi1	! nhi=nj
+	ldx	[$np+0],	$nj	! np[j]
+	add	$np,	8,	$np
+	umulxhi	$aj,	$m0,	$aj	! ahi=aj
+	addcc	$lo0,	$tj,	$lo0	! ap[j]*bp[i]+tp[j]
+	mulx	$nj,	$m1,	$nlo	! np[j]*m1
+	addxc	%g0,	$hi0,	$hi0
+	umulxhi	$nj,	$m1,	$nj	! nhi=nj
+	addcc	$lo1,	$lo0,	$lo1	! np[j]*m1+ap[j]*bp[i]+tp[j]
+	addxc	%g0,	$hi1,	$hi1
+	stx	$lo1,	[$tp]		! tp[j-1]
+	add	$tp,	8,	$tp
+	brnz,pt	$cnt,	.Linner
+	sub	$cnt,	8,	$cnt
+!.Linner
+	ldx	[$tp+8],	$tj	! tp[j]
+	addcc	$alo,	$hi0,	$lo0
+	addxc	$aj,	%g0,	$hi0	! ahi=aj
+	addcc	$lo0,	$tj,	$lo0	! ap[j]*bp[i]+tp[j]
+	addxc	%g0,	$hi0,	$hi0
+
+	addcc	$nlo,	$hi1,	$lo1
+	addxc	$nj,	%g0,	$hi1	! nhi=nj
+	addcc	$lo1,	$lo0,	$lo1	! np[j]*m1+ap[j]*bp[i]+tp[j]
+	addxc	%g0,	$hi1,	$hi1
+	stx	$lo1,	[$tp]		! tp[j-1]
+
+	subcc	%g0,	$ovf,	%g0	! move upmost overflow to CCR.xcc
+	addxccc	$hi1,	$hi0,	$hi1
+	addxc	%g0,	%g0,	$ovf
+	stx	$hi1,	[$tp+8]
+	add	$tp,	16,	$tp
+
+	brnz,pt	$i,	.Louter
+	sub	$i,	8,	$i
+
+	sub	$ap,	$num,	$ap	! rewind
+	sub	$np,	$num,	$np
+	sub	$tp,	$num,	$tp
+	ba	.Lsub
+	subcc	$num,	8,	$cnt	! cnt=num-1 and clear CCR.xcc
+
+.align	16
+.Lsub:
+	ldx	[$tp],		$tj
+	add	$tp,	8,	$tp
+	ldx	[$np+0],	$nj
+	add	$np,	8,	$np
+	subccc	$tj,	$nj,	$t2	! tp[j]-np[j]
+	srlx	$tj,	32,	$tj
+	srlx	$nj,	32,	$nj
+	subccc	$tj,	$nj,	$t3
+	add	$rp,	8,	$rp
+	st	$t2,	[$rp-4]		! reverse order
+	st	$t3,	[$rp-8]
+	brnz,pt	$cnt,	.Lsub
+	sub	$cnt,	8,	$cnt
+
+	sub	$np,	$num,	$np	! rewind
+	sub	$tp,	$num,	$tp
+	sub	$rp,	$num,	$rp
+
+	subc	$ovf,	%g0,	$ovf	! handle upmost overflow bit
+	and	$tp,	$ovf,	$ap
+	andn	$rp,	$ovf,	$np
+	or	$np,	$ap,	$ap	! ap=borrow?tp:rp
+	ba	.Lcopy
+	sub	$num,	8,	$cnt
+
+.align	16
+.Lcopy:					! copy or in-place refresh
+	ldx	[$ap+0],	$t2
+	add	$ap,	8,	$ap
+	stx	%g0,	[$tp]		! zap
+	add	$tp,	8,	$tp
+	stx	$t2,	[$rp+0]
+	add	$rp,	8,	$rp
+	brnz	$cnt,	.Lcopy
+	sub	$cnt,	8,	$cnt
+
+	mov	1,	%o0
+	ret
+	restore
+.type	bn_mul_mont_t4, #function
+.size	bn_mul_mont_t4, .-bn_mul_mont_t4
+___
+
+# int bn_mul_mont_gather5(
+$rp="%o0";	# u64 *rp,
+$ap="%o1";	# const u64 *ap,
+$bp="%o2";	# const u64 *pwrtbl,
+$np="%o3";	# const u64 *np,
+$n0p="%o4";	# const BN_ULONG *n0,
+$num="%o5";	# int num,	# caller ensures that num is >=3
+		# int power);
+$code.=<<___;
+.globl	bn_mul_mont_gather5_t4
+.align	32
+bn_mul_mont_gather5_t4:
+	add	%sp,	STACK_BIAS,	%g4	! real top of stack
+	sll	$num,	3,	$num		! size in bytes
+	add	$num,	63,	%g1
+	andn	%g1,	63,	%g1		! buffer size rounded up to 64 bytes
+	sub	%g4,	%g1,	%g1
+	andn	%g1,	63,	%g1		! align at 64 byte
+	sub	%g1,	STACK_FRAME,	%g1	! new top of stack
+	sub	%g1,	%g4,	%g1
+	LDPTR	[%sp+STACK_7thARG],	%g4	! load power, 7th argument
+
+	save	%sp,	%g1,	%sp
+___
+#	+-------------------------------+<-----	%sp
+#	.				.
+#	+-------------------------------+<-----	aligned at 64 bytes
+#	| __int64 tmp[0]		|
+#	+-------------------------------+
+#	.				.
+#	.				.
+#	+-------------------------------+<-----	aligned at 64 bytes
+#	.				.
+($rp,$ap,$bp,$np,$n0p,$num)=map("%i$_",(0..5));
+($t0,$t1,$t2,$t3,$cnt,$tp,$bufsz,$ccr)=map("%l$_",(0..7));
+($ovf,$i)=($t0,$t1);
+	&load_ccr($bp,"%g4",$ccr);
+	&load_b($bp,$m0,"%o7");		# m0=bp[0]
+
+$code.=<<___;
+	ld	[$n0p+0],	$t0	! pull n0[0..1] value
+	ld	[$n0p+4],	$t1
+	add	%sp, STACK_BIAS+STACK_FRAME, $tp
+	sllx	$t1,	32,	$n0
+	or	$t0,	$n0,	$n0
+
+	ldx	[$ap+0],	$aj	! ap[0]
+
+	mulx	$aj,	$m0,	$lo0	! ap[0]*bp[0]
+	umulxhi	$aj,	$m0,	$hi0
+
+	ldx	[$ap+8],	$aj	! ap[1]
+	add	$ap,	16,	$ap
+	ldx	[$np+0],	$nj	! np[0]
+
+	mulx	$lo0,	$n0,	$m1	! "tp[0]"*n0
+
+	mulx	$aj,	$m0,	$alo	! ap[1]*bp[0]
+	umulxhi	$aj,	$m0,	$aj	! ahi=aj
+
+	mulx	$nj,	$m1,	$lo1	! np[0]*m1
+	umulxhi	$nj,	$m1,	$hi1
+
+	ldx	[$np+8],	$nj	! np[1]
+
+	addcc	$lo0,	$lo1,	$lo1
+	add	$np,	16,	$np
+	addxc	%g0,	$hi1,	$hi1
+
+	mulx	$nj,	$m1,	$nlo	! np[1]*m1
+	umulxhi	$nj,	$m1,	$nj	! nhi=nj
+
+	ba	.L1st_g5
+	sub	$num,	24,	$cnt	! cnt=num-3
+
+.align	16
+.L1st_g5:
+	addcc	$alo,	$hi0,	$lo0
+	addxc	$aj,	%g0,	$hi0
+
+	ldx	[$ap+0],	$aj	! ap[j]
+	addcc	$nlo,	$hi1,	$lo1
+	add	$ap,	8,	$ap
+	addxc	$nj,	%g0,	$hi1	! nhi=nj
+
+	ldx	[$np+0],	$nj	! np[j]
+	mulx	$aj,	$m0,	$alo	! ap[j]*bp[0]
+	add	$np,	8,	$np
+	umulxhi	$aj,	$m0,	$aj	! ahi=aj
+
+	mulx	$nj,	$m1,	$nlo	! np[j]*m1
+	addcc	$lo0,	$lo1,	$lo1	! np[j]*m1+ap[j]*bp[0]
+	umulxhi	$nj,	$m1,	$nj	! nhi=nj
+	addxc	%g0,	$hi1,	$hi1
+	stxa	$lo1,	[$tp]0xe2	! tp[j-1]
+	add	$tp,	8,	$tp	! tp++
+
+	brnz,pt	$cnt,	.L1st_g5
+	sub	$cnt,	8,	$cnt	! j--
+!.L1st_g5
+	addcc	$alo,	$hi0,	$lo0
+	addxc	$aj,	%g0,	$hi0	! ahi=aj
+
+	addcc	$nlo,	$hi1,	$lo1
+	addxc	$nj,	%g0,	$hi1
+	addcc	$lo0,	$lo1,	$lo1	! np[j]*m1+ap[j]*bp[0]
+	addxc	%g0,	$hi1,	$hi1
+	stxa	$lo1,	[$tp]0xe2	! tp[j-1]
+	add	$tp,	8,	$tp
+
+	addcc	$hi0,	$hi1,	$hi1
+	addxc	%g0,	%g0,	$ovf	! upmost overflow bit
+	stxa	$hi1,	[$tp]0xe2
+	add	$tp,	8,	$tp
+
+	ba	.Louter_g5
+	sub	$num,	16,	$i	! i=num-2
+
+.align	16
+.Louter_g5:
+	wr	$ccr,	%g0,	%ccr
+___
+	&load_b($bp,$m0);		# m0=bp[i]
+$code.=<<___;
+	sub	$ap,	$num,	$ap	! rewind
+	sub	$np,	$num,	$np
+	sub	$tp,	$num,	$tp
+
+	ldx	[$ap+0],	$aj	! ap[0]
+	ldx	[$np+0],	$nj	! np[0]
+
+	mulx	$aj,	$m0,	$lo0	! ap[0]*bp[i]
+	ldx	[$tp],		$tj	! tp[0]
+	umulxhi	$aj,	$m0,	$hi0
+	ldx	[$ap+8],	$aj	! ap[1]
+	addcc	$lo0,	$tj,	$lo0	! ap[0]*bp[i]+tp[0]
+	mulx	$aj,	$m0,	$alo	! ap[1]*bp[i]
+	addxc	%g0,	$hi0,	$hi0
+	mulx	$lo0,	$n0,	$m1	! tp[0]*n0
+	umulxhi	$aj,	$m0,	$aj	! ahi=aj
+	mulx	$nj,	$m1,	$lo1	! np[0]*m1
+	add	$ap,	16,	$ap
+	umulxhi	$nj,	$m1,	$hi1
+	ldx	[$np+8],	$nj	! np[1]
+	add	$np,	16,	$np
+	addcc	$lo1,	$lo0,	$lo1
+	mulx	$nj,	$m1,	$nlo	! np[1]*m1
+	addxc	%g0,	$hi1,	$hi1
+	umulxhi	$nj,	$m1,	$nj	! nhi=nj
+
+	ba	.Linner_g5
+	sub	$num,	24,	$cnt	! cnt=num-3
+.align	16
+.Linner_g5:
+	addcc	$alo,	$hi0,	$lo0
+	ldx	[$tp+8],	$tj	! tp[j]
+	addxc	$aj,	%g0,	$hi0	! ahi=aj
+	ldx	[$ap+0],	$aj	! ap[j]
+	add	$ap,	8,	$ap
+	addcc	$nlo,	$hi1,	$lo1
+	mulx	$aj,	$m0,	$alo	! ap[j]*bp[i]
+	addxc	$nj,	%g0,	$hi1	! nhi=nj
+	ldx	[$np+0],	$nj	! np[j]
+	add	$np,	8,	$np
+	umulxhi	$aj,	$m0,	$aj	! ahi=aj
+	addcc	$lo0,	$tj,	$lo0	! ap[j]*bp[i]+tp[j]
+	mulx	$nj,	$m1,	$nlo	! np[j]*m1
+	addxc	%g0,	$hi0,	$hi0
+	umulxhi	$nj,	$m1,	$nj	! nhi=nj
+	addcc	$lo1,	$lo0,	$lo1	! np[j]*m1+ap[j]*bp[i]+tp[j]
+	addxc	%g0,	$hi1,	$hi1
+	stx	$lo1,	[$tp]		! tp[j-1]
+	add	$tp,	8,	$tp
+	brnz,pt	$cnt,	.Linner_g5
+	sub	$cnt,	8,	$cnt
+!.Linner_g5
+	ldx	[$tp+8],	$tj	! tp[j]
+	addcc	$alo,	$hi0,	$lo0
+	addxc	$aj,	%g0,	$hi0	! ahi=aj
+	addcc	$lo0,	$tj,	$lo0	! ap[j]*bp[i]+tp[j]
+	addxc	%g0,	$hi0,	$hi0
+
+	addcc	$nlo,	$hi1,	$lo1
+	addxc	$nj,	%g0,	$hi1	! nhi=nj
+	addcc	$lo1,	$lo0,	$lo1	! np[j]*m1+ap[j]*bp[i]+tp[j]
+	addxc	%g0,	$hi1,	$hi1
+	stx	$lo1,	[$tp]		! tp[j-1]
+
+	subcc	%g0,	$ovf,	%g0	! move upmost overflow to CCR.xcc
+	addxccc	$hi1,	$hi0,	$hi1
+	addxc	%g0,	%g0,	$ovf
+	stx	$hi1,	[$tp+8]
+	add	$tp,	16,	$tp
+
+	brnz,pt	$i,	.Louter_g5
+	sub	$i,	8,	$i
+
+	sub	$ap,	$num,	$ap	! rewind
+	sub	$np,	$num,	$np
+	sub	$tp,	$num,	$tp
+	ba	.Lsub_g5
+	subcc	$num,	8,	$cnt	! cnt=num-1 and clear CCR.xcc
+
+.align	16
+.Lsub_g5:
+	ldx	[$tp],		$tj
+	add	$tp,	8,	$tp
+	ldx	[$np+0],	$nj
+	add	$np,	8,	$np
+	subccc	$tj,	$nj,	$t2	! tp[j]-np[j]
+	srlx	$tj,	32,	$tj
+	srlx	$nj,	32,	$nj
+	subccc	$tj,	$nj,	$t3
+	add	$rp,	8,	$rp
+	st	$t2,	[$rp-4]		! reverse order
+	st	$t3,	[$rp-8]
+	brnz,pt	$cnt,	.Lsub_g5
+	sub	$cnt,	8,	$cnt
+
+	sub	$np,	$num,	$np	! rewind
+	sub	$tp,	$num,	$tp
+	sub	$rp,	$num,	$rp
+
+	subc	$ovf,	%g0,	$ovf	! handle upmost overflow bit
+	and	$tp,	$ovf,	$ap
+	andn	$rp,	$ovf,	$np
+	or	$np,	$ap,	$ap	! ap=borrow?tp:rp
+	ba	.Lcopy_g5
+	sub	$num,	8,	$cnt
+
+.align	16
+.Lcopy_g5:				! copy or in-place refresh
+	ldx	[$ap+0],	$t2
+	add	$ap,	8,	$ap
+	stx	%g0,	[$tp]		! zap
+	add	$tp,	8,	$tp
+	stx	$t2,	[$rp+0]
+	add	$rp,	8,	$rp
+	brnz	$cnt,	.Lcopy_g5
+	sub	$cnt,	8,	$cnt
+
+	mov	1,	%o0
+	ret
+	restore
+.type	bn_mul_mont_gather5_t4, #function
+.size	bn_mul_mont_gather5_t4, .-bn_mul_mont_gather5_t4
+___
+}
+
+$code.=<<___;
+.globl	bn_flip_t4
+.align	32
+bn_flip_t4:
+.Loop_flip:
+	ld	[%o1+0],	%o4
+	sub	%o2,	1,	%o2
+	ld	[%o1+4],	%o5
+	add	%o1,	8,	%o1
+	st	%o5,	[%o0+0]
+	st	%o4,	[%o0+4]
+	brnz	%o2,	.Loop_flip
+	add	%o0,	8,	%o0
+	retl
+	nop
+.type	bn_flip_t4, #function
+.size	bn_flip_t4, .-bn_flip_t4
+
+.globl	bn_flip_n_scatter5_t4
+.align	32
+bn_flip_n_scatter5_t4:
+	sll	%o3,	3,	%o3
+	srl	%o1,	1,	%o1
+	add	%o3,	%o2,	%o2	! &pwrtbl[pwr]
+	sub	%o1,	1,	%o1
+.Loop_flip_n_scatter5:
+	ld	[%o0+0],	%o4	! inp[i]
+	ld	[%o0+4],	%o5
+	add	%o0,	8,	%o0
+	sllx	%o5,	32,	%o5
+	or	%o4,	%o5,	%o5
+	stx	%o5,	[%o2]
+	add	%o2,	32*8,	%o2
+	brnz	%o1,	.Loop_flip_n_scatter5
+	sub	%o1,	1,	%o1
+	retl
+	nop
+.type	bn_flip_n_scatter5_t4, #function
+.size	bn_flip_n_scatter5_t4, .-bn_flip_n_scatter5_t4
+
+.globl	bn_gather5_t4
+.align	32
+bn_gather5_t4:
+___
+	&load_ccr("%o2","%o3","%g1");
+$code.=<<___;
+	sub	%o1,	1,	%o1
+.Loop_gather5:
+___
+	&load_b("%o2","%g1");
+$code.=<<___;
+	stx	%g1,	[%o0]
+	add	%o0,	8,	%o0
+	brnz	%o1,	.Loop_gather5
+	sub	%o1,	1,	%o1
+
+	retl
+	nop
+.type	bn_gather5_t4, #function
+.size	bn_gather5_t4, .-bn_gather5_t4
+
+.asciz	"Montgomery Multiplication for SPARC T4, David S. Miller, Andy Polyakov"
+.align	4
+___
+
+&emit_assembler();
+
+close STDOUT;
diff --git a/crypto/bn/asm/sparcv8.S b/crypto/bn/asm/sparcv8.S
new file mode 100644
index 000000000..9c31073b2
--- /dev/null
+++ b/crypto/bn/asm/sparcv8.S
@@ -0,0 +1,1458 @@
+.ident	"sparcv8.s, Version 1.4"
+.ident	"SPARC v8 ISA artwork by Andy Polyakov "
+
+/*
+ * ====================================================================
+ * Copyright 1999-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ * ====================================================================
+ */
+
+/*
+ * This is my modest contributon to OpenSSL project (see
+ * http://www.openssl.org/ for more information about it) and is
+ * a drop-in SuperSPARC ISA replacement for crypto/bn/bn_asm.c
+ * module. For updates see http://fy.chalmers.se/~appro/hpe/.
+ *
+ * See bn_asm.sparc.v8plus.S for more details.
+ */
+
+/*
+ * Revision history.
+ *
+ * 1.1	- new loop unrolling model(*);
+ * 1.2	- made gas friendly;
+ * 1.3	- fixed problem with /usr/ccs/lib/cpp;
+ * 1.4	- some retunes;
+ *
+ * (*)	see bn_asm.sparc.v8plus.S for details
+ */
+
+.section	".text",#alloc,#execinstr
+.file		"bn_asm.sparc.v8.S"
+
+.align	32
+
+.global bn_mul_add_words
+/*
+ * BN_ULONG bn_mul_add_words(rp,ap,num,w)
+ * BN_ULONG *rp,*ap;
+ * int num;
+ * BN_ULONG w;
+ */
+bn_mul_add_words:
+	cmp	%o2,0
+	bg,a	.L_bn_mul_add_words_proceed
+	ld	[%o1],%g2
+	retl
+	clr	%o0
+
+.L_bn_mul_add_words_proceed:
+	andcc	%o2,-4,%g0
+	bz	.L_bn_mul_add_words_tail
+	clr	%o5
+
+.L_bn_mul_add_words_loop:
+	ld	[%o0],%o4
+	ld	[%o1+4],%g3
+	umul	%o3,%g2,%g2
+	rd	%y,%g1
+	addcc	%o4,%o5,%o4
+	addx	%g1,0,%g1
+	addcc	%o4,%g2,%o4
+	st	%o4,[%o0]
+	addx	%g1,0,%o5
+
+	ld	[%o0+4],%o4
+	ld	[%o1+8],%g2
+	umul	%o3,%g3,%g3
+	dec	4,%o2
+	rd	%y,%g1
+	addcc	%o4,%o5,%o4
+	addx	%g1,0,%g1
+	addcc	%o4,%g3,%o4
+	st	%o4,[%o0+4]
+	addx	%g1,0,%o5
+
+	ld	[%o0+8],%o4
+	ld	[%o1+12],%g3
+	umul	%o3,%g2,%g2
+	inc	16,%o1
+	rd	%y,%g1
+	addcc	%o4,%o5,%o4
+	addx	%g1,0,%g1
+	addcc	%o4,%g2,%o4
+	st	%o4,[%o0+8]
+	addx	%g1,0,%o5
+
+	ld	[%o0+12],%o4
+	umul	%o3,%g3,%g3
+	inc	16,%o0
+	rd	%y,%g1
+	addcc	%o4,%o5,%o4
+	addx	%g1,0,%g1
+	addcc	%o4,%g3,%o4
+	st	%o4,[%o0-4]
+	addx	%g1,0,%o5
+	andcc	%o2,-4,%g0
+	bnz,a	.L_bn_mul_add_words_loop
+	ld	[%o1],%g2
+
+	tst	%o2
+	bnz,a	.L_bn_mul_add_words_tail
+	ld	[%o1],%g2
+.L_bn_mul_add_words_return:
+	retl
+	mov	%o5,%o0
+	nop
+
+.L_bn_mul_add_words_tail:
+	ld	[%o0],%o4
+	umul	%o3,%g2,%g2
+	addcc	%o4,%o5,%o4
+	rd	%y,%g1
+	addx	%g1,0,%g1
+	addcc	%o4,%g2,%o4
+	addx	%g1,0,%o5
+	deccc	%o2
+	bz	.L_bn_mul_add_words_return
+	st	%o4,[%o0]
+
+	ld	[%o1+4],%g2
+	ld	[%o0+4],%o4
+	umul	%o3,%g2,%g2
+	rd	%y,%g1
+	addcc	%o4,%o5,%o4
+	addx	%g1,0,%g1
+	addcc	%o4,%g2,%o4
+	addx	%g1,0,%o5
+	deccc	%o2
+	bz	.L_bn_mul_add_words_return
+	st	%o4,[%o0+4]
+
+	ld	[%o1+8],%g2
+	ld	[%o0+8],%o4
+	umul	%o3,%g2,%g2
+	rd	%y,%g1
+	addcc	%o4,%o5,%o4
+	addx	%g1,0,%g1
+	addcc	%o4,%g2,%o4
+	st	%o4,[%o0+8]
+	retl
+	addx	%g1,0,%o0
+
+.type	bn_mul_add_words,#function
+.size	bn_mul_add_words,(.-bn_mul_add_words)
+
+.align	32
+
+.global bn_mul_words
+/*
+ * BN_ULONG bn_mul_words(rp,ap,num,w)
+ * BN_ULONG *rp,*ap;
+ * int num;
+ * BN_ULONG w;
+ */
+bn_mul_words:
+	cmp	%o2,0
+	bg,a	.L_bn_mul_words_proceeed
+	ld	[%o1],%g2
+	retl
+	clr	%o0
+
+.L_bn_mul_words_proceeed:
+	andcc	%o2,-4,%g0
+	bz	.L_bn_mul_words_tail
+	clr	%o5
+
+.L_bn_mul_words_loop:
+	ld	[%o1+4],%g3
+	umul	%o3,%g2,%g2
+	addcc	%g2,%o5,%g2
+	rd	%y,%g1
+	addx	%g1,0,%o5
+	st	%g2,[%o0]
+
+	ld	[%o1+8],%g2
+	umul	%o3,%g3,%g3
+	addcc	%g3,%o5,%g3
+	rd	%y,%g1
+	dec	4,%o2
+	addx	%g1,0,%o5
+	st	%g3,[%o0+4]
+
+	ld	[%o1+12],%g3
+	umul	%o3,%g2,%g2
+	addcc	%g2,%o5,%g2
+	rd	%y,%g1
+	inc	16,%o1
+	st	%g2,[%o0+8]
+	addx	%g1,0,%o5
+
+	umul	%o3,%g3,%g3
+	addcc	%g3,%o5,%g3
+	rd	%y,%g1
+	inc	16,%o0
+	addx	%g1,0,%o5
+	st	%g3,[%o0-4]
+	andcc	%o2,-4,%g0
+	nop
+	bnz,a	.L_bn_mul_words_loop
+	ld	[%o1],%g2
+
+	tst	%o2
+	bnz,a	.L_bn_mul_words_tail
+	ld	[%o1],%g2
+.L_bn_mul_words_return:
+	retl
+	mov	%o5,%o0
+	nop
+
+.L_bn_mul_words_tail:
+	umul	%o3,%g2,%g2
+	addcc	%g2,%o5,%g2
+	rd	%y,%g1
+	addx	%g1,0,%o5
+	deccc	%o2
+	bz	.L_bn_mul_words_return
+	st	%g2,[%o0]
+	nop
+
+	ld	[%o1+4],%g2
+	umul	%o3,%g2,%g2
+	addcc	%g2,%o5,%g2
+	rd	%y,%g1
+	addx	%g1,0,%o5
+	deccc	%o2
+	bz	.L_bn_mul_words_return
+	st	%g2,[%o0+4]
+
+	ld	[%o1+8],%g2
+	umul	%o3,%g2,%g2
+	addcc	%g2,%o5,%g2
+	rd	%y,%g1
+	st	%g2,[%o0+8]
+	retl
+	addx	%g1,0,%o0
+
+.type	bn_mul_words,#function
+.size	bn_mul_words,(.-bn_mul_words)
+
+.align  32
+.global	bn_sqr_words
+/*
+ * void bn_sqr_words(r,a,n)
+ * BN_ULONG *r,*a;
+ * int n;
+ */
+bn_sqr_words:
+	cmp	%o2,0
+	bg,a	.L_bn_sqr_words_proceeed
+	ld	[%o1],%g2
+	retl
+	clr	%o0
+
+.L_bn_sqr_words_proceeed:
+	andcc	%o2,-4,%g0
+	bz	.L_bn_sqr_words_tail
+	clr	%o5
+
+.L_bn_sqr_words_loop:
+	ld	[%o1+4],%g3
+	umul	%g2,%g2,%o4
+	st	%o4,[%o0]
+	rd	%y,%o5
+	st	%o5,[%o0+4]
+
+	ld	[%o1+8],%g2
+	umul	%g3,%g3,%o4
+	dec	4,%o2
+	st	%o4,[%o0+8]
+	rd	%y,%o5
+	st	%o5,[%o0+12]
+	nop
+
+	ld	[%o1+12],%g3
+	umul	%g2,%g2,%o4
+	st	%o4,[%o0+16]
+	rd	%y,%o5
+	inc	16,%o1
+	st	%o5,[%o0+20]
+
+	umul	%g3,%g3,%o4
+	inc	32,%o0
+	st	%o4,[%o0-8]
+	rd	%y,%o5
+	st	%o5,[%o0-4]
+	andcc	%o2,-4,%g2
+	bnz,a	.L_bn_sqr_words_loop
+	ld	[%o1],%g2
+
+	tst	%o2
+	nop
+	bnz,a	.L_bn_sqr_words_tail
+	ld	[%o1],%g2
+.L_bn_sqr_words_return:
+	retl
+	clr	%o0
+
+.L_bn_sqr_words_tail:
+	umul	%g2,%g2,%o4
+	st	%o4,[%o0]
+	deccc	%o2
+	rd	%y,%o5
+	bz	.L_bn_sqr_words_return
+	st	%o5,[%o0+4]
+
+	ld	[%o1+4],%g2
+	umul	%g2,%g2,%o4
+	st	%o4,[%o0+8]
+	deccc	%o2
+	rd	%y,%o5
+	nop
+	bz	.L_bn_sqr_words_return
+	st	%o5,[%o0+12]
+
+	ld	[%o1+8],%g2
+	umul	%g2,%g2,%o4
+	st	%o4,[%o0+16]
+	rd	%y,%o5
+	st	%o5,[%o0+20]
+	retl
+	clr	%o0
+
+.type	bn_sqr_words,#function
+.size	bn_sqr_words,(.-bn_sqr_words)
+
+.align	32
+
+.global bn_div_words
+/*
+ * BN_ULONG bn_div_words(h,l,d)
+ * BN_ULONG h,l,d;
+ */
+bn_div_words:
+	wr	%o0,%y
+	udiv	%o1,%o2,%o0
+	retl
+	nop
+
+.type	bn_div_words,#function
+.size	bn_div_words,(.-bn_div_words)
+
+.align	32
+
+.global bn_add_words
+/*
+ * BN_ULONG bn_add_words(rp,ap,bp,n)
+ * BN_ULONG *rp,*ap,*bp;
+ * int n;
+ */
+bn_add_words:
+	cmp	%o3,0
+	bg,a	.L_bn_add_words_proceed
+	ld	[%o1],%o4
+	retl
+	clr	%o0
+
+.L_bn_add_words_proceed:
+	andcc	%o3,-4,%g0
+	bz	.L_bn_add_words_tail
+	clr	%g1
+	ba	.L_bn_add_words_warn_loop
+	addcc	%g0,0,%g0	! clear carry flag
+
+.L_bn_add_words_loop:
+	ld	[%o1],%o4
+.L_bn_add_words_warn_loop:
+	ld	[%o2],%o5
+	ld	[%o1+4],%g3
+	ld	[%o2+4],%g4
+	dec	4,%o3
+	addxcc	%o5,%o4,%o5
+	st	%o5,[%o0]
+
+	ld	[%o1+8],%o4
+	ld	[%o2+8],%o5
+	inc	16,%o1
+	addxcc	%g3,%g4,%g3
+	st	%g3,[%o0+4]
+	
+	ld	[%o1-4],%g3
+	ld	[%o2+12],%g4
+	inc	16,%o2
+	addxcc	%o5,%o4,%o5
+	st	%o5,[%o0+8]
+
+	inc	16,%o0
+	addxcc	%g3,%g4,%g3
+	st	%g3,[%o0-4]
+	addx	%g0,0,%g1
+	andcc	%o3,-4,%g0
+	bnz,a	.L_bn_add_words_loop
+	addcc	%g1,-1,%g0
+
+	tst	%o3
+	bnz,a	.L_bn_add_words_tail
+	ld	[%o1],%o4
+.L_bn_add_words_return:
+	retl
+	mov	%g1,%o0
+
+.L_bn_add_words_tail:
+	addcc	%g1,-1,%g0
+	ld	[%o2],%o5
+	addxcc	%o5,%o4,%o5
+	addx	%g0,0,%g1
+	deccc	%o3
+	bz	.L_bn_add_words_return
+	st	%o5,[%o0]
+
+	ld	[%o1+4],%o4
+	addcc	%g1,-1,%g0
+	ld	[%o2+4],%o5
+	addxcc	%o5,%o4,%o5
+	addx	%g0,0,%g1
+	deccc	%o3
+	bz	.L_bn_add_words_return
+	st	%o5,[%o0+4]
+
+	ld	[%o1+8],%o4
+	addcc	%g1,-1,%g0
+	ld	[%o2+8],%o5
+	addxcc	%o5,%o4,%o5
+	st	%o5,[%o0+8]
+	retl
+	addx	%g0,0,%o0
+
+.type	bn_add_words,#function
+.size	bn_add_words,(.-bn_add_words)
+
+.align	32
+
+.global bn_sub_words
+/*
+ * BN_ULONG bn_sub_words(rp,ap,bp,n)
+ * BN_ULONG *rp,*ap,*bp;
+ * int n;
+ */
+bn_sub_words:
+	cmp	%o3,0
+	bg,a	.L_bn_sub_words_proceed
+	ld	[%o1],%o4
+	retl
+	clr	%o0
+
+.L_bn_sub_words_proceed:
+	andcc	%o3,-4,%g0
+	bz	.L_bn_sub_words_tail
+	clr	%g1
+	ba	.L_bn_sub_words_warm_loop
+	addcc	%g0,0,%g0	! clear carry flag
+
+.L_bn_sub_words_loop:
+	ld	[%o1],%o4
+.L_bn_sub_words_warm_loop:
+	ld	[%o2],%o5
+	ld	[%o1+4],%g3
+	ld	[%o2+4],%g4
+	dec	4,%o3
+	subxcc	%o4,%o5,%o5
+	st	%o5,[%o0]
+
+	ld	[%o1+8],%o4
+	ld	[%o2+8],%o5
+	inc	16,%o1
+	subxcc	%g3,%g4,%g4
+	st	%g4,[%o0+4]
+	
+	ld	[%o1-4],%g3
+	ld	[%o2+12],%g4
+	inc	16,%o2
+	subxcc	%o4,%o5,%o5
+	st	%o5,[%o0+8]
+
+	inc	16,%o0
+	subxcc	%g3,%g4,%g4
+	st	%g4,[%o0-4]
+	addx	%g0,0,%g1
+	andcc	%o3,-4,%g0
+	bnz,a	.L_bn_sub_words_loop
+	addcc	%g1,-1,%g0
+
+	tst	%o3
+	nop
+	bnz,a	.L_bn_sub_words_tail
+	ld	[%o1],%o4
+.L_bn_sub_words_return:
+	retl
+	mov	%g1,%o0
+
+.L_bn_sub_words_tail:
+	addcc	%g1,-1,%g0
+	ld	[%o2],%o5
+	subxcc	%o4,%o5,%o5
+	addx	%g0,0,%g1
+	deccc	%o3
+	bz	.L_bn_sub_words_return
+	st	%o5,[%o0]
+	nop
+
+	ld	[%o1+4],%o4
+	addcc	%g1,-1,%g0
+	ld	[%o2+4],%o5
+	subxcc	%o4,%o5,%o5
+	addx	%g0,0,%g1
+	deccc	%o3
+	bz	.L_bn_sub_words_return
+	st	%o5,[%o0+4]
+
+	ld	[%o1+8],%o4
+	addcc	%g1,-1,%g0
+	ld	[%o2+8],%o5
+	subxcc	%o4,%o5,%o5
+	st	%o5,[%o0+8]
+	retl
+	addx	%g0,0,%o0
+
+.type	bn_sub_words,#function
+.size	bn_sub_words,(.-bn_sub_words)
+
+#define FRAME_SIZE	-96
+
+/*
+ * Here is register usage map for *all* routines below.
+ */
+#define t_1	%o0
+#define	t_2	%o1
+#define c_1	%o2
+#define c_2	%o3
+#define c_3	%o4
+
+#define ap(I)	[%i1+4*I]
+#define bp(I)	[%i2+4*I]
+#define rp(I)	[%i0+4*I]
+
+#define	a_0	%l0
+#define	a_1	%l1
+#define	a_2	%l2
+#define	a_3	%l3
+#define	a_4	%l4
+#define	a_5	%l5
+#define	a_6	%l6
+#define	a_7	%l7
+
+#define	b_0	%i3
+#define	b_1	%i4
+#define	b_2	%i5
+#define	b_3	%o5
+#define	b_4	%g1
+#define	b_5	%g2
+#define	b_6	%g3
+#define	b_7	%g4
+
+.align	32
+.global bn_mul_comba8
+/*
+ * void bn_mul_comba8(r,a,b)
+ * BN_ULONG *r,*a,*b;
+ */
+bn_mul_comba8:
+	save	%sp,FRAME_SIZE,%sp
+	ld	ap(0),a_0
+	ld	bp(0),b_0
+	umul	a_0,b_0,c_1	!=!mul_add_c(a[0],b[0],c1,c2,c3);
+	ld	bp(1),b_1
+	rd	%y,c_2
+	st	c_1,rp(0)	!r[0]=c1;
+
+	umul	a_0,b_1,t_1	!=!mul_add_c(a[0],b[1],c2,c3,c1);
+	ld	ap(1),a_1
+	addcc	c_2,t_1,c_2
+	rd	%y,t_2
+	addxcc	%g0,t_2,c_3	!=
+	addx	%g0,%g0,c_1
+	ld	ap(2),a_2
+	umul	a_1,b_0,t_1	!mul_add_c(a[1],b[0],c2,c3,c1);
+	addcc	c_2,t_1,c_2	!=
+	rd	%y,t_2
+	addxcc	c_3,t_2,c_3
+	st	c_2,rp(1)	!r[1]=c2;
+	addx	c_1,%g0,c_1	!=
+
+	umul	a_2,b_0,t_1	!mul_add_c(a[2],b[0],c3,c1,c2);
+	addcc	c_3,t_1,c_3
+	rd	%y,t_2
+	addxcc	c_1,t_2,c_1	!=
+	addx	%g0,%g0,c_2
+	ld	bp(2),b_2
+	umul	a_1,b_1,t_1	!mul_add_c(a[1],b[1],c3,c1,c2);
+	addcc	c_3,t_1,c_3	!=
+	rd	%y,t_2
+	addxcc	c_1,t_2,c_1
+	ld	bp(3),b_3
+	addx	c_2,%g0,c_2	!=
+	umul	a_0,b_2,t_1	!mul_add_c(a[0],b[2],c3,c1,c2);
+	addcc	c_3,t_1,c_3
+	rd	%y,t_2
+	addxcc	c_1,t_2,c_1	!=
+	addx	c_2,%g0,c_2
+	st	c_3,rp(2)	!r[2]=c3;
+
+	umul	a_0,b_3,t_1	!mul_add_c(a[0],b[3],c1,c2,c3);
+	addcc	c_1,t_1,c_1	!=
+	rd	%y,t_2
+	addxcc	c_2,t_2,c_2
+	addx	%g0,%g0,c_3
+	umul	a_1,b_2,t_1	!=!mul_add_c(a[1],b[2],c1,c2,c3);
+	addcc	c_1,t_1,c_1
+	rd	%y,t_2
+	addxcc	c_2,t_2,c_2
+	addx	c_3,%g0,c_3	!=
+	ld	ap(3),a_3
+	umul	a_2,b_1,t_1	!mul_add_c(a[2],b[1],c1,c2,c3);
+	addcc	c_1,t_1,c_1
+	rd	%y,t_2		!=
+	addxcc	c_2,t_2,c_2
+	addx	c_3,%g0,c_3
+	ld	ap(4),a_4
+	umul	a_3,b_0,t_1	!mul_add_c(a[3],b[0],c1,c2,c3);!=
+	addcc	c_1,t_1,c_1
+	rd	%y,t_2
+	addxcc	c_2,t_2,c_2
+	addx	c_3,%g0,c_3	!=
+	st	c_1,rp(3)	!r[3]=c1;
+
+	umul	a_4,b_0,t_1	!mul_add_c(a[4],b[0],c2,c3,c1);
+	addcc	c_2,t_1,c_2
+	rd	%y,t_2		!=
+	addxcc	c_3,t_2,c_3
+	addx	%g0,%g0,c_1
+	umul	a_3,b_1,t_1	!mul_add_c(a[3],b[1],c2,c3,c1);
+	addcc	c_2,t_1,c_2	!=
+	rd	%y,t_2
+	addxcc	c_3,t_2,c_3
+	addx	c_1,%g0,c_1
+	umul	a_2,b_2,t_1	!=!mul_add_c(a[2],b[2],c2,c3,c1);
+	addcc	c_2,t_1,c_2
+	rd	%y,t_2
+	addxcc	c_3,t_2,c_3
+	addx	c_1,%g0,c_1	!=
+	ld	bp(4),b_4
+	umul	a_1,b_3,t_1	!mul_add_c(a[1],b[3],c2,c3,c1);
+	addcc	c_2,t_1,c_2
+	rd	%y,t_2		!=
+	addxcc	c_3,t_2,c_3
+	addx	c_1,%g0,c_1
+	ld	bp(5),b_5
+	umul	a_0,b_4,t_1	!=!mul_add_c(a[0],b[4],c2,c3,c1);
+	addcc	c_2,t_1,c_2
+	rd	%y,t_2
+	addxcc	c_3,t_2,c_3
+	addx	c_1,%g0,c_1	!=
+	st	c_2,rp(4)	!r[4]=c2;
+
+	umul	a_0,b_5,t_1	!mul_add_c(a[0],b[5],c3,c1,c2);
+	addcc	c_3,t_1,c_3
+	rd	%y,t_2		!=
+	addxcc	c_1,t_2,c_1
+	addx	%g0,%g0,c_2
+	umul	a_1,b_4,t_1	!mul_add_c(a[1],b[4],c3,c1,c2);
+	addcc	c_3,t_1,c_3	!=
+	rd	%y,t_2
+	addxcc	c_1,t_2,c_1
+	addx	c_2,%g0,c_2
+	umul	a_2,b_3,t_1	!=!mul_add_c(a[2],b[3],c3,c1,c2);
+	addcc	c_3,t_1,c_3
+	rd	%y,t_2
+	addxcc	c_1,t_2,c_1
+	addx	c_2,%g0,c_2	!=
+	umul	a_3,b_2,t_1	!mul_add_c(a[3],b[2],c3,c1,c2);
+	addcc	c_3,t_1,c_3
+	rd	%y,t_2
+	addxcc	c_1,t_2,c_1	!=
+	addx	c_2,%g0,c_2
+	ld	ap(5),a_5
+	umul	a_4,b_1,t_1	!mul_add_c(a[4],b[1],c3,c1,c2);
+	addcc	c_3,t_1,c_3	!=
+	rd	%y,t_2
+	addxcc	c_1,t_2,c_1
+	ld	ap(6),a_6
+	addx	c_2,%g0,c_2	!=
+	umul	a_5,b_0,t_1	!mul_add_c(a[5],b[0],c3,c1,c2);
+	addcc	c_3,t_1,c_3
+	rd	%y,t_2
+	addxcc	c_1,t_2,c_1	!=
+	addx	c_2,%g0,c_2
+	st	c_3,rp(5)	!r[5]=c3;
+
+	umul	a_6,b_0,t_1	!mul_add_c(a[6],b[0],c1,c2,c3);
+	addcc	c_1,t_1,c_1	!=
+	rd	%y,t_2
+	addxcc	c_2,t_2,c_2
+	addx	%g0,%g0,c_3
+	umul	a_5,b_1,t_1	!=!mul_add_c(a[5],b[1],c1,c2,c3);
+	addcc	c_1,t_1,c_1
+	rd	%y,t_2
+	addxcc	c_2,t_2,c_2
+	addx	c_3,%g0,c_3	!=
+	umul	a_4,b_2,t_1	!mul_add_c(a[4],b[2],c1,c2,c3);
+	addcc	c_1,t_1,c_1
+	rd	%y,t_2
+	addxcc	c_2,t_2,c_2	!=
+	addx	c_3,%g0,c_3
+	umul	a_3,b_3,t_1	!mul_add_c(a[3],b[3],c1,c2,c3);
+	addcc	c_1,t_1,c_1
+	rd	%y,t_2		!=
+	addxcc	c_2,t_2,c_2
+	addx	c_3,%g0,c_3
+	umul	a_2,b_4,t_1	!mul_add_c(a[2],b[4],c1,c2,c3);
+	addcc	c_1,t_1,c_1	!=
+	rd	%y,t_2
+	addxcc	c_2,t_2,c_2
+	ld	bp(6),b_6
+	addx	c_3,%g0,c_3	!=
+	umul	a_1,b_5,t_1	!mul_add_c(a[1],b[5],c1,c2,c3);
+	addcc	c_1,t_1,c_1
+	rd	%y,t_2
+	addxcc	c_2,t_2,c_2	!=
+	addx	c_3,%g0,c_3
+	ld	bp(7),b_7
+	umul	a_0,b_6,t_1	!mul_add_c(a[0],b[6],c1,c2,c3);
+	addcc	c_1,t_1,c_1	!=
+	rd	%y,t_2
+	addxcc	c_2,t_2,c_2
+	st	c_1,rp(6)	!r[6]=c1;
+	addx	c_3,%g0,c_3	!=
+
+	umul	a_0,b_7,t_1	!mul_add_c(a[0],b[7],c2,c3,c1);
+	addcc	c_2,t_1,c_2
+	rd	%y,t_2
+	addxcc	c_3,t_2,c_3	!=
+	addx	%g0,%g0,c_1
+	umul	a_1,b_6,t_1	!mul_add_c(a[1],b[6],c2,c3,c1);
+	addcc	c_2,t_1,c_2
+	rd	%y,t_2		!=
+	addxcc	c_3,t_2,c_3
+	addx	c_1,%g0,c_1
+	umul	a_2,b_5,t_1	!mul_add_c(a[2],b[5],c2,c3,c1);
+	addcc	c_2,t_1,c_2	!=
+	rd	%y,t_2
+	addxcc	c_3,t_2,c_3
+	addx	c_1,%g0,c_1
+	umul	a_3,b_4,t_1	!=!mul_add_c(a[3],b[4],c2,c3,c1);
+	addcc	c_2,t_1,c_2
+	rd	%y,t_2
+	addxcc	c_3,t_2,c_3
+	addx	c_1,%g0,c_1	!=
+	umul	a_4,b_3,t_1	!mul_add_c(a[4],b[3],c2,c3,c1);
+	addcc	c_2,t_1,c_2
+	rd	%y,t_2
+	addxcc	c_3,t_2,c_3	!=
+	addx	c_1,%g0,c_1
+	umul	a_5,b_2,t_1	!mul_add_c(a[5],b[2],c2,c3,c1);
+	addcc	c_2,t_1,c_2
+	rd	%y,t_2		!=
+	addxcc	c_3,t_2,c_3
+	addx	c_1,%g0,c_1
+	ld	ap(7),a_7
+	umul	a_6,b_1,t_1	!=!mul_add_c(a[6],b[1],c2,c3,c1);
+	addcc	c_2,t_1,c_2
+	rd	%y,t_2
+	addxcc	c_3,t_2,c_3
+	addx	c_1,%g0,c_1	!=
+	umul	a_7,b_0,t_1	!mul_add_c(a[7],b[0],c2,c3,c1);
+	addcc	c_2,t_1,c_2
+	rd	%y,t_2
+	addxcc	c_3,t_2,c_3	!=
+	addx	c_1,%g0,c_1
+	st	c_2,rp(7)	!r[7]=c2;
+
+	umul	a_7,b_1,t_1	!mul_add_c(a[7],b[1],c3,c1,c2);
+	addcc	c_3,t_1,c_3	!=
+	rd	%y,t_2
+	addxcc	c_1,t_2,c_1
+	addx	%g0,%g0,c_2
+	umul	a_6,b_2,t_1	!=!mul_add_c(a[6],b[2],c3,c1,c2);
+	addcc	c_3,t_1,c_3
+	rd	%y,t_2
+	addxcc	c_1,t_2,c_1
+	addx	c_2,%g0,c_2	!=
+	umul	a_5,b_3,t_1	!mul_add_c(a[5],b[3],c3,c1,c2);
+	addcc	c_3,t_1,c_3
+	rd	%y,t_2
+	addxcc	c_1,t_2,c_1	!=
+	addx	c_2,%g0,c_2
+	umul	a_4,b_4,t_1	!mul_add_c(a[4],b[4],c3,c1,c2);
+	addcc	c_3,t_1,c_3
+	rd	%y,t_2		!=
+	addxcc	c_1,t_2,c_1
+	addx	c_2,%g0,c_2
+	umul	a_3,b_5,t_1	!mul_add_c(a[3],b[5],c3,c1,c2);
+	addcc	c_3,t_1,c_3	!=
+	rd	%y,t_2
+	addxcc	c_1,t_2,c_1
+	addx	c_2,%g0,c_2
+	umul	a_2,b_6,t_1	!=!mul_add_c(a[2],b[6],c3,c1,c2);
+	addcc	c_3,t_1,c_3
+	rd	%y,t_2
+	addxcc	c_1,t_2,c_1
+	addx	c_2,%g0,c_2	!=
+	umul	a_1,b_7,t_1	!mul_add_c(a[1],b[7],c3,c1,c2);
+	addcc	c_3,t_1,c_3
+	rd	%y,t_2
+	addxcc	c_1,t_2,c_1	!
+	addx	c_2,%g0,c_2
+	st	c_3,rp(8)	!r[8]=c3;
+
+	umul	a_2,b_7,t_1	!mul_add_c(a[2],b[7],c1,c2,c3);
+	addcc	c_1,t_1,c_1	!=
+	rd	%y,t_2
+	addxcc	c_2,t_2,c_2
+	addx	%g0,%g0,c_3
+	umul	a_3,b_6,t_1	!=!mul_add_c(a[3],b[6],c1,c2,c3);
+	addcc	c_1,t_1,c_1
+	rd	%y,t_2
+	addxcc	c_2,t_2,c_2
+	addx	c_3,%g0,c_3	!=
+	umul	a_4,b_5,t_1	!mul_add_c(a[4],b[5],c1,c2,c3);
+	addcc	c_1,t_1,c_1
+	rd	%y,t_2
+	addxcc	c_2,t_2,c_2	!=
+	addx	c_3,%g0,c_3
+	umul	a_5,b_4,t_1	!mul_add_c(a[5],b[4],c1,c2,c3);
+	addcc	c_1,t_1,c_1
+	rd	%y,t_2		!=
+	addxcc	c_2,t_2,c_2
+	addx	c_3,%g0,c_3
+	umul	a_6,b_3,t_1	!mul_add_c(a[6],b[3],c1,c2,c3);
+	addcc	c_1,t_1,c_1	!=
+	rd	%y,t_2
+	addxcc	c_2,t_2,c_2
+	addx	c_3,%g0,c_3
+	umul	a_7,b_2,t_1	!=!mul_add_c(a[7],b[2],c1,c2,c3);
+	addcc	c_1,t_1,c_1
+	rd	%y,t_2
+	addxcc	c_2,t_2,c_2
+	addx	c_3,%g0,c_3	!=
+	st	c_1,rp(9)	!r[9]=c1;
+
+	umul	a_7,b_3,t_1	!mul_add_c(a[7],b[3],c2,c3,c1);
+	addcc	c_2,t_1,c_2
+	rd	%y,t_2		!=
+	addxcc	c_3,t_2,c_3
+	addx	%g0,%g0,c_1
+	umul	a_6,b_4,t_1	!mul_add_c(a[6],b[4],c2,c3,c1);
+	addcc	c_2,t_1,c_2	!=
+	rd	%y,t_2
+	addxcc	c_3,t_2,c_3
+	addx	c_1,%g0,c_1
+	umul	a_5,b_5,t_1	!=!mul_add_c(a[5],b[5],c2,c3,c1);
+	addcc	c_2,t_1,c_2
+	rd	%y,t_2
+	addxcc	c_3,t_2,c_3
+	addx	c_1,%g0,c_1	!=
+	umul	a_4,b_6,t_1	!mul_add_c(a[4],b[6],c2,c3,c1);
+	addcc	c_2,t_1,c_2
+	rd	%y,t_2
+	addxcc	c_3,t_2,c_3	!=
+	addx	c_1,%g0,c_1
+	umul	a_3,b_7,t_1	!mul_add_c(a[3],b[7],c2,c3,c1);
+	addcc	c_2,t_1,c_2
+	rd	%y,t_2		!=
+	addxcc	c_3,t_2,c_3
+	addx	c_1,%g0,c_1
+	st	c_2,rp(10)	!r[10]=c2;
+
+	umul	a_4,b_7,t_1	!=!mul_add_c(a[4],b[7],c3,c1,c2);
+	addcc	c_3,t_1,c_3
+	rd	%y,t_2
+	addxcc	c_1,t_2,c_1
+	addx	%g0,%g0,c_2	!=
+	umul	a_5,b_6,t_1	!mul_add_c(a[5],b[6],c3,c1,c2);
+	addcc	c_3,t_1,c_3
+	rd	%y,t_2
+	addxcc	c_1,t_2,c_1	!=
+	addx	c_2,%g0,c_2
+	umul	a_6,b_5,t_1	!mul_add_c(a[6],b[5],c3,c1,c2);
+	addcc	c_3,t_1,c_3
+	rd	%y,t_2		!=
+	addxcc	c_1,t_2,c_1
+	addx	c_2,%g0,c_2
+	umul	a_7,b_4,t_1	!mul_add_c(a[7],b[4],c3,c1,c2);
+	addcc	c_3,t_1,c_3	!=
+	rd	%y,t_2
+	addxcc	c_1,t_2,c_1
+	st	c_3,rp(11)	!r[11]=c3;
+	addx	c_2,%g0,c_2	!=
+
+	umul	a_7,b_5,t_1	!mul_add_c(a[7],b[5],c1,c2,c3);
+	addcc	c_1,t_1,c_1
+	rd	%y,t_2
+	addxcc	c_2,t_2,c_2	!=
+	addx	%g0,%g0,c_3
+	umul	a_6,b_6,t_1	!mul_add_c(a[6],b[6],c1,c2,c3);
+	addcc	c_1,t_1,c_1
+	rd	%y,t_2		!=
+	addxcc	c_2,t_2,c_2
+	addx	c_3,%g0,c_3
+	umul	a_5,b_7,t_1	!mul_add_c(a[5],b[7],c1,c2,c3);
+	addcc	c_1,t_1,c_1	!=
+	rd	%y,t_2
+	addxcc	c_2,t_2,c_2
+	st	c_1,rp(12)	!r[12]=c1;
+	addx	c_3,%g0,c_3	!=
+
+	umul	a_6,b_7,t_1	!mul_add_c(a[6],b[7],c2,c3,c1);
+	addcc	c_2,t_1,c_2
+	rd	%y,t_2
+	addxcc	c_3,t_2,c_3	!=
+	addx	%g0,%g0,c_1
+	umul	a_7,b_6,t_1	!mul_add_c(a[7],b[6],c2,c3,c1);
+	addcc	c_2,t_1,c_2
+	rd	%y,t_2		!=
+	addxcc	c_3,t_2,c_3
+	addx	c_1,%g0,c_1
+	st	c_2,rp(13)	!r[13]=c2;
+
+	umul	a_7,b_7,t_1	!=!mul_add_c(a[7],b[7],c3,c1,c2);
+	addcc	c_3,t_1,c_3
+	rd	%y,t_2
+	addxcc	c_1,t_2,c_1
+	nop			!=
+	st	c_3,rp(14)	!r[14]=c3;
+	st	c_1,rp(15)	!r[15]=c1;
+
+	ret
+	restore	%g0,%g0,%o0
+
+.type	bn_mul_comba8,#function
+.size	bn_mul_comba8,(.-bn_mul_comba8)
+
+.align	32
+
+.global bn_mul_comba4
+/*
+ * void bn_mul_comba4(r,a,b)
+ * BN_ULONG *r,*a,*b;
+ */
+bn_mul_comba4:
+	save	%sp,FRAME_SIZE,%sp
+	ld	ap(0),a_0
+	ld	bp(0),b_0
+	umul	a_0,b_0,c_1	!=!mul_add_c(a[0],b[0],c1,c2,c3);
+	ld	bp(1),b_1
+	rd	%y,c_2
+	st	c_1,rp(0)	!r[0]=c1;
+
+	umul	a_0,b_1,t_1	!=!mul_add_c(a[0],b[1],c2,c3,c1);
+	ld	ap(1),a_1
+	addcc	c_2,t_1,c_2
+	rd	%y,t_2		!=
+	addxcc	%g0,t_2,c_3
+	addx	%g0,%g0,c_1
+	ld	ap(2),a_2
+	umul	a_1,b_0,t_1	!=!mul_add_c(a[1],b[0],c2,c3,c1);
+	addcc	c_2,t_1,c_2
+	rd	%y,t_2
+	addxcc	c_3,t_2,c_3
+	addx	c_1,%g0,c_1	!=
+	st	c_2,rp(1)	!r[1]=c2;
+
+	umul	a_2,b_0,t_1	!mul_add_c(a[2],b[0],c3,c1,c2);
+	addcc	c_3,t_1,c_3
+	rd	%y,t_2		!=
+	addxcc	c_1,t_2,c_1
+	addx	%g0,%g0,c_2
+	ld	bp(2),b_2
+	umul	a_1,b_1,t_1	!=!mul_add_c(a[1],b[1],c3,c1,c2);
+	addcc	c_3,t_1,c_3
+	rd	%y,t_2
+	addxcc	c_1,t_2,c_1
+	addx	c_2,%g0,c_2	!=
+	ld	bp(3),b_3
+	umul	a_0,b_2,t_1	!mul_add_c(a[0],b[2],c3,c1,c2);
+	addcc	c_3,t_1,c_3
+	rd	%y,t_2		!=
+	addxcc	c_1,t_2,c_1
+	addx	c_2,%g0,c_2
+	st	c_3,rp(2)	!r[2]=c3;
+
+	umul	a_0,b_3,t_1	!=!mul_add_c(a[0],b[3],c1,c2,c3);
+	addcc	c_1,t_1,c_1
+	rd	%y,t_2
+	addxcc	c_2,t_2,c_2
+	addx	%g0,%g0,c_3	!=
+	umul	a_1,b_2,t_1	!mul_add_c(a[1],b[2],c1,c2,c3);
+	addcc	c_1,t_1,c_1
+	rd	%y,t_2
+	addxcc	c_2,t_2,c_2	!=
+	addx	c_3,%g0,c_3
+	ld	ap(3),a_3
+	umul	a_2,b_1,t_1	!mul_add_c(a[2],b[1],c1,c2,c3);
+	addcc	c_1,t_1,c_1	!=
+	rd	%y,t_2
+	addxcc	c_2,t_2,c_2
+	addx	c_3,%g0,c_3
+	umul	a_3,b_0,t_1	!=!mul_add_c(a[3],b[0],c1,c2,c3);
+	addcc	c_1,t_1,c_1
+	rd	%y,t_2
+	addxcc	c_2,t_2,c_2
+	addx	c_3,%g0,c_3	!=
+	st	c_1,rp(3)	!r[3]=c1;
+
+	umul	a_3,b_1,t_1	!mul_add_c(a[3],b[1],c2,c3,c1);
+	addcc	c_2,t_1,c_2
+	rd	%y,t_2		!=
+	addxcc	c_3,t_2,c_3
+	addx	%g0,%g0,c_1
+	umul	a_2,b_2,t_1	!mul_add_c(a[2],b[2],c2,c3,c1);
+	addcc	c_2,t_1,c_2	!=
+	rd	%y,t_2
+	addxcc	c_3,t_2,c_3
+	addx	c_1,%g0,c_1
+	umul	a_1,b_3,t_1	!=!mul_add_c(a[1],b[3],c2,c3,c1);
+	addcc	c_2,t_1,c_2
+	rd	%y,t_2
+	addxcc	c_3,t_2,c_3
+	addx	c_1,%g0,c_1	!=
+	st	c_2,rp(4)	!r[4]=c2;
+
+	umul	a_2,b_3,t_1	!mul_add_c(a[2],b[3],c3,c1,c2);
+	addcc	c_3,t_1,c_3
+	rd	%y,t_2		!=
+	addxcc	c_1,t_2,c_1
+	addx	%g0,%g0,c_2
+	umul	a_3,b_2,t_1	!mul_add_c(a[3],b[2],c3,c1,c2);
+	addcc	c_3,t_1,c_3	!=
+	rd	%y,t_2
+	addxcc	c_1,t_2,c_1
+	st	c_3,rp(5)	!r[5]=c3;
+	addx	c_2,%g0,c_2	!=
+
+	umul	a_3,b_3,t_1	!mul_add_c(a[3],b[3],c1,c2,c3);
+	addcc	c_1,t_1,c_1
+	rd	%y,t_2
+	addxcc	c_2,t_2,c_2	!=
+	st	c_1,rp(6)	!r[6]=c1;
+	st	c_2,rp(7)	!r[7]=c2;
+	
+	ret
+	restore	%g0,%g0,%o0
+
+.type	bn_mul_comba4,#function
+.size	bn_mul_comba4,(.-bn_mul_comba4)
+
+.align	32
+
+.global bn_sqr_comba8
+bn_sqr_comba8:
+	save	%sp,FRAME_SIZE,%sp
+	ld	ap(0),a_0
+	ld	ap(1),a_1
+	umul	a_0,a_0,c_1	!=!sqr_add_c(a,0,c1,c2,c3);
+	rd	%y,c_2
+	st	c_1,rp(0)	!r[0]=c1;
+
+	ld	ap(2),a_2
+	umul	a_0,a_1,t_1	!=!sqr_add_c2(a,1,0,c2,c3,c1);
+	addcc	c_2,t_1,c_2
+	rd	%y,t_2
+	addxcc	%g0,t_2,c_3
+	addx	%g0,%g0,c_1	!=
+	addcc	c_2,t_1,c_2
+	addxcc	c_3,t_2,c_3
+	st	c_2,rp(1)	!r[1]=c2;
+	addx	c_1,%g0,c_1	!=
+
+	umul	a_2,a_0,t_1	!sqr_add_c2(a,2,0,c3,c1,c2);
+	addcc	c_3,t_1,c_3
+	rd	%y,t_2
+	addxcc	c_1,t_2,c_1	!=
+	addx	%g0,%g0,c_2
+	addcc	c_3,t_1,c_3
+	addxcc	c_1,t_2,c_1
+	addx	c_2,%g0,c_2	!=
+	ld	ap(3),a_3
+	umul	a_1,a_1,t_1	!sqr_add_c(a,1,c3,c1,c2);
+	addcc	c_3,t_1,c_3
+	rd	%y,t_2		!=
+	addxcc	c_1,t_2,c_1
+	addx	c_2,%g0,c_2
+	st	c_3,rp(2)	!r[2]=c3;
+
+	umul	a_0,a_3,t_1	!=!sqr_add_c2(a,3,0,c1,c2,c3);
+	addcc	c_1,t_1,c_1
+	rd	%y,t_2
+	addxcc	c_2,t_2,c_2
+	addx	%g0,%g0,c_3	!=
+	addcc	c_1,t_1,c_1
+	addxcc	c_2,t_2,c_2
+	ld	ap(4),a_4
+	addx	c_3,%g0,c_3	!=
+	umul	a_1,a_2,t_1	!sqr_add_c2(a,2,1,c1,c2,c3);
+	addcc	c_1,t_1,c_1
+	rd	%y,t_2
+	addxcc	c_2,t_2,c_2	!=
+	addx	c_3,%g0,c_3
+	addcc	c_1,t_1,c_1
+	addxcc	c_2,t_2,c_2
+	addx	c_3,%g0,c_3	!=
+	st	c_1,rp(3)	!r[3]=c1;
+
+	umul	a_4,a_0,t_1	!sqr_add_c2(a,4,0,c2,c3,c1);
+	addcc	c_2,t_1,c_2
+	rd	%y,t_2		!=
+	addxcc	c_3,t_2,c_3
+	addx	%g0,%g0,c_1
+	addcc	c_2,t_1,c_2
+	addxcc	c_3,t_2,c_3	!=
+	addx	c_1,%g0,c_1
+	umul	a_3,a_1,t_1	!sqr_add_c2(a,3,1,c2,c3,c1);
+	addcc	c_2,t_1,c_2
+	rd	%y,t_2		!=
+	addxcc	c_3,t_2,c_3
+	addx	c_1,%g0,c_1
+	addcc	c_2,t_1,c_2
+	addxcc	c_3,t_2,c_3	!=
+	addx	c_1,%g0,c_1
+	ld	ap(5),a_5
+	umul	a_2,a_2,t_1	!sqr_add_c(a,2,c2,c3,c1);
+	addcc	c_2,t_1,c_2	!=
+	rd	%y,t_2
+	addxcc	c_3,t_2,c_3
+	st	c_2,rp(4)	!r[4]=c2;
+	addx	c_1,%g0,c_1	!=
+
+	umul	a_0,a_5,t_1	!sqr_add_c2(a,5,0,c3,c1,c2);
+	addcc	c_3,t_1,c_3
+	rd	%y,t_2
+	addxcc	c_1,t_2,c_1	!=
+	addx	%g0,%g0,c_2
+	addcc	c_3,t_1,c_3
+	addxcc	c_1,t_2,c_1
+	addx	c_2,%g0,c_2	!=
+	umul	a_1,a_4,t_1	!sqr_add_c2(a,4,1,c3,c1,c2);
+	addcc	c_3,t_1,c_3
+	rd	%y,t_2
+	addxcc	c_1,t_2,c_1	!=
+	addx	c_2,%g0,c_2
+	addcc	c_3,t_1,c_3
+	addxcc	c_1,t_2,c_1
+	addx	c_2,%g0,c_2	!=
+	ld	ap(6),a_6
+	umul	a_2,a_3,t_1	!sqr_add_c2(a,3,2,c3,c1,c2);
+	addcc	c_3,t_1,c_3
+	rd	%y,t_2		!=
+	addxcc	c_1,t_2,c_1
+	addx	c_2,%g0,c_2
+	addcc	c_3,t_1,c_3
+	addxcc	c_1,t_2,c_1	!=
+	addx	c_2,%g0,c_2
+	st	c_3,rp(5)	!r[5]=c3;
+
+	umul	a_6,a_0,t_1	!sqr_add_c2(a,6,0,c1,c2,c3);
+	addcc	c_1,t_1,c_1	!=
+	rd	%y,t_2
+	addxcc	c_2,t_2,c_2
+	addx	%g0,%g0,c_3
+	addcc	c_1,t_1,c_1	!=
+	addxcc	c_2,t_2,c_2
+	addx	c_3,%g0,c_3
+	umul	a_5,a_1,t_1	!sqr_add_c2(a,5,1,c1,c2,c3);
+	addcc	c_1,t_1,c_1	!=
+	rd	%y,t_2
+	addxcc	c_2,t_2,c_2
+	addx	c_3,%g0,c_3
+	addcc	c_1,t_1,c_1	!=
+	addxcc	c_2,t_2,c_2
+	addx	c_3,%g0,c_3
+	umul	a_4,a_2,t_1	!sqr_add_c2(a,4,2,c1,c2,c3);
+	addcc	c_1,t_1,c_1	!=
+	rd	%y,t_2
+	addxcc	c_2,t_2,c_2
+	addx	c_3,%g0,c_3
+	addcc	c_1,t_1,c_1	!=
+	addxcc	c_2,t_2,c_2
+	addx	c_3,%g0,c_3
+	ld	ap(7),a_7
+	umul	a_3,a_3,t_1	!=!sqr_add_c(a,3,c1,c2,c3);
+	addcc	c_1,t_1,c_1
+	rd	%y,t_2
+	addxcc	c_2,t_2,c_2
+	addx	c_3,%g0,c_3	!=
+	st	c_1,rp(6)	!r[6]=c1;
+
+	umul	a_0,a_7,t_1	!sqr_add_c2(a,7,0,c2,c3,c1);
+	addcc	c_2,t_1,c_2
+	rd	%y,t_2		!=
+	addxcc	c_3,t_2,c_3
+	addx	%g0,%g0,c_1
+	addcc	c_2,t_1,c_2
+	addxcc	c_3,t_2,c_3	!=
+	addx	c_1,%g0,c_1
+	umul	a_1,a_6,t_1	!sqr_add_c2(a,6,1,c2,c3,c1);
+	addcc	c_2,t_1,c_2
+	rd	%y,t_2		!=
+	addxcc	c_3,t_2,c_3
+	addx	c_1,%g0,c_1
+	addcc	c_2,t_1,c_2
+	addxcc	c_3,t_2,c_3	!=
+	addx	c_1,%g0,c_1
+	umul	a_2,a_5,t_1	!sqr_add_c2(a,5,2,c2,c3,c1);
+	addcc	c_2,t_1,c_2
+	rd	%y,t_2		!=
+	addxcc	c_3,t_2,c_3
+	addx	c_1,%g0,c_1
+	addcc	c_2,t_1,c_2
+	addxcc	c_3,t_2,c_3	!=
+	addx	c_1,%g0,c_1
+	umul	a_3,a_4,t_1	!sqr_add_c2(a,4,3,c2,c3,c1);
+	addcc	c_2,t_1,c_2
+	rd	%y,t_2		!=
+	addxcc	c_3,t_2,c_3
+	addx	c_1,%g0,c_1
+	addcc	c_2,t_1,c_2
+	addxcc	c_3,t_2,c_3	!=
+	addx	c_1,%g0,c_1
+	st	c_2,rp(7)	!r[7]=c2;
+
+	umul	a_7,a_1,t_1	!sqr_add_c2(a,7,1,c3,c1,c2);
+	addcc	c_3,t_1,c_3	!=
+	rd	%y,t_2
+	addxcc	c_1,t_2,c_1
+	addx	%g0,%g0,c_2
+	addcc	c_3,t_1,c_3	!=
+	addxcc	c_1,t_2,c_1
+	addx	c_2,%g0,c_2
+	umul	a_6,a_2,t_1	!sqr_add_c2(a,6,2,c3,c1,c2);
+	addcc	c_3,t_1,c_3	!=
+	rd	%y,t_2
+	addxcc	c_1,t_2,c_1
+	addx	c_2,%g0,c_2
+	addcc	c_3,t_1,c_3	!=
+	addxcc	c_1,t_2,c_1
+	addx	c_2,%g0,c_2
+	umul	a_5,a_3,t_1	!sqr_add_c2(a,5,3,c3,c1,c2);
+	addcc	c_3,t_1,c_3	!=
+	rd	%y,t_2
+	addxcc	c_1,t_2,c_1
+	addx	c_2,%g0,c_2
+	addcc	c_3,t_1,c_3	!=
+	addxcc	c_1,t_2,c_1
+	addx	c_2,%g0,c_2
+	umul	a_4,a_4,t_1	!sqr_add_c(a,4,c3,c1,c2);
+	addcc	c_3,t_1,c_3	!=
+	rd	%y,t_2
+	addxcc	c_1,t_2,c_1
+	st	c_3,rp(8)	!r[8]=c3;
+	addx	c_2,%g0,c_2	!=
+
+	umul	a_2,a_7,t_1	!sqr_add_c2(a,7,2,c1,c2,c3);
+	addcc	c_1,t_1,c_1
+	rd	%y,t_2
+	addxcc	c_2,t_2,c_2	!=
+	addx	%g0,%g0,c_3
+	addcc	c_1,t_1,c_1
+	addxcc	c_2,t_2,c_2
+	addx	c_3,%g0,c_3	!=
+	umul	a_3,a_6,t_1	!sqr_add_c2(a,6,3,c1,c2,c3);
+	addcc	c_1,t_1,c_1
+	rd	%y,t_2
+	addxcc	c_2,t_2,c_2	!=
+	addx	c_3,%g0,c_3
+	addcc	c_1,t_1,c_1
+	addxcc	c_2,t_2,c_2
+	addx	c_3,%g0,c_3	!=
+	umul	a_4,a_5,t_1	!sqr_add_c2(a,5,4,c1,c2,c3);
+	addcc	c_1,t_1,c_1
+	rd	%y,t_2
+	addxcc	c_2,t_2,c_2	!=
+	addx	c_3,%g0,c_3
+	addcc	c_1,t_1,c_1
+	addxcc	c_2,t_2,c_2
+	addx	c_3,%g0,c_3	!=
+	st	c_1,rp(9)	!r[9]=c1;
+
+	umul	a_7,a_3,t_1	!sqr_add_c2(a,7,3,c2,c3,c1);
+	addcc	c_2,t_1,c_2
+	rd	%y,t_2		!=
+	addxcc	c_3,t_2,c_3
+	addx	%g0,%g0,c_1
+	addcc	c_2,t_1,c_2
+	addxcc	c_3,t_2,c_3	!=
+	addx	c_1,%g0,c_1
+	umul	a_6,a_4,t_1	!sqr_add_c2(a,6,4,c2,c3,c1);
+	addcc	c_2,t_1,c_2
+	rd	%y,t_2		!=
+	addxcc	c_3,t_2,c_3
+	addx	c_1,%g0,c_1
+	addcc	c_2,t_1,c_2
+	addxcc	c_3,t_2,c_3	!=
+	addx	c_1,%g0,c_1
+	umul	a_5,a_5,t_1	!sqr_add_c(a,5,c2,c3,c1);
+	addcc	c_2,t_1,c_2
+	rd	%y,t_2		!=
+	addxcc	c_3,t_2,c_3
+	addx	c_1,%g0,c_1
+	st	c_2,rp(10)	!r[10]=c2;
+
+	umul	a_4,a_7,t_1	!=!sqr_add_c2(a,7,4,c3,c1,c2);
+	addcc	c_3,t_1,c_3
+	rd	%y,t_2
+	addxcc	c_1,t_2,c_1
+	addx	%g0,%g0,c_2	!=
+	addcc	c_3,t_1,c_3
+	addxcc	c_1,t_2,c_1
+	addx	c_2,%g0,c_2
+	umul	a_5,a_6,t_1	!=!sqr_add_c2(a,6,5,c3,c1,c2);
+	addcc	c_3,t_1,c_3
+	rd	%y,t_2
+	addxcc	c_1,t_2,c_1
+	addx	c_2,%g0,c_2	!=
+	addcc	c_3,t_1,c_3
+	addxcc	c_1,t_2,c_1
+	st	c_3,rp(11)	!r[11]=c3;
+	addx	c_2,%g0,c_2	!=
+
+	umul	a_7,a_5,t_1	!sqr_add_c2(a,7,5,c1,c2,c3);
+	addcc	c_1,t_1,c_1
+	rd	%y,t_2
+	addxcc	c_2,t_2,c_2	!=
+	addx	%g0,%g0,c_3
+	addcc	c_1,t_1,c_1
+	addxcc	c_2,t_2,c_2
+	addx	c_3,%g0,c_3	!=
+	umul	a_6,a_6,t_1	!sqr_add_c(a,6,c1,c2,c3);
+	addcc	c_1,t_1,c_1
+	rd	%y,t_2
+	addxcc	c_2,t_2,c_2	!=
+	addx	c_3,%g0,c_3
+	st	c_1,rp(12)	!r[12]=c1;
+
+	umul	a_6,a_7,t_1	!sqr_add_c2(a,7,6,c2,c3,c1);
+	addcc	c_2,t_1,c_2	!=
+	rd	%y,t_2
+	addxcc	c_3,t_2,c_3
+	addx	%g0,%g0,c_1
+	addcc	c_2,t_1,c_2	!=
+	addxcc	c_3,t_2,c_3
+	st	c_2,rp(13)	!r[13]=c2;
+	addx	c_1,%g0,c_1	!=
+
+	umul	a_7,a_7,t_1	!sqr_add_c(a,7,c3,c1,c2);
+	addcc	c_3,t_1,c_3
+	rd	%y,t_2
+	addxcc	c_1,t_2,c_1	!=
+	st	c_3,rp(14)	!r[14]=c3;
+	st	c_1,rp(15)	!r[15]=c1;
+
+	ret
+	restore	%g0,%g0,%o0
+
+.type	bn_sqr_comba8,#function
+.size	bn_sqr_comba8,(.-bn_sqr_comba8)
+
+.align	32
+
+.global bn_sqr_comba4
+/*
+ * void bn_sqr_comba4(r,a)
+ * BN_ULONG *r,*a;
+ */
+bn_sqr_comba4:
+	save	%sp,FRAME_SIZE,%sp
+	ld	ap(0),a_0
+	umul	a_0,a_0,c_1	!sqr_add_c(a,0,c1,c2,c3);
+	ld	ap(1),a_1	!=
+	rd	%y,c_2
+	st	c_1,rp(0)	!r[0]=c1;
+
+	ld	ap(2),a_2
+	umul	a_0,a_1,t_1	!=!sqr_add_c2(a,1,0,c2,c3,c1);
+	addcc	c_2,t_1,c_2
+	rd	%y,t_2
+	addxcc	%g0,t_2,c_3
+	addx	%g0,%g0,c_1	!=
+	addcc	c_2,t_1,c_2
+	addxcc	c_3,t_2,c_3
+	addx	c_1,%g0,c_1	!=
+	st	c_2,rp(1)	!r[1]=c2;
+
+	umul	a_2,a_0,t_1	!sqr_add_c2(a,2,0,c3,c1,c2);
+	addcc	c_3,t_1,c_3
+	rd	%y,t_2		!=
+	addxcc	c_1,t_2,c_1
+	addx	%g0,%g0,c_2
+	addcc	c_3,t_1,c_3
+	addxcc	c_1,t_2,c_1	!=
+	addx	c_2,%g0,c_2
+	ld	ap(3),a_3
+	umul	a_1,a_1,t_1	!sqr_add_c(a,1,c3,c1,c2);
+	addcc	c_3,t_1,c_3	!=
+	rd	%y,t_2
+	addxcc	c_1,t_2,c_1
+	st	c_3,rp(2)	!r[2]=c3;
+	addx	c_2,%g0,c_2	!=
+
+	umul	a_0,a_3,t_1	!sqr_add_c2(a,3,0,c1,c2,c3);
+	addcc	c_1,t_1,c_1
+	rd	%y,t_2
+	addxcc	c_2,t_2,c_2	!=
+	addx	%g0,%g0,c_3
+	addcc	c_1,t_1,c_1
+	addxcc	c_2,t_2,c_2
+	addx	c_3,%g0,c_3	!=
+	umul	a_1,a_2,t_1	!sqr_add_c2(a,2,1,c1,c2,c3);
+	addcc	c_1,t_1,c_1
+	rd	%y,t_2
+	addxcc	c_2,t_2,c_2	!=
+	addx	c_3,%g0,c_3
+	addcc	c_1,t_1,c_1
+	addxcc	c_2,t_2,c_2
+	addx	c_3,%g0,c_3	!=
+	st	c_1,rp(3)	!r[3]=c1;
+
+	umul	a_3,a_1,t_1	!sqr_add_c2(a,3,1,c2,c3,c1);
+	addcc	c_2,t_1,c_2
+	rd	%y,t_2		!=
+	addxcc	c_3,t_2,c_3
+	addx	%g0,%g0,c_1
+	addcc	c_2,t_1,c_2
+	addxcc	c_3,t_2,c_3	!=
+	addx	c_1,%g0,c_1
+	umul	a_2,a_2,t_1	!sqr_add_c(a,2,c2,c3,c1);
+	addcc	c_2,t_1,c_2
+	rd	%y,t_2		!=
+	addxcc	c_3,t_2,c_3
+	addx	c_1,%g0,c_1
+	st	c_2,rp(4)	!r[4]=c2;
+
+	umul	a_2,a_3,t_1	!=!sqr_add_c2(a,3,2,c3,c1,c2);
+	addcc	c_3,t_1,c_3
+	rd	%y,t_2
+	addxcc	c_1,t_2,c_1
+	addx	%g0,%g0,c_2	!=
+	addcc	c_3,t_1,c_3
+	addxcc	c_1,t_2,c_1
+	st	c_3,rp(5)	!r[5]=c3;
+	addx	c_2,%g0,c_2	!=
+
+	umul	a_3,a_3,t_1	!sqr_add_c(a,3,c1,c2,c3);
+	addcc	c_1,t_1,c_1
+	rd	%y,t_2
+	addxcc	c_2,t_2,c_2	!=
+	st	c_1,rp(6)	!r[6]=c1;
+	st	c_2,rp(7)	!r[7]=c2;
+	
+	ret
+	restore	%g0,%g0,%o0
+
+.type	bn_sqr_comba4,#function
+.size	bn_sqr_comba4,(.-bn_sqr_comba4)
+
+.align	32
diff --git a/crypto/bn/asm/sparcv8plus.S b/crypto/bn/asm/sparcv8plus.S
new file mode 100644
index 000000000..714a13667
--- /dev/null
+++ b/crypto/bn/asm/sparcv8plus.S
@@ -0,0 +1,1562 @@
+.ident	"sparcv8plus.s, Version 1.4"
+.ident	"SPARC v9 ISA artwork by Andy Polyakov "
+
+/*
+ * ====================================================================
+ * Copyright 1999-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ * ====================================================================
+ */
+
+/*
+ * This is my modest contributon to OpenSSL project (see
+ * http://www.openssl.org/ for more information about it) and is
+ * a drop-in UltraSPARC ISA replacement for crypto/bn/bn_asm.c
+ * module. For updates see http://fy.chalmers.se/~appro/hpe/.
+ *
+ * Questions-n-answers.
+ *
+ * Q. How to compile?
+ * A. With SC4.x/SC5.x:
+ *
+ *	cc -xarch=v8plus -c bn_asm.sparc.v8plus.S -o bn_asm.o
+ *
+ *    and with gcc:
+ *
+ *	gcc -mcpu=ultrasparc -c bn_asm.sparc.v8plus.S -o bn_asm.o
+ *
+ *    or if above fails (it does if you have gas installed):
+ *
+ *	gcc -E bn_asm.sparc.v8plus.S | as -xarch=v8plus /dev/fd/0 -o bn_asm.o
+ *
+ *    Quick-n-dirty way to fuse the module into the library.
+ *    Provided that the library is already configured and built
+ *    (in 0.9.2 case with no-asm option):
+ *
+ *	# cd crypto/bn
+ *	# cp /some/place/bn_asm.sparc.v8plus.S .
+ *	# cc -xarch=v8plus -c bn_asm.sparc.v8plus.S -o bn_asm.o
+ *	# make
+ *	# cd ../..
+ *	# make; make test
+ *
+ *    Quick-n-dirty way to get rid of it:
+ *
+ *	# cd crypto/bn
+ *	# touch bn_asm.c
+ *	# make
+ *	# cd ../..
+ *	# make; make test
+ *
+ * Q. V8plus architecture? What kind of beast is that?
+ * A. Well, it's rather a programming model than an architecture...
+ *    It's actually v9-compliant, i.e. *any* UltraSPARC, CPU under
+ *    special conditions, namely when kernel doesn't preserve upper
+ *    32 bits of otherwise 64-bit registers during a context switch.
+ *
+ * Q. Why just UltraSPARC? What about SuperSPARC?
+ * A. Original release did target UltraSPARC only. Now SuperSPARC
+ *    version is provided along. Both version share bn_*comba[48]
+ *    implementations (see comment later in code for explanation).
+ *    But what's so special about this UltraSPARC implementation?
+ *    Why didn't I let compiler do the job? Trouble is that most of
+ *    available compilers (well, SC5.0 is the only exception) don't
+ *    attempt to take advantage of UltraSPARC's 64-bitness under
+ *    32-bit kernels even though it's perfectly possible (see next
+ *    question).
+ *
+ * Q. 64-bit registers under 32-bit kernels? Didn't you just say it
+ *    doesn't work?
+ * A. You can't address *all* registers as 64-bit wide:-( The catch is
+ *    that you actually may rely upon %o0-%o5 and %g1-%g4 being fully
+ *    preserved if you're in a leaf function, i.e. such never calling
+ *    any other functions. All functions in this module are leaf and
+ *    10 registers is a handful. And as a matter of fact none-"comba"
+ *    routines don't require even that much and I could even afford to
+ *    not allocate own stack frame for 'em:-)
+ *
+ * Q. What about 64-bit kernels?
+ * A. What about 'em? Just kidding:-) Pure 64-bit version is currently
+ *    under evaluation and development...
+ *
+ * Q. What about shared libraries?
+ * A. What about 'em? Kidding again:-) Code does *not* contain any
+ *    code position dependencies and it's safe to include it into
+ *    shared library as is.
+ *
+ * Q. How much faster does it go?
+ * A. Do you have a good benchmark? In either case below is what I
+ *    experience with crypto/bn/expspeed.c test program:
+ *
+ *	v8plus module on U10/300MHz against bn_asm.c compiled with:
+ *
+ *	cc-5.0 -xarch=v8plus -xO5 -xdepend	+7-12%
+ *	cc-4.2 -xarch=v8plus -xO5 -xdepend	+25-35%
+ *	egcs-1.1.2 -mcpu=ultrasparc -O3		+35-45%
+ *
+ *	v8 module on SS10/60MHz against bn_asm.c compiled with:
+ *
+ *	cc-5.0 -xarch=v8 -xO5 -xdepend		+7-10%
+ *	cc-4.2 -xarch=v8 -xO5 -xdepend		+10%
+ *	egcs-1.1.2 -mv8 -O3			+35-45%
+ *
+ *    As you can see it's damn hard to beat the new Sun C compiler
+ *    and it's in first place GNU C users who will appreciate this
+ *    assembler implementation:-)	
+ */
+
+/*
+ * Revision history.
+ *
+ * 1.0	- initial release;
+ * 1.1	- new loop unrolling model(*);
+ *	- some more fine tuning;
+ * 1.2	- made gas friendly;
+ *	- updates to documentation concerning v9;
+ *	- new performance comparison matrix;
+ * 1.3	- fixed problem with /usr/ccs/lib/cpp;
+ * 1.4	- native V9 bn_*_comba[48] implementation (15% more efficient)
+ *	  resulting in slight overall performance kick;
+ *	- some retunes;
+ *	- support for GNU as added;
+ *
+ * (*)	Originally unrolled loop looked like this:
+ *	    for (;;) {
+ *		op(p+0); if (--n==0) break;
+ *		op(p+1); if (--n==0) break;
+ *		op(p+2); if (--n==0) break;
+ *		op(p+3); if (--n==0) break;
+ *		p+=4;
+ *	    }
+ *	I unroll according to following:
+ *	    while (n&~3) {
+ *		op(p+0); op(p+1); op(p+2); op(p+3);
+ *		p+=4; n=-4;
+ *	    }
+ *	    if (n) {
+ *		op(p+0); if (--n==0) return;
+ *		op(p+2); if (--n==0) return;
+ *		op(p+3); return;
+ *	    }
+ */
+
+#ifdef OPENSSL_FIPSCANISTER
+#include 
+#endif
+
+#if defined(__SUNPRO_C) && defined(__sparcv9)
+  /* They've said -xarch=v9 at command line */
+  .register	%g2,#scratch
+  .register	%g3,#scratch
+# define	FRAME_SIZE	-192
+#elif defined(__GNUC__) && defined(__arch64__)
+  /* They've said -m64 at command line */
+  .register	%g2,#scratch
+  .register	%g3,#scratch
+# define	FRAME_SIZE	-192
+#else 
+# define	FRAME_SIZE	-96
+#endif 
+/*
+ * GNU assembler can't stand stuw:-(
+ */
+#define stuw st
+
+.section	".text",#alloc,#execinstr
+.file		"bn_asm.sparc.v8plus.S"
+
+.align	32
+
+.global bn_mul_add_words
+/*
+ * BN_ULONG bn_mul_add_words(rp,ap,num,w)
+ * BN_ULONG *rp,*ap;
+ * int num;
+ * BN_ULONG w;
+ */
+bn_mul_add_words:
+	sra	%o2,%g0,%o2	! signx %o2
+	brgz,a	%o2,.L_bn_mul_add_words_proceed
+	lduw	[%o1],%g2
+	retl
+	clr	%o0
+	nop
+	nop
+	nop
+
+.L_bn_mul_add_words_proceed:
+	srl	%o3,%g0,%o3	! clruw	%o3
+	andcc	%o2,-4,%g0
+	bz,pn	%icc,.L_bn_mul_add_words_tail
+	clr	%o5
+
+.L_bn_mul_add_words_loop:	! wow! 32 aligned!
+	lduw	[%o0],%g1
+	lduw	[%o1+4],%g3
+	mulx	%o3,%g2,%g2
+	add	%g1,%o5,%o4
+	nop
+	add	%o4,%g2,%o4
+	stuw	%o4,[%o0]
+	srlx	%o4,32,%o5
+
+	lduw	[%o0+4],%g1
+	lduw	[%o1+8],%g2
+	mulx	%o3,%g3,%g3
+	add	%g1,%o5,%o4
+	dec	4,%o2
+	add	%o4,%g3,%o4
+	stuw	%o4,[%o0+4]
+	srlx	%o4,32,%o5
+
+	lduw	[%o0+8],%g1
+	lduw	[%o1+12],%g3
+	mulx	%o3,%g2,%g2
+	add	%g1,%o5,%o4
+	inc	16,%o1
+	add	%o4,%g2,%o4
+	stuw	%o4,[%o0+8]
+	srlx	%o4,32,%o5
+
+	lduw	[%o0+12],%g1
+	mulx	%o3,%g3,%g3
+	add	%g1,%o5,%o4
+	inc	16,%o0
+	add	%o4,%g3,%o4
+	andcc	%o2,-4,%g0
+	stuw	%o4,[%o0-4]
+	srlx	%o4,32,%o5
+	bnz,a,pt	%icc,.L_bn_mul_add_words_loop
+	lduw	[%o1],%g2
+
+	brnz,a,pn	%o2,.L_bn_mul_add_words_tail
+	lduw	[%o1],%g2
+.L_bn_mul_add_words_return:
+	retl
+	mov	%o5,%o0
+
+.L_bn_mul_add_words_tail:
+	lduw	[%o0],%g1
+	mulx	%o3,%g2,%g2
+	add	%g1,%o5,%o4
+	dec	%o2
+	add	%o4,%g2,%o4
+	srlx	%o4,32,%o5
+	brz,pt	%o2,.L_bn_mul_add_words_return
+	stuw	%o4,[%o0]
+
+	lduw	[%o1+4],%g2
+	lduw	[%o0+4],%g1
+	mulx	%o3,%g2,%g2
+	add	%g1,%o5,%o4
+	dec	%o2
+	add	%o4,%g2,%o4
+	srlx	%o4,32,%o5
+	brz,pt	%o2,.L_bn_mul_add_words_return
+	stuw	%o4,[%o0+4]
+
+	lduw	[%o1+8],%g2
+	lduw	[%o0+8],%g1
+	mulx	%o3,%g2,%g2
+	add	%g1,%o5,%o4
+	add	%o4,%g2,%o4
+	stuw	%o4,[%o0+8]
+	retl
+	srlx	%o4,32,%o0
+
+.type	bn_mul_add_words,#function
+.size	bn_mul_add_words,(.-bn_mul_add_words)
+
+.align	32
+
+.global bn_mul_words
+/*
+ * BN_ULONG bn_mul_words(rp,ap,num,w)
+ * BN_ULONG *rp,*ap;
+ * int num;
+ * BN_ULONG w;
+ */
+bn_mul_words:
+	sra	%o2,%g0,%o2	! signx %o2
+	brgz,a	%o2,.L_bn_mul_words_proceeed
+	lduw	[%o1],%g2
+	retl
+	clr	%o0
+	nop
+	nop
+	nop
+
+.L_bn_mul_words_proceeed:
+	srl	%o3,%g0,%o3	! clruw	%o3
+	andcc	%o2,-4,%g0
+	bz,pn	%icc,.L_bn_mul_words_tail
+	clr	%o5
+
+.L_bn_mul_words_loop:		! wow! 32 aligned!
+	lduw	[%o1+4],%g3
+	mulx	%o3,%g2,%g2
+	add	%g2,%o5,%o4
+	nop
+	stuw	%o4,[%o0]
+	srlx	%o4,32,%o5
+
+	lduw	[%o1+8],%g2
+	mulx	%o3,%g3,%g3
+	add	%g3,%o5,%o4
+	dec	4,%o2
+	stuw	%o4,[%o0+4]
+	srlx	%o4,32,%o5
+
+	lduw	[%o1+12],%g3
+	mulx	%o3,%g2,%g2
+	add	%g2,%o5,%o4
+	inc	16,%o1
+	stuw	%o4,[%o0+8]
+	srlx	%o4,32,%o5
+
+	mulx	%o3,%g3,%g3
+	add	%g3,%o5,%o4
+	inc	16,%o0
+	stuw	%o4,[%o0-4]
+	srlx	%o4,32,%o5
+	andcc	%o2,-4,%g0
+	bnz,a,pt	%icc,.L_bn_mul_words_loop
+	lduw	[%o1],%g2
+	nop
+	nop
+
+	brnz,a,pn	%o2,.L_bn_mul_words_tail
+	lduw	[%o1],%g2
+.L_bn_mul_words_return:
+	retl
+	mov	%o5,%o0
+
+.L_bn_mul_words_tail:
+	mulx	%o3,%g2,%g2
+	add	%g2,%o5,%o4
+	dec	%o2
+	srlx	%o4,32,%o5
+	brz,pt	%o2,.L_bn_mul_words_return
+	stuw	%o4,[%o0]
+
+	lduw	[%o1+4],%g2
+	mulx	%o3,%g2,%g2
+	add	%g2,%o5,%o4
+	dec	%o2
+	srlx	%o4,32,%o5
+	brz,pt	%o2,.L_bn_mul_words_return
+	stuw	%o4,[%o0+4]
+
+	lduw	[%o1+8],%g2
+	mulx	%o3,%g2,%g2
+	add	%g2,%o5,%o4
+	stuw	%o4,[%o0+8]
+	retl
+	srlx	%o4,32,%o0
+
+.type	bn_mul_words,#function
+.size	bn_mul_words,(.-bn_mul_words)
+
+.align  32
+.global	bn_sqr_words
+/*
+ * void bn_sqr_words(r,a,n)
+ * BN_ULONG *r,*a;
+ * int n;
+ */
+bn_sqr_words:
+	sra	%o2,%g0,%o2	! signx %o2
+	brgz,a	%o2,.L_bn_sqr_words_proceeed
+	lduw	[%o1],%g2
+	retl
+	clr	%o0
+	nop
+	nop
+	nop
+
+.L_bn_sqr_words_proceeed:
+	andcc	%o2,-4,%g0
+	nop
+	bz,pn	%icc,.L_bn_sqr_words_tail
+	nop
+
+.L_bn_sqr_words_loop:		! wow! 32 aligned!
+	lduw	[%o1+4],%g3
+	mulx	%g2,%g2,%o4
+	stuw	%o4,[%o0]
+	srlx	%o4,32,%o5
+	stuw	%o5,[%o0+4]
+	nop
+
+	lduw	[%o1+8],%g2
+	mulx	%g3,%g3,%o4
+	dec	4,%o2
+	stuw	%o4,[%o0+8]
+	srlx	%o4,32,%o5
+	stuw	%o5,[%o0+12]
+
+	lduw	[%o1+12],%g3
+	mulx	%g2,%g2,%o4
+	srlx	%o4,32,%o5
+	stuw	%o4,[%o0+16]
+	inc	16,%o1
+	stuw	%o5,[%o0+20]
+
+	mulx	%g3,%g3,%o4
+	inc	32,%o0
+	stuw	%o4,[%o0-8]
+	srlx	%o4,32,%o5
+	andcc	%o2,-4,%g2
+	stuw	%o5,[%o0-4]
+	bnz,a,pt	%icc,.L_bn_sqr_words_loop
+	lduw	[%o1],%g2
+	nop
+
+	brnz,a,pn	%o2,.L_bn_sqr_words_tail
+	lduw	[%o1],%g2
+.L_bn_sqr_words_return:
+	retl
+	clr	%o0
+
+.L_bn_sqr_words_tail:
+	mulx	%g2,%g2,%o4
+	dec	%o2
+	stuw	%o4,[%o0]
+	srlx	%o4,32,%o5
+	brz,pt	%o2,.L_bn_sqr_words_return
+	stuw	%o5,[%o0+4]
+
+	lduw	[%o1+4],%g2
+	mulx	%g2,%g2,%o4
+	dec	%o2
+	stuw	%o4,[%o0+8]
+	srlx	%o4,32,%o5
+	brz,pt	%o2,.L_bn_sqr_words_return
+	stuw	%o5,[%o0+12]
+
+	lduw	[%o1+8],%g2
+	mulx	%g2,%g2,%o4
+	srlx	%o4,32,%o5
+	stuw	%o4,[%o0+16]
+	stuw	%o5,[%o0+20]
+	retl
+	clr	%o0
+
+.type	bn_sqr_words,#function
+.size	bn_sqr_words,(.-bn_sqr_words)
+
+.align	32
+.global bn_div_words
+/*
+ * BN_ULONG bn_div_words(h,l,d)
+ * BN_ULONG h,l,d;
+ */
+bn_div_words:
+	sllx	%o0,32,%o0
+	or	%o0,%o1,%o0
+	udivx	%o0,%o2,%o0
+	retl
+	srl	%o0,%g0,%o0	! clruw	%o0
+
+.type	bn_div_words,#function
+.size	bn_div_words,(.-bn_div_words)
+
+.align	32
+
+.global bn_add_words
+/*
+ * BN_ULONG bn_add_words(rp,ap,bp,n)
+ * BN_ULONG *rp,*ap,*bp;
+ * int n;
+ */
+bn_add_words:
+	sra	%o3,%g0,%o3	! signx %o3
+	brgz,a	%o3,.L_bn_add_words_proceed
+	lduw	[%o1],%o4
+	retl
+	clr	%o0
+
+.L_bn_add_words_proceed:
+	andcc	%o3,-4,%g0
+	bz,pn	%icc,.L_bn_add_words_tail
+	addcc	%g0,0,%g0	! clear carry flag
+
+.L_bn_add_words_loop:		! wow! 32 aligned!
+	dec	4,%o3
+	lduw	[%o2],%o5
+	lduw	[%o1+4],%g1
+	lduw	[%o2+4],%g2
+	lduw	[%o1+8],%g3
+	lduw	[%o2+8],%g4
+	addccc	%o5,%o4,%o5
+	stuw	%o5,[%o0]
+
+	lduw	[%o1+12],%o4
+	lduw	[%o2+12],%o5
+	inc	16,%o1
+	addccc	%g1,%g2,%g1
+	stuw	%g1,[%o0+4]
+	
+	inc	16,%o2
+	addccc	%g3,%g4,%g3
+	stuw	%g3,[%o0+8]
+
+	inc	16,%o0
+	addccc	%o5,%o4,%o5
+	stuw	%o5,[%o0-4]
+	and	%o3,-4,%g1
+	brnz,a,pt	%g1,.L_bn_add_words_loop
+	lduw	[%o1],%o4
+
+	brnz,a,pn	%o3,.L_bn_add_words_tail
+	lduw	[%o1],%o4
+.L_bn_add_words_return:
+	clr	%o0
+	retl
+	movcs	%icc,1,%o0
+	nop
+
+.L_bn_add_words_tail:
+	lduw	[%o2],%o5
+	dec	%o3
+	addccc	%o5,%o4,%o5
+	brz,pt	%o3,.L_bn_add_words_return
+	stuw	%o5,[%o0]
+
+	lduw	[%o1+4],%o4
+	lduw	[%o2+4],%o5
+	dec	%o3
+	addccc	%o5,%o4,%o5
+	brz,pt	%o3,.L_bn_add_words_return
+	stuw	%o5,[%o0+4]
+
+	lduw	[%o1+8],%o4
+	lduw	[%o2+8],%o5
+	addccc	%o5,%o4,%o5
+	stuw	%o5,[%o0+8]
+	clr	%o0
+	retl
+	movcs	%icc,1,%o0
+
+.type	bn_add_words,#function
+.size	bn_add_words,(.-bn_add_words)
+
+.global bn_sub_words
+/*
+ * BN_ULONG bn_sub_words(rp,ap,bp,n)
+ * BN_ULONG *rp,*ap,*bp;
+ * int n;
+ */
+bn_sub_words:
+	sra	%o3,%g0,%o3	! signx %o3
+	brgz,a	%o3,.L_bn_sub_words_proceed
+	lduw	[%o1],%o4
+	retl
+	clr	%o0
+
+.L_bn_sub_words_proceed:
+	andcc	%o3,-4,%g0
+	bz,pn	%icc,.L_bn_sub_words_tail
+	addcc	%g0,0,%g0	! clear carry flag
+
+.L_bn_sub_words_loop:		! wow! 32 aligned!
+	dec	4,%o3
+	lduw	[%o2],%o5
+	lduw	[%o1+4],%g1
+	lduw	[%o2+4],%g2
+	lduw	[%o1+8],%g3
+	lduw	[%o2+8],%g4
+	subccc	%o4,%o5,%o5
+	stuw	%o5,[%o0]
+
+	lduw	[%o1+12],%o4
+	lduw	[%o2+12],%o5
+	inc	16,%o1
+	subccc	%g1,%g2,%g2
+	stuw	%g2,[%o0+4]
+
+	inc	16,%o2
+	subccc	%g3,%g4,%g4
+	stuw	%g4,[%o0+8]
+
+	inc	16,%o0
+	subccc	%o4,%o5,%o5
+	stuw	%o5,[%o0-4]
+	and	%o3,-4,%g1
+	brnz,a,pt	%g1,.L_bn_sub_words_loop
+	lduw	[%o1],%o4
+
+	brnz,a,pn	%o3,.L_bn_sub_words_tail
+	lduw	[%o1],%o4
+.L_bn_sub_words_return:
+	clr	%o0
+	retl
+	movcs	%icc,1,%o0
+	nop
+
+.L_bn_sub_words_tail:		! wow! 32 aligned!
+	lduw	[%o2],%o5
+	dec	%o3
+	subccc	%o4,%o5,%o5
+	brz,pt	%o3,.L_bn_sub_words_return
+	stuw	%o5,[%o0]
+
+	lduw	[%o1+4],%o4
+	lduw	[%o2+4],%o5
+	dec	%o3
+	subccc	%o4,%o5,%o5
+	brz,pt	%o3,.L_bn_sub_words_return
+	stuw	%o5,[%o0+4]
+
+	lduw	[%o1+8],%o4
+	lduw	[%o2+8],%o5
+	subccc	%o4,%o5,%o5
+	stuw	%o5,[%o0+8]
+	clr	%o0
+	retl
+	movcs	%icc,1,%o0
+
+.type	bn_sub_words,#function
+.size	bn_sub_words,(.-bn_sub_words)
+
+/*
+ * Code below depends on the fact that upper parts of the %l0-%l7
+ * and %i0-%i7 are zeroed by kernel after context switch. In
+ * previous versions this comment stated that "the trouble is that
+ * it's not feasible to implement the mumbo-jumbo in less V9
+ * instructions:-(" which apparently isn't true thanks to
+ * 'bcs,a %xcc,.+8; inc %rd' pair. But the performance improvement
+ * results not from the shorter code, but from elimination of
+ * multicycle none-pairable 'rd %y,%rd' instructions.
+ *
+ *							Andy.
+ */
+
+/*
+ * Here is register usage map for *all* routines below.
+ */
+#define t_1	%o0
+#define	t_2	%o1
+#define c_12	%o2
+#define c_3	%o3
+
+#define ap(I)	[%i1+4*I]
+#define bp(I)	[%i2+4*I]
+#define rp(I)	[%i0+4*I]
+
+#define	a_0	%l0
+#define	a_1	%l1
+#define	a_2	%l2
+#define	a_3	%l3
+#define	a_4	%l4
+#define	a_5	%l5
+#define	a_6	%l6
+#define	a_7	%l7
+
+#define	b_0	%i3
+#define	b_1	%i4
+#define	b_2	%i5
+#define	b_3	%o4
+#define	b_4	%o5
+#define	b_5	%o7
+#define	b_6	%g1
+#define	b_7	%g4
+
+.align	32
+.global bn_mul_comba8
+/*
+ * void bn_mul_comba8(r,a,b)
+ * BN_ULONG *r,*a,*b;
+ */
+bn_mul_comba8:
+	save	%sp,FRAME_SIZE,%sp
+	mov	1,t_2
+	lduw	ap(0),a_0
+	sllx	t_2,32,t_2
+	lduw	bp(0),b_0	!=
+	lduw	bp(1),b_1
+	mulx	a_0,b_0,t_1	!mul_add_c(a[0],b[0],c1,c2,c3);
+	srlx	t_1,32,c_12
+	stuw	t_1,rp(0)	!=!r[0]=c1;
+
+	lduw	ap(1),a_1
+	mulx	a_0,b_1,t_1	!mul_add_c(a[0],b[1],c2,c3,c1);
+	addcc	c_12,t_1,c_12
+	clr	c_3		!=
+	bcs,a	%xcc,.+8
+	add	c_3,t_2,c_3
+	lduw	ap(2),a_2
+	mulx	a_1,b_0,t_1	!=!mul_add_c(a[1],b[0],c2,c3,c1);
+	addcc	c_12,t_1,t_1
+	bcs,a	%xcc,.+8
+	add	c_3,t_2,c_3
+	srlx	t_1,32,c_12	!=
+	stuw	t_1,rp(1)	!r[1]=c2;
+	or	c_12,c_3,c_12
+
+	mulx	a_2,b_0,t_1	!mul_add_c(a[2],b[0],c3,c1,c2);
+	addcc	c_12,t_1,c_12	!=
+	clr	c_3
+	bcs,a	%xcc,.+8
+	add	c_3,t_2,c_3
+	lduw	bp(2),b_2	!=
+	mulx	a_1,b_1,t_1	!mul_add_c(a[1],b[1],c3,c1,c2);
+	addcc	c_12,t_1,c_12
+	bcs,a	%xcc,.+8
+	add	c_3,t_2,c_3	!=
+	lduw	bp(3),b_3
+	mulx	a_0,b_2,t_1	!mul_add_c(a[0],b[2],c3,c1,c2);
+	addcc	c_12,t_1,t_1
+	bcs,a	%xcc,.+8	!=
+	add	c_3,t_2,c_3
+	srlx	t_1,32,c_12
+	stuw	t_1,rp(2)	!r[2]=c3;
+	or	c_12,c_3,c_12	!=
+
+	mulx	a_0,b_3,t_1	!mul_add_c(a[0],b[3],c1,c2,c3);
+	addcc	c_12,t_1,c_12
+	clr	c_3
+	bcs,a	%xcc,.+8	!=
+	add	c_3,t_2,c_3
+	mulx	a_1,b_2,t_1	!=!mul_add_c(a[1],b[2],c1,c2,c3);
+	addcc	c_12,t_1,c_12
+	bcs,a	%xcc,.+8	!=
+	add	c_3,t_2,c_3
+	lduw	ap(3),a_3
+	mulx	a_2,b_1,t_1	!mul_add_c(a[2],b[1],c1,c2,c3);
+	addcc	c_12,t_1,c_12	!=
+	bcs,a	%xcc,.+8
+	add	c_3,t_2,c_3
+	lduw	ap(4),a_4
+	mulx	a_3,b_0,t_1	!=!mul_add_c(a[3],b[0],c1,c2,c3);!=
+	addcc	c_12,t_1,t_1
+	bcs,a	%xcc,.+8
+	add	c_3,t_2,c_3
+	srlx	t_1,32,c_12	!=
+	stuw	t_1,rp(3)	!r[3]=c1;
+	or	c_12,c_3,c_12
+
+	mulx	a_4,b_0,t_1	!mul_add_c(a[4],b[0],c2,c3,c1);
+	addcc	c_12,t_1,c_12	!=
+	clr	c_3
+	bcs,a	%xcc,.+8
+	add	c_3,t_2,c_3
+	mulx	a_3,b_1,t_1	!=!mul_add_c(a[3],b[1],c2,c3,c1);
+	addcc	c_12,t_1,c_12
+	bcs,a	%xcc,.+8
+	add	c_3,t_2,c_3
+	mulx	a_2,b_2,t_1	!=!mul_add_c(a[2],b[2],c2,c3,c1);
+	addcc	c_12,t_1,c_12
+	bcs,a	%xcc,.+8
+	add	c_3,t_2,c_3
+	lduw	bp(4),b_4	!=
+	mulx	a_1,b_3,t_1	!mul_add_c(a[1],b[3],c2,c3,c1);
+	addcc	c_12,t_1,c_12
+	bcs,a	%xcc,.+8
+	add	c_3,t_2,c_3	!=
+	lduw	bp(5),b_5
+	mulx	a_0,b_4,t_1	!mul_add_c(a[0],b[4],c2,c3,c1);
+	addcc	c_12,t_1,t_1
+	bcs,a	%xcc,.+8	!=
+	add	c_3,t_2,c_3
+	srlx	t_1,32,c_12
+	stuw	t_1,rp(4)	!r[4]=c2;
+	or	c_12,c_3,c_12	!=
+
+	mulx	a_0,b_5,t_1	!mul_add_c(a[0],b[5],c3,c1,c2);
+	addcc	c_12,t_1,c_12
+	clr	c_3
+	bcs,a	%xcc,.+8	!=
+	add	c_3,t_2,c_3
+	mulx	a_1,b_4,t_1	!mul_add_c(a[1],b[4],c3,c1,c2);
+	addcc	c_12,t_1,c_12
+	bcs,a	%xcc,.+8	!=
+	add	c_3,t_2,c_3
+	mulx	a_2,b_3,t_1	!mul_add_c(a[2],b[3],c3,c1,c2);
+	addcc	c_12,t_1,c_12
+	bcs,a	%xcc,.+8	!=
+	add	c_3,t_2,c_3
+	mulx	a_3,b_2,t_1	!mul_add_c(a[3],b[2],c3,c1,c2);
+	addcc	c_12,t_1,c_12
+	bcs,a	%xcc,.+8	!=
+	add	c_3,t_2,c_3
+	lduw	ap(5),a_5
+	mulx	a_4,b_1,t_1	!mul_add_c(a[4],b[1],c3,c1,c2);
+	addcc	c_12,t_1,c_12	!=
+	bcs,a	%xcc,.+8
+	add	c_3,t_2,c_3
+	lduw	ap(6),a_6
+	mulx	a_5,b_0,t_1	!=!mul_add_c(a[5],b[0],c3,c1,c2);
+	addcc	c_12,t_1,t_1
+	bcs,a	%xcc,.+8
+	add	c_3,t_2,c_3
+	srlx	t_1,32,c_12	!=
+	stuw	t_1,rp(5)	!r[5]=c3;
+	or	c_12,c_3,c_12
+
+	mulx	a_6,b_0,t_1	!mul_add_c(a[6],b[0],c1,c2,c3);
+	addcc	c_12,t_1,c_12	!=
+	clr	c_3
+	bcs,a	%xcc,.+8
+	add	c_3,t_2,c_3
+	mulx	a_5,b_1,t_1	!=!mul_add_c(a[5],b[1],c1,c2,c3);
+	addcc	c_12,t_1,c_12
+	bcs,a	%xcc,.+8
+	add	c_3,t_2,c_3
+	mulx	a_4,b_2,t_1	!=!mul_add_c(a[4],b[2],c1,c2,c3);
+	addcc	c_12,t_1,c_12
+	bcs,a	%xcc,.+8
+	add	c_3,t_2,c_3
+	mulx	a_3,b_3,t_1	!=!mul_add_c(a[3],b[3],c1,c2,c3);
+	addcc	c_12,t_1,c_12
+	bcs,a	%xcc,.+8
+	add	c_3,t_2,c_3
+	mulx	a_2,b_4,t_1	!=!mul_add_c(a[2],b[4],c1,c2,c3);
+	addcc	c_12,t_1,c_12
+	bcs,a	%xcc,.+8
+	add	c_3,t_2,c_3
+	lduw	bp(6),b_6	!=
+	mulx	a_1,b_5,t_1	!mul_add_c(a[1],b[5],c1,c2,c3);
+	addcc	c_12,t_1,c_12
+	bcs,a	%xcc,.+8
+	add	c_3,t_2,c_3	!=
+	lduw	bp(7),b_7
+	mulx	a_0,b_6,t_1	!mul_add_c(a[0],b[6],c1,c2,c3);
+	addcc	c_12,t_1,t_1
+	bcs,a	%xcc,.+8	!=
+	add	c_3,t_2,c_3
+	srlx	t_1,32,c_12
+	stuw	t_1,rp(6)	!r[6]=c1;
+	or	c_12,c_3,c_12	!=
+
+	mulx	a_0,b_7,t_1	!mul_add_c(a[0],b[7],c2,c3,c1);
+	addcc	c_12,t_1,c_12
+	clr	c_3
+	bcs,a	%xcc,.+8	!=
+	add	c_3,t_2,c_3
+	mulx	a_1,b_6,t_1	!mul_add_c(a[1],b[6],c2,c3,c1);
+	addcc	c_12,t_1,c_12
+	bcs,a	%xcc,.+8	!=
+	add	c_3,t_2,c_3
+	mulx	a_2,b_5,t_1	!mul_add_c(a[2],b[5],c2,c3,c1);
+	addcc	c_12,t_1,c_12
+	bcs,a	%xcc,.+8	!=
+	add	c_3,t_2,c_3
+	mulx	a_3,b_4,t_1	!mul_add_c(a[3],b[4],c2,c3,c1);
+	addcc	c_12,t_1,c_12
+	bcs,a	%xcc,.+8	!=
+	add	c_3,t_2,c_3
+	mulx	a_4,b_3,t_1	!mul_add_c(a[4],b[3],c2,c3,c1);
+	addcc	c_12,t_1,c_12
+	bcs,a	%xcc,.+8	!=
+	add	c_3,t_2,c_3
+	mulx	a_5,b_2,t_1	!mul_add_c(a[5],b[2],c2,c3,c1);
+	addcc	c_12,t_1,c_12
+	bcs,a	%xcc,.+8	!=
+	add	c_3,t_2,c_3
+	lduw	ap(7),a_7
+	mulx	a_6,b_1,t_1	!=!mul_add_c(a[6],b[1],c2,c3,c1);
+	addcc	c_12,t_1,c_12
+	bcs,a	%xcc,.+8
+	add	c_3,t_2,c_3
+	mulx	a_7,b_0,t_1	!=!mul_add_c(a[7],b[0],c2,c3,c1);
+	addcc	c_12,t_1,t_1
+	bcs,a	%xcc,.+8
+	add	c_3,t_2,c_3
+	srlx	t_1,32,c_12	!=
+	stuw	t_1,rp(7)	!r[7]=c2;
+	or	c_12,c_3,c_12
+
+	mulx	a_7,b_1,t_1	!=!mul_add_c(a[7],b[1],c3,c1,c2);
+	addcc	c_12,t_1,c_12
+	clr	c_3
+	bcs,a	%xcc,.+8
+	add	c_3,t_2,c_3	!=
+	mulx	a_6,b_2,t_1	!mul_add_c(a[6],b[2],c3,c1,c2);
+	addcc	c_12,t_1,c_12
+	bcs,a	%xcc,.+8
+	add	c_3,t_2,c_3	!=
+	mulx	a_5,b_3,t_1	!mul_add_c(a[5],b[3],c3,c1,c2);
+	addcc	c_12,t_1,c_12
+	bcs,a	%xcc,.+8
+	add	c_3,t_2,c_3	!=
+	mulx	a_4,b_4,t_1	!mul_add_c(a[4],b[4],c3,c1,c2);
+	addcc	c_12,t_1,c_12
+	bcs,a	%xcc,.+8
+	add	c_3,t_2,c_3	!=
+	mulx	a_3,b_5,t_1	!mul_add_c(a[3],b[5],c3,c1,c2);
+	addcc	c_12,t_1,c_12
+	bcs,a	%xcc,.+8
+	add	c_3,t_2,c_3	!=
+	mulx	a_2,b_6,t_1	!mul_add_c(a[2],b[6],c3,c1,c2);
+	addcc	c_12,t_1,c_12
+	bcs,a	%xcc,.+8
+	add	c_3,t_2,c_3	!=
+	mulx	a_1,b_7,t_1	!mul_add_c(a[1],b[7],c3,c1,c2);
+	addcc	c_12,t_1,t_1
+	bcs,a	%xcc,.+8
+	add	c_3,t_2,c_3	!=
+	srlx	t_1,32,c_12
+	stuw	t_1,rp(8)	!r[8]=c3;
+	or	c_12,c_3,c_12
+
+	mulx	a_2,b_7,t_1	!=!mul_add_c(a[2],b[7],c1,c2,c3);
+	addcc	c_12,t_1,c_12
+	clr	c_3
+	bcs,a	%xcc,.+8
+	add	c_3,t_2,c_3	!=
+	mulx	a_3,b_6,t_1	!mul_add_c(a[3],b[6],c1,c2,c3);
+	addcc	c_12,t_1,c_12
+	bcs,a	%xcc,.+8	!=
+	add	c_3,t_2,c_3
+	mulx	a_4,b_5,t_1	!mul_add_c(a[4],b[5],c1,c2,c3);
+	addcc	c_12,t_1,c_12
+	bcs,a	%xcc,.+8	!=
+	add	c_3,t_2,c_3
+	mulx	a_5,b_4,t_1	!mul_add_c(a[5],b[4],c1,c2,c3);
+	addcc	c_12,t_1,c_12
+	bcs,a	%xcc,.+8	!=
+	add	c_3,t_2,c_3
+	mulx	a_6,b_3,t_1	!mul_add_c(a[6],b[3],c1,c2,c3);
+	addcc	c_12,t_1,c_12
+	bcs,a	%xcc,.+8	!=
+	add	c_3,t_2,c_3
+	mulx	a_7,b_2,t_1	!mul_add_c(a[7],b[2],c1,c2,c3);
+	addcc	c_12,t_1,t_1
+	bcs,a	%xcc,.+8	!=
+	add	c_3,t_2,c_3
+	srlx	t_1,32,c_12
+	stuw	t_1,rp(9)	!r[9]=c1;
+	or	c_12,c_3,c_12	!=
+
+	mulx	a_7,b_3,t_1	!mul_add_c(a[7],b[3],c2,c3,c1);
+	addcc	c_12,t_1,c_12
+	clr	c_3
+	bcs,a	%xcc,.+8	!=
+	add	c_3,t_2,c_3
+	mulx	a_6,b_4,t_1	!mul_add_c(a[6],b[4],c2,c3,c1);
+	addcc	c_12,t_1,c_12
+	bcs,a	%xcc,.+8	!=
+	add	c_3,t_2,c_3
+	mulx	a_5,b_5,t_1	!mul_add_c(a[5],b[5],c2,c3,c1);
+	addcc	c_12,t_1,c_12
+	bcs,a	%xcc,.+8	!=
+	add	c_3,t_2,c_3
+	mulx	a_4,b_6,t_1	!mul_add_c(a[4],b[6],c2,c3,c1);
+	addcc	c_12,t_1,c_12
+	bcs,a	%xcc,.+8	!=
+	add	c_3,t_2,c_3
+	mulx	a_3,b_7,t_1	!mul_add_c(a[3],b[7],c2,c3,c1);
+	addcc	c_12,t_1,t_1
+	bcs,a	%xcc,.+8	!=
+	add	c_3,t_2,c_3
+	srlx	t_1,32,c_12
+	stuw	t_1,rp(10)	!r[10]=c2;
+	or	c_12,c_3,c_12	!=
+
+	mulx	a_4,b_7,t_1	!mul_add_c(a[4],b[7],c3,c1,c2);
+	addcc	c_12,t_1,c_12
+	clr	c_3
+	bcs,a	%xcc,.+8	!=
+	add	c_3,t_2,c_3
+	mulx	a_5,b_6,t_1	!mul_add_c(a[5],b[6],c3,c1,c2);
+	addcc	c_12,t_1,c_12
+	bcs,a	%xcc,.+8	!=
+	add	c_3,t_2,c_3
+	mulx	a_6,b_5,t_1	!mul_add_c(a[6],b[5],c3,c1,c2);
+	addcc	c_12,t_1,c_12
+	bcs,a	%xcc,.+8	!=
+	add	c_3,t_2,c_3
+	mulx	a_7,b_4,t_1	!mul_add_c(a[7],b[4],c3,c1,c2);
+	addcc	c_12,t_1,t_1
+	bcs,a	%xcc,.+8	!=
+	add	c_3,t_2,c_3
+	srlx	t_1,32,c_12
+	stuw	t_1,rp(11)	!r[11]=c3;
+	or	c_12,c_3,c_12	!=
+
+	mulx	a_7,b_5,t_1	!mul_add_c(a[7],b[5],c1,c2,c3);
+	addcc	c_12,t_1,c_12
+	clr	c_3
+	bcs,a	%xcc,.+8	!=
+	add	c_3,t_2,c_3
+	mulx	a_6,b_6,t_1	!mul_add_c(a[6],b[6],c1,c2,c3);
+	addcc	c_12,t_1,c_12
+	bcs,a	%xcc,.+8	!=
+	add	c_3,t_2,c_3
+	mulx	a_5,b_7,t_1	!mul_add_c(a[5],b[7],c1,c2,c3);
+	addcc	c_12,t_1,t_1
+	bcs,a	%xcc,.+8	!=
+	add	c_3,t_2,c_3
+	srlx	t_1,32,c_12
+	stuw	t_1,rp(12)	!r[12]=c1;
+	or	c_12,c_3,c_12	!=
+
+	mulx	a_6,b_7,t_1	!mul_add_c(a[6],b[7],c2,c3,c1);
+	addcc	c_12,t_1,c_12
+	clr	c_3
+	bcs,a	%xcc,.+8	!=
+	add	c_3,t_2,c_3
+	mulx	a_7,b_6,t_1	!mul_add_c(a[7],b[6],c2,c3,c1);
+	addcc	c_12,t_1,t_1
+	bcs,a	%xcc,.+8	!=
+	add	c_3,t_2,c_3
+	srlx	t_1,32,c_12
+	st	t_1,rp(13)	!r[13]=c2;
+	or	c_12,c_3,c_12	!=
+
+	mulx	a_7,b_7,t_1	!mul_add_c(a[7],b[7],c3,c1,c2);
+	addcc	c_12,t_1,t_1
+	srlx	t_1,32,c_12	!=
+	stuw	t_1,rp(14)	!r[14]=c3;
+	stuw	c_12,rp(15)	!r[15]=c1;
+
+	ret
+	restore	%g0,%g0,%o0	!=
+
+.type	bn_mul_comba8,#function
+.size	bn_mul_comba8,(.-bn_mul_comba8)
+
+.align	32
+
+.global bn_mul_comba4
+/*
+ * void bn_mul_comba4(r,a,b)
+ * BN_ULONG *r,*a,*b;
+ */
+bn_mul_comba4:
+	save	%sp,FRAME_SIZE,%sp
+	lduw	ap(0),a_0
+	mov	1,t_2
+	lduw	bp(0),b_0
+	sllx	t_2,32,t_2	!=
+	lduw	bp(1),b_1
+	mulx	a_0,b_0,t_1	!mul_add_c(a[0],b[0],c1,c2,c3);
+	srlx	t_1,32,c_12
+	stuw	t_1,rp(0)	!=!r[0]=c1;
+
+	lduw	ap(1),a_1
+	mulx	a_0,b_1,t_1	!mul_add_c(a[0],b[1],c2,c3,c1);
+	addcc	c_12,t_1,c_12
+	clr	c_3		!=
+	bcs,a	%xcc,.+8
+	add	c_3,t_2,c_3
+	lduw	ap(2),a_2
+	mulx	a_1,b_0,t_1	!=!mul_add_c(a[1],b[0],c2,c3,c1);
+	addcc	c_12,t_1,t_1
+	bcs,a	%xcc,.+8
+	add	c_3,t_2,c_3
+	srlx	t_1,32,c_12	!=
+	stuw	t_1,rp(1)	!r[1]=c2;
+	or	c_12,c_3,c_12
+
+	mulx	a_2,b_0,t_1	!mul_add_c(a[2],b[0],c3,c1,c2);
+	addcc	c_12,t_1,c_12	!=
+	clr	c_3
+	bcs,a	%xcc,.+8
+	add	c_3,t_2,c_3
+	lduw	bp(2),b_2	!=
+	mulx	a_1,b_1,t_1	!mul_add_c(a[1],b[1],c3,c1,c2);
+	addcc	c_12,t_1,c_12
+	bcs,a	%xcc,.+8
+	add	c_3,t_2,c_3	!=
+	lduw	bp(3),b_3
+	mulx	a_0,b_2,t_1	!mul_add_c(a[0],b[2],c3,c1,c2);
+	addcc	c_12,t_1,t_1
+	bcs,a	%xcc,.+8	!=
+	add	c_3,t_2,c_3
+	srlx	t_1,32,c_12
+	stuw	t_1,rp(2)	!r[2]=c3;
+	or	c_12,c_3,c_12	!=
+
+	mulx	a_0,b_3,t_1	!mul_add_c(a[0],b[3],c1,c2,c3);
+	addcc	c_12,t_1,c_12
+	clr	c_3
+	bcs,a	%xcc,.+8	!=
+	add	c_3,t_2,c_3
+	mulx	a_1,b_2,t_1	!mul_add_c(a[1],b[2],c1,c2,c3);
+	addcc	c_12,t_1,c_12
+	bcs,a	%xcc,.+8	!=
+	add	c_3,t_2,c_3
+	lduw	ap(3),a_3
+	mulx	a_2,b_1,t_1	!mul_add_c(a[2],b[1],c1,c2,c3);
+	addcc	c_12,t_1,c_12	!=
+	bcs,a	%xcc,.+8
+	add	c_3,t_2,c_3
+	mulx	a_3,b_0,t_1	!mul_add_c(a[3],b[0],c1,c2,c3);!=
+	addcc	c_12,t_1,t_1	!=
+	bcs,a	%xcc,.+8
+	add	c_3,t_2,c_3
+	srlx	t_1,32,c_12
+	stuw	t_1,rp(3)	!=!r[3]=c1;
+	or	c_12,c_3,c_12
+
+	mulx	a_3,b_1,t_1	!mul_add_c(a[3],b[1],c2,c3,c1);
+	addcc	c_12,t_1,c_12
+	clr	c_3		!=
+	bcs,a	%xcc,.+8
+	add	c_3,t_2,c_3
+	mulx	a_2,b_2,t_1	!mul_add_c(a[2],b[2],c2,c3,c1);
+	addcc	c_12,t_1,c_12	!=
+	bcs,a	%xcc,.+8
+	add	c_3,t_2,c_3
+	mulx	a_1,b_3,t_1	!mul_add_c(a[1],b[3],c2,c3,c1);
+	addcc	c_12,t_1,t_1	!=
+	bcs,a	%xcc,.+8
+	add	c_3,t_2,c_3
+	srlx	t_1,32,c_12
+	stuw	t_1,rp(4)	!=!r[4]=c2;
+	or	c_12,c_3,c_12
+
+	mulx	a_2,b_3,t_1	!mul_add_c(a[2],b[3],c3,c1,c2);
+	addcc	c_12,t_1,c_12
+	clr	c_3		!=
+	bcs,a	%xcc,.+8
+	add	c_3,t_2,c_3
+	mulx	a_3,b_2,t_1	!mul_add_c(a[3],b[2],c3,c1,c2);
+	addcc	c_12,t_1,t_1	!=
+	bcs,a	%xcc,.+8
+	add	c_3,t_2,c_3
+	srlx	t_1,32,c_12
+	stuw	t_1,rp(5)	!=!r[5]=c3;
+	or	c_12,c_3,c_12
+
+	mulx	a_3,b_3,t_1	!mul_add_c(a[3],b[3],c1,c2,c3);
+	addcc	c_12,t_1,t_1
+	srlx	t_1,32,c_12	!=
+	stuw	t_1,rp(6)	!r[6]=c1;
+	stuw	c_12,rp(7)	!r[7]=c2;
+	
+	ret
+	restore	%g0,%g0,%o0
+
+.type	bn_mul_comba4,#function
+.size	bn_mul_comba4,(.-bn_mul_comba4)
+
+.align	32
+
+.global bn_sqr_comba8
+bn_sqr_comba8:
+	save	%sp,FRAME_SIZE,%sp
+	mov	1,t_2
+	lduw	ap(0),a_0
+	sllx	t_2,32,t_2
+	lduw	ap(1),a_1
+	mulx	a_0,a_0,t_1	!sqr_add_c(a,0,c1,c2,c3);
+	srlx	t_1,32,c_12
+	stuw	t_1,rp(0)	!r[0]=c1;
+
+	lduw	ap(2),a_2
+	mulx	a_0,a_1,t_1	!=!sqr_add_c2(a,1,0,c2,c3,c1);
+	addcc	c_12,t_1,c_12
+	clr	c_3
+	bcs,a	%xcc,.+8
+	add	c_3,t_2,c_3
+	addcc	c_12,t_1,t_1
+	bcs,a	%xcc,.+8
+	add	c_3,t_2,c_3
+	srlx	t_1,32,c_12
+	stuw	t_1,rp(1)	!r[1]=c2;
+	or	c_12,c_3,c_12
+
+	mulx	a_2,a_0,t_1	!sqr_add_c2(a,2,0,c3,c1,c2);
+	addcc	c_12,t_1,c_12
+	clr	c_3
+	bcs,a	%xcc,.+8
+	add	c_3,t_2,c_3
+	addcc	c_12,t_1,c_12
+	bcs,a	%xcc,.+8
+	add	c_3,t_2,c_3
+	lduw	ap(3),a_3
+	mulx	a_1,a_1,t_1	!sqr_add_c(a,1,c3,c1,c2);
+	addcc	c_12,t_1,t_1
+	bcs,a	%xcc,.+8
+	add	c_3,t_2,c_3
+	srlx	t_1,32,c_12
+	stuw	t_1,rp(2)	!r[2]=c3;
+	or	c_12,c_3,c_12
+
+	mulx	a_0,a_3,t_1	!sqr_add_c2(a,3,0,c1,c2,c3);
+	addcc	c_12,t_1,c_12
+	clr	c_3
+	bcs,a	%xcc,.+8
+	add	c_3,t_2,c_3
+	addcc	c_12,t_1,c_12
+	bcs,a	%xcc,.+8
+	add	c_3,t_2,c_3
+	lduw	ap(4),a_4
+	mulx	a_1,a_2,t_1	!sqr_add_c2(a,2,1,c1,c2,c3);
+	addcc	c_12,t_1,c_12
+	bcs,a	%xcc,.+8
+	add	c_3,t_2,c_3
+	addcc	c_12,t_1,t_1
+	bcs,a	%xcc,.+8
+	add	c_3,t_2,c_3
+	srlx	t_1,32,c_12
+	st	t_1,rp(3)	!r[3]=c1;
+	or	c_12,c_3,c_12
+
+	mulx	a_4,a_0,t_1	!sqr_add_c2(a,4,0,c2,c3,c1);
+	addcc	c_12,t_1,c_12
+	clr	c_3
+	bcs,a	%xcc,.+8
+	add	c_3,t_2,c_3
+	addcc	c_12,t_1,c_12
+	bcs,a	%xcc,.+8
+	add	c_3,t_2,c_3
+	mulx	a_3,a_1,t_1	!sqr_add_c2(a,3,1,c2,c3,c1);
+	addcc	c_12,t_1,c_12
+	bcs,a	%xcc,.+8
+	add	c_3,t_2,c_3
+	addcc	c_12,t_1,c_12
+	bcs,a	%xcc,.+8
+	add	c_3,t_2,c_3
+	lduw	ap(5),a_5
+	mulx	a_2,a_2,t_1	!sqr_add_c(a,2,c2,c3,c1);
+	addcc	c_12,t_1,t_1
+	bcs,a	%xcc,.+8
+	add	c_3,t_2,c_3
+	srlx	t_1,32,c_12
+	stuw	t_1,rp(4)	!r[4]=c2;
+	or	c_12,c_3,c_12
+
+	mulx	a_0,a_5,t_1	!sqr_add_c2(a,5,0,c3,c1,c2);
+	addcc	c_12,t_1,c_12
+	clr	c_3
+	bcs,a	%xcc,.+8
+	add	c_3,t_2,c_3
+	addcc	c_12,t_1,c_12
+	bcs,a	%xcc,.+8
+	add	c_3,t_2,c_3
+	mulx	a_1,a_4,t_1	!sqr_add_c2(a,4,1,c3,c1,c2);
+	addcc	c_12,t_1,c_12
+	bcs,a	%xcc,.+8
+	add	c_3,t_2,c_3
+	addcc	c_12,t_1,c_12
+	bcs,a	%xcc,.+8
+	add	c_3,t_2,c_3
+	lduw	ap(6),a_6
+	mulx	a_2,a_3,t_1	!sqr_add_c2(a,3,2,c3,c1,c2);
+	addcc	c_12,t_1,c_12
+	bcs,a	%xcc,.+8
+	add	c_3,t_2,c_3
+	addcc	c_12,t_1,t_1
+	bcs,a	%xcc,.+8
+	add	c_3,t_2,c_3
+	srlx	t_1,32,c_12
+	stuw	t_1,rp(5)	!r[5]=c3;
+	or	c_12,c_3,c_12
+
+	mulx	a_6,a_0,t_1	!sqr_add_c2(a,6,0,c1,c2,c3);
+	addcc	c_12,t_1,c_12
+	clr	c_3
+	bcs,a	%xcc,.+8
+	add	c_3,t_2,c_3
+	addcc	c_12,t_1,c_12
+	bcs,a	%xcc,.+8
+	add	c_3,t_2,c_3
+	mulx	a_5,a_1,t_1	!sqr_add_c2(a,5,1,c1,c2,c3);
+	addcc	c_12,t_1,c_12
+	bcs,a	%xcc,.+8
+	add	c_3,t_2,c_3
+	addcc	c_12,t_1,c_12
+	bcs,a	%xcc,.+8
+	add	c_3,t_2,c_3
+	mulx	a_4,a_2,t_1	!sqr_add_c2(a,4,2,c1,c2,c3);
+	addcc	c_12,t_1,c_12
+	bcs,a	%xcc,.+8
+	add	c_3,t_2,c_3
+	addcc	c_12,t_1,c_12
+	bcs,a	%xcc,.+8
+	add	c_3,t_2,c_3
+	lduw	ap(7),a_7
+	mulx	a_3,a_3,t_1	!=!sqr_add_c(a,3,c1,c2,c3);
+	addcc	c_12,t_1,t_1
+	bcs,a	%xcc,.+8
+	add	c_3,t_2,c_3
+	srlx	t_1,32,c_12
+	stuw	t_1,rp(6)	!r[6]=c1;
+	or	c_12,c_3,c_12
+
+	mulx	a_0,a_7,t_1	!sqr_add_c2(a,7,0,c2,c3,c1);
+	addcc	c_12,t_1,c_12
+	clr	c_3
+	bcs,a	%xcc,.+8
+	add	c_3,t_2,c_3
+	addcc	c_12,t_1,c_12
+	bcs,a	%xcc,.+8
+	add	c_3,t_2,c_3
+	mulx	a_1,a_6,t_1	!sqr_add_c2(a,6,1,c2,c3,c1);
+	addcc	c_12,t_1,c_12
+	bcs,a	%xcc,.+8
+	add	c_3,t_2,c_3
+	addcc	c_12,t_1,c_12
+	bcs,a	%xcc,.+8
+	add	c_3,t_2,c_3
+	mulx	a_2,a_5,t_1	!sqr_add_c2(a,5,2,c2,c3,c1);
+	addcc	c_12,t_1,c_12
+	bcs,a	%xcc,.+8
+	add	c_3,t_2,c_3
+	addcc	c_12,t_1,c_12
+	bcs,a	%xcc,.+8
+	add	c_3,t_2,c_3
+	mulx	a_3,a_4,t_1	!sqr_add_c2(a,4,3,c2,c3,c1);
+	addcc	c_12,t_1,c_12
+	bcs,a	%xcc,.+8
+	add	c_3,t_2,c_3
+	addcc	c_12,t_1,t_1
+	bcs,a	%xcc,.+8
+	add	c_3,t_2,c_3
+	srlx	t_1,32,c_12
+	stuw	t_1,rp(7)	!r[7]=c2;
+	or	c_12,c_3,c_12
+
+	mulx	a_7,a_1,t_1	!sqr_add_c2(a,7,1,c3,c1,c2);
+	addcc	c_12,t_1,c_12
+	clr	c_3
+	bcs,a	%xcc,.+8
+	add	c_3,t_2,c_3
+	addcc	c_12,t_1,c_12
+	bcs,a	%xcc,.+8
+	add	c_3,t_2,c_3
+	mulx	a_6,a_2,t_1	!sqr_add_c2(a,6,2,c3,c1,c2);
+	addcc	c_12,t_1,c_12
+	bcs,a	%xcc,.+8
+	add	c_3,t_2,c_3
+	addcc	c_12,t_1,c_12
+	bcs,a	%xcc,.+8
+	add	c_3,t_2,c_3
+	mulx	a_5,a_3,t_1	!sqr_add_c2(a,5,3,c3,c1,c2);
+	addcc	c_12,t_1,c_12
+	bcs,a	%xcc,.+8
+	add	c_3,t_2,c_3
+	addcc	c_12,t_1,c_12
+	bcs,a	%xcc,.+8
+	add	c_3,t_2,c_3
+	mulx	a_4,a_4,t_1	!sqr_add_c(a,4,c3,c1,c2);
+	addcc	c_12,t_1,t_1
+	bcs,a	%xcc,.+8
+	add	c_3,t_2,c_3
+	srlx	t_1,32,c_12
+	stuw	t_1,rp(8)	!r[8]=c3;
+	or	c_12,c_3,c_12
+
+	mulx	a_2,a_7,t_1	!sqr_add_c2(a,7,2,c1,c2,c3);
+	addcc	c_12,t_1,c_12
+	clr	c_3
+	bcs,a	%xcc,.+8
+	add	c_3,t_2,c_3
+	addcc	c_12,t_1,c_12
+	bcs,a	%xcc,.+8
+	add	c_3,t_2,c_3
+	mulx	a_3,a_6,t_1	!sqr_add_c2(a,6,3,c1,c2,c3);
+	addcc	c_12,t_1,c_12
+	bcs,a	%xcc,.+8
+	add	c_3,t_2,c_3
+	addcc	c_12,t_1,c_12
+	bcs,a	%xcc,.+8
+	add	c_3,t_2,c_3
+	mulx	a_4,a_5,t_1	!sqr_add_c2(a,5,4,c1,c2,c3);
+	addcc	c_12,t_1,c_12
+	bcs,a	%xcc,.+8
+	add	c_3,t_2,c_3
+	addcc	c_12,t_1,t_1
+	bcs,a	%xcc,.+8
+	add	c_3,t_2,c_3
+	srlx	t_1,32,c_12
+	stuw	t_1,rp(9)	!r[9]=c1;
+	or	c_12,c_3,c_12
+
+	mulx	a_7,a_3,t_1	!sqr_add_c2(a,7,3,c2,c3,c1);
+	addcc	c_12,t_1,c_12
+	clr	c_3
+	bcs,a	%xcc,.+8
+	add	c_3,t_2,c_3
+	addcc	c_12,t_1,c_12
+	bcs,a	%xcc,.+8
+	add	c_3,t_2,c_3
+	mulx	a_6,a_4,t_1	!sqr_add_c2(a,6,4,c2,c3,c1);
+	addcc	c_12,t_1,c_12
+	bcs,a	%xcc,.+8
+	add	c_3,t_2,c_3
+	addcc	c_12,t_1,c_12
+	bcs,a	%xcc,.+8
+	add	c_3,t_2,c_3
+	mulx	a_5,a_5,t_1	!sqr_add_c(a,5,c2,c3,c1);
+	addcc	c_12,t_1,t_1
+	bcs,a	%xcc,.+8
+	add	c_3,t_2,c_3
+	srlx	t_1,32,c_12
+	stuw	t_1,rp(10)	!r[10]=c2;
+	or	c_12,c_3,c_12
+
+	mulx	a_4,a_7,t_1	!sqr_add_c2(a,7,4,c3,c1,c2);
+	addcc	c_12,t_1,c_12
+	clr	c_3
+	bcs,a	%xcc,.+8
+	add	c_3,t_2,c_3
+	addcc	c_12,t_1,c_12
+	bcs,a	%xcc,.+8
+	add	c_3,t_2,c_3
+	mulx	a_5,a_6,t_1	!sqr_add_c2(a,6,5,c3,c1,c2);
+	addcc	c_12,t_1,c_12
+	bcs,a	%xcc,.+8
+	add	c_3,t_2,c_3
+	addcc	c_12,t_1,t_1
+	bcs,a	%xcc,.+8
+	add	c_3,t_2,c_3
+	srlx	t_1,32,c_12
+	stuw	t_1,rp(11)	!r[11]=c3;
+	or	c_12,c_3,c_12
+
+	mulx	a_7,a_5,t_1	!sqr_add_c2(a,7,5,c1,c2,c3);
+	addcc	c_12,t_1,c_12
+	clr	c_3
+	bcs,a	%xcc,.+8
+	add	c_3,t_2,c_3
+	addcc	c_12,t_1,c_12
+	bcs,a	%xcc,.+8
+	add	c_3,t_2,c_3
+	mulx	a_6,a_6,t_1	!sqr_add_c(a,6,c1,c2,c3);
+	addcc	c_12,t_1,t_1
+	bcs,a	%xcc,.+8
+	add	c_3,t_2,c_3
+	srlx	t_1,32,c_12
+	stuw	t_1,rp(12)	!r[12]=c1;
+	or	c_12,c_3,c_12
+
+	mulx	a_6,a_7,t_1	!sqr_add_c2(a,7,6,c2,c3,c1);
+	addcc	c_12,t_1,c_12
+	clr	c_3
+	bcs,a	%xcc,.+8
+	add	c_3,t_2,c_3
+	addcc	c_12,t_1,t_1
+	bcs,a	%xcc,.+8
+	add	c_3,t_2,c_3
+	srlx	t_1,32,c_12
+	stuw	t_1,rp(13)	!r[13]=c2;
+	or	c_12,c_3,c_12
+
+	mulx	a_7,a_7,t_1	!sqr_add_c(a,7,c3,c1,c2);
+	addcc	c_12,t_1,t_1
+	srlx	t_1,32,c_12
+	stuw	t_1,rp(14)	!r[14]=c3;
+	stuw	c_12,rp(15)	!r[15]=c1;
+
+	ret
+	restore	%g0,%g0,%o0
+
+.type	bn_sqr_comba8,#function
+.size	bn_sqr_comba8,(.-bn_sqr_comba8)
+
+.align	32
+
+.global bn_sqr_comba4
+/*
+ * void bn_sqr_comba4(r,a)
+ * BN_ULONG *r,*a;
+ */
+bn_sqr_comba4:
+	save	%sp,FRAME_SIZE,%sp
+	mov	1,t_2
+	lduw	ap(0),a_0
+	sllx	t_2,32,t_2
+	lduw	ap(1),a_1
+	mulx	a_0,a_0,t_1	!sqr_add_c(a,0,c1,c2,c3);
+	srlx	t_1,32,c_12
+	stuw	t_1,rp(0)	!r[0]=c1;
+
+	lduw	ap(2),a_2
+	mulx	a_0,a_1,t_1	!sqr_add_c2(a,1,0,c2,c3,c1);
+	addcc	c_12,t_1,c_12
+	clr	c_3
+	bcs,a	%xcc,.+8
+	add	c_3,t_2,c_3
+	addcc	c_12,t_1,t_1
+	bcs,a	%xcc,.+8
+	add	c_3,t_2,c_3
+	srlx	t_1,32,c_12
+	stuw	t_1,rp(1)	!r[1]=c2;
+	or	c_12,c_3,c_12
+
+	mulx	a_2,a_0,t_1	!sqr_add_c2(a,2,0,c3,c1,c2);
+	addcc	c_12,t_1,c_12
+	clr	c_3
+	bcs,a	%xcc,.+8
+	add	c_3,t_2,c_3
+	addcc	c_12,t_1,c_12
+	bcs,a	%xcc,.+8
+	add	c_3,t_2,c_3
+	lduw	ap(3),a_3
+	mulx	a_1,a_1,t_1	!sqr_add_c(a,1,c3,c1,c2);
+	addcc	c_12,t_1,t_1
+	bcs,a	%xcc,.+8
+	add	c_3,t_2,c_3
+	srlx	t_1,32,c_12
+	stuw	t_1,rp(2)	!r[2]=c3;
+	or	c_12,c_3,c_12
+
+	mulx	a_0,a_3,t_1	!sqr_add_c2(a,3,0,c1,c2,c3);
+	addcc	c_12,t_1,c_12
+	clr	c_3
+	bcs,a	%xcc,.+8
+	add	c_3,t_2,c_3
+	addcc	c_12,t_1,c_12
+	bcs,a	%xcc,.+8
+	add	c_3,t_2,c_3
+	mulx	a_1,a_2,t_1	!sqr_add_c2(a,2,1,c1,c2,c3);
+	addcc	c_12,t_1,c_12
+	bcs,a	%xcc,.+8
+	add	c_3,t_2,c_3
+	addcc	c_12,t_1,t_1
+	bcs,a	%xcc,.+8
+	add	c_3,t_2,c_3
+	srlx	t_1,32,c_12
+	stuw	t_1,rp(3)	!r[3]=c1;
+	or	c_12,c_3,c_12
+
+	mulx	a_3,a_1,t_1	!sqr_add_c2(a,3,1,c2,c3,c1);
+	addcc	c_12,t_1,c_12
+	clr	c_3
+	bcs,a	%xcc,.+8
+	add	c_3,t_2,c_3
+	addcc	c_12,t_1,c_12
+	bcs,a	%xcc,.+8
+	add	c_3,t_2,c_3
+	mulx	a_2,a_2,t_1	!sqr_add_c(a,2,c2,c3,c1);
+	addcc	c_12,t_1,t_1
+	bcs,a	%xcc,.+8
+	add	c_3,t_2,c_3
+	srlx	t_1,32,c_12
+	stuw	t_1,rp(4)	!r[4]=c2;
+	or	c_12,c_3,c_12
+
+	mulx	a_2,a_3,t_1	!sqr_add_c2(a,3,2,c3,c1,c2);
+	addcc	c_12,t_1,c_12
+	clr	c_3
+	bcs,a	%xcc,.+8
+	add	c_3,t_2,c_3
+	addcc	c_12,t_1,t_1
+	bcs,a	%xcc,.+8
+	add	c_3,t_2,c_3
+	srlx	t_1,32,c_12
+	stuw	t_1,rp(5)	!r[5]=c3;
+	or	c_12,c_3,c_12
+
+	mulx	a_3,a_3,t_1	!sqr_add_c(a,3,c1,c2,c3);
+	addcc	c_12,t_1,t_1
+	srlx	t_1,32,c_12
+	stuw	t_1,rp(6)	!r[6]=c1;
+	stuw	c_12,rp(7)	!r[7]=c2;
+	
+	ret
+	restore	%g0,%g0,%o0
+
+.type	bn_sqr_comba4,#function
+.size	bn_sqr_comba4,(.-bn_sqr_comba4)
+
+.align	32
diff --git a/crypto/bn/asm/sparcv9-gf2m.pl b/crypto/bn/asm/sparcv9-gf2m.pl
new file mode 100644
index 000000000..dcf11a87a
--- /dev/null
+++ b/crypto/bn/asm/sparcv9-gf2m.pl
@@ -0,0 +1,200 @@
+#! /usr/bin/env perl
+# Copyright 2012-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+#
+# ====================================================================
+# Written by Andy Polyakov  for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+#
+# October 2012
+#
+# The module implements bn_GF2m_mul_2x2 polynomial multiplication used
+# in bn_gf2m.c. It's kind of low-hanging mechanical port from C for
+# the time being... Except that it has two code paths: one suitable
+# for all SPARCv9 processors and one for VIS3-capable ones. Former
+# delivers ~25-45% more, more for longer keys, heaviest DH and DSA
+# verify operations on venerable UltraSPARC II. On T4 VIS3 code is
+# ~100-230% faster than gcc-generated code and ~35-90% faster than
+# the pure SPARCv9 code path.
+
+$output = pop;
+open STDOUT,">$output";
+
+$locals=16*8;
+
+$tab="%l0";
+
+@T=("%g2","%g3");
+@i=("%g4","%g5");
+
+($a1,$a2,$a4,$a8,$a12,$a48)=map("%o$_",(0..5));
+($lo,$hi,$b)=("%g1",$a8,"%o7"); $a=$lo;
+
+$code.=<<___;
+#include 
+
+#ifdef __arch64__
+.register	%g2,#scratch
+.register	%g3,#scratch
+#endif
+
+#ifdef __PIC__
+SPARC_PIC_THUNK(%g1)
+#endif
+
+.globl	bn_GF2m_mul_2x2
+.align	16
+bn_GF2m_mul_2x2:
+        SPARC_LOAD_ADDRESS_LEAF(OPENSSL_sparcv9cap_P,%g1,%g5)
+        ld	[%g1+0],%g1             	! OPENSSL_sparcv9cap_P[0]
+
+        andcc	%g1, SPARCV9_VIS3, %g0
+        bz,pn	%icc,.Lsoftware
+        nop
+
+	sllx	%o1, 32, %o1
+	sllx	%o3, 32, %o3
+	or	%o2, %o1, %o1
+	or	%o4, %o3, %o3
+	.word	0x95b262ab			! xmulx   %o1, %o3, %o2
+	.word	0x99b262cb			! xmulxhi %o1, %o3, %o4
+	srlx	%o2, 32, %o1			! 13 cycles later
+	st	%o2, [%o0+0]
+	st	%o1, [%o0+4]
+	srlx	%o4, 32, %o3
+	st	%o4, [%o0+8]
+	retl
+	st	%o3, [%o0+12]
+
+.align	16
+.Lsoftware:
+	save	%sp,-STACK_FRAME-$locals,%sp
+
+	sllx	%i1,32,$a
+	mov	-1,$a12
+	sllx	%i3,32,$b
+	or	%i2,$a,$a
+	srlx	$a12,1,$a48			! 0x7fff...
+	or	%i4,$b,$b
+	srlx	$a12,2,$a12			! 0x3fff...
+	add	%sp,STACK_BIAS+STACK_FRAME,$tab
+
+	sllx	$a,2,$a4
+	mov	$a,$a1
+	sllx	$a,1,$a2
+
+	srax	$a4,63,@i[1]			! broadcast 61st bit
+	and	$a48,$a4,$a4			! (a<<2)&0x7fff...
+	srlx	$a48,2,$a48
+	srax	$a2,63,@i[0]			! broadcast 62nd bit
+	and	$a12,$a2,$a2			! (a<<1)&0x3fff...
+	srax	$a1,63,$lo			! broadcast 63rd bit
+	and	$a48,$a1,$a1			! (a<<0)&0x1fff...
+
+	sllx	$a1,3,$a8
+	and	$b,$lo,$lo
+	and	$b,@i[0],@i[0]
+	and	$b,@i[1],@i[1]
+
+	stx	%g0,[$tab+0*8]			! tab[0]=0
+	xor	$a1,$a2,$a12
+	stx	$a1,[$tab+1*8]			! tab[1]=a1
+	stx	$a2,[$tab+2*8]			! tab[2]=a2
+	 xor	$a4,$a8,$a48
+	stx	$a12,[$tab+3*8]			! tab[3]=a1^a2
+	 xor	$a4,$a1,$a1
+
+	stx	$a4,[$tab+4*8]			! tab[4]=a4
+	xor	$a4,$a2,$a2
+	stx	$a1,[$tab+5*8]			! tab[5]=a1^a4
+	xor	$a4,$a12,$a12
+	stx	$a2,[$tab+6*8]			! tab[6]=a2^a4
+	 xor	$a48,$a1,$a1
+	stx	$a12,[$tab+7*8]			! tab[7]=a1^a2^a4
+	 xor	$a48,$a2,$a2
+
+	stx	$a8,[$tab+8*8]			! tab[8]=a8
+	xor	$a48,$a12,$a12
+	stx	$a1,[$tab+9*8]			! tab[9]=a1^a8
+	 xor	$a4,$a1,$a1
+	stx	$a2,[$tab+10*8]			! tab[10]=a2^a8
+	 xor	$a4,$a2,$a2
+	stx	$a12,[$tab+11*8]		! tab[11]=a1^a2^a8
+
+	xor	$a4,$a12,$a12
+	stx	$a48,[$tab+12*8]		! tab[12]=a4^a8
+	 srlx	$lo,1,$hi
+	stx	$a1,[$tab+13*8]			! tab[13]=a1^a4^a8
+	 sllx	$lo,63,$lo
+	stx	$a2,[$tab+14*8]			! tab[14]=a2^a4^a8
+	 srlx	@i[0],2,@T[0]
+	stx	$a12,[$tab+15*8]		! tab[15]=a1^a2^a4^a8
+
+	sllx	@i[0],62,$a1
+	 sllx	$b,3,@i[0]
+	srlx	@i[1],3,@T[1]
+	 and	@i[0],`0xf<<3`,@i[0]
+	sllx	@i[1],61,$a2
+	 ldx	[$tab+@i[0]],@i[0]
+	 srlx	$b,4-3,@i[1]
+	xor	@T[0],$hi,$hi
+	 and	@i[1],`0xf<<3`,@i[1]
+	xor	$a1,$lo,$lo
+	 ldx	[$tab+@i[1]],@i[1]
+	xor	@T[1],$hi,$hi
+
+	xor	@i[0],$lo,$lo
+	srlx	$b,8-3,@i[0]
+	 xor	$a2,$lo,$lo
+	and	@i[0],`0xf<<3`,@i[0]
+___
+for($n=1;$n<14;$n++) {
+$code.=<<___;
+	sllx	@i[1],`$n*4`,@T[0]
+	ldx	[$tab+@i[0]],@i[0]
+	srlx	@i[1],`64-$n*4`,@T[1]
+	xor	@T[0],$lo,$lo
+	srlx	$b,`($n+2)*4`-3,@i[1]
+	xor	@T[1],$hi,$hi
+	and	@i[1],`0xf<<3`,@i[1]
+___
+	push(@i,shift(@i)); push(@T,shift(@T));
+}
+$code.=<<___;
+	sllx	@i[1],`$n*4`,@T[0]
+	ldx	[$tab+@i[0]],@i[0]
+	srlx	@i[1],`64-$n*4`,@T[1]
+	xor	@T[0],$lo,$lo
+
+	sllx	@i[0],`($n+1)*4`,@T[0]
+	 xor	@T[1],$hi,$hi
+	srlx	@i[0],`64-($n+1)*4`,@T[1]
+	xor	@T[0],$lo,$lo
+	xor	@T[1],$hi,$hi
+
+	srlx	$lo,32,%i1
+	st	$lo,[%i0+0]
+	st	%i1,[%i0+4]
+	srlx	$hi,32,%i2
+	st	$hi,[%i0+8]
+	st	%i2,[%i0+12]
+
+	ret
+	restore
+.type	bn_GF2m_mul_2x2,#function
+.size	bn_GF2m_mul_2x2,.-bn_GF2m_mul_2x2
+.asciz	"GF(2^m) Multiplication for SPARCv9, CRYPTOGAMS by "
+.align	4
+___
+
+$code =~ s/\`([^\`]*)\`/eval($1)/gem;
+print $code;
+close STDOUT;
diff --git a/crypto/bn/asm/sparcv9-mont.pl b/crypto/bn/asm/sparcv9-mont.pl
new file mode 100644
index 000000000..c36ce3680
--- /dev/null
+++ b/crypto/bn/asm/sparcv9-mont.pl
@@ -0,0 +1,616 @@
+#! /usr/bin/env perl
+# Copyright 2005-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+
+# ====================================================================
+# Written by Andy Polyakov  for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+
+# December 2005
+#
+# Pure SPARCv9/8+ and IALU-only bn_mul_mont implementation. The reasons
+# for undertaken effort are multiple. First of all, UltraSPARC is not
+# the whole SPARCv9 universe and other VIS-free implementations deserve
+# optimized code as much. Secondly, newly introduced UltraSPARC T1,
+# a.k.a. Niagara, has shared FPU and concurrent FPU-intensive paths,
+# such as sparcv9a-mont, will simply sink it. Yes, T1 is equipped with
+# several integrated RSA/DSA accelerator circuits accessible through
+# kernel driver [only(*)], but having decent user-land software
+# implementation is important too. Finally, reasons like desire to
+# experiment with dedicated squaring procedure. Yes, this module
+# implements one, because it was easiest to draft it in SPARCv9
+# instructions...
+
+# (*)	Engine accessing the driver in question is on my TODO list.
+#	For reference, acceleator is estimated to give 6 to 10 times
+#	improvement on single-threaded RSA sign. It should be noted
+#	that 6-10x improvement coefficient does not actually mean
+#	something extraordinary in terms of absolute [single-threaded]
+#	performance, as SPARCv9 instruction set is by all means least
+#	suitable for high performance crypto among other 64 bit
+#	platforms. 6-10x factor simply places T1 in same performance
+#	domain as say AMD64 and IA-64. Improvement of RSA verify don't
+#	appear impressive at all, but it's the sign operation which is
+#	far more critical/interesting.
+
+# You might notice that inner loops are modulo-scheduled:-) This has
+# essentially negligible impact on UltraSPARC performance, it's
+# Fujitsu SPARC64 V users who should notice and hopefully appreciate
+# the advantage... Currently this module surpasses sparcv9a-mont.pl
+# by ~20% on UltraSPARC-III and later cores, but recall that sparcv9a
+# module still have hidden potential [see TODO list there], which is
+# estimated to be larger than 20%...
+
+$output = pop;
+open STDOUT,">$output";
+
+# int bn_mul_mont(
+$rp="%i0";	# BN_ULONG *rp,
+$ap="%i1";	# const BN_ULONG *ap,
+$bp="%i2";	# const BN_ULONG *bp,
+$np="%i3";	# const BN_ULONG *np,
+$n0="%i4";	# const BN_ULONG *n0,
+$num="%i5";	# int num);
+
+$frame="STACK_FRAME";
+$bias="STACK_BIAS";
+
+$car0="%o0";
+$car1="%o1";
+$car2="%o2";	# 1 bit
+$acc0="%o3";
+$acc1="%o4";
+$mask="%g1";	# 32 bits, what a waste...
+$tmp0="%g4";
+$tmp1="%g5";
+
+$i="%l0";
+$j="%l1";
+$mul0="%l2";
+$mul1="%l3";
+$tp="%l4";
+$apj="%l5";
+$npj="%l6";
+$tpj="%l7";
+
+$fname="bn_mul_mont_int";
+
+$code=<<___;
+#include "sparc_arch.h"
+
+.section	".text",#alloc,#execinstr
+
+.global	$fname
+.align	32
+$fname:
+	cmp	%o5,4			! 128 bits minimum
+	bge,pt	%icc,.Lenter
+	sethi	%hi(0xffffffff),$mask
+	retl
+	clr	%o0
+.align	32
+.Lenter:
+	save	%sp,-$frame,%sp
+	sll	$num,2,$num		! num*=4
+	or	$mask,%lo(0xffffffff),$mask
+	ld	[$n0],$n0
+	cmp	$ap,$bp
+	and	$num,$mask,$num
+	ld	[$bp],$mul0		! bp[0]
+	nop
+
+	add	%sp,$bias,%o7		! real top of stack
+	ld	[$ap],$car0		! ap[0] ! redundant in squaring context
+	sub	%o7,$num,%o7
+	ld	[$ap+4],$apj		! ap[1]
+	and	%o7,-1024,%o7
+	ld	[$np],$car1		! np[0]
+	sub	%o7,$bias,%sp		! alloca
+	ld	[$np+4],$npj		! np[1]
+	be,pt	SIZE_T_CC,.Lbn_sqr_mont
+	mov	12,$j
+
+	mulx	$car0,$mul0,$car0	! ap[0]*bp[0]
+	mulx	$apj,$mul0,$tmp0	!prologue! ap[1]*bp[0]
+	and	$car0,$mask,$acc0
+	add	%sp,$bias+$frame,$tp
+	ld	[$ap+8],$apj		!prologue!
+
+	mulx	$n0,$acc0,$mul1		! "t[0]"*n0
+	and	$mul1,$mask,$mul1
+
+	mulx	$car1,$mul1,$car1	! np[0]*"t[0]"*n0
+	mulx	$npj,$mul1,$acc1	!prologue! np[1]*"t[0]"*n0
+	srlx	$car0,32,$car0
+	add	$acc0,$car1,$car1
+	ld	[$np+8],$npj		!prologue!
+	srlx	$car1,32,$car1
+	mov	$tmp0,$acc0		!prologue!
+
+.L1st:
+	mulx	$apj,$mul0,$tmp0
+	mulx	$npj,$mul1,$tmp1
+	add	$acc0,$car0,$car0
+	ld	[$ap+$j],$apj		! ap[j]
+	and	$car0,$mask,$acc0
+	add	$acc1,$car1,$car1
+	ld	[$np+$j],$npj		! np[j]
+	srlx	$car0,32,$car0
+	add	$acc0,$car1,$car1
+	add	$j,4,$j			! j++
+	mov	$tmp0,$acc0
+	st	$car1,[$tp]
+	cmp	$j,$num
+	mov	$tmp1,$acc1
+	srlx	$car1,32,$car1
+	bl	%icc,.L1st
+	add	$tp,4,$tp		! tp++
+!.L1st
+
+	mulx	$apj,$mul0,$tmp0	!epilogue!
+	mulx	$npj,$mul1,$tmp1
+	add	$acc0,$car0,$car0
+	and	$car0,$mask,$acc0
+	add	$acc1,$car1,$car1
+	srlx	$car0,32,$car0
+	add	$acc0,$car1,$car1
+	st	$car1,[$tp]
+	srlx	$car1,32,$car1
+
+	add	$tmp0,$car0,$car0
+	and	$car0,$mask,$acc0
+	add	$tmp1,$car1,$car1
+	srlx	$car0,32,$car0
+	add	$acc0,$car1,$car1
+	st	$car1,[$tp+4]
+	srlx	$car1,32,$car1
+
+	add	$car0,$car1,$car1
+	st	$car1,[$tp+8]
+	srlx	$car1,32,$car2
+
+	mov	4,$i			! i++
+	ld	[$bp+4],$mul0		! bp[1]
+.Louter:
+	add	%sp,$bias+$frame,$tp
+	ld	[$ap],$car0		! ap[0]
+	ld	[$ap+4],$apj		! ap[1]
+	ld	[$np],$car1		! np[0]
+	ld	[$np+4],$npj		! np[1]
+	ld	[$tp],$tmp1		! tp[0]
+	ld	[$tp+4],$tpj		! tp[1]
+	mov	12,$j
+
+	mulx	$car0,$mul0,$car0
+	mulx	$apj,$mul0,$tmp0	!prologue!
+	add	$tmp1,$car0,$car0
+	ld	[$ap+8],$apj		!prologue!
+	and	$car0,$mask,$acc0
+
+	mulx	$n0,$acc0,$mul1
+	and	$mul1,$mask,$mul1
+
+	mulx	$car1,$mul1,$car1
+	mulx	$npj,$mul1,$acc1	!prologue!
+	srlx	$car0,32,$car0
+	add	$acc0,$car1,$car1
+	ld	[$np+8],$npj		!prologue!
+	srlx	$car1,32,$car1
+	mov	$tmp0,$acc0		!prologue!
+
+.Linner:
+	mulx	$apj,$mul0,$tmp0
+	mulx	$npj,$mul1,$tmp1
+	add	$tpj,$car0,$car0
+	ld	[$ap+$j],$apj		! ap[j]
+	add	$acc0,$car0,$car0
+	add	$acc1,$car1,$car1
+	ld	[$np+$j],$npj		! np[j]
+	and	$car0,$mask,$acc0
+	ld	[$tp+8],$tpj		! tp[j]
+	srlx	$car0,32,$car0
+	add	$acc0,$car1,$car1
+	add	$j,4,$j			! j++
+	mov	$tmp0,$acc0
+	st	$car1,[$tp]		! tp[j-1]
+	srlx	$car1,32,$car1
+	mov	$tmp1,$acc1
+	cmp	$j,$num
+	bl	%icc,.Linner
+	add	$tp,4,$tp		! tp++
+!.Linner
+
+	mulx	$apj,$mul0,$tmp0	!epilogue!
+	mulx	$npj,$mul1,$tmp1
+	add	$tpj,$car0,$car0
+	add	$acc0,$car0,$car0
+	ld	[$tp+8],$tpj		! tp[j]
+	and	$car0,$mask,$acc0
+	add	$acc1,$car1,$car1
+	srlx	$car0,32,$car0
+	add	$acc0,$car1,$car1
+	st	$car1,[$tp]		! tp[j-1]
+	srlx	$car1,32,$car1
+
+	add	$tpj,$car0,$car0
+	add	$tmp0,$car0,$car0
+	and	$car0,$mask,$acc0
+	add	$tmp1,$car1,$car1
+	add	$acc0,$car1,$car1
+	st	$car1,[$tp+4]		! tp[j-1]
+	srlx	$car0,32,$car0
+	add	$i,4,$i			! i++
+	srlx	$car1,32,$car1
+
+	add	$car0,$car1,$car1
+	cmp	$i,$num
+	add	$car2,$car1,$car1
+	st	$car1,[$tp+8]
+
+	srlx	$car1,32,$car2
+	bl,a	%icc,.Louter
+	ld	[$bp+$i],$mul0		! bp[i]
+!.Louter
+
+	add	$tp,12,$tp
+
+.Ltail:
+	add	$np,$num,$np
+	add	$rp,$num,$rp
+	mov	$tp,$ap
+	sub	%g0,$num,%o7		! k=-num
+	ba	.Lsub
+	subcc	%g0,%g0,%g0		! clear %icc.c
+.align	16
+.Lsub:
+	ld	[$tp+%o7],%o0
+	ld	[$np+%o7],%o1
+	subccc	%o0,%o1,%o1		! tp[j]-np[j]
+	add	$rp,%o7,$i
+	add	%o7,4,%o7
+	brnz	%o7,.Lsub
+	st	%o1,[$i]
+	subc	$car2,0,$car2		! handle upmost overflow bit
+	and	$tp,$car2,$ap
+	andn	$rp,$car2,$np
+	or	$ap,$np,$ap
+	sub	%g0,$num,%o7
+
+.Lcopy:
+	ld	[$ap+%o7],%o0		! copy or in-place refresh
+	st	%g0,[$tp+%o7]		! zap tp
+	st	%o0,[$rp+%o7]
+	add	%o7,4,%o7
+	brnz	%o7,.Lcopy
+	nop
+	mov	1,%i0
+	ret
+	restore
+___
+
+########
+######## .Lbn_sqr_mont gives up to 20% *overall* improvement over
+######## code without following dedicated squaring procedure.
+########
+$sbit="%i2";		# re-use $bp!
+
+$code.=<<___;
+.align	32
+.Lbn_sqr_mont:
+	mulx	$mul0,$mul0,$car0		! ap[0]*ap[0]
+	mulx	$apj,$mul0,$tmp0		!prologue!
+	and	$car0,$mask,$acc0
+	add	%sp,$bias+$frame,$tp
+	ld	[$ap+8],$apj			!prologue!
+
+	mulx	$n0,$acc0,$mul1			! "t[0]"*n0
+	srlx	$car0,32,$car0
+	and	$mul1,$mask,$mul1
+
+	mulx	$car1,$mul1,$car1		! np[0]*"t[0]"*n0
+	mulx	$npj,$mul1,$acc1		!prologue!
+	and	$car0,1,$sbit
+	ld	[$np+8],$npj			!prologue!
+	srlx	$car0,1,$car0
+	add	$acc0,$car1,$car1
+	srlx	$car1,32,$car1
+	mov	$tmp0,$acc0			!prologue!
+
+.Lsqr_1st:
+	mulx	$apj,$mul0,$tmp0
+	mulx	$npj,$mul1,$tmp1
+	add	$acc0,$car0,$car0		! ap[j]*a0+c0
+	add	$acc1,$car1,$car1
+	ld	[$ap+$j],$apj			! ap[j]
+	and	$car0,$mask,$acc0
+	ld	[$np+$j],$npj			! np[j]
+	srlx	$car0,32,$car0
+	add	$acc0,$acc0,$acc0
+	or	$sbit,$acc0,$acc0
+	mov	$tmp1,$acc1
+	srlx	$acc0,32,$sbit
+	add	$j,4,$j				! j++
+	and	$acc0,$mask,$acc0
+	cmp	$j,$num
+	add	$acc0,$car1,$car1
+	st	$car1,[$tp]
+	mov	$tmp0,$acc0
+	srlx	$car1,32,$car1
+	bl	%icc,.Lsqr_1st
+	add	$tp,4,$tp			! tp++
+!.Lsqr_1st
+
+	mulx	$apj,$mul0,$tmp0		! epilogue
+	mulx	$npj,$mul1,$tmp1
+	add	$acc0,$car0,$car0		! ap[j]*a0+c0
+	add	$acc1,$car1,$car1
+	and	$car0,$mask,$acc0
+	srlx	$car0,32,$car0
+	add	$acc0,$acc0,$acc0
+	or	$sbit,$acc0,$acc0
+	srlx	$acc0,32,$sbit
+	and	$acc0,$mask,$acc0
+	add	$acc0,$car1,$car1
+	st	$car1,[$tp]
+	srlx	$car1,32,$car1
+
+	add	$tmp0,$car0,$car0		! ap[j]*a0+c0
+	add	$tmp1,$car1,$car1
+	and	$car0,$mask,$acc0
+	srlx	$car0,32,$car0
+	add	$acc0,$acc0,$acc0
+	or	$sbit,$acc0,$acc0
+	srlx	$acc0,32,$sbit
+	and	$acc0,$mask,$acc0
+	add	$acc0,$car1,$car1
+	st	$car1,[$tp+4]
+	srlx	$car1,32,$car1
+
+	add	$car0,$car0,$car0
+	or	$sbit,$car0,$car0
+	add	$car0,$car1,$car1
+	st	$car1,[$tp+8]
+	srlx	$car1,32,$car2
+
+	ld	[%sp+$bias+$frame],$tmp0	! tp[0]
+	ld	[%sp+$bias+$frame+4],$tmp1	! tp[1]
+	ld	[%sp+$bias+$frame+8],$tpj	! tp[2]
+	ld	[$ap+4],$mul0			! ap[1]
+	ld	[$ap+8],$apj			! ap[2]
+	ld	[$np],$car1			! np[0]
+	ld	[$np+4],$npj			! np[1]
+	mulx	$n0,$tmp0,$mul1
+
+	mulx	$mul0,$mul0,$car0
+	and	$mul1,$mask,$mul1
+
+	mulx	$car1,$mul1,$car1
+	mulx	$npj,$mul1,$acc1
+	add	$tmp0,$car1,$car1
+	and	$car0,$mask,$acc0
+	ld	[$np+8],$npj			! np[2]
+	srlx	$car1,32,$car1
+	add	$tmp1,$car1,$car1
+	srlx	$car0,32,$car0
+	add	$acc0,$car1,$car1
+	and	$car0,1,$sbit
+	add	$acc1,$car1,$car1
+	srlx	$car0,1,$car0
+	mov	12,$j
+	st	$car1,[%sp+$bias+$frame]	! tp[0]=
+	srlx	$car1,32,$car1
+	add	%sp,$bias+$frame+4,$tp
+
+.Lsqr_2nd:
+	mulx	$apj,$mul0,$acc0
+	mulx	$npj,$mul1,$acc1
+	add	$acc0,$car0,$car0
+	add	$tpj,$car1,$car1
+	ld	[$ap+$j],$apj			! ap[j]
+	and	$car0,$mask,$acc0
+	ld	[$np+$j],$npj			! np[j]
+	srlx	$car0,32,$car0
+	add	$acc1,$car1,$car1
+	ld	[$tp+8],$tpj			! tp[j]
+	add	$acc0,$acc0,$acc0
+	add	$j,4,$j				! j++
+	or	$sbit,$acc0,$acc0
+	srlx	$acc0,32,$sbit
+	and	$acc0,$mask,$acc0
+	cmp	$j,$num
+	add	$acc0,$car1,$car1
+	st	$car1,[$tp]			! tp[j-1]
+	srlx	$car1,32,$car1
+	bl	%icc,.Lsqr_2nd
+	add	$tp,4,$tp			! tp++
+!.Lsqr_2nd
+
+	mulx	$apj,$mul0,$acc0
+	mulx	$npj,$mul1,$acc1
+	add	$acc0,$car0,$car0
+	add	$tpj,$car1,$car1
+	and	$car0,$mask,$acc0
+	srlx	$car0,32,$car0
+	add	$acc1,$car1,$car1
+	add	$acc0,$acc0,$acc0
+	or	$sbit,$acc0,$acc0
+	srlx	$acc0,32,$sbit
+	and	$acc0,$mask,$acc0
+	add	$acc0,$car1,$car1
+	st	$car1,[$tp]			! tp[j-1]
+	srlx	$car1,32,$car1
+
+	add	$car0,$car0,$car0
+	or	$sbit,$car0,$car0
+	add	$car0,$car1,$car1
+	add	$car2,$car1,$car1
+	st	$car1,[$tp+4]
+	srlx	$car1,32,$car2
+
+	ld	[%sp+$bias+$frame],$tmp1	! tp[0]
+	ld	[%sp+$bias+$frame+4],$tpj	! tp[1]
+	ld	[$ap+8],$mul0			! ap[2]
+	ld	[$np],$car1			! np[0]
+	ld	[$np+4],$npj			! np[1]
+	mulx	$n0,$tmp1,$mul1
+	and	$mul1,$mask,$mul1
+	mov	8,$i
+
+	mulx	$mul0,$mul0,$car0
+	mulx	$car1,$mul1,$car1
+	and	$car0,$mask,$acc0
+	add	$tmp1,$car1,$car1
+	srlx	$car0,32,$car0
+	add	%sp,$bias+$frame,$tp
+	srlx	$car1,32,$car1
+	and	$car0,1,$sbit
+	srlx	$car0,1,$car0
+	mov	4,$j
+
+.Lsqr_outer:
+.Lsqr_inner1:
+	mulx	$npj,$mul1,$acc1
+	add	$tpj,$car1,$car1
+	add	$j,4,$j
+	ld	[$tp+8],$tpj
+	cmp	$j,$i
+	add	$acc1,$car1,$car1
+	ld	[$np+$j],$npj
+	st	$car1,[$tp]
+	srlx	$car1,32,$car1
+	bl	%icc,.Lsqr_inner1
+	add	$tp,4,$tp
+!.Lsqr_inner1
+
+	add	$j,4,$j
+	ld	[$ap+$j],$apj			! ap[j]
+	mulx	$npj,$mul1,$acc1
+	add	$tpj,$car1,$car1
+	ld	[$np+$j],$npj			! np[j]
+	add	$acc0,$car1,$car1
+	ld	[$tp+8],$tpj			! tp[j]
+	add	$acc1,$car1,$car1
+	st	$car1,[$tp]
+	srlx	$car1,32,$car1
+
+	add	$j,4,$j
+	cmp	$j,$num
+	be,pn	%icc,.Lsqr_no_inner2
+	add	$tp,4,$tp
+
+.Lsqr_inner2:
+	mulx	$apj,$mul0,$acc0
+	mulx	$npj,$mul1,$acc1
+	add	$tpj,$car1,$car1
+	add	$acc0,$car0,$car0
+	ld	[$ap+$j],$apj			! ap[j]
+	and	$car0,$mask,$acc0
+	ld	[$np+$j],$npj			! np[j]
+	srlx	$car0,32,$car0
+	add	$acc0,$acc0,$acc0
+	ld	[$tp+8],$tpj			! tp[j]
+	or	$sbit,$acc0,$acc0
+	add	$j,4,$j				! j++
+	srlx	$acc0,32,$sbit
+	and	$acc0,$mask,$acc0
+	cmp	$j,$num
+	add	$acc0,$car1,$car1
+	add	$acc1,$car1,$car1
+	st	$car1,[$tp]			! tp[j-1]
+	srlx	$car1,32,$car1
+	bl	%icc,.Lsqr_inner2
+	add	$tp,4,$tp			! tp++
+
+.Lsqr_no_inner2:
+	mulx	$apj,$mul0,$acc0
+	mulx	$npj,$mul1,$acc1
+	add	$tpj,$car1,$car1
+	add	$acc0,$car0,$car0
+	and	$car0,$mask,$acc0
+	srlx	$car0,32,$car0
+	add	$acc0,$acc0,$acc0
+	or	$sbit,$acc0,$acc0
+	srlx	$acc0,32,$sbit
+	and	$acc0,$mask,$acc0
+	add	$acc0,$car1,$car1
+	add	$acc1,$car1,$car1
+	st	$car1,[$tp]			! tp[j-1]
+	srlx	$car1,32,$car1
+
+	add	$car0,$car0,$car0
+	or	$sbit,$car0,$car0
+	add	$car0,$car1,$car1
+	add	$car2,$car1,$car1
+	st	$car1,[$tp+4]
+	srlx	$car1,32,$car2
+
+	add	$i,4,$i				! i++
+	ld	[%sp+$bias+$frame],$tmp1	! tp[0]
+	ld	[%sp+$bias+$frame+4],$tpj	! tp[1]
+	ld	[$ap+$i],$mul0			! ap[j]
+	ld	[$np],$car1			! np[0]
+	ld	[$np+4],$npj			! np[1]
+	mulx	$n0,$tmp1,$mul1
+	and	$mul1,$mask,$mul1
+	add	$i,4,$tmp0
+
+	mulx	$mul0,$mul0,$car0
+	mulx	$car1,$mul1,$car1
+	and	$car0,$mask,$acc0
+	add	$tmp1,$car1,$car1
+	srlx	$car0,32,$car0
+	add	%sp,$bias+$frame,$tp
+	srlx	$car1,32,$car1
+	and	$car0,1,$sbit
+	srlx	$car0,1,$car0
+
+	cmp	$tmp0,$num			! i"
+.align	32
+___
+$code =~ s/\`([^\`]*)\`/eval($1)/gem;
+print $code;
+close STDOUT;
diff --git a/crypto/bn/asm/sparcv9a-mont.pl b/crypto/bn/asm/sparcv9a-mont.pl
new file mode 100755
index 000000000..50b690653
--- /dev/null
+++ b/crypto/bn/asm/sparcv9a-mont.pl
@@ -0,0 +1,887 @@
+#! /usr/bin/env perl
+# Copyright 2005-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+
+# ====================================================================
+# Written by Andy Polyakov  for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+
+# October 2005
+#
+# "Teaser" Montgomery multiplication module for UltraSPARC. Why FPU?
+# Because unlike integer multiplier, which simply stalls whole CPU,
+# FPU is fully pipelined and can effectively emit 48 bit partial
+# product every cycle. Why not blended SPARC v9? One can argue that
+# making this module dependent on UltraSPARC VIS extension limits its
+# binary compatibility. Well yes, it does exclude SPARC64 prior-V(!)
+# implementations from compatibility matrix. But the rest, whole Sun
+# UltraSPARC family and brand new Fujitsu's SPARC64 V, all support
+# VIS extension instructions used in this module. This is considered
+# good enough to not care about HAL SPARC64 users [if any] who have
+# integer-only pure SPARCv9 module to "fall down" to.
+
+# USI&II cores currently exhibit uniform 2x improvement [over pre-
+# bn_mul_mont codebase] for all key lengths and benchmarks. On USIII
+# performance improves few percents for shorter keys and worsens few
+# percents for longer keys. This is because USIII integer multiplier
+# is >3x faster than USI&II one, which is harder to match [but see
+# TODO list below]. It should also be noted that SPARC64 V features
+# out-of-order execution, which *might* mean that integer multiplier
+# is pipelined, which in turn *might* be impossible to match... On
+# additional note, SPARC64 V implements FP Multiply-Add instruction,
+# which is perfectly usable in this context... In other words, as far
+# as Fujitsu SPARC64 V goes, talk to the author:-)
+
+# The implementation implies following "non-natural" limitations on
+# input arguments:
+# - num may not be less than 4;
+# - num has to be even;
+# Failure to meet either condition has no fatal effects, simply
+# doesn't give any performance gain.
+
+# TODO:
+# - modulo-schedule inner loop for better performance (on in-order
+#   execution core such as UltraSPARC this shall result in further
+#   noticeable(!) improvement);
+# - dedicated squaring procedure[?];
+
+######################################################################
+# November 2006
+#
+# Modulo-scheduled inner loops allow to interleave floating point and
+# integer instructions and minimize Read-After-Write penalties. This
+# results in *further* 20-50% performance improvement [depending on
+# key length, more for longer keys] on USI&II cores and 30-80% - on
+# USIII&IV.
+
+$output = pop;
+open STDOUT,">$output";
+
+$fname="bn_mul_mont_fpu";
+
+$frame="STACK_FRAME";
+$bias="STACK_BIAS";
+$locals=64;
+
+# In order to provide for 32-/64-bit ABI duality, I keep integers wider
+# than 32 bit in %g1-%g4 and %o0-%o5. %l0-%l7 and %i0-%i5 are used
+# exclusively for pointers, indexes and other small values...
+# int bn_mul_mont(
+$rp="%i0";	# BN_ULONG *rp,
+$ap="%i1";	# const BN_ULONG *ap,
+$bp="%i2";	# const BN_ULONG *bp,
+$np="%i3";	# const BN_ULONG *np,
+$n0="%i4";	# const BN_ULONG *n0,
+$num="%i5";	# int num);
+
+$tp="%l0";	# t[num]
+$ap_l="%l1";	# a[num],n[num] are smashed to 32-bit words and saved
+$ap_h="%l2";	# to these four vectors as double-precision FP values.
+$np_l="%l3";	# This way a bunch of fxtods are eliminated in second
+$np_h="%l4";	# loop and L1-cache aliasing is minimized...
+$i="%l5";
+$j="%l6";
+$mask="%l7";	# 16-bit mask, 0xffff
+
+$n0="%g4";	# reassigned(!) to "64-bit" register
+$carry="%i4";	# %i4 reused(!) for a carry bit
+
+# FP register naming chart
+#
+#     ..HILO
+#       dcba
+#   --------
+#        LOa
+#       LOb
+#      LOc
+#     LOd
+#      HIa
+#     HIb
+#    HIc
+#   HId
+#    ..a
+#   ..b
+$ba="%f0";    $bb="%f2";    $bc="%f4";    $bd="%f6";
+$na="%f8";    $nb="%f10";   $nc="%f12";   $nd="%f14";
+$alo="%f16";  $alo_="%f17"; $ahi="%f18";  $ahi_="%f19";
+$nlo="%f20";  $nlo_="%f21"; $nhi="%f22";  $nhi_="%f23";
+
+$dota="%f24"; $dotb="%f26";
+
+$aloa="%f32"; $alob="%f34"; $aloc="%f36"; $alod="%f38";
+$ahia="%f40"; $ahib="%f42"; $ahic="%f44"; $ahid="%f46";
+$nloa="%f48"; $nlob="%f50"; $nloc="%f52"; $nlod="%f54";
+$nhia="%f56"; $nhib="%f58"; $nhic="%f60"; $nhid="%f62";
+
+$ASI_FL16_P=0xD2;	# magic ASI value to engage 16-bit FP load
+
+$code=<<___;
+#include "sparc_arch.h"
+
+.section	".text",#alloc,#execinstr
+
+.global $fname
+.align  32
+$fname:
+	save	%sp,-$frame-$locals,%sp
+
+	cmp	$num,4
+	bl,a,pn %icc,.Lret
+	clr	%i0
+	andcc	$num,1,%g0		! $num has to be even...
+	bnz,a,pn %icc,.Lret
+	clr	%i0			! signal "unsupported input value"
+
+	srl	$num,1,$num
+	sethi	%hi(0xffff),$mask
+	ld	[%i4+0],$n0		! $n0 reassigned, remember?
+	or	$mask,%lo(0xffff),$mask
+	ld	[%i4+4],%o0
+	sllx	%o0,32,%o0
+	or	%o0,$n0,$n0		! $n0=n0[1].n0[0]
+
+	sll	$num,3,$num		! num*=8
+
+	add	%sp,$bias,%o0		! real top of stack
+	sll	$num,2,%o1
+	add	%o1,$num,%o1		! %o1=num*5
+	sub	%o0,%o1,%o0
+	and	%o0,-2048,%o0		! optimize TLB utilization
+	sub	%o0,$bias,%sp		! alloca(5*num*8)
+
+	rd	%asi,%o7		! save %asi
+	add	%sp,$bias+$frame+$locals,$tp
+	add	$tp,$num,$ap_l
+	add	$ap_l,$num,$ap_l	! [an]p_[lh] point at the vectors' ends !
+	add	$ap_l,$num,$ap_h
+	add	$ap_h,$num,$np_l
+	add	$np_l,$num,$np_h
+
+	wr	%g0,$ASI_FL16_P,%asi	! setup %asi for 16-bit FP loads
+
+	add	$rp,$num,$rp		! readjust input pointers to point
+	add	$ap,$num,$ap		! at the ends too...
+	add	$bp,$num,$bp
+	add	$np,$num,$np
+
+	stx	%o7,[%sp+$bias+$frame+48]	! save %asi
+
+	sub	%g0,$num,$i		! i=-num
+	sub	%g0,$num,$j		! j=-num
+
+	add	$ap,$j,%o3
+	add	$bp,$i,%o4
+
+	ld	[%o3+4],%g1		! bp[0]
+	ld	[%o3+0],%o0
+	ld	[%o4+4],%g5		! ap[0]
+	sllx	%g1,32,%g1
+	ld	[%o4+0],%o1
+	sllx	%g5,32,%g5
+	or	%g1,%o0,%o0
+	or	%g5,%o1,%o1
+
+	add	$np,$j,%o5
+
+	mulx	%o1,%o0,%o0		! ap[0]*bp[0]
+	mulx	$n0,%o0,%o0		! ap[0]*bp[0]*n0
+	stx	%o0,[%sp+$bias+$frame+0]
+
+	ld	[%o3+0],$alo_	! load a[j] as pair of 32-bit words
+	fzeros	$alo
+	ld	[%o3+4],$ahi_
+	fzeros	$ahi
+	ld	[%o5+0],$nlo_	! load n[j] as pair of 32-bit words
+	fzeros	$nlo
+	ld	[%o5+4],$nhi_
+	fzeros	$nhi
+
+	! transfer b[i] to FPU as 4x16-bit values
+	ldda	[%o4+2]%asi,$ba
+	fxtod	$alo,$alo
+	ldda	[%o4+0]%asi,$bb
+	fxtod	$ahi,$ahi
+	ldda	[%o4+6]%asi,$bc
+	fxtod	$nlo,$nlo
+	ldda	[%o4+4]%asi,$bd
+	fxtod	$nhi,$nhi
+
+	! transfer ap[0]*b[0]*n0 to FPU as 4x16-bit values
+	ldda	[%sp+$bias+$frame+6]%asi,$na
+	fxtod	$ba,$ba
+	ldda	[%sp+$bias+$frame+4]%asi,$nb
+	fxtod	$bb,$bb
+	ldda	[%sp+$bias+$frame+2]%asi,$nc
+	fxtod	$bc,$bc
+	ldda	[%sp+$bias+$frame+0]%asi,$nd
+	fxtod	$bd,$bd
+
+	std	$alo,[$ap_l+$j]		! save smashed ap[j] in double format
+	fxtod	$na,$na
+	std	$ahi,[$ap_h+$j]
+	fxtod	$nb,$nb
+	std	$nlo,[$np_l+$j]		! save smashed np[j] in double format
+	fxtod	$nc,$nc
+	std	$nhi,[$np_h+$j]
+	fxtod	$nd,$nd
+
+		fmuld	$alo,$ba,$aloa
+		fmuld	$nlo,$na,$nloa
+		fmuld	$alo,$bb,$alob
+		fmuld	$nlo,$nb,$nlob
+		fmuld	$alo,$bc,$aloc
+	faddd	$aloa,$nloa,$nloa
+		fmuld	$nlo,$nc,$nloc
+		fmuld	$alo,$bd,$alod
+	faddd	$alob,$nlob,$nlob
+		fmuld	$nlo,$nd,$nlod
+		fmuld	$ahi,$ba,$ahia
+	faddd	$aloc,$nloc,$nloc
+		fmuld	$nhi,$na,$nhia
+		fmuld	$ahi,$bb,$ahib
+	faddd	$alod,$nlod,$nlod
+		fmuld	$nhi,$nb,$nhib
+		fmuld	$ahi,$bc,$ahic
+	faddd	$ahia,$nhia,$nhia
+		fmuld	$nhi,$nc,$nhic
+		fmuld	$ahi,$bd,$ahid
+	faddd	$ahib,$nhib,$nhib
+		fmuld	$nhi,$nd,$nhid
+
+	faddd	$ahic,$nhic,$dota	! $nhic
+	faddd	$ahid,$nhid,$dotb	! $nhid
+
+	faddd	$nloc,$nhia,$nloc
+	faddd	$nlod,$nhib,$nlod
+
+	fdtox	$nloa,$nloa
+	fdtox	$nlob,$nlob
+	fdtox	$nloc,$nloc
+	fdtox	$nlod,$nlod
+
+	std	$nloa,[%sp+$bias+$frame+0]
+	add	$j,8,$j
+	std	$nlob,[%sp+$bias+$frame+8]
+	add	$ap,$j,%o4
+	std	$nloc,[%sp+$bias+$frame+16]
+	add	$np,$j,%o5
+	std	$nlod,[%sp+$bias+$frame+24]
+
+	ld	[%o4+0],$alo_	! load a[j] as pair of 32-bit words
+	fzeros	$alo
+	ld	[%o4+4],$ahi_
+	fzeros	$ahi
+	ld	[%o5+0],$nlo_	! load n[j] as pair of 32-bit words
+	fzeros	$nlo
+	ld	[%o5+4],$nhi_
+	fzeros	$nhi
+
+	fxtod	$alo,$alo
+	fxtod	$ahi,$ahi
+	fxtod	$nlo,$nlo
+	fxtod	$nhi,$nhi
+
+	ldx	[%sp+$bias+$frame+0],%o0
+		fmuld	$alo,$ba,$aloa
+	ldx	[%sp+$bias+$frame+8],%o1
+		fmuld	$nlo,$na,$nloa
+	ldx	[%sp+$bias+$frame+16],%o2
+		fmuld	$alo,$bb,$alob
+	ldx	[%sp+$bias+$frame+24],%o3
+		fmuld	$nlo,$nb,$nlob
+
+	srlx	%o0,16,%o7
+	std	$alo,[$ap_l+$j]		! save smashed ap[j] in double format
+		fmuld	$alo,$bc,$aloc
+	add	%o7,%o1,%o1
+	std	$ahi,[$ap_h+$j]
+		faddd	$aloa,$nloa,$nloa
+		fmuld	$nlo,$nc,$nloc
+	srlx	%o1,16,%o7
+	std	$nlo,[$np_l+$j]		! save smashed np[j] in double format
+		fmuld	$alo,$bd,$alod
+	add	%o7,%o2,%o2
+	std	$nhi,[$np_h+$j]
+		faddd	$alob,$nlob,$nlob
+		fmuld	$nlo,$nd,$nlod
+	srlx	%o2,16,%o7
+		fmuld	$ahi,$ba,$ahia
+	add	%o7,%o3,%o3		! %o3.%o2[0..15].%o1[0..15].%o0[0..15]
+		faddd	$aloc,$nloc,$nloc
+		fmuld	$nhi,$na,$nhia
+	!and	%o0,$mask,%o0
+	!and	%o1,$mask,%o1
+	!and	%o2,$mask,%o2
+	!sllx	%o1,16,%o1
+	!sllx	%o2,32,%o2
+	!sllx	%o3,48,%o7
+	!or	%o1,%o0,%o0
+	!or	%o2,%o0,%o0
+	!or	%o7,%o0,%o0		! 64-bit result
+	srlx	%o3,16,%g1		! 34-bit carry
+		fmuld	$ahi,$bb,$ahib
+
+	faddd	$alod,$nlod,$nlod
+		fmuld	$nhi,$nb,$nhib
+		fmuld	$ahi,$bc,$ahic
+	faddd	$ahia,$nhia,$nhia
+		fmuld	$nhi,$nc,$nhic
+		fmuld	$ahi,$bd,$ahid
+	faddd	$ahib,$nhib,$nhib
+		fmuld	$nhi,$nd,$nhid
+
+	faddd	$dota,$nloa,$nloa
+	faddd	$dotb,$nlob,$nlob
+	faddd	$ahic,$nhic,$dota	! $nhic
+	faddd	$ahid,$nhid,$dotb	! $nhid
+
+	faddd	$nloc,$nhia,$nloc
+	faddd	$nlod,$nhib,$nlod
+
+	fdtox	$nloa,$nloa
+	fdtox	$nlob,$nlob
+	fdtox	$nloc,$nloc
+	fdtox	$nlod,$nlod
+
+	std	$nloa,[%sp+$bias+$frame+0]
+	std	$nlob,[%sp+$bias+$frame+8]
+	addcc	$j,8,$j
+	std	$nloc,[%sp+$bias+$frame+16]
+	bz,pn	%icc,.L1stskip
+	std	$nlod,[%sp+$bias+$frame+24]
+
+.align	32			! incidentally already aligned !
+.L1st:
+	add	$ap,$j,%o4
+	add	$np,$j,%o5
+	ld	[%o4+0],$alo_	! load a[j] as pair of 32-bit words
+	fzeros	$alo
+	ld	[%o4+4],$ahi_
+	fzeros	$ahi
+	ld	[%o5+0],$nlo_	! load n[j] as pair of 32-bit words
+	fzeros	$nlo
+	ld	[%o5+4],$nhi_
+	fzeros	$nhi
+
+	fxtod	$alo,$alo
+	fxtod	$ahi,$ahi
+	fxtod	$nlo,$nlo
+	fxtod	$nhi,$nhi
+
+	ldx	[%sp+$bias+$frame+0],%o0
+		fmuld	$alo,$ba,$aloa
+	ldx	[%sp+$bias+$frame+8],%o1
+		fmuld	$nlo,$na,$nloa
+	ldx	[%sp+$bias+$frame+16],%o2
+		fmuld	$alo,$bb,$alob
+	ldx	[%sp+$bias+$frame+24],%o3
+		fmuld	$nlo,$nb,$nlob
+
+	srlx	%o0,16,%o7
+	std	$alo,[$ap_l+$j]		! save smashed ap[j] in double format
+		fmuld	$alo,$bc,$aloc
+	add	%o7,%o1,%o1
+	std	$ahi,[$ap_h+$j]
+		faddd	$aloa,$nloa,$nloa
+		fmuld	$nlo,$nc,$nloc
+	srlx	%o1,16,%o7
+	std	$nlo,[$np_l+$j]		! save smashed np[j] in double format
+		fmuld	$alo,$bd,$alod
+	add	%o7,%o2,%o2
+	std	$nhi,[$np_h+$j]
+		faddd	$alob,$nlob,$nlob
+		fmuld	$nlo,$nd,$nlod
+	srlx	%o2,16,%o7
+		fmuld	$ahi,$ba,$ahia
+	add	%o7,%o3,%o3		! %o3.%o2[0..15].%o1[0..15].%o0[0..15]
+	and	%o0,$mask,%o0
+		faddd	$aloc,$nloc,$nloc
+		fmuld	$nhi,$na,$nhia
+	and	%o1,$mask,%o1
+	and	%o2,$mask,%o2
+		fmuld	$ahi,$bb,$ahib
+	sllx	%o1,16,%o1
+		faddd	$alod,$nlod,$nlod
+		fmuld	$nhi,$nb,$nhib
+	sllx	%o2,32,%o2
+		fmuld	$ahi,$bc,$ahic
+	sllx	%o3,48,%o7
+	or	%o1,%o0,%o0
+		faddd	$ahia,$nhia,$nhia
+		fmuld	$nhi,$nc,$nhic
+	or	%o2,%o0,%o0
+		fmuld	$ahi,$bd,$ahid
+	or	%o7,%o0,%o0		! 64-bit result
+		faddd	$ahib,$nhib,$nhib
+		fmuld	$nhi,$nd,$nhid
+	addcc	%g1,%o0,%o0
+		faddd	$dota,$nloa,$nloa
+	srlx	%o3,16,%g1		! 34-bit carry
+		faddd	$dotb,$nlob,$nlob
+	bcs,a	%xcc,.+8
+	add	%g1,1,%g1
+
+	stx	%o0,[$tp]		! tp[j-1]=
+
+	faddd	$ahic,$nhic,$dota	! $nhic
+	faddd	$ahid,$nhid,$dotb	! $nhid
+
+	faddd	$nloc,$nhia,$nloc
+	faddd	$nlod,$nhib,$nlod
+
+	fdtox	$nloa,$nloa
+	fdtox	$nlob,$nlob
+	fdtox	$nloc,$nloc
+	fdtox	$nlod,$nlod
+
+	std	$nloa,[%sp+$bias+$frame+0]
+	std	$nlob,[%sp+$bias+$frame+8]
+	std	$nloc,[%sp+$bias+$frame+16]
+	std	$nlod,[%sp+$bias+$frame+24]
+
+	addcc	$j,8,$j
+	bnz,pt	%icc,.L1st
+	add	$tp,8,$tp
+
+.L1stskip:
+	fdtox	$dota,$dota
+	fdtox	$dotb,$dotb
+
+	ldx	[%sp+$bias+$frame+0],%o0
+	ldx	[%sp+$bias+$frame+8],%o1
+	ldx	[%sp+$bias+$frame+16],%o2
+	ldx	[%sp+$bias+$frame+24],%o3
+
+	srlx	%o0,16,%o7
+	std	$dota,[%sp+$bias+$frame+32]
+	add	%o7,%o1,%o1
+	std	$dotb,[%sp+$bias+$frame+40]
+	srlx	%o1,16,%o7
+	add	%o7,%o2,%o2
+	srlx	%o2,16,%o7
+	add	%o7,%o3,%o3		! %o3.%o2[0..15].%o1[0..15].%o0[0..15]
+	and	%o0,$mask,%o0
+	and	%o1,$mask,%o1
+	and	%o2,$mask,%o2
+	sllx	%o1,16,%o1
+	sllx	%o2,32,%o2
+	sllx	%o3,48,%o7
+	or	%o1,%o0,%o0
+	or	%o2,%o0,%o0
+	or	%o7,%o0,%o0		! 64-bit result
+	ldx	[%sp+$bias+$frame+32],%o4
+	addcc	%g1,%o0,%o0
+	ldx	[%sp+$bias+$frame+40],%o5
+	srlx	%o3,16,%g1		! 34-bit carry
+	bcs,a	%xcc,.+8
+	add	%g1,1,%g1
+
+	stx	%o0,[$tp]		! tp[j-1]=
+	add	$tp,8,$tp
+
+	srlx	%o4,16,%o7
+	add	%o7,%o5,%o5
+	and	%o4,$mask,%o4
+	sllx	%o5,16,%o7
+	or	%o7,%o4,%o4
+	addcc	%g1,%o4,%o4
+	srlx	%o5,48,%g1
+	bcs,a	%xcc,.+8
+	add	%g1,1,%g1
+
+	mov	%g1,$carry
+	stx	%o4,[$tp]		! tp[num-1]=
+
+	ba	.Louter
+	add	$i,8,$i
+.align	32
+.Louter:
+	sub	%g0,$num,$j		! j=-num
+	add	%sp,$bias+$frame+$locals,$tp
+
+	add	$ap,$j,%o3
+	add	$bp,$i,%o4
+
+	ld	[%o3+4],%g1		! bp[i]
+	ld	[%o3+0],%o0
+	ld	[%o4+4],%g5		! ap[0]
+	sllx	%g1,32,%g1
+	ld	[%o4+0],%o1
+	sllx	%g5,32,%g5
+	or	%g1,%o0,%o0
+	or	%g5,%o1,%o1
+
+	ldx	[$tp],%o2		! tp[0]
+	mulx	%o1,%o0,%o0
+	addcc	%o2,%o0,%o0
+	mulx	$n0,%o0,%o0		! (ap[0]*bp[i]+t[0])*n0
+	stx	%o0,[%sp+$bias+$frame+0]
+
+	! transfer b[i] to FPU as 4x16-bit values
+	ldda	[%o4+2]%asi,$ba
+	ldda	[%o4+0]%asi,$bb
+	ldda	[%o4+6]%asi,$bc
+	ldda	[%o4+4]%asi,$bd
+
+	! transfer (ap[0]*b[i]+t[0])*n0 to FPU as 4x16-bit values
+	ldda	[%sp+$bias+$frame+6]%asi,$na
+	fxtod	$ba,$ba
+	ldda	[%sp+$bias+$frame+4]%asi,$nb
+	fxtod	$bb,$bb
+	ldda	[%sp+$bias+$frame+2]%asi,$nc
+	fxtod	$bc,$bc
+	ldda	[%sp+$bias+$frame+0]%asi,$nd
+	fxtod	$bd,$bd
+	ldd	[$ap_l+$j],$alo		! load a[j] in double format
+	fxtod	$na,$na
+	ldd	[$ap_h+$j],$ahi
+	fxtod	$nb,$nb
+	ldd	[$np_l+$j],$nlo		! load n[j] in double format
+	fxtod	$nc,$nc
+	ldd	[$np_h+$j],$nhi
+	fxtod	$nd,$nd
+
+		fmuld	$alo,$ba,$aloa
+		fmuld	$nlo,$na,$nloa
+		fmuld	$alo,$bb,$alob
+		fmuld	$nlo,$nb,$nlob
+		fmuld	$alo,$bc,$aloc
+	faddd	$aloa,$nloa,$nloa
+		fmuld	$nlo,$nc,$nloc
+		fmuld	$alo,$bd,$alod
+	faddd	$alob,$nlob,$nlob
+		fmuld	$nlo,$nd,$nlod
+		fmuld	$ahi,$ba,$ahia
+	faddd	$aloc,$nloc,$nloc
+		fmuld	$nhi,$na,$nhia
+		fmuld	$ahi,$bb,$ahib
+	faddd	$alod,$nlod,$nlod
+		fmuld	$nhi,$nb,$nhib
+		fmuld	$ahi,$bc,$ahic
+	faddd	$ahia,$nhia,$nhia
+		fmuld	$nhi,$nc,$nhic
+		fmuld	$ahi,$bd,$ahid
+	faddd	$ahib,$nhib,$nhib
+		fmuld	$nhi,$nd,$nhid
+
+	faddd	$ahic,$nhic,$dota	! $nhic
+	faddd	$ahid,$nhid,$dotb	! $nhid
+
+	faddd	$nloc,$nhia,$nloc
+	faddd	$nlod,$nhib,$nlod
+
+	fdtox	$nloa,$nloa
+	fdtox	$nlob,$nlob
+	fdtox	$nloc,$nloc
+	fdtox	$nlod,$nlod
+
+	std	$nloa,[%sp+$bias+$frame+0]
+	std	$nlob,[%sp+$bias+$frame+8]
+	std	$nloc,[%sp+$bias+$frame+16]
+	add	$j,8,$j
+	std	$nlod,[%sp+$bias+$frame+24]
+
+	ldd	[$ap_l+$j],$alo		! load a[j] in double format
+	ldd	[$ap_h+$j],$ahi
+	ldd	[$np_l+$j],$nlo		! load n[j] in double format
+	ldd	[$np_h+$j],$nhi
+
+		fmuld	$alo,$ba,$aloa
+		fmuld	$nlo,$na,$nloa
+		fmuld	$alo,$bb,$alob
+		fmuld	$nlo,$nb,$nlob
+		fmuld	$alo,$bc,$aloc
+	ldx	[%sp+$bias+$frame+0],%o0
+		faddd	$aloa,$nloa,$nloa
+		fmuld	$nlo,$nc,$nloc
+	ldx	[%sp+$bias+$frame+8],%o1
+		fmuld	$alo,$bd,$alod
+	ldx	[%sp+$bias+$frame+16],%o2
+		faddd	$alob,$nlob,$nlob
+		fmuld	$nlo,$nd,$nlod
+	ldx	[%sp+$bias+$frame+24],%o3
+		fmuld	$ahi,$ba,$ahia
+
+	srlx	%o0,16,%o7
+		faddd	$aloc,$nloc,$nloc
+		fmuld	$nhi,$na,$nhia
+	add	%o7,%o1,%o1
+		fmuld	$ahi,$bb,$ahib
+	srlx	%o1,16,%o7
+		faddd	$alod,$nlod,$nlod
+		fmuld	$nhi,$nb,$nhib
+	add	%o7,%o2,%o2
+		fmuld	$ahi,$bc,$ahic
+	srlx	%o2,16,%o7
+		faddd	$ahia,$nhia,$nhia
+		fmuld	$nhi,$nc,$nhic
+	add	%o7,%o3,%o3		! %o3.%o2[0..15].%o1[0..15].%o0[0..15]
+	! why?
+	and	%o0,$mask,%o0
+		fmuld	$ahi,$bd,$ahid
+	and	%o1,$mask,%o1
+	and	%o2,$mask,%o2
+		faddd	$ahib,$nhib,$nhib
+		fmuld	$nhi,$nd,$nhid
+	sllx	%o1,16,%o1
+		faddd	$dota,$nloa,$nloa
+	sllx	%o2,32,%o2
+		faddd	$dotb,$nlob,$nlob
+	sllx	%o3,48,%o7
+	or	%o1,%o0,%o0
+		faddd	$ahic,$nhic,$dota	! $nhic
+	or	%o2,%o0,%o0
+		faddd	$ahid,$nhid,$dotb	! $nhid
+	or	%o7,%o0,%o0		! 64-bit result
+	ldx	[$tp],%o7
+		faddd	$nloc,$nhia,$nloc
+	addcc	%o7,%o0,%o0
+	! end-of-why?
+		faddd	$nlod,$nhib,$nlod
+	srlx	%o3,16,%g1		! 34-bit carry
+		fdtox	$nloa,$nloa
+	bcs,a	%xcc,.+8
+	add	%g1,1,%g1
+
+	fdtox	$nlob,$nlob
+	fdtox	$nloc,$nloc
+	fdtox	$nlod,$nlod
+
+	std	$nloa,[%sp+$bias+$frame+0]
+	std	$nlob,[%sp+$bias+$frame+8]
+	addcc	$j,8,$j
+	std	$nloc,[%sp+$bias+$frame+16]
+	bz,pn	%icc,.Linnerskip
+	std	$nlod,[%sp+$bias+$frame+24]
+
+	ba	.Linner
+	nop
+.align	32
+.Linner:
+	ldd	[$ap_l+$j],$alo		! load a[j] in double format
+	ldd	[$ap_h+$j],$ahi
+	ldd	[$np_l+$j],$nlo		! load n[j] in double format
+	ldd	[$np_h+$j],$nhi
+
+		fmuld	$alo,$ba,$aloa
+		fmuld	$nlo,$na,$nloa
+		fmuld	$alo,$bb,$alob
+		fmuld	$nlo,$nb,$nlob
+		fmuld	$alo,$bc,$aloc
+	ldx	[%sp+$bias+$frame+0],%o0
+		faddd	$aloa,$nloa,$nloa
+		fmuld	$nlo,$nc,$nloc
+	ldx	[%sp+$bias+$frame+8],%o1
+		fmuld	$alo,$bd,$alod
+	ldx	[%sp+$bias+$frame+16],%o2
+		faddd	$alob,$nlob,$nlob
+		fmuld	$nlo,$nd,$nlod
+	ldx	[%sp+$bias+$frame+24],%o3
+		fmuld	$ahi,$ba,$ahia
+
+	srlx	%o0,16,%o7
+		faddd	$aloc,$nloc,$nloc
+		fmuld	$nhi,$na,$nhia
+	add	%o7,%o1,%o1
+		fmuld	$ahi,$bb,$ahib
+	srlx	%o1,16,%o7
+		faddd	$alod,$nlod,$nlod
+		fmuld	$nhi,$nb,$nhib
+	add	%o7,%o2,%o2
+		fmuld	$ahi,$bc,$ahic
+	srlx	%o2,16,%o7
+		faddd	$ahia,$nhia,$nhia
+		fmuld	$nhi,$nc,$nhic
+	add	%o7,%o3,%o3		! %o3.%o2[0..15].%o1[0..15].%o0[0..15]
+	and	%o0,$mask,%o0
+		fmuld	$ahi,$bd,$ahid
+	and	%o1,$mask,%o1
+	and	%o2,$mask,%o2
+		faddd	$ahib,$nhib,$nhib
+		fmuld	$nhi,$nd,$nhid
+	sllx	%o1,16,%o1
+		faddd	$dota,$nloa,$nloa
+	sllx	%o2,32,%o2
+		faddd	$dotb,$nlob,$nlob
+	sllx	%o3,48,%o7
+	or	%o1,%o0,%o0
+		faddd	$ahic,$nhic,$dota	! $nhic
+	or	%o2,%o0,%o0
+		faddd	$ahid,$nhid,$dotb	! $nhid
+	or	%o7,%o0,%o0		! 64-bit result
+		faddd	$nloc,$nhia,$nloc
+	addcc	%g1,%o0,%o0
+	ldx	[$tp+8],%o7		! tp[j]
+		faddd	$nlod,$nhib,$nlod
+	srlx	%o3,16,%g1		! 34-bit carry
+		fdtox	$nloa,$nloa
+	bcs,a	%xcc,.+8
+	add	%g1,1,%g1
+		fdtox	$nlob,$nlob
+	addcc	%o7,%o0,%o0
+		fdtox	$nloc,$nloc
+	bcs,a	%xcc,.+8
+	add	%g1,1,%g1
+
+	stx	%o0,[$tp]		! tp[j-1]
+		fdtox	$nlod,$nlod
+
+	std	$nloa,[%sp+$bias+$frame+0]
+	std	$nlob,[%sp+$bias+$frame+8]
+	std	$nloc,[%sp+$bias+$frame+16]
+	addcc	$j,8,$j
+	std	$nlod,[%sp+$bias+$frame+24]
+	bnz,pt	%icc,.Linner
+	add	$tp,8,$tp
+
+.Linnerskip:
+	fdtox	$dota,$dota
+	fdtox	$dotb,$dotb
+
+	ldx	[%sp+$bias+$frame+0],%o0
+	ldx	[%sp+$bias+$frame+8],%o1
+	ldx	[%sp+$bias+$frame+16],%o2
+	ldx	[%sp+$bias+$frame+24],%o3
+
+	srlx	%o0,16,%o7
+	std	$dota,[%sp+$bias+$frame+32]
+	add	%o7,%o1,%o1
+	std	$dotb,[%sp+$bias+$frame+40]
+	srlx	%o1,16,%o7
+	add	%o7,%o2,%o2
+	srlx	%o2,16,%o7
+	add	%o7,%o3,%o3		! %o3.%o2[0..15].%o1[0..15].%o0[0..15]
+	and	%o0,$mask,%o0
+	and	%o1,$mask,%o1
+	and	%o2,$mask,%o2
+	sllx	%o1,16,%o1
+	sllx	%o2,32,%o2
+	sllx	%o3,48,%o7
+	or	%o1,%o0,%o0
+	or	%o2,%o0,%o0
+	ldx	[%sp+$bias+$frame+32],%o4
+	or	%o7,%o0,%o0		! 64-bit result
+	ldx	[%sp+$bias+$frame+40],%o5
+	addcc	%g1,%o0,%o0
+	ldx	[$tp+8],%o7		! tp[j]
+	srlx	%o3,16,%g1		! 34-bit carry
+	bcs,a	%xcc,.+8
+	add	%g1,1,%g1
+
+	addcc	%o7,%o0,%o0
+	bcs,a	%xcc,.+8
+	add	%g1,1,%g1
+
+	stx	%o0,[$tp]		! tp[j-1]
+	add	$tp,8,$tp
+
+	srlx	%o4,16,%o7
+	add	%o7,%o5,%o5
+	and	%o4,$mask,%o4
+	sllx	%o5,16,%o7
+	or	%o7,%o4,%o4
+	addcc	%g1,%o4,%o4
+	srlx	%o5,48,%g1
+	bcs,a	%xcc,.+8
+	add	%g1,1,%g1
+
+	addcc	$carry,%o4,%o4
+	stx	%o4,[$tp]		! tp[num-1]
+	mov	%g1,$carry
+	bcs,a	%xcc,.+8
+	add	$carry,1,$carry
+
+	addcc	$i,8,$i
+	bnz	%icc,.Louter
+	nop
+
+	add	$tp,8,$tp		! adjust tp to point at the end
+	orn	%g0,%g0,%g4
+	sub	%g0,$num,%o7		! n=-num
+	ba	.Lsub
+	subcc	%g0,%g0,%g0		! clear %icc.c
+
+.align	32
+.Lsub:
+	ldx	[$tp+%o7],%o0
+	add	$np,%o7,%g1
+	ld	[%g1+0],%o2
+	ld	[%g1+4],%o3
+	srlx	%o0,32,%o1
+	subccc	%o0,%o2,%o2
+	add	$rp,%o7,%g1
+	subccc	%o1,%o3,%o3
+	st	%o2,[%g1+0]
+	add	%o7,8,%o7
+	brnz,pt	%o7,.Lsub
+	st	%o3,[%g1+4]
+	subc	$carry,0,%g4
+	sub	%g0,$num,%o7		! n=-num
+	ba	.Lcopy
+	nop
+
+.align	32
+.Lcopy:
+	ldx	[$tp+%o7],%o0
+	add	$rp,%o7,%g1
+	ld	[%g1+0],%o2
+	ld	[%g1+4],%o3
+	stx	%g0,[$tp+%o7]
+	and	%o0,%g4,%o0
+	srlx	%o0,32,%o1
+	andn	%o2,%g4,%o2
+	andn	%o3,%g4,%o3
+	or	%o2,%o0,%o0
+	or	%o3,%o1,%o1
+	st	%o0,[%g1+0]
+	add	%o7,8,%o7
+	brnz,pt	%o7,.Lcopy
+	st	%o1,[%g1+4]
+	sub	%g0,$num,%o7		! n=-num
+
+.Lzap:
+	stx	%g0,[$ap_l+%o7]
+	stx	%g0,[$ap_h+%o7]
+	stx	%g0,[$np_l+%o7]
+	stx	%g0,[$np_h+%o7]
+	add	%o7,8,%o7
+	brnz,pt	%o7,.Lzap
+	nop
+
+	ldx	[%sp+$bias+$frame+48],%o7
+	wr	%g0,%o7,%asi		! restore %asi
+
+	mov	1,%i0
+.Lret:
+	ret
+	restore
+.type   $fname,#function
+.size	$fname,(.-$fname)
+.asciz	"Montgomery Multipltication for UltraSPARC, CRYPTOGAMS by "
+.align	32
+___
+
+$code =~ s/\`([^\`]*)\`/eval($1)/gem;
+
+# Below substitution makes it possible to compile without demanding
+# VIS extensions on command line, e.g. -xarch=v9 vs. -xarch=v9a. I
+# dare to do this, because VIS capability is detected at run-time now
+# and this routine is not called on CPU not capable to execute it. Do
+# note that fzeros is not the only VIS dependency! Another dependency
+# is implicit and is just _a_ numerical value loaded to %asi register,
+# which assembler can't recognize as VIS specific...
+$code =~ s/fzeros\s+%f([0-9]+)/
+	   sprintf(".word\t0x%x\t! fzeros %%f%d",0x81b00c20|($1<<25),$1)
+	  /gem;
+
+print $code;
+# flush
+close STDOUT;
diff --git a/crypto/bn/asm/via-mont.pl b/crypto/bn/asm/via-mont.pl
new file mode 100644
index 000000000..9f81bc822
--- /dev/null
+++ b/crypto/bn/asm/via-mont.pl
@@ -0,0 +1,254 @@
+#! /usr/bin/env perl
+# Copyright 2006-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+#
+# ====================================================================
+# Written by Andy Polyakov  for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+#
+# Wrapper around 'rep montmul', VIA-specific instruction accessing
+# PadLock Montgomery Multiplier. The wrapper is designed as drop-in
+# replacement for OpenSSL bn_mul_mont [first implemented in 0.9.9].
+#
+# Below are interleaved outputs from 'openssl speed rsa dsa' for 4
+# different software configurations on 1.5GHz VIA Esther processor.
+# Lines marked with "software integer" denote performance of hand-
+# coded integer-only assembler found in OpenSSL 0.9.7. "Software SSE2"
+# refers to hand-coded SSE2 Montgomery multiplication procedure found
+# OpenSSL 0.9.9. "Hardware VIA SDK" refers to padlock_pmm routine from
+# Padlock SDK 2.0.1 available for download from VIA, which naturally
+# utilizes the magic 'repz montmul' instruction. And finally "hardware
+# this" refers to *this* implementation which also uses 'repz montmul'
+#
+#                   sign    verify    sign/s verify/s
+# rsa  512 bits 0.001720s 0.000140s    581.4   7149.7	software integer
+# rsa  512 bits 0.000690s 0.000086s   1450.3  11606.0	software SSE2
+# rsa  512 bits 0.006136s 0.000201s    163.0   4974.5	hardware VIA SDK
+# rsa  512 bits 0.000712s 0.000050s   1404.9  19858.5	hardware this
+#
+# rsa 1024 bits 0.008518s 0.000413s    117.4   2420.8	software integer
+# rsa 1024 bits 0.004275s 0.000277s    233.9   3609.7	software SSE2
+# rsa 1024 bits 0.012136s 0.000260s     82.4   3844.5	hardware VIA SDK
+# rsa 1024 bits 0.002522s 0.000116s    396.5   8650.9	hardware this
+#
+# rsa 2048 bits 0.050101s 0.001371s     20.0    729.6	software integer
+# rsa 2048 bits 0.030273s 0.001008s     33.0    991.9	software SSE2
+# rsa 2048 bits 0.030833s 0.000976s     32.4   1025.1	hardware VIA SDK
+# rsa 2048 bits 0.011879s 0.000342s     84.2   2921.7	hardware this
+#
+# rsa 4096 bits 0.327097s 0.004859s      3.1    205.8	software integer
+# rsa 4096 bits 0.229318s 0.003859s      4.4    259.2	software SSE2
+# rsa 4096 bits 0.233953s 0.003274s      4.3    305.4	hardware VIA SDK
+# rsa 4096 bits 0.070493s 0.001166s     14.2    857.6	hardware this
+#
+# dsa  512 bits 0.001342s 0.001651s    745.2    605.7	software integer
+# dsa  512 bits 0.000844s 0.000987s   1185.3   1013.1	software SSE2
+# dsa  512 bits 0.001902s 0.002247s    525.6    444.9	hardware VIA SDK
+# dsa  512 bits 0.000458s 0.000524s   2182.2   1909.1	hardware this
+#
+# dsa 1024 bits 0.003964s 0.004926s    252.3    203.0	software integer
+# dsa 1024 bits 0.002686s 0.003166s    372.3    315.8	software SSE2
+# dsa 1024 bits 0.002397s 0.002823s    417.1    354.3	hardware VIA SDK
+# dsa 1024 bits 0.000978s 0.001170s   1022.2    855.0	hardware this
+#
+# dsa 2048 bits 0.013280s 0.016518s     75.3     60.5	software integer
+# dsa 2048 bits 0.009911s 0.011522s    100.9     86.8	software SSE2
+# dsa 2048 bits 0.009542s 0.011763s    104.8     85.0	hardware VIA SDK
+# dsa 2048 bits 0.002884s 0.003352s    346.8    298.3	hardware this
+#
+# To give you some other reference point here is output for 2.4GHz P4
+# running hand-coded SSE2 bn_mul_mont found in 0.9.9, i.e. "software
+# SSE2" in above terms.
+#
+# rsa  512 bits 0.000407s 0.000047s   2454.2  21137.0
+# rsa 1024 bits 0.002426s 0.000141s    412.1   7100.0
+# rsa 2048 bits 0.015046s 0.000491s     66.5   2034.9
+# rsa 4096 bits 0.109770s 0.002379s      9.1    420.3
+# dsa  512 bits 0.000438s 0.000525s   2281.1   1904.1
+# dsa 1024 bits 0.001346s 0.001595s    742.7    627.0
+# dsa 2048 bits 0.004745s 0.005582s    210.7    179.1
+#
+# Conclusions: 
+# - VIA SDK leaves a *lot* of room for improvement (which this
+#   implementation successfully fills:-);
+# - 'rep montmul' gives up to >3x performance improvement depending on
+#   key length;
+# - in terms of absolute performance it delivers approximately as much
+#   as modern out-of-order 32-bit cores [again, for longer keys].
+
+$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+push(@INC,"${dir}","${dir}../../perlasm");
+require "x86asm.pl";
+
+$output = pop;
+open STDOUT,">$output";
+
+&asm_init($ARGV[0],"via-mont.pl");
+
+# int bn_mul_mont(BN_ULONG *rp, const BN_ULONG *ap, const BN_ULONG *bp, const BN_ULONG *np,const BN_ULONG *n0, int num);
+$func="bn_mul_mont_padlock";
+
+$pad=16*1;	# amount of reserved bytes on top of every vector
+
+# stack layout
+$mZeroPrime=&DWP(0,"esp");		# these are specified by VIA
+$A=&DWP(4,"esp");
+$B=&DWP(8,"esp");
+$T=&DWP(12,"esp");
+$M=&DWP(16,"esp");
+$scratch=&DWP(20,"esp");
+$rp=&DWP(24,"esp");			# these are mine
+$sp=&DWP(28,"esp");
+# &DWP(32,"esp")			# 32 byte scratch area
+# &DWP(64+(4*$num+$pad)*0,"esp")	# padded tp[num]
+# &DWP(64+(4*$num+$pad)*1,"esp")	# padded copy of ap[num]
+# &DWP(64+(4*$num+$pad)*2,"esp")	# padded copy of bp[num]
+# &DWP(64+(4*$num+$pad)*3,"esp")	# padded copy of np[num]
+# Note that SDK suggests to unconditionally allocate 2K per vector. This
+# has quite an impact on performance. It naturally depends on key length,
+# but to give an example 1024 bit private RSA key operations suffer >30%
+# penalty. I allocate only as much as actually required...
+
+&function_begin($func);
+	&xor	("eax","eax");
+	&mov	("ecx",&wparam(5));	# num
+	# meet VIA's limitations for num [note that the specification
+	# expresses them in bits, while we work with amount of 32-bit words]
+	&test	("ecx",3);
+	&jnz	(&label("leave"));	# num % 4 != 0
+	&cmp	("ecx",8);
+	&jb	(&label("leave"));	# num < 8
+	&cmp	("ecx",1024);
+	&ja	(&label("leave"));	# num > 1024
+
+	&pushf	();
+	&cld	();
+
+	&mov	("edi",&wparam(0));	# rp
+	&mov	("eax",&wparam(1));	# ap
+	&mov	("ebx",&wparam(2));	# bp
+	&mov	("edx",&wparam(3));	# np
+	&mov	("esi",&wparam(4));	# n0
+	&mov	("esi",&DWP(0,"esi"));	# *n0
+
+	&lea	("ecx",&DWP($pad,"","ecx",4));	# ecx becomes vector size in bytes
+	&lea	("ebp",&DWP(64,"","ecx",4));	# allocate 4 vectors + 64 bytes
+	&neg	("ebp");
+	&add	("ebp","esp");
+	&and	("ebp",-64);		# align to cache-line
+	&xchg	("ebp","esp");		# alloca
+
+	&mov	($rp,"edi");		# save rp
+	&mov	($sp,"ebp");		# save esp
+
+	&mov	($mZeroPrime,"esi");
+	&lea	("esi",&DWP(64,"esp"));	# tp
+	&mov	($T,"esi");
+	&lea	("edi",&DWP(32,"esp"));	# scratch area
+	&mov	($scratch,"edi");
+	&mov	("esi","eax");
+
+	&lea	("ebp",&DWP(-$pad,"ecx"));
+	&shr	("ebp",2);		# restore original num value in ebp
+
+	&xor	("eax","eax");
+
+	&mov	("ecx","ebp");
+	&lea	("ecx",&DWP((32+$pad)/4,"ecx"));# padded tp + scratch
+	&data_byte(0xf3,0xab);		# rep stosl, bzero
+
+	&mov	("ecx","ebp");
+	&lea	("edi",&DWP(64+$pad,"esp","ecx",4));# pointer to ap copy
+	&mov	($A,"edi");
+	&data_byte(0xf3,0xa5);		# rep movsl, memcpy
+	&mov	("ecx",$pad/4);
+	&data_byte(0xf3,0xab);		# rep stosl, bzero pad
+	# edi points at the end of padded ap copy...
+
+	&mov	("ecx","ebp");
+	&mov	("esi","ebx");
+	&mov	($B,"edi");
+	&data_byte(0xf3,0xa5);		# rep movsl, memcpy
+	&mov	("ecx",$pad/4);
+	&data_byte(0xf3,0xab);		# rep stosl, bzero pad
+	# edi points at the end of padded bp copy...
+
+	&mov	("ecx","ebp");
+	&mov	("esi","edx");
+	&mov	($M,"edi");
+	&data_byte(0xf3,0xa5);		# rep movsl, memcpy
+	&mov	("ecx",$pad/4);
+	&data_byte(0xf3,0xab);		# rep stosl, bzero pad
+	# edi points at the end of padded np copy...
+
+	# let magic happen...
+	&mov	("ecx","ebp");
+	&mov	("esi","esp");
+	&shl	("ecx",5);		# convert word counter to bit counter
+	&align	(4);
+	&data_byte(0xf3,0x0f,0xa6,0xc0);# rep montmul
+
+	&mov	("ecx","ebp");
+	&lea	("esi",&DWP(64,"esp"));		# tp
+	# edi still points at the end of padded np copy...
+	&neg	("ebp");
+	&lea	("ebp",&DWP(-$pad,"edi","ebp",4));	# so just "rewind"
+	&mov	("edi",$rp);			# restore rp
+	&xor	("edx","edx");			# i=0 and clear CF
+
+&set_label("sub",8);
+	&mov	("eax",&DWP(0,"esi","edx",4));
+	&sbb	("eax",&DWP(0,"ebp","edx",4));
+	&mov	(&DWP(0,"edi","edx",4),"eax");	# rp[i]=tp[i]-np[i]
+	&lea	("edx",&DWP(1,"edx"));		# i++
+	&loop	(&label("sub"));		# doesn't affect CF!
+
+	&mov	("eax",&DWP(0,"esi","edx",4));	# upmost overflow bit
+	&sbb	("eax",0);
+	&and	("esi","eax");
+	¬	("eax");
+	&mov	("ebp","edi");
+	&and	("ebp","eax");
+	&or	("esi","ebp");			# tp=carry?tp:rp
+
+	&mov	("ecx","edx");			# num
+	&xor	("edx","edx");			# i=0
+
+&set_label("copy",8);
+	&mov	("eax",&DWP(0,"esi","edx",4));
+	&mov	(&DWP(64,"esp","edx",4),"ecx");	# zap tp
+	&mov	(&DWP(0,"edi","edx",4),"eax");
+	&lea	("edx",&DWP(1,"edx"));		# i++
+	&loop	(&label("copy"));
+
+	&mov	("ebp",$sp);
+	&xor	("eax","eax");
+
+	&mov	("ecx",64/4);
+	&mov	("edi","esp");		# zap frame including scratch area
+	&data_byte(0xf3,0xab);		# rep stosl, bzero
+
+	# zap copies of ap, bp and np
+	&lea	("edi",&DWP(64+$pad,"esp","edx",4));# pointer to ap
+	&lea	("ecx",&DWP(3*$pad/4,"edx","edx",2));
+	&data_byte(0xf3,0xab);		# rep stosl, bzero
+
+	&mov	("esp","ebp");
+	&inc	("eax");		# signal "done"
+	&popf	();
+&set_label("leave");
+&function_end($func);
+
+&asciz("Padlock Montgomery Multiplication, CRYPTOGAMS by ");
+
+&asm_finish();
+
+close STDOUT;
diff --git a/crypto/bn/asm/vis3-mont.pl b/crypto/bn/asm/vis3-mont.pl
new file mode 100644
index 000000000..64dba4480
--- /dev/null
+++ b/crypto/bn/asm/vis3-mont.pl
@@ -0,0 +1,384 @@
+#! /usr/bin/env perl
+# Copyright 2012-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+
+# ====================================================================
+# Written by Andy Polyakov  for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+
+# October 2012.
+#
+# SPARCv9 VIS3 Montgomery multiplicaion procedure suitable for T3 and
+# onward. There are three new instructions used here: umulxhi,
+# addxc[cc] and initializing store. On T3 RSA private key operations
+# are 1.54/1.87/2.11/2.26 times faster for 512/1024/2048/4096-bit key
+# lengths. This is without dedicated squaring procedure. On T4
+# corresponding coefficients are 1.47/2.10/2.80/2.90x, which is mostly
+# for reference purposes, because T4 has dedicated Montgomery
+# multiplication and squaring *instructions* that deliver even more.
+
+$output = pop;
+open STDOUT,">$output";
+
+$frame = "STACK_FRAME";
+$bias = "STACK_BIAS";
+
+$code.=<<___;
+#include "sparc_arch.h"
+
+#ifdef	__arch64__
+.register	%g2,#scratch
+.register	%g3,#scratch
+#endif
+
+.section	".text",#alloc,#execinstr
+___
+
+($n0,$m0,$m1,$lo0,$hi0, $lo1,$hi1,$aj,$alo,$nj,$nlo,$tj)=
+	(map("%g$_",(1..5)),map("%o$_",(0..5,7)));
+
+# int bn_mul_mont(
+$rp="%o0";	# BN_ULONG *rp,
+$ap="%o1";	# const BN_ULONG *ap,
+$bp="%o2";	# const BN_ULONG *bp,
+$np="%o3";	# const BN_ULONG *np,
+$n0p="%o4";	# const BN_ULONG *n0,
+$num="%o5";	# int num);	# caller ensures that num is even
+				# and >=6
+$code.=<<___;
+.globl	bn_mul_mont_vis3
+.align	32
+bn_mul_mont_vis3:
+	add	%sp,	$bias,	%g4	! real top of stack
+	sll	$num,	2,	$num	! size in bytes
+	add	$num,	63,	%g5
+	andn	%g5,	63,	%g5	! buffer size rounded up to 64 bytes
+	add	%g5,	%g5,	%g1
+	add	%g5,	%g1,	%g1	! 3*buffer size
+	sub	%g4,	%g1,	%g1
+	andn	%g1,	63,	%g1	! align at 64 byte
+	sub	%g1,	$frame,	%g1	! new top of stack
+	sub	%g1,	%g4,	%g1
+
+	save	%sp,	%g1,	%sp
+___
+
+#	+-------------------------------+<-----	%sp
+#	.				.
+#	+-------------------------------+<-----	aligned at 64 bytes
+#	| __int64 tmp[0]		|
+#	+-------------------------------+
+#	.				.
+#	.				.
+#	+-------------------------------+<----- aligned at 64 bytes
+#	| __int64 ap[1..0]		|	converted ap[]
+#	+-------------------------------+
+#	| __int64 np[1..0]		|	converted np[]
+#	+-------------------------------+
+#	| __int64 ap[3..2]		|
+#	.				.
+#	.				.
+#	+-------------------------------+
+($rp,$ap,$bp,$np,$n0p,$num)=map("%i$_",(0..5));
+($t0,$t1,$t2,$t3,$cnt,$tp,$bufsz,$anp)=map("%l$_",(0..7));
+($ovf,$i)=($t0,$t1);
+$code.=<<___;
+	ld	[$n0p+0],	$t0	! pull n0[0..1] value
+	add	%sp, $bias+$frame, $tp
+	ld	[$n0p+4],	$t1
+	add	$tp,	%g5,	$anp
+	ld	[$bp+0],	$t2	! m0=bp[0]
+	sllx	$t1,	32,	$n0
+	ld	[$bp+4],	$t3
+	or	$t0,	$n0,	$n0
+	add	$bp,	8,	$bp
+
+	ld	[$ap+0],	$t0	! ap[0]
+	sllx	$t3,	32,	$m0
+	ld	[$ap+4],	$t1
+	or	$t2,	$m0,	$m0
+
+	ld	[$ap+8],	$t2	! ap[1]
+	sllx	$t1,	32,	$aj
+	ld	[$ap+12],	$t3
+	or	$t0,	$aj,	$aj
+	add	$ap,	16,	$ap
+	stx	$aj,	[$anp]		! converted ap[0]
+
+	mulx	$aj,	$m0,	$lo0	! ap[0]*bp[0]
+	umulxhi	$aj,	$m0,	$hi0
+
+	ld	[$np+0],	$t0	! np[0]
+	sllx	$t3,	32,	$aj
+	ld	[$np+4],	$t1
+	or	$t2,	$aj,	$aj
+
+	ld	[$np+8],	$t2	! np[1]
+	sllx	$t1,	32,	$nj
+	ld	[$np+12],	$t3
+	or	$t0, $nj,	$nj
+	add	$np,	16,	$np
+	stx	$nj,	[$anp+8]	! converted np[0]
+
+	mulx	$lo0,	$n0,	$m1	! "tp[0]"*n0
+	stx	$aj,	[$anp+16]	! converted ap[1]
+
+	mulx	$aj,	$m0,	$alo	! ap[1]*bp[0]
+	umulxhi	$aj,	$m0,	$aj	! ahi=aj
+
+	mulx	$nj,	$m1,	$lo1	! np[0]*m1
+	umulxhi	$nj,	$m1,	$hi1
+
+	sllx	$t3,	32,	$nj
+	or	$t2,	$nj,	$nj
+	stx	$nj,	[$anp+24]	! converted np[1]
+	add	$anp,	32,	$anp
+
+	addcc	$lo0,	$lo1,	$lo1
+	addxc	%g0,	$hi1,	$hi1
+
+	mulx	$nj,	$m1,	$nlo	! np[1]*m1
+	umulxhi	$nj,	$m1,	$nj	! nhi=nj
+
+	ba	.L1st
+	sub	$num,	24,	$cnt	! cnt=num-3
+
+.align	16
+.L1st:
+	ld	[$ap+0],	$t0	! ap[j]
+	addcc	$alo,	$hi0,	$lo0
+	ld	[$ap+4],	$t1
+	addxc	$aj,	%g0,	$hi0
+
+	sllx	$t1,	32,	$aj
+	add	$ap,	8,	$ap
+	or	$t0,	$aj,	$aj
+	stx	$aj,	[$anp]		! converted ap[j]
+
+	ld	[$np+0],	$t2	! np[j]
+	addcc	$nlo,	$hi1,	$lo1
+	ld	[$np+4],	$t3
+	addxc	$nj,	%g0,	$hi1	! nhi=nj
+
+	sllx	$t3,	32,	$nj
+	add	$np,	8,	$np
+	mulx	$aj,	$m0,	$alo	! ap[j]*bp[0]
+	or	$t2,	$nj,	$nj
+	umulxhi	$aj,	$m0,	$aj	! ahi=aj
+	stx	$nj,	[$anp+8]	! converted np[j]
+	add	$anp,	16,	$anp	! anp++
+
+	mulx	$nj,	$m1,	$nlo	! np[j]*m1
+	addcc	$lo0,	$lo1,	$lo1	! np[j]*m1+ap[j]*bp[0]
+	umulxhi	$nj,	$m1,	$nj	! nhi=nj
+	addxc	%g0,	$hi1,	$hi1
+	stx	$lo1,	[$tp]		! tp[j-1]
+	add	$tp,	8,	$tp	! tp++
+
+	brnz,pt	$cnt,	.L1st
+	sub	$cnt,	8,	$cnt	! j--
+!.L1st
+	addcc	$alo,	$hi0,	$lo0
+	addxc	$aj,	%g0,	$hi0	! ahi=aj
+
+	addcc	$nlo,	$hi1,	$lo1
+	addxc	$nj,	%g0,	$hi1
+	addcc	$lo0,	$lo1,	$lo1	! np[j]*m1+ap[j]*bp[0]
+	addxc	%g0,	$hi1,	$hi1
+	stx	$lo1,	[$tp]		! tp[j-1]
+	add	$tp,	8,	$tp
+
+	addcc	$hi0,	$hi1,	$hi1
+	addxc	%g0,	%g0,	$ovf	! upmost overflow bit
+	stx	$hi1,	[$tp]
+	add	$tp,	8,	$tp
+
+	ba	.Louter
+	sub	$num,	16,	$i	! i=num-2
+
+.align	16
+.Louter:
+	ld	[$bp+0],	$t2	! m0=bp[i]
+	ld	[$bp+4],	$t3
+
+	sub	$anp,	$num,	$anp	! rewind
+	sub	$tp,	$num,	$tp
+	sub	$anp,	$num,	$anp
+
+	add	$bp,	8,	$bp
+	sllx	$t3,	32,	$m0
+	ldx	[$anp+0],	$aj	! ap[0]
+	or	$t2,	$m0,	$m0
+	ldx	[$anp+8],	$nj	! np[0]
+
+	mulx	$aj,	$m0,	$lo0	! ap[0]*bp[i]
+	ldx	[$tp],		$tj	! tp[0]
+	umulxhi	$aj,	$m0,	$hi0
+	ldx	[$anp+16],	$aj	! ap[1]
+	addcc	$lo0,	$tj,	$lo0	! ap[0]*bp[i]+tp[0]
+	mulx	$aj,	$m0,	$alo	! ap[1]*bp[i]
+	addxc	%g0,	$hi0,	$hi0
+	mulx	$lo0,	$n0,	$m1	! tp[0]*n0
+	umulxhi	$aj,	$m0,	$aj	! ahi=aj
+	mulx	$nj,	$m1,	$lo1	! np[0]*m1
+	umulxhi	$nj,	$m1,	$hi1
+	ldx	[$anp+24],	$nj	! np[1]
+	add	$anp,	32,	$anp
+	addcc	$lo1,	$lo0,	$lo1
+	mulx	$nj,	$m1,	$nlo	! np[1]*m1
+	addxc	%g0,	$hi1,	$hi1
+	umulxhi	$nj,	$m1,	$nj	! nhi=nj
+
+	ba	.Linner
+	sub	$num,	24,	$cnt	! cnt=num-3
+.align	16
+.Linner:
+	addcc	$alo,	$hi0,	$lo0
+	ldx	[$tp+8],	$tj	! tp[j]
+	addxc	$aj,	%g0,	$hi0	! ahi=aj
+	ldx	[$anp+0],	$aj	! ap[j]
+	addcc	$nlo,	$hi1,	$lo1
+	mulx	$aj,	$m0,	$alo	! ap[j]*bp[i]
+	addxc	$nj,	%g0,	$hi1	! nhi=nj
+	ldx	[$anp+8],	$nj	! np[j]
+	add	$anp,	16,	$anp
+	umulxhi	$aj,	$m0,	$aj	! ahi=aj
+	addcc	$lo0,	$tj,	$lo0	! ap[j]*bp[i]+tp[j]
+	mulx	$nj,	$m1,	$nlo	! np[j]*m1
+	addxc	%g0,	$hi0,	$hi0
+	umulxhi	$nj,	$m1,	$nj	! nhi=nj
+	addcc	$lo1,	$lo0,	$lo1	! np[j]*m1+ap[j]*bp[i]+tp[j]
+	addxc	%g0,	$hi1,	$hi1
+	stx	$lo1,	[$tp]		! tp[j-1]
+	add	$tp,	8,	$tp
+	brnz,pt	$cnt,	.Linner
+	sub	$cnt,	8,	$cnt
+!.Linner
+	ldx	[$tp+8],	$tj	! tp[j]
+	addcc	$alo,	$hi0,	$lo0
+	addxc	$aj,	%g0,	$hi0	! ahi=aj
+	addcc	$lo0,	$tj,	$lo0	! ap[j]*bp[i]+tp[j]
+	addxc	%g0,	$hi0,	$hi0
+
+	addcc	$nlo,	$hi1,	$lo1
+	addxc	$nj,	%g0,	$hi1	! nhi=nj
+	addcc	$lo1,	$lo0,	$lo1	! np[j]*m1+ap[j]*bp[i]+tp[j]
+	addxc	%g0,	$hi1,	$hi1
+	stx	$lo1,	[$tp]		! tp[j-1]
+
+	subcc	%g0,	$ovf,	%g0	! move upmost overflow to CCR.xcc
+	addxccc	$hi1,	$hi0,	$hi1
+	addxc	%g0,	%g0,	$ovf
+	stx	$hi1,	[$tp+8]
+	add	$tp,	16,	$tp
+
+	brnz,pt	$i,	.Louter
+	sub	$i,	8,	$i
+
+	sub	$anp,	$num,	$anp	! rewind
+	sub	$tp,	$num,	$tp
+	sub	$anp,	$num,	$anp
+	ba	.Lsub
+	subcc	$num,	8,	$cnt	! cnt=num-1 and clear CCR.xcc
+
+.align	16
+.Lsub:
+	ldx	[$tp],		$tj
+	add	$tp,	8,	$tp
+	ldx	[$anp+8],	$nj
+	add	$anp,	16,	$anp
+	subccc	$tj,	$nj,	$t2	! tp[j]-np[j]
+	srlx	$tj,	32,	$tj
+	srlx	$nj,	32,	$nj
+	subccc	$tj,	$nj,	$t3
+	add	$rp,	8,	$rp
+	st	$t2,	[$rp-4]		! reverse order
+	st	$t3,	[$rp-8]
+	brnz,pt	$cnt,	.Lsub
+	sub	$cnt,	8,	$cnt
+
+	sub	$anp,	$num,	$anp	! rewind
+	sub	$tp,	$num,	$tp
+	sub	$anp,	$num,	$anp
+	sub	$rp,	$num,	$rp
+
+	subc	$ovf,	%g0,	$ovf	! handle upmost overflow bit
+	and	$tp,	$ovf,	$ap
+	andn	$rp,	$ovf,	$np
+	or	$np,	$ap,	$ap	! ap=borrow?tp:rp
+	ba	.Lcopy
+	sub	$num,	8,	$cnt
+
+.align	16
+.Lcopy:					! copy or in-place refresh
+	ld	[$ap+0],	$t2
+	ld	[$ap+4],	$t3
+	add	$ap,	8,	$ap
+	stx	%g0,	[$tp]		! zap
+	add	$tp,	8,	$tp
+	stx	%g0,	[$anp]		! zap
+	stx	%g0,	[$anp+8]
+	add	$anp,	16,	$anp
+	st	$t3,	[$rp+0]		! flip order
+	st	$t2,	[$rp+4]
+	add	$rp,	8,	$rp
+	brnz	$cnt,	.Lcopy
+	sub	$cnt,	8,	$cnt
+
+	mov	1,	%o0
+	ret
+	restore
+.type	bn_mul_mont_vis3, #function
+.size	bn_mul_mont_vis3, .-bn_mul_mont_vis3
+.asciz  "Montgomery Multiplication for SPARCv9 VIS3, CRYPTOGAMS by "
+.align	4
+___
+
+# Purpose of these subroutines is to explicitly encode VIS instructions,
+# so that one can compile the module without having to specify VIS
+# extensions on compiler command line, e.g. -xarch=v9 vs. -xarch=v9a.
+# Idea is to reserve for option to produce "universal" binary and let
+# programmer detect if current CPU is VIS capable at run-time.
+sub unvis3 {
+my ($mnemonic,$rs1,$rs2,$rd)=@_;
+my %bias = ( "g" => 0, "o" => 8, "l" => 16, "i" => 24 );
+my ($ref,$opf);
+my %visopf = (	"addxc"		=> 0x011,
+		"addxccc"	=> 0x013,
+		"umulxhi"	=> 0x016	);
+
+    $ref = "$mnemonic\t$rs1,$rs2,$rd";
+
+    if ($opf=$visopf{$mnemonic}) {
+	foreach ($rs1,$rs2,$rd) {
+	    return $ref if (!/%([goli])([0-9])/);
+	    $_=$bias{$1}+$2;
+	}
+
+	return	sprintf ".word\t0x%08x !%s",
+			0x81b00000|$rd<<25|$rs1<<14|$opf<<5|$rs2,
+			$ref;
+    } else {
+	return $ref;
+    }
+}
+
+foreach (split("\n",$code)) {
+	s/\`([^\`]*)\`/eval $1/ge;
+
+	s/\b(umulxhi|addxc[c]{0,2})\s+(%[goli][0-7]),\s*(%[goli][0-7]),\s*(%[goli][0-7])/
+		&unvis3($1,$2,$3,$4)
+	 /ge;
+
+	print $_,"\n";
+}
+
+close STDOUT;
diff --git a/crypto/bn/asm/x86-gf2m.pl b/crypto/bn/asm/x86-gf2m.pl
new file mode 100644
index 000000000..f46436873
--- /dev/null
+++ b/crypto/bn/asm/x86-gf2m.pl
@@ -0,0 +1,325 @@
+#! /usr/bin/env perl
+# Copyright 2011-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+#
+# ====================================================================
+# Written by Andy Polyakov  for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+#
+# May 2011
+#
+# The module implements bn_GF2m_mul_2x2 polynomial multiplication used
+# in bn_gf2m.c. It's kind of low-hanging mechanical port from C for
+# the time being... Except that it has three code paths: pure integer
+# code suitable for any x86 CPU, MMX code suitable for PIII and later
+# and PCLMULQDQ suitable for Westmere and later. Improvement varies
+# from one benchmark and µ-arch to another. Below are interval values
+# for 163- and 571-bit ECDH benchmarks relative to compiler-generated
+# code:
+#
+# PIII		16%-30%
+# P4		12%-12%
+# Opteron	18%-40%
+# Core2		19%-44%
+# Atom		38%-64%
+# Westmere	53%-121%(PCLMULQDQ)/20%-32%(MMX)
+# Sandy Bridge	72%-127%(PCLMULQDQ)/27%-23%(MMX)
+#
+# Note that above improvement coefficients are not coefficients for
+# bn_GF2m_mul_2x2 itself. For example 120% ECDH improvement is result
+# of bn_GF2m_mul_2x2 being >4x faster. As it gets faster, benchmark
+# is more and more dominated by other subroutines, most notably by
+# BN_GF2m_mod[_mul]_arr...
+
+$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+push(@INC,"${dir}","${dir}../../perlasm");
+require "x86asm.pl";
+
+$output = pop;
+open STDOUT,">$output";
+
+&asm_init($ARGV[0],$0,$x86only = $ARGV[$#ARGV] eq "386");
+
+$sse2=0;
+for (@ARGV) { $sse2=1 if (/-DOPENSSL_IA32_SSE2/); }
+
+&external_label("OPENSSL_ia32cap_P") if ($sse2);
+
+$a="eax";
+$b="ebx";
+($a1,$a2,$a4)=("ecx","edx","ebp");
+
+$R="mm0";
+@T=("mm1","mm2");
+($A,$B,$B30,$B31)=("mm2","mm3","mm4","mm5");
+@i=("esi","edi");
+
+					if (!$x86only) {
+&function_begin_B("_mul_1x1_mmx");
+	&sub	("esp",32+4);
+	 &mov	($a1,$a);
+	 &lea	($a2,&DWP(0,$a,$a));
+	 &and	($a1,0x3fffffff);
+	 &lea	($a4,&DWP(0,$a2,$a2));
+	 &mov	(&DWP(0*4,"esp"),0);
+	 &and	($a2,0x7fffffff);
+	&movd	($A,$a);
+	&movd	($B,$b);
+	 &mov	(&DWP(1*4,"esp"),$a1);	# a1
+	 &xor	($a1,$a2);		# a1^a2
+	&pxor	($B31,$B31);
+	&pxor	($B30,$B30);
+	 &mov	(&DWP(2*4,"esp"),$a2);	# a2
+	 &xor	($a2,$a4);		# a2^a4
+	 &mov	(&DWP(3*4,"esp"),$a1);	# a1^a2
+	&pcmpgtd($B31,$A);		# broadcast 31st bit
+	&paddd	($A,$A);		# $A<<=1
+	 &xor	($a1,$a2);		# a1^a4=a1^a2^a2^a4
+	 &mov	(&DWP(4*4,"esp"),$a4);	# a4
+	 &xor	($a4,$a2);		# a2=a4^a2^a4
+	&pand	($B31,$B);
+	&pcmpgtd($B30,$A);		# broadcast 30th bit
+	 &mov	(&DWP(5*4,"esp"),$a1);	# a1^a4
+	 &xor	($a4,$a1);		# a1^a2^a4
+	&psllq	($B31,31);
+	&pand	($B30,$B);
+	 &mov	(&DWP(6*4,"esp"),$a2);	# a2^a4
+	&mov	(@i[0],0x7);
+	 &mov	(&DWP(7*4,"esp"),$a4);	# a1^a2^a4
+	 &mov	($a4,@i[0]);
+	&and	(@i[0],$b);
+	&shr	($b,3);
+	&mov	(@i[1],$a4);
+	&psllq	($B30,30);
+	&and	(@i[1],$b);
+	&shr	($b,3);
+	&movd	($R,&DWP(0,"esp",@i[0],4));
+	&mov	(@i[0],$a4);
+	&and	(@i[0],$b);
+	&shr	($b,3);
+	for($n=1;$n<9;$n++) {
+		&movd	(@T[1],&DWP(0,"esp",@i[1],4));
+		&mov	(@i[1],$a4);
+		&psllq	(@T[1],3*$n);
+		&and	(@i[1],$b);
+		&shr	($b,3);
+		&pxor	($R,@T[1]);
+
+		push(@i,shift(@i)); push(@T,shift(@T));
+	}
+	&movd	(@T[1],&DWP(0,"esp",@i[1],4));
+	&pxor	($R,$B30);
+	&psllq	(@T[1],3*$n++);
+	&pxor	($R,@T[1]);
+
+	&movd	(@T[0],&DWP(0,"esp",@i[0],4));
+	&pxor	($R,$B31);
+	&psllq	(@T[0],3*$n);
+	&add	("esp",32+4);
+	&pxor	($R,@T[0]);
+	&ret	();
+&function_end_B("_mul_1x1_mmx");
+					}
+
+($lo,$hi)=("eax","edx");
+@T=("ecx","ebp");
+
+&function_begin_B("_mul_1x1_ialu");
+	&sub	("esp",32+4);
+	 &mov	($a1,$a);
+	 &lea	($a2,&DWP(0,$a,$a));
+	 &lea	($a4,&DWP(0,"",$a,4));
+	 &and	($a1,0x3fffffff);
+	&lea	(@i[1],&DWP(0,$lo,$lo));
+	&sar	($lo,31);		# broadcast 31st bit
+	 &mov	(&DWP(0*4,"esp"),0);
+	 &and	($a2,0x7fffffff);
+	 &mov	(&DWP(1*4,"esp"),$a1);	# a1
+	 &xor	($a1,$a2);		# a1^a2
+	 &mov	(&DWP(2*4,"esp"),$a2);	# a2
+	 &xor	($a2,$a4);		# a2^a4
+	 &mov	(&DWP(3*4,"esp"),$a1);	# a1^a2
+	 &xor	($a1,$a2);		# a1^a4=a1^a2^a2^a4
+	 &mov	(&DWP(4*4,"esp"),$a4);	# a4
+	 &xor	($a4,$a2);		# a2=a4^a2^a4
+	 &mov	(&DWP(5*4,"esp"),$a1);	# a1^a4
+	 &xor	($a4,$a1);		# a1^a2^a4
+	&sar	(@i[1],31);		# broardcast 30th bit
+	&and	($lo,$b);
+	 &mov	(&DWP(6*4,"esp"),$a2);	# a2^a4
+	&and	(@i[1],$b);
+	 &mov	(&DWP(7*4,"esp"),$a4);	# a1^a2^a4
+	&mov	($hi,$lo);
+	&shl	($lo,31);
+	&mov	(@T[0],@i[1]);
+	&shr	($hi,1);
+
+	 &mov	(@i[0],0x7);
+	&shl	(@i[1],30);
+	 &and	(@i[0],$b);
+	&shr	(@T[0],2);
+	&xor	($lo,@i[1]);
+
+	&shr	($b,3);
+	&mov	(@i[1],0x7);		# 5-byte instruction!?
+	&and	(@i[1],$b);
+	&shr	($b,3);
+	 &xor	($hi,@T[0]);
+	&xor	($lo,&DWP(0,"esp",@i[0],4));
+	&mov	(@i[0],0x7);
+	&and	(@i[0],$b);
+	&shr	($b,3);
+	for($n=1;$n<9;$n++) {
+		&mov	(@T[1],&DWP(0,"esp",@i[1],4));
+		&mov	(@i[1],0x7);
+		&mov	(@T[0],@T[1]);
+		&shl	(@T[1],3*$n);
+		&and	(@i[1],$b);
+		&shr	(@T[0],32-3*$n);
+		&xor	($lo,@T[1]);
+		&shr	($b,3);
+		&xor	($hi,@T[0]);
+
+		push(@i,shift(@i)); push(@T,shift(@T));
+	}
+	&mov	(@T[1],&DWP(0,"esp",@i[1],4));
+	&mov	(@T[0],@T[1]);
+	&shl	(@T[1],3*$n);
+	&mov	(@i[1],&DWP(0,"esp",@i[0],4));
+	&shr	(@T[0],32-3*$n);	$n++;
+	&mov	(@i[0],@i[1]);
+	&xor	($lo,@T[1]);
+	&shl	(@i[1],3*$n);
+	&xor	($hi,@T[0]);
+	&shr	(@i[0],32-3*$n);
+	&xor	($lo,@i[1]);
+	&xor	($hi,@i[0]);
+
+	&add	("esp",32+4);
+	&ret	();
+&function_end_B("_mul_1x1_ialu");
+
+# void bn_GF2m_mul_2x2(BN_ULONG *r, BN_ULONG a1, BN_ULONG a0, BN_ULONG b1, BN_ULONG b0);
+&function_begin_B("bn_GF2m_mul_2x2");
+if (!$x86only) {
+	&picmeup("edx","OPENSSL_ia32cap_P");
+	&mov	("eax",&DWP(0,"edx"));
+	&mov	("edx",&DWP(4,"edx"));
+	&test	("eax",1<<23);		# check MMX bit
+	&jz	(&label("ialu"));
+if ($sse2) {
+	&test	("eax",1<<24);		# check FXSR bit
+	&jz	(&label("mmx"));
+	&test	("edx",1<<1);		# check PCLMULQDQ bit
+	&jz	(&label("mmx"));
+
+	&movups		("xmm0",&QWP(8,"esp"));
+	&shufps		("xmm0","xmm0",0b10110001);
+	&pclmulqdq	("xmm0","xmm0",1);
+	&mov		("eax",&DWP(4,"esp"));
+	&movups		(&QWP(0,"eax"),"xmm0");
+	&ret	();
+
+&set_label("mmx",16);
+}
+	&push	("ebp");
+	&push	("ebx");
+	&push	("esi");
+	&push	("edi");
+	&mov	($a,&wparam(1));
+	&mov	($b,&wparam(3));
+	&call	("_mul_1x1_mmx");	# a1·b1
+	&movq	("mm7",$R);
+
+	&mov	($a,&wparam(2));
+	&mov	($b,&wparam(4));
+	&call	("_mul_1x1_mmx");	# a0·b0
+	&movq	("mm6",$R);
+
+	&mov	($a,&wparam(1));
+	&mov	($b,&wparam(3));
+	&xor	($a,&wparam(2));
+	&xor	($b,&wparam(4));
+	&call	("_mul_1x1_mmx");	# (a0+a1)·(b0+b1)
+	&pxor	($R,"mm7");
+	&mov	($a,&wparam(0));
+	&pxor	($R,"mm6");		# (a0+a1)·(b0+b1)-a1·b1-a0·b0
+
+	&movq	($A,$R);
+	&psllq	($R,32);
+	&pop	("edi");
+	&psrlq	($A,32);
+	&pop	("esi");
+	&pxor	($R,"mm6");
+	&pop	("ebx");
+	&pxor	($A,"mm7");
+	&movq	(&QWP(0,$a),$R);
+	&pop	("ebp");
+	&movq	(&QWP(8,$a),$A);
+	&emms	();
+	&ret	();
+&set_label("ialu",16);
+}
+	&push	("ebp");
+	&push	("ebx");
+	&push	("esi");
+	&push	("edi");
+	&stack_push(4+1);
+
+	&mov	($a,&wparam(1));
+	&mov	($b,&wparam(3));
+	&call	("_mul_1x1_ialu");	# a1·b1
+	&mov	(&DWP(8,"esp"),$lo);
+	&mov	(&DWP(12,"esp"),$hi);
+
+	&mov	($a,&wparam(2));
+	&mov	($b,&wparam(4));
+	&call	("_mul_1x1_ialu");	# a0·b0
+	&mov	(&DWP(0,"esp"),$lo);
+	&mov	(&DWP(4,"esp"),$hi);
+
+	&mov	($a,&wparam(1));
+	&mov	($b,&wparam(3));
+	&xor	($a,&wparam(2));
+	&xor	($b,&wparam(4));
+	&call	("_mul_1x1_ialu");	# (a0+a1)·(b0+b1)
+
+	&mov	("ebp",&wparam(0));
+		 @r=("ebx","ecx","edi","esi");
+	&mov	(@r[0],&DWP(0,"esp"));
+	&mov	(@r[1],&DWP(4,"esp"));
+	&mov	(@r[2],&DWP(8,"esp"));
+	&mov	(@r[3],&DWP(12,"esp"));
+
+	&xor	($lo,$hi);
+	&xor	($hi,@r[1]);
+	&xor	($lo,@r[0]);
+	&mov	(&DWP(0,"ebp"),@r[0]);
+	&xor	($hi,@r[2]);
+	&mov	(&DWP(12,"ebp"),@r[3]);
+	&xor	($lo,@r[3]);
+	&stack_pop(4+1);
+	&xor	($hi,@r[3]);
+	&pop	("edi");
+	&xor	($lo,$hi);
+	&pop	("esi");
+	&mov	(&DWP(8,"ebp"),$hi);
+	&pop	("ebx");
+	&mov	(&DWP(4,"ebp"),$lo);
+	&pop	("ebp");
+	&ret	();
+&function_end_B("bn_GF2m_mul_2x2");
+
+&asciz	("GF(2^m) Multiplication for x86, CRYPTOGAMS by ");
+
+&asm_finish();
+
+close STDOUT;
diff --git a/crypto/bn/asm/x86-mont.pl b/crypto/bn/asm/x86-mont.pl
new file mode 100755
index 000000000..678750366
--- /dev/null
+++ b/crypto/bn/asm/x86-mont.pl
@@ -0,0 +1,629 @@
+#! /usr/bin/env perl
+# Copyright 2005-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+
+# ====================================================================
+# Written by Andy Polyakov  for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+
+# October 2005
+#
+# This is a "teaser" code, as it can be improved in several ways...
+# First of all non-SSE2 path should be implemented (yes, for now it
+# performs Montgomery multiplication/convolution only on SSE2-capable
+# CPUs such as P4, others fall down to original code). Then inner loop
+# can be unrolled and modulo-scheduled to improve ILP and possibly
+# moved to 128-bit XMM register bank (though it would require input
+# rearrangement and/or increase bus bandwidth utilization). Dedicated
+# squaring procedure should give further performance improvement...
+# Yet, for being draft, the code improves rsa512 *sign* benchmark by
+# 110%(!), rsa1024 one - by 70% and rsa4096 - by 20%:-)
+
+# December 2006
+#
+# Modulo-scheduling SSE2 loops results in further 15-20% improvement.
+# Integer-only code [being equipped with dedicated squaring procedure]
+# gives ~40% on rsa512 sign benchmark...
+
+$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+push(@INC,"${dir}","${dir}../../perlasm");
+require "x86asm.pl";
+
+$output = pop;
+open STDOUT,">$output";
+ 
+&asm_init($ARGV[0],$0);
+
+$sse2=0;
+for (@ARGV) { $sse2=1 if (/-DOPENSSL_IA32_SSE2/); }
+
+&external_label("OPENSSL_ia32cap_P") if ($sse2);
+
+&function_begin("bn_mul_mont");
+
+$i="edx";
+$j="ecx";
+$ap="esi";	$tp="esi";		# overlapping variables!!!
+$rp="edi";	$bp="edi";		# overlapping variables!!!
+$np="ebp";
+$num="ebx";
+
+$_num=&DWP(4*0,"esp");			# stack top layout
+$_rp=&DWP(4*1,"esp");
+$_ap=&DWP(4*2,"esp");
+$_bp=&DWP(4*3,"esp");
+$_np=&DWP(4*4,"esp");
+$_n0=&DWP(4*5,"esp");	$_n0q=&QWP(4*5,"esp");
+$_sp=&DWP(4*6,"esp");
+$_bpend=&DWP(4*7,"esp");
+$frame=32;				# size of above frame rounded up to 16n
+
+	&xor	("eax","eax");
+	&mov	("edi",&wparam(5));	# int num
+	&cmp	("edi",4);
+	&jl	(&label("just_leave"));
+
+	&lea	("esi",&wparam(0));	# put aside pointer to argument block
+	&lea	("edx",&wparam(1));	# load ap
+	&add	("edi",2);		# extra two words on top of tp
+	&neg	("edi");
+	&lea	("ebp",&DWP(-$frame,"esp","edi",4));	# future alloca($frame+4*(num+2))
+	&neg	("edi");
+
+	# minimize cache contention by arraning 2K window between stack
+	# pointer and ap argument [np is also position sensitive vector,
+	# but it's assumed to be near ap, as it's allocated at ~same
+	# time].
+	&mov	("eax","ebp");
+	&sub	("eax","edx");
+	&and	("eax",2047);
+	&sub	("ebp","eax");		# this aligns sp and ap modulo 2048
+
+	&xor	("edx","ebp");
+	&and	("edx",2048);
+	&xor	("edx",2048);
+	&sub	("ebp","edx");		# this splits them apart modulo 4096
+
+	&and	("ebp",-64);		# align to cache line
+
+	# An OS-agnostic version of __chkstk.
+	#
+	# Some OSes (Windows) insist on stack being "wired" to
+	# physical memory in strictly sequential manner, i.e. if stack
+	# allocation spans two pages, then reference to farmost one can
+	# be punishable by SEGV. But page walking can do good even on
+	# other OSes, because it guarantees that villain thread hits
+	# the guard page before it can make damage to innocent one...
+	&mov	("eax","esp");
+	&sub	("eax","ebp");
+	&and	("eax",-4096);
+	&mov	("edx","esp");		# saved stack pointer!
+	&lea	("esp",&DWP(0,"ebp","eax"));
+	&mov	("eax",&DWP(0,"esp"));
+	&cmp	("esp","ebp");
+	&ja	(&label("page_walk"));
+	&jmp	(&label("page_walk_done"));
+
+&set_label("page_walk",16);
+	&lea	("esp",&DWP(-4096,"esp"));
+	&mov	("eax",&DWP(0,"esp"));
+	&cmp	("esp","ebp");
+	&ja	(&label("page_walk"));
+&set_label("page_walk_done");
+
+	################################# load argument block...
+	&mov	("eax",&DWP(0*4,"esi"));# BN_ULONG *rp
+	&mov	("ebx",&DWP(1*4,"esi"));# const BN_ULONG *ap
+	&mov	("ecx",&DWP(2*4,"esi"));# const BN_ULONG *bp
+	&mov	("ebp",&DWP(3*4,"esi"));# const BN_ULONG *np
+	&mov	("esi",&DWP(4*4,"esi"));# const BN_ULONG *n0
+	#&mov	("edi",&DWP(5*4,"esi"));# int num
+
+	&mov	("esi",&DWP(0,"esi"));	# pull n0[0]
+	&mov	($_rp,"eax");		# ... save a copy of argument block
+	&mov	($_ap,"ebx");
+	&mov	($_bp,"ecx");
+	&mov	($_np,"ebp");
+	&mov	($_n0,"esi");
+	&lea	($num,&DWP(-3,"edi"));	# num=num-1 to assist modulo-scheduling
+	#&mov	($_num,$num);		# redundant as $num is not reused
+	&mov	($_sp,"edx");		# saved stack pointer!
+
+if($sse2) {
+$acc0="mm0";	# mmx register bank layout
+$acc1="mm1";
+$car0="mm2";
+$car1="mm3";
+$mul0="mm4";
+$mul1="mm5";
+$temp="mm6";
+$mask="mm7";
+
+	&picmeup("eax","OPENSSL_ia32cap_P");
+	&bt	(&DWP(0,"eax"),26);
+	&jnc	(&label("non_sse2"));
+
+	&mov	("eax",-1);
+	&movd	($mask,"eax");		# mask 32 lower bits
+
+	&mov	($ap,$_ap);		# load input pointers
+	&mov	($bp,$_bp);
+	&mov	($np,$_np);
+
+	&xor	($i,$i);		# i=0
+	&xor	($j,$j);		# j=0
+
+	&movd	($mul0,&DWP(0,$bp));		# bp[0]
+	&movd	($mul1,&DWP(0,$ap));		# ap[0]
+	&movd	($car1,&DWP(0,$np));		# np[0]
+
+	&pmuludq($mul1,$mul0);			# ap[0]*bp[0]
+	&movq	($car0,$mul1);
+	&movq	($acc0,$mul1);			# I wish movd worked for
+	&pand	($acc0,$mask);			# inter-register transfers
+
+	&pmuludq($mul1,$_n0q);			# *=n0
+
+	&pmuludq($car1,$mul1);			# "t[0]"*np[0]*n0
+	&paddq	($car1,$acc0);
+
+	&movd	($acc1,&DWP(4,$np));		# np[1]
+	&movd	($acc0,&DWP(4,$ap));		# ap[1]
+
+	&psrlq	($car0,32);
+	&psrlq	($car1,32);
+
+	&inc	($j);				# j++
+&set_label("1st",16);
+	&pmuludq($acc0,$mul0);			# ap[j]*bp[0]
+	&pmuludq($acc1,$mul1);			# np[j]*m1
+	&paddq	($car0,$acc0);			# +=c0
+	&paddq	($car1,$acc1);			# +=c1
+
+	&movq	($acc0,$car0);
+	&pand	($acc0,$mask);
+	&movd	($acc1,&DWP(4,$np,$j,4));	# np[j+1]
+	&paddq	($car1,$acc0);			# +=ap[j]*bp[0];
+	&movd	($acc0,&DWP(4,$ap,$j,4));	# ap[j+1]
+	&psrlq	($car0,32);
+	&movd	(&DWP($frame-4,"esp",$j,4),$car1);	# tp[j-1]=
+	&psrlq	($car1,32);
+
+	&lea	($j,&DWP(1,$j));
+	&cmp	($j,$num);
+	&jl	(&label("1st"));
+
+	&pmuludq($acc0,$mul0);			# ap[num-1]*bp[0]
+	&pmuludq($acc1,$mul1);			# np[num-1]*m1
+	&paddq	($car0,$acc0);			# +=c0
+	&paddq	($car1,$acc1);			# +=c1
+
+	&movq	($acc0,$car0);
+	&pand	($acc0,$mask);
+	&paddq	($car1,$acc0);			# +=ap[num-1]*bp[0];
+	&movd	(&DWP($frame-4,"esp",$j,4),$car1);	# tp[num-2]=
+
+	&psrlq	($car0,32);
+	&psrlq	($car1,32);
+
+	&paddq	($car1,$car0);
+	&movq	(&QWP($frame,"esp",$num,4),$car1);	# tp[num].tp[num-1]
+
+	&inc	($i);				# i++
+&set_label("outer");
+	&xor	($j,$j);			# j=0
+
+	&movd	($mul0,&DWP(0,$bp,$i,4));	# bp[i]
+	&movd	($mul1,&DWP(0,$ap));		# ap[0]
+	&movd	($temp,&DWP($frame,"esp"));	# tp[0]
+	&movd	($car1,&DWP(0,$np));		# np[0]
+	&pmuludq($mul1,$mul0);			# ap[0]*bp[i]
+
+	&paddq	($mul1,$temp);			# +=tp[0]
+	&movq	($acc0,$mul1);
+	&movq	($car0,$mul1);
+	&pand	($acc0,$mask);
+
+	&pmuludq($mul1,$_n0q);			# *=n0
+
+	&pmuludq($car1,$mul1);
+	&paddq	($car1,$acc0);
+
+	&movd	($temp,&DWP($frame+4,"esp"));	# tp[1]
+	&movd	($acc1,&DWP(4,$np));		# np[1]
+	&movd	($acc0,&DWP(4,$ap));		# ap[1]
+
+	&psrlq	($car0,32);
+	&psrlq	($car1,32);
+	&paddq	($car0,$temp);			# +=tp[1]
+
+	&inc	($j);				# j++
+	&dec	($num);
+&set_label("inner");
+	&pmuludq($acc0,$mul0);			# ap[j]*bp[i]
+	&pmuludq($acc1,$mul1);			# np[j]*m1
+	&paddq	($car0,$acc0);			# +=c0
+	&paddq	($car1,$acc1);			# +=c1
+
+	&movq	($acc0,$car0);
+	&movd	($temp,&DWP($frame+4,"esp",$j,4));# tp[j+1]
+	&pand	($acc0,$mask);
+	&movd	($acc1,&DWP(4,$np,$j,4));	# np[j+1]
+	&paddq	($car1,$acc0);			# +=ap[j]*bp[i]+tp[j]
+	&movd	($acc0,&DWP(4,$ap,$j,4));	# ap[j+1]
+	&psrlq	($car0,32);
+	&movd	(&DWP($frame-4,"esp",$j,4),$car1);# tp[j-1]=
+	&psrlq	($car1,32);
+	&paddq	($car0,$temp);			# +=tp[j+1]
+
+	&dec	($num);
+	&lea	($j,&DWP(1,$j));		# j++
+	&jnz	(&label("inner"));
+
+	&mov	($num,$j);
+	&pmuludq($acc0,$mul0);			# ap[num-1]*bp[i]
+	&pmuludq($acc1,$mul1);			# np[num-1]*m1
+	&paddq	($car0,$acc0);			# +=c0
+	&paddq	($car1,$acc1);			# +=c1
+
+	&movq	($acc0,$car0);
+	&pand	($acc0,$mask);
+	&paddq	($car1,$acc0);			# +=ap[num-1]*bp[i]+tp[num-1]
+	&movd	(&DWP($frame-4,"esp",$j,4),$car1);	# tp[num-2]=
+	&psrlq	($car0,32);
+	&psrlq	($car1,32);
+
+	&movd	($temp,&DWP($frame+4,"esp",$num,4));	# += tp[num]
+	&paddq	($car1,$car0);
+	&paddq	($car1,$temp);
+	&movq	(&QWP($frame,"esp",$num,4),$car1);	# tp[num].tp[num-1]
+
+	&lea	($i,&DWP(1,$i));		# i++
+	&cmp	($i,$num);
+	&jle	(&label("outer"));
+
+	&emms	();				# done with mmx bank
+	&jmp	(&label("common_tail"));
+
+&set_label("non_sse2",16);
+}
+
+if (0) {
+	&mov	("esp",$_sp);
+	&xor	("eax","eax");	# signal "not fast enough [yet]"
+	&jmp	(&label("just_leave"));
+	# While the below code provides competitive performance for
+	# all key lengths on modern Intel cores, it's still more
+	# than 10% slower for 4096-bit key elsewhere:-( "Competitive"
+	# means compared to the original integer-only assembler.
+	# 512-bit RSA sign is better by ~40%, but that's about all
+	# one can say about all CPUs...
+} else {
+$inp="esi";	# integer path uses these registers differently
+$word="edi";
+$carry="ebp";
+
+	&mov	($inp,$_ap);
+	&lea	($carry,&DWP(1,$num));
+	&mov	($word,$_bp);
+	&xor	($j,$j);				# j=0
+	&mov	("edx",$inp);
+	&and	($carry,1);				# see if num is even
+	&sub	("edx",$word);				# see if ap==bp
+	&lea	("eax",&DWP(4,$word,$num,4));		# &bp[num]
+	&or	($carry,"edx");
+	&mov	($word,&DWP(0,$word));			# bp[0]
+	&jz	(&label("bn_sqr_mont"));
+	&mov	($_bpend,"eax");
+	&mov	("eax",&DWP(0,$inp));
+	&xor	("edx","edx");
+
+&set_label("mull",16);
+	&mov	($carry,"edx");
+	&mul	($word);				# ap[j]*bp[0]
+	&add	($carry,"eax");
+	&lea	($j,&DWP(1,$j));
+	&adc	("edx",0);
+	&mov	("eax",&DWP(0,$inp,$j,4));		# ap[j+1]
+	&cmp	($j,$num);
+	&mov	(&DWP($frame-4,"esp",$j,4),$carry);	# tp[j]=
+	&jl	(&label("mull"));
+
+	&mov	($carry,"edx");
+	&mul	($word);				# ap[num-1]*bp[0]
+	 &mov	($word,$_n0);
+	&add	("eax",$carry);
+	 &mov	($inp,$_np);
+	&adc	("edx",0);
+	 &imul	($word,&DWP($frame,"esp"));		# n0*tp[0]
+
+	&mov	(&DWP($frame,"esp",$num,4),"eax");	# tp[num-1]=
+	&xor	($j,$j);
+	&mov	(&DWP($frame+4,"esp",$num,4),"edx");	# tp[num]=
+	&mov	(&DWP($frame+8,"esp",$num,4),$j);	# tp[num+1]=
+
+	&mov	("eax",&DWP(0,$inp));			# np[0]
+	&mul	($word);				# np[0]*m
+	&add	("eax",&DWP($frame,"esp"));		# +=tp[0]
+	&mov	("eax",&DWP(4,$inp));			# np[1]
+	&adc	("edx",0);
+	&inc	($j);
+
+	&jmp	(&label("2ndmadd"));
+
+&set_label("1stmadd",16);
+	&mov	($carry,"edx");
+	&mul	($word);				# ap[j]*bp[i]
+	&add	($carry,&DWP($frame,"esp",$j,4));	# +=tp[j]
+	&lea	($j,&DWP(1,$j));
+	&adc	("edx",0);
+	&add	($carry,"eax");
+	&mov	("eax",&DWP(0,$inp,$j,4));		# ap[j+1]
+	&adc	("edx",0);
+	&cmp	($j,$num);
+	&mov	(&DWP($frame-4,"esp",$j,4),$carry);	# tp[j]=
+	&jl	(&label("1stmadd"));
+
+	&mov	($carry,"edx");
+	&mul	($word);				# ap[num-1]*bp[i]
+	&add	("eax",&DWP($frame,"esp",$num,4));	# +=tp[num-1]
+	 &mov	($word,$_n0);
+	&adc	("edx",0);
+	 &mov	($inp,$_np);
+	&add	($carry,"eax");
+	&adc	("edx",0);
+	 &imul	($word,&DWP($frame,"esp"));		# n0*tp[0]
+
+	&xor	($j,$j);
+	&add	("edx",&DWP($frame+4,"esp",$num,4));	# carry+=tp[num]
+	&mov	(&DWP($frame,"esp",$num,4),$carry);	# tp[num-1]=
+	&adc	($j,0);
+	 &mov	("eax",&DWP(0,$inp));			# np[0]
+	&mov	(&DWP($frame+4,"esp",$num,4),"edx");	# tp[num]=
+	&mov	(&DWP($frame+8,"esp",$num,4),$j);	# tp[num+1]=
+
+	&mul	($word);				# np[0]*m
+	&add	("eax",&DWP($frame,"esp"));		# +=tp[0]
+	&mov	("eax",&DWP(4,$inp));			# np[1]
+	&adc	("edx",0);
+	&mov	($j,1);
+
+&set_label("2ndmadd",16);
+	&mov	($carry,"edx");
+	&mul	($word);				# np[j]*m
+	&add	($carry,&DWP($frame,"esp",$j,4));	# +=tp[j]
+	&lea	($j,&DWP(1,$j));
+	&adc	("edx",0);
+	&add	($carry,"eax");
+	&mov	("eax",&DWP(0,$inp,$j,4));		# np[j+1]
+	&adc	("edx",0);
+	&cmp	($j,$num);
+	&mov	(&DWP($frame-8,"esp",$j,4),$carry);	# tp[j-1]=
+	&jl	(&label("2ndmadd"));
+
+	&mov	($carry,"edx");
+	&mul	($word);				# np[j]*m
+	&add	($carry,&DWP($frame,"esp",$num,4));	# +=tp[num-1]
+	&adc	("edx",0);
+	&add	($carry,"eax");
+	&adc	("edx",0);
+	&mov	(&DWP($frame-4,"esp",$num,4),$carry);	# tp[num-2]=
+
+	&xor	("eax","eax");
+	 &mov	($j,$_bp);				# &bp[i]
+	&add	("edx",&DWP($frame+4,"esp",$num,4));	# carry+=tp[num]
+	&adc	("eax",&DWP($frame+8,"esp",$num,4));	# +=tp[num+1]
+	 &lea	($j,&DWP(4,$j));
+	&mov	(&DWP($frame,"esp",$num,4),"edx");	# tp[num-1]=
+	 &cmp	($j,$_bpend);
+	&mov	(&DWP($frame+4,"esp",$num,4),"eax");	# tp[num]=
+	&je	(&label("common_tail"));
+
+	&mov	($word,&DWP(0,$j));			# bp[i+1]
+	&mov	($inp,$_ap);
+	&mov	($_bp,$j);				# &bp[++i]
+	&xor	($j,$j);
+	&xor	("edx","edx");
+	&mov	("eax",&DWP(0,$inp));
+	&jmp	(&label("1stmadd"));
+
+&set_label("bn_sqr_mont",16);
+$sbit=$num;
+	&mov	($_num,$num);
+	&mov	($_bp,$j);				# i=0
+
+	&mov	("eax",$word);				# ap[0]
+	&mul	($word);				# ap[0]*ap[0]
+	&mov	(&DWP($frame,"esp"),"eax");		# tp[0]=
+	&mov	($sbit,"edx");
+	&shr	("edx",1);
+	&and	($sbit,1);
+	&inc	($j);
+&set_label("sqr",16);
+	&mov	("eax",&DWP(0,$inp,$j,4));		# ap[j]
+	&mov	($carry,"edx");
+	&mul	($word);				# ap[j]*ap[0]
+	&add	("eax",$carry);
+	&lea	($j,&DWP(1,$j));
+	&adc	("edx",0);
+	&lea	($carry,&DWP(0,$sbit,"eax",2));
+	&shr	("eax",31);
+	&cmp	($j,$_num);
+	&mov	($sbit,"eax");
+	&mov	(&DWP($frame-4,"esp",$j,4),$carry);	# tp[j]=
+	&jl	(&label("sqr"));
+
+	&mov	("eax",&DWP(0,$inp,$j,4));		# ap[num-1]
+	&mov	($carry,"edx");
+	&mul	($word);				# ap[num-1]*ap[0]
+	&add	("eax",$carry);
+	 &mov	($word,$_n0);
+	&adc	("edx",0);
+	 &mov	($inp,$_np);
+	&lea	($carry,&DWP(0,$sbit,"eax",2));
+	 &imul	($word,&DWP($frame,"esp"));		# n0*tp[0]
+	&shr	("eax",31);
+	&mov	(&DWP($frame,"esp",$j,4),$carry);	# tp[num-1]=
+
+	&lea	($carry,&DWP(0,"eax","edx",2));
+	 &mov	("eax",&DWP(0,$inp));			# np[0]
+	&shr	("edx",31);
+	&mov	(&DWP($frame+4,"esp",$j,4),$carry);	# tp[num]=
+	&mov	(&DWP($frame+8,"esp",$j,4),"edx");	# tp[num+1]=
+
+	&mul	($word);				# np[0]*m
+	&add	("eax",&DWP($frame,"esp"));		# +=tp[0]
+	&mov	($num,$j);
+	&adc	("edx",0);
+	&mov	("eax",&DWP(4,$inp));			# np[1]
+	&mov	($j,1);
+
+&set_label("3rdmadd",16);
+	&mov	($carry,"edx");
+	&mul	($word);				# np[j]*m
+	&add	($carry,&DWP($frame,"esp",$j,4));	# +=tp[j]
+	&adc	("edx",0);
+	&add	($carry,"eax");
+	&mov	("eax",&DWP(4,$inp,$j,4));		# np[j+1]
+	&adc	("edx",0);
+	&mov	(&DWP($frame-4,"esp",$j,4),$carry);	# tp[j-1]=
+
+	&mov	($carry,"edx");
+	&mul	($word);				# np[j+1]*m
+	&add	($carry,&DWP($frame+4,"esp",$j,4));	# +=tp[j+1]
+	&lea	($j,&DWP(2,$j));
+	&adc	("edx",0);
+	&add	($carry,"eax");
+	&mov	("eax",&DWP(0,$inp,$j,4));		# np[j+2]
+	&adc	("edx",0);
+	&cmp	($j,$num);
+	&mov	(&DWP($frame-8,"esp",$j,4),$carry);	# tp[j]=
+	&jl	(&label("3rdmadd"));
+
+	&mov	($carry,"edx");
+	&mul	($word);				# np[j]*m
+	&add	($carry,&DWP($frame,"esp",$num,4));	# +=tp[num-1]
+	&adc	("edx",0);
+	&add	($carry,"eax");
+	&adc	("edx",0);
+	&mov	(&DWP($frame-4,"esp",$num,4),$carry);	# tp[num-2]=
+
+	&mov	($j,$_bp);				# i
+	&xor	("eax","eax");
+	&mov	($inp,$_ap);
+	&add	("edx",&DWP($frame+4,"esp",$num,4));	# carry+=tp[num]
+	&adc	("eax",&DWP($frame+8,"esp",$num,4));	# +=tp[num+1]
+	&mov	(&DWP($frame,"esp",$num,4),"edx");	# tp[num-1]=
+	&cmp	($j,$num);
+	&mov	(&DWP($frame+4,"esp",$num,4),"eax");	# tp[num]=
+	&je	(&label("common_tail"));
+
+	&mov	($word,&DWP(4,$inp,$j,4));		# ap[i]
+	&lea	($j,&DWP(1,$j));
+	&mov	("eax",$word);
+	&mov	($_bp,$j);				# ++i
+	&mul	($word);				# ap[i]*ap[i]
+	&add	("eax",&DWP($frame,"esp",$j,4));	# +=tp[i]
+	&adc	("edx",0);
+	&mov	(&DWP($frame,"esp",$j,4),"eax");	# tp[i]=
+	&xor	($carry,$carry);
+	&cmp	($j,$num);
+	&lea	($j,&DWP(1,$j));
+	&je	(&label("sqrlast"));
+
+	&mov	($sbit,"edx");				# zaps $num
+	&shr	("edx",1);
+	&and	($sbit,1);
+&set_label("sqradd",16);
+	&mov	("eax",&DWP(0,$inp,$j,4));		# ap[j]
+	&mov	($carry,"edx");
+	&mul	($word);				# ap[j]*ap[i]
+	&add	("eax",$carry);
+	&lea	($carry,&DWP(0,"eax","eax"));
+	&adc	("edx",0);
+	&shr	("eax",31);
+	&add	($carry,&DWP($frame,"esp",$j,4));	# +=tp[j]
+	&lea	($j,&DWP(1,$j));
+	&adc	("eax",0);
+	&add	($carry,$sbit);
+	&adc	("eax",0);
+	&cmp	($j,$_num);
+	&mov	(&DWP($frame-4,"esp",$j,4),$carry);	# tp[j]=
+	&mov	($sbit,"eax");
+	&jle	(&label("sqradd"));
+
+	&mov	($carry,"edx");
+	&add	("edx","edx");
+	&shr	($carry,31);
+	&add	("edx",$sbit);
+	&adc	($carry,0);
+&set_label("sqrlast");
+	&mov	($word,$_n0);
+	&mov	($inp,$_np);
+	&imul	($word,&DWP($frame,"esp"));		# n0*tp[0]
+
+	&add	("edx",&DWP($frame,"esp",$j,4));	# +=tp[num]
+	&mov	("eax",&DWP(0,$inp));			# np[0]
+	&adc	($carry,0);
+	&mov	(&DWP($frame,"esp",$j,4),"edx");	# tp[num]=
+	&mov	(&DWP($frame+4,"esp",$j,4),$carry);	# tp[num+1]=
+
+	&mul	($word);				# np[0]*m
+	&add	("eax",&DWP($frame,"esp"));		# +=tp[0]
+	&lea	($num,&DWP(-1,$j));
+	&adc	("edx",0);
+	&mov	($j,1);
+	&mov	("eax",&DWP(4,$inp));			# np[1]
+
+	&jmp	(&label("3rdmadd"));
+}
+
+&set_label("common_tail",16);
+	&mov	($np,$_np);			# load modulus pointer
+	&mov	($rp,$_rp);			# load result pointer
+	&lea	($tp,&DWP($frame,"esp"));	# [$ap and $bp are zapped]
+
+	&mov	("eax",&DWP(0,$tp));		# tp[0]
+	&mov	($j,$num);			# j=num-1
+	&xor	($i,$i);			# i=0 and clear CF!
+
+&set_label("sub",16);
+	&sbb	("eax",&DWP(0,$np,$i,4));
+	&mov	(&DWP(0,$rp,$i,4),"eax");	# rp[i]=tp[i]-np[i]
+	&dec	($j);				# doesn't affect CF!
+	&mov	("eax",&DWP(4,$tp,$i,4));	# tp[i+1]
+	&lea	($i,&DWP(1,$i));		# i++
+	&jge	(&label("sub"));
+
+	&sbb	("eax",0);			# handle upmost overflow bit
+	&and	($tp,"eax");
+	¬	("eax");
+	&mov	($np,$rp);
+	&and	($np,"eax");
+	&or	($tp,$np);			# tp=carry?tp:rp
+
+&set_label("copy",16);				# copy or in-place refresh
+	&mov	("eax",&DWP(0,$tp,$num,4));
+	&mov	(&DWP(0,$rp,$num,4),"eax");	# rp[i]=tp[i]
+	&mov	(&DWP($frame,"esp",$num,4),$j);	# zap temporary vector
+	&dec	($num);
+	&jge	(&label("copy"));
+
+	&mov	("esp",$_sp);		# pull saved stack pointer
+	&mov	("eax",1);
+&set_label("just_leave");
+&function_end("bn_mul_mont");
+
+&asciz("Montgomery Multiplication for x86, CRYPTOGAMS by ");
+
+&asm_finish();
+
+close STDOUT;
diff --git a/crypto/bn/asm/x86.pl b/crypto/bn/asm/x86.pl
new file mode 100644
index 000000000..d57571db5
--- /dev/null
+++ b/crypto/bn/asm/x86.pl
@@ -0,0 +1,38 @@
+#! /usr/bin/env perl
+# Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+push(@INC,"perlasm","../../perlasm");
+require "x86asm.pl";
+
+require("x86/mul_add.pl");
+require("x86/mul.pl");
+require("x86/sqr.pl");
+require("x86/div.pl");
+require("x86/add.pl");
+require("x86/sub.pl");
+require("x86/comba.pl");
+
+$output = pop;
+open STDOUT,">$output";
+
+&asm_init($ARGV[0],$0);
+
+&bn_mul_add_words("bn_mul_add_words");
+&bn_mul_words("bn_mul_words");
+&bn_sqr_words("bn_sqr_words");
+&bn_div_words("bn_div_words");
+&bn_add_words("bn_add_words");
+&bn_sub_words("bn_sub_words");
+&bn_mul_comba("bn_mul_comba8",8);
+&bn_mul_comba("bn_mul_comba4",4);
+&bn_sqr_comba("bn_sqr_comba8",8);
+&bn_sqr_comba("bn_sqr_comba4",4);
+
+&asm_finish();
+
+close STDOUT;
diff --git a/crypto/bn/asm/x86_64-gcc.c b/crypto/bn/asm/x86_64-gcc.c
new file mode 100644
index 000000000..4f19abe43
--- /dev/null
+++ b/crypto/bn/asm/x86_64-gcc.c
@@ -0,0 +1,647 @@
+/*
+ * Copyright 2002-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "../bn_lcl.h"
+#if !(defined(__GNUC__) && __GNUC__>=2)
+# include "../bn_asm.c"         /* kind of dirty hack for Sun Studio */
+#else
+/*-
+ * x86_64 BIGNUM accelerator version 0.1, December 2002.
+ *
+ * Implemented by Andy Polyakov  for the OpenSSL
+ * project.
+ *
+ * Rights for redistribution and usage in source and binary forms are
+ * granted according to the OpenSSL license. Warranty of any kind is
+ * disclaimed.
+ *
+ * Q. Version 0.1? It doesn't sound like Andy, he used to assign real
+ *    versions, like 1.0...
+ * A. Well, that's because this code is basically a quick-n-dirty
+ *    proof-of-concept hack. As you can see it's implemented with
+ *    inline assembler, which means that you're bound to GCC and that
+ *    there might be enough room for further improvement.
+ *
+ * Q. Why inline assembler?
+ * A. x86_64 features own ABI which I'm not familiar with. This is
+ *    why I decided to let the compiler take care of subroutine
+ *    prologue/epilogue as well as register allocation. For reference.
+ *    Win64 implements different ABI for AMD64, different from Linux.
+ *
+ * Q. How much faster does it get?
+ * A. 'apps/openssl speed rsa dsa' output with no-asm:
+ *
+ *                        sign    verify    sign/s verify/s
+ *      rsa  512 bits   0.0006s   0.0001s   1683.8  18456.2
+ *      rsa 1024 bits   0.0028s   0.0002s    356.0   6407.0
+ *      rsa 2048 bits   0.0172s   0.0005s     58.0   1957.8
+ *      rsa 4096 bits   0.1155s   0.0018s      8.7    555.6
+ *                        sign    verify    sign/s verify/s
+ *      dsa  512 bits   0.0005s   0.0006s   2100.8   1768.3
+ *      dsa 1024 bits   0.0014s   0.0018s    692.3    559.2
+ *      dsa 2048 bits   0.0049s   0.0061s    204.7    165.0
+ *
+ *    'apps/openssl speed rsa dsa' output with this module:
+ *
+ *                        sign    verify    sign/s verify/s
+ *      rsa  512 bits   0.0004s   0.0000s   2767.1  33297.9
+ *      rsa 1024 bits   0.0012s   0.0001s    867.4  14674.7
+ *      rsa 2048 bits   0.0061s   0.0002s    164.0   5270.0
+ *      rsa 4096 bits   0.0384s   0.0006s     26.1   1650.8
+ *                        sign    verify    sign/s verify/s
+ *      dsa  512 bits   0.0002s   0.0003s   4442.2   3786.3
+ *      dsa 1024 bits   0.0005s   0.0007s   1835.1   1497.4
+ *      dsa 2048 bits   0.0016s   0.0020s    620.4    504.6
+ *
+ *    For the reference. IA-32 assembler implementation performs
+ *    very much like 64-bit code compiled with no-asm on the same
+ *    machine.
+ */
+
+# if defined(_WIN64) || !defined(__LP64__)
+#  define BN_ULONG unsigned long long
+# else
+#  define BN_ULONG unsigned long
+# endif
+
+# undef mul
+# undef mul_add
+
+/*-
+ * "m"(a), "+m"(r)      is the way to favor DirectPath µ-code;
+ * "g"(0)               let the compiler to decide where does it
+ *                      want to keep the value of zero;
+ */
+# define mul_add(r,a,word,carry) do {   \
+        register BN_ULONG high,low;     \
+        asm ("mulq %3"                  \
+                : "=a"(low),"=d"(high)  \
+                : "a"(word),"m"(a)      \
+                : "cc");                \
+        asm ("addq %2,%0; adcq %3,%1"   \
+                : "+r"(carry),"+d"(high)\
+                : "a"(low),"g"(0)       \
+                : "cc");                \
+        asm ("addq %2,%0; adcq %3,%1"   \
+                : "+m"(r),"+d"(high)    \
+                : "r"(carry),"g"(0)     \
+                : "cc");                \
+        carry=high;                     \
+        } while (0)
+
+# define mul(r,a,word,carry) do {       \
+        register BN_ULONG high,low;     \
+        asm ("mulq %3"                  \
+                : "=a"(low),"=d"(high)  \
+                : "a"(word),"g"(a)      \
+                : "cc");                \
+        asm ("addq %2,%0; adcq %3,%1"   \
+                : "+r"(carry),"+d"(high)\
+                : "a"(low),"g"(0)       \
+                : "cc");                \
+        (r)=carry, carry=high;          \
+        } while (0)
+# undef sqr
+# define sqr(r0,r1,a)                   \
+        asm ("mulq %2"                  \
+                : "=a"(r0),"=d"(r1)     \
+                : "a"(a)                \
+                : "cc");
+
+BN_ULONG bn_mul_add_words(BN_ULONG *rp, const BN_ULONG *ap, int num,
+                          BN_ULONG w)
+{
+    BN_ULONG c1 = 0;
+
+    if (num <= 0)
+        return (c1);
+
+    while (num & ~3) {
+        mul_add(rp[0], ap[0], w, c1);
+        mul_add(rp[1], ap[1], w, c1);
+        mul_add(rp[2], ap[2], w, c1);
+        mul_add(rp[3], ap[3], w, c1);
+        ap += 4;
+        rp += 4;
+        num -= 4;
+    }
+    if (num) {
+        mul_add(rp[0], ap[0], w, c1);
+        if (--num == 0)
+            return c1;
+        mul_add(rp[1], ap[1], w, c1);
+        if (--num == 0)
+            return c1;
+        mul_add(rp[2], ap[2], w, c1);
+        return c1;
+    }
+
+    return (c1);
+}
+
+BN_ULONG bn_mul_words(BN_ULONG *rp, const BN_ULONG *ap, int num, BN_ULONG w)
+{
+    BN_ULONG c1 = 0;
+
+    if (num <= 0)
+        return (c1);
+
+    while (num & ~3) {
+        mul(rp[0], ap[0], w, c1);
+        mul(rp[1], ap[1], w, c1);
+        mul(rp[2], ap[2], w, c1);
+        mul(rp[3], ap[3], w, c1);
+        ap += 4;
+        rp += 4;
+        num -= 4;
+    }
+    if (num) {
+        mul(rp[0], ap[0], w, c1);
+        if (--num == 0)
+            return c1;
+        mul(rp[1], ap[1], w, c1);
+        if (--num == 0)
+            return c1;
+        mul(rp[2], ap[2], w, c1);
+    }
+    return (c1);
+}
+
+void bn_sqr_words(BN_ULONG *r, const BN_ULONG *a, int n)
+{
+    if (n <= 0)
+        return;
+
+    while (n & ~3) {
+        sqr(r[0], r[1], a[0]);
+        sqr(r[2], r[3], a[1]);
+        sqr(r[4], r[5], a[2]);
+        sqr(r[6], r[7], a[3]);
+        a += 4;
+        r += 8;
+        n -= 4;
+    }
+    if (n) {
+        sqr(r[0], r[1], a[0]);
+        if (--n == 0)
+            return;
+        sqr(r[2], r[3], a[1]);
+        if (--n == 0)
+            return;
+        sqr(r[4], r[5], a[2]);
+    }
+}
+
+BN_ULONG bn_div_words(BN_ULONG h, BN_ULONG l, BN_ULONG d)
+{
+    BN_ULONG ret, waste;
+
+ asm("divq      %4":"=a"(ret), "=d"(waste)
+ :     "a"(l), "d"(h), "r"(d)
+ :     "cc");
+
+    return ret;
+}
+
+BN_ULONG bn_add_words(BN_ULONG *rp, const BN_ULONG *ap, const BN_ULONG *bp,
+                      int n)
+{
+    BN_ULONG ret;
+    size_t i = 0;
+
+    if (n <= 0)
+        return 0;
+
+    asm volatile ("       subq    %0,%0           \n" /* clear carry */
+                  "       jmp     1f              \n"
+                  ".p2align 4                     \n"
+                  "1:     movq    (%4,%2,8),%0    \n"
+                  "       adcq    (%5,%2,8),%0    \n"
+                  "       movq    %0,(%3,%2,8)    \n"
+                  "       lea     1(%2),%2        \n"
+                  "       loop    1b              \n"
+                  "       sbbq    %0,%0           \n":"=&r" (ret), "+c"(n),
+                  "+r"(i)
+                  :"r"(rp), "r"(ap), "r"(bp)
+                  :"cc", "memory");
+
+    return ret & 1;
+}
+
+# ifndef SIMICS
+BN_ULONG bn_sub_words(BN_ULONG *rp, const BN_ULONG *ap, const BN_ULONG *bp,
+                      int n)
+{
+    BN_ULONG ret;
+    size_t i = 0;
+
+    if (n <= 0)
+        return 0;
+
+    asm volatile ("       subq    %0,%0           \n" /* clear borrow */
+                  "       jmp     1f              \n"
+                  ".p2align 4                     \n"
+                  "1:     movq    (%4,%2,8),%0    \n"
+                  "       sbbq    (%5,%2,8),%0    \n"
+                  "       movq    %0,(%3,%2,8)    \n"
+                  "       lea     1(%2),%2        \n"
+                  "       loop    1b              \n"
+                  "       sbbq    %0,%0           \n":"=&r" (ret), "+c"(n),
+                  "+r"(i)
+                  :"r"(rp), "r"(ap), "r"(bp)
+                  :"cc", "memory");
+
+    return ret & 1;
+}
+# else
+/* Simics 1.4<7 has buggy sbbq:-( */
+#  define BN_MASK2 0xffffffffffffffffL
+BN_ULONG bn_sub_words(BN_ULONG *r, BN_ULONG *a, BN_ULONG *b, int n)
+{
+    BN_ULONG t1, t2;
+    int c = 0;
+
+    if (n <= 0)
+        return ((BN_ULONG)0);
+
+    for (;;) {
+        t1 = a[0];
+        t2 = b[0];
+        r[0] = (t1 - t2 - c) & BN_MASK2;
+        if (t1 != t2)
+            c = (t1 < t2);
+        if (--n <= 0)
+            break;
+
+        t1 = a[1];
+        t2 = b[1];
+        r[1] = (t1 - t2 - c) & BN_MASK2;
+        if (t1 != t2)
+            c = (t1 < t2);
+        if (--n <= 0)
+            break;
+
+        t1 = a[2];
+        t2 = b[2];
+        r[2] = (t1 - t2 - c) & BN_MASK2;
+        if (t1 != t2)
+            c = (t1 < t2);
+        if (--n <= 0)
+            break;
+
+        t1 = a[3];
+        t2 = b[3];
+        r[3] = (t1 - t2 - c) & BN_MASK2;
+        if (t1 != t2)
+            c = (t1 < t2);
+        if (--n <= 0)
+            break;
+
+        a += 4;
+        b += 4;
+        r += 4;
+    }
+    return (c);
+}
+# endif
+
+/* mul_add_c(a,b,c0,c1,c2)  -- c+=a*b for three word number c=(c2,c1,c0) */
+/* mul_add_c2(a,b,c0,c1,c2) -- c+=2*a*b for three word number c=(c2,c1,c0) */
+/* sqr_add_c(a,i,c0,c1,c2)  -- c+=a[i]^2 for three word number c=(c2,c1,c0) */
+/*
+ * sqr_add_c2(a,i,c0,c1,c2) -- c+=2*a[i]*a[j] for three word number
+ * c=(c2,c1,c0)
+ */
+
+/*
+ * Keep in mind that carrying into high part of multiplication result
+ * can not overflow, because it cannot be all-ones.
+ */
+# if 0
+/* original macros are kept for reference purposes */
+#  define mul_add_c(a,b,c0,c1,c2)       do {    \
+        BN_ULONG ta = (a), tb = (b);            \
+        BN_ULONG lo, hi;                        \
+        BN_UMULT_LOHI(lo,hi,ta,tb);             \
+        c0 += lo; hi += (c0 for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+#
+# May 2011
+#
+# The module implements bn_GF2m_mul_2x2 polynomial multiplication used
+# in bn_gf2m.c. It's kind of low-hanging mechanical port from C for
+# the time being... Except that it has two code paths: code suitable
+# for any x86_64 CPU and PCLMULQDQ one suitable for Westmere and
+# later. Improvement varies from one benchmark and µ-arch to another.
+# Vanilla code path is at most 20% faster than compiler-generated code
+# [not very impressive], while PCLMULQDQ - whole 85%-160% better on
+# 163- and 571-bit ECDH benchmarks on Intel CPUs. Keep in mind that
+# these coefficients are not ones for bn_GF2m_mul_2x2 itself, as not
+# all CPU time is burnt in it...
+
+$flavour = shift;
+$output  = shift;
+if ($flavour =~ /\./) { $output = $flavour; undef $flavour; }
+
+$win64=0; $win64=1 if ($flavour =~ /[nm]asm|mingw64/ || $output =~ /\.asm$/);
+
+$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+( $xlate="${dir}x86_64-xlate.pl" and -f $xlate ) or
+( $xlate="${dir}../../perlasm/x86_64-xlate.pl" and -f $xlate) or
+die "can't locate x86_64-xlate.pl";
+
+open OUT,"| \"$^X\" \"$xlate\" $flavour \"$output\"";
+*STDOUT=*OUT;
+
+($lo,$hi)=("%rax","%rdx");	$a=$lo;
+($i0,$i1)=("%rsi","%rdi");
+($t0,$t1)=("%rbx","%rcx");
+($b,$mask)=("%rbp","%r8");
+($a1,$a2,$a4,$a8,$a12,$a48)=map("%r$_",(9..15));
+($R,$Tx)=("%xmm0","%xmm1");
+
+$code.=<<___;
+.text
+
+.type	_mul_1x1,\@abi-omnipotent
+.align	16
+_mul_1x1:
+	sub	\$128+8,%rsp
+	mov	\$-1,$a1
+	lea	($a,$a),$i0
+	shr	\$3,$a1
+	lea	(,$a,4),$i1
+	and	$a,$a1			# a1=a&0x1fffffffffffffff
+	lea	(,$a,8),$a8
+	sar	\$63,$a			# broadcast 63rd bit
+	lea	($a1,$a1),$a2
+	sar	\$63,$i0		# broadcast 62nd bit
+	lea	(,$a1,4),$a4
+	and	$b,$a
+	sar	\$63,$i1		# boardcast 61st bit
+	mov	$a,$hi			# $a is $lo
+	shl	\$63,$lo
+	and	$b,$i0
+	shr	\$1,$hi
+	mov	$i0,$t1
+	shl	\$62,$i0
+	and	$b,$i1
+	shr	\$2,$t1
+	xor	$i0,$lo
+	mov	$i1,$t0
+	shl	\$61,$i1
+	xor	$t1,$hi
+	shr	\$3,$t0
+	xor	$i1,$lo
+	xor	$t0,$hi
+
+	mov	$a1,$a12
+	movq	\$0,0(%rsp)		# tab[0]=0
+	xor	$a2,$a12		# a1^a2
+	mov	$a1,8(%rsp)		# tab[1]=a1
+	 mov	$a4,$a48
+	mov	$a2,16(%rsp)		# tab[2]=a2
+	 xor	$a8,$a48		# a4^a8
+	mov	$a12,24(%rsp)		# tab[3]=a1^a2
+
+	xor	$a4,$a1
+	mov	$a4,32(%rsp)		# tab[4]=a4
+	xor	$a4,$a2
+	mov	$a1,40(%rsp)		# tab[5]=a1^a4
+	xor	$a4,$a12
+	mov	$a2,48(%rsp)		# tab[6]=a2^a4
+	 xor	$a48,$a1		# a1^a4^a4^a8=a1^a8
+	mov	$a12,56(%rsp)		# tab[7]=a1^a2^a4
+	 xor	$a48,$a2		# a2^a4^a4^a8=a1^a8
+
+	mov	$a8,64(%rsp)		# tab[8]=a8
+	xor	$a48,$a12		# a1^a2^a4^a4^a8=a1^a2^a8
+	mov	$a1,72(%rsp)		# tab[9]=a1^a8
+	 xor	$a4,$a1			# a1^a8^a4
+	mov	$a2,80(%rsp)		# tab[10]=a2^a8
+	 xor	$a4,$a2			# a2^a8^a4
+	mov	$a12,88(%rsp)		# tab[11]=a1^a2^a8
+
+	xor	$a4,$a12		# a1^a2^a8^a4
+	mov	$a48,96(%rsp)		# tab[12]=a4^a8
+	 mov	$mask,$i0
+	mov	$a1,104(%rsp)		# tab[13]=a1^a4^a8
+	 and	$b,$i0
+	mov	$a2,112(%rsp)		# tab[14]=a2^a4^a8
+	 shr	\$4,$b
+	mov	$a12,120(%rsp)		# tab[15]=a1^a2^a4^a8
+	 mov	$mask,$i1
+	 and	$b,$i1
+	 shr	\$4,$b
+
+	movq	(%rsp,$i0,8),$R		# half of calculations is done in SSE2
+	mov	$mask,$i0
+	and	$b,$i0
+	shr	\$4,$b
+___
+    for ($n=1;$n<8;$n++) {
+	$code.=<<___;
+	mov	(%rsp,$i1,8),$t1
+	mov	$mask,$i1
+	mov	$t1,$t0
+	shl	\$`8*$n-4`,$t1
+	and	$b,$i1
+	 movq	(%rsp,$i0,8),$Tx
+	shr	\$`64-(8*$n-4)`,$t0
+	xor	$t1,$lo
+	 pslldq	\$$n,$Tx
+	 mov	$mask,$i0
+	shr	\$4,$b
+	xor	$t0,$hi
+	 and	$b,$i0
+	 shr	\$4,$b
+	 pxor	$Tx,$R
+___
+    }
+$code.=<<___;
+	mov	(%rsp,$i1,8),$t1
+	mov	$t1,$t0
+	shl	\$`8*$n-4`,$t1
+	movq	$R,$i0
+	shr	\$`64-(8*$n-4)`,$t0
+	xor	$t1,$lo
+	psrldq	\$8,$R
+	xor	$t0,$hi
+	movq	$R,$i1
+	xor	$i0,$lo
+	xor	$i1,$hi
+
+	add	\$128+8,%rsp
+	ret
+.Lend_mul_1x1:
+.size	_mul_1x1,.-_mul_1x1
+___
+
+($rp,$a1,$a0,$b1,$b0) = $win64?	("%rcx","%rdx","%r8", "%r9","%r10") :	# Win64 order
+				("%rdi","%rsi","%rdx","%rcx","%r8");	# Unix order
+
+$code.=<<___;
+.extern	OPENSSL_ia32cap_P
+.globl	bn_GF2m_mul_2x2
+.type	bn_GF2m_mul_2x2,\@abi-omnipotent
+.align	16
+bn_GF2m_mul_2x2:
+	mov	OPENSSL_ia32cap_P(%rip),%rax
+	bt	\$33,%rax
+	jnc	.Lvanilla_mul_2x2
+
+	movq		$a1,%xmm0
+	movq		$b1,%xmm1
+	movq		$a0,%xmm2
+___
+$code.=<<___ if ($win64);
+	movq		40(%rsp),%xmm3
+___
+$code.=<<___ if (!$win64);
+	movq		$b0,%xmm3
+___
+$code.=<<___;
+	movdqa		%xmm0,%xmm4
+	movdqa		%xmm1,%xmm5
+	pclmulqdq	\$0,%xmm1,%xmm0	# a1·b1
+	pxor		%xmm2,%xmm4
+	pxor		%xmm3,%xmm5
+	pclmulqdq	\$0,%xmm3,%xmm2	# a0·b0
+	pclmulqdq	\$0,%xmm5,%xmm4	# (a0+a1)·(b0+b1)
+	xorps		%xmm0,%xmm4
+	xorps		%xmm2,%xmm4	# (a0+a1)·(b0+b1)-a0·b0-a1·b1
+	movdqa		%xmm4,%xmm5
+	pslldq		\$8,%xmm4
+	psrldq		\$8,%xmm5
+	pxor		%xmm4,%xmm2
+	pxor		%xmm5,%xmm0
+	movdqu		%xmm2,0($rp)
+	movdqu		%xmm0,16($rp)
+	ret
+
+.align	16
+.Lvanilla_mul_2x2:
+	lea	-8*17(%rsp),%rsp
+___
+$code.=<<___ if ($win64);
+	mov	`8*17+40`(%rsp),$b0
+	mov	%rdi,8*15(%rsp)
+	mov	%rsi,8*16(%rsp)
+___
+$code.=<<___;
+	mov	%r14,8*10(%rsp)
+	mov	%r13,8*11(%rsp)
+	mov	%r12,8*12(%rsp)
+	mov	%rbp,8*13(%rsp)
+	mov	%rbx,8*14(%rsp)
+.Lbody_mul_2x2:
+	mov	$rp,32(%rsp)		# save the arguments
+	mov	$a1,40(%rsp)
+	mov	$a0,48(%rsp)
+	mov	$b1,56(%rsp)
+	mov	$b0,64(%rsp)
+
+	mov	\$0xf,$mask
+	mov	$a1,$a
+	mov	$b1,$b
+	call	_mul_1x1		# a1·b1
+	mov	$lo,16(%rsp)
+	mov	$hi,24(%rsp)
+
+	mov	48(%rsp),$a
+	mov	64(%rsp),$b
+	call	_mul_1x1		# a0·b0
+	mov	$lo,0(%rsp)
+	mov	$hi,8(%rsp)
+
+	mov	40(%rsp),$a
+	mov	56(%rsp),$b
+	xor	48(%rsp),$a
+	xor	64(%rsp),$b
+	call	_mul_1x1		# (a0+a1)·(b0+b1)
+___
+	@r=("%rbx","%rcx","%rdi","%rsi");
+$code.=<<___;
+	mov	0(%rsp),@r[0]
+	mov	8(%rsp),@r[1]
+	mov	16(%rsp),@r[2]
+	mov	24(%rsp),@r[3]
+	mov	32(%rsp),%rbp
+
+	xor	$hi,$lo
+	xor	@r[1],$hi
+	xor	@r[0],$lo
+	mov	@r[0],0(%rbp)
+	xor	@r[2],$hi
+	mov	@r[3],24(%rbp)
+	xor	@r[3],$lo
+	xor	@r[3],$hi
+	xor	$hi,$lo
+	mov	$hi,16(%rbp)
+	mov	$lo,8(%rbp)
+
+	mov	8*10(%rsp),%r14
+	mov	8*11(%rsp),%r13
+	mov	8*12(%rsp),%r12
+	mov	8*13(%rsp),%rbp
+	mov	8*14(%rsp),%rbx
+___
+$code.=<<___ if ($win64);
+	mov	8*15(%rsp),%rdi
+	mov	8*16(%rsp),%rsi
+___
+$code.=<<___;
+	lea	8*17(%rsp),%rsp
+	ret
+.Lend_mul_2x2:
+.size	bn_GF2m_mul_2x2,.-bn_GF2m_mul_2x2
+.asciz	"GF(2^m) Multiplication for x86_64, CRYPTOGAMS by "
+.align	16
+___
+
+# EXCEPTION_DISPOSITION handler (EXCEPTION_RECORD *rec,ULONG64 frame,
+#               CONTEXT *context,DISPATCHER_CONTEXT *disp)
+if ($win64) {
+$rec="%rcx";
+$frame="%rdx";
+$context="%r8";
+$disp="%r9";
+
+$code.=<<___;
+.extern __imp_RtlVirtualUnwind
+
+.type	se_handler,\@abi-omnipotent
+.align	16
+se_handler:
+	push	%rsi
+	push	%rdi
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+	pushfq
+	sub	\$64,%rsp
+
+	mov	152($context),%rax	# pull context->Rsp
+	mov	248($context),%rbx	# pull context->Rip
+
+	lea	.Lbody_mul_2x2(%rip),%r10
+	cmp	%r10,%rbx		# context->Rip<"prologue" label
+	jb	.Lin_prologue
+
+	mov	8*10(%rax),%r14		# mimic epilogue
+	mov	8*11(%rax),%r13
+	mov	8*12(%rax),%r12
+	mov	8*13(%rax),%rbp
+	mov	8*14(%rax),%rbx
+	mov	8*15(%rax),%rdi
+	mov	8*16(%rax),%rsi
+
+	mov	%rbx,144($context)	# restore context->Rbx
+	mov	%rbp,160($context)	# restore context->Rbp
+	mov	%rsi,168($context)	# restore context->Rsi
+	mov	%rdi,176($context)	# restore context->Rdi
+	mov	%r12,216($context)	# restore context->R12
+	mov	%r13,224($context)	# restore context->R13
+	mov	%r14,232($context)	# restore context->R14
+
+.Lin_prologue:
+	lea	8*17(%rax),%rax
+	mov	%rax,152($context)	# restore context->Rsp
+
+	mov	40($disp),%rdi		# disp->ContextRecord
+	mov	$context,%rsi		# context
+	mov	\$154,%ecx		# sizeof(CONTEXT)
+	.long	0xa548f3fc		# cld; rep movsq
+
+	mov	$disp,%rsi
+	xor	%rcx,%rcx		# arg1, UNW_FLAG_NHANDLER
+	mov	8(%rsi),%rdx		# arg2, disp->ImageBase
+	mov	0(%rsi),%r8		# arg3, disp->ControlPc
+	mov	16(%rsi),%r9		# arg4, disp->FunctionEntry
+	mov	40(%rsi),%r10		# disp->ContextRecord
+	lea	56(%rsi),%r11		# &disp->HandlerData
+	lea	24(%rsi),%r12		# &disp->EstablisherFrame
+	mov	%r10,32(%rsp)		# arg5
+	mov	%r11,40(%rsp)		# arg6
+	mov	%r12,48(%rsp)		# arg7
+	mov	%rcx,56(%rsp)		# arg8, (NULL)
+	call	*__imp_RtlVirtualUnwind(%rip)
+
+	mov	\$1,%eax		# ExceptionContinueSearch
+	add	\$64,%rsp
+	popfq
+	pop	%r15
+	pop	%r14
+	pop	%r13
+	pop	%r12
+	pop	%rbp
+	pop	%rbx
+	pop	%rdi
+	pop	%rsi
+	ret
+.size	se_handler,.-se_handler
+
+.section	.pdata
+.align	4
+	.rva	_mul_1x1
+	.rva	.Lend_mul_1x1
+	.rva	.LSEH_info_1x1
+
+	.rva	.Lvanilla_mul_2x2
+	.rva	.Lend_mul_2x2
+	.rva	.LSEH_info_2x2
+.section	.xdata
+.align	8
+.LSEH_info_1x1:
+	.byte	0x01,0x07,0x02,0x00
+	.byte	0x07,0x01,0x11,0x00	# sub rsp,128+8
+.LSEH_info_2x2:
+	.byte	9,0,0,0
+	.rva	se_handler
+___
+}
+
+$code =~ s/\`([^\`]*)\`/eval($1)/gem;
+print $code;
+close STDOUT;
diff --git a/crypto/bn/asm/x86_64-mont.pl b/crypto/bn/asm/x86_64-mont.pl
new file mode 100755
index 000000000..df4cca5bf
--- /dev/null
+++ b/crypto/bn/asm/x86_64-mont.pl
@@ -0,0 +1,1521 @@
+#! /usr/bin/env perl
+# Copyright 2005-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+
+# ====================================================================
+# Written by Andy Polyakov  for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+
+# October 2005.
+#
+# Montgomery multiplication routine for x86_64. While it gives modest
+# 9% improvement of rsa4096 sign on Opteron, rsa512 sign runs more
+# than twice, >2x, as fast. Most common rsa1024 sign is improved by
+# respectful 50%. It remains to be seen if loop unrolling and
+# dedicated squaring routine can provide further improvement...
+
+# July 2011.
+#
+# Add dedicated squaring procedure. Performance improvement varies
+# from platform to platform, but in average it's ~5%/15%/25%/33%
+# for 512-/1024-/2048-/4096-bit RSA *sign* benchmarks respectively.
+
+# August 2011.
+#
+# Unroll and modulo-schedule inner loops in such manner that they
+# are "fallen through" for input lengths of 8, which is critical for
+# 1024-bit RSA *sign*. Average performance improvement in comparison
+# to *initial* version of this module from 2005 is ~0%/30%/40%/45%
+# for 512-/1024-/2048-/4096-bit RSA *sign* benchmarks respectively.
+
+# June 2013.
+#
+# Optimize reduction in squaring procedure and improve 1024+-bit RSA
+# sign performance by 10-16% on Intel Sandy Bridge and later
+# (virtually same on non-Intel processors).
+
+# August 2013.
+#
+# Add MULX/ADOX/ADCX code path.
+
+$flavour = shift;
+$output  = shift;
+if ($flavour =~ /\./) { $output = $flavour; undef $flavour; }
+
+$win64=0; $win64=1 if ($flavour =~ /[nm]asm|mingw64/ || $output =~ /\.asm$/);
+
+$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+( $xlate="${dir}x86_64-xlate.pl" and -f $xlate ) or
+( $xlate="${dir}../../perlasm/x86_64-xlate.pl" and -f $xlate) or
+die "can't locate x86_64-xlate.pl";
+
+open OUT,"| \"$^X\" \"$xlate\" $flavour \"$output\"";
+*STDOUT=*OUT;
+
+if (`$ENV{CC} -Wa,-v -c -o /dev/null -x assembler /dev/null 2>&1`
+		=~ /GNU assembler version ([2-9]\.[0-9]+)/) {
+	$addx = ($1>=2.23);
+}
+
+if (!$addx && $win64 && ($flavour =~ /nasm/ || $ENV{ASM} =~ /nasm/) &&
+	    `nasm -v 2>&1` =~ /NASM version ([2-9]\.[0-9]+)/) {
+	$addx = ($1>=2.10);
+}
+
+if (!$addx && $win64 && ($flavour =~ /masm/ || $ENV{ASM} =~ /ml64/) &&
+	    `ml64 2>&1` =~ /Version ([0-9]+)\./) {
+	$addx = ($1>=12);
+}
+
+if (!$addx && `$ENV{CC} -v 2>&1` =~ /((?:^clang|LLVM) version|.*based on LLVM) ([3-9])\.([0-9]+)/) {
+	my $ver = $2 + $3/100.0;	# 3.1->3.01, 3.10->3.10
+	$addx = ($ver>=3.03);
+}
+
+# int bn_mul_mont(
+$rp="%rdi";	# BN_ULONG *rp,
+$ap="%rsi";	# const BN_ULONG *ap,
+$bp="%rdx";	# const BN_ULONG *bp,
+$np="%rcx";	# const BN_ULONG *np,
+$n0="%r8";	# const BN_ULONG *n0,
+$num="%r9";	# int num);
+$lo0="%r10";
+$hi0="%r11";
+$hi1="%r13";
+$i="%r14";
+$j="%r15";
+$m0="%rbx";
+$m1="%rbp";
+
+$code=<<___;
+.text
+
+.extern	OPENSSL_ia32cap_P
+
+.globl	bn_mul_mont
+.type	bn_mul_mont,\@function,6
+.align	16
+bn_mul_mont:
+	mov	${num}d,${num}d
+	mov	%rsp,%rax
+	test	\$3,${num}d
+	jnz	.Lmul_enter
+	cmp	\$8,${num}d
+	jb	.Lmul_enter
+___
+$code.=<<___ if ($addx);
+	mov	OPENSSL_ia32cap_P+8(%rip),%r11d
+___
+$code.=<<___;
+	cmp	$ap,$bp
+	jne	.Lmul4x_enter
+	test	\$7,${num}d
+	jz	.Lsqr8x_enter
+	jmp	.Lmul4x_enter
+
+.align	16
+.Lmul_enter:
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+
+	neg	$num
+	mov	%rsp,%r11
+	lea	-16(%rsp,$num,8),%r10	# future alloca(8*(num+2))
+	neg	$num			# restore $num
+	and	\$-1024,%r10		# minimize TLB usage
+
+	# An OS-agnostic version of __chkstk.
+	#
+	# Some OSes (Windows) insist on stack being "wired" to
+	# physical memory in strictly sequential manner, i.e. if stack
+	# allocation spans two pages, then reference to farmost one can
+	# be punishable by SEGV. But page walking can do good even on
+	# other OSes, because it guarantees that villain thread hits
+	# the guard page before it can make damage to innocent one...
+	sub	%r10,%r11
+	and	\$-4096,%r11
+	lea	(%r10,%r11),%rsp
+	mov	(%rsp),%r11
+	cmp	%r10,%rsp
+	ja	.Lmul_page_walk
+	jmp	.Lmul_page_walk_done
+
+.align	16
+.Lmul_page_walk:
+	lea	-4096(%rsp),%rsp
+	mov	(%rsp),%r11
+	cmp	%r10,%rsp
+	ja	.Lmul_page_walk
+.Lmul_page_walk_done:
+
+	mov	%rax,8(%rsp,$num,8)	# tp[num+1]=%rsp
+.Lmul_body:
+	mov	$bp,%r12		# reassign $bp
+___
+		$bp="%r12";
+$code.=<<___;
+	mov	($n0),$n0		# pull n0[0] value
+	mov	($bp),$m0		# m0=bp[0]
+	mov	($ap),%rax
+
+	xor	$i,$i			# i=0
+	xor	$j,$j			# j=0
+
+	mov	$n0,$m1
+	mulq	$m0			# ap[0]*bp[0]
+	mov	%rax,$lo0
+	mov	($np),%rax
+
+	imulq	$lo0,$m1		# "tp[0]"*n0
+	mov	%rdx,$hi0
+
+	mulq	$m1			# np[0]*m1
+	add	%rax,$lo0		# discarded
+	mov	8($ap),%rax
+	adc	\$0,%rdx
+	mov	%rdx,$hi1
+
+	lea	1($j),$j		# j++
+	jmp	.L1st_enter
+
+.align	16
+.L1st:
+	add	%rax,$hi1
+	mov	($ap,$j,8),%rax
+	adc	\$0,%rdx
+	add	$hi0,$hi1		# np[j]*m1+ap[j]*bp[0]
+	mov	$lo0,$hi0
+	adc	\$0,%rdx
+	mov	$hi1,-16(%rsp,$j,8)	# tp[j-1]
+	mov	%rdx,$hi1
+
+.L1st_enter:
+	mulq	$m0			# ap[j]*bp[0]
+	add	%rax,$hi0
+	mov	($np,$j,8),%rax
+	adc	\$0,%rdx
+	lea	1($j),$j		# j++
+	mov	%rdx,$lo0
+
+	mulq	$m1			# np[j]*m1
+	cmp	$num,$j
+	jne	.L1st
+
+	add	%rax,$hi1
+	mov	($ap),%rax		# ap[0]
+	adc	\$0,%rdx
+	add	$hi0,$hi1		# np[j]*m1+ap[j]*bp[0]
+	adc	\$0,%rdx
+	mov	$hi1,-16(%rsp,$j,8)	# tp[j-1]
+	mov	%rdx,$hi1
+	mov	$lo0,$hi0
+
+	xor	%rdx,%rdx
+	add	$hi0,$hi1
+	adc	\$0,%rdx
+	mov	$hi1,-8(%rsp,$num,8)
+	mov	%rdx,(%rsp,$num,8)	# store upmost overflow bit
+
+	lea	1($i),$i		# i++
+	jmp	.Louter
+.align	16
+.Louter:
+	mov	($bp,$i,8),$m0		# m0=bp[i]
+	xor	$j,$j			# j=0
+	mov	$n0,$m1
+	mov	(%rsp),$lo0
+	mulq	$m0			# ap[0]*bp[i]
+	add	%rax,$lo0		# ap[0]*bp[i]+tp[0]
+	mov	($np),%rax
+	adc	\$0,%rdx
+
+	imulq	$lo0,$m1		# tp[0]*n0
+	mov	%rdx,$hi0
+
+	mulq	$m1			# np[0]*m1
+	add	%rax,$lo0		# discarded
+	mov	8($ap),%rax
+	adc	\$0,%rdx
+	mov	8(%rsp),$lo0		# tp[1]
+	mov	%rdx,$hi1
+
+	lea	1($j),$j		# j++
+	jmp	.Linner_enter
+
+.align	16
+.Linner:
+	add	%rax,$hi1
+	mov	($ap,$j,8),%rax
+	adc	\$0,%rdx
+	add	$lo0,$hi1		# np[j]*m1+ap[j]*bp[i]+tp[j]
+	mov	(%rsp,$j,8),$lo0
+	adc	\$0,%rdx
+	mov	$hi1,-16(%rsp,$j,8)	# tp[j-1]
+	mov	%rdx,$hi1
+
+.Linner_enter:
+	mulq	$m0			# ap[j]*bp[i]
+	add	%rax,$hi0
+	mov	($np,$j,8),%rax
+	adc	\$0,%rdx
+	add	$hi0,$lo0		# ap[j]*bp[i]+tp[j]
+	mov	%rdx,$hi0
+	adc	\$0,$hi0
+	lea	1($j),$j		# j++
+
+	mulq	$m1			# np[j]*m1
+	cmp	$num,$j
+	jne	.Linner
+
+	add	%rax,$hi1
+	mov	($ap),%rax		# ap[0]
+	adc	\$0,%rdx
+	add	$lo0,$hi1		# np[j]*m1+ap[j]*bp[i]+tp[j]
+	mov	(%rsp,$j,8),$lo0
+	adc	\$0,%rdx
+	mov	$hi1,-16(%rsp,$j,8)	# tp[j-1]
+	mov	%rdx,$hi1
+
+	xor	%rdx,%rdx
+	add	$hi0,$hi1
+	adc	\$0,%rdx
+	add	$lo0,$hi1		# pull upmost overflow bit
+	adc	\$0,%rdx
+	mov	$hi1,-8(%rsp,$num,8)
+	mov	%rdx,(%rsp,$num,8)	# store upmost overflow bit
+
+	lea	1($i),$i		# i++
+	cmp	$num,$i
+	jb	.Louter
+
+	xor	$i,$i			# i=0 and clear CF!
+	mov	(%rsp),%rax		# tp[0]
+	lea	(%rsp),$ap		# borrow ap for tp
+	mov	$num,$j			# j=num
+	jmp	.Lsub
+.align	16
+.Lsub:	sbb	($np,$i,8),%rax
+	mov	%rax,($rp,$i,8)		# rp[i]=tp[i]-np[i]
+	mov	8($ap,$i,8),%rax	# tp[i+1]
+	lea	1($i),$i		# i++
+	dec	$j			# doesnn't affect CF!
+	jnz	.Lsub
+
+	sbb	\$0,%rax		# handle upmost overflow bit
+	xor	$i,$i
+	and	%rax,$ap
+	not	%rax
+	mov	$rp,$np
+	and	%rax,$np
+	mov	$num,$j			# j=num
+	or	$np,$ap			# ap=borrow?tp:rp
+.align	16
+.Lcopy:					# copy or in-place refresh
+	mov	($ap,$i,8),%rax
+	mov	$i,(%rsp,$i,8)		# zap temporary vector
+	mov	%rax,($rp,$i,8)		# rp[i]=tp[i]
+	lea	1($i),$i
+	sub	\$1,$j
+	jnz	.Lcopy
+
+	mov	8(%rsp,$num,8),%rsi	# restore %rsp
+	mov	\$1,%rax
+	mov	-48(%rsi),%r15
+	mov	-40(%rsi),%r14
+	mov	-32(%rsi),%r13
+	mov	-24(%rsi),%r12
+	mov	-16(%rsi),%rbp
+	mov	-8(%rsi),%rbx
+	lea	(%rsi),%rsp
+.Lmul_epilogue:
+	ret
+.size	bn_mul_mont,.-bn_mul_mont
+___
+{{{
+my @A=("%r10","%r11");
+my @N=("%r13","%rdi");
+$code.=<<___;
+.type	bn_mul4x_mont,\@function,6
+.align	16
+bn_mul4x_mont:
+	mov	${num}d,${num}d
+	mov	%rsp,%rax
+.Lmul4x_enter:
+___
+$code.=<<___ if ($addx);
+	and	\$0x80100,%r11d
+	cmp	\$0x80100,%r11d
+	je	.Lmulx4x_enter
+___
+$code.=<<___;
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+
+	neg	$num
+	mov	%rsp,%r11
+	lea	-32(%rsp,$num,8),%r10	# future alloca(8*(num+4))
+	neg	$num			# restore
+	and	\$-1024,%r10		# minimize TLB usage
+
+	sub	%r10,%r11
+	and	\$-4096,%r11
+	lea	(%r10,%r11),%rsp
+	mov	(%rsp),%r11
+	cmp	%r10,%rsp
+	ja	.Lmul4x_page_walk
+	jmp	.Lmul4x_page_walk_done
+
+.Lmul4x_page_walk:
+	lea	-4096(%rsp),%rsp
+	mov	(%rsp),%r11
+	cmp	%r10,%rsp
+	ja	.Lmul4x_page_walk
+.Lmul4x_page_walk_done:
+
+	mov	%rax,8(%rsp,$num,8)	# tp[num+1]=%rsp
+.Lmul4x_body:
+	mov	$rp,16(%rsp,$num,8)	# tp[num+2]=$rp
+	mov	%rdx,%r12		# reassign $bp
+___
+		$bp="%r12";
+$code.=<<___;
+	mov	($n0),$n0		# pull n0[0] value
+	mov	($bp),$m0		# m0=bp[0]
+	mov	($ap),%rax
+
+	xor	$i,$i			# i=0
+	xor	$j,$j			# j=0
+
+	mov	$n0,$m1
+	mulq	$m0			# ap[0]*bp[0]
+	mov	%rax,$A[0]
+	mov	($np),%rax
+
+	imulq	$A[0],$m1		# "tp[0]"*n0
+	mov	%rdx,$A[1]
+
+	mulq	$m1			# np[0]*m1
+	add	%rax,$A[0]		# discarded
+	mov	8($ap),%rax
+	adc	\$0,%rdx
+	mov	%rdx,$N[1]
+
+	mulq	$m0
+	add	%rax,$A[1]
+	mov	8($np),%rax
+	adc	\$0,%rdx
+	mov	%rdx,$A[0]
+
+	mulq	$m1
+	add	%rax,$N[1]
+	mov	16($ap),%rax
+	adc	\$0,%rdx
+	add	$A[1],$N[1]
+	lea	4($j),$j		# j++
+	adc	\$0,%rdx
+	mov	$N[1],(%rsp)
+	mov	%rdx,$N[0]
+	jmp	.L1st4x
+.align	16
+.L1st4x:
+	mulq	$m0			# ap[j]*bp[0]
+	add	%rax,$A[0]
+	mov	-16($np,$j,8),%rax
+	adc	\$0,%rdx
+	mov	%rdx,$A[1]
+
+	mulq	$m1			# np[j]*m1
+	add	%rax,$N[0]
+	mov	-8($ap,$j,8),%rax
+	adc	\$0,%rdx
+	add	$A[0],$N[0]		# np[j]*m1+ap[j]*bp[0]
+	adc	\$0,%rdx
+	mov	$N[0],-24(%rsp,$j,8)	# tp[j-1]
+	mov	%rdx,$N[1]
+
+	mulq	$m0			# ap[j]*bp[0]
+	add	%rax,$A[1]
+	mov	-8($np,$j,8),%rax
+	adc	\$0,%rdx
+	mov	%rdx,$A[0]
+
+	mulq	$m1			# np[j]*m1
+	add	%rax,$N[1]
+	mov	($ap,$j,8),%rax
+	adc	\$0,%rdx
+	add	$A[1],$N[1]		# np[j]*m1+ap[j]*bp[0]
+	adc	\$0,%rdx
+	mov	$N[1],-16(%rsp,$j,8)	# tp[j-1]
+	mov	%rdx,$N[0]
+
+	mulq	$m0			# ap[j]*bp[0]
+	add	%rax,$A[0]
+	mov	($np,$j,8),%rax
+	adc	\$0,%rdx
+	mov	%rdx,$A[1]
+
+	mulq	$m1			# np[j]*m1
+	add	%rax,$N[0]
+	mov	8($ap,$j,8),%rax
+	adc	\$0,%rdx
+	add	$A[0],$N[0]		# np[j]*m1+ap[j]*bp[0]
+	adc	\$0,%rdx
+	mov	$N[0],-8(%rsp,$j,8)	# tp[j-1]
+	mov	%rdx,$N[1]
+
+	mulq	$m0			# ap[j]*bp[0]
+	add	%rax,$A[1]
+	mov	8($np,$j,8),%rax
+	adc	\$0,%rdx
+	lea	4($j),$j		# j++
+	mov	%rdx,$A[0]
+
+	mulq	$m1			# np[j]*m1
+	add	%rax,$N[1]
+	mov	-16($ap,$j,8),%rax
+	adc	\$0,%rdx
+	add	$A[1],$N[1]		# np[j]*m1+ap[j]*bp[0]
+	adc	\$0,%rdx
+	mov	$N[1],-32(%rsp,$j,8)	# tp[j-1]
+	mov	%rdx,$N[0]
+	cmp	$num,$j
+	jb	.L1st4x
+
+	mulq	$m0			# ap[j]*bp[0]
+	add	%rax,$A[0]
+	mov	-16($np,$j,8),%rax
+	adc	\$0,%rdx
+	mov	%rdx,$A[1]
+
+	mulq	$m1			# np[j]*m1
+	add	%rax,$N[0]
+	mov	-8($ap,$j,8),%rax
+	adc	\$0,%rdx
+	add	$A[0],$N[0]		# np[j]*m1+ap[j]*bp[0]
+	adc	\$0,%rdx
+	mov	$N[0],-24(%rsp,$j,8)	# tp[j-1]
+	mov	%rdx,$N[1]
+
+	mulq	$m0			# ap[j]*bp[0]
+	add	%rax,$A[1]
+	mov	-8($np,$j,8),%rax
+	adc	\$0,%rdx
+	mov	%rdx,$A[0]
+
+	mulq	$m1			# np[j]*m1
+	add	%rax,$N[1]
+	mov	($ap),%rax		# ap[0]
+	adc	\$0,%rdx
+	add	$A[1],$N[1]		# np[j]*m1+ap[j]*bp[0]
+	adc	\$0,%rdx
+	mov	$N[1],-16(%rsp,$j,8)	# tp[j-1]
+	mov	%rdx,$N[0]
+
+	xor	$N[1],$N[1]
+	add	$A[0],$N[0]
+	adc	\$0,$N[1]
+	mov	$N[0],-8(%rsp,$j,8)
+	mov	$N[1],(%rsp,$j,8)	# store upmost overflow bit
+
+	lea	1($i),$i		# i++
+.align	4
+.Louter4x:
+	mov	($bp,$i,8),$m0		# m0=bp[i]
+	xor	$j,$j			# j=0
+	mov	(%rsp),$A[0]
+	mov	$n0,$m1
+	mulq	$m0			# ap[0]*bp[i]
+	add	%rax,$A[0]		# ap[0]*bp[i]+tp[0]
+	mov	($np),%rax
+	adc	\$0,%rdx
+
+	imulq	$A[0],$m1		# tp[0]*n0
+	mov	%rdx,$A[1]
+
+	mulq	$m1			# np[0]*m1
+	add	%rax,$A[0]		# "$N[0]", discarded
+	mov	8($ap),%rax
+	adc	\$0,%rdx
+	mov	%rdx,$N[1]
+
+	mulq	$m0			# ap[j]*bp[i]
+	add	%rax,$A[1]
+	mov	8($np),%rax
+	adc	\$0,%rdx
+	add	8(%rsp),$A[1]		# +tp[1]
+	adc	\$0,%rdx
+	mov	%rdx,$A[0]
+
+	mulq	$m1			# np[j]*m1
+	add	%rax,$N[1]
+	mov	16($ap),%rax
+	adc	\$0,%rdx
+	add	$A[1],$N[1]		# np[j]*m1+ap[j]*bp[i]+tp[j]
+	lea	4($j),$j		# j+=2
+	adc	\$0,%rdx
+	mov	$N[1],(%rsp)		# tp[j-1]
+	mov	%rdx,$N[0]
+	jmp	.Linner4x
+.align	16
+.Linner4x:
+	mulq	$m0			# ap[j]*bp[i]
+	add	%rax,$A[0]
+	mov	-16($np,$j,8),%rax
+	adc	\$0,%rdx
+	add	-16(%rsp,$j,8),$A[0]	# ap[j]*bp[i]+tp[j]
+	adc	\$0,%rdx
+	mov	%rdx,$A[1]
+
+	mulq	$m1			# np[j]*m1
+	add	%rax,$N[0]
+	mov	-8($ap,$j,8),%rax
+	adc	\$0,%rdx
+	add	$A[0],$N[0]
+	adc	\$0,%rdx
+	mov	$N[0],-24(%rsp,$j,8)	# tp[j-1]
+	mov	%rdx,$N[1]
+
+	mulq	$m0			# ap[j]*bp[i]
+	add	%rax,$A[1]
+	mov	-8($np,$j,8),%rax
+	adc	\$0,%rdx
+	add	-8(%rsp,$j,8),$A[1]
+	adc	\$0,%rdx
+	mov	%rdx,$A[0]
+
+	mulq	$m1			# np[j]*m1
+	add	%rax,$N[1]
+	mov	($ap,$j,8),%rax
+	adc	\$0,%rdx
+	add	$A[1],$N[1]
+	adc	\$0,%rdx
+	mov	$N[1],-16(%rsp,$j,8)	# tp[j-1]
+	mov	%rdx,$N[0]
+
+	mulq	$m0			# ap[j]*bp[i]
+	add	%rax,$A[0]
+	mov	($np,$j,8),%rax
+	adc	\$0,%rdx
+	add	(%rsp,$j,8),$A[0]	# ap[j]*bp[i]+tp[j]
+	adc	\$0,%rdx
+	mov	%rdx,$A[1]
+
+	mulq	$m1			# np[j]*m1
+	add	%rax,$N[0]
+	mov	8($ap,$j,8),%rax
+	adc	\$0,%rdx
+	add	$A[0],$N[0]
+	adc	\$0,%rdx
+	mov	$N[0],-8(%rsp,$j,8)	# tp[j-1]
+	mov	%rdx,$N[1]
+
+	mulq	$m0			# ap[j]*bp[i]
+	add	%rax,$A[1]
+	mov	8($np,$j,8),%rax
+	adc	\$0,%rdx
+	add	8(%rsp,$j,8),$A[1]
+	adc	\$0,%rdx
+	lea	4($j),$j		# j++
+	mov	%rdx,$A[0]
+
+	mulq	$m1			# np[j]*m1
+	add	%rax,$N[1]
+	mov	-16($ap,$j,8),%rax
+	adc	\$0,%rdx
+	add	$A[1],$N[1]
+	adc	\$0,%rdx
+	mov	$N[1],-32(%rsp,$j,8)	# tp[j-1]
+	mov	%rdx,$N[0]
+	cmp	$num,$j
+	jb	.Linner4x
+
+	mulq	$m0			# ap[j]*bp[i]
+	add	%rax,$A[0]
+	mov	-16($np,$j,8),%rax
+	adc	\$0,%rdx
+	add	-16(%rsp,$j,8),$A[0]	# ap[j]*bp[i]+tp[j]
+	adc	\$0,%rdx
+	mov	%rdx,$A[1]
+
+	mulq	$m1			# np[j]*m1
+	add	%rax,$N[0]
+	mov	-8($ap,$j,8),%rax
+	adc	\$0,%rdx
+	add	$A[0],$N[0]
+	adc	\$0,%rdx
+	mov	$N[0],-24(%rsp,$j,8)	# tp[j-1]
+	mov	%rdx,$N[1]
+
+	mulq	$m0			# ap[j]*bp[i]
+	add	%rax,$A[1]
+	mov	-8($np,$j,8),%rax
+	adc	\$0,%rdx
+	add	-8(%rsp,$j,8),$A[1]
+	adc	\$0,%rdx
+	lea	1($i),$i		# i++
+	mov	%rdx,$A[0]
+
+	mulq	$m1			# np[j]*m1
+	add	%rax,$N[1]
+	mov	($ap),%rax		# ap[0]
+	adc	\$0,%rdx
+	add	$A[1],$N[1]
+	adc	\$0,%rdx
+	mov	$N[1],-16(%rsp,$j,8)	# tp[j-1]
+	mov	%rdx,$N[0]
+
+	xor	$N[1],$N[1]
+	add	$A[0],$N[0]
+	adc	\$0,$N[1]
+	add	(%rsp,$num,8),$N[0]	# pull upmost overflow bit
+	adc	\$0,$N[1]
+	mov	$N[0],-8(%rsp,$j,8)
+	mov	$N[1],(%rsp,$j,8)	# store upmost overflow bit
+
+	cmp	$num,$i
+	jb	.Louter4x
+___
+{
+my @ri=("%rax","%rdx",$m0,$m1);
+$code.=<<___;
+	mov	16(%rsp,$num,8),$rp	# restore $rp
+	mov	0(%rsp),@ri[0]		# tp[0]
+	pxor	%xmm0,%xmm0
+	mov	8(%rsp),@ri[1]		# tp[1]
+	shr	\$2,$num		# num/=4
+	lea	(%rsp),$ap		# borrow ap for tp
+	xor	$i,$i			# i=0 and clear CF!
+
+	sub	0($np),@ri[0]
+	mov	16($ap),@ri[2]		# tp[2]
+	mov	24($ap),@ri[3]		# tp[3]
+	sbb	8($np),@ri[1]
+	lea	-1($num),$j		# j=num/4-1
+	jmp	.Lsub4x
+.align	16
+.Lsub4x:
+	mov	@ri[0],0($rp,$i,8)	# rp[i]=tp[i]-np[i]
+	mov	@ri[1],8($rp,$i,8)	# rp[i]=tp[i]-np[i]
+	sbb	16($np,$i,8),@ri[2]
+	mov	32($ap,$i,8),@ri[0]	# tp[i+1]
+	mov	40($ap,$i,8),@ri[1]
+	sbb	24($np,$i,8),@ri[3]
+	mov	@ri[2],16($rp,$i,8)	# rp[i]=tp[i]-np[i]
+	mov	@ri[3],24($rp,$i,8)	# rp[i]=tp[i]-np[i]
+	sbb	32($np,$i,8),@ri[0]
+	mov	48($ap,$i,8),@ri[2]
+	mov	56($ap,$i,8),@ri[3]
+	sbb	40($np,$i,8),@ri[1]
+	lea	4($i),$i		# i++
+	dec	$j			# doesnn't affect CF!
+	jnz	.Lsub4x
+
+	mov	@ri[0],0($rp,$i,8)	# rp[i]=tp[i]-np[i]
+	mov	32($ap,$i,8),@ri[0]	# load overflow bit
+	sbb	16($np,$i,8),@ri[2]
+	mov	@ri[1],8($rp,$i,8)	# rp[i]=tp[i]-np[i]
+	sbb	24($np,$i,8),@ri[3]
+	mov	@ri[2],16($rp,$i,8)	# rp[i]=tp[i]-np[i]
+
+	sbb	\$0,@ri[0]		# handle upmost overflow bit
+	mov	@ri[3],24($rp,$i,8)	# rp[i]=tp[i]-np[i]
+	xor	$i,$i			# i=0
+	and	@ri[0],$ap
+	not	@ri[0]
+	mov	$rp,$np
+	and	@ri[0],$np
+	lea	-1($num),$j
+	or	$np,$ap			# ap=borrow?tp:rp
+
+	movdqu	($ap),%xmm1
+	movdqa	%xmm0,(%rsp)
+	movdqu	%xmm1,($rp)
+	jmp	.Lcopy4x
+.align	16
+.Lcopy4x:					# copy or in-place refresh
+	movdqu	16($ap,$i),%xmm2
+	movdqu	32($ap,$i),%xmm1
+	movdqa	%xmm0,16(%rsp,$i)
+	movdqu	%xmm2,16($rp,$i)
+	movdqa	%xmm0,32(%rsp,$i)
+	movdqu	%xmm1,32($rp,$i)
+	lea	32($i),$i
+	dec	$j
+	jnz	.Lcopy4x
+
+	shl	\$2,$num
+	movdqu	16($ap,$i),%xmm2
+	movdqa	%xmm0,16(%rsp,$i)
+	movdqu	%xmm2,16($rp,$i)
+___
+}
+$code.=<<___;
+	mov	8(%rsp,$num,8),%rsi	# restore %rsp
+	mov	\$1,%rax
+	mov	-48(%rsi),%r15
+	mov	-40(%rsi),%r14
+	mov	-32(%rsi),%r13
+	mov	-24(%rsi),%r12
+	mov	-16(%rsi),%rbp
+	mov	-8(%rsi),%rbx
+	lea	(%rsi),%rsp
+.Lmul4x_epilogue:
+	ret
+.size	bn_mul4x_mont,.-bn_mul4x_mont
+___
+}}}
+{{{
+######################################################################
+# void bn_sqr8x_mont(
+my $rptr="%rdi";	# const BN_ULONG *rptr,
+my $aptr="%rsi";	# const BN_ULONG *aptr,
+my $bptr="%rdx";	# not used
+my $nptr="%rcx";	# const BN_ULONG *nptr,
+my $n0  ="%r8";		# const BN_ULONG *n0);
+my $num ="%r9";		# int num, has to be divisible by 8
+
+my ($i,$j,$tptr)=("%rbp","%rcx",$rptr);
+my @A0=("%r10","%r11");
+my @A1=("%r12","%r13");
+my ($a0,$a1,$ai)=("%r14","%r15","%rbx");
+
+$code.=<<___	if ($addx);
+.extern	bn_sqrx8x_internal		# see x86_64-mont5 module
+___
+$code.=<<___;
+.extern	bn_sqr8x_internal		# see x86_64-mont5 module
+
+.type	bn_sqr8x_mont,\@function,6
+.align	32
+bn_sqr8x_mont:
+	mov	%rsp,%rax
+.Lsqr8x_enter:
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+.Lsqr8x_prologue:
+
+	mov	${num}d,%r10d
+	shl	\$3,${num}d		# convert $num to bytes
+	shl	\$3+2,%r10		# 4*$num
+	neg	$num
+
+	##############################################################
+	# ensure that stack frame doesn't alias with $aptr modulo
+	# 4096. this is done to allow memory disambiguation logic
+	# do its job.
+	#
+	lea	-64(%rsp,$num,2),%r11
+	mov	%rsp,%rbp
+	mov	($n0),$n0		# *n0
+	sub	$aptr,%r11
+	and	\$4095,%r11
+	cmp	%r11,%r10
+	jb	.Lsqr8x_sp_alt
+	sub	%r11,%rbp		# align with $aptr
+	lea	-64(%rbp,$num,2),%rbp	# future alloca(frame+2*$num)
+	jmp	.Lsqr8x_sp_done
+
+.align	32
+.Lsqr8x_sp_alt:
+	lea	4096-64(,$num,2),%r10	# 4096-frame-2*$num
+	lea	-64(%rbp,$num,2),%rbp	# future alloca(frame+2*$num)
+	sub	%r10,%r11
+	mov	\$0,%r10
+	cmovc	%r10,%r11
+	sub	%r11,%rbp
+.Lsqr8x_sp_done:
+	and	\$-64,%rbp
+	mov	%rsp,%r11
+	sub	%rbp,%r11
+	and	\$-4096,%r11
+	lea	(%rbp,%r11),%rsp
+	mov	(%rsp),%r10
+	cmp	%rbp,%rsp
+	ja	.Lsqr8x_page_walk
+	jmp	.Lsqr8x_page_walk_done
+
+.align	16
+.Lsqr8x_page_walk:
+	lea	-4096(%rsp),%rsp
+	mov	(%rsp),%r10
+	cmp	%rbp,%rsp
+	ja	.Lsqr8x_page_walk
+.Lsqr8x_page_walk_done:
+
+	mov	$num,%r10
+	neg	$num
+
+	mov	$n0,  32(%rsp)
+	mov	%rax, 40(%rsp)		# save original %rsp
+.Lsqr8x_body:
+
+	movq	$nptr, %xmm2		# save pointer to modulus
+	pxor	%xmm0,%xmm0
+	movq	$rptr,%xmm1		# save $rptr
+	movq	%r10, %xmm3		# -$num
+___
+$code.=<<___ if ($addx);
+	mov	OPENSSL_ia32cap_P+8(%rip),%eax
+	and	\$0x80100,%eax
+	cmp	\$0x80100,%eax
+	jne	.Lsqr8x_nox
+
+	call	bn_sqrx8x_internal	# see x86_64-mont5 module
+					# %rax	top-most carry
+					# %rbp	nptr
+					# %rcx	-8*num
+					# %r8	end of tp[2*num]
+	lea	(%r8,%rcx),%rbx
+	mov	%rcx,$num
+	mov	%rcx,%rdx
+	movq	%xmm1,$rptr
+	sar	\$3+2,%rcx		# %cf=0
+	jmp	.Lsqr8x_sub
+
+.align	32
+.Lsqr8x_nox:
+___
+$code.=<<___;
+	call	bn_sqr8x_internal	# see x86_64-mont5 module
+					# %rax	top-most carry
+					# %rbp	nptr
+					# %r8	-8*num
+					# %rdi	end of tp[2*num]
+	lea	(%rdi,$num),%rbx
+	mov	$num,%rcx
+	mov	$num,%rdx
+	movq	%xmm1,$rptr
+	sar	\$3+2,%rcx		# %cf=0
+	jmp	.Lsqr8x_sub
+
+.align	32
+.Lsqr8x_sub:
+	mov	8*0(%rbx),%r12
+	mov	8*1(%rbx),%r13
+	mov	8*2(%rbx),%r14
+	mov	8*3(%rbx),%r15
+	lea	8*4(%rbx),%rbx
+	sbb	8*0(%rbp),%r12
+	sbb	8*1(%rbp),%r13
+	sbb	8*2(%rbp),%r14
+	sbb	8*3(%rbp),%r15
+	lea	8*4(%rbp),%rbp
+	mov	%r12,8*0($rptr)
+	mov	%r13,8*1($rptr)
+	mov	%r14,8*2($rptr)
+	mov	%r15,8*3($rptr)
+	lea	8*4($rptr),$rptr
+	inc	%rcx			# preserves %cf
+	jnz	.Lsqr8x_sub
+
+	sbb	\$0,%rax		# top-most carry
+	lea	(%rbx,$num),%rbx	# rewind
+	lea	($rptr,$num),$rptr	# rewind
+
+	movq	%rax,%xmm1
+	pxor	%xmm0,%xmm0
+	pshufd	\$0,%xmm1,%xmm1
+	mov	40(%rsp),%rsi		# restore %rsp
+	jmp	.Lsqr8x_cond_copy
+
+.align	32
+.Lsqr8x_cond_copy:
+	movdqa	16*0(%rbx),%xmm2
+	movdqa	16*1(%rbx),%xmm3
+	lea	16*2(%rbx),%rbx
+	movdqu	16*0($rptr),%xmm4
+	movdqu	16*1($rptr),%xmm5
+	lea	16*2($rptr),$rptr
+	movdqa	%xmm0,-16*2(%rbx)	# zero tp
+	movdqa	%xmm0,-16*1(%rbx)
+	movdqa	%xmm0,-16*2(%rbx,%rdx)
+	movdqa	%xmm0,-16*1(%rbx,%rdx)
+	pcmpeqd	%xmm1,%xmm0
+	pand	%xmm1,%xmm2
+	pand	%xmm1,%xmm3
+	pand	%xmm0,%xmm4
+	pand	%xmm0,%xmm5
+	pxor	%xmm0,%xmm0
+	por	%xmm2,%xmm4
+	por	%xmm3,%xmm5
+	movdqu	%xmm4,-16*2($rptr)
+	movdqu	%xmm5,-16*1($rptr)
+	add	\$32,$num
+	jnz	.Lsqr8x_cond_copy
+
+	mov	\$1,%rax
+	mov	-48(%rsi),%r15
+	mov	-40(%rsi),%r14
+	mov	-32(%rsi),%r13
+	mov	-24(%rsi),%r12
+	mov	-16(%rsi),%rbp
+	mov	-8(%rsi),%rbx
+	lea	(%rsi),%rsp
+.Lsqr8x_epilogue:
+	ret
+.size	bn_sqr8x_mont,.-bn_sqr8x_mont
+___
+}}}
+
+if ($addx) {{{
+my $bp="%rdx";	# original value
+
+$code.=<<___;
+.type	bn_mulx4x_mont,\@function,6
+.align	32
+bn_mulx4x_mont:
+	mov	%rsp,%rax
+.Lmulx4x_enter:
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+.Lmulx4x_prologue:
+
+	shl	\$3,${num}d		# convert $num to bytes
+	xor	%r10,%r10
+	sub	$num,%r10		# -$num
+	mov	($n0),$n0		# *n0
+	lea	-72(%rsp,%r10),%rbp	# future alloca(frame+$num+8)
+	and	\$-128,%rbp
+	mov	%rsp,%r11
+	sub	%rbp,%r11
+	and	\$-4096,%r11
+	lea	(%rbp,%r11),%rsp
+	mov	(%rsp),%r10
+	cmp	%rbp,%rsp
+	ja	.Lmulx4x_page_walk
+	jmp	.Lmulx4x_page_walk_done
+
+.align	16
+.Lmulx4x_page_walk:
+	lea	-4096(%rsp),%rsp
+	mov	(%rsp),%r10
+	cmp	%rbp,%rsp
+	ja	.Lmulx4x_page_walk
+.Lmulx4x_page_walk_done:
+
+	lea	($bp,$num),%r10
+	##############################################################
+	# Stack layout
+	# +0	num
+	# +8	off-loaded &b[i]
+	# +16	end of b[num]
+	# +24	saved n0
+	# +32	saved rp
+	# +40	saved %rsp
+	# +48	inner counter
+	# +56
+	# +64	tmp[num+1]
+	#
+	mov	$num,0(%rsp)		# save $num
+	shr	\$5,$num
+	mov	%r10,16(%rsp)		# end of b[num]
+	sub	\$1,$num
+	mov	$n0, 24(%rsp)		# save *n0
+	mov	$rp, 32(%rsp)		# save $rp
+	mov	%rax,40(%rsp)		# save original %rsp
+	mov	$num,48(%rsp)		# inner counter
+	jmp	.Lmulx4x_body
+
+.align	32
+.Lmulx4x_body:
+___
+my ($aptr, $bptr, $nptr, $tptr, $mi,  $bi,  $zero, $num)=
+   ("%rsi","%rdi","%rcx","%rbx","%r8","%r9","%rbp","%rax");
+my $rptr=$bptr;
+$code.=<<___;
+	lea	8($bp),$bptr
+	mov	($bp),%rdx		# b[0], $bp==%rdx actually
+	lea	64+32(%rsp),$tptr
+	mov	%rdx,$bi
+
+	mulx	0*8($aptr),$mi,%rax	# a[0]*b[0]
+	mulx	1*8($aptr),%r11,%r14	# a[1]*b[0]
+	add	%rax,%r11
+	mov	$bptr,8(%rsp)		# off-load &b[i]
+	mulx	2*8($aptr),%r12,%r13	# ...
+	adc	%r14,%r12
+	adc	\$0,%r13
+
+	mov	$mi,$bptr		# borrow $bptr
+	imulq	24(%rsp),$mi		# "t[0]"*n0
+	xor	$zero,$zero		# cf=0, of=0
+
+	mulx	3*8($aptr),%rax,%r14
+	 mov	$mi,%rdx
+	lea	4*8($aptr),$aptr
+	adcx	%rax,%r13
+	adcx	$zero,%r14		# cf=0
+
+	mulx	0*8($nptr),%rax,%r10
+	adcx	%rax,$bptr		# discarded
+	adox	%r11,%r10
+	mulx	1*8($nptr),%rax,%r11
+	adcx	%rax,%r10
+	adox	%r12,%r11
+	.byte	0xc4,0x62,0xfb,0xf6,0xa1,0x10,0x00,0x00,0x00	# mulx	2*8($nptr),%rax,%r12
+	mov	48(%rsp),$bptr		# counter value
+	mov	%r10,-4*8($tptr)
+	adcx	%rax,%r11
+	adox	%r13,%r12
+	mulx	3*8($nptr),%rax,%r15
+	 mov	$bi,%rdx
+	mov	%r11,-3*8($tptr)
+	adcx	%rax,%r12
+	adox	$zero,%r15		# of=0
+	lea	4*8($nptr),$nptr
+	mov	%r12,-2*8($tptr)
+
+	jmp	.Lmulx4x_1st
+
+.align	32
+.Lmulx4x_1st:
+	adcx	$zero,%r15		# cf=0, modulo-scheduled
+	mulx	0*8($aptr),%r10,%rax	# a[4]*b[0]
+	adcx	%r14,%r10
+	mulx	1*8($aptr),%r11,%r14	# a[5]*b[0]
+	adcx	%rax,%r11
+	mulx	2*8($aptr),%r12,%rax	# ...
+	adcx	%r14,%r12
+	mulx	3*8($aptr),%r13,%r14
+	 .byte	0x67,0x67
+	 mov	$mi,%rdx
+	adcx	%rax,%r13
+	adcx	$zero,%r14		# cf=0
+	lea	4*8($aptr),$aptr
+	lea	4*8($tptr),$tptr
+
+	adox	%r15,%r10
+	mulx	0*8($nptr),%rax,%r15
+	adcx	%rax,%r10
+	adox	%r15,%r11
+	mulx	1*8($nptr),%rax,%r15
+	adcx	%rax,%r11
+	adox	%r15,%r12
+	mulx	2*8($nptr),%rax,%r15
+	mov	%r10,-5*8($tptr)
+	adcx	%rax,%r12
+	mov	%r11,-4*8($tptr)
+	adox	%r15,%r13
+	mulx	3*8($nptr),%rax,%r15
+	 mov	$bi,%rdx
+	mov	%r12,-3*8($tptr)
+	adcx	%rax,%r13
+	adox	$zero,%r15
+	lea	4*8($nptr),$nptr
+	mov	%r13,-2*8($tptr)
+
+	dec	$bptr			# of=0, pass cf
+	jnz	.Lmulx4x_1st
+
+	mov	0(%rsp),$num		# load num
+	mov	8(%rsp),$bptr		# re-load &b[i]
+	adc	$zero,%r15		# modulo-scheduled
+	add	%r15,%r14
+	sbb	%r15,%r15		# top-most carry
+	mov	%r14,-1*8($tptr)
+	jmp	.Lmulx4x_outer
+
+.align	32
+.Lmulx4x_outer:
+	mov	($bptr),%rdx		# b[i]
+	lea	8($bptr),$bptr		# b++
+	sub	$num,$aptr		# rewind $aptr
+	mov	%r15,($tptr)		# save top-most carry
+	lea	64+4*8(%rsp),$tptr
+	sub	$num,$nptr		# rewind $nptr
+
+	mulx	0*8($aptr),$mi,%r11	# a[0]*b[i]
+	xor	%ebp,%ebp		# xor	$zero,$zero	# cf=0, of=0
+	mov	%rdx,$bi
+	mulx	1*8($aptr),%r14,%r12	# a[1]*b[i]
+	adox	-4*8($tptr),$mi
+	adcx	%r14,%r11
+	mulx	2*8($aptr),%r15,%r13	# ...
+	adox	-3*8($tptr),%r11
+	adcx	%r15,%r12
+	adox	-2*8($tptr),%r12
+	adcx	$zero,%r13
+	adox	$zero,%r13
+
+	mov	$bptr,8(%rsp)		# off-load &b[i]
+	mov	$mi,%r15
+	imulq	24(%rsp),$mi		# "t[0]"*n0
+	xor	%ebp,%ebp		# xor	$zero,$zero	# cf=0, of=0
+
+	mulx	3*8($aptr),%rax,%r14
+	 mov	$mi,%rdx
+	adcx	%rax,%r13
+	adox	-1*8($tptr),%r13
+	adcx	$zero,%r14
+	lea	4*8($aptr),$aptr
+	adox	$zero,%r14
+
+	mulx	0*8($nptr),%rax,%r10
+	adcx	%rax,%r15		# discarded
+	adox	%r11,%r10
+	mulx	1*8($nptr),%rax,%r11
+	adcx	%rax,%r10
+	adox	%r12,%r11
+	mulx	2*8($nptr),%rax,%r12
+	mov	%r10,-4*8($tptr)
+	adcx	%rax,%r11
+	adox	%r13,%r12
+	mulx	3*8($nptr),%rax,%r15
+	 mov	$bi,%rdx
+	mov	%r11,-3*8($tptr)
+	lea	4*8($nptr),$nptr
+	adcx	%rax,%r12
+	adox	$zero,%r15		# of=0
+	mov	48(%rsp),$bptr		# counter value
+	mov	%r12,-2*8($tptr)
+
+	jmp	.Lmulx4x_inner
+
+.align	32
+.Lmulx4x_inner:
+	mulx	0*8($aptr),%r10,%rax	# a[4]*b[i]
+	adcx	$zero,%r15		# cf=0, modulo-scheduled
+	adox	%r14,%r10
+	mulx	1*8($aptr),%r11,%r14	# a[5]*b[i]
+	adcx	0*8($tptr),%r10
+	adox	%rax,%r11
+	mulx	2*8($aptr),%r12,%rax	# ...
+	adcx	1*8($tptr),%r11
+	adox	%r14,%r12
+	mulx	3*8($aptr),%r13,%r14
+	 mov	$mi,%rdx
+	adcx	2*8($tptr),%r12
+	adox	%rax,%r13
+	adcx	3*8($tptr),%r13
+	adox	$zero,%r14		# of=0
+	lea	4*8($aptr),$aptr
+	lea	4*8($tptr),$tptr
+	adcx	$zero,%r14		# cf=0
+
+	adox	%r15,%r10
+	mulx	0*8($nptr),%rax,%r15
+	adcx	%rax,%r10
+	adox	%r15,%r11
+	mulx	1*8($nptr),%rax,%r15
+	adcx	%rax,%r11
+	adox	%r15,%r12
+	mulx	2*8($nptr),%rax,%r15
+	mov	%r10,-5*8($tptr)
+	adcx	%rax,%r12
+	adox	%r15,%r13
+	mulx	3*8($nptr),%rax,%r15
+	 mov	$bi,%rdx
+	mov	%r11,-4*8($tptr)
+	mov	%r12,-3*8($tptr)
+	adcx	%rax,%r13
+	adox	$zero,%r15
+	lea	4*8($nptr),$nptr
+	mov	%r13,-2*8($tptr)
+
+	dec	$bptr			# of=0, pass cf
+	jnz	.Lmulx4x_inner
+
+	mov	0(%rsp),$num		# load num
+	mov	8(%rsp),$bptr		# re-load &b[i]
+	adc	$zero,%r15		# modulo-scheduled
+	sub	0*8($tptr),$zero	# pull top-most carry
+	adc	%r15,%r14
+	sbb	%r15,%r15		# top-most carry
+	mov	%r14,-1*8($tptr)
+
+	cmp	16(%rsp),$bptr
+	jne	.Lmulx4x_outer
+
+	lea	64(%rsp),$tptr
+	sub	$num,$nptr		# rewind $nptr
+	neg	%r15
+	mov	$num,%rdx
+	shr	\$3+2,$num		# %cf=0
+	mov	32(%rsp),$rptr		# restore rp
+	jmp	.Lmulx4x_sub
+
+.align	32
+.Lmulx4x_sub:
+	mov	8*0($tptr),%r11
+	mov	8*1($tptr),%r12
+	mov	8*2($tptr),%r13
+	mov	8*3($tptr),%r14
+	lea	8*4($tptr),$tptr
+	sbb	8*0($nptr),%r11
+	sbb	8*1($nptr),%r12
+	sbb	8*2($nptr),%r13
+	sbb	8*3($nptr),%r14
+	lea	8*4($nptr),$nptr
+	mov	%r11,8*0($rptr)
+	mov	%r12,8*1($rptr)
+	mov	%r13,8*2($rptr)
+	mov	%r14,8*3($rptr)
+	lea	8*4($rptr),$rptr
+	dec	$num			# preserves %cf
+	jnz	.Lmulx4x_sub
+
+	sbb	\$0,%r15		# top-most carry
+	lea	64(%rsp),$tptr
+	sub	%rdx,$rptr		# rewind
+
+	movq	%r15,%xmm1
+	pxor	%xmm0,%xmm0
+	pshufd	\$0,%xmm1,%xmm1
+	mov	40(%rsp),%rsi		# restore %rsp
+	jmp	.Lmulx4x_cond_copy
+
+.align	32
+.Lmulx4x_cond_copy:
+	movdqa	16*0($tptr),%xmm2
+	movdqa	16*1($tptr),%xmm3
+	lea	16*2($tptr),$tptr
+	movdqu	16*0($rptr),%xmm4
+	movdqu	16*1($rptr),%xmm5
+	lea	16*2($rptr),$rptr
+	movdqa	%xmm0,-16*2($tptr)	# zero tp
+	movdqa	%xmm0,-16*1($tptr)
+	pcmpeqd	%xmm1,%xmm0
+	pand	%xmm1,%xmm2
+	pand	%xmm1,%xmm3
+	pand	%xmm0,%xmm4
+	pand	%xmm0,%xmm5
+	pxor	%xmm0,%xmm0
+	por	%xmm2,%xmm4
+	por	%xmm3,%xmm5
+	movdqu	%xmm4,-16*2($rptr)
+	movdqu	%xmm5,-16*1($rptr)
+	sub	\$32,%rdx
+	jnz	.Lmulx4x_cond_copy
+
+	mov	%rdx,($tptr)
+
+	mov	\$1,%rax
+	mov	-48(%rsi),%r15
+	mov	-40(%rsi),%r14
+	mov	-32(%rsi),%r13
+	mov	-24(%rsi),%r12
+	mov	-16(%rsi),%rbp
+	mov	-8(%rsi),%rbx
+	lea	(%rsi),%rsp
+.Lmulx4x_epilogue:
+	ret
+.size	bn_mulx4x_mont,.-bn_mulx4x_mont
+___
+}}}
+$code.=<<___;
+.asciz	"Montgomery Multiplication for x86_64, CRYPTOGAMS by "
+.align	16
+___
+
+# EXCEPTION_DISPOSITION handler (EXCEPTION_RECORD *rec,ULONG64 frame,
+#		CONTEXT *context,DISPATCHER_CONTEXT *disp)
+if ($win64) {
+$rec="%rcx";
+$frame="%rdx";
+$context="%r8";
+$disp="%r9";
+
+$code.=<<___;
+.extern	__imp_RtlVirtualUnwind
+.type	mul_handler,\@abi-omnipotent
+.align	16
+mul_handler:
+	push	%rsi
+	push	%rdi
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+	pushfq
+	sub	\$64,%rsp
+
+	mov	120($context),%rax	# pull context->Rax
+	mov	248($context),%rbx	# pull context->Rip
+
+	mov	8($disp),%rsi		# disp->ImageBase
+	mov	56($disp),%r11		# disp->HandlerData
+
+	mov	0(%r11),%r10d		# HandlerData[0]
+	lea	(%rsi,%r10),%r10	# end of prologue label
+	cmp	%r10,%rbx		# context->RipRsp
+
+	mov	4(%r11),%r10d		# HandlerData[1]
+	lea	(%rsi,%r10),%r10	# epilogue label
+	cmp	%r10,%rbx		# context->Rip>=epilogue label
+	jae	.Lcommon_seh_tail
+
+	mov	192($context),%r10	# pull $num
+	mov	8(%rax,%r10,8),%rax	# pull saved stack pointer
+
+	jmp	.Lcommon_pop_regs
+.size	mul_handler,.-mul_handler
+
+.type	sqr_handler,\@abi-omnipotent
+.align	16
+sqr_handler:
+	push	%rsi
+	push	%rdi
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+	pushfq
+	sub	\$64,%rsp
+
+	mov	120($context),%rax	# pull context->Rax
+	mov	248($context),%rbx	# pull context->Rip
+
+	mov	8($disp),%rsi		# disp->ImageBase
+	mov	56($disp),%r11		# disp->HandlerData
+
+	mov	0(%r11),%r10d		# HandlerData[0]
+	lea	(%rsi,%r10),%r10	# end of prologue label
+	cmp	%r10,%rbx		# context->Rip<.Lsqr_body
+	jb	.Lcommon_seh_tail
+
+	mov	4(%r11),%r10d		# HandlerData[1]
+	lea	(%rsi,%r10),%r10	# body label
+	cmp	%r10,%rbx		# context->Rip>=.Lsqr_epilogue
+	jb	.Lcommon_pop_regs
+
+	mov	152($context),%rax	# pull context->Rsp
+
+	mov	8(%r11),%r10d		# HandlerData[2]
+	lea	(%rsi,%r10),%r10	# epilogue label
+	cmp	%r10,%rbx		# context->Rip>=.Lsqr_epilogue
+	jae	.Lcommon_seh_tail
+
+	mov	40(%rax),%rax		# pull saved stack pointer
+
+.Lcommon_pop_regs:
+	mov	-8(%rax),%rbx
+	mov	-16(%rax),%rbp
+	mov	-24(%rax),%r12
+	mov	-32(%rax),%r13
+	mov	-40(%rax),%r14
+	mov	-48(%rax),%r15
+	mov	%rbx,144($context)	# restore context->Rbx
+	mov	%rbp,160($context)	# restore context->Rbp
+	mov	%r12,216($context)	# restore context->R12
+	mov	%r13,224($context)	# restore context->R13
+	mov	%r14,232($context)	# restore context->R14
+	mov	%r15,240($context)	# restore context->R15
+
+.Lcommon_seh_tail:
+	mov	8(%rax),%rdi
+	mov	16(%rax),%rsi
+	mov	%rax,152($context)	# restore context->Rsp
+	mov	%rsi,168($context)	# restore context->Rsi
+	mov	%rdi,176($context)	# restore context->Rdi
+
+	mov	40($disp),%rdi		# disp->ContextRecord
+	mov	$context,%rsi		# context
+	mov	\$154,%ecx		# sizeof(CONTEXT)
+	.long	0xa548f3fc		# cld; rep movsq
+
+	mov	$disp,%rsi
+	xor	%rcx,%rcx		# arg1, UNW_FLAG_NHANDLER
+	mov	8(%rsi),%rdx		# arg2, disp->ImageBase
+	mov	0(%rsi),%r8		# arg3, disp->ControlPc
+	mov	16(%rsi),%r9		# arg4, disp->FunctionEntry
+	mov	40(%rsi),%r10		# disp->ContextRecord
+	lea	56(%rsi),%r11		# &disp->HandlerData
+	lea	24(%rsi),%r12		# &disp->EstablisherFrame
+	mov	%r10,32(%rsp)		# arg5
+	mov	%r11,40(%rsp)		# arg6
+	mov	%r12,48(%rsp)		# arg7
+	mov	%rcx,56(%rsp)		# arg8, (NULL)
+	call	*__imp_RtlVirtualUnwind(%rip)
+
+	mov	\$1,%eax		# ExceptionContinueSearch
+	add	\$64,%rsp
+	popfq
+	pop	%r15
+	pop	%r14
+	pop	%r13
+	pop	%r12
+	pop	%rbp
+	pop	%rbx
+	pop	%rdi
+	pop	%rsi
+	ret
+.size	sqr_handler,.-sqr_handler
+
+.section	.pdata
+.align	4
+	.rva	.LSEH_begin_bn_mul_mont
+	.rva	.LSEH_end_bn_mul_mont
+	.rva	.LSEH_info_bn_mul_mont
+
+	.rva	.LSEH_begin_bn_mul4x_mont
+	.rva	.LSEH_end_bn_mul4x_mont
+	.rva	.LSEH_info_bn_mul4x_mont
+
+	.rva	.LSEH_begin_bn_sqr8x_mont
+	.rva	.LSEH_end_bn_sqr8x_mont
+	.rva	.LSEH_info_bn_sqr8x_mont
+___
+$code.=<<___ if ($addx);
+	.rva	.LSEH_begin_bn_mulx4x_mont
+	.rva	.LSEH_end_bn_mulx4x_mont
+	.rva	.LSEH_info_bn_mulx4x_mont
+___
+$code.=<<___;
+.section	.xdata
+.align	8
+.LSEH_info_bn_mul_mont:
+	.byte	9,0,0,0
+	.rva	mul_handler
+	.rva	.Lmul_body,.Lmul_epilogue	# HandlerData[]
+.LSEH_info_bn_mul4x_mont:
+	.byte	9,0,0,0
+	.rva	mul_handler
+	.rva	.Lmul4x_body,.Lmul4x_epilogue	# HandlerData[]
+.LSEH_info_bn_sqr8x_mont:
+	.byte	9,0,0,0
+	.rva	sqr_handler
+	.rva	.Lsqr8x_prologue,.Lsqr8x_body,.Lsqr8x_epilogue		# HandlerData[]
+.align	8
+___
+$code.=<<___ if ($addx);
+.LSEH_info_bn_mulx4x_mont:
+	.byte	9,0,0,0
+	.rva	sqr_handler
+	.rva	.Lmulx4x_prologue,.Lmulx4x_body,.Lmulx4x_epilogue	# HandlerData[]
+.align	8
+___
+}
+
+print $code;
+close STDOUT;
diff --git a/crypto/bn/asm/x86_64-mont5.pl b/crypto/bn/asm/x86_64-mont5.pl
new file mode 100755
index 000000000..6807ab5cf
--- /dev/null
+++ b/crypto/bn/asm/x86_64-mont5.pl
@@ -0,0 +1,3827 @@
+#! /usr/bin/env perl
+# Copyright 2011-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+
+# ====================================================================
+# Written by Andy Polyakov  for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+
+# August 2011.
+#
+# Companion to x86_64-mont.pl that optimizes cache-timing attack
+# countermeasures. The subroutines are produced by replacing bp[i]
+# references in their x86_64-mont.pl counterparts with cache-neutral
+# references to powers table computed in BN_mod_exp_mont_consttime.
+# In addition subroutine that scatters elements of the powers table
+# is implemented, so that scatter-/gathering can be tuned without
+# bn_exp.c modifications.
+
+# August 2013.
+#
+# Add MULX/AD*X code paths and additional interfaces to optimize for
+# branch prediction unit. For input lengths that are multiples of 8
+# the np argument is not just modulus value, but one interleaved
+# with 0. This is to optimize post-condition...
+
+$flavour = shift;
+$output  = shift;
+if ($flavour =~ /\./) { $output = $flavour; undef $flavour; }
+
+$win64=0; $win64=1 if ($flavour =~ /[nm]asm|mingw64/ || $output =~ /\.asm$/);
+
+$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+( $xlate="${dir}x86_64-xlate.pl" and -f $xlate ) or
+( $xlate="${dir}../../perlasm/x86_64-xlate.pl" and -f $xlate) or
+die "can't locate x86_64-xlate.pl";
+
+open OUT,"| \"$^X\" \"$xlate\" $flavour \"$output\"";
+*STDOUT=*OUT;
+
+if (`$ENV{CC} -Wa,-v -c -o /dev/null -x assembler /dev/null 2>&1`
+		=~ /GNU assembler version ([2-9]\.[0-9]+)/) {
+	$addx = ($1>=2.23);
+}
+
+if (!$addx && $win64 && ($flavour =~ /nasm/ || $ENV{ASM} =~ /nasm/) &&
+	    `nasm -v 2>&1` =~ /NASM version ([2-9]\.[0-9]+)/) {
+	$addx = ($1>=2.10);
+}
+
+if (!$addx && $win64 && ($flavour =~ /masm/ || $ENV{ASM} =~ /ml64/) &&
+	    `ml64 2>&1` =~ /Version ([0-9]+)\./) {
+	$addx = ($1>=12);
+}
+
+if (!$addx && `$ENV{CC} -v 2>&1` =~ /((?:^clang|LLVM) version|.*based on LLVM) ([3-9])\.([0-9]+)/) {
+	my $ver = $2 + $3/100.0;	# 3.1->3.01, 3.10->3.10
+	$addx = ($ver>=3.03);
+}
+
+# int bn_mul_mont_gather5(
+$rp="%rdi";	# BN_ULONG *rp,
+$ap="%rsi";	# const BN_ULONG *ap,
+$bp="%rdx";	# const BN_ULONG *bp,
+$np="%rcx";	# const BN_ULONG *np,
+$n0="%r8";	# const BN_ULONG *n0,
+$num="%r9";	# int num,
+		# int idx);	# 0 to 2^5-1, "index" in $bp holding
+				# pre-computed powers of a', interlaced
+				# in such manner that b[0] is $bp[idx],
+				# b[1] is [2^5+idx], etc.
+$lo0="%r10";
+$hi0="%r11";
+$hi1="%r13";
+$i="%r14";
+$j="%r15";
+$m0="%rbx";
+$m1="%rbp";
+
+$code=<<___;
+.text
+
+.extern	OPENSSL_ia32cap_P
+
+.globl	bn_mul_mont_gather5
+.type	bn_mul_mont_gather5,\@function,6
+.align	64
+bn_mul_mont_gather5:
+	mov	${num}d,${num}d
+	mov	%rsp,%rax
+	test	\$7,${num}d
+	jnz	.Lmul_enter
+___
+$code.=<<___ if ($addx);
+	mov	OPENSSL_ia32cap_P+8(%rip),%r11d
+___
+$code.=<<___;
+	jmp	.Lmul4x_enter
+
+.align	16
+.Lmul_enter:
+	movd	`($win64?56:8)`(%rsp),%xmm5	# load 7th argument
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+
+	neg	$num
+	mov	%rsp,%r11
+	lea	-280(%rsp,$num,8),%r10	# future alloca(8*(num+2)+256+8)
+	neg	$num			# restore $num
+	and	\$-1024,%r10		# minimize TLB usage
+
+	# An OS-agnostic version of __chkstk.
+	#
+	# Some OSes (Windows) insist on stack being "wired" to
+	# physical memory in strictly sequential manner, i.e. if stack
+	# allocation spans two pages, then reference to farmost one can
+	# be punishable by SEGV. But page walking can do good even on
+	# other OSes, because it guarantees that villain thread hits
+	# the guard page before it can make damage to innocent one...
+	sub	%r10,%r11
+	and	\$-4096,%r11
+	lea	(%r10,%r11),%rsp
+	mov	(%rsp),%r11
+	cmp	%r10,%rsp
+	ja	.Lmul_page_walk
+	jmp	.Lmul_page_walk_done
+
+.Lmul_page_walk:
+	lea	-4096(%rsp),%rsp
+	mov	(%rsp),%r11
+	cmp	%r10,%rsp
+	ja	.Lmul_page_walk
+.Lmul_page_walk_done:
+
+	lea	.Linc(%rip),%r10
+	mov	%rax,8(%rsp,$num,8)	# tp[num+1]=%rsp
+.Lmul_body:
+
+	lea	128($bp),%r12		# reassign $bp (+size optimization)
+___
+		$bp="%r12";
+		$STRIDE=2**5*8;		# 5 is "window size"
+		$N=$STRIDE/4;		# should match cache line size
+$code.=<<___;
+	movdqa	0(%r10),%xmm0		# 00000001000000010000000000000000
+	movdqa	16(%r10),%xmm1		# 00000002000000020000000200000002
+	lea	24-112(%rsp,$num,8),%r10# place the mask after tp[num+3] (+ICache optimization)
+	and	\$-16,%r10
+
+	pshufd	\$0,%xmm5,%xmm5		# broadcast index
+	movdqa	%xmm1,%xmm4
+	movdqa	%xmm1,%xmm2
+___
+########################################################################
+# calculate mask by comparing 0..31 to index and save result to stack
+#
+$code.=<<___;
+	paddd	%xmm0,%xmm1
+	pcmpeqd	%xmm5,%xmm0		# compare to 1,0
+	.byte	0x67
+	movdqa	%xmm4,%xmm3
+___
+for($k=0;$k<$STRIDE/16-4;$k+=4) {
+$code.=<<___;
+	paddd	%xmm1,%xmm2
+	pcmpeqd	%xmm5,%xmm1		# compare to 3,2
+	movdqa	%xmm0,`16*($k+0)+112`(%r10)
+	movdqa	%xmm4,%xmm0
+
+	paddd	%xmm2,%xmm3
+	pcmpeqd	%xmm5,%xmm2		# compare to 5,4
+	movdqa	%xmm1,`16*($k+1)+112`(%r10)
+	movdqa	%xmm4,%xmm1
+
+	paddd	%xmm3,%xmm0
+	pcmpeqd	%xmm5,%xmm3		# compare to 7,6
+	movdqa	%xmm2,`16*($k+2)+112`(%r10)
+	movdqa	%xmm4,%xmm2
+
+	paddd	%xmm0,%xmm1
+	pcmpeqd	%xmm5,%xmm0
+	movdqa	%xmm3,`16*($k+3)+112`(%r10)
+	movdqa	%xmm4,%xmm3
+___
+}
+$code.=<<___;				# last iteration can be optimized
+	paddd	%xmm1,%xmm2
+	pcmpeqd	%xmm5,%xmm1
+	movdqa	%xmm0,`16*($k+0)+112`(%r10)
+
+	paddd	%xmm2,%xmm3
+	.byte	0x67
+	pcmpeqd	%xmm5,%xmm2
+	movdqa	%xmm1,`16*($k+1)+112`(%r10)
+
+	pcmpeqd	%xmm5,%xmm3
+	movdqa	%xmm2,`16*($k+2)+112`(%r10)
+	pand	`16*($k+0)-128`($bp),%xmm0	# while it's still in register
+
+	pand	`16*($k+1)-128`($bp),%xmm1
+	pand	`16*($k+2)-128`($bp),%xmm2
+	movdqa	%xmm3,`16*($k+3)+112`(%r10)
+	pand	`16*($k+3)-128`($bp),%xmm3
+	por	%xmm2,%xmm0
+	por	%xmm3,%xmm1
+___
+for($k=0;$k<$STRIDE/16-4;$k+=4) {
+$code.=<<___;
+	movdqa	`16*($k+0)-128`($bp),%xmm4
+	movdqa	`16*($k+1)-128`($bp),%xmm5
+	movdqa	`16*($k+2)-128`($bp),%xmm2
+	pand	`16*($k+0)+112`(%r10),%xmm4
+	movdqa	`16*($k+3)-128`($bp),%xmm3
+	pand	`16*($k+1)+112`(%r10),%xmm5
+	por	%xmm4,%xmm0
+	pand	`16*($k+2)+112`(%r10),%xmm2
+	por	%xmm5,%xmm1
+	pand	`16*($k+3)+112`(%r10),%xmm3
+	por	%xmm2,%xmm0
+	por	%xmm3,%xmm1
+___
+}
+$code.=<<___;
+	por	%xmm1,%xmm0
+	pshufd	\$0x4e,%xmm0,%xmm1
+	por	%xmm1,%xmm0
+	lea	$STRIDE($bp),$bp
+	movq	%xmm0,$m0		# m0=bp[0]
+
+	mov	($n0),$n0		# pull n0[0] value
+	mov	($ap),%rax
+
+	xor	$i,$i			# i=0
+	xor	$j,$j			# j=0
+
+	mov	$n0,$m1
+	mulq	$m0			# ap[0]*bp[0]
+	mov	%rax,$lo0
+	mov	($np),%rax
+
+	imulq	$lo0,$m1		# "tp[0]"*n0
+	mov	%rdx,$hi0
+
+	mulq	$m1			# np[0]*m1
+	add	%rax,$lo0		# discarded
+	mov	8($ap),%rax
+	adc	\$0,%rdx
+	mov	%rdx,$hi1
+
+	lea	1($j),$j		# j++
+	jmp	.L1st_enter
+
+.align	16
+.L1st:
+	add	%rax,$hi1
+	mov	($ap,$j,8),%rax
+	adc	\$0,%rdx
+	add	$hi0,$hi1		# np[j]*m1+ap[j]*bp[0]
+	mov	$lo0,$hi0
+	adc	\$0,%rdx
+	mov	$hi1,-16(%rsp,$j,8)	# tp[j-1]
+	mov	%rdx,$hi1
+
+.L1st_enter:
+	mulq	$m0			# ap[j]*bp[0]
+	add	%rax,$hi0
+	mov	($np,$j,8),%rax
+	adc	\$0,%rdx
+	lea	1($j),$j		# j++
+	mov	%rdx,$lo0
+
+	mulq	$m1			# np[j]*m1
+	cmp	$num,$j
+	jne	.L1st			# note that upon exit $j==$num, so
+					# they can be used interchangeably
+
+	add	%rax,$hi1
+	adc	\$0,%rdx
+	add	$hi0,$hi1		# np[j]*m1+ap[j]*bp[0]
+	adc	\$0,%rdx
+	mov	$hi1,-16(%rsp,$num,8)	# tp[num-1]
+	mov	%rdx,$hi1
+	mov	$lo0,$hi0
+
+	xor	%rdx,%rdx
+	add	$hi0,$hi1
+	adc	\$0,%rdx
+	mov	$hi1,-8(%rsp,$num,8)
+	mov	%rdx,(%rsp,$num,8)	# store upmost overflow bit
+
+	lea	1($i),$i		# i++
+	jmp	.Louter
+.align	16
+.Louter:
+	lea	24+128(%rsp,$num,8),%rdx	# where 256-byte mask is (+size optimization)
+	and	\$-16,%rdx
+	pxor	%xmm4,%xmm4
+	pxor	%xmm5,%xmm5
+___
+for($k=0;$k<$STRIDE/16;$k+=4) {
+$code.=<<___;
+	movdqa	`16*($k+0)-128`($bp),%xmm0
+	movdqa	`16*($k+1)-128`($bp),%xmm1
+	movdqa	`16*($k+2)-128`($bp),%xmm2
+	movdqa	`16*($k+3)-128`($bp),%xmm3
+	pand	`16*($k+0)-128`(%rdx),%xmm0
+	pand	`16*($k+1)-128`(%rdx),%xmm1
+	por	%xmm0,%xmm4
+	pand	`16*($k+2)-128`(%rdx),%xmm2
+	por	%xmm1,%xmm5
+	pand	`16*($k+3)-128`(%rdx),%xmm3
+	por	%xmm2,%xmm4
+	por	%xmm3,%xmm5
+___
+}
+$code.=<<___;
+	por	%xmm5,%xmm4
+	pshufd	\$0x4e,%xmm4,%xmm0
+	por	%xmm4,%xmm0
+	lea	$STRIDE($bp),$bp
+
+	mov	($ap),%rax		# ap[0]
+	movq	%xmm0,$m0		# m0=bp[i]
+
+	xor	$j,$j			# j=0
+	mov	$n0,$m1
+	mov	(%rsp),$lo0
+
+	mulq	$m0			# ap[0]*bp[i]
+	add	%rax,$lo0		# ap[0]*bp[i]+tp[0]
+	mov	($np),%rax
+	adc	\$0,%rdx
+
+	imulq	$lo0,$m1		# tp[0]*n0
+	mov	%rdx,$hi0
+
+	mulq	$m1			# np[0]*m1
+	add	%rax,$lo0		# discarded
+	mov	8($ap),%rax
+	adc	\$0,%rdx
+	mov	8(%rsp),$lo0		# tp[1]
+	mov	%rdx,$hi1
+
+	lea	1($j),$j		# j++
+	jmp	.Linner_enter
+
+.align	16
+.Linner:
+	add	%rax,$hi1
+	mov	($ap,$j,8),%rax
+	adc	\$0,%rdx
+	add	$lo0,$hi1		# np[j]*m1+ap[j]*bp[i]+tp[j]
+	mov	(%rsp,$j,8),$lo0
+	adc	\$0,%rdx
+	mov	$hi1,-16(%rsp,$j,8)	# tp[j-1]
+	mov	%rdx,$hi1
+
+.Linner_enter:
+	mulq	$m0			# ap[j]*bp[i]
+	add	%rax,$hi0
+	mov	($np,$j,8),%rax
+	adc	\$0,%rdx
+	add	$hi0,$lo0		# ap[j]*bp[i]+tp[j]
+	mov	%rdx,$hi0
+	adc	\$0,$hi0
+	lea	1($j),$j		# j++
+
+	mulq	$m1			# np[j]*m1
+	cmp	$num,$j
+	jne	.Linner			# note that upon exit $j==$num, so
+					# they can be used interchangeably
+	add	%rax,$hi1
+	adc	\$0,%rdx
+	add	$lo0,$hi1		# np[j]*m1+ap[j]*bp[i]+tp[j]
+	mov	(%rsp,$num,8),$lo0
+	adc	\$0,%rdx
+	mov	$hi1,-16(%rsp,$num,8)	# tp[num-1]
+	mov	%rdx,$hi1
+
+	xor	%rdx,%rdx
+	add	$hi0,$hi1
+	adc	\$0,%rdx
+	add	$lo0,$hi1		# pull upmost overflow bit
+	adc	\$0,%rdx
+	mov	$hi1,-8(%rsp,$num,8)
+	mov	%rdx,(%rsp,$num,8)	# store upmost overflow bit
+
+	lea	1($i),$i		# i++
+	cmp	$num,$i
+	jb	.Louter
+
+	xor	$i,$i			# i=0 and clear CF!
+	mov	(%rsp),%rax		# tp[0]
+	lea	(%rsp),$ap		# borrow ap for tp
+	mov	$num,$j			# j=num
+	jmp	.Lsub
+.align	16
+.Lsub:	sbb	($np,$i,8),%rax
+	mov	%rax,($rp,$i,8)		# rp[i]=tp[i]-np[i]
+	mov	8($ap,$i,8),%rax	# tp[i+1]
+	lea	1($i),$i		# i++
+	dec	$j			# doesnn't affect CF!
+	jnz	.Lsub
+
+	sbb	\$0,%rax		# handle upmost overflow bit
+	xor	$i,$i
+	and	%rax,$ap
+	not	%rax
+	mov	$rp,$np
+	and	%rax,$np
+	mov	$num,$j			# j=num
+	or	$np,$ap			# ap=borrow?tp:rp
+.align	16
+.Lcopy:					# copy or in-place refresh
+	mov	($ap,$i,8),%rax
+	mov	$i,(%rsp,$i,8)		# zap temporary vector
+	mov	%rax,($rp,$i,8)		# rp[i]=tp[i]
+	lea	1($i),$i
+	sub	\$1,$j
+	jnz	.Lcopy
+
+	mov	8(%rsp,$num,8),%rsi	# restore %rsp
+	mov	\$1,%rax
+
+	mov	-48(%rsi),%r15
+	mov	-40(%rsi),%r14
+	mov	-32(%rsi),%r13
+	mov	-24(%rsi),%r12
+	mov	-16(%rsi),%rbp
+	mov	-8(%rsi),%rbx
+	lea	(%rsi),%rsp
+.Lmul_epilogue:
+	ret
+.size	bn_mul_mont_gather5,.-bn_mul_mont_gather5
+___
+{{{
+my @A=("%r10","%r11");
+my @N=("%r13","%rdi");
+$code.=<<___;
+.type	bn_mul4x_mont_gather5,\@function,6
+.align	32
+bn_mul4x_mont_gather5:
+	.byte	0x67
+	mov	%rsp,%rax
+.Lmul4x_enter:
+___
+$code.=<<___ if ($addx);
+	and	\$0x80108,%r11d
+	cmp	\$0x80108,%r11d		# check for AD*X+BMI2+BMI1
+	je	.Lmulx4x_enter
+___
+$code.=<<___;
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+.Lmul4x_prologue:
+
+	.byte	0x67
+	shl	\$3,${num}d		# convert $num to bytes
+	lea	($num,$num,2),%r10	# 3*$num in bytes
+	neg	$num			# -$num
+
+	##############################################################
+	# Ensure that stack frame doesn't alias with $rptr+3*$num
+	# modulo 4096, which covers ret[num], am[num] and n[num]
+	# (see bn_exp.c). This is done to allow memory disambiguation
+	# logic do its magic. [Extra [num] is allocated in order
+	# to align with bn_power5's frame, which is cleansed after
+	# completing exponentiation. Extra 256 bytes is for power mask
+	# calculated from 7th argument, the index.]
+	#
+	lea	-320(%rsp,$num,2),%r11
+	mov	%rsp,%rbp
+	sub	$rp,%r11
+	and	\$4095,%r11
+	cmp	%r11,%r10
+	jb	.Lmul4xsp_alt
+	sub	%r11,%rbp		# align with $rp
+	lea	-320(%rbp,$num,2),%rbp	# future alloca(frame+2*num*8+256)
+	jmp	.Lmul4xsp_done
+
+.align	32
+.Lmul4xsp_alt:
+	lea	4096-320(,$num,2),%r10
+	lea	-320(%rbp,$num,2),%rbp	# future alloca(frame+2*num*8+256)
+	sub	%r10,%r11
+	mov	\$0,%r10
+	cmovc	%r10,%r11
+	sub	%r11,%rbp
+.Lmul4xsp_done:
+	and	\$-64,%rbp
+	mov	%rsp,%r11
+	sub	%rbp,%r11
+	and	\$-4096,%r11
+	lea	(%rbp,%r11),%rsp
+	mov	(%rsp),%r10
+	cmp	%rbp,%rsp
+	ja	.Lmul4x_page_walk
+	jmp	.Lmul4x_page_walk_done
+
+.Lmul4x_page_walk:
+	lea	-4096(%rsp),%rsp
+	mov	(%rsp),%r10
+	cmp	%rbp,%rsp
+	ja	.Lmul4x_page_walk
+.Lmul4x_page_walk_done:
+
+	neg	$num
+
+	mov	%rax,40(%rsp)
+.Lmul4x_body:
+
+	call	mul4x_internal
+
+	mov	40(%rsp),%rsi		# restore %rsp
+	mov	\$1,%rax
+
+	mov	-48(%rsi),%r15
+	mov	-40(%rsi),%r14
+	mov	-32(%rsi),%r13
+	mov	-24(%rsi),%r12
+	mov	-16(%rsi),%rbp
+	mov	-8(%rsi),%rbx
+	lea	(%rsi),%rsp
+.Lmul4x_epilogue:
+	ret
+.size	bn_mul4x_mont_gather5,.-bn_mul4x_mont_gather5
+
+.type	mul4x_internal,\@abi-omnipotent
+.align	32
+mul4x_internal:
+	shl	\$5,$num		# $num was in bytes
+	movd	`($win64?56:8)`(%rax),%xmm5	# load 7th argument, index
+	lea	.Linc(%rip),%rax
+	lea	128(%rdx,$num),%r13	# end of powers table (+size optimization)
+	shr	\$5,$num		# restore $num
+___
+		$bp="%r12";
+		$STRIDE=2**5*8;		# 5 is "window size"
+		$N=$STRIDE/4;		# should match cache line size
+		$tp=$i;
+$code.=<<___;
+	movdqa	0(%rax),%xmm0		# 00000001000000010000000000000000
+	movdqa	16(%rax),%xmm1		# 00000002000000020000000200000002
+	lea	88-112(%rsp,$num),%r10	# place the mask after tp[num+1] (+ICache optimization)
+	lea	128(%rdx),$bp		# size optimization
+
+	pshufd	\$0,%xmm5,%xmm5		# broadcast index
+	movdqa	%xmm1,%xmm4
+	.byte	0x67,0x67
+	movdqa	%xmm1,%xmm2
+___
+########################################################################
+# calculate mask by comparing 0..31 to index and save result to stack
+#
+$code.=<<___;
+	paddd	%xmm0,%xmm1
+	pcmpeqd	%xmm5,%xmm0		# compare to 1,0
+	.byte	0x67
+	movdqa	%xmm4,%xmm3
+___
+for($i=0;$i<$STRIDE/16-4;$i+=4) {
+$code.=<<___;
+	paddd	%xmm1,%xmm2
+	pcmpeqd	%xmm5,%xmm1		# compare to 3,2
+	movdqa	%xmm0,`16*($i+0)+112`(%r10)
+	movdqa	%xmm4,%xmm0
+
+	paddd	%xmm2,%xmm3
+	pcmpeqd	%xmm5,%xmm2		# compare to 5,4
+	movdqa	%xmm1,`16*($i+1)+112`(%r10)
+	movdqa	%xmm4,%xmm1
+
+	paddd	%xmm3,%xmm0
+	pcmpeqd	%xmm5,%xmm3		# compare to 7,6
+	movdqa	%xmm2,`16*($i+2)+112`(%r10)
+	movdqa	%xmm4,%xmm2
+
+	paddd	%xmm0,%xmm1
+	pcmpeqd	%xmm5,%xmm0
+	movdqa	%xmm3,`16*($i+3)+112`(%r10)
+	movdqa	%xmm4,%xmm3
+___
+}
+$code.=<<___;				# last iteration can be optimized
+	paddd	%xmm1,%xmm2
+	pcmpeqd	%xmm5,%xmm1
+	movdqa	%xmm0,`16*($i+0)+112`(%r10)
+
+	paddd	%xmm2,%xmm3
+	.byte	0x67
+	pcmpeqd	%xmm5,%xmm2
+	movdqa	%xmm1,`16*($i+1)+112`(%r10)
+
+	pcmpeqd	%xmm5,%xmm3
+	movdqa	%xmm2,`16*($i+2)+112`(%r10)
+	pand	`16*($i+0)-128`($bp),%xmm0	# while it's still in register
+
+	pand	`16*($i+1)-128`($bp),%xmm1
+	pand	`16*($i+2)-128`($bp),%xmm2
+	movdqa	%xmm3,`16*($i+3)+112`(%r10)
+	pand	`16*($i+3)-128`($bp),%xmm3
+	por	%xmm2,%xmm0
+	por	%xmm3,%xmm1
+___
+for($i=0;$i<$STRIDE/16-4;$i+=4) {
+$code.=<<___;
+	movdqa	`16*($i+0)-128`($bp),%xmm4
+	movdqa	`16*($i+1)-128`($bp),%xmm5
+	movdqa	`16*($i+2)-128`($bp),%xmm2
+	pand	`16*($i+0)+112`(%r10),%xmm4
+	movdqa	`16*($i+3)-128`($bp),%xmm3
+	pand	`16*($i+1)+112`(%r10),%xmm5
+	por	%xmm4,%xmm0
+	pand	`16*($i+2)+112`(%r10),%xmm2
+	por	%xmm5,%xmm1
+	pand	`16*($i+3)+112`(%r10),%xmm3
+	por	%xmm2,%xmm0
+	por	%xmm3,%xmm1
+___
+}
+$code.=<<___;
+	por	%xmm1,%xmm0
+	pshufd	\$0x4e,%xmm0,%xmm1
+	por	%xmm1,%xmm0
+	lea	$STRIDE($bp),$bp
+	movq	%xmm0,$m0		# m0=bp[0]
+
+	mov	%r13,16+8(%rsp)		# save end of b[num]
+	mov	$rp, 56+8(%rsp)		# save $rp
+
+	mov	($n0),$n0		# pull n0[0] value
+	mov	($ap),%rax
+	lea	($ap,$num),$ap		# end of a[num]
+	neg	$num
+
+	mov	$n0,$m1
+	mulq	$m0			# ap[0]*bp[0]
+	mov	%rax,$A[0]
+	mov	($np),%rax
+
+	imulq	$A[0],$m1		# "tp[0]"*n0
+	lea	64+8(%rsp),$tp
+	mov	%rdx,$A[1]
+
+	mulq	$m1			# np[0]*m1
+	add	%rax,$A[0]		# discarded
+	mov	8($ap,$num),%rax
+	adc	\$0,%rdx
+	mov	%rdx,$N[1]
+
+	mulq	$m0
+	add	%rax,$A[1]
+	mov	8*1($np),%rax
+	adc	\$0,%rdx
+	mov	%rdx,$A[0]
+
+	mulq	$m1
+	add	%rax,$N[1]
+	mov	16($ap,$num),%rax
+	adc	\$0,%rdx
+	add	$A[1],$N[1]
+	lea	4*8($num),$j		# j=4
+	lea	8*4($np),$np
+	adc	\$0,%rdx
+	mov	$N[1],($tp)
+	mov	%rdx,$N[0]
+	jmp	.L1st4x
+
+.align	32
+.L1st4x:
+	mulq	$m0			# ap[j]*bp[0]
+	add	%rax,$A[0]
+	mov	-8*2($np),%rax
+	lea	32($tp),$tp
+	adc	\$0,%rdx
+	mov	%rdx,$A[1]
+
+	mulq	$m1			# np[j]*m1
+	add	%rax,$N[0]
+	mov	-8($ap,$j),%rax
+	adc	\$0,%rdx
+	add	$A[0],$N[0]		# np[j]*m1+ap[j]*bp[0]
+	adc	\$0,%rdx
+	mov	$N[0],-24($tp)		# tp[j-1]
+	mov	%rdx,$N[1]
+
+	mulq	$m0			# ap[j]*bp[0]
+	add	%rax,$A[1]
+	mov	-8*1($np),%rax
+	adc	\$0,%rdx
+	mov	%rdx,$A[0]
+
+	mulq	$m1			# np[j]*m1
+	add	%rax,$N[1]
+	mov	($ap,$j),%rax
+	adc	\$0,%rdx
+	add	$A[1],$N[1]		# np[j]*m1+ap[j]*bp[0]
+	adc	\$0,%rdx
+	mov	$N[1],-16($tp)		# tp[j-1]
+	mov	%rdx,$N[0]
+
+	mulq	$m0			# ap[j]*bp[0]
+	add	%rax,$A[0]
+	mov	8*0($np),%rax
+	adc	\$0,%rdx
+	mov	%rdx,$A[1]
+
+	mulq	$m1			# np[j]*m1
+	add	%rax,$N[0]
+	mov	8($ap,$j),%rax
+	adc	\$0,%rdx
+	add	$A[0],$N[0]		# np[j]*m1+ap[j]*bp[0]
+	adc	\$0,%rdx
+	mov	$N[0],-8($tp)		# tp[j-1]
+	mov	%rdx,$N[1]
+
+	mulq	$m0			# ap[j]*bp[0]
+	add	%rax,$A[1]
+	mov	8*1($np),%rax
+	adc	\$0,%rdx
+	mov	%rdx,$A[0]
+
+	mulq	$m1			# np[j]*m1
+	add	%rax,$N[1]
+	mov	16($ap,$j),%rax
+	adc	\$0,%rdx
+	add	$A[1],$N[1]		# np[j]*m1+ap[j]*bp[0]
+	lea	8*4($np),$np
+	adc	\$0,%rdx
+	mov	$N[1],($tp)		# tp[j-1]
+	mov	%rdx,$N[0]
+
+	add	\$32,$j			# j+=4
+	jnz	.L1st4x
+
+	mulq	$m0			# ap[j]*bp[0]
+	add	%rax,$A[0]
+	mov	-8*2($np),%rax
+	lea	32($tp),$tp
+	adc	\$0,%rdx
+	mov	%rdx,$A[1]
+
+	mulq	$m1			# np[j]*m1
+	add	%rax,$N[0]
+	mov	-8($ap),%rax
+	adc	\$0,%rdx
+	add	$A[0],$N[0]		# np[j]*m1+ap[j]*bp[0]
+	adc	\$0,%rdx
+	mov	$N[0],-24($tp)		# tp[j-1]
+	mov	%rdx,$N[1]
+
+	mulq	$m0			# ap[j]*bp[0]
+	add	%rax,$A[1]
+	mov	-8*1($np),%rax
+	adc	\$0,%rdx
+	mov	%rdx,$A[0]
+
+	mulq	$m1			# np[j]*m1
+	add	%rax,$N[1]
+	mov	($ap,$num),%rax		# ap[0]
+	adc	\$0,%rdx
+	add	$A[1],$N[1]		# np[j]*m1+ap[j]*bp[0]
+	adc	\$0,%rdx
+	mov	$N[1],-16($tp)		# tp[j-1]
+	mov	%rdx,$N[0]
+
+	lea	($np,$num),$np		# rewind $np
+
+	xor	$N[1],$N[1]
+	add	$A[0],$N[0]
+	adc	\$0,$N[1]
+	mov	$N[0],-8($tp)
+
+	jmp	.Louter4x
+
+.align	32
+.Louter4x:
+	lea	16+128($tp),%rdx	# where 256-byte mask is (+size optimization)
+	pxor	%xmm4,%xmm4
+	pxor	%xmm5,%xmm5
+___
+for($i=0;$i<$STRIDE/16;$i+=4) {
+$code.=<<___;
+	movdqa	`16*($i+0)-128`($bp),%xmm0
+	movdqa	`16*($i+1)-128`($bp),%xmm1
+	movdqa	`16*($i+2)-128`($bp),%xmm2
+	movdqa	`16*($i+3)-128`($bp),%xmm3
+	pand	`16*($i+0)-128`(%rdx),%xmm0
+	pand	`16*($i+1)-128`(%rdx),%xmm1
+	por	%xmm0,%xmm4
+	pand	`16*($i+2)-128`(%rdx),%xmm2
+	por	%xmm1,%xmm5
+	pand	`16*($i+3)-128`(%rdx),%xmm3
+	por	%xmm2,%xmm4
+	por	%xmm3,%xmm5
+___
+}
+$code.=<<___;
+	por	%xmm5,%xmm4
+	pshufd	\$0x4e,%xmm4,%xmm0
+	por	%xmm4,%xmm0
+	lea	$STRIDE($bp),$bp
+	movq	%xmm0,$m0		# m0=bp[i]
+
+	mov	($tp,$num),$A[0]
+	mov	$n0,$m1
+	mulq	$m0			# ap[0]*bp[i]
+	add	%rax,$A[0]		# ap[0]*bp[i]+tp[0]
+	mov	($np),%rax
+	adc	\$0,%rdx
+
+	imulq	$A[0],$m1		# tp[0]*n0
+	mov	%rdx,$A[1]
+	mov	$N[1],($tp)		# store upmost overflow bit
+
+	lea	($tp,$num),$tp		# rewind $tp
+
+	mulq	$m1			# np[0]*m1
+	add	%rax,$A[0]		# "$N[0]", discarded
+	mov	8($ap,$num),%rax
+	adc	\$0,%rdx
+	mov	%rdx,$N[1]
+
+	mulq	$m0			# ap[j]*bp[i]
+	add	%rax,$A[1]
+	mov	8*1($np),%rax
+	adc	\$0,%rdx
+	add	8($tp),$A[1]		# +tp[1]
+	adc	\$0,%rdx
+	mov	%rdx,$A[0]
+
+	mulq	$m1			# np[j]*m1
+	add	%rax,$N[1]
+	mov	16($ap,$num),%rax
+	adc	\$0,%rdx
+	add	$A[1],$N[1]		# np[j]*m1+ap[j]*bp[i]+tp[j]
+	lea	4*8($num),$j		# j=4
+	lea	8*4($np),$np
+	adc	\$0,%rdx
+	mov	%rdx,$N[0]
+	jmp	.Linner4x
+
+.align	32
+.Linner4x:
+	mulq	$m0			# ap[j]*bp[i]
+	add	%rax,$A[0]
+	mov	-8*2($np),%rax
+	adc	\$0,%rdx
+	add	16($tp),$A[0]		# ap[j]*bp[i]+tp[j]
+	lea	32($tp),$tp
+	adc	\$0,%rdx
+	mov	%rdx,$A[1]
+
+	mulq	$m1			# np[j]*m1
+	add	%rax,$N[0]
+	mov	-8($ap,$j),%rax
+	adc	\$0,%rdx
+	add	$A[0],$N[0]
+	adc	\$0,%rdx
+	mov	$N[1],-32($tp)		# tp[j-1]
+	mov	%rdx,$N[1]
+
+	mulq	$m0			# ap[j]*bp[i]
+	add	%rax,$A[1]
+	mov	-8*1($np),%rax
+	adc	\$0,%rdx
+	add	-8($tp),$A[1]
+	adc	\$0,%rdx
+	mov	%rdx,$A[0]
+
+	mulq	$m1			# np[j]*m1
+	add	%rax,$N[1]
+	mov	($ap,$j),%rax
+	adc	\$0,%rdx
+	add	$A[1],$N[1]
+	adc	\$0,%rdx
+	mov	$N[0],-24($tp)		# tp[j-1]
+	mov	%rdx,$N[0]
+
+	mulq	$m0			# ap[j]*bp[i]
+	add	%rax,$A[0]
+	mov	8*0($np),%rax
+	adc	\$0,%rdx
+	add	($tp),$A[0]		# ap[j]*bp[i]+tp[j]
+	adc	\$0,%rdx
+	mov	%rdx,$A[1]
+
+	mulq	$m1			# np[j]*m1
+	add	%rax,$N[0]
+	mov	8($ap,$j),%rax
+	adc	\$0,%rdx
+	add	$A[0],$N[0]
+	adc	\$0,%rdx
+	mov	$N[1],-16($tp)		# tp[j-1]
+	mov	%rdx,$N[1]
+
+	mulq	$m0			# ap[j]*bp[i]
+	add	%rax,$A[1]
+	mov	8*1($np),%rax
+	adc	\$0,%rdx
+	add	8($tp),$A[1]
+	adc	\$0,%rdx
+	mov	%rdx,$A[0]
+
+	mulq	$m1			# np[j]*m1
+	add	%rax,$N[1]
+	mov	16($ap,$j),%rax
+	adc	\$0,%rdx
+	add	$A[1],$N[1]
+	lea	8*4($np),$np
+	adc	\$0,%rdx
+	mov	$N[0],-8($tp)		# tp[j-1]
+	mov	%rdx,$N[0]
+
+	add	\$32,$j			# j+=4
+	jnz	.Linner4x
+
+	mulq	$m0			# ap[j]*bp[i]
+	add	%rax,$A[0]
+	mov	-8*2($np),%rax
+	adc	\$0,%rdx
+	add	16($tp),$A[0]		# ap[j]*bp[i]+tp[j]
+	lea	32($tp),$tp
+	adc	\$0,%rdx
+	mov	%rdx,$A[1]
+
+	mulq	$m1			# np[j]*m1
+	add	%rax,$N[0]
+	mov	-8($ap),%rax
+	adc	\$0,%rdx
+	add	$A[0],$N[0]
+	adc	\$0,%rdx
+	mov	$N[1],-32($tp)		# tp[j-1]
+	mov	%rdx,$N[1]
+
+	mulq	$m0			# ap[j]*bp[i]
+	add	%rax,$A[1]
+	mov	$m1,%rax
+	mov	-8*1($np),$m1
+	adc	\$0,%rdx
+	add	-8($tp),$A[1]
+	adc	\$0,%rdx
+	mov	%rdx,$A[0]
+
+	mulq	$m1			# np[j]*m1
+	add	%rax,$N[1]
+	mov	($ap,$num),%rax		# ap[0]
+	adc	\$0,%rdx
+	add	$A[1],$N[1]
+	adc	\$0,%rdx
+	mov	$N[0],-24($tp)		# tp[j-1]
+	mov	%rdx,$N[0]
+
+	mov	$N[1],-16($tp)		# tp[j-1]
+	lea	($np,$num),$np		# rewind $np
+
+	xor	$N[1],$N[1]
+	add	$A[0],$N[0]
+	adc	\$0,$N[1]
+	add	($tp),$N[0]		# pull upmost overflow bit
+	adc	\$0,$N[1]		# upmost overflow bit
+	mov	$N[0],-8($tp)
+
+	cmp	16+8(%rsp),$bp
+	jb	.Louter4x
+___
+if (1) {
+$code.=<<___;
+	xor	%rax,%rax
+	sub	$N[0],$m1		# compare top-most words
+	adc	$j,$j			# $j is zero
+	or	$j,$N[1]
+	sub	$N[1],%rax		# %rax=-$N[1]
+	lea	($tp,$num),%rbx		# tptr in .sqr4x_sub
+	mov	($np),%r12
+	lea	($np),%rbp		# nptr in .sqr4x_sub
+	mov	%r9,%rcx
+	sar	\$3+2,%rcx
+	mov	56+8(%rsp),%rdi		# rptr in .sqr4x_sub
+	dec	%r12			# so that after 'not' we get -n[0]
+	xor	%r10,%r10
+	mov	8*1(%rbp),%r13
+	mov	8*2(%rbp),%r14
+	mov	8*3(%rbp),%r15
+	jmp	.Lsqr4x_sub_entry
+___
+} else {
+my @ri=("%rax",$bp,$m0,$m1);
+my $rp="%rdx";
+$code.=<<___
+	xor	\$1,$N[1]
+	lea	($tp,$num),$tp		# rewind $tp
+	sar	\$5,$num		# cf=0
+	lea	($np,$N[1],8),$np
+	mov	56+8(%rsp),$rp		# restore $rp
+	jmp	.Lsub4x
+
+.align	32
+.Lsub4x:
+	.byte	0x66
+	mov	8*0($tp),@ri[0]
+	mov	8*1($tp),@ri[1]
+	.byte	0x66
+	sbb	16*0($np),@ri[0]
+	mov	8*2($tp),@ri[2]
+	sbb	16*1($np),@ri[1]
+	mov	3*8($tp),@ri[3]
+	lea	4*8($tp),$tp
+	sbb	16*2($np),@ri[2]
+	mov	@ri[0],8*0($rp)
+	sbb	16*3($np),@ri[3]
+	lea	16*4($np),$np
+	mov	@ri[1],8*1($rp)
+	mov	@ri[2],8*2($rp)
+	mov	@ri[3],8*3($rp)
+	lea	8*4($rp),$rp
+
+	inc	$num
+	jnz	.Lsub4x
+
+	ret
+___
+}
+$code.=<<___;
+.size	mul4x_internal,.-mul4x_internal
+___
+}}}
+{{{
+######################################################################
+# void bn_power5(
+my $rptr="%rdi";	# BN_ULONG *rptr,
+my $aptr="%rsi";	# const BN_ULONG *aptr,
+my $bptr="%rdx";	# const void *table,
+my $nptr="%rcx";	# const BN_ULONG *nptr,
+my $n0  ="%r8";		# const BN_ULONG *n0);
+my $num ="%r9";		# int num, has to be divisible by 8
+			# int pwr 
+
+my ($i,$j,$tptr)=("%rbp","%rcx",$rptr);
+my @A0=("%r10","%r11");
+my @A1=("%r12","%r13");
+my ($a0,$a1,$ai)=("%r14","%r15","%rbx");
+
+$code.=<<___;
+.globl	bn_power5
+.type	bn_power5,\@function,6
+.align	32
+bn_power5:
+	mov	%rsp,%rax
+___
+$code.=<<___ if ($addx);
+	mov	OPENSSL_ia32cap_P+8(%rip),%r11d
+	and	\$0x80108,%r11d
+	cmp	\$0x80108,%r11d		# check for AD*X+BMI2+BMI1
+	je	.Lpowerx5_enter
+___
+$code.=<<___;
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+.Lpower5_prologue:
+
+	shl	\$3,${num}d		# convert $num to bytes
+	lea	($num,$num,2),%r10d	# 3*$num
+	neg	$num
+	mov	($n0),$n0		# *n0
+
+	##############################################################
+	# Ensure that stack frame doesn't alias with $rptr+3*$num
+	# modulo 4096, which covers ret[num], am[num] and n[num]
+	# (see bn_exp.c). This is done to allow memory disambiguation
+	# logic do its magic. [Extra 256 bytes is for power mask
+	# calculated from 7th argument, the index.]
+	#
+	lea	-320(%rsp,$num,2),%r11
+	mov	%rsp,%rbp
+	sub	$rptr,%r11
+	and	\$4095,%r11
+	cmp	%r11,%r10
+	jb	.Lpwr_sp_alt
+	sub	%r11,%rbp		# align with $aptr
+	lea	-320(%rbp,$num,2),%rbp	# future alloca(frame+2*num*8+256)
+	jmp	.Lpwr_sp_done
+
+.align	32
+.Lpwr_sp_alt:
+	lea	4096-320(,$num,2),%r10
+	lea	-320(%rbp,$num,2),%rbp	# future alloca(frame+2*num*8+256)
+	sub	%r10,%r11
+	mov	\$0,%r10
+	cmovc	%r10,%r11
+	sub	%r11,%rbp
+.Lpwr_sp_done:
+	and	\$-64,%rbp
+	mov	%rsp,%r11
+	sub	%rbp,%r11
+	and	\$-4096,%r11
+	lea	(%rbp,%r11),%rsp
+	mov	(%rsp),%r10
+	cmp	%rbp,%rsp
+	ja	.Lpwr_page_walk
+	jmp	.Lpwr_page_walk_done
+
+.Lpwr_page_walk:
+	lea	-4096(%rsp),%rsp
+	mov	(%rsp),%r10
+	cmp	%rbp,%rsp
+	ja	.Lpwr_page_walk
+.Lpwr_page_walk_done:
+
+	mov	$num,%r10	
+	neg	$num
+
+	##############################################################
+	# Stack layout
+	#
+	# +0	saved $num, used in reduction section
+	# +8	&t[2*$num], used in reduction section
+	# +32	saved *n0
+	# +40	saved %rsp
+	# +48	t[2*$num]
+	#
+	mov	$n0,  32(%rsp)
+	mov	%rax, 40(%rsp)		# save original %rsp
+.Lpower5_body:
+	movq	$rptr,%xmm1		# save $rptr, used in sqr8x
+	movq	$nptr,%xmm2		# save $nptr
+	movq	%r10, %xmm3		# -$num, used in sqr8x
+	movq	$bptr,%xmm4
+
+	call	__bn_sqr8x_internal
+	call	__bn_post4x_internal
+	call	__bn_sqr8x_internal
+	call	__bn_post4x_internal
+	call	__bn_sqr8x_internal
+	call	__bn_post4x_internal
+	call	__bn_sqr8x_internal
+	call	__bn_post4x_internal
+	call	__bn_sqr8x_internal
+	call	__bn_post4x_internal
+
+	movq	%xmm2,$nptr
+	movq	%xmm4,$bptr
+	mov	$aptr,$rptr
+	mov	40(%rsp),%rax
+	lea	32(%rsp),$n0
+
+	call	mul4x_internal
+
+	mov	40(%rsp),%rsi		# restore %rsp
+	mov	\$1,%rax
+	mov	-48(%rsi),%r15
+	mov	-40(%rsi),%r14
+	mov	-32(%rsi),%r13
+	mov	-24(%rsi),%r12
+	mov	-16(%rsi),%rbp
+	mov	-8(%rsi),%rbx
+	lea	(%rsi),%rsp
+.Lpower5_epilogue:
+	ret
+.size	bn_power5,.-bn_power5
+
+.globl	bn_sqr8x_internal
+.hidden	bn_sqr8x_internal
+.type	bn_sqr8x_internal,\@abi-omnipotent
+.align	32
+bn_sqr8x_internal:
+__bn_sqr8x_internal:
+	##############################################################
+	# Squaring part:
+	#
+	# a) multiply-n-add everything but a[i]*a[i];
+	# b) shift result of a) by 1 to the left and accumulate
+	#    a[i]*a[i] products;
+	#
+	##############################################################
+	#                                                     a[1]a[0]
+	#                                                 a[2]a[0]
+	#                                             a[3]a[0]
+	#                                             a[2]a[1]
+	#                                         a[4]a[0]
+	#                                         a[3]a[1]
+	#                                     a[5]a[0]
+	#                                     a[4]a[1]
+	#                                     a[3]a[2]
+	#                                 a[6]a[0]
+	#                                 a[5]a[1]
+	#                                 a[4]a[2]
+	#                             a[7]a[0]
+	#                             a[6]a[1]
+	#                             a[5]a[2]
+	#                             a[4]a[3]
+	#                         a[7]a[1]
+	#                         a[6]a[2]
+	#                         a[5]a[3]
+	#                     a[7]a[2]
+	#                     a[6]a[3]
+	#                     a[5]a[4]
+	#                 a[7]a[3]
+	#                 a[6]a[4]
+	#             a[7]a[4]
+	#             a[6]a[5]
+	#         a[7]a[5]
+	#     a[7]a[6]
+	#                                                     a[1]a[0]
+	#                                                 a[2]a[0]
+	#                                             a[3]a[0]
+	#                                         a[4]a[0]
+	#                                     a[5]a[0]
+	#                                 a[6]a[0]
+	#                             a[7]a[0]
+	#                                             a[2]a[1]
+	#                                         a[3]a[1]
+	#                                     a[4]a[1]
+	#                                 a[5]a[1]
+	#                             a[6]a[1]
+	#                         a[7]a[1]
+	#                                     a[3]a[2]
+	#                                 a[4]a[2]
+	#                             a[5]a[2]
+	#                         a[6]a[2]
+	#                     a[7]a[2]
+	#                             a[4]a[3]
+	#                         a[5]a[3]
+	#                     a[6]a[3]
+	#                 a[7]a[3]
+	#                     a[5]a[4]
+	#                 a[6]a[4]
+	#             a[7]a[4]
+	#             a[6]a[5]
+	#         a[7]a[5]
+	#     a[7]a[6]
+	#                                                         a[0]a[0]
+	#                                                 a[1]a[1]
+	#                                         a[2]a[2]
+	#                                 a[3]a[3]
+	#                         a[4]a[4]
+	#                 a[5]a[5]
+	#         a[6]a[6]
+	# a[7]a[7]
+
+	lea	32(%r10),$i		# $i=-($num-32)
+	lea	($aptr,$num),$aptr	# end of a[] buffer, ($aptr,$i)=&ap[2]
+
+	mov	$num,$j			# $j=$num
+
+					# comments apply to $num==8 case
+	mov	-32($aptr,$i),$a0	# a[0]
+	lea	48+8(%rsp,$num,2),$tptr	# end of tp[] buffer, &tp[2*$num]
+	mov	-24($aptr,$i),%rax	# a[1]
+	lea	-32($tptr,$i),$tptr	# end of tp[] window, &tp[2*$num-"$i"]
+	mov	-16($aptr,$i),$ai	# a[2]
+	mov	%rax,$a1
+
+	mul	$a0			# a[1]*a[0]
+	mov	%rax,$A0[0]		# a[1]*a[0]
+	 mov	$ai,%rax		# a[2]
+	mov	%rdx,$A0[1]
+	mov	$A0[0],-24($tptr,$i)	# t[1]
+
+	mul	$a0			# a[2]*a[0]
+	add	%rax,$A0[1]
+	 mov	$ai,%rax
+	adc	\$0,%rdx
+	mov	$A0[1],-16($tptr,$i)	# t[2]
+	mov	%rdx,$A0[0]
+
+
+	 mov	-8($aptr,$i),$ai	# a[3]
+	mul	$a1			# a[2]*a[1]
+	mov	%rax,$A1[0]		# a[2]*a[1]+t[3]
+	 mov	$ai,%rax
+	mov	%rdx,$A1[1]
+
+	 lea	($i),$j
+	mul	$a0			# a[3]*a[0]
+	add	%rax,$A0[0]		# a[3]*a[0]+a[2]*a[1]+t[3]
+	 mov	$ai,%rax
+	mov	%rdx,$A0[1]
+	adc	\$0,$A0[1]
+	add	$A1[0],$A0[0]
+	adc	\$0,$A0[1]
+	mov	$A0[0],-8($tptr,$j)	# t[3]
+	jmp	.Lsqr4x_1st
+
+.align	32
+.Lsqr4x_1st:
+	 mov	($aptr,$j),$ai		# a[4]
+	mul	$a1			# a[3]*a[1]
+	add	%rax,$A1[1]		# a[3]*a[1]+t[4]
+	 mov	$ai,%rax
+	mov	%rdx,$A1[0]
+	adc	\$0,$A1[0]
+
+	mul	$a0			# a[4]*a[0]
+	add	%rax,$A0[1]		# a[4]*a[0]+a[3]*a[1]+t[4]
+	 mov	$ai,%rax		# a[3]
+	 mov	8($aptr,$j),$ai		# a[5]
+	mov	%rdx,$A0[0]
+	adc	\$0,$A0[0]
+	add	$A1[1],$A0[1]
+	adc	\$0,$A0[0]
+
+
+	mul	$a1			# a[4]*a[3]
+	add	%rax,$A1[0]		# a[4]*a[3]+t[5]
+	 mov	$ai,%rax
+	 mov	$A0[1],($tptr,$j)	# t[4]
+	mov	%rdx,$A1[1]
+	adc	\$0,$A1[1]
+
+	mul	$a0			# a[5]*a[2]
+	add	%rax,$A0[0]		# a[5]*a[2]+a[4]*a[3]+t[5]
+	 mov	$ai,%rax
+	 mov	16($aptr,$j),$ai	# a[6]
+	mov	%rdx,$A0[1]
+	adc	\$0,$A0[1]
+	add	$A1[0],$A0[0]
+	adc	\$0,$A0[1]
+
+	mul	$a1			# a[5]*a[3]
+	add	%rax,$A1[1]		# a[5]*a[3]+t[6]
+	 mov	$ai,%rax
+	 mov	$A0[0],8($tptr,$j)	# t[5]
+	mov	%rdx,$A1[0]
+	adc	\$0,$A1[0]
+
+	mul	$a0			# a[6]*a[2]
+	add	%rax,$A0[1]		# a[6]*a[2]+a[5]*a[3]+t[6]
+	 mov	$ai,%rax		# a[3]
+	 mov	24($aptr,$j),$ai	# a[7]
+	mov	%rdx,$A0[0]
+	adc	\$0,$A0[0]
+	add	$A1[1],$A0[1]
+	adc	\$0,$A0[0]
+
+
+	mul	$a1			# a[6]*a[5]
+	add	%rax,$A1[0]		# a[6]*a[5]+t[7]
+	 mov	$ai,%rax
+	 mov	$A0[1],16($tptr,$j)	# t[6]
+	mov	%rdx,$A1[1]
+	adc	\$0,$A1[1]
+	 lea	32($j),$j
+
+	mul	$a0			# a[7]*a[4]
+	add	%rax,$A0[0]		# a[7]*a[4]+a[6]*a[5]+t[6]
+	 mov	$ai,%rax
+	mov	%rdx,$A0[1]
+	adc	\$0,$A0[1]
+	add	$A1[0],$A0[0]
+	adc	\$0,$A0[1]
+	mov	$A0[0],-8($tptr,$j)	# t[7]
+
+	cmp	\$0,$j
+	jne	.Lsqr4x_1st
+
+	mul	$a1			# a[7]*a[5]
+	add	%rax,$A1[1]
+	lea	16($i),$i
+	adc	\$0,%rdx
+	add	$A0[1],$A1[1]
+	adc	\$0,%rdx
+
+	mov	$A1[1],($tptr)		# t[8]
+	mov	%rdx,$A1[0]
+	mov	%rdx,8($tptr)		# t[9]
+	jmp	.Lsqr4x_outer
+
+.align	32
+.Lsqr4x_outer:				# comments apply to $num==6 case
+	mov	-32($aptr,$i),$a0	# a[0]
+	lea	48+8(%rsp,$num,2),$tptr	# end of tp[] buffer, &tp[2*$num]
+	mov	-24($aptr,$i),%rax	# a[1]
+	lea	-32($tptr,$i),$tptr	# end of tp[] window, &tp[2*$num-"$i"]
+	mov	-16($aptr,$i),$ai	# a[2]
+	mov	%rax,$a1
+
+	mul	$a0			# a[1]*a[0]
+	mov	-24($tptr,$i),$A0[0]	# t[1]
+	add	%rax,$A0[0]		# a[1]*a[0]+t[1]
+	 mov	$ai,%rax		# a[2]
+	adc	\$0,%rdx
+	mov	$A0[0],-24($tptr,$i)	# t[1]
+	mov	%rdx,$A0[1]
+
+	mul	$a0			# a[2]*a[0]
+	add	%rax,$A0[1]
+	 mov	$ai,%rax
+	adc	\$0,%rdx
+	add	-16($tptr,$i),$A0[1]	# a[2]*a[0]+t[2]
+	mov	%rdx,$A0[0]
+	adc	\$0,$A0[0]
+	mov	$A0[1],-16($tptr,$i)	# t[2]
+
+	xor	$A1[0],$A1[0]
+
+	 mov	-8($aptr,$i),$ai	# a[3]
+	mul	$a1			# a[2]*a[1]
+	add	%rax,$A1[0]		# a[2]*a[1]+t[3]
+	 mov	$ai,%rax
+	adc	\$0,%rdx
+	add	-8($tptr,$i),$A1[0]
+	mov	%rdx,$A1[1]
+	adc	\$0,$A1[1]
+
+	mul	$a0			# a[3]*a[0]
+	add	%rax,$A0[0]		# a[3]*a[0]+a[2]*a[1]+t[3]
+	 mov	$ai,%rax
+	adc	\$0,%rdx
+	add	$A1[0],$A0[0]
+	mov	%rdx,$A0[1]
+	adc	\$0,$A0[1]
+	mov	$A0[0],-8($tptr,$i)	# t[3]
+
+	lea	($i),$j
+	jmp	.Lsqr4x_inner
+
+.align	32
+.Lsqr4x_inner:
+	 mov	($aptr,$j),$ai		# a[4]
+	mul	$a1			# a[3]*a[1]
+	add	%rax,$A1[1]		# a[3]*a[1]+t[4]
+	 mov	$ai,%rax
+	mov	%rdx,$A1[0]
+	adc	\$0,$A1[0]
+	add	($tptr,$j),$A1[1]
+	adc	\$0,$A1[0]
+
+	.byte	0x67
+	mul	$a0			# a[4]*a[0]
+	add	%rax,$A0[1]		# a[4]*a[0]+a[3]*a[1]+t[4]
+	 mov	$ai,%rax		# a[3]
+	 mov	8($aptr,$j),$ai		# a[5]
+	mov	%rdx,$A0[0]
+	adc	\$0,$A0[0]
+	add	$A1[1],$A0[1]
+	adc	\$0,$A0[0]
+
+	mul	$a1			# a[4]*a[3]
+	add	%rax,$A1[0]		# a[4]*a[3]+t[5]
+	mov	$A0[1],($tptr,$j)	# t[4]
+	 mov	$ai,%rax
+	mov	%rdx,$A1[1]
+	adc	\$0,$A1[1]
+	add	8($tptr,$j),$A1[0]
+	lea	16($j),$j		# j++
+	adc	\$0,$A1[1]
+
+	mul	$a0			# a[5]*a[2]
+	add	%rax,$A0[0]		# a[5]*a[2]+a[4]*a[3]+t[5]
+	 mov	$ai,%rax
+	adc	\$0,%rdx
+	add	$A1[0],$A0[0]
+	mov	%rdx,$A0[1]
+	adc	\$0,$A0[1]
+	mov	$A0[0],-8($tptr,$j)	# t[5], "preloaded t[1]" below
+
+	cmp	\$0,$j
+	jne	.Lsqr4x_inner
+
+	.byte	0x67
+	mul	$a1			# a[5]*a[3]
+	add	%rax,$A1[1]
+	adc	\$0,%rdx
+	add	$A0[1],$A1[1]
+	adc	\$0,%rdx
+
+	mov	$A1[1],($tptr)		# t[6], "preloaded t[2]" below
+	mov	%rdx,$A1[0]
+	mov	%rdx,8($tptr)		# t[7], "preloaded t[3]" below
+
+	add	\$16,$i
+	jnz	.Lsqr4x_outer
+
+					# comments apply to $num==4 case
+	mov	-32($aptr),$a0		# a[0]
+	lea	48+8(%rsp,$num,2),$tptr	# end of tp[] buffer, &tp[2*$num]
+	mov	-24($aptr),%rax		# a[1]
+	lea	-32($tptr,$i),$tptr	# end of tp[] window, &tp[2*$num-"$i"]
+	mov	-16($aptr),$ai		# a[2]
+	mov	%rax,$a1
+
+	mul	$a0			# a[1]*a[0]
+	add	%rax,$A0[0]		# a[1]*a[0]+t[1], preloaded t[1]
+	 mov	$ai,%rax		# a[2]
+	mov	%rdx,$A0[1]
+	adc	\$0,$A0[1]
+
+	mul	$a0			# a[2]*a[0]
+	add	%rax,$A0[1]
+	 mov	$ai,%rax
+	 mov	$A0[0],-24($tptr)	# t[1]
+	mov	%rdx,$A0[0]
+	adc	\$0,$A0[0]
+	add	$A1[1],$A0[1]		# a[2]*a[0]+t[2], preloaded t[2]
+	 mov	-8($aptr),$ai		# a[3]
+	adc	\$0,$A0[0]
+
+	mul	$a1			# a[2]*a[1]
+	add	%rax,$A1[0]		# a[2]*a[1]+t[3], preloaded t[3]
+	 mov	$ai,%rax
+	 mov	$A0[1],-16($tptr)	# t[2]
+	mov	%rdx,$A1[1]
+	adc	\$0,$A1[1]
+
+	mul	$a0			# a[3]*a[0]
+	add	%rax,$A0[0]		# a[3]*a[0]+a[2]*a[1]+t[3]
+	 mov	$ai,%rax
+	mov	%rdx,$A0[1]
+	adc	\$0,$A0[1]
+	add	$A1[0],$A0[0]
+	adc	\$0,$A0[1]
+	mov	$A0[0],-8($tptr)	# t[3]
+
+	mul	$a1			# a[3]*a[1]
+	add	%rax,$A1[1]
+	 mov	-16($aptr),%rax		# a[2]
+	adc	\$0,%rdx
+	add	$A0[1],$A1[1]
+	adc	\$0,%rdx
+
+	mov	$A1[1],($tptr)		# t[4]
+	mov	%rdx,$A1[0]
+	mov	%rdx,8($tptr)		# t[5]
+
+	mul	$ai			# a[2]*a[3]
+___
+{
+my ($shift,$carry)=($a0,$a1);
+my @S=(@A1,$ai,$n0);
+$code.=<<___;
+	 add	\$16,$i
+	 xor	$shift,$shift
+	 sub	$num,$i			# $i=16-$num
+	 xor	$carry,$carry
+
+	add	$A1[0],%rax		# t[5]
+	adc	\$0,%rdx
+	mov	%rax,8($tptr)		# t[5]
+	mov	%rdx,16($tptr)		# t[6]
+	mov	$carry,24($tptr)	# t[7]
+
+	 mov	-16($aptr,$i),%rax	# a[0]
+	lea	48+8(%rsp),$tptr
+	 xor	$A0[0],$A0[0]		# t[0]
+	 mov	8($tptr),$A0[1]		# t[1]
+
+	lea	($shift,$A0[0],2),$S[0]	# t[2*i]<<1 | shift
+	shr	\$63,$A0[0]
+	lea	($j,$A0[1],2),$S[1]	# t[2*i+1]<<1 |
+	shr	\$63,$A0[1]
+	or	$A0[0],$S[1]		# | t[2*i]>>63
+	 mov	16($tptr),$A0[0]	# t[2*i+2]	# prefetch
+	mov	$A0[1],$shift		# shift=t[2*i+1]>>63
+	mul	%rax			# a[i]*a[i]
+	neg	$carry			# mov $carry,cf
+	 mov	24($tptr),$A0[1]	# t[2*i+2+1]	# prefetch
+	adc	%rax,$S[0]
+	 mov	-8($aptr,$i),%rax	# a[i+1]	# prefetch
+	mov	$S[0],($tptr)
+	adc	%rdx,$S[1]
+
+	lea	($shift,$A0[0],2),$S[2]	# t[2*i]<<1 | shift
+	 mov	$S[1],8($tptr)
+	 sbb	$carry,$carry		# mov cf,$carry
+	shr	\$63,$A0[0]
+	lea	($j,$A0[1],2),$S[3]	# t[2*i+1]<<1 |
+	shr	\$63,$A0[1]
+	or	$A0[0],$S[3]		# | t[2*i]>>63
+	 mov	32($tptr),$A0[0]	# t[2*i+2]	# prefetch
+	mov	$A0[1],$shift		# shift=t[2*i+1]>>63
+	mul	%rax			# a[i]*a[i]
+	neg	$carry			# mov $carry,cf
+	 mov	40($tptr),$A0[1]	# t[2*i+2+1]	# prefetch
+	adc	%rax,$S[2]
+	 mov	0($aptr,$i),%rax	# a[i+1]	# prefetch
+	mov	$S[2],16($tptr)
+	adc	%rdx,$S[3]
+	lea	16($i),$i
+	mov	$S[3],24($tptr)
+	sbb	$carry,$carry		# mov cf,$carry
+	lea	64($tptr),$tptr
+	jmp	.Lsqr4x_shift_n_add
+
+.align	32
+.Lsqr4x_shift_n_add:
+	lea	($shift,$A0[0],2),$S[0]	# t[2*i]<<1 | shift
+	shr	\$63,$A0[0]
+	lea	($j,$A0[1],2),$S[1]	# t[2*i+1]<<1 |
+	shr	\$63,$A0[1]
+	or	$A0[0],$S[1]		# | t[2*i]>>63
+	 mov	-16($tptr),$A0[0]	# t[2*i+2]	# prefetch
+	mov	$A0[1],$shift		# shift=t[2*i+1]>>63
+	mul	%rax			# a[i]*a[i]
+	neg	$carry			# mov $carry,cf
+	 mov	-8($tptr),$A0[1]	# t[2*i+2+1]	# prefetch
+	adc	%rax,$S[0]
+	 mov	-8($aptr,$i),%rax	# a[i+1]	# prefetch
+	mov	$S[0],-32($tptr)
+	adc	%rdx,$S[1]
+
+	lea	($shift,$A0[0],2),$S[2]	# t[2*i]<<1 | shift
+	 mov	$S[1],-24($tptr)
+	 sbb	$carry,$carry		# mov cf,$carry
+	shr	\$63,$A0[0]
+	lea	($j,$A0[1],2),$S[3]	# t[2*i+1]<<1 |
+	shr	\$63,$A0[1]
+	or	$A0[0],$S[3]		# | t[2*i]>>63
+	 mov	0($tptr),$A0[0]		# t[2*i+2]	# prefetch
+	mov	$A0[1],$shift		# shift=t[2*i+1]>>63
+	mul	%rax			# a[i]*a[i]
+	neg	$carry			# mov $carry,cf
+	 mov	8($tptr),$A0[1]		# t[2*i+2+1]	# prefetch
+	adc	%rax,$S[2]
+	 mov	0($aptr,$i),%rax	# a[i+1]	# prefetch
+	mov	$S[2],-16($tptr)
+	adc	%rdx,$S[3]
+
+	lea	($shift,$A0[0],2),$S[0]	# t[2*i]<<1 | shift
+	 mov	$S[3],-8($tptr)
+	 sbb	$carry,$carry		# mov cf,$carry
+	shr	\$63,$A0[0]
+	lea	($j,$A0[1],2),$S[1]	# t[2*i+1]<<1 |
+	shr	\$63,$A0[1]
+	or	$A0[0],$S[1]		# | t[2*i]>>63
+	 mov	16($tptr),$A0[0]	# t[2*i+2]	# prefetch
+	mov	$A0[1],$shift		# shift=t[2*i+1]>>63
+	mul	%rax			# a[i]*a[i]
+	neg	$carry			# mov $carry,cf
+	 mov	24($tptr),$A0[1]	# t[2*i+2+1]	# prefetch
+	adc	%rax,$S[0]
+	 mov	8($aptr,$i),%rax	# a[i+1]	# prefetch
+	mov	$S[0],0($tptr)
+	adc	%rdx,$S[1]
+
+	lea	($shift,$A0[0],2),$S[2]	# t[2*i]<<1 | shift
+	 mov	$S[1],8($tptr)
+	 sbb	$carry,$carry		# mov cf,$carry
+	shr	\$63,$A0[0]
+	lea	($j,$A0[1],2),$S[3]	# t[2*i+1]<<1 |
+	shr	\$63,$A0[1]
+	or	$A0[0],$S[3]		# | t[2*i]>>63
+	 mov	32($tptr),$A0[0]	# t[2*i+2]	# prefetch
+	mov	$A0[1],$shift		# shift=t[2*i+1]>>63
+	mul	%rax			# a[i]*a[i]
+	neg	$carry			# mov $carry,cf
+	 mov	40($tptr),$A0[1]	# t[2*i+2+1]	# prefetch
+	adc	%rax,$S[2]
+	 mov	16($aptr,$i),%rax	# a[i+1]	# prefetch
+	mov	$S[2],16($tptr)
+	adc	%rdx,$S[3]
+	mov	$S[3],24($tptr)
+	sbb	$carry,$carry		# mov cf,$carry
+	lea	64($tptr),$tptr
+	add	\$32,$i
+	jnz	.Lsqr4x_shift_n_add
+
+	lea	($shift,$A0[0],2),$S[0]	# t[2*i]<<1 | shift
+	.byte	0x67
+	shr	\$63,$A0[0]
+	lea	($j,$A0[1],2),$S[1]	# t[2*i+1]<<1 |
+	shr	\$63,$A0[1]
+	or	$A0[0],$S[1]		# | t[2*i]>>63
+	 mov	-16($tptr),$A0[0]	# t[2*i+2]	# prefetch
+	mov	$A0[1],$shift		# shift=t[2*i+1]>>63
+	mul	%rax			# a[i]*a[i]
+	neg	$carry			# mov $carry,cf
+	 mov	-8($tptr),$A0[1]	# t[2*i+2+1]	# prefetch
+	adc	%rax,$S[0]
+	 mov	-8($aptr),%rax		# a[i+1]	# prefetch
+	mov	$S[0],-32($tptr)
+	adc	%rdx,$S[1]
+
+	lea	($shift,$A0[0],2),$S[2]	# t[2*i]<<1|shift
+	 mov	$S[1],-24($tptr)
+	 sbb	$carry,$carry		# mov cf,$carry
+	shr	\$63,$A0[0]
+	lea	($j,$A0[1],2),$S[3]	# t[2*i+1]<<1 |
+	shr	\$63,$A0[1]
+	or	$A0[0],$S[3]		# | t[2*i]>>63
+	mul	%rax			# a[i]*a[i]
+	neg	$carry			# mov $carry,cf
+	adc	%rax,$S[2]
+	adc	%rdx,$S[3]
+	mov	$S[2],-16($tptr)
+	mov	$S[3],-8($tptr)
+___
+}
+######################################################################
+# Montgomery reduction part, "word-by-word" algorithm.
+#
+# This new path is inspired by multiple submissions from Intel, by
+# Shay Gueron, Vlad Krasnov, Erdinc Ozturk, James Guilford,
+# Vinodh Gopal...
+{
+my ($nptr,$tptr,$carry,$m0)=("%rbp","%rdi","%rsi","%rbx");
+
+$code.=<<___;
+	movq	%xmm2,$nptr
+__bn_sqr8x_reduction:
+	xor	%rax,%rax
+	lea	($nptr,$num),%rcx	# end of n[]
+	lea	48+8(%rsp,$num,2),%rdx	# end of t[] buffer
+	mov	%rcx,0+8(%rsp)
+	lea	48+8(%rsp,$num),$tptr	# end of initial t[] window
+	mov	%rdx,8+8(%rsp)
+	neg	$num
+	jmp	.L8x_reduction_loop
+
+.align	32
+.L8x_reduction_loop:
+	lea	($tptr,$num),$tptr	# start of current t[] window
+	.byte	0x66
+	mov	8*0($tptr),$m0
+	mov	8*1($tptr),%r9
+	mov	8*2($tptr),%r10
+	mov	8*3($tptr),%r11
+	mov	8*4($tptr),%r12
+	mov	8*5($tptr),%r13
+	mov	8*6($tptr),%r14
+	mov	8*7($tptr),%r15
+	mov	%rax,(%rdx)		# store top-most carry bit
+	lea	8*8($tptr),$tptr
+
+	.byte	0x67
+	mov	$m0,%r8
+	imulq	32+8(%rsp),$m0		# n0*a[0]
+	mov	8*0($nptr),%rax		# n[0]
+	mov	\$8,%ecx
+	jmp	.L8x_reduce
+
+.align	32
+.L8x_reduce:
+	mulq	$m0
+	 mov	8*1($nptr),%rax		# n[1]
+	neg	%r8
+	mov	%rdx,%r8
+	adc	\$0,%r8
+
+	mulq	$m0
+	add	%rax,%r9
+	 mov	8*2($nptr),%rax
+	adc	\$0,%rdx
+	add	%r9,%r8
+	 mov	$m0,48-8+8(%rsp,%rcx,8)	# put aside n0*a[i]
+	mov	%rdx,%r9
+	adc	\$0,%r9
+
+	mulq	$m0
+	add	%rax,%r10
+	 mov	8*3($nptr),%rax
+	adc	\$0,%rdx
+	add	%r10,%r9
+	 mov	32+8(%rsp),$carry	# pull n0, borrow $carry
+	mov	%rdx,%r10
+	adc	\$0,%r10
+
+	mulq	$m0
+	add	%rax,%r11
+	 mov	8*4($nptr),%rax
+	adc	\$0,%rdx
+	 imulq	%r8,$carry		# modulo-scheduled
+	add	%r11,%r10
+	mov	%rdx,%r11
+	adc	\$0,%r11
+
+	mulq	$m0
+	add	%rax,%r12
+	 mov	8*5($nptr),%rax
+	adc	\$0,%rdx
+	add	%r12,%r11
+	mov	%rdx,%r12
+	adc	\$0,%r12
+
+	mulq	$m0
+	add	%rax,%r13
+	 mov	8*6($nptr),%rax
+	adc	\$0,%rdx
+	add	%r13,%r12
+	mov	%rdx,%r13
+	adc	\$0,%r13
+
+	mulq	$m0
+	add	%rax,%r14
+	 mov	8*7($nptr),%rax
+	adc	\$0,%rdx
+	add	%r14,%r13
+	mov	%rdx,%r14
+	adc	\$0,%r14
+
+	mulq	$m0
+	 mov	$carry,$m0		# n0*a[i]
+	add	%rax,%r15
+	 mov	8*0($nptr),%rax		# n[0]
+	adc	\$0,%rdx
+	add	%r15,%r14
+	mov	%rdx,%r15
+	adc	\$0,%r15
+
+	dec	%ecx
+	jnz	.L8x_reduce
+
+	lea	8*8($nptr),$nptr
+	xor	%rax,%rax
+	mov	8+8(%rsp),%rdx		# pull end of t[]
+	cmp	0+8(%rsp),$nptr		# end of n[]?
+	jae	.L8x_no_tail
+
+	.byte	0x66
+	add	8*0($tptr),%r8
+	adc	8*1($tptr),%r9
+	adc	8*2($tptr),%r10
+	adc	8*3($tptr),%r11
+	adc	8*4($tptr),%r12
+	adc	8*5($tptr),%r13
+	adc	8*6($tptr),%r14
+	adc	8*7($tptr),%r15
+	sbb	$carry,$carry		# top carry
+
+	mov	48+56+8(%rsp),$m0	# pull n0*a[0]
+	mov	\$8,%ecx
+	mov	8*0($nptr),%rax
+	jmp	.L8x_tail
+
+.align	32
+.L8x_tail:
+	mulq	$m0
+	add	%rax,%r8
+	 mov	8*1($nptr),%rax
+	 mov	%r8,($tptr)		# save result
+	mov	%rdx,%r8
+	adc	\$0,%r8
+
+	mulq	$m0
+	add	%rax,%r9
+	 mov	8*2($nptr),%rax
+	adc	\$0,%rdx
+	add	%r9,%r8
+	 lea	8($tptr),$tptr		# $tptr++
+	mov	%rdx,%r9
+	adc	\$0,%r9
+
+	mulq	$m0
+	add	%rax,%r10
+	 mov	8*3($nptr),%rax
+	adc	\$0,%rdx
+	add	%r10,%r9
+	mov	%rdx,%r10
+	adc	\$0,%r10
+
+	mulq	$m0
+	add	%rax,%r11
+	 mov	8*4($nptr),%rax
+	adc	\$0,%rdx
+	add	%r11,%r10
+	mov	%rdx,%r11
+	adc	\$0,%r11
+
+	mulq	$m0
+	add	%rax,%r12
+	 mov	8*5($nptr),%rax
+	adc	\$0,%rdx
+	add	%r12,%r11
+	mov	%rdx,%r12
+	adc	\$0,%r12
+
+	mulq	$m0
+	add	%rax,%r13
+	 mov	8*6($nptr),%rax
+	adc	\$0,%rdx
+	add	%r13,%r12
+	mov	%rdx,%r13
+	adc	\$0,%r13
+
+	mulq	$m0
+	add	%rax,%r14
+	 mov	8*7($nptr),%rax
+	adc	\$0,%rdx
+	add	%r14,%r13
+	mov	%rdx,%r14
+	adc	\$0,%r14
+
+	mulq	$m0
+	 mov	48-16+8(%rsp,%rcx,8),$m0# pull n0*a[i]
+	add	%rax,%r15
+	adc	\$0,%rdx
+	add	%r15,%r14
+	 mov	8*0($nptr),%rax		# pull n[0]
+	mov	%rdx,%r15
+	adc	\$0,%r15
+
+	dec	%ecx
+	jnz	.L8x_tail
+
+	lea	8*8($nptr),$nptr
+	mov	8+8(%rsp),%rdx		# pull end of t[]
+	cmp	0+8(%rsp),$nptr		# end of n[]?
+	jae	.L8x_tail_done		# break out of loop
+
+	 mov	48+56+8(%rsp),$m0	# pull n0*a[0]
+	neg	$carry
+	 mov	8*0($nptr),%rax		# pull n[0]
+	adc	8*0($tptr),%r8
+	adc	8*1($tptr),%r9
+	adc	8*2($tptr),%r10
+	adc	8*3($tptr),%r11
+	adc	8*4($tptr),%r12
+	adc	8*5($tptr),%r13
+	adc	8*6($tptr),%r14
+	adc	8*7($tptr),%r15
+	sbb	$carry,$carry		# top carry
+
+	mov	\$8,%ecx
+	jmp	.L8x_tail
+
+.align	32
+.L8x_tail_done:
+	xor	%rax,%rax
+	add	(%rdx),%r8		# can this overflow?
+	adc	\$0,%r9
+	adc	\$0,%r10
+	adc	\$0,%r11
+	adc	\$0,%r12
+	adc	\$0,%r13
+	adc	\$0,%r14
+	adc	\$0,%r15
+	adc	\$0,%rax
+
+	neg	$carry
+.L8x_no_tail:
+	adc	8*0($tptr),%r8
+	adc	8*1($tptr),%r9
+	adc	8*2($tptr),%r10
+	adc	8*3($tptr),%r11
+	adc	8*4($tptr),%r12
+	adc	8*5($tptr),%r13
+	adc	8*6($tptr),%r14
+	adc	8*7($tptr),%r15
+	adc	\$0,%rax		# top-most carry
+	 mov	-8($nptr),%rcx		# np[num-1]
+	 xor	$carry,$carry
+
+	movq	%xmm2,$nptr		# restore $nptr
+
+	mov	%r8,8*0($tptr)		# store top 512 bits
+	mov	%r9,8*1($tptr)
+	 movq	%xmm3,$num		# $num is %r9, can't be moved upwards
+	mov	%r10,8*2($tptr)
+	mov	%r11,8*3($tptr)
+	mov	%r12,8*4($tptr)
+	mov	%r13,8*5($tptr)
+	mov	%r14,8*6($tptr)
+	mov	%r15,8*7($tptr)
+	lea	8*8($tptr),$tptr
+
+	cmp	%rdx,$tptr		# end of t[]?
+	jb	.L8x_reduction_loop
+	ret
+.size	bn_sqr8x_internal,.-bn_sqr8x_internal
+___
+}
+##############################################################
+# Post-condition, 4x unrolled
+#
+{
+my ($tptr,$nptr)=("%rbx","%rbp");
+$code.=<<___;
+.type	__bn_post4x_internal,\@abi-omnipotent
+.align	32
+__bn_post4x_internal:
+	mov	8*0($nptr),%r12
+	lea	(%rdi,$num),$tptr	# %rdi was $tptr above
+	mov	$num,%rcx
+	movq	%xmm1,$rptr		# restore $rptr
+	neg	%rax
+	movq	%xmm1,$aptr		# prepare for back-to-back call
+	sar	\$3+2,%rcx
+	dec	%r12			# so that after 'not' we get -n[0]
+	xor	%r10,%r10
+	mov	8*1($nptr),%r13
+	mov	8*2($nptr),%r14
+	mov	8*3($nptr),%r15
+	jmp	.Lsqr4x_sub_entry
+
+.align	16
+.Lsqr4x_sub:
+	mov	8*0($nptr),%r12
+	mov	8*1($nptr),%r13
+	mov	8*2($nptr),%r14
+	mov	8*3($nptr),%r15
+.Lsqr4x_sub_entry:
+	lea	8*4($nptr),$nptr
+	not	%r12
+	not	%r13
+	not	%r14
+	not	%r15
+	and	%rax,%r12
+	and	%rax,%r13
+	and	%rax,%r14
+	and	%rax,%r15
+
+	neg	%r10			# mov %r10,%cf
+	adc	8*0($tptr),%r12
+	adc	8*1($tptr),%r13
+	adc	8*2($tptr),%r14
+	adc	8*3($tptr),%r15
+	mov	%r12,8*0($rptr)
+	lea	8*4($tptr),$tptr
+	mov	%r13,8*1($rptr)
+	sbb	%r10,%r10		# mov %cf,%r10
+	mov	%r14,8*2($rptr)
+	mov	%r15,8*3($rptr)
+	lea	8*4($rptr),$rptr
+
+	inc	%rcx			# pass %cf
+	jnz	.Lsqr4x_sub
+
+	mov	$num,%r10		# prepare for back-to-back call
+	neg	$num			# restore $num	
+	ret
+.size	__bn_post4x_internal,.-__bn_post4x_internal
+___
+}
+{
+$code.=<<___;
+.globl	bn_from_montgomery
+.type	bn_from_montgomery,\@abi-omnipotent
+.align	32
+bn_from_montgomery:
+	testl	\$7,`($win64?"48(%rsp)":"%r9d")`
+	jz	bn_from_mont8x
+	xor	%eax,%eax
+	ret
+.size	bn_from_montgomery,.-bn_from_montgomery
+
+.type	bn_from_mont8x,\@function,6
+.align	32
+bn_from_mont8x:
+	.byte	0x67
+	mov	%rsp,%rax
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+.Lfrom_prologue:
+
+	shl	\$3,${num}d		# convert $num to bytes
+	lea	($num,$num,2),%r10	# 3*$num in bytes
+	neg	$num
+	mov	($n0),$n0		# *n0
+
+	##############################################################
+	# Ensure that stack frame doesn't alias with $rptr+3*$num
+	# modulo 4096, which covers ret[num], am[num] and n[num]
+	# (see bn_exp.c). The stack is allocated to aligned with
+	# bn_power5's frame, and as bn_from_montgomery happens to be
+	# last operation, we use the opportunity to cleanse it.
+	#
+	lea	-320(%rsp,$num,2),%r11
+	mov	%rsp,%rbp
+	sub	$rptr,%r11
+	and	\$4095,%r11
+	cmp	%r11,%r10
+	jb	.Lfrom_sp_alt
+	sub	%r11,%rbp		# align with $aptr
+	lea	-320(%rbp,$num,2),%rbp	# future alloca(frame+2*$num*8+256)
+	jmp	.Lfrom_sp_done
+
+.align	32
+.Lfrom_sp_alt:
+	lea	4096-320(,$num,2),%r10
+	lea	-320(%rbp,$num,2),%rbp	# future alloca(frame+2*$num*8+256)
+	sub	%r10,%r11
+	mov	\$0,%r10
+	cmovc	%r10,%r11
+	sub	%r11,%rbp
+.Lfrom_sp_done:
+	and	\$-64,%rbp
+	mov	%rsp,%r11
+	sub	%rbp,%r11
+	and	\$-4096,%r11
+	lea	(%rbp,%r11),%rsp
+	mov	(%rsp),%r10
+	cmp	%rbp,%rsp
+	ja	.Lfrom_page_walk
+	jmp	.Lfrom_page_walk_done
+
+.Lfrom_page_walk:
+	lea	-4096(%rsp),%rsp
+	mov	(%rsp),%r10
+	cmp	%rbp,%rsp
+	ja	.Lfrom_page_walk
+.Lfrom_page_walk_done:
+
+	mov	$num,%r10
+	neg	$num
+
+	##############################################################
+	# Stack layout
+	#
+	# +0	saved $num, used in reduction section
+	# +8	&t[2*$num], used in reduction section
+	# +32	saved *n0
+	# +40	saved %rsp
+	# +48	t[2*$num]
+	#
+	mov	$n0,  32(%rsp)
+	mov	%rax, 40(%rsp)		# save original %rsp
+.Lfrom_body:
+	mov	$num,%r11
+	lea	48(%rsp),%rax
+	pxor	%xmm0,%xmm0
+	jmp	.Lmul_by_1
+
+.align	32
+.Lmul_by_1:
+	movdqu	($aptr),%xmm1
+	movdqu	16($aptr),%xmm2
+	movdqu	32($aptr),%xmm3
+	movdqa	%xmm0,(%rax,$num)
+	movdqu	48($aptr),%xmm4
+	movdqa	%xmm0,16(%rax,$num)
+	.byte	0x48,0x8d,0xb6,0x40,0x00,0x00,0x00	# lea	64($aptr),$aptr
+	movdqa	%xmm1,(%rax)
+	movdqa	%xmm0,32(%rax,$num)
+	movdqa	%xmm2,16(%rax)
+	movdqa	%xmm0,48(%rax,$num)
+	movdqa	%xmm3,32(%rax)
+	movdqa	%xmm4,48(%rax)
+	lea	64(%rax),%rax
+	sub	\$64,%r11
+	jnz	.Lmul_by_1
+
+	movq	$rptr,%xmm1
+	movq	$nptr,%xmm2
+	.byte	0x67
+	mov	$nptr,%rbp
+	movq	%r10, %xmm3		# -num
+___
+$code.=<<___ if ($addx);
+	mov	OPENSSL_ia32cap_P+8(%rip),%r11d
+	and	\$0x80108,%r11d
+	cmp	\$0x80108,%r11d		# check for AD*X+BMI2+BMI1
+	jne	.Lfrom_mont_nox
+
+	lea	(%rax,$num),$rptr
+	call	__bn_sqrx8x_reduction
+	call	__bn_postx4x_internal
+
+	pxor	%xmm0,%xmm0
+	lea	48(%rsp),%rax
+	mov	40(%rsp),%rsi		# restore %rsp
+	jmp	.Lfrom_mont_zero
+
+.align	32
+.Lfrom_mont_nox:
+___
+$code.=<<___;
+	call	__bn_sqr8x_reduction
+	call	__bn_post4x_internal
+
+	pxor	%xmm0,%xmm0
+	lea	48(%rsp),%rax
+	mov	40(%rsp),%rsi		# restore %rsp
+	jmp	.Lfrom_mont_zero
+
+.align	32
+.Lfrom_mont_zero:
+	movdqa	%xmm0,16*0(%rax)
+	movdqa	%xmm0,16*1(%rax)
+	movdqa	%xmm0,16*2(%rax)
+	movdqa	%xmm0,16*3(%rax)
+	lea	16*4(%rax),%rax
+	sub	\$32,$num
+	jnz	.Lfrom_mont_zero
+
+	mov	\$1,%rax
+	mov	-48(%rsi),%r15
+	mov	-40(%rsi),%r14
+	mov	-32(%rsi),%r13
+	mov	-24(%rsi),%r12
+	mov	-16(%rsi),%rbp
+	mov	-8(%rsi),%rbx
+	lea	(%rsi),%rsp
+.Lfrom_epilogue:
+	ret
+.size	bn_from_mont8x,.-bn_from_mont8x
+___
+}
+}}}
+
+if ($addx) {{{
+my $bp="%rdx";	# restore original value
+
+$code.=<<___;
+.type	bn_mulx4x_mont_gather5,\@function,6
+.align	32
+bn_mulx4x_mont_gather5:
+	mov	%rsp,%rax
+.Lmulx4x_enter:
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+.Lmulx4x_prologue:
+
+	shl	\$3,${num}d		# convert $num to bytes
+	lea	($num,$num,2),%r10	# 3*$num in bytes
+	neg	$num			# -$num
+	mov	($n0),$n0		# *n0
+
+	##############################################################
+	# Ensure that stack frame doesn't alias with $rptr+3*$num
+	# modulo 4096, which covers ret[num], am[num] and n[num]
+	# (see bn_exp.c). This is done to allow memory disambiguation
+	# logic do its magic. [Extra [num] is allocated in order
+	# to align with bn_power5's frame, which is cleansed after
+	# completing exponentiation. Extra 256 bytes is for power mask
+	# calculated from 7th argument, the index.]
+	#
+	lea	-320(%rsp,$num,2),%r11
+	mov	%rsp,%rbp
+	sub	$rp,%r11
+	and	\$4095,%r11
+	cmp	%r11,%r10
+	jb	.Lmulx4xsp_alt
+	sub	%r11,%rbp		# align with $aptr
+	lea	-320(%rbp,$num,2),%rbp	# future alloca(frame+2*$num*8+256)
+	jmp	.Lmulx4xsp_done
+
+.Lmulx4xsp_alt:
+	lea	4096-320(,$num,2),%r10
+	lea	-320(%rbp,$num,2),%rbp	# future alloca(frame+2*$num*8+256)
+	sub	%r10,%r11
+	mov	\$0,%r10
+	cmovc	%r10,%r11
+	sub	%r11,%rbp
+.Lmulx4xsp_done:	
+	and	\$-64,%rbp		# ensure alignment
+	mov	%rsp,%r11
+	sub	%rbp,%r11
+	and	\$-4096,%r11
+	lea	(%rbp,%r11),%rsp
+	mov	(%rsp),%r10
+	cmp	%rbp,%rsp
+	ja	.Lmulx4x_page_walk
+	jmp	.Lmulx4x_page_walk_done
+
+.Lmulx4x_page_walk:
+	lea	-4096(%rsp),%rsp
+	mov	(%rsp),%r10
+	cmp	%rbp,%rsp
+	ja	.Lmulx4x_page_walk
+.Lmulx4x_page_walk_done:
+
+	##############################################################
+	# Stack layout
+	# +0	-num
+	# +8	off-loaded &b[i]
+	# +16	end of b[num]
+	# +24	inner counter
+	# +32	saved n0
+	# +40	saved %rsp
+	# +48
+	# +56	saved rp
+	# +64	tmp[num+1]
+	#
+	mov	$n0, 32(%rsp)		# save *n0
+	mov	%rax,40(%rsp)		# save original %rsp
+.Lmulx4x_body:
+	call	mulx4x_internal
+
+	mov	40(%rsp),%rsi		# restore %rsp
+	mov	\$1,%rax
+
+	mov	-48(%rsi),%r15
+	mov	-40(%rsi),%r14
+	mov	-32(%rsi),%r13
+	mov	-24(%rsi),%r12
+	mov	-16(%rsi),%rbp
+	mov	-8(%rsi),%rbx
+	lea	(%rsi),%rsp
+.Lmulx4x_epilogue:
+	ret
+.size	bn_mulx4x_mont_gather5,.-bn_mulx4x_mont_gather5
+
+.type	mulx4x_internal,\@abi-omnipotent
+.align	32
+mulx4x_internal:
+	mov	$num,8(%rsp)		# save -$num (it was in bytes)
+	mov	$num,%r10
+	neg	$num			# restore $num
+	shl	\$5,$num
+	neg	%r10			# restore $num
+	lea	128($bp,$num),%r13	# end of powers table (+size optimization)
+	shr	\$5+5,$num
+	movd	`($win64?56:8)`(%rax),%xmm5	# load 7th argument
+	sub	\$1,$num
+	lea	.Linc(%rip),%rax
+	mov	%r13,16+8(%rsp)		# end of b[num]
+	mov	$num,24+8(%rsp)		# inner counter
+	mov	$rp, 56+8(%rsp)		# save $rp
+___
+my ($aptr, $bptr, $nptr, $tptr, $mi,  $bi,  $zero, $num)=
+   ("%rsi","%rdi","%rcx","%rbx","%r8","%r9","%rbp","%rax");
+my $rptr=$bptr;
+my $STRIDE=2**5*8;		# 5 is "window size"
+my $N=$STRIDE/4;		# should match cache line size
+$code.=<<___;
+	movdqa	0(%rax),%xmm0		# 00000001000000010000000000000000
+	movdqa	16(%rax),%xmm1		# 00000002000000020000000200000002
+	lea	88-112(%rsp,%r10),%r10	# place the mask after tp[num+1] (+ICache optimizaton)
+	lea	128($bp),$bptr		# size optimization
+
+	pshufd	\$0,%xmm5,%xmm5		# broadcast index
+	movdqa	%xmm1,%xmm4
+	.byte	0x67
+	movdqa	%xmm1,%xmm2
+___
+########################################################################
+# calculate mask by comparing 0..31 to index and save result to stack
+#
+$code.=<<___;
+	.byte	0x67
+	paddd	%xmm0,%xmm1
+	pcmpeqd	%xmm5,%xmm0		# compare to 1,0
+	movdqa	%xmm4,%xmm3
+___
+for($i=0;$i<$STRIDE/16-4;$i+=4) {
+$code.=<<___;
+	paddd	%xmm1,%xmm2
+	pcmpeqd	%xmm5,%xmm1		# compare to 3,2
+	movdqa	%xmm0,`16*($i+0)+112`(%r10)
+	movdqa	%xmm4,%xmm0
+
+	paddd	%xmm2,%xmm3
+	pcmpeqd	%xmm5,%xmm2		# compare to 5,4
+	movdqa	%xmm1,`16*($i+1)+112`(%r10)
+	movdqa	%xmm4,%xmm1
+
+	paddd	%xmm3,%xmm0
+	pcmpeqd	%xmm5,%xmm3		# compare to 7,6
+	movdqa	%xmm2,`16*($i+2)+112`(%r10)
+	movdqa	%xmm4,%xmm2
+
+	paddd	%xmm0,%xmm1
+	pcmpeqd	%xmm5,%xmm0
+	movdqa	%xmm3,`16*($i+3)+112`(%r10)
+	movdqa	%xmm4,%xmm3
+___
+}
+$code.=<<___;				# last iteration can be optimized
+	.byte	0x67
+	paddd	%xmm1,%xmm2
+	pcmpeqd	%xmm5,%xmm1
+	movdqa	%xmm0,`16*($i+0)+112`(%r10)
+
+	paddd	%xmm2,%xmm3
+	pcmpeqd	%xmm5,%xmm2
+	movdqa	%xmm1,`16*($i+1)+112`(%r10)
+
+	pcmpeqd	%xmm5,%xmm3
+	movdqa	%xmm2,`16*($i+2)+112`(%r10)
+
+	pand	`16*($i+0)-128`($bptr),%xmm0	# while it's still in register
+	pand	`16*($i+1)-128`($bptr),%xmm1
+	pand	`16*($i+2)-128`($bptr),%xmm2
+	movdqa	%xmm3,`16*($i+3)+112`(%r10)
+	pand	`16*($i+3)-128`($bptr),%xmm3
+	por	%xmm2,%xmm0
+	por	%xmm3,%xmm1
+___
+for($i=0;$i<$STRIDE/16-4;$i+=4) {
+$code.=<<___;
+	movdqa	`16*($i+0)-128`($bptr),%xmm4
+	movdqa	`16*($i+1)-128`($bptr),%xmm5
+	movdqa	`16*($i+2)-128`($bptr),%xmm2
+	pand	`16*($i+0)+112`(%r10),%xmm4
+	movdqa	`16*($i+3)-128`($bptr),%xmm3
+	pand	`16*($i+1)+112`(%r10),%xmm5
+	por	%xmm4,%xmm0
+	pand	`16*($i+2)+112`(%r10),%xmm2
+	por	%xmm5,%xmm1
+	pand	`16*($i+3)+112`(%r10),%xmm3
+	por	%xmm2,%xmm0
+	por	%xmm3,%xmm1
+___
+}
+$code.=<<___;
+	pxor	%xmm1,%xmm0
+	pshufd	\$0x4e,%xmm0,%xmm1
+	por	%xmm1,%xmm0
+	lea	$STRIDE($bptr),$bptr
+	movq	%xmm0,%rdx		# bp[0]
+	lea	64+8*4+8(%rsp),$tptr
+
+	mov	%rdx,$bi
+	mulx	0*8($aptr),$mi,%rax	# a[0]*b[0]
+	mulx	1*8($aptr),%r11,%r12	# a[1]*b[0]
+	add	%rax,%r11
+	mulx	2*8($aptr),%rax,%r13	# ...
+	adc	%rax,%r12
+	adc	\$0,%r13
+	mulx	3*8($aptr),%rax,%r14
+
+	mov	$mi,%r15
+	imulq	32+8(%rsp),$mi		# "t[0]"*n0
+	xor	$zero,$zero		# cf=0, of=0
+	mov	$mi,%rdx
+
+	mov	$bptr,8+8(%rsp)		# off-load &b[i]
+
+	lea	4*8($aptr),$aptr
+	adcx	%rax,%r13
+	adcx	$zero,%r14		# cf=0
+
+	mulx	0*8($nptr),%rax,%r10
+	adcx	%rax,%r15		# discarded
+	adox	%r11,%r10
+	mulx	1*8($nptr),%rax,%r11
+	adcx	%rax,%r10
+	adox	%r12,%r11
+	mulx	2*8($nptr),%rax,%r12
+	mov	24+8(%rsp),$bptr	# counter value
+	mov	%r10,-8*4($tptr)
+	adcx	%rax,%r11
+	adox	%r13,%r12
+	mulx	3*8($nptr),%rax,%r15
+	 mov	$bi,%rdx
+	mov	%r11,-8*3($tptr)
+	adcx	%rax,%r12
+	adox	$zero,%r15		# of=0
+	lea	4*8($nptr),$nptr
+	mov	%r12,-8*2($tptr)
+	jmp	.Lmulx4x_1st
+
+.align	32
+.Lmulx4x_1st:
+	adcx	$zero,%r15		# cf=0, modulo-scheduled
+	mulx	0*8($aptr),%r10,%rax	# a[4]*b[0]
+	adcx	%r14,%r10
+	mulx	1*8($aptr),%r11,%r14	# a[5]*b[0]
+	adcx	%rax,%r11
+	mulx	2*8($aptr),%r12,%rax	# ...
+	adcx	%r14,%r12
+	mulx	3*8($aptr),%r13,%r14
+	 .byte	0x67,0x67
+	 mov	$mi,%rdx
+	adcx	%rax,%r13
+	adcx	$zero,%r14		# cf=0
+	lea	4*8($aptr),$aptr
+	lea	4*8($tptr),$tptr
+
+	adox	%r15,%r10
+	mulx	0*8($nptr),%rax,%r15
+	adcx	%rax,%r10
+	adox	%r15,%r11
+	mulx	1*8($nptr),%rax,%r15
+	adcx	%rax,%r11
+	adox	%r15,%r12
+	mulx	2*8($nptr),%rax,%r15
+	mov	%r10,-5*8($tptr)
+	adcx	%rax,%r12
+	mov	%r11,-4*8($tptr)
+	adox	%r15,%r13
+	mulx	3*8($nptr),%rax,%r15
+	 mov	$bi,%rdx
+	mov	%r12,-3*8($tptr)
+	adcx	%rax,%r13
+	adox	$zero,%r15
+	lea	4*8($nptr),$nptr
+	mov	%r13,-2*8($tptr)
+
+	dec	$bptr			# of=0, pass cf
+	jnz	.Lmulx4x_1st
+
+	mov	8(%rsp),$num		# load -num
+	adc	$zero,%r15		# modulo-scheduled
+	lea	($aptr,$num),$aptr	# rewind $aptr
+	add	%r15,%r14
+	mov	8+8(%rsp),$bptr		# re-load &b[i]
+	adc	$zero,$zero		# top-most carry
+	mov	%r14,-1*8($tptr)
+	jmp	.Lmulx4x_outer
+
+.align	32
+.Lmulx4x_outer:
+	lea	16-256($tptr),%r10	# where 256-byte mask is (+density control)
+	pxor	%xmm4,%xmm4
+	.byte	0x67,0x67
+	pxor	%xmm5,%xmm5
+___
+for($i=0;$i<$STRIDE/16;$i+=4) {
+$code.=<<___;
+	movdqa	`16*($i+0)-128`($bptr),%xmm0
+	movdqa	`16*($i+1)-128`($bptr),%xmm1
+	movdqa	`16*($i+2)-128`($bptr),%xmm2
+	pand	`16*($i+0)+256`(%r10),%xmm0
+	movdqa	`16*($i+3)-128`($bptr),%xmm3
+	pand	`16*($i+1)+256`(%r10),%xmm1
+	por	%xmm0,%xmm4
+	pand	`16*($i+2)+256`(%r10),%xmm2
+	por	%xmm1,%xmm5
+	pand	`16*($i+3)+256`(%r10),%xmm3
+	por	%xmm2,%xmm4
+	por	%xmm3,%xmm5
+___
+}
+$code.=<<___;
+	por	%xmm5,%xmm4
+	pshufd	\$0x4e,%xmm4,%xmm0
+	por	%xmm4,%xmm0
+	lea	$STRIDE($bptr),$bptr
+	movq	%xmm0,%rdx		# m0=bp[i]
+
+	mov	$zero,($tptr)		# save top-most carry
+	lea	4*8($tptr,$num),$tptr	# rewind $tptr
+	mulx	0*8($aptr),$mi,%r11	# a[0]*b[i]
+	xor	$zero,$zero		# cf=0, of=0
+	mov	%rdx,$bi
+	mulx	1*8($aptr),%r14,%r12	# a[1]*b[i]
+	adox	-4*8($tptr),$mi		# +t[0]
+	adcx	%r14,%r11
+	mulx	2*8($aptr),%r15,%r13	# ...
+	adox	-3*8($tptr),%r11
+	adcx	%r15,%r12
+	mulx	3*8($aptr),%rdx,%r14
+	adox	-2*8($tptr),%r12
+	adcx	%rdx,%r13
+	lea	($nptr,$num),$nptr	# rewind $nptr
+	lea	4*8($aptr),$aptr
+	adox	-1*8($tptr),%r13
+	adcx	$zero,%r14
+	adox	$zero,%r14
+
+	mov	$mi,%r15
+	imulq	32+8(%rsp),$mi		# "t[0]"*n0
+
+	mov	$mi,%rdx
+	xor	$zero,$zero		# cf=0, of=0
+	mov	$bptr,8+8(%rsp)		# off-load &b[i]
+
+	mulx	0*8($nptr),%rax,%r10
+	adcx	%rax,%r15		# discarded
+	adox	%r11,%r10
+	mulx	1*8($nptr),%rax,%r11
+	adcx	%rax,%r10
+	adox	%r12,%r11
+	mulx	2*8($nptr),%rax,%r12
+	adcx	%rax,%r11
+	adox	%r13,%r12
+	mulx	3*8($nptr),%rax,%r15
+	 mov	$bi,%rdx
+	mov	24+8(%rsp),$bptr	# counter value
+	mov	%r10,-8*4($tptr)
+	adcx	%rax,%r12
+	mov	%r11,-8*3($tptr)
+	adox	$zero,%r15		# of=0
+	mov	%r12,-8*2($tptr)
+	lea	4*8($nptr),$nptr
+	jmp	.Lmulx4x_inner
+
+.align	32
+.Lmulx4x_inner:
+	mulx	0*8($aptr),%r10,%rax	# a[4]*b[i]
+	adcx	$zero,%r15		# cf=0, modulo-scheduled
+	adox	%r14,%r10
+	mulx	1*8($aptr),%r11,%r14	# a[5]*b[i]
+	adcx	0*8($tptr),%r10
+	adox	%rax,%r11
+	mulx	2*8($aptr),%r12,%rax	# ...
+	adcx	1*8($tptr),%r11
+	adox	%r14,%r12
+	mulx	3*8($aptr),%r13,%r14
+	 mov	$mi,%rdx
+	adcx	2*8($tptr),%r12
+	adox	%rax,%r13
+	adcx	3*8($tptr),%r13
+	adox	$zero,%r14		# of=0
+	lea	4*8($aptr),$aptr
+	lea	4*8($tptr),$tptr
+	adcx	$zero,%r14		# cf=0
+
+	adox	%r15,%r10
+	mulx	0*8($nptr),%rax,%r15
+	adcx	%rax,%r10
+	adox	%r15,%r11
+	mulx	1*8($nptr),%rax,%r15
+	adcx	%rax,%r11
+	adox	%r15,%r12
+	mulx	2*8($nptr),%rax,%r15
+	mov	%r10,-5*8($tptr)
+	adcx	%rax,%r12
+	adox	%r15,%r13
+	mov	%r11,-4*8($tptr)
+	mulx	3*8($nptr),%rax,%r15
+	 mov	$bi,%rdx
+	lea	4*8($nptr),$nptr
+	mov	%r12,-3*8($tptr)
+	adcx	%rax,%r13
+	adox	$zero,%r15
+	mov	%r13,-2*8($tptr)
+
+	dec	$bptr			# of=0, pass cf
+	jnz	.Lmulx4x_inner
+
+	mov	0+8(%rsp),$num		# load -num
+	adc	$zero,%r15		# modulo-scheduled
+	sub	0*8($tptr),$bptr	# pull top-most carry to %cf
+	mov	8+8(%rsp),$bptr		# re-load &b[i]
+	mov	16+8(%rsp),%r10
+	adc	%r15,%r14
+	lea	($aptr,$num),$aptr	# rewind $aptr
+	adc	$zero,$zero		# top-most carry
+	mov	%r14,-1*8($tptr)
+
+	cmp	%r10,$bptr
+	jb	.Lmulx4x_outer
+
+	mov	-8($nptr),%r10
+	mov	$zero,%r8
+	mov	($nptr,$num),%r12
+	lea	($nptr,$num),%rbp	# rewind $nptr
+	mov	$num,%rcx
+	lea	($tptr,$num),%rdi	# rewind $tptr
+	xor	%eax,%eax
+	xor	%r15,%r15
+	sub	%r14,%r10		# compare top-most words
+	adc	%r15,%r15
+	or	%r15,%r8
+	sar	\$3+2,%rcx
+	sub	%r8,%rax		# %rax=-%r8
+	mov	56+8(%rsp),%rdx		# restore rp
+	dec	%r12			# so that after 'not' we get -n[0]
+	mov	8*1(%rbp),%r13
+	xor	%r8,%r8
+	mov	8*2(%rbp),%r14
+	mov	8*3(%rbp),%r15
+	jmp	.Lsqrx4x_sub_entry	# common post-condition
+.size	mulx4x_internal,.-mulx4x_internal
+___
+}{
+######################################################################
+# void bn_power5(
+my $rptr="%rdi";	# BN_ULONG *rptr,
+my $aptr="%rsi";	# const BN_ULONG *aptr,
+my $bptr="%rdx";	# const void *table,
+my $nptr="%rcx";	# const BN_ULONG *nptr,
+my $n0  ="%r8";		# const BN_ULONG *n0);
+my $num ="%r9";		# int num, has to be divisible by 8
+			# int pwr);
+
+my ($i,$j,$tptr)=("%rbp","%rcx",$rptr);
+my @A0=("%r10","%r11");
+my @A1=("%r12","%r13");
+my ($a0,$a1,$ai)=("%r14","%r15","%rbx");
+
+$code.=<<___;
+.type	bn_powerx5,\@function,6
+.align	32
+bn_powerx5:
+	mov	%rsp,%rax
+.Lpowerx5_enter:
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+.Lpowerx5_prologue:
+
+	shl	\$3,${num}d		# convert $num to bytes
+	lea	($num,$num,2),%r10	# 3*$num in bytes
+	neg	$num
+	mov	($n0),$n0		# *n0
+
+	##############################################################
+	# Ensure that stack frame doesn't alias with $rptr+3*$num
+	# modulo 4096, which covers ret[num], am[num] and n[num]
+	# (see bn_exp.c). This is done to allow memory disambiguation
+	# logic do its magic. [Extra 256 bytes is for power mask
+	# calculated from 7th argument, the index.]
+	#
+	lea	-320(%rsp,$num,2),%r11
+	mov	%rsp,%rbp
+	sub	$rptr,%r11
+	and	\$4095,%r11
+	cmp	%r11,%r10
+	jb	.Lpwrx_sp_alt
+	sub	%r11,%rbp		# align with $aptr
+	lea	-320(%rbp,$num,2),%rbp	# future alloca(frame+2*$num*8+256)
+	jmp	.Lpwrx_sp_done
+
+.align	32
+.Lpwrx_sp_alt:
+	lea	4096-320(,$num,2),%r10
+	lea	-320(%rbp,$num,2),%rbp	# alloca(frame+2*$num*8+256)
+	sub	%r10,%r11
+	mov	\$0,%r10
+	cmovc	%r10,%r11
+	sub	%r11,%rbp
+.Lpwrx_sp_done:
+	and	\$-64,%rbp
+	mov	%rsp,%r11
+	sub	%rbp,%r11
+	and	\$-4096,%r11
+	lea	(%rbp,%r11),%rsp
+	mov	(%rsp),%r10
+	cmp	%rbp,%rsp
+	ja	.Lpwrx_page_walk
+	jmp	.Lpwrx_page_walk_done
+
+.Lpwrx_page_walk:
+	lea	-4096(%rsp),%rsp
+	mov	(%rsp),%r10
+	cmp	%rbp,%rsp
+	ja	.Lpwrx_page_walk
+.Lpwrx_page_walk_done:
+
+	mov	$num,%r10	
+	neg	$num
+
+	##############################################################
+	# Stack layout
+	#
+	# +0	saved $num, used in reduction section
+	# +8	&t[2*$num], used in reduction section
+	# +16	intermediate carry bit
+	# +24	top-most carry bit, used in reduction section
+	# +32	saved *n0
+	# +40	saved %rsp
+	# +48	t[2*$num]
+	#
+	pxor	%xmm0,%xmm0
+	movq	$rptr,%xmm1		# save $rptr
+	movq	$nptr,%xmm2		# save $nptr
+	movq	%r10, %xmm3		# -$num
+	movq	$bptr,%xmm4
+	mov	$n0,  32(%rsp)
+	mov	%rax, 40(%rsp)		# save original %rsp
+.Lpowerx5_body:
+
+	call	__bn_sqrx8x_internal
+	call	__bn_postx4x_internal
+	call	__bn_sqrx8x_internal
+	call	__bn_postx4x_internal
+	call	__bn_sqrx8x_internal
+	call	__bn_postx4x_internal
+	call	__bn_sqrx8x_internal
+	call	__bn_postx4x_internal
+	call	__bn_sqrx8x_internal
+	call	__bn_postx4x_internal
+
+	mov	%r10,$num		# -num
+	mov	$aptr,$rptr
+	movq	%xmm2,$nptr
+	movq	%xmm4,$bptr
+	mov	40(%rsp),%rax
+
+	call	mulx4x_internal
+
+	mov	40(%rsp),%rsi		# restore %rsp
+	mov	\$1,%rax
+
+	mov	-48(%rsi),%r15
+	mov	-40(%rsi),%r14
+	mov	-32(%rsi),%r13
+	mov	-24(%rsi),%r12
+	mov	-16(%rsi),%rbp
+	mov	-8(%rsi),%rbx
+	lea	(%rsi),%rsp
+.Lpowerx5_epilogue:
+	ret
+.size	bn_powerx5,.-bn_powerx5
+
+.globl	bn_sqrx8x_internal
+.hidden	bn_sqrx8x_internal
+.type	bn_sqrx8x_internal,\@abi-omnipotent
+.align	32
+bn_sqrx8x_internal:
+__bn_sqrx8x_internal:
+	##################################################################
+	# Squaring part:
+	#
+	# a) multiply-n-add everything but a[i]*a[i];
+	# b) shift result of a) by 1 to the left and accumulate
+	#    a[i]*a[i] products;
+	#
+	##################################################################
+	# a[7]a[7]a[6]a[6]a[5]a[5]a[4]a[4]a[3]a[3]a[2]a[2]a[1]a[1]a[0]a[0]
+	#                                                     a[1]a[0]
+	#                                                 a[2]a[0]
+	#                                             a[3]a[0]
+	#                                             a[2]a[1]
+	#                                         a[3]a[1]
+	#                                     a[3]a[2]
+	#
+	#                                         a[4]a[0]
+	#                                     a[5]a[0]
+	#                                 a[6]a[0]
+	#                             a[7]a[0]
+	#                                     a[4]a[1]
+	#                                 a[5]a[1]
+	#                             a[6]a[1]
+	#                         a[7]a[1]
+	#                                 a[4]a[2]
+	#                             a[5]a[2]
+	#                         a[6]a[2]
+	#                     a[7]a[2]
+	#                             a[4]a[3]
+	#                         a[5]a[3]
+	#                     a[6]a[3]
+	#                 a[7]a[3]
+	#
+	#                     a[5]a[4]
+	#                 a[6]a[4]
+	#             a[7]a[4]
+	#             a[6]a[5]
+	#         a[7]a[5]
+	#     a[7]a[6]
+	# a[7]a[7]a[6]a[6]a[5]a[5]a[4]a[4]a[3]a[3]a[2]a[2]a[1]a[1]a[0]a[0]
+___
+{
+my ($zero,$carry)=("%rbp","%rcx");
+my $aaptr=$zero;
+$code.=<<___;
+	lea	48+8(%rsp),$tptr
+	lea	($aptr,$num),$aaptr
+	mov	$num,0+8(%rsp)			# save $num
+	mov	$aaptr,8+8(%rsp)		# save end of $aptr
+	jmp	.Lsqr8x_zero_start
+
+.align	32
+.byte	0x66,0x66,0x66,0x2e,0x0f,0x1f,0x84,0x00,0x00,0x00,0x00,0x00
+.Lsqrx8x_zero:
+	.byte	0x3e
+	movdqa	%xmm0,0*8($tptr)
+	movdqa	%xmm0,2*8($tptr)
+	movdqa	%xmm0,4*8($tptr)
+	movdqa	%xmm0,6*8($tptr)
+.Lsqr8x_zero_start:			# aligned at 32
+	movdqa	%xmm0,8*8($tptr)
+	movdqa	%xmm0,10*8($tptr)
+	movdqa	%xmm0,12*8($tptr)
+	movdqa	%xmm0,14*8($tptr)
+	lea	16*8($tptr),$tptr
+	sub	\$64,$num
+	jnz	.Lsqrx8x_zero
+
+	mov	0*8($aptr),%rdx		# a[0], modulo-scheduled
+	#xor	%r9,%r9			# t[1], ex-$num, zero already
+	xor	%r10,%r10
+	xor	%r11,%r11
+	xor	%r12,%r12
+	xor	%r13,%r13
+	xor	%r14,%r14
+	xor	%r15,%r15
+	lea	48+8(%rsp),$tptr
+	xor	$zero,$zero		# cf=0, cf=0
+	jmp	.Lsqrx8x_outer_loop
+
+.align	32
+.Lsqrx8x_outer_loop:
+	mulx	1*8($aptr),%r8,%rax	# a[1]*a[0]
+	adcx	%r9,%r8			# a[1]*a[0]+=t[1]
+	adox	%rax,%r10
+	mulx	2*8($aptr),%r9,%rax	# a[2]*a[0]
+	adcx	%r10,%r9
+	adox	%rax,%r11
+	.byte	0xc4,0xe2,0xab,0xf6,0x86,0x18,0x00,0x00,0x00	# mulx	3*8($aptr),%r10,%rax	# ...
+	adcx	%r11,%r10
+	adox	%rax,%r12
+	.byte	0xc4,0xe2,0xa3,0xf6,0x86,0x20,0x00,0x00,0x00	# mulx	4*8($aptr),%r11,%rax
+	adcx	%r12,%r11
+	adox	%rax,%r13
+	mulx	5*8($aptr),%r12,%rax
+	adcx	%r13,%r12
+	adox	%rax,%r14
+	mulx	6*8($aptr),%r13,%rax
+	adcx	%r14,%r13
+	adox	%r15,%rax
+	mulx	7*8($aptr),%r14,%r15
+	 mov	1*8($aptr),%rdx		# a[1]
+	adcx	%rax,%r14
+	adox	$zero,%r15
+	adc	8*8($tptr),%r15
+	mov	%r8,1*8($tptr)		# t[1]
+	mov	%r9,2*8($tptr)		# t[2]
+	sbb	$carry,$carry		# mov %cf,$carry
+	xor	$zero,$zero		# cf=0, of=0
+
+
+	mulx	2*8($aptr),%r8,%rbx	# a[2]*a[1]
+	mulx	3*8($aptr),%r9,%rax	# a[3]*a[1]
+	adcx	%r10,%r8
+	adox	%rbx,%r9
+	mulx	4*8($aptr),%r10,%rbx	# ...
+	adcx	%r11,%r9
+	adox	%rax,%r10
+	.byte	0xc4,0xe2,0xa3,0xf6,0x86,0x28,0x00,0x00,0x00	# mulx	5*8($aptr),%r11,%rax
+	adcx	%r12,%r10
+	adox	%rbx,%r11
+	.byte	0xc4,0xe2,0x9b,0xf6,0x9e,0x30,0x00,0x00,0x00	# mulx	6*8($aptr),%r12,%rbx
+	adcx	%r13,%r11
+	adox	%r14,%r12
+	.byte	0xc4,0x62,0x93,0xf6,0xb6,0x38,0x00,0x00,0x00	# mulx	7*8($aptr),%r13,%r14
+	 mov	2*8($aptr),%rdx		# a[2]
+	adcx	%rax,%r12
+	adox	%rbx,%r13
+	adcx	%r15,%r13
+	adox	$zero,%r14		# of=0
+	adcx	$zero,%r14		# cf=0
+
+	mov	%r8,3*8($tptr)		# t[3]
+	mov	%r9,4*8($tptr)		# t[4]
+
+	mulx	3*8($aptr),%r8,%rbx	# a[3]*a[2]
+	mulx	4*8($aptr),%r9,%rax	# a[4]*a[2]
+	adcx	%r10,%r8
+	adox	%rbx,%r9
+	mulx	5*8($aptr),%r10,%rbx	# ...
+	adcx	%r11,%r9
+	adox	%rax,%r10
+	.byte	0xc4,0xe2,0xa3,0xf6,0x86,0x30,0x00,0x00,0x00	# mulx	6*8($aptr),%r11,%rax
+	adcx	%r12,%r10
+	adox	%r13,%r11
+	.byte	0xc4,0x62,0x9b,0xf6,0xae,0x38,0x00,0x00,0x00	# mulx	7*8($aptr),%r12,%r13
+	.byte	0x3e
+	 mov	3*8($aptr),%rdx		# a[3]
+	adcx	%rbx,%r11
+	adox	%rax,%r12
+	adcx	%r14,%r12
+	mov	%r8,5*8($tptr)		# t[5]
+	mov	%r9,6*8($tptr)		# t[6]
+	 mulx	4*8($aptr),%r8,%rax	# a[4]*a[3]
+	adox	$zero,%r13		# of=0
+	adcx	$zero,%r13		# cf=0
+
+	mulx	5*8($aptr),%r9,%rbx	# a[5]*a[3]
+	adcx	%r10,%r8
+	adox	%rax,%r9
+	mulx	6*8($aptr),%r10,%rax	# ...
+	adcx	%r11,%r9
+	adox	%r12,%r10
+	mulx	7*8($aptr),%r11,%r12
+	 mov	4*8($aptr),%rdx		# a[4]
+	 mov	5*8($aptr),%r14		# a[5]
+	adcx	%rbx,%r10
+	adox	%rax,%r11
+	 mov	6*8($aptr),%r15		# a[6]
+	adcx	%r13,%r11
+	adox	$zero,%r12		# of=0
+	adcx	$zero,%r12		# cf=0
+
+	mov	%r8,7*8($tptr)		# t[7]
+	mov	%r9,8*8($tptr)		# t[8]
+
+	mulx	%r14,%r9,%rax		# a[5]*a[4]
+	 mov	7*8($aptr),%r8		# a[7]
+	adcx	%r10,%r9
+	mulx	%r15,%r10,%rbx		# a[6]*a[4]
+	adox	%rax,%r10
+	adcx	%r11,%r10
+	mulx	%r8,%r11,%rax		# a[7]*a[4]
+	 mov	%r14,%rdx		# a[5]
+	adox	%rbx,%r11
+	adcx	%r12,%r11
+	#adox	$zero,%rax		# of=0
+	adcx	$zero,%rax		# cf=0
+
+	mulx	%r15,%r14,%rbx		# a[6]*a[5]
+	mulx	%r8,%r12,%r13		# a[7]*a[5]
+	 mov	%r15,%rdx		# a[6]
+	 lea	8*8($aptr),$aptr
+	adcx	%r14,%r11
+	adox	%rbx,%r12
+	adcx	%rax,%r12
+	adox	$zero,%r13
+
+	.byte	0x67,0x67
+	mulx	%r8,%r8,%r14		# a[7]*a[6]
+	adcx	%r8,%r13
+	adcx	$zero,%r14
+
+	cmp	8+8(%rsp),$aptr
+	je	.Lsqrx8x_outer_break
+
+	neg	$carry			# mov $carry,%cf
+	mov	\$-8,%rcx
+	mov	$zero,%r15
+	mov	8*8($tptr),%r8
+	adcx	9*8($tptr),%r9		# +=t[9]
+	adcx	10*8($tptr),%r10	# ...
+	adcx	11*8($tptr),%r11
+	adc	12*8($tptr),%r12
+	adc	13*8($tptr),%r13
+	adc	14*8($tptr),%r14
+	adc	15*8($tptr),%r15
+	lea	($aptr),$aaptr
+	lea	2*64($tptr),$tptr
+	sbb	%rax,%rax		# mov %cf,$carry
+
+	mov	-64($aptr),%rdx		# a[0]
+	mov	%rax,16+8(%rsp)		# offload $carry
+	mov	$tptr,24+8(%rsp)
+
+	#lea	8*8($tptr),$tptr	# see 2*8*8($tptr) above
+	xor	%eax,%eax		# cf=0, of=0
+	jmp	.Lsqrx8x_loop
+
+.align	32
+.Lsqrx8x_loop:
+	mov	%r8,%rbx
+	mulx	0*8($aaptr),%rax,%r8	# a[8]*a[i]
+	adcx	%rax,%rbx		# +=t[8]
+	adox	%r9,%r8
+
+	mulx	1*8($aaptr),%rax,%r9	# ...
+	adcx	%rax,%r8
+	adox	%r10,%r9
+
+	mulx	2*8($aaptr),%rax,%r10
+	adcx	%rax,%r9
+	adox	%r11,%r10
+
+	mulx	3*8($aaptr),%rax,%r11
+	adcx	%rax,%r10
+	adox	%r12,%r11
+
+	.byte	0xc4,0x62,0xfb,0xf6,0xa5,0x20,0x00,0x00,0x00	# mulx	4*8($aaptr),%rax,%r12
+	adcx	%rax,%r11
+	adox	%r13,%r12
+
+	mulx	5*8($aaptr),%rax,%r13
+	adcx	%rax,%r12
+	adox	%r14,%r13
+
+	mulx	6*8($aaptr),%rax,%r14
+	 mov	%rbx,($tptr,%rcx,8)	# store t[8+i]
+	 mov	\$0,%ebx
+	adcx	%rax,%r13
+	adox	%r15,%r14
+
+	.byte	0xc4,0x62,0xfb,0xf6,0xbd,0x38,0x00,0x00,0x00	# mulx	7*8($aaptr),%rax,%r15
+	 mov	8($aptr,%rcx,8),%rdx	# a[i]
+	adcx	%rax,%r14
+	adox	%rbx,%r15		# %rbx is 0, of=0
+	adcx	%rbx,%r15		# cf=0
+
+	.byte	0x67
+	inc	%rcx			# of=0
+	jnz	.Lsqrx8x_loop
+
+	lea	8*8($aaptr),$aaptr
+	mov	\$-8,%rcx
+	cmp	8+8(%rsp),$aaptr	# done?
+	je	.Lsqrx8x_break
+
+	sub	16+8(%rsp),%rbx		# mov 16(%rsp),%cf
+	.byte	0x66
+	mov	-64($aptr),%rdx
+	adcx	0*8($tptr),%r8
+	adcx	1*8($tptr),%r9
+	adc	2*8($tptr),%r10
+	adc	3*8($tptr),%r11
+	adc	4*8($tptr),%r12
+	adc	5*8($tptr),%r13
+	adc	6*8($tptr),%r14
+	adc	7*8($tptr),%r15
+	lea	8*8($tptr),$tptr
+	.byte	0x67
+	sbb	%rax,%rax		# mov %cf,%rax
+	xor	%ebx,%ebx		# cf=0, of=0
+	mov	%rax,16+8(%rsp)		# offload carry
+	jmp	.Lsqrx8x_loop
+
+.align	32
+.Lsqrx8x_break:
+	sub	16+8(%rsp),%r8		# consume last carry
+	mov	24+8(%rsp),$carry	# initial $tptr, borrow $carry
+	mov	0*8($aptr),%rdx		# a[8], modulo-scheduled
+	xor	%ebp,%ebp		# xor	$zero,$zero
+	mov	%r8,0*8($tptr)
+	cmp	$carry,$tptr		# cf=0, of=0
+	je	.Lsqrx8x_outer_loop
+
+	mov	%r9,1*8($tptr)
+	 mov	1*8($carry),%r9
+	mov	%r10,2*8($tptr)
+	 mov	2*8($carry),%r10
+	mov	%r11,3*8($tptr)
+	 mov	3*8($carry),%r11
+	mov	%r12,4*8($tptr)
+	 mov	4*8($carry),%r12
+	mov	%r13,5*8($tptr)
+	 mov	5*8($carry),%r13
+	mov	%r14,6*8($tptr)
+	 mov	6*8($carry),%r14
+	mov	%r15,7*8($tptr)
+	 mov	7*8($carry),%r15
+	mov	$carry,$tptr
+	jmp	.Lsqrx8x_outer_loop
+
+.align	32
+.Lsqrx8x_outer_break:
+	mov	%r9,9*8($tptr)		# t[9]
+	 movq	%xmm3,%rcx		# -$num
+	mov	%r10,10*8($tptr)	# ...
+	mov	%r11,11*8($tptr)
+	mov	%r12,12*8($tptr)
+	mov	%r13,13*8($tptr)
+	mov	%r14,14*8($tptr)
+___
+}{
+my $i="%rcx";
+$code.=<<___;
+	lea	48+8(%rsp),$tptr
+	mov	($aptr,$i),%rdx		# a[0]
+
+	mov	8($tptr),$A0[1]		# t[1]
+	xor	$A0[0],$A0[0]		# t[0], of=0, cf=0
+	mov	0+8(%rsp),$num		# restore $num
+	adox	$A0[1],$A0[1]
+	 mov	16($tptr),$A1[0]	# t[2]	# prefetch
+	 mov	24($tptr),$A1[1]	# t[3]	# prefetch
+	#jmp	.Lsqrx4x_shift_n_add	# happens to be aligned
+
+.align	32
+.Lsqrx4x_shift_n_add:
+	mulx	%rdx,%rax,%rbx
+	 adox	$A1[0],$A1[0]
+	adcx	$A0[0],%rax
+	 .byte	0x48,0x8b,0x94,0x0e,0x08,0x00,0x00,0x00	# mov	8($aptr,$i),%rdx	# a[i+1]	# prefetch
+	 .byte	0x4c,0x8b,0x97,0x20,0x00,0x00,0x00	# mov	32($tptr),$A0[0]	# t[2*i+4]	# prefetch
+	 adox	$A1[1],$A1[1]
+	adcx	$A0[1],%rbx
+	 mov	40($tptr),$A0[1]		# t[2*i+4+1]	# prefetch
+	mov	%rax,0($tptr)
+	mov	%rbx,8($tptr)
+
+	mulx	%rdx,%rax,%rbx
+	 adox	$A0[0],$A0[0]
+	adcx	$A1[0],%rax
+	 mov	16($aptr,$i),%rdx	# a[i+2]	# prefetch
+	 mov	48($tptr),$A1[0]	# t[2*i+6]	# prefetch
+	 adox	$A0[1],$A0[1]
+	adcx	$A1[1],%rbx
+	 mov	56($tptr),$A1[1]	# t[2*i+6+1]	# prefetch
+	mov	%rax,16($tptr)
+	mov	%rbx,24($tptr)
+
+	mulx	%rdx,%rax,%rbx
+	 adox	$A1[0],$A1[0]
+	adcx	$A0[0],%rax
+	 mov	24($aptr,$i),%rdx	# a[i+3]	# prefetch
+	 lea	32($i),$i
+	 mov	64($tptr),$A0[0]	# t[2*i+8]	# prefetch
+	 adox	$A1[1],$A1[1]
+	adcx	$A0[1],%rbx
+	 mov	72($tptr),$A0[1]	# t[2*i+8+1]	# prefetch
+	mov	%rax,32($tptr)
+	mov	%rbx,40($tptr)
+
+	mulx	%rdx,%rax,%rbx
+	 adox	$A0[0],$A0[0]
+	adcx	$A1[0],%rax
+	jrcxz	.Lsqrx4x_shift_n_add_break
+	 .byte	0x48,0x8b,0x94,0x0e,0x00,0x00,0x00,0x00	# mov	0($aptr,$i),%rdx	# a[i+4]	# prefetch
+	 adox	$A0[1],$A0[1]
+	adcx	$A1[1],%rbx
+	 mov	80($tptr),$A1[0]	# t[2*i+10]	# prefetch
+	 mov	88($tptr),$A1[1]	# t[2*i+10+1]	# prefetch
+	mov	%rax,48($tptr)
+	mov	%rbx,56($tptr)
+	lea	64($tptr),$tptr
+	nop
+	jmp	.Lsqrx4x_shift_n_add
+
+.align	32
+.Lsqrx4x_shift_n_add_break:
+	adcx	$A1[1],%rbx
+	mov	%rax,48($tptr)
+	mov	%rbx,56($tptr)
+	lea	64($tptr),$tptr		# end of t[] buffer
+___
+}
+######################################################################
+# Montgomery reduction part, "word-by-word" algorithm.
+#
+# This new path is inspired by multiple submissions from Intel, by
+# Shay Gueron, Vlad Krasnov, Erdinc Ozturk, James Guilford,
+# Vinodh Gopal...
+{
+my ($nptr,$carry,$m0)=("%rbp","%rsi","%rdx");
+
+$code.=<<___;
+	movq	%xmm2,$nptr
+__bn_sqrx8x_reduction:
+	xor	%eax,%eax		# initial top-most carry bit
+	mov	32+8(%rsp),%rbx		# n0
+	mov	48+8(%rsp),%rdx		# "%r8", 8*0($tptr)
+	lea	-8*8($nptr,$num),%rcx	# end of n[]
+	#lea	48+8(%rsp,$num,2),$tptr	# end of t[] buffer
+	mov	%rcx, 0+8(%rsp)		# save end of n[]
+	mov	$tptr,8+8(%rsp)		# save end of t[]
+
+	lea	48+8(%rsp),$tptr		# initial t[] window
+	jmp	.Lsqrx8x_reduction_loop
+
+.align	32
+.Lsqrx8x_reduction_loop:
+	mov	8*1($tptr),%r9
+	mov	8*2($tptr),%r10
+	mov	8*3($tptr),%r11
+	mov	8*4($tptr),%r12
+	mov	%rdx,%r8
+	imulq	%rbx,%rdx		# n0*a[i]
+	mov	8*5($tptr),%r13
+	mov	8*6($tptr),%r14
+	mov	8*7($tptr),%r15
+	mov	%rax,24+8(%rsp)		# store top-most carry bit
+
+	lea	8*8($tptr),$tptr
+	xor	$carry,$carry		# cf=0,of=0
+	mov	\$-8,%rcx
+	jmp	.Lsqrx8x_reduce
+
+.align	32
+.Lsqrx8x_reduce:
+	mov	%r8, %rbx
+	mulx	8*0($nptr),%rax,%r8	# n[0]
+	adcx	%rbx,%rax		# discarded
+	adox	%r9,%r8
+
+	mulx	8*1($nptr),%rbx,%r9	# n[1]
+	adcx	%rbx,%r8
+	adox	%r10,%r9
+
+	mulx	8*2($nptr),%rbx,%r10
+	adcx	%rbx,%r9
+	adox	%r11,%r10
+
+	mulx	8*3($nptr),%rbx,%r11
+	adcx	%rbx,%r10
+	adox	%r12,%r11
+
+	.byte	0xc4,0x62,0xe3,0xf6,0xa5,0x20,0x00,0x00,0x00	# mulx	8*4($nptr),%rbx,%r12
+	 mov	%rdx,%rax
+	 mov	%r8,%rdx
+	adcx	%rbx,%r11
+	adox	%r13,%r12
+
+	 mulx	32+8(%rsp),%rbx,%rdx	# %rdx discarded
+	 mov	%rax,%rdx
+	 mov	%rax,64+48+8(%rsp,%rcx,8)	# put aside n0*a[i]
+
+	mulx	8*5($nptr),%rax,%r13
+	adcx	%rax,%r12
+	adox	%r14,%r13
+
+	mulx	8*6($nptr),%rax,%r14
+	adcx	%rax,%r13
+	adox	%r15,%r14
+
+	mulx	8*7($nptr),%rax,%r15
+	 mov	%rbx,%rdx
+	adcx	%rax,%r14
+	adox	$carry,%r15		# $carry is 0
+	adcx	$carry,%r15		# cf=0
+
+	.byte	0x67,0x67,0x67
+	inc	%rcx			# of=0
+	jnz	.Lsqrx8x_reduce
+
+	mov	$carry,%rax		# xor	%rax,%rax
+	cmp	0+8(%rsp),$nptr		# end of n[]?
+	jae	.Lsqrx8x_no_tail
+
+	mov	48+8(%rsp),%rdx		# pull n0*a[0]
+	add	8*0($tptr),%r8
+	lea	8*8($nptr),$nptr
+	mov	\$-8,%rcx
+	adcx	8*1($tptr),%r9
+	adcx	8*2($tptr),%r10
+	adc	8*3($tptr),%r11
+	adc	8*4($tptr),%r12
+	adc	8*5($tptr),%r13
+	adc	8*6($tptr),%r14
+	adc	8*7($tptr),%r15
+	lea	8*8($tptr),$tptr
+	sbb	%rax,%rax		# top carry
+
+	xor	$carry,$carry		# of=0, cf=0
+	mov	%rax,16+8(%rsp)
+	jmp	.Lsqrx8x_tail
+
+.align	32
+.Lsqrx8x_tail:
+	mov	%r8,%rbx
+	mulx	8*0($nptr),%rax,%r8
+	adcx	%rax,%rbx
+	adox	%r9,%r8
+
+	mulx	8*1($nptr),%rax,%r9
+	adcx	%rax,%r8
+	adox	%r10,%r9
+
+	mulx	8*2($nptr),%rax,%r10
+	adcx	%rax,%r9
+	adox	%r11,%r10
+
+	mulx	8*3($nptr),%rax,%r11
+	adcx	%rax,%r10
+	adox	%r12,%r11
+
+	.byte	0xc4,0x62,0xfb,0xf6,0xa5,0x20,0x00,0x00,0x00	# mulx	8*4($nptr),%rax,%r12
+	adcx	%rax,%r11
+	adox	%r13,%r12
+
+	mulx	8*5($nptr),%rax,%r13
+	adcx	%rax,%r12
+	adox	%r14,%r13
+
+	mulx	8*6($nptr),%rax,%r14
+	adcx	%rax,%r13
+	adox	%r15,%r14
+
+	mulx	8*7($nptr),%rax,%r15
+	 mov	72+48+8(%rsp,%rcx,8),%rdx	# pull n0*a[i]
+	adcx	%rax,%r14
+	adox	$carry,%r15
+	 mov	%rbx,($tptr,%rcx,8)	# save result
+	 mov	%r8,%rbx
+	adcx	$carry,%r15		# cf=0
+
+	inc	%rcx			# of=0
+	jnz	.Lsqrx8x_tail
+
+	cmp	0+8(%rsp),$nptr		# end of n[]?
+	jae	.Lsqrx8x_tail_done	# break out of loop
+
+	sub	16+8(%rsp),$carry	# mov 16(%rsp),%cf
+	 mov	48+8(%rsp),%rdx		# pull n0*a[0]
+	 lea	8*8($nptr),$nptr
+	adc	8*0($tptr),%r8
+	adc	8*1($tptr),%r9
+	adc	8*2($tptr),%r10
+	adc	8*3($tptr),%r11
+	adc	8*4($tptr),%r12
+	adc	8*5($tptr),%r13
+	adc	8*6($tptr),%r14
+	adc	8*7($tptr),%r15
+	lea	8*8($tptr),$tptr
+	sbb	%rax,%rax
+	sub	\$8,%rcx		# mov	\$-8,%rcx
+
+	xor	$carry,$carry		# of=0, cf=0
+	mov	%rax,16+8(%rsp)
+	jmp	.Lsqrx8x_tail
+
+.align	32
+.Lsqrx8x_tail_done:
+	xor	%rax,%rax
+	add	24+8(%rsp),%r8		# can this overflow?
+	adc	\$0,%r9
+	adc	\$0,%r10
+	adc	\$0,%r11
+	adc	\$0,%r12
+	adc	\$0,%r13
+	adc	\$0,%r14
+	adc	\$0,%r15
+	adc	\$0,%rax
+
+	sub	16+8(%rsp),$carry	# mov 16(%rsp),%cf
+.Lsqrx8x_no_tail:			# %cf is 0 if jumped here
+	adc	8*0($tptr),%r8
+	 movq	%xmm3,%rcx
+	adc	8*1($tptr),%r9
+	 mov	8*7($nptr),$carry
+	 movq	%xmm2,$nptr		# restore $nptr
+	adc	8*2($tptr),%r10
+	adc	8*3($tptr),%r11
+	adc	8*4($tptr),%r12
+	adc	8*5($tptr),%r13
+	adc	8*6($tptr),%r14
+	adc	8*7($tptr),%r15
+	adc	\$0,%rax		# top-most carry
+
+	mov	32+8(%rsp),%rbx		# n0
+	mov	8*8($tptr,%rcx),%rdx	# modulo-scheduled "%r8"
+
+	mov	%r8,8*0($tptr)		# store top 512 bits
+	 lea	8*8($tptr),%r8		# borrow %r8
+	mov	%r9,8*1($tptr)
+	mov	%r10,8*2($tptr)
+	mov	%r11,8*3($tptr)
+	mov	%r12,8*4($tptr)
+	mov	%r13,8*5($tptr)
+	mov	%r14,8*6($tptr)
+	mov	%r15,8*7($tptr)
+
+	lea	8*8($tptr,%rcx),$tptr	# start of current t[] window
+	cmp	8+8(%rsp),%r8		# end of t[]?
+	jb	.Lsqrx8x_reduction_loop
+	ret
+.size	bn_sqrx8x_internal,.-bn_sqrx8x_internal
+___
+}
+##############################################################
+# Post-condition, 4x unrolled
+#
+{
+my ($rptr,$nptr)=("%rdx","%rbp");
+$code.=<<___;
+.align	32
+__bn_postx4x_internal:
+	mov	8*0($nptr),%r12
+	mov	%rcx,%r10		# -$num
+	mov	%rcx,%r9		# -$num
+	neg	%rax
+	sar	\$3+2,%rcx
+	#lea	48+8(%rsp,%r9),$tptr
+	movq	%xmm1,$rptr		# restore $rptr
+	movq	%xmm1,$aptr		# prepare for back-to-back call
+	dec	%r12			# so that after 'not' we get -n[0]
+	mov	8*1($nptr),%r13
+	xor	%r8,%r8
+	mov	8*2($nptr),%r14
+	mov	8*3($nptr),%r15
+	jmp	.Lsqrx4x_sub_entry
+
+.align	16
+.Lsqrx4x_sub:
+	mov	8*0($nptr),%r12
+	mov	8*1($nptr),%r13
+	mov	8*2($nptr),%r14
+	mov	8*3($nptr),%r15
+.Lsqrx4x_sub_entry:
+	andn	%rax,%r12,%r12
+	lea	8*4($nptr),$nptr
+	andn	%rax,%r13,%r13
+	andn	%rax,%r14,%r14
+	andn	%rax,%r15,%r15
+
+	neg	%r8			# mov %r8,%cf
+	adc	8*0($tptr),%r12
+	adc	8*1($tptr),%r13
+	adc	8*2($tptr),%r14
+	adc	8*3($tptr),%r15
+	mov	%r12,8*0($rptr)
+	lea	8*4($tptr),$tptr
+	mov	%r13,8*1($rptr)
+	sbb	%r8,%r8			# mov %cf,%r8
+	mov	%r14,8*2($rptr)
+	mov	%r15,8*3($rptr)
+	lea	8*4($rptr),$rptr
+
+	inc	%rcx
+	jnz	.Lsqrx4x_sub
+
+	neg	%r9			# restore $num
+
+	ret
+.size	__bn_postx4x_internal,.-__bn_postx4x_internal
+___
+}
+}}}
+{
+my ($inp,$num,$tbl,$idx)=$win64?("%rcx","%edx","%r8", "%r9d") : # Win64 order
+				("%rdi","%esi","%rdx","%ecx");  # Unix order
+my $out=$inp;
+my $STRIDE=2**5*8;
+my $N=$STRIDE/4;
+
+$code.=<<___;
+.globl	bn_get_bits5
+.type	bn_get_bits5,\@abi-omnipotent
+.align	16
+bn_get_bits5:
+	lea	0($inp),%r10
+	lea	1($inp),%r11
+	mov	$num,%ecx
+	shr	\$4,$num
+	and	\$15,%ecx
+	lea	-8(%ecx),%eax
+	cmp	\$11,%ecx
+	cmova	%r11,%r10
+	cmova	%eax,%ecx
+	movzw	(%r10,$num,2),%eax
+	shrl	%cl,%eax
+	and	\$31,%eax
+	ret
+.size	bn_get_bits5,.-bn_get_bits5
+
+.globl	bn_scatter5
+.type	bn_scatter5,\@abi-omnipotent
+.align	16
+bn_scatter5:
+	cmp	\$0, $num
+	jz	.Lscatter_epilogue
+	lea	($tbl,$idx,8),$tbl
+.Lscatter:
+	mov	($inp),%rax
+	lea	8($inp),$inp
+	mov	%rax,($tbl)
+	lea	32*8($tbl),$tbl
+	sub	\$1,$num
+	jnz	.Lscatter
+.Lscatter_epilogue:
+	ret
+.size	bn_scatter5,.-bn_scatter5
+
+.globl	bn_gather5
+.type	bn_gather5,\@abi-omnipotent
+.align	32
+bn_gather5:
+.LSEH_begin_bn_gather5:			# Win64 thing, but harmless in other cases
+	# I can't trust assembler to use specific encoding:-(
+	.byte	0x4c,0x8d,0x14,0x24			#lea    (%rsp),%r10
+	.byte	0x48,0x81,0xec,0x08,0x01,0x00,0x00	#sub	$0x108,%rsp
+	lea	.Linc(%rip),%rax
+	and	\$-16,%rsp		# shouldn't be formally required
+
+	movd	$idx,%xmm5
+	movdqa	0(%rax),%xmm0		# 00000001000000010000000000000000
+	movdqa	16(%rax),%xmm1		# 00000002000000020000000200000002
+	lea	128($tbl),%r11		# size optimization
+	lea	128(%rsp),%rax		# size optimization
+
+	pshufd	\$0,%xmm5,%xmm5		# broadcast $idx
+	movdqa	%xmm1,%xmm4
+	movdqa	%xmm1,%xmm2
+___
+########################################################################
+# calculate mask by comparing 0..31 to $idx and save result to stack
+#
+for($i=0;$i<$STRIDE/16;$i+=4) {
+$code.=<<___;
+	paddd	%xmm0,%xmm1
+	pcmpeqd	%xmm5,%xmm0		# compare to 1,0
+___
+$code.=<<___	if ($i);
+	movdqa	%xmm3,`16*($i-1)-128`(%rax)
+___
+$code.=<<___;
+	movdqa	%xmm4,%xmm3
+
+	paddd	%xmm1,%xmm2
+	pcmpeqd	%xmm5,%xmm1		# compare to 3,2
+	movdqa	%xmm0,`16*($i+0)-128`(%rax)
+	movdqa	%xmm4,%xmm0
+
+	paddd	%xmm2,%xmm3
+	pcmpeqd	%xmm5,%xmm2		# compare to 5,4
+	movdqa	%xmm1,`16*($i+1)-128`(%rax)
+	movdqa	%xmm4,%xmm1
+
+	paddd	%xmm3,%xmm0
+	pcmpeqd	%xmm5,%xmm3		# compare to 7,6
+	movdqa	%xmm2,`16*($i+2)-128`(%rax)
+	movdqa	%xmm4,%xmm2
+___
+}
+$code.=<<___;
+	movdqa	%xmm3,`16*($i-1)-128`(%rax)
+	jmp	.Lgather
+
+.align	32
+.Lgather:
+	pxor	%xmm4,%xmm4
+	pxor	%xmm5,%xmm5
+___
+for($i=0;$i<$STRIDE/16;$i+=4) {
+$code.=<<___;
+	movdqa	`16*($i+0)-128`(%r11),%xmm0
+	movdqa	`16*($i+1)-128`(%r11),%xmm1
+	movdqa	`16*($i+2)-128`(%r11),%xmm2
+	pand	`16*($i+0)-128`(%rax),%xmm0
+	movdqa	`16*($i+3)-128`(%r11),%xmm3
+	pand	`16*($i+1)-128`(%rax),%xmm1
+	por	%xmm0,%xmm4
+	pand	`16*($i+2)-128`(%rax),%xmm2
+	por	%xmm1,%xmm5
+	pand	`16*($i+3)-128`(%rax),%xmm3
+	por	%xmm2,%xmm4
+	por	%xmm3,%xmm5
+___
+}
+$code.=<<___;
+	por	%xmm5,%xmm4
+	lea	$STRIDE(%r11),%r11
+	pshufd	\$0x4e,%xmm4,%xmm0
+	por	%xmm4,%xmm0
+	movq	%xmm0,($out)		# m0=bp[0]
+	lea	8($out),$out
+	sub	\$1,$num
+	jnz	.Lgather
+
+	lea	(%r10),%rsp
+	ret
+.LSEH_end_bn_gather5:
+.size	bn_gather5,.-bn_gather5
+___
+}
+$code.=<<___;
+.align	64
+.Linc:
+	.long	0,0, 1,1
+	.long	2,2, 2,2
+.asciz	"Montgomery Multiplication with scatter/gather for x86_64, CRYPTOGAMS by "
+___
+
+# EXCEPTION_DISPOSITION handler (EXCEPTION_RECORD *rec,ULONG64 frame,
+#		CONTEXT *context,DISPATCHER_CONTEXT *disp)
+if ($win64) {
+$rec="%rcx";
+$frame="%rdx";
+$context="%r8";
+$disp="%r9";
+
+$code.=<<___;
+.extern	__imp_RtlVirtualUnwind
+.type	mul_handler,\@abi-omnipotent
+.align	16
+mul_handler:
+	push	%rsi
+	push	%rdi
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+	pushfq
+	sub	\$64,%rsp
+
+	mov	120($context),%rax	# pull context->Rax
+	mov	248($context),%rbx	# pull context->Rip
+
+	mov	8($disp),%rsi		# disp->ImageBase
+	mov	56($disp),%r11		# disp->HandlerData
+
+	mov	0(%r11),%r10d		# HandlerData[0]
+	lea	(%rsi,%r10),%r10	# end of prologue label
+	cmp	%r10,%rbx		# context->RipRip>=epilogue label
+	jb	.Lcommon_pop_regs
+
+	mov	152($context),%rax	# pull context->Rsp
+
+	mov	8(%r11),%r10d		# HandlerData[2]
+	lea	(%rsi,%r10),%r10	# epilogue label
+	cmp	%r10,%rbx		# context->Rip>=epilogue label
+	jae	.Lcommon_seh_tail
+
+	lea	.Lmul_epilogue(%rip),%r10
+	cmp	%r10,%rbx
+	ja	.Lbody_40
+
+	mov	192($context),%r10	# pull $num
+	mov	8(%rax,%r10,8),%rax	# pull saved stack pointer
+
+	jmp	.Lcommon_pop_regs
+
+.Lbody_40:
+	mov	40(%rax),%rax		# pull saved stack pointer
+.Lcommon_pop_regs:
+	mov	-8(%rax),%rbx
+	mov	-16(%rax),%rbp
+	mov	-24(%rax),%r12
+	mov	-32(%rax),%r13
+	mov	-40(%rax),%r14
+	mov	-48(%rax),%r15
+	mov	%rbx,144($context)	# restore context->Rbx
+	mov	%rbp,160($context)	# restore context->Rbp
+	mov	%r12,216($context)	# restore context->R12
+	mov	%r13,224($context)	# restore context->R13
+	mov	%r14,232($context)	# restore context->R14
+	mov	%r15,240($context)	# restore context->R15
+
+.Lcommon_seh_tail:
+	mov	8(%rax),%rdi
+	mov	16(%rax),%rsi
+	mov	%rax,152($context)	# restore context->Rsp
+	mov	%rsi,168($context)	# restore context->Rsi
+	mov	%rdi,176($context)	# restore context->Rdi
+
+	mov	40($disp),%rdi		# disp->ContextRecord
+	mov	$context,%rsi		# context
+	mov	\$154,%ecx		# sizeof(CONTEXT)
+	.long	0xa548f3fc		# cld; rep movsq
+
+	mov	$disp,%rsi
+	xor	%rcx,%rcx		# arg1, UNW_FLAG_NHANDLER
+	mov	8(%rsi),%rdx		# arg2, disp->ImageBase
+	mov	0(%rsi),%r8		# arg3, disp->ControlPc
+	mov	16(%rsi),%r9		# arg4, disp->FunctionEntry
+	mov	40(%rsi),%r10		# disp->ContextRecord
+	lea	56(%rsi),%r11		# &disp->HandlerData
+	lea	24(%rsi),%r12		# &disp->EstablisherFrame
+	mov	%r10,32(%rsp)		# arg5
+	mov	%r11,40(%rsp)		# arg6
+	mov	%r12,48(%rsp)		# arg7
+	mov	%rcx,56(%rsp)		# arg8, (NULL)
+	call	*__imp_RtlVirtualUnwind(%rip)
+
+	mov	\$1,%eax		# ExceptionContinueSearch
+	add	\$64,%rsp
+	popfq
+	pop	%r15
+	pop	%r14
+	pop	%r13
+	pop	%r12
+	pop	%rbp
+	pop	%rbx
+	pop	%rdi
+	pop	%rsi
+	ret
+.size	mul_handler,.-mul_handler
+
+.section	.pdata
+.align	4
+	.rva	.LSEH_begin_bn_mul_mont_gather5
+	.rva	.LSEH_end_bn_mul_mont_gather5
+	.rva	.LSEH_info_bn_mul_mont_gather5
+
+	.rva	.LSEH_begin_bn_mul4x_mont_gather5
+	.rva	.LSEH_end_bn_mul4x_mont_gather5
+	.rva	.LSEH_info_bn_mul4x_mont_gather5
+
+	.rva	.LSEH_begin_bn_power5
+	.rva	.LSEH_end_bn_power5
+	.rva	.LSEH_info_bn_power5
+
+	.rva	.LSEH_begin_bn_from_mont8x
+	.rva	.LSEH_end_bn_from_mont8x
+	.rva	.LSEH_info_bn_from_mont8x
+___
+$code.=<<___ if ($addx);
+	.rva	.LSEH_begin_bn_mulx4x_mont_gather5
+	.rva	.LSEH_end_bn_mulx4x_mont_gather5
+	.rva	.LSEH_info_bn_mulx4x_mont_gather5
+
+	.rva	.LSEH_begin_bn_powerx5
+	.rva	.LSEH_end_bn_powerx5
+	.rva	.LSEH_info_bn_powerx5
+___
+$code.=<<___;
+	.rva	.LSEH_begin_bn_gather5
+	.rva	.LSEH_end_bn_gather5
+	.rva	.LSEH_info_bn_gather5
+
+.section	.xdata
+.align	8
+.LSEH_info_bn_mul_mont_gather5:
+	.byte	9,0,0,0
+	.rva	mul_handler
+	.rva	.Lmul_body,.Lmul_body,.Lmul_epilogue		# HandlerData[]
+.align	8
+.LSEH_info_bn_mul4x_mont_gather5:
+	.byte	9,0,0,0
+	.rva	mul_handler
+	.rva	.Lmul4x_prologue,.Lmul4x_body,.Lmul4x_epilogue		# HandlerData[]
+.align	8
+.LSEH_info_bn_power5:
+	.byte	9,0,0,0
+	.rva	mul_handler
+	.rva	.Lpower5_prologue,.Lpower5_body,.Lpower5_epilogue	# HandlerData[]
+.align	8
+.LSEH_info_bn_from_mont8x:
+	.byte	9,0,0,0
+	.rva	mul_handler
+	.rva	.Lfrom_prologue,.Lfrom_body,.Lfrom_epilogue		# HandlerData[]
+___
+$code.=<<___ if ($addx);
+.align	8
+.LSEH_info_bn_mulx4x_mont_gather5:
+	.byte	9,0,0,0
+	.rva	mul_handler
+	.rva	.Lmulx4x_prologue,.Lmulx4x_body,.Lmulx4x_epilogue	# HandlerData[]
+.align	8
+.LSEH_info_bn_powerx5:
+	.byte	9,0,0,0
+	.rva	mul_handler
+	.rva	.Lpowerx5_prologue,.Lpowerx5_body,.Lpowerx5_epilogue	# HandlerData[]
+___
+$code.=<<___;
+.align	8
+.LSEH_info_bn_gather5:
+	.byte	0x01,0x0b,0x03,0x0a
+	.byte	0x0b,0x01,0x21,0x00	# sub	rsp,0x108
+	.byte	0x04,0xa3,0x00,0x00	# lea	r10,(rsp)
+.align	8
+___
+}
+
+$code =~ s/\`([^\`]*)\`/eval($1)/gem;
+
+print $code;
+close STDOUT;
diff --git a/crypto/bn/bn_add.c b/crypto/bn/bn_add.c
new file mode 100644
index 000000000..6479650af
--- /dev/null
+++ b/crypto/bn/bn_add.c
@@ -0,0 +1,205 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "internal/cryptlib.h"
+#include "bn_lcl.h"
+
+/* r can == a or b */
+int BN_add(BIGNUM *r, const BIGNUM *a, const BIGNUM *b)
+{
+    int a_neg = a->neg, ret;
+
+    bn_check_top(a);
+    bn_check_top(b);
+
+    /*-
+     *  a +  b      a+b
+     *  a + -b      a-b
+     * -a +  b      b-a
+     * -a + -b      -(a+b)
+     */
+    if (a_neg ^ b->neg) {
+        /* only one is negative */
+        if (a_neg) {
+            const BIGNUM *tmp;
+
+            tmp = a;
+            a = b;
+            b = tmp;
+        }
+
+        /* we are now a - b */
+
+        if (BN_ucmp(a, b) < 0) {
+            if (!BN_usub(r, b, a))
+                return 0;
+            r->neg = 1;
+        } else {
+            if (!BN_usub(r, a, b))
+                return 0;
+            r->neg = 0;
+        }
+        return 1;
+    }
+
+    ret = BN_uadd(r, a, b);
+    r->neg = a_neg;
+    bn_check_top(r);
+    return ret;
+}
+
+/* unsigned add of b to a */
+int BN_uadd(BIGNUM *r, const BIGNUM *a, const BIGNUM *b)
+{
+    int max, min, dif;
+    const BN_ULONG *ap, *bp;
+    BN_ULONG *rp, carry, t1, t2;
+
+    bn_check_top(a);
+    bn_check_top(b);
+
+    if (a->top < b->top) {
+        const BIGNUM *tmp;
+
+        tmp = a;
+        a = b;
+        b = tmp;
+    }
+    max = a->top;
+    min = b->top;
+    dif = max - min;
+
+    if (bn_wexpand(r, max + 1) == NULL)
+        return 0;
+
+    r->top = max;
+
+    ap = a->d;
+    bp = b->d;
+    rp = r->d;
+
+    carry = bn_add_words(rp, ap, bp, min);
+    rp += min;
+    ap += min;
+
+    while (dif) {
+        dif--;
+        t1 = *(ap++);
+        t2 = (t1 + carry) & BN_MASK2;
+        *(rp++) = t2;
+        carry &= (t2 == 0);
+    }
+    *rp = carry;
+    r->top += carry;
+
+    r->neg = 0;
+    bn_check_top(r);
+    return 1;
+}
+
+/* unsigned subtraction of b from a, a must be larger than b. */
+int BN_usub(BIGNUM *r, const BIGNUM *a, const BIGNUM *b)
+{
+    int max, min, dif;
+    BN_ULONG t1, t2, borrow, *rp;
+    const BN_ULONG *ap, *bp;
+
+    bn_check_top(a);
+    bn_check_top(b);
+
+    max = a->top;
+    min = b->top;
+    dif = max - min;
+
+    if (dif < 0) {              /* hmm... should not be happening */
+        BNerr(BN_F_BN_USUB, BN_R_ARG2_LT_ARG3);
+        return 0;
+    }
+
+    if (bn_wexpand(r, max) == NULL)
+        return 0;
+
+    ap = a->d;
+    bp = b->d;
+    rp = r->d;
+
+    borrow = bn_sub_words(rp, ap, bp, min);
+    ap += min;
+    rp += min;
+
+    while (dif) {
+        dif--;
+        t1 = *(ap++);
+        t2 = (t1 - borrow) & BN_MASK2;
+        *(rp++) = t2;
+        borrow &= (t1 == 0);
+    }
+
+    r->top = max;
+    r->neg = 0;
+    bn_correct_top(r);
+    return 1;
+}
+
+int BN_sub(BIGNUM *r, const BIGNUM *a, const BIGNUM *b)
+{
+    int max;
+    int add = 0, neg = 0;
+
+    bn_check_top(a);
+    bn_check_top(b);
+
+    /*-
+     *  a -  b      a-b
+     *  a - -b      a+b
+     * -a -  b      -(a+b)
+     * -a - -b      b-a
+     */
+    if (a->neg) {
+        if (b->neg) {
+            const BIGNUM *tmp;
+
+            tmp = a;
+            a = b;
+            b = tmp;
+        } else {
+            add = 1;
+            neg = 1;
+        }
+    } else {
+        if (b->neg) {
+            add = 1;
+            neg = 0;
+        }
+    }
+
+    if (add) {
+        if (!BN_uadd(r, a, b))
+            return 0;
+        r->neg = neg;
+        return 1;
+    }
+
+    /* We are actually doing a - b :-) */
+
+    max = (a->top > b->top) ? a->top : b->top;
+    if (bn_wexpand(r, max) == NULL)
+        return 0;
+    if (BN_ucmp(a, b) < 0) {
+        if (!BN_usub(r, b, a))
+            return 0;
+        r->neg = 1;
+    } else {
+        if (!BN_usub(r, a, b))
+            return 0;
+        r->neg = 0;
+    }
+    bn_check_top(r);
+    return 1;
+}
diff --git a/crypto/bn/bn_asm.c b/crypto/bn/bn_asm.c
new file mode 100644
index 000000000..39c6c2134
--- /dev/null
+++ b/crypto/bn/bn_asm.c
@@ -0,0 +1,1039 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include "internal/cryptlib.h"
+#include "bn_lcl.h"
+
+#if defined(BN_LLONG) || defined(BN_UMULT_HIGH)
+
+BN_ULONG bn_mul_add_words(BN_ULONG *rp, const BN_ULONG *ap, int num,
+                          BN_ULONG w)
+{
+    BN_ULONG c1 = 0;
+
+    assert(num >= 0);
+    if (num <= 0)
+        return (c1);
+
+# ifndef OPENSSL_SMALL_FOOTPRINT
+    while (num & ~3) {
+        mul_add(rp[0], ap[0], w, c1);
+        mul_add(rp[1], ap[1], w, c1);
+        mul_add(rp[2], ap[2], w, c1);
+        mul_add(rp[3], ap[3], w, c1);
+        ap += 4;
+        rp += 4;
+        num -= 4;
+    }
+# endif
+    while (num) {
+        mul_add(rp[0], ap[0], w, c1);
+        ap++;
+        rp++;
+        num--;
+    }
+
+    return (c1);
+}
+
+BN_ULONG bn_mul_words(BN_ULONG *rp, const BN_ULONG *ap, int num, BN_ULONG w)
+{
+    BN_ULONG c1 = 0;
+
+    assert(num >= 0);
+    if (num <= 0)
+        return (c1);
+
+# ifndef OPENSSL_SMALL_FOOTPRINT
+    while (num & ~3) {
+        mul(rp[0], ap[0], w, c1);
+        mul(rp[1], ap[1], w, c1);
+        mul(rp[2], ap[2], w, c1);
+        mul(rp[3], ap[3], w, c1);
+        ap += 4;
+        rp += 4;
+        num -= 4;
+    }
+# endif
+    while (num) {
+        mul(rp[0], ap[0], w, c1);
+        ap++;
+        rp++;
+        num--;
+    }
+    return (c1);
+}
+
+void bn_sqr_words(BN_ULONG *r, const BN_ULONG *a, int n)
+{
+    assert(n >= 0);
+    if (n <= 0)
+        return;
+
+# ifndef OPENSSL_SMALL_FOOTPRINT
+    while (n & ~3) {
+        sqr(r[0], r[1], a[0]);
+        sqr(r[2], r[3], a[1]);
+        sqr(r[4], r[5], a[2]);
+        sqr(r[6], r[7], a[3]);
+        a += 4;
+        r += 8;
+        n -= 4;
+    }
+# endif
+    while (n) {
+        sqr(r[0], r[1], a[0]);
+        a++;
+        r += 2;
+        n--;
+    }
+}
+
+#else                           /* !(defined(BN_LLONG) ||
+                                 * defined(BN_UMULT_HIGH)) */
+
+BN_ULONG bn_mul_add_words(BN_ULONG *rp, const BN_ULONG *ap, int num,
+                          BN_ULONG w)
+{
+    BN_ULONG c = 0;
+    BN_ULONG bl, bh;
+
+    assert(num >= 0);
+    if (num <= 0)
+        return ((BN_ULONG)0);
+
+    bl = LBITS(w);
+    bh = HBITS(w);
+
+# ifndef OPENSSL_SMALL_FOOTPRINT
+    while (num & ~3) {
+        mul_add(rp[0], ap[0], bl, bh, c);
+        mul_add(rp[1], ap[1], bl, bh, c);
+        mul_add(rp[2], ap[2], bl, bh, c);
+        mul_add(rp[3], ap[3], bl, bh, c);
+        ap += 4;
+        rp += 4;
+        num -= 4;
+    }
+# endif
+    while (num) {
+        mul_add(rp[0], ap[0], bl, bh, c);
+        ap++;
+        rp++;
+        num--;
+    }
+    return (c);
+}
+
+BN_ULONG bn_mul_words(BN_ULONG *rp, const BN_ULONG *ap, int num, BN_ULONG w)
+{
+    BN_ULONG carry = 0;
+    BN_ULONG bl, bh;
+
+    assert(num >= 0);
+    if (num <= 0)
+        return ((BN_ULONG)0);
+
+    bl = LBITS(w);
+    bh = HBITS(w);
+
+# ifndef OPENSSL_SMALL_FOOTPRINT
+    while (num & ~3) {
+        mul(rp[0], ap[0], bl, bh, carry);
+        mul(rp[1], ap[1], bl, bh, carry);
+        mul(rp[2], ap[2], bl, bh, carry);
+        mul(rp[3], ap[3], bl, bh, carry);
+        ap += 4;
+        rp += 4;
+        num -= 4;
+    }
+# endif
+    while (num) {
+        mul(rp[0], ap[0], bl, bh, carry);
+        ap++;
+        rp++;
+        num--;
+    }
+    return (carry);
+}
+
+void bn_sqr_words(BN_ULONG *r, const BN_ULONG *a, int n)
+{
+    assert(n >= 0);
+    if (n <= 0)
+        return;
+
+# ifndef OPENSSL_SMALL_FOOTPRINT
+    while (n & ~3) {
+        sqr64(r[0], r[1], a[0]);
+        sqr64(r[2], r[3], a[1]);
+        sqr64(r[4], r[5], a[2]);
+        sqr64(r[6], r[7], a[3]);
+        a += 4;
+        r += 8;
+        n -= 4;
+    }
+# endif
+    while (n) {
+        sqr64(r[0], r[1], a[0]);
+        a++;
+        r += 2;
+        n--;
+    }
+}
+
+#endif                          /* !(defined(BN_LLONG) ||
+                                 * defined(BN_UMULT_HIGH)) */
+
+#if defined(BN_LLONG) && defined(BN_DIV2W)
+
+BN_ULONG bn_div_words(BN_ULONG h, BN_ULONG l, BN_ULONG d)
+{
+    return ((BN_ULONG)(((((BN_ULLONG) h) << BN_BITS2) | l) / (BN_ULLONG) d));
+}
+
+#else
+
+/* Divide h,l by d and return the result. */
+/* I need to test this some more :-( */
+BN_ULONG bn_div_words(BN_ULONG h, BN_ULONG l, BN_ULONG d)
+{
+    BN_ULONG dh, dl, q, ret = 0, th, tl, t;
+    int i, count = 2;
+
+    if (d == 0)
+        return (BN_MASK2);
+
+    i = BN_num_bits_word(d);
+    assert((i == BN_BITS2) || (h <= (BN_ULONG)1 << i));
+
+    i = BN_BITS2 - i;
+    if (h >= d)
+        h -= d;
+
+    if (i) {
+        d <<= i;
+        h = (h << i) | (l >> (BN_BITS2 - i));
+        l <<= i;
+    }
+    dh = (d & BN_MASK2h) >> BN_BITS4;
+    dl = (d & BN_MASK2l);
+    for (;;) {
+        if ((h >> BN_BITS4) == dh)
+            q = BN_MASK2l;
+        else
+            q = h / dh;
+
+        th = q * dh;
+        tl = dl * q;
+        for (;;) {
+            t = h - th;
+            if ((t & BN_MASK2h) ||
+                ((tl) <= ((t << BN_BITS4) | ((l & BN_MASK2h) >> BN_BITS4))))
+                break;
+            q--;
+            th -= dh;
+            tl -= dl;
+        }
+        t = (tl >> BN_BITS4);
+        tl = (tl << BN_BITS4) & BN_MASK2h;
+        th += t;
+
+        if (l < tl)
+            th++;
+        l -= tl;
+        if (h < th) {
+            h += d;
+            q--;
+        }
+        h -= th;
+
+        if (--count == 0)
+            break;
+
+        ret = q << BN_BITS4;
+        h = ((h << BN_BITS4) | (l >> BN_BITS4)) & BN_MASK2;
+        l = (l & BN_MASK2l) << BN_BITS4;
+    }
+    ret |= q;
+    return (ret);
+}
+#endif                          /* !defined(BN_LLONG) && defined(BN_DIV2W) */
+
+#ifdef BN_LLONG
+BN_ULONG bn_add_words(BN_ULONG *r, const BN_ULONG *a, const BN_ULONG *b,
+                      int n)
+{
+    BN_ULLONG ll = 0;
+
+    assert(n >= 0);
+    if (n <= 0)
+        return ((BN_ULONG)0);
+
+# ifndef OPENSSL_SMALL_FOOTPRINT
+    while (n & ~3) {
+        ll += (BN_ULLONG) a[0] + b[0];
+        r[0] = (BN_ULONG)ll & BN_MASK2;
+        ll >>= BN_BITS2;
+        ll += (BN_ULLONG) a[1] + b[1];
+        r[1] = (BN_ULONG)ll & BN_MASK2;
+        ll >>= BN_BITS2;
+        ll += (BN_ULLONG) a[2] + b[2];
+        r[2] = (BN_ULONG)ll & BN_MASK2;
+        ll >>= BN_BITS2;
+        ll += (BN_ULLONG) a[3] + b[3];
+        r[3] = (BN_ULONG)ll & BN_MASK2;
+        ll >>= BN_BITS2;
+        a += 4;
+        b += 4;
+        r += 4;
+        n -= 4;
+    }
+# endif
+    while (n) {
+        ll += (BN_ULLONG) a[0] + b[0];
+        r[0] = (BN_ULONG)ll & BN_MASK2;
+        ll >>= BN_BITS2;
+        a++;
+        b++;
+        r++;
+        n--;
+    }
+    return ((BN_ULONG)ll);
+}
+#else                           /* !BN_LLONG */
+BN_ULONG bn_add_words(BN_ULONG *r, const BN_ULONG *a, const BN_ULONG *b,
+                      int n)
+{
+    BN_ULONG c, l, t;
+
+    assert(n >= 0);
+    if (n <= 0)
+        return ((BN_ULONG)0);
+
+    c = 0;
+# ifndef OPENSSL_SMALL_FOOTPRINT
+    while (n & ~3) {
+        t = a[0];
+        t = (t + c) & BN_MASK2;
+        c = (t < c);
+        l = (t + b[0]) & BN_MASK2;
+        c += (l < t);
+        r[0] = l;
+        t = a[1];
+        t = (t + c) & BN_MASK2;
+        c = (t < c);
+        l = (t + b[1]) & BN_MASK2;
+        c += (l < t);
+        r[1] = l;
+        t = a[2];
+        t = (t + c) & BN_MASK2;
+        c = (t < c);
+        l = (t + b[2]) & BN_MASK2;
+        c += (l < t);
+        r[2] = l;
+        t = a[3];
+        t = (t + c) & BN_MASK2;
+        c = (t < c);
+        l = (t + b[3]) & BN_MASK2;
+        c += (l < t);
+        r[3] = l;
+        a += 4;
+        b += 4;
+        r += 4;
+        n -= 4;
+    }
+# endif
+    while (n) {
+        t = a[0];
+        t = (t + c) & BN_MASK2;
+        c = (t < c);
+        l = (t + b[0]) & BN_MASK2;
+        c += (l < t);
+        r[0] = l;
+        a++;
+        b++;
+        r++;
+        n--;
+    }
+    return ((BN_ULONG)c);
+}
+#endif                          /* !BN_LLONG */
+
+BN_ULONG bn_sub_words(BN_ULONG *r, const BN_ULONG *a, const BN_ULONG *b,
+                      int n)
+{
+    BN_ULONG t1, t2;
+    int c = 0;
+
+    assert(n >= 0);
+    if (n <= 0)
+        return ((BN_ULONG)0);
+
+#ifndef OPENSSL_SMALL_FOOTPRINT
+    while (n & ~3) {
+        t1 = a[0];
+        t2 = b[0];
+        r[0] = (t1 - t2 - c) & BN_MASK2;
+        if (t1 != t2)
+            c = (t1 < t2);
+        t1 = a[1];
+        t2 = b[1];
+        r[1] = (t1 - t2 - c) & BN_MASK2;
+        if (t1 != t2)
+            c = (t1 < t2);
+        t1 = a[2];
+        t2 = b[2];
+        r[2] = (t1 - t2 - c) & BN_MASK2;
+        if (t1 != t2)
+            c = (t1 < t2);
+        t1 = a[3];
+        t2 = b[3];
+        r[3] = (t1 - t2 - c) & BN_MASK2;
+        if (t1 != t2)
+            c = (t1 < t2);
+        a += 4;
+        b += 4;
+        r += 4;
+        n -= 4;
+    }
+#endif
+    while (n) {
+        t1 = a[0];
+        t2 = b[0];
+        r[0] = (t1 - t2 - c) & BN_MASK2;
+        if (t1 != t2)
+            c = (t1 < t2);
+        a++;
+        b++;
+        r++;
+        n--;
+    }
+    return (c);
+}
+
+#if defined(BN_MUL_COMBA) && !defined(OPENSSL_SMALL_FOOTPRINT)
+
+# undef bn_mul_comba8
+# undef bn_mul_comba4
+# undef bn_sqr_comba8
+# undef bn_sqr_comba4
+
+/* mul_add_c(a,b,c0,c1,c2)  -- c+=a*b for three word number c=(c2,c1,c0) */
+/* mul_add_c2(a,b,c0,c1,c2) -- c+=2*a*b for three word number c=(c2,c1,c0) */
+/* sqr_add_c(a,i,c0,c1,c2)  -- c+=a[i]^2 for three word number c=(c2,c1,c0) */
+/*
+ * sqr_add_c2(a,i,c0,c1,c2) -- c+=2*a[i]*a[j] for three word number
+ * c=(c2,c1,c0)
+ */
+
+# ifdef BN_LLONG
+/*
+ * Keep in mind that additions to multiplication result can not
+ * overflow, because its high half cannot be all-ones.
+ */
+#  define mul_add_c(a,b,c0,c1,c2)       do {    \
+        BN_ULONG hi;                            \
+        BN_ULLONG t = (BN_ULLONG)(a)*(b);       \
+        t += c0;                /* no carry */  \
+        c0 = (BN_ULONG)Lw(t);                   \
+        hi = (BN_ULONG)Hw(t);                   \
+        c1 = (c1+hi)&BN_MASK2; if (c1
+/*
+ * This is essentially reference implementation, which may or may not
+ * result in performance improvement. E.g. on IA-32 this routine was
+ * observed to give 40% faster rsa1024 private key operations and 10%
+ * faster rsa4096 ones, while on AMD64 it improves rsa1024 sign only
+ * by 10% and *worsens* rsa4096 sign by 15%. Once again, it's a
+ * reference implementation, one to be used as starting point for
+ * platform-specific assembler. Mentioned numbers apply to compiler
+ * generated code compiled with and without -DOPENSSL_BN_ASM_MONT and
+ * can vary not only from platform to platform, but even for compiler
+ * versions. Assembler vs. assembler improvement coefficients can
+ * [and are known to] differ and are to be documented elsewhere.
+ */
+int bn_mul_mont(BN_ULONG *rp, const BN_ULONG *ap, const BN_ULONG *bp,
+                const BN_ULONG *np, const BN_ULONG *n0p, int num)
+{
+    BN_ULONG c0, c1, ml, *tp, n0;
+#   ifdef mul64
+    BN_ULONG mh;
+#   endif
+    volatile BN_ULONG *vp;
+    int i = 0, j;
+
+#   if 0                        /* template for platform-specific
+                                 * implementation */
+    if (ap == bp)
+        return bn_sqr_mont(rp, ap, np, n0p, num);
+#   endif
+    vp = tp = alloca((num + 2) * sizeof(BN_ULONG));
+
+    n0 = *n0p;
+
+    c0 = 0;
+    ml = bp[0];
+#   ifdef mul64
+    mh = HBITS(ml);
+    ml = LBITS(ml);
+    for (j = 0; j < num; ++j)
+        mul(tp[j], ap[j], ml, mh, c0);
+#   else
+    for (j = 0; j < num; ++j)
+        mul(tp[j], ap[j], ml, c0);
+#   endif
+
+    tp[num] = c0;
+    tp[num + 1] = 0;
+    goto enter;
+
+    for (i = 0; i < num; i++) {
+        c0 = 0;
+        ml = bp[i];
+#   ifdef mul64
+        mh = HBITS(ml);
+        ml = LBITS(ml);
+        for (j = 0; j < num; ++j)
+            mul_add(tp[j], ap[j], ml, mh, c0);
+#   else
+        for (j = 0; j < num; ++j)
+            mul_add(tp[j], ap[j], ml, c0);
+#   endif
+        c1 = (tp[num] + c0) & BN_MASK2;
+        tp[num] = c1;
+        tp[num + 1] = (c1 < c0 ? 1 : 0);
+ enter:
+        c1 = tp[0];
+        ml = (c1 * n0) & BN_MASK2;
+        c0 = 0;
+#   ifdef mul64
+        mh = HBITS(ml);
+        ml = LBITS(ml);
+        mul_add(c1, np[0], ml, mh, c0);
+#   else
+        mul_add(c1, ml, np[0], c0);
+#   endif
+        for (j = 1; j < num; j++) {
+            c1 = tp[j];
+#   ifdef mul64
+            mul_add(c1, np[j], ml, mh, c0);
+#   else
+            mul_add(c1, ml, np[j], c0);
+#   endif
+            tp[j - 1] = c1 & BN_MASK2;
+        }
+        c1 = (tp[num] + c0) & BN_MASK2;
+        tp[num - 1] = c1;
+        tp[num] = tp[num + 1] + (c1 < c0 ? 1 : 0);
+    }
+
+    if (tp[num] != 0 || tp[num - 1] >= np[num - 1]) {
+        c0 = bn_sub_words(rp, tp, np, num);
+        if (tp[num] != 0 || c0 == 0) {
+            for (i = 0; i < num + 2; i++)
+                vp[i] = 0;
+            return 1;
+        }
+    }
+    for (i = 0; i < num; i++)
+        rp[i] = tp[i], vp[i] = 0;
+    vp[num] = 0;
+    vp[num + 1] = 0;
+    return 1;
+}
+#  else
+/*
+ * Return value of 0 indicates that multiplication/convolution was not
+ * performed to signal the caller to fall down to alternative/original
+ * code-path.
+ */
+int bn_mul_mont(BN_ULONG *rp, const BN_ULONG *ap, const BN_ULONG *bp,
+                const BN_ULONG *np, const BN_ULONG *n0, int num)
+{
+    return 0;
+}
+#  endif                        /* OPENSSL_BN_ASM_MONT */
+# endif
+
+#else                           /* !BN_MUL_COMBA */
+
+/* hmm... is it faster just to do a multiply? */
+# undef bn_sqr_comba4
+# undef bn_sqr_comba8
+void bn_sqr_comba4(BN_ULONG *r, const BN_ULONG *a)
+{
+    BN_ULONG t[8];
+    bn_sqr_normal(r, a, 4, t);
+}
+
+void bn_sqr_comba8(BN_ULONG *r, const BN_ULONG *a)
+{
+    BN_ULONG t[16];
+    bn_sqr_normal(r, a, 8, t);
+}
+
+void bn_mul_comba4(BN_ULONG *r, BN_ULONG *a, BN_ULONG *b)
+{
+    r[4] = bn_mul_words(&(r[0]), a, 4, b[0]);
+    r[5] = bn_mul_add_words(&(r[1]), a, 4, b[1]);
+    r[6] = bn_mul_add_words(&(r[2]), a, 4, b[2]);
+    r[7] = bn_mul_add_words(&(r[3]), a, 4, b[3]);
+}
+
+void bn_mul_comba8(BN_ULONG *r, BN_ULONG *a, BN_ULONG *b)
+{
+    r[8] = bn_mul_words(&(r[0]), a, 8, b[0]);
+    r[9] = bn_mul_add_words(&(r[1]), a, 8, b[1]);
+    r[10] = bn_mul_add_words(&(r[2]), a, 8, b[2]);
+    r[11] = bn_mul_add_words(&(r[3]), a, 8, b[3]);
+    r[12] = bn_mul_add_words(&(r[4]), a, 8, b[4]);
+    r[13] = bn_mul_add_words(&(r[5]), a, 8, b[5]);
+    r[14] = bn_mul_add_words(&(r[6]), a, 8, b[6]);
+    r[15] = bn_mul_add_words(&(r[7]), a, 8, b[7]);
+}
+
+# ifdef OPENSSL_NO_ASM
+#  ifdef OPENSSL_BN_ASM_MONT
+#   include 
+int bn_mul_mont(BN_ULONG *rp, const BN_ULONG *ap, const BN_ULONG *bp,
+                const BN_ULONG *np, const BN_ULONG *n0p, int num)
+{
+    BN_ULONG c0, c1, *tp, n0 = *n0p;
+    volatile BN_ULONG *vp;
+    int i = 0, j;
+
+    vp = tp = alloca((num + 2) * sizeof(BN_ULONG));
+
+    for (i = 0; i <= num; i++)
+        tp[i] = 0;
+
+    for (i = 0; i < num; i++) {
+        c0 = bn_mul_add_words(tp, ap, num, bp[i]);
+        c1 = (tp[num] + c0) & BN_MASK2;
+        tp[num] = c1;
+        tp[num + 1] = (c1 < c0 ? 1 : 0);
+
+        c0 = bn_mul_add_words(tp, np, num, tp[0] * n0);
+        c1 = (tp[num] + c0) & BN_MASK2;
+        tp[num] = c1;
+        tp[num + 1] += (c1 < c0 ? 1 : 0);
+        for (j = 0; j <= num; j++)
+            tp[j] = tp[j + 1];
+    }
+
+    if (tp[num] != 0 || tp[num - 1] >= np[num - 1]) {
+        c0 = bn_sub_words(rp, tp, np, num);
+        if (tp[num] != 0 || c0 == 0) {
+            for (i = 0; i < num + 2; i++)
+                vp[i] = 0;
+            return 1;
+        }
+    }
+    for (i = 0; i < num; i++)
+        rp[i] = tp[i], vp[i] = 0;
+    vp[num] = 0;
+    vp[num + 1] = 0;
+    return 1;
+}
+#  else
+int bn_mul_mont(BN_ULONG *rp, const BN_ULONG *ap, const BN_ULONG *bp,
+                const BN_ULONG *np, const BN_ULONG *n0, int num)
+{
+    return 0;
+}
+#  endif                        /* OPENSSL_BN_ASM_MONT */
+# endif
+
+#endif                          /* !BN_MUL_COMBA */
diff --git a/crypto/bn/bn_blind.c b/crypto/bn/bn_blind.c
new file mode 100644
index 000000000..24d138309
--- /dev/null
+++ b/crypto/bn/bn_blind.c
@@ -0,0 +1,289 @@
+/*
+ * Copyright 1998-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include "bn_lcl.h"
+
+#define BN_BLINDING_COUNTER     32
+
+struct bn_blinding_st {
+    BIGNUM *A;
+    BIGNUM *Ai;
+    BIGNUM *e;
+    BIGNUM *mod;                /* just a reference */
+    CRYPTO_THREAD_ID tid;
+    int counter;
+    unsigned long flags;
+    BN_MONT_CTX *m_ctx;
+    int (*bn_mod_exp) (BIGNUM *r, const BIGNUM *a, const BIGNUM *p,
+                       const BIGNUM *m, BN_CTX *ctx, BN_MONT_CTX *m_ctx);
+    CRYPTO_RWLOCK *lock;
+};
+
+BN_BLINDING *BN_BLINDING_new(const BIGNUM *A, const BIGNUM *Ai, BIGNUM *mod)
+{
+    BN_BLINDING *ret = NULL;
+
+    bn_check_top(mod);
+
+    if ((ret = OPENSSL_zalloc(sizeof(*ret))) == NULL) {
+        BNerr(BN_F_BN_BLINDING_NEW, ERR_R_MALLOC_FAILURE);
+        return NULL;
+    }
+
+    ret->lock = CRYPTO_THREAD_lock_new();
+    if (ret->lock == NULL) {
+        BNerr(BN_F_BN_BLINDING_NEW, ERR_R_MALLOC_FAILURE);
+        OPENSSL_free(ret);
+        return NULL;
+    }
+
+    BN_BLINDING_set_current_thread(ret);
+
+    if (A != NULL) {
+        if ((ret->A = BN_dup(A)) == NULL)
+            goto err;
+    }
+
+    if (Ai != NULL) {
+        if ((ret->Ai = BN_dup(Ai)) == NULL)
+            goto err;
+    }
+
+    /* save a copy of mod in the BN_BLINDING structure */
+    if ((ret->mod = BN_dup(mod)) == NULL)
+        goto err;
+
+    if (BN_get_flags(mod, BN_FLG_CONSTTIME) != 0)
+        BN_set_flags(ret->mod, BN_FLG_CONSTTIME);
+
+    /*
+     * Set the counter to the special value -1 to indicate that this is
+     * never-used fresh blinding that does not need updating before first
+     * use.
+     */
+    ret->counter = -1;
+
+    return ret;
+
+ err:
+    BN_BLINDING_free(ret);
+    return NULL;
+}
+
+void BN_BLINDING_free(BN_BLINDING *r)
+{
+    if (r == NULL)
+        return;
+
+    BN_free(r->A);
+    BN_free(r->Ai);
+    BN_free(r->e);
+    BN_free(r->mod);
+    CRYPTO_THREAD_lock_free(r->lock);
+    OPENSSL_free(r);
+}
+
+int BN_BLINDING_update(BN_BLINDING *b, BN_CTX *ctx)
+{
+    int ret = 0;
+
+    if ((b->A == NULL) || (b->Ai == NULL)) {
+        BNerr(BN_F_BN_BLINDING_UPDATE, BN_R_NOT_INITIALIZED);
+        goto err;
+    }
+
+    if (b->counter == -1)
+        b->counter = 0;
+
+    if (++b->counter == BN_BLINDING_COUNTER && b->e != NULL &&
+        !(b->flags & BN_BLINDING_NO_RECREATE)) {
+        /* re-create blinding parameters */
+        if (!BN_BLINDING_create_param(b, NULL, NULL, ctx, NULL, NULL))
+            goto err;
+    } else if (!(b->flags & BN_BLINDING_NO_UPDATE)) {
+        if (!BN_mod_mul(b->A, b->A, b->A, b->mod, ctx))
+            goto err;
+        if (!BN_mod_mul(b->Ai, b->Ai, b->Ai, b->mod, ctx))
+            goto err;
+    }
+
+    ret = 1;
+ err:
+    if (b->counter == BN_BLINDING_COUNTER)
+        b->counter = 0;
+    return (ret);
+}
+
+int BN_BLINDING_convert(BIGNUM *n, BN_BLINDING *b, BN_CTX *ctx)
+{
+    return BN_BLINDING_convert_ex(n, NULL, b, ctx);
+}
+
+int BN_BLINDING_convert_ex(BIGNUM *n, BIGNUM *r, BN_BLINDING *b, BN_CTX *ctx)
+{
+    int ret = 1;
+
+    bn_check_top(n);
+
+    if ((b->A == NULL) || (b->Ai == NULL)) {
+        BNerr(BN_F_BN_BLINDING_CONVERT_EX, BN_R_NOT_INITIALIZED);
+        return (0);
+    }
+
+    if (b->counter == -1)
+        /* Fresh blinding, doesn't need updating. */
+        b->counter = 0;
+    else if (!BN_BLINDING_update(b, ctx))
+        return (0);
+
+    if (r != NULL) {
+        if (!BN_copy(r, b->Ai))
+            ret = 0;
+    }
+
+    if (!BN_mod_mul(n, n, b->A, b->mod, ctx))
+        ret = 0;
+
+    return ret;
+}
+
+int BN_BLINDING_invert(BIGNUM *n, BN_BLINDING *b, BN_CTX *ctx)
+{
+    return BN_BLINDING_invert_ex(n, NULL, b, ctx);
+}
+
+int BN_BLINDING_invert_ex(BIGNUM *n, const BIGNUM *r, BN_BLINDING *b,
+                          BN_CTX *ctx)
+{
+    int ret;
+
+    bn_check_top(n);
+
+    if (r != NULL)
+        ret = BN_mod_mul(n, n, r, b->mod, ctx);
+    else {
+        if (b->Ai == NULL) {
+            BNerr(BN_F_BN_BLINDING_INVERT_EX, BN_R_NOT_INITIALIZED);
+            return (0);
+        }
+        ret = BN_mod_mul(n, n, b->Ai, b->mod, ctx);
+    }
+
+    bn_check_top(n);
+    return (ret);
+}
+
+int BN_BLINDING_is_current_thread(BN_BLINDING *b)
+{
+    return CRYPTO_THREAD_compare_id(CRYPTO_THREAD_get_current_id(), b->tid);
+}
+
+void BN_BLINDING_set_current_thread(BN_BLINDING *b)
+{
+    b->tid = CRYPTO_THREAD_get_current_id();
+}
+
+int BN_BLINDING_lock(BN_BLINDING *b)
+{
+    return CRYPTO_THREAD_write_lock(b->lock);
+}
+
+int BN_BLINDING_unlock(BN_BLINDING *b)
+{
+    return CRYPTO_THREAD_unlock(b->lock);
+}
+
+unsigned long BN_BLINDING_get_flags(const BN_BLINDING *b)
+{
+    return b->flags;
+}
+
+void BN_BLINDING_set_flags(BN_BLINDING *b, unsigned long flags)
+{
+    b->flags = flags;
+}
+
+BN_BLINDING *BN_BLINDING_create_param(BN_BLINDING *b,
+                                      const BIGNUM *e, BIGNUM *m, BN_CTX *ctx,
+                                      int (*bn_mod_exp) (BIGNUM *r,
+                                                         const BIGNUM *a,
+                                                         const BIGNUM *p,
+                                                         const BIGNUM *m,
+                                                         BN_CTX *ctx,
+                                                         BN_MONT_CTX *m_ctx),
+                                      BN_MONT_CTX *m_ctx)
+{
+    int retry_counter = 32;
+    BN_BLINDING *ret = NULL;
+
+    if (b == NULL)
+        ret = BN_BLINDING_new(NULL, NULL, m);
+    else
+        ret = b;
+
+    if (ret == NULL)
+        goto err;
+
+    if (ret->A == NULL && (ret->A = BN_new()) == NULL)
+        goto err;
+    if (ret->Ai == NULL && (ret->Ai = BN_new()) == NULL)
+        goto err;
+
+    if (e != NULL) {
+        BN_free(ret->e);
+        ret->e = BN_dup(e);
+    }
+    if (ret->e == NULL)
+        goto err;
+
+    if (bn_mod_exp != NULL)
+        ret->bn_mod_exp = bn_mod_exp;
+    if (m_ctx != NULL)
+        ret->m_ctx = m_ctx;
+
+    do {
+        int rv;
+        if (!BN_rand_range(ret->A, ret->mod))
+            goto err;
+        if (!int_bn_mod_inverse(ret->Ai, ret->A, ret->mod, ctx, &rv)) {
+            /*
+             * this should almost never happen for good RSA keys
+             */
+            if (rv) {
+                if (retry_counter-- == 0) {
+                    BNerr(BN_F_BN_BLINDING_CREATE_PARAM,
+                          BN_R_TOO_MANY_ITERATIONS);
+                    goto err;
+                }
+            } else
+                goto err;
+        } else
+            break;
+    } while (1);
+
+    if (ret->bn_mod_exp != NULL && ret->m_ctx != NULL) {
+        if (!ret->bn_mod_exp
+            (ret->A, ret->A, ret->e, ret->mod, ctx, ret->m_ctx))
+            goto err;
+    } else {
+        if (!BN_mod_exp(ret->A, ret->A, ret->e, ret->mod, ctx))
+            goto err;
+    }
+
+    return ret;
+ err:
+    if (b == NULL) {
+        BN_BLINDING_free(ret);
+        ret = NULL;
+    }
+
+    return ret;
+}
diff --git a/crypto/bn/bn_const.c b/crypto/bn/bn_const.c
new file mode 100644
index 000000000..39dd61202
--- /dev/null
+++ b/crypto/bn/bn_const.c
@@ -0,0 +1,553 @@
+/*
+ * Copyright 2005-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+
+/*-
+ * "First Oakley Default Group" from RFC2409, section 6.1.
+ *
+ * The prime is: 2^768 - 2 ^704 - 1 + 2^64 * { [2^638 pi] + 149686 }
+ *
+ * RFC2409 specifies a generator of 2.
+ * RFC2412 specifies a generator of of 22.
+ */
+
+BIGNUM *BN_get_rfc2409_prime_768(BIGNUM *bn)
+{
+    static const unsigned char RFC2409_PRIME_768[] = {
+        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+        0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34,
+        0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1,
+        0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74,
+        0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22,
+        0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
+        0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B,
+        0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37,
+        0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45,
+        0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6,
+        0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x3A, 0x36, 0x20,
+        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    };
+    return BN_bin2bn(RFC2409_PRIME_768, sizeof(RFC2409_PRIME_768), bn);
+}
+
+/*-
+ * "Second Oakley Default Group" from RFC2409, section 6.2.
+ *
+ * The prime is: 2^1024 - 2^960 - 1 + 2^64 * { [2^894 pi] + 129093 }.
+ *
+ * RFC2409 specifies a generator of 2.
+ * RFC2412 specifies a generator of 22.
+ */
+
+BIGNUM *BN_get_rfc2409_prime_1024(BIGNUM *bn)
+{
+    static const unsigned char RFC2409_PRIME_1024[] = {
+        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+        0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34,
+        0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1,
+        0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74,
+        0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22,
+        0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
+        0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B,
+        0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37,
+        0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45,
+        0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6,
+        0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B,
+        0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED,
+        0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5,
+        0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6,
+        0x49, 0x28, 0x66, 0x51, 0xEC, 0xE6, 0x53, 0x81,
+        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    };
+    return BN_bin2bn(RFC2409_PRIME_1024, sizeof(RFC2409_PRIME_1024), bn);
+}
+
+/*-
+ * "1536-bit MODP Group" from RFC3526, Section 2.
+ *
+ * The prime is: 2^1536 - 2^1472 - 1 + 2^64 * { [2^1406 pi] + 741804 }
+ *
+ * RFC3526 specifies a generator of 2.
+ * RFC2312 specifies a generator of 22.
+ */
+
+BIGNUM *BN_get_rfc3526_prime_1536(BIGNUM *bn)
+{
+    static const unsigned char RFC3526_PRIME_1536[] = {
+        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+        0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34,
+        0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1,
+        0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74,
+        0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22,
+        0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
+        0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B,
+        0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37,
+        0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45,
+        0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6,
+        0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B,
+        0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED,
+        0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5,
+        0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6,
+        0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D,
+        0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05,
+        0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, 0x9A,
+        0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F,
+        0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96,
+        0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB,
+        0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D,
+        0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04,
+        0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x23, 0x73, 0x27,
+        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    };
+    return BN_bin2bn(RFC3526_PRIME_1536, sizeof(RFC3526_PRIME_1536), bn);
+}
+
+/*-
+ * "2048-bit MODP Group" from RFC3526, Section 3.
+ *
+ * The prime is: 2^2048 - 2^1984 - 1 + 2^64 * { [2^1918 pi] + 124476 }
+ *
+ * RFC3526 specifies a generator of 2.
+ */
+
+BIGNUM *BN_get_rfc3526_prime_2048(BIGNUM *bn)
+{
+    static const unsigned char RFC3526_PRIME_2048[] = {
+        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+        0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34,
+        0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1,
+        0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74,
+        0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22,
+        0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
+        0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B,
+        0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37,
+        0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45,
+        0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6,
+        0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B,
+        0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED,
+        0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5,
+        0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6,
+        0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D,
+        0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05,
+        0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, 0x9A,
+        0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F,
+        0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96,
+        0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB,
+        0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D,
+        0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04,
+        0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x18, 0x21, 0x7C,
+        0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B,
+        0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03,
+        0x9B, 0x27, 0x83, 0xA2, 0xEC, 0x07, 0xA2, 0x8F,
+        0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9,
+        0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18,
+        0x39, 0x95, 0x49, 0x7C, 0xEA, 0x95, 0x6A, 0xE5,
+        0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10,
+        0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAC, 0xAA, 0x68,
+        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    };
+    return BN_bin2bn(RFC3526_PRIME_2048, sizeof(RFC3526_PRIME_2048), bn);
+}
+
+/*-
+ * "3072-bit MODP Group" from RFC3526, Section 4.
+ *
+ * The prime is: 2^3072 - 2^3008 - 1 + 2^64 * { [2^2942 pi] + 1690314 }
+ *
+ * RFC3526 specifies a generator of 2.
+ */
+
+BIGNUM *BN_get_rfc3526_prime_3072(BIGNUM *bn)
+{
+    static const unsigned char RFC3526_PRIME_3072[] = {
+        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+        0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34,
+        0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1,
+        0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74,
+        0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22,
+        0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
+        0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B,
+        0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37,
+        0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45,
+        0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6,
+        0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B,
+        0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED,
+        0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5,
+        0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6,
+        0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D,
+        0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05,
+        0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, 0x9A,
+        0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F,
+        0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96,
+        0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB,
+        0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D,
+        0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04,
+        0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x18, 0x21, 0x7C,
+        0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B,
+        0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03,
+        0x9B, 0x27, 0x83, 0xA2, 0xEC, 0x07, 0xA2, 0x8F,
+        0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9,
+        0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18,
+        0x39, 0x95, 0x49, 0x7C, 0xEA, 0x95, 0x6A, 0xE5,
+        0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10,
+        0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAA, 0xC4, 0x2D,
+        0xAD, 0x33, 0x17, 0x0D, 0x04, 0x50, 0x7A, 0x33,
+        0xA8, 0x55, 0x21, 0xAB, 0xDF, 0x1C, 0xBA, 0x64,
+        0xEC, 0xFB, 0x85, 0x04, 0x58, 0xDB, 0xEF, 0x0A,
+        0x8A, 0xEA, 0x71, 0x57, 0x5D, 0x06, 0x0C, 0x7D,
+        0xB3, 0x97, 0x0F, 0x85, 0xA6, 0xE1, 0xE4, 0xC7,
+        0xAB, 0xF5, 0xAE, 0x8C, 0xDB, 0x09, 0x33, 0xD7,
+        0x1E, 0x8C, 0x94, 0xE0, 0x4A, 0x25, 0x61, 0x9D,
+        0xCE, 0xE3, 0xD2, 0x26, 0x1A, 0xD2, 0xEE, 0x6B,
+        0xF1, 0x2F, 0xFA, 0x06, 0xD9, 0x8A, 0x08, 0x64,
+        0xD8, 0x76, 0x02, 0x73, 0x3E, 0xC8, 0x6A, 0x64,
+        0x52, 0x1F, 0x2B, 0x18, 0x17, 0x7B, 0x20, 0x0C,
+        0xBB, 0xE1, 0x17, 0x57, 0x7A, 0x61, 0x5D, 0x6C,
+        0x77, 0x09, 0x88, 0xC0, 0xBA, 0xD9, 0x46, 0xE2,
+        0x08, 0xE2, 0x4F, 0xA0, 0x74, 0xE5, 0xAB, 0x31,
+        0x43, 0xDB, 0x5B, 0xFC, 0xE0, 0xFD, 0x10, 0x8E,
+        0x4B, 0x82, 0xD1, 0x20, 0xA9, 0x3A, 0xD2, 0xCA,
+        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    };
+    return BN_bin2bn(RFC3526_PRIME_3072, sizeof(RFC3526_PRIME_3072), bn);
+}
+
+/*-
+ * "4096-bit MODP Group" from RFC3526, Section 5.
+ *
+ * The prime is: 2^4096 - 2^4032 - 1 + 2^64 * { [2^3966 pi] + 240904 }
+ *
+ * RFC3526 specifies a generator of 2.
+ */
+
+BIGNUM *BN_get_rfc3526_prime_4096(BIGNUM *bn)
+{
+    static const unsigned char RFC3526_PRIME_4096[] = {
+        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+        0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34,
+        0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1,
+        0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74,
+        0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22,
+        0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
+        0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B,
+        0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37,
+        0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45,
+        0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6,
+        0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B,
+        0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED,
+        0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5,
+        0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6,
+        0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D,
+        0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05,
+        0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, 0x9A,
+        0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F,
+        0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96,
+        0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB,
+        0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D,
+        0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04,
+        0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x18, 0x21, 0x7C,
+        0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B,
+        0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03,
+        0x9B, 0x27, 0x83, 0xA2, 0xEC, 0x07, 0xA2, 0x8F,
+        0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9,
+        0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18,
+        0x39, 0x95, 0x49, 0x7C, 0xEA, 0x95, 0x6A, 0xE5,
+        0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10,
+        0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAA, 0xC4, 0x2D,
+        0xAD, 0x33, 0x17, 0x0D, 0x04, 0x50, 0x7A, 0x33,
+        0xA8, 0x55, 0x21, 0xAB, 0xDF, 0x1C, 0xBA, 0x64,
+        0xEC, 0xFB, 0x85, 0x04, 0x58, 0xDB, 0xEF, 0x0A,
+        0x8A, 0xEA, 0x71, 0x57, 0x5D, 0x06, 0x0C, 0x7D,
+        0xB3, 0x97, 0x0F, 0x85, 0xA6, 0xE1, 0xE4, 0xC7,
+        0xAB, 0xF5, 0xAE, 0x8C, 0xDB, 0x09, 0x33, 0xD7,
+        0x1E, 0x8C, 0x94, 0xE0, 0x4A, 0x25, 0x61, 0x9D,
+        0xCE, 0xE3, 0xD2, 0x26, 0x1A, 0xD2, 0xEE, 0x6B,
+        0xF1, 0x2F, 0xFA, 0x06, 0xD9, 0x8A, 0x08, 0x64,
+        0xD8, 0x76, 0x02, 0x73, 0x3E, 0xC8, 0x6A, 0x64,
+        0x52, 0x1F, 0x2B, 0x18, 0x17, 0x7B, 0x20, 0x0C,
+        0xBB, 0xE1, 0x17, 0x57, 0x7A, 0x61, 0x5D, 0x6C,
+        0x77, 0x09, 0x88, 0xC0, 0xBA, 0xD9, 0x46, 0xE2,
+        0x08, 0xE2, 0x4F, 0xA0, 0x74, 0xE5, 0xAB, 0x31,
+        0x43, 0xDB, 0x5B, 0xFC, 0xE0, 0xFD, 0x10, 0x8E,
+        0x4B, 0x82, 0xD1, 0x20, 0xA9, 0x21, 0x08, 0x01,
+        0x1A, 0x72, 0x3C, 0x12, 0xA7, 0x87, 0xE6, 0xD7,
+        0x88, 0x71, 0x9A, 0x10, 0xBD, 0xBA, 0x5B, 0x26,
+        0x99, 0xC3, 0x27, 0x18, 0x6A, 0xF4, 0xE2, 0x3C,
+        0x1A, 0x94, 0x68, 0x34, 0xB6, 0x15, 0x0B, 0xDA,
+        0x25, 0x83, 0xE9, 0xCA, 0x2A, 0xD4, 0x4C, 0xE8,
+        0xDB, 0xBB, 0xC2, 0xDB, 0x04, 0xDE, 0x8E, 0xF9,
+        0x2E, 0x8E, 0xFC, 0x14, 0x1F, 0xBE, 0xCA, 0xA6,
+        0x28, 0x7C, 0x59, 0x47, 0x4E, 0x6B, 0xC0, 0x5D,
+        0x99, 0xB2, 0x96, 0x4F, 0xA0, 0x90, 0xC3, 0xA2,
+        0x23, 0x3B, 0xA1, 0x86, 0x51, 0x5B, 0xE7, 0xED,
+        0x1F, 0x61, 0x29, 0x70, 0xCE, 0xE2, 0xD7, 0xAF,
+        0xB8, 0x1B, 0xDD, 0x76, 0x21, 0x70, 0x48, 0x1C,
+        0xD0, 0x06, 0x91, 0x27, 0xD5, 0xB0, 0x5A, 0xA9,
+        0x93, 0xB4, 0xEA, 0x98, 0x8D, 0x8F, 0xDD, 0xC1,
+        0x86, 0xFF, 0xB7, 0xDC, 0x90, 0xA6, 0xC0, 0x8F,
+        0x4D, 0xF4, 0x35, 0xC9, 0x34, 0x06, 0x31, 0x99,
+        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    };
+    return BN_bin2bn(RFC3526_PRIME_4096, sizeof(RFC3526_PRIME_4096), bn);
+}
+
+/*-
+ * "6144-bit MODP Group" from RFC3526, Section 6.
+ *
+ * The prime is: 2^6144 - 2^6080 - 1 + 2^64 * { [2^6014 pi] + 929484 }
+ *
+ * RFC3526 specifies a generator of 2.
+ */
+
+BIGNUM *BN_get_rfc3526_prime_6144(BIGNUM *bn)
+{
+    static const unsigned char RFC3526_PRIME_6144[] = {
+        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+        0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34,
+        0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1,
+        0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74,
+        0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22,
+        0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
+        0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B,
+        0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37,
+        0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45,
+        0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6,
+        0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B,
+        0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED,
+        0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5,
+        0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6,
+        0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D,
+        0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05,
+        0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, 0x9A,
+        0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F,
+        0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96,
+        0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB,
+        0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D,
+        0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04,
+        0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x18, 0x21, 0x7C,
+        0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B,
+        0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03,
+        0x9B, 0x27, 0x83, 0xA2, 0xEC, 0x07, 0xA2, 0x8F,
+        0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9,
+        0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18,
+        0x39, 0x95, 0x49, 0x7C, 0xEA, 0x95, 0x6A, 0xE5,
+        0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10,
+        0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAA, 0xC4, 0x2D,
+        0xAD, 0x33, 0x17, 0x0D, 0x04, 0x50, 0x7A, 0x33,
+        0xA8, 0x55, 0x21, 0xAB, 0xDF, 0x1C, 0xBA, 0x64,
+        0xEC, 0xFB, 0x85, 0x04, 0x58, 0xDB, 0xEF, 0x0A,
+        0x8A, 0xEA, 0x71, 0x57, 0x5D, 0x06, 0x0C, 0x7D,
+        0xB3, 0x97, 0x0F, 0x85, 0xA6, 0xE1, 0xE4, 0xC7,
+        0xAB, 0xF5, 0xAE, 0x8C, 0xDB, 0x09, 0x33, 0xD7,
+        0x1E, 0x8C, 0x94, 0xE0, 0x4A, 0x25, 0x61, 0x9D,
+        0xCE, 0xE3, 0xD2, 0x26, 0x1A, 0xD2, 0xEE, 0x6B,
+        0xF1, 0x2F, 0xFA, 0x06, 0xD9, 0x8A, 0x08, 0x64,
+        0xD8, 0x76, 0x02, 0x73, 0x3E, 0xC8, 0x6A, 0x64,
+        0x52, 0x1F, 0x2B, 0x18, 0x17, 0x7B, 0x20, 0x0C,
+        0xBB, 0xE1, 0x17, 0x57, 0x7A, 0x61, 0x5D, 0x6C,
+        0x77, 0x09, 0x88, 0xC0, 0xBA, 0xD9, 0x46, 0xE2,
+        0x08, 0xE2, 0x4F, 0xA0, 0x74, 0xE5, 0xAB, 0x31,
+        0x43, 0xDB, 0x5B, 0xFC, 0xE0, 0xFD, 0x10, 0x8E,
+        0x4B, 0x82, 0xD1, 0x20, 0xA9, 0x21, 0x08, 0x01,
+        0x1A, 0x72, 0x3C, 0x12, 0xA7, 0x87, 0xE6, 0xD7,
+        0x88, 0x71, 0x9A, 0x10, 0xBD, 0xBA, 0x5B, 0x26,
+        0x99, 0xC3, 0x27, 0x18, 0x6A, 0xF4, 0xE2, 0x3C,
+        0x1A, 0x94, 0x68, 0x34, 0xB6, 0x15, 0x0B, 0xDA,
+        0x25, 0x83, 0xE9, 0xCA, 0x2A, 0xD4, 0x4C, 0xE8,
+        0xDB, 0xBB, 0xC2, 0xDB, 0x04, 0xDE, 0x8E, 0xF9,
+        0x2E, 0x8E, 0xFC, 0x14, 0x1F, 0xBE, 0xCA, 0xA6,
+        0x28, 0x7C, 0x59, 0x47, 0x4E, 0x6B, 0xC0, 0x5D,
+        0x99, 0xB2, 0x96, 0x4F, 0xA0, 0x90, 0xC3, 0xA2,
+        0x23, 0x3B, 0xA1, 0x86, 0x51, 0x5B, 0xE7, 0xED,
+        0x1F, 0x61, 0x29, 0x70, 0xCE, 0xE2, 0xD7, 0xAF,
+        0xB8, 0x1B, 0xDD, 0x76, 0x21, 0x70, 0x48, 0x1C,
+        0xD0, 0x06, 0x91, 0x27, 0xD5, 0xB0, 0x5A, 0xA9,
+        0x93, 0xB4, 0xEA, 0x98, 0x8D, 0x8F, 0xDD, 0xC1,
+        0x86, 0xFF, 0xB7, 0xDC, 0x90, 0xA6, 0xC0, 0x8F,
+        0x4D, 0xF4, 0x35, 0xC9, 0x34, 0x02, 0x84, 0x92,
+        0x36, 0xC3, 0xFA, 0xB4, 0xD2, 0x7C, 0x70, 0x26,
+        0xC1, 0xD4, 0xDC, 0xB2, 0x60, 0x26, 0x46, 0xDE,
+        0xC9, 0x75, 0x1E, 0x76, 0x3D, 0xBA, 0x37, 0xBD,
+        0xF8, 0xFF, 0x94, 0x06, 0xAD, 0x9E, 0x53, 0x0E,
+        0xE5, 0xDB, 0x38, 0x2F, 0x41, 0x30, 0x01, 0xAE,
+        0xB0, 0x6A, 0x53, 0xED, 0x90, 0x27, 0xD8, 0x31,
+        0x17, 0x97, 0x27, 0xB0, 0x86, 0x5A, 0x89, 0x18,
+        0xDA, 0x3E, 0xDB, 0xEB, 0xCF, 0x9B, 0x14, 0xED,
+        0x44, 0xCE, 0x6C, 0xBA, 0xCE, 0xD4, 0xBB, 0x1B,
+        0xDB, 0x7F, 0x14, 0x47, 0xE6, 0xCC, 0x25, 0x4B,
+        0x33, 0x20, 0x51, 0x51, 0x2B, 0xD7, 0xAF, 0x42,
+        0x6F, 0xB8, 0xF4, 0x01, 0x37, 0x8C, 0xD2, 0xBF,
+        0x59, 0x83, 0xCA, 0x01, 0xC6, 0x4B, 0x92, 0xEC,
+        0xF0, 0x32, 0xEA, 0x15, 0xD1, 0x72, 0x1D, 0x03,
+        0xF4, 0x82, 0xD7, 0xCE, 0x6E, 0x74, 0xFE, 0xF6,
+        0xD5, 0x5E, 0x70, 0x2F, 0x46, 0x98, 0x0C, 0x82,
+        0xB5, 0xA8, 0x40, 0x31, 0x90, 0x0B, 0x1C, 0x9E,
+        0x59, 0xE7, 0xC9, 0x7F, 0xBE, 0xC7, 0xE8, 0xF3,
+        0x23, 0xA9, 0x7A, 0x7E, 0x36, 0xCC, 0x88, 0xBE,
+        0x0F, 0x1D, 0x45, 0xB7, 0xFF, 0x58, 0x5A, 0xC5,
+        0x4B, 0xD4, 0x07, 0xB2, 0x2B, 0x41, 0x54, 0xAA,
+        0xCC, 0x8F, 0x6D, 0x7E, 0xBF, 0x48, 0xE1, 0xD8,
+        0x14, 0xCC, 0x5E, 0xD2, 0x0F, 0x80, 0x37, 0xE0,
+        0xA7, 0x97, 0x15, 0xEE, 0xF2, 0x9B, 0xE3, 0x28,
+        0x06, 0xA1, 0xD5, 0x8B, 0xB7, 0xC5, 0xDA, 0x76,
+        0xF5, 0x50, 0xAA, 0x3D, 0x8A, 0x1F, 0xBF, 0xF0,
+        0xEB, 0x19, 0xCC, 0xB1, 0xA3, 0x13, 0xD5, 0x5C,
+        0xDA, 0x56, 0xC9, 0xEC, 0x2E, 0xF2, 0x96, 0x32,
+        0x38, 0x7F, 0xE8, 0xD7, 0x6E, 0x3C, 0x04, 0x68,
+        0x04, 0x3E, 0x8F, 0x66, 0x3F, 0x48, 0x60, 0xEE,
+        0x12, 0xBF, 0x2D, 0x5B, 0x0B, 0x74, 0x74, 0xD6,
+        0xE6, 0x94, 0xF9, 0x1E, 0x6D, 0xCC, 0x40, 0x24,
+        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    };
+    return BN_bin2bn(RFC3526_PRIME_6144, sizeof(RFC3526_PRIME_6144), bn);
+}
+
+/*-
+ * "8192-bit MODP Group" from RFC3526, Section 7.
+ *
+ * The prime is: 2^8192 - 2^8128 - 1 + 2^64 * { [2^8062 pi] + 4743158 }
+ *
+ * RFC3526 specifies a generator of 2.
+ */
+
+BIGNUM *BN_get_rfc3526_prime_8192(BIGNUM *bn)
+{
+    static const unsigned char RFC3526_PRIME_8192[] = {
+        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+        0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34,
+        0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1,
+        0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74,
+        0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22,
+        0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
+        0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B,
+        0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37,
+        0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45,
+        0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6,
+        0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B,
+        0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED,
+        0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5,
+        0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6,
+        0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D,
+        0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05,
+        0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, 0x9A,
+        0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F,
+        0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96,
+        0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB,
+        0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D,
+        0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04,
+        0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x18, 0x21, 0x7C,
+        0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B,
+        0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03,
+        0x9B, 0x27, 0x83, 0xA2, 0xEC, 0x07, 0xA2, 0x8F,
+        0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9,
+        0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18,
+        0x39, 0x95, 0x49, 0x7C, 0xEA, 0x95, 0x6A, 0xE5,
+        0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10,
+        0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAA, 0xC4, 0x2D,
+        0xAD, 0x33, 0x17, 0x0D, 0x04, 0x50, 0x7A, 0x33,
+        0xA8, 0x55, 0x21, 0xAB, 0xDF, 0x1C, 0xBA, 0x64,
+        0xEC, 0xFB, 0x85, 0x04, 0x58, 0xDB, 0xEF, 0x0A,
+        0x8A, 0xEA, 0x71, 0x57, 0x5D, 0x06, 0x0C, 0x7D,
+        0xB3, 0x97, 0x0F, 0x85, 0xA6, 0xE1, 0xE4, 0xC7,
+        0xAB, 0xF5, 0xAE, 0x8C, 0xDB, 0x09, 0x33, 0xD7,
+        0x1E, 0x8C, 0x94, 0xE0, 0x4A, 0x25, 0x61, 0x9D,
+        0xCE, 0xE3, 0xD2, 0x26, 0x1A, 0xD2, 0xEE, 0x6B,
+        0xF1, 0x2F, 0xFA, 0x06, 0xD9, 0x8A, 0x08, 0x64,
+        0xD8, 0x76, 0x02, 0x73, 0x3E, 0xC8, 0x6A, 0x64,
+        0x52, 0x1F, 0x2B, 0x18, 0x17, 0x7B, 0x20, 0x0C,
+        0xBB, 0xE1, 0x17, 0x57, 0x7A, 0x61, 0x5D, 0x6C,
+        0x77, 0x09, 0x88, 0xC0, 0xBA, 0xD9, 0x46, 0xE2,
+        0x08, 0xE2, 0x4F, 0xA0, 0x74, 0xE5, 0xAB, 0x31,
+        0x43, 0xDB, 0x5B, 0xFC, 0xE0, 0xFD, 0x10, 0x8E,
+        0x4B, 0x82, 0xD1, 0x20, 0xA9, 0x21, 0x08, 0x01,
+        0x1A, 0x72, 0x3C, 0x12, 0xA7, 0x87, 0xE6, 0xD7,
+        0x88, 0x71, 0x9A, 0x10, 0xBD, 0xBA, 0x5B, 0x26,
+        0x99, 0xC3, 0x27, 0x18, 0x6A, 0xF4, 0xE2, 0x3C,
+        0x1A, 0x94, 0x68, 0x34, 0xB6, 0x15, 0x0B, 0xDA,
+        0x25, 0x83, 0xE9, 0xCA, 0x2A, 0xD4, 0x4C, 0xE8,
+        0xDB, 0xBB, 0xC2, 0xDB, 0x04, 0xDE, 0x8E, 0xF9,
+        0x2E, 0x8E, 0xFC, 0x14, 0x1F, 0xBE, 0xCA, 0xA6,
+        0x28, 0x7C, 0x59, 0x47, 0x4E, 0x6B, 0xC0, 0x5D,
+        0x99, 0xB2, 0x96, 0x4F, 0xA0, 0x90, 0xC3, 0xA2,
+        0x23, 0x3B, 0xA1, 0x86, 0x51, 0x5B, 0xE7, 0xED,
+        0x1F, 0x61, 0x29, 0x70, 0xCE, 0xE2, 0xD7, 0xAF,
+        0xB8, 0x1B, 0xDD, 0x76, 0x21, 0x70, 0x48, 0x1C,
+        0xD0, 0x06, 0x91, 0x27, 0xD5, 0xB0, 0x5A, 0xA9,
+        0x93, 0xB4, 0xEA, 0x98, 0x8D, 0x8F, 0xDD, 0xC1,
+        0x86, 0xFF, 0xB7, 0xDC, 0x90, 0xA6, 0xC0, 0x8F,
+        0x4D, 0xF4, 0x35, 0xC9, 0x34, 0x02, 0x84, 0x92,
+        0x36, 0xC3, 0xFA, 0xB4, 0xD2, 0x7C, 0x70, 0x26,
+        0xC1, 0xD4, 0xDC, 0xB2, 0x60, 0x26, 0x46, 0xDE,
+        0xC9, 0x75, 0x1E, 0x76, 0x3D, 0xBA, 0x37, 0xBD,
+        0xF8, 0xFF, 0x94, 0x06, 0xAD, 0x9E, 0x53, 0x0E,
+        0xE5, 0xDB, 0x38, 0x2F, 0x41, 0x30, 0x01, 0xAE,
+        0xB0, 0x6A, 0x53, 0xED, 0x90, 0x27, 0xD8, 0x31,
+        0x17, 0x97, 0x27, 0xB0, 0x86, 0x5A, 0x89, 0x18,
+        0xDA, 0x3E, 0xDB, 0xEB, 0xCF, 0x9B, 0x14, 0xED,
+        0x44, 0xCE, 0x6C, 0xBA, 0xCE, 0xD4, 0xBB, 0x1B,
+        0xDB, 0x7F, 0x14, 0x47, 0xE6, 0xCC, 0x25, 0x4B,
+        0x33, 0x20, 0x51, 0x51, 0x2B, 0xD7, 0xAF, 0x42,
+        0x6F, 0xB8, 0xF4, 0x01, 0x37, 0x8C, 0xD2, 0xBF,
+        0x59, 0x83, 0xCA, 0x01, 0xC6, 0x4B, 0x92, 0xEC,
+        0xF0, 0x32, 0xEA, 0x15, 0xD1, 0x72, 0x1D, 0x03,
+        0xF4, 0x82, 0xD7, 0xCE, 0x6E, 0x74, 0xFE, 0xF6,
+        0xD5, 0x5E, 0x70, 0x2F, 0x46, 0x98, 0x0C, 0x82,
+        0xB5, 0xA8, 0x40, 0x31, 0x90, 0x0B, 0x1C, 0x9E,
+        0x59, 0xE7, 0xC9, 0x7F, 0xBE, 0xC7, 0xE8, 0xF3,
+        0x23, 0xA9, 0x7A, 0x7E, 0x36, 0xCC, 0x88, 0xBE,
+        0x0F, 0x1D, 0x45, 0xB7, 0xFF, 0x58, 0x5A, 0xC5,
+        0x4B, 0xD4, 0x07, 0xB2, 0x2B, 0x41, 0x54, 0xAA,
+        0xCC, 0x8F, 0x6D, 0x7E, 0xBF, 0x48, 0xE1, 0xD8,
+        0x14, 0xCC, 0x5E, 0xD2, 0x0F, 0x80, 0x37, 0xE0,
+        0xA7, 0x97, 0x15, 0xEE, 0xF2, 0x9B, 0xE3, 0x28,
+        0x06, 0xA1, 0xD5, 0x8B, 0xB7, 0xC5, 0xDA, 0x76,
+        0xF5, 0x50, 0xAA, 0x3D, 0x8A, 0x1F, 0xBF, 0xF0,
+        0xEB, 0x19, 0xCC, 0xB1, 0xA3, 0x13, 0xD5, 0x5C,
+        0xDA, 0x56, 0xC9, 0xEC, 0x2E, 0xF2, 0x96, 0x32,
+        0x38, 0x7F, 0xE8, 0xD7, 0x6E, 0x3C, 0x04, 0x68,
+        0x04, 0x3E, 0x8F, 0x66, 0x3F, 0x48, 0x60, 0xEE,
+        0x12, 0xBF, 0x2D, 0x5B, 0x0B, 0x74, 0x74, 0xD6,
+        0xE6, 0x94, 0xF9, 0x1E, 0x6D, 0xBE, 0x11, 0x59,
+        0x74, 0xA3, 0x92, 0x6F, 0x12, 0xFE, 0xE5, 0xE4,
+        0x38, 0x77, 0x7C, 0xB6, 0xA9, 0x32, 0xDF, 0x8C,
+        0xD8, 0xBE, 0xC4, 0xD0, 0x73, 0xB9, 0x31, 0xBA,
+        0x3B, 0xC8, 0x32, 0xB6, 0x8D, 0x9D, 0xD3, 0x00,
+        0x74, 0x1F, 0xA7, 0xBF, 0x8A, 0xFC, 0x47, 0xED,
+        0x25, 0x76, 0xF6, 0x93, 0x6B, 0xA4, 0x24, 0x66,
+        0x3A, 0xAB, 0x63, 0x9C, 0x5A, 0xE4, 0xF5, 0x68,
+        0x34, 0x23, 0xB4, 0x74, 0x2B, 0xF1, 0xC9, 0x78,
+        0x23, 0x8F, 0x16, 0xCB, 0xE3, 0x9D, 0x65, 0x2D,
+        0xE3, 0xFD, 0xB8, 0xBE, 0xFC, 0x84, 0x8A, 0xD9,
+        0x22, 0x22, 0x2E, 0x04, 0xA4, 0x03, 0x7C, 0x07,
+        0x13, 0xEB, 0x57, 0xA8, 0x1A, 0x23, 0xF0, 0xC7,
+        0x34, 0x73, 0xFC, 0x64, 0x6C, 0xEA, 0x30, 0x6B,
+        0x4B, 0xCB, 0xC8, 0x86, 0x2F, 0x83, 0x85, 0xDD,
+        0xFA, 0x9D, 0x4B, 0x7F, 0xA2, 0xC0, 0x87, 0xE8,
+        0x79, 0x68, 0x33, 0x03, 0xED, 0x5B, 0xDD, 0x3A,
+        0x06, 0x2B, 0x3C, 0xF5, 0xB3, 0xA2, 0x78, 0xA6,
+        0x6D, 0x2A, 0x13, 0xF8, 0x3F, 0x44, 0xF8, 0x2D,
+        0xDF, 0x31, 0x0E, 0xE0, 0x74, 0xAB, 0x6A, 0x36,
+        0x45, 0x97, 0xE8, 0x99, 0xA0, 0x25, 0x5D, 0xC1,
+        0x64, 0xF3, 0x1C, 0xC5, 0x08, 0x46, 0x85, 0x1D,
+        0xF9, 0xAB, 0x48, 0x19, 0x5D, 0xED, 0x7E, 0xA1,
+        0xB1, 0xD5, 0x10, 0xBD, 0x7E, 0xE7, 0x4D, 0x73,
+        0xFA, 0xF3, 0x6B, 0xC3, 0x1E, 0xCF, 0xA2, 0x68,
+        0x35, 0x90, 0x46, 0xF4, 0xEB, 0x87, 0x9F, 0x92,
+        0x40, 0x09, 0x43, 0x8B, 0x48, 0x1C, 0x6C, 0xD7,
+        0x88, 0x9A, 0x00, 0x2E, 0xD5, 0xEE, 0x38, 0x2B,
+        0xC9, 0x19, 0x0D, 0xA6, 0xFC, 0x02, 0x6E, 0x47,
+        0x95, 0x58, 0xE4, 0x47, 0x56, 0x77, 0xE9, 0xAA,
+        0x9E, 0x30, 0x50, 0xE2, 0x76, 0x56, 0x94, 0xDF,
+        0xC8, 0x1F, 0x56, 0xE8, 0x80, 0xB9, 0x6E, 0x71,
+        0x60, 0xC9, 0x80, 0xDD, 0x98, 0xED, 0xD3, 0xDF,
+        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    };
+    return BN_bin2bn(RFC3526_PRIME_8192, sizeof(RFC3526_PRIME_8192), bn);
+}
diff --git a/crypto/bn/bn_ctx.c b/crypto/bn/bn_ctx.c
new file mode 100644
index 000000000..68c046874
--- /dev/null
+++ b/crypto/bn/bn_ctx.c
@@ -0,0 +1,353 @@
+/*
+ * Copyright 2000-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "internal/cryptlib.h"
+#include "bn_lcl.h"
+
+/*-
+ * TODO list
+ *
+ * 1. Check a bunch of "(words+1)" type hacks in various bignum functions and
+ * check they can be safely removed.
+ *  - Check +1 and other ugliness in BN_from_montgomery()
+ *
+ * 2. Consider allowing a BN_new_ex() that, at least, lets you specify an
+ * appropriate 'block' size that will be honoured by bn_expand_internal() to
+ * prevent piddly little reallocations. OTOH, profiling bignum expansions in
+ * BN_CTX doesn't show this to be a big issue.
+ */
+
+/* How many bignums are in each "pool item"; */
+#define BN_CTX_POOL_SIZE        16
+/* The stack frame info is resizing, set a first-time expansion size; */
+#define BN_CTX_START_FRAMES     32
+
+/***********/
+/* BN_POOL */
+/***********/
+
+/* A bundle of bignums that can be linked with other bundles */
+typedef struct bignum_pool_item {
+    /* The bignum values */
+    BIGNUM vals[BN_CTX_POOL_SIZE];
+    /* Linked-list admin */
+    struct bignum_pool_item *prev, *next;
+} BN_POOL_ITEM;
+/* A linked-list of bignums grouped in bundles */
+typedef struct bignum_pool {
+    /* Linked-list admin */
+    BN_POOL_ITEM *head, *current, *tail;
+    /* Stack depth and allocation size */
+    unsigned used, size;
+} BN_POOL;
+static void BN_POOL_init(BN_POOL *);
+static void BN_POOL_finish(BN_POOL *);
+static BIGNUM *BN_POOL_get(BN_POOL *, int);
+static void BN_POOL_release(BN_POOL *, unsigned int);
+
+/************/
+/* BN_STACK */
+/************/
+
+/* A wrapper to manage the "stack frames" */
+typedef struct bignum_ctx_stack {
+    /* Array of indexes into the bignum stack */
+    unsigned int *indexes;
+    /* Number of stack frames, and the size of the allocated array */
+    unsigned int depth, size;
+} BN_STACK;
+static void BN_STACK_init(BN_STACK *);
+static void BN_STACK_finish(BN_STACK *);
+static int BN_STACK_push(BN_STACK *, unsigned int);
+static unsigned int BN_STACK_pop(BN_STACK *);
+
+/**********/
+/* BN_CTX */
+/**********/
+
+/* The opaque BN_CTX type */
+struct bignum_ctx {
+    /* The bignum bundles */
+    BN_POOL pool;
+    /* The "stack frames", if you will */
+    BN_STACK stack;
+    /* The number of bignums currently assigned */
+    unsigned int used;
+    /* Depth of stack overflow */
+    int err_stack;
+    /* Block "gets" until an "end" (compatibility behaviour) */
+    int too_many;
+    /* Flags. */
+    int flags;
+};
+
+/* Enable this to find BN_CTX bugs */
+#ifdef BN_CTX_DEBUG
+static const char *ctxdbg_cur = NULL;
+static void ctxdbg(BN_CTX *ctx)
+{
+    unsigned int bnidx = 0, fpidx = 0;
+    BN_POOL_ITEM *item = ctx->pool.head;
+    BN_STACK *stack = &ctx->stack;
+    fprintf(stderr, "(%16p): ", ctx);
+    while (bnidx < ctx->used) {
+        fprintf(stderr, "%03x ", item->vals[bnidx++ % BN_CTX_POOL_SIZE].dmax);
+        if (!(bnidx % BN_CTX_POOL_SIZE))
+            item = item->next;
+    }
+    fprintf(stderr, "\n");
+    bnidx = 0;
+    fprintf(stderr, "          : ");
+    while (fpidx < stack->depth) {
+        while (bnidx++ < stack->indexes[fpidx])
+            fprintf(stderr, "    ");
+        fprintf(stderr, "^^^ ");
+        bnidx++;
+        fpidx++;
+    }
+    fprintf(stderr, "\n");
+}
+
+# define CTXDBG_ENTRY(str, ctx)  do { \
+                                ctxdbg_cur = (str); \
+                                fprintf(stderr,"Starting %s\n", ctxdbg_cur); \
+                                ctxdbg(ctx); \
+                                } while(0)
+# define CTXDBG_EXIT(ctx)        do { \
+                                fprintf(stderr,"Ending %s\n", ctxdbg_cur); \
+                                ctxdbg(ctx); \
+                                } while(0)
+# define CTXDBG_RET(ctx,ret)
+#else
+# define CTXDBG_ENTRY(str, ctx)
+# define CTXDBG_EXIT(ctx)
+# define CTXDBG_RET(ctx,ret)
+#endif
+
+
+BN_CTX *BN_CTX_new(void)
+{
+    BN_CTX *ret;
+
+    if ((ret = OPENSSL_zalloc(sizeof(*ret))) == NULL) {
+        BNerr(BN_F_BN_CTX_NEW, ERR_R_MALLOC_FAILURE);
+        return NULL;
+    }
+    /* Initialise the structure */
+    BN_POOL_init(&ret->pool);
+    BN_STACK_init(&ret->stack);
+    return ret;
+}
+
+BN_CTX *BN_CTX_secure_new(void)
+{
+    BN_CTX *ret = BN_CTX_new();
+
+    if (ret != NULL)
+        ret->flags = BN_FLG_SECURE;
+    return ret;
+}
+
+void BN_CTX_free(BN_CTX *ctx)
+{
+    if (ctx == NULL)
+        return;
+#ifdef BN_CTX_DEBUG
+    {
+        BN_POOL_ITEM *pool = ctx->pool.head;
+        fprintf(stderr, "BN_CTX_free, stack-size=%d, pool-bignums=%d\n",
+                ctx->stack.size, ctx->pool.size);
+        fprintf(stderr, "dmaxs: ");
+        while (pool) {
+            unsigned loop = 0;
+            while (loop < BN_CTX_POOL_SIZE)
+                fprintf(stderr, "%02x ", pool->vals[loop++].dmax);
+            pool = pool->next;
+        }
+        fprintf(stderr, "\n");
+    }
+#endif
+    BN_STACK_finish(&ctx->stack);
+    BN_POOL_finish(&ctx->pool);
+    OPENSSL_free(ctx);
+}
+
+void BN_CTX_start(BN_CTX *ctx)
+{
+    CTXDBG_ENTRY("BN_CTX_start", ctx);
+    /* If we're already overflowing ... */
+    if (ctx->err_stack || ctx->too_many)
+        ctx->err_stack++;
+    /* (Try to) get a new frame pointer */
+    else if (!BN_STACK_push(&ctx->stack, ctx->used)) {
+        BNerr(BN_F_BN_CTX_START, BN_R_TOO_MANY_TEMPORARY_VARIABLES);
+        ctx->err_stack++;
+    }
+    CTXDBG_EXIT(ctx);
+}
+
+void BN_CTX_end(BN_CTX *ctx)
+{
+    CTXDBG_ENTRY("BN_CTX_end", ctx);
+    if (ctx->err_stack)
+        ctx->err_stack--;
+    else {
+        unsigned int fp = BN_STACK_pop(&ctx->stack);
+        /* Does this stack frame have anything to release? */
+        if (fp < ctx->used)
+            BN_POOL_release(&ctx->pool, ctx->used - fp);
+        ctx->used = fp;
+        /* Unjam "too_many" in case "get" had failed */
+        ctx->too_many = 0;
+    }
+    CTXDBG_EXIT(ctx);
+}
+
+BIGNUM *BN_CTX_get(BN_CTX *ctx)
+{
+    BIGNUM *ret;
+
+    CTXDBG_ENTRY("BN_CTX_get", ctx);
+    if (ctx->err_stack || ctx->too_many)
+        return NULL;
+    if ((ret = BN_POOL_get(&ctx->pool, ctx->flags)) == NULL) {
+        /*
+         * Setting too_many prevents repeated "get" attempts from cluttering
+         * the error stack.
+         */
+        ctx->too_many = 1;
+        BNerr(BN_F_BN_CTX_GET, BN_R_TOO_MANY_TEMPORARY_VARIABLES);
+        return NULL;
+    }
+    /* OK, make sure the returned bignum is "zero" */
+    BN_zero(ret);
+    ctx->used++;
+    CTXDBG_RET(ctx, ret);
+    return ret;
+}
+
+/************/
+/* BN_STACK */
+/************/
+
+static void BN_STACK_init(BN_STACK *st)
+{
+    st->indexes = NULL;
+    st->depth = st->size = 0;
+}
+
+static void BN_STACK_finish(BN_STACK *st)
+{
+    OPENSSL_free(st->indexes);
+    st->indexes = NULL;
+}
+
+
+static int BN_STACK_push(BN_STACK *st, unsigned int idx)
+{
+    if (st->depth == st->size) {
+        /* Need to expand */
+        unsigned int newsize =
+            st->size ? (st->size * 3 / 2) : BN_CTX_START_FRAMES;
+        unsigned int *newitems = OPENSSL_malloc(sizeof(*newitems) * newsize);
+        if (newitems == NULL)
+            return 0;
+        if (st->depth)
+            memcpy(newitems, st->indexes, sizeof(*newitems) * st->depth);
+        OPENSSL_free(st->indexes);
+        st->indexes = newitems;
+        st->size = newsize;
+    }
+    st->indexes[(st->depth)++] = idx;
+    return 1;
+}
+
+static unsigned int BN_STACK_pop(BN_STACK *st)
+{
+    return st->indexes[--(st->depth)];
+}
+
+/***********/
+/* BN_POOL */
+/***********/
+
+static void BN_POOL_init(BN_POOL *p)
+{
+    p->head = p->current = p->tail = NULL;
+    p->used = p->size = 0;
+}
+
+static void BN_POOL_finish(BN_POOL *p)
+{
+    unsigned int loop;
+    BIGNUM *bn;
+
+    while (p->head) {
+        for (loop = 0, bn = p->head->vals; loop++ < BN_CTX_POOL_SIZE; bn++)
+            if (bn->d)
+                BN_clear_free(bn);
+        p->current = p->head->next;
+        OPENSSL_free(p->head);
+        p->head = p->current;
+    }
+}
+
+
+static BIGNUM *BN_POOL_get(BN_POOL *p, int flag)
+{
+    BIGNUM *bn;
+    unsigned int loop;
+
+    /* Full; allocate a new pool item and link it in. */
+    if (p->used == p->size) {
+        BN_POOL_ITEM *item = OPENSSL_malloc(sizeof(*item));
+        if (item == NULL)
+            return NULL;
+        for (loop = 0, bn = item->vals; loop++ < BN_CTX_POOL_SIZE; bn++) {
+            bn_init(bn);
+            if ((flag & BN_FLG_SECURE) != 0)
+                BN_set_flags(bn, BN_FLG_SECURE);
+        }
+        item->prev = p->tail;
+        item->next = NULL;
+
+        if (p->head == NULL)
+            p->head = p->current = p->tail = item;
+        else {
+            p->tail->next = item;
+            p->tail = item;
+            p->current = item;
+        }
+        p->size += BN_CTX_POOL_SIZE;
+        p->used++;
+        /* Return the first bignum from the new pool */
+        return item->vals;
+    }
+
+    if (!p->used)
+        p->current = p->head;
+    else if ((p->used % BN_CTX_POOL_SIZE) == 0)
+        p->current = p->current->next;
+    return p->current->vals + ((p->used++) % BN_CTX_POOL_SIZE);
+}
+
+static void BN_POOL_release(BN_POOL *p, unsigned int num)
+{
+    unsigned int offset = (p->used - 1) % BN_CTX_POOL_SIZE;
+
+    p->used -= num;
+    while (num--) {
+        bn_check_top(p->current->vals + offset);
+        if (offset == 0) {
+            offset = BN_CTX_POOL_SIZE - 1;
+            p->current = p->current->prev;
+        } else
+            offset--;
+    }
+}
diff --git a/crypto/bn/bn_depr.c b/crypto/bn/bn_depr.c
new file mode 100644
index 000000000..7d89214b1
--- /dev/null
+++ b/crypto/bn/bn_depr.c
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2002-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * Support for deprecated functions goes here - static linkage will only
+ * slurp this code if applications are using them directly.
+ */
+
+#include 
+#if OPENSSL_API_COMPAT >= 0x00908000L
+NON_EMPTY_TRANSLATION_UNIT
+#else
+
+# include 
+# include 
+# include "internal/cryptlib.h"
+# include "bn_lcl.h"
+
+BIGNUM *BN_generate_prime(BIGNUM *ret, int bits, int safe,
+                          const BIGNUM *add, const BIGNUM *rem,
+                          void (*callback) (int, int, void *), void *cb_arg)
+{
+    BN_GENCB cb;
+    BIGNUM *rnd = NULL;
+
+    BN_GENCB_set_old(&cb, callback, cb_arg);
+
+    if (ret == NULL) {
+        if ((rnd = BN_new()) == NULL)
+            goto err;
+    } else
+        rnd = ret;
+    if (!BN_generate_prime_ex(rnd, bits, safe, add, rem, &cb))
+        goto err;
+
+    /* we have a prime :-) */
+    return ret;
+ err:
+    BN_free(rnd);
+    return NULL;
+}
+
+int BN_is_prime(const BIGNUM *a, int checks,
+                void (*callback) (int, int, void *), BN_CTX *ctx_passed,
+                void *cb_arg)
+{
+    BN_GENCB cb;
+    BN_GENCB_set_old(&cb, callback, cb_arg);
+    return BN_is_prime_ex(a, checks, ctx_passed, &cb);
+}
+
+int BN_is_prime_fasttest(const BIGNUM *a, int checks,
+                         void (*callback) (int, int, void *),
+                         BN_CTX *ctx_passed, void *cb_arg,
+                         int do_trial_division)
+{
+    BN_GENCB cb;
+    BN_GENCB_set_old(&cb, callback, cb_arg);
+    return BN_is_prime_fasttest_ex(a, checks, ctx_passed,
+                                   do_trial_division, &cb);
+}
+#endif
diff --git a/crypto/bn/bn_dh.c b/crypto/bn/bn_dh.c
new file mode 100644
index 000000000..17d05597b
--- /dev/null
+++ b/crypto/bn/bn_dh.c
@@ -0,0 +1,220 @@
+/*
+ * Copyright 2014-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "bn_lcl.h"
+#include "e_os.h"
+
+#ifndef OPENSSL_NO_DH
+#include 
+#include "internal/bn_dh.h"
+/* DH parameters from RFC5114 */
+
+# if BN_BITS2 == 64
+static const BN_ULONG dh1024_160_p[] = {
+    0xDF1FB2BC2E4A4371ULL, 0xE68CFDA76D4DA708ULL, 0x45BF37DF365C1A65ULL,
+    0xA151AF5F0DC8B4BDULL, 0xFAA31A4FF55BCCC0ULL, 0x4EFFD6FAE5644738ULL,
+    0x98488E9C219A7372ULL, 0xACCBDD7D90C4BD70ULL, 0x24975C3CD49B83BFULL,
+    0x13ECB4AEA9061123ULL, 0x9838EF1E2EE652C0ULL, 0x6073E28675A23D18ULL,
+    0x9A6A9DCA52D23B61ULL, 0x52C99FBCFB06A3C6ULL, 0xDE92DE5EAE5D54ECULL,
+    0xB10B8F96A080E01DULL
+};
+
+static const BN_ULONG dh1024_160_g[] = {
+    0x855E6EEB22B3B2E5ULL, 0x858F4DCEF97C2A24ULL, 0x2D779D5918D08BC8ULL,
+    0xD662A4D18E73AFA3ULL, 0x1DBF0A0169B6A28AULL, 0xA6A24C087A091F53ULL,
+    0x909D0D2263F80A76ULL, 0xD7FBD7D3B9A92EE1ULL, 0x5E91547F9E2749F4ULL,
+    0x160217B4B01B886AULL, 0x777E690F5504F213ULL, 0x266FEA1E5C41564BULL,
+    0xD6406CFF14266D31ULL, 0xF8104DD258AC507FULL, 0x6765A442EFB99905ULL,
+    0xA4D1CBD5C3FD3412ULL
+};
+
+static const BN_ULONG dh1024_160_q[] = {
+    0x64B7CB9D49462353ULL, 0x81A8DF278ABA4E7DULL, 0x00000000F518AA87ULL
+};
+
+static const BN_ULONG dh2048_224_p[] = {
+    0x0AC4DFFE0C10E64FULL, 0xCF9DE5384E71B81CULL, 0x7EF363E2FFA31F71ULL,
+    0xE3FB73C16B8E75B9ULL, 0xC9B53DCF4BA80A29ULL, 0x23F10B0E16E79763ULL,
+    0xC52172E413042E9BULL, 0xBE60E69CC928B2B9ULL, 0x80CD86A1B9E587E8ULL,
+    0x315D75E198C641A4ULL, 0xCDF93ACC44328387ULL, 0x15987D9ADC0A486DULL,
+    0x7310F7121FD5A074ULL, 0x278273C7DE31EFDCULL, 0x1602E714415D9330ULL,
+    0x81286130BC8985DBULL, 0xB3BF8A3170918836ULL, 0x6A00E0A0B9C49708ULL,
+    0xC6BA0B2C8BBC27BEULL, 0xC9F98D11ED34DBF6ULL, 0x7AD5B7D0B6C12207ULL,
+    0xD91E8FEF55B7394BULL, 0x9037C9EDEFDA4DF8ULL, 0x6D3F8152AD6AC212ULL,
+    0x1DE6B85A1274A0A6ULL, 0xEB3D688A309C180EULL, 0xAF9A3C407BA1DF15ULL,
+    0xE6FA141DF95A56DBULL, 0xB54B1597B61D0A75ULL, 0xA20D64E5683B9FD1ULL,
+    0xD660FAA79559C51FULL, 0xAD107E1E9123A9D0ULL
+};
+
+static const BN_ULONG dh2048_224_g[] = {
+    0x84B890D3191F2BFAULL, 0x81BC087F2A7065B3ULL, 0x19C418E1F6EC0179ULL,
+    0x7B5A0F1C71CFFF4CULL, 0xEDFE72FE9B6AA4BDULL, 0x81E1BCFE94B30269ULL,
+    0x566AFBB48D6C0191ULL, 0xB539CCE3409D13CDULL, 0x6AA21E7F5F2FF381ULL,
+    0xD9E263E4770589EFULL, 0x10E183EDD19963DDULL, 0xB70A8137150B8EEBULL,
+    0x051AE3D428C8F8ACULL, 0xBB77A86F0C1AB15BULL, 0x6E3025E316A330EFULL,
+    0x19529A45D6F83456ULL, 0xF180EB34118E98D1ULL, 0xB5F6C6B250717CBEULL,
+    0x09939D54DA7460CDULL, 0xE247150422EA1ED4ULL, 0xB8A762D0521BC98AULL,
+    0xF4D027275AC1348BULL, 0xC17669101999024AULL, 0xBE5E9001A8D66AD7ULL,
+    0xC57DB17C620A8652ULL, 0xAB739D7700C29F52ULL, 0xDD921F01A70C4AFAULL,
+    0xA6824A4E10B9A6F0ULL, 0x74866A08CFE4FFE3ULL, 0x6CDEBE7B89998CAFULL,
+    0x9DF30B5C8FFDAC50ULL, 0xAC4032EF4F2D9AE3ULL
+};
+
+static const BN_ULONG dh2048_224_q[] = {
+    0xBF389A99B36371EBULL, 0x1F80535A4738CEBCULL, 0xC58D93FE99717710ULL,
+    0x00000000801C0D34ULL
+};
+
+static const BN_ULONG dh2048_256_p[] = {
+    0xDB094AE91E1A1597ULL, 0x693877FAD7EF09CAULL, 0x6116D2276E11715FULL,
+    0xA4B54330C198AF12ULL, 0x75F26375D7014103ULL, 0xC3A3960A54E710C3ULL,
+    0xDED4010ABD0BE621ULL, 0xC0B857F689962856ULL, 0xB3CA3F7971506026ULL,
+    0x1CCACB83E6B486F6ULL, 0x67E144E514056425ULL, 0xF6A167B5A41825D9ULL,
+    0x3AD8347796524D8EULL, 0xF13C6D9A51BFA4ABULL, 0x2D52526735488A0EULL,
+    0xB63ACAE1CAA6B790ULL, 0x4FDB70C581B23F76ULL, 0xBC39A0BF12307F5CULL,
+    0xB941F54EB1E59BB8ULL, 0x6C5BFC11D45F9088ULL, 0x22E0B1EF4275BF7BULL,
+    0x91F9E6725B4758C0ULL, 0x5A8A9D306BCF67EDULL, 0x209E0C6497517ABDULL,
+    0x3BF4296D830E9A7CULL, 0x16C3D91134096FAAULL, 0xFAF7DF4561B2AA30ULL,
+    0xE00DF8F1D61957D4ULL, 0x5D2CEED4435E3B00ULL, 0x8CEEF608660DD0F2ULL,
+    0xFFBBD19C65195999ULL, 0x87A8E61DB4B6663CULL
+};
+
+static const BN_ULONG dh2048_256_g[] = {
+    0x664B4C0F6CC41659ULL, 0x5E2327CFEF98C582ULL, 0xD647D148D4795451ULL,
+    0x2F63078490F00EF8ULL, 0x184B523D1DB246C3ULL, 0xC7891428CDC67EB6ULL,
+    0x7FD028370DF92B52ULL, 0xB3353BBB64E0EC37ULL, 0xECD06E1557CD0915ULL,
+    0xB7D2BBD2DF016199ULL, 0xC8484B1E052588B9ULL, 0xDB2A3B7313D3FE14ULL,
+    0xD052B985D182EA0AULL, 0xA4BD1BFFE83B9C80ULL, 0xDFC967C1FB3F2E55ULL,
+    0xB5045AF2767164E1ULL, 0x1D14348F6F2F9193ULL, 0x64E67982428EBC83ULL,
+    0x8AC376D282D6ED38ULL, 0x777DE62AAAB8A862ULL, 0xDDF463E5E9EC144BULL,
+    0x0196F931C77A57F2ULL, 0xA55AE31341000A65ULL, 0x901228F8C28CBB18ULL,
+    0xBC3773BF7E8C6F62ULL, 0xBE3A6C1B0C6B47B1ULL, 0xFF4FED4AAC0BB555ULL,
+    0x10DBC15077BE463FULL, 0x07F4793A1A0BA125ULL, 0x4CA7B18F21EF2054ULL,
+    0x2E77506660EDBD48ULL, 0x3FB32C9B73134D0BULL
+};
+
+static const BN_ULONG dh2048_256_q[] = {
+    0xA308B0FE64F5FBD3ULL, 0x99B1A47D1EB3750BULL, 0xB447997640129DA2ULL,
+    0x8CF83642A709A097ULL
+};
+
+# elif BN_BITS2 == 32
+
+static const BN_ULONG dh1024_160_p[] = {
+    0x2E4A4371, 0xDF1FB2BC, 0x6D4DA708, 0xE68CFDA7, 0x365C1A65, 0x45BF37DF,
+    0x0DC8B4BD, 0xA151AF5F, 0xF55BCCC0, 0xFAA31A4F, 0xE5644738, 0x4EFFD6FA,
+    0x219A7372, 0x98488E9C, 0x90C4BD70, 0xACCBDD7D, 0xD49B83BF, 0x24975C3C,
+    0xA9061123, 0x13ECB4AE, 0x2EE652C0, 0x9838EF1E, 0x75A23D18, 0x6073E286,
+    0x52D23B61, 0x9A6A9DCA, 0xFB06A3C6, 0x52C99FBC, 0xAE5D54EC, 0xDE92DE5E,
+    0xA080E01D, 0xB10B8F96
+};
+
+static const BN_ULONG dh1024_160_g[] = {
+    0x22B3B2E5, 0x855E6EEB, 0xF97C2A24, 0x858F4DCE, 0x18D08BC8, 0x2D779D59,
+    0x8E73AFA3, 0xD662A4D1, 0x69B6A28A, 0x1DBF0A01, 0x7A091F53, 0xA6A24C08,
+    0x63F80A76, 0x909D0D22, 0xB9A92EE1, 0xD7FBD7D3, 0x9E2749F4, 0x5E91547F,
+    0xB01B886A, 0x160217B4, 0x5504F213, 0x777E690F, 0x5C41564B, 0x266FEA1E,
+    0x14266D31, 0xD6406CFF, 0x58AC507F, 0xF8104DD2, 0xEFB99905, 0x6765A442,
+    0xC3FD3412, 0xA4D1CBD5
+};
+
+static const BN_ULONG dh1024_160_q[] = {
+    0x49462353, 0x64B7CB9D, 0x8ABA4E7D, 0x81A8DF27, 0xF518AA87
+};
+
+static const BN_ULONG dh2048_224_p[] = {
+    0x0C10E64F, 0x0AC4DFFE, 0x4E71B81C, 0xCF9DE538, 0xFFA31F71, 0x7EF363E2,
+    0x6B8E75B9, 0xE3FB73C1, 0x4BA80A29, 0xC9B53DCF, 0x16E79763, 0x23F10B0E,
+    0x13042E9B, 0xC52172E4, 0xC928B2B9, 0xBE60E69C, 0xB9E587E8, 0x80CD86A1,
+    0x98C641A4, 0x315D75E1, 0x44328387, 0xCDF93ACC, 0xDC0A486D, 0x15987D9A,
+    0x1FD5A074, 0x7310F712, 0xDE31EFDC, 0x278273C7, 0x415D9330, 0x1602E714,
+    0xBC8985DB, 0x81286130, 0x70918836, 0xB3BF8A31, 0xB9C49708, 0x6A00E0A0,
+    0x8BBC27BE, 0xC6BA0B2C, 0xED34DBF6, 0xC9F98D11, 0xB6C12207, 0x7AD5B7D0,
+    0x55B7394B, 0xD91E8FEF, 0xEFDA4DF8, 0x9037C9ED, 0xAD6AC212, 0x6D3F8152,
+    0x1274A0A6, 0x1DE6B85A, 0x309C180E, 0xEB3D688A, 0x7BA1DF15, 0xAF9A3C40,
+    0xF95A56DB, 0xE6FA141D, 0xB61D0A75, 0xB54B1597, 0x683B9FD1, 0xA20D64E5,
+    0x9559C51F, 0xD660FAA7, 0x9123A9D0, 0xAD107E1E
+};
+
+static const BN_ULONG dh2048_224_g[] = {
+    0x191F2BFA, 0x84B890D3, 0x2A7065B3, 0x81BC087F, 0xF6EC0179, 0x19C418E1,
+    0x71CFFF4C, 0x7B5A0F1C, 0x9B6AA4BD, 0xEDFE72FE, 0x94B30269, 0x81E1BCFE,
+    0x8D6C0191, 0x566AFBB4, 0x409D13CD, 0xB539CCE3, 0x5F2FF381, 0x6AA21E7F,
+    0x770589EF, 0xD9E263E4, 0xD19963DD, 0x10E183ED, 0x150B8EEB, 0xB70A8137,
+    0x28C8F8AC, 0x051AE3D4, 0x0C1AB15B, 0xBB77A86F, 0x16A330EF, 0x6E3025E3,
+    0xD6F83456, 0x19529A45, 0x118E98D1, 0xF180EB34, 0x50717CBE, 0xB5F6C6B2,
+    0xDA7460CD, 0x09939D54, 0x22EA1ED4, 0xE2471504, 0x521BC98A, 0xB8A762D0,
+    0x5AC1348B, 0xF4D02727, 0x1999024A, 0xC1766910, 0xA8D66AD7, 0xBE5E9001,
+    0x620A8652, 0xC57DB17C, 0x00C29F52, 0xAB739D77, 0xA70C4AFA, 0xDD921F01,
+    0x10B9A6F0, 0xA6824A4E, 0xCFE4FFE3, 0x74866A08, 0x89998CAF, 0x6CDEBE7B,
+    0x8FFDAC50, 0x9DF30B5C, 0x4F2D9AE3, 0xAC4032EF
+};
+
+static const BN_ULONG dh2048_224_q[] = {
+    0xB36371EB, 0xBF389A99, 0x4738CEBC, 0x1F80535A, 0x99717710, 0xC58D93FE,
+    0x801C0D34
+};
+
+static const BN_ULONG dh2048_256_p[] = {
+    0x1E1A1597, 0xDB094AE9, 0xD7EF09CA, 0x693877FA, 0x6E11715F, 0x6116D227,
+    0xC198AF12, 0xA4B54330, 0xD7014103, 0x75F26375, 0x54E710C3, 0xC3A3960A,
+    0xBD0BE621, 0xDED4010A, 0x89962856, 0xC0B857F6, 0x71506026, 0xB3CA3F79,
+    0xE6B486F6, 0x1CCACB83, 0x14056425, 0x67E144E5, 0xA41825D9, 0xF6A167B5,
+    0x96524D8E, 0x3AD83477, 0x51BFA4AB, 0xF13C6D9A, 0x35488A0E, 0x2D525267,
+    0xCAA6B790, 0xB63ACAE1, 0x81B23F76, 0x4FDB70C5, 0x12307F5C, 0xBC39A0BF,
+    0xB1E59BB8, 0xB941F54E, 0xD45F9088, 0x6C5BFC11, 0x4275BF7B, 0x22E0B1EF,
+    0x5B4758C0, 0x91F9E672, 0x6BCF67ED, 0x5A8A9D30, 0x97517ABD, 0x209E0C64,
+    0x830E9A7C, 0x3BF4296D, 0x34096FAA, 0x16C3D911, 0x61B2AA30, 0xFAF7DF45,
+    0xD61957D4, 0xE00DF8F1, 0x435E3B00, 0x5D2CEED4, 0x660DD0F2, 0x8CEEF608,
+    0x65195999, 0xFFBBD19C, 0xB4B6663C, 0x87A8E61D
+};
+
+static const BN_ULONG dh2048_256_g[] = {
+    0x6CC41659, 0x664B4C0F, 0xEF98C582, 0x5E2327CF, 0xD4795451, 0xD647D148,
+    0x90F00EF8, 0x2F630784, 0x1DB246C3, 0x184B523D, 0xCDC67EB6, 0xC7891428,
+    0x0DF92B52, 0x7FD02837, 0x64E0EC37, 0xB3353BBB, 0x57CD0915, 0xECD06E15,
+    0xDF016199, 0xB7D2BBD2, 0x052588B9, 0xC8484B1E, 0x13D3FE14, 0xDB2A3B73,
+    0xD182EA0A, 0xD052B985, 0xE83B9C80, 0xA4BD1BFF, 0xFB3F2E55, 0xDFC967C1,
+    0x767164E1, 0xB5045AF2, 0x6F2F9193, 0x1D14348F, 0x428EBC83, 0x64E67982,
+    0x82D6ED38, 0x8AC376D2, 0xAAB8A862, 0x777DE62A, 0xE9EC144B, 0xDDF463E5,
+    0xC77A57F2, 0x0196F931, 0x41000A65, 0xA55AE313, 0xC28CBB18, 0x901228F8,
+    0x7E8C6F62, 0xBC3773BF, 0x0C6B47B1, 0xBE3A6C1B, 0xAC0BB555, 0xFF4FED4A,
+    0x77BE463F, 0x10DBC150, 0x1A0BA125, 0x07F4793A, 0x21EF2054, 0x4CA7B18F,
+    0x60EDBD48, 0x2E775066, 0x73134D0B, 0x3FB32C9B
+};
+
+static const BN_ULONG dh2048_256_q[] = {
+    0x64F5FBD3, 0xA308B0FE, 0x1EB3750B, 0x99B1A47D, 0x40129DA2, 0xB4479976,
+    0xA709A097, 0x8CF83642
+};
+
+# else
+#  error "unsupported BN_BITS2"
+# endif
+
+/* Macro to make a BIGNUM from static data */
+
+# define make_dh_bn(x) extern const BIGNUM _bignum_##x; \
+                       const BIGNUM _bignum_##x = { (BN_ULONG *) x, \
+                        OSSL_NELEM(x),\
+                        OSSL_NELEM(x),\
+                        0, BN_FLG_STATIC_DATA };
+
+
+make_dh_bn(dh1024_160_p)
+make_dh_bn(dh1024_160_g)
+make_dh_bn(dh1024_160_q)
+make_dh_bn(dh2048_224_p)
+make_dh_bn(dh2048_224_g)
+make_dh_bn(dh2048_224_q)
+make_dh_bn(dh2048_256_p)
+make_dh_bn(dh2048_256_g)
+make_dh_bn(dh2048_256_q)
+
+#endif
diff --git a/crypto/bn/bn_div.c b/crypto/bn/bn_div.c
new file mode 100644
index 000000000..99abf35c4
--- /dev/null
+++ b/crypto/bn/bn_div.c
@@ -0,0 +1,423 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include "bn_lcl.h"
+
+/* The old slow way */
+#if 0
+int BN_div(BIGNUM *dv, BIGNUM *rem, const BIGNUM *m, const BIGNUM *d,
+           BN_CTX *ctx)
+{
+    int i, nm, nd;
+    int ret = 0;
+    BIGNUM *D;
+
+    bn_check_top(m);
+    bn_check_top(d);
+    if (BN_is_zero(d)) {
+        BNerr(BN_F_BN_DIV, BN_R_DIV_BY_ZERO);
+        return (0);
+    }
+
+    if (BN_ucmp(m, d) < 0) {
+        if (rem != NULL) {
+            if (BN_copy(rem, m) == NULL)
+                return (0);
+        }
+        if (dv != NULL)
+            BN_zero(dv);
+        return (1);
+    }
+
+    BN_CTX_start(ctx);
+    D = BN_CTX_get(ctx);
+    if (dv == NULL)
+        dv = BN_CTX_get(ctx);
+    if (rem == NULL)
+        rem = BN_CTX_get(ctx);
+    if (D == NULL || dv == NULL || rem == NULL)
+        goto end;
+
+    nd = BN_num_bits(d);
+    nm = BN_num_bits(m);
+    if (BN_copy(D, d) == NULL)
+        goto end;
+    if (BN_copy(rem, m) == NULL)
+        goto end;
+
+    /*
+     * The next 2 are needed so we can do a dv->d[0]|=1 later since
+     * BN_lshift1 will only work once there is a value :-)
+     */
+    BN_zero(dv);
+    if (bn_wexpand(dv, 1) == NULL)
+        goto end;
+    dv->top = 1;
+
+    if (!BN_lshift(D, D, nm - nd))
+        goto end;
+    for (i = nm - nd; i >= 0; i--) {
+        if (!BN_lshift1(dv, dv))
+            goto end;
+        if (BN_ucmp(rem, D) >= 0) {
+            dv->d[0] |= 1;
+            if (!BN_usub(rem, rem, D))
+                goto end;
+        }
+/* CAN IMPROVE (and have now :=) */
+        if (!BN_rshift1(D, D))
+            goto end;
+    }
+    rem->neg = BN_is_zero(rem) ? 0 : m->neg;
+    dv->neg = m->neg ^ d->neg;
+    ret = 1;
+ end:
+    BN_CTX_end(ctx);
+    return (ret);
+}
+
+#else
+
+# if !defined(OPENSSL_NO_ASM) && !defined(OPENSSL_NO_INLINE_ASM) \
+    && !defined(PEDANTIC) && !defined(BN_DIV3W)
+#  if defined(__GNUC__) && __GNUC__>=2
+#   if defined(__i386) || defined (__i386__)
+   /*-
+    * There were two reasons for implementing this template:
+    * - GNU C generates a call to a function (__udivdi3 to be exact)
+    *   in reply to ((((BN_ULLONG)n0)<
+    */
+#    undef bn_div_words
+#    define bn_div_words(n0,n1,d0)                \
+        ({  asm volatile (                      \
+                "divl   %4"                     \
+                : "=a"(q), "=d"(rem)            \
+                : "a"(n1), "d"(n0), "r"(d0)     \
+                : "cc");                        \
+            q;                                  \
+        })
+#    define REMAINDER_IS_ALREADY_CALCULATED
+#   elif defined(__x86_64) && defined(SIXTY_FOUR_BIT_LONG)
+   /*
+    * Same story here, but it's 128-bit by 64-bit division. Wow!
+    *                                   
+    */
+#    undef bn_div_words
+#    define bn_div_words(n0,n1,d0)                \
+        ({  asm volatile (                      \
+                "divq   %4"                     \
+                : "=a"(q), "=d"(rem)            \
+                : "a"(n1), "d"(n0), "r"(d0)     \
+                : "cc");                        \
+            q;                                  \
+        })
+#    define REMAINDER_IS_ALREADY_CALCULATED
+#   endif                       /* __ */
+#  endif                        /* __GNUC__ */
+# endif                         /* OPENSSL_NO_ASM */
+
+/*-
+ * BN_div computes  dv := num / divisor, rounding towards
+ * zero, and sets up rm  such that  dv*divisor + rm = num  holds.
+ * Thus:
+ *     dv->neg == num->neg ^ divisor->neg  (unless the result is zero)
+ *     rm->neg == num->neg                 (unless the remainder is zero)
+ * If 'dv' or 'rm' is NULL, the respective value is not returned.
+ */
+int BN_div(BIGNUM *dv, BIGNUM *rm, const BIGNUM *num, const BIGNUM *divisor,
+           BN_CTX *ctx)
+{
+    int norm_shift, i, loop;
+    BIGNUM *tmp, wnum, *snum, *sdiv, *res;
+    BN_ULONG *resp, *wnump;
+    BN_ULONG d0, d1;
+    int num_n, div_n;
+    int no_branch = 0;
+
+    /*
+     * Invalid zero-padding would have particularly bad consequences so don't
+     * just rely on bn_check_top() here (bn_check_top() works only for
+     * BN_DEBUG builds)
+     */
+    if ((num->top > 0 && num->d[num->top - 1] == 0) ||
+        (divisor->top > 0 && divisor->d[divisor->top - 1] == 0)) {
+        BNerr(BN_F_BN_DIV, BN_R_NOT_INITIALIZED);
+        return 0;
+    }
+
+    bn_check_top(num);
+    bn_check_top(divisor);
+
+    if ((BN_get_flags(num, BN_FLG_CONSTTIME) != 0)
+        || (BN_get_flags(divisor, BN_FLG_CONSTTIME) != 0)) {
+        no_branch = 1;
+    }
+
+    bn_check_top(dv);
+    bn_check_top(rm);
+    /*- bn_check_top(num); *//*
+     * 'num' has been checked already
+     */
+    /*- bn_check_top(divisor); *//*
+     * 'divisor' has been checked already
+     */
+
+    if (BN_is_zero(divisor)) {
+        BNerr(BN_F_BN_DIV, BN_R_DIV_BY_ZERO);
+        return (0);
+    }
+
+    if (!no_branch && BN_ucmp(num, divisor) < 0) {
+        if (rm != NULL) {
+            if (BN_copy(rm, num) == NULL)
+                return (0);
+        }
+        if (dv != NULL)
+            BN_zero(dv);
+        return (1);
+    }
+
+    BN_CTX_start(ctx);
+    tmp = BN_CTX_get(ctx);
+    snum = BN_CTX_get(ctx);
+    sdiv = BN_CTX_get(ctx);
+    if (dv == NULL)
+        res = BN_CTX_get(ctx);
+    else
+        res = dv;
+    if (sdiv == NULL || res == NULL || tmp == NULL || snum == NULL)
+        goto err;
+
+    /* First we normalise the numbers */
+    norm_shift = BN_BITS2 - ((BN_num_bits(divisor)) % BN_BITS2);
+    if (!(BN_lshift(sdiv, divisor, norm_shift)))
+        goto err;
+    sdiv->neg = 0;
+    norm_shift += BN_BITS2;
+    if (!(BN_lshift(snum, num, norm_shift)))
+        goto err;
+    snum->neg = 0;
+
+    if (no_branch) {
+        /*
+         * Since we don't know whether snum is larger than sdiv, we pad snum
+         * with enough zeroes without changing its value.
+         */
+        if (snum->top <= sdiv->top + 1) {
+            if (bn_wexpand(snum, sdiv->top + 2) == NULL)
+                goto err;
+            for (i = snum->top; i < sdiv->top + 2; i++)
+                snum->d[i] = 0;
+            snum->top = sdiv->top + 2;
+        } else {
+            if (bn_wexpand(snum, snum->top + 1) == NULL)
+                goto err;
+            snum->d[snum->top] = 0;
+            snum->top++;
+        }
+    }
+
+    div_n = sdiv->top;
+    num_n = snum->top;
+    loop = num_n - div_n;
+    /*
+     * Lets setup a 'window' into snum This is the part that corresponds to
+     * the current 'area' being divided
+     */
+    wnum.neg = 0;
+    wnum.d = &(snum->d[loop]);
+    wnum.top = div_n;
+    /*
+     * only needed when BN_ucmp messes up the values between top and max
+     */
+    wnum.dmax = snum->dmax - loop; /* so we don't step out of bounds */
+
+    /* Get the top 2 words of sdiv */
+    /* div_n=sdiv->top; */
+    d0 = sdiv->d[div_n - 1];
+    d1 = (div_n == 1) ? 0 : sdiv->d[div_n - 2];
+
+    /* pointer to the 'top' of snum */
+    wnump = &(snum->d[num_n - 1]);
+
+    /* Setup to 'res' */
+    res->neg = (num->neg ^ divisor->neg);
+    if (!bn_wexpand(res, (loop + 1)))
+        goto err;
+    res->top = loop - no_branch;
+    resp = &(res->d[loop - 1]);
+
+    /* space for temp */
+    if (!bn_wexpand(tmp, (div_n + 1)))
+        goto err;
+
+    if (!no_branch) {
+        if (BN_ucmp(&wnum, sdiv) >= 0) {
+            /*
+             * If BN_DEBUG_RAND is defined BN_ucmp changes (via bn_pollute)
+             * the const bignum arguments => clean the values between top and
+             * max again
+             */
+            bn_clear_top2max(&wnum);
+            bn_sub_words(wnum.d, wnum.d, sdiv->d, div_n);
+            *resp = 1;
+        } else
+            res->top--;
+    }
+
+    /* Increase the resp pointer so that we never create an invalid pointer. */
+    resp++;
+
+    /*
+     * if res->top == 0 then clear the neg value otherwise decrease the resp
+     * pointer
+     */
+    if (res->top == 0)
+        res->neg = 0;
+    else
+        resp--;
+
+    for (i = 0; i < loop - 1; i++, wnump--) {
+        BN_ULONG q, l0;
+        /*
+         * the first part of the loop uses the top two words of snum and sdiv
+         * to calculate a BN_ULONG q such that | wnum - sdiv * q | < sdiv
+         */
+# if defined(BN_DIV3W) && !defined(OPENSSL_NO_ASM)
+        BN_ULONG bn_div_3_words(BN_ULONG *, BN_ULONG, BN_ULONG);
+        q = bn_div_3_words(wnump, d1, d0);
+# else
+        BN_ULONG n0, n1, rem = 0;
+
+        n0 = wnump[0];
+        n1 = wnump[-1];
+        if (n0 == d0)
+            q = BN_MASK2;
+        else {                  /* n0 < d0 */
+
+#  ifdef BN_LLONG
+            BN_ULLONG t2;
+
+#   if defined(BN_LLONG) && defined(BN_DIV2W) && !defined(bn_div_words)
+            q = (BN_ULONG)(((((BN_ULLONG) n0) << BN_BITS2) | n1) / d0);
+#   else
+            q = bn_div_words(n0, n1, d0);
+#   endif
+
+#   ifndef REMAINDER_IS_ALREADY_CALCULATED
+            /*
+             * rem doesn't have to be BN_ULLONG. The least we
+             * know it's less that d0, isn't it?
+             */
+            rem = (n1 - q * d0) & BN_MASK2;
+#   endif
+            t2 = (BN_ULLONG) d1 *q;
+
+            for (;;) {
+                if (t2 <= ((((BN_ULLONG) rem) << BN_BITS2) | wnump[-2]))
+                    break;
+                q--;
+                rem += d0;
+                if (rem < d0)
+                    break;      /* don't let rem overflow */
+                t2 -= d1;
+            }
+#  else                         /* !BN_LLONG */
+            BN_ULONG t2l, t2h;
+
+            q = bn_div_words(n0, n1, d0);
+#   ifndef REMAINDER_IS_ALREADY_CALCULATED
+            rem = (n1 - q * d0) & BN_MASK2;
+#   endif
+
+#   if defined(BN_UMULT_LOHI)
+            BN_UMULT_LOHI(t2l, t2h, d1, q);
+#   elif defined(BN_UMULT_HIGH)
+            t2l = d1 * q;
+            t2h = BN_UMULT_HIGH(d1, q);
+#   else
+            {
+                BN_ULONG ql, qh;
+                t2l = LBITS(d1);
+                t2h = HBITS(d1);
+                ql = LBITS(q);
+                qh = HBITS(q);
+                mul64(t2l, t2h, ql, qh); /* t2=(BN_ULLONG)d1*q; */
+            }
+#   endif
+
+            for (;;) {
+                if ((t2h < rem) || ((t2h == rem) && (t2l <= wnump[-2])))
+                    break;
+                q--;
+                rem += d0;
+                if (rem < d0)
+                    break;      /* don't let rem overflow */
+                if (t2l < d1)
+                    t2h--;
+                t2l -= d1;
+            }
+#  endif                        /* !BN_LLONG */
+        }
+# endif                         /* !BN_DIV3W */
+
+        l0 = bn_mul_words(tmp->d, sdiv->d, div_n, q);
+        tmp->d[div_n] = l0;
+        wnum.d--;
+        /*
+         * ingore top values of the bignums just sub the two BN_ULONG arrays
+         * with bn_sub_words
+         */
+        if (bn_sub_words(wnum.d, wnum.d, tmp->d, div_n + 1)) {
+            /*
+             * Note: As we have considered only the leading two BN_ULONGs in
+             * the calculation of q, sdiv * q might be greater than wnum (but
+             * then (q-1) * sdiv is less or equal than wnum)
+             */
+            q--;
+            if (bn_add_words(wnum.d, wnum.d, sdiv->d, div_n))
+                /*
+                 * we can't have an overflow here (assuming that q != 0, but
+                 * if q == 0 then tmp is zero anyway)
+                 */
+                (*wnump)++;
+        }
+        /* store part of the result */
+        resp--;
+        *resp = q;
+    }
+    bn_correct_top(snum);
+    if (rm != NULL) {
+        /*
+         * Keep a copy of the neg flag in num because if rm==num BN_rshift()
+         * will overwrite it.
+         */
+        int neg = num->neg;
+        BN_rshift(rm, snum, norm_shift);
+        if (!BN_is_zero(rm))
+            rm->neg = neg;
+        bn_check_top(rm);
+    }
+    if (no_branch)
+        bn_correct_top(res);
+    BN_CTX_end(ctx);
+    return (1);
+ err:
+    bn_check_top(rm);
+    BN_CTX_end(ctx);
+    return (0);
+}
+#endif
diff --git a/crypto/bn/bn_err.c b/crypto/bn/bn_err.c
new file mode 100644
index 000000000..d43e8710e
--- /dev/null
+++ b/crypto/bn/bn_err.c
@@ -0,0 +1,131 @@
+/*
+ * Generated by util/mkerr.pl DO NOT EDIT
+ * Copyright 1995-2017 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include 
+
+/* BEGIN ERROR CODES */
+#ifndef OPENSSL_NO_ERR
+
+# define ERR_FUNC(func) ERR_PACK(ERR_LIB_BN,func,0)
+# define ERR_REASON(reason) ERR_PACK(ERR_LIB_BN,0,reason)
+
+static ERR_STRING_DATA BN_str_functs[] = {
+    {ERR_FUNC(BN_F_BNRAND), "bnrand"},
+    {ERR_FUNC(BN_F_BN_BLINDING_CONVERT_EX), "BN_BLINDING_convert_ex"},
+    {ERR_FUNC(BN_F_BN_BLINDING_CREATE_PARAM), "BN_BLINDING_create_param"},
+    {ERR_FUNC(BN_F_BN_BLINDING_INVERT_EX), "BN_BLINDING_invert_ex"},
+    {ERR_FUNC(BN_F_BN_BLINDING_NEW), "BN_BLINDING_new"},
+    {ERR_FUNC(BN_F_BN_BLINDING_UPDATE), "BN_BLINDING_update"},
+    {ERR_FUNC(BN_F_BN_BN2DEC), "BN_bn2dec"},
+    {ERR_FUNC(BN_F_BN_BN2HEX), "BN_bn2hex"},
+    {ERR_FUNC(BN_F_BN_BN2SOLINAS), "BN_bn2solinas"},
+    {ERR_FUNC(BN_F_BN_COMPUTE_WNAF), "bn_compute_wNAF"},
+    {ERR_FUNC(BN_F_BN_CTX_GET), "BN_CTX_get"},
+    {ERR_FUNC(BN_F_BN_CTX_NEW), "BN_CTX_new"},
+    {ERR_FUNC(BN_F_BN_CTX_START), "BN_CTX_start"},
+    {ERR_FUNC(BN_F_BN_DIV), "BN_div"},
+    {ERR_FUNC(BN_F_BN_DIV_RECP), "BN_div_recp"},
+    {ERR_FUNC(BN_F_BN_EXP), "BN_exp"},
+    {ERR_FUNC(BN_F_BN_EXPAND_INTERNAL), "bn_expand_internal"},
+    {ERR_FUNC(BN_F_BN_GENCB_NEW), "BN_GENCB_new"},
+    {ERR_FUNC(BN_F_BN_GENERATE_DSA_NONCE), "BN_generate_dsa_nonce"},
+    {ERR_FUNC(BN_F_BN_GENERATE_PRIME_EX), "BN_generate_prime_ex"},
+    {ERR_FUNC(BN_F_BN_GF2M_MOD), "BN_GF2m_mod"},
+    {ERR_FUNC(BN_F_BN_GF2M_MOD_EXP), "BN_GF2m_mod_exp"},
+    {ERR_FUNC(BN_F_BN_GF2M_MOD_MUL), "BN_GF2m_mod_mul"},
+    {ERR_FUNC(BN_F_BN_GF2M_MOD_SOLVE_QUAD), "BN_GF2m_mod_solve_quad"},
+    {ERR_FUNC(BN_F_BN_GF2M_MOD_SOLVE_QUAD_ARR), "BN_GF2m_mod_solve_quad_arr"},
+    {ERR_FUNC(BN_F_BN_GF2M_MOD_SQR), "BN_GF2m_mod_sqr"},
+    {ERR_FUNC(BN_F_BN_GF2M_MOD_SQRT), "BN_GF2m_mod_sqrt"},
+    {ERR_FUNC(BN_F_BN_GFP2_ADD), "BN_GFP2_add"},
+    {ERR_FUNC(BN_F_BN_GFP2_CANONICAL), "BN_GFP2_canonical"},
+    {ERR_FUNC(BN_F_BN_GFP2_CMP), "BN_GFP2_cmp"},
+    {ERR_FUNC(BN_F_BN_GFP2_COPY), "BN_GFP2_copy"},
+    {ERR_FUNC(BN_F_BN_GFP2_DIV_BN), "BN_GFP2_div_bn"},
+    {ERR_FUNC(BN_F_BN_GFP2_EQU), "BN_GFP2_equ"},
+    {ERR_FUNC(BN_F_BN_GFP2_INV), "BN_GFP2_inv"},
+    {ERR_FUNC(BN_F_BN_GFP2_IS_ZERO), "BN_GFP2_is_zero"},
+    {ERR_FUNC(BN_F_BN_GFP2_MUL), "BN_GFP2_mul"},
+    {ERR_FUNC(BN_F_BN_GFP2_NEW), "BN_GFP2_new"},
+    {ERR_FUNC(BN_F_BN_GFP2_ONE), "BN_GFP2_one"},
+    {ERR_FUNC(BN_F_BN_GFP2_SET_BN), "BN_GFP2_set_bn"},
+    {ERR_FUNC(BN_F_BN_GFP2_SUB), "BN_GFP2_sub"},
+    {ERR_FUNC(BN_F_BN_GFP2_ZERO), "BN_GFP2_zero"},
+    {ERR_FUNC(BN_F_BN_HASH_TO_RANGE), "BN_hash_to_range"},
+    {ERR_FUNC(BN_F_BN_LSHIFT), "BN_lshift"},
+    {ERR_FUNC(BN_F_BN_MOD_EXP2_MONT), "BN_mod_exp2_mont"},
+    {ERR_FUNC(BN_F_BN_MOD_EXP_MONT), "BN_mod_exp_mont"},
+    {ERR_FUNC(BN_F_BN_MOD_EXP_MONT_CONSTTIME), "BN_mod_exp_mont_consttime"},
+    {ERR_FUNC(BN_F_BN_MOD_EXP_MONT_WORD), "BN_mod_exp_mont_word"},
+    {ERR_FUNC(BN_F_BN_MOD_EXP_RECP), "BN_mod_exp_recp"},
+    {ERR_FUNC(BN_F_BN_MOD_EXP_SIMPLE), "BN_mod_exp_simple"},
+    {ERR_FUNC(BN_F_BN_MOD_INVERSE), "BN_mod_inverse"},
+    {ERR_FUNC(BN_F_BN_MOD_INVERSE_NO_BRANCH), "BN_mod_inverse_no_branch"},
+    {ERR_FUNC(BN_F_BN_MOD_LSHIFT_QUICK), "BN_mod_lshift_quick"},
+    {ERR_FUNC(BN_F_BN_MOD_SQRT), "BN_mod_sqrt"},
+    {ERR_FUNC(BN_F_BN_MPI2BN), "BN_mpi2bn"},
+    {ERR_FUNC(BN_F_BN_NEW), "BN_new"},
+    {ERR_FUNC(BN_F_BN_RAND), "BN_rand"},
+    {ERR_FUNC(BN_F_BN_RAND_RANGE), "BN_rand_range"},
+    {ERR_FUNC(BN_F_BN_RSHIFT), "BN_rshift"},
+    {ERR_FUNC(BN_F_BN_SET_WORDS), "bn_set_words"},
+    {ERR_FUNC(BN_F_BN_SOLINAS2BN), "BN_solinas2bn"},
+    {ERR_FUNC(BN_F_BN_USUB), "BN_usub"},
+    {0, NULL}
+};
+
+static ERR_STRING_DATA BN_str_reasons[] = {
+    {ERR_REASON(BN_R_ARG2_LT_ARG3), "arg2 lt arg3"},
+    {ERR_REASON(BN_R_BAD_ENCODING), "bad encoding"},
+    {ERR_REASON(BN_R_BAD_RECIPROCAL), "bad reciprocal"},
+    {ERR_REASON(BN_R_BIGNUM_TOO_LONG), "bignum too long"},
+    {ERR_REASON(BN_R_BITS_TOO_SMALL), "bits too small"},
+    {ERR_REASON(BN_R_BUFFER_TOO_SMALL), "buffer too small"},
+    {ERR_REASON(BN_R_CALLED_WITH_EVEN_MODULUS), "called with even modulus"},
+    {ERR_REASON(BN_R_DIV_BY_ZERO), "div by zero"},
+    {ERR_REASON(BN_R_ENCODE_ERROR), "encode error"},
+    {ERR_REASON(BN_R_ENCODING_ERROR), "encoding error"},
+    {ERR_REASON(BN_R_EXPAND_ON_STATIC_BIGNUM_DATA),
+     "expand on static bignum data"},
+    {ERR_REASON(BN_R_INPUT_NOT_REDUCED), "input not reduced"},
+    {ERR_REASON(BN_R_INVALID_LENGTH), "invalid length"},
+    {ERR_REASON(BN_R_INVALID_RANGE), "invalid range"},
+    {ERR_REASON(BN_R_INVALID_SHIFT), "invalid shift"},
+    {ERR_REASON(BN_R_INVALID_SOLINAS), "invalid solinas"},
+    {ERR_REASON(BN_R_INVALID_SOLINAS_PARAMETERS),
+     "invalid solinas parameters"},
+    {ERR_REASON(BN_R_NEGATIVE_NUMBER), "negative number"},
+    {ERR_REASON(BN_R_NOT_A_SQUARE), "not a square"},
+    {ERR_REASON(BN_R_NOT_INITIALIZED), "not initialized"},
+    {ERR_REASON(BN_R_NO_INVERSE), "no inverse"},
+    {ERR_REASON(BN_R_NO_SOLUTION), "no solution"},
+    {ERR_REASON(BN_R_PRIVATE_KEY_TOO_LARGE), "private key too large"},
+    {ERR_REASON(BN_R_P_IS_NOT_PRIME), "p is not prime"},
+    {ERR_REASON(BN_R_TOO_MANY_ITERATIONS), "too many iterations"},
+    {ERR_REASON(BN_R_TOO_MANY_TEMPORARY_VARIABLES),
+     "too many temporary variables"},
+    {0, NULL}
+};
+
+#endif
+
+int ERR_load_BN_strings(void)
+{
+#ifndef OPENSSL_NO_ERR
+
+    if (ERR_func_error_string(BN_str_functs[0].error) == NULL) {
+        ERR_load_strings(0, BN_str_functs);
+        ERR_load_strings(0, BN_str_reasons);
+    }
+#endif
+    return 1;
+}
diff --git a/crypto/bn/bn_exp.c b/crypto/bn/bn_exp.c
new file mode 100644
index 000000000..feeb7649a
--- /dev/null
+++ b/crypto/bn/bn_exp.c
@@ -0,0 +1,1362 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "internal/cryptlib.h"
+#include "internal/constant_time_locl.h"
+#include "bn_lcl.h"
+
+#include 
+#ifdef _WIN32
+# include 
+# ifndef alloca
+#  define alloca _alloca
+# endif
+#elif defined(__GNUC__)
+# ifndef alloca
+#  define alloca(s) __builtin_alloca((s))
+# endif
+#elif defined(__sun)
+# include 
+#endif
+
+#include "rsaz_exp.h"
+
+#undef SPARC_T4_MONT
+#if defined(OPENSSL_BN_ASM_MONT) && (defined(__sparc__) || defined(__sparc))
+# include "sparc_arch.h"
+extern unsigned int OPENSSL_sparcv9cap_P[];
+# define SPARC_T4_MONT
+#endif
+
+/* maximum precomputation table size for *variable* sliding windows */
+#define TABLE_SIZE      32
+
+/* this one works - simple but works */
+int BN_exp(BIGNUM *r, const BIGNUM *a, const BIGNUM *p, BN_CTX *ctx)
+{
+    int i, bits, ret = 0;
+    BIGNUM *v, *rr;
+
+    if (BN_get_flags(p, BN_FLG_CONSTTIME) != 0) {
+        /* BN_FLG_CONSTTIME only supported by BN_mod_exp_mont() */
+        BNerr(BN_F_BN_EXP, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
+        return 0;
+    }
+
+    BN_CTX_start(ctx);
+    if ((r == a) || (r == p))
+        rr = BN_CTX_get(ctx);
+    else
+        rr = r;
+    v = BN_CTX_get(ctx);
+    if (rr == NULL || v == NULL)
+        goto err;
+
+    if (BN_copy(v, a) == NULL)
+        goto err;
+    bits = BN_num_bits(p);
+
+    if (BN_is_odd(p)) {
+        if (BN_copy(rr, a) == NULL)
+            goto err;
+    } else {
+        if (!BN_one(rr))
+            goto err;
+    }
+
+    for (i = 1; i < bits; i++) {
+        if (!BN_sqr(v, v, ctx))
+            goto err;
+        if (BN_is_bit_set(p, i)) {
+            if (!BN_mul(rr, rr, v, ctx))
+                goto err;
+        }
+    }
+    if (r != rr && BN_copy(r, rr) == NULL)
+        goto err;
+
+    ret = 1;
+ err:
+    BN_CTX_end(ctx);
+    bn_check_top(r);
+    return (ret);
+}
+
+int BN_mod_exp(BIGNUM *r, const BIGNUM *a, const BIGNUM *p, const BIGNUM *m,
+               BN_CTX *ctx)
+{
+    int ret;
+
+    bn_check_top(a);
+    bn_check_top(p);
+    bn_check_top(m);
+
+    /*-
+     * For even modulus  m = 2^k*m_odd, it might make sense to compute
+     * a^p mod m_odd  and  a^p mod 2^k  separately (with Montgomery
+     * exponentiation for the odd part), using appropriate exponent
+     * reductions, and combine the results using the CRT.
+     *
+     * For now, we use Montgomery only if the modulus is odd; otherwise,
+     * exponentiation using the reciprocal-based quick remaindering
+     * algorithm is used.
+     *
+     * (Timing obtained with expspeed.c [computations  a^p mod m
+     * where  a, p, m  are of the same length: 256, 512, 1024, 2048,
+     * 4096, 8192 bits], compared to the running time of the
+     * standard algorithm:
+     *
+     *   BN_mod_exp_mont   33 .. 40 %  [AMD K6-2, Linux, debug configuration]
+     *                     55 .. 77 %  [UltraSparc processor, but
+     *                                  debug-solaris-sparcv8-gcc conf.]
+     *
+     *   BN_mod_exp_recp   50 .. 70 %  [AMD K6-2, Linux, debug configuration]
+     *                     62 .. 118 % [UltraSparc, debug-solaris-sparcv8-gcc]
+     *
+     * On the Sparc, BN_mod_exp_recp was faster than BN_mod_exp_mont
+     * at 2048 and more bits, but at 512 and 1024 bits, it was
+     * slower even than the standard algorithm!
+     *
+     * "Real" timings [linux-elf, solaris-sparcv9-gcc configurations]
+     * should be obtained when the new Montgomery reduction code
+     * has been integrated into OpenSSL.)
+     */
+
+#define MONT_MUL_MOD
+#define MONT_EXP_WORD
+#define RECP_MUL_MOD
+
+#ifdef MONT_MUL_MOD
+    /*
+     * I have finally been able to take out this pre-condition of the top bit
+     * being set.  It was caused by an error in BN_div with negatives.  There
+     * was also another problem when for a^b%m a >= m.  eay 07-May-97
+     */
+    /* if ((m->d[m->top-1]&BN_TBIT) && BN_is_odd(m)) */
+
+    if (BN_is_odd(m)) {
+# ifdef MONT_EXP_WORD
+        if (a->top == 1 && !a->neg
+            && (BN_get_flags(p, BN_FLG_CONSTTIME) == 0)) {
+            BN_ULONG A = a->d[0];
+            ret = BN_mod_exp_mont_word(r, A, p, m, ctx, NULL);
+        } else
+# endif
+            ret = BN_mod_exp_mont(r, a, p, m, ctx, NULL);
+    } else
+#endif
+#ifdef RECP_MUL_MOD
+    {
+        ret = BN_mod_exp_recp(r, a, p, m, ctx);
+    }
+#else
+    {
+        ret = BN_mod_exp_simple(r, a, p, m, ctx);
+    }
+#endif
+
+    bn_check_top(r);
+    return (ret);
+}
+
+int BN_mod_exp_recp(BIGNUM *r, const BIGNUM *a, const BIGNUM *p,
+                    const BIGNUM *m, BN_CTX *ctx)
+{
+    int i, j, bits, ret = 0, wstart, wend, window, wvalue;
+    int start = 1;
+    BIGNUM *aa;
+    /* Table of variables obtained from 'ctx' */
+    BIGNUM *val[TABLE_SIZE];
+    BN_RECP_CTX recp;
+
+    if (BN_get_flags(p, BN_FLG_CONSTTIME) != 0) {
+        /* BN_FLG_CONSTTIME only supported by BN_mod_exp_mont() */
+        BNerr(BN_F_BN_MOD_EXP_RECP, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
+        return 0;
+    }
+
+    bits = BN_num_bits(p);
+    if (bits == 0) {
+        /* x**0 mod 1 is still zero. */
+        if (BN_is_one(m)) {
+            ret = 1;
+            BN_zero(r);
+        } else {
+            ret = BN_one(r);
+        }
+        return ret;
+    }
+
+    BN_CTX_start(ctx);
+    aa = BN_CTX_get(ctx);
+    val[0] = BN_CTX_get(ctx);
+    if (!aa || !val[0])
+        goto err;
+
+    BN_RECP_CTX_init(&recp);
+    if (m->neg) {
+        /* ignore sign of 'm' */
+        if (!BN_copy(aa, m))
+            goto err;
+        aa->neg = 0;
+        if (BN_RECP_CTX_set(&recp, aa, ctx) <= 0)
+            goto err;
+    } else {
+        if (BN_RECP_CTX_set(&recp, m, ctx) <= 0)
+            goto err;
+    }
+
+    if (!BN_nnmod(val[0], a, m, ctx))
+        goto err;               /* 1 */
+    if (BN_is_zero(val[0])) {
+        BN_zero(r);
+        ret = 1;
+        goto err;
+    }
+
+    window = BN_window_bits_for_exponent_size(bits);
+    if (window > 1) {
+        if (!BN_mod_mul_reciprocal(aa, val[0], val[0], &recp, ctx))
+            goto err;           /* 2 */
+        j = 1 << (window - 1);
+        for (i = 1; i < j; i++) {
+            if (((val[i] = BN_CTX_get(ctx)) == NULL) ||
+                !BN_mod_mul_reciprocal(val[i], val[i - 1], aa, &recp, ctx))
+                goto err;
+        }
+    }
+
+    start = 1;                  /* This is used to avoid multiplication etc
+                                 * when there is only the value '1' in the
+                                 * buffer. */
+    wvalue = 0;                 /* The 'value' of the window */
+    wstart = bits - 1;          /* The top bit of the window */
+    wend = 0;                   /* The bottom bit of the window */
+
+    if (!BN_one(r))
+        goto err;
+
+    for (;;) {
+        if (BN_is_bit_set(p, wstart) == 0) {
+            if (!start)
+                if (!BN_mod_mul_reciprocal(r, r, r, &recp, ctx))
+                    goto err;
+            if (wstart == 0)
+                break;
+            wstart--;
+            continue;
+        }
+        /*
+         * We now have wstart on a 'set' bit, we now need to work out how bit
+         * a window to do.  To do this we need to scan forward until the last
+         * set bit before the end of the window
+         */
+        j = wstart;
+        wvalue = 1;
+        wend = 0;
+        for (i = 1; i < window; i++) {
+            if (wstart - i < 0)
+                break;
+            if (BN_is_bit_set(p, wstart - i)) {
+                wvalue <<= (i - wend);
+                wvalue |= 1;
+                wend = i;
+            }
+        }
+
+        /* wend is the size of the current window */
+        j = wend + 1;
+        /* add the 'bytes above' */
+        if (!start)
+            for (i = 0; i < j; i++) {
+                if (!BN_mod_mul_reciprocal(r, r, r, &recp, ctx))
+                    goto err;
+            }
+
+        /* wvalue will be an odd number < 2^window */
+        if (!BN_mod_mul_reciprocal(r, r, val[wvalue >> 1], &recp, ctx))
+            goto err;
+
+        /* move the 'window' down further */
+        wstart -= wend + 1;
+        wvalue = 0;
+        start = 0;
+        if (wstart < 0)
+            break;
+    }
+    ret = 1;
+ err:
+    BN_CTX_end(ctx);
+    BN_RECP_CTX_free(&recp);
+    bn_check_top(r);
+    return (ret);
+}
+
+int BN_mod_exp_mont(BIGNUM *rr, const BIGNUM *a, const BIGNUM *p,
+                    const BIGNUM *m, BN_CTX *ctx, BN_MONT_CTX *in_mont)
+{
+    int i, j, bits, ret = 0, wstart, wend, window, wvalue;
+    int start = 1;
+    BIGNUM *d, *r;
+    const BIGNUM *aa;
+    /* Table of variables obtained from 'ctx' */
+    BIGNUM *val[TABLE_SIZE];
+    BN_MONT_CTX *mont = NULL;
+
+    if (BN_get_flags(p, BN_FLG_CONSTTIME) != 0) {
+        return BN_mod_exp_mont_consttime(rr, a, p, m, ctx, in_mont);
+    }
+
+    bn_check_top(a);
+    bn_check_top(p);
+    bn_check_top(m);
+
+    if (!BN_is_odd(m)) {
+        BNerr(BN_F_BN_MOD_EXP_MONT, BN_R_CALLED_WITH_EVEN_MODULUS);
+        return (0);
+    }
+    bits = BN_num_bits(p);
+    if (bits == 0) {
+        /* x**0 mod 1 is still zero. */
+        if (BN_is_one(m)) {
+            ret = 1;
+            BN_zero(rr);
+        } else {
+            ret = BN_one(rr);
+        }
+        return ret;
+    }
+
+    BN_CTX_start(ctx);
+    d = BN_CTX_get(ctx);
+    r = BN_CTX_get(ctx);
+    val[0] = BN_CTX_get(ctx);
+    if (!d || !r || !val[0])
+        goto err;
+
+    /*
+     * If this is not done, things will break in the montgomery part
+     */
+
+    if (in_mont != NULL)
+        mont = in_mont;
+    else {
+        if ((mont = BN_MONT_CTX_new()) == NULL)
+            goto err;
+        if (!BN_MONT_CTX_set(mont, m, ctx))
+            goto err;
+    }
+
+    if (a->neg || BN_ucmp(a, m) >= 0) {
+        if (!BN_nnmod(val[0], a, m, ctx))
+            goto err;
+        aa = val[0];
+    } else
+        aa = a;
+    if (BN_is_zero(aa)) {
+        BN_zero(rr);
+        ret = 1;
+        goto err;
+    }
+    if (!BN_to_montgomery(val[0], aa, mont, ctx))
+        goto err;               /* 1 */
+
+    window = BN_window_bits_for_exponent_size(bits);
+    if (window > 1) {
+        if (!BN_mod_mul_montgomery(d, val[0], val[0], mont, ctx))
+            goto err;           /* 2 */
+        j = 1 << (window - 1);
+        for (i = 1; i < j; i++) {
+            if (((val[i] = BN_CTX_get(ctx)) == NULL) ||
+                !BN_mod_mul_montgomery(val[i], val[i - 1], d, mont, ctx))
+                goto err;
+        }
+    }
+
+    start = 1;                  /* This is used to avoid multiplication etc
+                                 * when there is only the value '1' in the
+                                 * buffer. */
+    wvalue = 0;                 /* The 'value' of the window */
+    wstart = bits - 1;          /* The top bit of the window */
+    wend = 0;                   /* The bottom bit of the window */
+
+#if 1                           /* by Shay Gueron's suggestion */
+    j = m->top;                 /* borrow j */
+    if (m->d[j - 1] & (((BN_ULONG)1) << (BN_BITS2 - 1))) {
+        if (bn_wexpand(r, j) == NULL)
+            goto err;
+        /* 2^(top*BN_BITS2) - m */
+        r->d[0] = (0 - m->d[0]) & BN_MASK2;
+        for (i = 1; i < j; i++)
+            r->d[i] = (~m->d[i]) & BN_MASK2;
+        r->top = j;
+        /*
+         * Upper words will be zero if the corresponding words of 'm' were
+         * 0xfff[...], so decrement r->top accordingly.
+         */
+        bn_correct_top(r);
+    } else
+#endif
+    if (!BN_to_montgomery(r, BN_value_one(), mont, ctx))
+        goto err;
+    for (;;) {
+        if (BN_is_bit_set(p, wstart) == 0) {
+            if (!start) {
+                if (!BN_mod_mul_montgomery(r, r, r, mont, ctx))
+                    goto err;
+            }
+            if (wstart == 0)
+                break;
+            wstart--;
+            continue;
+        }
+        /*
+         * We now have wstart on a 'set' bit, we now need to work out how bit
+         * a window to do.  To do this we need to scan forward until the last
+         * set bit before the end of the window
+         */
+        j = wstart;
+        wvalue = 1;
+        wend = 0;
+        for (i = 1; i < window; i++) {
+            if (wstart - i < 0)
+                break;
+            if (BN_is_bit_set(p, wstart - i)) {
+                wvalue <<= (i - wend);
+                wvalue |= 1;
+                wend = i;
+            }
+        }
+
+        /* wend is the size of the current window */
+        j = wend + 1;
+        /* add the 'bytes above' */
+        if (!start)
+            for (i = 0; i < j; i++) {
+                if (!BN_mod_mul_montgomery(r, r, r, mont, ctx))
+                    goto err;
+            }
+
+        /* wvalue will be an odd number < 2^window */
+        if (!BN_mod_mul_montgomery(r, r, val[wvalue >> 1], mont, ctx))
+            goto err;
+
+        /* move the 'window' down further */
+        wstart -= wend + 1;
+        wvalue = 0;
+        start = 0;
+        if (wstart < 0)
+            break;
+    }
+#if defined(SPARC_T4_MONT)
+    if (OPENSSL_sparcv9cap_P[0] & (SPARCV9_VIS3 | SPARCV9_PREFER_FPU)) {
+        j = mont->N.top;        /* borrow j */
+        val[0]->d[0] = 1;       /* borrow val[0] */
+        for (i = 1; i < j; i++)
+            val[0]->d[i] = 0;
+        val[0]->top = j;
+        if (!BN_mod_mul_montgomery(rr, r, val[0], mont, ctx))
+            goto err;
+    } else
+#endif
+    if (!BN_from_montgomery(rr, r, mont, ctx))
+        goto err;
+    ret = 1;
+ err:
+    if (in_mont == NULL)
+        BN_MONT_CTX_free(mont);
+    BN_CTX_end(ctx);
+    bn_check_top(rr);
+    return (ret);
+}
+
+#if defined(SPARC_T4_MONT)
+static BN_ULONG bn_get_bits(const BIGNUM *a, int bitpos)
+{
+    BN_ULONG ret = 0;
+    int wordpos;
+
+    wordpos = bitpos / BN_BITS2;
+    bitpos %= BN_BITS2;
+    if (wordpos >= 0 && wordpos < a->top) {
+        ret = a->d[wordpos] & BN_MASK2;
+        if (bitpos) {
+            ret >>= bitpos;
+            if (++wordpos < a->top)
+                ret |= a->d[wordpos] << (BN_BITS2 - bitpos);
+        }
+    }
+
+    return ret & BN_MASK2;
+}
+#endif
+
+/*
+ * BN_mod_exp_mont_consttime() stores the precomputed powers in a specific
+ * layout so that accessing any of these table values shows the same access
+ * pattern as far as cache lines are concerned.  The following functions are
+ * used to transfer a BIGNUM from/to that table.
+ */
+
+static int MOD_EXP_CTIME_COPY_TO_PREBUF(const BIGNUM *b, int top,
+                                        unsigned char *buf, int idx,
+                                        int window)
+{
+    int i, j;
+    int width = 1 << window;
+    BN_ULONG *table = (BN_ULONG *)buf;
+
+    if (top > b->top)
+        top = b->top;           /* this works because 'buf' is explicitly
+                                 * zeroed */
+    for (i = 0, j = idx; i < top; i++, j += width) {
+        table[j] = b->d[i];
+    }
+
+    return 1;
+}
+
+static int MOD_EXP_CTIME_COPY_FROM_PREBUF(BIGNUM *b, int top,
+                                          unsigned char *buf, int idx,
+                                          int window)
+{
+    int i, j;
+    int width = 1 << window;
+    /*
+     * We declare table 'volatile' in order to discourage compiler
+     * from reordering loads from the table. Concern is that if
+     * reordered in specific manner loads might give away the
+     * information we are trying to conceal. Some would argue that
+     * compiler can reorder them anyway, but it can as well be
+     * argued that doing so would be violation of standard...
+     */
+    volatile BN_ULONG *table = (volatile BN_ULONG *)buf;
+
+    if (bn_wexpand(b, top) == NULL)
+        return 0;
+
+    if (window <= 3) {
+        for (i = 0; i < top; i++, table += width) {
+            BN_ULONG acc = 0;
+
+            for (j = 0; j < width; j++) {
+                acc |= table[j] &
+                       ((BN_ULONG)0 - (constant_time_eq_int(j,idx)&1));
+            }
+
+            b->d[i] = acc;
+        }
+    } else {
+        int xstride = 1 << (window - 2);
+        BN_ULONG y0, y1, y2, y3;
+
+        i = idx >> (window - 2);        /* equivalent of idx / xstride */
+        idx &= xstride - 1;             /* equivalent of idx % xstride */
+
+        y0 = (BN_ULONG)0 - (constant_time_eq_int(i,0)&1);
+        y1 = (BN_ULONG)0 - (constant_time_eq_int(i,1)&1);
+        y2 = (BN_ULONG)0 - (constant_time_eq_int(i,2)&1);
+        y3 = (BN_ULONG)0 - (constant_time_eq_int(i,3)&1);
+
+        for (i = 0; i < top; i++, table += width) {
+            BN_ULONG acc = 0;
+
+            for (j = 0; j < xstride; j++) {
+                acc |= ( (table[j + 0 * xstride] & y0) |
+                         (table[j + 1 * xstride] & y1) |
+                         (table[j + 2 * xstride] & y2) |
+                         (table[j + 3 * xstride] & y3) )
+                       & ((BN_ULONG)0 - (constant_time_eq_int(j,idx)&1));
+            }
+
+            b->d[i] = acc;
+        }
+    }
+
+    b->top = top;
+    bn_correct_top(b);
+    return 1;
+}
+
+/*
+ * Given a pointer value, compute the next address that is a cache line
+ * multiple.
+ */
+#define MOD_EXP_CTIME_ALIGN(x_) \
+        ((unsigned char*)(x_) + (MOD_EXP_CTIME_MIN_CACHE_LINE_WIDTH - (((size_t)(x_)) & (MOD_EXP_CTIME_MIN_CACHE_LINE_MASK))))
+
+/*
+ * This variant of BN_mod_exp_mont() uses fixed windows and the special
+ * precomputation memory layout to limit data-dependency to a minimum to
+ * protect secret exponents (cf. the hyper-threading timing attacks pointed
+ * out by Colin Percival,
+ * http://www.daemonology.net/hyperthreading-considered-harmful/)
+ */
+int BN_mod_exp_mont_consttime(BIGNUM *rr, const BIGNUM *a, const BIGNUM *p,
+                              const BIGNUM *m, BN_CTX *ctx,
+                              BN_MONT_CTX *in_mont)
+{
+    int i, bits, ret = 0, window, wvalue;
+    int top;
+    BN_MONT_CTX *mont = NULL;
+
+    int numPowers;
+    unsigned char *powerbufFree = NULL;
+    int powerbufLen = 0;
+    unsigned char *powerbuf = NULL;
+    BIGNUM tmp, am;
+#if defined(SPARC_T4_MONT)
+    unsigned int t4 = 0;
+#endif
+
+    bn_check_top(a);
+    bn_check_top(p);
+    bn_check_top(m);
+
+    if (!BN_is_odd(m)) {
+        BNerr(BN_F_BN_MOD_EXP_MONT_CONSTTIME, BN_R_CALLED_WITH_EVEN_MODULUS);
+        return (0);
+    }
+
+    top = m->top;
+
+    bits = BN_num_bits(p);
+    if (bits == 0) {
+        /* x**0 mod 1 is still zero. */
+        if (BN_is_one(m)) {
+            ret = 1;
+            BN_zero(rr);
+        } else {
+            ret = BN_one(rr);
+        }
+        return ret;
+    }
+
+    BN_CTX_start(ctx);
+
+    /*
+     * Allocate a montgomery context if it was not supplied by the caller. If
+     * this is not done, things will break in the montgomery part.
+     */
+    if (in_mont != NULL)
+        mont = in_mont;
+    else {
+        if ((mont = BN_MONT_CTX_new()) == NULL)
+            goto err;
+        if (!BN_MONT_CTX_set(mont, m, ctx))
+            goto err;
+    }
+
+#ifdef RSAZ_ENABLED
+    /*
+     * If the size of the operands allow it, perform the optimized
+     * RSAZ exponentiation. For further information see
+     * crypto/bn/rsaz_exp.c and accompanying assembly modules.
+     */
+    if ((16 == a->top) && (16 == p->top) && (BN_num_bits(m) == 1024)
+        && rsaz_avx2_eligible()) {
+        if (NULL == bn_wexpand(rr, 16))
+            goto err;
+        RSAZ_1024_mod_exp_avx2(rr->d, a->d, p->d, m->d, mont->RR.d,
+                               mont->n0[0]);
+        rr->top = 16;
+        rr->neg = 0;
+        bn_correct_top(rr);
+        ret = 1;
+        goto err;
+    } else if ((8 == a->top) && (8 == p->top) && (BN_num_bits(m) == 512)) {
+        if (NULL == bn_wexpand(rr, 8))
+            goto err;
+        RSAZ_512_mod_exp(rr->d, a->d, p->d, m->d, mont->n0[0], mont->RR.d);
+        rr->top = 8;
+        rr->neg = 0;
+        bn_correct_top(rr);
+        ret = 1;
+        goto err;
+    }
+#endif
+
+    /* Get the window size to use with size of p. */
+    window = BN_window_bits_for_ctime_exponent_size(bits);
+#if defined(SPARC_T4_MONT)
+    if (window >= 5 && (top & 15) == 0 && top <= 64 &&
+        (OPENSSL_sparcv9cap_P[1] & (CFR_MONTMUL | CFR_MONTSQR)) ==
+        (CFR_MONTMUL | CFR_MONTSQR) && (t4 = OPENSSL_sparcv9cap_P[0]))
+        window = 5;
+    else
+#endif
+#if defined(OPENSSL_BN_ASM_MONT5)
+    if (window >= 5) {
+        window = 5;             /* ~5% improvement for RSA2048 sign, and even
+                                 * for RSA4096 */
+        /* reserve space for mont->N.d[] copy */
+        powerbufLen += top * sizeof(mont->N.d[0]);
+    }
+#endif
+    (void)0;
+
+    /*
+     * Allocate a buffer large enough to hold all of the pre-computed powers
+     * of am, am itself and tmp.
+     */
+    numPowers = 1 << window;
+    powerbufLen += sizeof(m->d[0]) * (top * numPowers +
+                                      ((2 * top) >
+                                       numPowers ? (2 * top) : numPowers));
+#ifdef alloca
+    if (powerbufLen < 3072)
+        powerbufFree =
+            alloca(powerbufLen + MOD_EXP_CTIME_MIN_CACHE_LINE_WIDTH);
+    else
+#endif
+        if ((powerbufFree =
+             OPENSSL_malloc(powerbufLen + MOD_EXP_CTIME_MIN_CACHE_LINE_WIDTH))
+            == NULL)
+        goto err;
+
+    powerbuf = MOD_EXP_CTIME_ALIGN(powerbufFree);
+    memset(powerbuf, 0, powerbufLen);
+
+#ifdef alloca
+    if (powerbufLen < 3072)
+        powerbufFree = NULL;
+#endif
+
+    /* lay down tmp and am right after powers table */
+    tmp.d = (BN_ULONG *)(powerbuf + sizeof(m->d[0]) * top * numPowers);
+    am.d = tmp.d + top;
+    tmp.top = am.top = 0;
+    tmp.dmax = am.dmax = top;
+    tmp.neg = am.neg = 0;
+    tmp.flags = am.flags = BN_FLG_STATIC_DATA;
+
+    /* prepare a^0 in Montgomery domain */
+#if 1                           /* by Shay Gueron's suggestion */
+    if (m->d[top - 1] & (((BN_ULONG)1) << (BN_BITS2 - 1))) {
+        /* 2^(top*BN_BITS2) - m */
+        tmp.d[0] = (0 - m->d[0]) & BN_MASK2;
+        for (i = 1; i < top; i++)
+            tmp.d[i] = (~m->d[i]) & BN_MASK2;
+        tmp.top = top;
+    } else
+#endif
+    if (!BN_to_montgomery(&tmp, BN_value_one(), mont, ctx))
+        goto err;
+
+    /* prepare a^1 in Montgomery domain */
+    if (a->neg || BN_ucmp(a, m) >= 0) {
+        if (!BN_mod(&am, a, m, ctx))
+            goto err;
+        if (!BN_to_montgomery(&am, &am, mont, ctx))
+            goto err;
+    } else if (!BN_to_montgomery(&am, a, mont, ctx))
+        goto err;
+
+#if defined(SPARC_T4_MONT)
+    if (t4) {
+        typedef int (*bn_pwr5_mont_f) (BN_ULONG *tp, const BN_ULONG *np,
+                                       const BN_ULONG *n0, const void *table,
+                                       int power, int bits);
+        int bn_pwr5_mont_t4_8(BN_ULONG *tp, const BN_ULONG *np,
+                              const BN_ULONG *n0, const void *table,
+                              int power, int bits);
+        int bn_pwr5_mont_t4_16(BN_ULONG *tp, const BN_ULONG *np,
+                               const BN_ULONG *n0, const void *table,
+                               int power, int bits);
+        int bn_pwr5_mont_t4_24(BN_ULONG *tp, const BN_ULONG *np,
+                               const BN_ULONG *n0, const void *table,
+                               int power, int bits);
+        int bn_pwr5_mont_t4_32(BN_ULONG *tp, const BN_ULONG *np,
+                               const BN_ULONG *n0, const void *table,
+                               int power, int bits);
+        static const bn_pwr5_mont_f pwr5_funcs[4] = {
+            bn_pwr5_mont_t4_8, bn_pwr5_mont_t4_16,
+            bn_pwr5_mont_t4_24, bn_pwr5_mont_t4_32
+        };
+        bn_pwr5_mont_f pwr5_worker = pwr5_funcs[top / 16 - 1];
+
+        typedef int (*bn_mul_mont_f) (BN_ULONG *rp, const BN_ULONG *ap,
+                                      const void *bp, const BN_ULONG *np,
+                                      const BN_ULONG *n0);
+        int bn_mul_mont_t4_8(BN_ULONG *rp, const BN_ULONG *ap, const void *bp,
+                             const BN_ULONG *np, const BN_ULONG *n0);
+        int bn_mul_mont_t4_16(BN_ULONG *rp, const BN_ULONG *ap,
+                              const void *bp, const BN_ULONG *np,
+                              const BN_ULONG *n0);
+        int bn_mul_mont_t4_24(BN_ULONG *rp, const BN_ULONG *ap,
+                              const void *bp, const BN_ULONG *np,
+                              const BN_ULONG *n0);
+        int bn_mul_mont_t4_32(BN_ULONG *rp, const BN_ULONG *ap,
+                              const void *bp, const BN_ULONG *np,
+                              const BN_ULONG *n0);
+        static const bn_mul_mont_f mul_funcs[4] = {
+            bn_mul_mont_t4_8, bn_mul_mont_t4_16,
+            bn_mul_mont_t4_24, bn_mul_mont_t4_32
+        };
+        bn_mul_mont_f mul_worker = mul_funcs[top / 16 - 1];
+
+        void bn_mul_mont_vis3(BN_ULONG *rp, const BN_ULONG *ap,
+                              const void *bp, const BN_ULONG *np,
+                              const BN_ULONG *n0, int num);
+        void bn_mul_mont_t4(BN_ULONG *rp, const BN_ULONG *ap,
+                            const void *bp, const BN_ULONG *np,
+                            const BN_ULONG *n0, int num);
+        void bn_mul_mont_gather5_t4(BN_ULONG *rp, const BN_ULONG *ap,
+                                    const void *table, const BN_ULONG *np,
+                                    const BN_ULONG *n0, int num, int power);
+        void bn_flip_n_scatter5_t4(const BN_ULONG *inp, size_t num,
+                                   void *table, size_t power);
+        void bn_gather5_t4(BN_ULONG *out, size_t num,
+                           void *table, size_t power);
+        void bn_flip_t4(BN_ULONG *dst, BN_ULONG *src, size_t num);
+
+        BN_ULONG *np = mont->N.d, *n0 = mont->n0;
+        int stride = 5 * (6 - (top / 16 - 1)); /* multiple of 5, but less
+                                                * than 32 */
+
+        /*
+         * BN_to_montgomery can contaminate words above .top [in
+         * BN_DEBUG[_DEBUG] build]...
+         */
+        for (i = am.top; i < top; i++)
+            am.d[i] = 0;
+        for (i = tmp.top; i < top; i++)
+            tmp.d[i] = 0;
+
+        bn_flip_n_scatter5_t4(tmp.d, top, powerbuf, 0);
+        bn_flip_n_scatter5_t4(am.d, top, powerbuf, 1);
+        if (!(*mul_worker) (tmp.d, am.d, am.d, np, n0) &&
+            !(*mul_worker) (tmp.d, am.d, am.d, np, n0))
+            bn_mul_mont_vis3(tmp.d, am.d, am.d, np, n0, top);
+        bn_flip_n_scatter5_t4(tmp.d, top, powerbuf, 2);
+
+        for (i = 3; i < 32; i++) {
+            /* Calculate a^i = a^(i-1) * a */
+            if (!(*mul_worker) (tmp.d, tmp.d, am.d, np, n0) &&
+                !(*mul_worker) (tmp.d, tmp.d, am.d, np, n0))
+                bn_mul_mont_vis3(tmp.d, tmp.d, am.d, np, n0, top);
+            bn_flip_n_scatter5_t4(tmp.d, top, powerbuf, i);
+        }
+
+        /* switch to 64-bit domain */
+        np = alloca(top * sizeof(BN_ULONG));
+        top /= 2;
+        bn_flip_t4(np, mont->N.d, top);
+
+        bits--;
+        for (wvalue = 0, i = bits % 5; i >= 0; i--, bits--)
+            wvalue = (wvalue << 1) + BN_is_bit_set(p, bits);
+        bn_gather5_t4(tmp.d, top, powerbuf, wvalue);
+
+        /*
+         * Scan the exponent one window at a time starting from the most
+         * significant bits.
+         */
+        while (bits >= 0) {
+            if (bits < stride)
+                stride = bits + 1;
+            bits -= stride;
+            wvalue = bn_get_bits(p, bits + 1);
+
+            if ((*pwr5_worker) (tmp.d, np, n0, powerbuf, wvalue, stride))
+                continue;
+            /* retry once and fall back */
+            if ((*pwr5_worker) (tmp.d, np, n0, powerbuf, wvalue, stride))
+                continue;
+
+            bits += stride - 5;
+            wvalue >>= stride - 5;
+            wvalue &= 31;
+            bn_mul_mont_t4(tmp.d, tmp.d, tmp.d, np, n0, top);
+            bn_mul_mont_t4(tmp.d, tmp.d, tmp.d, np, n0, top);
+            bn_mul_mont_t4(tmp.d, tmp.d, tmp.d, np, n0, top);
+            bn_mul_mont_t4(tmp.d, tmp.d, tmp.d, np, n0, top);
+            bn_mul_mont_t4(tmp.d, tmp.d, tmp.d, np, n0, top);
+            bn_mul_mont_gather5_t4(tmp.d, tmp.d, powerbuf, np, n0, top,
+                                   wvalue);
+        }
+
+        bn_flip_t4(tmp.d, tmp.d, top);
+        top *= 2;
+        /* back to 32-bit domain */
+        tmp.top = top;
+        bn_correct_top(&tmp);
+        OPENSSL_cleanse(np, top * sizeof(BN_ULONG));
+    } else
+#endif
+#if defined(OPENSSL_BN_ASM_MONT5)
+    if (window == 5 && top > 1) {
+        /*
+         * This optimization uses ideas from http://eprint.iacr.org/2011/239,
+         * specifically optimization of cache-timing attack countermeasures
+         * and pre-computation optimization.
+         */
+
+        /*
+         * Dedicated window==4 case improves 512-bit RSA sign by ~15%, but as
+         * 512-bit RSA is hardly relevant, we omit it to spare size...
+         */
+        void bn_mul_mont_gather5(BN_ULONG *rp, const BN_ULONG *ap,
+                                 const void *table, const BN_ULONG *np,
+                                 const BN_ULONG *n0, int num, int power);
+        void bn_scatter5(const BN_ULONG *inp, size_t num,
+                         void *table, size_t power);
+        void bn_gather5(BN_ULONG *out, size_t num, void *table, size_t power);
+        void bn_power5(BN_ULONG *rp, const BN_ULONG *ap,
+                       const void *table, const BN_ULONG *np,
+                       const BN_ULONG *n0, int num, int power);
+        int bn_get_bits5(const BN_ULONG *ap, int off);
+        int bn_from_montgomery(BN_ULONG *rp, const BN_ULONG *ap,
+                               const BN_ULONG *not_used, const BN_ULONG *np,
+                               const BN_ULONG *n0, int num);
+
+        BN_ULONG *n0 = mont->n0, *np;
+
+        /*
+         * BN_to_montgomery can contaminate words above .top [in
+         * BN_DEBUG[_DEBUG] build]...
+         */
+        for (i = am.top; i < top; i++)
+            am.d[i] = 0;
+        for (i = tmp.top; i < top; i++)
+            tmp.d[i] = 0;
+
+        /*
+         * copy mont->N.d[] to improve cache locality
+         */
+        for (np = am.d + top, i = 0; i < top; i++)
+            np[i] = mont->N.d[i];
+
+        bn_scatter5(tmp.d, top, powerbuf, 0);
+        bn_scatter5(am.d, am.top, powerbuf, 1);
+        bn_mul_mont(tmp.d, am.d, am.d, np, n0, top);
+        bn_scatter5(tmp.d, top, powerbuf, 2);
+
+# if 0
+        for (i = 3; i < 32; i++) {
+            /* Calculate a^i = a^(i-1) * a */
+            bn_mul_mont_gather5(tmp.d, am.d, powerbuf, np, n0, top, i - 1);
+            bn_scatter5(tmp.d, top, powerbuf, i);
+        }
+# else
+        /* same as above, but uses squaring for 1/2 of operations */
+        for (i = 4; i < 32; i *= 2) {
+            bn_mul_mont(tmp.d, tmp.d, tmp.d, np, n0, top);
+            bn_scatter5(tmp.d, top, powerbuf, i);
+        }
+        for (i = 3; i < 8; i += 2) {
+            int j;
+            bn_mul_mont_gather5(tmp.d, am.d, powerbuf, np, n0, top, i - 1);
+            bn_scatter5(tmp.d, top, powerbuf, i);
+            for (j = 2 * i; j < 32; j *= 2) {
+                bn_mul_mont(tmp.d, tmp.d, tmp.d, np, n0, top);
+                bn_scatter5(tmp.d, top, powerbuf, j);
+            }
+        }
+        for (; i < 16; i += 2) {
+            bn_mul_mont_gather5(tmp.d, am.d, powerbuf, np, n0, top, i - 1);
+            bn_scatter5(tmp.d, top, powerbuf, i);
+            bn_mul_mont(tmp.d, tmp.d, tmp.d, np, n0, top);
+            bn_scatter5(tmp.d, top, powerbuf, 2 * i);
+        }
+        for (; i < 32; i += 2) {
+            bn_mul_mont_gather5(tmp.d, am.d, powerbuf, np, n0, top, i - 1);
+            bn_scatter5(tmp.d, top, powerbuf, i);
+        }
+# endif
+        bits--;
+        for (wvalue = 0, i = bits % 5; i >= 0; i--, bits--)
+            wvalue = (wvalue << 1) + BN_is_bit_set(p, bits);
+        bn_gather5(tmp.d, top, powerbuf, wvalue);
+
+        /*
+         * Scan the exponent one window at a time starting from the most
+         * significant bits.
+         */
+        if (top & 7)
+            while (bits >= 0) {
+                for (wvalue = 0, i = 0; i < 5; i++, bits--)
+                    wvalue = (wvalue << 1) + BN_is_bit_set(p, bits);
+
+                bn_mul_mont(tmp.d, tmp.d, tmp.d, np, n0, top);
+                bn_mul_mont(tmp.d, tmp.d, tmp.d, np, n0, top);
+                bn_mul_mont(tmp.d, tmp.d, tmp.d, np, n0, top);
+                bn_mul_mont(tmp.d, tmp.d, tmp.d, np, n0, top);
+                bn_mul_mont(tmp.d, tmp.d, tmp.d, np, n0, top);
+                bn_mul_mont_gather5(tmp.d, tmp.d, powerbuf, np, n0, top,
+                                    wvalue);
+        } else {
+            while (bits >= 0) {
+                wvalue = bn_get_bits5(p->d, bits - 4);
+                bits -= 5;
+                bn_power5(tmp.d, tmp.d, powerbuf, np, n0, top, wvalue);
+            }
+        }
+
+        ret = bn_from_montgomery(tmp.d, tmp.d, NULL, np, n0, top);
+        tmp.top = top;
+        bn_correct_top(&tmp);
+        if (ret) {
+            if (!BN_copy(rr, &tmp))
+                ret = 0;
+            goto err;           /* non-zero ret means it's not error */
+        }
+    } else
+#endif
+    {
+        if (!MOD_EXP_CTIME_COPY_TO_PREBUF(&tmp, top, powerbuf, 0, window))
+            goto err;
+        if (!MOD_EXP_CTIME_COPY_TO_PREBUF(&am, top, powerbuf, 1, window))
+            goto err;
+
+        /*
+         * If the window size is greater than 1, then calculate
+         * val[i=2..2^winsize-1]. Powers are computed as a*a^(i-1) (even
+         * powers could instead be computed as (a^(i/2))^2 to use the slight
+         * performance advantage of sqr over mul).
+         */
+        if (window > 1) {
+            if (!BN_mod_mul_montgomery(&tmp, &am, &am, mont, ctx))
+                goto err;
+            if (!MOD_EXP_CTIME_COPY_TO_PREBUF(&tmp, top, powerbuf, 2,
+                                              window))
+                goto err;
+            for (i = 3; i < numPowers; i++) {
+                /* Calculate a^i = a^(i-1) * a */
+                if (!BN_mod_mul_montgomery(&tmp, &am, &tmp, mont, ctx))
+                    goto err;
+                if (!MOD_EXP_CTIME_COPY_TO_PREBUF(&tmp, top, powerbuf, i,
+                                                  window))
+                    goto err;
+            }
+        }
+
+        bits--;
+        for (wvalue = 0, i = bits % window; i >= 0; i--, bits--)
+            wvalue = (wvalue << 1) + BN_is_bit_set(p, bits);
+        if (!MOD_EXP_CTIME_COPY_FROM_PREBUF(&tmp, top, powerbuf, wvalue,
+                                            window))
+            goto err;
+
+        /*
+         * Scan the exponent one window at a time starting from the most
+         * significant bits.
+         */
+        while (bits >= 0) {
+            wvalue = 0;         /* The 'value' of the window */
+
+            /* Scan the window, squaring the result as we go */
+            for (i = 0; i < window; i++, bits--) {
+                if (!BN_mod_mul_montgomery(&tmp, &tmp, &tmp, mont, ctx))
+                    goto err;
+                wvalue = (wvalue << 1) + BN_is_bit_set(p, bits);
+            }
+
+            /*
+             * Fetch the appropriate pre-computed value from the pre-buf
+             */
+            if (!MOD_EXP_CTIME_COPY_FROM_PREBUF(&am, top, powerbuf, wvalue,
+                                                window))
+                goto err;
+
+            /* Multiply the result into the intermediate result */
+            if (!BN_mod_mul_montgomery(&tmp, &tmp, &am, mont, ctx))
+                goto err;
+        }
+    }
+
+    /* Convert the final result from montgomery to standard format */
+#if defined(SPARC_T4_MONT)
+    if (OPENSSL_sparcv9cap_P[0] & (SPARCV9_VIS3 | SPARCV9_PREFER_FPU)) {
+        am.d[0] = 1;            /* borrow am */
+        for (i = 1; i < top; i++)
+            am.d[i] = 0;
+        if (!BN_mod_mul_montgomery(rr, &tmp, &am, mont, ctx))
+            goto err;
+    } else
+#endif
+    if (!BN_from_montgomery(rr, &tmp, mont, ctx))
+        goto err;
+    ret = 1;
+ err:
+    if (in_mont == NULL)
+        BN_MONT_CTX_free(mont);
+    if (powerbuf != NULL) {
+        OPENSSL_cleanse(powerbuf, powerbufLen);
+        OPENSSL_free(powerbufFree);
+    }
+    BN_CTX_end(ctx);
+    return (ret);
+}
+
+int BN_mod_exp_mont_word(BIGNUM *rr, BN_ULONG a, const BIGNUM *p,
+                         const BIGNUM *m, BN_CTX *ctx, BN_MONT_CTX *in_mont)
+{
+    BN_MONT_CTX *mont = NULL;
+    int b, bits, ret = 0;
+    int r_is_one;
+    BN_ULONG w, next_w;
+    BIGNUM *d, *r, *t;
+    BIGNUM *swap_tmp;
+#define BN_MOD_MUL_WORD(r, w, m) \
+                (BN_mul_word(r, (w)) && \
+                (/* BN_ucmp(r, (m)) < 0 ? 1 :*/  \
+                        (BN_mod(t, r, m, ctx) && (swap_tmp = r, r = t, t = swap_tmp, 1))))
+    /*
+     * BN_MOD_MUL_WORD is only used with 'w' large, so the BN_ucmp test is
+     * probably more overhead than always using BN_mod (which uses BN_copy if
+     * a similar test returns true).
+     */
+    /*
+     * We can use BN_mod and do not need BN_nnmod because our accumulator is
+     * never negative (the result of BN_mod does not depend on the sign of
+     * the modulus).
+     */
+#define BN_TO_MONTGOMERY_WORD(r, w, mont) \
+                (BN_set_word(r, (w)) && BN_to_montgomery(r, r, (mont), ctx))
+
+    if (BN_get_flags(p, BN_FLG_CONSTTIME) != 0) {
+        /* BN_FLG_CONSTTIME only supported by BN_mod_exp_mont() */
+        BNerr(BN_F_BN_MOD_EXP_MONT_WORD, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
+        return 0;
+    }
+
+    bn_check_top(p);
+    bn_check_top(m);
+
+    if (!BN_is_odd(m)) {
+        BNerr(BN_F_BN_MOD_EXP_MONT_WORD, BN_R_CALLED_WITH_EVEN_MODULUS);
+        return (0);
+    }
+    if (m->top == 1)
+        a %= m->d[0];           /* make sure that 'a' is reduced */
+
+    bits = BN_num_bits(p);
+    if (bits == 0) {
+        /* x**0 mod 1 is still zero. */
+        if (BN_is_one(m)) {
+            ret = 1;
+            BN_zero(rr);
+        } else {
+            ret = BN_one(rr);
+        }
+        return ret;
+    }
+    if (a == 0) {
+        BN_zero(rr);
+        ret = 1;
+        return ret;
+    }
+
+    BN_CTX_start(ctx);
+    d = BN_CTX_get(ctx);
+    r = BN_CTX_get(ctx);
+    t = BN_CTX_get(ctx);
+    if (d == NULL || r == NULL || t == NULL)
+        goto err;
+
+    if (in_mont != NULL)
+        mont = in_mont;
+    else {
+        if ((mont = BN_MONT_CTX_new()) == NULL)
+            goto err;
+        if (!BN_MONT_CTX_set(mont, m, ctx))
+            goto err;
+    }
+
+    r_is_one = 1;               /* except for Montgomery factor */
+
+    /* bits-1 >= 0 */
+
+    /* The result is accumulated in the product r*w. */
+    w = a;                      /* bit 'bits-1' of 'p' is always set */
+    for (b = bits - 2; b >= 0; b--) {
+        /* First, square r*w. */
+        next_w = w * w;
+        if ((next_w / w) != w) { /* overflow */
+            if (r_is_one) {
+                if (!BN_TO_MONTGOMERY_WORD(r, w, mont))
+                    goto err;
+                r_is_one = 0;
+            } else {
+                if (!BN_MOD_MUL_WORD(r, w, m))
+                    goto err;
+            }
+            next_w = 1;
+        }
+        w = next_w;
+        if (!r_is_one) {
+            if (!BN_mod_mul_montgomery(r, r, r, mont, ctx))
+                goto err;
+        }
+
+        /* Second, multiply r*w by 'a' if exponent bit is set. */
+        if (BN_is_bit_set(p, b)) {
+            next_w = w * a;
+            if ((next_w / a) != w) { /* overflow */
+                if (r_is_one) {
+                    if (!BN_TO_MONTGOMERY_WORD(r, w, mont))
+                        goto err;
+                    r_is_one = 0;
+                } else {
+                    if (!BN_MOD_MUL_WORD(r, w, m))
+                        goto err;
+                }
+                next_w = a;
+            }
+            w = next_w;
+        }
+    }
+
+    /* Finally, set r:=r*w. */
+    if (w != 1) {
+        if (r_is_one) {
+            if (!BN_TO_MONTGOMERY_WORD(r, w, mont))
+                goto err;
+            r_is_one = 0;
+        } else {
+            if (!BN_MOD_MUL_WORD(r, w, m))
+                goto err;
+        }
+    }
+
+    if (r_is_one) {             /* can happen only if a == 1 */
+        if (!BN_one(rr))
+            goto err;
+    } else {
+        if (!BN_from_montgomery(rr, r, mont, ctx))
+            goto err;
+    }
+    ret = 1;
+ err:
+    if (in_mont == NULL)
+        BN_MONT_CTX_free(mont);
+    BN_CTX_end(ctx);
+    bn_check_top(rr);
+    return (ret);
+}
+
+/* The old fallback, simple version :-) */
+int BN_mod_exp_simple(BIGNUM *r, const BIGNUM *a, const BIGNUM *p,
+                      const BIGNUM *m, BN_CTX *ctx)
+{
+    int i, j, bits, ret = 0, wstart, wend, window, wvalue;
+    int start = 1;
+    BIGNUM *d;
+    /* Table of variables obtained from 'ctx' */
+    BIGNUM *val[TABLE_SIZE];
+
+    if (BN_get_flags(p, BN_FLG_CONSTTIME) != 0) {
+        /* BN_FLG_CONSTTIME only supported by BN_mod_exp_mont() */
+        BNerr(BN_F_BN_MOD_EXP_SIMPLE, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
+        return 0;
+    }
+
+    bits = BN_num_bits(p);
+   if (bits == 0) {
+        /* x**0 mod 1 is still zero. */
+        if (BN_is_one(m)) {
+            ret = 1;
+            BN_zero(r);
+        } else {
+            ret = BN_one(r);
+        }
+        return ret;
+    }
+
+    BN_CTX_start(ctx);
+    d = BN_CTX_get(ctx);
+    val[0] = BN_CTX_get(ctx);
+    if (!d || !val[0])
+        goto err;
+
+    if (!BN_nnmod(val[0], a, m, ctx))
+        goto err;               /* 1 */
+    if (BN_is_zero(val[0])) {
+        BN_zero(r);
+        ret = 1;
+        goto err;
+    }
+
+    window = BN_window_bits_for_exponent_size(bits);
+    if (window > 1) {
+        if (!BN_mod_mul(d, val[0], val[0], m, ctx))
+            goto err;           /* 2 */
+        j = 1 << (window - 1);
+        for (i = 1; i < j; i++) {
+            if (((val[i] = BN_CTX_get(ctx)) == NULL) ||
+                !BN_mod_mul(val[i], val[i - 1], d, m, ctx))
+                goto err;
+        }
+    }
+
+    start = 1;                  /* This is used to avoid multiplication etc
+                                 * when there is only the value '1' in the
+                                 * buffer. */
+    wvalue = 0;                 /* The 'value' of the window */
+    wstart = bits - 1;          /* The top bit of the window */
+    wend = 0;                   /* The bottom bit of the window */
+
+    if (!BN_one(r))
+        goto err;
+
+    for (;;) {
+        if (BN_is_bit_set(p, wstart) == 0) {
+            if (!start)
+                if (!BN_mod_mul(r, r, r, m, ctx))
+                    goto err;
+            if (wstart == 0)
+                break;
+            wstart--;
+            continue;
+        }
+        /*
+         * We now have wstart on a 'set' bit, we now need to work out how bit
+         * a window to do.  To do this we need to scan forward until the last
+         * set bit before the end of the window
+         */
+        j = wstart;
+        wvalue = 1;
+        wend = 0;
+        for (i = 1; i < window; i++) {
+            if (wstart - i < 0)
+                break;
+            if (BN_is_bit_set(p, wstart - i)) {
+                wvalue <<= (i - wend);
+                wvalue |= 1;
+                wend = i;
+            }
+        }
+
+        /* wend is the size of the current window */
+        j = wend + 1;
+        /* add the 'bytes above' */
+        if (!start)
+            for (i = 0; i < j; i++) {
+                if (!BN_mod_mul(r, r, r, m, ctx))
+                    goto err;
+            }
+
+        /* wvalue will be an odd number < 2^window */
+        if (!BN_mod_mul(r, r, val[wvalue >> 1], m, ctx))
+            goto err;
+
+        /* move the 'window' down further */
+        wstart -= wend + 1;
+        wvalue = 0;
+        start = 0;
+        if (wstart < 0)
+            break;
+    }
+    ret = 1;
+ err:
+    BN_CTX_end(ctx);
+    bn_check_top(r);
+    return (ret);
+}
diff --git a/crypto/bn/bn_exp2.c b/crypto/bn/bn_exp2.c
new file mode 100644
index 000000000..5141c21f6
--- /dev/null
+++ b/crypto/bn/bn_exp2.c
@@ -0,0 +1,201 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include "bn_lcl.h"
+
+#define TABLE_SIZE      32
+
+int BN_mod_exp2_mont(BIGNUM *rr, const BIGNUM *a1, const BIGNUM *p1,
+                     const BIGNUM *a2, const BIGNUM *p2, const BIGNUM *m,
+                     BN_CTX *ctx, BN_MONT_CTX *in_mont)
+{
+    int i, j, bits, b, bits1, bits2, ret =
+        0, wpos1, wpos2, window1, window2, wvalue1, wvalue2;
+    int r_is_one = 1;
+    BIGNUM *d, *r;
+    const BIGNUM *a_mod_m;
+    /* Tables of variables obtained from 'ctx' */
+    BIGNUM *val1[TABLE_SIZE], *val2[TABLE_SIZE];
+    BN_MONT_CTX *mont = NULL;
+
+    bn_check_top(a1);
+    bn_check_top(p1);
+    bn_check_top(a2);
+    bn_check_top(p2);
+    bn_check_top(m);
+
+    if (!(m->d[0] & 1)) {
+        BNerr(BN_F_BN_MOD_EXP2_MONT, BN_R_CALLED_WITH_EVEN_MODULUS);
+        return (0);
+    }
+    bits1 = BN_num_bits(p1);
+    bits2 = BN_num_bits(p2);
+    if ((bits1 == 0) && (bits2 == 0)) {
+        ret = BN_one(rr);
+        return ret;
+    }
+
+    bits = (bits1 > bits2) ? bits1 : bits2;
+
+    BN_CTX_start(ctx);
+    d = BN_CTX_get(ctx);
+    r = BN_CTX_get(ctx);
+    val1[0] = BN_CTX_get(ctx);
+    val2[0] = BN_CTX_get(ctx);
+    if (!d || !r || !val1[0] || !val2[0])
+        goto err;
+
+    if (in_mont != NULL)
+        mont = in_mont;
+    else {
+        if ((mont = BN_MONT_CTX_new()) == NULL)
+            goto err;
+        if (!BN_MONT_CTX_set(mont, m, ctx))
+            goto err;
+    }
+
+    window1 = BN_window_bits_for_exponent_size(bits1);
+    window2 = BN_window_bits_for_exponent_size(bits2);
+
+    /*
+     * Build table for a1:   val1[i] := a1^(2*i + 1) mod m  for i = 0 .. 2^(window1-1)
+     */
+    if (a1->neg || BN_ucmp(a1, m) >= 0) {
+        if (!BN_mod(val1[0], a1, m, ctx))
+            goto err;
+        a_mod_m = val1[0];
+    } else
+        a_mod_m = a1;
+    if (BN_is_zero(a_mod_m)) {
+        BN_zero(rr);
+        ret = 1;
+        goto err;
+    }
+
+    if (!BN_to_montgomery(val1[0], a_mod_m, mont, ctx))
+        goto err;
+    if (window1 > 1) {
+        if (!BN_mod_mul_montgomery(d, val1[0], val1[0], mont, ctx))
+            goto err;
+
+        j = 1 << (window1 - 1);
+        for (i = 1; i < j; i++) {
+            if (((val1[i] = BN_CTX_get(ctx)) == NULL) ||
+                !BN_mod_mul_montgomery(val1[i], val1[i - 1], d, mont, ctx))
+                goto err;
+        }
+    }
+
+    /*
+     * Build table for a2:   val2[i] := a2^(2*i + 1) mod m  for i = 0 .. 2^(window2-1)
+     */
+    if (a2->neg || BN_ucmp(a2, m) >= 0) {
+        if (!BN_mod(val2[0], a2, m, ctx))
+            goto err;
+        a_mod_m = val2[0];
+    } else
+        a_mod_m = a2;
+    if (BN_is_zero(a_mod_m)) {
+        BN_zero(rr);
+        ret = 1;
+        goto err;
+    }
+    if (!BN_to_montgomery(val2[0], a_mod_m, mont, ctx))
+        goto err;
+    if (window2 > 1) {
+        if (!BN_mod_mul_montgomery(d, val2[0], val2[0], mont, ctx))
+            goto err;
+
+        j = 1 << (window2 - 1);
+        for (i = 1; i < j; i++) {
+            if (((val2[i] = BN_CTX_get(ctx)) == NULL) ||
+                !BN_mod_mul_montgomery(val2[i], val2[i - 1], d, mont, ctx))
+                goto err;
+        }
+    }
+
+    /* Now compute the power product, using independent windows. */
+    r_is_one = 1;
+    wvalue1 = 0;                /* The 'value' of the first window */
+    wvalue2 = 0;                /* The 'value' of the second window */
+    wpos1 = 0;                  /* If wvalue1 > 0, the bottom bit of the
+                                 * first window */
+    wpos2 = 0;                  /* If wvalue2 > 0, the bottom bit of the
+                                 * second window */
+
+    if (!BN_to_montgomery(r, BN_value_one(), mont, ctx))
+        goto err;
+    for (b = bits - 1; b >= 0; b--) {
+        if (!r_is_one) {
+            if (!BN_mod_mul_montgomery(r, r, r, mont, ctx))
+                goto err;
+        }
+
+        if (!wvalue1)
+            if (BN_is_bit_set(p1, b)) {
+                /*
+                 * consider bits b-window1+1 .. b for this window
+                 */
+                i = b - window1 + 1;
+                while (!BN_is_bit_set(p1, i)) /* works for i<0 */
+                    i++;
+                wpos1 = i;
+                wvalue1 = 1;
+                for (i = b - 1; i >= wpos1; i--) {
+                    wvalue1 <<= 1;
+                    if (BN_is_bit_set(p1, i))
+                        wvalue1++;
+                }
+            }
+
+        if (!wvalue2)
+            if (BN_is_bit_set(p2, b)) {
+                /*
+                 * consider bits b-window2+1 .. b for this window
+                 */
+                i = b - window2 + 1;
+                while (!BN_is_bit_set(p2, i))
+                    i++;
+                wpos2 = i;
+                wvalue2 = 1;
+                for (i = b - 1; i >= wpos2; i--) {
+                    wvalue2 <<= 1;
+                    if (BN_is_bit_set(p2, i))
+                        wvalue2++;
+                }
+            }
+
+        if (wvalue1 && b == wpos1) {
+            /* wvalue1 is odd and < 2^window1 */
+            if (!BN_mod_mul_montgomery(r, r, val1[wvalue1 >> 1], mont, ctx))
+                goto err;
+            wvalue1 = 0;
+            r_is_one = 0;
+        }
+
+        if (wvalue2 && b == wpos2) {
+            /* wvalue2 is odd and < 2^window2 */
+            if (!BN_mod_mul_montgomery(r, r, val2[wvalue2 >> 1], mont, ctx))
+                goto err;
+            wvalue2 = 0;
+            r_is_one = 0;
+        }
+    }
+    if (!BN_from_montgomery(rr, r, mont, ctx))
+        goto err;
+    ret = 1;
+ err:
+    if (in_mont == NULL)
+        BN_MONT_CTX_free(mont);
+    BN_CTX_end(ctx);
+    bn_check_top(rr);
+    return (ret);
+}
diff --git a/crypto/bn/bn_gcd.c b/crypto/bn/bn_gcd.c
new file mode 100644
index 000000000..e1aac131c
--- /dev/null
+++ b/crypto/bn/bn_gcd.c
@@ -0,0 +1,620 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "internal/cryptlib.h"
+#include "bn_lcl.h"
+
+static BIGNUM *euclid(BIGNUM *a, BIGNUM *b);
+
+int BN_gcd(BIGNUM *r, const BIGNUM *in_a, const BIGNUM *in_b, BN_CTX *ctx)
+{
+    BIGNUM *a, *b, *t;
+    int ret = 0;
+
+    bn_check_top(in_a);
+    bn_check_top(in_b);
+
+    BN_CTX_start(ctx);
+    a = BN_CTX_get(ctx);
+    b = BN_CTX_get(ctx);
+    if (a == NULL || b == NULL)
+        goto err;
+
+    if (BN_copy(a, in_a) == NULL)
+        goto err;
+    if (BN_copy(b, in_b) == NULL)
+        goto err;
+    a->neg = 0;
+    b->neg = 0;
+
+    if (BN_cmp(a, b) < 0) {
+        t = a;
+        a = b;
+        b = t;
+    }
+    t = euclid(a, b);
+    if (t == NULL)
+        goto err;
+
+    if (BN_copy(r, t) == NULL)
+        goto err;
+    ret = 1;
+ err:
+    BN_CTX_end(ctx);
+    bn_check_top(r);
+    return (ret);
+}
+
+static BIGNUM *euclid(BIGNUM *a, BIGNUM *b)
+{
+    BIGNUM *t;
+    int shifts = 0;
+
+    bn_check_top(a);
+    bn_check_top(b);
+
+    /* 0 <= b <= a */
+    while (!BN_is_zero(b)) {
+        /* 0 < b <= a */
+
+        if (BN_is_odd(a)) {
+            if (BN_is_odd(b)) {
+                if (!BN_sub(a, a, b))
+                    goto err;
+                if (!BN_rshift1(a, a))
+                    goto err;
+                if (BN_cmp(a, b) < 0) {
+                    t = a;
+                    a = b;
+                    b = t;
+                }
+            } else {            /* a odd - b even */
+
+                if (!BN_rshift1(b, b))
+                    goto err;
+                if (BN_cmp(a, b) < 0) {
+                    t = a;
+                    a = b;
+                    b = t;
+                }
+            }
+        } else {                /* a is even */
+
+            if (BN_is_odd(b)) {
+                if (!BN_rshift1(a, a))
+                    goto err;
+                if (BN_cmp(a, b) < 0) {
+                    t = a;
+                    a = b;
+                    b = t;
+                }
+            } else {            /* a even - b even */
+
+                if (!BN_rshift1(a, a))
+                    goto err;
+                if (!BN_rshift1(b, b))
+                    goto err;
+                shifts++;
+            }
+        }
+        /* 0 <= b <= a */
+    }
+
+    if (shifts) {
+        if (!BN_lshift(a, a, shifts))
+            goto err;
+    }
+    bn_check_top(a);
+    return (a);
+ err:
+    return (NULL);
+}
+
+/* solves ax == 1 (mod n) */
+static BIGNUM *BN_mod_inverse_no_branch(BIGNUM *in,
+                                        const BIGNUM *a, const BIGNUM *n,
+                                        BN_CTX *ctx);
+
+BIGNUM *BN_mod_inverse(BIGNUM *in,
+                       const BIGNUM *a, const BIGNUM *n, BN_CTX *ctx)
+{
+    BIGNUM *rv;
+    int noinv;
+    rv = int_bn_mod_inverse(in, a, n, ctx, &noinv);
+    if (noinv)
+        BNerr(BN_F_BN_MOD_INVERSE, BN_R_NO_INVERSE);
+    return rv;
+}
+
+BIGNUM *int_bn_mod_inverse(BIGNUM *in,
+                           const BIGNUM *a, const BIGNUM *n, BN_CTX *ctx,
+                           int *pnoinv)
+{
+    BIGNUM *A, *B, *X, *Y, *M, *D, *T, *R = NULL;
+    BIGNUM *ret = NULL;
+    int sign;
+
+    if (pnoinv)
+        *pnoinv = 0;
+
+    if ((BN_get_flags(a, BN_FLG_CONSTTIME) != 0)
+        || (BN_get_flags(n, BN_FLG_CONSTTIME) != 0)) {
+        return BN_mod_inverse_no_branch(in, a, n, ctx);
+    }
+
+    bn_check_top(a);
+    bn_check_top(n);
+
+    BN_CTX_start(ctx);
+    A = BN_CTX_get(ctx);
+    B = BN_CTX_get(ctx);
+    X = BN_CTX_get(ctx);
+    D = BN_CTX_get(ctx);
+    M = BN_CTX_get(ctx);
+    Y = BN_CTX_get(ctx);
+    T = BN_CTX_get(ctx);
+    if (T == NULL)
+        goto err;
+
+    if (in == NULL)
+        R = BN_new();
+    else
+        R = in;
+    if (R == NULL)
+        goto err;
+
+    BN_one(X);
+    BN_zero(Y);
+    if (BN_copy(B, a) == NULL)
+        goto err;
+    if (BN_copy(A, n) == NULL)
+        goto err;
+    A->neg = 0;
+    if (B->neg || (BN_ucmp(B, A) >= 0)) {
+        if (!BN_nnmod(B, B, A, ctx))
+            goto err;
+    }
+    sign = -1;
+    /*-
+     * From  B = a mod |n|,  A = |n|  it follows that
+     *
+     *      0 <= B < A,
+     *     -sign*X*a  ==  B   (mod |n|),
+     *      sign*Y*a  ==  A   (mod |n|).
+     */
+
+    if (BN_is_odd(n) && (BN_num_bits(n) <= 2048)) {
+        /*
+         * Binary inversion algorithm; requires odd modulus. This is faster
+         * than the general algorithm if the modulus is sufficiently small
+         * (about 400 .. 500 bits on 32-bit systems, but much more on 64-bit
+         * systems)
+         */
+        int shift;
+
+        while (!BN_is_zero(B)) {
+            /*-
+             *      0 < B < |n|,
+             *      0 < A <= |n|,
+             * (1) -sign*X*a  ==  B   (mod |n|),
+             * (2)  sign*Y*a  ==  A   (mod |n|)
+             */
+
+            /*
+             * Now divide B by the maximum possible power of two in the
+             * integers, and divide X by the same value mod |n|. When we're
+             * done, (1) still holds.
+             */
+            shift = 0;
+            while (!BN_is_bit_set(B, shift)) { /* note that 0 < B */
+                shift++;
+
+                if (BN_is_odd(X)) {
+                    if (!BN_uadd(X, X, n))
+                        goto err;
+                }
+                /*
+                 * now X is even, so we can easily divide it by two
+                 */
+                if (!BN_rshift1(X, X))
+                    goto err;
+            }
+            if (shift > 0) {
+                if (!BN_rshift(B, B, shift))
+                    goto err;
+            }
+
+            /*
+             * Same for A and Y.  Afterwards, (2) still holds.
+             */
+            shift = 0;
+            while (!BN_is_bit_set(A, shift)) { /* note that 0 < A */
+                shift++;
+
+                if (BN_is_odd(Y)) {
+                    if (!BN_uadd(Y, Y, n))
+                        goto err;
+                }
+                /* now Y is even */
+                if (!BN_rshift1(Y, Y))
+                    goto err;
+            }
+            if (shift > 0) {
+                if (!BN_rshift(A, A, shift))
+                    goto err;
+            }
+
+            /*-
+             * We still have (1) and (2).
+             * Both  A  and  B  are odd.
+             * The following computations ensure that
+             *
+             *     0 <= B < |n|,
+             *      0 < A < |n|,
+             * (1) -sign*X*a  ==  B   (mod |n|),
+             * (2)  sign*Y*a  ==  A   (mod |n|),
+             *
+             * and that either  A  or  B  is even in the next iteration.
+             */
+            if (BN_ucmp(B, A) >= 0) {
+                /* -sign*(X + Y)*a == B - A  (mod |n|) */
+                if (!BN_uadd(X, X, Y))
+                    goto err;
+                /*
+                 * NB: we could use BN_mod_add_quick(X, X, Y, n), but that
+                 * actually makes the algorithm slower
+                 */
+                if (!BN_usub(B, B, A))
+                    goto err;
+            } else {
+                /*  sign*(X + Y)*a == A - B  (mod |n|) */
+                if (!BN_uadd(Y, Y, X))
+                    goto err;
+                /*
+                 * as above, BN_mod_add_quick(Y, Y, X, n) would slow things
+                 * down
+                 */
+                if (!BN_usub(A, A, B))
+                    goto err;
+            }
+        }
+    } else {
+        /* general inversion algorithm */
+
+        while (!BN_is_zero(B)) {
+            BIGNUM *tmp;
+
+            /*-
+             *      0 < B < A,
+             * (*) -sign*X*a  ==  B   (mod |n|),
+             *      sign*Y*a  ==  A   (mod |n|)
+             */
+
+            /* (D, M) := (A/B, A%B) ... */
+            if (BN_num_bits(A) == BN_num_bits(B)) {
+                if (!BN_one(D))
+                    goto err;
+                if (!BN_sub(M, A, B))
+                    goto err;
+            } else if (BN_num_bits(A) == BN_num_bits(B) + 1) {
+                /* A/B is 1, 2, or 3 */
+                if (!BN_lshift1(T, B))
+                    goto err;
+                if (BN_ucmp(A, T) < 0) {
+                    /* A < 2*B, so D=1 */
+                    if (!BN_one(D))
+                        goto err;
+                    if (!BN_sub(M, A, B))
+                        goto err;
+                } else {
+                    /* A >= 2*B, so D=2 or D=3 */
+                    if (!BN_sub(M, A, T))
+                        goto err;
+                    if (!BN_add(D, T, B))
+                        goto err; /* use D (:= 3*B) as temp */
+                    if (BN_ucmp(A, D) < 0) {
+                        /* A < 3*B, so D=2 */
+                        if (!BN_set_word(D, 2))
+                            goto err;
+                        /*
+                         * M (= A - 2*B) already has the correct value
+                         */
+                    } else {
+                        /* only D=3 remains */
+                        if (!BN_set_word(D, 3))
+                            goto err;
+                        /*
+                         * currently M = A - 2*B, but we need M = A - 3*B
+                         */
+                        if (!BN_sub(M, M, B))
+                            goto err;
+                    }
+                }
+            } else {
+                if (!BN_div(D, M, A, B, ctx))
+                    goto err;
+            }
+
+            /*-
+             * Now
+             *      A = D*B + M;
+             * thus we have
+             * (**)  sign*Y*a  ==  D*B + M   (mod |n|).
+             */
+
+            tmp = A;            /* keep the BIGNUM object, the value does not
+                                 * matter */
+
+            /* (A, B) := (B, A mod B) ... */
+            A = B;
+            B = M;
+            /* ... so we have  0 <= B < A  again */
+
+            /*-
+             * Since the former  M  is now  B  and the former  B  is now  A,
+             * (**) translates into
+             *       sign*Y*a  ==  D*A + B    (mod |n|),
+             * i.e.
+             *       sign*Y*a - D*A  ==  B    (mod |n|).
+             * Similarly, (*) translates into
+             *      -sign*X*a  ==  A          (mod |n|).
+             *
+             * Thus,
+             *   sign*Y*a + D*sign*X*a  ==  B  (mod |n|),
+             * i.e.
+             *        sign*(Y + D*X)*a  ==  B  (mod |n|).
+             *
+             * So if we set  (X, Y, sign) := (Y + D*X, X, -sign), we arrive back at
+             *      -sign*X*a  ==  B   (mod |n|),
+             *       sign*Y*a  ==  A   (mod |n|).
+             * Note that  X  and  Y  stay non-negative all the time.
+             */
+
+            /*
+             * most of the time D is very small, so we can optimize tmp :=
+             * D*X+Y
+             */
+            if (BN_is_one(D)) {
+                if (!BN_add(tmp, X, Y))
+                    goto err;
+            } else {
+                if (BN_is_word(D, 2)) {
+                    if (!BN_lshift1(tmp, X))
+                        goto err;
+                } else if (BN_is_word(D, 4)) {
+                    if (!BN_lshift(tmp, X, 2))
+                        goto err;
+                } else if (D->top == 1) {
+                    if (!BN_copy(tmp, X))
+                        goto err;
+                    if (!BN_mul_word(tmp, D->d[0]))
+                        goto err;
+                } else {
+                    if (!BN_mul(tmp, D, X, ctx))
+                        goto err;
+                }
+                if (!BN_add(tmp, tmp, Y))
+                    goto err;
+            }
+
+            M = Y;              /* keep the BIGNUM object, the value does not
+                                 * matter */
+            Y = X;
+            X = tmp;
+            sign = -sign;
+        }
+    }
+
+    /*-
+     * The while loop (Euclid's algorithm) ends when
+     *      A == gcd(a,n);
+     * we have
+     *       sign*Y*a  ==  A  (mod |n|),
+     * where  Y  is non-negative.
+     */
+
+    if (sign < 0) {
+        if (!BN_sub(Y, n, Y))
+            goto err;
+    }
+    /* Now  Y*a  ==  A  (mod |n|).  */
+
+    if (BN_is_one(A)) {
+        /* Y*a == 1  (mod |n|) */
+        if (!Y->neg && BN_ucmp(Y, n) < 0) {
+            if (!BN_copy(R, Y))
+                goto err;
+        } else {
+            if (!BN_nnmod(R, Y, n, ctx))
+                goto err;
+        }
+    } else {
+        if (pnoinv)
+            *pnoinv = 1;
+        goto err;
+    }
+    ret = R;
+ err:
+    if ((ret == NULL) && (in == NULL))
+        BN_free(R);
+    BN_CTX_end(ctx);
+    bn_check_top(ret);
+    return (ret);
+}
+
+/*
+ * BN_mod_inverse_no_branch is a special version of BN_mod_inverse. It does
+ * not contain branches that may leak sensitive information.
+ */
+static BIGNUM *BN_mod_inverse_no_branch(BIGNUM *in,
+                                        const BIGNUM *a, const BIGNUM *n,
+                                        BN_CTX *ctx)
+{
+    BIGNUM *A, *B, *X, *Y, *M, *D, *T, *R = NULL;
+    BIGNUM *ret = NULL;
+    int sign;
+
+    bn_check_top(a);
+    bn_check_top(n);
+
+    BN_CTX_start(ctx);
+    A = BN_CTX_get(ctx);
+    B = BN_CTX_get(ctx);
+    X = BN_CTX_get(ctx);
+    D = BN_CTX_get(ctx);
+    M = BN_CTX_get(ctx);
+    Y = BN_CTX_get(ctx);
+    T = BN_CTX_get(ctx);
+    if (T == NULL)
+        goto err;
+
+    if (in == NULL)
+        R = BN_new();
+    else
+        R = in;
+    if (R == NULL)
+        goto err;
+
+    BN_one(X);
+    BN_zero(Y);
+    if (BN_copy(B, a) == NULL)
+        goto err;
+    if (BN_copy(A, n) == NULL)
+        goto err;
+    A->neg = 0;
+
+    if (B->neg || (BN_ucmp(B, A) >= 0)) {
+        /*
+         * Turn BN_FLG_CONSTTIME flag on, so that when BN_div is invoked,
+         * BN_div_no_branch will be called eventually.
+         */
+         {
+            BIGNUM local_B;
+            bn_init(&local_B);
+            BN_with_flags(&local_B, B, BN_FLG_CONSTTIME);
+            if (!BN_nnmod(B, &local_B, A, ctx))
+                goto err;
+            /* Ensure local_B goes out of scope before any further use of B */
+        }
+    }
+    sign = -1;
+    /*-
+     * From  B = a mod |n|,  A = |n|  it follows that
+     *
+     *      0 <= B < A,
+     *     -sign*X*a  ==  B   (mod |n|),
+     *      sign*Y*a  ==  A   (mod |n|).
+     */
+
+    while (!BN_is_zero(B)) {
+        BIGNUM *tmp;
+
+        /*-
+         *      0 < B < A,
+         * (*) -sign*X*a  ==  B   (mod |n|),
+         *      sign*Y*a  ==  A   (mod |n|)
+         */
+
+        /*
+         * Turn BN_FLG_CONSTTIME flag on, so that when BN_div is invoked,
+         * BN_div_no_branch will be called eventually.
+         */
+        {
+            BIGNUM local_A;
+            bn_init(&local_A);
+            BN_with_flags(&local_A, A, BN_FLG_CONSTTIME);
+
+            /* (D, M) := (A/B, A%B) ... */
+            if (!BN_div(D, M, &local_A, B, ctx))
+                goto err;
+            /* Ensure local_A goes out of scope before any further use of A */
+        }
+
+        /*-
+         * Now
+         *      A = D*B + M;
+         * thus we have
+         * (**)  sign*Y*a  ==  D*B + M   (mod |n|).
+         */
+
+        tmp = A;                /* keep the BIGNUM object, the value does not
+                                 * matter */
+
+        /* (A, B) := (B, A mod B) ... */
+        A = B;
+        B = M;
+        /* ... so we have  0 <= B < A  again */
+
+        /*-
+         * Since the former  M  is now  B  and the former  B  is now  A,
+         * (**) translates into
+         *       sign*Y*a  ==  D*A + B    (mod |n|),
+         * i.e.
+         *       sign*Y*a - D*A  ==  B    (mod |n|).
+         * Similarly, (*) translates into
+         *      -sign*X*a  ==  A          (mod |n|).
+         *
+         * Thus,
+         *   sign*Y*a + D*sign*X*a  ==  B  (mod |n|),
+         * i.e.
+         *        sign*(Y + D*X)*a  ==  B  (mod |n|).
+         *
+         * So if we set  (X, Y, sign) := (Y + D*X, X, -sign), we arrive back at
+         *      -sign*X*a  ==  B   (mod |n|),
+         *       sign*Y*a  ==  A   (mod |n|).
+         * Note that  X  and  Y  stay non-negative all the time.
+         */
+
+        if (!BN_mul(tmp, D, X, ctx))
+            goto err;
+        if (!BN_add(tmp, tmp, Y))
+            goto err;
+
+        M = Y;                  /* keep the BIGNUM object, the value does not
+                                 * matter */
+        Y = X;
+        X = tmp;
+        sign = -sign;
+    }
+
+    /*-
+     * The while loop (Euclid's algorithm) ends when
+     *      A == gcd(a,n);
+     * we have
+     *       sign*Y*a  ==  A  (mod |n|),
+     * where  Y  is non-negative.
+     */
+
+    if (sign < 0) {
+        if (!BN_sub(Y, n, Y))
+            goto err;
+    }
+    /* Now  Y*a  ==  A  (mod |n|).  */
+
+    if (BN_is_one(A)) {
+        /* Y*a == 1  (mod |n|) */
+        if (!Y->neg && BN_ucmp(Y, n) < 0) {
+            if (!BN_copy(R, Y))
+                goto err;
+        } else {
+            if (!BN_nnmod(R, Y, n, ctx))
+                goto err;
+        }
+    } else {
+        BNerr(BN_F_BN_MOD_INVERSE_NO_BRANCH, BN_R_NO_INVERSE);
+        goto err;
+    }
+    ret = R;
+ err:
+    if ((ret == NULL) && (in == NULL))
+        BN_free(R);
+    BN_CTX_end(ctx);
+    bn_check_top(ret);
+    return (ret);
+}
diff --git a/crypto/bn/bn_gf2m.c b/crypto/bn/bn_gf2m.c
new file mode 100644
index 000000000..b1987f55d
--- /dev/null
+++ b/crypto/bn/bn_gf2m.c
@@ -0,0 +1,1224 @@
+/*
+ * Copyright 2002-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/* ====================================================================
+ * Copyright 2002 Sun Microsystems, Inc. ALL RIGHTS RESERVED.
+ *
+ * The Elliptic Curve Public-Key Crypto Library (ECC Code) included
+ * herein is developed by SUN MICROSYSTEMS, INC., and is contributed
+ * to the OpenSSL project.
+ *
+ * The ECC Code is licensed pursuant to the OpenSSL open source
+ * license provided below.
+ */
+
+#include 
+#include 
+#include 
+#include "internal/cryptlib.h"
+#include "bn_lcl.h"
+
+#ifndef OPENSSL_NO_EC2M
+
+/*
+ * Maximum number of iterations before BN_GF2m_mod_solve_quad_arr should
+ * fail.
+ */
+# define MAX_ITERATIONS 50
+
+static const BN_ULONG SQR_tb[16] = { 0, 1, 4, 5, 16, 17, 20, 21,
+    64, 65, 68, 69, 80, 81, 84, 85
+};
+
+/* Platform-specific macros to accelerate squaring. */
+# if defined(SIXTY_FOUR_BIT) || defined(SIXTY_FOUR_BIT_LONG)
+#  define SQR1(w) \
+    SQR_tb[(w) >> 60 & 0xF] << 56 | SQR_tb[(w) >> 56 & 0xF] << 48 | \
+    SQR_tb[(w) >> 52 & 0xF] << 40 | SQR_tb[(w) >> 48 & 0xF] << 32 | \
+    SQR_tb[(w) >> 44 & 0xF] << 24 | SQR_tb[(w) >> 40 & 0xF] << 16 | \
+    SQR_tb[(w) >> 36 & 0xF] <<  8 | SQR_tb[(w) >> 32 & 0xF]
+#  define SQR0(w) \
+    SQR_tb[(w) >> 28 & 0xF] << 56 | SQR_tb[(w) >> 24 & 0xF] << 48 | \
+    SQR_tb[(w) >> 20 & 0xF] << 40 | SQR_tb[(w) >> 16 & 0xF] << 32 | \
+    SQR_tb[(w) >> 12 & 0xF] << 24 | SQR_tb[(w) >>  8 & 0xF] << 16 | \
+    SQR_tb[(w) >>  4 & 0xF] <<  8 | SQR_tb[(w)       & 0xF]
+# endif
+# ifdef THIRTY_TWO_BIT
+#  define SQR1(w) \
+    SQR_tb[(w) >> 28 & 0xF] << 24 | SQR_tb[(w) >> 24 & 0xF] << 16 | \
+    SQR_tb[(w) >> 20 & 0xF] <<  8 | SQR_tb[(w) >> 16 & 0xF]
+#  define SQR0(w) \
+    SQR_tb[(w) >> 12 & 0xF] << 24 | SQR_tb[(w) >>  8 & 0xF] << 16 | \
+    SQR_tb[(w) >>  4 & 0xF] <<  8 | SQR_tb[(w)       & 0xF]
+# endif
+
+# if !defined(OPENSSL_BN_ASM_GF2m)
+/*
+ * Product of two polynomials a, b each with degree < BN_BITS2 - 1, result is
+ * a polynomial r with degree < 2 * BN_BITS - 1 The caller MUST ensure that
+ * the variables have the right amount of space allocated.
+ */
+#  ifdef THIRTY_TWO_BIT
+static void bn_GF2m_mul_1x1(BN_ULONG *r1, BN_ULONG *r0, const BN_ULONG a,
+                            const BN_ULONG b)
+{
+    register BN_ULONG h, l, s;
+    BN_ULONG tab[8], top2b = a >> 30;
+    register BN_ULONG a1, a2, a4;
+
+    a1 = a & (0x3FFFFFFF);
+    a2 = a1 << 1;
+    a4 = a2 << 1;
+
+    tab[0] = 0;
+    tab[1] = a1;
+    tab[2] = a2;
+    tab[3] = a1 ^ a2;
+    tab[4] = a4;
+    tab[5] = a1 ^ a4;
+    tab[6] = a2 ^ a4;
+    tab[7] = a1 ^ a2 ^ a4;
+
+    s = tab[b & 0x7];
+    l = s;
+    s = tab[b >> 3 & 0x7];
+    l ^= s << 3;
+    h = s >> 29;
+    s = tab[b >> 6 & 0x7];
+    l ^= s << 6;
+    h ^= s >> 26;
+    s = tab[b >> 9 & 0x7];
+    l ^= s << 9;
+    h ^= s >> 23;
+    s = tab[b >> 12 & 0x7];
+    l ^= s << 12;
+    h ^= s >> 20;
+    s = tab[b >> 15 & 0x7];
+    l ^= s << 15;
+    h ^= s >> 17;
+    s = tab[b >> 18 & 0x7];
+    l ^= s << 18;
+    h ^= s >> 14;
+    s = tab[b >> 21 & 0x7];
+    l ^= s << 21;
+    h ^= s >> 11;
+    s = tab[b >> 24 & 0x7];
+    l ^= s << 24;
+    h ^= s >> 8;
+    s = tab[b >> 27 & 0x7];
+    l ^= s << 27;
+    h ^= s >> 5;
+    s = tab[b >> 30];
+    l ^= s << 30;
+    h ^= s >> 2;
+
+    /* compensate for the top two bits of a */
+
+    if (top2b & 01) {
+        l ^= b << 30;
+        h ^= b >> 2;
+    }
+    if (top2b & 02) {
+        l ^= b << 31;
+        h ^= b >> 1;
+    }
+
+    *r1 = h;
+    *r0 = l;
+}
+#  endif
+#  if defined(SIXTY_FOUR_BIT) || defined(SIXTY_FOUR_BIT_LONG)
+static void bn_GF2m_mul_1x1(BN_ULONG *r1, BN_ULONG *r0, const BN_ULONG a,
+                            const BN_ULONG b)
+{
+    register BN_ULONG h, l, s;
+    BN_ULONG tab[16], top3b = a >> 61;
+    register BN_ULONG a1, a2, a4, a8;
+
+    a1 = a & (0x1FFFFFFFFFFFFFFFULL);
+    a2 = a1 << 1;
+    a4 = a2 << 1;
+    a8 = a4 << 1;
+
+    tab[0] = 0;
+    tab[1] = a1;
+    tab[2] = a2;
+    tab[3] = a1 ^ a2;
+    tab[4] = a4;
+    tab[5] = a1 ^ a4;
+    tab[6] = a2 ^ a4;
+    tab[7] = a1 ^ a2 ^ a4;
+    tab[8] = a8;
+    tab[9] = a1 ^ a8;
+    tab[10] = a2 ^ a8;
+    tab[11] = a1 ^ a2 ^ a8;
+    tab[12] = a4 ^ a8;
+    tab[13] = a1 ^ a4 ^ a8;
+    tab[14] = a2 ^ a4 ^ a8;
+    tab[15] = a1 ^ a2 ^ a4 ^ a8;
+
+    s = tab[b & 0xF];
+    l = s;
+    s = tab[b >> 4 & 0xF];
+    l ^= s << 4;
+    h = s >> 60;
+    s = tab[b >> 8 & 0xF];
+    l ^= s << 8;
+    h ^= s >> 56;
+    s = tab[b >> 12 & 0xF];
+    l ^= s << 12;
+    h ^= s >> 52;
+    s = tab[b >> 16 & 0xF];
+    l ^= s << 16;
+    h ^= s >> 48;
+    s = tab[b >> 20 & 0xF];
+    l ^= s << 20;
+    h ^= s >> 44;
+    s = tab[b >> 24 & 0xF];
+    l ^= s << 24;
+    h ^= s >> 40;
+    s = tab[b >> 28 & 0xF];
+    l ^= s << 28;
+    h ^= s >> 36;
+    s = tab[b >> 32 & 0xF];
+    l ^= s << 32;
+    h ^= s >> 32;
+    s = tab[b >> 36 & 0xF];
+    l ^= s << 36;
+    h ^= s >> 28;
+    s = tab[b >> 40 & 0xF];
+    l ^= s << 40;
+    h ^= s >> 24;
+    s = tab[b >> 44 & 0xF];
+    l ^= s << 44;
+    h ^= s >> 20;
+    s = tab[b >> 48 & 0xF];
+    l ^= s << 48;
+    h ^= s >> 16;
+    s = tab[b >> 52 & 0xF];
+    l ^= s << 52;
+    h ^= s >> 12;
+    s = tab[b >> 56 & 0xF];
+    l ^= s << 56;
+    h ^= s >> 8;
+    s = tab[b >> 60];
+    l ^= s << 60;
+    h ^= s >> 4;
+
+    /* compensate for the top three bits of a */
+
+    if (top3b & 01) {
+        l ^= b << 61;
+        h ^= b >> 3;
+    }
+    if (top3b & 02) {
+        l ^= b << 62;
+        h ^= b >> 2;
+    }
+    if (top3b & 04) {
+        l ^= b << 63;
+        h ^= b >> 1;
+    }
+
+    *r1 = h;
+    *r0 = l;
+}
+#  endif
+
+/*
+ * Product of two polynomials a, b each with degree < 2 * BN_BITS2 - 1,
+ * result is a polynomial r with degree < 4 * BN_BITS2 - 1 The caller MUST
+ * ensure that the variables have the right amount of space allocated.
+ */
+static void bn_GF2m_mul_2x2(BN_ULONG *r, const BN_ULONG a1, const BN_ULONG a0,
+                            const BN_ULONG b1, const BN_ULONG b0)
+{
+    BN_ULONG m1, m0;
+    /* r[3] = h1, r[2] = h0; r[1] = l1; r[0] = l0 */
+    bn_GF2m_mul_1x1(r + 3, r + 2, a1, b1);
+    bn_GF2m_mul_1x1(r + 1, r, a0, b0);
+    bn_GF2m_mul_1x1(&m1, &m0, a0 ^ a1, b0 ^ b1);
+    /* Correction on m1 ^= l1 ^ h1; m0 ^= l0 ^ h0; */
+    r[2] ^= m1 ^ r[1] ^ r[3];   /* h0 ^= m1 ^ l1 ^ h1; */
+    r[1] = r[3] ^ r[2] ^ r[0] ^ m1 ^ m0; /* l1 ^= l0 ^ h0 ^ m0; */
+}
+# else
+void bn_GF2m_mul_2x2(BN_ULONG *r, BN_ULONG a1, BN_ULONG a0, BN_ULONG b1,
+                     BN_ULONG b0);
+# endif
+
+/*
+ * Add polynomials a and b and store result in r; r could be a or b, a and b
+ * could be equal; r is the bitwise XOR of a and b.
+ */
+int BN_GF2m_add(BIGNUM *r, const BIGNUM *a, const BIGNUM *b)
+{
+    int i;
+    const BIGNUM *at, *bt;
+
+    bn_check_top(a);
+    bn_check_top(b);
+
+    if (a->top < b->top) {
+        at = b;
+        bt = a;
+    } else {
+        at = a;
+        bt = b;
+    }
+
+    if (bn_wexpand(r, at->top) == NULL)
+        return 0;
+
+    for (i = 0; i < bt->top; i++) {
+        r->d[i] = at->d[i] ^ bt->d[i];
+    }
+    for (; i < at->top; i++) {
+        r->d[i] = at->d[i];
+    }
+
+    r->top = at->top;
+    bn_correct_top(r);
+
+    return 1;
+}
+
+/*-
+ * Some functions allow for representation of the irreducible polynomials
+ * as an int[], say p.  The irreducible f(t) is then of the form:
+ *     t^p[0] + t^p[1] + ... + t^p[k]
+ * where m = p[0] > p[1] > ... > p[k] = 0.
+ */
+
+/* Performs modular reduction of a and store result in r.  r could be a. */
+int BN_GF2m_mod_arr(BIGNUM *r, const BIGNUM *a, const int p[])
+{
+    int j, k;
+    int n, dN, d0, d1;
+    BN_ULONG zz, *z;
+
+    bn_check_top(a);
+
+    if (!p[0]) {
+        /* reduction mod 1 => return 0 */
+        BN_zero(r);
+        return 1;
+    }
+
+    /*
+     * Since the algorithm does reduction in the r value, if a != r, copy the
+     * contents of a into r so we can do reduction in r.
+     */
+    if (a != r) {
+        if (!bn_wexpand(r, a->top))
+            return 0;
+        for (j = 0; j < a->top; j++) {
+            r->d[j] = a->d[j];
+        }
+        r->top = a->top;
+    }
+    z = r->d;
+
+    /* start reduction */
+    dN = p[0] / BN_BITS2;
+    for (j = r->top - 1; j > dN;) {
+        zz = z[j];
+        if (z[j] == 0) {
+            j--;
+            continue;
+        }
+        z[j] = 0;
+
+        for (k = 1; p[k] != 0; k++) {
+            /* reducing component t^p[k] */
+            n = p[0] - p[k];
+            d0 = n % BN_BITS2;
+            d1 = BN_BITS2 - d0;
+            n /= BN_BITS2;
+            z[j - n] ^= (zz >> d0);
+            if (d0)
+                z[j - n - 1] ^= (zz << d1);
+        }
+
+        /* reducing component t^0 */
+        n = dN;
+        d0 = p[0] % BN_BITS2;
+        d1 = BN_BITS2 - d0;
+        z[j - n] ^= (zz >> d0);
+        if (d0)
+            z[j - n - 1] ^= (zz << d1);
+    }
+
+    /* final round of reduction */
+    while (j == dN) {
+
+        d0 = p[0] % BN_BITS2;
+        zz = z[dN] >> d0;
+        if (zz == 0)
+            break;
+        d1 = BN_BITS2 - d0;
+
+        /* clear up the top d1 bits */
+        if (d0)
+            z[dN] = (z[dN] << d1) >> d1;
+        else
+            z[dN] = 0;
+        z[0] ^= zz;             /* reduction t^0 component */
+
+        for (k = 1; p[k] != 0; k++) {
+            BN_ULONG tmp_ulong;
+
+            /* reducing component t^p[k] */
+            n = p[k] / BN_BITS2;
+            d0 = p[k] % BN_BITS2;
+            d1 = BN_BITS2 - d0;
+            z[n] ^= (zz << d0);
+            if (d0 && (tmp_ulong = zz >> d1))
+                z[n + 1] ^= tmp_ulong;
+        }
+
+    }
+
+    bn_correct_top(r);
+    return 1;
+}
+
+/*
+ * Performs modular reduction of a by p and store result in r.  r could be a.
+ * This function calls down to the BN_GF2m_mod_arr implementation; this wrapper
+ * function is only provided for convenience; for best performance, use the
+ * BN_GF2m_mod_arr function.
+ */
+int BN_GF2m_mod(BIGNUM *r, const BIGNUM *a, const BIGNUM *p)
+{
+    int ret = 0;
+    int arr[6];
+    bn_check_top(a);
+    bn_check_top(p);
+    ret = BN_GF2m_poly2arr(p, arr, OSSL_NELEM(arr));
+    if (!ret || ret > (int)OSSL_NELEM(arr)) {
+        BNerr(BN_F_BN_GF2M_MOD, BN_R_INVALID_LENGTH);
+        return 0;
+    }
+    ret = BN_GF2m_mod_arr(r, a, arr);
+    bn_check_top(r);
+    return ret;
+}
+
+/*
+ * Compute the product of two polynomials a and b, reduce modulo p, and store
+ * the result in r.  r could be a or b; a could be b.
+ */
+int BN_GF2m_mod_mul_arr(BIGNUM *r, const BIGNUM *a, const BIGNUM *b,
+                        const int p[], BN_CTX *ctx)
+{
+    int zlen, i, j, k, ret = 0;
+    BIGNUM *s;
+    BN_ULONG x1, x0, y1, y0, zz[4];
+
+    bn_check_top(a);
+    bn_check_top(b);
+
+    if (a == b) {
+        return BN_GF2m_mod_sqr_arr(r, a, p, ctx);
+    }
+
+    BN_CTX_start(ctx);
+    if ((s = BN_CTX_get(ctx)) == NULL)
+        goto err;
+
+    zlen = a->top + b->top + 4;
+    if (!bn_wexpand(s, zlen))
+        goto err;
+    s->top = zlen;
+
+    for (i = 0; i < zlen; i++)
+        s->d[i] = 0;
+
+    for (j = 0; j < b->top; j += 2) {
+        y0 = b->d[j];
+        y1 = ((j + 1) == b->top) ? 0 : b->d[j + 1];
+        for (i = 0; i < a->top; i += 2) {
+            x0 = a->d[i];
+            x1 = ((i + 1) == a->top) ? 0 : a->d[i + 1];
+            bn_GF2m_mul_2x2(zz, x1, x0, y1, y0);
+            for (k = 0; k < 4; k++)
+                s->d[i + j + k] ^= zz[k];
+        }
+    }
+
+    bn_correct_top(s);
+    if (BN_GF2m_mod_arr(r, s, p))
+        ret = 1;
+    bn_check_top(r);
+
+ err:
+    BN_CTX_end(ctx);
+    return ret;
+}
+
+/*
+ * Compute the product of two polynomials a and b, reduce modulo p, and store
+ * the result in r.  r could be a or b; a could equal b. This function calls
+ * down to the BN_GF2m_mod_mul_arr implementation; this wrapper function is
+ * only provided for convenience; for best performance, use the
+ * BN_GF2m_mod_mul_arr function.
+ */
+int BN_GF2m_mod_mul(BIGNUM *r, const BIGNUM *a, const BIGNUM *b,
+                    const BIGNUM *p, BN_CTX *ctx)
+{
+    int ret = 0;
+    const int max = BN_num_bits(p) + 1;
+    int *arr = NULL;
+    bn_check_top(a);
+    bn_check_top(b);
+    bn_check_top(p);
+    if ((arr = OPENSSL_malloc(sizeof(*arr) * max)) == NULL)
+        goto err;
+    ret = BN_GF2m_poly2arr(p, arr, max);
+    if (!ret || ret > max) {
+        BNerr(BN_F_BN_GF2M_MOD_MUL, BN_R_INVALID_LENGTH);
+        goto err;
+    }
+    ret = BN_GF2m_mod_mul_arr(r, a, b, arr, ctx);
+    bn_check_top(r);
+ err:
+    OPENSSL_free(arr);
+    return ret;
+}
+
+/* Square a, reduce the result mod p, and store it in a.  r could be a. */
+int BN_GF2m_mod_sqr_arr(BIGNUM *r, const BIGNUM *a, const int p[],
+                        BN_CTX *ctx)
+{
+    int i, ret = 0;
+    BIGNUM *s;
+
+    bn_check_top(a);
+    BN_CTX_start(ctx);
+    if ((s = BN_CTX_get(ctx)) == NULL)
+        goto err;
+    if (!bn_wexpand(s, 2 * a->top))
+        goto err;
+
+    for (i = a->top - 1; i >= 0; i--) {
+        s->d[2 * i + 1] = SQR1(a->d[i]);
+        s->d[2 * i] = SQR0(a->d[i]);
+    }
+
+    s->top = 2 * a->top;
+    bn_correct_top(s);
+    if (!BN_GF2m_mod_arr(r, s, p))
+        goto err;
+    bn_check_top(r);
+    ret = 1;
+ err:
+    BN_CTX_end(ctx);
+    return ret;
+}
+
+/*
+ * Square a, reduce the result mod p, and store it in a.  r could be a. This
+ * function calls down to the BN_GF2m_mod_sqr_arr implementation; this
+ * wrapper function is only provided for convenience; for best performance,
+ * use the BN_GF2m_mod_sqr_arr function.
+ */
+int BN_GF2m_mod_sqr(BIGNUM *r, const BIGNUM *a, const BIGNUM *p, BN_CTX *ctx)
+{
+    int ret = 0;
+    const int max = BN_num_bits(p) + 1;
+    int *arr = NULL;
+
+    bn_check_top(a);
+    bn_check_top(p);
+    if ((arr = OPENSSL_malloc(sizeof(*arr) * max)) == NULL)
+        goto err;
+    ret = BN_GF2m_poly2arr(p, arr, max);
+    if (!ret || ret > max) {
+        BNerr(BN_F_BN_GF2M_MOD_SQR, BN_R_INVALID_LENGTH);
+        goto err;
+    }
+    ret = BN_GF2m_mod_sqr_arr(r, a, arr, ctx);
+    bn_check_top(r);
+ err:
+    OPENSSL_free(arr);
+    return ret;
+}
+
+/*
+ * Invert a, reduce modulo p, and store the result in r. r could be a. Uses
+ * Modified Almost Inverse Algorithm (Algorithm 10) from Hankerson, D.,
+ * Hernandez, J.L., and Menezes, A.  "Software Implementation of Elliptic
+ * Curve Cryptography Over Binary Fields".
+ */
+int BN_GF2m_mod_inv(BIGNUM *r, const BIGNUM *a, const BIGNUM *p, BN_CTX *ctx)
+{
+    BIGNUM *b, *c = NULL, *u = NULL, *v = NULL, *tmp;
+    int ret = 0;
+
+    bn_check_top(a);
+    bn_check_top(p);
+
+    BN_CTX_start(ctx);
+
+    if ((b = BN_CTX_get(ctx)) == NULL)
+        goto err;
+    if ((c = BN_CTX_get(ctx)) == NULL)
+        goto err;
+    if ((u = BN_CTX_get(ctx)) == NULL)
+        goto err;
+    if ((v = BN_CTX_get(ctx)) == NULL)
+        goto err;
+
+    if (!BN_GF2m_mod(u, a, p))
+        goto err;
+    if (BN_is_zero(u))
+        goto err;
+
+    if (!BN_copy(v, p))
+        goto err;
+# if 0
+    if (!BN_one(b))
+        goto err;
+
+    while (1) {
+        while (!BN_is_odd(u)) {
+            if (BN_is_zero(u))
+                goto err;
+            if (!BN_rshift1(u, u))
+                goto err;
+            if (BN_is_odd(b)) {
+                if (!BN_GF2m_add(b, b, p))
+                    goto err;
+            }
+            if (!BN_rshift1(b, b))
+                goto err;
+        }
+
+        if (BN_abs_is_word(u, 1))
+            break;
+
+        if (BN_num_bits(u) < BN_num_bits(v)) {
+            tmp = u;
+            u = v;
+            v = tmp;
+            tmp = b;
+            b = c;
+            c = tmp;
+        }
+
+        if (!BN_GF2m_add(u, u, v))
+            goto err;
+        if (!BN_GF2m_add(b, b, c))
+            goto err;
+    }
+# else
+    {
+        int i;
+        int ubits = BN_num_bits(u);
+        int vbits = BN_num_bits(v); /* v is copy of p */
+        int top = p->top;
+        BN_ULONG *udp, *bdp, *vdp, *cdp;
+
+        if (!bn_wexpand(u, top))
+            goto err;
+        udp = u->d;
+        for (i = u->top; i < top; i++)
+            udp[i] = 0;
+        u->top = top;
+        if (!bn_wexpand(b, top))
+          goto err;
+        bdp = b->d;
+        bdp[0] = 1;
+        for (i = 1; i < top; i++)
+            bdp[i] = 0;
+        b->top = top;
+        if (!bn_wexpand(c, top))
+          goto err;
+        cdp = c->d;
+        for (i = 0; i < top; i++)
+            cdp[i] = 0;
+        c->top = top;
+        vdp = v->d;             /* It pays off to "cache" *->d pointers,
+                                 * because it allows optimizer to be more
+                                 * aggressive. But we don't have to "cache"
+                                 * p->d, because *p is declared 'const'... */
+        while (1) {
+            while (ubits && !(udp[0] & 1)) {
+                BN_ULONG u0, u1, b0, b1, mask;
+
+                u0 = udp[0];
+                b0 = bdp[0];
+                mask = (BN_ULONG)0 - (b0 & 1);
+                b0 ^= p->d[0] & mask;
+                for (i = 0; i < top - 1; i++) {
+                    u1 = udp[i + 1];
+                    udp[i] = ((u0 >> 1) | (u1 << (BN_BITS2 - 1))) & BN_MASK2;
+                    u0 = u1;
+                    b1 = bdp[i + 1] ^ (p->d[i + 1] & mask);
+                    bdp[i] = ((b0 >> 1) | (b1 << (BN_BITS2 - 1))) & BN_MASK2;
+                    b0 = b1;
+                }
+                udp[i] = u0 >> 1;
+                bdp[i] = b0 >> 1;
+                ubits--;
+            }
+
+            if (ubits <= BN_BITS2) {
+                if (udp[0] == 0) /* poly was reducible */
+                    goto err;
+                if (udp[0] == 1)
+                    break;
+            }
+
+            if (ubits < vbits) {
+                i = ubits;
+                ubits = vbits;
+                vbits = i;
+                tmp = u;
+                u = v;
+                v = tmp;
+                tmp = b;
+                b = c;
+                c = tmp;
+                udp = vdp;
+                vdp = v->d;
+                bdp = cdp;
+                cdp = c->d;
+            }
+            for (i = 0; i < top; i++) {
+                udp[i] ^= vdp[i];
+                bdp[i] ^= cdp[i];
+            }
+            if (ubits == vbits) {
+                BN_ULONG ul;
+                int utop = (ubits - 1) / BN_BITS2;
+
+                while ((ul = udp[utop]) == 0 && utop)
+                    utop--;
+                ubits = utop * BN_BITS2 + BN_num_bits_word(ul);
+            }
+        }
+        bn_correct_top(b);
+    }
+# endif
+
+    if (!BN_copy(r, b))
+        goto err;
+    bn_check_top(r);
+    ret = 1;
+
+ err:
+# ifdef BN_DEBUG                /* BN_CTX_end would complain about the
+                                 * expanded form */
+    bn_correct_top(c);
+    bn_correct_top(u);
+    bn_correct_top(v);
+# endif
+    BN_CTX_end(ctx);
+    return ret;
+}
+
+/*
+ * Invert xx, reduce modulo p, and store the result in r. r could be xx.
+ * This function calls down to the BN_GF2m_mod_inv implementation; this
+ * wrapper function is only provided for convenience; for best performance,
+ * use the BN_GF2m_mod_inv function.
+ */
+int BN_GF2m_mod_inv_arr(BIGNUM *r, const BIGNUM *xx, const int p[],
+                        BN_CTX *ctx)
+{
+    BIGNUM *field;
+    int ret = 0;
+
+    bn_check_top(xx);
+    BN_CTX_start(ctx);
+    if ((field = BN_CTX_get(ctx)) == NULL)
+        goto err;
+    if (!BN_GF2m_arr2poly(p, field))
+        goto err;
+
+    ret = BN_GF2m_mod_inv(r, xx, field, ctx);
+    bn_check_top(r);
+
+ err:
+    BN_CTX_end(ctx);
+    return ret;
+}
+
+# ifndef OPENSSL_SUN_GF2M_DIV
+/*
+ * Divide y by x, reduce modulo p, and store the result in r. r could be x
+ * or y, x could equal y.
+ */
+int BN_GF2m_mod_div(BIGNUM *r, const BIGNUM *y, const BIGNUM *x,
+                    const BIGNUM *p, BN_CTX *ctx)
+{
+    BIGNUM *xinv = NULL;
+    int ret = 0;
+
+    bn_check_top(y);
+    bn_check_top(x);
+    bn_check_top(p);
+
+    BN_CTX_start(ctx);
+    xinv = BN_CTX_get(ctx);
+    if (xinv == NULL)
+        goto err;
+
+    if (!BN_GF2m_mod_inv(xinv, x, p, ctx))
+        goto err;
+    if (!BN_GF2m_mod_mul(r, y, xinv, p, ctx))
+        goto err;
+    bn_check_top(r);
+    ret = 1;
+
+ err:
+    BN_CTX_end(ctx);
+    return ret;
+}
+# else
+/*
+ * Divide y by x, reduce modulo p, and store the result in r. r could be x
+ * or y, x could equal y. Uses algorithm Modular_Division_GF(2^m) from
+ * Chang-Shantz, S.  "From Euclid's GCD to Montgomery Multiplication to the
+ * Great Divide".
+ */
+int BN_GF2m_mod_div(BIGNUM *r, const BIGNUM *y, const BIGNUM *x,
+                    const BIGNUM *p, BN_CTX *ctx)
+{
+    BIGNUM *a, *b, *u, *v;
+    int ret = 0;
+
+    bn_check_top(y);
+    bn_check_top(x);
+    bn_check_top(p);
+
+    BN_CTX_start(ctx);
+
+    a = BN_CTX_get(ctx);
+    b = BN_CTX_get(ctx);
+    u = BN_CTX_get(ctx);
+    v = BN_CTX_get(ctx);
+    if (v == NULL)
+        goto err;
+
+    /* reduce x and y mod p */
+    if (!BN_GF2m_mod(u, y, p))
+        goto err;
+    if (!BN_GF2m_mod(a, x, p))
+        goto err;
+    if (!BN_copy(b, p))
+        goto err;
+
+    while (!BN_is_odd(a)) {
+        if (!BN_rshift1(a, a))
+            goto err;
+        if (BN_is_odd(u))
+            if (!BN_GF2m_add(u, u, p))
+                goto err;
+        if (!BN_rshift1(u, u))
+            goto err;
+    }
+
+    do {
+        if (BN_GF2m_cmp(b, a) > 0) {
+            if (!BN_GF2m_add(b, b, a))
+                goto err;
+            if (!BN_GF2m_add(v, v, u))
+                goto err;
+            do {
+                if (!BN_rshift1(b, b))
+                    goto err;
+                if (BN_is_odd(v))
+                    if (!BN_GF2m_add(v, v, p))
+                        goto err;
+                if (!BN_rshift1(v, v))
+                    goto err;
+            } while (!BN_is_odd(b));
+        } else if (BN_abs_is_word(a, 1))
+            break;
+        else {
+            if (!BN_GF2m_add(a, a, b))
+                goto err;
+            if (!BN_GF2m_add(u, u, v))
+                goto err;
+            do {
+                if (!BN_rshift1(a, a))
+                    goto err;
+                if (BN_is_odd(u))
+                    if (!BN_GF2m_add(u, u, p))
+                        goto err;
+                if (!BN_rshift1(u, u))
+                    goto err;
+            } while (!BN_is_odd(a));
+        }
+    } while (1);
+
+    if (!BN_copy(r, u))
+        goto err;
+    bn_check_top(r);
+    ret = 1;
+
+ err:
+    BN_CTX_end(ctx);
+    return ret;
+}
+# endif
+
+/*
+ * Divide yy by xx, reduce modulo p, and store the result in r. r could be xx
+ * * or yy, xx could equal yy. This function calls down to the
+ * BN_GF2m_mod_div implementation; this wrapper function is only provided for
+ * convenience; for best performance, use the BN_GF2m_mod_div function.
+ */
+int BN_GF2m_mod_div_arr(BIGNUM *r, const BIGNUM *yy, const BIGNUM *xx,
+                        const int p[], BN_CTX *ctx)
+{
+    BIGNUM *field;
+    int ret = 0;
+
+    bn_check_top(yy);
+    bn_check_top(xx);
+
+    BN_CTX_start(ctx);
+    if ((field = BN_CTX_get(ctx)) == NULL)
+        goto err;
+    if (!BN_GF2m_arr2poly(p, field))
+        goto err;
+
+    ret = BN_GF2m_mod_div(r, yy, xx, field, ctx);
+    bn_check_top(r);
+
+ err:
+    BN_CTX_end(ctx);
+    return ret;
+}
+
+/*
+ * Compute the bth power of a, reduce modulo p, and store the result in r.  r
+ * could be a. Uses simple square-and-multiply algorithm A.5.1 from IEEE
+ * P1363.
+ */
+int BN_GF2m_mod_exp_arr(BIGNUM *r, const BIGNUM *a, const BIGNUM *b,
+                        const int p[], BN_CTX *ctx)
+{
+    int ret = 0, i, n;
+    BIGNUM *u;
+
+    bn_check_top(a);
+    bn_check_top(b);
+
+    if (BN_is_zero(b))
+        return (BN_one(r));
+
+    if (BN_abs_is_word(b, 1))
+        return (BN_copy(r, a) != NULL);
+
+    BN_CTX_start(ctx);
+    if ((u = BN_CTX_get(ctx)) == NULL)
+        goto err;
+
+    if (!BN_GF2m_mod_arr(u, a, p))
+        goto err;
+
+    n = BN_num_bits(b) - 1;
+    for (i = n - 1; i >= 0; i--) {
+        if (!BN_GF2m_mod_sqr_arr(u, u, p, ctx))
+            goto err;
+        if (BN_is_bit_set(b, i)) {
+            if (!BN_GF2m_mod_mul_arr(u, u, a, p, ctx))
+                goto err;
+        }
+    }
+    if (!BN_copy(r, u))
+        goto err;
+    bn_check_top(r);
+    ret = 1;
+ err:
+    BN_CTX_end(ctx);
+    return ret;
+}
+
+/*
+ * Compute the bth power of a, reduce modulo p, and store the result in r.  r
+ * could be a. This function calls down to the BN_GF2m_mod_exp_arr
+ * implementation; this wrapper function is only provided for convenience;
+ * for best performance, use the BN_GF2m_mod_exp_arr function.
+ */
+int BN_GF2m_mod_exp(BIGNUM *r, const BIGNUM *a, const BIGNUM *b,
+                    const BIGNUM *p, BN_CTX *ctx)
+{
+    int ret = 0;
+    const int max = BN_num_bits(p) + 1;
+    int *arr = NULL;
+    bn_check_top(a);
+    bn_check_top(b);
+    bn_check_top(p);
+    if ((arr = OPENSSL_malloc(sizeof(*arr) * max)) == NULL)
+        goto err;
+    ret = BN_GF2m_poly2arr(p, arr, max);
+    if (!ret || ret > max) {
+        BNerr(BN_F_BN_GF2M_MOD_EXP, BN_R_INVALID_LENGTH);
+        goto err;
+    }
+    ret = BN_GF2m_mod_exp_arr(r, a, b, arr, ctx);
+    bn_check_top(r);
+ err:
+    OPENSSL_free(arr);
+    return ret;
+}
+
+/*
+ * Compute the square root of a, reduce modulo p, and store the result in r.
+ * r could be a. Uses exponentiation as in algorithm A.4.1 from IEEE P1363.
+ */
+int BN_GF2m_mod_sqrt_arr(BIGNUM *r, const BIGNUM *a, const int p[],
+                         BN_CTX *ctx)
+{
+    int ret = 0;
+    BIGNUM *u;
+
+    bn_check_top(a);
+
+    if (!p[0]) {
+        /* reduction mod 1 => return 0 */
+        BN_zero(r);
+        return 1;
+    }
+
+    BN_CTX_start(ctx);
+    if ((u = BN_CTX_get(ctx)) == NULL)
+        goto err;
+
+    if (!BN_set_bit(u, p[0] - 1))
+        goto err;
+    ret = BN_GF2m_mod_exp_arr(r, a, u, p, ctx);
+    bn_check_top(r);
+
+ err:
+    BN_CTX_end(ctx);
+    return ret;
+}
+
+/*
+ * Compute the square root of a, reduce modulo p, and store the result in r.
+ * r could be a. This function calls down to the BN_GF2m_mod_sqrt_arr
+ * implementation; this wrapper function is only provided for convenience;
+ * for best performance, use the BN_GF2m_mod_sqrt_arr function.
+ */
+int BN_GF2m_mod_sqrt(BIGNUM *r, const BIGNUM *a, const BIGNUM *p, BN_CTX *ctx)
+{
+    int ret = 0;
+    const int max = BN_num_bits(p) + 1;
+    int *arr = NULL;
+    bn_check_top(a);
+    bn_check_top(p);
+    if ((arr = OPENSSL_malloc(sizeof(*arr) * max)) == NULL)
+        goto err;
+    ret = BN_GF2m_poly2arr(p, arr, max);
+    if (!ret || ret > max) {
+        BNerr(BN_F_BN_GF2M_MOD_SQRT, BN_R_INVALID_LENGTH);
+        goto err;
+    }
+    ret = BN_GF2m_mod_sqrt_arr(r, a, arr, ctx);
+    bn_check_top(r);
+ err:
+    OPENSSL_free(arr);
+    return ret;
+}
+
+/*
+ * Find r such that r^2 + r = a mod p.  r could be a. If no r exists returns
+ * 0. Uses algorithms A.4.7 and A.4.6 from IEEE P1363.
+ */
+int BN_GF2m_mod_solve_quad_arr(BIGNUM *r, const BIGNUM *a_, const int p[],
+                               BN_CTX *ctx)
+{
+    int ret = 0, count = 0, j;
+    BIGNUM *a, *z, *rho, *w, *w2, *tmp;
+
+    bn_check_top(a_);
+
+    if (!p[0]) {
+        /* reduction mod 1 => return 0 */
+        BN_zero(r);
+        return 1;
+    }
+
+    BN_CTX_start(ctx);
+    a = BN_CTX_get(ctx);
+    z = BN_CTX_get(ctx);
+    w = BN_CTX_get(ctx);
+    if (w == NULL)
+        goto err;
+
+    if (!BN_GF2m_mod_arr(a, a_, p))
+        goto err;
+
+    if (BN_is_zero(a)) {
+        BN_zero(r);
+        ret = 1;
+        goto err;
+    }
+
+    if (p[0] & 0x1) {           /* m is odd */
+        /* compute half-trace of a */
+        if (!BN_copy(z, a))
+            goto err;
+        for (j = 1; j <= (p[0] - 1) / 2; j++) {
+            if (!BN_GF2m_mod_sqr_arr(z, z, p, ctx))
+                goto err;
+            if (!BN_GF2m_mod_sqr_arr(z, z, p, ctx))
+                goto err;
+            if (!BN_GF2m_add(z, z, a))
+                goto err;
+        }
+
+    } else {                    /* m is even */
+
+        rho = BN_CTX_get(ctx);
+        w2 = BN_CTX_get(ctx);
+        tmp = BN_CTX_get(ctx);
+        if (tmp == NULL)
+            goto err;
+        do {
+            if (!BN_rand(rho, p[0], BN_RAND_TOP_ONE, BN_RAND_BOTTOM_ANY))
+                goto err;
+            if (!BN_GF2m_mod_arr(rho, rho, p))
+                goto err;
+            BN_zero(z);
+            if (!BN_copy(w, rho))
+                goto err;
+            for (j = 1; j <= p[0] - 1; j++) {
+                if (!BN_GF2m_mod_sqr_arr(z, z, p, ctx))
+                    goto err;
+                if (!BN_GF2m_mod_sqr_arr(w2, w, p, ctx))
+                    goto err;
+                if (!BN_GF2m_mod_mul_arr(tmp, w2, a, p, ctx))
+                    goto err;
+                if (!BN_GF2m_add(z, z, tmp))
+                    goto err;
+                if (!BN_GF2m_add(w, w2, rho))
+                    goto err;
+            }
+            count++;
+        } while (BN_is_zero(w) && (count < MAX_ITERATIONS));
+        if (BN_is_zero(w)) {
+            BNerr(BN_F_BN_GF2M_MOD_SOLVE_QUAD_ARR, BN_R_TOO_MANY_ITERATIONS);
+            goto err;
+        }
+    }
+
+    if (!BN_GF2m_mod_sqr_arr(w, z, p, ctx))
+        goto err;
+    if (!BN_GF2m_add(w, z, w))
+        goto err;
+    if (BN_GF2m_cmp(w, a)) {
+        BNerr(BN_F_BN_GF2M_MOD_SOLVE_QUAD_ARR, BN_R_NO_SOLUTION);
+        goto err;
+    }
+
+    if (!BN_copy(r, z))
+        goto err;
+    bn_check_top(r);
+
+    ret = 1;
+
+ err:
+    BN_CTX_end(ctx);
+    return ret;
+}
+
+/*
+ * Find r such that r^2 + r = a mod p.  r could be a. If no r exists returns
+ * 0. This function calls down to the BN_GF2m_mod_solve_quad_arr
+ * implementation; this wrapper function is only provided for convenience;
+ * for best performance, use the BN_GF2m_mod_solve_quad_arr function.
+ */
+int BN_GF2m_mod_solve_quad(BIGNUM *r, const BIGNUM *a, const BIGNUM *p,
+                           BN_CTX *ctx)
+{
+    int ret = 0;
+    const int max = BN_num_bits(p) + 1;
+    int *arr = NULL;
+    bn_check_top(a);
+    bn_check_top(p);
+    if ((arr = OPENSSL_malloc(sizeof(*arr) * max)) == NULL)
+        goto err;
+    ret = BN_GF2m_poly2arr(p, arr, max);
+    if (!ret || ret > max) {
+        BNerr(BN_F_BN_GF2M_MOD_SOLVE_QUAD, BN_R_INVALID_LENGTH);
+        goto err;
+    }
+    ret = BN_GF2m_mod_solve_quad_arr(r, a, arr, ctx);
+    bn_check_top(r);
+ err:
+    OPENSSL_free(arr);
+    return ret;
+}
+
+/*
+ * Convert the bit-string representation of a polynomial ( \sum_{i=0}^n a_i *
+ * x^i) into an array of integers corresponding to the bits with non-zero
+ * coefficient.  Array is terminated with -1. Up to max elements of the array
+ * will be filled.  Return value is total number of array elements that would
+ * be filled if array was large enough.
+ */
+int BN_GF2m_poly2arr(const BIGNUM *a, int p[], int max)
+{
+    int i, j, k = 0;
+    BN_ULONG mask;
+
+    if (BN_is_zero(a))
+        return 0;
+
+    for (i = a->top - 1; i >= 0; i--) {
+        if (!a->d[i])
+            /* skip word if a->d[i] == 0 */
+            continue;
+        mask = BN_TBIT;
+        for (j = BN_BITS2 - 1; j >= 0; j--) {
+            if (a->d[i] & mask) {
+                if (k < max)
+                    p[k] = BN_BITS2 * i + j;
+                k++;
+            }
+            mask >>= 1;
+        }
+    }
+
+    if (k < max) {
+        p[k] = -1;
+        k++;
+    }
+
+    return k;
+}
+
+/*
+ * Convert the coefficient array representation of a polynomial to a
+ * bit-string.  The array must be terminated by -1.
+ */
+int BN_GF2m_arr2poly(const int p[], BIGNUM *a)
+{
+    int i;
+
+    bn_check_top(a);
+    BN_zero(a);
+    for (i = 0; p[i] != -1; i++) {
+        if (BN_set_bit(a, p[i]) == 0)
+            return 0;
+    }
+    bn_check_top(a);
+
+    return 1;
+}
+
+#endif
diff --git a/crypto/bn/bn_intern.c b/crypto/bn/bn_intern.c
new file mode 100644
index 000000000..2c970647d
--- /dev/null
+++ b/crypto/bn/bn_intern.c
@@ -0,0 +1,210 @@
+/*
+ * Copyright 2014-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "internal/cryptlib.h"
+#include "bn_lcl.h"
+
+/*
+ * Determine the modified width-(w+1) Non-Adjacent Form (wNAF) of 'scalar'.
+ * This is an array  r[]  of values that are either zero or odd with an
+ * absolute value less than  2^w  satisfying
+ *     scalar = \sum_j r[j]*2^j
+ * where at most one of any  w+1  consecutive digits is non-zero
+ * with the exception that the most significant digit may be only
+ * w-1 zeros away from that next non-zero digit.
+ */
+signed char *bn_compute_wNAF(const BIGNUM *scalar, int w, size_t *ret_len)
+{
+    int window_val;
+    signed char *r = NULL;
+    int sign = 1;
+    int bit, next_bit, mask;
+    size_t len = 0, j;
+
+    if (BN_is_zero(scalar)) {
+        r = OPENSSL_malloc(1);
+        if (r == NULL) {
+            BNerr(BN_F_BN_COMPUTE_WNAF, ERR_R_MALLOC_FAILURE);
+            goto err;
+        }
+        r[0] = 0;
+        *ret_len = 1;
+        return r;
+    }
+
+    if (w <= 0 || w > 7) {      /* 'signed char' can represent integers with
+                                 * absolute values less than 2^7 */
+        BNerr(BN_F_BN_COMPUTE_WNAF, ERR_R_INTERNAL_ERROR);
+        goto err;
+    }
+    bit = 1 << w;               /* at most 128 */
+    next_bit = bit << 1;        /* at most 256 */
+    mask = next_bit - 1;        /* at most 255 */
+
+    if (BN_is_negative(scalar)) {
+        sign = -1;
+    }
+
+    if (scalar->d == NULL || scalar->top == 0) {
+        BNerr(BN_F_BN_COMPUTE_WNAF, ERR_R_INTERNAL_ERROR);
+        goto err;
+    }
+
+    len = BN_num_bits(scalar);
+    r = OPENSSL_malloc(len + 1); /*
+                                  * Modified wNAF may be one digit longer than binary representation
+                                  * (*ret_len will be set to the actual length, i.e. at most
+                                  * BN_num_bits(scalar) + 1)
+                                  */
+    if (r == NULL) {
+        BNerr(BN_F_BN_COMPUTE_WNAF, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+    window_val = scalar->d[0] & mask;
+    j = 0;
+    while ((window_val != 0) || (j + w + 1 < len)) { /* if j+w+1 >= len,
+                                                      * window_val will not
+                                                      * increase */
+        int digit = 0;
+
+        /* 0 <= window_val <= 2^(w+1) */
+
+        if (window_val & 1) {
+            /* 0 < window_val < 2^(w+1) */
+
+            if (window_val & bit) {
+                digit = window_val - next_bit; /* -2^w < digit < 0 */
+
+#if 1                           /* modified wNAF */
+                if (j + w + 1 >= len) {
+                    /*
+                     * Special case for generating modified wNAFs:
+                     * no new bits will be added into window_val,
+                     * so using a positive digit here will decrease
+                     * the total length of the representation
+                     */
+
+                    digit = window_val & (mask >> 1); /* 0 < digit < 2^w */
+                }
+#endif
+            } else {
+                digit = window_val; /* 0 < digit < 2^w */
+            }
+
+            if (digit <= -bit || digit >= bit || !(digit & 1)) {
+                BNerr(BN_F_BN_COMPUTE_WNAF, ERR_R_INTERNAL_ERROR);
+                goto err;
+            }
+
+            window_val -= digit;
+
+            /*
+             * now window_val is 0 or 2^(w+1) in standard wNAF generation;
+             * for modified window NAFs, it may also be 2^w
+             */
+            if (window_val != 0 && window_val != next_bit
+                && window_val != bit) {
+                BNerr(BN_F_BN_COMPUTE_WNAF, ERR_R_INTERNAL_ERROR);
+                goto err;
+            }
+        }
+
+        r[j++] = sign * digit;
+
+        window_val >>= 1;
+        window_val += bit * BN_is_bit_set(scalar, j + w);
+
+        if (window_val > next_bit) {
+            BNerr(BN_F_BN_COMPUTE_WNAF, ERR_R_INTERNAL_ERROR);
+            goto err;
+        }
+    }
+
+    if (j > len + 1) {
+        BNerr(BN_F_BN_COMPUTE_WNAF, ERR_R_INTERNAL_ERROR);
+        goto err;
+    }
+    *ret_len = j;
+    return r;
+
+ err:
+    OPENSSL_free(r);
+    return NULL;
+}
+
+int bn_get_top(const BIGNUM *a)
+{
+    return a->top;
+}
+
+void bn_set_top(BIGNUM *a, int top)
+{
+    a->top = top;
+}
+
+int bn_get_dmax(const BIGNUM *a)
+{
+    return a->dmax;
+}
+
+void bn_set_all_zero(BIGNUM *a)
+{
+    int i;
+
+    for (i = a->top; i < a->dmax; i++)
+        a->d[i] = 0;
+}
+
+int bn_copy_words(BN_ULONG *out, const BIGNUM *in, int size)
+{
+    if (in->top > size)
+        return 0;
+
+    memset(out, 0, sizeof(*out) * size);
+    if (in->d != NULL)
+        memcpy(out, in->d, sizeof(*out) * in->top);
+    return 1;
+}
+
+BN_ULONG *bn_get_words(const BIGNUM *a)
+{
+    return a->d;
+}
+
+void bn_set_static_words(BIGNUM *a, BN_ULONG *words, int size)
+{
+    a->d = words;
+    a->dmax = a->top = size;
+    a->neg = 0;
+    a->flags |= BN_FLG_STATIC_DATA;
+    bn_correct_top(a);
+}
+
+int bn_set_words(BIGNUM *a, BN_ULONG *words, int num_words)
+{
+    if (bn_wexpand(a, num_words) == NULL) {
+        BNerr(BN_F_BN_SET_WORDS, ERR_R_MALLOC_FAILURE);
+        return 0;
+    }
+
+    memcpy(a->d, words, sizeof(BN_ULONG) * num_words);
+    a->top = num_words;
+    bn_correct_top(a);
+    return 1;
+}
+
+size_t bn_sizeof_BIGNUM(void)
+{
+    return sizeof(BIGNUM);
+}
+
+BIGNUM *bn_array_el(BIGNUM *base, int el)
+{
+    return &base[el];
+}
diff --git a/crypto/bn/bn_kron.c b/crypto/bn/bn_kron.c
new file mode 100644
index 000000000..b9bc6cca2
--- /dev/null
+++ b/crypto/bn/bn_kron.c
@@ -0,0 +1,140 @@
+/*
+ * Copyright 2000-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "internal/cryptlib.h"
+#include "bn_lcl.h"
+
+/* least significant word */
+#define BN_lsw(n) (((n)->top == 0) ? (BN_ULONG) 0 : (n)->d[0])
+
+/* Returns -2 for errors because both -1 and 0 are valid results. */
+int BN_kronecker(const BIGNUM *a, const BIGNUM *b, BN_CTX *ctx)
+{
+    int i;
+    int ret = -2;               /* avoid 'uninitialized' warning */
+    int err = 0;
+    BIGNUM *A, *B, *tmp;
+    /*-
+     * In 'tab', only odd-indexed entries are relevant:
+     * For any odd BIGNUM n,
+     *     tab[BN_lsw(n) & 7]
+     * is $(-1)^{(n^2-1)/8}$ (using TeX notation).
+     * Note that the sign of n does not matter.
+     */
+    static const int tab[8] = { 0, 1, 0, -1, 0, -1, 0, 1 };
+
+    bn_check_top(a);
+    bn_check_top(b);
+
+    BN_CTX_start(ctx);
+    A = BN_CTX_get(ctx);
+    B = BN_CTX_get(ctx);
+    if (B == NULL)
+        goto end;
+
+    err = !BN_copy(A, a);
+    if (err)
+        goto end;
+    err = !BN_copy(B, b);
+    if (err)
+        goto end;
+
+    /*
+     * Kronecker symbol, implemented according to Henri Cohen,
+     * "A Course in Computational Algebraic Number Theory"
+     * (algorithm 1.4.10).
+     */
+
+    /* Cohen's step 1: */
+
+    if (BN_is_zero(B)) {
+        ret = BN_abs_is_word(A, 1);
+        goto end;
+    }
+
+    /* Cohen's step 2: */
+
+    if (!BN_is_odd(A) && !BN_is_odd(B)) {
+        ret = 0;
+        goto end;
+    }
+
+    /* now  B  is non-zero */
+    i = 0;
+    while (!BN_is_bit_set(B, i))
+        i++;
+    err = !BN_rshift(B, B, i);
+    if (err)
+        goto end;
+    if (i & 1) {
+        /* i is odd */
+        /* (thus  B  was even, thus  A  must be odd!)  */
+
+        /* set 'ret' to $(-1)^{(A^2-1)/8}$ */
+        ret = tab[BN_lsw(A) & 7];
+    } else {
+        /* i is even */
+        ret = 1;
+    }
+
+    if (B->neg) {
+        B->neg = 0;
+        if (A->neg)
+            ret = -ret;
+    }
+
+    /*
+     * now B is positive and odd, so what remains to be done is to compute
+     * the Jacobi symbol (A/B) and multiply it by 'ret'
+     */
+
+    while (1) {
+        /* Cohen's step 3: */
+
+        /*  B  is positive and odd */
+
+        if (BN_is_zero(A)) {
+            ret = BN_is_one(B) ? ret : 0;
+            goto end;
+        }
+
+        /* now  A  is non-zero */
+        i = 0;
+        while (!BN_is_bit_set(A, i))
+            i++;
+        err = !BN_rshift(A, A, i);
+        if (err)
+            goto end;
+        if (i & 1) {
+            /* i is odd */
+            /* multiply 'ret' by  $(-1)^{(B^2-1)/8}$ */
+            ret = ret * tab[BN_lsw(B) & 7];
+        }
+
+        /* Cohen's step 4: */
+        /* multiply 'ret' by  $(-1)^{(A-1)(B-1)/4}$ */
+        if ((A->neg ? ~BN_lsw(A) : BN_lsw(A)) & BN_lsw(B) & 2)
+            ret = -ret;
+
+        /* (A, B) := (B mod |A|, |A|) */
+        err = !BN_nnmod(B, B, A, ctx);
+        if (err)
+            goto end;
+        tmp = A;
+        A = B;
+        B = tmp;
+        tmp->neg = 0;
+    }
+ end:
+    BN_CTX_end(ctx);
+    if (err)
+        return -2;
+    else
+        return ret;
+}
diff --git a/crypto/bn/bn_lcl.h b/crypto/bn/bn_lcl.h
new file mode 100644
index 000000000..a3911b191
--- /dev/null
+++ b/crypto/bn/bn_lcl.h
@@ -0,0 +1,691 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#ifndef HEADER_BN_LCL_H
+# define HEADER_BN_LCL_H
+
+/*
+ * The EDK2 build doesn't use bn_conf.h; it sets THIRTY_TWO_BIT or
+ * SIXTY_FOUR_BIT in its own environment since it doesn't re-run our
+ * Configure script and needs to support both 32-bit and 64-bit.
+ */
+# include 
+
+# if !defined(OPENSSL_SYS_UEFI)
+#  include "internal/bn_conf.h"
+# endif
+
+# include "internal/bn_int.h"
+
+#ifdef  __cplusplus
+extern "C" {
+#endif
+
+/*
+ * These preprocessor symbols control various aspects of the bignum headers
+ * and library code. They're not defined by any "normal" configuration, as
+ * they are intended for development and testing purposes. NB: defining all
+ * three can be useful for debugging application code as well as openssl
+ * itself. BN_DEBUG - turn on various debugging alterations to the bignum
+ * code BN_DEBUG_RAND - uses random poisoning of unused words to trip up
+ * mismanagement of bignum internals. You must also define BN_DEBUG.
+ */
+/* #define BN_DEBUG */
+/* #define BN_DEBUG_RAND */
+
+# ifndef OPENSSL_SMALL_FOOTPRINT
+#  define BN_MUL_COMBA
+#  define BN_SQR_COMBA
+#  define BN_RECURSION
+# endif
+
+/*
+ * This next option uses the C libraries (2 word)/(1 word) function. If it is
+ * not defined, I use my C version (which is slower). The reason for this
+ * flag is that when the particular C compiler library routine is used, and
+ * the library is linked with a different compiler, the library is missing.
+ * This mostly happens when the library is built with gcc and then linked
+ * using normal cc.  This would be a common occurrence because gcc normally
+ * produces code that is 2 times faster than system compilers for the big
+ * number stuff. For machines with only one compiler (or shared libraries),
+ * this should be on.  Again this in only really a problem on machines using
+ * "long long's", are 32bit, and are not using my assembler code.
+ */
+# if defined(OPENSSL_SYS_MSDOS) || defined(OPENSSL_SYS_WINDOWS) || \
+    defined(OPENSSL_SYS_WIN32) || defined(linux)
+#  define BN_DIV2W
+# endif
+
+/*
+ * 64-bit processor with LP64 ABI
+ */
+# ifdef SIXTY_FOUR_BIT_LONG
+#  define BN_ULLONG       unsigned long long
+#  define BN_BITS4        32
+#  define BN_MASK2        (0xffffffffffffffffL)
+#  define BN_MASK2l       (0xffffffffL)
+#  define BN_MASK2h       (0xffffffff00000000L)
+#  define BN_MASK2h1      (0xffffffff80000000L)
+#  define BN_DEC_CONV     (10000000000000000000UL)
+#  define BN_DEC_NUM      19
+#  define BN_DEC_FMT1     "%lu"
+#  define BN_DEC_FMT2     "%019lu"
+# endif
+
+/*
+ * 64-bit processor other than LP64 ABI
+ */
+# ifdef SIXTY_FOUR_BIT
+#  undef BN_LLONG
+#  undef BN_ULLONG
+#  define BN_BITS4        32
+#  define BN_MASK2        (0xffffffffffffffffLL)
+#  define BN_MASK2l       (0xffffffffL)
+#  define BN_MASK2h       (0xffffffff00000000LL)
+#  define BN_MASK2h1      (0xffffffff80000000LL)
+#  define BN_DEC_CONV     (10000000000000000000ULL)
+#  define BN_DEC_NUM      19
+#  define BN_DEC_FMT1     "%llu"
+#  define BN_DEC_FMT2     "%019llu"
+# endif
+
+# ifdef THIRTY_TWO_BIT
+#  ifdef BN_LLONG
+#   if defined(_WIN32) && !defined(__GNUC__)
+#    define BN_ULLONG     unsigned __int64
+#   else
+#    define BN_ULLONG     unsigned long long
+#   endif
+#  endif
+#  define BN_BITS4        16
+#  define BN_MASK2        (0xffffffffL)
+#  define BN_MASK2l       (0xffff)
+#  define BN_MASK2h1      (0xffff8000L)
+#  define BN_MASK2h       (0xffff0000L)
+#  define BN_DEC_CONV     (1000000000L)
+#  define BN_DEC_NUM      9
+#  define BN_DEC_FMT1     "%u"
+#  define BN_DEC_FMT2     "%09u"
+# endif
+
+
+/*-
+ * Bignum consistency macros
+ * There is one "API" macro, bn_fix_top(), for stripping leading zeroes from
+ * bignum data after direct manipulations on the data. There is also an
+ * "internal" macro, bn_check_top(), for verifying that there are no leading
+ * zeroes. Unfortunately, some auditing is required due to the fact that
+ * bn_fix_top() has become an overabused duct-tape because bignum data is
+ * occasionally passed around in an inconsistent state. So the following
+ * changes have been made to sort this out;
+ * - bn_fix_top()s implementation has been moved to bn_correct_top()
+ * - if BN_DEBUG isn't defined, bn_fix_top() maps to bn_correct_top(), and
+ *   bn_check_top() is as before.
+ * - if BN_DEBUG *is* defined;
+ *   - bn_check_top() tries to pollute unused words even if the bignum 'top' is
+ *     consistent. (ed: only if BN_DEBUG_RAND is defined)
+ *   - bn_fix_top() maps to bn_check_top() rather than "fixing" anything.
+ * The idea is to have debug builds flag up inconsistent bignums when they
+ * occur. If that occurs in a bn_fix_top(), we examine the code in question; if
+ * the use of bn_fix_top() was appropriate (ie. it follows directly after code
+ * that manipulates the bignum) it is converted to bn_correct_top(), and if it
+ * was not appropriate, we convert it permanently to bn_check_top() and track
+ * down the cause of the bug. Eventually, no internal code should be using the
+ * bn_fix_top() macro. External applications and libraries should try this with
+ * their own code too, both in terms of building against the openssl headers
+ * with BN_DEBUG defined *and* linking with a version of OpenSSL built with it
+ * defined. This not only improves external code, it provides more test
+ * coverage for openssl's own code.
+ */
+
+# ifdef BN_DEBUG
+
+/* We only need assert() when debugging */
+#  include 
+
+#  ifdef BN_DEBUG_RAND
+/* To avoid "make update" cvs wars due to BN_DEBUG, use some tricks */
+#   ifndef RAND_pseudo_bytes
+int RAND_pseudo_bytes(unsigned char *buf, int num);
+#    define BN_DEBUG_TRIX
+#   endif
+#   define bn_pollute(a) \
+        do { \
+            const BIGNUM *_bnum1 = (a); \
+            if (_bnum1->top < _bnum1->dmax) { \
+                unsigned char _tmp_char; \
+                /* We cast away const without the compiler knowing, any \
+                 * *genuinely* constant variables that aren't mutable \
+                 * wouldn't be constructed with top!=dmax. */ \
+                BN_ULONG *_not_const; \
+                memcpy(&_not_const, &_bnum1->d, sizeof(_not_const)); \
+                RAND_bytes(&_tmp_char, 1); /* Debug only - safe to ignore error return */\
+                memset(_not_const + _bnum1->top, _tmp_char, \
+                       sizeof(*_not_const) * (_bnum1->dmax - _bnum1->top)); \
+            } \
+        } while(0)
+#   ifdef BN_DEBUG_TRIX
+#    undef RAND_pseudo_bytes
+#   endif
+#  else
+#   define bn_pollute(a)
+#  endif
+#  define bn_check_top(a) \
+        do { \
+                const BIGNUM *_bnum2 = (a); \
+                if (_bnum2 != NULL) { \
+                        assert((_bnum2->top == 0) || \
+                                (_bnum2->d[_bnum2->top - 1] != 0)); \
+                        bn_pollute(_bnum2); \
+                } \
+        } while(0)
+
+#  define bn_fix_top(a)           bn_check_top(a)
+
+#  define bn_check_size(bn, bits) bn_wcheck_size(bn, ((bits+BN_BITS2-1))/BN_BITS2)
+#  define bn_wcheck_size(bn, words) \
+        do { \
+                const BIGNUM *_bnum2 = (bn); \
+                assert((words) <= (_bnum2)->dmax && (words) >= (_bnum2)->top); \
+                /* avoid unused variable warning with NDEBUG */ \
+                (void)(_bnum2); \
+        } while(0)
+
+# else                          /* !BN_DEBUG */
+
+#  define bn_pollute(a)
+#  define bn_check_top(a)
+#  define bn_fix_top(a)           bn_correct_top(a)
+#  define bn_check_size(bn, bits)
+#  define bn_wcheck_size(bn, words)
+
+# endif
+
+BN_ULONG bn_mul_add_words(BN_ULONG *rp, const BN_ULONG *ap, int num,
+                          BN_ULONG w);
+BN_ULONG bn_mul_words(BN_ULONG *rp, const BN_ULONG *ap, int num, BN_ULONG w);
+void bn_sqr_words(BN_ULONG *rp, const BN_ULONG *ap, int num);
+BN_ULONG bn_div_words(BN_ULONG h, BN_ULONG l, BN_ULONG d);
+BN_ULONG bn_add_words(BN_ULONG *rp, const BN_ULONG *ap, const BN_ULONG *bp,
+                      int num);
+BN_ULONG bn_sub_words(BN_ULONG *rp, const BN_ULONG *ap, const BN_ULONG *bp,
+                      int num);
+
+struct bignum_st {
+    BN_ULONG *d;                /* Pointer to an array of 'BN_BITS2' bit
+                                 * chunks. */
+    int top;                    /* Index of last used d +1. */
+    /* The next are internal book keeping for bn_expand. */
+    int dmax;                   /* Size of the d array. */
+    int neg;                    /* one if the number is negative */
+    int flags;
+};
+
+/* Used for montgomery multiplication */
+struct bn_mont_ctx_st {
+    int ri;                     /* number of bits in R */
+    BIGNUM RR;                  /* used to convert to montgomery form */
+    BIGNUM N;                   /* The modulus */
+    BIGNUM Ni;                  /* R*(1/R mod N) - N*Ni = 1 (Ni is only
+                                 * stored for bignum algorithm) */
+    BN_ULONG n0[2];             /* least significant word(s) of Ni; (type
+                                 * changed with 0.9.9, was "BN_ULONG n0;"
+                                 * before) */
+    int flags;
+};
+
+/*
+ * Used for reciprocal division/mod functions It cannot be shared between
+ * threads
+ */
+struct bn_recp_ctx_st {
+    BIGNUM N;                   /* the divisor */
+    BIGNUM Nr;                  /* the reciprocal */
+    int num_bits;
+    int shift;
+    int flags;
+};
+
+/* Used for slow "generation" functions. */
+struct bn_gencb_st {
+    unsigned int ver;           /* To handle binary (in)compatibility */
+    void *arg;                  /* callback-specific data */
+    union {
+        /* if (ver==1) - handles old style callbacks */
+        void (*cb_1) (int, int, void *);
+        /* if (ver==2) - new callback style */
+        int (*cb_2) (int, int, BN_GENCB *);
+    } cb;
+};
+
+/*-
+ * BN_window_bits_for_exponent_size -- macro for sliding window mod_exp functions
+ *
+ *
+ * For window size 'w' (w >= 2) and a random 'b' bits exponent,
+ * the number of multiplications is a constant plus on average
+ *
+ *    2^(w-1) + (b-w)/(w+1);
+ *
+ * here  2^(w-1)  is for precomputing the table (we actually need
+ * entries only for windows that have the lowest bit set), and
+ * (b-w)/(w+1)  is an approximation for the expected number of
+ * w-bit windows, not counting the first one.
+ *
+ * Thus we should use
+ *
+ *    w >= 6  if        b > 671
+ *     w = 5  if  671 > b > 239
+ *     w = 4  if  239 > b >  79
+ *     w = 3  if   79 > b >  23
+ *    w <= 2  if   23 > b
+ *
+ * (with draws in between).  Very small exponents are often selected
+ * with low Hamming weight, so we use  w = 1  for b <= 23.
+ */
+# define BN_window_bits_for_exponent_size(b) \
+                ((b) > 671 ? 6 : \
+                 (b) > 239 ? 5 : \
+                 (b) >  79 ? 4 : \
+                 (b) >  23 ? 3 : 1)
+
+/*
+ * BN_mod_exp_mont_conttime is based on the assumption that the L1 data cache
+ * line width of the target processor is at least the following value.
+ */
+# define MOD_EXP_CTIME_MIN_CACHE_LINE_WIDTH      ( 64 )
+# define MOD_EXP_CTIME_MIN_CACHE_LINE_MASK       (MOD_EXP_CTIME_MIN_CACHE_LINE_WIDTH - 1)
+
+/*
+ * Window sizes optimized for fixed window size modular exponentiation
+ * algorithm (BN_mod_exp_mont_consttime). To achieve the security goals of
+ * BN_mode_exp_mont_consttime, the maximum size of the window must not exceed
+ * log_2(MOD_EXP_CTIME_MIN_CACHE_LINE_WIDTH). Window size thresholds are
+ * defined for cache line sizes of 32 and 64, cache line sizes where
+ * log_2(32)=5 and log_2(64)=6 respectively. A window size of 7 should only be
+ * used on processors that have a 128 byte or greater cache line size.
+ */
+# if MOD_EXP_CTIME_MIN_CACHE_LINE_WIDTH == 64
+
+#  define BN_window_bits_for_ctime_exponent_size(b) \
+                ((b) > 937 ? 6 : \
+                 (b) > 306 ? 5 : \
+                 (b) >  89 ? 4 : \
+                 (b) >  22 ? 3 : 1)
+#  define BN_MAX_WINDOW_BITS_FOR_CTIME_EXPONENT_SIZE    (6)
+
+# elif MOD_EXP_CTIME_MIN_CACHE_LINE_WIDTH == 32
+
+#  define BN_window_bits_for_ctime_exponent_size(b) \
+                ((b) > 306 ? 5 : \
+                 (b) >  89 ? 4 : \
+                 (b) >  22 ? 3 : 1)
+#  define BN_MAX_WINDOW_BITS_FOR_CTIME_EXPONENT_SIZE    (5)
+
+# endif
+
+/* Pentium pro 16,16,16,32,64 */
+/* Alpha       16,16,16,16.64 */
+# define BN_MULL_SIZE_NORMAL                     (16)/* 32 */
+# define BN_MUL_RECURSIVE_SIZE_NORMAL            (16)/* 32 less than */
+# define BN_SQR_RECURSIVE_SIZE_NORMAL            (16)/* 32 */
+# define BN_MUL_LOW_RECURSIVE_SIZE_NORMAL        (32)/* 32 */
+# define BN_MONT_CTX_SET_SIZE_WORD               (64)/* 32 */
+
+/*
+ * 2011-02-22 SMS. In various places, a size_t variable or a type cast to
+ * size_t was used to perform integer-only operations on pointers.  This
+ * failed on VMS with 64-bit pointers (CC /POINTER_SIZE = 64) because size_t
+ * is still only 32 bits.  What's needed in these cases is an integer type
+ * with the same size as a pointer, which size_t is not certain to be. The
+ * only fix here is VMS-specific.
+ */
+# if defined(OPENSSL_SYS_VMS)
+#  if __INITIAL_POINTER_SIZE == 64
+#   define PTR_SIZE_INT long long
+#  else                         /* __INITIAL_POINTER_SIZE == 64 */
+#   define PTR_SIZE_INT int
+#  endif                        /* __INITIAL_POINTER_SIZE == 64 [else] */
+# elif !defined(PTR_SIZE_INT)   /* defined(OPENSSL_SYS_VMS) */
+#  define PTR_SIZE_INT size_t
+# endif                         /* defined(OPENSSL_SYS_VMS) [else] */
+
+# if !defined(OPENSSL_NO_ASM) && !defined(OPENSSL_NO_INLINE_ASM) && !defined(PEDANTIC)
+/*
+ * BN_UMULT_HIGH section.
+ *
+ * No, I'm not trying to overwhelm you when stating that the
+ * product of N-bit numbers is 2*N bits wide:-) No, I don't expect
+ * you to be impressed when I say that if the compiler doesn't
+ * support 2*N integer type, then you have to replace every N*N
+ * multiplication with 4 (N/2)*(N/2) accompanied by some shifts
+ * and additions which unavoidably results in severe performance
+ * penalties. Of course provided that the hardware is capable of
+ * producing 2*N result... That's when you normally start
+ * considering assembler implementation. However! It should be
+ * pointed out that some CPUs (most notably Alpha, PowerPC and
+ * upcoming IA-64 family:-) provide *separate* instruction
+ * calculating the upper half of the product placing the result
+ * into a general purpose register. Now *if* the compiler supports
+ * inline assembler, then it's not impossible to implement the
+ * "bignum" routines (and have the compiler optimize 'em)
+ * exhibiting "native" performance in C. That's what BN_UMULT_HIGH
+ * macro is about:-)
+ *
+ *                                      
+ */
+#  if defined(__alpha) && (defined(SIXTY_FOUR_BIT_LONG) || defined(SIXTY_FOUR_BIT))
+#   if defined(__DECC)
+#    include 
+#    define BN_UMULT_HIGH(a,b)   (BN_ULONG)asm("umulh %a0,%a1,%v0",(a),(b))
+#   elif defined(__GNUC__) && __GNUC__>=2
+#    define BN_UMULT_HIGH(a,b)   ({      \
+        register BN_ULONG ret;          \
+        asm ("umulh     %1,%2,%0"       \
+             : "=r"(ret)                \
+             : "r"(a), "r"(b));         \
+        ret;                    })
+#   endif                       /* compiler */
+#  elif defined(_ARCH_PPC) && defined(__64BIT__) && defined(SIXTY_FOUR_BIT_LONG)
+#   if defined(__GNUC__) && __GNUC__>=2
+#    define BN_UMULT_HIGH(a,b)   ({      \
+        register BN_ULONG ret;          \
+        asm ("mulhdu    %0,%1,%2"       \
+             : "=r"(ret)                \
+             : "r"(a), "r"(b));         \
+        ret;                    })
+#   endif                       /* compiler */
+#  elif (defined(__x86_64) || defined(__x86_64__)) && \
+       (defined(SIXTY_FOUR_BIT_LONG) || defined(SIXTY_FOUR_BIT))
+#   if defined(__GNUC__) && __GNUC__>=2
+#    define BN_UMULT_HIGH(a,b)   ({      \
+        register BN_ULONG ret,discard;  \
+        asm ("mulq      %3"             \
+             : "=a"(discard),"=d"(ret)  \
+             : "a"(a), "g"(b)           \
+             : "cc");                   \
+        ret;                    })
+#    define BN_UMULT_LOHI(low,high,a,b)  \
+        asm ("mulq      %3"             \
+                : "=a"(low),"=d"(high)  \
+                : "a"(a),"g"(b)         \
+                : "cc");
+#   endif
+#  elif (defined(_M_AMD64) || defined(_M_X64)) && defined(SIXTY_FOUR_BIT)
+#   if defined(_MSC_VER) && _MSC_VER>=1400
+unsigned __int64 __umulh(unsigned __int64 a, unsigned __int64 b);
+unsigned __int64 _umul128(unsigned __int64 a, unsigned __int64 b,
+                          unsigned __int64 *h);
+#    pragma intrinsic(__umulh,_umul128)
+#    define BN_UMULT_HIGH(a,b)           __umulh((a),(b))
+#    define BN_UMULT_LOHI(low,high,a,b)  ((low)=_umul128((a),(b),&(high)))
+#   endif
+#  elif defined(__mips) && (defined(SIXTY_FOUR_BIT) || defined(SIXTY_FOUR_BIT_LONG))
+#   if defined(__GNUC__) && __GNUC__>=2
+#    if defined(__SIZEOF_INT128__) && __SIZEOF_INT128__==16
+      /* "h" constraint is not an option on R6 and was removed in 4.4 */
+#     define BN_UMULT_HIGH(a,b)          (((__uint128_t)(a)*(b))>>64)
+#     define BN_UMULT_LOHI(low,high,a,b) ({     \
+        __uint128_t ret=(__uint128_t)(a)*(b);   \
+        (high)=ret>>64; (low)=ret;       })
+#    else
+#     define BN_UMULT_HIGH(a,b) ({      \
+        register BN_ULONG ret;          \
+        asm ("dmultu    %1,%2"          \
+             : "=h"(ret)                \
+             : "r"(a), "r"(b) : "l");   \
+        ret;                    })
+#     define BN_UMULT_LOHI(low,high,a,b)\
+        asm ("dmultu    %2,%3"          \
+             : "=l"(low),"=h"(high)     \
+             : "r"(a), "r"(b));
+#    endif
+#   endif
+#  elif defined(__aarch64__) && defined(SIXTY_FOUR_BIT_LONG)
+#   if defined(__GNUC__) && __GNUC__>=2
+#    define BN_UMULT_HIGH(a,b)   ({      \
+        register BN_ULONG ret;          \
+        asm ("umulh     %0,%1,%2"       \
+             : "=r"(ret)                \
+             : "r"(a), "r"(b));         \
+        ret;                    })
+#   endif
+#  endif                        /* cpu */
+# endif                         /* OPENSSL_NO_ASM */
+
+/*************************************************************
+ * Using the long long type
+ */
+# define Lw(t)    (((BN_ULONG)(t))&BN_MASK2)
+# define Hw(t)    (((BN_ULONG)((t)>>BN_BITS2))&BN_MASK2)
+
+# ifdef BN_DEBUG_RAND
+#  define bn_clear_top2max(a) \
+        { \
+        int      ind = (a)->dmax - (a)->top; \
+        BN_ULONG *ftl = &(a)->d[(a)->top-1]; \
+        for (; ind != 0; ind--) \
+                *(++ftl) = 0x0; \
+        }
+# else
+#  define bn_clear_top2max(a)
+# endif
+
+# ifdef BN_LLONG
+#  define mul_add(r,a,w,c) { \
+        BN_ULLONG t; \
+        t=(BN_ULLONG)w * (a) + (r) + (c); \
+        (r)= Lw(t); \
+        (c)= Hw(t); \
+        }
+
+#  define mul(r,a,w,c) { \
+        BN_ULLONG t; \
+        t=(BN_ULLONG)w * (a) + (c); \
+        (r)= Lw(t); \
+        (c)= Hw(t); \
+        }
+
+#  define sqr(r0,r1,a) { \
+        BN_ULLONG t; \
+        t=(BN_ULLONG)(a)*(a); \
+        (r0)=Lw(t); \
+        (r1)=Hw(t); \
+        }
+
+# elif defined(BN_UMULT_LOHI)
+#  define mul_add(r,a,w,c) {              \
+        BN_ULONG high,low,ret,tmp=(a);  \
+        ret =  (r);                     \
+        BN_UMULT_LOHI(low,high,w,tmp);  \
+        ret += (c);                     \
+        (c) =  (ret<(c))?1:0;           \
+        (c) += high;                    \
+        ret += low;                     \
+        (c) += (ret>BN_BITS4)&BN_MASK2l)
+#  define L2HBITS(a)      (((a)<>BN_BITS2)&BN_MASKl)
+#  define LL2HBITS(a)     ((BN_ULLONG)((a)&BN_MASKl)<>(BN_BITS4-1); \
+        m =(m&BN_MASK2l)<<(BN_BITS4+1); \
+        l=(l+m)&BN_MASK2; if (l < m) h++; \
+        (lo)=l; \
+        (ho)=h; \
+        }
+
+#  define mul_add(r,a,bl,bh,c) { \
+        BN_ULONG l,h; \
+ \
+        h= (a); \
+        l=LBITS(h); \
+        h=HBITS(h); \
+        mul64(l,h,(bl),(bh)); \
+ \
+        /* non-multiply part */ \
+        l=(l+(c))&BN_MASK2; if (l < (c)) h++; \
+        (c)=(r); \
+        l=(l+(c))&BN_MASK2; if (l < (c)) h++; \
+        (c)=h&BN_MASK2; \
+        (r)=l; \
+        }
+
+#  define mul(r,a,bl,bh,c) { \
+        BN_ULONG l,h; \
+ \
+        h= (a); \
+        l=LBITS(h); \
+        h=HBITS(h); \
+        mul64(l,h,(bl),(bh)); \
+ \
+        /* non-multiply part */ \
+        l+=(c); if ((l&BN_MASK2) < (c)) h++; \
+        (c)=h&BN_MASK2; \
+        (r)=l&BN_MASK2; \
+        }
+# endif                         /* !BN_LLONG */
+
+void BN_RECP_CTX_init(BN_RECP_CTX *recp);
+void BN_MONT_CTX_init(BN_MONT_CTX *ctx);
+
+void bn_init(BIGNUM *a);
+void bn_mul_normal(BN_ULONG *r, BN_ULONG *a, int na, BN_ULONG *b, int nb);
+void bn_mul_comba8(BN_ULONG *r, BN_ULONG *a, BN_ULONG *b);
+void bn_mul_comba4(BN_ULONG *r, BN_ULONG *a, BN_ULONG *b);
+void bn_sqr_normal(BN_ULONG *r, const BN_ULONG *a, int n, BN_ULONG *tmp);
+void bn_sqr_comba8(BN_ULONG *r, const BN_ULONG *a);
+void bn_sqr_comba4(BN_ULONG *r, const BN_ULONG *a);
+int bn_cmp_words(const BN_ULONG *a, const BN_ULONG *b, int n);
+int bn_cmp_part_words(const BN_ULONG *a, const BN_ULONG *b, int cl, int dl);
+void bn_mul_recursive(BN_ULONG *r, BN_ULONG *a, BN_ULONG *b, int n2,
+                      int dna, int dnb, BN_ULONG *t);
+void bn_mul_part_recursive(BN_ULONG *r, BN_ULONG *a, BN_ULONG *b,
+                           int n, int tna, int tnb, BN_ULONG *t);
+void bn_sqr_recursive(BN_ULONG *r, const BN_ULONG *a, int n2, BN_ULONG *t);
+void bn_mul_low_normal(BN_ULONG *r, BN_ULONG *a, BN_ULONG *b, int n);
+void bn_mul_low_recursive(BN_ULONG *r, BN_ULONG *a, BN_ULONG *b, int n2,
+                          BN_ULONG *t);
+void bn_mul_high(BN_ULONG *r, BN_ULONG *a, BN_ULONG *b, BN_ULONG *l, int n2,
+                 BN_ULONG *t);
+BN_ULONG bn_add_part_words(BN_ULONG *r, const BN_ULONG *a, const BN_ULONG *b,
+                           int cl, int dl);
+BN_ULONG bn_sub_part_words(BN_ULONG *r, const BN_ULONG *a, const BN_ULONG *b,
+                           int cl, int dl);
+int bn_mul_mont(BN_ULONG *rp, const BN_ULONG *ap, const BN_ULONG *bp,
+                const BN_ULONG *np, const BN_ULONG *n0, int num);
+
+BIGNUM *int_bn_mod_inverse(BIGNUM *in,
+                           const BIGNUM *a, const BIGNUM *n, BN_CTX *ctx,
+                           int *noinv);
+
+int bn_probable_prime_dh(BIGNUM *rnd, int bits,
+                         const BIGNUM *add, const BIGNUM *rem, BN_CTX *ctx);
+int bn_probable_prime_dh_retry(BIGNUM *rnd, int bits, BN_CTX *ctx);
+int bn_probable_prime_dh_coprime(BIGNUM *rnd, int bits, BN_CTX *ctx);
+
+static ossl_inline BIGNUM *bn_expand(BIGNUM *a, int bits)
+{
+    if (bits > (INT_MAX - BN_BITS2 + 1))
+        return NULL;
+
+    if (((bits+BN_BITS2-1)/BN_BITS2) <= (a)->dmax)
+        return a;
+
+    return bn_expand2((a),(bits+BN_BITS2-1)/BN_BITS2);
+}
+
+#ifdef  __cplusplus
+}
+#endif
+
+#endif
diff --git a/crypto/bn/bn_lib.c b/crypto/bn/bn_lib.c
new file mode 100644
index 000000000..6a635a8fa
--- /dev/null
+++ b/crypto/bn/bn_lib.c
@@ -0,0 +1,1044 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include "internal/cryptlib.h"
+#include "bn_lcl.h"
+#include 
+
+/* This stuff appears to be completely unused, so is deprecated */
+#if OPENSSL_API_COMPAT < 0x00908000L
+/*-
+ * For a 32 bit machine
+ * 2 -   4 ==  128
+ * 3 -   8 ==  256
+ * 4 -  16 ==  512
+ * 5 -  32 == 1024
+ * 6 -  64 == 2048
+ * 7 - 128 == 4096
+ * 8 - 256 == 8192
+ */
+static int bn_limit_bits = 0;
+static int bn_limit_num = 8;    /* (1<= 0) {
+        if (mult > (int)(sizeof(int) * 8) - 1)
+            mult = sizeof(int) * 8 - 1;
+        bn_limit_bits = mult;
+        bn_limit_num = 1 << mult;
+    }
+    if (high >= 0) {
+        if (high > (int)(sizeof(int) * 8) - 1)
+            high = sizeof(int) * 8 - 1;
+        bn_limit_bits_high = high;
+        bn_limit_num_high = 1 << high;
+    }
+    if (low >= 0) {
+        if (low > (int)(sizeof(int) * 8) - 1)
+            low = sizeof(int) * 8 - 1;
+        bn_limit_bits_low = low;
+        bn_limit_num_low = 1 << low;
+    }
+    if (mont >= 0) {
+        if (mont > (int)(sizeof(int) * 8) - 1)
+            mont = sizeof(int) * 8 - 1;
+        bn_limit_bits_mont = mont;
+        bn_limit_num_mont = 1 << mont;
+    }
+}
+
+int BN_get_params(int which)
+{
+    if (which == 0)
+        return (bn_limit_bits);
+    else if (which == 1)
+        return (bn_limit_bits_high);
+    else if (which == 2)
+        return (bn_limit_bits_low);
+    else if (which == 3)
+        return (bn_limit_bits_mont);
+    else
+        return (0);
+}
+#endif
+
+const BIGNUM *BN_value_one(void)
+{
+    static const BN_ULONG data_one = 1L;
+    static const BIGNUM const_one =
+        { (BN_ULONG *)&data_one, 1, 1, 0, BN_FLG_STATIC_DATA };
+
+    return (&const_one);
+}
+
+int BN_num_bits_word(BN_ULONG l)
+{
+    static const unsigned char bits[256] = {
+        0, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4,
+        5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+        6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+        6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+        7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+        7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+        7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+        7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+        8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+        8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+        8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+        8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+        8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+        8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+        8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+        8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+    };
+
+#if defined(SIXTY_FOUR_BIT_LONG)
+    if (l & 0xffffffff00000000L) {
+        if (l & 0xffff000000000000L) {
+            if (l & 0xff00000000000000L) {
+                return (bits[(int)(l >> 56)] + 56);
+            } else
+                return (bits[(int)(l >> 48)] + 48);
+        } else {
+            if (l & 0x0000ff0000000000L) {
+                return (bits[(int)(l >> 40)] + 40);
+            } else
+                return (bits[(int)(l >> 32)] + 32);
+        }
+    } else
+#else
+# ifdef SIXTY_FOUR_BIT
+    if (l & 0xffffffff00000000LL) {
+        if (l & 0xffff000000000000LL) {
+            if (l & 0xff00000000000000LL) {
+                return (bits[(int)(l >> 56)] + 56);
+            } else
+                return (bits[(int)(l >> 48)] + 48);
+        } else {
+            if (l & 0x0000ff0000000000LL) {
+                return (bits[(int)(l >> 40)] + 40);
+            } else
+                return (bits[(int)(l >> 32)] + 32);
+        }
+    } else
+# endif
+#endif
+    {
+#if defined(THIRTY_TWO_BIT) || defined(SIXTY_FOUR_BIT) || defined(SIXTY_FOUR_BIT_LONG)
+        if (l & 0xffff0000L) {
+            if (l & 0xff000000L)
+                return (bits[(int)(l >> 24L)] + 24);
+            else
+                return (bits[(int)(l >> 16L)] + 16);
+        } else
+#endif
+        {
+#if defined(THIRTY_TWO_BIT) || defined(SIXTY_FOUR_BIT) || defined(SIXTY_FOUR_BIT_LONG)
+            if (l & 0xff00L)
+                return (bits[(int)(l >> 8)] + 8);
+            else
+#endif
+                return (bits[(int)(l)]);
+        }
+    }
+}
+
+int BN_num_bits(const BIGNUM *a)
+{
+    int i = a->top - 1;
+    bn_check_top(a);
+
+    if (BN_is_zero(a))
+        return 0;
+    return ((i * BN_BITS2) + BN_num_bits_word(a->d[i]));
+}
+
+static void bn_free_d(BIGNUM *a)
+{
+    if (BN_get_flags(a, BN_FLG_SECURE))
+        OPENSSL_secure_free(a->d);
+    else
+        OPENSSL_free(a->d);
+}
+
+
+void BN_clear_free(BIGNUM *a)
+{
+    int i;
+
+    if (a == NULL)
+        return;
+    bn_check_top(a);
+    if (a->d != NULL) {
+        OPENSSL_cleanse(a->d, a->dmax * sizeof(a->d[0]));
+        if (!BN_get_flags(a, BN_FLG_STATIC_DATA))
+            bn_free_d(a);
+    }
+    i = BN_get_flags(a, BN_FLG_MALLOCED);
+    OPENSSL_cleanse(a, sizeof(*a));
+    if (i)
+        OPENSSL_free(a);
+}
+
+void BN_free(BIGNUM *a)
+{
+    if (a == NULL)
+        return;
+    bn_check_top(a);
+    if (!BN_get_flags(a, BN_FLG_STATIC_DATA))
+        bn_free_d(a);
+    if (a->flags & BN_FLG_MALLOCED)
+        OPENSSL_free(a);
+    else {
+#if OPENSSL_API_COMPAT < 0x00908000L
+        a->flags |= BN_FLG_FREE;
+#endif
+        a->d = NULL;
+    }
+}
+
+void bn_init(BIGNUM *a)
+{
+    static BIGNUM nilbn;
+
+    *a = nilbn;
+    bn_check_top(a);
+}
+
+#if OPENSSL_API_COMPAT < 0x10100000L
+void BN_init(BIGNUM *a)
+{
+    bn_init(a);
+}
+#endif
+
+BIGNUM *BN_new(void)
+{
+    BIGNUM *ret;
+
+    if ((ret = OPENSSL_zalloc(sizeof(*ret))) == NULL) {
+        BNerr(BN_F_BN_NEW, ERR_R_MALLOC_FAILURE);
+        return (NULL);
+    }
+    ret->flags = BN_FLG_MALLOCED;
+    bn_check_top(ret);
+    return (ret);
+}
+
+ BIGNUM *BN_secure_new(void)
+ {
+     BIGNUM *ret = BN_new();
+     if (ret != NULL)
+         ret->flags |= BN_FLG_SECURE;
+     return (ret);
+ }
+
+/* This is used by bn_expand2() */
+/* The caller MUST check that words > b->dmax before calling this */
+static BN_ULONG *bn_expand_internal(const BIGNUM *b, int words)
+{
+    BN_ULONG *A, *a = NULL;
+    const BN_ULONG *B;
+    int i;
+
+    bn_check_top(b);
+
+    if (words > (INT_MAX / (4 * BN_BITS2))) {
+        BNerr(BN_F_BN_EXPAND_INTERNAL, BN_R_BIGNUM_TOO_LONG);
+        return NULL;
+    }
+    if (BN_get_flags(b, BN_FLG_STATIC_DATA)) {
+        BNerr(BN_F_BN_EXPAND_INTERNAL, BN_R_EXPAND_ON_STATIC_BIGNUM_DATA);
+        return (NULL);
+    }
+    if (BN_get_flags(b, BN_FLG_SECURE))
+        a = A = OPENSSL_secure_zalloc(words * sizeof(*a));
+    else
+        a = A = OPENSSL_zalloc(words * sizeof(*a));
+    if (A == NULL) {
+        BNerr(BN_F_BN_EXPAND_INTERNAL, ERR_R_MALLOC_FAILURE);
+        return (NULL);
+    }
+
+#if 1
+    B = b->d;
+    /* Check if the previous number needs to be copied */
+    if (B != NULL) {
+        for (i = b->top >> 2; i > 0; i--, A += 4, B += 4) {
+            /*
+             * The fact that the loop is unrolled
+             * 4-wise is a tribute to Intel. It's
+             * the one that doesn't have enough
+             * registers to accommodate more data.
+             * I'd unroll it 8-wise otherwise:-)
+             *
+             *              
+             */
+            BN_ULONG a0, a1, a2, a3;
+            a0 = B[0];
+            a1 = B[1];
+            a2 = B[2];
+            a3 = B[3];
+            A[0] = a0;
+            A[1] = a1;
+            A[2] = a2;
+            A[3] = a3;
+        }
+        switch (b->top & 3) {
+        case 3:
+            A[2] = B[2];
+        case 2:
+            A[1] = B[1];
+        case 1:
+            A[0] = B[0];
+        case 0:
+            /* Without the "case 0" some old optimizers got this wrong. */
+            ;
+        }
+    }
+#else
+    memset(A, 0, sizeof(*A) * words);
+    memcpy(A, b->d, sizeof(b->d[0]) * b->top);
+#endif
+
+    return (a);
+}
+
+/*
+ * This is an internal function that should not be used in applications. It
+ * ensures that 'b' has enough room for a 'words' word number and initialises
+ * any unused part of b->d with leading zeros. It is mostly used by the
+ * various BIGNUM routines. If there is an error, NULL is returned. If not,
+ * 'b' is returned.
+ */
+
+BIGNUM *bn_expand2(BIGNUM *b, int words)
+{
+    bn_check_top(b);
+
+    if (words > b->dmax) {
+        BN_ULONG *a = bn_expand_internal(b, words);
+        if (!a)
+            return NULL;
+        if (b->d) {
+            OPENSSL_cleanse(b->d, b->dmax * sizeof(b->d[0]));
+            bn_free_d(b);
+        }
+        b->d = a;
+        b->dmax = words;
+    }
+
+    bn_check_top(b);
+    return b;
+}
+
+BIGNUM *BN_dup(const BIGNUM *a)
+{
+    BIGNUM *t;
+
+    if (a == NULL)
+        return NULL;
+    bn_check_top(a);
+
+    t = BN_get_flags(a, BN_FLG_SECURE) ? BN_secure_new() : BN_new();
+    if (t == NULL)
+        return NULL;
+    if (!BN_copy(t, a)) {
+        BN_free(t);
+        return NULL;
+    }
+    bn_check_top(t);
+    return t;
+}
+
+BIGNUM *BN_copy(BIGNUM *a, const BIGNUM *b)
+{
+    int i;
+    BN_ULONG *A;
+    const BN_ULONG *B;
+
+    bn_check_top(b);
+
+    if (a == b)
+        return (a);
+    if (bn_wexpand(a, b->top) == NULL)
+        return (NULL);
+
+#if 1
+    A = a->d;
+    B = b->d;
+    for (i = b->top >> 2; i > 0; i--, A += 4, B += 4) {
+        BN_ULONG a0, a1, a2, a3;
+        a0 = B[0];
+        a1 = B[1];
+        a2 = B[2];
+        a3 = B[3];
+        A[0] = a0;
+        A[1] = a1;
+        A[2] = a2;
+        A[3] = a3;
+    }
+    /* ultrix cc workaround, see comments in bn_expand_internal */
+    switch (b->top & 3) {
+    case 3:
+        A[2] = B[2];
+    case 2:
+        A[1] = B[1];
+    case 1:
+        A[0] = B[0];
+    case 0:;
+    }
+#else
+    memcpy(a->d, b->d, sizeof(b->d[0]) * b->top);
+#endif
+
+    a->top = b->top;
+    a->neg = b->neg;
+    bn_check_top(a);
+    return (a);
+}
+
+void BN_swap(BIGNUM *a, BIGNUM *b)
+{
+    int flags_old_a, flags_old_b;
+    BN_ULONG *tmp_d;
+    int tmp_top, tmp_dmax, tmp_neg;
+
+    bn_check_top(a);
+    bn_check_top(b);
+
+    flags_old_a = a->flags;
+    flags_old_b = b->flags;
+
+    tmp_d = a->d;
+    tmp_top = a->top;
+    tmp_dmax = a->dmax;
+    tmp_neg = a->neg;
+
+    a->d = b->d;
+    a->top = b->top;
+    a->dmax = b->dmax;
+    a->neg = b->neg;
+
+    b->d = tmp_d;
+    b->top = tmp_top;
+    b->dmax = tmp_dmax;
+    b->neg = tmp_neg;
+
+    a->flags =
+        (flags_old_a & BN_FLG_MALLOCED) | (flags_old_b & BN_FLG_STATIC_DATA);
+    b->flags =
+        (flags_old_b & BN_FLG_MALLOCED) | (flags_old_a & BN_FLG_STATIC_DATA);
+    bn_check_top(a);
+    bn_check_top(b);
+}
+
+void BN_clear(BIGNUM *a)
+{
+    bn_check_top(a);
+    if (a->d != NULL)
+        OPENSSL_cleanse(a->d, sizeof(*a->d) * a->dmax);
+    a->top = 0;
+    a->neg = 0;
+}
+
+BN_ULONG BN_get_word(const BIGNUM *a)
+{
+    if (a->top > 1)
+        return BN_MASK2;
+    else if (a->top == 1)
+        return a->d[0];
+    /* a->top == 0 */
+    return 0;
+}
+
+int BN_set_word(BIGNUM *a, BN_ULONG w)
+{
+    bn_check_top(a);
+    if (bn_expand(a, (int)sizeof(BN_ULONG) * 8) == NULL)
+        return (0);
+    a->neg = 0;
+    a->d[0] = w;
+    a->top = (w ? 1 : 0);
+    bn_check_top(a);
+    return (1);
+}
+
+BIGNUM *BN_bin2bn(const unsigned char *s, int len, BIGNUM *ret)
+{
+    unsigned int i, m;
+    unsigned int n;
+    BN_ULONG l;
+    BIGNUM *bn = NULL;
+
+    if (ret == NULL)
+        ret = bn = BN_new();
+    if (ret == NULL)
+        return (NULL);
+    bn_check_top(ret);
+    /* Skip leading zero's. */
+    for ( ; len > 0 && *s == 0; s++, len--)
+        continue;
+    n = len;
+    if (n == 0) {
+        ret->top = 0;
+        return (ret);
+    }
+    i = ((n - 1) / BN_BYTES) + 1;
+    m = ((n - 1) % (BN_BYTES));
+    if (bn_wexpand(ret, (int)i) == NULL) {
+        BN_free(bn);
+        return NULL;
+    }
+    ret->top = i;
+    ret->neg = 0;
+    l = 0;
+    while (n--) {
+        l = (l << 8L) | *(s++);
+        if (m-- == 0) {
+            ret->d[--i] = l;
+            l = 0;
+            m = BN_BYTES - 1;
+        }
+    }
+    /*
+     * need to call this due to clear byte at top if avoiding having the top
+     * bit set (-ve number)
+     */
+    bn_correct_top(ret);
+    return (ret);
+}
+
+/* ignore negative */
+static int bn2binpad(const BIGNUM *a, unsigned char *to, int tolen)
+{
+    int i;
+    BN_ULONG l;
+
+    bn_check_top(a);
+    i = BN_num_bytes(a);
+    if (tolen == -1)
+        tolen = i;
+    else if (tolen < i)
+        return -1;
+    /* Add leading zeroes if necessary */
+    if (tolen > i) {
+        memset(to, 0, tolen - i);
+        to += tolen - i;
+    }
+    while (i--) {
+        l = a->d[i / BN_BYTES];
+        *(to++) = (unsigned char)(l >> (8 * (i % BN_BYTES))) & 0xff;
+    }
+    return tolen;
+}
+
+int BN_bn2binpad(const BIGNUM *a, unsigned char *to, int tolen)
+{
+    if (tolen < 0)
+        return -1;
+    return bn2binpad(a, to, tolen);
+}
+
+int BN_bn2bin(const BIGNUM *a, unsigned char *to)
+{
+    return bn2binpad(a, to, -1);
+}
+
+BIGNUM *BN_lebin2bn(const unsigned char *s, int len, BIGNUM *ret)
+{
+    unsigned int i, m;
+    unsigned int n;
+    BN_ULONG l;
+    BIGNUM *bn = NULL;
+
+    if (ret == NULL)
+        ret = bn = BN_new();
+    if (ret == NULL)
+        return (NULL);
+    bn_check_top(ret);
+    s += len;
+    /* Skip trailing zeroes. */
+    for ( ; len > 0 && s[-1] == 0; s--, len--)
+        continue;
+    n = len;
+    if (n == 0) {
+        ret->top = 0;
+        return ret;
+    }
+    i = ((n - 1) / BN_BYTES) + 1;
+    m = ((n - 1) % (BN_BYTES));
+    if (bn_wexpand(ret, (int)i) == NULL) {
+        BN_free(bn);
+        return NULL;
+    }
+    ret->top = i;
+    ret->neg = 0;
+    l = 0;
+    while (n--) {
+        s--;
+        l = (l << 8L) | *s;
+        if (m-- == 0) {
+            ret->d[--i] = l;
+            l = 0;
+            m = BN_BYTES - 1;
+        }
+    }
+    /*
+     * need to call this due to clear byte at top if avoiding having the top
+     * bit set (-ve number)
+     */
+    bn_correct_top(ret);
+    return ret;
+}
+
+int BN_bn2lebinpad(const BIGNUM *a, unsigned char *to, int tolen)
+{
+    int i;
+    BN_ULONG l;
+    bn_check_top(a);
+    i = BN_num_bytes(a);
+    if (tolen < i)
+        return -1;
+    /* Add trailing zeroes if necessary */
+    if (tolen > i)
+        memset(to + i, 0, tolen - i);
+    to += i;
+    while (i--) {
+        l = a->d[i / BN_BYTES];
+        to--;
+        *to = (unsigned char)(l >> (8 * (i % BN_BYTES))) & 0xff;
+    }
+    return tolen;
+}
+
+int BN_ucmp(const BIGNUM *a, const BIGNUM *b)
+{
+    int i;
+    BN_ULONG t1, t2, *ap, *bp;
+
+    bn_check_top(a);
+    bn_check_top(b);
+
+    i = a->top - b->top;
+    if (i != 0)
+        return (i);
+    ap = a->d;
+    bp = b->d;
+    for (i = a->top - 1; i >= 0; i--) {
+        t1 = ap[i];
+        t2 = bp[i];
+        if (t1 != t2)
+            return ((t1 > t2) ? 1 : -1);
+    }
+    return (0);
+}
+
+int BN_cmp(const BIGNUM *a, const BIGNUM *b)
+{
+    int i;
+    int gt, lt;
+    BN_ULONG t1, t2;
+
+    if ((a == NULL) || (b == NULL)) {
+        if (a != NULL)
+            return (-1);
+        else if (b != NULL)
+            return (1);
+        else
+            return (0);
+    }
+
+    bn_check_top(a);
+    bn_check_top(b);
+
+    if (a->neg != b->neg) {
+        if (a->neg)
+            return (-1);
+        else
+            return (1);
+    }
+    if (a->neg == 0) {
+        gt = 1;
+        lt = -1;
+    } else {
+        gt = -1;
+        lt = 1;
+    }
+
+    if (a->top > b->top)
+        return (gt);
+    if (a->top < b->top)
+        return (lt);
+    for (i = a->top - 1; i >= 0; i--) {
+        t1 = a->d[i];
+        t2 = b->d[i];
+        if (t1 > t2)
+            return (gt);
+        if (t1 < t2)
+            return (lt);
+    }
+    return (0);
+}
+
+int BN_set_bit(BIGNUM *a, int n)
+{
+    int i, j, k;
+
+    if (n < 0)
+        return 0;
+
+    i = n / BN_BITS2;
+    j = n % BN_BITS2;
+    if (a->top <= i) {
+        if (bn_wexpand(a, i + 1) == NULL)
+            return (0);
+        for (k = a->top; k < i + 1; k++)
+            a->d[k] = 0;
+        a->top = i + 1;
+    }
+
+    a->d[i] |= (((BN_ULONG)1) << j);
+    bn_check_top(a);
+    return (1);
+}
+
+int BN_clear_bit(BIGNUM *a, int n)
+{
+    int i, j;
+
+    bn_check_top(a);
+    if (n < 0)
+        return 0;
+
+    i = n / BN_BITS2;
+    j = n % BN_BITS2;
+    if (a->top <= i)
+        return (0);
+
+    a->d[i] &= (~(((BN_ULONG)1) << j));
+    bn_correct_top(a);
+    return (1);
+}
+
+int BN_is_bit_set(const BIGNUM *a, int n)
+{
+    int i, j;
+
+    bn_check_top(a);
+    if (n < 0)
+        return 0;
+    i = n / BN_BITS2;
+    j = n % BN_BITS2;
+    if (a->top <= i)
+        return 0;
+    return (int)(((a->d[i]) >> j) & ((BN_ULONG)1));
+}
+
+int BN_mask_bits(BIGNUM *a, int n)
+{
+    int b, w;
+
+    bn_check_top(a);
+    if (n < 0)
+        return 0;
+
+    w = n / BN_BITS2;
+    b = n % BN_BITS2;
+    if (w >= a->top)
+        return 0;
+    if (b == 0)
+        a->top = w;
+    else {
+        a->top = w + 1;
+        a->d[w] &= ~(BN_MASK2 << b);
+    }
+    bn_correct_top(a);
+    return (1);
+}
+
+void BN_set_negative(BIGNUM *a, int b)
+{
+    if (b && !BN_is_zero(a))
+        a->neg = 1;
+    else
+        a->neg = 0;
+}
+
+int bn_cmp_words(const BN_ULONG *a, const BN_ULONG *b, int n)
+{
+    int i;
+    BN_ULONG aa, bb;
+
+    aa = a[n - 1];
+    bb = b[n - 1];
+    if (aa != bb)
+        return ((aa > bb) ? 1 : -1);
+    for (i = n - 2; i >= 0; i--) {
+        aa = a[i];
+        bb = b[i];
+        if (aa != bb)
+            return ((aa > bb) ? 1 : -1);
+    }
+    return (0);
+}
+
+/*
+ * Here follows a specialised variants of bn_cmp_words().  It has the
+ * capability of performing the operation on arrays of different sizes. The
+ * sizes of those arrays is expressed through cl, which is the common length
+ * ( basically, min(len(a),len(b)) ), and dl, which is the delta between the
+ * two lengths, calculated as len(a)-len(b). All lengths are the number of
+ * BN_ULONGs...
+ */
+
+int bn_cmp_part_words(const BN_ULONG *a, const BN_ULONG *b, int cl, int dl)
+{
+    int n, i;
+    n = cl - 1;
+
+    if (dl < 0) {
+        for (i = dl; i < 0; i++) {
+            if (b[n - i] != 0)
+                return -1;      /* a < b */
+        }
+    }
+    if (dl > 0) {
+        for (i = dl; i > 0; i--) {
+            if (a[n + i] != 0)
+                return 1;       /* a > b */
+        }
+    }
+    return bn_cmp_words(a, b, cl);
+}
+
+/*
+ * Constant-time conditional swap of a and b.
+ * a and b are swapped if condition is not 0.  The code assumes that at most one bit of condition is set.
+ * nwords is the number of words to swap.  The code assumes that at least nwords are allocated in both a and b,
+ * and that no more than nwords are used by either a or b.
+ * a and b cannot be the same number
+ */
+void BN_consttime_swap(BN_ULONG condition, BIGNUM *a, BIGNUM *b, int nwords)
+{
+    BN_ULONG t;
+    int i;
+
+    bn_wcheck_size(a, nwords);
+    bn_wcheck_size(b, nwords);
+
+    assert(a != b);
+    assert((condition & (condition - 1)) == 0);
+    assert(sizeof(BN_ULONG) >= sizeof(int));
+
+    condition = ((condition - 1) >> (BN_BITS2 - 1)) - 1;
+
+    t = (a->top ^ b->top) & condition;
+    a->top ^= t;
+    b->top ^= t;
+
+#define BN_CONSTTIME_SWAP(ind) \
+        do { \
+                t = (a->d[ind] ^ b->d[ind]) & condition; \
+                a->d[ind] ^= t; \
+                b->d[ind] ^= t; \
+        } while (0)
+
+    switch (nwords) {
+    default:
+        for (i = 10; i < nwords; i++)
+            BN_CONSTTIME_SWAP(i);
+        /* Fallthrough */
+    case 10:
+        BN_CONSTTIME_SWAP(9);   /* Fallthrough */
+    case 9:
+        BN_CONSTTIME_SWAP(8);   /* Fallthrough */
+    case 8:
+        BN_CONSTTIME_SWAP(7);   /* Fallthrough */
+    case 7:
+        BN_CONSTTIME_SWAP(6);   /* Fallthrough */
+    case 6:
+        BN_CONSTTIME_SWAP(5);   /* Fallthrough */
+    case 5:
+        BN_CONSTTIME_SWAP(4);   /* Fallthrough */
+    case 4:
+        BN_CONSTTIME_SWAP(3);   /* Fallthrough */
+    case 3:
+        BN_CONSTTIME_SWAP(2);   /* Fallthrough */
+    case 2:
+        BN_CONSTTIME_SWAP(1);   /* Fallthrough */
+    case 1:
+        BN_CONSTTIME_SWAP(0);
+    }
+#undef BN_CONSTTIME_SWAP
+}
+
+/* Bits of security, see SP800-57 */
+
+int BN_security_bits(int L, int N)
+{
+    int secbits, bits;
+    if (L >= 15360)
+        secbits = 256;
+    else if (L >= 7690)
+        secbits = 192;
+    else if (L >= 3072)
+        secbits = 128;
+    else if (L >= 2048)
+        secbits = 112;
+    else if (L >= 1024)
+        secbits = 80;
+    else
+        return 0;
+    if (N == -1)
+        return secbits;
+    bits = N / 2;
+    if (bits < 80)
+        return 0;
+    return bits >= secbits ? secbits : bits;
+}
+
+void BN_zero_ex(BIGNUM *a)
+{
+    a->top = 0;
+    a->neg = 0;
+}
+
+int BN_abs_is_word(const BIGNUM *a, const BN_ULONG w)
+{
+    return ((a->top == 1) && (a->d[0] == w)) || ((w == 0) && (a->top == 0));
+}
+
+int BN_is_zero(const BIGNUM *a)
+{
+    return a->top == 0;
+}
+
+int BN_is_one(const BIGNUM *a)
+{
+    return BN_abs_is_word(a, 1) && !a->neg;
+}
+
+int BN_is_word(const BIGNUM *a, const BN_ULONG w)
+{
+    return BN_abs_is_word(a, w) && (!w || !a->neg);
+}
+
+int BN_is_odd(const BIGNUM *a)
+{
+    return (a->top > 0) && (a->d[0] & 1);
+}
+
+int BN_is_negative(const BIGNUM *a)
+{
+    return (a->neg != 0);
+}
+
+int BN_to_montgomery(BIGNUM *r, const BIGNUM *a, BN_MONT_CTX *mont,
+                     BN_CTX *ctx)
+{
+    return BN_mod_mul_montgomery(r, a, &(mont->RR), mont, ctx);
+}
+
+void BN_with_flags(BIGNUM *dest, const BIGNUM *b, int flags)
+{
+    dest->d = b->d;
+    dest->top = b->top;
+    dest->dmax = b->dmax;
+    dest->neg = b->neg;
+    dest->flags = ((dest->flags & BN_FLG_MALLOCED)
+                   | (b->flags & ~BN_FLG_MALLOCED)
+                   | BN_FLG_STATIC_DATA | flags);
+}
+
+BN_GENCB *BN_GENCB_new(void)
+{
+    BN_GENCB *ret;
+
+    if ((ret = OPENSSL_malloc(sizeof(*ret))) == NULL) {
+        BNerr(BN_F_BN_GENCB_NEW, ERR_R_MALLOC_FAILURE);
+        return (NULL);
+    }
+
+    return ret;
+}
+
+void BN_GENCB_free(BN_GENCB *cb)
+{
+    if (cb == NULL)
+        return;
+    OPENSSL_free(cb);
+}
+
+void BN_set_flags(BIGNUM *b, int n)
+{
+    b->flags |= n;
+}
+
+int BN_get_flags(const BIGNUM *b, int n)
+{
+    return b->flags & n;
+}
+
+/* Populate a BN_GENCB structure with an "old"-style callback */
+void BN_GENCB_set_old(BN_GENCB *gencb, void (*callback) (int, int, void *),
+                      void *cb_arg)
+{
+    BN_GENCB *tmp_gencb = gencb;
+    tmp_gencb->ver = 1;
+    tmp_gencb->arg = cb_arg;
+    tmp_gencb->cb.cb_1 = callback;
+}
+
+/* Populate a BN_GENCB structure with a "new"-style callback */
+void BN_GENCB_set(BN_GENCB *gencb, int (*callback) (int, int, BN_GENCB *),
+                  void *cb_arg)
+{
+    BN_GENCB *tmp_gencb = gencb;
+    tmp_gencb->ver = 2;
+    tmp_gencb->arg = cb_arg;
+    tmp_gencb->cb.cb_2 = callback;
+}
+
+void *BN_GENCB_get_arg(BN_GENCB *cb)
+{
+    return cb->arg;
+}
+
+BIGNUM *bn_wexpand(BIGNUM *a, int words)
+{
+    return (words <= a->dmax) ? a : bn_expand2(a, words);
+}
+
+void bn_correct_top(BIGNUM *a)
+{
+    BN_ULONG *ftl;
+    int tmp_top = a->top;
+
+    if (tmp_top > 0) {
+        for (ftl = &(a->d[tmp_top]); tmp_top > 0; tmp_top--) {
+            ftl--;
+            if (*ftl != 0)
+                break;
+        }
+        a->top = tmp_top;
+    }
+    if (a->top == 0)
+        a->neg = 0;
+    bn_pollute(a);
+}
diff --git a/crypto/bn/bn_mod.c b/crypto/bn/bn_mod.c
new file mode 100644
index 000000000..13b583f76
--- /dev/null
+++ b/crypto/bn/bn_mod.c
@@ -0,0 +1,201 @@
+/*
+ * Copyright 1998-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "internal/cryptlib.h"
+#include "bn_lcl.h"
+
+int BN_nnmod(BIGNUM *r, const BIGNUM *m, const BIGNUM *d, BN_CTX *ctx)
+{
+    /*
+     * like BN_mod, but returns non-negative remainder (i.e., 0 <= r < |d|
+     * always holds)
+     */
+
+    if (!(BN_mod(r, m, d, ctx)))
+        return 0;
+    if (!r->neg)
+        return 1;
+    /* now   -|d| < r < 0,  so we have to set  r := r + |d| */
+    return (d->neg ? BN_sub : BN_add) (r, r, d);
+}
+
+int BN_mod_add(BIGNUM *r, const BIGNUM *a, const BIGNUM *b, const BIGNUM *m,
+               BN_CTX *ctx)
+{
+    if (!BN_add(r, a, b))
+        return 0;
+    return BN_nnmod(r, r, m, ctx);
+}
+
+/*
+ * BN_mod_add variant that may be used if both a and b are non-negative and
+ * less than m
+ */
+int BN_mod_add_quick(BIGNUM *r, const BIGNUM *a, const BIGNUM *b,
+                     const BIGNUM *m)
+{
+    if (!BN_uadd(r, a, b))
+        return 0;
+    if (BN_ucmp(r, m) >= 0)
+        return BN_usub(r, r, m);
+    return 1;
+}
+
+int BN_mod_sub(BIGNUM *r, const BIGNUM *a, const BIGNUM *b, const BIGNUM *m,
+               BN_CTX *ctx)
+{
+    if (!BN_sub(r, a, b))
+        return 0;
+    return BN_nnmod(r, r, m, ctx);
+}
+
+/*
+ * BN_mod_sub variant that may be used if both a and b are non-negative and
+ * less than m
+ */
+int BN_mod_sub_quick(BIGNUM *r, const BIGNUM *a, const BIGNUM *b,
+                     const BIGNUM *m)
+{
+    if (!BN_sub(r, a, b))
+        return 0;
+    if (r->neg)
+        return BN_add(r, r, m);
+    return 1;
+}
+
+/* slow but works */
+int BN_mod_mul(BIGNUM *r, const BIGNUM *a, const BIGNUM *b, const BIGNUM *m,
+               BN_CTX *ctx)
+{
+    BIGNUM *t;
+    int ret = 0;
+
+    bn_check_top(a);
+    bn_check_top(b);
+    bn_check_top(m);
+
+    BN_CTX_start(ctx);
+    if ((t = BN_CTX_get(ctx)) == NULL)
+        goto err;
+    if (a == b) {
+        if (!BN_sqr(t, a, ctx))
+            goto err;
+    } else {
+        if (!BN_mul(t, a, b, ctx))
+            goto err;
+    }
+    if (!BN_nnmod(r, t, m, ctx))
+        goto err;
+    bn_check_top(r);
+    ret = 1;
+ err:
+    BN_CTX_end(ctx);
+    return (ret);
+}
+
+int BN_mod_sqr(BIGNUM *r, const BIGNUM *a, const BIGNUM *m, BN_CTX *ctx)
+{
+    if (!BN_sqr(r, a, ctx))
+        return 0;
+    /* r->neg == 0,  thus we don't need BN_nnmod */
+    return BN_mod(r, r, m, ctx);
+}
+
+int BN_mod_lshift1(BIGNUM *r, const BIGNUM *a, const BIGNUM *m, BN_CTX *ctx)
+{
+    if (!BN_lshift1(r, a))
+        return 0;
+    bn_check_top(r);
+    return BN_nnmod(r, r, m, ctx);
+}
+
+/*
+ * BN_mod_lshift1 variant that may be used if a is non-negative and less than
+ * m
+ */
+int BN_mod_lshift1_quick(BIGNUM *r, const BIGNUM *a, const BIGNUM *m)
+{
+    if (!BN_lshift1(r, a))
+        return 0;
+    bn_check_top(r);
+    if (BN_cmp(r, m) >= 0)
+        return BN_sub(r, r, m);
+    return 1;
+}
+
+int BN_mod_lshift(BIGNUM *r, const BIGNUM *a, int n, const BIGNUM *m,
+                  BN_CTX *ctx)
+{
+    BIGNUM *abs_m = NULL;
+    int ret;
+
+    if (!BN_nnmod(r, a, m, ctx))
+        return 0;
+
+    if (m->neg) {
+        abs_m = BN_dup(m);
+        if (abs_m == NULL)
+            return 0;
+        abs_m->neg = 0;
+    }
+
+    ret = BN_mod_lshift_quick(r, r, n, (abs_m ? abs_m : m));
+    bn_check_top(r);
+
+    BN_free(abs_m);
+    return ret;
+}
+
+/*
+ * BN_mod_lshift variant that may be used if a is non-negative and less than
+ * m
+ */
+int BN_mod_lshift_quick(BIGNUM *r, const BIGNUM *a, int n, const BIGNUM *m)
+{
+    if (r != a) {
+        if (BN_copy(r, a) == NULL)
+            return 0;
+    }
+
+    while (n > 0) {
+        int max_shift;
+
+        /* 0 < r < m */
+        max_shift = BN_num_bits(m) - BN_num_bits(r);
+        /* max_shift >= 0 */
+
+        if (max_shift < 0) {
+            BNerr(BN_F_BN_MOD_LSHIFT_QUICK, BN_R_INPUT_NOT_REDUCED);
+            return 0;
+        }
+
+        if (max_shift > n)
+            max_shift = n;
+
+        if (max_shift) {
+            if (!BN_lshift(r, r, max_shift))
+                return 0;
+            n -= max_shift;
+        } else {
+            if (!BN_lshift1(r, r))
+                return 0;
+            --n;
+        }
+
+        /* BN_num_bits(r) <= BN_num_bits(m) */
+
+        if (BN_cmp(r, m) >= 0) {
+            if (!BN_sub(r, r, m))
+                return 0;
+        }
+    }
+    bn_check_top(r);
+
+    return 1;
+}
diff --git a/crypto/bn/bn_mont.c b/crypto/bn/bn_mont.c
new file mode 100644
index 000000000..6d37279a5
--- /dev/null
+++ b/crypto/bn/bn_mont.c
@@ -0,0 +1,434 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * Details about Montgomery multiplication algorithms can be found at
+ * http://security.ece.orst.edu/publications.html, e.g.
+ * http://security.ece.orst.edu/koc/papers/j37acmon.pdf and
+ * sections 3.8 and 4.2 in http://security.ece.orst.edu/koc/papers/r01rsasw.pdf
+ */
+
+#include "internal/cryptlib.h"
+#include "bn_lcl.h"
+
+#define MONT_WORD               /* use the faster word-based algorithm */
+
+#ifdef MONT_WORD
+static int BN_from_montgomery_word(BIGNUM *ret, BIGNUM *r, BN_MONT_CTX *mont);
+#endif
+
+int BN_mod_mul_montgomery(BIGNUM *r, const BIGNUM *a, const BIGNUM *b,
+                          BN_MONT_CTX *mont, BN_CTX *ctx)
+{
+    BIGNUM *tmp;
+    int ret = 0;
+#if defined(OPENSSL_BN_ASM_MONT) && defined(MONT_WORD)
+    int num = mont->N.top;
+
+    if (num > 1 && a->top == num && b->top == num) {
+        if (bn_wexpand(r, num) == NULL)
+            return (0);
+        if (bn_mul_mont(r->d, a->d, b->d, mont->N.d, mont->n0, num)) {
+            r->neg = a->neg ^ b->neg;
+            r->top = num;
+            bn_correct_top(r);
+            return (1);
+        }
+    }
+#endif
+
+    BN_CTX_start(ctx);
+    tmp = BN_CTX_get(ctx);
+    if (tmp == NULL)
+        goto err;
+
+    bn_check_top(tmp);
+    if (a == b) {
+        if (!BN_sqr(tmp, a, ctx))
+            goto err;
+    } else {
+        if (!BN_mul(tmp, a, b, ctx))
+            goto err;
+    }
+    /* reduce from aRR to aR */
+#ifdef MONT_WORD
+    if (!BN_from_montgomery_word(r, tmp, mont))
+        goto err;
+#else
+    if (!BN_from_montgomery(r, tmp, mont, ctx))
+        goto err;
+#endif
+    bn_check_top(r);
+    ret = 1;
+ err:
+    BN_CTX_end(ctx);
+    return (ret);
+}
+
+#ifdef MONT_WORD
+static int BN_from_montgomery_word(BIGNUM *ret, BIGNUM *r, BN_MONT_CTX *mont)
+{
+    BIGNUM *n;
+    BN_ULONG *ap, *np, *rp, n0, v, carry;
+    int nl, max, i;
+
+    n = &(mont->N);
+    nl = n->top;
+    if (nl == 0) {
+        ret->top = 0;
+        return (1);
+    }
+
+    max = (2 * nl);             /* carry is stored separately */
+    if (bn_wexpand(r, max) == NULL)
+        return (0);
+
+    r->neg ^= n->neg;
+    np = n->d;
+    rp = r->d;
+
+    /* clear the top words of T */
+    i = max - r->top;
+    if (i)
+        memset(&rp[r->top], 0, sizeof(*rp) * i);
+
+    r->top = max;
+    n0 = mont->n0[0];
+
+    for (carry = 0, i = 0; i < nl; i++, rp++) {
+        v = bn_mul_add_words(rp, np, nl, (rp[0] * n0) & BN_MASK2);
+        v = (v + carry + rp[nl]) & BN_MASK2;
+        carry |= (v != rp[nl]);
+        carry &= (v <= rp[nl]);
+        rp[nl] = v;
+    }
+
+    if (bn_wexpand(ret, nl) == NULL)
+        return (0);
+    ret->top = nl;
+    ret->neg = r->neg;
+
+    rp = ret->d;
+    ap = &(r->d[nl]);
+
+# define BRANCH_FREE 1
+# if BRANCH_FREE
+    {
+        BN_ULONG *nrp;
+        size_t m;
+
+        v = bn_sub_words(rp, ap, np, nl) - carry;
+        /*
+         * if subtraction result is real, then trick unconditional memcpy
+         * below to perform in-place "refresh" instead of actual copy.
+         */
+        m = (0 - (size_t)v);
+        nrp =
+            (BN_ULONG *)(((PTR_SIZE_INT) rp & ~m) | ((PTR_SIZE_INT) ap & m));
+
+        for (i = 0, nl -= 4; i < nl; i += 4) {
+            BN_ULONG t1, t2, t3, t4;
+
+            t1 = nrp[i + 0];
+            t2 = nrp[i + 1];
+            t3 = nrp[i + 2];
+            ap[i + 0] = 0;
+            t4 = nrp[i + 3];
+            ap[i + 1] = 0;
+            rp[i + 0] = t1;
+            ap[i + 2] = 0;
+            rp[i + 1] = t2;
+            ap[i + 3] = 0;
+            rp[i + 2] = t3;
+            rp[i + 3] = t4;
+        }
+        for (nl += 4; i < nl; i++)
+            rp[i] = nrp[i], ap[i] = 0;
+    }
+# else
+    if (bn_sub_words(rp, ap, np, nl) - carry)
+        memcpy(rp, ap, nl * sizeof(BN_ULONG));
+# endif
+    bn_correct_top(r);
+    bn_correct_top(ret);
+    bn_check_top(ret);
+
+    return (1);
+}
+#endif                          /* MONT_WORD */
+
+int BN_from_montgomery(BIGNUM *ret, const BIGNUM *a, BN_MONT_CTX *mont,
+                       BN_CTX *ctx)
+{
+    int retn = 0;
+#ifdef MONT_WORD
+    BIGNUM *t;
+
+    BN_CTX_start(ctx);
+    if ((t = BN_CTX_get(ctx)) && BN_copy(t, a))
+        retn = BN_from_montgomery_word(ret, t, mont);
+    BN_CTX_end(ctx);
+#else                           /* !MONT_WORD */
+    BIGNUM *t1, *t2;
+
+    BN_CTX_start(ctx);
+    t1 = BN_CTX_get(ctx);
+    t2 = BN_CTX_get(ctx);
+    if (t1 == NULL || t2 == NULL)
+        goto err;
+
+    if (!BN_copy(t1, a))
+        goto err;
+    BN_mask_bits(t1, mont->ri);
+
+    if (!BN_mul(t2, t1, &mont->Ni, ctx))
+        goto err;
+    BN_mask_bits(t2, mont->ri);
+
+    if (!BN_mul(t1, t2, &mont->N, ctx))
+        goto err;
+    if (!BN_add(t2, a, t1))
+        goto err;
+    if (!BN_rshift(ret, t2, mont->ri))
+        goto err;
+
+    if (BN_ucmp(ret, &(mont->N)) >= 0) {
+        if (!BN_usub(ret, ret, &(mont->N)))
+            goto err;
+    }
+    retn = 1;
+    bn_check_top(ret);
+ err:
+    BN_CTX_end(ctx);
+#endif                          /* MONT_WORD */
+    return (retn);
+}
+
+BN_MONT_CTX *BN_MONT_CTX_new(void)
+{
+    BN_MONT_CTX *ret;
+
+    if ((ret = OPENSSL_malloc(sizeof(*ret))) == NULL)
+        return (NULL);
+
+    BN_MONT_CTX_init(ret);
+    ret->flags = BN_FLG_MALLOCED;
+    return (ret);
+}
+
+void BN_MONT_CTX_init(BN_MONT_CTX *ctx)
+{
+    ctx->ri = 0;
+    bn_init(&(ctx->RR));
+    bn_init(&(ctx->N));
+    bn_init(&(ctx->Ni));
+    ctx->n0[0] = ctx->n0[1] = 0;
+    ctx->flags = 0;
+}
+
+void BN_MONT_CTX_free(BN_MONT_CTX *mont)
+{
+    if (mont == NULL)
+        return;
+
+    BN_clear_free(&(mont->RR));
+    BN_clear_free(&(mont->N));
+    BN_clear_free(&(mont->Ni));
+    if (mont->flags & BN_FLG_MALLOCED)
+        OPENSSL_free(mont);
+}
+
+int BN_MONT_CTX_set(BN_MONT_CTX *mont, const BIGNUM *mod, BN_CTX *ctx)
+{
+    int ret = 0;
+    BIGNUM *Ri, *R;
+
+    if (BN_is_zero(mod))
+        return 0;
+
+    BN_CTX_start(ctx);
+    if ((Ri = BN_CTX_get(ctx)) == NULL)
+        goto err;
+    R = &(mont->RR);            /* grab RR as a temp */
+    if (!BN_copy(&(mont->N), mod))
+        goto err;               /* Set N */
+    mont->N.neg = 0;
+
+#ifdef MONT_WORD
+    {
+        BIGNUM tmod;
+        BN_ULONG buf[2];
+
+        bn_init(&tmod);
+        tmod.d = buf;
+        tmod.dmax = 2;
+        tmod.neg = 0;
+
+        mont->ri = (BN_num_bits(mod) + (BN_BITS2 - 1)) / BN_BITS2 * BN_BITS2;
+
+# if defined(OPENSSL_BN_ASM_MONT) && (BN_BITS2<=32)
+        /*
+         * Only certain BN_BITS2<=32 platforms actually make use of n0[1],
+         * and we could use the #else case (with a shorter R value) for the
+         * others.  However, currently only the assembler files do know which
+         * is which.
+         */
+
+        BN_zero(R);
+        if (!(BN_set_bit(R, 2 * BN_BITS2)))
+            goto err;
+
+        tmod.top = 0;
+        if ((buf[0] = mod->d[0]))
+            tmod.top = 1;
+        if ((buf[1] = mod->top > 1 ? mod->d[1] : 0))
+            tmod.top = 2;
+
+        if ((BN_mod_inverse(Ri, R, &tmod, ctx)) == NULL)
+            goto err;
+        if (!BN_lshift(Ri, Ri, 2 * BN_BITS2))
+            goto err;           /* R*Ri */
+        if (!BN_is_zero(Ri)) {
+            if (!BN_sub_word(Ri, 1))
+                goto err;
+        } else {                /* if N mod word size == 1 */
+
+            if (bn_expand(Ri, (int)sizeof(BN_ULONG) * 2) == NULL)
+                goto err;
+            /* Ri-- (mod double word size) */
+            Ri->neg = 0;
+            Ri->d[0] = BN_MASK2;
+            Ri->d[1] = BN_MASK2;
+            Ri->top = 2;
+        }
+        if (!BN_div(Ri, NULL, Ri, &tmod, ctx))
+            goto err;
+        /*
+         * Ni = (R*Ri-1)/N, keep only couple of least significant words:
+         */
+        mont->n0[0] = (Ri->top > 0) ? Ri->d[0] : 0;
+        mont->n0[1] = (Ri->top > 1) ? Ri->d[1] : 0;
+# else
+        BN_zero(R);
+        if (!(BN_set_bit(R, BN_BITS2)))
+            goto err;           /* R */
+
+        buf[0] = mod->d[0];     /* tmod = N mod word size */
+        buf[1] = 0;
+        tmod.top = buf[0] != 0 ? 1 : 0;
+        /* Ri = R^-1 mod N */
+        if ((BN_mod_inverse(Ri, R, &tmod, ctx)) == NULL)
+            goto err;
+        if (!BN_lshift(Ri, Ri, BN_BITS2))
+            goto err;           /* R*Ri */
+        if (!BN_is_zero(Ri)) {
+            if (!BN_sub_word(Ri, 1))
+                goto err;
+        } else {                /* if N mod word size == 1 */
+
+            if (!BN_set_word(Ri, BN_MASK2))
+                goto err;       /* Ri-- (mod word size) */
+        }
+        if (!BN_div(Ri, NULL, Ri, &tmod, ctx))
+            goto err;
+        /*
+         * Ni = (R*Ri-1)/N, keep only least significant word:
+         */
+        mont->n0[0] = (Ri->top > 0) ? Ri->d[0] : 0;
+        mont->n0[1] = 0;
+# endif
+    }
+#else                           /* !MONT_WORD */
+    {                           /* bignum version */
+        mont->ri = BN_num_bits(&mont->N);
+        BN_zero(R);
+        if (!BN_set_bit(R, mont->ri))
+            goto err;           /* R = 2^ri */
+        /* Ri = R^-1 mod N */
+        if ((BN_mod_inverse(Ri, R, &mont->N, ctx)) == NULL)
+            goto err;
+        if (!BN_lshift(Ri, Ri, mont->ri))
+            goto err;           /* R*Ri */
+        if (!BN_sub_word(Ri, 1))
+            goto err;
+        /*
+         * Ni = (R*Ri-1) / N
+         */
+        if (!BN_div(&(mont->Ni), NULL, Ri, &mont->N, ctx))
+            goto err;
+    }
+#endif
+
+    /* setup RR for conversions */
+    BN_zero(&(mont->RR));
+    if (!BN_set_bit(&(mont->RR), mont->ri * 2))
+        goto err;
+    if (!BN_mod(&(mont->RR), &(mont->RR), &(mont->N), ctx))
+        goto err;
+
+    ret = 1;
+ err:
+    BN_CTX_end(ctx);
+    return ret;
+}
+
+BN_MONT_CTX *BN_MONT_CTX_copy(BN_MONT_CTX *to, BN_MONT_CTX *from)
+{
+    if (to == from)
+        return (to);
+
+    if (!BN_copy(&(to->RR), &(from->RR)))
+        return NULL;
+    if (!BN_copy(&(to->N), &(from->N)))
+        return NULL;
+    if (!BN_copy(&(to->Ni), &(from->Ni)))
+        return NULL;
+    to->ri = from->ri;
+    to->n0[0] = from->n0[0];
+    to->n0[1] = from->n0[1];
+    return (to);
+}
+
+BN_MONT_CTX *BN_MONT_CTX_set_locked(BN_MONT_CTX **pmont, CRYPTO_RWLOCK *lock,
+                                    const BIGNUM *mod, BN_CTX *ctx)
+{
+    BN_MONT_CTX *ret;
+
+    CRYPTO_THREAD_read_lock(lock);
+    ret = *pmont;
+    CRYPTO_THREAD_unlock(lock);
+    if (ret)
+        return ret;
+
+    /*
+     * We don't want to serialise globally while doing our lazy-init math in
+     * BN_MONT_CTX_set. That punishes threads that are doing independent
+     * things. Instead, punish the case where more than one thread tries to
+     * lazy-init the same 'pmont', by having each do the lazy-init math work
+     * independently and only use the one from the thread that wins the race
+     * (the losers throw away the work they've done).
+     */
+    ret = BN_MONT_CTX_new();
+    if (ret == NULL)
+        return NULL;
+    if (!BN_MONT_CTX_set(ret, mod, ctx)) {
+        BN_MONT_CTX_free(ret);
+        return NULL;
+    }
+
+    /* The locked compare-and-set, after the local work is done. */
+    CRYPTO_THREAD_write_lock(lock);
+    if (*pmont) {
+        BN_MONT_CTX_free(ret);
+        ret = *pmont;
+    } else
+        *pmont = ret;
+    CRYPTO_THREAD_unlock(lock);
+    return ret;
+}
diff --git a/crypto/bn/bn_mpi.c b/crypto/bn/bn_mpi.c
new file mode 100644
index 000000000..043e21d26
--- /dev/null
+++ b/crypto/bn/bn_mpi.c
@@ -0,0 +1,86 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include "bn_lcl.h"
+
+int BN_bn2mpi(const BIGNUM *a, unsigned char *d)
+{
+    int bits;
+    int num = 0;
+    int ext = 0;
+    long l;
+
+    bits = BN_num_bits(a);
+    num = (bits + 7) / 8;
+    if (bits > 0) {
+        ext = ((bits & 0x07) == 0);
+    }
+    if (d == NULL)
+        return (num + 4 + ext);
+
+    l = num + ext;
+    d[0] = (unsigned char)(l >> 24) & 0xff;
+    d[1] = (unsigned char)(l >> 16) & 0xff;
+    d[2] = (unsigned char)(l >> 8) & 0xff;
+    d[3] = (unsigned char)(l) & 0xff;
+    if (ext)
+        d[4] = 0;
+    num = BN_bn2bin(a, &(d[4 + ext]));
+    if (a->neg)
+        d[4] |= 0x80;
+    return (num + 4 + ext);
+}
+
+BIGNUM *BN_mpi2bn(const unsigned char *d, int n, BIGNUM *ain)
+{
+    long len;
+    int neg = 0;
+    BIGNUM *a = NULL;
+
+    if (n < 4) {
+        BNerr(BN_F_BN_MPI2BN, BN_R_INVALID_LENGTH);
+        return NULL;
+    }
+    len = ((long)d[0] << 24) | ((long)d[1] << 16) | ((int)d[2] << 8) | (int)
+        d[3];
+    if ((len + 4) != n) {
+        BNerr(BN_F_BN_MPI2BN, BN_R_ENCODING_ERROR);
+        return NULL;
+    }
+
+    if (ain == NULL)
+        a = BN_new();
+    else
+        a = ain;
+
+    if (a == NULL)
+        return NULL;
+
+    if (len == 0) {
+        a->neg = 0;
+        a->top = 0;
+        return a;
+    }
+    d += 4;
+    if ((*d) & 0x80)
+        neg = 1;
+    if (BN_bin2bn(d, (int)len, a) == NULL) {
+        if (ain == NULL)
+            BN_free(a);
+        return NULL;
+    }
+    a->neg = neg;
+    if (neg) {
+        BN_clear_bit(a, BN_num_bits(a) - 1);
+    }
+    bn_check_top(a);
+    return a;
+}
diff --git a/crypto/bn/bn_mul.c b/crypto/bn/bn_mul.c
new file mode 100644
index 000000000..4c39d404b
--- /dev/null
+++ b/crypto/bn/bn_mul.c
@@ -0,0 +1,1045 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include "bn_lcl.h"
+
+#if defined(OPENSSL_NO_ASM) || !defined(OPENSSL_BN_ASM_PART_WORDS)
+/*
+ * Here follows specialised variants of bn_add_words() and bn_sub_words().
+ * They have the property performing operations on arrays of different sizes.
+ * The sizes of those arrays is expressed through cl, which is the common
+ * length ( basically, min(len(a),len(b)) ), and dl, which is the delta
+ * between the two lengths, calculated as len(a)-len(b). All lengths are the
+ * number of BN_ULONGs...  For the operations that require a result array as
+ * parameter, it must have the length cl+abs(dl). These functions should
+ * probably end up in bn_asm.c as soon as there are assembler counterparts
+ * for the systems that use assembler files.
+ */
+
+BN_ULONG bn_sub_part_words(BN_ULONG *r,
+                           const BN_ULONG *a, const BN_ULONG *b,
+                           int cl, int dl)
+{
+    BN_ULONG c, t;
+
+    assert(cl >= 0);
+    c = bn_sub_words(r, a, b, cl);
+
+    if (dl == 0)
+        return c;
+
+    r += cl;
+    a += cl;
+    b += cl;
+
+    if (dl < 0) {
+        for (;;) {
+            t = b[0];
+            r[0] = (0 - t - c) & BN_MASK2;
+            if (t != 0)
+                c = 1;
+            if (++dl >= 0)
+                break;
+
+            t = b[1];
+            r[1] = (0 - t - c) & BN_MASK2;
+            if (t != 0)
+                c = 1;
+            if (++dl >= 0)
+                break;
+
+            t = b[2];
+            r[2] = (0 - t - c) & BN_MASK2;
+            if (t != 0)
+                c = 1;
+            if (++dl >= 0)
+                break;
+
+            t = b[3];
+            r[3] = (0 - t - c) & BN_MASK2;
+            if (t != 0)
+                c = 1;
+            if (++dl >= 0)
+                break;
+
+            b += 4;
+            r += 4;
+        }
+    } else {
+        int save_dl = dl;
+        while (c) {
+            t = a[0];
+            r[0] = (t - c) & BN_MASK2;
+            if (t != 0)
+                c = 0;
+            if (--dl <= 0)
+                break;
+
+            t = a[1];
+            r[1] = (t - c) & BN_MASK2;
+            if (t != 0)
+                c = 0;
+            if (--dl <= 0)
+                break;
+
+            t = a[2];
+            r[2] = (t - c) & BN_MASK2;
+            if (t != 0)
+                c = 0;
+            if (--dl <= 0)
+                break;
+
+            t = a[3];
+            r[3] = (t - c) & BN_MASK2;
+            if (t != 0)
+                c = 0;
+            if (--dl <= 0)
+                break;
+
+            save_dl = dl;
+            a += 4;
+            r += 4;
+        }
+        if (dl > 0) {
+            if (save_dl > dl) {
+                switch (save_dl - dl) {
+                case 1:
+                    r[1] = a[1];
+                    if (--dl <= 0)
+                        break;
+                case 2:
+                    r[2] = a[2];
+                    if (--dl <= 0)
+                        break;
+                case 3:
+                    r[3] = a[3];
+                    if (--dl <= 0)
+                        break;
+                }
+                a += 4;
+                r += 4;
+            }
+        }
+        if (dl > 0) {
+            for (;;) {
+                r[0] = a[0];
+                if (--dl <= 0)
+                    break;
+                r[1] = a[1];
+                if (--dl <= 0)
+                    break;
+                r[2] = a[2];
+                if (--dl <= 0)
+                    break;
+                r[3] = a[3];
+                if (--dl <= 0)
+                    break;
+
+                a += 4;
+                r += 4;
+            }
+        }
+    }
+    return c;
+}
+#endif
+
+BN_ULONG bn_add_part_words(BN_ULONG *r,
+                           const BN_ULONG *a, const BN_ULONG *b,
+                           int cl, int dl)
+{
+    BN_ULONG c, l, t;
+
+    assert(cl >= 0);
+    c = bn_add_words(r, a, b, cl);
+
+    if (dl == 0)
+        return c;
+
+    r += cl;
+    a += cl;
+    b += cl;
+
+    if (dl < 0) {
+        int save_dl = dl;
+        while (c) {
+            l = (c + b[0]) & BN_MASK2;
+            c = (l < c);
+            r[0] = l;
+            if (++dl >= 0)
+                break;
+
+            l = (c + b[1]) & BN_MASK2;
+            c = (l < c);
+            r[1] = l;
+            if (++dl >= 0)
+                break;
+
+            l = (c + b[2]) & BN_MASK2;
+            c = (l < c);
+            r[2] = l;
+            if (++dl >= 0)
+                break;
+
+            l = (c + b[3]) & BN_MASK2;
+            c = (l < c);
+            r[3] = l;
+            if (++dl >= 0)
+                break;
+
+            save_dl = dl;
+            b += 4;
+            r += 4;
+        }
+        if (dl < 0) {
+            if (save_dl < dl) {
+                switch (dl - save_dl) {
+                case 1:
+                    r[1] = b[1];
+                    if (++dl >= 0)
+                        break;
+                case 2:
+                    r[2] = b[2];
+                    if (++dl >= 0)
+                        break;
+                case 3:
+                    r[3] = b[3];
+                    if (++dl >= 0)
+                        break;
+                }
+                b += 4;
+                r += 4;
+            }
+        }
+        if (dl < 0) {
+            for (;;) {
+                r[0] = b[0];
+                if (++dl >= 0)
+                    break;
+                r[1] = b[1];
+                if (++dl >= 0)
+                    break;
+                r[2] = b[2];
+                if (++dl >= 0)
+                    break;
+                r[3] = b[3];
+                if (++dl >= 0)
+                    break;
+
+                b += 4;
+                r += 4;
+            }
+        }
+    } else {
+        int save_dl = dl;
+        while (c) {
+            t = (a[0] + c) & BN_MASK2;
+            c = (t < c);
+            r[0] = t;
+            if (--dl <= 0)
+                break;
+
+            t = (a[1] + c) & BN_MASK2;
+            c = (t < c);
+            r[1] = t;
+            if (--dl <= 0)
+                break;
+
+            t = (a[2] + c) & BN_MASK2;
+            c = (t < c);
+            r[2] = t;
+            if (--dl <= 0)
+                break;
+
+            t = (a[3] + c) & BN_MASK2;
+            c = (t < c);
+            r[3] = t;
+            if (--dl <= 0)
+                break;
+
+            save_dl = dl;
+            a += 4;
+            r += 4;
+        }
+        if (dl > 0) {
+            if (save_dl > dl) {
+                switch (save_dl - dl) {
+                case 1:
+                    r[1] = a[1];
+                    if (--dl <= 0)
+                        break;
+                case 2:
+                    r[2] = a[2];
+                    if (--dl <= 0)
+                        break;
+                case 3:
+                    r[3] = a[3];
+                    if (--dl <= 0)
+                        break;
+                }
+                a += 4;
+                r += 4;
+            }
+        }
+        if (dl > 0) {
+            for (;;) {
+                r[0] = a[0];
+                if (--dl <= 0)
+                    break;
+                r[1] = a[1];
+                if (--dl <= 0)
+                    break;
+                r[2] = a[2];
+                if (--dl <= 0)
+                    break;
+                r[3] = a[3];
+                if (--dl <= 0)
+                    break;
+
+                a += 4;
+                r += 4;
+            }
+        }
+    }
+    return c;
+}
+
+#ifdef BN_RECURSION
+/*
+ * Karatsuba recursive multiplication algorithm (cf. Knuth, The Art of
+ * Computer Programming, Vol. 2)
+ */
+
+/*-
+ * r is 2*n2 words in size,
+ * a and b are both n2 words in size.
+ * n2 must be a power of 2.
+ * We multiply and return the result.
+ * t must be 2*n2 words in size
+ * We calculate
+ * a[0]*b[0]
+ * a[0]*b[0]+a[1]*b[1]+(a[0]-a[1])*(b[1]-b[0])
+ * a[1]*b[1]
+ */
+/* dnX may not be positive, but n2/2+dnX has to be */
+void bn_mul_recursive(BN_ULONG *r, BN_ULONG *a, BN_ULONG *b, int n2,
+                      int dna, int dnb, BN_ULONG *t)
+{
+    int n = n2 / 2, c1, c2;
+    int tna = n + dna, tnb = n + dnb;
+    unsigned int neg, zero;
+    BN_ULONG ln, lo, *p;
+
+# ifdef BN_MUL_COMBA
+#  if 0
+    if (n2 == 4) {
+        bn_mul_comba4(r, a, b);
+        return;
+    }
+#  endif
+    /*
+     * Only call bn_mul_comba 8 if n2 == 8 and the two arrays are complete
+     * [steve]
+     */
+    if (n2 == 8 && dna == 0 && dnb == 0) {
+        bn_mul_comba8(r, a, b);
+        return;
+    }
+# endif                         /* BN_MUL_COMBA */
+    /* Else do normal multiply */
+    if (n2 < BN_MUL_RECURSIVE_SIZE_NORMAL) {
+        bn_mul_normal(r, a, n2 + dna, b, n2 + dnb);
+        if ((dna + dnb) < 0)
+            memset(&r[2 * n2 + dna + dnb], 0,
+                   sizeof(BN_ULONG) * -(dna + dnb));
+        return;
+    }
+    /* r=(a[0]-a[1])*(b[1]-b[0]) */
+    c1 = bn_cmp_part_words(a, &(a[n]), tna, n - tna);
+    c2 = bn_cmp_part_words(&(b[n]), b, tnb, tnb - n);
+    zero = neg = 0;
+    switch (c1 * 3 + c2) {
+    case -4:
+        bn_sub_part_words(t, &(a[n]), a, tna, tna - n); /* - */
+        bn_sub_part_words(&(t[n]), b, &(b[n]), tnb, n - tnb); /* - */
+        break;
+    case -3:
+        zero = 1;
+        break;
+    case -2:
+        bn_sub_part_words(t, &(a[n]), a, tna, tna - n); /* - */
+        bn_sub_part_words(&(t[n]), &(b[n]), b, tnb, tnb - n); /* + */
+        neg = 1;
+        break;
+    case -1:
+    case 0:
+    case 1:
+        zero = 1;
+        break;
+    case 2:
+        bn_sub_part_words(t, a, &(a[n]), tna, n - tna); /* + */
+        bn_sub_part_words(&(t[n]), b, &(b[n]), tnb, n - tnb); /* - */
+        neg = 1;
+        break;
+    case 3:
+        zero = 1;
+        break;
+    case 4:
+        bn_sub_part_words(t, a, &(a[n]), tna, n - tna);
+        bn_sub_part_words(&(t[n]), &(b[n]), b, tnb, tnb - n);
+        break;
+    }
+
+# ifdef BN_MUL_COMBA
+    if (n == 4 && dna == 0 && dnb == 0) { /* XXX: bn_mul_comba4 could take
+                                           * extra args to do this well */
+        if (!zero)
+            bn_mul_comba4(&(t[n2]), t, &(t[n]));
+        else
+            memset(&t[n2], 0, sizeof(*t) * 8);
+
+        bn_mul_comba4(r, a, b);
+        bn_mul_comba4(&(r[n2]), &(a[n]), &(b[n]));
+    } else if (n == 8 && dna == 0 && dnb == 0) { /* XXX: bn_mul_comba8 could
+                                                  * take extra args to do
+                                                  * this well */
+        if (!zero)
+            bn_mul_comba8(&(t[n2]), t, &(t[n]));
+        else
+            memset(&t[n2], 0, sizeof(*t) * 16);
+
+        bn_mul_comba8(r, a, b);
+        bn_mul_comba8(&(r[n2]), &(a[n]), &(b[n]));
+    } else
+# endif                         /* BN_MUL_COMBA */
+    {
+        p = &(t[n2 * 2]);
+        if (!zero)
+            bn_mul_recursive(&(t[n2]), t, &(t[n]), n, 0, 0, p);
+        else
+            memset(&t[n2], 0, sizeof(*t) * n2);
+        bn_mul_recursive(r, a, b, n, 0, 0, p);
+        bn_mul_recursive(&(r[n2]), &(a[n]), &(b[n]), n, dna, dnb, p);
+    }
+
+    /*-
+     * t[32] holds (a[0]-a[1])*(b[1]-b[0]), c1 is the sign
+     * r[10] holds (a[0]*b[0])
+     * r[32] holds (b[1]*b[1])
+     */
+
+    c1 = (int)(bn_add_words(t, r, &(r[n2]), n2));
+
+    if (neg) {                  /* if t[32] is negative */
+        c1 -= (int)(bn_sub_words(&(t[n2]), t, &(t[n2]), n2));
+    } else {
+        /* Might have a carry */
+        c1 += (int)(bn_add_words(&(t[n2]), &(t[n2]), t, n2));
+    }
+
+    /*-
+     * t[32] holds (a[0]-a[1])*(b[1]-b[0])+(a[0]*b[0])+(a[1]*b[1])
+     * r[10] holds (a[0]*b[0])
+     * r[32] holds (b[1]*b[1])
+     * c1 holds the carry bits
+     */
+    c1 += (int)(bn_add_words(&(r[n]), &(r[n]), &(t[n2]), n2));
+    if (c1) {
+        p = &(r[n + n2]);
+        lo = *p;
+        ln = (lo + c1) & BN_MASK2;
+        *p = ln;
+
+        /*
+         * The overflow will stop before we over write words we should not
+         * overwrite
+         */
+        if (ln < (BN_ULONG)c1) {
+            do {
+                p++;
+                lo = *p;
+                ln = (lo + 1) & BN_MASK2;
+                *p = ln;
+            } while (ln == 0);
+        }
+    }
+}
+
+/*
+ * n+tn is the word length t needs to be n*4 is size, as does r
+ */
+/* tnX may not be negative but less than n */
+void bn_mul_part_recursive(BN_ULONG *r, BN_ULONG *a, BN_ULONG *b, int n,
+                           int tna, int tnb, BN_ULONG *t)
+{
+    int i, j, n2 = n * 2;
+    int c1, c2, neg;
+    BN_ULONG ln, lo, *p;
+
+    if (n < 8) {
+        bn_mul_normal(r, a, n + tna, b, n + tnb);
+        return;
+    }
+
+    /* r=(a[0]-a[1])*(b[1]-b[0]) */
+    c1 = bn_cmp_part_words(a, &(a[n]), tna, n - tna);
+    c2 = bn_cmp_part_words(&(b[n]), b, tnb, tnb - n);
+    neg = 0;
+    switch (c1 * 3 + c2) {
+    case -4:
+        bn_sub_part_words(t, &(a[n]), a, tna, tna - n); /* - */
+        bn_sub_part_words(&(t[n]), b, &(b[n]), tnb, n - tnb); /* - */
+        break;
+    case -3:
+        /* break; */
+    case -2:
+        bn_sub_part_words(t, &(a[n]), a, tna, tna - n); /* - */
+        bn_sub_part_words(&(t[n]), &(b[n]), b, tnb, tnb - n); /* + */
+        neg = 1;
+        break;
+    case -1:
+    case 0:
+    case 1:
+        /* break; */
+    case 2:
+        bn_sub_part_words(t, a, &(a[n]), tna, n - tna); /* + */
+        bn_sub_part_words(&(t[n]), b, &(b[n]), tnb, n - tnb); /* - */
+        neg = 1;
+        break;
+    case 3:
+        /* break; */
+    case 4:
+        bn_sub_part_words(t, a, &(a[n]), tna, n - tna);
+        bn_sub_part_words(&(t[n]), &(b[n]), b, tnb, tnb - n);
+        break;
+    }
+    /*
+     * The zero case isn't yet implemented here. The speedup would probably
+     * be negligible.
+     */
+# if 0
+    if (n == 4) {
+        bn_mul_comba4(&(t[n2]), t, &(t[n]));
+        bn_mul_comba4(r, a, b);
+        bn_mul_normal(&(r[n2]), &(a[n]), tn, &(b[n]), tn);
+        memset(&r[n2 + tn * 2], 0, sizeof(*r) * (n2 - tn * 2));
+    } else
+# endif
+    if (n == 8) {
+        bn_mul_comba8(&(t[n2]), t, &(t[n]));
+        bn_mul_comba8(r, a, b);
+        bn_mul_normal(&(r[n2]), &(a[n]), tna, &(b[n]), tnb);
+        memset(&r[n2 + tna + tnb], 0, sizeof(*r) * (n2 - tna - tnb));
+    } else {
+        p = &(t[n2 * 2]);
+        bn_mul_recursive(&(t[n2]), t, &(t[n]), n, 0, 0, p);
+        bn_mul_recursive(r, a, b, n, 0, 0, p);
+        i = n / 2;
+        /*
+         * If there is only a bottom half to the number, just do it
+         */
+        if (tna > tnb)
+            j = tna - i;
+        else
+            j = tnb - i;
+        if (j == 0) {
+            bn_mul_recursive(&(r[n2]), &(a[n]), &(b[n]),
+                             i, tna - i, tnb - i, p);
+            memset(&r[n2 + i * 2], 0, sizeof(*r) * (n2 - i * 2));
+        } else if (j > 0) {     /* eg, n == 16, i == 8 and tn == 11 */
+            bn_mul_part_recursive(&(r[n2]), &(a[n]), &(b[n]),
+                                  i, tna - i, tnb - i, p);
+            memset(&(r[n2 + tna + tnb]), 0,
+                   sizeof(BN_ULONG) * (n2 - tna - tnb));
+        } else {                /* (j < 0) eg, n == 16, i == 8 and tn == 5 */
+
+            memset(&r[n2], 0, sizeof(*r) * n2);
+            if (tna < BN_MUL_RECURSIVE_SIZE_NORMAL
+                && tnb < BN_MUL_RECURSIVE_SIZE_NORMAL) {
+                bn_mul_normal(&(r[n2]), &(a[n]), tna, &(b[n]), tnb);
+            } else {
+                for (;;) {
+                    i /= 2;
+                    /*
+                     * these simplified conditions work exclusively because
+                     * difference between tna and tnb is 1 or 0
+                     */
+                    if (i < tna || i < tnb) {
+                        bn_mul_part_recursive(&(r[n2]),
+                                              &(a[n]), &(b[n]),
+                                              i, tna - i, tnb - i, p);
+                        break;
+                    } else if (i == tna || i == tnb) {
+                        bn_mul_recursive(&(r[n2]),
+                                         &(a[n]), &(b[n]),
+                                         i, tna - i, tnb - i, p);
+                        break;
+                    }
+                }
+            }
+        }
+    }
+
+    /*-
+     * t[32] holds (a[0]-a[1])*(b[1]-b[0]), c1 is the sign
+     * r[10] holds (a[0]*b[0])
+     * r[32] holds (b[1]*b[1])
+     */
+
+    c1 = (int)(bn_add_words(t, r, &(r[n2]), n2));
+
+    if (neg) {                  /* if t[32] is negative */
+        c1 -= (int)(bn_sub_words(&(t[n2]), t, &(t[n2]), n2));
+    } else {
+        /* Might have a carry */
+        c1 += (int)(bn_add_words(&(t[n2]), &(t[n2]), t, n2));
+    }
+
+    /*-
+     * t[32] holds (a[0]-a[1])*(b[1]-b[0])+(a[0]*b[0])+(a[1]*b[1])
+     * r[10] holds (a[0]*b[0])
+     * r[32] holds (b[1]*b[1])
+     * c1 holds the carry bits
+     */
+    c1 += (int)(bn_add_words(&(r[n]), &(r[n]), &(t[n2]), n2));
+    if (c1) {
+        p = &(r[n + n2]);
+        lo = *p;
+        ln = (lo + c1) & BN_MASK2;
+        *p = ln;
+
+        /*
+         * The overflow will stop before we over write words we should not
+         * overwrite
+         */
+        if (ln < (BN_ULONG)c1) {
+            do {
+                p++;
+                lo = *p;
+                ln = (lo + 1) & BN_MASK2;
+                *p = ln;
+            } while (ln == 0);
+        }
+    }
+}
+
+/*-
+ * a and b must be the same size, which is n2.
+ * r needs to be n2 words and t needs to be n2*2
+ */
+void bn_mul_low_recursive(BN_ULONG *r, BN_ULONG *a, BN_ULONG *b, int n2,
+                          BN_ULONG *t)
+{
+    int n = n2 / 2;
+
+    bn_mul_recursive(r, a, b, n, 0, 0, &(t[0]));
+    if (n >= BN_MUL_LOW_RECURSIVE_SIZE_NORMAL) {
+        bn_mul_low_recursive(&(t[0]), &(a[0]), &(b[n]), n, &(t[n2]));
+        bn_add_words(&(r[n]), &(r[n]), &(t[0]), n);
+        bn_mul_low_recursive(&(t[0]), &(a[n]), &(b[0]), n, &(t[n2]));
+        bn_add_words(&(r[n]), &(r[n]), &(t[0]), n);
+    } else {
+        bn_mul_low_normal(&(t[0]), &(a[0]), &(b[n]), n);
+        bn_mul_low_normal(&(t[n]), &(a[n]), &(b[0]), n);
+        bn_add_words(&(r[n]), &(r[n]), &(t[0]), n);
+        bn_add_words(&(r[n]), &(r[n]), &(t[n]), n);
+    }
+}
+
+/*-
+ * a and b must be the same size, which is n2.
+ * r needs to be n2 words and t needs to be n2*2
+ * l is the low words of the output.
+ * t needs to be n2*3
+ */
+void bn_mul_high(BN_ULONG *r, BN_ULONG *a, BN_ULONG *b, BN_ULONG *l, int n2,
+                 BN_ULONG *t)
+{
+    int i, n;
+    int c1, c2;
+    int neg, oneg, zero;
+    BN_ULONG ll, lc, *lp, *mp;
+
+    n = n2 / 2;
+
+    /* Calculate (al-ah)*(bh-bl) */
+    neg = zero = 0;
+    c1 = bn_cmp_words(&(a[0]), &(a[n]), n);
+    c2 = bn_cmp_words(&(b[n]), &(b[0]), n);
+    switch (c1 * 3 + c2) {
+    case -4:
+        bn_sub_words(&(r[0]), &(a[n]), &(a[0]), n);
+        bn_sub_words(&(r[n]), &(b[0]), &(b[n]), n);
+        break;
+    case -3:
+        zero = 1;
+        break;
+    case -2:
+        bn_sub_words(&(r[0]), &(a[n]), &(a[0]), n);
+        bn_sub_words(&(r[n]), &(b[n]), &(b[0]), n);
+        neg = 1;
+        break;
+    case -1:
+    case 0:
+    case 1:
+        zero = 1;
+        break;
+    case 2:
+        bn_sub_words(&(r[0]), &(a[0]), &(a[n]), n);
+        bn_sub_words(&(r[n]), &(b[0]), &(b[n]), n);
+        neg = 1;
+        break;
+    case 3:
+        zero = 1;
+        break;
+    case 4:
+        bn_sub_words(&(r[0]), &(a[0]), &(a[n]), n);
+        bn_sub_words(&(r[n]), &(b[n]), &(b[0]), n);
+        break;
+    }
+
+    oneg = neg;
+    /* t[10] = (a[0]-a[1])*(b[1]-b[0]) */
+    /* r[10] = (a[1]*b[1]) */
+# ifdef BN_MUL_COMBA
+    if (n == 8) {
+        bn_mul_comba8(&(t[0]), &(r[0]), &(r[n]));
+        bn_mul_comba8(r, &(a[n]), &(b[n]));
+    } else
+# endif
+    {
+        bn_mul_recursive(&(t[0]), &(r[0]), &(r[n]), n, 0, 0, &(t[n2]));
+        bn_mul_recursive(r, &(a[n]), &(b[n]), n, 0, 0, &(t[n2]));
+    }
+
+    /*-
+     * s0 == low(al*bl)
+     * s1 == low(ah*bh)+low((al-ah)*(bh-bl))+low(al*bl)+high(al*bl)
+     * We know s0 and s1 so the only unknown is high(al*bl)
+     * high(al*bl) == s1 - low(ah*bh+s0+(al-ah)*(bh-bl))
+     * high(al*bl) == s1 - (r[0]+l[0]+t[0])
+     */
+    if (l != NULL) {
+        lp = &(t[n2 + n]);
+        bn_add_words(lp, &(r[0]), &(l[0]), n);
+    } else {
+        lp = &(r[0]);
+    }
+
+    if (neg)
+        neg = (int)(bn_sub_words(&(t[n2]), lp, &(t[0]), n));
+    else {
+        bn_add_words(&(t[n2]), lp, &(t[0]), n);
+        neg = 0;
+    }
+
+    if (l != NULL) {
+        bn_sub_words(&(t[n2 + n]), &(l[n]), &(t[n2]), n);
+    } else {
+        lp = &(t[n2 + n]);
+        mp = &(t[n2]);
+        for (i = 0; i < n; i++)
+            lp[i] = ((~mp[i]) + 1) & BN_MASK2;
+    }
+
+    /*-
+     * s[0] = low(al*bl)
+     * t[3] = high(al*bl)
+     * t[10] = (a[0]-a[1])*(b[1]-b[0]) neg is the sign
+     * r[10] = (a[1]*b[1])
+     */
+    /*-
+     * R[10] = al*bl
+     * R[21] = al*bl + ah*bh + (a[0]-a[1])*(b[1]-b[0])
+     * R[32] = ah*bh
+     */
+    /*-
+     * R[1]=t[3]+l[0]+r[0](+-)t[0] (have carry/borrow)
+     * R[2]=r[0]+t[3]+r[1](+-)t[1] (have carry/borrow)
+     * R[3]=r[1]+(carry/borrow)
+     */
+    if (l != NULL) {
+        lp = &(t[n2]);
+        c1 = (int)(bn_add_words(lp, &(t[n2 + n]), &(l[0]), n));
+    } else {
+        lp = &(t[n2 + n]);
+        c1 = 0;
+    }
+    c1 += (int)(bn_add_words(&(t[n2]), lp, &(r[0]), n));
+    if (oneg)
+        c1 -= (int)(bn_sub_words(&(t[n2]), &(t[n2]), &(t[0]), n));
+    else
+        c1 += (int)(bn_add_words(&(t[n2]), &(t[n2]), &(t[0]), n));
+
+    c2 = (int)(bn_add_words(&(r[0]), &(r[0]), &(t[n2 + n]), n));
+    c2 += (int)(bn_add_words(&(r[0]), &(r[0]), &(r[n]), n));
+    if (oneg)
+        c2 -= (int)(bn_sub_words(&(r[0]), &(r[0]), &(t[n]), n));
+    else
+        c2 += (int)(bn_add_words(&(r[0]), &(r[0]), &(t[n]), n));
+
+    if (c1 != 0) {              /* Add starting at r[0], could be +ve or -ve */
+        i = 0;
+        if (c1 > 0) {
+            lc = c1;
+            do {
+                ll = (r[i] + lc) & BN_MASK2;
+                r[i++] = ll;
+                lc = (lc > ll);
+            } while (lc);
+        } else {
+            lc = -c1;
+            do {
+                ll = r[i];
+                r[i++] = (ll - lc) & BN_MASK2;
+                lc = (lc > ll);
+            } while (lc);
+        }
+    }
+    if (c2 != 0) {              /* Add starting at r[1] */
+        i = n;
+        if (c2 > 0) {
+            lc = c2;
+            do {
+                ll = (r[i] + lc) & BN_MASK2;
+                r[i++] = ll;
+                lc = (lc > ll);
+            } while (lc);
+        } else {
+            lc = -c2;
+            do {
+                ll = r[i];
+                r[i++] = (ll - lc) & BN_MASK2;
+                lc = (lc > ll);
+            } while (lc);
+        }
+    }
+}
+#endif                          /* BN_RECURSION */
+
+int BN_mul(BIGNUM *r, const BIGNUM *a, const BIGNUM *b, BN_CTX *ctx)
+{
+    int ret = 0;
+    int top, al, bl;
+    BIGNUM *rr;
+#if defined(BN_MUL_COMBA) || defined(BN_RECURSION)
+    int i;
+#endif
+#ifdef BN_RECURSION
+    BIGNUM *t = NULL;
+    int j = 0, k;
+#endif
+
+    bn_check_top(a);
+    bn_check_top(b);
+    bn_check_top(r);
+
+    al = a->top;
+    bl = b->top;
+
+    if ((al == 0) || (bl == 0)) {
+        BN_zero(r);
+        return (1);
+    }
+    top = al + bl;
+
+    BN_CTX_start(ctx);
+    if ((r == a) || (r == b)) {
+        if ((rr = BN_CTX_get(ctx)) == NULL)
+            goto err;
+    } else
+        rr = r;
+    rr->neg = a->neg ^ b->neg;
+
+#if defined(BN_MUL_COMBA) || defined(BN_RECURSION)
+    i = al - bl;
+#endif
+#ifdef BN_MUL_COMBA
+    if (i == 0) {
+# if 0
+        if (al == 4) {
+            if (bn_wexpand(rr, 8) == NULL)
+                goto err;
+            rr->top = 8;
+            bn_mul_comba4(rr->d, a->d, b->d);
+            goto end;
+        }
+# endif
+        if (al == 8) {
+            if (bn_wexpand(rr, 16) == NULL)
+                goto err;
+            rr->top = 16;
+            bn_mul_comba8(rr->d, a->d, b->d);
+            goto end;
+        }
+    }
+#endif                          /* BN_MUL_COMBA */
+#ifdef BN_RECURSION
+    if ((al >= BN_MULL_SIZE_NORMAL) && (bl >= BN_MULL_SIZE_NORMAL)) {
+        if (i >= -1 && i <= 1) {
+            /*
+             * Find out the power of two lower or equal to the longest of the
+             * two numbers
+             */
+            if (i >= 0) {
+                j = BN_num_bits_word((BN_ULONG)al);
+            }
+            if (i == -1) {
+                j = BN_num_bits_word((BN_ULONG)bl);
+            }
+            j = 1 << (j - 1);
+            assert(j <= al || j <= bl);
+            k = j + j;
+            t = BN_CTX_get(ctx);
+            if (t == NULL)
+                goto err;
+            if (al > j || bl > j) {
+                if (bn_wexpand(t, k * 4) == NULL)
+                    goto err;
+                if (bn_wexpand(rr, k * 4) == NULL)
+                    goto err;
+                bn_mul_part_recursive(rr->d, a->d, b->d,
+                                      j, al - j, bl - j, t->d);
+            } else {            /* al <= j || bl <= j */
+
+                if (bn_wexpand(t, k * 2) == NULL)
+                    goto err;
+                if (bn_wexpand(rr, k * 2) == NULL)
+                    goto err;
+                bn_mul_recursive(rr->d, a->d, b->d, j, al - j, bl - j, t->d);
+            }
+            rr->top = top;
+            goto end;
+        }
+# if 0
+        if (i == 1 && !BN_get_flags(b, BN_FLG_STATIC_DATA)) {
+            BIGNUM *tmp_bn = (BIGNUM *)b;
+            if (bn_wexpand(tmp_bn, al) == NULL)
+                goto err;
+            tmp_bn->d[bl] = 0;
+            bl++;
+            i--;
+        } else if (i == -1 && !BN_get_flags(a, BN_FLG_STATIC_DATA)) {
+            BIGNUM *tmp_bn = (BIGNUM *)a;
+            if (bn_wexpand(tmp_bn, bl) == NULL)
+                goto err;
+            tmp_bn->d[al] = 0;
+            al++;
+            i++;
+        }
+        if (i == 0) {
+            /* symmetric and > 4 */
+            /* 16 or larger */
+            j = BN_num_bits_word((BN_ULONG)al);
+            j = 1 << (j - 1);
+            k = j + j;
+            t = BN_CTX_get(ctx);
+            if (al == j) {      /* exact multiple */
+                if (bn_wexpand(t, k * 2) == NULL)
+                    goto err;
+                if (bn_wexpand(rr, k * 2) == NULL)
+                    goto err;
+                bn_mul_recursive(rr->d, a->d, b->d, al, t->d);
+            } else {
+                if (bn_wexpand(t, k * 4) == NULL)
+                    goto err;
+                if (bn_wexpand(rr, k * 4) == NULL)
+                    goto err;
+                bn_mul_part_recursive(rr->d, a->d, b->d, al - j, j, t->d);
+            }
+            rr->top = top;
+            goto end;
+        }
+# endif
+    }
+#endif                          /* BN_RECURSION */
+    if (bn_wexpand(rr, top) == NULL)
+        goto err;
+    rr->top = top;
+    bn_mul_normal(rr->d, a->d, al, b->d, bl);
+
+#if defined(BN_MUL_COMBA) || defined(BN_RECURSION)
+ end:
+#endif
+    bn_correct_top(rr);
+    if (r != rr && BN_copy(r, rr) == NULL)
+        goto err;
+
+    ret = 1;
+ err:
+    bn_check_top(r);
+    BN_CTX_end(ctx);
+    return (ret);
+}
+
+void bn_mul_normal(BN_ULONG *r, BN_ULONG *a, int na, BN_ULONG *b, int nb)
+{
+    BN_ULONG *rr;
+
+    if (na < nb) {
+        int itmp;
+        BN_ULONG *ltmp;
+
+        itmp = na;
+        na = nb;
+        nb = itmp;
+        ltmp = a;
+        a = b;
+        b = ltmp;
+
+    }
+    rr = &(r[na]);
+    if (nb <= 0) {
+        (void)bn_mul_words(r, a, na, 0);
+        return;
+    } else
+        rr[0] = bn_mul_words(r, a, na, b[0]);
+
+    for (;;) {
+        if (--nb <= 0)
+            return;
+        rr[1] = bn_mul_add_words(&(r[1]), a, na, b[1]);
+        if (--nb <= 0)
+            return;
+        rr[2] = bn_mul_add_words(&(r[2]), a, na, b[2]);
+        if (--nb <= 0)
+            return;
+        rr[3] = bn_mul_add_words(&(r[3]), a, na, b[3]);
+        if (--nb <= 0)
+            return;
+        rr[4] = bn_mul_add_words(&(r[4]), a, na, b[4]);
+        rr += 4;
+        r += 4;
+        b += 4;
+    }
+}
+
+void bn_mul_low_normal(BN_ULONG *r, BN_ULONG *a, BN_ULONG *b, int n)
+{
+    bn_mul_words(r, a, n, b[0]);
+
+    for (;;) {
+        if (--n <= 0)
+            return;
+        bn_mul_add_words(&(r[1]), a, n, b[1]);
+        if (--n <= 0)
+            return;
+        bn_mul_add_words(&(r[2]), a, n, b[2]);
+        if (--n <= 0)
+            return;
+        bn_mul_add_words(&(r[3]), a, n, b[3]);
+        if (--n <= 0)
+            return;
+        bn_mul_add_words(&(r[4]), a, n, b[4]);
+        r += 4;
+        b += 4;
+    }
+}
diff --git a/crypto/bn/bn_nist.c b/crypto/bn/bn_nist.c
new file mode 100644
index 000000000..53598f97e
--- /dev/null
+++ b/crypto/bn/bn_nist.c
@@ -0,0 +1,1239 @@
+/*
+ * Copyright 2002-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "bn_lcl.h"
+#include "internal/cryptlib.h"
+
+#define BN_NIST_192_TOP (192+BN_BITS2-1)/BN_BITS2
+#define BN_NIST_224_TOP (224+BN_BITS2-1)/BN_BITS2
+#define BN_NIST_256_TOP (256+BN_BITS2-1)/BN_BITS2
+#define BN_NIST_384_TOP (384+BN_BITS2-1)/BN_BITS2
+#define BN_NIST_521_TOP (521+BN_BITS2-1)/BN_BITS2
+
+/* pre-computed tables are "carry-less" values of modulus*(i+1) */
+#if BN_BITS2 == 64
+static const BN_ULONG _nist_p_192[][BN_NIST_192_TOP] = {
+    {0xFFFFFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFFEULL, 0xFFFFFFFFFFFFFFFFULL},
+    {0xFFFFFFFFFFFFFFFEULL, 0xFFFFFFFFFFFFFFFDULL, 0xFFFFFFFFFFFFFFFFULL},
+    {0xFFFFFFFFFFFFFFFDULL, 0xFFFFFFFFFFFFFFFCULL, 0xFFFFFFFFFFFFFFFFULL}
+};
+
+static const BN_ULONG _nist_p_192_sqr[] = {
+    0x0000000000000001ULL, 0x0000000000000002ULL, 0x0000000000000001ULL,
+    0xFFFFFFFFFFFFFFFEULL, 0xFFFFFFFFFFFFFFFDULL, 0xFFFFFFFFFFFFFFFFULL
+};
+
+static const BN_ULONG _nist_p_224[][BN_NIST_224_TOP] = {
+    {0x0000000000000001ULL, 0xFFFFFFFF00000000ULL,
+     0xFFFFFFFFFFFFFFFFULL, 0x00000000FFFFFFFFULL},
+    {0x0000000000000002ULL, 0xFFFFFFFE00000000ULL,
+     0xFFFFFFFFFFFFFFFFULL, 0x00000001FFFFFFFFULL} /* this one is
+                                                    * "carry-full" */
+};
+
+static const BN_ULONG _nist_p_224_sqr[] = {
+    0x0000000000000001ULL, 0xFFFFFFFE00000000ULL,
+    0xFFFFFFFFFFFFFFFFULL, 0x0000000200000000ULL,
+    0x0000000000000000ULL, 0xFFFFFFFFFFFFFFFEULL,
+    0xFFFFFFFFFFFFFFFFULL
+};
+
+static const BN_ULONG _nist_p_256[][BN_NIST_256_TOP] = {
+    {0xFFFFFFFFFFFFFFFFULL, 0x00000000FFFFFFFFULL,
+     0x0000000000000000ULL, 0xFFFFFFFF00000001ULL},
+    {0xFFFFFFFFFFFFFFFEULL, 0x00000001FFFFFFFFULL,
+     0x0000000000000000ULL, 0xFFFFFFFE00000002ULL},
+    {0xFFFFFFFFFFFFFFFDULL, 0x00000002FFFFFFFFULL,
+     0x0000000000000000ULL, 0xFFFFFFFD00000003ULL},
+    {0xFFFFFFFFFFFFFFFCULL, 0x00000003FFFFFFFFULL,
+     0x0000000000000000ULL, 0xFFFFFFFC00000004ULL},
+    {0xFFFFFFFFFFFFFFFBULL, 0x00000004FFFFFFFFULL,
+     0x0000000000000000ULL, 0xFFFFFFFB00000005ULL},
+};
+
+static const BN_ULONG _nist_p_256_sqr[] = {
+    0x0000000000000001ULL, 0xFFFFFFFE00000000ULL,
+    0xFFFFFFFFFFFFFFFFULL, 0x00000001FFFFFFFEULL,
+    0x00000001FFFFFFFEULL, 0x00000001FFFFFFFEULL,
+    0xFFFFFFFE00000001ULL, 0xFFFFFFFE00000002ULL
+};
+
+static const BN_ULONG _nist_p_384[][BN_NIST_384_TOP] = {
+    {0x00000000FFFFFFFFULL, 0xFFFFFFFF00000000ULL, 0xFFFFFFFFFFFFFFFEULL,
+     0xFFFFFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFFFULL},
+    {0x00000001FFFFFFFEULL, 0xFFFFFFFE00000000ULL, 0xFFFFFFFFFFFFFFFDULL,
+     0xFFFFFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFFFULL},
+    {0x00000002FFFFFFFDULL, 0xFFFFFFFD00000000ULL, 0xFFFFFFFFFFFFFFFCULL,
+     0xFFFFFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFFFULL},
+    {0x00000003FFFFFFFCULL, 0xFFFFFFFC00000000ULL, 0xFFFFFFFFFFFFFFFBULL,
+     0xFFFFFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFFFULL},
+    {0x00000004FFFFFFFBULL, 0xFFFFFFFB00000000ULL, 0xFFFFFFFFFFFFFFFAULL,
+     0xFFFFFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFFFULL},
+};
+
+static const BN_ULONG _nist_p_384_sqr[] = {
+    0xFFFFFFFE00000001ULL, 0x0000000200000000ULL, 0xFFFFFFFE00000000ULL,
+    0x0000000200000000ULL, 0x0000000000000001ULL, 0x0000000000000000ULL,
+    0x00000001FFFFFFFEULL, 0xFFFFFFFE00000000ULL, 0xFFFFFFFFFFFFFFFDULL,
+    0xFFFFFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFFFULL
+};
+
+static const BN_ULONG _nist_p_521[] =
+    { 0xFFFFFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFFFULL,
+    0xFFFFFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFFFULL,
+    0xFFFFFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFFFULL,
+    0xFFFFFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFFFULL,
+    0x00000000000001FFULL
+};
+
+static const BN_ULONG _nist_p_521_sqr[] = {
+    0x0000000000000001ULL, 0x0000000000000000ULL, 0x0000000000000000ULL,
+    0x0000000000000000ULL, 0x0000000000000000ULL, 0x0000000000000000ULL,
+    0x0000000000000000ULL, 0x0000000000000000ULL, 0xFFFFFFFFFFFFFC00ULL,
+    0xFFFFFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFFFULL,
+    0xFFFFFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFFFULL,
+    0xFFFFFFFFFFFFFFFFULL, 0x000000000003FFFFULL
+};
+#elif BN_BITS2 == 32
+static const BN_ULONG _nist_p_192[][BN_NIST_192_TOP] = {
+    {0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFE, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF},
+    {0xFFFFFFFE, 0xFFFFFFFF, 0xFFFFFFFD, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF},
+    {0xFFFFFFFD, 0xFFFFFFFF, 0xFFFFFFFC, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF}
+};
+
+static const BN_ULONG _nist_p_192_sqr[] = {
+    0x00000001, 0x00000000, 0x00000002, 0x00000000, 0x00000001, 0x00000000,
+    0xFFFFFFFE, 0xFFFFFFFF, 0xFFFFFFFD, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF
+};
+
+static const BN_ULONG _nist_p_224[][BN_NIST_224_TOP] = {
+    {0x00000001, 0x00000000, 0x00000000, 0xFFFFFFFF,
+     0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF},
+    {0x00000002, 0x00000000, 0x00000000, 0xFFFFFFFE,
+     0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF}
+};
+
+static const BN_ULONG _nist_p_224_sqr[] = {
+    0x00000001, 0x00000000, 0x00000000, 0xFFFFFFFE,
+    0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000002,
+    0x00000000, 0x00000000, 0xFFFFFFFE, 0xFFFFFFFF,
+    0xFFFFFFFF, 0xFFFFFFFF
+};
+
+static const BN_ULONG _nist_p_256[][BN_NIST_256_TOP] = {
+    {0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000,
+     0x00000000, 0x00000000, 0x00000001, 0xFFFFFFFF},
+    {0xFFFFFFFE, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000001,
+     0x00000000, 0x00000000, 0x00000002, 0xFFFFFFFE},
+    {0xFFFFFFFD, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000002,
+     0x00000000, 0x00000000, 0x00000003, 0xFFFFFFFD},
+    {0xFFFFFFFC, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000003,
+     0x00000000, 0x00000000, 0x00000004, 0xFFFFFFFC},
+    {0xFFFFFFFB, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000004,
+     0x00000000, 0x00000000, 0x00000005, 0xFFFFFFFB},
+};
+
+static const BN_ULONG _nist_p_256_sqr[] = {
+    0x00000001, 0x00000000, 0x00000000, 0xFFFFFFFE,
+    0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFE, 0x00000001,
+    0xFFFFFFFE, 0x00000001, 0xFFFFFFFE, 0x00000001,
+    0x00000001, 0xFFFFFFFE, 0x00000002, 0xFFFFFFFE
+};
+
+static const BN_ULONG _nist_p_384[][BN_NIST_384_TOP] = {
+    {0xFFFFFFFF, 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFE, 0xFFFFFFFF,
+     0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF},
+    {0xFFFFFFFE, 0x00000001, 0x00000000, 0xFFFFFFFE, 0xFFFFFFFD, 0xFFFFFFFF,
+     0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF},
+    {0xFFFFFFFD, 0x00000002, 0x00000000, 0xFFFFFFFD, 0xFFFFFFFC, 0xFFFFFFFF,
+     0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF},
+    {0xFFFFFFFC, 0x00000003, 0x00000000, 0xFFFFFFFC, 0xFFFFFFFB, 0xFFFFFFFF,
+     0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF},
+    {0xFFFFFFFB, 0x00000004, 0x00000000, 0xFFFFFFFB, 0xFFFFFFFA, 0xFFFFFFFF,
+     0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF},
+};
+
+static const BN_ULONG _nist_p_384_sqr[] = {
+    0x00000001, 0xFFFFFFFE, 0x00000000, 0x00000002, 0x00000000, 0xFFFFFFFE,
+    0x00000000, 0x00000002, 0x00000001, 0x00000000, 0x00000000, 0x00000000,
+    0xFFFFFFFE, 0x00000001, 0x00000000, 0xFFFFFFFE, 0xFFFFFFFD, 0xFFFFFFFF,
+    0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF
+};
+
+static const BN_ULONG _nist_p_521[] = { 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+    0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+    0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+    0xFFFFFFFF, 0x000001FF
+};
+
+static const BN_ULONG _nist_p_521_sqr[] = {
+    0x00000001, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+    0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+    0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFFFFFC00, 0xFFFFFFFF,
+    0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+    0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+    0xFFFFFFFF, 0xFFFFFFFF, 0x0003FFFF
+};
+#else
+# error "unsupported BN_BITS2"
+#endif
+
+static const BIGNUM _bignum_nist_p_192 = {
+    (BN_ULONG *)_nist_p_192[0],
+    BN_NIST_192_TOP,
+    BN_NIST_192_TOP,
+    0,
+    BN_FLG_STATIC_DATA
+};
+
+static const BIGNUM _bignum_nist_p_224 = {
+    (BN_ULONG *)_nist_p_224[0],
+    BN_NIST_224_TOP,
+    BN_NIST_224_TOP,
+    0,
+    BN_FLG_STATIC_DATA
+};
+
+static const BIGNUM _bignum_nist_p_256 = {
+    (BN_ULONG *)_nist_p_256[0],
+    BN_NIST_256_TOP,
+    BN_NIST_256_TOP,
+    0,
+    BN_FLG_STATIC_DATA
+};
+
+static const BIGNUM _bignum_nist_p_384 = {
+    (BN_ULONG *)_nist_p_384[0],
+    BN_NIST_384_TOP,
+    BN_NIST_384_TOP,
+    0,
+    BN_FLG_STATIC_DATA
+};
+
+static const BIGNUM _bignum_nist_p_521 = {
+    (BN_ULONG *)_nist_p_521,
+    BN_NIST_521_TOP,
+    BN_NIST_521_TOP,
+    0,
+    BN_FLG_STATIC_DATA
+};
+
+const BIGNUM *BN_get0_nist_prime_192(void)
+{
+    return &_bignum_nist_p_192;
+}
+
+const BIGNUM *BN_get0_nist_prime_224(void)
+{
+    return &_bignum_nist_p_224;
+}
+
+const BIGNUM *BN_get0_nist_prime_256(void)
+{
+    return &_bignum_nist_p_256;
+}
+
+const BIGNUM *BN_get0_nist_prime_384(void)
+{
+    return &_bignum_nist_p_384;
+}
+
+const BIGNUM *BN_get0_nist_prime_521(void)
+{
+    return &_bignum_nist_p_521;
+}
+
+static void nist_cp_bn_0(BN_ULONG *dst, const BN_ULONG *src, int top, int max)
+{
+    int i;
+
+#ifdef BN_DEBUG
+    OPENSSL_assert(top <= max);
+#endif
+    for (i = 0; i < top; i++)
+        dst[i] = src[i];
+    for (; i < max; i++)
+        dst[i] = 0;
+}
+
+static void nist_cp_bn(BN_ULONG *dst, const BN_ULONG *src, int top)
+{
+    int i;
+
+    for (i = 0; i < top; i++)
+        dst[i] = src[i];
+}
+
+#if BN_BITS2 == 64
+# define bn_cp_64(to, n, from, m)        (to)[n] = (m>=0)?((from)[m]):0;
+# define bn_64_set_0(to, n)              (to)[n] = (BN_ULONG)0;
+/*
+ * two following macros are implemented under assumption that they
+ * are called in a sequence with *ascending* n, i.e. as they are...
+ */
+# define bn_cp_32_naked(to, n, from, m)  (((n)&1)?(to[(n)/2]|=((m)&1)?(from[(m)/2]&BN_MASK2h):(from[(m)/2]<<32))\
+                                                :(to[(n)/2] =((m)&1)?(from[(m)/2]>>32):(from[(m)/2]&BN_MASK2l)))
+# define bn_32_set_0(to, n)              (((n)&1)?(to[(n)/2]&=BN_MASK2l):(to[(n)/2]=0));
+# define bn_cp_32(to,n,from,m)           ((m)>=0)?bn_cp_32_naked(to,n,from,m):bn_32_set_0(to,n)
+# if defined(L_ENDIAN)
+#  if defined(__arch64__)
+#   define NIST_INT64 long
+#  else
+#   define NIST_INT64 long long
+#  endif
+# endif
+#else
+# define bn_cp_64(to, n, from, m) \
+        { \
+        bn_cp_32(to, (n)*2, from, (m)*2); \
+        bn_cp_32(to, (n)*2+1, from, (m)*2+1); \
+        }
+# define bn_64_set_0(to, n) \
+        { \
+        bn_32_set_0(to, (n)*2); \
+        bn_32_set_0(to, (n)*2+1); \
+        }
+# define bn_cp_32(to, n, from, m)        (to)[n] = (m>=0)?((from)[m]):0;
+# define bn_32_set_0(to, n)              (to)[n] = (BN_ULONG)0;
+# if defined(_WIN32) && !defined(__GNUC__)
+#  define NIST_INT64 __int64
+# elif defined(BN_LLONG)
+#  define NIST_INT64 long long
+# endif
+#endif                          /* BN_BITS2 != 64 */
+
+#define nist_set_192(to, from, a1, a2, a3) \
+        { \
+        bn_cp_64(to, 0, from, (a3) - 3) \
+        bn_cp_64(to, 1, from, (a2) - 3) \
+        bn_cp_64(to, 2, from, (a1) - 3) \
+        }
+
+int BN_nist_mod_192(BIGNUM *r, const BIGNUM *a, const BIGNUM *field,
+                    BN_CTX *ctx)
+{
+    int top = a->top, i;
+    int carry;
+    register BN_ULONG *r_d, *a_d = a->d;
+    union {
+        BN_ULONG bn[BN_NIST_192_TOP];
+        unsigned int ui[BN_NIST_192_TOP * sizeof(BN_ULONG) /
+                        sizeof(unsigned int)];
+    } buf;
+    BN_ULONG c_d[BN_NIST_192_TOP], *res;
+    PTR_SIZE_INT mask;
+    static const BIGNUM _bignum_nist_p_192_sqr = {
+        (BN_ULONG *)_nist_p_192_sqr,
+        OSSL_NELEM(_nist_p_192_sqr),
+        OSSL_NELEM(_nist_p_192_sqr),
+        0, BN_FLG_STATIC_DATA
+    };
+
+    field = &_bignum_nist_p_192; /* just to make sure */
+
+    if (BN_is_negative(a) || BN_ucmp(a, &_bignum_nist_p_192_sqr) >= 0)
+        return BN_nnmod(r, a, field, ctx);
+
+    i = BN_ucmp(field, a);
+    if (i == 0) {
+        BN_zero(r);
+        return 1;
+    } else if (i > 0)
+        return (r == a) ? 1 : (BN_copy(r, a) != NULL);
+
+    if (r != a) {
+        if (!bn_wexpand(r, BN_NIST_192_TOP))
+            return 0;
+        r_d = r->d;
+        nist_cp_bn(r_d, a_d, BN_NIST_192_TOP);
+    } else
+        r_d = a_d;
+
+    nist_cp_bn_0(buf.bn, a_d + BN_NIST_192_TOP, top - BN_NIST_192_TOP,
+                 BN_NIST_192_TOP);
+
+#if defined(NIST_INT64)
+    {
+        NIST_INT64 acc;         /* accumulator */
+        unsigned int *rp = (unsigned int *)r_d;
+        const unsigned int *bp = (const unsigned int *)buf.ui;
+
+        acc = rp[0];
+        acc += bp[3 * 2 - 6];
+        acc += bp[5 * 2 - 6];
+        rp[0] = (unsigned int)acc;
+        acc >>= 32;
+
+        acc += rp[1];
+        acc += bp[3 * 2 - 5];
+        acc += bp[5 * 2 - 5];
+        rp[1] = (unsigned int)acc;
+        acc >>= 32;
+
+        acc += rp[2];
+        acc += bp[3 * 2 - 6];
+        acc += bp[4 * 2 - 6];
+        acc += bp[5 * 2 - 6];
+        rp[2] = (unsigned int)acc;
+        acc >>= 32;
+
+        acc += rp[3];
+        acc += bp[3 * 2 - 5];
+        acc += bp[4 * 2 - 5];
+        acc += bp[5 * 2 - 5];
+        rp[3] = (unsigned int)acc;
+        acc >>= 32;
+
+        acc += rp[4];
+        acc += bp[4 * 2 - 6];
+        acc += bp[5 * 2 - 6];
+        rp[4] = (unsigned int)acc;
+        acc >>= 32;
+
+        acc += rp[5];
+        acc += bp[4 * 2 - 5];
+        acc += bp[5 * 2 - 5];
+        rp[5] = (unsigned int)acc;
+
+        carry = (int)(acc >> 32);
+    }
+#else
+    {
+        BN_ULONG t_d[BN_NIST_192_TOP];
+
+        nist_set_192(t_d, buf.bn, 0, 3, 3);
+        carry = (int)bn_add_words(r_d, r_d, t_d, BN_NIST_192_TOP);
+        nist_set_192(t_d, buf.bn, 4, 4, 0);
+        carry += (int)bn_add_words(r_d, r_d, t_d, BN_NIST_192_TOP);
+        nist_set_192(t_d, buf.bn, 5, 5, 5)
+            carry += (int)bn_add_words(r_d, r_d, t_d, BN_NIST_192_TOP);
+    }
+#endif
+    if (carry > 0)
+        carry =
+            (int)bn_sub_words(r_d, r_d, _nist_p_192[carry - 1],
+                              BN_NIST_192_TOP);
+    else
+        carry = 1;
+
+    /*
+     * we need 'if (carry==0 || result>=modulus) result-=modulus;'
+     * as comparison implies subtraction, we can write
+     * 'tmp=result-modulus; if (!carry || !borrow) result=tmp;'
+     * this is what happens below, but without explicit if:-) a.
+     */
+    mask =
+        0 - (PTR_SIZE_INT) bn_sub_words(c_d, r_d, _nist_p_192[0],
+                                        BN_NIST_192_TOP);
+    mask &= 0 - (PTR_SIZE_INT) carry;
+    res = c_d;
+    res = (BN_ULONG *)
+        (((PTR_SIZE_INT) res & ~mask) | ((PTR_SIZE_INT) r_d & mask));
+    nist_cp_bn(r_d, res, BN_NIST_192_TOP);
+    r->top = BN_NIST_192_TOP;
+    bn_correct_top(r);
+
+    return 1;
+}
+
+typedef BN_ULONG (*bn_addsub_f) (BN_ULONG *, const BN_ULONG *,
+                                 const BN_ULONG *, int);
+
+#define nist_set_224(to, from, a1, a2, a3, a4, a5, a6, a7) \
+        { \
+        bn_cp_32(to, 0, from, (a7) - 7) \
+        bn_cp_32(to, 1, from, (a6) - 7) \
+        bn_cp_32(to, 2, from, (a5) - 7) \
+        bn_cp_32(to, 3, from, (a4) - 7) \
+        bn_cp_32(to, 4, from, (a3) - 7) \
+        bn_cp_32(to, 5, from, (a2) - 7) \
+        bn_cp_32(to, 6, from, (a1) - 7) \
+        }
+
+int BN_nist_mod_224(BIGNUM *r, const BIGNUM *a, const BIGNUM *field,
+                    BN_CTX *ctx)
+{
+    int top = a->top, i;
+    int carry;
+    BN_ULONG *r_d, *a_d = a->d;
+    union {
+        BN_ULONG bn[BN_NIST_224_TOP];
+        unsigned int ui[BN_NIST_224_TOP * sizeof(BN_ULONG) /
+                        sizeof(unsigned int)];
+    } buf;
+    BN_ULONG c_d[BN_NIST_224_TOP], *res;
+    PTR_SIZE_INT mask;
+    union {
+        bn_addsub_f f;
+        PTR_SIZE_INT p;
+    } u;
+    static const BIGNUM _bignum_nist_p_224_sqr = {
+        (BN_ULONG *)_nist_p_224_sqr,
+        OSSL_NELEM(_nist_p_224_sqr),
+        OSSL_NELEM(_nist_p_224_sqr),
+        0, BN_FLG_STATIC_DATA
+    };
+
+    field = &_bignum_nist_p_224; /* just to make sure */
+
+    if (BN_is_negative(a) || BN_ucmp(a, &_bignum_nist_p_224_sqr) >= 0)
+        return BN_nnmod(r, a, field, ctx);
+
+    i = BN_ucmp(field, a);
+    if (i == 0) {
+        BN_zero(r);
+        return 1;
+    } else if (i > 0)
+        return (r == a) ? 1 : (BN_copy(r, a) != NULL);
+
+    if (r != a) {
+        if (!bn_wexpand(r, BN_NIST_224_TOP))
+            return 0;
+        r_d = r->d;
+        nist_cp_bn(r_d, a_d, BN_NIST_224_TOP);
+    } else
+        r_d = a_d;
+
+#if BN_BITS2==64
+    /* copy upper 256 bits of 448 bit number ... */
+    nist_cp_bn_0(c_d, a_d + (BN_NIST_224_TOP - 1),
+                 top - (BN_NIST_224_TOP - 1), BN_NIST_224_TOP);
+    /* ... and right shift by 32 to obtain upper 224 bits */
+    nist_set_224(buf.bn, c_d, 14, 13, 12, 11, 10, 9, 8);
+    /* truncate lower part to 224 bits too */
+    r_d[BN_NIST_224_TOP - 1] &= BN_MASK2l;
+#else
+    nist_cp_bn_0(buf.bn, a_d + BN_NIST_224_TOP, top - BN_NIST_224_TOP,
+                 BN_NIST_224_TOP);
+#endif
+
+#if defined(NIST_INT64) && BN_BITS2!=64
+    {
+        NIST_INT64 acc;         /* accumulator */
+        unsigned int *rp = (unsigned int *)r_d;
+        const unsigned int *bp = (const unsigned int *)buf.ui;
+
+        acc = rp[0];
+        acc -= bp[7 - 7];
+        acc -= bp[11 - 7];
+        rp[0] = (unsigned int)acc;
+        acc >>= 32;
+
+        acc += rp[1];
+        acc -= bp[8 - 7];
+        acc -= bp[12 - 7];
+        rp[1] = (unsigned int)acc;
+        acc >>= 32;
+
+        acc += rp[2];
+        acc -= bp[9 - 7];
+        acc -= bp[13 - 7];
+        rp[2] = (unsigned int)acc;
+        acc >>= 32;
+
+        acc += rp[3];
+        acc += bp[7 - 7];
+        acc += bp[11 - 7];
+        acc -= bp[10 - 7];
+        rp[3] = (unsigned int)acc;
+        acc >>= 32;
+
+        acc += rp[4];
+        acc += bp[8 - 7];
+        acc += bp[12 - 7];
+        acc -= bp[11 - 7];
+        rp[4] = (unsigned int)acc;
+        acc >>= 32;
+
+        acc += rp[5];
+        acc += bp[9 - 7];
+        acc += bp[13 - 7];
+        acc -= bp[12 - 7];
+        rp[5] = (unsigned int)acc;
+        acc >>= 32;
+
+        acc += rp[6];
+        acc += bp[10 - 7];
+        acc -= bp[13 - 7];
+        rp[6] = (unsigned int)acc;
+
+        carry = (int)(acc >> 32);
+# if BN_BITS2==64
+        rp[7] = carry;
+# endif
+    }
+#else
+    {
+        BN_ULONG t_d[BN_NIST_224_TOP];
+
+        nist_set_224(t_d, buf.bn, 10, 9, 8, 7, 0, 0, 0);
+        carry = (int)bn_add_words(r_d, r_d, t_d, BN_NIST_224_TOP);
+        nist_set_224(t_d, buf.bn, 0, 13, 12, 11, 0, 0, 0);
+        carry += (int)bn_add_words(r_d, r_d, t_d, BN_NIST_224_TOP);
+        nist_set_224(t_d, buf.bn, 13, 12, 11, 10, 9, 8, 7);
+        carry -= (int)bn_sub_words(r_d, r_d, t_d, BN_NIST_224_TOP);
+        nist_set_224(t_d, buf.bn, 0, 0, 0, 0, 13, 12, 11);
+        carry -= (int)bn_sub_words(r_d, r_d, t_d, BN_NIST_224_TOP);
+
+# if BN_BITS2==64
+        carry = (int)(r_d[BN_NIST_224_TOP - 1] >> 32);
+# endif
+    }
+#endif
+    u.f = bn_sub_words;
+    if (carry > 0) {
+        carry =
+            (int)bn_sub_words(r_d, r_d, _nist_p_224[carry - 1],
+                              BN_NIST_224_TOP);
+#if BN_BITS2==64
+        carry = (int)(~(r_d[BN_NIST_224_TOP - 1] >> 32)) & 1;
+#endif
+    } else if (carry < 0) {
+        /*
+         * it's a bit more complicated logic in this case. if bn_add_words
+         * yields no carry, then result has to be adjusted by unconditionally
+         * *adding* the modulus. but if it does, then result has to be
+         * compared to the modulus and conditionally adjusted by
+         * *subtracting* the latter.
+         */
+        carry =
+            (int)bn_add_words(r_d, r_d, _nist_p_224[-carry - 1],
+                              BN_NIST_224_TOP);
+        mask = 0 - (PTR_SIZE_INT) carry;
+        u.p = ((PTR_SIZE_INT) bn_sub_words & mask) |
+            ((PTR_SIZE_INT) bn_add_words & ~mask);
+    } else
+        carry = 1;
+
+    /* otherwise it's effectively same as in BN_nist_mod_192... */
+    mask =
+        0 - (PTR_SIZE_INT) (*u.f) (c_d, r_d, _nist_p_224[0], BN_NIST_224_TOP);
+    mask &= 0 - (PTR_SIZE_INT) carry;
+    res = c_d;
+    res = (BN_ULONG *)(((PTR_SIZE_INT) res & ~mask) |
+                       ((PTR_SIZE_INT) r_d & mask));
+    nist_cp_bn(r_d, res, BN_NIST_224_TOP);
+    r->top = BN_NIST_224_TOP;
+    bn_correct_top(r);
+
+    return 1;
+}
+
+#define nist_set_256(to, from, a1, a2, a3, a4, a5, a6, a7, a8) \
+        { \
+        bn_cp_32(to, 0, from, (a8) - 8) \
+        bn_cp_32(to, 1, from, (a7) - 8) \
+        bn_cp_32(to, 2, from, (a6) - 8) \
+        bn_cp_32(to, 3, from, (a5) - 8) \
+        bn_cp_32(to, 4, from, (a4) - 8) \
+        bn_cp_32(to, 5, from, (a3) - 8) \
+        bn_cp_32(to, 6, from, (a2) - 8) \
+        bn_cp_32(to, 7, from, (a1) - 8) \
+        }
+
+int BN_nist_mod_256(BIGNUM *r, const BIGNUM *a, const BIGNUM *field,
+                    BN_CTX *ctx)
+{
+    int i, top = a->top;
+    int carry = 0;
+    register BN_ULONG *a_d = a->d, *r_d;
+    union {
+        BN_ULONG bn[BN_NIST_256_TOP];
+        unsigned int ui[BN_NIST_256_TOP * sizeof(BN_ULONG) /
+                        sizeof(unsigned int)];
+    } buf;
+    BN_ULONG c_d[BN_NIST_256_TOP], *res;
+    PTR_SIZE_INT mask;
+    union {
+        bn_addsub_f f;
+        PTR_SIZE_INT p;
+    } u;
+    static const BIGNUM _bignum_nist_p_256_sqr = {
+        (BN_ULONG *)_nist_p_256_sqr,
+        OSSL_NELEM(_nist_p_256_sqr),
+        OSSL_NELEM(_nist_p_256_sqr),
+        0, BN_FLG_STATIC_DATA
+    };
+
+    field = &_bignum_nist_p_256; /* just to make sure */
+
+    if (BN_is_negative(a) || BN_ucmp(a, &_bignum_nist_p_256_sqr) >= 0)
+        return BN_nnmod(r, a, field, ctx);
+
+    i = BN_ucmp(field, a);
+    if (i == 0) {
+        BN_zero(r);
+        return 1;
+    } else if (i > 0)
+        return (r == a) ? 1 : (BN_copy(r, a) != NULL);
+
+    if (r != a) {
+        if (!bn_wexpand(r, BN_NIST_256_TOP))
+            return 0;
+        r_d = r->d;
+        nist_cp_bn(r_d, a_d, BN_NIST_256_TOP);
+    } else
+        r_d = a_d;
+
+    nist_cp_bn_0(buf.bn, a_d + BN_NIST_256_TOP, top - BN_NIST_256_TOP,
+                 BN_NIST_256_TOP);
+
+#if defined(NIST_INT64)
+    {
+        NIST_INT64 acc;         /* accumulator */
+        unsigned int *rp = (unsigned int *)r_d;
+        const unsigned int *bp = (const unsigned int *)buf.ui;
+
+        acc = rp[0];
+        acc += bp[8 - 8];
+        acc += bp[9 - 8];
+        acc -= bp[11 - 8];
+        acc -= bp[12 - 8];
+        acc -= bp[13 - 8];
+        acc -= bp[14 - 8];
+        rp[0] = (unsigned int)acc;
+        acc >>= 32;
+
+        acc += rp[1];
+        acc += bp[9 - 8];
+        acc += bp[10 - 8];
+        acc -= bp[12 - 8];
+        acc -= bp[13 - 8];
+        acc -= bp[14 - 8];
+        acc -= bp[15 - 8];
+        rp[1] = (unsigned int)acc;
+        acc >>= 32;
+
+        acc += rp[2];
+        acc += bp[10 - 8];
+        acc += bp[11 - 8];
+        acc -= bp[13 - 8];
+        acc -= bp[14 - 8];
+        acc -= bp[15 - 8];
+        rp[2] = (unsigned int)acc;
+        acc >>= 32;
+
+        acc += rp[3];
+        acc += bp[11 - 8];
+        acc += bp[11 - 8];
+        acc += bp[12 - 8];
+        acc += bp[12 - 8];
+        acc += bp[13 - 8];
+        acc -= bp[15 - 8];
+        acc -= bp[8 - 8];
+        acc -= bp[9 - 8];
+        rp[3] = (unsigned int)acc;
+        acc >>= 32;
+
+        acc += rp[4];
+        acc += bp[12 - 8];
+        acc += bp[12 - 8];
+        acc += bp[13 - 8];
+        acc += bp[13 - 8];
+        acc += bp[14 - 8];
+        acc -= bp[9 - 8];
+        acc -= bp[10 - 8];
+        rp[4] = (unsigned int)acc;
+        acc >>= 32;
+
+        acc += rp[5];
+        acc += bp[13 - 8];
+        acc += bp[13 - 8];
+        acc += bp[14 - 8];
+        acc += bp[14 - 8];
+        acc += bp[15 - 8];
+        acc -= bp[10 - 8];
+        acc -= bp[11 - 8];
+        rp[5] = (unsigned int)acc;
+        acc >>= 32;
+
+        acc += rp[6];
+        acc += bp[14 - 8];
+        acc += bp[14 - 8];
+        acc += bp[15 - 8];
+        acc += bp[15 - 8];
+        acc += bp[14 - 8];
+        acc += bp[13 - 8];
+        acc -= bp[8 - 8];
+        acc -= bp[9 - 8];
+        rp[6] = (unsigned int)acc;
+        acc >>= 32;
+
+        acc += rp[7];
+        acc += bp[15 - 8];
+        acc += bp[15 - 8];
+        acc += bp[15 - 8];
+        acc += bp[8 - 8];
+        acc -= bp[10 - 8];
+        acc -= bp[11 - 8];
+        acc -= bp[12 - 8];
+        acc -= bp[13 - 8];
+        rp[7] = (unsigned int)acc;
+
+        carry = (int)(acc >> 32);
+    }
+#else
+    {
+        BN_ULONG t_d[BN_NIST_256_TOP];
+
+        /*
+         * S1
+         */
+        nist_set_256(t_d, buf.bn, 15, 14, 13, 12, 11, 0, 0, 0);
+        /*
+         * S2
+         */
+        nist_set_256(c_d, buf.bn, 0, 15, 14, 13, 12, 0, 0, 0);
+        carry = (int)bn_add_words(t_d, t_d, c_d, BN_NIST_256_TOP);
+        /* left shift */
+        {
+            register BN_ULONG *ap, t, c;
+            ap = t_d;
+            c = 0;
+            for (i = BN_NIST_256_TOP; i != 0; --i) {
+                t = *ap;
+                *(ap++) = ((t << 1) | c) & BN_MASK2;
+                c = (t & BN_TBIT) ? 1 : 0;
+            }
+            carry <<= 1;
+            carry |= c;
+        }
+        carry += (int)bn_add_words(r_d, r_d, t_d, BN_NIST_256_TOP);
+        /*
+         * S3
+         */
+        nist_set_256(t_d, buf.bn, 15, 14, 0, 0, 0, 10, 9, 8);
+        carry += (int)bn_add_words(r_d, r_d, t_d, BN_NIST_256_TOP);
+        /*
+         * S4
+         */
+        nist_set_256(t_d, buf.bn, 8, 13, 15, 14, 13, 11, 10, 9);
+        carry += (int)bn_add_words(r_d, r_d, t_d, BN_NIST_256_TOP);
+        /*
+         * D1
+         */
+        nist_set_256(t_d, buf.bn, 10, 8, 0, 0, 0, 13, 12, 11);
+        carry -= (int)bn_sub_words(r_d, r_d, t_d, BN_NIST_256_TOP);
+        /*
+         * D2
+         */
+        nist_set_256(t_d, buf.bn, 11, 9, 0, 0, 15, 14, 13, 12);
+        carry -= (int)bn_sub_words(r_d, r_d, t_d, BN_NIST_256_TOP);
+        /*
+         * D3
+         */
+        nist_set_256(t_d, buf.bn, 12, 0, 10, 9, 8, 15, 14, 13);
+        carry -= (int)bn_sub_words(r_d, r_d, t_d, BN_NIST_256_TOP);
+        /*
+         * D4
+         */
+        nist_set_256(t_d, buf.bn, 13, 0, 11, 10, 9, 0, 15, 14);
+        carry -= (int)bn_sub_words(r_d, r_d, t_d, BN_NIST_256_TOP);
+
+    }
+#endif
+    /* see BN_nist_mod_224 for explanation */
+    u.f = bn_sub_words;
+    if (carry > 0)
+        carry =
+            (int)bn_sub_words(r_d, r_d, _nist_p_256[carry - 1],
+                              BN_NIST_256_TOP);
+    else if (carry < 0) {
+        carry =
+            (int)bn_add_words(r_d, r_d, _nist_p_256[-carry - 1],
+                              BN_NIST_256_TOP);
+        mask = 0 - (PTR_SIZE_INT) carry;
+        u.p = ((PTR_SIZE_INT) bn_sub_words & mask) |
+            ((PTR_SIZE_INT) bn_add_words & ~mask);
+    } else
+        carry = 1;
+
+    mask =
+        0 - (PTR_SIZE_INT) (*u.f) (c_d, r_d, _nist_p_256[0], BN_NIST_256_TOP);
+    mask &= 0 - (PTR_SIZE_INT) carry;
+    res = c_d;
+    res = (BN_ULONG *)(((PTR_SIZE_INT) res & ~mask) |
+                       ((PTR_SIZE_INT) r_d & mask));
+    nist_cp_bn(r_d, res, BN_NIST_256_TOP);
+    r->top = BN_NIST_256_TOP;
+    bn_correct_top(r);
+
+    return 1;
+}
+
+#define nist_set_384(to,from,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12) \
+        { \
+        bn_cp_32(to, 0, from,  (a12) - 12) \
+        bn_cp_32(to, 1, from,  (a11) - 12) \
+        bn_cp_32(to, 2, from,  (a10) - 12) \
+        bn_cp_32(to, 3, from,  (a9) - 12)  \
+        bn_cp_32(to, 4, from,  (a8) - 12)  \
+        bn_cp_32(to, 5, from,  (a7) - 12)  \
+        bn_cp_32(to, 6, from,  (a6) - 12)  \
+        bn_cp_32(to, 7, from,  (a5) - 12)  \
+        bn_cp_32(to, 8, from,  (a4) - 12)  \
+        bn_cp_32(to, 9, from,  (a3) - 12)  \
+        bn_cp_32(to, 10, from, (a2) - 12)  \
+        bn_cp_32(to, 11, from, (a1) - 12)  \
+        }
+
+int BN_nist_mod_384(BIGNUM *r, const BIGNUM *a, const BIGNUM *field,
+                    BN_CTX *ctx)
+{
+    int i, top = a->top;
+    int carry = 0;
+    register BN_ULONG *r_d, *a_d = a->d;
+    union {
+        BN_ULONG bn[BN_NIST_384_TOP];
+        unsigned int ui[BN_NIST_384_TOP * sizeof(BN_ULONG) /
+                        sizeof(unsigned int)];
+    } buf;
+    BN_ULONG c_d[BN_NIST_384_TOP], *res;
+    PTR_SIZE_INT mask;
+    union {
+        bn_addsub_f f;
+        PTR_SIZE_INT p;
+    } u;
+    static const BIGNUM _bignum_nist_p_384_sqr = {
+        (BN_ULONG *)_nist_p_384_sqr,
+        OSSL_NELEM(_nist_p_384_sqr),
+        OSSL_NELEM(_nist_p_384_sqr),
+        0, BN_FLG_STATIC_DATA
+    };
+
+    field = &_bignum_nist_p_384; /* just to make sure */
+
+    if (BN_is_negative(a) || BN_ucmp(a, &_bignum_nist_p_384_sqr) >= 0)
+        return BN_nnmod(r, a, field, ctx);
+
+    i = BN_ucmp(field, a);
+    if (i == 0) {
+        BN_zero(r);
+        return 1;
+    } else if (i > 0)
+        return (r == a) ? 1 : (BN_copy(r, a) != NULL);
+
+    if (r != a) {
+        if (!bn_wexpand(r, BN_NIST_384_TOP))
+            return 0;
+        r_d = r->d;
+        nist_cp_bn(r_d, a_d, BN_NIST_384_TOP);
+    } else
+        r_d = a_d;
+
+    nist_cp_bn_0(buf.bn, a_d + BN_NIST_384_TOP, top - BN_NIST_384_TOP,
+                 BN_NIST_384_TOP);
+
+#if defined(NIST_INT64)
+    {
+        NIST_INT64 acc;         /* accumulator */
+        unsigned int *rp = (unsigned int *)r_d;
+        const unsigned int *bp = (const unsigned int *)buf.ui;
+
+        acc = rp[0];
+        acc += bp[12 - 12];
+        acc += bp[21 - 12];
+        acc += bp[20 - 12];
+        acc -= bp[23 - 12];
+        rp[0] = (unsigned int)acc;
+        acc >>= 32;
+
+        acc += rp[1];
+        acc += bp[13 - 12];
+        acc += bp[22 - 12];
+        acc += bp[23 - 12];
+        acc -= bp[12 - 12];
+        acc -= bp[20 - 12];
+        rp[1] = (unsigned int)acc;
+        acc >>= 32;
+
+        acc += rp[2];
+        acc += bp[14 - 12];
+        acc += bp[23 - 12];
+        acc -= bp[13 - 12];
+        acc -= bp[21 - 12];
+        rp[2] = (unsigned int)acc;
+        acc >>= 32;
+
+        acc += rp[3];
+        acc += bp[15 - 12];
+        acc += bp[12 - 12];
+        acc += bp[20 - 12];
+        acc += bp[21 - 12];
+        acc -= bp[14 - 12];
+        acc -= bp[22 - 12];
+        acc -= bp[23 - 12];
+        rp[3] = (unsigned int)acc;
+        acc >>= 32;
+
+        acc += rp[4];
+        acc += bp[21 - 12];
+        acc += bp[21 - 12];
+        acc += bp[16 - 12];
+        acc += bp[13 - 12];
+        acc += bp[12 - 12];
+        acc += bp[20 - 12];
+        acc += bp[22 - 12];
+        acc -= bp[15 - 12];
+        acc -= bp[23 - 12];
+        acc -= bp[23 - 12];
+        rp[4] = (unsigned int)acc;
+        acc >>= 32;
+
+        acc += rp[5];
+        acc += bp[22 - 12];
+        acc += bp[22 - 12];
+        acc += bp[17 - 12];
+        acc += bp[14 - 12];
+        acc += bp[13 - 12];
+        acc += bp[21 - 12];
+        acc += bp[23 - 12];
+        acc -= bp[16 - 12];
+        rp[5] = (unsigned int)acc;
+        acc >>= 32;
+
+        acc += rp[6];
+        acc += bp[23 - 12];
+        acc += bp[23 - 12];
+        acc += bp[18 - 12];
+        acc += bp[15 - 12];
+        acc += bp[14 - 12];
+        acc += bp[22 - 12];
+        acc -= bp[17 - 12];
+        rp[6] = (unsigned int)acc;
+        acc >>= 32;
+
+        acc += rp[7];
+        acc += bp[19 - 12];
+        acc += bp[16 - 12];
+        acc += bp[15 - 12];
+        acc += bp[23 - 12];
+        acc -= bp[18 - 12];
+        rp[7] = (unsigned int)acc;
+        acc >>= 32;
+
+        acc += rp[8];
+        acc += bp[20 - 12];
+        acc += bp[17 - 12];
+        acc += bp[16 - 12];
+        acc -= bp[19 - 12];
+        rp[8] = (unsigned int)acc;
+        acc >>= 32;
+
+        acc += rp[9];
+        acc += bp[21 - 12];
+        acc += bp[18 - 12];
+        acc += bp[17 - 12];
+        acc -= bp[20 - 12];
+        rp[9] = (unsigned int)acc;
+        acc >>= 32;
+
+        acc += rp[10];
+        acc += bp[22 - 12];
+        acc += bp[19 - 12];
+        acc += bp[18 - 12];
+        acc -= bp[21 - 12];
+        rp[10] = (unsigned int)acc;
+        acc >>= 32;
+
+        acc += rp[11];
+        acc += bp[23 - 12];
+        acc += bp[20 - 12];
+        acc += bp[19 - 12];
+        acc -= bp[22 - 12];
+        rp[11] = (unsigned int)acc;
+
+        carry = (int)(acc >> 32);
+    }
+#else
+    {
+        BN_ULONG t_d[BN_NIST_384_TOP];
+
+        /*
+         * S1
+         */
+        nist_set_256(t_d, buf.bn, 0, 0, 0, 0, 0, 23 - 4, 22 - 4, 21 - 4);
+        /* left shift */
+        {
+            register BN_ULONG *ap, t, c;
+            ap = t_d;
+            c = 0;
+            for (i = 3; i != 0; --i) {
+                t = *ap;
+                *(ap++) = ((t << 1) | c) & BN_MASK2;
+                c = (t & BN_TBIT) ? 1 : 0;
+            }
+            *ap = c;
+        }
+        carry =
+            (int)bn_add_words(r_d + (128 / BN_BITS2), r_d + (128 / BN_BITS2),
+                              t_d, BN_NIST_256_TOP);
+        /*
+         * S2
+         */
+        carry += (int)bn_add_words(r_d, r_d, buf.bn, BN_NIST_384_TOP);
+        /*
+         * S3
+         */
+        nist_set_384(t_d, buf.bn, 20, 19, 18, 17, 16, 15, 14, 13, 12, 23, 22,
+                     21);
+        carry += (int)bn_add_words(r_d, r_d, t_d, BN_NIST_384_TOP);
+        /*
+         * S4
+         */
+        nist_set_384(t_d, buf.bn, 19, 18, 17, 16, 15, 14, 13, 12, 20, 0, 23,
+                     0);
+        carry += (int)bn_add_words(r_d, r_d, t_d, BN_NIST_384_TOP);
+        /*
+         * S5
+         */
+        nist_set_384(t_d, buf.bn, 0, 0, 0, 0, 23, 22, 21, 20, 0, 0, 0, 0);
+        carry += (int)bn_add_words(r_d, r_d, t_d, BN_NIST_384_TOP);
+        /*
+         * S6
+         */
+        nist_set_384(t_d, buf.bn, 0, 0, 0, 0, 0, 0, 23, 22, 21, 0, 0, 20);
+        carry += (int)bn_add_words(r_d, r_d, t_d, BN_NIST_384_TOP);
+        /*
+         * D1
+         */
+        nist_set_384(t_d, buf.bn, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12,
+                     23);
+        carry -= (int)bn_sub_words(r_d, r_d, t_d, BN_NIST_384_TOP);
+        /*
+         * D2
+         */
+        nist_set_384(t_d, buf.bn, 0, 0, 0, 0, 0, 0, 0, 23, 22, 21, 20, 0);
+        carry -= (int)bn_sub_words(r_d, r_d, t_d, BN_NIST_384_TOP);
+        /*
+         * D3
+         */
+        nist_set_384(t_d, buf.bn, 0, 0, 0, 0, 0, 0, 0, 23, 23, 0, 0, 0);
+        carry -= (int)bn_sub_words(r_d, r_d, t_d, BN_NIST_384_TOP);
+
+    }
+#endif
+    /* see BN_nist_mod_224 for explanation */
+    u.f = bn_sub_words;
+    if (carry > 0)
+        carry =
+            (int)bn_sub_words(r_d, r_d, _nist_p_384[carry - 1],
+                              BN_NIST_384_TOP);
+    else if (carry < 0) {
+        carry =
+            (int)bn_add_words(r_d, r_d, _nist_p_384[-carry - 1],
+                              BN_NIST_384_TOP);
+        mask = 0 - (PTR_SIZE_INT) carry;
+        u.p = ((PTR_SIZE_INT) bn_sub_words & mask) |
+            ((PTR_SIZE_INT) bn_add_words & ~mask);
+    } else
+        carry = 1;
+
+    mask =
+        0 - (PTR_SIZE_INT) (*u.f) (c_d, r_d, _nist_p_384[0], BN_NIST_384_TOP);
+    mask &= 0 - (PTR_SIZE_INT) carry;
+    res = c_d;
+    res = (BN_ULONG *)(((PTR_SIZE_INT) res & ~mask) |
+                       ((PTR_SIZE_INT) r_d & mask));
+    nist_cp_bn(r_d, res, BN_NIST_384_TOP);
+    r->top = BN_NIST_384_TOP;
+    bn_correct_top(r);
+
+    return 1;
+}
+
+#define BN_NIST_521_RSHIFT      (521%BN_BITS2)
+#define BN_NIST_521_LSHIFT      (BN_BITS2-BN_NIST_521_RSHIFT)
+#define BN_NIST_521_TOP_MASK    ((BN_ULONG)BN_MASK2>>BN_NIST_521_LSHIFT)
+
+int BN_nist_mod_521(BIGNUM *r, const BIGNUM *a, const BIGNUM *field,
+                    BN_CTX *ctx)
+{
+    int top = a->top, i;
+    BN_ULONG *r_d, *a_d = a->d, t_d[BN_NIST_521_TOP], val, tmp, *res;
+    PTR_SIZE_INT mask;
+    static const BIGNUM _bignum_nist_p_521_sqr = {
+        (BN_ULONG *)_nist_p_521_sqr,
+        OSSL_NELEM(_nist_p_521_sqr),
+        OSSL_NELEM(_nist_p_521_sqr),
+        0, BN_FLG_STATIC_DATA
+    };
+
+    field = &_bignum_nist_p_521; /* just to make sure */
+
+    if (BN_is_negative(a) || BN_ucmp(a, &_bignum_nist_p_521_sqr) >= 0)
+        return BN_nnmod(r, a, field, ctx);
+
+    i = BN_ucmp(field, a);
+    if (i == 0) {
+        BN_zero(r);
+        return 1;
+    } else if (i > 0)
+        return (r == a) ? 1 : (BN_copy(r, a) != NULL);
+
+    if (r != a) {
+        if (!bn_wexpand(r, BN_NIST_521_TOP))
+            return 0;
+        r_d = r->d;
+        nist_cp_bn(r_d, a_d, BN_NIST_521_TOP);
+    } else
+        r_d = a_d;
+
+    /* upper 521 bits, copy ... */
+    nist_cp_bn_0(t_d, a_d + (BN_NIST_521_TOP - 1),
+                 top - (BN_NIST_521_TOP - 1), BN_NIST_521_TOP);
+    /* ... and right shift */
+    for (val = t_d[0], i = 0; i < BN_NIST_521_TOP - 1; i++) {
+#if 0
+        /*
+         * MSC ARM compiler [version 2013, presumably even earlier,
+         * much earlier] miscompiles this code, but not one in
+         * #else section. See RT#3541.
+         */
+        tmp = val >> BN_NIST_521_RSHIFT;
+        val = t_d[i + 1];
+        t_d[i] = (tmp | val << BN_NIST_521_LSHIFT) & BN_MASK2;
+#else
+        t_d[i] = (val >> BN_NIST_521_RSHIFT |
+                  (tmp = t_d[i + 1]) << BN_NIST_521_LSHIFT) & BN_MASK2;
+        val = tmp;
+#endif
+    }
+    t_d[i] = val >> BN_NIST_521_RSHIFT;
+    /* lower 521 bits */
+    r_d[i] &= BN_NIST_521_TOP_MASK;
+
+    bn_add_words(r_d, r_d, t_d, BN_NIST_521_TOP);
+    mask =
+        0 - (PTR_SIZE_INT) bn_sub_words(t_d, r_d, _nist_p_521,
+                                        BN_NIST_521_TOP);
+    res = t_d;
+    res = (BN_ULONG *)(((PTR_SIZE_INT) res & ~mask) |
+                       ((PTR_SIZE_INT) r_d & mask));
+    nist_cp_bn(r_d, res, BN_NIST_521_TOP);
+    r->top = BN_NIST_521_TOP;
+    bn_correct_top(r);
+
+    return 1;
+}
+
+int (*BN_nist_mod_func(const BIGNUM *p)) (BIGNUM *r, const BIGNUM *a,
+                                          const BIGNUM *field, BN_CTX *ctx) {
+    if (BN_ucmp(&_bignum_nist_p_192, p) == 0)
+        return BN_nist_mod_192;
+    if (BN_ucmp(&_bignum_nist_p_224, p) == 0)
+        return BN_nist_mod_224;
+    if (BN_ucmp(&_bignum_nist_p_256, p) == 0)
+        return BN_nist_mod_256;
+    if (BN_ucmp(&_bignum_nist_p_384, p) == 0)
+        return BN_nist_mod_384;
+    if (BN_ucmp(&_bignum_nist_p_521, p) == 0)
+        return BN_nist_mod_521;
+    return 0;
+}
diff --git a/crypto/bn/bn_prime.c b/crypto/bn/bn_prime.c
new file mode 100644
index 000000000..7103acfe0
--- /dev/null
+++ b/crypto/bn/bn_prime.c
@@ -0,0 +1,608 @@
+/*
+ * WARNING: do not edit!
+ * Generated by crypto/bn/bn_prime.pl
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include "internal/cryptlib.h"
+#include "bn_lcl.h"
+
+/*
+ * The quick sieve algorithm approach to weeding out primes is Philip
+ * Zimmermann's, as implemented in PGP.  I have had a read of his comments
+ * and implemented my own version.
+ */
+#include "bn_prime.h"
+
+static int witness(BIGNUM *w, const BIGNUM *a, const BIGNUM *a1,
+                   const BIGNUM *a1_odd, int k, BN_CTX *ctx,
+                   BN_MONT_CTX *mont);
+static int probable_prime(BIGNUM *rnd, int bits, prime_t *mods);
+static int probable_prime_dh_safe(BIGNUM *rnd, int bits,
+                                  const BIGNUM *add, const BIGNUM *rem,
+                                  BN_CTX *ctx);
+
+static const int prime_offsets[480] = {
+    13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83,
+    89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163,
+    167, 169, 173, 179, 181, 191, 193, 197, 199, 211, 221, 223, 227, 229,
+    233, 239, 241, 247, 251, 257, 263, 269, 271, 277, 281, 283, 289, 293,
+    299, 307, 311, 313, 317, 323, 331, 337, 347, 349, 353, 359, 361, 367,
+    373, 377, 379, 383, 389, 391, 397, 401, 403, 409, 419, 421, 431, 433,
+    437, 439, 443, 449, 457, 461, 463, 467, 479, 481, 487, 491, 493, 499,
+    503, 509, 521, 523, 527, 529, 533, 541, 547, 551, 557, 559, 563, 569,
+    571, 577, 587, 589, 593, 599, 601, 607, 611, 613, 617, 619, 629, 631,
+    641, 643, 647, 653, 659, 661, 667, 673, 677, 683, 689, 691, 697, 701,
+    703, 709, 713, 719, 727, 731, 733, 739, 743, 751, 757, 761, 767, 769,
+    773, 779, 787, 793, 797, 799, 809, 811, 817, 821, 823, 827, 829, 839,
+    841, 851, 853, 857, 859, 863, 871, 877, 881, 883, 887, 893, 899, 901,
+    907, 911, 919, 923, 929, 937, 941, 943, 947, 949, 953, 961, 967, 971,
+    977, 983, 989, 991, 997, 1003, 1007, 1009, 1013, 1019, 1021, 1027, 1031,
+    1033, 1037, 1039, 1049, 1051, 1061, 1063, 1069, 1073, 1079, 1081, 1087,
+    1091, 1093, 1097, 1103, 1109, 1117, 1121, 1123, 1129, 1139, 1147, 1151,
+    1153, 1157, 1159, 1163, 1171, 1181, 1187, 1189, 1193, 1201, 1207, 1213,
+    1217, 1219, 1223, 1229, 1231, 1237, 1241, 1247, 1249, 1259, 1261, 1271,
+    1273, 1277, 1279, 1283, 1289, 1291, 1297, 1301, 1303, 1307, 1313, 1319,
+    1321, 1327, 1333, 1339, 1343, 1349, 1357, 1361, 1363, 1367, 1369, 1373,
+    1381, 1387, 1391, 1399, 1403, 1409, 1411, 1417, 1423, 1427, 1429, 1433,
+    1439, 1447, 1451, 1453, 1457, 1459, 1469, 1471, 1481, 1483, 1487, 1489,
+    1493, 1499, 1501, 1511, 1513, 1517, 1523, 1531, 1537, 1541, 1543, 1549,
+    1553, 1559, 1567, 1571, 1577, 1579, 1583, 1591, 1597, 1601, 1607, 1609,
+    1613, 1619, 1621, 1627, 1633, 1637, 1643, 1649, 1651, 1657, 1663, 1667,
+    1669, 1679, 1681, 1691, 1693, 1697, 1699, 1703, 1709, 1711, 1717, 1721,
+    1723, 1733, 1739, 1741, 1747, 1751, 1753, 1759, 1763, 1769, 1777, 1781,
+    1783, 1787, 1789, 1801, 1807, 1811, 1817, 1819, 1823, 1829, 1831, 1843,
+    1847, 1849, 1853, 1861, 1867, 1871, 1873, 1877, 1879, 1889, 1891, 1901,
+    1907, 1909, 1913, 1919, 1921, 1927, 1931, 1933, 1937, 1943, 1949, 1951,
+    1957, 1961, 1963, 1973, 1979, 1987, 1993, 1997, 1999, 2003, 2011, 2017,
+    2021, 2027, 2029, 2033, 2039, 2041, 2047, 2053, 2059, 2063, 2069, 2071,
+    2077, 2081, 2083, 2087, 2089, 2099, 2111, 2113, 2117, 2119, 2129, 2131,
+    2137, 2141, 2143, 2147, 2153, 2159, 2161, 2171, 2173, 2179, 2183, 2197,
+    2201, 2203, 2207, 2209, 2213, 2221, 2227, 2231, 2237, 2239, 2243, 2249,
+    2251, 2257, 2263, 2267, 2269, 2273, 2279, 2281, 2287, 2291, 2293, 2297,
+    2309, 2311
+};
+
+static const int prime_offset_count = 480;
+static const int prime_multiplier = 2310;
+static const int prime_multiplier_bits = 11; /* 2^|prime_multiplier_bits| <=
+                                              * |prime_multiplier| */
+static const int first_prime_index = 5;
+
+int BN_GENCB_call(BN_GENCB *cb, int a, int b)
+{
+    /* No callback means continue */
+    if (!cb)
+        return 1;
+    switch (cb->ver) {
+    case 1:
+        /* Deprecated-style callbacks */
+        if (!cb->cb.cb_1)
+            return 1;
+        cb->cb.cb_1(a, b, cb->arg);
+        return 1;
+    case 2:
+        /* New-style callbacks */
+        return cb->cb.cb_2(a, b, cb);
+    default:
+        break;
+    }
+    /* Unrecognised callback type */
+    return 0;
+}
+
+int BN_generate_prime_ex(BIGNUM *ret, int bits, int safe,
+                         const BIGNUM *add, const BIGNUM *rem, BN_GENCB *cb)
+{
+    BIGNUM *t;
+    int found = 0;
+    int i, j, c1 = 0;
+    BN_CTX *ctx = NULL;
+    prime_t *mods = NULL;
+    int checks = BN_prime_checks_for_size(bits);
+
+    if (bits < 2) {
+        /* There are no prime numbers this small. */
+        BNerr(BN_F_BN_GENERATE_PRIME_EX, BN_R_BITS_TOO_SMALL);
+        return 0;
+    } else if (bits == 2 && safe) {
+        /* The smallest safe prime (7) is three bits. */
+        BNerr(BN_F_BN_GENERATE_PRIME_EX, BN_R_BITS_TOO_SMALL);
+        return 0;
+    }
+
+    mods = OPENSSL_zalloc(sizeof(*mods) * NUMPRIMES);
+    if (mods == NULL)
+        goto err;
+
+    ctx = BN_CTX_new();
+    if (ctx == NULL)
+        goto err;
+    BN_CTX_start(ctx);
+    t = BN_CTX_get(ctx);
+    if (!t)
+        goto err;
+ loop:
+    /* make a random number and set the top and bottom bits */
+    if (add == NULL) {
+        if (!probable_prime(ret, bits, mods))
+            goto err;
+    } else {
+        if (safe) {
+            if (!probable_prime_dh_safe(ret, bits, add, rem, ctx))
+                goto err;
+        } else {
+            if (!bn_probable_prime_dh(ret, bits, add, rem, ctx))
+                goto err;
+        }
+    }
+
+    if (!BN_GENCB_call(cb, 0, c1++))
+        /* aborted */
+        goto err;
+
+    if (!safe) {
+        i = BN_is_prime_fasttest_ex(ret, checks, ctx, 0, cb);
+        if (i == -1)
+            goto err;
+        if (i == 0)
+            goto loop;
+    } else {
+        /*
+         * for "safe prime" generation, check that (p-1)/2 is prime. Since a
+         * prime is odd, We just need to divide by 2
+         */
+        if (!BN_rshift1(t, ret))
+            goto err;
+
+        for (i = 0; i < checks; i++) {
+            j = BN_is_prime_fasttest_ex(ret, 1, ctx, 0, cb);
+            if (j == -1)
+                goto err;
+            if (j == 0)
+                goto loop;
+
+            j = BN_is_prime_fasttest_ex(t, 1, ctx, 0, cb);
+            if (j == -1)
+                goto err;
+            if (j == 0)
+                goto loop;
+
+            if (!BN_GENCB_call(cb, 2, c1 - 1))
+                goto err;
+            /* We have a safe prime test pass */
+        }
+    }
+    /* we have a prime :-) */
+    found = 1;
+ err:
+    OPENSSL_free(mods);
+    if (ctx != NULL)
+        BN_CTX_end(ctx);
+    BN_CTX_free(ctx);
+    bn_check_top(ret);
+    return found;
+}
+
+int BN_is_prime_ex(const BIGNUM *a, int checks, BN_CTX *ctx_passed,
+                   BN_GENCB *cb)
+{
+    return BN_is_prime_fasttest_ex(a, checks, ctx_passed, 0, cb);
+}
+
+int BN_is_prime_fasttest_ex(const BIGNUM *a, int checks, BN_CTX *ctx_passed,
+                            int do_trial_division, BN_GENCB *cb)
+{
+    int i, j, ret = -1;
+    int k;
+    BN_CTX *ctx = NULL;
+    BIGNUM *A1, *A1_odd, *check; /* taken from ctx */
+    BN_MONT_CTX *mont = NULL;
+    const BIGNUM *A = NULL;
+
+    if (BN_cmp(a, BN_value_one()) <= 0)
+        return 0;
+
+    if (checks == BN_prime_checks)
+        checks = BN_prime_checks_for_size(BN_num_bits(a));
+
+    /* first look for small factors */
+    if (!BN_is_odd(a))
+        /* a is even => a is prime if and only if a == 2 */
+        return BN_is_word(a, 2);
+    if (do_trial_division) {
+        for (i = 1; i < NUMPRIMES; i++) {
+            BN_ULONG mod = BN_mod_word(a, primes[i]);
+            if (mod == (BN_ULONG)-1)
+                goto err;
+            if (mod == 0)
+                return 0;
+        }
+        if (!BN_GENCB_call(cb, 1, -1))
+            goto err;
+    }
+
+    if (ctx_passed != NULL)
+        ctx = ctx_passed;
+    else if ((ctx = BN_CTX_new()) == NULL)
+        goto err;
+    BN_CTX_start(ctx);
+
+    /* A := abs(a) */
+    if (a->neg) {
+        BIGNUM *t;
+        if ((t = BN_CTX_get(ctx)) == NULL)
+            goto err;
+        if (BN_copy(t, a) == NULL)
+            goto err;
+        t->neg = 0;
+        A = t;
+    } else
+        A = a;
+    A1 = BN_CTX_get(ctx);
+    A1_odd = BN_CTX_get(ctx);
+    check = BN_CTX_get(ctx);
+    if (check == NULL)
+        goto err;
+
+    /* compute A1 := A - 1 */
+    if (!BN_copy(A1, A))
+        goto err;
+    if (!BN_sub_word(A1, 1))
+        goto err;
+    if (BN_is_zero(A1)) {
+        ret = 0;
+        goto err;
+    }
+
+    /* write  A1  as  A1_odd * 2^k */
+    k = 1;
+    while (!BN_is_bit_set(A1, k))
+        k++;
+    if (!BN_rshift(A1_odd, A1, k))
+        goto err;
+
+    /* Montgomery setup for computations mod A */
+    mont = BN_MONT_CTX_new();
+    if (mont == NULL)
+        goto err;
+    if (!BN_MONT_CTX_set(mont, A, ctx))
+        goto err;
+
+    for (i = 0; i < checks; i++) {
+        if (!BN_pseudo_rand_range(check, A1))
+            goto err;
+        if (!BN_add_word(check, 1))
+            goto err;
+        /* now 1 <= check < A */
+
+        j = witness(check, A, A1, A1_odd, k, ctx, mont);
+        if (j == -1)
+            goto err;
+        if (j) {
+            ret = 0;
+            goto err;
+        }
+        if (!BN_GENCB_call(cb, 1, i))
+            goto err;
+    }
+    ret = 1;
+ err:
+    if (ctx != NULL) {
+        BN_CTX_end(ctx);
+        if (ctx_passed == NULL)
+            BN_CTX_free(ctx);
+    }
+    BN_MONT_CTX_free(mont);
+
+    return (ret);
+}
+
+int bn_probable_prime_dh_retry(BIGNUM *rnd, int bits, BN_CTX *ctx)
+{
+    int i;
+    int ret = 0;
+
+ loop:
+    if (!BN_rand(rnd, bits, BN_RAND_TOP_ONE, BN_RAND_BOTTOM_ODD))
+        goto err;
+
+    /* we now have a random number 'rand' to test. */
+
+    for (i = 1; i < NUMPRIMES; i++) {
+        /* check that rnd is a prime */
+        BN_ULONG mod = BN_mod_word(rnd, (BN_ULONG)primes[i]);
+        if (mod == (BN_ULONG)-1)
+            goto err;
+        if (mod <= 1) {
+            goto loop;
+        }
+    }
+    ret = 1;
+
+ err:
+    bn_check_top(rnd);
+    return (ret);
+}
+
+int bn_probable_prime_dh_coprime(BIGNUM *rnd, int bits, BN_CTX *ctx)
+{
+    int i;
+    BIGNUM *offset_index;
+    BIGNUM *offset_count;
+    int ret = 0;
+
+    OPENSSL_assert(bits > prime_multiplier_bits);
+
+    BN_CTX_start(ctx);
+    if ((offset_index = BN_CTX_get(ctx)) == NULL)
+        goto err;
+    if ((offset_count = BN_CTX_get(ctx)) == NULL)
+        goto err;
+
+    if (!BN_add_word(offset_count, prime_offset_count))
+        goto err;
+
+ loop:
+    if (!BN_rand(rnd, bits - prime_multiplier_bits,
+                 BN_RAND_TOP_ONE, BN_RAND_BOTTOM_ODD))
+        goto err;
+    if (BN_is_bit_set(rnd, bits))
+        goto loop;
+    if (!BN_rand_range(offset_index, offset_count))
+        goto err;
+
+    if (!BN_mul_word(rnd, prime_multiplier)
+        || !BN_add_word(rnd, prime_offsets[BN_get_word(offset_index)]))
+        goto err;
+
+    /* we now have a random number 'rand' to test. */
+
+    /* skip coprimes */
+    for (i = first_prime_index; i < NUMPRIMES; i++) {
+        /* check that rnd is a prime */
+        BN_ULONG mod = BN_mod_word(rnd, (BN_ULONG)primes[i]);
+        if (mod == (BN_ULONG)-1)
+            goto err;
+        if (mod <= 1)
+            goto loop;
+    }
+    ret = 1;
+
+ err:
+    BN_CTX_end(ctx);
+    bn_check_top(rnd);
+    return ret;
+}
+
+static int witness(BIGNUM *w, const BIGNUM *a, const BIGNUM *a1,
+                   const BIGNUM *a1_odd, int k, BN_CTX *ctx,
+                   BN_MONT_CTX *mont)
+{
+    if (!BN_mod_exp_mont(w, w, a1_odd, a, ctx, mont)) /* w := w^a1_odd mod a */
+        return -1;
+    if (BN_is_one(w))
+        return 0;               /* probably prime */
+    if (BN_cmp(w, a1) == 0)
+        return 0;               /* w == -1 (mod a), 'a' is probably prime */
+    while (--k) {
+        if (!BN_mod_mul(w, w, w, a, ctx)) /* w := w^2 mod a */
+            return -1;
+        if (BN_is_one(w))
+            return 1;           /* 'a' is composite, otherwise a previous 'w'
+                                 * would have been == -1 (mod 'a') */
+        if (BN_cmp(w, a1) == 0)
+            return 0;           /* w == -1 (mod a), 'a' is probably prime */
+    }
+    /*
+     * If we get here, 'w' is the (a-1)/2-th power of the original 'w', and
+     * it is neither -1 nor +1 -- so 'a' cannot be prime
+     */
+    bn_check_top(w);
+    return 1;
+}
+
+static int probable_prime(BIGNUM *rnd, int bits, prime_t *mods)
+{
+    int i;
+    BN_ULONG delta;
+    BN_ULONG maxdelta = BN_MASK2 - primes[NUMPRIMES - 1];
+    char is_single_word = bits <= BN_BITS2;
+
+ again:
+    if (!BN_rand(rnd, bits, BN_RAND_TOP_TWO, BN_RAND_BOTTOM_ODD))
+        return (0);
+    /* we now have a random number 'rnd' to test. */
+    for (i = 1; i < NUMPRIMES; i++) {
+        BN_ULONG mod = BN_mod_word(rnd, (BN_ULONG)primes[i]);
+        if (mod == (BN_ULONG)-1)
+            return 0;
+        mods[i] = (prime_t) mod;
+    }
+    /*
+     * If bits is so small that it fits into a single word then we
+     * additionally don't want to exceed that many bits.
+     */
+    if (is_single_word) {
+        BN_ULONG size_limit;
+
+        if (bits == BN_BITS2) {
+            /*
+             * Shifting by this much has undefined behaviour so we do it a
+             * different way
+             */
+            size_limit = ~((BN_ULONG)0) - BN_get_word(rnd);
+        } else {
+            size_limit = (((BN_ULONG)1) << bits) - BN_get_word(rnd) - 1;
+        }
+        if (size_limit < maxdelta)
+            maxdelta = size_limit;
+    }
+    delta = 0;
+ loop:
+    if (is_single_word) {
+        BN_ULONG rnd_word = BN_get_word(rnd);
+
+        /*-
+         * In the case that the candidate prime is a single word then
+         * we check that:
+         *   1) It's greater than primes[i] because we shouldn't reject
+         *      3 as being a prime number because it's a multiple of
+         *      three.
+         *   2) That it's not a multiple of a known prime. We don't
+         *      check that rnd-1 is also coprime to all the known
+         *      primes because there aren't many small primes where
+         *      that's true.
+         */
+        for (i = 1; i < NUMPRIMES && primes[i] < rnd_word; i++) {
+            if ((mods[i] + delta) % primes[i] == 0) {
+                delta += 2;
+                if (delta > maxdelta)
+                    goto again;
+                goto loop;
+            }
+        }
+    } else {
+        for (i = 1; i < NUMPRIMES; i++) {
+            /*
+             * check that rnd is not a prime and also that gcd(rnd-1,primes)
+             * == 1 (except for 2)
+             */
+            if (((mods[i] + delta) % primes[i]) <= 1) {
+                delta += 2;
+                if (delta > maxdelta)
+                    goto again;
+                goto loop;
+            }
+        }
+    }
+    if (!BN_add_word(rnd, delta))
+        return (0);
+    if (BN_num_bits(rnd) != bits)
+        goto again;
+    bn_check_top(rnd);
+    return (1);
+}
+
+int bn_probable_prime_dh(BIGNUM *rnd, int bits,
+                         const BIGNUM *add, const BIGNUM *rem, BN_CTX *ctx)
+{
+    int i, ret = 0;
+    BIGNUM *t1;
+
+    BN_CTX_start(ctx);
+    if ((t1 = BN_CTX_get(ctx)) == NULL)
+        goto err;
+
+    if (!BN_rand(rnd, bits, BN_RAND_TOP_ONE, BN_RAND_BOTTOM_ODD))
+        goto err;
+
+    /* we need ((rnd-rem) % add) == 0 */
+
+    if (!BN_mod(t1, rnd, add, ctx))
+        goto err;
+    if (!BN_sub(rnd, rnd, t1))
+        goto err;
+    if (rem == NULL) {
+        if (!BN_add_word(rnd, 1))
+            goto err;
+    } else {
+        if (!BN_add(rnd, rnd, rem))
+            goto err;
+    }
+
+    /* we now have a random number 'rand' to test. */
+
+ loop:
+    for (i = 1; i < NUMPRIMES; i++) {
+        /* check that rnd is a prime */
+        BN_ULONG mod = BN_mod_word(rnd, (BN_ULONG)primes[i]);
+        if (mod == (BN_ULONG)-1)
+            goto err;
+        if (mod <= 1) {
+            if (!BN_add(rnd, rnd, add))
+                goto err;
+            goto loop;
+        }
+    }
+    ret = 1;
+
+ err:
+    BN_CTX_end(ctx);
+    bn_check_top(rnd);
+    return (ret);
+}
+
+static int probable_prime_dh_safe(BIGNUM *p, int bits, const BIGNUM *padd,
+                                  const BIGNUM *rem, BN_CTX *ctx)
+{
+    int i, ret = 0;
+    BIGNUM *t1, *qadd, *q;
+
+    bits--;
+    BN_CTX_start(ctx);
+    t1 = BN_CTX_get(ctx);
+    q = BN_CTX_get(ctx);
+    qadd = BN_CTX_get(ctx);
+    if (qadd == NULL)
+        goto err;
+
+    if (!BN_rshift1(qadd, padd))
+        goto err;
+
+    if (!BN_rand(q, bits, BN_RAND_TOP_ONE, BN_RAND_BOTTOM_ODD))
+        goto err;
+
+    /* we need ((rnd-rem) % add) == 0 */
+    if (!BN_mod(t1, q, qadd, ctx))
+        goto err;
+    if (!BN_sub(q, q, t1))
+        goto err;
+    if (rem == NULL) {
+        if (!BN_add_word(q, 1))
+            goto err;
+    } else {
+        if (!BN_rshift1(t1, rem))
+            goto err;
+        if (!BN_add(q, q, t1))
+            goto err;
+    }
+
+    /* we now have a random number 'rand' to test. */
+    if (!BN_lshift1(p, q))
+        goto err;
+    if (!BN_add_word(p, 1))
+        goto err;
+
+ loop:
+    for (i = 1; i < NUMPRIMES; i++) {
+        /* check that p and q are prime */
+        /*
+         * check that for p and q gcd(p-1,primes) == 1 (except for 2)
+         */
+        BN_ULONG pmod = BN_mod_word(p, (BN_ULONG)primes[i]);
+        BN_ULONG qmod = BN_mod_word(q, (BN_ULONG)primes[i]);
+        if (pmod == (BN_ULONG)-1 || qmod == (BN_ULONG)-1)
+            goto err;
+        if (pmod == 0 || qmod == 0) {
+            if (!BN_add(p, p, padd))
+                goto err;
+            if (!BN_add(q, q, qadd))
+                goto err;
+            goto loop;
+        }
+    }
+    ret = 1;
+
+ err:
+    BN_CTX_end(ctx);
+    bn_check_top(p);
+    return (ret);
+}
diff --git a/crypto/bn/bn_prime.h b/crypto/bn/bn_prime.h
new file mode 100644
index 000000000..41440fa4e
--- /dev/null
+++ b/crypto/bn/bn_prime.h
@@ -0,0 +1,274 @@
+/*
+ * WARNING: do not edit!
+ * Generated by crypto/bn/bn_prime.pl
+ *
+ * Copyright 1998-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+typedef unsigned short prime_t;
+# define NUMPRIMES 2048
+
+static const prime_t primes[2048] = {
+
+       2,    3,    5,    7,   11,   13,   17,   19, 
+      23,   29,   31,   37,   41,   43,   47,   53, 
+      59,   61,   67,   71,   73,   79,   83,   89, 
+      97,  101,  103,  107,  109,  113,  127,  131, 
+     137,  139,  149,  151,  157,  163,  167,  173, 
+     179,  181,  191,  193,  197,  199,  211,  223, 
+     227,  229,  233,  239,  241,  251,  257,  263, 
+     269,  271,  277,  281,  283,  293,  307,  311, 
+     313,  317,  331,  337,  347,  349,  353,  359, 
+     367,  373,  379,  383,  389,  397,  401,  409, 
+     419,  421,  431,  433,  439,  443,  449,  457, 
+     461,  463,  467,  479,  487,  491,  499,  503, 
+     509,  521,  523,  541,  547,  557,  563,  569, 
+     571,  577,  587,  593,  599,  601,  607,  613, 
+     617,  619,  631,  641,  643,  647,  653,  659, 
+     661,  673,  677,  683,  691,  701,  709,  719, 
+     727,  733,  739,  743,  751,  757,  761,  769, 
+     773,  787,  797,  809,  811,  821,  823,  827, 
+     829,  839,  853,  857,  859,  863,  877,  881, 
+     883,  887,  907,  911,  919,  929,  937,  941, 
+     947,  953,  967,  971,  977,  983,  991,  997, 
+    1009, 1013, 1019, 1021, 1031, 1033, 1039, 1049, 
+    1051, 1061, 1063, 1069, 1087, 1091, 1093, 1097, 
+    1103, 1109, 1117, 1123, 1129, 1151, 1153, 1163, 
+    1171, 1181, 1187, 1193, 1201, 1213, 1217, 1223, 
+    1229, 1231, 1237, 1249, 1259, 1277, 1279, 1283, 
+    1289, 1291, 1297, 1301, 1303, 1307, 1319, 1321, 
+    1327, 1361, 1367, 1373, 1381, 1399, 1409, 1423, 
+    1427, 1429, 1433, 1439, 1447, 1451, 1453, 1459, 
+    1471, 1481, 1483, 1487, 1489, 1493, 1499, 1511, 
+    1523, 1531, 1543, 1549, 1553, 1559, 1567, 1571, 
+    1579, 1583, 1597, 1601, 1607, 1609, 1613, 1619, 
+    1621, 1627, 1637, 1657, 1663, 1667, 1669, 1693, 
+    1697, 1699, 1709, 1721, 1723, 1733, 1741, 1747, 
+    1753, 1759, 1777, 1783, 1787, 1789, 1801, 1811, 
+    1823, 1831, 1847, 1861, 1867, 1871, 1873, 1877, 
+    1879, 1889, 1901, 1907, 1913, 1931, 1933, 1949, 
+    1951, 1973, 1979, 1987, 1993, 1997, 1999, 2003, 
+    2011, 2017, 2027, 2029, 2039, 2053, 2063, 2069, 
+    2081, 2083, 2087, 2089, 2099, 2111, 2113, 2129, 
+    2131, 2137, 2141, 2143, 2153, 2161, 2179, 2203, 
+    2207, 2213, 2221, 2237, 2239, 2243, 2251, 2267, 
+    2269, 2273, 2281, 2287, 2293, 2297, 2309, 2311, 
+    2333, 2339, 2341, 2347, 2351, 2357, 2371, 2377, 
+    2381, 2383, 2389, 2393, 2399, 2411, 2417, 2423, 
+    2437, 2441, 2447, 2459, 2467, 2473, 2477, 2503, 
+    2521, 2531, 2539, 2543, 2549, 2551, 2557, 2579, 
+    2591, 2593, 2609, 2617, 2621, 2633, 2647, 2657, 
+    2659, 2663, 2671, 2677, 2683, 2687, 2689, 2693, 
+    2699, 2707, 2711, 2713, 2719, 2729, 2731, 2741, 
+    2749, 2753, 2767, 2777, 2789, 2791, 2797, 2801, 
+    2803, 2819, 2833, 2837, 2843, 2851, 2857, 2861, 
+    2879, 2887, 2897, 2903, 2909, 2917, 2927, 2939, 
+    2953, 2957, 2963, 2969, 2971, 2999, 3001, 3011, 
+    3019, 3023, 3037, 3041, 3049, 3061, 3067, 3079, 
+    3083, 3089, 3109, 3119, 3121, 3137, 3163, 3167, 
+    3169, 3181, 3187, 3191, 3203, 3209, 3217, 3221, 
+    3229, 3251, 3253, 3257, 3259, 3271, 3299, 3301, 
+    3307, 3313, 3319, 3323, 3329, 3331, 3343, 3347, 
+    3359, 3361, 3371, 3373, 3389, 3391, 3407, 3413, 
+    3433, 3449, 3457, 3461, 3463, 3467, 3469, 3491, 
+    3499, 3511, 3517, 3527, 3529, 3533, 3539, 3541, 
+    3547, 3557, 3559, 3571, 3581, 3583, 3593, 3607, 
+    3613, 3617, 3623, 3631, 3637, 3643, 3659, 3671, 
+    3673, 3677, 3691, 3697, 3701, 3709, 3719, 3727, 
+    3733, 3739, 3761, 3767, 3769, 3779, 3793, 3797, 
+    3803, 3821, 3823, 3833, 3847, 3851, 3853, 3863, 
+    3877, 3881, 3889, 3907, 3911, 3917, 3919, 3923, 
+    3929, 3931, 3943, 3947, 3967, 3989, 4001, 4003, 
+    4007, 4013, 4019, 4021, 4027, 4049, 4051, 4057, 
+    4073, 4079, 4091, 4093, 4099, 4111, 4127, 4129, 
+    4133, 4139, 4153, 4157, 4159, 4177, 4201, 4211, 
+    4217, 4219, 4229, 4231, 4241, 4243, 4253, 4259, 
+    4261, 4271, 4273, 4283, 4289, 4297, 4327, 4337, 
+    4339, 4349, 4357, 4363, 4373, 4391, 4397, 4409, 
+    4421, 4423, 4441, 4447, 4451, 4457, 4463, 4481, 
+    4483, 4493, 4507, 4513, 4517, 4519, 4523, 4547, 
+    4549, 4561, 4567, 4583, 4591, 4597, 4603, 4621, 
+    4637, 4639, 4643, 4649, 4651, 4657, 4663, 4673, 
+    4679, 4691, 4703, 4721, 4723, 4729, 4733, 4751, 
+    4759, 4783, 4787, 4789, 4793, 4799, 4801, 4813, 
+    4817, 4831, 4861, 4871, 4877, 4889, 4903, 4909, 
+    4919, 4931, 4933, 4937, 4943, 4951, 4957, 4967, 
+    4969, 4973, 4987, 4993, 4999, 5003, 5009, 5011, 
+    5021, 5023, 5039, 5051, 5059, 5077, 5081, 5087, 
+    5099, 5101, 5107, 5113, 5119, 5147, 5153, 5167, 
+    5171, 5179, 5189, 5197, 5209, 5227, 5231, 5233, 
+    5237, 5261, 5273, 5279, 5281, 5297, 5303, 5309, 
+    5323, 5333, 5347, 5351, 5381, 5387, 5393, 5399, 
+    5407, 5413, 5417, 5419, 5431, 5437, 5441, 5443, 
+    5449, 5471, 5477, 5479, 5483, 5501, 5503, 5507, 
+    5519, 5521, 5527, 5531, 5557, 5563, 5569, 5573, 
+    5581, 5591, 5623, 5639, 5641, 5647, 5651, 5653, 
+    5657, 5659, 5669, 5683, 5689, 5693, 5701, 5711, 
+    5717, 5737, 5741, 5743, 5749, 5779, 5783, 5791, 
+    5801, 5807, 5813, 5821, 5827, 5839, 5843, 5849, 
+    5851, 5857, 5861, 5867, 5869, 5879, 5881, 5897, 
+    5903, 5923, 5927, 5939, 5953, 5981, 5987, 6007, 
+    6011, 6029, 6037, 6043, 6047, 6053, 6067, 6073, 
+    6079, 6089, 6091, 6101, 6113, 6121, 6131, 6133, 
+    6143, 6151, 6163, 6173, 6197, 6199, 6203, 6211, 
+    6217, 6221, 6229, 6247, 6257, 6263, 6269, 6271, 
+    6277, 6287, 6299, 6301, 6311, 6317, 6323, 6329, 
+    6337, 6343, 6353, 6359, 6361, 6367, 6373, 6379, 
+    6389, 6397, 6421, 6427, 6449, 6451, 6469, 6473, 
+    6481, 6491, 6521, 6529, 6547, 6551, 6553, 6563, 
+    6569, 6571, 6577, 6581, 6599, 6607, 6619, 6637, 
+    6653, 6659, 6661, 6673, 6679, 6689, 6691, 6701, 
+    6703, 6709, 6719, 6733, 6737, 6761, 6763, 6779, 
+    6781, 6791, 6793, 6803, 6823, 6827, 6829, 6833, 
+    6841, 6857, 6863, 6869, 6871, 6883, 6899, 6907, 
+    6911, 6917, 6947, 6949, 6959, 6961, 6967, 6971, 
+    6977, 6983, 6991, 6997, 7001, 7013, 7019, 7027, 
+    7039, 7043, 7057, 7069, 7079, 7103, 7109, 7121, 
+    7127, 7129, 7151, 7159, 7177, 7187, 7193, 7207, 
+    7211, 7213, 7219, 7229, 7237, 7243, 7247, 7253, 
+    7283, 7297, 7307, 7309, 7321, 7331, 7333, 7349, 
+    7351, 7369, 7393, 7411, 7417, 7433, 7451, 7457, 
+    7459, 7477, 7481, 7487, 7489, 7499, 7507, 7517, 
+    7523, 7529, 7537, 7541, 7547, 7549, 7559, 7561, 
+    7573, 7577, 7583, 7589, 7591, 7603, 7607, 7621, 
+    7639, 7643, 7649, 7669, 7673, 7681, 7687, 7691, 
+    7699, 7703, 7717, 7723, 7727, 7741, 7753, 7757, 
+    7759, 7789, 7793, 7817, 7823, 7829, 7841, 7853, 
+    7867, 7873, 7877, 7879, 7883, 7901, 7907, 7919, 
+    7927, 7933, 7937, 7949, 7951, 7963, 7993, 8009, 
+    8011, 8017, 8039, 8053, 8059, 8069, 8081, 8087, 
+    8089, 8093, 8101, 8111, 8117, 8123, 8147, 8161, 
+    8167, 8171, 8179, 8191, 8209, 8219, 8221, 8231, 
+    8233, 8237, 8243, 8263, 8269, 8273, 8287, 8291, 
+    8293, 8297, 8311, 8317, 8329, 8353, 8363, 8369, 
+    8377, 8387, 8389, 8419, 8423, 8429, 8431, 8443, 
+    8447, 8461, 8467, 8501, 8513, 8521, 8527, 8537, 
+    8539, 8543, 8563, 8573, 8581, 8597, 8599, 8609, 
+    8623, 8627, 8629, 8641, 8647, 8663, 8669, 8677, 
+    8681, 8689, 8693, 8699, 8707, 8713, 8719, 8731, 
+    8737, 8741, 8747, 8753, 8761, 8779, 8783, 8803, 
+    8807, 8819, 8821, 8831, 8837, 8839, 8849, 8861, 
+    8863, 8867, 8887, 8893, 8923, 8929, 8933, 8941, 
+    8951, 8963, 8969, 8971, 8999, 9001, 9007, 9011, 
+    9013, 9029, 9041, 9043, 9049, 9059, 9067, 9091, 
+    9103, 9109, 9127, 9133, 9137, 9151, 9157, 9161, 
+    9173, 9181, 9187, 9199, 9203, 9209, 9221, 9227, 
+    9239, 9241, 9257, 9277, 9281, 9283, 9293, 9311, 
+    9319, 9323, 9337, 9341, 9343, 9349, 9371, 9377, 
+    9391, 9397, 9403, 9413, 9419, 9421, 9431, 9433, 
+    9437, 9439, 9461, 9463, 9467, 9473, 9479, 9491, 
+    9497, 9511, 9521, 9533, 9539, 9547, 9551, 9587, 
+    9601, 9613, 9619, 9623, 9629, 9631, 9643, 9649, 
+    9661, 9677, 9679, 9689, 9697, 9719, 9721, 9733, 
+    9739, 9743, 9749, 9767, 9769, 9781, 9787, 9791, 
+    9803, 9811, 9817, 9829, 9833, 9839, 9851, 9857, 
+    9859, 9871, 9883, 9887, 9901, 9907, 9923, 9929, 
+    9931, 9941, 9949, 9967, 9973, 10007, 10009, 10037, 
+    10039, 10061, 10067, 10069, 10079, 10091, 10093, 10099, 
+    10103, 10111, 10133, 10139, 10141, 10151, 10159, 10163, 
+    10169, 10177, 10181, 10193, 10211, 10223, 10243, 10247, 
+    10253, 10259, 10267, 10271, 10273, 10289, 10301, 10303, 
+    10313, 10321, 10331, 10333, 10337, 10343, 10357, 10369, 
+    10391, 10399, 10427, 10429, 10433, 10453, 10457, 10459, 
+    10463, 10477, 10487, 10499, 10501, 10513, 10529, 10531, 
+    10559, 10567, 10589, 10597, 10601, 10607, 10613, 10627, 
+    10631, 10639, 10651, 10657, 10663, 10667, 10687, 10691, 
+    10709, 10711, 10723, 10729, 10733, 10739, 10753, 10771, 
+    10781, 10789, 10799, 10831, 10837, 10847, 10853, 10859, 
+    10861, 10867, 10883, 10889, 10891, 10903, 10909, 10937, 
+    10939, 10949, 10957, 10973, 10979, 10987, 10993, 11003, 
+    11027, 11047, 11057, 11059, 11069, 11071, 11083, 11087, 
+    11093, 11113, 11117, 11119, 11131, 11149, 11159, 11161, 
+    11171, 11173, 11177, 11197, 11213, 11239, 11243, 11251, 
+    11257, 11261, 11273, 11279, 11287, 11299, 11311, 11317, 
+    11321, 11329, 11351, 11353, 11369, 11383, 11393, 11399, 
+    11411, 11423, 11437, 11443, 11447, 11467, 11471, 11483, 
+    11489, 11491, 11497, 11503, 11519, 11527, 11549, 11551, 
+    11579, 11587, 11593, 11597, 11617, 11621, 11633, 11657, 
+    11677, 11681, 11689, 11699, 11701, 11717, 11719, 11731, 
+    11743, 11777, 11779, 11783, 11789, 11801, 11807, 11813, 
+    11821, 11827, 11831, 11833, 11839, 11863, 11867, 11887, 
+    11897, 11903, 11909, 11923, 11927, 11933, 11939, 11941, 
+    11953, 11959, 11969, 11971, 11981, 11987, 12007, 12011, 
+    12037, 12041, 12043, 12049, 12071, 12073, 12097, 12101, 
+    12107, 12109, 12113, 12119, 12143, 12149, 12157, 12161, 
+    12163, 12197, 12203, 12211, 12227, 12239, 12241, 12251, 
+    12253, 12263, 12269, 12277, 12281, 12289, 12301, 12323, 
+    12329, 12343, 12347, 12373, 12377, 12379, 12391, 12401, 
+    12409, 12413, 12421, 12433, 12437, 12451, 12457, 12473, 
+    12479, 12487, 12491, 12497, 12503, 12511, 12517, 12527, 
+    12539, 12541, 12547, 12553, 12569, 12577, 12583, 12589, 
+    12601, 12611, 12613, 12619, 12637, 12641, 12647, 12653, 
+    12659, 12671, 12689, 12697, 12703, 12713, 12721, 12739, 
+    12743, 12757, 12763, 12781, 12791, 12799, 12809, 12821, 
+    12823, 12829, 12841, 12853, 12889, 12893, 12899, 12907, 
+    12911, 12917, 12919, 12923, 12941, 12953, 12959, 12967, 
+    12973, 12979, 12983, 13001, 13003, 13007, 13009, 13033, 
+    13037, 13043, 13049, 13063, 13093, 13099, 13103, 13109, 
+    13121, 13127, 13147, 13151, 13159, 13163, 13171, 13177, 
+    13183, 13187, 13217, 13219, 13229, 13241, 13249, 13259, 
+    13267, 13291, 13297, 13309, 13313, 13327, 13331, 13337, 
+    13339, 13367, 13381, 13397, 13399, 13411, 13417, 13421, 
+    13441, 13451, 13457, 13463, 13469, 13477, 13487, 13499, 
+    13513, 13523, 13537, 13553, 13567, 13577, 13591, 13597, 
+    13613, 13619, 13627, 13633, 13649, 13669, 13679, 13681, 
+    13687, 13691, 13693, 13697, 13709, 13711, 13721, 13723, 
+    13729, 13751, 13757, 13759, 13763, 13781, 13789, 13799, 
+    13807, 13829, 13831, 13841, 13859, 13873, 13877, 13879, 
+    13883, 13901, 13903, 13907, 13913, 13921, 13931, 13933, 
+    13963, 13967, 13997, 13999, 14009, 14011, 14029, 14033, 
+    14051, 14057, 14071, 14081, 14083, 14087, 14107, 14143, 
+    14149, 14153, 14159, 14173, 14177, 14197, 14207, 14221, 
+    14243, 14249, 14251, 14281, 14293, 14303, 14321, 14323, 
+    14327, 14341, 14347, 14369, 14387, 14389, 14401, 14407, 
+    14411, 14419, 14423, 14431, 14437, 14447, 14449, 14461, 
+    14479, 14489, 14503, 14519, 14533, 14537, 14543, 14549, 
+    14551, 14557, 14561, 14563, 14591, 14593, 14621, 14627, 
+    14629, 14633, 14639, 14653, 14657, 14669, 14683, 14699, 
+    14713, 14717, 14723, 14731, 14737, 14741, 14747, 14753, 
+    14759, 14767, 14771, 14779, 14783, 14797, 14813, 14821, 
+    14827, 14831, 14843, 14851, 14867, 14869, 14879, 14887, 
+    14891, 14897, 14923, 14929, 14939, 14947, 14951, 14957, 
+    14969, 14983, 15013, 15017, 15031, 15053, 15061, 15073, 
+    15077, 15083, 15091, 15101, 15107, 15121, 15131, 15137, 
+    15139, 15149, 15161, 15173, 15187, 15193, 15199, 15217, 
+    15227, 15233, 15241, 15259, 15263, 15269, 15271, 15277, 
+    15287, 15289, 15299, 15307, 15313, 15319, 15329, 15331, 
+    15349, 15359, 15361, 15373, 15377, 15383, 15391, 15401, 
+    15413, 15427, 15439, 15443, 15451, 15461, 15467, 15473, 
+    15493, 15497, 15511, 15527, 15541, 15551, 15559, 15569, 
+    15581, 15583, 15601, 15607, 15619, 15629, 15641, 15643, 
+    15647, 15649, 15661, 15667, 15671, 15679, 15683, 15727, 
+    15731, 15733, 15737, 15739, 15749, 15761, 15767, 15773, 
+    15787, 15791, 15797, 15803, 15809, 15817, 15823, 15859, 
+    15877, 15881, 15887, 15889, 15901, 15907, 15913, 15919, 
+    15923, 15937, 15959, 15971, 15973, 15991, 16001, 16007, 
+    16033, 16057, 16061, 16063, 16067, 16069, 16073, 16087, 
+    16091, 16097, 16103, 16111, 16127, 16139, 16141, 16183, 
+    16187, 16189, 16193, 16217, 16223, 16229, 16231, 16249, 
+    16253, 16267, 16273, 16301, 16319, 16333, 16339, 16349, 
+    16361, 16363, 16369, 16381, 16411, 16417, 16421, 16427, 
+    16433, 16447, 16451, 16453, 16477, 16481, 16487, 16493, 
+    16519, 16529, 16547, 16553, 16561, 16567, 16573, 16603, 
+    16607, 16619, 16631, 16633, 16649, 16651, 16657, 16661, 
+    16673, 16691, 16693, 16699, 16703, 16729, 16741, 16747, 
+    16759, 16763, 16787, 16811, 16823, 16829, 16831, 16843, 
+    16871, 16879, 16883, 16889, 16901, 16903, 16921, 16927, 
+    16931, 16937, 16943, 16963, 16979, 16981, 16987, 16993, 
+    17011, 17021, 17027, 17029, 17033, 17041, 17047, 17053, 
+    17077, 17093, 17099, 17107, 17117, 17123, 17137, 17159, 
+    17167, 17183, 17189, 17191, 17203, 17207, 17209, 17231, 
+    17239, 17257, 17291, 17293, 17299, 17317, 17321, 17327, 
+    17333, 17341, 17351, 17359, 17377, 17383, 17387, 17389, 
+    17393, 17401, 17417, 17419, 17431, 17443, 17449, 17467, 
+    17471, 17477, 17483, 17489, 17491, 17497, 17509, 17519, 
+    17539, 17551, 17569, 17573, 17579, 17581, 17597, 17599, 
+    17609, 17623, 17627, 17657, 17659, 17669, 17681, 17683, 
+    17707, 17713, 17729, 17737, 17747, 17749, 17761, 17783, 
+    17789, 17791, 17807, 17827, 17837, 17839, 17851, 17863, 
+};
diff --git a/crypto/bn/bn_prime.pl b/crypto/bn/bn_prime.pl
new file mode 100644
index 000000000..163d4a9d3
--- /dev/null
+++ b/crypto/bn/bn_prime.pl
@@ -0,0 +1,46 @@
+#! /usr/bin/env perl
+# Copyright 1998-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+print <<"EOF";
+/*
+ * WARNING: do not edit!
+ * Generated by crypto/bn/bn_prime.pl
+ *
+ * Copyright 1998-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+EOF
+
+
+my $num = shift || 2048;
+my @primes = ( 2 );
+my $p = 1;
+loop: while ($#primes < $num-1) {
+    $p += 2;
+    my $s = int(sqrt($p));
+
+    for (my $i = 0; defined($primes[$i]) && $primes[$i] <= $s; $i++) {
+        next loop if ($p % $primes[$i]) == 0;
+    }
+    push(@primes, $p);
+}
+
+print "typedef unsigned short prime_t;\n";
+printf "# define NUMPRIMES %d\n\n", $num;
+
+printf "static const prime_t primes[%d] = {\n", $num;
+for (my $i = 0; $i <= $#primes; $i++) {
+    printf "\n    " if ($i % 8) == 0;
+    printf "%4d, ", $primes[$i];
+}
+print "\n};\n";
diff --git a/crypto/bn/bn_print.c b/crypto/bn/bn_print.c
new file mode 100644
index 000000000..a16bde862
--- /dev/null
+++ b/crypto/bn/bn_print.c
@@ -0,0 +1,345 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include "bn_lcl.h"
+
+static const char Hex[] = "0123456789ABCDEF";
+
+/* Must 'OPENSSL_free' the returned data */
+char *BN_bn2hex(const BIGNUM *a)
+{
+    int i, j, v, z = 0;
+    char *buf;
+    char *p;
+
+    if (BN_is_zero(a))
+        return OPENSSL_strdup("0");
+    buf = OPENSSL_malloc(a->top * BN_BYTES * 2 + 2);
+    if (buf == NULL) {
+        BNerr(BN_F_BN_BN2HEX, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+    p = buf;
+    if (a->neg)
+        *(p++) = '-';
+    if (BN_is_zero(a))
+        *(p++) = '0';
+    for (i = a->top - 1; i >= 0; i--) {
+        for (j = BN_BITS2 - 8; j >= 0; j -= 8) {
+            /* strip leading zeros */
+            v = ((int)(a->d[i] >> (long)j)) & 0xff;
+            if (z || (v != 0)) {
+                *(p++) = Hex[v >> 4];
+                *(p++) = Hex[v & 0x0f];
+                z = 1;
+            }
+        }
+    }
+    *p = '\0';
+ err:
+    return (buf);
+}
+
+/* Must 'OPENSSL_free' the returned data */
+char *BN_bn2dec(const BIGNUM *a)
+{
+    int i = 0, num, ok = 0;
+    char *buf = NULL;
+    char *p;
+    BIGNUM *t = NULL;
+    BN_ULONG *bn_data = NULL, *lp;
+    int bn_data_num;
+
+    /*-
+     * get an upper bound for the length of the decimal integer
+     * num <= (BN_num_bits(a) + 1) * log(2)
+     *     <= 3 * BN_num_bits(a) * 0.101 + log(2) + 1     (rounding error)
+     *     <= 3 * BN_num_bits(a) / 10 + 3 * BN_num_bits / 1000 + 1 + 1
+     */
+    i = BN_num_bits(a) * 3;
+    num = (i / 10 + i / 1000 + 1) + 1;
+    bn_data_num = num / BN_DEC_NUM + 1;
+    bn_data = OPENSSL_malloc(bn_data_num * sizeof(BN_ULONG));
+    buf = OPENSSL_malloc(num + 3);
+    if ((buf == NULL) || (bn_data == NULL)) {
+        BNerr(BN_F_BN_BN2DEC, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+    if ((t = BN_dup(a)) == NULL)
+        goto err;
+
+#define BUF_REMAIN (num+3 - (size_t)(p - buf))
+    p = buf;
+    lp = bn_data;
+    if (BN_is_zero(t)) {
+        *(p++) = '0';
+        *(p++) = '\0';
+    } else {
+        if (BN_is_negative(t))
+            *p++ = '-';
+
+        while (!BN_is_zero(t)) {
+            if (lp - bn_data >= bn_data_num)
+                goto err;
+            *lp = BN_div_word(t, BN_DEC_CONV);
+            if (*lp == (BN_ULONG)-1)
+                goto err;
+            lp++;
+        }
+        lp--;
+        /*
+         * We now have a series of blocks, BN_DEC_NUM chars in length, where
+         * the last one needs truncation. The blocks need to be reversed in
+         * order.
+         */
+        BIO_snprintf(p, BUF_REMAIN, BN_DEC_FMT1, *lp);
+        while (*p)
+            p++;
+        while (lp != bn_data) {
+            lp--;
+            BIO_snprintf(p, BUF_REMAIN, BN_DEC_FMT2, *lp);
+            while (*p)
+                p++;
+        }
+    }
+    ok = 1;
+ err:
+    OPENSSL_free(bn_data);
+    BN_free(t);
+    if (ok)
+        return buf;
+    OPENSSL_free(buf);
+    return NULL;
+}
+
+int BN_hex2bn(BIGNUM **bn, const char *a)
+{
+    BIGNUM *ret = NULL;
+    BN_ULONG l = 0;
+    int neg = 0, h, m, i, j, k, c;
+    int num;
+
+    if ((a == NULL) || (*a == '\0'))
+        return (0);
+
+    if (*a == '-') {
+        neg = 1;
+        a++;
+    }
+
+    for (i = 0; i <= (INT_MAX/4) && isxdigit((unsigned char)a[i]); i++)
+        continue;
+
+    if (i == 0 || i > INT_MAX/4)
+        goto err;
+
+    num = i + neg;
+    if (bn == NULL)
+        return (num);
+
+    /* a is the start of the hex digits, and it is 'i' long */
+    if (*bn == NULL) {
+        if ((ret = BN_new()) == NULL)
+            return (0);
+    } else {
+        ret = *bn;
+        BN_zero(ret);
+    }
+
+    /* i is the number of hex digits */
+    if (bn_expand(ret, i * 4) == NULL)
+        goto err;
+
+    j = i;                      /* least significant 'hex' */
+    m = 0;
+    h = 0;
+    while (j > 0) {
+        m = ((BN_BYTES * 2) <= j) ? (BN_BYTES * 2) : j;
+        l = 0;
+        for (;;) {
+            c = a[j - m];
+            k = OPENSSL_hexchar2int(c);
+            if (k < 0)
+                k = 0;          /* paranoia */
+            l = (l << 4) | k;
+
+            if (--m <= 0) {
+                ret->d[h++] = l;
+                break;
+            }
+        }
+        j -= (BN_BYTES * 2);
+    }
+    ret->top = h;
+    bn_correct_top(ret);
+
+    *bn = ret;
+    bn_check_top(ret);
+    /* Don't set the negative flag if it's zero. */
+    if (ret->top != 0)
+        ret->neg = neg;
+    return (num);
+ err:
+    if (*bn == NULL)
+        BN_free(ret);
+    return (0);
+}
+
+int BN_dec2bn(BIGNUM **bn, const char *a)
+{
+    BIGNUM *ret = NULL;
+    BN_ULONG l = 0;
+    int neg = 0, i, j;
+    int num;
+
+    if ((a == NULL) || (*a == '\0'))
+        return (0);
+    if (*a == '-') {
+        neg = 1;
+        a++;
+    }
+
+    for (i = 0; i <= (INT_MAX/4) && isdigit((unsigned char)a[i]); i++)
+        continue;
+
+    if (i == 0 || i > INT_MAX/4)
+        goto err;
+
+    num = i + neg;
+    if (bn == NULL)
+        return (num);
+
+    /*
+     * a is the start of the digits, and it is 'i' long. We chop it into
+     * BN_DEC_NUM digits at a time
+     */
+    if (*bn == NULL) {
+        if ((ret = BN_new()) == NULL)
+            return (0);
+    } else {
+        ret = *bn;
+        BN_zero(ret);
+    }
+
+    /* i is the number of digits, a bit of an over expand */
+    if (bn_expand(ret, i * 4) == NULL)
+        goto err;
+
+    j = BN_DEC_NUM - (i % BN_DEC_NUM);
+    if (j == BN_DEC_NUM)
+        j = 0;
+    l = 0;
+    while (--i >= 0) {
+        l *= 10;
+        l += *a - '0';
+        a++;
+        if (++j == BN_DEC_NUM) {
+            if (!BN_mul_word(ret, BN_DEC_CONV)
+                || !BN_add_word(ret, l))
+                goto err;
+            l = 0;
+            j = 0;
+        }
+    }
+
+    bn_correct_top(ret);
+    *bn = ret;
+    bn_check_top(ret);
+    /* Don't set the negative flag if it's zero. */
+    if (ret->top != 0)
+        ret->neg = neg;
+    return (num);
+ err:
+    if (*bn == NULL)
+        BN_free(ret);
+    return (0);
+}
+
+int BN_asc2bn(BIGNUM **bn, const char *a)
+{
+    const char *p = a;
+
+    if (*p == '-')
+        p++;
+
+    if (p[0] == '0' && (p[1] == 'X' || p[1] == 'x')) {
+        if (!BN_hex2bn(bn, p + 2))
+            return 0;
+    } else {
+        if (!BN_dec2bn(bn, p))
+            return 0;
+    }
+    /* Don't set the negative flag if it's zero. */
+    if (*a == '-' && (*bn)->top != 0)
+        (*bn)->neg = 1;
+    return 1;
+}
+
+# ifndef OPENSSL_NO_STDIO
+int BN_print_fp(FILE *fp, const BIGNUM *a)
+{
+    BIO *b;
+    int ret;
+
+    if ((b = BIO_new(BIO_s_file())) == NULL)
+        return (0);
+    BIO_set_fp(b, fp, BIO_NOCLOSE);
+    ret = BN_print(b, a);
+    BIO_free(b);
+    return (ret);
+}
+# endif
+
+int BN_print(BIO *bp, const BIGNUM *a)
+{
+    int i, j, v, z = 0;
+    int ret = 0;
+
+    if ((a->neg) && (BIO_write(bp, "-", 1) != 1))
+        goto end;
+    if (BN_is_zero(a) && (BIO_write(bp, "0", 1) != 1))
+        goto end;
+    for (i = a->top - 1; i >= 0; i--) {
+        for (j = BN_BITS2 - 4; j >= 0; j -= 4) {
+            /* strip leading zeros */
+            v = ((int)(a->d[i] >> (long)j)) & 0x0f;
+            if (z || (v != 0)) {
+                if (BIO_write(bp, &(Hex[v]), 1) != 1)
+                    goto end;
+                z = 1;
+            }
+        }
+    }
+    ret = 1;
+ end:
+    return (ret);
+}
+
+char *BN_options(void)
+{
+    static int init = 0;
+    static char data[16];
+
+    if (!init) {
+        init++;
+#ifdef BN_LLONG
+        BIO_snprintf(data, sizeof data, "bn(%d,%d)",
+                     (int)sizeof(BN_ULLONG) * 8, (int)sizeof(BN_ULONG) * 8);
+#else
+        BIO_snprintf(data, sizeof data, "bn(%d,%d)",
+                     (int)sizeof(BN_ULONG) * 8, (int)sizeof(BN_ULONG) * 8);
+#endif
+    }
+    return (data);
+}
diff --git a/crypto/bn/bn_rand.c b/crypto/bn/bn_rand.c
new file mode 100644
index 000000000..6029a6189
--- /dev/null
+++ b/crypto/bn/bn_rand.c
@@ -0,0 +1,262 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include "internal/cryptlib.h"
+#include "bn_lcl.h"
+#include 
+#ifndef OPENSSL_NO_SHA
+# include 
+#endif
+
+static int bnrand(int pseudorand, BIGNUM *rnd, int bits, int top, int bottom)
+{
+    unsigned char *buf = NULL;
+    int ret = 0, bit, bytes, mask;
+    time_t tim;
+
+    if (bits == 0) {
+        if (top != BN_RAND_TOP_ANY || bottom != BN_RAND_BOTTOM_ANY)
+            goto toosmall;
+        BN_zero(rnd);
+        return 1;
+    }
+    if (bits < 0 || (bits == 1 && top > 0))
+        goto toosmall;
+
+    bytes = (bits + 7) / 8;
+    bit = (bits - 1) % 8;
+    mask = 0xff << (bit + 1);
+
+    buf = OPENSSL_malloc(bytes);
+    if (buf == NULL) {
+        BNerr(BN_F_BNRAND, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+
+    /* make a random number and set the top and bottom bits */
+    time(&tim);
+    RAND_add(&tim, sizeof(tim), 0.0);
+
+    if (RAND_bytes(buf, bytes) <= 0)
+        goto err;
+
+    if (pseudorand == 2) {
+        /*
+         * generate patterns that are more likely to trigger BN library bugs
+         */
+        int i;
+        unsigned char c;
+
+        for (i = 0; i < bytes; i++) {
+            if (RAND_bytes(&c, 1) <= 0)
+                goto err;
+            if (c >= 128 && i > 0)
+                buf[i] = buf[i - 1];
+            else if (c < 42)
+                buf[i] = 0;
+            else if (c < 84)
+                buf[i] = 255;
+        }
+    }
+
+    if (top >= 0) {
+        if (top) {
+            if (bit == 0) {
+                buf[0] = 1;
+                buf[1] |= 0x80;
+            } else {
+                buf[0] |= (3 << (bit - 1));
+            }
+        } else {
+            buf[0] |= (1 << bit);
+        }
+    }
+    buf[0] &= ~mask;
+    if (bottom)                 /* set bottom bit if requested */
+        buf[bytes - 1] |= 1;
+    if (!BN_bin2bn(buf, bytes, rnd))
+        goto err;
+    ret = 1;
+ err:
+    OPENSSL_clear_free(buf, bytes);
+    bn_check_top(rnd);
+    return (ret);
+
+toosmall:
+    BNerr(BN_F_BNRAND, BN_R_BITS_TOO_SMALL);
+    return 0;
+}
+
+int BN_rand(BIGNUM *rnd, int bits, int top, int bottom)
+{
+    return bnrand(0, rnd, bits, top, bottom);
+}
+
+int BN_pseudo_rand(BIGNUM *rnd, int bits, int top, int bottom)
+{
+    return bnrand(1, rnd, bits, top, bottom);
+}
+
+int BN_bntest_rand(BIGNUM *rnd, int bits, int top, int bottom)
+{
+    return bnrand(2, rnd, bits, top, bottom);
+}
+
+/* random number r:  0 <= r < range */
+static int bn_rand_range(int pseudo, BIGNUM *r, const BIGNUM *range)
+{
+    int (*bn_rand) (BIGNUM *, int, int, int) =
+        pseudo ? BN_pseudo_rand : BN_rand;
+    int n;
+    int count = 100;
+
+    if (range->neg || BN_is_zero(range)) {
+        BNerr(BN_F_BN_RAND_RANGE, BN_R_INVALID_RANGE);
+        return 0;
+    }
+
+    n = BN_num_bits(range);     /* n > 0 */
+
+    /* BN_is_bit_set(range, n - 1) always holds */
+
+    if (n == 1)
+        BN_zero(r);
+    else if (!BN_is_bit_set(range, n - 2) && !BN_is_bit_set(range, n - 3)) {
+        /*
+         * range = 100..._2, so 3*range (= 11..._2) is exactly one bit longer
+         * than range
+         */
+        do {
+            if (!bn_rand(r, n + 1, BN_RAND_TOP_ANY, BN_RAND_BOTTOM_ANY))
+                return 0;
+            /*
+             * If r < 3*range, use r := r MOD range (which is either r, r -
+             * range, or r - 2*range). Otherwise, iterate once more. Since
+             * 3*range = 11..._2, each iteration succeeds with probability >=
+             * .75.
+             */
+            if (BN_cmp(r, range) >= 0) {
+                if (!BN_sub(r, r, range))
+                    return 0;
+                if (BN_cmp(r, range) >= 0)
+                    if (!BN_sub(r, r, range))
+                        return 0;
+            }
+
+            if (!--count) {
+                BNerr(BN_F_BN_RAND_RANGE, BN_R_TOO_MANY_ITERATIONS);
+                return 0;
+            }
+
+        }
+        while (BN_cmp(r, range) >= 0);
+    } else {
+        do {
+            /* range = 11..._2  or  range = 101..._2 */
+            if (!bn_rand(r, n, BN_RAND_TOP_ANY, BN_RAND_BOTTOM_ANY))
+                return 0;
+
+            if (!--count) {
+                BNerr(BN_F_BN_RAND_RANGE, BN_R_TOO_MANY_ITERATIONS);
+                return 0;
+            }
+        }
+        while (BN_cmp(r, range) >= 0);
+    }
+
+    bn_check_top(r);
+    return 1;
+}
+
+int BN_rand_range(BIGNUM *r, const BIGNUM *range)
+{
+    return bn_rand_range(0, r, range);
+}
+
+int BN_pseudo_rand_range(BIGNUM *r, const BIGNUM *range)
+{
+    return bn_rand_range(1, r, range);
+}
+
+/*
+ * BN_generate_dsa_nonce generates a random number 0 <= out < range. Unlike
+ * BN_rand_range, it also includes the contents of |priv| and |message| in
+ * the generation so that an RNG failure isn't fatal as long as |priv|
+ * remains secret. This is intended for use in DSA and ECDSA where an RNG
+ * weakness leads directly to private key exposure unless this function is
+ * used.
+ */
+int BN_generate_dsa_nonce(BIGNUM *out, const BIGNUM *range,
+                          const BIGNUM *priv, const unsigned char *message,
+                          size_t message_len, BN_CTX *ctx)
+{
+    int ret = 0;
+#ifndef OPENSSL_NO_DSA
+    SHA512_CTX sha;
+    /*
+     * We use 512 bits of random data per iteration to ensure that we have at
+     * least |range| bits of randomness.
+     */
+    unsigned char random_bytes[64];
+    unsigned char digest[SHA512_DIGEST_LENGTH];
+    unsigned done, todo;
+    /* We generate |range|+8 bytes of random output. */
+    const unsigned num_k_bytes = BN_num_bytes(range) + 8;
+    unsigned char private_bytes[96];
+    unsigned char *k_bytes;
+
+    k_bytes = OPENSSL_malloc(num_k_bytes);
+    if (k_bytes == NULL)
+        goto err;
+
+    /* We copy |priv| into a local buffer to avoid exposing its length. */
+    todo = sizeof(priv->d[0]) * priv->top;
+    if (todo > sizeof(private_bytes)) {
+        /*
+         * No reasonable DSA or ECDSA key should have a private key this
+         * large and we don't handle this case in order to avoid leaking the
+         * length of the private key.
+         */
+        BNerr(BN_F_BN_GENERATE_DSA_NONCE, BN_R_PRIVATE_KEY_TOO_LARGE);
+        goto err;
+    }
+    memcpy(private_bytes, priv->d, todo);
+    memset(private_bytes + todo, 0, sizeof(private_bytes) - todo);
+
+    for (done = 0; done < num_k_bytes;) {
+        if (RAND_bytes(random_bytes, sizeof(random_bytes)) != 1)
+            goto err;
+        SHA512_Init(&sha);
+        SHA512_Update(&sha, &done, sizeof(done));
+        SHA512_Update(&sha, private_bytes, sizeof(private_bytes));
+        SHA512_Update(&sha, message, message_len);
+        SHA512_Update(&sha, random_bytes, sizeof(random_bytes));
+        SHA512_Final(digest, &sha);
+
+        todo = num_k_bytes - done;
+        if (todo > SHA512_DIGEST_LENGTH)
+            todo = SHA512_DIGEST_LENGTH;
+        memcpy(k_bytes + done, digest, todo);
+        done += todo;
+    }
+
+    if (!BN_bin2bn(k_bytes, num_k_bytes, out))
+        goto err;
+    if (BN_mod(out, out, range, ctx) != 1)
+        goto err;
+    ret = 1;
+
+ err:
+    OPENSSL_free(k_bytes);
+    OPENSSL_cleanse(private_bytes, sizeof(private_bytes));
+#endif
+    return ret;
+}
diff --git a/crypto/bn/bn_recp.c b/crypto/bn/bn_recp.c
new file mode 100644
index 000000000..20585b9d4
--- /dev/null
+++ b/crypto/bn/bn_recp.c
@@ -0,0 +1,199 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "internal/cryptlib.h"
+#include "bn_lcl.h"
+
+void BN_RECP_CTX_init(BN_RECP_CTX *recp)
+{
+    memset(recp, 0, sizeof(*recp));
+    bn_init(&(recp->N));
+    bn_init(&(recp->Nr));
+}
+
+BN_RECP_CTX *BN_RECP_CTX_new(void)
+{
+    BN_RECP_CTX *ret;
+
+    if ((ret = OPENSSL_zalloc(sizeof(*ret))) == NULL)
+        return (NULL);
+
+    bn_init(&(ret->N));
+    bn_init(&(ret->Nr));
+    ret->flags = BN_FLG_MALLOCED;
+    return (ret);
+}
+
+void BN_RECP_CTX_free(BN_RECP_CTX *recp)
+{
+    if (recp == NULL)
+        return;
+
+    BN_free(&(recp->N));
+    BN_free(&(recp->Nr));
+    if (recp->flags & BN_FLG_MALLOCED)
+        OPENSSL_free(recp);
+}
+
+int BN_RECP_CTX_set(BN_RECP_CTX *recp, const BIGNUM *d, BN_CTX *ctx)
+{
+    if (!BN_copy(&(recp->N), d))
+        return 0;
+    BN_zero(&(recp->Nr));
+    recp->num_bits = BN_num_bits(d);
+    recp->shift = 0;
+    return (1);
+}
+
+int BN_mod_mul_reciprocal(BIGNUM *r, const BIGNUM *x, const BIGNUM *y,
+                          BN_RECP_CTX *recp, BN_CTX *ctx)
+{
+    int ret = 0;
+    BIGNUM *a;
+    const BIGNUM *ca;
+
+    BN_CTX_start(ctx);
+    if ((a = BN_CTX_get(ctx)) == NULL)
+        goto err;
+    if (y != NULL) {
+        if (x == y) {
+            if (!BN_sqr(a, x, ctx))
+                goto err;
+        } else {
+            if (!BN_mul(a, x, y, ctx))
+                goto err;
+        }
+        ca = a;
+    } else
+        ca = x;                 /* Just do the mod */
+
+    ret = BN_div_recp(NULL, r, ca, recp, ctx);
+ err:
+    BN_CTX_end(ctx);
+    bn_check_top(r);
+    return (ret);
+}
+
+int BN_div_recp(BIGNUM *dv, BIGNUM *rem, const BIGNUM *m,
+                BN_RECP_CTX *recp, BN_CTX *ctx)
+{
+    int i, j, ret = 0;
+    BIGNUM *a, *b, *d, *r;
+
+    BN_CTX_start(ctx);
+    a = BN_CTX_get(ctx);
+    b = BN_CTX_get(ctx);
+    if (dv != NULL)
+        d = dv;
+    else
+        d = BN_CTX_get(ctx);
+    if (rem != NULL)
+        r = rem;
+    else
+        r = BN_CTX_get(ctx);
+    if (a == NULL || b == NULL || d == NULL || r == NULL)
+        goto err;
+
+    if (BN_ucmp(m, &(recp->N)) < 0) {
+        BN_zero(d);
+        if (!BN_copy(r, m)) {
+            BN_CTX_end(ctx);
+            return 0;
+        }
+        BN_CTX_end(ctx);
+        return (1);
+    }
+
+    /*
+     * We want the remainder Given input of ABCDEF / ab we need multiply
+     * ABCDEF by 3 digests of the reciprocal of ab
+     */
+
+    /* i := max(BN_num_bits(m), 2*BN_num_bits(N)) */
+    i = BN_num_bits(m);
+    j = recp->num_bits << 1;
+    if (j > i)
+        i = j;
+
+    /* Nr := round(2^i / N) */
+    if (i != recp->shift)
+        recp->shift = BN_reciprocal(&(recp->Nr), &(recp->N), i, ctx);
+    /* BN_reciprocal could have returned -1 for an error */
+    if (recp->shift == -1)
+        goto err;
+
+    /*-
+     * d := |round(round(m / 2^BN_num_bits(N)) * recp->Nr / 2^(i - BN_num_bits(N)))|
+     *    = |round(round(m / 2^BN_num_bits(N)) * round(2^i / N) / 2^(i - BN_num_bits(N)))|
+     *   <= |(m / 2^BN_num_bits(N)) * (2^i / N) * (2^BN_num_bits(N) / 2^i)|
+     *    = |m/N|
+     */
+    if (!BN_rshift(a, m, recp->num_bits))
+        goto err;
+    if (!BN_mul(b, a, &(recp->Nr), ctx))
+        goto err;
+    if (!BN_rshift(d, b, i - recp->num_bits))
+        goto err;
+    d->neg = 0;
+
+    if (!BN_mul(b, &(recp->N), d, ctx))
+        goto err;
+    if (!BN_usub(r, m, b))
+        goto err;
+    r->neg = 0;
+
+    j = 0;
+    while (BN_ucmp(r, &(recp->N)) >= 0) {
+        if (j++ > 2) {
+            BNerr(BN_F_BN_DIV_RECP, BN_R_BAD_RECIPROCAL);
+            goto err;
+        }
+        if (!BN_usub(r, r, &(recp->N)))
+            goto err;
+        if (!BN_add_word(d, 1))
+            goto err;
+    }
+
+    r->neg = BN_is_zero(r) ? 0 : m->neg;
+    d->neg = m->neg ^ recp->N.neg;
+    ret = 1;
+ err:
+    BN_CTX_end(ctx);
+    bn_check_top(dv);
+    bn_check_top(rem);
+    return (ret);
+}
+
+/*
+ * len is the expected size of the result We actually calculate with an extra
+ * word of precision, so we can do faster division if the remainder is not
+ * required.
+ */
+/* r := 2^len / m */
+int BN_reciprocal(BIGNUM *r, const BIGNUM *m, int len, BN_CTX *ctx)
+{
+    int ret = -1;
+    BIGNUM *t;
+
+    BN_CTX_start(ctx);
+    if ((t = BN_CTX_get(ctx)) == NULL)
+        goto err;
+
+    if (!BN_set_bit(t, len))
+        goto err;
+
+    if (!BN_div(r, NULL, t, m, ctx))
+        goto err;
+
+    ret = len;
+ err:
+    bn_check_top(r);
+    BN_CTX_end(ctx);
+    return (ret);
+}
diff --git a/crypto/bn/bn_shift.c b/crypto/bn/bn_shift.c
new file mode 100644
index 000000000..9907b82fa
--- /dev/null
+++ b/crypto/bn/bn_shift.c
@@ -0,0 +1,171 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "internal/cryptlib.h"
+#include "bn_lcl.h"
+
+int BN_lshift1(BIGNUM *r, const BIGNUM *a)
+{
+    register BN_ULONG *ap, *rp, t, c;
+    int i;
+
+    bn_check_top(r);
+    bn_check_top(a);
+
+    if (r != a) {
+        r->neg = a->neg;
+        if (bn_wexpand(r, a->top + 1) == NULL)
+            return (0);
+        r->top = a->top;
+    } else {
+        if (bn_wexpand(r, a->top + 1) == NULL)
+            return (0);
+    }
+    ap = a->d;
+    rp = r->d;
+    c = 0;
+    for (i = 0; i < a->top; i++) {
+        t = *(ap++);
+        *(rp++) = ((t << 1) | c) & BN_MASK2;
+        c = (t & BN_TBIT) ? 1 : 0;
+    }
+    if (c) {
+        *rp = 1;
+        r->top++;
+    }
+    bn_check_top(r);
+    return (1);
+}
+
+int BN_rshift1(BIGNUM *r, const BIGNUM *a)
+{
+    BN_ULONG *ap, *rp, t, c;
+    int i, j;
+
+    bn_check_top(r);
+    bn_check_top(a);
+
+    if (BN_is_zero(a)) {
+        BN_zero(r);
+        return (1);
+    }
+    i = a->top;
+    ap = a->d;
+    j = i - (ap[i - 1] == 1);
+    if (a != r) {
+        if (bn_wexpand(r, j) == NULL)
+            return (0);
+        r->neg = a->neg;
+    }
+    rp = r->d;
+    t = ap[--i];
+    c = (t & 1) ? BN_TBIT : 0;
+    if (t >>= 1)
+        rp[i] = t;
+    while (i > 0) {
+        t = ap[--i];
+        rp[i] = ((t >> 1) & BN_MASK2) | c;
+        c = (t & 1) ? BN_TBIT : 0;
+    }
+    r->top = j;
+    bn_check_top(r);
+    return (1);
+}
+
+int BN_lshift(BIGNUM *r, const BIGNUM *a, int n)
+{
+    int i, nw, lb, rb;
+    BN_ULONG *t, *f;
+    BN_ULONG l;
+
+    bn_check_top(r);
+    bn_check_top(a);
+
+    if (n < 0) {
+        BNerr(BN_F_BN_LSHIFT, BN_R_INVALID_SHIFT);
+        return 0;
+    }
+
+    r->neg = a->neg;
+    nw = n / BN_BITS2;
+    if (bn_wexpand(r, a->top + nw + 1) == NULL)
+        return (0);
+    lb = n % BN_BITS2;
+    rb = BN_BITS2 - lb;
+    f = a->d;
+    t = r->d;
+    t[a->top + nw] = 0;
+    if (lb == 0)
+        for (i = a->top - 1; i >= 0; i--)
+            t[nw + i] = f[i];
+    else
+        for (i = a->top - 1; i >= 0; i--) {
+            l = f[i];
+            t[nw + i + 1] |= (l >> rb) & BN_MASK2;
+            t[nw + i] = (l << lb) & BN_MASK2;
+        }
+    memset(t, 0, sizeof(*t) * nw);
+    r->top = a->top + nw + 1;
+    bn_correct_top(r);
+    bn_check_top(r);
+    return (1);
+}
+
+int BN_rshift(BIGNUM *r, const BIGNUM *a, int n)
+{
+    int i, j, nw, lb, rb;
+    BN_ULONG *t, *f;
+    BN_ULONG l, tmp;
+
+    bn_check_top(r);
+    bn_check_top(a);
+
+    if (n < 0) {
+        BNerr(BN_F_BN_RSHIFT, BN_R_INVALID_SHIFT);
+        return 0;
+    }
+
+    nw = n / BN_BITS2;
+    rb = n % BN_BITS2;
+    lb = BN_BITS2 - rb;
+    if (nw >= a->top || a->top == 0) {
+        BN_zero(r);
+        return (1);
+    }
+    i = (BN_num_bits(a) - n + (BN_BITS2 - 1)) / BN_BITS2;
+    if (r != a) {
+        r->neg = a->neg;
+        if (bn_wexpand(r, i) == NULL)
+            return (0);
+    } else {
+        if (n == 0)
+            return 1;           /* or the copying loop will go berserk */
+    }
+
+    f = &(a->d[nw]);
+    t = r->d;
+    j = a->top - nw;
+    r->top = i;
+
+    if (rb == 0) {
+        for (i = j; i != 0; i--)
+            *(t++) = *(f++);
+    } else {
+        l = *(f++);
+        for (i = j - 1; i != 0; i--) {
+            tmp = (l >> rb) & BN_MASK2;
+            l = *(f++);
+            *(t++) = (tmp | (l << lb)) & BN_MASK2;
+        }
+        if ((l = (l >> rb) & BN_MASK2))
+            *(t) = l;
+    }
+    bn_check_top(r);
+    return (1);
+}
diff --git a/crypto/bn/bn_sm2p256.c b/crypto/bn/bn_sm2p256.c
new file mode 100644
index 000000000..8f0f9ccf0
--- /dev/null
+++ b/crypto/bn/bn_sm2p256.c
@@ -0,0 +1,485 @@
+/* ====================================================================
+ * Copyright (c) 2014 - 2018 The GmSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the GmSSL Project.
+ *    (http://gmssl.org/)"
+ *
+ * 4. The name "GmSSL Project" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    guanzhi1980@gmail.com.
+ *
+ * 5. Products derived from this software may not be called "GmSSL"
+ *    nor may "GmSSL" appear in their names without prior written
+ *    permission of the GmSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the GmSSL Project
+ *    (http://gmssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE GmSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE GmSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+
+/*
+ * Copyright 2002-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "bn_lcl.h"
+#include "internal/cryptlib.h"
+
+#define BN_SM2_256_TOP (256+BN_BITS2-1)/BN_BITS2
+
+/* pre-computed tables are "carry-less" values of modulus*(i+1) */
+#if BN_BITS2 == 64
+
+/* p, 2p, 3p, 4p, 5p in little-endian */
+static const BN_ULONG _sm2_p_256[][BN_SM2_256_TOP] = {
+    {0xffffffffffffffffull, 0xffffffff00000000ull,
+     0xffffffffffffffffull, 0xfffffffeffffffffull},
+    {0xfffffffffffffffeull, 0xfffffffe00000001ull,
+     0xffffffffffffffffull, 0xfffffffdffffffffull},
+    {0xfffffffffffffffdull, 0xfffffffd00000002ull,
+     0xffffffffffffffffull, 0xfffffffcffffffffull},
+    {0xfffffffffffffffcull, 0xfffffffc00000003ull,
+     0xffffffffffffffffull, 0xfffffffbffffffffull},
+    {0xfffffffffffffffbull, 0xfffffffb00000004ull,
+     0xffffffffffffffffull, 0xfffffffaffffffffull},
+    {0xfffffffffffffffaull, 0xfffffffa00000005ull,
+     0xffffffffffffffffull, 0xfffffff9ffffffffull},
+    {0xfffffffffffffff9ull, 0xfffffff900000006ull,
+     0xffffffffffffffffull, 0xfffffff8ffffffffull},
+    {0xfffffffffffffff8ull, 0xfffffff800000007ull,
+     0xffffffffffffffffull, 0xfffffff7ffffffffull},
+    {0xfffffffffffffff7ull, 0xfffffff700000008ull,
+     0xffffffffffffffffull, 0xfffffff6ffffffffull},
+    {0xfffffffffffffff6ull, 0xfffffff600000009ull,
+     0xffffffffffffffffull, 0xfffffff5ffffffffull},
+    {0xfffffffffffffff5ull, 0xfffffff50000000aull,
+     0xffffffffffffffffull, 0xfffffff4ffffffffull},
+    {0xfffffffffffffff4ull, 0xfffffff40000000bull,
+     0xffffffffffffffffull, 0xfffffff3ffffffffull},
+    {0xfffffffffffffff3ull, 0xfffffff30000000cull,
+     0xffffffffffffffffull, 0xfffffff2ffffffffull}
+};
+
+/* p^2 in little-endian */
+static const BN_ULONG _sm2_p_256_sqr[] = {
+    0x0000000000000001ull, 0x00000001fffffffeull,
+    0xfffffffe00000001ull, 0x0000000200000000ull,
+    0xfffffffdfffffffeull, 0xfffffffe00000003ull,
+    0xffffffffffffffffull, 0xfffffffe00000000ull,
+};
+
+#elif BN_BITS2 == 32
+
+static const BN_ULONG _sm2_p_256[][BN_SM2_256_TOP] = {
+    {0xffffffff, 0xffffffff, 0x0, 0xffffffff,
+     0xffffffff, 0xffffffff, 0xffffffff, 0xfffffffe},
+    {0xfffffffe, 0xffffffff, 0x1, 0xfffffffe,
+     0xffffffff, 0xffffffff, 0xffffffff, 0xfffffffd},
+    {0xfffffffd, 0xffffffff, 0x2, 0xfffffffd,
+     0xffffffff, 0xffffffff, 0xffffffff, 0xfffffffc},
+    {0xfffffffc, 0xffffffff, 0x3, 0xfffffffc,
+     0xffffffff, 0xffffffff, 0xffffffff, 0xfffffffb},
+    {0xfffffffb, 0xffffffff, 0x4, 0xfffffffb,
+     0xffffffff, 0xffffffff, 0xffffffff, 0xfffffffa},
+    {0xfffffffa, 0xffffffff, 0x5, 0xfffffffa,
+     0xffffffff, 0xffffffff, 0xffffffff, 0xfffffff9},
+    {0xfffffff9, 0xffffffff, 0x6, 0xfffffff9,
+     0xffffffff, 0xffffffff, 0xffffffff, 0xfffffff8},
+    {0xfffffff8, 0xffffffff, 0x7, 0xfffffff8,
+     0xffffffff, 0xffffffff, 0xffffffff, 0xfffffff7},
+    {0xfffffff7, 0xffffffff, 0x8, 0xfffffff7,
+     0xffffffff, 0xffffffff, 0xffffffff, 0xfffffff6},
+    {0xfffffff6, 0xffffffff, 0x9, 0xfffffff6,
+     0xffffffff, 0xffffffff, 0xffffffff, 0xfffffff5},
+    {0xfffffff5, 0xffffffff, 0xa, 0xfffffff5,
+     0xffffffff, 0xffffffff, 0xffffffff, 0xfffffff4},
+    {0xfffffff4, 0xffffffff, 0xb, 0xfffffff4,
+     0xffffffff, 0xffffffff, 0xffffffff, 0xfffffff3},
+    {0xfffffff3, 0xffffffff, 0xc, 0xfffffff3,
+     0xffffffff, 0xffffffff, 0xffffffff, 0xfffffff2},
+};
+
+static const BN_ULONG _sm2_p_256_sqr[] = {
+    0x00000001, 0x00000000, 0xfffffffe, 0x00000001,
+    0x00000001, 0xfffffffe, 0x00000000, 0x00000002,
+    0xfffffffe, 0xfffffffd, 0x00000003, 0xfffffffe,
+    0xffffffff, 0xffffffff, 0x00000000, 0xfffffffe
+};
+
+#else
+# error "unsupported BN_BITS2"
+#endif
+
+static const BIGNUM _bignum_sm2_p_256 = {
+    (BN_ULONG *)_sm2_p_256[0],
+    BN_SM2_256_TOP,
+    BN_SM2_256_TOP,
+    0,
+    BN_FLG_STATIC_DATA
+};
+
+const BIGNUM *BN_get0_sm2_prime_256(void)
+{
+    return &_bignum_sm2_p_256;
+}
+
+static void sm2_cp_bn_0(BN_ULONG *dst, const BN_ULONG *src, int top, int max)
+{
+    int i;
+
+#ifdef BN_DEBUG
+    OPENSSL_assert(top <= max);
+#endif
+    for (i = 0; i < top; i++)
+        dst[i] = src[i];
+    for (; i < max; i++)
+        dst[i] = 0;
+}
+
+static void sm2_cp_bn(BN_ULONG *dst, const BN_ULONG *src, int top)
+{
+    int i;
+
+    for (i = 0; i < top; i++)
+        dst[i] = src[i];
+}
+
+#if BN_BITS2 == 64
+# define bn_cp_64(to, n, from, m)        (to)[n] = (m>=0)?((from)[m]):0;
+# define bn_64_set_0(to, n)              (to)[n] = (BN_ULONG)0;
+/*
+ * two following macros are implemented under assumption that they
+ * are called in a sequence with *ascending* n, i.e. as they are...
+ */
+# define bn_cp_32_naked(to, n, from, m)  (((n)&1)?(to[(n)/2]|=((m)&1)?(from[(m)/2]&BN_MASK2h):(from[(m)/2]<<32))\
+                                                :(to[(n)/2] =((m)&1)?(from[(m)/2]>>32):(from[(m)/2]&BN_MASK2l)))
+# define bn_32_set_0(to, n)              (((n)&1)?(to[(n)/2]&=BN_MASK2l):(to[(n)/2]=0));
+# define bn_cp_32(to,n,from,m)           ((m)>=0)?bn_cp_32_naked(to,n,from,m):bn_32_set_0(to,n)
+# if defined(L_ENDIAN)
+#  if defined(__arch64__)
+#   define NIST_INT64 long
+#  else
+#   define NIST_INT64 long long
+#  endif
+# endif
+#else
+# define bn_cp_64(to, n, from, m) \
+        { \
+        bn_cp_32(to, (n)*2, from, (m)*2); \
+        bn_cp_32(to, (n)*2+1, from, (m)*2+1); \
+        }
+# define bn_64_set_0(to, n) \
+        { \
+        bn_32_set_0(to, (n)*2); \
+        bn_32_set_0(to, (n)*2+1); \
+        }
+# define bn_cp_32(to, n, from, m)        (to)[n] = (m>=0)?((from)[m]):0;
+# define bn_32_set_0(to, n)              (to)[n] = (BN_ULONG)0;
+# if defined(_WIN32) && !defined(__GNUC__)
+#  define NIST_INT64 __int64
+# elif defined(BN_LLONG)
+#  define NIST_INT64 long long
+# endif
+#endif                          /* BN_BITS2 != 64 */
+
+
+typedef BN_ULONG (*bn_addsub_f) (BN_ULONG *, const BN_ULONG *,
+                                 const BN_ULONG *, int);
+
+#define sm2_set_256(to, from, a1, a2, a3, a4, a5, a6, a7, a8) \
+        { \
+        bn_cp_32(to, 0, from, (a8) - 8) \
+        bn_cp_32(to, 1, from, (a7) - 8) \
+        bn_cp_32(to, 2, from, (a6) - 8) \
+        bn_cp_32(to, 3, from, (a5) - 8) \
+        bn_cp_32(to, 4, from, (a4) - 8) \
+        bn_cp_32(to, 5, from, (a3) - 8) \
+        bn_cp_32(to, 6, from, (a2) - 8) \
+        bn_cp_32(to, 7, from, (a1) - 8) \
+        }
+
+int BN_sm2_mod_256(BIGNUM *r, const BIGNUM *a, const BIGNUM *field,
+                   BN_CTX *ctx)
+{
+    int i, top = a->top;
+    int carry = 0;
+    register BN_ULONG *a_d = a->d, *r_d;
+    union {
+        BN_ULONG bn[BN_SM2_256_TOP];
+        unsigned int ui[BN_SM2_256_TOP * sizeof(BN_ULONG) /
+                        sizeof(unsigned int)];
+    } buf;
+    BN_ULONG c_d[BN_SM2_256_TOP], *res;
+    PTR_SIZE_INT mask;
+    union {
+        bn_addsub_f f;
+        PTR_SIZE_INT p;
+    } u;
+    static const BIGNUM _bignum_sm2_p_256_sqr = {
+        (BN_ULONG *)_sm2_p_256_sqr,
+        OSSL_NELEM(_sm2_p_256_sqr),
+        OSSL_NELEM(_sm2_p_256_sqr),
+        0, BN_FLG_STATIC_DATA
+    };
+
+    field = &_bignum_sm2_p_256; /* just to make sure */
+
+    if (BN_is_negative(a) || BN_ucmp(a, &_bignum_sm2_p_256_sqr) >= 0)
+        return BN_nnmod(r, a, field, ctx);
+
+    i = BN_ucmp(field, a);
+    if (i == 0) {
+        BN_zero(r);
+        return 1;
+    } else if (i > 0)
+        return (r == a) ? 1 : (BN_copy(r, a) != NULL);
+
+    if (r != a) {
+        if (!bn_wexpand(r, BN_SM2_256_TOP))
+            return 0;
+        r_d = r->d;
+        sm2_cp_bn(r_d, a_d, BN_SM2_256_TOP);
+    } else
+        r_d = a_d;
+
+    sm2_cp_bn_0(buf.bn, a_d + BN_SM2_256_TOP, top - BN_SM2_256_TOP,
+                BN_SM2_256_TOP);
+
+#if defined(NIST_INT64)
+    {
+        NIST_INT64 acc;         /* accumulator */
+        unsigned int *rp = (unsigned int *)r_d;
+        const unsigned int *bp = (const unsigned int *)buf.ui;
+
+        acc = rp[0];
+        acc += bp[8 - 8];
+        acc += bp[9 - 8];
+        acc += bp[10 - 8];
+        acc += bp[11 - 8];
+        acc += bp[12 - 8];
+        acc += bp[13 - 8];
+        acc += bp[13 - 8];
+        acc += bp[14 - 8];
+        acc += bp[14 - 8];
+        acc += bp[15 - 8];
+        acc += bp[15 - 8];
+        rp[0] = (unsigned int)acc;
+        acc >>= 32;
+
+        acc += rp[1];
+        acc += bp[9 - 8];
+        acc += bp[10 - 8];
+        acc += bp[11 - 8];
+        acc += bp[12 - 8];
+        acc += bp[13 - 8];
+        acc += bp[14 - 8];
+        acc += bp[14 - 8];
+        acc += bp[15 - 8];
+        acc += bp[15 - 8];
+        rp[1] = (unsigned int)acc;
+        acc >>= 32;
+
+        acc += rp[2];
+        acc -= bp[8 - 8];
+        acc -= bp[9 - 8];
+        acc -= bp[13 - 8];
+        acc -= bp[14 - 8];
+        rp[2] = (unsigned int)acc;
+        acc >>= 32;
+
+        acc += rp[3];
+        acc += bp[8 - 8];
+        acc += bp[11 - 8];
+        acc += bp[12 - 8];
+        acc += bp[13 - 8];
+        acc += bp[13 - 8];
+        acc += bp[14 - 8];
+        acc += bp[15 - 8];
+        rp[3] = (unsigned int)acc;
+        acc >>= 32;
+
+        acc += rp[4];
+        acc += bp[9 - 8];
+        acc += bp[12 - 8];
+        acc += bp[13 - 8];
+        acc += bp[14 - 8];
+        acc += bp[14 - 8];
+        acc += bp[15 - 8];
+        rp[4] = (unsigned int)acc;
+        acc >>= 32;
+
+        acc += rp[5];
+        acc += bp[10 - 8];
+        acc += bp[13 - 8];
+        acc += bp[14 - 8];
+        acc += bp[15 - 8];
+        acc += bp[15 - 8];
+        rp[5] = (unsigned int)acc;
+        acc >>= 32;
+
+        acc += rp[6];
+        acc += bp[11 - 8];
+        acc += bp[14 - 8];
+        acc += bp[15 - 8];
+        rp[6] = (unsigned int)acc;
+        acc >>= 32;
+
+        acc += rp[7];
+        acc += bp[8 - 8];
+        acc += bp[9 - 8];
+        acc += bp[10 - 8];
+        acc += bp[11 - 8];
+        acc += bp[12 - 8];
+        acc += bp[12 - 8];
+        acc += bp[13 - 8];
+        acc += bp[13 - 8];
+        acc += bp[14 - 8];
+        acc += bp[14 - 8];
+        acc += bp[15 - 8];
+        acc += bp[15 - 8];
+        acc += bp[15 - 8];
+        rp[7] = (unsigned int)acc;
+        carry = (int)(acc >> 32);
+    }
+#else
+    {
+        BN_ULONG t_d[BN_SM2_256_TOP];
+
+        /*
+         * + 2(c6 + c7 + c8 + c9)
+         */
+	sm2_set_256(t_d, buf.bn, 12,  0, 15, 14, 13, 0, 14, 13);
+
+	sm2_set_256(c_d, buf.bn, 13,  0,  0,  0, 14, 0, 15, 14);
+	carry = (int)bn_add_words(t_d, t_d, c_d, BN_SM2_256_TOP);
+
+	sm2_set_256(c_d, buf.bn, 14,  0,  0,  0,  0, 0,  0, 15);
+	carry += (int)bn_add_words(t_d, c_d, c_d, BN_SM2_256_TOP);
+
+	sm2_set_256(c_d, buf.bn, 15,  0,  0,  0,  0, 0,  0,  0);
+	carry += (int)bn_add_words(t_d, c_d, c_d, BN_SM2_256_TOP);
+
+        /* left shift */
+        {
+            register BN_ULONG *ap, t, c;
+            ap = t_d;
+            c = 0;
+            for (i = BN_SM2_256_TOP; i != 0; --i) {
+                t = *ap;
+                *(ap++) = ((t << 1) | c) & BN_MASK2;
+                c = (t & BN_TBIT) ? 1 : 0;
+            }
+            carry <<= 1;
+            carry |= c;
+        }
+        carry += (int)bn_add_words(r_d, r_d, t_d, BN_SM2_256_TOP);
+
+	/*
+	 * + c1 + c2 + c3 + c4 + c5
+	 */
+	sm2_set_256(t_d, buf.bn,  8, 11, 10,  9,  8, 0,  9,  8);
+        carry += (int)bn_add_words(r_d, r_d, t_d, BN_SM2_256_TOP);
+
+	sm2_set_256(t_d, buf.bn,  9, 14, 13, 12, 11, 0, 10,  9);
+        carry += (int)bn_add_words(r_d, r_d, t_d, BN_SM2_256_TOP);
+
+	sm2_set_256(t_d, buf.bn, 10, 15, 14, 13, 12, 0, 11, 10);
+        carry += (int)bn_add_words(r_d, r_d, t_d, BN_SM2_256_TOP);
+
+	sm2_set_256(t_d, buf.bn, 11,  0,  0,  0,  0, 0, 12, 11);
+        carry += (int)bn_add_words(r_d, r_d, t_d, BN_SM2_256_TOP);
+
+	sm2_set_256(t_d, buf.bn, 15,  0,  0,  0,  0, 0, 13, 12);
+        carry += (int)bn_add_words(r_d, r_d, t_d, BN_SM2_256_TOP);
+
+	/*
+	 * - (c10 + c11 + c12 + c13)
+	 */
+	sm2_set_256(t_d, buf.bn,  0,  0,  0,  0,  0, 8,  0,  0);
+	(void)bn_add_words(c_d, c_d, t_d, BN_SM2_256_TOP);
+
+	sm2_set_256(t_d, buf.bn,  0,  0,  0,  0,  0, 9,  0,  0);
+	(void)bn_add_words(c_d, c_d, t_d, BN_SM2_256_TOP);
+
+	sm2_set_256(t_d, buf.bn,  0,  0,  0,  0,  0,13,  0,  0);
+	(void)bn_add_words(c_d, c_d, t_d, BN_SM2_256_TOP);
+
+	sm2_set_256(t_d, buf.bn,  0,  0,  0,  0,  0,14,  0,  0);
+	(void)bn_add_words(c_d, c_d, t_d, BN_SM2_256_TOP);
+
+	carry -= (int)bn_sub_words(r_d, r_d, c_d, BN_SM2_256_TOP);
+
+    }
+#endif
+    /* see BN_nist_mod_224 for explanation */
+    u.f = bn_sub_words;
+    if (carry > 0)
+        carry =
+            (int)bn_sub_words(r_d, r_d, _sm2_p_256[carry - 1],
+                              BN_SM2_256_TOP);
+    else if (carry < 0) {
+        carry =
+            (int)bn_add_words(r_d, r_d, _sm2_p_256[-carry - 1],
+                              BN_SM2_256_TOP);
+        mask = 0 - (PTR_SIZE_INT) carry;
+        u.p = ((PTR_SIZE_INT) bn_sub_words & mask) |
+            ((PTR_SIZE_INT) bn_add_words & ~mask);
+    } else
+        carry = 1;
+
+    mask =
+        0 - (PTR_SIZE_INT) (*u.f) (c_d, r_d, _sm2_p_256[0], BN_SM2_256_TOP);
+    mask &= 0 - (PTR_SIZE_INT) carry;
+    res = c_d;
+    res = (BN_ULONG *)(((PTR_SIZE_INT) res & ~mask) |
+                       ((PTR_SIZE_INT) r_d & mask));
+    sm2_cp_bn(r_d, res, BN_SM2_256_TOP);
+    r->top = BN_SM2_256_TOP;
+    bn_correct_top(r);
+
+    return 1;
+}
+
+/* I dont think we need this */
+#if 0
+int (*BN_nist_mod_func(const BIGNUM *p)) (BIGNUM *r, const BIGNUM *a,
+                                          const BIGNUM *field, BN_CTX *ctx) {
+    if (BN_ucmp(&_bignum_nist_p_256, p) == 0)
+        return BN_nist_mod_256;
+    return 0;
+}
+#endif
diff --git a/crypto/bn/bn_sqr.c b/crypto/bn/bn_sqr.c
new file mode 100644
index 000000000..44e7332ac
--- /dev/null
+++ b/crypto/bn/bn_sqr.c
@@ -0,0 +1,235 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "internal/cryptlib.h"
+#include "bn_lcl.h"
+
+/* r must not be a */
+/*
+ * I've just gone over this and it is now %20 faster on x86 - eay - 27 Jun 96
+ */
+int BN_sqr(BIGNUM *r, const BIGNUM *a, BN_CTX *ctx)
+{
+    int max, al;
+    int ret = 0;
+    BIGNUM *tmp, *rr;
+
+    bn_check_top(a);
+
+    al = a->top;
+    if (al <= 0) {
+        r->top = 0;
+        r->neg = 0;
+        return 1;
+    }
+
+    BN_CTX_start(ctx);
+    rr = (a != r) ? r : BN_CTX_get(ctx);
+    tmp = BN_CTX_get(ctx);
+    if (!rr || !tmp)
+        goto err;
+
+    max = 2 * al;               /* Non-zero (from above) */
+    if (bn_wexpand(rr, max) == NULL)
+        goto err;
+
+    if (al == 4) {
+#ifndef BN_SQR_COMBA
+        BN_ULONG t[8];
+        bn_sqr_normal(rr->d, a->d, 4, t);
+#else
+        bn_sqr_comba4(rr->d, a->d);
+#endif
+    } else if (al == 8) {
+#ifndef BN_SQR_COMBA
+        BN_ULONG t[16];
+        bn_sqr_normal(rr->d, a->d, 8, t);
+#else
+        bn_sqr_comba8(rr->d, a->d);
+#endif
+    } else {
+#if defined(BN_RECURSION)
+        if (al < BN_SQR_RECURSIVE_SIZE_NORMAL) {
+            BN_ULONG t[BN_SQR_RECURSIVE_SIZE_NORMAL * 2];
+            bn_sqr_normal(rr->d, a->d, al, t);
+        } else {
+            int j, k;
+
+            j = BN_num_bits_word((BN_ULONG)al);
+            j = 1 << (j - 1);
+            k = j + j;
+            if (al == j) {
+                if (bn_wexpand(tmp, k * 2) == NULL)
+                    goto err;
+                bn_sqr_recursive(rr->d, a->d, al, tmp->d);
+            } else {
+                if (bn_wexpand(tmp, max) == NULL)
+                    goto err;
+                bn_sqr_normal(rr->d, a->d, al, tmp->d);
+            }
+        }
+#else
+        if (bn_wexpand(tmp, max) == NULL)
+            goto err;
+        bn_sqr_normal(rr->d, a->d, al, tmp->d);
+#endif
+    }
+
+    rr->neg = 0;
+    /*
+     * If the most-significant half of the top word of 'a' is zero, then the
+     * square of 'a' will max-1 words.
+     */
+    if (a->d[al - 1] == (a->d[al - 1] & BN_MASK2l))
+        rr->top = max - 1;
+    else
+        rr->top = max;
+    if (r != rr && BN_copy(r, rr) == NULL)
+        goto err;
+
+    ret = 1;
+ err:
+    bn_check_top(rr);
+    bn_check_top(tmp);
+    BN_CTX_end(ctx);
+    return (ret);
+}
+
+/* tmp must have 2*n words */
+void bn_sqr_normal(BN_ULONG *r, const BN_ULONG *a, int n, BN_ULONG *tmp)
+{
+    int i, j, max;
+    const BN_ULONG *ap;
+    BN_ULONG *rp;
+
+    max = n * 2;
+    ap = a;
+    rp = r;
+    rp[0] = rp[max - 1] = 0;
+    rp++;
+    j = n;
+
+    if (--j > 0) {
+        ap++;
+        rp[j] = bn_mul_words(rp, ap, j, ap[-1]);
+        rp += 2;
+    }
+
+    for (i = n - 2; i > 0; i--) {
+        j--;
+        ap++;
+        rp[j] = bn_mul_add_words(rp, ap, j, ap[-1]);
+        rp += 2;
+    }
+
+    bn_add_words(r, r, r, max);
+
+    /* There will not be a carry */
+
+    bn_sqr_words(tmp, a, n);
+
+    bn_add_words(r, r, tmp, max);
+}
+
+#ifdef BN_RECURSION
+/*-
+ * r is 2*n words in size,
+ * a and b are both n words in size.    (There's not actually a 'b' here ...)
+ * n must be a power of 2.
+ * We multiply and return the result.
+ * t must be 2*n words in size
+ * We calculate
+ * a[0]*b[0]
+ * a[0]*b[0]+a[1]*b[1]+(a[0]-a[1])*(b[1]-b[0])
+ * a[1]*b[1]
+ */
+void bn_sqr_recursive(BN_ULONG *r, const BN_ULONG *a, int n2, BN_ULONG *t)
+{
+    int n = n2 / 2;
+    int zero, c1;
+    BN_ULONG ln, lo, *p;
+
+    if (n2 == 4) {
+# ifndef BN_SQR_COMBA
+        bn_sqr_normal(r, a, 4, t);
+# else
+        bn_sqr_comba4(r, a);
+# endif
+        return;
+    } else if (n2 == 8) {
+# ifndef BN_SQR_COMBA
+        bn_sqr_normal(r, a, 8, t);
+# else
+        bn_sqr_comba8(r, a);
+# endif
+        return;
+    }
+    if (n2 < BN_SQR_RECURSIVE_SIZE_NORMAL) {
+        bn_sqr_normal(r, a, n2, t);
+        return;
+    }
+    /* r=(a[0]-a[1])*(a[1]-a[0]) */
+    c1 = bn_cmp_words(a, &(a[n]), n);
+    zero = 0;
+    if (c1 > 0)
+        bn_sub_words(t, a, &(a[n]), n);
+    else if (c1 < 0)
+        bn_sub_words(t, &(a[n]), a, n);
+    else
+        zero = 1;
+
+    /* The result will always be negative unless it is zero */
+    p = &(t[n2 * 2]);
+
+    if (!zero)
+        bn_sqr_recursive(&(t[n2]), t, n, p);
+    else
+        memset(&t[n2], 0, sizeof(*t) * n2);
+    bn_sqr_recursive(r, a, n, p);
+    bn_sqr_recursive(&(r[n2]), &(a[n]), n, p);
+
+    /*-
+     * t[32] holds (a[0]-a[1])*(a[1]-a[0]), it is negative or zero
+     * r[10] holds (a[0]*b[0])
+     * r[32] holds (b[1]*b[1])
+     */
+
+    c1 = (int)(bn_add_words(t, r, &(r[n2]), n2));
+
+    /* t[32] is negative */
+    c1 -= (int)(bn_sub_words(&(t[n2]), t, &(t[n2]), n2));
+
+    /*-
+     * t[32] holds (a[0]-a[1])*(a[1]-a[0])+(a[0]*a[0])+(a[1]*a[1])
+     * r[10] holds (a[0]*a[0])
+     * r[32] holds (a[1]*a[1])
+     * c1 holds the carry bits
+     */
+    c1 += (int)(bn_add_words(&(r[n]), &(r[n]), &(t[n2]), n2));
+    if (c1) {
+        p = &(r[n + n2]);
+        lo = *p;
+        ln = (lo + c1) & BN_MASK2;
+        *p = ln;
+
+        /*
+         * The overflow will stop before we over write words we should not
+         * overwrite
+         */
+        if (ln < (BN_ULONG)c1) {
+            do {
+                p++;
+                lo = *p;
+                ln = (lo + 1) & BN_MASK2;
+                *p = ln;
+            } while (ln == 0);
+        }
+    }
+}
+#endif
diff --git a/crypto/bn/bn_sqrt.c b/crypto/bn/bn_sqrt.c
new file mode 100644
index 000000000..84376c78e
--- /dev/null
+++ b/crypto/bn/bn_sqrt.c
@@ -0,0 +1,358 @@
+/*
+ * Copyright 2000-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "internal/cryptlib.h"
+#include "bn_lcl.h"
+
+BIGNUM *BN_mod_sqrt(BIGNUM *in, const BIGNUM *a, const BIGNUM *p, BN_CTX *ctx)
+/*
+ * Returns 'ret' such that ret^2 == a (mod p), using the Tonelli/Shanks
+ * algorithm (cf. Henri Cohen, "A Course in Algebraic Computational Number
+ * Theory", algorithm 1.5.1). 'p' must be prime!
+ */
+{
+    BIGNUM *ret = in;
+    int err = 1;
+    int r;
+    BIGNUM *A, *b, *q, *t, *x, *y;
+    int e, i, j;
+
+    if (!BN_is_odd(p) || BN_abs_is_word(p, 1)) {
+        if (BN_abs_is_word(p, 2)) {
+            if (ret == NULL)
+                ret = BN_new();
+            if (ret == NULL)
+                goto end;
+            if (!BN_set_word(ret, BN_is_bit_set(a, 0))) {
+                if (ret != in)
+                    BN_free(ret);
+                return NULL;
+            }
+            bn_check_top(ret);
+            return ret;
+        }
+
+        BNerr(BN_F_BN_MOD_SQRT, BN_R_P_IS_NOT_PRIME);
+        return (NULL);
+    }
+
+    if (BN_is_zero(a) || BN_is_one(a)) {
+        if (ret == NULL)
+            ret = BN_new();
+        if (ret == NULL)
+            goto end;
+        if (!BN_set_word(ret, BN_is_one(a))) {
+            if (ret != in)
+                BN_free(ret);
+            return NULL;
+        }
+        bn_check_top(ret);
+        return ret;
+    }
+
+    BN_CTX_start(ctx);
+    A = BN_CTX_get(ctx);
+    b = BN_CTX_get(ctx);
+    q = BN_CTX_get(ctx);
+    t = BN_CTX_get(ctx);
+    x = BN_CTX_get(ctx);
+    y = BN_CTX_get(ctx);
+    if (y == NULL)
+        goto end;
+
+    if (ret == NULL)
+        ret = BN_new();
+    if (ret == NULL)
+        goto end;
+
+    /* A = a mod p */
+    if (!BN_nnmod(A, a, p, ctx))
+        goto end;
+
+    /* now write  |p| - 1  as  2^e*q  where  q  is odd */
+    e = 1;
+    while (!BN_is_bit_set(p, e))
+        e++;
+    /* we'll set  q  later (if needed) */
+
+    if (e == 1) {
+        /*-
+         * The easy case:  (|p|-1)/2  is odd, so 2 has an inverse
+         * modulo  (|p|-1)/2,  and square roots can be computed
+         * directly by modular exponentiation.
+         * We have
+         *     2 * (|p|+1)/4 == 1   (mod (|p|-1)/2),
+         * so we can use exponent  (|p|+1)/4,  i.e.  (|p|-3)/4 + 1.
+         */
+        if (!BN_rshift(q, p, 2))
+            goto end;
+        q->neg = 0;
+        if (!BN_add_word(q, 1))
+            goto end;
+        if (!BN_mod_exp(ret, A, q, p, ctx))
+            goto end;
+        err = 0;
+        goto vrfy;
+    }
+
+    if (e == 2) {
+        /*-
+         * |p| == 5  (mod 8)
+         *
+         * In this case  2  is always a non-square since
+         * Legendre(2,p) = (-1)^((p^2-1)/8)  for any odd prime.
+         * So if  a  really is a square, then  2*a  is a non-square.
+         * Thus for
+         *      b := (2*a)^((|p|-5)/8),
+         *      i := (2*a)*b^2
+         * we have
+         *     i^2 = (2*a)^((1 + (|p|-5)/4)*2)
+         *         = (2*a)^((p-1)/2)
+         *         = -1;
+         * so if we set
+         *      x := a*b*(i-1),
+         * then
+         *     x^2 = a^2 * b^2 * (i^2 - 2*i + 1)
+         *         = a^2 * b^2 * (-2*i)
+         *         = a*(-i)*(2*a*b^2)
+         *         = a*(-i)*i
+         *         = a.
+         *
+         * (This is due to A.O.L. Atkin,
+         * ,
+         * November 1992.)
+         */
+
+        /* t := 2*a */
+        if (!BN_mod_lshift1_quick(t, A, p))
+            goto end;
+
+        /* b := (2*a)^((|p|-5)/8) */
+        if (!BN_rshift(q, p, 3))
+            goto end;
+        q->neg = 0;
+        if (!BN_mod_exp(b, t, q, p, ctx))
+            goto end;
+
+        /* y := b^2 */
+        if (!BN_mod_sqr(y, b, p, ctx))
+            goto end;
+
+        /* t := (2*a)*b^2 - 1 */
+        if (!BN_mod_mul(t, t, y, p, ctx))
+            goto end;
+        if (!BN_sub_word(t, 1))
+            goto end;
+
+        /* x = a*b*t */
+        if (!BN_mod_mul(x, A, b, p, ctx))
+            goto end;
+        if (!BN_mod_mul(x, x, t, p, ctx))
+            goto end;
+
+        if (!BN_copy(ret, x))
+            goto end;
+        err = 0;
+        goto vrfy;
+    }
+
+    /*
+     * e > 2, so we really have to use the Tonelli/Shanks algorithm. First,
+     * find some y that is not a square.
+     */
+    if (!BN_copy(q, p))
+        goto end;               /* use 'q' as temp */
+    q->neg = 0;
+    i = 2;
+    do {
+        /*
+         * For efficiency, try small numbers first; if this fails, try random
+         * numbers.
+         */
+        if (i < 22) {
+            if (!BN_set_word(y, i))
+                goto end;
+        } else {
+            if (!BN_pseudo_rand(y, BN_num_bits(p), 0, 0))
+                goto end;
+            if (BN_ucmp(y, p) >= 0) {
+                if (!(p->neg ? BN_add : BN_sub) (y, y, p))
+                    goto end;
+            }
+            /* now 0 <= y < |p| */
+            if (BN_is_zero(y))
+                if (!BN_set_word(y, i))
+                    goto end;
+        }
+
+        r = BN_kronecker(y, q, ctx); /* here 'q' is |p| */
+        if (r < -1)
+            goto end;
+        if (r == 0) {
+            /* m divides p */
+            BNerr(BN_F_BN_MOD_SQRT, BN_R_P_IS_NOT_PRIME);
+            goto end;
+        }
+    }
+    while (r == 1 && ++i < 82);
+
+    if (r != -1) {
+        /*
+         * Many rounds and still no non-square -- this is more likely a bug
+         * than just bad luck. Even if p is not prime, we should have found
+         * some y such that r == -1.
+         */
+        BNerr(BN_F_BN_MOD_SQRT, BN_R_TOO_MANY_ITERATIONS);
+        goto end;
+    }
+
+    /* Here's our actual 'q': */
+    if (!BN_rshift(q, q, e))
+        goto end;
+
+    /*
+     * Now that we have some non-square, we can find an element of order 2^e
+     * by computing its q'th power.
+     */
+    if (!BN_mod_exp(y, y, q, p, ctx))
+        goto end;
+    if (BN_is_one(y)) {
+        BNerr(BN_F_BN_MOD_SQRT, BN_R_P_IS_NOT_PRIME);
+        goto end;
+    }
+
+    /*-
+     * Now we know that (if  p  is indeed prime) there is an integer
+     * k,  0 <= k < 2^e,  such that
+     *
+     *      a^q * y^k == 1   (mod p).
+     *
+     * As  a^q  is a square and  y  is not,  k  must be even.
+     * q+1  is even, too, so there is an element
+     *
+     *     X := a^((q+1)/2) * y^(k/2),
+     *
+     * and it satisfies
+     *
+     *     X^2 = a^q * a     * y^k
+     *         = a,
+     *
+     * so it is the square root that we are looking for.
+     */
+
+    /* t := (q-1)/2  (note that  q  is odd) */
+    if (!BN_rshift1(t, q))
+        goto end;
+
+    /* x := a^((q-1)/2) */
+    if (BN_is_zero(t)) {        /* special case: p = 2^e + 1 */
+        if (!BN_nnmod(t, A, p, ctx))
+            goto end;
+        if (BN_is_zero(t)) {
+            /* special case: a == 0  (mod p) */
+            BN_zero(ret);
+            err = 0;
+            goto end;
+        } else if (!BN_one(x))
+            goto end;
+    } else {
+        if (!BN_mod_exp(x, A, t, p, ctx))
+            goto end;
+        if (BN_is_zero(x)) {
+            /* special case: a == 0  (mod p) */
+            BN_zero(ret);
+            err = 0;
+            goto end;
+        }
+    }
+
+    /* b := a*x^2  (= a^q) */
+    if (!BN_mod_sqr(b, x, p, ctx))
+        goto end;
+    if (!BN_mod_mul(b, b, A, p, ctx))
+        goto end;
+
+    /* x := a*x    (= a^((q+1)/2)) */
+    if (!BN_mod_mul(x, x, A, p, ctx))
+        goto end;
+
+    while (1) {
+        /*-
+         * Now  b  is  a^q * y^k  for some even  k  (0 <= k < 2^E
+         * where  E  refers to the original value of  e,  which we
+         * don't keep in a variable),  and  x  is  a^((q+1)/2) * y^(k/2).
+         *
+         * We have  a*b = x^2,
+         *    y^2^(e-1) = -1,
+         *    b^2^(e-1) = 1.
+         */
+
+        if (BN_is_one(b)) {
+            if (!BN_copy(ret, x))
+                goto end;
+            err = 0;
+            goto vrfy;
+        }
+
+        /* find smallest  i  such that  b^(2^i) = 1 */
+        i = 1;
+        if (!BN_mod_sqr(t, b, p, ctx))
+            goto end;
+        while (!BN_is_one(t)) {
+            i++;
+            if (i == e) {
+                BNerr(BN_F_BN_MOD_SQRT, BN_R_NOT_A_SQUARE);
+                goto end;
+            }
+            if (!BN_mod_mul(t, t, t, p, ctx))
+                goto end;
+        }
+
+        /* t := y^2^(e - i - 1) */
+        if (!BN_copy(t, y))
+            goto end;
+        for (j = e - i - 1; j > 0; j--) {
+            if (!BN_mod_sqr(t, t, p, ctx))
+                goto end;
+        }
+        if (!BN_mod_mul(y, t, t, p, ctx))
+            goto end;
+        if (!BN_mod_mul(x, x, t, p, ctx))
+            goto end;
+        if (!BN_mod_mul(b, b, y, p, ctx))
+            goto end;
+        e = i;
+    }
+
+ vrfy:
+    if (!err) {
+        /*
+         * verify the result -- the input might have been not a square (test
+         * added in 0.9.8)
+         */
+
+        if (!BN_mod_sqr(x, ret, p, ctx))
+            err = 1;
+
+        if (!err && 0 != BN_cmp(x, A)) {
+            BNerr(BN_F_BN_MOD_SQRT, BN_R_NOT_A_SQUARE);
+            err = 1;
+        }
+    }
+
+ end:
+    if (err) {
+        if (ret != in)
+            BN_clear_free(ret);
+        ret = NULL;
+    }
+    BN_CTX_end(ctx);
+    bn_check_top(ret);
+    return ret;
+}
diff --git a/crypto/bn/bn_srp.c b/crypto/bn/bn_srp.c
new file mode 100644
index 000000000..58b1691ee
--- /dev/null
+++ b/crypto/bn/bn_srp.c
@@ -0,0 +1,545 @@
+/*
+ * Copyright 2014-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "bn_lcl.h"
+#include "e_os.h"
+
+#ifndef OPENSSL_NO_SRP
+
+#include 
+#include 
+
+# if (BN_BYTES == 8)
+#  if (defined(_WIN32) || defined(_WIN64)) && !defined(__MINGW32__)
+#   define bn_pack4(a1,a2,a3,a4) ((a1##UI64<<48)|(a2##UI64<<32)|(a3##UI64<<16)|a4##UI64)
+#  elif defined(__arch64__)
+#   define bn_pack4(a1,a2,a3,a4) ((a1##UL<<48)|(a2##UL<<32)|(a3##UL<<16)|a4##UL)
+#  else
+#   define bn_pack4(a1,a2,a3,a4) ((a1##ULL<<48)|(a2##ULL<<32)|(a3##ULL<<16)|a4##ULL)
+#  endif
+# elif (BN_BYTES == 4)
+#  define bn_pack4(a1,a2,a3,a4)  ((a3##UL<<16)|a4##UL), ((a1##UL<<16)|a2##UL)
+# else
+#  error "unsupported BN_BYTES"
+# endif
+
+static const BN_ULONG bn_group_1024_value[] = {
+    bn_pack4(0x9FC6, 0x1D2F, 0xC0EB, 0x06E3),
+    bn_pack4(0xFD51, 0x38FE, 0x8376, 0x435B),
+    bn_pack4(0x2FD4, 0xCBF4, 0x976E, 0xAA9A),
+    bn_pack4(0x68ED, 0xBC3C, 0x0572, 0x6CC0),
+    bn_pack4(0xC529, 0xF566, 0x660E, 0x57EC),
+    bn_pack4(0x8255, 0x9B29, 0x7BCF, 0x1885),
+    bn_pack4(0xCE8E, 0xF4AD, 0x69B1, 0x5D49),
+    bn_pack4(0x5DC7, 0xD7B4, 0x6154, 0xD6B6),
+    bn_pack4(0x8E49, 0x5C1D, 0x6089, 0xDAD1),
+    bn_pack4(0xE0D5, 0xD8E2, 0x50B9, 0x8BE4),
+    bn_pack4(0x383B, 0x4813, 0xD692, 0xC6E0),
+    bn_pack4(0xD674, 0xDF74, 0x96EA, 0x81D3),
+    bn_pack4(0x9EA2, 0x314C, 0x9C25, 0x6576),
+    bn_pack4(0x6072, 0x6187, 0x75FF, 0x3C0B),
+    bn_pack4(0x9C33, 0xF80A, 0xFA8F, 0xC5E8),
+    bn_pack4(0xEEAF, 0x0AB9, 0xADB3, 0x8DD6)
+};
+
+const BIGNUM bn_group_1024 = {
+    (BN_ULONG *)bn_group_1024_value,
+    OSSL_NELEM(bn_group_1024_value),
+    OSSL_NELEM(bn_group_1024_value),
+    0,
+    BN_FLG_STATIC_DATA
+};
+
+static const BN_ULONG bn_group_1536_value[] = {
+    bn_pack4(0xCF76, 0xE3FE, 0xD135, 0xF9BB),
+    bn_pack4(0x1518, 0x0F93, 0x499A, 0x234D),
+    bn_pack4(0x8CE7, 0xA28C, 0x2442, 0xC6F3),
+    bn_pack4(0x5A02, 0x1FFF, 0x5E91, 0x479E),
+    bn_pack4(0x7F8A, 0x2FE9, 0xB8B5, 0x292E),
+    bn_pack4(0x837C, 0x264A, 0xE3A9, 0xBEB8),
+    bn_pack4(0xE442, 0x734A, 0xF7CC, 0xB7AE),
+    bn_pack4(0x6577, 0x2E43, 0x7D6C, 0x7F8C),
+    bn_pack4(0xDB2F, 0xD53D, 0x24B7, 0xC486),
+    bn_pack4(0x6EDF, 0x0195, 0x3934, 0x9627),
+    bn_pack4(0x158B, 0xFD3E, 0x2B9C, 0x8CF5),
+    bn_pack4(0x764E, 0x3F4B, 0x53DD, 0x9DA1),
+    bn_pack4(0x4754, 0x8381, 0xDBC5, 0xB1FC),
+    bn_pack4(0x9B60, 0x9E0B, 0xE3BA, 0xB63D),
+    bn_pack4(0x8134, 0xB1C8, 0xB979, 0x8914),
+    bn_pack4(0xDF02, 0x8A7C, 0xEC67, 0xF0D0),
+    bn_pack4(0x80B6, 0x55BB, 0x9A22, 0xE8DC),
+    bn_pack4(0x1558, 0x903B, 0xA0D0, 0xF843),
+    bn_pack4(0x51C6, 0xA94B, 0xE460, 0x7A29),
+    bn_pack4(0x5F4F, 0x5F55, 0x6E27, 0xCBDE),
+    bn_pack4(0xBEEE, 0xA961, 0x4B19, 0xCC4D),
+    bn_pack4(0xDBA5, 0x1DF4, 0x99AC, 0x4C80),
+    bn_pack4(0xB1F1, 0x2A86, 0x17A4, 0x7BBB),
+    bn_pack4(0x9DEF, 0x3CAF, 0xB939, 0x277A)
+};
+
+const BIGNUM bn_group_1536 = {
+    (BN_ULONG *)bn_group_1536_value,
+    OSSL_NELEM(bn_group_1536_value),
+    OSSL_NELEM(bn_group_1536_value),
+    0,
+    BN_FLG_STATIC_DATA
+};
+
+static const BN_ULONG bn_group_2048_value[] = {
+    bn_pack4(0x0FA7, 0x111F, 0x9E4A, 0xFF73),
+    bn_pack4(0x9B65, 0xE372, 0xFCD6, 0x8EF2),
+    bn_pack4(0x35DE, 0x236D, 0x525F, 0x5475),
+    bn_pack4(0x94B5, 0xC803, 0xD89F, 0x7AE4),
+    bn_pack4(0x71AE, 0x35F8, 0xE9DB, 0xFBB6),
+    bn_pack4(0x2A56, 0x98F3, 0xA8D0, 0xC382),
+    bn_pack4(0x9CCC, 0x041C, 0x7BC3, 0x08D8),
+    bn_pack4(0xAF87, 0x4E73, 0x03CE, 0x5329),
+    bn_pack4(0x6160, 0x2790, 0x04E5, 0x7AE6),
+    bn_pack4(0x032C, 0xFBDB, 0xF52F, 0xB378),
+    bn_pack4(0x5EA7, 0x7A27, 0x75D2, 0xECFA),
+    bn_pack4(0x5445, 0x23B5, 0x24B0, 0xD57D),
+    bn_pack4(0x5B9D, 0x32E6, 0x88F8, 0x7748),
+    bn_pack4(0xF1D2, 0xB907, 0x8717, 0x461A),
+    bn_pack4(0x76BD, 0x207A, 0x436C, 0x6481),
+    bn_pack4(0xCA97, 0xB43A, 0x23FB, 0x8016),
+    bn_pack4(0x1D28, 0x1E44, 0x6B14, 0x773B),
+    bn_pack4(0x7359, 0xD041, 0xD5C3, 0x3EA7),
+    bn_pack4(0xA80D, 0x740A, 0xDBF4, 0xFF74),
+    bn_pack4(0x55F9, 0x7993, 0xEC97, 0x5EEA),
+    bn_pack4(0x2918, 0xA996, 0x2F0B, 0x93B8),
+    bn_pack4(0x661A, 0x05FB, 0xD5FA, 0xAAE8),
+    bn_pack4(0xCF60, 0x9517, 0x9A16, 0x3AB3),
+    bn_pack4(0xE808, 0x3969, 0xEDB7, 0x67B0),
+    bn_pack4(0xCD7F, 0x48A9, 0xDA04, 0xFD50),
+    bn_pack4(0xD523, 0x12AB, 0x4B03, 0x310D),
+    bn_pack4(0x8193, 0xE075, 0x7767, 0xA13D),
+    bn_pack4(0xA373, 0x29CB, 0xB4A0, 0x99ED),
+    bn_pack4(0xFC31, 0x9294, 0x3DB5, 0x6050),
+    bn_pack4(0xAF72, 0xB665, 0x1987, 0xEE07),
+    bn_pack4(0xF166, 0xDE5E, 0x1389, 0x582F),
+    bn_pack4(0xAC6B, 0xDB41, 0x324A, 0x9A9B)
+};
+
+const BIGNUM bn_group_2048 = {
+    (BN_ULONG *)bn_group_2048_value,
+    OSSL_NELEM(bn_group_2048_value),
+    OSSL_NELEM(bn_group_2048_value),
+    0,
+    BN_FLG_STATIC_DATA
+};
+
+static const BN_ULONG bn_group_3072_value[] = {
+    bn_pack4(0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF),
+    bn_pack4(0x4B82, 0xD120, 0xA93A, 0xD2CA),
+    bn_pack4(0x43DB, 0x5BFC, 0xE0FD, 0x108E),
+    bn_pack4(0x08E2, 0x4FA0, 0x74E5, 0xAB31),
+    bn_pack4(0x7709, 0x88C0, 0xBAD9, 0x46E2),
+    bn_pack4(0xBBE1, 0x1757, 0x7A61, 0x5D6C),
+    bn_pack4(0x521F, 0x2B18, 0x177B, 0x200C),
+    bn_pack4(0xD876, 0x0273, 0x3EC8, 0x6A64),
+    bn_pack4(0xF12F, 0xFA06, 0xD98A, 0x0864),
+    bn_pack4(0xCEE3, 0xD226, 0x1AD2, 0xEE6B),
+    bn_pack4(0x1E8C, 0x94E0, 0x4A25, 0x619D),
+    bn_pack4(0xABF5, 0xAE8C, 0xDB09, 0x33D7),
+    bn_pack4(0xB397, 0x0F85, 0xA6E1, 0xE4C7),
+    bn_pack4(0x8AEA, 0x7157, 0x5D06, 0x0C7D),
+    bn_pack4(0xECFB, 0x8504, 0x58DB, 0xEF0A),
+    bn_pack4(0xA855, 0x21AB, 0xDF1C, 0xBA64),
+    bn_pack4(0xAD33, 0x170D, 0x0450, 0x7A33),
+    bn_pack4(0x1572, 0x8E5A, 0x8AAA, 0xC42D),
+    bn_pack4(0x15D2, 0x2618, 0x98FA, 0x0510),
+    bn_pack4(0x3995, 0x497C, 0xEA95, 0x6AE5),
+    bn_pack4(0xDE2B, 0xCBF6, 0x9558, 0x1718),
+    bn_pack4(0xB5C5, 0x5DF0, 0x6F4C, 0x52C9),
+    bn_pack4(0x9B27, 0x83A2, 0xEC07, 0xA28F),
+    bn_pack4(0xE39E, 0x772C, 0x180E, 0x8603),
+    bn_pack4(0x3290, 0x5E46, 0x2E36, 0xCE3B),
+    bn_pack4(0xF174, 0x6C08, 0xCA18, 0x217C),
+    bn_pack4(0x670C, 0x354E, 0x4ABC, 0x9804),
+    bn_pack4(0x9ED5, 0x2907, 0x7096, 0x966D),
+    bn_pack4(0x1C62, 0xF356, 0x2085, 0x52BB),
+    bn_pack4(0x8365, 0x5D23, 0xDCA3, 0xAD96),
+    bn_pack4(0x6916, 0x3FA8, 0xFD24, 0xCF5F),
+    bn_pack4(0x98DA, 0x4836, 0x1C55, 0xD39A),
+    bn_pack4(0xC200, 0x7CB8, 0xA163, 0xBF05),
+    bn_pack4(0x4928, 0x6651, 0xECE4, 0x5B3D),
+    bn_pack4(0xAE9F, 0x2411, 0x7C4B, 0x1FE6),
+    bn_pack4(0xEE38, 0x6BFB, 0x5A89, 0x9FA5),
+    bn_pack4(0x0BFF, 0x5CB6, 0xF406, 0xB7ED),
+    bn_pack4(0xF44C, 0x42E9, 0xA637, 0xED6B),
+    bn_pack4(0xE485, 0xB576, 0x625E, 0x7EC6),
+    bn_pack4(0x4FE1, 0x356D, 0x6D51, 0xC245),
+    bn_pack4(0x302B, 0x0A6D, 0xF25F, 0x1437),
+    bn_pack4(0xEF95, 0x19B3, 0xCD3A, 0x431B),
+    bn_pack4(0x514A, 0x0879, 0x8E34, 0x04DD),
+    bn_pack4(0x020B, 0xBEA6, 0x3B13, 0x9B22),
+    bn_pack4(0x2902, 0x4E08, 0x8A67, 0xCC74),
+    bn_pack4(0xC4C6, 0x628B, 0x80DC, 0x1CD1),
+    bn_pack4(0xC90F, 0xDAA2, 0x2168, 0xC234),
+    bn_pack4(0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF)
+};
+
+const BIGNUM bn_group_3072 = {
+    (BN_ULONG *)bn_group_3072_value,
+    OSSL_NELEM(bn_group_3072_value),
+    OSSL_NELEM(bn_group_3072_value),
+    0,
+    BN_FLG_STATIC_DATA
+};
+
+static const BN_ULONG bn_group_4096_value[] = {
+    bn_pack4(0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF),
+    bn_pack4(0x4DF4, 0x35C9, 0x3406, 0x3199),
+    bn_pack4(0x86FF, 0xB7DC, 0x90A6, 0xC08F),
+    bn_pack4(0x93B4, 0xEA98, 0x8D8F, 0xDDC1),
+    bn_pack4(0xD006, 0x9127, 0xD5B0, 0x5AA9),
+    bn_pack4(0xB81B, 0xDD76, 0x2170, 0x481C),
+    bn_pack4(0x1F61, 0x2970, 0xCEE2, 0xD7AF),
+    bn_pack4(0x233B, 0xA186, 0x515B, 0xE7ED),
+    bn_pack4(0x99B2, 0x964F, 0xA090, 0xC3A2),
+    bn_pack4(0x287C, 0x5947, 0x4E6B, 0xC05D),
+    bn_pack4(0x2E8E, 0xFC14, 0x1FBE, 0xCAA6),
+    bn_pack4(0xDBBB, 0xC2DB, 0x04DE, 0x8EF9),
+    bn_pack4(0x2583, 0xE9CA, 0x2AD4, 0x4CE8),
+    bn_pack4(0x1A94, 0x6834, 0xB615, 0x0BDA),
+    bn_pack4(0x99C3, 0x2718, 0x6AF4, 0xE23C),
+    bn_pack4(0x8871, 0x9A10, 0xBDBA, 0x5B26),
+    bn_pack4(0x1A72, 0x3C12, 0xA787, 0xE6D7),
+    bn_pack4(0x4B82, 0xD120, 0xA921, 0x0801),
+    bn_pack4(0x43DB, 0x5BFC, 0xE0FD, 0x108E),
+    bn_pack4(0x08E2, 0x4FA0, 0x74E5, 0xAB31),
+    bn_pack4(0x7709, 0x88C0, 0xBAD9, 0x46E2),
+    bn_pack4(0xBBE1, 0x1757, 0x7A61, 0x5D6C),
+    bn_pack4(0x521F, 0x2B18, 0x177B, 0x200C),
+    bn_pack4(0xD876, 0x0273, 0x3EC8, 0x6A64),
+    bn_pack4(0xF12F, 0xFA06, 0xD98A, 0x0864),
+    bn_pack4(0xCEE3, 0xD226, 0x1AD2, 0xEE6B),
+    bn_pack4(0x1E8C, 0x94E0, 0x4A25, 0x619D),
+    bn_pack4(0xABF5, 0xAE8C, 0xDB09, 0x33D7),
+    bn_pack4(0xB397, 0x0F85, 0xA6E1, 0xE4C7),
+    bn_pack4(0x8AEA, 0x7157, 0x5D06, 0x0C7D),
+    bn_pack4(0xECFB, 0x8504, 0x58DB, 0xEF0A),
+    bn_pack4(0xA855, 0x21AB, 0xDF1C, 0xBA64),
+    bn_pack4(0xAD33, 0x170D, 0x0450, 0x7A33),
+    bn_pack4(0x1572, 0x8E5A, 0x8AAA, 0xC42D),
+    bn_pack4(0x15D2, 0x2618, 0x98FA, 0x0510),
+    bn_pack4(0x3995, 0x497C, 0xEA95, 0x6AE5),
+    bn_pack4(0xDE2B, 0xCBF6, 0x9558, 0x1718),
+    bn_pack4(0xB5C5, 0x5DF0, 0x6F4C, 0x52C9),
+    bn_pack4(0x9B27, 0x83A2, 0xEC07, 0xA28F),
+    bn_pack4(0xE39E, 0x772C, 0x180E, 0x8603),
+    bn_pack4(0x3290, 0x5E46, 0x2E36, 0xCE3B),
+    bn_pack4(0xF174, 0x6C08, 0xCA18, 0x217C),
+    bn_pack4(0x670C, 0x354E, 0x4ABC, 0x9804),
+    bn_pack4(0x9ED5, 0x2907, 0x7096, 0x966D),
+    bn_pack4(0x1C62, 0xF356, 0x2085, 0x52BB),
+    bn_pack4(0x8365, 0x5D23, 0xDCA3, 0xAD96),
+    bn_pack4(0x6916, 0x3FA8, 0xFD24, 0xCF5F),
+    bn_pack4(0x98DA, 0x4836, 0x1C55, 0xD39A),
+    bn_pack4(0xC200, 0x7CB8, 0xA163, 0xBF05),
+    bn_pack4(0x4928, 0x6651, 0xECE4, 0x5B3D),
+    bn_pack4(0xAE9F, 0x2411, 0x7C4B, 0x1FE6),
+    bn_pack4(0xEE38, 0x6BFB, 0x5A89, 0x9FA5),
+    bn_pack4(0x0BFF, 0x5CB6, 0xF406, 0xB7ED),
+    bn_pack4(0xF44C, 0x42E9, 0xA637, 0xED6B),
+    bn_pack4(0xE485, 0xB576, 0x625E, 0x7EC6),
+    bn_pack4(0x4FE1, 0x356D, 0x6D51, 0xC245),
+    bn_pack4(0x302B, 0x0A6D, 0xF25F, 0x1437),
+    bn_pack4(0xEF95, 0x19B3, 0xCD3A, 0x431B),
+    bn_pack4(0x514A, 0x0879, 0x8E34, 0x04DD),
+    bn_pack4(0x020B, 0xBEA6, 0x3B13, 0x9B22),
+    bn_pack4(0x2902, 0x4E08, 0x8A67, 0xCC74),
+    bn_pack4(0xC4C6, 0x628B, 0x80DC, 0x1CD1),
+    bn_pack4(0xC90F, 0xDAA2, 0x2168, 0xC234),
+    bn_pack4(0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF)
+};
+
+const BIGNUM bn_group_4096 = {
+    (BN_ULONG *)bn_group_4096_value,
+    OSSL_NELEM(bn_group_4096_value),
+    OSSL_NELEM(bn_group_4096_value),
+    0,
+    BN_FLG_STATIC_DATA
+};
+
+static const BN_ULONG bn_group_6144_value[] = {
+    bn_pack4(0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF),
+    bn_pack4(0xE694, 0xF91E, 0x6DCC, 0x4024),
+    bn_pack4(0x12BF, 0x2D5B, 0x0B74, 0x74D6),
+    bn_pack4(0x043E, 0x8F66, 0x3F48, 0x60EE),
+    bn_pack4(0x387F, 0xE8D7, 0x6E3C, 0x0468),
+    bn_pack4(0xDA56, 0xC9EC, 0x2EF2, 0x9632),
+    bn_pack4(0xEB19, 0xCCB1, 0xA313, 0xD55C),
+    bn_pack4(0xF550, 0xAA3D, 0x8A1F, 0xBFF0),
+    bn_pack4(0x06A1, 0xD58B, 0xB7C5, 0xDA76),
+    bn_pack4(0xA797, 0x15EE, 0xF29B, 0xE328),
+    bn_pack4(0x14CC, 0x5ED2, 0x0F80, 0x37E0),
+    bn_pack4(0xCC8F, 0x6D7E, 0xBF48, 0xE1D8),
+    bn_pack4(0x4BD4, 0x07B2, 0x2B41, 0x54AA),
+    bn_pack4(0x0F1D, 0x45B7, 0xFF58, 0x5AC5),
+    bn_pack4(0x23A9, 0x7A7E, 0x36CC, 0x88BE),
+    bn_pack4(0x59E7, 0xC97F, 0xBEC7, 0xE8F3),
+    bn_pack4(0xB5A8, 0x4031, 0x900B, 0x1C9E),
+    bn_pack4(0xD55E, 0x702F, 0x4698, 0x0C82),
+    bn_pack4(0xF482, 0xD7CE, 0x6E74, 0xFEF6),
+    bn_pack4(0xF032, 0xEA15, 0xD172, 0x1D03),
+    bn_pack4(0x5983, 0xCA01, 0xC64B, 0x92EC),
+    bn_pack4(0x6FB8, 0xF401, 0x378C, 0xD2BF),
+    bn_pack4(0x3320, 0x5151, 0x2BD7, 0xAF42),
+    bn_pack4(0xDB7F, 0x1447, 0xE6CC, 0x254B),
+    bn_pack4(0x44CE, 0x6CBA, 0xCED4, 0xBB1B),
+    bn_pack4(0xDA3E, 0xDBEB, 0xCF9B, 0x14ED),
+    bn_pack4(0x1797, 0x27B0, 0x865A, 0x8918),
+    bn_pack4(0xB06A, 0x53ED, 0x9027, 0xD831),
+    bn_pack4(0xE5DB, 0x382F, 0x4130, 0x01AE),
+    bn_pack4(0xF8FF, 0x9406, 0xAD9E, 0x530E),
+    bn_pack4(0xC975, 0x1E76, 0x3DBA, 0x37BD),
+    bn_pack4(0xC1D4, 0xDCB2, 0x6026, 0x46DE),
+    bn_pack4(0x36C3, 0xFAB4, 0xD27C, 0x7026),
+    bn_pack4(0x4DF4, 0x35C9, 0x3402, 0x8492),
+    bn_pack4(0x86FF, 0xB7DC, 0x90A6, 0xC08F),
+    bn_pack4(0x93B4, 0xEA98, 0x8D8F, 0xDDC1),
+    bn_pack4(0xD006, 0x9127, 0xD5B0, 0x5AA9),
+    bn_pack4(0xB81B, 0xDD76, 0x2170, 0x481C),
+    bn_pack4(0x1F61, 0x2970, 0xCEE2, 0xD7AF),
+    bn_pack4(0x233B, 0xA186, 0x515B, 0xE7ED),
+    bn_pack4(0x99B2, 0x964F, 0xA090, 0xC3A2),
+    bn_pack4(0x287C, 0x5947, 0x4E6B, 0xC05D),
+    bn_pack4(0x2E8E, 0xFC14, 0x1FBE, 0xCAA6),
+    bn_pack4(0xDBBB, 0xC2DB, 0x04DE, 0x8EF9),
+    bn_pack4(0x2583, 0xE9CA, 0x2AD4, 0x4CE8),
+    bn_pack4(0x1A94, 0x6834, 0xB615, 0x0BDA),
+    bn_pack4(0x99C3, 0x2718, 0x6AF4, 0xE23C),
+    bn_pack4(0x8871, 0x9A10, 0xBDBA, 0x5B26),
+    bn_pack4(0x1A72, 0x3C12, 0xA787, 0xE6D7),
+    bn_pack4(0x4B82, 0xD120, 0xA921, 0x0801),
+    bn_pack4(0x43DB, 0x5BFC, 0xE0FD, 0x108E),
+    bn_pack4(0x08E2, 0x4FA0, 0x74E5, 0xAB31),
+    bn_pack4(0x7709, 0x88C0, 0xBAD9, 0x46E2),
+    bn_pack4(0xBBE1, 0x1757, 0x7A61, 0x5D6C),
+    bn_pack4(0x521F, 0x2B18, 0x177B, 0x200C),
+    bn_pack4(0xD876, 0x0273, 0x3EC8, 0x6A64),
+    bn_pack4(0xF12F, 0xFA06, 0xD98A, 0x0864),
+    bn_pack4(0xCEE3, 0xD226, 0x1AD2, 0xEE6B),
+    bn_pack4(0x1E8C, 0x94E0, 0x4A25, 0x619D),
+    bn_pack4(0xABF5, 0xAE8C, 0xDB09, 0x33D7),
+    bn_pack4(0xB397, 0x0F85, 0xA6E1, 0xE4C7),
+    bn_pack4(0x8AEA, 0x7157, 0x5D06, 0x0C7D),
+    bn_pack4(0xECFB, 0x8504, 0x58DB, 0xEF0A),
+    bn_pack4(0xA855, 0x21AB, 0xDF1C, 0xBA64),
+    bn_pack4(0xAD33, 0x170D, 0x0450, 0x7A33),
+    bn_pack4(0x1572, 0x8E5A, 0x8AAA, 0xC42D),
+    bn_pack4(0x15D2, 0x2618, 0x98FA, 0x0510),
+    bn_pack4(0x3995, 0x497C, 0xEA95, 0x6AE5),
+    bn_pack4(0xDE2B, 0xCBF6, 0x9558, 0x1718),
+    bn_pack4(0xB5C5, 0x5DF0, 0x6F4C, 0x52C9),
+    bn_pack4(0x9B27, 0x83A2, 0xEC07, 0xA28F),
+    bn_pack4(0xE39E, 0x772C, 0x180E, 0x8603),
+    bn_pack4(0x3290, 0x5E46, 0x2E36, 0xCE3B),
+    bn_pack4(0xF174, 0x6C08, 0xCA18, 0x217C),
+    bn_pack4(0x670C, 0x354E, 0x4ABC, 0x9804),
+    bn_pack4(0x9ED5, 0x2907, 0x7096, 0x966D),
+    bn_pack4(0x1C62, 0xF356, 0x2085, 0x52BB),
+    bn_pack4(0x8365, 0x5D23, 0xDCA3, 0xAD96),
+    bn_pack4(0x6916, 0x3FA8, 0xFD24, 0xCF5F),
+    bn_pack4(0x98DA, 0x4836, 0x1C55, 0xD39A),
+    bn_pack4(0xC200, 0x7CB8, 0xA163, 0xBF05),
+    bn_pack4(0x4928, 0x6651, 0xECE4, 0x5B3D),
+    bn_pack4(0xAE9F, 0x2411, 0x7C4B, 0x1FE6),
+    bn_pack4(0xEE38, 0x6BFB, 0x5A89, 0x9FA5),
+    bn_pack4(0x0BFF, 0x5CB6, 0xF406, 0xB7ED),
+    bn_pack4(0xF44C, 0x42E9, 0xA637, 0xED6B),
+    bn_pack4(0xE485, 0xB576, 0x625E, 0x7EC6),
+    bn_pack4(0x4FE1, 0x356D, 0x6D51, 0xC245),
+    bn_pack4(0x302B, 0x0A6D, 0xF25F, 0x1437),
+    bn_pack4(0xEF95, 0x19B3, 0xCD3A, 0x431B),
+    bn_pack4(0x514A, 0x0879, 0x8E34, 0x04DD),
+    bn_pack4(0x020B, 0xBEA6, 0x3B13, 0x9B22),
+    bn_pack4(0x2902, 0x4E08, 0x8A67, 0xCC74),
+    bn_pack4(0xC4C6, 0x628B, 0x80DC, 0x1CD1),
+    bn_pack4(0xC90F, 0xDAA2, 0x2168, 0xC234),
+    bn_pack4(0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF)
+};
+
+const BIGNUM bn_group_6144 = {
+    (BN_ULONG *)bn_group_6144_value,
+    OSSL_NELEM(bn_group_6144_value),
+    OSSL_NELEM(bn_group_6144_value),
+    0,
+    BN_FLG_STATIC_DATA
+};
+
+static const BN_ULONG bn_group_8192_value[] = {
+    bn_pack4(0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF),
+    bn_pack4(0x60C9, 0x80DD, 0x98ED, 0xD3DF),
+    bn_pack4(0xC81F, 0x56E8, 0x80B9, 0x6E71),
+    bn_pack4(0x9E30, 0x50E2, 0x7656, 0x94DF),
+    bn_pack4(0x9558, 0xE447, 0x5677, 0xE9AA),
+    bn_pack4(0xC919, 0x0DA6, 0xFC02, 0x6E47),
+    bn_pack4(0x889A, 0x002E, 0xD5EE, 0x382B),
+    bn_pack4(0x4009, 0x438B, 0x481C, 0x6CD7),
+    bn_pack4(0x3590, 0x46F4, 0xEB87, 0x9F92),
+    bn_pack4(0xFAF3, 0x6BC3, 0x1ECF, 0xA268),
+    bn_pack4(0xB1D5, 0x10BD, 0x7EE7, 0x4D73),
+    bn_pack4(0xF9AB, 0x4819, 0x5DED, 0x7EA1),
+    bn_pack4(0x64F3, 0x1CC5, 0x0846, 0x851D),
+    bn_pack4(0x4597, 0xE899, 0xA025, 0x5DC1),
+    bn_pack4(0xDF31, 0x0EE0, 0x74AB, 0x6A36),
+    bn_pack4(0x6D2A, 0x13F8, 0x3F44, 0xF82D),
+    bn_pack4(0x062B, 0x3CF5, 0xB3A2, 0x78A6),
+    bn_pack4(0x7968, 0x3303, 0xED5B, 0xDD3A),
+    bn_pack4(0xFA9D, 0x4B7F, 0xA2C0, 0x87E8),
+    bn_pack4(0x4BCB, 0xC886, 0x2F83, 0x85DD),
+    bn_pack4(0x3473, 0xFC64, 0x6CEA, 0x306B),
+    bn_pack4(0x13EB, 0x57A8, 0x1A23, 0xF0C7),
+    bn_pack4(0x2222, 0x2E04, 0xA403, 0x7C07),
+    bn_pack4(0xE3FD, 0xB8BE, 0xFC84, 0x8AD9),
+    bn_pack4(0x238F, 0x16CB, 0xE39D, 0x652D),
+    bn_pack4(0x3423, 0xB474, 0x2BF1, 0xC978),
+    bn_pack4(0x3AAB, 0x639C, 0x5AE4, 0xF568),
+    bn_pack4(0x2576, 0xF693, 0x6BA4, 0x2466),
+    bn_pack4(0x741F, 0xA7BF, 0x8AFC, 0x47ED),
+    bn_pack4(0x3BC8, 0x32B6, 0x8D9D, 0xD300),
+    bn_pack4(0xD8BE, 0xC4D0, 0x73B9, 0x31BA),
+    bn_pack4(0x3877, 0x7CB6, 0xA932, 0xDF8C),
+    bn_pack4(0x74A3, 0x926F, 0x12FE, 0xE5E4),
+    bn_pack4(0xE694, 0xF91E, 0x6DBE, 0x1159),
+    bn_pack4(0x12BF, 0x2D5B, 0x0B74, 0x74D6),
+    bn_pack4(0x043E, 0x8F66, 0x3F48, 0x60EE),
+    bn_pack4(0x387F, 0xE8D7, 0x6E3C, 0x0468),
+    bn_pack4(0xDA56, 0xC9EC, 0x2EF2, 0x9632),
+    bn_pack4(0xEB19, 0xCCB1, 0xA313, 0xD55C),
+    bn_pack4(0xF550, 0xAA3D, 0x8A1F, 0xBFF0),
+    bn_pack4(0x06A1, 0xD58B, 0xB7C5, 0xDA76),
+    bn_pack4(0xA797, 0x15EE, 0xF29B, 0xE328),
+    bn_pack4(0x14CC, 0x5ED2, 0x0F80, 0x37E0),
+    bn_pack4(0xCC8F, 0x6D7E, 0xBF48, 0xE1D8),
+    bn_pack4(0x4BD4, 0x07B2, 0x2B41, 0x54AA),
+    bn_pack4(0x0F1D, 0x45B7, 0xFF58, 0x5AC5),
+    bn_pack4(0x23A9, 0x7A7E, 0x36CC, 0x88BE),
+    bn_pack4(0x59E7, 0xC97F, 0xBEC7, 0xE8F3),
+    bn_pack4(0xB5A8, 0x4031, 0x900B, 0x1C9E),
+    bn_pack4(0xD55E, 0x702F, 0x4698, 0x0C82),
+    bn_pack4(0xF482, 0xD7CE, 0x6E74, 0xFEF6),
+    bn_pack4(0xF032, 0xEA15, 0xD172, 0x1D03),
+    bn_pack4(0x5983, 0xCA01, 0xC64B, 0x92EC),
+    bn_pack4(0x6FB8, 0xF401, 0x378C, 0xD2BF),
+    bn_pack4(0x3320, 0x5151, 0x2BD7, 0xAF42),
+    bn_pack4(0xDB7F, 0x1447, 0xE6CC, 0x254B),
+    bn_pack4(0x44CE, 0x6CBA, 0xCED4, 0xBB1B),
+    bn_pack4(0xDA3E, 0xDBEB, 0xCF9B, 0x14ED),
+    bn_pack4(0x1797, 0x27B0, 0x865A, 0x8918),
+    bn_pack4(0xB06A, 0x53ED, 0x9027, 0xD831),
+    bn_pack4(0xE5DB, 0x382F, 0x4130, 0x01AE),
+    bn_pack4(0xF8FF, 0x9406, 0xAD9E, 0x530E),
+    bn_pack4(0xC975, 0x1E76, 0x3DBA, 0x37BD),
+    bn_pack4(0xC1D4, 0xDCB2, 0x6026, 0x46DE),
+    bn_pack4(0x36C3, 0xFAB4, 0xD27C, 0x7026),
+    bn_pack4(0x4DF4, 0x35C9, 0x3402, 0x8492),
+    bn_pack4(0x86FF, 0xB7DC, 0x90A6, 0xC08F),
+    bn_pack4(0x93B4, 0xEA98, 0x8D8F, 0xDDC1),
+    bn_pack4(0xD006, 0x9127, 0xD5B0, 0x5AA9),
+    bn_pack4(0xB81B, 0xDD76, 0x2170, 0x481C),
+    bn_pack4(0x1F61, 0x2970, 0xCEE2, 0xD7AF),
+    bn_pack4(0x233B, 0xA186, 0x515B, 0xE7ED),
+    bn_pack4(0x99B2, 0x964F, 0xA090, 0xC3A2),
+    bn_pack4(0x287C, 0x5947, 0x4E6B, 0xC05D),
+    bn_pack4(0x2E8E, 0xFC14, 0x1FBE, 0xCAA6),
+    bn_pack4(0xDBBB, 0xC2DB, 0x04DE, 0x8EF9),
+    bn_pack4(0x2583, 0xE9CA, 0x2AD4, 0x4CE8),
+    bn_pack4(0x1A94, 0x6834, 0xB615, 0x0BDA),
+    bn_pack4(0x99C3, 0x2718, 0x6AF4, 0xE23C),
+    bn_pack4(0x8871, 0x9A10, 0xBDBA, 0x5B26),
+    bn_pack4(0x1A72, 0x3C12, 0xA787, 0xE6D7),
+    bn_pack4(0x4B82, 0xD120, 0xA921, 0x0801),
+    bn_pack4(0x43DB, 0x5BFC, 0xE0FD, 0x108E),
+    bn_pack4(0x08E2, 0x4FA0, 0x74E5, 0xAB31),
+    bn_pack4(0x7709, 0x88C0, 0xBAD9, 0x46E2),
+    bn_pack4(0xBBE1, 0x1757, 0x7A61, 0x5D6C),
+    bn_pack4(0x521F, 0x2B18, 0x177B, 0x200C),
+    bn_pack4(0xD876, 0x0273, 0x3EC8, 0x6A64),
+    bn_pack4(0xF12F, 0xFA06, 0xD98A, 0x0864),
+    bn_pack4(0xCEE3, 0xD226, 0x1AD2, 0xEE6B),
+    bn_pack4(0x1E8C, 0x94E0, 0x4A25, 0x619D),
+    bn_pack4(0xABF5, 0xAE8C, 0xDB09, 0x33D7),
+    bn_pack4(0xB397, 0x0F85, 0xA6E1, 0xE4C7),
+    bn_pack4(0x8AEA, 0x7157, 0x5D06, 0x0C7D),
+    bn_pack4(0xECFB, 0x8504, 0x58DB, 0xEF0A),
+    bn_pack4(0xA855, 0x21AB, 0xDF1C, 0xBA64),
+    bn_pack4(0xAD33, 0x170D, 0x0450, 0x7A33),
+    bn_pack4(0x1572, 0x8E5A, 0x8AAA, 0xC42D),
+    bn_pack4(0x15D2, 0x2618, 0x98FA, 0x0510),
+    bn_pack4(0x3995, 0x497C, 0xEA95, 0x6AE5),
+    bn_pack4(0xDE2B, 0xCBF6, 0x9558, 0x1718),
+    bn_pack4(0xB5C5, 0x5DF0, 0x6F4C, 0x52C9),
+    bn_pack4(0x9B27, 0x83A2, 0xEC07, 0xA28F),
+    bn_pack4(0xE39E, 0x772C, 0x180E, 0x8603),
+    bn_pack4(0x3290, 0x5E46, 0x2E36, 0xCE3B),
+    bn_pack4(0xF174, 0x6C08, 0xCA18, 0x217C),
+    bn_pack4(0x670C, 0x354E, 0x4ABC, 0x9804),
+    bn_pack4(0x9ED5, 0x2907, 0x7096, 0x966D),
+    bn_pack4(0x1C62, 0xF356, 0x2085, 0x52BB),
+    bn_pack4(0x8365, 0x5D23, 0xDCA3, 0xAD96),
+    bn_pack4(0x6916, 0x3FA8, 0xFD24, 0xCF5F),
+    bn_pack4(0x98DA, 0x4836, 0x1C55, 0xD39A),
+    bn_pack4(0xC200, 0x7CB8, 0xA163, 0xBF05),
+    bn_pack4(0x4928, 0x6651, 0xECE4, 0x5B3D),
+    bn_pack4(0xAE9F, 0x2411, 0x7C4B, 0x1FE6),
+    bn_pack4(0xEE38, 0x6BFB, 0x5A89, 0x9FA5),
+    bn_pack4(0x0BFF, 0x5CB6, 0xF406, 0xB7ED),
+    bn_pack4(0xF44C, 0x42E9, 0xA637, 0xED6B),
+    bn_pack4(0xE485, 0xB576, 0x625E, 0x7EC6),
+    bn_pack4(0x4FE1, 0x356D, 0x6D51, 0xC245),
+    bn_pack4(0x302B, 0x0A6D, 0xF25F, 0x1437),
+    bn_pack4(0xEF95, 0x19B3, 0xCD3A, 0x431B),
+    bn_pack4(0x514A, 0x0879, 0x8E34, 0x04DD),
+    bn_pack4(0x020B, 0xBEA6, 0x3B13, 0x9B22),
+    bn_pack4(0x2902, 0x4E08, 0x8A67, 0xCC74),
+    bn_pack4(0xC4C6, 0x628B, 0x80DC, 0x1CD1),
+    bn_pack4(0xC90F, 0xDAA2, 0x2168, 0xC234),
+    bn_pack4(0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF)
+};
+
+const BIGNUM bn_group_8192 = {
+    (BN_ULONG *)bn_group_8192_value,
+    OSSL_NELEM(bn_group_8192_value),
+    OSSL_NELEM(bn_group_8192_value),
+    0,
+    BN_FLG_STATIC_DATA
+};
+
+static const BN_ULONG bn_generator_19_value[] = { 19 };
+
+const BIGNUM bn_generator_19 = {
+    (BN_ULONG *)bn_generator_19_value,
+    1,
+    1,
+    0,
+    BN_FLG_STATIC_DATA
+};
+static const BN_ULONG bn_generator_5_value[] = { 5 };
+
+const BIGNUM bn_generator_5 = {
+    (BN_ULONG *)bn_generator_5_value,
+    1,
+    1,
+    0,
+    BN_FLG_STATIC_DATA
+};
+static const BN_ULONG bn_generator_2_value[] = { 2 };
+
+const BIGNUM bn_generator_2 = {
+    (BN_ULONG *)bn_generator_2_value,
+    1,
+    1,
+    0,
+    BN_FLG_STATIC_DATA
+};
+
+#endif
diff --git a/crypto/bn/bn_word.c b/crypto/bn/bn_word.c
new file mode 100644
index 000000000..a34244c4a
--- /dev/null
+++ b/crypto/bn/bn_word.c
@@ -0,0 +1,199 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "internal/cryptlib.h"
+#include "bn_lcl.h"
+
+BN_ULONG BN_mod_word(const BIGNUM *a, BN_ULONG w)
+{
+#ifndef BN_LLONG
+    BN_ULONG ret = 0;
+#else
+    BN_ULLONG ret = 0;
+#endif
+    int i;
+
+    if (w == 0)
+        return (BN_ULONG)-1;
+
+#ifndef BN_LLONG
+    /*
+     * If |w| is too long and we don't have BN_ULLONG then we need to fall
+     * back to using BN_div_word
+     */
+    if (w > ((BN_ULONG)1 << BN_BITS4)) {
+        BIGNUM *tmp = BN_dup(a);
+        if (tmp == NULL)
+            return (BN_ULONG)-1;
+
+        ret = BN_div_word(tmp, w);
+        BN_free(tmp);
+
+        return ret;
+    }
+#endif
+
+    bn_check_top(a);
+    w &= BN_MASK2;
+    for (i = a->top - 1; i >= 0; i--) {
+#ifndef BN_LLONG
+        /*
+         * We can assume here that | w <= ((BN_ULONG)1 << BN_BITS4) | and so
+         * | ret < ((BN_ULONG)1 << BN_BITS4) | and therefore the shifts here are
+         * safe and will not overflow
+         */
+        ret = ((ret << BN_BITS4) | ((a->d[i] >> BN_BITS4) & BN_MASK2l)) % w;
+        ret = ((ret << BN_BITS4) | (a->d[i] & BN_MASK2l)) % w;
+#else
+        ret = (BN_ULLONG) (((ret << (BN_ULLONG) BN_BITS2) | a->d[i]) %
+                           (BN_ULLONG) w);
+#endif
+    }
+    return ((BN_ULONG)ret);
+}
+
+BN_ULONG BN_div_word(BIGNUM *a, BN_ULONG w)
+{
+    BN_ULONG ret = 0;
+    int i, j;
+
+    bn_check_top(a);
+    w &= BN_MASK2;
+
+    if (!w)
+        /* actually this an error (division by zero) */
+        return (BN_ULONG)-1;
+    if (a->top == 0)
+        return 0;
+
+    /* normalize input (so bn_div_words doesn't complain) */
+    j = BN_BITS2 - BN_num_bits_word(w);
+    w <<= j;
+    if (!BN_lshift(a, a, j))
+        return (BN_ULONG)-1;
+
+    for (i = a->top - 1; i >= 0; i--) {
+        BN_ULONG l, d;
+
+        l = a->d[i];
+        d = bn_div_words(ret, l, w);
+        ret = (l - ((d * w) & BN_MASK2)) & BN_MASK2;
+        a->d[i] = d;
+    }
+    if ((a->top > 0) && (a->d[a->top - 1] == 0))
+        a->top--;
+    ret >>= j;
+    bn_check_top(a);
+    return (ret);
+}
+
+int BN_add_word(BIGNUM *a, BN_ULONG w)
+{
+    BN_ULONG l;
+    int i;
+
+    bn_check_top(a);
+    w &= BN_MASK2;
+
+    /* degenerate case: w is zero */
+    if (!w)
+        return 1;
+    /* degenerate case: a is zero */
+    if (BN_is_zero(a))
+        return BN_set_word(a, w);
+    /* handle 'a' when negative */
+    if (a->neg) {
+        a->neg = 0;
+        i = BN_sub_word(a, w);
+        if (!BN_is_zero(a))
+            a->neg = !(a->neg);
+        return (i);
+    }
+    for (i = 0; w != 0 && i < a->top; i++) {
+        a->d[i] = l = (a->d[i] + w) & BN_MASK2;
+        w = (w > l) ? 1 : 0;
+    }
+    if (w && i == a->top) {
+        if (bn_wexpand(a, a->top + 1) == NULL)
+            return 0;
+        a->top++;
+        a->d[i] = w;
+    }
+    bn_check_top(a);
+    return (1);
+}
+
+int BN_sub_word(BIGNUM *a, BN_ULONG w)
+{
+    int i;
+
+    bn_check_top(a);
+    w &= BN_MASK2;
+
+    /* degenerate case: w is zero */
+    if (!w)
+        return 1;
+    /* degenerate case: a is zero */
+    if (BN_is_zero(a)) {
+        i = BN_set_word(a, w);
+        if (i != 0)
+            BN_set_negative(a, 1);
+        return i;
+    }
+    /* handle 'a' when negative */
+    if (a->neg) {
+        a->neg = 0;
+        i = BN_add_word(a, w);
+        a->neg = 1;
+        return (i);
+    }
+
+    if ((a->top == 1) && (a->d[0] < w)) {
+        a->d[0] = w - a->d[0];
+        a->neg = 1;
+        return (1);
+    }
+    i = 0;
+    for (;;) {
+        if (a->d[i] >= w) {
+            a->d[i] -= w;
+            break;
+        } else {
+            a->d[i] = (a->d[i] - w) & BN_MASK2;
+            i++;
+            w = 1;
+        }
+    }
+    if ((a->d[i] == 0) && (i == (a->top - 1)))
+        a->top--;
+    bn_check_top(a);
+    return (1);
+}
+
+int BN_mul_word(BIGNUM *a, BN_ULONG w)
+{
+    BN_ULONG ll;
+
+    bn_check_top(a);
+    w &= BN_MASK2;
+    if (a->top) {
+        if (w == 0)
+            BN_zero(a);
+        else {
+            ll = bn_mul_words(a->d, a->d, a->top, w);
+            if (ll) {
+                if (bn_wexpand(a, a->top + 1) == NULL)
+                    return (0);
+                a->d[a->top++] = ll;
+            }
+        }
+    }
+    bn_check_top(a);
+    return (1);
+}
diff --git a/crypto/bn/bn_x931p.c b/crypto/bn/bn_x931p.c
new file mode 100644
index 000000000..40734cb2f
--- /dev/null
+++ b/crypto/bn/bn_x931p.c
@@ -0,0 +1,238 @@
+/*
+ * Copyright 2011-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include "bn_lcl.h"
+
+/* X9.31 routines for prime derivation */
+
+/*
+ * X9.31 prime derivation. This is used to generate the primes pi (p1, p2,
+ * q1, q2) from a parameter Xpi by checking successive odd integers.
+ */
+
+static int bn_x931_derive_pi(BIGNUM *pi, const BIGNUM *Xpi, BN_CTX *ctx,
+                             BN_GENCB *cb)
+{
+    int i = 0, is_prime;
+    if (!BN_copy(pi, Xpi))
+        return 0;
+    if (!BN_is_odd(pi) && !BN_add_word(pi, 1))
+        return 0;
+    for (;;) {
+        i++;
+        BN_GENCB_call(cb, 0, i);
+        /* NB 27 MR is specified in X9.31 */
+        is_prime = BN_is_prime_fasttest_ex(pi, 27, ctx, 1, cb);
+        if (is_prime < 0)
+            return 0;
+        if (is_prime)
+            break;
+        if (!BN_add_word(pi, 2))
+            return 0;
+    }
+    BN_GENCB_call(cb, 2, i);
+    return 1;
+}
+
+/*
+ * This is the main X9.31 prime derivation function. From parameters Xp1, Xp2
+ * and Xp derive the prime p. If the parameters p1 or p2 are not NULL they
+ * will be returned too: this is needed for testing.
+ */
+
+int BN_X931_derive_prime_ex(BIGNUM *p, BIGNUM *p1, BIGNUM *p2,
+                            const BIGNUM *Xp, const BIGNUM *Xp1,
+                            const BIGNUM *Xp2, const BIGNUM *e, BN_CTX *ctx,
+                            BN_GENCB *cb)
+{
+    int ret = 0;
+
+    BIGNUM *t, *p1p2, *pm1;
+
+    /* Only even e supported */
+    if (!BN_is_odd(e))
+        return 0;
+
+    BN_CTX_start(ctx);
+    if (!p1)
+        p1 = BN_CTX_get(ctx);
+
+    if (!p2)
+        p2 = BN_CTX_get(ctx);
+
+    t = BN_CTX_get(ctx);
+
+    p1p2 = BN_CTX_get(ctx);
+
+    pm1 = BN_CTX_get(ctx);
+
+    if (pm1 == NULL)
+        goto err;
+
+    if (!bn_x931_derive_pi(p1, Xp1, ctx, cb))
+        goto err;
+
+    if (!bn_x931_derive_pi(p2, Xp2, ctx, cb))
+        goto err;
+
+    if (!BN_mul(p1p2, p1, p2, ctx))
+        goto err;
+
+    /* First set p to value of Rp */
+
+    if (!BN_mod_inverse(p, p2, p1, ctx))
+        goto err;
+
+    if (!BN_mul(p, p, p2, ctx))
+        goto err;
+
+    if (!BN_mod_inverse(t, p1, p2, ctx))
+        goto err;
+
+    if (!BN_mul(t, t, p1, ctx))
+        goto err;
+
+    if (!BN_sub(p, p, t))
+        goto err;
+
+    if (p->neg && !BN_add(p, p, p1p2))
+        goto err;
+
+    /* p now equals Rp */
+
+    if (!BN_mod_sub(p, p, Xp, p1p2, ctx))
+        goto err;
+
+    if (!BN_add(p, p, Xp))
+        goto err;
+
+    /* p now equals Yp0 */
+
+    for (;;) {
+        int i = 1;
+        BN_GENCB_call(cb, 0, i++);
+        if (!BN_copy(pm1, p))
+            goto err;
+        if (!BN_sub_word(pm1, 1))
+            goto err;
+        if (!BN_gcd(t, pm1, e, ctx))
+            goto err;
+        if (BN_is_one(t)) {
+            /*
+             * X9.31 specifies 8 MR and 1 Lucas test or any prime test
+             * offering similar or better guarantees 50 MR is considerably
+             * better.
+             */
+            int r = BN_is_prime_fasttest_ex(p, 50, ctx, 1, cb);
+            if (r < 0)
+                goto err;
+            if (r)
+                break;
+        }
+        if (!BN_add(p, p, p1p2))
+            goto err;
+    }
+
+    BN_GENCB_call(cb, 3, 0);
+
+    ret = 1;
+
+ err:
+
+    BN_CTX_end(ctx);
+
+    return ret;
+}
+
+/*
+ * Generate pair of parameters Xp, Xq for X9.31 prime generation. Note: nbits
+ * parameter is sum of number of bits in both.
+ */
+
+int BN_X931_generate_Xpq(BIGNUM *Xp, BIGNUM *Xq, int nbits, BN_CTX *ctx)
+{
+    BIGNUM *t;
+    int i;
+    /*
+     * Number of bits for each prime is of the form 512+128s for s = 0, 1,
+     * ...
+     */
+    if ((nbits < 1024) || (nbits & 0xff))
+        return 0;
+    nbits >>= 1;
+    /*
+     * The random value Xp must be between sqrt(2) * 2^(nbits-1) and 2^nbits
+     * - 1. By setting the top two bits we ensure that the lower bound is
+     * exceeded.
+     */
+    if (!BN_rand(Xp, nbits, BN_RAND_TOP_TWO, BN_RAND_BOTTOM_ANY))
+        goto err;
+
+    BN_CTX_start(ctx);
+    t = BN_CTX_get(ctx);
+
+    for (i = 0; i < 1000; i++) {
+        if (!BN_rand(Xq, nbits, BN_RAND_TOP_TWO, BN_RAND_BOTTOM_ANY))
+            goto err;
+        /* Check that |Xp - Xq| > 2^(nbits - 100) */
+        BN_sub(t, Xp, Xq);
+        if (BN_num_bits(t) > (nbits - 100))
+            break;
+    }
+
+    BN_CTX_end(ctx);
+
+    if (i < 1000)
+        return 1;
+
+    return 0;
+
+ err:
+    BN_CTX_end(ctx);
+    return 0;
+}
+
+/*
+ * Generate primes using X9.31 algorithm. Of the values p, p1, p2, Xp1 and
+ * Xp2 only 'p' needs to be non-NULL. If any of the others are not NULL the
+ * relevant parameter will be stored in it. Due to the fact that |Xp - Xq| >
+ * 2^(nbits - 100) must be satisfied Xp and Xq are generated using the
+ * previous function and supplied as input.
+ */
+
+int BN_X931_generate_prime_ex(BIGNUM *p, BIGNUM *p1, BIGNUM *p2,
+                              BIGNUM *Xp1, BIGNUM *Xp2,
+                              const BIGNUM *Xp,
+                              const BIGNUM *e, BN_CTX *ctx, BN_GENCB *cb)
+{
+    int ret = 0;
+
+    BN_CTX_start(ctx);
+    if (!Xp1)
+        Xp1 = BN_CTX_get(ctx);
+    if (!Xp2)
+        Xp2 = BN_CTX_get(ctx);
+
+    if (!BN_rand(Xp1, 101, BN_RAND_TOP_ONE, BN_RAND_BOTTOM_ANY))
+        goto error;
+    if (!BN_rand(Xp2, 101, BN_RAND_TOP_ONE, BN_RAND_BOTTOM_ANY))
+        goto error;
+    if (!BN_X931_derive_prime_ex(p, p1, p2, Xp, Xp1, Xp2, e, ctx, cb))
+        goto error;
+
+    ret = 1;
+
+ error:
+    BN_CTX_end(ctx);
+
+    return ret;
+
+}
diff --git a/crypto/bn/build.info b/crypto/bn/build.info
new file mode 100644
index 000000000..c0d7bde7c
--- /dev/null
+++ b/crypto/bn/build.info
@@ -0,0 +1,84 @@
+LIBS=../../libcrypto
+SOURCE[../../libcrypto]=\
+        bn_add.c bn_div.c bn_exp.c bn_lib.c bn_ctx.c bn_mul.c bn_mod.c \
+        bn_print.c bn_rand.c bn_shift.c bn_word.c bn_blind.c \
+        bn_kron.c bn_sqrt.c bn_gcd.c bn_prime.c bn_err.c bn_sqr.c \
+        {- $target{bn_asm_src} -} bn_sm2p256.c \
+        bn_recp.c bn_mont.c bn_mpi.c bn_exp2.c bn_gf2m.c bn_nist.c \
+        bn_depr.c bn_const.c bn_x931p.c bn_intern.c bn_dh.c bn_srp.c
+INCLUDE[../../libcrypto]=../../crypto/include
+
+INCLUDE[bn_exp.o]=..
+
+GENERATE[bn-586.s]=asm/bn-586.pl \
+	$(PERLASM_SCHEME) $(CFLAGS) $(LIB_CFLAGS) $(PROCESSOR)
+DEPEND[bn-586.s]=../perlasm/x86asm.pl
+GENERATE[co-586.s]=asm/co-586.pl \
+	$(PERLASM_SCHEME) $(CFLAGS) $(LIB_CFLAGS) $(PROCESSOR)
+DEPEND[co-586.s]=../perlasm/x86asm.pl
+GENERATE[x86-mont.s]=asm/x86-mont.pl \
+	$(PERLASM_SCHEME) $(CFLAGS) $(LIB_CFLAGS) $(PROCESSOR)
+DEPEND[x86-mont.s]=../perlasm/x86asm.pl
+GENERATE[x86-gf2m.s]=asm/x86-gf2m.pl \
+	$(PERLASM_SCHEME) $(CFLAGS) $(LIB_CFLAGS) $(PROCESSOR)
+DEPEND[x86-gf2m.s]=../perlasm/x86asm.pl
+
+GENERATE[sparcv9a-mont.S]=asm/sparcv9a-mont.pl $(PERLASM_SCHEME)
+INCLUDE[sparcv9a-mont.o]=..
+GENERATE[sparcv9-mont.S]=asm/sparcv9-mont.pl $(PERLASM_SCHEME)
+INCLUDE[sparcv9-mont.o]=..
+GENERATE[vis3-mont.S]=asm/vis3-mont.pl $(PERLASM_SCHEME)
+INCLUDE[vis3-mont.o]=..
+GENERATE[sparct4-mont.S]=asm/sparct4-mont.pl $(PERLASM_SCHEME)
+INCLUDE[sparct4-mont.o]=..
+GENERATE[sparcv9-gf2m.S]=asm/sparcv9-gf2m.pl $(PERLASM_SCHEME)
+INCLUDE[sparcv9-gf2m.o]=..
+
+GENERATE[bn-mips.s]=asm/mips.pl $(PERLASM_SCHEME)
+GENERATE[mips-mont.s]=asm/mips-mont.pl $(PERLASM_SCHEME)
+
+GENERATE[s390x-mont.S]=asm/s390x-mont.pl $(PERLASM_SCHEME)
+GENERATE[s390x-gf2m.s]=asm/s390x-gf2m.pl $(PERLASM_SCHEME)
+
+GENERATE[x86_64-mont.s]=asm/x86_64-mont.pl $(PERLASM_SCHEME)
+GENERATE[x86_64-mont5.s]=asm/x86_64-mont5.pl $(PERLASM_SCHEME)
+GENERATE[x86_64-gf2m.s]=asm/x86_64-gf2m.pl $(PERLASM_SCHEME)
+GENERATE[rsaz-x86_64.s]=asm/rsaz-x86_64.pl $(PERLASM_SCHEME)
+GENERATE[rsaz-avx2.s]=asm/rsaz-avx2.pl $(PERLASM_SCHEME)
+
+GENERATE[bn-ia64.s]=asm/ia64.S
+GENERATE[ia64-mont.s]=asm/ia64-mont.pl $(CFLAGS) $(LIB_CFLAGS)
+
+GENERATE[parisc-mont.s]=asm/parisc-mont.pl $(PERLASM_SCHEME)
+
+# ppc - AIX, Linux, MacOS X...
+GENERATE[bn-ppc.s]=asm/ppc.pl $(PERLASM_SCHEME)
+GENERATE[ppc-mont.s]=asm/ppc-mont.pl $(PERLASM_SCHEME)
+GENERATE[ppc64-mont.s]=asm/ppc64-mont.pl $(PERLASM_SCHEME)
+
+GENERATE[alpha-mont.S]=asm/alpha-mont.pl $(PERLASM_SCHEME)
+
+GENERATE[armv4-mont.S]=asm/armv4-mont.pl $(PERLASM_SCHEME)
+INCLUDE[armv4-mont.o]=..
+GENERATE[armv4-gf2m.S]=asm/armv4-gf2m.pl $(PERLASM_SCHEME)
+INCLUDE[armv4-gf2m.o]=..
+GENERATE[armv8-mont.S]=asm/armv8-mont.pl $(PERLASM_SCHEME)
+
+OVERRIDES=bn-mips3.o pa-risc2W.o pa-risc2.c
+BEGINRAW[Makefile]
+##### BN assembler implementations
+
+{- $builddir -}/bn-mips3.o:	{- $sourcedir -}/asm/mips3.s
+	@if [ "$(CC)" = "gcc" ]; then \
+		ABI=`expr "$(CFLAGS)" : ".*-mabi=\([n3264]*\)"` && \
+		as -$$ABI -O -o $@ {- $sourcedir -}/asm/mips3.s; \
+	else	$(CC) -c $(CFLAGS) $(LIB_CFLAGS) -o $@ {- $sourcedir -}/asm/mips3.s; fi
+
+# GNU assembler fails to compile PA-RISC2 modules, insist on calling
+# vendor assembler...
+{- $builddir -}/pa-risc2W.o: {- $sourcedir -}/asm/pa-risc2W.s
+	CC="$(CC)" $(PERL) $(SRCDIR)/util/fipsas.pl $(SRCDIR) $< /usr/ccs/bin/as -o pa-risc2W.o {- $sourcedir -}/asm/pa-risc2W.s
+{- $builddir -}/pa-risc2.o: {- $sourcedir -}/asm/pa-risc2.s
+	CC="$(CC)" $(PERL) $(SRCDIR)/util/fipsas.pl $(SRCDIR) $< /usr/ccs/bin/as -o pa-risc2.o {- $sourcedir -}/asm/pa-risc2.s
+
+ENDRAW[Makefile]
diff --git a/crypto/bn/rsaz_exp.c b/crypto/bn/rsaz_exp.c
new file mode 100644
index 000000000..1a70f6cad
--- /dev/null
+++ b/crypto/bn/rsaz_exp.c
@@ -0,0 +1,352 @@
+/*
+ * Copyright 2013-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*****************************************************************************
+*                                                                            *
+*  Copyright (c) 2012, Intel Corporation                                     *
+*                                                                            *
+*  All rights reserved.                                                      *
+*                                                                            *
+*  Redistribution and use in source and binary forms, with or without        *
+*  modification, are permitted provided that the following conditions are    *
+*  met:                                                                      *
+*                                                                            *
+*  *  Redistributions of source code must retain the above copyright         *
+*     notice, this list of conditions and the following disclaimer.          *
+*                                                                            *
+*  *  Redistributions in binary form must reproduce the above copyright      *
+*     notice, this list of conditions and the following disclaimer in the    *
+*     documentation and/or other materials provided with the                 *
+*     distribution.                                                          *
+*                                                                            *
+*  *  Neither the name of the Intel Corporation nor the names of its         *
+*     contributors may be used to endorse or promote products derived from   *
+*     this software without specific prior written permission.               *
+*                                                                            *
+*                                                                            *
+*  THIS SOFTWARE IS PROVIDED BY INTEL CORPORATION ""AS IS"" AND ANY          *
+*  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE         *
+*  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR        *
+*  PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL CORPORATION OR            *
+*  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,     *
+*  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,       *
+*  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR        *
+*  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF    *
+*  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING      *
+*  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS        *
+*  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.              *
+*                                                                            *
+******************************************************************************
+* Developers and authors:                                                    *
+* Shay Gueron (1, 2), and Vlad Krasnov (1)                                   *
+* (1) Intel Corporation, Israel Development Center, Haifa, Israel            *
+* (2) University of Haifa, Israel                                            *
+*****************************************************************************/
+
+#include 
+#include "rsaz_exp.h"
+
+#ifndef RSAZ_ENABLED
+NON_EMPTY_TRANSLATION_UNIT
+#else
+
+/*
+ * See crypto/bn/asm/rsaz-avx2.pl for further details.
+ */
+void rsaz_1024_norm2red_avx2(void *red, const void *norm);
+void rsaz_1024_mul_avx2(void *ret, const void *a, const void *b,
+                        const void *n, BN_ULONG k);
+void rsaz_1024_sqr_avx2(void *ret, const void *a, const void *n, BN_ULONG k,
+                        int cnt);
+void rsaz_1024_scatter5_avx2(void *tbl, const void *val, int i);
+void rsaz_1024_gather5_avx2(void *val, const void *tbl, int i);
+void rsaz_1024_red2norm_avx2(void *norm, const void *red);
+
+#if defined(__GNUC__)
+# define ALIGN64        __attribute__((aligned(64)))
+#elif defined(_MSC_VER)
+# define ALIGN64        __declspec(align(64))
+#elif defined(__SUNPRO_C)
+# define ALIGN64
+# pragma align 64(one,two80)
+#else
+/* not fatal, might hurt performance a little */
+# define ALIGN64
+#endif
+
+ALIGN64 static const BN_ULONG one[40] = {
+    1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+ALIGN64 static const BN_ULONG two80[40] = {
+    0, 0, 1 << 22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+void RSAZ_1024_mod_exp_avx2(BN_ULONG result_norm[16],
+                            const BN_ULONG base_norm[16],
+                            const BN_ULONG exponent[16],
+                            const BN_ULONG m_norm[16], const BN_ULONG RR[16],
+                            BN_ULONG k0)
+{
+    unsigned char storage[320 * 3 + 32 * 9 * 16 + 64]; /* 5.5KB */
+    unsigned char *p_str = storage + (64 - ((size_t)storage % 64));
+    unsigned char *a_inv, *m, *result;
+    unsigned char *table_s = p_str + 320 * 3;
+    unsigned char *R2 = table_s; /* borrow */
+    int index;
+    int wvalue;
+
+    if ((((size_t)p_str & 4095) + 320) >> 12) {
+        result = p_str;
+        a_inv = p_str + 320;
+        m = p_str + 320 * 2;    /* should not cross page */
+    } else {
+        m = p_str;              /* should not cross page */
+        result = p_str + 320;
+        a_inv = p_str + 320 * 2;
+    }
+
+    rsaz_1024_norm2red_avx2(m, m_norm);
+    rsaz_1024_norm2red_avx2(a_inv, base_norm);
+    rsaz_1024_norm2red_avx2(R2, RR);
+
+    rsaz_1024_mul_avx2(R2, R2, R2, m, k0);
+    rsaz_1024_mul_avx2(R2, R2, two80, m, k0);
+
+    /* table[0] = 1 */
+    rsaz_1024_mul_avx2(result, R2, one, m, k0);
+    /* table[1] = a_inv^1 */
+    rsaz_1024_mul_avx2(a_inv, a_inv, R2, m, k0);
+
+    rsaz_1024_scatter5_avx2(table_s, result, 0);
+    rsaz_1024_scatter5_avx2(table_s, a_inv, 1);
+
+    /* table[2] = a_inv^2 */
+    rsaz_1024_sqr_avx2(result, a_inv, m, k0, 1);
+    rsaz_1024_scatter5_avx2(table_s, result, 2);
+#if 0
+    /* this is almost 2x smaller and less than 1% slower */
+    for (index = 3; index < 32; index++) {
+        rsaz_1024_mul_avx2(result, result, a_inv, m, k0);
+        rsaz_1024_scatter5_avx2(table_s, result, index);
+    }
+#else
+    /* table[4] = a_inv^4 */
+    rsaz_1024_sqr_avx2(result, result, m, k0, 1);
+    rsaz_1024_scatter5_avx2(table_s, result, 4);
+    /* table[8] = a_inv^8 */
+    rsaz_1024_sqr_avx2(result, result, m, k0, 1);
+    rsaz_1024_scatter5_avx2(table_s, result, 8);
+    /* table[16] = a_inv^16 */
+    rsaz_1024_sqr_avx2(result, result, m, k0, 1);
+    rsaz_1024_scatter5_avx2(table_s, result, 16);
+    /* table[17] = a_inv^17 */
+    rsaz_1024_mul_avx2(result, result, a_inv, m, k0);
+    rsaz_1024_scatter5_avx2(table_s, result, 17);
+
+    /* table[3] */
+    rsaz_1024_gather5_avx2(result, table_s, 2);
+    rsaz_1024_mul_avx2(result, result, a_inv, m, k0);
+    rsaz_1024_scatter5_avx2(table_s, result, 3);
+    /* table[6] */
+    rsaz_1024_sqr_avx2(result, result, m, k0, 1);
+    rsaz_1024_scatter5_avx2(table_s, result, 6);
+    /* table[12] */
+    rsaz_1024_sqr_avx2(result, result, m, k0, 1);
+    rsaz_1024_scatter5_avx2(table_s, result, 12);
+    /* table[24] */
+    rsaz_1024_sqr_avx2(result, result, m, k0, 1);
+    rsaz_1024_scatter5_avx2(table_s, result, 24);
+    /* table[25] */
+    rsaz_1024_mul_avx2(result, result, a_inv, m, k0);
+    rsaz_1024_scatter5_avx2(table_s, result, 25);
+
+    /* table[5] */
+    rsaz_1024_gather5_avx2(result, table_s, 4);
+    rsaz_1024_mul_avx2(result, result, a_inv, m, k0);
+    rsaz_1024_scatter5_avx2(table_s, result, 5);
+    /* table[10] */
+    rsaz_1024_sqr_avx2(result, result, m, k0, 1);
+    rsaz_1024_scatter5_avx2(table_s, result, 10);
+    /* table[20] */
+    rsaz_1024_sqr_avx2(result, result, m, k0, 1);
+    rsaz_1024_scatter5_avx2(table_s, result, 20);
+    /* table[21] */
+    rsaz_1024_mul_avx2(result, result, a_inv, m, k0);
+    rsaz_1024_scatter5_avx2(table_s, result, 21);
+
+    /* table[7] */
+    rsaz_1024_gather5_avx2(result, table_s, 6);
+    rsaz_1024_mul_avx2(result, result, a_inv, m, k0);
+    rsaz_1024_scatter5_avx2(table_s, result, 7);
+    /* table[14] */
+    rsaz_1024_sqr_avx2(result, result, m, k0, 1);
+    rsaz_1024_scatter5_avx2(table_s, result, 14);
+    /* table[28] */
+    rsaz_1024_sqr_avx2(result, result, m, k0, 1);
+    rsaz_1024_scatter5_avx2(table_s, result, 28);
+    /* table[29] */
+    rsaz_1024_mul_avx2(result, result, a_inv, m, k0);
+    rsaz_1024_scatter5_avx2(table_s, result, 29);
+
+    /* table[9] */
+    rsaz_1024_gather5_avx2(result, table_s, 8);
+    rsaz_1024_mul_avx2(result, result, a_inv, m, k0);
+    rsaz_1024_scatter5_avx2(table_s, result, 9);
+    /* table[18] */
+    rsaz_1024_sqr_avx2(result, result, m, k0, 1);
+    rsaz_1024_scatter5_avx2(table_s, result, 18);
+    /* table[19] */
+    rsaz_1024_mul_avx2(result, result, a_inv, m, k0);
+    rsaz_1024_scatter5_avx2(table_s, result, 19);
+
+    /* table[11] */
+    rsaz_1024_gather5_avx2(result, table_s, 10);
+    rsaz_1024_mul_avx2(result, result, a_inv, m, k0);
+    rsaz_1024_scatter5_avx2(table_s, result, 11);
+    /* table[22] */
+    rsaz_1024_sqr_avx2(result, result, m, k0, 1);
+    rsaz_1024_scatter5_avx2(table_s, result, 22);
+    /* table[23] */
+    rsaz_1024_mul_avx2(result, result, a_inv, m, k0);
+    rsaz_1024_scatter5_avx2(table_s, result, 23);
+
+    /* table[13] */
+    rsaz_1024_gather5_avx2(result, table_s, 12);
+    rsaz_1024_mul_avx2(result, result, a_inv, m, k0);
+    rsaz_1024_scatter5_avx2(table_s, result, 13);
+    /* table[26] */
+    rsaz_1024_sqr_avx2(result, result, m, k0, 1);
+    rsaz_1024_scatter5_avx2(table_s, result, 26);
+    /* table[27] */
+    rsaz_1024_mul_avx2(result, result, a_inv, m, k0);
+    rsaz_1024_scatter5_avx2(table_s, result, 27);
+
+    /* table[15] */
+    rsaz_1024_gather5_avx2(result, table_s, 14);
+    rsaz_1024_mul_avx2(result, result, a_inv, m, k0);
+    rsaz_1024_scatter5_avx2(table_s, result, 15);
+    /* table[30] */
+    rsaz_1024_sqr_avx2(result, result, m, k0, 1);
+    rsaz_1024_scatter5_avx2(table_s, result, 30);
+    /* table[31] */
+    rsaz_1024_mul_avx2(result, result, a_inv, m, k0);
+    rsaz_1024_scatter5_avx2(table_s, result, 31);
+#endif
+
+    /* load first window */
+    p_str = (unsigned char *)exponent;
+    wvalue = p_str[127] >> 3;
+    rsaz_1024_gather5_avx2(result, table_s, wvalue);
+
+    index = 1014;
+
+    while (index > -1) {        /* loop for the remaining 127 windows */
+
+        rsaz_1024_sqr_avx2(result, result, m, k0, 5);
+
+        wvalue = (p_str[(index / 8) + 1] << 8) | p_str[index / 8];
+        wvalue = (wvalue >> (index % 8)) & 31;
+        index -= 5;
+
+        rsaz_1024_gather5_avx2(a_inv, table_s, wvalue); /* borrow a_inv */
+        rsaz_1024_mul_avx2(result, result, a_inv, m, k0);
+    }
+
+    /* square four times */
+    rsaz_1024_sqr_avx2(result, result, m, k0, 4);
+
+    wvalue = p_str[0] & 15;
+
+    rsaz_1024_gather5_avx2(a_inv, table_s, wvalue); /* borrow a_inv */
+    rsaz_1024_mul_avx2(result, result, a_inv, m, k0);
+
+    /* from Montgomery */
+    rsaz_1024_mul_avx2(result, result, one, m, k0);
+
+    rsaz_1024_red2norm_avx2(result_norm, result);
+
+    OPENSSL_cleanse(storage, sizeof(storage));
+}
+
+/*
+ * See crypto/bn/rsaz-x86_64.pl for further details.
+ */
+void rsaz_512_mul(void *ret, const void *a, const void *b, const void *n,
+                  BN_ULONG k);
+void rsaz_512_mul_scatter4(void *ret, const void *a, const void *n,
+                           BN_ULONG k, const void *tbl, unsigned int power);
+void rsaz_512_mul_gather4(void *ret, const void *a, const void *tbl,
+                          const void *n, BN_ULONG k, unsigned int power);
+void rsaz_512_mul_by_one(void *ret, const void *a, const void *n, BN_ULONG k);
+void rsaz_512_sqr(void *ret, const void *a, const void *n, BN_ULONG k,
+                  int cnt);
+void rsaz_512_scatter4(void *tbl, const BN_ULONG *val, int power);
+void rsaz_512_gather4(BN_ULONG *val, const void *tbl, int power);
+
+void RSAZ_512_mod_exp(BN_ULONG result[8],
+                      const BN_ULONG base[8], const BN_ULONG exponent[8],
+                      const BN_ULONG m[8], BN_ULONG k0, const BN_ULONG RR[8])
+{
+    unsigned char storage[16 * 8 * 8 + 64 * 2 + 64]; /* 1.2KB */
+    unsigned char *table = storage + (64 - ((size_t)storage % 64));
+    BN_ULONG *a_inv = (BN_ULONG *)(table + 16 * 8 * 8);
+    BN_ULONG *temp = (BN_ULONG *)(table + 16 * 8 * 8 + 8 * 8);
+    unsigned char *p_str = (unsigned char *)exponent;
+    int index;
+    unsigned int wvalue;
+
+    /* table[0] = 1_inv */
+    temp[0] = 0 - m[0];
+    temp[1] = ~m[1];
+    temp[2] = ~m[2];
+    temp[3] = ~m[3];
+    temp[4] = ~m[4];
+    temp[5] = ~m[5];
+    temp[6] = ~m[6];
+    temp[7] = ~m[7];
+    rsaz_512_scatter4(table, temp, 0);
+
+    /* table [1] = a_inv^1 */
+    rsaz_512_mul(a_inv, base, RR, m, k0);
+    rsaz_512_scatter4(table, a_inv, 1);
+
+    /* table [2] = a_inv^2 */
+    rsaz_512_sqr(temp, a_inv, m, k0, 1);
+    rsaz_512_scatter4(table, temp, 2);
+
+    for (index = 3; index < 16; index++)
+        rsaz_512_mul_scatter4(temp, a_inv, m, k0, table, index);
+
+    /* load first window */
+    wvalue = p_str[63];
+
+    rsaz_512_gather4(temp, table, wvalue >> 4);
+    rsaz_512_sqr(temp, temp, m, k0, 4);
+    rsaz_512_mul_gather4(temp, temp, table, m, k0, wvalue & 0xf);
+
+    for (index = 62; index >= 0; index--) {
+        wvalue = p_str[index];
+
+        rsaz_512_sqr(temp, temp, m, k0, 4);
+        rsaz_512_mul_gather4(temp, temp, table, m, k0, wvalue >> 4);
+
+        rsaz_512_sqr(temp, temp, m, k0, 4);
+        rsaz_512_mul_gather4(temp, temp, table, m, k0, wvalue & 0x0f);
+    }
+
+    /* from Montgomery */
+    rsaz_512_mul_by_one(result, temp, m, k0);
+
+    OPENSSL_cleanse(storage, sizeof(storage));
+}
+
+#endif
diff --git a/crypto/bn/rsaz_exp.h b/crypto/bn/rsaz_exp.h
new file mode 100644
index 000000000..9501cc808
--- /dev/null
+++ b/crypto/bn/rsaz_exp.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2013-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*****************************************************************************
+*                                                                            *
+*  Copyright (c) 2012, Intel Corporation                                     *
+*                                                                            *
+*  All rights reserved.                                                      *
+*                                                                            *
+*  Redistribution and use in source and binary forms, with or without        *
+*  modification, are permitted provided that the following conditions are    *
+*  met:                                                                      *
+*                                                                            *
+*  *  Redistributions of source code must retain the above copyright         *
+*     notice, this list of conditions and the following disclaimer.          *
+*                                                                            *
+*  *  Redistributions in binary form must reproduce the above copyright      *
+*     notice, this list of conditions and the following disclaimer in the    *
+*     documentation and/or other materials provided with the                 *
+*     distribution.                                                          *
+*                                                                            *
+*  *  Neither the name of the Intel Corporation nor the names of its         *
+*     contributors may be used to endorse or promote products derived from   *
+*     this software without specific prior written permission.               *
+*                                                                            *
+*                                                                            *
+*  THIS SOFTWARE IS PROVIDED BY INTEL CORPORATION ""AS IS"" AND ANY          *
+*  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE         *
+*  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR        *
+*  PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL CORPORATION OR            *
+*  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,     *
+*  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,       *
+*  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR        *
+*  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF    *
+*  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING      *
+*  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS        *
+*  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.              *
+*                                                                            *
+******************************************************************************
+* Developers and authors:                                                    *
+* Shay Gueron (1, 2), and Vlad Krasnov (1)                                   *
+* (1) Intel Corporation, Israel Development Center, Haifa, Israel            *
+* (2) University of Haifa, Israel                                            *
+*****************************************************************************/
+
+#ifndef RSAZ_EXP_H
+# define RSAZ_EXP_H
+
+# undef RSAZ_ENABLED
+# if defined(OPENSSL_BN_ASM_MONT) && \
+        (defined(__x86_64) || defined(__x86_64__) || \
+         defined(_M_AMD64) || defined(_M_X64))
+#  define RSAZ_ENABLED
+
+#  include 
+
+void RSAZ_1024_mod_exp_avx2(BN_ULONG result[16],
+                            const BN_ULONG base_norm[16],
+                            const BN_ULONG exponent[16],
+                            const BN_ULONG m_norm[16], const BN_ULONG RR[16],
+                            BN_ULONG k0);
+int rsaz_avx2_eligible();
+
+void RSAZ_512_mod_exp(BN_ULONG result[8],
+                      const BN_ULONG base_norm[8], const BN_ULONG exponent[8],
+                      const BN_ULONG m_norm[8], BN_ULONG k0,
+                      const BN_ULONG RR[8]);
+
+# endif
+
+#endif
diff --git a/crypto/buffer/buf_err.c b/crypto/buffer/buf_err.c
new file mode 100644
index 000000000..a6a2ab88a
--- /dev/null
+++ b/crypto/buffer/buf_err.c
@@ -0,0 +1,44 @@
+/*
+ * Generated by util/mkerr.pl DO NOT EDIT
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include 
+
+/* BEGIN ERROR CODES */
+#ifndef OPENSSL_NO_ERR
+
+# define ERR_FUNC(func) ERR_PACK(ERR_LIB_BUF,func,0)
+# define ERR_REASON(reason) ERR_PACK(ERR_LIB_BUF,0,reason)
+
+static ERR_STRING_DATA BUF_str_functs[] = {
+    {ERR_FUNC(BUF_F_BUF_MEM_GROW), "BUF_MEM_grow"},
+    {ERR_FUNC(BUF_F_BUF_MEM_GROW_CLEAN), "BUF_MEM_grow_clean"},
+    {ERR_FUNC(BUF_F_BUF_MEM_NEW), "BUF_MEM_new"},
+    {0, NULL}
+};
+
+static ERR_STRING_DATA BUF_str_reasons[] = {
+    {0, NULL}
+};
+
+#endif
+
+int ERR_load_BUF_strings(void)
+{
+#ifndef OPENSSL_NO_ERR
+
+    if (ERR_func_error_string(BUF_str_functs[0].error) == NULL) {
+        ERR_load_strings(0, BUF_str_functs);
+        ERR_load_strings(0, BUF_str_reasons);
+    }
+#endif
+    return 1;
+}
diff --git a/crypto/buffer/buffer.c b/crypto/buffer/buffer.c
new file mode 100644
index 000000000..6b0bd4a40
--- /dev/null
+++ b/crypto/buffer/buffer.c
@@ -0,0 +1,164 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+
+/*
+ * LIMIT_BEFORE_EXPANSION is the maximum n such that (n+3)/3*4 < 2**31. That
+ * function is applied in several functions in this file and this limit
+ * ensures that the result fits in an int.
+ */
+#define LIMIT_BEFORE_EXPANSION 0x5ffffffc
+
+BUF_MEM *BUF_MEM_new_ex(unsigned long flags)
+{
+    BUF_MEM *ret;
+
+    ret = BUF_MEM_new();
+    if (ret != NULL)
+        ret->flags = flags;
+    return (ret);
+}
+
+BUF_MEM *BUF_MEM_new(void)
+{
+    BUF_MEM *ret;
+
+    ret = OPENSSL_zalloc(sizeof(*ret));
+    if (ret == NULL) {
+        BUFerr(BUF_F_BUF_MEM_NEW, ERR_R_MALLOC_FAILURE);
+        return (NULL);
+    }
+    return (ret);
+}
+
+void BUF_MEM_free(BUF_MEM *a)
+{
+    if (a == NULL)
+        return;
+
+    if (a->data != NULL) {
+        if (a->flags & BUF_MEM_FLAG_SECURE)
+            OPENSSL_secure_free(a->data);
+        else
+            OPENSSL_clear_free(a->data, a->max);
+    }
+    OPENSSL_free(a);
+}
+
+/* Allocate a block of secure memory; copy over old data if there
+ * was any, and then free it. */
+static char *sec_alloc_realloc(BUF_MEM *str, size_t len)
+{
+    char *ret;
+
+    ret = OPENSSL_secure_malloc(len);
+    if (str->data != NULL) {
+        if (ret != NULL)
+            memcpy(ret, str->data, str->length);
+        OPENSSL_secure_free(str->data);
+    }
+    return (ret);
+}
+
+size_t BUF_MEM_grow(BUF_MEM *str, size_t len)
+{
+    char *ret;
+    size_t n;
+
+    if (str->length >= len) {
+        str->length = len;
+        return (len);
+    }
+    if (str->max >= len) {
+        if (str->data != NULL)
+            memset(&str->data[str->length], 0, len - str->length);
+        str->length = len;
+        return (len);
+    }
+    /* This limit is sufficient to ensure (len+3)/3*4 < 2**31 */
+    if (len > LIMIT_BEFORE_EXPANSION) {
+        BUFerr(BUF_F_BUF_MEM_GROW, ERR_R_MALLOC_FAILURE);
+        return 0;
+    }
+    n = (len + 3) / 3 * 4;
+    if ((str->flags & BUF_MEM_FLAG_SECURE))
+        ret = sec_alloc_realloc(str, n);
+    else
+        ret = OPENSSL_realloc(str->data, n);
+    if (ret == NULL) {
+        BUFerr(BUF_F_BUF_MEM_GROW, ERR_R_MALLOC_FAILURE);
+        len = 0;
+    } else {
+        str->data = ret;
+        str->max = n;
+        memset(&str->data[str->length], 0, len - str->length);
+        str->length = len;
+    }
+    return (len);
+}
+
+size_t BUF_MEM_grow_clean(BUF_MEM *str, size_t len)
+{
+    char *ret;
+    size_t n;
+
+    if (str->length >= len) {
+        if (str->data != NULL)
+            memset(&str->data[len], 0, str->length - len);
+        str->length = len;
+        return (len);
+    }
+    if (str->max >= len) {
+        memset(&str->data[str->length], 0, len - str->length);
+        str->length = len;
+        return (len);
+    }
+    /* This limit is sufficient to ensure (len+3)/3*4 < 2**31 */
+    if (len > LIMIT_BEFORE_EXPANSION) {
+        BUFerr(BUF_F_BUF_MEM_GROW_CLEAN, ERR_R_MALLOC_FAILURE);
+        return 0;
+    }
+    n = (len + 3) / 3 * 4;
+    if ((str->flags & BUF_MEM_FLAG_SECURE))
+        ret = sec_alloc_realloc(str, n);
+    else
+        ret = OPENSSL_clear_realloc(str->data, str->max, n);
+    if (ret == NULL) {
+        BUFerr(BUF_F_BUF_MEM_GROW_CLEAN, ERR_R_MALLOC_FAILURE);
+        len = 0;
+    } else {
+        str->data = ret;
+        str->max = n;
+        memset(&str->data[str->length], 0, len - str->length);
+        str->length = len;
+    }
+    return (len);
+}
+
+void BUF_reverse(unsigned char *out, const unsigned char *in, size_t size)
+{
+    size_t i;
+    if (in) {
+        out += size - 1;
+        for (i = 0; i < size; i++)
+            *out-- = *in++;
+    } else {
+        unsigned char *q;
+        char c;
+        q = out + size - 1;
+        for (i = 0; i < size / 2; i++) {
+            c = *q;
+            *q-- = *out;
+            *out++ = c;
+        }
+    }
+}
diff --git a/crypto/buffer/build.info b/crypto/buffer/build.info
new file mode 100644
index 000000000..54da1f92a
--- /dev/null
+++ b/crypto/buffer/build.info
@@ -0,0 +1,2 @@
+LIBS=../../libcrypto
+SOURCE[../../libcrypto]=buffer.c buf_err.c
diff --git a/crypto/build.info b/crypto/build.info
new file mode 100644
index 000000000..916d24f66
--- /dev/null
+++ b/crypto/build.info
@@ -0,0 +1,37 @@
+{- use File::Spec::Functions qw/catdir catfile/; -}
+LIBS=../libcrypto
+SOURCE[../libcrypto]=\
+        cryptlib.c mem.c mem_dbg.c cversion.c ex_data.c cpt_err.c \
+        ebcdic.c uid.c o_time.c o_str.c o_dir.c o_fopen.c \
+        threads_pthread.c threads_win.c threads_none.c \
+        o_init.c o_fips.c mem_sec.c init.c {- $target{cpuid_asm_src} -} \
+        {- $target{uplink_aux_src} -}
+EXTRA=  ../ms/uplink-x86.pl ../ms/uplink.c ../ms/applink.c \
+        x86cpuid.pl x86_64cpuid.pl ia64cpuid.S \
+        ppccpuid.pl pariscid.pl alphacpuid.pl arm64cpuid.pl armv4cpuid.pl
+
+DEPEND[cversion.o]=buildinf.h
+GENERATE[buildinf.h]=../util/mkbuildinf.pl "$(CC) $(CFLAGS_Q)" "$(PLATFORM)"
+DEPEND[buildinf.h]=../configdata.pm
+
+GENERATE[uplink-x86.s]=../ms/uplink-x86.pl $(PERLASM_SCHEME)
+GENERATE[uplink-x86_64.s]=../ms/uplink-x86_64.pl $(PERLASM_SCHEME)
+GENERATE[uplink-ia64.s]=../ms/uplink-ia64.pl $(PERLASM_SCHEME)
+
+GENERATE[x86cpuid.s]=x86cpuid.pl $(PERLASM_SCHEME) $(CFLAGS) $(LIB_CFLAGS) $(PROCESSOR)
+DEPEND[x86cpuid.s]=perlasm/x86asm.pl
+
+GENERATE[x86_64cpuid.s]=x86_64cpuid.pl $(PERLASM_SCHEME)
+
+GENERATE[ia64cpuid.s]=ia64cpuid.S
+GENERATE[ppccpuid.s]=ppccpuid.pl $(PERLASM_SCHEME)
+GENERATE[pariscid.s]=pariscid.pl $(PERLASM_SCHEME)
+GENERATE[alphacpuid.s]=alphacpuid.pl
+GENERATE[arm64cpuid.S]=arm64cpuid.pl $(PERLASM_SCHEME)
+INCLUDE[arm64cpuid.o]=.
+GENERATE[armv4cpuid.S]=armv4cpuid.pl $(PERLASM_SCHEME)
+INCLUDE[armv4cpuid.o]=.
+
+IF[{- $config{target} =~ /^(?:Cygwin|mingw|VC-)/ -}]
+  SHARED_SOURCE[../libcrypto]=dllmain.c
+ENDIF
diff --git a/crypto/c64xpluscpuid.pl b/crypto/c64xpluscpuid.pl
new file mode 100644
index 000000000..9efe1205f
--- /dev/null
+++ b/crypto/c64xpluscpuid.pl
@@ -0,0 +1,287 @@
+#! /usr/bin/env perl
+# Copyright 2012-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+while (($output=shift) && ($output!~/\w[\w\-]*\.\w+$/)) {}
+open STDOUT,">$output";
+
+$code.=<<___;
+	.text
+
+	.if	.ASSEMBLER_VERSION<7000000
+	.asg	0,__TI_EABI__
+	.endif
+	.if	__TI_EABI__
+	.asg	OPENSSL_rdtsc,_OPENSSL_rdtsc
+	.asg	OPENSSL_cleanse,_OPENSSL_cleanse
+	.asg	CRYPTO_memcmp,_CRYPTO_memcmp
+	.asg	OPENSSL_atomic_add,_OPENSSL_atomic_add
+	.asg	OPENSSL_wipe_cpu,_OPENSSL_wipe_cpu
+	.asg	OPENSSL_instrument_bus,_OPENSSL_instrument_bus
+	.asg	OPENSSL_instrument_bus2,_OPENSSL_instrument_bus2
+	.endif
+
+	.asg	B3,RA
+
+	.global	_OPENSSL_rdtsc
+_OPENSSL_rdtsc:
+	.asmfunc
+	B	RA
+	MVC	TSCL,B0
+	MVC	TSCH,B1
+  [!B0]	MVC	B0,TSCL		; start TSC
+	MV	B0,A4
+	MV	B1,A5
+	.endasmfunc
+
+	.global	_OPENSSL_cleanse
+_OPENSSL_cleanse:
+	.asmfunc
+	ZERO	A3:A2
+||	ZERO	B2
+||	SHRU	B4,3,B0		; is length >= 8
+||	ADD	1,A4,B6
+  [!B0]	BNOP	RA
+||	ZERO	A1
+||	ZERO	B1
+   [B0]	MVC	B0,ILC
+||[!B0]	CMPLT	0,B4,A1
+||[!B0]	CMPLT	1,B4,B1
+   [A1]	STB	A2,*A4++[2]
+|| [B1] STB	B2,*B6++[2]
+||[!B0]	CMPLT	2,B4,A1
+||[!B0]	CMPLT	3,B4,B1
+   [A1]	STB	A2,*A4++[2]
+|| [B1] STB	B2,*B6++[2]
+||[!B0]	CMPLT	4,B4,A1
+||[!B0]	CMPLT	5,B4,B1
+   [A1]	STB	A2,*A4++[2]
+|| [B1] STB	B2,*B6++[2]
+||[!B0]	CMPLT	6,B4,A1
+   [A1]	STB	A2,*A4++[2]
+
+	SPLOOP	1
+	STNDW	A3:A2,*A4++
+||	SUB	B4,8,B4
+	SPKERNEL
+
+	MV	B4,B0		; remaining bytes
+||	ADD	1,A4,B6
+||	BNOP	RA
+   [B0]	CMPLT	0,B0,A1
+|| [B0]	CMPLT	1,B0,B1
+   [A1]	STB	A2,*A4++[2]
+|| [B1] STB	B2,*B6++[2]
+|| [B0]	CMPLT	2,B0,A1
+|| [B0]	CMPLT	3,B0,B1
+   [A1]	STB	A2,*A4++[2]
+|| [B1] STB	B2,*B6++[2]
+|| [B0]	CMPLT	4,B0,A1
+|| [B0]	CMPLT	5,B0,B1
+   [A1]	STB	A2,*A4++[2]
+|| [B1] STB	B2,*B6++[2]
+|| [B0]	CMPLT	6,B0,A1
+   [A1]	STB	A2,*A4++[2]
+	.endasmfunc
+
+	.global	_CRYPTO_memcmp
+_CRYPTO_memcmp:
+	.asmfunc
+	MV	A6,B0
+  [!B0]	BNOP	RA
+||[!B0]	ZERO	A4
+   [B0]	MVC	B0,ILC
+|| [B0]	ZERO	A0
+	NOP	4
+
+	SPLOOP	1
+	LDBU	*A4++,A1
+||	LDBU	*B4++,B1
+	NOP	4
+	XOR.L	B1,A1,A2
+	SPKERNEL 1,0
+||	OR.S	A2,A0,A0
+
+	BNOP	RA,3
+	ZERO.L	A4
+  [A0]	MVK	1,A4
+	.endasmfunc
+
+	.global	_OPENSSL_atomic_add
+_OPENSSL_atomic_add:
+	.asmfunc
+	MV	A4,B0
+atomic_add?:
+	LL	*B0,B5
+	NOP	4
+	ADD	B4,B5,B5
+	SL	B5,*B0
+	CMTL	*B0,B1
+	NOP	4
+  [!B1]	B	atomic_add?
+   [B1]	BNOP	RA,4
+	MV	B5,A4
+	.endasmfunc
+
+	.global	_OPENSSL_wipe_cpu
+_OPENSSL_wipe_cpu:
+	.asmfunc
+	ZERO	A0
+||	ZERO	B0
+||	ZERO	A1
+||	ZERO	B1
+	ZERO	A3:A2
+||	MVD	B0,B2
+||	ZERO	A4
+||	ZERO	B4
+||	ZERO	A5
+||	ZERO	B5
+||	BNOP	RA
+	ZERO	A7:A6
+||	ZERO	B7:B6
+||	ZERO	A8
+||	ZERO	B8
+||	ZERO	A9
+||	ZERO	B9
+	ZERO	A17:A16
+||	ZERO	B17:B16
+||	ZERO	A18
+||	ZERO	B18
+||	ZERO	A19
+||	ZERO	B19
+	ZERO	A21:A20
+||	ZERO	B21:B20
+||	ZERO	A22
+||	ZERO	B22
+||	ZERO	A23
+||	ZERO	B23
+	ZERO	A25:A24
+||	ZERO	B25:B24
+||	ZERO	A26
+||	ZERO	B26
+||	ZERO	A27
+||	ZERO	B27
+	ZERO	A29:A28
+||	ZERO	B29:B28
+||	ZERO	A30
+||	ZERO	B30
+||	ZERO	A31
+||	ZERO	B31
+	.endasmfunc
+
+CLFLUSH	.macro	CONTROL,ADDR,LEN
+	B	passthrough?
+||	STW	ADDR,*CONTROL[0]
+	STW	LEN,*CONTROL[1]
+spinlock?:
+	LDW	*CONTROL[1],A0
+	NOP	3
+passthrough?:
+	NOP
+  [A0]	BNOP	spinlock?,5
+	.endm
+
+	.global	_OPENSSL_instrument_bus
+_OPENSSL_instrument_bus:
+	.asmfunc
+	MV	B4,B0			; reassign sizeof(output)
+||	MV	A4,B4			; reassign output
+||	MVK	0x00004030,A3
+	MV	B0,A4			; return value
+||	MVK	1,A1
+||	MVKH	0x01840000,A3		; L1DWIBAR
+	MVC	TSCL,B8			; collect 1st tick
+||	MVK	0x00004010,A5
+	MV	B8,B9			; lasttick = tick
+||	MVK	0,B7			; lastdiff = 0
+||	MVKH	0x01840000,A5		; L2WIBAR
+	CLFLUSH	A3,B4,A1		; write-back and invalidate L1D line
+	CLFLUSH	A5,B4,A1		; write-back and invalidate L2 line
+	LL	*B4,B5
+	NOP	4
+	ADD	B7,B5,B5
+	SL	B5,*B4
+	CMTL	*B4,B1
+	NOP	4
+	STW	B5,*B4
+bus_loop1?:
+	MVC	TSCL,B8
+|| [B0]	SUB	B0,1,B0
+	SUB	B8,B9,B7		; lastdiff = tick - lasttick
+||	MV	B8,B9			; lasttick = tick
+	CLFLUSH	A3,B4,A1		; write-back and invalidate L1D line
+	CLFLUSH	A5,B4,A1		; write-back and invalidate L2 line
+	LL	*B4,B5
+	NOP	4
+	ADD	B7,B5,B5
+	SL	B5,*B4
+	CMTL	*B4,B1
+	STW	B5,*B4			; [!B1] is removed to flatten samples
+||	ADDK	4,B4
+|| [B0]	BNOP	bus_loop1?,5
+
+	BNOP	RA,5
+	.endasmfunc
+
+	.global	_OPENSSL_instrument_bus2
+_OPENSSL_instrument_bus2:
+	.asmfunc
+	MV	A6,B0			; reassign max
+||	MV	B4,A6			; reassing sizeof(output)
+||	MVK	0x00004030,A3
+	MV	A4,B4			; reassign output
+||	MVK	0,A4			; return value
+||	MVK	1,A1
+||	MVKH	0x01840000,A3		; L1DWIBAR
+
+	MVC	TSCL,B8			; collect 1st tick
+||	MVK	0x00004010,A5
+	MV	B8,B9			; lasttick = tick
+||	MVK	0,B7			; lastdiff = 0
+||	MVKH	0x01840000,A5		; L2WIBAR
+	CLFLUSH	A3,B4,A1		; write-back and invalidate L1D line
+	CLFLUSH	A5,B4,A1		; write-back and invalidate L2 line
+	LL	*B4,B5
+	NOP	4
+	ADD	B7,B5,B5
+	SL	B5,*B4
+	CMTL	*B4,B1
+	NOP	4
+	STW	B5,*B4
+
+	MVC	TSCL,B8			; collect 1st diff
+	SUB	B8,B9,B7		; lastdiff = tick - lasttick
+||	MV	B8,B9			; lasttick = tick
+||	SUB	B0,1,B0
+bus_loop2?:
+	CLFLUSH	A3,B4,A1		; write-back and invalidate L1D line
+	CLFLUSH	A5,B4,A1		; write-back and invalidate L2 line
+	LL	*B4,B5
+	NOP	4
+	ADD	B7,B5,B5
+	SL	B5,*B4
+	CMTL	*B4,B1
+	STW	B5,*B4			; [!B1] is removed to flatten samples
+||[!B0]	BNOP	bus_loop2_done?,2
+||	SUB	B0,1,B0
+	MVC	TSCL,B8
+	SUB	B8,B9,B8
+||	MV	B8,B9
+	CMPEQ	B8,B7,B2
+||	MV	B8,B7
+  [!B2]	ADDAW	B4,1,B4
+||[!B2]	ADDK	1,A4
+	CMPEQ	A4,A6,A2
+  [!A2]	BNOP	bus_loop2?,5
+
+bus_loop2_done?:
+	BNOP	RA,5
+	.endasmfunc
+___
+
+print $code;
+close STDOUT;
diff --git a/crypto/camellia/asm/cmll-x86.pl b/crypto/camellia/asm/cmll-x86.pl
new file mode 100644
index 000000000..59f9ed914
--- /dev/null
+++ b/crypto/camellia/asm/cmll-x86.pl
@@ -0,0 +1,1150 @@
+#! /usr/bin/env perl
+# Copyright 2008-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+
+# ====================================================================
+# Copyright (c) 2008 Andy Polyakov 
+#
+# This module may be used under the terms of either the GNU General
+# Public License version 2 or later, the GNU Lesser General Public
+# License version 2.1 or later, the Mozilla Public License version
+# 1.1 or the BSD License. The exact terms of either license are
+# distributed along with this module. For further details see
+# http://www.openssl.org/~appro/camellia/.
+# ====================================================================
+
+# Performance in cycles per processed byte (less is better) in
+# 'openssl speed ...' benchmark:
+#
+#			AMD K8	Core2	PIII	P4
+# -evp camellia-128-ecb	21.5	22.8	27.0	28.9
+# + over gcc 3.4.6	+90/11% +70/10%	+53/4%	+160/64%
+# + over icc 8.0	+48/19% +21/15%	+21/17%	+55/37%
+#
+# camellia-128-cbc	17.3	21.1	23.9	25.9
+#
+# 128-bit key setup	196	280	256	240	cycles/key
+# + over gcc 3.4.6	+30/0%	+17/11%	+11/0%	+63/40%
+# + over icc 8.0	+18/3%	+10/0%	+10/3%	+21/10%
+#
+# Pairs of numbers in "+" rows represent performance improvement over
+# compiler generated position-independent code, PIC, and non-PIC
+# respectively. PIC results are of greater relevance, as this module
+# is position-independent, i.e. suitable for a shared library or PIE.
+# Position independence "costs" one register, which is why compilers
+# are so close with non-PIC results, they have an extra register to
+# spare. CBC results are better than ECB ones thanks to "zero-copy"
+# private _x86_* interface, and are ~30-40% better than with compiler
+# generated cmll_cbc.o, and reach ~80-90% of x86_64 performance on
+# same CPU (where applicable).
+
+$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+push(@INC,"${dir}","${dir}../../perlasm");
+require "x86asm.pl";
+
+$OPENSSL=1;
+
+$output = pop;
+open STDOUT,">$output";
+
+&asm_init($ARGV[0],"cmll-586.pl",$ARGV[$#ARGV] eq "386");
+
+@T=("eax","ebx","ecx","edx");
+$idx="esi";
+$key="edi";
+$Tbl="ebp";
+
+# stack frame layout in _x86_Camellia_* routines, frame is allocated
+# by caller
+$__ra=&DWP(0,"esp");	# return address
+$__s0=&DWP(4,"esp");	# s0 backing store
+$__s1=&DWP(8,"esp");	# s1 backing store
+$__s2=&DWP(12,"esp");	# s2 backing store
+$__s3=&DWP(16,"esp");	# s3 backing store
+$__end=&DWP(20,"esp");	# pointer to end/start of key schedule
+
+# stack frame layout in Camellia_[en|crypt] routines, which differs from
+# above by 4 and overlaps by pointer to end/start of key schedule
+$_end=&DWP(16,"esp");
+$_esp=&DWP(20,"esp");
+
+# const unsigned int Camellia_SBOX[4][256];
+# Well, sort of... Camellia_SBOX[0][] is interleaved with [1][],
+# and [2][] - with [3][]. This is done to optimize code size.
+$SBOX1_1110=0;		# Camellia_SBOX[0]
+$SBOX4_4404=4;		# Camellia_SBOX[1]
+$SBOX2_0222=2048;	# Camellia_SBOX[2]
+$SBOX3_3033=2052;	# Camellia_SBOX[3]
+&static_label("Camellia_SIGMA");
+&static_label("Camellia_SBOX");
+
+sub Camellia_Feistel {
+my $i=@_[0];
+my $seed=defined(@_[1])?@_[1]:0;
+my $scale=$seed<0?-8:8;
+my $frame=defined(@_[2])?@_[2]:0;
+my $j=($i&1)*2;
+my $t0=@T[($j)%4],$t1=@T[($j+1)%4],$t2=@T[($j+2)%4],$t3=@T[($j+3)%4];
+
+	&xor	($t0,$idx);				# t0^=key[0]
+	&xor	($t1,&DWP($seed+$i*$scale+4,$key));	# t1^=key[1]
+	&movz	($idx,&HB($t0));			# (t0>>8)&0xff
+	&mov	($t3,&DWP($SBOX3_3033,$Tbl,$idx,8));	# t3=SBOX3_3033[0]
+	&movz	($idx,&LB($t0));			# (t0>>0)&0xff
+	&xor	($t3,&DWP($SBOX4_4404,$Tbl,$idx,8));	# t3^=SBOX4_4404[0]
+	&shr	($t0,16);
+	&movz	($idx,&LB($t1));			# (t1>>0)&0xff
+	&mov	($t2,&DWP($SBOX1_1110,$Tbl,$idx,8));	# t2=SBOX1_1110[1]
+	&movz	($idx,&HB($t0));			# (t0>>24)&0xff
+	&xor	($t3,&DWP($SBOX1_1110,$Tbl,$idx,8));	# t3^=SBOX1_1110[0]
+	&movz	($idx,&HB($t1));			# (t1>>8)&0xff
+	&xor	($t2,&DWP($SBOX4_4404,$Tbl,$idx,8));	# t2^=SBOX4_4404[1]
+	&shr	($t1,16);
+	&movz	($t0,&LB($t0));				# (t0>>16)&0xff
+	&xor	($t3,&DWP($SBOX2_0222,$Tbl,$t0,8));	# t3^=SBOX2_0222[0]
+	&movz	($idx,&HB($t1));			# (t1>>24)&0xff
+	&mov	($t0,&DWP($frame+4*(($j+3)%4),"esp"));	# prefetch "s3"
+	&xor	($t2,$t3);				# t2^=t3
+	&rotr	($t3,8);				# t3=RightRotate(t3,8)
+	&xor	($t2,&DWP($SBOX2_0222,$Tbl,$idx,8));	# t2^=SBOX2_0222[1]
+	&movz	($idx,&LB($t1));			# (t1>>16)&0xff
+	&mov	($t1,&DWP($frame+4*(($j+2)%4),"esp"));	# prefetch "s2"
+	&xor	($t3,$t0);				# t3^=s3
+	&xor	($t2,&DWP($SBOX3_3033,$Tbl,$idx,8));	# t2^=SBOX3_3033[1]
+	&mov	($idx,&DWP($seed+($i+1)*$scale,$key));	# prefetch key[i+1]
+	&xor	($t3,$t2);				# t3^=t2
+	&mov	(&DWP($frame+4*(($j+3)%4),"esp"),$t3);	# s3=t3
+	&xor	($t2,$t1);				# t2^=s2
+	&mov	(&DWP($frame+4*(($j+2)%4),"esp"),$t2);	# s2=t2
+}
+
+# void Camellia_EncryptBlock_Rounds(
+#		int grandRounds,
+#		const Byte plaintext[],
+#		const KEY_TABLE_TYPE keyTable,
+#		Byte ciphertext[])
+&function_begin("Camellia_EncryptBlock_Rounds");
+	&mov	("eax",&wparam(0));	# load grandRounds
+	&mov	($idx,&wparam(1));	# load plaintext pointer
+	&mov	($key,&wparam(2));	# load key schedule pointer
+
+	&mov	("ebx","esp");
+	&sub	("esp",7*4);		# place for s[0-3],keyEnd,esp and ra
+	&and	("esp",-64);
+
+	# place stack frame just "above mod 1024" the key schedule
+	# this ensures that cache associativity of 2 suffices
+	&lea	("ecx",&DWP(-64-63,$key));
+	&sub	("ecx","esp");
+	&neg	("ecx");
+	&and	("ecx",0x3C0);	# modulo 1024, but aligned to cache-line
+	&sub	("esp","ecx");
+	&add	("esp",4);	# 4 is reserved for callee's return address
+
+	&shl	("eax",6);
+	&lea	("eax",&DWP(0,$key,"eax"));
+	&mov	($_esp,"ebx");	# save %esp
+	&mov	($_end,"eax");	# save keyEnd
+
+	&call	(&label("pic_point"));
+	&set_label("pic_point");
+	&blindpop($Tbl);
+	&lea	($Tbl,&DWP(&label("Camellia_SBOX")."-".&label("pic_point"),$Tbl));
+
+	&mov	(@T[0],&DWP(0,$idx));	# load plaintext
+	&mov	(@T[1],&DWP(4,$idx));
+	&mov	(@T[2],&DWP(8,$idx));
+	&bswap	(@T[0]);
+	&mov	(@T[3],&DWP(12,$idx));
+	&bswap	(@T[1]);
+	&bswap	(@T[2]);
+	&bswap	(@T[3]);
+
+	&call	("_x86_Camellia_encrypt");
+
+	&mov	("esp",$_esp);
+	&bswap	(@T[0]);
+	&mov	($idx,&wparam(3));	# load ciphertext pointer
+	&bswap	(@T[1]);
+	&bswap	(@T[2]);
+	&bswap	(@T[3]);
+	&mov	(&DWP(0,$idx),@T[0]);	# write ciphertext
+	&mov	(&DWP(4,$idx),@T[1]);
+	&mov	(&DWP(8,$idx),@T[2]);
+	&mov	(&DWP(12,$idx),@T[3]);
+&function_end("Camellia_EncryptBlock_Rounds");
+# V1.x API
+&function_begin_B("Camellia_EncryptBlock");
+	&mov	("eax",128);
+	&sub	("eax",&wparam(0));	# load keyBitLength
+	&mov	("eax",3);
+	&adc	("eax",0);		# keyBitLength==128?3:4
+	&mov	(&wparam(0),"eax");
+	&jmp	(&label("Camellia_EncryptBlock_Rounds"));
+&function_end_B("Camellia_EncryptBlock");
+
+if ($OPENSSL) {
+# void Camellia_encrypt(
+#		const unsigned char *in,
+#		unsigned char *out,
+#		const CAMELLIA_KEY *key)
+&function_begin("Camellia_encrypt");
+	&mov	($idx,&wparam(0));	# load plaintext pointer
+	&mov	($key,&wparam(2));	# load key schedule pointer
+
+	&mov	("ebx","esp");
+	&sub	("esp",7*4);		# place for s[0-3],keyEnd,esp and ra
+	&and	("esp",-64);
+	&mov	("eax",&DWP(272,$key));	# load grandRounds counter
+
+	# place stack frame just "above mod 1024" the key schedule
+	# this ensures that cache associativity of 2 suffices
+	&lea	("ecx",&DWP(-64-63,$key));
+	&sub	("ecx","esp");
+	&neg	("ecx");
+	&and	("ecx",0x3C0);	# modulo 1024, but aligned to cache-line
+	&sub	("esp","ecx");
+	&add	("esp",4);	# 4 is reserved for callee's return address
+
+	&shl	("eax",6);
+	&lea	("eax",&DWP(0,$key,"eax"));
+	&mov	($_esp,"ebx");	# save %esp
+	&mov	($_end,"eax");	# save keyEnd
+
+	&call	(&label("pic_point"));
+	&set_label("pic_point");
+	&blindpop($Tbl);
+	&lea	($Tbl,&DWP(&label("Camellia_SBOX")."-".&label("pic_point"),$Tbl));
+
+	&mov	(@T[0],&DWP(0,$idx));	# load plaintext
+	&mov	(@T[1],&DWP(4,$idx));
+	&mov	(@T[2],&DWP(8,$idx));
+	&bswap	(@T[0]);
+	&mov	(@T[3],&DWP(12,$idx));
+	&bswap	(@T[1]);
+	&bswap	(@T[2]);
+	&bswap	(@T[3]);
+
+	&call	("_x86_Camellia_encrypt");
+
+	&mov	("esp",$_esp);
+	&bswap	(@T[0]);
+	&mov	($idx,&wparam(1));	# load ciphertext pointer
+	&bswap	(@T[1]);
+	&bswap	(@T[2]);
+	&bswap	(@T[3]);
+	&mov	(&DWP(0,$idx),@T[0]);	# write ciphertext
+	&mov	(&DWP(4,$idx),@T[1]);
+	&mov	(&DWP(8,$idx),@T[2]);
+	&mov	(&DWP(12,$idx),@T[3]);
+&function_end("Camellia_encrypt");
+}
+
+&function_begin_B("_x86_Camellia_encrypt");
+	&xor	(@T[0],&DWP(0,$key));	# ^=key[0-3]
+	&xor	(@T[1],&DWP(4,$key));
+	&xor	(@T[2],&DWP(8,$key));
+	&xor	(@T[3],&DWP(12,$key));
+	&mov	($idx,&DWP(16,$key));	# prefetch key[4]
+
+	&mov	($__s0,@T[0]);		# save s[0-3]
+	&mov	($__s1,@T[1]);
+	&mov	($__s2,@T[2]);
+	&mov	($__s3,@T[3]);
+
+&set_label("loop",16);
+	for ($i=0;$i<6;$i++) { Camellia_Feistel($i,16,4); }
+
+	&add	($key,16*4);
+	&cmp	($key,$__end);
+	&je	(&label("done"));
+
+	# @T[0-1] are preloaded, $idx is preloaded with key[0]
+	&and	($idx,@T[0]);
+	 &mov	 (@T[3],$__s3);
+	&rotl	($idx,1);
+	 &mov	 (@T[2],@T[3]);
+	&xor	(@T[1],$idx);
+	 &or	 (@T[2],&DWP(12,$key));
+	&mov	($__s1,@T[1]);		# s1^=LeftRotate(s0&key[0],1);
+	 &xor	 (@T[2],$__s2);
+
+	&mov	($idx,&DWP(4,$key));
+	 &mov	 ($__s2,@T[2]);		# s2^=s3|key[3];
+	&or	($idx,@T[1]);
+	 &and	 (@T[2],&DWP(8,$key));
+	&xor	(@T[0],$idx);
+	 &rotl	 (@T[2],1);
+	&mov	($__s0,@T[0]);		# s0^=s1|key[1];
+	 &xor	 (@T[3],@T[2]);
+	&mov	($idx,&DWP(16,$key));		# prefetch key[4]
+	 &mov	 ($__s3,@T[3]);		# s3^=LeftRotate(s2&key[2],1);
+	&jmp	(&label("loop"));
+
+&set_label("done",8);
+	&mov	(@T[2],@T[0]);		# SwapHalf
+	&mov	(@T[3],@T[1]);
+	&mov	(@T[0],$__s2);
+	&mov	(@T[1],$__s3);
+	&xor	(@T[0],$idx);		# $idx is preloaded with key[0]
+	&xor	(@T[1],&DWP(4,$key));
+	&xor	(@T[2],&DWP(8,$key));
+	&xor	(@T[3],&DWP(12,$key));
+	&ret	();
+&function_end_B("_x86_Camellia_encrypt");
+
+# void Camellia_DecryptBlock_Rounds(
+#		int grandRounds,
+#		const Byte ciphertext[],
+#		const KEY_TABLE_TYPE keyTable,
+#		Byte plaintext[])
+&function_begin("Camellia_DecryptBlock_Rounds");
+	&mov	("eax",&wparam(0));	# load grandRounds
+	&mov	($idx,&wparam(1));	# load ciphertext pointer
+	&mov	($key,&wparam(2));	# load key schedule pointer
+
+	&mov	("ebx","esp");
+	&sub	("esp",7*4);		# place for s[0-3],keyEnd,esp and ra
+	&and	("esp",-64);
+
+	# place stack frame just "above mod 1024" the key schedule
+	# this ensures that cache associativity of 2 suffices
+	&lea	("ecx",&DWP(-64-63,$key));
+	&sub	("ecx","esp");
+	&neg	("ecx");
+	&and	("ecx",0x3C0);	# modulo 1024, but aligned to cache-line
+	&sub	("esp","ecx");
+	&add	("esp",4);	# 4 is reserved for callee's return address
+
+	&shl	("eax",6);
+	&mov	(&DWP(4*4,"esp"),$key);	# save keyStart
+	&lea	($key,&DWP(0,$key,"eax"));
+	&mov	(&DWP(5*4,"esp"),"ebx");# save %esp
+
+	&call	(&label("pic_point"));
+	&set_label("pic_point");
+	&blindpop($Tbl);
+	&lea	($Tbl,&DWP(&label("Camellia_SBOX")."-".&label("pic_point"),$Tbl));
+
+	&mov	(@T[0],&DWP(0,$idx));	# load ciphertext
+	&mov	(@T[1],&DWP(4,$idx));
+	&mov	(@T[2],&DWP(8,$idx));
+	&bswap	(@T[0]);
+	&mov	(@T[3],&DWP(12,$idx));
+	&bswap	(@T[1]);
+	&bswap	(@T[2]);
+	&bswap	(@T[3]);
+
+	&call	("_x86_Camellia_decrypt");
+
+	&mov	("esp",&DWP(5*4,"esp"));
+	&bswap	(@T[0]);
+	&mov	($idx,&wparam(3));	# load plaintext pointer
+	&bswap	(@T[1]);
+	&bswap	(@T[2]);
+	&bswap	(@T[3]);
+	&mov	(&DWP(0,$idx),@T[0]);	# write plaintext
+	&mov	(&DWP(4,$idx),@T[1]);
+	&mov	(&DWP(8,$idx),@T[2]);
+	&mov	(&DWP(12,$idx),@T[3]);
+&function_end("Camellia_DecryptBlock_Rounds");
+# V1.x API
+&function_begin_B("Camellia_DecryptBlock");
+	&mov	("eax",128);
+	&sub	("eax",&wparam(0));	# load keyBitLength
+	&mov	("eax",3);
+	&adc	("eax",0);		# keyBitLength==128?3:4
+	&mov	(&wparam(0),"eax");
+	&jmp	(&label("Camellia_DecryptBlock_Rounds"));
+&function_end_B("Camellia_DecryptBlock");
+
+if ($OPENSSL) {
+# void Camellia_decrypt(
+#		const unsigned char *in,
+#		unsigned char *out,
+#		const CAMELLIA_KEY *key)
+&function_begin("Camellia_decrypt");
+	&mov	($idx,&wparam(0));	# load ciphertext pointer
+	&mov	($key,&wparam(2));	# load key schedule pointer
+
+	&mov	("ebx","esp");
+	&sub	("esp",7*4);		# place for s[0-3],keyEnd,esp and ra
+	&and	("esp",-64);
+	&mov	("eax",&DWP(272,$key));	# load grandRounds counter
+
+	# place stack frame just "above mod 1024" the key schedule
+	# this ensures that cache associativity of 2 suffices
+	&lea	("ecx",&DWP(-64-63,$key));
+	&sub	("ecx","esp");
+	&neg	("ecx");
+	&and	("ecx",0x3C0);	# modulo 1024, but aligned to cache-line
+	&sub	("esp","ecx");
+	&add	("esp",4);	# 4 is reserved for callee's return address
+
+	&shl	("eax",6);
+	&mov	(&DWP(4*4,"esp"),$key);	# save keyStart
+	&lea	($key,&DWP(0,$key,"eax"));
+	&mov	(&DWP(5*4,"esp"),"ebx");# save %esp
+
+	&call	(&label("pic_point"));
+	&set_label("pic_point");
+	&blindpop($Tbl);
+	&lea	($Tbl,&DWP(&label("Camellia_SBOX")."-".&label("pic_point"),$Tbl));
+
+	&mov	(@T[0],&DWP(0,$idx));	# load ciphertext
+	&mov	(@T[1],&DWP(4,$idx));
+	&mov	(@T[2],&DWP(8,$idx));
+	&bswap	(@T[0]);
+	&mov	(@T[3],&DWP(12,$idx));
+	&bswap	(@T[1]);
+	&bswap	(@T[2]);
+	&bswap	(@T[3]);
+
+	&call	("_x86_Camellia_decrypt");
+
+	&mov	("esp",&DWP(5*4,"esp"));
+	&bswap	(@T[0]);
+	&mov	($idx,&wparam(1));	# load plaintext pointer
+	&bswap	(@T[1]);
+	&bswap	(@T[2]);
+	&bswap	(@T[3]);
+	&mov	(&DWP(0,$idx),@T[0]);	# write plaintext
+	&mov	(&DWP(4,$idx),@T[1]);
+	&mov	(&DWP(8,$idx),@T[2]);
+	&mov	(&DWP(12,$idx),@T[3]);
+&function_end("Camellia_decrypt");
+}
+
+&function_begin_B("_x86_Camellia_decrypt");
+	&xor	(@T[0],&DWP(0,$key));	# ^=key[0-3]
+	&xor	(@T[1],&DWP(4,$key));
+	&xor	(@T[2],&DWP(8,$key));
+	&xor	(@T[3],&DWP(12,$key));
+	&mov	($idx,&DWP(-8,$key));	# prefetch key[-2]
+
+	&mov	($__s0,@T[0]);		# save s[0-3]
+	&mov	($__s1,@T[1]);
+	&mov	($__s2,@T[2]);
+	&mov	($__s3,@T[3]);
+
+&set_label("loop",16);
+	for ($i=0;$i<6;$i++) { Camellia_Feistel($i,-8,4); }
+
+	&sub	($key,16*4);
+	&cmp	($key,$__end);
+	&je	(&label("done"));
+
+	# @T[0-1] are preloaded, $idx is preloaded with key[2]
+	&and	($idx,@T[0]);
+	 &mov	 (@T[3],$__s3);
+	&rotl	($idx,1);
+	 &mov	 (@T[2],@T[3]);
+	&xor	(@T[1],$idx);
+	 &or	 (@T[2],&DWP(4,$key));
+	&mov	($__s1,@T[1]);		# s1^=LeftRotate(s0&key[0],1);
+	 &xor	 (@T[2],$__s2);
+
+	&mov	($idx,&DWP(12,$key));
+	 &mov	 ($__s2,@T[2]);		# s2^=s3|key[3];
+	&or	($idx,@T[1]);
+	 &and	 (@T[2],&DWP(0,$key));
+	&xor	(@T[0],$idx);
+	 &rotl	 (@T[2],1);
+	&mov	($__s0,@T[0]);		# s0^=s1|key[1];
+	 &xor	 (@T[3],@T[2]);
+	&mov	($idx,&DWP(-8,$key));	# prefetch key[4]
+	 &mov	 ($__s3,@T[3]);		# s3^=LeftRotate(s2&key[2],1);
+	&jmp	(&label("loop"));
+
+&set_label("done",8);
+	&mov	(@T[2],@T[0]);		# SwapHalf
+	&mov	(@T[3],@T[1]);
+	&mov	(@T[0],$__s2);
+	&mov	(@T[1],$__s3);
+	&xor	(@T[2],$idx);		# $idx is preloaded with key[2]
+	&xor	(@T[3],&DWP(12,$key));
+	&xor	(@T[0],&DWP(0,$key));
+	&xor	(@T[1],&DWP(4,$key));
+	&ret	();
+&function_end_B("_x86_Camellia_decrypt");
+
+# shld is very slow on Intel P4 family. Even on AMD it limits
+# instruction decode rate [because it's VectorPath] and consequently
+# performance. PIII, PM and Core[2] seem to be the only ones which
+# execute this code ~7% faster...
+sub __rotl128 {
+  my ($i0,$i1,$i2,$i3,$rot,$rnd,@T)=@_;
+
+    $rnd *= 2;
+    if ($rot) {
+	&mov	($idx,$i0);
+	&shld	($i0,$i1,$rot);
+	&shld	($i1,$i2,$rot);
+	&shld	($i2,$i3,$rot);
+	&shld	($i3,$idx,$rot);
+    }
+    &mov	(&DWP(-128+4*$rnd++,$key),shift(@T))	if ($i0 eq @T[0]);
+    &mov	(&DWP(-128+4*$rnd++,$key),shift(@T))	if ($i1 eq @T[0]);
+    &mov	(&DWP(-128+4*$rnd++,$key),shift(@T))	if ($i2 eq @T[0]);
+    &mov	(&DWP(-128+4*$rnd++,$key),shift(@T))	if ($i3 eq @T[0]);
+}
+
+# ... Implementing 128-bit rotate without shld gives >3x performance
+# improvement on P4, only ~7% degradation on other Intel CPUs and
+# not worse performance on AMD. This is therefore preferred.
+sub _rotl128 {
+  my ($i0,$i1,$i2,$i3,$rot,$rnd,@T)=@_;
+
+    $rnd *= 2;
+    if ($rot) {
+	&mov	($Tbl,$i0);
+	&shl	($i0,$rot);
+	&mov	($idx,$i1);
+	&shr	($idx,32-$rot);
+	&shl	($i1,$rot);
+	&or	($i0,$idx);
+	&mov	($idx,$i2);
+	&shl	($i2,$rot);
+	&mov	(&DWP(-128+4*$rnd++,$key),shift(@T))	if ($i0 eq @T[0]);
+	&shr	($idx,32-$rot);
+	&or	($i1,$idx);
+	&shr	($Tbl,32-$rot);
+	&mov	($idx,$i3);
+	&shr	($idx,32-$rot);
+	&mov	(&DWP(-128+4*$rnd++,$key),shift(@T))	if ($i1 eq @T[0]);
+	&shl	($i3,$rot);
+	&or	($i2,$idx);
+	&or	($i3,$Tbl);
+	&mov	(&DWP(-128+4*$rnd++,$key),shift(@T))	if ($i2 eq @T[0]);
+	&mov	(&DWP(-128+4*$rnd++,$key),shift(@T))	if ($i3 eq @T[0]);
+    } else {
+	&mov	(&DWP(-128+4*$rnd++,$key),shift(@T))	if ($i0 eq @T[0]);
+	&mov	(&DWP(-128+4*$rnd++,$key),shift(@T))	if ($i1 eq @T[0]);
+	&mov	(&DWP(-128+4*$rnd++,$key),shift(@T))	if ($i2 eq @T[0]);
+	&mov	(&DWP(-128+4*$rnd++,$key),shift(@T))	if ($i3 eq @T[0]);
+    }
+}
+
+sub _saveround {
+my ($rnd,$key,@T)=@_;
+my $bias=int(@T[0])?shift(@T):0;
+
+	&mov	(&DWP($bias+$rnd*8+0,$key),@T[0]);
+	&mov	(&DWP($bias+$rnd*8+4,$key),@T[1])	if ($#T>=1);
+	&mov	(&DWP($bias+$rnd*8+8,$key),@T[2])	if ($#T>=2);
+	&mov	(&DWP($bias+$rnd*8+12,$key),@T[3])	if ($#T>=3);
+}
+
+sub _loadround {
+my ($rnd,$key,@T)=@_;
+my $bias=int(@T[0])?shift(@T):0;
+
+	&mov	(@T[0],&DWP($bias+$rnd*8+0,$key));
+	&mov	(@T[1],&DWP($bias+$rnd*8+4,$key))	if ($#T>=1);
+	&mov	(@T[2],&DWP($bias+$rnd*8+8,$key))	if ($#T>=2);
+	&mov	(@T[3],&DWP($bias+$rnd*8+12,$key))	if ($#T>=3);
+}
+
+# void Camellia_Ekeygen(
+#		const int keyBitLength,
+#		const Byte *rawKey,
+#		KEY_TABLE_TYPE keyTable)
+&function_begin("Camellia_Ekeygen");
+{ my $step=0;
+
+	&stack_push(4);				# place for s[0-3]
+
+	&mov	($Tbl,&wparam(0));		# load arguments
+	&mov	($idx,&wparam(1));
+	&mov	($key,&wparam(2));
+
+	&mov	(@T[0],&DWP(0,$idx));		# load 0-127 bits
+	&mov	(@T[1],&DWP(4,$idx));
+	&mov	(@T[2],&DWP(8,$idx));
+	&mov	(@T[3],&DWP(12,$idx));
+
+	&bswap	(@T[0]);
+	&bswap	(@T[1]);
+	&bswap	(@T[2]);
+	&bswap	(@T[3]);
+
+	&_saveround	(0,$key,@T);		# KL<<<0
+
+	&cmp	($Tbl,128);
+	&je	(&label("1st128"));
+
+	&mov	(@T[0],&DWP(16,$idx));		# load 128-191 bits
+	&mov	(@T[1],&DWP(20,$idx));
+	&cmp	($Tbl,192);
+	&je	(&label("1st192"));
+	&mov	(@T[2],&DWP(24,$idx));		# load 192-255 bits
+	&mov	(@T[3],&DWP(28,$idx));
+	&jmp	(&label("1st256"));
+&set_label("1st192",4);
+	&mov	(@T[2],@T[0]);
+	&mov	(@T[3],@T[1]);
+	¬	(@T[2]);
+	¬	(@T[3]);
+&set_label("1st256",4);
+	&bswap	(@T[0]);
+	&bswap	(@T[1]);
+	&bswap	(@T[2]);
+	&bswap	(@T[3]);
+
+	&_saveround	(4,$key,@T);		# temporary storage for KR!
+
+	&xor	(@T[0],&DWP(0*8+0,$key));	# KR^KL
+	&xor	(@T[1],&DWP(0*8+4,$key));
+	&xor	(@T[2],&DWP(1*8+0,$key));
+	&xor	(@T[3],&DWP(1*8+4,$key));
+
+&set_label("1st128",4);
+	&call	(&label("pic_point"));
+	&set_label("pic_point");
+	&blindpop($Tbl);
+	&lea	($Tbl,&DWP(&label("Camellia_SBOX")."-".&label("pic_point"),$Tbl));
+	&lea	($key,&DWP(&label("Camellia_SIGMA")."-".&label("Camellia_SBOX"),$Tbl));
+
+	&mov	($idx,&DWP($step*8,$key));	# prefetch SIGMA[0]
+	&mov	(&swtmp(0),@T[0]);		# save s[0-3]
+	&mov	(&swtmp(1),@T[1]);
+	&mov	(&swtmp(2),@T[2]);
+	&mov	(&swtmp(3),@T[3]);
+	&Camellia_Feistel($step++);
+	&Camellia_Feistel($step++);
+	&mov	(@T[2],&swtmp(2));
+	&mov	(@T[3],&swtmp(3));
+
+	&mov	($idx,&wparam(2));
+	&xor	(@T[0],&DWP(0*8+0,$idx));	# ^KL
+	&xor	(@T[1],&DWP(0*8+4,$idx));
+	&xor	(@T[2],&DWP(1*8+0,$idx));
+	&xor	(@T[3],&DWP(1*8+4,$idx));
+
+	&mov	($idx,&DWP($step*8,$key));	# prefetch SIGMA[4]
+	&mov	(&swtmp(0),@T[0]);		# save s[0-3]
+	&mov	(&swtmp(1),@T[1]);
+	&mov	(&swtmp(2),@T[2]);
+	&mov	(&swtmp(3),@T[3]);
+	&Camellia_Feistel($step++);
+	&Camellia_Feistel($step++);
+	&mov	(@T[2],&swtmp(2));
+	&mov	(@T[3],&swtmp(3));
+
+	&mov	($idx,&wparam(0));
+	&cmp	($idx,128);
+	&jne	(&label("2nd256"));
+
+	&mov	($key,&wparam(2));
+	&lea	($key,&DWP(128,$key));		# size optimization
+
+	####### process KA
+	&_saveround	(2,$key,-128,@T);	# KA<<<0
+	&_rotl128	(@T,15,6,@T);		# KA<<<15
+	&_rotl128	(@T,15,8,@T);		# KA<<<(15+15=30)
+	&_rotl128	(@T,15,12,@T[0],@T[1]);	# KA<<<(30+15=45)
+	&_rotl128	(@T,15,14,@T);		# KA<<<(45+15=60)
+	push		(@T,shift(@T));		# rotl128(@T,32);
+	&_rotl128	(@T,2,20,@T);		# KA<<<(60+32+2=94)
+	&_rotl128	(@T,17,24,@T);		# KA<<<(94+17=111)
+
+	####### process KL
+	&_loadround	(0,$key,-128,@T);	# load KL
+	&_rotl128	(@T,15,4,@T);		# KL<<<15
+	&_rotl128	(@T,30,10,@T);		# KL<<<(15+30=45)
+	&_rotl128	(@T,15,13,@T[2],@T[3]);	# KL<<<(45+15=60)
+	&_rotl128	(@T,17,16,@T);		# KL<<<(60+17=77)
+	&_rotl128	(@T,17,18,@T);		# KL<<<(77+17=94)
+	&_rotl128	(@T,17,22,@T);		# KL<<<(94+17=111)
+
+	while (@T[0] ne "eax")			# restore order
+	{   unshift	(@T,pop(@T));   }
+
+	&mov	("eax",3);			# 3 grandRounds
+	&jmp	(&label("done"));
+
+&set_label("2nd256",16);
+	&mov	($idx,&wparam(2));
+	&_saveround	(6,$idx,@T);		# temporary storage for KA!
+
+	&xor	(@T[0],&DWP(4*8+0,$idx));	# KA^KR
+	&xor	(@T[1],&DWP(4*8+4,$idx));
+	&xor	(@T[2],&DWP(5*8+0,$idx));
+	&xor	(@T[3],&DWP(5*8+4,$idx));
+
+	&mov	($idx,&DWP($step*8,$key));	# prefetch SIGMA[8]
+	&mov	(&swtmp(0),@T[0]);		# save s[0-3]
+	&mov	(&swtmp(1),@T[1]);
+	&mov	(&swtmp(2),@T[2]);
+	&mov	(&swtmp(3),@T[3]);
+	&Camellia_Feistel($step++);
+	&Camellia_Feistel($step++);
+	&mov	(@T[2],&swtmp(2));
+	&mov	(@T[3],&swtmp(3));
+
+	&mov	($key,&wparam(2));
+	&lea	($key,&DWP(128,$key));		# size optimization
+
+	####### process KB
+	&_saveround	(2,$key,-128,@T);	# KB<<<0
+	&_rotl128	(@T,30,10,@T);		# KB<<<30
+	&_rotl128	(@T,30,20,@T);		# KB<<<(30+30=60)
+	push		(@T,shift(@T));		# rotl128(@T,32);
+	&_rotl128	(@T,19,32,@T);		# KB<<<(60+32+19=111)
+
+	####### process KR
+	&_loadround	(4,$key,-128,@T);	# load KR
+	&_rotl128	(@T,15,4,@T);		# KR<<<15
+	&_rotl128	(@T,15,8,@T);		# KR<<<(15+15=30)
+	&_rotl128	(@T,30,18,@T);		# KR<<<(30+30=60)
+	push		(@T,shift(@T));		# rotl128(@T,32);
+	&_rotl128	(@T,2,26,@T);		# KR<<<(60+32+2=94)
+
+	####### process KA
+	&_loadround	(6,$key,-128,@T);	# load KA
+	&_rotl128	(@T,15,6,@T);		# KA<<<15
+	&_rotl128	(@T,30,14,@T);		# KA<<<(15+30=45)
+	push		(@T,shift(@T));		# rotl128(@T,32);
+	&_rotl128	(@T,0,24,@T);		# KA<<<(45+32+0=77)
+	&_rotl128	(@T,17,28,@T);		# KA<<<(77+17=94)
+
+	####### process KL
+	&_loadround	(0,$key,-128,@T);	# load KL
+	push		(@T,shift(@T));		# rotl128(@T,32);
+	&_rotl128	(@T,13,12,@T);		# KL<<<(32+13=45)
+	&_rotl128	(@T,15,16,@T);		# KL<<<(45+15=60)
+	&_rotl128	(@T,17,22,@T);		# KL<<<(60+17=77)
+	push		(@T,shift(@T));		# rotl128(@T,32);
+	&_rotl128	(@T,2,30,@T);		# KL<<<(77+32+2=111)
+
+	while (@T[0] ne "eax")			# restore order
+	{   unshift	(@T,pop(@T));   }
+
+	&mov	("eax",4);			# 4 grandRounds
+&set_label("done");
+	&lea	("edx",&DWP(272-128,$key));	# end of key schedule
+	&stack_pop(4);
+}
+&function_end("Camellia_Ekeygen");
+
+if ($OPENSSL) {
+# int Camellia_set_key (
+#		const unsigned char *userKey,
+#		int bits,
+#		CAMELLIA_KEY *key)
+&function_begin_B("Camellia_set_key");
+	&push	("ebx");
+	&mov	("ecx",&wparam(0));	# pull arguments
+	&mov	("ebx",&wparam(1));
+	&mov	("edx",&wparam(2));
+
+	&mov	("eax",-1);
+	&test	("ecx","ecx");
+	&jz	(&label("done"));	# userKey==NULL?
+	&test	("edx","edx");
+	&jz	(&label("done"));	# key==NULL?
+
+	&mov	("eax",-2);
+	&cmp	("ebx",256);
+	&je	(&label("arg_ok"));	# bits==256?
+	&cmp	("ebx",192);
+	&je	(&label("arg_ok"));	# bits==192?
+	&cmp	("ebx",128);
+	&jne	(&label("done"));	# bits!=128?
+&set_label("arg_ok",4);
+
+	&push	("edx");		# push arguments
+	&push	("ecx");
+	&push	("ebx");
+	&call	("Camellia_Ekeygen");
+	&stack_pop(3);
+
+	# eax holds grandRounds and edx points at where to put it
+	&mov	(&DWP(0,"edx"),"eax");
+	&xor	("eax","eax");
+&set_label("done",4);
+	&pop	("ebx");
+	&ret	();
+&function_end_B("Camellia_set_key");
+}
+
+@SBOX=(
+112,130, 44,236,179, 39,192,229,228,133, 87, 53,234, 12,174, 65,
+ 35,239,107,147, 69, 25,165, 33,237, 14, 79, 78, 29,101,146,189,
+134,184,175,143,124,235, 31,206, 62, 48,220, 95, 94,197, 11, 26,
+166,225, 57,202,213, 71, 93, 61,217,  1, 90,214, 81, 86,108, 77,
+139, 13,154,102,251,204,176, 45,116, 18, 43, 32,240,177,132,153,
+223, 76,203,194, 52,126,118,  5,109,183,169, 49,209, 23,  4,215,
+ 20, 88, 58, 97,222, 27, 17, 28, 50, 15,156, 22, 83, 24,242, 34,
+254, 68,207,178,195,181,122,145, 36,  8,232,168, 96,252,105, 80,
+170,208,160,125,161,137, 98,151, 84, 91, 30,149,224,255,100,210,
+ 16,196,  0, 72,163,247,117,219,138,  3,230,218,  9, 63,221,148,
+135, 92,131,  2,205, 74,144, 51,115,103,246,243,157,127,191,226,
+ 82,155,216, 38,200, 55,198, 59,129,150,111, 75, 19,190, 99, 46,
+233,121,167,140,159,110,188,142, 41,245,249,182, 47,253,180, 89,
+120,152,  6,106,231, 70,113,186,212, 37,171, 66,136,162,141,250,
+114,  7,185, 85,248,238,172, 10, 54, 73, 42,104, 60, 56,241,164,
+ 64, 40,211,123,187,201, 67,193, 21,227,173,244,119,199,128,158);
+
+sub S1110 { my $i=shift; $i=@SBOX[$i]; return $i<<24|$i<<16|$i<<8; }
+sub S4404 { my $i=shift; $i=($i<<1|$i>>7)&0xff; $i=@SBOX[$i]; return $i<<24|$i<<16|$i; }	
+sub S0222 { my $i=shift; $i=@SBOX[$i]; $i=($i<<1|$i>>7)&0xff; return $i<<16|$i<<8|$i; }	
+sub S3033 { my $i=shift; $i=@SBOX[$i]; $i=($i>>1|$i<<7)&0xff; return $i<<24|$i<<8|$i; }	
+
+&set_label("Camellia_SIGMA",64);
+&data_word(
+    0xa09e667f, 0x3bcc908b, 0xb67ae858, 0x4caa73b2,
+    0xc6ef372f, 0xe94f82be, 0x54ff53a5, 0xf1d36f1c,
+    0x10e527fa, 0xde682d1d, 0xb05688c2, 0xb3e6c1fd,
+    0,          0,          0,          0);
+&set_label("Camellia_SBOX",64);
+# tables are interleaved, remember?
+for ($i=0;$i<256;$i++) { &data_word(&S1110($i),&S4404($i)); }
+for ($i=0;$i<256;$i++) { &data_word(&S0222($i),&S3033($i)); }
+
+# void Camellia_cbc_encrypt (const void char *inp, unsigned char *out,
+#			size_t length, const CAMELLIA_KEY *key,
+#			unsigned char *ivp,const int enc);
+{
+# stack frame layout
+#             -4(%esp)		# return address	 0(%esp)
+#              0(%esp)		# s0			 4(%esp)
+#              4(%esp)		# s1			 8(%esp)
+#              8(%esp)		# s2			12(%esp)
+#             12(%esp)		# s3			16(%esp)
+#             16(%esp)		# end of key schedule	20(%esp)
+#             20(%esp)		# %esp backup
+my $_inp=&DWP(24,"esp");	#copy of wparam(0)
+my $_out=&DWP(28,"esp");	#copy of wparam(1)
+my $_len=&DWP(32,"esp");	#copy of wparam(2)
+my $_key=&DWP(36,"esp");	#copy of wparam(3)
+my $_ivp=&DWP(40,"esp");	#copy of wparam(4)
+my $ivec=&DWP(44,"esp");	#ivec[16]
+my $_tmp=&DWP(44,"esp");	#volatile variable [yes, aliases with ivec]
+my ($s0,$s1,$s2,$s3) = @T;
+
+&function_begin("Camellia_cbc_encrypt");
+	&mov	($s2 eq "ecx"? $s2 : "",&wparam(2));	# load len
+	&cmp	($s2,0);
+	&je	(&label("enc_out"));
+
+	&pushf	();
+	&cld	();
+
+	&mov	($s0,&wparam(0));	# load inp
+	&mov	($s1,&wparam(1));	# load out
+	#&mov	($s2,&wparam(2));	# load len
+	&mov	($s3,&wparam(3));	# load key
+	&mov	($Tbl,&wparam(4));	# load ivp
+
+	# allocate aligned stack frame...
+	&lea	($idx,&DWP(-64,"esp"));
+	&and	($idx,-64);
+
+	# place stack frame just "above mod 1024" the key schedule
+	# this ensures that cache associativity of 2 suffices
+	&lea	($key,&DWP(-64-63,$s3));
+	&sub	($key,$idx);
+	&neg	($key);
+	&and	($key,0x3C0);	# modulo 1024, but aligned to cache-line
+	&sub	($idx,$key);
+
+	&mov	($key,&wparam(5));	# load enc
+
+	&exch	("esp",$idx);
+	&add	("esp",4);		# reserve for return address!
+	&mov	($_esp,$idx);		# save %esp
+
+	&mov	($_inp,$s0);		# save copy of inp
+	&mov	($_out,$s1);		# save copy of out
+	&mov	($_len,$s2);		# save copy of len
+	&mov	($_key,$s3);		# save copy of key
+	&mov	($_ivp,$Tbl);		# save copy of ivp
+
+	&call   (&label("pic_point"));	# make it PIC!
+	&set_label("pic_point");
+	&blindpop($Tbl);
+	&lea    ($Tbl,&DWP(&label("Camellia_SBOX")."-".&label("pic_point"),$Tbl));
+
+	&mov	($idx,32);
+	&set_label("prefetch_sbox",4);
+		&mov	($s0,&DWP(0,$Tbl));
+		&mov	($s1,&DWP(32,$Tbl));
+		&mov	($s2,&DWP(64,$Tbl));
+		&mov	($s3,&DWP(96,$Tbl));
+		&lea	($Tbl,&DWP(128,$Tbl));
+		&dec	($idx);
+	&jnz	(&label("prefetch_sbox"));
+	&mov	($s0,$_key);
+	&sub	($Tbl,4096);
+	&mov	($idx,$_inp);
+	&mov	($s3,&DWP(272,$s0));		# load grandRounds
+
+	&cmp	($key,0);
+	&je	(&label("DECRYPT"));
+
+	&mov	($s2,$_len);
+	&mov	($key,$_ivp);
+	&shl	($s3,6);
+	&lea	($s3,&DWP(0,$s0,$s3));
+	&mov	($_end,$s3);
+
+	&test	($s2,0xFFFFFFF0);
+	&jz	(&label("enc_tail"));		# short input...
+
+	&mov	($s0,&DWP(0,$key));		# load iv
+	&mov	($s1,&DWP(4,$key));
+
+	&set_label("enc_loop",4);
+		&mov	($s2,&DWP(8,$key));
+		&mov	($s3,&DWP(12,$key));
+
+		&xor	($s0,&DWP(0,$idx));	# xor input data
+		&xor	($s1,&DWP(4,$idx));
+		&xor	($s2,&DWP(8,$idx));
+		&bswap	($s0);
+		&xor	($s3,&DWP(12,$idx));
+		&bswap	($s1);
+		&mov	($key,$_key);		# load key
+		&bswap	($s2);
+		&bswap	($s3);
+
+		&call	("_x86_Camellia_encrypt");
+
+		&mov	($idx,$_inp);		# load inp
+		&mov	($key,$_out);		# load out
+
+		&bswap	($s0);
+		&bswap	($s1);
+		&bswap	($s2);
+		&mov	(&DWP(0,$key),$s0);	# save output data
+		&bswap	($s3);
+		&mov	(&DWP(4,$key),$s1);
+		&mov	(&DWP(8,$key),$s2);
+		&mov	(&DWP(12,$key),$s3);
+
+		&mov	($s2,$_len);		# load len
+
+		&lea	($idx,&DWP(16,$idx));
+		&mov	($_inp,$idx);		# save inp
+
+		&lea	($s3,&DWP(16,$key));
+		&mov	($_out,$s3);		# save out
+
+		&sub	($s2,16);
+		&test	($s2,0xFFFFFFF0);
+		&mov	($_len,$s2);		# save len
+	&jnz	(&label("enc_loop"));
+	&test	($s2,15);
+	&jnz	(&label("enc_tail"));
+	&mov	($idx,$_ivp);		# load ivp
+	&mov	($s2,&DWP(8,$key));	# restore last dwords
+	&mov	($s3,&DWP(12,$key));
+	&mov	(&DWP(0,$idx),$s0);	# save ivec
+	&mov	(&DWP(4,$idx),$s1);
+	&mov	(&DWP(8,$idx),$s2);
+	&mov	(&DWP(12,$idx),$s3);
+
+	&mov	("esp",$_esp);
+	&popf	();
+    &set_label("enc_out");
+	&function_end_A();
+	&pushf	();			# kludge, never executed
+
+    &set_label("enc_tail",4);
+	&mov	($s0,$key eq "edi" ? $key : "");
+	&mov	($key,$_out);			# load out
+	&push	($s0);				# push ivp
+	&mov	($s1,16);
+	&sub	($s1,$s2);
+	&cmp	($key,$idx);			# compare with inp
+	&je	(&label("enc_in_place"));
+	&align	(4);
+	&data_word(0xA4F3F689);	# rep movsb	# copy input
+	&jmp	(&label("enc_skip_in_place"));
+    &set_label("enc_in_place");
+	&lea	($key,&DWP(0,$key,$s2));
+    &set_label("enc_skip_in_place");
+	&mov	($s2,$s1);
+	&xor	($s0,$s0);
+	&align	(4);
+	&data_word(0xAAF3F689);	# rep stosb	# zero tail
+	&pop	($key);				# pop ivp
+
+	&mov	($idx,$_out);			# output as input
+	&mov	($s0,&DWP(0,$key));
+	&mov	($s1,&DWP(4,$key));
+	&mov	($_len,16);			# len=16
+	&jmp	(&label("enc_loop"));		# one more spin...
+
+#----------------------------- DECRYPT -----------------------------#
+&set_label("DECRYPT",16);
+	&shl	($s3,6);
+	&lea	($s3,&DWP(0,$s0,$s3));
+	&mov	($_end,$s0);
+	&mov	($_key,$s3);
+
+	&cmp	($idx,$_out);
+	&je	(&label("dec_in_place"));	# in-place processing...
+
+	&mov	($key,$_ivp);			# load ivp
+	&mov	($_tmp,$key);
+
+	&set_label("dec_loop",4);
+		&mov	($s0,&DWP(0,$idx));	# read input
+		&mov	($s1,&DWP(4,$idx));
+		&mov	($s2,&DWP(8,$idx));
+		&bswap	($s0);
+		&mov	($s3,&DWP(12,$idx));
+		&bswap	($s1);
+		&mov	($key,$_key);		# load key
+		&bswap	($s2);
+		&bswap	($s3);
+
+		&call	("_x86_Camellia_decrypt");
+
+		&mov	($key,$_tmp);		# load ivp
+		&mov	($idx,$_len);		# load len
+
+		&bswap	($s0);
+		&bswap	($s1);
+		&bswap	($s2);
+		&xor	($s0,&DWP(0,$key));	# xor iv
+		&bswap	($s3);
+		&xor	($s1,&DWP(4,$key));
+		&xor	($s2,&DWP(8,$key));
+		&xor	($s3,&DWP(12,$key));
+
+		&sub	($idx,16);
+		&jc	(&label("dec_partial"));
+		&mov	($_len,$idx);		# save len
+		&mov	($idx,$_inp);		# load inp
+		&mov	($key,$_out);		# load out
+
+		&mov	(&DWP(0,$key),$s0);	# write output
+		&mov	(&DWP(4,$key),$s1);
+		&mov	(&DWP(8,$key),$s2);
+		&mov	(&DWP(12,$key),$s3);
+
+		&mov	($_tmp,$idx);		# save ivp
+		&lea	($idx,&DWP(16,$idx));
+		&mov	($_inp,$idx);		# save inp
+
+		&lea	($key,&DWP(16,$key));
+		&mov	($_out,$key);		# save out
+
+	&jnz	(&label("dec_loop"));
+	&mov	($key,$_tmp);		# load temp ivp
+    &set_label("dec_end");
+	&mov	($idx,$_ivp);		# load user ivp
+	&mov	($s0,&DWP(0,$key));	# load iv
+	&mov	($s1,&DWP(4,$key));
+	&mov	($s2,&DWP(8,$key));
+	&mov	($s3,&DWP(12,$key));
+	&mov	(&DWP(0,$idx),$s0);	# copy back to user
+	&mov	(&DWP(4,$idx),$s1);
+	&mov	(&DWP(8,$idx),$s2);
+	&mov	(&DWP(12,$idx),$s3);
+	&jmp	(&label("dec_out"));
+
+    &set_label("dec_partial",4);
+	&lea	($key,$ivec);
+	&mov	(&DWP(0,$key),$s0);	# dump output to stack
+	&mov	(&DWP(4,$key),$s1);
+	&mov	(&DWP(8,$key),$s2);
+	&mov	(&DWP(12,$key),$s3);
+	&lea	($s2 eq "ecx" ? $s2 : "",&DWP(16,$idx));
+	&mov	($idx eq "esi" ? $idx : "",$key);
+	&mov	($key eq "edi" ? $key : "",$_out);	# load out
+	&data_word(0xA4F3F689);	# rep movsb		# copy output
+	&mov	($key,$_inp);				# use inp as temp ivp
+	&jmp	(&label("dec_end"));
+
+    &set_label("dec_in_place",4);
+	&set_label("dec_in_place_loop");
+		&lea	($key,$ivec);
+		&mov	($s0,&DWP(0,$idx));	# read input
+		&mov	($s1,&DWP(4,$idx));
+		&mov	($s2,&DWP(8,$idx));
+		&mov	($s3,&DWP(12,$idx));
+
+		&mov	(&DWP(0,$key),$s0);	# copy to temp
+		&mov	(&DWP(4,$key),$s1);
+		&mov	(&DWP(8,$key),$s2);
+		&bswap	($s0);
+		&mov	(&DWP(12,$key),$s3);
+		&bswap	($s1);
+		&mov	($key,$_key);		# load key
+		&bswap	($s2);
+		&bswap	($s3);
+
+		&call	("_x86_Camellia_decrypt");
+
+		&mov	($key,$_ivp);		# load ivp
+		&mov	($idx,$_out);		# load out
+
+		&bswap	($s0);
+		&bswap	($s1);
+		&bswap	($s2);
+		&xor	($s0,&DWP(0,$key));	# xor iv
+		&bswap	($s3);
+		&xor	($s1,&DWP(4,$key));
+		&xor	($s2,&DWP(8,$key));
+		&xor	($s3,&DWP(12,$key));
+
+		&mov	(&DWP(0,$idx),$s0);	# write output
+		&mov	(&DWP(4,$idx),$s1);
+		&mov	(&DWP(8,$idx),$s2);
+		&mov	(&DWP(12,$idx),$s3);
+
+		&lea	($idx,&DWP(16,$idx));
+		&mov	($_out,$idx);		# save out
+
+		&lea	($idx,$ivec);
+		&mov	($s0,&DWP(0,$idx));	# read temp
+		&mov	($s1,&DWP(4,$idx));
+		&mov	($s2,&DWP(8,$idx));
+		&mov	($s3,&DWP(12,$idx));
+
+		&mov	(&DWP(0,$key),$s0);	# copy iv
+		&mov	(&DWP(4,$key),$s1);
+		&mov	(&DWP(8,$key),$s2);
+		&mov	(&DWP(12,$key),$s3);
+
+		&mov	($idx,$_inp);		# load inp
+
+		&lea	($idx,&DWP(16,$idx));
+		&mov	($_inp,$idx);		# save inp
+
+		&mov	($s2,$_len);		# load len
+		&sub	($s2,16);
+		&jc	(&label("dec_in_place_partial"));
+		&mov	($_len,$s2);		# save len
+	&jnz	(&label("dec_in_place_loop"));
+	&jmp	(&label("dec_out"));
+
+    &set_label("dec_in_place_partial",4);
+	# one can argue if this is actually required...
+	&mov	($key eq "edi" ? $key : "",$_out);
+	&lea	($idx eq "esi" ? $idx : "",$ivec);
+	&lea	($key,&DWP(0,$key,$s2));
+	&lea	($idx,&DWP(16,$idx,$s2));
+	&neg	($s2 eq "ecx" ? $s2 : "");
+	&data_word(0xA4F3F689);	# rep movsb	# restore tail
+
+    &set_label("dec_out",4);
+    &mov	("esp",$_esp);
+    &popf	();
+&function_end("Camellia_cbc_encrypt");
+}
+
+&asciz("Camellia for x86 by ");
+
+&asm_finish();
+
+close STDOUT;
diff --git a/crypto/camellia/asm/cmll-x86_64.pl b/crypto/camellia/asm/cmll-x86_64.pl
new file mode 100644
index 000000000..da5ad7b7e
--- /dev/null
+++ b/crypto/camellia/asm/cmll-x86_64.pl
@@ -0,0 +1,1088 @@
+#! /usr/bin/env perl
+# Copyright 2008-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+
+# ====================================================================
+# Copyright (c) 2008 Andy Polyakov 
+#
+# This module may be used under the terms of either the GNU General
+# Public License version 2 or later, the GNU Lesser General Public
+# License version 2.1 or later, the Mozilla Public License version
+# 1.1 or the BSD License. The exact terms of either license are
+# distributed along with this module. For further details see
+# http://www.openssl.org/~appro/camellia/.
+# ====================================================================
+
+# Performance in cycles per processed byte (less is better) in
+# 'openssl speed ...' benchmark:
+#
+#			AMD64	Core2	EM64T
+# -evp camellia-128-ecb	16.7	21.0	22.7
+# + over gcc 3.4.6	+25%	+5%	0%
+#
+# camellia-128-cbc	15.7	20.4	21.1
+#
+# 128-bit key setup	128	216	205	cycles/key
+# + over gcc 3.4.6	+54%	+39%	+15%
+#
+# Numbers in "+" rows represent performance improvement over compiler
+# generated code. Key setup timings are impressive on AMD and Core2
+# thanks to 64-bit operations being covertly deployed. Improvement on
+# EM64T, pre-Core2 Intel x86_64 CPU, is not as impressive, because it
+# apparently emulates some of 64-bit operations in [32-bit] microcode.
+
+$flavour = shift;
+$output  = shift;
+if ($flavour =~ /\./) { $output = $flavour; undef $flavour; }
+
+$win64=0; $win64=1 if ($flavour =~ /[nm]asm|mingw64/ || $output =~ /\.asm$/);
+
+$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+( $xlate="${dir}x86_64-xlate.pl" and -f $xlate ) or
+( $xlate="${dir}../../perlasm/x86_64-xlate.pl" and -f $xlate) or
+die "can't locate x86_64-xlate.pl";
+
+open OUT,"| \"$^X\" \"$xlate\" $flavour \"$output\"";
+*STDOUT=*OUT;
+
+sub hi() { my $r=shift; $r =~ s/%[er]([a-d])x/%\1h/;    $r; }
+sub lo() { my $r=shift; $r =~ s/%[er]([a-d])x/%\1l/;
+                        $r =~ s/%[er]([sd]i)/%\1l/;
+                        $r =~ s/%(r[0-9]+)[d]?/%\1b/;   $r; }
+
+$t0="%eax";$t1="%ebx";$t2="%ecx";$t3="%edx";
+@S=("%r8d","%r9d","%r10d","%r11d");
+$i0="%esi";
+$i1="%edi";
+$Tbl="%rbp";	# size optimization
+$inp="%r12";
+$out="%r13";
+$key="%r14";
+$keyend="%r15";
+$arg0d=$win64?"%ecx":"%edi";
+
+# const unsigned int Camellia_SBOX[4][256];
+# Well, sort of... Camellia_SBOX[0][] is interleaved with [1][],
+# and [2][] - with [3][]. This is done to minimize code size.
+$SBOX1_1110=0;		# Camellia_SBOX[0]
+$SBOX4_4404=4;		# Camellia_SBOX[1]
+$SBOX2_0222=2048;	# Camellia_SBOX[2]
+$SBOX3_3033=2052;	# Camellia_SBOX[3]
+
+sub Camellia_Feistel {
+my $i=@_[0];
+my $seed=defined(@_[1])?@_[1]:0;
+my $scale=$seed<0?-8:8;
+my $j=($i&1)*2;
+my ($s0,$s1,$s2,$s3)=(@S[($j)%4],@S[($j+1)%4],@S[($j+2)%4],@S[($j+3)%4]);
+
+$code.=<<___;
+	xor	$s0,$t0				# t0^=key[0]
+	xor	$s1,$t1				# t1^=key[1]
+	movz	`&hi("$t0")`,$i0		# (t0>>8)&0xff
+	movz	`&lo("$t1")`,$i1		# (t1>>0)&0xff
+	mov	$SBOX3_3033($Tbl,$i0,8),$t3	# t3=SBOX3_3033[0]
+	mov	$SBOX1_1110($Tbl,$i1,8),$t2	# t2=SBOX1_1110[1]
+	movz	`&lo("$t0")`,$i0		# (t0>>0)&0xff
+	shr	\$16,$t0
+	movz	`&hi("$t1")`,$i1		# (t1>>8)&0xff
+	xor	$SBOX4_4404($Tbl,$i0,8),$t3	# t3^=SBOX4_4404[0]
+	shr	\$16,$t1
+	xor	$SBOX4_4404($Tbl,$i1,8),$t2	# t2^=SBOX4_4404[1]
+	movz	`&hi("$t0")`,$i0		# (t0>>24)&0xff
+	movz	`&lo("$t1")`,$i1		# (t1>>16)&0xff
+	xor	$SBOX1_1110($Tbl,$i0,8),$t3	# t3^=SBOX1_1110[0]
+	xor	$SBOX3_3033($Tbl,$i1,8),$t2	# t2^=SBOX3_3033[1]
+	movz	`&lo("$t0")`,$i0		# (t0>>16)&0xff
+	movz	`&hi("$t1")`,$i1		# (t1>>24)&0xff
+	xor	$SBOX2_0222($Tbl,$i0,8),$t3	# t3^=SBOX2_0222[0]
+	xor	$SBOX2_0222($Tbl,$i1,8),$t2	# t2^=SBOX2_0222[1]
+	mov	`$seed+($i+1)*$scale`($key),$t1	# prefetch key[i+1]
+	mov	`$seed+($i+1)*$scale+4`($key),$t0
+	xor	$t3,$t2				# t2^=t3
+	ror	\$8,$t3				# t3=RightRotate(t3,8)
+	xor	$t2,$s2
+	xor	$t2,$s3
+	xor	$t3,$s3
+___
+}
+
+# void Camellia_EncryptBlock_Rounds(
+#		int grandRounds,
+#		const Byte plaintext[],
+#		const KEY_TABLE_TYPE keyTable,
+#		Byte ciphertext[])
+$code=<<___;
+.text
+
+# V1.x API
+.globl	Camellia_EncryptBlock
+.type	Camellia_EncryptBlock,\@abi-omnipotent
+.align	16
+Camellia_EncryptBlock:
+	movl	\$128,%eax
+	subl	$arg0d,%eax
+	movl	\$3,$arg0d
+	adcl	\$0,$arg0d	# keyBitLength==128?3:4
+	jmp	.Lenc_rounds
+.size	Camellia_EncryptBlock,.-Camellia_EncryptBlock
+# V2
+.globl	Camellia_EncryptBlock_Rounds
+.type	Camellia_EncryptBlock_Rounds,\@function,4
+.align	16
+.Lenc_rounds:
+Camellia_EncryptBlock_Rounds:
+	push	%rbx
+	push	%rbp
+	push	%r13
+	push	%r14
+	push	%r15
+.Lenc_prologue:
+
+	#mov	%rsi,$inp		# put away arguments
+	mov	%rcx,$out
+	mov	%rdx,$key
+
+	shl	\$6,%edi		# process grandRounds
+	lea	.LCamellia_SBOX(%rip),$Tbl
+	lea	($key,%rdi),$keyend
+
+	mov	0(%rsi),@S[0]		# load plaintext
+	mov	4(%rsi),@S[1]
+	mov	8(%rsi),@S[2]
+	bswap	@S[0]
+	mov	12(%rsi),@S[3]
+	bswap	@S[1]
+	bswap	@S[2]
+	bswap	@S[3]
+
+	call	_x86_64_Camellia_encrypt
+
+	bswap	@S[0]
+	bswap	@S[1]
+	bswap	@S[2]
+	mov	@S[0],0($out)
+	bswap	@S[3]
+	mov	@S[1],4($out)
+	mov	@S[2],8($out)
+	mov	@S[3],12($out)
+
+	mov	0(%rsp),%r15
+	mov	8(%rsp),%r14
+	mov	16(%rsp),%r13
+	mov	24(%rsp),%rbp
+	mov	32(%rsp),%rbx
+	lea	40(%rsp),%rsp
+.Lenc_epilogue:
+	ret
+.size	Camellia_EncryptBlock_Rounds,.-Camellia_EncryptBlock_Rounds
+
+.type	_x86_64_Camellia_encrypt,\@abi-omnipotent
+.align	16
+_x86_64_Camellia_encrypt:
+	xor	0($key),@S[1]
+	xor	4($key),@S[0]		# ^=key[0-3]
+	xor	8($key),@S[3]
+	xor	12($key),@S[2]
+.align	16
+.Leloop:
+	mov	16($key),$t1		# prefetch key[4-5]
+	mov	20($key),$t0
+
+___
+	for ($i=0;$i<6;$i++) { Camellia_Feistel($i,16); }
+$code.=<<___;
+	lea	16*4($key),$key
+	cmp	$keyend,$key
+	mov	8($key),$t3		# prefetch key[2-3]
+	mov	12($key),$t2
+	je	.Ledone
+
+	and	@S[0],$t0
+	or	@S[3],$t3
+	rol	\$1,$t0
+	xor	$t3,@S[2]		# s2^=s3|key[3];
+	xor	$t0,@S[1]		# s1^=LeftRotate(s0&key[0],1);
+	and	@S[2],$t2
+	or	@S[1],$t1
+	rol	\$1,$t2
+	xor	$t1,@S[0]		# s0^=s1|key[1];
+	xor	$t2,@S[3]		# s3^=LeftRotate(s2&key[2],1);
+	jmp	.Leloop
+
+.align	16
+.Ledone:
+	xor	@S[2],$t0		# SwapHalf
+	xor	@S[3],$t1
+	xor	@S[0],$t2
+	xor	@S[1],$t3
+
+	mov	$t0,@S[0]
+	mov	$t1,@S[1]
+	mov	$t2,@S[2]
+	mov	$t3,@S[3]
+
+	.byte	0xf3,0xc3		# rep ret
+.size	_x86_64_Camellia_encrypt,.-_x86_64_Camellia_encrypt
+
+# V1.x API
+.globl	Camellia_DecryptBlock
+.type	Camellia_DecryptBlock,\@abi-omnipotent
+.align	16
+Camellia_DecryptBlock:
+	movl	\$128,%eax
+	subl	$arg0d,%eax
+	movl	\$3,$arg0d
+	adcl	\$0,$arg0d	# keyBitLength==128?3:4
+	jmp	.Ldec_rounds
+.size	Camellia_DecryptBlock,.-Camellia_DecryptBlock
+# V2
+.globl	Camellia_DecryptBlock_Rounds
+.type	Camellia_DecryptBlock_Rounds,\@function,4
+.align	16
+.Ldec_rounds:
+Camellia_DecryptBlock_Rounds:
+	push	%rbx
+	push	%rbp
+	push	%r13
+	push	%r14
+	push	%r15
+.Ldec_prologue:
+
+	#mov	%rsi,$inp		# put away arguments
+	mov	%rcx,$out
+	mov	%rdx,$keyend
+
+	shl	\$6,%edi		# process grandRounds
+	lea	.LCamellia_SBOX(%rip),$Tbl
+	lea	($keyend,%rdi),$key
+
+	mov	0(%rsi),@S[0]		# load plaintext
+	mov	4(%rsi),@S[1]
+	mov	8(%rsi),@S[2]
+	bswap	@S[0]
+	mov	12(%rsi),@S[3]
+	bswap	@S[1]
+	bswap	@S[2]
+	bswap	@S[3]
+
+	call	_x86_64_Camellia_decrypt
+
+	bswap	@S[0]
+	bswap	@S[1]
+	bswap	@S[2]
+	mov	@S[0],0($out)
+	bswap	@S[3]
+	mov	@S[1],4($out)
+	mov	@S[2],8($out)
+	mov	@S[3],12($out)
+
+	mov	0(%rsp),%r15
+	mov	8(%rsp),%r14
+	mov	16(%rsp),%r13
+	mov	24(%rsp),%rbp
+	mov	32(%rsp),%rbx
+	lea	40(%rsp),%rsp
+.Ldec_epilogue:
+	ret
+.size	Camellia_DecryptBlock_Rounds,.-Camellia_DecryptBlock_Rounds
+
+.type	_x86_64_Camellia_decrypt,\@abi-omnipotent
+.align	16
+_x86_64_Camellia_decrypt:
+	xor	0($key),@S[1]
+	xor	4($key),@S[0]		# ^=key[0-3]
+	xor	8($key),@S[3]
+	xor	12($key),@S[2]
+.align	16
+.Ldloop:
+	mov	-8($key),$t1		# prefetch key[4-5]
+	mov	-4($key),$t0
+
+___
+	for ($i=0;$i<6;$i++) { Camellia_Feistel($i,-8); }
+$code.=<<___;
+	lea	-16*4($key),$key
+	cmp	$keyend,$key
+	mov	0($key),$t3		# prefetch key[2-3]
+	mov	4($key),$t2
+	je	.Lddone
+
+	and	@S[0],$t0
+	or	@S[3],$t3
+	rol	\$1,$t0
+	xor	$t3,@S[2]		# s2^=s3|key[3];
+	xor	$t0,@S[1]		# s1^=LeftRotate(s0&key[0],1);
+	and	@S[2],$t2
+	or	@S[1],$t1
+	rol	\$1,$t2
+	xor	$t1,@S[0]		# s0^=s1|key[1];
+	xor	$t2,@S[3]		# s3^=LeftRotate(s2&key[2],1);
+
+	jmp	.Ldloop
+
+.align	16
+.Lddone:
+	xor	@S[2],$t2
+	xor	@S[3],$t3
+	xor	@S[0],$t0
+	xor	@S[1],$t1
+
+	mov	$t2,@S[0]		# SwapHalf
+	mov	$t3,@S[1]
+	mov	$t0,@S[2]
+	mov	$t1,@S[3]
+
+	.byte	0xf3,0xc3		# rep ret
+.size	_x86_64_Camellia_decrypt,.-_x86_64_Camellia_decrypt
+___
+
+sub _saveround {
+my ($rnd,$key,@T)=@_;
+my $bias=int(@T[0])?shift(@T):0;
+
+    if ($#T==3) {
+	$code.=<<___;
+	mov	@T[1],`$bias+$rnd*8+0`($key)
+	mov	@T[0],`$bias+$rnd*8+4`($key)
+	mov	@T[3],`$bias+$rnd*8+8`($key)
+	mov	@T[2],`$bias+$rnd*8+12`($key)
+___
+    } else {
+	$code.="	mov	@T[0],`$bias+$rnd*8+0`($key)\n";
+	$code.="	mov	@T[1],`$bias+$rnd*8+8`($key)\n"	if ($#T>=1);
+    }
+}
+
+sub _loadround {
+my ($rnd,$key,@T)=@_;
+my $bias=int(@T[0])?shift(@T):0;
+
+$code.="	mov	`$bias+$rnd*8+0`($key),@T[0]\n";
+$code.="	mov	`$bias+$rnd*8+8`($key),@T[1]\n"	if ($#T>=1);
+}
+
+# shld is very slow on Intel EM64T family. Even on AMD it limits
+# instruction decode rate [because it's VectorPath] and consequently
+# performance...
+sub __rotl128 {
+my ($i0,$i1,$rot)=@_;
+
+    if ($rot) {
+	$code.=<<___;
+	mov	$i0,%r11
+	shld	\$$rot,$i1,$i0
+	shld	\$$rot,%r11,$i1
+___
+    }
+}
+
+# ... Implementing 128-bit rotate without shld gives 80% better
+# performance EM64T, +15% on AMD64 and only ~7% degradation on
+# Core2. This is therefore preferred.
+sub _rotl128 {
+my ($i0,$i1,$rot)=@_;
+
+    if ($rot) {
+	$code.=<<___;
+	mov	$i0,%r11
+	shl	\$$rot,$i0
+	mov	$i1,%r9
+	shr	\$`64-$rot`,%r9
+	shr	\$`64-$rot`,%r11
+	or	%r9,$i0
+	shl	\$$rot,$i1
+	or	%r11,$i1
+___
+    }
+}
+
+{ my $step=0;
+
+$code.=<<___;
+.globl	Camellia_Ekeygen
+.type	Camellia_Ekeygen,\@function,3
+.align	16
+Camellia_Ekeygen:
+	push	%rbx
+	push	%rbp
+	push	%r13
+	push	%r14
+	push	%r15
+.Lkey_prologue:
+
+	mov	%edi,${keyend}d		# put away arguments, keyBitLength
+	mov	%rdx,$out		# keyTable
+
+	mov	0(%rsi),@S[0]		# load 0-127 bits
+	mov	4(%rsi),@S[1]
+	mov	8(%rsi),@S[2]
+	mov	12(%rsi),@S[3]
+
+	bswap	@S[0]
+	bswap	@S[1]
+	bswap	@S[2]
+	bswap	@S[3]
+___
+	&_saveround	(0,$out,@S);	# KL<<<0
+$code.=<<___;
+	cmp	\$128,$keyend		# check keyBitLength
+	je	.L1st128
+
+	mov	16(%rsi),@S[0]		# load 128-191 bits
+	mov	20(%rsi),@S[1]
+	cmp	\$192,$keyend
+	je	.L1st192
+	mov	24(%rsi),@S[2]		# load 192-255 bits
+	mov	28(%rsi),@S[3]
+	jmp	.L1st256
+.L1st192:
+	mov	@S[0],@S[2]
+	mov	@S[1],@S[3]
+	not	@S[2]
+	not	@S[3]
+.L1st256:
+	bswap	@S[0]
+	bswap	@S[1]
+	bswap	@S[2]
+	bswap	@S[3]
+___
+	&_saveround	(4,$out,@S);	# temp storage for KR!
+$code.=<<___;
+	xor	0($out),@S[1]		# KR^KL
+	xor	4($out),@S[0]
+	xor	8($out),@S[3]
+	xor	12($out),@S[2]
+
+.L1st128:
+	lea	.LCamellia_SIGMA(%rip),$key
+	lea	.LCamellia_SBOX(%rip),$Tbl
+
+	mov	0($key),$t1
+	mov	4($key),$t0
+___
+	&Camellia_Feistel($step++);
+	&Camellia_Feistel($step++);
+$code.=<<___;
+	xor	0($out),@S[1]		# ^KL
+	xor	4($out),@S[0]
+	xor	8($out),@S[3]
+	xor	12($out),@S[2]
+___
+	&Camellia_Feistel($step++);
+	&Camellia_Feistel($step++);
+$code.=<<___;
+	cmp	\$128,$keyend
+	jne	.L2nd256
+
+	lea	128($out),$out		# size optimization
+	shl	\$32,%r8		# @S[0]||
+	shl	\$32,%r10		# @S[2]||
+	or	%r9,%r8			# ||@S[1]
+	or	%r11,%r10		# ||@S[3]
+___
+	&_loadround	(0,$out,-128,"%rax","%rbx");	# KL
+	&_saveround	(2,$out,-128,"%r8","%r10");	# KA<<<0
+	&_rotl128	("%rax","%rbx",15);
+	&_saveround	(4,$out,-128,"%rax","%rbx");	# KL<<<15
+	&_rotl128	("%r8","%r10",15);
+	&_saveround	(6,$out,-128,"%r8","%r10");	# KA<<<15
+	&_rotl128	("%r8","%r10",15);		# 15+15=30
+	&_saveround	(8,$out,-128,"%r8","%r10");	# KA<<<30
+	&_rotl128	("%rax","%rbx",30);		# 15+30=45
+	&_saveround	(10,$out,-128,"%rax","%rbx");	# KL<<<45
+	&_rotl128	("%r8","%r10",15);		# 30+15=45
+	&_saveround	(12,$out,-128,"%r8");		# KA<<<45
+	&_rotl128	("%rax","%rbx",15);		# 45+15=60
+	&_saveround	(13,$out,-128,"%rbx");		# KL<<<60
+	&_rotl128	("%r8","%r10",15);		# 45+15=60
+	&_saveround	(14,$out,-128,"%r8","%r10");	# KA<<<60
+	&_rotl128	("%rax","%rbx",17);		# 60+17=77
+	&_saveround	(16,$out,-128,"%rax","%rbx");	# KL<<<77
+	&_rotl128	("%rax","%rbx",17);		# 77+17=94
+	&_saveround	(18,$out,-128,"%rax","%rbx");	# KL<<<94
+	&_rotl128	("%r8","%r10",34);		# 60+34=94
+	&_saveround	(20,$out,-128,"%r8","%r10");	# KA<<<94
+	&_rotl128	("%rax","%rbx",17);		# 94+17=111
+	&_saveround	(22,$out,-128,"%rax","%rbx");	# KL<<<111
+	&_rotl128	("%r8","%r10",17);		# 94+17=111
+	&_saveround	(24,$out,-128,"%r8","%r10");	# KA<<<111
+$code.=<<___;
+	mov	\$3,%eax
+	jmp	.Ldone
+.align	16
+.L2nd256:
+___
+	&_saveround	(6,$out,@S);	# temp storage for KA!
+$code.=<<___;
+	xor	`4*8+0`($out),@S[1]	# KA^KR
+	xor	`4*8+4`($out),@S[0]
+	xor	`5*8+0`($out),@S[3]
+	xor	`5*8+4`($out),@S[2]
+___
+	&Camellia_Feistel($step++);
+	&Camellia_Feistel($step++);
+
+	&_loadround	(0,$out,"%rax","%rbx");	# KL
+	&_loadround	(4,$out,"%rcx","%rdx");	# KR
+	&_loadround	(6,$out,"%r14","%r15");	# KA
+$code.=<<___;
+	lea	128($out),$out		# size optimization
+	shl	\$32,%r8		# @S[0]||
+	shl	\$32,%r10		# @S[2]||
+	or	%r9,%r8			# ||@S[1]
+	or	%r11,%r10		# ||@S[3]
+___
+	&_saveround	(2,$out,-128,"%r8","%r10");	# KB<<<0
+	&_rotl128	("%rcx","%rdx",15);
+	&_saveround	(4,$out,-128,"%rcx","%rdx");	# KR<<<15
+	&_rotl128	("%r14","%r15",15);
+	&_saveround	(6,$out,-128,"%r14","%r15");	# KA<<<15
+	&_rotl128	("%rcx","%rdx",15);		# 15+15=30
+	&_saveround	(8,$out,-128,"%rcx","%rdx");	# KR<<<30
+	&_rotl128	("%r8","%r10",30);
+	&_saveround	(10,$out,-128,"%r8","%r10");	# KB<<<30
+	&_rotl128	("%rax","%rbx",45);
+	&_saveround	(12,$out,-128,"%rax","%rbx");	# KL<<<45
+	&_rotl128	("%r14","%r15",30);		# 15+30=45
+	&_saveround	(14,$out,-128,"%r14","%r15");	# KA<<<45
+	&_rotl128	("%rax","%rbx",15);		# 45+15=60
+	&_saveround	(16,$out,-128,"%rax","%rbx");	# KL<<<60
+	&_rotl128	("%rcx","%rdx",30);		# 30+30=60
+	&_saveround	(18,$out,-128,"%rcx","%rdx");	# KR<<<60
+	&_rotl128	("%r8","%r10",30);		# 30+30=60
+	&_saveround	(20,$out,-128,"%r8","%r10");	# KB<<<60
+	&_rotl128	("%rax","%rbx",17);		# 60+17=77
+	&_saveround	(22,$out,-128,"%rax","%rbx");	# KL<<<77
+	&_rotl128	("%r14","%r15",32);		# 45+32=77
+	&_saveround	(24,$out,-128,"%r14","%r15");	# KA<<<77
+	&_rotl128	("%rcx","%rdx",34);		# 60+34=94
+	&_saveround	(26,$out,-128,"%rcx","%rdx");	# KR<<<94
+	&_rotl128	("%r14","%r15",17);		# 77+17=94
+	&_saveround	(28,$out,-128,"%r14","%r15");	# KA<<<77
+	&_rotl128	("%rax","%rbx",34);		# 77+34=111
+	&_saveround	(30,$out,-128,"%rax","%rbx");	# KL<<<111
+	&_rotl128	("%r8","%r10",51);		# 60+51=111
+	&_saveround	(32,$out,-128,"%r8","%r10");	# KB<<<111
+$code.=<<___;
+	mov	\$4,%eax
+.Ldone:
+	mov	0(%rsp),%r15
+	mov	8(%rsp),%r14
+	mov	16(%rsp),%r13
+	mov	24(%rsp),%rbp
+	mov	32(%rsp),%rbx
+	lea	40(%rsp),%rsp
+.Lkey_epilogue:
+	ret
+.size	Camellia_Ekeygen,.-Camellia_Ekeygen
+___
+}
+
+@SBOX=(
+112,130, 44,236,179, 39,192,229,228,133, 87, 53,234, 12,174, 65,
+ 35,239,107,147, 69, 25,165, 33,237, 14, 79, 78, 29,101,146,189,
+134,184,175,143,124,235, 31,206, 62, 48,220, 95, 94,197, 11, 26,
+166,225, 57,202,213, 71, 93, 61,217,  1, 90,214, 81, 86,108, 77,
+139, 13,154,102,251,204,176, 45,116, 18, 43, 32,240,177,132,153,
+223, 76,203,194, 52,126,118,  5,109,183,169, 49,209, 23,  4,215,
+ 20, 88, 58, 97,222, 27, 17, 28, 50, 15,156, 22, 83, 24,242, 34,
+254, 68,207,178,195,181,122,145, 36,  8,232,168, 96,252,105, 80,
+170,208,160,125,161,137, 98,151, 84, 91, 30,149,224,255,100,210,
+ 16,196,  0, 72,163,247,117,219,138,  3,230,218,  9, 63,221,148,
+135, 92,131,  2,205, 74,144, 51,115,103,246,243,157,127,191,226,
+ 82,155,216, 38,200, 55,198, 59,129,150,111, 75, 19,190, 99, 46,
+233,121,167,140,159,110,188,142, 41,245,249,182, 47,253,180, 89,
+120,152,  6,106,231, 70,113,186,212, 37,171, 66,136,162,141,250,
+114,  7,185, 85,248,238,172, 10, 54, 73, 42,104, 60, 56,241,164,
+ 64, 40,211,123,187,201, 67,193, 21,227,173,244,119,199,128,158);
+
+sub S1110 { my $i=shift; $i=@SBOX[$i]; $i=$i<<24|$i<<16|$i<<8; sprintf("0x%08x",$i); }
+sub S4404 { my $i=shift; $i=($i<<1|$i>>7)&0xff; $i=@SBOX[$i]; $i=$i<<24|$i<<16|$i; sprintf("0x%08x",$i); }
+sub S0222 { my $i=shift; $i=@SBOX[$i]; $i=($i<<1|$i>>7)&0xff; $i=$i<<16|$i<<8|$i; sprintf("0x%08x",$i); }
+sub S3033 { my $i=shift; $i=@SBOX[$i]; $i=($i>>1|$i<<7)&0xff; $i=$i<<24|$i<<8|$i; sprintf("0x%08x",$i); }
+
+$code.=<<___;
+.align	64
+.LCamellia_SIGMA:
+.long	0x3bcc908b, 0xa09e667f, 0x4caa73b2, 0xb67ae858
+.long	0xe94f82be, 0xc6ef372f, 0xf1d36f1c, 0x54ff53a5
+.long	0xde682d1d, 0x10e527fa, 0xb3e6c1fd, 0xb05688c2
+.long	0,          0,          0,          0
+.LCamellia_SBOX:
+___
+# tables are interleaved, remember?
+sub data_word { $code.=".long\t".join(',',@_)."\n"; }
+for ($i=0;$i<256;$i++) { &data_word(&S1110($i),&S4404($i)); }
+for ($i=0;$i<256;$i++) { &data_word(&S0222($i),&S3033($i)); }
+
+# void Camellia_cbc_encrypt (const void char *inp, unsigned char *out,
+#			size_t length, const CAMELLIA_KEY *key,
+#			unsigned char *ivp,const int enc);
+{
+$_key="0(%rsp)";
+$_end="8(%rsp)";	# inp+len&~15
+$_res="16(%rsp)";	# len&15
+$ivec="24(%rsp)";
+$_ivp="40(%rsp)";
+$_rsp="48(%rsp)";
+
+$code.=<<___;
+.globl	Camellia_cbc_encrypt
+.type	Camellia_cbc_encrypt,\@function,6
+.align	16
+Camellia_cbc_encrypt:
+	cmp	\$0,%rdx
+	je	.Lcbc_abort
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+.Lcbc_prologue:
+
+	mov	%rsp,%rbp
+	sub	\$64,%rsp
+	and	\$-64,%rsp
+
+	# place stack frame just "above mod 1024" the key schedule,
+	# this ensures that cache associativity suffices
+	lea	-64-63(%rcx),%r10
+	sub	%rsp,%r10
+	neg	%r10
+	and	\$0x3C0,%r10
+	sub	%r10,%rsp
+	#add	\$8,%rsp		# 8 is reserved for callee's ra
+
+	mov	%rdi,$inp		# inp argument
+	mov	%rsi,$out		# out argument
+	mov	%r8,%rbx		# ivp argument
+	mov	%rcx,$key		# key argument
+	mov	272(%rcx),${keyend}d	# grandRounds
+
+	mov	%r8,$_ivp
+	mov	%rbp,$_rsp
+
+.Lcbc_body:
+	lea	.LCamellia_SBOX(%rip),$Tbl
+
+	mov	\$32,%ecx
+.align	4
+.Lcbc_prefetch_sbox:
+	mov	0($Tbl),%rax
+	mov	32($Tbl),%rsi
+	mov	64($Tbl),%rdi
+	mov	96($Tbl),%r11
+	lea	128($Tbl),$Tbl
+	loop	.Lcbc_prefetch_sbox
+	sub	\$4096,$Tbl
+	shl	\$6,$keyend
+	mov	%rdx,%rcx		# len argument
+	lea	($key,$keyend),$keyend
+
+	cmp	\$0,%r9d		# enc argument
+	je	.LCBC_DECRYPT
+
+	and	\$-16,%rdx
+	and	\$15,%rcx		# length residue
+	lea	($inp,%rdx),%rdx
+	mov	$key,$_key
+	mov	%rdx,$_end
+	mov	%rcx,$_res
+
+	cmp	$inp,%rdx
+	mov	0(%rbx),@S[0]		# load IV
+	mov	4(%rbx),@S[1]
+	mov	8(%rbx),@S[2]
+	mov	12(%rbx),@S[3]
+	je	.Lcbc_enc_tail
+	jmp	.Lcbc_eloop
+
+.align	16
+.Lcbc_eloop:
+	xor	0($inp),@S[0]
+	xor	4($inp),@S[1]
+	xor	8($inp),@S[2]
+	bswap	@S[0]
+	xor	12($inp),@S[3]
+	bswap	@S[1]
+	bswap	@S[2]
+	bswap	@S[3]
+
+	call	_x86_64_Camellia_encrypt
+
+	mov	$_key,$key		# "rewind" the key
+	bswap	@S[0]
+	mov	$_end,%rdx
+	bswap	@S[1]
+	mov	$_res,%rcx
+	bswap	@S[2]
+	mov	@S[0],0($out)
+	bswap	@S[3]
+	mov	@S[1],4($out)
+	mov	@S[2],8($out)
+	lea	16($inp),$inp
+	mov	@S[3],12($out)
+	cmp	%rdx,$inp
+	lea	16($out),$out
+	jne	.Lcbc_eloop
+
+	cmp	\$0,%rcx
+	jne	.Lcbc_enc_tail
+
+	mov	$_ivp,$out
+	mov	@S[0],0($out)		# write out IV residue
+	mov	@S[1],4($out)
+	mov	@S[2],8($out)
+	mov	@S[3],12($out)
+	jmp	.Lcbc_done
+
+.align	16
+.Lcbc_enc_tail:
+	xor	%rax,%rax
+	mov	%rax,0+$ivec
+	mov	%rax,8+$ivec
+	mov	%rax,$_res
+
+.Lcbc_enc_pushf:
+	pushfq
+	cld
+	mov	$inp,%rsi
+	lea	8+$ivec,%rdi
+	.long	0x9066A4F3		# rep movsb
+	popfq
+.Lcbc_enc_popf:
+
+	lea	$ivec,$inp
+	lea	16+$ivec,%rax
+	mov	%rax,$_end
+	jmp	.Lcbc_eloop		# one more time
+
+.align	16
+.LCBC_DECRYPT:
+	xchg	$key,$keyend
+	add	\$15,%rdx
+	and	\$15,%rcx		# length residue
+	and	\$-16,%rdx
+	mov	$key,$_key
+	lea	($inp,%rdx),%rdx
+	mov	%rdx,$_end
+	mov	%rcx,$_res
+
+	mov	(%rbx),%rax		# load IV
+	mov	8(%rbx),%rbx
+	jmp	.Lcbc_dloop
+.align	16
+.Lcbc_dloop:
+	mov	0($inp),@S[0]
+	mov	4($inp),@S[1]
+	mov	8($inp),@S[2]
+	bswap	@S[0]
+	mov	12($inp),@S[3]
+	bswap	@S[1]
+	mov	%rax,0+$ivec		# save IV to temporary storage
+	bswap	@S[2]
+	mov	%rbx,8+$ivec
+	bswap	@S[3]
+
+	call	_x86_64_Camellia_decrypt
+
+	mov	$_key,$key		# "rewind" the key
+	mov	$_end,%rdx
+	mov	$_res,%rcx
+
+	bswap	@S[0]
+	mov	($inp),%rax		# load IV for next iteration
+	bswap	@S[1]
+	mov	8($inp),%rbx
+	bswap	@S[2]
+	xor	0+$ivec,@S[0]
+	bswap	@S[3]
+	xor	4+$ivec,@S[1]
+	xor	8+$ivec,@S[2]
+	lea	16($inp),$inp
+	xor	12+$ivec,@S[3]
+	cmp	%rdx,$inp
+	je	.Lcbc_ddone
+
+	mov	@S[0],0($out)
+	mov	@S[1],4($out)
+	mov	@S[2],8($out)
+	mov	@S[3],12($out)
+
+	lea	16($out),$out
+	jmp	.Lcbc_dloop
+
+.align	16
+.Lcbc_ddone:
+	mov	$_ivp,%rdx
+	cmp	\$0,%rcx
+	jne	.Lcbc_dec_tail
+
+	mov	@S[0],0($out)
+	mov	@S[1],4($out)
+	mov	@S[2],8($out)
+	mov	@S[3],12($out)
+
+	mov	%rax,(%rdx)		# write out IV residue
+	mov	%rbx,8(%rdx)
+	jmp	.Lcbc_done
+.align	16
+.Lcbc_dec_tail:
+	mov	@S[0],0+$ivec
+	mov	@S[1],4+$ivec
+	mov	@S[2],8+$ivec
+	mov	@S[3],12+$ivec
+
+.Lcbc_dec_pushf:
+	pushfq
+	cld
+	lea	8+$ivec,%rsi
+	lea	($out),%rdi
+	.long	0x9066A4F3		# rep movsb
+	popfq
+.Lcbc_dec_popf:
+
+	mov	%rax,(%rdx)		# write out IV residue
+	mov	%rbx,8(%rdx)
+	jmp	.Lcbc_done
+
+.align	16
+.Lcbc_done:
+	mov	$_rsp,%rcx
+	mov	0(%rcx),%r15
+	mov	8(%rcx),%r14
+	mov	16(%rcx),%r13
+	mov	24(%rcx),%r12
+	mov	32(%rcx),%rbp
+	mov	40(%rcx),%rbx
+	lea	48(%rcx),%rsp
+.Lcbc_abort:
+	ret
+.size	Camellia_cbc_encrypt,.-Camellia_cbc_encrypt
+
+.asciz	"Camellia for x86_64 by "
+___
+}
+
+# EXCEPTION_DISPOSITION handler (EXCEPTION_RECORD *rec,ULONG64 frame,
+#		CONTEXT *context,DISPATCHER_CONTEXT *disp)
+if ($win64) {
+$rec="%rcx";
+$frame="%rdx";
+$context="%r8";
+$disp="%r9";
+
+$code.=<<___;
+.extern	__imp_RtlVirtualUnwind
+.type	common_se_handler,\@abi-omnipotent
+.align	16
+common_se_handler:
+	push	%rsi
+	push	%rdi
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+	pushfq
+	lea	-64(%rsp),%rsp
+
+	mov	120($context),%rax	# pull context->Rax
+	mov	248($context),%rbx	# pull context->Rip
+
+	mov	8($disp),%rsi		# disp->ImageBase
+	mov	56($disp),%r11		# disp->HandlerData
+
+	mov	0(%r11),%r10d		# HandlerData[0]
+	lea	(%rsi,%r10),%r10	# prologue label
+	cmp	%r10,%rbx		# context->RipRsp
+
+	mov	4(%r11),%r10d		# HandlerData[1]
+	lea	(%rsi,%r10),%r10	# epilogue label
+	cmp	%r10,%rbx		# context->Rip>=epilogue label
+	jae	.Lin_prologue
+
+	lea	40(%rax),%rax
+	mov	-8(%rax),%rbx
+	mov	-16(%rax),%rbp
+	mov	-24(%rax),%r13
+	mov	-32(%rax),%r14
+	mov	-40(%rax),%r15
+	mov	%rbx,144($context)	# restore context->Rbx
+	mov	%rbp,160($context)	# restore context->Rbp
+	mov	%r13,224($context)	# restore context->R13
+	mov	%r14,232($context)	# restore context->R14
+	mov	%r15,240($context)	# restore context->R15
+
+.Lin_prologue:
+	mov	8(%rax),%rdi
+	mov	16(%rax),%rsi
+	mov	%rax,152($context)	# restore context->Rsp
+	mov	%rsi,168($context)	# restore context->Rsi
+	mov	%rdi,176($context)	# restore context->Rdi
+
+	jmp	.Lcommon_seh_exit
+.size	common_se_handler,.-common_se_handler
+
+.type	cbc_se_handler,\@abi-omnipotent
+.align	16
+cbc_se_handler:
+	push	%rsi
+	push	%rdi
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+	pushfq
+	lea	-64(%rsp),%rsp
+
+	mov	120($context),%rax	# pull context->Rax
+	mov	248($context),%rbx	# pull context->Rip
+
+	lea	.Lcbc_prologue(%rip),%r10
+	cmp	%r10,%rbx		# context->Rip<.Lcbc_prologue
+	jb	.Lin_cbc_prologue
+
+	lea	.Lcbc_body(%rip),%r10
+	cmp	%r10,%rbx		# context->Rip<.Lcbc_body
+	jb	.Lin_cbc_frame_setup
+
+	mov	152($context),%rax	# pull context->Rsp
+
+	lea	.Lcbc_abort(%rip),%r10
+	cmp	%r10,%rbx		# context->Rip>=.Lcbc_abort
+	jae	.Lin_cbc_prologue
+
+	# handle pushf/popf in Camellia_cbc_encrypt
+	lea	.Lcbc_enc_pushf(%rip),%r10
+	cmp	%r10,%rbx		# context->Rip<=.Lcbc_enc_pushf
+	jbe	.Lin_cbc_no_flag
+	lea	8(%rax),%rax
+	lea	.Lcbc_enc_popf(%rip),%r10
+	cmp	%r10,%rbx		# context->Rip<.Lcbc_enc_popf
+	jb	.Lin_cbc_no_flag
+	lea	-8(%rax),%rax
+	lea	.Lcbc_dec_pushf(%rip),%r10
+	cmp	%r10,%rbx		# context->Rip<=.Lcbc_dec_pushf
+	jbe	.Lin_cbc_no_flag
+	lea	8(%rax),%rax
+	lea	.Lcbc_dec_popf(%rip),%r10
+	cmp	%r10,%rbx		# context->Rip<.Lcbc_dec_popf
+	jb	.Lin_cbc_no_flag
+	lea	-8(%rax),%rax
+
+.Lin_cbc_no_flag:
+	mov	48(%rax),%rax		# $_rsp
+	lea	48(%rax),%rax
+
+.Lin_cbc_frame_setup:
+	mov	-8(%rax),%rbx
+	mov	-16(%rax),%rbp
+	mov	-24(%rax),%r12
+	mov	-32(%rax),%r13
+	mov	-40(%rax),%r14
+	mov	-48(%rax),%r15
+	mov	%rbx,144($context)	# restore context->Rbx
+	mov	%rbp,160($context)	# restore context->Rbp
+	mov	%r12,216($context)	# restore context->R12
+	mov	%r13,224($context)	# restore context->R13
+	mov	%r14,232($context)	# restore context->R14
+	mov	%r15,240($context)	# restore context->R15
+
+.Lin_cbc_prologue:
+	mov	8(%rax),%rdi
+	mov	16(%rax),%rsi
+	mov	%rax,152($context)	# restore context->Rsp
+	mov	%rsi,168($context)	# restore context->Rsi
+	mov	%rdi,176($context)	# restore context->Rdi
+
+.align	4
+.Lcommon_seh_exit:
+
+	mov	40($disp),%rdi		# disp->ContextRecord
+	mov	$context,%rsi		# context
+	mov	\$`1232/8`,%ecx		# sizeof(CONTEXT)
+	.long	0xa548f3fc		# cld; rep movsq
+
+	mov	$disp,%rsi
+	xor	%rcx,%rcx		# arg1, UNW_FLAG_NHANDLER
+	mov	8(%rsi),%rdx		# arg2, disp->ImageBase
+	mov	0(%rsi),%r8		# arg3, disp->ControlPc
+	mov	16(%rsi),%r9		# arg4, disp->FunctionEntry
+	mov	40(%rsi),%r10		# disp->ContextRecord
+	lea	56(%rsi),%r11		# &disp->HandlerData
+	lea	24(%rsi),%r12		# &disp->EstablisherFrame
+	mov	%r10,32(%rsp)		# arg5
+	mov	%r11,40(%rsp)		# arg6
+	mov	%r12,48(%rsp)		# arg7
+	mov	%rcx,56(%rsp)		# arg8, (NULL)
+	call	*__imp_RtlVirtualUnwind(%rip)
+
+	mov	\$1,%eax		# ExceptionContinueSearch
+	lea	64(%rsp),%rsp
+	popfq
+	pop	%r15
+	pop	%r14
+	pop	%r13
+	pop	%r12
+	pop	%rbp
+	pop	%rbx
+	pop	%rdi
+	pop	%rsi
+	ret
+.size	cbc_se_handler,.-cbc_se_handler
+
+.section	.pdata
+.align	4
+	.rva	.LSEH_begin_Camellia_EncryptBlock_Rounds
+	.rva	.LSEH_end_Camellia_EncryptBlock_Rounds
+	.rva	.LSEH_info_Camellia_EncryptBlock_Rounds
+
+	.rva	.LSEH_begin_Camellia_DecryptBlock_Rounds
+	.rva	.LSEH_end_Camellia_DecryptBlock_Rounds
+	.rva	.LSEH_info_Camellia_DecryptBlock_Rounds
+
+	.rva	.LSEH_begin_Camellia_Ekeygen
+	.rva	.LSEH_end_Camellia_Ekeygen
+	.rva	.LSEH_info_Camellia_Ekeygen
+
+	.rva	.LSEH_begin_Camellia_cbc_encrypt
+	.rva	.LSEH_end_Camellia_cbc_encrypt
+	.rva	.LSEH_info_Camellia_cbc_encrypt
+
+.section	.xdata
+.align	8
+.LSEH_info_Camellia_EncryptBlock_Rounds:
+	.byte	9,0,0,0
+	.rva	common_se_handler
+	.rva	.Lenc_prologue,.Lenc_epilogue	# HandlerData[]
+.LSEH_info_Camellia_DecryptBlock_Rounds:
+	.byte	9,0,0,0
+	.rva	common_se_handler
+	.rva	.Ldec_prologue,.Ldec_epilogue	# HandlerData[]
+.LSEH_info_Camellia_Ekeygen:
+	.byte	9,0,0,0
+	.rva	common_se_handler
+	.rva	.Lkey_prologue,.Lkey_epilogue	# HandlerData[]
+.LSEH_info_Camellia_cbc_encrypt:
+	.byte	9,0,0,0
+	.rva	cbc_se_handler
+___
+}
+
+$code =~ s/\`([^\`]*)\`/eval $1/gem;
+print $code;
+close STDOUT;
diff --git a/crypto/camellia/asm/cmllt4-sparcv9.pl b/crypto/camellia/asm/cmllt4-sparcv9.pl
new file mode 100644
index 000000000..ffe4a7d91
--- /dev/null
+++ b/crypto/camellia/asm/cmllt4-sparcv9.pl
@@ -0,0 +1,939 @@
+#! /usr/bin/env perl
+# Copyright 2012-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+
+# ====================================================================
+# Written by David S. Miller  and Andy Polyakov
+# . The module is licensed under 2-clause BSD
+# license. October 2012. All rights reserved.
+# ====================================================================
+
+######################################################################
+# Camellia for SPARC T4.
+#
+# As with AES below results [for aligned data] are virtually identical
+# to critical path lenths for 3-cycle instruction latency:
+#
+#		128-bit key	192/256-
+# CBC encrypt	4.14/4.21(*)	5.46/5.52
+#			 (*) numbers after slash are for
+#			     misaligned data.
+#
+# As with Intel AES-NI, question is if it's possible to improve
+# performance of parallelizeable modes by interleaving round
+# instructions. In Camellia every instruction is dependent on
+# previous, which means that there is place for 2 additional ones
+# in between two dependent. Can we expect 3x performance improvement?
+# At least one can argue that it should be possible to break 2x
+# barrier... For some reason not even 2x appears to be possible:
+#
+#		128-bit key	192/256-
+# CBC decrypt	2.21/2.74	2.99/3.40
+# CTR		2.15/2.68(*)	2.93/3.34
+#			 (*) numbers after slash are for
+#			     misaligned data.
+#
+# This is for 2x interleave. But compared to 1x interleave CBC decrypt
+# improved by ... 0% for 128-bit key, and 11% for 192/256-bit one.
+# So that out-of-order execution logic can take non-interleaved code
+# to 1.87x, but can't take 2x interleaved one any further. There
+# surely is some explanation... As result 3x interleave was not even
+# attempted. Instead an effort was made to share specific modes
+# implementations with AES module (therefore sparct4_modes.pl).
+#
+# To anchor to something else, software C implementation processes
+# one byte in 38 cycles with 128-bit key on same processor.
+
+$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+push(@INC,"${dir}","${dir}../../perlasm");
+require "sparcv9_modes.pl";
+
+$output = pop;
+open STDOUT,">$output";
+
+$::evp=1;	# if $evp is set to 0, script generates module with
+# Camellia_[en|de]crypt, Camellia_set_key and Camellia_cbc_encrypt
+# entry points. These are fully compatible with openssl/camellia.h.
+
+######################################################################
+# single-round subroutines
+#
+{
+my ($inp,$out,$key,$rounds,$tmp,$mask)=map("%o$_",(0..5));
+
+$code=<<___;
+#include "sparc_arch.h"
+
+.text
+
+.globl	cmll_t4_encrypt
+.align	32
+cmll_t4_encrypt:
+	andcc		$inp, 7, %g1		! is input aligned?
+	andn		$inp, 7, $inp
+
+	ldx		[$key + 0], %g4
+	ldx		[$key + 8], %g5
+
+	ldx		[$inp + 0], %o4
+	bz,pt		%icc, 1f
+	ldx		[$inp + 8], %o5
+	ldx		[$inp + 16], $inp
+	sll		%g1, 3, %g1
+	sub		%g0, %g1, %o3
+	sllx		%o4, %g1, %o4
+	sllx		%o5, %g1, %g1
+	srlx		%o5, %o3, %o5
+	srlx		$inp, %o3, %o3
+	or		%o5, %o4, %o4
+	or		%o3, %g1, %o5
+1:
+	ld		[$key + 272], $rounds	! grandRounds, 3 or 4
+	ldd		[$key + 16], %f12
+	ldd		[$key + 24], %f14
+	xor		%g4, %o4, %o4
+	xor		%g5, %o5, %o5
+	ldd		[$key + 32], %f16
+	ldd		[$key + 40], %f18
+	movxtod		%o4, %f0
+	movxtod		%o5, %f2
+	ldd		[$key + 48], %f20
+	ldd		[$key + 56], %f22
+	sub		$rounds, 1, $rounds
+	ldd		[$key + 64], %f24
+	ldd		[$key + 72], %f26
+	add		$key, 80, $key
+
+.Lenc:
+	camellia_f	%f12, %f2, %f0, %f2
+	ldd		[$key + 0], %f12
+	sub		$rounds,1,$rounds
+	camellia_f	%f14, %f0, %f2, %f0
+	ldd		[$key + 8], %f14
+	camellia_f	%f16, %f2, %f0, %f2
+	ldd		[$key + 16], %f16
+	camellia_f	%f18, %f0, %f2, %f0
+	ldd		[$key + 24], %f18
+	camellia_f	%f20, %f2, %f0, %f2
+	ldd		[$key + 32], %f20
+	camellia_f	%f22, %f0, %f2, %f0
+	ldd		[$key + 40], %f22
+	camellia_fl	%f24, %f0, %f0
+	ldd		[$key + 48], %f24
+	camellia_fli	%f26, %f2, %f2
+	ldd		[$key + 56], %f26
+	brnz,pt		$rounds, .Lenc
+	add		$key, 64, $key
+
+	andcc		$out, 7, $tmp		! is output aligned?
+	camellia_f	%f12, %f2, %f0, %f2
+	camellia_f	%f14, %f0, %f2, %f0
+	camellia_f	%f16, %f2, %f0, %f2
+	camellia_f	%f18, %f0, %f2, %f0
+	camellia_f	%f20, %f2, %f0, %f4
+	camellia_f	%f22, %f0, %f4, %f2
+	fxor		%f24, %f4, %f0
+	fxor		%f26, %f2, %f2
+
+	bnz,pn		%icc, 2f
+	nop
+
+	std		%f0, [$out + 0]
+	retl
+	std		%f2, [$out + 8]
+
+2:	alignaddrl	$out, %g0, $out
+	mov		0xff, $mask
+	srl		$mask, $tmp, $mask
+
+	faligndata	%f0, %f0, %f4
+	faligndata	%f0, %f2, %f6
+	faligndata	%f2, %f2, %f8
+
+	stda		%f4, [$out + $mask]0xc0	! partial store
+	std		%f6, [$out + 8]
+	add		$out, 16, $out
+	orn		%g0, $mask, $mask
+	retl
+	stda		%f8, [$out + $mask]0xc0	! partial store
+.type	cmll_t4_encrypt,#function
+.size	cmll_t4_encrypt,.-cmll_t4_encrypt
+
+.globl	cmll_t4_decrypt
+.align	32
+cmll_t4_decrypt:
+	ld		[$key + 272], $rounds	! grandRounds, 3 or 4
+	andcc		$inp, 7, %g1		! is input aligned?
+	andn		$inp, 7, $inp
+
+	sll		$rounds, 6, $rounds
+	add		$rounds, $key, $key
+
+	ldx		[$inp + 0], %o4
+	bz,pt		%icc, 1f
+	ldx		[$inp + 8], %o5
+	ldx		[$inp + 16], $inp
+	sll		%g1, 3, %g1
+	sub		%g0, %g1, %g4
+	sllx		%o4, %g1, %o4
+	sllx		%o5, %g1, %g1
+	srlx		%o5, %g4, %o5
+	srlx		$inp, %g4, %g4
+	or		%o5, %o4, %o4
+	or		%g4, %g1, %o5
+1:
+	ldx		[$key + 0], %g4
+	ldx		[$key + 8], %g5
+	ldd		[$key - 8], %f12
+	ldd		[$key - 16], %f14
+	xor		%g4, %o4, %o4
+	xor		%g5, %o5, %o5
+	ldd		[$key - 24], %f16
+	ldd		[$key - 32], %f18
+	movxtod		%o4, %f0
+	movxtod		%o5, %f2
+	ldd		[$key - 40], %f20
+	ldd		[$key - 48], %f22
+	sub		$rounds, 64, $rounds
+	ldd		[$key - 56], %f24
+	ldd		[$key - 64], %f26
+	sub		$key, 64, $key
+
+.Ldec:
+	camellia_f	%f12, %f2, %f0, %f2
+	ldd		[$key - 8], %f12
+	sub		$rounds, 64, $rounds
+	camellia_f	%f14, %f0, %f2, %f0
+	ldd		[$key - 16], %f14
+	camellia_f	%f16, %f2, %f0, %f2
+	ldd		[$key - 24], %f16
+	camellia_f	%f18, %f0, %f2, %f0
+	ldd		[$key - 32], %f18
+	camellia_f	%f20, %f2, %f0, %f2
+	ldd		[$key - 40], %f20
+	camellia_f	%f22, %f0, %f2, %f0
+	ldd		[$key - 48], %f22
+	camellia_fl	%f24, %f0, %f0
+	ldd		[$key - 56], %f24
+	camellia_fli	%f26, %f2, %f2
+	ldd		[$key - 64], %f26
+	brnz,pt		$rounds, .Ldec
+	sub		$key, 64, $key
+
+	andcc		$out, 7, $tmp		! is output aligned?
+	camellia_f	%f12, %f2, %f0, %f2
+	camellia_f	%f14, %f0, %f2, %f0
+	camellia_f	%f16, %f2, %f0, %f2
+	camellia_f	%f18, %f0, %f2, %f0
+	camellia_f	%f20, %f2, %f0, %f4
+	camellia_f	%f22, %f0, %f4, %f2
+	fxor		%f26, %f4, %f0
+	fxor		%f24, %f2, %f2
+
+	bnz,pn		%icc, 2f
+	nop
+
+	std		%f0, [$out + 0]
+	retl
+	std		%f2, [$out + 8]
+
+2:	alignaddrl	$out, %g0, $out
+	mov		0xff, $mask
+	srl		$mask, $tmp, $mask
+
+	faligndata	%f0, %f0, %f4
+	faligndata	%f0, %f2, %f6
+	faligndata	%f2, %f2, %f8
+
+	stda		%f4, [$out + $mask]0xc0	! partial store
+	std		%f6, [$out + 8]
+	add		$out, 16, $out
+	orn		%g0, $mask, $mask
+	retl
+	stda		%f8, [$out + $mask]0xc0	! partial store
+.type	cmll_t4_decrypt,#function
+.size	cmll_t4_decrypt,.-cmll_t4_decrypt
+___
+}
+
+######################################################################
+# key setup subroutines
+#
+{
+sub ROTL128 {
+  my $rot = shift;
+
+	"srlx	%o4, 64-$rot, %g4\n\t".
+	"sllx	%o4, $rot, %o4\n\t".
+	"srlx	%o5, 64-$rot, %g5\n\t".
+	"sllx	%o5, $rot, %o5\n\t".
+	"or	%o4, %g5, %o4\n\t".
+	"or	%o5, %g4, %o5";
+}
+
+my ($inp,$bits,$out,$tmp)=map("%o$_",(0..5));
+$code.=<<___;
+.globl	cmll_t4_set_key
+.align	32
+cmll_t4_set_key:
+	and		$inp, 7, $tmp
+	alignaddr	$inp, %g0, $inp
+	cmp		$bits, 192
+	ldd		[$inp + 0], %f0
+	bl,pt		%icc,.L128
+	ldd		[$inp + 8], %f2
+
+	be,pt		%icc,.L192
+	ldd		[$inp + 16], %f4
+
+	brz,pt		$tmp, .L256aligned
+	ldd		[$inp + 24], %f6
+
+	ldd		[$inp + 32], %f8
+	faligndata	%f0, %f2, %f0
+	faligndata	%f2, %f4, %f2
+	faligndata	%f4, %f6, %f4
+	b		.L256aligned
+	faligndata	%f6, %f8, %f6
+
+.align	16
+.L192:
+	brz,a,pt	$tmp, .L256aligned
+	fnot2		%f4, %f6
+
+	ldd		[$inp + 24], %f6
+	nop
+	faligndata	%f0, %f2, %f0
+	faligndata	%f2, %f4, %f2
+	faligndata	%f4, %f6, %f4
+	fnot2		%f4, %f6
+
+.L256aligned:
+	std		%f0, [$out + 0]		! k[0, 1]
+	fsrc2		%f0, %f28
+	std		%f2, [$out + 8]		! k[2, 3]
+	fsrc2		%f2, %f30
+	fxor		%f4, %f0, %f0
+	b		.L128key
+	fxor		%f6, %f2, %f2
+
+.align	16
+.L128:
+	brz,pt		$tmp, .L128aligned
+	nop
+
+	ldd		[$inp + 16], %f4
+	nop
+	faligndata	%f0, %f2, %f0
+	faligndata	%f2, %f4, %f2
+
+.L128aligned:
+	std		%f0, [$out + 0]		! k[0, 1]
+	fsrc2		%f0, %f28
+	std		%f2, [$out + 8]		! k[2, 3]
+	fsrc2		%f2, %f30
+
+.L128key:
+	mov		%o7, %o5
+1:	call		.+8
+	add		%o7, SIGMA-1b, %o4
+	mov		%o5, %o7
+
+	ldd		[%o4 + 0], %f16
+	ldd		[%o4 + 8], %f18
+	ldd		[%o4 + 16], %f20
+	ldd		[%o4 + 24], %f22
+
+	camellia_f	%f16, %f2, %f0, %f2
+	camellia_f	%f18, %f0, %f2, %f0
+	fxor		%f28, %f0, %f0
+	fxor		%f30, %f2, %f2
+	camellia_f	%f20, %f2, %f0, %f2
+	camellia_f	%f22, %f0, %f2, %f0
+
+	bge,pn		%icc, .L256key
+	nop
+	std	%f0, [$out + 0x10]	! k[ 4,  5]
+	std	%f2, [$out + 0x18]	! k[ 6,  7]
+
+	movdtox	%f0, %o4
+	movdtox	%f2, %o5
+	`&ROTL128(15)`
+	stx	%o4, [$out + 0x30]	! k[12, 13]
+	stx	%o5, [$out + 0x38]	! k[14, 15]
+	`&ROTL128(15)`
+	stx	%o4, [$out + 0x40]	! k[16, 17]
+	stx	%o5, [$out + 0x48]	! k[18, 19]
+	`&ROTL128(15)`
+	stx	%o4, [$out + 0x60]	! k[24, 25]
+	`&ROTL128(15)`
+	stx	%o4, [$out + 0x70]	! k[28, 29]
+	stx	%o5, [$out + 0x78]	! k[30, 31]
+	`&ROTL128(34)`
+	stx	%o4, [$out + 0xa0]	! k[40, 41]
+	stx	%o5, [$out + 0xa8]	! k[42, 43]
+	`&ROTL128(17)`
+	stx	%o4, [$out + 0xc0]	! k[48, 49]
+	stx	%o5, [$out + 0xc8]	! k[50, 51]
+
+	movdtox	%f28, %o4		! k[ 0,  1]
+	movdtox	%f30, %o5		! k[ 2,  3]
+	`&ROTL128(15)`
+	stx	%o4, [$out + 0x20]	! k[ 8,  9]
+	stx	%o5, [$out + 0x28]	! k[10, 11]
+	`&ROTL128(30)`
+	stx	%o4, [$out + 0x50]	! k[20, 21]
+	stx	%o5, [$out + 0x58]	! k[22, 23]
+	`&ROTL128(15)`
+	stx	%o5, [$out + 0x68]	! k[26, 27]
+	`&ROTL128(17)`
+	stx	%o4, [$out + 0x80]	! k[32, 33]
+	stx	%o5, [$out + 0x88]	! k[34, 35]
+	`&ROTL128(17)`
+	stx	%o4, [$out + 0x90]	! k[36, 37]
+	stx	%o5, [$out + 0x98]	! k[38, 39]
+	`&ROTL128(17)`
+	stx	%o4, [$out + 0xb0]	! k[44, 45]
+	stx	%o5, [$out + 0xb8]	! k[46, 47]
+
+	mov		3, $tmp
+	st		$tmp, [$out + 0x110]
+	retl
+	xor		%o0, %o0, %o0
+
+.align	16
+.L256key:
+	ldd		[%o4 + 32], %f24
+	ldd		[%o4 + 40], %f26
+
+	std		%f0, [$out + 0x30]	! k[12, 13]
+	std		%f2, [$out + 0x38]	! k[14, 15]
+
+	fxor		%f4, %f0, %f0
+	fxor		%f6, %f2, %f2
+	camellia_f	%f24, %f2, %f0, %f2
+	camellia_f	%f26, %f0, %f2, %f0
+
+	std	%f0, [$out + 0x10]	! k[ 4,  5]
+	std	%f2, [$out + 0x18]	! k[ 6,  7]
+
+	movdtox	%f0, %o4
+	movdtox	%f2, %o5
+	`&ROTL128(30)`
+	stx	%o4, [$out + 0x50]	! k[20, 21]
+	stx	%o5, [$out + 0x58]	! k[22, 23]
+	`&ROTL128(30)`
+	stx	%o4, [$out + 0xa0]	! k[40, 41]
+	stx	%o5, [$out + 0xa8]	! k[42, 43]
+	`&ROTL128(51)`
+	stx	%o4, [$out + 0x100]	! k[64, 65]
+	stx	%o5, [$out + 0x108]	! k[66, 67]
+
+	movdtox	%f4, %o4		! k[ 8,  9]
+	movdtox	%f6, %o5		! k[10, 11]
+	`&ROTL128(15)`
+	stx	%o4, [$out + 0x20]	! k[ 8,  9]
+	stx	%o5, [$out + 0x28]	! k[10, 11]
+	`&ROTL128(15)`
+	stx	%o4, [$out + 0x40]	! k[16, 17]
+	stx	%o5, [$out + 0x48]	! k[18, 19]
+	`&ROTL128(30)`
+	stx	%o4, [$out + 0x90]	! k[36, 37]
+	stx	%o5, [$out + 0x98]	! k[38, 39]
+	`&ROTL128(34)`
+	stx	%o4, [$out + 0xd0]	! k[52, 53]
+	stx	%o5, [$out + 0xd8]	! k[54, 55]
+	ldx	[$out + 0x30], %o4	! k[12, 13]
+	ldx	[$out + 0x38], %o5	! k[14, 15]
+	`&ROTL128(15)`
+	stx	%o4, [$out + 0x30]	! k[12, 13]
+	stx	%o5, [$out + 0x38]	! k[14, 15]
+	`&ROTL128(30)`
+	stx	%o4, [$out + 0x70]	! k[28, 29]
+	stx	%o5, [$out + 0x78]	! k[30, 31]
+	srlx	%o4, 32, %g4
+	srlx	%o5, 32, %g5
+	st	%o4, [$out + 0xc0]	! k[48]
+	st	%g5, [$out + 0xc4]	! k[49]
+	st	%o5, [$out + 0xc8]	! k[50]
+	st	%g4, [$out + 0xcc]	! k[51]
+	`&ROTL128(49)`
+	stx	%o4, [$out + 0xe0]	! k[56, 57]
+	stx	%o5, [$out + 0xe8]	! k[58, 59]
+
+	movdtox	%f28, %o4		! k[ 0,  1]
+	movdtox	%f30, %o5		! k[ 2,  3]
+	`&ROTL128(45)`
+	stx	%o4, [$out + 0x60]	! k[24, 25]
+	stx	%o5, [$out + 0x68]	! k[26, 27]
+	`&ROTL128(15)`
+	stx	%o4, [$out + 0x80]	! k[32, 33]
+	stx	%o5, [$out + 0x88]	! k[34, 35]
+	`&ROTL128(17)`
+	stx	%o4, [$out + 0xb0]	! k[44, 45]
+	stx	%o5, [$out + 0xb8]	! k[46, 47]
+	`&ROTL128(34)`
+	stx	%o4, [$out + 0xf0]	! k[60, 61]
+	stx	%o5, [$out + 0xf8]	! k[62, 63]
+
+	mov		4, $tmp
+	st		$tmp, [$out + 0x110]
+	retl
+	xor		%o0, %o0, %o0
+.type	cmll_t4_set_key,#function
+.size	cmll_t4_set_key,.-cmll_t4_set_key
+.align	32
+SIGMA:
+	.long	0xa09e667f, 0x3bcc908b, 0xb67ae858, 0x4caa73b2
+	.long	0xc6ef372f, 0xe94f82be, 0x54ff53a5, 0xf1d36f1c
+	.long	0x10e527fa, 0xde682d1d, 0xb05688c2, 0xb3e6c1fd
+.type	SIGMA,#object
+.size	SIGMA,.-SIGMA
+.asciz	"Camellia for SPARC T4, David S. Miller, Andy Polyakov"
+___
+}
+
+{{{
+my ($inp,$out,$len,$key,$ivec,$enc)=map("%i$_",(0..5));
+my ($ileft,$iright,$ooff,$omask,$ivoff)=map("%l$_",(1..7));
+
+$code.=<<___;
+.align	32
+_cmll128_load_enckey:
+	ldx		[$key + 0], %g4
+	ldx		[$key + 8], %g5
+___
+for ($i=2; $i<26;$i++) {			# load key schedule
+    $code.=<<___;
+	ldd		[$key + `8*$i`], %f`12+2*$i`
+___
+}
+$code.=<<___;
+	retl
+	nop
+.type	_cmll128_load_enckey,#function
+.size	_cmll128_load_enckey,.-_cmll128_load_enckey
+_cmll256_load_enckey=_cmll128_load_enckey
+
+.align	32
+_cmll256_load_deckey:
+	ldd		[$key + 64], %f62
+	ldd		[$key + 72], %f60
+	b		.Load_deckey
+	add		$key, 64, $key
+_cmll128_load_deckey:
+	ldd		[$key + 0], %f60
+	ldd		[$key + 8], %f62
+.Load_deckey:
+___
+for ($i=2; $i<24;$i++) {			# load key schedule
+    $code.=<<___;
+	ldd		[$key + `8*$i`], %f`62-2*$i`
+___
+}
+$code.=<<___;
+	ldx		[$key + 192], %g4
+	retl
+	ldx		[$key + 200], %g5
+.type	_cmll256_load_deckey,#function
+.size	_cmll256_load_deckey,.-_cmll256_load_deckey
+
+.align	32
+_cmll128_encrypt_1x:
+___
+for ($i=0; $i<3; $i++) {
+    $code.=<<___;
+	camellia_f	%f`16+16*$i+0`, %f2, %f0, %f2
+	camellia_f	%f`16+16*$i+2`, %f0, %f2, %f0
+	camellia_f	%f`16+16*$i+4`, %f2, %f0, %f2
+	camellia_f	%f`16+16*$i+6`, %f0, %f2, %f0
+___
+$code.=<<___ if ($i<2);
+	camellia_f	%f`16+16*$i+8`, %f2, %f0, %f2
+	camellia_f	%f`16+16*$i+10`, %f0, %f2, %f0
+	camellia_fl	%f`16+16*$i+12`, %f0,      %f0
+	camellia_fli	%f`16+16*$i+14`, %f2,      %f2
+___
+}
+$code.=<<___;
+	camellia_f	%f56, %f2, %f0, %f4
+	camellia_f	%f58, %f0, %f4, %f2
+	fxor		%f60, %f4, %f0
+	retl
+	fxor		%f62, %f2, %f2
+.type	_cmll128_encrypt_1x,#function
+.size	_cmll128_encrypt_1x,.-_cmll128_encrypt_1x
+_cmll128_decrypt_1x=_cmll128_encrypt_1x
+
+.align	32
+_cmll128_encrypt_2x:
+___
+for ($i=0; $i<3; $i++) {
+    $code.=<<___;
+	camellia_f	%f`16+16*$i+0`, %f2, %f0, %f2
+	camellia_f	%f`16+16*$i+0`, %f6, %f4, %f6
+	camellia_f	%f`16+16*$i+2`, %f0, %f2, %f0
+	camellia_f	%f`16+16*$i+2`, %f4, %f6, %f4
+	camellia_f	%f`16+16*$i+4`, %f2, %f0, %f2
+	camellia_f	%f`16+16*$i+4`, %f6, %f4, %f6
+	camellia_f	%f`16+16*$i+6`, %f0, %f2, %f0
+	camellia_f	%f`16+16*$i+6`, %f4, %f6, %f4
+___
+$code.=<<___ if ($i<2);
+	camellia_f	%f`16+16*$i+8`, %f2, %f0, %f2
+	camellia_f	%f`16+16*$i+8`, %f6, %f4, %f6
+	camellia_f	%f`16+16*$i+10`, %f0, %f2, %f0
+	camellia_f	%f`16+16*$i+10`, %f4, %f6, %f4
+	camellia_fl	%f`16+16*$i+12`, %f0,      %f0
+	camellia_fl	%f`16+16*$i+12`, %f4,      %f4
+	camellia_fli	%f`16+16*$i+14`, %f2,      %f2
+	camellia_fli	%f`16+16*$i+14`, %f6,      %f6
+___
+}
+$code.=<<___;
+	camellia_f	%f56, %f2, %f0, %f8
+	camellia_f	%f56, %f6, %f4, %f10
+	camellia_f	%f58, %f0, %f8, %f2
+	camellia_f	%f58, %f4, %f10, %f6
+	fxor		%f60, %f8, %f0
+	fxor		%f60, %f10, %f4
+	fxor		%f62, %f2, %f2
+	retl
+	fxor		%f62, %f6, %f6
+.type	_cmll128_encrypt_2x,#function
+.size	_cmll128_encrypt_2x,.-_cmll128_encrypt_2x
+_cmll128_decrypt_2x=_cmll128_encrypt_2x
+
+.align	32
+_cmll256_encrypt_1x:
+	camellia_f	%f16, %f2, %f0, %f2
+	camellia_f	%f18, %f0, %f2, %f0
+	ldd		[$key + 208], %f16
+	ldd		[$key + 216], %f18
+	camellia_f	%f20, %f2, %f0, %f2
+	camellia_f	%f22, %f0, %f2, %f0
+	ldd		[$key + 224], %f20
+	ldd		[$key + 232], %f22
+	camellia_f	%f24, %f2, %f0, %f2
+	camellia_f	%f26, %f0, %f2, %f0
+	ldd		[$key + 240], %f24
+	ldd		[$key + 248], %f26
+	camellia_fl	%f28, %f0, %f0
+	camellia_fli	%f30, %f2, %f2
+	ldd		[$key + 256], %f28
+	ldd		[$key + 264], %f30
+___
+for ($i=1; $i<3; $i++) {
+    $code.=<<___;
+	camellia_f	%f`16+16*$i+0`, %f2, %f0, %f2
+	camellia_f	%f`16+16*$i+2`, %f0, %f2, %f0
+	camellia_f	%f`16+16*$i+4`, %f2, %f0, %f2
+	camellia_f	%f`16+16*$i+6`, %f0, %f2, %f0
+	camellia_f	%f`16+16*$i+8`, %f2, %f0, %f2
+	camellia_f	%f`16+16*$i+10`, %f0, %f2, %f0
+	camellia_fl	%f`16+16*$i+12`, %f0,      %f0
+	camellia_fli	%f`16+16*$i+14`, %f2,      %f2
+___
+}
+$code.=<<___;
+	camellia_f	%f16, %f2, %f0, %f2
+	camellia_f	%f18, %f0, %f2, %f0
+	ldd		[$key + 16], %f16
+	ldd		[$key + 24], %f18
+	camellia_f	%f20, %f2, %f0, %f2
+	camellia_f	%f22, %f0, %f2, %f0
+	ldd		[$key + 32], %f20
+	ldd		[$key + 40], %f22
+	camellia_f	%f24, %f2, %f0, %f4
+	camellia_f	%f26, %f0, %f4, %f2
+	ldd		[$key + 48], %f24
+	ldd		[$key + 56], %f26
+	fxor		%f28, %f4, %f0
+	fxor		%f30, %f2, %f2
+	ldd		[$key + 64], %f28
+	retl
+	ldd		[$key + 72], %f30
+.type	_cmll256_encrypt_1x,#function
+.size	_cmll256_encrypt_1x,.-_cmll256_encrypt_1x
+
+.align	32
+_cmll256_encrypt_2x:
+	camellia_f	%f16, %f2, %f0, %f2
+	camellia_f	%f16, %f6, %f4, %f6
+	camellia_f	%f18, %f0, %f2, %f0
+	camellia_f	%f18, %f4, %f6, %f4
+	ldd		[$key + 208], %f16
+	ldd		[$key + 216], %f18
+	camellia_f	%f20, %f2, %f0, %f2
+	camellia_f	%f20, %f6, %f4, %f6
+	camellia_f	%f22, %f0, %f2, %f0
+	camellia_f	%f22, %f4, %f6, %f4
+	ldd		[$key + 224], %f20
+	ldd		[$key + 232], %f22
+	camellia_f	%f24, %f2, %f0, %f2
+	camellia_f	%f24, %f6, %f4, %f6
+	camellia_f	%f26, %f0, %f2, %f0
+	camellia_f	%f26, %f4, %f6, %f4
+	ldd		[$key + 240], %f24
+	ldd		[$key + 248], %f26
+	camellia_fl	%f28, %f0, %f0
+	camellia_fl	%f28, %f4, %f4
+	camellia_fli	%f30, %f2, %f2
+	camellia_fli	%f30, %f6, %f6
+	ldd		[$key + 256], %f28
+	ldd		[$key + 264], %f30
+___
+for ($i=1; $i<3; $i++) {
+    $code.=<<___;
+	camellia_f	%f`16+16*$i+0`, %f2, %f0, %f2
+	camellia_f	%f`16+16*$i+0`, %f6, %f4, %f6
+	camellia_f	%f`16+16*$i+2`, %f0, %f2, %f0
+	camellia_f	%f`16+16*$i+2`, %f4, %f6, %f4
+	camellia_f	%f`16+16*$i+4`, %f2, %f0, %f2
+	camellia_f	%f`16+16*$i+4`, %f6, %f4, %f6
+	camellia_f	%f`16+16*$i+6`, %f0, %f2, %f0
+	camellia_f	%f`16+16*$i+6`, %f4, %f6, %f4
+	camellia_f	%f`16+16*$i+8`, %f2, %f0, %f2
+	camellia_f	%f`16+16*$i+8`, %f6, %f4, %f6
+	camellia_f	%f`16+16*$i+10`, %f0, %f2, %f0
+	camellia_f	%f`16+16*$i+10`, %f4, %f6, %f4
+	camellia_fl	%f`16+16*$i+12`, %f0,      %f0
+	camellia_fl	%f`16+16*$i+12`, %f4,      %f4
+	camellia_fli	%f`16+16*$i+14`, %f2,      %f2
+	camellia_fli	%f`16+16*$i+14`, %f6,      %f6
+___
+}
+$code.=<<___;
+	camellia_f	%f16, %f2, %f0, %f2
+	camellia_f	%f16, %f6, %f4, %f6
+	camellia_f	%f18, %f0, %f2, %f0
+	camellia_f	%f18, %f4, %f6, %f4
+	ldd		[$key + 16], %f16
+	ldd		[$key + 24], %f18
+	camellia_f	%f20, %f2, %f0, %f2
+	camellia_f	%f20, %f6, %f4, %f6
+	camellia_f	%f22, %f0, %f2, %f0
+	camellia_f	%f22, %f4, %f6, %f4
+	ldd		[$key + 32], %f20
+	ldd		[$key + 40], %f22
+	camellia_f	%f24, %f2, %f0, %f8
+	camellia_f	%f24, %f6, %f4, %f10
+	camellia_f	%f26, %f0, %f8, %f2
+	camellia_f	%f26, %f4, %f10, %f6
+	ldd		[$key + 48], %f24
+	ldd		[$key + 56], %f26
+	fxor		%f28, %f8, %f0
+	fxor		%f28, %f10, %f4
+	fxor		%f30, %f2, %f2
+	fxor		%f30, %f6, %f6
+	ldd		[$key + 64], %f28
+	retl
+	ldd		[$key + 72], %f30
+.type	_cmll256_encrypt_2x,#function
+.size	_cmll256_encrypt_2x,.-_cmll256_encrypt_2x
+
+.align	32
+_cmll256_decrypt_1x:
+	camellia_f	%f16, %f2, %f0, %f2
+	camellia_f	%f18, %f0, %f2, %f0
+	ldd		[$key - 8], %f16
+	ldd		[$key - 16], %f18
+	camellia_f	%f20, %f2, %f0, %f2
+	camellia_f	%f22, %f0, %f2, %f0
+	ldd		[$key - 24], %f20
+	ldd		[$key - 32], %f22
+	camellia_f	%f24, %f2, %f0, %f2
+	camellia_f	%f26, %f0, %f2, %f0
+	ldd		[$key - 40], %f24
+	ldd		[$key - 48], %f26
+	camellia_fl	%f28, %f0, %f0
+	camellia_fli	%f30, %f2, %f2
+	ldd		[$key - 56], %f28
+	ldd		[$key - 64], %f30
+___
+for ($i=1; $i<3; $i++) {
+    $code.=<<___;
+	camellia_f	%f`16+16*$i+0`, %f2, %f0, %f2
+	camellia_f	%f`16+16*$i+2`, %f0, %f2, %f0
+	camellia_f	%f`16+16*$i+4`, %f2, %f0, %f2
+	camellia_f	%f`16+16*$i+6`, %f0, %f2, %f0
+	camellia_f	%f`16+16*$i+8`, %f2, %f0, %f2
+	camellia_f	%f`16+16*$i+10`, %f0, %f2, %f0
+	camellia_fl	%f`16+16*$i+12`, %f0,      %f0
+	camellia_fli	%f`16+16*$i+14`, %f2,      %f2
+___
+}
+$code.=<<___;
+	camellia_f	%f16, %f2, %f0, %f2
+	camellia_f	%f18, %f0, %f2, %f0
+	ldd		[$key + 184], %f16
+	ldd		[$key + 176], %f18
+	camellia_f	%f20, %f2, %f0, %f2
+	camellia_f	%f22, %f0, %f2, %f0
+	ldd		[$key + 168], %f20
+	ldd		[$key + 160], %f22
+	camellia_f	%f24, %f2, %f0, %f4
+	camellia_f	%f26, %f0, %f4, %f2
+	ldd		[$key + 152], %f24
+	ldd		[$key + 144], %f26
+	fxor		%f30, %f4, %f0
+	fxor		%f28, %f2, %f2
+	ldd		[$key + 136], %f28
+	retl
+	ldd		[$key + 128], %f30
+.type	_cmll256_decrypt_1x,#function
+.size	_cmll256_decrypt_1x,.-_cmll256_decrypt_1x
+
+.align	32
+_cmll256_decrypt_2x:
+	camellia_f	%f16, %f2, %f0, %f2
+	camellia_f	%f16, %f6, %f4, %f6
+	camellia_f	%f18, %f0, %f2, %f0
+	camellia_f	%f18, %f4, %f6, %f4
+	ldd		[$key - 8], %f16
+	ldd		[$key - 16], %f18
+	camellia_f	%f20, %f2, %f0, %f2
+	camellia_f	%f20, %f6, %f4, %f6
+	camellia_f	%f22, %f0, %f2, %f0
+	camellia_f	%f22, %f4, %f6, %f4
+	ldd		[$key - 24], %f20
+	ldd		[$key - 32], %f22
+	camellia_f	%f24, %f2, %f0, %f2
+	camellia_f	%f24, %f6, %f4, %f6
+	camellia_f	%f26, %f0, %f2, %f0
+	camellia_f	%f26, %f4, %f6, %f4
+	ldd		[$key - 40], %f24
+	ldd		[$key - 48], %f26
+	camellia_fl	%f28, %f0, %f0
+	camellia_fl	%f28, %f4, %f4
+	camellia_fli	%f30, %f2, %f2
+	camellia_fli	%f30, %f6, %f6
+	ldd		[$key - 56], %f28
+	ldd		[$key - 64], %f30
+___
+for ($i=1; $i<3; $i++) {
+    $code.=<<___;
+	camellia_f	%f`16+16*$i+0`, %f2, %f0, %f2
+	camellia_f	%f`16+16*$i+0`, %f6, %f4, %f6
+	camellia_f	%f`16+16*$i+2`, %f0, %f2, %f0
+	camellia_f	%f`16+16*$i+2`, %f4, %f6, %f4
+	camellia_f	%f`16+16*$i+4`, %f2, %f0, %f2
+	camellia_f	%f`16+16*$i+4`, %f6, %f4, %f6
+	camellia_f	%f`16+16*$i+6`, %f0, %f2, %f0
+	camellia_f	%f`16+16*$i+6`, %f4, %f6, %f4
+	camellia_f	%f`16+16*$i+8`, %f2, %f0, %f2
+	camellia_f	%f`16+16*$i+8`, %f6, %f4, %f6
+	camellia_f	%f`16+16*$i+10`, %f0, %f2, %f0
+	camellia_f	%f`16+16*$i+10`, %f4, %f6, %f4
+	camellia_fl	%f`16+16*$i+12`, %f0,      %f0
+	camellia_fl	%f`16+16*$i+12`, %f4,      %f4
+	camellia_fli	%f`16+16*$i+14`, %f2,      %f2
+	camellia_fli	%f`16+16*$i+14`, %f6,      %f6
+___
+}
+$code.=<<___;
+	camellia_f	%f16, %f2, %f0, %f2
+	camellia_f	%f16, %f6, %f4, %f6
+	camellia_f	%f18, %f0, %f2, %f0
+	camellia_f	%f18, %f4, %f6, %f4
+	ldd		[$key + 184], %f16
+	ldd		[$key + 176], %f18
+	camellia_f	%f20, %f2, %f0, %f2
+	camellia_f	%f20, %f6, %f4, %f6
+	camellia_f	%f22, %f0, %f2, %f0
+	camellia_f	%f22, %f4, %f6, %f4
+	ldd		[$key + 168], %f20
+	ldd		[$key + 160], %f22
+	camellia_f	%f24, %f2, %f0, %f8
+	camellia_f	%f24, %f6, %f4, %f10
+	camellia_f	%f26, %f0, %f8, %f2
+	camellia_f	%f26, %f4, %f10, %f6
+	ldd		[$key + 152], %f24
+	ldd		[$key + 144], %f26
+	fxor		%f30, %f8, %f0
+	fxor		%f30, %f10, %f4
+	fxor		%f28, %f2, %f2
+	fxor		%f28, %f6, %f6
+	ldd		[$key + 136], %f28
+	retl
+	ldd		[$key + 128], %f30
+.type	_cmll256_decrypt_2x,#function
+.size	_cmll256_decrypt_2x,.-_cmll256_decrypt_2x
+___
+
+&alg_cbc_encrypt_implement("cmll",128);
+&alg_cbc_encrypt_implement("cmll",256);
+
+&alg_cbc_decrypt_implement("cmll",128);
+&alg_cbc_decrypt_implement("cmll",256);
+
+if ($::evp) {
+    &alg_ctr32_implement("cmll",128);
+    &alg_ctr32_implement("cmll",256);
+}
+}}}
+
+if (!$::evp) {
+$code.=<<___;
+.global	Camellia_encrypt
+Camellia_encrypt=cmll_t4_encrypt
+.global	Camellia_decrypt
+Camellia_decrypt=cmll_t4_decrypt
+.global	Camellia_set_key
+.align	32
+Camellia_set_key:
+	andcc		%o2, 7, %g0		! double-check alignment
+	bnz,a,pn	%icc, 1f
+	mov		-1, %o0
+	brz,a,pn	%o0, 1f
+	mov		-1, %o0
+	brz,a,pn	%o2, 1f
+	mov		-1, %o0
+	andncc		%o1, 0x1c0, %g0
+	bnz,a,pn	%icc, 1f
+	mov		-2, %o0
+	cmp		%o1, 128
+	bl,a,pn		%icc, 1f
+	mov		-2, %o0
+	b		cmll_t4_set_key
+	nop
+1:	retl
+	nop
+.type	Camellia_set_key,#function
+.size	Camellia_set_key,.-Camellia_set_key
+___
+
+my ($inp,$out,$len,$key,$ivec,$enc)=map("%o$_",(0..5));
+
+$code.=<<___;
+.globl	Camellia_cbc_encrypt
+.align	32
+Camellia_cbc_encrypt:
+	ld		[$key + 272], %g1
+	nop
+	brz		$enc, .Lcbc_decrypt
+	cmp		%g1, 3
+
+	be,pt		%icc, cmll128_t4_cbc_encrypt
+	nop
+	ba		cmll256_t4_cbc_encrypt
+	nop
+
+.Lcbc_decrypt:
+	be,pt		%icc, cmll128_t4_cbc_decrypt
+	nop
+	ba		cmll256_t4_cbc_decrypt
+	nop
+.type	Camellia_cbc_encrypt,#function
+.size	Camellia_cbc_encrypt,.-Camellia_cbc_encrypt
+___
+}
+
+&emit_assembler();
+
+close STDOUT;
diff --git a/crypto/camellia/build.info b/crypto/camellia/build.info
new file mode 100644
index 000000000..fd782724f
--- /dev/null
+++ b/crypto/camellia/build.info
@@ -0,0 +1,11 @@
+LIBS=../../libcrypto
+SOURCE[../../libcrypto]=\
+        cmll_ecb.c cmll_ofb.c cmll_cfb.c cmll_ctr.c \
+        {- $target{cmll_asm_src} -}
+
+GENERATE[cmll-x86.s]=asm/cmll-x86.pl $(PERLASM_SCHEME) $(CFLAGS) $(LIB_CFLAGS) $(PROCESSOR)
+DEPEND[cmll-x86.s]=../perlasm/x86asm.pl
+GENERATE[cmll-x86_64.s]=asm/cmll-x86_64.pl $(PERLASM_SCHEME)
+GENERATE[cmllt4-sparcv9.S]=asm/cmllt4-sparcv9.pl $(PERLASM_SCHEME)
+INCLUDE[cmllt4-sparcv9.o]=..
+DEPEND[cmllt4-sparcv9.S]=../perlasm/sparcv9_modes.pl
diff --git a/crypto/camellia/camellia.c b/crypto/camellia/camellia.c
new file mode 100644
index 000000000..6641a6220
--- /dev/null
+++ b/crypto/camellia/camellia.c
@@ -0,0 +1,541 @@
+/*
+ * Copyright 2006-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/* ====================================================================
+ * Copyright 2006 NTT (Nippon Telegraph and Telephone Corporation) .
+ * ALL RIGHTS RESERVED.
+ *
+ * Intellectual Property information for Camellia:
+ *     http://info.isl.ntt.co.jp/crypt/eng/info/chiteki.html
+ *
+ * News Release for Announcement of Camellia open source:
+ *     http://www.ntt.co.jp/news/news06e/0604/060413a.html
+ *
+ * The Camellia Code included herein is developed by
+ * NTT (Nippon Telegraph and Telephone Corporation), and is contributed
+ * to the OpenSSL project.
+ */
+
+/*
+ * Algorithm Specification
+ * http://info.isl.ntt.co.jp/crypt/eng/camellia/specifications.html
+ */
+
+/*
+ * This release balances code size and performance. In particular key
+ * schedule setup is fully unrolled, because doing so *significantly*
+ * reduces amount of instructions per setup round and code increase is
+ * justifiable. In block functions on the other hand only inner loops
+ * are unrolled, as full unroll gives only nominal performance boost,
+ * while code size grows 4 or 7 times. Also, unlike previous versions
+ * this one "encourages" compiler to keep intermediate variables in
+ * registers, which should give better "all round" results, in other
+ * words reasonable performance even with not so modern compilers.
+ */
+
+#include 
+#include "cmll_locl.h"
+#include 
+#include 
+
+/* 32-bit rotations */
+#if !defined(PEDANTIC) && !defined(OPENSSL_NO_ASM) && !defined(OPENSSL_NO_INLINE_ASM)
+# if defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_AMD64) || defined(_M_X64))
+#  define RightRotate(x, s) _lrotr(x, s)
+#  define LeftRotate(x, s)  _lrotl(x, s)
+#  if _MSC_VER >= 1400
+#   define SWAP(x) _byteswap_ulong(x)
+#  else
+#   define SWAP(x) (_lrotl(x, 8) & 0x00ff00ff | _lrotr(x, 8) & 0xff00ff00)
+#  endif
+#  define GETU32(p)   SWAP(*((u32 *)(p)))
+#  define PUTU32(p,v) (*((u32 *)(p)) = SWAP((v)))
+# elif defined(__GNUC__) && __GNUC__>=2
+#  if defined(__i386) || defined(__x86_64)
+#   define RightRotate(x,s) ({u32 ret; asm ("rorl %1,%0":"=r"(ret):"I"(s),"0"(x):"cc"); ret; })
+#   define LeftRotate(x,s)  ({u32 ret; asm ("roll %1,%0":"=r"(ret):"I"(s),"0"(x):"cc"); ret; })
+#   if defined(B_ENDIAN)        /* stratus.com does it */
+#    define GETU32(p)   (*(u32 *)(p))
+#    define PUTU32(p,v) (*(u32 *)(p)=(v))
+#   else
+#    define GETU32(p)   ({u32 r=*(const u32 *)(p); asm("bswapl %0":"=r"(r):"0"(r)); r; })
+#    define PUTU32(p,v) ({u32 r=(v); asm("bswapl %0":"=r"(r):"0"(r)); *(u32 *)(p)=r; })
+#   endif
+#  elif defined(_ARCH_PPC) || defined(_ARCH_PPC64) || \
+        defined(__powerpc) || defined(__ppc__) || defined(__powerpc64__)
+#   define LeftRotate(x,s)  ({u32 ret; asm ("rlwinm %0,%1,%2,0,31":"=r"(ret):"r"(x),"I"(s)); ret; })
+#   define RightRotate(x,s) LeftRotate(x,(32-s))
+#  elif defined(__s390x__)
+#   define LeftRotate(x,s)  ({u32 ret; asm ("rll %0,%1,%2":"=r"(ret):"r"(x),"I"(s)); ret; })
+#   define RightRotate(x,s) LeftRotate(x,(32-s))
+#   define GETU32(p)   (*(u32 *)(p))
+#   define PUTU32(p,v) (*(u32 *)(p)=(v))
+#  endif
+# endif
+#endif
+
+#if !defined(RightRotate) && !defined(LeftRotate)
+# define RightRotate(x, s) ( ((x) >> (s)) + ((x) << (32 - s)) )
+# define LeftRotate(x, s)  ( ((x) << (s)) + ((x) >> (32 - s)) )
+#endif
+
+#if !defined(GETU32) && !defined(PUTU32)
+# define GETU32(p)   (((u32)(p)[0] << 24) ^ ((u32)(p)[1] << 16) ^ ((u32)(p)[2] <<  8) ^ ((u32)(p)[3]))
+# define PUTU32(p,v) ((p)[0] = (u8)((v) >> 24), (p)[1] = (u8)((v) >> 16), (p)[2] = (u8)((v) >>  8), (p)[3] = (u8)(v))
+#endif
+
+/* S-box data */
+#define SBOX1_1110 Camellia_SBOX[0]
+#define SBOX4_4404 Camellia_SBOX[1]
+#define SBOX2_0222 Camellia_SBOX[2]
+#define SBOX3_3033 Camellia_SBOX[3]
+static const u32 Camellia_SBOX[][256] = {
+    {0x70707000, 0x82828200, 0x2c2c2c00, 0xececec00, 0xb3b3b300, 0x27272700,
+     0xc0c0c000, 0xe5e5e500, 0xe4e4e400, 0x85858500, 0x57575700, 0x35353500,
+     0xeaeaea00, 0x0c0c0c00, 0xaeaeae00, 0x41414100, 0x23232300, 0xefefef00,
+     0x6b6b6b00, 0x93939300, 0x45454500, 0x19191900, 0xa5a5a500, 0x21212100,
+     0xededed00, 0x0e0e0e00, 0x4f4f4f00, 0x4e4e4e00, 0x1d1d1d00, 0x65656500,
+     0x92929200, 0xbdbdbd00, 0x86868600, 0xb8b8b800, 0xafafaf00, 0x8f8f8f00,
+     0x7c7c7c00, 0xebebeb00, 0x1f1f1f00, 0xcecece00, 0x3e3e3e00, 0x30303000,
+     0xdcdcdc00, 0x5f5f5f00, 0x5e5e5e00, 0xc5c5c500, 0x0b0b0b00, 0x1a1a1a00,
+     0xa6a6a600, 0xe1e1e100, 0x39393900, 0xcacaca00, 0xd5d5d500, 0x47474700,
+     0x5d5d5d00, 0x3d3d3d00, 0xd9d9d900, 0x01010100, 0x5a5a5a00, 0xd6d6d600,
+     0x51515100, 0x56565600, 0x6c6c6c00, 0x4d4d4d00, 0x8b8b8b00, 0x0d0d0d00,
+     0x9a9a9a00, 0x66666600, 0xfbfbfb00, 0xcccccc00, 0xb0b0b000, 0x2d2d2d00,
+     0x74747400, 0x12121200, 0x2b2b2b00, 0x20202000, 0xf0f0f000, 0xb1b1b100,
+     0x84848400, 0x99999900, 0xdfdfdf00, 0x4c4c4c00, 0xcbcbcb00, 0xc2c2c200,
+     0x34343400, 0x7e7e7e00, 0x76767600, 0x05050500, 0x6d6d6d00, 0xb7b7b700,
+     0xa9a9a900, 0x31313100, 0xd1d1d100, 0x17171700, 0x04040400, 0xd7d7d700,
+     0x14141400, 0x58585800, 0x3a3a3a00, 0x61616100, 0xdedede00, 0x1b1b1b00,
+     0x11111100, 0x1c1c1c00, 0x32323200, 0x0f0f0f00, 0x9c9c9c00, 0x16161600,
+     0x53535300, 0x18181800, 0xf2f2f200, 0x22222200, 0xfefefe00, 0x44444400,
+     0xcfcfcf00, 0xb2b2b200, 0xc3c3c300, 0xb5b5b500, 0x7a7a7a00, 0x91919100,
+     0x24242400, 0x08080800, 0xe8e8e800, 0xa8a8a800, 0x60606000, 0xfcfcfc00,
+     0x69696900, 0x50505000, 0xaaaaaa00, 0xd0d0d000, 0xa0a0a000, 0x7d7d7d00,
+     0xa1a1a100, 0x89898900, 0x62626200, 0x97979700, 0x54545400, 0x5b5b5b00,
+     0x1e1e1e00, 0x95959500, 0xe0e0e000, 0xffffff00, 0x64646400, 0xd2d2d200,
+     0x10101000, 0xc4c4c400, 0x00000000, 0x48484800, 0xa3a3a300, 0xf7f7f700,
+     0x75757500, 0xdbdbdb00, 0x8a8a8a00, 0x03030300, 0xe6e6e600, 0xdadada00,
+     0x09090900, 0x3f3f3f00, 0xdddddd00, 0x94949400, 0x87878700, 0x5c5c5c00,
+     0x83838300, 0x02020200, 0xcdcdcd00, 0x4a4a4a00, 0x90909000, 0x33333300,
+     0x73737300, 0x67676700, 0xf6f6f600, 0xf3f3f300, 0x9d9d9d00, 0x7f7f7f00,
+     0xbfbfbf00, 0xe2e2e200, 0x52525200, 0x9b9b9b00, 0xd8d8d800, 0x26262600,
+     0xc8c8c800, 0x37373700, 0xc6c6c600, 0x3b3b3b00, 0x81818100, 0x96969600,
+     0x6f6f6f00, 0x4b4b4b00, 0x13131300, 0xbebebe00, 0x63636300, 0x2e2e2e00,
+     0xe9e9e900, 0x79797900, 0xa7a7a700, 0x8c8c8c00, 0x9f9f9f00, 0x6e6e6e00,
+     0xbcbcbc00, 0x8e8e8e00, 0x29292900, 0xf5f5f500, 0xf9f9f900, 0xb6b6b600,
+     0x2f2f2f00, 0xfdfdfd00, 0xb4b4b400, 0x59595900, 0x78787800, 0x98989800,
+     0x06060600, 0x6a6a6a00, 0xe7e7e700, 0x46464600, 0x71717100, 0xbababa00,
+     0xd4d4d400, 0x25252500, 0xababab00, 0x42424200, 0x88888800, 0xa2a2a200,
+     0x8d8d8d00, 0xfafafa00, 0x72727200, 0x07070700, 0xb9b9b900, 0x55555500,
+     0xf8f8f800, 0xeeeeee00, 0xacacac00, 0x0a0a0a00, 0x36363600, 0x49494900,
+     0x2a2a2a00, 0x68686800, 0x3c3c3c00, 0x38383800, 0xf1f1f100, 0xa4a4a400,
+     0x40404000, 0x28282800, 0xd3d3d300, 0x7b7b7b00, 0xbbbbbb00, 0xc9c9c900,
+     0x43434300, 0xc1c1c100, 0x15151500, 0xe3e3e300, 0xadadad00, 0xf4f4f400,
+     0x77777700, 0xc7c7c700, 0x80808000, 0x9e9e9e00},
+    {0x70700070, 0x2c2c002c, 0xb3b300b3, 0xc0c000c0, 0xe4e400e4, 0x57570057,
+     0xeaea00ea, 0xaeae00ae, 0x23230023, 0x6b6b006b, 0x45450045, 0xa5a500a5,
+     0xeded00ed, 0x4f4f004f, 0x1d1d001d, 0x92920092, 0x86860086, 0xafaf00af,
+     0x7c7c007c, 0x1f1f001f, 0x3e3e003e, 0xdcdc00dc, 0x5e5e005e, 0x0b0b000b,
+     0xa6a600a6, 0x39390039, 0xd5d500d5, 0x5d5d005d, 0xd9d900d9, 0x5a5a005a,
+     0x51510051, 0x6c6c006c, 0x8b8b008b, 0x9a9a009a, 0xfbfb00fb, 0xb0b000b0,
+     0x74740074, 0x2b2b002b, 0xf0f000f0, 0x84840084, 0xdfdf00df, 0xcbcb00cb,
+     0x34340034, 0x76760076, 0x6d6d006d, 0xa9a900a9, 0xd1d100d1, 0x04040004,
+     0x14140014, 0x3a3a003a, 0xdede00de, 0x11110011, 0x32320032, 0x9c9c009c,
+     0x53530053, 0xf2f200f2, 0xfefe00fe, 0xcfcf00cf, 0xc3c300c3, 0x7a7a007a,
+     0x24240024, 0xe8e800e8, 0x60600060, 0x69690069, 0xaaaa00aa, 0xa0a000a0,
+     0xa1a100a1, 0x62620062, 0x54540054, 0x1e1e001e, 0xe0e000e0, 0x64640064,
+     0x10100010, 0x00000000, 0xa3a300a3, 0x75750075, 0x8a8a008a, 0xe6e600e6,
+     0x09090009, 0xdddd00dd, 0x87870087, 0x83830083, 0xcdcd00cd, 0x90900090,
+     0x73730073, 0xf6f600f6, 0x9d9d009d, 0xbfbf00bf, 0x52520052, 0xd8d800d8,
+     0xc8c800c8, 0xc6c600c6, 0x81810081, 0x6f6f006f, 0x13130013, 0x63630063,
+     0xe9e900e9, 0xa7a700a7, 0x9f9f009f, 0xbcbc00bc, 0x29290029, 0xf9f900f9,
+     0x2f2f002f, 0xb4b400b4, 0x78780078, 0x06060006, 0xe7e700e7, 0x71710071,
+     0xd4d400d4, 0xabab00ab, 0x88880088, 0x8d8d008d, 0x72720072, 0xb9b900b9,
+     0xf8f800f8, 0xacac00ac, 0x36360036, 0x2a2a002a, 0x3c3c003c, 0xf1f100f1,
+     0x40400040, 0xd3d300d3, 0xbbbb00bb, 0x43430043, 0x15150015, 0xadad00ad,
+     0x77770077, 0x80800080, 0x82820082, 0xecec00ec, 0x27270027, 0xe5e500e5,
+     0x85850085, 0x35350035, 0x0c0c000c, 0x41410041, 0xefef00ef, 0x93930093,
+     0x19190019, 0x21210021, 0x0e0e000e, 0x4e4e004e, 0x65650065, 0xbdbd00bd,
+     0xb8b800b8, 0x8f8f008f, 0xebeb00eb, 0xcece00ce, 0x30300030, 0x5f5f005f,
+     0xc5c500c5, 0x1a1a001a, 0xe1e100e1, 0xcaca00ca, 0x47470047, 0x3d3d003d,
+     0x01010001, 0xd6d600d6, 0x56560056, 0x4d4d004d, 0x0d0d000d, 0x66660066,
+     0xcccc00cc, 0x2d2d002d, 0x12120012, 0x20200020, 0xb1b100b1, 0x99990099,
+     0x4c4c004c, 0xc2c200c2, 0x7e7e007e, 0x05050005, 0xb7b700b7, 0x31310031,
+     0x17170017, 0xd7d700d7, 0x58580058, 0x61610061, 0x1b1b001b, 0x1c1c001c,
+     0x0f0f000f, 0x16160016, 0x18180018, 0x22220022, 0x44440044, 0xb2b200b2,
+     0xb5b500b5, 0x91910091, 0x08080008, 0xa8a800a8, 0xfcfc00fc, 0x50500050,
+     0xd0d000d0, 0x7d7d007d, 0x89890089, 0x97970097, 0x5b5b005b, 0x95950095,
+     0xffff00ff, 0xd2d200d2, 0xc4c400c4, 0x48480048, 0xf7f700f7, 0xdbdb00db,
+     0x03030003, 0xdada00da, 0x3f3f003f, 0x94940094, 0x5c5c005c, 0x02020002,
+     0x4a4a004a, 0x33330033, 0x67670067, 0xf3f300f3, 0x7f7f007f, 0xe2e200e2,
+     0x9b9b009b, 0x26260026, 0x37370037, 0x3b3b003b, 0x96960096, 0x4b4b004b,
+     0xbebe00be, 0x2e2e002e, 0x79790079, 0x8c8c008c, 0x6e6e006e, 0x8e8e008e,
+     0xf5f500f5, 0xb6b600b6, 0xfdfd00fd, 0x59590059, 0x98980098, 0x6a6a006a,
+     0x46460046, 0xbaba00ba, 0x25250025, 0x42420042, 0xa2a200a2, 0xfafa00fa,
+     0x07070007, 0x55550055, 0xeeee00ee, 0x0a0a000a, 0x49490049, 0x68680068,
+     0x38380038, 0xa4a400a4, 0x28280028, 0x7b7b007b, 0xc9c900c9, 0xc1c100c1,
+     0xe3e300e3, 0xf4f400f4, 0xc7c700c7, 0x9e9e009e},
+    {0x00e0e0e0, 0x00050505, 0x00585858, 0x00d9d9d9, 0x00676767, 0x004e4e4e,
+     0x00818181, 0x00cbcbcb, 0x00c9c9c9, 0x000b0b0b, 0x00aeaeae, 0x006a6a6a,
+     0x00d5d5d5, 0x00181818, 0x005d5d5d, 0x00828282, 0x00464646, 0x00dfdfdf,
+     0x00d6d6d6, 0x00272727, 0x008a8a8a, 0x00323232, 0x004b4b4b, 0x00424242,
+     0x00dbdbdb, 0x001c1c1c, 0x009e9e9e, 0x009c9c9c, 0x003a3a3a, 0x00cacaca,
+     0x00252525, 0x007b7b7b, 0x000d0d0d, 0x00717171, 0x005f5f5f, 0x001f1f1f,
+     0x00f8f8f8, 0x00d7d7d7, 0x003e3e3e, 0x009d9d9d, 0x007c7c7c, 0x00606060,
+     0x00b9b9b9, 0x00bebebe, 0x00bcbcbc, 0x008b8b8b, 0x00161616, 0x00343434,
+     0x004d4d4d, 0x00c3c3c3, 0x00727272, 0x00959595, 0x00ababab, 0x008e8e8e,
+     0x00bababa, 0x007a7a7a, 0x00b3b3b3, 0x00020202, 0x00b4b4b4, 0x00adadad,
+     0x00a2a2a2, 0x00acacac, 0x00d8d8d8, 0x009a9a9a, 0x00171717, 0x001a1a1a,
+     0x00353535, 0x00cccccc, 0x00f7f7f7, 0x00999999, 0x00616161, 0x005a5a5a,
+     0x00e8e8e8, 0x00242424, 0x00565656, 0x00404040, 0x00e1e1e1, 0x00636363,
+     0x00090909, 0x00333333, 0x00bfbfbf, 0x00989898, 0x00979797, 0x00858585,
+     0x00686868, 0x00fcfcfc, 0x00ececec, 0x000a0a0a, 0x00dadada, 0x006f6f6f,
+     0x00535353, 0x00626262, 0x00a3a3a3, 0x002e2e2e, 0x00080808, 0x00afafaf,
+     0x00282828, 0x00b0b0b0, 0x00747474, 0x00c2c2c2, 0x00bdbdbd, 0x00363636,
+     0x00222222, 0x00383838, 0x00646464, 0x001e1e1e, 0x00393939, 0x002c2c2c,
+     0x00a6a6a6, 0x00303030, 0x00e5e5e5, 0x00444444, 0x00fdfdfd, 0x00888888,
+     0x009f9f9f, 0x00656565, 0x00878787, 0x006b6b6b, 0x00f4f4f4, 0x00232323,
+     0x00484848, 0x00101010, 0x00d1d1d1, 0x00515151, 0x00c0c0c0, 0x00f9f9f9,
+     0x00d2d2d2, 0x00a0a0a0, 0x00555555, 0x00a1a1a1, 0x00414141, 0x00fafafa,
+     0x00434343, 0x00131313, 0x00c4c4c4, 0x002f2f2f, 0x00a8a8a8, 0x00b6b6b6,
+     0x003c3c3c, 0x002b2b2b, 0x00c1c1c1, 0x00ffffff, 0x00c8c8c8, 0x00a5a5a5,
+     0x00202020, 0x00898989, 0x00000000, 0x00909090, 0x00474747, 0x00efefef,
+     0x00eaeaea, 0x00b7b7b7, 0x00151515, 0x00060606, 0x00cdcdcd, 0x00b5b5b5,
+     0x00121212, 0x007e7e7e, 0x00bbbbbb, 0x00292929, 0x000f0f0f, 0x00b8b8b8,
+     0x00070707, 0x00040404, 0x009b9b9b, 0x00949494, 0x00212121, 0x00666666,
+     0x00e6e6e6, 0x00cecece, 0x00ededed, 0x00e7e7e7, 0x003b3b3b, 0x00fefefe,
+     0x007f7f7f, 0x00c5c5c5, 0x00a4a4a4, 0x00373737, 0x00b1b1b1, 0x004c4c4c,
+     0x00919191, 0x006e6e6e, 0x008d8d8d, 0x00767676, 0x00030303, 0x002d2d2d,
+     0x00dedede, 0x00969696, 0x00262626, 0x007d7d7d, 0x00c6c6c6, 0x005c5c5c,
+     0x00d3d3d3, 0x00f2f2f2, 0x004f4f4f, 0x00191919, 0x003f3f3f, 0x00dcdcdc,
+     0x00797979, 0x001d1d1d, 0x00525252, 0x00ebebeb, 0x00f3f3f3, 0x006d6d6d,
+     0x005e5e5e, 0x00fbfbfb, 0x00696969, 0x00b2b2b2, 0x00f0f0f0, 0x00313131,
+     0x000c0c0c, 0x00d4d4d4, 0x00cfcfcf, 0x008c8c8c, 0x00e2e2e2, 0x00757575,
+     0x00a9a9a9, 0x004a4a4a, 0x00575757, 0x00848484, 0x00111111, 0x00454545,
+     0x001b1b1b, 0x00f5f5f5, 0x00e4e4e4, 0x000e0e0e, 0x00737373, 0x00aaaaaa,
+     0x00f1f1f1, 0x00dddddd, 0x00595959, 0x00141414, 0x006c6c6c, 0x00929292,
+     0x00545454, 0x00d0d0d0, 0x00787878, 0x00707070, 0x00e3e3e3, 0x00494949,
+     0x00808080, 0x00505050, 0x00a7a7a7, 0x00f6f6f6, 0x00777777, 0x00939393,
+     0x00868686, 0x00838383, 0x002a2a2a, 0x00c7c7c7, 0x005b5b5b, 0x00e9e9e9,
+     0x00eeeeee, 0x008f8f8f, 0x00010101, 0x003d3d3d},
+    {0x38003838, 0x41004141, 0x16001616, 0x76007676, 0xd900d9d9, 0x93009393,
+     0x60006060, 0xf200f2f2, 0x72007272, 0xc200c2c2, 0xab00abab, 0x9a009a9a,
+     0x75007575, 0x06000606, 0x57005757, 0xa000a0a0, 0x91009191, 0xf700f7f7,
+     0xb500b5b5, 0xc900c9c9, 0xa200a2a2, 0x8c008c8c, 0xd200d2d2, 0x90009090,
+     0xf600f6f6, 0x07000707, 0xa700a7a7, 0x27002727, 0x8e008e8e, 0xb200b2b2,
+     0x49004949, 0xde00dede, 0x43004343, 0x5c005c5c, 0xd700d7d7, 0xc700c7c7,
+     0x3e003e3e, 0xf500f5f5, 0x8f008f8f, 0x67006767, 0x1f001f1f, 0x18001818,
+     0x6e006e6e, 0xaf00afaf, 0x2f002f2f, 0xe200e2e2, 0x85008585, 0x0d000d0d,
+     0x53005353, 0xf000f0f0, 0x9c009c9c, 0x65006565, 0xea00eaea, 0xa300a3a3,
+     0xae00aeae, 0x9e009e9e, 0xec00ecec, 0x80008080, 0x2d002d2d, 0x6b006b6b,
+     0xa800a8a8, 0x2b002b2b, 0x36003636, 0xa600a6a6, 0xc500c5c5, 0x86008686,
+     0x4d004d4d, 0x33003333, 0xfd00fdfd, 0x66006666, 0x58005858, 0x96009696,
+     0x3a003a3a, 0x09000909, 0x95009595, 0x10001010, 0x78007878, 0xd800d8d8,
+     0x42004242, 0xcc00cccc, 0xef00efef, 0x26002626, 0xe500e5e5, 0x61006161,
+     0x1a001a1a, 0x3f003f3f, 0x3b003b3b, 0x82008282, 0xb600b6b6, 0xdb00dbdb,
+     0xd400d4d4, 0x98009898, 0xe800e8e8, 0x8b008b8b, 0x02000202, 0xeb00ebeb,
+     0x0a000a0a, 0x2c002c2c, 0x1d001d1d, 0xb000b0b0, 0x6f006f6f, 0x8d008d8d,
+     0x88008888, 0x0e000e0e, 0x19001919, 0x87008787, 0x4e004e4e, 0x0b000b0b,
+     0xa900a9a9, 0x0c000c0c, 0x79007979, 0x11001111, 0x7f007f7f, 0x22002222,
+     0xe700e7e7, 0x59005959, 0xe100e1e1, 0xda00dada, 0x3d003d3d, 0xc800c8c8,
+     0x12001212, 0x04000404, 0x74007474, 0x54005454, 0x30003030, 0x7e007e7e,
+     0xb400b4b4, 0x28002828, 0x55005555, 0x68006868, 0x50005050, 0xbe00bebe,
+     0xd000d0d0, 0xc400c4c4, 0x31003131, 0xcb00cbcb, 0x2a002a2a, 0xad00adad,
+     0x0f000f0f, 0xca00caca, 0x70007070, 0xff00ffff, 0x32003232, 0x69006969,
+     0x08000808, 0x62006262, 0x00000000, 0x24002424, 0xd100d1d1, 0xfb00fbfb,
+     0xba00baba, 0xed00eded, 0x45004545, 0x81008181, 0x73007373, 0x6d006d6d,
+     0x84008484, 0x9f009f9f, 0xee00eeee, 0x4a004a4a, 0xc300c3c3, 0x2e002e2e,
+     0xc100c1c1, 0x01000101, 0xe600e6e6, 0x25002525, 0x48004848, 0x99009999,
+     0xb900b9b9, 0xb300b3b3, 0x7b007b7b, 0xf900f9f9, 0xce00cece, 0xbf00bfbf,
+     0xdf00dfdf, 0x71007171, 0x29002929, 0xcd00cdcd, 0x6c006c6c, 0x13001313,
+     0x64006464, 0x9b009b9b, 0x63006363, 0x9d009d9d, 0xc000c0c0, 0x4b004b4b,
+     0xb700b7b7, 0xa500a5a5, 0x89008989, 0x5f005f5f, 0xb100b1b1, 0x17001717,
+     0xf400f4f4, 0xbc00bcbc, 0xd300d3d3, 0x46004646, 0xcf00cfcf, 0x37003737,
+     0x5e005e5e, 0x47004747, 0x94009494, 0xfa00fafa, 0xfc00fcfc, 0x5b005b5b,
+     0x97009797, 0xfe00fefe, 0x5a005a5a, 0xac00acac, 0x3c003c3c, 0x4c004c4c,
+     0x03000303, 0x35003535, 0xf300f3f3, 0x23002323, 0xb800b8b8, 0x5d005d5d,
+     0x6a006a6a, 0x92009292, 0xd500d5d5, 0x21002121, 0x44004444, 0x51005151,
+     0xc600c6c6, 0x7d007d7d, 0x39003939, 0x83008383, 0xdc00dcdc, 0xaa00aaaa,
+     0x7c007c7c, 0x77007777, 0x56005656, 0x05000505, 0x1b001b1b, 0xa400a4a4,
+     0x15001515, 0x34003434, 0x1e001e1e, 0x1c001c1c, 0xf800f8f8, 0x52005252,
+     0x20002020, 0x14001414, 0xe900e9e9, 0xbd00bdbd, 0xdd00dddd, 0xe400e4e4,
+     0xa100a1a1, 0xe000e0e0, 0x8a008a8a, 0xf100f1f1, 0xd600d6d6, 0x7a007a7a,
+     0xbb00bbbb, 0xe300e3e3, 0x40004040, 0x4f004f4f}
+};
+
+/* Key generation constants */
+static const u32 SIGMA[] = {
+    0xa09e667f, 0x3bcc908b, 0xb67ae858, 0x4caa73b2, 0xc6ef372f, 0xe94f82be,
+    0x54ff53a5, 0xf1d36f1c, 0x10e527fa, 0xde682d1d, 0xb05688c2, 0xb3e6c1fd
+};
+
+/* The phi algorithm given in C.2.7 of the Camellia spec document. */
+/*
+ * This version does not attempt to minimize amount of temporary
+ * variables, but instead explicitly exposes algorithm's parallelism.
+ * It is therefore most appropriate for platforms with not less than
+ * ~16 registers. For platforms with less registers [well, x86 to be
+ * specific] assembler version should be/is provided anyway...
+ */
+#define Camellia_Feistel(_s0,_s1,_s2,_s3,_key) do {\
+        register u32 _t0,_t1,_t2,_t3;\
+\
+        _t0  = _s0 ^ (_key)[0];\
+        _t3  = SBOX4_4404[_t0&0xff];\
+        _t1  = _s1 ^ (_key)[1];\
+        _t3 ^= SBOX3_3033[(_t0 >> 8)&0xff];\
+        _t2  = SBOX1_1110[_t1&0xff];\
+        _t3 ^= SBOX2_0222[(_t0 >> 16)&0xff];\
+        _t2 ^= SBOX4_4404[(_t1 >> 8)&0xff];\
+        _t3 ^= SBOX1_1110[(_t0 >> 24)];\
+        _t2 ^= _t3;\
+        _t3  = RightRotate(_t3,8);\
+        _t2 ^= SBOX3_3033[(_t1 >> 16)&0xff];\
+        _s3 ^= _t3;\
+        _t2 ^= SBOX2_0222[(_t1 >> 24)];\
+        _s2 ^= _t2; \
+        _s3 ^= _t2;\
+} while(0)
+
+/*
+ * Note that n has to be less than 32. Rotations for larger amount
+ * of bits are achieved by "rotating" order of s-elements and
+ * adjusting n accordingly, e.g. RotLeft128(s1,s2,s3,s0,n-32).
+ */
+#define RotLeft128(_s0,_s1,_s2,_s3,_n) do {\
+        u32 _t0=_s0>>(32-_n);\
+        _s0 = (_s0<<_n) | (_s1>>(32-_n));\
+        _s1 = (_s1<<_n) | (_s2>>(32-_n));\
+        _s2 = (_s2<<_n) | (_s3>>(32-_n));\
+        _s3 = (_s3<<_n) | _t0;\
+} while (0)
+
+int Camellia_Ekeygen(int keyBitLength, const u8 *rawKey, KEY_TABLE_TYPE k)
+{
+    register u32 s0, s1, s2, s3;
+
+    k[0] = s0 = GETU32(rawKey);
+    k[1] = s1 = GETU32(rawKey + 4);
+    k[2] = s2 = GETU32(rawKey + 8);
+    k[3] = s3 = GETU32(rawKey + 12);
+
+    if (keyBitLength != 128) {
+        k[8] = s0 = GETU32(rawKey + 16);
+        k[9] = s1 = GETU32(rawKey + 20);
+        if (keyBitLength == 192) {
+            k[10] = s2 = ~s0;
+            k[11] = s3 = ~s1;
+        } else {
+            k[10] = s2 = GETU32(rawKey + 24);
+            k[11] = s3 = GETU32(rawKey + 28);
+        }
+        s0 ^= k[0], s1 ^= k[1], s2 ^= k[2], s3 ^= k[3];
+    }
+
+    /* Use the Feistel routine to scramble the key material */
+    Camellia_Feistel(s0, s1, s2, s3, SIGMA + 0);
+    Camellia_Feistel(s2, s3, s0, s1, SIGMA + 2);
+
+    s0 ^= k[0], s1 ^= k[1], s2 ^= k[2], s3 ^= k[3];
+    Camellia_Feistel(s0, s1, s2, s3, SIGMA + 4);
+    Camellia_Feistel(s2, s3, s0, s1, SIGMA + 6);
+
+    /* Fill the keyTable. Requires many block rotations. */
+    if (keyBitLength == 128) {
+        k[4] = s0, k[5] = s1, k[6] = s2, k[7] = s3;
+        RotLeft128(s0, s1, s2, s3, 15); /* KA <<< 15 */
+        k[12] = s0, k[13] = s1, k[14] = s2, k[15] = s3;
+        RotLeft128(s0, s1, s2, s3, 15); /* KA <<< 30 */
+        k[16] = s0, k[17] = s1, k[18] = s2, k[19] = s3;
+        RotLeft128(s0, s1, s2, s3, 15); /* KA <<< 45 */
+        k[24] = s0, k[25] = s1;
+        RotLeft128(s0, s1, s2, s3, 15); /* KA <<< 60 */
+        k[28] = s0, k[29] = s1, k[30] = s2, k[31] = s3;
+        RotLeft128(s1, s2, s3, s0, 2); /* KA <<< 94 */
+        k[40] = s1, k[41] = s2, k[42] = s3, k[43] = s0;
+        RotLeft128(s1, s2, s3, s0, 17); /* KA <<<111 */
+        k[48] = s1, k[49] = s2, k[50] = s3, k[51] = s0;
+
+        s0 = k[0], s1 = k[1], s2 = k[2], s3 = k[3];
+        RotLeft128(s0, s1, s2, s3, 15); /* KL <<< 15 */
+        k[8] = s0, k[9] = s1, k[10] = s2, k[11] = s3;
+        RotLeft128(s0, s1, s2, s3, 30); /* KL <<< 45 */
+        k[20] = s0, k[21] = s1, k[22] = s2, k[23] = s3;
+        RotLeft128(s0, s1, s2, s3, 15); /* KL <<< 60 */
+        k[26] = s2, k[27] = s3;
+        RotLeft128(s0, s1, s2, s3, 17); /* KL <<< 77 */
+        k[32] = s0, k[33] = s1, k[34] = s2, k[35] = s3;
+        RotLeft128(s0, s1, s2, s3, 17); /* KL <<< 94 */
+        k[36] = s0, k[37] = s1, k[38] = s2, k[39] = s3;
+        RotLeft128(s0, s1, s2, s3, 17); /* KL <<<111 */
+        k[44] = s0, k[45] = s1, k[46] = s2, k[47] = s3;
+
+        return 3;               /* grand rounds */
+    } else {
+        k[12] = s0, k[13] = s1, k[14] = s2, k[15] = s3;
+        s0 ^= k[8], s1 ^= k[9], s2 ^= k[10], s3 ^= k[11];
+        Camellia_Feistel(s0, s1, s2, s3, (SIGMA + 8));
+        Camellia_Feistel(s2, s3, s0, s1, (SIGMA + 10));
+
+        k[4] = s0, k[5] = s1, k[6] = s2, k[7] = s3;
+        RotLeft128(s0, s1, s2, s3, 30); /* KB <<< 30 */
+        k[20] = s0, k[21] = s1, k[22] = s2, k[23] = s3;
+        RotLeft128(s0, s1, s2, s3, 30); /* KB <<< 60 */
+        k[40] = s0, k[41] = s1, k[42] = s2, k[43] = s3;
+        RotLeft128(s1, s2, s3, s0, 19); /* KB <<<111 */
+        k[64] = s1, k[65] = s2, k[66] = s3, k[67] = s0;
+
+        s0 = k[8], s1 = k[9], s2 = k[10], s3 = k[11];
+        RotLeft128(s0, s1, s2, s3, 15); /* KR <<< 15 */
+        k[8] = s0, k[9] = s1, k[10] = s2, k[11] = s3;
+        RotLeft128(s0, s1, s2, s3, 15); /* KR <<< 30 */
+        k[16] = s0, k[17] = s1, k[18] = s2, k[19] = s3;
+        RotLeft128(s0, s1, s2, s3, 30); /* KR <<< 60 */
+        k[36] = s0, k[37] = s1, k[38] = s2, k[39] = s3;
+        RotLeft128(s1, s2, s3, s0, 2); /* KR <<< 94 */
+        k[52] = s1, k[53] = s2, k[54] = s3, k[55] = s0;
+
+        s0 = k[12], s1 = k[13], s2 = k[14], s3 = k[15];
+        RotLeft128(s0, s1, s2, s3, 15); /* KA <<< 15 */
+        k[12] = s0, k[13] = s1, k[14] = s2, k[15] = s3;
+        RotLeft128(s0, s1, s2, s3, 30); /* KA <<< 45 */
+        k[28] = s0, k[29] = s1, k[30] = s2, k[31] = s3;
+        /* KA <<< 77 */
+        k[48] = s1, k[49] = s2, k[50] = s3, k[51] = s0;
+        RotLeft128(s1, s2, s3, s0, 17); /* KA <<< 94 */
+        k[56] = s1, k[57] = s2, k[58] = s3, k[59] = s0;
+
+        s0 = k[0], s1 = k[1], s2 = k[2], s3 = k[3];
+        RotLeft128(s1, s2, s3, s0, 13); /* KL <<< 45 */
+        k[24] = s1, k[25] = s2, k[26] = s3, k[27] = s0;
+        RotLeft128(s1, s2, s3, s0, 15); /* KL <<< 60 */
+        k[32] = s1, k[33] = s2, k[34] = s3, k[35] = s0;
+        RotLeft128(s1, s2, s3, s0, 17); /* KL <<< 77 */
+        k[44] = s1, k[45] = s2, k[46] = s3, k[47] = s0;
+        RotLeft128(s2, s3, s0, s1, 2); /* KL <<<111 */
+        k[60] = s2, k[61] = s3, k[62] = s0, k[63] = s1;
+
+        return 4;               /* grand rounds */
+    }
+    /*
+     * It is possible to perform certain precalculations, which
+     * would spare few cycles in block procedure. It's not done,
+     * because it upsets the performance balance between key
+     * setup and block procedures, negatively affecting overall
+     * throughput in applications operating on short messages
+     * and volatile keys.
+     */
+}
+
+void Camellia_EncryptBlock_Rounds(int grandRounds, const u8 plaintext[],
+                                  const KEY_TABLE_TYPE keyTable,
+                                  u8 ciphertext[])
+{
+    register u32 s0, s1, s2, s3;
+    const u32 *k = keyTable, *kend = keyTable + grandRounds * 16;
+
+    s0 = GETU32(plaintext) ^ k[0];
+    s1 = GETU32(plaintext + 4) ^ k[1];
+    s2 = GETU32(plaintext + 8) ^ k[2];
+    s3 = GETU32(plaintext + 12) ^ k[3];
+    k += 4;
+
+    while (1) {
+        /* Camellia makes 6 Feistel rounds */
+        Camellia_Feistel(s0, s1, s2, s3, k + 0);
+        Camellia_Feistel(s2, s3, s0, s1, k + 2);
+        Camellia_Feistel(s0, s1, s2, s3, k + 4);
+        Camellia_Feistel(s2, s3, s0, s1, k + 6);
+        Camellia_Feistel(s0, s1, s2, s3, k + 8);
+        Camellia_Feistel(s2, s3, s0, s1, k + 10);
+        k += 12;
+
+        if (k == kend)
+            break;
+
+        /*
+         * This is the same function as the diffusion function D of the
+         * accompanying documentation. See section 3.2 for properties of the
+         * FLlayer function.
+         */
+        s1 ^= LeftRotate(s0 & k[0], 1);
+        s2 ^= s3 | k[3];
+        s0 ^= s1 | k[1];
+        s3 ^= LeftRotate(s2 & k[2], 1);
+        k += 4;
+    }
+
+    s2 ^= k[0], s3 ^= k[1], s0 ^= k[2], s1 ^= k[3];
+
+    PUTU32(ciphertext, s2);
+    PUTU32(ciphertext + 4, s3);
+    PUTU32(ciphertext + 8, s0);
+    PUTU32(ciphertext + 12, s1);
+}
+
+void Camellia_EncryptBlock(int keyBitLength, const u8 plaintext[],
+                           const KEY_TABLE_TYPE keyTable, u8 ciphertext[])
+{
+    Camellia_EncryptBlock_Rounds(keyBitLength == 128 ? 3 : 4,
+                                 plaintext, keyTable, ciphertext);
+}
+
+void Camellia_DecryptBlock_Rounds(int grandRounds, const u8 ciphertext[],
+                                  const KEY_TABLE_TYPE keyTable,
+                                  u8 plaintext[])
+{
+    u32 s0, s1, s2, s3;
+    const u32 *k = keyTable + grandRounds * 16, *kend = keyTable + 4;
+
+    s0 = GETU32(ciphertext) ^ k[0];
+    s1 = GETU32(ciphertext + 4) ^ k[1];
+    s2 = GETU32(ciphertext + 8) ^ k[2];
+    s3 = GETU32(ciphertext + 12) ^ k[3];
+
+    while (1) {
+        /* Camellia makes 6 Feistel rounds */
+        k -= 12;
+        Camellia_Feistel(s0, s1, s2, s3, k + 10);
+        Camellia_Feistel(s2, s3, s0, s1, k + 8);
+        Camellia_Feistel(s0, s1, s2, s3, k + 6);
+        Camellia_Feistel(s2, s3, s0, s1, k + 4);
+        Camellia_Feistel(s0, s1, s2, s3, k + 2);
+        Camellia_Feistel(s2, s3, s0, s1, k + 0);
+
+        if (k == kend)
+            break;
+
+        /*
+         * This is the same function as the diffusion function D of the
+         * accompanying documentation. See section 3.2 for properties of the
+         * FLlayer function.
+         */
+        k -= 4;
+        s1 ^= LeftRotate(s0 & k[2], 1);
+        s2 ^= s3 | k[1];
+        s0 ^= s1 | k[3];
+        s3 ^= LeftRotate(s2 & k[0], 1);
+    }
+
+    k -= 4;
+    s2 ^= k[0], s3 ^= k[1], s0 ^= k[2], s1 ^= k[3];
+
+    PUTU32(plaintext, s2);
+    PUTU32(plaintext + 4, s3);
+    PUTU32(plaintext + 8, s0);
+    PUTU32(plaintext + 12, s1);
+}
+
+void Camellia_DecryptBlock(int keyBitLength, const u8 plaintext[],
+                           const KEY_TABLE_TYPE keyTable, u8 ciphertext[])
+{
+    Camellia_DecryptBlock_Rounds(keyBitLength == 128 ? 3 : 4,
+                                 plaintext, keyTable, ciphertext);
+}
diff --git a/crypto/camellia/cmll_cbc.c b/crypto/camellia/cmll_cbc.c
new file mode 100644
index 000000000..b19171ded
--- /dev/null
+++ b/crypto/camellia/cmll_cbc.c
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2006-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+
+void Camellia_cbc_encrypt(const unsigned char *in, unsigned char *out,
+                          size_t len, const CAMELLIA_KEY *key,
+                          unsigned char *ivec, const int enc)
+{
+
+    if (enc)
+        CRYPTO_cbc128_encrypt(in, out, len, key, ivec,
+                              (block128_f) Camellia_encrypt);
+    else
+        CRYPTO_cbc128_decrypt(in, out, len, key, ivec,
+                              (block128_f) Camellia_decrypt);
+}
diff --git a/crypto/camellia/cmll_cfb.c b/crypto/camellia/cmll_cfb.c
new file mode 100644
index 000000000..4f49eaded
--- /dev/null
+++ b/crypto/camellia/cmll_cfb.c
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2006-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+
+/*
+ * The input and output encrypted as though 128bit cfb mode is being used.
+ * The extra state information to record how much of the 128bit block we have
+ * used is contained in *num;
+ */
+
+void Camellia_cfb128_encrypt(const unsigned char *in, unsigned char *out,
+                             size_t length, const CAMELLIA_KEY *key,
+                             unsigned char *ivec, int *num, const int enc)
+{
+
+    CRYPTO_cfb128_encrypt(in, out, length, key, ivec, num, enc,
+                          (block128_f) Camellia_encrypt);
+}
+
+/* N.B. This expects the input to be packed, MS bit first */
+void Camellia_cfb1_encrypt(const unsigned char *in, unsigned char *out,
+                           size_t length, const CAMELLIA_KEY *key,
+                           unsigned char *ivec, int *num, const int enc)
+{
+    CRYPTO_cfb128_1_encrypt(in, out, length, key, ivec, num, enc,
+                            (block128_f) Camellia_encrypt);
+}
+
+void Camellia_cfb8_encrypt(const unsigned char *in, unsigned char *out,
+                           size_t length, const CAMELLIA_KEY *key,
+                           unsigned char *ivec, int *num, const int enc)
+{
+    CRYPTO_cfb128_8_encrypt(in, out, length, key, ivec, num, enc,
+                            (block128_f) Camellia_encrypt);
+}
diff --git a/crypto/camellia/cmll_ctr.c b/crypto/camellia/cmll_ctr.c
new file mode 100644
index 000000000..161d1e18c
--- /dev/null
+++ b/crypto/camellia/cmll_ctr.c
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2006-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+
+void Camellia_ctr128_encrypt(const unsigned char *in, unsigned char *out,
+                             size_t length, const CAMELLIA_KEY *key,
+                             unsigned char ivec[CAMELLIA_BLOCK_SIZE],
+                             unsigned char ecount_buf[CAMELLIA_BLOCK_SIZE],
+                             unsigned int *num)
+{
+
+    CRYPTO_ctr128_encrypt(in, out, length, key, ivec, ecount_buf, num,
+                          (block128_f) Camellia_encrypt);
+}
diff --git a/crypto/camellia/cmll_ecb.c b/crypto/camellia/cmll_ecb.c
new file mode 100644
index 000000000..d932f1b37
--- /dev/null
+++ b/crypto/camellia/cmll_ecb.c
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2006-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "cmll_locl.h"
+
+void Camellia_ecb_encrypt(const unsigned char *in, unsigned char *out,
+                          const CAMELLIA_KEY *key, const int enc)
+{
+    if (CAMELLIA_ENCRYPT == enc)
+        Camellia_encrypt(in, out, key);
+    else
+        Camellia_decrypt(in, out, key);
+}
diff --git a/crypto/camellia/cmll_locl.h b/crypto/camellia/cmll_locl.h
new file mode 100644
index 000000000..6403b390d
--- /dev/null
+++ b/crypto/camellia/cmll_locl.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2006-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/* ====================================================================
+ * Copyright 2006 NTT (Nippon Telegraph and Telephone Corporation) .
+ * ALL RIGHTS RESERVED.
+ *
+ * Intellectual Property information for Camellia:
+ *     http://info.isl.ntt.co.jp/crypt/eng/info/chiteki.html
+ *
+ * News Release for Announcement of Camellia open source:
+ *     http://www.ntt.co.jp/news/news06e/0604/060413a.html
+ *
+ * The Camellia Code included herein is developed by
+ * NTT (Nippon Telegraph and Telephone Corporation), and is contributed
+ * to the OpenSSL project.
+ */
+
+#ifndef HEADER_CAMELLIA_LOCL_H
+# define HEADER_CAMELLIA_LOCL_H
+
+typedef unsigned int u32;
+typedef unsigned char u8;
+
+int Camellia_Ekeygen(int keyBitLength, const u8 *rawKey,
+                     KEY_TABLE_TYPE keyTable);
+void Camellia_EncryptBlock_Rounds(int grandRounds, const u8 plaintext[],
+                                  const KEY_TABLE_TYPE keyTable,
+                                  u8 ciphertext[]);
+void Camellia_DecryptBlock_Rounds(int grandRounds, const u8 ciphertext[],
+                                  const KEY_TABLE_TYPE keyTable,
+                                  u8 plaintext[]);
+void Camellia_EncryptBlock(int keyBitLength, const u8 plaintext[],
+                           const KEY_TABLE_TYPE keyTable, u8 ciphertext[]);
+void Camellia_DecryptBlock(int keyBitLength, const u8 ciphertext[],
+                           const KEY_TABLE_TYPE keyTable, u8 plaintext[]);
+#endif                          /* #ifndef HEADER_CAMELLIA_LOCL_H */
diff --git a/crypto/camellia/cmll_misc.c b/crypto/camellia/cmll_misc.c
new file mode 100644
index 000000000..e5f014b79
--- /dev/null
+++ b/crypto/camellia/cmll_misc.c
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2006-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include "cmll_locl.h"
+
+int Camellia_set_key(const unsigned char *userKey, const int bits,
+                     CAMELLIA_KEY *key)
+{
+    if (!userKey || !key)
+        return -1;
+    if (bits != 128 && bits != 192 && bits != 256)
+        return -2;
+    key->grand_rounds = Camellia_Ekeygen(bits, userKey, key->u.rd_key);
+    return 0;
+}
+
+void Camellia_encrypt(const unsigned char *in, unsigned char *out,
+                      const CAMELLIA_KEY *key)
+{
+    Camellia_EncryptBlock_Rounds(key->grand_rounds, in, key->u.rd_key, out);
+}
+
+void Camellia_decrypt(const unsigned char *in, unsigned char *out,
+                      const CAMELLIA_KEY *key)
+{
+    Camellia_DecryptBlock_Rounds(key->grand_rounds, in, key->u.rd_key, out);
+}
diff --git a/crypto/camellia/cmll_ofb.c b/crypto/camellia/cmll_ofb.c
new file mode 100644
index 000000000..b43c685c7
--- /dev/null
+++ b/crypto/camellia/cmll_ofb.c
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2006-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+
+/*
+ * The input and output encrypted as though 128bit ofb mode is being used.
+ * The extra state information to record how much of the 128bit block we have
+ * used is contained in *num;
+ */
+void Camellia_ofb128_encrypt(const unsigned char *in, unsigned char *out,
+                             size_t length, const CAMELLIA_KEY *key,
+                             unsigned char *ivec, int *num)
+{
+    CRYPTO_ofb128_encrypt(in, out, length, key, ivec, num,
+                          (block128_f) Camellia_encrypt);
+}
diff --git a/crypto/cast/asm/cast-586.pl b/crypto/cast/asm/cast-586.pl
new file mode 100644
index 000000000..6beb9c5f2
--- /dev/null
+++ b/crypto/cast/asm/cast-586.pl
@@ -0,0 +1,192 @@
+#! /usr/bin/env perl
+# Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+
+# This flag makes the inner loop one cycle longer, but generates 
+# code that runs %30 faster on the pentium pro/II, 44% faster
+# of PIII, while only %7 slower on the pentium.
+# By default, this flag is on.
+$ppro=1;
+
+$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+push(@INC,"${dir}","${dir}../../perlasm");
+require "x86asm.pl";
+require "cbc.pl";
+
+$output=pop;
+open STDOUT,">$output";
+
+&asm_init($ARGV[0],"cast-586.pl",$ARGV[$#ARGV] eq "386");
+
+$CAST_ROUNDS=16;
+$L="edi";
+$R="esi";
+$K="ebp";
+$tmp1="ecx";
+$tmp2="ebx";
+$tmp3="eax";
+$tmp4="edx";
+$S1="CAST_S_table0";
+$S2="CAST_S_table1";
+$S3="CAST_S_table2";
+$S4="CAST_S_table3";
+
+@F1=("add","xor","sub");
+@F2=("xor","sub","add");
+@F3=("sub","add","xor");
+
+&CAST_encrypt("CAST_encrypt",1);
+&CAST_encrypt("CAST_decrypt",0);
+&cbc("CAST_cbc_encrypt","CAST_encrypt","CAST_decrypt",1,4,5,3,-1,-1);
+
+&asm_finish();
+
+close STDOUT;
+
+sub CAST_encrypt {
+    local($name,$enc)=@_;
+
+    local($win_ex)=<<"EOF";
+EXTERN	_CAST_S_table0:DWORD
+EXTERN	_CAST_S_table1:DWORD
+EXTERN	_CAST_S_table2:DWORD
+EXTERN	_CAST_S_table3:DWORD
+EOF
+    &main::external_label(
+			  "CAST_S_table0",
+			  "CAST_S_table1",
+			  "CAST_S_table2",
+			  "CAST_S_table3",
+			  );
+
+    &function_begin_B($name,$win_ex);
+
+    &comment("");
+
+    &push("ebp");
+    &push("ebx");
+    &mov($tmp2,&wparam(0));
+    &mov($K,&wparam(1));
+    &push("esi");
+    &push("edi");
+
+    &comment("Load the 2 words");
+    &mov($L,&DWP(0,$tmp2,"",0));
+    &mov($R,&DWP(4,$tmp2,"",0));
+
+    &comment('Get short key flag');
+    &mov($tmp3,&DWP(128,$K,"",0));
+    if($enc) {
+	&push($tmp3);
+    } else {
+	&or($tmp3,$tmp3);
+	&jnz(&label('cast_dec_skip'));
+    }
+
+    &xor($tmp3,	$tmp3);
+
+    # encrypting part
+
+    if ($enc) {
+	&E_CAST( 0,$S,$L,$R,$K,@F1,$tmp1,$tmp2,$tmp3,$tmp4);
+	&E_CAST( 1,$S,$R,$L,$K,@F2,$tmp1,$tmp2,$tmp3,$tmp4);
+	&E_CAST( 2,$S,$L,$R,$K,@F3,$tmp1,$tmp2,$tmp3,$tmp4);
+	&E_CAST( 3,$S,$R,$L,$K,@F1,$tmp1,$tmp2,$tmp3,$tmp4);
+	&E_CAST( 4,$S,$L,$R,$K,@F2,$tmp1,$tmp2,$tmp3,$tmp4);
+	&E_CAST( 5,$S,$R,$L,$K,@F3,$tmp1,$tmp2,$tmp3,$tmp4);
+	&E_CAST( 6,$S,$L,$R,$K,@F1,$tmp1,$tmp2,$tmp3,$tmp4);
+	&E_CAST( 7,$S,$R,$L,$K,@F2,$tmp1,$tmp2,$tmp3,$tmp4);
+	&E_CAST( 8,$S,$L,$R,$K,@F3,$tmp1,$tmp2,$tmp3,$tmp4);
+	&E_CAST( 9,$S,$R,$L,$K,@F1,$tmp1,$tmp2,$tmp3,$tmp4);
+	&E_CAST(10,$S,$L,$R,$K,@F2,$tmp1,$tmp2,$tmp3,$tmp4);
+	&E_CAST(11,$S,$R,$L,$K,@F3,$tmp1,$tmp2,$tmp3,$tmp4);
+	&comment('test short key flag');
+	&pop($tmp4);
+	&or($tmp4,$tmp4);
+	&jnz(&label('cast_enc_done'));
+	&E_CAST(12,$S,$L,$R,$K,@F1,$tmp1,$tmp2,$tmp3,$tmp4);
+	&E_CAST(13,$S,$R,$L,$K,@F2,$tmp1,$tmp2,$tmp3,$tmp4);
+	&E_CAST(14,$S,$L,$R,$K,@F3,$tmp1,$tmp2,$tmp3,$tmp4);
+	&E_CAST(15,$S,$R,$L,$K,@F1,$tmp1,$tmp2,$tmp3,$tmp4);
+    } else {
+	&E_CAST(15,$S,$L,$R,$K,@F1,$tmp1,$tmp2,$tmp3,$tmp4);
+	&E_CAST(14,$S,$R,$L,$K,@F3,$tmp1,$tmp2,$tmp3,$tmp4);
+	&E_CAST(13,$S,$L,$R,$K,@F2,$tmp1,$tmp2,$tmp3,$tmp4);
+	&E_CAST(12,$S,$R,$L,$K,@F1,$tmp1,$tmp2,$tmp3,$tmp4);
+	&set_label('cast_dec_skip');
+	&E_CAST(11,$S,$L,$R,$K,@F3,$tmp1,$tmp2,$tmp3,$tmp4);
+	&E_CAST(10,$S,$R,$L,$K,@F2,$tmp1,$tmp2,$tmp3,$tmp4);
+	&E_CAST( 9,$S,$L,$R,$K,@F1,$tmp1,$tmp2,$tmp3,$tmp4);
+	&E_CAST( 8,$S,$R,$L,$K,@F3,$tmp1,$tmp2,$tmp3,$tmp4);
+	&E_CAST( 7,$S,$L,$R,$K,@F2,$tmp1,$tmp2,$tmp3,$tmp4);
+	&E_CAST( 6,$S,$R,$L,$K,@F1,$tmp1,$tmp2,$tmp3,$tmp4);
+	&E_CAST( 5,$S,$L,$R,$K,@F3,$tmp1,$tmp2,$tmp3,$tmp4);
+	&E_CAST( 4,$S,$R,$L,$K,@F2,$tmp1,$tmp2,$tmp3,$tmp4);
+	&E_CAST( 3,$S,$L,$R,$K,@F1,$tmp1,$tmp2,$tmp3,$tmp4);
+	&E_CAST( 2,$S,$R,$L,$K,@F3,$tmp1,$tmp2,$tmp3,$tmp4);
+	&E_CAST( 1,$S,$L,$R,$K,@F2,$tmp1,$tmp2,$tmp3,$tmp4);
+	&E_CAST( 0,$S,$R,$L,$K,@F1,$tmp1,$tmp2,$tmp3,$tmp4);
+    }
+
+    &set_label('cast_enc_done') if $enc;
+# Why the nop? - Ben 17/1/99
+    &nop();
+    &mov($tmp3,&wparam(0));
+    &mov(&DWP(4,$tmp3,"",0),$L);
+    &mov(&DWP(0,$tmp3,"",0),$R);
+    &function_end($name);
+}
+
+sub E_CAST {
+    local($i,$S,$L,$R,$K,$OP1,$OP2,$OP3,$tmp1,$tmp2,$tmp3,$tmp4)=@_;
+    # Ri needs to have 16 pre added.
+
+    &comment("round $i");
+    &mov(	$tmp4,		&DWP($i*8,$K,"",1));
+
+    &mov(	$tmp1,		&DWP($i*8+4,$K,"",1));
+    &$OP1(	$tmp4,		$R);
+
+    &rotl(	$tmp4,		&LB($tmp1));
+
+    if ($ppro) {
+	&xor(	$tmp1,		$tmp1);
+	&mov(	$tmp2,		0xff);
+	
+	&movb(	&LB($tmp1),	&HB($tmp4));	# A
+	&and(	$tmp2,		$tmp4);
+
+	&shr(	$tmp4,		16); 		#
+	&xor(	$tmp3,		$tmp3);
+    } else {
+	&mov(	$tmp2,		$tmp4);		# B
+	&movb(	&LB($tmp1),	&HB($tmp4));	# A	# BAD BAD BAD
+	
+	&shr(	$tmp4,		16); 		#
+	&and(	$tmp2,		0xff);
+    }
+
+    &movb(	&LB($tmp3),	&HB($tmp4));	# C	# BAD BAD BAD
+    &and(	$tmp4,		0xff);		# D
+
+    &mov(	$tmp1,		&DWP($S1,"",$tmp1,4));
+    &mov(	$tmp2,		&DWP($S2,"",$tmp2,4));
+
+    &$OP2(	$tmp1,		$tmp2);
+    &mov(	$tmp2,		&DWP($S3,"",$tmp3,4));
+
+    &$OP3(	$tmp1,		$tmp2);
+    &mov(	$tmp2,		&DWP($S4,"",$tmp4,4));
+
+    &$OP1(	$tmp1,		$tmp2);
+    # XXX
+
+    &xor(	$L,		$tmp1);
+    # XXX
+}
+
diff --git a/crypto/cast/build.info b/crypto/cast/build.info
new file mode 100644
index 000000000..f6a25c9a5
--- /dev/null
+++ b/crypto/cast/build.info
@@ -0,0 +1,6 @@
+LIBS=../../libcrypto
+SOURCE[../../libcrypto]=\
+        c_skey.c c_ecb.c {- $target{cast_asm_src} -} c_cfb64.c c_ofb64.c
+
+GENERATE[cast-586.s]=asm/cast-586.pl $(PERLASM_SCHEME) $(CFLAGS) $(LIB_CFLAGS) $(PROCESSOR)
+DEPEND[cast-586.s]=../perlasm/x86asm.pl ../perlasm/cbc.pl
diff --git a/crypto/cast/c_cfb64.c b/crypto/cast/c_cfb64.c
new file mode 100644
index 000000000..bd7cb2f46
--- /dev/null
+++ b/crypto/cast/c_cfb64.c
@@ -0,0 +1,74 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "cast_lcl.h"
+
+/*
+ * The input and output encrypted as though 64bit cfb mode is being used.
+ * The extra state information to record how much of the 64bit block we have
+ * used is contained in *num;
+ */
+
+void CAST_cfb64_encrypt(const unsigned char *in, unsigned char *out,
+                        long length, const CAST_KEY *schedule,
+                        unsigned char *ivec, int *num, int enc)
+{
+    register CAST_LONG v0, v1, t;
+    register int n = *num;
+    register long l = length;
+    CAST_LONG ti[2];
+    unsigned char *iv, c, cc;
+
+    iv = ivec;
+    if (enc) {
+        while (l--) {
+            if (n == 0) {
+                n2l(iv, v0);
+                ti[0] = v0;
+                n2l(iv, v1);
+                ti[1] = v1;
+                CAST_encrypt((CAST_LONG *)ti, schedule);
+                iv = ivec;
+                t = ti[0];
+                l2n(t, iv);
+                t = ti[1];
+                l2n(t, iv);
+                iv = ivec;
+            }
+            c = *(in++) ^ iv[n];
+            *(out++) = c;
+            iv[n] = c;
+            n = (n + 1) & 0x07;
+        }
+    } else {
+        while (l--) {
+            if (n == 0) {
+                n2l(iv, v0);
+                ti[0] = v0;
+                n2l(iv, v1);
+                ti[1] = v1;
+                CAST_encrypt((CAST_LONG *)ti, schedule);
+                iv = ivec;
+                t = ti[0];
+                l2n(t, iv);
+                t = ti[1];
+                l2n(t, iv);
+                iv = ivec;
+            }
+            cc = *(in++);
+            c = iv[n];
+            iv[n] = cc;
+            *(out++) = c ^ cc;
+            n = (n + 1) & 0x07;
+        }
+    }
+    v0 = v1 = ti[0] = ti[1] = t = c = cc = 0;
+    *num = n;
+}
diff --git a/crypto/cast/c_ecb.c b/crypto/cast/c_ecb.c
new file mode 100644
index 000000000..da4179438
--- /dev/null
+++ b/crypto/cast/c_ecb.c
@@ -0,0 +1,32 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "cast_lcl.h"
+#include 
+
+void CAST_ecb_encrypt(const unsigned char *in, unsigned char *out,
+                      const CAST_KEY *ks, int enc)
+{
+    CAST_LONG l, d[2];
+
+    n2l(in, l);
+    d[0] = l;
+    n2l(in, l);
+    d[1] = l;
+    if (enc)
+        CAST_encrypt(d, ks);
+    else
+        CAST_decrypt(d, ks);
+    l = d[0];
+    l2n(l, out);
+    l = d[1];
+    l2n(l, out);
+    l = d[0] = d[1] = 0;
+}
diff --git a/crypto/cast/c_enc.c b/crypto/cast/c_enc.c
new file mode 100644
index 000000000..9a8581259
--- /dev/null
+++ b/crypto/cast/c_enc.c
@@ -0,0 +1,151 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "cast_lcl.h"
+
+void CAST_encrypt(CAST_LONG *data, const CAST_KEY *key)
+{
+    register CAST_LONG l, r, t;
+    const register CAST_LONG *k;
+
+    k = &(key->data[0]);
+    l = data[0];
+    r = data[1];
+
+    E_CAST(0, k, l, r, +, ^, -);
+    E_CAST(1, k, r, l, ^, -, +);
+    E_CAST(2, k, l, r, -, +, ^);
+    E_CAST(3, k, r, l, +, ^, -);
+    E_CAST(4, k, l, r, ^, -, +);
+    E_CAST(5, k, r, l, -, +, ^);
+    E_CAST(6, k, l, r, +, ^, -);
+    E_CAST(7, k, r, l, ^, -, +);
+    E_CAST(8, k, l, r, -, +, ^);
+    E_CAST(9, k, r, l, +, ^, -);
+    E_CAST(10, k, l, r, ^, -, +);
+    E_CAST(11, k, r, l, -, +, ^);
+    if (!key->short_key) {
+        E_CAST(12, k, l, r, +, ^, -);
+        E_CAST(13, k, r, l, ^, -, +);
+        E_CAST(14, k, l, r, -, +, ^);
+        E_CAST(15, k, r, l, +, ^, -);
+    }
+
+    data[1] = l & 0xffffffffL;
+    data[0] = r & 0xffffffffL;
+}
+
+void CAST_decrypt(CAST_LONG *data, const CAST_KEY *key)
+{
+    register CAST_LONG l, r, t;
+    const register CAST_LONG *k;
+
+    k = &(key->data[0]);
+    l = data[0];
+    r = data[1];
+
+    if (!key->short_key) {
+        E_CAST(15, k, l, r, +, ^, -);
+        E_CAST(14, k, r, l, -, +, ^);
+        E_CAST(13, k, l, r, ^, -, +);
+        E_CAST(12, k, r, l, +, ^, -);
+    }
+    E_CAST(11, k, l, r, -, +, ^);
+    E_CAST(10, k, r, l, ^, -, +);
+    E_CAST(9, k, l, r, +, ^, -);
+    E_CAST(8, k, r, l, -, +, ^);
+    E_CAST(7, k, l, r, ^, -, +);
+    E_CAST(6, k, r, l, +, ^, -);
+    E_CAST(5, k, l, r, -, +, ^);
+    E_CAST(4, k, r, l, ^, -, +);
+    E_CAST(3, k, l, r, +, ^, -);
+    E_CAST(2, k, r, l, -, +, ^);
+    E_CAST(1, k, l, r, ^, -, +);
+    E_CAST(0, k, r, l, +, ^, -);
+
+    data[1] = l & 0xffffffffL;
+    data[0] = r & 0xffffffffL;
+}
+
+void CAST_cbc_encrypt(const unsigned char *in, unsigned char *out,
+                      long length, const CAST_KEY *ks, unsigned char *iv,
+                      int enc)
+{
+    register CAST_LONG tin0, tin1;
+    register CAST_LONG tout0, tout1, xor0, xor1;
+    register long l = length;
+    CAST_LONG tin[2];
+
+    if (enc) {
+        n2l(iv, tout0);
+        n2l(iv, tout1);
+        iv -= 8;
+        for (l -= 8; l >= 0; l -= 8) {
+            n2l(in, tin0);
+            n2l(in, tin1);
+            tin0 ^= tout0;
+            tin1 ^= tout1;
+            tin[0] = tin0;
+            tin[1] = tin1;
+            CAST_encrypt(tin, ks);
+            tout0 = tin[0];
+            tout1 = tin[1];
+            l2n(tout0, out);
+            l2n(tout1, out);
+        }
+        if (l != -8) {
+            n2ln(in, tin0, tin1, l + 8);
+            tin0 ^= tout0;
+            tin1 ^= tout1;
+            tin[0] = tin0;
+            tin[1] = tin1;
+            CAST_encrypt(tin, ks);
+            tout0 = tin[0];
+            tout1 = tin[1];
+            l2n(tout0, out);
+            l2n(tout1, out);
+        }
+        l2n(tout0, iv);
+        l2n(tout1, iv);
+    } else {
+        n2l(iv, xor0);
+        n2l(iv, xor1);
+        iv -= 8;
+        for (l -= 8; l >= 0; l -= 8) {
+            n2l(in, tin0);
+            n2l(in, tin1);
+            tin[0] = tin0;
+            tin[1] = tin1;
+            CAST_decrypt(tin, ks);
+            tout0 = tin[0] ^ xor0;
+            tout1 = tin[1] ^ xor1;
+            l2n(tout0, out);
+            l2n(tout1, out);
+            xor0 = tin0;
+            xor1 = tin1;
+        }
+        if (l != -8) {
+            n2l(in, tin0);
+            n2l(in, tin1);
+            tin[0] = tin0;
+            tin[1] = tin1;
+            CAST_decrypt(tin, ks);
+            tout0 = tin[0] ^ xor0;
+            tout1 = tin[1] ^ xor1;
+            l2nn(tout0, tout1, out, l + 8);
+            xor0 = tin0;
+            xor1 = tin1;
+        }
+        l2n(xor0, iv);
+        l2n(xor1, iv);
+    }
+    tin0 = tin1 = tout0 = tout1 = xor0 = xor1 = 0;
+    tin[0] = tin[1] = 0;
+}
diff --git a/crypto/cast/c_ofb64.c b/crypto/cast/c_ofb64.c
new file mode 100644
index 000000000..dffb07476
--- /dev/null
+++ b/crypto/cast/c_ofb64.c
@@ -0,0 +1,61 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "cast_lcl.h"
+
+/*
+ * The input and output encrypted as though 64bit ofb mode is being used.
+ * The extra state information to record how much of the 64bit block we have
+ * used is contained in *num;
+ */
+void CAST_ofb64_encrypt(const unsigned char *in, unsigned char *out,
+                        long length, const CAST_KEY *schedule,
+                        unsigned char *ivec, int *num)
+{
+    register CAST_LONG v0, v1, t;
+    register int n = *num;
+    register long l = length;
+    unsigned char d[8];
+    register char *dp;
+    CAST_LONG ti[2];
+    unsigned char *iv;
+    int save = 0;
+
+    iv = ivec;
+    n2l(iv, v0);
+    n2l(iv, v1);
+    ti[0] = v0;
+    ti[1] = v1;
+    dp = (char *)d;
+    l2n(v0, dp);
+    l2n(v1, dp);
+    while (l--) {
+        if (n == 0) {
+            CAST_encrypt((CAST_LONG *)ti, schedule);
+            dp = (char *)d;
+            t = ti[0];
+            l2n(t, dp);
+            t = ti[1];
+            l2n(t, dp);
+            save++;
+        }
+        *(out++) = *(in++) ^ d[n];
+        n = (n + 1) & 0x07;
+    }
+    if (save) {
+        v0 = ti[0];
+        v1 = ti[1];
+        iv = ivec;
+        l2n(v0, iv);
+        l2n(v1, iv);
+    }
+    t = v0 = v1 = ti[0] = ti[1] = 0;
+    *num = n;
+}
diff --git a/crypto/cast/c_skey.c b/crypto/cast/c_skey.c
new file mode 100644
index 000000000..962d2a60b
--- /dev/null
+++ b/crypto/cast/c_skey.c
@@ -0,0 +1,118 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "cast_lcl.h"
+#include "cast_s.h"
+
+#define CAST_exp(l,A,a,n) \
+        A[n/4]=l; \
+        a[n+3]=(l    )&0xff; \
+        a[n+2]=(l>> 8)&0xff; \
+        a[n+1]=(l>>16)&0xff; \
+        a[n+0]=(l>>24)&0xff;
+
+#define S4 CAST_S_table4
+#define S5 CAST_S_table5
+#define S6 CAST_S_table6
+#define S7 CAST_S_table7
+
+void CAST_set_key(CAST_KEY *key, int len, const unsigned char *data)
+{
+    CAST_LONG x[16];
+    CAST_LONG z[16];
+    CAST_LONG k[32];
+    CAST_LONG X[4], Z[4];
+    CAST_LONG l, *K;
+    int i;
+
+    for (i = 0; i < 16; i++)
+        x[i] = 0;
+    if (len > 16)
+        len = 16;
+    for (i = 0; i < len; i++)
+        x[i] = data[i];
+    if (len <= 10)
+        key->short_key = 1;
+    else
+        key->short_key = 0;
+
+    K = &k[0];
+    X[0] = ((x[0] << 24) | (x[1] << 16) | (x[2] << 8) | x[3]) & 0xffffffffL;
+    X[1] = ((x[4] << 24) | (x[5] << 16) | (x[6] << 8) | x[7]) & 0xffffffffL;
+    X[2] = ((x[8] << 24) | (x[9] << 16) | (x[10] << 8) | x[11]) & 0xffffffffL;
+    X[3] =
+        ((x[12] << 24) | (x[13] << 16) | (x[14] << 8) | x[15]) & 0xffffffffL;
+
+    for (;;) {
+        l = X[0] ^ S4[x[13]] ^ S5[x[15]] ^ S6[x[12]] ^ S7[x[14]] ^ S6[x[8]];
+        CAST_exp(l, Z, z, 0);
+        l = X[2] ^ S4[z[0]] ^ S5[z[2]] ^ S6[z[1]] ^ S7[z[3]] ^ S7[x[10]];
+        CAST_exp(l, Z, z, 4);
+        l = X[3] ^ S4[z[7]] ^ S5[z[6]] ^ S6[z[5]] ^ S7[z[4]] ^ S4[x[9]];
+        CAST_exp(l, Z, z, 8);
+        l = X[1] ^ S4[z[10]] ^ S5[z[9]] ^ S6[z[11]] ^ S7[z[8]] ^ S5[x[11]];
+        CAST_exp(l, Z, z, 12);
+
+        K[0] = S4[z[8]] ^ S5[z[9]] ^ S6[z[7]] ^ S7[z[6]] ^ S4[z[2]];
+        K[1] = S4[z[10]] ^ S5[z[11]] ^ S6[z[5]] ^ S7[z[4]] ^ S5[z[6]];
+        K[2] = S4[z[12]] ^ S5[z[13]] ^ S6[z[3]] ^ S7[z[2]] ^ S6[z[9]];
+        K[3] = S4[z[14]] ^ S5[z[15]] ^ S6[z[1]] ^ S7[z[0]] ^ S7[z[12]];
+
+        l = Z[2] ^ S4[z[5]] ^ S5[z[7]] ^ S6[z[4]] ^ S7[z[6]] ^ S6[z[0]];
+        CAST_exp(l, X, x, 0);
+        l = Z[0] ^ S4[x[0]] ^ S5[x[2]] ^ S6[x[1]] ^ S7[x[3]] ^ S7[z[2]];
+        CAST_exp(l, X, x, 4);
+        l = Z[1] ^ S4[x[7]] ^ S5[x[6]] ^ S6[x[5]] ^ S7[x[4]] ^ S4[z[1]];
+        CAST_exp(l, X, x, 8);
+        l = Z[3] ^ S4[x[10]] ^ S5[x[9]] ^ S6[x[11]] ^ S7[x[8]] ^ S5[z[3]];
+        CAST_exp(l, X, x, 12);
+
+        K[4] = S4[x[3]] ^ S5[x[2]] ^ S6[x[12]] ^ S7[x[13]] ^ S4[x[8]];
+        K[5] = S4[x[1]] ^ S5[x[0]] ^ S6[x[14]] ^ S7[x[15]] ^ S5[x[13]];
+        K[6] = S4[x[7]] ^ S5[x[6]] ^ S6[x[8]] ^ S7[x[9]] ^ S6[x[3]];
+        K[7] = S4[x[5]] ^ S5[x[4]] ^ S6[x[10]] ^ S7[x[11]] ^ S7[x[7]];
+
+        l = X[0] ^ S4[x[13]] ^ S5[x[15]] ^ S6[x[12]] ^ S7[x[14]] ^ S6[x[8]];
+        CAST_exp(l, Z, z, 0);
+        l = X[2] ^ S4[z[0]] ^ S5[z[2]] ^ S6[z[1]] ^ S7[z[3]] ^ S7[x[10]];
+        CAST_exp(l, Z, z, 4);
+        l = X[3] ^ S4[z[7]] ^ S5[z[6]] ^ S6[z[5]] ^ S7[z[4]] ^ S4[x[9]];
+        CAST_exp(l, Z, z, 8);
+        l = X[1] ^ S4[z[10]] ^ S5[z[9]] ^ S6[z[11]] ^ S7[z[8]] ^ S5[x[11]];
+        CAST_exp(l, Z, z, 12);
+
+        K[8] = S4[z[3]] ^ S5[z[2]] ^ S6[z[12]] ^ S7[z[13]] ^ S4[z[9]];
+        K[9] = S4[z[1]] ^ S5[z[0]] ^ S6[z[14]] ^ S7[z[15]] ^ S5[z[12]];
+        K[10] = S4[z[7]] ^ S5[z[6]] ^ S6[z[8]] ^ S7[z[9]] ^ S6[z[2]];
+        K[11] = S4[z[5]] ^ S5[z[4]] ^ S6[z[10]] ^ S7[z[11]] ^ S7[z[6]];
+
+        l = Z[2] ^ S4[z[5]] ^ S5[z[7]] ^ S6[z[4]] ^ S7[z[6]] ^ S6[z[0]];
+        CAST_exp(l, X, x, 0);
+        l = Z[0] ^ S4[x[0]] ^ S5[x[2]] ^ S6[x[1]] ^ S7[x[3]] ^ S7[z[2]];
+        CAST_exp(l, X, x, 4);
+        l = Z[1] ^ S4[x[7]] ^ S5[x[6]] ^ S6[x[5]] ^ S7[x[4]] ^ S4[z[1]];
+        CAST_exp(l, X, x, 8);
+        l = Z[3] ^ S4[x[10]] ^ S5[x[9]] ^ S6[x[11]] ^ S7[x[8]] ^ S5[z[3]];
+        CAST_exp(l, X, x, 12);
+
+        K[12] = S4[x[8]] ^ S5[x[9]] ^ S6[x[7]] ^ S7[x[6]] ^ S4[x[3]];
+        K[13] = S4[x[10]] ^ S5[x[11]] ^ S6[x[5]] ^ S7[x[4]] ^ S5[x[7]];
+        K[14] = S4[x[12]] ^ S5[x[13]] ^ S6[x[3]] ^ S7[x[2]] ^ S6[x[8]];
+        K[15] = S4[x[14]] ^ S5[x[15]] ^ S6[x[1]] ^ S7[x[0]] ^ S7[x[13]];
+        if (K != k)
+            break;
+        K += 16;
+    }
+
+    for (i = 0; i < 16; i++) {
+        key->data[i * 2] = k[i];
+        key->data[i * 2 + 1] = ((k[i + 16]) + 16) & 0x1f;
+    }
+}
diff --git a/crypto/cast/cast_lcl.h b/crypto/cast/cast_lcl.h
new file mode 100644
index 000000000..504232a1a
--- /dev/null
+++ b/crypto/cast/cast_lcl.h
@@ -0,0 +1,176 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "e_os.h"
+
+#ifdef OPENSSL_SYS_WIN32
+# include 
+#endif
+
+#undef c2l
+#define c2l(c,l)        (l =((unsigned long)(*((c)++)))    , \
+                         l|=((unsigned long)(*((c)++)))<< 8L, \
+                         l|=((unsigned long)(*((c)++)))<<16L, \
+                         l|=((unsigned long)(*((c)++)))<<24L)
+
+/* NOTE - c is not incremented as per c2l */
+#undef c2ln
+#define c2ln(c,l1,l2,n) { \
+                        c+=n; \
+                        l1=l2=0; \
+                        switch (n) { \
+                        case 8: l2 =((unsigned long)(*(--(c))))<<24L; \
+                        case 7: l2|=((unsigned long)(*(--(c))))<<16L; \
+                        case 6: l2|=((unsigned long)(*(--(c))))<< 8L; \
+                        case 5: l2|=((unsigned long)(*(--(c))));     \
+                        case 4: l1 =((unsigned long)(*(--(c))))<<24L; \
+                        case 3: l1|=((unsigned long)(*(--(c))))<<16L; \
+                        case 2: l1|=((unsigned long)(*(--(c))))<< 8L; \
+                        case 1: l1|=((unsigned long)(*(--(c))));     \
+                                } \
+                        }
+
+#undef l2c
+#define l2c(l,c)        (*((c)++)=(unsigned char)(((l)     )&0xff), \
+                         *((c)++)=(unsigned char)(((l)>> 8L)&0xff), \
+                         *((c)++)=(unsigned char)(((l)>>16L)&0xff), \
+                         *((c)++)=(unsigned char)(((l)>>24L)&0xff))
+
+/* NOTE - c is not incremented as per l2c */
+#undef l2cn
+#define l2cn(l1,l2,c,n) { \
+                        c+=n; \
+                        switch (n) { \
+                        case 8: *(--(c))=(unsigned char)(((l2)>>24L)&0xff); \
+                        case 7: *(--(c))=(unsigned char)(((l2)>>16L)&0xff); \
+                        case 6: *(--(c))=(unsigned char)(((l2)>> 8L)&0xff); \
+                        case 5: *(--(c))=(unsigned char)(((l2)     )&0xff); \
+                        case 4: *(--(c))=(unsigned char)(((l1)>>24L)&0xff); \
+                        case 3: *(--(c))=(unsigned char)(((l1)>>16L)&0xff); \
+                        case 2: *(--(c))=(unsigned char)(((l1)>> 8L)&0xff); \
+                        case 1: *(--(c))=(unsigned char)(((l1)     )&0xff); \
+                                } \
+                        }
+
+/* NOTE - c is not incremented as per n2l */
+#define n2ln(c,l1,l2,n) { \
+                        c+=n; \
+                        l1=l2=0; \
+                        switch (n) { \
+                        case 8: l2 =((unsigned long)(*(--(c))))    ; \
+                        case 7: l2|=((unsigned long)(*(--(c))))<< 8; \
+                        case 6: l2|=((unsigned long)(*(--(c))))<<16; \
+                        case 5: l2|=((unsigned long)(*(--(c))))<<24; \
+                        case 4: l1 =((unsigned long)(*(--(c))))    ; \
+                        case 3: l1|=((unsigned long)(*(--(c))))<< 8; \
+                        case 2: l1|=((unsigned long)(*(--(c))))<<16; \
+                        case 1: l1|=((unsigned long)(*(--(c))))<<24; \
+                                } \
+                        }
+
+/* NOTE - c is not incremented as per l2n */
+#define l2nn(l1,l2,c,n) { \
+                        c+=n; \
+                        switch (n) { \
+                        case 8: *(--(c))=(unsigned char)(((l2)    )&0xff); \
+                        case 7: *(--(c))=(unsigned char)(((l2)>> 8)&0xff); \
+                        case 6: *(--(c))=(unsigned char)(((l2)>>16)&0xff); \
+                        case 5: *(--(c))=(unsigned char)(((l2)>>24)&0xff); \
+                        case 4: *(--(c))=(unsigned char)(((l1)    )&0xff); \
+                        case 3: *(--(c))=(unsigned char)(((l1)>> 8)&0xff); \
+                        case 2: *(--(c))=(unsigned char)(((l1)>>16)&0xff); \
+                        case 1: *(--(c))=(unsigned char)(((l1)>>24)&0xff); \
+                                } \
+                        }
+
+#undef n2l
+#define n2l(c,l)        (l =((unsigned long)(*((c)++)))<<24L, \
+                         l|=((unsigned long)(*((c)++)))<<16L, \
+                         l|=((unsigned long)(*((c)++)))<< 8L, \
+                         l|=((unsigned long)(*((c)++))))
+
+#undef l2n
+#define l2n(l,c)        (*((c)++)=(unsigned char)(((l)>>24L)&0xff), \
+                         *((c)++)=(unsigned char)(((l)>>16L)&0xff), \
+                         *((c)++)=(unsigned char)(((l)>> 8L)&0xff), \
+                         *((c)++)=(unsigned char)(((l)     )&0xff))
+
+#if defined(OPENSSL_SYS_WIN32) && defined(_MSC_VER)
+# define ROTL(a,n)     (_lrotl(a,n))
+#else
+# define ROTL(a,n)     ((((a)<<(n))&0xffffffffL)|((a)>>((32-(n))&31)))
+#endif
+
+#define C_M    0x3fc
+#define C_0    22L
+#define C_1    14L
+#define C_2     6L
+#define C_3     2L              /* left shift */
+
+/* The rotate has an extra 16 added to it to help the x86 asm */
+#if defined(CAST_PTR)
+# define E_CAST(n,key,L,R,OP1,OP2,OP3) \
+        { \
+        int i; \
+        t=(key[n*2] OP1 R)&0xffffffffL; \
+        i=key[n*2+1]; \
+        t=ROTL(t,i); \
+        L^= (((((*(CAST_LONG *)((unsigned char *) \
+                        CAST_S_table0+((t>>C_2)&C_M)) OP2 \
+                *(CAST_LONG *)((unsigned char *) \
+                        CAST_S_table1+((t<>C_0)&C_M)))&0xffffffffL) OP1 \
+                *(CAST_LONG *)((unsigned char *) \
+                        CAST_S_table3+((t>>C_1)&C_M)))&0xffffffffL; \
+        }
+#elif defined(CAST_PTR2)
+# define E_CAST(n,key,L,R,OP1,OP2,OP3) \
+        { \
+        int i; \
+        CAST_LONG u,v,w; \
+        w=(key[n*2] OP1 R)&0xffffffffL; \
+        i=key[n*2+1]; \
+        w=ROTL(w,i); \
+        u=w>>C_2; \
+        v=w<>C_0; \
+        t=(t OP2 *(CAST_LONG *)((unsigned char *)CAST_S_table1+v))&0xffffffffL;\
+        v=w>>C_1; \
+        u&=C_M; \
+        v&=C_M; \
+        t=(t OP3 *(CAST_LONG *)((unsigned char *)CAST_S_table2+u)&0xffffffffL);\
+        t=(t OP1 *(CAST_LONG *)((unsigned char *)CAST_S_table3+v)&0xffffffffL);\
+        L^=(t&0xffffffff); \
+        }
+#else
+# define E_CAST(n,key,L,R,OP1,OP2,OP3) \
+        { \
+        CAST_LONG a,b,c,d; \
+        t=(key[n*2] OP1 R)&0xffffffff; \
+        t=ROTL(t,(key[n*2+1])); \
+        a=CAST_S_table0[(t>> 8)&0xff]; \
+        b=CAST_S_table1[(t    )&0xff]; \
+        c=CAST_S_table2[(t>>24)&0xff]; \
+        d=CAST_S_table3[(t>>16)&0xff]; \
+        L^=(((((a OP2 b)&0xffffffffL) OP3 c)&0xffffffffL) OP1 d)&0xffffffffL; \
+        }
+#endif
+
+extern const CAST_LONG CAST_S_table0[256];
+extern const CAST_LONG CAST_S_table1[256];
+extern const CAST_LONG CAST_S_table2[256];
+extern const CAST_LONG CAST_S_table3[256];
+extern const CAST_LONG CAST_S_table4[256];
+extern const CAST_LONG CAST_S_table5[256];
+extern const CAST_LONG CAST_S_table6[256];
+extern const CAST_LONG CAST_S_table7[256];
diff --git a/crypto/cast/cast_s.h b/crypto/cast/cast_s.h
new file mode 100644
index 000000000..d9fd6ac41
--- /dev/null
+++ b/crypto/cast/cast_s.h
@@ -0,0 +1,544 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+OPENSSL_GLOBAL const CAST_LONG CAST_S_table0[256] = {
+    0x30fb40d4, 0x9fa0ff0b, 0x6beccd2f, 0x3f258c7a,
+    0x1e213f2f, 0x9c004dd3, 0x6003e540, 0xcf9fc949,
+    0xbfd4af27, 0x88bbbdb5, 0xe2034090, 0x98d09675,
+    0x6e63a0e0, 0x15c361d2, 0xc2e7661d, 0x22d4ff8e,
+    0x28683b6f, 0xc07fd059, 0xff2379c8, 0x775f50e2,
+    0x43c340d3, 0xdf2f8656, 0x887ca41a, 0xa2d2bd2d,
+    0xa1c9e0d6, 0x346c4819, 0x61b76d87, 0x22540f2f,
+    0x2abe32e1, 0xaa54166b, 0x22568e3a, 0xa2d341d0,
+    0x66db40c8, 0xa784392f, 0x004dff2f, 0x2db9d2de,
+    0x97943fac, 0x4a97c1d8, 0x527644b7, 0xb5f437a7,
+    0xb82cbaef, 0xd751d159, 0x6ff7f0ed, 0x5a097a1f,
+    0x827b68d0, 0x90ecf52e, 0x22b0c054, 0xbc8e5935,
+    0x4b6d2f7f, 0x50bb64a2, 0xd2664910, 0xbee5812d,
+    0xb7332290, 0xe93b159f, 0xb48ee411, 0x4bff345d,
+    0xfd45c240, 0xad31973f, 0xc4f6d02e, 0x55fc8165,
+    0xd5b1caad, 0xa1ac2dae, 0xa2d4b76d, 0xc19b0c50,
+    0x882240f2, 0x0c6e4f38, 0xa4e4bfd7, 0x4f5ba272,
+    0x564c1d2f, 0xc59c5319, 0xb949e354, 0xb04669fe,
+    0xb1b6ab8a, 0xc71358dd, 0x6385c545, 0x110f935d,
+    0x57538ad5, 0x6a390493, 0xe63d37e0, 0x2a54f6b3,
+    0x3a787d5f, 0x6276a0b5, 0x19a6fcdf, 0x7a42206a,
+    0x29f9d4d5, 0xf61b1891, 0xbb72275e, 0xaa508167,
+    0x38901091, 0xc6b505eb, 0x84c7cb8c, 0x2ad75a0f,
+    0x874a1427, 0xa2d1936b, 0x2ad286af, 0xaa56d291,
+    0xd7894360, 0x425c750d, 0x93b39e26, 0x187184c9,
+    0x6c00b32d, 0x73e2bb14, 0xa0bebc3c, 0x54623779,
+    0x64459eab, 0x3f328b82, 0x7718cf82, 0x59a2cea6,
+    0x04ee002e, 0x89fe78e6, 0x3fab0950, 0x325ff6c2,
+    0x81383f05, 0x6963c5c8, 0x76cb5ad6, 0xd49974c9,
+    0xca180dcf, 0x380782d5, 0xc7fa5cf6, 0x8ac31511,
+    0x35e79e13, 0x47da91d0, 0xf40f9086, 0xa7e2419e,
+    0x31366241, 0x051ef495, 0xaa573b04, 0x4a805d8d,
+    0x548300d0, 0x00322a3c, 0xbf64cddf, 0xba57a68e,
+    0x75c6372b, 0x50afd341, 0xa7c13275, 0x915a0bf5,
+    0x6b54bfab, 0x2b0b1426, 0xab4cc9d7, 0x449ccd82,
+    0xf7fbf265, 0xab85c5f3, 0x1b55db94, 0xaad4e324,
+    0xcfa4bd3f, 0x2deaa3e2, 0x9e204d02, 0xc8bd25ac,
+    0xeadf55b3, 0xd5bd9e98, 0xe31231b2, 0x2ad5ad6c,
+    0x954329de, 0xadbe4528, 0xd8710f69, 0xaa51c90f,
+    0xaa786bf6, 0x22513f1e, 0xaa51a79b, 0x2ad344cc,
+    0x7b5a41f0, 0xd37cfbad, 0x1b069505, 0x41ece491,
+    0xb4c332e6, 0x032268d4, 0xc9600acc, 0xce387e6d,
+    0xbf6bb16c, 0x6a70fb78, 0x0d03d9c9, 0xd4df39de,
+    0xe01063da, 0x4736f464, 0x5ad328d8, 0xb347cc96,
+    0x75bb0fc3, 0x98511bfb, 0x4ffbcc35, 0xb58bcf6a,
+    0xe11f0abc, 0xbfc5fe4a, 0xa70aec10, 0xac39570a,
+    0x3f04442f, 0x6188b153, 0xe0397a2e, 0x5727cb79,
+    0x9ceb418f, 0x1cacd68d, 0x2ad37c96, 0x0175cb9d,
+    0xc69dff09, 0xc75b65f0, 0xd9db40d8, 0xec0e7779,
+    0x4744ead4, 0xb11c3274, 0xdd24cb9e, 0x7e1c54bd,
+    0xf01144f9, 0xd2240eb1, 0x9675b3fd, 0xa3ac3755,
+    0xd47c27af, 0x51c85f4d, 0x56907596, 0xa5bb15e6,
+    0x580304f0, 0xca042cf1, 0x011a37ea, 0x8dbfaadb,
+    0x35ba3e4a, 0x3526ffa0, 0xc37b4d09, 0xbc306ed9,
+    0x98a52666, 0x5648f725, 0xff5e569d, 0x0ced63d0,
+    0x7c63b2cf, 0x700b45e1, 0xd5ea50f1, 0x85a92872,
+    0xaf1fbda7, 0xd4234870, 0xa7870bf3, 0x2d3b4d79,
+    0x42e04198, 0x0cd0ede7, 0x26470db8, 0xf881814c,
+    0x474d6ad7, 0x7c0c5e5c, 0xd1231959, 0x381b7298,
+    0xf5d2f4db, 0xab838653, 0x6e2f1e23, 0x83719c9e,
+    0xbd91e046, 0x9a56456e, 0xdc39200c, 0x20c8c571,
+    0x962bda1c, 0xe1e696ff, 0xb141ab08, 0x7cca89b9,
+    0x1a69e783, 0x02cc4843, 0xa2f7c579, 0x429ef47d,
+    0x427b169c, 0x5ac9f049, 0xdd8f0f00, 0x5c8165bf,
+};
+
+OPENSSL_GLOBAL const CAST_LONG CAST_S_table1[256] = {
+    0x1f201094, 0xef0ba75b, 0x69e3cf7e, 0x393f4380,
+    0xfe61cf7a, 0xeec5207a, 0x55889c94, 0x72fc0651,
+    0xada7ef79, 0x4e1d7235, 0xd55a63ce, 0xde0436ba,
+    0x99c430ef, 0x5f0c0794, 0x18dcdb7d, 0xa1d6eff3,
+    0xa0b52f7b, 0x59e83605, 0xee15b094, 0xe9ffd909,
+    0xdc440086, 0xef944459, 0xba83ccb3, 0xe0c3cdfb,
+    0xd1da4181, 0x3b092ab1, 0xf997f1c1, 0xa5e6cf7b,
+    0x01420ddb, 0xe4e7ef5b, 0x25a1ff41, 0xe180f806,
+    0x1fc41080, 0x179bee7a, 0xd37ac6a9, 0xfe5830a4,
+    0x98de8b7f, 0x77e83f4e, 0x79929269, 0x24fa9f7b,
+    0xe113c85b, 0xacc40083, 0xd7503525, 0xf7ea615f,
+    0x62143154, 0x0d554b63, 0x5d681121, 0xc866c359,
+    0x3d63cf73, 0xcee234c0, 0xd4d87e87, 0x5c672b21,
+    0x071f6181, 0x39f7627f, 0x361e3084, 0xe4eb573b,
+    0x602f64a4, 0xd63acd9c, 0x1bbc4635, 0x9e81032d,
+    0x2701f50c, 0x99847ab4, 0xa0e3df79, 0xba6cf38c,
+    0x10843094, 0x2537a95e, 0xf46f6ffe, 0xa1ff3b1f,
+    0x208cfb6a, 0x8f458c74, 0xd9e0a227, 0x4ec73a34,
+    0xfc884f69, 0x3e4de8df, 0xef0e0088, 0x3559648d,
+    0x8a45388c, 0x1d804366, 0x721d9bfd, 0xa58684bb,
+    0xe8256333, 0x844e8212, 0x128d8098, 0xfed33fb4,
+    0xce280ae1, 0x27e19ba5, 0xd5a6c252, 0xe49754bd,
+    0xc5d655dd, 0xeb667064, 0x77840b4d, 0xa1b6a801,
+    0x84db26a9, 0xe0b56714, 0x21f043b7, 0xe5d05860,
+    0x54f03084, 0x066ff472, 0xa31aa153, 0xdadc4755,
+    0xb5625dbf, 0x68561be6, 0x83ca6b94, 0x2d6ed23b,
+    0xeccf01db, 0xa6d3d0ba, 0xb6803d5c, 0xaf77a709,
+    0x33b4a34c, 0x397bc8d6, 0x5ee22b95, 0x5f0e5304,
+    0x81ed6f61, 0x20e74364, 0xb45e1378, 0xde18639b,
+    0x881ca122, 0xb96726d1, 0x8049a7e8, 0x22b7da7b,
+    0x5e552d25, 0x5272d237, 0x79d2951c, 0xc60d894c,
+    0x488cb402, 0x1ba4fe5b, 0xa4b09f6b, 0x1ca815cf,
+    0xa20c3005, 0x8871df63, 0xb9de2fcb, 0x0cc6c9e9,
+    0x0beeff53, 0xe3214517, 0xb4542835, 0x9f63293c,
+    0xee41e729, 0x6e1d2d7c, 0x50045286, 0x1e6685f3,
+    0xf33401c6, 0x30a22c95, 0x31a70850, 0x60930f13,
+    0x73f98417, 0xa1269859, 0xec645c44, 0x52c877a9,
+    0xcdff33a6, 0xa02b1741, 0x7cbad9a2, 0x2180036f,
+    0x50d99c08, 0xcb3f4861, 0xc26bd765, 0x64a3f6ab,
+    0x80342676, 0x25a75e7b, 0xe4e6d1fc, 0x20c710e6,
+    0xcdf0b680, 0x17844d3b, 0x31eef84d, 0x7e0824e4,
+    0x2ccb49eb, 0x846a3bae, 0x8ff77888, 0xee5d60f6,
+    0x7af75673, 0x2fdd5cdb, 0xa11631c1, 0x30f66f43,
+    0xb3faec54, 0x157fd7fa, 0xef8579cc, 0xd152de58,
+    0xdb2ffd5e, 0x8f32ce19, 0x306af97a, 0x02f03ef8,
+    0x99319ad5, 0xc242fa0f, 0xa7e3ebb0, 0xc68e4906,
+    0xb8da230c, 0x80823028, 0xdcdef3c8, 0xd35fb171,
+    0x088a1bc8, 0xbec0c560, 0x61a3c9e8, 0xbca8f54d,
+    0xc72feffa, 0x22822e99, 0x82c570b4, 0xd8d94e89,
+    0x8b1c34bc, 0x301e16e6, 0x273be979, 0xb0ffeaa6,
+    0x61d9b8c6, 0x00b24869, 0xb7ffce3f, 0x08dc283b,
+    0x43daf65a, 0xf7e19798, 0x7619b72f, 0x8f1c9ba4,
+    0xdc8637a0, 0x16a7d3b1, 0x9fc393b7, 0xa7136eeb,
+    0xc6bcc63e, 0x1a513742, 0xef6828bc, 0x520365d6,
+    0x2d6a77ab, 0x3527ed4b, 0x821fd216, 0x095c6e2e,
+    0xdb92f2fb, 0x5eea29cb, 0x145892f5, 0x91584f7f,
+    0x5483697b, 0x2667a8cc, 0x85196048, 0x8c4bacea,
+    0x833860d4, 0x0d23e0f9, 0x6c387e8a, 0x0ae6d249,
+    0xb284600c, 0xd835731d, 0xdcb1c647, 0xac4c56ea,
+    0x3ebd81b3, 0x230eabb0, 0x6438bc87, 0xf0b5b1fa,
+    0x8f5ea2b3, 0xfc184642, 0x0a036b7a, 0x4fb089bd,
+    0x649da589, 0xa345415e, 0x5c038323, 0x3e5d3bb9,
+    0x43d79572, 0x7e6dd07c, 0x06dfdf1e, 0x6c6cc4ef,
+    0x7160a539, 0x73bfbe70, 0x83877605, 0x4523ecf1,
+};
+
+OPENSSL_GLOBAL const CAST_LONG CAST_S_table2[256] = {
+    0x8defc240, 0x25fa5d9f, 0xeb903dbf, 0xe810c907,
+    0x47607fff, 0x369fe44b, 0x8c1fc644, 0xaececa90,
+    0xbeb1f9bf, 0xeefbcaea, 0xe8cf1950, 0x51df07ae,
+    0x920e8806, 0xf0ad0548, 0xe13c8d83, 0x927010d5,
+    0x11107d9f, 0x07647db9, 0xb2e3e4d4, 0x3d4f285e,
+    0xb9afa820, 0xfade82e0, 0xa067268b, 0x8272792e,
+    0x553fb2c0, 0x489ae22b, 0xd4ef9794, 0x125e3fbc,
+    0x21fffcee, 0x825b1bfd, 0x9255c5ed, 0x1257a240,
+    0x4e1a8302, 0xbae07fff, 0x528246e7, 0x8e57140e,
+    0x3373f7bf, 0x8c9f8188, 0xa6fc4ee8, 0xc982b5a5,
+    0xa8c01db7, 0x579fc264, 0x67094f31, 0xf2bd3f5f,
+    0x40fff7c1, 0x1fb78dfc, 0x8e6bd2c1, 0x437be59b,
+    0x99b03dbf, 0xb5dbc64b, 0x638dc0e6, 0x55819d99,
+    0xa197c81c, 0x4a012d6e, 0xc5884a28, 0xccc36f71,
+    0xb843c213, 0x6c0743f1, 0x8309893c, 0x0feddd5f,
+    0x2f7fe850, 0xd7c07f7e, 0x02507fbf, 0x5afb9a04,
+    0xa747d2d0, 0x1651192e, 0xaf70bf3e, 0x58c31380,
+    0x5f98302e, 0x727cc3c4, 0x0a0fb402, 0x0f7fef82,
+    0x8c96fdad, 0x5d2c2aae, 0x8ee99a49, 0x50da88b8,
+    0x8427f4a0, 0x1eac5790, 0x796fb449, 0x8252dc15,
+    0xefbd7d9b, 0xa672597d, 0xada840d8, 0x45f54504,
+    0xfa5d7403, 0xe83ec305, 0x4f91751a, 0x925669c2,
+    0x23efe941, 0xa903f12e, 0x60270df2, 0x0276e4b6,
+    0x94fd6574, 0x927985b2, 0x8276dbcb, 0x02778176,
+    0xf8af918d, 0x4e48f79e, 0x8f616ddf, 0xe29d840e,
+    0x842f7d83, 0x340ce5c8, 0x96bbb682, 0x93b4b148,
+    0xef303cab, 0x984faf28, 0x779faf9b, 0x92dc560d,
+    0x224d1e20, 0x8437aa88, 0x7d29dc96, 0x2756d3dc,
+    0x8b907cee, 0xb51fd240, 0xe7c07ce3, 0xe566b4a1,
+    0xc3e9615e, 0x3cf8209d, 0x6094d1e3, 0xcd9ca341,
+    0x5c76460e, 0x00ea983b, 0xd4d67881, 0xfd47572c,
+    0xf76cedd9, 0xbda8229c, 0x127dadaa, 0x438a074e,
+    0x1f97c090, 0x081bdb8a, 0x93a07ebe, 0xb938ca15,
+    0x97b03cff, 0x3dc2c0f8, 0x8d1ab2ec, 0x64380e51,
+    0x68cc7bfb, 0xd90f2788, 0x12490181, 0x5de5ffd4,
+    0xdd7ef86a, 0x76a2e214, 0xb9a40368, 0x925d958f,
+    0x4b39fffa, 0xba39aee9, 0xa4ffd30b, 0xfaf7933b,
+    0x6d498623, 0x193cbcfa, 0x27627545, 0x825cf47a,
+    0x61bd8ba0, 0xd11e42d1, 0xcead04f4, 0x127ea392,
+    0x10428db7, 0x8272a972, 0x9270c4a8, 0x127de50b,
+    0x285ba1c8, 0x3c62f44f, 0x35c0eaa5, 0xe805d231,
+    0x428929fb, 0xb4fcdf82, 0x4fb66a53, 0x0e7dc15b,
+    0x1f081fab, 0x108618ae, 0xfcfd086d, 0xf9ff2889,
+    0x694bcc11, 0x236a5cae, 0x12deca4d, 0x2c3f8cc5,
+    0xd2d02dfe, 0xf8ef5896, 0xe4cf52da, 0x95155b67,
+    0x494a488c, 0xb9b6a80c, 0x5c8f82bc, 0x89d36b45,
+    0x3a609437, 0xec00c9a9, 0x44715253, 0x0a874b49,
+    0xd773bc40, 0x7c34671c, 0x02717ef6, 0x4feb5536,
+    0xa2d02fff, 0xd2bf60c4, 0xd43f03c0, 0x50b4ef6d,
+    0x07478cd1, 0x006e1888, 0xa2e53f55, 0xb9e6d4bc,
+    0xa2048016, 0x97573833, 0xd7207d67, 0xde0f8f3d,
+    0x72f87b33, 0xabcc4f33, 0x7688c55d, 0x7b00a6b0,
+    0x947b0001, 0x570075d2, 0xf9bb88f8, 0x8942019e,
+    0x4264a5ff, 0x856302e0, 0x72dbd92b, 0xee971b69,
+    0x6ea22fde, 0x5f08ae2b, 0xaf7a616d, 0xe5c98767,
+    0xcf1febd2, 0x61efc8c2, 0xf1ac2571, 0xcc8239c2,
+    0x67214cb8, 0xb1e583d1, 0xb7dc3e62, 0x7f10bdce,
+    0xf90a5c38, 0x0ff0443d, 0x606e6dc6, 0x60543a49,
+    0x5727c148, 0x2be98a1d, 0x8ab41738, 0x20e1be24,
+    0xaf96da0f, 0x68458425, 0x99833be5, 0x600d457d,
+    0x282f9350, 0x8334b362, 0xd91d1120, 0x2b6d8da0,
+    0x642b1e31, 0x9c305a00, 0x52bce688, 0x1b03588a,
+    0xf7baefd5, 0x4142ed9c, 0xa4315c11, 0x83323ec5,
+    0xdfef4636, 0xa133c501, 0xe9d3531c, 0xee353783,
+};
+
+OPENSSL_GLOBAL const CAST_LONG CAST_S_table3[256] = {
+    0x9db30420, 0x1fb6e9de, 0xa7be7bef, 0xd273a298,
+    0x4a4f7bdb, 0x64ad8c57, 0x85510443, 0xfa020ed1,
+    0x7e287aff, 0xe60fb663, 0x095f35a1, 0x79ebf120,
+    0xfd059d43, 0x6497b7b1, 0xf3641f63, 0x241e4adf,
+    0x28147f5f, 0x4fa2b8cd, 0xc9430040, 0x0cc32220,
+    0xfdd30b30, 0xc0a5374f, 0x1d2d00d9, 0x24147b15,
+    0xee4d111a, 0x0fca5167, 0x71ff904c, 0x2d195ffe,
+    0x1a05645f, 0x0c13fefe, 0x081b08ca, 0x05170121,
+    0x80530100, 0xe83e5efe, 0xac9af4f8, 0x7fe72701,
+    0xd2b8ee5f, 0x06df4261, 0xbb9e9b8a, 0x7293ea25,
+    0xce84ffdf, 0xf5718801, 0x3dd64b04, 0xa26f263b,
+    0x7ed48400, 0x547eebe6, 0x446d4ca0, 0x6cf3d6f5,
+    0x2649abdf, 0xaea0c7f5, 0x36338cc1, 0x503f7e93,
+    0xd3772061, 0x11b638e1, 0x72500e03, 0xf80eb2bb,
+    0xabe0502e, 0xec8d77de, 0x57971e81, 0xe14f6746,
+    0xc9335400, 0x6920318f, 0x081dbb99, 0xffc304a5,
+    0x4d351805, 0x7f3d5ce3, 0xa6c866c6, 0x5d5bcca9,
+    0xdaec6fea, 0x9f926f91, 0x9f46222f, 0x3991467d,
+    0xa5bf6d8e, 0x1143c44f, 0x43958302, 0xd0214eeb,
+    0x022083b8, 0x3fb6180c, 0x18f8931e, 0x281658e6,
+    0x26486e3e, 0x8bd78a70, 0x7477e4c1, 0xb506e07c,
+    0xf32d0a25, 0x79098b02, 0xe4eabb81, 0x28123b23,
+    0x69dead38, 0x1574ca16, 0xdf871b62, 0x211c40b7,
+    0xa51a9ef9, 0x0014377b, 0x041e8ac8, 0x09114003,
+    0xbd59e4d2, 0xe3d156d5, 0x4fe876d5, 0x2f91a340,
+    0x557be8de, 0x00eae4a7, 0x0ce5c2ec, 0x4db4bba6,
+    0xe756bdff, 0xdd3369ac, 0xec17b035, 0x06572327,
+    0x99afc8b0, 0x56c8c391, 0x6b65811c, 0x5e146119,
+    0x6e85cb75, 0xbe07c002, 0xc2325577, 0x893ff4ec,
+    0x5bbfc92d, 0xd0ec3b25, 0xb7801ab7, 0x8d6d3b24,
+    0x20c763ef, 0xc366a5fc, 0x9c382880, 0x0ace3205,
+    0xaac9548a, 0xeca1d7c7, 0x041afa32, 0x1d16625a,
+    0x6701902c, 0x9b757a54, 0x31d477f7, 0x9126b031,
+    0x36cc6fdb, 0xc70b8b46, 0xd9e66a48, 0x56e55a79,
+    0x026a4ceb, 0x52437eff, 0x2f8f76b4, 0x0df980a5,
+    0x8674cde3, 0xedda04eb, 0x17a9be04, 0x2c18f4df,
+    0xb7747f9d, 0xab2af7b4, 0xefc34d20, 0x2e096b7c,
+    0x1741a254, 0xe5b6a035, 0x213d42f6, 0x2c1c7c26,
+    0x61c2f50f, 0x6552daf9, 0xd2c231f8, 0x25130f69,
+    0xd8167fa2, 0x0418f2c8, 0x001a96a6, 0x0d1526ab,
+    0x63315c21, 0x5e0a72ec, 0x49bafefd, 0x187908d9,
+    0x8d0dbd86, 0x311170a7, 0x3e9b640c, 0xcc3e10d7,
+    0xd5cad3b6, 0x0caec388, 0xf73001e1, 0x6c728aff,
+    0x71eae2a1, 0x1f9af36e, 0xcfcbd12f, 0xc1de8417,
+    0xac07be6b, 0xcb44a1d8, 0x8b9b0f56, 0x013988c3,
+    0xb1c52fca, 0xb4be31cd, 0xd8782806, 0x12a3a4e2,
+    0x6f7de532, 0x58fd7eb6, 0xd01ee900, 0x24adffc2,
+    0xf4990fc5, 0x9711aac5, 0x001d7b95, 0x82e5e7d2,
+    0x109873f6, 0x00613096, 0xc32d9521, 0xada121ff,
+    0x29908415, 0x7fbb977f, 0xaf9eb3db, 0x29c9ed2a,
+    0x5ce2a465, 0xa730f32c, 0xd0aa3fe8, 0x8a5cc091,
+    0xd49e2ce7, 0x0ce454a9, 0xd60acd86, 0x015f1919,
+    0x77079103, 0xdea03af6, 0x78a8565e, 0xdee356df,
+    0x21f05cbe, 0x8b75e387, 0xb3c50651, 0xb8a5c3ef,
+    0xd8eeb6d2, 0xe523be77, 0xc2154529, 0x2f69efdf,
+    0xafe67afb, 0xf470c4b2, 0xf3e0eb5b, 0xd6cc9876,
+    0x39e4460c, 0x1fda8538, 0x1987832f, 0xca007367,
+    0xa99144f8, 0x296b299e, 0x492fc295, 0x9266beab,
+    0xb5676e69, 0x9bd3ddda, 0xdf7e052f, 0xdb25701c,
+    0x1b5e51ee, 0xf65324e6, 0x6afce36c, 0x0316cc04,
+    0x8644213e, 0xb7dc59d0, 0x7965291f, 0xccd6fd43,
+    0x41823979, 0x932bcdf6, 0xb657c34d, 0x4edfd282,
+    0x7ae5290c, 0x3cb9536b, 0x851e20fe, 0x9833557e,
+    0x13ecf0b0, 0xd3ffb372, 0x3f85c5c1, 0x0aef7ed2,
+};
+
+OPENSSL_GLOBAL const CAST_LONG CAST_S_table4[256] = {
+    0x7ec90c04, 0x2c6e74b9, 0x9b0e66df, 0xa6337911,
+    0xb86a7fff, 0x1dd358f5, 0x44dd9d44, 0x1731167f,
+    0x08fbf1fa, 0xe7f511cc, 0xd2051b00, 0x735aba00,
+    0x2ab722d8, 0x386381cb, 0xacf6243a, 0x69befd7a,
+    0xe6a2e77f, 0xf0c720cd, 0xc4494816, 0xccf5c180,
+    0x38851640, 0x15b0a848, 0xe68b18cb, 0x4caadeff,
+    0x5f480a01, 0x0412b2aa, 0x259814fc, 0x41d0efe2,
+    0x4e40b48d, 0x248eb6fb, 0x8dba1cfe, 0x41a99b02,
+    0x1a550a04, 0xba8f65cb, 0x7251f4e7, 0x95a51725,
+    0xc106ecd7, 0x97a5980a, 0xc539b9aa, 0x4d79fe6a,
+    0xf2f3f763, 0x68af8040, 0xed0c9e56, 0x11b4958b,
+    0xe1eb5a88, 0x8709e6b0, 0xd7e07156, 0x4e29fea7,
+    0x6366e52d, 0x02d1c000, 0xc4ac8e05, 0x9377f571,
+    0x0c05372a, 0x578535f2, 0x2261be02, 0xd642a0c9,
+    0xdf13a280, 0x74b55bd2, 0x682199c0, 0xd421e5ec,
+    0x53fb3ce8, 0xc8adedb3, 0x28a87fc9, 0x3d959981,
+    0x5c1ff900, 0xfe38d399, 0x0c4eff0b, 0x062407ea,
+    0xaa2f4fb1, 0x4fb96976, 0x90c79505, 0xb0a8a774,
+    0xef55a1ff, 0xe59ca2c2, 0xa6b62d27, 0xe66a4263,
+    0xdf65001f, 0x0ec50966, 0xdfdd55bc, 0x29de0655,
+    0x911e739a, 0x17af8975, 0x32c7911c, 0x89f89468,
+    0x0d01e980, 0x524755f4, 0x03b63cc9, 0x0cc844b2,
+    0xbcf3f0aa, 0x87ac36e9, 0xe53a7426, 0x01b3d82b,
+    0x1a9e7449, 0x64ee2d7e, 0xcddbb1da, 0x01c94910,
+    0xb868bf80, 0x0d26f3fd, 0x9342ede7, 0x04a5c284,
+    0x636737b6, 0x50f5b616, 0xf24766e3, 0x8eca36c1,
+    0x136e05db, 0xfef18391, 0xfb887a37, 0xd6e7f7d4,
+    0xc7fb7dc9, 0x3063fcdf, 0xb6f589de, 0xec2941da,
+    0x26e46695, 0xb7566419, 0xf654efc5, 0xd08d58b7,
+    0x48925401, 0xc1bacb7f, 0xe5ff550f, 0xb6083049,
+    0x5bb5d0e8, 0x87d72e5a, 0xab6a6ee1, 0x223a66ce,
+    0xc62bf3cd, 0x9e0885f9, 0x68cb3e47, 0x086c010f,
+    0xa21de820, 0xd18b69de, 0xf3f65777, 0xfa02c3f6,
+    0x407edac3, 0xcbb3d550, 0x1793084d, 0xb0d70eba,
+    0x0ab378d5, 0xd951fb0c, 0xded7da56, 0x4124bbe4,
+    0x94ca0b56, 0x0f5755d1, 0xe0e1e56e, 0x6184b5be,
+    0x580a249f, 0x94f74bc0, 0xe327888e, 0x9f7b5561,
+    0xc3dc0280, 0x05687715, 0x646c6bd7, 0x44904db3,
+    0x66b4f0a3, 0xc0f1648a, 0x697ed5af, 0x49e92ff6,
+    0x309e374f, 0x2cb6356a, 0x85808573, 0x4991f840,
+    0x76f0ae02, 0x083be84d, 0x28421c9a, 0x44489406,
+    0x736e4cb8, 0xc1092910, 0x8bc95fc6, 0x7d869cf4,
+    0x134f616f, 0x2e77118d, 0xb31b2be1, 0xaa90b472,
+    0x3ca5d717, 0x7d161bba, 0x9cad9010, 0xaf462ba2,
+    0x9fe459d2, 0x45d34559, 0xd9f2da13, 0xdbc65487,
+    0xf3e4f94e, 0x176d486f, 0x097c13ea, 0x631da5c7,
+    0x445f7382, 0x175683f4, 0xcdc66a97, 0x70be0288,
+    0xb3cdcf72, 0x6e5dd2f3, 0x20936079, 0x459b80a5,
+    0xbe60e2db, 0xa9c23101, 0xeba5315c, 0x224e42f2,
+    0x1c5c1572, 0xf6721b2c, 0x1ad2fff3, 0x8c25404e,
+    0x324ed72f, 0x4067b7fd, 0x0523138e, 0x5ca3bc78,
+    0xdc0fd66e, 0x75922283, 0x784d6b17, 0x58ebb16e,
+    0x44094f85, 0x3f481d87, 0xfcfeae7b, 0x77b5ff76,
+    0x8c2302bf, 0xaaf47556, 0x5f46b02a, 0x2b092801,
+    0x3d38f5f7, 0x0ca81f36, 0x52af4a8a, 0x66d5e7c0,
+    0xdf3b0874, 0x95055110, 0x1b5ad7a8, 0xf61ed5ad,
+    0x6cf6e479, 0x20758184, 0xd0cefa65, 0x88f7be58,
+    0x4a046826, 0x0ff6f8f3, 0xa09c7f70, 0x5346aba0,
+    0x5ce96c28, 0xe176eda3, 0x6bac307f, 0x376829d2,
+    0x85360fa9, 0x17e3fe2a, 0x24b79767, 0xf5a96b20,
+    0xd6cd2595, 0x68ff1ebf, 0x7555442c, 0xf19f06be,
+    0xf9e0659a, 0xeeb9491d, 0x34010718, 0xbb30cab8,
+    0xe822fe15, 0x88570983, 0x750e6249, 0xda627e55,
+    0x5e76ffa8, 0xb1534546, 0x6d47de08, 0xefe9e7d4,
+};
+
+OPENSSL_GLOBAL const CAST_LONG CAST_S_table5[256] = {
+    0xf6fa8f9d, 0x2cac6ce1, 0x4ca34867, 0xe2337f7c,
+    0x95db08e7, 0x016843b4, 0xeced5cbc, 0x325553ac,
+    0xbf9f0960, 0xdfa1e2ed, 0x83f0579d, 0x63ed86b9,
+    0x1ab6a6b8, 0xde5ebe39, 0xf38ff732, 0x8989b138,
+    0x33f14961, 0xc01937bd, 0xf506c6da, 0xe4625e7e,
+    0xa308ea99, 0x4e23e33c, 0x79cbd7cc, 0x48a14367,
+    0xa3149619, 0xfec94bd5, 0xa114174a, 0xeaa01866,
+    0xa084db2d, 0x09a8486f, 0xa888614a, 0x2900af98,
+    0x01665991, 0xe1992863, 0xc8f30c60, 0x2e78ef3c,
+    0xd0d51932, 0xcf0fec14, 0xf7ca07d2, 0xd0a82072,
+    0xfd41197e, 0x9305a6b0, 0xe86be3da, 0x74bed3cd,
+    0x372da53c, 0x4c7f4448, 0xdab5d440, 0x6dba0ec3,
+    0x083919a7, 0x9fbaeed9, 0x49dbcfb0, 0x4e670c53,
+    0x5c3d9c01, 0x64bdb941, 0x2c0e636a, 0xba7dd9cd,
+    0xea6f7388, 0xe70bc762, 0x35f29adb, 0x5c4cdd8d,
+    0xf0d48d8c, 0xb88153e2, 0x08a19866, 0x1ae2eac8,
+    0x284caf89, 0xaa928223, 0x9334be53, 0x3b3a21bf,
+    0x16434be3, 0x9aea3906, 0xefe8c36e, 0xf890cdd9,
+    0x80226dae, 0xc340a4a3, 0xdf7e9c09, 0xa694a807,
+    0x5b7c5ecc, 0x221db3a6, 0x9a69a02f, 0x68818a54,
+    0xceb2296f, 0x53c0843a, 0xfe893655, 0x25bfe68a,
+    0xb4628abc, 0xcf222ebf, 0x25ac6f48, 0xa9a99387,
+    0x53bddb65, 0xe76ffbe7, 0xe967fd78, 0x0ba93563,
+    0x8e342bc1, 0xe8a11be9, 0x4980740d, 0xc8087dfc,
+    0x8de4bf99, 0xa11101a0, 0x7fd37975, 0xda5a26c0,
+    0xe81f994f, 0x9528cd89, 0xfd339fed, 0xb87834bf,
+    0x5f04456d, 0x22258698, 0xc9c4c83b, 0x2dc156be,
+    0x4f628daa, 0x57f55ec5, 0xe2220abe, 0xd2916ebf,
+    0x4ec75b95, 0x24f2c3c0, 0x42d15d99, 0xcd0d7fa0,
+    0x7b6e27ff, 0xa8dc8af0, 0x7345c106, 0xf41e232f,
+    0x35162386, 0xe6ea8926, 0x3333b094, 0x157ec6f2,
+    0x372b74af, 0x692573e4, 0xe9a9d848, 0xf3160289,
+    0x3a62ef1d, 0xa787e238, 0xf3a5f676, 0x74364853,
+    0x20951063, 0x4576698d, 0xb6fad407, 0x592af950,
+    0x36f73523, 0x4cfb6e87, 0x7da4cec0, 0x6c152daa,
+    0xcb0396a8, 0xc50dfe5d, 0xfcd707ab, 0x0921c42f,
+    0x89dff0bb, 0x5fe2be78, 0x448f4f33, 0x754613c9,
+    0x2b05d08d, 0x48b9d585, 0xdc049441, 0xc8098f9b,
+    0x7dede786, 0xc39a3373, 0x42410005, 0x6a091751,
+    0x0ef3c8a6, 0x890072d6, 0x28207682, 0xa9a9f7be,
+    0xbf32679d, 0xd45b5b75, 0xb353fd00, 0xcbb0e358,
+    0x830f220a, 0x1f8fb214, 0xd372cf08, 0xcc3c4a13,
+    0x8cf63166, 0x061c87be, 0x88c98f88, 0x6062e397,
+    0x47cf8e7a, 0xb6c85283, 0x3cc2acfb, 0x3fc06976,
+    0x4e8f0252, 0x64d8314d, 0xda3870e3, 0x1e665459,
+    0xc10908f0, 0x513021a5, 0x6c5b68b7, 0x822f8aa0,
+    0x3007cd3e, 0x74719eef, 0xdc872681, 0x073340d4,
+    0x7e432fd9, 0x0c5ec241, 0x8809286c, 0xf592d891,
+    0x08a930f6, 0x957ef305, 0xb7fbffbd, 0xc266e96f,
+    0x6fe4ac98, 0xb173ecc0, 0xbc60b42a, 0x953498da,
+    0xfba1ae12, 0x2d4bd736, 0x0f25faab, 0xa4f3fceb,
+    0xe2969123, 0x257f0c3d, 0x9348af49, 0x361400bc,
+    0xe8816f4a, 0x3814f200, 0xa3f94043, 0x9c7a54c2,
+    0xbc704f57, 0xda41e7f9, 0xc25ad33a, 0x54f4a084,
+    0xb17f5505, 0x59357cbe, 0xedbd15c8, 0x7f97c5ab,
+    0xba5ac7b5, 0xb6f6deaf, 0x3a479c3a, 0x5302da25,
+    0x653d7e6a, 0x54268d49, 0x51a477ea, 0x5017d55b,
+    0xd7d25d88, 0x44136c76, 0x0404a8c8, 0xb8e5a121,
+    0xb81a928a, 0x60ed5869, 0x97c55b96, 0xeaec991b,
+    0x29935913, 0x01fdb7f1, 0x088e8dfa, 0x9ab6f6f5,
+    0x3b4cbf9f, 0x4a5de3ab, 0xe6051d35, 0xa0e1d855,
+    0xd36b4cf1, 0xf544edeb, 0xb0e93524, 0xbebb8fbd,
+    0xa2d762cf, 0x49c92f54, 0x38b5f331, 0x7128a454,
+    0x48392905, 0xa65b1db8, 0x851c97bd, 0xd675cf2f,
+};
+
+OPENSSL_GLOBAL const CAST_LONG CAST_S_table6[256] = {
+    0x85e04019, 0x332bf567, 0x662dbfff, 0xcfc65693,
+    0x2a8d7f6f, 0xab9bc912, 0xde6008a1, 0x2028da1f,
+    0x0227bce7, 0x4d642916, 0x18fac300, 0x50f18b82,
+    0x2cb2cb11, 0xb232e75c, 0x4b3695f2, 0xb28707de,
+    0xa05fbcf6, 0xcd4181e9, 0xe150210c, 0xe24ef1bd,
+    0xb168c381, 0xfde4e789, 0x5c79b0d8, 0x1e8bfd43,
+    0x4d495001, 0x38be4341, 0x913cee1d, 0x92a79c3f,
+    0x089766be, 0xbaeeadf4, 0x1286becf, 0xb6eacb19,
+    0x2660c200, 0x7565bde4, 0x64241f7a, 0x8248dca9,
+    0xc3b3ad66, 0x28136086, 0x0bd8dfa8, 0x356d1cf2,
+    0x107789be, 0xb3b2e9ce, 0x0502aa8f, 0x0bc0351e,
+    0x166bf52a, 0xeb12ff82, 0xe3486911, 0xd34d7516,
+    0x4e7b3aff, 0x5f43671b, 0x9cf6e037, 0x4981ac83,
+    0x334266ce, 0x8c9341b7, 0xd0d854c0, 0xcb3a6c88,
+    0x47bc2829, 0x4725ba37, 0xa66ad22b, 0x7ad61f1e,
+    0x0c5cbafa, 0x4437f107, 0xb6e79962, 0x42d2d816,
+    0x0a961288, 0xe1a5c06e, 0x13749e67, 0x72fc081a,
+    0xb1d139f7, 0xf9583745, 0xcf19df58, 0xbec3f756,
+    0xc06eba30, 0x07211b24, 0x45c28829, 0xc95e317f,
+    0xbc8ec511, 0x38bc46e9, 0xc6e6fa14, 0xbae8584a,
+    0xad4ebc46, 0x468f508b, 0x7829435f, 0xf124183b,
+    0x821dba9f, 0xaff60ff4, 0xea2c4e6d, 0x16e39264,
+    0x92544a8b, 0x009b4fc3, 0xaba68ced, 0x9ac96f78,
+    0x06a5b79a, 0xb2856e6e, 0x1aec3ca9, 0xbe838688,
+    0x0e0804e9, 0x55f1be56, 0xe7e5363b, 0xb3a1f25d,
+    0xf7debb85, 0x61fe033c, 0x16746233, 0x3c034c28,
+    0xda6d0c74, 0x79aac56c, 0x3ce4e1ad, 0x51f0c802,
+    0x98f8f35a, 0x1626a49f, 0xeed82b29, 0x1d382fe3,
+    0x0c4fb99a, 0xbb325778, 0x3ec6d97b, 0x6e77a6a9,
+    0xcb658b5c, 0xd45230c7, 0x2bd1408b, 0x60c03eb7,
+    0xb9068d78, 0xa33754f4, 0xf430c87d, 0xc8a71302,
+    0xb96d8c32, 0xebd4e7be, 0xbe8b9d2d, 0x7979fb06,
+    0xe7225308, 0x8b75cf77, 0x11ef8da4, 0xe083c858,
+    0x8d6b786f, 0x5a6317a6, 0xfa5cf7a0, 0x5dda0033,
+    0xf28ebfb0, 0xf5b9c310, 0xa0eac280, 0x08b9767a,
+    0xa3d9d2b0, 0x79d34217, 0x021a718d, 0x9ac6336a,
+    0x2711fd60, 0x438050e3, 0x069908a8, 0x3d7fedc4,
+    0x826d2bef, 0x4eeb8476, 0x488dcf25, 0x36c9d566,
+    0x28e74e41, 0xc2610aca, 0x3d49a9cf, 0xbae3b9df,
+    0xb65f8de6, 0x92aeaf64, 0x3ac7d5e6, 0x9ea80509,
+    0xf22b017d, 0xa4173f70, 0xdd1e16c3, 0x15e0d7f9,
+    0x50b1b887, 0x2b9f4fd5, 0x625aba82, 0x6a017962,
+    0x2ec01b9c, 0x15488aa9, 0xd716e740, 0x40055a2c,
+    0x93d29a22, 0xe32dbf9a, 0x058745b9, 0x3453dc1e,
+    0xd699296e, 0x496cff6f, 0x1c9f4986, 0xdfe2ed07,
+    0xb87242d1, 0x19de7eae, 0x053e561a, 0x15ad6f8c,
+    0x66626c1c, 0x7154c24c, 0xea082b2a, 0x93eb2939,
+    0x17dcb0f0, 0x58d4f2ae, 0x9ea294fb, 0x52cf564c,
+    0x9883fe66, 0x2ec40581, 0x763953c3, 0x01d6692e,
+    0xd3a0c108, 0xa1e7160e, 0xe4f2dfa6, 0x693ed285,
+    0x74904698, 0x4c2b0edd, 0x4f757656, 0x5d393378,
+    0xa132234f, 0x3d321c5d, 0xc3f5e194, 0x4b269301,
+    0xc79f022f, 0x3c997e7e, 0x5e4f9504, 0x3ffafbbd,
+    0x76f7ad0e, 0x296693f4, 0x3d1fce6f, 0xc61e45be,
+    0xd3b5ab34, 0xf72bf9b7, 0x1b0434c0, 0x4e72b567,
+    0x5592a33d, 0xb5229301, 0xcfd2a87f, 0x60aeb767,
+    0x1814386b, 0x30bcc33d, 0x38a0c07d, 0xfd1606f2,
+    0xc363519b, 0x589dd390, 0x5479f8e6, 0x1cb8d647,
+    0x97fd61a9, 0xea7759f4, 0x2d57539d, 0x569a58cf,
+    0xe84e63ad, 0x462e1b78, 0x6580f87e, 0xf3817914,
+    0x91da55f4, 0x40a230f3, 0xd1988f35, 0xb6e318d2,
+    0x3ffa50bc, 0x3d40f021, 0xc3c0bdae, 0x4958c24c,
+    0x518f36b2, 0x84b1d370, 0x0fedce83, 0x878ddada,
+    0xf2a279c7, 0x94e01be8, 0x90716f4b, 0x954b8aa3,
+};
+
+OPENSSL_GLOBAL const CAST_LONG CAST_S_table7[256] = {
+    0xe216300d, 0xbbddfffc, 0xa7ebdabd, 0x35648095,
+    0x7789f8b7, 0xe6c1121b, 0x0e241600, 0x052ce8b5,
+    0x11a9cfb0, 0xe5952f11, 0xece7990a, 0x9386d174,
+    0x2a42931c, 0x76e38111, 0xb12def3a, 0x37ddddfc,
+    0xde9adeb1, 0x0a0cc32c, 0xbe197029, 0x84a00940,
+    0xbb243a0f, 0xb4d137cf, 0xb44e79f0, 0x049eedfd,
+    0x0b15a15d, 0x480d3168, 0x8bbbde5a, 0x669ded42,
+    0xc7ece831, 0x3f8f95e7, 0x72df191b, 0x7580330d,
+    0x94074251, 0x5c7dcdfa, 0xabbe6d63, 0xaa402164,
+    0xb301d40a, 0x02e7d1ca, 0x53571dae, 0x7a3182a2,
+    0x12a8ddec, 0xfdaa335d, 0x176f43e8, 0x71fb46d4,
+    0x38129022, 0xce949ad4, 0xb84769ad, 0x965bd862,
+    0x82f3d055, 0x66fb9767, 0x15b80b4e, 0x1d5b47a0,
+    0x4cfde06f, 0xc28ec4b8, 0x57e8726e, 0x647a78fc,
+    0x99865d44, 0x608bd593, 0x6c200e03, 0x39dc5ff6,
+    0x5d0b00a3, 0xae63aff2, 0x7e8bd632, 0x70108c0c,
+    0xbbd35049, 0x2998df04, 0x980cf42a, 0x9b6df491,
+    0x9e7edd53, 0x06918548, 0x58cb7e07, 0x3b74ef2e,
+    0x522fffb1, 0xd24708cc, 0x1c7e27cd, 0xa4eb215b,
+    0x3cf1d2e2, 0x19b47a38, 0x424f7618, 0x35856039,
+    0x9d17dee7, 0x27eb35e6, 0xc9aff67b, 0x36baf5b8,
+    0x09c467cd, 0xc18910b1, 0xe11dbf7b, 0x06cd1af8,
+    0x7170c608, 0x2d5e3354, 0xd4de495a, 0x64c6d006,
+    0xbcc0c62c, 0x3dd00db3, 0x708f8f34, 0x77d51b42,
+    0x264f620f, 0x24b8d2bf, 0x15c1b79e, 0x46a52564,
+    0xf8d7e54e, 0x3e378160, 0x7895cda5, 0x859c15a5,
+    0xe6459788, 0xc37bc75f, 0xdb07ba0c, 0x0676a3ab,
+    0x7f229b1e, 0x31842e7b, 0x24259fd7, 0xf8bef472,
+    0x835ffcb8, 0x6df4c1f2, 0x96f5b195, 0xfd0af0fc,
+    0xb0fe134c, 0xe2506d3d, 0x4f9b12ea, 0xf215f225,
+    0xa223736f, 0x9fb4c428, 0x25d04979, 0x34c713f8,
+    0xc4618187, 0xea7a6e98, 0x7cd16efc, 0x1436876c,
+    0xf1544107, 0xbedeee14, 0x56e9af27, 0xa04aa441,
+    0x3cf7c899, 0x92ecbae6, 0xdd67016d, 0x151682eb,
+    0xa842eedf, 0xfdba60b4, 0xf1907b75, 0x20e3030f,
+    0x24d8c29e, 0xe139673b, 0xefa63fb8, 0x71873054,
+    0xb6f2cf3b, 0x9f326442, 0xcb15a4cc, 0xb01a4504,
+    0xf1e47d8d, 0x844a1be5, 0xbae7dfdc, 0x42cbda70,
+    0xcd7dae0a, 0x57e85b7a, 0xd53f5af6, 0x20cf4d8c,
+    0xcea4d428, 0x79d130a4, 0x3486ebfb, 0x33d3cddc,
+    0x77853b53, 0x37effcb5, 0xc5068778, 0xe580b3e6,
+    0x4e68b8f4, 0xc5c8b37e, 0x0d809ea2, 0x398feb7c,
+    0x132a4f94, 0x43b7950e, 0x2fee7d1c, 0x223613bd,
+    0xdd06caa2, 0x37df932b, 0xc4248289, 0xacf3ebc3,
+    0x5715f6b7, 0xef3478dd, 0xf267616f, 0xc148cbe4,
+    0x9052815e, 0x5e410fab, 0xb48a2465, 0x2eda7fa4,
+    0xe87b40e4, 0xe98ea084, 0x5889e9e1, 0xefd390fc,
+    0xdd07d35b, 0xdb485694, 0x38d7e5b2, 0x57720101,
+    0x730edebc, 0x5b643113, 0x94917e4f, 0x503c2fba,
+    0x646f1282, 0x7523d24a, 0xe0779695, 0xf9c17a8f,
+    0x7a5b2121, 0xd187b896, 0x29263a4d, 0xba510cdf,
+    0x81f47c9f, 0xad1163ed, 0xea7b5965, 0x1a00726e,
+    0x11403092, 0x00da6d77, 0x4a0cdd61, 0xad1f4603,
+    0x605bdfb0, 0x9eedc364, 0x22ebe6a8, 0xcee7d28a,
+    0xa0e736a0, 0x5564a6b9, 0x10853209, 0xc7eb8f37,
+    0x2de705ca, 0x8951570f, 0xdf09822b, 0xbd691a6c,
+    0xaa12e4f2, 0x87451c0f, 0xe0f6a27a, 0x3ada4819,
+    0x4cf1764f, 0x0d771c2b, 0x67cdb156, 0x350d8384,
+    0x5938fa0f, 0x42399ef3, 0x36997b07, 0x0e84093d,
+    0x4aa93e61, 0x8360d87b, 0x1fa98b0c, 0x1149382c,
+    0xe97625a5, 0x0614d1b7, 0x0e25244b, 0x0c768347,
+    0x589e8d82, 0x0d2059d1, 0xa466bb1e, 0xf8da0a82,
+    0x04f19130, 0xba6e4ec0, 0x99265164, 0x1ee7230d,
+    0x50b2ad80, 0xeaee6801, 0x8db2a283, 0xea8bf59e,
+};
diff --git a/crypto/chacha/asm/chacha-armv4.pl b/crypto/chacha/asm/chacha-armv4.pl
new file mode 100755
index 000000000..b5e21e493
--- /dev/null
+++ b/crypto/chacha/asm/chacha-armv4.pl
@@ -0,0 +1,1158 @@
+#! /usr/bin/env perl
+# Copyright 2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+#
+# ====================================================================
+# Written by Andy Polyakov  for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+#
+# December 2014
+# 
+# ChaCha20 for ARMv4.
+#
+# Performance in cycles per byte out of large buffer.
+#
+#			IALU/gcc-4.4    1xNEON      3xNEON+1xIALU
+#
+# Cortex-A5		19.3(*)/+95%    21.8        14.1
+# Cortex-A8		10.5(*)/+160%   13.9        6.35
+# Cortex-A9		12.9(**)/+110%  14.3        6.50
+# Cortex-A15		11.0/+40%       16.0        5.00
+# Snapdragon S4		11.5/+125%      13.6        4.90
+#
+# (*)	most "favourable" result for aligned data on little-endian
+#	processor, result for misaligned data is 10-15% lower;
+# (**)	this result is a trade-off: it can be improved by 20%,
+#	but then Snapdragon S4 and Cortex-A8 results get
+#	20-25% worse;
+
+$flavour = shift;
+if ($flavour=~/\w[\w\-]*\.\w+$/) { $output=$flavour; undef $flavour; }
+else { while (($output=shift) && ($output!~/\w[\w\-]*\.\w+$/)) {} }
+
+if ($flavour && $flavour ne "void") {
+    $0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+    ( $xlate="${dir}arm-xlate.pl" and -f $xlate ) or
+    ( $xlate="${dir}../../perlasm/arm-xlate.pl" and -f $xlate) or
+    die "can't locate arm-xlate.pl";
+
+    open STDOUT,"| \"$^X\" $xlate $flavour $output";
+} else {
+    open STDOUT,">$output";
+}
+
+sub AUTOLOAD()		# thunk [simplified] x86-style perlasm
+{ my $opcode = $AUTOLOAD; $opcode =~ s/.*:://; $opcode =~ s/_/\./;
+  my $arg = pop;
+    $arg = "#$arg" if ($arg*1 eq $arg);
+    $code .= "\t$opcode\t".join(',',@_,$arg)."\n";
+}
+
+my @x=map("r$_",(0..7,"x","x","x","x",12,"x",14,"x"));
+my @t=map("r$_",(8..11));
+
+sub ROUND {
+my ($a0,$b0,$c0,$d0)=@_;
+my ($a1,$b1,$c1,$d1)=map(($_&~3)+(($_+1)&3),($a0,$b0,$c0,$d0));
+my ($a2,$b2,$c2,$d2)=map(($_&~3)+(($_+1)&3),($a1,$b1,$c1,$d1));
+my ($a3,$b3,$c3,$d3)=map(($_&~3)+(($_+1)&3),($a2,$b2,$c2,$d2));
+my $odd = $d0&1;
+my ($xc,$xc_) = (@t[0..1]);
+my ($xd,$xd_) = $odd ? (@t[2],@x[$d1]) : (@x[$d0],@t[2]);
+my @ret;
+
+	# Consider order in which variables are addressed by their
+	# index:
+	#
+	#       a   b   c   d
+	#
+	#       0   4   8  12 < even round
+	#       1   5   9  13
+	#       2   6  10  14
+	#       3   7  11  15
+	#       0   5  10  15 < odd round
+	#       1   6  11  12
+	#       2   7   8  13
+	#       3   4   9  14
+	#
+	# 'a', 'b' are permanently allocated in registers, @x[0..7],
+	# while 'c's and pair of 'd's are maintained in memory. If
+	# you observe 'c' column, you'll notice that pair of 'c's is
+	# invariant between rounds. This means that we have to reload
+	# them once per round, in the middle. This is why you'll see
+	# bunch of 'c' stores and loads in the middle, but none in
+	# the beginning or end. If you observe 'd' column, you'll
+	# notice that 15 and 13 are reused in next pair of rounds.
+	# This is why these two are chosen for offloading to memory,
+	# to make loads count more.
+							push @ret,(
+	"&add	(@x[$a0],@x[$a0],@x[$b0])",
+	"&mov	($xd,$xd,'ror#16')",
+	 "&add	(@x[$a1],@x[$a1],@x[$b1])",
+	 "&mov	($xd_,$xd_,'ror#16')",
+	"&eor	($xd,$xd,@x[$a0],'ror#16')",
+	 "&eor	($xd_,$xd_,@x[$a1],'ror#16')",
+
+	"&add	($xc,$xc,$xd)",
+	"&mov	(@x[$b0],@x[$b0],'ror#20')",
+	 "&add	($xc_,$xc_,$xd_)",
+	 "&mov	(@x[$b1],@x[$b1],'ror#20')",
+	"&eor	(@x[$b0],@x[$b0],$xc,'ror#20')",
+	 "&eor	(@x[$b1],@x[$b1],$xc_,'ror#20')",
+
+	"&add	(@x[$a0],@x[$a0],@x[$b0])",
+	"&mov	($xd,$xd,'ror#24')",
+	 "&add	(@x[$a1],@x[$a1],@x[$b1])",
+	 "&mov	($xd_,$xd_,'ror#24')",
+	"&eor	($xd,$xd,@x[$a0],'ror#24')",
+	 "&eor	($xd_,$xd_,@x[$a1],'ror#24')",
+
+	"&add	($xc,$xc,$xd)",
+	"&mov	(@x[$b0],@x[$b0],'ror#25')"		);
+							push @ret,(
+	"&str	($xd,'[sp,#4*(16+$d0)]')",
+	"&ldr	($xd,'[sp,#4*(16+$d2)]')"		) if ($odd);
+							push @ret,(
+	 "&add	($xc_,$xc_,$xd_)",
+	 "&mov	(@x[$b1],@x[$b1],'ror#25')"		);
+							push @ret,(
+	 "&str	($xd_,'[sp,#4*(16+$d1)]')",
+	 "&ldr	($xd_,'[sp,#4*(16+$d3)]')"		) if (!$odd);
+							push @ret,(
+	"&eor	(@x[$b0],@x[$b0],$xc,'ror#25')",
+	 "&eor	(@x[$b1],@x[$b1],$xc_,'ror#25')"	);
+
+	$xd=@x[$d2]					if (!$odd);
+	$xd_=@x[$d3]					if ($odd);
+							push @ret,(
+	"&str	($xc,'[sp,#4*(16+$c0)]')",
+	"&ldr	($xc,'[sp,#4*(16+$c2)]')",
+	"&add	(@x[$a2],@x[$a2],@x[$b2])",
+	"&mov	($xd,$xd,'ror#16')",
+	 "&str	($xc_,'[sp,#4*(16+$c1)]')",
+	 "&ldr	($xc_,'[sp,#4*(16+$c3)]')",
+	 "&add	(@x[$a3],@x[$a3],@x[$b3])",
+	 "&mov	($xd_,$xd_,'ror#16')",
+	"&eor	($xd,$xd,@x[$a2],'ror#16')",
+	 "&eor	($xd_,$xd_,@x[$a3],'ror#16')",
+
+	"&add	($xc,$xc,$xd)",
+	"&mov	(@x[$b2],@x[$b2],'ror#20')",
+	 "&add	($xc_,$xc_,$xd_)",
+	 "&mov	(@x[$b3],@x[$b3],'ror#20')",
+	"&eor	(@x[$b2],@x[$b2],$xc,'ror#20')",
+	 "&eor	(@x[$b3],@x[$b3],$xc_,'ror#20')",
+
+	"&add	(@x[$a2],@x[$a2],@x[$b2])",
+	"&mov	($xd,$xd,'ror#24')",
+	 "&add	(@x[$a3],@x[$a3],@x[$b3])",
+	 "&mov	($xd_,$xd_,'ror#24')",
+	"&eor	($xd,$xd,@x[$a2],'ror#24')",
+	 "&eor	($xd_,$xd_,@x[$a3],'ror#24')",
+
+	"&add	($xc,$xc,$xd)",
+	"&mov	(@x[$b2],@x[$b2],'ror#25')",
+	 "&add	($xc_,$xc_,$xd_)",
+	 "&mov	(@x[$b3],@x[$b3],'ror#25')",
+	"&eor	(@x[$b2],@x[$b2],$xc,'ror#25')",
+	 "&eor	(@x[$b3],@x[$b3],$xc_,'ror#25')"	);
+
+	@ret;
+}
+
+$code.=<<___;
+#include "arm_arch.h"
+
+.text
+#if defined(__thumb2__)
+.syntax	unified
+.thumb
+#else
+.code	32
+#endif
+
+#if defined(__thumb2__) || defined(__clang__)
+#define ldrhsb	ldrbhs
+#endif
+
+.align	5
+.Lsigma:
+.long	0x61707865,0x3320646e,0x79622d32,0x6b206574	@ endian-neutral
+.Lone:
+.long	1,0,0,0
+#if __ARM_MAX_ARCH__>=7
+.LOPENSSL_armcap:
+.word   OPENSSL_armcap_P-.LChaCha20_ctr32
+#else
+.word	-1
+#endif
+
+.globl	ChaCha20_ctr32
+.type	ChaCha20_ctr32,%function
+.align	5
+ChaCha20_ctr32:
+.LChaCha20_ctr32:
+	ldr	r12,[sp,#0]		@ pull pointer to counter and nonce
+	stmdb	sp!,{r0-r2,r4-r11,lr}
+#if __ARM_ARCH__<7 && !defined(__thumb2__)
+	sub	r14,pc,#16		@ ChaCha20_ctr32
+#else
+	adr	r14,.LChaCha20_ctr32
+#endif
+	cmp	r2,#0			@ len==0?
+#ifdef	__thumb2__
+	itt	eq
+#endif
+	addeq	sp,sp,#4*3
+	beq	.Lno_data
+#if __ARM_MAX_ARCH__>=7
+	cmp	r2,#192			@ test len
+	bls	.Lshort
+	ldr	r4,[r14,#-32]
+	ldr	r4,[r14,r4]
+# ifdef	__APPLE__
+	ldr	r4,[r4]
+# endif
+	tst	r4,#ARMV7_NEON
+	bne	.LChaCha20_neon
+.Lshort:
+#endif
+	ldmia	r12,{r4-r7}		@ load counter and nonce
+	sub	sp,sp,#4*(16)		@ off-load area
+	sub	r14,r14,#64		@ .Lsigma
+	stmdb	sp!,{r4-r7}		@ copy counter and nonce
+	ldmia	r3,{r4-r11}		@ load key
+	ldmia	r14,{r0-r3}		@ load sigma
+	stmdb	sp!,{r4-r11}		@ copy key
+	stmdb	sp!,{r0-r3}		@ copy sigma
+	str	r10,[sp,#4*(16+10)]	@ off-load "@x[10]"
+	str	r11,[sp,#4*(16+11)]	@ off-load "@x[11]"
+	b	.Loop_outer_enter
+
+.align	4
+.Loop_outer:
+	ldmia	sp,{r0-r9}		@ load key material
+	str	@t[3],[sp,#4*(32+2)]	@ save len
+	str	r12,  [sp,#4*(32+1)]	@ save inp
+	str	r14,  [sp,#4*(32+0)]	@ save out
+.Loop_outer_enter:
+	ldr	@t[3], [sp,#4*(15)]
+	ldr	@x[12],[sp,#4*(12)]	@ modulo-scheduled load
+	ldr	@t[2], [sp,#4*(13)]
+	ldr	@x[14],[sp,#4*(14)]
+	str	@t[3], [sp,#4*(16+15)]
+	mov	@t[3],#10
+	b	.Loop
+
+.align	4
+.Loop:
+	subs	@t[3],@t[3],#1
+___
+	foreach (&ROUND(0, 4, 8,12)) { eval; }
+	foreach (&ROUND(0, 5,10,15)) { eval; }
+$code.=<<___;
+	bne	.Loop
+
+	ldr	@t[3],[sp,#4*(32+2)]	@ load len
+
+	str	@t[0], [sp,#4*(16+8)]	@ modulo-scheduled store
+	str	@t[1], [sp,#4*(16+9)]
+	str	@x[12],[sp,#4*(16+12)]
+	str	@t[2], [sp,#4*(16+13)]
+	str	@x[14],[sp,#4*(16+14)]
+
+	@ at this point we have first half of 512-bit result in
+	@ @x[0-7] and second half at sp+4*(16+8)
+
+	cmp	@t[3],#64		@ done yet?
+#ifdef	__thumb2__
+	itete	lo
+#endif
+	addlo	r12,sp,#4*(0)		@ shortcut or ...
+	ldrhs	r12,[sp,#4*(32+1)]	@ ... load inp
+	addlo	r14,sp,#4*(0)		@ shortcut or ...
+	ldrhs	r14,[sp,#4*(32+0)]	@ ... load out
+
+	ldr	@t[0],[sp,#4*(0)]	@ load key material
+	ldr	@t[1],[sp,#4*(1)]
+
+#if __ARM_ARCH__>=6 || !defined(__ARMEB__)
+# if __ARM_ARCH__<7
+	orr	@t[2],r12,r14
+	tst	@t[2],#3		@ are input and output aligned?
+	ldr	@t[2],[sp,#4*(2)]
+	bne	.Lunaligned
+	cmp	@t[3],#64		@ restore flags
+# else
+	ldr	@t[2],[sp,#4*(2)]
+# endif
+	ldr	@t[3],[sp,#4*(3)]
+
+	add	@x[0],@x[0],@t[0]	@ accumulate key material
+	add	@x[1],@x[1],@t[1]
+# ifdef	__thumb2__
+	itt	hs
+# endif
+	ldrhs	@t[0],[r12],#16		@ load input
+	ldrhs	@t[1],[r12,#-12]
+
+	add	@x[2],@x[2],@t[2]
+	add	@x[3],@x[3],@t[3]
+# ifdef	__thumb2__
+	itt	hs
+# endif
+	ldrhs	@t[2],[r12,#-8]
+	ldrhs	@t[3],[r12,#-4]
+# if __ARM_ARCH__>=6 && defined(__ARMEB__)
+	rev	@x[0],@x[0]
+	rev	@x[1],@x[1]
+	rev	@x[2],@x[2]
+	rev	@x[3],@x[3]
+# endif
+# ifdef	__thumb2__
+	itt	hs
+# endif
+	eorhs	@x[0],@x[0],@t[0]	@ xor with input
+	eorhs	@x[1],@x[1],@t[1]
+	 add	@t[0],sp,#4*(4)
+	str	@x[0],[r14],#16		@ store output
+# ifdef	__thumb2__
+	itt	hs
+# endif
+	eorhs	@x[2],@x[2],@t[2]
+	eorhs	@x[3],@x[3],@t[3]
+	 ldmia	@t[0],{@t[0]-@t[3]}	@ load key material
+	str	@x[1],[r14,#-12]
+	str	@x[2],[r14,#-8]
+	str	@x[3],[r14,#-4]
+
+	add	@x[4],@x[4],@t[0]	@ accumulate key material
+	add	@x[5],@x[5],@t[1]
+# ifdef	__thumb2__
+	itt	hs
+# endif
+	ldrhs	@t[0],[r12],#16		@ load input
+	ldrhs	@t[1],[r12,#-12]
+	add	@x[6],@x[6],@t[2]
+	add	@x[7],@x[7],@t[3]
+# ifdef	__thumb2__
+	itt	hs
+# endif
+	ldrhs	@t[2],[r12,#-8]
+	ldrhs	@t[3],[r12,#-4]
+# if __ARM_ARCH__>=6 && defined(__ARMEB__)
+	rev	@x[4],@x[4]
+	rev	@x[5],@x[5]
+	rev	@x[6],@x[6]
+	rev	@x[7],@x[7]
+# endif
+# ifdef	__thumb2__
+	itt	hs
+# endif
+	eorhs	@x[4],@x[4],@t[0]
+	eorhs	@x[5],@x[5],@t[1]
+	 add	@t[0],sp,#4*(8)
+	str	@x[4],[r14],#16		@ store output
+# ifdef	__thumb2__
+	itt	hs
+# endif
+	eorhs	@x[6],@x[6],@t[2]
+	eorhs	@x[7],@x[7],@t[3]
+	str	@x[5],[r14,#-12]
+	 ldmia	@t[0],{@t[0]-@t[3]}	@ load key material
+	str	@x[6],[r14,#-8]
+	 add	@x[0],sp,#4*(16+8)
+	str	@x[7],[r14,#-4]
+
+	ldmia	@x[0],{@x[0]-@x[7]}	@ load second half
+
+	add	@x[0],@x[0],@t[0]	@ accumulate key material
+	add	@x[1],@x[1],@t[1]
+# ifdef	__thumb2__
+	itt	hs
+# endif
+	ldrhs	@t[0],[r12],#16		@ load input
+	ldrhs	@t[1],[r12,#-12]
+# ifdef	__thumb2__
+	itt	hi
+# endif
+	 strhi	@t[2],[sp,#4*(16+10)]	@ copy "@x[10]" while at it
+	 strhi	@t[3],[sp,#4*(16+11)]	@ copy "@x[11]" while at it
+	add	@x[2],@x[2],@t[2]
+	add	@x[3],@x[3],@t[3]
+# ifdef	__thumb2__
+	itt	hs
+# endif
+	ldrhs	@t[2],[r12,#-8]
+	ldrhs	@t[3],[r12,#-4]
+# if __ARM_ARCH__>=6 && defined(__ARMEB__)
+	rev	@x[0],@x[0]
+	rev	@x[1],@x[1]
+	rev	@x[2],@x[2]
+	rev	@x[3],@x[3]
+# endif
+# ifdef	__thumb2__
+	itt	hs
+# endif
+	eorhs	@x[0],@x[0],@t[0]
+	eorhs	@x[1],@x[1],@t[1]
+	 add	@t[0],sp,#4*(12)
+	str	@x[0],[r14],#16		@ store output
+# ifdef	__thumb2__
+	itt	hs
+# endif
+	eorhs	@x[2],@x[2],@t[2]
+	eorhs	@x[3],@x[3],@t[3]
+	str	@x[1],[r14,#-12]
+	 ldmia	@t[0],{@t[0]-@t[3]}	@ load key material
+	str	@x[2],[r14,#-8]
+	str	@x[3],[r14,#-4]
+
+	add	@x[4],@x[4],@t[0]	@ accumulate key material
+	add	@x[5],@x[5],@t[1]
+# ifdef	__thumb2__
+	itt	hi
+# endif
+	 addhi	@t[0],@t[0],#1		@ next counter value
+	 strhi	@t[0],[sp,#4*(12)]	@ save next counter value
+# ifdef	__thumb2__
+	itt	hs
+# endif
+	ldrhs	@t[0],[r12],#16		@ load input
+	ldrhs	@t[1],[r12,#-12]
+	add	@x[6],@x[6],@t[2]
+	add	@x[7],@x[7],@t[3]
+# ifdef	__thumb2__
+	itt	hs
+# endif
+	ldrhs	@t[2],[r12,#-8]
+	ldrhs	@t[3],[r12,#-4]
+# if __ARM_ARCH__>=6 && defined(__ARMEB__)
+	rev	@x[4],@x[4]
+	rev	@x[5],@x[5]
+	rev	@x[6],@x[6]
+	rev	@x[7],@x[7]
+# endif
+# ifdef	__thumb2__
+	itt	hs
+# endif
+	eorhs	@x[4],@x[4],@t[0]
+	eorhs	@x[5],@x[5],@t[1]
+# ifdef	__thumb2__
+	 it	ne
+# endif
+	 ldrne	@t[0],[sp,#4*(32+2)]	@ re-load len
+# ifdef	__thumb2__
+	itt	hs
+# endif
+	eorhs	@x[6],@x[6],@t[2]
+	eorhs	@x[7],@x[7],@t[3]
+	str	@x[4],[r14],#16		@ store output
+	str	@x[5],[r14,#-12]
+# ifdef	__thumb2__
+	it	hs
+# endif
+	 subhs	@t[3],@t[0],#64		@ len-=64
+	str	@x[6],[r14,#-8]
+	str	@x[7],[r14,#-4]
+	bhi	.Loop_outer
+
+	beq	.Ldone
+# if __ARM_ARCH__<7
+	b	.Ltail
+
+.align	4
+.Lunaligned:				@ unaligned endian-neutral path
+	cmp	@t[3],#64		@ restore flags
+# endif
+#endif
+#if __ARM_ARCH__<7
+	ldr	@t[3],[sp,#4*(3)]
+___
+for ($i=0;$i<16;$i+=4) {
+my $j=$i&0x7;
+
+$code.=<<___	if ($i==4);
+	add	@x[0],sp,#4*(16+8)
+___
+$code.=<<___	if ($i==8);
+	ldmia	@x[0],{@x[0]-@x[7]}		@ load second half
+# ifdef	__thumb2__
+	itt	hi
+# endif
+	strhi	@t[2],[sp,#4*(16+10)]		@ copy "@x[10]"
+	strhi	@t[3],[sp,#4*(16+11)]		@ copy "@x[11]"
+___
+$code.=<<___;
+	add	@x[$j+0],@x[$j+0],@t[0]		@ accumulate key material
+___
+$code.=<<___	if ($i==12);
+# ifdef	__thumb2__
+	itt	hi
+# endif
+	addhi	@t[0],@t[0],#1			@ next counter value
+	strhi	@t[0],[sp,#4*(12)]		@ save next counter value
+___
+$code.=<<___;
+	add	@x[$j+1],@x[$j+1],@t[1]
+	add	@x[$j+2],@x[$j+2],@t[2]
+# ifdef	__thumb2__
+	itete	lo
+# endif
+	eorlo	@t[0],@t[0],@t[0]		@ zero or ...
+	ldrhsb	@t[0],[r12],#16			@ ... load input
+	eorlo	@t[1],@t[1],@t[1]
+	ldrhsb	@t[1],[r12,#-12]
+
+	add	@x[$j+3],@x[$j+3],@t[3]
+# ifdef	__thumb2__
+	itete	lo
+# endif
+	eorlo	@t[2],@t[2],@t[2]
+	ldrhsb	@t[2],[r12,#-8]
+	eorlo	@t[3],@t[3],@t[3]
+	ldrhsb	@t[3],[r12,#-4]
+
+	eor	@x[$j+0],@t[0],@x[$j+0]		@ xor with input (or zero)
+	eor	@x[$j+1],@t[1],@x[$j+1]
+# ifdef	__thumb2__
+	itt	hs
+# endif
+	ldrhsb	@t[0],[r12,#-15]		@ load more input
+	ldrhsb	@t[1],[r12,#-11]
+	eor	@x[$j+2],@t[2],@x[$j+2]
+	 strb	@x[$j+0],[r14],#16		@ store output
+	eor	@x[$j+3],@t[3],@x[$j+3]
+# ifdef	__thumb2__
+	itt	hs
+# endif
+	ldrhsb	@t[2],[r12,#-7]
+	ldrhsb	@t[3],[r12,#-3]
+	 strb	@x[$j+1],[r14,#-12]
+	eor	@x[$j+0],@t[0],@x[$j+0],lsr#8
+	 strb	@x[$j+2],[r14,#-8]
+	eor	@x[$j+1],@t[1],@x[$j+1],lsr#8
+# ifdef	__thumb2__
+	itt	hs
+# endif
+	ldrhsb	@t[0],[r12,#-14]		@ load more input
+	ldrhsb	@t[1],[r12,#-10]
+	 strb	@x[$j+3],[r14,#-4]
+	eor	@x[$j+2],@t[2],@x[$j+2],lsr#8
+	 strb	@x[$j+0],[r14,#-15]
+	eor	@x[$j+3],@t[3],@x[$j+3],lsr#8
+# ifdef	__thumb2__
+	itt	hs
+# endif
+	ldrhsb	@t[2],[r12,#-6]
+	ldrhsb	@t[3],[r12,#-2]
+	 strb	@x[$j+1],[r14,#-11]
+	eor	@x[$j+0],@t[0],@x[$j+0],lsr#8
+	 strb	@x[$j+2],[r14,#-7]
+	eor	@x[$j+1],@t[1],@x[$j+1],lsr#8
+# ifdef	__thumb2__
+	itt	hs
+# endif
+	ldrhsb	@t[0],[r12,#-13]		@ load more input
+	ldrhsb	@t[1],[r12,#-9]
+	 strb	@x[$j+3],[r14,#-3]
+	eor	@x[$j+2],@t[2],@x[$j+2],lsr#8
+	 strb	@x[$j+0],[r14,#-14]
+	eor	@x[$j+3],@t[3],@x[$j+3],lsr#8
+# ifdef	__thumb2__
+	itt	hs
+# endif
+	ldrhsb	@t[2],[r12,#-5]
+	ldrhsb	@t[3],[r12,#-1]
+	 strb	@x[$j+1],[r14,#-10]
+	 strb	@x[$j+2],[r14,#-6]
+	eor	@x[$j+0],@t[0],@x[$j+0],lsr#8
+	 strb	@x[$j+3],[r14,#-2]
+	eor	@x[$j+1],@t[1],@x[$j+1],lsr#8
+	 strb	@x[$j+0],[r14,#-13]
+	eor	@x[$j+2],@t[2],@x[$j+2],lsr#8
+	 strb	@x[$j+1],[r14,#-9]
+	eor	@x[$j+3],@t[3],@x[$j+3],lsr#8
+	 strb	@x[$j+2],[r14,#-5]
+	 strb	@x[$j+3],[r14,#-1]
+___
+$code.=<<___	if ($i<12);
+	add	@t[0],sp,#4*(4+$i)
+	ldmia	@t[0],{@t[0]-@t[3]}		@ load key material
+___
+}
+$code.=<<___;
+# ifdef	__thumb2__
+	it	ne
+# endif
+	ldrne	@t[0],[sp,#4*(32+2)]		@ re-load len
+# ifdef	__thumb2__
+	it	hs
+# endif
+	subhs	@t[3],@t[0],#64			@ len-=64
+	bhi	.Loop_outer
+
+	beq	.Ldone
+#endif
+
+.Ltail:
+	ldr	r12,[sp,#4*(32+1)]	@ load inp
+	add	@t[1],sp,#4*(0)
+	ldr	r14,[sp,#4*(32+0)]	@ load out
+
+.Loop_tail:
+	ldrb	@t[2],[@t[1]],#1	@ read buffer on stack
+	ldrb	@t[3],[r12],#1		@ read input
+	subs	@t[0],@t[0],#1
+	eor	@t[3],@t[3],@t[2]
+	strb	@t[3],[r14],#1		@ store output
+	bne	.Loop_tail
+
+.Ldone:
+	add	sp,sp,#4*(32+3)
+.Lno_data:
+	ldmia	sp!,{r4-r11,pc}
+.size	ChaCha20_ctr32,.-ChaCha20_ctr32
+___
+
+{{{
+my ($a0,$b0,$c0,$d0,$a1,$b1,$c1,$d1,$a2,$b2,$c2,$d2,$t0,$t1,$t2,$t3) =
+    map("q$_",(0..15));
+
+sub NEONROUND {
+my $odd = pop;
+my ($a,$b,$c,$d,$t)=@_;
+
+	(
+	"&vadd_i32	($a,$a,$b)",
+	"&veor		($d,$d,$a)",
+	"&vrev32_16	($d,$d)",	# vrot ($d,16)
+
+	"&vadd_i32	($c,$c,$d)",
+	"&veor		($t,$b,$c)",
+	"&vshr_u32	($b,$t,20)",
+	"&vsli_32	($b,$t,12)",
+
+	"&vadd_i32	($a,$a,$b)",
+	"&veor		($t,$d,$a)",
+	"&vshr_u32	($d,$t,24)",
+	"&vsli_32	($d,$t,8)",
+
+	"&vadd_i32	($c,$c,$d)",
+	"&veor		($t,$b,$c)",
+	"&vshr_u32	($b,$t,25)",
+	"&vsli_32	($b,$t,7)",
+
+	"&vext_8	($c,$c,$c,8)",
+	"&vext_8	($b,$b,$b,$odd?12:4)",
+	"&vext_8	($d,$d,$d,$odd?4:12)"
+	);
+}
+
+$code.=<<___;
+#if __ARM_MAX_ARCH__>=7
+.arch	armv7-a
+.fpu	neon
+
+.type	ChaCha20_neon,%function
+.align	5
+ChaCha20_neon:
+	ldr		r12,[sp,#0]		@ pull pointer to counter and nonce
+	stmdb		sp!,{r0-r2,r4-r11,lr}
+.LChaCha20_neon:
+	adr		r14,.Lsigma
+	vstmdb		sp!,{d8-d15}		@ ABI spec says so
+	stmdb		sp!,{r0-r3}
+
+	vld1.32		{$b0-$c0},[r3]		@ load key
+	ldmia		r3,{r4-r11}		@ load key
+
+	sub		sp,sp,#4*(16+16)
+	vld1.32		{$d0},[r12]		@ load counter and nonce
+	add		r12,sp,#4*8
+	ldmia		r14,{r0-r3}		@ load sigma
+	vld1.32		{$a0},[r14]!		@ load sigma
+	vld1.32		{$t0},[r14]		@ one
+	vst1.32		{$c0-$d0},[r12]		@ copy 1/2key|counter|nonce
+	vst1.32		{$a0-$b0},[sp]		@ copy sigma|1/2key
+
+	str		r10,[sp,#4*(16+10)]	@ off-load "@x[10]"
+	str		r11,[sp,#4*(16+11)]	@ off-load "@x[11]"
+	vshl.i32	$t1#lo,$t0#lo,#1	@ two
+	vstr		$t0#lo,[sp,#4*(16+0)]
+	vshl.i32	$t2#lo,$t0#lo,#2	@ four
+	vstr		$t1#lo,[sp,#4*(16+2)]
+	vmov		$a1,$a0
+	vstr		$t2#lo,[sp,#4*(16+4)]
+	vmov		$a2,$a0
+	vmov		$b1,$b0
+	vmov		$b2,$b0
+	b		.Loop_neon_enter
+
+.align	4
+.Loop_neon_outer:
+	ldmia		sp,{r0-r9}		@ load key material
+	cmp		@t[3],#64*2		@ if len<=64*2
+	bls		.Lbreak_neon		@ switch to integer-only
+	vmov		$a1,$a0
+	str		@t[3],[sp,#4*(32+2)]	@ save len
+	vmov		$a2,$a0
+	str		r12,  [sp,#4*(32+1)]	@ save inp
+	vmov		$b1,$b0
+	str		r14,  [sp,#4*(32+0)]	@ save out
+	vmov		$b2,$b0
+.Loop_neon_enter:
+	ldr		@t[3], [sp,#4*(15)]
+	vadd.i32	$d1,$d0,$t0		@ counter+1
+	ldr		@x[12],[sp,#4*(12)]	@ modulo-scheduled load
+	vmov		$c1,$c0
+	ldr		@t[2], [sp,#4*(13)]
+	vmov		$c2,$c0
+	ldr		@x[14],[sp,#4*(14)]
+	vadd.i32	$d2,$d1,$t0		@ counter+2
+	str		@t[3], [sp,#4*(16+15)]
+	mov		@t[3],#10
+	add		@x[12],@x[12],#3	@ counter+3 
+	b		.Loop_neon
+
+.align	4
+.Loop_neon:
+	subs		@t[3],@t[3],#1
+___
+	my @thread0=&NEONROUND($a0,$b0,$c0,$d0,$t0,0);
+	my @thread1=&NEONROUND($a1,$b1,$c1,$d1,$t1,0);
+	my @thread2=&NEONROUND($a2,$b2,$c2,$d2,$t2,0);
+	my @thread3=&ROUND(0,4,8,12);
+
+	foreach (@thread0) {
+		eval;			eval(shift(@thread3));
+		eval(shift(@thread1));	eval(shift(@thread3));
+		eval(shift(@thread2));	eval(shift(@thread3));
+	}
+
+	@thread0=&NEONROUND($a0,$b0,$c0,$d0,$t0,1);
+	@thread1=&NEONROUND($a1,$b1,$c1,$d1,$t1,1);
+	@thread2=&NEONROUND($a2,$b2,$c2,$d2,$t2,1);
+	@thread3=&ROUND(0,5,10,15);
+
+	foreach (@thread0) {
+		eval;			eval(shift(@thread3));
+		eval(shift(@thread1));	eval(shift(@thread3));
+		eval(shift(@thread2));	eval(shift(@thread3));
+	}
+$code.=<<___;
+	bne		.Loop_neon
+
+	add		@t[3],sp,#32
+	vld1.32		{$t0-$t1},[sp]		@ load key material
+	vld1.32		{$t2-$t3},[@t[3]]
+
+	ldr		@t[3],[sp,#4*(32+2)]	@ load len
+
+	str		@t[0], [sp,#4*(16+8)]	@ modulo-scheduled store
+	str		@t[1], [sp,#4*(16+9)]
+	str		@x[12],[sp,#4*(16+12)]
+	str		@t[2], [sp,#4*(16+13)]
+	str		@x[14],[sp,#4*(16+14)]
+
+	@ at this point we have first half of 512-bit result in
+	@ @x[0-7] and second half at sp+4*(16+8)
+
+	ldr		r12,[sp,#4*(32+1)]	@ load inp
+	ldr		r14,[sp,#4*(32+0)]	@ load out
+
+	vadd.i32	$a0,$a0,$t0		@ accumulate key material
+	vadd.i32	$a1,$a1,$t0
+	vadd.i32	$a2,$a2,$t0
+	vldr		$t0#lo,[sp,#4*(16+0)]	@ one
+
+	vadd.i32	$b0,$b0,$t1
+	vadd.i32	$b1,$b1,$t1
+	vadd.i32	$b2,$b2,$t1
+	vldr		$t1#lo,[sp,#4*(16+2)]	@ two
+
+	vadd.i32	$c0,$c0,$t2
+	vadd.i32	$c1,$c1,$t2
+	vadd.i32	$c2,$c2,$t2
+	vadd.i32	$d1#lo,$d1#lo,$t0#lo	@ counter+1
+	vadd.i32	$d2#lo,$d2#lo,$t1#lo	@ counter+2
+
+	vadd.i32	$d0,$d0,$t3
+	vadd.i32	$d1,$d1,$t3
+	vadd.i32	$d2,$d2,$t3
+
+	cmp		@t[3],#64*4
+	blo		.Ltail_neon
+
+	vld1.8		{$t0-$t1},[r12]!	@ load input
+	 mov		@t[3],sp
+	vld1.8		{$t2-$t3},[r12]!
+	veor		$a0,$a0,$t0		@ xor with input
+	veor		$b0,$b0,$t1
+	vld1.8		{$t0-$t1},[r12]!
+	veor		$c0,$c0,$t2
+	veor		$d0,$d0,$t3
+	vld1.8		{$t2-$t3},[r12]!
+
+	veor		$a1,$a1,$t0
+	 vst1.8		{$a0-$b0},[r14]!	@ store output
+	veor		$b1,$b1,$t1
+	vld1.8		{$t0-$t1},[r12]!
+	veor		$c1,$c1,$t2
+	 vst1.8		{$c0-$d0},[r14]!
+	veor		$d1,$d1,$t3
+	vld1.8		{$t2-$t3},[r12]!
+
+	veor		$a2,$a2,$t0
+	 vld1.32	{$a0-$b0},[@t[3]]!	@ load for next iteration
+	 veor		$t0#hi,$t0#hi,$t0#hi
+	 vldr		$t0#lo,[sp,#4*(16+4)]	@ four
+	veor		$b2,$b2,$t1
+	 vld1.32	{$c0-$d0},[@t[3]]
+	veor		$c2,$c2,$t2
+	 vst1.8		{$a1-$b1},[r14]!
+	veor		$d2,$d2,$t3
+	 vst1.8		{$c1-$d1},[r14]!
+
+	vadd.i32	$d0#lo,$d0#lo,$t0#lo	@ next counter value
+	vldr		$t0#lo,[sp,#4*(16+0)]	@ one
+
+	ldmia		sp,{@t[0]-@t[3]}	@ load key material
+	add		@x[0],@x[0],@t[0]	@ accumulate key material
+	ldr		@t[0],[r12],#16		@ load input
+	 vst1.8		{$a2-$b2},[r14]!
+	add		@x[1],@x[1],@t[1]
+	ldr		@t[1],[r12,#-12]
+	 vst1.8		{$c2-$d2},[r14]!
+	add		@x[2],@x[2],@t[2]
+	ldr		@t[2],[r12,#-8]
+	add		@x[3],@x[3],@t[3]
+	ldr		@t[3],[r12,#-4]
+# ifdef	__ARMEB__
+	rev		@x[0],@x[0]
+	rev		@x[1],@x[1]
+	rev		@x[2],@x[2]
+	rev		@x[3],@x[3]
+# endif
+	eor		@x[0],@x[0],@t[0]	@ xor with input
+	 add		@t[0],sp,#4*(4)
+	eor		@x[1],@x[1],@t[1]
+	str		@x[0],[r14],#16		@ store output
+	eor		@x[2],@x[2],@t[2]
+	str		@x[1],[r14,#-12]
+	eor		@x[3],@x[3],@t[3]
+	 ldmia		@t[0],{@t[0]-@t[3]}	@ load key material
+	str		@x[2],[r14,#-8]
+	str		@x[3],[r14,#-4]
+
+	add		@x[4],@x[4],@t[0]	@ accumulate key material
+	ldr		@t[0],[r12],#16		@ load input
+	add		@x[5],@x[5],@t[1]
+	ldr		@t[1],[r12,#-12]
+	add		@x[6],@x[6],@t[2]
+	ldr		@t[2],[r12,#-8]
+	add		@x[7],@x[7],@t[3]
+	ldr		@t[3],[r12,#-4]
+# ifdef	__ARMEB__
+	rev		@x[4],@x[4]
+	rev		@x[5],@x[5]
+	rev		@x[6],@x[6]
+	rev		@x[7],@x[7]
+# endif
+	eor		@x[4],@x[4],@t[0]
+	 add		@t[0],sp,#4*(8)
+	eor		@x[5],@x[5],@t[1]
+	str		@x[4],[r14],#16		@ store output
+	eor		@x[6],@x[6],@t[2]
+	str		@x[5],[r14,#-12]
+	eor		@x[7],@x[7],@t[3]
+	 ldmia		@t[0],{@t[0]-@t[3]}	@ load key material
+	str		@x[6],[r14,#-8]
+	 add		@x[0],sp,#4*(16+8)
+	str		@x[7],[r14,#-4]
+
+	ldmia		@x[0],{@x[0]-@x[7]}	@ load second half
+
+	add		@x[0],@x[0],@t[0]	@ accumulate key material
+	ldr		@t[0],[r12],#16		@ load input
+	add		@x[1],@x[1],@t[1]
+	ldr		@t[1],[r12,#-12]
+# ifdef	__thumb2__
+	it	hi
+# endif
+	 strhi		@t[2],[sp,#4*(16+10)]	@ copy "@x[10]" while at it
+	add		@x[2],@x[2],@t[2]
+	ldr		@t[2],[r12,#-8]
+# ifdef	__thumb2__
+	it	hi
+# endif
+	 strhi		@t[3],[sp,#4*(16+11)]	@ copy "@x[11]" while at it
+	add		@x[3],@x[3],@t[3]
+	ldr		@t[3],[r12,#-4]
+# ifdef	__ARMEB__
+	rev		@x[0],@x[0]
+	rev		@x[1],@x[1]
+	rev		@x[2],@x[2]
+	rev		@x[3],@x[3]
+# endif
+	eor		@x[0],@x[0],@t[0]
+	 add		@t[0],sp,#4*(12)
+	eor		@x[1],@x[1],@t[1]
+	str		@x[0],[r14],#16		@ store output
+	eor		@x[2],@x[2],@t[2]
+	str		@x[1],[r14,#-12]
+	eor		@x[3],@x[3],@t[3]
+	 ldmia		@t[0],{@t[0]-@t[3]}	@ load key material
+	str		@x[2],[r14,#-8]
+	str		@x[3],[r14,#-4]
+
+	add		@x[4],@x[4],@t[0]	@ accumulate key material
+	 add		@t[0],@t[0],#4		@ next counter value
+	add		@x[5],@x[5],@t[1]
+	 str		@t[0],[sp,#4*(12)]	@ save next counter value
+	ldr		@t[0],[r12],#16		@ load input
+	add		@x[6],@x[6],@t[2]
+	 add		@x[4],@x[4],#3		@ counter+3
+	ldr		@t[1],[r12,#-12]
+	add		@x[7],@x[7],@t[3]
+	ldr		@t[2],[r12,#-8]
+	ldr		@t[3],[r12,#-4]
+# ifdef	__ARMEB__
+	rev		@x[4],@x[4]
+	rev		@x[5],@x[5]
+	rev		@x[6],@x[6]
+	rev		@x[7],@x[7]
+# endif
+	eor		@x[4],@x[4],@t[0]
+# ifdef	__thumb2__
+	it	hi
+# endif
+	 ldrhi		@t[0],[sp,#4*(32+2)]	@ re-load len
+	eor		@x[5],@x[5],@t[1]
+	eor		@x[6],@x[6],@t[2]
+	str		@x[4],[r14],#16		@ store output
+	eor		@x[7],@x[7],@t[3]
+	str		@x[5],[r14,#-12]
+	 sub		@t[3],@t[0],#64*4	@ len-=64*4
+	str		@x[6],[r14,#-8]
+	str		@x[7],[r14,#-4]
+	bhi		.Loop_neon_outer
+
+	b		.Ldone_neon
+
+.align	4
+.Lbreak_neon:
+	@ harmonize NEON and integer-only stack frames: load data
+	@ from NEON frame, but save to integer-only one; distance
+	@ between the two is 4*(32+4+16-32)=4*(20).
+
+	str		@t[3], [sp,#4*(20+32+2)]	@ save len
+	 add		@t[3],sp,#4*(32+4)
+	str		r12,   [sp,#4*(20+32+1)]	@ save inp
+	str		r14,   [sp,#4*(20+32+0)]	@ save out
+
+	ldr		@x[12],[sp,#4*(16+10)]
+	ldr		@x[14],[sp,#4*(16+11)]
+	 vldmia		@t[3],{d8-d15}			@ fulfill ABI requirement
+	str		@x[12],[sp,#4*(20+16+10)]	@ copy "@x[10]"
+	str		@x[14],[sp,#4*(20+16+11)]	@ copy "@x[11]"
+
+	ldr		@t[3], [sp,#4*(15)]
+	ldr		@x[12],[sp,#4*(12)]		@ modulo-scheduled load
+	ldr		@t[2], [sp,#4*(13)]
+	ldr		@x[14],[sp,#4*(14)]
+	str		@t[3], [sp,#4*(20+16+15)]
+	add		@t[3],sp,#4*(20)
+	vst1.32		{$a0-$b0},[@t[3]]!		@ copy key
+	add		sp,sp,#4*(20)			@ switch frame
+	vst1.32		{$c0-$d0},[@t[3]]
+	mov		@t[3],#10
+	b		.Loop				@ go integer-only
+
+.align	4
+.Ltail_neon:
+	cmp		@t[3],#64*3
+	bhs		.L192_or_more_neon
+	cmp		@t[3],#64*2
+	bhs		.L128_or_more_neon
+	cmp		@t[3],#64*1
+	bhs		.L64_or_more_neon
+
+	add		@t[0],sp,#4*(8)
+	vst1.8		{$a0-$b0},[sp]
+	add		@t[2],sp,#4*(0)
+	vst1.8		{$c0-$d0},[@t[0]]
+	b		.Loop_tail_neon
+
+.align	4
+.L64_or_more_neon:
+	vld1.8		{$t0-$t1},[r12]!
+	vld1.8		{$t2-$t3},[r12]!
+	veor		$a0,$a0,$t0
+	veor		$b0,$b0,$t1
+	veor		$c0,$c0,$t2
+	veor		$d0,$d0,$t3
+	vst1.8		{$a0-$b0},[r14]!
+	vst1.8		{$c0-$d0},[r14]!
+
+	beq		.Ldone_neon
+
+	add		@t[0],sp,#4*(8)
+	vst1.8		{$a1-$b1},[sp]
+	add		@t[2],sp,#4*(0)
+	vst1.8		{$c1-$d1},[@t[0]]
+	sub		@t[3],@t[3],#64*1	@ len-=64*1
+	b		.Loop_tail_neon
+
+.align	4
+.L128_or_more_neon:
+	vld1.8		{$t0-$t1},[r12]!
+	vld1.8		{$t2-$t3},[r12]!
+	veor		$a0,$a0,$t0
+	veor		$b0,$b0,$t1
+	vld1.8		{$t0-$t1},[r12]!
+	veor		$c0,$c0,$t2
+	veor		$d0,$d0,$t3
+	vld1.8		{$t2-$t3},[r12]!
+
+	veor		$a1,$a1,$t0
+	veor		$b1,$b1,$t1
+	 vst1.8		{$a0-$b0},[r14]!
+	veor		$c1,$c1,$t2
+	 vst1.8		{$c0-$d0},[r14]!
+	veor		$d1,$d1,$t3
+	vst1.8		{$a1-$b1},[r14]!
+	vst1.8		{$c1-$d1},[r14]!
+
+	beq		.Ldone_neon
+
+	add		@t[0],sp,#4*(8)
+	vst1.8		{$a2-$b2},[sp]
+	add		@t[2],sp,#4*(0)
+	vst1.8		{$c2-$d2},[@t[0]]
+	sub		@t[3],@t[3],#64*2	@ len-=64*2
+	b		.Loop_tail_neon
+
+.align	4
+.L192_or_more_neon:
+	vld1.8		{$t0-$t1},[r12]!
+	vld1.8		{$t2-$t3},[r12]!
+	veor		$a0,$a0,$t0
+	veor		$b0,$b0,$t1
+	vld1.8		{$t0-$t1},[r12]!
+	veor		$c0,$c0,$t2
+	veor		$d0,$d0,$t3
+	vld1.8		{$t2-$t3},[r12]!
+
+	veor		$a1,$a1,$t0
+	veor		$b1,$b1,$t1
+	vld1.8		{$t0-$t1},[r12]!
+	veor		$c1,$c1,$t2
+	 vst1.8		{$a0-$b0},[r14]!
+	veor		$d1,$d1,$t3
+	vld1.8		{$t2-$t3},[r12]!
+
+	veor		$a2,$a2,$t0
+	 vst1.8		{$c0-$d0},[r14]!
+	veor		$b2,$b2,$t1
+	 vst1.8		{$a1-$b1},[r14]!
+	veor		$c2,$c2,$t2
+	 vst1.8		{$c1-$d1},[r14]!
+	veor		$d2,$d2,$t3
+	vst1.8		{$a2-$b2},[r14]!
+	vst1.8		{$c2-$d2},[r14]!
+
+	beq		.Ldone_neon
+
+	ldmia		sp,{@t[0]-@t[3]}	@ load key material
+	add		@x[0],@x[0],@t[0]	@ accumulate key material
+	 add		@t[0],sp,#4*(4)
+	add		@x[1],@x[1],@t[1]
+	add		@x[2],@x[2],@t[2]
+	add		@x[3],@x[3],@t[3]
+	 ldmia		@t[0],{@t[0]-@t[3]}	@ load key material
+
+	add		@x[4],@x[4],@t[0]	@ accumulate key material
+	 add		@t[0],sp,#4*(8)
+	add		@x[5],@x[5],@t[1]
+	add		@x[6],@x[6],@t[2]
+	add		@x[7],@x[7],@t[3]
+	 ldmia		@t[0],{@t[0]-@t[3]}	@ load key material
+# ifdef	__ARMEB__
+	rev		@x[0],@x[0]
+	rev		@x[1],@x[1]
+	rev		@x[2],@x[2]
+	rev		@x[3],@x[3]
+	rev		@x[4],@x[4]
+	rev		@x[5],@x[5]
+	rev		@x[6],@x[6]
+	rev		@x[7],@x[7]
+# endif
+	stmia		sp,{@x[0]-@x[7]}
+	 add		@x[0],sp,#4*(16+8)
+
+	ldmia		@x[0],{@x[0]-@x[7]}	@ load second half
+
+	add		@x[0],@x[0],@t[0]	@ accumulate key material
+	 add		@t[0],sp,#4*(12)
+	add		@x[1],@x[1],@t[1]
+	add		@x[2],@x[2],@t[2]
+	add		@x[3],@x[3],@t[3]
+	 ldmia		@t[0],{@t[0]-@t[3]}	@ load key material
+
+	add		@x[4],@x[4],@t[0]	@ accumulate key material
+	 add		@t[0],sp,#4*(8)
+	add		@x[5],@x[5],@t[1]
+	 add		@x[4],@x[4],#3		@ counter+3
+	add		@x[6],@x[6],@t[2]
+	add		@x[7],@x[7],@t[3]
+	 ldr		@t[3],[sp,#4*(32+2)]	@ re-load len
+# ifdef	__ARMEB__
+	rev		@x[0],@x[0]
+	rev		@x[1],@x[1]
+	rev		@x[2],@x[2]
+	rev		@x[3],@x[3]
+	rev		@x[4],@x[4]
+	rev		@x[5],@x[5]
+	rev		@x[6],@x[6]
+	rev		@x[7],@x[7]
+# endif
+	stmia		@t[0],{@x[0]-@x[7]}
+	 add		@t[2],sp,#4*(0)
+	 sub		@t[3],@t[3],#64*3	@ len-=64*3
+
+.Loop_tail_neon:
+	ldrb		@t[0],[@t[2]],#1	@ read buffer on stack
+	ldrb		@t[1],[r12],#1		@ read input
+	subs		@t[3],@t[3],#1
+	eor		@t[0],@t[0],@t[1]
+	strb		@t[0],[r14],#1		@ store output
+	bne		.Loop_tail_neon
+
+.Ldone_neon:
+	add		sp,sp,#4*(32+4)
+	vldmia		sp,{d8-d15}
+	add		sp,sp,#4*(16+3)
+	ldmia		sp!,{r4-r11,pc}
+.size	ChaCha20_neon,.-ChaCha20_neon
+.comm	OPENSSL_armcap_P,4,4
+#endif
+___
+}}}
+
+foreach (split("\n",$code)) {
+	s/\`([^\`]*)\`/eval $1/geo;
+
+	s/\bq([0-9]+)#(lo|hi)/sprintf "d%d",2*$1+($2 eq "hi")/geo;
+
+	print $_,"\n";
+}
+close STDOUT;
diff --git a/crypto/chacha/asm/chacha-armv8.pl b/crypto/chacha/asm/chacha-armv8.pl
new file mode 100755
index 000000000..f7e107471
--- /dev/null
+++ b/crypto/chacha/asm/chacha-armv8.pl
@@ -0,0 +1,1135 @@
+#! /usr/bin/env perl
+# Copyright 2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+#
+# ====================================================================
+# Written by Andy Polyakov  for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+#
+# June 2015
+# 
+# ChaCha20 for ARMv8.
+#
+# Performance in cycles per byte out of large buffer.
+#
+#			IALU/gcc-4.9    3xNEON+1xIALU	6xNEON+2xIALU
+#
+# Apple A7		5.50/+49%       3.33            1.70
+# Cortex-A53		8.40/+80%       4.72		4.72(*)
+# Cortex-A57		8.06/+43%       4.90            4.43(**)
+# Denver		4.50/+82%       2.63		2.67(*)
+# X-Gene		9.50/+46%       8.82		8.89(*)
+# Mongoose		8.00/+44%	3.64		3.25
+#
+# (*)	it's expected that doubling interleave factor doesn't help
+#	all processors, only those with higher NEON latency and
+#	higher instruction issue rate;
+# (**)	expected improvement was actually higher;
+
+$flavour=shift;
+$output=shift;
+
+$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+( $xlate="${dir}arm-xlate.pl" and -f $xlate ) or
+( $xlate="${dir}../../perlasm/arm-xlate.pl" and -f $xlate) or
+die "can't locate arm-xlate.pl";
+
+open OUT,"| \"$^X\" $xlate $flavour $output";
+*STDOUT=*OUT;
+
+sub AUTOLOAD()		# thunk [simplified] x86-style perlasm
+{ my $opcode = $AUTOLOAD; $opcode =~ s/.*:://; $opcode =~ s/_/\./;
+  my $arg = pop;
+    $arg = "#$arg" if ($arg*1 eq $arg);
+    $code .= "\t$opcode\t".join(',',@_,$arg)."\n";
+}
+
+my ($out,$inp,$len,$key,$ctr) = map("x$_",(0..4));
+
+my @x=map("x$_",(5..17,19..21));
+my @d=map("x$_",(22..28,30));
+
+sub ROUND {
+my ($a0,$b0,$c0,$d0)=@_;
+my ($a1,$b1,$c1,$d1)=map(($_&~3)+(($_+1)&3),($a0,$b0,$c0,$d0));
+my ($a2,$b2,$c2,$d2)=map(($_&~3)+(($_+1)&3),($a1,$b1,$c1,$d1));
+my ($a3,$b3,$c3,$d3)=map(($_&~3)+(($_+1)&3),($a2,$b2,$c2,$d2));
+
+    (
+	"&add_32	(@x[$a0],@x[$a0],@x[$b0])",
+	 "&add_32	(@x[$a1],@x[$a1],@x[$b1])",
+	  "&add_32	(@x[$a2],@x[$a2],@x[$b2])",
+	   "&add_32	(@x[$a3],@x[$a3],@x[$b3])",
+	"&eor_32	(@x[$d0],@x[$d0],@x[$a0])",
+	 "&eor_32	(@x[$d1],@x[$d1],@x[$a1])",
+	  "&eor_32	(@x[$d2],@x[$d2],@x[$a2])",
+	   "&eor_32	(@x[$d3],@x[$d3],@x[$a3])",
+	"&ror_32	(@x[$d0],@x[$d0],16)",
+	 "&ror_32	(@x[$d1],@x[$d1],16)",
+	  "&ror_32	(@x[$d2],@x[$d2],16)",
+	   "&ror_32	(@x[$d3],@x[$d3],16)",
+
+	"&add_32	(@x[$c0],@x[$c0],@x[$d0])",
+	 "&add_32	(@x[$c1],@x[$c1],@x[$d1])",
+	  "&add_32	(@x[$c2],@x[$c2],@x[$d2])",
+	   "&add_32	(@x[$c3],@x[$c3],@x[$d3])",
+	"&eor_32	(@x[$b0],@x[$b0],@x[$c0])",
+	 "&eor_32	(@x[$b1],@x[$b1],@x[$c1])",
+	  "&eor_32	(@x[$b2],@x[$b2],@x[$c2])",
+	   "&eor_32	(@x[$b3],@x[$b3],@x[$c3])",
+	"&ror_32	(@x[$b0],@x[$b0],20)",
+	 "&ror_32	(@x[$b1],@x[$b1],20)",
+	  "&ror_32	(@x[$b2],@x[$b2],20)",
+	   "&ror_32	(@x[$b3],@x[$b3],20)",
+
+	"&add_32	(@x[$a0],@x[$a0],@x[$b0])",
+	 "&add_32	(@x[$a1],@x[$a1],@x[$b1])",
+	  "&add_32	(@x[$a2],@x[$a2],@x[$b2])",
+	   "&add_32	(@x[$a3],@x[$a3],@x[$b3])",
+	"&eor_32	(@x[$d0],@x[$d0],@x[$a0])",
+	 "&eor_32	(@x[$d1],@x[$d1],@x[$a1])",
+	  "&eor_32	(@x[$d2],@x[$d2],@x[$a2])",
+	   "&eor_32	(@x[$d3],@x[$d3],@x[$a3])",
+	"&ror_32	(@x[$d0],@x[$d0],24)",
+	 "&ror_32	(@x[$d1],@x[$d1],24)",
+	  "&ror_32	(@x[$d2],@x[$d2],24)",
+	   "&ror_32	(@x[$d3],@x[$d3],24)",
+
+	"&add_32	(@x[$c0],@x[$c0],@x[$d0])",
+	 "&add_32	(@x[$c1],@x[$c1],@x[$d1])",
+	  "&add_32	(@x[$c2],@x[$c2],@x[$d2])",
+	   "&add_32	(@x[$c3],@x[$c3],@x[$d3])",
+	"&eor_32	(@x[$b0],@x[$b0],@x[$c0])",
+	 "&eor_32	(@x[$b1],@x[$b1],@x[$c1])",
+	  "&eor_32	(@x[$b2],@x[$b2],@x[$c2])",
+	   "&eor_32	(@x[$b3],@x[$b3],@x[$c3])",
+	"&ror_32	(@x[$b0],@x[$b0],25)",
+	 "&ror_32	(@x[$b1],@x[$b1],25)",
+	  "&ror_32	(@x[$b2],@x[$b2],25)",
+	   "&ror_32	(@x[$b3],@x[$b3],25)"
+    );
+}
+
+$code.=<<___;
+#include "arm_arch.h"
+
+.text
+
+.extern	OPENSSL_armcap_P
+
+.align	5
+.Lsigma:
+.quad	0x3320646e61707865,0x6b20657479622d32		// endian-neutral
+.Lone:
+.long	1,0,0,0
+.LOPENSSL_armcap_P:
+#ifdef	__ILP32__
+.long	OPENSSL_armcap_P-.
+#else
+.quad	OPENSSL_armcap_P-.
+#endif
+.asciz	"ChaCha20 for ARMv8, CRYPTOGAMS by "
+
+.globl	ChaCha20_ctr32
+.type	ChaCha20_ctr32,%function
+.align	5
+ChaCha20_ctr32:
+	cbz	$len,.Labort
+	adr	@x[0],.LOPENSSL_armcap_P
+	cmp	$len,#192
+	b.lo	.Lshort
+#ifdef	__ILP32__
+	ldrsw	@x[1],[@x[0]]
+#else
+	ldr	@x[1],[@x[0]]
+#endif
+	ldr	w17,[@x[1],@x[0]]
+	tst	w17,#ARMV7_NEON
+	b.ne	ChaCha20_neon
+
+.Lshort:
+	stp	x29,x30,[sp,#-96]!
+	add	x29,sp,#0
+
+	adr	@x[0],.Lsigma
+	stp	x19,x20,[sp,#16]
+	stp	x21,x22,[sp,#32]
+	stp	x23,x24,[sp,#48]
+	stp	x25,x26,[sp,#64]
+	stp	x27,x28,[sp,#80]
+	sub	sp,sp,#64
+
+	ldp	@d[0],@d[1],[@x[0]]		// load sigma
+	ldp	@d[2],@d[3],[$key]		// load key
+	ldp	@d[4],@d[5],[$key,#16]
+	ldp	@d[6],@d[7],[$ctr]		// load counter
+#ifdef	__ARMEB__
+	ror	@d[2],@d[2],#32
+	ror	@d[3],@d[3],#32
+	ror	@d[4],@d[4],#32
+	ror	@d[5],@d[5],#32
+	ror	@d[6],@d[6],#32
+	ror	@d[7],@d[7],#32
+#endif
+
+.Loop_outer:
+	mov.32	@x[0],@d[0]			// unpack key block
+	lsr	@x[1],@d[0],#32
+	mov.32	@x[2],@d[1]
+	lsr	@x[3],@d[1],#32
+	mov.32	@x[4],@d[2]
+	lsr	@x[5],@d[2],#32
+	mov.32	@x[6],@d[3]
+	lsr	@x[7],@d[3],#32
+	mov.32	@x[8],@d[4]
+	lsr	@x[9],@d[4],#32
+	mov.32	@x[10],@d[5]
+	lsr	@x[11],@d[5],#32
+	mov.32	@x[12],@d[6]
+	lsr	@x[13],@d[6],#32
+	mov.32	@x[14],@d[7]
+	lsr	@x[15],@d[7],#32
+
+	mov	$ctr,#10
+	subs	$len,$len,#64
+.Loop:
+	sub	$ctr,$ctr,#1	
+___
+	foreach (&ROUND(0, 4, 8,12)) { eval; }
+	foreach (&ROUND(0, 5,10,15)) { eval; }
+$code.=<<___;
+	cbnz	$ctr,.Loop
+
+	add.32	@x[0],@x[0],@d[0]		// accumulate key block
+	add	@x[1],@x[1],@d[0],lsr#32
+	add.32	@x[2],@x[2],@d[1]
+	add	@x[3],@x[3],@d[1],lsr#32
+	add.32	@x[4],@x[4],@d[2]
+	add	@x[5],@x[5],@d[2],lsr#32
+	add.32	@x[6],@x[6],@d[3]
+	add	@x[7],@x[7],@d[3],lsr#32
+	add.32	@x[8],@x[8],@d[4]
+	add	@x[9],@x[9],@d[4],lsr#32
+	add.32	@x[10],@x[10],@d[5]
+	add	@x[11],@x[11],@d[5],lsr#32
+	add.32	@x[12],@x[12],@d[6]
+	add	@x[13],@x[13],@d[6],lsr#32
+	add.32	@x[14],@x[14],@d[7]
+	add	@x[15],@x[15],@d[7],lsr#32
+
+	b.lo	.Ltail
+
+	add	@x[0],@x[0],@x[1],lsl#32	// pack
+	add	@x[2],@x[2],@x[3],lsl#32
+	ldp	@x[1],@x[3],[$inp,#0]		// load input
+	add	@x[4],@x[4],@x[5],lsl#32
+	add	@x[6],@x[6],@x[7],lsl#32
+	ldp	@x[5],@x[7],[$inp,#16]
+	add	@x[8],@x[8],@x[9],lsl#32
+	add	@x[10],@x[10],@x[11],lsl#32
+	ldp	@x[9],@x[11],[$inp,#32]
+	add	@x[12],@x[12],@x[13],lsl#32
+	add	@x[14],@x[14],@x[15],lsl#32
+	ldp	@x[13],@x[15],[$inp,#48]
+	add	$inp,$inp,#64
+#ifdef	__ARMEB__
+	rev	@x[0],@x[0]
+	rev	@x[2],@x[2]
+	rev	@x[4],@x[4]
+	rev	@x[6],@x[6]
+	rev	@x[8],@x[8]
+	rev	@x[10],@x[10]
+	rev	@x[12],@x[12]
+	rev	@x[14],@x[14]
+#endif
+	eor	@x[0],@x[0],@x[1]
+	eor	@x[2],@x[2],@x[3]
+	eor	@x[4],@x[4],@x[5]
+	eor	@x[6],@x[6],@x[7]
+	eor	@x[8],@x[8],@x[9]
+	eor	@x[10],@x[10],@x[11]
+	eor	@x[12],@x[12],@x[13]
+	eor	@x[14],@x[14],@x[15]
+
+	stp	@x[0],@x[2],[$out,#0]		// store output
+	 add	@d[6],@d[6],#1			// increment counter
+	stp	@x[4],@x[6],[$out,#16]
+	stp	@x[8],@x[10],[$out,#32]
+	stp	@x[12],@x[14],[$out,#48]
+	add	$out,$out,#64
+
+	b.hi	.Loop_outer
+
+	ldp	x19,x20,[x29,#16]
+	add	sp,sp,#64
+	ldp	x21,x22,[x29,#32]
+	ldp	x23,x24,[x29,#48]
+	ldp	x25,x26,[x29,#64]
+	ldp	x27,x28,[x29,#80]
+	ldp	x29,x30,[sp],#96
+.Labort:
+	ret
+
+.align	4
+.Ltail:
+	add	$len,$len,#64
+.Less_than_64:
+	sub	$out,$out,#1
+	add	$inp,$inp,$len
+	add	$out,$out,$len
+	add	$ctr,sp,$len
+	neg	$len,$len
+
+	add	@x[0],@x[0],@x[1],lsl#32	// pack
+	add	@x[2],@x[2],@x[3],lsl#32
+	add	@x[4],@x[4],@x[5],lsl#32
+	add	@x[6],@x[6],@x[7],lsl#32
+	add	@x[8],@x[8],@x[9],lsl#32
+	add	@x[10],@x[10],@x[11],lsl#32
+	add	@x[12],@x[12],@x[13],lsl#32
+	add	@x[14],@x[14],@x[15],lsl#32
+#ifdef	__ARMEB__
+	rev	@x[0],@x[0]
+	rev	@x[2],@x[2]
+	rev	@x[4],@x[4]
+	rev	@x[6],@x[6]
+	rev	@x[8],@x[8]
+	rev	@x[10],@x[10]
+	rev	@x[12],@x[12]
+	rev	@x[14],@x[14]
+#endif
+	stp	@x[0],@x[2],[sp,#0]
+	stp	@x[4],@x[6],[sp,#16]
+	stp	@x[8],@x[10],[sp,#32]
+	stp	@x[12],@x[14],[sp,#48]
+
+.Loop_tail:
+	ldrb	w10,[$inp,$len]
+	ldrb	w11,[$ctr,$len]
+	add	$len,$len,#1
+	eor	w10,w10,w11
+	strb	w10,[$out,$len]
+	cbnz	$len,.Loop_tail
+
+	stp	xzr,xzr,[sp,#0]
+	stp	xzr,xzr,[sp,#16]
+	stp	xzr,xzr,[sp,#32]
+	stp	xzr,xzr,[sp,#48]
+
+	ldp	x19,x20,[x29,#16]
+	add	sp,sp,#64
+	ldp	x21,x22,[x29,#32]
+	ldp	x23,x24,[x29,#48]
+	ldp	x25,x26,[x29,#64]
+	ldp	x27,x28,[x29,#80]
+	ldp	x29,x30,[sp],#96
+	ret
+.size	ChaCha20_ctr32,.-ChaCha20_ctr32
+___
+
+{{{
+my ($A0,$B0,$C0,$D0,$A1,$B1,$C1,$D1,$A2,$B2,$C2,$D2,$T0,$T1,$T2,$T3) =
+    map("v$_.4s",(0..7,16..23));
+my (@K)=map("v$_.4s",(24..30));
+my $ONE="v31.4s";
+
+sub NEONROUND {
+my $odd = pop;
+my ($a,$b,$c,$d,$t)=@_;
+
+	(
+	"&add		('$a','$a','$b')",
+	"&eor		('$d','$d','$a')",
+	"&rev32_16	('$d','$d')",		# vrot ($d,16)
+
+	"&add		('$c','$c','$d')",
+	"&eor		('$t','$b','$c')",
+	"&ushr		('$b','$t',20)",
+	"&sli		('$b','$t',12)",
+
+	"&add		('$a','$a','$b')",
+	"&eor		('$t','$d','$a')",
+	"&ushr		('$d','$t',24)",
+	"&sli		('$d','$t',8)",
+
+	"&add		('$c','$c','$d')",
+	"&eor		('$t','$b','$c')",
+	"&ushr		('$b','$t',25)",
+	"&sli		('$b','$t',7)",
+
+	"&ext		('$c','$c','$c',8)",
+	"&ext		('$d','$d','$d',$odd?4:12)",
+	"&ext		('$b','$b','$b',$odd?12:4)"
+	);
+}
+
+$code.=<<___;
+
+.type	ChaCha20_neon,%function
+.align	5
+ChaCha20_neon:
+	stp	x29,x30,[sp,#-96]!
+	add	x29,sp,#0
+
+	adr	@x[0],.Lsigma
+	stp	x19,x20,[sp,#16]
+	stp	x21,x22,[sp,#32]
+	stp	x23,x24,[sp,#48]
+	stp	x25,x26,[sp,#64]
+	stp	x27,x28,[sp,#80]
+	cmp	$len,#512
+	b.hs	.L512_or_more_neon
+
+	sub	sp,sp,#64
+
+	ldp	@d[0],@d[1],[@x[0]]		// load sigma
+	ld1	{@K[0]},[@x[0]],#16
+	ldp	@d[2],@d[3],[$key]		// load key
+	ldp	@d[4],@d[5],[$key,#16]
+	ld1	{@K[1],@K[2]},[$key]
+	ldp	@d[6],@d[7],[$ctr]		// load counter
+	ld1	{@K[3]},[$ctr]
+	ld1	{$ONE},[@x[0]]
+#ifdef	__ARMEB__
+	rev64	@K[0],@K[0]
+	ror	@d[2],@d[2],#32
+	ror	@d[3],@d[3],#32
+	ror	@d[4],@d[4],#32
+	ror	@d[5],@d[5],#32
+	ror	@d[6],@d[6],#32
+	ror	@d[7],@d[7],#32
+#endif
+	add	@K[3],@K[3],$ONE		// += 1
+	add	@K[4],@K[3],$ONE
+	add	@K[5],@K[4],$ONE
+	shl	$ONE,$ONE,#2			// 1 -> 4
+
+.Loop_outer_neon:
+	mov.32	@x[0],@d[0]			// unpack key block
+	lsr	@x[1],@d[0],#32
+	 mov	$A0,@K[0]
+	mov.32	@x[2],@d[1]
+	lsr	@x[3],@d[1],#32
+	 mov	$A1,@K[0]
+	mov.32	@x[4],@d[2]
+	lsr	@x[5],@d[2],#32
+	 mov	$A2,@K[0]
+	mov.32	@x[6],@d[3]
+	 mov	$B0,@K[1]
+	lsr	@x[7],@d[3],#32
+	 mov	$B1,@K[1]
+	mov.32	@x[8],@d[4]
+	 mov	$B2,@K[1]
+	lsr	@x[9],@d[4],#32
+	 mov	$D0,@K[3]
+	mov.32	@x[10],@d[5]
+	 mov	$D1,@K[4]
+	lsr	@x[11],@d[5],#32
+	 mov	$D2,@K[5]
+	mov.32	@x[12],@d[6]
+	 mov	$C0,@K[2]
+	lsr	@x[13],@d[6],#32
+	 mov	$C1,@K[2]
+	mov.32	@x[14],@d[7]
+	 mov	$C2,@K[2]
+	lsr	@x[15],@d[7],#32
+
+	mov	$ctr,#10
+	subs	$len,$len,#256
+.Loop_neon:
+	sub	$ctr,$ctr,#1
+___
+	my @thread0=&NEONROUND($A0,$B0,$C0,$D0,$T0,0);
+	my @thread1=&NEONROUND($A1,$B1,$C1,$D1,$T1,0);
+	my @thread2=&NEONROUND($A2,$B2,$C2,$D2,$T2,0);
+	my @thread3=&ROUND(0,4,8,12);
+
+	foreach (@thread0) {
+		eval;			eval(shift(@thread3));
+		eval(shift(@thread1));	eval(shift(@thread3));
+		eval(shift(@thread2));	eval(shift(@thread3));
+	}
+
+	@thread0=&NEONROUND($A0,$B0,$C0,$D0,$T0,1);
+	@thread1=&NEONROUND($A1,$B1,$C1,$D1,$T1,1);
+	@thread2=&NEONROUND($A2,$B2,$C2,$D2,$T2,1);
+	@thread3=&ROUND(0,5,10,15);
+
+	foreach (@thread0) {
+		eval;			eval(shift(@thread3));
+		eval(shift(@thread1));	eval(shift(@thread3));
+		eval(shift(@thread2));	eval(shift(@thread3));
+	}
+$code.=<<___;
+	cbnz	$ctr,.Loop_neon
+
+	add.32	@x[0],@x[0],@d[0]		// accumulate key block
+	 add	$A0,$A0,@K[0]
+	add	@x[1],@x[1],@d[0],lsr#32
+	 add	$A1,$A1,@K[0]
+	add.32	@x[2],@x[2],@d[1]
+	 add	$A2,$A2,@K[0]
+	add	@x[3],@x[3],@d[1],lsr#32
+	 add	$C0,$C0,@K[2]
+	add.32	@x[4],@x[4],@d[2]
+	 add	$C1,$C1,@K[2]
+	add	@x[5],@x[5],@d[2],lsr#32
+	 add	$C2,$C2,@K[2]
+	add.32	@x[6],@x[6],@d[3]
+	 add	$D0,$D0,@K[3]
+	add	@x[7],@x[7],@d[3],lsr#32
+	add.32	@x[8],@x[8],@d[4]
+	 add	$D1,$D1,@K[4]
+	add	@x[9],@x[9],@d[4],lsr#32
+	add.32	@x[10],@x[10],@d[5]
+	 add	$D2,$D2,@K[5]
+	add	@x[11],@x[11],@d[5],lsr#32
+	add.32	@x[12],@x[12],@d[6]
+	 add	$B0,$B0,@K[1]
+	add	@x[13],@x[13],@d[6],lsr#32
+	add.32	@x[14],@x[14],@d[7]
+	 add	$B1,$B1,@K[1]
+	add	@x[15],@x[15],@d[7],lsr#32
+	 add	$B2,$B2,@K[1]
+
+	b.lo	.Ltail_neon
+
+	add	@x[0],@x[0],@x[1],lsl#32	// pack
+	add	@x[2],@x[2],@x[3],lsl#32
+	ldp	@x[1],@x[3],[$inp,#0]		// load input
+	add	@x[4],@x[4],@x[5],lsl#32
+	add	@x[6],@x[6],@x[7],lsl#32
+	ldp	@x[5],@x[7],[$inp,#16]
+	add	@x[8],@x[8],@x[9],lsl#32
+	add	@x[10],@x[10],@x[11],lsl#32
+	ldp	@x[9],@x[11],[$inp,#32]
+	add	@x[12],@x[12],@x[13],lsl#32
+	add	@x[14],@x[14],@x[15],lsl#32
+	ldp	@x[13],@x[15],[$inp,#48]
+	add	$inp,$inp,#64
+#ifdef	__ARMEB__
+	rev	@x[0],@x[0]
+	rev	@x[2],@x[2]
+	rev	@x[4],@x[4]
+	rev	@x[6],@x[6]
+	rev	@x[8],@x[8]
+	rev	@x[10],@x[10]
+	rev	@x[12],@x[12]
+	rev	@x[14],@x[14]
+#endif
+	ld1.8	{$T0-$T3},[$inp],#64
+	eor	@x[0],@x[0],@x[1]
+	eor	@x[2],@x[2],@x[3]
+	eor	@x[4],@x[4],@x[5]
+	eor	@x[6],@x[6],@x[7]
+	eor	@x[8],@x[8],@x[9]
+	 eor	$A0,$A0,$T0
+	eor	@x[10],@x[10],@x[11]
+	 eor	$B0,$B0,$T1
+	eor	@x[12],@x[12],@x[13]
+	 eor	$C0,$C0,$T2
+	eor	@x[14],@x[14],@x[15]
+	 eor	$D0,$D0,$T3
+	 ld1.8	{$T0-$T3},[$inp],#64
+
+	stp	@x[0],@x[2],[$out,#0]		// store output
+	 add	@d[6],@d[6],#4			// increment counter
+	stp	@x[4],@x[6],[$out,#16]
+	 add	@K[3],@K[3],$ONE		// += 4
+	stp	@x[8],@x[10],[$out,#32]
+	 add	@K[4],@K[4],$ONE
+	stp	@x[12],@x[14],[$out,#48]
+	 add	@K[5],@K[5],$ONE
+	add	$out,$out,#64
+
+	st1.8	{$A0-$D0},[$out],#64
+	ld1.8	{$A0-$D0},[$inp],#64
+
+	eor	$A1,$A1,$T0
+	eor	$B1,$B1,$T1
+	eor	$C1,$C1,$T2
+	eor	$D1,$D1,$T3
+	st1.8	{$A1-$D1},[$out],#64
+
+	eor	$A2,$A2,$A0
+	eor	$B2,$B2,$B0
+	eor	$C2,$C2,$C0
+	eor	$D2,$D2,$D0
+	st1.8	{$A2-$D2},[$out],#64
+
+	b.hi	.Loop_outer_neon
+
+	ldp	x19,x20,[x29,#16]
+	add	sp,sp,#64
+	ldp	x21,x22,[x29,#32]
+	ldp	x23,x24,[x29,#48]
+	ldp	x25,x26,[x29,#64]
+	ldp	x27,x28,[x29,#80]
+	ldp	x29,x30,[sp],#96
+	ret
+
+.Ltail_neon:
+	add	$len,$len,#256
+	cmp	$len,#64
+	b.lo	.Less_than_64
+
+	add	@x[0],@x[0],@x[1],lsl#32	// pack
+	add	@x[2],@x[2],@x[3],lsl#32
+	ldp	@x[1],@x[3],[$inp,#0]		// load input
+	add	@x[4],@x[4],@x[5],lsl#32
+	add	@x[6],@x[6],@x[7],lsl#32
+	ldp	@x[5],@x[7],[$inp,#16]
+	add	@x[8],@x[8],@x[9],lsl#32
+	add	@x[10],@x[10],@x[11],lsl#32
+	ldp	@x[9],@x[11],[$inp,#32]
+	add	@x[12],@x[12],@x[13],lsl#32
+	add	@x[14],@x[14],@x[15],lsl#32
+	ldp	@x[13],@x[15],[$inp,#48]
+	add	$inp,$inp,#64
+#ifdef	__ARMEB__
+	rev	@x[0],@x[0]
+	rev	@x[2],@x[2]
+	rev	@x[4],@x[4]
+	rev	@x[6],@x[6]
+	rev	@x[8],@x[8]
+	rev	@x[10],@x[10]
+	rev	@x[12],@x[12]
+	rev	@x[14],@x[14]
+#endif
+	eor	@x[0],@x[0],@x[1]
+	eor	@x[2],@x[2],@x[3]
+	eor	@x[4],@x[4],@x[5]
+	eor	@x[6],@x[6],@x[7]
+	eor	@x[8],@x[8],@x[9]
+	eor	@x[10],@x[10],@x[11]
+	eor	@x[12],@x[12],@x[13]
+	eor	@x[14],@x[14],@x[15]
+
+	stp	@x[0],@x[2],[$out,#0]		// store output
+	 add	@d[6],@d[6],#4			// increment counter
+	stp	@x[4],@x[6],[$out,#16]
+	stp	@x[8],@x[10],[$out,#32]
+	stp	@x[12],@x[14],[$out,#48]
+	add	$out,$out,#64
+	b.eq	.Ldone_neon
+	sub	$len,$len,#64
+	cmp	$len,#64
+	b.lo	.Less_than_128
+
+	ld1.8	{$T0-$T3},[$inp],#64
+	eor	$A0,$A0,$T0
+	eor	$B0,$B0,$T1
+	eor	$C0,$C0,$T2
+	eor	$D0,$D0,$T3
+	st1.8	{$A0-$D0},[$out],#64
+	b.eq	.Ldone_neon
+	sub	$len,$len,#64
+	cmp	$len,#64
+	b.lo	.Less_than_192
+
+	ld1.8	{$T0-$T3},[$inp],#64
+	eor	$A1,$A1,$T0
+	eor	$B1,$B1,$T1
+	eor	$C1,$C1,$T2
+	eor	$D1,$D1,$T3
+	st1.8	{$A1-$D1},[$out],#64
+	b.eq	.Ldone_neon
+	sub	$len,$len,#64
+
+	st1.8	{$A2-$D2},[sp]
+	b	.Last_neon
+
+.Less_than_128:
+	st1.8	{$A0-$D0},[sp]
+	b	.Last_neon
+.Less_than_192:
+	st1.8	{$A1-$D1},[sp]
+	b	.Last_neon
+
+.align	4
+.Last_neon:
+	sub	$out,$out,#1
+	add	$inp,$inp,$len
+	add	$out,$out,$len
+	add	$ctr,sp,$len
+	neg	$len,$len
+
+.Loop_tail_neon:
+	ldrb	w10,[$inp,$len]
+	ldrb	w11,[$ctr,$len]
+	add	$len,$len,#1
+	eor	w10,w10,w11
+	strb	w10,[$out,$len]
+	cbnz	$len,.Loop_tail_neon
+
+	stp	xzr,xzr,[sp,#0]
+	stp	xzr,xzr,[sp,#16]
+	stp	xzr,xzr,[sp,#32]
+	stp	xzr,xzr,[sp,#48]
+
+.Ldone_neon:
+	ldp	x19,x20,[x29,#16]
+	add	sp,sp,#64
+	ldp	x21,x22,[x29,#32]
+	ldp	x23,x24,[x29,#48]
+	ldp	x25,x26,[x29,#64]
+	ldp	x27,x28,[x29,#80]
+	ldp	x29,x30,[sp],#96
+	ret
+.size	ChaCha20_neon,.-ChaCha20_neon
+___
+{
+my ($T0,$T1,$T2,$T3,$T4,$T5)=@K;
+my ($A0,$B0,$C0,$D0,$A1,$B1,$C1,$D1,$A2,$B2,$C2,$D2,
+    $A3,$B3,$C3,$D3,$A4,$B4,$C4,$D4,$A5,$B5,$C5,$D5) = map("v$_.4s",(0..23));
+
+$code.=<<___;
+.type	ChaCha20_512_neon,%function
+.align	5
+ChaCha20_512_neon:
+	stp	x29,x30,[sp,#-96]!
+	add	x29,sp,#0
+
+	adr	@x[0],.Lsigma
+	stp	x19,x20,[sp,#16]
+	stp	x21,x22,[sp,#32]
+	stp	x23,x24,[sp,#48]
+	stp	x25,x26,[sp,#64]
+	stp	x27,x28,[sp,#80]
+
+.L512_or_more_neon:
+	sub	sp,sp,#128+64
+
+	ldp	@d[0],@d[1],[@x[0]]		// load sigma
+	ld1	{@K[0]},[@x[0]],#16
+	ldp	@d[2],@d[3],[$key]		// load key
+	ldp	@d[4],@d[5],[$key,#16]
+	ld1	{@K[1],@K[2]},[$key]
+	ldp	@d[6],@d[7],[$ctr]		// load counter
+	ld1	{@K[3]},[$ctr]
+	ld1	{$ONE},[@x[0]]
+#ifdef	__ARMEB__
+	rev64	@K[0],@K[0]
+	ror	@d[2],@d[2],#32
+	ror	@d[3],@d[3],#32
+	ror	@d[4],@d[4],#32
+	ror	@d[5],@d[5],#32
+	ror	@d[6],@d[6],#32
+	ror	@d[7],@d[7],#32
+#endif
+	add	@K[3],@K[3],$ONE		// += 1
+	stp	@K[0],@K[1],[sp,#0]		// off-load key block, invariant part
+	add	@K[3],@K[3],$ONE		// not typo
+	str	@K[2],[sp,#32]
+	add	@K[4],@K[3],$ONE
+	add	@K[5],@K[4],$ONE
+	add	@K[6],@K[5],$ONE
+	shl	$ONE,$ONE,#2			// 1 -> 4
+
+	stp	d8,d9,[sp,#128+0]		// meet ABI requirements
+	stp	d10,d11,[sp,#128+16]
+	stp	d12,d13,[sp,#128+32]
+	stp	d14,d15,[sp,#128+48]
+
+	sub	$len,$len,#512			// not typo
+
+.Loop_outer_512_neon:
+	 mov	$A0,@K[0]
+	 mov	$A1,@K[0]
+	 mov	$A2,@K[0]
+	 mov	$A3,@K[0]
+	 mov	$A4,@K[0]
+	 mov	$A5,@K[0]
+	 mov	$B0,@K[1]
+	mov.32	@x[0],@d[0]			// unpack key block
+	 mov	$B1,@K[1]
+	lsr	@x[1],@d[0],#32
+	 mov	$B2,@K[1]
+	mov.32	@x[2],@d[1]
+	 mov	$B3,@K[1]
+	lsr	@x[3],@d[1],#32
+	 mov	$B4,@K[1]
+	mov.32	@x[4],@d[2]
+	 mov	$B5,@K[1]
+	lsr	@x[5],@d[2],#32
+	 mov	$D0,@K[3]
+	mov.32	@x[6],@d[3]
+	 mov	$D1,@K[4]
+	lsr	@x[7],@d[3],#32
+	 mov	$D2,@K[5]
+	mov.32	@x[8],@d[4]
+	 mov	$D3,@K[6]
+	lsr	@x[9],@d[4],#32
+	 mov	$C0,@K[2]
+	mov.32	@x[10],@d[5]
+	 mov	$C1,@K[2]
+	lsr	@x[11],@d[5],#32
+	 add	$D4,$D0,$ONE			// +4
+	mov.32	@x[12],@d[6]
+	 add	$D5,$D1,$ONE			// +4
+	lsr	@x[13],@d[6],#32
+	 mov	$C2,@K[2]
+	mov.32	@x[14],@d[7]
+	 mov	$C3,@K[2]
+	lsr	@x[15],@d[7],#32
+	 mov	$C4,@K[2]
+	 stp	@K[3],@K[4],[sp,#48]		// off-load key block, variable part
+	 mov	$C5,@K[2]
+	 str	@K[5],[sp,#80]
+
+	mov	$ctr,#5
+	subs	$len,$len,#512
+.Loop_upper_neon:
+	sub	$ctr,$ctr,#1
+___
+	my @thread0=&NEONROUND($A0,$B0,$C0,$D0,$T0,0);
+	my @thread1=&NEONROUND($A1,$B1,$C1,$D1,$T1,0);
+	my @thread2=&NEONROUND($A2,$B2,$C2,$D2,$T2,0);
+	my @thread3=&NEONROUND($A3,$B3,$C3,$D3,$T3,0);
+	my @thread4=&NEONROUND($A4,$B4,$C4,$D4,$T4,0);
+	my @thread5=&NEONROUND($A5,$B5,$C5,$D5,$T5,0);
+	my @thread67=(&ROUND(0,4,8,12),&ROUND(0,5,10,15));
+	my $diff = ($#thread0+1)*6 - $#thread67 - 1;
+	my $i = 0;
+
+	foreach (@thread0) {
+		eval;			eval(shift(@thread67));
+		eval(shift(@thread1));	eval(shift(@thread67));
+		eval(shift(@thread2));	eval(shift(@thread67));
+		eval(shift(@thread3));	eval(shift(@thread67));
+		eval(shift(@thread4));	eval(shift(@thread67));
+		eval(shift(@thread5));	eval(shift(@thread67));
+	}
+
+	@thread0=&NEONROUND($A0,$B0,$C0,$D0,$T0,1);
+	@thread1=&NEONROUND($A1,$B1,$C1,$D1,$T1,1);
+	@thread2=&NEONROUND($A2,$B2,$C2,$D2,$T2,1);
+	@thread3=&NEONROUND($A3,$B3,$C3,$D3,$T3,1);
+	@thread4=&NEONROUND($A4,$B4,$C4,$D4,$T4,1);
+	@thread5=&NEONROUND($A5,$B5,$C5,$D5,$T5,1);
+	@thread67=(&ROUND(0,4,8,12),&ROUND(0,5,10,15));
+
+	foreach (@thread0) {
+		eval;			eval(shift(@thread67));
+		eval(shift(@thread1));	eval(shift(@thread67));
+		eval(shift(@thread2));	eval(shift(@thread67));
+		eval(shift(@thread3));	eval(shift(@thread67));
+		eval(shift(@thread4));	eval(shift(@thread67));
+		eval(shift(@thread5));	eval(shift(@thread67));
+	}
+$code.=<<___;
+	cbnz	$ctr,.Loop_upper_neon
+
+	add.32	@x[0],@x[0],@d[0]		// accumulate key block
+	add	@x[1],@x[1],@d[0],lsr#32
+	add.32	@x[2],@x[2],@d[1]
+	add	@x[3],@x[3],@d[1],lsr#32
+	add.32	@x[4],@x[4],@d[2]
+	add	@x[5],@x[5],@d[2],lsr#32
+	add.32	@x[6],@x[6],@d[3]
+	add	@x[7],@x[7],@d[3],lsr#32
+	add.32	@x[8],@x[8],@d[4]
+	add	@x[9],@x[9],@d[4],lsr#32
+	add.32	@x[10],@x[10],@d[5]
+	add	@x[11],@x[11],@d[5],lsr#32
+	add.32	@x[12],@x[12],@d[6]
+	add	@x[13],@x[13],@d[6],lsr#32
+	add.32	@x[14],@x[14],@d[7]
+	add	@x[15],@x[15],@d[7],lsr#32
+
+	add	@x[0],@x[0],@x[1],lsl#32	// pack
+	add	@x[2],@x[2],@x[3],lsl#32
+	ldp	@x[1],@x[3],[$inp,#0]		// load input
+	add	@x[4],@x[4],@x[5],lsl#32
+	add	@x[6],@x[6],@x[7],lsl#32
+	ldp	@x[5],@x[7],[$inp,#16]
+	add	@x[8],@x[8],@x[9],lsl#32
+	add	@x[10],@x[10],@x[11],lsl#32
+	ldp	@x[9],@x[11],[$inp,#32]
+	add	@x[12],@x[12],@x[13],lsl#32
+	add	@x[14],@x[14],@x[15],lsl#32
+	ldp	@x[13],@x[15],[$inp,#48]
+	add	$inp,$inp,#64
+#ifdef	__ARMEB__
+	rev	@x[0],@x[0]
+	rev	@x[2],@x[2]
+	rev	@x[4],@x[4]
+	rev	@x[6],@x[6]
+	rev	@x[8],@x[8]
+	rev	@x[10],@x[10]
+	rev	@x[12],@x[12]
+	rev	@x[14],@x[14]
+#endif
+	eor	@x[0],@x[0],@x[1]
+	eor	@x[2],@x[2],@x[3]
+	eor	@x[4],@x[4],@x[5]
+	eor	@x[6],@x[6],@x[7]
+	eor	@x[8],@x[8],@x[9]
+	eor	@x[10],@x[10],@x[11]
+	eor	@x[12],@x[12],@x[13]
+	eor	@x[14],@x[14],@x[15]
+
+	 stp	@x[0],@x[2],[$out,#0]		// store output
+	 add	@d[6],@d[6],#1			// increment counter
+	mov.32	@x[0],@d[0]			// unpack key block
+	lsr	@x[1],@d[0],#32
+	 stp	@x[4],@x[6],[$out,#16]
+	mov.32	@x[2],@d[1]
+	lsr	@x[3],@d[1],#32
+	 stp	@x[8],@x[10],[$out,#32]
+	mov.32	@x[4],@d[2]
+	lsr	@x[5],@d[2],#32
+	 stp	@x[12],@x[14],[$out,#48]
+	 add	$out,$out,#64
+	mov.32	@x[6],@d[3]
+	lsr	@x[7],@d[3],#32
+	mov.32	@x[8],@d[4]
+	lsr	@x[9],@d[4],#32
+	mov.32	@x[10],@d[5]
+	lsr	@x[11],@d[5],#32
+	mov.32	@x[12],@d[6]
+	lsr	@x[13],@d[6],#32
+	mov.32	@x[14],@d[7]
+	lsr	@x[15],@d[7],#32
+
+	mov	$ctr,#5
+.Loop_lower_neon:
+	sub	$ctr,$ctr,#1
+___
+	@thread0=&NEONROUND($A0,$B0,$C0,$D0,$T0,0);
+	@thread1=&NEONROUND($A1,$B1,$C1,$D1,$T1,0);
+	@thread2=&NEONROUND($A2,$B2,$C2,$D2,$T2,0);
+	@thread3=&NEONROUND($A3,$B3,$C3,$D3,$T3,0);
+	@thread4=&NEONROUND($A4,$B4,$C4,$D4,$T4,0);
+	@thread5=&NEONROUND($A5,$B5,$C5,$D5,$T5,0);
+	@thread67=(&ROUND(0,4,8,12),&ROUND(0,5,10,15));
+
+	foreach (@thread0) {
+		eval;			eval(shift(@thread67));
+		eval(shift(@thread1));	eval(shift(@thread67));
+		eval(shift(@thread2));	eval(shift(@thread67));
+		eval(shift(@thread3));	eval(shift(@thread67));
+		eval(shift(@thread4));	eval(shift(@thread67));
+		eval(shift(@thread5));	eval(shift(@thread67));
+	}
+
+	@thread0=&NEONROUND($A0,$B0,$C0,$D0,$T0,1);
+	@thread1=&NEONROUND($A1,$B1,$C1,$D1,$T1,1);
+	@thread2=&NEONROUND($A2,$B2,$C2,$D2,$T2,1);
+	@thread3=&NEONROUND($A3,$B3,$C3,$D3,$T3,1);
+	@thread4=&NEONROUND($A4,$B4,$C4,$D4,$T4,1);
+	@thread5=&NEONROUND($A5,$B5,$C5,$D5,$T5,1);
+	@thread67=(&ROUND(0,4,8,12),&ROUND(0,5,10,15));
+
+	foreach (@thread0) {
+		eval;			eval(shift(@thread67));
+		eval(shift(@thread1));	eval(shift(@thread67));
+		eval(shift(@thread2));	eval(shift(@thread67));
+		eval(shift(@thread3));	eval(shift(@thread67));
+		eval(shift(@thread4));	eval(shift(@thread67));
+		eval(shift(@thread5));	eval(shift(@thread67));
+	}
+$code.=<<___;
+	cbnz	$ctr,.Loop_lower_neon
+
+	add.32	@x[0],@x[0],@d[0]		// accumulate key block
+	 ldp	@K[0],@K[1],[sp,#0]
+	add	@x[1],@x[1],@d[0],lsr#32
+	 ldp	@K[2],@K[3],[sp,#32]
+	add.32	@x[2],@x[2],@d[1]
+	 ldp	@K[4],@K[5],[sp,#64]
+	add	@x[3],@x[3],@d[1],lsr#32
+	 add	$A0,$A0,@K[0]
+	add.32	@x[4],@x[4],@d[2]
+	 add	$A1,$A1,@K[0]
+	add	@x[5],@x[5],@d[2],lsr#32
+	 add	$A2,$A2,@K[0]
+	add.32	@x[6],@x[6],@d[3]
+	 add	$A3,$A3,@K[0]
+	add	@x[7],@x[7],@d[3],lsr#32
+	 add	$A4,$A4,@K[0]
+	add.32	@x[8],@x[8],@d[4]
+	 add	$A5,$A5,@K[0]
+	add	@x[9],@x[9],@d[4],lsr#32
+	 add	$C0,$C0,@K[2]
+	add.32	@x[10],@x[10],@d[5]
+	 add	$C1,$C1,@K[2]
+	add	@x[11],@x[11],@d[5],lsr#32
+	 add	$C2,$C2,@K[2]
+	add.32	@x[12],@x[12],@d[6]
+	 add	$C3,$C3,@K[2]
+	add	@x[13],@x[13],@d[6],lsr#32
+	 add	$C4,$C4,@K[2]
+	add.32	@x[14],@x[14],@d[7]
+	 add	$C5,$C5,@K[2]
+	add	@x[15],@x[15],@d[7],lsr#32
+	 add	$D4,$D4,$ONE			// +4
+	add	@x[0],@x[0],@x[1],lsl#32	// pack
+	 add	$D5,$D5,$ONE			// +4
+	add	@x[2],@x[2],@x[3],lsl#32
+	 add	$D0,$D0,@K[3]
+	ldp	@x[1],@x[3],[$inp,#0]		// load input
+	 add	$D1,$D1,@K[4]
+	add	@x[4],@x[4],@x[5],lsl#32
+	 add	$D2,$D2,@K[5]
+	add	@x[6],@x[6],@x[7],lsl#32
+	 add	$D3,$D3,@K[6]
+	ldp	@x[5],@x[7],[$inp,#16]
+	 add	$D4,$D4,@K[3]
+	add	@x[8],@x[8],@x[9],lsl#32
+	 add	$D5,$D5,@K[4]
+	add	@x[10],@x[10],@x[11],lsl#32
+	 add	$B0,$B0,@K[1]
+	ldp	@x[9],@x[11],[$inp,#32]
+	 add	$B1,$B1,@K[1]
+	add	@x[12],@x[12],@x[13],lsl#32
+	 add	$B2,$B2,@K[1]
+	add	@x[14],@x[14],@x[15],lsl#32
+	 add	$B3,$B3,@K[1]
+	ldp	@x[13],@x[15],[$inp,#48]
+	 add	$B4,$B4,@K[1]
+	add	$inp,$inp,#64
+	 add	$B5,$B5,@K[1]
+
+#ifdef	__ARMEB__
+	rev	@x[0],@x[0]
+	rev	@x[2],@x[2]
+	rev	@x[4],@x[4]
+	rev	@x[6],@x[6]
+	rev	@x[8],@x[8]
+	rev	@x[10],@x[10]
+	rev	@x[12],@x[12]
+	rev	@x[14],@x[14]
+#endif
+	ld1.8	{$T0-$T3},[$inp],#64
+	eor	@x[0],@x[0],@x[1]
+	eor	@x[2],@x[2],@x[3]
+	eor	@x[4],@x[4],@x[5]
+	eor	@x[6],@x[6],@x[7]
+	eor	@x[8],@x[8],@x[9]
+	 eor	$A0,$A0,$T0
+	eor	@x[10],@x[10],@x[11]
+	 eor	$B0,$B0,$T1
+	eor	@x[12],@x[12],@x[13]
+	 eor	$C0,$C0,$T2
+	eor	@x[14],@x[14],@x[15]
+	 eor	$D0,$D0,$T3
+	 ld1.8	{$T0-$T3},[$inp],#64
+
+	stp	@x[0],@x[2],[$out,#0]		// store output
+	 add	@d[6],@d[6],#7			// increment counter
+	stp	@x[4],@x[6],[$out,#16]
+	stp	@x[8],@x[10],[$out,#32]
+	stp	@x[12],@x[14],[$out,#48]
+	add	$out,$out,#64
+	st1.8	{$A0-$D0},[$out],#64
+
+	ld1.8	{$A0-$D0},[$inp],#64
+	eor	$A1,$A1,$T0
+	eor	$B1,$B1,$T1
+	eor	$C1,$C1,$T2
+	eor	$D1,$D1,$T3
+	st1.8	{$A1-$D1},[$out],#64
+
+	ld1.8	{$A1-$D1},[$inp],#64
+	eor	$A2,$A2,$A0
+	 ldp	@K[0],@K[1],[sp,#0]
+	eor	$B2,$B2,$B0
+	 ldp	@K[2],@K[3],[sp,#32]
+	eor	$C2,$C2,$C0
+	eor	$D2,$D2,$D0
+	st1.8	{$A2-$D2},[$out],#64
+
+	ld1.8	{$A2-$D2},[$inp],#64
+	eor	$A3,$A3,$A1
+	eor	$B3,$B3,$B1
+	eor	$C3,$C3,$C1
+	eor	$D3,$D3,$D1
+	st1.8	{$A3-$D3},[$out],#64
+
+	ld1.8	{$A3-$D3},[$inp],#64
+	eor	$A4,$A4,$A2
+	eor	$B4,$B4,$B2
+	eor	$C4,$C4,$C2
+	eor	$D4,$D4,$D2
+	st1.8	{$A4-$D4},[$out],#64
+
+	shl	$A0,$ONE,#1			// 4 -> 8
+	eor	$A5,$A5,$A3
+	eor	$B5,$B5,$B3
+	eor	$C5,$C5,$C3
+	eor	$D5,$D5,$D3
+	st1.8	{$A5-$D5},[$out],#64
+
+	add	@K[3],@K[3],$A0			// += 8
+	add	@K[4],@K[4],$A0
+	add	@K[5],@K[5],$A0
+	add	@K[6],@K[6],$A0
+
+	b.hs	.Loop_outer_512_neon
+
+	adds	$len,$len,#512
+	ushr	$A0,$ONE,#2			// 4 -> 1
+
+	ldp	d8,d9,[sp,#128+0]		// meet ABI requirements
+	ldp	d10,d11,[sp,#128+16]
+	ldp	d12,d13,[sp,#128+32]
+	ldp	d14,d15,[sp,#128+48]
+
+	stp	@K[0],$ONE,[sp,#0]		// wipe off-load area
+	stp	@K[0],$ONE,[sp,#32]
+	stp	@K[0],$ONE,[sp,#64]
+
+	b.eq	.Ldone_512_neon
+
+	cmp	$len,#192
+	sub	@K[3],@K[3],$A0			// -= 1
+	sub	@K[4],@K[4],$A0
+	sub	@K[5],@K[5],$A0
+	add	sp,sp,#128
+	b.hs	.Loop_outer_neon
+
+	eor	@K[1],@K[1],@K[1]
+	eor	@K[2],@K[2],@K[2]
+	eor	@K[3],@K[3],@K[3]
+	eor	@K[4],@K[4],@K[4]
+	eor	@K[5],@K[5],@K[5]
+	eor	@K[6],@K[6],@K[6]
+	b	.Loop_outer
+
+.Ldone_512_neon:
+	ldp	x19,x20,[x29,#16]
+	add	sp,sp,#128+64
+	ldp	x21,x22,[x29,#32]
+	ldp	x23,x24,[x29,#48]
+	ldp	x25,x26,[x29,#64]
+	ldp	x27,x28,[x29,#80]
+	ldp	x29,x30,[sp],#96
+	ret
+.size	ChaCha20_512_neon,.-ChaCha20_512_neon
+___
+}
+}}}
+
+foreach (split("\n",$code)) {
+	s/\`([^\`]*)\`/eval $1/geo;
+
+	(s/\b([a-z]+)\.32\b/$1/ and (s/x([0-9]+)/w$1/g or 1))	or
+	(m/\b(eor|ext|mov)\b/ and (s/\.4s/\.16b/g or 1))	or
+	(s/\b((?:ld|st)1)\.8\b/$1/ and (s/\.4s/\.16b/g or 1))	or
+	(m/\b(ld|st)[rp]\b/ and (s/v([0-9]+)\.4s/q$1/g or 1))	or
+	(s/\brev32\.16\b/rev32/ and (s/\.4s/\.8h/g or 1));
+
+	#s/\bq([0-9]+)#(lo|hi)/sprintf "d%d",2*$1+($2 eq "hi")/geo;
+
+	print $_,"\n";
+}
+close STDOUT;	# flush
diff --git a/crypto/chacha/asm/chacha-c64xplus.pl b/crypto/chacha/asm/chacha-c64xplus.pl
new file mode 100755
index 000000000..bdb380442
--- /dev/null
+++ b/crypto/chacha/asm/chacha-c64xplus.pl
@@ -0,0 +1,926 @@
+#! /usr/bin/env perl
+# Copyright 2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+#
+# ====================================================================
+# Written by Andy Polyakov  for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+#
+# ChaCha20 for C64x+.
+#
+# October 2015
+#
+# Performance is 3.54 cycles per processed byte, which is ~4.3 times
+# faster than code generated by TI compiler. Compiler also disables
+# interrupts for some reason, thus making interrupt response time
+# dependent on input length. This module on the other hand is free
+# from such limiation.
+
+$output=pop;
+open STDOUT,">$output";
+
+($OUT,$INP,$LEN,$KEYB,$COUNTERA)=("A4","B4","A6","B6","A8");
+($KEYA,$COUNTERB,$STEP)=("A7","B7","A3");
+
+@X=  ("A16","B16","A17","B17","A18","B18","A19","B19",
+      "A20","B20","A21","B21","A22","B22","A23","B23");
+@Y=  ("A24","B24","A25","B25","A26","B26","A27","B27",
+      "A28","B28","A29","B29","A30","B30","A31","B31");
+@DAT=("A6", "A7", "B6", "B7", "A8", "A9", "B8", "B9",
+      "A10","A11","B10","B11","A12","A13","B12","B13");
+
+# yes, overlaps with @DAT, used only in 2x interleave code path...
+@K2x=("A6", "B6", "A7", "B7", "A8", "B8", "A9", "B9",
+      "A10","B10","A11","B11","A2", "B2", "A13","B13");
+
+$code.=<<___;
+	.text
+
+	.if	.ASSEMBLER_VERSION<7000000
+	.asg	0,__TI_EABI__
+	.endif
+	.if	__TI_EABI__
+	.asg	ChaCha20_ctr32,_ChaCha20_ctr32
+	.endif
+
+	.asg	B3,RA
+	.asg	A15,FP
+	.asg	B15,SP
+
+	.global	_ChaCha20_ctr32
+	.align	32
+_ChaCha20_ctr32:
+	.asmfunc	stack_usage(40+64)
+	MV	$LEN,A0			; reassign
+  [!A0]	BNOP	RA			; no data
+|| [A0]	STW	FP,*SP--(40+64)		; save frame pointer and alloca(40+64)
+|| [A0]	MV	SP,FP
+   [A0]	STDW	B13:B12,*SP[4+8]	; ABI says so
+|| [A0]	MV	$KEYB,$KEYA
+|| [A0]	MV	$COUNTERA,$COUNTERB
+   [A0]	STDW	B11:B10,*SP[3+8]
+|| [A0]	STDW	A13:A12,*FP[-3]
+   [A0]	STDW	A11:A10,*FP[-4]
+|| [A0]	MVK	128,$STEP		; 2 * input block size
+
+   [A0]	LDW	*${KEYA}[0],@Y[4]	; load key
+|| [A0]	LDW	*${KEYB}[1],@Y[5]
+|| [A0]	MVK	0x00007865,@Y[0]	; synthesize sigma
+|| [A0]	MVK	0x0000646e,@Y[1]
+   [A0]	LDW	*${KEYA}[2],@Y[6]
+|| [A0]	LDW	*${KEYB}[3],@Y[7]
+|| [A0]	MVKH	0x61700000,@Y[0]
+|| [A0]	MVKH	0x33200000,@Y[1]
+	LDW	*${KEYA}[4],@Y[8]
+||	LDW	*${KEYB}[5],@Y[9]
+||	MVK	0x00002d32,@Y[2]
+||	MVK	0x00006574,@Y[3]
+	LDW	*${KEYA}[6],@Y[10]
+||	LDW	*${KEYB}[7],@Y[11]
+||	MVKH	0x79620000,@Y[2]
+||	MVKH	0x6b200000,@Y[3]
+	LDW	*${COUNTERA}[0],@Y[12]	; load counter||nonce
+||	LDW	*${COUNTERB}[1],@Y[13]
+||	CMPLTU	A0,$STEP,A1		; is length < 2*blocks?
+	LDW	*${COUNTERA}[2],@Y[14]
+||	LDW	*${COUNTERB}[3],@Y[15]
+|| [A1]	BNOP	top1x?
+   [A1]	MVK	64,$STEP		; input block size
+||	MVK	10,B0			; inner loop counter
+
+	DMV	@Y[2],@Y[0],@X[2]:@X[0]	; copy block
+||	DMV	@Y[3],@Y[1],@X[3]:@X[1]
+||[!A1]	STDW	@Y[2]:@Y[0],*FP[-12]	; offload key material to stack
+||[!A1]	STDW	@Y[3]:@Y[1],*SP[2]
+	DMV	@Y[6],@Y[4],@X[6]:@X[4]
+||	DMV	@Y[7],@Y[5],@X[7]:@X[5]
+||[!A1]	STDW	@Y[6]:@Y[4],*FP[-10]
+||[!A1]	STDW	@Y[7]:@Y[5],*SP[4]
+	DMV	@Y[10],@Y[8],@X[10]:@X[8]
+||	DMV	@Y[11],@Y[9],@X[11]:@X[9]
+||[!A1]	STDW	@Y[10]:@Y[8],*FP[-8]
+||[!A1]	STDW	@Y[11]:@Y[9],*SP[6]
+	DMV	@Y[14],@Y[12],@X[14]:@X[12]
+||	DMV	@Y[15],@Y[13],@X[15]:@X[13]
+||[!A1]	MV	@Y[12],@K2x[12]		; counter
+||[!A1]	MV	@Y[13],@K2x[13]
+||[!A1]	STW	@Y[14],*FP[-6*2]
+||[!A1]	STW	@Y[15],*SP[8*2]
+___
+{	################################################################
+	# 2x interleave gives 50% performance improvement
+	#
+my ($a0,$a1,$a2,$a3) = (0..3);
+my ($b0,$b1,$b2,$b3) = (4..7);
+my ($c0,$c1,$c2,$c3) = (8..11);
+my ($d0,$d1,$d2,$d3) = (12..15);
+
+$code.=<<___;
+outer2x?:
+	ADD	@X[$b1],@X[$a1],@X[$a1]
+||	ADD	@X[$b2],@X[$a2],@X[$a2]
+||	ADD	@X[$b0],@X[$a0],@X[$a0]
+||	ADD	@X[$b3],@X[$a3],@X[$a3]
+||	 DMV	@Y[2],@Y[0],@K2x[2]:@K2x[0]
+||	 DMV	@Y[3],@Y[1],@K2x[3]:@K2x[1]
+	XOR	@X[$a1],@X[$d1],@X[$d1]
+||	XOR	@X[$a2],@X[$d2],@X[$d2]
+||	XOR	@X[$a0],@X[$d0],@X[$d0]
+||	XOR	@X[$a3],@X[$d3],@X[$d3]
+||	 DMV	@Y[6],@Y[4],@K2x[6]:@K2x[4]
+||	 DMV	@Y[7],@Y[5],@K2x[7]:@K2x[5]
+	SWAP2	@X[$d1],@X[$d1]		; rotate by 16
+||	SWAP2	@X[$d2],@X[$d2]
+||	SWAP2	@X[$d0],@X[$d0]
+||	SWAP2	@X[$d3],@X[$d3]
+
+	ADD	@X[$d1],@X[$c1],@X[$c1]
+||	ADD	@X[$d2],@X[$c2],@X[$c2]
+||	ADD	@X[$d0],@X[$c0],@X[$c0]
+||	ADD	@X[$d3],@X[$c3],@X[$c3]
+||	 DMV	@Y[10],@Y[8],@K2x[10]:@K2x[8]
+||	 DMV	@Y[11],@Y[9],@K2x[11]:@K2x[9]
+	XOR	@X[$c1],@X[$b1],@X[$b1]
+||	XOR	@X[$c2],@X[$b2],@X[$b2]
+||	XOR	@X[$c0],@X[$b0],@X[$b0]
+||	XOR	@X[$c3],@X[$b3],@X[$b3]
+||	 ADD	1,@Y[12],@Y[12]		; adjust counter for 2nd block
+	ROTL	@X[$b1],12,@X[$b1]
+||	ROTL	@X[$b2],12,@X[$b2]
+||	 MV	@Y[14],@K2x[14]
+||	 MV	@Y[15],@K2x[15]
+top2x?:
+	ROTL	@X[$b0],12,@X[$b0]
+||	ROTL	@X[$b3],12,@X[$b3]
+||	 ADD	@Y[$b1],@Y[$a1],@Y[$a1]
+||	 ADD	@Y[$b2],@Y[$a2],@Y[$a2]
+	 ADD	@Y[$b0],@Y[$a0],@Y[$a0]
+||	 ADD	@Y[$b3],@Y[$a3],@Y[$a3]
+
+||	ADD	@X[$b1],@X[$a1],@X[$a1]
+||	ADD	@X[$b2],@X[$a2],@X[$a2]
+||	 XOR	@Y[$a1],@Y[$d1],@Y[$d1]
+||	 XOR	@Y[$a2],@Y[$d2],@Y[$d2]
+	 XOR	@Y[$a0],@Y[$d0],@Y[$d0]
+||	 XOR	@Y[$a3],@Y[$d3],@Y[$d3]
+||	ADD	@X[$b0],@X[$a0],@X[$a0]
+||	ADD	@X[$b3],@X[$a3],@X[$a3]
+||	XOR	@X[$a1],@X[$d1],@X[$d1]
+||	XOR	@X[$a2],@X[$d2],@X[$d2]
+	XOR	@X[$a0],@X[$d0],@X[$d0]
+||	XOR	@X[$a3],@X[$d3],@X[$d3]
+||	ROTL	@X[$d1],8,@X[$d1]
+||	ROTL	@X[$d2],8,@X[$d2]
+||	 SWAP2	@Y[$d1],@Y[$d1]		; rotate by 16
+||	 SWAP2	@Y[$d2],@Y[$d2]
+||	 SWAP2	@Y[$d0],@Y[$d0]
+||	 SWAP2	@Y[$d3],@Y[$d3]
+	ROTL	@X[$d0],8,@X[$d0]
+||	ROTL	@X[$d3],8,@X[$d3]
+||	 ADD	@Y[$d1],@Y[$c1],@Y[$c1]
+||	 ADD	@Y[$d2],@Y[$c2],@Y[$c2]
+||	 ADD	@Y[$d0],@Y[$c0],@Y[$c0]
+||	 ADD	@Y[$d3],@Y[$c3],@Y[$c3]
+||	BNOP	middle2x1?		; protect from interrupt
+
+	ADD	@X[$d1],@X[$c1],@X[$c1]
+||	ADD	@X[$d2],@X[$c2],@X[$c2]
+||	 XOR	@Y[$c1],@Y[$b1],@Y[$b1]
+||	 XOR	@Y[$c2],@Y[$b2],@Y[$b2]
+||	 XOR	@Y[$c0],@Y[$b0],@Y[$b0]
+||	 XOR	@Y[$c3],@Y[$b3],@Y[$b3]
+	ADD	@X[$d0],@X[$c0],@X[$c0]
+||	ADD	@X[$d3],@X[$c3],@X[$c3]
+||	XOR	@X[$c1],@X[$b1],@X[$b1]
+||	XOR	@X[$c2],@X[$b2],@X[$b2]
+||	ROTL	@X[$d1],0,@X[$d2]	; moved to avoid cross-path stall
+||	ROTL	@X[$d2],0,@X[$d3]
+	XOR	@X[$c0],@X[$b0],@X[$b0]
+||	XOR	@X[$c3],@X[$b3],@X[$b3]
+||	MV	@X[$d0],@X[$d1]
+||	MV	@X[$d3],@X[$d0]
+||	 ROTL	@Y[$b1],12,@Y[$b1]
+||	 ROTL	@Y[$b2],12,@Y[$b2]
+	ROTL	@X[$b1],7,@X[$b0]	; avoided cross-path stall
+||	ROTL	@X[$b2],7,@X[$b1]
+	ROTL	@X[$b0],7,@X[$b3]
+||	ROTL	@X[$b3],7,@X[$b2]
+middle2x1?:
+
+	 ROTL	@Y[$b0],12,@Y[$b0]
+||	 ROTL	@Y[$b3],12,@Y[$b3]
+||	ADD	@X[$b0],@X[$a0],@X[$a0]
+||	ADD	@X[$b1],@X[$a1],@X[$a1]
+	ADD	@X[$b2],@X[$a2],@X[$a2]
+||	ADD	@X[$b3],@X[$a3],@X[$a3]
+
+||	 ADD	@Y[$b1],@Y[$a1],@Y[$a1]
+||	 ADD	@Y[$b2],@Y[$a2],@Y[$a2]
+||	XOR	@X[$a0],@X[$d0],@X[$d0]
+||	XOR	@X[$a1],@X[$d1],@X[$d1]
+	XOR	@X[$a2],@X[$d2],@X[$d2]
+||	XOR	@X[$a3],@X[$d3],@X[$d3]
+||	 ADD	@Y[$b0],@Y[$a0],@Y[$a0]
+||	 ADD	@Y[$b3],@Y[$a3],@Y[$a3]
+||	 XOR	@Y[$a1],@Y[$d1],@Y[$d1]
+||	 XOR	@Y[$a2],@Y[$d2],@Y[$d2]
+	 XOR	@Y[$a0],@Y[$d0],@Y[$d0]
+||	 XOR	@Y[$a3],@Y[$d3],@Y[$d3]
+||	 ROTL	@Y[$d1],8,@Y[$d1]
+||	 ROTL	@Y[$d2],8,@Y[$d2]
+||	SWAP2	@X[$d0],@X[$d0]		; rotate by 16
+||	SWAP2	@X[$d1],@X[$d1]
+||	SWAP2	@X[$d2],@X[$d2]
+||	SWAP2	@X[$d3],@X[$d3]
+	 ROTL	@Y[$d0],8,@Y[$d0]
+||	 ROTL	@Y[$d3],8,@Y[$d3]
+||	ADD	@X[$d0],@X[$c2],@X[$c2]
+||	ADD	@X[$d1],@X[$c3],@X[$c3]
+||	ADD	@X[$d2],@X[$c0],@X[$c0]
+||	ADD	@X[$d3],@X[$c1],@X[$c1]
+||	BNOP	middle2x2?		; protect from interrupt
+
+	 ADD	@Y[$d1],@Y[$c1],@Y[$c1]
+||	 ADD	@Y[$d2],@Y[$c2],@Y[$c2]
+||	XOR	@X[$c2],@X[$b0],@X[$b0]
+||	XOR	@X[$c3],@X[$b1],@X[$b1]
+||	XOR	@X[$c0],@X[$b2],@X[$b2]
+||	XOR	@X[$c1],@X[$b3],@X[$b3]
+	 ADD	@Y[$d0],@Y[$c0],@Y[$c0]
+||	 ADD	@Y[$d3],@Y[$c3],@Y[$c3]
+||	 XOR	@Y[$c1],@Y[$b1],@Y[$b1]
+||	 XOR	@Y[$c2],@Y[$b2],@Y[$b2]
+||	 ROTL	@Y[$d1],0,@Y[$d2]	; moved to avoid cross-path stall
+||	 ROTL	@Y[$d2],0,@Y[$d3]
+	 XOR	@Y[$c0],@Y[$b0],@Y[$b0]
+||	 XOR	@Y[$c3],@Y[$b3],@Y[$b3]
+||	 MV	@Y[$d0],@Y[$d1]
+||	 MV	@Y[$d3],@Y[$d0]
+||	ROTL	@X[$b0],12,@X[$b0]
+||	ROTL	@X[$b1],12,@X[$b1]
+	 ROTL	@Y[$b1],7,@Y[$b0]	; avoided cross-path stall
+||	 ROTL	@Y[$b2],7,@Y[$b1]
+	 ROTL	@Y[$b0],7,@Y[$b3]
+||	 ROTL	@Y[$b3],7,@Y[$b2]
+middle2x2?:
+
+	ROTL	@X[$b2],12,@X[$b2]
+||	ROTL	@X[$b3],12,@X[$b3]
+||	 ADD	@Y[$b0],@Y[$a0],@Y[$a0]
+||	 ADD	@Y[$b1],@Y[$a1],@Y[$a1]
+	 ADD	@Y[$b2],@Y[$a2],@Y[$a2]
+||	 ADD	@Y[$b3],@Y[$a3],@Y[$a3]
+
+||	ADD	@X[$b0],@X[$a0],@X[$a0]
+||	ADD	@X[$b1],@X[$a1],@X[$a1]
+||	 XOR	@Y[$a0],@Y[$d0],@Y[$d0]
+||	 XOR	@Y[$a1],@Y[$d1],@Y[$d1]
+	 XOR	@Y[$a2],@Y[$d2],@Y[$d2]
+||	 XOR	@Y[$a3],@Y[$d3],@Y[$d3]
+||	ADD	@X[$b2],@X[$a2],@X[$a2]
+||	ADD	@X[$b3],@X[$a3],@X[$a3]
+||	XOR	@X[$a0],@X[$d0],@X[$d0]
+||	XOR	@X[$a1],@X[$d1],@X[$d1]
+	XOR	@X[$a2],@X[$d2],@X[$d2]
+||	XOR	@X[$a3],@X[$d3],@X[$d3]
+||	ROTL	@X[$d0],8,@X[$d0]
+||	ROTL	@X[$d1],8,@X[$d1]
+||	 SWAP2	@Y[$d0],@Y[$d0]		; rotate by 16
+||	 SWAP2	@Y[$d1],@Y[$d1]
+||	 SWAP2	@Y[$d2],@Y[$d2]
+||	 SWAP2	@Y[$d3],@Y[$d3]
+	ROTL	@X[$d2],8,@X[$d2]
+||	ROTL	@X[$d3],8,@X[$d3]
+||	 ADD	@Y[$d0],@Y[$c2],@Y[$c2]
+||	 ADD	@Y[$d1],@Y[$c3],@Y[$c3]
+||	 ADD	@Y[$d2],@Y[$c0],@Y[$c0]
+||	 ADD	@Y[$d3],@Y[$c1],@Y[$c1]
+||	BNOP	bottom2x1?		; protect from interrupt
+
+	ADD	@X[$d0],@X[$c2],@X[$c2]
+||	ADD	@X[$d1],@X[$c3],@X[$c3]
+||	 XOR	@Y[$c2],@Y[$b0],@Y[$b0]
+||	 XOR	@Y[$c3],@Y[$b1],@Y[$b1]
+||	 XOR	@Y[$c0],@Y[$b2],@Y[$b2]
+||	 XOR	@Y[$c1],@Y[$b3],@Y[$b3]
+	ADD	@X[$d2],@X[$c0],@X[$c0]
+||	ADD	@X[$d3],@X[$c1],@X[$c1]
+||	XOR	@X[$c2],@X[$b0],@X[$b0]
+||	XOR	@X[$c3],@X[$b1],@X[$b1]
+||	ROTL	@X[$d0],0,@X[$d3]	; moved to avoid cross-path stall
+||	ROTL	@X[$d1],0,@X[$d0]
+	XOR	@X[$c0],@X[$b2],@X[$b2]
+||	XOR	@X[$c1],@X[$b3],@X[$b3]
+||	MV	@X[$d2],@X[$d1]
+||	MV	@X[$d3],@X[$d2]
+||	 ROTL	@Y[$b0],12,@Y[$b0]
+||	 ROTL	@Y[$b1],12,@Y[$b1]
+	ROTL	@X[$b0],7,@X[$b1]	; avoided cross-path stall
+||	ROTL	@X[$b1],7,@X[$b2]
+	ROTL	@X[$b2],7,@X[$b3]
+||	ROTL	@X[$b3],7,@X[$b0]
+|| [B0]	SUB	B0,1,B0			; decrement inner loop counter
+bottom2x1?:
+
+	 ROTL	@Y[$b2],12,@Y[$b2]
+||	 ROTL	@Y[$b3],12,@Y[$b3]
+|| [B0]	ADD	@X[$b1],@X[$a1],@X[$a1]	; modulo-scheduled
+|| [B0]	ADD	@X[$b2],@X[$a2],@X[$a2]
+   [B0]	ADD	@X[$b0],@X[$a0],@X[$a0]
+|| [B0]	ADD	@X[$b3],@X[$a3],@X[$a3]
+
+||	 ADD	@Y[$b0],@Y[$a0],@Y[$a0]
+||	 ADD	@Y[$b1],@Y[$a1],@Y[$a1]
+|| [B0]	XOR	@X[$a1],@X[$d1],@X[$d1]
+|| [B0]	XOR	@X[$a2],@X[$d2],@X[$d2]
+   [B0]	XOR	@X[$a0],@X[$d0],@X[$d0]
+|| [B0]	XOR	@X[$a3],@X[$d3],@X[$d3]
+||	 ADD	@Y[$b2],@Y[$a2],@Y[$a2]
+||	 ADD	@Y[$b3],@Y[$a3],@Y[$a3]
+||	 XOR	@Y[$a0],@Y[$d0],@Y[$d0]
+||	 XOR	@Y[$a1],@Y[$d1],@Y[$d1]
+	 XOR	@Y[$a2],@Y[$d2],@Y[$d2]
+||	 XOR	@Y[$a3],@Y[$d3],@Y[$d3]
+||	 ROTL	@Y[$d0],8,@Y[$d0]
+||	 ROTL	@Y[$d1],8,@Y[$d1]
+|| [B0]	SWAP2	@X[$d1],@X[$d1]		; rotate by 16
+|| [B0]	SWAP2	@X[$d2],@X[$d2]
+|| [B0]	SWAP2	@X[$d0],@X[$d0]
+|| [B0]	SWAP2	@X[$d3],@X[$d3]
+	 ROTL	@Y[$d2],8,@Y[$d2]
+||	 ROTL	@Y[$d3],8,@Y[$d3]
+|| [B0]	ADD	@X[$d1],@X[$c1],@X[$c1]
+|| [B0]	ADD	@X[$d2],@X[$c2],@X[$c2]
+|| [B0]	ADD	@X[$d0],@X[$c0],@X[$c0]
+|| [B0]	ADD	@X[$d3],@X[$c3],@X[$c3]
+|| [B0]	BNOP	top2x?			; even protects from interrupt
+
+	 ADD	@Y[$d0],@Y[$c2],@Y[$c2]
+||	 ADD	@Y[$d1],@Y[$c3],@Y[$c3]
+|| [B0]	XOR	@X[$c1],@X[$b1],@X[$b1]
+|| [B0]	XOR	@X[$c2],@X[$b2],@X[$b2]
+|| [B0]	XOR	@X[$c0],@X[$b0],@X[$b0]
+|| [B0]	XOR	@X[$c3],@X[$b3],@X[$b3]
+	 ADD	@Y[$d2],@Y[$c0],@Y[$c0]
+||	 ADD	@Y[$d3],@Y[$c1],@Y[$c1]
+||	 XOR	@Y[$c2],@Y[$b0],@Y[$b0]
+||	 XOR	@Y[$c3],@Y[$b1],@Y[$b1]
+||	 ROTL	@Y[$d0],0,@Y[$d3]	; moved to avoid cross-path stall
+||	 ROTL	@Y[$d1],0,@Y[$d0]
+	 XOR	@Y[$c0],@Y[$b2],@Y[$b2]
+||	 XOR	@Y[$c1],@Y[$b3],@Y[$b3]
+||	 MV	@Y[$d2],@Y[$d1]
+||	 MV	@Y[$d3],@Y[$d2]
+|| [B0]	ROTL	@X[$b1],12,@X[$b1]
+|| [B0]	ROTL	@X[$b2],12,@X[$b2]
+	 ROTL	@Y[$b0],7,@Y[$b1]	; avoided cross-path stall
+||	 ROTL	@Y[$b1],7,@Y[$b2]
+	 ROTL	@Y[$b2],7,@Y[$b3]
+||	 ROTL	@Y[$b3],7,@Y[$b0]
+bottom2x2?:
+___
+}
+
+$code.=<<___;
+	ADD	@K2x[0],@X[0],@X[0]	; accumulate key material
+||	ADD	@K2x[1],@X[1],@X[1]
+||	ADD	@K2x[2],@X[2],@X[2]
+||	ADD	@K2x[3],@X[3],@X[3]
+	 ADD	@K2x[0],@Y[0],@Y[0]
+||	 ADD	@K2x[1],@Y[1],@Y[1]
+||	 ADD	@K2x[2],@Y[2],@Y[2]
+||	 ADD	@K2x[3],@Y[3],@Y[3]
+||	LDNDW	*${INP}++[8],@DAT[1]:@DAT[0]
+	ADD	@K2x[4],@X[4],@X[4]
+||	ADD	@K2x[5],@X[5],@X[5]
+||	ADD	@K2x[6],@X[6],@X[6]
+||	ADD	@K2x[7],@X[7],@X[7]
+||	LDNDW	*${INP}[-7],@DAT[3]:@DAT[2]
+	 ADD	@K2x[4],@Y[4],@Y[4]
+||	 ADD	@K2x[5],@Y[5],@Y[5]
+||	 ADD	@K2x[6],@Y[6],@Y[6]
+||	 ADD	@K2x[7],@Y[7],@Y[7]
+||	LDNDW	*${INP}[-6],@DAT[5]:@DAT[4]
+	ADD	@K2x[8],@X[8],@X[8]
+||	ADD	@K2x[9],@X[9],@X[9]
+||	ADD	@K2x[10],@X[10],@X[10]
+||	ADD	@K2x[11],@X[11],@X[11]
+||	LDNDW	*${INP}[-5],@DAT[7]:@DAT[6]
+	 ADD	@K2x[8],@Y[8],@Y[8]
+||	 ADD	@K2x[9],@Y[9],@Y[9]
+||	 ADD	@K2x[10],@Y[10],@Y[10]
+||	 ADD	@K2x[11],@Y[11],@Y[11]
+||	LDNDW	*${INP}[-4],@DAT[9]:@DAT[8]
+	ADD	@K2x[12],@X[12],@X[12]
+||	ADD	@K2x[13],@X[13],@X[13]
+||	ADD	@K2x[14],@X[14],@X[14]
+||	ADD	@K2x[15],@X[15],@X[15]
+||	LDNDW	*${INP}[-3],@DAT[11]:@DAT[10]
+	 ADD	@K2x[12],@Y[12],@Y[12]
+||	 ADD	@K2x[13],@Y[13],@Y[13]
+||	 ADD	@K2x[14],@Y[14],@Y[14]
+||	 ADD	@K2x[15],@Y[15],@Y[15]
+||	LDNDW	*${INP}[-2],@DAT[13]:@DAT[12]
+	 ADD	1,@Y[12],@Y[12]		; adjust counter for 2nd block
+||	ADD	2,@K2x[12],@K2x[12]	; increment counter
+||	LDNDW	*${INP}[-1],@DAT[15]:@DAT[14]
+
+	.if	.BIG_ENDIAN
+	SWAP2	@X[0],@X[0]
+||	SWAP2	@X[1],@X[1]
+||	SWAP2	@X[2],@X[2]
+||	SWAP2	@X[3],@X[3]
+	SWAP2	@X[4],@X[4]
+||	SWAP2	@X[5],@X[5]
+||	SWAP2	@X[6],@X[6]
+||	SWAP2	@X[7],@X[7]
+	SWAP2	@X[8],@X[8]
+||	SWAP2	@X[9],@X[9]
+||	SWAP4	@X[0],@X[1]
+||	SWAP4	@X[1],@X[0]
+	SWAP2	@X[10],@X[10]
+||	SWAP2	@X[11],@X[11]
+||	SWAP4	@X[2],@X[3]
+||	SWAP4	@X[3],@X[2]
+	SWAP2	@X[12],@X[12]
+||	SWAP2	@X[13],@X[13]
+||	SWAP4	@X[4],@X[5]
+||	SWAP4	@X[5],@X[4]
+	SWAP2	@X[14],@X[14]
+||	SWAP2	@X[15],@X[15]
+||	SWAP4	@X[6],@X[7]
+||	SWAP4	@X[7],@X[6]
+	SWAP4	@X[8],@X[9]
+||	SWAP4	@X[9],@X[8]
+||	 SWAP2	@Y[0],@Y[0]
+||	 SWAP2	@Y[1],@Y[1]
+	SWAP4	@X[10],@X[11]
+||	SWAP4	@X[11],@X[10]
+||	 SWAP2	@Y[2],@Y[2]
+||	 SWAP2	@Y[3],@Y[3]
+	SWAP4	@X[12],@X[13]
+||	SWAP4	@X[13],@X[12]
+||	 SWAP2	@Y[4],@Y[4]
+||	 SWAP2	@Y[5],@Y[5]
+	SWAP4	@X[14],@X[15]
+||	SWAP4	@X[15],@X[14]
+||	 SWAP2	@Y[6],@Y[6]
+||	 SWAP2	@Y[7],@Y[7]
+	 SWAP2	@Y[8],@Y[8]
+||	 SWAP2	@Y[9],@Y[9]
+||	 SWAP4	@Y[0],@Y[1]
+||	 SWAP4	@Y[1],@Y[0]
+	 SWAP2	@Y[10],@Y[10]
+||	 SWAP2	@Y[11],@Y[11]
+||	 SWAP4	@Y[2],@Y[3]
+||	 SWAP4	@Y[3],@Y[2]
+	 SWAP2	@Y[12],@Y[12]
+||	 SWAP2	@Y[13],@Y[13]
+||	 SWAP4	@Y[4],@Y[5]
+||	 SWAP4	@Y[5],@Y[4]
+	 SWAP2	@Y[14],@Y[14]
+||	 SWAP2	@Y[15],@Y[15]
+||	 SWAP4	@Y[6],@Y[7]
+||	 SWAP4	@Y[7],@Y[6]
+	 SWAP4	@Y[8],@Y[9]
+||	 SWAP4	@Y[9],@Y[8]
+	 SWAP4	@Y[10],@Y[11]
+||	 SWAP4	@Y[11],@Y[10]
+	 SWAP4	@Y[12],@Y[13]
+||	 SWAP4	@Y[13],@Y[12]
+	 SWAP4	@Y[14],@Y[15]
+||	 SWAP4	@Y[15],@Y[14]
+	.endif
+
+	XOR	@DAT[0],@X[0],@X[0]	; xor 1st block
+||	XOR	@DAT[3],@X[3],@X[3]
+||	XOR	@DAT[2],@X[2],@X[1]
+||	XOR	@DAT[1],@X[1],@X[2]
+||	LDNDW	*${INP}++[8],@DAT[1]:@DAT[0]
+	XOR	@DAT[4],@X[4],@X[4]
+||	XOR	@DAT[7],@X[7],@X[7]
+||	LDNDW	*${INP}[-7],@DAT[3]:@DAT[2]
+	XOR	@DAT[6],@X[6],@X[5]
+||	XOR	@DAT[5],@X[5],@X[6]
+||	LDNDW	*${INP}[-6],@DAT[5]:@DAT[4]
+	XOR	@DAT[8],@X[8],@X[8]
+||	XOR	@DAT[11],@X[11],@X[11]
+||	LDNDW	*${INP}[-5],@DAT[7]:@DAT[6]
+	XOR	@DAT[10],@X[10],@X[9]
+||	XOR	@DAT[9],@X[9],@X[10]
+||	LDNDW	*${INP}[-4],@DAT[9]:@DAT[8]
+	XOR	@DAT[12],@X[12],@X[12]
+||	XOR	@DAT[15],@X[15],@X[15]
+||	LDNDW	*${INP}[-3],@DAT[11]:@DAT[10]
+	XOR	@DAT[14],@X[14],@X[13]
+||	XOR	@DAT[13],@X[13],@X[14]
+||	LDNDW	*${INP}[-2],@DAT[13]:@DAT[12]
+   [A0]	SUB	A0,$STEP,A0		; SUB	A0,128,A0
+||	LDNDW	*${INP}[-1],@DAT[15]:@DAT[14]
+
+	XOR	@Y[0],@DAT[0],@DAT[0]	; xor 2nd block
+||	XOR	@Y[1],@DAT[1],@DAT[1]
+||	STNDW	@X[2]:@X[0],*${OUT}++[8]
+	XOR	@Y[2],@DAT[2],@DAT[2]
+||	XOR	@Y[3],@DAT[3],@DAT[3]
+||	STNDW	@X[3]:@X[1],*${OUT}[-7]
+	XOR	@Y[4],@DAT[4],@DAT[4]
+|| [A0]	LDDW	*FP[-12],@X[2]:@X[0]	; re-load key material from stack
+|| [A0]	LDDW	*SP[2],  @X[3]:@X[1]
+	XOR	@Y[5],@DAT[5],@DAT[5]
+||	STNDW	@X[6]:@X[4],*${OUT}[-6]
+	XOR	@Y[6],@DAT[6],@DAT[6]
+||	XOR	@Y[7],@DAT[7],@DAT[7]
+||	STNDW	@X[7]:@X[5],*${OUT}[-5]
+	XOR	@Y[8],@DAT[8],@DAT[8]
+|| [A0]	LDDW	*FP[-10],@X[6]:@X[4]
+|| [A0]	LDDW	*SP[4],  @X[7]:@X[5]
+	XOR	@Y[9],@DAT[9],@DAT[9]
+||	STNDW	@X[10]:@X[8],*${OUT}[-4]
+	XOR	@Y[10],@DAT[10],@DAT[10]
+||	XOR	@Y[11],@DAT[11],@DAT[11]
+||	STNDW	@X[11]:@X[9],*${OUT}[-3]
+	XOR	@Y[12],@DAT[12],@DAT[12]
+|| [A0]	LDDW	*FP[-8], @X[10]:@X[8]
+|| [A0]	LDDW	*SP[6],  @X[11]:@X[9]
+	XOR	@Y[13],@DAT[13],@DAT[13]
+||	STNDW	@X[14]:@X[12],*${OUT}[-2]
+	XOR	@Y[14],@DAT[14],@DAT[14]
+||	XOR	@Y[15],@DAT[15],@DAT[15]
+||	STNDW	@X[15]:@X[13],*${OUT}[-1]
+
+   [A0]	MV	@K2x[12],@X[12]
+|| [A0]	MV	@K2x[13],@X[13]
+|| [A0]	LDW	*FP[-6*2], @X[14]
+|| [A0]	LDW	*SP[8*2],  @X[15]
+
+   [A0]	DMV	@X[2],@X[0],@Y[2]:@Y[0]	; duplicate key material
+||	STNDW	@DAT[1]:@DAT[0],*${OUT}++[8]
+   [A0]	DMV	@X[3],@X[1],@Y[3]:@Y[1]
+||	STNDW	@DAT[3]:@DAT[2],*${OUT}[-7]
+   [A0]	DMV	@X[6],@X[4],@Y[6]:@Y[4]
+||	STNDW	@DAT[5]:@DAT[4],*${OUT}[-6]
+||	CMPLTU	A0,$STEP,A1		; is remaining length < 2*blocks?
+||[!A0]	BNOP	epilogue?
+   [A0]	DMV	@X[7],@X[5],@Y[7]:@Y[5]
+||	STNDW	@DAT[7]:@DAT[6],*${OUT}[-5]
+||[!A1]	BNOP	outer2x?
+   [A0]	DMV	@X[10],@X[8],@Y[10]:@Y[8]
+||	STNDW	@DAT[9]:@DAT[8],*${OUT}[-4]
+   [A0]	DMV	@X[11],@X[9],@Y[11]:@Y[9]
+||	STNDW	@DAT[11]:@DAT[10],*${OUT}[-3]
+   [A0]	DMV	@X[14],@X[12],@Y[14]:@Y[12]
+||	STNDW	@DAT[13]:@DAT[12],*${OUT}[-2]
+   [A0]	DMV	@X[15],@X[13],@Y[15]:@Y[13]
+||	STNDW	@DAT[15]:@DAT[14],*${OUT}[-1]
+;;===== branch to epilogue? is taken here
+   [A1]	MVK	64,$STEP
+|| [A0]	MVK	10,B0			; inner loop counter
+;;===== branch to outer2x? is taken here
+___
+{
+my ($a0,$a1,$a2,$a3) = (0..3);
+my ($b0,$b1,$b2,$b3) = (4..7);
+my ($c0,$c1,$c2,$c3) = (8..11);
+my ($d0,$d1,$d2,$d3) = (12..15);
+
+$code.=<<___;
+top1x?:
+	ADD	@X[$b1],@X[$a1],@X[$a1]
+||	ADD	@X[$b2],@X[$a2],@X[$a2]
+	ADD	@X[$b0],@X[$a0],@X[$a0]
+||	ADD	@X[$b3],@X[$a3],@X[$a3]
+||	XOR	@X[$a1],@X[$d1],@X[$d1]
+||	XOR	@X[$a2],@X[$d2],@X[$d2]
+	XOR	@X[$a0],@X[$d0],@X[$d0]
+||	XOR	@X[$a3],@X[$d3],@X[$d3]
+||	SWAP2	@X[$d1],@X[$d1]		; rotate by 16
+||	SWAP2	@X[$d2],@X[$d2]
+	SWAP2	@X[$d0],@X[$d0]
+||	SWAP2	@X[$d3],@X[$d3]
+
+||	ADD	@X[$d1],@X[$c1],@X[$c1]
+||	ADD	@X[$d2],@X[$c2],@X[$c2]
+	ADD	@X[$d0],@X[$c0],@X[$c0]
+||	ADD	@X[$d3],@X[$c3],@X[$c3]
+||	XOR	@X[$c1],@X[$b1],@X[$b1]
+||	XOR	@X[$c2],@X[$b2],@X[$b2]
+	XOR	@X[$c0],@X[$b0],@X[$b0]
+||	XOR	@X[$c3],@X[$b3],@X[$b3]
+||	ROTL	@X[$b1],12,@X[$b1]
+||	ROTL	@X[$b2],12,@X[$b2]
+	ROTL	@X[$b0],12,@X[$b0]
+||	ROTL	@X[$b3],12,@X[$b3]
+
+	ADD	@X[$b1],@X[$a1],@X[$a1]
+||	ADD	@X[$b2],@X[$a2],@X[$a2]
+	ADD	@X[$b0],@X[$a0],@X[$a0]
+||	ADD	@X[$b3],@X[$a3],@X[$a3]
+||	XOR	@X[$a1],@X[$d1],@X[$d1]
+||	XOR	@X[$a2],@X[$d2],@X[$d2]
+	XOR	@X[$a0],@X[$d0],@X[$d0]
+||	XOR	@X[$a3],@X[$d3],@X[$d3]
+||	ROTL	@X[$d1],8,@X[$d1]
+||	ROTL	@X[$d2],8,@X[$d2]
+	ROTL	@X[$d0],8,@X[$d0]
+||	ROTL	@X[$d3],8,@X[$d3]
+||	BNOP	middle1x?		; protect from interrupt
+
+	ADD	@X[$d1],@X[$c1],@X[$c1]
+||	ADD	@X[$d2],@X[$c2],@X[$c2]
+	ADD	@X[$d0],@X[$c0],@X[$c0]
+||	ADD	@X[$d3],@X[$c3],@X[$c3]
+||	XOR	@X[$c1],@X[$b1],@X[$b1]
+||	XOR	@X[$c2],@X[$b2],@X[$b2]
+||	ROTL	@X[$d1],0,@X[$d2]	; moved to avoid cross-path stall
+||	ROTL	@X[$d2],0,@X[$d3]
+	XOR	@X[$c0],@X[$b0],@X[$b0]
+||	XOR	@X[$c3],@X[$b3],@X[$b3]
+||	ROTL	@X[$d0],0,@X[$d1]
+||	ROTL	@X[$d3],0,@X[$d0]
+	ROTL	@X[$b1],7,@X[$b0]	; avoided cross-path stall
+||	ROTL	@X[$b2],7,@X[$b1]
+	ROTL	@X[$b0],7,@X[$b3]
+||	ROTL	@X[$b3],7,@X[$b2]
+middle1x?:
+
+	ADD	@X[$b0],@X[$a0],@X[$a0]
+||	ADD	@X[$b1],@X[$a1],@X[$a1]
+	ADD	@X[$b2],@X[$a2],@X[$a2]
+||	ADD	@X[$b3],@X[$a3],@X[$a3]
+||	XOR	@X[$a0],@X[$d0],@X[$d0]
+||	XOR	@X[$a1],@X[$d1],@X[$d1]
+	XOR	@X[$a2],@X[$d2],@X[$d2]
+||	XOR	@X[$a3],@X[$d3],@X[$d3]
+||	SWAP2	@X[$d0],@X[$d0]		; rotate by 16
+||	SWAP2	@X[$d1],@X[$d1]
+	SWAP2	@X[$d2],@X[$d2]
+||	SWAP2	@X[$d3],@X[$d3]
+
+||	ADD	@X[$d0],@X[$c2],@X[$c2]
+||	ADD	@X[$d1],@X[$c3],@X[$c3]
+	ADD	@X[$d2],@X[$c0],@X[$c0]
+||	ADD	@X[$d3],@X[$c1],@X[$c1]
+||	XOR	@X[$c2],@X[$b0],@X[$b0]
+||	XOR	@X[$c3],@X[$b1],@X[$b1]
+	XOR	@X[$c0],@X[$b2],@X[$b2]
+||	XOR	@X[$c1],@X[$b3],@X[$b3]
+||	ROTL	@X[$b0],12,@X[$b0]
+||	ROTL	@X[$b1],12,@X[$b1]
+	ROTL	@X[$b2],12,@X[$b2]
+||	ROTL	@X[$b3],12,@X[$b3]
+
+	ADD	@X[$b0],@X[$a0],@X[$a0]
+||	ADD	@X[$b1],@X[$a1],@X[$a1]
+|| [B0]	SUB	B0,1,B0			; decrement inner loop counter
+	ADD	@X[$b2],@X[$a2],@X[$a2]
+||	ADD	@X[$b3],@X[$a3],@X[$a3]
+||	XOR	@X[$a0],@X[$d0],@X[$d0]
+||	XOR	@X[$a1],@X[$d1],@X[$d1]
+	XOR	@X[$a2],@X[$d2],@X[$d2]
+||	XOR	@X[$a3],@X[$d3],@X[$d3]
+||	ROTL	@X[$d0],8,@X[$d0]
+||	ROTL	@X[$d1],8,@X[$d1]
+	ROTL	@X[$d2],8,@X[$d2]
+||	ROTL	@X[$d3],8,@X[$d3]
+|| [B0]	BNOP	top1x?			; even protects from interrupt
+
+	ADD	@X[$d0],@X[$c2],@X[$c2]
+||	ADD	@X[$d1],@X[$c3],@X[$c3]
+	ADD	@X[$d2],@X[$c0],@X[$c0]
+||	ADD	@X[$d3],@X[$c1],@X[$c1]
+||	XOR	@X[$c2],@X[$b0],@X[$b0]
+||	XOR	@X[$c3],@X[$b1],@X[$b1]
+||	ROTL	@X[$d0],0,@X[$d3]	; moved to avoid cross-path stall
+||	ROTL	@X[$d1],0,@X[$d0]
+	XOR	@X[$c0],@X[$b2],@X[$b2]
+||	XOR	@X[$c1],@X[$b3],@X[$b3]
+||	ROTL	@X[$d2],0,@X[$d1]
+||	ROTL	@X[$d3],0,@X[$d2]
+	ROTL	@X[$b0],7,@X[$b1]	; avoided cross-path stall
+||	ROTL	@X[$b1],7,@X[$b2]
+	ROTL	@X[$b2],7,@X[$b3]
+||	ROTL	@X[$b3],7,@X[$b0]
+||[!B0]	CMPLTU	A0,$STEP,A1		; less than 64 bytes left?
+bottom1x?:
+___
+}
+
+$code.=<<___;
+	ADD	@Y[0],@X[0],@X[0]	; accumulate key material
+||	ADD	@Y[1],@X[1],@X[1]
+||	ADD	@Y[2],@X[2],@X[2]
+||	ADD	@Y[3],@X[3],@X[3]
+||[!A1]	LDNDW	*${INP}++[8],@DAT[1]:@DAT[0]
+|| [A1]	BNOP	tail?
+	ADD	@Y[4],@X[4],@X[4]
+||	ADD	@Y[5],@X[5],@X[5]
+||	ADD	@Y[6],@X[6],@X[6]
+||	ADD	@Y[7],@X[7],@X[7]
+||[!A1]	LDNDW	*${INP}[-7],@DAT[3]:@DAT[2]
+	ADD	@Y[8],@X[8],@X[8]
+||	ADD	@Y[9],@X[9],@X[9]
+||	ADD	@Y[10],@X[10],@X[10]
+||	ADD	@Y[11],@X[11],@X[11]
+||[!A1]	LDNDW	*${INP}[-6],@DAT[5]:@DAT[4]
+	ADD	@Y[12],@X[12],@X[12]
+||	ADD	@Y[13],@X[13],@X[13]
+||	ADD	@Y[14],@X[14],@X[14]
+||	ADD	@Y[15],@X[15],@X[15]
+||[!A1]	LDNDW	*${INP}[-5],@DAT[7]:@DAT[6]
+  [!A1]	LDNDW	*${INP}[-4],@DAT[9]:@DAT[8]
+  [!A1]	LDNDW	*${INP}[-3],@DAT[11]:@DAT[10]
+	LDNDW	*${INP}[-2],@DAT[13]:@DAT[12]
+	LDNDW	*${INP}[-1],@DAT[15]:@DAT[14]
+
+	.if	.BIG_ENDIAN
+	SWAP2	@X[0],@X[0]
+||	SWAP2	@X[1],@X[1]
+||	SWAP2	@X[2],@X[2]
+||	SWAP2	@X[3],@X[3]
+	SWAP2	@X[4],@X[4]
+||	SWAP2	@X[5],@X[5]
+||	SWAP2	@X[6],@X[6]
+||	SWAP2	@X[7],@X[7]
+	SWAP2	@X[8],@X[8]
+||	SWAP2	@X[9],@X[9]
+||	SWAP4	@X[0],@X[1]
+||	SWAP4	@X[1],@X[0]
+	SWAP2	@X[10],@X[10]
+||	SWAP2	@X[11],@X[11]
+||	SWAP4	@X[2],@X[3]
+||	SWAP4	@X[3],@X[2]
+	SWAP2	@X[12],@X[12]
+||	SWAP2	@X[13],@X[13]
+||	SWAP4	@X[4],@X[5]
+||	SWAP4	@X[5],@X[4]
+	SWAP2	@X[14],@X[14]
+||	SWAP2	@X[15],@X[15]
+||	SWAP4	@X[6],@X[7]
+||	SWAP4	@X[7],@X[6]
+	SWAP4	@X[8],@X[9]
+||	SWAP4	@X[9],@X[8]
+	SWAP4	@X[10],@X[11]
+||	SWAP4	@X[11],@X[10]
+	SWAP4	@X[12],@X[13]
+||	SWAP4	@X[13],@X[12]
+	SWAP4	@X[14],@X[15]
+||	SWAP4	@X[15],@X[14]
+	.else
+	NOP	1
+	.endif
+
+	XOR	@X[0],@DAT[0],@DAT[0]	; xor with input
+||	XOR	@X[1],@DAT[1],@DAT[1]
+||	XOR	@X[2],@DAT[2],@DAT[2]
+||	XOR	@X[3],@DAT[3],@DAT[3]
+|| [A0]	SUB	A0,$STEP,A0		; SUB	A0,64,A0
+	XOR	@X[4],@DAT[4],@DAT[4]
+||	XOR	@X[5],@DAT[5],@DAT[5]
+||	XOR	@X[6],@DAT[6],@DAT[6]
+||	XOR	@X[7],@DAT[7],@DAT[7]
+||	STNDW	@DAT[1]:@DAT[0],*${OUT}++[8]
+	XOR	@X[8],@DAT[8],@DAT[8]
+||	XOR	@X[9],@DAT[9],@DAT[9]
+||	XOR	@X[10],@DAT[10],@DAT[10]
+||	XOR	@X[11],@DAT[11],@DAT[11]
+||	STNDW	@DAT[3]:@DAT[2],*${OUT}[-7]
+	XOR	@X[12],@DAT[12],@DAT[12]
+||	XOR	@X[13],@DAT[13],@DAT[13]
+||	XOR	@X[14],@DAT[14],@DAT[14]
+||	XOR	@X[15],@DAT[15],@DAT[15]
+||	STNDW	@DAT[5]:@DAT[4],*${OUT}[-6]
+|| [A0]	BNOP	top1x?
+   [A0]	DMV	@Y[2],@Y[0],@X[2]:@X[0]	; duplicate key material
+|| [A0]	DMV	@Y[3],@Y[1],@X[3]:@X[1]
+||	STNDW	@DAT[7]:@DAT[6],*${OUT}[-5]
+   [A0]	DMV	@Y[6],@Y[4],@X[6]:@X[4]
+|| [A0]	DMV	@Y[7],@Y[5],@X[7]:@X[5]
+||	STNDW	@DAT[9]:@DAT[8],*${OUT}[-4]
+   [A0]	DMV	@Y[10],@Y[8],@X[10]:@X[8]
+|| [A0]	DMV	@Y[11],@Y[9],@X[11]:@X[9]
+|| [A0]	ADD	1,@Y[12],@Y[12]		; increment counter
+||	STNDW	@DAT[11]:@DAT[10],*${OUT}[-3]
+   [A0]	DMV	@Y[14],@Y[12],@X[14]:@X[12]
+|| [A0]	DMV	@Y[15],@Y[13],@X[15]:@X[13]
+||	STNDW	@DAT[13]:@DAT[12],*${OUT}[-2]
+   [A0]	MVK	10,B0			; inner loop counter
+||	STNDW	@DAT[15]:@DAT[14],*${OUT}[-1]
+;;===== branch to top1x? is taken here
+
+epilogue?:
+	LDDW	*FP[-4],A11:A10		; ABI says so
+	LDDW	*FP[-3],A13:A12
+||	LDDW	*SP[3+8],B11:B10
+	LDDW	*SP[4+8],B13:B12
+||	BNOP	RA
+	LDW	*++SP(40+64),FP		; restore frame pointer
+	NOP	4
+
+tail?:
+	LDBU	*${INP}++[1],B24	; load byte by byte
+||	SUB	A0,1,A0
+||	SUB	A0,1,B1
+  [!B1]	BNOP	epilogue?		; interrupts are disabled for whole time
+|| [A0] LDBU	*${INP}++[1],B24
+|| [A0]	SUB	A0,1,A0
+||	SUB	B1,1,B1
+  [!B1]	BNOP	epilogue?
+|| [A0] LDBU	*${INP}++[1],B24
+|| [A0]	SUB	A0,1,A0
+||	SUB	B1,1,B1
+  [!B1]	BNOP	epilogue?
+||	ROTL	@X[0],0,A24
+|| [A0] LDBU	*${INP}++[1],B24
+|| [A0]	SUB	A0,1,A0
+||	SUB	B1,1,B1
+  [!B1]	BNOP	epilogue?
+||	ROTL	@X[0],24,A24
+|| [A0] LDBU	*${INP}++[1],A24
+|| [A0]	SUB	A0,1,A0
+||	SUB	B1,1,B1
+  [!B1]	BNOP	epilogue?
+||	ROTL	@X[0],16,A24
+|| [A0] LDBU	*${INP}++[1],A24
+|| [A0]	SUB	A0,1,A0
+||	SUB	B1,1,B1
+||	XOR	A24,B24,B25
+	STB	B25,*${OUT}++[1]	; store byte by byte
+||[!B1]	BNOP	epilogue?
+||	ROTL	@X[0],8,A24
+|| [A0] LDBU	*${INP}++[1],A24
+|| [A0]	SUB	A0,1,A0
+||	SUB	B1,1,B1
+||	XOR	A24,B24,B25
+	STB	B25,*${OUT}++[1]
+___
+sub TAIL_STEP {
+my $Xi= shift;
+my $T = ($Xi=~/^B/?"B24":"A24");	# match @X[i] to avoid cross path
+my $D = $T; $D=~tr/AB/BA/;
+my $O = $D; $O=~s/24/25/;
+
+$code.=<<___;
+||[!B1]	BNOP	epilogue?
+||	ROTL	$Xi,0,$T
+|| [A0] LDBU	*${INP}++[1],$D
+|| [A0]	SUB	A0,1,A0
+||	SUB	B1,1,B1
+||	XOR	A24,B24,$O
+	STB	$O,*${OUT}++[1]
+||[!B1]	BNOP	epilogue?
+||	ROTL	$Xi,24,$T
+|| [A0] LDBU	*${INP}++[1],$T
+|| [A0]	SUB	A0,1,A0
+||	SUB	B1,1,B1
+||	XOR	A24,B24,$O
+	STB	$O,*${OUT}++[1]
+||[!B1]	BNOP	epilogue?
+||	ROTL	$Xi,16,$T
+|| [A0] LDBU	*${INP}++[1],$T
+|| [A0]	SUB	A0,1,A0
+||	SUB	B1,1,B1
+||	XOR	A24,B24,$O
+	STB	$O,*${OUT}++[1]
+||[!B1]	BNOP	epilogue?
+||	ROTL	$Xi,8,$T
+|| [A0] LDBU	*${INP}++[1],$T
+|| [A0]	SUB	A0,1,A0
+||	SUB	B1,1,B1
+||	XOR	A24,B24,$O
+	STB	$O,*${OUT}++[1]
+___
+}
+	foreach (1..14) { TAIL_STEP(@X[$_]); }
+$code.=<<___;
+||[!B1]	BNOP	epilogue?
+||	ROTL	@X[15],0,B24
+||	XOR	A24,B24,A25
+	STB	A25,*${OUT}++[1]
+||	ROTL	@X[15],24,B24
+||	XOR	A24,B24,A25
+	STB	A25,*${OUT}++[1]
+||	ROTL	@X[15],16,B24
+||	XOR	A24,B24,A25
+	STB	A25,*${OUT}++[1]
+||	XOR	A24,B24,A25
+	STB	A25,*${OUT}++[1]
+||	XOR	A24,B24,B25
+	STB	B25,*${OUT}++[1]
+	.endasmfunc
+
+	.sect	.const
+	.cstring "ChaCha20 for C64x+, CRYPTOGAMS by "
+	.align	4
+___
+
+print $code;
+close STDOUT;
diff --git a/crypto/chacha/asm/chacha-ppc.pl b/crypto/chacha/asm/chacha-ppc.pl
new file mode 100755
index 000000000..181decdad
--- /dev/null
+++ b/crypto/chacha/asm/chacha-ppc.pl
@@ -0,0 +1,953 @@
+#! /usr/bin/env perl
+# Copyright 2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+#
+# ====================================================================
+# Written by Andy Polyakov  for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+#
+# October 2015
+# 
+# ChaCha20 for PowerPC/AltiVec.
+#
+# Performance in cycles per byte out of large buffer.
+#
+#			IALU/gcc-4.x    3xAltiVec+1xIALU
+#
+# Freescale e300	13.6/+115%	-
+# PPC74x0/G4e		6.81/+310%	4.66
+# PPC970/G5		9.29/+160%	4.60
+# POWER7		8.62/+61%	4.27
+# POWER8		8.70/+51%	3.96
+
+$flavour = shift;
+
+if ($flavour =~ /64/) {
+	$SIZE_T	=8;
+	$LRSAVE	=2*$SIZE_T;
+	$STU	="stdu";
+	$POP	="ld";
+	$PUSH	="std";
+	$UCMP	="cmpld";
+} elsif ($flavour =~ /32/) {
+	$SIZE_T	=4;
+	$LRSAVE	=$SIZE_T;
+	$STU	="stwu";
+	$POP	="lwz";
+	$PUSH	="stw";
+	$UCMP	="cmplw";
+} else { die "nonsense $flavour"; }
+
+$LITTLE_ENDIAN = ($flavour=~/le$/) ? 1 : 0;
+
+$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+( $xlate="${dir}ppc-xlate.pl" and -f $xlate ) or
+( $xlate="${dir}../../perlasm/ppc-xlate.pl" and -f $xlate) or
+die "can't locate ppc-xlate.pl";
+
+open STDOUT,"| $^X $xlate $flavour ".shift || die "can't call $xlate: $!";
+
+$LOCALS=6*$SIZE_T;
+$FRAME=$LOCALS+64+18*$SIZE_T;	# 64 is for local variables
+
+sub AUTOLOAD()		# thunk [simplified] x86-style perlasm
+{ my $opcode = $AUTOLOAD; $opcode =~ s/.*:://; $opcode =~ s/_/\./;
+    $code .= "\t$opcode\t".join(',',@_)."\n";
+}
+
+my $sp = "r1";
+
+my ($out,$inp,$len,$key,$ctr) = map("r$_",(3..7));
+
+my @x=map("r$_",(16..31));
+my @d=map("r$_",(11,12,14,15));
+my @t=map("r$_",(7..10));
+
+sub ROUND {
+my ($a0,$b0,$c0,$d0)=@_;
+my ($a1,$b1,$c1,$d1)=map(($_&~3)+(($_+1)&3),($a0,$b0,$c0,$d0));
+my ($a2,$b2,$c2,$d2)=map(($_&~3)+(($_+1)&3),($a1,$b1,$c1,$d1));
+my ($a3,$b3,$c3,$d3)=map(($_&~3)+(($_+1)&3),($a2,$b2,$c2,$d2));
+
+    (
+	"&add		(@x[$a0],@x[$a0],@x[$b0])",
+	 "&add		(@x[$a1],@x[$a1],@x[$b1])",
+	  "&add		(@x[$a2],@x[$a2],@x[$b2])",
+	   "&add	(@x[$a3],@x[$a3],@x[$b3])",
+	"&xor		(@x[$d0],@x[$d0],@x[$a0])",
+	 "&xor		(@x[$d1],@x[$d1],@x[$a1])",
+	  "&xor		(@x[$d2],@x[$d2],@x[$a2])",
+	   "&xor	(@x[$d3],@x[$d3],@x[$a3])",
+	"&rotlwi	(@x[$d0],@x[$d0],16)",
+	 "&rotlwi	(@x[$d1],@x[$d1],16)",
+	  "&rotlwi	(@x[$d2],@x[$d2],16)",
+	   "&rotlwi	(@x[$d3],@x[$d3],16)",
+
+	"&add		(@x[$c0],@x[$c0],@x[$d0])",
+	 "&add		(@x[$c1],@x[$c1],@x[$d1])",
+	  "&add		(@x[$c2],@x[$c2],@x[$d2])",
+	   "&add	(@x[$c3],@x[$c3],@x[$d3])",
+	"&xor		(@x[$b0],@x[$b0],@x[$c0])",
+	 "&xor		(@x[$b1],@x[$b1],@x[$c1])",
+	  "&xor		(@x[$b2],@x[$b2],@x[$c2])",
+	   "&xor	(@x[$b3],@x[$b3],@x[$c3])",
+	"&rotlwi	(@x[$b0],@x[$b0],12)",
+	 "&rotlwi	(@x[$b1],@x[$b1],12)",
+	  "&rotlwi	(@x[$b2],@x[$b2],12)",
+	   "&rotlwi	(@x[$b3],@x[$b3],12)",
+
+	"&add		(@x[$a0],@x[$a0],@x[$b0])",
+	 "&add		(@x[$a1],@x[$a1],@x[$b1])",
+	  "&add		(@x[$a2],@x[$a2],@x[$b2])",
+	   "&add	(@x[$a3],@x[$a3],@x[$b3])",
+	"&xor		(@x[$d0],@x[$d0],@x[$a0])",
+	 "&xor		(@x[$d1],@x[$d1],@x[$a1])",
+	  "&xor		(@x[$d2],@x[$d2],@x[$a2])",
+	   "&xor	(@x[$d3],@x[$d3],@x[$a3])",
+	"&rotlwi	(@x[$d0],@x[$d0],8)",
+	 "&rotlwi	(@x[$d1],@x[$d1],8)",
+	  "&rotlwi	(@x[$d2],@x[$d2],8)",
+	   "&rotlwi	(@x[$d3],@x[$d3],8)",
+
+	"&add		(@x[$c0],@x[$c0],@x[$d0])",
+	 "&add		(@x[$c1],@x[$c1],@x[$d1])",
+	  "&add		(@x[$c2],@x[$c2],@x[$d2])",
+	   "&add	(@x[$c3],@x[$c3],@x[$d3])",
+	"&xor		(@x[$b0],@x[$b0],@x[$c0])",
+	 "&xor		(@x[$b1],@x[$b1],@x[$c1])",
+	  "&xor		(@x[$b2],@x[$b2],@x[$c2])",
+	   "&xor	(@x[$b3],@x[$b3],@x[$c3])",
+	"&rotlwi	(@x[$b0],@x[$b0],7)",
+	 "&rotlwi	(@x[$b1],@x[$b1],7)",
+	  "&rotlwi	(@x[$b2],@x[$b2],7)",
+	   "&rotlwi	(@x[$b3],@x[$b3],7)"
+    );
+}
+
+$code.=<<___;
+.machine	"any"
+.text
+
+.globl	.ChaCha20_ctr32_int
+.align	5
+.ChaCha20_ctr32_int:
+__ChaCha20_ctr32_int:
+	${UCMP}i $len,0
+	beqlr-
+
+	$STU	$sp,-$FRAME($sp)
+	mflr	r0
+
+	$PUSH	r14,`$FRAME-$SIZE_T*18`($sp)
+	$PUSH	r15,`$FRAME-$SIZE_T*17`($sp)
+	$PUSH	r16,`$FRAME-$SIZE_T*16`($sp)
+	$PUSH	r17,`$FRAME-$SIZE_T*15`($sp)
+	$PUSH	r18,`$FRAME-$SIZE_T*14`($sp)
+	$PUSH	r19,`$FRAME-$SIZE_T*13`($sp)
+	$PUSH	r20,`$FRAME-$SIZE_T*12`($sp)
+	$PUSH	r21,`$FRAME-$SIZE_T*11`($sp)
+	$PUSH	r22,`$FRAME-$SIZE_T*10`($sp)
+	$PUSH	r23,`$FRAME-$SIZE_T*9`($sp)
+	$PUSH	r24,`$FRAME-$SIZE_T*8`($sp)
+	$PUSH	r25,`$FRAME-$SIZE_T*7`($sp)
+	$PUSH	r26,`$FRAME-$SIZE_T*6`($sp)
+	$PUSH	r27,`$FRAME-$SIZE_T*5`($sp)
+	$PUSH	r28,`$FRAME-$SIZE_T*4`($sp)
+	$PUSH	r29,`$FRAME-$SIZE_T*3`($sp)
+	$PUSH	r30,`$FRAME-$SIZE_T*2`($sp)
+	$PUSH	r31,`$FRAME-$SIZE_T*1`($sp)
+	$PUSH	r0,`$FRAME+$LRSAVE`($sp)
+
+	lwz	@d[0],0($ctr)			# load counter
+	lwz	@d[1],4($ctr)
+	lwz	@d[2],8($ctr)
+	lwz	@d[3],12($ctr)
+
+	bl	__ChaCha20_1x
+
+	$POP	r0,`$FRAME+$LRSAVE`($sp)
+	$POP	r14,`$FRAME-$SIZE_T*18`($sp)
+	$POP	r15,`$FRAME-$SIZE_T*17`($sp)
+	$POP	r16,`$FRAME-$SIZE_T*16`($sp)
+	$POP	r17,`$FRAME-$SIZE_T*15`($sp)
+	$POP	r18,`$FRAME-$SIZE_T*14`($sp)
+	$POP	r19,`$FRAME-$SIZE_T*13`($sp)
+	$POP	r20,`$FRAME-$SIZE_T*12`($sp)
+	$POP	r21,`$FRAME-$SIZE_T*11`($sp)
+	$POP	r22,`$FRAME-$SIZE_T*10`($sp)
+	$POP	r23,`$FRAME-$SIZE_T*9`($sp)
+	$POP	r24,`$FRAME-$SIZE_T*8`($sp)
+	$POP	r25,`$FRAME-$SIZE_T*7`($sp)
+	$POP	r26,`$FRAME-$SIZE_T*6`($sp)
+	$POP	r27,`$FRAME-$SIZE_T*5`($sp)
+	$POP	r28,`$FRAME-$SIZE_T*4`($sp)
+	$POP	r29,`$FRAME-$SIZE_T*3`($sp)
+	$POP	r30,`$FRAME-$SIZE_T*2`($sp)
+	$POP	r31,`$FRAME-$SIZE_T*1`($sp)
+	mtlr	r0
+	addi	$sp,$sp,$FRAME
+	blr
+	.long	0
+	.byte	0,12,4,1,0x80,18,5,0
+	.long	0
+.size	.ChaCha20_ctr32_int,.-.ChaCha20_ctr32_int
+
+.align	5
+__ChaCha20_1x:
+Loop_outer:
+	lis	@x[0],0x6170			# synthesize sigma
+	lis	@x[1],0x3320
+	lis	@x[2],0x7962
+	lis	@x[3],0x6b20
+	ori	@x[0],@x[0],0x7865
+	ori	@x[1],@x[1],0x646e
+	ori	@x[2],@x[2],0x2d32
+	ori	@x[3],@x[3],0x6574
+
+	li	r0,10				# inner loop counter
+	lwz	@x[4],0($key)			# load key
+	lwz	@x[5],4($key)
+	lwz	@x[6],8($key)
+	lwz	@x[7],12($key)
+	lwz	@x[8],16($key)
+	mr	@x[12],@d[0]			# copy counter
+	lwz	@x[9],20($key)
+	mr	@x[13],@d[1]
+	lwz	@x[10],24($key)
+	mr	@x[14],@d[2]
+	lwz	@x[11],28($key)
+	mr	@x[15],@d[3]
+
+	mr	@t[0],@x[4]
+	mr	@t[1],@x[5]
+	mr	@t[2],@x[6]
+	mr	@t[3],@x[7]
+
+	mtctr	r0
+Loop:
+___
+	foreach (&ROUND(0, 4, 8,12)) { eval; }
+	foreach (&ROUND(0, 5,10,15)) { eval; }
+$code.=<<___;
+	bdnz	Loop
+
+	subic	$len,$len,64			# $len-=64
+	addi	@x[0],@x[0],0x7865		# accumulate key block
+	addi	@x[1],@x[1],0x646e
+	addi	@x[2],@x[2],0x2d32
+	addi	@x[3],@x[3],0x6574
+	addis	@x[0],@x[0],0x6170
+	addis	@x[1],@x[1],0x3320
+	addis	@x[2],@x[2],0x7962
+	addis	@x[3],@x[3],0x6b20
+
+	subfe.	r0,r0,r0			# borrow?-1:0
+	add	@x[4],@x[4],@t[0]
+	lwz	@t[0],16($key)
+	add	@x[5],@x[5],@t[1]
+	lwz	@t[1],20($key)
+	add	@x[6],@x[6],@t[2]
+	lwz	@t[2],24($key)
+	add	@x[7],@x[7],@t[3]
+	lwz	@t[3],28($key)
+	add	@x[8],@x[8],@t[0]
+	add	@x[9],@x[9],@t[1]
+	add	@x[10],@x[10],@t[2]
+	add	@x[11],@x[11],@t[3]
+
+	add	@x[12],@x[12],@d[0]
+	add	@x[13],@x[13],@d[1]
+	add	@x[14],@x[14],@d[2]
+	add	@x[15],@x[15],@d[3]
+	addi	@d[0],@d[0],1			# increment counter
+___
+if (!$LITTLE_ENDIAN) { for($i=0;$i<16;$i++) {	# flip byte order
+$code.=<<___;
+	mr	@t[$i&3],@x[$i]
+	rotlwi	@x[$i],@x[$i],8
+	rlwimi	@x[$i],@t[$i&3],24,0,7
+	rlwimi	@x[$i],@t[$i&3],24,16,23
+___
+} }
+$code.=<<___;
+	bne	Ltail				# $len-=64 borrowed
+
+	lwz	@t[0],0($inp)			# load input, aligned or not
+	lwz	@t[1],4($inp)
+	${UCMP}i $len,0				# done already?
+	lwz	@t[2],8($inp)
+	lwz	@t[3],12($inp)
+	xor	@x[0],@x[0],@t[0]		# xor with input
+	lwz	@t[0],16($inp)
+	xor	@x[1],@x[1],@t[1]
+	lwz	@t[1],20($inp)
+	xor	@x[2],@x[2],@t[2]
+	lwz	@t[2],24($inp)
+	xor	@x[3],@x[3],@t[3]
+	lwz	@t[3],28($inp)
+	xor	@x[4],@x[4],@t[0]
+	lwz	@t[0],32($inp)
+	xor	@x[5],@x[5],@t[1]
+	lwz	@t[1],36($inp)
+	xor	@x[6],@x[6],@t[2]
+	lwz	@t[2],40($inp)
+	xor	@x[7],@x[7],@t[3]
+	lwz	@t[3],44($inp)
+	xor	@x[8],@x[8],@t[0]
+	lwz	@t[0],48($inp)
+	xor	@x[9],@x[9],@t[1]
+	lwz	@t[1],52($inp)
+	xor	@x[10],@x[10],@t[2]
+	lwz	@t[2],56($inp)
+	xor	@x[11],@x[11],@t[3]
+	lwz	@t[3],60($inp)
+	xor	@x[12],@x[12],@t[0]
+	stw	@x[0],0($out)			# store output, aligned or not
+	xor	@x[13],@x[13],@t[1]
+	stw	@x[1],4($out)
+	xor	@x[14],@x[14],@t[2]
+	stw	@x[2],8($out)
+	xor	@x[15],@x[15],@t[3]
+	stw	@x[3],12($out)
+	stw	@x[4],16($out)
+	stw	@x[5],20($out)
+	stw	@x[6],24($out)
+	stw	@x[7],28($out)
+	stw	@x[8],32($out)
+	stw	@x[9],36($out)
+	stw	@x[10],40($out)
+	stw	@x[11],44($out)
+	stw	@x[12],48($out)
+	stw	@x[13],52($out)
+	stw	@x[14],56($out)
+	addi	$inp,$inp,64
+	stw	@x[15],60($out)
+	addi	$out,$out,64
+
+	bne	Loop_outer
+
+	blr
+
+.align	4
+Ltail:
+	addi	$len,$len,64			# restore tail length
+	subi	$inp,$inp,1			# prepare for *++ptr
+	subi	$out,$out,1
+	addi	@t[0],$sp,$LOCALS-1
+	mtctr	$len
+
+	stw	@x[0],`$LOCALS+0`($sp)		# save whole block to stack
+	stw	@x[1],`$LOCALS+4`($sp)
+	stw	@x[2],`$LOCALS+8`($sp)
+	stw	@x[3],`$LOCALS+12`($sp)
+	stw	@x[4],`$LOCALS+16`($sp)
+	stw	@x[5],`$LOCALS+20`($sp)
+	stw	@x[6],`$LOCALS+24`($sp)
+	stw	@x[7],`$LOCALS+28`($sp)
+	stw	@x[8],`$LOCALS+32`($sp)
+	stw	@x[9],`$LOCALS+36`($sp)
+	stw	@x[10],`$LOCALS+40`($sp)
+	stw	@x[11],`$LOCALS+44`($sp)
+	stw	@x[12],`$LOCALS+48`($sp)
+	stw	@x[13],`$LOCALS+52`($sp)
+	stw	@x[14],`$LOCALS+56`($sp)
+	stw	@x[15],`$LOCALS+60`($sp)
+
+Loop_tail:					# byte-by-byte loop
+	lbzu	@d[0],1($inp)
+	lbzu	@x[0],1(@t[0])
+	xor	@d[1],@d[0],@x[0]
+	stbu	@d[1],1($out)
+	bdnz	Loop_tail
+
+	stw	$sp,`$LOCALS+0`($sp)		# wipe block on stack
+	stw	$sp,`$LOCALS+4`($sp)
+	stw	$sp,`$LOCALS+8`($sp)
+	stw	$sp,`$LOCALS+12`($sp)
+	stw	$sp,`$LOCALS+16`($sp)
+	stw	$sp,`$LOCALS+20`($sp)
+	stw	$sp,`$LOCALS+24`($sp)
+	stw	$sp,`$LOCALS+28`($sp)
+	stw	$sp,`$LOCALS+32`($sp)
+	stw	$sp,`$LOCALS+36`($sp)
+	stw	$sp,`$LOCALS+40`($sp)
+	stw	$sp,`$LOCALS+44`($sp)
+	stw	$sp,`$LOCALS+48`($sp)
+	stw	$sp,`$LOCALS+52`($sp)
+	stw	$sp,`$LOCALS+56`($sp)
+	stw	$sp,`$LOCALS+60`($sp)
+
+	blr
+	.long	0
+	.byte	0,12,0x14,0,0,0,0,0
+___
+
+{{{
+my ($A0,$B0,$C0,$D0,$A1,$B1,$C1,$D1,$A2,$B2,$C2,$D2,$T0,$T1,$T2) =
+    map("v$_",(0..14));
+my (@K)=map("v$_",(15..20));
+my ($FOUR,$sixteen,$twenty4,$twenty,$twelve,$twenty5,$seven) =
+    map("v$_",(21..27));
+my ($inpperm,$outperm,$outmask) = map("v$_",(28..30));
+my @D=("v31",$seven,$T0,$T1,$T2);
+
+my $FRAME=$LOCALS+64+13*16+18*$SIZE_T;	# 13*16 is for v20-v31 offload
+
+sub VMXROUND {
+my $odd = pop;
+my ($a,$b,$c,$d,$t)=@_;
+
+	(
+	"&vadduwm	('$a','$a','$b')",
+	"&vxor		('$d','$d','$a')",
+	"&vperm		('$d','$d','$d','$sixteen')",
+
+	"&vadduwm	('$c','$c','$d')",
+	"&vxor		('$t','$b','$c')",
+	"&vsrw		('$b','$t','$twenty')",
+	"&vslw		('$t','$t','$twelve')",
+	"&vor		('$b','$b','$t')",
+
+	"&vadduwm	('$a','$a','$b')",
+	"&vxor		('$d','$d','$a')",
+	"&vperm		('$d','$d','$d','$twenty4')",
+
+	"&vadduwm	('$c','$c','$d')",
+	"&vxor		('$t','$b','$c')",
+	"&vsrw		('$b','$t','$twenty5')",
+	"&vslw		('$t','$t','$seven')",
+	"&vor		('$b','$b','$t')",
+
+	"&vsldoi	('$c','$c','$c',8)",
+	"&vsldoi	('$b','$b','$b',$odd?4:12)",
+	"&vsldoi	('$d','$d','$d',$odd?12:4)"
+	);
+}
+
+$code.=<<___;
+
+.globl	.ChaCha20_ctr32_vmx
+.align	5
+.ChaCha20_ctr32_vmx:
+	${UCMP}i $len,256
+	blt	__ChaCha20_ctr32_int
+
+	$STU	$sp,-$FRAME($sp)
+	mflr	r0
+	li	r10,`15+$LOCALS+64`
+	li	r11,`31+$LOCALS+64`
+	mfspr	r12,256
+	stvx	v20,r10,$sp
+	addi	r10,r10,32
+	stvx	v21,r11,$sp
+	addi	r11,r11,32
+	stvx	v22,r10,$sp
+	addi	r10,r10,32
+	stvx	v23,r11,$sp
+	addi	r11,r11,32
+	stvx	v24,r10,$sp
+	addi	r10,r10,32
+	stvx	v25,r11,$sp
+	addi	r11,r11,32
+	stvx	v26,r10,$sp
+	addi	r10,r10,32
+	stvx	v27,r11,$sp
+	addi	r11,r11,32
+	stvx	v28,r10,$sp
+	addi	r10,r10,32
+	stvx	v29,r11,$sp
+	addi	r11,r11,32
+	stvx	v30,r10,$sp
+	stvx	v31,r11,$sp
+	stw	r12,`$FRAME-$SIZE_T*18-4`($sp)	# save vrsave
+	$PUSH	r14,`$FRAME-$SIZE_T*18`($sp)
+	$PUSH	r15,`$FRAME-$SIZE_T*17`($sp)
+	$PUSH	r16,`$FRAME-$SIZE_T*16`($sp)
+	$PUSH	r17,`$FRAME-$SIZE_T*15`($sp)
+	$PUSH	r18,`$FRAME-$SIZE_T*14`($sp)
+	$PUSH	r19,`$FRAME-$SIZE_T*13`($sp)
+	$PUSH	r20,`$FRAME-$SIZE_T*12`($sp)
+	$PUSH	r21,`$FRAME-$SIZE_T*11`($sp)
+	$PUSH	r22,`$FRAME-$SIZE_T*10`($sp)
+	$PUSH	r23,`$FRAME-$SIZE_T*9`($sp)
+	$PUSH	r24,`$FRAME-$SIZE_T*8`($sp)
+	$PUSH	r25,`$FRAME-$SIZE_T*7`($sp)
+	$PUSH	r26,`$FRAME-$SIZE_T*6`($sp)
+	$PUSH	r27,`$FRAME-$SIZE_T*5`($sp)
+	$PUSH	r28,`$FRAME-$SIZE_T*4`($sp)
+	$PUSH	r29,`$FRAME-$SIZE_T*3`($sp)
+	$PUSH	r30,`$FRAME-$SIZE_T*2`($sp)
+	$PUSH	r31,`$FRAME-$SIZE_T*1`($sp)
+	li	r12,-1
+	$PUSH	r0, `$FRAME+$LRSAVE`($sp)
+	mtspr	256,r12				# preserve all AltiVec registers
+
+	bl	Lconsts				# returns pointer Lsigma in r12
+	li	@x[0],16
+	li	@x[1],32
+	li	@x[2],48
+	li	@x[3],64
+	li	@x[4],31			# 31 is not a typo
+	li	@x[5],15			# nor is 15
+
+	lvx	@K[1],0,$key			# load key
+	?lvsr	$T0,0,$key			# prepare unaligned load
+	lvx	@K[2],@x[0],$key
+	lvx	@D[0],@x[4],$key
+
+	lvx	@K[3],0,$ctr			# load counter
+	?lvsr	$T1,0,$ctr			# prepare unaligned load
+	lvx	@D[1],@x[5],$ctr
+
+	lvx	@K[0],0,r12			# load constants
+	lvx	@K[5],@x[0],r12			# one
+	lvx	$FOUR,@x[1],r12
+	lvx	$sixteen,@x[2],r12
+	lvx	$twenty4,@x[3],r12
+
+	?vperm	@K[1],@K[2],@K[1],$T0		# align key
+	?vperm	@K[2],@D[0],@K[2],$T0
+	?vperm	@K[3],@D[1],@K[3],$T1		# align counter
+
+	lwz	@d[0],0($ctr)			# load counter to GPR
+	lwz	@d[1],4($ctr)
+	vadduwm	@K[3],@K[3],@K[5]		# adjust AltiVec counter
+	lwz	@d[2],8($ctr)
+	vadduwm	@K[4],@K[3],@K[5]
+	lwz	@d[3],12($ctr)
+	vadduwm	@K[5],@K[4],@K[5]
+
+	vspltisw $twenty,-12			# synthesize constants 
+	vspltisw $twelve,12
+	vspltisw $twenty5,-7
+	#vspltisw $seven,7			# synthesized in the loop
+
+	vxor	$T0,$T0,$T0			# 0x00..00
+	vspltisw $outmask,-1			# 0xff..ff
+	?lvsr	$inpperm,0,$inp			# prepare for unaligned load
+	?lvsl	$outperm,0,$out			# prepare for unaligned store
+	?vperm	$outmask,$outmask,$T0,$outperm
+
+	be?lvsl	$T0,0,@x[0]			# 0x00..0f
+	be?vspltisb $T1,3			# 0x03..03
+	be?vxor	$T0,$T0,$T1			# swap bytes within words
+	be?vxor	$outperm,$outperm,$T1
+	be?vperm $inpperm,$inpperm,$inpperm,$T0
+
+	b	Loop_outer_vmx
+
+.align	4
+Loop_outer_vmx:
+	lis	@x[0],0x6170			# synthesize sigma
+	lis	@x[1],0x3320
+	 vmr	$A0,@K[0]
+	lis	@x[2],0x7962
+	lis	@x[3],0x6b20
+	 vmr	$A1,@K[0]
+	ori	@x[0],@x[0],0x7865
+	ori	@x[1],@x[1],0x646e
+	 vmr	$A2,@K[0]
+	ori	@x[2],@x[2],0x2d32
+	ori	@x[3],@x[3],0x6574
+	 vmr	$B0,@K[1]
+
+	li	r0,10				# inner loop counter
+	lwz	@x[4],0($key)			# load key to GPR
+	 vmr	$B1,@K[1]
+	lwz	@x[5],4($key)
+	 vmr	$B2,@K[1]
+	lwz	@x[6],8($key)
+	 vmr	$C0,@K[2]
+	lwz	@x[7],12($key)
+	 vmr	$C1,@K[2]
+	lwz	@x[8],16($key)
+	 vmr	$C2,@K[2]
+	mr	@x[12],@d[0]			# copy GPR counter
+	lwz	@x[9],20($key)
+	 vmr	$D0,@K[3]
+	mr	@x[13],@d[1]
+	lwz	@x[10],24($key)
+	 vmr	$D1,@K[4]
+	mr	@x[14],@d[2]
+	lwz	@x[11],28($key)
+	 vmr	$D2,@K[5]
+	mr	@x[15],@d[3]
+
+	mr	@t[0],@x[4]
+	mr	@t[1],@x[5]
+	mr	@t[2],@x[6]
+	mr	@t[3],@x[7]
+	vspltisw $seven,7
+
+	mtctr	r0
+	nop
+Loop_vmx:
+___
+	my @thread0=&VMXROUND($A0,$B0,$C0,$D0,$T0,0);
+	my @thread1=&VMXROUND($A1,$B1,$C1,$D1,$T1,0);
+	my @thread2=&VMXROUND($A2,$B2,$C2,$D2,$T2,0);
+	my @thread3=&ROUND(0,4,8,12);
+
+	foreach (@thread0) {
+		eval;			eval(shift(@thread3));
+		eval(shift(@thread1));	eval(shift(@thread3));
+		eval(shift(@thread2));	eval(shift(@thread3));
+	}
+
+	@thread0=&VMXROUND($A0,$B0,$C0,$D0,$T0,1);
+	@thread1=&VMXROUND($A1,$B1,$C1,$D1,$T1,1);
+	@thread2=&VMXROUND($A2,$B2,$C2,$D2,$T2,1);
+	@thread3=&ROUND(0,5,10,15);
+
+	foreach (@thread0) {
+		eval;			eval(shift(@thread3));
+		eval(shift(@thread1));	eval(shift(@thread3));
+		eval(shift(@thread2));	eval(shift(@thread3));
+	}
+$code.=<<___;
+	bdnz	Loop_vmx
+
+	subi	$len,$len,256			# $len-=256
+	addi	@x[0],@x[0],0x7865		# accumulate key block
+	addi	@x[1],@x[1],0x646e
+	addi	@x[2],@x[2],0x2d32
+	addi	@x[3],@x[3],0x6574
+	addis	@x[0],@x[0],0x6170
+	addis	@x[1],@x[1],0x3320
+	addis	@x[2],@x[2],0x7962
+	addis	@x[3],@x[3],0x6b20
+	add	@x[4],@x[4],@t[0]
+	lwz	@t[0],16($key)
+	add	@x[5],@x[5],@t[1]
+	lwz	@t[1],20($key)
+	add	@x[6],@x[6],@t[2]
+	lwz	@t[2],24($key)
+	add	@x[7],@x[7],@t[3]
+	lwz	@t[3],28($key)
+	add	@x[8],@x[8],@t[0]
+	add	@x[9],@x[9],@t[1]
+	add	@x[10],@x[10],@t[2]
+	add	@x[11],@x[11],@t[3]
+	add	@x[12],@x[12],@d[0]
+	add	@x[13],@x[13],@d[1]
+	add	@x[14],@x[14],@d[2]
+	add	@x[15],@x[15],@d[3]
+
+	vadduwm	$A0,$A0,@K[0]			# accumulate key block
+	vadduwm	$A1,$A1,@K[0]
+	vadduwm	$A2,$A2,@K[0]
+	vadduwm	$B0,$B0,@K[1]
+	vadduwm	$B1,$B1,@K[1]
+	vadduwm	$B2,$B2,@K[1]
+	vadduwm	$C0,$C0,@K[2]
+	vadduwm	$C1,$C1,@K[2]
+	vadduwm	$C2,$C2,@K[2]
+	vadduwm	$D0,$D0,@K[3]
+	vadduwm	$D1,$D1,@K[4]
+	vadduwm	$D2,$D2,@K[5]
+
+	addi	@d[0],@d[0],4			# increment counter
+	vadduwm	@K[3],@K[3],$FOUR
+	vadduwm	@K[4],@K[4],$FOUR
+	vadduwm	@K[5],@K[5],$FOUR
+
+___
+if (!$LITTLE_ENDIAN) { for($i=0;$i<16;$i++) {	# flip byte order
+$code.=<<___;
+	mr	@t[$i&3],@x[$i]
+	rotlwi	@x[$i],@x[$i],8
+	rlwimi	@x[$i],@t[$i&3],24,0,7
+	rlwimi	@x[$i],@t[$i&3],24,16,23
+___
+} }
+$code.=<<___;
+	lwz	@t[0],0($inp)			# load input, aligned or not
+	lwz	@t[1],4($inp)
+	lwz	@t[2],8($inp)
+	lwz	@t[3],12($inp)
+	xor	@x[0],@x[0],@t[0]		# xor with input
+	lwz	@t[0],16($inp)
+	xor	@x[1],@x[1],@t[1]
+	lwz	@t[1],20($inp)
+	xor	@x[2],@x[2],@t[2]
+	lwz	@t[2],24($inp)
+	xor	@x[3],@x[3],@t[3]
+	lwz	@t[3],28($inp)
+	xor	@x[4],@x[4],@t[0]
+	lwz	@t[0],32($inp)
+	xor	@x[5],@x[5],@t[1]
+	lwz	@t[1],36($inp)
+	xor	@x[6],@x[6],@t[2]
+	lwz	@t[2],40($inp)
+	xor	@x[7],@x[7],@t[3]
+	lwz	@t[3],44($inp)
+	xor	@x[8],@x[8],@t[0]
+	lwz	@t[0],48($inp)
+	xor	@x[9],@x[9],@t[1]
+	lwz	@t[1],52($inp)
+	xor	@x[10],@x[10],@t[2]
+	lwz	@t[2],56($inp)
+	xor	@x[11],@x[11],@t[3]
+	lwz	@t[3],60($inp)
+	xor	@x[12],@x[12],@t[0]
+	stw	@x[0],0($out)			# store output, aligned or not
+	xor	@x[13],@x[13],@t[1]
+	stw	@x[1],4($out)
+	xor	@x[14],@x[14],@t[2]
+	stw	@x[2],8($out)
+	xor	@x[15],@x[15],@t[3]
+	stw	@x[3],12($out)
+	addi	$inp,$inp,64
+	stw	@x[4],16($out)
+	li	@t[0],16
+	stw	@x[5],20($out)
+	li	@t[1],32
+	stw	@x[6],24($out)
+	li	@t[2],48
+	stw	@x[7],28($out)
+	li	@t[3],64
+	stw	@x[8],32($out)
+	stw	@x[9],36($out)
+	stw	@x[10],40($out)
+	stw	@x[11],44($out)
+	stw	@x[12],48($out)
+	stw	@x[13],52($out)
+	stw	@x[14],56($out)
+	stw	@x[15],60($out)
+	addi	$out,$out,64
+
+	lvx	@D[0],0,$inp			# load input
+	lvx	@D[1],@t[0],$inp
+	lvx	@D[2],@t[1],$inp
+	lvx	@D[3],@t[2],$inp
+	lvx	@D[4],@t[3],$inp
+	addi	$inp,$inp,64
+
+	?vperm	@D[0],@D[1],@D[0],$inpperm	# align input
+	?vperm	@D[1],@D[2],@D[1],$inpperm
+	?vperm	@D[2],@D[3],@D[2],$inpperm
+	?vperm	@D[3],@D[4],@D[3],$inpperm
+	vxor	$A0,$A0,@D[0]			# xor with input
+	vxor	$B0,$B0,@D[1]
+	lvx	@D[1],@t[0],$inp		# keep loading input
+	vxor	$C0,$C0,@D[2]
+	lvx	@D[2],@t[1],$inp
+	vxor	$D0,$D0,@D[3]
+	lvx	@D[3],@t[2],$inp
+	lvx	@D[0],@t[3],$inp
+	addi	$inp,$inp,64
+	li	@t[3],63			# 63 is not a typo
+	vperm	$A0,$A0,$A0,$outperm		# pre-misalign output
+	vperm	$B0,$B0,$B0,$outperm
+	vperm	$C0,$C0,$C0,$outperm
+	vperm	$D0,$D0,$D0,$outperm
+
+	?vperm	@D[4],@D[1],@D[4],$inpperm	# align input
+	?vperm	@D[1],@D[2],@D[1],$inpperm
+	?vperm	@D[2],@D[3],@D[2],$inpperm
+	?vperm	@D[3],@D[0],@D[3],$inpperm
+	vxor	$A1,$A1,@D[4]
+	vxor	$B1,$B1,@D[1]
+	lvx	@D[1],@t[0],$inp		# keep loading input
+	vxor	$C1,$C1,@D[2]
+	lvx	@D[2],@t[1],$inp
+	vxor	$D1,$D1,@D[3]
+	lvx	@D[3],@t[2],$inp
+	lvx	@D[4],@t[3],$inp		# redundant in aligned case
+	addi	$inp,$inp,64
+	vperm	$A1,$A1,$A1,$outperm		# pre-misalign output
+	vperm	$B1,$B1,$B1,$outperm
+	vperm	$C1,$C1,$C1,$outperm
+	vperm	$D1,$D1,$D1,$outperm
+
+	?vperm	@D[0],@D[1],@D[0],$inpperm	# align input
+	?vperm	@D[1],@D[2],@D[1],$inpperm
+	?vperm	@D[2],@D[3],@D[2],$inpperm
+	?vperm	@D[3],@D[4],@D[3],$inpperm
+	vxor	$A2,$A2,@D[0]
+	vxor	$B2,$B2,@D[1]
+	vxor	$C2,$C2,@D[2]
+	vxor	$D2,$D2,@D[3]
+	vperm	$A2,$A2,$A2,$outperm		# pre-misalign output
+	vperm	$B2,$B2,$B2,$outperm
+	vperm	$C2,$C2,$C2,$outperm
+	vperm	$D2,$D2,$D2,$outperm
+
+	andi.	@x[1],$out,15			# is $out aligned?
+	mr	@x[0],$out
+
+	vsel	@D[0],$A0,$B0,$outmask		# collect pre-misaligned output
+	vsel	@D[1],$B0,$C0,$outmask
+	vsel	@D[2],$C0,$D0,$outmask
+	vsel	@D[3],$D0,$A1,$outmask
+	vsel	$B0,$A1,$B1,$outmask
+	vsel	$C0,$B1,$C1,$outmask
+	vsel	$D0,$C1,$D1,$outmask
+	vsel	$A1,$D1,$A2,$outmask
+	vsel	$B1,$A2,$B2,$outmask
+	vsel	$C1,$B2,$C2,$outmask
+	vsel	$D1,$C2,$D2,$outmask
+
+	#stvx	$A0,0,$out			# take it easy on the edges
+	stvx	@D[0],@t[0],$out		# store output
+	stvx	@D[1],@t[1],$out
+	stvx	@D[2],@t[2],$out
+	addi	$out,$out,64
+	stvx	@D[3],0,$out
+	stvx	$B0,@t[0],$out
+	stvx	$C0,@t[1],$out
+	stvx	$D0,@t[2],$out
+	addi	$out,$out,64
+	stvx	$A1,0,$out
+	stvx	$B1,@t[0],$out
+	stvx	$C1,@t[1],$out
+	stvx	$D1,@t[2],$out
+	addi	$out,$out,64
+
+	beq	Laligned_vmx
+
+	sub	@x[2],$out,@x[1]		# in misaligned case edges
+	li	@x[3],0				# are written byte-by-byte
+Lunaligned_tail_vmx:
+	stvebx	$D2,@x[3],@x[2]
+	addi	@x[3],@x[3],1
+	cmpw	@x[3],@x[1]
+	bne	Lunaligned_tail_vmx
+
+	sub	@x[2],@x[0],@x[1]
+Lunaligned_head_vmx:
+	stvebx	$A0,@x[1],@x[2]
+	cmpwi	@x[1],15
+	addi	@x[1],@x[1],1
+	bne	Lunaligned_head_vmx
+
+	${UCMP}i $len,255			# done with 256-byte blocks yet?
+	bgt	Loop_outer_vmx
+
+	b	Ldone_vmx
+
+.align	4
+Laligned_vmx:
+	stvx	$A0,0,@x[0]			# head hexaword was not stored
+
+	${UCMP}i $len,255			# done with 256-byte blocks yet?
+	bgt	Loop_outer_vmx
+	nop
+
+Ldone_vmx:
+	${UCMP}i $len,0				# done yet?
+	bnel	__ChaCha20_1x
+
+	lwz	r12,`$FRAME-$SIZE_T*18-4`($sp)	# pull vrsave
+	li	r10,`15+$LOCALS+64`
+	li	r11,`31+$LOCALS+64`
+	mtspr	256,r12				# restore vrsave
+	lvx	v20,r10,$sp
+	addi	r10,r10,32
+	lvx	v21,r11,$sp
+	addi	r11,r11,32
+	lvx	v22,r10,$sp
+	addi	r10,r10,32
+	lvx	v23,r11,$sp
+	addi	r11,r11,32
+	lvx	v24,r10,$sp
+	addi	r10,r10,32
+	lvx	v25,r11,$sp
+	addi	r11,r11,32
+	lvx	v26,r10,$sp
+	addi	r10,r10,32
+	lvx	v27,r11,$sp
+	addi	r11,r11,32
+	lvx	v28,r10,$sp
+	addi	r10,r10,32
+	lvx	v29,r11,$sp
+	addi	r11,r11,32
+	lvx	v30,r10,$sp
+	lvx	v31,r11,$sp
+	$POP	r0, `$FRAME+$LRSAVE`($sp)
+	$POP	r14,`$FRAME-$SIZE_T*18`($sp)
+	$POP	r15,`$FRAME-$SIZE_T*17`($sp)
+	$POP	r16,`$FRAME-$SIZE_T*16`($sp)
+	$POP	r17,`$FRAME-$SIZE_T*15`($sp)
+	$POP	r18,`$FRAME-$SIZE_T*14`($sp)
+	$POP	r19,`$FRAME-$SIZE_T*13`($sp)
+	$POP	r20,`$FRAME-$SIZE_T*12`($sp)
+	$POP	r21,`$FRAME-$SIZE_T*11`($sp)
+	$POP	r22,`$FRAME-$SIZE_T*10`($sp)
+	$POP	r23,`$FRAME-$SIZE_T*9`($sp)
+	$POP	r24,`$FRAME-$SIZE_T*8`($sp)
+	$POP	r25,`$FRAME-$SIZE_T*7`($sp)
+	$POP	r26,`$FRAME-$SIZE_T*6`($sp)
+	$POP	r27,`$FRAME-$SIZE_T*5`($sp)
+	$POP	r28,`$FRAME-$SIZE_T*4`($sp)
+	$POP	r29,`$FRAME-$SIZE_T*3`($sp)
+	$POP	r30,`$FRAME-$SIZE_T*2`($sp)
+	$POP	r31,`$FRAME-$SIZE_T*1`($sp)
+	mtlr	r0
+	addi	$sp,$sp,$FRAME
+	blr
+	.long	0
+	.byte	0,12,0x04,1,0x80,18,5,0
+	.long	0
+.size	.ChaCha20_ctr32_vmx,.-.ChaCha20_ctr32_vmx
+
+.align	5
+Lconsts:
+	mflr	r0
+	bcl	20,31,\$+4
+	mflr	r12	#vvvvv "distance between . and _vpaes_consts
+	addi	r12,r12,`64-8`
+	mtlr	r0
+	blr
+	.long	0
+	.byte	0,12,0x14,0,0,0,0,0
+	.space	`64-9*4`
+Lsigma:
+	.long   0x61707865,0x3320646e,0x79622d32,0x6b206574
+	.long	1,0,0,0
+	.long	4,0,0,0
+___
+$code.=<<___ 	if ($LITTLE_ENDIAN);
+	.long	0x0e0f0c0d,0x0a0b0809,0x06070405,0x02030001
+	.long	0x0d0e0f0c,0x090a0b08,0x05060704,0x01020300
+___
+$code.=<<___ 	if (!$LITTLE_ENDIAN);	# flipped words
+	.long	0x02030001,0x06070405,0x0a0b0809,0x0e0f0c0d
+	.long	0x01020300,0x05060704,0x090a0b08,0x0d0e0f0c
+___
+$code.=<<___;
+.asciz  "ChaCha20 for PowerPC/AltiVec, CRYPTOGAMS by "
+.align	2
+___
+}}}
+
+foreach (split("\n",$code)) {
+	s/\`([^\`]*)\`/eval $1/ge;
+
+	# instructions prefixed with '?' are endian-specific and need
+	# to be adjusted accordingly...
+	if ($flavour !~ /le$/) {	# big-endian
+	    s/be\?//		or
+	    s/le\?/#le#/	or
+	    s/\?lvsr/lvsl/	or
+	    s/\?lvsl/lvsr/	or
+	    s/\?(vperm\s+v[0-9]+,\s*)(v[0-9]+,\s*)(v[0-9]+,\s*)(v[0-9]+)/$1$3$2$4/ or
+	    s/(vsldoi\s+v[0-9]+,\s*)(v[0-9]+,)\s*(v[0-9]+,\s*)([0-9]+)/$1$3$2 16-$4/;
+	} else {			# little-endian
+	    s/le\?//		or
+	    s/be\?/#be#/	or
+	    s/\?([a-z]+)/$1/;
+	}
+
+	print $_,"\n";
+}
+
+close STDOUT;
diff --git a/crypto/chacha/asm/chacha-s390x.pl b/crypto/chacha/asm/chacha-s390x.pl
new file mode 100755
index 000000000..c31526473
--- /dev/null
+++ b/crypto/chacha/asm/chacha-s390x.pl
@@ -0,0 +1,326 @@
+#! /usr/bin/env perl
+# Copyright 2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+#
+# ====================================================================
+# Written by Andy Polyakov  for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+#
+# December 2015
+#
+# ChaCha20 for s390x.
+#
+# 3 times faster than compiler-generated code.
+
+$flavour = shift;
+
+if ($flavour =~ /3[12]/) {
+	$SIZE_T=4;
+	$g="";
+} else {
+	$SIZE_T=8;
+	$g="g";
+}
+
+while (($output=shift) && ($output!~/\w[\w\-]*\.\w+$/)) {}
+open STDOUT,">$output";
+
+sub AUTOLOAD()		# thunk [simplified] x86-style perlasm
+{ my $opcode = $AUTOLOAD; $opcode =~ s/.*:://;
+    $code .= "\t$opcode\t".join(',',@_)."\n";
+}
+
+my $sp="%r15";
+
+my $stdframe=16*$SIZE_T+4*8;
+my $frame=$stdframe+4*20;
+
+my ($out,$inp,$len,$key,$counter)=map("%r$_",(2..6));
+
+my @x=map("%r$_",(0..7,"x","x","x","x",(10..13)));
+my @t=map("%r$_",(8,9));
+
+sub ROUND {
+my ($a0,$b0,$c0,$d0)=@_;
+my ($a1,$b1,$c1,$d1)=map(($_&~3)+(($_+1)&3),($a0,$b0,$c0,$d0));
+my ($a2,$b2,$c2,$d2)=map(($_&~3)+(($_+1)&3),($a1,$b1,$c1,$d1));
+my ($a3,$b3,$c3,$d3)=map(($_&~3)+(($_+1)&3),($a2,$b2,$c2,$d2));
+my ($xc,$xc_)=map("\"$_\"",@t);
+my @x=map("\"$_\"",@x);
+
+	# Consider order in which variables are addressed by their
+	# index:
+	#
+	#	a   b   c   d
+	#
+	#	0   4   8  12 < even round
+	#	1   5   9  13
+	#	2   6  10  14
+	#	3   7  11  15
+	#	0   5  10  15 < odd round
+	#	1   6  11  12
+	#	2   7   8  13
+	#	3   4   9  14
+	#
+	# 'a', 'b' and 'd's are permanently allocated in registers,
+	# @x[0..7,12..15], while 'c's are maintained in memory. If
+	# you observe 'c' column, you'll notice that pair of 'c's is
+	# invariant between rounds. This means that we have to reload
+	# them once per round, in the middle. This is why you'll see
+	# 'c' stores and loads in the middle, but none in the beginning
+	# or end.
+
+	(
+	"&alr	(@x[$a0],@x[$b0])",	# Q1
+	 "&alr	(@x[$a1],@x[$b1])",	# Q2
+	"&xr	(@x[$d0],@x[$a0])",
+	 "&xr	(@x[$d1],@x[$a1])",
+	"&rll	(@x[$d0],@x[$d0],16)",
+	 "&rll	(@x[$d1],@x[$d1],16)",
+
+	"&alr	($xc,@x[$d0])",
+	 "&alr	($xc_,@x[$d1])",
+	"&xr	(@x[$b0],$xc)",
+	 "&xr	(@x[$b1],$xc_)",
+	"&rll	(@x[$b0],@x[$b0],12)",
+	 "&rll	(@x[$b1],@x[$b1],12)",
+
+	"&alr	(@x[$a0],@x[$b0])",
+	 "&alr	(@x[$a1],@x[$b1])",
+	"&xr	(@x[$d0],@x[$a0])",
+	 "&xr	(@x[$d1],@x[$a1])",
+	"&rll	(@x[$d0],@x[$d0],8)",
+	 "&rll	(@x[$d1],@x[$d1],8)",
+
+	"&alr	($xc,@x[$d0])",
+	 "&alr	($xc_,@x[$d1])",
+	"&xr	(@x[$b0],$xc)",
+	 "&xr	(@x[$b1],$xc_)",
+	"&rll	(@x[$b0],@x[$b0],7)",
+	 "&rll	(@x[$b1],@x[$b1],7)",
+
+	"&stm	($xc,$xc_,'$stdframe+4*8+4*$c0($sp)')",	# reload pair of 'c's
+	"&lm	($xc,$xc_,'$stdframe+4*8+4*$c2($sp)')",
+
+	"&alr	(@x[$a2],@x[$b2])",	# Q3
+	 "&alr	(@x[$a3],@x[$b3])",	# Q4
+	"&xr	(@x[$d2],@x[$a2])",
+	 "&xr	(@x[$d3],@x[$a3])",
+	"&rll	(@x[$d2],@x[$d2],16)",
+	 "&rll	(@x[$d3],@x[$d3],16)",
+
+	"&alr	($xc,@x[$d2])",
+	 "&alr	($xc_,@x[$d3])",
+	"&xr	(@x[$b2],$xc)",
+	 "&xr	(@x[$b3],$xc_)",
+	"&rll	(@x[$b2],@x[$b2],12)",
+	 "&rll	(@x[$b3],@x[$b3],12)",
+
+	"&alr	(@x[$a2],@x[$b2])",
+	 "&alr	(@x[$a3],@x[$b3])",
+	"&xr	(@x[$d2],@x[$a2])",
+	 "&xr	(@x[$d3],@x[$a3])",
+	"&rll	(@x[$d2],@x[$d2],8)",
+	 "&rll	(@x[$d3],@x[$d3],8)",
+
+	"&alr	($xc,@x[$d2])",
+	 "&alr	($xc_,@x[$d3])",
+	"&xr	(@x[$b2],$xc)",
+	 "&xr	(@x[$b3],$xc_)",
+	"&rll	(@x[$b2],@x[$b2],7)",
+	 "&rll	(@x[$b3],@x[$b3],7)"
+	);
+}
+
+$code.=<<___;
+.text
+
+.globl	ChaCha20_ctr32
+.type	ChaCha20_ctr32,\@function
+.align	32
+ChaCha20_ctr32:
+	lt${g}r	$len,$len			# $len==0?
+	bzr	%r14
+	a${g}hi	$len,-64
+	l${g}hi	%r1,-$frame
+	stm${g}	%r6,%r15,`6*$SIZE_T`($sp)
+	sl${g}r	$out,$inp			# difference
+	la	$len,0($inp,$len)		# end of input minus 64
+	larl	%r7,.Lsigma
+	lgr	%r0,$sp
+	la	$sp,0(%r1,$sp)
+	st${g}	%r0,0($sp)
+
+	lmg	%r8,%r11,0($key)		# load key
+	lmg	%r12,%r13,0($counter)		# load counter
+	lmg	%r6,%r7,0(%r7)			# load sigma constant
+
+	la	%r14,0($inp)
+	st${g}	$out,$frame+3*$SIZE_T($sp)
+	st${g}	$len,$frame+4*$SIZE_T($sp)
+	stmg	%r6,%r13,$stdframe($sp)		# copy key schedule to stack
+	srlg	@x[12],%r12,32			# 32-bit counter value
+	j	.Loop_outer
+
+.align	16
+.Loop_outer:
+	lm	@x[0],@x[7],$stdframe+4*0($sp)		# load x[0]-x[7]
+	lm	@t[0],@t[1],$stdframe+4*10($sp)		# load x[10]-x[11]
+	lm	@x[13],@x[15],$stdframe+4*13($sp)	# load x[13]-x[15]
+	stm	@t[0],@t[1],$stdframe+4*8+4*10($sp)	# offload x[10]-x[11]
+	lm	@t[0],@t[1],$stdframe+4*8($sp)		# load x[8]-x[9]
+	st	@x[12],$stdframe+4*12($sp)		# save counter
+	st${g}	%r14,$frame+2*$SIZE_T($sp)		# save input pointer
+	lhi	%r14,10
+	j	.Loop
+
+.align	4
+.Loop:
+___
+	foreach (&ROUND(0, 4, 8,12)) { eval; }
+	foreach (&ROUND(0, 5,10,15)) { eval; }
+$code.=<<___;
+	brct	%r14,.Loop
+
+	l${g}	%r14,$frame+2*$SIZE_T($sp)		# pull input pointer
+	stm	@t[0],@t[1],$stdframe+4*8+4*8($sp)	# offload x[8]-x[9]
+	lm${g}	@t[0],@t[1],$frame+3*$SIZE_T($sp)
+
+	al	@x[0],$stdframe+4*0($sp)	# accumulate key schedule
+	al	@x[1],$stdframe+4*1($sp)
+	al	@x[2],$stdframe+4*2($sp)
+	al	@x[3],$stdframe+4*3($sp)
+	al	@x[4],$stdframe+4*4($sp)
+	al	@x[5],$stdframe+4*5($sp)
+	al	@x[6],$stdframe+4*6($sp)
+	al	@x[7],$stdframe+4*7($sp)
+	lrvr	@x[0],@x[0]
+	lrvr	@x[1],@x[1]
+	lrvr	@x[2],@x[2]
+	lrvr	@x[3],@x[3]
+	lrvr	@x[4],@x[4]
+	lrvr	@x[5],@x[5]
+	lrvr	@x[6],@x[6]
+	lrvr	@x[7],@x[7]
+	al	@x[12],$stdframe+4*12($sp)
+	al	@x[13],$stdframe+4*13($sp)
+	al	@x[14],$stdframe+4*14($sp)
+	al	@x[15],$stdframe+4*15($sp)
+	lrvr	@x[12],@x[12]
+	lrvr	@x[13],@x[13]
+	lrvr	@x[14],@x[14]
+	lrvr	@x[15],@x[15]
+
+	la	@t[0],0(@t[0],%r14)		# reconstruct output pointer
+	cl${g}r	%r14,@t[1]
+	jh	.Ltail
+
+	x	@x[0],4*0(%r14)			# xor with input
+	x	@x[1],4*1(%r14)
+	st	@x[0],4*0(@t[0])		# store output
+	x	@x[2],4*2(%r14)
+	st	@x[1],4*1(@t[0])
+	x	@x[3],4*3(%r14)
+	st	@x[2],4*2(@t[0])
+	x	@x[4],4*4(%r14)
+	st	@x[3],4*3(@t[0])
+	 lm	@x[0],@x[3],$stdframe+4*8+4*8($sp)	# load x[8]-x[11]
+	x	@x[5],4*5(%r14)
+	st	@x[4],4*4(@t[0])
+	x	@x[6],4*6(%r14)
+	 al	@x[0],$stdframe+4*8($sp)
+	st	@x[5],4*5(@t[0])
+	x	@x[7],4*7(%r14)
+	 al	@x[1],$stdframe+4*9($sp)
+	st	@x[6],4*6(@t[0])
+	x	@x[12],4*12(%r14)
+	 al	@x[2],$stdframe+4*10($sp)
+	st	@x[7],4*7(@t[0])
+	x	@x[13],4*13(%r14)
+	 al	@x[3],$stdframe+4*11($sp)
+	st	@x[12],4*12(@t[0])
+	x	@x[14],4*14(%r14)
+	st	@x[13],4*13(@t[0])
+	x	@x[15],4*15(%r14)
+	st	@x[14],4*14(@t[0])
+	 lrvr	@x[0],@x[0]
+	st	@x[15],4*15(@t[0])
+	 lrvr	@x[1],@x[1]
+	 lrvr	@x[2],@x[2]
+	 lrvr	@x[3],@x[3]
+	lhi	@x[12],1
+	 x	@x[0],4*8(%r14)
+	al	@x[12],$stdframe+4*12($sp)	# increment counter
+	 x	@x[1],4*9(%r14)
+	 st	@x[0],4*8(@t[0])
+	 x	@x[2],4*10(%r14)
+	 st	@x[1],4*9(@t[0])
+	 x	@x[3],4*11(%r14)
+	 st	@x[2],4*10(@t[0])
+	 st	@x[3],4*11(@t[0])
+
+	cl${g}r	%r14,@t[1]			# done yet?
+	la	%r14,64(%r14)
+	jl	.Loop_outer
+
+.Ldone:
+	xgr	%r0,%r0
+	xgr	%r1,%r1
+	xgr	%r2,%r2
+	xgr	%r3,%r3
+	stmg	%r0,%r3,$stdframe+4*4($sp)	# wipe key copy
+	stmg	%r0,%r3,$stdframe+4*12($sp)
+
+	lm${g}	%r6,%r15,`$frame+6*$SIZE_T`($sp)
+	br	%r14
+
+.align	16
+.Ltail:
+	la	@t[1],64($t[1])
+	stm	@x[0],@x[7],$stdframe+4*0($sp)
+	sl${g}r	@t[1],%r14
+	lm	@x[0],@x[3],$stdframe+4*8+4*8($sp)
+	l${g}hi	@x[6],0
+	stm	@x[12],@x[15],$stdframe+4*12($sp)
+	al	@x[0],$stdframe+4*8($sp)
+	al	@x[1],$stdframe+4*9($sp)
+	al	@x[2],$stdframe+4*10($sp)
+	al	@x[3],$stdframe+4*11($sp)
+	lrvr	@x[0],@x[0]
+	lrvr	@x[1],@x[1]
+	lrvr	@x[2],@x[2]
+	lrvr	@x[3],@x[3]
+	stm	@x[0],@x[3],$stdframe+4*8($sp)
+
+.Loop_tail:
+	llgc	@x[4],0(@x[6],%r14)
+	llgc	@x[5],$stdframe(@x[6],$sp)
+	xr	@x[5],@x[4]
+	stc	@x[5],0(@x[6],@t[0])
+	la	@x[6],1(@x[6])
+	brct	@t[1],.Loop_tail
+
+	j	.Ldone
+.size	ChaCha20_ctr32,.-ChaCha20_ctr32
+
+.align	32
+.Lsigma:
+.long	0x61707865,0x3320646e,0x79622d32,0x6b206574	# endian-neutral
+.asciz	"ChaCha20 for s390x, CRYPTOGAMS by "
+.align	4
+___
+
+foreach (split("\n",$code)) {
+	s/\`([^\`]*)\`/eval $1/ge;
+
+	print $_,"\n";
+}
+close STDOUT;
diff --git a/crypto/chacha/asm/chacha-x86.pl b/crypto/chacha/asm/chacha-x86.pl
new file mode 100755
index 000000000..61b328612
--- /dev/null
+++ b/crypto/chacha/asm/chacha-x86.pl
@@ -0,0 +1,1154 @@
+#! /usr/bin/env perl
+# Copyright 2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+#
+# ====================================================================
+# Written by Andy Polyakov  for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+#
+# January 2015
+#
+# ChaCha20 for x86.
+#
+# Performance in cycles per byte out of large buffer.
+#
+#		1xIALU/gcc	4xSSSE3
+# Pentium	17.5/+80%
+# PIII		14.2/+60%
+# P4		18.6/+84%
+# Core2		9.56/+89%	4.83
+# Westmere	9.50/+45%	3.35
+# Sandy Bridge	10.5/+47%	3.20
+# Haswell	8.15/+50%	2.83
+# Silvermont	17.4/+36%	8.35
+# Goldmont	13.4/+40%	4.36
+# Sledgehammer	10.2/+54%
+# Bulldozer	13.4/+50%	4.38(*)
+#
+# (*)	Bulldozer actually executes 4xXOP code path that delivers 3.55;
+
+$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+push(@INC,"${dir}","${dir}../../perlasm");
+require "x86asm.pl";
+
+$output=pop;
+open STDOUT,">$output";
+
+&asm_init($ARGV[0],"chacha-x86.pl",$ARGV[$#ARGV] eq "386");
+
+$xmm=$ymm=0;
+for (@ARGV) { $xmm=1 if (/-DOPENSSL_IA32_SSE2/); }
+
+$ymm=1 if ($xmm &&
+		`$ENV{CC} -Wa,-v -c -o /dev/null -x assembler /dev/null 2>&1`
+			=~ /GNU assembler version ([2-9]\.[0-9]+)/ &&
+		($gasver=$1)>=2.19);	# first version supporting AVX
+
+$ymm=1 if ($xmm && !$ymm && $ARGV[0] eq "win32n" &&
+		`nasm -v 2>&1` =~ /NASM version ([2-9]\.[0-9]+)/ &&
+		$1>=2.03);	# first version supporting AVX
+
+$ymm=1 if ($xmm && !$ymm && $ARGV[0] eq "win32" &&
+		`ml 2>&1` =~ /Version ([0-9]+)\./ &&
+		$1>=10);	# first version supporting AVX
+
+$ymm=1 if ($xmm && !$ymm &&
+		`$ENV{CC} -v 2>&1` =~ /(^clang version|based on LLVM) ([3-9]\.[0-9]+)/ &&
+		$2>=3.0);	# first version supporting AVX
+
+$a="eax";
+($b,$b_)=("ebx","ebp");
+($c,$c_)=("ecx","esi");
+($d,$d_)=("edx","edi");
+
+sub QUARTERROUND {
+my ($ai,$bi,$ci,$di,$i)=@_;
+my ($an,$bn,$cn,$dn)=map(($_&~3)+(($_+1)&3),($ai,$bi,$ci,$di));	# next
+my ($ap,$bp,$cp,$dp)=map(($_&~3)+(($_-1)&3),($ai,$bi,$ci,$di));	# previous
+
+	#       a   b   c   d
+	#
+	#       0   4   8  12 < even round
+	#       1   5   9  13
+	#       2   6  10  14
+	#       3   7  11  15
+	#       0   5  10  15 < odd round
+	#       1   6  11  12
+	#       2   7   8  13
+	#       3   4   9  14
+
+	if ($i==0) {
+            my $j=4;
+	    ($ap,$bp,$cp,$dp)=map(($_&~3)+(($_-$j--)&3),($ap,$bp,$cp,$dp));
+	} elsif ($i==3) {
+            my $j=0;
+	    ($an,$bn,$cn,$dn)=map(($_&~3)+(($_+$j++)&3),($an,$bn,$cn,$dn));
+	} elsif ($i==4) {
+            my $j=4;
+	    ($ap,$bp,$cp,$dp)=map(($_&~3)+(($_+$j--)&3),($ap,$bp,$cp,$dp));
+	} elsif ($i==7) {
+            my $j=0;
+	    ($an,$bn,$cn,$dn)=map(($_&~3)+(($_-$j++)&3),($an,$bn,$cn,$dn));
+	}
+
+	#&add	($a,$b);			# see elsewhere
+	&xor	($d,$a);
+	 &mov	(&DWP(4*$cp,"esp"),$c_)		if ($ai>0 && $ai<3);
+	&rol	($d,16);
+	 &mov	(&DWP(4*$bp,"esp"),$b_)		if ($i!=0);
+	&add	($c,$d);
+	 &mov	($c_,&DWP(4*$cn,"esp"))		if ($ai>0 && $ai<3);
+	&xor	($b,$c);
+	 &mov	($d_,&DWP(4*$dn,"esp"))		if ($di!=$dn);
+	&rol	($b,12);
+	 &mov	($b_,&DWP(4*$bn,"esp"))		if ($i<7);
+	 &mov	($b_,&DWP(128,"esp"))		if ($i==7);	# loop counter
+	&add	($a,$b);
+	&xor	($d,$a);
+	&mov	(&DWP(4*$ai,"esp"),$a);
+	&rol	($d,8);
+	&mov	($a,&DWP(4*$an,"esp"));
+	&add	($c,$d);
+	&mov	(&DWP(4*$di,"esp"),$d)		if ($di!=$dn);
+	&mov	($d_,$d)			if ($di==$dn);
+	&xor	($b,$c);
+	 &add	($a,$b_)			if ($i<7);	# elsewhere
+	&rol	($b,7);
+
+	($b,$b_)=($b_,$b);
+	($c,$c_)=($c_,$c);
+	($d,$d_)=($d_,$d);
+}
+
+&static_label("ssse3_shortcut");
+&static_label("xop_shortcut");
+&static_label("ssse3_data");
+&static_label("pic_point");
+
+&function_begin("ChaCha20_ctr32");
+	&xor	("eax","eax");
+	&cmp	("eax",&wparam(2));		# len==0?
+	&je	(&label("no_data"));
+if ($xmm) {
+	&call	(&label("pic_point"));
+&set_label("pic_point");
+	&blindpop("eax");
+	&picmeup("ebp","OPENSSL_ia32cap_P","eax",&label("pic_point"));
+	&test	(&DWP(0,"ebp"),1<<24);		# test FXSR bit
+	&jz	(&label("x86"));
+	&test	(&DWP(4,"ebp"),1<<9);		# test SSSE3 bit
+	&jz	(&label("x86"));
+	&jmp	(&label("ssse3_shortcut"));
+&set_label("x86");
+}
+	&mov	("esi",&wparam(3));		# key
+	&mov	("edi",&wparam(4));		# counter and nonce
+
+	&stack_push(33);
+
+	&mov	("eax",&DWP(4*0,"esi"));	# copy key
+	&mov	("ebx",&DWP(4*1,"esi"));
+	&mov	("ecx",&DWP(4*2,"esi"));
+	&mov	("edx",&DWP(4*3,"esi"));
+	&mov	(&DWP(64+4*4,"esp"),"eax");
+	&mov	(&DWP(64+4*5,"esp"),"ebx");
+	&mov	(&DWP(64+4*6,"esp"),"ecx");
+	&mov	(&DWP(64+4*7,"esp"),"edx");
+	&mov	("eax",&DWP(4*4,"esi"));
+	&mov	("ebx",&DWP(4*5,"esi"));
+	&mov	("ecx",&DWP(4*6,"esi"));
+	&mov	("edx",&DWP(4*7,"esi"));
+	&mov	(&DWP(64+4*8,"esp"),"eax");
+	&mov	(&DWP(64+4*9,"esp"),"ebx");
+	&mov	(&DWP(64+4*10,"esp"),"ecx");
+	&mov	(&DWP(64+4*11,"esp"),"edx");
+	&mov	("eax",&DWP(4*0,"edi"));	# copy counter and nonce
+	&mov	("ebx",&DWP(4*1,"edi"));
+	&mov	("ecx",&DWP(4*2,"edi"));
+	&mov	("edx",&DWP(4*3,"edi"));
+	&sub	("eax",1);
+	&mov	(&DWP(64+4*12,"esp"),"eax");
+	&mov	(&DWP(64+4*13,"esp"),"ebx");
+	&mov	(&DWP(64+4*14,"esp"),"ecx");
+	&mov	(&DWP(64+4*15,"esp"),"edx");
+	&jmp	(&label("entry"));
+
+&set_label("outer_loop",16);
+	&mov	(&wparam(1),$b);		# save input
+	&mov	(&wparam(0),$a);		# save output
+	&mov	(&wparam(2),$c);		# save len
+&set_label("entry");
+	&mov	($a,0x61707865);
+	&mov	(&DWP(4*1,"esp"),0x3320646e);
+	&mov	(&DWP(4*2,"esp"),0x79622d32);
+	&mov	(&DWP(4*3,"esp"),0x6b206574);
+
+	&mov	($b, &DWP(64+4*5,"esp"));	# copy key material
+	&mov	($b_,&DWP(64+4*6,"esp"));
+	&mov	($c, &DWP(64+4*10,"esp"));
+	&mov	($c_,&DWP(64+4*11,"esp"));
+	&mov	($d, &DWP(64+4*13,"esp"));
+	&mov	($d_,&DWP(64+4*14,"esp"));
+	&mov	(&DWP(4*5,"esp"),$b);
+	&mov	(&DWP(4*6,"esp"),$b_);
+	&mov	(&DWP(4*10,"esp"),$c);
+	&mov	(&DWP(4*11,"esp"),$c_);
+	&mov	(&DWP(4*13,"esp"),$d);
+	&mov	(&DWP(4*14,"esp"),$d_);
+
+	&mov	($b, &DWP(64+4*7,"esp"));
+	&mov	($d_,&DWP(64+4*15,"esp"));
+	&mov	($d, &DWP(64+4*12,"esp"));
+	&mov	($b_,&DWP(64+4*4,"esp"));
+	&mov	($c, &DWP(64+4*8,"esp"));
+	&mov	($c_,&DWP(64+4*9,"esp"));
+	&add	($d,1);				# counter value
+	&mov	(&DWP(4*7,"esp"),$b);
+	&mov	(&DWP(4*15,"esp"),$d_);
+	&mov	(&DWP(64+4*12,"esp"),$d);	# save counter value
+
+	&mov	($b,10);			# loop counter
+	&jmp	(&label("loop"));
+
+&set_label("loop",16);
+	&add	($a,$b_);			# elsewhere
+	&mov	(&DWP(128,"esp"),$b);		# save loop counter
+	&mov	($b,$b_);
+	&QUARTERROUND(0, 4, 8, 12, 0);
+	&QUARTERROUND(1, 5, 9, 13, 1);
+	&QUARTERROUND(2, 6,10, 14, 2);
+	&QUARTERROUND(3, 7,11, 15, 3);
+	&QUARTERROUND(0, 5,10, 15, 4);
+	&QUARTERROUND(1, 6,11, 12, 5);
+	&QUARTERROUND(2, 7, 8, 13, 6);
+	&QUARTERROUND(3, 4, 9, 14, 7);
+	&dec	($b);
+	&jnz	(&label("loop"));
+
+	&mov	($b,&wparam(2));		# load len
+
+	&add	($a,0x61707865);		# accumulate key material
+	&add	($b_,&DWP(64+4*4,"esp"));
+	&add	($c, &DWP(64+4*8,"esp"));
+	&add	($c_,&DWP(64+4*9,"esp"));
+
+	&cmp	($b,64);
+	&jb	(&label("tail"));
+
+	&mov	($b,&wparam(1));		# load input pointer
+	&add	($d, &DWP(64+4*12,"esp"));
+	&add	($d_,&DWP(64+4*14,"esp"));
+
+	&xor	($a, &DWP(4*0,$b));		# xor with input
+	&xor	($b_,&DWP(4*4,$b));
+	&mov	(&DWP(4*0,"esp"),$a);
+	&mov	($a,&wparam(0));		# load output pointer
+	&xor	($c, &DWP(4*8,$b));
+	&xor	($c_,&DWP(4*9,$b));
+	&xor	($d, &DWP(4*12,$b));
+	&xor	($d_,&DWP(4*14,$b));
+	&mov	(&DWP(4*4,$a),$b_);		# write output
+	&mov	(&DWP(4*8,$a),$c);
+	&mov	(&DWP(4*9,$a),$c_);
+	&mov	(&DWP(4*12,$a),$d);
+	&mov	(&DWP(4*14,$a),$d_);
+
+	&mov	($b_,&DWP(4*1,"esp"));
+	&mov	($c, &DWP(4*2,"esp"));
+	&mov	($c_,&DWP(4*3,"esp"));
+	&mov	($d, &DWP(4*5,"esp"));
+	&mov	($d_,&DWP(4*6,"esp"));
+	&add	($b_,0x3320646e);		# accumulate key material
+	&add	($c, 0x79622d32);
+	&add	($c_,0x6b206574);
+	&add	($d, &DWP(64+4*5,"esp"));
+	&add	($d_,&DWP(64+4*6,"esp"));
+	&xor	($b_,&DWP(4*1,$b));
+	&xor	($c, &DWP(4*2,$b));
+	&xor	($c_,&DWP(4*3,$b));
+	&xor	($d, &DWP(4*5,$b));
+	&xor	($d_,&DWP(4*6,$b));
+	&mov	(&DWP(4*1,$a),$b_);
+	&mov	(&DWP(4*2,$a),$c);
+	&mov	(&DWP(4*3,$a),$c_);
+	&mov	(&DWP(4*5,$a),$d);
+	&mov	(&DWP(4*6,$a),$d_);
+
+	&mov	($b_,&DWP(4*7,"esp"));
+	&mov	($c, &DWP(4*10,"esp"));
+	&mov	($c_,&DWP(4*11,"esp"));
+	&mov	($d, &DWP(4*13,"esp"));
+	&mov	($d_,&DWP(4*15,"esp"));
+	&add	($b_,&DWP(64+4*7,"esp"));
+	&add	($c, &DWP(64+4*10,"esp"));
+	&add	($c_,&DWP(64+4*11,"esp"));
+	&add	($d, &DWP(64+4*13,"esp"));
+	&add	($d_,&DWP(64+4*15,"esp"));
+	&xor	($b_,&DWP(4*7,$b));
+	&xor	($c, &DWP(4*10,$b));
+	&xor	($c_,&DWP(4*11,$b));
+	&xor	($d, &DWP(4*13,$b));
+	&xor	($d_,&DWP(4*15,$b));
+	&lea	($b,&DWP(4*16,$b));
+	&mov	(&DWP(4*7,$a),$b_);
+	&mov	($b_,&DWP(4*0,"esp"));
+	&mov	(&DWP(4*10,$a),$c);
+	&mov	($c,&wparam(2));		# len
+	&mov	(&DWP(4*11,$a),$c_);
+	&mov	(&DWP(4*13,$a),$d);
+	&mov	(&DWP(4*15,$a),$d_);
+	&mov	(&DWP(4*0,$a),$b_);
+	&lea	($a,&DWP(4*16,$a));
+	&sub	($c,64);
+	&jnz	(&label("outer_loop"));
+
+	&jmp	(&label("done"));
+
+&set_label("tail");
+	&add	($d, &DWP(64+4*12,"esp"));
+	&add	($d_,&DWP(64+4*14,"esp"));
+	&mov	(&DWP(4*0,"esp"),$a);
+	&mov	(&DWP(4*4,"esp"),$b_);
+	&mov	(&DWP(4*8,"esp"),$c);
+	&mov	(&DWP(4*9,"esp"),$c_);
+	&mov	(&DWP(4*12,"esp"),$d);
+	&mov	(&DWP(4*14,"esp"),$d_);
+
+	&mov	($b_,&DWP(4*1,"esp"));
+	&mov	($c, &DWP(4*2,"esp"));
+	&mov	($c_,&DWP(4*3,"esp"));
+	&mov	($d, &DWP(4*5,"esp"));
+	&mov	($d_,&DWP(4*6,"esp"));
+	&add	($b_,0x3320646e);		# accumulate key material
+	&add	($c, 0x79622d32);
+	&add	($c_,0x6b206574);
+	&add	($d, &DWP(64+4*5,"esp"));
+	&add	($d_,&DWP(64+4*6,"esp"));
+	&mov	(&DWP(4*1,"esp"),$b_);
+	&mov	(&DWP(4*2,"esp"),$c);
+	&mov	(&DWP(4*3,"esp"),$c_);
+	&mov	(&DWP(4*5,"esp"),$d);
+	&mov	(&DWP(4*6,"esp"),$d_);
+
+	&mov	($b_,&DWP(4*7,"esp"));
+	&mov	($c, &DWP(4*10,"esp"));
+	&mov	($c_,&DWP(4*11,"esp"));
+	&mov	($d, &DWP(4*13,"esp"));
+	&mov	($d_,&DWP(4*15,"esp"));
+	&add	($b_,&DWP(64+4*7,"esp"));
+	&add	($c, &DWP(64+4*10,"esp"));
+	&add	($c_,&DWP(64+4*11,"esp"));
+	&add	($d, &DWP(64+4*13,"esp"));
+	&add	($d_,&DWP(64+4*15,"esp"));
+	&mov	(&DWP(4*7,"esp"),$b_);
+	&mov	($b_,&wparam(1));		# load input
+	&mov	(&DWP(4*10,"esp"),$c);
+	&mov	($c,&wparam(0));		# load output
+	&mov	(&DWP(4*11,"esp"),$c_);
+	&xor	($c_,$c_);
+	&mov	(&DWP(4*13,"esp"),$d);
+	&mov	(&DWP(4*15,"esp"),$d_);
+
+	&xor	("eax","eax");
+	&xor	("edx","edx");
+&set_label("tail_loop");
+	&movb	("al",&BP(0,$c_,$b_));
+	&movb	("dl",&BP(0,"esp",$c_));
+	&lea	($c_,&DWP(1,$c_));
+	&xor	("al","dl");
+	&mov	(&BP(-1,$c,$c_),"al");
+	&dec	($b);
+	&jnz	(&label("tail_loop"));
+
+&set_label("done");
+	&stack_pop(33);
+&set_label("no_data");
+&function_end("ChaCha20_ctr32");
+
+if ($xmm) {
+my ($xa,$xa_,$xb,$xb_,$xc,$xc_,$xd,$xd_)=map("xmm$_",(0..7));
+my ($out,$inp,$len)=("edi","esi","ecx");
+
+sub QUARTERROUND_SSSE3 {
+my ($ai,$bi,$ci,$di,$i)=@_;
+my ($an,$bn,$cn,$dn)=map(($_&~3)+(($_+1)&3),($ai,$bi,$ci,$di));	# next
+my ($ap,$bp,$cp,$dp)=map(($_&~3)+(($_-1)&3),($ai,$bi,$ci,$di));	# previous
+
+	#       a   b   c   d
+	#
+	#       0   4   8  12 < even round
+	#       1   5   9  13
+	#       2   6  10  14
+	#       3   7  11  15
+	#       0   5  10  15 < odd round
+	#       1   6  11  12
+	#       2   7   8  13
+	#       3   4   9  14
+
+	if ($i==0) {
+            my $j=4;
+	    ($ap,$bp,$cp,$dp)=map(($_&~3)+(($_-$j--)&3),($ap,$bp,$cp,$dp));
+	} elsif ($i==3) {
+            my $j=0;
+	    ($an,$bn,$cn,$dn)=map(($_&~3)+(($_+$j++)&3),($an,$bn,$cn,$dn));
+	} elsif ($i==4) {
+            my $j=4;
+	    ($ap,$bp,$cp,$dp)=map(($_&~3)+(($_+$j--)&3),($ap,$bp,$cp,$dp));
+	} elsif ($i==7) {
+            my $j=0;
+	    ($an,$bn,$cn,$dn)=map(($_&~3)+(($_-$j++)&3),($an,$bn,$cn,$dn));
+	}
+
+	#&paddd	($xa,$xb);			# see elsewhere
+	#&pxor	($xd,$xa);			# see elsewhere
+	 &movdqa(&QWP(16*$cp-128,"ebx"),$xc_)	if ($ai>0 && $ai<3);
+	&pshufb	($xd,&QWP(0,"eax"));		# rot16
+	 &movdqa(&QWP(16*$bp-128,"ebx"),$xb_)	if ($i!=0);
+	&paddd	($xc,$xd);
+	 &movdqa($xc_,&QWP(16*$cn-128,"ebx"))	if ($ai>0 && $ai<3);
+	&pxor	($xb,$xc);
+	 &movdqa($xb_,&QWP(16*$bn-128,"ebx"))	if ($i<7);
+	&movdqa	($xa_,$xb);			# borrow as temporary
+	&pslld	($xb,12);
+	&psrld	($xa_,20);
+	&por	($xb,$xa_);
+	 &movdqa($xa_,&QWP(16*$an-128,"ebx"));
+	&paddd	($xa,$xb);
+	 &movdqa($xd_,&QWP(16*$dn-128,"ebx"))	if ($di!=$dn);
+	&pxor	($xd,$xa);
+	&movdqa	(&QWP(16*$ai-128,"ebx"),$xa);
+	&pshufb	($xd,&QWP(16,"eax"));		# rot8
+	&paddd	($xc,$xd);
+	&movdqa	(&QWP(16*$di-128,"ebx"),$xd)	if ($di!=$dn);
+	&movdqa	($xd_,$xd)			if ($di==$dn);
+	&pxor	($xb,$xc);
+	 &paddd	($xa_,$xb_)			if ($i<7);	# elsewhere
+	&movdqa	($xa,$xb);			# borrow as temporary
+	&pslld	($xb,7);
+	&psrld	($xa,25);
+	 &pxor	($xd_,$xa_)			if ($i<7);	# elsewhere
+	&por	($xb,$xa);
+
+	($xa,$xa_)=($xa_,$xa);
+	($xb,$xb_)=($xb_,$xb);
+	($xc,$xc_)=($xc_,$xc);
+	($xd,$xd_)=($xd_,$xd);
+}
+
+&function_begin("ChaCha20_ssse3");
+&set_label("ssse3_shortcut");
+if ($ymm) {
+	&test		(&DWP(4,"ebp"),1<<11);		# test XOP bit
+	&jnz		(&label("xop_shortcut"));
+}
+
+	&mov		($out,&wparam(0));
+	&mov		($inp,&wparam(1));
+	&mov		($len,&wparam(2));
+	&mov		("edx",&wparam(3));		# key
+	&mov		("ebx",&wparam(4));		# counter and nonce
+
+	&mov		("ebp","esp");
+	&stack_push	(131);
+	&and		("esp",-64);
+	&mov		(&DWP(512,"esp"),"ebp");
+
+	&lea		("eax",&DWP(&label("ssse3_data")."-".
+				    &label("pic_point"),"eax"));
+	&movdqu		("xmm3",&QWP(0,"ebx"));		# counter and nonce
+
+if (defined($gasver) && $gasver>=2.17) {		# even though we encode
+							# pshufb manually, we
+							# handle only register
+							# operands, while this
+							# segment uses memory
+							# operand...
+	&cmp		($len,64*4);
+	&jb		(&label("1x"));
+
+	&mov		(&DWP(512+4,"esp"),"edx");	# offload pointers
+	&mov		(&DWP(512+8,"esp"),"ebx");
+	&sub		($len,64*4);			# bias len
+	&lea		("ebp",&DWP(256+128,"esp"));	# size optimization
+
+	&movdqu		("xmm7",&QWP(0,"edx"));		# key
+	&pshufd		("xmm0","xmm3",0x00);
+	&pshufd		("xmm1","xmm3",0x55);
+	&pshufd		("xmm2","xmm3",0xaa);
+	&pshufd		("xmm3","xmm3",0xff);
+	 &paddd		("xmm0",&QWP(16*3,"eax"));	# fix counters
+	&pshufd		("xmm4","xmm7",0x00);
+	&pshufd		("xmm5","xmm7",0x55);
+	 &psubd		("xmm0",&QWP(16*4,"eax"));
+	&pshufd		("xmm6","xmm7",0xaa);
+	&pshufd		("xmm7","xmm7",0xff);
+	&movdqa		(&QWP(16*12-128,"ebp"),"xmm0");
+	&movdqa		(&QWP(16*13-128,"ebp"),"xmm1");
+	&movdqa		(&QWP(16*14-128,"ebp"),"xmm2");
+	&movdqa		(&QWP(16*15-128,"ebp"),"xmm3");
+	 &movdqu	("xmm3",&QWP(16,"edx"));	# key
+	&movdqa		(&QWP(16*4-128,"ebp"),"xmm4");
+	&movdqa		(&QWP(16*5-128,"ebp"),"xmm5");
+	&movdqa		(&QWP(16*6-128,"ebp"),"xmm6");
+	&movdqa		(&QWP(16*7-128,"ebp"),"xmm7");
+	 &movdqa	("xmm7",&QWP(16*2,"eax"));	# sigma
+	 &lea		("ebx",&DWP(128,"esp"));	# size optimization
+
+	&pshufd		("xmm0","xmm3",0x00);
+	&pshufd		("xmm1","xmm3",0x55);
+	&pshufd		("xmm2","xmm3",0xaa);
+	&pshufd		("xmm3","xmm3",0xff);
+	&pshufd		("xmm4","xmm7",0x00);
+	&pshufd		("xmm5","xmm7",0x55);
+	&pshufd		("xmm6","xmm7",0xaa);
+	&pshufd		("xmm7","xmm7",0xff);
+	&movdqa		(&QWP(16*8-128,"ebp"),"xmm0");
+	&movdqa		(&QWP(16*9-128,"ebp"),"xmm1");
+	&movdqa		(&QWP(16*10-128,"ebp"),"xmm2");
+	&movdqa		(&QWP(16*11-128,"ebp"),"xmm3");
+	&movdqa		(&QWP(16*0-128,"ebp"),"xmm4");
+	&movdqa		(&QWP(16*1-128,"ebp"),"xmm5");
+	&movdqa		(&QWP(16*2-128,"ebp"),"xmm6");
+	&movdqa		(&QWP(16*3-128,"ebp"),"xmm7");
+
+	&lea		($inp,&DWP(128,$inp));		# size optimization
+	&lea		($out,&DWP(128,$out));		# size optimization
+	&jmp		(&label("outer_loop"));
+
+&set_label("outer_loop",16);
+	#&movdqa	("xmm0",&QWP(16*0-128,"ebp"));	# copy key material
+	&movdqa		("xmm1",&QWP(16*1-128,"ebp"));
+	&movdqa		("xmm2",&QWP(16*2-128,"ebp"));
+	&movdqa		("xmm3",&QWP(16*3-128,"ebp"));
+	#&movdqa	("xmm4",&QWP(16*4-128,"ebp"));
+	&movdqa		("xmm5",&QWP(16*5-128,"ebp"));
+	&movdqa		("xmm6",&QWP(16*6-128,"ebp"));
+	&movdqa		("xmm7",&QWP(16*7-128,"ebp"));
+	#&movdqa	(&QWP(16*0-128,"ebx"),"xmm0");
+	&movdqa		(&QWP(16*1-128,"ebx"),"xmm1");
+	&movdqa		(&QWP(16*2-128,"ebx"),"xmm2");
+	&movdqa		(&QWP(16*3-128,"ebx"),"xmm3");
+	#&movdqa	(&QWP(16*4-128,"ebx"),"xmm4");
+	&movdqa		(&QWP(16*5-128,"ebx"),"xmm5");
+	&movdqa		(&QWP(16*6-128,"ebx"),"xmm6");
+	&movdqa		(&QWP(16*7-128,"ebx"),"xmm7");
+	#&movdqa	("xmm0",&QWP(16*8-128,"ebp"));
+	#&movdqa	("xmm1",&QWP(16*9-128,"ebp"));
+	&movdqa		("xmm2",&QWP(16*10-128,"ebp"));
+	&movdqa		("xmm3",&QWP(16*11-128,"ebp"));
+	&movdqa		("xmm4",&QWP(16*12-128,"ebp"));
+	&movdqa		("xmm5",&QWP(16*13-128,"ebp"));
+	&movdqa		("xmm6",&QWP(16*14-128,"ebp"));
+	&movdqa		("xmm7",&QWP(16*15-128,"ebp"));
+	&paddd		("xmm4",&QWP(16*4,"eax"));	# counter value
+	#&movdqa	(&QWP(16*8-128,"ebx"),"xmm0");
+	#&movdqa	(&QWP(16*9-128,"ebx"),"xmm1");
+	&movdqa		(&QWP(16*10-128,"ebx"),"xmm2");
+	&movdqa		(&QWP(16*11-128,"ebx"),"xmm3");
+	&movdqa		(&QWP(16*12-128,"ebx"),"xmm4");
+	&movdqa		(&QWP(16*13-128,"ebx"),"xmm5");
+	&movdqa		(&QWP(16*14-128,"ebx"),"xmm6");
+	&movdqa		(&QWP(16*15-128,"ebx"),"xmm7");
+	&movdqa		(&QWP(16*12-128,"ebp"),"xmm4");	# save counter value
+
+	&movdqa		($xa, &QWP(16*0-128,"ebp"));
+	&movdqa		($xd, "xmm4");
+	&movdqa		($xb_,&QWP(16*4-128,"ebp"));
+	&movdqa		($xc, &QWP(16*8-128,"ebp"));
+	&movdqa		($xc_,&QWP(16*9-128,"ebp"));
+
+	&mov		("edx",10);			# loop counter
+	&nop		();
+
+&set_label("loop",16);
+	&paddd		($xa,$xb_);			# elsewhere
+	&movdqa		($xb,$xb_);
+	&pxor		($xd,$xa);			# elsewhere
+	&QUARTERROUND_SSSE3(0, 4, 8, 12, 0);
+	&QUARTERROUND_SSSE3(1, 5, 9, 13, 1);
+	&QUARTERROUND_SSSE3(2, 6,10, 14, 2);
+	&QUARTERROUND_SSSE3(3, 7,11, 15, 3);
+	&QUARTERROUND_SSSE3(0, 5,10, 15, 4);
+	&QUARTERROUND_SSSE3(1, 6,11, 12, 5);
+	&QUARTERROUND_SSSE3(2, 7, 8, 13, 6);
+	&QUARTERROUND_SSSE3(3, 4, 9, 14, 7);
+	&dec		("edx");
+	&jnz		(&label("loop"));
+
+	&movdqa		(&QWP(16*4-128,"ebx"),$xb_);
+	&movdqa		(&QWP(16*8-128,"ebx"),$xc);
+	&movdqa		(&QWP(16*9-128,"ebx"),$xc_);
+	&movdqa		(&QWP(16*12-128,"ebx"),$xd);
+	&movdqa		(&QWP(16*14-128,"ebx"),$xd_);
+
+    my ($xa0,$xa1,$xa2,$xa3,$xt0,$xt1,$xt2,$xt3)=map("xmm$_",(0..7));
+
+	#&movdqa	($xa0,&QWP(16*0-128,"ebx"));	# it's there
+	&movdqa		($xa1,&QWP(16*1-128,"ebx"));
+	&movdqa		($xa2,&QWP(16*2-128,"ebx"));
+	&movdqa		($xa3,&QWP(16*3-128,"ebx"));
+
+    for($i=0;$i<256;$i+=64) {
+	&paddd		($xa0,&QWP($i+16*0-128,"ebp"));	# accumulate key material
+	&paddd		($xa1,&QWP($i+16*1-128,"ebp"));
+	&paddd		($xa2,&QWP($i+16*2-128,"ebp"));
+	&paddd		($xa3,&QWP($i+16*3-128,"ebp"));
+
+	&movdqa		($xt2,$xa0);		# "de-interlace" data
+	&punpckldq	($xa0,$xa1);
+	&movdqa		($xt3,$xa2);
+	&punpckldq	($xa2,$xa3);
+	&punpckhdq	($xt2,$xa1);
+	&punpckhdq	($xt3,$xa3);
+	&movdqa		($xa1,$xa0);
+	&punpcklqdq	($xa0,$xa2);		# "a0"
+	&movdqa		($xa3,$xt2);
+	&punpcklqdq	($xt2,$xt3);		# "a2"
+	&punpckhqdq	($xa1,$xa2);		# "a1"
+	&punpckhqdq	($xa3,$xt3);		# "a3"
+
+	#($xa2,$xt2)=($xt2,$xa2);
+
+	&movdqu		($xt0,&QWP(64*0-128,$inp));	# load input
+	&movdqu		($xt1,&QWP(64*1-128,$inp));
+	&movdqu		($xa2,&QWP(64*2-128,$inp));
+	&movdqu		($xt3,&QWP(64*3-128,$inp));
+	&lea		($inp,&QWP($i<192?16:(64*4-16*3),$inp));
+	&pxor		($xt0,$xa0);
+	&movdqa		($xa0,&QWP($i+16*4-128,"ebx"))	if ($i<192);
+	&pxor		($xt1,$xa1);
+	&movdqa		($xa1,&QWP($i+16*5-128,"ebx"))	if ($i<192);
+	&pxor		($xt2,$xa2);
+	&movdqa		($xa2,&QWP($i+16*6-128,"ebx"))	if ($i<192);
+	&pxor		($xt3,$xa3);
+	&movdqa		($xa3,&QWP($i+16*7-128,"ebx"))	if ($i<192);
+	&movdqu		(&QWP(64*0-128,$out),$xt0);	# store output
+	&movdqu		(&QWP(64*1-128,$out),$xt1);
+	&movdqu		(&QWP(64*2-128,$out),$xt2);
+	&movdqu		(&QWP(64*3-128,$out),$xt3);
+	&lea		($out,&QWP($i<192?16:(64*4-16*3),$out));
+    }
+	&sub		($len,64*4);
+	&jnc		(&label("outer_loop"));
+
+	&add		($len,64*4);
+	&jz		(&label("done"));
+
+	&mov		("ebx",&DWP(512+8,"esp"));	# restore pointers
+	&lea		($inp,&DWP(-128,$inp));
+	&mov		("edx",&DWP(512+4,"esp"));
+	&lea		($out,&DWP(-128,$out));
+
+	&movd		("xmm2",&DWP(16*12-128,"ebp"));	# counter value
+	&movdqu		("xmm3",&QWP(0,"ebx"));
+	&paddd		("xmm2",&QWP(16*6,"eax"));	# +four
+	&pand		("xmm3",&QWP(16*7,"eax"));
+	&por		("xmm3","xmm2");		# counter value
+}
+{
+my ($a,$b,$c,$d,$t,$t1,$rot16,$rot24)=map("xmm$_",(0..7));
+
+sub SSSE3ROUND {	# critical path is 20 "SIMD ticks" per round
+	&paddd		($a,$b);
+	&pxor		($d,$a);
+	&pshufb		($d,$rot16);
+
+	&paddd		($c,$d);
+	&pxor		($b,$c);
+	&movdqa		($t,$b);
+	&psrld		($b,20);
+	&pslld		($t,12);
+	&por		($b,$t);
+
+	&paddd		($a,$b);
+	&pxor		($d,$a);
+	&pshufb		($d,$rot24);
+
+	&paddd		($c,$d);
+	&pxor		($b,$c);
+	&movdqa		($t,$b);
+	&psrld		($b,25);
+	&pslld		($t,7);
+	&por		($b,$t);
+}
+
+&set_label("1x");
+	&movdqa		($a,&QWP(16*2,"eax"));		# sigma
+	&movdqu		($b,&QWP(0,"edx"));
+	&movdqu		($c,&QWP(16,"edx"));
+	#&movdqu	($d,&QWP(0,"ebx"));		# already loaded
+	&movdqa		($rot16,&QWP(0,"eax"));
+	&movdqa		($rot24,&QWP(16,"eax"));
+	&mov		(&DWP(16*3,"esp"),"ebp");
+
+	&movdqa		(&QWP(16*0,"esp"),$a);
+	&movdqa		(&QWP(16*1,"esp"),$b);
+	&movdqa		(&QWP(16*2,"esp"),$c);
+	&movdqa		(&QWP(16*3,"esp"),$d);
+	&mov		("edx",10);
+	&jmp		(&label("loop1x"));
+
+&set_label("outer1x",16);
+	&movdqa		($d,&QWP(16*5,"eax"));		# one
+	&movdqa		($a,&QWP(16*0,"esp"));
+	&movdqa		($b,&QWP(16*1,"esp"));
+	&movdqa		($c,&QWP(16*2,"esp"));
+	&paddd		($d,&QWP(16*3,"esp"));
+	&mov		("edx",10);
+	&movdqa		(&QWP(16*3,"esp"),$d);
+	&jmp		(&label("loop1x"));
+
+&set_label("loop1x",16);
+	&SSSE3ROUND();
+	&pshufd	($c,$c,0b01001110);
+	&pshufd	($b,$b,0b00111001);
+	&pshufd	($d,$d,0b10010011);
+	&nop	();
+
+	&SSSE3ROUND();
+	&pshufd	($c,$c,0b01001110);
+	&pshufd	($b,$b,0b10010011);
+	&pshufd	($d,$d,0b00111001);
+
+	&dec		("edx");
+	&jnz		(&label("loop1x"));
+
+	&paddd		($a,&QWP(16*0,"esp"));
+	&paddd		($b,&QWP(16*1,"esp"));
+	&paddd		($c,&QWP(16*2,"esp"));
+	&paddd		($d,&QWP(16*3,"esp"));
+
+	&cmp		($len,64);
+	&jb		(&label("tail"));
+
+	&movdqu		($t,&QWP(16*0,$inp));
+	&movdqu		($t1,&QWP(16*1,$inp));
+	&pxor		($a,$t);		# xor with input
+	&movdqu		($t,&QWP(16*2,$inp));
+	&pxor		($b,$t1);
+	&movdqu		($t1,&QWP(16*3,$inp));
+	&pxor		($c,$t);
+	&pxor		($d,$t1);
+	&lea		($inp,&DWP(16*4,$inp));	# inp+=64
+
+	&movdqu		(&QWP(16*0,$out),$a);	# write output
+	&movdqu		(&QWP(16*1,$out),$b);
+	&movdqu		(&QWP(16*2,$out),$c);
+	&movdqu		(&QWP(16*3,$out),$d);
+	&lea		($out,&DWP(16*4,$out));	# inp+=64
+
+	&sub		($len,64);
+	&jnz		(&label("outer1x"));
+
+	&jmp		(&label("done"));
+
+&set_label("tail");
+	&movdqa		(&QWP(16*0,"esp"),$a);
+	&movdqa		(&QWP(16*1,"esp"),$b);
+	&movdqa		(&QWP(16*2,"esp"),$c);
+	&movdqa		(&QWP(16*3,"esp"),$d);
+
+	&xor		("eax","eax");
+	&xor		("edx","edx");
+	&xor		("ebp","ebp");
+
+&set_label("tail_loop");
+	&movb		("al",&BP(0,"esp","ebp"));
+	&movb		("dl",&BP(0,$inp,"ebp"));
+	&lea		("ebp",&DWP(1,"ebp"));
+	&xor		("al","dl");
+	&movb		(&BP(-1,$out,"ebp"),"al");
+	&dec		($len);
+	&jnz		(&label("tail_loop"));
+}
+&set_label("done");
+	&mov		("esp",&DWP(512,"esp"));
+&function_end("ChaCha20_ssse3");
+
+&align	(64);
+&set_label("ssse3_data");
+&data_byte(0x2,0x3,0x0,0x1, 0x6,0x7,0x4,0x5, 0xa,0xb,0x8,0x9, 0xe,0xf,0xc,0xd);
+&data_byte(0x3,0x0,0x1,0x2, 0x7,0x4,0x5,0x6, 0xb,0x8,0x9,0xa, 0xf,0xc,0xd,0xe);
+&data_word(0x61707865,0x3320646e,0x79622d32,0x6b206574);
+&data_word(0,1,2,3);
+&data_word(4,4,4,4);
+&data_word(1,0,0,0);
+&data_word(4,0,0,0);
+&data_word(0,-1,-1,-1);
+&align	(64);
+}
+&asciz	("ChaCha20 for x86, CRYPTOGAMS by ");
+
+if ($ymm) {
+my ($xa,$xa_,$xb,$xb_,$xc,$xc_,$xd,$xd_)=map("xmm$_",(0..7));
+my ($out,$inp,$len)=("edi","esi","ecx");
+
+sub QUARTERROUND_XOP {
+my ($ai,$bi,$ci,$di,$i)=@_;
+my ($an,$bn,$cn,$dn)=map(($_&~3)+(($_+1)&3),($ai,$bi,$ci,$di));	# next
+my ($ap,$bp,$cp,$dp)=map(($_&~3)+(($_-1)&3),($ai,$bi,$ci,$di));	# previous
+
+	#       a   b   c   d
+	#
+	#       0   4   8  12 < even round
+	#       1   5   9  13
+	#       2   6  10  14
+	#       3   7  11  15
+	#       0   5  10  15 < odd round
+	#       1   6  11  12
+	#       2   7   8  13
+	#       3   4   9  14
+
+	if ($i==0) {
+            my $j=4;
+	    ($ap,$bp,$cp,$dp)=map(($_&~3)+(($_-$j--)&3),($ap,$bp,$cp,$dp));
+	} elsif ($i==3) {
+            my $j=0;
+	    ($an,$bn,$cn,$dn)=map(($_&~3)+(($_+$j++)&3),($an,$bn,$cn,$dn));
+	} elsif ($i==4) {
+            my $j=4;
+	    ($ap,$bp,$cp,$dp)=map(($_&~3)+(($_+$j--)&3),($ap,$bp,$cp,$dp));
+	} elsif ($i==7) {
+            my $j=0;
+	    ($an,$bn,$cn,$dn)=map(($_&~3)+(($_-$j++)&3),($an,$bn,$cn,$dn));
+	}
+
+	#&vpaddd	($xa,$xa,$xb);			# see elsewhere
+	#&vpxor		($xd,$xd,$xa);			# see elsewhere
+	 &vmovdqa	(&QWP(16*$cp-128,"ebx"),$xc_)	if ($ai>0 && $ai<3);
+	&vprotd		($xd,$xd,16);
+	 &vmovdqa	(&QWP(16*$bp-128,"ebx"),$xb_)	if ($i!=0);
+	&vpaddd		($xc,$xc,$xd);
+	 &vmovdqa	($xc_,&QWP(16*$cn-128,"ebx"))	if ($ai>0 && $ai<3);
+	&vpxor		($xb,$i!=0?$xb:$xb_,$xc);
+	 &vmovdqa	($xa_,&QWP(16*$an-128,"ebx"));
+	&vprotd		($xb,$xb,12);
+	 &vmovdqa	($xb_,&QWP(16*$bn-128,"ebx"))	if ($i<7);
+	&vpaddd		($xa,$xa,$xb);
+	 &vmovdqa	($xd_,&QWP(16*$dn-128,"ebx"))	if ($di!=$dn);
+	&vpxor		($xd,$xd,$xa);
+	 &vpaddd	($xa_,$xa_,$xb_)		if ($i<7);	# elsewhere
+	&vprotd		($xd,$xd,8);
+	&vmovdqa	(&QWP(16*$ai-128,"ebx"),$xa);
+	&vpaddd		($xc,$xc,$xd);
+	&vmovdqa	(&QWP(16*$di-128,"ebx"),$xd)	if ($di!=$dn);
+	&vpxor		($xb,$xb,$xc);
+	 &vpxor		($xd_,$di==$dn?$xd:$xd_,$xa_)	if ($i<7);	# elsewhere
+	&vprotd		($xb,$xb,7);
+
+	($xa,$xa_)=($xa_,$xa);
+	($xb,$xb_)=($xb_,$xb);
+	($xc,$xc_)=($xc_,$xc);
+	($xd,$xd_)=($xd_,$xd);
+}
+
+&function_begin("ChaCha20_xop");
+&set_label("xop_shortcut");
+	&mov		($out,&wparam(0));
+	&mov		($inp,&wparam(1));
+	&mov		($len,&wparam(2));
+	&mov		("edx",&wparam(3));		# key
+	&mov		("ebx",&wparam(4));		# counter and nonce
+	&vzeroupper	();
+
+	&mov		("ebp","esp");
+	&stack_push	(131);
+	&and		("esp",-64);
+	&mov		(&DWP(512,"esp"),"ebp");
+
+	&lea		("eax",&DWP(&label("ssse3_data")."-".
+				    &label("pic_point"),"eax"));
+	&vmovdqu	("xmm3",&QWP(0,"ebx"));		# counter and nonce
+
+	&cmp		($len,64*4);
+	&jb		(&label("1x"));
+
+	&mov		(&DWP(512+4,"esp"),"edx");	# offload pointers
+	&mov		(&DWP(512+8,"esp"),"ebx");
+	&sub		($len,64*4);			# bias len
+	&lea		("ebp",&DWP(256+128,"esp"));	# size optimization
+
+	&vmovdqu	("xmm7",&QWP(0,"edx"));		# key
+	&vpshufd	("xmm0","xmm3",0x00);
+	&vpshufd	("xmm1","xmm3",0x55);
+	&vpshufd	("xmm2","xmm3",0xaa);
+	&vpshufd	("xmm3","xmm3",0xff);
+	 &vpaddd	("xmm0","xmm0",&QWP(16*3,"eax"));	# fix counters
+	&vpshufd	("xmm4","xmm7",0x00);
+	&vpshufd	("xmm5","xmm7",0x55);
+	 &vpsubd	("xmm0","xmm0",&QWP(16*4,"eax"));
+	&vpshufd	("xmm6","xmm7",0xaa);
+	&vpshufd	("xmm7","xmm7",0xff);
+	&vmovdqa	(&QWP(16*12-128,"ebp"),"xmm0");
+	&vmovdqa	(&QWP(16*13-128,"ebp"),"xmm1");
+	&vmovdqa	(&QWP(16*14-128,"ebp"),"xmm2");
+	&vmovdqa	(&QWP(16*15-128,"ebp"),"xmm3");
+	 &vmovdqu	("xmm3",&QWP(16,"edx"));	# key
+	&vmovdqa	(&QWP(16*4-128,"ebp"),"xmm4");
+	&vmovdqa	(&QWP(16*5-128,"ebp"),"xmm5");
+	&vmovdqa	(&QWP(16*6-128,"ebp"),"xmm6");
+	&vmovdqa	(&QWP(16*7-128,"ebp"),"xmm7");
+	 &vmovdqa	("xmm7",&QWP(16*2,"eax"));	# sigma
+	 &lea		("ebx",&DWP(128,"esp"));	# size optimization
+
+	&vpshufd	("xmm0","xmm3",0x00);
+	&vpshufd	("xmm1","xmm3",0x55);
+	&vpshufd	("xmm2","xmm3",0xaa);
+	&vpshufd	("xmm3","xmm3",0xff);
+	&vpshufd	("xmm4","xmm7",0x00);
+	&vpshufd	("xmm5","xmm7",0x55);
+	&vpshufd	("xmm6","xmm7",0xaa);
+	&vpshufd	("xmm7","xmm7",0xff);
+	&vmovdqa	(&QWP(16*8-128,"ebp"),"xmm0");
+	&vmovdqa	(&QWP(16*9-128,"ebp"),"xmm1");
+	&vmovdqa	(&QWP(16*10-128,"ebp"),"xmm2");
+	&vmovdqa	(&QWP(16*11-128,"ebp"),"xmm3");
+	&vmovdqa	(&QWP(16*0-128,"ebp"),"xmm4");
+	&vmovdqa	(&QWP(16*1-128,"ebp"),"xmm5");
+	&vmovdqa	(&QWP(16*2-128,"ebp"),"xmm6");
+	&vmovdqa	(&QWP(16*3-128,"ebp"),"xmm7");
+
+	&lea		($inp,&DWP(128,$inp));		# size optimization
+	&lea		($out,&DWP(128,$out));		# size optimization
+	&jmp		(&label("outer_loop"));
+
+&set_label("outer_loop",32);
+	#&vmovdqa	("xmm0",&QWP(16*0-128,"ebp"));	# copy key material
+	&vmovdqa	("xmm1",&QWP(16*1-128,"ebp"));
+	&vmovdqa	("xmm2",&QWP(16*2-128,"ebp"));
+	&vmovdqa	("xmm3",&QWP(16*3-128,"ebp"));
+	#&vmovdqa	("xmm4",&QWP(16*4-128,"ebp"));
+	&vmovdqa	("xmm5",&QWP(16*5-128,"ebp"));
+	&vmovdqa	("xmm6",&QWP(16*6-128,"ebp"));
+	&vmovdqa	("xmm7",&QWP(16*7-128,"ebp"));
+	#&vmovdqa	(&QWP(16*0-128,"ebx"),"xmm0");
+	&vmovdqa	(&QWP(16*1-128,"ebx"),"xmm1");
+	&vmovdqa	(&QWP(16*2-128,"ebx"),"xmm2");
+	&vmovdqa	(&QWP(16*3-128,"ebx"),"xmm3");
+	#&vmovdqa	(&QWP(16*4-128,"ebx"),"xmm4");
+	&vmovdqa	(&QWP(16*5-128,"ebx"),"xmm5");
+	&vmovdqa	(&QWP(16*6-128,"ebx"),"xmm6");
+	&vmovdqa	(&QWP(16*7-128,"ebx"),"xmm7");
+	#&vmovdqa	("xmm0",&QWP(16*8-128,"ebp"));
+	#&vmovdqa	("xmm1",&QWP(16*9-128,"ebp"));
+	&vmovdqa	("xmm2",&QWP(16*10-128,"ebp"));
+	&vmovdqa	("xmm3",&QWP(16*11-128,"ebp"));
+	&vmovdqa	("xmm4",&QWP(16*12-128,"ebp"));
+	&vmovdqa	("xmm5",&QWP(16*13-128,"ebp"));
+	&vmovdqa	("xmm6",&QWP(16*14-128,"ebp"));
+	&vmovdqa	("xmm7",&QWP(16*15-128,"ebp"));
+	&vpaddd		("xmm4","xmm4",&QWP(16*4,"eax"));	# counter value
+	#&vmovdqa	(&QWP(16*8-128,"ebx"),"xmm0");
+	#&vmovdqa	(&QWP(16*9-128,"ebx"),"xmm1");
+	&vmovdqa	(&QWP(16*10-128,"ebx"),"xmm2");
+	&vmovdqa	(&QWP(16*11-128,"ebx"),"xmm3");
+	&vmovdqa	(&QWP(16*12-128,"ebx"),"xmm4");
+	&vmovdqa	(&QWP(16*13-128,"ebx"),"xmm5");
+	&vmovdqa	(&QWP(16*14-128,"ebx"),"xmm6");
+	&vmovdqa	(&QWP(16*15-128,"ebx"),"xmm7");
+	&vmovdqa	(&QWP(16*12-128,"ebp"),"xmm4");	# save counter value
+
+	&vmovdqa	($xa, &QWP(16*0-128,"ebp"));
+	&vmovdqa	($xd, "xmm4");
+	&vmovdqa	($xb_,&QWP(16*4-128,"ebp"));
+	&vmovdqa	($xc, &QWP(16*8-128,"ebp"));
+	&vmovdqa	($xc_,&QWP(16*9-128,"ebp"));
+
+	&mov		("edx",10);			# loop counter
+	&nop		();
+
+&set_label("loop",32);
+	&vpaddd		($xa,$xa,$xb_);			# elsewhere
+	&vpxor		($xd,$xd,$xa);			# elsewhere
+	&QUARTERROUND_XOP(0, 4, 8, 12, 0);
+	&QUARTERROUND_XOP(1, 5, 9, 13, 1);
+	&QUARTERROUND_XOP(2, 6,10, 14, 2);
+	&QUARTERROUND_XOP(3, 7,11, 15, 3);
+	&QUARTERROUND_XOP(0, 5,10, 15, 4);
+	&QUARTERROUND_XOP(1, 6,11, 12, 5);
+	&QUARTERROUND_XOP(2, 7, 8, 13, 6);
+	&QUARTERROUND_XOP(3, 4, 9, 14, 7);
+	&dec		("edx");
+	&jnz		(&label("loop"));
+
+	&vmovdqa	(&QWP(16*4-128,"ebx"),$xb_);
+	&vmovdqa	(&QWP(16*8-128,"ebx"),$xc);
+	&vmovdqa	(&QWP(16*9-128,"ebx"),$xc_);
+	&vmovdqa	(&QWP(16*12-128,"ebx"),$xd);
+	&vmovdqa	(&QWP(16*14-128,"ebx"),$xd_);
+
+    my ($xa0,$xa1,$xa2,$xa3,$xt0,$xt1,$xt2,$xt3)=map("xmm$_",(0..7));
+
+	#&vmovdqa	($xa0,&QWP(16*0-128,"ebx"));	# it's there
+	&vmovdqa	($xa1,&QWP(16*1-128,"ebx"));
+	&vmovdqa	($xa2,&QWP(16*2-128,"ebx"));
+	&vmovdqa	($xa3,&QWP(16*3-128,"ebx"));
+
+    for($i=0;$i<256;$i+=64) {
+	&vpaddd		($xa0,$xa0,&QWP($i+16*0-128,"ebp"));	# accumulate key material
+	&vpaddd		($xa1,$xa1,&QWP($i+16*1-128,"ebp"));
+	&vpaddd		($xa2,$xa2,&QWP($i+16*2-128,"ebp"));
+	&vpaddd		($xa3,$xa3,&QWP($i+16*3-128,"ebp"));
+
+	&vpunpckldq	($xt2,$xa0,$xa1);	# "de-interlace" data
+	&vpunpckldq	($xt3,$xa2,$xa3);
+	&vpunpckhdq	($xa0,$xa0,$xa1);
+	&vpunpckhdq	($xa2,$xa2,$xa3);
+	&vpunpcklqdq	($xa1,$xt2,$xt3);	# "a0"
+	&vpunpckhqdq	($xt2,$xt2,$xt3);	# "a1"
+	&vpunpcklqdq	($xt3,$xa0,$xa2);	# "a2"
+	&vpunpckhqdq	($xa3,$xa0,$xa2);	# "a3"
+
+	&vpxor		($xt0,$xa1,&QWP(64*0-128,$inp));
+	&vpxor		($xt1,$xt2,&QWP(64*1-128,$inp));
+	&vpxor		($xt2,$xt3,&QWP(64*2-128,$inp));
+	&vpxor		($xt3,$xa3,&QWP(64*3-128,$inp));
+	&lea		($inp,&QWP($i<192?16:(64*4-16*3),$inp));
+	&vmovdqa	($xa0,&QWP($i+16*4-128,"ebx"))	if ($i<192);
+	&vmovdqa	($xa1,&QWP($i+16*5-128,"ebx"))	if ($i<192);
+	&vmovdqa	($xa2,&QWP($i+16*6-128,"ebx"))	if ($i<192);
+	&vmovdqa	($xa3,&QWP($i+16*7-128,"ebx"))	if ($i<192);
+	&vmovdqu	(&QWP(64*0-128,$out),$xt0);	# store output
+	&vmovdqu	(&QWP(64*1-128,$out),$xt1);
+	&vmovdqu	(&QWP(64*2-128,$out),$xt2);
+	&vmovdqu	(&QWP(64*3-128,$out),$xt3);
+	&lea		($out,&QWP($i<192?16:(64*4-16*3),$out));
+    }
+	&sub		($len,64*4);
+	&jnc		(&label("outer_loop"));
+
+	&add		($len,64*4);
+	&jz		(&label("done"));
+
+	&mov		("ebx",&DWP(512+8,"esp"));	# restore pointers
+	&lea		($inp,&DWP(-128,$inp));
+	&mov		("edx",&DWP(512+4,"esp"));
+	&lea		($out,&DWP(-128,$out));
+
+	&vmovd		("xmm2",&DWP(16*12-128,"ebp"));	# counter value
+	&vmovdqu	("xmm3",&QWP(0,"ebx"));
+	&vpaddd		("xmm2","xmm2",&QWP(16*6,"eax"));# +four
+	&vpand		("xmm3","xmm3",&QWP(16*7,"eax"));
+	&vpor		("xmm3","xmm3","xmm2");		# counter value
+{
+my ($a,$b,$c,$d,$t,$t1,$rot16,$rot24)=map("xmm$_",(0..7));
+
+sub XOPROUND {
+	&vpaddd		($a,$a,$b);
+	&vpxor		($d,$d,$a);
+	&vprotd		($d,$d,16);
+
+	&vpaddd		($c,$c,$d);
+	&vpxor		($b,$b,$c);
+	&vprotd		($b,$b,12);
+
+	&vpaddd		($a,$a,$b);
+	&vpxor		($d,$d,$a);
+	&vprotd		($d,$d,8);
+
+	&vpaddd		($c,$c,$d);
+	&vpxor		($b,$b,$c);
+	&vprotd		($b,$b,7);
+}
+
+&set_label("1x");
+	&vmovdqa	($a,&QWP(16*2,"eax"));		# sigma
+	&vmovdqu	($b,&QWP(0,"edx"));
+	&vmovdqu	($c,&QWP(16,"edx"));
+	#&vmovdqu	($d,&QWP(0,"ebx"));		# already loaded
+	&vmovdqa	($rot16,&QWP(0,"eax"));
+	&vmovdqa	($rot24,&QWP(16,"eax"));
+	&mov		(&DWP(16*3,"esp"),"ebp");
+
+	&vmovdqa	(&QWP(16*0,"esp"),$a);
+	&vmovdqa	(&QWP(16*1,"esp"),$b);
+	&vmovdqa	(&QWP(16*2,"esp"),$c);
+	&vmovdqa	(&QWP(16*3,"esp"),$d);
+	&mov		("edx",10);
+	&jmp		(&label("loop1x"));
+
+&set_label("outer1x",16);
+	&vmovdqa	($d,&QWP(16*5,"eax"));		# one
+	&vmovdqa	($a,&QWP(16*0,"esp"));
+	&vmovdqa	($b,&QWP(16*1,"esp"));
+	&vmovdqa	($c,&QWP(16*2,"esp"));
+	&vpaddd		($d,$d,&QWP(16*3,"esp"));
+	&mov		("edx",10);
+	&vmovdqa	(&QWP(16*3,"esp"),$d);
+	&jmp		(&label("loop1x"));
+
+&set_label("loop1x",16);
+	&XOPROUND();
+	&vpshufd	($c,$c,0b01001110);
+	&vpshufd	($b,$b,0b00111001);
+	&vpshufd	($d,$d,0b10010011);
+
+	&XOPROUND();
+	&vpshufd	($c,$c,0b01001110);
+	&vpshufd	($b,$b,0b10010011);
+	&vpshufd	($d,$d,0b00111001);
+
+	&dec		("edx");
+	&jnz		(&label("loop1x"));
+
+	&vpaddd		($a,$a,&QWP(16*0,"esp"));
+	&vpaddd		($b,$b,&QWP(16*1,"esp"));
+	&vpaddd		($c,$c,&QWP(16*2,"esp"));
+	&vpaddd		($d,$d,&QWP(16*3,"esp"));
+
+	&cmp		($len,64);
+	&jb		(&label("tail"));
+
+	&vpxor		($a,$a,&QWP(16*0,$inp));	# xor with input
+	&vpxor		($b,$b,&QWP(16*1,$inp));
+	&vpxor		($c,$c,&QWP(16*2,$inp));
+	&vpxor		($d,$d,&QWP(16*3,$inp));
+	&lea		($inp,&DWP(16*4,$inp));		# inp+=64
+
+	&vmovdqu	(&QWP(16*0,$out),$a);		# write output
+	&vmovdqu	(&QWP(16*1,$out),$b);
+	&vmovdqu	(&QWP(16*2,$out),$c);
+	&vmovdqu	(&QWP(16*3,$out),$d);
+	&lea		($out,&DWP(16*4,$out));		# inp+=64
+
+	&sub		($len,64);
+	&jnz		(&label("outer1x"));
+
+	&jmp		(&label("done"));
+
+&set_label("tail");
+	&vmovdqa	(&QWP(16*0,"esp"),$a);
+	&vmovdqa	(&QWP(16*1,"esp"),$b);
+	&vmovdqa	(&QWP(16*2,"esp"),$c);
+	&vmovdqa	(&QWP(16*3,"esp"),$d);
+
+	&xor		("eax","eax");
+	&xor		("edx","edx");
+	&xor		("ebp","ebp");
+
+&set_label("tail_loop");
+	&movb		("al",&BP(0,"esp","ebp"));
+	&movb		("dl",&BP(0,$inp,"ebp"));
+	&lea		("ebp",&DWP(1,"ebp"));
+	&xor		("al","dl");
+	&movb		(&BP(-1,$out,"ebp"),"al");
+	&dec		($len);
+	&jnz		(&label("tail_loop"));
+}
+&set_label("done");
+	&vzeroupper	();
+	&mov		("esp",&DWP(512,"esp"));
+&function_end("ChaCha20_xop");
+}
+
+&asm_finish();
+
+close STDOUT;
diff --git a/crypto/chacha/asm/chacha-x86_64.pl b/crypto/chacha/asm/chacha-x86_64.pl
new file mode 100755
index 000000000..347dfcb3e
--- /dev/null
+++ b/crypto/chacha/asm/chacha-x86_64.pl
@@ -0,0 +1,2245 @@
+#! /usr/bin/env perl
+# Copyright 2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+#
+# ====================================================================
+# Written by Andy Polyakov  for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+#
+# November 2014
+#
+# ChaCha20 for x86_64.
+#
+# Performance in cycles per byte out of large buffer.
+#
+#		IALU/gcc 4.8(i)	1xSSSE3/SSE2	4xSSSE3	    8xAVX2
+#
+# P4		9.48/+99%	-/22.7(ii)	-
+# Core2		7.83/+55%	7.90/8.08	4.35
+# Westmere	7.19/+50%	5.60/6.70	3.00
+# Sandy Bridge	8.31/+42%	5.45/6.76	2.72
+# Ivy Bridge	6.71/+46%	5.40/6.49	2.41
+# Haswell	5.92/+43%	5.20/6.45	2.42	    1.23
+# Silvermont	12.0/+33%	7.75/7.40	7.03(iii)
+# Goldmont	10.6/+17%	5.10/-		3.28
+# Sledgehammer	7.28/+52%	-/14.2(ii)	-
+# Bulldozer	9.66/+28%	9.85/11.1	3.06(iv)
+# VIA Nano	10.5/+46%	6.72/8.60	6.05
+#
+# (i)	compared to older gcc 3.x one can observe >2x improvement on
+#	most platforms;
+# (ii)	as it can be seen, SSE2 performance is too low on legacy
+#	processors; NxSSE2 results are naturally better, but not
+#	impressively better than IALU ones, which is why you won't
+#	find SSE2 code below;
+# (iii)	this is not optimal result for Atom because of MSROM
+#	limitations, SSE2 can do better, but gain is considered too
+#	low to justify the [maintenance] effort;
+# (iv)	Bulldozer actually executes 4xXOP code path that delivers 2.20;
+
+$flavour = shift;
+$output  = shift;
+if ($flavour =~ /\./) { $output = $flavour; undef $flavour; }
+
+$win64=0; $win64=1 if ($flavour =~ /[nm]asm|mingw64/ || $output =~ /\.asm$/);
+
+$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+( $xlate="${dir}x86_64-xlate.pl" and -f $xlate ) or
+( $xlate="${dir}../../perlasm/x86_64-xlate.pl" and -f $xlate) or
+die "can't locate x86_64-xlate.pl";
+
+if (`$ENV{CC} -Wa,-v -c -o /dev/null -x assembler /dev/null 2>&1`
+		=~ /GNU assembler version ([2-9]\.[0-9]+)/) {
+	$avx = ($1>=2.19) + ($1>=2.22);
+}
+
+if (!$avx && $win64 && ($flavour =~ /nasm/ || $ENV{ASM} =~ /nasm/) &&
+	   `nasm -v 2>&1` =~ /NASM version ([2-9]\.[0-9]+)/) {
+	$avx = ($1>=2.09) + ($1>=2.10);
+}
+
+if (!$avx && $win64 && ($flavour =~ /masm/ || $ENV{ASM} =~ /ml64/) &&
+	   `ml64 2>&1` =~ /Version ([0-9]+)\./) {
+	$avx = ($1>=10) + ($1>=11);
+}
+
+if (!$avx && `$ENV{CC} -v 2>&1` =~ /((?:^clang|LLVM) version|.*based on LLVM) ([3-9]\.[0-9]+)/) {
+	$avx = ($2>=3.0) + ($2>3.0);
+}
+
+open OUT,"| \"$^X\" \"$xlate\" $flavour \"$output\"";
+*STDOUT=*OUT;
+
+# input parameter block
+($out,$inp,$len,$key,$counter)=("%rdi","%rsi","%rdx","%rcx","%r8");
+
+$code.=<<___;
+.text
+
+.extern OPENSSL_ia32cap_P
+
+.align	64
+.Lzero:
+.long	0,0,0,0
+.Lone:
+.long	1,0,0,0
+.Linc:
+.long	0,1,2,3
+.Lfour:
+.long	4,4,4,4
+.Lincy:
+.long	0,2,4,6,1,3,5,7
+.Leight:
+.long	8,8,8,8,8,8,8,8
+.Lrot16:
+.byte	0x2,0x3,0x0,0x1, 0x6,0x7,0x4,0x5, 0xa,0xb,0x8,0x9, 0xe,0xf,0xc,0xd
+.Lrot24:
+.byte	0x3,0x0,0x1,0x2, 0x7,0x4,0x5,0x6, 0xb,0x8,0x9,0xa, 0xf,0xc,0xd,0xe
+.Lsigma:
+.asciz	"expand 32-byte k"
+.asciz	"ChaCha20 for x86_64, CRYPTOGAMS by "
+___
+
+sub AUTOLOAD()          # thunk [simplified] 32-bit style perlasm
+{ my $opcode = $AUTOLOAD; $opcode =~ s/.*:://;
+  my $arg = pop;
+    $arg = "\$$arg" if ($arg*1 eq $arg);
+    $code .= "\t$opcode\t".join(',',$arg,reverse @_)."\n";
+}
+
+@x=("%eax","%ebx","%ecx","%edx",map("%r${_}d",(8..11)),
+    "%nox","%nox","%nox","%nox",map("%r${_}d",(12..15)));
+@t=("%esi","%edi");
+
+sub ROUND {			# critical path is 24 cycles per round
+my ($a0,$b0,$c0,$d0)=@_;
+my ($a1,$b1,$c1,$d1)=map(($_&~3)+(($_+1)&3),($a0,$b0,$c0,$d0));
+my ($a2,$b2,$c2,$d2)=map(($_&~3)+(($_+1)&3),($a1,$b1,$c1,$d1));
+my ($a3,$b3,$c3,$d3)=map(($_&~3)+(($_+1)&3),($a2,$b2,$c2,$d2));
+my ($xc,$xc_)=map("\"$_\"",@t);
+my @x=map("\"$_\"",@x);
+
+	# Consider order in which variables are addressed by their
+	# index:
+	#
+	#	a   b   c   d
+	#
+	#	0   4   8  12 < even round
+	#	1   5   9  13
+	#	2   6  10  14
+	#	3   7  11  15
+	#	0   5  10  15 < odd round
+	#	1   6  11  12
+	#	2   7   8  13
+	#	3   4   9  14
+	#
+	# 'a', 'b' and 'd's are permanently allocated in registers,
+	# @x[0..7,12..15], while 'c's are maintained in memory. If
+	# you observe 'c' column, you'll notice that pair of 'c's is
+	# invariant between rounds. This means that we have to reload
+	# them once per round, in the middle. This is why you'll see
+	# bunch of 'c' stores and loads in the middle, but none in
+	# the beginning or end.
+
+	# Normally instructions would be interleaved to favour in-order
+	# execution. Generally out-of-order cores manage it gracefully,
+	# but not this time for some reason. As in-order execution
+	# cores are dying breed, old Atom is the only one around,
+	# instructions are left uninterleaved. Besides, Atom is better
+	# off executing 1xSSSE3 code anyway...
+
+	(
+	"&add	(@x[$a0],@x[$b0])",	# Q1
+	"&xor	(@x[$d0],@x[$a0])",
+	"&rol	(@x[$d0],16)",
+	 "&add	(@x[$a1],@x[$b1])",	# Q2
+	 "&xor	(@x[$d1],@x[$a1])",
+	 "&rol	(@x[$d1],16)",
+
+	"&add	($xc,@x[$d0])",
+	"&xor	(@x[$b0],$xc)",
+	"&rol	(@x[$b0],12)",
+	 "&add	($xc_,@x[$d1])",
+	 "&xor	(@x[$b1],$xc_)",
+	 "&rol	(@x[$b1],12)",
+
+	"&add	(@x[$a0],@x[$b0])",
+	"&xor	(@x[$d0],@x[$a0])",
+	"&rol	(@x[$d0],8)",
+	 "&add	(@x[$a1],@x[$b1])",
+	 "&xor	(@x[$d1],@x[$a1])",
+	 "&rol	(@x[$d1],8)",
+
+	"&add	($xc,@x[$d0])",
+	"&xor	(@x[$b0],$xc)",
+	"&rol	(@x[$b0],7)",
+	 "&add	($xc_,@x[$d1])",
+	 "&xor	(@x[$b1],$xc_)",
+	 "&rol	(@x[$b1],7)",
+
+	"&mov	(\"4*$c0(%rsp)\",$xc)",	# reload pair of 'c's
+	 "&mov	(\"4*$c1(%rsp)\",$xc_)",
+	"&mov	($xc,\"4*$c2(%rsp)\")",
+	 "&mov	($xc_,\"4*$c3(%rsp)\")",
+
+	"&add	(@x[$a2],@x[$b2])",	# Q3
+	"&xor	(@x[$d2],@x[$a2])",
+	"&rol	(@x[$d2],16)",
+	 "&add	(@x[$a3],@x[$b3])",	# Q4
+	 "&xor	(@x[$d3],@x[$a3])",
+	 "&rol	(@x[$d3],16)",
+
+	"&add	($xc,@x[$d2])",
+	"&xor	(@x[$b2],$xc)",
+	"&rol	(@x[$b2],12)",
+	 "&add	($xc_,@x[$d3])",
+	 "&xor	(@x[$b3],$xc_)",
+	 "&rol	(@x[$b3],12)",
+
+	"&add	(@x[$a2],@x[$b2])",
+	"&xor	(@x[$d2],@x[$a2])",
+	"&rol	(@x[$d2],8)",
+	 "&add	(@x[$a3],@x[$b3])",
+	 "&xor	(@x[$d3],@x[$a3])",
+	 "&rol	(@x[$d3],8)",
+
+	"&add	($xc,@x[$d2])",
+	"&xor	(@x[$b2],$xc)",
+	"&rol	(@x[$b2],7)",
+	 "&add	($xc_,@x[$d3])",
+	 "&xor	(@x[$b3],$xc_)",
+	 "&rol	(@x[$b3],7)"
+	);
+}
+
+########################################################################
+# Generic code path that handles all lengths on pre-SSSE3 processors.
+$code.=<<___;
+.globl	ChaCha20_ctr32
+.type	ChaCha20_ctr32,\@function,5
+.align	64
+ChaCha20_ctr32:
+	cmp	\$0,$len
+	je	.Lno_data
+	mov	OPENSSL_ia32cap_P+4(%rip),%r10
+	test	\$`1<<(41-32)`,%r10d
+	jnz	.LChaCha20_ssse3
+
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+	sub	\$64+24,%rsp
+
+	#movdqa	.Lsigma(%rip),%xmm0
+	movdqu	($key),%xmm1
+	movdqu	16($key),%xmm2
+	movdqu	($counter),%xmm3
+	movdqa	.Lone(%rip),%xmm4
+
+	#movdqa	%xmm0,4*0(%rsp)		# key[0]
+	movdqa	%xmm1,4*4(%rsp)		# key[1]
+	movdqa	%xmm2,4*8(%rsp)		# key[2]
+	movdqa	%xmm3,4*12(%rsp)	# key[3]
+	mov	$len,%rbp		# reassign $len
+	jmp	.Loop_outer
+
+.align	32
+.Loop_outer:
+	mov	\$0x61707865,@x[0]      # 'expa'
+	mov	\$0x3320646e,@x[1]      # 'nd 3'
+	mov	\$0x79622d32,@x[2]      # '2-by'
+	mov	\$0x6b206574,@x[3]      # 'te k'
+	mov	4*4(%rsp),@x[4]
+	mov	4*5(%rsp),@x[5]
+	mov	4*6(%rsp),@x[6]
+	mov	4*7(%rsp),@x[7]
+	movd	%xmm3,@x[12]
+	mov	4*13(%rsp),@x[13]
+	mov	4*14(%rsp),@x[14]
+	mov	4*15(%rsp),@x[15]
+
+	mov	%rbp,64+0(%rsp)		# save len
+	mov	\$10,%ebp
+	mov	$inp,64+8(%rsp)		# save inp
+	movq	%xmm2,%rsi		# "@x[8]"
+	mov	$out,64+16(%rsp)	# save out
+	mov	%rsi,%rdi
+	shr	\$32,%rdi		# "@x[9]"
+	jmp	.Loop
+
+.align	32
+.Loop:
+___
+	foreach (&ROUND (0, 4, 8,12)) { eval; }
+	foreach (&ROUND	(0, 5,10,15)) { eval; }
+	&dec	("%ebp");
+	&jnz	(".Loop");
+
+$code.=<<___;
+	mov	@t[1],4*9(%rsp)		# modulo-scheduled
+	mov	@t[0],4*8(%rsp)
+	mov	64(%rsp),%rbp		# load len
+	movdqa	%xmm2,%xmm1
+	mov	64+8(%rsp),$inp		# load inp
+	paddd	%xmm4,%xmm3		# increment counter
+	mov	64+16(%rsp),$out	# load out
+
+	add	\$0x61707865,@x[0]      # 'expa'
+	add	\$0x3320646e,@x[1]      # 'nd 3'
+	add	\$0x79622d32,@x[2]      # '2-by'
+	add	\$0x6b206574,@x[3]      # 'te k'
+	add	4*4(%rsp),@x[4]
+	add	4*5(%rsp),@x[5]
+	add	4*6(%rsp),@x[6]
+	add	4*7(%rsp),@x[7]
+	add	4*12(%rsp),@x[12]
+	add	4*13(%rsp),@x[13]
+	add	4*14(%rsp),@x[14]
+	add	4*15(%rsp),@x[15]
+	paddd	4*8(%rsp),%xmm1
+
+	cmp	\$64,%rbp
+	jb	.Ltail
+
+	xor	4*0($inp),@x[0]		# xor with input
+	xor	4*1($inp),@x[1]
+	xor	4*2($inp),@x[2]
+	xor	4*3($inp),@x[3]
+	xor	4*4($inp),@x[4]
+	xor	4*5($inp),@x[5]
+	xor	4*6($inp),@x[6]
+	xor	4*7($inp),@x[7]
+	movdqu	4*8($inp),%xmm0
+	xor	4*12($inp),@x[12]
+	xor	4*13($inp),@x[13]
+	xor	4*14($inp),@x[14]
+	xor	4*15($inp),@x[15]
+	lea	4*16($inp),$inp		# inp+=64
+	pxor	%xmm1,%xmm0
+
+	movdqa	%xmm2,4*8(%rsp)
+	movd	%xmm3,4*12(%rsp)
+
+	mov	@x[0],4*0($out)		# write output
+	mov	@x[1],4*1($out)
+	mov	@x[2],4*2($out)
+	mov	@x[3],4*3($out)
+	mov	@x[4],4*4($out)
+	mov	@x[5],4*5($out)
+	mov	@x[6],4*6($out)
+	mov	@x[7],4*7($out)
+	movdqu	%xmm0,4*8($out)
+	mov	@x[12],4*12($out)
+	mov	@x[13],4*13($out)
+	mov	@x[14],4*14($out)
+	mov	@x[15],4*15($out)
+	lea	4*16($out),$out		# out+=64
+
+	sub	\$64,%rbp
+	jnz	.Loop_outer
+
+	jmp	.Ldone
+
+.align	16
+.Ltail:
+	mov	@x[0],4*0(%rsp)
+	mov	@x[1],4*1(%rsp)
+	xor	%rbx,%rbx
+	mov	@x[2],4*2(%rsp)
+	mov	@x[3],4*3(%rsp)
+	mov	@x[4],4*4(%rsp)
+	mov	@x[5],4*5(%rsp)
+	mov	@x[6],4*6(%rsp)
+	mov	@x[7],4*7(%rsp)
+	movdqa	%xmm1,4*8(%rsp)
+	mov	@x[12],4*12(%rsp)
+	mov	@x[13],4*13(%rsp)
+	mov	@x[14],4*14(%rsp)
+	mov	@x[15],4*15(%rsp)
+
+.Loop_tail:
+	movzb	($inp,%rbx),%eax
+	movzb	(%rsp,%rbx),%edx
+	lea	1(%rbx),%rbx
+	xor	%edx,%eax
+	mov	%al,-1($out,%rbx)
+	dec	%rbp
+	jnz	.Loop_tail
+
+.Ldone:
+	add	\$64+24,%rsp
+	pop	%r15
+	pop	%r14
+	pop	%r13
+	pop	%r12
+	pop	%rbp
+	pop	%rbx
+.Lno_data:
+	ret
+.size	ChaCha20_ctr32,.-ChaCha20_ctr32
+___
+
+########################################################################
+# SSSE3 code path that handles shorter lengths
+{
+my ($a,$b,$c,$d,$t,$t1,$rot16,$rot24)=map("%xmm$_",(0..7));
+
+sub SSSE3ROUND {	# critical path is 20 "SIMD ticks" per round
+	&paddd	($a,$b);
+	&pxor	($d,$a);
+	&pshufb	($d,$rot16);
+
+	&paddd	($c,$d);
+	&pxor	($b,$c);
+	&movdqa	($t,$b);
+	&psrld	($b,20);
+	&pslld	($t,12);
+	&por	($b,$t);
+
+	&paddd	($a,$b);
+	&pxor	($d,$a);
+	&pshufb	($d,$rot24);
+
+	&paddd	($c,$d);
+	&pxor	($b,$c);
+	&movdqa	($t,$b);
+	&psrld	($b,25);
+	&pslld	($t,7);
+	&por	($b,$t);
+}
+
+my $xframe = $win64 ? 32+32+8 : 24;
+
+$code.=<<___;
+.type	ChaCha20_ssse3,\@function,5
+.align	32
+ChaCha20_ssse3:
+.LChaCha20_ssse3:
+___
+$code.=<<___	if ($avx);
+	test	\$`1<<(43-32)`,%r10d
+	jnz	.LChaCha20_4xop		# XOP is fastest even if we use 1/4
+___
+$code.=<<___;
+	cmp	\$128,$len		# we might throw away some data,
+	ja	.LChaCha20_4x		# but overall it won't be slower
+
+.Ldo_sse3_after_all:
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+
+	sub	\$64+$xframe,%rsp
+___
+$code.=<<___	if ($win64);
+	movaps	%xmm6,64+32(%rsp)
+	movaps	%xmm7,64+48(%rsp)
+___
+$code.=<<___;
+	movdqa	.Lsigma(%rip),$a
+	movdqu	($key),$b
+	movdqu	16($key),$c
+	movdqu	($counter),$d
+	movdqa	.Lrot16(%rip),$rot16
+	movdqa	.Lrot24(%rip),$rot24
+
+	movdqa	$a,0x00(%rsp)
+	movdqa	$b,0x10(%rsp)
+	movdqa	$c,0x20(%rsp)
+	movdqa	$d,0x30(%rsp)
+	mov	\$10,%ebp
+	jmp	.Loop_ssse3
+
+.align	32
+.Loop_outer_ssse3:
+	movdqa	.Lone(%rip),$d
+	movdqa	0x00(%rsp),$a
+	movdqa	0x10(%rsp),$b
+	movdqa	0x20(%rsp),$c
+	paddd	0x30(%rsp),$d
+	mov	\$10,%ebp
+	movdqa	$d,0x30(%rsp)
+	jmp	.Loop_ssse3
+
+.align	32
+.Loop_ssse3:
+___
+	&SSSE3ROUND();
+	&pshufd	($c,$c,0b01001110);
+	&pshufd	($b,$b,0b00111001);
+	&pshufd	($d,$d,0b10010011);
+	&nop	();
+
+	&SSSE3ROUND();
+	&pshufd	($c,$c,0b01001110);
+	&pshufd	($b,$b,0b10010011);
+	&pshufd	($d,$d,0b00111001);
+
+	&dec	("%ebp");
+	&jnz	(".Loop_ssse3");
+
+$code.=<<___;
+	paddd	0x00(%rsp),$a
+	paddd	0x10(%rsp),$b
+	paddd	0x20(%rsp),$c
+	paddd	0x30(%rsp),$d
+
+	cmp	\$64,$len
+	jb	.Ltail_ssse3
+
+	movdqu	0x00($inp),$t
+	movdqu	0x10($inp),$t1
+	pxor	$t,$a			# xor with input
+	movdqu	0x20($inp),$t
+	pxor	$t1,$b
+	movdqu	0x30($inp),$t1
+	lea	0x40($inp),$inp		# inp+=64
+	pxor	$t,$c
+	pxor	$t1,$d
+
+	movdqu	$a,0x00($out)		# write output
+	movdqu	$b,0x10($out)
+	movdqu	$c,0x20($out)
+	movdqu	$d,0x30($out)
+	lea	0x40($out),$out		# out+=64
+
+	sub	\$64,$len
+	jnz	.Loop_outer_ssse3
+
+	jmp	.Ldone_ssse3
+
+.align	16
+.Ltail_ssse3:
+	movdqa	$a,0x00(%rsp)
+	movdqa	$b,0x10(%rsp)
+	movdqa	$c,0x20(%rsp)
+	movdqa	$d,0x30(%rsp)
+	xor	%rbx,%rbx
+
+.Loop_tail_ssse3:
+	movzb	($inp,%rbx),%eax
+	movzb	(%rsp,%rbx),%ecx
+	lea	1(%rbx),%rbx
+	xor	%ecx,%eax
+	mov	%al,-1($out,%rbx)
+	dec	$len
+	jnz	.Loop_tail_ssse3
+
+.Ldone_ssse3:
+___
+$code.=<<___	if ($win64);
+	movaps	64+32(%rsp),%xmm6
+	movaps	64+48(%rsp),%xmm7
+___
+$code.=<<___;
+	add	\$64+$xframe,%rsp
+	pop	%r15
+	pop	%r14
+	pop	%r13
+	pop	%r12
+	pop	%rbp
+	pop	%rbx
+	ret
+.size	ChaCha20_ssse3,.-ChaCha20_ssse3
+___
+}
+
+########################################################################
+# SSSE3 code path that handles longer messages.
+{
+# assign variables to favor Atom front-end
+my ($xd0,$xd1,$xd2,$xd3, $xt0,$xt1,$xt2,$xt3,
+    $xa0,$xa1,$xa2,$xa3, $xb0,$xb1,$xb2,$xb3)=map("%xmm$_",(0..15));
+my  @xx=($xa0,$xa1,$xa2,$xa3, $xb0,$xb1,$xb2,$xb3,
+	"%nox","%nox","%nox","%nox", $xd0,$xd1,$xd2,$xd3);
+
+sub SSSE3_lane_ROUND {
+my ($a0,$b0,$c0,$d0)=@_;
+my ($a1,$b1,$c1,$d1)=map(($_&~3)+(($_+1)&3),($a0,$b0,$c0,$d0));
+my ($a2,$b2,$c2,$d2)=map(($_&~3)+(($_+1)&3),($a1,$b1,$c1,$d1));
+my ($a3,$b3,$c3,$d3)=map(($_&~3)+(($_+1)&3),($a2,$b2,$c2,$d2));
+my ($xc,$xc_,$t0,$t1)=map("\"$_\"",$xt0,$xt1,$xt2,$xt3);
+my @x=map("\"$_\"",@xx);
+
+	# Consider order in which variables are addressed by their
+	# index:
+	#
+	#	a   b   c   d
+	#
+	#	0   4   8  12 < even round
+	#	1   5   9  13
+	#	2   6  10  14
+	#	3   7  11  15
+	#	0   5  10  15 < odd round
+	#	1   6  11  12
+	#	2   7   8  13
+	#	3   4   9  14
+	#
+	# 'a', 'b' and 'd's are permanently allocated in registers,
+	# @x[0..7,12..15], while 'c's are maintained in memory. If
+	# you observe 'c' column, you'll notice that pair of 'c's is
+	# invariant between rounds. This means that we have to reload
+	# them once per round, in the middle. This is why you'll see
+	# bunch of 'c' stores and loads in the middle, but none in
+	# the beginning or end.
+
+	(
+	"&paddd		(@x[$a0],@x[$b0])",	# Q1
+	 "&paddd	(@x[$a1],@x[$b1])",	# Q2
+	"&pxor		(@x[$d0],@x[$a0])",
+	 "&pxor		(@x[$d1],@x[$a1])",
+	"&pshufb	(@x[$d0],$t1)",
+	 "&pshufb	(@x[$d1],$t1)",
+
+	"&paddd		($xc,@x[$d0])",
+	 "&paddd	($xc_,@x[$d1])",
+	"&pxor		(@x[$b0],$xc)",
+	 "&pxor		(@x[$b1],$xc_)",
+	"&movdqa	($t0,@x[$b0])",
+	"&pslld		(@x[$b0],12)",
+	"&psrld		($t0,20)",
+	 "&movdqa	($t1,@x[$b1])",
+	 "&pslld	(@x[$b1],12)",
+	"&por		(@x[$b0],$t0)",
+	 "&psrld	($t1,20)",
+	"&movdqa	($t0,'(%r11)')",	# .Lrot24(%rip)
+	 "&por		(@x[$b1],$t1)",
+
+	"&paddd		(@x[$a0],@x[$b0])",
+	 "&paddd	(@x[$a1],@x[$b1])",
+	"&pxor		(@x[$d0],@x[$a0])",
+	 "&pxor		(@x[$d1],@x[$a1])",
+	"&pshufb	(@x[$d0],$t0)",
+	 "&pshufb	(@x[$d1],$t0)",
+
+	"&paddd		($xc,@x[$d0])",
+	 "&paddd	($xc_,@x[$d1])",
+	"&pxor		(@x[$b0],$xc)",
+	 "&pxor		(@x[$b1],$xc_)",
+	"&movdqa	($t1,@x[$b0])",
+	"&pslld		(@x[$b0],7)",
+	"&psrld		($t1,25)",
+	 "&movdqa	($t0,@x[$b1])",
+	 "&pslld	(@x[$b1],7)",
+	"&por		(@x[$b0],$t1)",
+	 "&psrld	($t0,25)",
+	"&movdqa	($t1,'(%r10)')",	# .Lrot16(%rip)
+	 "&por		(@x[$b1],$t0)",
+
+	"&movdqa	(\"`16*($c0-8)`(%rsp)\",$xc)",	# reload pair of 'c's
+	 "&movdqa	(\"`16*($c1-8)`(%rsp)\",$xc_)",
+	"&movdqa	($xc,\"`16*($c2-8)`(%rsp)\")",
+	 "&movdqa	($xc_,\"`16*($c3-8)`(%rsp)\")",
+
+	"&paddd		(@x[$a2],@x[$b2])",	# Q3
+	 "&paddd	(@x[$a3],@x[$b3])",	# Q4
+	"&pxor		(@x[$d2],@x[$a2])",
+	 "&pxor		(@x[$d3],@x[$a3])",
+	"&pshufb	(@x[$d2],$t1)",
+	 "&pshufb	(@x[$d3],$t1)",
+
+	"&paddd		($xc,@x[$d2])",
+	 "&paddd	($xc_,@x[$d3])",
+	"&pxor		(@x[$b2],$xc)",
+	 "&pxor		(@x[$b3],$xc_)",
+	"&movdqa	($t0,@x[$b2])",
+	"&pslld		(@x[$b2],12)",
+	"&psrld		($t0,20)",
+	 "&movdqa	($t1,@x[$b3])",
+	 "&pslld	(@x[$b3],12)",
+	"&por		(@x[$b2],$t0)",
+	 "&psrld	($t1,20)",
+	"&movdqa	($t0,'(%r11)')",	# .Lrot24(%rip)
+	 "&por		(@x[$b3],$t1)",
+
+	"&paddd		(@x[$a2],@x[$b2])",
+	 "&paddd	(@x[$a3],@x[$b3])",
+	"&pxor		(@x[$d2],@x[$a2])",
+	 "&pxor		(@x[$d3],@x[$a3])",
+	"&pshufb	(@x[$d2],$t0)",
+	 "&pshufb	(@x[$d3],$t0)",
+
+	"&paddd		($xc,@x[$d2])",
+	 "&paddd	($xc_,@x[$d3])",
+	"&pxor		(@x[$b2],$xc)",
+	 "&pxor		(@x[$b3],$xc_)",
+	"&movdqa	($t1,@x[$b2])",
+	"&pslld		(@x[$b2],7)",
+	"&psrld		($t1,25)",
+	 "&movdqa	($t0,@x[$b3])",
+	 "&pslld	(@x[$b3],7)",
+	"&por		(@x[$b2],$t1)",
+	 "&psrld	($t0,25)",
+	"&movdqa	($t1,'(%r10)')",	# .Lrot16(%rip)
+	 "&por		(@x[$b3],$t0)"
+	);
+}
+
+my $xframe = $win64 ? 0xa0 : 0;
+
+$code.=<<___;
+.type	ChaCha20_4x,\@function,5
+.align	32
+ChaCha20_4x:
+.LChaCha20_4x:
+	mov		%r10,%r11
+___
+$code.=<<___	if ($avx>1);
+	shr		\$32,%r10		# OPENSSL_ia32cap_P+8
+	test		\$`1<<5`,%r10		# test AVX2
+	jnz		.LChaCha20_8x
+___
+$code.=<<___;
+	cmp		\$192,$len
+	ja		.Lproceed4x
+
+	and		\$`1<<26|1<<22`,%r11	# isolate XSAVE+MOVBE
+	cmp		\$`1<<22`,%r11		# check for MOVBE without XSAVE
+	je		.Ldo_sse3_after_all	# to detect Atom
+
+.Lproceed4x:
+	lea		-0x78(%rsp),%r11
+	sub		\$0x148+$xframe,%rsp
+___
+	################ stack layout
+	# +0x00		SIMD equivalent of @x[8-12]
+	# ...
+	# +0x40		constant copy of key[0-2] smashed by lanes
+	# ...
+	# +0x100	SIMD counters (with nonce smashed by lanes)
+	# ...
+	# +0x140
+$code.=<<___	if ($win64);
+	movaps		%xmm6,-0x30(%r11)
+	movaps		%xmm7,-0x20(%r11)
+	movaps		%xmm8,-0x10(%r11)
+	movaps		%xmm9,0x00(%r11)
+	movaps		%xmm10,0x10(%r11)
+	movaps		%xmm11,0x20(%r11)
+	movaps		%xmm12,0x30(%r11)
+	movaps		%xmm13,0x40(%r11)
+	movaps		%xmm14,0x50(%r11)
+	movaps		%xmm15,0x60(%r11)
+___
+$code.=<<___;
+	movdqa		.Lsigma(%rip),$xa3	# key[0]
+	movdqu		($key),$xb3		# key[1]
+	movdqu		16($key),$xt3		# key[2]
+	movdqu		($counter),$xd3		# key[3]
+	lea		0x100(%rsp),%rcx	# size optimization
+	lea		.Lrot16(%rip),%r10
+	lea		.Lrot24(%rip),%r11
+
+	pshufd		\$0x00,$xa3,$xa0	# smash key by lanes...
+	pshufd		\$0x55,$xa3,$xa1
+	movdqa		$xa0,0x40(%rsp)		# ... and offload
+	pshufd		\$0xaa,$xa3,$xa2
+	movdqa		$xa1,0x50(%rsp)
+	pshufd		\$0xff,$xa3,$xa3
+	movdqa		$xa2,0x60(%rsp)
+	movdqa		$xa3,0x70(%rsp)
+
+	pshufd		\$0x00,$xb3,$xb0
+	pshufd		\$0x55,$xb3,$xb1
+	movdqa		$xb0,0x80-0x100(%rcx)
+	pshufd		\$0xaa,$xb3,$xb2
+	movdqa		$xb1,0x90-0x100(%rcx)
+	pshufd		\$0xff,$xb3,$xb3
+	movdqa		$xb2,0xa0-0x100(%rcx)
+	movdqa		$xb3,0xb0-0x100(%rcx)
+
+	pshufd		\$0x00,$xt3,$xt0	# "$xc0"
+	pshufd		\$0x55,$xt3,$xt1	# "$xc1"
+	movdqa		$xt0,0xc0-0x100(%rcx)
+	pshufd		\$0xaa,$xt3,$xt2	# "$xc2"
+	movdqa		$xt1,0xd0-0x100(%rcx)
+	pshufd		\$0xff,$xt3,$xt3	# "$xc3"
+	movdqa		$xt2,0xe0-0x100(%rcx)
+	movdqa		$xt3,0xf0-0x100(%rcx)
+
+	pshufd		\$0x00,$xd3,$xd0
+	pshufd		\$0x55,$xd3,$xd1
+	paddd		.Linc(%rip),$xd0	# don't save counters yet
+	pshufd		\$0xaa,$xd3,$xd2
+	movdqa		$xd1,0x110-0x100(%rcx)
+	pshufd		\$0xff,$xd3,$xd3
+	movdqa		$xd2,0x120-0x100(%rcx)
+	movdqa		$xd3,0x130-0x100(%rcx)
+
+	jmp		.Loop_enter4x
+
+.align	32
+.Loop_outer4x:
+	movdqa		0x40(%rsp),$xa0		# re-load smashed key
+	movdqa		0x50(%rsp),$xa1
+	movdqa		0x60(%rsp),$xa2
+	movdqa		0x70(%rsp),$xa3
+	movdqa		0x80-0x100(%rcx),$xb0
+	movdqa		0x90-0x100(%rcx),$xb1
+	movdqa		0xa0-0x100(%rcx),$xb2
+	movdqa		0xb0-0x100(%rcx),$xb3
+	movdqa		0xc0-0x100(%rcx),$xt0	# "$xc0"
+	movdqa		0xd0-0x100(%rcx),$xt1	# "$xc1"
+	movdqa		0xe0-0x100(%rcx),$xt2	# "$xc2"
+	movdqa		0xf0-0x100(%rcx),$xt3	# "$xc3"
+	movdqa		0x100-0x100(%rcx),$xd0
+	movdqa		0x110-0x100(%rcx),$xd1
+	movdqa		0x120-0x100(%rcx),$xd2
+	movdqa		0x130-0x100(%rcx),$xd3
+	paddd		.Lfour(%rip),$xd0	# next SIMD counters
+
+.Loop_enter4x:
+	movdqa		$xt2,0x20(%rsp)		# SIMD equivalent of "@x[10]"
+	movdqa		$xt3,0x30(%rsp)		# SIMD equivalent of "@x[11]"
+	movdqa		(%r10),$xt3		# .Lrot16(%rip)
+	mov		\$10,%eax
+	movdqa		$xd0,0x100-0x100(%rcx)	# save SIMD counters
+	jmp		.Loop4x
+
+.align	32
+.Loop4x:
+___
+	foreach (&SSSE3_lane_ROUND(0, 4, 8,12)) { eval; }
+	foreach (&SSSE3_lane_ROUND(0, 5,10,15)) { eval; }
+$code.=<<___;
+	dec		%eax
+	jnz		.Loop4x
+
+	paddd		0x40(%rsp),$xa0		# accumulate key material
+	paddd		0x50(%rsp),$xa1
+	paddd		0x60(%rsp),$xa2
+	paddd		0x70(%rsp),$xa3
+
+	movdqa		$xa0,$xt2		# "de-interlace" data
+	punpckldq	$xa1,$xa0
+	movdqa		$xa2,$xt3
+	punpckldq	$xa3,$xa2
+	punpckhdq	$xa1,$xt2
+	punpckhdq	$xa3,$xt3
+	movdqa		$xa0,$xa1
+	punpcklqdq	$xa2,$xa0		# "a0"
+	movdqa		$xt2,$xa3
+	punpcklqdq	$xt3,$xt2		# "a2"
+	punpckhqdq	$xa2,$xa1		# "a1"
+	punpckhqdq	$xt3,$xa3		# "a3"
+___
+	($xa2,$xt2)=($xt2,$xa2);
+$code.=<<___;
+	paddd		0x80-0x100(%rcx),$xb0
+	paddd		0x90-0x100(%rcx),$xb1
+	paddd		0xa0-0x100(%rcx),$xb2
+	paddd		0xb0-0x100(%rcx),$xb3
+
+	movdqa		$xa0,0x00(%rsp)		# offload $xaN
+	movdqa		$xa1,0x10(%rsp)
+	movdqa		0x20(%rsp),$xa0		# "xc2"
+	movdqa		0x30(%rsp),$xa1		# "xc3"
+
+	movdqa		$xb0,$xt2
+	punpckldq	$xb1,$xb0
+	movdqa		$xb2,$xt3
+	punpckldq	$xb3,$xb2
+	punpckhdq	$xb1,$xt2
+	punpckhdq	$xb3,$xt3
+	movdqa		$xb0,$xb1
+	punpcklqdq	$xb2,$xb0		# "b0"
+	movdqa		$xt2,$xb3
+	punpcklqdq	$xt3,$xt2		# "b2"
+	punpckhqdq	$xb2,$xb1		# "b1"
+	punpckhqdq	$xt3,$xb3		# "b3"
+___
+	($xb2,$xt2)=($xt2,$xb2);
+	my ($xc0,$xc1,$xc2,$xc3)=($xt0,$xt1,$xa0,$xa1);
+$code.=<<___;
+	paddd		0xc0-0x100(%rcx),$xc0
+	paddd		0xd0-0x100(%rcx),$xc1
+	paddd		0xe0-0x100(%rcx),$xc2
+	paddd		0xf0-0x100(%rcx),$xc3
+
+	movdqa		$xa2,0x20(%rsp)		# keep offloading $xaN
+	movdqa		$xa3,0x30(%rsp)
+
+	movdqa		$xc0,$xt2
+	punpckldq	$xc1,$xc0
+	movdqa		$xc2,$xt3
+	punpckldq	$xc3,$xc2
+	punpckhdq	$xc1,$xt2
+	punpckhdq	$xc3,$xt3
+	movdqa		$xc0,$xc1
+	punpcklqdq	$xc2,$xc0		# "c0"
+	movdqa		$xt2,$xc3
+	punpcklqdq	$xt3,$xt2		# "c2"
+	punpckhqdq	$xc2,$xc1		# "c1"
+	punpckhqdq	$xt3,$xc3		# "c3"
+___
+	($xc2,$xt2)=($xt2,$xc2);
+	($xt0,$xt1)=($xa2,$xa3);		# use $xaN as temporary
+$code.=<<___;
+	paddd		0x100-0x100(%rcx),$xd0
+	paddd		0x110-0x100(%rcx),$xd1
+	paddd		0x120-0x100(%rcx),$xd2
+	paddd		0x130-0x100(%rcx),$xd3
+
+	movdqa		$xd0,$xt2
+	punpckldq	$xd1,$xd0
+	movdqa		$xd2,$xt3
+	punpckldq	$xd3,$xd2
+	punpckhdq	$xd1,$xt2
+	punpckhdq	$xd3,$xt3
+	movdqa		$xd0,$xd1
+	punpcklqdq	$xd2,$xd0		# "d0"
+	movdqa		$xt2,$xd3
+	punpcklqdq	$xt3,$xt2		# "d2"
+	punpckhqdq	$xd2,$xd1		# "d1"
+	punpckhqdq	$xt3,$xd3		# "d3"
+___
+	($xd2,$xt2)=($xt2,$xd2);
+$code.=<<___;
+	cmp		\$64*4,$len
+	jb		.Ltail4x
+
+	movdqu		0x00($inp),$xt0		# xor with input
+	movdqu		0x10($inp),$xt1
+	movdqu		0x20($inp),$xt2
+	movdqu		0x30($inp),$xt3
+	pxor		0x00(%rsp),$xt0		# $xaN is offloaded, remember?
+	pxor		$xb0,$xt1
+	pxor		$xc0,$xt2
+	pxor		$xd0,$xt3
+
+	 movdqu		$xt0,0x00($out)
+	movdqu		0x40($inp),$xt0
+	 movdqu		$xt1,0x10($out)
+	movdqu		0x50($inp),$xt1
+	 movdqu		$xt2,0x20($out)
+	movdqu		0x60($inp),$xt2
+	 movdqu		$xt3,0x30($out)
+	movdqu		0x70($inp),$xt3
+	lea		0x80($inp),$inp		# size optimization
+	pxor		0x10(%rsp),$xt0
+	pxor		$xb1,$xt1
+	pxor		$xc1,$xt2
+	pxor		$xd1,$xt3
+
+	 movdqu		$xt0,0x40($out)
+	movdqu		0x00($inp),$xt0
+	 movdqu		$xt1,0x50($out)
+	movdqu		0x10($inp),$xt1
+	 movdqu		$xt2,0x60($out)
+	movdqu		0x20($inp),$xt2
+	 movdqu		$xt3,0x70($out)
+	 lea		0x80($out),$out		# size optimization
+	movdqu		0x30($inp),$xt3
+	pxor		0x20(%rsp),$xt0
+	pxor		$xb2,$xt1
+	pxor		$xc2,$xt2
+	pxor		$xd2,$xt3
+
+	 movdqu		$xt0,0x00($out)
+	movdqu		0x40($inp),$xt0
+	 movdqu		$xt1,0x10($out)
+	movdqu		0x50($inp),$xt1
+	 movdqu		$xt2,0x20($out)
+	movdqu		0x60($inp),$xt2
+	 movdqu		$xt3,0x30($out)
+	movdqu		0x70($inp),$xt3
+	lea		0x80($inp),$inp		# inp+=64*4
+	pxor		0x30(%rsp),$xt0
+	pxor		$xb3,$xt1
+	pxor		$xc3,$xt2
+	pxor		$xd3,$xt3
+	movdqu		$xt0,0x40($out)
+	movdqu		$xt1,0x50($out)
+	movdqu		$xt2,0x60($out)
+	movdqu		$xt3,0x70($out)
+	lea		0x80($out),$out		# out+=64*4
+
+	sub		\$64*4,$len
+	jnz		.Loop_outer4x
+
+	jmp		.Ldone4x
+
+.Ltail4x:
+	cmp		\$192,$len
+	jae		.L192_or_more4x
+	cmp		\$128,$len
+	jae		.L128_or_more4x
+	cmp		\$64,$len
+	jae		.L64_or_more4x
+
+	#movdqa		0x00(%rsp),$xt0		# $xaN is offloaded, remember?
+	xor		%r10,%r10
+	#movdqa		$xt0,0x00(%rsp)
+	movdqa		$xb0,0x10(%rsp)
+	movdqa		$xc0,0x20(%rsp)
+	movdqa		$xd0,0x30(%rsp)
+	jmp		.Loop_tail4x
+
+.align	32
+.L64_or_more4x:
+	movdqu		0x00($inp),$xt0		# xor with input
+	movdqu		0x10($inp),$xt1
+	movdqu		0x20($inp),$xt2
+	movdqu		0x30($inp),$xt3
+	pxor		0x00(%rsp),$xt0		# $xaxN is offloaded, remember?
+	pxor		$xb0,$xt1
+	pxor		$xc0,$xt2
+	pxor		$xd0,$xt3
+	movdqu		$xt0,0x00($out)
+	movdqu		$xt1,0x10($out)
+	movdqu		$xt2,0x20($out)
+	movdqu		$xt3,0x30($out)
+	je		.Ldone4x
+
+	movdqa		0x10(%rsp),$xt0		# $xaN is offloaded, remember?
+	lea		0x40($inp),$inp		# inp+=64*1
+	xor		%r10,%r10
+	movdqa		$xt0,0x00(%rsp)
+	movdqa		$xb1,0x10(%rsp)
+	lea		0x40($out),$out		# out+=64*1
+	movdqa		$xc1,0x20(%rsp)
+	sub		\$64,$len		# len-=64*1
+	movdqa		$xd1,0x30(%rsp)
+	jmp		.Loop_tail4x
+
+.align	32
+.L128_or_more4x:
+	movdqu		0x00($inp),$xt0		# xor with input
+	movdqu		0x10($inp),$xt1
+	movdqu		0x20($inp),$xt2
+	movdqu		0x30($inp),$xt3
+	pxor		0x00(%rsp),$xt0		# $xaN is offloaded, remember?
+	pxor		$xb0,$xt1
+	pxor		$xc0,$xt2
+	pxor		$xd0,$xt3
+
+	 movdqu		$xt0,0x00($out)
+	movdqu		0x40($inp),$xt0
+	 movdqu		$xt1,0x10($out)
+	movdqu		0x50($inp),$xt1
+	 movdqu		$xt2,0x20($out)
+	movdqu		0x60($inp),$xt2
+	 movdqu		$xt3,0x30($out)
+	movdqu		0x70($inp),$xt3
+	pxor		0x10(%rsp),$xt0
+	pxor		$xb1,$xt1
+	pxor		$xc1,$xt2
+	pxor		$xd1,$xt3
+	movdqu		$xt0,0x40($out)
+	movdqu		$xt1,0x50($out)
+	movdqu		$xt2,0x60($out)
+	movdqu		$xt3,0x70($out)
+	je		.Ldone4x
+
+	movdqa		0x20(%rsp),$xt0		# $xaN is offloaded, remember?
+	lea		0x80($inp),$inp		# inp+=64*2
+	xor		%r10,%r10
+	movdqa		$xt0,0x00(%rsp)
+	movdqa		$xb2,0x10(%rsp)
+	lea		0x80($out),$out		# out+=64*2
+	movdqa		$xc2,0x20(%rsp)
+	sub		\$128,$len		# len-=64*2
+	movdqa		$xd2,0x30(%rsp)
+	jmp		.Loop_tail4x
+
+.align	32
+.L192_or_more4x:
+	movdqu		0x00($inp),$xt0		# xor with input
+	movdqu		0x10($inp),$xt1
+	movdqu		0x20($inp),$xt2
+	movdqu		0x30($inp),$xt3
+	pxor		0x00(%rsp),$xt0		# $xaN is offloaded, remember?
+	pxor		$xb0,$xt1
+	pxor		$xc0,$xt2
+	pxor		$xd0,$xt3
+
+	 movdqu		$xt0,0x00($out)
+	movdqu		0x40($inp),$xt0
+	 movdqu		$xt1,0x10($out)
+	movdqu		0x50($inp),$xt1
+	 movdqu		$xt2,0x20($out)
+	movdqu		0x60($inp),$xt2
+	 movdqu		$xt3,0x30($out)
+	movdqu		0x70($inp),$xt3
+	lea		0x80($inp),$inp		# size optimization
+	pxor		0x10(%rsp),$xt0
+	pxor		$xb1,$xt1
+	pxor		$xc1,$xt2
+	pxor		$xd1,$xt3
+
+	 movdqu		$xt0,0x40($out)
+	movdqu		0x00($inp),$xt0
+	 movdqu		$xt1,0x50($out)
+	movdqu		0x10($inp),$xt1
+	 movdqu		$xt2,0x60($out)
+	movdqu		0x20($inp),$xt2
+	 movdqu		$xt3,0x70($out)
+	 lea		0x80($out),$out		# size optimization
+	movdqu		0x30($inp),$xt3
+	pxor		0x20(%rsp),$xt0
+	pxor		$xb2,$xt1
+	pxor		$xc2,$xt2
+	pxor		$xd2,$xt3
+	movdqu		$xt0,0x00($out)
+	movdqu		$xt1,0x10($out)
+	movdqu		$xt2,0x20($out)
+	movdqu		$xt3,0x30($out)
+	je		.Ldone4x
+
+	movdqa		0x30(%rsp),$xt0		# $xaN is offloaded, remember?
+	lea		0x40($inp),$inp		# inp+=64*3
+	xor		%r10,%r10
+	movdqa		$xt0,0x00(%rsp)
+	movdqa		$xb3,0x10(%rsp)
+	lea		0x40($out),$out		# out+=64*3
+	movdqa		$xc3,0x20(%rsp)
+	sub		\$192,$len		# len-=64*3
+	movdqa		$xd3,0x30(%rsp)
+
+.Loop_tail4x:
+	movzb		($inp,%r10),%eax
+	movzb		(%rsp,%r10),%ecx
+	lea		1(%r10),%r10
+	xor		%ecx,%eax
+	mov		%al,-1($out,%r10)
+	dec		$len
+	jnz		.Loop_tail4x
+
+.Ldone4x:
+___
+$code.=<<___	if ($win64);
+	lea		0x140+0x30(%rsp),%r11
+	movaps		-0x30(%r11),%xmm6
+	movaps		-0x20(%r11),%xmm7
+	movaps		-0x10(%r11),%xmm8
+	movaps		0x00(%r11),%xmm9
+	movaps		0x10(%r11),%xmm10
+	movaps		0x20(%r11),%xmm11
+	movaps		0x30(%r11),%xmm12
+	movaps		0x40(%r11),%xmm13
+	movaps		0x50(%r11),%xmm14
+	movaps		0x60(%r11),%xmm15
+___
+$code.=<<___;
+	add		\$0x148+$xframe,%rsp
+	ret
+.size	ChaCha20_4x,.-ChaCha20_4x
+___
+}
+
+########################################################################
+# XOP code path that handles all lengths.
+if ($avx) {
+# There is some "anomaly" observed depending on instructions' size or
+# alignment. If you look closely at below code you'll notice that
+# sometimes argument order varies. The order affects instruction
+# encoding by making it larger, and such fiddling gives 5% performance
+# improvement. This is on FX-4100...
+
+my ($xb0,$xb1,$xb2,$xb3, $xd0,$xd1,$xd2,$xd3,
+    $xa0,$xa1,$xa2,$xa3, $xt0,$xt1,$xt2,$xt3)=map("%xmm$_",(0..15));
+my  @xx=($xa0,$xa1,$xa2,$xa3, $xb0,$xb1,$xb2,$xb3,
+	 $xt0,$xt1,$xt2,$xt3, $xd0,$xd1,$xd2,$xd3);
+
+sub XOP_lane_ROUND {
+my ($a0,$b0,$c0,$d0)=@_;
+my ($a1,$b1,$c1,$d1)=map(($_&~3)+(($_+1)&3),($a0,$b0,$c0,$d0));
+my ($a2,$b2,$c2,$d2)=map(($_&~3)+(($_+1)&3),($a1,$b1,$c1,$d1));
+my ($a3,$b3,$c3,$d3)=map(($_&~3)+(($_+1)&3),($a2,$b2,$c2,$d2));
+my @x=map("\"$_\"",@xx);
+
+	(
+	"&vpaddd	(@x[$a0],@x[$a0],@x[$b0])",	# Q1
+	 "&vpaddd	(@x[$a1],@x[$a1],@x[$b1])",	# Q2
+	  "&vpaddd	(@x[$a2],@x[$a2],@x[$b2])",	# Q3
+	   "&vpaddd	(@x[$a3],@x[$a3],@x[$b3])",	# Q4
+	"&vpxor		(@x[$d0],@x[$a0],@x[$d0])",
+	 "&vpxor	(@x[$d1],@x[$a1],@x[$d1])",
+	  "&vpxor	(@x[$d2],@x[$a2],@x[$d2])",
+	   "&vpxor	(@x[$d3],@x[$a3],@x[$d3])",
+	"&vprotd	(@x[$d0],@x[$d0],16)",
+	 "&vprotd	(@x[$d1],@x[$d1],16)",
+	  "&vprotd	(@x[$d2],@x[$d2],16)",
+	   "&vprotd	(@x[$d3],@x[$d3],16)",
+
+	"&vpaddd	(@x[$c0],@x[$c0],@x[$d0])",
+	 "&vpaddd	(@x[$c1],@x[$c1],@x[$d1])",
+	  "&vpaddd	(@x[$c2],@x[$c2],@x[$d2])",
+	   "&vpaddd	(@x[$c3],@x[$c3],@x[$d3])",
+	"&vpxor		(@x[$b0],@x[$c0],@x[$b0])",
+	 "&vpxor	(@x[$b1],@x[$c1],@x[$b1])",
+	  "&vpxor	(@x[$b2],@x[$b2],@x[$c2])",	# flip
+	   "&vpxor	(@x[$b3],@x[$b3],@x[$c3])",	# flip
+	"&vprotd	(@x[$b0],@x[$b0],12)",
+	 "&vprotd	(@x[$b1],@x[$b1],12)",
+	  "&vprotd	(@x[$b2],@x[$b2],12)",
+	   "&vprotd	(@x[$b3],@x[$b3],12)",
+
+	"&vpaddd	(@x[$a0],@x[$b0],@x[$a0])",	# flip
+	 "&vpaddd	(@x[$a1],@x[$b1],@x[$a1])",	# flip
+	  "&vpaddd	(@x[$a2],@x[$a2],@x[$b2])",
+	   "&vpaddd	(@x[$a3],@x[$a3],@x[$b3])",
+	"&vpxor		(@x[$d0],@x[$a0],@x[$d0])",
+	 "&vpxor	(@x[$d1],@x[$a1],@x[$d1])",
+	  "&vpxor	(@x[$d2],@x[$a2],@x[$d2])",
+	   "&vpxor	(@x[$d3],@x[$a3],@x[$d3])",
+	"&vprotd	(@x[$d0],@x[$d0],8)",
+	 "&vprotd	(@x[$d1],@x[$d1],8)",
+	  "&vprotd	(@x[$d2],@x[$d2],8)",
+	   "&vprotd	(@x[$d3],@x[$d3],8)",
+
+	"&vpaddd	(@x[$c0],@x[$c0],@x[$d0])",
+	 "&vpaddd	(@x[$c1],@x[$c1],@x[$d1])",
+	  "&vpaddd	(@x[$c2],@x[$c2],@x[$d2])",
+	   "&vpaddd	(@x[$c3],@x[$c3],@x[$d3])",
+	"&vpxor		(@x[$b0],@x[$c0],@x[$b0])",
+	 "&vpxor	(@x[$b1],@x[$c1],@x[$b1])",
+	  "&vpxor	(@x[$b2],@x[$b2],@x[$c2])",	# flip
+	   "&vpxor	(@x[$b3],@x[$b3],@x[$c3])",	# flip
+	"&vprotd	(@x[$b0],@x[$b0],7)",
+	 "&vprotd	(@x[$b1],@x[$b1],7)",
+	  "&vprotd	(@x[$b2],@x[$b2],7)",
+	   "&vprotd	(@x[$b3],@x[$b3],7)"
+	);
+}
+
+my $xframe = $win64 ? 0xa0 : 0;
+
+$code.=<<___;
+.type	ChaCha20_4xop,\@function,5
+.align	32
+ChaCha20_4xop:
+.LChaCha20_4xop:
+	lea		-0x78(%rsp),%r11
+	sub		\$0x148+$xframe,%rsp
+___
+	################ stack layout
+	# +0x00		SIMD equivalent of @x[8-12]
+	# ...
+	# +0x40		constant copy of key[0-2] smashed by lanes
+	# ...
+	# +0x100	SIMD counters (with nonce smashed by lanes)
+	# ...
+	# +0x140
+$code.=<<___	if ($win64);
+	movaps		%xmm6,-0x30(%r11)
+	movaps		%xmm7,-0x20(%r11)
+	movaps		%xmm8,-0x10(%r11)
+	movaps		%xmm9,0x00(%r11)
+	movaps		%xmm10,0x10(%r11)
+	movaps		%xmm11,0x20(%r11)
+	movaps		%xmm12,0x30(%r11)
+	movaps		%xmm13,0x40(%r11)
+	movaps		%xmm14,0x50(%r11)
+	movaps		%xmm15,0x60(%r11)
+___
+$code.=<<___;
+	vzeroupper
+
+	vmovdqa		.Lsigma(%rip),$xa3	# key[0]
+	vmovdqu		($key),$xb3		# key[1]
+	vmovdqu		16($key),$xt3		# key[2]
+	vmovdqu		($counter),$xd3		# key[3]
+	lea		0x100(%rsp),%rcx	# size optimization
+
+	vpshufd		\$0x00,$xa3,$xa0	# smash key by lanes...
+	vpshufd		\$0x55,$xa3,$xa1
+	vmovdqa		$xa0,0x40(%rsp)		# ... and offload
+	vpshufd		\$0xaa,$xa3,$xa2
+	vmovdqa		$xa1,0x50(%rsp)
+	vpshufd		\$0xff,$xa3,$xa3
+	vmovdqa		$xa2,0x60(%rsp)
+	vmovdqa		$xa3,0x70(%rsp)
+
+	vpshufd		\$0x00,$xb3,$xb0
+	vpshufd		\$0x55,$xb3,$xb1
+	vmovdqa		$xb0,0x80-0x100(%rcx)
+	vpshufd		\$0xaa,$xb3,$xb2
+	vmovdqa		$xb1,0x90-0x100(%rcx)
+	vpshufd		\$0xff,$xb3,$xb3
+	vmovdqa		$xb2,0xa0-0x100(%rcx)
+	vmovdqa		$xb3,0xb0-0x100(%rcx)
+
+	vpshufd		\$0x00,$xt3,$xt0	# "$xc0"
+	vpshufd		\$0x55,$xt3,$xt1	# "$xc1"
+	vmovdqa		$xt0,0xc0-0x100(%rcx)
+	vpshufd		\$0xaa,$xt3,$xt2	# "$xc2"
+	vmovdqa		$xt1,0xd0-0x100(%rcx)
+	vpshufd		\$0xff,$xt3,$xt3	# "$xc3"
+	vmovdqa		$xt2,0xe0-0x100(%rcx)
+	vmovdqa		$xt3,0xf0-0x100(%rcx)
+
+	vpshufd		\$0x00,$xd3,$xd0
+	vpshufd		\$0x55,$xd3,$xd1
+	vpaddd		.Linc(%rip),$xd0,$xd0	# don't save counters yet
+	vpshufd		\$0xaa,$xd3,$xd2
+	vmovdqa		$xd1,0x110-0x100(%rcx)
+	vpshufd		\$0xff,$xd3,$xd3
+	vmovdqa		$xd2,0x120-0x100(%rcx)
+	vmovdqa		$xd3,0x130-0x100(%rcx)
+
+	jmp		.Loop_enter4xop
+
+.align	32
+.Loop_outer4xop:
+	vmovdqa		0x40(%rsp),$xa0		# re-load smashed key
+	vmovdqa		0x50(%rsp),$xa1
+	vmovdqa		0x60(%rsp),$xa2
+	vmovdqa		0x70(%rsp),$xa3
+	vmovdqa		0x80-0x100(%rcx),$xb0
+	vmovdqa		0x90-0x100(%rcx),$xb1
+	vmovdqa		0xa0-0x100(%rcx),$xb2
+	vmovdqa		0xb0-0x100(%rcx),$xb3
+	vmovdqa		0xc0-0x100(%rcx),$xt0	# "$xc0"
+	vmovdqa		0xd0-0x100(%rcx),$xt1	# "$xc1"
+	vmovdqa		0xe0-0x100(%rcx),$xt2	# "$xc2"
+	vmovdqa		0xf0-0x100(%rcx),$xt3	# "$xc3"
+	vmovdqa		0x100-0x100(%rcx),$xd0
+	vmovdqa		0x110-0x100(%rcx),$xd1
+	vmovdqa		0x120-0x100(%rcx),$xd2
+	vmovdqa		0x130-0x100(%rcx),$xd3
+	vpaddd		.Lfour(%rip),$xd0,$xd0	# next SIMD counters
+
+.Loop_enter4xop:
+	mov		\$10,%eax
+	vmovdqa		$xd0,0x100-0x100(%rcx)	# save SIMD counters
+	jmp		.Loop4xop
+
+.align	32
+.Loop4xop:
+___
+	foreach (&XOP_lane_ROUND(0, 4, 8,12)) { eval; }
+	foreach (&XOP_lane_ROUND(0, 5,10,15)) { eval; }
+$code.=<<___;
+	dec		%eax
+	jnz		.Loop4xop
+
+	vpaddd		0x40(%rsp),$xa0,$xa0	# accumulate key material
+	vpaddd		0x50(%rsp),$xa1,$xa1
+	vpaddd		0x60(%rsp),$xa2,$xa2
+	vpaddd		0x70(%rsp),$xa3,$xa3
+
+	vmovdqa		$xt2,0x20(%rsp)		# offload $xc2,3
+	vmovdqa		$xt3,0x30(%rsp)
+
+	vpunpckldq	$xa1,$xa0,$xt2		# "de-interlace" data
+	vpunpckldq	$xa3,$xa2,$xt3
+	vpunpckhdq	$xa1,$xa0,$xa0
+	vpunpckhdq	$xa3,$xa2,$xa2
+	vpunpcklqdq	$xt3,$xt2,$xa1		# "a0"
+	vpunpckhqdq	$xt3,$xt2,$xt2		# "a1"
+	vpunpcklqdq	$xa2,$xa0,$xa3		# "a2"
+	vpunpckhqdq	$xa2,$xa0,$xa0		# "a3"
+___
+        ($xa0,$xa1,$xa2,$xa3,$xt2)=($xa1,$xt2,$xa3,$xa0,$xa2);
+$code.=<<___;
+	vpaddd		0x80-0x100(%rcx),$xb0,$xb0
+	vpaddd		0x90-0x100(%rcx),$xb1,$xb1
+	vpaddd		0xa0-0x100(%rcx),$xb2,$xb2
+	vpaddd		0xb0-0x100(%rcx),$xb3,$xb3
+
+	vmovdqa		$xa0,0x00(%rsp)		# offload $xa0,1
+	vmovdqa		$xa1,0x10(%rsp)
+	vmovdqa		0x20(%rsp),$xa0		# "xc2"
+	vmovdqa		0x30(%rsp),$xa1		# "xc3"
+
+	vpunpckldq	$xb1,$xb0,$xt2
+	vpunpckldq	$xb3,$xb2,$xt3
+	vpunpckhdq	$xb1,$xb0,$xb0
+	vpunpckhdq	$xb3,$xb2,$xb2
+	vpunpcklqdq	$xt3,$xt2,$xb1		# "b0"
+	vpunpckhqdq	$xt3,$xt2,$xt2		# "b1"
+	vpunpcklqdq	$xb2,$xb0,$xb3		# "b2"
+	vpunpckhqdq	$xb2,$xb0,$xb0		# "b3"
+___
+	($xb0,$xb1,$xb2,$xb3,$xt2)=($xb1,$xt2,$xb3,$xb0,$xb2);
+	my ($xc0,$xc1,$xc2,$xc3)=($xt0,$xt1,$xa0,$xa1);
+$code.=<<___;
+	vpaddd		0xc0-0x100(%rcx),$xc0,$xc0
+	vpaddd		0xd0-0x100(%rcx),$xc1,$xc1
+	vpaddd		0xe0-0x100(%rcx),$xc2,$xc2
+	vpaddd		0xf0-0x100(%rcx),$xc3,$xc3
+
+	vpunpckldq	$xc1,$xc0,$xt2
+	vpunpckldq	$xc3,$xc2,$xt3
+	vpunpckhdq	$xc1,$xc0,$xc0
+	vpunpckhdq	$xc3,$xc2,$xc2
+	vpunpcklqdq	$xt3,$xt2,$xc1		# "c0"
+	vpunpckhqdq	$xt3,$xt2,$xt2		# "c1"
+	vpunpcklqdq	$xc2,$xc0,$xc3		# "c2"
+	vpunpckhqdq	$xc2,$xc0,$xc0		# "c3"
+___
+	($xc0,$xc1,$xc2,$xc3,$xt2)=($xc1,$xt2,$xc3,$xc0,$xc2);
+$code.=<<___;
+	vpaddd		0x100-0x100(%rcx),$xd0,$xd0
+	vpaddd		0x110-0x100(%rcx),$xd1,$xd1
+	vpaddd		0x120-0x100(%rcx),$xd2,$xd2
+	vpaddd		0x130-0x100(%rcx),$xd3,$xd3
+
+	vpunpckldq	$xd1,$xd0,$xt2
+	vpunpckldq	$xd3,$xd2,$xt3
+	vpunpckhdq	$xd1,$xd0,$xd0
+	vpunpckhdq	$xd3,$xd2,$xd2
+	vpunpcklqdq	$xt3,$xt2,$xd1		# "d0"
+	vpunpckhqdq	$xt3,$xt2,$xt2		# "d1"
+	vpunpcklqdq	$xd2,$xd0,$xd3		# "d2"
+	vpunpckhqdq	$xd2,$xd0,$xd0		# "d3"
+___
+	($xd0,$xd1,$xd2,$xd3,$xt2)=($xd1,$xt2,$xd3,$xd0,$xd2);
+	($xa0,$xa1)=($xt2,$xt3);
+$code.=<<___;
+	vmovdqa		0x00(%rsp),$xa0		# restore $xa0,1
+	vmovdqa		0x10(%rsp),$xa1
+
+	cmp		\$64*4,$len
+	jb		.Ltail4xop
+
+	vpxor		0x00($inp),$xa0,$xa0	# xor with input
+	vpxor		0x10($inp),$xb0,$xb0
+	vpxor		0x20($inp),$xc0,$xc0
+	vpxor		0x30($inp),$xd0,$xd0
+	vpxor		0x40($inp),$xa1,$xa1
+	vpxor		0x50($inp),$xb1,$xb1
+	vpxor		0x60($inp),$xc1,$xc1
+	vpxor		0x70($inp),$xd1,$xd1
+	lea		0x80($inp),$inp		# size optimization
+	vpxor		0x00($inp),$xa2,$xa2
+	vpxor		0x10($inp),$xb2,$xb2
+	vpxor		0x20($inp),$xc2,$xc2
+	vpxor		0x30($inp),$xd2,$xd2
+	vpxor		0x40($inp),$xa3,$xa3
+	vpxor		0x50($inp),$xb3,$xb3
+	vpxor		0x60($inp),$xc3,$xc3
+	vpxor		0x70($inp),$xd3,$xd3
+	lea		0x80($inp),$inp		# inp+=64*4
+
+	vmovdqu		$xa0,0x00($out)
+	vmovdqu		$xb0,0x10($out)
+	vmovdqu		$xc0,0x20($out)
+	vmovdqu		$xd0,0x30($out)
+	vmovdqu		$xa1,0x40($out)
+	vmovdqu		$xb1,0x50($out)
+	vmovdqu		$xc1,0x60($out)
+	vmovdqu		$xd1,0x70($out)
+	lea		0x80($out),$out		# size optimization
+	vmovdqu		$xa2,0x00($out)
+	vmovdqu		$xb2,0x10($out)
+	vmovdqu		$xc2,0x20($out)
+	vmovdqu		$xd2,0x30($out)
+	vmovdqu		$xa3,0x40($out)
+	vmovdqu		$xb3,0x50($out)
+	vmovdqu		$xc3,0x60($out)
+	vmovdqu		$xd3,0x70($out)
+	lea		0x80($out),$out		# out+=64*4
+
+	sub		\$64*4,$len
+	jnz		.Loop_outer4xop
+
+	jmp		.Ldone4xop
+
+.align	32
+.Ltail4xop:
+	cmp		\$192,$len
+	jae		.L192_or_more4xop
+	cmp		\$128,$len
+	jae		.L128_or_more4xop
+	cmp		\$64,$len
+	jae		.L64_or_more4xop
+
+	xor		%r10,%r10
+	vmovdqa		$xa0,0x00(%rsp)
+	vmovdqa		$xb0,0x10(%rsp)
+	vmovdqa		$xc0,0x20(%rsp)
+	vmovdqa		$xd0,0x30(%rsp)
+	jmp		.Loop_tail4xop
+
+.align	32
+.L64_or_more4xop:
+	vpxor		0x00($inp),$xa0,$xa0	# xor with input
+	vpxor		0x10($inp),$xb0,$xb0
+	vpxor		0x20($inp),$xc0,$xc0
+	vpxor		0x30($inp),$xd0,$xd0
+	vmovdqu		$xa0,0x00($out)
+	vmovdqu		$xb0,0x10($out)
+	vmovdqu		$xc0,0x20($out)
+	vmovdqu		$xd0,0x30($out)
+	je		.Ldone4xop
+
+	lea		0x40($inp),$inp		# inp+=64*1
+	vmovdqa		$xa1,0x00(%rsp)
+	xor		%r10,%r10
+	vmovdqa		$xb1,0x10(%rsp)
+	lea		0x40($out),$out		# out+=64*1
+	vmovdqa		$xc1,0x20(%rsp)
+	sub		\$64,$len		# len-=64*1
+	vmovdqa		$xd1,0x30(%rsp)
+	jmp		.Loop_tail4xop
+
+.align	32
+.L128_or_more4xop:
+	vpxor		0x00($inp),$xa0,$xa0	# xor with input
+	vpxor		0x10($inp),$xb0,$xb0
+	vpxor		0x20($inp),$xc0,$xc0
+	vpxor		0x30($inp),$xd0,$xd0
+	vpxor		0x40($inp),$xa1,$xa1
+	vpxor		0x50($inp),$xb1,$xb1
+	vpxor		0x60($inp),$xc1,$xc1
+	vpxor		0x70($inp),$xd1,$xd1
+
+	vmovdqu		$xa0,0x00($out)
+	vmovdqu		$xb0,0x10($out)
+	vmovdqu		$xc0,0x20($out)
+	vmovdqu		$xd0,0x30($out)
+	vmovdqu		$xa1,0x40($out)
+	vmovdqu		$xb1,0x50($out)
+	vmovdqu		$xc1,0x60($out)
+	vmovdqu		$xd1,0x70($out)
+	je		.Ldone4xop
+
+	lea		0x80($inp),$inp		# inp+=64*2
+	vmovdqa		$xa2,0x00(%rsp)
+	xor		%r10,%r10
+	vmovdqa		$xb2,0x10(%rsp)
+	lea		0x80($out),$out		# out+=64*2
+	vmovdqa		$xc2,0x20(%rsp)
+	sub		\$128,$len		# len-=64*2
+	vmovdqa		$xd2,0x30(%rsp)
+	jmp		.Loop_tail4xop
+
+.align	32
+.L192_or_more4xop:
+	vpxor		0x00($inp),$xa0,$xa0	# xor with input
+	vpxor		0x10($inp),$xb0,$xb0
+	vpxor		0x20($inp),$xc0,$xc0
+	vpxor		0x30($inp),$xd0,$xd0
+	vpxor		0x40($inp),$xa1,$xa1
+	vpxor		0x50($inp),$xb1,$xb1
+	vpxor		0x60($inp),$xc1,$xc1
+	vpxor		0x70($inp),$xd1,$xd1
+	lea		0x80($inp),$inp		# size optimization
+	vpxor		0x00($inp),$xa2,$xa2
+	vpxor		0x10($inp),$xb2,$xb2
+	vpxor		0x20($inp),$xc2,$xc2
+	vpxor		0x30($inp),$xd2,$xd2
+
+	vmovdqu		$xa0,0x00($out)
+	vmovdqu		$xb0,0x10($out)
+	vmovdqu		$xc0,0x20($out)
+	vmovdqu		$xd0,0x30($out)
+	vmovdqu		$xa1,0x40($out)
+	vmovdqu		$xb1,0x50($out)
+	vmovdqu		$xc1,0x60($out)
+	vmovdqu		$xd1,0x70($out)
+	lea		0x80($out),$out		# size optimization
+	vmovdqu		$xa2,0x00($out)
+	vmovdqu		$xb2,0x10($out)
+	vmovdqu		$xc2,0x20($out)
+	vmovdqu		$xd2,0x30($out)
+	je		.Ldone4xop
+
+	lea		0x40($inp),$inp		# inp+=64*3
+	vmovdqa		$xa3,0x00(%rsp)
+	xor		%r10,%r10
+	vmovdqa		$xb3,0x10(%rsp)
+	lea		0x40($out),$out		# out+=64*3
+	vmovdqa		$xc3,0x20(%rsp)
+	sub		\$192,$len		# len-=64*3
+	vmovdqa		$xd3,0x30(%rsp)
+
+.Loop_tail4xop:
+	movzb		($inp,%r10),%eax
+	movzb		(%rsp,%r10),%ecx
+	lea		1(%r10),%r10
+	xor		%ecx,%eax
+	mov		%al,-1($out,%r10)
+	dec		$len
+	jnz		.Loop_tail4xop
+
+.Ldone4xop:
+	vzeroupper
+___
+$code.=<<___	if ($win64);
+	lea		0x140+0x30(%rsp),%r11
+	movaps		-0x30(%r11),%xmm6
+	movaps		-0x20(%r11),%xmm7
+	movaps		-0x10(%r11),%xmm8
+	movaps		0x00(%r11),%xmm9
+	movaps		0x10(%r11),%xmm10
+	movaps		0x20(%r11),%xmm11
+	movaps		0x30(%r11),%xmm12
+	movaps		0x40(%r11),%xmm13
+	movaps		0x50(%r11),%xmm14
+	movaps		0x60(%r11),%xmm15
+___
+$code.=<<___;
+	add		\$0x148+$xframe,%rsp
+	ret
+.size	ChaCha20_4xop,.-ChaCha20_4xop
+___
+}
+
+########################################################################
+# AVX2 code path
+if ($avx>1) {
+my ($xb0,$xb1,$xb2,$xb3, $xd0,$xd1,$xd2,$xd3,
+    $xa0,$xa1,$xa2,$xa3, $xt0,$xt1,$xt2,$xt3)=map("%ymm$_",(0..15));
+my @xx=($xa0,$xa1,$xa2,$xa3, $xb0,$xb1,$xb2,$xb3,
+	"%nox","%nox","%nox","%nox", $xd0,$xd1,$xd2,$xd3);
+
+sub AVX2_lane_ROUND {
+my ($a0,$b0,$c0,$d0)=@_;
+my ($a1,$b1,$c1,$d1)=map(($_&~3)+(($_+1)&3),($a0,$b0,$c0,$d0));
+my ($a2,$b2,$c2,$d2)=map(($_&~3)+(($_+1)&3),($a1,$b1,$c1,$d1));
+my ($a3,$b3,$c3,$d3)=map(($_&~3)+(($_+1)&3),($a2,$b2,$c2,$d2));
+my ($xc,$xc_,$t0,$t1)=map("\"$_\"",$xt0,$xt1,$xt2,$xt3);
+my @x=map("\"$_\"",@xx);
+
+	# Consider order in which variables are addressed by their
+	# index:
+	#
+	#	a   b   c   d
+	#
+	#	0   4   8  12 < even round
+	#	1   5   9  13
+	#	2   6  10  14
+	#	3   7  11  15
+	#	0   5  10  15 < odd round
+	#	1   6  11  12
+	#	2   7   8  13
+	#	3   4   9  14
+	#
+	# 'a', 'b' and 'd's are permanently allocated in registers,
+	# @x[0..7,12..15], while 'c's are maintained in memory. If
+	# you observe 'c' column, you'll notice that pair of 'c's is
+	# invariant between rounds. This means that we have to reload
+	# them once per round, in the middle. This is why you'll see
+	# bunch of 'c' stores and loads in the middle, but none in
+	# the beginning or end.
+
+	(
+	"&vpaddd	(@x[$a0],@x[$a0],@x[$b0])",	# Q1
+	"&vpxor		(@x[$d0],@x[$a0],@x[$d0])",
+	"&vpshufb	(@x[$d0],@x[$d0],$t1)",
+	 "&vpaddd	(@x[$a1],@x[$a1],@x[$b1])",	# Q2
+	 "&vpxor	(@x[$d1],@x[$a1],@x[$d1])",
+	 "&vpshufb	(@x[$d1],@x[$d1],$t1)",
+
+	"&vpaddd	($xc,$xc,@x[$d0])",
+	"&vpxor		(@x[$b0],$xc,@x[$b0])",
+	"&vpslld	($t0,@x[$b0],12)",
+	"&vpsrld	(@x[$b0],@x[$b0],20)",
+	"&vpor		(@x[$b0],$t0,@x[$b0])",
+	"&vbroadcasti128($t0,'(%r11)')",		# .Lrot24(%rip)
+	 "&vpaddd	($xc_,$xc_,@x[$d1])",
+	 "&vpxor	(@x[$b1],$xc_,@x[$b1])",
+	 "&vpslld	($t1,@x[$b1],12)",
+	 "&vpsrld	(@x[$b1],@x[$b1],20)",
+	 "&vpor		(@x[$b1],$t1,@x[$b1])",
+
+	"&vpaddd	(@x[$a0],@x[$a0],@x[$b0])",
+	"&vpxor		(@x[$d0],@x[$a0],@x[$d0])",
+	"&vpshufb	(@x[$d0],@x[$d0],$t0)",
+	 "&vpaddd	(@x[$a1],@x[$a1],@x[$b1])",
+	 "&vpxor	(@x[$d1],@x[$a1],@x[$d1])",
+	 "&vpshufb	(@x[$d1],@x[$d1],$t0)",
+
+	"&vpaddd	($xc,$xc,@x[$d0])",
+	"&vpxor		(@x[$b0],$xc,@x[$b0])",
+	"&vpslld	($t1,@x[$b0],7)",
+	"&vpsrld	(@x[$b0],@x[$b0],25)",
+	"&vpor		(@x[$b0],$t1,@x[$b0])",
+	"&vbroadcasti128($t1,'(%r10)')",		# .Lrot16(%rip)
+	 "&vpaddd	($xc_,$xc_,@x[$d1])",
+	 "&vpxor	(@x[$b1],$xc_,@x[$b1])",
+	 "&vpslld	($t0,@x[$b1],7)",
+	 "&vpsrld	(@x[$b1],@x[$b1],25)",
+	 "&vpor		(@x[$b1],$t0,@x[$b1])",
+
+	"&vmovdqa	(\"`32*($c0-8)`(%rsp)\",$xc)",	# reload pair of 'c's
+	 "&vmovdqa	(\"`32*($c1-8)`(%rsp)\",$xc_)",
+	"&vmovdqa	($xc,\"`32*($c2-8)`(%rsp)\")",
+	 "&vmovdqa	($xc_,\"`32*($c3-8)`(%rsp)\")",
+
+	"&vpaddd	(@x[$a2],@x[$a2],@x[$b2])",	# Q3
+	"&vpxor		(@x[$d2],@x[$a2],@x[$d2])",
+	"&vpshufb	(@x[$d2],@x[$d2],$t1)",
+	 "&vpaddd	(@x[$a3],@x[$a3],@x[$b3])",	# Q4
+	 "&vpxor	(@x[$d3],@x[$a3],@x[$d3])",
+	 "&vpshufb	(@x[$d3],@x[$d3],$t1)",
+
+	"&vpaddd	($xc,$xc,@x[$d2])",
+	"&vpxor		(@x[$b2],$xc,@x[$b2])",
+	"&vpslld	($t0,@x[$b2],12)",
+	"&vpsrld	(@x[$b2],@x[$b2],20)",
+	"&vpor		(@x[$b2],$t0,@x[$b2])",
+	"&vbroadcasti128($t0,'(%r11)')",		# .Lrot24(%rip)
+	 "&vpaddd	($xc_,$xc_,@x[$d3])",
+	 "&vpxor	(@x[$b3],$xc_,@x[$b3])",
+	 "&vpslld	($t1,@x[$b3],12)",
+	 "&vpsrld	(@x[$b3],@x[$b3],20)",
+	 "&vpor		(@x[$b3],$t1,@x[$b3])",
+
+	"&vpaddd	(@x[$a2],@x[$a2],@x[$b2])",
+	"&vpxor		(@x[$d2],@x[$a2],@x[$d2])",
+	"&vpshufb	(@x[$d2],@x[$d2],$t0)",
+	 "&vpaddd	(@x[$a3],@x[$a3],@x[$b3])",
+	 "&vpxor	(@x[$d3],@x[$a3],@x[$d3])",
+	 "&vpshufb	(@x[$d3],@x[$d3],$t0)",
+
+	"&vpaddd	($xc,$xc,@x[$d2])",
+	"&vpxor		(@x[$b2],$xc,@x[$b2])",
+	"&vpslld	($t1,@x[$b2],7)",
+	"&vpsrld	(@x[$b2],@x[$b2],25)",
+	"&vpor		(@x[$b2],$t1,@x[$b2])",
+	"&vbroadcasti128($t1,'(%r10)')",		# .Lrot16(%rip)
+	 "&vpaddd	($xc_,$xc_,@x[$d3])",
+	 "&vpxor	(@x[$b3],$xc_,@x[$b3])",
+	 "&vpslld	($t0,@x[$b3],7)",
+	 "&vpsrld	(@x[$b3],@x[$b3],25)",
+	 "&vpor		(@x[$b3],$t0,@x[$b3])"
+	);
+}
+
+my $xframe = $win64 ? 0xb0 : 8;
+
+$code.=<<___;
+.type	ChaCha20_8x,\@function,5
+.align	32
+ChaCha20_8x:
+.LChaCha20_8x:
+	mov		%rsp,%r10
+	sub		\$0x280+$xframe,%rsp
+	and		\$-32,%rsp
+___
+$code.=<<___	if ($win64);
+	lea		0x290+0x30(%rsp),%r11
+	movaps		%xmm6,-0x30(%r11)
+	movaps		%xmm7,-0x20(%r11)
+	movaps		%xmm8,-0x10(%r11)
+	movaps		%xmm9,0x00(%r11)
+	movaps		%xmm10,0x10(%r11)
+	movaps		%xmm11,0x20(%r11)
+	movaps		%xmm12,0x30(%r11)
+	movaps		%xmm13,0x40(%r11)
+	movaps		%xmm14,0x50(%r11)
+	movaps		%xmm15,0x60(%r11)
+___
+$code.=<<___;
+	vzeroupper
+	mov		%r10,0x280(%rsp)
+
+	################ stack layout
+	# +0x00		SIMD equivalent of @x[8-12]
+	# ...
+	# +0x80		constant copy of key[0-2] smashed by lanes
+	# ...
+	# +0x200	SIMD counters (with nonce smashed by lanes)
+	# ...
+	# +0x280	saved %rsp
+
+	vbroadcasti128	.Lsigma(%rip),$xa3	# key[0]
+	vbroadcasti128	($key),$xb3		# key[1]
+	vbroadcasti128	16($key),$xt3		# key[2]
+	vbroadcasti128	($counter),$xd3		# key[3]
+	lea		0x100(%rsp),%rcx	# size optimization
+	lea		0x200(%rsp),%rax	# size optimization
+	lea		.Lrot16(%rip),%r10
+	lea		.Lrot24(%rip),%r11
+
+	vpshufd		\$0x00,$xa3,$xa0	# smash key by lanes...
+	vpshufd		\$0x55,$xa3,$xa1
+	vmovdqa		$xa0,0x80-0x100(%rcx)	# ... and offload
+	vpshufd		\$0xaa,$xa3,$xa2
+	vmovdqa		$xa1,0xa0-0x100(%rcx)
+	vpshufd		\$0xff,$xa3,$xa3
+	vmovdqa		$xa2,0xc0-0x100(%rcx)
+	vmovdqa		$xa3,0xe0-0x100(%rcx)
+
+	vpshufd		\$0x00,$xb3,$xb0
+	vpshufd		\$0x55,$xb3,$xb1
+	vmovdqa		$xb0,0x100-0x100(%rcx)
+	vpshufd		\$0xaa,$xb3,$xb2
+	vmovdqa		$xb1,0x120-0x100(%rcx)
+	vpshufd		\$0xff,$xb3,$xb3
+	vmovdqa		$xb2,0x140-0x100(%rcx)
+	vmovdqa		$xb3,0x160-0x100(%rcx)
+
+	vpshufd		\$0x00,$xt3,$xt0	# "xc0"
+	vpshufd		\$0x55,$xt3,$xt1	# "xc1"
+	vmovdqa		$xt0,0x180-0x200(%rax)
+	vpshufd		\$0xaa,$xt3,$xt2	# "xc2"
+	vmovdqa		$xt1,0x1a0-0x200(%rax)
+	vpshufd		\$0xff,$xt3,$xt3	# "xc3"
+	vmovdqa		$xt2,0x1c0-0x200(%rax)
+	vmovdqa		$xt3,0x1e0-0x200(%rax)
+
+	vpshufd		\$0x00,$xd3,$xd0
+	vpshufd		\$0x55,$xd3,$xd1
+	vpaddd		.Lincy(%rip),$xd0,$xd0	# don't save counters yet
+	vpshufd		\$0xaa,$xd3,$xd2
+	vmovdqa		$xd1,0x220-0x200(%rax)
+	vpshufd		\$0xff,$xd3,$xd3
+	vmovdqa		$xd2,0x240-0x200(%rax)
+	vmovdqa		$xd3,0x260-0x200(%rax)
+
+	jmp		.Loop_enter8x
+
+.align	32
+.Loop_outer8x:
+	vmovdqa		0x80-0x100(%rcx),$xa0	# re-load smashed key
+	vmovdqa		0xa0-0x100(%rcx),$xa1
+	vmovdqa		0xc0-0x100(%rcx),$xa2
+	vmovdqa		0xe0-0x100(%rcx),$xa3
+	vmovdqa		0x100-0x100(%rcx),$xb0
+	vmovdqa		0x120-0x100(%rcx),$xb1
+	vmovdqa		0x140-0x100(%rcx),$xb2
+	vmovdqa		0x160-0x100(%rcx),$xb3
+	vmovdqa		0x180-0x200(%rax),$xt0	# "xc0"
+	vmovdqa		0x1a0-0x200(%rax),$xt1	# "xc1"
+	vmovdqa		0x1c0-0x200(%rax),$xt2	# "xc2"
+	vmovdqa		0x1e0-0x200(%rax),$xt3	# "xc3"
+	vmovdqa		0x200-0x200(%rax),$xd0
+	vmovdqa		0x220-0x200(%rax),$xd1
+	vmovdqa		0x240-0x200(%rax),$xd2
+	vmovdqa		0x260-0x200(%rax),$xd3
+	vpaddd		.Leight(%rip),$xd0,$xd0	# next SIMD counters
+
+.Loop_enter8x:
+	vmovdqa		$xt2,0x40(%rsp)		# SIMD equivalent of "@x[10]"
+	vmovdqa		$xt3,0x60(%rsp)		# SIMD equivalent of "@x[11]"
+	vbroadcasti128	(%r10),$xt3
+	vmovdqa		$xd0,0x200-0x200(%rax)	# save SIMD counters
+	mov		\$10,%eax
+	jmp		.Loop8x
+
+.align	32
+.Loop8x:
+___
+	foreach (&AVX2_lane_ROUND(0, 4, 8,12)) { eval; }
+	foreach (&AVX2_lane_ROUND(0, 5,10,15)) { eval; }
+$code.=<<___;
+	dec		%eax
+	jnz		.Loop8x
+
+	lea		0x200(%rsp),%rax	# size optimization
+	vpaddd		0x80-0x100(%rcx),$xa0,$xa0	# accumulate key
+	vpaddd		0xa0-0x100(%rcx),$xa1,$xa1
+	vpaddd		0xc0-0x100(%rcx),$xa2,$xa2
+	vpaddd		0xe0-0x100(%rcx),$xa3,$xa3
+
+	vpunpckldq	$xa1,$xa0,$xt2		# "de-interlace" data
+	vpunpckldq	$xa3,$xa2,$xt3
+	vpunpckhdq	$xa1,$xa0,$xa0
+	vpunpckhdq	$xa3,$xa2,$xa2
+	vpunpcklqdq	$xt3,$xt2,$xa1		# "a0"
+	vpunpckhqdq	$xt3,$xt2,$xt2		# "a1"
+	vpunpcklqdq	$xa2,$xa0,$xa3		# "a2"
+	vpunpckhqdq	$xa2,$xa0,$xa0		# "a3"
+___
+	($xa0,$xa1,$xa2,$xa3,$xt2)=($xa1,$xt2,$xa3,$xa0,$xa2);
+$code.=<<___;
+	vpaddd		0x100-0x100(%rcx),$xb0,$xb0
+	vpaddd		0x120-0x100(%rcx),$xb1,$xb1
+	vpaddd		0x140-0x100(%rcx),$xb2,$xb2
+	vpaddd		0x160-0x100(%rcx),$xb3,$xb3
+
+	vpunpckldq	$xb1,$xb0,$xt2
+	vpunpckldq	$xb3,$xb2,$xt3
+	vpunpckhdq	$xb1,$xb0,$xb0
+	vpunpckhdq	$xb3,$xb2,$xb2
+	vpunpcklqdq	$xt3,$xt2,$xb1		# "b0"
+	vpunpckhqdq	$xt3,$xt2,$xt2		# "b1"
+	vpunpcklqdq	$xb2,$xb0,$xb3		# "b2"
+	vpunpckhqdq	$xb2,$xb0,$xb0		# "b3"
+___
+	($xb0,$xb1,$xb2,$xb3,$xt2)=($xb1,$xt2,$xb3,$xb0,$xb2);
+$code.=<<___;
+	vperm2i128	\$0x20,$xb0,$xa0,$xt3	# "de-interlace" further
+	vperm2i128	\$0x31,$xb0,$xa0,$xb0
+	vperm2i128	\$0x20,$xb1,$xa1,$xa0
+	vperm2i128	\$0x31,$xb1,$xa1,$xb1
+	vperm2i128	\$0x20,$xb2,$xa2,$xa1
+	vperm2i128	\$0x31,$xb2,$xa2,$xb2
+	vperm2i128	\$0x20,$xb3,$xa3,$xa2
+	vperm2i128	\$0x31,$xb3,$xa3,$xb3
+___
+	($xa0,$xa1,$xa2,$xa3,$xt3)=($xt3,$xa0,$xa1,$xa2,$xa3);
+	my ($xc0,$xc1,$xc2,$xc3)=($xt0,$xt1,$xa0,$xa1);
+$code.=<<___;
+	vmovdqa		$xa0,0x00(%rsp)		# offload $xaN
+	vmovdqa		$xa1,0x20(%rsp)
+	vmovdqa		0x40(%rsp),$xc2		# $xa0
+	vmovdqa		0x60(%rsp),$xc3		# $xa1
+
+	vpaddd		0x180-0x200(%rax),$xc0,$xc0
+	vpaddd		0x1a0-0x200(%rax),$xc1,$xc1
+	vpaddd		0x1c0-0x200(%rax),$xc2,$xc2
+	vpaddd		0x1e0-0x200(%rax),$xc3,$xc3
+
+	vpunpckldq	$xc1,$xc0,$xt2
+	vpunpckldq	$xc3,$xc2,$xt3
+	vpunpckhdq	$xc1,$xc0,$xc0
+	vpunpckhdq	$xc3,$xc2,$xc2
+	vpunpcklqdq	$xt3,$xt2,$xc1		# "c0"
+	vpunpckhqdq	$xt3,$xt2,$xt2		# "c1"
+	vpunpcklqdq	$xc2,$xc0,$xc3		# "c2"
+	vpunpckhqdq	$xc2,$xc0,$xc0		# "c3"
+___
+	($xc0,$xc1,$xc2,$xc3,$xt2)=($xc1,$xt2,$xc3,$xc0,$xc2);
+$code.=<<___;
+	vpaddd		0x200-0x200(%rax),$xd0,$xd0
+	vpaddd		0x220-0x200(%rax),$xd1,$xd1
+	vpaddd		0x240-0x200(%rax),$xd2,$xd2
+	vpaddd		0x260-0x200(%rax),$xd3,$xd3
+
+	vpunpckldq	$xd1,$xd0,$xt2
+	vpunpckldq	$xd3,$xd2,$xt3
+	vpunpckhdq	$xd1,$xd0,$xd0
+	vpunpckhdq	$xd3,$xd2,$xd2
+	vpunpcklqdq	$xt3,$xt2,$xd1		# "d0"
+	vpunpckhqdq	$xt3,$xt2,$xt2		# "d1"
+	vpunpcklqdq	$xd2,$xd0,$xd3		# "d2"
+	vpunpckhqdq	$xd2,$xd0,$xd0		# "d3"
+___
+	($xd0,$xd1,$xd2,$xd3,$xt2)=($xd1,$xt2,$xd3,$xd0,$xd2);
+$code.=<<___;
+	vperm2i128	\$0x20,$xd0,$xc0,$xt3	# "de-interlace" further
+	vperm2i128	\$0x31,$xd0,$xc0,$xd0
+	vperm2i128	\$0x20,$xd1,$xc1,$xc0
+	vperm2i128	\$0x31,$xd1,$xc1,$xd1
+	vperm2i128	\$0x20,$xd2,$xc2,$xc1
+	vperm2i128	\$0x31,$xd2,$xc2,$xd2
+	vperm2i128	\$0x20,$xd3,$xc3,$xc2
+	vperm2i128	\$0x31,$xd3,$xc3,$xd3
+___
+	($xc0,$xc1,$xc2,$xc3,$xt3)=($xt3,$xc0,$xc1,$xc2,$xc3);
+	($xb0,$xb1,$xb2,$xb3,$xc0,$xc1,$xc2,$xc3)=
+	($xc0,$xc1,$xc2,$xc3,$xb0,$xb1,$xb2,$xb3);
+	($xa0,$xa1)=($xt2,$xt3);
+$code.=<<___;
+	vmovdqa		0x00(%rsp),$xa0		# $xaN was offloaded, remember?
+	vmovdqa		0x20(%rsp),$xa1
+
+	cmp		\$64*8,$len
+	jb		.Ltail8x
+
+	vpxor		0x00($inp),$xa0,$xa0	# xor with input
+	vpxor		0x20($inp),$xb0,$xb0
+	vpxor		0x40($inp),$xc0,$xc0
+	vpxor		0x60($inp),$xd0,$xd0
+	lea		0x80($inp),$inp		# size optimization
+	vmovdqu		$xa0,0x00($out)
+	vmovdqu		$xb0,0x20($out)
+	vmovdqu		$xc0,0x40($out)
+	vmovdqu		$xd0,0x60($out)
+	lea		0x80($out),$out		# size optimization
+
+	vpxor		0x00($inp),$xa1,$xa1
+	vpxor		0x20($inp),$xb1,$xb1
+	vpxor		0x40($inp),$xc1,$xc1
+	vpxor		0x60($inp),$xd1,$xd1
+	lea		0x80($inp),$inp		# size optimization
+	vmovdqu		$xa1,0x00($out)
+	vmovdqu		$xb1,0x20($out)
+	vmovdqu		$xc1,0x40($out)
+	vmovdqu		$xd1,0x60($out)
+	lea		0x80($out),$out		# size optimization
+
+	vpxor		0x00($inp),$xa2,$xa2
+	vpxor		0x20($inp),$xb2,$xb2
+	vpxor		0x40($inp),$xc2,$xc2
+	vpxor		0x60($inp),$xd2,$xd2
+	lea		0x80($inp),$inp		# size optimization
+	vmovdqu		$xa2,0x00($out)
+	vmovdqu		$xb2,0x20($out)
+	vmovdqu		$xc2,0x40($out)
+	vmovdqu		$xd2,0x60($out)
+	lea		0x80($out),$out		# size optimization
+
+	vpxor		0x00($inp),$xa3,$xa3
+	vpxor		0x20($inp),$xb3,$xb3
+	vpxor		0x40($inp),$xc3,$xc3
+	vpxor		0x60($inp),$xd3,$xd3
+	lea		0x80($inp),$inp		# size optimization
+	vmovdqu		$xa3,0x00($out)
+	vmovdqu		$xb3,0x20($out)
+	vmovdqu		$xc3,0x40($out)
+	vmovdqu		$xd3,0x60($out)
+	lea		0x80($out),$out		# size optimization
+
+	sub		\$64*8,$len
+	jnz		.Loop_outer8x
+
+	jmp		.Ldone8x
+
+.Ltail8x:
+	cmp		\$448,$len
+	jae		.L448_or_more8x
+	cmp		\$384,$len
+	jae		.L384_or_more8x
+	cmp		\$320,$len
+	jae		.L320_or_more8x
+	cmp		\$256,$len
+	jae		.L256_or_more8x
+	cmp		\$192,$len
+	jae		.L192_or_more8x
+	cmp		\$128,$len
+	jae		.L128_or_more8x
+	cmp		\$64,$len
+	jae		.L64_or_more8x
+
+	xor		%r10,%r10
+	vmovdqa		$xa0,0x00(%rsp)
+	vmovdqa		$xb0,0x20(%rsp)
+	jmp		.Loop_tail8x
+
+.align	32
+.L64_or_more8x:
+	vpxor		0x00($inp),$xa0,$xa0	# xor with input
+	vpxor		0x20($inp),$xb0,$xb0
+	vmovdqu		$xa0,0x00($out)
+	vmovdqu		$xb0,0x20($out)
+	je		.Ldone8x
+
+	lea		0x40($inp),$inp		# inp+=64*1
+	xor		%r10,%r10
+	vmovdqa		$xc0,0x00(%rsp)
+	lea		0x40($out),$out		# out+=64*1
+	sub		\$64,$len		# len-=64*1
+	vmovdqa		$xd0,0x20(%rsp)
+	jmp		.Loop_tail8x
+
+.align	32
+.L128_or_more8x:
+	vpxor		0x00($inp),$xa0,$xa0	# xor with input
+	vpxor		0x20($inp),$xb0,$xb0
+	vpxor		0x40($inp),$xc0,$xc0
+	vpxor		0x60($inp),$xd0,$xd0
+	vmovdqu		$xa0,0x00($out)
+	vmovdqu		$xb0,0x20($out)
+	vmovdqu		$xc0,0x40($out)
+	vmovdqu		$xd0,0x60($out)
+	je		.Ldone8x
+
+	lea		0x80($inp),$inp		# inp+=64*2
+	xor		%r10,%r10
+	vmovdqa		$xa1,0x00(%rsp)
+	lea		0x80($out),$out		# out+=64*2
+	sub		\$128,$len		# len-=64*2
+	vmovdqa		$xb1,0x20(%rsp)
+	jmp		.Loop_tail8x
+
+.align	32
+.L192_or_more8x:
+	vpxor		0x00($inp),$xa0,$xa0	# xor with input
+	vpxor		0x20($inp),$xb0,$xb0
+	vpxor		0x40($inp),$xc0,$xc0
+	vpxor		0x60($inp),$xd0,$xd0
+	vpxor		0x80($inp),$xa1,$xa1
+	vpxor		0xa0($inp),$xb1,$xb1
+	vmovdqu		$xa0,0x00($out)
+	vmovdqu		$xb0,0x20($out)
+	vmovdqu		$xc0,0x40($out)
+	vmovdqu		$xd0,0x60($out)
+	vmovdqu		$xa1,0x80($out)
+	vmovdqu		$xb1,0xa0($out)
+	je		.Ldone8x
+
+	lea		0xc0($inp),$inp		# inp+=64*3
+	xor		%r10,%r10
+	vmovdqa		$xc1,0x00(%rsp)
+	lea		0xc0($out),$out		# out+=64*3
+	sub		\$192,$len		# len-=64*3
+	vmovdqa		$xd1,0x20(%rsp)
+	jmp		.Loop_tail8x
+
+.align	32
+.L256_or_more8x:
+	vpxor		0x00($inp),$xa0,$xa0	# xor with input
+	vpxor		0x20($inp),$xb0,$xb0
+	vpxor		0x40($inp),$xc0,$xc0
+	vpxor		0x60($inp),$xd0,$xd0
+	vpxor		0x80($inp),$xa1,$xa1
+	vpxor		0xa0($inp),$xb1,$xb1
+	vpxor		0xc0($inp),$xc1,$xc1
+	vpxor		0xe0($inp),$xd1,$xd1
+	vmovdqu		$xa0,0x00($out)
+	vmovdqu		$xb0,0x20($out)
+	vmovdqu		$xc0,0x40($out)
+	vmovdqu		$xd0,0x60($out)
+	vmovdqu		$xa1,0x80($out)
+	vmovdqu		$xb1,0xa0($out)
+	vmovdqu		$xc1,0xc0($out)
+	vmovdqu		$xd1,0xe0($out)
+	je		.Ldone8x
+
+	lea		0x100($inp),$inp	# inp+=64*4
+	xor		%r10,%r10
+	vmovdqa		$xa2,0x00(%rsp)
+	lea		0x100($out),$out	# out+=64*4
+	sub		\$256,$len		# len-=64*4
+	vmovdqa		$xb2,0x20(%rsp)
+	jmp		.Loop_tail8x
+
+.align	32
+.L320_or_more8x:
+	vpxor		0x00($inp),$xa0,$xa0	# xor with input
+	vpxor		0x20($inp),$xb0,$xb0
+	vpxor		0x40($inp),$xc0,$xc0
+	vpxor		0x60($inp),$xd0,$xd0
+	vpxor		0x80($inp),$xa1,$xa1
+	vpxor		0xa0($inp),$xb1,$xb1
+	vpxor		0xc0($inp),$xc1,$xc1
+	vpxor		0xe0($inp),$xd1,$xd1
+	vpxor		0x100($inp),$xa2,$xa2
+	vpxor		0x120($inp),$xb2,$xb2
+	vmovdqu		$xa0,0x00($out)
+	vmovdqu		$xb0,0x20($out)
+	vmovdqu		$xc0,0x40($out)
+	vmovdqu		$xd0,0x60($out)
+	vmovdqu		$xa1,0x80($out)
+	vmovdqu		$xb1,0xa0($out)
+	vmovdqu		$xc1,0xc0($out)
+	vmovdqu		$xd1,0xe0($out)
+	vmovdqu		$xa2,0x100($out)
+	vmovdqu		$xb2,0x120($out)
+	je		.Ldone8x
+
+	lea		0x140($inp),$inp	# inp+=64*5
+	xor		%r10,%r10
+	vmovdqa		$xc2,0x00(%rsp)
+	lea		0x140($out),$out	# out+=64*5
+	sub		\$320,$len		# len-=64*5
+	vmovdqa		$xd2,0x20(%rsp)
+	jmp		.Loop_tail8x
+
+.align	32
+.L384_or_more8x:
+	vpxor		0x00($inp),$xa0,$xa0	# xor with input
+	vpxor		0x20($inp),$xb0,$xb0
+	vpxor		0x40($inp),$xc0,$xc0
+	vpxor		0x60($inp),$xd0,$xd0
+	vpxor		0x80($inp),$xa1,$xa1
+	vpxor		0xa0($inp),$xb1,$xb1
+	vpxor		0xc0($inp),$xc1,$xc1
+	vpxor		0xe0($inp),$xd1,$xd1
+	vpxor		0x100($inp),$xa2,$xa2
+	vpxor		0x120($inp),$xb2,$xb2
+	vpxor		0x140($inp),$xc2,$xc2
+	vpxor		0x160($inp),$xd2,$xd2
+	vmovdqu		$xa0,0x00($out)
+	vmovdqu		$xb0,0x20($out)
+	vmovdqu		$xc0,0x40($out)
+	vmovdqu		$xd0,0x60($out)
+	vmovdqu		$xa1,0x80($out)
+	vmovdqu		$xb1,0xa0($out)
+	vmovdqu		$xc1,0xc0($out)
+	vmovdqu		$xd1,0xe0($out)
+	vmovdqu		$xa2,0x100($out)
+	vmovdqu		$xb2,0x120($out)
+	vmovdqu		$xc2,0x140($out)
+	vmovdqu		$xd2,0x160($out)
+	je		.Ldone8x
+
+	lea		0x180($inp),$inp	# inp+=64*6
+	xor		%r10,%r10
+	vmovdqa		$xa3,0x00(%rsp)
+	lea		0x180($out),$out	# out+=64*6
+	sub		\$384,$len		# len-=64*6
+	vmovdqa		$xb3,0x20(%rsp)
+	jmp		.Loop_tail8x
+
+.align	32
+.L448_or_more8x:
+	vpxor		0x00($inp),$xa0,$xa0	# xor with input
+	vpxor		0x20($inp),$xb0,$xb0
+	vpxor		0x40($inp),$xc0,$xc0
+	vpxor		0x60($inp),$xd0,$xd0
+	vpxor		0x80($inp),$xa1,$xa1
+	vpxor		0xa0($inp),$xb1,$xb1
+	vpxor		0xc0($inp),$xc1,$xc1
+	vpxor		0xe0($inp),$xd1,$xd1
+	vpxor		0x100($inp),$xa2,$xa2
+	vpxor		0x120($inp),$xb2,$xb2
+	vpxor		0x140($inp),$xc2,$xc2
+	vpxor		0x160($inp),$xd2,$xd2
+	vpxor		0x180($inp),$xa3,$xa3
+	vpxor		0x1a0($inp),$xb3,$xb3
+	vmovdqu		$xa0,0x00($out)
+	vmovdqu		$xb0,0x20($out)
+	vmovdqu		$xc0,0x40($out)
+	vmovdqu		$xd0,0x60($out)
+	vmovdqu		$xa1,0x80($out)
+	vmovdqu		$xb1,0xa0($out)
+	vmovdqu		$xc1,0xc0($out)
+	vmovdqu		$xd1,0xe0($out)
+	vmovdqu		$xa2,0x100($out)
+	vmovdqu		$xb2,0x120($out)
+	vmovdqu		$xc2,0x140($out)
+	vmovdqu		$xd2,0x160($out)
+	vmovdqu		$xa3,0x180($out)
+	vmovdqu		$xb3,0x1a0($out)
+	je		.Ldone8x
+
+	lea		0x1c0($inp),$inp	# inp+=64*7
+	xor		%r10,%r10
+	vmovdqa		$xc3,0x00(%rsp)
+	lea		0x1c0($out),$out	# out+=64*7
+	sub		\$448,$len		# len-=64*7
+	vmovdqa		$xd3,0x20(%rsp)
+
+.Loop_tail8x:
+	movzb		($inp,%r10),%eax
+	movzb		(%rsp,%r10),%ecx
+	lea		1(%r10),%r10
+	xor		%ecx,%eax
+	mov		%al,-1($out,%r10)
+	dec		$len
+	jnz		.Loop_tail8x
+
+.Ldone8x:
+	vzeroall
+___
+$code.=<<___	if ($win64);
+	lea		0x290+0x30(%rsp),%r11
+	movaps		-0x30(%r11),%xmm6
+	movaps		-0x20(%r11),%xmm7
+	movaps		-0x10(%r11),%xmm8
+	movaps		0x00(%r11),%xmm9
+	movaps		0x10(%r11),%xmm10
+	movaps		0x20(%r11),%xmm11
+	movaps		0x30(%r11),%xmm12
+	movaps		0x40(%r11),%xmm13
+	movaps		0x50(%r11),%xmm14
+	movaps		0x60(%r11),%xmm15
+___
+$code.=<<___;
+	mov		0x280(%rsp),%rsp
+	ret
+.size	ChaCha20_8x,.-ChaCha20_8x
+___
+}
+
+foreach (split("\n",$code)) {
+	s/\`([^\`]*)\`/eval $1/geo;
+
+	s/%x#%y/%x/go;
+
+	print $_,"\n";
+}
+
+close STDOUT;
diff --git a/crypto/chacha/build.info b/crypto/chacha/build.info
new file mode 100644
index 000000000..f99114c13
--- /dev/null
+++ b/crypto/chacha/build.info
@@ -0,0 +1,17 @@
+LIBS=../../libcrypto
+SOURCE[../../libcrypto]={- $target{chacha_asm_src} -}
+
+GENERATE[chacha-x86.s]=asm/chacha-x86.pl $(PERLASM_SCHEME) $(CFLAGS) $(LIB_CFLAGS) $(PROCESSOR)
+GENERATE[chacha-x86_64.s]=asm/chacha-x86_64.pl $(PERLASM_SCHEME)
+GENERATE[chacha-ppc.s]=asm/chacha-ppc.pl $(PERLASM_SCHEME)
+GENERATE[chacha-armv4.S]=asm/chacha-armv4.pl $(PERLASM_SCHEME)
+INCLUDE[chacha-armv4.o]=..
+GENERATE[chacha-armv8.S]=asm/chacha-armv8.pl $(PERLASM_SCHEME)
+INCLUDE[chacha-armv8.o]=..
+
+BEGINRAW[Makefile(unix)]
+##### CHACHA assembler implementations
+
+{- $builddir -}/chacha-%.S:	{- $sourcedir -}/asm/chacha-%.pl
+	CC="$(CC)" $(PERL) $< $(PERLASM_SCHEME) $@
+ENDRAW[Makefile(unix)]
diff --git a/crypto/chacha/chacha_enc.c b/crypto/chacha/chacha_enc.c
new file mode 100644
index 000000000..239f68ab8
--- /dev/null
+++ b/crypto/chacha/chacha_enc.c
@@ -0,0 +1,121 @@
+/*
+ * Copyright 2015-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/* Adapted from the public domain code by D. Bernstein from SUPERCOP. */
+
+#include 
+
+#include "internal/chacha.h"
+
+typedef unsigned int u32;
+typedef unsigned char u8;
+typedef union {
+    u32 u[16];
+    u8 c[64];
+} chacha_buf;
+
+# define ROTATE(v, n) (((v) << (n)) | ((v) >> (32 - (n))))
+
+# define U32TO8_LITTLE(p, v) do { \
+                                (p)[0] = (u8)(v >>  0); \
+                                (p)[1] = (u8)(v >>  8); \
+                                (p)[2] = (u8)(v >> 16); \
+                                (p)[3] = (u8)(v >> 24); \
+                                } while(0)
+
+/* QUARTERROUND updates a, b, c, d with a ChaCha "quarter" round. */
+# define QUARTERROUND(a,b,c,d) ( \
+                x[a] += x[b], x[d] = ROTATE((x[d] ^ x[a]),16), \
+                x[c] += x[d], x[b] = ROTATE((x[b] ^ x[c]),12), \
+                x[a] += x[b], x[d] = ROTATE((x[d] ^ x[a]), 8), \
+                x[c] += x[d], x[b] = ROTATE((x[b] ^ x[c]), 7)  )
+
+/* chacha_core performs 20 rounds of ChaCha on the input words in
+ * |input| and writes the 64 output bytes to |output|. */
+static void chacha20_core(chacha_buf *output, const u32 input[16])
+{
+    u32 x[16];
+    int i;
+    const union {
+        long one;
+        char little;
+    } is_endian = { 1 };
+
+    memcpy(x, input, sizeof(x));
+
+    for (i = 20; i > 0; i -= 2) {
+        QUARTERROUND(0, 4, 8, 12);
+        QUARTERROUND(1, 5, 9, 13);
+        QUARTERROUND(2, 6, 10, 14);
+        QUARTERROUND(3, 7, 11, 15);
+        QUARTERROUND(0, 5, 10, 15);
+        QUARTERROUND(1, 6, 11, 12);
+        QUARTERROUND(2, 7, 8, 13);
+        QUARTERROUND(3, 4, 9, 14);
+    }
+
+    if (is_endian.little) {
+        for (i = 0; i < 16; ++i)
+            output->u[i] = x[i] + input[i];
+    } else {
+        for (i = 0; i < 16; ++i)
+            U32TO8_LITTLE(output->c + 4 * i, (x[i] + input[i]));
+    }
+}
+
+void ChaCha20_ctr32(unsigned char *out, const unsigned char *inp,
+                    size_t len, const unsigned int key[8],
+                    const unsigned int counter[4])
+{
+    u32 input[16];
+    chacha_buf buf;
+    size_t todo, i;
+
+    /* sigma constant "expand 32-byte k" in little-endian encoding */
+    input[0] = ((u32)'e') | ((u32)'x'<<8) | ((u32)'p'<<16) | ((u32)'a'<<24);
+    input[1] = ((u32)'n') | ((u32)'d'<<8) | ((u32)' '<<16) | ((u32)'3'<<24);
+    input[2] = ((u32)'2') | ((u32)'-'<<8) | ((u32)'b'<<16) | ((u32)'y'<<24);
+    input[3] = ((u32)'t') | ((u32)'e'<<8) | ((u32)' '<<16) | ((u32)'k'<<24);
+
+    input[4] = key[0];
+    input[5] = key[1];
+    input[6] = key[2];
+    input[7] = key[3];
+    input[8] = key[4];
+    input[9] = key[5];
+    input[10] = key[6];
+    input[11] = key[7];
+
+    input[12] = counter[0];
+    input[13] = counter[1];
+    input[14] = counter[2];
+    input[15] = counter[3];
+
+    while (len > 0) {
+        todo = sizeof(buf);
+        if (len < todo)
+            todo = len;
+
+        chacha20_core(&buf, input);
+
+        for (i = 0; i < todo; i++)
+            out[i] = inp[i] ^ buf.c[i];
+        out += todo;
+        inp += todo;
+        len -= todo;
+
+        /*
+         * Advance 32-bit counter. Note that as subroutine is so to
+         * say nonce-agnostic, this limited counter width doesn't
+         * prevent caller from implementing wider counter. It would
+         * simply take two calls split on counter overflow...
+         */
+        input[12]++;
+    }
+}
diff --git a/crypto/cmac/build.info b/crypto/cmac/build.info
new file mode 100644
index 000000000..c8a4949a0
--- /dev/null
+++ b/crypto/cmac/build.info
@@ -0,0 +1,2 @@
+LIBS=../../libcrypto
+SOURCE[../../libcrypto]=cmac.c cm_ameth.c cm_pmeth.c
diff --git a/crypto/cmac/cm_ameth.c b/crypto/cmac/cm_ameth.c
new file mode 100644
index 000000000..a58454a08
--- /dev/null
+++ b/crypto/cmac/cm_ameth.c
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2010-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+#include "internal/asn1_int.h"
+
+/*
+ * CMAC "ASN1" method. This is just here to indicate the maximum CMAC output
+ * length and to free up a CMAC key.
+ */
+
+static int cmac_size(const EVP_PKEY *pkey)
+{
+    return EVP_MAX_BLOCK_LENGTH;
+}
+
+static void cmac_key_free(EVP_PKEY *pkey)
+{
+    CMAC_CTX *cmctx = EVP_PKEY_get0(pkey);
+    CMAC_CTX_free(cmctx);
+}
+
+const EVP_PKEY_ASN1_METHOD cmac_asn1_meth = {
+    EVP_PKEY_CMAC,
+    EVP_PKEY_CMAC,
+    0,
+
+    "CMAC",
+    "OpenSSL CMAC method",
+
+    0, 0, 0, 0,
+
+    0, 0, 0,
+
+    cmac_size,
+    0, 0,
+    0, 0, 0, 0, 0, 0, 0,
+
+    cmac_key_free,
+    0,
+    0, 0
+};
diff --git a/crypto/cmac/cm_pmeth.c b/crypto/cmac/cm_pmeth.c
new file mode 100644
index 000000000..10748f148
--- /dev/null
+++ b/crypto/cmac/cm_pmeth.c
@@ -0,0 +1,161 @@
+/*
+ * Copyright 2010-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+#include 
+#include 
+#include "internal/evp_int.h"
+
+/* The context structure and "key" is simply a CMAC_CTX */
+
+static int pkey_cmac_init(EVP_PKEY_CTX *ctx)
+{
+    ctx->data = CMAC_CTX_new();
+    if (ctx->data == NULL)
+        return 0;
+    ctx->keygen_info_count = 0;
+    return 1;
+}
+
+static int pkey_cmac_copy(EVP_PKEY_CTX *dst, EVP_PKEY_CTX *src)
+{
+    if (!pkey_cmac_init(dst))
+        return 0;
+    if (!CMAC_CTX_copy(dst->data, src->data))
+        return 0;
+    return 1;
+}
+
+static void pkey_cmac_cleanup(EVP_PKEY_CTX *ctx)
+{
+    CMAC_CTX_free(ctx->data);
+}
+
+static int pkey_cmac_keygen(EVP_PKEY_CTX *ctx, EVP_PKEY *pkey)
+{
+    CMAC_CTX *cmkey = CMAC_CTX_new();
+    CMAC_CTX *cmctx = ctx->data;
+    if (cmkey == NULL)
+        return 0;
+    if (!CMAC_CTX_copy(cmkey, cmctx)) {
+        CMAC_CTX_free(cmkey);
+        return 0;
+    }
+    EVP_PKEY_assign(pkey, EVP_PKEY_CMAC, cmkey);
+
+    return 1;
+}
+
+static int int_update(EVP_MD_CTX *ctx, const void *data, size_t count)
+{
+    if (!CMAC_Update(EVP_MD_CTX_pkey_ctx(ctx)->data, data, count))
+        return 0;
+    return 1;
+}
+
+static int cmac_signctx_init(EVP_PKEY_CTX *ctx, EVP_MD_CTX *mctx)
+{
+    EVP_MD_CTX_set_flags(mctx, EVP_MD_CTX_FLAG_NO_INIT);
+    EVP_MD_CTX_set_update_fn(mctx, int_update);
+    return 1;
+}
+
+static int cmac_signctx(EVP_PKEY_CTX *ctx, unsigned char *sig, size_t *siglen,
+                        EVP_MD_CTX *mctx)
+{
+    return CMAC_Final(ctx->data, sig, siglen);
+}
+
+static int pkey_cmac_ctrl(EVP_PKEY_CTX *ctx, int type, int p1, void *p2)
+{
+    CMAC_CTX *cmctx = ctx->data;
+    switch (type) {
+
+    case EVP_PKEY_CTRL_SET_MAC_KEY:
+        if (!p2 || p1 < 0)
+            return 0;
+        if (!CMAC_Init(cmctx, p2, p1, NULL, NULL))
+            return 0;
+        break;
+
+    case EVP_PKEY_CTRL_CIPHER:
+        if (!CMAC_Init(cmctx, NULL, 0, p2, ctx->engine))
+            return 0;
+        break;
+
+    case EVP_PKEY_CTRL_MD:
+        if (ctx->pkey && !CMAC_CTX_copy(ctx->data,
+                                        (CMAC_CTX *)ctx->pkey->pkey.ptr))
+            return 0;
+        if (!CMAC_Init(cmctx, NULL, 0, NULL, NULL))
+            return 0;
+        break;
+
+    default:
+        return -2;
+
+    }
+    return 1;
+}
+
+static int pkey_cmac_ctrl_str(EVP_PKEY_CTX *ctx,
+                              const char *type, const char *value)
+{
+    if (!value) {
+        return 0;
+    }
+    if (strcmp(type, "cipher") == 0) {
+        const EVP_CIPHER *c;
+        c = EVP_get_cipherbyname(value);
+        if (!c)
+            return 0;
+        return pkey_cmac_ctrl(ctx, EVP_PKEY_CTRL_CIPHER, -1, (void *)c);
+    }
+    if (strcmp(type, "key") == 0)
+        return EVP_PKEY_CTX_str2ctrl(ctx, EVP_PKEY_CTRL_SET_MAC_KEY, value);
+    if (strcmp(type, "hexkey") == 0)
+        return EVP_PKEY_CTX_hex2ctrl(ctx, EVP_PKEY_CTRL_SET_MAC_KEY, value);
+    return -2;
+}
+
+const EVP_PKEY_METHOD cmac_pkey_meth = {
+    EVP_PKEY_CMAC,
+    EVP_PKEY_FLAG_SIGCTX_CUSTOM,
+    pkey_cmac_init,
+    pkey_cmac_copy,
+    pkey_cmac_cleanup,
+
+    0, 0,
+
+    0,
+    pkey_cmac_keygen,
+
+    0, 0,
+
+    0, 0,
+
+    0, 0,
+
+    cmac_signctx_init,
+    cmac_signctx,
+
+    0, 0,
+
+    0, 0,
+
+    0, 0,
+
+    0, 0,
+
+    pkey_cmac_ctrl,
+    pkey_cmac_ctrl_str
+};
diff --git a/crypto/cmac/cmac.c b/crypto/cmac/cmac.c
new file mode 100644
index 000000000..c4f13a069
--- /dev/null
+++ b/crypto/cmac/cmac.c
@@ -0,0 +1,223 @@
+/*
+ * Copyright 2010-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include 
+#include "internal/cryptlib.h"
+#include 
+
+struct CMAC_CTX_st {
+    /* Cipher context to use */
+    EVP_CIPHER_CTX *cctx;
+    /* Keys k1 and k2 */
+    unsigned char k1[EVP_MAX_BLOCK_LENGTH];
+    unsigned char k2[EVP_MAX_BLOCK_LENGTH];
+    /* Temporary block */
+    unsigned char tbl[EVP_MAX_BLOCK_LENGTH];
+    /* Last (possibly partial) block */
+    unsigned char last_block[EVP_MAX_BLOCK_LENGTH];
+    /* Number of bytes in last block: -1 means context not initialised */
+    int nlast_block;
+};
+
+/* Make temporary keys K1 and K2 */
+
+static void make_kn(unsigned char *k1, const unsigned char *l, int bl)
+{
+    int i;
+    unsigned char c = l[0], carry = c >> 7, cnext;
+
+    /* Shift block to left, including carry */
+    for (i = 0; i < bl - 1; i++, c = cnext)
+        k1[i] = (c << 1) | ((cnext = l[i + 1]) >> 7);
+
+    /* If MSB set fixup with R */
+    k1[i] = (c << 1) ^ ((0 - carry) & (bl == 16 ? 0x87 : 0x1b));
+}
+
+CMAC_CTX *CMAC_CTX_new(void)
+{
+    CMAC_CTX *ctx;
+
+    ctx = OPENSSL_malloc(sizeof(*ctx));
+    if (ctx == NULL)
+        return NULL;
+    ctx->cctx = EVP_CIPHER_CTX_new();
+    if (ctx->cctx == NULL) {
+        OPENSSL_free(ctx);
+        return NULL;
+    }
+    ctx->nlast_block = -1;
+    return ctx;
+}
+
+void CMAC_CTX_cleanup(CMAC_CTX *ctx)
+{
+    EVP_CIPHER_CTX_free(ctx->cctx);
+    OPENSSL_cleanse(ctx->tbl, EVP_MAX_BLOCK_LENGTH);
+    OPENSSL_cleanse(ctx->k1, EVP_MAX_BLOCK_LENGTH);
+    OPENSSL_cleanse(ctx->k2, EVP_MAX_BLOCK_LENGTH);
+    OPENSSL_cleanse(ctx->last_block, EVP_MAX_BLOCK_LENGTH);
+    ctx->nlast_block = -1;
+}
+
+EVP_CIPHER_CTX *CMAC_CTX_get0_cipher_ctx(CMAC_CTX *ctx)
+{
+    return ctx->cctx;
+}
+
+void CMAC_CTX_free(CMAC_CTX *ctx)
+{
+    if (!ctx)
+        return;
+    CMAC_CTX_cleanup(ctx);
+    OPENSSL_free(ctx);
+}
+
+int CMAC_CTX_copy(CMAC_CTX *out, const CMAC_CTX *in)
+{
+    int bl;
+    if (in->nlast_block == -1)
+        return 0;
+    if (!EVP_CIPHER_CTX_copy(out->cctx, in->cctx))
+        return 0;
+    bl = EVP_CIPHER_CTX_block_size(in->cctx);
+    memcpy(out->k1, in->k1, bl);
+    memcpy(out->k2, in->k2, bl);
+    memcpy(out->tbl, in->tbl, bl);
+    memcpy(out->last_block, in->last_block, bl);
+    out->nlast_block = in->nlast_block;
+    return 1;
+}
+
+int CMAC_Init(CMAC_CTX *ctx, const void *key, size_t keylen,
+              const EVP_CIPHER *cipher, ENGINE *impl)
+{
+    static const unsigned char zero_iv[EVP_MAX_BLOCK_LENGTH] = { 0 };
+    /* All zeros means restart */
+    if (!key && !cipher && !impl && keylen == 0) {
+        /* Not initialised */
+        if (ctx->nlast_block == -1)
+            return 0;
+        if (!EVP_EncryptInit_ex(ctx->cctx, NULL, NULL, NULL, zero_iv))
+            return 0;
+        memset(ctx->tbl, 0, EVP_CIPHER_CTX_block_size(ctx->cctx));
+        ctx->nlast_block = 0;
+        return 1;
+    }
+    /* Initialise context */
+    if (cipher && !EVP_EncryptInit_ex(ctx->cctx, cipher, impl, NULL, NULL))
+        return 0;
+    /* Non-NULL key means initialisation complete */
+    if (key) {
+        int bl;
+        if (!EVP_CIPHER_CTX_cipher(ctx->cctx))
+            return 0;
+        if (!EVP_CIPHER_CTX_set_key_length(ctx->cctx, keylen))
+            return 0;
+        if (!EVP_EncryptInit_ex(ctx->cctx, NULL, NULL, key, zero_iv))
+            return 0;
+        bl = EVP_CIPHER_CTX_block_size(ctx->cctx);
+        if (!EVP_Cipher(ctx->cctx, ctx->tbl, zero_iv, bl))
+            return 0;
+        make_kn(ctx->k1, ctx->tbl, bl);
+        make_kn(ctx->k2, ctx->k1, bl);
+        OPENSSL_cleanse(ctx->tbl, bl);
+        /* Reset context again ready for first data block */
+        if (!EVP_EncryptInit_ex(ctx->cctx, NULL, NULL, NULL, zero_iv))
+            return 0;
+        /* Zero tbl so resume works */
+        memset(ctx->tbl, 0, bl);
+        ctx->nlast_block = 0;
+    }
+    return 1;
+}
+
+int CMAC_Update(CMAC_CTX *ctx, const void *in, size_t dlen)
+{
+    const unsigned char *data = in;
+    size_t bl;
+    if (ctx->nlast_block == -1)
+        return 0;
+    if (dlen == 0)
+        return 1;
+    bl = EVP_CIPHER_CTX_block_size(ctx->cctx);
+    /* Copy into partial block if we need to */
+    if (ctx->nlast_block > 0) {
+        size_t nleft;
+        nleft = bl - ctx->nlast_block;
+        if (dlen < nleft)
+            nleft = dlen;
+        memcpy(ctx->last_block + ctx->nlast_block, data, nleft);
+        dlen -= nleft;
+        ctx->nlast_block += nleft;
+        /* If no more to process return */
+        if (dlen == 0)
+            return 1;
+        data += nleft;
+        /* Else not final block so encrypt it */
+        if (!EVP_Cipher(ctx->cctx, ctx->tbl, ctx->last_block, bl))
+            return 0;
+    }
+    /* Encrypt all but one of the complete blocks left */
+    while (dlen > bl) {
+        if (!EVP_Cipher(ctx->cctx, ctx->tbl, data, bl))
+            return 0;
+        dlen -= bl;
+        data += bl;
+    }
+    /* Copy any data left to last block buffer */
+    memcpy(ctx->last_block, data, dlen);
+    ctx->nlast_block = dlen;
+    return 1;
+
+}
+
+int CMAC_Final(CMAC_CTX *ctx, unsigned char *out, size_t *poutlen)
+{
+    int i, bl, lb;
+    if (ctx->nlast_block == -1)
+        return 0;
+    bl = EVP_CIPHER_CTX_block_size(ctx->cctx);
+    *poutlen = (size_t)bl;
+    if (!out)
+        return 1;
+    lb = ctx->nlast_block;
+    /* Is last block complete? */
+    if (lb == bl) {
+        for (i = 0; i < bl; i++)
+            out[i] = ctx->last_block[i] ^ ctx->k1[i];
+    } else {
+        ctx->last_block[lb] = 0x80;
+        if (bl - lb > 1)
+            memset(ctx->last_block + lb + 1, 0, bl - lb - 1);
+        for (i = 0; i < bl; i++)
+            out[i] = ctx->last_block[i] ^ ctx->k2[i];
+    }
+    if (!EVP_Cipher(ctx->cctx, out, out, bl)) {
+        OPENSSL_cleanse(out, bl);
+        return 0;
+    }
+    return 1;
+}
+
+int CMAC_resume(CMAC_CTX *ctx)
+{
+    if (ctx->nlast_block == -1)
+        return 0;
+    /*
+     * The buffer "tbl" contains the last fully encrypted block which is the
+     * last IV (or all zeroes if no last encrypted block). The last block has
+     * not been modified since CMAC_final(). So reinitialising using the last
+     * decrypted block will allow CMAC to continue after calling
+     * CMAC_Final().
+     */
+    return EVP_EncryptInit_ex(ctx->cctx, NULL, NULL, NULL, ctx->tbl);
+}
diff --git a/crypto/cms/build.info b/crypto/cms/build.info
new file mode 100644
index 000000000..cb675436e
--- /dev/null
+++ b/crypto/cms/build.info
@@ -0,0 +1,5 @@
+LIBS=../../libcrypto
+SOURCE[../../libcrypto]= \
+        cms_lib.c cms_asn1.c cms_att.c cms_io.c cms_smime.c cms_err.c \
+        cms_sd.c cms_dd.c cms_cd.c cms_env.c cms_enc.c cms_ess.c \
+        cms_pwri.c cms_kari.c
diff --git a/crypto/cms/cms_asn1.c b/crypto/cms/cms_asn1.c
new file mode 100644
index 000000000..81e9a5306
--- /dev/null
+++ b/crypto/cms/cms_asn1.c
@@ -0,0 +1,402 @@
+/*
+ * Copyright 2008-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include "cms_lcl.h"
+
+
+ASN1_SEQUENCE(CMS_IssuerAndSerialNumber) = {
+        ASN1_SIMPLE(CMS_IssuerAndSerialNumber, issuer, X509_NAME),
+        ASN1_SIMPLE(CMS_IssuerAndSerialNumber, serialNumber, ASN1_INTEGER)
+} ASN1_SEQUENCE_END(CMS_IssuerAndSerialNumber)
+
+ASN1_SEQUENCE(CMS_OtherCertificateFormat) = {
+        ASN1_SIMPLE(CMS_OtherCertificateFormat, otherCertFormat, ASN1_OBJECT),
+        ASN1_OPT(CMS_OtherCertificateFormat, otherCert, ASN1_ANY)
+} static_ASN1_SEQUENCE_END(CMS_OtherCertificateFormat)
+
+ASN1_CHOICE(CMS_CertificateChoices) = {
+        ASN1_SIMPLE(CMS_CertificateChoices, d.certificate, X509),
+        ASN1_IMP(CMS_CertificateChoices, d.extendedCertificate, ASN1_SEQUENCE, 0),
+        ASN1_IMP(CMS_CertificateChoices, d.v1AttrCert, ASN1_SEQUENCE, 1),
+        ASN1_IMP(CMS_CertificateChoices, d.v2AttrCert, ASN1_SEQUENCE, 2),
+        ASN1_IMP(CMS_CertificateChoices, d.other, CMS_OtherCertificateFormat, 3)
+} ASN1_CHOICE_END(CMS_CertificateChoices)
+
+ASN1_CHOICE(CMS_SignerIdentifier) = {
+        ASN1_SIMPLE(CMS_SignerIdentifier, d.issuerAndSerialNumber, CMS_IssuerAndSerialNumber),
+        ASN1_IMP(CMS_SignerIdentifier, d.subjectKeyIdentifier, ASN1_OCTET_STRING, 0)
+} static_ASN1_CHOICE_END(CMS_SignerIdentifier)
+
+ASN1_NDEF_SEQUENCE(CMS_EncapsulatedContentInfo) = {
+        ASN1_SIMPLE(CMS_EncapsulatedContentInfo, eContentType, ASN1_OBJECT),
+        ASN1_NDEF_EXP_OPT(CMS_EncapsulatedContentInfo, eContent, ASN1_OCTET_STRING_NDEF, 0)
+} static_ASN1_NDEF_SEQUENCE_END(CMS_EncapsulatedContentInfo)
+
+/* Minor tweak to operation: free up signer key, cert */
+static int cms_si_cb(int operation, ASN1_VALUE **pval, const ASN1_ITEM *it,
+                     void *exarg)
+{
+    if (operation == ASN1_OP_FREE_POST) {
+        CMS_SignerInfo *si = (CMS_SignerInfo *)*pval;
+        EVP_PKEY_free(si->pkey);
+        X509_free(si->signer);
+        EVP_MD_CTX_free(si->mctx);
+    }
+    return 1;
+}
+
+ASN1_SEQUENCE_cb(CMS_SignerInfo, cms_si_cb) = {
+        ASN1_SIMPLE(CMS_SignerInfo, version, LONG),
+        ASN1_SIMPLE(CMS_SignerInfo, sid, CMS_SignerIdentifier),
+        ASN1_SIMPLE(CMS_SignerInfo, digestAlgorithm, X509_ALGOR),
+        ASN1_IMP_SET_OF_OPT(CMS_SignerInfo, signedAttrs, X509_ATTRIBUTE, 0),
+        ASN1_SIMPLE(CMS_SignerInfo, signatureAlgorithm, X509_ALGOR),
+        ASN1_SIMPLE(CMS_SignerInfo, signature, ASN1_OCTET_STRING),
+        ASN1_IMP_SET_OF_OPT(CMS_SignerInfo, unsignedAttrs, X509_ATTRIBUTE, 1)
+} ASN1_SEQUENCE_END_cb(CMS_SignerInfo, CMS_SignerInfo)
+
+ASN1_SEQUENCE(CMS_OtherRevocationInfoFormat) = {
+        ASN1_SIMPLE(CMS_OtherRevocationInfoFormat, otherRevInfoFormat, ASN1_OBJECT),
+        ASN1_OPT(CMS_OtherRevocationInfoFormat, otherRevInfo, ASN1_ANY)
+} static_ASN1_SEQUENCE_END(CMS_OtherRevocationInfoFormat)
+
+ASN1_CHOICE(CMS_RevocationInfoChoice) = {
+        ASN1_SIMPLE(CMS_RevocationInfoChoice, d.crl, X509_CRL),
+        ASN1_IMP(CMS_RevocationInfoChoice, d.other, CMS_OtherRevocationInfoFormat, 1)
+} ASN1_CHOICE_END(CMS_RevocationInfoChoice)
+
+ASN1_NDEF_SEQUENCE(CMS_SignedData) = {
+        ASN1_SIMPLE(CMS_SignedData, version, LONG),
+        ASN1_SET_OF(CMS_SignedData, digestAlgorithms, X509_ALGOR),
+        ASN1_SIMPLE(CMS_SignedData, encapContentInfo, CMS_EncapsulatedContentInfo),
+        ASN1_IMP_SET_OF_OPT(CMS_SignedData, certificates, CMS_CertificateChoices, 0),
+        ASN1_IMP_SET_OF_OPT(CMS_SignedData, crls, CMS_RevocationInfoChoice, 1),
+        ASN1_SET_OF(CMS_SignedData, signerInfos, CMS_SignerInfo)
+} ASN1_NDEF_SEQUENCE_END(CMS_SignedData)
+
+ASN1_SEQUENCE(CMS_OriginatorInfo) = {
+        ASN1_IMP_SET_OF_OPT(CMS_OriginatorInfo, certificates, CMS_CertificateChoices, 0),
+        ASN1_IMP_SET_OF_OPT(CMS_OriginatorInfo, crls, CMS_RevocationInfoChoice, 1)
+} static_ASN1_SEQUENCE_END(CMS_OriginatorInfo)
+
+ASN1_NDEF_SEQUENCE(CMS_EncryptedContentInfo) = {
+        ASN1_SIMPLE(CMS_EncryptedContentInfo, contentType, ASN1_OBJECT),
+        ASN1_SIMPLE(CMS_EncryptedContentInfo, contentEncryptionAlgorithm, X509_ALGOR),
+        ASN1_IMP_OPT(CMS_EncryptedContentInfo, encryptedContent, ASN1_OCTET_STRING_NDEF, 0)
+} static_ASN1_NDEF_SEQUENCE_END(CMS_EncryptedContentInfo)
+
+ASN1_SEQUENCE(CMS_KeyTransRecipientInfo) = {
+        ASN1_SIMPLE(CMS_KeyTransRecipientInfo, version, LONG),
+        ASN1_SIMPLE(CMS_KeyTransRecipientInfo, rid, CMS_SignerIdentifier),
+        ASN1_SIMPLE(CMS_KeyTransRecipientInfo, keyEncryptionAlgorithm, X509_ALGOR),
+        ASN1_SIMPLE(CMS_KeyTransRecipientInfo, encryptedKey, ASN1_OCTET_STRING)
+} ASN1_SEQUENCE_END(CMS_KeyTransRecipientInfo)
+
+ASN1_SEQUENCE(CMS_OtherKeyAttribute) = {
+        ASN1_SIMPLE(CMS_OtherKeyAttribute, keyAttrId, ASN1_OBJECT),
+        ASN1_OPT(CMS_OtherKeyAttribute, keyAttr, ASN1_ANY)
+} ASN1_SEQUENCE_END(CMS_OtherKeyAttribute)
+
+ASN1_SEQUENCE(CMS_RecipientKeyIdentifier) = {
+        ASN1_SIMPLE(CMS_RecipientKeyIdentifier, subjectKeyIdentifier, ASN1_OCTET_STRING),
+        ASN1_OPT(CMS_RecipientKeyIdentifier, date, ASN1_GENERALIZEDTIME),
+        ASN1_OPT(CMS_RecipientKeyIdentifier, other, CMS_OtherKeyAttribute)
+} ASN1_SEQUENCE_END(CMS_RecipientKeyIdentifier)
+
+ASN1_CHOICE(CMS_KeyAgreeRecipientIdentifier) = {
+  ASN1_SIMPLE(CMS_KeyAgreeRecipientIdentifier, d.issuerAndSerialNumber, CMS_IssuerAndSerialNumber),
+  ASN1_IMP(CMS_KeyAgreeRecipientIdentifier, d.rKeyId, CMS_RecipientKeyIdentifier, 0)
+} static_ASN1_CHOICE_END(CMS_KeyAgreeRecipientIdentifier)
+
+static int cms_rek_cb(int operation, ASN1_VALUE **pval, const ASN1_ITEM *it,
+                      void *exarg)
+{
+    CMS_RecipientEncryptedKey *rek = (CMS_RecipientEncryptedKey *)*pval;
+    if (operation == ASN1_OP_FREE_POST) {
+        EVP_PKEY_free(rek->pkey);
+    }
+    return 1;
+}
+
+ASN1_SEQUENCE_cb(CMS_RecipientEncryptedKey, cms_rek_cb) = {
+        ASN1_SIMPLE(CMS_RecipientEncryptedKey, rid, CMS_KeyAgreeRecipientIdentifier),
+        ASN1_SIMPLE(CMS_RecipientEncryptedKey, encryptedKey, ASN1_OCTET_STRING)
+} ASN1_SEQUENCE_END_cb(CMS_RecipientEncryptedKey, CMS_RecipientEncryptedKey)
+
+ASN1_SEQUENCE(CMS_OriginatorPublicKey) = {
+  ASN1_SIMPLE(CMS_OriginatorPublicKey, algorithm, X509_ALGOR),
+  ASN1_SIMPLE(CMS_OriginatorPublicKey, publicKey, ASN1_BIT_STRING)
+} ASN1_SEQUENCE_END(CMS_OriginatorPublicKey)
+
+ASN1_CHOICE(CMS_OriginatorIdentifierOrKey) = {
+  ASN1_SIMPLE(CMS_OriginatorIdentifierOrKey, d.issuerAndSerialNumber, CMS_IssuerAndSerialNumber),
+  ASN1_IMP(CMS_OriginatorIdentifierOrKey, d.subjectKeyIdentifier, ASN1_OCTET_STRING, 0),
+  ASN1_IMP(CMS_OriginatorIdentifierOrKey, d.originatorKey, CMS_OriginatorPublicKey, 1)
+} static_ASN1_CHOICE_END(CMS_OriginatorIdentifierOrKey)
+
+static int cms_kari_cb(int operation, ASN1_VALUE **pval, const ASN1_ITEM *it,
+                       void *exarg)
+{
+    CMS_KeyAgreeRecipientInfo *kari = (CMS_KeyAgreeRecipientInfo *)*pval;
+    if (operation == ASN1_OP_NEW_POST) {
+        kari->ctx = EVP_CIPHER_CTX_new();
+        if (kari->ctx == NULL)
+            return 0;
+        EVP_CIPHER_CTX_set_flags(kari->ctx, EVP_CIPHER_CTX_FLAG_WRAP_ALLOW);
+        kari->pctx = NULL;
+    } else if (operation == ASN1_OP_FREE_POST) {
+        EVP_PKEY_CTX_free(kari->pctx);
+        EVP_CIPHER_CTX_free(kari->ctx);
+    }
+    return 1;
+}
+
+ASN1_SEQUENCE_cb(CMS_KeyAgreeRecipientInfo, cms_kari_cb) = {
+        ASN1_SIMPLE(CMS_KeyAgreeRecipientInfo, version, LONG),
+        ASN1_EXP(CMS_KeyAgreeRecipientInfo, originator, CMS_OriginatorIdentifierOrKey, 0),
+        ASN1_EXP_OPT(CMS_KeyAgreeRecipientInfo, ukm, ASN1_OCTET_STRING, 1),
+        ASN1_SIMPLE(CMS_KeyAgreeRecipientInfo, keyEncryptionAlgorithm, X509_ALGOR),
+        ASN1_SEQUENCE_OF(CMS_KeyAgreeRecipientInfo, recipientEncryptedKeys, CMS_RecipientEncryptedKey)
+} ASN1_SEQUENCE_END_cb(CMS_KeyAgreeRecipientInfo, CMS_KeyAgreeRecipientInfo)
+
+ASN1_SEQUENCE(CMS_KEKIdentifier) = {
+        ASN1_SIMPLE(CMS_KEKIdentifier, keyIdentifier, ASN1_OCTET_STRING),
+        ASN1_OPT(CMS_KEKIdentifier, date, ASN1_GENERALIZEDTIME),
+        ASN1_OPT(CMS_KEKIdentifier, other, CMS_OtherKeyAttribute)
+} static_ASN1_SEQUENCE_END(CMS_KEKIdentifier)
+
+ASN1_SEQUENCE(CMS_KEKRecipientInfo) = {
+        ASN1_SIMPLE(CMS_KEKRecipientInfo, version, LONG),
+        ASN1_SIMPLE(CMS_KEKRecipientInfo, kekid, CMS_KEKIdentifier),
+        ASN1_SIMPLE(CMS_KEKRecipientInfo, keyEncryptionAlgorithm, X509_ALGOR),
+        ASN1_SIMPLE(CMS_KEKRecipientInfo, encryptedKey, ASN1_OCTET_STRING)
+} ASN1_SEQUENCE_END(CMS_KEKRecipientInfo)
+
+ASN1_SEQUENCE(CMS_PasswordRecipientInfo) = {
+        ASN1_SIMPLE(CMS_PasswordRecipientInfo, version, LONG),
+        ASN1_IMP_OPT(CMS_PasswordRecipientInfo, keyDerivationAlgorithm, X509_ALGOR, 0),
+        ASN1_SIMPLE(CMS_PasswordRecipientInfo, keyEncryptionAlgorithm, X509_ALGOR),
+        ASN1_SIMPLE(CMS_PasswordRecipientInfo, encryptedKey, ASN1_OCTET_STRING)
+} ASN1_SEQUENCE_END(CMS_PasswordRecipientInfo)
+
+ASN1_SEQUENCE(CMS_OtherRecipientInfo) = {
+  ASN1_SIMPLE(CMS_OtherRecipientInfo, oriType, ASN1_OBJECT),
+  ASN1_OPT(CMS_OtherRecipientInfo, oriValue, ASN1_ANY)
+} static_ASN1_SEQUENCE_END(CMS_OtherRecipientInfo)
+
+/* Free up RecipientInfo additional data */
+static int cms_ri_cb(int operation, ASN1_VALUE **pval, const ASN1_ITEM *it,
+                     void *exarg)
+{
+    if (operation == ASN1_OP_FREE_PRE) {
+        CMS_RecipientInfo *ri = (CMS_RecipientInfo *)*pval;
+        if (ri->type == CMS_RECIPINFO_TRANS) {
+            CMS_KeyTransRecipientInfo *ktri = ri->d.ktri;
+            EVP_PKEY_free(ktri->pkey);
+            X509_free(ktri->recip);
+            EVP_PKEY_CTX_free(ktri->pctx);
+        } else if (ri->type == CMS_RECIPINFO_KEK) {
+            CMS_KEKRecipientInfo *kekri = ri->d.kekri;
+            OPENSSL_clear_free(kekri->key, kekri->keylen);
+        } else if (ri->type == CMS_RECIPINFO_PASS) {
+            CMS_PasswordRecipientInfo *pwri = ri->d.pwri;
+            OPENSSL_clear_free(pwri->pass, pwri->passlen);
+        }
+    }
+    return 1;
+}
+
+ASN1_CHOICE_cb(CMS_RecipientInfo, cms_ri_cb) = {
+        ASN1_SIMPLE(CMS_RecipientInfo, d.ktri, CMS_KeyTransRecipientInfo),
+        ASN1_IMP(CMS_RecipientInfo, d.kari, CMS_KeyAgreeRecipientInfo, 1),
+        ASN1_IMP(CMS_RecipientInfo, d.kekri, CMS_KEKRecipientInfo, 2),
+        ASN1_IMP(CMS_RecipientInfo, d.pwri, CMS_PasswordRecipientInfo, 3),
+        ASN1_IMP(CMS_RecipientInfo, d.ori, CMS_OtherRecipientInfo, 4)
+} ASN1_CHOICE_END_cb(CMS_RecipientInfo, CMS_RecipientInfo, type)
+
+ASN1_NDEF_SEQUENCE(CMS_EnvelopedData) = {
+        ASN1_SIMPLE(CMS_EnvelopedData, version, LONG),
+        ASN1_IMP_OPT(CMS_EnvelopedData, originatorInfo, CMS_OriginatorInfo, 0),
+        ASN1_SET_OF(CMS_EnvelopedData, recipientInfos, CMS_RecipientInfo),
+        ASN1_SIMPLE(CMS_EnvelopedData, encryptedContentInfo, CMS_EncryptedContentInfo),
+        ASN1_IMP_SET_OF_OPT(CMS_EnvelopedData, unprotectedAttrs, X509_ATTRIBUTE, 1)
+} ASN1_NDEF_SEQUENCE_END(CMS_EnvelopedData)
+
+ASN1_NDEF_SEQUENCE(CMS_DigestedData) = {
+        ASN1_SIMPLE(CMS_DigestedData, version, LONG),
+        ASN1_SIMPLE(CMS_DigestedData, digestAlgorithm, X509_ALGOR),
+        ASN1_SIMPLE(CMS_DigestedData, encapContentInfo, CMS_EncapsulatedContentInfo),
+        ASN1_SIMPLE(CMS_DigestedData, digest, ASN1_OCTET_STRING)
+} ASN1_NDEF_SEQUENCE_END(CMS_DigestedData)
+
+ASN1_NDEF_SEQUENCE(CMS_EncryptedData) = {
+        ASN1_SIMPLE(CMS_EncryptedData, version, LONG),
+        ASN1_SIMPLE(CMS_EncryptedData, encryptedContentInfo, CMS_EncryptedContentInfo),
+        ASN1_IMP_SET_OF_OPT(CMS_EncryptedData, unprotectedAttrs, X509_ATTRIBUTE, 1)
+} ASN1_NDEF_SEQUENCE_END(CMS_EncryptedData)
+
+ASN1_NDEF_SEQUENCE(CMS_AuthenticatedData) = {
+        ASN1_SIMPLE(CMS_AuthenticatedData, version, LONG),
+        ASN1_IMP_OPT(CMS_AuthenticatedData, originatorInfo, CMS_OriginatorInfo, 0),
+        ASN1_SET_OF(CMS_AuthenticatedData, recipientInfos, CMS_RecipientInfo),
+        ASN1_SIMPLE(CMS_AuthenticatedData, macAlgorithm, X509_ALGOR),
+        ASN1_IMP(CMS_AuthenticatedData, digestAlgorithm, X509_ALGOR, 1),
+        ASN1_SIMPLE(CMS_AuthenticatedData, encapContentInfo, CMS_EncapsulatedContentInfo),
+        ASN1_IMP_SET_OF_OPT(CMS_AuthenticatedData, authAttrs, X509_ALGOR, 2),
+        ASN1_SIMPLE(CMS_AuthenticatedData, mac, ASN1_OCTET_STRING),
+        ASN1_IMP_SET_OF_OPT(CMS_AuthenticatedData, unauthAttrs, X509_ALGOR, 3)
+} static_ASN1_NDEF_SEQUENCE_END(CMS_AuthenticatedData)
+
+ASN1_NDEF_SEQUENCE(CMS_CompressedData) = {
+        ASN1_SIMPLE(CMS_CompressedData, version, LONG),
+        ASN1_SIMPLE(CMS_CompressedData, compressionAlgorithm, X509_ALGOR),
+        ASN1_SIMPLE(CMS_CompressedData, encapContentInfo, CMS_EncapsulatedContentInfo),
+} ASN1_NDEF_SEQUENCE_END(CMS_CompressedData)
+
+/* This is the ANY DEFINED BY table for the top level ContentInfo structure */
+
+ASN1_ADB_TEMPLATE(cms_default) = ASN1_EXP(CMS_ContentInfo, d.other, ASN1_ANY, 0);
+
+ASN1_ADB(CMS_ContentInfo) = {
+        ADB_ENTRY(NID_pkcs7_data, ASN1_NDEF_EXP(CMS_ContentInfo, d.data, ASN1_OCTET_STRING_NDEF, 0)),
+        ADB_ENTRY(NID_pkcs7_signed, ASN1_NDEF_EXP(CMS_ContentInfo, d.signedData, CMS_SignedData, 0)),
+        ADB_ENTRY(NID_pkcs7_enveloped, ASN1_NDEF_EXP(CMS_ContentInfo, d.envelopedData, CMS_EnvelopedData, 0)),
+        ADB_ENTRY(NID_pkcs7_digest, ASN1_NDEF_EXP(CMS_ContentInfo, d.digestedData, CMS_DigestedData, 0)),
+        ADB_ENTRY(NID_pkcs7_encrypted, ASN1_NDEF_EXP(CMS_ContentInfo, d.encryptedData, CMS_EncryptedData, 0)),
+        ADB_ENTRY(NID_id_smime_ct_authData, ASN1_NDEF_EXP(CMS_ContentInfo, d.authenticatedData, CMS_AuthenticatedData, 0)),
+        ADB_ENTRY(NID_id_smime_ct_compressedData, ASN1_NDEF_EXP(CMS_ContentInfo, d.compressedData, CMS_CompressedData, 0)),
+} ASN1_ADB_END(CMS_ContentInfo, 0, contentType, 0, &cms_default_tt, NULL);
+
+/* CMS streaming support */
+static int cms_cb(int operation, ASN1_VALUE **pval, const ASN1_ITEM *it,
+                  void *exarg)
+{
+    ASN1_STREAM_ARG *sarg = exarg;
+    CMS_ContentInfo *cms = NULL;
+    if (pval)
+        cms = (CMS_ContentInfo *)*pval;
+    else
+        return 1;
+    switch (operation) {
+
+    case ASN1_OP_STREAM_PRE:
+        if (CMS_stream(&sarg->boundary, cms) <= 0)
+            return 0;
+    case ASN1_OP_DETACHED_PRE:
+        sarg->ndef_bio = CMS_dataInit(cms, sarg->out);
+        if (!sarg->ndef_bio)
+            return 0;
+        break;
+
+    case ASN1_OP_STREAM_POST:
+    case ASN1_OP_DETACHED_POST:
+        if (CMS_dataFinal(cms, sarg->ndef_bio) <= 0)
+            return 0;
+        break;
+
+    }
+    return 1;
+}
+
+ASN1_NDEF_SEQUENCE_cb(CMS_ContentInfo, cms_cb) = {
+        ASN1_SIMPLE(CMS_ContentInfo, contentType, ASN1_OBJECT),
+        ASN1_ADB_OBJECT(CMS_ContentInfo)
+} ASN1_NDEF_SEQUENCE_END_cb(CMS_ContentInfo, CMS_ContentInfo)
+
+/* Specials for signed attributes */
+
+/*
+ * When signing attributes we want to reorder them to match the sorted
+ * encoding.
+ */
+
+ASN1_ITEM_TEMPLATE(CMS_Attributes_Sign) =
+        ASN1_EX_TEMPLATE_TYPE(ASN1_TFLG_SET_ORDER, 0, CMS_ATTRIBUTES, X509_ATTRIBUTE)
+ASN1_ITEM_TEMPLATE_END(CMS_Attributes_Sign)
+
+/*
+ * When verifying attributes we need to use the received order. So we use
+ * SEQUENCE OF and tag it to SET OF
+ */
+
+ASN1_ITEM_TEMPLATE(CMS_Attributes_Verify) =
+        ASN1_EX_TEMPLATE_TYPE(ASN1_TFLG_SEQUENCE_OF | ASN1_TFLG_IMPTAG | ASN1_TFLG_UNIVERSAL,
+                                V_ASN1_SET, CMS_ATTRIBUTES, X509_ATTRIBUTE)
+ASN1_ITEM_TEMPLATE_END(CMS_Attributes_Verify)
+
+
+
+ASN1_CHOICE(CMS_ReceiptsFrom) = {
+  ASN1_IMP(CMS_ReceiptsFrom, d.allOrFirstTier, LONG, 0),
+  ASN1_IMP_SEQUENCE_OF(CMS_ReceiptsFrom, d.receiptList, GENERAL_NAMES, 1)
+} static_ASN1_CHOICE_END(CMS_ReceiptsFrom)
+
+ASN1_SEQUENCE(CMS_ReceiptRequest) = {
+  ASN1_SIMPLE(CMS_ReceiptRequest, signedContentIdentifier, ASN1_OCTET_STRING),
+  ASN1_SIMPLE(CMS_ReceiptRequest, receiptsFrom, CMS_ReceiptsFrom),
+  ASN1_SEQUENCE_OF(CMS_ReceiptRequest, receiptsTo, GENERAL_NAMES)
+} ASN1_SEQUENCE_END(CMS_ReceiptRequest)
+
+ASN1_SEQUENCE(CMS_Receipt) = {
+  ASN1_SIMPLE(CMS_Receipt, version, LONG),
+  ASN1_SIMPLE(CMS_Receipt, contentType, ASN1_OBJECT),
+  ASN1_SIMPLE(CMS_Receipt, signedContentIdentifier, ASN1_OCTET_STRING),
+  ASN1_SIMPLE(CMS_Receipt, originatorSignatureValue, ASN1_OCTET_STRING)
+} ASN1_SEQUENCE_END(CMS_Receipt)
+
+/*
+ * Utilities to encode the CMS_SharedInfo structure used during key
+ * derivation.
+ */
+
+typedef struct {
+    X509_ALGOR *keyInfo;
+    ASN1_OCTET_STRING *entityUInfo;
+    ASN1_OCTET_STRING *suppPubInfo;
+} CMS_SharedInfo;
+
+ASN1_SEQUENCE(CMS_SharedInfo) = {
+  ASN1_SIMPLE(CMS_SharedInfo, keyInfo, X509_ALGOR),
+  ASN1_EXP_OPT(CMS_SharedInfo, entityUInfo, ASN1_OCTET_STRING, 0),
+  ASN1_EXP_OPT(CMS_SharedInfo, suppPubInfo, ASN1_OCTET_STRING, 2),
+} static_ASN1_SEQUENCE_END(CMS_SharedInfo)
+
+int CMS_SharedInfo_encode(unsigned char **pder, X509_ALGOR *kekalg,
+                          ASN1_OCTET_STRING *ukm, int keylen)
+{
+    union {
+        CMS_SharedInfo *pecsi;
+        ASN1_VALUE *a;
+    } intsi = {
+        NULL
+    };
+
+    ASN1_OCTET_STRING oklen;
+    unsigned char kl[4];
+    CMS_SharedInfo ecsi;
+
+    keylen <<= 3;
+    kl[0] = (keylen >> 24) & 0xff;
+    kl[1] = (keylen >> 16) & 0xff;
+    kl[2] = (keylen >> 8) & 0xff;
+    kl[3] = keylen & 0xff;
+    oklen.length = 4;
+    oklen.data = kl;
+    oklen.type = V_ASN1_OCTET_STRING;
+    oklen.flags = 0;
+    ecsi.keyInfo = kekalg;
+    ecsi.entityUInfo = ukm;
+    ecsi.suppPubInfo = &oklen;
+    intsi.pecsi = &ecsi;
+    return ASN1_item_i2d(intsi.a, pder, ASN1_ITEM_rptr(CMS_SharedInfo));
+}
diff --git a/crypto/cms/cms_att.c b/crypto/cms/cms_att.c
new file mode 100644
index 000000000..664e64971
--- /dev/null
+++ b/crypto/cms/cms_att.c
@@ -0,0 +1,152 @@
+/*
+ * Copyright 2008-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include "cms_lcl.h"
+
+/* CMS SignedData Attribute utilities */
+
+int CMS_signed_get_attr_count(const CMS_SignerInfo *si)
+{
+    return X509at_get_attr_count(si->signedAttrs);
+}
+
+int CMS_signed_get_attr_by_NID(const CMS_SignerInfo *si, int nid, int lastpos)
+{
+    return X509at_get_attr_by_NID(si->signedAttrs, nid, lastpos);
+}
+
+int CMS_signed_get_attr_by_OBJ(const CMS_SignerInfo *si, const ASN1_OBJECT *obj,
+                               int lastpos)
+{
+    return X509at_get_attr_by_OBJ(si->signedAttrs, obj, lastpos);
+}
+
+X509_ATTRIBUTE *CMS_signed_get_attr(const CMS_SignerInfo *si, int loc)
+{
+    return X509at_get_attr(si->signedAttrs, loc);
+}
+
+X509_ATTRIBUTE *CMS_signed_delete_attr(CMS_SignerInfo *si, int loc)
+{
+    return X509at_delete_attr(si->signedAttrs, loc);
+}
+
+int CMS_signed_add1_attr(CMS_SignerInfo *si, X509_ATTRIBUTE *attr)
+{
+    if (X509at_add1_attr(&si->signedAttrs, attr))
+        return 1;
+    return 0;
+}
+
+int CMS_signed_add1_attr_by_OBJ(CMS_SignerInfo *si,
+                                const ASN1_OBJECT *obj, int type,
+                                const void *bytes, int len)
+{
+    if (X509at_add1_attr_by_OBJ(&si->signedAttrs, obj, type, bytes, len))
+        return 1;
+    return 0;
+}
+
+int CMS_signed_add1_attr_by_NID(CMS_SignerInfo *si,
+                                int nid, int type, const void *bytes, int len)
+{
+    if (X509at_add1_attr_by_NID(&si->signedAttrs, nid, type, bytes, len))
+        return 1;
+    return 0;
+}
+
+int CMS_signed_add1_attr_by_txt(CMS_SignerInfo *si,
+                                const char *attrname, int type,
+                                const void *bytes, int len)
+{
+    if (X509at_add1_attr_by_txt(&si->signedAttrs, attrname, type, bytes, len))
+        return 1;
+    return 0;
+}
+
+void *CMS_signed_get0_data_by_OBJ(CMS_SignerInfo *si, const ASN1_OBJECT *oid,
+                                  int lastpos, int type)
+{
+    return X509at_get0_data_by_OBJ(si->signedAttrs, oid, lastpos, type);
+}
+
+int CMS_unsigned_get_attr_count(const CMS_SignerInfo *si)
+{
+    return X509at_get_attr_count(si->unsignedAttrs);
+}
+
+int CMS_unsigned_get_attr_by_NID(const CMS_SignerInfo *si, int nid,
+                                 int lastpos)
+{
+    return X509at_get_attr_by_NID(si->unsignedAttrs, nid, lastpos);
+}
+
+int CMS_unsigned_get_attr_by_OBJ(const CMS_SignerInfo *si,
+                                 const ASN1_OBJECT *obj, int lastpos)
+{
+    return X509at_get_attr_by_OBJ(si->unsignedAttrs, obj, lastpos);
+}
+
+X509_ATTRIBUTE *CMS_unsigned_get_attr(const CMS_SignerInfo *si, int loc)
+{
+    return X509at_get_attr(si->unsignedAttrs, loc);
+}
+
+X509_ATTRIBUTE *CMS_unsigned_delete_attr(CMS_SignerInfo *si, int loc)
+{
+    return X509at_delete_attr(si->unsignedAttrs, loc);
+}
+
+int CMS_unsigned_add1_attr(CMS_SignerInfo *si, X509_ATTRIBUTE *attr)
+{
+    if (X509at_add1_attr(&si->unsignedAttrs, attr))
+        return 1;
+    return 0;
+}
+
+int CMS_unsigned_add1_attr_by_OBJ(CMS_SignerInfo *si,
+                                  const ASN1_OBJECT *obj, int type,
+                                  const void *bytes, int len)
+{
+    if (X509at_add1_attr_by_OBJ(&si->unsignedAttrs, obj, type, bytes, len))
+        return 1;
+    return 0;
+}
+
+int CMS_unsigned_add1_attr_by_NID(CMS_SignerInfo *si,
+                                  int nid, int type,
+                                  const void *bytes, int len)
+{
+    if (X509at_add1_attr_by_NID(&si->unsignedAttrs, nid, type, bytes, len))
+        return 1;
+    return 0;
+}
+
+int CMS_unsigned_add1_attr_by_txt(CMS_SignerInfo *si,
+                                  const char *attrname, int type,
+                                  const void *bytes, int len)
+{
+    if (X509at_add1_attr_by_txt(&si->unsignedAttrs, attrname,
+                                type, bytes, len))
+        return 1;
+    return 0;
+}
+
+void *CMS_unsigned_get0_data_by_OBJ(CMS_SignerInfo *si, ASN1_OBJECT *oid,
+                                    int lastpos, int type)
+{
+    return X509at_get0_data_by_OBJ(si->unsignedAttrs, oid, lastpos, type);
+}
+
+/* Specific attribute cases */
diff --git a/crypto/cms/cms_cd.c b/crypto/cms/cms_cd.c
new file mode 100644
index 000000000..f05e30841
--- /dev/null
+++ b/crypto/cms/cms_cd.c
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2008-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "internal/cryptlib.h"
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include "cms_lcl.h"
+
+#ifdef ZLIB
+
+/* CMS CompressedData Utilities */
+
+CMS_ContentInfo *cms_CompressedData_create(int comp_nid)
+{
+    CMS_ContentInfo *cms;
+    CMS_CompressedData *cd;
+    /*
+     * Will need something cleverer if there is ever more than one
+     * compression algorithm or parameters have some meaning...
+     */
+    if (comp_nid != NID_zlib_compression) {
+        CMSerr(CMS_F_CMS_COMPRESSEDDATA_CREATE,
+               CMS_R_UNSUPPORTED_COMPRESSION_ALGORITHM);
+        return NULL;
+    }
+    cms = CMS_ContentInfo_new();
+    if (cms == NULL)
+        return NULL;
+
+    cd = M_ASN1_new_of(CMS_CompressedData);
+
+    if (cd == NULL)
+        goto err;
+
+    cms->contentType = OBJ_nid2obj(NID_id_smime_ct_compressedData);
+    cms->d.compressedData = cd;
+
+    cd->version = 0;
+
+    X509_ALGOR_set0(cd->compressionAlgorithm,
+                    OBJ_nid2obj(NID_zlib_compression), V_ASN1_UNDEF, NULL);
+
+    cd->encapContentInfo->eContentType = OBJ_nid2obj(NID_pkcs7_data);
+
+    return cms;
+
+ err:
+    CMS_ContentInfo_free(cms);
+    return NULL;
+}
+
+BIO *cms_CompressedData_init_bio(CMS_ContentInfo *cms)
+{
+    CMS_CompressedData *cd;
+    const ASN1_OBJECT *compoid;
+    if (OBJ_obj2nid(cms->contentType) != NID_id_smime_ct_compressedData) {
+        CMSerr(CMS_F_CMS_COMPRESSEDDATA_INIT_BIO,
+               CMS_R_CONTENT_TYPE_NOT_COMPRESSED_DATA);
+        return NULL;
+    }
+    cd = cms->d.compressedData;
+    X509_ALGOR_get0(&compoid, NULL, NULL, cd->compressionAlgorithm);
+    if (OBJ_obj2nid(compoid) != NID_zlib_compression) {
+        CMSerr(CMS_F_CMS_COMPRESSEDDATA_INIT_BIO,
+               CMS_R_UNSUPPORTED_COMPRESSION_ALGORITHM);
+        return NULL;
+    }
+    return BIO_new(BIO_f_zlib());
+}
+
+#endif
diff --git a/crypto/cms/cms_dd.c b/crypto/cms/cms_dd.c
new file mode 100644
index 000000000..5da6802fc
--- /dev/null
+++ b/crypto/cms/cms_dd.c
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2008-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "internal/cryptlib.h"
+#include 
+#include 
+#include 
+#include 
+#include 
+#include "cms_lcl.h"
+
+/* CMS DigestedData Utilities */
+
+CMS_ContentInfo *cms_DigestedData_create(const EVP_MD *md)
+{
+    CMS_ContentInfo *cms;
+    CMS_DigestedData *dd;
+    cms = CMS_ContentInfo_new();
+    if (cms == NULL)
+        return NULL;
+
+    dd = M_ASN1_new_of(CMS_DigestedData);
+
+    if (dd == NULL)
+        goto err;
+
+    cms->contentType = OBJ_nid2obj(NID_pkcs7_digest);
+    cms->d.digestedData = dd;
+
+    dd->version = 0;
+    dd->encapContentInfo->eContentType = OBJ_nid2obj(NID_pkcs7_data);
+
+    X509_ALGOR_set_md(dd->digestAlgorithm, md);
+
+    return cms;
+
+ err:
+    CMS_ContentInfo_free(cms);
+    return NULL;
+}
+
+BIO *cms_DigestedData_init_bio(CMS_ContentInfo *cms)
+{
+    CMS_DigestedData *dd;
+    dd = cms->d.digestedData;
+    return cms_DigestAlgorithm_init_bio(dd->digestAlgorithm);
+}
+
+int cms_DigestedData_do_final(CMS_ContentInfo *cms, BIO *chain, int verify)
+{
+    EVP_MD_CTX *mctx = EVP_MD_CTX_new();
+    unsigned char md[EVP_MAX_MD_SIZE];
+    unsigned int mdlen;
+    int r = 0;
+    CMS_DigestedData *dd;
+
+    if (mctx == NULL) {
+        CMSerr(CMS_F_CMS_DIGESTEDDATA_DO_FINAL, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+
+    dd = cms->d.digestedData;
+
+    if (!cms_DigestAlgorithm_find_ctx(mctx, chain, dd->digestAlgorithm))
+        goto err;
+
+    if (EVP_DigestFinal_ex(mctx, md, &mdlen) <= 0)
+        goto err;
+
+    if (verify) {
+        if (mdlen != (unsigned int)dd->digest->length) {
+            CMSerr(CMS_F_CMS_DIGESTEDDATA_DO_FINAL,
+                   CMS_R_MESSAGEDIGEST_WRONG_LENGTH);
+            goto err;
+        }
+
+        if (memcmp(md, dd->digest->data, mdlen))
+            CMSerr(CMS_F_CMS_DIGESTEDDATA_DO_FINAL,
+                   CMS_R_VERIFICATION_FAILURE);
+        else
+            r = 1;
+    } else {
+        if (!ASN1_STRING_set(dd->digest, md, mdlen))
+            goto err;
+        r = 1;
+    }
+
+ err:
+    EVP_MD_CTX_free(mctx);
+
+    return r;
+
+}
diff --git a/crypto/cms/cms_enc.c b/crypto/cms/cms_enc.c
new file mode 100644
index 000000000..ed913426b
--- /dev/null
+++ b/crypto/cms/cms_enc.c
@@ -0,0 +1,212 @@
+/*
+ * Copyright 2008-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "internal/cryptlib.h"
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include "cms_lcl.h"
+
+/* CMS EncryptedData Utilities */
+
+/* Return BIO based on EncryptedContentInfo and key */
+
+BIO *cms_EncryptedContent_init_bio(CMS_EncryptedContentInfo *ec)
+{
+    BIO *b;
+    EVP_CIPHER_CTX *ctx;
+    const EVP_CIPHER *ciph;
+    X509_ALGOR *calg = ec->contentEncryptionAlgorithm;
+    unsigned char iv[EVP_MAX_IV_LENGTH], *piv = NULL;
+    unsigned char *tkey = NULL;
+    size_t tkeylen = 0;
+
+    int ok = 0;
+
+    int enc, keep_key = 0;
+
+    enc = ec->cipher ? 1 : 0;
+
+    b = BIO_new(BIO_f_cipher());
+    if (b == NULL) {
+        CMSerr(CMS_F_CMS_ENCRYPTEDCONTENT_INIT_BIO, ERR_R_MALLOC_FAILURE);
+        return NULL;
+    }
+
+    BIO_get_cipher_ctx(b, &ctx);
+
+    if (enc) {
+        ciph = ec->cipher;
+        /*
+         * If not keeping key set cipher to NULL so subsequent calls decrypt.
+         */
+        if (ec->key)
+            ec->cipher = NULL;
+    } else {
+        ciph = EVP_get_cipherbyobj(calg->algorithm);
+
+        if (!ciph) {
+            CMSerr(CMS_F_CMS_ENCRYPTEDCONTENT_INIT_BIO, CMS_R_UNKNOWN_CIPHER);
+            goto err;
+        }
+    }
+
+    if (EVP_CipherInit_ex(ctx, ciph, NULL, NULL, NULL, enc) <= 0) {
+        CMSerr(CMS_F_CMS_ENCRYPTEDCONTENT_INIT_BIO,
+               CMS_R_CIPHER_INITIALISATION_ERROR);
+        goto err;
+    }
+
+    if (enc) {
+        int ivlen;
+        calg->algorithm = OBJ_nid2obj(EVP_CIPHER_CTX_type(ctx));
+        /* Generate a random IV if we need one */
+        ivlen = EVP_CIPHER_CTX_iv_length(ctx);
+        if (ivlen > 0) {
+            if (RAND_bytes(iv, ivlen) <= 0)
+                goto err;
+            piv = iv;
+        }
+    } else if (EVP_CIPHER_asn1_to_param(ctx, calg->parameter) <= 0) {
+        CMSerr(CMS_F_CMS_ENCRYPTEDCONTENT_INIT_BIO,
+               CMS_R_CIPHER_PARAMETER_INITIALISATION_ERROR);
+        goto err;
+    }
+    tkeylen = EVP_CIPHER_CTX_key_length(ctx);
+    /* Generate random session key */
+    if (!enc || !ec->key) {
+        tkey = OPENSSL_malloc(tkeylen);
+        if (tkey == NULL) {
+            CMSerr(CMS_F_CMS_ENCRYPTEDCONTENT_INIT_BIO, ERR_R_MALLOC_FAILURE);
+            goto err;
+        }
+        if (EVP_CIPHER_CTX_rand_key(ctx, tkey) <= 0)
+            goto err;
+    }
+
+    if (!ec->key) {
+        ec->key = tkey;
+        ec->keylen = tkeylen;
+        tkey = NULL;
+        if (enc)
+            keep_key = 1;
+        else
+            ERR_clear_error();
+
+    }
+
+    if (ec->keylen != tkeylen) {
+        /* If necessary set key length */
+        if (EVP_CIPHER_CTX_set_key_length(ctx, ec->keylen) <= 0) {
+            /*
+             * Only reveal failure if debugging so we don't leak information
+             * which may be useful in MMA.
+             */
+            if (enc || ec->debug) {
+                CMSerr(CMS_F_CMS_ENCRYPTEDCONTENT_INIT_BIO,
+                       CMS_R_INVALID_KEY_LENGTH);
+                goto err;
+            } else {
+                /* Use random key */
+                OPENSSL_clear_free(ec->key, ec->keylen);
+                ec->key = tkey;
+                ec->keylen = tkeylen;
+                tkey = NULL;
+                ERR_clear_error();
+            }
+        }
+    }
+
+    if (EVP_CipherInit_ex(ctx, NULL, NULL, ec->key, piv, enc) <= 0) {
+        CMSerr(CMS_F_CMS_ENCRYPTEDCONTENT_INIT_BIO,
+               CMS_R_CIPHER_INITIALISATION_ERROR);
+        goto err;
+    }
+    if (enc) {
+        calg->parameter = ASN1_TYPE_new();
+        if (calg->parameter == NULL) {
+            CMSerr(CMS_F_CMS_ENCRYPTEDCONTENT_INIT_BIO, ERR_R_MALLOC_FAILURE);
+            goto err;
+        }
+        if (EVP_CIPHER_param_to_asn1(ctx, calg->parameter) <= 0) {
+            CMSerr(CMS_F_CMS_ENCRYPTEDCONTENT_INIT_BIO,
+                   CMS_R_CIPHER_PARAMETER_INITIALISATION_ERROR);
+            goto err;
+        }
+        /* If parameter type not set omit parameter */
+        if (calg->parameter->type == V_ASN1_UNDEF) {
+            ASN1_TYPE_free(calg->parameter);
+            calg->parameter = NULL;
+        }
+    }
+    ok = 1;
+
+ err:
+    if (!keep_key || !ok) {
+        OPENSSL_clear_free(ec->key, ec->keylen);
+        ec->key = NULL;
+    }
+    OPENSSL_clear_free(tkey, tkeylen);
+    if (ok)
+        return b;
+    BIO_free(b);
+    return NULL;
+}
+
+int cms_EncryptedContent_init(CMS_EncryptedContentInfo *ec,
+                              const EVP_CIPHER *cipher,
+                              const unsigned char *key, size_t keylen)
+{
+    ec->cipher = cipher;
+    if (key) {
+        ec->key = OPENSSL_malloc(keylen);
+        if (ec->key == NULL)
+            return 0;
+        memcpy(ec->key, key, keylen);
+    }
+    ec->keylen = keylen;
+    if (cipher)
+        ec->contentType = OBJ_nid2obj(NID_pkcs7_data);
+    return 1;
+}
+
+int CMS_EncryptedData_set1_key(CMS_ContentInfo *cms, const EVP_CIPHER *ciph,
+                               const unsigned char *key, size_t keylen)
+{
+    CMS_EncryptedContentInfo *ec;
+    if (!key || !keylen) {
+        CMSerr(CMS_F_CMS_ENCRYPTEDDATA_SET1_KEY, CMS_R_NO_KEY);
+        return 0;
+    }
+    if (ciph) {
+        cms->d.encryptedData = M_ASN1_new_of(CMS_EncryptedData);
+        if (!cms->d.encryptedData) {
+            CMSerr(CMS_F_CMS_ENCRYPTEDDATA_SET1_KEY, ERR_R_MALLOC_FAILURE);
+            return 0;
+        }
+        cms->contentType = OBJ_nid2obj(NID_pkcs7_encrypted);
+        cms->d.encryptedData->version = 0;
+    } else if (OBJ_obj2nid(cms->contentType) != NID_pkcs7_encrypted) {
+        CMSerr(CMS_F_CMS_ENCRYPTEDDATA_SET1_KEY, CMS_R_NOT_ENCRYPTED_DATA);
+        return 0;
+    }
+    ec = cms->d.encryptedData->encryptedContentInfo;
+    return cms_EncryptedContent_init(ec, ciph, key, keylen);
+}
+
+BIO *cms_EncryptedData_init_bio(CMS_ContentInfo *cms)
+{
+    CMS_EncryptedData *enc = cms->d.encryptedData;
+    if (enc->encryptedContentInfo->cipher && enc->unprotectedAttrs)
+        enc->version = 2;
+    return cms_EncryptedContent_init_bio(enc->encryptedContentInfo);
+}
diff --git a/crypto/cms/cms_env.c b/crypto/cms/cms_env.c
new file mode 100644
index 000000000..c3de747cb
--- /dev/null
+++ b/crypto/cms/cms_env.c
@@ -0,0 +1,955 @@
+/*
+ * Copyright 2008-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "internal/cryptlib.h"
+#include 
+#include 
+#include 
+#include 
+#include 
+#ifndef OPENSSL_NO_AES
+# include 
+#endif
+#ifndef OPENSSL_NO_SMS4
+# include 
+#endif
+#include "cms_lcl.h"
+#include "internal/asn1_int.h"
+#include "internal/evp_int.h"
+
+/* CMS EnvelopedData Utilities */
+
+CMS_EnvelopedData *cms_get0_enveloped(CMS_ContentInfo *cms)
+{
+    if (OBJ_obj2nid(cms->contentType) != NID_pkcs7_enveloped) {
+        CMSerr(CMS_F_CMS_GET0_ENVELOPED,
+               CMS_R_CONTENT_TYPE_NOT_ENVELOPED_DATA);
+        return NULL;
+    }
+    return cms->d.envelopedData;
+}
+
+static CMS_EnvelopedData *cms_enveloped_data_init(CMS_ContentInfo *cms)
+{
+    if (cms->d.other == NULL) {
+        cms->d.envelopedData = M_ASN1_new_of(CMS_EnvelopedData);
+        if (!cms->d.envelopedData) {
+            CMSerr(CMS_F_CMS_ENVELOPED_DATA_INIT, ERR_R_MALLOC_FAILURE);
+            return NULL;
+        }
+        cms->d.envelopedData->version = 0;
+        cms->d.envelopedData->encryptedContentInfo->contentType =
+            OBJ_nid2obj(NID_pkcs7_data);
+        ASN1_OBJECT_free(cms->contentType);
+        cms->contentType = OBJ_nid2obj(NID_pkcs7_enveloped);
+        return cms->d.envelopedData;
+    }
+    return cms_get0_enveloped(cms);
+}
+
+int cms_env_asn1_ctrl(CMS_RecipientInfo *ri, int cmd)
+{
+    EVP_PKEY *pkey;
+    int i;
+    if (ri->type == CMS_RECIPINFO_TRANS)
+        pkey = ri->d.ktri->pkey;
+    else if (ri->type == CMS_RECIPINFO_AGREE) {
+        EVP_PKEY_CTX *pctx = ri->d.kari->pctx;
+        if (!pctx)
+            return 0;
+        pkey = EVP_PKEY_CTX_get0_pkey(pctx);
+        if (!pkey)
+            return 0;
+    } else
+        return 0;
+    if (!pkey->ameth || !pkey->ameth->pkey_ctrl)
+        return 1;
+    i = pkey->ameth->pkey_ctrl(pkey, ASN1_PKEY_CTRL_CMS_ENVELOPE, cmd, ri);
+    if (i == -2) {
+        CMSerr(CMS_F_CMS_ENV_ASN1_CTRL,
+               CMS_R_NOT_SUPPORTED_FOR_THIS_KEY_TYPE);
+        return 0;
+    }
+    if (i <= 0) {
+        CMSerr(CMS_F_CMS_ENV_ASN1_CTRL, CMS_R_CTRL_FAILURE);
+        return 0;
+    }
+    return 1;
+}
+
+STACK_OF(CMS_RecipientInfo) *CMS_get0_RecipientInfos(CMS_ContentInfo *cms)
+{
+    CMS_EnvelopedData *env;
+    env = cms_get0_enveloped(cms);
+    if (!env)
+        return NULL;
+    return env->recipientInfos;
+}
+
+int CMS_RecipientInfo_type(CMS_RecipientInfo *ri)
+{
+    return ri->type;
+}
+
+EVP_PKEY_CTX *CMS_RecipientInfo_get0_pkey_ctx(CMS_RecipientInfo *ri)
+{
+    if (ri->type == CMS_RECIPINFO_TRANS)
+        return ri->d.ktri->pctx;
+    else if (ri->type == CMS_RECIPINFO_AGREE)
+        return ri->d.kari->pctx;
+    return NULL;
+}
+
+CMS_ContentInfo *CMS_EnvelopedData_create(const EVP_CIPHER *cipher)
+{
+    CMS_ContentInfo *cms;
+    CMS_EnvelopedData *env;
+    cms = CMS_ContentInfo_new();
+    if (cms == NULL)
+        goto merr;
+    env = cms_enveloped_data_init(cms);
+    if (env == NULL)
+        goto merr;
+    if (!cms_EncryptedContent_init(env->encryptedContentInfo,
+                                   cipher, NULL, 0))
+        goto merr;
+    return cms;
+ merr:
+    CMS_ContentInfo_free(cms);
+    CMSerr(CMS_F_CMS_ENVELOPEDDATA_CREATE, ERR_R_MALLOC_FAILURE);
+    return NULL;
+}
+
+/* Key Transport Recipient Info (KTRI) routines */
+
+/* Initialise a ktri based on passed certificate and key */
+
+static int cms_RecipientInfo_ktri_init(CMS_RecipientInfo *ri, X509 *recip,
+                                       EVP_PKEY *pk, unsigned int flags)
+{
+    CMS_KeyTransRecipientInfo *ktri;
+    int idtype;
+
+    ri->d.ktri = M_ASN1_new_of(CMS_KeyTransRecipientInfo);
+    if (!ri->d.ktri)
+        return 0;
+    ri->type = CMS_RECIPINFO_TRANS;
+
+    ktri = ri->d.ktri;
+
+    if (flags & CMS_USE_KEYID) {
+        ktri->version = 2;
+        idtype = CMS_RECIPINFO_KEYIDENTIFIER;
+    } else {
+        ktri->version = 0;
+        idtype = CMS_RECIPINFO_ISSUER_SERIAL;
+    }
+
+    /*
+     * Not a typo: RecipientIdentifier and SignerIdentifier are the same
+     * structure.
+     */
+
+    if (!cms_set1_SignerIdentifier(ktri->rid, recip, idtype))
+        return 0;
+
+    X509_up_ref(recip);
+    EVP_PKEY_up_ref(pk);
+
+    ktri->pkey = pk;
+    ktri->recip = recip;
+
+    if (flags & CMS_KEY_PARAM) {
+        ktri->pctx = EVP_PKEY_CTX_new(ktri->pkey, NULL);
+        if (ktri->pctx == NULL)
+            return 0;
+        if (EVP_PKEY_encrypt_init(ktri->pctx) <= 0)
+            return 0;
+    } else if (!cms_env_asn1_ctrl(ri, 0))
+        return 0;
+    return 1;
+}
+
+/*
+ * Add a recipient certificate using appropriate type of RecipientInfo
+ */
+
+CMS_RecipientInfo *CMS_add1_recipient_cert(CMS_ContentInfo *cms,
+                                           X509 *recip, unsigned int flags)
+{
+    CMS_RecipientInfo *ri = NULL;
+    CMS_EnvelopedData *env;
+    EVP_PKEY *pk = NULL;
+    env = cms_get0_enveloped(cms);
+    if (!env)
+        goto err;
+
+    /* Initialize recipient info */
+    ri = M_ASN1_new_of(CMS_RecipientInfo);
+    if (!ri)
+        goto merr;
+
+    pk = X509_get0_pubkey(recip);
+    if (!pk) {
+        CMSerr(CMS_F_CMS_ADD1_RECIPIENT_CERT, CMS_R_ERROR_GETTING_PUBLIC_KEY);
+        goto err;
+    }
+
+    switch (cms_pkey_get_ri_type(pk)) {
+
+    case CMS_RECIPINFO_TRANS:
+        if (!cms_RecipientInfo_ktri_init(ri, recip, pk, flags))
+            goto err;
+        break;
+
+    case CMS_RECIPINFO_AGREE:
+        if (!cms_RecipientInfo_kari_init(ri, recip, pk, flags))
+            goto err;
+        break;
+
+    default:
+        CMSerr(CMS_F_CMS_ADD1_RECIPIENT_CERT,
+               CMS_R_NOT_SUPPORTED_FOR_THIS_KEY_TYPE);
+        goto err;
+
+    }
+
+    if (!sk_CMS_RecipientInfo_push(env->recipientInfos, ri))
+        goto merr;
+
+    return ri;
+
+ merr:
+    CMSerr(CMS_F_CMS_ADD1_RECIPIENT_CERT, ERR_R_MALLOC_FAILURE);
+ err:
+    M_ASN1_free_of(ri, CMS_RecipientInfo);
+    return NULL;
+
+}
+
+int CMS_RecipientInfo_ktri_get0_algs(CMS_RecipientInfo *ri,
+                                     EVP_PKEY **pk, X509 **recip,
+                                     X509_ALGOR **palg)
+{
+    CMS_KeyTransRecipientInfo *ktri;
+    if (ri->type != CMS_RECIPINFO_TRANS) {
+        CMSerr(CMS_F_CMS_RECIPIENTINFO_KTRI_GET0_ALGS,
+               CMS_R_NOT_KEY_TRANSPORT);
+        return 0;
+    }
+
+    ktri = ri->d.ktri;
+
+    if (pk)
+        *pk = ktri->pkey;
+    if (recip)
+        *recip = ktri->recip;
+    if (palg)
+        *palg = ktri->keyEncryptionAlgorithm;
+    return 1;
+}
+
+int CMS_RecipientInfo_ktri_get0_signer_id(CMS_RecipientInfo *ri,
+                                          ASN1_OCTET_STRING **keyid,
+                                          X509_NAME **issuer,
+                                          ASN1_INTEGER **sno)
+{
+    CMS_KeyTransRecipientInfo *ktri;
+    if (ri->type != CMS_RECIPINFO_TRANS) {
+        CMSerr(CMS_F_CMS_RECIPIENTINFO_KTRI_GET0_SIGNER_ID,
+               CMS_R_NOT_KEY_TRANSPORT);
+        return 0;
+    }
+    ktri = ri->d.ktri;
+
+    return cms_SignerIdentifier_get0_signer_id(ktri->rid, keyid, issuer, sno);
+}
+
+int CMS_RecipientInfo_ktri_cert_cmp(CMS_RecipientInfo *ri, X509 *cert)
+{
+    if (ri->type != CMS_RECIPINFO_TRANS) {
+        CMSerr(CMS_F_CMS_RECIPIENTINFO_KTRI_CERT_CMP,
+               CMS_R_NOT_KEY_TRANSPORT);
+        return -2;
+    }
+    return cms_SignerIdentifier_cert_cmp(ri->d.ktri->rid, cert);
+}
+
+int CMS_RecipientInfo_set0_pkey(CMS_RecipientInfo *ri, EVP_PKEY *pkey)
+{
+    if (ri->type != CMS_RECIPINFO_TRANS) {
+        CMSerr(CMS_F_CMS_RECIPIENTINFO_SET0_PKEY, CMS_R_NOT_KEY_TRANSPORT);
+        return 0;
+    }
+    ri->d.ktri->pkey = pkey;
+    return 1;
+}
+
+/* Encrypt content key in key transport recipient info */
+
+static int cms_RecipientInfo_ktri_encrypt(CMS_ContentInfo *cms,
+                                          CMS_RecipientInfo *ri)
+{
+    CMS_KeyTransRecipientInfo *ktri;
+    CMS_EncryptedContentInfo *ec;
+    EVP_PKEY_CTX *pctx;
+    unsigned char *ek = NULL;
+    size_t eklen;
+
+    int ret = 0;
+
+    if (ri->type != CMS_RECIPINFO_TRANS) {
+        CMSerr(CMS_F_CMS_RECIPIENTINFO_KTRI_ENCRYPT, CMS_R_NOT_KEY_TRANSPORT);
+        return 0;
+    }
+    ktri = ri->d.ktri;
+    ec = cms->d.envelopedData->encryptedContentInfo;
+
+    pctx = ktri->pctx;
+
+    if (pctx) {
+        if (!cms_env_asn1_ctrl(ri, 0))
+            goto err;
+    } else {
+        pctx = EVP_PKEY_CTX_new(ktri->pkey, NULL);
+        if (pctx == NULL)
+            return 0;
+
+        if (EVP_PKEY_encrypt_init(pctx) <= 0)
+            goto err;
+    }
+
+    if (EVP_PKEY_CTX_ctrl(pctx, -1, EVP_PKEY_OP_ENCRYPT,
+                          EVP_PKEY_CTRL_CMS_ENCRYPT, 0, ri) <= 0) {
+        CMSerr(CMS_F_CMS_RECIPIENTINFO_KTRI_ENCRYPT, CMS_R_CTRL_ERROR);
+        goto err;
+    }
+
+    if (EVP_PKEY_encrypt(pctx, NULL, &eklen, ec->key, ec->keylen) <= 0)
+        goto err;
+
+    ek = OPENSSL_malloc(eklen);
+
+    if (ek == NULL) {
+        CMSerr(CMS_F_CMS_RECIPIENTINFO_KTRI_ENCRYPT, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+
+    if (EVP_PKEY_encrypt(pctx, ek, &eklen, ec->key, ec->keylen) <= 0)
+        goto err;
+
+    ASN1_STRING_set0(ktri->encryptedKey, ek, eklen);
+    ek = NULL;
+
+    ret = 1;
+
+ err:
+    EVP_PKEY_CTX_free(pctx);
+    ktri->pctx = NULL;
+    OPENSSL_free(ek);
+    return ret;
+
+}
+
+/* Decrypt content key from KTRI */
+
+static int cms_RecipientInfo_ktri_decrypt(CMS_ContentInfo *cms,
+                                          CMS_RecipientInfo *ri)
+{
+    CMS_KeyTransRecipientInfo *ktri = ri->d.ktri;
+    EVP_PKEY *pkey = ktri->pkey;
+    unsigned char *ek = NULL;
+    size_t eklen;
+    int ret = 0;
+    CMS_EncryptedContentInfo *ec;
+    ec = cms->d.envelopedData->encryptedContentInfo;
+
+    if (ktri->pkey == NULL) {
+        CMSerr(CMS_F_CMS_RECIPIENTINFO_KTRI_DECRYPT, CMS_R_NO_PRIVATE_KEY);
+        return 0;
+    }
+
+    ktri->pctx = EVP_PKEY_CTX_new(pkey, NULL);
+    if (ktri->pctx == NULL)
+        return 0;
+
+    if (EVP_PKEY_decrypt_init(ktri->pctx) <= 0)
+        goto err;
+
+    if (!cms_env_asn1_ctrl(ri, 1))
+        goto err;
+
+    if (EVP_PKEY_CTX_ctrl(ktri->pctx, -1, EVP_PKEY_OP_DECRYPT,
+                          EVP_PKEY_CTRL_CMS_DECRYPT, 0, ri) <= 0) {
+        CMSerr(CMS_F_CMS_RECIPIENTINFO_KTRI_DECRYPT, CMS_R_CTRL_ERROR);
+        goto err;
+    }
+
+    if (EVP_PKEY_decrypt(ktri->pctx, NULL, &eklen,
+                         ktri->encryptedKey->data,
+                         ktri->encryptedKey->length) <= 0)
+        goto err;
+
+    ek = OPENSSL_malloc(eklen);
+
+    if (ek == NULL) {
+        CMSerr(CMS_F_CMS_RECIPIENTINFO_KTRI_DECRYPT, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+
+    if (EVP_PKEY_decrypt(ktri->pctx, ek, &eklen,
+                         ktri->encryptedKey->data,
+                         ktri->encryptedKey->length) <= 0) {
+        CMSerr(CMS_F_CMS_RECIPIENTINFO_KTRI_DECRYPT, CMS_R_CMS_LIB);
+        goto err;
+    }
+
+    ret = 1;
+
+    OPENSSL_clear_free(ec->key, ec->keylen);
+    ec->key = ek;
+    ec->keylen = eklen;
+
+ err:
+    EVP_PKEY_CTX_free(ktri->pctx);
+    ktri->pctx = NULL;
+    if (!ret)
+        OPENSSL_free(ek);
+
+    return ret;
+}
+
+/* Key Encrypted Key (KEK) RecipientInfo routines */
+
+int CMS_RecipientInfo_kekri_id_cmp(CMS_RecipientInfo *ri,
+                                   const unsigned char *id, size_t idlen)
+{
+    ASN1_OCTET_STRING tmp_os;
+    CMS_KEKRecipientInfo *kekri;
+    if (ri->type != CMS_RECIPINFO_KEK) {
+        CMSerr(CMS_F_CMS_RECIPIENTINFO_KEKRI_ID_CMP, CMS_R_NOT_KEK);
+        return -2;
+    }
+    kekri = ri->d.kekri;
+    tmp_os.type = V_ASN1_OCTET_STRING;
+    tmp_os.flags = 0;
+    tmp_os.data = (unsigned char *)id;
+    tmp_os.length = (int)idlen;
+    return ASN1_OCTET_STRING_cmp(&tmp_os, kekri->kekid->keyIdentifier);
+}
+
+/* For now hard code AES key wrap info */
+
+static size_t aes_wrap_keylen(int nid)
+{
+    switch (nid) {
+    case NID_id_aes128_wrap:
+        return 16;
+
+    case NID_id_aes192_wrap:
+        return 24;
+
+    case NID_id_aes256_wrap:
+        return 32;
+
+    default:
+        return 0;
+    }
+}
+
+CMS_RecipientInfo *CMS_add0_recipient_key(CMS_ContentInfo *cms, int nid,
+                                          unsigned char *key, size_t keylen,
+                                          unsigned char *id, size_t idlen,
+                                          ASN1_GENERALIZEDTIME *date,
+                                          ASN1_OBJECT *otherTypeId,
+                                          ASN1_TYPE *otherType)
+{
+    CMS_RecipientInfo *ri = NULL;
+    CMS_EnvelopedData *env;
+    CMS_KEKRecipientInfo *kekri;
+    env = cms_get0_enveloped(cms);
+    if (!env)
+        goto err;
+
+    if (nid == NID_undef) {
+        switch (keylen) {
+        case 16:
+            nid = NID_id_aes128_wrap;
+            break;
+
+        case 24:
+            nid = NID_id_aes192_wrap;
+            break;
+
+        case 32:
+            nid = NID_id_aes256_wrap;
+            break;
+
+        default:
+            CMSerr(CMS_F_CMS_ADD0_RECIPIENT_KEY, CMS_R_INVALID_KEY_LENGTH);
+            goto err;
+        }
+
+    } else {
+
+        size_t exp_keylen = aes_wrap_keylen(nid);
+
+        if (!exp_keylen) {
+            CMSerr(CMS_F_CMS_ADD0_RECIPIENT_KEY,
+                   CMS_R_UNSUPPORTED_KEK_ALGORITHM);
+            goto err;
+        }
+
+        if (keylen != exp_keylen) {
+            CMSerr(CMS_F_CMS_ADD0_RECIPIENT_KEY, CMS_R_INVALID_KEY_LENGTH);
+            goto err;
+        }
+
+    }
+
+    /* Initialize recipient info */
+    ri = M_ASN1_new_of(CMS_RecipientInfo);
+    if (!ri)
+        goto merr;
+
+    ri->d.kekri = M_ASN1_new_of(CMS_KEKRecipientInfo);
+    if (!ri->d.kekri)
+        goto merr;
+    ri->type = CMS_RECIPINFO_KEK;
+
+    kekri = ri->d.kekri;
+
+    if (otherTypeId) {
+        kekri->kekid->other = M_ASN1_new_of(CMS_OtherKeyAttribute);
+        if (kekri->kekid->other == NULL)
+            goto merr;
+    }
+
+    if (!sk_CMS_RecipientInfo_push(env->recipientInfos, ri))
+        goto merr;
+
+    /* After this point no calls can fail */
+
+    kekri->version = 4;
+
+    kekri->key = key;
+    kekri->keylen = keylen;
+
+    ASN1_STRING_set0(kekri->kekid->keyIdentifier, id, idlen);
+
+    kekri->kekid->date = date;
+
+    if (kekri->kekid->other) {
+        kekri->kekid->other->keyAttrId = otherTypeId;
+        kekri->kekid->other->keyAttr = otherType;
+    }
+
+    X509_ALGOR_set0(kekri->keyEncryptionAlgorithm,
+                    OBJ_nid2obj(nid), V_ASN1_UNDEF, NULL);
+
+    return ri;
+
+ merr:
+    CMSerr(CMS_F_CMS_ADD0_RECIPIENT_KEY, ERR_R_MALLOC_FAILURE);
+ err:
+    M_ASN1_free_of(ri, CMS_RecipientInfo);
+    return NULL;
+
+}
+
+int CMS_RecipientInfo_kekri_get0_id(CMS_RecipientInfo *ri,
+                                    X509_ALGOR **palg,
+                                    ASN1_OCTET_STRING **pid,
+                                    ASN1_GENERALIZEDTIME **pdate,
+                                    ASN1_OBJECT **potherid,
+                                    ASN1_TYPE **pothertype)
+{
+    CMS_KEKIdentifier *rkid;
+    if (ri->type != CMS_RECIPINFO_KEK) {
+        CMSerr(CMS_F_CMS_RECIPIENTINFO_KEKRI_GET0_ID, CMS_R_NOT_KEK);
+        return 0;
+    }
+    rkid = ri->d.kekri->kekid;
+    if (palg)
+        *palg = ri->d.kekri->keyEncryptionAlgorithm;
+    if (pid)
+        *pid = rkid->keyIdentifier;
+    if (pdate)
+        *pdate = rkid->date;
+    if (potherid) {
+        if (rkid->other)
+            *potherid = rkid->other->keyAttrId;
+        else
+            *potherid = NULL;
+    }
+    if (pothertype) {
+        if (rkid->other)
+            *pothertype = rkid->other->keyAttr;
+        else
+            *pothertype = NULL;
+    }
+    return 1;
+}
+
+int CMS_RecipientInfo_set0_key(CMS_RecipientInfo *ri,
+                               unsigned char *key, size_t keylen)
+{
+    CMS_KEKRecipientInfo *kekri;
+    if (ri->type != CMS_RECIPINFO_KEK) {
+        CMSerr(CMS_F_CMS_RECIPIENTINFO_SET0_KEY, CMS_R_NOT_KEK);
+        return 0;
+    }
+
+    kekri = ri->d.kekri;
+    kekri->key = key;
+    kekri->keylen = keylen;
+    return 1;
+}
+
+/* Encrypt content key in KEK recipient info */
+
+static int cms_RecipientInfo_kekri_encrypt(CMS_ContentInfo *cms,
+                                           CMS_RecipientInfo *ri)
+{
+    CMS_EncryptedContentInfo *ec;
+    CMS_KEKRecipientInfo *kekri;
+    unsigned char *wkey = NULL;
+    int wkeylen;
+    int r = 0;
+#if !defined(OPENSSL_NO_AES)
+    AES_KEY actx;
+#elif !defined(OPENSSL_NO_SMS4)
+    sms4_key_t sctx;
+#else
+    CMSerr(CMS_F_CMS_RECIPIENTINFO_KEKRI_ENCRYPT, CMS_R_NO_AVAILABLE_CIPHER);
+    return 0;
+#endif
+
+    ec = cms->d.envelopedData->encryptedContentInfo;
+
+    kekri = ri->d.kekri;
+
+    if (!kekri->key) {
+        CMSerr(CMS_F_CMS_RECIPIENTINFO_KEKRI_ENCRYPT, CMS_R_NO_KEY);
+        return 0;
+    }
+
+#if !defined(OPENSSL_NO_AES)
+    if (AES_set_encrypt_key(kekri->key, kekri->keylen << 3, &actx)) {
+        CMSerr(CMS_F_CMS_RECIPIENTINFO_KEKRI_ENCRYPT,
+               CMS_R_ERROR_SETTING_KEY);
+        goto err;
+    }
+#elif !defined(OPENSSL_NO_SMS4)
+    sms4_set_encrypt_key(&sctx, kekri->key);
+#endif
+
+    wkey = OPENSSL_malloc(ec->keylen + 8);
+
+    if (wkey == NULL) {
+        CMSerr(CMS_F_CMS_RECIPIENTINFO_KEKRI_ENCRYPT, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+
+#ifndef OPENSSL_NO_AES
+    wkeylen = AES_wrap_key(&actx, NULL, wkey, ec->key, ec->keylen);
+#elif !defined(OPENSSL_NO_SMS4)
+    wkeylen = sms4_wrap_key(&sctx, NULL, wkey, ec->key, ec->keylen);
+#endif
+
+    if (wkeylen <= 0) {
+        CMSerr(CMS_F_CMS_RECIPIENTINFO_KEKRI_ENCRYPT, CMS_R_WRAP_ERROR);
+        goto err;
+    }
+
+    ASN1_STRING_set0(kekri->encryptedKey, wkey, wkeylen);
+
+    r = 1;
+
+ err:
+
+    if (!r)
+        OPENSSL_free(wkey);
+#ifndef OPENSSL_NO_AES
+    OPENSSL_cleanse(&actx, sizeof(actx));
+#elif !defined(OPENSSL_NO_SMS4)
+    OPENSSL_cleanse(&sctx, sizeof(sctx));
+#endif
+
+    return r;
+
+}
+
+/* Decrypt content key in KEK recipient info */
+
+static int cms_RecipientInfo_kekri_decrypt(CMS_ContentInfo *cms,
+                                           CMS_RecipientInfo *ri)
+{
+    CMS_EncryptedContentInfo *ec;
+    CMS_KEKRecipientInfo *kekri;
+    unsigned char *ukey = NULL;
+    int ukeylen;
+    int r = 0, wrap_nid;
+#ifndef OPENSSL_NO_AES
+    AES_KEY actx;
+#elif !defined(OPENSSL_NO_SMS4)
+    sms4_key_t sctx;
+#else
+    CMSerr(CMS_F_CMS_RECIPIENTINFO_KEKRI_DECRYPT, CMS_R_NO_AVAILABLE_CIPHER);
+    return 0;
+#endif
+
+    ec = cms->d.envelopedData->encryptedContentInfo;
+
+    kekri = ri->d.kekri;
+
+    if (!kekri->key) {
+        CMSerr(CMS_F_CMS_RECIPIENTINFO_KEKRI_DECRYPT, CMS_R_NO_KEY);
+        return 0;
+    }
+
+    wrap_nid = OBJ_obj2nid(kekri->keyEncryptionAlgorithm->algorithm);
+#ifndef OPENSSL_NO_AES
+    if (aes_wrap_keylen(wrap_nid) != kekri->keylen) {
+        CMSerr(CMS_F_CMS_RECIPIENTINFO_KEKRI_DECRYPT,
+               CMS_R_INVALID_KEY_LENGTH);
+        return 0;
+    }
+#elif !defined(OPENSSL_NO_SMS4)
+    if (SMS4_KEY_LENGTH != kekri->keylen) {
+        CMSerr(CMS_F_CMS_RECIPIENTINFO_KEKRI_DECRYPT,
+               CMS_R_INVALID_KEY_LENGTH);
+        return 0;
+    }
+#endif
+
+    /* If encrypted key length is invalid don't bother */
+
+    if (kekri->encryptedKey->length < 16) {
+        CMSerr(CMS_F_CMS_RECIPIENTINFO_KEKRI_DECRYPT,
+               CMS_R_INVALID_ENCRYPTED_KEY_LENGTH);
+        goto err;
+    }
+
+#ifndef OPENSSL_NO_AES
+    if (AES_set_decrypt_key(kekri->key, kekri->keylen << 3, &actx)) {
+        CMSerr(CMS_F_CMS_RECIPIENTINFO_KEKRI_DECRYPT,
+               CMS_R_ERROR_SETTING_KEY);
+        goto err;
+    }
+#elif !defined(OPENSSL_NO_SMS4)
+    sms4_set_decrypt_key(&sctx, kekri->key);
+#endif
+
+    ukey = OPENSSL_malloc(kekri->encryptedKey->length - 8);
+
+    if (ukey == NULL) {
+        CMSerr(CMS_F_CMS_RECIPIENTINFO_KEKRI_DECRYPT, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+
+#ifndef OPENSSL_NO_AES
+    ukeylen = AES_unwrap_key(&actx, NULL, ukey,
+                             kekri->encryptedKey->data,
+                             kekri->encryptedKey->length);
+#elif !defined(OPENSSL_NO_SMS4)
+    ukeylen = sms4_unwrap_key(&sctx, NULL, ukey,
+                              kekri->encryptedKey->data,
+                              kekri->encryptedKey->length);
+#endif
+
+    if (ukeylen <= 0) {
+        CMSerr(CMS_F_CMS_RECIPIENTINFO_KEKRI_DECRYPT, CMS_R_UNWRAP_ERROR);
+        goto err;
+    }
+
+    ec->key = ukey;
+    ec->keylen = ukeylen;
+
+    r = 1;
+
+ err:
+
+    if (!r)
+        OPENSSL_free(ukey);
+#ifndef OPENSSL_NO_AES
+    OPENSSL_cleanse(&actx, sizeof(actx));
+#elif !defined(OPENSSL_NO_SMS4)
+    OPENSSL_cleanse(&sctx, sizeof(sctx));
+#endif
+
+    return r;
+
+}
+
+int CMS_RecipientInfo_decrypt(CMS_ContentInfo *cms, CMS_RecipientInfo *ri)
+{
+    switch (ri->type) {
+    case CMS_RECIPINFO_TRANS:
+        return cms_RecipientInfo_ktri_decrypt(cms, ri);
+
+    case CMS_RECIPINFO_KEK:
+        return cms_RecipientInfo_kekri_decrypt(cms, ri);
+
+    case CMS_RECIPINFO_PASS:
+        return cms_RecipientInfo_pwri_crypt(cms, ri, 0);
+
+    default:
+        CMSerr(CMS_F_CMS_RECIPIENTINFO_DECRYPT,
+               CMS_R_UNSUPPORTED_RECPIENTINFO_TYPE);
+        return 0;
+    }
+}
+
+int CMS_RecipientInfo_encrypt(CMS_ContentInfo *cms, CMS_RecipientInfo *ri)
+{
+    switch (ri->type) {
+    case CMS_RECIPINFO_TRANS:
+        return cms_RecipientInfo_ktri_encrypt(cms, ri);
+
+    case CMS_RECIPINFO_AGREE:
+        return cms_RecipientInfo_kari_encrypt(cms, ri);
+
+    case CMS_RECIPINFO_KEK:
+        return cms_RecipientInfo_kekri_encrypt(cms, ri);
+
+    case CMS_RECIPINFO_PASS:
+        return cms_RecipientInfo_pwri_crypt(cms, ri, 1);
+
+    default:
+        CMSerr(CMS_F_CMS_RECIPIENTINFO_ENCRYPT,
+               CMS_R_UNSUPPORTED_RECIPIENT_TYPE);
+        return 0;
+    }
+}
+
+/* Check structures and fixup version numbers (if necessary) */
+
+static void cms_env_set_originfo_version(CMS_EnvelopedData *env)
+{
+    CMS_OriginatorInfo *org = env->originatorInfo;
+    int i;
+    if (org == NULL)
+        return;
+    for (i = 0; i < sk_CMS_CertificateChoices_num(org->certificates); i++) {
+        CMS_CertificateChoices *cch;
+        cch = sk_CMS_CertificateChoices_value(org->certificates, i);
+        if (cch->type == CMS_CERTCHOICE_OTHER) {
+            env->version = 4;
+            return;
+        } else if (cch->type == CMS_CERTCHOICE_V2ACERT) {
+            if (env->version < 3)
+                env->version = 3;
+        }
+    }
+
+    for (i = 0; i < sk_CMS_RevocationInfoChoice_num(org->crls); i++) {
+        CMS_RevocationInfoChoice *rch;
+        rch = sk_CMS_RevocationInfoChoice_value(org->crls, i);
+        if (rch->type == CMS_REVCHOICE_OTHER) {
+            env->version = 4;
+            return;
+        }
+    }
+}
+
+static void cms_env_set_version(CMS_EnvelopedData *env)
+{
+    int i;
+    CMS_RecipientInfo *ri;
+
+    /*
+     * Can't set version higher than 4 so if 4 or more already nothing to do.
+     */
+    if (env->version >= 4)
+        return;
+
+    cms_env_set_originfo_version(env);
+
+    if (env->version >= 3)
+        return;
+
+    for (i = 0; i < sk_CMS_RecipientInfo_num(env->recipientInfos); i++) {
+        ri = sk_CMS_RecipientInfo_value(env->recipientInfos, i);
+        if (ri->type == CMS_RECIPINFO_PASS || ri->type == CMS_RECIPINFO_OTHER) {
+            env->version = 3;
+            return;
+        } else if (ri->type != CMS_RECIPINFO_TRANS
+                   || ri->d.ktri->version != 0) {
+            env->version = 2;
+        }
+    }
+    if (env->originatorInfo || env->unprotectedAttrs)
+        env->version = 2;
+    if (env->version == 2)
+        return;
+    env->version = 0;
+}
+
+BIO *cms_EnvelopedData_init_bio(CMS_ContentInfo *cms)
+{
+    CMS_EncryptedContentInfo *ec;
+    STACK_OF(CMS_RecipientInfo) *rinfos;
+    CMS_RecipientInfo *ri;
+    int i, ok = 0;
+    BIO *ret;
+
+    /* Get BIO first to set up key */
+
+    ec = cms->d.envelopedData->encryptedContentInfo;
+    ret = cms_EncryptedContent_init_bio(ec);
+
+    /* If error or no cipher end of processing */
+
+    if (!ret || !ec->cipher)
+        return ret;
+
+    /* Now encrypt content key according to each RecipientInfo type */
+
+    rinfos = cms->d.envelopedData->recipientInfos;
+
+    for (i = 0; i < sk_CMS_RecipientInfo_num(rinfos); i++) {
+        ri = sk_CMS_RecipientInfo_value(rinfos, i);
+        if (CMS_RecipientInfo_encrypt(cms, ri) <= 0) {
+            CMSerr(CMS_F_CMS_ENVELOPEDDATA_INIT_BIO,
+                   CMS_R_ERROR_SETTING_RECIPIENTINFO);
+            goto err;
+        }
+    }
+    cms_env_set_version(cms->d.envelopedData);
+
+    ok = 1;
+
+ err:
+    ec->cipher = NULL;
+    OPENSSL_clear_free(ec->key, ec->keylen);
+    ec->key = NULL;
+    ec->keylen = 0;
+    if (ok)
+        return ret;
+    BIO_free(ret);
+    return NULL;
+
+}
+
+/*
+ * Get RecipientInfo type (if any) supported by a key (public or private). To
+ * retain compatibility with previous behaviour if the ctrl value isn't
+ * supported we assume key transport.
+ */
+int cms_pkey_get_ri_type(EVP_PKEY *pk)
+{
+    if (pk->ameth && pk->ameth->pkey_ctrl) {
+        int i, r;
+        i = pk->ameth->pkey_ctrl(pk, ASN1_PKEY_CTRL_CMS_RI_TYPE, 0, &r);
+        if (i > 0)
+            return r;
+    }
+    return CMS_RECIPINFO_TRANS;
+}
diff --git a/crypto/cms/cms_err.c b/crypto/cms/cms_err.c
new file mode 100644
index 000000000..2b4aad00e
--- /dev/null
+++ b/crypto/cms/cms_err.c
@@ -0,0 +1,259 @@
+/*
+ * Generated by util/mkerr.pl DO NOT EDIT
+ * Copyright 1995-2017 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include 
+
+/* BEGIN ERROR CODES */
+#ifndef OPENSSL_NO_ERR
+
+# define ERR_FUNC(func) ERR_PACK(ERR_LIB_CMS,func,0)
+# define ERR_REASON(reason) ERR_PACK(ERR_LIB_CMS,0,reason)
+
+static ERR_STRING_DATA CMS_str_functs[] = {
+    {ERR_FUNC(CMS_F_CHECK_CONTENT), "check_content"},
+    {ERR_FUNC(CMS_F_CMS_ADD0_CERT), "CMS_add0_cert"},
+    {ERR_FUNC(CMS_F_CMS_ADD0_RECIPIENT_KEY), "CMS_add0_recipient_key"},
+    {ERR_FUNC(CMS_F_CMS_ADD0_RECIPIENT_PASSWORD),
+     "CMS_add0_recipient_password"},
+    {ERR_FUNC(CMS_F_CMS_ADD1_RECEIPTREQUEST), "CMS_add1_ReceiptRequest"},
+    {ERR_FUNC(CMS_F_CMS_ADD1_RECIPIENT_CERT), "CMS_add1_recipient_cert"},
+    {ERR_FUNC(CMS_F_CMS_ADD1_SIGNER), "CMS_add1_signer"},
+    {ERR_FUNC(CMS_F_CMS_ADD1_SIGNINGTIME), "cms_add1_signingTime"},
+    {ERR_FUNC(CMS_F_CMS_COMPRESS), "CMS_compress"},
+    {ERR_FUNC(CMS_F_CMS_COMPRESSEDDATA_CREATE), "cms_CompressedData_create"},
+    {ERR_FUNC(CMS_F_CMS_COMPRESSEDDATA_INIT_BIO),
+     "cms_CompressedData_init_bio"},
+    {ERR_FUNC(CMS_F_CMS_COPY_CONTENT), "cms_copy_content"},
+    {ERR_FUNC(CMS_F_CMS_COPY_MESSAGEDIGEST), "cms_copy_messageDigest"},
+    {ERR_FUNC(CMS_F_CMS_DATA), "CMS_data"},
+    {ERR_FUNC(CMS_F_CMS_DATAFINAL), "CMS_dataFinal"},
+    {ERR_FUNC(CMS_F_CMS_DATAINIT), "CMS_dataInit"},
+    {ERR_FUNC(CMS_F_CMS_DECRYPT), "CMS_decrypt"},
+    {ERR_FUNC(CMS_F_CMS_DECRYPT_SET1_KEY), "CMS_decrypt_set1_key"},
+    {ERR_FUNC(CMS_F_CMS_DECRYPT_SET1_PASSWORD), "CMS_decrypt_set1_password"},
+    {ERR_FUNC(CMS_F_CMS_DECRYPT_SET1_PKEY), "CMS_decrypt_set1_pkey"},
+    {ERR_FUNC(CMS_F_CMS_DIGESTALGORITHM_FIND_CTX),
+     "cms_DigestAlgorithm_find_ctx"},
+    {ERR_FUNC(CMS_F_CMS_DIGESTALGORITHM_INIT_BIO),
+     "cms_DigestAlgorithm_init_bio"},
+    {ERR_FUNC(CMS_F_CMS_DIGESTEDDATA_DO_FINAL), "cms_DigestedData_do_final"},
+    {ERR_FUNC(CMS_F_CMS_DIGEST_VERIFY), "CMS_digest_verify"},
+    {ERR_FUNC(CMS_F_CMS_ENCODE_RECEIPT), "cms_encode_Receipt"},
+    {ERR_FUNC(CMS_F_CMS_ENCRYPT), "CMS_encrypt"},
+    {ERR_FUNC(CMS_F_CMS_ENCRYPTEDCONTENT_INIT_BIO),
+     "cms_EncryptedContent_init_bio"},
+    {ERR_FUNC(CMS_F_CMS_ENCRYPTEDDATA_DECRYPT), "CMS_EncryptedData_decrypt"},
+    {ERR_FUNC(CMS_F_CMS_ENCRYPTEDDATA_ENCRYPT), "CMS_EncryptedData_encrypt"},
+    {ERR_FUNC(CMS_F_CMS_ENCRYPTEDDATA_SET1_KEY),
+     "CMS_EncryptedData_set1_key"},
+    {ERR_FUNC(CMS_F_CMS_ENVELOPEDDATA_CREATE), "CMS_EnvelopedData_create"},
+    {ERR_FUNC(CMS_F_CMS_ENVELOPEDDATA_INIT_BIO),
+     "cms_EnvelopedData_init_bio"},
+    {ERR_FUNC(CMS_F_CMS_ENVELOPED_DATA_INIT), "cms_enveloped_data_init"},
+    {ERR_FUNC(CMS_F_CMS_ENV_ASN1_CTRL), "cms_env_asn1_ctrl"},
+    {ERR_FUNC(CMS_F_CMS_FINAL), "CMS_final"},
+    {ERR_FUNC(CMS_F_CMS_GET0_CERTIFICATE_CHOICES),
+     "cms_get0_certificate_choices"},
+    {ERR_FUNC(CMS_F_CMS_GET0_CONTENT), "CMS_get0_content"},
+    {ERR_FUNC(CMS_F_CMS_GET0_ECONTENT_TYPE), "cms_get0_econtent_type"},
+    {ERR_FUNC(CMS_F_CMS_GET0_ENVELOPED), "cms_get0_enveloped"},
+    {ERR_FUNC(CMS_F_CMS_GET0_REVOCATION_CHOICES),
+     "cms_get0_revocation_choices"},
+    {ERR_FUNC(CMS_F_CMS_GET0_SIGNED), "cms_get0_signed"},
+    {ERR_FUNC(CMS_F_CMS_MSGSIGDIGEST_ADD1), "cms_msgSigDigest_add1"},
+    {ERR_FUNC(CMS_F_CMS_RECEIPTREQUEST_CREATE0),
+     "CMS_ReceiptRequest_create0"},
+    {ERR_FUNC(CMS_F_CMS_RECEIPT_VERIFY), "cms_Receipt_verify"},
+    {ERR_FUNC(CMS_F_CMS_RECIPIENTINFO_DECRYPT), "CMS_RecipientInfo_decrypt"},
+    {ERR_FUNC(CMS_F_CMS_RECIPIENTINFO_ENCRYPT), "CMS_RecipientInfo_encrypt"},
+    {ERR_FUNC(CMS_F_CMS_RECIPIENTINFO_KARI_ENCRYPT),
+     "cms_RecipientInfo_kari_encrypt"},
+    {ERR_FUNC(CMS_F_CMS_RECIPIENTINFO_KARI_GET0_ALG),
+     "CMS_RecipientInfo_kari_get0_alg"},
+    {ERR_FUNC(CMS_F_CMS_RECIPIENTINFO_KARI_GET0_ORIG_ID),
+     "CMS_RecipientInfo_kari_get0_orig_id"},
+    {ERR_FUNC(CMS_F_CMS_RECIPIENTINFO_KARI_GET0_REKS),
+     "CMS_RecipientInfo_kari_get0_reks"},
+    {ERR_FUNC(CMS_F_CMS_RECIPIENTINFO_KARI_ORIG_ID_CMP),
+     "CMS_RecipientInfo_kari_orig_id_cmp"},
+    {ERR_FUNC(CMS_F_CMS_RECIPIENTINFO_KEKRI_DECRYPT),
+     "cms_RecipientInfo_kekri_decrypt"},
+    {ERR_FUNC(CMS_F_CMS_RECIPIENTINFO_KEKRI_ENCRYPT),
+     "cms_RecipientInfo_kekri_encrypt"},
+    {ERR_FUNC(CMS_F_CMS_RECIPIENTINFO_KEKRI_GET0_ID),
+     "CMS_RecipientInfo_kekri_get0_id"},
+    {ERR_FUNC(CMS_F_CMS_RECIPIENTINFO_KEKRI_ID_CMP),
+     "CMS_RecipientInfo_kekri_id_cmp"},
+    {ERR_FUNC(CMS_F_CMS_RECIPIENTINFO_KTRI_CERT_CMP),
+     "CMS_RecipientInfo_ktri_cert_cmp"},
+    {ERR_FUNC(CMS_F_CMS_RECIPIENTINFO_KTRI_DECRYPT),
+     "cms_RecipientInfo_ktri_decrypt"},
+    {ERR_FUNC(CMS_F_CMS_RECIPIENTINFO_KTRI_ENCRYPT),
+     "cms_RecipientInfo_ktri_encrypt"},
+    {ERR_FUNC(CMS_F_CMS_RECIPIENTINFO_KTRI_GET0_ALGS),
+     "CMS_RecipientInfo_ktri_get0_algs"},
+    {ERR_FUNC(CMS_F_CMS_RECIPIENTINFO_KTRI_GET0_SIGNER_ID),
+     "CMS_RecipientInfo_ktri_get0_signer_id"},
+    {ERR_FUNC(CMS_F_CMS_RECIPIENTINFO_PWRI_CRYPT),
+     "cms_RecipientInfo_pwri_crypt"},
+    {ERR_FUNC(CMS_F_CMS_RECIPIENTINFO_SET0_KEY),
+     "CMS_RecipientInfo_set0_key"},
+    {ERR_FUNC(CMS_F_CMS_RECIPIENTINFO_SET0_PASSWORD),
+     "CMS_RecipientInfo_set0_password"},
+    {ERR_FUNC(CMS_F_CMS_RECIPIENTINFO_SET0_PKEY),
+     "CMS_RecipientInfo_set0_pkey"},
+    {ERR_FUNC(CMS_F_CMS_SD_ASN1_CTRL), "cms_sd_asn1_ctrl"},
+    {ERR_FUNC(CMS_F_CMS_SET1_IAS), "cms_set1_ias"},
+    {ERR_FUNC(CMS_F_CMS_SET1_KEYID), "cms_set1_keyid"},
+    {ERR_FUNC(CMS_F_CMS_SET1_SIGNERIDENTIFIER), "cms_set1_SignerIdentifier"},
+    {ERR_FUNC(CMS_F_CMS_SET_DETACHED), "CMS_set_detached"},
+    {ERR_FUNC(CMS_F_CMS_SIGN), "CMS_sign"},
+    {ERR_FUNC(CMS_F_CMS_SIGNED_DATA_INIT), "cms_signed_data_init"},
+    {ERR_FUNC(CMS_F_CMS_SIGNERINFO_CONTENT_SIGN),
+     "cms_SignerInfo_content_sign"},
+    {ERR_FUNC(CMS_F_CMS_SIGNERINFO_SIGN), "CMS_SignerInfo_sign"},
+    {ERR_FUNC(CMS_F_CMS_SIGNERINFO_VERIFY), "CMS_SignerInfo_verify"},
+    {ERR_FUNC(CMS_F_CMS_SIGNERINFO_VERIFY_CERT),
+     "cms_signerinfo_verify_cert"},
+    {ERR_FUNC(CMS_F_CMS_SIGNERINFO_VERIFY_CONTENT),
+     "CMS_SignerInfo_verify_content"},
+    {ERR_FUNC(CMS_F_CMS_SIGN_RECEIPT), "CMS_sign_receipt"},
+    {ERR_FUNC(CMS_F_CMS_STREAM), "CMS_stream"},
+    {ERR_FUNC(CMS_F_CMS_UNCOMPRESS), "CMS_uncompress"},
+    {ERR_FUNC(CMS_F_CMS_VERIFY), "CMS_verify"},
+    {0, NULL}
+};
+
+static ERR_STRING_DATA CMS_str_reasons[] = {
+    {ERR_REASON(CMS_R_ADD_SIGNER_ERROR), "add signer error"},
+    {ERR_REASON(CMS_R_CERTIFICATE_ALREADY_PRESENT),
+     "certificate already present"},
+    {ERR_REASON(CMS_R_CERTIFICATE_HAS_NO_KEYID), "certificate has no keyid"},
+    {ERR_REASON(CMS_R_CERTIFICATE_VERIFY_ERROR), "certificate verify error"},
+    {ERR_REASON(CMS_R_CIPHER_INITIALISATION_ERROR),
+     "cipher initialisation error"},
+    {ERR_REASON(CMS_R_CIPHER_PARAMETER_INITIALISATION_ERROR),
+     "cipher parameter initialisation error"},
+    {ERR_REASON(CMS_R_CMS_DATAFINAL_ERROR), "cms datafinal error"},
+    {ERR_REASON(CMS_R_CMS_LIB), "cms lib"},
+    {ERR_REASON(CMS_R_CONTENTIDENTIFIER_MISMATCH),
+     "contentidentifier mismatch"},
+    {ERR_REASON(CMS_R_CONTENT_NOT_FOUND), "content not found"},
+    {ERR_REASON(CMS_R_CONTENT_TYPE_MISMATCH), "content type mismatch"},
+    {ERR_REASON(CMS_R_CONTENT_TYPE_NOT_COMPRESSED_DATA),
+     "content type not compressed data"},
+    {ERR_REASON(CMS_R_CONTENT_TYPE_NOT_ENVELOPED_DATA),
+     "content type not enveloped data"},
+    {ERR_REASON(CMS_R_CONTENT_TYPE_NOT_SIGNED_DATA),
+     "content type not signed data"},
+    {ERR_REASON(CMS_R_CONTENT_VERIFY_ERROR), "content verify error"},
+    {ERR_REASON(CMS_R_CTRL_ERROR), "ctrl error"},
+    {ERR_REASON(CMS_R_CTRL_FAILURE), "ctrl failure"},
+    {ERR_REASON(CMS_R_DECRYPT_ERROR), "decrypt error"},
+    {ERR_REASON(CMS_R_ERROR_GETTING_PUBLIC_KEY), "error getting public key"},
+    {ERR_REASON(CMS_R_ERROR_READING_MESSAGEDIGEST_ATTRIBUTE),
+     "error reading messagedigest attribute"},
+    {ERR_REASON(CMS_R_ERROR_SETTING_KEY), "error setting key"},
+    {ERR_REASON(CMS_R_ERROR_SETTING_RECIPIENTINFO),
+     "error setting recipientinfo"},
+    {ERR_REASON(CMS_R_INVALID_ENCRYPTED_KEY_LENGTH),
+     "invalid encrypted key length"},
+    {ERR_REASON(CMS_R_INVALID_KEY_ENCRYPTION_PARAMETER),
+     "invalid key encryption parameter"},
+    {ERR_REASON(CMS_R_INVALID_KEY_LENGTH), "invalid key length"},
+    {ERR_REASON(CMS_R_MD_BIO_INIT_ERROR), "md bio init error"},
+    {ERR_REASON(CMS_R_MESSAGEDIGEST_ATTRIBUTE_WRONG_LENGTH),
+     "messagedigest attribute wrong length"},
+    {ERR_REASON(CMS_R_MESSAGEDIGEST_WRONG_LENGTH),
+     "messagedigest wrong length"},
+    {ERR_REASON(CMS_R_MSGSIGDIGEST_ERROR), "msgsigdigest error"},
+    {ERR_REASON(CMS_R_MSGSIGDIGEST_VERIFICATION_FAILURE),
+     "msgsigdigest verification failure"},
+    {ERR_REASON(CMS_R_MSGSIGDIGEST_WRONG_LENGTH),
+     "msgsigdigest wrong length"},
+    {ERR_REASON(CMS_R_NEED_ONE_SIGNER), "need one signer"},
+    {ERR_REASON(CMS_R_NOT_A_SIGNED_RECEIPT), "not a signed receipt"},
+    {ERR_REASON(CMS_R_NOT_ENCRYPTED_DATA), "not encrypted data"},
+    {ERR_REASON(CMS_R_NOT_KEK), "not kek"},
+    {ERR_REASON(CMS_R_NOT_KEY_AGREEMENT), "not key agreement"},
+    {ERR_REASON(CMS_R_NOT_KEY_TRANSPORT), "not key transport"},
+    {ERR_REASON(CMS_R_NOT_PWRI), "not pwri"},
+    {ERR_REASON(CMS_R_NOT_SUPPORTED_FOR_THIS_KEY_TYPE),
+     "not supported for this key type"},
+    {ERR_REASON(CMS_R_NO_AVAILABLE_CIPHER), "no available cipher"},
+    {ERR_REASON(CMS_R_NO_CIPHER), "no cipher"},
+    {ERR_REASON(CMS_R_NO_CONTENT), "no content"},
+    {ERR_REASON(CMS_R_NO_CONTENT_TYPE), "no content type"},
+    {ERR_REASON(CMS_R_NO_DEFAULT_DIGEST), "no default digest"},
+    {ERR_REASON(CMS_R_NO_DIGEST_SET), "no digest set"},
+    {ERR_REASON(CMS_R_NO_KEY), "no key"},
+    {ERR_REASON(CMS_R_NO_KEY_OR_CERT), "no key or cert"},
+    {ERR_REASON(CMS_R_NO_MATCHING_DIGEST), "no matching digest"},
+    {ERR_REASON(CMS_R_NO_MATCHING_RECIPIENT), "no matching recipient"},
+    {ERR_REASON(CMS_R_NO_MATCHING_SIGNATURE), "no matching signature"},
+    {ERR_REASON(CMS_R_NO_MSGSIGDIGEST), "no msgsigdigest"},
+    {ERR_REASON(CMS_R_NO_PASSWORD), "no password"},
+    {ERR_REASON(CMS_R_NO_PRIVATE_KEY), "no private key"},
+    {ERR_REASON(CMS_R_NO_PUBLIC_KEY), "no public key"},
+    {ERR_REASON(CMS_R_NO_RECEIPT_REQUEST), "no receipt request"},
+    {ERR_REASON(CMS_R_NO_SIGNERS), "no signers"},
+    {ERR_REASON(CMS_R_PRIVATE_KEY_DOES_NOT_MATCH_CERTIFICATE),
+     "private key does not match certificate"},
+    {ERR_REASON(CMS_R_RECEIPT_DECODE_ERROR), "receipt decode error"},
+    {ERR_REASON(CMS_R_RECIPIENT_ERROR), "recipient error"},
+    {ERR_REASON(CMS_R_SIGNER_CERTIFICATE_NOT_FOUND),
+     "signer certificate not found"},
+    {ERR_REASON(CMS_R_SIGNFINAL_ERROR), "signfinal error"},
+    {ERR_REASON(CMS_R_SMIME_TEXT_ERROR), "smime text error"},
+    {ERR_REASON(CMS_R_STORE_INIT_ERROR), "store init error"},
+    {ERR_REASON(CMS_R_TYPE_NOT_COMPRESSED_DATA), "type not compressed data"},
+    {ERR_REASON(CMS_R_TYPE_NOT_DATA), "type not data"},
+    {ERR_REASON(CMS_R_TYPE_NOT_DIGESTED_DATA), "type not digested data"},
+    {ERR_REASON(CMS_R_TYPE_NOT_ENCRYPTED_DATA), "type not encrypted data"},
+    {ERR_REASON(CMS_R_TYPE_NOT_ENVELOPED_DATA), "type not enveloped data"},
+    {ERR_REASON(CMS_R_UNABLE_TO_FINALIZE_CONTEXT),
+     "unable to finalize context"},
+    {ERR_REASON(CMS_R_UNKNOWN_CIPHER), "unknown cipher"},
+    {ERR_REASON(CMS_R_UNKNOWN_DIGEST_ALGORIHM), "unknown digest algorihm"},
+    {ERR_REASON(CMS_R_UNKNOWN_ID), "unknown id"},
+    {ERR_REASON(CMS_R_UNSUPPORTED_COMPRESSION_ALGORITHM),
+     "unsupported compression algorithm"},
+    {ERR_REASON(CMS_R_UNSUPPORTED_CONTENT_TYPE), "unsupported content type"},
+    {ERR_REASON(CMS_R_UNSUPPORTED_KEK_ALGORITHM),
+     "unsupported kek algorithm"},
+    {ERR_REASON(CMS_R_UNSUPPORTED_KEY_ENCRYPTION_ALGORITHM),
+     "unsupported key encryption algorithm"},
+    {ERR_REASON(CMS_R_UNSUPPORTED_RECIPIENT_TYPE),
+     "unsupported recipient type"},
+    {ERR_REASON(CMS_R_UNSUPPORTED_RECPIENTINFO_TYPE),
+     "unsupported recpientinfo type"},
+    {ERR_REASON(CMS_R_UNSUPPORTED_TYPE), "unsupported type"},
+    {ERR_REASON(CMS_R_UNWRAP_ERROR), "unwrap error"},
+    {ERR_REASON(CMS_R_UNWRAP_FAILURE), "unwrap failure"},
+    {ERR_REASON(CMS_R_VERIFICATION_FAILURE), "verification failure"},
+    {ERR_REASON(CMS_R_WRAP_ERROR), "wrap error"},
+    {0, NULL}
+};
+
+#endif
+
+int ERR_load_CMS_strings(void)
+{
+#ifndef OPENSSL_NO_ERR
+
+    if (ERR_func_error_string(CMS_str_functs[0].error) == NULL) {
+        ERR_load_strings(0, CMS_str_functs);
+        ERR_load_strings(0, CMS_str_reasons);
+    }
+#endif
+    return 1;
+}
diff --git a/crypto/cms/cms_ess.c b/crypto/cms/cms_ess.c
new file mode 100644
index 000000000..4780231c2
--- /dev/null
+++ b/crypto/cms/cms_ess.c
@@ -0,0 +1,337 @@
+/*
+ * Copyright 2008-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "internal/cryptlib.h"
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include "cms_lcl.h"
+
+IMPLEMENT_ASN1_FUNCTIONS(CMS_ReceiptRequest)
+
+/* ESS services: for now just Signed Receipt related */
+
+int CMS_get1_ReceiptRequest(CMS_SignerInfo *si, CMS_ReceiptRequest **prr)
+{
+    ASN1_STRING *str;
+    CMS_ReceiptRequest *rr = NULL;
+    if (prr)
+        *prr = NULL;
+    str = CMS_signed_get0_data_by_OBJ(si,
+                                      OBJ_nid2obj
+                                      (NID_id_smime_aa_receiptRequest), -3,
+                                      V_ASN1_SEQUENCE);
+    if (!str)
+        return 0;
+
+    rr = ASN1_item_unpack(str, ASN1_ITEM_rptr(CMS_ReceiptRequest));
+    if (!rr)
+        return -1;
+    if (prr)
+        *prr = rr;
+    else
+        CMS_ReceiptRequest_free(rr);
+    return 1;
+}
+
+CMS_ReceiptRequest *CMS_ReceiptRequest_create0(unsigned char *id, int idlen,
+                                               int allorfirst,
+                                               STACK_OF(GENERAL_NAMES)
+                                               *receiptList, STACK_OF(GENERAL_NAMES)
+                                               *receiptsTo)
+{
+    CMS_ReceiptRequest *rr = NULL;
+
+    rr = CMS_ReceiptRequest_new();
+    if (rr == NULL)
+        goto merr;
+    if (id)
+        ASN1_STRING_set0(rr->signedContentIdentifier, id, idlen);
+    else {
+        if (!ASN1_STRING_set(rr->signedContentIdentifier, NULL, 32))
+            goto merr;
+        if (RAND_bytes(rr->signedContentIdentifier->data, 32) <= 0)
+            goto err;
+    }
+
+    sk_GENERAL_NAMES_pop_free(rr->receiptsTo, GENERAL_NAMES_free);
+    rr->receiptsTo = receiptsTo;
+
+    if (receiptList) {
+        rr->receiptsFrom->type = 1;
+        rr->receiptsFrom->d.receiptList = receiptList;
+    } else {
+        rr->receiptsFrom->type = 0;
+        rr->receiptsFrom->d.allOrFirstTier = allorfirst;
+    }
+
+    return rr;
+
+ merr:
+    CMSerr(CMS_F_CMS_RECEIPTREQUEST_CREATE0, ERR_R_MALLOC_FAILURE);
+
+ err:
+    CMS_ReceiptRequest_free(rr);
+    return NULL;
+
+}
+
+int CMS_add1_ReceiptRequest(CMS_SignerInfo *si, CMS_ReceiptRequest *rr)
+{
+    unsigned char *rrder = NULL;
+    int rrderlen, r = 0;
+
+    rrderlen = i2d_CMS_ReceiptRequest(rr, &rrder);
+    if (rrderlen < 0)
+        goto merr;
+
+    if (!CMS_signed_add1_attr_by_NID(si, NID_id_smime_aa_receiptRequest,
+                                     V_ASN1_SEQUENCE, rrder, rrderlen))
+        goto merr;
+
+    r = 1;
+
+ merr:
+    if (!r)
+        CMSerr(CMS_F_CMS_ADD1_RECEIPTREQUEST, ERR_R_MALLOC_FAILURE);
+
+    OPENSSL_free(rrder);
+
+    return r;
+
+}
+
+void CMS_ReceiptRequest_get0_values(CMS_ReceiptRequest *rr,
+                                    ASN1_STRING **pcid,
+                                    int *pallorfirst,
+                                    STACK_OF(GENERAL_NAMES) **plist,
+                                    STACK_OF(GENERAL_NAMES) **prto)
+{
+    if (pcid)
+        *pcid = rr->signedContentIdentifier;
+    if (rr->receiptsFrom->type == 0) {
+        if (pallorfirst)
+            *pallorfirst = (int)rr->receiptsFrom->d.allOrFirstTier;
+        if (plist)
+            *plist = NULL;
+    } else {
+        if (pallorfirst)
+            *pallorfirst = -1;
+        if (plist)
+            *plist = rr->receiptsFrom->d.receiptList;
+    }
+    if (prto)
+        *prto = rr->receiptsTo;
+}
+
+/* Digest a SignerInfo structure for msgSigDigest attribute processing */
+
+static int cms_msgSigDigest(CMS_SignerInfo *si,
+                            unsigned char *dig, unsigned int *diglen)
+{
+    const EVP_MD *md;
+    md = EVP_get_digestbyobj(si->digestAlgorithm->algorithm);
+    if (md == NULL)
+        return 0;
+    if (!ASN1_item_digest(ASN1_ITEM_rptr(CMS_Attributes_Verify), md,
+                          si->signedAttrs, dig, diglen))
+        return 0;
+    return 1;
+}
+
+/* Add a msgSigDigest attribute to a SignerInfo */
+
+int cms_msgSigDigest_add1(CMS_SignerInfo *dest, CMS_SignerInfo *src)
+{
+    unsigned char dig[EVP_MAX_MD_SIZE];
+    unsigned int diglen;
+    if (!cms_msgSigDigest(src, dig, &diglen)) {
+        CMSerr(CMS_F_CMS_MSGSIGDIGEST_ADD1, CMS_R_MSGSIGDIGEST_ERROR);
+        return 0;
+    }
+    if (!CMS_signed_add1_attr_by_NID(dest, NID_id_smime_aa_msgSigDigest,
+                                     V_ASN1_OCTET_STRING, dig, diglen)) {
+        CMSerr(CMS_F_CMS_MSGSIGDIGEST_ADD1, ERR_R_MALLOC_FAILURE);
+        return 0;
+    }
+    return 1;
+}
+
+/* Verify signed receipt after it has already passed normal CMS verify */
+
+int cms_Receipt_verify(CMS_ContentInfo *cms, CMS_ContentInfo *req_cms)
+{
+    int r = 0, i;
+    CMS_ReceiptRequest *rr = NULL;
+    CMS_Receipt *rct = NULL;
+    STACK_OF(CMS_SignerInfo) *sis, *osis;
+    CMS_SignerInfo *si, *osi = NULL;
+    ASN1_OCTET_STRING *msig, **pcont;
+    ASN1_OBJECT *octype;
+    unsigned char dig[EVP_MAX_MD_SIZE];
+    unsigned int diglen;
+
+    /* Get SignerInfos, also checks SignedData content type */
+    osis = CMS_get0_SignerInfos(req_cms);
+    sis = CMS_get0_SignerInfos(cms);
+    if (!osis || !sis)
+        goto err;
+
+    if (sk_CMS_SignerInfo_num(sis) != 1) {
+        CMSerr(CMS_F_CMS_RECEIPT_VERIFY, CMS_R_NEED_ONE_SIGNER);
+        goto err;
+    }
+
+    /* Check receipt content type */
+    if (OBJ_obj2nid(CMS_get0_eContentType(cms)) != NID_id_smime_ct_receipt) {
+        CMSerr(CMS_F_CMS_RECEIPT_VERIFY, CMS_R_NOT_A_SIGNED_RECEIPT);
+        goto err;
+    }
+
+    /* Extract and decode receipt content */
+    pcont = CMS_get0_content(cms);
+    if (!pcont || !*pcont) {
+        CMSerr(CMS_F_CMS_RECEIPT_VERIFY, CMS_R_NO_CONTENT);
+        goto err;
+    }
+
+    rct = ASN1_item_unpack(*pcont, ASN1_ITEM_rptr(CMS_Receipt));
+
+    if (!rct) {
+        CMSerr(CMS_F_CMS_RECEIPT_VERIFY, CMS_R_RECEIPT_DECODE_ERROR);
+        goto err;
+    }
+
+    /* Locate original request */
+
+    for (i = 0; i < sk_CMS_SignerInfo_num(osis); i++) {
+        osi = sk_CMS_SignerInfo_value(osis, i);
+        if (!ASN1_STRING_cmp(osi->signature, rct->originatorSignatureValue))
+            break;
+    }
+
+    if (i == sk_CMS_SignerInfo_num(osis)) {
+        CMSerr(CMS_F_CMS_RECEIPT_VERIFY, CMS_R_NO_MATCHING_SIGNATURE);
+        goto err;
+    }
+
+    si = sk_CMS_SignerInfo_value(sis, 0);
+
+    /* Get msgSigDigest value and compare */
+
+    msig = CMS_signed_get0_data_by_OBJ(si,
+                                       OBJ_nid2obj
+                                       (NID_id_smime_aa_msgSigDigest), -3,
+                                       V_ASN1_OCTET_STRING);
+
+    if (!msig) {
+        CMSerr(CMS_F_CMS_RECEIPT_VERIFY, CMS_R_NO_MSGSIGDIGEST);
+        goto err;
+    }
+
+    if (!cms_msgSigDigest(osi, dig, &diglen)) {
+        CMSerr(CMS_F_CMS_RECEIPT_VERIFY, CMS_R_MSGSIGDIGEST_ERROR);
+        goto err;
+    }
+
+    if (diglen != (unsigned int)msig->length) {
+        CMSerr(CMS_F_CMS_RECEIPT_VERIFY, CMS_R_MSGSIGDIGEST_WRONG_LENGTH);
+        goto err;
+    }
+
+    if (memcmp(dig, msig->data, diglen)) {
+        CMSerr(CMS_F_CMS_RECEIPT_VERIFY,
+               CMS_R_MSGSIGDIGEST_VERIFICATION_FAILURE);
+        goto err;
+    }
+
+    /* Compare content types */
+
+    octype = CMS_signed_get0_data_by_OBJ(osi,
+                                         OBJ_nid2obj(NID_pkcs9_contentType),
+                                         -3, V_ASN1_OBJECT);
+    if (!octype) {
+        CMSerr(CMS_F_CMS_RECEIPT_VERIFY, CMS_R_NO_CONTENT_TYPE);
+        goto err;
+    }
+
+    /* Compare details in receipt request */
+
+    if (OBJ_cmp(octype, rct->contentType)) {
+        CMSerr(CMS_F_CMS_RECEIPT_VERIFY, CMS_R_CONTENT_TYPE_MISMATCH);
+        goto err;
+    }
+
+    /* Get original receipt request details */
+
+    if (CMS_get1_ReceiptRequest(osi, &rr) <= 0) {
+        CMSerr(CMS_F_CMS_RECEIPT_VERIFY, CMS_R_NO_RECEIPT_REQUEST);
+        goto err;
+    }
+
+    if (ASN1_STRING_cmp(rr->signedContentIdentifier,
+                        rct->signedContentIdentifier)) {
+        CMSerr(CMS_F_CMS_RECEIPT_VERIFY, CMS_R_CONTENTIDENTIFIER_MISMATCH);
+        goto err;
+    }
+
+    r = 1;
+
+ err:
+    CMS_ReceiptRequest_free(rr);
+    M_ASN1_free_of(rct, CMS_Receipt);
+    return r;
+
+}
+
+/*
+ * Encode a Receipt into an OCTET STRING read for including into content of a
+ * SignedData ContentInfo.
+ */
+
+ASN1_OCTET_STRING *cms_encode_Receipt(CMS_SignerInfo *si)
+{
+    CMS_Receipt rct;
+    CMS_ReceiptRequest *rr = NULL;
+    ASN1_OBJECT *ctype;
+    ASN1_OCTET_STRING *os = NULL;
+
+    /* Get original receipt request */
+
+    /* Get original receipt request details */
+
+    if (CMS_get1_ReceiptRequest(si, &rr) <= 0) {
+        CMSerr(CMS_F_CMS_ENCODE_RECEIPT, CMS_R_NO_RECEIPT_REQUEST);
+        goto err;
+    }
+
+    /* Get original content type */
+
+    ctype = CMS_signed_get0_data_by_OBJ(si,
+                                        OBJ_nid2obj(NID_pkcs9_contentType),
+                                        -3, V_ASN1_OBJECT);
+    if (!ctype) {
+        CMSerr(CMS_F_CMS_ENCODE_RECEIPT, CMS_R_NO_CONTENT_TYPE);
+        goto err;
+    }
+
+    rct.version = 1;
+    rct.contentType = ctype;
+    rct.signedContentIdentifier = rr->signedContentIdentifier;
+    rct.originatorSignatureValue = si->signature;
+
+    os = ASN1_item_pack(&rct, ASN1_ITEM_rptr(CMS_Receipt), NULL);
+
+ err:
+    CMS_ReceiptRequest_free(rr);
+    return os;
+}
diff --git a/crypto/cms/cms_io.c b/crypto/cms/cms_io.c
new file mode 100644
index 000000000..d18f980a9
--- /dev/null
+++ b/crypto/cms/cms_io.c
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2008-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include "cms_lcl.h"
+
+int CMS_stream(unsigned char ***boundary, CMS_ContentInfo *cms)
+{
+    ASN1_OCTET_STRING **pos;
+    pos = CMS_get0_content(cms);
+    if (pos == NULL)
+        return 0;
+    if (*pos == NULL)
+        *pos = ASN1_OCTET_STRING_new();
+    if (*pos != NULL) {
+        (*pos)->flags |= ASN1_STRING_FLAG_NDEF;
+        (*pos)->flags &= ~ASN1_STRING_FLAG_CONT;
+        *boundary = &(*pos)->data;
+        return 1;
+    }
+    CMSerr(CMS_F_CMS_STREAM, ERR_R_MALLOC_FAILURE);
+    return 0;
+}
+
+CMS_ContentInfo *d2i_CMS_bio(BIO *bp, CMS_ContentInfo **cms)
+{
+    return ASN1_item_d2i_bio(ASN1_ITEM_rptr(CMS_ContentInfo), bp, cms);
+}
+
+int i2d_CMS_bio(BIO *bp, CMS_ContentInfo *cms)
+{
+    return ASN1_item_i2d_bio(ASN1_ITEM_rptr(CMS_ContentInfo), bp, cms);
+}
+
+IMPLEMENT_PEM_rw_const(CMS, CMS_ContentInfo, PEM_STRING_CMS, CMS_ContentInfo)
+
+BIO *BIO_new_CMS(BIO *out, CMS_ContentInfo *cms)
+{
+    return BIO_new_NDEF(out, (ASN1_VALUE *)cms,
+                        ASN1_ITEM_rptr(CMS_ContentInfo));
+}
+
+/* CMS wrappers round generalised stream and MIME routines */
+
+int i2d_CMS_bio_stream(BIO *out, CMS_ContentInfo *cms, BIO *in, int flags)
+{
+    return i2d_ASN1_bio_stream(out, (ASN1_VALUE *)cms, in, flags,
+                               ASN1_ITEM_rptr(CMS_ContentInfo));
+}
+
+int PEM_write_bio_CMS_stream(BIO *out, CMS_ContentInfo *cms, BIO *in,
+                             int flags)
+{
+    return PEM_write_bio_ASN1_stream(out, (ASN1_VALUE *)cms, in, flags,
+                                     "CMS", ASN1_ITEM_rptr(CMS_ContentInfo));
+}
+
+int SMIME_write_CMS(BIO *bio, CMS_ContentInfo *cms, BIO *data, int flags)
+{
+    STACK_OF(X509_ALGOR) *mdalgs;
+    int ctype_nid = OBJ_obj2nid(cms->contentType);
+    int econt_nid = OBJ_obj2nid(CMS_get0_eContentType(cms));
+    if (ctype_nid == NID_pkcs7_signed)
+        mdalgs = cms->d.signedData->digestAlgorithms;
+    else
+        mdalgs = NULL;
+
+    return SMIME_write_ASN1(bio, (ASN1_VALUE *)cms, data, flags,
+                            ctype_nid, econt_nid, mdalgs,
+                            ASN1_ITEM_rptr(CMS_ContentInfo));
+}
+
+CMS_ContentInfo *SMIME_read_CMS(BIO *bio, BIO **bcont)
+{
+    return (CMS_ContentInfo *)SMIME_read_ASN1(bio, bcont,
+                                              ASN1_ITEM_rptr
+                                              (CMS_ContentInfo));
+}
diff --git a/crypto/cms/cms_kari.c b/crypto/cms/cms_kari.c
new file mode 100644
index 000000000..63999eb49
--- /dev/null
+++ b/crypto/cms/cms_kari.c
@@ -0,0 +1,428 @@
+/*
+ * Copyright 2013-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "internal/cryptlib.h"
+#include 
+#include 
+#include 
+#include 
+#include 
+#ifndef OPENSSL_NO_AES
+# include 
+#endif
+#include "cms_lcl.h"
+#include "internal/asn1_int.h"
+
+/* Key Agreement Recipient Info (KARI) routines */
+
+int CMS_RecipientInfo_kari_get0_alg(CMS_RecipientInfo *ri,
+                                    X509_ALGOR **palg,
+                                    ASN1_OCTET_STRING **pukm)
+{
+    if (ri->type != CMS_RECIPINFO_AGREE) {
+        CMSerr(CMS_F_CMS_RECIPIENTINFO_KARI_GET0_ALG,
+               CMS_R_NOT_KEY_AGREEMENT);
+        return 0;
+    }
+    if (palg)
+        *palg = ri->d.kari->keyEncryptionAlgorithm;
+    if (pukm)
+        *pukm = ri->d.kari->ukm;
+    return 1;
+}
+
+/* Retrieve recipient encrypted keys from a kari */
+
+STACK_OF(CMS_RecipientEncryptedKey)
+*CMS_RecipientInfo_kari_get0_reks(CMS_RecipientInfo *ri)
+{
+    if (ri->type != CMS_RECIPINFO_AGREE) {
+        CMSerr(CMS_F_CMS_RECIPIENTINFO_KARI_GET0_REKS,
+               CMS_R_NOT_KEY_AGREEMENT);
+        return NULL;
+    }
+    return ri->d.kari->recipientEncryptedKeys;
+}
+
+int CMS_RecipientInfo_kari_get0_orig_id(CMS_RecipientInfo *ri,
+                                        X509_ALGOR **pubalg,
+                                        ASN1_BIT_STRING **pubkey,
+                                        ASN1_OCTET_STRING **keyid,
+                                        X509_NAME **issuer,
+                                        ASN1_INTEGER **sno)
+{
+    CMS_OriginatorIdentifierOrKey *oik;
+    if (ri->type != CMS_RECIPINFO_AGREE) {
+        CMSerr(CMS_F_CMS_RECIPIENTINFO_KARI_GET0_ORIG_ID,
+               CMS_R_NOT_KEY_AGREEMENT);
+        return 0;
+    }
+    oik = ri->d.kari->originator;
+    if (issuer)
+        *issuer = NULL;
+    if (sno)
+        *sno = NULL;
+    if (keyid)
+        *keyid = NULL;
+    if (pubalg)
+        *pubalg = NULL;
+    if (pubkey)
+        *pubkey = NULL;
+    if (oik->type == CMS_OIK_ISSUER_SERIAL) {
+        if (issuer)
+            *issuer = oik->d.issuerAndSerialNumber->issuer;
+        if (sno)
+            *sno = oik->d.issuerAndSerialNumber->serialNumber;
+    } else if (oik->type == CMS_OIK_KEYIDENTIFIER) {
+        if (keyid)
+            *keyid = oik->d.subjectKeyIdentifier;
+    } else if (oik->type == CMS_OIK_PUBKEY) {
+        if (pubalg)
+            *pubalg = oik->d.originatorKey->algorithm;
+        if (pubkey)
+            *pubkey = oik->d.originatorKey->publicKey;
+    } else
+        return 0;
+    return 1;
+}
+
+int CMS_RecipientInfo_kari_orig_id_cmp(CMS_RecipientInfo *ri, X509 *cert)
+{
+    CMS_OriginatorIdentifierOrKey *oik;
+    if (ri->type != CMS_RECIPINFO_AGREE) {
+        CMSerr(CMS_F_CMS_RECIPIENTINFO_KARI_ORIG_ID_CMP,
+               CMS_R_NOT_KEY_AGREEMENT);
+        return -2;
+    }
+    oik = ri->d.kari->originator;
+    if (oik->type == CMS_OIK_ISSUER_SERIAL)
+        return cms_ias_cert_cmp(oik->d.issuerAndSerialNumber, cert);
+    else if (oik->type == CMS_OIK_KEYIDENTIFIER)
+        return cms_keyid_cert_cmp(oik->d.subjectKeyIdentifier, cert);
+    return -1;
+}
+
+int CMS_RecipientEncryptedKey_get0_id(CMS_RecipientEncryptedKey *rek,
+                                      ASN1_OCTET_STRING **keyid,
+                                      ASN1_GENERALIZEDTIME **tm,
+                                      CMS_OtherKeyAttribute **other,
+                                      X509_NAME **issuer, ASN1_INTEGER **sno)
+{
+    CMS_KeyAgreeRecipientIdentifier *rid = rek->rid;
+    if (rid->type == CMS_REK_ISSUER_SERIAL) {
+        if (issuer)
+            *issuer = rid->d.issuerAndSerialNumber->issuer;
+        if (sno)
+            *sno = rid->d.issuerAndSerialNumber->serialNumber;
+        if (keyid)
+            *keyid = NULL;
+        if (tm)
+            *tm = NULL;
+        if (other)
+            *other = NULL;
+    } else if (rid->type == CMS_REK_KEYIDENTIFIER) {
+        if (keyid)
+            *keyid = rid->d.rKeyId->subjectKeyIdentifier;
+        if (tm)
+            *tm = rid->d.rKeyId->date;
+        if (other)
+            *other = rid->d.rKeyId->other;
+        if (issuer)
+            *issuer = NULL;
+        if (sno)
+            *sno = NULL;
+    } else
+        return 0;
+    return 1;
+}
+
+int CMS_RecipientEncryptedKey_cert_cmp(CMS_RecipientEncryptedKey *rek,
+                                       X509 *cert)
+{
+    CMS_KeyAgreeRecipientIdentifier *rid = rek->rid;
+    if (rid->type == CMS_REK_ISSUER_SERIAL)
+        return cms_ias_cert_cmp(rid->d.issuerAndSerialNumber, cert);
+    else if (rid->type == CMS_REK_KEYIDENTIFIER)
+        return cms_keyid_cert_cmp(rid->d.rKeyId->subjectKeyIdentifier, cert);
+    else
+        return -1;
+}
+
+int CMS_RecipientInfo_kari_set0_pkey(CMS_RecipientInfo *ri, EVP_PKEY *pk)
+{
+    EVP_PKEY_CTX *pctx;
+    CMS_KeyAgreeRecipientInfo *kari = ri->d.kari;
+
+    EVP_PKEY_CTX_free(kari->pctx);
+    kari->pctx = NULL;
+    if (!pk)
+        return 1;
+    pctx = EVP_PKEY_CTX_new(pk, NULL);
+    if (!pctx || !EVP_PKEY_derive_init(pctx))
+        goto err;
+    kari->pctx = pctx;
+    return 1;
+ err:
+    EVP_PKEY_CTX_free(pctx);
+    return 0;
+}
+
+EVP_CIPHER_CTX *CMS_RecipientInfo_kari_get0_ctx(CMS_RecipientInfo *ri)
+{
+    if (ri->type == CMS_RECIPINFO_AGREE)
+        return ri->d.kari->ctx;
+    return NULL;
+}
+
+/*
+ * Derive KEK and decrypt/encrypt with it to produce either the original CEK
+ * or the encrypted CEK.
+ */
+
+static int cms_kek_cipher(unsigned char **pout, size_t *poutlen,
+                          const unsigned char *in, size_t inlen,
+                          CMS_KeyAgreeRecipientInfo *kari, int enc)
+{
+    /* Key encryption key */
+    unsigned char kek[EVP_MAX_KEY_LENGTH];
+    size_t keklen;
+    int rv = 0;
+    unsigned char *out = NULL;
+    int outlen;
+    keklen = EVP_CIPHER_CTX_key_length(kari->ctx);
+    if (keklen > EVP_MAX_KEY_LENGTH)
+        return 0;
+    /* Derive KEK */
+    if (EVP_PKEY_derive(kari->pctx, kek, &keklen) <= 0)
+        goto err;
+    /* Set KEK in context */
+    if (!EVP_CipherInit_ex(kari->ctx, NULL, NULL, kek, NULL, enc))
+        goto err;
+    /* obtain output length of ciphered key */
+    if (!EVP_CipherUpdate(kari->ctx, NULL, &outlen, in, inlen))
+        goto err;
+    out = OPENSSL_malloc(outlen);
+    if (out == NULL)
+        goto err;
+    if (!EVP_CipherUpdate(kari->ctx, out, &outlen, in, inlen))
+        goto err;
+    *pout = out;
+    *poutlen = (size_t)outlen;
+    rv = 1;
+
+ err:
+    OPENSSL_cleanse(kek, keklen);
+    if (!rv)
+        OPENSSL_free(out);
+    EVP_CIPHER_CTX_reset(kari->ctx);
+    /* FIXME: WHY IS kari->pctx freed here?  /RL */
+    EVP_PKEY_CTX_free(kari->pctx);
+    kari->pctx = NULL;
+    return rv;
+}
+
+int CMS_RecipientInfo_kari_decrypt(CMS_ContentInfo *cms,
+                                   CMS_RecipientInfo *ri,
+                                   CMS_RecipientEncryptedKey *rek)
+{
+    int rv = 0;
+    unsigned char *enckey = NULL, *cek = NULL;
+    size_t enckeylen;
+    size_t ceklen;
+    CMS_EncryptedContentInfo *ec;
+    enckeylen = rek->encryptedKey->length;
+    enckey = rek->encryptedKey->data;
+    /* Setup all parameters to derive KEK */
+    if (!cms_env_asn1_ctrl(ri, 1))
+        goto err;
+    /* Attempt to decrypt CEK */
+    if (!cms_kek_cipher(&cek, &ceklen, enckey, enckeylen, ri->d.kari, 0))
+        goto err;
+    ec = cms->d.envelopedData->encryptedContentInfo;
+    OPENSSL_clear_free(ec->key, ec->keylen);
+    ec->key = cek;
+    ec->keylen = ceklen;
+    cek = NULL;
+    rv = 1;
+ err:
+    OPENSSL_free(cek);
+    return rv;
+}
+
+/* Create ephemeral key and initialise context based on it */
+static int cms_kari_create_ephemeral_key(CMS_KeyAgreeRecipientInfo *kari,
+                                         EVP_PKEY *pk)
+{
+    EVP_PKEY_CTX *pctx = NULL;
+    EVP_PKEY *ekey = NULL;
+    int rv = 0;
+    pctx = EVP_PKEY_CTX_new(pk, NULL);
+    if (!pctx)
+        goto err;
+    if (EVP_PKEY_keygen_init(pctx) <= 0)
+        goto err;
+    if (EVP_PKEY_keygen(pctx, &ekey) <= 0)
+        goto err;
+    EVP_PKEY_CTX_free(pctx);
+    pctx = EVP_PKEY_CTX_new(ekey, NULL);
+    if (!pctx)
+        goto err;
+    if (EVP_PKEY_derive_init(pctx) <= 0)
+        goto err;
+    kari->pctx = pctx;
+    rv = 1;
+ err:
+    if (!rv)
+        EVP_PKEY_CTX_free(pctx);
+    EVP_PKEY_free(ekey);
+    return rv;
+}
+
+/* Initialise a ktri based on passed certificate and key */
+
+int cms_RecipientInfo_kari_init(CMS_RecipientInfo *ri, X509 *recip,
+                                EVP_PKEY *pk, unsigned int flags)
+{
+    CMS_KeyAgreeRecipientInfo *kari;
+    CMS_RecipientEncryptedKey *rek = NULL;
+
+    ri->d.kari = M_ASN1_new_of(CMS_KeyAgreeRecipientInfo);
+    if (!ri->d.kari)
+        return 0;
+    ri->type = CMS_RECIPINFO_AGREE;
+
+    kari = ri->d.kari;
+    kari->version = 3;
+
+    rek = M_ASN1_new_of(CMS_RecipientEncryptedKey);
+    if (!sk_CMS_RecipientEncryptedKey_push(kari->recipientEncryptedKeys, rek)) {
+        M_ASN1_free_of(rek, CMS_RecipientEncryptedKey);
+        return 0;
+    }
+
+    if (flags & CMS_USE_KEYID) {
+        rek->rid->type = CMS_REK_KEYIDENTIFIER;
+        rek->rid->d.rKeyId = M_ASN1_new_of(CMS_RecipientKeyIdentifier);
+        if (rek->rid->d.rKeyId == NULL)
+            return 0;
+        if (!cms_set1_keyid(&rek->rid->d.rKeyId->subjectKeyIdentifier, recip))
+            return 0;
+    } else {
+        rek->rid->type = CMS_REK_ISSUER_SERIAL;
+        if (!cms_set1_ias(&rek->rid->d.issuerAndSerialNumber, recip))
+            return 0;
+    }
+
+    /* Create ephemeral key */
+    if (!cms_kari_create_ephemeral_key(kari, pk))
+        return 0;
+
+    EVP_PKEY_up_ref(pk);
+    rek->pkey = pk;
+    return 1;
+}
+
+static int cms_wrap_init(CMS_KeyAgreeRecipientInfo *kari,
+                         const EVP_CIPHER *cipher)
+{
+    EVP_CIPHER_CTX *ctx = kari->ctx;
+    const EVP_CIPHER *kekcipher;
+#ifndef OPENSSL_NO_AES
+    int keylen = EVP_CIPHER_key_length(cipher);
+#endif
+    /* If a suitable wrap algorithm is already set nothing to do */
+    kekcipher = EVP_CIPHER_CTX_cipher(ctx);
+
+    if (kekcipher) {
+        if (EVP_CIPHER_CTX_mode(ctx) != EVP_CIPH_WRAP_MODE)
+            return 0;
+        return 1;
+    }
+    /*
+     * Pick a cipher based on content encryption cipher. If it is DES3 use
+     * DES3 wrap otherwise use AES wrap similar to key size.
+     */
+#if !defined(OPENSSL_NO_DES) && !defined(OPENSSL_NO_SHA)
+    /* EVP_des_ede3_wrap() depends on EVP_sha1() */
+    if (EVP_CIPHER_type(cipher) == NID_des_ede3_cbc)
+        kekcipher = EVP_des_ede3_wrap();
+    else
+#endif
+#ifndef OPENSSL_NO_SMS4
+    if (EVP_CIPHER_type(cipher) == NID_sms4_cbc
+        || EVP_CIPHER_type(cipher) == NID_sm1_cbc
+        || EVP_CIPHER_type(cipher) == NID_ssf33_cbc)
+        kekcipher = EVP_sms4_wrap();
+    else
+#endif
+#ifndef OPENSSL_NO_AES
+    if (keylen <= 16)
+        kekcipher = EVP_aes_128_wrap();
+    else if (keylen <= 24)
+        kekcipher = EVP_aes_192_wrap();
+    else
+        kekcipher = EVP_aes_256_wrap();
+#endif
+    if (kekcipher == NULL)
+        return 0;
+
+    return EVP_EncryptInit_ex(ctx, kekcipher, NULL, NULL, NULL);
+}
+
+/* Encrypt content key in key agreement recipient info */
+
+int cms_RecipientInfo_kari_encrypt(CMS_ContentInfo *cms,
+                                   CMS_RecipientInfo *ri)
+{
+    CMS_KeyAgreeRecipientInfo *kari;
+    CMS_EncryptedContentInfo *ec;
+    CMS_RecipientEncryptedKey *rek;
+    STACK_OF(CMS_RecipientEncryptedKey) *reks;
+    int i;
+
+    if (ri->type != CMS_RECIPINFO_AGREE) {
+        CMSerr(CMS_F_CMS_RECIPIENTINFO_KARI_ENCRYPT, CMS_R_NOT_KEY_AGREEMENT);
+        return 0;
+    }
+    kari = ri->d.kari;
+    reks = kari->recipientEncryptedKeys;
+    ec = cms->d.envelopedData->encryptedContentInfo;
+    /* Initialise wrap algorithm parameters */
+    if (!cms_wrap_init(kari, ec->cipher))
+        return 0;
+    /*
+     * If no originator key set up initialise for ephemeral key the public key
+     * ASN1 structure will set the actual public key value.
+     */
+    if (kari->originator->type == -1) {
+        CMS_OriginatorIdentifierOrKey *oik = kari->originator;
+        oik->type = CMS_OIK_PUBKEY;
+        oik->d.originatorKey = M_ASN1_new_of(CMS_OriginatorPublicKey);
+        if (!oik->d.originatorKey)
+            return 0;
+    }
+    /* Initialise KDF algorithm */
+    if (!cms_env_asn1_ctrl(ri, 0))
+        return 0;
+    /* For each rek, derive KEK, encrypt CEK */
+    for (i = 0; i < sk_CMS_RecipientEncryptedKey_num(reks); i++) {
+        unsigned char *enckey;
+        size_t enckeylen;
+        rek = sk_CMS_RecipientEncryptedKey_value(reks, i);
+        if (EVP_PKEY_derive_set_peer(kari->pctx, rek->pkey) <= 0)
+            return 0;
+        if (!cms_kek_cipher(&enckey, &enckeylen, ec->key, ec->keylen,
+                            kari, 1))
+            return 0;
+        ASN1_STRING_set0(rek->encryptedKey, enckey, enckeylen);
+    }
+
+    return 1;
+
+}
diff --git a/crypto/cms/cms_lcl.h b/crypto/cms/cms_lcl.h
new file mode 100644
index 000000000..d0c0e8136
--- /dev/null
+++ b/crypto/cms/cms_lcl.h
@@ -0,0 +1,444 @@
+/*
+ * Copyright 2008-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#ifndef HEADER_CMS_LCL_H
+# define HEADER_CMS_LCL_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+# include 
+
+/*
+ * Cryptographic message syntax (CMS) structures: taken from RFC3852
+ */
+
+/* Forward references */
+
+typedef struct CMS_IssuerAndSerialNumber_st CMS_IssuerAndSerialNumber;
+typedef struct CMS_EncapsulatedContentInfo_st CMS_EncapsulatedContentInfo;
+typedef struct CMS_SignerIdentifier_st CMS_SignerIdentifier;
+typedef struct CMS_SignedData_st CMS_SignedData;
+typedef struct CMS_OtherRevocationInfoFormat_st CMS_OtherRevocationInfoFormat;
+typedef struct CMS_OriginatorInfo_st CMS_OriginatorInfo;
+typedef struct CMS_EncryptedContentInfo_st CMS_EncryptedContentInfo;
+typedef struct CMS_EnvelopedData_st CMS_EnvelopedData;
+typedef struct CMS_DigestedData_st CMS_DigestedData;
+typedef struct CMS_EncryptedData_st CMS_EncryptedData;
+typedef struct CMS_AuthenticatedData_st CMS_AuthenticatedData;
+typedef struct CMS_CompressedData_st CMS_CompressedData;
+typedef struct CMS_OtherCertificateFormat_st CMS_OtherCertificateFormat;
+typedef struct CMS_KeyTransRecipientInfo_st CMS_KeyTransRecipientInfo;
+typedef struct CMS_OriginatorPublicKey_st CMS_OriginatorPublicKey;
+typedef struct CMS_OriginatorIdentifierOrKey_st CMS_OriginatorIdentifierOrKey;
+typedef struct CMS_KeyAgreeRecipientInfo_st CMS_KeyAgreeRecipientInfo;
+typedef struct CMS_RecipientKeyIdentifier_st CMS_RecipientKeyIdentifier;
+typedef struct CMS_KeyAgreeRecipientIdentifier_st
+    CMS_KeyAgreeRecipientIdentifier;
+typedef struct CMS_KEKIdentifier_st CMS_KEKIdentifier;
+typedef struct CMS_KEKRecipientInfo_st CMS_KEKRecipientInfo;
+typedef struct CMS_PasswordRecipientInfo_st CMS_PasswordRecipientInfo;
+typedef struct CMS_OtherRecipientInfo_st CMS_OtherRecipientInfo;
+typedef struct CMS_ReceiptsFrom_st CMS_ReceiptsFrom;
+
+struct CMS_ContentInfo_st {
+    ASN1_OBJECT *contentType;
+    union {
+        ASN1_OCTET_STRING *data;
+        CMS_SignedData *signedData;
+        CMS_EnvelopedData *envelopedData;
+        CMS_DigestedData *digestedData;
+        CMS_EncryptedData *encryptedData;
+        CMS_AuthenticatedData *authenticatedData;
+        CMS_CompressedData *compressedData;
+        ASN1_TYPE *other;
+        /* Other types ... */
+        void *otherData;
+    } d;
+};
+
+DEFINE_STACK_OF(CMS_CertificateChoices)
+
+struct CMS_SignedData_st {
+    long version;
+    STACK_OF(X509_ALGOR) *digestAlgorithms;
+    CMS_EncapsulatedContentInfo *encapContentInfo;
+    STACK_OF(CMS_CertificateChoices) *certificates;
+    STACK_OF(CMS_RevocationInfoChoice) *crls;
+    STACK_OF(CMS_SignerInfo) *signerInfos;
+};
+
+struct CMS_EncapsulatedContentInfo_st {
+    ASN1_OBJECT *eContentType;
+    ASN1_OCTET_STRING *eContent;
+    /* Set to 1 if incomplete structure only part set up */
+    int partial;
+};
+
+struct CMS_SignerInfo_st {
+    long version;
+    CMS_SignerIdentifier *sid;
+    X509_ALGOR *digestAlgorithm;
+    STACK_OF(X509_ATTRIBUTE) *signedAttrs;
+    X509_ALGOR *signatureAlgorithm;
+    ASN1_OCTET_STRING *signature;
+    STACK_OF(X509_ATTRIBUTE) *unsignedAttrs;
+    /* Signing certificate and key */
+    X509 *signer;
+    EVP_PKEY *pkey;
+    /* Digest and public key context for alternative parameters */
+    EVP_MD_CTX *mctx;
+    EVP_PKEY_CTX *pctx;
+};
+
+struct CMS_SignerIdentifier_st {
+    int type;
+    union {
+        CMS_IssuerAndSerialNumber *issuerAndSerialNumber;
+        ASN1_OCTET_STRING *subjectKeyIdentifier;
+    } d;
+};
+
+struct CMS_EnvelopedData_st {
+    long version;
+    CMS_OriginatorInfo *originatorInfo;
+    STACK_OF(CMS_RecipientInfo) *recipientInfos;
+    CMS_EncryptedContentInfo *encryptedContentInfo;
+    STACK_OF(X509_ATTRIBUTE) *unprotectedAttrs;
+};
+
+struct CMS_OriginatorInfo_st {
+    STACK_OF(CMS_CertificateChoices) *certificates;
+    STACK_OF(CMS_RevocationInfoChoice) *crls;
+};
+
+struct CMS_EncryptedContentInfo_st {
+    ASN1_OBJECT *contentType;
+    X509_ALGOR *contentEncryptionAlgorithm;
+    ASN1_OCTET_STRING *encryptedContent;
+    /* Content encryption algorithm and key */
+    const EVP_CIPHER *cipher;
+    unsigned char *key;
+    size_t keylen;
+    /* Set to 1 if we are debugging decrypt and don't fake keys for MMA */
+    int debug;
+};
+
+struct CMS_RecipientInfo_st {
+    int type;
+    union {
+        CMS_KeyTransRecipientInfo *ktri;
+        CMS_KeyAgreeRecipientInfo *kari;
+        CMS_KEKRecipientInfo *kekri;
+        CMS_PasswordRecipientInfo *pwri;
+        CMS_OtherRecipientInfo *ori;
+    } d;
+};
+
+typedef CMS_SignerIdentifier CMS_RecipientIdentifier;
+
+struct CMS_KeyTransRecipientInfo_st {
+    long version;
+    CMS_RecipientIdentifier *rid;
+    X509_ALGOR *keyEncryptionAlgorithm;
+    ASN1_OCTET_STRING *encryptedKey;
+    /* Recipient Key and cert */
+    X509 *recip;
+    EVP_PKEY *pkey;
+    /* Public key context for this operation */
+    EVP_PKEY_CTX *pctx;
+};
+
+struct CMS_KeyAgreeRecipientInfo_st {
+    long version;
+    CMS_OriginatorIdentifierOrKey *originator;
+    ASN1_OCTET_STRING *ukm;
+    X509_ALGOR *keyEncryptionAlgorithm;
+    STACK_OF(CMS_RecipientEncryptedKey) *recipientEncryptedKeys;
+    /* Public key context associated with current operation */
+    EVP_PKEY_CTX *pctx;
+    /* Cipher context for CEK wrapping */
+    EVP_CIPHER_CTX *ctx;
+};
+
+struct CMS_OriginatorIdentifierOrKey_st {
+    int type;
+    union {
+        CMS_IssuerAndSerialNumber *issuerAndSerialNumber;
+        ASN1_OCTET_STRING *subjectKeyIdentifier;
+        CMS_OriginatorPublicKey *originatorKey;
+    } d;
+};
+
+struct CMS_OriginatorPublicKey_st {
+    X509_ALGOR *algorithm;
+    ASN1_BIT_STRING *publicKey;
+};
+
+struct CMS_RecipientEncryptedKey_st {
+    CMS_KeyAgreeRecipientIdentifier *rid;
+    ASN1_OCTET_STRING *encryptedKey;
+    /* Public key associated with this recipient */
+    EVP_PKEY *pkey;
+};
+
+struct CMS_KeyAgreeRecipientIdentifier_st {
+    int type;
+    union {
+        CMS_IssuerAndSerialNumber *issuerAndSerialNumber;
+        CMS_RecipientKeyIdentifier *rKeyId;
+    } d;
+};
+
+struct CMS_RecipientKeyIdentifier_st {
+    ASN1_OCTET_STRING *subjectKeyIdentifier;
+    ASN1_GENERALIZEDTIME *date;
+    CMS_OtherKeyAttribute *other;
+};
+
+struct CMS_KEKRecipientInfo_st {
+    long version;
+    CMS_KEKIdentifier *kekid;
+    X509_ALGOR *keyEncryptionAlgorithm;
+    ASN1_OCTET_STRING *encryptedKey;
+    /* Extra info: symmetric key to use */
+    unsigned char *key;
+    size_t keylen;
+};
+
+struct CMS_KEKIdentifier_st {
+    ASN1_OCTET_STRING *keyIdentifier;
+    ASN1_GENERALIZEDTIME *date;
+    CMS_OtherKeyAttribute *other;
+};
+
+struct CMS_PasswordRecipientInfo_st {
+    long version;
+    X509_ALGOR *keyDerivationAlgorithm;
+    X509_ALGOR *keyEncryptionAlgorithm;
+    ASN1_OCTET_STRING *encryptedKey;
+    /* Extra info: password to use */
+    unsigned char *pass;
+    size_t passlen;
+};
+
+struct CMS_OtherRecipientInfo_st {
+    ASN1_OBJECT *oriType;
+    ASN1_TYPE *oriValue;
+};
+
+struct CMS_DigestedData_st {
+    long version;
+    X509_ALGOR *digestAlgorithm;
+    CMS_EncapsulatedContentInfo *encapContentInfo;
+    ASN1_OCTET_STRING *digest;
+};
+
+struct CMS_EncryptedData_st {
+    long version;
+    CMS_EncryptedContentInfo *encryptedContentInfo;
+    STACK_OF(X509_ATTRIBUTE) *unprotectedAttrs;
+};
+
+struct CMS_AuthenticatedData_st {
+    long version;
+    CMS_OriginatorInfo *originatorInfo;
+    STACK_OF(CMS_RecipientInfo) *recipientInfos;
+    X509_ALGOR *macAlgorithm;
+    X509_ALGOR *digestAlgorithm;
+    CMS_EncapsulatedContentInfo *encapContentInfo;
+    STACK_OF(X509_ATTRIBUTE) *authAttrs;
+    ASN1_OCTET_STRING *mac;
+    STACK_OF(X509_ATTRIBUTE) *unauthAttrs;
+};
+
+struct CMS_CompressedData_st {
+    long version;
+    X509_ALGOR *compressionAlgorithm;
+    STACK_OF(CMS_RecipientInfo) *recipientInfos;
+    CMS_EncapsulatedContentInfo *encapContentInfo;
+};
+
+struct CMS_RevocationInfoChoice_st {
+    int type;
+    union {
+        X509_CRL *crl;
+        CMS_OtherRevocationInfoFormat *other;
+    } d;
+};
+
+# define CMS_REVCHOICE_CRL               0
+# define CMS_REVCHOICE_OTHER             1
+
+struct CMS_OtherRevocationInfoFormat_st {
+    ASN1_OBJECT *otherRevInfoFormat;
+    ASN1_TYPE *otherRevInfo;
+};
+
+struct CMS_CertificateChoices {
+    int type;
+    union {
+        X509 *certificate;
+        ASN1_STRING *extendedCertificate; /* Obsolete */
+        ASN1_STRING *v1AttrCert; /* Left encoded for now */
+        ASN1_STRING *v2AttrCert; /* Left encoded for now */
+        CMS_OtherCertificateFormat *other;
+    } d;
+};
+
+# define CMS_CERTCHOICE_CERT             0
+# define CMS_CERTCHOICE_EXCERT           1
+# define CMS_CERTCHOICE_V1ACERT          2
+# define CMS_CERTCHOICE_V2ACERT          3
+# define CMS_CERTCHOICE_OTHER            4
+
+struct CMS_OtherCertificateFormat_st {
+    ASN1_OBJECT *otherCertFormat;
+    ASN1_TYPE *otherCert;
+};
+
+/*
+ * This is also defined in pkcs7.h but we duplicate it to allow the CMS code
+ * to be independent of PKCS#7
+ */
+
+struct CMS_IssuerAndSerialNumber_st {
+    X509_NAME *issuer;
+    ASN1_INTEGER *serialNumber;
+};
+
+struct CMS_OtherKeyAttribute_st {
+    ASN1_OBJECT *keyAttrId;
+    ASN1_TYPE *keyAttr;
+};
+
+/* ESS structures */
+
+# ifdef HEADER_X509V3_H
+
+struct CMS_ReceiptRequest_st {
+    ASN1_OCTET_STRING *signedContentIdentifier;
+    CMS_ReceiptsFrom *receiptsFrom;
+    STACK_OF(GENERAL_NAMES) *receiptsTo;
+};
+
+struct CMS_ReceiptsFrom_st {
+    int type;
+    union {
+        long allOrFirstTier;
+        STACK_OF(GENERAL_NAMES) *receiptList;
+    } d;
+};
+# endif
+
+struct CMS_Receipt_st {
+    long version;
+    ASN1_OBJECT *contentType;
+    ASN1_OCTET_STRING *signedContentIdentifier;
+    ASN1_OCTET_STRING *originatorSignatureValue;
+};
+
+DECLARE_ASN1_FUNCTIONS(CMS_ContentInfo)
+DECLARE_ASN1_ITEM(CMS_SignerInfo)
+DECLARE_ASN1_ITEM(CMS_IssuerAndSerialNumber)
+DECLARE_ASN1_ITEM(CMS_Attributes_Sign)
+DECLARE_ASN1_ITEM(CMS_Attributes_Verify)
+DECLARE_ASN1_ITEM(CMS_RecipientInfo)
+DECLARE_ASN1_ITEM(CMS_PasswordRecipientInfo)
+DECLARE_ASN1_ALLOC_FUNCTIONS(CMS_IssuerAndSerialNumber)
+
+# define CMS_SIGNERINFO_ISSUER_SERIAL    0
+# define CMS_SIGNERINFO_KEYIDENTIFIER    1
+
+# define CMS_RECIPINFO_ISSUER_SERIAL     0
+# define CMS_RECIPINFO_KEYIDENTIFIER     1
+
+# define CMS_REK_ISSUER_SERIAL           0
+# define CMS_REK_KEYIDENTIFIER           1
+
+# define CMS_OIK_ISSUER_SERIAL           0
+# define CMS_OIK_KEYIDENTIFIER           1
+# define CMS_OIK_PUBKEY                  2
+
+BIO *cms_content_bio(CMS_ContentInfo *cms);
+
+CMS_ContentInfo *cms_Data_create(void);
+
+CMS_ContentInfo *cms_DigestedData_create(const EVP_MD *md);
+BIO *cms_DigestedData_init_bio(CMS_ContentInfo *cms);
+int cms_DigestedData_do_final(CMS_ContentInfo *cms, BIO *chain, int verify);
+
+BIO *cms_SignedData_init_bio(CMS_ContentInfo *cms);
+int cms_SignedData_final(CMS_ContentInfo *cms, BIO *chain);
+int cms_set1_SignerIdentifier(CMS_SignerIdentifier *sid, X509 *cert,
+                              int type);
+int cms_SignerIdentifier_get0_signer_id(CMS_SignerIdentifier *sid,
+                                        ASN1_OCTET_STRING **keyid,
+                                        X509_NAME **issuer,
+                                        ASN1_INTEGER **sno);
+int cms_SignerIdentifier_cert_cmp(CMS_SignerIdentifier *sid, X509 *cert);
+
+CMS_ContentInfo *cms_CompressedData_create(int comp_nid);
+BIO *cms_CompressedData_init_bio(CMS_ContentInfo *cms);
+
+BIO *cms_DigestAlgorithm_init_bio(X509_ALGOR *digestAlgorithm);
+int cms_DigestAlgorithm_find_ctx(EVP_MD_CTX *mctx, BIO *chain,
+                                 X509_ALGOR *mdalg);
+
+int cms_ias_cert_cmp(CMS_IssuerAndSerialNumber *ias, X509 *cert);
+int cms_keyid_cert_cmp(ASN1_OCTET_STRING *keyid, X509 *cert);
+int cms_set1_ias(CMS_IssuerAndSerialNumber **pias, X509 *cert);
+int cms_set1_keyid(ASN1_OCTET_STRING **pkeyid, X509 *cert);
+
+BIO *cms_EncryptedContent_init_bio(CMS_EncryptedContentInfo *ec);
+BIO *cms_EncryptedData_init_bio(CMS_ContentInfo *cms);
+int cms_EncryptedContent_init(CMS_EncryptedContentInfo *ec,
+                              const EVP_CIPHER *cipher,
+                              const unsigned char *key, size_t keylen);
+
+int cms_Receipt_verify(CMS_ContentInfo *cms, CMS_ContentInfo *req_cms);
+int cms_msgSigDigest_add1(CMS_SignerInfo *dest, CMS_SignerInfo *src);
+ASN1_OCTET_STRING *cms_encode_Receipt(CMS_SignerInfo *si);
+
+BIO *cms_EnvelopedData_init_bio(CMS_ContentInfo *cms);
+CMS_EnvelopedData *cms_get0_enveloped(CMS_ContentInfo *cms);
+int cms_env_asn1_ctrl(CMS_RecipientInfo *ri, int cmd);
+int cms_pkey_get_ri_type(EVP_PKEY *pk);
+/* KARI routines */
+int cms_RecipientInfo_kari_init(CMS_RecipientInfo *ri, X509 *recip,
+                                EVP_PKEY *pk, unsigned int flags);
+int cms_RecipientInfo_kari_encrypt(CMS_ContentInfo *cms,
+                                   CMS_RecipientInfo *ri);
+
+/* PWRI routines */
+int cms_RecipientInfo_pwri_crypt(CMS_ContentInfo *cms, CMS_RecipientInfo *ri,
+                                 int en_de);
+
+DECLARE_ASN1_ITEM(CMS_CertificateChoices)
+DECLARE_ASN1_ITEM(CMS_DigestedData)
+DECLARE_ASN1_ITEM(CMS_EncryptedData)
+DECLARE_ASN1_ITEM(CMS_EnvelopedData)
+DECLARE_ASN1_ITEM(CMS_KEKRecipientInfo)
+DECLARE_ASN1_ITEM(CMS_KeyAgreeRecipientInfo)
+DECLARE_ASN1_ITEM(CMS_KeyTransRecipientInfo)
+DECLARE_ASN1_ITEM(CMS_OriginatorPublicKey)
+DECLARE_ASN1_ITEM(CMS_OtherKeyAttribute)
+DECLARE_ASN1_ITEM(CMS_Receipt)
+DECLARE_ASN1_ITEM(CMS_ReceiptRequest)
+DECLARE_ASN1_ITEM(CMS_RecipientEncryptedKey)
+DECLARE_ASN1_ITEM(CMS_RecipientKeyIdentifier)
+DECLARE_ASN1_ITEM(CMS_RevocationInfoChoice)
+DECLARE_ASN1_ITEM(CMS_SignedData)
+DECLARE_ASN1_ITEM(CMS_CompressedData)
+
+#ifdef  __cplusplus
+}
+#endif
+#endif
diff --git a/crypto/cms/cms_lib.c b/crypto/cms/cms_lib.c
new file mode 100644
index 000000000..7395684b6
--- /dev/null
+++ b/crypto/cms/cms_lib.c
@@ -0,0 +1,587 @@
+/*
+ * Copyright 2008-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include "cms_lcl.h"
+
+IMPLEMENT_ASN1_FUNCTIONS(CMS_ContentInfo)
+IMPLEMENT_ASN1_PRINT_FUNCTION(CMS_ContentInfo)
+
+const ASN1_OBJECT *CMS_get0_type(const CMS_ContentInfo *cms)
+{
+    return cms->contentType;
+}
+
+CMS_ContentInfo *cms_Data_create(void)
+{
+    CMS_ContentInfo *cms;
+    cms = CMS_ContentInfo_new();
+    if (cms != NULL) {
+        cms->contentType = OBJ_nid2obj(NID_pkcs7_data);
+        /* Never detached */
+        CMS_set_detached(cms, 0);
+    }
+    return cms;
+}
+
+BIO *cms_content_bio(CMS_ContentInfo *cms)
+{
+    ASN1_OCTET_STRING **pos = CMS_get0_content(cms);
+    if (!pos)
+        return NULL;
+    /* If content detached data goes nowhere: create NULL BIO */
+    if (!*pos)
+        return BIO_new(BIO_s_null());
+    /*
+     * If content not detached and created return memory BIO
+     */
+    if (!*pos || ((*pos)->flags == ASN1_STRING_FLAG_CONT))
+        return BIO_new(BIO_s_mem());
+    /* Else content was read in: return read only BIO for it */
+    return BIO_new_mem_buf((*pos)->data, (*pos)->length);
+}
+
+BIO *CMS_dataInit(CMS_ContentInfo *cms, BIO *icont)
+{
+    BIO *cmsbio, *cont;
+    if (icont)
+        cont = icont;
+    else
+        cont = cms_content_bio(cms);
+    if (!cont) {
+        CMSerr(CMS_F_CMS_DATAINIT, CMS_R_NO_CONTENT);
+        return NULL;
+    }
+    switch (OBJ_obj2nid(cms->contentType)) {
+
+    case NID_pkcs7_data:
+        return cont;
+
+    case NID_pkcs7_signed:
+        cmsbio = cms_SignedData_init_bio(cms);
+        break;
+
+    case NID_pkcs7_digest:
+        cmsbio = cms_DigestedData_init_bio(cms);
+        break;
+#ifdef ZLIB
+    case NID_id_smime_ct_compressedData:
+        cmsbio = cms_CompressedData_init_bio(cms);
+        break;
+#endif
+
+    case NID_pkcs7_encrypted:
+        cmsbio = cms_EncryptedData_init_bio(cms);
+        break;
+
+    case NID_pkcs7_enveloped:
+        cmsbio = cms_EnvelopedData_init_bio(cms);
+        break;
+
+    default:
+        CMSerr(CMS_F_CMS_DATAINIT, CMS_R_UNSUPPORTED_TYPE);
+        return NULL;
+    }
+
+    if (cmsbio)
+        return BIO_push(cmsbio, cont);
+
+    if (!icont)
+        BIO_free(cont);
+    return NULL;
+
+}
+
+int CMS_dataFinal(CMS_ContentInfo *cms, BIO *cmsbio)
+{
+    ASN1_OCTET_STRING **pos = CMS_get0_content(cms);
+    if (!pos)
+        return 0;
+    /* If embedded content find memory BIO and set content */
+    if (*pos && ((*pos)->flags & ASN1_STRING_FLAG_CONT)) {
+        BIO *mbio;
+        unsigned char *cont;
+        long contlen;
+        mbio = BIO_find_type(cmsbio, BIO_TYPE_MEM);
+        if (!mbio) {
+            CMSerr(CMS_F_CMS_DATAFINAL, CMS_R_CONTENT_NOT_FOUND);
+            return 0;
+        }
+        contlen = BIO_get_mem_data(mbio, &cont);
+        /* Set bio as read only so its content can't be clobbered */
+        BIO_set_flags(mbio, BIO_FLAGS_MEM_RDONLY);
+        BIO_set_mem_eof_return(mbio, 0);
+        ASN1_STRING_set0(*pos, cont, contlen);
+        (*pos)->flags &= ~ASN1_STRING_FLAG_CONT;
+    }
+
+    switch (OBJ_obj2nid(cms->contentType)) {
+
+    case NID_pkcs7_data:
+    case NID_pkcs7_enveloped:
+    case NID_pkcs7_encrypted:
+    case NID_id_smime_ct_compressedData:
+        /* Nothing to do */
+        return 1;
+
+    case NID_pkcs7_signed:
+        return cms_SignedData_final(cms, cmsbio);
+
+    case NID_pkcs7_digest:
+        return cms_DigestedData_do_final(cms, cmsbio, 0);
+
+    default:
+        CMSerr(CMS_F_CMS_DATAFINAL, CMS_R_UNSUPPORTED_TYPE);
+        return 0;
+    }
+}
+
+/*
+ * Return an OCTET STRING pointer to content. This allows it to be accessed
+ * or set later.
+ */
+
+ASN1_OCTET_STRING **CMS_get0_content(CMS_ContentInfo *cms)
+{
+    switch (OBJ_obj2nid(cms->contentType)) {
+
+    case NID_pkcs7_data:
+        return &cms->d.data;
+
+    case NID_pkcs7_signed:
+        return &cms->d.signedData->encapContentInfo->eContent;
+
+    case NID_pkcs7_enveloped:
+        return &cms->d.envelopedData->encryptedContentInfo->encryptedContent;
+
+    case NID_pkcs7_digest:
+        return &cms->d.digestedData->encapContentInfo->eContent;
+
+    case NID_pkcs7_encrypted:
+        return &cms->d.encryptedData->encryptedContentInfo->encryptedContent;
+
+    case NID_id_smime_ct_authData:
+        return &cms->d.authenticatedData->encapContentInfo->eContent;
+
+    case NID_id_smime_ct_compressedData:
+        return &cms->d.compressedData->encapContentInfo->eContent;
+
+    default:
+        if (cms->d.other->type == V_ASN1_OCTET_STRING)
+            return &cms->d.other->value.octet_string;
+        CMSerr(CMS_F_CMS_GET0_CONTENT, CMS_R_UNSUPPORTED_CONTENT_TYPE);
+        return NULL;
+
+    }
+}
+
+/*
+ * Return an ASN1_OBJECT pointer to content type. This allows it to be
+ * accessed or set later.
+ */
+
+static ASN1_OBJECT **cms_get0_econtent_type(CMS_ContentInfo *cms)
+{
+    switch (OBJ_obj2nid(cms->contentType)) {
+
+    case NID_pkcs7_signed:
+        return &cms->d.signedData->encapContentInfo->eContentType;
+
+    case NID_pkcs7_enveloped:
+        return &cms->d.envelopedData->encryptedContentInfo->contentType;
+
+    case NID_pkcs7_digest:
+        return &cms->d.digestedData->encapContentInfo->eContentType;
+
+    case NID_pkcs7_encrypted:
+        return &cms->d.encryptedData->encryptedContentInfo->contentType;
+
+    case NID_id_smime_ct_authData:
+        return &cms->d.authenticatedData->encapContentInfo->eContentType;
+
+    case NID_id_smime_ct_compressedData:
+        return &cms->d.compressedData->encapContentInfo->eContentType;
+
+    default:
+        CMSerr(CMS_F_CMS_GET0_ECONTENT_TYPE, CMS_R_UNSUPPORTED_CONTENT_TYPE);
+        return NULL;
+
+    }
+}
+
+const ASN1_OBJECT *CMS_get0_eContentType(CMS_ContentInfo *cms)
+{
+    ASN1_OBJECT **petype;
+    petype = cms_get0_econtent_type(cms);
+    if (petype)
+        return *petype;
+    return NULL;
+}
+
+int CMS_set1_eContentType(CMS_ContentInfo *cms, const ASN1_OBJECT *oid)
+{
+    ASN1_OBJECT **petype, *etype;
+    petype = cms_get0_econtent_type(cms);
+    if (!petype)
+        return 0;
+    if (!oid)
+        return 1;
+    etype = OBJ_dup(oid);
+    if (!etype)
+        return 0;
+    ASN1_OBJECT_free(*petype);
+    *petype = etype;
+    return 1;
+}
+
+int CMS_is_detached(CMS_ContentInfo *cms)
+{
+    ASN1_OCTET_STRING **pos;
+    pos = CMS_get0_content(cms);
+    if (!pos)
+        return -1;
+    if (*pos)
+        return 0;
+    return 1;
+}
+
+int CMS_set_detached(CMS_ContentInfo *cms, int detached)
+{
+    ASN1_OCTET_STRING **pos;
+    pos = CMS_get0_content(cms);
+    if (!pos)
+        return 0;
+    if (detached) {
+        ASN1_OCTET_STRING_free(*pos);
+        *pos = NULL;
+        return 1;
+    }
+    if (*pos == NULL)
+        *pos = ASN1_OCTET_STRING_new();
+    if (*pos != NULL) {
+        /*
+         * NB: special flag to show content is created and not read in.
+         */
+        (*pos)->flags |= ASN1_STRING_FLAG_CONT;
+        return 1;
+    }
+    CMSerr(CMS_F_CMS_SET_DETACHED, ERR_R_MALLOC_FAILURE);
+    return 0;
+}
+
+/* Create a digest BIO from an X509_ALGOR structure */
+
+BIO *cms_DigestAlgorithm_init_bio(X509_ALGOR *digestAlgorithm)
+{
+    BIO *mdbio = NULL;
+    const ASN1_OBJECT *digestoid;
+    const EVP_MD *digest;
+    X509_ALGOR_get0(&digestoid, NULL, NULL, digestAlgorithm);
+    digest = EVP_get_digestbyobj(digestoid);
+    if (!digest) {
+        CMSerr(CMS_F_CMS_DIGESTALGORITHM_INIT_BIO,
+               CMS_R_UNKNOWN_DIGEST_ALGORIHM);
+        goto err;
+    }
+    mdbio = BIO_new(BIO_f_md());
+    if (mdbio == NULL || !BIO_set_md(mdbio, digest)) {
+        CMSerr(CMS_F_CMS_DIGESTALGORITHM_INIT_BIO, CMS_R_MD_BIO_INIT_ERROR);
+        goto err;
+    }
+    return mdbio;
+ err:
+    BIO_free(mdbio);
+    return NULL;
+}
+
+/* Locate a message digest content from a BIO chain based on SignerInfo */
+
+int cms_DigestAlgorithm_find_ctx(EVP_MD_CTX *mctx, BIO *chain,
+                                 X509_ALGOR *mdalg)
+{
+    int nid;
+    const ASN1_OBJECT *mdoid;
+    X509_ALGOR_get0(&mdoid, NULL, NULL, mdalg);
+    nid = OBJ_obj2nid(mdoid);
+    /* Look for digest type to match signature */
+    for (;;) {
+        EVP_MD_CTX *mtmp;
+        chain = BIO_find_type(chain, BIO_TYPE_MD);
+        if (chain == NULL) {
+            CMSerr(CMS_F_CMS_DIGESTALGORITHM_FIND_CTX,
+                   CMS_R_NO_MATCHING_DIGEST);
+            return 0;
+        }
+        BIO_get_md_ctx(chain, &mtmp);
+        if (EVP_MD_CTX_type(mtmp) == nid
+            /*
+             * Workaround for broken implementations that use signature
+             * algorithm OID instead of digest.
+             */
+            || EVP_MD_pkey_type(EVP_MD_CTX_md(mtmp)) == nid)
+            return EVP_MD_CTX_copy_ex(mctx, mtmp);
+        chain = BIO_next(chain);
+    }
+}
+
+static STACK_OF(CMS_CertificateChoices)
+**cms_get0_certificate_choices(CMS_ContentInfo *cms)
+{
+    switch (OBJ_obj2nid(cms->contentType)) {
+
+    case NID_pkcs7_signed:
+        return &cms->d.signedData->certificates;
+
+    case NID_pkcs7_enveloped:
+        if (cms->d.envelopedData->originatorInfo == NULL)
+            return NULL;
+        return &cms->d.envelopedData->originatorInfo->certificates;
+
+    default:
+        CMSerr(CMS_F_CMS_GET0_CERTIFICATE_CHOICES,
+               CMS_R_UNSUPPORTED_CONTENT_TYPE);
+        return NULL;
+
+    }
+}
+
+CMS_CertificateChoices *CMS_add0_CertificateChoices(CMS_ContentInfo *cms)
+{
+    STACK_OF(CMS_CertificateChoices) **pcerts;
+    CMS_CertificateChoices *cch;
+    pcerts = cms_get0_certificate_choices(cms);
+    if (!pcerts)
+        return NULL;
+    if (!*pcerts)
+        *pcerts = sk_CMS_CertificateChoices_new_null();
+    if (!*pcerts)
+        return NULL;
+    cch = M_ASN1_new_of(CMS_CertificateChoices);
+    if (!cch)
+        return NULL;
+    if (!sk_CMS_CertificateChoices_push(*pcerts, cch)) {
+        M_ASN1_free_of(cch, CMS_CertificateChoices);
+        return NULL;
+    }
+    return cch;
+}
+
+int CMS_add0_cert(CMS_ContentInfo *cms, X509 *cert)
+{
+    CMS_CertificateChoices *cch;
+    STACK_OF(CMS_CertificateChoices) **pcerts;
+    int i;
+    pcerts = cms_get0_certificate_choices(cms);
+    if (!pcerts)
+        return 0;
+    for (i = 0; i < sk_CMS_CertificateChoices_num(*pcerts); i++) {
+        cch = sk_CMS_CertificateChoices_value(*pcerts, i);
+        if (cch->type == CMS_CERTCHOICE_CERT) {
+            if (!X509_cmp(cch->d.certificate, cert)) {
+                CMSerr(CMS_F_CMS_ADD0_CERT,
+                       CMS_R_CERTIFICATE_ALREADY_PRESENT);
+                return 0;
+            }
+        }
+    }
+    cch = CMS_add0_CertificateChoices(cms);
+    if (!cch)
+        return 0;
+    cch->type = CMS_CERTCHOICE_CERT;
+    cch->d.certificate = cert;
+    return 1;
+}
+
+int CMS_add1_cert(CMS_ContentInfo *cms, X509 *cert)
+{
+    int r;
+    r = CMS_add0_cert(cms, cert);
+    if (r > 0)
+        X509_up_ref(cert);
+    return r;
+}
+
+static STACK_OF(CMS_RevocationInfoChoice)
+**cms_get0_revocation_choices(CMS_ContentInfo *cms)
+{
+    switch (OBJ_obj2nid(cms->contentType)) {
+
+    case NID_pkcs7_signed:
+        return &cms->d.signedData->crls;
+
+    case NID_pkcs7_enveloped:
+        if (cms->d.envelopedData->originatorInfo == NULL)
+            return NULL;
+        return &cms->d.envelopedData->originatorInfo->crls;
+
+    default:
+        CMSerr(CMS_F_CMS_GET0_REVOCATION_CHOICES,
+               CMS_R_UNSUPPORTED_CONTENT_TYPE);
+        return NULL;
+
+    }
+}
+
+CMS_RevocationInfoChoice *CMS_add0_RevocationInfoChoice(CMS_ContentInfo *cms)
+{
+    STACK_OF(CMS_RevocationInfoChoice) **pcrls;
+    CMS_RevocationInfoChoice *rch;
+    pcrls = cms_get0_revocation_choices(cms);
+    if (!pcrls)
+        return NULL;
+    if (!*pcrls)
+        *pcrls = sk_CMS_RevocationInfoChoice_new_null();
+    if (!*pcrls)
+        return NULL;
+    rch = M_ASN1_new_of(CMS_RevocationInfoChoice);
+    if (!rch)
+        return NULL;
+    if (!sk_CMS_RevocationInfoChoice_push(*pcrls, rch)) {
+        M_ASN1_free_of(rch, CMS_RevocationInfoChoice);
+        return NULL;
+    }
+    return rch;
+}
+
+int CMS_add0_crl(CMS_ContentInfo *cms, X509_CRL *crl)
+{
+    CMS_RevocationInfoChoice *rch;
+    rch = CMS_add0_RevocationInfoChoice(cms);
+    if (!rch)
+        return 0;
+    rch->type = CMS_REVCHOICE_CRL;
+    rch->d.crl = crl;
+    return 1;
+}
+
+int CMS_add1_crl(CMS_ContentInfo *cms, X509_CRL *crl)
+{
+    int r;
+    r = CMS_add0_crl(cms, crl);
+    if (r > 0)
+        X509_CRL_up_ref(crl);
+    return r;
+}
+
+STACK_OF(X509) *CMS_get1_certs(CMS_ContentInfo *cms)
+{
+    STACK_OF(X509) *certs = NULL;
+    CMS_CertificateChoices *cch;
+    STACK_OF(CMS_CertificateChoices) **pcerts;
+    int i;
+    pcerts = cms_get0_certificate_choices(cms);
+    if (!pcerts)
+        return NULL;
+    for (i = 0; i < sk_CMS_CertificateChoices_num(*pcerts); i++) {
+        cch = sk_CMS_CertificateChoices_value(*pcerts, i);
+        if (cch->type == 0) {
+            if (!certs) {
+                certs = sk_X509_new_null();
+                if (!certs)
+                    return NULL;
+            }
+            if (!sk_X509_push(certs, cch->d.certificate)) {
+                sk_X509_pop_free(certs, X509_free);
+                return NULL;
+            }
+            X509_up_ref(cch->d.certificate);
+        }
+    }
+    return certs;
+
+}
+
+STACK_OF(X509_CRL) *CMS_get1_crls(CMS_ContentInfo *cms)
+{
+    STACK_OF(X509_CRL) *crls = NULL;
+    STACK_OF(CMS_RevocationInfoChoice) **pcrls;
+    CMS_RevocationInfoChoice *rch;
+    int i;
+    pcrls = cms_get0_revocation_choices(cms);
+    if (!pcrls)
+        return NULL;
+    for (i = 0; i < sk_CMS_RevocationInfoChoice_num(*pcrls); i++) {
+        rch = sk_CMS_RevocationInfoChoice_value(*pcrls, i);
+        if (rch->type == 0) {
+            if (!crls) {
+                crls = sk_X509_CRL_new_null();
+                if (!crls)
+                    return NULL;
+            }
+            if (!sk_X509_CRL_push(crls, rch->d.crl)) {
+                sk_X509_CRL_pop_free(crls, X509_CRL_free);
+                return NULL;
+            }
+            X509_CRL_up_ref(rch->d.crl);
+        }
+    }
+    return crls;
+}
+
+int cms_ias_cert_cmp(CMS_IssuerAndSerialNumber *ias, X509 *cert)
+{
+    int ret;
+    ret = X509_NAME_cmp(ias->issuer, X509_get_issuer_name(cert));
+    if (ret)
+        return ret;
+    return ASN1_INTEGER_cmp(ias->serialNumber, X509_get_serialNumber(cert));
+}
+
+int cms_keyid_cert_cmp(ASN1_OCTET_STRING *keyid, X509 *cert)
+{
+    const ASN1_OCTET_STRING *cert_keyid = X509_get0_subject_key_id(cert);
+
+    if (cert_keyid == NULL)
+        return -1;
+    return ASN1_OCTET_STRING_cmp(keyid, cert_keyid);
+}
+
+int cms_set1_ias(CMS_IssuerAndSerialNumber **pias, X509 *cert)
+{
+    CMS_IssuerAndSerialNumber *ias;
+    ias = M_ASN1_new_of(CMS_IssuerAndSerialNumber);
+    if (!ias)
+        goto err;
+    if (!X509_NAME_set(&ias->issuer, X509_get_issuer_name(cert)))
+        goto err;
+    if (!ASN1_STRING_copy(ias->serialNumber, X509_get_serialNumber(cert)))
+        goto err;
+    M_ASN1_free_of(*pias, CMS_IssuerAndSerialNumber);
+    *pias = ias;
+    return 1;
+ err:
+    M_ASN1_free_of(ias, CMS_IssuerAndSerialNumber);
+    CMSerr(CMS_F_CMS_SET1_IAS, ERR_R_MALLOC_FAILURE);
+    return 0;
+}
+
+int cms_set1_keyid(ASN1_OCTET_STRING **pkeyid, X509 *cert)
+{
+    ASN1_OCTET_STRING *keyid = NULL;
+    const ASN1_OCTET_STRING *cert_keyid;
+    cert_keyid = X509_get0_subject_key_id(cert);
+    if (cert_keyid == NULL) {
+        CMSerr(CMS_F_CMS_SET1_KEYID, CMS_R_CERTIFICATE_HAS_NO_KEYID);
+        return 0;
+    }
+    keyid = ASN1_STRING_dup(cert_keyid);
+    if (!keyid) {
+        CMSerr(CMS_F_CMS_SET1_KEYID, ERR_R_MALLOC_FAILURE);
+        return 0;
+    }
+    ASN1_OCTET_STRING_free(*pkeyid);
+    *pkeyid = keyid;
+    return 1;
+}
diff --git a/crypto/cms/cms_pwri.c b/crypto/cms/cms_pwri.c
new file mode 100644
index 000000000..a7043e230
--- /dev/null
+++ b/crypto/cms/cms_pwri.c
@@ -0,0 +1,394 @@
+/*
+ * Copyright 2009-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "internal/cryptlib.h"
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#ifndef OPENSSL_NO_AES
+# include 
+#endif
+#include "cms_lcl.h"
+#include "internal/asn1_int.h"
+
+int CMS_RecipientInfo_set0_password(CMS_RecipientInfo *ri,
+                                    unsigned char *pass, ossl_ssize_t passlen)
+{
+    CMS_PasswordRecipientInfo *pwri;
+    if (ri->type != CMS_RECIPINFO_PASS) {
+        CMSerr(CMS_F_CMS_RECIPIENTINFO_SET0_PASSWORD, CMS_R_NOT_PWRI);
+        return 0;
+    }
+
+    pwri = ri->d.pwri;
+    pwri->pass = pass;
+    if (pass && passlen < 0)
+        passlen = strlen((char *)pass);
+    pwri->passlen = passlen;
+    return 1;
+}
+
+CMS_RecipientInfo *CMS_add0_recipient_password(CMS_ContentInfo *cms,
+                                               int iter, int wrap_nid,
+                                               int pbe_nid,
+                                               unsigned char *pass,
+                                               ossl_ssize_t passlen,
+                                               const EVP_CIPHER *kekciph)
+{
+    CMS_RecipientInfo *ri = NULL;
+    CMS_EnvelopedData *env;
+    CMS_PasswordRecipientInfo *pwri;
+    EVP_CIPHER_CTX *ctx = NULL;
+    X509_ALGOR *encalg = NULL;
+    unsigned char iv[EVP_MAX_IV_LENGTH];
+    int ivlen;
+
+    env = cms_get0_enveloped(cms);
+    if (!env)
+        return NULL;
+
+    if (wrap_nid <= 0)
+        wrap_nid = NID_id_alg_PWRI_KEK;
+
+    if (pbe_nid <= 0)
+        pbe_nid = NID_id_pbkdf2;
+
+    /* Get from enveloped data */
+    if (kekciph == NULL)
+        kekciph = env->encryptedContentInfo->cipher;
+
+    if (kekciph == NULL) {
+        CMSerr(CMS_F_CMS_ADD0_RECIPIENT_PASSWORD, CMS_R_NO_CIPHER);
+        return NULL;
+    }
+    if (wrap_nid != NID_id_alg_PWRI_KEK) {
+        CMSerr(CMS_F_CMS_ADD0_RECIPIENT_PASSWORD,
+               CMS_R_UNSUPPORTED_KEY_ENCRYPTION_ALGORITHM);
+        return NULL;
+    }
+
+    /* Setup algorithm identifier for cipher */
+    encalg = X509_ALGOR_new();
+    if (encalg == NULL) {
+        goto merr;
+    }
+    ctx = EVP_CIPHER_CTX_new();
+
+    if (EVP_EncryptInit_ex(ctx, kekciph, NULL, NULL, NULL) <= 0) {
+        CMSerr(CMS_F_CMS_ADD0_RECIPIENT_PASSWORD, ERR_R_EVP_LIB);
+        goto err;
+    }
+
+    ivlen = EVP_CIPHER_CTX_iv_length(ctx);
+
+    if (ivlen > 0) {
+        if (RAND_bytes(iv, ivlen) <= 0)
+            goto err;
+        if (EVP_EncryptInit_ex(ctx, NULL, NULL, NULL, iv) <= 0) {
+            CMSerr(CMS_F_CMS_ADD0_RECIPIENT_PASSWORD, ERR_R_EVP_LIB);
+            goto err;
+        }
+        encalg->parameter = ASN1_TYPE_new();
+        if (!encalg->parameter) {
+            CMSerr(CMS_F_CMS_ADD0_RECIPIENT_PASSWORD, ERR_R_MALLOC_FAILURE);
+            goto err;
+        }
+        if (EVP_CIPHER_param_to_asn1(ctx, encalg->parameter) <= 0) {
+            CMSerr(CMS_F_CMS_ADD0_RECIPIENT_PASSWORD,
+                   CMS_R_CIPHER_PARAMETER_INITIALISATION_ERROR);
+            goto err;
+        }
+    }
+
+    encalg->algorithm = OBJ_nid2obj(EVP_CIPHER_CTX_type(ctx));
+
+    EVP_CIPHER_CTX_free(ctx);
+    ctx = NULL;
+
+    /* Initialize recipient info */
+    ri = M_ASN1_new_of(CMS_RecipientInfo);
+    if (ri == NULL)
+        goto merr;
+
+    ri->d.pwri = M_ASN1_new_of(CMS_PasswordRecipientInfo);
+    if (ri->d.pwri == NULL)
+        goto merr;
+    ri->type = CMS_RECIPINFO_PASS;
+
+    pwri = ri->d.pwri;
+    /* Since this is overwritten, free up empty structure already there */
+    X509_ALGOR_free(pwri->keyEncryptionAlgorithm);
+    pwri->keyEncryptionAlgorithm = X509_ALGOR_new();
+    if (pwri->keyEncryptionAlgorithm == NULL)
+        goto merr;
+    pwri->keyEncryptionAlgorithm->algorithm = OBJ_nid2obj(wrap_nid);
+    pwri->keyEncryptionAlgorithm->parameter = ASN1_TYPE_new();
+    if (pwri->keyEncryptionAlgorithm->parameter == NULL)
+        goto merr;
+
+    if (!ASN1_item_pack(encalg, ASN1_ITEM_rptr(X509_ALGOR),
+                        &pwri->keyEncryptionAlgorithm->parameter->
+                        value.sequence))
+         goto merr;
+    pwri->keyEncryptionAlgorithm->parameter->type = V_ASN1_SEQUENCE;
+
+    X509_ALGOR_free(encalg);
+    encalg = NULL;
+
+    /* Setup PBE algorithm */
+
+    pwri->keyDerivationAlgorithm = PKCS5_pbkdf2_set(iter, NULL, 0, -1, -1);
+
+    if (!pwri->keyDerivationAlgorithm)
+        goto err;
+
+    CMS_RecipientInfo_set0_password(ri, pass, passlen);
+    pwri->version = 0;
+
+    if (!sk_CMS_RecipientInfo_push(env->recipientInfos, ri))
+        goto merr;
+
+    return ri;
+
+ merr:
+    CMSerr(CMS_F_CMS_ADD0_RECIPIENT_PASSWORD, ERR_R_MALLOC_FAILURE);
+ err:
+    EVP_CIPHER_CTX_free(ctx);
+    if (ri)
+        M_ASN1_free_of(ri, CMS_RecipientInfo);
+    X509_ALGOR_free(encalg);
+    return NULL;
+
+}
+
+/*
+ * This is an implementation of the key wrapping mechanism in RFC3211, at
+ * some point this should go into EVP.
+ */
+
+static int kek_unwrap_key(unsigned char *out, size_t *outlen,
+                          const unsigned char *in, size_t inlen,
+                          EVP_CIPHER_CTX *ctx)
+{
+    size_t blocklen = EVP_CIPHER_CTX_block_size(ctx);
+    unsigned char *tmp;
+    int outl, rv = 0;
+    if (inlen < 2 * blocklen) {
+        /* too small */
+        return 0;
+    }
+    if (inlen % blocklen) {
+        /* Invalid size */
+        return 0;
+    }
+    tmp = OPENSSL_malloc(inlen);
+    if (tmp == NULL)
+        return 0;
+    /* setup IV by decrypting last two blocks */
+    if (!EVP_DecryptUpdate(ctx, tmp + inlen - 2 * blocklen, &outl,
+                           in + inlen - 2 * blocklen, blocklen * 2)
+        /*
+         * Do a decrypt of last decrypted block to set IV to correct value
+         * output it to start of buffer so we don't corrupt decrypted block
+         * this works because buffer is at least two block lengths long.
+         */
+        || !EVP_DecryptUpdate(ctx, tmp, &outl,
+                              tmp + inlen - blocklen, blocklen)
+        /* Can now decrypt first n - 1 blocks */
+        || !EVP_DecryptUpdate(ctx, tmp, &outl, in, inlen - blocklen)
+
+        /* Reset IV to original value */
+        || !EVP_DecryptInit_ex(ctx, NULL, NULL, NULL, NULL)
+        /* Decrypt again */
+        || !EVP_DecryptUpdate(ctx, tmp, &outl, tmp, inlen))
+        goto err;
+    /* Check check bytes */
+    if (((tmp[1] ^ tmp[4]) & (tmp[2] ^ tmp[5]) & (tmp[3] ^ tmp[6])) != 0xff) {
+        /* Check byte failure */
+        goto err;
+    }
+    if (inlen < (size_t)(tmp[0] - 4)) {
+        /* Invalid length value */
+        goto err;
+    }
+    *outlen = (size_t)tmp[0];
+    memcpy(out, tmp + 4, *outlen);
+    rv = 1;
+ err:
+    OPENSSL_clear_free(tmp, inlen);
+    return rv;
+
+}
+
+static int kek_wrap_key(unsigned char *out, size_t *outlen,
+                        const unsigned char *in, size_t inlen,
+                        EVP_CIPHER_CTX *ctx)
+{
+    size_t blocklen = EVP_CIPHER_CTX_block_size(ctx);
+    size_t olen;
+    int dummy;
+    /*
+     * First decide length of output buffer: need header and round up to
+     * multiple of block length.
+     */
+    olen = (inlen + 4 + blocklen - 1) / blocklen;
+    olen *= blocklen;
+    if (olen < 2 * blocklen) {
+        /* Key too small */
+        return 0;
+    }
+    if (inlen > 0xFF) {
+        /* Key too large */
+        return 0;
+    }
+    if (out) {
+        /* Set header */
+        out[0] = (unsigned char)inlen;
+        out[1] = in[0] ^ 0xFF;
+        out[2] = in[1] ^ 0xFF;
+        out[3] = in[2] ^ 0xFF;
+        memcpy(out + 4, in, inlen);
+        /* Add random padding to end */
+        if (olen > inlen + 4
+            && RAND_bytes(out + 4 + inlen, olen - 4 - inlen) <= 0)
+            return 0;
+        /* Encrypt twice */
+        if (!EVP_EncryptUpdate(ctx, out, &dummy, out, olen)
+            || !EVP_EncryptUpdate(ctx, out, &dummy, out, olen))
+            return 0;
+    }
+
+    *outlen = olen;
+
+    return 1;
+}
+
+/* Encrypt/Decrypt content key in PWRI recipient info */
+
+int cms_RecipientInfo_pwri_crypt(CMS_ContentInfo *cms, CMS_RecipientInfo *ri,
+                                 int en_de)
+{
+    CMS_EncryptedContentInfo *ec;
+    CMS_PasswordRecipientInfo *pwri;
+    int r = 0;
+    X509_ALGOR *algtmp, *kekalg = NULL;
+    EVP_CIPHER_CTX *kekctx = NULL;
+    const EVP_CIPHER *kekcipher;
+    unsigned char *key = NULL;
+    size_t keylen;
+
+    ec = cms->d.envelopedData->encryptedContentInfo;
+
+    pwri = ri->d.pwri;
+
+    if (!pwri->pass) {
+        CMSerr(CMS_F_CMS_RECIPIENTINFO_PWRI_CRYPT, CMS_R_NO_PASSWORD);
+        return 0;
+    }
+    algtmp = pwri->keyEncryptionAlgorithm;
+
+    if (!algtmp || OBJ_obj2nid(algtmp->algorithm) != NID_id_alg_PWRI_KEK) {
+        CMSerr(CMS_F_CMS_RECIPIENTINFO_PWRI_CRYPT,
+               CMS_R_UNSUPPORTED_KEY_ENCRYPTION_ALGORITHM);
+        return 0;
+    }
+
+    kekalg = ASN1_TYPE_unpack_sequence(ASN1_ITEM_rptr(X509_ALGOR),
+                                       algtmp->parameter);
+
+    if (kekalg == NULL) {
+        CMSerr(CMS_F_CMS_RECIPIENTINFO_PWRI_CRYPT,
+               CMS_R_INVALID_KEY_ENCRYPTION_PARAMETER);
+        return 0;
+    }
+
+    kekcipher = EVP_get_cipherbyobj(kekalg->algorithm);
+
+    if (!kekcipher) {
+        CMSerr(CMS_F_CMS_RECIPIENTINFO_PWRI_CRYPT, CMS_R_UNKNOWN_CIPHER);
+        return 0;
+    }
+
+    kekctx = EVP_CIPHER_CTX_new();
+    if (kekctx == NULL) {
+        CMSerr(CMS_F_CMS_RECIPIENTINFO_PWRI_CRYPT, ERR_R_MALLOC_FAILURE);
+        return 0;
+    }
+    /* Fixup cipher based on AlgorithmIdentifier to set IV etc */
+    if (!EVP_CipherInit_ex(kekctx, kekcipher, NULL, NULL, NULL, en_de))
+        goto err;
+    EVP_CIPHER_CTX_set_padding(kekctx, 0);
+    if (EVP_CIPHER_asn1_to_param(kekctx, kekalg->parameter) < 0) {
+        CMSerr(CMS_F_CMS_RECIPIENTINFO_PWRI_CRYPT,
+               CMS_R_CIPHER_PARAMETER_INITIALISATION_ERROR);
+        goto err;
+    }
+
+    algtmp = pwri->keyDerivationAlgorithm;
+
+    /* Finish password based key derivation to setup key in "ctx" */
+
+    if (EVP_PBE_CipherInit(algtmp->algorithm,
+                           (char *)pwri->pass, pwri->passlen,
+                           algtmp->parameter, kekctx, en_de) < 0) {
+        CMSerr(CMS_F_CMS_RECIPIENTINFO_PWRI_CRYPT, ERR_R_EVP_LIB);
+        goto err;
+    }
+
+    /* Finally wrap/unwrap the key */
+
+    if (en_de) {
+
+        if (!kek_wrap_key(NULL, &keylen, ec->key, ec->keylen, kekctx))
+            goto err;
+
+        key = OPENSSL_malloc(keylen);
+
+        if (key == NULL)
+            goto err;
+
+        if (!kek_wrap_key(key, &keylen, ec->key, ec->keylen, kekctx))
+            goto err;
+        pwri->encryptedKey->data = key;
+        pwri->encryptedKey->length = keylen;
+    } else {
+        key = OPENSSL_malloc(pwri->encryptedKey->length);
+
+        if (key == NULL) {
+            CMSerr(CMS_F_CMS_RECIPIENTINFO_PWRI_CRYPT, ERR_R_MALLOC_FAILURE);
+            goto err;
+        }
+        if (!kek_unwrap_key(key, &keylen,
+                            pwri->encryptedKey->data,
+                            pwri->encryptedKey->length, kekctx)) {
+            CMSerr(CMS_F_CMS_RECIPIENTINFO_PWRI_CRYPT, CMS_R_UNWRAP_FAILURE);
+            goto err;
+        }
+
+        ec->key = key;
+        ec->keylen = keylen;
+
+    }
+
+    r = 1;
+
+ err:
+
+    EVP_CIPHER_CTX_free(kekctx);
+
+    if (!r)
+        OPENSSL_free(key);
+    X509_ALGOR_free(kekalg);
+
+    return r;
+
+}
diff --git a/crypto/cms/cms_sd.c b/crypto/cms/cms_sd.c
new file mode 100644
index 000000000..76c1f53b5
--- /dev/null
+++ b/crypto/cms/cms_sd.c
@@ -0,0 +1,923 @@
+/*
+ * Copyright 2008-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "internal/cryptlib.h"
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include "cms_lcl.h"
+#include "internal/asn1_int.h"
+#include "internal/evp_int.h"
+
+/* CMS SignedData Utilities */
+
+static CMS_SignedData *cms_get0_signed(CMS_ContentInfo *cms)
+{
+    if (OBJ_obj2nid(cms->contentType) != NID_pkcs7_signed) {
+        CMSerr(CMS_F_CMS_GET0_SIGNED, CMS_R_CONTENT_TYPE_NOT_SIGNED_DATA);
+        return NULL;
+    }
+    return cms->d.signedData;
+}
+
+static CMS_SignedData *cms_signed_data_init(CMS_ContentInfo *cms)
+{
+    if (cms->d.other == NULL) {
+        cms->d.signedData = M_ASN1_new_of(CMS_SignedData);
+        if (!cms->d.signedData) {
+            CMSerr(CMS_F_CMS_SIGNED_DATA_INIT, ERR_R_MALLOC_FAILURE);
+            return NULL;
+        }
+        cms->d.signedData->version = 1;
+        cms->d.signedData->encapContentInfo->eContentType =
+            OBJ_nid2obj(NID_pkcs7_data);
+        cms->d.signedData->encapContentInfo->partial = 1;
+        ASN1_OBJECT_free(cms->contentType);
+        cms->contentType = OBJ_nid2obj(NID_pkcs7_signed);
+        return cms->d.signedData;
+    }
+    return cms_get0_signed(cms);
+}
+
+/* Just initialise SignedData e.g. for certs only structure */
+
+int CMS_SignedData_init(CMS_ContentInfo *cms)
+{
+    if (cms_signed_data_init(cms))
+        return 1;
+    else
+        return 0;
+}
+
+/* Check structures and fixup version numbers (if necessary) */
+
+static void cms_sd_set_version(CMS_SignedData *sd)
+{
+    int i;
+    CMS_CertificateChoices *cch;
+    CMS_RevocationInfoChoice *rch;
+    CMS_SignerInfo *si;
+
+    for (i = 0; i < sk_CMS_CertificateChoices_num(sd->certificates); i++) {
+        cch = sk_CMS_CertificateChoices_value(sd->certificates, i);
+        if (cch->type == CMS_CERTCHOICE_OTHER) {
+            if (sd->version < 5)
+                sd->version = 5;
+        } else if (cch->type == CMS_CERTCHOICE_V2ACERT) {
+            if (sd->version < 4)
+                sd->version = 4;
+        } else if (cch->type == CMS_CERTCHOICE_V1ACERT) {
+            if (sd->version < 3)
+                sd->version = 3;
+        }
+    }
+
+    for (i = 0; i < sk_CMS_RevocationInfoChoice_num(sd->crls); i++) {
+        rch = sk_CMS_RevocationInfoChoice_value(sd->crls, i);
+        if (rch->type == CMS_REVCHOICE_OTHER) {
+            if (sd->version < 5)
+                sd->version = 5;
+        }
+    }
+
+    if ((OBJ_obj2nid(sd->encapContentInfo->eContentType) != NID_pkcs7_data)
+        && (sd->version < 3))
+        sd->version = 3;
+
+    for (i = 0; i < sk_CMS_SignerInfo_num(sd->signerInfos); i++) {
+        si = sk_CMS_SignerInfo_value(sd->signerInfos, i);
+        if (si->sid->type == CMS_SIGNERINFO_KEYIDENTIFIER) {
+            if (si->version < 3)
+                si->version = 3;
+            if (sd->version < 3)
+                sd->version = 3;
+        } else if (si->version < 1)
+            si->version = 1;
+    }
+
+    if (sd->version < 1)
+        sd->version = 1;
+
+}
+
+/* Copy an existing messageDigest value */
+
+static int cms_copy_messageDigest(CMS_ContentInfo *cms, CMS_SignerInfo *si)
+{
+    STACK_OF(CMS_SignerInfo) *sinfos;
+    CMS_SignerInfo *sitmp;
+    int i;
+    sinfos = CMS_get0_SignerInfos(cms);
+    for (i = 0; i < sk_CMS_SignerInfo_num(sinfos); i++) {
+        ASN1_OCTET_STRING *messageDigest;
+        sitmp = sk_CMS_SignerInfo_value(sinfos, i);
+        if (sitmp == si)
+            continue;
+        if (CMS_signed_get_attr_count(sitmp) < 0)
+            continue;
+        if (OBJ_cmp(si->digestAlgorithm->algorithm,
+                    sitmp->digestAlgorithm->algorithm))
+            continue;
+        messageDigest = CMS_signed_get0_data_by_OBJ(sitmp,
+                                                    OBJ_nid2obj
+                                                    (NID_pkcs9_messageDigest),
+                                                    -3, V_ASN1_OCTET_STRING);
+        if (!messageDigest) {
+            CMSerr(CMS_F_CMS_COPY_MESSAGEDIGEST,
+                   CMS_R_ERROR_READING_MESSAGEDIGEST_ATTRIBUTE);
+            return 0;
+        }
+
+        if (CMS_signed_add1_attr_by_NID(si, NID_pkcs9_messageDigest,
+                                        V_ASN1_OCTET_STRING,
+                                        messageDigest, -1))
+            return 1;
+        else
+            return 0;
+    }
+    CMSerr(CMS_F_CMS_COPY_MESSAGEDIGEST, CMS_R_NO_MATCHING_DIGEST);
+    return 0;
+}
+
+int cms_set1_SignerIdentifier(CMS_SignerIdentifier *sid, X509 *cert, int type)
+{
+    switch (type) {
+    case CMS_SIGNERINFO_ISSUER_SERIAL:
+        if (!cms_set1_ias(&sid->d.issuerAndSerialNumber, cert))
+            return 0;
+        break;
+
+    case CMS_SIGNERINFO_KEYIDENTIFIER:
+        if (!cms_set1_keyid(&sid->d.subjectKeyIdentifier, cert))
+            return 0;
+        break;
+
+    default:
+        CMSerr(CMS_F_CMS_SET1_SIGNERIDENTIFIER, CMS_R_UNKNOWN_ID);
+        return 0;
+    }
+
+    sid->type = type;
+
+    return 1;
+}
+
+int cms_SignerIdentifier_get0_signer_id(CMS_SignerIdentifier *sid,
+                                        ASN1_OCTET_STRING **keyid,
+                                        X509_NAME **issuer,
+                                        ASN1_INTEGER **sno)
+{
+    if (sid->type == CMS_SIGNERINFO_ISSUER_SERIAL) {
+        if (issuer)
+            *issuer = sid->d.issuerAndSerialNumber->issuer;
+        if (sno)
+            *sno = sid->d.issuerAndSerialNumber->serialNumber;
+    } else if (sid->type == CMS_SIGNERINFO_KEYIDENTIFIER) {
+        if (keyid)
+            *keyid = sid->d.subjectKeyIdentifier;
+    } else
+        return 0;
+    return 1;
+}
+
+int cms_SignerIdentifier_cert_cmp(CMS_SignerIdentifier *sid, X509 *cert)
+{
+    if (sid->type == CMS_SIGNERINFO_ISSUER_SERIAL)
+        return cms_ias_cert_cmp(sid->d.issuerAndSerialNumber, cert);
+    else if (sid->type == CMS_SIGNERINFO_KEYIDENTIFIER)
+        return cms_keyid_cert_cmp(sid->d.subjectKeyIdentifier, cert);
+    else
+        return -1;
+}
+
+static int cms_sd_asn1_ctrl(CMS_SignerInfo *si, int cmd)
+{
+    EVP_PKEY *pkey = si->pkey;
+    int i;
+    if (!pkey->ameth || !pkey->ameth->pkey_ctrl)
+        return 1;
+    i = pkey->ameth->pkey_ctrl(pkey, ASN1_PKEY_CTRL_CMS_SIGN, cmd, si);
+    if (i == -2) {
+        CMSerr(CMS_F_CMS_SD_ASN1_CTRL, CMS_R_NOT_SUPPORTED_FOR_THIS_KEY_TYPE);
+        return 0;
+    }
+    if (i <= 0) {
+        CMSerr(CMS_F_CMS_SD_ASN1_CTRL, CMS_R_CTRL_FAILURE);
+        return 0;
+    }
+    return 1;
+}
+
+CMS_SignerInfo *CMS_add1_signer(CMS_ContentInfo *cms,
+                                X509 *signer, EVP_PKEY *pk, const EVP_MD *md,
+                                unsigned int flags)
+{
+    CMS_SignedData *sd;
+    CMS_SignerInfo *si = NULL;
+    X509_ALGOR *alg;
+    int i, type;
+    if (!X509_check_private_key(signer, pk)) {
+        CMSerr(CMS_F_CMS_ADD1_SIGNER,
+               CMS_R_PRIVATE_KEY_DOES_NOT_MATCH_CERTIFICATE);
+        return NULL;
+    }
+    sd = cms_signed_data_init(cms);
+    if (!sd)
+        goto err;
+    si = M_ASN1_new_of(CMS_SignerInfo);
+    if (!si)
+        goto merr;
+    /* Call for side-effect of computing hash and caching extensions */
+    X509_check_purpose(signer, -1, -1);
+
+    X509_up_ref(signer);
+    EVP_PKEY_up_ref(pk);
+
+    si->pkey = pk;
+    si->signer = signer;
+    si->mctx = EVP_MD_CTX_new();
+    si->pctx = NULL;
+
+    if (si->mctx == NULL) {
+        CMSerr(CMS_F_CMS_ADD1_SIGNER, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+
+    if (flags & CMS_USE_KEYID) {
+        si->version = 3;
+        if (sd->version < 3)
+            sd->version = 3;
+        type = CMS_SIGNERINFO_KEYIDENTIFIER;
+    } else {
+        type = CMS_SIGNERINFO_ISSUER_SERIAL;
+        si->version = 1;
+    }
+
+    if (!cms_set1_SignerIdentifier(si->sid, signer, type))
+        goto err;
+
+    if (md == NULL) {
+        int def_nid;
+        if (EVP_PKEY_get_default_digest_nid(pk, &def_nid) <= 0)
+            goto err;
+        md = EVP_get_digestbynid(def_nid);
+        if (md == NULL) {
+            CMSerr(CMS_F_CMS_ADD1_SIGNER, CMS_R_NO_DEFAULT_DIGEST);
+            goto err;
+        }
+    }
+
+    if (!md) {
+        CMSerr(CMS_F_CMS_ADD1_SIGNER, CMS_R_NO_DIGEST_SET);
+        goto err;
+    }
+
+    X509_ALGOR_set_md(si->digestAlgorithm, md);
+
+    /* See if digest is present in digestAlgorithms */
+    for (i = 0; i < sk_X509_ALGOR_num(sd->digestAlgorithms); i++) {
+        const ASN1_OBJECT *aoid;
+        alg = sk_X509_ALGOR_value(sd->digestAlgorithms, i);
+        X509_ALGOR_get0(&aoid, NULL, NULL, alg);
+        if (OBJ_obj2nid(aoid) == EVP_MD_type(md))
+            break;
+    }
+
+    if (i == sk_X509_ALGOR_num(sd->digestAlgorithms)) {
+        alg = X509_ALGOR_new();
+        if (alg == NULL)
+            goto merr;
+        X509_ALGOR_set_md(alg, md);
+        if (!sk_X509_ALGOR_push(sd->digestAlgorithms, alg)) {
+            X509_ALGOR_free(alg);
+            goto merr;
+        }
+    }
+
+    if (!(flags & CMS_KEY_PARAM) && !cms_sd_asn1_ctrl(si, 0))
+        goto err;
+    if (!(flags & CMS_NOATTR)) {
+        /*
+         * Initialize signed attributes structure so other attributes
+         * such as signing time etc are added later even if we add none here.
+         */
+        if (!si->signedAttrs) {
+            si->signedAttrs = sk_X509_ATTRIBUTE_new_null();
+            if (!si->signedAttrs)
+                goto merr;
+        }
+
+        if (!(flags & CMS_NOSMIMECAP)) {
+            STACK_OF(X509_ALGOR) *smcap = NULL;
+            i = CMS_add_standard_smimecap(&smcap);
+            if (i)
+                i = CMS_add_smimecap(si, smcap);
+            sk_X509_ALGOR_pop_free(smcap, X509_ALGOR_free);
+            if (!i)
+                goto merr;
+        }
+        if (flags & CMS_REUSE_DIGEST) {
+            if (!cms_copy_messageDigest(cms, si))
+                goto err;
+            if (!(flags & (CMS_PARTIAL | CMS_KEY_PARAM)) &&
+                !CMS_SignerInfo_sign(si))
+                goto err;
+        }
+    }
+
+    if (!(flags & CMS_NOCERTS)) {
+        /* NB ignore -1 return for duplicate cert */
+        if (!CMS_add1_cert(cms, signer))
+            goto merr;
+    }
+
+    if (flags & CMS_KEY_PARAM) {
+        if (flags & CMS_NOATTR) {
+            si->pctx = EVP_PKEY_CTX_new(si->pkey, NULL);
+            if (si->pctx == NULL)
+                goto err;
+            if (EVP_PKEY_sign_init(si->pctx) <= 0)
+                goto err;
+            if (EVP_PKEY_CTX_set_signature_md(si->pctx, md) <= 0)
+                goto err;
+        } else if (EVP_DigestSignInit(si->mctx, &si->pctx, md, NULL, pk) <=
+                   0)
+            goto err;
+    }
+
+    if (!sd->signerInfos)
+        sd->signerInfos = sk_CMS_SignerInfo_new_null();
+    if (!sd->signerInfos || !sk_CMS_SignerInfo_push(sd->signerInfos, si))
+        goto merr;
+
+    return si;
+
+ merr:
+    CMSerr(CMS_F_CMS_ADD1_SIGNER, ERR_R_MALLOC_FAILURE);
+ err:
+    M_ASN1_free_of(si, CMS_SignerInfo);
+    return NULL;
+
+}
+
+static int cms_add1_signingTime(CMS_SignerInfo *si, ASN1_TIME *t)
+{
+    ASN1_TIME *tt;
+    int r = 0;
+    if (t)
+        tt = t;
+    else
+        tt = X509_gmtime_adj(NULL, 0);
+
+    if (!tt)
+        goto merr;
+
+    if (CMS_signed_add1_attr_by_NID(si, NID_pkcs9_signingTime,
+                                    tt->type, tt, -1) <= 0)
+        goto merr;
+
+    r = 1;
+
+ merr:
+
+    if (!t)
+        ASN1_TIME_free(tt);
+
+    if (!r)
+        CMSerr(CMS_F_CMS_ADD1_SIGNINGTIME, ERR_R_MALLOC_FAILURE);
+
+    return r;
+
+}
+
+EVP_PKEY_CTX *CMS_SignerInfo_get0_pkey_ctx(CMS_SignerInfo *si)
+{
+    return si->pctx;
+}
+
+EVP_MD_CTX *CMS_SignerInfo_get0_md_ctx(CMS_SignerInfo *si)
+{
+    return si->mctx;
+}
+
+STACK_OF(CMS_SignerInfo) *CMS_get0_SignerInfos(CMS_ContentInfo *cms)
+{
+    CMS_SignedData *sd;
+    sd = cms_get0_signed(cms);
+    if (!sd)
+        return NULL;
+    return sd->signerInfos;
+}
+
+STACK_OF(X509) *CMS_get0_signers(CMS_ContentInfo *cms)
+{
+    STACK_OF(X509) *signers = NULL;
+    STACK_OF(CMS_SignerInfo) *sinfos;
+    CMS_SignerInfo *si;
+    int i;
+    sinfos = CMS_get0_SignerInfos(cms);
+    for (i = 0; i < sk_CMS_SignerInfo_num(sinfos); i++) {
+        si = sk_CMS_SignerInfo_value(sinfos, i);
+        if (si->signer) {
+            if (!signers) {
+                signers = sk_X509_new_null();
+                if (!signers)
+                    return NULL;
+            }
+            if (!sk_X509_push(signers, si->signer)) {
+                sk_X509_free(signers);
+                return NULL;
+            }
+        }
+    }
+    return signers;
+}
+
+void CMS_SignerInfo_set1_signer_cert(CMS_SignerInfo *si, X509 *signer)
+{
+    if (signer) {
+        X509_up_ref(signer);
+        EVP_PKEY_free(si->pkey);
+        si->pkey = X509_get_pubkey(signer);
+    }
+    X509_free(si->signer);
+    si->signer = signer;
+}
+
+int CMS_SignerInfo_get0_signer_id(CMS_SignerInfo *si,
+                                  ASN1_OCTET_STRING **keyid,
+                                  X509_NAME **issuer, ASN1_INTEGER **sno)
+{
+    return cms_SignerIdentifier_get0_signer_id(si->sid, keyid, issuer, sno);
+}
+
+int CMS_SignerInfo_cert_cmp(CMS_SignerInfo *si, X509 *cert)
+{
+    return cms_SignerIdentifier_cert_cmp(si->sid, cert);
+}
+
+int CMS_set1_signers_certs(CMS_ContentInfo *cms, STACK_OF(X509) *scerts,
+                           unsigned int flags)
+{
+    CMS_SignedData *sd;
+    CMS_SignerInfo *si;
+    CMS_CertificateChoices *cch;
+    STACK_OF(CMS_CertificateChoices) *certs;
+    X509 *x;
+    int i, j;
+    int ret = 0;
+    sd = cms_get0_signed(cms);
+    if (!sd)
+        return -1;
+    certs = sd->certificates;
+    for (i = 0; i < sk_CMS_SignerInfo_num(sd->signerInfos); i++) {
+        si = sk_CMS_SignerInfo_value(sd->signerInfos, i);
+        if (si->signer)
+            continue;
+
+        for (j = 0; j < sk_X509_num(scerts); j++) {
+            x = sk_X509_value(scerts, j);
+            if (CMS_SignerInfo_cert_cmp(si, x) == 0) {
+                CMS_SignerInfo_set1_signer_cert(si, x);
+                ret++;
+                break;
+            }
+        }
+
+        if (si->signer || (flags & CMS_NOINTERN))
+            continue;
+
+        for (j = 0; j < sk_CMS_CertificateChoices_num(certs); j++) {
+            cch = sk_CMS_CertificateChoices_value(certs, j);
+            if (cch->type != 0)
+                continue;
+            x = cch->d.certificate;
+            if (CMS_SignerInfo_cert_cmp(si, x) == 0) {
+                CMS_SignerInfo_set1_signer_cert(si, x);
+                ret++;
+                break;
+            }
+        }
+    }
+    return ret;
+}
+
+void CMS_SignerInfo_get0_algs(CMS_SignerInfo *si, EVP_PKEY **pk,
+                              X509 **signer, X509_ALGOR **pdig,
+                              X509_ALGOR **psig)
+{
+    if (pk)
+        *pk = si->pkey;
+    if (signer)
+        *signer = si->signer;
+    if (pdig)
+        *pdig = si->digestAlgorithm;
+    if (psig)
+        *psig = si->signatureAlgorithm;
+}
+
+ASN1_OCTET_STRING *CMS_SignerInfo_get0_signature(CMS_SignerInfo *si)
+{
+    return si->signature;
+}
+
+static int cms_SignerInfo_content_sign(CMS_ContentInfo *cms,
+                                       CMS_SignerInfo *si, BIO *chain)
+{
+    EVP_MD_CTX *mctx = EVP_MD_CTX_new();
+    int r = 0;
+    EVP_PKEY_CTX *pctx = NULL;
+
+    if (mctx == NULL) {
+        CMSerr(CMS_F_CMS_SIGNERINFO_CONTENT_SIGN, ERR_R_MALLOC_FAILURE);
+        return 0;
+    }
+
+    if (!si->pkey) {
+        CMSerr(CMS_F_CMS_SIGNERINFO_CONTENT_SIGN, CMS_R_NO_PRIVATE_KEY);
+        goto err;
+    }
+
+    if (!cms_DigestAlgorithm_find_ctx(mctx, chain, si->digestAlgorithm))
+        goto err;
+    /* Set SignerInfo algorithm details if we used custom parameter */
+    if (si->pctx && !cms_sd_asn1_ctrl(si, 0))
+        goto err;
+
+    /*
+     * If any signed attributes calculate and add messageDigest attribute
+     */
+
+    if (CMS_signed_get_attr_count(si) >= 0) {
+        ASN1_OBJECT *ctype =
+            cms->d.signedData->encapContentInfo->eContentType;
+        unsigned char md[EVP_MAX_MD_SIZE];
+        unsigned int mdlen;
+        if (!EVP_DigestFinal_ex(mctx, md, &mdlen))
+            goto err;
+        if (!CMS_signed_add1_attr_by_NID(si, NID_pkcs9_messageDigest,
+                                         V_ASN1_OCTET_STRING, md, mdlen))
+            goto err;
+        /* Copy content type across */
+        if (CMS_signed_add1_attr_by_NID(si, NID_pkcs9_contentType,
+                                        V_ASN1_OBJECT, ctype, -1) <= 0)
+            goto err;
+        if (!CMS_SignerInfo_sign(si))
+            goto err;
+    } else if (si->pctx) {
+        unsigned char *sig;
+        size_t siglen;
+        unsigned char md[EVP_MAX_MD_SIZE];
+        unsigned int mdlen;
+        pctx = si->pctx;
+        if (!EVP_DigestFinal_ex(mctx, md, &mdlen))
+            goto err;
+        siglen = EVP_PKEY_size(si->pkey);
+        sig = OPENSSL_malloc(siglen);
+        if (sig == NULL) {
+            CMSerr(CMS_F_CMS_SIGNERINFO_CONTENT_SIGN, ERR_R_MALLOC_FAILURE);
+            goto err;
+        }
+        if (EVP_PKEY_sign(pctx, sig, &siglen, md, mdlen) <= 0) {
+            OPENSSL_free(sig);
+            goto err;
+        }
+        ASN1_STRING_set0(si->signature, sig, siglen);
+    } else {
+        unsigned char *sig;
+        unsigned int siglen;
+        sig = OPENSSL_malloc(EVP_PKEY_size(si->pkey));
+        if (sig == NULL) {
+            CMSerr(CMS_F_CMS_SIGNERINFO_CONTENT_SIGN, ERR_R_MALLOC_FAILURE);
+            goto err;
+        }
+        if (!EVP_SignFinal(mctx, sig, &siglen, si->pkey)) {
+            CMSerr(CMS_F_CMS_SIGNERINFO_CONTENT_SIGN, CMS_R_SIGNFINAL_ERROR);
+            OPENSSL_free(sig);
+            goto err;
+        }
+        ASN1_STRING_set0(si->signature, sig, siglen);
+    }
+
+    r = 1;
+
+ err:
+    EVP_MD_CTX_free(mctx);
+    EVP_PKEY_CTX_free(pctx);
+    return r;
+
+}
+
+int cms_SignedData_final(CMS_ContentInfo *cms, BIO *chain)
+{
+    STACK_OF(CMS_SignerInfo) *sinfos;
+    CMS_SignerInfo *si;
+    int i;
+    sinfos = CMS_get0_SignerInfos(cms);
+    for (i = 0; i < sk_CMS_SignerInfo_num(sinfos); i++) {
+        si = sk_CMS_SignerInfo_value(sinfos, i);
+        if (!cms_SignerInfo_content_sign(cms, si, chain))
+            return 0;
+    }
+    cms->d.signedData->encapContentInfo->partial = 0;
+    return 1;
+}
+
+int CMS_SignerInfo_sign(CMS_SignerInfo *si)
+{
+    EVP_MD_CTX *mctx = si->mctx;
+    EVP_PKEY_CTX *pctx;
+    unsigned char *abuf = NULL;
+    int alen;
+    size_t siglen;
+    const EVP_MD *md = NULL;
+
+    md = EVP_get_digestbyobj(si->digestAlgorithm->algorithm);
+    if (md == NULL)
+        return 0;
+
+    if (CMS_signed_get_attr_by_NID(si, NID_pkcs9_signingTime, -1) < 0) {
+        if (!cms_add1_signingTime(si, NULL))
+            goto err;
+    }
+
+    if (si->pctx)
+        pctx = si->pctx;
+    else {
+        EVP_MD_CTX_reset(mctx);
+        if (EVP_DigestSignInit(mctx, &pctx, md, NULL, si->pkey) <= 0)
+            goto err;
+    }
+
+    if (EVP_PKEY_CTX_ctrl(pctx, -1, EVP_PKEY_OP_SIGN,
+                          EVP_PKEY_CTRL_CMS_SIGN, 0, si) <= 0) {
+        CMSerr(CMS_F_CMS_SIGNERINFO_SIGN, CMS_R_CTRL_ERROR);
+        goto err;
+    }
+
+    alen = ASN1_item_i2d((ASN1_VALUE *)si->signedAttrs, &abuf,
+                         ASN1_ITEM_rptr(CMS_Attributes_Sign));
+    if (!abuf)
+        goto err;
+    if (EVP_DigestSignUpdate(mctx, abuf, alen) <= 0)
+        goto err;
+    if (EVP_DigestSignFinal(mctx, NULL, &siglen) <= 0)
+        goto err;
+    OPENSSL_free(abuf);
+    abuf = OPENSSL_malloc(siglen);
+    if (abuf == NULL)
+        goto err;
+    if (EVP_DigestSignFinal(mctx, abuf, &siglen) <= 0)
+        goto err;
+
+    if (EVP_PKEY_CTX_ctrl(pctx, -1, EVP_PKEY_OP_SIGN,
+                          EVP_PKEY_CTRL_CMS_SIGN, 1, si) <= 0) {
+        CMSerr(CMS_F_CMS_SIGNERINFO_SIGN, CMS_R_CTRL_ERROR);
+        goto err;
+    }
+
+    EVP_MD_CTX_reset(mctx);
+
+    ASN1_STRING_set0(si->signature, abuf, siglen);
+
+    return 1;
+
+ err:
+    OPENSSL_free(abuf);
+    EVP_MD_CTX_reset(mctx);
+    return 0;
+
+}
+
+int CMS_SignerInfo_verify(CMS_SignerInfo *si)
+{
+    EVP_MD_CTX *mctx = NULL;
+    unsigned char *abuf = NULL;
+    int alen, r = -1;
+    const EVP_MD *md = NULL;
+
+    if (!si->pkey) {
+        CMSerr(CMS_F_CMS_SIGNERINFO_VERIFY, CMS_R_NO_PUBLIC_KEY);
+        return -1;
+    }
+
+    md = EVP_get_digestbyobj(si->digestAlgorithm->algorithm);
+    if (md == NULL)
+        return -1;
+    if (si->mctx == NULL)
+        si->mctx = EVP_MD_CTX_new();
+    mctx = si->mctx;
+    if (EVP_DigestVerifyInit(mctx, &si->pctx, md, NULL, si->pkey) <= 0)
+        goto err;
+
+    if (!cms_sd_asn1_ctrl(si, 1))
+        goto err;
+
+    alen = ASN1_item_i2d((ASN1_VALUE *)si->signedAttrs, &abuf,
+                         ASN1_ITEM_rptr(CMS_Attributes_Verify));
+    if (!abuf)
+        goto err;
+    r = EVP_DigestVerifyUpdate(mctx, abuf, alen);
+    OPENSSL_free(abuf);
+    if (r <= 0) {
+        r = -1;
+        goto err;
+    }
+    r = EVP_DigestVerifyFinal(mctx,
+                              si->signature->data, si->signature->length);
+    if (r <= 0)
+        CMSerr(CMS_F_CMS_SIGNERINFO_VERIFY, CMS_R_VERIFICATION_FAILURE);
+ err:
+    EVP_MD_CTX_reset(mctx);
+    return r;
+}
+
+/* Create a chain of digest BIOs from a CMS ContentInfo */
+
+BIO *cms_SignedData_init_bio(CMS_ContentInfo *cms)
+{
+    int i;
+    CMS_SignedData *sd;
+    BIO *chain = NULL;
+    sd = cms_get0_signed(cms);
+    if (!sd)
+        return NULL;
+    if (cms->d.signedData->encapContentInfo->partial)
+        cms_sd_set_version(sd);
+    for (i = 0; i < sk_X509_ALGOR_num(sd->digestAlgorithms); i++) {
+        X509_ALGOR *digestAlgorithm;
+        BIO *mdbio;
+        digestAlgorithm = sk_X509_ALGOR_value(sd->digestAlgorithms, i);
+        mdbio = cms_DigestAlgorithm_init_bio(digestAlgorithm);
+        if (!mdbio)
+            goto err;
+        if (chain)
+            BIO_push(chain, mdbio);
+        else
+            chain = mdbio;
+    }
+    return chain;
+ err:
+    BIO_free_all(chain);
+    return NULL;
+}
+
+int CMS_SignerInfo_verify_content(CMS_SignerInfo *si, BIO *chain)
+{
+    ASN1_OCTET_STRING *os = NULL;
+    EVP_MD_CTX *mctx = EVP_MD_CTX_new();
+    EVP_PKEY_CTX *pkctx = NULL;
+    int r = -1;
+    unsigned char mval[EVP_MAX_MD_SIZE];
+    unsigned int mlen;
+
+    if (mctx == NULL) {
+        CMSerr(CMS_F_CMS_SIGNERINFO_VERIFY_CONTENT, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+    /* If we have any signed attributes look for messageDigest value */
+    if (CMS_signed_get_attr_count(si) >= 0) {
+        os = CMS_signed_get0_data_by_OBJ(si,
+                                         OBJ_nid2obj(NID_pkcs9_messageDigest),
+                                         -3, V_ASN1_OCTET_STRING);
+        if (!os) {
+            CMSerr(CMS_F_CMS_SIGNERINFO_VERIFY_CONTENT,
+                   CMS_R_ERROR_READING_MESSAGEDIGEST_ATTRIBUTE);
+            goto err;
+        }
+    }
+
+    if (!cms_DigestAlgorithm_find_ctx(mctx, chain, si->digestAlgorithm))
+        goto err;
+
+    if (EVP_DigestFinal_ex(mctx, mval, &mlen) <= 0) {
+        CMSerr(CMS_F_CMS_SIGNERINFO_VERIFY_CONTENT,
+               CMS_R_UNABLE_TO_FINALIZE_CONTEXT);
+        goto err;
+    }
+
+    /* If messageDigest found compare it */
+
+    if (os) {
+        if (mlen != (unsigned int)os->length) {
+            CMSerr(CMS_F_CMS_SIGNERINFO_VERIFY_CONTENT,
+                   CMS_R_MESSAGEDIGEST_ATTRIBUTE_WRONG_LENGTH);
+            goto err;
+        }
+
+        if (memcmp(mval, os->data, mlen)) {
+            CMSerr(CMS_F_CMS_SIGNERINFO_VERIFY_CONTENT,
+                   CMS_R_VERIFICATION_FAILURE);
+            r = 0;
+        } else
+            r = 1;
+    } else {
+        const EVP_MD *md = EVP_MD_CTX_md(mctx);
+        pkctx = EVP_PKEY_CTX_new(si->pkey, NULL);
+        if (pkctx == NULL)
+            goto err;
+        if (EVP_PKEY_verify_init(pkctx) <= 0)
+            goto err;
+        if (EVP_PKEY_CTX_set_signature_md(pkctx, md) <= 0)
+            goto err;
+        si->pctx = pkctx;
+        if (!cms_sd_asn1_ctrl(si, 1))
+            goto err;
+        r = EVP_PKEY_verify(pkctx, si->signature->data,
+                            si->signature->length, mval, mlen);
+        if (r <= 0) {
+            CMSerr(CMS_F_CMS_SIGNERINFO_VERIFY_CONTENT,
+                   CMS_R_VERIFICATION_FAILURE);
+            r = 0;
+        }
+    }
+
+ err:
+    EVP_PKEY_CTX_free(pkctx);
+    EVP_MD_CTX_free(mctx);
+    return r;
+
+}
+
+int CMS_add_smimecap(CMS_SignerInfo *si, STACK_OF(X509_ALGOR) *algs)
+{
+    unsigned char *smder = NULL;
+    int smderlen, r;
+    smderlen = i2d_X509_ALGORS(algs, &smder);
+    if (smderlen <= 0)
+        return 0;
+    r = CMS_signed_add1_attr_by_NID(si, NID_SMIMECapabilities,
+                                    V_ASN1_SEQUENCE, smder, smderlen);
+    OPENSSL_free(smder);
+    return r;
+}
+
+int CMS_add_simple_smimecap(STACK_OF(X509_ALGOR) **algs,
+                            int algnid, int keysize)
+{
+    X509_ALGOR *alg;
+    ASN1_INTEGER *key = NULL;
+    if (keysize > 0) {
+        key = ASN1_INTEGER_new();
+        if (key == NULL || !ASN1_INTEGER_set(key, keysize))
+            return 0;
+    }
+    alg = X509_ALGOR_new();
+    if (alg == NULL) {
+        ASN1_INTEGER_free(key);
+        return 0;
+    }
+
+    X509_ALGOR_set0(alg, OBJ_nid2obj(algnid),
+                    key ? V_ASN1_INTEGER : V_ASN1_UNDEF, key);
+    if (*algs == NULL)
+        *algs = sk_X509_ALGOR_new_null();
+    if (*algs == NULL || !sk_X509_ALGOR_push(*algs, alg)) {
+        X509_ALGOR_free(alg);
+        return 0;
+    }
+    return 1;
+}
+
+/* Check to see if a cipher exists and if so add S/MIME capabilities */
+
+static int cms_add_cipher_smcap(STACK_OF(X509_ALGOR) **sk, int nid, int arg)
+{
+    if (EVP_get_cipherbynid(nid))
+        return CMS_add_simple_smimecap(sk, nid, arg);
+    return 1;
+}
+
+static int cms_add_digest_smcap(STACK_OF(X509_ALGOR) **sk, int nid, int arg)
+{
+    if (EVP_get_digestbynid(nid))
+        return CMS_add_simple_smimecap(sk, nid, arg);
+    return 1;
+}
+
+int CMS_add_standard_smimecap(STACK_OF(X509_ALGOR) **smcap)
+{
+    if (!cms_add_cipher_smcap(smcap, NID_aes_256_cbc, -1)
+        || !cms_add_digest_smcap(smcap, NID_id_GostR3411_2012_256, -1)
+        || !cms_add_digest_smcap(smcap, NID_id_GostR3411_2012_512, -1)
+        || !cms_add_digest_smcap(smcap, NID_id_GostR3411_94, -1)
+        || !cms_add_cipher_smcap(smcap, NID_id_Gost28147_89, -1)
+        || !cms_add_cipher_smcap(smcap, NID_aes_192_cbc, -1)
+        || !cms_add_cipher_smcap(smcap, NID_aes_128_cbc, -1)
+        || !cms_add_cipher_smcap(smcap, NID_des_ede3_cbc, -1)
+        || !cms_add_cipher_smcap(smcap, NID_rc2_cbc, 128)
+        || !cms_add_cipher_smcap(smcap, NID_rc2_cbc, 64)
+        || !cms_add_cipher_smcap(smcap, NID_des_cbc, -1)
+        || !cms_add_cipher_smcap(smcap, NID_rc2_cbc, 40))
+        return 0;
+    return 1;
+}
diff --git a/crypto/cms/cms_smime.c b/crypto/cms/cms_smime.c
new file mode 100644
index 000000000..68737aa06
--- /dev/null
+++ b/crypto/cms/cms_smime.c
@@ -0,0 +1,849 @@
+/*
+ * Copyright 2008-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "internal/cryptlib.h"
+#include 
+#include 
+#include 
+#include 
+#include 
+#include "cms_lcl.h"
+#include "internal/asn1_int.h"
+
+static BIO *cms_get_text_bio(BIO *out, unsigned int flags)
+{
+    BIO *rbio;
+    if (out == NULL)
+        rbio = BIO_new(BIO_s_null());
+    else if (flags & CMS_TEXT) {
+        rbio = BIO_new(BIO_s_mem());
+        BIO_set_mem_eof_return(rbio, 0);
+    } else
+        rbio = out;
+    return rbio;
+}
+
+static int cms_copy_content(BIO *out, BIO *in, unsigned int flags)
+{
+    unsigned char buf[4096];
+    int r = 0, i;
+    BIO *tmpout;
+
+    tmpout = cms_get_text_bio(out, flags);
+
+    if (tmpout == NULL) {
+        CMSerr(CMS_F_CMS_COPY_CONTENT, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+
+    /* Read all content through chain to process digest, decrypt etc */
+    for (;;) {
+        i = BIO_read(in, buf, sizeof(buf));
+        if (i <= 0) {
+            if (BIO_method_type(in) == BIO_TYPE_CIPHER) {
+                if (!BIO_get_cipher_status(in))
+                    goto err;
+            }
+            if (i < 0)
+                goto err;
+            break;
+        }
+
+        if (tmpout && (BIO_write(tmpout, buf, i) != i))
+            goto err;
+    }
+
+    if (flags & CMS_TEXT) {
+        if (!SMIME_text(tmpout, out)) {
+            CMSerr(CMS_F_CMS_COPY_CONTENT, CMS_R_SMIME_TEXT_ERROR);
+            goto err;
+        }
+    }
+
+    r = 1;
+
+ err:
+    if (tmpout != out)
+        BIO_free(tmpout);
+    return r;
+
+}
+
+static int check_content(CMS_ContentInfo *cms)
+{
+    ASN1_OCTET_STRING **pos = CMS_get0_content(cms);
+    if (!pos || !*pos) {
+        CMSerr(CMS_F_CHECK_CONTENT, CMS_R_NO_CONTENT);
+        return 0;
+    }
+    return 1;
+}
+
+static void do_free_upto(BIO *f, BIO *upto)
+{
+    if (upto) {
+        BIO *tbio;
+        do {
+            tbio = BIO_pop(f);
+            BIO_free(f);
+            f = tbio;
+        }
+        while (f && f != upto);
+    } else
+        BIO_free_all(f);
+}
+
+int CMS_data(CMS_ContentInfo *cms, BIO *out, unsigned int flags)
+{
+    BIO *cont;
+    int r;
+    if (OBJ_obj2nid(CMS_get0_type(cms)) != NID_pkcs7_data) {
+        CMSerr(CMS_F_CMS_DATA, CMS_R_TYPE_NOT_DATA);
+        return 0;
+    }
+    cont = CMS_dataInit(cms, NULL);
+    if (!cont)
+        return 0;
+    r = cms_copy_content(out, cont, flags);
+    BIO_free_all(cont);
+    return r;
+}
+
+CMS_ContentInfo *CMS_data_create(BIO *in, unsigned int flags)
+{
+    CMS_ContentInfo *cms;
+    cms = cms_Data_create();
+    if (!cms)
+        return NULL;
+
+    if ((flags & CMS_STREAM) || CMS_final(cms, in, NULL, flags))
+        return cms;
+
+    CMS_ContentInfo_free(cms);
+
+    return NULL;
+}
+
+int CMS_digest_verify(CMS_ContentInfo *cms, BIO *dcont, BIO *out,
+                      unsigned int flags)
+{
+    BIO *cont;
+    int r;
+    if (OBJ_obj2nid(CMS_get0_type(cms)) != NID_pkcs7_digest) {
+        CMSerr(CMS_F_CMS_DIGEST_VERIFY, CMS_R_TYPE_NOT_DIGESTED_DATA);
+        return 0;
+    }
+
+    if (!dcont && !check_content(cms))
+        return 0;
+
+    cont = CMS_dataInit(cms, dcont);
+    if (!cont)
+        return 0;
+    r = cms_copy_content(out, cont, flags);
+    if (r)
+        r = cms_DigestedData_do_final(cms, cont, 1);
+    do_free_upto(cont, dcont);
+    return r;
+}
+
+CMS_ContentInfo *CMS_digest_create(BIO *in, const EVP_MD *md,
+                                   unsigned int flags)
+{
+    CMS_ContentInfo *cms;
+    if (!md)
+#ifndef OPENSSL_NO_SHA
+        md = EVP_sha1();
+#else
+        md = EVP_get_default_digest();
+#endif
+
+    cms = cms_DigestedData_create(md);
+    if (!cms)
+        return NULL;
+
+    if (!(flags & CMS_DETACHED))
+        CMS_set_detached(cms, 0);
+
+    if ((flags & CMS_STREAM) || CMS_final(cms, in, NULL, flags))
+        return cms;
+
+    CMS_ContentInfo_free(cms);
+    return NULL;
+}
+
+int CMS_EncryptedData_decrypt(CMS_ContentInfo *cms,
+                              const unsigned char *key, size_t keylen,
+                              BIO *dcont, BIO *out, unsigned int flags)
+{
+    BIO *cont;
+    int r;
+    if (OBJ_obj2nid(CMS_get0_type(cms)) != NID_pkcs7_encrypted) {
+        CMSerr(CMS_F_CMS_ENCRYPTEDDATA_DECRYPT,
+               CMS_R_TYPE_NOT_ENCRYPTED_DATA);
+        return 0;
+    }
+
+    if (!dcont && !check_content(cms))
+        return 0;
+
+    if (CMS_EncryptedData_set1_key(cms, NULL, key, keylen) <= 0)
+        return 0;
+    cont = CMS_dataInit(cms, dcont);
+    if (!cont)
+        return 0;
+    r = cms_copy_content(out, cont, flags);
+    do_free_upto(cont, dcont);
+    return r;
+}
+
+CMS_ContentInfo *CMS_EncryptedData_encrypt(BIO *in, const EVP_CIPHER *cipher,
+                                           const unsigned char *key,
+                                           size_t keylen, unsigned int flags)
+{
+    CMS_ContentInfo *cms;
+    if (!cipher) {
+        CMSerr(CMS_F_CMS_ENCRYPTEDDATA_ENCRYPT, CMS_R_NO_CIPHER);
+        return NULL;
+    }
+    cms = CMS_ContentInfo_new();
+    if (cms == NULL)
+        return NULL;
+    if (!CMS_EncryptedData_set1_key(cms, cipher, key, keylen))
+        return NULL;
+
+    if (!(flags & CMS_DETACHED))
+        CMS_set_detached(cms, 0);
+
+    if ((flags & (CMS_STREAM | CMS_PARTIAL))
+        || CMS_final(cms, in, NULL, flags))
+        return cms;
+
+    CMS_ContentInfo_free(cms);
+    return NULL;
+}
+
+static int cms_signerinfo_verify_cert(CMS_SignerInfo *si,
+                                      X509_STORE *store,
+                                      STACK_OF(X509) *certs,
+                                      STACK_OF(X509_CRL) *crls)
+{
+    X509_STORE_CTX *ctx = X509_STORE_CTX_new();
+    X509 *signer;
+    int i, j, r = 0;
+
+    if (ctx == NULL) {
+        CMSerr(CMS_F_CMS_SIGNERINFO_VERIFY_CERT, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+    CMS_SignerInfo_get0_algs(si, NULL, &signer, NULL, NULL);
+    if (!X509_STORE_CTX_init(ctx, store, signer, certs)) {
+        CMSerr(CMS_F_CMS_SIGNERINFO_VERIFY_CERT, CMS_R_STORE_INIT_ERROR);
+        goto err;
+    }
+    X509_STORE_CTX_set_default(ctx, "smime_sign");
+    if (crls)
+        X509_STORE_CTX_set0_crls(ctx, crls);
+
+    i = X509_verify_cert(ctx);
+    if (i <= 0) {
+        j = X509_STORE_CTX_get_error(ctx);
+        CMSerr(CMS_F_CMS_SIGNERINFO_VERIFY_CERT,
+               CMS_R_CERTIFICATE_VERIFY_ERROR);
+        ERR_add_error_data(2, "Verify error:",
+                           X509_verify_cert_error_string(j));
+        goto err;
+    }
+    r = 1;
+ err:
+    X509_STORE_CTX_free(ctx);
+    return r;
+
+}
+
+int CMS_verify(CMS_ContentInfo *cms, STACK_OF(X509) *certs,
+               X509_STORE *store, BIO *dcont, BIO *out, unsigned int flags)
+{
+    CMS_SignerInfo *si;
+    STACK_OF(CMS_SignerInfo) *sinfos;
+    STACK_OF(X509) *cms_certs = NULL;
+    STACK_OF(X509_CRL) *crls = NULL;
+    X509 *signer;
+    int i, scount = 0, ret = 0;
+    BIO *cmsbio = NULL, *tmpin = NULL, *tmpout = NULL;
+
+    if (!dcont && !check_content(cms))
+        return 0;
+    if (dcont && !(flags & CMS_BINARY)) {
+        const ASN1_OBJECT *coid = CMS_get0_eContentType(cms);
+        if (OBJ_obj2nid(coid) == NID_id_ct_asciiTextWithCRLF)
+            flags |= CMS_ASCIICRLF;
+    }
+
+    /* Attempt to find all signer certificates */
+
+    sinfos = CMS_get0_SignerInfos(cms);
+
+    if (sk_CMS_SignerInfo_num(sinfos) <= 0) {
+        CMSerr(CMS_F_CMS_VERIFY, CMS_R_NO_SIGNERS);
+        goto err;
+    }
+
+    for (i = 0; i < sk_CMS_SignerInfo_num(sinfos); i++) {
+        si = sk_CMS_SignerInfo_value(sinfos, i);
+        CMS_SignerInfo_get0_algs(si, NULL, &signer, NULL, NULL);
+        if (signer)
+            scount++;
+    }
+
+    if (scount != sk_CMS_SignerInfo_num(sinfos))
+        scount += CMS_set1_signers_certs(cms, certs, flags);
+
+    if (scount != sk_CMS_SignerInfo_num(sinfos)) {
+        CMSerr(CMS_F_CMS_VERIFY, CMS_R_SIGNER_CERTIFICATE_NOT_FOUND);
+        goto err;
+    }
+
+    /* Attempt to verify all signers certs */
+
+    if (!(flags & CMS_NO_SIGNER_CERT_VERIFY)) {
+        cms_certs = CMS_get1_certs(cms);
+        if (!(flags & CMS_NOCRL))
+            crls = CMS_get1_crls(cms);
+        for (i = 0; i < sk_CMS_SignerInfo_num(sinfos); i++) {
+            si = sk_CMS_SignerInfo_value(sinfos, i);
+            if (!cms_signerinfo_verify_cert(si, store, cms_certs, crls))
+                goto err;
+        }
+    }
+
+    /* Attempt to verify all SignerInfo signed attribute signatures */
+
+    if (!(flags & CMS_NO_ATTR_VERIFY)) {
+        for (i = 0; i < sk_CMS_SignerInfo_num(sinfos); i++) {
+            si = sk_CMS_SignerInfo_value(sinfos, i);
+            if (CMS_signed_get_attr_count(si) < 0)
+                continue;
+            if (CMS_SignerInfo_verify(si) <= 0)
+                goto err;
+        }
+    }
+
+    /*
+     * Performance optimization: if the content is a memory BIO then store
+     * its contents in a temporary read only memory BIO. This avoids
+     * potentially large numbers of slow copies of data which will occur when
+     * reading from a read write memory BIO when signatures are calculated.
+     */
+
+    if (dcont && (BIO_method_type(dcont) == BIO_TYPE_MEM)) {
+        char *ptr;
+        long len;
+        len = BIO_get_mem_data(dcont, &ptr);
+        tmpin = BIO_new_mem_buf(ptr, len);
+        if (tmpin == NULL) {
+            CMSerr(CMS_F_CMS_VERIFY, ERR_R_MALLOC_FAILURE);
+            goto err2;
+        }
+    } else
+        tmpin = dcont;
+    /*
+     * If not binary mode and detached generate digests by *writing* through
+     * the BIO. That makes it possible to canonicalise the input.
+     */
+    if (!(flags & SMIME_BINARY) && dcont) {
+        /*
+         * Create output BIO so we can either handle text or to ensure
+         * included content doesn't override detached content.
+         */
+        tmpout = cms_get_text_bio(out, flags);
+        if (!tmpout) {
+            CMSerr(CMS_F_CMS_VERIFY, ERR_R_MALLOC_FAILURE);
+            goto err;
+        }
+        cmsbio = CMS_dataInit(cms, tmpout);
+        if (!cmsbio)
+            goto err;
+        /*
+         * Don't use SMIME_TEXT for verify: it adds headers and we want to
+         * remove them.
+         */
+        SMIME_crlf_copy(dcont, cmsbio, flags & ~SMIME_TEXT);
+
+        if (flags & CMS_TEXT) {
+            if (!SMIME_text(tmpout, out)) {
+                CMSerr(CMS_F_CMS_VERIFY, CMS_R_SMIME_TEXT_ERROR);
+                goto err;
+            }
+        }
+    } else {
+        cmsbio = CMS_dataInit(cms, tmpin);
+        if (!cmsbio)
+            goto err;
+
+        if (!cms_copy_content(out, cmsbio, flags))
+            goto err;
+
+    }
+    if (!(flags & CMS_NO_CONTENT_VERIFY)) {
+        for (i = 0; i < sk_CMS_SignerInfo_num(sinfos); i++) {
+            si = sk_CMS_SignerInfo_value(sinfos, i);
+            if (CMS_SignerInfo_verify_content(si, cmsbio) <= 0) {
+                CMSerr(CMS_F_CMS_VERIFY, CMS_R_CONTENT_VERIFY_ERROR);
+                goto err;
+            }
+        }
+    }
+
+    ret = 1;
+
+ err:
+    if (!(flags & SMIME_BINARY) && dcont) {
+        do_free_upto(cmsbio, tmpout);
+        if (tmpin != dcont)
+            BIO_free(tmpin);
+    } else {
+        if (dcont && (tmpin == dcont))
+            do_free_upto(cmsbio, dcont);
+        else
+            BIO_free_all(cmsbio);
+    }
+
+    if (out != tmpout)
+        BIO_free_all(tmpout);
+
+ err2:
+    sk_X509_pop_free(cms_certs, X509_free);
+    sk_X509_CRL_pop_free(crls, X509_CRL_free);
+
+    return ret;
+}
+
+int CMS_verify_receipt(CMS_ContentInfo *rcms, CMS_ContentInfo *ocms,
+                       STACK_OF(X509) *certs,
+                       X509_STORE *store, unsigned int flags)
+{
+    int r;
+    flags &= ~(CMS_DETACHED | CMS_TEXT);
+    r = CMS_verify(rcms, certs, store, NULL, NULL, flags);
+    if (r <= 0)
+        return r;
+    return cms_Receipt_verify(rcms, ocms);
+}
+
+CMS_ContentInfo *CMS_sign(X509 *signcert, EVP_PKEY *pkey,
+                          STACK_OF(X509) *certs, BIO *data,
+                          unsigned int flags)
+{
+    CMS_ContentInfo *cms;
+    int i;
+
+    cms = CMS_ContentInfo_new();
+    if (cms == NULL || !CMS_SignedData_init(cms))
+        goto merr;
+    if (flags & CMS_ASCIICRLF
+        && !CMS_set1_eContentType(cms,
+                                  OBJ_nid2obj(NID_id_ct_asciiTextWithCRLF)))
+        goto err;
+
+    if (pkey && !CMS_add1_signer(cms, signcert, pkey, NULL, flags)) {
+        CMSerr(CMS_F_CMS_SIGN, CMS_R_ADD_SIGNER_ERROR);
+        goto err;
+    }
+
+    for (i = 0; i < sk_X509_num(certs); i++) {
+        X509 *x = sk_X509_value(certs, i);
+        if (!CMS_add1_cert(cms, x))
+            goto merr;
+    }
+
+    if (!(flags & CMS_DETACHED))
+        CMS_set_detached(cms, 0);
+
+    if ((flags & (CMS_STREAM | CMS_PARTIAL))
+        || CMS_final(cms, data, NULL, flags))
+        return cms;
+    else
+        goto err;
+
+ merr:
+    CMSerr(CMS_F_CMS_SIGN, ERR_R_MALLOC_FAILURE);
+
+ err:
+    CMS_ContentInfo_free(cms);
+    return NULL;
+}
+
+CMS_ContentInfo *CMS_sign_receipt(CMS_SignerInfo *si,
+                                  X509 *signcert, EVP_PKEY *pkey,
+                                  STACK_OF(X509) *certs, unsigned int flags)
+{
+    CMS_SignerInfo *rct_si;
+    CMS_ContentInfo *cms = NULL;
+    ASN1_OCTET_STRING **pos, *os;
+    BIO *rct_cont = NULL;
+    int r = 0;
+
+    flags &= ~(CMS_STREAM | CMS_TEXT);
+    /* Not really detached but avoids content being allocated */
+    flags |= CMS_PARTIAL | CMS_BINARY | CMS_DETACHED;
+    if (!pkey || !signcert) {
+        CMSerr(CMS_F_CMS_SIGN_RECEIPT, CMS_R_NO_KEY_OR_CERT);
+        return NULL;
+    }
+
+    /* Initialize signed data */
+
+    cms = CMS_sign(NULL, NULL, certs, NULL, flags);
+    if (!cms)
+        goto err;
+
+    /* Set inner content type to signed receipt */
+    if (!CMS_set1_eContentType(cms, OBJ_nid2obj(NID_id_smime_ct_receipt)))
+        goto err;
+
+    rct_si = CMS_add1_signer(cms, signcert, pkey, NULL, flags);
+    if (!rct_si) {
+        CMSerr(CMS_F_CMS_SIGN_RECEIPT, CMS_R_ADD_SIGNER_ERROR);
+        goto err;
+    }
+
+    os = cms_encode_Receipt(si);
+
+    if (!os)
+        goto err;
+
+    /* Set content to digest */
+    rct_cont = BIO_new_mem_buf(os->data, os->length);
+    if (!rct_cont)
+        goto err;
+
+    /* Add msgSigDigest attribute */
+
+    if (!cms_msgSigDigest_add1(rct_si, si))
+        goto err;
+
+    /* Finalize structure */
+    if (!CMS_final(cms, rct_cont, NULL, flags))
+        goto err;
+
+    /* Set embedded content */
+    pos = CMS_get0_content(cms);
+    *pos = os;
+
+    r = 1;
+
+ err:
+    BIO_free(rct_cont);
+    if (r)
+        return cms;
+    CMS_ContentInfo_free(cms);
+    return NULL;
+
+}
+
+CMS_ContentInfo *CMS_encrypt(STACK_OF(X509) *certs, BIO *data,
+                             const EVP_CIPHER *cipher, unsigned int flags)
+{
+    CMS_ContentInfo *cms;
+    int i;
+    X509 *recip;
+    cms = CMS_EnvelopedData_create(cipher);
+    if (!cms)
+        goto merr;
+    for (i = 0; i < sk_X509_num(certs); i++) {
+        recip = sk_X509_value(certs, i);
+        if (!CMS_add1_recipient_cert(cms, recip, flags)) {
+            CMSerr(CMS_F_CMS_ENCRYPT, CMS_R_RECIPIENT_ERROR);
+            goto err;
+        }
+    }
+
+    if (!(flags & CMS_DETACHED))
+        CMS_set_detached(cms, 0);
+
+    if ((flags & (CMS_STREAM | CMS_PARTIAL))
+        || CMS_final(cms, data, NULL, flags))
+        return cms;
+    else
+        goto err;
+
+ merr:
+    CMSerr(CMS_F_CMS_ENCRYPT, ERR_R_MALLOC_FAILURE);
+ err:
+    CMS_ContentInfo_free(cms);
+    return NULL;
+}
+
+static int cms_kari_set1_pkey(CMS_ContentInfo *cms, CMS_RecipientInfo *ri,
+                              EVP_PKEY *pk, X509 *cert)
+{
+    int i;
+    STACK_OF(CMS_RecipientEncryptedKey) *reks;
+    CMS_RecipientEncryptedKey *rek;
+    reks = CMS_RecipientInfo_kari_get0_reks(ri);
+    if (!cert)
+        return 0;
+    for (i = 0; i < sk_CMS_RecipientEncryptedKey_num(reks); i++) {
+        int rv;
+        rek = sk_CMS_RecipientEncryptedKey_value(reks, i);
+        if (CMS_RecipientEncryptedKey_cert_cmp(rek, cert))
+            continue;
+        CMS_RecipientInfo_kari_set0_pkey(ri, pk);
+        rv = CMS_RecipientInfo_kari_decrypt(cms, ri, rek);
+        CMS_RecipientInfo_kari_set0_pkey(ri, NULL);
+        if (rv > 0)
+            return 1;
+        return -1;
+    }
+    return 0;
+}
+
+int CMS_decrypt_set1_pkey(CMS_ContentInfo *cms, EVP_PKEY *pk, X509 *cert)
+{
+    STACK_OF(CMS_RecipientInfo) *ris;
+    CMS_RecipientInfo *ri;
+    int i, r, ri_type;
+    int debug = 0, match_ri = 0;
+    ris = CMS_get0_RecipientInfos(cms);
+    if (ris)
+        debug = cms->d.envelopedData->encryptedContentInfo->debug;
+    ri_type = cms_pkey_get_ri_type(pk);
+    if (ri_type == CMS_RECIPINFO_NONE) {
+        CMSerr(CMS_F_CMS_DECRYPT_SET1_PKEY,
+               CMS_R_NOT_SUPPORTED_FOR_THIS_KEY_TYPE);
+        return 0;
+    }
+
+    for (i = 0; i < sk_CMS_RecipientInfo_num(ris); i++) {
+        ri = sk_CMS_RecipientInfo_value(ris, i);
+        if (CMS_RecipientInfo_type(ri) != ri_type)
+            continue;
+        match_ri = 1;
+        if (ri_type == CMS_RECIPINFO_AGREE) {
+            r = cms_kari_set1_pkey(cms, ri, pk, cert);
+            if (r > 0)
+                return 1;
+            if (r < 0)
+                return 0;
+        }
+        /*
+         * If we have a cert try matching RecipientInfo otherwise try them
+         * all.
+         */
+        else if (!cert || !CMS_RecipientInfo_ktri_cert_cmp(ri, cert)) {
+            CMS_RecipientInfo_set0_pkey(ri, pk);
+            r = CMS_RecipientInfo_decrypt(cms, ri);
+            CMS_RecipientInfo_set0_pkey(ri, NULL);
+            if (cert) {
+                /*
+                 * If not debugging clear any error and return success to
+                 * avoid leaking of information useful to MMA
+                 */
+                if (!debug) {
+                    ERR_clear_error();
+                    return 1;
+                }
+                if (r > 0)
+                    return 1;
+                CMSerr(CMS_F_CMS_DECRYPT_SET1_PKEY, CMS_R_DECRYPT_ERROR);
+                return 0;
+            }
+            /*
+             * If no cert and not debugging don't leave loop after first
+             * successful decrypt. Always attempt to decrypt all recipients
+             * to avoid leaking timing of a successful decrypt.
+             */
+            else if (r > 0 && debug)
+                return 1;
+        }
+    }
+    /* If no cert and not debugging always return success */
+    if (match_ri && !cert && !debug) {
+        ERR_clear_error();
+        return 1;
+    }
+
+    CMSerr(CMS_F_CMS_DECRYPT_SET1_PKEY, CMS_R_NO_MATCHING_RECIPIENT);
+    return 0;
+
+}
+
+int CMS_decrypt_set1_key(CMS_ContentInfo *cms,
+                         unsigned char *key, size_t keylen,
+                         const unsigned char *id, size_t idlen)
+{
+    STACK_OF(CMS_RecipientInfo) *ris;
+    CMS_RecipientInfo *ri;
+    int i, r;
+    ris = CMS_get0_RecipientInfos(cms);
+    for (i = 0; i < sk_CMS_RecipientInfo_num(ris); i++) {
+        ri = sk_CMS_RecipientInfo_value(ris, i);
+        if (CMS_RecipientInfo_type(ri) != CMS_RECIPINFO_KEK)
+            continue;
+
+        /*
+         * If we have an id try matching RecipientInfo otherwise try them
+         * all.
+         */
+        if (!id || (CMS_RecipientInfo_kekri_id_cmp(ri, id, idlen) == 0)) {
+            CMS_RecipientInfo_set0_key(ri, key, keylen);
+            r = CMS_RecipientInfo_decrypt(cms, ri);
+            CMS_RecipientInfo_set0_key(ri, NULL, 0);
+            if (r > 0)
+                return 1;
+            if (id) {
+                CMSerr(CMS_F_CMS_DECRYPT_SET1_KEY, CMS_R_DECRYPT_ERROR);
+                return 0;
+            }
+            ERR_clear_error();
+        }
+    }
+
+    CMSerr(CMS_F_CMS_DECRYPT_SET1_KEY, CMS_R_NO_MATCHING_RECIPIENT);
+    return 0;
+
+}
+
+int CMS_decrypt_set1_password(CMS_ContentInfo *cms,
+                              unsigned char *pass, ossl_ssize_t passlen)
+{
+    STACK_OF(CMS_RecipientInfo) *ris;
+    CMS_RecipientInfo *ri;
+    int i, r;
+    ris = CMS_get0_RecipientInfos(cms);
+    for (i = 0; i < sk_CMS_RecipientInfo_num(ris); i++) {
+        ri = sk_CMS_RecipientInfo_value(ris, i);
+        if (CMS_RecipientInfo_type(ri) != CMS_RECIPINFO_PASS)
+            continue;
+        CMS_RecipientInfo_set0_password(ri, pass, passlen);
+        r = CMS_RecipientInfo_decrypt(cms, ri);
+        CMS_RecipientInfo_set0_password(ri, NULL, 0);
+        if (r > 0)
+            return 1;
+    }
+
+    CMSerr(CMS_F_CMS_DECRYPT_SET1_PASSWORD, CMS_R_NO_MATCHING_RECIPIENT);
+    return 0;
+
+}
+
+int CMS_decrypt(CMS_ContentInfo *cms, EVP_PKEY *pk, X509 *cert,
+                BIO *dcont, BIO *out, unsigned int flags)
+{
+    int r;
+    BIO *cont;
+    if (OBJ_obj2nid(CMS_get0_type(cms)) != NID_pkcs7_enveloped) {
+        CMSerr(CMS_F_CMS_DECRYPT, CMS_R_TYPE_NOT_ENVELOPED_DATA);
+        return 0;
+    }
+    if (!dcont && !check_content(cms))
+        return 0;
+    if (flags & CMS_DEBUG_DECRYPT)
+        cms->d.envelopedData->encryptedContentInfo->debug = 1;
+    else
+        cms->d.envelopedData->encryptedContentInfo->debug = 0;
+    if (!pk && !cert && !dcont && !out)
+        return 1;
+    if (pk && !CMS_decrypt_set1_pkey(cms, pk, cert))
+        return 0;
+    cont = CMS_dataInit(cms, dcont);
+    if (!cont)
+        return 0;
+    r = cms_copy_content(out, cont, flags);
+    do_free_upto(cont, dcont);
+    return r;
+}
+
+int CMS_final(CMS_ContentInfo *cms, BIO *data, BIO *dcont, unsigned int flags)
+{
+    BIO *cmsbio;
+    int ret = 0;
+
+    if ((cmsbio = CMS_dataInit(cms, dcont)) == NULL) {
+        CMSerr(CMS_F_CMS_FINAL, CMS_R_CMS_LIB);
+        return 0;
+    }
+
+    SMIME_crlf_copy(data, cmsbio, flags);
+
+    (void)BIO_flush(cmsbio);
+
+    if (!CMS_dataFinal(cms, cmsbio)) {
+        CMSerr(CMS_F_CMS_FINAL, CMS_R_CMS_DATAFINAL_ERROR);
+        goto err;
+    }
+
+    ret = 1;
+
+ err:
+    do_free_upto(cmsbio, dcont);
+
+    return ret;
+
+}
+
+#ifdef ZLIB
+
+int CMS_uncompress(CMS_ContentInfo *cms, BIO *dcont, BIO *out,
+                   unsigned int flags)
+{
+    BIO *cont;
+    int r;
+    if (OBJ_obj2nid(CMS_get0_type(cms)) != NID_id_smime_ct_compressedData) {
+        CMSerr(CMS_F_CMS_UNCOMPRESS, CMS_R_TYPE_NOT_COMPRESSED_DATA);
+        return 0;
+    }
+
+    if (!dcont && !check_content(cms))
+        return 0;
+
+    cont = CMS_dataInit(cms, dcont);
+    if (!cont)
+        return 0;
+    r = cms_copy_content(out, cont, flags);
+    do_free_upto(cont, dcont);
+    return r;
+}
+
+CMS_ContentInfo *CMS_compress(BIO *in, int comp_nid, unsigned int flags)
+{
+    CMS_ContentInfo *cms;
+    if (comp_nid <= 0)
+        comp_nid = NID_zlib_compression;
+    cms = cms_CompressedData_create(comp_nid);
+    if (!cms)
+        return NULL;
+
+    if (!(flags & CMS_DETACHED))
+        CMS_set_detached(cms, 0);
+
+    if ((flags & CMS_STREAM) || CMS_final(cms, in, NULL, flags))
+        return cms;
+
+    CMS_ContentInfo_free(cms);
+    return NULL;
+}
+
+#else
+
+int CMS_uncompress(CMS_ContentInfo *cms, BIO *dcont, BIO *out,
+                   unsigned int flags)
+{
+    CMSerr(CMS_F_CMS_UNCOMPRESS, CMS_R_UNSUPPORTED_COMPRESSION_ALGORITHM);
+    return 0;
+}
+
+CMS_ContentInfo *CMS_compress(BIO *in, int comp_nid, unsigned int flags)
+{
+    CMSerr(CMS_F_CMS_COMPRESS, CMS_R_UNSUPPORTED_COMPRESSION_ALGORITHM);
+    return NULL;
+}
+
+#endif
diff --git a/crypto/comp/build.info b/crypto/comp/build.info
new file mode 100644
index 000000000..65df46a17
--- /dev/null
+++ b/crypto/comp/build.info
@@ -0,0 +1,4 @@
+LIBS=../../libcrypto
+SOURCE[../../libcrypto]= \
+        comp_lib.c comp_err.c \
+        c_zlib.c
diff --git a/crypto/comp/c_zlib.c b/crypto/comp/c_zlib.c
new file mode 100644
index 000000000..2f38c2e94
--- /dev/null
+++ b/crypto/comp/c_zlib.c
@@ -0,0 +1,615 @@
+/*
+ * Copyright 1998-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include "internal/comp.h"
+#include 
+#include "internal/cryptlib_int.h"
+#include "internal/bio.h"
+#include "comp_lcl.h"
+
+COMP_METHOD *COMP_zlib(void);
+
+static COMP_METHOD zlib_method_nozlib = {
+    NID_undef,
+    "(undef)",
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+};
+
+#ifndef ZLIB
+# undef ZLIB_SHARED
+#else
+
+# include 
+
+static int zlib_stateful_init(COMP_CTX *ctx);
+static void zlib_stateful_finish(COMP_CTX *ctx);
+static int zlib_stateful_compress_block(COMP_CTX *ctx, unsigned char *out,
+                                        unsigned int olen, unsigned char *in,
+                                        unsigned int ilen);
+static int zlib_stateful_expand_block(COMP_CTX *ctx, unsigned char *out,
+                                      unsigned int olen, unsigned char *in,
+                                      unsigned int ilen);
+
+/* memory allocations functions for zlib initialisation */
+static void *zlib_zalloc(void *opaque, unsigned int no, unsigned int size)
+{
+    void *p;
+
+    p = OPENSSL_zalloc(no * size);
+    return p;
+}
+
+static void zlib_zfree(void *opaque, void *address)
+{
+    OPENSSL_free(address);
+}
+
+
+static COMP_METHOD zlib_stateful_method = {
+    NID_zlib_compression,
+    LN_zlib_compression,
+    zlib_stateful_init,
+    zlib_stateful_finish,
+    zlib_stateful_compress_block,
+    zlib_stateful_expand_block
+};
+
+/*
+ * When OpenSSL is built on Windows, we do not want to require that
+ * the ZLIB.DLL be available in order for the OpenSSL DLLs to
+ * work.  Therefore, all ZLIB routines are loaded at run time
+ * and we do not link to a .LIB file when ZLIB_SHARED is set.
+ */
+# if defined(OPENSSL_SYS_WINDOWS) || defined(OPENSSL_SYS_WIN32)
+#  include 
+# endif                         /* !(OPENSSL_SYS_WINDOWS ||
+                                 * OPENSSL_SYS_WIN32) */
+
+# ifdef ZLIB_SHARED
+#  include "internal/dso.h"
+
+/* Function pointers */
+typedef int (*compress_ft) (Bytef *dest, uLongf * destLen,
+                            const Bytef *source, uLong sourceLen);
+typedef int (*inflateEnd_ft) (z_streamp strm);
+typedef int (*inflate_ft) (z_streamp strm, int flush);
+typedef int (*inflateInit__ft) (z_streamp strm,
+                                const char *version, int stream_size);
+typedef int (*deflateEnd_ft) (z_streamp strm);
+typedef int (*deflate_ft) (z_streamp strm, int flush);
+typedef int (*deflateInit__ft) (z_streamp strm, int level,
+                                const char *version, int stream_size);
+typedef const char *(*zError__ft) (int err);
+static compress_ft p_compress = NULL;
+static inflateEnd_ft p_inflateEnd = NULL;
+static inflate_ft p_inflate = NULL;
+static inflateInit__ft p_inflateInit_ = NULL;
+static deflateEnd_ft p_deflateEnd = NULL;
+static deflate_ft p_deflate = NULL;
+static deflateInit__ft p_deflateInit_ = NULL;
+static zError__ft p_zError = NULL;
+
+static int zlib_loaded = 0;     /* only attempt to init func pts once */
+static DSO *zlib_dso = NULL;
+
+#  define compress                p_compress
+#  define inflateEnd              p_inflateEnd
+#  define inflate                 p_inflate
+#  define inflateInit_            p_inflateInit_
+#  define deflateEnd              p_deflateEnd
+#  define deflate                 p_deflate
+#  define deflateInit_            p_deflateInit_
+#  define zError                  p_zError
+# endif                         /* ZLIB_SHARED */
+
+struct zlib_state {
+    z_stream istream;
+    z_stream ostream;
+};
+
+static int zlib_stateful_init(COMP_CTX *ctx)
+{
+    int err;
+    struct zlib_state *state = OPENSSL_zalloc(sizeof(*state));
+
+    if (state == NULL)
+        goto err;
+
+    state->istream.zalloc = zlib_zalloc;
+    state->istream.zfree = zlib_zfree;
+    state->istream.opaque = Z_NULL;
+    state->istream.next_in = Z_NULL;
+    state->istream.next_out = Z_NULL;
+    err = inflateInit_(&state->istream, ZLIB_VERSION, sizeof(z_stream));
+    if (err != Z_OK)
+        goto err;
+
+    state->ostream.zalloc = zlib_zalloc;
+    state->ostream.zfree = zlib_zfree;
+    state->ostream.opaque = Z_NULL;
+    state->ostream.next_in = Z_NULL;
+    state->ostream.next_out = Z_NULL;
+    err = deflateInit_(&state->ostream, Z_DEFAULT_COMPRESSION,
+                       ZLIB_VERSION, sizeof(z_stream));
+    if (err != Z_OK)
+        goto err;
+
+    ctx->data = state;
+    return 1;
+ err:
+    OPENSSL_free(state);
+    return 0;
+}
+
+static void zlib_stateful_finish(COMP_CTX *ctx)
+{
+    struct zlib_state *state = ctx->data;
+    inflateEnd(&state->istream);
+    deflateEnd(&state->ostream);
+    OPENSSL_free(state);
+}
+
+static int zlib_stateful_compress_block(COMP_CTX *ctx, unsigned char *out,
+                                        unsigned int olen, unsigned char *in,
+                                        unsigned int ilen)
+{
+    int err = Z_OK;
+    struct zlib_state *state = ctx->data;
+
+    if (state == NULL)
+        return -1;
+
+    state->ostream.next_in = in;
+    state->ostream.avail_in = ilen;
+    state->ostream.next_out = out;
+    state->ostream.avail_out = olen;
+    if (ilen > 0)
+        err = deflate(&state->ostream, Z_SYNC_FLUSH);
+    if (err != Z_OK)
+        return -1;
+    return olen - state->ostream.avail_out;
+}
+
+static int zlib_stateful_expand_block(COMP_CTX *ctx, unsigned char *out,
+                                      unsigned int olen, unsigned char *in,
+                                      unsigned int ilen)
+{
+    int err = Z_OK;
+    struct zlib_state *state = ctx->data;
+
+    if (state == NULL)
+        return 0;
+
+    state->istream.next_in = in;
+    state->istream.avail_in = ilen;
+    state->istream.next_out = out;
+    state->istream.avail_out = olen;
+    if (ilen > 0)
+        err = inflate(&state->istream, Z_SYNC_FLUSH);
+    if (err != Z_OK)
+        return -1;
+    return olen - state->istream.avail_out;
+}
+
+#endif
+
+COMP_METHOD *COMP_zlib(void)
+{
+    COMP_METHOD *meth = &zlib_method_nozlib;
+
+#ifdef ZLIB_SHARED
+    /* LIBZ may be externally defined, and we should respect that value */
+# ifndef LIBZ
+#  if defined(OPENSSL_SYS_WINDOWS) || defined(OPENSSL_SYS_WIN32)
+#   define LIBZ "ZLIB1"
+#  elif defined(OPENSSL_SYS_VMS)
+#   define LIBZ "LIBZ"
+#  else
+#   define LIBZ "z"
+#  endif
+# endif
+
+    if (!zlib_loaded) {
+        zlib_dso = DSO_load(NULL, LIBZ, NULL, 0);
+        if (zlib_dso != NULL) {
+            p_compress = (compress_ft) DSO_bind_func(zlib_dso, "compress");
+            p_inflateEnd
+                = (inflateEnd_ft) DSO_bind_func(zlib_dso, "inflateEnd");
+            p_inflate = (inflate_ft) DSO_bind_func(zlib_dso, "inflate");
+            p_inflateInit_
+                = (inflateInit__ft) DSO_bind_func(zlib_dso, "inflateInit_");
+            p_deflateEnd
+                = (deflateEnd_ft) DSO_bind_func(zlib_dso, "deflateEnd");
+            p_deflate = (deflate_ft) DSO_bind_func(zlib_dso, "deflate");
+            p_deflateInit_
+                = (deflateInit__ft) DSO_bind_func(zlib_dso, "deflateInit_");
+            p_zError = (zError__ft) DSO_bind_func(zlib_dso, "zError");
+
+            if (p_compress && p_inflateEnd && p_inflate
+                && p_inflateInit_ && p_deflateEnd
+                && p_deflate && p_deflateInit_ && p_zError)
+                zlib_loaded++;
+
+            if (!OPENSSL_init_crypto(OPENSSL_INIT_ZLIB, NULL)) {
+                comp_zlib_cleanup_int();
+                return meth;
+            }
+            if (zlib_loaded)
+                meth = &zlib_stateful_method;
+        }
+    }
+#endif
+#if defined(ZLIB)
+    meth = &zlib_stateful_method;
+#endif
+
+    return (meth);
+}
+
+void comp_zlib_cleanup_int(void)
+{
+#ifdef ZLIB_SHARED
+    if (zlib_dso != NULL)
+        DSO_free(zlib_dso);
+    zlib_dso = NULL;
+#endif
+}
+
+#ifdef ZLIB
+
+/* Zlib based compression/decompression filter BIO */
+
+typedef struct {
+    unsigned char *ibuf;        /* Input buffer */
+    int ibufsize;               /* Buffer size */
+    z_stream zin;               /* Input decompress context */
+    unsigned char *obuf;        /* Output buffer */
+    int obufsize;               /* Output buffer size */
+    unsigned char *optr;        /* Position in output buffer */
+    int ocount;                 /* Amount of data in output buffer */
+    int odone;                  /* deflate EOF */
+    int comp_level;             /* Compression level to use */
+    z_stream zout;              /* Output compression context */
+} BIO_ZLIB_CTX;
+
+# define ZLIB_DEFAULT_BUFSIZE 1024
+
+static int bio_zlib_new(BIO *bi);
+static int bio_zlib_free(BIO *bi);
+static int bio_zlib_read(BIO *b, char *out, int outl);
+static int bio_zlib_write(BIO *b, const char *in, int inl);
+static long bio_zlib_ctrl(BIO *b, int cmd, long num, void *ptr);
+static long bio_zlib_callback_ctrl(BIO *b, int cmd, bio_info_cb *fp);
+
+static const BIO_METHOD bio_meth_zlib = {
+    BIO_TYPE_COMP,
+    "zlib",
+    bio_zlib_write,
+    bio_zlib_read,
+    NULL,
+    NULL,
+    bio_zlib_ctrl,
+    bio_zlib_new,
+    bio_zlib_free,
+    bio_zlib_callback_ctrl
+};
+
+const BIO_METHOD *BIO_f_zlib(void)
+{
+    return &bio_meth_zlib;
+}
+
+static int bio_zlib_new(BIO *bi)
+{
+    BIO_ZLIB_CTX *ctx;
+# ifdef ZLIB_SHARED
+    (void)COMP_zlib();
+    if (!zlib_loaded) {
+        COMPerr(COMP_F_BIO_ZLIB_NEW, COMP_R_ZLIB_NOT_SUPPORTED);
+        return 0;
+    }
+# endif
+    ctx = OPENSSL_zalloc(sizeof(*ctx));
+    if (ctx == NULL) {
+        COMPerr(COMP_F_BIO_ZLIB_NEW, ERR_R_MALLOC_FAILURE);
+        return 0;
+    }
+    ctx->ibufsize = ZLIB_DEFAULT_BUFSIZE;
+    ctx->obufsize = ZLIB_DEFAULT_BUFSIZE;
+    ctx->zin.zalloc = Z_NULL;
+    ctx->zin.zfree = Z_NULL;
+    ctx->zout.zalloc = Z_NULL;
+    ctx->zout.zfree = Z_NULL;
+    ctx->comp_level = Z_DEFAULT_COMPRESSION;
+    BIO_set_init(bi, 1);
+    BIO_set_data(bi, ctx);
+
+    return 1;
+}
+
+static int bio_zlib_free(BIO *bi)
+{
+    BIO_ZLIB_CTX *ctx;
+    if (!bi)
+        return 0;
+    ctx = BIO_get_data(bi);
+    if (ctx->ibuf) {
+        /* Destroy decompress context */
+        inflateEnd(&ctx->zin);
+        OPENSSL_free(ctx->ibuf);
+    }
+    if (ctx->obuf) {
+        /* Destroy compress context */
+        deflateEnd(&ctx->zout);
+        OPENSSL_free(ctx->obuf);
+    }
+    OPENSSL_free(ctx);
+    BIO_set_data(bi, NULL);
+    BIO_set_init(bi, 0);
+
+    return 1;
+}
+
+static int bio_zlib_read(BIO *b, char *out, int outl)
+{
+    BIO_ZLIB_CTX *ctx;
+    int ret;
+    z_stream *zin;
+    BIO *next = BIO_next(b);
+
+    if (!out || !outl)
+        return 0;
+    ctx = BIO_get_data(b);
+    zin = &ctx->zin;
+    BIO_clear_retry_flags(b);
+    if (!ctx->ibuf) {
+        ctx->ibuf = OPENSSL_malloc(ctx->ibufsize);
+        if (ctx->ibuf == NULL) {
+            COMPerr(COMP_F_BIO_ZLIB_READ, ERR_R_MALLOC_FAILURE);
+            return 0;
+        }
+        inflateInit(zin);
+        zin->next_in = ctx->ibuf;
+        zin->avail_in = 0;
+    }
+
+    /* Copy output data directly to supplied buffer */
+    zin->next_out = (unsigned char *)out;
+    zin->avail_out = (unsigned int)outl;
+    for (;;) {
+        /* Decompress while data available */
+        while (zin->avail_in) {
+            ret = inflate(zin, 0);
+            if ((ret != Z_OK) && (ret != Z_STREAM_END)) {
+                COMPerr(COMP_F_BIO_ZLIB_READ, COMP_R_ZLIB_INFLATE_ERROR);
+                ERR_add_error_data(2, "zlib error:", zError(ret));
+                return 0;
+            }
+            /* If EOF or we've read everything then return */
+            if ((ret == Z_STREAM_END) || !zin->avail_out)
+                return outl - zin->avail_out;
+        }
+
+        /*
+         * No data in input buffer try to read some in, if an error then
+         * return the total data read.
+         */
+        ret = BIO_read(next, ctx->ibuf, ctx->ibufsize);
+        if (ret <= 0) {
+            /* Total data read */
+            int tot = outl - zin->avail_out;
+            BIO_copy_next_retry(b);
+            if (ret < 0)
+                return (tot > 0) ? tot : ret;
+            return tot;
+        }
+        zin->avail_in = ret;
+        zin->next_in = ctx->ibuf;
+    }
+}
+
+static int bio_zlib_write(BIO *b, const char *in, int inl)
+{
+    BIO_ZLIB_CTX *ctx;
+    int ret;
+    z_stream *zout;
+    BIO *next = BIO_next(b);
+
+    if (!in || !inl)
+        return 0;
+    ctx = BIO_get_data(b);
+    if (ctx->odone)
+        return 0;
+    zout = &ctx->zout;
+    BIO_clear_retry_flags(b);
+    if (!ctx->obuf) {
+        ctx->obuf = OPENSSL_malloc(ctx->obufsize);
+        /* Need error here */
+        if (ctx->obuf == NULL) {
+            COMPerr(COMP_F_BIO_ZLIB_WRITE, ERR_R_MALLOC_FAILURE);
+            return 0;
+        }
+        ctx->optr = ctx->obuf;
+        ctx->ocount = 0;
+        deflateInit(zout, ctx->comp_level);
+        zout->next_out = ctx->obuf;
+        zout->avail_out = ctx->obufsize;
+    }
+    /* Obtain input data directly from supplied buffer */
+    zout->next_in = (void *)in;
+    zout->avail_in = inl;
+    for (;;) {
+        /* If data in output buffer write it first */
+        while (ctx->ocount) {
+            ret = BIO_write(next, ctx->optr, ctx->ocount);
+            if (ret <= 0) {
+                /* Total data written */
+                int tot = inl - zout->avail_in;
+                BIO_copy_next_retry(b);
+                if (ret < 0)
+                    return (tot > 0) ? tot : ret;
+                return tot;
+            }
+            ctx->optr += ret;
+            ctx->ocount -= ret;
+        }
+
+        /* Have we consumed all supplied data? */
+        if (!zout->avail_in)
+            return inl;
+
+        /* Compress some more */
+
+        /* Reset buffer */
+        ctx->optr = ctx->obuf;
+        zout->next_out = ctx->obuf;
+        zout->avail_out = ctx->obufsize;
+        /* Compress some more */
+        ret = deflate(zout, 0);
+        if (ret != Z_OK) {
+            COMPerr(COMP_F_BIO_ZLIB_WRITE, COMP_R_ZLIB_DEFLATE_ERROR);
+            ERR_add_error_data(2, "zlib error:", zError(ret));
+            return 0;
+        }
+        ctx->ocount = ctx->obufsize - zout->avail_out;
+    }
+}
+
+static int bio_zlib_flush(BIO *b)
+{
+    BIO_ZLIB_CTX *ctx;
+    int ret;
+    z_stream *zout;
+    BIO *next = BIO_next(b);
+
+    ctx = BIO_get_data(b);
+    /* If no data written or already flush show success */
+    if (!ctx->obuf || (ctx->odone && !ctx->ocount))
+        return 1;
+    zout = &ctx->zout;
+    BIO_clear_retry_flags(b);
+    /* No more input data */
+    zout->next_in = NULL;
+    zout->avail_in = 0;
+    for (;;) {
+        /* If data in output buffer write it first */
+        while (ctx->ocount) {
+            ret = BIO_write(next, ctx->optr, ctx->ocount);
+            if (ret <= 0) {
+                BIO_copy_next_retry(b);
+                return ret;
+            }
+            ctx->optr += ret;
+            ctx->ocount -= ret;
+        }
+        if (ctx->odone)
+            return 1;
+
+        /* Compress some more */
+
+        /* Reset buffer */
+        ctx->optr = ctx->obuf;
+        zout->next_out = ctx->obuf;
+        zout->avail_out = ctx->obufsize;
+        /* Compress some more */
+        ret = deflate(zout, Z_FINISH);
+        if (ret == Z_STREAM_END)
+            ctx->odone = 1;
+        else if (ret != Z_OK) {
+            COMPerr(COMP_F_BIO_ZLIB_FLUSH, COMP_R_ZLIB_DEFLATE_ERROR);
+            ERR_add_error_data(2, "zlib error:", zError(ret));
+            return 0;
+        }
+        ctx->ocount = ctx->obufsize - zout->avail_out;
+    }
+}
+
+static long bio_zlib_ctrl(BIO *b, int cmd, long num, void *ptr)
+{
+    BIO_ZLIB_CTX *ctx;
+    int ret, *ip;
+    int ibs, obs;
+    BIO *next = BIO_next(b);
+
+    if (next == NULL)
+        return 0;
+    ctx = BIO_get_data(b);
+    switch (cmd) {
+
+    case BIO_CTRL_RESET:
+        ctx->ocount = 0;
+        ctx->odone = 0;
+        ret = 1;
+        break;
+
+    case BIO_CTRL_FLUSH:
+        ret = bio_zlib_flush(b);
+        if (ret > 0)
+            ret = BIO_flush(next);
+        break;
+
+    case BIO_C_SET_BUFF_SIZE:
+        ibs = -1;
+        obs = -1;
+        if (ptr != NULL) {
+            ip = ptr;
+            if (*ip == 0)
+                ibs = (int)num;
+            else
+                obs = (int)num;
+        } else {
+            ibs = (int)num;
+            obs = ibs;
+        }
+
+        if (ibs != -1) {
+            OPENSSL_free(ctx->ibuf);
+            ctx->ibuf = NULL;
+            ctx->ibufsize = ibs;
+        }
+
+        if (obs != -1) {
+            OPENSSL_free(ctx->obuf);
+            ctx->obuf = NULL;
+            ctx->obufsize = obs;
+        }
+        ret = 1;
+        break;
+
+    case BIO_C_DO_STATE_MACHINE:
+        BIO_clear_retry_flags(b);
+        ret = BIO_ctrl(next, cmd, num, ptr);
+        BIO_copy_next_retry(b);
+        break;
+
+    default:
+        ret = BIO_ctrl(next, cmd, num, ptr);
+        break;
+
+    }
+
+    return ret;
+}
+
+static long bio_zlib_callback_ctrl(BIO *b, int cmd, bio_info_cb *fp)
+{
+    BIO *next = BIO_next(b);
+    if (next == NULL)
+        return 0;
+    return BIO_callback_ctrl(next, cmd, fp);
+}
+
+#endif
diff --git a/crypto/comp/comp_err.c b/crypto/comp/comp_err.c
new file mode 100644
index 000000000..8e2e69568
--- /dev/null
+++ b/crypto/comp/comp_err.c
@@ -0,0 +1,48 @@
+/*
+ * Generated by util/mkerr.pl DO NOT EDIT
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include 
+
+/* BEGIN ERROR CODES */
+#ifndef OPENSSL_NO_ERR
+
+# define ERR_FUNC(func) ERR_PACK(ERR_LIB_COMP,func,0)
+# define ERR_REASON(reason) ERR_PACK(ERR_LIB_COMP,0,reason)
+
+static ERR_STRING_DATA COMP_str_functs[] = {
+    {ERR_FUNC(COMP_F_BIO_ZLIB_FLUSH), "bio_zlib_flush"},
+    {ERR_FUNC(COMP_F_BIO_ZLIB_NEW), "bio_zlib_new"},
+    {ERR_FUNC(COMP_F_BIO_ZLIB_READ), "bio_zlib_read"},
+    {ERR_FUNC(COMP_F_BIO_ZLIB_WRITE), "bio_zlib_write"},
+    {0, NULL}
+};
+
+static ERR_STRING_DATA COMP_str_reasons[] = {
+    {ERR_REASON(COMP_R_ZLIB_DEFLATE_ERROR), "zlib deflate error"},
+    {ERR_REASON(COMP_R_ZLIB_INFLATE_ERROR), "zlib inflate error"},
+    {ERR_REASON(COMP_R_ZLIB_NOT_SUPPORTED), "zlib not supported"},
+    {0, NULL}
+};
+
+#endif
+
+int ERR_load_COMP_strings(void)
+{
+#ifndef OPENSSL_NO_ERR
+
+    if (ERR_func_error_string(COMP_str_functs[0].error) == NULL) {
+        ERR_load_strings(0, COMP_str_functs);
+        ERR_load_strings(0, COMP_str_reasons);
+    }
+#endif
+    return 1;
+}
diff --git a/crypto/comp/comp_lcl.h b/crypto/comp/comp_lcl.h
new file mode 100644
index 000000000..aa45fca23
--- /dev/null
+++ b/crypto/comp/comp_lcl.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2015-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+struct comp_method_st {
+    int type;                   /* NID for compression library */
+    const char *name;           /* A text string to identify the library */
+    int (*init) (COMP_CTX *ctx);
+    void (*finish) (COMP_CTX *ctx);
+    int (*compress) (COMP_CTX *ctx,
+                     unsigned char *out, unsigned int olen,
+                     unsigned char *in, unsigned int ilen);
+    int (*expand) (COMP_CTX *ctx,
+                   unsigned char *out, unsigned int olen,
+                   unsigned char *in, unsigned int ilen);
+};
+
+struct comp_ctx_st {
+    struct comp_method_st *meth;
+    unsigned long compress_in;
+    unsigned long compress_out;
+    unsigned long expand_in;
+    unsigned long expand_out;
+    void* data;
+};
diff --git a/crypto/comp/comp_lib.c b/crypto/comp/comp_lib.c
new file mode 100644
index 000000000..32afd0dba
--- /dev/null
+++ b/crypto/comp/comp_lib.c
@@ -0,0 +1,91 @@
+/*
+ * Copyright 1998-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include "comp_lcl.h"
+
+COMP_CTX *COMP_CTX_new(COMP_METHOD *meth)
+{
+    COMP_CTX *ret;
+
+    if ((ret = OPENSSL_zalloc(sizeof(*ret))) == NULL)
+        return (NULL);
+    ret->meth = meth;
+    if ((ret->meth->init != NULL) && !ret->meth->init(ret)) {
+        OPENSSL_free(ret);
+        ret = NULL;
+    }
+    return (ret);
+}
+
+const COMP_METHOD *COMP_CTX_get_method(const COMP_CTX *ctx)
+{
+    return ctx->meth;
+}
+
+int COMP_get_type(const COMP_METHOD *meth)
+{
+    return meth->type;
+}
+
+const char *COMP_get_name(const COMP_METHOD *meth)
+{
+    return meth->name;
+}
+
+void COMP_CTX_free(COMP_CTX *ctx)
+{
+    if (ctx == NULL)
+        return;
+
+    if (ctx->meth->finish != NULL)
+        ctx->meth->finish(ctx);
+
+    OPENSSL_free(ctx);
+}
+
+int COMP_compress_block(COMP_CTX *ctx, unsigned char *out, int olen,
+                        unsigned char *in, int ilen)
+{
+    int ret;
+    if (ctx->meth->compress == NULL) {
+        return (-1);
+    }
+    ret = ctx->meth->compress(ctx, out, olen, in, ilen);
+    if (ret > 0) {
+        ctx->compress_in += ilen;
+        ctx->compress_out += ret;
+    }
+    return (ret);
+}
+
+int COMP_expand_block(COMP_CTX *ctx, unsigned char *out, int olen,
+                      unsigned char *in, int ilen)
+{
+    int ret;
+
+    if (ctx->meth->expand == NULL) {
+        return (-1);
+    }
+    ret = ctx->meth->expand(ctx, out, olen, in, ilen);
+    if (ret > 0) {
+        ctx->expand_in += ilen;
+        ctx->expand_out += ret;
+    }
+    return (ret);
+}
+
+int COMP_CTX_get_type(const COMP_CTX* comp)
+{
+    return comp->meth ? comp->meth->type : NID_undef;
+}
diff --git a/crypto/conf/build.info b/crypto/conf/build.info
new file mode 100644
index 000000000..4438eb426
--- /dev/null
+++ b/crypto/conf/build.info
@@ -0,0 +1,4 @@
+LIBS=../../libcrypto
+SOURCE[../../libcrypto]= \
+        conf_err.c conf_lib.c conf_api.c conf_def.c conf_mod.c \
+        conf_mall.c conf_sap.c
diff --git a/crypto/conf/conf_api.c b/crypto/conf/conf_api.c
new file mode 100644
index 000000000..5535416ab
--- /dev/null
+++ b/crypto/conf/conf_api.c
@@ -0,0 +1,214 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/* Part of the code in here was originally in conf.c, which is now removed */
+
+#include 
+#include 
+#include 
+#include 
+#include "e_os.h"
+
+static void value_free_hash(const CONF_VALUE *a, LHASH_OF(CONF_VALUE) *conf);
+static void value_free_stack_doall(CONF_VALUE *a);
+
+/* Up until OpenSSL 0.9.5a, this was get_section */
+CONF_VALUE *_CONF_get_section(const CONF *conf, const char *section)
+{
+    CONF_VALUE *v, vv;
+
+    if ((conf == NULL) || (section == NULL))
+        return (NULL);
+    vv.name = NULL;
+    vv.section = (char *)section;
+    v = lh_CONF_VALUE_retrieve(conf->data, &vv);
+    return (v);
+}
+
+/* Up until OpenSSL 0.9.5a, this was CONF_get_section */
+STACK_OF(CONF_VALUE) *_CONF_get_section_values(const CONF *conf,
+                                               const char *section)
+{
+    CONF_VALUE *v;
+
+    v = _CONF_get_section(conf, section);
+    if (v != NULL)
+        return ((STACK_OF(CONF_VALUE) *)v->value);
+    else
+        return (NULL);
+}
+
+int _CONF_add_string(CONF *conf, CONF_VALUE *section, CONF_VALUE *value)
+{
+    CONF_VALUE *v = NULL;
+    STACK_OF(CONF_VALUE) *ts;
+
+    ts = (STACK_OF(CONF_VALUE) *)section->value;
+
+    value->section = section->section;
+    if (!sk_CONF_VALUE_push(ts, value)) {
+        return 0;
+    }
+
+    v = lh_CONF_VALUE_insert(conf->data, value);
+    if (v != NULL) {
+        (void)sk_CONF_VALUE_delete_ptr(ts, v);
+        OPENSSL_free(v->name);
+        OPENSSL_free(v->value);
+        OPENSSL_free(v);
+    }
+    return 1;
+}
+
+char *_CONF_get_string(const CONF *conf, const char *section,
+                       const char *name)
+{
+    CONF_VALUE *v, vv;
+    char *p;
+
+    if (name == NULL)
+        return (NULL);
+    if (conf != NULL) {
+        if (section != NULL) {
+            vv.name = (char *)name;
+            vv.section = (char *)section;
+            v = lh_CONF_VALUE_retrieve(conf->data, &vv);
+            if (v != NULL)
+                return (v->value);
+            if (strcmp(section, "ENV") == 0) {
+                p = getenv(name);
+                if (p != NULL)
+                    return (p);
+            }
+        }
+        vv.section = "default";
+        vv.name = (char *)name;
+        v = lh_CONF_VALUE_retrieve(conf->data, &vv);
+        if (v != NULL)
+            return (v->value);
+        else
+            return (NULL);
+    } else
+        return (getenv(name));
+}
+
+static unsigned long conf_value_hash(const CONF_VALUE *v)
+{
+    return (OPENSSL_LH_strhash(v->section) << 2) ^ OPENSSL_LH_strhash(v->name);
+}
+
+static int conf_value_cmp(const CONF_VALUE *a, const CONF_VALUE *b)
+{
+    int i;
+
+    if (a->section != b->section) {
+        i = strcmp(a->section, b->section);
+        if (i)
+            return (i);
+    }
+
+    if ((a->name != NULL) && (b->name != NULL)) {
+        i = strcmp(a->name, b->name);
+        return (i);
+    } else if (a->name == b->name)
+        return (0);
+    else
+        return ((a->name == NULL) ? -1 : 1);
+}
+
+int _CONF_new_data(CONF *conf)
+{
+    if (conf == NULL) {
+        return 0;
+    }
+    if (conf->data == NULL) {
+        conf->data = lh_CONF_VALUE_new(conf_value_hash, conf_value_cmp);
+        if (conf->data == NULL)
+            return 0;
+    }
+    return 1;
+}
+
+typedef LHASH_OF(CONF_VALUE) LH_CONF_VALUE;
+
+IMPLEMENT_LHASH_DOALL_ARG_CONST(CONF_VALUE, LH_CONF_VALUE);
+
+void _CONF_free_data(CONF *conf)
+{
+    if (conf == NULL || conf->data == NULL)
+        return;
+
+    /* evil thing to make sure the 'OPENSSL_free()' works as expected */
+    lh_CONF_VALUE_set_down_load(conf->data, 0);
+    lh_CONF_VALUE_doall_LH_CONF_VALUE(conf->data, value_free_hash, conf->data);
+
+    /*
+     * We now have only 'section' entries in the hash table. Due to problems
+     * with
+     */
+
+    lh_CONF_VALUE_doall(conf->data, value_free_stack_doall);
+    lh_CONF_VALUE_free(conf->data);
+}
+
+static void value_free_hash(const CONF_VALUE *a, LHASH_OF(CONF_VALUE) *conf)
+{
+    if (a->name != NULL)
+        (void)lh_CONF_VALUE_delete(conf, a);
+}
+
+static void value_free_stack_doall(CONF_VALUE *a)
+{
+    CONF_VALUE *vv;
+    STACK_OF(CONF_VALUE) *sk;
+    int i;
+
+    if (a->name != NULL)
+        return;
+
+    sk = (STACK_OF(CONF_VALUE) *)a->value;
+    for (i = sk_CONF_VALUE_num(sk) - 1; i >= 0; i--) {
+        vv = sk_CONF_VALUE_value(sk, i);
+        OPENSSL_free(vv->value);
+        OPENSSL_free(vv->name);
+        OPENSSL_free(vv);
+    }
+    sk_CONF_VALUE_free(sk);
+    OPENSSL_free(a->section);
+    OPENSSL_free(a);
+}
+
+/* Up until OpenSSL 0.9.5a, this was new_section */
+CONF_VALUE *_CONF_new_section(CONF *conf, const char *section)
+{
+    STACK_OF(CONF_VALUE) *sk = NULL;
+    int i;
+    CONF_VALUE *v = NULL, *vv;
+
+    if ((sk = sk_CONF_VALUE_new_null()) == NULL)
+        goto err;
+    if ((v = OPENSSL_malloc(sizeof(*v))) == NULL)
+        goto err;
+    i = strlen(section) + 1;
+    if ((v->section = OPENSSL_malloc(i)) == NULL)
+        goto err;
+
+    memcpy(v->section, section, i);
+    v->name = NULL;
+    v->value = (char *)sk;
+
+    vv = lh_CONF_VALUE_insert(conf->data, v);
+    OPENSSL_assert(vv == NULL);
+    return v;
+
+ err:
+    sk_CONF_VALUE_free(sk);
+    OPENSSL_free(v);
+    return NULL;
+}
diff --git a/crypto/conf/conf_def.c b/crypto/conf/conf_def.c
new file mode 100644
index 000000000..8861b3a5a
--- /dev/null
+++ b/crypto/conf/conf_def.c
@@ -0,0 +1,630 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/* Part of the code in here was originally in conf.c, which is now removed */
+
+#include 
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+#include 
+#include 
+#include "conf_def.h"
+#include 
+#include 
+
+static char *eat_ws(CONF *conf, char *p);
+static char *eat_alpha_numeric(CONF *conf, char *p);
+static void clear_comments(CONF *conf, char *p);
+static int str_copy(CONF *conf, char *section, char **to, char *from);
+static char *scan_quote(CONF *conf, char *p);
+static char *scan_dquote(CONF *conf, char *p);
+#define scan_esc(conf,p)        (((IS_EOF((conf),(p)[1]))?((p)+1):((p)+2)))
+
+static CONF *def_create(CONF_METHOD *meth);
+static int def_init_default(CONF *conf);
+static int def_init_WIN32(CONF *conf);
+static int def_destroy(CONF *conf);
+static int def_destroy_data(CONF *conf);
+static int def_load(CONF *conf, const char *name, long *eline);
+static int def_load_bio(CONF *conf, BIO *bp, long *eline);
+static int def_dump(const CONF *conf, BIO *bp);
+static int def_is_number(const CONF *conf, char c);
+static int def_to_int(const CONF *conf, char c);
+
+static CONF_METHOD default_method = {
+    "OpenSSL default",
+    def_create,
+    def_init_default,
+    def_destroy,
+    def_destroy_data,
+    def_load_bio,
+    def_dump,
+    def_is_number,
+    def_to_int,
+    def_load
+};
+
+static CONF_METHOD WIN32_method = {
+    "WIN32",
+    def_create,
+    def_init_WIN32,
+    def_destroy,
+    def_destroy_data,
+    def_load_bio,
+    def_dump,
+    def_is_number,
+    def_to_int,
+    def_load
+};
+
+CONF_METHOD *NCONF_default()
+{
+    return &default_method;
+}
+
+CONF_METHOD *NCONF_WIN32()
+{
+    return &WIN32_method;
+}
+
+static CONF *def_create(CONF_METHOD *meth)
+{
+    CONF *ret;
+
+    ret = OPENSSL_malloc(sizeof(*ret));
+    if (ret != NULL)
+        if (meth->init(ret) == 0) {
+            OPENSSL_free(ret);
+            ret = NULL;
+        }
+    return ret;
+}
+
+static int def_init_default(CONF *conf)
+{
+    if (conf == NULL)
+        return 0;
+
+    conf->meth = &default_method;
+    conf->meth_data = (void *)CONF_type_default;
+    conf->data = NULL;
+
+    return 1;
+}
+
+static int def_init_WIN32(CONF *conf)
+{
+    if (conf == NULL)
+        return 0;
+
+    conf->meth = &WIN32_method;
+    conf->meth_data = (void *)CONF_type_win32;
+    conf->data = NULL;
+
+    return 1;
+}
+
+static int def_destroy(CONF *conf)
+{
+    if (def_destroy_data(conf)) {
+        OPENSSL_free(conf);
+        return 1;
+    }
+    return 0;
+}
+
+static int def_destroy_data(CONF *conf)
+{
+    if (conf == NULL)
+        return 0;
+    _CONF_free_data(conf);
+    return 1;
+}
+
+static int def_load(CONF *conf, const char *name, long *line)
+{
+    int ret;
+    BIO *in = NULL;
+
+#ifdef OPENSSL_SYS_VMS
+    in = BIO_new_file(name, "r");
+#else
+    in = BIO_new_file(name, "rb");
+#endif
+    if (in == NULL) {
+        if (ERR_GET_REASON(ERR_peek_last_error()) == BIO_R_NO_SUCH_FILE)
+            CONFerr(CONF_F_DEF_LOAD, CONF_R_NO_SUCH_FILE);
+        else
+            CONFerr(CONF_F_DEF_LOAD, ERR_R_SYS_LIB);
+        return 0;
+    }
+
+    ret = def_load_bio(conf, in, line);
+    BIO_free(in);
+
+    return ret;
+}
+
+static int def_load_bio(CONF *conf, BIO *in, long *line)
+{
+/* The macro BUFSIZE conflicts with a system macro in VxWorks */
+#define CONFBUFSIZE     512
+    int bufnum = 0, i, ii;
+    BUF_MEM *buff = NULL;
+    char *s, *p, *end;
+    int again;
+    long eline = 0;
+    char btmp[DECIMAL_SIZE(eline) + 1];
+    CONF_VALUE *v = NULL, *tv;
+    CONF_VALUE *sv = NULL;
+    char *section = NULL, *buf;
+    char *start, *psection, *pname;
+    void *h = (void *)(conf->data);
+
+    if ((buff = BUF_MEM_new()) == NULL) {
+        CONFerr(CONF_F_DEF_LOAD_BIO, ERR_R_BUF_LIB);
+        goto err;
+    }
+
+    section = OPENSSL_strdup("default");
+    if (section == NULL) {
+        CONFerr(CONF_F_DEF_LOAD_BIO, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+
+    if (_CONF_new_data(conf) == 0) {
+        CONFerr(CONF_F_DEF_LOAD_BIO, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+
+    sv = _CONF_new_section(conf, section);
+    if (sv == NULL) {
+        CONFerr(CONF_F_DEF_LOAD_BIO, CONF_R_UNABLE_TO_CREATE_NEW_SECTION);
+        goto err;
+    }
+
+    bufnum = 0;
+    again = 0;
+    for (;;) {
+        if (!BUF_MEM_grow(buff, bufnum + CONFBUFSIZE)) {
+            CONFerr(CONF_F_DEF_LOAD_BIO, ERR_R_BUF_LIB);
+            goto err;
+        }
+        p = &(buff->data[bufnum]);
+        *p = '\0';
+        BIO_gets(in, p, CONFBUFSIZE - 1);
+        p[CONFBUFSIZE - 1] = '\0';
+        ii = i = strlen(p);
+        if (i == 0 && !again)
+            break;
+        again = 0;
+        while (i > 0) {
+            if ((p[i - 1] != '\r') && (p[i - 1] != '\n'))
+                break;
+            else
+                i--;
+        }
+        /*
+         * we removed some trailing stuff so there is a new line on the end.
+         */
+        if (ii && i == ii)
+            again = 1;          /* long line */
+        else {
+            p[i] = '\0';
+            eline++;            /* another input line */
+        }
+
+        /* we now have a line with trailing \r\n removed */
+
+        /* i is the number of bytes */
+        bufnum += i;
+
+        v = NULL;
+        /* check for line continuation */
+        if (bufnum >= 1) {
+            /*
+             * If we have bytes and the last char '\\' and second last char
+             * is not '\\'
+             */
+            p = &(buff->data[bufnum - 1]);
+            if (IS_ESC(conf, p[0]) && ((bufnum <= 1) || !IS_ESC(conf, p[-1]))) {
+                bufnum--;
+                again = 1;
+            }
+        }
+        if (again)
+            continue;
+        bufnum = 0;
+        buf = buff->data;
+
+        clear_comments(conf, buf);
+        s = eat_ws(conf, buf);
+        if (IS_EOF(conf, *s))
+            continue;           /* blank line */
+        if (*s == '[') {
+            char *ss;
+
+            s++;
+            start = eat_ws(conf, s);
+            ss = start;
+ again:
+            end = eat_alpha_numeric(conf, ss);
+            p = eat_ws(conf, end);
+            if (*p != ']') {
+                if (*p != '\0' && ss != p) {
+                    ss = p;
+                    goto again;
+                }
+                CONFerr(CONF_F_DEF_LOAD_BIO,
+                        CONF_R_MISSING_CLOSE_SQUARE_BRACKET);
+                goto err;
+            }
+            *end = '\0';
+            if (!str_copy(conf, NULL, §ion, start))
+                goto err;
+            if ((sv = _CONF_get_section(conf, section)) == NULL)
+                sv = _CONF_new_section(conf, section);
+            if (sv == NULL) {
+                CONFerr(CONF_F_DEF_LOAD_BIO,
+                        CONF_R_UNABLE_TO_CREATE_NEW_SECTION);
+                goto err;
+            }
+            continue;
+        } else {
+            pname = s;
+            psection = NULL;
+            end = eat_alpha_numeric(conf, s);
+            if ((end[0] == ':') && (end[1] == ':')) {
+                *end = '\0';
+                end += 2;
+                psection = pname;
+                pname = end;
+                end = eat_alpha_numeric(conf, end);
+            }
+            p = eat_ws(conf, end);
+            if (*p != '=') {
+                CONFerr(CONF_F_DEF_LOAD_BIO, CONF_R_MISSING_EQUAL_SIGN);
+                goto err;
+            }
+            *end = '\0';
+            p++;
+            start = eat_ws(conf, p);
+            while (!IS_EOF(conf, *p))
+                p++;
+            p--;
+            while ((p != start) && (IS_WS(conf, *p)))
+                p--;
+            p++;
+            *p = '\0';
+
+            if ((v = OPENSSL_malloc(sizeof(*v))) == NULL) {
+                CONFerr(CONF_F_DEF_LOAD_BIO, ERR_R_MALLOC_FAILURE);
+                goto err;
+            }
+            if (psection == NULL)
+                psection = section;
+            v->name = OPENSSL_malloc(strlen(pname) + 1);
+            v->value = NULL;
+            if (v->name == NULL) {
+                CONFerr(CONF_F_DEF_LOAD_BIO, ERR_R_MALLOC_FAILURE);
+                goto err;
+            }
+            OPENSSL_strlcpy(v->name, pname, strlen(pname) + 1);
+            if (!str_copy(conf, psection, &(v->value), start))
+                goto err;
+
+            if (strcmp(psection, section) != 0) {
+                if ((tv = _CONF_get_section(conf, psection))
+                    == NULL)
+                    tv = _CONF_new_section(conf, psection);
+                if (tv == NULL) {
+                    CONFerr(CONF_F_DEF_LOAD_BIO,
+                            CONF_R_UNABLE_TO_CREATE_NEW_SECTION);
+                    goto err;
+                }
+            } else
+                tv = sv;
+            if (_CONF_add_string(conf, tv, v) == 0) {
+                CONFerr(CONF_F_DEF_LOAD_BIO, ERR_R_MALLOC_FAILURE);
+                goto err;
+            }
+            v = NULL;
+        }
+    }
+    BUF_MEM_free(buff);
+    OPENSSL_free(section);
+    return (1);
+ err:
+    BUF_MEM_free(buff);
+    OPENSSL_free(section);
+    if (line != NULL)
+        *line = eline;
+    BIO_snprintf(btmp, sizeof btmp, "%ld", eline);
+    ERR_add_error_data(2, "line ", btmp);
+    if (h != conf->data) {
+        CONF_free(conf->data);
+        conf->data = NULL;
+    }
+    if (v != NULL) {
+        OPENSSL_free(v->name);
+        OPENSSL_free(v->value);
+        OPENSSL_free(v);
+    }
+    return (0);
+}
+
+static void clear_comments(CONF *conf, char *p)
+{
+    for (;;) {
+        if (IS_FCOMMENT(conf, *p)) {
+            *p = '\0';
+            return;
+        }
+        if (!IS_WS(conf, *p)) {
+            break;
+        }
+        p++;
+    }
+
+    for (;;) {
+        if (IS_COMMENT(conf, *p)) {
+            *p = '\0';
+            return;
+        }
+        if (IS_DQUOTE(conf, *p)) {
+            p = scan_dquote(conf, p);
+            continue;
+        }
+        if (IS_QUOTE(conf, *p)) {
+            p = scan_quote(conf, p);
+            continue;
+        }
+        if (IS_ESC(conf, *p)) {
+            p = scan_esc(conf, p);
+            continue;
+        }
+        if (IS_EOF(conf, *p))
+            return;
+        else
+            p++;
+    }
+}
+
+static int str_copy(CONF *conf, char *section, char **pto, char *from)
+{
+    int q, r, rr = 0, to = 0, len = 0;
+    char *s, *e, *rp, *p, *rrp, *np, *cp, v;
+    BUF_MEM *buf;
+
+    if ((buf = BUF_MEM_new()) == NULL)
+        return (0);
+
+    len = strlen(from) + 1;
+    if (!BUF_MEM_grow(buf, len))
+        goto err;
+
+    for (;;) {
+        if (IS_QUOTE(conf, *from)) {
+            q = *from;
+            from++;
+            while (!IS_EOF(conf, *from) && (*from != q)) {
+                if (IS_ESC(conf, *from)) {
+                    from++;
+                    if (IS_EOF(conf, *from))
+                        break;
+                }
+                buf->data[to++] = *(from++);
+            }
+            if (*from == q)
+                from++;
+        } else if (IS_DQUOTE(conf, *from)) {
+            q = *from;
+            from++;
+            while (!IS_EOF(conf, *from)) {
+                if (*from == q) {
+                    if (*(from + 1) == q) {
+                        from++;
+                    } else {
+                        break;
+                    }
+                }
+                buf->data[to++] = *(from++);
+            }
+            if (*from == q)
+                from++;
+        } else if (IS_ESC(conf, *from)) {
+            from++;
+            v = *(from++);
+            if (IS_EOF(conf, v))
+                break;
+            else if (v == 'r')
+                v = '\r';
+            else if (v == 'n')
+                v = '\n';
+            else if (v == 'b')
+                v = '\b';
+            else if (v == 't')
+                v = '\t';
+            buf->data[to++] = v;
+        } else if (IS_EOF(conf, *from))
+            break;
+        else if (*from == '$') {
+            /* try to expand it */
+            rrp = NULL;
+            s = &(from[1]);
+            if (*s == '{')
+                q = '}';
+            else if (*s == '(')
+                q = ')';
+            else
+                q = 0;
+
+            if (q)
+                s++;
+            cp = section;
+            e = np = s;
+            while (IS_ALPHA_NUMERIC(conf, *e))
+                e++;
+            if ((e[0] == ':') && (e[1] == ':')) {
+                cp = np;
+                rrp = e;
+                rr = *e;
+                *rrp = '\0';
+                e += 2;
+                np = e;
+                while (IS_ALPHA_NUMERIC(conf, *e))
+                    e++;
+            }
+            r = *e;
+            *e = '\0';
+            rp = e;
+            if (q) {
+                if (r != q) {
+                    CONFerr(CONF_F_STR_COPY, CONF_R_NO_CLOSE_BRACE);
+                    goto err;
+                }
+                e++;
+            }
+            /*-
+             * So at this point we have
+             * np which is the start of the name string which is
+             *   '\0' terminated.
+             * cp which is the start of the section string which is
+             *   '\0' terminated.
+             * e is the 'next point after'.
+             * r and rr are the chars replaced by the '\0'
+             * rp and rrp is where 'r' and 'rr' came from.
+             */
+            p = _CONF_get_string(conf, cp, np);
+            if (rrp != NULL)
+                *rrp = rr;
+            *rp = r;
+            if (p == NULL) {
+                CONFerr(CONF_F_STR_COPY, CONF_R_VARIABLE_HAS_NO_VALUE);
+                goto err;
+            }
+            if (!BUF_MEM_grow_clean(buf,
+                        (strlen(p) + buf->length - (e - from)))) {
+                CONFerr(CONF_F_STR_COPY, ERR_R_MALLOC_FAILURE);
+                goto err;
+            }
+            while (*p)
+                buf->data[to++] = *(p++);
+
+            /*
+             * Since we change the pointer 'from', we also have to change the
+             * perceived length of the string it points at.  /RL
+             */
+            len -= e - from;
+            from = e;
+
+            /*
+             * In case there were no braces or parenthesis around the
+             * variable reference, we have to put back the character that was
+             * replaced with a '\0'.  /RL
+             */
+            *rp = r;
+        } else
+            buf->data[to++] = *(from++);
+    }
+    buf->data[to] = '\0';
+    OPENSSL_free(*pto);
+    *pto = buf->data;
+    OPENSSL_free(buf);
+    return (1);
+ err:
+    BUF_MEM_free(buf);
+    return (0);
+}
+
+static char *eat_ws(CONF *conf, char *p)
+{
+    while (IS_WS(conf, *p) && (!IS_EOF(conf, *p)))
+        p++;
+    return (p);
+}
+
+static char *eat_alpha_numeric(CONF *conf, char *p)
+{
+    for (;;) {
+        if (IS_ESC(conf, *p)) {
+            p = scan_esc(conf, p);
+            continue;
+        }
+        if (!IS_ALPHA_NUMERIC_PUNCT(conf, *p))
+            return (p);
+        p++;
+    }
+}
+
+static char *scan_quote(CONF *conf, char *p)
+{
+    int q = *p;
+
+    p++;
+    while (!(IS_EOF(conf, *p)) && (*p != q)) {
+        if (IS_ESC(conf, *p)) {
+            p++;
+            if (IS_EOF(conf, *p))
+                return (p);
+        }
+        p++;
+    }
+    if (*p == q)
+        p++;
+    return (p);
+}
+
+static char *scan_dquote(CONF *conf, char *p)
+{
+    int q = *p;
+
+    p++;
+    while (!(IS_EOF(conf, *p))) {
+        if (*p == q) {
+            if (*(p + 1) == q) {
+                p++;
+            } else {
+                break;
+            }
+        }
+        p++;
+    }
+    if (*p == q)
+        p++;
+    return (p);
+}
+
+static void dump_value_doall_arg(const CONF_VALUE *a, BIO *out)
+{
+    if (a->name)
+        BIO_printf(out, "[%s] %s=%s\n", a->section, a->name, a->value);
+    else
+        BIO_printf(out, "[[%s]]\n", a->section);
+}
+
+IMPLEMENT_LHASH_DOALL_ARG_CONST(CONF_VALUE, BIO);
+
+static int def_dump(const CONF *conf, BIO *out)
+{
+    lh_CONF_VALUE_doall_BIO(conf->data, dump_value_doall_arg, out);
+    return 1;
+}
+
+static int def_is_number(const CONF *conf, char c)
+{
+    return IS_NUMBER(conf, c);
+}
+
+static int def_to_int(const CONF *conf, char c)
+{
+    return c - '0';
+}
diff --git a/crypto/conf/conf_def.h b/crypto/conf/conf_def.h
new file mode 100644
index 000000000..da4767e19
--- /dev/null
+++ b/crypto/conf/conf_def.h
@@ -0,0 +1,129 @@
+/*
+ * WARNING: do not edit!
+ * Generated by crypto/conf/keysets.pl
+ *
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#define CONF_NUMBER             1
+#define CONF_UPPER              2
+#define CONF_LOWER              4
+#define CONF_UNDER              256
+#define CONF_PUNCTUATION        512
+#define CONF_WS                 16
+#define CONF_ESC                32
+#define CONF_QUOTE              64
+#define CONF_DQUOTE             1024
+#define CONF_COMMENT            128
+#define CONF_FCOMMENT           2048
+#define CONF_EOF                8
+#define CONF_HIGHBIT            4096
+#define CONF_ALPHA              (CONF_UPPER|CONF_LOWER)
+#define CONF_ALPHA_NUMERIC      (CONF_ALPHA|CONF_NUMBER|CONF_UNDER)
+#define CONF_ALPHA_NUMERIC_PUNCT (CONF_ALPHA|CONF_NUMBER|CONF_UNDER| \
+                                        CONF_PUNCTUATION)
+
+#define KEYTYPES(c)             ((const unsigned short *)((c)->meth_data))
+#ifndef CHARSET_EBCDIC
+# define IS_COMMENT(c,a)         (KEYTYPES(c)[(a)&0xff]&CONF_COMMENT)
+# define IS_FCOMMENT(c,a)        (KEYTYPES(c)[(a)&0xff]&CONF_FCOMMENT)
+# define IS_EOF(c,a)             (KEYTYPES(c)[(a)&0xff]&CONF_EOF)
+# define IS_ESC(c,a)             (KEYTYPES(c)[(a)&0xff]&CONF_ESC)
+# define IS_NUMBER(c,a)          (KEYTYPES(c)[(a)&0xff]&CONF_NUMBER)
+# define IS_WS(c,a)              (KEYTYPES(c)[(a)&0xff]&CONF_WS)
+# define IS_ALPHA_NUMERIC(c,a)   (KEYTYPES(c)[(a)&0xff]&CONF_ALPHA_NUMERIC)
+# define IS_ALPHA_NUMERIC_PUNCT(c,a) \
+                                (KEYTYPES(c)[(a)&0xff]&CONF_ALPHA_NUMERIC_PUNCT)
+# define IS_QUOTE(c,a)           (KEYTYPES(c)[(a)&0xff]&CONF_QUOTE)
+# define IS_DQUOTE(c,a)          (KEYTYPES(c)[(a)&0xff]&CONF_DQUOTE)
+# define IS_HIGHBIT(c,a)         (KEYTYPES(c)[(a)&0xff]&CONF_HIGHBIT)
+
+#else                           /* CHARSET_EBCDIC */
+
+# define IS_COMMENT(c,a)         (KEYTYPES(c)[os_toascii[a & 0xff]]&CONF_COMMENT)
+# define IS_FCOMMENT(c,a)        (KEYTYPES(c)[os_toascii[a & 0xff]]&CONF_FCOMMENT)
+# define IS_EOF(c,a)             (KEYTYPES(c)[os_toascii[a & 0xff]]&CONF_EOF)
+# define IS_ESC(c,a)             (KEYTYPES(c)[os_toascii[a & 0xff]]&CONF_ESC)
+# define IS_NUMBER(c,a)          (KEYTYPES(c)[os_toascii[a & 0xff]]&CONF_NUMBER)
+# define IS_WS(c,a)              (KEYTYPES(c)[os_toascii[a & 0xff]]&CONF_WS)
+# define IS_ALPHA_NUMERIC(c,a)   (KEYTYPES(c)[os_toascii[a & 0xff]]&CONF_ALPHA_NUMERIC)
+# define IS_ALPHA_NUMERIC_PUNCT(c,a) \
+                                (KEYTYPES(c)[os_toascii[a & 0xff]]&CONF_ALPHA_NUMERIC_PUNCT)
+# define IS_QUOTE(c,a)           (KEYTYPES(c)[os_toascii[a & 0xff]]&CONF_QUOTE)
+# define IS_DQUOTE(c,a)          (KEYTYPES(c)[os_toascii[a & 0xff]]&CONF_DQUOTE)
+# define IS_HIGHBIT(c,a)         (KEYTYPES(c)[os_toascii[a & 0xff]]&CONF_HIGHBIT)
+#endif                          /* CHARSET_EBCDIC */
+
+static const unsigned short CONF_type_default[256] = {
+    0x0008, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+    0x0000, 0x0010, 0x0010, 0x0000, 0x0000, 0x0010, 0x0000, 0x0000,
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+    0x0010, 0x0200, 0x0040, 0x0080, 0x0000, 0x0200, 0x0200, 0x0040,
+    0x0000, 0x0000, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+    0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
+    0x0001, 0x0001, 0x0000, 0x0200, 0x0000, 0x0000, 0x0000, 0x0200,
+    0x0200, 0x0002, 0x0002, 0x0002, 0x0002, 0x0002, 0x0002, 0x0002,
+    0x0002, 0x0002, 0x0002, 0x0002, 0x0002, 0x0002, 0x0002, 0x0002,
+    0x0002, 0x0002, 0x0002, 0x0002, 0x0002, 0x0002, 0x0002, 0x0002,
+    0x0002, 0x0002, 0x0002, 0x0000, 0x0020, 0x0000, 0x0200, 0x0100,
+    0x0040, 0x0004, 0x0004, 0x0004, 0x0004, 0x0004, 0x0004, 0x0004,
+    0x0004, 0x0004, 0x0004, 0x0004, 0x0004, 0x0004, 0x0004, 0x0004,
+    0x0004, 0x0004, 0x0004, 0x0004, 0x0004, 0x0004, 0x0004, 0x0004,
+    0x0004, 0x0004, 0x0004, 0x0000, 0x0200, 0x0000, 0x0200, 0x0000,
+    0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+    0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+    0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+    0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+    0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+    0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+    0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+    0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+    0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+    0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+    0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+    0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+    0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+    0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+    0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+    0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+};
+
+static const unsigned short CONF_type_win32[256] = {
+    0x0008, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+    0x0000, 0x0010, 0x0010, 0x0000, 0x0000, 0x0010, 0x0000, 0x0000,
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+    0x0010, 0x0200, 0x0400, 0x0000, 0x0000, 0x0200, 0x0200, 0x0000,
+    0x0000, 0x0000, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+    0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
+    0x0001, 0x0001, 0x0000, 0x0A00, 0x0000, 0x0000, 0x0000, 0x0200,
+    0x0200, 0x0002, 0x0002, 0x0002, 0x0002, 0x0002, 0x0002, 0x0002,
+    0x0002, 0x0002, 0x0002, 0x0002, 0x0002, 0x0002, 0x0002, 0x0002,
+    0x0002, 0x0002, 0x0002, 0x0002, 0x0002, 0x0002, 0x0002, 0x0002,
+    0x0002, 0x0002, 0x0002, 0x0000, 0x0000, 0x0000, 0x0200, 0x0100,
+    0x0000, 0x0004, 0x0004, 0x0004, 0x0004, 0x0004, 0x0004, 0x0004,
+    0x0004, 0x0004, 0x0004, 0x0004, 0x0004, 0x0004, 0x0004, 0x0004,
+    0x0004, 0x0004, 0x0004, 0x0004, 0x0004, 0x0004, 0x0004, 0x0004,
+    0x0004, 0x0004, 0x0004, 0x0000, 0x0200, 0x0000, 0x0200, 0x0000,
+    0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+    0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+    0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+    0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+    0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+    0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+    0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+    0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+    0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+    0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+    0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+    0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+    0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+    0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+    0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+    0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+};
diff --git a/crypto/conf/conf_err.c b/crypto/conf/conf_err.c
new file mode 100644
index 000000000..b583c057c
--- /dev/null
+++ b/crypto/conf/conf_err.c
@@ -0,0 +1,79 @@
+/*
+ * Generated by util/mkerr.pl DO NOT EDIT
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include 
+
+/* BEGIN ERROR CODES */
+#ifndef OPENSSL_NO_ERR
+
+# define ERR_FUNC(func) ERR_PACK(ERR_LIB_CONF,func,0)
+# define ERR_REASON(reason) ERR_PACK(ERR_LIB_CONF,0,reason)
+
+static ERR_STRING_DATA CONF_str_functs[] = {
+    {ERR_FUNC(CONF_F_CONF_DUMP_FP), "CONF_dump_fp"},
+    {ERR_FUNC(CONF_F_CONF_LOAD), "CONF_load"},
+    {ERR_FUNC(CONF_F_CONF_LOAD_FP), "CONF_load_fp"},
+    {ERR_FUNC(CONF_F_CONF_PARSE_LIST), "CONF_parse_list"},
+    {ERR_FUNC(CONF_F_DEF_LOAD), "def_load"},
+    {ERR_FUNC(CONF_F_DEF_LOAD_BIO), "def_load_bio"},
+    {ERR_FUNC(CONF_F_MODULE_INIT), "module_init"},
+    {ERR_FUNC(CONF_F_MODULE_LOAD_DSO), "module_load_dso"},
+    {ERR_FUNC(CONF_F_MODULE_RUN), "module_run"},
+    {ERR_FUNC(CONF_F_NCONF_DUMP_BIO), "NCONF_dump_bio"},
+    {ERR_FUNC(CONF_F_NCONF_DUMP_FP), "NCONF_dump_fp"},
+    {ERR_FUNC(CONF_F_NCONF_GET_NUMBER_E), "NCONF_get_number_e"},
+    {ERR_FUNC(CONF_F_NCONF_GET_SECTION), "NCONF_get_section"},
+    {ERR_FUNC(CONF_F_NCONF_GET_STRING), "NCONF_get_string"},
+    {ERR_FUNC(CONF_F_NCONF_LOAD), "NCONF_load"},
+    {ERR_FUNC(CONF_F_NCONF_LOAD_BIO), "NCONF_load_bio"},
+    {ERR_FUNC(CONF_F_NCONF_LOAD_FP), "NCONF_load_fp"},
+    {ERR_FUNC(CONF_F_NCONF_NEW), "NCONF_new"},
+    {ERR_FUNC(CONF_F_STR_COPY), "str_copy"},
+    {0, NULL}
+};
+
+static ERR_STRING_DATA CONF_str_reasons[] = {
+    {ERR_REASON(CONF_R_ERROR_LOADING_DSO), "error loading dso"},
+    {ERR_REASON(CONF_R_LIST_CANNOT_BE_NULL), "list cannot be null"},
+    {ERR_REASON(CONF_R_MISSING_CLOSE_SQUARE_BRACKET),
+     "missing close square bracket"},
+    {ERR_REASON(CONF_R_MISSING_EQUAL_SIGN), "missing equal sign"},
+    {ERR_REASON(CONF_R_MISSING_INIT_FUNCTION), "missing init function"},
+    {ERR_REASON(CONF_R_MODULE_INITIALIZATION_ERROR),
+     "module initialization error"},
+    {ERR_REASON(CONF_R_NO_CLOSE_BRACE), "no close brace"},
+    {ERR_REASON(CONF_R_NO_CONF), "no conf"},
+    {ERR_REASON(CONF_R_NO_CONF_OR_ENVIRONMENT_VARIABLE),
+     "no conf or environment variable"},
+    {ERR_REASON(CONF_R_NO_SECTION), "no section"},
+    {ERR_REASON(CONF_R_NO_SUCH_FILE), "no such file"},
+    {ERR_REASON(CONF_R_NO_VALUE), "no value"},
+    {ERR_REASON(CONF_R_UNABLE_TO_CREATE_NEW_SECTION),
+     "unable to create new section"},
+    {ERR_REASON(CONF_R_UNKNOWN_MODULE_NAME), "unknown module name"},
+    {ERR_REASON(CONF_R_VARIABLE_HAS_NO_VALUE), "variable has no value"},
+    {0, NULL}
+};
+
+#endif
+
+int ERR_load_CONF_strings(void)
+{
+#ifndef OPENSSL_NO_ERR
+
+    if (ERR_func_error_string(CONF_str_functs[0].error) == NULL) {
+        ERR_load_strings(0, CONF_str_functs);
+        ERR_load_strings(0, CONF_str_reasons);
+    }
+#endif
+    return 1;
+}
diff --git a/crypto/conf/conf_lib.c b/crypto/conf/conf_lib.c
new file mode 100644
index 000000000..353211491
--- /dev/null
+++ b/crypto/conf/conf_lib.c
@@ -0,0 +1,365 @@
+/*
+ * Copyright 2000-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include "e_os.h"
+
+static CONF_METHOD *default_CONF_method = NULL;
+
+/* Init a 'CONF' structure from an old LHASH */
+
+void CONF_set_nconf(CONF *conf, LHASH_OF(CONF_VALUE) *hash)
+{
+    if (default_CONF_method == NULL)
+        default_CONF_method = NCONF_default();
+
+    default_CONF_method->init(conf);
+    conf->data = hash;
+}
+
+/*
+ * The following section contains the "CONF classic" functions, rewritten in
+ * terms of the new CONF interface.
+ */
+
+int CONF_set_default_method(CONF_METHOD *meth)
+{
+    default_CONF_method = meth;
+    return 1;
+}
+
+LHASH_OF(CONF_VALUE) *CONF_load(LHASH_OF(CONF_VALUE) *conf, const char *file,
+                                long *eline)
+{
+    LHASH_OF(CONF_VALUE) *ltmp;
+    BIO *in = NULL;
+
+#ifdef OPENSSL_SYS_VMS
+    in = BIO_new_file(file, "r");
+#else
+    in = BIO_new_file(file, "rb");
+#endif
+    if (in == NULL) {
+        CONFerr(CONF_F_CONF_LOAD, ERR_R_SYS_LIB);
+        return NULL;
+    }
+
+    ltmp = CONF_load_bio(conf, in, eline);
+    BIO_free(in);
+
+    return ltmp;
+}
+
+#ifndef OPENSSL_NO_STDIO
+LHASH_OF(CONF_VALUE) *CONF_load_fp(LHASH_OF(CONF_VALUE) *conf, FILE *fp,
+                                   long *eline)
+{
+    BIO *btmp;
+    LHASH_OF(CONF_VALUE) *ltmp;
+    if ((btmp = BIO_new_fp(fp, BIO_NOCLOSE)) == NULL) {
+        CONFerr(CONF_F_CONF_LOAD_FP, ERR_R_BUF_LIB);
+        return NULL;
+    }
+    ltmp = CONF_load_bio(conf, btmp, eline);
+    BIO_free(btmp);
+    return ltmp;
+}
+#endif
+
+LHASH_OF(CONF_VALUE) *CONF_load_bio(LHASH_OF(CONF_VALUE) *conf, BIO *bp,
+                                    long *eline)
+{
+    CONF ctmp;
+    int ret;
+
+    CONF_set_nconf(&ctmp, conf);
+
+    ret = NCONF_load_bio(&ctmp, bp, eline);
+    if (ret)
+        return ctmp.data;
+    return NULL;
+}
+
+STACK_OF(CONF_VALUE) *CONF_get_section(LHASH_OF(CONF_VALUE) *conf,
+                                       const char *section)
+{
+    if (conf == NULL) {
+        return NULL;
+    } else {
+        CONF ctmp;
+        CONF_set_nconf(&ctmp, conf);
+        return NCONF_get_section(&ctmp, section);
+    }
+}
+
+char *CONF_get_string(LHASH_OF(CONF_VALUE) *conf, const char *group,
+                      const char *name)
+{
+    if (conf == NULL) {
+        return NCONF_get_string(NULL, group, name);
+    } else {
+        CONF ctmp;
+        CONF_set_nconf(&ctmp, conf);
+        return NCONF_get_string(&ctmp, group, name);
+    }
+}
+
+long CONF_get_number(LHASH_OF(CONF_VALUE) *conf, const char *group,
+                     const char *name)
+{
+    int status;
+    long result = 0;
+
+    if (conf == NULL) {
+        status = NCONF_get_number_e(NULL, group, name, &result);
+    } else {
+        CONF ctmp;
+        CONF_set_nconf(&ctmp, conf);
+        status = NCONF_get_number_e(&ctmp, group, name, &result);
+    }
+
+    if (status == 0) {
+        /* This function does not believe in errors... */
+        ERR_clear_error();
+    }
+    return result;
+}
+
+void CONF_free(LHASH_OF(CONF_VALUE) *conf)
+{
+    CONF ctmp;
+    CONF_set_nconf(&ctmp, conf);
+    NCONF_free_data(&ctmp);
+}
+
+#ifndef OPENSSL_NO_STDIO
+int CONF_dump_fp(LHASH_OF(CONF_VALUE) *conf, FILE *out)
+{
+    BIO *btmp;
+    int ret;
+
+    if ((btmp = BIO_new_fp(out, BIO_NOCLOSE)) == NULL) {
+        CONFerr(CONF_F_CONF_DUMP_FP, ERR_R_BUF_LIB);
+        return 0;
+    }
+    ret = CONF_dump_bio(conf, btmp);
+    BIO_free(btmp);
+    return ret;
+}
+#endif
+
+int CONF_dump_bio(LHASH_OF(CONF_VALUE) *conf, BIO *out)
+{
+    CONF ctmp;
+    CONF_set_nconf(&ctmp, conf);
+    return NCONF_dump_bio(&ctmp, out);
+}
+
+/*
+ * The following section contains the "New CONF" functions.  They are
+ * completely centralised around a new CONF structure that may contain
+ * basically anything, but at least a method pointer and a table of data.
+ * These functions are also written in terms of the bridge functions used by
+ * the "CONF classic" functions, for consistency.
+ */
+
+CONF *NCONF_new(CONF_METHOD *meth)
+{
+    CONF *ret;
+
+    if (meth == NULL)
+        meth = NCONF_default();
+
+    ret = meth->create(meth);
+    if (ret == NULL) {
+        CONFerr(CONF_F_NCONF_NEW, ERR_R_MALLOC_FAILURE);
+        return (NULL);
+    }
+
+    return ret;
+}
+
+void NCONF_free(CONF *conf)
+{
+    if (conf == NULL)
+        return;
+    conf->meth->destroy(conf);
+}
+
+void NCONF_free_data(CONF *conf)
+{
+    if (conf == NULL)
+        return;
+    conf->meth->destroy_data(conf);
+}
+
+int NCONF_load(CONF *conf, const char *file, long *eline)
+{
+    if (conf == NULL) {
+        CONFerr(CONF_F_NCONF_LOAD, CONF_R_NO_CONF);
+        return 0;
+    }
+
+    return conf->meth->load(conf, file, eline);
+}
+
+#ifndef OPENSSL_NO_STDIO
+int NCONF_load_fp(CONF *conf, FILE *fp, long *eline)
+{
+    BIO *btmp;
+    int ret;
+    if ((btmp = BIO_new_fp(fp, BIO_NOCLOSE)) == NULL) {
+        CONFerr(CONF_F_NCONF_LOAD_FP, ERR_R_BUF_LIB);
+        return 0;
+    }
+    ret = NCONF_load_bio(conf, btmp, eline);
+    BIO_free(btmp);
+    return ret;
+}
+#endif
+
+int NCONF_load_bio(CONF *conf, BIO *bp, long *eline)
+{
+    if (conf == NULL) {
+        CONFerr(CONF_F_NCONF_LOAD_BIO, CONF_R_NO_CONF);
+        return 0;
+    }
+
+    return conf->meth->load_bio(conf, bp, eline);
+}
+
+STACK_OF(CONF_VALUE) *NCONF_get_section(const CONF *conf, const char *section)
+{
+    if (conf == NULL) {
+        CONFerr(CONF_F_NCONF_GET_SECTION, CONF_R_NO_CONF);
+        return NULL;
+    }
+
+    if (section == NULL) {
+        CONFerr(CONF_F_NCONF_GET_SECTION, CONF_R_NO_SECTION);
+        return NULL;
+    }
+
+    return _CONF_get_section_values(conf, section);
+}
+
+char *NCONF_get_string(const CONF *conf, const char *group, const char *name)
+{
+    char *s = _CONF_get_string(conf, group, name);
+
+    /*
+     * Since we may get a value from an environment variable even if conf is
+     * NULL, let's check the value first
+     */
+    if (s)
+        return s;
+
+    if (conf == NULL) {
+        CONFerr(CONF_F_NCONF_GET_STRING,
+                CONF_R_NO_CONF_OR_ENVIRONMENT_VARIABLE);
+        return NULL;
+    }
+    CONFerr(CONF_F_NCONF_GET_STRING, CONF_R_NO_VALUE);
+    ERR_add_error_data(4, "group=", group, " name=", name);
+    return NULL;
+}
+
+int NCONF_get_number_e(const CONF *conf, const char *group, const char *name,
+                       long *result)
+{
+    char *str;
+
+    if (result == NULL) {
+        CONFerr(CONF_F_NCONF_GET_NUMBER_E, ERR_R_PASSED_NULL_PARAMETER);
+        return 0;
+    }
+
+    str = NCONF_get_string(conf, group, name);
+
+    if (str == NULL)
+        return 0;
+
+    for (*result = 0; conf->meth->is_number(conf, *str);) {
+        *result = (*result) * 10 + conf->meth->to_int(conf, *str);
+        str++;
+    }
+
+    return 1;
+}
+
+#ifndef OPENSSL_NO_STDIO
+int NCONF_dump_fp(const CONF *conf, FILE *out)
+{
+    BIO *btmp;
+    int ret;
+    if ((btmp = BIO_new_fp(out, BIO_NOCLOSE)) == NULL) {
+        CONFerr(CONF_F_NCONF_DUMP_FP, ERR_R_BUF_LIB);
+        return 0;
+    }
+    ret = NCONF_dump_bio(conf, btmp);
+    BIO_free(btmp);
+    return ret;
+}
+#endif
+
+int NCONF_dump_bio(const CONF *conf, BIO *out)
+{
+    if (conf == NULL) {
+        CONFerr(CONF_F_NCONF_DUMP_BIO, CONF_R_NO_CONF);
+        return 0;
+    }
+
+    return conf->meth->dump(conf, out);
+}
+
+/*
+ * These routines call the C malloc/free, to avoid intermixing with
+ * OpenSSL function pointers before the library is initialized.
+ */
+OPENSSL_INIT_SETTINGS *OPENSSL_INIT_new(void)
+{
+    OPENSSL_INIT_SETTINGS *ret = malloc(sizeof(*ret));
+
+    if (ret != NULL)
+        memset(ret, 0, sizeof(*ret));
+    return ret;
+}
+
+
+#ifndef OPENSSL_NO_STDIO
+int OPENSSL_INIT_set_config_appname(OPENSSL_INIT_SETTINGS *settings,
+                                    const char *appname)
+{
+    char *newappname = NULL;
+
+    if (appname != NULL) {
+        newappname = strdup(appname);
+        if (newappname == NULL)
+            return 0;
+    }
+
+    free(settings->appname);
+    settings->appname = newappname;
+
+    return 1;
+}
+#endif
+
+void OPENSSL_INIT_free(OPENSSL_INIT_SETTINGS *settings)
+{
+    free(settings->appname);
+    free(settings);
+}
diff --git a/crypto/conf/conf_mall.c b/crypto/conf/conf_mall.c
new file mode 100644
index 000000000..b2850b30c
--- /dev/null
+++ b/crypto/conf/conf_mall.c
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2002-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+#include 
+#ifndef OPENSSL_NO_ENGINE
+# include 
+#endif
+
+/* Load all OpenSSL builtin modules */
+
+void OPENSSL_load_builtin_modules(void)
+{
+    /* Add builtin modules here */
+    ASN1_add_oid_module();
+    ASN1_add_stable_module();
+#ifndef OPENSSL_NO_ENGINE
+    ENGINE_add_conf_module();
+#endif
+    EVP_add_alg_module();
+}
diff --git a/crypto/conf/conf_mod.c b/crypto/conf/conf_mod.c
new file mode 100644
index 000000000..31f838e0f
--- /dev/null
+++ b/crypto/conf/conf_mod.c
@@ -0,0 +1,549 @@
+/*
+ * Copyright 2002-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include 
+#include "internal/cryptlib.h"
+#include "internal/conf.h"
+#include "internal/dso.h"
+#include 
+
+#define DSO_mod_init_name "OPENSSL_init"
+#define DSO_mod_finish_name "OPENSSL_finish"
+
+/*
+ * This structure contains a data about supported modules. entries in this
+ * table correspond to either dynamic or static modules.
+ */
+
+struct conf_module_st {
+    /* DSO of this module or NULL if static */
+    DSO *dso;
+    /* Name of the module */
+    char *name;
+    /* Init function */
+    conf_init_func *init;
+    /* Finish function */
+    conf_finish_func *finish;
+    /* Number of successfully initialized modules */
+    int links;
+    void *usr_data;
+};
+
+/*
+ * This structure contains information about modules that have been
+ * successfully initialized. There may be more than one entry for a given
+ * module.
+ */
+
+struct conf_imodule_st {
+    CONF_MODULE *pmod;
+    char *name;
+    char *value;
+    unsigned long flags;
+    void *usr_data;
+};
+
+static STACK_OF(CONF_MODULE) *supported_modules = NULL;
+static STACK_OF(CONF_IMODULE) *initialized_modules = NULL;
+
+static void module_free(CONF_MODULE *md);
+static void module_finish(CONF_IMODULE *imod);
+static int module_run(const CONF *cnf, const char *name, const char *value,
+                      unsigned long flags);
+static CONF_MODULE *module_add(DSO *dso, const char *name,
+                               conf_init_func *ifunc,
+                               conf_finish_func *ffunc);
+static CONF_MODULE *module_find(const char *name);
+static int module_init(CONF_MODULE *pmod, const char *name, const char *value,
+                       const CONF *cnf);
+static CONF_MODULE *module_load_dso(const CONF *cnf, const char *name,
+                                    const char *value);
+
+/* Main function: load modules from a CONF structure */
+
+int CONF_modules_load(const CONF *cnf, const char *appname,
+                      unsigned long flags)
+{
+    STACK_OF(CONF_VALUE) *values;
+    CONF_VALUE *vl;
+    char *vsection = NULL;
+
+    int ret, i;
+
+    if (!cnf)
+        return 1;
+
+    if (appname)
+        vsection = NCONF_get_string(cnf, NULL, appname);
+
+    if (!appname || (!vsection && (flags & CONF_MFLAGS_DEFAULT_SECTION)))
+        vsection = NCONF_get_string(cnf, NULL, "openssl_conf");
+
+    if (!vsection) {
+        ERR_clear_error();
+        return 1;
+    }
+
+    values = NCONF_get_section(cnf, vsection);
+
+    if (!values)
+        return 0;
+
+    for (i = 0; i < sk_CONF_VALUE_num(values); i++) {
+        vl = sk_CONF_VALUE_value(values, i);
+        ret = module_run(cnf, vl->name, vl->value, flags);
+        if (ret <= 0)
+            if (!(flags & CONF_MFLAGS_IGNORE_ERRORS))
+                return ret;
+    }
+
+    return 1;
+
+}
+
+int CONF_modules_load_file(const char *filename, const char *appname,
+                           unsigned long flags)
+{
+    char *file = NULL;
+    CONF *conf = NULL;
+    int ret = 0;
+    conf = NCONF_new(NULL);
+    if (conf == NULL)
+        goto err;
+
+    if (filename == NULL) {
+        file = CONF_get1_default_config_file();
+        if (!file)
+            goto err;
+    } else
+        file = (char *)filename;
+
+    if (NCONF_load(conf, file, NULL) <= 0) {
+        if ((flags & CONF_MFLAGS_IGNORE_MISSING_FILE) &&
+            (ERR_GET_REASON(ERR_peek_last_error()) == CONF_R_NO_SUCH_FILE)) {
+            ERR_clear_error();
+            ret = 1;
+        }
+        goto err;
+    }
+
+    ret = CONF_modules_load(conf, appname, flags);
+
+ err:
+    if (filename == NULL)
+        OPENSSL_free(file);
+    NCONF_free(conf);
+
+    return ret;
+}
+
+static int module_run(const CONF *cnf, const char *name, const char *value,
+                      unsigned long flags)
+{
+    CONF_MODULE *md;
+    int ret;
+
+    md = module_find(name);
+
+    /* Module not found: try to load DSO */
+    if (!md && !(flags & CONF_MFLAGS_NO_DSO))
+        md = module_load_dso(cnf, name, value);
+
+    if (!md) {
+        if (!(flags & CONF_MFLAGS_SILENT)) {
+            CONFerr(CONF_F_MODULE_RUN, CONF_R_UNKNOWN_MODULE_NAME);
+            ERR_add_error_data(2, "module=", name);
+        }
+        return -1;
+    }
+
+    ret = module_init(md, name, value, cnf);
+
+    if (ret <= 0) {
+        if (!(flags & CONF_MFLAGS_SILENT)) {
+            char rcode[DECIMAL_SIZE(ret) + 1];
+            CONFerr(CONF_F_MODULE_RUN, CONF_R_MODULE_INITIALIZATION_ERROR);
+            BIO_snprintf(rcode, sizeof rcode, "%-8d", ret);
+            ERR_add_error_data(6, "module=", name, ", value=", value,
+                               ", retcode=", rcode);
+        }
+    }
+
+    return ret;
+}
+
+/* Load a module from a DSO */
+static CONF_MODULE *module_load_dso(const CONF *cnf,
+                                    const char *name, const char *value)
+{
+    DSO *dso = NULL;
+    conf_init_func *ifunc;
+    conf_finish_func *ffunc;
+    const char *path = NULL;
+    int errcode = 0;
+    CONF_MODULE *md;
+    /* Look for alternative path in module section */
+    path = NCONF_get_string(cnf, value, "path");
+    if (!path) {
+        ERR_clear_error();
+        path = name;
+    }
+    dso = DSO_load(NULL, path, NULL, 0);
+    if (!dso) {
+        errcode = CONF_R_ERROR_LOADING_DSO;
+        goto err;
+    }
+    ifunc = (conf_init_func *)DSO_bind_func(dso, DSO_mod_init_name);
+    if (!ifunc) {
+        errcode = CONF_R_MISSING_INIT_FUNCTION;
+        goto err;
+    }
+    ffunc = (conf_finish_func *)DSO_bind_func(dso, DSO_mod_finish_name);
+    /* All OK, add module */
+    md = module_add(dso, name, ifunc, ffunc);
+
+    if (!md)
+        goto err;
+
+    return md;
+
+ err:
+    DSO_free(dso);
+    CONFerr(CONF_F_MODULE_LOAD_DSO, errcode);
+    ERR_add_error_data(4, "module=", name, ", path=", path);
+    return NULL;
+}
+
+/* add module to list */
+static CONF_MODULE *module_add(DSO *dso, const char *name,
+                               conf_init_func *ifunc, conf_finish_func *ffunc)
+{
+    CONF_MODULE *tmod = NULL;
+    if (supported_modules == NULL)
+        supported_modules = sk_CONF_MODULE_new_null();
+    if (supported_modules == NULL)
+        return NULL;
+    tmod = OPENSSL_zalloc(sizeof(*tmod));
+    if (tmod == NULL)
+        return NULL;
+
+    tmod->dso = dso;
+    tmod->name = OPENSSL_strdup(name);
+    tmod->init = ifunc;
+    tmod->finish = ffunc;
+    if (tmod->name == NULL) {
+        OPENSSL_free(tmod);
+        return NULL;
+    }
+
+    if (!sk_CONF_MODULE_push(supported_modules, tmod)) {
+        OPENSSL_free(tmod->name);
+        OPENSSL_free(tmod);
+        return NULL;
+    }
+
+    return tmod;
+}
+
+/*
+ * Find a module from the list. We allow module names of the form
+ * modname.XXXX to just search for modname to allow the same module to be
+ * initialized more than once.
+ */
+
+static CONF_MODULE *module_find(const char *name)
+{
+    CONF_MODULE *tmod;
+    int i, nchar;
+    char *p;
+    p = strrchr(name, '.');
+
+    if (p)
+        nchar = p - name;
+    else
+        nchar = strlen(name);
+
+    for (i = 0; i < sk_CONF_MODULE_num(supported_modules); i++) {
+        tmod = sk_CONF_MODULE_value(supported_modules, i);
+        if (strncmp(tmod->name, name, nchar) == 0)
+            return tmod;
+    }
+
+    return NULL;
+
+}
+
+/* initialize a module */
+static int module_init(CONF_MODULE *pmod, const char *name, const char *value,
+                       const CONF *cnf)
+{
+    int ret = 1;
+    int init_called = 0;
+    CONF_IMODULE *imod = NULL;
+
+    /* Otherwise add initialized module to list */
+    imod = OPENSSL_malloc(sizeof(*imod));
+    if (imod == NULL)
+        goto err;
+
+    imod->pmod = pmod;
+    imod->name = OPENSSL_strdup(name);
+    imod->value = OPENSSL_strdup(value);
+    imod->usr_data = NULL;
+
+    if (!imod->name || !imod->value)
+        goto memerr;
+
+    /* Try to initialize module */
+    if (pmod->init) {
+        ret = pmod->init(imod, cnf);
+        init_called = 1;
+        /* Error occurred, exit */
+        if (ret <= 0)
+            goto err;
+    }
+
+    if (initialized_modules == NULL) {
+        initialized_modules = sk_CONF_IMODULE_new_null();
+        if (!initialized_modules) {
+            CONFerr(CONF_F_MODULE_INIT, ERR_R_MALLOC_FAILURE);
+            goto err;
+        }
+    }
+
+    if (!sk_CONF_IMODULE_push(initialized_modules, imod)) {
+        CONFerr(CONF_F_MODULE_INIT, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+
+    pmod->links++;
+
+    return ret;
+
+ err:
+
+    /* We've started the module so we'd better finish it */
+    if (pmod->finish && init_called)
+        pmod->finish(imod);
+
+ memerr:
+    if (imod) {
+        OPENSSL_free(imod->name);
+        OPENSSL_free(imod->value);
+        OPENSSL_free(imod);
+    }
+
+    return -1;
+
+}
+
+/*
+ * Unload any dynamic modules that have a link count of zero: i.e. have no
+ * active initialized modules. If 'all' is set then all modules are unloaded
+ * including static ones.
+ */
+
+void CONF_modules_unload(int all)
+{
+    int i;
+    CONF_MODULE *md;
+    CONF_modules_finish();
+    /* unload modules in reverse order */
+    for (i = sk_CONF_MODULE_num(supported_modules) - 1; i >= 0; i--) {
+        md = sk_CONF_MODULE_value(supported_modules, i);
+        /* If static or in use and 'all' not set ignore it */
+        if (((md->links > 0) || !md->dso) && !all)
+            continue;
+        /* Since we're working in reverse this is OK */
+        (void)sk_CONF_MODULE_delete(supported_modules, i);
+        module_free(md);
+    }
+    if (sk_CONF_MODULE_num(supported_modules) == 0) {
+        sk_CONF_MODULE_free(supported_modules);
+        supported_modules = NULL;
+    }
+}
+
+/* unload a single module */
+static void module_free(CONF_MODULE *md)
+{
+    DSO_free(md->dso);
+    OPENSSL_free(md->name);
+    OPENSSL_free(md);
+}
+
+/* finish and free up all modules instances */
+
+void CONF_modules_finish(void)
+{
+    CONF_IMODULE *imod;
+    while (sk_CONF_IMODULE_num(initialized_modules) > 0) {
+        imod = sk_CONF_IMODULE_pop(initialized_modules);
+        module_finish(imod);
+    }
+    sk_CONF_IMODULE_free(initialized_modules);
+    initialized_modules = NULL;
+}
+
+/* finish a module instance */
+
+static void module_finish(CONF_IMODULE *imod)
+{
+    if (!imod)
+        return;
+    if (imod->pmod->finish)
+        imod->pmod->finish(imod);
+    imod->pmod->links--;
+    OPENSSL_free(imod->name);
+    OPENSSL_free(imod->value);
+    OPENSSL_free(imod);
+}
+
+/* Add a static module to OpenSSL */
+
+int CONF_module_add(const char *name, conf_init_func *ifunc,
+                    conf_finish_func *ffunc)
+{
+    if (module_add(NULL, name, ifunc, ffunc))
+        return 1;
+    else
+        return 0;
+}
+
+void conf_modules_free_int(void)
+{
+    CONF_modules_finish();
+    CONF_modules_unload(1);
+}
+
+/* Utility functions */
+
+const char *CONF_imodule_get_name(const CONF_IMODULE *md)
+{
+    return md->name;
+}
+
+const char *CONF_imodule_get_value(const CONF_IMODULE *md)
+{
+    return md->value;
+}
+
+void *CONF_imodule_get_usr_data(const CONF_IMODULE *md)
+{
+    return md->usr_data;
+}
+
+void CONF_imodule_set_usr_data(CONF_IMODULE *md, void *usr_data)
+{
+    md->usr_data = usr_data;
+}
+
+CONF_MODULE *CONF_imodule_get_module(const CONF_IMODULE *md)
+{
+    return md->pmod;
+}
+
+unsigned long CONF_imodule_get_flags(const CONF_IMODULE *md)
+{
+    return md->flags;
+}
+
+void CONF_imodule_set_flags(CONF_IMODULE *md, unsigned long flags)
+{
+    md->flags = flags;
+}
+
+void *CONF_module_get_usr_data(CONF_MODULE *pmod)
+{
+    return pmod->usr_data;
+}
+
+void CONF_module_set_usr_data(CONF_MODULE *pmod, void *usr_data)
+{
+    pmod->usr_data = usr_data;
+}
+
+/* Return default config file name */
+
+char *CONF_get1_default_config_file(void)
+{
+    char *file;
+    int len;
+
+    file = getenv("OPENSSL_CONF");
+    if (file)
+        return OPENSSL_strdup(file);
+
+    len = strlen(X509_get_default_cert_area());
+#ifndef OPENSSL_SYS_VMS
+    len++;
+#endif
+    len += strlen(OPENSSL_CONF);
+
+    file = OPENSSL_malloc(len + 1);
+
+    if (file == NULL)
+        return NULL;
+    OPENSSL_strlcpy(file, X509_get_default_cert_area(), len + 1);
+#ifndef OPENSSL_SYS_VMS
+    OPENSSL_strlcat(file, "/", len + 1);
+#endif
+    OPENSSL_strlcat(file, OPENSSL_CONF, len + 1);
+
+    return file;
+}
+
+/*
+ * This function takes a list separated by 'sep' and calls the callback
+ * function giving the start and length of each member optionally stripping
+ * leading and trailing whitespace. This can be used to parse comma separated
+ * lists for example.
+ */
+
+int CONF_parse_list(const char *list_, int sep, int nospc,
+                    int (*list_cb) (const char *elem, int len, void *usr),
+                    void *arg)
+{
+    int ret;
+    const char *lstart, *tmpend, *p;
+
+    if (list_ == NULL) {
+        CONFerr(CONF_F_CONF_PARSE_LIST, CONF_R_LIST_CANNOT_BE_NULL);
+        return 0;
+    }
+
+    lstart = list_;
+    for (;;) {
+        if (nospc) {
+            while (*lstart && isspace((unsigned char)*lstart))
+                lstart++;
+        }
+        p = strchr(lstart, sep);
+        if (p == lstart || !*lstart)
+            ret = list_cb(NULL, 0, arg);
+        else {
+            if (p)
+                tmpend = p - 1;
+            else
+                tmpend = lstart + strlen(lstart) - 1;
+            if (nospc) {
+                while (isspace((unsigned char)*tmpend))
+                    tmpend--;
+            }
+            ret = list_cb(lstart, tmpend - lstart + 1, arg);
+        }
+        if (ret <= 0)
+            return ret;
+        if (p == NULL)
+            return 1;
+        lstart = p + 1;
+    }
+}
diff --git a/crypto/conf/conf_sap.c b/crypto/conf/conf_sap.c
new file mode 100644
index 000000000..517c70533
--- /dev/null
+++ b/crypto/conf/conf_sap.c
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2002-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+#include 
+#ifndef OPENSSL_NO_ENGINE
+# include 
+#endif
+
+/*
+ * This is the automatic configuration loader: it is called automatically by
+ * OpenSSL when any of a number of standard initialisation functions are
+ * called, unless this is overridden by calling OPENSSL_no_config()
+ */
+
+static int openssl_configured = 0;
+
+#if OPENSSL_API_COMPAT < 0x10100000L
+void OPENSSL_config(const char *appname)
+{
+    OPENSSL_INIT_SETTINGS settings;
+
+    memset(&settings, 0, sizeof(settings));
+    if (appname != NULL)
+        settings.appname = strdup(appname);
+    OPENSSL_init_crypto(OPENSSL_INIT_LOAD_CONFIG, &settings);
+}
+#endif
+
+void openssl_config_int(const char *appname)
+{
+    if (openssl_configured)
+        return;
+
+    OPENSSL_load_builtin_modules();
+#ifndef OPENSSL_NO_ENGINE
+    /* Need to load ENGINEs */
+    ENGINE_load_builtin_engines();
+#endif
+    ERR_clear_error();
+#ifndef OPENSSL_SYS_UEFI
+    CONF_modules_load_file(NULL, appname,
+                               CONF_MFLAGS_DEFAULT_SECTION |
+                               CONF_MFLAGS_IGNORE_MISSING_FILE);
+#endif
+    openssl_configured = 1;
+}
+
+void openssl_no_config_int(void)
+{
+    openssl_configured = 1;
+}
diff --git a/crypto/conf/keysets.pl b/crypto/conf/keysets.pl
new file mode 100644
index 000000000..5af08ae20
--- /dev/null
+++ b/crypto/conf/keysets.pl
@@ -0,0 +1,141 @@
+#! /usr/bin/env perl
+# Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+$NUMBER=0x01;
+$UPPER=0x02;
+$LOWER=0x04;
+$UNDER=0x100;
+$PUNCTUATION=0x200;
+$WS=0x10;
+$ESC=0x20;
+$QUOTE=0x40;
+$DQUOTE=0x400;
+$COMMENT=0x80;
+$FCOMMENT=0x800;
+$EOF=0x08;
+$HIGHBIT=0x1000;
+
+foreach (0 .. 255)
+	{
+	$v=0;
+	$c=sprintf("%c",$_);
+	$v|=$NUMBER	if ($c =~ /[0-9]/);
+	$v|=$UPPER	if ($c =~ /[A-Z]/);
+	$v|=$LOWER	if ($c =~ /[a-z]/);
+	$v|=$UNDER	if ($c =~ /_/);
+	$v|=$PUNCTUATION if ($c =~ /[!\.%&\*\+,\/;\?\@\^\~\|-]/);
+	$v|=$WS		if ($c =~ /[ \t\r\n]/);
+	$v|=$ESC	if ($c =~ /\\/);
+	$v|=$QUOTE	if ($c =~ /['`"]/); # for emacs: "`'}/)
+	$v|=$COMMENT	if ($c =~ /\#/);
+	$v|=$EOF	if ($c =~ /\0/);
+	$v|=$HIGHBIT	if ($c =~/[\x80-\xff]/);
+
+	push(@V_def,$v);
+	}
+
+foreach (0 .. 255)
+	{
+	$v=0;
+	$c=sprintf("%c",$_);
+	$v|=$NUMBER	if ($c =~ /[0-9]/);
+	$v|=$UPPER	if ($c =~ /[A-Z]/);
+	$v|=$LOWER	if ($c =~ /[a-z]/);
+	$v|=$UNDER	if ($c =~ /_/);
+	$v|=$PUNCTUATION if ($c =~ /[!\.%&\*\+,\/;\?\@\^\~\|-]/);
+	$v|=$WS		if ($c =~ /[ \t\r\n]/);
+	$v|=$DQUOTE	if ($c =~ /["]/); # for emacs: "}/)
+	$v|=$FCOMMENT	if ($c =~ /;/);
+	$v|=$EOF	if ($c =~ /\0/);
+	$v|=$HIGHBIT	if ($c =~/[\x80-\xff]/);
+
+	push(@V_w32,$v);
+	}
+
+print <<"EOF";
+/*
+ * WARNING: do not edit!
+ * Generated by crypto/conf/keysets.pl
+ *
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#define CONF_NUMBER             $NUMBER
+#define CONF_UPPER              $UPPER
+#define CONF_LOWER              $LOWER
+#define CONF_UNDER              $UNDER
+#define CONF_PUNCTUATION        $PUNCTUATION
+#define CONF_WS                 $WS
+#define CONF_ESC                $ESC
+#define CONF_QUOTE              $QUOTE
+#define CONF_DQUOTE             $DQUOTE
+#define CONF_COMMENT            $COMMENT
+#define CONF_FCOMMENT           $FCOMMENT
+#define CONF_EOF                $EOF
+#define CONF_HIGHBIT            $HIGHBIT
+#define CONF_ALPHA              (CONF_UPPER|CONF_LOWER)
+#define CONF_ALPHA_NUMERIC      (CONF_ALPHA|CONF_NUMBER|CONF_UNDER)
+#define CONF_ALPHA_NUMERIC_PUNCT (CONF_ALPHA|CONF_NUMBER|CONF_UNDER| \\
+                                        CONF_PUNCTUATION)
+
+#define KEYTYPES(c)             ((const unsigned short *)((c)->meth_data))
+#ifndef CHARSET_EBCDIC
+# define IS_COMMENT(c,a)         (KEYTYPES(c)[(a)&0xff]&CONF_COMMENT)
+# define IS_FCOMMENT(c,a)        (KEYTYPES(c)[(a)&0xff]&CONF_FCOMMENT)
+# define IS_EOF(c,a)             (KEYTYPES(c)[(a)&0xff]&CONF_EOF)
+# define IS_ESC(c,a)             (KEYTYPES(c)[(a)&0xff]&CONF_ESC)
+# define IS_NUMBER(c,a)          (KEYTYPES(c)[(a)&0xff]&CONF_NUMBER)
+# define IS_WS(c,a)              (KEYTYPES(c)[(a)&0xff]&CONF_WS)
+# define IS_ALPHA_NUMERIC(c,a)   (KEYTYPES(c)[(a)&0xff]&CONF_ALPHA_NUMERIC)
+# define IS_ALPHA_NUMERIC_PUNCT(c,a) \\
+                                (KEYTYPES(c)[(a)&0xff]&CONF_ALPHA_NUMERIC_PUNCT)
+# define IS_QUOTE(c,a)           (KEYTYPES(c)[(a)&0xff]&CONF_QUOTE)
+# define IS_DQUOTE(c,a)          (KEYTYPES(c)[(a)&0xff]&CONF_DQUOTE)
+# define IS_HIGHBIT(c,a)         (KEYTYPES(c)[(a)&0xff]&CONF_HIGHBIT)
+
+#else                           /* CHARSET_EBCDIC */
+
+# define IS_COMMENT(c,a)         (KEYTYPES(c)[os_toascii[a & 0xff]]&CONF_COMMENT)
+# define IS_FCOMMENT(c,a)        (KEYTYPES(c)[os_toascii[a & 0xff]]&CONF_FCOMMENT)
+# define IS_EOF(c,a)             (KEYTYPES(c)[os_toascii[a & 0xff]]&CONF_EOF)
+# define IS_ESC(c,a)             (KEYTYPES(c)[os_toascii[a & 0xff]]&CONF_ESC)
+# define IS_NUMBER(c,a)          (KEYTYPES(c)[os_toascii[a & 0xff]]&CONF_NUMBER)
+# define IS_WS(c,a)              (KEYTYPES(c)[os_toascii[a & 0xff]]&CONF_WS)
+# define IS_ALPHA_NUMERIC(c,a)   (KEYTYPES(c)[os_toascii[a & 0xff]]&CONF_ALPHA_NUMERIC)
+# define IS_ALPHA_NUMERIC_PUNCT(c,a) \\
+                                (KEYTYPES(c)[os_toascii[a & 0xff]]&CONF_ALPHA_NUMERIC_PUNCT)
+# define IS_QUOTE(c,a)           (KEYTYPES(c)[os_toascii[a & 0xff]]&CONF_QUOTE)
+# define IS_DQUOTE(c,a)          (KEYTYPES(c)[os_toascii[a & 0xff]]&CONF_DQUOTE)
+# define IS_HIGHBIT(c,a)         (KEYTYPES(c)[os_toascii[a & 0xff]]&CONF_HIGHBIT)
+#endif                          /* CHARSET_EBCDIC */
+
+EOF
+
+print "static const unsigned short CONF_type_default[256] = {";
+
+for ($i=0; $i<256; $i++)
+	{
+	print "\n   " if ($i % 8) == 0;
+	printf " 0x%04X,",$V_def[$i];
+	}
+
+print "\n};\n\n";
+
+print "static const unsigned short CONF_type_win32[256] = {";
+
+for ($i=0; $i<256; $i++)
+	{
+	print "\n   " if ($i % 8) == 0;
+	printf " 0x%04X,",$V_w32[$i];
+	}
+
+print "\n};\n";
diff --git a/crypto/cpt_err.c b/crypto/cpt_err.c
new file mode 100644
index 000000000..c28dcf19a
--- /dev/null
+++ b/crypto/cpt_err.c
@@ -0,0 +1,55 @@
+/*
+ * Generated by util/mkerr.pl DO NOT EDIT
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include 
+
+/* BEGIN ERROR CODES */
+#ifndef OPENSSL_NO_ERR
+
+# define ERR_FUNC(func) ERR_PACK(ERR_LIB_CRYPTO,func,0)
+# define ERR_REASON(reason) ERR_PACK(ERR_LIB_CRYPTO,0,reason)
+
+static ERR_STRING_DATA CRYPTO_str_functs[] = {
+    {ERR_FUNC(CRYPTO_F_CRYPTO_DUP_EX_DATA), "CRYPTO_dup_ex_data"},
+    {ERR_FUNC(CRYPTO_F_CRYPTO_FREE_EX_DATA), "CRYPTO_free_ex_data"},
+    {ERR_FUNC(CRYPTO_F_CRYPTO_GET_EX_NEW_INDEX), "CRYPTO_get_ex_new_index"},
+    {ERR_FUNC(CRYPTO_F_CRYPTO_MEMDUP), "CRYPTO_memdup"},
+    {ERR_FUNC(CRYPTO_F_CRYPTO_NEW_EX_DATA), "CRYPTO_new_ex_data"},
+    {ERR_FUNC(CRYPTO_F_CRYPTO_SET_EX_DATA), "CRYPTO_set_ex_data"},
+    {ERR_FUNC(CRYPTO_F_FIPS_MODE_SET), "FIPS_mode_set"},
+    {ERR_FUNC(CRYPTO_F_GET_AND_LOCK), "get_and_lock"},
+    {ERR_FUNC(CRYPTO_F_OPENSSL_BUF2HEXSTR), "OPENSSL_buf2hexstr"},
+    {ERR_FUNC(CRYPTO_F_OPENSSL_HEXSTR2BUF), "OPENSSL_hexstr2buf"},
+    {ERR_FUNC(CRYPTO_F_OPENSSL_INIT_CRYPTO), "OPENSSL_init_crypto"},
+    {0, NULL}
+};
+
+static ERR_STRING_DATA CRYPTO_str_reasons[] = {
+    {ERR_REASON(CRYPTO_R_FIPS_MODE_NOT_SUPPORTED), "fips mode not supported"},
+    {ERR_REASON(CRYPTO_R_ILLEGAL_HEX_DIGIT), "illegal hex digit"},
+    {ERR_REASON(CRYPTO_R_ODD_NUMBER_OF_DIGITS), "odd number of digits"},
+    {0, NULL}
+};
+
+#endif
+
+int ERR_load_CRYPTO_strings(void)
+{
+#ifndef OPENSSL_NO_ERR
+
+    if (ERR_func_error_string(CRYPTO_str_functs[0].error) == NULL) {
+        ERR_load_strings(0, CRYPTO_str_functs);
+        ERR_load_strings(0, CRYPTO_str_reasons);
+    }
+#endif
+    return 1;
+}
diff --git a/crypto/cryptlib.c b/crypto/cryptlib.c
new file mode 100644
index 000000000..006b81bc5
--- /dev/null
+++ b/crypto/cryptlib.c
@@ -0,0 +1,348 @@
+/*
+ * Copyright 1998-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/* ====================================================================
+ * Copyright 2002 Sun Microsystems, Inc. ALL RIGHTS RESERVED.
+ * ECDH support in OpenSSL originally developed by
+ * SUN MICROSYSTEMS, INC., and contributed to the OpenSSL project.
+ */
+
+#include "internal/cryptlib_int.h"
+#include 
+
+#if     defined(__i386)   || defined(__i386__)   || defined(_M_IX86) || \
+        defined(__x86_64) || defined(__x86_64__) || \
+        defined(_M_AMD64) || defined(_M_X64)
+
+extern unsigned int OPENSSL_ia32cap_P[4];
+
+# if defined(OPENSSL_CPUID_OBJ) && !defined(OPENSSL_NO_ASM) && !defined(I386_ONLY)
+#include 
+#  define OPENSSL_CPUID_SETUP
+typedef uint64_t IA32CAP;
+void OPENSSL_cpuid_setup(void)
+{
+    static int trigger = 0;
+    IA32CAP OPENSSL_ia32_cpuid(unsigned int *);
+    IA32CAP vec;
+    char *env;
+
+    if (trigger)
+        return;
+
+    trigger = 1;
+    if ((env = getenv("OPENSSL_ia32cap"))) {
+        int off = (env[0] == '~') ? 1 : 0;
+#  if defined(_WIN32)
+        if (!sscanf(env + off, "%I64i", &vec))
+            vec = strtoul(env + off, NULL, 0);
+#  else
+        if (!sscanf(env + off, "%lli", (long long *)&vec))
+            vec = strtoul(env + off, NULL, 0);
+#  endif
+        if (off)
+            vec = OPENSSL_ia32_cpuid(OPENSSL_ia32cap_P) & ~vec;
+        else if (env[0] == ':')
+            vec = OPENSSL_ia32_cpuid(OPENSSL_ia32cap_P);
+
+        OPENSSL_ia32cap_P[2] = 0;
+        if ((env = strchr(env, ':'))) {
+            unsigned int vecx;
+            env++;
+            off = (env[0] == '~') ? 1 : 0;
+            vecx = strtoul(env + off, NULL, 0);
+            if (off)
+                OPENSSL_ia32cap_P[2] &= ~vecx;
+            else
+                OPENSSL_ia32cap_P[2] = vecx;
+        }
+    } else
+        vec = OPENSSL_ia32_cpuid(OPENSSL_ia32cap_P);
+
+    /*
+     * |(1<<10) sets a reserved bit to signal that variable
+     * was initialized already... This is to avoid interference
+     * with cpuid snippets in ELF .init segment.
+     */
+    OPENSSL_ia32cap_P[0] = (unsigned int)vec | (1 << 10);
+    OPENSSL_ia32cap_P[1] = (unsigned int)(vec >> 32);
+}
+# else
+unsigned int OPENSSL_ia32cap_P[4];
+# endif
+#endif
+int OPENSSL_NONPIC_relocated = 0;
+#if !defined(OPENSSL_CPUID_SETUP) && !defined(OPENSSL_CPUID_OBJ)
+void OPENSSL_cpuid_setup(void)
+{
+}
+#endif
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+# include 
+# include 
+# ifdef __WATCOMC__
+#  if defined(_UNICODE) || defined(__UNICODE__)
+#   define _vsntprintf _vsnwprintf
+#  else
+#   define _vsntprintf _vsnprintf
+#  endif
+# endif
+# ifdef _MSC_VER
+#  define alloca _alloca
+# endif
+
+# if defined(_WIN32_WINNT) && _WIN32_WINNT>=0x0333
+int OPENSSL_isservice(void)
+{
+    HWINSTA h;
+    DWORD len;
+    WCHAR *name;
+    static union {
+        void *p;
+        FARPROC f;
+    } _OPENSSL_isservice = {
+        NULL
+    };
+
+    if (_OPENSSL_isservice.p == NULL) {
+        HANDLE mod = GetModuleHandle(NULL);
+        if (mod != NULL)
+            _OPENSSL_isservice.f = GetProcAddress(mod, "_OPENSSL_isservice");
+        if (_OPENSSL_isservice.p == NULL)
+            _OPENSSL_isservice.p = (void *)-1;
+    }
+
+    if (_OPENSSL_isservice.p != (void *)-1)
+        return (*_OPENSSL_isservice.f) ();
+
+    h = GetProcessWindowStation();
+    if (h == NULL)
+        return -1;
+
+    if (GetUserObjectInformationW(h, UOI_NAME, NULL, 0, &len) ||
+        GetLastError() != ERROR_INSUFFICIENT_BUFFER)
+        return -1;
+
+    if (len > 512)
+        return -1;              /* paranoia */
+    len++, len &= ~1;           /* paranoia */
+    name = (WCHAR *)alloca(len + sizeof(WCHAR));
+    if (!GetUserObjectInformationW(h, UOI_NAME, name, len, &len))
+        return -1;
+
+    len++, len &= ~1;           /* paranoia */
+    name[len / sizeof(WCHAR)] = L'\0'; /* paranoia */
+#  if 1
+    /*
+     * This doesn't cover "interactive" services [working with real
+     * WinSta0's] nor programs started non-interactively by Task Scheduler
+     * [those are working with SAWinSta].
+     */
+    if (wcsstr(name, L"Service-0x"))
+        return 1;
+#  else
+    /* This covers all non-interactive programs such as services. */
+    if (!wcsstr(name, L"WinSta0"))
+        return 1;
+#  endif
+    else
+        return 0;
+}
+# else
+int OPENSSL_isservice(void)
+{
+    return 0;
+}
+# endif
+
+void OPENSSL_showfatal(const char *fmta, ...)
+{
+    va_list ap;
+    TCHAR buf[256];
+    const TCHAR *fmt;
+# ifdef STD_ERROR_HANDLE        /* what a dirty trick! */
+    HANDLE h;
+
+    if ((h = GetStdHandle(STD_ERROR_HANDLE)) != NULL &&
+        GetFileType(h) != FILE_TYPE_UNKNOWN) {
+        /* must be console application */
+        int len;
+        DWORD out;
+
+        va_start(ap, fmta);
+        len = _vsnprintf((char *)buf, sizeof(buf), fmta, ap);
+        WriteFile(h, buf, len < 0 ? sizeof(buf) : (DWORD) len, &out, NULL);
+        va_end(ap);
+        return;
+    }
+# endif
+
+    if (sizeof(TCHAR) == sizeof(char))
+        fmt = (const TCHAR *)fmta;
+    else
+        do {
+            int keepgoing;
+            size_t len_0 = strlen(fmta) + 1, i;
+            WCHAR *fmtw;
+
+            fmtw = (WCHAR *)alloca(len_0 * sizeof(WCHAR));
+            if (fmtw == NULL) {
+                fmt = (const TCHAR *)L"no stack?";
+                break;
+            }
+            if (!MultiByteToWideChar(CP_ACP, 0, fmta, len_0, fmtw, len_0))
+                for (i = 0; i < len_0; i++)
+                    fmtw[i] = (WCHAR)fmta[i];
+            for (i = 0; i < len_0; i++) {
+                if (fmtw[i] == L'%')
+                    do {
+                        keepgoing = 0;
+                        switch (fmtw[i + 1]) {
+                        case L'0':
+                        case L'1':
+                        case L'2':
+                        case L'3':
+                        case L'4':
+                        case L'5':
+                        case L'6':
+                        case L'7':
+                        case L'8':
+                        case L'9':
+                        case L'.':
+                        case L'*':
+                        case L'-':
+                            i++;
+                            keepgoing = 1;
+                            break;
+                        case L's':
+                            fmtw[i + 1] = L'S';
+                            break;
+                        case L'S':
+                            fmtw[i + 1] = L's';
+                            break;
+                        case L'c':
+                            fmtw[i + 1] = L'C';
+                            break;
+                        case L'C':
+                            fmtw[i + 1] = L'c';
+                            break;
+                        }
+                    } while (keepgoing);
+            }
+            fmt = (const TCHAR *)fmtw;
+        } while (0);
+
+    va_start(ap, fmta);
+    _vsntprintf(buf, OSSL_NELEM(buf) - 1, fmt, ap);
+    buf[OSSL_NELEM(buf) - 1] = _T('\0');
+    va_end(ap);
+
+# if defined(_WIN32_WINNT) && _WIN32_WINNT>=0x0333
+    /* this -------------v--- guards NT-specific calls */
+    if (check_winnt() && OPENSSL_isservice() > 0) {
+        HANDLE hEventLog = RegisterEventSource(NULL, _T("OpenSSL"));
+
+        if (hEventLog != NULL) {
+            const TCHAR *pmsg = buf;
+
+            if (!ReportEvent(hEventLog, EVENTLOG_ERROR_TYPE, 0, 0, NULL,
+                             1, 0, &pmsg, NULL)) {
+#if defined(DEBUG)
+                /*
+                 * We are in a situation where we tried to report a critical
+                 * error and this failed for some reason. As a last resort,
+                 * in debug builds, send output to the debugger or any other
+                 * tool like DebugView which can monitor the output.
+                 */
+                OutputDebugString(pmsg);
+#endif
+            }
+
+            (void)DeregisterEventSource(hEventLog);
+        }
+    } else
+# endif
+        MessageBox(NULL, buf, _T("OpenSSL: FATAL"), MB_OK | MB_ICONERROR);
+}
+#else
+void OPENSSL_showfatal(const char *fmta, ...)
+{
+#ifndef OPENSSL_NO_STDIO
+    va_list ap;
+
+    va_start(ap, fmta);
+    vfprintf(stderr, fmta, ap);
+    va_end(ap);
+#endif
+}
+
+int OPENSSL_isservice(void)
+{
+    return 0;
+}
+#endif
+
+void OPENSSL_die(const char *message, const char *file, int line)
+{
+    OPENSSL_showfatal("%s:%d: OpenSSL internal error: %s\n",
+                      file, line, message);
+#if !defined(_WIN32) || defined(__CYGWIN__)
+    abort();
+#else
+    /*
+     * Win32 abort() customarily shows a dialog, but we just did that...
+     */
+# if !defined(_WIN32_WCE)
+    raise(SIGABRT);
+# endif
+    _exit(3);
+#endif
+}
+
+#if !defined(OPENSSL_CPUID_OBJ)
+/* volatile unsigned char* pointers are there because
+ * 1. Accessing a variable declared volatile via a pointer
+ *    that lacks a volatile qualifier causes undefined behavior.
+ * 2. When the variable itself is not volatile the compiler is
+ *    not required to keep all those reads and can convert
+ *    this into canonical memcmp() which doesn't read the whole block.
+ * Pointers to volatile resolve the first problem fully. The second
+ * problem cannot be resolved in any Standard-compliant way but this
+ * works the problem around. Compilers typically react to
+ * pointers to volatile by preserving the reads and writes through them.
+ * The latter is not required by the Standard if the memory pointed to
+ * is not volatile.
+ * Pointers themselves are volatile in the function signature to work
+ * around a subtle bug in gcc 4.6+ which causes writes through
+ * pointers to volatile to not be emitted in some rare,
+ * never needed in real life, pieces of code.
+ */
+int CRYPTO_memcmp(const volatile void * volatile in_a,
+                  const volatile void * volatile in_b,
+                  size_t len)
+{
+    size_t i;
+    const volatile unsigned char *a = in_a;
+    const volatile unsigned char *b = in_b;
+    unsigned char x = 0;
+
+    for (i = 0; i < len; i++)
+        x |= a[i] ^ b[i];
+
+    return x;
+}
+#endif
+
+#if OPENSSL_API_COMPAT < 0x10100000L
+int CRYPTO_num_locks(void)
+{
+    return 1;
+}
+#endif
diff --git a/crypto/ct/build.info b/crypto/ct/build.info
new file mode 100644
index 000000000..3ca0e3160
--- /dev/null
+++ b/crypto/ct/build.info
@@ -0,0 +1,3 @@
+LIBS=../../libcrypto
+SOURCE[../../libcrypto]= ct_b64.c ct_err.c ct_log.c ct_oct.c ct_policy.c \
+                         ct_prn.c ct_sct.c ct_sct_ctx.c ct_vfy.c ct_x509v3.c
diff --git a/crypto/ct/ct_b64.c b/crypto/ct/ct_b64.c
new file mode 100644
index 000000000..f0bf3aff2
--- /dev/null
+++ b/crypto/ct/ct_b64.c
@@ -0,0 +1,164 @@
+/*
+ * Copyright 2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+
+#include 
+#include 
+#include 
+
+#include "ct_locl.h"
+
+/*
+ * Decodes the base64 string |in| into |out|.
+ * A new string will be malloc'd and assigned to |out|. This will be owned by
+ * the caller. Do not provide a pre-allocated string in |out|.
+ */
+static int ct_base64_decode(const char *in, unsigned char **out)
+{
+    size_t inlen = strlen(in);
+    int outlen;
+    unsigned char *outbuf = NULL;
+
+    if (inlen == 0) {
+        *out = NULL;
+        return 0;
+    }
+
+    outlen = (inlen / 4) * 3;
+    outbuf = OPENSSL_malloc(outlen);
+    if (outbuf == NULL) {
+        CTerr(CT_F_CT_BASE64_DECODE, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+
+    outlen = EVP_DecodeBlock(outbuf, (unsigned char *)in, inlen);
+    if (outlen < 0) {
+        CTerr(CT_F_CT_BASE64_DECODE, CT_R_BASE64_DECODE_ERROR);
+        goto err;
+    }
+
+    /* Subtract padding bytes from |outlen| */
+    while (in[--inlen] == '=') {
+        --outlen;
+    }
+
+    *out = outbuf;
+    return outlen;
+err:
+    OPENSSL_free(outbuf);
+    return -1;
+}
+
+SCT *SCT_new_from_base64(unsigned char version, const char *logid_base64,
+                         ct_log_entry_type_t entry_type, uint64_t timestamp,
+                         const char *extensions_base64,
+                         const char *signature_base64)
+{
+    SCT *sct = SCT_new();
+    unsigned char *dec = NULL;
+    const unsigned char* p = NULL;
+    int declen;
+
+    if (sct == NULL) {
+        CTerr(CT_F_SCT_NEW_FROM_BASE64, ERR_R_MALLOC_FAILURE);
+        return NULL;
+    }
+
+    /*
+     * RFC6962 section 4.1 says we "MUST NOT expect this to be 0", but we
+     * can only construct SCT versions that have been defined.
+     */
+    if (!SCT_set_version(sct, version)) {
+        CTerr(CT_F_SCT_NEW_FROM_BASE64, CT_R_SCT_UNSUPPORTED_VERSION);
+        goto err;
+    }
+
+    declen = ct_base64_decode(logid_base64, &dec);
+    if (declen < 0) {
+        CTerr(CT_F_SCT_NEW_FROM_BASE64, X509_R_BASE64_DECODE_ERROR);
+        goto err;
+    }
+    if (!SCT_set0_log_id(sct, dec, declen))
+        goto err;
+    dec = NULL;
+
+    declen = ct_base64_decode(extensions_base64, &dec);
+    if (declen < 0) {
+        CTerr(CT_F_SCT_NEW_FROM_BASE64, X509_R_BASE64_DECODE_ERROR);
+        goto err;
+    }
+    SCT_set0_extensions(sct, dec, declen);
+    dec = NULL;
+
+    declen = ct_base64_decode(signature_base64, &dec);
+    if (declen < 0) {
+        CTerr(CT_F_SCT_NEW_FROM_BASE64, X509_R_BASE64_DECODE_ERROR);
+        goto err;
+    }
+
+    p = dec;
+    if (o2i_SCT_signature(sct, &p, declen) <= 0)
+        goto err;
+    OPENSSL_free(dec);
+    dec = NULL;
+
+    SCT_set_timestamp(sct, timestamp);
+
+    if (!SCT_set_log_entry_type(sct, entry_type))
+        goto err;
+
+    return sct;
+
+ err:
+    OPENSSL_free(dec);
+    SCT_free(sct);
+    return NULL;
+}
+
+/*
+ * Allocate, build and returns a new |ct_log| from input |pkey_base64|
+ * It returns 1 on success,
+ * 0 on decoding failure, or invalid parameter if any
+ * -1 on internal (malloc) failure
+ */
+int CTLOG_new_from_base64(CTLOG **ct_log, const char *pkey_base64, const char *name)
+{
+    unsigned char *pkey_der = NULL;
+    int pkey_der_len = ct_base64_decode(pkey_base64, &pkey_der);
+    const unsigned char *p;
+    EVP_PKEY *pkey = NULL;
+
+    if (ct_log == NULL) {
+        CTerr(CT_F_CTLOG_NEW_FROM_BASE64, ERR_R_PASSED_INVALID_ARGUMENT);
+        return 0;
+    }
+
+    if (pkey_der_len <= 0) {
+        CTerr(CT_F_CTLOG_NEW_FROM_BASE64, CT_R_LOG_CONF_INVALID_KEY);
+        return 0;
+    }
+
+    p = pkey_der;
+    pkey = d2i_PUBKEY(NULL, &p, pkey_der_len);
+    OPENSSL_free(pkey_der);
+    if (pkey == NULL) {
+        CTerr(CT_F_CTLOG_NEW_FROM_BASE64, CT_R_LOG_CONF_INVALID_KEY);
+        return 0;
+    }
+
+    *ct_log = CTLOG_new(pkey, name);
+    if (*ct_log == NULL) {
+        EVP_PKEY_free(pkey);
+        return 0;
+    }
+
+    return 1;
+}
diff --git a/crypto/ct/ct_err.c b/crypto/ct/ct_err.c
new file mode 100644
index 000000000..fe0778b27
--- /dev/null
+++ b/crypto/ct/ct_err.c
@@ -0,0 +1,87 @@
+/*
+ * Generated by util/mkerr.pl DO NOT EDIT
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include 
+
+/* BEGIN ERROR CODES */
+#ifndef OPENSSL_NO_ERR
+
+# define ERR_FUNC(func) ERR_PACK(ERR_LIB_CT,func,0)
+# define ERR_REASON(reason) ERR_PACK(ERR_LIB_CT,0,reason)
+
+static ERR_STRING_DATA CT_str_functs[] = {
+    {ERR_FUNC(CT_F_CTLOG_NEW), "CTLOG_new"},
+    {ERR_FUNC(CT_F_CTLOG_NEW_FROM_BASE64), "CTLOG_new_from_base64"},
+    {ERR_FUNC(CT_F_CTLOG_NEW_FROM_CONF), "ctlog_new_from_conf"},
+    {ERR_FUNC(CT_F_CTLOG_STORE_LOAD_CTX_NEW), "ctlog_store_load_ctx_new"},
+    {ERR_FUNC(CT_F_CTLOG_STORE_LOAD_FILE), "CTLOG_STORE_load_file"},
+    {ERR_FUNC(CT_F_CTLOG_STORE_LOAD_LOG), "ctlog_store_load_log"},
+    {ERR_FUNC(CT_F_CTLOG_STORE_NEW), "CTLOG_STORE_new"},
+    {ERR_FUNC(CT_F_CT_BASE64_DECODE), "ct_base64_decode"},
+    {ERR_FUNC(CT_F_CT_POLICY_EVAL_CTX_NEW), "CT_POLICY_EVAL_CTX_new"},
+    {ERR_FUNC(CT_F_CT_V1_LOG_ID_FROM_PKEY), "ct_v1_log_id_from_pkey"},
+    {ERR_FUNC(CT_F_I2O_SCT), "i2o_SCT"},
+    {ERR_FUNC(CT_F_I2O_SCT_LIST), "i2o_SCT_LIST"},
+    {ERR_FUNC(CT_F_I2O_SCT_SIGNATURE), "i2o_SCT_signature"},
+    {ERR_FUNC(CT_F_O2I_SCT), "o2i_SCT"},
+    {ERR_FUNC(CT_F_O2I_SCT_LIST), "o2i_SCT_LIST"},
+    {ERR_FUNC(CT_F_O2I_SCT_SIGNATURE), "o2i_SCT_signature"},
+    {ERR_FUNC(CT_F_SCT_CTX_NEW), "SCT_CTX_new"},
+    {ERR_FUNC(CT_F_SCT_CTX_VERIFY), "SCT_CTX_verify"},
+    {ERR_FUNC(CT_F_SCT_NEW), "SCT_new"},
+    {ERR_FUNC(CT_F_SCT_NEW_FROM_BASE64), "SCT_new_from_base64"},
+    {ERR_FUNC(CT_F_SCT_SET0_LOG_ID), "SCT_set0_log_id"},
+    {ERR_FUNC(CT_F_SCT_SET1_EXTENSIONS), "SCT_set1_extensions"},
+    {ERR_FUNC(CT_F_SCT_SET1_LOG_ID), "SCT_set1_log_id"},
+    {ERR_FUNC(CT_F_SCT_SET1_SIGNATURE), "SCT_set1_signature"},
+    {ERR_FUNC(CT_F_SCT_SET_LOG_ENTRY_TYPE), "SCT_set_log_entry_type"},
+    {ERR_FUNC(CT_F_SCT_SET_SIGNATURE_NID), "SCT_set_signature_nid"},
+    {ERR_FUNC(CT_F_SCT_SET_VERSION), "SCT_set_version"},
+    {0, NULL}
+};
+
+static ERR_STRING_DATA CT_str_reasons[] = {
+    {ERR_REASON(CT_R_BASE64_DECODE_ERROR), "base64 decode error"},
+    {ERR_REASON(CT_R_INVALID_LOG_ID_LENGTH), "invalid log id length"},
+    {ERR_REASON(CT_R_LOG_CONF_INVALID), "log conf invalid"},
+    {ERR_REASON(CT_R_LOG_CONF_INVALID_KEY), "log conf invalid key"},
+    {ERR_REASON(CT_R_LOG_CONF_MISSING_DESCRIPTION),
+     "log conf missing description"},
+    {ERR_REASON(CT_R_LOG_CONF_MISSING_KEY), "log conf missing key"},
+    {ERR_REASON(CT_R_LOG_KEY_INVALID), "log key invalid"},
+    {ERR_REASON(CT_R_SCT_FUTURE_TIMESTAMP), "sct future timestamp"},
+    {ERR_REASON(CT_R_SCT_INVALID), "sct invalid"},
+    {ERR_REASON(CT_R_SCT_INVALID_SIGNATURE), "sct invalid signature"},
+    {ERR_REASON(CT_R_SCT_LIST_INVALID), "sct list invalid"},
+    {ERR_REASON(CT_R_SCT_LOG_ID_MISMATCH), "sct log id mismatch"},
+    {ERR_REASON(CT_R_SCT_NOT_SET), "sct not set"},
+    {ERR_REASON(CT_R_SCT_UNSUPPORTED_VERSION), "sct unsupported version"},
+    {ERR_REASON(CT_R_UNRECOGNIZED_SIGNATURE_NID),
+     "unrecognized signature nid"},
+    {ERR_REASON(CT_R_UNSUPPORTED_ENTRY_TYPE), "unsupported entry type"},
+    {ERR_REASON(CT_R_UNSUPPORTED_VERSION), "unsupported version"},
+    {0, NULL}
+};
+
+#endif
+
+int ERR_load_CT_strings(void)
+{
+#ifndef OPENSSL_NO_ERR
+
+    if (ERR_func_error_string(CT_str_functs[0].error) == NULL) {
+        ERR_load_strings(0, CT_str_functs);
+        ERR_load_strings(0, CT_str_reasons);
+    }
+#endif
+    return 1;
+}
diff --git a/crypto/ct/ct_locl.h b/crypto/ct/ct_locl.h
new file mode 100644
index 000000000..9f983c91b
--- /dev/null
+++ b/crypto/ct/ct_locl.h
@@ -0,0 +1,216 @@
+/*
+ * Copyright 2015-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+/*
+ * From RFC6962: opaque SerializedSCT<1..2^16-1>; struct { SerializedSCT
+ * sct_list <1..2^16-1>; } SignedCertificateTimestampList;
+ */
+# define MAX_SCT_SIZE            65535
+# define MAX_SCT_LIST_SIZE       MAX_SCT_SIZE
+
+/*
+ * Macros to read and write integers in network-byte order.
+ */
+
+#define n2s(c,s)        ((s=(((unsigned int)((c)[0]))<< 8)| \
+                            (((unsigned int)((c)[1]))    )),c+=2)
+
+#define s2n(s,c)        ((c[0]=(unsigned char)(((s)>> 8)&0xff), \
+                          c[1]=(unsigned char)(((s)    )&0xff)),c+=2)
+
+#define l2n3(l,c)       ((c[0]=(unsigned char)(((l)>>16)&0xff), \
+                          c[1]=(unsigned char)(((l)>> 8)&0xff), \
+                          c[2]=(unsigned char)(((l)    )&0xff)),c+=3)
+
+#define n2l8(c,l)       (l =((uint64_t)(*((c)++)))<<56, \
+                         l|=((uint64_t)(*((c)++)))<<48, \
+                         l|=((uint64_t)(*((c)++)))<<40, \
+                         l|=((uint64_t)(*((c)++)))<<32, \
+                         l|=((uint64_t)(*((c)++)))<<24, \
+                         l|=((uint64_t)(*((c)++)))<<16, \
+                         l|=((uint64_t)(*((c)++)))<< 8, \
+                         l|=((uint64_t)(*((c)++))))
+
+#define l2n8(l,c)       (*((c)++)=(unsigned char)(((l)>>56)&0xff), \
+                         *((c)++)=(unsigned char)(((l)>>48)&0xff), \
+                         *((c)++)=(unsigned char)(((l)>>40)&0xff), \
+                         *((c)++)=(unsigned char)(((l)>>32)&0xff), \
+                         *((c)++)=(unsigned char)(((l)>>24)&0xff), \
+                         *((c)++)=(unsigned char)(((l)>>16)&0xff), \
+                         *((c)++)=(unsigned char)(((l)>> 8)&0xff), \
+                         *((c)++)=(unsigned char)(((l)    )&0xff))
+
+/* Signed Certificate Timestamp */
+struct sct_st {
+    sct_version_t version;
+    /* If version is not SCT_VERSION_V1, this contains the encoded SCT */
+    unsigned char *sct;
+    size_t sct_len;
+    /* If version is SCT_VERSION_V1, fields below contain components of the SCT */
+    unsigned char *log_id;
+    size_t log_id_len;
+    /*
+    * Note, we cannot distinguish between an unset timestamp, and one
+    * that is set to 0.  However since CT didn't exist in 1970, no real
+    * SCT should ever be set as such.
+    */
+    uint64_t timestamp;
+    unsigned char *ext;
+    size_t ext_len;
+    unsigned char hash_alg;
+    unsigned char sig_alg;
+    unsigned char *sig;
+    size_t sig_len;
+    /* Log entry type */
+    ct_log_entry_type_t entry_type;
+    /* Where this SCT was found, e.g. certificate, OCSP response, etc. */
+    sct_source_t source;
+    /* The result of the last attempt to validate this SCT. */
+    sct_validation_status_t validation_status;
+};
+
+/* Miscellaneous data that is useful when verifying an SCT  */
+struct sct_ctx_st {
+    /* Public key */
+    EVP_PKEY *pkey;
+    /* Hash of public key */
+    unsigned char *pkeyhash;
+    size_t pkeyhashlen;
+    /* For pre-certificate: issuer public key hash */
+    unsigned char *ihash;
+    size_t ihashlen;
+    /* certificate encoding */
+    unsigned char *certder;
+    size_t certderlen;
+    /* pre-certificate encoding */
+    unsigned char *preder;
+    size_t prederlen;
+    /* milliseconds since epoch (to check that the SCT isn't from the future) */
+    uint64_t epoch_time_in_ms;
+};
+
+/* Context when evaluating whether a Certificate Transparency policy is met */
+struct ct_policy_eval_ctx_st {
+    X509 *cert;
+    X509 *issuer;
+    CTLOG_STORE *log_store;
+    /* milliseconds since epoch (to check that SCTs aren't from the future) */
+    uint64_t epoch_time_in_ms;
+};
+
+/*
+ * Creates a new context for verifying an SCT.
+ */
+SCT_CTX *SCT_CTX_new(void);
+/*
+ * Deletes an SCT verification context.
+ */
+void SCT_CTX_free(SCT_CTX *sctx);
+
+/*
+ * Sets the certificate that the SCT was created for.
+ * If *cert does not have a poison extension, presigner must be NULL.
+ * If *cert does not have a poison extension, it may have a single SCT
+ * (NID_ct_precert_scts) extension.
+ * If either *cert or *presigner have an AKID (NID_authority_key_identifier)
+ * extension, both must have one.
+ * Returns 1 on success, 0 on failure.
+ */
+__owur int SCT_CTX_set1_cert(SCT_CTX *sctx, X509 *cert, X509 *presigner);
+
+/*
+ * Sets the issuer of the certificate that the SCT was created for.
+ * This is just a convenience method to save extracting the public key and
+ * calling SCT_CTX_set1_issuer_pubkey().
+ * Issuer must not be NULL.
+ * Returns 1 on success, 0 on failure.
+ */
+__owur int SCT_CTX_set1_issuer(SCT_CTX *sctx, const X509 *issuer);
+
+/*
+ * Sets the public key of the issuer of the certificate that the SCT was created
+ * for.
+ * The public key must not be NULL.
+ * Returns 1 on success, 0 on failure.
+ */
+__owur int SCT_CTX_set1_issuer_pubkey(SCT_CTX *sctx, X509_PUBKEY *pubkey);
+
+/*
+ * Sets the public key of the CT log that the SCT is from.
+ * Returns 1 on success, 0 on failure.
+ */
+__owur int SCT_CTX_set1_pubkey(SCT_CTX *sctx, X509_PUBKEY *pubkey);
+
+/*
+ * Sets the time to evaluate the SCT against, in milliseconds since the Unix
+ * epoch. If the SCT's timestamp is after this time, it will be interpreted as
+ * having been issued in the future. RFC6962 states that "TLS clients MUST
+ * reject SCTs whose timestamp is in the future", so an SCT will not validate
+ * in this case.
+ */
+void SCT_CTX_set_time(SCT_CTX *sctx, uint64_t time_in_ms);
+
+/*
+ * Verifies an SCT with the given context.
+ * Returns 1 if the SCT verifies successfully; any other value indicates
+ * failure. See EVP_DigestVerifyFinal() for the meaning of those values.
+ */
+__owur int SCT_CTX_verify(const SCT_CTX *sctx, const SCT *sct);
+
+/*
+ * Does this SCT have the minimum fields populated to be usable?
+ * Returns 1 if so, 0 otherwise.
+ */
+__owur int SCT_is_complete(const SCT *sct);
+
+/*
+ * Does this SCT have the signature-related fields populated?
+ * Returns 1 if so, 0 otherwise.
+ * This checks that the signature and hash algorithms are set to supported
+ * values and that the signature field is set.
+ */
+__owur int SCT_signature_is_complete(const SCT *sct);
+
+/*
+ * TODO(RJPercival): Create an SCT_signature struct and make i2o_SCT_signature
+ * and o2i_SCT_signature conform to the i2d/d2i conventions.
+ */
+
+/*
+* Serialize (to TLS format) an |sct| signature and write it to |out|.
+* If |out| is null, no signature will be output but the length will be returned.
+* If |out| points to a null pointer, a string will be allocated to hold the
+* TLS-format signature. It is the responsibility of the caller to free it.
+* If |out| points to an allocated string, the signature will be written to it.
+* The length of the signature in TLS format will be returned.
+*/
+__owur int i2o_SCT_signature(const SCT *sct, unsigned char **out);
+
+/*
+* Parses an SCT signature in TLS format and populates the |sct| with it.
+* |in| should be a pointer to a string containing the TLS-format signature.
+* |in| will be advanced to the end of the signature if parsing succeeds.
+* |len| should be the length of the signature in |in|.
+* Returns the number of bytes parsed, or a negative integer if an error occurs.
+* If an error occurs, the SCT's signature NID may be updated whilst the
+* signature field itself remains unset.
+*/
+__owur int o2i_SCT_signature(SCT *sct, const unsigned char **in, size_t len);
+
+/*
+ * Handlers for Certificate Transparency X509v3/OCSP extensions
+ */
+extern const X509V3_EXT_METHOD v3_ct_scts[3];
diff --git a/crypto/ct/ct_log.c b/crypto/ct/ct_log.c
new file mode 100644
index 000000000..6db4c3eba
--- /dev/null
+++ b/crypto/ct/ct_log.c
@@ -0,0 +1,304 @@
+/*
+ * Copyright 2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#include "internal/cryptlib.h"
+
+/*
+ * Information about a CT log server.
+ */
+struct ctlog_st {
+    char *name;
+    uint8_t log_id[CT_V1_HASHLEN];
+    EVP_PKEY *public_key;
+};
+
+/*
+ * A store for multiple CTLOG instances.
+ * It takes ownership of any CTLOG instances added to it.
+ */
+struct ctlog_store_st {
+    STACK_OF(CTLOG) *logs;
+};
+
+/* The context when loading a CT log list from a CONF file. */
+typedef struct ctlog_store_load_ctx_st {
+    CTLOG_STORE *log_store;
+    CONF *conf;
+    size_t invalid_log_entries;
+} CTLOG_STORE_LOAD_CTX;
+
+/*
+ * Creates an empty context for loading a CT log store.
+ * It should be populated before use.
+ */
+static CTLOG_STORE_LOAD_CTX *ctlog_store_load_ctx_new();
+
+/*
+ * Deletes a CT log store load context.
+ * Does not delete any of the fields.
+ */
+static void ctlog_store_load_ctx_free(CTLOG_STORE_LOAD_CTX* ctx);
+
+static CTLOG_STORE_LOAD_CTX *ctlog_store_load_ctx_new()
+{
+    CTLOG_STORE_LOAD_CTX *ctx = OPENSSL_zalloc(sizeof(*ctx));
+
+    if (ctx == NULL)
+        CTerr(CT_F_CTLOG_STORE_LOAD_CTX_NEW, ERR_R_MALLOC_FAILURE);
+
+    return ctx;
+}
+
+static void ctlog_store_load_ctx_free(CTLOG_STORE_LOAD_CTX* ctx)
+{
+    OPENSSL_free(ctx);
+}
+
+/* Converts a log's public key into a SHA256 log ID */
+static int ct_v1_log_id_from_pkey(EVP_PKEY *pkey,
+                                  unsigned char log_id[CT_V1_HASHLEN])
+{
+    int ret = 0;
+    unsigned char *pkey_der = NULL;
+    int pkey_der_len = i2d_PUBKEY(pkey, &pkey_der);
+
+    if (pkey_der_len <= 0) {
+        CTerr(CT_F_CT_V1_LOG_ID_FROM_PKEY, CT_R_LOG_KEY_INVALID);
+        goto err;
+    }
+
+    SHA256(pkey_der, pkey_der_len, log_id);
+    ret = 1;
+err:
+    OPENSSL_free(pkey_der);
+    return ret;
+}
+
+CTLOG_STORE *CTLOG_STORE_new(void)
+{
+    CTLOG_STORE *ret = OPENSSL_zalloc(sizeof(*ret));
+
+    if (ret == NULL) {
+        CTerr(CT_F_CTLOG_STORE_NEW, ERR_R_MALLOC_FAILURE);
+        return NULL;
+    }
+
+    ret->logs = sk_CTLOG_new_null();
+    if (ret->logs == NULL)
+        goto err;
+
+    return ret;
+err:
+    OPENSSL_free(ret);
+    return NULL;
+}
+
+void CTLOG_STORE_free(CTLOG_STORE *store)
+{
+    if (store != NULL) {
+        sk_CTLOG_pop_free(store->logs, CTLOG_free);
+        OPENSSL_free(store);
+    }
+}
+
+static int ctlog_new_from_conf(CTLOG **ct_log, const CONF *conf, const char *section)
+{
+    const char *description = NCONF_get_string(conf, section, "description");
+    char *pkey_base64;
+
+    if (description == NULL) {
+        CTerr(CT_F_CTLOG_NEW_FROM_CONF, CT_R_LOG_CONF_MISSING_DESCRIPTION);
+        return 0;
+    }
+
+    pkey_base64 = NCONF_get_string(conf, section, "key");
+    if (pkey_base64 == NULL) {
+        CTerr(CT_F_CTLOG_NEW_FROM_CONF, CT_R_LOG_CONF_MISSING_KEY);
+        return 0;
+    }
+
+    return CTLOG_new_from_base64(ct_log, pkey_base64, description);
+}
+
+int CTLOG_STORE_load_default_file(CTLOG_STORE *store)
+{
+    const char *fpath = getenv(CTLOG_FILE_EVP);
+
+    if (fpath == NULL)
+      fpath = CTLOG_FILE;
+
+    return CTLOG_STORE_load_file(store, fpath);
+}
+
+/*
+ * Called by CONF_parse_list, which stops if this returns <= 0,
+ * Otherwise, one bad log entry would stop loading of any of
+ * the following log entries.
+ * It may stop parsing and returns -1 on any internal (malloc) error.
+ */
+static int ctlog_store_load_log(const char *log_name, int log_name_len,
+                                void *arg)
+{
+    CTLOG_STORE_LOAD_CTX *load_ctx = arg;
+    CTLOG *ct_log = NULL;
+    /* log_name may not be null-terminated, so fix that before using it */
+    char *tmp;
+    int ret = 0;
+
+    /* log_name will be NULL for empty list entries */
+    if (log_name == NULL)
+        return 1;
+
+    tmp = OPENSSL_strndup(log_name, log_name_len);
+    if (tmp == NULL)
+        goto mem_err;
+
+    ret = ctlog_new_from_conf(&ct_log, load_ctx->conf, tmp);
+    OPENSSL_free(tmp);
+
+    if (ret < 0) {
+        /* Propagate any internal error */
+        return ret;
+    }
+    if (ret == 0) {
+        /* If we can't load this log, record that fact and skip it */
+        ++load_ctx->invalid_log_entries;
+        return 1;
+    }
+
+    if (!sk_CTLOG_push(load_ctx->log_store->logs, ct_log)) {
+        goto mem_err;
+    }
+    return 1;
+
+mem_err:
+    CTLOG_free(ct_log);
+    CTerr(CT_F_CTLOG_STORE_LOAD_LOG, ERR_R_MALLOC_FAILURE);
+    return -1;
+}
+
+int CTLOG_STORE_load_file(CTLOG_STORE *store, const char *file)
+{
+    int ret = 0;
+    char *enabled_logs;
+    CTLOG_STORE_LOAD_CTX* load_ctx = ctlog_store_load_ctx_new();
+
+    load_ctx->log_store = store;
+    load_ctx->conf = NCONF_new(NULL);
+    if (load_ctx->conf == NULL)
+        goto end;
+
+    if (NCONF_load(load_ctx->conf, file, NULL) <= 0) {
+        CTerr(CT_F_CTLOG_STORE_LOAD_FILE, CT_R_LOG_CONF_INVALID);
+        goto end;
+    }
+
+    enabled_logs = NCONF_get_string(load_ctx->conf, NULL, "enabled_logs");
+    if (enabled_logs == NULL) {
+        CTerr(CT_F_CTLOG_STORE_LOAD_FILE, CT_R_LOG_CONF_INVALID);
+        goto end;
+    }
+
+    if (!CONF_parse_list(enabled_logs, ',', 1, ctlog_store_load_log, load_ctx) ||
+        load_ctx->invalid_log_entries > 0) {
+        CTerr(CT_F_CTLOG_STORE_LOAD_FILE, CT_R_LOG_CONF_INVALID);
+        goto end;
+    }
+
+    ret = 1;
+end:
+    NCONF_free(load_ctx->conf);
+    ctlog_store_load_ctx_free(load_ctx);
+    return ret;
+}
+
+/*
+ * Initialize a new CTLOG object.
+ * Takes ownership of the public key.
+ * Copies the name.
+ */
+CTLOG *CTLOG_new(EVP_PKEY *public_key, const char *name)
+{
+    CTLOG *ret = OPENSSL_zalloc(sizeof(*ret));
+
+    if (ret == NULL) {
+        CTerr(CT_F_CTLOG_NEW, ERR_R_MALLOC_FAILURE);
+        return NULL;
+    }
+
+    ret->name = OPENSSL_strdup(name);
+    if (ret->name == NULL) {
+        CTerr(CT_F_CTLOG_NEW, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+
+    if (ct_v1_log_id_from_pkey(public_key, ret->log_id) != 1)
+        goto err;
+
+    ret->public_key = public_key;
+    return ret;
+err:
+    CTLOG_free(ret);
+    return NULL;
+}
+
+/* Frees CT log and associated structures */
+void CTLOG_free(CTLOG *log)
+{
+    if (log != NULL) {
+        OPENSSL_free(log->name);
+        EVP_PKEY_free(log->public_key);
+        OPENSSL_free(log);
+    }
+}
+
+const char *CTLOG_get0_name(const CTLOG *log)
+{
+    return log->name;
+}
+
+void CTLOG_get0_log_id(const CTLOG *log, const uint8_t **log_id,
+                       size_t *log_id_len)
+{
+    *log_id = log->log_id;
+    *log_id_len = CT_V1_HASHLEN;
+}
+
+EVP_PKEY *CTLOG_get0_public_key(const CTLOG *log)
+{
+    return log->public_key;
+}
+
+/*
+ * Given a log ID, finds the matching log.
+ * Returns NULL if no match found.
+ */
+const CTLOG *CTLOG_STORE_get0_log_by_id(const CTLOG_STORE *store,
+                                        const uint8_t *log_id,
+                                        size_t log_id_len)
+{
+    int i;
+
+    for (i = 0; i < sk_CTLOG_num(store->logs); ++i) {
+        const CTLOG *log = sk_CTLOG_value(store->logs, i);
+        if (memcmp(log->log_id, log_id, log_id_len) == 0)
+            return log;
+    }
+
+    return NULL;
+}
diff --git a/crypto/ct/ct_oct.c b/crypto/ct/ct_oct.c
new file mode 100644
index 000000000..d3edd39f5
--- /dev/null
+++ b/crypto/ct/ct_oct.c
@@ -0,0 +1,407 @@
+/*
+ * Copyright 2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#ifdef OPENSSL_NO_CT
+# error "CT is disabled"
+#endif
+
+#include 
+#include 
+
+#include 
+#include 
+#include 
+#include 
+
+#include "ct_locl.h"
+
+int o2i_SCT_signature(SCT *sct, const unsigned char **in, size_t len)
+{
+    size_t siglen;
+    size_t len_remaining = len;
+    const unsigned char *p;
+
+    if (sct->version != SCT_VERSION_V1) {
+        CTerr(CT_F_O2I_SCT_SIGNATURE, CT_R_UNSUPPORTED_VERSION);
+        return -1;
+    }
+    /*
+     * digitally-signed struct header: (1 byte) Hash algorithm (1 byte)
+     * Signature algorithm (2 bytes + ?) Signature
+     *
+     * This explicitly rejects empty signatures: they're invalid for
+     * all supported algorithms.
+     */
+    if (len <= 4) {
+        CTerr(CT_F_O2I_SCT_SIGNATURE, CT_R_SCT_INVALID_SIGNATURE);
+        return -1;
+    }
+
+    p = *in;
+    /* Get hash and signature algorithm */
+    sct->hash_alg = *p++;
+    sct->sig_alg = *p++;
+    if (SCT_get_signature_nid(sct) == NID_undef) {
+        CTerr(CT_F_O2I_SCT_SIGNATURE, CT_R_SCT_INVALID_SIGNATURE);
+        return -1;
+    }
+    /* Retrieve signature and check it is consistent with the buffer length */
+    n2s(p, siglen);
+    len_remaining -= (p - *in);
+    if (siglen > len_remaining) {
+        CTerr(CT_F_O2I_SCT_SIGNATURE, CT_R_SCT_INVALID_SIGNATURE);
+        return -1;
+    }
+
+    if (SCT_set1_signature(sct, p, siglen) != 1)
+        return -1;
+    len_remaining -= siglen;
+    *in = p + siglen;
+
+    return len - len_remaining;
+}
+
+SCT *o2i_SCT(SCT **psct, const unsigned char **in, size_t len)
+{
+    SCT *sct = NULL;
+    const unsigned char *p;
+
+    if (len == 0 || len > MAX_SCT_SIZE) {
+        CTerr(CT_F_O2I_SCT, CT_R_SCT_INVALID);
+        goto err;
+    }
+
+    if ((sct = SCT_new()) == NULL)
+        goto err;
+
+    p = *in;
+
+    sct->version = *p;
+    if (sct->version == SCT_VERSION_V1) {
+        int sig_len;
+        size_t len2;
+        /*-
+         * Fixed-length header:
+         *   struct {
+         *     Version sct_version;     (1 byte)
+         *     log_id id;               (32 bytes)
+         *     uint64 timestamp;        (8 bytes)
+         *     CtExtensions extensions; (2 bytes + ?)
+         *   }
+         */
+        if (len < 43) {
+            CTerr(CT_F_O2I_SCT, CT_R_SCT_INVALID);
+            goto err;
+        }
+        len -= 43;
+        p++;
+        sct->log_id = BUF_memdup(p, CT_V1_HASHLEN);
+        if (sct->log_id == NULL)
+            goto err;
+        sct->log_id_len = CT_V1_HASHLEN;
+        p += CT_V1_HASHLEN;
+
+        n2l8(p, sct->timestamp);
+
+        n2s(p, len2);
+        if (len < len2) {
+            CTerr(CT_F_O2I_SCT, CT_R_SCT_INVALID);
+            goto err;
+        }
+        if (len2 > 0) {
+            sct->ext = BUF_memdup(p, len2);
+            if (sct->ext == NULL)
+                goto err;
+        }
+        sct->ext_len = len2;
+        p += len2;
+        len -= len2;
+
+        sig_len = o2i_SCT_signature(sct, &p, len);
+        if (sig_len <= 0) {
+            CTerr(CT_F_O2I_SCT, CT_R_SCT_INVALID);
+            goto err;
+        }
+        len -= sig_len;
+        *in = p + len;
+    } else {
+        /* If not V1 just cache encoding */
+        sct->sct = BUF_memdup(p, len);
+        if (sct->sct == NULL)
+            goto err;
+        sct->sct_len = len;
+        *in = p + len;
+    }
+
+    if (psct != NULL) {
+        SCT_free(*psct);
+        *psct = sct;
+    }
+
+    return sct;
+err:
+    SCT_free(sct);
+    return NULL;
+}
+
+int i2o_SCT_signature(const SCT *sct, unsigned char **out)
+{
+    size_t len;
+    unsigned char *p = NULL;
+
+    if (!SCT_signature_is_complete(sct)) {
+        CTerr(CT_F_I2O_SCT_SIGNATURE, CT_R_SCT_INVALID_SIGNATURE);
+        goto err;
+    }
+
+    if (sct->version != SCT_VERSION_V1) {
+        CTerr(CT_F_I2O_SCT_SIGNATURE, CT_R_UNSUPPORTED_VERSION);
+        goto err;
+    }
+
+    /*
+    * (1 byte) Hash algorithm
+    * (1 byte) Signature algorithm
+    * (2 bytes + ?) Signature
+    */
+    len = 4 + sct->sig_len;
+
+    if (out != NULL) {
+        if (*out != NULL) {
+            p = *out;
+            *out += len;
+        } else {
+            p = OPENSSL_malloc(len);
+            if (p == NULL) {
+                CTerr(CT_F_I2O_SCT_SIGNATURE, ERR_R_MALLOC_FAILURE);
+                goto err;
+            }
+            *out = p;
+        }
+
+        *p++ = sct->hash_alg;
+        *p++ = sct->sig_alg;
+        s2n(sct->sig_len, p);
+        memcpy(p, sct->sig, sct->sig_len);
+    }
+
+    return len;
+err:
+    OPENSSL_free(p);
+    return -1;
+}
+
+int i2o_SCT(const SCT *sct, unsigned char **out)
+{
+    size_t len;
+    unsigned char *p = NULL;
+
+    if (!SCT_is_complete(sct)) {
+        CTerr(CT_F_I2O_SCT, CT_R_SCT_NOT_SET);
+        goto err;
+    }
+    /*
+     * Fixed-length header: struct { (1 byte) Version sct_version; (32 bytes)
+     * log_id id; (8 bytes) uint64 timestamp; (2 bytes + ?) CtExtensions
+     * extensions; (1 byte) Hash algorithm (1 byte) Signature algorithm (2
+     * bytes + ?) Signature
+     */
+    if (sct->version == SCT_VERSION_V1)
+        len = 43 + sct->ext_len + 4 + sct->sig_len;
+    else
+        len = sct->sct_len;
+
+    if (out == NULL)
+        return len;
+
+    if (*out != NULL) {
+        p = *out;
+        *out += len;
+    } else {
+        p = OPENSSL_malloc(len);
+        if (p == NULL) {
+            CTerr(CT_F_I2O_SCT, ERR_R_MALLOC_FAILURE);
+            goto err;
+        }
+        *out = p;
+    }
+
+    if (sct->version == SCT_VERSION_V1) {
+        *p++ = sct->version;
+        memcpy(p, sct->log_id, CT_V1_HASHLEN);
+        p += CT_V1_HASHLEN;
+        l2n8(sct->timestamp, p);
+        s2n(sct->ext_len, p);
+        if (sct->ext_len > 0) {
+            memcpy(p, sct->ext, sct->ext_len);
+            p += sct->ext_len;
+        }
+        if (i2o_SCT_signature(sct, &p) <= 0)
+            goto err;
+    } else {
+        memcpy(p, sct->sct, len);
+    }
+
+    return len;
+err:
+    OPENSSL_free(p);
+    return -1;
+}
+
+STACK_OF(SCT) *o2i_SCT_LIST(STACK_OF(SCT) **a, const unsigned char **pp,
+                            size_t len)
+{
+    STACK_OF(SCT) *sk = NULL;
+    size_t list_len, sct_len;
+
+    if (len < 2 || len > MAX_SCT_LIST_SIZE) {
+        CTerr(CT_F_O2I_SCT_LIST, CT_R_SCT_LIST_INVALID);
+        return NULL;
+    }
+
+    n2s(*pp, list_len);
+    if (list_len != len - 2) {
+        CTerr(CT_F_O2I_SCT_LIST, CT_R_SCT_LIST_INVALID);
+        return NULL;
+    }
+
+    if (a == NULL || *a == NULL) {
+        sk = sk_SCT_new_null();
+        if (sk == NULL)
+            return NULL;
+    } else {
+        SCT *sct;
+
+        /* Use the given stack, but empty it first. */
+        sk = *a;
+        while ((sct = sk_SCT_pop(sk)) != NULL)
+            SCT_free(sct);
+    }
+
+    while (list_len > 0) {
+        SCT *sct;
+
+        if (list_len < 2) {
+            CTerr(CT_F_O2I_SCT_LIST, CT_R_SCT_LIST_INVALID);
+            goto err;
+        }
+        n2s(*pp, sct_len);
+        list_len -= 2;
+
+        if (sct_len == 0 || sct_len > list_len) {
+            CTerr(CT_F_O2I_SCT_LIST, CT_R_SCT_LIST_INVALID);
+            goto err;
+        }
+        list_len -= sct_len;
+
+        if ((sct = o2i_SCT(NULL, pp, sct_len)) == NULL)
+            goto err;
+        if (!sk_SCT_push(sk, sct)) {
+            SCT_free(sct);
+            goto err;
+        }
+    }
+
+    if (a != NULL && *a == NULL)
+        *a = sk;
+    return sk;
+
+ err:
+    if (a == NULL || *a == NULL)
+        SCT_LIST_free(sk);
+    return NULL;
+}
+
+int i2o_SCT_LIST(const STACK_OF(SCT) *a, unsigned char **pp)
+{
+    int len, sct_len, i, is_pp_new = 0;
+    size_t len2;
+    unsigned char *p = NULL, *p2;
+
+    if (pp != NULL) {
+        if (*pp == NULL) {
+            if ((len = i2o_SCT_LIST(a, NULL)) == -1) {
+                CTerr(CT_F_I2O_SCT_LIST, CT_R_SCT_LIST_INVALID);
+                return -1;
+            }
+            if ((*pp = OPENSSL_malloc(len)) == NULL) {
+                CTerr(CT_F_I2O_SCT_LIST, ERR_R_MALLOC_FAILURE);
+                return -1;
+            }
+            is_pp_new = 1;
+        }
+        p = *pp + 2;
+    }
+
+    len2 = 2;
+    for (i = 0; i < sk_SCT_num(a); i++) {
+        if (pp != NULL) {
+            p2 = p;
+            p += 2;
+            if ((sct_len = i2o_SCT(sk_SCT_value(a, i), &p)) == -1)
+                goto err;
+            s2n(sct_len, p2);
+        } else {
+          if ((sct_len = i2o_SCT(sk_SCT_value(a, i), NULL)) == -1)
+              goto err;
+        }
+        len2 += 2 + sct_len;
+    }
+
+    if (len2 > MAX_SCT_LIST_SIZE)
+        goto err;
+
+    if (pp != NULL) {
+        p = *pp;
+        s2n(len2 - 2, p);
+        if (!is_pp_new)
+            *pp += len2;
+    }
+    return len2;
+
+ err:
+    if (is_pp_new) {
+        OPENSSL_free(*pp);
+        *pp = NULL;
+    }
+    return -1;
+}
+
+STACK_OF(SCT) *d2i_SCT_LIST(STACK_OF(SCT) **a, const unsigned char **pp,
+                            long len)
+{
+    ASN1_OCTET_STRING *oct = NULL;
+    STACK_OF(SCT) *sk = NULL;
+    const unsigned char *p;
+
+    p = *pp;
+    if (d2i_ASN1_OCTET_STRING(&oct, &p, len) == NULL)
+        return NULL;
+
+    p = oct->data;
+    if ((sk = o2i_SCT_LIST(a, &p, oct->length)) != NULL)
+        *pp += len;
+
+    ASN1_OCTET_STRING_free(oct);
+    return sk;
+}
+
+int i2d_SCT_LIST(const STACK_OF(SCT) *a, unsigned char **out)
+{
+    ASN1_OCTET_STRING oct;
+    int len;
+
+    oct.data = NULL;
+    if ((oct.length = i2o_SCT_LIST(a, &oct.data)) == -1)
+        return -1;
+
+    len = i2d_ASN1_OCTET_STRING(&oct, out);
+    OPENSSL_free(oct.data);
+    return len;
+}
diff --git a/crypto/ct/ct_policy.c b/crypto/ct/ct_policy.c
new file mode 100644
index 000000000..0d7b34638
--- /dev/null
+++ b/crypto/ct/ct_policy.c
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#ifdef OPENSSL_NO_CT
+# error "CT is disabled"
+#endif
+
+#include 
+#include 
+#include 
+
+#include "ct_locl.h"
+
+/*
+ * Number of seconds in the future that an SCT timestamp can be, by default,
+ * without being considered invalid. This is added to time() when setting a
+ * default value for CT_POLICY_EVAL_CTX.epoch_time_in_ms.
+ * It can be overridden by calling CT_POLICY_EVAL_CTX_set_time().
+ */
+static const time_t SCT_CLOCK_DRIFT_TOLERANCE = 300;
+
+CT_POLICY_EVAL_CTX *CT_POLICY_EVAL_CTX_new(void)
+{
+    CT_POLICY_EVAL_CTX *ctx = OPENSSL_zalloc(sizeof(CT_POLICY_EVAL_CTX));
+
+    if (ctx == NULL) {
+        CTerr(CT_F_CT_POLICY_EVAL_CTX_NEW, ERR_R_MALLOC_FAILURE);
+        return NULL;
+    }
+
+    /* time(NULL) shouldn't ever fail, so don't bother checking for -1. */
+    ctx->epoch_time_in_ms = (uint64_t)(time(NULL) + SCT_CLOCK_DRIFT_TOLERANCE) *
+            1000;
+
+    return ctx;
+}
+
+void CT_POLICY_EVAL_CTX_free(CT_POLICY_EVAL_CTX *ctx)
+{
+    if (ctx == NULL)
+        return;
+    X509_free(ctx->cert);
+    X509_free(ctx->issuer);
+    OPENSSL_free(ctx);
+}
+
+int CT_POLICY_EVAL_CTX_set1_cert(CT_POLICY_EVAL_CTX *ctx, X509 *cert)
+{
+    if (!X509_up_ref(cert))
+        return 0;
+    ctx->cert = cert;
+    return 1;
+}
+
+int CT_POLICY_EVAL_CTX_set1_issuer(CT_POLICY_EVAL_CTX *ctx, X509 *issuer)
+{
+    if (!X509_up_ref(issuer))
+        return 0;
+    ctx->issuer = issuer;
+    return 1;
+}
+
+void CT_POLICY_EVAL_CTX_set_shared_CTLOG_STORE(CT_POLICY_EVAL_CTX *ctx,
+                                               CTLOG_STORE *log_store)
+{
+    ctx->log_store = log_store;
+}
+
+void CT_POLICY_EVAL_CTX_set_time(CT_POLICY_EVAL_CTX *ctx, uint64_t time_in_ms)
+{
+    ctx->epoch_time_in_ms = time_in_ms;
+}
+
+X509* CT_POLICY_EVAL_CTX_get0_cert(const CT_POLICY_EVAL_CTX *ctx)
+{
+    return ctx->cert;
+}
+
+X509* CT_POLICY_EVAL_CTX_get0_issuer(const CT_POLICY_EVAL_CTX *ctx)
+{
+    return ctx->issuer;
+}
+
+const CTLOG_STORE *CT_POLICY_EVAL_CTX_get0_log_store(const CT_POLICY_EVAL_CTX *ctx)
+{
+    return ctx->log_store;
+}
+
+uint64_t CT_POLICY_EVAL_CTX_get_time(const CT_POLICY_EVAL_CTX *ctx)
+{
+    return ctx->epoch_time_in_ms;
+}
diff --git a/crypto/ct/ct_prn.c b/crypto/ct/ct_prn.c
new file mode 100644
index 000000000..376e04523
--- /dev/null
+++ b/crypto/ct/ct_prn.c
@@ -0,0 +1,127 @@
+/*
+ * Copyright 2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#ifdef OPENSSL_NO_CT
+# error "CT is disabled"
+#endif
+
+#include 
+#include 
+
+#include "ct_locl.h"
+
+static void SCT_signature_algorithms_print(const SCT *sct, BIO *out)
+{
+    int nid = SCT_get_signature_nid(sct);
+
+    if (nid == NID_undef)
+        BIO_printf(out, "%02X%02X", sct->hash_alg, sct->sig_alg);
+    else
+        BIO_printf(out, "%s", OBJ_nid2ln(nid));
+}
+
+static void timestamp_print(uint64_t timestamp, BIO *out)
+{
+    ASN1_GENERALIZEDTIME *gen = ASN1_GENERALIZEDTIME_new();
+    char genstr[20];
+
+    if (gen == NULL)
+        return;
+    ASN1_GENERALIZEDTIME_adj(gen, (time_t)0,
+                             (int)(timestamp / 86400000),
+                             (timestamp % 86400000) / 1000);
+    /*
+     * Note GeneralizedTime from ASN1_GENERALIZETIME_adj is always 15
+     * characters long with a final Z. Update it with fractional seconds.
+     */
+    BIO_snprintf(genstr, sizeof(genstr), "%.14s.%03dZ",
+                 ASN1_STRING_get0_data(gen), (unsigned int)(timestamp % 1000));
+    if (ASN1_GENERALIZEDTIME_set_string(gen, genstr))
+        ASN1_GENERALIZEDTIME_print(out, gen);
+    ASN1_GENERALIZEDTIME_free(gen);
+}
+
+const char *SCT_validation_status_string(const SCT *sct)
+{
+
+    switch (SCT_get_validation_status(sct)) {
+    case SCT_VALIDATION_STATUS_NOT_SET:
+        return "not set";
+    case SCT_VALIDATION_STATUS_UNKNOWN_VERSION:
+        return "unknown version";
+    case SCT_VALIDATION_STATUS_UNKNOWN_LOG:
+        return "unknown log";
+    case SCT_VALIDATION_STATUS_UNVERIFIED:
+        return "unverified";
+    case SCT_VALIDATION_STATUS_INVALID:
+        return "invalid";
+    case SCT_VALIDATION_STATUS_VALID:
+        return "valid";
+    }
+    return "unknown status";
+}
+
+void SCT_print(const SCT *sct, BIO *out, int indent,
+               const CTLOG_STORE *log_store)
+{
+    const CTLOG *log = NULL;
+
+    if (log_store != NULL) {
+        log = CTLOG_STORE_get0_log_by_id(log_store, sct->log_id,
+                                         sct->log_id_len);
+    }
+
+    BIO_printf(out, "%*sSigned Certificate Timestamp:", indent, "");
+    BIO_printf(out, "\n%*sVersion   : ", indent + 4, "");
+
+    if (sct->version != SCT_VERSION_V1) {
+        BIO_printf(out, "unknown\n%*s", indent + 16, "");
+        BIO_hex_string(out, indent + 16, 16, sct->sct, sct->sct_len);
+        return;
+    }
+
+    BIO_printf(out, "v1 (0x0)");
+
+    if (log != NULL) {
+        BIO_printf(out, "\n%*sLog       : %s", indent + 4, "",
+                   CTLOG_get0_name(log));
+    }
+
+    BIO_printf(out, "\n%*sLog ID    : ", indent + 4, "");
+    BIO_hex_string(out, indent + 16, 16, sct->log_id, sct->log_id_len);
+
+    BIO_printf(out, "\n%*sTimestamp : ", indent + 4, "");
+    timestamp_print(sct->timestamp, out);
+
+    BIO_printf(out, "\n%*sExtensions: ", indent + 4, "");
+    if (sct->ext_len == 0)
+        BIO_printf(out, "none");
+    else
+        BIO_hex_string(out, indent + 16, 16, sct->ext, sct->ext_len);
+
+    BIO_printf(out, "\n%*sSignature : ", indent + 4, "");
+    SCT_signature_algorithms_print(sct, out);
+    BIO_printf(out, "\n%*s            ", indent + 4, "");
+    BIO_hex_string(out, indent + 16, 16, sct->sig, sct->sig_len);
+}
+
+void SCT_LIST_print(const STACK_OF(SCT) *sct_list, BIO *out, int indent,
+                    const char *separator, const CTLOG_STORE *log_store)
+{
+    int sct_count = sk_SCT_num(sct_list);
+    int i;
+
+    for (i = 0; i < sct_count; ++i) {
+        SCT *sct = sk_SCT_value(sct_list, i);
+
+        SCT_print(sct, out, indent, log_store);
+        if (i < sk_SCT_num(sct_list) - 1)
+            BIO_printf(out, "%s", separator);
+    }
+}
diff --git a/crypto/ct/ct_sct.c b/crypto/ct/ct_sct.c
new file mode 100644
index 000000000..cd2cf6096
--- /dev/null
+++ b/crypto/ct/ct_sct.c
@@ -0,0 +1,393 @@
+/*
+ * Copyright 2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#ifdef OPENSSL_NO_CT
+# error "CT disabled"
+#endif
+
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#include "ct_locl.h"
+
+SCT *SCT_new(void)
+{
+    SCT *sct = OPENSSL_zalloc(sizeof(*sct));
+
+    if (sct == NULL) {
+        CTerr(CT_F_SCT_NEW, ERR_R_MALLOC_FAILURE);
+        return NULL;
+    }
+
+    sct->entry_type = CT_LOG_ENTRY_TYPE_NOT_SET;
+    sct->version = SCT_VERSION_NOT_SET;
+    return sct;
+}
+
+void SCT_free(SCT *sct)
+{
+    if (sct == NULL)
+        return;
+
+    OPENSSL_free(sct->log_id);
+    OPENSSL_free(sct->ext);
+    OPENSSL_free(sct->sig);
+    OPENSSL_free(sct->sct);
+    OPENSSL_free(sct);
+}
+
+void SCT_LIST_free(STACK_OF(SCT) *a)
+{
+    sk_SCT_pop_free(a, SCT_free);
+}
+
+int SCT_set_version(SCT *sct, sct_version_t version)
+{
+    if (version != SCT_VERSION_V1) {
+        CTerr(CT_F_SCT_SET_VERSION, CT_R_UNSUPPORTED_VERSION);
+        return 0;
+    }
+    sct->version = version;
+    sct->validation_status = SCT_VALIDATION_STATUS_NOT_SET;
+    return 1;
+}
+
+int SCT_set_log_entry_type(SCT *sct, ct_log_entry_type_t entry_type)
+{
+    sct->validation_status = SCT_VALIDATION_STATUS_NOT_SET;
+
+    switch (entry_type) {
+    case CT_LOG_ENTRY_TYPE_X509:
+    case CT_LOG_ENTRY_TYPE_PRECERT:
+        sct->entry_type = entry_type;
+        return 1;
+    default:
+        CTerr(CT_F_SCT_SET_LOG_ENTRY_TYPE, CT_R_UNSUPPORTED_ENTRY_TYPE);
+        return 0;
+    }
+}
+
+int SCT_set0_log_id(SCT *sct, unsigned char *log_id, size_t log_id_len)
+{
+    if (sct->version == SCT_VERSION_V1 && log_id_len != CT_V1_HASHLEN) {
+        CTerr(CT_F_SCT_SET0_LOG_ID, CT_R_INVALID_LOG_ID_LENGTH);
+        return 0;
+    }
+
+    OPENSSL_free(sct->log_id);
+    sct->log_id = log_id;
+    sct->log_id_len = log_id_len;
+    sct->validation_status = SCT_VALIDATION_STATUS_NOT_SET;
+    return 1;
+}
+
+int SCT_set1_log_id(SCT *sct, const unsigned char *log_id, size_t log_id_len)
+{
+    if (sct->version == SCT_VERSION_V1 && log_id_len != CT_V1_HASHLEN) {
+        CTerr(CT_F_SCT_SET1_LOG_ID, CT_R_INVALID_LOG_ID_LENGTH);
+        return 0;
+    }
+
+    OPENSSL_free(sct->log_id);
+    sct->log_id = NULL;
+    sct->log_id_len = 0;
+    sct->validation_status = SCT_VALIDATION_STATUS_NOT_SET;
+
+    if (log_id != NULL && log_id_len > 0) {
+        sct->log_id = OPENSSL_memdup(log_id, log_id_len);
+        if (sct->log_id == NULL) {
+            CTerr(CT_F_SCT_SET1_LOG_ID, ERR_R_MALLOC_FAILURE);
+            return 0;
+        }
+        sct->log_id_len = log_id_len;
+    }
+    return 1;
+}
+
+
+void SCT_set_timestamp(SCT *sct, uint64_t timestamp)
+{
+    sct->timestamp = timestamp;
+    sct->validation_status = SCT_VALIDATION_STATUS_NOT_SET;
+}
+
+int SCT_set_signature_nid(SCT *sct, int nid)
+{
+    switch (nid) {
+    case NID_sha256WithRSAEncryption:
+        sct->hash_alg = TLSEXT_hash_sha256;
+        sct->sig_alg = TLSEXT_signature_rsa;
+        sct->validation_status = SCT_VALIDATION_STATUS_NOT_SET;
+        return 1;
+    case NID_ecdsa_with_SHA256:
+        sct->hash_alg = TLSEXT_hash_sha256;
+        sct->sig_alg = TLSEXT_signature_ecdsa;
+        sct->validation_status = SCT_VALIDATION_STATUS_NOT_SET;
+        return 1;
+    default:
+        CTerr(CT_F_SCT_SET_SIGNATURE_NID, CT_R_UNRECOGNIZED_SIGNATURE_NID);
+        return 0;
+    }
+}
+
+void SCT_set0_extensions(SCT *sct, unsigned char *ext, size_t ext_len)
+{
+    OPENSSL_free(sct->ext);
+    sct->ext = ext;
+    sct->ext_len = ext_len;
+    sct->validation_status = SCT_VALIDATION_STATUS_NOT_SET;
+}
+
+int SCT_set1_extensions(SCT *sct, const unsigned char *ext, size_t ext_len)
+{
+    OPENSSL_free(sct->ext);
+    sct->ext = NULL;
+    sct->ext_len = 0;
+    sct->validation_status = SCT_VALIDATION_STATUS_NOT_SET;
+
+    if (ext != NULL && ext_len > 0) {
+        sct->ext = OPENSSL_memdup(ext, ext_len);
+        if (sct->ext == NULL) {
+            CTerr(CT_F_SCT_SET1_EXTENSIONS, ERR_R_MALLOC_FAILURE);
+            return 0;
+        }
+        sct->ext_len = ext_len;
+    }
+    return 1;
+}
+
+void SCT_set0_signature(SCT *sct, unsigned char *sig, size_t sig_len)
+{
+    OPENSSL_free(sct->sig);
+    sct->sig = sig;
+    sct->sig_len = sig_len;
+    sct->validation_status = SCT_VALIDATION_STATUS_NOT_SET;
+}
+
+int SCT_set1_signature(SCT *sct, const unsigned char *sig, size_t sig_len)
+{
+    OPENSSL_free(sct->sig);
+    sct->sig = NULL;
+    sct->sig_len = 0;
+    sct->validation_status = SCT_VALIDATION_STATUS_NOT_SET;
+
+    if (sig != NULL && sig_len > 0) {
+        sct->sig = OPENSSL_memdup(sig, sig_len);
+        if (sct->sig == NULL) {
+            CTerr(CT_F_SCT_SET1_SIGNATURE, ERR_R_MALLOC_FAILURE);
+            return 0;
+        }
+        sct->sig_len = sig_len;
+    }
+    return 1;
+}
+
+sct_version_t SCT_get_version(const SCT *sct)
+{
+    return sct->version;
+}
+
+ct_log_entry_type_t SCT_get_log_entry_type(const SCT *sct)
+{
+    return sct->entry_type;
+}
+
+size_t SCT_get0_log_id(const SCT *sct, unsigned char **log_id)
+{
+    *log_id = sct->log_id;
+    return sct->log_id_len;
+}
+
+uint64_t SCT_get_timestamp(const SCT *sct)
+{
+    return sct->timestamp;
+}
+
+int SCT_get_signature_nid(const SCT *sct)
+{
+    if (sct->version == SCT_VERSION_V1) {
+        if (sct->hash_alg == TLSEXT_hash_sha256) {
+            switch (sct->sig_alg) {
+            case TLSEXT_signature_ecdsa:
+                return NID_ecdsa_with_SHA256;
+            case TLSEXT_signature_rsa:
+                return NID_sha256WithRSAEncryption;
+            default:
+                return NID_undef;
+            }
+        }
+    }
+    return NID_undef;
+}
+
+size_t SCT_get0_extensions(const SCT *sct, unsigned char **ext)
+{
+    *ext = sct->ext;
+    return sct->ext_len;
+}
+
+size_t SCT_get0_signature(const SCT *sct, unsigned char **sig)
+{
+    *sig = sct->sig;
+    return sct->sig_len;
+}
+
+int SCT_is_complete(const SCT *sct)
+{
+    switch (sct->version) {
+    case SCT_VERSION_NOT_SET:
+        return 0;
+    case SCT_VERSION_V1:
+        return sct->log_id != NULL && SCT_signature_is_complete(sct);
+    default:
+        return sct->sct != NULL; /* Just need cached encoding */
+    }
+}
+
+int SCT_signature_is_complete(const SCT *sct)
+{
+    return SCT_get_signature_nid(sct) != NID_undef &&
+        sct->sig != NULL && sct->sig_len > 0;
+}
+
+sct_source_t SCT_get_source(const SCT *sct)
+{
+    return sct->source;
+}
+
+int SCT_set_source(SCT *sct, sct_source_t source)
+{
+    sct->source = source;
+    sct->validation_status = SCT_VALIDATION_STATUS_NOT_SET;
+    switch (source) {
+    case SCT_SOURCE_TLS_EXTENSION:
+    case SCT_SOURCE_OCSP_STAPLED_RESPONSE:
+        return SCT_set_log_entry_type(sct, CT_LOG_ENTRY_TYPE_X509);
+    case SCT_SOURCE_X509V3_EXTENSION:
+        return SCT_set_log_entry_type(sct, CT_LOG_ENTRY_TYPE_PRECERT);
+    default: /* if we aren't sure, leave the log entry type alone */
+        return 1;
+    }
+}
+
+sct_validation_status_t SCT_get_validation_status(const SCT *sct)
+{
+    return sct->validation_status;
+}
+
+int SCT_validate(SCT *sct, const CT_POLICY_EVAL_CTX *ctx)
+{
+    int is_sct_valid = -1;
+    SCT_CTX *sctx = NULL;
+    X509_PUBKEY *pub = NULL, *log_pkey = NULL;
+    const CTLOG *log;
+
+    /*
+     * With an unrecognized SCT version we don't know what such an SCT means,
+     * let alone validate one.  So we return validation failure (0).
+     */
+    if (sct->version != SCT_VERSION_V1) {
+        sct->validation_status = SCT_VALIDATION_STATUS_UNKNOWN_VERSION;
+        return 0;
+    }
+
+    log = CTLOG_STORE_get0_log_by_id(ctx->log_store,
+                                     sct->log_id, sct->log_id_len);
+
+    /* Similarly, an SCT from an unknown log also cannot be validated. */
+    if (log == NULL) {
+        sct->validation_status = SCT_VALIDATION_STATUS_UNKNOWN_LOG;
+        return 0;
+    }
+
+    sctx = SCT_CTX_new();
+    if (sctx == NULL)
+        goto err;
+
+    if (X509_PUBKEY_set(&log_pkey, CTLOG_get0_public_key(log)) != 1)
+        goto err;
+    if (SCT_CTX_set1_pubkey(sctx, log_pkey) != 1)
+        goto err;
+
+    if (SCT_get_log_entry_type(sct) == CT_LOG_ENTRY_TYPE_PRECERT) {
+        EVP_PKEY *issuer_pkey;
+
+        if (ctx->issuer == NULL) {
+            sct->validation_status = SCT_VALIDATION_STATUS_UNVERIFIED;
+            goto end;
+        }
+
+        issuer_pkey = X509_get0_pubkey(ctx->issuer);
+
+        if (X509_PUBKEY_set(&pub, issuer_pkey) != 1)
+            goto err;
+        if (SCT_CTX_set1_issuer_pubkey(sctx, pub) != 1)
+            goto err;
+    }
+
+    SCT_CTX_set_time(sctx, ctx->epoch_time_in_ms);
+
+    /*
+     * XXX: Potential for optimization.  This repeats some idempotent heavy
+     * lifting on the certificate for each candidate SCT, and appears to not
+     * use any information in the SCT itself, only the certificate is
+     * processed.  So it may make more sense to to do this just once, perhaps
+     * associated with the shared (by all SCTs) policy eval ctx.
+     *
+     * XXX: Failure here is global (SCT independent) and represents either an
+     * issue with the certificate (e.g. duplicate extensions) or an out of
+     * memory condition.  When the certificate is incompatible with CT, we just
+     * mark the SCTs invalid, rather than report a failure to determine the
+     * validation status.  That way, callbacks that want to do "soft" SCT
+     * processing will not abort handshakes with false positive internal
+     * errors.  Since the function does not distinguish between certificate
+     * issues (peer's fault) and internal problems (out fault) the safe thing
+     * to do is to report a validation failure and let the callback or
+     * application decide what to do.
+     */
+    if (SCT_CTX_set1_cert(sctx, ctx->cert, NULL) != 1)
+        sct->validation_status = SCT_VALIDATION_STATUS_UNVERIFIED;
+    else
+        sct->validation_status = SCT_CTX_verify(sctx, sct) == 1 ?
+            SCT_VALIDATION_STATUS_VALID : SCT_VALIDATION_STATUS_INVALID;
+
+end:
+    is_sct_valid = sct->validation_status == SCT_VALIDATION_STATUS_VALID;
+err:
+    X509_PUBKEY_free(pub);
+    X509_PUBKEY_free(log_pkey);
+    SCT_CTX_free(sctx);
+
+    return is_sct_valid;
+}
+
+int SCT_LIST_validate(const STACK_OF(SCT) *scts, CT_POLICY_EVAL_CTX *ctx)
+{
+    int are_scts_valid = 1;
+    int sct_count = scts != NULL ? sk_SCT_num(scts) : 0;
+    int i;
+
+    for (i = 0; i < sct_count; ++i) {
+        int is_sct_valid = -1;
+        SCT *sct = sk_SCT_value(scts, i);
+
+        if (sct == NULL)
+            continue;
+
+        is_sct_valid = SCT_validate(sct, ctx);
+        if (is_sct_valid < 0)
+            return is_sct_valid;
+        are_scts_valid &= is_sct_valid;
+    }
+
+    return are_scts_valid;
+}
diff --git a/crypto/ct/ct_sct_ctx.c b/crypto/ct/ct_sct_ctx.c
new file mode 100644
index 000000000..75a5027df
--- /dev/null
+++ b/crypto/ct/ct_sct_ctx.c
@@ -0,0 +1,263 @@
+/*
+ * Copyright 2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#ifdef OPENSSL_NO_CT
+# error "CT is disabled"
+#endif
+
+#include 
+#include 
+
+#include 
+#include 
+#include 
+
+#include "ct_locl.h"
+
+SCT_CTX *SCT_CTX_new(void)
+{
+    SCT_CTX *sctx = OPENSSL_zalloc(sizeof(*sctx));
+
+    if (sctx == NULL)
+        CTerr(CT_F_SCT_CTX_NEW, ERR_R_MALLOC_FAILURE);
+
+    return sctx;
+}
+
+void SCT_CTX_free(SCT_CTX *sctx)
+{
+    if (sctx == NULL)
+        return;
+    EVP_PKEY_free(sctx->pkey);
+    OPENSSL_free(sctx->pkeyhash);
+    OPENSSL_free(sctx->ihash);
+    OPENSSL_free(sctx->certder);
+    OPENSSL_free(sctx->preder);
+    OPENSSL_free(sctx);
+}
+
+/*
+ * Finds the index of the first extension with the given NID in cert.
+ * If there is more than one extension with that NID, *is_duplicated is set to
+ * 1, otherwise 0 (unless it is NULL).
+ */
+static int ct_x509_get_ext(X509 *cert, int nid, int *is_duplicated)
+{
+    int ret = X509_get_ext_by_NID(cert, nid, -1);
+
+    if (is_duplicated != NULL)
+        *is_duplicated = ret >= 0 && X509_get_ext_by_NID(cert, nid, ret) >= 0;
+
+    return ret;
+}
+
+/*
+ * Modifies a certificate by deleting extensions and copying the issuer and
+ * AKID from the presigner certificate, if necessary.
+ * Returns 1 on success, 0 otherwise.
+ */
+__owur static int ct_x509_cert_fixup(X509 *cert, X509 *presigner)
+{
+    int preidx, certidx;
+    int pre_akid_ext_is_dup, cert_akid_ext_is_dup;
+
+    if (presigner == NULL)
+        return 1;
+
+    preidx = ct_x509_get_ext(presigner, NID_authority_key_identifier,
+                             &pre_akid_ext_is_dup);
+    certidx = ct_x509_get_ext(cert, NID_authority_key_identifier,
+                              &cert_akid_ext_is_dup);
+
+    /* An error occurred whilst searching for the extension */
+    if (preidx < -1 || certidx < -1)
+        return 0;
+    /* Invalid certificate if they contain duplicate extensions */
+    if (pre_akid_ext_is_dup || cert_akid_ext_is_dup)
+        return 0;
+    /* AKID must be present in both certificate or absent in both */
+    if (preidx >= 0 && certidx == -1)
+        return 0;
+    if (preidx == -1 && certidx >= 0)
+        return 0;
+    /* Copy issuer name */
+    if (!X509_set_issuer_name(cert, X509_get_issuer_name(presigner)))
+        return 0;
+    if (preidx != -1) {
+        /* Retrieve and copy AKID encoding */
+        X509_EXTENSION *preext = X509_get_ext(presigner, preidx);
+        X509_EXTENSION *certext = X509_get_ext(cert, certidx);
+        ASN1_OCTET_STRING *preextdata;
+
+        /* Should never happen */
+        if (preext == NULL || certext == NULL)
+            return 0;
+        preextdata = X509_EXTENSION_get_data(preext);
+        if (preextdata == NULL ||
+            !X509_EXTENSION_set_data(certext, preextdata))
+            return 0;
+    }
+    return 1;
+}
+
+int SCT_CTX_set1_cert(SCT_CTX *sctx, X509 *cert, X509 *presigner)
+{
+    unsigned char *certder = NULL, *preder = NULL;
+    X509 *pretmp = NULL;
+    int certderlen = 0, prederlen = 0;
+    int idx = -1;
+    int poison_ext_is_dup, sct_ext_is_dup;
+    int poison_idx = ct_x509_get_ext(cert, NID_ct_precert_poison, &poison_ext_is_dup);
+
+    /* Duplicate poison extensions are present - error */
+    if (poison_ext_is_dup)
+        goto err;
+
+    /* If *cert doesn't have a poison extension, it isn't a precert */
+    if (poison_idx == -1) {
+        /* cert isn't a precert, so we shouldn't have a presigner */
+        if (presigner != NULL)
+            goto err;
+
+        certderlen = i2d_X509(cert, &certder);
+        if (certderlen < 0)
+            goto err;
+    }
+
+    /* See if cert has a precert SCTs extension */
+    idx = ct_x509_get_ext(cert, NID_ct_precert_scts, &sct_ext_is_dup);
+    /* Duplicate SCT extensions are present - error */
+    if (sct_ext_is_dup)
+        goto err;
+
+    if (idx >= 0 && poison_idx >= 0) {
+        /*
+         * cert can't both contain SCTs (i.e. have an SCT extension) and be a
+         * precert (i.e. have a poison extension).
+         */
+        goto err;
+    }
+
+    if (idx == -1) {
+        idx = poison_idx;
+    }
+
+    /*
+     * If either a poison or SCT extension is present, remove it before encoding
+     * cert. This, along with ct_x509_cert_fixup(), gets a TBSCertificate (see
+     * RFC5280) from cert, which is what the CT log signed when it produced the
+     * SCT.
+     */
+    if (idx >= 0) {
+        X509_EXTENSION *ext;
+
+        /* Take a copy of certificate so we don't modify passed version */
+        pretmp = X509_dup(cert);
+        if (pretmp == NULL)
+            goto err;
+
+        ext = X509_delete_ext(pretmp, idx);
+        X509_EXTENSION_free(ext);
+
+        if (!ct_x509_cert_fixup(pretmp, presigner))
+            goto err;
+
+        prederlen = i2d_re_X509_tbs(pretmp, &preder);
+        if (prederlen <= 0)
+            goto err;
+    }
+
+    X509_free(pretmp);
+
+    OPENSSL_free(sctx->certder);
+    sctx->certder = certder;
+    sctx->certderlen = certderlen;
+
+    OPENSSL_free(sctx->preder);
+    sctx->preder = preder;
+    sctx->prederlen = prederlen;
+
+    return 1;
+err:
+    OPENSSL_free(certder);
+    OPENSSL_free(preder);
+    X509_free(pretmp);
+    return 0;
+}
+
+__owur static int ct_public_key_hash(X509_PUBKEY *pkey, unsigned char **hash,
+                                     size_t *hash_len)
+{
+    int ret = 0;
+    unsigned char *md = NULL, *der = NULL;
+    int der_len;
+    unsigned int md_len;
+
+    /* Reuse buffer if possible */
+    if (*hash != NULL && *hash_len >= SHA256_DIGEST_LENGTH) {
+        md = *hash;
+    } else {
+        md = OPENSSL_malloc(SHA256_DIGEST_LENGTH);
+        if (md == NULL)
+            goto err;
+    }
+
+    /* Calculate key hash */
+    der_len = i2d_X509_PUBKEY(pkey, &der);
+    if (der_len <= 0)
+        goto err;
+
+    if (!EVP_Digest(der, der_len, md, &md_len, EVP_sha256(), NULL))
+        goto err;
+
+    if (md != *hash) {
+        OPENSSL_free(*hash);
+        *hash = md;
+        *hash_len = SHA256_DIGEST_LENGTH;
+    }
+
+    md = NULL;
+    ret = 1;
+ err:
+    OPENSSL_free(md);
+    OPENSSL_free(der);
+    return ret;
+}
+
+int SCT_CTX_set1_issuer(SCT_CTX *sctx, const X509 *issuer)
+{
+    return SCT_CTX_set1_issuer_pubkey(sctx, X509_get_X509_PUBKEY(issuer));
+}
+
+int SCT_CTX_set1_issuer_pubkey(SCT_CTX *sctx, X509_PUBKEY *pubkey)
+{
+    return ct_public_key_hash(pubkey, &sctx->ihash, &sctx->ihashlen);
+}
+
+int SCT_CTX_set1_pubkey(SCT_CTX *sctx, X509_PUBKEY *pubkey)
+{
+    EVP_PKEY *pkey = X509_PUBKEY_get(pubkey);
+
+    if (pkey == NULL)
+        return 0;
+
+    if (!ct_public_key_hash(pubkey, &sctx->pkeyhash, &sctx->pkeyhashlen)) {
+        EVP_PKEY_free(pkey);
+        return 0;
+    }
+
+    EVP_PKEY_free(sctx->pkey);
+    sctx->pkey = pkey;
+    return 1;
+}
+
+void SCT_CTX_set_time(SCT_CTX *sctx, uint64_t time_in_ms)
+{
+    sctx->epoch_time_in_ms = time_in_ms;
+}
diff --git a/crypto/ct/ct_vfy.c b/crypto/ct/ct_vfy.c
new file mode 100644
index 000000000..cabcf5782
--- /dev/null
+++ b/crypto/ct/ct_vfy.c
@@ -0,0 +1,140 @@
+/*
+ * Copyright 2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+
+#include 
+#include 
+#include 
+#include 
+
+#include "ct_locl.h"
+
+typedef enum sct_signature_type_t {
+    SIGNATURE_TYPE_NOT_SET = -1,
+    SIGNATURE_TYPE_CERT_TIMESTAMP,
+    SIGNATURE_TYPE_TREE_HASH
+} SCT_SIGNATURE_TYPE;
+
+/*
+ * Update encoding for SCT signature verification/generation to supplied
+ * EVP_MD_CTX.
+ */
+static int sct_ctx_update(EVP_MD_CTX *ctx, const SCT_CTX *sctx, const SCT *sct)
+{
+    unsigned char tmpbuf[12];
+    unsigned char *p, *der;
+    size_t derlen;
+    /*+
+     * digitally-signed struct {
+     *   (1 byte) Version sct_version;
+     *   (1 byte) SignatureType signature_type = certificate_timestamp;
+     *   (8 bytes) uint64 timestamp;
+     *   (2 bytes) LogEntryType entry_type;
+     *   (? bytes) select(entry_type) {
+     *     case x509_entry: ASN.1Cert;
+     *     case precert_entry: PreCert;
+     *   } signed_entry;
+     *   (2 bytes + sct->ext_len) CtExtensions extensions;
+     * }
+     */
+    if (sct->entry_type == CT_LOG_ENTRY_TYPE_NOT_SET)
+        return 0;
+    if (sct->entry_type == CT_LOG_ENTRY_TYPE_PRECERT && sctx->ihash == NULL)
+        return 0;
+
+    p = tmpbuf;
+    *p++ = sct->version;
+    *p++ = SIGNATURE_TYPE_CERT_TIMESTAMP;
+    l2n8(sct->timestamp, p);
+    s2n(sct->entry_type, p);
+
+    if (!EVP_DigestUpdate(ctx, tmpbuf, p - tmpbuf))
+        return 0;
+
+    if (sct->entry_type == CT_LOG_ENTRY_TYPE_X509) {
+        der = sctx->certder;
+        derlen = sctx->certderlen;
+    } else {
+        if (!EVP_DigestUpdate(ctx, sctx->ihash, sctx->ihashlen))
+            return 0;
+        der = sctx->preder;
+        derlen = sctx->prederlen;
+    }
+
+    /* If no encoding available, fatal error */
+    if (der == NULL)
+        return 0;
+
+    /* Include length first */
+    p = tmpbuf;
+    l2n3(derlen, p);
+
+    if (!EVP_DigestUpdate(ctx, tmpbuf, 3))
+        return 0;
+    if (!EVP_DigestUpdate(ctx, der, derlen))
+        return 0;
+
+    /* Add any extensions */
+    p = tmpbuf;
+    s2n(sct->ext_len, p);
+    if (!EVP_DigestUpdate(ctx, tmpbuf, 2))
+        return 0;
+
+    if (sct->ext_len && !EVP_DigestUpdate(ctx, sct->ext, sct->ext_len))
+        return 0;
+
+    return 1;
+}
+
+int SCT_CTX_verify(const SCT_CTX *sctx, const SCT *sct)
+{
+    EVP_MD_CTX *ctx = NULL;
+    int ret = 0;
+
+    if (!SCT_is_complete(sct) || sctx->pkey == NULL ||
+        sct->entry_type == CT_LOG_ENTRY_TYPE_NOT_SET ||
+        (sct->entry_type == CT_LOG_ENTRY_TYPE_PRECERT && sctx->ihash == NULL)) {
+        CTerr(CT_F_SCT_CTX_VERIFY, CT_R_SCT_NOT_SET);
+        return 0;
+    }
+    if (sct->version != SCT_VERSION_V1) {
+        CTerr(CT_F_SCT_CTX_VERIFY, CT_R_SCT_UNSUPPORTED_VERSION);
+        return 0;
+    }
+    if (sct->log_id_len != sctx->pkeyhashlen ||
+        memcmp(sct->log_id, sctx->pkeyhash, sctx->pkeyhashlen) != 0) {
+        CTerr(CT_F_SCT_CTX_VERIFY, CT_R_SCT_LOG_ID_MISMATCH);
+        return 0;
+    }
+    if (sct->timestamp > sctx->epoch_time_in_ms) {
+        CTerr(CT_F_SCT_CTX_VERIFY, CT_R_SCT_FUTURE_TIMESTAMP);
+        return 0;
+    }
+
+    ctx = EVP_MD_CTX_new();
+    if (ctx == NULL)
+        goto end;
+
+    if (!EVP_DigestVerifyInit(ctx, NULL, EVP_sha256(), NULL, sctx->pkey))
+        goto end;
+
+    if (!sct_ctx_update(ctx, sctx, sct))
+        goto end;
+
+    /* Verify signature */
+    ret = EVP_DigestVerifyFinal(ctx, sct->sig, sct->sig_len);
+    /* If ret < 0 some other error: fall through without setting error */
+    if (ret == 0)
+        CTerr(CT_F_SCT_CTX_VERIFY, CT_R_SCT_INVALID_SIGNATURE);
+
+end:
+    EVP_MD_CTX_free(ctx);
+    return ret;
+}
diff --git a/crypto/ct/ct_vfy.o-29b9236e b/crypto/ct/ct_vfy.o-29b9236e
new file mode 100644
index 000000000..e69de29bb
diff --git a/crypto/ct/ct_x509v3.c b/crypto/ct/ct_x509v3.c
new file mode 100644
index 000000000..805ada088
--- /dev/null
+++ b/crypto/ct/ct_x509v3.c
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#ifdef OPENSSL_NO_CT
+# error "CT is disabled"
+#endif
+
+#include "ct_locl.h"
+
+static char *i2s_poison(const X509V3_EXT_METHOD *method, void *val)
+{
+    return OPENSSL_strdup("NULL");
+}
+
+static void *s2i_poison(const X509V3_EXT_METHOD *method, X509V3_CTX *ctx, const char *str)
+{
+   return ASN1_NULL_new();
+}
+
+static int i2r_SCT_LIST(X509V3_EXT_METHOD *method, STACK_OF(SCT) *sct_list,
+                 BIO *out, int indent)
+{
+    SCT_LIST_print(sct_list, out, indent, "\n", NULL);
+    return 1;
+}
+
+/* Handlers for X509v3/OCSP Certificate Transparency extensions */
+const X509V3_EXT_METHOD v3_ct_scts[3] = {
+    /* X509v3 extension in certificates that contains SCTs */
+    { NID_ct_precert_scts, 0, NULL,
+    NULL, (X509V3_EXT_FREE)SCT_LIST_free,
+    (X509V3_EXT_D2I)d2i_SCT_LIST, (X509V3_EXT_I2D)i2d_SCT_LIST,
+    NULL, NULL,
+    NULL, NULL,
+    (X509V3_EXT_I2R)i2r_SCT_LIST, NULL,
+    NULL },
+
+    /* X509v3 extension to mark a certificate as a pre-certificate */
+    { NID_ct_precert_poison, 0, ASN1_ITEM_ref(ASN1_NULL),
+    NULL, NULL, NULL, NULL,
+    i2s_poison, s2i_poison,
+    NULL, NULL,
+    NULL, NULL,
+    NULL },
+
+    /* OCSP extension that contains SCTs */
+    { NID_ct_cert_scts, 0, NULL,
+    0, (X509V3_EXT_FREE)SCT_LIST_free,
+    (X509V3_EXT_D2I)d2i_SCT_LIST, (X509V3_EXT_I2D)i2d_SCT_LIST,
+    NULL, NULL,
+    NULL, NULL,
+    (X509V3_EXT_I2R)i2r_SCT_LIST, NULL,
+    NULL },
+};
diff --git a/crypto/cversion.c b/crypto/cversion.c
new file mode 100644
index 000000000..96d8a5b5e
--- /dev/null
+++ b/crypto/cversion.c
@@ -0,0 +1,65 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "internal/cryptlib.h"
+
+#ifndef NO_WINDOWS_BRAINDEATH
+# include "buildinf.h"
+#endif
+
+unsigned long OpenSSL_version_num(void)
+{
+    return OPENSSL_VERSION_NUMBER;
+}
+
+const char *OpenSSL_version(int t)
+{
+    if (t == OPENSSL_VERSION)
+        return OPENSSL_VERSION_TEXT;
+    if (t == OPENSSL_BUILT_ON) {
+#ifdef DATE
+# ifdef OPENSSL_USE_BUILD_DATE
+        return (DATE);
+# else
+        return ("built on: reproducible build, date unspecified");
+# endif
+#else
+        return ("built on: date not available");
+#endif
+    }
+    if (t == OPENSSL_CFLAGS) {
+#ifdef CFLAGS
+        return (CFLAGS);
+#else
+        return ("compiler: information not available");
+#endif
+    }
+    if (t == OPENSSL_PLATFORM) {
+#ifdef PLATFORM
+        return (PLATFORM);
+#else
+        return ("platform: information not available");
+#endif
+    }
+    if (t == OPENSSL_DIR) {
+#ifdef OPENSSLDIR
+        return "OPENSSLDIR: \"" OPENSSLDIR "\"";
+#else
+        return "OPENSSLDIR: N/A";
+#endif
+    }
+    if (t == OPENSSL_ENGINES_DIR) {
+#ifdef ENGINESDIR
+        return "ENGINESDIR: \"" ENGINESDIR "\"";
+#else
+        return "ENGINESDIR: N/A";
+#endif
+    }
+    return ("not available");
+}
diff --git a/crypto/delocate.h b/crypto/delocate.h
new file mode 100644
index 000000000..0153a4eba
--- /dev/null
+++ b/crypto/delocate.h
@@ -0,0 +1,88 @@
+/* Copyright (c) 2017, Google Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
+
+#ifndef OPENSSL_HEADER_FIPSMODULE_DELOCATE_H
+#define OPENSSL_HEADER_FIPSMODULE_DELOCATE_H
+
+#include 
+
+#include "../internal.h"
+
+
+#if defined(BORINGSSL_FIPS) && !defined(OPENSSL_ASAN) && !defined(OPENSSL_MSAN)
+#define DEFINE_BSS_GET(type, name)        \
+  static type name __attribute__((used)); \
+  type *name##_bss_get(void);
+/* For FIPS builds we require that CRYPTO_ONCE_INIT be zero. */
+#define DEFINE_STATIC_ONCE(name) DEFINE_BSS_GET(CRYPTO_once_t, name)
+/* For FIPS builds we require that CRYPTO_STATIC_MUTEX_INIT be zero. */
+#define DEFINE_STATIC_MUTEX(name) \
+  DEFINE_BSS_GET(struct CRYPTO_STATIC_MUTEX, name)
+/* For FIPS builds we require that CRYPTO_EX_DATA_CLASS_INIT be zero. */
+#define DEFINE_STATIC_EX_DATA_CLASS(name) \
+  DEFINE_BSS_GET(CRYPTO_EX_DATA_CLASS, name)
+#else
+#define DEFINE_BSS_GET(type, name) \
+  static type name;                \
+  static type *name##_bss_get(void) { return &name; }
+#define DEFINE_STATIC_ONCE(name)                \
+  static CRYPTO_once_t name = CRYPTO_ONCE_INIT; \
+  static CRYPTO_once_t *name##_bss_get(void) { return &name; }
+#define DEFINE_STATIC_MUTEX(name)                                    \
+  static struct CRYPTO_STATIC_MUTEX name = CRYPTO_STATIC_MUTEX_INIT; \
+  static struct CRYPTO_STATIC_MUTEX *name##_bss_get(void) { return &name; }
+#define DEFINE_STATIC_EX_DATA_CLASS(name)                       \
+  static CRYPTO_EX_DATA_CLASS name = CRYPTO_EX_DATA_CLASS_INIT; \
+  static CRYPTO_EX_DATA_CLASS *name##_bss_get(void) { return &name; }
+#endif
+
+#define DEFINE_DATA(type, name, accessor_decorations)                         \
+  DEFINE_BSS_GET(type, name##_storage)                                        \
+  DEFINE_STATIC_ONCE(name##_once)                                             \
+  static void name##_do_init(type *out);                                      \
+  static void name##_init(void) { name##_do_init(name##_storage_bss_get()); } \
+  accessor_decorations type *name(void) {                                     \
+    CRYPTO_once(name##_once_bss_get(), name##_init);                          \
+    /* See http://c-faq.com/ansi/constmismatch.html for why the following     \
+     * cast is needed. */                                                     \
+    return (const type *)name##_storage_bss_get();                            \
+  }                                                                           \
+  static void name##_do_init(type *out)
+
+/* DEFINE_METHOD_FUNCTION defines a function named |name| which returns a
+ * method table of type const |type|*. In FIPS mode, to avoid rel.ro data, it
+ * is split into a CRYPTO_once_t-guarded initializer in the module and
+ * unhashed, non-module accessor functions to space reserved in the BSS. The
+ * method table is initialized by a caller-supplied function which takes a
+ * parameter named |out| of type |type|*. The caller should follow the macro
+ * invocation with the body of this function:
+ *
+ *     DEFINE_METHOD_FUNCTION(EVP_MD, EVP_md4) {
+ *       out->type = NID_md4;
+ *       out->md_size = MD4_DIGEST_LENGTH;
+ *       out->flags = 0;
+ *       out->init = md4_init;
+ *       out->update = md4_update;
+ *       out->final = md4_final;
+ *       out->block_size = 64;
+ *       out->ctx_size = sizeof(MD4_CTX);
+ *     }
+ *
+ * This mechanism does not use a static initializer because their execution
+ * order is undefined. See FIPS.md for more details. */
+#define DEFINE_METHOD_FUNCTION(type, name) DEFINE_DATA(type, name, const)
+
+#define DEFINE_LOCAL_DATA(type, name) DEFINE_DATA(type, name, static const)
+
+#endif /* OPENSSL_HEADER_FIPSMODULE_DELOCATE_H */
diff --git a/crypto/des/asm/crypt586.pl b/crypto/des/asm/crypt586.pl
new file mode 100644
index 000000000..d5911a185
--- /dev/null
+++ b/crypto/des/asm/crypt586.pl
@@ -0,0 +1,217 @@
+#! /usr/bin/env perl
+# Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+# The inner loop instruction sequence and the IP/FP modifications are from
+# Svend Olaf Mikkelsen 
+
+$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+push(@INC,"${dir}","${dir}../../perlasm");
+require "x86asm.pl";
+
+$output=pop;
+open STDOUT,">$output";
+
+&asm_init($ARGV[0],"crypt586.pl");
+
+$L="edi";
+$R="esi";
+
+&external_label("DES_SPtrans");
+&fcrypt_body("fcrypt_body");
+&asm_finish();
+
+close STDOUT;
+
+sub fcrypt_body
+	{
+	local($name,$do_ip)=@_;
+
+	&function_begin($name);
+
+	&comment("");
+	&comment("Load the 2 words");
+	$trans="ebp";
+
+	&xor(	$L,	$L);
+	&xor(	$R,	$R);
+
+	# PIC-ification:-)
+	&picmeup("edx","DES_SPtrans");
+	#if ($cpp)	{ &picmeup("edx","DES_SPtrans");   }
+	#else		{ &lea("edx",&DWP("DES_SPtrans")); }
+	&push("edx");	# becomes &swtmp(1)
+	#
+	&mov($trans,&wparam(1)); # reloaded with DES_SPtrans in D_ENCRYPT
+
+	&push(&DWC(25)); # add a variable
+
+	&set_label("start");
+	for ($i=0; $i<16; $i+=2)
+		{
+		&comment("");
+		&comment("Round $i");
+		&D_ENCRYPT($i,$L,$R,$i*2,$trans,"eax","ebx","ecx","edx");
+
+		&comment("");
+		&comment("Round ".sprintf("%d",$i+1));
+		&D_ENCRYPT($i+1,$R,$L,($i+1)*2,$trans,"eax","ebx","ecx","edx");
+		}
+	 &mov("ebx",	&swtmp(0));
+	&mov("eax",	$L);
+	 &dec("ebx");
+	&mov($L,	$R);
+	 &mov($R,	"eax");
+	&mov(&swtmp(0),	"ebx");
+	 &jnz(&label("start"));
+
+	&comment("");
+	&comment("FP");
+	&mov("edx",&wparam(0));
+
+	&FP_new($R,$L,"eax",3);
+	&mov(&DWP(0,"edx","",0),"eax");
+	&mov(&DWP(4,"edx","",0),$L);
+
+	&add("esp",8);	# remove variables
+
+	&function_end($name);
+	}
+
+sub D_ENCRYPT
+	{
+	local($r,$L,$R,$S,$trans,$u,$tmp1,$tmp2,$t)=@_;
+
+	&mov(	$u,		&wparam(2));			# 2
+	&mov(	$t,		$R);
+	&shr(	$t,		16);				# 1
+	&mov(	$tmp2,		&wparam(3));			# 2
+	&xor(	$t,		$R);				# 1
+
+	&and(	$u,		$t);				# 2
+	&and(	$t,		$tmp2);				# 2
+
+	&mov(	$tmp1,		$u);
+	&shl(	$tmp1,		16); 				# 1
+	&mov(	$tmp2,		$t);
+	&shl(	$tmp2,		16); 				# 1
+	&xor(	$u,		$tmp1);				# 2
+	&xor(	$t,		$tmp2);				# 2
+	&mov(	$tmp1,		&DWP(&n2a($S*4),$trans,"",0));	# 2
+	&xor(	$u,		$tmp1);
+	&mov(	$tmp2,		&DWP(&n2a(($S+1)*4),$trans,"",0));	# 2
+	&xor(	$u,		$R);
+	&xor(	$t,		$R);
+	&xor(	$t,		$tmp2);
+
+	&and(	$u,		"0xfcfcfcfc"	);		# 2
+	&xor(	$tmp1,		$tmp1);				# 1
+	&and(	$t,		"0xcfcfcfcf"	);		# 2
+	&xor(	$tmp2,		$tmp2);	
+	&movb(	&LB($tmp1),	&LB($u)	);
+	&movb(	&LB($tmp2),	&HB($u)	);
+	&rotr(	$t,		4		);
+	&mov(	$trans,		&swtmp(1));
+	&xor(	$L,		&DWP("     ",$trans,$tmp1,0));
+	&movb(	&LB($tmp1),	&LB($t)	);
+	&xor(	$L,		&DWP("0x200",$trans,$tmp2,0));
+	&movb(	&LB($tmp2),	&HB($t)	);
+	&shr(	$u,		16);
+	&xor(	$L,		&DWP("0x100",$trans,$tmp1,0));
+	&movb(	&LB($tmp1),	&HB($u)	);
+	&shr(	$t,		16);
+	&xor(	$L,		&DWP("0x300",$trans,$tmp2,0));
+	&movb(	&LB($tmp2),	&HB($t)	);
+	&and(	$u,		"0xff"	);
+	&and(	$t,		"0xff"	);
+	&mov(	$tmp1,		&DWP("0x600",$trans,$tmp1,0));
+	&xor(	$L,		$tmp1);
+	&mov(	$tmp1,		&DWP("0x700",$trans,$tmp2,0));
+	&xor(	$L,		$tmp1);
+	&mov(	$tmp1,		&DWP("0x400",$trans,$u,0));
+	&xor(	$L,		$tmp1);
+	&mov(	$tmp1,		&DWP("0x500",$trans,$t,0));
+	&xor(	$L,		$tmp1);
+	&mov(	$trans,		&wparam(1));
+	}
+
+sub n2a
+	{
+	sprintf("%d",$_[0]);
+	}
+
+# now has a side affect of rotating $a by $shift
+sub R_PERM_OP
+	{
+	local($a,$b,$tt,$shift,$mask,$last)=@_;
+
+	&rotl(	$a,		$shift		) if ($shift != 0);
+	&mov(	$tt,		$a		);
+	&xor(	$a,		$b		);
+	&and(	$a,		$mask		);
+	if ($notlast eq $b)
+		{
+		&xor(	$b,		$a		);
+		&xor(	$tt,		$a		);
+		}
+	else
+		{
+		&xor(	$tt,		$a		);
+		&xor(	$b,		$a		);
+		}
+	&comment("");
+	}
+
+sub IP_new
+	{
+	local($l,$r,$tt,$lr)=@_;
+
+	&R_PERM_OP($l,$r,$tt, 4,"0xf0f0f0f0",$l);
+	&R_PERM_OP($r,$tt,$l,20,"0xfff0000f",$l);
+	&R_PERM_OP($l,$tt,$r,14,"0x33333333",$r);
+	&R_PERM_OP($tt,$r,$l,22,"0x03fc03fc",$r);
+	&R_PERM_OP($l,$r,$tt, 9,"0xaaaaaaaa",$r);
+	
+	if ($lr != 3)
+		{
+		if (($lr-3) < 0)
+			{ &rotr($tt,	3-$lr); }
+		else	{ &rotl($tt,	$lr-3); }
+		}
+	if ($lr != 2)
+		{
+		if (($lr-2) < 0)
+			{ &rotr($r,	2-$lr); }
+		else	{ &rotl($r,	$lr-2); }
+		}
+	}
+
+sub FP_new
+	{
+	local($l,$r,$tt,$lr)=@_;
+
+	if ($lr != 2)
+		{
+		if (($lr-2) < 0)
+			{ &rotl($r,	2-$lr); }
+		else	{ &rotr($r,	$lr-2); }
+		}
+	if ($lr != 3)
+		{
+		if (($lr-3) < 0)
+			{ &rotl($l,	3-$lr); }
+		else	{ &rotr($l,	$lr-3); }
+		}
+
+	&R_PERM_OP($l,$r,$tt, 0,"0xaaaaaaaa",$r);
+	&R_PERM_OP($tt,$r,$l,23,"0x03fc03fc",$r);
+	&R_PERM_OP($l,$r,$tt,10,"0x33333333",$l);
+	&R_PERM_OP($r,$tt,$l,18,"0xfff0000f",$l);
+	&R_PERM_OP($l,$tt,$r,12,"0xf0f0f0f0",$r);
+	&rotr($tt	, 4);
+	}
+
diff --git a/crypto/des/asm/des-586.pl b/crypto/des/asm/des-586.pl
new file mode 100644
index 000000000..3d7c7f1b9
--- /dev/null
+++ b/crypto/des/asm/des-586.pl
@@ -0,0 +1,465 @@
+#! /usr/bin/env perl
+# Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+# The inner loop instruction sequence and the IP/FP modifications are from
+# Svend Olaf Mikkelsen 
+
+$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+push(@INC,"${dir}","${dir}../../perlasm");
+require "x86asm.pl";
+require "cbc.pl";
+require "desboth.pl";
+
+# base code is in microsft
+# op dest, source
+# format.
+#
+
+$output=pop;
+open STDOUT,">$output";
+
+&asm_init($ARGV[0],"des-586.pl");
+
+$L="edi";
+$R="esi";
+$trans="ebp";
+$small_footprint=1 if (grep(/\-DOPENSSL_SMALL_FOOTPRINT/,@ARGV));
+# one can discuss setting this variable to 1 unconditionally, as
+# the folded loop is only 3% slower than unrolled, but >7 times smaller
+
+&public_label("DES_SPtrans");
+&static_label("des_sptrans");
+
+&DES_encrypt_internal();
+&DES_decrypt_internal();
+&DES_encrypt("DES_encrypt1",1);
+&DES_encrypt("DES_encrypt2",0);
+&DES_encrypt3("DES_encrypt3",1);
+&DES_encrypt3("DES_decrypt3",0);
+&cbc("DES_ncbc_encrypt","DES_encrypt1","DES_encrypt1",0,4,5,3,5,-1);
+&cbc("DES_ede3_cbc_encrypt","DES_encrypt3","DES_decrypt3",0,6,7,3,4,5);
+&DES_SPtrans();
+
+&asm_finish();
+
+close STDOUT;
+
+sub DES_encrypt_internal()
+	{
+	&function_begin_B("_x86_DES_encrypt");
+
+	if ($small_footprint)
+	    {
+	    &lea("edx",&DWP(128,"ecx"));
+	    &push("edx");
+	    &push("ecx");
+	    &set_label("eloop");
+		&D_ENCRYPT(0,$L,$R,0,$trans,"eax","ebx","ecx","edx",&swtmp(0));
+		&comment("");
+		&D_ENCRYPT(1,$R,$L,2,$trans,"eax","ebx","ecx","edx",&swtmp(0));
+		&comment("");
+		&add("ecx",16);
+		&cmp("ecx",&swtmp(1));
+		&mov(&swtmp(0),"ecx");
+		&jb(&label("eloop"));
+	    &add("esp",8);
+	    }
+	else
+	    {
+	    &push("ecx");
+	    for ($i=0; $i<16; $i+=2)
+		{
+		&comment("Round $i");
+		&D_ENCRYPT($i,$L,$R,$i*2,$trans,"eax","ebx","ecx","edx",&swtmp(0));
+		&comment("Round ".sprintf("%d",$i+1));
+		&D_ENCRYPT($i+1,$R,$L,($i+1)*2,$trans,"eax","ebx","ecx","edx",&swtmp(0));
+		}
+	    &add("esp",4);
+	}
+	&ret();
+
+	&function_end_B("_x86_DES_encrypt");
+	}
+	
+sub DES_decrypt_internal()
+	{
+	&function_begin_B("_x86_DES_decrypt");
+
+	if ($small_footprint)
+	    {
+	    &push("ecx");
+	    &lea("ecx",&DWP(128,"ecx"));
+	    &push("ecx");
+	    &set_label("dloop");
+		&D_ENCRYPT(0,$L,$R,-2,$trans,"eax","ebx","ecx","edx",&swtmp(0));
+		&comment("");
+		&D_ENCRYPT(1,$R,$L,-4,$trans,"eax","ebx","ecx","edx",&swtmp(0));
+		&comment("");
+		&sub("ecx",16);
+		&cmp("ecx",&swtmp(1));
+		&mov(&swtmp(0),"ecx");
+		&ja(&label("dloop"));
+	    &add("esp",8);
+	    }
+	else
+	    {
+	    &push("ecx");
+	    for ($i=15; $i>0; $i-=2)
+		{
+		&comment("Round $i");
+		&D_ENCRYPT(15-$i,$L,$R,$i*2,$trans,"eax","ebx","ecx","edx",&swtmp(0));
+		&comment("Round ".sprintf("%d",$i-1));
+		&D_ENCRYPT(15-$i+1,$R,$L,($i-1)*2,$trans,"eax","ebx","ecx","edx",&swtmp(0));
+		}
+	    &add("esp",4);
+	    }
+	&ret();
+
+	&function_end_B("_x86_DES_decrypt");
+	}
+	
+sub DES_encrypt
+	{
+	local($name,$do_ip)=@_;
+
+	&function_begin_B($name);
+
+	&push("esi");
+	&push("edi");
+
+	&comment("");
+	&comment("Load the 2 words");
+
+	if ($do_ip)
+		{
+		&mov($R,&wparam(0));
+		 &xor(	"ecx",		"ecx"		);
+
+		&push("ebx");
+		&push("ebp");
+
+		&mov("eax",&DWP(0,$R,"",0));
+		 &mov("ebx",&wparam(2));	# get encrypt flag
+		&mov($L,&DWP(4,$R,"",0));
+		&comment("");
+		&comment("IP");
+		&IP_new("eax",$L,$R,3);
+		}
+	else
+		{
+		&mov("eax",&wparam(0));
+		 &xor(	"ecx",		"ecx"		);
+
+		&push("ebx");
+		&push("ebp");
+
+		&mov($R,&DWP(0,"eax","",0));
+		 &mov("ebx",&wparam(2));	# get encrypt flag
+		&rotl($R,3);
+		&mov($L,&DWP(4,"eax","",0));
+		&rotl($L,3);
+		}
+
+	# PIC-ification:-)
+	&call	(&label("pic_point"));
+	&set_label("pic_point");
+	&blindpop($trans);
+	&lea	($trans,&DWP(&label("des_sptrans")."-".&label("pic_point"),$trans));
+
+	&mov(	"ecx",	&wparam(1)	);
+
+	&cmp("ebx","0");
+	&je(&label("decrypt"));
+	&call("_x86_DES_encrypt");
+	&jmp(&label("done"));
+	&set_label("decrypt");
+	&call("_x86_DES_decrypt");
+	&set_label("done");
+
+	if ($do_ip)
+		{
+		&comment("");
+		&comment("FP");
+		&mov("edx",&wparam(0));
+		&FP_new($L,$R,"eax",3);
+
+		&mov(&DWP(0,"edx","",0),"eax");
+		&mov(&DWP(4,"edx","",0),$R);
+		}
+	else
+		{
+		&comment("");
+		&comment("Fixup");
+		&rotr($L,3);		# r
+		 &mov("eax",&wparam(0));
+		&rotr($R,3);		# l
+		 &mov(&DWP(0,"eax","",0),$L);
+		 &mov(&DWP(4,"eax","",0),$R);
+		}
+
+	&pop("ebp");
+	&pop("ebx");
+	&pop("edi");
+	&pop("esi");
+	&ret();
+
+	&function_end_B($name);
+	}
+
+sub D_ENCRYPT
+	{
+	local($r,$L,$R,$S,$trans,$u,$tmp1,$tmp2,$t,$wp1)=@_;
+
+	 &mov(	$u,		&DWP(&n2a($S*4),$tmp2,"",0));
+	&xor(	$tmp1,		$tmp1);
+	 &mov(	$t,		&DWP(&n2a(($S+1)*4),$tmp2,"",0));
+	&xor(	$u,		$R);
+	&xor(	$tmp2,		$tmp2);
+	 &xor(	$t,		$R);
+	&and(	$u,		"0xfcfcfcfc"	);
+	 &and(	$t,		"0xcfcfcfcf"	);
+	&movb(	&LB($tmp1),	&LB($u)	);
+	 &movb(	&LB($tmp2),	&HB($u)	);
+	&rotr(	$t,		4		);
+	&xor(	$L,		&DWP("     ",$trans,$tmp1,0));
+	 &movb(	&LB($tmp1),	&LB($t)	);
+	 &xor(	$L,		&DWP("0x200",$trans,$tmp2,0));
+	 &movb(	&LB($tmp2),	&HB($t)	);
+	&shr(	$u,		16);
+	 &xor(	$L,		&DWP("0x100",$trans,$tmp1,0));
+	 &movb(	&LB($tmp1),	&HB($u)	);
+	&shr(	$t,		16);
+	 &xor(	$L,		&DWP("0x300",$trans,$tmp2,0));
+	&movb(	&LB($tmp2),	&HB($t)	);
+	 &and(	$u,		"0xff"	);
+	&and(	$t,		"0xff"	);
+	 &xor(	$L,		&DWP("0x600",$trans,$tmp1,0));
+	 &xor(	$L,		&DWP("0x700",$trans,$tmp2,0));
+	&mov(	$tmp2,		$wp1	);
+	 &xor(	$L,		&DWP("0x400",$trans,$u,0));
+	 &xor(	$L,		&DWP("0x500",$trans,$t,0));
+	}
+
+sub n2a
+	{
+	sprintf("%d",$_[0]);
+	}
+
+# now has a side affect of rotating $a by $shift
+sub R_PERM_OP
+	{
+	local($a,$b,$tt,$shift,$mask,$last)=@_;
+
+	&rotl(	$a,		$shift		) if ($shift != 0);
+	&mov(	$tt,		$a		);
+	&xor(	$a,		$b		);
+	&and(	$a,		$mask		);
+	# This can never succeed, and besides it is difficult to see what the
+	# idea was - Ben 13 Feb 99
+	if (!$last eq $b)
+		{
+		&xor(	$b,		$a		);
+		&xor(	$tt,		$a		);
+		}
+	else
+		{
+		&xor(	$tt,		$a		);
+		&xor(	$b,		$a		);
+		}
+	&comment("");
+	}
+
+sub IP_new
+	{
+	local($l,$r,$tt,$lr)=@_;
+
+	&R_PERM_OP($l,$r,$tt, 4,"0xf0f0f0f0",$l);
+	&R_PERM_OP($r,$tt,$l,20,"0xfff0000f",$l);
+	&R_PERM_OP($l,$tt,$r,14,"0x33333333",$r);
+	&R_PERM_OP($tt,$r,$l,22,"0x03fc03fc",$r);
+	&R_PERM_OP($l,$r,$tt, 9,"0xaaaaaaaa",$r);
+	
+	if ($lr != 3)
+		{
+		if (($lr-3) < 0)
+			{ &rotr($tt,	3-$lr); }
+		else	{ &rotl($tt,	$lr-3); }
+		}
+	if ($lr != 2)
+		{
+		if (($lr-2) < 0)
+			{ &rotr($r,	2-$lr); }
+		else	{ &rotl($r,	$lr-2); }
+		}
+	}
+
+sub FP_new
+	{
+	local($l,$r,$tt,$lr)=@_;
+
+	if ($lr != 2)
+		{
+		if (($lr-2) < 0)
+			{ &rotl($r,	2-$lr); }
+		else	{ &rotr($r,	$lr-2); }
+		}
+	if ($lr != 3)
+		{
+		if (($lr-3) < 0)
+			{ &rotl($l,	3-$lr); }
+		else	{ &rotr($l,	$lr-3); }
+		}
+
+	&R_PERM_OP($l,$r,$tt, 0,"0xaaaaaaaa",$r);
+	&R_PERM_OP($tt,$r,$l,23,"0x03fc03fc",$r);
+	&R_PERM_OP($l,$r,$tt,10,"0x33333333",$l);
+	&R_PERM_OP($r,$tt,$l,18,"0xfff0000f",$l);
+	&R_PERM_OP($l,$tt,$r,12,"0xf0f0f0f0",$r);
+	&rotr($tt	, 4);
+	}
+
+sub DES_SPtrans
+	{
+	&set_label("DES_SPtrans",64);
+	&set_label("des_sptrans");
+	&data_word(0x02080800, 0x00080000, 0x02000002, 0x02080802);
+	&data_word(0x02000000, 0x00080802, 0x00080002, 0x02000002);
+	&data_word(0x00080802, 0x02080800, 0x02080000, 0x00000802);
+	&data_word(0x02000802, 0x02000000, 0x00000000, 0x00080002);
+	&data_word(0x00080000, 0x00000002, 0x02000800, 0x00080800);
+	&data_word(0x02080802, 0x02080000, 0x00000802, 0x02000800);
+	&data_word(0x00000002, 0x00000800, 0x00080800, 0x02080002);
+	&data_word(0x00000800, 0x02000802, 0x02080002, 0x00000000);
+	&data_word(0x00000000, 0x02080802, 0x02000800, 0x00080002);
+	&data_word(0x02080800, 0x00080000, 0x00000802, 0x02000800);
+	&data_word(0x02080002, 0x00000800, 0x00080800, 0x02000002);
+	&data_word(0x00080802, 0x00000002, 0x02000002, 0x02080000);
+	&data_word(0x02080802, 0x00080800, 0x02080000, 0x02000802);
+	&data_word(0x02000000, 0x00000802, 0x00080002, 0x00000000);
+	&data_word(0x00080000, 0x02000000, 0x02000802, 0x02080800);
+	&data_word(0x00000002, 0x02080002, 0x00000800, 0x00080802);
+	# nibble 1
+	&data_word(0x40108010, 0x00000000, 0x00108000, 0x40100000);
+	&data_word(0x40000010, 0x00008010, 0x40008000, 0x00108000);
+	&data_word(0x00008000, 0x40100010, 0x00000010, 0x40008000);
+	&data_word(0x00100010, 0x40108000, 0x40100000, 0x00000010);
+	&data_word(0x00100000, 0x40008010, 0x40100010, 0x00008000);
+	&data_word(0x00108010, 0x40000000, 0x00000000, 0x00100010);
+	&data_word(0x40008010, 0x00108010, 0x40108000, 0x40000010);
+	&data_word(0x40000000, 0x00100000, 0x00008010, 0x40108010);
+	&data_word(0x00100010, 0x40108000, 0x40008000, 0x00108010);
+	&data_word(0x40108010, 0x00100010, 0x40000010, 0x00000000);
+	&data_word(0x40000000, 0x00008010, 0x00100000, 0x40100010);
+	&data_word(0x00008000, 0x40000000, 0x00108010, 0x40008010);
+	&data_word(0x40108000, 0x00008000, 0x00000000, 0x40000010);
+	&data_word(0x00000010, 0x40108010, 0x00108000, 0x40100000);
+	&data_word(0x40100010, 0x00100000, 0x00008010, 0x40008000);
+	&data_word(0x40008010, 0x00000010, 0x40100000, 0x00108000);
+	# nibble 2
+	&data_word(0x04000001, 0x04040100, 0x00000100, 0x04000101);
+	&data_word(0x00040001, 0x04000000, 0x04000101, 0x00040100);
+	&data_word(0x04000100, 0x00040000, 0x04040000, 0x00000001);
+	&data_word(0x04040101, 0x00000101, 0x00000001, 0x04040001);
+	&data_word(0x00000000, 0x00040001, 0x04040100, 0x00000100);
+	&data_word(0x00000101, 0x04040101, 0x00040000, 0x04000001);
+	&data_word(0x04040001, 0x04000100, 0x00040101, 0x04040000);
+	&data_word(0x00040100, 0x00000000, 0x04000000, 0x00040101);
+	&data_word(0x04040100, 0x00000100, 0x00000001, 0x00040000);
+	&data_word(0x00000101, 0x00040001, 0x04040000, 0x04000101);
+	&data_word(0x00000000, 0x04040100, 0x00040100, 0x04040001);
+	&data_word(0x00040001, 0x04000000, 0x04040101, 0x00000001);
+	&data_word(0x00040101, 0x04000001, 0x04000000, 0x04040101);
+	&data_word(0x00040000, 0x04000100, 0x04000101, 0x00040100);
+	&data_word(0x04000100, 0x00000000, 0x04040001, 0x00000101);
+	&data_word(0x04000001, 0x00040101, 0x00000100, 0x04040000);
+	# nibble 3
+	&data_word(0x00401008, 0x10001000, 0x00000008, 0x10401008);
+	&data_word(0x00000000, 0x10400000, 0x10001008, 0x00400008);
+	&data_word(0x10401000, 0x10000008, 0x10000000, 0x00001008);
+	&data_word(0x10000008, 0x00401008, 0x00400000, 0x10000000);
+	&data_word(0x10400008, 0x00401000, 0x00001000, 0x00000008);
+	&data_word(0x00401000, 0x10001008, 0x10400000, 0x00001000);
+	&data_word(0x00001008, 0x00000000, 0x00400008, 0x10401000);
+	&data_word(0x10001000, 0x10400008, 0x10401008, 0x00400000);
+	&data_word(0x10400008, 0x00001008, 0x00400000, 0x10000008);
+	&data_word(0x00401000, 0x10001000, 0x00000008, 0x10400000);
+	&data_word(0x10001008, 0x00000000, 0x00001000, 0x00400008);
+	&data_word(0x00000000, 0x10400008, 0x10401000, 0x00001000);
+	&data_word(0x10000000, 0x10401008, 0x00401008, 0x00400000);
+	&data_word(0x10401008, 0x00000008, 0x10001000, 0x00401008);
+	&data_word(0x00400008, 0x00401000, 0x10400000, 0x10001008);
+	&data_word(0x00001008, 0x10000000, 0x10000008, 0x10401000);
+	# nibble 4
+	&data_word(0x08000000, 0x00010000, 0x00000400, 0x08010420);
+	&data_word(0x08010020, 0x08000400, 0x00010420, 0x08010000);
+	&data_word(0x00010000, 0x00000020, 0x08000020, 0x00010400);
+	&data_word(0x08000420, 0x08010020, 0x08010400, 0x00000000);
+	&data_word(0x00010400, 0x08000000, 0x00010020, 0x00000420);
+	&data_word(0x08000400, 0x00010420, 0x00000000, 0x08000020);
+	&data_word(0x00000020, 0x08000420, 0x08010420, 0x00010020);
+	&data_word(0x08010000, 0x00000400, 0x00000420, 0x08010400);
+	&data_word(0x08010400, 0x08000420, 0x00010020, 0x08010000);
+	&data_word(0x00010000, 0x00000020, 0x08000020, 0x08000400);
+	&data_word(0x08000000, 0x00010400, 0x08010420, 0x00000000);
+	&data_word(0x00010420, 0x08000000, 0x00000400, 0x00010020);
+	&data_word(0x08000420, 0x00000400, 0x00000000, 0x08010420);
+	&data_word(0x08010020, 0x08010400, 0x00000420, 0x00010000);
+	&data_word(0x00010400, 0x08010020, 0x08000400, 0x00000420);
+	&data_word(0x00000020, 0x00010420, 0x08010000, 0x08000020);
+	# nibble 5
+	&data_word(0x80000040, 0x00200040, 0x00000000, 0x80202000);
+	&data_word(0x00200040, 0x00002000, 0x80002040, 0x00200000);
+	&data_word(0x00002040, 0x80202040, 0x00202000, 0x80000000);
+	&data_word(0x80002000, 0x80000040, 0x80200000, 0x00202040);
+	&data_word(0x00200000, 0x80002040, 0x80200040, 0x00000000);
+	&data_word(0x00002000, 0x00000040, 0x80202000, 0x80200040);
+	&data_word(0x80202040, 0x80200000, 0x80000000, 0x00002040);
+	&data_word(0x00000040, 0x00202000, 0x00202040, 0x80002000);
+	&data_word(0x00002040, 0x80000000, 0x80002000, 0x00202040);
+	&data_word(0x80202000, 0x00200040, 0x00000000, 0x80002000);
+	&data_word(0x80000000, 0x00002000, 0x80200040, 0x00200000);
+	&data_word(0x00200040, 0x80202040, 0x00202000, 0x00000040);
+	&data_word(0x80202040, 0x00202000, 0x00200000, 0x80002040);
+	&data_word(0x80000040, 0x80200000, 0x00202040, 0x00000000);
+	&data_word(0x00002000, 0x80000040, 0x80002040, 0x80202000);
+	&data_word(0x80200000, 0x00002040, 0x00000040, 0x80200040);
+	# nibble 6
+	&data_word(0x00004000, 0x00000200, 0x01000200, 0x01000004);
+	&data_word(0x01004204, 0x00004004, 0x00004200, 0x00000000);
+	&data_word(0x01000000, 0x01000204, 0x00000204, 0x01004000);
+	&data_word(0x00000004, 0x01004200, 0x01004000, 0x00000204);
+	&data_word(0x01000204, 0x00004000, 0x00004004, 0x01004204);
+	&data_word(0x00000000, 0x01000200, 0x01000004, 0x00004200);
+	&data_word(0x01004004, 0x00004204, 0x01004200, 0x00000004);
+	&data_word(0x00004204, 0x01004004, 0x00000200, 0x01000000);
+	&data_word(0x00004204, 0x01004000, 0x01004004, 0x00000204);
+	&data_word(0x00004000, 0x00000200, 0x01000000, 0x01004004);
+	&data_word(0x01000204, 0x00004204, 0x00004200, 0x00000000);
+	&data_word(0x00000200, 0x01000004, 0x00000004, 0x01000200);
+	&data_word(0x00000000, 0x01000204, 0x01000200, 0x00004200);
+	&data_word(0x00000204, 0x00004000, 0x01004204, 0x01000000);
+	&data_word(0x01004200, 0x00000004, 0x00004004, 0x01004204);
+	&data_word(0x01000004, 0x01004200, 0x01004000, 0x00004004);
+	# nibble 7
+	&data_word(0x20800080, 0x20820000, 0x00020080, 0x00000000);
+	&data_word(0x20020000, 0x00800080, 0x20800000, 0x20820080);
+	&data_word(0x00000080, 0x20000000, 0x00820000, 0x00020080);
+	&data_word(0x00820080, 0x20020080, 0x20000080, 0x20800000);
+	&data_word(0x00020000, 0x00820080, 0x00800080, 0x20020000);
+	&data_word(0x20820080, 0x20000080, 0x00000000, 0x00820000);
+	&data_word(0x20000000, 0x00800000, 0x20020080, 0x20800080);
+	&data_word(0x00800000, 0x00020000, 0x20820000, 0x00000080);
+	&data_word(0x00800000, 0x00020000, 0x20000080, 0x20820080);
+	&data_word(0x00020080, 0x20000000, 0x00000000, 0x00820000);
+	&data_word(0x20800080, 0x20020080, 0x20020000, 0x00800080);
+	&data_word(0x20820000, 0x00000080, 0x00800080, 0x20020000);
+	&data_word(0x20820080, 0x00800000, 0x20800000, 0x20000080);
+	&data_word(0x00820000, 0x00020080, 0x20020080, 0x20800000);
+	&data_word(0x00000080, 0x20820000, 0x00820080, 0x00000000);
+	&data_word(0x20000000, 0x20800080, 0x00020000, 0x00820080);
+	}
diff --git a/crypto/des/asm/des_enc.m4 b/crypto/des/asm/des_enc.m4
new file mode 100644
index 000000000..2d794d337
--- /dev/null
+++ b/crypto/des/asm/des_enc.m4
@@ -0,0 +1,1972 @@
+! Copyright 2000-2016 The OpenSSL Project Authors. All Rights Reserved.
+!
+! Licensed under the OpenSSL license (the "License").  You may not use
+! this file except in compliance with the License.  You can obtain a copy
+! in the file LICENSE in the source distribution or at
+! https://www.openssl.org/source/license.html
+!
+!  To expand the m4 macros: m4 -B 8192 des_enc.m4 > des_enc.S
+!
+!  Global registers 1 to 5 are used. This is the same as done by the
+!  cc compiler. The UltraSPARC load/store little endian feature is used.
+!
+!  Instruction grouping often refers to one CPU cycle.
+!
+!  Assemble through gcc: gcc -c -mcpu=ultrasparc -o des_enc.o des_enc.S
+!
+!  Assemble through cc:  cc -c -xarch=v8plusa -o des_enc.o des_enc.S
+!
+!  Performance improvement according to './apps/openssl speed des'
+!
+!	32-bit build:
+!		23%  faster than cc-5.2 -xarch=v8plus -xO5
+!		115% faster than gcc-3.2.1 -m32 -mcpu=ultrasparc -O5
+!	64-bit build:
+!		50%  faster than cc-5.2 -xarch=v9 -xO5
+!		100% faster than gcc-3.2.1 -m64 -mcpu=ultrasparc -O5
+!
+
+.ident "des_enc.m4 2.1"
+.file  "des_enc-sparc.S"
+
+#include 
+
+#ifdef OPENSSL_FIPSCANISTER
+#include 
+#endif
+
+#if defined(__SUNPRO_C) && defined(__sparcv9)
+# define ABI64  /* They've said -xarch=v9 at command line */
+#elif defined(__GNUC__) && defined(__arch64__)
+# define ABI64  /* They've said -m64 at command line */
+#endif
+
+#ifdef ABI64
+  .register	%g2,#scratch
+  .register	%g3,#scratch
+# define	FRAME	-192
+# define	BIAS	2047
+# define	LDPTR	ldx
+# define	STPTR	stx
+# define	ARG0	128
+# define	ARGSZ	8
+#else
+# define	FRAME	-96
+# define	BIAS	0
+# define	LDPTR	ld
+# define	STPTR	st
+# define	ARG0	68
+# define	ARGSZ	4
+#endif
+
+#define LOOPS 7
+
+#define global0 %g0
+#define global1 %g1
+#define global2 %g2
+#define global3 %g3
+#define global4 %g4
+#define global5 %g5
+
+#define local0 %l0
+#define local1 %l1
+#define local2 %l2
+#define local3 %l3
+#define local4 %l4
+#define local5 %l5
+#define local7 %l6
+#define local6 %l7
+
+#define in0 %i0
+#define in1 %i1
+#define in2 %i2
+#define in3 %i3
+#define in4 %i4
+#define in5 %i5
+#define in6 %i6
+#define in7 %i7
+
+#define out0 %o0
+#define out1 %o1
+#define out2 %o2
+#define out3 %o3
+#define out4 %o4
+#define out5 %o5
+#define out6 %o6
+#define out7 %o7
+
+#define stub stb
+
+changequote({,})
+
+
+! Macro definitions:
+
+
+! {ip_macro}
+!
+! The logic used in initial and final permutations is the same as in
+! the C code. The permutations are done with a clever shift, xor, and
+! technique.
+!
+! The macro also loads address sbox 1 to 5 to global 1 to 5, address
+! sbox 6 to local6, and addres sbox 8 to out3.
+!
+! Rotates the halfs 3 left to bring the sbox bits in convenient positions.
+!
+! Loads key first round from address in parameter 5 to out0, out1.
+!
+! After the the original LibDES initial permutation, the resulting left
+! is in the variable initially used for right and vice versa. The macro
+! implements the possibility to keep the halfs in the original registers.
+!
+! parameter 1  left
+! parameter 2  right
+! parameter 3  result left (modify in first round)
+! parameter 4  result right (use in first round)
+! parameter 5  key address
+! parameter 6  1/2 for include encryption/decryption
+! parameter 7  1 for move in1 to in3
+! parameter 8  1 for move in3 to in4, 2 for move in4 to in3
+! parameter 9  1 for load ks3 and ks2 to in4 and in3
+
+define(ip_macro, {
+
+! {ip_macro}
+! $1 $2 $4 $3 $5 $6 $7 $8 $9
+
+	ld	[out2+256], local1
+	srl	$2, 4, local4
+
+	xor	local4, $1, local4
+	ifelse($7,1,{mov in1, in3},{nop})
+
+	ld	[out2+260], local2
+	and	local4, local1, local4
+	ifelse($8,1,{mov in3, in4},{})
+	ifelse($8,2,{mov in4, in3},{})
+
+	ld	[out2+280], out4          ! loop counter
+	sll	local4, 4, local1
+	xor	$1, local4, $1
+
+	ld	[out2+264], local3
+	srl	$1, 16, local4
+	xor	$2, local1, $2
+
+	ifelse($9,1,{LDPTR	KS3, in4},{})
+	xor	local4, $2, local4
+	nop	!sethi	%hi(DES_SPtrans), global1 ! sbox addr
+
+	ifelse($9,1,{LDPTR	KS2, in3},{})
+	and	local4, local2, local4
+	nop	!or	global1, %lo(DES_SPtrans), global1   ! sbox addr
+
+	sll	local4, 16, local1
+	xor	$2, local4, $2
+
+	srl	$2, 2, local4
+	xor	$1, local1, $1
+
+	sethi	%hi(16711680), local5
+	xor	local4, $1, local4
+
+	and	local4, local3, local4
+	or	local5, 255, local5
+
+	sll	local4, 2, local2
+	xor	$1, local4, $1
+
+	srl	$1, 8, local4
+	xor	$2, local2, $2
+
+	xor	local4, $2, local4
+	add	global1, 768, global4
+
+	and	local4, local5, local4
+	add	global1, 1024, global5
+
+	ld	[out2+272], local7
+	sll	local4, 8, local1
+	xor	$2, local4, $2
+
+	srl	$2, 1, local4
+	xor	$1, local1, $1
+
+	ld	[$5], out0                ! key 7531
+	xor	local4, $1, local4
+	add	global1, 256, global2
+
+	ld	[$5+4], out1              ! key 8642
+	and	local4, local7, local4
+	add	global1, 512, global3
+
+	sll	local4, 1, local1
+	xor	$1, local4, $1
+
+	sll	$1, 3, local3
+	xor	$2, local1, $2
+
+	sll	$2, 3, local2
+	add	global1, 1280, local6     ! address sbox 8
+
+	srl	$1, 29, local4
+	add	global1, 1792, out3       ! address sbox 8
+
+	srl	$2, 29, local1
+	or	local4, local3, $4
+
+	or	local2, local1, $3
+
+	ifelse($6, 1, {
+
+		ld	[out2+284], local5     ! 0x0000FC00 used in the rounds
+		or	local2, local1, $3
+		xor	$4, out0, local1
+
+		call .des_enc.1
+		and	local1, 252, local1
+
+	},{})
+
+	ifelse($6, 2, {
+
+		ld	[out2+284], local5     ! 0x0000FC00 used in the rounds
+		or	local2, local1, $3
+		xor	$4, out0, local1
+
+		call .des_dec.1
+		and	local1, 252, local1
+
+	},{})
+})
+
+
+! {rounds_macro}
+!
+! The logic used in the DES rounds is the same as in the C code,
+! except that calculations for sbox 1 and sbox 5 begin before
+! the previous round is finished.
+!
+! In each round one half (work) is modified based on key and the
+! other half (use).
+!
+! In this version we do two rounds in a loop repeated 7 times
+! and two rounds separately.
+!
+! One half has the bits for the sboxes in the following positions:
+!
+!	777777xx555555xx333333xx111111xx
+!
+!	88xx666666xx444444xx222222xx8888
+!
+! The bits for each sbox are xor-ed with the key bits for that box.
+! The above xx bits are cleared, and the result used for lookup in
+! the sbox table. Each sbox entry contains the 4 output bits permuted
+! into 32 bits according to the P permutation.
+!
+! In the description of DES, left and right are switched after
+! each round, except after last round. In this code the original
+! left and right are kept in the same register in all rounds, meaning
+! that after the 16 rounds the result for right is in the register
+! originally used for left.
+!
+! parameter 1  first work (left in first round)
+! parameter 2  first use (right in first round)
+! parameter 3  enc/dec  1/-1
+! parameter 4  loop label
+! parameter 5  key address register
+! parameter 6  optional address for key next encryption/decryption
+! parameter 7  not empty for include retl
+!
+! also compares in2 to 8
+
+define(rounds_macro, {
+
+! {rounds_macro}
+! $1 $2 $3 $4 $5 $6 $7 $8 $9
+
+	xor	$2, out0, local1
+
+	ld	[out2+284], local5        ! 0x0000FC00
+	ba	$4
+	and	local1, 252, local1
+
+	.align 32
+
+$4:
+	! local6 is address sbox 6
+	! out3   is address sbox 8
+	! out4   is loop counter
+
+	ld	[global1+local1], local1
+	xor	$2, out1, out1            ! 8642
+	xor	$2, out0, out0            ! 7531
+	! fmovs	%f0, %f0                  ! fxor used for alignment
+
+	srl	out1, 4, local0           ! rotate 4 right
+	and	out0, local5, local3      ! 3
+	! fmovs	%f0, %f0
+
+	ld	[$5+$3*8], local7         ! key 7531 next round
+	srl	local3, 8, local3         ! 3
+	and	local0, 252, local2       ! 2
+	! fmovs	%f0, %f0
+
+	ld	[global3+local3],local3   ! 3
+	sll	out1, 28, out1            ! rotate
+	xor	$1, local1, $1            ! 1 finished, local1 now sbox 7
+
+	ld	[global2+local2], local2  ! 2 
+	srl	out0, 24, local1          ! 7
+	or	out1, local0, out1        ! rotate
+
+	ldub	[out2+local1], local1     ! 7 (and 0xFC)
+	srl	out1, 24, local0          ! 8
+	and	out1, local5, local4      ! 4
+
+	ldub	[out2+local0], local0     ! 8 (and 0xFC)
+	srl	local4, 8, local4         ! 4
+	xor	$1, local2, $1            ! 2 finished local2 now sbox 6
+
+	ld	[global4+local4],local4   ! 4
+	srl	out1, 16, local2          ! 6
+	xor	$1, local3, $1            ! 3 finished local3 now sbox 5
+
+	ld	[out3+local0],local0      ! 8
+	and	local2, 252, local2       ! 6
+	add	global1, 1536, local5     ! address sbox 7
+
+	ld	[local6+local2], local2   ! 6
+	srl	out0, 16, local3          ! 5
+	xor	$1, local4, $1            ! 4 finished
+
+	ld	[local5+local1],local1    ! 7
+	and	local3, 252, local3       ! 5
+	xor	$1, local0, $1            ! 8 finished
+
+	ld	[global5+local3],local3   ! 5
+	xor	$1, local2, $1            ! 6 finished
+	subcc	out4, 1, out4
+
+	ld	[$5+$3*8+4], out0         ! key 8642 next round
+	xor	$1, local7, local2        ! sbox 5 next round
+	xor	$1, local1, $1            ! 7 finished
+
+	srl	local2, 16, local2        ! sbox 5 next round
+	xor	$1, local3, $1            ! 5 finished
+
+	ld	[$5+$3*16+4], out1        ! key 8642 next round again
+	and	local2, 252, local2       ! sbox5 next round
+! next round
+	xor	$1, local7, local7        ! 7531
+
+	ld	[global5+local2], local2  ! 5
+	srl	local7, 24, local3        ! 7
+	xor	$1, out0, out0            ! 8642
+
+	ldub	[out2+local3], local3     ! 7 (and 0xFC)
+	srl	out0, 4, local0           ! rotate 4 right
+	and	local7, 252, local1       ! 1
+
+	sll	out0, 28, out0            ! rotate
+	xor	$2, local2, $2            ! 5 finished local2 used
+
+	srl	local0, 8, local4         ! 4
+	and	local0, 252, local2       ! 2
+	ld	[local5+local3], local3   ! 7
+
+	srl	local0, 16, local5        ! 6
+	or	out0, local0, out0        ! rotate
+	ld	[global2+local2], local2  ! 2
+
+	srl	out0, 24, local0
+	ld	[$5+$3*16], out0          ! key 7531 next round
+	and	local4, 252, local4	  ! 4
+
+	and	local5, 252, local5       ! 6
+	ld	[global4+local4], local4  ! 4
+	xor	$2, local3, $2            ! 7 finished local3 used
+
+	and	local0, 252, local0       ! 8
+	ld	[local6+local5], local5   ! 6
+	xor	$2, local2, $2            ! 2 finished local2 now sbox 3
+
+	srl	local7, 8, local2         ! 3 start
+	ld	[out3+local0], local0     ! 8
+	xor	$2, local4, $2            ! 4 finished
+
+	and	local2, 252, local2       ! 3
+	ld	[global1+local1], local1  ! 1
+	xor	$2, local5, $2            ! 6 finished local5 used
+
+	ld	[global3+local2], local2  ! 3
+	xor	$2, local0, $2            ! 8 finished
+	add	$5, $3*16, $5             ! enc add 8, dec add -8 to key pointer
+
+	ld	[out2+284], local5        ! 0x0000FC00
+	xor	$2, out0, local4          ! sbox 1 next round
+	xor	$2, local1, $2            ! 1 finished
+
+	xor	$2, local2, $2            ! 3 finished
+	bne	$4
+	and	local4, 252, local1       ! sbox 1 next round
+
+! two rounds more:
+
+	ld	[global1+local1], local1
+	xor	$2, out1, out1
+	xor	$2, out0, out0
+
+	srl	out1, 4, local0           ! rotate
+	and	out0, local5, local3
+
+	ld	[$5+$3*8], local7         ! key 7531
+	srl	local3, 8, local3
+	and	local0, 252, local2
+
+	ld	[global3+local3],local3
+	sll	out1, 28, out1            ! rotate
+	xor	$1, local1, $1            ! 1 finished, local1 now sbox 7
+
+	ld	[global2+local2], local2
+	srl	out0, 24, local1
+	or	out1, local0, out1        ! rotate
+
+	ldub	[out2+local1], local1
+	srl	out1, 24, local0
+	and	out1, local5, local4
+
+	ldub	[out2+local0], local0
+	srl	local4, 8, local4
+	xor	$1, local2, $1            ! 2 finished local2 now sbox 6
+
+	ld	[global4+local4],local4
+	srl	out1, 16, local2
+	xor	$1, local3, $1            ! 3 finished local3 now sbox 5
+
+	ld	[out3+local0],local0
+	and	local2, 252, local2
+	add	global1, 1536, local5     ! address sbox 7
+
+	ld	[local6+local2], local2
+	srl	out0, 16, local3
+	xor	$1, local4, $1            ! 4 finished
+
+	ld	[local5+local1],local1
+	and	local3, 252, local3
+	xor	$1, local0, $1
+
+	ld	[global5+local3],local3
+	xor	$1, local2, $1            ! 6 finished
+	cmp	in2, 8
+
+	ifelse($6,{}, {}, {ld	[out2+280], out4})  ! loop counter
+	xor	$1, local7, local2        ! sbox 5 next round
+	xor	$1, local1, $1            ! 7 finished
+
+	ld	[$5+$3*8+4], out0
+	srl	local2, 16, local2        ! sbox 5 next round
+	xor	$1, local3, $1            ! 5 finished
+
+	and	local2, 252, local2
+! next round (two rounds more)
+	xor	$1, local7, local7        ! 7531
+
+	ld	[global5+local2], local2
+	srl	local7, 24, local3
+	xor	$1, out0, out0            ! 8642
+
+	ldub	[out2+local3], local3
+	srl	out0, 4, local0           ! rotate
+	and	local7, 252, local1
+
+	sll	out0, 28, out0            ! rotate
+	xor	$2, local2, $2            ! 5 finished local2 used
+
+	srl	local0, 8, local4
+	and	local0, 252, local2
+	ld	[local5+local3], local3
+
+	srl	local0, 16, local5
+	or	out0, local0, out0        ! rotate
+	ld	[global2+local2], local2
+
+	srl	out0, 24, local0
+	ifelse($6,{}, {}, {ld	[$6], out0})   ! key next encryption/decryption
+	and	local4, 252, local4
+
+	and	local5, 252, local5
+	ld	[global4+local4], local4
+	xor	$2, local3, $2            ! 7 finished local3 used
+
+	and	local0, 252, local0
+	ld	[local6+local5], local5
+	xor	$2, local2, $2            ! 2 finished local2 now sbox 3
+
+	srl	local7, 8, local2         ! 3 start
+	ld	[out3+local0], local0
+	xor	$2, local4, $2
+
+	and	local2, 252, local2
+	ld	[global1+local1], local1
+	xor	$2, local5, $2            ! 6 finished local5 used
+
+	ld	[global3+local2], local2
+	srl	$1, 3, local3
+	xor	$2, local0, $2
+
+	ifelse($6,{}, {}, {ld	[$6+4], out1}) ! key next encryption/decryption
+	sll	$1, 29, local4
+	xor	$2, local1, $2
+
+	ifelse($7,{}, {}, {retl})
+	xor	$2, local2, $2
+})
+
+
+! {fp_macro}
+!
+!  parameter 1   right (original left)
+!  parameter 2   left (original right)
+!  parameter 3   1 for optional store to [in0]
+!  parameter 4   1 for load input/output address to local5/7
+!
+!  The final permutation logic switches the halfes, meaning that
+!  left and right ends up the the registers originally used.
+
+define(fp_macro, {
+
+! {fp_macro}
+! $1 $2 $3 $4 $5 $6 $7 $8 $9
+
+	! initially undo the rotate 3 left done after initial permutation
+	! original left is received shifted 3 right and 29 left in local3/4
+
+	sll	$2, 29, local1
+	or	local3, local4, $1
+
+	srl	$2, 3, $2
+	sethi	%hi(0x55555555), local2
+
+	or	$2, local1, $2
+	or	local2, %lo(0x55555555), local2
+
+	srl	$2, 1, local3
+	sethi	%hi(0x00ff00ff), local1
+	xor	local3, $1, local3
+	or	local1, %lo(0x00ff00ff), local1
+	and	local3, local2, local3
+	sethi	%hi(0x33333333), local4
+	sll	local3, 1, local2
+
+	xor	$1, local3, $1
+
+	srl	$1, 8, local3
+	xor	$2, local2, $2
+	xor	local3, $2, local3
+	or	local4, %lo(0x33333333), local4
+	and	local3, local1, local3
+	sethi	%hi(0x0000ffff), local1
+	sll	local3, 8, local2
+
+	xor	$2, local3, $2
+
+	srl	$2, 2, local3
+	xor	$1, local2, $1
+	xor	local3, $1, local3
+	or	local1, %lo(0x0000ffff), local1
+	and	local3, local4, local3
+	sethi	%hi(0x0f0f0f0f), local4
+	sll	local3, 2, local2
+
+	ifelse($4,1, {LDPTR INPUT, local5})
+	xor	$1, local3, $1
+
+	ifelse($4,1, {LDPTR OUTPUT, local7})
+	srl	$1, 16, local3
+	xor	$2, local2, $2
+	xor	local3, $2, local3
+	or	local4, %lo(0x0f0f0f0f), local4
+	and	local3, local1, local3
+	sll	local3, 16, local2
+
+	xor	$2, local3, local1
+
+	srl	local1, 4, local3
+	xor	$1, local2, $1
+	xor	local3, $1, local3
+	and	local3, local4, local3
+	sll	local3, 4, local2
+
+	xor	$1, local3, $1
+
+	! optional store:
+
+	ifelse($3,1, {st $1, [in0]})
+
+	xor	local1, local2, $2
+
+	ifelse($3,1, {st $2, [in0+4]})
+
+})
+
+
+! {fp_ip_macro}
+!
+! Does initial permutation for next block mixed with
+! final permutation for current block.
+!
+! parameter 1   original left
+! parameter 2   original right
+! parameter 3   left ip
+! parameter 4   right ip
+! parameter 5   1: load ks1/ks2 to in3/in4, add 120 to in4
+!                2: mov in4 to in3
+!
+! also adds -8 to length in2 and loads loop counter to out4
+
+define(fp_ip_macro, {
+
+! {fp_ip_macro}
+! $1 $2 $3 $4 $5 $6 $7 $8 $9
+
+	define({temp1},{out4})
+	define({temp2},{local3})
+
+	define({ip1},{local1})
+	define({ip2},{local2})
+	define({ip4},{local4})
+	define({ip5},{local5})
+
+	! $1 in local3, local4
+
+	ld	[out2+256], ip1
+	sll	out5, 29, temp1
+	or	local3, local4, $1
+
+	srl	out5, 3, $2
+	ifelse($5,2,{mov in4, in3})
+
+	ld	[out2+272], ip5
+	srl	$4, 4, local0
+	or	$2, temp1, $2
+
+	srl	$2, 1, temp1
+	xor	temp1, $1, temp1
+
+	and	temp1, ip5, temp1
+	xor	local0, $3, local0
+
+	sll	temp1, 1, temp2
+	xor	$1, temp1, $1
+
+	and	local0, ip1, local0
+	add	in2, -8, in2
+
+	sll	local0, 4, local7
+	xor	$3, local0, $3
+
+	ld	[out2+268], ip4
+	srl	$1, 8, temp1
+	xor	$2, temp2, $2
+	ld	[out2+260], ip2
+	srl	$3, 16, local0
+	xor	$4, local7, $4
+	xor	temp1, $2, temp1
+	xor	local0, $4, local0
+	and	temp1, ip4, temp1
+	and	local0, ip2, local0
+	sll	temp1, 8, temp2
+	xor	$2, temp1, $2
+	sll	local0, 16, local7
+	xor	$4, local0, $4
+
+	srl	$2, 2, temp1
+	xor	$1, temp2, $1
+
+	ld	[out2+264], temp2         ! ip3
+	srl	$4, 2, local0
+	xor	$3, local7, $3
+	xor	temp1, $1, temp1
+	xor	local0, $3, local0
+	and	temp1, temp2, temp1
+	and	local0, temp2, local0
+	sll	temp1, 2, temp2
+	xor	$1, temp1, $1
+	sll	local0, 2, local7
+	xor	$3, local0, $3
+
+	srl	$1, 16, temp1
+	xor	$2, temp2, $2
+	srl	$3, 8, local0
+	xor	$4, local7, $4
+	xor	temp1, $2, temp1
+	xor	local0, $4, local0
+	and	temp1, ip2, temp1
+	and	local0, ip4, local0
+	sll	temp1, 16, temp2
+	xor	$2, temp1, local4
+	sll	local0, 8, local7
+	xor	$4, local0, $4
+
+	srl	$4, 1, local0
+	xor	$3, local7, $3
+
+	srl	local4, 4, temp1
+	xor	local0, $3, local0
+
+	xor	$1, temp2, $1
+	and	local0, ip5, local0
+
+	sll	local0, 1, local7
+	xor	temp1, $1, temp1
+
+	xor	$3, local0, $3
+	xor	$4, local7, $4
+
+	sll	$3, 3, local5
+	and	temp1, ip1, temp1
+
+	sll	temp1, 4, temp2
+	xor	$1, temp1, $1
+
+	ifelse($5,1,{LDPTR	KS2, in4})
+	sll	$4, 3, local2
+	xor	local4, temp2, $2
+
+	! reload since used as temporar:
+
+	ld	[out2+280], out4          ! loop counter
+
+	srl	$3, 29, local0
+	ifelse($5,1,{add in4, 120, in4})
+
+	ifelse($5,1,{LDPTR	KS1, in3})
+	srl	$4, 29, local7
+
+	or	local0, local5, $4
+	or	local2, local7, $3
+
+})
+
+
+
+! {load_little_endian}
+!
+! parameter 1  address
+! parameter 2  destination left
+! parameter 3  destination right
+! parameter 4  temporar
+! parameter 5  label
+
+define(load_little_endian, {
+
+! {load_little_endian}
+! $1 $2 $3 $4 $5 $6 $7 $8 $9
+
+	! first in memory to rightmost in register
+
+$5:
+	ldub	[$1+3], $2
+
+	ldub	[$1+2], $4
+	sll	$2, 8, $2
+	or	$2, $4, $2
+
+	ldub	[$1+1], $4
+	sll	$2, 8, $2
+	or	$2, $4, $2
+
+	ldub	[$1+0], $4
+	sll	$2, 8, $2
+	or	$2, $4, $2
+
+
+	ldub	[$1+3+4], $3
+
+	ldub	[$1+2+4], $4
+	sll	$3, 8, $3
+	or	$3, $4, $3
+
+	ldub	[$1+1+4], $4
+	sll	$3, 8, $3
+	or	$3, $4, $3
+
+	ldub	[$1+0+4], $4
+	sll	$3, 8, $3
+	or	$3, $4, $3
+$5a:
+
+})
+
+
+! {load_little_endian_inc}
+!
+! parameter 1  address
+! parameter 2  destination left
+! parameter 3  destination right
+! parameter 4  temporar
+! parameter 4  label
+!
+! adds 8 to address
+
+define(load_little_endian_inc, {
+
+! {load_little_endian_inc}
+! $1 $2 $3 $4 $5 $6 $7 $8 $9
+
+	! first in memory to rightmost in register
+
+$5:
+	ldub	[$1+3], $2
+
+	ldub	[$1+2], $4
+	sll	$2, 8, $2
+	or	$2, $4, $2
+
+	ldub	[$1+1], $4
+	sll	$2, 8, $2
+	or	$2, $4, $2
+
+	ldub	[$1+0], $4
+	sll	$2, 8, $2
+	or	$2, $4, $2
+
+	ldub	[$1+3+4], $3
+	add	$1, 8, $1
+
+	ldub	[$1+2+4-8], $4
+	sll	$3, 8, $3
+	or	$3, $4, $3
+
+	ldub	[$1+1+4-8], $4
+	sll	$3, 8, $3
+	or	$3, $4, $3
+
+	ldub	[$1+0+4-8], $4
+	sll	$3, 8, $3
+	or	$3, $4, $3
+$5a:
+
+})
+
+
+! {load_n_bytes}
+!
+! Loads 1 to 7 bytes little endian
+! Remaining bytes are zeroed.
+!
+! parameter 1  address
+! parameter 2  length
+! parameter 3  destination register left
+! parameter 4  destination register right
+! parameter 5  temp
+! parameter 6  temp2
+! parameter 7  label
+! parameter 8  return label
+
+define(load_n_bytes, {
+
+! {load_n_bytes}
+! $1 $2 $5 $6 $7 $8 $7 $8 $9
+
+$7.0:	call	.+8
+	sll	$2, 2, $6
+
+	add	%o7,$7.jmp.table-$7.0,$5
+
+	add	$5, $6, $5
+	mov	0, $4
+
+	ld	[$5], $5
+
+	jmp	%o7+$5
+	mov	0, $3
+
+$7.7:
+	ldub	[$1+6], $5
+	sll	$5, 16, $5
+	or	$3, $5, $3
+$7.6:
+	ldub	[$1+5], $5
+	sll	$5, 8, $5
+	or	$3, $5, $3
+$7.5:
+	ldub	[$1+4], $5
+	or	$3, $5, $3
+$7.4:
+	ldub	[$1+3], $5
+	sll	$5, 24, $5
+	or	$4, $5, $4
+$7.3:
+	ldub	[$1+2], $5
+	sll	$5, 16, $5
+	or	$4, $5, $4
+$7.2:
+	ldub	[$1+1], $5
+	sll	$5, 8, $5
+	or	$4, $5, $4
+$7.1:
+	ldub	[$1+0], $5
+	ba	$8
+	or	$4, $5, $4
+
+	.align 4
+
+$7.jmp.table:
+	.word	0
+	.word	$7.1-$7.0
+	.word	$7.2-$7.0
+	.word	$7.3-$7.0
+	.word	$7.4-$7.0
+	.word	$7.5-$7.0
+	.word	$7.6-$7.0
+	.word	$7.7-$7.0
+})
+
+
+! {store_little_endian}
+!
+! parameter 1  address
+! parameter 2  source left
+! parameter 3  source right
+! parameter 4  temporar
+
+define(store_little_endian, {
+
+! {store_little_endian}
+! $1 $2 $3 $4 $5 $6 $7 $8 $9
+
+	! rightmost in register to first in memory
+
+$5:
+	and	$2, 255, $4
+	stub	$4, [$1+0]
+
+	srl	$2, 8, $4
+	and	$4, 255, $4
+	stub	$4, [$1+1]
+
+	srl	$2, 16, $4
+	and	$4, 255, $4
+	stub	$4, [$1+2]
+
+	srl	$2, 24, $4
+	stub	$4, [$1+3]
+
+
+	and	$3, 255, $4
+	stub	$4, [$1+0+4]
+
+	srl	$3, 8, $4
+	and	$4, 255, $4
+	stub	$4, [$1+1+4]
+
+	srl	$3, 16, $4
+	and	$4, 255, $4
+	stub	$4, [$1+2+4]
+
+	srl	$3, 24, $4
+	stub	$4, [$1+3+4]
+
+$5a:
+
+})
+
+
+! {store_n_bytes}
+!
+! Stores 1 to 7 bytes little endian
+!
+! parameter 1  address
+! parameter 2  length
+! parameter 3  source register left
+! parameter 4  source register right
+! parameter 5  temp
+! parameter 6  temp2
+! parameter 7  label
+! parameter 8  return label
+
+define(store_n_bytes, {
+
+! {store_n_bytes}
+! $1 $2 $5 $6 $7 $8 $7 $8 $9
+
+$7.0:	call	.+8
+	sll	$2, 2, $6
+
+	add	%o7,$7.jmp.table-$7.0,$5
+
+	add	$5, $6, $5
+
+	ld	[$5], $5
+
+	jmp	%o7+$5
+	nop
+
+$7.7:
+	srl	$3, 16, $5
+	and	$5, 0xff, $5
+	stub	$5, [$1+6]
+$7.6:
+	srl	$3, 8, $5
+	and	$5, 0xff, $5
+	stub	$5, [$1+5]
+$7.5:
+	and	$3, 0xff, $5
+	stub	$5, [$1+4]
+$7.4:
+	srl	$4, 24, $5
+	stub	$5, [$1+3]
+$7.3:
+	srl	$4, 16, $5
+	and	$5, 0xff, $5
+	stub	$5, [$1+2]
+$7.2:
+	srl	$4, 8, $5
+	and	$5, 0xff, $5
+	stub	$5, [$1+1]
+$7.1:
+	and	$4, 0xff, $5
+
+
+	ba	$8
+	stub	$5, [$1]
+
+	.align 4
+
+$7.jmp.table:
+
+	.word	0
+	.word	$7.1-$7.0
+	.word	$7.2-$7.0
+	.word	$7.3-$7.0
+	.word	$7.4-$7.0
+	.word	$7.5-$7.0
+	.word	$7.6-$7.0
+	.word	$7.7-$7.0
+})
+
+
+define(testvalue,{1})
+
+define(register_init, {
+
+! For test purposes:
+
+	sethi	%hi(testvalue), local0
+	or	local0, %lo(testvalue), local0
+
+	ifelse($1,{},{}, {mov	local0, $1})
+	ifelse($2,{},{}, {mov	local0, $2})
+	ifelse($3,{},{}, {mov	local0, $3})
+	ifelse($4,{},{}, {mov	local0, $4})
+	ifelse($5,{},{}, {mov	local0, $5})
+	ifelse($6,{},{}, {mov	local0, $6})
+	ifelse($7,{},{}, {mov	local0, $7})
+	ifelse($8,{},{}, {mov	local0, $8})
+
+	mov	local0, local1
+	mov	local0, local2
+	mov	local0, local3
+	mov	local0, local4
+	mov	local0, local5
+	mov	local0, local7
+	mov	local0, local6
+	mov	local0, out0
+	mov	local0, out1
+	mov	local0, out2
+	mov	local0, out3
+	mov	local0, out4
+	mov	local0, out5
+	mov	local0, global1
+	mov	local0, global2
+	mov	local0, global3
+	mov	local0, global4
+	mov	local0, global5
+
+})
+
+.section	".text"
+
+	.align 32
+
+.des_enc:
+
+	! key address in3
+	! loads key next encryption/decryption first round from [in4]
+
+	rounds_macro(in5, out5, 1, .des_enc.1, in3, in4, retl)
+
+
+	.align 32
+
+.des_dec:
+
+	! implemented with out5 as first parameter to avoid
+	! register exchange in ede modes
+
+	! key address in4
+	! loads key next encryption/decryption first round from [in3]
+
+	rounds_macro(out5, in5, -1, .des_dec.1, in4, in3, retl)
+
+
+
+! void DES_encrypt1(data, ks, enc)
+! *******************************
+
+	.align 32
+	.global DES_encrypt1
+	.type	 DES_encrypt1,#function
+
+DES_encrypt1:
+
+	save	%sp, FRAME, %sp
+
+	sethi	%hi(.PIC.DES_SPtrans-1f),global1
+	or	global1,%lo(.PIC.DES_SPtrans-1f),global1
+1:	call	.+8
+	add	%o7,global1,global1
+	sub	global1,.PIC.DES_SPtrans-.des_and,out2
+
+	ld	[in0], in5                ! left
+	cmp	in2, 0                    ! enc
+
+	be	.encrypt.dec
+	ld	[in0+4], out5             ! right
+
+	! parameter 6  1/2 for include encryption/decryption
+	! parameter 7  1 for move in1 to in3
+	! parameter 8  1 for move in3 to in4, 2 for move in4 to in3
+
+	ip_macro(in5, out5, in5, out5, in3, 0, 1, 1)
+
+	rounds_macro(in5, out5, 1, .des_encrypt1.1, in3, in4) ! in4 not used
+
+	fp_macro(in5, out5, 1)            ! 1 for store to [in0]
+
+	ret
+	restore
+
+.encrypt.dec:
+
+	add	in1, 120, in3             ! use last subkey for first round
+
+	! parameter 6  1/2 for include encryption/decryption
+	! parameter 7  1 for move in1 to in3
+	! parameter 8  1 for move in3 to in4, 2 for move in4 to in3
+
+	ip_macro(in5, out5, out5, in5, in4, 2, 0, 1) ! include dec,  ks in4
+
+	fp_macro(out5, in5, 1)            ! 1 for store to [in0]
+
+	ret
+	restore
+
+.DES_encrypt1.end:
+	.size	 DES_encrypt1,.DES_encrypt1.end-DES_encrypt1
+
+
+! void DES_encrypt2(data, ks, enc)
+!*********************************
+
+	! encrypts/decrypts without initial/final permutation
+
+	.align 32
+	.global DES_encrypt2
+	.type	 DES_encrypt2,#function
+
+DES_encrypt2:
+
+	save	%sp, FRAME, %sp
+
+	sethi	%hi(.PIC.DES_SPtrans-1f),global1
+	or	global1,%lo(.PIC.DES_SPtrans-1f),global1
+1:	call	.+8
+	add	%o7,global1,global1
+	sub	global1,.PIC.DES_SPtrans-.des_and,out2
+
+	! Set sbox address 1 to 6 and rotate halfs 3 left
+	! Errors caught by destest? Yes. Still? *NO*
+
+	!sethi	%hi(DES_SPtrans), global1 ! address sbox 1
+
+	!or	global1, %lo(DES_SPtrans), global1  ! sbox 1
+
+	add	global1, 256, global2     ! sbox 2
+	add	global1, 512, global3     ! sbox 3
+
+	ld	[in0], out5               ! right
+	add	global1, 768, global4     ! sbox 4
+	add	global1, 1024, global5    ! sbox 5
+
+	ld	[in0+4], in5              ! left
+	add	global1, 1280, local6     ! sbox 6
+	add	global1, 1792, out3       ! sbox 8
+
+	! rotate
+
+	sll	in5, 3, local5
+	mov	in1, in3                  ! key address to in3
+
+	sll	out5, 3, local7
+	srl	in5, 29, in5
+
+	srl	out5, 29, out5
+	add	in5, local5, in5
+
+	add	out5, local7, out5
+	cmp	in2, 0
+
+	! we use our own stackframe
+
+	be	.encrypt2.dec
+	STPTR	in0, [%sp+BIAS+ARG0+0*ARGSZ]
+
+	ld	[in3], out0               ! key 7531 first round
+	mov	LOOPS, out4               ! loop counter
+
+	ld	[in3+4], out1             ! key 8642 first round
+	sethi	%hi(0x0000FC00), local5
+
+	call .des_enc
+	mov	in3, in4
+
+	! rotate
+	sll	in5, 29, in0
+	srl	in5, 3, in5
+	sll	out5, 29, in1
+	add	in5, in0, in5
+	srl	out5, 3, out5
+	LDPTR	[%sp+BIAS+ARG0+0*ARGSZ], in0
+	add	out5, in1, out5
+	st	in5, [in0]
+	st	out5, [in0+4]
+
+	ret
+	restore
+
+
+.encrypt2.dec:
+
+	add in3, 120, in4
+
+	ld	[in4], out0               ! key 7531 first round
+	mov	LOOPS, out4               ! loop counter
+
+	ld	[in4+4], out1             ! key 8642 first round
+	sethi	%hi(0x0000FC00), local5
+
+	mov	in5, local1               ! left expected in out5
+	mov	out5, in5
+
+	call .des_dec
+	mov	local1, out5
+
+.encrypt2.finish:
+
+	! rotate
+	sll	in5, 29, in0
+	srl	in5, 3, in5
+	sll	out5, 29, in1
+	add	in5, in0, in5
+	srl	out5, 3, out5
+	LDPTR	[%sp+BIAS+ARG0+0*ARGSZ], in0
+	add	out5, in1, out5
+	st	out5, [in0]
+	st	in5, [in0+4]
+
+	ret
+	restore
+
+.DES_encrypt2.end:
+	.size	 DES_encrypt2, .DES_encrypt2.end-DES_encrypt2
+
+
+! void DES_encrypt3(data, ks1, ks2, ks3)
+! **************************************
+
+	.align 32
+	.global DES_encrypt3
+	.type	 DES_encrypt3,#function
+
+DES_encrypt3:
+
+	save	%sp, FRAME, %sp
+	
+	sethi	%hi(.PIC.DES_SPtrans-1f),global1
+	or	global1,%lo(.PIC.DES_SPtrans-1f),global1
+1:	call	.+8
+	add	%o7,global1,global1
+	sub	global1,.PIC.DES_SPtrans-.des_and,out2
+
+	ld	[in0], in5                ! left
+	add	in2, 120, in4             ! ks2
+
+	ld	[in0+4], out5             ! right
+	mov	in3, in2                  ! save ks3
+
+	! parameter 6  1/2 for include encryption/decryption
+	! parameter 7  1 for mov in1 to in3
+	! parameter 8  1 for mov in3 to in4
+	! parameter 9  1 for load ks3 and ks2 to in4 and in3
+
+	ip_macro(in5, out5, in5, out5, in3, 1, 1, 0, 0)
+
+	call	.des_dec
+	mov	in2, in3                  ! preload ks3
+
+	call	.des_enc
+	nop
+
+	fp_macro(in5, out5, 1)
+
+	ret
+	restore
+
+.DES_encrypt3.end:
+	.size	 DES_encrypt3,.DES_encrypt3.end-DES_encrypt3
+
+
+! void DES_decrypt3(data, ks1, ks2, ks3)
+! **************************************
+
+	.align 32
+	.global DES_decrypt3
+	.type	 DES_decrypt3,#function
+
+DES_decrypt3:
+
+	save	%sp, FRAME, %sp
+	
+	sethi	%hi(.PIC.DES_SPtrans-1f),global1
+	or	global1,%lo(.PIC.DES_SPtrans-1f),global1
+1:	call	.+8
+	add	%o7,global1,global1
+	sub	global1,.PIC.DES_SPtrans-.des_and,out2
+
+	ld	[in0], in5                ! left
+	add	in3, 120, in4             ! ks3
+
+	ld	[in0+4], out5             ! right
+	mov	in2, in3                  ! ks2
+
+	! parameter 6  1/2 for include encryption/decryption
+	! parameter 7  1 for mov in1 to in3
+	! parameter 8  1 for mov in3 to in4
+	! parameter 9  1 for load ks3 and ks2 to in4 and in3
+
+	ip_macro(in5, out5, out5, in5, in4, 2, 0, 0, 0)
+
+	call	.des_enc
+	add	in1, 120, in4             ! preload ks1
+
+	call	.des_dec
+	nop
+
+	fp_macro(out5, in5, 1)
+
+	ret
+	restore
+
+.DES_decrypt3.end:
+	.size	 DES_decrypt3,.DES_decrypt3.end-DES_decrypt3
+
+! void DES_ncbc_encrypt(input, output, length, schedule, ivec, enc)
+! *****************************************************************
+
+
+	.align 32
+	.global DES_ncbc_encrypt
+	.type	 DES_ncbc_encrypt,#function
+
+DES_ncbc_encrypt:
+
+	save	%sp, FRAME, %sp
+	
+	define({INPUT},  { [%sp+BIAS+ARG0+0*ARGSZ] })
+	define({OUTPUT}, { [%sp+BIAS+ARG0+1*ARGSZ] })
+	define({IVEC},   { [%sp+BIAS+ARG0+4*ARGSZ] })
+
+	sethi	%hi(.PIC.DES_SPtrans-1f),global1
+	or	global1,%lo(.PIC.DES_SPtrans-1f),global1
+1:	call	.+8
+	add	%o7,global1,global1
+	sub	global1,.PIC.DES_SPtrans-.des_and,out2
+
+	cmp	in5, 0                    ! enc   
+
+	be	.ncbc.dec
+	STPTR	in4, IVEC
+
+	! addr  left  right  temp  label
+	load_little_endian(in4, in5, out5, local3, .LLE1)  ! iv
+
+	addcc	in2, -8, in2              ! bytes missing when first block done
+
+	bl	.ncbc.enc.seven.or.less
+	mov	in3, in4                  ! schedule
+
+.ncbc.enc.next.block:
+
+	load_little_endian(in0, out4, global4, local3, .LLE2)  ! block
+
+.ncbc.enc.next.block_1:
+
+	xor	in5, out4, in5            ! iv xor
+	xor	out5, global4, out5       ! iv xor
+
+	! parameter 8  1 for move in3 to in4, 2 for move in4 to in3
+	ip_macro(in5, out5, in5, out5, in3, 0, 0, 2)
+
+.ncbc.enc.next.block_2:
+
+!//	call .des_enc                     ! compares in2 to 8
+!	rounds inlined for alignment purposes
+
+	add	global1, 768, global4     ! address sbox 4 since register used below
+
+	rounds_macro(in5, out5, 1, .ncbc.enc.1, in3, in4) ! include encryption  ks in3
+
+	bl	.ncbc.enc.next.block_fp
+	add	in0, 8, in0               ! input address
+
+	! If 8 or more bytes are to be encrypted after this block,
+	! we combine final permutation for this block with initial
+	! permutation for next block. Load next block:
+
+	load_little_endian(in0, global3, global4, local5, .LLE12)
+
+	!  parameter 1   original left
+	!  parameter 2   original right
+	!  parameter 3   left ip
+	!  parameter 4   right ip
+	!  parameter 5   1: load ks1/ks2 to in3/in4, add 120 to in4
+	!                2: mov in4 to in3
+	!
+	! also adds -8 to length in2 and loads loop counter to out4
+
+	fp_ip_macro(out0, out1, global3, global4, 2)
+
+	store_little_endian(in1, out0, out1, local3, .SLE10)  ! block
+
+	ld	[in3], out0               ! key 7531 first round next block
+	mov 	in5, local1
+	xor	global3, out5, in5        ! iv xor next block
+
+	ld	[in3+4], out1             ! key 8642
+	add	global1, 512, global3     ! address sbox 3 since register used
+	xor	global4, local1, out5     ! iv xor next block
+
+	ba	.ncbc.enc.next.block_2
+	add	in1, 8, in1               ! output address
+
+.ncbc.enc.next.block_fp:
+
+	fp_macro(in5, out5)
+
+	store_little_endian(in1, in5, out5, local3, .SLE1)  ! block
+
+	addcc   in2, -8, in2              ! bytes missing when next block done
+
+	bpos	.ncbc.enc.next.block
+	add	in1, 8, in1
+
+.ncbc.enc.seven.or.less:
+
+	cmp	in2, -8
+
+	ble	.ncbc.enc.finish
+	nop
+
+	add	in2, 8, local1            ! bytes to load
+
+	! addr, length, dest left, dest right, temp, temp2, label, ret label
+	load_n_bytes(in0, local1, global4, out4, local2, local3, .LNB1, .ncbc.enc.next.block_1)
+
+	! Loads 1 to 7 bytes little endian to global4, out4
+
+
+.ncbc.enc.finish:
+
+	LDPTR	IVEC, local4
+	store_little_endian(local4, in5, out5, local5, .SLE2)  ! ivec
+
+	ret
+	restore
+
+
+.ncbc.dec:
+
+	STPTR	in0, INPUT
+	cmp	in2, 0                    ! length
+	add	in3, 120, in3
+
+	LDPTR	IVEC, local7              ! ivec
+	ble	.ncbc.dec.finish
+	mov	in3, in4                  ! schedule
+
+	STPTR	in1, OUTPUT
+	mov	in0, local5               ! input
+
+	load_little_endian(local7, in0, in1, local3, .LLE3)   ! ivec
+
+.ncbc.dec.next.block:
+
+	load_little_endian(local5, in5, out5, local3, .LLE4)  ! block
+
+	! parameter 6  1/2 for include encryption/decryption
+	! parameter 7  1 for mov in1 to in3
+	! parameter 8  1 for mov in3 to in4
+
+	ip_macro(in5, out5, out5, in5, in4, 2, 0, 1) ! include decryprion  ks in4
+
+	fp_macro(out5, in5, 0, 1) ! 1 for input and output address to local5/7
+
+	! in2 is bytes left to be stored
+	! in2 is compared to 8 in the rounds
+
+	xor	out5, in0, out4           ! iv xor
+	bl	.ncbc.dec.seven.or.less
+	xor	in5, in1, global4         ! iv xor
+
+	! Load ivec next block now, since input and output address might be the same.
+
+	load_little_endian_inc(local5, in0, in1, local3, .LLE5)  ! iv
+
+	store_little_endian(local7, out4, global4, local3, .SLE3)
+
+	STPTR	local5, INPUT
+	add	local7, 8, local7
+	addcc   in2, -8, in2
+
+	bg	.ncbc.dec.next.block
+	STPTR	local7, OUTPUT
+
+
+.ncbc.dec.store.iv:
+
+	LDPTR	IVEC, local4              ! ivec
+	store_little_endian(local4, in0, in1, local5, .SLE4)
+
+.ncbc.dec.finish:
+
+	ret
+	restore
+
+.ncbc.dec.seven.or.less:
+
+	load_little_endian_inc(local5, in0, in1, local3, .LLE13)     ! ivec
+
+	store_n_bytes(local7, in2, global4, out4, local3, local4, .SNB1, .ncbc.dec.store.iv)
+
+
+.DES_ncbc_encrypt.end:
+	.size	 DES_ncbc_encrypt, .DES_ncbc_encrypt.end-DES_ncbc_encrypt
+
+
+! void DES_ede3_cbc_encrypt(input, output, lenght, ks1, ks2, ks3, ivec, enc)
+! **************************************************************************
+
+
+	.align 32
+	.global DES_ede3_cbc_encrypt
+	.type	 DES_ede3_cbc_encrypt,#function
+
+DES_ede3_cbc_encrypt:
+
+	save	%sp, FRAME, %sp
+
+	define({KS1}, { [%sp+BIAS+ARG0+3*ARGSZ] })
+	define({KS2}, { [%sp+BIAS+ARG0+4*ARGSZ] })
+	define({KS3}, { [%sp+BIAS+ARG0+5*ARGSZ] })
+
+	sethi	%hi(.PIC.DES_SPtrans-1f),global1
+	or	global1,%lo(.PIC.DES_SPtrans-1f),global1
+1:	call	.+8
+	add	%o7,global1,global1
+	sub	global1,.PIC.DES_SPtrans-.des_and,out2
+
+	LDPTR	[%fp+BIAS+ARG0+7*ARGSZ], local3          ! enc
+	LDPTR	[%fp+BIAS+ARG0+6*ARGSZ], local4          ! ivec
+	cmp	local3, 0                 ! enc
+
+	be	.ede3.dec
+	STPTR	in4, KS2
+
+	STPTR	in5, KS3
+
+	load_little_endian(local4, in5, out5, local3, .LLE6)  ! ivec
+
+	addcc	in2, -8, in2              ! bytes missing after next block
+
+	bl	.ede3.enc.seven.or.less
+	STPTR	in3, KS1
+
+.ede3.enc.next.block:
+
+	load_little_endian(in0, out4, global4, local3, .LLE7)
+
+.ede3.enc.next.block_1:
+
+	LDPTR	KS2, in4
+	xor	in5, out4, in5            ! iv xor
+	xor	out5, global4, out5       ! iv xor
+
+	LDPTR	KS1, in3
+	add	in4, 120, in4             ! for decryption we use last subkey first
+	nop
+
+	ip_macro(in5, out5, in5, out5, in3)
+
+.ede3.enc.next.block_2:
+
+	call .des_enc                     ! ks1 in3
+	nop
+
+	call .des_dec                     ! ks2 in4
+	LDPTR	KS3, in3
+
+	call .des_enc                     ! ks3 in3  compares in2 to 8
+	nop
+
+	bl	.ede3.enc.next.block_fp
+	add	in0, 8, in0
+
+	! If 8 or more bytes are to be encrypted after this block,
+	! we combine final permutation for this block with initial
+	! permutation for next block. Load next block:
+
+	load_little_endian(in0, global3, global4, local5, .LLE11)
+
+	!  parameter 1   original left
+	!  parameter 2   original right
+	!  parameter 3   left ip
+	!  parameter 4   right ip
+	!  parameter 5   1: load ks1/ks2 to in3/in4, add 120 to in4
+	!                2: mov in4 to in3
+	!
+	! also adds -8 to length in2 and loads loop counter to out4
+
+	fp_ip_macro(out0, out1, global3, global4, 1)
+
+	store_little_endian(in1, out0, out1, local3, .SLE9)  ! block
+
+	mov 	in5, local1
+	xor	global3, out5, in5        ! iv xor next block
+
+	ld	[in3], out0               ! key 7531
+	add	global1, 512, global3     ! address sbox 3
+	xor	global4, local1, out5     ! iv xor next block
+
+	ld	[in3+4], out1             ! key 8642
+	add	global1, 768, global4     ! address sbox 4
+	ba	.ede3.enc.next.block_2
+	add	in1, 8, in1
+
+.ede3.enc.next.block_fp:
+
+	fp_macro(in5, out5)
+
+	store_little_endian(in1, in5, out5, local3, .SLE5)  ! block
+
+	addcc   in2, -8, in2              ! bytes missing when next block done
+
+	bpos	.ede3.enc.next.block
+	add	in1, 8, in1
+
+.ede3.enc.seven.or.less:
+
+	cmp	in2, -8
+
+	ble	.ede3.enc.finish
+	nop
+
+	add	in2, 8, local1            ! bytes to load
+
+	! addr, length, dest left, dest right, temp, temp2, label, ret label
+	load_n_bytes(in0, local1, global4, out4, local2, local3, .LNB2, .ede3.enc.next.block_1)
+
+.ede3.enc.finish:
+
+	LDPTR	[%fp+BIAS+ARG0+6*ARGSZ], local4          ! ivec
+	store_little_endian(local4, in5, out5, local5, .SLE6)  ! ivec
+
+	ret
+	restore
+
+.ede3.dec:
+
+	STPTR	in0, INPUT
+	add	in5, 120, in5
+
+	STPTR	in1, OUTPUT
+	mov	in0, local5
+	add	in3, 120, in3
+
+	STPTR	in3, KS1
+	cmp	in2, 0
+
+	ble	.ede3.dec.finish
+	STPTR	in5, KS3
+
+	LDPTR	[%fp+BIAS+ARG0+6*ARGSZ], local7          ! iv
+	load_little_endian(local7, in0, in1, local3, .LLE8)
+
+.ede3.dec.next.block:
+
+	load_little_endian(local5, in5, out5, local3, .LLE9)
+
+	! parameter 6  1/2 for include encryption/decryption
+	! parameter 7  1 for mov in1 to in3
+	! parameter 8  1 for mov in3 to in4
+	! parameter 9  1 for load ks3 and ks2 to in4 and in3
+
+	ip_macro(in5, out5, out5, in5, in4, 2, 0, 0, 1) ! inc .des_dec ks3 in4
+
+	call .des_enc                     ! ks2 in3
+	LDPTR	KS1, in4
+
+	call .des_dec                     ! ks1 in4
+	nop
+
+	fp_macro(out5, in5, 0, 1)   ! 1 for input and output address local5/7
+
+	! in2 is bytes left to be stored
+	! in2 is compared to 8 in the rounds
+
+	xor	out5, in0, out4
+	bl	.ede3.dec.seven.or.less
+	xor	in5, in1, global4
+
+	load_little_endian_inc(local5, in0, in1, local3, .LLE10)   ! iv next block
+
+	store_little_endian(local7, out4, global4, local3, .SLE7)  ! block
+
+	STPTR	local5, INPUT
+	addcc   in2, -8, in2
+	add	local7, 8, local7
+
+	bg	.ede3.dec.next.block
+	STPTR	local7, OUTPUT
+
+.ede3.dec.store.iv:
+
+	LDPTR	[%fp+BIAS+ARG0+6*ARGSZ], local4          ! ivec
+	store_little_endian(local4, in0, in1, local5, .SLE8)  ! ivec
+
+.ede3.dec.finish:
+
+	ret
+	restore
+
+.ede3.dec.seven.or.less:
+
+	load_little_endian_inc(local5, in0, in1, local3, .LLE14)     ! iv
+
+	store_n_bytes(local7, in2, global4, out4, local3, local4, .SNB2, .ede3.dec.store.iv)
+
+
+.DES_ede3_cbc_encrypt.end:
+	.size	 DES_ede3_cbc_encrypt,.DES_ede3_cbc_encrypt.end-DES_ede3_cbc_encrypt
+
+	.align	256
+	.type	 .des_and,#object
+	.size	 .des_and,284
+
+.des_and:
+
+! This table is used for AND 0xFC when it is known that register
+! bits 8-31 are zero. Makes it possible to do three arithmetic
+! operations in one cycle.
+
+	.byte  0, 0, 0, 0, 4, 4, 4, 4
+	.byte  8, 8, 8, 8, 12, 12, 12, 12
+	.byte  16, 16, 16, 16, 20, 20, 20, 20
+	.byte  24, 24, 24, 24, 28, 28, 28, 28
+	.byte  32, 32, 32, 32, 36, 36, 36, 36
+	.byte  40, 40, 40, 40, 44, 44, 44, 44
+	.byte  48, 48, 48, 48, 52, 52, 52, 52
+	.byte  56, 56, 56, 56, 60, 60, 60, 60
+	.byte  64, 64, 64, 64, 68, 68, 68, 68
+	.byte  72, 72, 72, 72, 76, 76, 76, 76
+	.byte  80, 80, 80, 80, 84, 84, 84, 84
+	.byte  88, 88, 88, 88, 92, 92, 92, 92
+	.byte  96, 96, 96, 96, 100, 100, 100, 100
+	.byte  104, 104, 104, 104, 108, 108, 108, 108
+	.byte  112, 112, 112, 112, 116, 116, 116, 116
+	.byte  120, 120, 120, 120, 124, 124, 124, 124
+	.byte  128, 128, 128, 128, 132, 132, 132, 132
+	.byte  136, 136, 136, 136, 140, 140, 140, 140
+	.byte  144, 144, 144, 144, 148, 148, 148, 148
+	.byte  152, 152, 152, 152, 156, 156, 156, 156
+	.byte  160, 160, 160, 160, 164, 164, 164, 164
+	.byte  168, 168, 168, 168, 172, 172, 172, 172
+	.byte  176, 176, 176, 176, 180, 180, 180, 180
+	.byte  184, 184, 184, 184, 188, 188, 188, 188
+	.byte  192, 192, 192, 192, 196, 196, 196, 196
+	.byte  200, 200, 200, 200, 204, 204, 204, 204
+	.byte  208, 208, 208, 208, 212, 212, 212, 212
+	.byte  216, 216, 216, 216, 220, 220, 220, 220
+	.byte  224, 224, 224, 224, 228, 228, 228, 228
+	.byte  232, 232, 232, 232, 236, 236, 236, 236
+	.byte  240, 240, 240, 240, 244, 244, 244, 244
+	.byte  248, 248, 248, 248, 252, 252, 252, 252
+
+	! 5 numbers for initil/final permutation
+
+	.word   0x0f0f0f0f                ! offset 256
+	.word	0x0000ffff                ! 260
+	.word	0x33333333                ! 264
+	.word	0x00ff00ff                ! 268
+	.word	0x55555555                ! 272
+
+	.word	0                         ! 276
+	.word	LOOPS                     ! 280
+	.word	0x0000FC00                ! 284
+
+	.global	DES_SPtrans
+	.type	DES_SPtrans,#object
+	.size	DES_SPtrans,2048
+.align	64
+DES_SPtrans:
+.PIC.DES_SPtrans:
+	! nibble 0
+	.word	0x02080800, 0x00080000, 0x02000002, 0x02080802
+	.word	0x02000000, 0x00080802, 0x00080002, 0x02000002
+	.word	0x00080802, 0x02080800, 0x02080000, 0x00000802
+	.word	0x02000802, 0x02000000, 0x00000000, 0x00080002
+	.word	0x00080000, 0x00000002, 0x02000800, 0x00080800
+	.word	0x02080802, 0x02080000, 0x00000802, 0x02000800
+	.word	0x00000002, 0x00000800, 0x00080800, 0x02080002
+	.word	0x00000800, 0x02000802, 0x02080002, 0x00000000
+	.word	0x00000000, 0x02080802, 0x02000800, 0x00080002
+	.word	0x02080800, 0x00080000, 0x00000802, 0x02000800
+	.word	0x02080002, 0x00000800, 0x00080800, 0x02000002
+	.word	0x00080802, 0x00000002, 0x02000002, 0x02080000
+	.word	0x02080802, 0x00080800, 0x02080000, 0x02000802
+	.word	0x02000000, 0x00000802, 0x00080002, 0x00000000
+	.word	0x00080000, 0x02000000, 0x02000802, 0x02080800
+	.word	0x00000002, 0x02080002, 0x00000800, 0x00080802
+	! nibble 1
+	.word	0x40108010, 0x00000000, 0x00108000, 0x40100000
+	.word	0x40000010, 0x00008010, 0x40008000, 0x00108000
+	.word	0x00008000, 0x40100010, 0x00000010, 0x40008000
+	.word	0x00100010, 0x40108000, 0x40100000, 0x00000010
+	.word	0x00100000, 0x40008010, 0x40100010, 0x00008000
+	.word	0x00108010, 0x40000000, 0x00000000, 0x00100010
+	.word	0x40008010, 0x00108010, 0x40108000, 0x40000010
+	.word	0x40000000, 0x00100000, 0x00008010, 0x40108010
+	.word	0x00100010, 0x40108000, 0x40008000, 0x00108010
+	.word	0x40108010, 0x00100010, 0x40000010, 0x00000000
+	.word	0x40000000, 0x00008010, 0x00100000, 0x40100010
+	.word	0x00008000, 0x40000000, 0x00108010, 0x40008010
+	.word	0x40108000, 0x00008000, 0x00000000, 0x40000010
+	.word	0x00000010, 0x40108010, 0x00108000, 0x40100000
+	.word	0x40100010, 0x00100000, 0x00008010, 0x40008000
+	.word	0x40008010, 0x00000010, 0x40100000, 0x00108000
+	! nibble 2
+	.word	0x04000001, 0x04040100, 0x00000100, 0x04000101
+	.word	0x00040001, 0x04000000, 0x04000101, 0x00040100
+	.word	0x04000100, 0x00040000, 0x04040000, 0x00000001
+	.word	0x04040101, 0x00000101, 0x00000001, 0x04040001
+	.word	0x00000000, 0x00040001, 0x04040100, 0x00000100
+	.word	0x00000101, 0x04040101, 0x00040000, 0x04000001
+	.word	0x04040001, 0x04000100, 0x00040101, 0x04040000
+	.word	0x00040100, 0x00000000, 0x04000000, 0x00040101
+	.word	0x04040100, 0x00000100, 0x00000001, 0x00040000
+	.word	0x00000101, 0x00040001, 0x04040000, 0x04000101
+	.word	0x00000000, 0x04040100, 0x00040100, 0x04040001
+	.word	0x00040001, 0x04000000, 0x04040101, 0x00000001
+	.word	0x00040101, 0x04000001, 0x04000000, 0x04040101
+	.word	0x00040000, 0x04000100, 0x04000101, 0x00040100
+	.word	0x04000100, 0x00000000, 0x04040001, 0x00000101
+	.word	0x04000001, 0x00040101, 0x00000100, 0x04040000
+	! nibble 3
+	.word	0x00401008, 0x10001000, 0x00000008, 0x10401008
+	.word	0x00000000, 0x10400000, 0x10001008, 0x00400008
+	.word	0x10401000, 0x10000008, 0x10000000, 0x00001008
+	.word	0x10000008, 0x00401008, 0x00400000, 0x10000000
+	.word	0x10400008, 0x00401000, 0x00001000, 0x00000008
+	.word	0x00401000, 0x10001008, 0x10400000, 0x00001000
+	.word	0x00001008, 0x00000000, 0x00400008, 0x10401000
+	.word	0x10001000, 0x10400008, 0x10401008, 0x00400000
+	.word	0x10400008, 0x00001008, 0x00400000, 0x10000008
+	.word	0x00401000, 0x10001000, 0x00000008, 0x10400000
+	.word	0x10001008, 0x00000000, 0x00001000, 0x00400008
+	.word	0x00000000, 0x10400008, 0x10401000, 0x00001000
+	.word	0x10000000, 0x10401008, 0x00401008, 0x00400000
+	.word	0x10401008, 0x00000008, 0x10001000, 0x00401008
+	.word	0x00400008, 0x00401000, 0x10400000, 0x10001008
+	.word	0x00001008, 0x10000000, 0x10000008, 0x10401000
+	! nibble 4
+	.word	0x08000000, 0x00010000, 0x00000400, 0x08010420
+	.word	0x08010020, 0x08000400, 0x00010420, 0x08010000
+	.word	0x00010000, 0x00000020, 0x08000020, 0x00010400
+	.word	0x08000420, 0x08010020, 0x08010400, 0x00000000
+	.word	0x00010400, 0x08000000, 0x00010020, 0x00000420
+	.word	0x08000400, 0x00010420, 0x00000000, 0x08000020
+	.word	0x00000020, 0x08000420, 0x08010420, 0x00010020
+	.word	0x08010000, 0x00000400, 0x00000420, 0x08010400
+	.word	0x08010400, 0x08000420, 0x00010020, 0x08010000
+	.word	0x00010000, 0x00000020, 0x08000020, 0x08000400
+	.word	0x08000000, 0x00010400, 0x08010420, 0x00000000
+	.word	0x00010420, 0x08000000, 0x00000400, 0x00010020
+	.word	0x08000420, 0x00000400, 0x00000000, 0x08010420
+	.word	0x08010020, 0x08010400, 0x00000420, 0x00010000
+	.word	0x00010400, 0x08010020, 0x08000400, 0x00000420
+	.word	0x00000020, 0x00010420, 0x08010000, 0x08000020
+	! nibble 5
+	.word	0x80000040, 0x00200040, 0x00000000, 0x80202000
+	.word	0x00200040, 0x00002000, 0x80002040, 0x00200000
+	.word	0x00002040, 0x80202040, 0x00202000, 0x80000000
+	.word	0x80002000, 0x80000040, 0x80200000, 0x00202040
+	.word	0x00200000, 0x80002040, 0x80200040, 0x00000000
+	.word	0x00002000, 0x00000040, 0x80202000, 0x80200040
+	.word	0x80202040, 0x80200000, 0x80000000, 0x00002040
+	.word	0x00000040, 0x00202000, 0x00202040, 0x80002000
+	.word	0x00002040, 0x80000000, 0x80002000, 0x00202040
+	.word	0x80202000, 0x00200040, 0x00000000, 0x80002000
+	.word	0x80000000, 0x00002000, 0x80200040, 0x00200000
+	.word	0x00200040, 0x80202040, 0x00202000, 0x00000040
+	.word	0x80202040, 0x00202000, 0x00200000, 0x80002040
+	.word	0x80000040, 0x80200000, 0x00202040, 0x00000000
+	.word	0x00002000, 0x80000040, 0x80002040, 0x80202000
+	.word	0x80200000, 0x00002040, 0x00000040, 0x80200040
+	! nibble 6
+	.word	0x00004000, 0x00000200, 0x01000200, 0x01000004
+	.word	0x01004204, 0x00004004, 0x00004200, 0x00000000
+	.word	0x01000000, 0x01000204, 0x00000204, 0x01004000
+	.word	0x00000004, 0x01004200, 0x01004000, 0x00000204
+	.word	0x01000204, 0x00004000, 0x00004004, 0x01004204
+	.word	0x00000000, 0x01000200, 0x01000004, 0x00004200
+	.word	0x01004004, 0x00004204, 0x01004200, 0x00000004
+	.word	0x00004204, 0x01004004, 0x00000200, 0x01000000
+	.word	0x00004204, 0x01004000, 0x01004004, 0x00000204
+	.word	0x00004000, 0x00000200, 0x01000000, 0x01004004
+	.word	0x01000204, 0x00004204, 0x00004200, 0x00000000
+	.word	0x00000200, 0x01000004, 0x00000004, 0x01000200
+	.word	0x00000000, 0x01000204, 0x01000200, 0x00004200
+	.word	0x00000204, 0x00004000, 0x01004204, 0x01000000
+	.word	0x01004200, 0x00000004, 0x00004004, 0x01004204
+	.word	0x01000004, 0x01004200, 0x01004000, 0x00004004
+	! nibble 7
+	.word	0x20800080, 0x20820000, 0x00020080, 0x00000000
+	.word	0x20020000, 0x00800080, 0x20800000, 0x20820080
+	.word	0x00000080, 0x20000000, 0x00820000, 0x00020080
+	.word	0x00820080, 0x20020080, 0x20000080, 0x20800000
+	.word	0x00020000, 0x00820080, 0x00800080, 0x20020000
+	.word	0x20820080, 0x20000080, 0x00000000, 0x00820000
+	.word	0x20000000, 0x00800000, 0x20020080, 0x20800080
+	.word	0x00800000, 0x00020000, 0x20820000, 0x00000080
+	.word	0x00800000, 0x00020000, 0x20000080, 0x20820080
+	.word	0x00020080, 0x20000000, 0x00000000, 0x00820000
+	.word	0x20800080, 0x20020080, 0x20020000, 0x00800080
+	.word	0x20820000, 0x00000080, 0x00800080, 0x20020000
+	.word	0x20820080, 0x00800000, 0x20800000, 0x20000080
+	.word	0x00820000, 0x00020080, 0x20020080, 0x20800000
+	.word	0x00000080, 0x20820000, 0x00820080, 0x00000000
+	.word	0x20000000, 0x20800080, 0x00020000, 0x00820080
+
diff --git a/crypto/des/asm/desboth.pl b/crypto/des/asm/desboth.pl
new file mode 100644
index 000000000..76759fb29
--- /dev/null
+++ b/crypto/des/asm/desboth.pl
@@ -0,0 +1,86 @@
+#! /usr/bin/env perl
+# Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+
+$L="edi";
+$R="esi";
+
+sub DES_encrypt3
+	{
+	local($name,$enc)=@_;
+
+	&function_begin_B($name,"");
+	&push("ebx");
+	&mov("ebx",&wparam(0));
+
+	&push("ebp");
+	&push("esi");
+
+	&push("edi");
+
+	&comment("");
+	&comment("Load the data words");
+	&mov($L,&DWP(0,"ebx","",0));
+	&mov($R,&DWP(4,"ebx","",0));
+	&stack_push(3);
+
+	&comment("");
+	&comment("IP");
+	&IP_new($L,$R,"edx",0);
+
+	# put them back
+	
+	if ($enc)
+		{
+		&mov(&DWP(4,"ebx","",0),$R);
+		 &mov("eax",&wparam(1));
+		&mov(&DWP(0,"ebx","",0),"edx");
+		 &mov("edi",&wparam(2));
+		 &mov("esi",&wparam(3));
+		}
+	else
+		{
+		&mov(&DWP(4,"ebx","",0),$R);
+		 &mov("esi",&wparam(1));
+		&mov(&DWP(0,"ebx","",0),"edx");
+		 &mov("edi",&wparam(2));
+		 &mov("eax",&wparam(3));
+		}
+	&mov(&swtmp(2),	(DWC(($enc)?"1":"0")));
+	&mov(&swtmp(1),	"eax");
+	&mov(&swtmp(0),	"ebx");
+	&call("DES_encrypt2");
+	&mov(&swtmp(2),	(DWC(($enc)?"0":"1")));
+	&mov(&swtmp(1),	"edi");
+	&mov(&swtmp(0),	"ebx");
+	&call("DES_encrypt2");
+	&mov(&swtmp(2),	(DWC(($enc)?"1":"0")));
+	&mov(&swtmp(1),	"esi");
+	&mov(&swtmp(0),	"ebx");
+	&call("DES_encrypt2");
+
+	&stack_pop(3);
+	&mov($L,&DWP(0,"ebx","",0));
+	&mov($R,&DWP(4,"ebx","",0));
+
+	&comment("");
+	&comment("FP");
+	&FP_new($L,$R,"eax",0);
+
+	&mov(&DWP(0,"ebx","",0),"eax");
+	&mov(&DWP(4,"ebx","",0),$R);
+
+	&pop("edi");
+	&pop("esi");
+	&pop("ebp");
+	&pop("ebx");
+	&ret();
+	&function_end_B($name);
+	}
+
+
diff --git a/crypto/des/asm/dest4-sparcv9.pl b/crypto/des/asm/dest4-sparcv9.pl
new file mode 100644
index 000000000..4a6e29fc5
--- /dev/null
+++ b/crypto/des/asm/dest4-sparcv9.pl
@@ -0,0 +1,627 @@
+#! /usr/bin/env perl
+# Copyright 2013-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+
+# ====================================================================
+# Written by David S. Miller  and Andy Polyakov
+# . The module is licensed under 2-clause BSD
+# license. March 2013. All rights reserved.
+# ====================================================================
+
+######################################################################
+# DES for SPARC T4.
+#
+# As with other hardware-assisted ciphers CBC encrypt results [for
+# aligned data] are virtually identical to critical path lengths:
+#
+#		DES		Triple-DES
+# CBC encrypt	4.14/4.15(*)	11.7/11.7
+# CBC decrypt	1.77/4.11(**)	6.42/7.47
+#
+#			 (*)	numbers after slash are for
+#				misaligned data;
+#			 (**)	this is result for largest
+#				block size, unlike all other
+#				cases smaller blocks results
+#				are better[?];
+
+$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+push(@INC,"${dir}","${dir}../../perlasm");
+require "sparcv9_modes.pl";
+
+$output=pop;
+open STDOUT,">$output";
+
+$code.=<<___;
+#include "sparc_arch.h"
+
+#ifdef	__arch64__
+.register       %g2,#scratch
+.register       %g3,#scratch
+#endif
+
+.text
+___
+
+{ my ($inp,$out)=("%o0","%o1");
+
+$code.=<<___;
+.align	32
+.globl	des_t4_key_expand
+.type	des_t4_key_expand,#function
+des_t4_key_expand:
+	andcc		$inp, 0x7, %g0
+	alignaddr	$inp, %g0, $inp
+	bz,pt		%icc, 1f
+	ldd		[$inp + 0x00], %f0
+	ldd		[$inp + 0x08], %f2
+	faligndata	%f0, %f2, %f0
+1:	des_kexpand	%f0, 0, %f0
+	des_kexpand	%f0, 1, %f2
+	std		%f0, [$out + 0x00]
+	des_kexpand	%f2, 3, %f6
+	std		%f2, [$out + 0x08]
+	des_kexpand	%f2, 2, %f4
+	des_kexpand	%f6, 3, %f10
+	std		%f6, [$out + 0x18]
+	des_kexpand	%f6, 2, %f8
+	std		%f4, [$out + 0x10]
+	des_kexpand	%f10, 3, %f14
+	std		%f10, [$out + 0x28]
+	des_kexpand	%f10, 2, %f12
+	std		%f8, [$out + 0x20]
+	des_kexpand	%f14, 1, %f16
+	std		%f14, [$out + 0x38]
+	des_kexpand	%f16, 3, %f20
+	std		%f12, [$out + 0x30]
+	des_kexpand	%f16, 2, %f18
+	std		%f16, [$out + 0x40]
+	des_kexpand	%f20, 3, %f24
+	std		%f20, [$out + 0x50]
+	des_kexpand	%f20, 2, %f22
+	std		%f18, [$out + 0x48]
+	des_kexpand	%f24, 3, %f28
+	std		%f24, [$out + 0x60]
+	des_kexpand	%f24, 2, %f26
+	std		%f22, [$out + 0x58]
+	des_kexpand	%f28, 1, %f30
+	std		%f28, [$out + 0x70]
+	std		%f26, [$out + 0x68]
+	retl
+	std		%f30, [$out + 0x78]
+.size	des_t4_key_expand,.-des_t4_key_expand
+___
+}
+{ my ($inp,$out,$len,$key,$ivec) = map("%o$_",(0..4));
+  my ($ileft,$iright,$omask) = map("%g$_",(1..3));
+
+$code.=<<___;
+.globl	des_t4_cbc_encrypt
+.align	32
+des_t4_cbc_encrypt:
+	cmp		$len, 0
+	be,pn		$::size_t_cc, .Lcbc_abort
+	srln		$len, 0, $len		! needed on v8+, "nop" on v9
+	ld		[$ivec + 0], %f0	! load ivec
+	ld		[$ivec + 4], %f1
+
+	and		$inp, 7, $ileft
+	andn		$inp, 7, $inp
+	sll		$ileft, 3, $ileft
+	mov		0xff, $omask
+	prefetch	[$inp], 20
+	prefetch	[$inp + 63], 20
+	sub		%g0, $ileft, $iright
+	and		$out, 7, %g4
+	alignaddrl	$out, %g0, $out
+	srl		$omask, %g4, $omask
+	srlx		$len, 3, $len
+	movrz		%g4, 0, $omask
+	prefetch	[$out], 22
+
+	ldd		[$key + 0x00], %f4	! load key schedule
+	ldd		[$key + 0x08], %f6
+	ldd		[$key + 0x10], %f8
+	ldd		[$key + 0x18], %f10
+	ldd		[$key + 0x20], %f12
+	ldd		[$key + 0x28], %f14
+	ldd		[$key + 0x30], %f16
+	ldd		[$key + 0x38], %f18
+	ldd		[$key + 0x40], %f20
+	ldd		[$key + 0x48], %f22
+	ldd		[$key + 0x50], %f24
+	ldd		[$key + 0x58], %f26
+	ldd		[$key + 0x60], %f28
+	ldd		[$key + 0x68], %f30
+	ldd		[$key + 0x70], %f32
+	ldd		[$key + 0x78], %f34
+
+.Ldes_cbc_enc_loop:
+	ldx		[$inp + 0], %g4
+	brz,pt		$ileft, 4f
+	nop
+
+	ldx		[$inp + 8], %g5
+	sllx		%g4, $ileft, %g4
+	srlx		%g5, $iright, %g5
+	or		%g5, %g4, %g4
+4:
+	movxtod		%g4, %f2
+	prefetch	[$inp + 8+63], 20
+	add		$inp, 8, $inp
+	fxor		%f2, %f0, %f0		! ^= ivec
+	prefetch	[$out + 63], 22
+
+	des_ip		%f0, %f0
+	des_round	%f4, %f6, %f0, %f0
+	des_round	%f8, %f10, %f0, %f0
+	des_round	%f12, %f14, %f0, %f0
+	des_round	%f16, %f18, %f0, %f0
+	des_round	%f20, %f22, %f0, %f0
+	des_round	%f24, %f26, %f0, %f0
+	des_round	%f28, %f30, %f0, %f0
+	des_round	%f32, %f34, %f0, %f0
+	des_iip		%f0, %f0
+
+	brnz,pn		$omask, 2f
+	sub		$len, 1, $len
+
+	std		%f0, [$out + 0]
+	brnz,pt		$len, .Ldes_cbc_enc_loop
+	add		$out, 8, $out
+
+	st		%f0, [$ivec + 0]	! write out ivec
+	retl
+	st		%f1, [$ivec + 4]
+.Lcbc_abort:
+	retl
+	nop
+
+.align	16
+2:	ldxa		[$inp]0x82, %g4		! avoid read-after-write hazard
+						! and ~4x deterioration
+						! in inp==out case
+	faligndata	%f0, %f0, %f2		! handle unaligned output
+
+	stda		%f2, [$out + $omask]0xc0	! partial store
+	add		$out, 8, $out
+	orn		%g0, $omask, $omask
+	stda		%f2, [$out + $omask]0xc0	! partial store
+
+	brnz,pt		$len, .Ldes_cbc_enc_loop+4
+	orn		%g0, $omask, $omask
+
+	st		%f0, [$ivec + 0]	! write out ivec
+	retl
+	st		%f1, [$ivec + 4]
+.type	des_t4_cbc_encrypt,#function
+.size	des_t4_cbc_encrypt,.-des_t4_cbc_encrypt
+
+.globl	des_t4_cbc_decrypt
+.align	32
+des_t4_cbc_decrypt:
+	cmp		$len, 0
+	be,pn		$::size_t_cc, .Lcbc_abort
+	srln		$len, 0, $len		! needed on v8+, "nop" on v9
+	ld		[$ivec + 0], %f2	! load ivec
+	ld		[$ivec + 4], %f3
+
+	and		$inp, 7, $ileft
+	andn		$inp, 7, $inp
+	sll		$ileft, 3, $ileft
+	mov		0xff, $omask
+	prefetch	[$inp], 20
+	prefetch	[$inp + 63], 20
+	sub		%g0, $ileft, $iright
+	and		$out, 7, %g4
+	alignaddrl	$out, %g0, $out
+	srl		$omask, %g4, $omask
+	srlx		$len, 3, $len
+	movrz		%g4, 0, $omask
+	prefetch	[$out], 22
+
+	ldd		[$key + 0x78], %f4	! load key schedule
+	ldd		[$key + 0x70], %f6
+	ldd		[$key + 0x68], %f8
+	ldd		[$key + 0x60], %f10
+	ldd		[$key + 0x58], %f12
+	ldd		[$key + 0x50], %f14
+	ldd		[$key + 0x48], %f16
+	ldd		[$key + 0x40], %f18
+	ldd		[$key + 0x38], %f20
+	ldd		[$key + 0x30], %f22
+	ldd		[$key + 0x28], %f24
+	ldd		[$key + 0x20], %f26
+	ldd		[$key + 0x18], %f28
+	ldd		[$key + 0x10], %f30
+	ldd		[$key + 0x08], %f32
+	ldd		[$key + 0x00], %f34
+
+.Ldes_cbc_dec_loop:
+	ldx		[$inp + 0], %g4
+	brz,pt		$ileft, 4f
+	nop
+
+	ldx		[$inp + 8], %g5
+	sllx		%g4, $ileft, %g4
+	srlx		%g5, $iright, %g5
+	or		%g5, %g4, %g4
+4:
+	movxtod		%g4, %f0
+	prefetch	[$inp + 8+63], 20
+	add		$inp, 8, $inp
+	prefetch	[$out + 63], 22
+
+	des_ip		%f0, %f0
+	des_round	%f4, %f6, %f0, %f0
+	des_round	%f8, %f10, %f0, %f0
+	des_round	%f12, %f14, %f0, %f0
+	des_round	%f16, %f18, %f0, %f0
+	des_round	%f20, %f22, %f0, %f0
+	des_round	%f24, %f26, %f0, %f0
+	des_round	%f28, %f30, %f0, %f0
+	des_round	%f32, %f34, %f0, %f0
+	des_iip		%f0, %f0
+
+	fxor		%f2, %f0, %f0		! ^= ivec
+	movxtod		%g4, %f2
+
+	brnz,pn		$omask, 2f
+	sub		$len, 1, $len
+
+	std		%f0, [$out + 0]
+	brnz,pt		$len, .Ldes_cbc_dec_loop
+	add		$out, 8, $out
+
+	st		%f2, [$ivec + 0]	! write out ivec
+	retl
+	st		%f3, [$ivec + 4]
+
+.align	16
+2:	ldxa		[$inp]0x82, %g4		! avoid read-after-write hazard
+						! and ~4x deterioration
+						! in inp==out case
+	faligndata	%f0, %f0, %f0		! handle unaligned output
+
+	stda		%f0, [$out + $omask]0xc0	! partial store
+	add		$out, 8, $out
+	orn		%g0, $omask, $omask
+	stda		%f0, [$out + $omask]0xc0	! partial store
+
+	brnz,pt		$len, .Ldes_cbc_dec_loop+4
+	orn		%g0, $omask, $omask
+
+	st		%f2, [$ivec + 0]	! write out ivec
+	retl
+	st		%f3, [$ivec + 4]
+.type	des_t4_cbc_decrypt,#function
+.size	des_t4_cbc_decrypt,.-des_t4_cbc_decrypt
+___
+
+# One might wonder why does one have back-to-back des_iip/des_ip
+# pairs between EDE passes. Indeed, aren't they inverse of each other?
+# They almost are. Outcome of the pair is 32-bit words being swapped
+# in target register. Consider pair of des_iip/des_ip as a way to
+# perform the due swap, it's actually fastest way in this case.
+
+$code.=<<___;
+.globl	des_t4_ede3_cbc_encrypt
+.align	32
+des_t4_ede3_cbc_encrypt:
+	cmp		$len, 0
+	be,pn		$::size_t_cc, .Lcbc_abort
+	srln		$len, 0, $len		! needed on v8+, "nop" on v9
+	ld		[$ivec + 0], %f0	! load ivec
+	ld		[$ivec + 4], %f1
+
+	and		$inp, 7, $ileft
+	andn		$inp, 7, $inp
+	sll		$ileft, 3, $ileft
+	mov		0xff, $omask
+	prefetch	[$inp], 20
+	prefetch	[$inp + 63], 20
+	sub		%g0, $ileft, $iright
+	and		$out, 7, %g4
+	alignaddrl	$out, %g0, $out
+	srl		$omask, %g4, $omask
+	srlx		$len, 3, $len
+	movrz		%g4, 0, $omask
+	prefetch	[$out], 22
+
+	ldd		[$key + 0x00], %f4	! load key schedule
+	ldd		[$key + 0x08], %f6
+	ldd		[$key + 0x10], %f8
+	ldd		[$key + 0x18], %f10
+	ldd		[$key + 0x20], %f12
+	ldd		[$key + 0x28], %f14
+	ldd		[$key + 0x30], %f16
+	ldd		[$key + 0x38], %f18
+	ldd		[$key + 0x40], %f20
+	ldd		[$key + 0x48], %f22
+	ldd		[$key + 0x50], %f24
+	ldd		[$key + 0x58], %f26
+	ldd		[$key + 0x60], %f28
+	ldd		[$key + 0x68], %f30
+	ldd		[$key + 0x70], %f32
+	ldd		[$key + 0x78], %f34
+
+.Ldes_ede3_cbc_enc_loop:
+	ldx		[$inp + 0], %g4
+	brz,pt		$ileft, 4f
+	nop
+
+	ldx		[$inp + 8], %g5
+	sllx		%g4, $ileft, %g4
+	srlx		%g5, $iright, %g5
+	or		%g5, %g4, %g4
+4:
+	movxtod		%g4, %f2
+	prefetch	[$inp + 8+63], 20
+	add		$inp, 8, $inp
+	fxor		%f2, %f0, %f0		! ^= ivec
+	prefetch	[$out + 63], 22
+
+	des_ip		%f0, %f0
+	des_round	%f4, %f6, %f0, %f0
+	des_round	%f8, %f10, %f0, %f0
+	des_round	%f12, %f14, %f0, %f0
+	des_round	%f16, %f18, %f0, %f0
+	ldd		[$key + 0x100-0x08], %f36
+	ldd		[$key + 0x100-0x10], %f38
+	des_round	%f20, %f22, %f0, %f0
+	ldd		[$key + 0x100-0x18], %f40
+	ldd		[$key + 0x100-0x20], %f42
+	des_round	%f24, %f26, %f0, %f0
+	ldd		[$key + 0x100-0x28], %f44
+	ldd		[$key + 0x100-0x30], %f46
+	des_round	%f28, %f30, %f0, %f0
+	ldd		[$key + 0x100-0x38], %f48
+	ldd		[$key + 0x100-0x40], %f50
+	des_round	%f32, %f34, %f0, %f0
+	ldd		[$key + 0x100-0x48], %f52
+	ldd		[$key + 0x100-0x50], %f54
+	des_iip		%f0, %f0
+
+	ldd		[$key + 0x100-0x58], %f56
+	ldd		[$key + 0x100-0x60], %f58
+	des_ip		%f0, %f0
+	ldd		[$key + 0x100-0x68], %f60
+	ldd		[$key + 0x100-0x70], %f62
+	des_round	%f36, %f38, %f0, %f0
+	ldd		[$key + 0x100-0x78], %f36
+	ldd		[$key + 0x100-0x80], %f38
+	des_round	%f40, %f42, %f0, %f0
+	des_round	%f44, %f46, %f0, %f0
+	des_round	%f48, %f50, %f0, %f0
+	ldd		[$key + 0x100+0x00], %f40
+	ldd		[$key + 0x100+0x08], %f42
+	des_round	%f52, %f54, %f0, %f0
+	ldd		[$key + 0x100+0x10], %f44
+	ldd		[$key + 0x100+0x18], %f46
+	des_round	%f56, %f58, %f0, %f0
+	ldd		[$key + 0x100+0x20], %f48
+	ldd		[$key + 0x100+0x28], %f50
+	des_round	%f60, %f62, %f0, %f0
+	ldd		[$key + 0x100+0x30], %f52
+	ldd		[$key + 0x100+0x38], %f54
+	des_round	%f36, %f38, %f0, %f0
+	ldd		[$key + 0x100+0x40], %f56
+	ldd		[$key + 0x100+0x48], %f58
+	des_iip		%f0, %f0
+
+	ldd		[$key + 0x100+0x50], %f60
+	ldd		[$key + 0x100+0x58], %f62
+	des_ip		%f0, %f0
+	ldd		[$key + 0x100+0x60], %f36
+	ldd		[$key + 0x100+0x68], %f38
+	des_round	%f40, %f42, %f0, %f0
+	ldd		[$key + 0x100+0x70], %f40
+	ldd		[$key + 0x100+0x78], %f42
+	des_round	%f44, %f46, %f0, %f0
+	des_round	%f48, %f50, %f0, %f0
+	des_round	%f52, %f54, %f0, %f0
+	des_round	%f56, %f58, %f0, %f0
+	des_round	%f60, %f62, %f0, %f0
+	des_round	%f36, %f38, %f0, %f0
+	des_round	%f40, %f42, %f0, %f0
+	des_iip		%f0, %f0
+
+	brnz,pn		$omask, 2f
+	sub		$len, 1, $len
+
+	std		%f0, [$out + 0]
+	brnz,pt		$len, .Ldes_ede3_cbc_enc_loop
+	add		$out, 8, $out
+
+	st		%f0, [$ivec + 0]	! write out ivec
+	retl
+	st		%f1, [$ivec + 4]
+
+.align	16
+2:	ldxa		[$inp]0x82, %g4		! avoid read-after-write hazard
+						! and ~2x deterioration
+						! in inp==out case
+	faligndata	%f0, %f0, %f2		! handle unaligned output
+
+	stda		%f2, [$out + $omask]0xc0	! partial store
+	add		$out, 8, $out
+	orn		%g0, $omask, $omask
+	stda		%f2, [$out + $omask]0xc0	! partial store
+
+	brnz,pt		$len, .Ldes_ede3_cbc_enc_loop+4
+	orn		%g0, $omask, $omask
+
+	st		%f0, [$ivec + 0]	! write out ivec
+	retl
+	st		%f1, [$ivec + 4]
+.type	des_t4_ede3_cbc_encrypt,#function
+.size	des_t4_ede3_cbc_encrypt,.-des_t4_ede3_cbc_encrypt
+
+.globl	des_t4_ede3_cbc_decrypt
+.align	32
+des_t4_ede3_cbc_decrypt:
+	cmp		$len, 0
+	be,pn		$::size_t_cc, .Lcbc_abort
+	srln		$len, 0, $len		! needed on v8+, "nop" on v9
+	ld		[$ivec + 0], %f2	! load ivec
+	ld		[$ivec + 4], %f3
+
+	and		$inp, 7, $ileft
+	andn		$inp, 7, $inp
+	sll		$ileft, 3, $ileft
+	mov		0xff, $omask
+	prefetch	[$inp], 20
+	prefetch	[$inp + 63], 20
+	sub		%g0, $ileft, $iright
+	and		$out, 7, %g4
+	alignaddrl	$out, %g0, $out
+	srl		$omask, %g4, $omask
+	srlx		$len, 3, $len
+	movrz		%g4, 0, $omask
+	prefetch	[$out], 22
+
+	ldd		[$key + 0x100+0x78], %f4	! load key schedule
+	ldd		[$key + 0x100+0x70], %f6
+	ldd		[$key + 0x100+0x68], %f8
+	ldd		[$key + 0x100+0x60], %f10
+	ldd		[$key + 0x100+0x58], %f12
+	ldd		[$key + 0x100+0x50], %f14
+	ldd		[$key + 0x100+0x48], %f16
+	ldd		[$key + 0x100+0x40], %f18
+	ldd		[$key + 0x100+0x38], %f20
+	ldd		[$key + 0x100+0x30], %f22
+	ldd		[$key + 0x100+0x28], %f24
+	ldd		[$key + 0x100+0x20], %f26
+	ldd		[$key + 0x100+0x18], %f28
+	ldd		[$key + 0x100+0x10], %f30
+	ldd		[$key + 0x100+0x08], %f32
+	ldd		[$key + 0x100+0x00], %f34
+
+.Ldes_ede3_cbc_dec_loop:
+	ldx		[$inp + 0], %g4
+	brz,pt		$ileft, 4f
+	nop
+
+	ldx		[$inp + 8], %g5
+	sllx		%g4, $ileft, %g4
+	srlx		%g5, $iright, %g5
+	or		%g5, %g4, %g4
+4:
+	movxtod		%g4, %f0
+	prefetch	[$inp + 8+63], 20
+	add		$inp, 8, $inp
+	prefetch	[$out + 63], 22
+
+	des_ip		%f0, %f0
+	des_round	%f4, %f6, %f0, %f0
+	des_round	%f8, %f10, %f0, %f0
+	des_round	%f12, %f14, %f0, %f0
+	des_round	%f16, %f18, %f0, %f0
+	ldd		[$key + 0x80+0x00], %f36
+	ldd		[$key + 0x80+0x08], %f38
+	des_round	%f20, %f22, %f0, %f0
+	ldd		[$key + 0x80+0x10], %f40
+	ldd		[$key + 0x80+0x18], %f42
+	des_round	%f24, %f26, %f0, %f0
+	ldd		[$key + 0x80+0x20], %f44
+	ldd		[$key + 0x80+0x28], %f46
+	des_round	%f28, %f30, %f0, %f0
+	ldd		[$key + 0x80+0x30], %f48
+	ldd		[$key + 0x80+0x38], %f50
+	des_round	%f32, %f34, %f0, %f0
+	ldd		[$key + 0x80+0x40], %f52
+	ldd		[$key + 0x80+0x48], %f54
+	des_iip		%f0, %f0
+
+	ldd		[$key + 0x80+0x50], %f56
+	ldd		[$key + 0x80+0x58], %f58
+	des_ip		%f0, %f0
+	ldd		[$key + 0x80+0x60], %f60
+	ldd		[$key + 0x80+0x68], %f62
+	des_round	%f36, %f38, %f0, %f0
+	ldd		[$key + 0x80+0x70], %f36
+	ldd		[$key + 0x80+0x78], %f38
+	des_round	%f40, %f42, %f0, %f0
+	des_round	%f44, %f46, %f0, %f0
+	des_round	%f48, %f50, %f0, %f0
+	ldd		[$key + 0x80-0x08], %f40
+	ldd		[$key + 0x80-0x10], %f42
+	des_round	%f52, %f54, %f0, %f0
+	ldd		[$key + 0x80-0x18], %f44
+	ldd		[$key + 0x80-0x20], %f46
+	des_round	%f56, %f58, %f0, %f0
+	ldd		[$key + 0x80-0x28], %f48
+	ldd		[$key + 0x80-0x30], %f50
+	des_round	%f60, %f62, %f0, %f0
+	ldd		[$key + 0x80-0x38], %f52
+	ldd		[$key + 0x80-0x40], %f54
+	des_round	%f36, %f38, %f0, %f0
+	ldd		[$key + 0x80-0x48], %f56
+	ldd		[$key + 0x80-0x50], %f58
+	des_iip		%f0, %f0
+
+	ldd		[$key + 0x80-0x58], %f60
+	ldd		[$key + 0x80-0x60], %f62
+	des_ip		%f0, %f0
+	ldd		[$key + 0x80-0x68], %f36
+	ldd		[$key + 0x80-0x70], %f38
+	des_round	%f40, %f42, %f0, %f0
+	ldd		[$key + 0x80-0x78], %f40
+	ldd		[$key + 0x80-0x80], %f42
+	des_round	%f44, %f46, %f0, %f0
+	des_round	%f48, %f50, %f0, %f0
+	des_round	%f52, %f54, %f0, %f0
+	des_round	%f56, %f58, %f0, %f0
+	des_round	%f60, %f62, %f0, %f0
+	des_round	%f36, %f38, %f0, %f0
+	des_round	%f40, %f42, %f0, %f0
+	des_iip		%f0, %f0
+
+	fxor		%f2, %f0, %f0		! ^= ivec
+	movxtod		%g4, %f2
+
+	brnz,pn		$omask, 2f
+	sub		$len, 1, $len
+
+	std		%f0, [$out + 0]
+	brnz,pt		$len, .Ldes_ede3_cbc_dec_loop
+	add		$out, 8, $out
+
+	st		%f2, [$ivec + 0]	! write out ivec
+	retl
+	st		%f3, [$ivec + 4]
+
+.align	16
+2:	ldxa		[$inp]0x82, %g4		! avoid read-after-write hazard
+						! and ~3x deterioration
+						! in inp==out case
+	faligndata	%f0, %f0, %f0		! handle unaligned output
+
+	stda		%f0, [$out + $omask]0xc0	! partial store
+	add		$out, 8, $out
+	orn		%g0, $omask, $omask
+	stda		%f0, [$out + $omask]0xc0	! partial store
+
+	brnz,pt		$len, .Ldes_ede3_cbc_dec_loop+4
+	orn		%g0, $omask, $omask
+
+	st		%f2, [$ivec + 0]	! write out ivec
+	retl
+	st		%f3, [$ivec + 4]
+.type	des_t4_ede3_cbc_decrypt,#function
+.size	des_t4_ede3_cbc_decrypt,.-des_t4_ede3_cbc_decrypt
+___
+}
+$code.=<<___;
+.asciz  "DES for SPARC T4, David S. Miller, Andy Polyakov"
+.align  4
+___
+
+&emit_assembler();
+
+close STDOUT;
diff --git a/crypto/des/build.info b/crypto/des/build.info
new file mode 100644
index 000000000..c0306cfd6
--- /dev/null
+++ b/crypto/des/build.info
@@ -0,0 +1,17 @@
+LIBS=../../libcrypto
+SOURCE[../../libcrypto]=\
+        set_key.c  ecb_enc.c  cbc_enc.c \
+        ecb3_enc.c cfb64enc.c cfb64ede.c cfb_enc.c \
+        ofb64ede.c ofb64enc.c ofb_enc.c \
+        str2key.c  pcbc_enc.c qud_cksm.c rand_key.c \
+        {- $target{des_asm_src} -} \
+        fcrypt.c xcbc_enc.c rpc_enc.c  cbc_cksm.c
+
+GENERATE[des_enc-sparc.S]=asm/des_enc.m4
+GENERATE[dest4-sparcv9.S]=asm/dest4-sparcv9.pl $(PERLASM_SCHEME)
+INCLUDE[dest4-sparcv9.o]=..
+
+GENERATE[des-586.s]=asm/des-586.pl $(PERLASM_SCHEME) $(CFLAGS) $(LIB_CFLAGS)
+DEPEND[des-586.s]=../perlasm/x86asm.pl ../perlasm/cbc.pl
+GENERATE[crypt586.s]=asm/crypt586.pl $(PERLASM_SCHEME) $(CFLAGS) $(LIB_CFLAGS)
+DEPEND[crypt586.s]=../perlasm/x86asm.pl ../perlasm/cbc.pl
diff --git a/crypto/des/cbc_cksm.c b/crypto/des/cbc_cksm.c
new file mode 100644
index 000000000..a7bf0689b
--- /dev/null
+++ b/crypto/des/cbc_cksm.c
@@ -0,0 +1,54 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "des_locl.h"
+
+DES_LONG DES_cbc_cksum(const unsigned char *in, DES_cblock *output,
+                       long length, DES_key_schedule *schedule,
+                       const_DES_cblock *ivec)
+{
+    register DES_LONG tout0, tout1, tin0, tin1;
+    register long l = length;
+    DES_LONG tin[2];
+    unsigned char *out = &(*output)[0];
+    const unsigned char *iv = &(*ivec)[0];
+
+    c2l(iv, tout0);
+    c2l(iv, tout1);
+    for (; l > 0; l -= 8) {
+        if (l >= 8) {
+            c2l(in, tin0);
+            c2l(in, tin1);
+        } else
+            c2ln(in, tin0, tin1, l);
+
+        tin0 ^= tout0;
+        tin[0] = tin0;
+        tin1 ^= tout1;
+        tin[1] = tin1;
+        DES_encrypt1((DES_LONG *)tin, schedule, DES_ENCRYPT);
+        /* fix 15/10/91 eay - thanks to keithr@sco.COM */
+        tout0 = tin[0];
+        tout1 = tin[1];
+    }
+    if (out != NULL) {
+        l2c(tout0, out);
+        l2c(tout1, out);
+    }
+    tout0 = tin0 = tin1 = tin[0] = tin[1] = 0;
+    /*
+     * Transform the data in tout1 so that it will match the return value
+     * that the MIT Kerberos mit_des_cbc_cksum API returns.
+     */
+    tout1 = ((tout1 >> 24L) & 0x000000FF)
+        | ((tout1 >> 8L) & 0x0000FF00)
+        | ((tout1 << 8L) & 0x00FF0000)
+        | ((tout1 << 24L) & 0xFF000000);
+    return (tout1);
+}
diff --git a/crypto/des/cbc_enc.c b/crypto/des/cbc_enc.c
new file mode 100644
index 000000000..92e773f81
--- /dev/null
+++ b/crypto/des/cbc_enc.c
@@ -0,0 +1,12 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#define CBC_ENC_C__DONT_UPDATE_IV
+
+#include "ncbc_enc.c"           /* des_cbc_encrypt */
diff --git a/crypto/des/cfb64ede.c b/crypto/des/cfb64ede.c
new file mode 100644
index 000000000..5edb979e1
--- /dev/null
+++ b/crypto/des/cfb64ede.c
@@ -0,0 +1,190 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "des_locl.h"
+#include "e_os.h"
+
+/*
+ * The input and output encrypted as though 64bit cfb mode is being used.
+ * The extra state information to record how much of the 64bit block we have
+ * used is contained in *num;
+ */
+
+void DES_ede3_cfb64_encrypt(const unsigned char *in, unsigned char *out,
+                            long length, DES_key_schedule *ks1,
+                            DES_key_schedule *ks2, DES_key_schedule *ks3,
+                            DES_cblock *ivec, int *num, int enc)
+{
+    register DES_LONG v0, v1;
+    register long l = length;
+    register int n = *num;
+    DES_LONG ti[2];
+    unsigned char *iv, c, cc;
+
+    iv = &(*ivec)[0];
+    if (enc) {
+        while (l--) {
+            if (n == 0) {
+                c2l(iv, v0);
+                c2l(iv, v1);
+
+                ti[0] = v0;
+                ti[1] = v1;
+                DES_encrypt3(ti, ks1, ks2, ks3);
+                v0 = ti[0];
+                v1 = ti[1];
+
+                iv = &(*ivec)[0];
+                l2c(v0, iv);
+                l2c(v1, iv);
+                iv = &(*ivec)[0];
+            }
+            c = *(in++) ^ iv[n];
+            *(out++) = c;
+            iv[n] = c;
+            n = (n + 1) & 0x07;
+        }
+    } else {
+        while (l--) {
+            if (n == 0) {
+                c2l(iv, v0);
+                c2l(iv, v1);
+
+                ti[0] = v0;
+                ti[1] = v1;
+                DES_encrypt3(ti, ks1, ks2, ks3);
+                v0 = ti[0];
+                v1 = ti[1];
+
+                iv = &(*ivec)[0];
+                l2c(v0, iv);
+                l2c(v1, iv);
+                iv = &(*ivec)[0];
+            }
+            cc = *(in++);
+            c = iv[n];
+            iv[n] = cc;
+            *(out++) = c ^ cc;
+            n = (n + 1) & 0x07;
+        }
+    }
+    v0 = v1 = ti[0] = ti[1] = c = cc = 0;
+    *num = n;
+}
+
+/*
+ * This is compatible with the single key CFB-r for DES, even thought that's
+ * not what EVP needs.
+ */
+
+void DES_ede3_cfb_encrypt(const unsigned char *in, unsigned char *out,
+                          int numbits, long length, DES_key_schedule *ks1,
+                          DES_key_schedule *ks2, DES_key_schedule *ks3,
+                          DES_cblock *ivec, int enc)
+{
+    register DES_LONG d0, d1, v0, v1;
+    register unsigned long l = length, n = ((unsigned int)numbits + 7) / 8;
+    register int num = numbits, i;
+    DES_LONG ti[2];
+    unsigned char *iv;
+    unsigned char ovec[16];
+
+    if (num > 64)
+        return;
+    iv = &(*ivec)[0];
+    c2l(iv, v0);
+    c2l(iv, v1);
+    if (enc) {
+        while (l >= n) {
+            l -= n;
+            ti[0] = v0;
+            ti[1] = v1;
+            DES_encrypt3(ti, ks1, ks2, ks3);
+            c2ln(in, d0, d1, n);
+            in += n;
+            d0 ^= ti[0];
+            d1 ^= ti[1];
+            l2cn(d0, d1, out, n);
+            out += n;
+            /*
+             * 30-08-94 - eay - changed because l>>32 and l<<32 are bad under
+             * gcc :-(
+             */
+            if (num == 32) {
+                v0 = v1;
+                v1 = d0;
+            } else if (num == 64) {
+                v0 = d0;
+                v1 = d1;
+            } else {
+                iv = &ovec[0];
+                l2c(v0, iv);
+                l2c(v1, iv);
+                l2c(d0, iv);
+                l2c(d1, iv);
+                /* shift ovec left most of the bits... */
+                memmove(ovec, ovec + num / 8, 8 + (num % 8 ? 1 : 0));
+                /* now the remaining bits */
+                if (num % 8 != 0)
+                    for (i = 0; i < 8; ++i) {
+                        ovec[i] <<= num % 8;
+                        ovec[i] |= ovec[i + 1] >> (8 - num % 8);
+                    }
+                iv = &ovec[0];
+                c2l(iv, v0);
+                c2l(iv, v1);
+            }
+        }
+    } else {
+        while (l >= n) {
+            l -= n;
+            ti[0] = v0;
+            ti[1] = v1;
+            DES_encrypt3(ti, ks1, ks2, ks3);
+            c2ln(in, d0, d1, n);
+            in += n;
+            /*
+             * 30-08-94 - eay - changed because l>>32 and l<<32 are bad under
+             * gcc :-(
+             */
+            if (num == 32) {
+                v0 = v1;
+                v1 = d0;
+            } else if (num == 64) {
+                v0 = d0;
+                v1 = d1;
+            } else {
+                iv = &ovec[0];
+                l2c(v0, iv);
+                l2c(v1, iv);
+                l2c(d0, iv);
+                l2c(d1, iv);
+                /* shift ovec left most of the bits... */
+                memmove(ovec, ovec + num / 8, 8 + (num % 8 ? 1 : 0));
+                /* now the remaining bits */
+                if (num % 8 != 0)
+                    for (i = 0; i < 8; ++i) {
+                        ovec[i] <<= num % 8;
+                        ovec[i] |= ovec[i + 1] >> (8 - num % 8);
+                    }
+                iv = &ovec[0];
+                c2l(iv, v0);
+                c2l(iv, v1);
+            }
+            d0 ^= ti[0];
+            d1 ^= ti[1];
+            l2cn(d0, d1, out, n);
+            out += n;
+        }
+    }
+    iv = &(*ivec)[0];
+    l2c(v0, iv);
+    l2c(v1, iv);
+    v0 = v1 = d0 = d1 = ti[0] = ti[1] = 0;
+}
diff --git a/crypto/des/cfb64enc.c b/crypto/des/cfb64enc.c
new file mode 100644
index 000000000..96de51b05
--- /dev/null
+++ b/crypto/des/cfb64enc.c
@@ -0,0 +1,73 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "des_locl.h"
+
+/*
+ * The input and output encrypted as though 64bit cfb mode is being used.
+ * The extra state information to record how much of the 64bit block we have
+ * used is contained in *num;
+ */
+
+void DES_cfb64_encrypt(const unsigned char *in, unsigned char *out,
+                       long length, DES_key_schedule *schedule,
+                       DES_cblock *ivec, int *num, int enc)
+{
+    register DES_LONG v0, v1;
+    register long l = length;
+    register int n = *num;
+    DES_LONG ti[2];
+    unsigned char *iv, c, cc;
+
+    iv = &(*ivec)[0];
+    if (enc) {
+        while (l--) {
+            if (n == 0) {
+                c2l(iv, v0);
+                ti[0] = v0;
+                c2l(iv, v1);
+                ti[1] = v1;
+                DES_encrypt1(ti, schedule, DES_ENCRYPT);
+                iv = &(*ivec)[0];
+                v0 = ti[0];
+                l2c(v0, iv);
+                v0 = ti[1];
+                l2c(v0, iv);
+                iv = &(*ivec)[0];
+            }
+            c = *(in++) ^ iv[n];
+            *(out++) = c;
+            iv[n] = c;
+            n = (n + 1) & 0x07;
+        }
+    } else {
+        while (l--) {
+            if (n == 0) {
+                c2l(iv, v0);
+                ti[0] = v0;
+                c2l(iv, v1);
+                ti[1] = v1;
+                DES_encrypt1(ti, schedule, DES_ENCRYPT);
+                iv = &(*ivec)[0];
+                v0 = ti[0];
+                l2c(v0, iv);
+                v0 = ti[1];
+                l2c(v0, iv);
+                iv = &(*ivec)[0];
+            }
+            cc = *(in++);
+            c = iv[n];
+            iv[n] = cc;
+            *(out++) = c ^ cc;
+            n = (n + 1) & 0x07;
+        }
+    }
+    v0 = v1 = ti[0] = ti[1] = c = cc = 0;
+    *num = n;
+}
diff --git a/crypto/des/cfb_enc.c b/crypto/des/cfb_enc.c
new file mode 100644
index 000000000..6c428ba61
--- /dev/null
+++ b/crypto/des/cfb_enc.c
@@ -0,0 +1,150 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "e_os.h"
+#include "des_locl.h"
+#include 
+
+/*
+ * The input and output are loaded in multiples of 8 bits. What this means is
+ * that if you hame numbits=12 and length=2 the first 12 bits will be
+ * retrieved from the first byte and half the second.  The second 12 bits
+ * will come from the 3rd and half the 4th byte.
+ */
+/*
+ * Until Aug 1 2003 this function did not correctly implement CFB-r, so it
+ * will not be compatible with any encryption prior to that date. Ben.
+ */
+void DES_cfb_encrypt(const unsigned char *in, unsigned char *out, int numbits,
+                     long length, DES_key_schedule *schedule,
+                     DES_cblock *ivec, int enc)
+{
+    register DES_LONG d0, d1, v0, v1;
+    register unsigned long l = length;
+    register int num = numbits / 8, n = (numbits + 7) / 8, i, rem =
+        numbits % 8;
+    DES_LONG ti[2];
+    unsigned char *iv;
+#ifndef L_ENDIAN
+    unsigned char ovec[16];
+#else
+    unsigned int sh[4];
+    unsigned char *ovec = (unsigned char *)sh;
+
+    /* I kind of count that compiler optimizes away this assertioni, */
+    assert(sizeof(sh[0]) == 4); /* as this holds true for all, */
+    /* but 16-bit platforms...      */
+
+#endif
+
+    if (numbits <= 0 || numbits > 64)
+        return;
+    iv = &(*ivec)[0];
+    c2l(iv, v0);
+    c2l(iv, v1);
+    if (enc) {
+        while (l >= (unsigned long)n) {
+            l -= n;
+            ti[0] = v0;
+            ti[1] = v1;
+            DES_encrypt1((DES_LONG *)ti, schedule, DES_ENCRYPT);
+            c2ln(in, d0, d1, n);
+            in += n;
+            d0 ^= ti[0];
+            d1 ^= ti[1];
+            l2cn(d0, d1, out, n);
+            out += n;
+            /*
+             * 30-08-94 - eay - changed because l>>32 and l<<32 are bad under
+             * gcc :-(
+             */
+            if (numbits == 32) {
+                v0 = v1;
+                v1 = d0;
+            } else if (numbits == 64) {
+                v0 = d0;
+                v1 = d1;
+            } else {
+#ifndef L_ENDIAN
+                iv = &ovec[0];
+                l2c(v0, iv);
+                l2c(v1, iv);
+                l2c(d0, iv);
+                l2c(d1, iv);
+#else
+                sh[0] = v0, sh[1] = v1, sh[2] = d0, sh[3] = d1;
+#endif
+                if (rem == 0)
+                    memmove(ovec, ovec + num, 8);
+                else
+                    for (i = 0; i < 8; ++i)
+                        ovec[i] = ovec[i + num] << rem |
+                            ovec[i + num + 1] >> (8 - rem);
+#ifdef L_ENDIAN
+                v0 = sh[0], v1 = sh[1];
+#else
+                iv = &ovec[0];
+                c2l(iv, v0);
+                c2l(iv, v1);
+#endif
+            }
+        }
+    } else {
+        while (l >= (unsigned long)n) {
+            l -= n;
+            ti[0] = v0;
+            ti[1] = v1;
+            DES_encrypt1((DES_LONG *)ti, schedule, DES_ENCRYPT);
+            c2ln(in, d0, d1, n);
+            in += n;
+            /*
+             * 30-08-94 - eay - changed because l>>32 and l<<32 are bad under
+             * gcc :-(
+             */
+            if (numbits == 32) {
+                v0 = v1;
+                v1 = d0;
+            } else if (numbits == 64) {
+                v0 = d0;
+                v1 = d1;
+            } else {
+#ifndef L_ENDIAN
+                iv = &ovec[0];
+                l2c(v0, iv);
+                l2c(v1, iv);
+                l2c(d0, iv);
+                l2c(d1, iv);
+#else
+                sh[0] = v0, sh[1] = v1, sh[2] = d0, sh[3] = d1;
+#endif
+                if (rem == 0)
+                    memmove(ovec, ovec + num, 8);
+                else
+                    for (i = 0; i < 8; ++i)
+                        ovec[i] = ovec[i + num] << rem |
+                            ovec[i + num + 1] >> (8 - rem);
+#ifdef L_ENDIAN
+                v0 = sh[0], v1 = sh[1];
+#else
+                iv = &ovec[0];
+                c2l(iv, v0);
+                c2l(iv, v1);
+#endif
+            }
+            d0 ^= ti[0];
+            d1 ^= ti[1];
+            l2cn(d0, d1, out, n);
+            out += n;
+        }
+    }
+    iv = &(*ivec)[0];
+    l2c(v0, iv);
+    l2c(v1, iv);
+    v0 = v1 = d0 = d1 = ti[0] = ti[1] = 0;
+}
diff --git a/crypto/des/des_enc.c b/crypto/des/des_enc.c
new file mode 100644
index 000000000..600f6df48
--- /dev/null
+++ b/crypto/des/des_enc.c
@@ -0,0 +1,301 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "des_locl.h"
+#include "spr.h"
+
+void DES_encrypt1(DES_LONG *data, DES_key_schedule *ks, int enc)
+{
+    register DES_LONG l, r, t, u;
+    register DES_LONG *s;
+
+    r = data[0];
+    l = data[1];
+
+    IP(r, l);
+    /*
+     * Things have been modified so that the initial rotate is done outside
+     * the loop.  This required the DES_SPtrans values in sp.h to be rotated
+     * 1 bit to the right. One perl script later and things have a 5% speed
+     * up on a sparc2. Thanks to Richard Outerbridge
+     * <71755.204@CompuServe.COM> for pointing this out.
+     */
+    /* clear the top bits on machines with 8byte longs */
+    /* shift left by 2 */
+    r = ROTATE(r, 29) & 0xffffffffL;
+    l = ROTATE(l, 29) & 0xffffffffL;
+
+    s = ks->ks->deslong;
+    /*
+     * I don't know if it is worth the effort of loop unrolling the inner
+     * loop
+     */
+    if (enc) {
+        D_ENCRYPT(l, r, 0);     /* 1 */
+        D_ENCRYPT(r, l, 2);     /* 2 */
+        D_ENCRYPT(l, r, 4);     /* 3 */
+        D_ENCRYPT(r, l, 6);     /* 4 */
+        D_ENCRYPT(l, r, 8);     /* 5 */
+        D_ENCRYPT(r, l, 10);    /* 6 */
+        D_ENCRYPT(l, r, 12);    /* 7 */
+        D_ENCRYPT(r, l, 14);    /* 8 */
+        D_ENCRYPT(l, r, 16);    /* 9 */
+        D_ENCRYPT(r, l, 18);    /* 10 */
+        D_ENCRYPT(l, r, 20);    /* 11 */
+        D_ENCRYPT(r, l, 22);    /* 12 */
+        D_ENCRYPT(l, r, 24);    /* 13 */
+        D_ENCRYPT(r, l, 26);    /* 14 */
+        D_ENCRYPT(l, r, 28);    /* 15 */
+        D_ENCRYPT(r, l, 30);    /* 16 */
+    } else {
+        D_ENCRYPT(l, r, 30);    /* 16 */
+        D_ENCRYPT(r, l, 28);    /* 15 */
+        D_ENCRYPT(l, r, 26);    /* 14 */
+        D_ENCRYPT(r, l, 24);    /* 13 */
+        D_ENCRYPT(l, r, 22);    /* 12 */
+        D_ENCRYPT(r, l, 20);    /* 11 */
+        D_ENCRYPT(l, r, 18);    /* 10 */
+        D_ENCRYPT(r, l, 16);    /* 9 */
+        D_ENCRYPT(l, r, 14);    /* 8 */
+        D_ENCRYPT(r, l, 12);    /* 7 */
+        D_ENCRYPT(l, r, 10);    /* 6 */
+        D_ENCRYPT(r, l, 8);     /* 5 */
+        D_ENCRYPT(l, r, 6);     /* 4 */
+        D_ENCRYPT(r, l, 4);     /* 3 */
+        D_ENCRYPT(l, r, 2);     /* 2 */
+        D_ENCRYPT(r, l, 0);     /* 1 */
+    }
+
+    /* rotate and clear the top bits on machines with 8byte longs */
+    l = ROTATE(l, 3) & 0xffffffffL;
+    r = ROTATE(r, 3) & 0xffffffffL;
+
+    FP(r, l);
+    data[0] = l;
+    data[1] = r;
+    l = r = t = u = 0;
+}
+
+void DES_encrypt2(DES_LONG *data, DES_key_schedule *ks, int enc)
+{
+    register DES_LONG l, r, t, u;
+    register DES_LONG *s;
+
+    r = data[0];
+    l = data[1];
+
+    /*
+     * Things have been modified so that the initial rotate is done outside
+     * the loop.  This required the DES_SPtrans values in sp.h to be rotated
+     * 1 bit to the right. One perl script later and things have a 5% speed
+     * up on a sparc2. Thanks to Richard Outerbridge
+     * <71755.204@CompuServe.COM> for pointing this out.
+     */
+    /* clear the top bits on machines with 8byte longs */
+    r = ROTATE(r, 29) & 0xffffffffL;
+    l = ROTATE(l, 29) & 0xffffffffL;
+
+    s = ks->ks->deslong;
+    /*
+     * I don't know if it is worth the effort of loop unrolling the inner
+     * loop
+     */
+    if (enc) {
+        D_ENCRYPT(l, r, 0);     /* 1 */
+        D_ENCRYPT(r, l, 2);     /* 2 */
+        D_ENCRYPT(l, r, 4);     /* 3 */
+        D_ENCRYPT(r, l, 6);     /* 4 */
+        D_ENCRYPT(l, r, 8);     /* 5 */
+        D_ENCRYPT(r, l, 10);    /* 6 */
+        D_ENCRYPT(l, r, 12);    /* 7 */
+        D_ENCRYPT(r, l, 14);    /* 8 */
+        D_ENCRYPT(l, r, 16);    /* 9 */
+        D_ENCRYPT(r, l, 18);    /* 10 */
+        D_ENCRYPT(l, r, 20);    /* 11 */
+        D_ENCRYPT(r, l, 22);    /* 12 */
+        D_ENCRYPT(l, r, 24);    /* 13 */
+        D_ENCRYPT(r, l, 26);    /* 14 */
+        D_ENCRYPT(l, r, 28);    /* 15 */
+        D_ENCRYPT(r, l, 30);    /* 16 */
+    } else {
+        D_ENCRYPT(l, r, 30);    /* 16 */
+        D_ENCRYPT(r, l, 28);    /* 15 */
+        D_ENCRYPT(l, r, 26);    /* 14 */
+        D_ENCRYPT(r, l, 24);    /* 13 */
+        D_ENCRYPT(l, r, 22);    /* 12 */
+        D_ENCRYPT(r, l, 20);    /* 11 */
+        D_ENCRYPT(l, r, 18);    /* 10 */
+        D_ENCRYPT(r, l, 16);    /* 9 */
+        D_ENCRYPT(l, r, 14);    /* 8 */
+        D_ENCRYPT(r, l, 12);    /* 7 */
+        D_ENCRYPT(l, r, 10);    /* 6 */
+        D_ENCRYPT(r, l, 8);     /* 5 */
+        D_ENCRYPT(l, r, 6);     /* 4 */
+        D_ENCRYPT(r, l, 4);     /* 3 */
+        D_ENCRYPT(l, r, 2);     /* 2 */
+        D_ENCRYPT(r, l, 0);     /* 1 */
+    }
+    /* rotate and clear the top bits on machines with 8byte longs */
+    data[0] = ROTATE(l, 3) & 0xffffffffL;
+    data[1] = ROTATE(r, 3) & 0xffffffffL;
+    l = r = t = u = 0;
+}
+
+void DES_encrypt3(DES_LONG *data, DES_key_schedule *ks1,
+                  DES_key_schedule *ks2, DES_key_schedule *ks3)
+{
+    register DES_LONG l, r;
+
+    l = data[0];
+    r = data[1];
+    IP(l, r);
+    data[0] = l;
+    data[1] = r;
+    DES_encrypt2((DES_LONG *)data, ks1, DES_ENCRYPT);
+    DES_encrypt2((DES_LONG *)data, ks2, DES_DECRYPT);
+    DES_encrypt2((DES_LONG *)data, ks3, DES_ENCRYPT);
+    l = data[0];
+    r = data[1];
+    FP(r, l);
+    data[0] = l;
+    data[1] = r;
+}
+
+void DES_decrypt3(DES_LONG *data, DES_key_schedule *ks1,
+                  DES_key_schedule *ks2, DES_key_schedule *ks3)
+{
+    register DES_LONG l, r;
+
+    l = data[0];
+    r = data[1];
+    IP(l, r);
+    data[0] = l;
+    data[1] = r;
+    DES_encrypt2((DES_LONG *)data, ks3, DES_DECRYPT);
+    DES_encrypt2((DES_LONG *)data, ks2, DES_ENCRYPT);
+    DES_encrypt2((DES_LONG *)data, ks1, DES_DECRYPT);
+    l = data[0];
+    r = data[1];
+    FP(r, l);
+    data[0] = l;
+    data[1] = r;
+}
+
+#ifndef DES_DEFAULT_OPTIONS
+
+# undef CBC_ENC_C__DONT_UPDATE_IV
+# include "ncbc_enc.c"          /* DES_ncbc_encrypt */
+
+void DES_ede3_cbc_encrypt(const unsigned char *input, unsigned char *output,
+                          long length, DES_key_schedule *ks1,
+                          DES_key_schedule *ks2, DES_key_schedule *ks3,
+                          DES_cblock *ivec, int enc)
+{
+    register DES_LONG tin0, tin1;
+    register DES_LONG tout0, tout1, xor0, xor1;
+    register const unsigned char *in;
+    unsigned char *out;
+    register long l = length;
+    DES_LONG tin[2];
+    unsigned char *iv;
+
+    in = input;
+    out = output;
+    iv = &(*ivec)[0];
+
+    if (enc) {
+        c2l(iv, tout0);
+        c2l(iv, tout1);
+        for (l -= 8; l >= 0; l -= 8) {
+            c2l(in, tin0);
+            c2l(in, tin1);
+            tin0 ^= tout0;
+            tin1 ^= tout1;
+
+            tin[0] = tin0;
+            tin[1] = tin1;
+            DES_encrypt3((DES_LONG *)tin, ks1, ks2, ks3);
+            tout0 = tin[0];
+            tout1 = tin[1];
+
+            l2c(tout0, out);
+            l2c(tout1, out);
+        }
+        if (l != -8) {
+            c2ln(in, tin0, tin1, l + 8);
+            tin0 ^= tout0;
+            tin1 ^= tout1;
+
+            tin[0] = tin0;
+            tin[1] = tin1;
+            DES_encrypt3((DES_LONG *)tin, ks1, ks2, ks3);
+            tout0 = tin[0];
+            tout1 = tin[1];
+
+            l2c(tout0, out);
+            l2c(tout1, out);
+        }
+        iv = &(*ivec)[0];
+        l2c(tout0, iv);
+        l2c(tout1, iv);
+    } else {
+        register DES_LONG t0, t1;
+
+        c2l(iv, xor0);
+        c2l(iv, xor1);
+        for (l -= 8; l >= 0; l -= 8) {
+            c2l(in, tin0);
+            c2l(in, tin1);
+
+            t0 = tin0;
+            t1 = tin1;
+
+            tin[0] = tin0;
+            tin[1] = tin1;
+            DES_decrypt3((DES_LONG *)tin, ks1, ks2, ks3);
+            tout0 = tin[0];
+            tout1 = tin[1];
+
+            tout0 ^= xor0;
+            tout1 ^= xor1;
+            l2c(tout0, out);
+            l2c(tout1, out);
+            xor0 = t0;
+            xor1 = t1;
+        }
+        if (l != -8) {
+            c2l(in, tin0);
+            c2l(in, tin1);
+
+            t0 = tin0;
+            t1 = tin1;
+
+            tin[0] = tin0;
+            tin[1] = tin1;
+            DES_decrypt3((DES_LONG *)tin, ks1, ks2, ks3);
+            tout0 = tin[0];
+            tout1 = tin[1];
+
+            tout0 ^= xor0;
+            tout1 ^= xor1;
+            l2cn(tout0, tout1, out, l + 8);
+            xor0 = t0;
+            xor1 = t1;
+        }
+
+        iv = &(*ivec)[0];
+        l2c(xor0, iv);
+        l2c(xor1, iv);
+    }
+    tin0 = tin1 = tout0 = tout1 = xor0 = xor1 = 0;
+    tin[0] = tin[1] = 0;
+}
+
+#endif                          /* DES_DEFAULT_OPTIONS */
diff --git a/crypto/des/des_locl.h b/crypto/des/des_locl.h
new file mode 100644
index 000000000..53881d417
--- /dev/null
+++ b/crypto/des/des_locl.h
@@ -0,0 +1,217 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#ifndef HEADER_DES_LOCL_H
+# define HEADER_DES_LOCL_H
+
+# include 
+
+# include 
+# include 
+# include 
+
+# include 
+
+# ifdef OPENSSL_BUILD_SHLIBCRYPTO
+#  undef OPENSSL_EXTERN
+#  define OPENSSL_EXTERN OPENSSL_EXPORT
+# endif
+
+# define ITERATIONS 16
+# define HALF_ITERATIONS 8
+
+/* used in des_read and des_write */
+# define MAXWRITE        (1024*16)
+# define BSIZE           (MAXWRITE+4)
+
+# define c2l(c,l)        (l =((DES_LONG)(*((c)++)))    , \
+                         l|=((DES_LONG)(*((c)++)))<< 8L, \
+                         l|=((DES_LONG)(*((c)++)))<<16L, \
+                         l|=((DES_LONG)(*((c)++)))<<24L)
+
+/* NOTE - c is not incremented as per c2l */
+# define c2ln(c,l1,l2,n) { \
+                        c+=n; \
+                        l1=l2=0; \
+                        switch (n) { \
+                        case 8: l2 =((DES_LONG)(*(--(c))))<<24L; \
+                        case 7: l2|=((DES_LONG)(*(--(c))))<<16L; \
+                        case 6: l2|=((DES_LONG)(*(--(c))))<< 8L; \
+                        case 5: l2|=((DES_LONG)(*(--(c))));     \
+                        case 4: l1 =((DES_LONG)(*(--(c))))<<24L; \
+                        case 3: l1|=((DES_LONG)(*(--(c))))<<16L; \
+                        case 2: l1|=((DES_LONG)(*(--(c))))<< 8L; \
+                        case 1: l1|=((DES_LONG)(*(--(c))));     \
+                                } \
+                        }
+
+# define l2c(l,c)        (*((c)++)=(unsigned char)(((l)     )&0xff), \
+                         *((c)++)=(unsigned char)(((l)>> 8L)&0xff), \
+                         *((c)++)=(unsigned char)(((l)>>16L)&0xff), \
+                         *((c)++)=(unsigned char)(((l)>>24L)&0xff))
+
+/*
+ * replacements for htonl and ntohl since I have no idea what to do when
+ * faced with machines with 8 byte longs.
+ */
+# define HDRSIZE 4
+
+# define n2l(c,l)        (l =((DES_LONG)(*((c)++)))<<24L, \
+                         l|=((DES_LONG)(*((c)++)))<<16L, \
+                         l|=((DES_LONG)(*((c)++)))<< 8L, \
+                         l|=((DES_LONG)(*((c)++))))
+
+# define l2n(l,c)        (*((c)++)=(unsigned char)(((l)>>24L)&0xff), \
+                         *((c)++)=(unsigned char)(((l)>>16L)&0xff), \
+                         *((c)++)=(unsigned char)(((l)>> 8L)&0xff), \
+                         *((c)++)=(unsigned char)(((l)     )&0xff))
+
+/* NOTE - c is not incremented as per l2c */
+# define l2cn(l1,l2,c,n) { \
+                        c+=n; \
+                        switch (n) { \
+                        case 8: *(--(c))=(unsigned char)(((l2)>>24L)&0xff); \
+                        case 7: *(--(c))=(unsigned char)(((l2)>>16L)&0xff); \
+                        case 6: *(--(c))=(unsigned char)(((l2)>> 8L)&0xff); \
+                        case 5: *(--(c))=(unsigned char)(((l2)     )&0xff); \
+                        case 4: *(--(c))=(unsigned char)(((l1)>>24L)&0xff); \
+                        case 3: *(--(c))=(unsigned char)(((l1)>>16L)&0xff); \
+                        case 2: *(--(c))=(unsigned char)(((l1)>> 8L)&0xff); \
+                        case 1: *(--(c))=(unsigned char)(((l1)     )&0xff); \
+                                } \
+                        }
+
+# if (defined(OPENSSL_SYS_WIN32) && defined(_MSC_VER))
+#  define ROTATE(a,n)     (_lrotr(a,n))
+# elif defined(__ICC)
+#  define ROTATE(a,n)     (_rotr(a,n))
+# elif defined(__GNUC__) && __GNUC__>=2 && !defined(__STRICT_ANSI__) && !defined(OPENSSL_NO_ASM) && !defined(OPENSSL_NO_INLINE_ASM) && !defined(PEDANTIC)
+#  if defined(__i386) || defined(__i386__) || defined(__x86_64) || defined(__x86_64__)
+#   define ROTATE(a,n)   ({ register unsigned int ret;   \
+                                asm ("rorl %1,%0"       \
+                                        : "=r"(ret)     \
+                                        : "I"(n),"0"(a) \
+                                        : "cc");        \
+                           ret;                         \
+                        })
+#  endif
+# endif
+# ifndef ROTATE
+#  define ROTATE(a,n)     (((a)>>(n))+((a)<<(32-(n))))
+# endif
+
+/*
+ * Don't worry about the LOAD_DATA() stuff, that is used by fcrypt() to add
+ * it's little bit to the front
+ */
+
+# ifdef DES_FCRYPT
+
+#  define LOAD_DATA_tmp(R,S,u,t,E0,E1) \
+        { DES_LONG tmp; LOAD_DATA(R,S,u,t,E0,E1,tmp); }
+
+#  define LOAD_DATA(R,S,u,t,E0,E1,tmp) \
+        t=R^(R>>16L); \
+        u=t&E0; t&=E1; \
+        tmp=(u<<16); u^=R^s[S  ]; u^=tmp; \
+        tmp=(t<<16); t^=R^s[S+1]; t^=tmp
+# else
+#  define LOAD_DATA_tmp(a,b,c,d,e,f) LOAD_DATA(a,b,c,d,e,f,g)
+#  define LOAD_DATA(R,S,u,t,E0,E1,tmp) \
+        u=R^s[S  ]; \
+        t=R^s[S+1]
+# endif
+
+/*
+ * It recently occurred to me that 0^0^0^0^0^0^0 == 0, so there is no reason
+ * to not xor all the sub items together.  This potentially saves a register
+ * since things can be xored directly into L
+ */
+
+# define D_ENCRYPT(LL,R,S) { \
+        LOAD_DATA_tmp(R,S,u,t,E0,E1); \
+        t=ROTATE(t,4); \
+        LL^= \
+            DES_SPtrans[0][(u>> 2L)&0x3f]^ \
+            DES_SPtrans[2][(u>>10L)&0x3f]^ \
+            DES_SPtrans[4][(u>>18L)&0x3f]^ \
+            DES_SPtrans[6][(u>>26L)&0x3f]^ \
+            DES_SPtrans[1][(t>> 2L)&0x3f]^ \
+            DES_SPtrans[3][(t>>10L)&0x3f]^ \
+            DES_SPtrans[5][(t>>18L)&0x3f]^ \
+            DES_SPtrans[7][(t>>26L)&0x3f]; }
+
+        /*-
+         * IP and FP
+         * The problem is more of a geometric problem that random bit fiddling.
+         0  1  2  3  4  5  6  7      62 54 46 38 30 22 14  6
+         8  9 10 11 12 13 14 15      60 52 44 36 28 20 12  4
+        16 17 18 19 20 21 22 23      58 50 42 34 26 18 10  2
+        24 25 26 27 28 29 30 31  to  56 48 40 32 24 16  8  0
+
+        32 33 34 35 36 37 38 39      63 55 47 39 31 23 15  7
+        40 41 42 43 44 45 46 47      61 53 45 37 29 21 13  5
+        48 49 50 51 52 53 54 55      59 51 43 35 27 19 11  3
+        56 57 58 59 60 61 62 63      57 49 41 33 25 17  9  1
+
+        The output has been subject to swaps of the form
+        0 1 -> 3 1 but the odd and even bits have been put into
+        2 3    2 0
+        different words.  The main trick is to remember that
+        t=((l>>size)^r)&(mask);
+        r^=t;
+        l^=(t<>(n))^(b))&(m)),\
+        (b)^=(t),\
+        (a)^=((t)<<(n)))
+
+# define IP(l,r) \
+        { \
+        register DES_LONG tt; \
+        PERM_OP(r,l,tt, 4,0x0f0f0f0fL); \
+        PERM_OP(l,r,tt,16,0x0000ffffL); \
+        PERM_OP(r,l,tt, 2,0x33333333L); \
+        PERM_OP(l,r,tt, 8,0x00ff00ffL); \
+        PERM_OP(r,l,tt, 1,0x55555555L); \
+        }
+
+# define FP(l,r) \
+        { \
+        register DES_LONG tt; \
+        PERM_OP(l,r,tt, 1,0x55555555L); \
+        PERM_OP(r,l,tt, 8,0x00ff00ffL); \
+        PERM_OP(l,r,tt, 2,0x33333333L); \
+        PERM_OP(r,l,tt,16,0x0000ffffL); \
+        PERM_OP(l,r,tt, 4,0x0f0f0f0fL); \
+        }
+
+extern const DES_LONG DES_SPtrans[8][64];
+
+void fcrypt_body(DES_LONG *out, DES_key_schedule *ks,
+                 DES_LONG Eswap0, DES_LONG Eswap1);
+
+#endif
diff --git a/crypto/des/ecb3_enc.c b/crypto/des/ecb3_enc.c
new file mode 100644
index 000000000..6ac89d4e7
--- /dev/null
+++ b/crypto/des/ecb3_enc.c
@@ -0,0 +1,33 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "des_locl.h"
+
+void DES_ecb3_encrypt(const_DES_cblock *input, DES_cblock *output,
+                      DES_key_schedule *ks1, DES_key_schedule *ks2,
+                      DES_key_schedule *ks3, int enc)
+{
+    register DES_LONG l0, l1;
+    DES_LONG ll[2];
+    const unsigned char *in = &(*input)[0];
+    unsigned char *out = &(*output)[0];
+
+    c2l(in, l0);
+    c2l(in, l1);
+    ll[0] = l0;
+    ll[1] = l1;
+    if (enc)
+        DES_encrypt3(ll, ks1, ks2, ks3);
+    else
+        DES_decrypt3(ll, ks1, ks2, ks3);
+    l0 = ll[0];
+    l1 = ll[1];
+    l2c(l0, out);
+    l2c(l1, out);
+}
diff --git a/crypto/des/ecb_enc.c b/crypto/des/ecb_enc.c
new file mode 100644
index 000000000..bd130c6e0
--- /dev/null
+++ b/crypto/des/ecb_enc.c
@@ -0,0 +1,51 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "des_locl.h"
+#include 
+#include 
+
+
+const char *DES_options(void)
+{
+    static int init = 1;
+    static char buf[32];
+
+    if (init) {
+        const char *size;
+
+        if (sizeof(DES_LONG) != sizeof(long))
+            size = "int";
+        else
+            size = "long";
+        BIO_snprintf(buf, sizeof buf, "des(%s)", size);
+        init = 0;
+    }
+    return (buf);
+}
+
+void DES_ecb_encrypt(const_DES_cblock *input, DES_cblock *output,
+                     DES_key_schedule *ks, int enc)
+{
+    register DES_LONG l;
+    DES_LONG ll[2];
+    const unsigned char *in = &(*input)[0];
+    unsigned char *out = &(*output)[0];
+
+    c2l(in, l);
+    ll[0] = l;
+    c2l(in, l);
+    ll[1] = l;
+    DES_encrypt1(ll, ks, enc);
+    l = ll[0];
+    l2c(l, out);
+    l = ll[1];
+    l2c(l, out);
+    l = ll[0] = ll[1] = 0;
+}
diff --git a/crypto/des/fcrypt.c b/crypto/des/fcrypt.c
new file mode 100644
index 000000000..5215ad3e6
--- /dev/null
+++ b/crypto/des/fcrypt.c
@@ -0,0 +1,149 @@
+/*
+ * Copyright 1998-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/* NOCW */
+#include 
+#ifdef _OSD_POSIX
+# ifndef CHARSET_EBCDIC
+#  define CHARSET_EBCDIC 1
+# endif
+#endif
+#ifdef CHARSET_EBCDIC
+# include 
+#endif
+
+#include 
+#include "des_locl.h"
+
+/*
+ * Added more values to handle illegal salt values the way normal crypt()
+ * implementations do.  The patch was sent by Bjorn Gronvall 
+ */
+static unsigned const char con_salt[128] = {
+    0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9,
+    0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF, 0xE0, 0xE1,
+    0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9,
+    0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, 0xF0, 0xF1,
+    0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9,
+    0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF, 0x00, 0x01,
+    0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
+    0x0A, 0x0B, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A,
+    0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12,
+    0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A,
+    0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22,
+    0x23, 0x24, 0x25, 0x20, 0x21, 0x22, 0x23, 0x24,
+    0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C,
+    0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0x34,
+    0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C,
+    0x3D, 0x3E, 0x3F, 0x40, 0x41, 0x42, 0x43, 0x44,
+};
+
+static unsigned const char cov_2char[64] = {
+    0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35,
+    0x36, 0x37, 0x38, 0x39, 0x41, 0x42, 0x43, 0x44,
+    0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C,
+    0x4D, 0x4E, 0x4F, 0x50, 0x51, 0x52, 0x53, 0x54,
+    0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x61, 0x62,
+    0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A,
+    0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72,
+    0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A
+};
+
+char *DES_crypt(const char *buf, const char *salt)
+{
+    static char buff[14];
+
+#ifndef CHARSET_EBCDIC
+    return (DES_fcrypt(buf, salt, buff));
+#else
+    char e_salt[2 + 1];
+    char e_buf[32 + 1];         /* replace 32 by 8 ? */
+    char *ret;
+
+    if (salt[0] == '\0' || salt[1] == '\0')
+        return NULL;
+
+    /* Copy salt, convert to ASCII. */
+    e_salt[0] = salt[0];
+    e_salt[1] = salt[1];
+    e_salt[2] = '\0';
+    ebcdic2ascii(e_salt, e_salt, sizeof(e_salt));
+
+    /* Convert password to ASCII. */
+    OPENSSL_strlcpy(e_buf, buf, sizeof(e_buf));
+    ebcdic2ascii(e_buf, e_buf, sizeof e_buf);
+
+    /* Encrypt it (from/to ASCII); if it worked, convert back. */
+    ret = DES_fcrypt(e_buf, e_salt, buff);
+    if (ret != NULL)
+        ascii2ebcdic(ret, ret, strlen(ret));
+
+    return ret;
+#endif
+}
+
+char *DES_fcrypt(const char *buf, const char *salt, char *ret)
+{
+    unsigned int i, j, x, y;
+    DES_LONG Eswap0, Eswap1;
+    DES_LONG out[2], ll;
+    DES_cblock key;
+    DES_key_schedule ks;
+    unsigned char bb[9];
+    unsigned char *b = bb;
+    unsigned char c, u;
+
+    x = ret[0] = salt[0];
+    if (x == 0 || x >= sizeof(con_salt))
+        return NULL;
+    Eswap0 = con_salt[x] << 2;
+    x = ret[1] = salt[1];
+    if (x == 0 || x >= sizeof(con_salt))
+        return NULL;
+    Eswap1 = con_salt[x] << 6;
+
+    /*
+     * EAY r=strlen(buf); r=(r+7)/8;
+     */
+    for (i = 0; i < 8; i++) {
+        c = *(buf++);
+        if (!c)
+            break;
+        key[i] = (c << 1);
+    }
+    for (; i < 8; i++)
+        key[i] = 0;
+
+    DES_set_key_unchecked(&key, &ks);
+    fcrypt_body(&(out[0]), &ks, Eswap0, Eswap1);
+
+    ll = out[0];
+    l2c(ll, b);
+    ll = out[1];
+    l2c(ll, b);
+    y = 0;
+    u = 0x80;
+    bb[8] = 0;
+    for (i = 2; i < 13; i++) {
+        c = 0;
+        for (j = 0; j < 6; j++) {
+            c <<= 1;
+            if (bb[y] & u)
+                c |= 1;
+            u >>= 1;
+            if (!u) {
+                y++;
+                u = 0x80;
+            }
+        }
+        ret[i] = cov_2char[c];
+    }
+    ret[13] = '\0';
+    return (ret);
+}
diff --git a/crypto/des/fcrypt_b.c b/crypto/des/fcrypt_b.c
new file mode 100644
index 000000000..fe2369a93
--- /dev/null
+++ b/crypto/des/fcrypt_b.c
@@ -0,0 +1,72 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+
+#define DES_FCRYPT
+#include "des_locl.h"
+#undef DES_FCRYPT
+
+#undef PERM_OP
+#define PERM_OP(a,b,t,n,m) ((t)=((((a)>>(n))^(b))&(m)),\
+        (b)^=(t),\
+        (a)^=((t)<<(n)))
+
+#undef HPERM_OP
+#define HPERM_OP(a,t,n,m) ((t)=((((a)<<(16-(n)))^(a))&(m)),\
+        (a)=(a)^(t)^(t>>(16-(n))))\
+
+void fcrypt_body(DES_LONG *out, DES_key_schedule *ks, DES_LONG Eswap0,
+                 DES_LONG Eswap1)
+{
+    register DES_LONG l, r, t, u;
+    register DES_LONG *s;
+    register int j;
+    register DES_LONG E0, E1;
+
+    l = 0;
+    r = 0;
+
+    s = (DES_LONG *)ks;
+    E0 = Eswap0;
+    E1 = Eswap1;
+
+    for (j = 0; j < 25; j++) {
+        D_ENCRYPT(l, r, 0);     /* 1 */
+        D_ENCRYPT(r, l, 2);     /* 2 */
+        D_ENCRYPT(l, r, 4);     /* 3 */
+        D_ENCRYPT(r, l, 6);     /* 4 */
+        D_ENCRYPT(l, r, 8);     /* 5 */
+        D_ENCRYPT(r, l, 10);    /* 6 */
+        D_ENCRYPT(l, r, 12);    /* 7 */
+        D_ENCRYPT(r, l, 14);    /* 8 */
+        D_ENCRYPT(l, r, 16);    /* 9 */
+        D_ENCRYPT(r, l, 18);    /* 10 */
+        D_ENCRYPT(l, r, 20);    /* 11 */
+        D_ENCRYPT(r, l, 22);    /* 12 */
+        D_ENCRYPT(l, r, 24);    /* 13 */
+        D_ENCRYPT(r, l, 26);    /* 14 */
+        D_ENCRYPT(l, r, 28);    /* 15 */
+        D_ENCRYPT(r, l, 30);    /* 16 */
+        t = l;
+        l = r;
+        r = t;
+    }
+    l = ROTATE(l, 3) & 0xffffffffL;
+    r = ROTATE(r, 3) & 0xffffffffL;
+
+    PERM_OP(l, r, t,  1, 0x55555555L);
+    PERM_OP(r, l, t,  8, 0x00ff00ffL);
+    PERM_OP(l, r, t,  2, 0x33333333L);
+    PERM_OP(r, l, t, 16, 0x0000ffffL);
+    PERM_OP(l, r, t,  4, 0x0f0f0f0fL);
+
+    out[0] = r;
+    out[1] = l;
+}
diff --git a/crypto/des/ncbc_enc.c b/crypto/des/ncbc_enc.c
new file mode 100644
index 000000000..244f15ca2
--- /dev/null
+++ b/crypto/des/ncbc_enc.c
@@ -0,0 +1,106 @@
+/*
+ * Copyright 1998-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*-
+ * #included by:
+ *    cbc_enc.c  (DES_cbc_encrypt)
+ *    des_enc.c  (DES_ncbc_encrypt)
+ */
+
+#include "des_locl.h"
+
+#ifdef CBC_ENC_C__DONT_UPDATE_IV
+void DES_cbc_encrypt(const unsigned char *in, unsigned char *out, long length,
+                     DES_key_schedule *_schedule, DES_cblock *ivec, int enc)
+#else
+void DES_ncbc_encrypt(const unsigned char *in, unsigned char *out,
+                      long length, DES_key_schedule *_schedule,
+                      DES_cblock *ivec, int enc)
+#endif
+{
+    register DES_LONG tin0, tin1;
+    register DES_LONG tout0, tout1, xor0, xor1;
+    register long l = length;
+    DES_LONG tin[2];
+    unsigned char *iv;
+
+    iv = &(*ivec)[0];
+
+    if (enc) {
+        c2l(iv, tout0);
+        c2l(iv, tout1);
+        for (l -= 8; l >= 0; l -= 8) {
+            c2l(in, tin0);
+            c2l(in, tin1);
+            tin0 ^= tout0;
+            tin[0] = tin0;
+            tin1 ^= tout1;
+            tin[1] = tin1;
+            DES_encrypt1((DES_LONG *)tin, _schedule, DES_ENCRYPT);
+            tout0 = tin[0];
+            l2c(tout0, out);
+            tout1 = tin[1];
+            l2c(tout1, out);
+        }
+        if (l != -8) {
+            c2ln(in, tin0, tin1, l + 8);
+            tin0 ^= tout0;
+            tin[0] = tin0;
+            tin1 ^= tout1;
+            tin[1] = tin1;
+            DES_encrypt1((DES_LONG *)tin, _schedule, DES_ENCRYPT);
+            tout0 = tin[0];
+            l2c(tout0, out);
+            tout1 = tin[1];
+            l2c(tout1, out);
+        }
+#ifndef CBC_ENC_C__DONT_UPDATE_IV
+        iv = &(*ivec)[0];
+        l2c(tout0, iv);
+        l2c(tout1, iv);
+#endif
+    } else {
+        c2l(iv, xor0);
+        c2l(iv, xor1);
+        for (l -= 8; l >= 0; l -= 8) {
+            c2l(in, tin0);
+            tin[0] = tin0;
+            c2l(in, tin1);
+            tin[1] = tin1;
+            DES_encrypt1((DES_LONG *)tin, _schedule, DES_DECRYPT);
+            tout0 = tin[0] ^ xor0;
+            tout1 = tin[1] ^ xor1;
+            l2c(tout0, out);
+            l2c(tout1, out);
+            xor0 = tin0;
+            xor1 = tin1;
+        }
+        if (l != -8) {
+            c2l(in, tin0);
+            tin[0] = tin0;
+            c2l(in, tin1);
+            tin[1] = tin1;
+            DES_encrypt1((DES_LONG *)tin, _schedule, DES_DECRYPT);
+            tout0 = tin[0] ^ xor0;
+            tout1 = tin[1] ^ xor1;
+            l2cn(tout0, tout1, out, l + 8);
+#ifndef CBC_ENC_C__DONT_UPDATE_IV
+            xor0 = tin0;
+            xor1 = tin1;
+#endif
+        }
+#ifndef CBC_ENC_C__DONT_UPDATE_IV
+        iv = &(*ivec)[0];
+        l2c(xor0, iv);
+        l2c(xor1, iv);
+#endif
+    }
+    tin0 = tin1 = tout0 = tout1 = xor0 = xor1 = 0;
+    tin[0] = tin[1] = 0;
+}
diff --git a/crypto/des/ofb64ede.c b/crypto/des/ofb64ede.c
new file mode 100644
index 000000000..a551a07e0
--- /dev/null
+++ b/crypto/des/ofb64ede.c
@@ -0,0 +1,62 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "des_locl.h"
+
+/*
+ * The input and output encrypted as though 64bit ofb mode is being used.
+ * The extra state information to record how much of the 64bit block we have
+ * used is contained in *num;
+ */
+void DES_ede3_ofb64_encrypt(register const unsigned char *in,
+                            register unsigned char *out, long length,
+                            DES_key_schedule *k1, DES_key_schedule *k2,
+                            DES_key_schedule *k3, DES_cblock *ivec, int *num)
+{
+    register DES_LONG v0, v1;
+    register int n = *num;
+    register long l = length;
+    DES_cblock d;
+    register char *dp;
+    DES_LONG ti[2];
+    unsigned char *iv;
+    int save = 0;
+
+    iv = &(*ivec)[0];
+    c2l(iv, v0);
+    c2l(iv, v1);
+    ti[0] = v0;
+    ti[1] = v1;
+    dp = (char *)d;
+    l2c(v0, dp);
+    l2c(v1, dp);
+    while (l--) {
+        if (n == 0) {
+            /* ti[0]=v0; */
+            /* ti[1]=v1; */
+            DES_encrypt3(ti, k1, k2, k3);
+            v0 = ti[0];
+            v1 = ti[1];
+
+            dp = (char *)d;
+            l2c(v0, dp);
+            l2c(v1, dp);
+            save++;
+        }
+        *(out++) = *(in++) ^ d[n];
+        n = (n + 1) & 0x07;
+    }
+    if (save) {
+        iv = &(*ivec)[0];
+        l2c(v0, iv);
+        l2c(v1, iv);
+    }
+    v0 = v1 = ti[0] = ti[1] = 0;
+    *num = n;
+}
diff --git a/crypto/des/ofb64enc.c b/crypto/des/ofb64enc.c
new file mode 100644
index 000000000..30976c871
--- /dev/null
+++ b/crypto/des/ofb64enc.c
@@ -0,0 +1,60 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "des_locl.h"
+
+/*
+ * The input and output encrypted as though 64bit ofb mode is being used.
+ * The extra state information to record how much of the 64bit block we have
+ * used is contained in *num;
+ */
+void DES_ofb64_encrypt(register const unsigned char *in,
+                       register unsigned char *out, long length,
+                       DES_key_schedule *schedule, DES_cblock *ivec, int *num)
+{
+    register DES_LONG v0, v1, t;
+    register int n = *num;
+    register long l = length;
+    DES_cblock d;
+    register unsigned char *dp;
+    DES_LONG ti[2];
+    unsigned char *iv;
+    int save = 0;
+
+    iv = &(*ivec)[0];
+    c2l(iv, v0);
+    c2l(iv, v1);
+    ti[0] = v0;
+    ti[1] = v1;
+    dp = d;
+    l2c(v0, dp);
+    l2c(v1, dp);
+    while (l--) {
+        if (n == 0) {
+            DES_encrypt1(ti, schedule, DES_ENCRYPT);
+            dp = d;
+            t = ti[0];
+            l2c(t, dp);
+            t = ti[1];
+            l2c(t, dp);
+            save++;
+        }
+        *(out++) = *(in++) ^ d[n];
+        n = (n + 1) & 0x07;
+    }
+    if (save) {
+        v0 = ti[0];
+        v1 = ti[1];
+        iv = &(*ivec)[0];
+        l2c(v0, iv);
+        l2c(v1, iv);
+    }
+    t = v0 = v1 = ti[0] = ti[1] = 0;
+    *num = n;
+}
diff --git a/crypto/des/ofb_enc.c b/crypto/des/ofb_enc.c
new file mode 100644
index 000000000..65a9b8604
--- /dev/null
+++ b/crypto/des/ofb_enc.c
@@ -0,0 +1,82 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "des_locl.h"
+
+/*
+ * The input and output are loaded in multiples of 8 bits. What this means is
+ * that if you have numbits=12 and length=2 the first 12 bits will be
+ * retrieved from the first byte and half the second.  The second 12 bits
+ * will come from the 3rd and half the 4th byte.
+ */
+void DES_ofb_encrypt(const unsigned char *in, unsigned char *out, int numbits,
+                     long length, DES_key_schedule *schedule,
+                     DES_cblock *ivec)
+{
+    register DES_LONG d0, d1, vv0, vv1, v0, v1, n = (numbits + 7) / 8;
+    register DES_LONG mask0, mask1;
+    register long l = length;
+    register int num = numbits;
+    DES_LONG ti[2];
+    unsigned char *iv;
+
+    if (num > 64)
+        return;
+    if (num > 32) {
+        mask0 = 0xffffffffL;
+        if (num >= 64)
+            mask1 = mask0;
+        else
+            mask1 = (1L << (num - 32)) - 1;
+    } else {
+        if (num == 32)
+            mask0 = 0xffffffffL;
+        else
+            mask0 = (1L << num) - 1;
+        mask1 = 0x00000000L;
+    }
+
+    iv = &(*ivec)[0];
+    c2l(iv, v0);
+    c2l(iv, v1);
+    ti[0] = v0;
+    ti[1] = v1;
+    while (l-- > 0) {
+        ti[0] = v0;
+        ti[1] = v1;
+        DES_encrypt1((DES_LONG *)ti, schedule, DES_ENCRYPT);
+        vv0 = ti[0];
+        vv1 = ti[1];
+        c2ln(in, d0, d1, n);
+        in += n;
+        d0 = (d0 ^ vv0) & mask0;
+        d1 = (d1 ^ vv1) & mask1;
+        l2cn(d0, d1, out, n);
+        out += n;
+
+        if (num == 32) {
+            v0 = v1;
+            v1 = vv0;
+        } else if (num == 64) {
+            v0 = vv0;
+            v1 = vv1;
+        } else if (num > 32) {  /* && num != 64 */
+            v0 = ((v1 >> (num - 32)) | (vv0 << (64 - num))) & 0xffffffffL;
+            v1 = ((vv0 >> (num - 32)) | (vv1 << (64 - num))) & 0xffffffffL;
+        } else {                /* num < 32 */
+
+            v0 = ((v0 >> num) | (v1 << (32 - num))) & 0xffffffffL;
+            v1 = ((v1 >> num) | (vv0 << (32 - num))) & 0xffffffffL;
+        }
+    }
+    iv = &(*ivec)[0];
+    l2c(v0, iv);
+    l2c(v1, iv);
+    v0 = v1 = d0 = d1 = ti[0] = ti[1] = vv0 = vv1 = 0;
+}
diff --git a/crypto/des/pcbc_enc.c b/crypto/des/pcbc_enc.c
new file mode 100644
index 000000000..0fa058f03
--- /dev/null
+++ b/crypto/des/pcbc_enc.c
@@ -0,0 +1,66 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "des_locl.h"
+
+void DES_pcbc_encrypt(const unsigned char *input, unsigned char *output,
+                      long length, DES_key_schedule *schedule,
+                      DES_cblock *ivec, int enc)
+{
+    register DES_LONG sin0, sin1, xor0, xor1, tout0, tout1;
+    DES_LONG tin[2];
+    const unsigned char *in;
+    unsigned char *out, *iv;
+
+    in = input;
+    out = output;
+    iv = &(*ivec)[0];
+
+    if (enc) {
+        c2l(iv, xor0);
+        c2l(iv, xor1);
+        for (; length > 0; length -= 8) {
+            if (length >= 8) {
+                c2l(in, sin0);
+                c2l(in, sin1);
+            } else
+                c2ln(in, sin0, sin1, length);
+            tin[0] = sin0 ^ xor0;
+            tin[1] = sin1 ^ xor1;
+            DES_encrypt1((DES_LONG *)tin, schedule, DES_ENCRYPT);
+            tout0 = tin[0];
+            tout1 = tin[1];
+            xor0 = sin0 ^ tout0;
+            xor1 = sin1 ^ tout1;
+            l2c(tout0, out);
+            l2c(tout1, out);
+        }
+    } else {
+        c2l(iv, xor0);
+        c2l(iv, xor1);
+        for (; length > 0; length -= 8) {
+            c2l(in, sin0);
+            c2l(in, sin1);
+            tin[0] = sin0;
+            tin[1] = sin1;
+            DES_encrypt1((DES_LONG *)tin, schedule, DES_DECRYPT);
+            tout0 = tin[0] ^ xor0;
+            tout1 = tin[1] ^ xor1;
+            if (length >= 8) {
+                l2c(tout0, out);
+                l2c(tout1, out);
+            } else
+                l2cn(tout0, tout1, out, length);
+            xor0 = tout0 ^ sin0;
+            xor1 = tout1 ^ sin1;
+        }
+    }
+    tin[0] = tin[1] = 0;
+    sin0 = sin1 = xor0 = xor1 = tout0 = tout1 = 0;
+}
diff --git a/crypto/des/qud_cksm.c b/crypto/des/qud_cksm.c
new file mode 100644
index 000000000..8710ceca9
--- /dev/null
+++ b/crypto/des/qud_cksm.c
@@ -0,0 +1,77 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * From "Message Authentication" R.R. Jueneman, S.M. Matyas, C.H. Meyer IEEE
+ * Communications Magazine Sept 1985 Vol. 23 No. 9 p 29-40 This module in
+ * only based on the code in this paper and is almost definitely not the same
+ * as the MIT implementation.
+ */
+#include "des_locl.h"
+
+/* bug fix for dos - 7/6/91 - Larry hughes@logos.ucs.indiana.edu */
+#define Q_B0(a) (((DES_LONG)(a)))
+#define Q_B1(a) (((DES_LONG)(a))<<8)
+#define Q_B2(a) (((DES_LONG)(a))<<16)
+#define Q_B3(a) (((DES_LONG)(a))<<24)
+
+/* used to scramble things a bit */
+/* Got the value MIT uses via brute force :-) 2/10/90 eay */
+#define NOISE   ((DES_LONG)83653421L)
+
+DES_LONG DES_quad_cksum(const unsigned char *input, DES_cblock output[],
+                        long length, int out_count, DES_cblock *seed)
+{
+    DES_LONG z0, z1, t0, t1;
+    int i;
+    long l;
+    const unsigned char *cp;
+    DES_LONG *lp;
+
+    if (out_count < 1)
+        out_count = 1;
+    lp = (DES_LONG *)&(output[0])[0];
+
+    z0 = Q_B0((*seed)[0]) | Q_B1((*seed)[1]) | Q_B2((*seed)[2]) |
+        Q_B3((*seed)[3]);
+    z1 = Q_B0((*seed)[4]) | Q_B1((*seed)[5]) | Q_B2((*seed)[6]) |
+        Q_B3((*seed)[7]);
+
+    for (i = 0; ((i < 4) && (i < out_count)); i++) {
+        cp = input;
+        l = length;
+        while (l > 0) {
+            if (l > 1) {
+                t0 = (DES_LONG)(*(cp++));
+                t0 |= (DES_LONG)Q_B1(*(cp++));
+                l--;
+            } else
+                t0 = (DES_LONG)(*(cp++));
+            l--;
+            /* add */
+            t0 += z0;
+            t0 &= 0xffffffffL;
+            t1 = z1;
+            /* square, well sort of square */
+            z0 = ((((t0 * t0) & 0xffffffffL) + ((t1 * t1) & 0xffffffffL))
+                  & 0xffffffffL) % 0x7fffffffL;
+            z1 = ((t0 * ((t1 + NOISE) & 0xffffffffL)) & 0xffffffffL) %
+                0x7fffffffL;
+        }
+        if (lp != NULL) {
+            /*
+             * The MIT library assumes that the checksum is composed of
+             * 2*out_count 32 bit ints
+             */
+            *lp++ = z0;
+            *lp++ = z1;
+        }
+    }
+    return (z0);
+}
diff --git a/crypto/des/rand_key.c b/crypto/des/rand_key.c
new file mode 100644
index 000000000..61e4f9d05
--- /dev/null
+++ b/crypto/des/rand_key.c
@@ -0,0 +1,21 @@
+/*
+ * Copyright 1998-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+
+int DES_random_key(DES_cblock *ret)
+{
+    do {
+        if (RAND_bytes((unsigned char *)ret, sizeof(DES_cblock)) != 1)
+            return (0);
+    } while (DES_is_weak_key(ret));
+    DES_set_odd_parity(ret);
+    return (1);
+}
diff --git a/crypto/des/rpc_des.h b/crypto/des/rpc_des.h
new file mode 100644
index 000000000..fe59e224d
--- /dev/null
+++ b/crypto/des/rpc_des.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*  @(#)des.h   2.2 88/08/10 4.0 RPCSRC; from 2.7 88/02/08 SMI  */
+/*-
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part.  Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California  94043
+ */
+/*
+ * Generic DES driver interface
+ * Keep this file hardware independent!
+ * Copyright (c) 1986 by Sun Microsystems, Inc.
+ */
+
+#define DES_MAXLEN      65536   /* maximum # of bytes to encrypt */
+#define DES_QUICKLEN    16      /* maximum # of bytes to encrypt quickly */
+
+enum desdir { ENCRYPT, DECRYPT };
+enum desmode { CBC, ECB };
+
+/*
+ * parameters to ioctl call
+ */
+struct desparams {
+    unsigned char des_key[8];   /* key (with low bit parity) */
+    enum desdir des_dir;        /* direction */
+    enum desmode des_mode;      /* mode */
+    unsigned char des_ivec[8];  /* input vector */
+    unsigned des_len;           /* number of bytes to crypt */
+    union {
+        unsigned char UDES_data[DES_QUICKLEN];
+        unsigned char *UDES_buf;
+    } UDES;
+#define des_data UDES.UDES_data /* direct data here if quick */
+#define des_buf  UDES.UDES_buf  /* otherwise, pointer to data */
+};
+
+/*
+ * Encrypt an arbitrary sized buffer
+ */
+#define DESIOCBLOCK     _IOWR('d', 6, struct desparams)
+
+/*
+ * Encrypt of small amount of data, quickly
+ */
+#define DESIOCQUICK     _IOWR('d', 7, struct desparams)
diff --git a/crypto/des/rpc_enc.c b/crypto/des/rpc_enc.c
new file mode 100644
index 000000000..bfa85115a
--- /dev/null
+++ b/crypto/des/rpc_enc.c
@@ -0,0 +1,30 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "rpc_des.h"
+#include "des_locl.h"
+
+int _des_crypt(char *buf, int len, struct desparams *desp);
+int _des_crypt(char *buf, int len, struct desparams *desp)
+{
+    DES_key_schedule ks;
+    int enc;
+
+    DES_set_key_unchecked(&desp->des_key, &ks);
+    enc = (desp->des_dir == ENCRYPT) ? DES_ENCRYPT : DES_DECRYPT;
+
+    if (desp->des_mode == CBC)
+        DES_ecb_encrypt((const_DES_cblock *)desp->UDES.UDES_buf,
+                        (DES_cblock *)desp->UDES.UDES_buf, &ks, enc);
+    else {
+        DES_ncbc_encrypt(desp->UDES.UDES_buf, desp->UDES.UDES_buf,
+                         len, &ks, &desp->des_ivec, enc);
+    }
+    return (1);
+}
diff --git a/crypto/des/set_key.c b/crypto/des/set_key.c
new file mode 100644
index 000000000..93adff398
--- /dev/null
+++ b/crypto/des/set_key.c
@@ -0,0 +1,389 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*-
+ * set_key.c v 1.4 eay 24/9/91
+ * 1.4 Speed up by 400% :-)
+ * 1.3 added register declarations.
+ * 1.2 unrolled make_key_sched a bit more
+ * 1.1 added norm_expand_bits
+ * 1.0 First working version
+ */
+#include 
+#include "des_locl.h"
+
+OPENSSL_IMPLEMENT_GLOBAL(int, DES_check_key, 0)
+                                                    /*
+                                                     * defaults to false
+                                                     */
+static const unsigned char odd_parity[256] = {
+    1, 1, 2, 2, 4, 4, 7, 7, 8, 8, 11, 11, 13, 13, 14, 14,
+    16, 16, 19, 19, 21, 21, 22, 22, 25, 25, 26, 26, 28, 28, 31, 31,
+    32, 32, 35, 35, 37, 37, 38, 38, 41, 41, 42, 42, 44, 44, 47, 47,
+    49, 49, 50, 50, 52, 52, 55, 55, 56, 56, 59, 59, 61, 61, 62, 62,
+    64, 64, 67, 67, 69, 69, 70, 70, 73, 73, 74, 74, 76, 76, 79, 79,
+    81, 81, 82, 82, 84, 84, 87, 87, 88, 88, 91, 91, 93, 93, 94, 94,
+    97, 97, 98, 98, 100, 100, 103, 103, 104, 104, 107, 107, 109, 109, 110,
+    110,
+    112, 112, 115, 115, 117, 117, 118, 118, 121, 121, 122, 122, 124, 124, 127,
+    127,
+    128, 128, 131, 131, 133, 133, 134, 134, 137, 137, 138, 138, 140, 140, 143,
+    143,
+    145, 145, 146, 146, 148, 148, 151, 151, 152, 152, 155, 155, 157, 157, 158,
+    158,
+    161, 161, 162, 162, 164, 164, 167, 167, 168, 168, 171, 171, 173, 173, 174,
+    174,
+    176, 176, 179, 179, 181, 181, 182, 182, 185, 185, 186, 186, 188, 188, 191,
+    191,
+    193, 193, 194, 194, 196, 196, 199, 199, 200, 200, 203, 203, 205, 205, 206,
+    206,
+    208, 208, 211, 211, 213, 213, 214, 214, 217, 217, 218, 218, 220, 220, 223,
+    223,
+    224, 224, 227, 227, 229, 229, 230, 230, 233, 233, 234, 234, 236, 236, 239,
+    239,
+    241, 241, 242, 242, 244, 244, 247, 247, 248, 248, 251, 251, 253, 253, 254,
+    254
+};
+
+void DES_set_odd_parity(DES_cblock *key)
+{
+    unsigned int i;
+
+    for (i = 0; i < DES_KEY_SZ; i++)
+        (*key)[i] = odd_parity[(*key)[i]];
+}
+
+int DES_check_key_parity(const_DES_cblock *key)
+{
+    unsigned int i;
+
+    for (i = 0; i < DES_KEY_SZ; i++) {
+        if ((*key)[i] != odd_parity[(*key)[i]])
+            return (0);
+    }
+    return (1);
+}
+
+/*-
+ * Weak and semi week keys as take from
+ * %A D.W. Davies
+ * %A W.L. Price
+ * %T Security for Computer Networks
+ * %I John Wiley & Sons
+ * %D 1984
+ * Many thanks to smb@ulysses.att.com (Steven Bellovin) for the reference
+ * (and actual cblock values).
+ */
+#define NUM_WEAK_KEY    16
+static const DES_cblock weak_keys[NUM_WEAK_KEY] = {
+    /* weak keys */
+    {0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01},
+    {0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE},
+    {0x1F, 0x1F, 0x1F, 0x1F, 0x0E, 0x0E, 0x0E, 0x0E},
+    {0xE0, 0xE0, 0xE0, 0xE0, 0xF1, 0xF1, 0xF1, 0xF1},
+    /* semi-weak keys */
+    {0x01, 0xFE, 0x01, 0xFE, 0x01, 0xFE, 0x01, 0xFE},
+    {0xFE, 0x01, 0xFE, 0x01, 0xFE, 0x01, 0xFE, 0x01},
+    {0x1F, 0xE0, 0x1F, 0xE0, 0x0E, 0xF1, 0x0E, 0xF1},
+    {0xE0, 0x1F, 0xE0, 0x1F, 0xF1, 0x0E, 0xF1, 0x0E},
+    {0x01, 0xE0, 0x01, 0xE0, 0x01, 0xF1, 0x01, 0xF1},
+    {0xE0, 0x01, 0xE0, 0x01, 0xF1, 0x01, 0xF1, 0x01},
+    {0x1F, 0xFE, 0x1F, 0xFE, 0x0E, 0xFE, 0x0E, 0xFE},
+    {0xFE, 0x1F, 0xFE, 0x1F, 0xFE, 0x0E, 0xFE, 0x0E},
+    {0x01, 0x1F, 0x01, 0x1F, 0x01, 0x0E, 0x01, 0x0E},
+    {0x1F, 0x01, 0x1F, 0x01, 0x0E, 0x01, 0x0E, 0x01},
+    {0xE0, 0xFE, 0xE0, 0xFE, 0xF1, 0xFE, 0xF1, 0xFE},
+    {0xFE, 0xE0, 0xFE, 0xE0, 0xFE, 0xF1, 0xFE, 0xF1}
+};
+
+int DES_is_weak_key(const_DES_cblock *key)
+{
+    int i;
+
+    for (i = 0; i < NUM_WEAK_KEY; i++)
+        /*
+         * Added == 0 to comparison, I obviously don't run this section very
+         * often :-(, thanks to engineering@MorningStar.Com for the fix eay
+         * 93/06/29 Another problem, I was comparing only the first 4 bytes,
+         * 97/03/18
+         */
+        if (memcmp(weak_keys[i], key, sizeof(DES_cblock)) == 0)
+            return (1);
+    return (0);
+}
+
+/*-
+ * NOW DEFINED IN des_local.h
+ * See ecb_encrypt.c for a pseudo description of these macros.
+ * #define PERM_OP(a,b,t,n,m) ((t)=((((a)>>(n))^(b))&(m)),\
+ *      (b)^=(t),\
+ *      (a)=((a)^((t)<<(n))))
+ */
+
+#define HPERM_OP(a,t,n,m) ((t)=((((a)<<(16-(n)))^(a))&(m)),\
+        (a)=(a)^(t)^(t>>(16-(n))))
+
+static const DES_LONG des_skb[8][64] = {
+    {
+     /* for C bits (numbered as per FIPS 46) 1 2 3 4 5 6 */
+     0x00000000L, 0x00000010L, 0x20000000L, 0x20000010L,
+     0x00010000L, 0x00010010L, 0x20010000L, 0x20010010L,
+     0x00000800L, 0x00000810L, 0x20000800L, 0x20000810L,
+     0x00010800L, 0x00010810L, 0x20010800L, 0x20010810L,
+     0x00000020L, 0x00000030L, 0x20000020L, 0x20000030L,
+     0x00010020L, 0x00010030L, 0x20010020L, 0x20010030L,
+     0x00000820L, 0x00000830L, 0x20000820L, 0x20000830L,
+     0x00010820L, 0x00010830L, 0x20010820L, 0x20010830L,
+     0x00080000L, 0x00080010L, 0x20080000L, 0x20080010L,
+     0x00090000L, 0x00090010L, 0x20090000L, 0x20090010L,
+     0x00080800L, 0x00080810L, 0x20080800L, 0x20080810L,
+     0x00090800L, 0x00090810L, 0x20090800L, 0x20090810L,
+     0x00080020L, 0x00080030L, 0x20080020L, 0x20080030L,
+     0x00090020L, 0x00090030L, 0x20090020L, 0x20090030L,
+     0x00080820L, 0x00080830L, 0x20080820L, 0x20080830L,
+     0x00090820L, 0x00090830L, 0x20090820L, 0x20090830L,
+     },
+    {
+     /* for C bits (numbered as per FIPS 46) 7 8 10 11 12 13 */
+     0x00000000L, 0x02000000L, 0x00002000L, 0x02002000L,
+     0x00200000L, 0x02200000L, 0x00202000L, 0x02202000L,
+     0x00000004L, 0x02000004L, 0x00002004L, 0x02002004L,
+     0x00200004L, 0x02200004L, 0x00202004L, 0x02202004L,
+     0x00000400L, 0x02000400L, 0x00002400L, 0x02002400L,
+     0x00200400L, 0x02200400L, 0x00202400L, 0x02202400L,
+     0x00000404L, 0x02000404L, 0x00002404L, 0x02002404L,
+     0x00200404L, 0x02200404L, 0x00202404L, 0x02202404L,
+     0x10000000L, 0x12000000L, 0x10002000L, 0x12002000L,
+     0x10200000L, 0x12200000L, 0x10202000L, 0x12202000L,
+     0x10000004L, 0x12000004L, 0x10002004L, 0x12002004L,
+     0x10200004L, 0x12200004L, 0x10202004L, 0x12202004L,
+     0x10000400L, 0x12000400L, 0x10002400L, 0x12002400L,
+     0x10200400L, 0x12200400L, 0x10202400L, 0x12202400L,
+     0x10000404L, 0x12000404L, 0x10002404L, 0x12002404L,
+     0x10200404L, 0x12200404L, 0x10202404L, 0x12202404L,
+     },
+    {
+     /* for C bits (numbered as per FIPS 46) 14 15 16 17 19 20 */
+     0x00000000L, 0x00000001L, 0x00040000L, 0x00040001L,
+     0x01000000L, 0x01000001L, 0x01040000L, 0x01040001L,
+     0x00000002L, 0x00000003L, 0x00040002L, 0x00040003L,
+     0x01000002L, 0x01000003L, 0x01040002L, 0x01040003L,
+     0x00000200L, 0x00000201L, 0x00040200L, 0x00040201L,
+     0x01000200L, 0x01000201L, 0x01040200L, 0x01040201L,
+     0x00000202L, 0x00000203L, 0x00040202L, 0x00040203L,
+     0x01000202L, 0x01000203L, 0x01040202L, 0x01040203L,
+     0x08000000L, 0x08000001L, 0x08040000L, 0x08040001L,
+     0x09000000L, 0x09000001L, 0x09040000L, 0x09040001L,
+     0x08000002L, 0x08000003L, 0x08040002L, 0x08040003L,
+     0x09000002L, 0x09000003L, 0x09040002L, 0x09040003L,
+     0x08000200L, 0x08000201L, 0x08040200L, 0x08040201L,
+     0x09000200L, 0x09000201L, 0x09040200L, 0x09040201L,
+     0x08000202L, 0x08000203L, 0x08040202L, 0x08040203L,
+     0x09000202L, 0x09000203L, 0x09040202L, 0x09040203L,
+     },
+    {
+     /* for C bits (numbered as per FIPS 46) 21 23 24 26 27 28 */
+     0x00000000L, 0x00100000L, 0x00000100L, 0x00100100L,
+     0x00000008L, 0x00100008L, 0x00000108L, 0x00100108L,
+     0x00001000L, 0x00101000L, 0x00001100L, 0x00101100L,
+     0x00001008L, 0x00101008L, 0x00001108L, 0x00101108L,
+     0x04000000L, 0x04100000L, 0x04000100L, 0x04100100L,
+     0x04000008L, 0x04100008L, 0x04000108L, 0x04100108L,
+     0x04001000L, 0x04101000L, 0x04001100L, 0x04101100L,
+     0x04001008L, 0x04101008L, 0x04001108L, 0x04101108L,
+     0x00020000L, 0x00120000L, 0x00020100L, 0x00120100L,
+     0x00020008L, 0x00120008L, 0x00020108L, 0x00120108L,
+     0x00021000L, 0x00121000L, 0x00021100L, 0x00121100L,
+     0x00021008L, 0x00121008L, 0x00021108L, 0x00121108L,
+     0x04020000L, 0x04120000L, 0x04020100L, 0x04120100L,
+     0x04020008L, 0x04120008L, 0x04020108L, 0x04120108L,
+     0x04021000L, 0x04121000L, 0x04021100L, 0x04121100L,
+     0x04021008L, 0x04121008L, 0x04021108L, 0x04121108L,
+     },
+    {
+     /* for D bits (numbered as per FIPS 46) 1 2 3 4 5 6 */
+     0x00000000L, 0x10000000L, 0x00010000L, 0x10010000L,
+     0x00000004L, 0x10000004L, 0x00010004L, 0x10010004L,
+     0x20000000L, 0x30000000L, 0x20010000L, 0x30010000L,
+     0x20000004L, 0x30000004L, 0x20010004L, 0x30010004L,
+     0x00100000L, 0x10100000L, 0x00110000L, 0x10110000L,
+     0x00100004L, 0x10100004L, 0x00110004L, 0x10110004L,
+     0x20100000L, 0x30100000L, 0x20110000L, 0x30110000L,
+     0x20100004L, 0x30100004L, 0x20110004L, 0x30110004L,
+     0x00001000L, 0x10001000L, 0x00011000L, 0x10011000L,
+     0x00001004L, 0x10001004L, 0x00011004L, 0x10011004L,
+     0x20001000L, 0x30001000L, 0x20011000L, 0x30011000L,
+     0x20001004L, 0x30001004L, 0x20011004L, 0x30011004L,
+     0x00101000L, 0x10101000L, 0x00111000L, 0x10111000L,
+     0x00101004L, 0x10101004L, 0x00111004L, 0x10111004L,
+     0x20101000L, 0x30101000L, 0x20111000L, 0x30111000L,
+     0x20101004L, 0x30101004L, 0x20111004L, 0x30111004L,
+     },
+    {
+     /* for D bits (numbered as per FIPS 46) 8 9 11 12 13 14 */
+     0x00000000L, 0x08000000L, 0x00000008L, 0x08000008L,
+     0x00000400L, 0x08000400L, 0x00000408L, 0x08000408L,
+     0x00020000L, 0x08020000L, 0x00020008L, 0x08020008L,
+     0x00020400L, 0x08020400L, 0x00020408L, 0x08020408L,
+     0x00000001L, 0x08000001L, 0x00000009L, 0x08000009L,
+     0x00000401L, 0x08000401L, 0x00000409L, 0x08000409L,
+     0x00020001L, 0x08020001L, 0x00020009L, 0x08020009L,
+     0x00020401L, 0x08020401L, 0x00020409L, 0x08020409L,
+     0x02000000L, 0x0A000000L, 0x02000008L, 0x0A000008L,
+     0x02000400L, 0x0A000400L, 0x02000408L, 0x0A000408L,
+     0x02020000L, 0x0A020000L, 0x02020008L, 0x0A020008L,
+     0x02020400L, 0x0A020400L, 0x02020408L, 0x0A020408L,
+     0x02000001L, 0x0A000001L, 0x02000009L, 0x0A000009L,
+     0x02000401L, 0x0A000401L, 0x02000409L, 0x0A000409L,
+     0x02020001L, 0x0A020001L, 0x02020009L, 0x0A020009L,
+     0x02020401L, 0x0A020401L, 0x02020409L, 0x0A020409L,
+     },
+    {
+     /* for D bits (numbered as per FIPS 46) 16 17 18 19 20 21 */
+     0x00000000L, 0x00000100L, 0x00080000L, 0x00080100L,
+     0x01000000L, 0x01000100L, 0x01080000L, 0x01080100L,
+     0x00000010L, 0x00000110L, 0x00080010L, 0x00080110L,
+     0x01000010L, 0x01000110L, 0x01080010L, 0x01080110L,
+     0x00200000L, 0x00200100L, 0x00280000L, 0x00280100L,
+     0x01200000L, 0x01200100L, 0x01280000L, 0x01280100L,
+     0x00200010L, 0x00200110L, 0x00280010L, 0x00280110L,
+     0x01200010L, 0x01200110L, 0x01280010L, 0x01280110L,
+     0x00000200L, 0x00000300L, 0x00080200L, 0x00080300L,
+     0x01000200L, 0x01000300L, 0x01080200L, 0x01080300L,
+     0x00000210L, 0x00000310L, 0x00080210L, 0x00080310L,
+     0x01000210L, 0x01000310L, 0x01080210L, 0x01080310L,
+     0x00200200L, 0x00200300L, 0x00280200L, 0x00280300L,
+     0x01200200L, 0x01200300L, 0x01280200L, 0x01280300L,
+     0x00200210L, 0x00200310L, 0x00280210L, 0x00280310L,
+     0x01200210L, 0x01200310L, 0x01280210L, 0x01280310L,
+     },
+    {
+     /* for D bits (numbered as per FIPS 46) 22 23 24 25 27 28 */
+     0x00000000L, 0x04000000L, 0x00040000L, 0x04040000L,
+     0x00000002L, 0x04000002L, 0x00040002L, 0x04040002L,
+     0x00002000L, 0x04002000L, 0x00042000L, 0x04042000L,
+     0x00002002L, 0x04002002L, 0x00042002L, 0x04042002L,
+     0x00000020L, 0x04000020L, 0x00040020L, 0x04040020L,
+     0x00000022L, 0x04000022L, 0x00040022L, 0x04040022L,
+     0x00002020L, 0x04002020L, 0x00042020L, 0x04042020L,
+     0x00002022L, 0x04002022L, 0x00042022L, 0x04042022L,
+     0x00000800L, 0x04000800L, 0x00040800L, 0x04040800L,
+     0x00000802L, 0x04000802L, 0x00040802L, 0x04040802L,
+     0x00002800L, 0x04002800L, 0x00042800L, 0x04042800L,
+     0x00002802L, 0x04002802L, 0x00042802L, 0x04042802L,
+     0x00000820L, 0x04000820L, 0x00040820L, 0x04040820L,
+     0x00000822L, 0x04000822L, 0x00040822L, 0x04040822L,
+     0x00002820L, 0x04002820L, 0x00042820L, 0x04042820L,
+     0x00002822L, 0x04002822L, 0x00042822L, 0x04042822L,
+     }
+};
+
+int DES_set_key(const_DES_cblock *key, DES_key_schedule *schedule)
+{
+    if (DES_check_key) {
+        return DES_set_key_checked(key, schedule);
+    } else {
+        DES_set_key_unchecked(key, schedule);
+        return 0;
+    }
+}
+
+/*-
+ * return 0 if key parity is odd (correct),
+ * return -1 if key parity error,
+ * return -2 if illegal weak key.
+ */
+int DES_set_key_checked(const_DES_cblock *key, DES_key_schedule *schedule)
+{
+    if (!DES_check_key_parity(key))
+        return (-1);
+    if (DES_is_weak_key(key))
+        return (-2);
+    DES_set_key_unchecked(key, schedule);
+    return 0;
+}
+
+void DES_set_key_unchecked(const_DES_cblock *key, DES_key_schedule *schedule)
+{
+    static const int shifts2[16] =
+        { 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0 };
+    register DES_LONG c, d, t, s, t2;
+    register const unsigned char *in;
+    register DES_LONG *k;
+    register int i;
+
+#ifdef OPENBSD_DEV_CRYPTO
+    memcpy(schedule->key, key, sizeof schedule->key);
+    schedule->session = NULL;
+#endif
+    k = &schedule->ks->deslong[0];
+    in = &(*key)[0];
+
+    c2l(in, c);
+    c2l(in, d);
+
+    /*
+     * do PC1 in 47 simple operations :-) Thanks to John Fletcher
+     * (john_fletcher@lccmail.ocf.llnl.gov) for the inspiration. :-)
+     */
+    PERM_OP(d, c, t, 4, 0x0f0f0f0fL);
+    HPERM_OP(c, t, -2, 0xcccc0000L);
+    HPERM_OP(d, t, -2, 0xcccc0000L);
+    PERM_OP(d, c, t, 1, 0x55555555L);
+    PERM_OP(c, d, t, 8, 0x00ff00ffL);
+    PERM_OP(d, c, t, 1, 0x55555555L);
+    d = (((d & 0x000000ffL) << 16L) | (d & 0x0000ff00L) |
+         ((d & 0x00ff0000L) >> 16L) | ((c & 0xf0000000L) >> 4L));
+    c &= 0x0fffffffL;
+
+    for (i = 0; i < ITERATIONS; i++) {
+        if (shifts2[i]) {
+            c = ((c >> 2L) | (c << 26L));
+            d = ((d >> 2L) | (d << 26L));
+        } else {
+            c = ((c >> 1L) | (c << 27L));
+            d = ((d >> 1L) | (d << 27L));
+        }
+        c &= 0x0fffffffL;
+        d &= 0x0fffffffL;
+        /*
+         * could be a few less shifts but I am to lazy at this point in time
+         * to investigate
+         */
+        s = des_skb[0][(c) & 0x3f] |
+            des_skb[1][((c >> 6L) & 0x03) | ((c >> 7L) & 0x3c)] |
+            des_skb[2][((c >> 13L) & 0x0f) | ((c >> 14L) & 0x30)] |
+            des_skb[3][((c >> 20L) & 0x01) | ((c >> 21L) & 0x06) |
+                       ((c >> 22L) & 0x38)];
+        t = des_skb[4][(d) & 0x3f] |
+            des_skb[5][((d >> 7L) & 0x03) | ((d >> 8L) & 0x3c)] |
+            des_skb[6][(d >> 15L) & 0x3f] |
+            des_skb[7][((d >> 21L) & 0x0f) | ((d >> 22L) & 0x30)];
+
+        /* table contained 0213 4657 */
+        t2 = ((t << 16L) | (s & 0x0000ffffL)) & 0xffffffffL;
+        *(k++) = ROTATE(t2, 30) & 0xffffffffL;
+
+        t2 = ((s >> 16L) | (t & 0xffff0000L));
+        *(k++) = ROTATE(t2, 26) & 0xffffffffL;
+    }
+}
+
+int DES_key_sched(const_DES_cblock *key, DES_key_schedule *schedule)
+{
+    return (DES_set_key(key, schedule));
+}
+
+/*-
+#undef des_fixup_key_parity
+void des_fixup_key_parity(des_cblock *key)
+        {
+        des_set_odd_parity(key);
+        }
+*/
diff --git a/crypto/des/spr.h b/crypto/des/spr.h
new file mode 100644
index 000000000..42adfbf6e
--- /dev/null
+++ b/crypto/des/spr.h
@@ -0,0 +1,163 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+OPENSSL_GLOBAL const DES_LONG DES_SPtrans[8][64] = {
+    {
+        /* nibble 0 */
+        0x02080800L, 0x00080000L, 0x02000002L, 0x02080802L,
+        0x02000000L, 0x00080802L, 0x00080002L, 0x02000002L,
+        0x00080802L, 0x02080800L, 0x02080000L, 0x00000802L,
+        0x02000802L, 0x02000000L, 0x00000000L, 0x00080002L,
+        0x00080000L, 0x00000002L, 0x02000800L, 0x00080800L,
+        0x02080802L, 0x02080000L, 0x00000802L, 0x02000800L,
+        0x00000002L, 0x00000800L, 0x00080800L, 0x02080002L,
+        0x00000800L, 0x02000802L, 0x02080002L, 0x00000000L,
+        0x00000000L, 0x02080802L, 0x02000800L, 0x00080002L,
+        0x02080800L, 0x00080000L, 0x00000802L, 0x02000800L,
+        0x02080002L, 0x00000800L, 0x00080800L, 0x02000002L,
+        0x00080802L, 0x00000002L, 0x02000002L, 0x02080000L,
+        0x02080802L, 0x00080800L, 0x02080000L, 0x02000802L,
+        0x02000000L, 0x00000802L, 0x00080002L, 0x00000000L,
+        0x00080000L, 0x02000000L, 0x02000802L, 0x02080800L,
+        0x00000002L, 0x02080002L, 0x00000800L, 0x00080802L,
+    },
+    {
+        /* nibble 1 */
+        0x40108010L, 0x00000000L, 0x00108000L, 0x40100000L,
+        0x40000010L, 0x00008010L, 0x40008000L, 0x00108000L,
+        0x00008000L, 0x40100010L, 0x00000010L, 0x40008000L,
+        0x00100010L, 0x40108000L, 0x40100000L, 0x00000010L,
+        0x00100000L, 0x40008010L, 0x40100010L, 0x00008000L,
+        0x00108010L, 0x40000000L, 0x00000000L, 0x00100010L,
+        0x40008010L, 0x00108010L, 0x40108000L, 0x40000010L,
+        0x40000000L, 0x00100000L, 0x00008010L, 0x40108010L,
+        0x00100010L, 0x40108000L, 0x40008000L, 0x00108010L,
+        0x40108010L, 0x00100010L, 0x40000010L, 0x00000000L,
+        0x40000000L, 0x00008010L, 0x00100000L, 0x40100010L,
+        0x00008000L, 0x40000000L, 0x00108010L, 0x40008010L,
+        0x40108000L, 0x00008000L, 0x00000000L, 0x40000010L,
+        0x00000010L, 0x40108010L, 0x00108000L, 0x40100000L,
+        0x40100010L, 0x00100000L, 0x00008010L, 0x40008000L,
+        0x40008010L, 0x00000010L, 0x40100000L, 0x00108000L,
+    },
+    {
+        /* nibble 2 */
+        0x04000001L, 0x04040100L, 0x00000100L, 0x04000101L,
+        0x00040001L, 0x04000000L, 0x04000101L, 0x00040100L,
+        0x04000100L, 0x00040000L, 0x04040000L, 0x00000001L,
+        0x04040101L, 0x00000101L, 0x00000001L, 0x04040001L,
+        0x00000000L, 0x00040001L, 0x04040100L, 0x00000100L,
+        0x00000101L, 0x04040101L, 0x00040000L, 0x04000001L,
+        0x04040001L, 0x04000100L, 0x00040101L, 0x04040000L,
+        0x00040100L, 0x00000000L, 0x04000000L, 0x00040101L,
+        0x04040100L, 0x00000100L, 0x00000001L, 0x00040000L,
+        0x00000101L, 0x00040001L, 0x04040000L, 0x04000101L,
+        0x00000000L, 0x04040100L, 0x00040100L, 0x04040001L,
+        0x00040001L, 0x04000000L, 0x04040101L, 0x00000001L,
+        0x00040101L, 0x04000001L, 0x04000000L, 0x04040101L,
+        0x00040000L, 0x04000100L, 0x04000101L, 0x00040100L,
+        0x04000100L, 0x00000000L, 0x04040001L, 0x00000101L,
+        0x04000001L, 0x00040101L, 0x00000100L, 0x04040000L,
+    },
+    {
+        /* nibble 3 */
+        0x00401008L, 0x10001000L, 0x00000008L, 0x10401008L,
+        0x00000000L, 0x10400000L, 0x10001008L, 0x00400008L,
+        0x10401000L, 0x10000008L, 0x10000000L, 0x00001008L,
+        0x10000008L, 0x00401008L, 0x00400000L, 0x10000000L,
+        0x10400008L, 0x00401000L, 0x00001000L, 0x00000008L,
+        0x00401000L, 0x10001008L, 0x10400000L, 0x00001000L,
+        0x00001008L, 0x00000000L, 0x00400008L, 0x10401000L,
+        0x10001000L, 0x10400008L, 0x10401008L, 0x00400000L,
+        0x10400008L, 0x00001008L, 0x00400000L, 0x10000008L,
+        0x00401000L, 0x10001000L, 0x00000008L, 0x10400000L,
+        0x10001008L, 0x00000000L, 0x00001000L, 0x00400008L,
+        0x00000000L, 0x10400008L, 0x10401000L, 0x00001000L,
+        0x10000000L, 0x10401008L, 0x00401008L, 0x00400000L,
+        0x10401008L, 0x00000008L, 0x10001000L, 0x00401008L,
+        0x00400008L, 0x00401000L, 0x10400000L, 0x10001008L,
+        0x00001008L, 0x10000000L, 0x10000008L, 0x10401000L,
+    },
+    {
+        /* nibble 4 */
+        0x08000000L, 0x00010000L, 0x00000400L, 0x08010420L,
+        0x08010020L, 0x08000400L, 0x00010420L, 0x08010000L,
+        0x00010000L, 0x00000020L, 0x08000020L, 0x00010400L,
+        0x08000420L, 0x08010020L, 0x08010400L, 0x00000000L,
+        0x00010400L, 0x08000000L, 0x00010020L, 0x00000420L,
+        0x08000400L, 0x00010420L, 0x00000000L, 0x08000020L,
+        0x00000020L, 0x08000420L, 0x08010420L, 0x00010020L,
+        0x08010000L, 0x00000400L, 0x00000420L, 0x08010400L,
+        0x08010400L, 0x08000420L, 0x00010020L, 0x08010000L,
+        0x00010000L, 0x00000020L, 0x08000020L, 0x08000400L,
+        0x08000000L, 0x00010400L, 0x08010420L, 0x00000000L,
+        0x00010420L, 0x08000000L, 0x00000400L, 0x00010020L,
+        0x08000420L, 0x00000400L, 0x00000000L, 0x08010420L,
+        0x08010020L, 0x08010400L, 0x00000420L, 0x00010000L,
+        0x00010400L, 0x08010020L, 0x08000400L, 0x00000420L,
+        0x00000020L, 0x00010420L, 0x08010000L, 0x08000020L,
+    },
+    {
+        /* nibble 5 */
+        0x80000040L, 0x00200040L, 0x00000000L, 0x80202000L,
+        0x00200040L, 0x00002000L, 0x80002040L, 0x00200000L,
+        0x00002040L, 0x80202040L, 0x00202000L, 0x80000000L,
+        0x80002000L, 0x80000040L, 0x80200000L, 0x00202040L,
+        0x00200000L, 0x80002040L, 0x80200040L, 0x00000000L,
+        0x00002000L, 0x00000040L, 0x80202000L, 0x80200040L,
+        0x80202040L, 0x80200000L, 0x80000000L, 0x00002040L,
+        0x00000040L, 0x00202000L, 0x00202040L, 0x80002000L,
+        0x00002040L, 0x80000000L, 0x80002000L, 0x00202040L,
+        0x80202000L, 0x00200040L, 0x00000000L, 0x80002000L,
+        0x80000000L, 0x00002000L, 0x80200040L, 0x00200000L,
+        0x00200040L, 0x80202040L, 0x00202000L, 0x00000040L,
+        0x80202040L, 0x00202000L, 0x00200000L, 0x80002040L,
+        0x80000040L, 0x80200000L, 0x00202040L, 0x00000000L,
+        0x00002000L, 0x80000040L, 0x80002040L, 0x80202000L,
+        0x80200000L, 0x00002040L, 0x00000040L, 0x80200040L,
+    },
+    {
+        /* nibble 6 */
+        0x00004000L, 0x00000200L, 0x01000200L, 0x01000004L,
+        0x01004204L, 0x00004004L, 0x00004200L, 0x00000000L,
+        0x01000000L, 0x01000204L, 0x00000204L, 0x01004000L,
+        0x00000004L, 0x01004200L, 0x01004000L, 0x00000204L,
+        0x01000204L, 0x00004000L, 0x00004004L, 0x01004204L,
+        0x00000000L, 0x01000200L, 0x01000004L, 0x00004200L,
+        0x01004004L, 0x00004204L, 0x01004200L, 0x00000004L,
+        0x00004204L, 0x01004004L, 0x00000200L, 0x01000000L,
+        0x00004204L, 0x01004000L, 0x01004004L, 0x00000204L,
+        0x00004000L, 0x00000200L, 0x01000000L, 0x01004004L,
+        0x01000204L, 0x00004204L, 0x00004200L, 0x00000000L,
+        0x00000200L, 0x01000004L, 0x00000004L, 0x01000200L,
+        0x00000000L, 0x01000204L, 0x01000200L, 0x00004200L,
+        0x00000204L, 0x00004000L, 0x01004204L, 0x01000000L,
+        0x01004200L, 0x00000004L, 0x00004004L, 0x01004204L,
+        0x01000004L, 0x01004200L, 0x01004000L, 0x00004004L,
+    },
+    {
+        /* nibble 7 */
+        0x20800080L, 0x20820000L, 0x00020080L, 0x00000000L,
+        0x20020000L, 0x00800080L, 0x20800000L, 0x20820080L,
+        0x00000080L, 0x20000000L, 0x00820000L, 0x00020080L,
+        0x00820080L, 0x20020080L, 0x20000080L, 0x20800000L,
+        0x00020000L, 0x00820080L, 0x00800080L, 0x20020000L,
+        0x20820080L, 0x20000080L, 0x00000000L, 0x00820000L,
+        0x20000000L, 0x00800000L, 0x20020080L, 0x20800080L,
+        0x00800000L, 0x00020000L, 0x20820000L, 0x00000080L,
+        0x00800000L, 0x00020000L, 0x20000080L, 0x20820080L,
+        0x00020080L, 0x20000000L, 0x00000000L, 0x00820000L,
+        0x20800080L, 0x20020080L, 0x20020000L, 0x00800080L,
+        0x20820000L, 0x00000080L, 0x00800080L, 0x20020000L,
+        0x20820080L, 0x00800000L, 0x20800000L, 0x20000080L,
+        0x00820000L, 0x00020080L, 0x20020080L, 0x20800000L,
+        0x00000080L, 0x20820000L, 0x00820080L, 0x00000000L,
+        0x20000000L, 0x20800080L, 0x00020000L, 0x00820080L,
+    }
+};
diff --git a/crypto/des/str2key.c b/crypto/des/str2key.c
new file mode 100644
index 000000000..78998a1cd
--- /dev/null
+++ b/crypto/des/str2key.c
@@ -0,0 +1,97 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "des_locl.h"
+
+void DES_string_to_key(const char *str, DES_cblock *key)
+{
+    DES_key_schedule ks;
+    int i, length;
+
+    memset(key, 0, 8);
+    length = strlen(str);
+#ifdef OLD_STR_TO_KEY
+    for (i = 0; i < length; i++)
+        (*key)[i % 8] ^= (str[i] << 1);
+#else                           /* MIT COMPATIBLE */
+    for (i = 0; i < length; i++) {
+        register unsigned char j = str[i];
+
+        if ((i % 16) < 8)
+            (*key)[i % 8] ^= (j << 1);
+        else {
+            /* Reverse the bit order 05/05/92 eay */
+            j = ((j << 4) & 0xf0) | ((j >> 4) & 0x0f);
+            j = ((j << 2) & 0xcc) | ((j >> 2) & 0x33);
+            j = ((j << 1) & 0xaa) | ((j >> 1) & 0x55);
+            (*key)[7 - (i % 8)] ^= j;
+        }
+    }
+#endif
+    DES_set_odd_parity(key);
+    DES_set_key_unchecked(key, &ks);
+    DES_cbc_cksum((const unsigned char *)str, key, length, &ks, key);
+    OPENSSL_cleanse(&ks, sizeof(ks));
+    DES_set_odd_parity(key);
+}
+
+void DES_string_to_2keys(const char *str, DES_cblock *key1, DES_cblock *key2)
+{
+    DES_key_schedule ks;
+    int i, length;
+
+    memset(key1, 0, 8);
+    memset(key2, 0, 8);
+    length = strlen(str);
+#ifdef OLD_STR_TO_KEY
+    if (length <= 8) {
+        for (i = 0; i < length; i++) {
+            (*key2)[i] = (*key1)[i] = (str[i] << 1);
+        }
+    } else {
+        for (i = 0; i < length; i++) {
+            if ((i / 8) & 1)
+                (*key2)[i % 8] ^= (str[i] << 1);
+            else
+                (*key1)[i % 8] ^= (str[i] << 1);
+        }
+    }
+#else                           /* MIT COMPATIBLE */
+    for (i = 0; i < length; i++) {
+        register unsigned char j = str[i];
+
+        if ((i % 32) < 16) {
+            if ((i % 16) < 8)
+                (*key1)[i % 8] ^= (j << 1);
+            else
+                (*key2)[i % 8] ^= (j << 1);
+        } else {
+            j = ((j << 4) & 0xf0) | ((j >> 4) & 0x0f);
+            j = ((j << 2) & 0xcc) | ((j >> 2) & 0x33);
+            j = ((j << 1) & 0xaa) | ((j >> 1) & 0x55);
+            if ((i % 16) < 8)
+                (*key1)[7 - (i % 8)] ^= j;
+            else
+                (*key2)[7 - (i % 8)] ^= j;
+        }
+    }
+    if (length <= 8)
+        memcpy(key2, key1, 8);
+#endif
+    DES_set_odd_parity(key1);
+    DES_set_odd_parity(key2);
+    DES_set_key_unchecked(key1, &ks);
+    DES_cbc_cksum((const unsigned char *)str, key1, length, &ks, key1);
+    DES_set_key_unchecked(key2, &ks);
+    DES_cbc_cksum((const unsigned char *)str, key2, length, &ks, key2);
+    OPENSSL_cleanse(&ks, sizeof(ks));
+    DES_set_odd_parity(key1);
+    DES_set_odd_parity(key2);
+}
diff --git a/crypto/des/xcbc_enc.c b/crypto/des/xcbc_enc.c
new file mode 100644
index 000000000..c4e455d9b
--- /dev/null
+++ b/crypto/des/xcbc_enc.c
@@ -0,0 +1,103 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "des_locl.h"
+
+/* RSA's DESX */
+
+void DES_xcbc_encrypt(const unsigned char *in, unsigned char *out,
+                      long length, DES_key_schedule *schedule,
+                      DES_cblock *ivec, const_DES_cblock *inw,
+                      const_DES_cblock *outw, int enc)
+{
+    register DES_LONG tin0, tin1;
+    register DES_LONG tout0, tout1, xor0, xor1;
+    register DES_LONG inW0, inW1, outW0, outW1;
+    register const unsigned char *in2;
+    register long l = length;
+    DES_LONG tin[2];
+    unsigned char *iv;
+
+    in2 = &(*inw)[0];
+    c2l(in2, inW0);
+    c2l(in2, inW1);
+    in2 = &(*outw)[0];
+    c2l(in2, outW0);
+    c2l(in2, outW1);
+
+    iv = &(*ivec)[0];
+
+    if (enc) {
+        c2l(iv, tout0);
+        c2l(iv, tout1);
+        for (l -= 8; l >= 0; l -= 8) {
+            c2l(in, tin0);
+            c2l(in, tin1);
+            tin0 ^= tout0 ^ inW0;
+            tin[0] = tin0;
+            tin1 ^= tout1 ^ inW1;
+            tin[1] = tin1;
+            DES_encrypt1(tin, schedule, DES_ENCRYPT);
+            tout0 = tin[0] ^ outW0;
+            l2c(tout0, out);
+            tout1 = tin[1] ^ outW1;
+            l2c(tout1, out);
+        }
+        if (l != -8) {
+            c2ln(in, tin0, tin1, l + 8);
+            tin0 ^= tout0 ^ inW0;
+            tin[0] = tin0;
+            tin1 ^= tout1 ^ inW1;
+            tin[1] = tin1;
+            DES_encrypt1(tin, schedule, DES_ENCRYPT);
+            tout0 = tin[0] ^ outW0;
+            l2c(tout0, out);
+            tout1 = tin[1] ^ outW1;
+            l2c(tout1, out);
+        }
+        iv = &(*ivec)[0];
+        l2c(tout0, iv);
+        l2c(tout1, iv);
+    } else {
+        c2l(iv, xor0);
+        c2l(iv, xor1);
+        for (l -= 8; l > 0; l -= 8) {
+            c2l(in, tin0);
+            tin[0] = tin0 ^ outW0;
+            c2l(in, tin1);
+            tin[1] = tin1 ^ outW1;
+            DES_encrypt1(tin, schedule, DES_DECRYPT);
+            tout0 = tin[0] ^ xor0 ^ inW0;
+            tout1 = tin[1] ^ xor1 ^ inW1;
+            l2c(tout0, out);
+            l2c(tout1, out);
+            xor0 = tin0;
+            xor1 = tin1;
+        }
+        if (l != -8) {
+            c2l(in, tin0);
+            tin[0] = tin0 ^ outW0;
+            c2l(in, tin1);
+            tin[1] = tin1 ^ outW1;
+            DES_encrypt1(tin, schedule, DES_DECRYPT);
+            tout0 = tin[0] ^ xor0 ^ inW0;
+            tout1 = tin[1] ^ xor1 ^ inW1;
+            l2cn(tout0, tout1, out, l + 8);
+            xor0 = tin0;
+            xor1 = tin1;
+        }
+
+        iv = &(*ivec)[0];
+        l2c(xor0, iv);
+        l2c(xor1, iv);
+    }
+    tin0 = tin1 = tout0 = tout1 = xor0 = xor1 = 0;
+    inW0 = inW1 = outW0 = outW1 = 0;
+    tin[0] = tin[1] = 0;
+}
diff --git a/crypto/dh/build.info b/crypto/dh/build.info
new file mode 100644
index 000000000..dba93066a
--- /dev/null
+++ b/crypto/dh/build.info
@@ -0,0 +1,4 @@
+LIBS=../../libcrypto
+SOURCE[../../libcrypto]=\
+        dh_asn1.c dh_gen.c dh_key.c dh_lib.c dh_check.c dh_err.c dh_depr.c \
+        dh_ameth.c dh_pmeth.c dh_prn.c dh_rfc5114.c dh_kdf.c dh_meth.c
diff --git a/crypto/dh/dh1024.pem b/crypto/dh/dh1024.pem
new file mode 100644
index 000000000..81d43f6a3
--- /dev/null
+++ b/crypto/dh/dh1024.pem
@@ -0,0 +1,5 @@
+-----BEGIN DH PARAMETERS-----
+MIGHAoGBAJf2QmHKtQXdKCjhPx1ottPb0PMTBH9A6FbaWMsTuKG/K3g6TG1Z1fkq
+/Gz/PWk/eLI9TzFgqVAuPvr3q14a1aZeVUMTgo2oO5/y2UHe6VaJ+trqCTat3xlx
+/mNbIK9HA2RgPC3gWfVLZQrY+gz3ASHHR5nXWHEyvpuZm7m3h+irAgEC
+-----END DH PARAMETERS-----
diff --git a/crypto/dh/dh192.pem b/crypto/dh/dh192.pem
new file mode 100644
index 000000000..521c07271
--- /dev/null
+++ b/crypto/dh/dh192.pem
@@ -0,0 +1,3 @@
+-----BEGIN DH PARAMETERS-----
+MB4CGQDUoLoCULb9LsYm5+/WN992xxbiLQlEuIsCAQM=
+-----END DH PARAMETERS-----
diff --git a/crypto/dh/dh2048.pem b/crypto/dh/dh2048.pem
new file mode 100644
index 000000000..295460f50
--- /dev/null
+++ b/crypto/dh/dh2048.pem
@@ -0,0 +1,16 @@
+-----BEGIN DH PARAMETERS-----
+MIIBCAKCAQEA7ZKJNYJFVcs7+6J2WmkEYb8h86tT0s0h2v94GRFS8Q7B4lW9aG9o
+AFO5Imov5Jo0H2XMWTKKvbHbSe3fpxJmw/0hBHAY8H/W91hRGXKCeyKpNBgdL8sh
+z22SrkO2qCnHJ6PLAMXy5fsKpFmFor2tRfCzrfnggTXu2YOzzK7q62bmqVdmufEo
+pT8igNcLpvZxk5uBDvhakObMym9mX3rAEBoe8PwttggMYiiw7NuJKO4MqD1llGkW
+aVM8U2ATsCun1IKHrRxynkE1/MJ86VHeYYX8GZt2YA8z+GuzylIOKcMH6JAWzMwA
+Gbatw6QwizOhr9iMjZ0B26TE3X8LvW84wwIBAg==
+-----END DH PARAMETERS-----
+-----BEGIN DH PARAMETERS-----
+MIIBCAKCAQEArtA3w73zP6Lu3EOQtwogiXt3AXXpuS6yD4BhzNS1pZFyPHk0/an5
+8ydEkPhQZHKDW+BZJxxPLANaTudWo2YT8TgtvUdN6KSgMiEi6McwqDw+SADuvW+F
+SKUYFxG6VFIxyEP6xBdf+vhJxEDbRG2EYsHDRRtJ76gp9cSKTHusf2R+4AAVGqnt
+gRAbNqtcOar/7FSj+Pl8G3v0Bty0LcCSpbqgYlnv6z+rErQmmC6PPvSz97TDMCok
+yKpCE9hFA1zkqK3TH4FmFvGeIaXJUIBZf4mArWuBTjWFW3nmhESRUn1VK3K3x42N
+a5k6c2+EhrMFiLjxuH6JZoqL0/E93FF9SwIBAg==
+-----END DH PARAMETERS-----
diff --git a/crypto/dh/dh4096.pem b/crypto/dh/dh4096.pem
new file mode 100644
index 000000000..390943a21
--- /dev/null
+++ b/crypto/dh/dh4096.pem
@@ -0,0 +1,14 @@
+-----BEGIN DH PARAMETERS-----
+MIICCAKCAgEA/urRnb6vkPYc/KEGXWnbCIOaKitq7ySIq9dTH7s+Ri59zs77zty7
+vfVlSe6VFTBWgYjD2XKUFmtqq6CqXMhVX5ElUDoYDpAyTH85xqNFLzFC7nKrff/H
+TFKNttp22cZE9V0IPpzedPfnQkE7aUdmF9JnDyv21Z/818O93u1B4r0szdnmEvEF
+bKuIxEHX+bp0ZR7RqE1AeifXGJX3d6tsd2PMAObxwwsv55RGkn50vHO4QxtTARr1
+rRUV5j3B3oPMgC7Offxx+98Xn45B1/G0Prp11anDsR1PGwtaCYipqsvMwQUSJtyE
+EOQWk+yFkeMe4vWv367eEi0Sd/wnC+TSXBE3pYvpYerJ8n1MceI5GQTdarJ77OW9
+bGTHmxRsLSCM1jpLdPja5jjb4siAa6EHc4qN9c/iFKS3PQPJEnX7pXKBRs5f7AF3
+W3RIGt+G9IVNZfXaS7Z/iCpgzgvKCs0VeqN38QsJGtC1aIkwOeyjPNy2G6jJ4yqH
+ovXYt/0mc00vCWeSNS1wren0pR2EiLxX0ypjjgsU1mk/Z3b/+zVf7fZSIB+nDLjb
+NPtUlJCVGnAeBK1J1nG3TQicqowOXoM6ISkdaXj5GPJdXHab2+S7cqhKGv5qC7rR
+jT6sx7RUr0CNTxzLI7muV2/a4tGmj0PSdXQdsZ7tw7gbXlaWT1+MM2MCAQI=
+-----END DH PARAMETERS-----
+
diff --git a/crypto/dh/dh512.pem b/crypto/dh/dh512.pem
new file mode 100644
index 000000000..0a4d863eb
--- /dev/null
+++ b/crypto/dh/dh512.pem
@@ -0,0 +1,4 @@
+-----BEGIN DH PARAMETERS-----
+MEYCQQDaWDwW2YUiidDkr3VvTMqS3UvlM7gE+w/tlO+cikQD7VdGUNNpmdsp13Yn
+a6LT1BLiGPTdHghM9tgAPnxHdOgzAgEC
+-----END DH PARAMETERS-----
diff --git a/crypto/dh/dh_ameth.c b/crypto/dh/dh_ameth.c
new file mode 100644
index 000000000..cd77867de
--- /dev/null
+++ b/crypto/dh/dh_ameth.c
@@ -0,0 +1,870 @@
+/*
+ * Copyright 2006-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+#include "dh_locl.h"
+#include 
+#include "internal/asn1_int.h"
+#include "internal/evp_int.h"
+#include 
+
+/*
+ * i2d/d2i like DH parameter functions which use the appropriate routine for
+ * PKCS#3 DH or X9.42 DH.
+ */
+
+static DH *d2i_dhp(const EVP_PKEY *pkey, const unsigned char **pp,
+                   long length)
+{
+    if (pkey->ameth == &dhx_asn1_meth)
+        return d2i_DHxparams(NULL, pp, length);
+    return d2i_DHparams(NULL, pp, length);
+}
+
+static int i2d_dhp(const EVP_PKEY *pkey, const DH *a, unsigned char **pp)
+{
+    if (pkey->ameth == &dhx_asn1_meth)
+        return i2d_DHxparams(a, pp);
+    return i2d_DHparams(a, pp);
+}
+
+static void int_dh_free(EVP_PKEY *pkey)
+{
+    DH_free(pkey->pkey.dh);
+}
+
+static int dh_pub_decode(EVP_PKEY *pkey, X509_PUBKEY *pubkey)
+{
+    const unsigned char *p, *pm;
+    int pklen, pmlen;
+    int ptype;
+    const void *pval;
+    const ASN1_STRING *pstr;
+    X509_ALGOR *palg;
+    ASN1_INTEGER *public_key = NULL;
+
+    DH *dh = NULL;
+
+    if (!X509_PUBKEY_get0_param(NULL, &p, &pklen, &palg, pubkey))
+        return 0;
+    X509_ALGOR_get0(NULL, &ptype, &pval, palg);
+
+    if (ptype != V_ASN1_SEQUENCE) {
+        DHerr(DH_F_DH_PUB_DECODE, DH_R_PARAMETER_ENCODING_ERROR);
+        goto err;
+    }
+
+    pstr = pval;
+    pm = pstr->data;
+    pmlen = pstr->length;
+
+    if ((dh = d2i_dhp(pkey, &pm, pmlen)) == NULL) {
+        DHerr(DH_F_DH_PUB_DECODE, DH_R_DECODE_ERROR);
+        goto err;
+    }
+
+    if ((public_key = d2i_ASN1_INTEGER(NULL, &p, pklen)) == NULL) {
+        DHerr(DH_F_DH_PUB_DECODE, DH_R_DECODE_ERROR);
+        goto err;
+    }
+
+    /* We have parameters now set public key */
+    if ((dh->pub_key = ASN1_INTEGER_to_BN(public_key, NULL)) == NULL) {
+        DHerr(DH_F_DH_PUB_DECODE, DH_R_BN_DECODE_ERROR);
+        goto err;
+    }
+
+    ASN1_INTEGER_free(public_key);
+    EVP_PKEY_assign(pkey, pkey->ameth->pkey_id, dh);
+    return 1;
+
+ err:
+    ASN1_INTEGER_free(public_key);
+    DH_free(dh);
+    return 0;
+
+}
+
+static int dh_pub_encode(X509_PUBKEY *pk, const EVP_PKEY *pkey)
+{
+    DH *dh;
+    int ptype;
+    unsigned char *penc = NULL;
+    int penclen;
+    ASN1_STRING *str;
+    ASN1_INTEGER *pub_key = NULL;
+
+    dh = pkey->pkey.dh;
+
+    str = ASN1_STRING_new();
+    if (str == NULL) {
+        DHerr(DH_F_DH_PUB_ENCODE, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+    str->length = i2d_dhp(pkey, dh, &str->data);
+    if (str->length <= 0) {
+        DHerr(DH_F_DH_PUB_ENCODE, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+    ptype = V_ASN1_SEQUENCE;
+
+    pub_key = BN_to_ASN1_INTEGER(dh->pub_key, NULL);
+    if (!pub_key)
+        goto err;
+
+    penclen = i2d_ASN1_INTEGER(pub_key, &penc);
+
+    ASN1_INTEGER_free(pub_key);
+
+    if (penclen <= 0) {
+        DHerr(DH_F_DH_PUB_ENCODE, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+
+    if (X509_PUBKEY_set0_param(pk, OBJ_nid2obj(pkey->ameth->pkey_id),
+                               ptype, str, penc, penclen))
+        return 1;
+
+ err:
+    OPENSSL_free(penc);
+    ASN1_STRING_free(str);
+
+    return 0;
+}
+
+/*
+ * PKCS#8 DH is defined in PKCS#11 of all places. It is similar to DH in that
+ * the AlgorithmIdentifier contains the parameters, the private key is
+ * explicitly included and the pubkey must be recalculated.
+ */
+
+static int dh_priv_decode(EVP_PKEY *pkey, const PKCS8_PRIV_KEY_INFO *p8)
+{
+    const unsigned char *p, *pm;
+    int pklen, pmlen;
+    int ptype;
+    const void *pval;
+    const ASN1_STRING *pstr;
+    const X509_ALGOR *palg;
+    ASN1_INTEGER *privkey = NULL;
+
+    DH *dh = NULL;
+
+    if (!PKCS8_pkey_get0(NULL, &p, &pklen, &palg, p8))
+        return 0;
+
+    X509_ALGOR_get0(NULL, &ptype, &pval, palg);
+
+    if (ptype != V_ASN1_SEQUENCE)
+        goto decerr;
+    if ((privkey = d2i_ASN1_INTEGER(NULL, &p, pklen)) == NULL)
+        goto decerr;
+
+    pstr = pval;
+    pm = pstr->data;
+    pmlen = pstr->length;
+    if ((dh = d2i_dhp(pkey, &pm, pmlen)) == NULL)
+        goto decerr;
+
+    /* We have parameters now set private key */
+    if ((dh->priv_key = BN_secure_new()) == NULL
+        || !ASN1_INTEGER_to_BN(privkey, dh->priv_key)) {
+        DHerr(DH_F_DH_PRIV_DECODE, DH_R_BN_ERROR);
+        goto dherr;
+    }
+    /* Calculate public key */
+    if (!DH_generate_key(dh))
+        goto dherr;
+
+    EVP_PKEY_assign(pkey, pkey->ameth->pkey_id, dh);
+
+    ASN1_STRING_clear_free(privkey);
+
+    return 1;
+
+ decerr:
+    DHerr(DH_F_DH_PRIV_DECODE, EVP_R_DECODE_ERROR);
+ dherr:
+    DH_free(dh);
+    ASN1_STRING_clear_free(privkey);
+    return 0;
+}
+
+static int dh_priv_encode(PKCS8_PRIV_KEY_INFO *p8, const EVP_PKEY *pkey)
+{
+    ASN1_STRING *params = NULL;
+    ASN1_INTEGER *prkey = NULL;
+    unsigned char *dp = NULL;
+    int dplen;
+
+    params = ASN1_STRING_new();
+
+    if (params == NULL) {
+        DHerr(DH_F_DH_PRIV_ENCODE, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+
+    params->length = i2d_dhp(pkey, pkey->pkey.dh, ¶ms->data);
+    if (params->length <= 0) {
+        DHerr(DH_F_DH_PRIV_ENCODE, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+    params->type = V_ASN1_SEQUENCE;
+
+    /* Get private key into integer */
+    prkey = BN_to_ASN1_INTEGER(pkey->pkey.dh->priv_key, NULL);
+
+    if (!prkey) {
+        DHerr(DH_F_DH_PRIV_ENCODE, DH_R_BN_ERROR);
+        goto err;
+    }
+
+    dplen = i2d_ASN1_INTEGER(prkey, &dp);
+
+    ASN1_STRING_clear_free(prkey);
+    prkey = NULL;
+
+    if (!PKCS8_pkey_set0(p8, OBJ_nid2obj(pkey->ameth->pkey_id), 0,
+                         V_ASN1_SEQUENCE, params, dp, dplen))
+        goto err;
+
+    return 1;
+
+ err:
+    OPENSSL_free(dp);
+    ASN1_STRING_free(params);
+    ASN1_STRING_clear_free(prkey);
+    return 0;
+}
+
+static int dh_param_decode(EVP_PKEY *pkey,
+                           const unsigned char **pder, int derlen)
+{
+    DH *dh;
+
+    if ((dh = d2i_dhp(pkey, pder, derlen)) == NULL) {
+        DHerr(DH_F_DH_PARAM_DECODE, ERR_R_DH_LIB);
+        return 0;
+    }
+    EVP_PKEY_assign(pkey, pkey->ameth->pkey_id, dh);
+    return 1;
+}
+
+static int dh_param_encode(const EVP_PKEY *pkey, unsigned char **pder)
+{
+    return i2d_dhp(pkey, pkey->pkey.dh, pder);
+}
+
+static int do_dh_print(BIO *bp, const DH *x, int indent, int ptype)
+{
+    int reason = ERR_R_BUF_LIB;
+    const char *ktype = NULL;
+    BIGNUM *priv_key, *pub_key;
+
+    if (ptype == 2)
+        priv_key = x->priv_key;
+    else
+        priv_key = NULL;
+
+    if (ptype > 0)
+        pub_key = x->pub_key;
+    else
+        pub_key = NULL;
+
+    if (x->p == NULL || (ptype == 2 && priv_key == NULL)
+            || (ptype > 0 && pub_key == NULL)) {
+        reason = ERR_R_PASSED_NULL_PARAMETER;
+        goto err;
+    }
+
+    if (ptype == 2)
+        ktype = "DH Private-Key";
+    else if (ptype == 1)
+        ktype = "DH Public-Key";
+    else
+        ktype = "DH Parameters";
+
+    BIO_indent(bp, indent, 128);
+    if (BIO_printf(bp, "%s: (%d bit)\n", ktype, BN_num_bits(x->p)) <= 0)
+        goto err;
+    indent += 4;
+
+    if (!ASN1_bn_print(bp, "private-key:", priv_key, NULL, indent))
+        goto err;
+    if (!ASN1_bn_print(bp, "public-key:", pub_key, NULL, indent))
+        goto err;
+
+    if (!ASN1_bn_print(bp, "prime:", x->p, NULL, indent))
+        goto err;
+    if (!ASN1_bn_print(bp, "generator:", x->g, NULL, indent))
+        goto err;
+    if (x->q && !ASN1_bn_print(bp, "subgroup order:", x->q, NULL, indent))
+        goto err;
+    if (x->j && !ASN1_bn_print(bp, "subgroup factor:", x->j, NULL, indent))
+        goto err;
+    if (x->seed) {
+        int i;
+        BIO_indent(bp, indent, 128);
+        BIO_puts(bp, "seed:");
+        for (i = 0; i < x->seedlen; i++) {
+            if ((i % 15) == 0) {
+                if (BIO_puts(bp, "\n") <= 0
+                    || !BIO_indent(bp, indent + 4, 128))
+                    goto err;
+            }
+            if (BIO_printf(bp, "%02x%s", x->seed[i],
+                           ((i + 1) == x->seedlen) ? "" : ":") <= 0)
+                goto err;
+        }
+        if (BIO_write(bp, "\n", 1) <= 0)
+            return (0);
+    }
+    if (x->counter && !ASN1_bn_print(bp, "counter:", x->counter, NULL, indent))
+        goto err;
+    if (x->length != 0) {
+        BIO_indent(bp, indent, 128);
+        if (BIO_printf(bp, "recommended-private-length: %d bits\n",
+                       (int)x->length) <= 0)
+            goto err;
+    }
+
+    return 1;
+
+ err:
+    DHerr(DH_F_DO_DH_PRINT, reason);
+    return 0;
+}
+
+static int int_dh_size(const EVP_PKEY *pkey)
+{
+    return (DH_size(pkey->pkey.dh));
+}
+
+static int dh_bits(const EVP_PKEY *pkey)
+{
+    return BN_num_bits(pkey->pkey.dh->p);
+}
+
+static int dh_security_bits(const EVP_PKEY *pkey)
+{
+    return DH_security_bits(pkey->pkey.dh);
+}
+
+static int dh_cmp_parameters(const EVP_PKEY *a, const EVP_PKEY *b)
+{
+    if (BN_cmp(a->pkey.dh->p, b->pkey.dh->p) ||
+        BN_cmp(a->pkey.dh->g, b->pkey.dh->g))
+        return 0;
+    else if (a->ameth == &dhx_asn1_meth) {
+        if (BN_cmp(a->pkey.dh->q, b->pkey.dh->q))
+            return 0;
+    }
+    return 1;
+}
+
+static int int_dh_bn_cpy(BIGNUM **dst, const BIGNUM *src)
+{
+    BIGNUM *a;
+    if (src) {
+        a = BN_dup(src);
+        if (!a)
+            return 0;
+    } else
+        a = NULL;
+    BN_free(*dst);
+    *dst = a;
+    return 1;
+}
+
+static int int_dh_param_copy(DH *to, const DH *from, int is_x942)
+{
+    if (is_x942 == -1)
+        is_x942 = ! !from->q;
+    if (!int_dh_bn_cpy(&to->p, from->p))
+        return 0;
+    if (!int_dh_bn_cpy(&to->g, from->g))
+        return 0;
+    if (is_x942) {
+        if (!int_dh_bn_cpy(&to->q, from->q))
+            return 0;
+        if (!int_dh_bn_cpy(&to->j, from->j))
+            return 0;
+        OPENSSL_free(to->seed);
+        to->seed = NULL;
+        to->seedlen = 0;
+        if (from->seed) {
+            to->seed = OPENSSL_memdup(from->seed, from->seedlen);
+            if (!to->seed)
+                return 0;
+            to->seedlen = from->seedlen;
+        }
+    } else
+        to->length = from->length;
+    return 1;
+}
+
+DH *DHparams_dup(DH *dh)
+{
+    DH *ret;
+    ret = DH_new();
+    if (ret == NULL)
+        return NULL;
+    if (!int_dh_param_copy(ret, dh, -1)) {
+        DH_free(ret);
+        return NULL;
+    }
+    return ret;
+}
+
+static int dh_copy_parameters(EVP_PKEY *to, const EVP_PKEY *from)
+{
+    if (to->pkey.dh == NULL) {
+        to->pkey.dh = DH_new();
+        if (to->pkey.dh == NULL)
+            return 0;
+    }
+    return int_dh_param_copy(to->pkey.dh, from->pkey.dh,
+                             from->ameth == &dhx_asn1_meth);
+}
+
+static int dh_missing_parameters(const EVP_PKEY *a)
+{
+    if (a->pkey.dh == NULL || a->pkey.dh->p == NULL || a->pkey.dh->g == NULL)
+        return 1;
+    return 0;
+}
+
+static int dh_pub_cmp(const EVP_PKEY *a, const EVP_PKEY *b)
+{
+    if (dh_cmp_parameters(a, b) == 0)
+        return 0;
+    if (BN_cmp(b->pkey.dh->pub_key, a->pkey.dh->pub_key) != 0)
+        return 0;
+    else
+        return 1;
+}
+
+static int dh_param_print(BIO *bp, const EVP_PKEY *pkey, int indent,
+                          ASN1_PCTX *ctx)
+{
+    return do_dh_print(bp, pkey->pkey.dh, indent, 0);
+}
+
+static int dh_public_print(BIO *bp, const EVP_PKEY *pkey, int indent,
+                           ASN1_PCTX *ctx)
+{
+    return do_dh_print(bp, pkey->pkey.dh, indent, 1);
+}
+
+static int dh_private_print(BIO *bp, const EVP_PKEY *pkey, int indent,
+                            ASN1_PCTX *ctx)
+{
+    return do_dh_print(bp, pkey->pkey.dh, indent, 2);
+}
+
+int DHparams_print(BIO *bp, const DH *x)
+{
+    return do_dh_print(bp, x, 4, 0);
+}
+
+#ifndef OPENSSL_NO_CMS
+static int dh_cms_decrypt(CMS_RecipientInfo *ri);
+static int dh_cms_encrypt(CMS_RecipientInfo *ri);
+#endif
+
+static int dh_pkey_ctrl(EVP_PKEY *pkey, int op, long arg1, void *arg2)
+{
+    switch (op) {
+#ifndef OPENSSL_NO_CMS
+
+    case ASN1_PKEY_CTRL_CMS_ENVELOPE:
+        if (arg1 == 1)
+            return dh_cms_decrypt(arg2);
+        else if (arg1 == 0)
+            return dh_cms_encrypt(arg2);
+        return -2;
+
+    case ASN1_PKEY_CTRL_CMS_RI_TYPE:
+        *(int *)arg2 = CMS_RECIPINFO_AGREE;
+        return 1;
+#endif
+    default:
+        return -2;
+    }
+
+}
+
+const EVP_PKEY_ASN1_METHOD dh_asn1_meth = {
+    EVP_PKEY_DH,
+    EVP_PKEY_DH,
+    0,
+
+    "DH",
+    "OpenSSL PKCS#3 DH method",
+
+    dh_pub_decode,
+    dh_pub_encode,
+    dh_pub_cmp,
+    dh_public_print,
+
+    dh_priv_decode,
+    dh_priv_encode,
+    dh_private_print,
+
+    int_dh_size,
+    dh_bits,
+    dh_security_bits,
+
+    dh_param_decode,
+    dh_param_encode,
+    dh_missing_parameters,
+    dh_copy_parameters,
+    dh_cmp_parameters,
+    dh_param_print,
+    0,
+
+    int_dh_free,
+    0
+};
+
+const EVP_PKEY_ASN1_METHOD dhx_asn1_meth = {
+    EVP_PKEY_DHX,
+    EVP_PKEY_DHX,
+    0,
+
+    "X9.42 DH",
+    "OpenSSL X9.42 DH method",
+
+    dh_pub_decode,
+    dh_pub_encode,
+    dh_pub_cmp,
+    dh_public_print,
+
+    dh_priv_decode,
+    dh_priv_encode,
+    dh_private_print,
+
+    int_dh_size,
+    dh_bits,
+    dh_security_bits,
+
+    dh_param_decode,
+    dh_param_encode,
+    dh_missing_parameters,
+    dh_copy_parameters,
+    dh_cmp_parameters,
+    dh_param_print,
+    0,
+
+    int_dh_free,
+    dh_pkey_ctrl
+};
+
+#ifndef OPENSSL_NO_CMS
+
+static int dh_cms_set_peerkey(EVP_PKEY_CTX *pctx,
+                              X509_ALGOR *alg, ASN1_BIT_STRING *pubkey)
+{
+    const ASN1_OBJECT *aoid;
+    int atype;
+    const void *aval;
+    ASN1_INTEGER *public_key = NULL;
+    int rv = 0;
+    EVP_PKEY *pkpeer = NULL, *pk = NULL;
+    DH *dhpeer = NULL;
+    const unsigned char *p;
+    int plen;
+
+    X509_ALGOR_get0(&aoid, &atype, &aval, alg);
+    if (OBJ_obj2nid(aoid) != NID_dhpublicnumber)
+        goto err;
+    /* Only absent parameters allowed in RFC XXXX */
+    if (atype != V_ASN1_UNDEF && atype == V_ASN1_NULL)
+        goto err;
+
+    pk = EVP_PKEY_CTX_get0_pkey(pctx);
+    if (!pk)
+        goto err;
+    if (pk->type != EVP_PKEY_DHX)
+        goto err;
+    /* Get parameters from parent key */
+    dhpeer = DHparams_dup(pk->pkey.dh);
+    /* We have parameters now set public key */
+    plen = ASN1_STRING_length(pubkey);
+    p = ASN1_STRING_get0_data(pubkey);
+    if (!p || !plen)
+        goto err;
+
+    if ((public_key = d2i_ASN1_INTEGER(NULL, &p, plen)) == NULL) {
+        DHerr(DH_F_DH_CMS_SET_PEERKEY, DH_R_DECODE_ERROR);
+        goto err;
+    }
+
+    /* We have parameters now set public key */
+    if ((dhpeer->pub_key = ASN1_INTEGER_to_BN(public_key, NULL)) == NULL) {
+        DHerr(DH_F_DH_CMS_SET_PEERKEY, DH_R_BN_DECODE_ERROR);
+        goto err;
+    }
+
+    pkpeer = EVP_PKEY_new();
+    if (pkpeer == NULL)
+        goto err;
+    EVP_PKEY_assign(pkpeer, pk->ameth->pkey_id, dhpeer);
+    dhpeer = NULL;
+    if (EVP_PKEY_derive_set_peer(pctx, pkpeer) > 0)
+        rv = 1;
+ err:
+    ASN1_INTEGER_free(public_key);
+    EVP_PKEY_free(pkpeer);
+    DH_free(dhpeer);
+    return rv;
+}
+
+static int dh_cms_set_shared_info(EVP_PKEY_CTX *pctx, CMS_RecipientInfo *ri)
+{
+    int rv = 0;
+
+    X509_ALGOR *alg, *kekalg = NULL;
+    ASN1_OCTET_STRING *ukm;
+    const unsigned char *p;
+    unsigned char *dukm = NULL;
+    size_t dukmlen = 0;
+    int keylen, plen;
+    const EVP_CIPHER *kekcipher;
+    EVP_CIPHER_CTX *kekctx;
+
+    if (!CMS_RecipientInfo_kari_get0_alg(ri, &alg, &ukm))
+        goto err;
+
+    /*
+     * For DH we only have one OID permissible. If ever any more get defined
+     * we will need something cleverer.
+     */
+    if (OBJ_obj2nid(alg->algorithm) != NID_id_smime_alg_ESDH) {
+        DHerr(DH_F_DH_CMS_SET_SHARED_INFO, DH_R_KDF_PARAMETER_ERROR);
+        goto err;
+    }
+
+    if (EVP_PKEY_CTX_set_dh_kdf_type(pctx, EVP_PKEY_DH_KDF_X9_42) <= 0)
+        goto err;
+
+    if (EVP_PKEY_CTX_set_dh_kdf_md(pctx, EVP_sha1()) <= 0)
+        goto err;
+
+    if (alg->parameter->type != V_ASN1_SEQUENCE)
+        goto err;
+
+    p = alg->parameter->value.sequence->data;
+    plen = alg->parameter->value.sequence->length;
+    kekalg = d2i_X509_ALGOR(NULL, &p, plen);
+    if (!kekalg)
+        goto err;
+    kekctx = CMS_RecipientInfo_kari_get0_ctx(ri);
+    if (!kekctx)
+        goto err;
+    kekcipher = EVP_get_cipherbyobj(kekalg->algorithm);
+    if (!kekcipher || EVP_CIPHER_mode(kekcipher) != EVP_CIPH_WRAP_MODE)
+        goto err;
+    if (!EVP_EncryptInit_ex(kekctx, kekcipher, NULL, NULL, NULL))
+        goto err;
+    if (EVP_CIPHER_asn1_to_param(kekctx, kekalg->parameter) <= 0)
+        goto err;
+
+    keylen = EVP_CIPHER_CTX_key_length(kekctx);
+    if (EVP_PKEY_CTX_set_dh_kdf_outlen(pctx, keylen) <= 0)
+        goto err;
+    /* Use OBJ_nid2obj to ensure we use built in OID that isn't freed */
+    if (EVP_PKEY_CTX_set0_dh_kdf_oid(pctx,
+                                     OBJ_nid2obj(EVP_CIPHER_type(kekcipher)))
+        <= 0)
+        goto err;
+
+    if (ukm) {
+        dukmlen = ASN1_STRING_length(ukm);
+        dukm = OPENSSL_memdup(ASN1_STRING_get0_data(ukm), dukmlen);
+        if (!dukm)
+            goto err;
+    }
+
+    if (EVP_PKEY_CTX_set0_dh_kdf_ukm(pctx, dukm, dukmlen) <= 0)
+        goto err;
+    dukm = NULL;
+
+    rv = 1;
+ err:
+    X509_ALGOR_free(kekalg);
+    OPENSSL_free(dukm);
+    return rv;
+}
+
+static int dh_cms_decrypt(CMS_RecipientInfo *ri)
+{
+    EVP_PKEY_CTX *pctx;
+    pctx = CMS_RecipientInfo_get0_pkey_ctx(ri);
+    if (!pctx)
+        return 0;
+    /* See if we need to set peer key */
+    if (!EVP_PKEY_CTX_get0_peerkey(pctx)) {
+        X509_ALGOR *alg;
+        ASN1_BIT_STRING *pubkey;
+        if (!CMS_RecipientInfo_kari_get0_orig_id(ri, &alg, &pubkey,
+                                                 NULL, NULL, NULL))
+            return 0;
+        if (!alg || !pubkey)
+            return 0;
+        if (!dh_cms_set_peerkey(pctx, alg, pubkey)) {
+            DHerr(DH_F_DH_CMS_DECRYPT, DH_R_PEER_KEY_ERROR);
+            return 0;
+        }
+    }
+    /* Set DH derivation parameters and initialise unwrap context */
+    if (!dh_cms_set_shared_info(pctx, ri)) {
+        DHerr(DH_F_DH_CMS_DECRYPT, DH_R_SHARED_INFO_ERROR);
+        return 0;
+    }
+    return 1;
+}
+
+static int dh_cms_encrypt(CMS_RecipientInfo *ri)
+{
+    EVP_PKEY_CTX *pctx;
+    EVP_PKEY *pkey;
+    EVP_CIPHER_CTX *ctx;
+    int keylen;
+    X509_ALGOR *talg, *wrap_alg = NULL;
+    const ASN1_OBJECT *aoid;
+    ASN1_BIT_STRING *pubkey;
+    ASN1_STRING *wrap_str;
+    ASN1_OCTET_STRING *ukm;
+    unsigned char *penc = NULL, *dukm = NULL;
+    int penclen;
+    size_t dukmlen = 0;
+    int rv = 0;
+    int kdf_type, wrap_nid;
+    const EVP_MD *kdf_md;
+    pctx = CMS_RecipientInfo_get0_pkey_ctx(ri);
+    if (!pctx)
+        return 0;
+    /* Get ephemeral key */
+    pkey = EVP_PKEY_CTX_get0_pkey(pctx);
+    if (!CMS_RecipientInfo_kari_get0_orig_id(ri, &talg, &pubkey,
+                                             NULL, NULL, NULL))
+        goto err;
+    X509_ALGOR_get0(&aoid, NULL, NULL, talg);
+    /* Is everything uninitialised? */
+    if (aoid == OBJ_nid2obj(NID_undef)) {
+        ASN1_INTEGER *pubk = BN_to_ASN1_INTEGER(pkey->pkey.dh->pub_key, NULL);
+        if (!pubk)
+            goto err;
+        /* Set the key */
+
+        penclen = i2d_ASN1_INTEGER(pubk, &penc);
+        ASN1_INTEGER_free(pubk);
+        if (penclen <= 0)
+            goto err;
+        ASN1_STRING_set0(pubkey, penc, penclen);
+        pubkey->flags &= ~(ASN1_STRING_FLAG_BITS_LEFT | 0x07);
+        pubkey->flags |= ASN1_STRING_FLAG_BITS_LEFT;
+
+        penc = NULL;
+        X509_ALGOR_set0(talg, OBJ_nid2obj(NID_dhpublicnumber),
+                        V_ASN1_UNDEF, NULL);
+    }
+
+    /* See if custom parameters set */
+    kdf_type = EVP_PKEY_CTX_get_dh_kdf_type(pctx);
+    if (kdf_type <= 0)
+        goto err;
+    if (!EVP_PKEY_CTX_get_dh_kdf_md(pctx, &kdf_md))
+        goto err;
+
+    if (kdf_type == EVP_PKEY_DH_KDF_NONE) {
+        kdf_type = EVP_PKEY_DH_KDF_X9_42;
+        if (EVP_PKEY_CTX_set_dh_kdf_type(pctx, kdf_type) <= 0)
+            goto err;
+    } else if (kdf_type != EVP_PKEY_DH_KDF_X9_42)
+        /* Unknown KDF */
+        goto err;
+    if (kdf_md == NULL) {
+        /* Only SHA1 supported */
+        kdf_md = EVP_sha1();
+        if (EVP_PKEY_CTX_set_dh_kdf_md(pctx, kdf_md) <= 0)
+            goto err;
+    } else if (EVP_MD_type(kdf_md) != NID_sha1)
+        /* Unsupported digest */
+        goto err;
+
+    if (!CMS_RecipientInfo_kari_get0_alg(ri, &talg, &ukm))
+        goto err;
+
+    /* Get wrap NID */
+    ctx = CMS_RecipientInfo_kari_get0_ctx(ri);
+    wrap_nid = EVP_CIPHER_CTX_type(ctx);
+    if (EVP_PKEY_CTX_set0_dh_kdf_oid(pctx, OBJ_nid2obj(wrap_nid)) <= 0)
+        goto err;
+    keylen = EVP_CIPHER_CTX_key_length(ctx);
+
+    /* Package wrap algorithm in an AlgorithmIdentifier */
+
+    wrap_alg = X509_ALGOR_new();
+    if (wrap_alg == NULL)
+        goto err;
+    wrap_alg->algorithm = OBJ_nid2obj(wrap_nid);
+    wrap_alg->parameter = ASN1_TYPE_new();
+    if (wrap_alg->parameter == NULL)
+        goto err;
+    if (EVP_CIPHER_param_to_asn1(ctx, wrap_alg->parameter) <= 0)
+        goto err;
+    if (ASN1_TYPE_get(wrap_alg->parameter) == NID_undef) {
+        ASN1_TYPE_free(wrap_alg->parameter);
+        wrap_alg->parameter = NULL;
+    }
+
+    if (EVP_PKEY_CTX_set_dh_kdf_outlen(pctx, keylen) <= 0)
+        goto err;
+
+    if (ukm) {
+        dukmlen = ASN1_STRING_length(ukm);
+        dukm = OPENSSL_memdup(ASN1_STRING_get0_data(ukm), dukmlen);
+        if (!dukm)
+            goto err;
+    }
+
+    if (EVP_PKEY_CTX_set0_dh_kdf_ukm(pctx, dukm, dukmlen) <= 0)
+        goto err;
+    dukm = NULL;
+
+    /*
+     * Now need to wrap encoding of wrap AlgorithmIdentifier into parameter
+     * of another AlgorithmIdentifier.
+     */
+    penc = NULL;
+    penclen = i2d_X509_ALGOR(wrap_alg, &penc);
+    if (!penc || !penclen)
+        goto err;
+    wrap_str = ASN1_STRING_new();
+    if (wrap_str == NULL)
+        goto err;
+    ASN1_STRING_set0(wrap_str, penc, penclen);
+    penc = NULL;
+    X509_ALGOR_set0(talg, OBJ_nid2obj(NID_id_smime_alg_ESDH),
+                    V_ASN1_SEQUENCE, wrap_str);
+
+    rv = 1;
+
+ err:
+    OPENSSL_free(penc);
+    X509_ALGOR_free(wrap_alg);
+    return rv;
+}
+
+#endif
diff --git a/crypto/dh/dh_asn1.c b/crypto/dh/dh_asn1.c
new file mode 100644
index 000000000..7c72fd64e
--- /dev/null
+++ b/crypto/dh/dh_asn1.c
@@ -0,0 +1,138 @@
+/*
+ * Copyright 2000-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include "dh_locl.h"
+#include 
+#include 
+
+/* Override the default free and new methods */
+static int dh_cb(int operation, ASN1_VALUE **pval, const ASN1_ITEM *it,
+                 void *exarg)
+{
+    if (operation == ASN1_OP_NEW_PRE) {
+        *pval = (ASN1_VALUE *)DH_new();
+        if (*pval != NULL)
+            return 2;
+        return 0;
+    } else if (operation == ASN1_OP_FREE_PRE) {
+        DH_free((DH *)*pval);
+        *pval = NULL;
+        return 2;
+    }
+    return 1;
+}
+
+ASN1_SEQUENCE_cb(DHparams, dh_cb) = {
+        ASN1_SIMPLE(DH, p, BIGNUM),
+        ASN1_SIMPLE(DH, g, BIGNUM),
+        ASN1_OPT(DH, length, ZLONG),
+} ASN1_SEQUENCE_END_cb(DH, DHparams)
+
+IMPLEMENT_ASN1_ENCODE_FUNCTIONS_const_fname(DH, DHparams, DHparams)
+
+/*
+ * Internal only structures for handling X9.42 DH: this gets translated to or
+ * from a DH structure straight away.
+ */
+
+typedef struct {
+    ASN1_BIT_STRING *seed;
+    BIGNUM *counter;
+} int_dhvparams;
+
+typedef struct {
+    BIGNUM *p;
+    BIGNUM *q;
+    BIGNUM *g;
+    BIGNUM *j;
+    int_dhvparams *vparams;
+} int_dhx942_dh;
+
+ASN1_SEQUENCE(DHvparams) = {
+        ASN1_SIMPLE(int_dhvparams, seed, ASN1_BIT_STRING),
+        ASN1_SIMPLE(int_dhvparams, counter, BIGNUM)
+} static_ASN1_SEQUENCE_END_name(int_dhvparams, DHvparams)
+
+ASN1_SEQUENCE(DHxparams) = {
+        ASN1_SIMPLE(int_dhx942_dh, p, BIGNUM),
+        ASN1_SIMPLE(int_dhx942_dh, g, BIGNUM),
+        ASN1_SIMPLE(int_dhx942_dh, q, BIGNUM),
+        ASN1_OPT(int_dhx942_dh, j, BIGNUM),
+        ASN1_OPT(int_dhx942_dh, vparams, DHvparams),
+} static_ASN1_SEQUENCE_END_name(int_dhx942_dh, DHxparams)
+
+int_dhx942_dh *d2i_int_dhx(int_dhx942_dh **a,
+                           const unsigned char **pp, long length);
+int i2d_int_dhx(const int_dhx942_dh *a, unsigned char **pp);
+
+IMPLEMENT_ASN1_ENCODE_FUNCTIONS_const_fname(int_dhx942_dh, DHxparams, int_dhx)
+
+/* Application public function: read in X9.42 DH parameters into DH structure */
+
+DH *d2i_DHxparams(DH **a, const unsigned char **pp, long length)
+{
+    int_dhx942_dh *dhx = NULL;
+    DH *dh = NULL;
+    dh = DH_new();
+    if (dh == NULL)
+        return NULL;
+    dhx = d2i_int_dhx(NULL, pp, length);
+    if (dhx == NULL) {
+        DH_free(dh);
+        return NULL;
+    }
+
+    if (a) {
+        DH_free(*a);
+        *a = dh;
+    }
+
+    dh->p = dhx->p;
+    dh->q = dhx->q;
+    dh->g = dhx->g;
+    dh->j = dhx->j;
+
+    if (dhx->vparams) {
+        dh->seed = dhx->vparams->seed->data;
+        dh->seedlen = dhx->vparams->seed->length;
+        dh->counter = dhx->vparams->counter;
+        dhx->vparams->seed->data = NULL;
+        ASN1_BIT_STRING_free(dhx->vparams->seed);
+        OPENSSL_free(dhx->vparams);
+        dhx->vparams = NULL;
+    }
+
+    OPENSSL_free(dhx);
+    return dh;
+}
+
+int i2d_DHxparams(const DH *dh, unsigned char **pp)
+{
+    int_dhx942_dh dhx;
+    int_dhvparams dhv;
+    ASN1_BIT_STRING bs;
+    dhx.p = dh->p;
+    dhx.g = dh->g;
+    dhx.q = dh->q;
+    dhx.j = dh->j;
+    if (dh->counter && dh->seed && dh->seedlen > 0) {
+        bs.flags = ASN1_STRING_FLAG_BITS_LEFT;
+        bs.data = dh->seed;
+        bs.length = dh->seedlen;
+        dhv.seed = &bs;
+        dhv.counter = dh->counter;
+        dhx.vparams = &dhv;
+    } else
+        dhx.vparams = NULL;
+
+    return i2d_int_dhx(&dhx, pp);
+}
diff --git a/crypto/dh/dh_check.c b/crypto/dh/dh_check.c
new file mode 100644
index 000000000..3b0fa5903
--- /dev/null
+++ b/crypto/dh/dh_check.c
@@ -0,0 +1,183 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include "dh_locl.h"
+
+/*-
+ * Check that p and g are suitable enough
+ *
+ * p is odd
+ * 1 < g < p - 1
+ */
+
+int DH_check_params(const DH *dh, int *ret)
+{
+    int ok = 0;
+    BIGNUM *tmp = NULL;
+    BN_CTX *ctx = NULL;
+
+    *ret = 0;
+    ctx = BN_CTX_new();
+    if (ctx == NULL)
+        goto err;
+    BN_CTX_start(ctx);
+    tmp = BN_CTX_get(ctx);
+    if (tmp == NULL)
+        goto err;
+
+    if (!BN_is_odd(dh->p))
+        *ret |= DH_CHECK_P_NOT_PRIME;
+    if (BN_is_negative(dh->g) || BN_is_zero(dh->g) || BN_is_one(dh->g))
+        *ret |= DH_NOT_SUITABLE_GENERATOR;
+    if (BN_copy(tmp, dh->p) == NULL || !BN_sub_word(tmp, 1))
+        goto err;
+    if (BN_cmp(dh->g, tmp) >= 0)
+        *ret |= DH_NOT_SUITABLE_GENERATOR;
+
+    ok = 1;
+ err:
+    if (ctx != NULL) {
+        BN_CTX_end(ctx);
+        BN_CTX_free(ctx);
+    }
+    return (ok);
+}
+
+/*-
+ * Check that p is a safe prime and
+ * if g is 2, 3 or 5, check that it is a suitable generator
+ * where
+ * for 2, p mod 24 == 11
+ * for 3, p mod 12 == 5
+ * for 5, p mod 10 == 3 or 7
+ * should hold.
+ */
+
+int DH_check(const DH *dh, int *ret)
+{
+    int ok = 0, r;
+    BN_CTX *ctx = NULL;
+    BN_ULONG l;
+    BIGNUM *t1 = NULL, *t2 = NULL;
+
+    *ret = 0;
+    ctx = BN_CTX_new();
+    if (ctx == NULL)
+        goto err;
+    BN_CTX_start(ctx);
+    t1 = BN_CTX_get(ctx);
+    if (t1 == NULL)
+        goto err;
+    t2 = BN_CTX_get(ctx);
+    if (t2 == NULL)
+        goto err;
+
+    if (dh->q) {
+        if (BN_cmp(dh->g, BN_value_one()) <= 0)
+            *ret |= DH_NOT_SUITABLE_GENERATOR;
+        else if (BN_cmp(dh->g, dh->p) >= 0)
+            *ret |= DH_NOT_SUITABLE_GENERATOR;
+        else {
+            /* Check g^q == 1 mod p */
+            if (!BN_mod_exp(t1, dh->g, dh->q, dh->p, ctx))
+                goto err;
+            if (!BN_is_one(t1))
+                *ret |= DH_NOT_SUITABLE_GENERATOR;
+        }
+        r = BN_is_prime_ex(dh->q, BN_prime_checks, ctx, NULL);
+        if (r < 0)
+            goto err;
+        if (!r)
+            *ret |= DH_CHECK_Q_NOT_PRIME;
+        /* Check p == 1 mod q  i.e. q divides p - 1 */
+        if (!BN_div(t1, t2, dh->p, dh->q, ctx))
+            goto err;
+        if (!BN_is_one(t2))
+            *ret |= DH_CHECK_INVALID_Q_VALUE;
+        if (dh->j && BN_cmp(dh->j, t1))
+            *ret |= DH_CHECK_INVALID_J_VALUE;
+
+    } else if (BN_is_word(dh->g, DH_GENERATOR_2)) {
+        l = BN_mod_word(dh->p, 24);
+        if (l == (BN_ULONG)-1)
+            goto err;
+        if (l != 11)
+            *ret |= DH_NOT_SUITABLE_GENERATOR;
+    } else if (BN_is_word(dh->g, DH_GENERATOR_5)) {
+        l = BN_mod_word(dh->p, 10);
+        if (l == (BN_ULONG)-1)
+            goto err;
+        if ((l != 3) && (l != 7))
+            *ret |= DH_NOT_SUITABLE_GENERATOR;
+    } else
+        *ret |= DH_UNABLE_TO_CHECK_GENERATOR;
+
+    r = BN_is_prime_ex(dh->p, BN_prime_checks, ctx, NULL);
+    if (r < 0)
+        goto err;
+    if (!r)
+        *ret |= DH_CHECK_P_NOT_PRIME;
+    else if (!dh->q) {
+        if (!BN_rshift1(t1, dh->p))
+            goto err;
+        r = BN_is_prime_ex(t1, BN_prime_checks, ctx, NULL);
+        if (r < 0)
+            goto err;
+	if (!r)
+            *ret |= DH_CHECK_P_NOT_SAFE_PRIME;
+    }
+    ok = 1;
+ err:
+    if (ctx != NULL) {
+        BN_CTX_end(ctx);
+        BN_CTX_free(ctx);
+    }
+    return (ok);
+}
+
+int DH_check_pub_key(const DH *dh, const BIGNUM *pub_key, int *ret)
+{
+    int ok = 0;
+    BIGNUM *tmp = NULL;
+    BN_CTX *ctx = NULL;
+
+    *ret = 0;
+    ctx = BN_CTX_new();
+    if (ctx == NULL)
+        goto err;
+    BN_CTX_start(ctx);
+    tmp = BN_CTX_get(ctx);
+    if (tmp == NULL || !BN_set_word(tmp, 1))
+        goto err;
+    if (BN_cmp(pub_key, tmp) <= 0)
+        *ret |= DH_CHECK_PUBKEY_TOO_SMALL;
+    if (BN_copy(tmp, dh->p) == NULL || !BN_sub_word(tmp, 1))
+        goto err;
+    if (BN_cmp(pub_key, tmp) >= 0)
+        *ret |= DH_CHECK_PUBKEY_TOO_LARGE;
+
+    if (dh->q != NULL) {
+        /* Check pub_key^q == 1 mod p */
+        if (!BN_mod_exp(tmp, pub_key, dh->q, dh->p, ctx))
+            goto err;
+        if (!BN_is_one(tmp))
+            *ret |= DH_CHECK_PUBKEY_INVALID;
+    }
+
+    ok = 1;
+ err:
+    if (ctx != NULL) {
+        BN_CTX_end(ctx);
+        BN_CTX_free(ctx);
+    }
+    return (ok);
+}
diff --git a/crypto/dh/dh_depr.c b/crypto/dh/dh_depr.c
new file mode 100644
index 000000000..f8ed1b746
--- /dev/null
+++ b/crypto/dh/dh_depr.c
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2002-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/* This file contains deprecated functions as wrappers to the new ones */
+
+#include 
+#if OPENSSL_API_COMPAT >= 0x00908000L
+NON_EMPTY_TRANSLATION_UNIT
+#else
+
+# include 
+# include "internal/cryptlib.h"
+# include 
+# include 
+
+DH *DH_generate_parameters(int prime_len, int generator,
+                           void (*callback) (int, int, void *), void *cb_arg)
+{
+    BN_GENCB *cb;
+    DH *ret = NULL;
+
+    if ((ret = DH_new()) == NULL)
+        return NULL;
+    cb = BN_GENCB_new();
+    if (cb == NULL) {
+        DH_free(ret);
+        return NULL;
+    }
+
+    BN_GENCB_set_old(cb, callback, cb_arg);
+
+    if (DH_generate_parameters_ex(ret, prime_len, generator, cb)) {
+        BN_GENCB_free(cb);
+        return ret;
+    }
+    BN_GENCB_free(cb);
+    DH_free(ret);
+    return NULL;
+}
+#endif
diff --git a/crypto/dh/dh_err.c b/crypto/dh/dh_err.c
new file mode 100644
index 000000000..4e21f284b
--- /dev/null
+++ b/crypto/dh/dh_err.c
@@ -0,0 +1,73 @@
+/*
+ * Generated by util/mkerr.pl DO NOT EDIT
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include 
+
+/* BEGIN ERROR CODES */
+#ifndef OPENSSL_NO_ERR
+
+# define ERR_FUNC(func) ERR_PACK(ERR_LIB_DH,func,0)
+# define ERR_REASON(reason) ERR_PACK(ERR_LIB_DH,0,reason)
+
+static ERR_STRING_DATA DH_str_functs[] = {
+    {ERR_FUNC(DH_F_COMPUTE_KEY), "compute_key"},
+    {ERR_FUNC(DH_F_DHPARAMS_PRINT_FP), "DHparams_print_fp"},
+    {ERR_FUNC(DH_F_DH_BUILTIN_GENPARAMS), "dh_builtin_genparams"},
+    {ERR_FUNC(DH_F_DH_CMS_DECRYPT), "dh_cms_decrypt"},
+    {ERR_FUNC(DH_F_DH_CMS_SET_PEERKEY), "dh_cms_set_peerkey"},
+    {ERR_FUNC(DH_F_DH_CMS_SET_SHARED_INFO), "dh_cms_set_shared_info"},
+    {ERR_FUNC(DH_F_DH_METH_DUP), "DH_meth_dup"},
+    {ERR_FUNC(DH_F_DH_METH_NEW), "DH_meth_new"},
+    {ERR_FUNC(DH_F_DH_METH_SET1_NAME), "DH_meth_set1_name"},
+    {ERR_FUNC(DH_F_DH_NEW_METHOD), "DH_new_method"},
+    {ERR_FUNC(DH_F_DH_PARAM_DECODE), "dh_param_decode"},
+    {ERR_FUNC(DH_F_DH_PRIV_DECODE), "dh_priv_decode"},
+    {ERR_FUNC(DH_F_DH_PRIV_ENCODE), "dh_priv_encode"},
+    {ERR_FUNC(DH_F_DH_PUB_DECODE), "dh_pub_decode"},
+    {ERR_FUNC(DH_F_DH_PUB_ENCODE), "dh_pub_encode"},
+    {ERR_FUNC(DH_F_DO_DH_PRINT), "do_dh_print"},
+    {ERR_FUNC(DH_F_GENERATE_KEY), "generate_key"},
+    {ERR_FUNC(DH_F_PKEY_DH_DERIVE), "pkey_dh_derive"},
+    {ERR_FUNC(DH_F_PKEY_DH_KEYGEN), "pkey_dh_keygen"},
+    {0, NULL}
+};
+
+static ERR_STRING_DATA DH_str_reasons[] = {
+    {ERR_REASON(DH_R_BAD_GENERATOR), "bad generator"},
+    {ERR_REASON(DH_R_BN_DECODE_ERROR), "bn decode error"},
+    {ERR_REASON(DH_R_BN_ERROR), "bn error"},
+    {ERR_REASON(DH_R_DECODE_ERROR), "decode error"},
+    {ERR_REASON(DH_R_INVALID_PUBKEY), "invalid public key"},
+    {ERR_REASON(DH_R_KDF_PARAMETER_ERROR), "kdf parameter error"},
+    {ERR_REASON(DH_R_KEYS_NOT_SET), "keys not set"},
+    {ERR_REASON(DH_R_MODULUS_TOO_LARGE), "modulus too large"},
+    {ERR_REASON(DH_R_NO_PARAMETERS_SET), "no parameters set"},
+    {ERR_REASON(DH_R_NO_PRIVATE_VALUE), "no private value"},
+    {ERR_REASON(DH_R_PARAMETER_ENCODING_ERROR), "parameter encoding error"},
+    {ERR_REASON(DH_R_PEER_KEY_ERROR), "peer key error"},
+    {ERR_REASON(DH_R_SHARED_INFO_ERROR), "shared info error"},
+    {0, NULL}
+};
+
+#endif
+
+int ERR_load_DH_strings(void)
+{
+#ifndef OPENSSL_NO_ERR
+
+    if (ERR_func_error_string(DH_str_functs[0].error) == NULL) {
+        ERR_load_strings(0, DH_str_functs);
+        ERR_load_strings(0, DH_str_reasons);
+    }
+#endif
+    return 1;
+}
diff --git a/crypto/dh/dh_gen.c b/crypto/dh/dh_gen.c
new file mode 100644
index 000000000..27ecb983d
--- /dev/null
+++ b/crypto/dh/dh_gen.c
@@ -0,0 +1,130 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * NB: These functions have been upgraded - the previous prototypes are in
+ * dh_depr.c as wrappers to these ones.  - Geoff
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include "dh_locl.h"
+
+static int dh_builtin_genparams(DH *ret, int prime_len, int generator,
+                                BN_GENCB *cb);
+
+int DH_generate_parameters_ex(DH *ret, int prime_len, int generator,
+                              BN_GENCB *cb)
+{
+    if (ret->meth->generate_params)
+        return ret->meth->generate_params(ret, prime_len, generator, cb);
+    return dh_builtin_genparams(ret, prime_len, generator, cb);
+}
+
+/*-
+ * We generate DH parameters as follows
+ * find a prime q which is prime_len/2 bits long.
+ * p=(2*q)+1 or (p-1)/2 = q
+ * For this case, g is a generator if
+ * g^((p-1)/q) mod p != 1 for values of q which are the factors of p-1.
+ * Since the factors of p-1 are q and 2, we just need to check
+ * g^2 mod p != 1 and g^q mod p != 1.
+ *
+ * Having said all that,
+ * there is another special case method for the generators 2, 3 and 5.
+ * for 2, p mod 24 == 11
+ * for 3, p mod 12 == 5  <<<<< does not work for safe primes.
+ * for 5, p mod 10 == 3 or 7
+ *
+ * Thanks to Phil Karn  for the pointers about the
+ * special generators and for answering some of my questions.
+ *
+ * I've implemented the second simple method :-).
+ * Since DH should be using a safe prime (both p and q are prime),
+ * this generator function can take a very very long time to run.
+ */
+/*
+ * Actually there is no reason to insist that 'generator' be a generator.
+ * It's just as OK (and in some sense better) to use a generator of the
+ * order-q subgroup.
+ */
+static int dh_builtin_genparams(DH *ret, int prime_len, int generator,
+                                BN_GENCB *cb)
+{
+    BIGNUM *t1, *t2;
+    int g, ok = -1;
+    BN_CTX *ctx = NULL;
+
+    ctx = BN_CTX_new();
+    if (ctx == NULL)
+        goto err;
+    BN_CTX_start(ctx);
+    t1 = BN_CTX_get(ctx);
+    t2 = BN_CTX_get(ctx);
+    if (t1 == NULL || t2 == NULL)
+        goto err;
+
+    /* Make sure 'ret' has the necessary elements */
+    if (!ret->p && ((ret->p = BN_new()) == NULL))
+        goto err;
+    if (!ret->g && ((ret->g = BN_new()) == NULL))
+        goto err;
+
+    if (generator <= 1) {
+        DHerr(DH_F_DH_BUILTIN_GENPARAMS, DH_R_BAD_GENERATOR);
+        goto err;
+    }
+    if (generator == DH_GENERATOR_2) {
+        if (!BN_set_word(t1, 24))
+            goto err;
+        if (!BN_set_word(t2, 11))
+            goto err;
+        g = 2;
+    } else if (generator == DH_GENERATOR_5) {
+        if (!BN_set_word(t1, 10))
+            goto err;
+        if (!BN_set_word(t2, 3))
+            goto err;
+        /*
+         * BN_set_word(t3,7); just have to miss out on these ones :-(
+         */
+        g = 5;
+    } else {
+        /*
+         * in the general case, don't worry if 'generator' is a generator or
+         * not: since we are using safe primes, it will generate either an
+         * order-q or an order-2q group, which both is OK
+         */
+        if (!BN_set_word(t1, 2))
+            goto err;
+        if (!BN_set_word(t2, 1))
+            goto err;
+        g = generator;
+    }
+
+    if (!BN_generate_prime_ex(ret->p, prime_len, 1, t1, t2, cb))
+        goto err;
+    if (!BN_GENCB_call(cb, 3, 0))
+        goto err;
+    if (!BN_set_word(ret->g, g))
+        goto err;
+    ok = 1;
+ err:
+    if (ok == -1) {
+        DHerr(DH_F_DH_BUILTIN_GENPARAMS, ERR_R_BN_LIB);
+        ok = 0;
+    }
+
+    if (ctx != NULL) {
+        BN_CTX_end(ctx);
+        BN_CTX_free(ctx);
+    }
+    return ok;
+}
diff --git a/crypto/dh/dh_kdf.c b/crypto/dh/dh_kdf.c
new file mode 100644
index 000000000..2782eeee6
--- /dev/null
+++ b/crypto/dh/dh_kdf.c
@@ -0,0 +1,150 @@
+/*
+ * Copyright 2013-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+
+#ifndef OPENSSL_NO_CMS
+#include 
+#include 
+#include 
+#include 
+#include 
+
+
+/* Key derivation from X9.42/RFC2631 */
+/* Uses CMS functions, hence the #ifdef wrapper. */
+
+#define DH_KDF_MAX      (1L << 30)
+
+/* Skip past an ASN1 structure: for OBJECT skip content octets too */
+
+static int skip_asn1(unsigned char **pp, long *plen, int exptag)
+{
+    const unsigned char *q = *pp;
+    int i, tag, xclass;
+    long tmplen;
+    i = ASN1_get_object(&q, &tmplen, &tag, &xclass, *plen);
+    if (i & 0x80)
+        return 0;
+    if (tag != exptag || xclass != V_ASN1_UNIVERSAL)
+        return 0;
+    if (tag == V_ASN1_OBJECT)
+        q += tmplen;
+    *plen -= q - *pp;
+    *pp = (unsigned char *)q;
+    return 1;
+}
+
+/*
+ * Encode the DH shared info structure, return an offset to the counter value
+ * so we can update the structure without reencoding it.
+ */
+
+static int dh_sharedinfo_encode(unsigned char **pder, unsigned char **pctr,
+                                ASN1_OBJECT *key_oid, size_t outlen,
+                                const unsigned char *ukm, size_t ukmlen)
+{
+    unsigned char *p;
+    int derlen;
+    long tlen;
+    /* "magic" value to check offset is sane */
+    static unsigned char ctr[4] = { 0xF3, 0x17, 0x22, 0x53 };
+    X509_ALGOR atmp;
+    ASN1_OCTET_STRING ctr_oct, ukm_oct, *pukm_oct;
+    ASN1_TYPE ctr_atype;
+    if (ukmlen > DH_KDF_MAX || outlen > DH_KDF_MAX)
+        return 0;
+    ctr_oct.data = ctr;
+    ctr_oct.length = 4;
+    ctr_oct.flags = 0;
+    ctr_oct.type = V_ASN1_OCTET_STRING;
+    ctr_atype.type = V_ASN1_OCTET_STRING;
+    ctr_atype.value.octet_string = &ctr_oct;
+    atmp.algorithm = key_oid;
+    atmp.parameter = &ctr_atype;
+    if (ukm) {
+        ukm_oct.type = V_ASN1_OCTET_STRING;
+        ukm_oct.flags = 0;
+        ukm_oct.data = (unsigned char *)ukm;
+        ukm_oct.length = ukmlen;
+        pukm_oct = &ukm_oct;
+    } else
+        pukm_oct = NULL;
+    derlen = CMS_SharedInfo_encode(pder, &atmp, pukm_oct, outlen);
+    if (derlen <= 0)
+        return 0;
+    p = *pder;
+    tlen = derlen;
+    if (!skip_asn1(&p, &tlen, V_ASN1_SEQUENCE))
+        return 0;
+    if (!skip_asn1(&p, &tlen, V_ASN1_SEQUENCE))
+        return 0;
+    if (!skip_asn1(&p, &tlen, V_ASN1_OBJECT))
+        return 0;
+    if (!skip_asn1(&p, &tlen, V_ASN1_OCTET_STRING))
+        return 0;
+    if (CRYPTO_memcmp(p, ctr, 4))
+        return 0;
+    *pctr = p;
+    return derlen;
+}
+
+int DH_KDF_X9_42(unsigned char *out, size_t outlen,
+                 const unsigned char *Z, size_t Zlen,
+                 ASN1_OBJECT *key_oid,
+                 const unsigned char *ukm, size_t ukmlen, const EVP_MD *md)
+{
+    EVP_MD_CTX *mctx = NULL;
+    int rv = 0;
+    unsigned int i;
+    size_t mdlen;
+    unsigned char *der = NULL, *ctr;
+    int derlen;
+    if (Zlen > DH_KDF_MAX)
+        return 0;
+    mctx = EVP_MD_CTX_new();
+    if (mctx == NULL)
+        return 0;
+    mdlen = EVP_MD_size(md);
+    derlen = dh_sharedinfo_encode(&der, &ctr, key_oid, outlen, ukm, ukmlen);
+    if (derlen == 0)
+        goto err;
+    for (i = 1;; i++) {
+        unsigned char mtmp[EVP_MAX_MD_SIZE];
+        if (!EVP_DigestInit_ex(mctx, md, NULL)
+            || !EVP_DigestUpdate(mctx, Z, Zlen))
+            goto err;
+        ctr[3] = i & 0xFF;
+        ctr[2] = (i >> 8) & 0xFF;
+        ctr[1] = (i >> 16) & 0xFF;
+        ctr[0] = (i >> 24) & 0xFF;
+        if (!EVP_DigestUpdate(mctx, der, derlen))
+            goto err;
+        if (outlen >= mdlen) {
+            if (!EVP_DigestFinal(mctx, out, NULL))
+                goto err;
+            outlen -= mdlen;
+            if (outlen == 0)
+                break;
+            out += mdlen;
+        } else {
+            if (!EVP_DigestFinal(mctx, mtmp, NULL))
+                goto err;
+            memcpy(out, mtmp, outlen);
+            OPENSSL_cleanse(mtmp, mdlen);
+            break;
+        }
+    }
+    rv = 1;
+ err:
+    OPENSSL_free(der);
+    EVP_MD_CTX_free(mctx);
+    return rv;
+}
+#endif
diff --git a/crypto/dh/dh_key.c b/crypto/dh/dh_key.c
new file mode 100644
index 000000000..204e5a7a4
--- /dev/null
+++ b/crypto/dh/dh_key.c
@@ -0,0 +1,215 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include "dh_locl.h"
+#include "internal/bn_int.h"
+
+static int generate_key(DH *dh);
+static int compute_key(unsigned char *key, const BIGNUM *pub_key, DH *dh);
+static int dh_bn_mod_exp(const DH *dh, BIGNUM *r,
+                         const BIGNUM *a, const BIGNUM *p,
+                         const BIGNUM *m, BN_CTX *ctx, BN_MONT_CTX *m_ctx);
+static int dh_init(DH *dh);
+static int dh_finish(DH *dh);
+
+int DH_generate_key(DH *dh)
+{
+    return dh->meth->generate_key(dh);
+}
+
+int DH_compute_key(unsigned char *key, const BIGNUM *pub_key, DH *dh)
+{
+    return dh->meth->compute_key(key, pub_key, dh);
+}
+
+int DH_compute_key_padded(unsigned char *key, const BIGNUM *pub_key, DH *dh)
+{
+    int rv, pad;
+    rv = dh->meth->compute_key(key, pub_key, dh);
+    if (rv <= 0)
+        return rv;
+    pad = BN_num_bytes(dh->p) - rv;
+    if (pad > 0) {
+        memmove(key + pad, key, rv);
+        memset(key, 0, pad);
+    }
+    return rv + pad;
+}
+
+static DH_METHOD dh_ossl = {
+    "OpenSSL DH Method",
+    generate_key,
+    compute_key,
+    dh_bn_mod_exp,
+    dh_init,
+    dh_finish,
+    DH_FLAG_FIPS_METHOD,
+    NULL,
+    NULL
+};
+
+const DH_METHOD *DH_OpenSSL(void)
+{
+    return &dh_ossl;
+}
+
+static int generate_key(DH *dh)
+{
+    int ok = 0;
+    int generate_new_key = 0;
+    unsigned l;
+    BN_CTX *ctx;
+    BN_MONT_CTX *mont = NULL;
+    BIGNUM *pub_key = NULL, *priv_key = NULL;
+
+    ctx = BN_CTX_new();
+    if (ctx == NULL)
+        goto err;
+
+    if (dh->priv_key == NULL) {
+        priv_key = BN_secure_new();
+        if (priv_key == NULL)
+            goto err;
+        generate_new_key = 1;
+    } else
+        priv_key = dh->priv_key;
+
+    if (dh->pub_key == NULL) {
+        pub_key = BN_new();
+        if (pub_key == NULL)
+            goto err;
+    } else
+        pub_key = dh->pub_key;
+
+    if (dh->flags & DH_FLAG_CACHE_MONT_P) {
+        mont = BN_MONT_CTX_set_locked(&dh->method_mont_p,
+                                      dh->lock, dh->p, ctx);
+        if (!mont)
+            goto err;
+    }
+
+    if (generate_new_key) {
+        if (dh->q) {
+            do {
+                if (!BN_rand_range(priv_key, dh->q))
+                    goto err;
+            }
+            while (BN_is_zero(priv_key) || BN_is_one(priv_key));
+        } else {
+            /* secret exponent length */
+            l = dh->length ? dh->length : BN_num_bits(dh->p) - 1;
+            if (!BN_rand(priv_key, l, BN_RAND_TOP_ONE, BN_RAND_BOTTOM_ANY))
+                goto err;
+        }
+    }
+
+    {
+        BIGNUM *prk = BN_new();
+
+        if (prk == NULL)
+            goto err;
+        BN_with_flags(prk, priv_key, BN_FLG_CONSTTIME);
+
+        if (!dh->meth->bn_mod_exp(dh, pub_key, dh->g, prk, dh->p, ctx, mont)) {
+            BN_free(prk);
+            goto err;
+        }
+        /* We MUST free prk before any further use of priv_key */
+        BN_free(prk);
+    }
+
+    dh->pub_key = pub_key;
+    dh->priv_key = priv_key;
+    ok = 1;
+ err:
+    if (ok != 1)
+        DHerr(DH_F_GENERATE_KEY, ERR_R_BN_LIB);
+
+    if (pub_key != dh->pub_key)
+        BN_free(pub_key);
+    if (priv_key != dh->priv_key)
+        BN_free(priv_key);
+    BN_CTX_free(ctx);
+    return (ok);
+}
+
+static int compute_key(unsigned char *key, const BIGNUM *pub_key, DH *dh)
+{
+    BN_CTX *ctx = NULL;
+    BN_MONT_CTX *mont = NULL;
+    BIGNUM *tmp;
+    int ret = -1;
+    int check_result;
+
+    if (BN_num_bits(dh->p) > OPENSSL_DH_MAX_MODULUS_BITS) {
+        DHerr(DH_F_COMPUTE_KEY, DH_R_MODULUS_TOO_LARGE);
+        goto err;
+    }
+
+    ctx = BN_CTX_new();
+    if (ctx == NULL)
+        goto err;
+    BN_CTX_start(ctx);
+    tmp = BN_CTX_get(ctx);
+    if (tmp == NULL)
+        goto err;
+
+    if (dh->priv_key == NULL) {
+        DHerr(DH_F_COMPUTE_KEY, DH_R_NO_PRIVATE_VALUE);
+        goto err;
+    }
+
+    if (dh->flags & DH_FLAG_CACHE_MONT_P) {
+        mont = BN_MONT_CTX_set_locked(&dh->method_mont_p,
+                                      dh->lock, dh->p, ctx);
+        BN_set_flags(dh->priv_key, BN_FLG_CONSTTIME);
+        if (!mont)
+            goto err;
+    }
+
+    if (!DH_check_pub_key(dh, pub_key, &check_result) || check_result) {
+        DHerr(DH_F_COMPUTE_KEY, DH_R_INVALID_PUBKEY);
+        goto err;
+    }
+
+    if (!dh->
+        meth->bn_mod_exp(dh, tmp, pub_key, dh->priv_key, dh->p, ctx, mont)) {
+        DHerr(DH_F_COMPUTE_KEY, ERR_R_BN_LIB);
+        goto err;
+    }
+
+    ret = BN_bn2bin(tmp, key);
+ err:
+    if (ctx != NULL) {
+        BN_CTX_end(ctx);
+        BN_CTX_free(ctx);
+    }
+    return (ret);
+}
+
+static int dh_bn_mod_exp(const DH *dh, BIGNUM *r,
+                         const BIGNUM *a, const BIGNUM *p,
+                         const BIGNUM *m, BN_CTX *ctx, BN_MONT_CTX *m_ctx)
+{
+    return BN_mod_exp_mont(r, a, p, m, ctx, m_ctx);
+}
+
+static int dh_init(DH *dh)
+{
+    dh->flags |= DH_FLAG_CACHE_MONT_P;
+    return (1);
+}
+
+static int dh_finish(DH *dh)
+{
+    BN_MONT_CTX_free(dh->method_mont_p);
+    return (1);
+}
diff --git a/crypto/dh/dh_lib.c b/crypto/dh/dh_lib.c
new file mode 100644
index 000000000..adf177151
--- /dev/null
+++ b/crypto/dh/dh_lib.c
@@ -0,0 +1,284 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include "dh_locl.h"
+#include 
+
+static const DH_METHOD *default_DH_method = NULL;
+
+void DH_set_default_method(const DH_METHOD *meth)
+{
+    default_DH_method = meth;
+}
+
+const DH_METHOD *DH_get_default_method(void)
+{
+    if (!default_DH_method)
+        default_DH_method = DH_OpenSSL();
+    return default_DH_method;
+}
+
+int DH_set_method(DH *dh, const DH_METHOD *meth)
+{
+    /*
+     * NB: The caller is specifically setting a method, so it's not up to us
+     * to deal with which ENGINE it comes from.
+     */
+    const DH_METHOD *mtmp;
+    mtmp = dh->meth;
+    if (mtmp->finish)
+        mtmp->finish(dh);
+#ifndef OPENSSL_NO_ENGINE
+    ENGINE_finish(dh->engine);
+    dh->engine = NULL;
+#endif
+    dh->meth = meth;
+    if (meth->init)
+        meth->init(dh);
+    return 1;
+}
+
+DH *DH_new(void)
+{
+    return DH_new_method(NULL);
+}
+
+DH *DH_new_method(ENGINE *engine)
+{
+    DH *ret = OPENSSL_zalloc(sizeof(*ret));
+
+    if (ret == NULL) {
+        DHerr(DH_F_DH_NEW_METHOD, ERR_R_MALLOC_FAILURE);
+        return NULL;
+    }
+
+    ret->references = 1;
+    ret->lock = CRYPTO_THREAD_lock_new();
+    if (ret->lock == NULL) {
+        DHerr(DH_F_DH_NEW_METHOD, ERR_R_MALLOC_FAILURE);
+        OPENSSL_free(ret);
+        return NULL;
+    }
+
+    ret->meth = DH_get_default_method();
+#ifndef OPENSSL_NO_ENGINE
+    ret->flags = ret->meth->flags;  /* early default init */
+    if (engine) {
+        if (!ENGINE_init(engine)) {
+            DHerr(DH_F_DH_NEW_METHOD, ERR_R_ENGINE_LIB);
+            goto err;
+        }
+        ret->engine = engine;
+    } else
+        ret->engine = ENGINE_get_default_DH();
+    if (ret->engine) {
+        ret->meth = ENGINE_get_DH(ret->engine);
+        if (ret->meth == NULL) {
+            DHerr(DH_F_DH_NEW_METHOD, ERR_R_ENGINE_LIB);
+            goto err;
+        }
+    }
+#endif
+
+    ret->flags = ret->meth->flags;
+
+    if (!CRYPTO_new_ex_data(CRYPTO_EX_INDEX_DH, ret, &ret->ex_data))
+        goto err;
+
+    if ((ret->meth->init != NULL) && !ret->meth->init(ret)) {
+        DHerr(DH_F_DH_NEW_METHOD, ERR_R_INIT_FAIL);
+err:
+        DH_free(ret);
+        ret = NULL;
+    }
+
+    return ret;
+}
+
+void DH_free(DH *r)
+{
+    int i;
+
+    if (r == NULL)
+        return;
+
+    CRYPTO_atomic_add(&r->references, -1, &i, r->lock);
+    REF_PRINT_COUNT("DH", r);
+    if (i > 0)
+        return;
+    REF_ASSERT_ISNT(i < 0);
+
+    if (r->meth->finish)
+        r->meth->finish(r);
+#ifndef OPENSSL_NO_ENGINE
+    ENGINE_finish(r->engine);
+#endif
+
+    CRYPTO_free_ex_data(CRYPTO_EX_INDEX_DH, r, &r->ex_data);
+
+    CRYPTO_THREAD_lock_free(r->lock);
+
+    BN_clear_free(r->p);
+    BN_clear_free(r->g);
+    BN_clear_free(r->q);
+    BN_clear_free(r->j);
+    OPENSSL_free(r->seed);
+    BN_clear_free(r->counter);
+    BN_clear_free(r->pub_key);
+    BN_clear_free(r->priv_key);
+    OPENSSL_free(r);
+}
+
+int DH_up_ref(DH *r)
+{
+    int i;
+
+    if (CRYPTO_atomic_add(&r->references, 1, &i, r->lock) <= 0)
+        return 0;
+
+    REF_PRINT_COUNT("DH", r);
+    REF_ASSERT_ISNT(i < 2);
+    return ((i > 1) ? 1 : 0);
+}
+
+int DH_set_ex_data(DH *d, int idx, void *arg)
+{
+    return (CRYPTO_set_ex_data(&d->ex_data, idx, arg));
+}
+
+void *DH_get_ex_data(DH *d, int idx)
+{
+    return (CRYPTO_get_ex_data(&d->ex_data, idx));
+}
+
+int DH_bits(const DH *dh)
+{
+    return BN_num_bits(dh->p);
+}
+
+int DH_size(const DH *dh)
+{
+    return (BN_num_bytes(dh->p));
+}
+
+int DH_security_bits(const DH *dh)
+{
+    int N;
+    if (dh->q)
+        N = BN_num_bits(dh->q);
+    else if (dh->length)
+        N = dh->length;
+    else
+        N = -1;
+    return BN_security_bits(BN_num_bits(dh->p), N);
+}
+
+
+void DH_get0_pqg(const DH *dh,
+                 const BIGNUM **p, const BIGNUM **q, const BIGNUM **g)
+{
+    if (p != NULL)
+        *p = dh->p;
+    if (q != NULL)
+        *q = dh->q;
+    if (g != NULL)
+        *g = dh->g;
+}
+
+int DH_set0_pqg(DH *dh, BIGNUM *p, BIGNUM *q, BIGNUM *g)
+{
+    /* If the fields p and g in d are NULL, the corresponding input
+     * parameters MUST be non-NULL.  q may remain NULL.
+     */
+    if ((dh->p == NULL && p == NULL)
+        || (dh->g == NULL && g == NULL))
+        return 0;
+
+    if (p != NULL) {
+        BN_free(dh->p);
+        dh->p = p;
+    }
+    if (q != NULL) {
+        BN_free(dh->q);
+        dh->q = q;
+    }
+    if (g != NULL) {
+        BN_free(dh->g);
+        dh->g = g;
+    }
+
+    if (q != NULL) {
+        dh->length = BN_num_bits(q);
+    }
+
+    return 1;
+}
+
+long DH_get_length(const DH *dh)
+{
+    return dh->length;
+}
+
+int DH_set_length(DH *dh, long length)
+{
+    dh->length = length;
+    return 1;
+}
+
+void DH_get0_key(const DH *dh, const BIGNUM **pub_key, const BIGNUM **priv_key)
+{
+    if (pub_key != NULL)
+        *pub_key = dh->pub_key;
+    if (priv_key != NULL)
+        *priv_key = dh->priv_key;
+}
+
+int DH_set0_key(DH *dh, BIGNUM *pub_key, BIGNUM *priv_key)
+{
+    /* If the field pub_key in dh is NULL, the corresponding input
+     * parameters MUST be non-NULL.  The priv_key field may
+     * be left NULL.
+     */
+    if (dh->pub_key == NULL && pub_key == NULL)
+        return 0;
+
+    if (pub_key != NULL) {
+        BN_free(dh->pub_key);
+        dh->pub_key = pub_key;
+    }
+    if (priv_key != NULL) {
+        BN_free(dh->priv_key);
+        dh->priv_key = priv_key;
+    }
+
+    return 1;
+}
+
+void DH_clear_flags(DH *dh, int flags)
+{
+    dh->flags &= ~flags;
+}
+
+int DH_test_flags(const DH *dh, int flags)
+{
+    return dh->flags & flags;
+}
+
+void DH_set_flags(DH *dh, int flags)
+{
+    dh->flags |= flags;
+}
+
+ENGINE *DH_get0_engine(DH *dh)
+{
+    return dh->engine;
+}
diff --git a/crypto/dh/dh_locl.h b/crypto/dh/dh_locl.h
new file mode 100644
index 000000000..19301c318
--- /dev/null
+++ b/crypto/dh/dh_locl.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+
+struct dh_st {
+    /*
+     * This first argument is used to pick up errors when a DH is passed
+     * instead of a EVP_PKEY
+     */
+    int pad;
+    int version;
+    BIGNUM *p;
+    BIGNUM *g;
+    long length;                /* optional */
+    BIGNUM *pub_key;            /* g^x % p */
+    BIGNUM *priv_key;           /* x */
+    int flags;
+    BN_MONT_CTX *method_mont_p;
+    /* Place holders if we want to do X9.42 DH */
+    BIGNUM *q;
+    BIGNUM *j;
+    unsigned char *seed;
+    int seedlen;
+    BIGNUM *counter;
+    int references;
+    CRYPTO_EX_DATA ex_data;
+    const DH_METHOD *meth;
+    ENGINE *engine;
+    CRYPTO_RWLOCK *lock;
+};
+
+struct dh_method {
+    char *name;
+    /* Methods here */
+    int (*generate_key) (DH *dh);
+    int (*compute_key) (unsigned char *key, const BIGNUM *pub_key, DH *dh);
+
+    /* Can be null */
+    int (*bn_mod_exp) (const DH *dh, BIGNUM *r, const BIGNUM *a,
+                       const BIGNUM *p, const BIGNUM *m, BN_CTX *ctx,
+                       BN_MONT_CTX *m_ctx);
+    int (*init) (DH *dh);
+    int (*finish) (DH *dh);
+    int flags;
+    char *app_data;
+    /* If this is non-NULL, it will be used to generate parameters */
+    int (*generate_params) (DH *dh, int prime_len, int generator,
+                            BN_GENCB *cb);
+};
diff --git a/crypto/dh/dh_meth.c b/crypto/dh/dh_meth.c
new file mode 100644
index 000000000..ce6114c13
--- /dev/null
+++ b/crypto/dh/dh_meth.c
@@ -0,0 +1,173 @@
+/*
+ * Copyright 2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "dh_locl.h"
+#include 
+#include 
+
+DH_METHOD *DH_meth_new(const char *name, int flags)
+{
+    DH_METHOD *dhm = OPENSSL_zalloc(sizeof(*dhm));
+
+    if (dhm != NULL) {
+        dhm->flags = flags;
+
+        dhm->name = OPENSSL_strdup(name);
+        if (dhm->name != NULL)
+            return dhm;
+
+        OPENSSL_free(dhm);
+    }
+
+    DHerr(DH_F_DH_METH_NEW, ERR_R_MALLOC_FAILURE);
+    return NULL;
+}
+
+void DH_meth_free(DH_METHOD *dhm)
+{
+    if (dhm != NULL) {
+        OPENSSL_free(dhm->name);
+        OPENSSL_free(dhm);
+    }
+}
+
+DH_METHOD *DH_meth_dup(const DH_METHOD *dhm)
+{
+    DH_METHOD *ret = OPENSSL_malloc(sizeof(*ret));
+
+    if (ret != NULL) {
+        memcpy(ret, dhm, sizeof(*dhm));
+
+        ret->name = OPENSSL_strdup(dhm->name);
+        if (ret->name != NULL)
+            return ret;
+
+        OPENSSL_free(ret);
+    }
+
+    DHerr(DH_F_DH_METH_DUP, ERR_R_MALLOC_FAILURE);
+    return NULL;
+}
+
+const char *DH_meth_get0_name(const DH_METHOD *dhm)
+{
+    return dhm->name;
+}
+
+int DH_meth_set1_name(DH_METHOD *dhm, const char *name)
+{
+    char *tmpname = OPENSSL_strdup(name);
+
+    if (tmpname == NULL) {
+        DHerr(DH_F_DH_METH_SET1_NAME, ERR_R_MALLOC_FAILURE);
+        return 0;
+    }
+
+    OPENSSL_free(dhm->name);
+    dhm->name = tmpname;
+
+    return 1;
+}
+
+int DH_meth_get_flags(DH_METHOD *dhm)
+{
+    return dhm->flags;
+}
+
+int DH_meth_set_flags(DH_METHOD *dhm, int flags)
+{
+    dhm->flags = flags;
+    return 1;
+}
+
+void *DH_meth_get0_app_data(const DH_METHOD *dhm)
+{
+    return dhm->app_data;
+}
+
+int DH_meth_set0_app_data(DH_METHOD *dhm, void *app_data)
+{
+    dhm->app_data = app_data;
+    return 1;
+}
+
+int (*DH_meth_get_generate_key(const DH_METHOD *dhm)) (DH *)
+{
+    return dhm->generate_key;
+}
+
+int DH_meth_set_generate_key(DH_METHOD *dhm, int (*generate_key) (DH *))
+{
+    dhm->generate_key = generate_key;
+    return 1;
+}
+
+int (*DH_meth_get_compute_key(const DH_METHOD *dhm))
+        (unsigned char *key, const BIGNUM *pub_key, DH *dh)
+{
+    return dhm->compute_key;
+}
+
+int DH_meth_set_compute_key(DH_METHOD *dhm,
+        int (*compute_key) (unsigned char *key, const BIGNUM *pub_key, DH *dh))
+{
+    dhm->compute_key = compute_key;
+    return 1;
+}
+
+
+int (*DH_meth_get_bn_mod_exp(const DH_METHOD *dhm))
+    (const DH *, BIGNUM *, const BIGNUM *, const BIGNUM *, const BIGNUM *,
+     BN_CTX *, BN_MONT_CTX *)
+{
+    return dhm->bn_mod_exp;
+}
+
+int DH_meth_set_bn_mod_exp(DH_METHOD *dhm,
+    int (*bn_mod_exp) (const DH *, BIGNUM *, const BIGNUM *, const BIGNUM *,
+                       const BIGNUM *, BN_CTX *, BN_MONT_CTX *))
+{
+    dhm->bn_mod_exp = bn_mod_exp;
+    return 1;
+}
+
+int (*DH_meth_get_init(const DH_METHOD *dhm))(DH *)
+{
+    return dhm->init;
+}
+
+int DH_meth_set_init(DH_METHOD *dhm, int (*init)(DH *))
+{
+    dhm->init = init;
+    return 1;
+}
+
+int (*DH_meth_get_finish(const DH_METHOD *dhm)) (DH *)
+{
+    return dhm->finish;
+}
+
+int DH_meth_set_finish(DH_METHOD *dhm, int (*finish) (DH *))
+{
+    dhm->finish = finish;
+    return 1;
+}
+
+int (*DH_meth_get_generate_params(const DH_METHOD *dhm))
+        (DH *, int, int, BN_GENCB *)
+{
+    return dhm->generate_params;
+}
+
+int DH_meth_set_generate_params(DH_METHOD *dhm,
+        int (*generate_params) (DH *, int, int, BN_GENCB *))
+{
+    dhm->generate_params = generate_params;
+    return 1;
+}
diff --git a/crypto/dh/dh_pmeth.c b/crypto/dh/dh_pmeth.c
new file mode 100644
index 000000000..c3e03c7a4
--- /dev/null
+++ b/crypto/dh/dh_pmeth.c
@@ -0,0 +1,501 @@
+/*
+ * Copyright 2006-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+#include 
+#include "dh_locl.h"
+#include 
+#include 
+#include 
+#include "internal/evp_int.h"
+
+/* DH pkey context structure */
+
+typedef struct {
+    /* Parameter gen parameters */
+    int prime_len;
+    int generator;
+    int use_dsa;
+    int subprime_len;
+    /* message digest used for parameter generation */
+    const EVP_MD *md;
+    int rfc5114_param;
+    /* Keygen callback info */
+    int gentmp[2];
+    /* KDF (if any) to use for DH */
+    char kdf_type;
+    /* OID to use for KDF */
+    ASN1_OBJECT *kdf_oid;
+    /* Message digest to use for key derivation */
+    const EVP_MD *kdf_md;
+    /* User key material */
+    unsigned char *kdf_ukm;
+    size_t kdf_ukmlen;
+    /* KDF output length */
+    size_t kdf_outlen;
+} DH_PKEY_CTX;
+
+static int pkey_dh_init(EVP_PKEY_CTX *ctx)
+{
+    DH_PKEY_CTX *dctx;
+
+    dctx = OPENSSL_zalloc(sizeof(*dctx));
+    if (dctx == NULL)
+        return 0;
+    dctx->prime_len = 1024;
+    dctx->subprime_len = -1;
+    dctx->generator = 2;
+    dctx->kdf_type = EVP_PKEY_DH_KDF_NONE;
+
+    ctx->data = dctx;
+    ctx->keygen_info = dctx->gentmp;
+    ctx->keygen_info_count = 2;
+
+    return 1;
+}
+
+static void pkey_dh_cleanup(EVP_PKEY_CTX *ctx)
+{
+    DH_PKEY_CTX *dctx = ctx->data;
+    if (dctx != NULL) {
+        OPENSSL_free(dctx->kdf_ukm);
+        ASN1_OBJECT_free(dctx->kdf_oid);
+        OPENSSL_free(dctx);
+    }
+}
+
+
+static int pkey_dh_copy(EVP_PKEY_CTX *dst, EVP_PKEY_CTX *src)
+{
+    DH_PKEY_CTX *dctx, *sctx;
+    if (!pkey_dh_init(dst))
+        return 0;
+    sctx = src->data;
+    dctx = dst->data;
+    dctx->prime_len = sctx->prime_len;
+    dctx->subprime_len = sctx->subprime_len;
+    dctx->generator = sctx->generator;
+    dctx->use_dsa = sctx->use_dsa;
+    dctx->md = sctx->md;
+    dctx->rfc5114_param = sctx->rfc5114_param;
+
+    dctx->kdf_type = sctx->kdf_type;
+    dctx->kdf_oid = OBJ_dup(sctx->kdf_oid);
+    if (dctx->kdf_oid == NULL)
+        return 0;
+    dctx->kdf_md = sctx->kdf_md;
+    if (sctx->kdf_ukm != NULL) {
+        dctx->kdf_ukm = OPENSSL_memdup(sctx->kdf_ukm, sctx->kdf_ukmlen);
+        if (dctx->kdf_ukm == NULL)
+          return 0;
+        dctx->kdf_ukmlen = sctx->kdf_ukmlen;
+    }
+    dctx->kdf_outlen = sctx->kdf_outlen;
+    return 1;
+}
+
+static int pkey_dh_ctrl(EVP_PKEY_CTX *ctx, int type, int p1, void *p2)
+{
+    DH_PKEY_CTX *dctx = ctx->data;
+    switch (type) {
+    case EVP_PKEY_CTRL_DH_PARAMGEN_PRIME_LEN:
+        if (p1 < 256)
+            return -2;
+        dctx->prime_len = p1;
+        return 1;
+
+    case EVP_PKEY_CTRL_DH_PARAMGEN_SUBPRIME_LEN:
+        if (dctx->use_dsa == 0)
+            return -2;
+        dctx->subprime_len = p1;
+        return 1;
+
+    case EVP_PKEY_CTRL_DH_PARAMGEN_GENERATOR:
+        if (dctx->use_dsa)
+            return -2;
+        dctx->generator = p1;
+        return 1;
+
+    case EVP_PKEY_CTRL_DH_PARAMGEN_TYPE:
+#ifdef OPENSSL_NO_DSA
+        if (p1 != 0)
+            return -2;
+#else
+        if (p1 < 0 || p1 > 2)
+            return -2;
+#endif
+        dctx->use_dsa = p1;
+        return 1;
+
+    case EVP_PKEY_CTRL_DH_RFC5114:
+        if (p1 < 1 || p1 > 3)
+            return -2;
+        dctx->rfc5114_param = p1;
+        return 1;
+
+    case EVP_PKEY_CTRL_PEER_KEY:
+        /* Default behaviour is OK */
+        return 1;
+
+    case EVP_PKEY_CTRL_DH_KDF_TYPE:
+        if (p1 == -2)
+            return dctx->kdf_type;
+#ifdef OPENSSL_NO_CMS
+        if (p1 != EVP_PKEY_DH_KDF_NONE)
+#else
+        if (p1 != EVP_PKEY_DH_KDF_NONE && p1 != EVP_PKEY_DH_KDF_X9_42)
+#endif
+            return -2;
+        dctx->kdf_type = p1;
+        return 1;
+
+    case EVP_PKEY_CTRL_DH_KDF_MD:
+        dctx->kdf_md = p2;
+        return 1;
+
+    case EVP_PKEY_CTRL_GET_DH_KDF_MD:
+        *(const EVP_MD **)p2 = dctx->kdf_md;
+        return 1;
+
+    case EVP_PKEY_CTRL_DH_KDF_OUTLEN:
+        if (p1 <= 0)
+            return -2;
+        dctx->kdf_outlen = (size_t)p1;
+        return 1;
+
+    case EVP_PKEY_CTRL_GET_DH_KDF_OUTLEN:
+        *(int *)p2 = dctx->kdf_outlen;
+        return 1;
+
+    case EVP_PKEY_CTRL_DH_KDF_UKM:
+        OPENSSL_free(dctx->kdf_ukm);
+        dctx->kdf_ukm = p2;
+        if (p2)
+            dctx->kdf_ukmlen = p1;
+        else
+            dctx->kdf_ukmlen = 0;
+        return 1;
+
+    case EVP_PKEY_CTRL_GET_DH_KDF_UKM:
+        *(unsigned char **)p2 = dctx->kdf_ukm;
+        return dctx->kdf_ukmlen;
+
+    case EVP_PKEY_CTRL_DH_KDF_OID:
+        ASN1_OBJECT_free(dctx->kdf_oid);
+        dctx->kdf_oid = p2;
+        return 1;
+
+    case EVP_PKEY_CTRL_GET_DH_KDF_OID:
+        *(ASN1_OBJECT **)p2 = dctx->kdf_oid;
+        return 1;
+
+    default:
+        return -2;
+
+    }
+}
+
+static int pkey_dh_ctrl_str(EVP_PKEY_CTX *ctx,
+                            const char *type, const char *value)
+{
+    if (strcmp(type, "dh_paramgen_prime_len") == 0) {
+        int len;
+        len = atoi(value);
+        return EVP_PKEY_CTX_set_dh_paramgen_prime_len(ctx, len);
+    }
+    if (strcmp(type, "dh_rfc5114") == 0) {
+        DH_PKEY_CTX *dctx = ctx->data;
+        int len;
+        len = atoi(value);
+        if (len < 0 || len > 3)
+            return -2;
+        dctx->rfc5114_param = len;
+        return 1;
+    }
+    if (strcmp(type, "dh_paramgen_generator") == 0) {
+        int len;
+        len = atoi(value);
+        return EVP_PKEY_CTX_set_dh_paramgen_generator(ctx, len);
+    }
+    if (strcmp(type, "dh_paramgen_subprime_len") == 0) {
+        int len;
+        len = atoi(value);
+        return EVP_PKEY_CTX_set_dh_paramgen_subprime_len(ctx, len);
+    }
+    if (strcmp(type, "dh_paramgen_type") == 0) {
+        int typ;
+        typ = atoi(value);
+        return EVP_PKEY_CTX_set_dh_paramgen_type(ctx, typ);
+    }
+    return -2;
+}
+
+#ifndef OPENSSL_NO_DSA
+
+extern int dsa_builtin_paramgen(DSA *ret, size_t bits, size_t qbits,
+                                const EVP_MD *evpmd,
+                                const unsigned char *seed_in, size_t seed_len,
+                                unsigned char *seed_out, int *counter_ret,
+                                unsigned long *h_ret, BN_GENCB *cb);
+
+extern int dsa_builtin_paramgen2(DSA *ret, size_t L, size_t N,
+                                 const EVP_MD *evpmd,
+                                 const unsigned char *seed_in,
+                                 size_t seed_len, int idx,
+                                 unsigned char *seed_out, int *counter_ret,
+                                 unsigned long *h_ret, BN_GENCB *cb);
+
+static DSA *dsa_dh_generate(DH_PKEY_CTX *dctx, BN_GENCB *pcb)
+{
+    DSA *ret;
+    int rv = 0;
+    int prime_len = dctx->prime_len;
+    int subprime_len = dctx->subprime_len;
+    const EVP_MD *md = dctx->md;
+    if (dctx->use_dsa > 2)
+        return NULL;
+    ret = DSA_new();
+    if (ret == NULL)
+        return NULL;
+    if (subprime_len == -1) {
+        if (prime_len >= 2048)
+            subprime_len = 256;
+        else
+            subprime_len = 160;
+    }
+    if (md == NULL) {
+        if (prime_len >= 2048)
+            md = EVP_sha256();
+        else
+            md = EVP_sha1();
+    }
+    if (dctx->use_dsa == 1)
+        rv = dsa_builtin_paramgen(ret, prime_len, subprime_len, md,
+                                  NULL, 0, NULL, NULL, NULL, pcb);
+    else if (dctx->use_dsa == 2)
+        rv = dsa_builtin_paramgen2(ret, prime_len, subprime_len, md,
+                                   NULL, 0, -1, NULL, NULL, NULL, pcb);
+    if (rv <= 0) {
+        DSA_free(ret);
+        return NULL;
+    }
+    return ret;
+}
+
+#endif
+
+static int pkey_dh_paramgen(EVP_PKEY_CTX *ctx, EVP_PKEY *pkey)
+{
+    DH *dh = NULL;
+    DH_PKEY_CTX *dctx = ctx->data;
+    BN_GENCB *pcb;
+    int ret;
+    if (dctx->rfc5114_param) {
+        switch (dctx->rfc5114_param) {
+        case 1:
+            dh = DH_get_1024_160();
+            break;
+
+        case 2:
+            dh = DH_get_2048_224();
+            break;
+
+        case 3:
+            dh = DH_get_2048_256();
+            break;
+
+        default:
+            return -2;
+        }
+        EVP_PKEY_assign(pkey, EVP_PKEY_DHX, dh);
+        return 1;
+    }
+
+    if (ctx->pkey_gencb) {
+        pcb = BN_GENCB_new();
+        if (pcb == NULL)
+            return 0;
+        evp_pkey_set_cb_translate(pcb, ctx);
+    } else
+        pcb = NULL;
+#ifndef OPENSSL_NO_DSA
+    if (dctx->use_dsa) {
+        DSA *dsa_dh;
+        dsa_dh = dsa_dh_generate(dctx, pcb);
+        BN_GENCB_free(pcb);
+        if (dsa_dh == NULL)
+            return 0;
+        dh = DSA_dup_DH(dsa_dh);
+        DSA_free(dsa_dh);
+        if (!dh)
+            return 0;
+        EVP_PKEY_assign(pkey, EVP_PKEY_DHX, dh);
+        return 1;
+    }
+#endif
+    dh = DH_new();
+    if (dh == NULL) {
+        BN_GENCB_free(pcb);
+        return 0;
+    }
+    ret = DH_generate_parameters_ex(dh,
+                                    dctx->prime_len, dctx->generator, pcb);
+    BN_GENCB_free(pcb);
+    if (ret)
+        EVP_PKEY_assign_DH(pkey, dh);
+    else
+        DH_free(dh);
+    return ret;
+}
+
+static int pkey_dh_keygen(EVP_PKEY_CTX *ctx, EVP_PKEY *pkey)
+{
+    DH *dh = NULL;
+    if (ctx->pkey == NULL) {
+        DHerr(DH_F_PKEY_DH_KEYGEN, DH_R_NO_PARAMETERS_SET);
+        return 0;
+    }
+    dh = DH_new();
+    if (dh == NULL)
+        return 0;
+    EVP_PKEY_assign(pkey, ctx->pmeth->pkey_id, dh);
+    /* Note: if error return, pkey is freed by parent routine */
+    if (!EVP_PKEY_copy_parameters(pkey, ctx->pkey))
+        return 0;
+    return DH_generate_key(pkey->pkey.dh);
+}
+
+static int pkey_dh_derive(EVP_PKEY_CTX *ctx, unsigned char *key,
+                          size_t *keylen)
+{
+    int ret;
+    DH *dh;
+    DH_PKEY_CTX *dctx = ctx->data;
+    BIGNUM *dhpub;
+    if (!ctx->pkey || !ctx->peerkey) {
+        DHerr(DH_F_PKEY_DH_DERIVE, DH_R_KEYS_NOT_SET);
+        return 0;
+    }
+    dh = ctx->pkey->pkey.dh;
+    dhpub = ctx->peerkey->pkey.dh->pub_key;
+    if (dctx->kdf_type == EVP_PKEY_DH_KDF_NONE) {
+        if (key == NULL) {
+            *keylen = DH_size(dh);
+            return 1;
+        }
+        ret = DH_compute_key(key, dhpub, dh);
+        if (ret < 0)
+            return ret;
+        *keylen = ret;
+        return 1;
+    }
+#ifndef OPENSSL_NO_CMS
+    else if (dctx->kdf_type == EVP_PKEY_DH_KDF_X9_42) {
+
+        unsigned char *Z = NULL;
+        size_t Zlen = 0;
+        if (!dctx->kdf_outlen || !dctx->kdf_oid)
+            return 0;
+        if (key == NULL) {
+            *keylen = dctx->kdf_outlen;
+            return 1;
+        }
+        if (*keylen != dctx->kdf_outlen)
+            return 0;
+        ret = 0;
+        Zlen = DH_size(dh);
+        Z = OPENSSL_malloc(Zlen);
+        if (Z == NULL) {
+            goto err;
+        }
+        if (DH_compute_key_padded(Z, dhpub, dh) <= 0)
+            goto err;
+        if (!DH_KDF_X9_42(key, *keylen, Z, Zlen, dctx->kdf_oid,
+                          dctx->kdf_ukm, dctx->kdf_ukmlen, dctx->kdf_md))
+            goto err;
+        *keylen = dctx->kdf_outlen;
+        ret = 1;
+ err:
+        OPENSSL_clear_free(Z, Zlen);
+        return ret;
+    }
+#endif
+    return 0;
+}
+
+const EVP_PKEY_METHOD dh_pkey_meth = {
+    EVP_PKEY_DH,
+    0,
+    pkey_dh_init,
+    pkey_dh_copy,
+    pkey_dh_cleanup,
+
+    0,
+    pkey_dh_paramgen,
+
+    0,
+    pkey_dh_keygen,
+
+    0,
+    0,
+
+    0,
+    0,
+
+    0, 0,
+
+    0, 0, 0, 0,
+
+    0, 0,
+
+    0, 0,
+
+    0,
+    pkey_dh_derive,
+
+    pkey_dh_ctrl,
+    pkey_dh_ctrl_str
+};
+
+const EVP_PKEY_METHOD dhx_pkey_meth = {
+    EVP_PKEY_DHX,
+    0,
+    pkey_dh_init,
+    pkey_dh_copy,
+    pkey_dh_cleanup,
+
+    0,
+    pkey_dh_paramgen,
+
+    0,
+    pkey_dh_keygen,
+
+    0,
+    0,
+
+    0,
+    0,
+
+    0, 0,
+
+    0, 0, 0, 0,
+
+    0, 0,
+
+    0, 0,
+
+    0,
+    pkey_dh_derive,
+
+    pkey_dh_ctrl,
+    pkey_dh_ctrl_str
+};
diff --git a/crypto/dh/dh_prn.c b/crypto/dh/dh_prn.c
new file mode 100644
index 000000000..283fb0f4a
--- /dev/null
+++ b/crypto/dh/dh_prn.c
@@ -0,0 +1,30 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+
+#ifndef OPENSSL_NO_STDIO
+int DHparams_print_fp(FILE *fp, const DH *x)
+{
+    BIO *b;
+    int ret;
+
+    if ((b = BIO_new(BIO_s_file())) == NULL) {
+        DHerr(DH_F_DHPARAMS_PRINT_FP, ERR_R_BUF_LIB);
+        return (0);
+    }
+    BIO_set_fp(b, fp, BIO_NOCLOSE);
+    ret = DHparams_print(b, x);
+    BIO_free(b);
+    return (ret);
+}
+#endif
diff --git a/crypto/dh/dh_rfc5114.c b/crypto/dh/dh_rfc5114.c
new file mode 100644
index 000000000..c4a219590
--- /dev/null
+++ b/crypto/dh/dh_rfc5114.c
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2011-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include "dh_locl.h"
+#include 
+#include "internal/bn_dh.h"
+
+/*
+ * Macro to make a DH structure from BIGNUM data. NB: although just copying
+ * the BIGNUM static pointers would be more efficient, we can't do that
+ * because they get wiped using BN_clear_free() when DH_free() is called.
+ */
+
+#define make_dh(x) \
+DH *DH_get_##x(void) \
+{ \
+    DH *dh = DH_new(); \
+\
+    if (dh == NULL) \
+        return NULL; \
+    dh->p = BN_dup(&_bignum_dh##x##_p); \
+    dh->g = BN_dup(&_bignum_dh##x##_g); \
+    dh->q = BN_dup(&_bignum_dh##x##_q); \
+    if (dh->p == NULL || dh->q == NULL || dh->g == NULL) {\
+        DH_free(dh); \
+        return NULL; \
+    } \
+    return dh; \
+}
+
+make_dh(1024_160)
+make_dh(2048_224)
+make_dh(2048_256)
diff --git a/crypto/dllmain.c b/crypto/dllmain.c
new file mode 100644
index 000000000..91904aad9
--- /dev/null
+++ b/crypto/dllmain.c
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "internal/cryptlib_int.h"
+
+#if defined(_WIN32) || defined(__CYGWIN__)
+# ifdef __CYGWIN__
+/* pick DLL_[PROCESS|THREAD]_[ATTACH|DETACH] definitions */
+#  include 
+/*
+ * this has side-effect of _WIN32 getting defined, which otherwise is
+ * mutually exclusive with __CYGWIN__...
+ */
+# endif
+
+/*
+ * All we really need to do is remove the 'error' state when a thread
+ * detaches
+ */
+
+BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved);
+BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
+{
+    switch (fdwReason) {
+    case DLL_PROCESS_ATTACH:
+        OPENSSL_cpuid_setup();
+# if defined(_WIN32_WINNT)
+        {
+            IMAGE_DOS_HEADER *dos_header = (IMAGE_DOS_HEADER *) hinstDLL;
+            IMAGE_NT_HEADERS *nt_headers;
+
+            if (dos_header->e_magic == IMAGE_DOS_SIGNATURE) {
+                nt_headers = (IMAGE_NT_HEADERS *) ((char *)dos_header
+                                                   + dos_header->e_lfanew);
+                if (nt_headers->Signature == IMAGE_NT_SIGNATURE &&
+                    hinstDLL !=
+                    (HINSTANCE) (nt_headers->OptionalHeader.ImageBase))
+                    OPENSSL_NONPIC_relocated = 1;
+            }
+        }
+# endif
+        break;
+    case DLL_THREAD_ATTACH:
+        break;
+    case DLL_THREAD_DETACH:
+        OPENSSL_thread_stop();
+        break;
+    case DLL_PROCESS_DETACH:
+        break;
+    }
+    return (TRUE);
+}
+#endif
+
diff --git a/crypto/dsa/build.info b/crypto/dsa/build.info
new file mode 100644
index 000000000..2e759853a
--- /dev/null
+++ b/crypto/dsa/build.info
@@ -0,0 +1,5 @@
+LIBS=../../libcrypto
+SOURCE[../../libcrypto]=\
+        dsa_gen.c dsa_key.c dsa_lib.c dsa_asn1.c dsa_vrf.c dsa_sign.c \
+        dsa_err.c dsa_ossl.c dsa_depr.c dsa_ameth.c dsa_pmeth.c dsa_prn.c \
+        dsa_meth.c
diff --git a/crypto/dsa/dsa_ameth.c b/crypto/dsa/dsa_ameth.c
new file mode 100644
index 000000000..7c0428d3f
--- /dev/null
+++ b/crypto/dsa/dsa_ameth.c
@@ -0,0 +1,567 @@
+/*
+ * Copyright 2006-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+#include "dsa_locl.h"
+#include 
+#include 
+#include "internal/asn1_int.h"
+#include "internal/evp_int.h"
+
+static int dsa_pub_decode(EVP_PKEY *pkey, X509_PUBKEY *pubkey)
+{
+    const unsigned char *p, *pm;
+    int pklen, pmlen;
+    int ptype;
+    const void *pval;
+    const ASN1_STRING *pstr;
+    X509_ALGOR *palg;
+    ASN1_INTEGER *public_key = NULL;
+
+    DSA *dsa = NULL;
+
+    if (!X509_PUBKEY_get0_param(NULL, &p, &pklen, &palg, pubkey))
+        return 0;
+    X509_ALGOR_get0(NULL, &ptype, &pval, palg);
+
+    if (ptype == V_ASN1_SEQUENCE) {
+        pstr = pval;
+        pm = pstr->data;
+        pmlen = pstr->length;
+
+        if ((dsa = d2i_DSAparams(NULL, &pm, pmlen)) == NULL) {
+            DSAerr(DSA_F_DSA_PUB_DECODE, DSA_R_DECODE_ERROR);
+            goto err;
+        }
+
+    } else if ((ptype == V_ASN1_NULL) || (ptype == V_ASN1_UNDEF)) {
+        if ((dsa = DSA_new()) == NULL) {
+            DSAerr(DSA_F_DSA_PUB_DECODE, ERR_R_MALLOC_FAILURE);
+            goto err;
+        }
+    } else {
+        DSAerr(DSA_F_DSA_PUB_DECODE, DSA_R_PARAMETER_ENCODING_ERROR);
+        goto err;
+    }
+
+    if ((public_key = d2i_ASN1_INTEGER(NULL, &p, pklen)) == NULL) {
+        DSAerr(DSA_F_DSA_PUB_DECODE, DSA_R_DECODE_ERROR);
+        goto err;
+    }
+
+    if ((dsa->pub_key = ASN1_INTEGER_to_BN(public_key, NULL)) == NULL) {
+        DSAerr(DSA_F_DSA_PUB_DECODE, DSA_R_BN_DECODE_ERROR);
+        goto err;
+    }
+
+    ASN1_INTEGER_free(public_key);
+    EVP_PKEY_assign_DSA(pkey, dsa);
+    return 1;
+
+ err:
+    ASN1_INTEGER_free(public_key);
+    DSA_free(dsa);
+    return 0;
+
+}
+
+static int dsa_pub_encode(X509_PUBKEY *pk, const EVP_PKEY *pkey)
+{
+    DSA *dsa;
+    int ptype;
+    unsigned char *penc = NULL;
+    int penclen;
+    ASN1_STRING *str = NULL;
+    ASN1_INTEGER *pubint = NULL;
+
+    dsa = pkey->pkey.dsa;
+    if (pkey->save_parameters && dsa->p && dsa->q && dsa->g) {
+        str = ASN1_STRING_new();
+        if (str == NULL) {
+            DSAerr(DSA_F_DSA_PUB_ENCODE, ERR_R_MALLOC_FAILURE);
+            goto err;
+        }
+        str->length = i2d_DSAparams(dsa, &str->data);
+        if (str->length <= 0) {
+            DSAerr(DSA_F_DSA_PUB_ENCODE, ERR_R_MALLOC_FAILURE);
+            goto err;
+        }
+        ptype = V_ASN1_SEQUENCE;
+    } else
+        ptype = V_ASN1_UNDEF;
+
+    pubint = BN_to_ASN1_INTEGER(dsa->pub_key, NULL);
+
+    if (pubint == NULL) {
+        DSAerr(DSA_F_DSA_PUB_ENCODE, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+
+    penclen = i2d_ASN1_INTEGER(pubint, &penc);
+    ASN1_INTEGER_free(pubint);
+
+    if (penclen <= 0) {
+        DSAerr(DSA_F_DSA_PUB_ENCODE, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+
+    if (X509_PUBKEY_set0_param(pk, OBJ_nid2obj(EVP_PKEY_DSA),
+                               ptype, str, penc, penclen))
+        return 1;
+
+ err:
+    OPENSSL_free(penc);
+    ASN1_STRING_free(str);
+
+    return 0;
+}
+
+/*
+ * In PKCS#8 DSA: you just get a private key integer and parameters in the
+ * AlgorithmIdentifier the pubkey must be recalculated.
+ */
+
+static int dsa_priv_decode(EVP_PKEY *pkey, const PKCS8_PRIV_KEY_INFO *p8)
+{
+    const unsigned char *p, *pm;
+    int pklen, pmlen;
+    int ptype;
+    const void *pval;
+    const ASN1_STRING *pstr;
+    const X509_ALGOR *palg;
+    ASN1_INTEGER *privkey = NULL;
+    BN_CTX *ctx = NULL;
+
+    DSA *dsa = NULL;
+
+    int ret = 0;
+
+    if (!PKCS8_pkey_get0(NULL, &p, &pklen, &palg, p8))
+        return 0;
+    X509_ALGOR_get0(NULL, &ptype, &pval, palg);
+
+    if ((privkey = d2i_ASN1_INTEGER(NULL, &p, pklen)) == NULL)
+        goto decerr;
+    if (privkey->type == V_ASN1_NEG_INTEGER || ptype != V_ASN1_SEQUENCE)
+        goto decerr;
+
+    pstr = pval;
+    pm = pstr->data;
+    pmlen = pstr->length;
+    if ((dsa = d2i_DSAparams(NULL, &pm, pmlen)) == NULL)
+        goto decerr;
+    /* We have parameters now set private key */
+    if ((dsa->priv_key = BN_secure_new()) == NULL
+        || !ASN1_INTEGER_to_BN(privkey, dsa->priv_key)) {
+        DSAerr(DSA_F_DSA_PRIV_DECODE, DSA_R_BN_ERROR);
+        goto dsaerr;
+    }
+    /* Calculate public key */
+    if ((dsa->pub_key = BN_new()) == NULL) {
+        DSAerr(DSA_F_DSA_PRIV_DECODE, ERR_R_MALLOC_FAILURE);
+        goto dsaerr;
+    }
+    if ((ctx = BN_CTX_new()) == NULL) {
+        DSAerr(DSA_F_DSA_PRIV_DECODE, ERR_R_MALLOC_FAILURE);
+        goto dsaerr;
+    }
+
+    if (!BN_mod_exp(dsa->pub_key, dsa->g, dsa->priv_key, dsa->p, ctx)) {
+        DSAerr(DSA_F_DSA_PRIV_DECODE, DSA_R_BN_ERROR);
+        goto dsaerr;
+    }
+
+    EVP_PKEY_assign_DSA(pkey, dsa);
+
+    ret = 1;
+    goto done;
+
+ decerr:
+    DSAerr(DSA_F_DSA_PRIV_DECODE, DSA_R_DECODE_ERROR);
+ dsaerr:
+    DSA_free(dsa);
+ done:
+    BN_CTX_free(ctx);
+    ASN1_STRING_clear_free(privkey);
+    return ret;
+}
+
+static int dsa_priv_encode(PKCS8_PRIV_KEY_INFO *p8, const EVP_PKEY *pkey)
+{
+    ASN1_STRING *params = NULL;
+    ASN1_INTEGER *prkey = NULL;
+    unsigned char *dp = NULL;
+    int dplen;
+
+    if (!pkey->pkey.dsa || !pkey->pkey.dsa->priv_key) {
+        DSAerr(DSA_F_DSA_PRIV_ENCODE, DSA_R_MISSING_PARAMETERS);
+        goto err;
+    }
+
+    params = ASN1_STRING_new();
+
+    if (params == NULL) {
+        DSAerr(DSA_F_DSA_PRIV_ENCODE, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+
+    params->length = i2d_DSAparams(pkey->pkey.dsa, ¶ms->data);
+    if (params->length <= 0) {
+        DSAerr(DSA_F_DSA_PRIV_ENCODE, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+    params->type = V_ASN1_SEQUENCE;
+
+    /* Get private key into integer */
+    prkey = BN_to_ASN1_INTEGER(pkey->pkey.dsa->priv_key, NULL);
+
+    if (!prkey) {
+        DSAerr(DSA_F_DSA_PRIV_ENCODE, DSA_R_BN_ERROR);
+        goto err;
+    }
+
+    dplen = i2d_ASN1_INTEGER(prkey, &dp);
+
+    ASN1_STRING_clear_free(prkey);
+    prkey = NULL;
+
+    if (!PKCS8_pkey_set0(p8, OBJ_nid2obj(NID_dsa), 0,
+                         V_ASN1_SEQUENCE, params, dp, dplen))
+        goto err;
+
+    return 1;
+
+ err:
+    OPENSSL_free(dp);
+    ASN1_STRING_free(params);
+    ASN1_STRING_clear_free(prkey);
+    return 0;
+}
+
+static int int_dsa_size(const EVP_PKEY *pkey)
+{
+    return (DSA_size(pkey->pkey.dsa));
+}
+
+static int dsa_bits(const EVP_PKEY *pkey)
+{
+    return DSA_bits(pkey->pkey.dsa);
+}
+
+static int dsa_security_bits(const EVP_PKEY *pkey)
+{
+    return DSA_security_bits(pkey->pkey.dsa);
+}
+
+static int dsa_missing_parameters(const EVP_PKEY *pkey)
+{
+    DSA *dsa;
+    dsa = pkey->pkey.dsa;
+    if (dsa == NULL || dsa->p == NULL || dsa->q == NULL || dsa->g == NULL)
+        return 1;
+    return 0;
+}
+
+static int dsa_copy_parameters(EVP_PKEY *to, const EVP_PKEY *from)
+{
+    BIGNUM *a;
+
+    if (to->pkey.dsa == NULL) {
+        to->pkey.dsa = DSA_new();
+        if (to->pkey.dsa == NULL)
+            return 0;
+    }
+
+    if ((a = BN_dup(from->pkey.dsa->p)) == NULL)
+        return 0;
+    BN_free(to->pkey.dsa->p);
+    to->pkey.dsa->p = a;
+
+    if ((a = BN_dup(from->pkey.dsa->q)) == NULL)
+        return 0;
+    BN_free(to->pkey.dsa->q);
+    to->pkey.dsa->q = a;
+
+    if ((a = BN_dup(from->pkey.dsa->g)) == NULL)
+        return 0;
+    BN_free(to->pkey.dsa->g);
+    to->pkey.dsa->g = a;
+    return 1;
+}
+
+static int dsa_cmp_parameters(const EVP_PKEY *a, const EVP_PKEY *b)
+{
+    if (BN_cmp(a->pkey.dsa->p, b->pkey.dsa->p) ||
+        BN_cmp(a->pkey.dsa->q, b->pkey.dsa->q) ||
+        BN_cmp(a->pkey.dsa->g, b->pkey.dsa->g))
+        return 0;
+    else
+        return 1;
+}
+
+static int dsa_pub_cmp(const EVP_PKEY *a, const EVP_PKEY *b)
+{
+    if (BN_cmp(b->pkey.dsa->pub_key, a->pkey.dsa->pub_key) != 0)
+        return 0;
+    else
+        return 1;
+}
+
+static void int_dsa_free(EVP_PKEY *pkey)
+{
+    DSA_free(pkey->pkey.dsa);
+}
+
+static int do_dsa_print(BIO *bp, const DSA *x, int off, int ptype)
+{
+    int ret = 0;
+    const char *ktype = NULL;
+    const BIGNUM *priv_key, *pub_key;
+
+    if (ptype == 2)
+        priv_key = x->priv_key;
+    else
+        priv_key = NULL;
+
+    if (ptype > 0)
+        pub_key = x->pub_key;
+    else
+        pub_key = NULL;
+
+    if (ptype == 2)
+        ktype = "Private-Key";
+    else if (ptype == 1)
+        ktype = "Public-Key";
+    else
+        ktype = "DSA-Parameters";
+
+    if (priv_key) {
+        if (!BIO_indent(bp, off, 128))
+            goto err;
+        if (BIO_printf(bp, "%s: (%d bit)\n", ktype, BN_num_bits(x->p))
+            <= 0)
+            goto err;
+    }
+
+    if (!ASN1_bn_print(bp, "priv:", priv_key, NULL, off))
+        goto err;
+    if (!ASN1_bn_print(bp, "pub: ", pub_key, NULL, off))
+        goto err;
+    if (!ASN1_bn_print(bp, "P:   ", x->p, NULL, off))
+        goto err;
+    if (!ASN1_bn_print(bp, "Q:   ", x->q, NULL, off))
+        goto err;
+    if (!ASN1_bn_print(bp, "G:   ", x->g, NULL, off))
+        goto err;
+    ret = 1;
+ err:
+    return (ret);
+}
+
+static int dsa_param_decode(EVP_PKEY *pkey,
+                            const unsigned char **pder, int derlen)
+{
+    DSA *dsa;
+
+    if ((dsa = d2i_DSAparams(NULL, pder, derlen)) == NULL) {
+        DSAerr(DSA_F_DSA_PARAM_DECODE, ERR_R_DSA_LIB);
+        return 0;
+    }
+    EVP_PKEY_assign_DSA(pkey, dsa);
+    return 1;
+}
+
+static int dsa_param_encode(const EVP_PKEY *pkey, unsigned char **pder)
+{
+    return i2d_DSAparams(pkey->pkey.dsa, pder);
+}
+
+static int dsa_param_print(BIO *bp, const EVP_PKEY *pkey, int indent,
+                           ASN1_PCTX *ctx)
+{
+    return do_dsa_print(bp, pkey->pkey.dsa, indent, 0);
+}
+
+static int dsa_pub_print(BIO *bp, const EVP_PKEY *pkey, int indent,
+                         ASN1_PCTX *ctx)
+{
+    return do_dsa_print(bp, pkey->pkey.dsa, indent, 1);
+}
+
+static int dsa_priv_print(BIO *bp, const EVP_PKEY *pkey, int indent,
+                          ASN1_PCTX *ctx)
+{
+    return do_dsa_print(bp, pkey->pkey.dsa, indent, 2);
+}
+
+static int old_dsa_priv_decode(EVP_PKEY *pkey,
+                               const unsigned char **pder, int derlen)
+{
+    DSA *dsa;
+
+    if ((dsa = d2i_DSAPrivateKey(NULL, pder, derlen)) == NULL) {
+        DSAerr(DSA_F_OLD_DSA_PRIV_DECODE, ERR_R_DSA_LIB);
+        return 0;
+    }
+    EVP_PKEY_assign_DSA(pkey, dsa);
+    return 1;
+}
+
+static int old_dsa_priv_encode(const EVP_PKEY *pkey, unsigned char **pder)
+{
+    return i2d_DSAPrivateKey(pkey->pkey.dsa, pder);
+}
+
+static int dsa_sig_print(BIO *bp, const X509_ALGOR *sigalg,
+                         const ASN1_STRING *sig, int indent, ASN1_PCTX *pctx)
+{
+    DSA_SIG *dsa_sig;
+    const unsigned char *p;
+
+    if (!sig) {
+        if (BIO_puts(bp, "\n") <= 0)
+            return 0;
+        else
+            return 1;
+    }
+    p = sig->data;
+    dsa_sig = d2i_DSA_SIG(NULL, &p, sig->length);
+    if (dsa_sig) {
+        int rv = 0;
+        const BIGNUM *r, *s;
+
+        DSA_SIG_get0(dsa_sig, &r, &s);
+
+        if (BIO_write(bp, "\n", 1) != 1)
+            goto err;
+
+        if (!ASN1_bn_print(bp, "r:   ", r, NULL, indent))
+            goto err;
+        if (!ASN1_bn_print(bp, "s:   ", s, NULL, indent))
+            goto err;
+        rv = 1;
+ err:
+        DSA_SIG_free(dsa_sig);
+        return rv;
+    }
+    return X509_signature_dump(bp, sig, indent);
+}
+
+static int dsa_pkey_ctrl(EVP_PKEY *pkey, int op, long arg1, void *arg2)
+{
+    switch (op) {
+    case ASN1_PKEY_CTRL_PKCS7_SIGN:
+        if (arg1 == 0) {
+            int snid, hnid;
+            X509_ALGOR *alg1, *alg2;
+            PKCS7_SIGNER_INFO_get0_algs(arg2, NULL, &alg1, &alg2);
+            if (alg1 == NULL || alg1->algorithm == NULL)
+                return -1;
+            hnid = OBJ_obj2nid(alg1->algorithm);
+            if (hnid == NID_undef)
+                return -1;
+            if (!OBJ_find_sigid_by_algs(&snid, hnid, EVP_PKEY_id(pkey)))
+                return -1;
+            X509_ALGOR_set0(alg2, OBJ_nid2obj(snid), V_ASN1_UNDEF, 0);
+        }
+        return 1;
+#ifndef OPENSSL_NO_CMS
+    case ASN1_PKEY_CTRL_CMS_SIGN:
+        if (arg1 == 0) {
+            int snid, hnid;
+            X509_ALGOR *alg1, *alg2;
+            CMS_SignerInfo_get0_algs(arg2, NULL, NULL, &alg1, &alg2);
+            if (alg1 == NULL || alg1->algorithm == NULL)
+                return -1;
+            hnid = OBJ_obj2nid(alg1->algorithm);
+            if (hnid == NID_undef)
+                return -1;
+            if (!OBJ_find_sigid_by_algs(&snid, hnid, EVP_PKEY_id(pkey)))
+                return -1;
+            X509_ALGOR_set0(alg2, OBJ_nid2obj(snid), V_ASN1_UNDEF, 0);
+        }
+        return 1;
+
+    case ASN1_PKEY_CTRL_CMS_RI_TYPE:
+        *(int *)arg2 = CMS_RECIPINFO_NONE;
+        return 1;
+#endif
+
+    case ASN1_PKEY_CTRL_DEFAULT_MD_NID:
+        *(int *)arg2 = NID_sha256;
+        return 2;
+
+    default:
+        return -2;
+
+    }
+
+}
+
+/* NB these are sorted in pkey_id order, lowest first */
+
+const EVP_PKEY_ASN1_METHOD dsa_asn1_meths[5] = {
+
+    {
+     EVP_PKEY_DSA2,
+     EVP_PKEY_DSA,
+     ASN1_PKEY_ALIAS},
+
+    {
+     EVP_PKEY_DSA1,
+     EVP_PKEY_DSA,
+     ASN1_PKEY_ALIAS},
+
+    {
+     EVP_PKEY_DSA4,
+     EVP_PKEY_DSA,
+     ASN1_PKEY_ALIAS},
+
+    {
+     EVP_PKEY_DSA3,
+     EVP_PKEY_DSA,
+     ASN1_PKEY_ALIAS},
+
+    {
+     EVP_PKEY_DSA,
+     EVP_PKEY_DSA,
+     0,
+
+     "DSA",
+     "OpenSSL DSA method",
+
+     dsa_pub_decode,
+     dsa_pub_encode,
+     dsa_pub_cmp,
+     dsa_pub_print,
+
+     dsa_priv_decode,
+     dsa_priv_encode,
+     dsa_priv_print,
+
+     int_dsa_size,
+     dsa_bits,
+     dsa_security_bits,
+
+     dsa_param_decode,
+     dsa_param_encode,
+     dsa_missing_parameters,
+     dsa_copy_parameters,
+     dsa_cmp_parameters,
+     dsa_param_print,
+     dsa_sig_print,
+
+     int_dsa_free,
+     dsa_pkey_ctrl,
+     old_dsa_priv_decode,
+     old_dsa_priv_encode}
+};
diff --git a/crypto/dsa/dsa_asn1.c b/crypto/dsa/dsa_asn1.c
new file mode 100644
index 000000000..551c10750
--- /dev/null
+++ b/crypto/dsa/dsa_asn1.c
@@ -0,0 +1,155 @@
+/*
+ * Copyright 1999-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include "dsa_locl.h"
+#include 
+#include 
+#include 
+
+ASN1_SEQUENCE(DSA_SIG) = {
+        ASN1_SIMPLE(DSA_SIG, r, CBIGNUM),
+        ASN1_SIMPLE(DSA_SIG, s, CBIGNUM)
+} static_ASN1_SEQUENCE_END(DSA_SIG)
+
+IMPLEMENT_ASN1_ENCODE_FUNCTIONS_const_fname(DSA_SIG, DSA_SIG, DSA_SIG)
+
+DSA_SIG *DSA_SIG_new(void)
+{
+    DSA_SIG *sig = OPENSSL_zalloc(sizeof(*sig));
+    if (sig == NULL)
+        DSAerr(DSA_F_DSA_SIG_NEW, ERR_R_MALLOC_FAILURE);
+    return sig;
+}
+
+void DSA_SIG_free(DSA_SIG *sig)
+{
+    if (sig == NULL)
+        return;
+    BN_clear_free(sig->r);
+    BN_clear_free(sig->s);
+    OPENSSL_free(sig);
+}
+
+void DSA_SIG_get0(const DSA_SIG *sig, const BIGNUM **pr, const BIGNUM **ps)
+{
+    if (pr != NULL)
+        *pr = sig->r;
+    if (ps != NULL)
+        *ps = sig->s;
+}
+
+int DSA_SIG_set0(DSA_SIG *sig, BIGNUM *r, BIGNUM *s)
+{
+    if (r == NULL || s == NULL)
+        return 0;
+    BN_clear_free(sig->r);
+    BN_clear_free(sig->s);
+    sig->r = r;
+    sig->s = s;
+    return 1;
+}
+
+/* Override the default free and new methods */
+static int dsa_cb(int operation, ASN1_VALUE **pval, const ASN1_ITEM *it,
+                  void *exarg)
+{
+    if (operation == ASN1_OP_NEW_PRE) {
+        *pval = (ASN1_VALUE *)DSA_new();
+        if (*pval != NULL)
+            return 2;
+        return 0;
+    } else if (operation == ASN1_OP_FREE_PRE) {
+        DSA_free((DSA *)*pval);
+        *pval = NULL;
+        return 2;
+    }
+    return 1;
+}
+
+ASN1_SEQUENCE_cb(DSAPrivateKey, dsa_cb) = {
+        ASN1_SIMPLE(DSA, version, LONG),
+        ASN1_SIMPLE(DSA, p, BIGNUM),
+        ASN1_SIMPLE(DSA, q, BIGNUM),
+        ASN1_SIMPLE(DSA, g, BIGNUM),
+        ASN1_SIMPLE(DSA, pub_key, BIGNUM),
+        ASN1_SIMPLE(DSA, priv_key, CBIGNUM)
+} static_ASN1_SEQUENCE_END_cb(DSA, DSAPrivateKey)
+
+IMPLEMENT_ASN1_ENCODE_FUNCTIONS_const_fname(DSA, DSAPrivateKey, DSAPrivateKey)
+
+ASN1_SEQUENCE_cb(DSAparams, dsa_cb) = {
+        ASN1_SIMPLE(DSA, p, BIGNUM),
+        ASN1_SIMPLE(DSA, q, BIGNUM),
+        ASN1_SIMPLE(DSA, g, BIGNUM),
+} static_ASN1_SEQUENCE_END_cb(DSA, DSAparams)
+
+IMPLEMENT_ASN1_ENCODE_FUNCTIONS_const_fname(DSA, DSAparams, DSAparams)
+
+ASN1_SEQUENCE_cb(DSAPublicKey, dsa_cb) = {
+        ASN1_SIMPLE(DSA, pub_key, BIGNUM),
+        ASN1_SIMPLE(DSA, p, BIGNUM),
+        ASN1_SIMPLE(DSA, q, BIGNUM),
+        ASN1_SIMPLE(DSA, g, BIGNUM)
+} static_ASN1_SEQUENCE_END_cb(DSA, DSAPublicKey)
+
+IMPLEMENT_ASN1_ENCODE_FUNCTIONS_const_fname(DSA, DSAPublicKey, DSAPublicKey)
+
+DSA *DSAparams_dup(DSA *dsa)
+{
+    return ASN1_item_dup(ASN1_ITEM_rptr(DSAparams), dsa);
+}
+
+int DSA_sign(int type, const unsigned char *dgst, int dlen,
+             unsigned char *sig, unsigned int *siglen, DSA *dsa)
+{
+    DSA_SIG *s;
+    RAND_seed(dgst, dlen);
+    s = DSA_do_sign(dgst, dlen, dsa);
+    if (s == NULL) {
+        *siglen = 0;
+        return (0);
+    }
+    *siglen = i2d_DSA_SIG(s, &sig);
+    DSA_SIG_free(s);
+    return (1);
+}
+
+/* data has already been hashed (probably with SHA or SHA-1). */
+/*-
+ * returns
+ *      1: correct signature
+ *      0: incorrect signature
+ *     -1: error
+ */
+int DSA_verify(int type, const unsigned char *dgst, int dgst_len,
+               const unsigned char *sigbuf, int siglen, DSA *dsa)
+{
+    DSA_SIG *s;
+    const unsigned char *p = sigbuf;
+    unsigned char *der = NULL;
+    int derlen = -1;
+    int ret = -1;
+
+    s = DSA_SIG_new();
+    if (s == NULL)
+        return (ret);
+    if (d2i_DSA_SIG(&s, &p, siglen) == NULL)
+        goto err;
+    /* Ensure signature uses DER and doesn't have trailing garbage */
+    derlen = i2d_DSA_SIG(s, &der);
+    if (derlen != siglen || memcmp(sigbuf, der, derlen))
+        goto err;
+    ret = DSA_do_verify(dgst, dgst_len, s, dsa);
+ err:
+    OPENSSL_clear_free(der, derlen);
+    DSA_SIG_free(s);
+    return (ret);
+}
diff --git a/crypto/dsa/dsa_depr.c b/crypto/dsa/dsa_depr.c
new file mode 100644
index 000000000..f51aea749
--- /dev/null
+++ b/crypto/dsa/dsa_depr.c
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2002-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * This file contains deprecated function(s) that are now wrappers to the new
+ * version(s).
+ */
+
+/*
+ * Parameter generation follows the updated Appendix 2.2 for FIPS PUB 186,
+ * also Appendix 2.2 of FIPS PUB 186-1 (i.e. use SHA as defined in FIPS PUB
+ * 180-1)
+ */
+#define xxxHASH    EVP_sha1()
+
+#include 
+#if OPENSSL_API_COMPAT >= 0x00908000L
+NON_EMPTY_TRANSLATION_UNIT
+#else
+
+# include 
+# include 
+# include "internal/cryptlib.h"
+# include 
+# include 
+# include 
+# include 
+
+DSA *DSA_generate_parameters(int bits,
+                             unsigned char *seed_in, int seed_len,
+                             int *counter_ret, unsigned long *h_ret,
+                             void (*callback) (int, int, void *),
+                             void *cb_arg)
+{
+    BN_GENCB *cb;
+    DSA *ret;
+
+    if ((ret = DSA_new()) == NULL)
+        return NULL;
+    cb = BN_GENCB_new();
+    if (cb == NULL)
+        goto err;
+
+    BN_GENCB_set_old(cb, callback, cb_arg);
+
+    if (DSA_generate_parameters_ex(ret, bits, seed_in, seed_len,
+                                   counter_ret, h_ret, cb)) {
+        BN_GENCB_free(cb);
+        return ret;
+    }
+    BN_GENCB_free(cb);
+err:
+    DSA_free(ret);
+    return NULL;
+}
+#endif
diff --git a/crypto/dsa/dsa_err.c b/crypto/dsa/dsa_err.c
new file mode 100644
index 000000000..b8f0af466
--- /dev/null
+++ b/crypto/dsa/dsa_err.c
@@ -0,0 +1,76 @@
+/*
+ * Generated by util/mkerr.pl DO NOT EDIT
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include 
+
+/* BEGIN ERROR CODES */
+#ifndef OPENSSL_NO_ERR
+
+# define ERR_FUNC(func) ERR_PACK(ERR_LIB_DSA,func,0)
+# define ERR_REASON(reason) ERR_PACK(ERR_LIB_DSA,0,reason)
+
+static ERR_STRING_DATA DSA_str_functs[] = {
+    {ERR_FUNC(DSA_F_DSAPARAMS_PRINT), "DSAparams_print"},
+    {ERR_FUNC(DSA_F_DSAPARAMS_PRINT_FP), "DSAparams_print_fp"},
+    {ERR_FUNC(DSA_F_DSA_BUILTIN_PARAMGEN), "dsa_builtin_paramgen"},
+    {ERR_FUNC(DSA_F_DSA_BUILTIN_PARAMGEN2), "dsa_builtin_paramgen2"},
+    {ERR_FUNC(DSA_F_DSA_DO_SIGN), "DSA_do_sign"},
+    {ERR_FUNC(DSA_F_DSA_DO_VERIFY), "DSA_do_verify"},
+    {ERR_FUNC(DSA_F_DSA_METH_DUP), "DSA_meth_dup"},
+    {ERR_FUNC(DSA_F_DSA_METH_NEW), "DSA_meth_new"},
+    {ERR_FUNC(DSA_F_DSA_METH_SET1_NAME), "DSA_meth_set1_name"},
+    {ERR_FUNC(DSA_F_DSA_NEW_METHOD), "DSA_new_method"},
+    {ERR_FUNC(DSA_F_DSA_PARAM_DECODE), "dsa_param_decode"},
+    {ERR_FUNC(DSA_F_DSA_PRINT_FP), "DSA_print_fp"},
+    {ERR_FUNC(DSA_F_DSA_PRIV_DECODE), "dsa_priv_decode"},
+    {ERR_FUNC(DSA_F_DSA_PRIV_ENCODE), "dsa_priv_encode"},
+    {ERR_FUNC(DSA_F_DSA_PUB_DECODE), "dsa_pub_decode"},
+    {ERR_FUNC(DSA_F_DSA_PUB_ENCODE), "dsa_pub_encode"},
+    {ERR_FUNC(DSA_F_DSA_SIGN), "DSA_sign"},
+    {ERR_FUNC(DSA_F_DSA_SIGN_SETUP), "DSA_sign_setup"},
+    {ERR_FUNC(DSA_F_DSA_SIG_NEW), "DSA_SIG_new"},
+    {ERR_FUNC(DSA_F_OLD_DSA_PRIV_DECODE), "old_dsa_priv_decode"},
+    {ERR_FUNC(DSA_F_PKEY_DSA_CTRL), "pkey_dsa_ctrl"},
+    {ERR_FUNC(DSA_F_PKEY_DSA_KEYGEN), "pkey_dsa_keygen"},
+    {0, NULL}
+};
+
+static ERR_STRING_DATA DSA_str_reasons[] = {
+    {ERR_REASON(DSA_R_BAD_Q_VALUE), "bad q value"},
+    {ERR_REASON(DSA_R_BN_DECODE_ERROR), "bn decode error"},
+    {ERR_REASON(DSA_R_BN_ERROR), "bn error"},
+    {ERR_REASON(DSA_R_DECODE_ERROR), "decode error"},
+    {ERR_REASON(DSA_R_INVALID_DIGEST_TYPE), "invalid digest type"},
+    {ERR_REASON(DSA_R_INVALID_PARAMETERS), "invalid parameters"},
+    {ERR_REASON(DSA_R_MISSING_PARAMETERS), "missing parameters"},
+    {ERR_REASON(DSA_R_MODULUS_TOO_LARGE), "modulus too large"},
+    {ERR_REASON(DSA_R_NO_PARAMETERS_SET), "no parameters set"},
+    {ERR_REASON(DSA_R_PARAMETER_ENCODING_ERROR), "parameter encoding error"},
+    {ERR_REASON(DSA_R_Q_NOT_PRIME), "q not prime"},
+    {ERR_REASON(DSA_R_SEED_LEN_SMALL),
+     "seed_len is less than the length of q"},
+    {0, NULL}
+};
+
+#endif
+
+int ERR_load_DSA_strings(void)
+{
+#ifndef OPENSSL_NO_ERR
+
+    if (ERR_func_error_string(DSA_str_functs[0].error) == NULL) {
+        ERR_load_strings(0, DSA_str_functs);
+        ERR_load_strings(0, DSA_str_reasons);
+    }
+#endif
+    return 1;
+}
diff --git a/crypto/dsa/dsa_gen.c b/crypto/dsa/dsa_gen.c
new file mode 100644
index 000000000..3efeab84f
--- /dev/null
+++ b/crypto/dsa/dsa_gen.c
@@ -0,0 +1,601 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * Parameter generation follows the updated Appendix 2.2 for FIPS PUB 186,
+ * also Appendix 2.2 of FIPS PUB 186-1 (i.e. use SHA as defined in FIPS PUB
+ * 180-1)
+ */
+#define xxxHASH    EVP_sha1()
+
+#include 
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+#include 
+#include 
+#include "dsa_locl.h"
+
+int DSA_generate_parameters_ex(DSA *ret, int bits,
+                               const unsigned char *seed_in, int seed_len,
+                               int *counter_ret, unsigned long *h_ret,
+                               BN_GENCB *cb)
+{
+    if (ret->meth->dsa_paramgen)
+        return ret->meth->dsa_paramgen(ret, bits, seed_in, seed_len,
+                                       counter_ret, h_ret, cb);
+    else {
+        const EVP_MD *evpmd = bits >= 2048 ? EVP_sha256() : EVP_sha1();
+        size_t qbits = EVP_MD_size(evpmd) * 8;
+
+        return dsa_builtin_paramgen(ret, bits, qbits, evpmd,
+                                    seed_in, seed_len, NULL, counter_ret,
+                                    h_ret, cb);
+    }
+}
+
+int dsa_builtin_paramgen(DSA *ret, size_t bits, size_t qbits,
+                         const EVP_MD *evpmd, const unsigned char *seed_in,
+                         size_t seed_len, unsigned char *seed_out,
+                         int *counter_ret, unsigned long *h_ret, BN_GENCB *cb)
+{
+    int ok = 0;
+    unsigned char seed[SHA256_DIGEST_LENGTH];
+    unsigned char md[SHA256_DIGEST_LENGTH];
+    unsigned char buf[SHA256_DIGEST_LENGTH], buf2[SHA256_DIGEST_LENGTH];
+    BIGNUM *r0, *W, *X, *c, *test;
+    BIGNUM *g = NULL, *q = NULL, *p = NULL;
+    BN_MONT_CTX *mont = NULL;
+    int i, k, n = 0, m = 0, qsize = qbits >> 3;
+    int counter = 0;
+    int r = 0;
+    BN_CTX *ctx = NULL;
+    unsigned int h = 2;
+
+    if (qsize != SHA_DIGEST_LENGTH && qsize != SHA224_DIGEST_LENGTH &&
+        qsize != SHA256_DIGEST_LENGTH)
+        /* invalid q size */
+        return 0;
+
+    if (evpmd == NULL)
+        /* use SHA1 as default */
+        evpmd = EVP_sha1();
+
+    if (bits < 512)
+        bits = 512;
+
+    bits = (bits + 63) / 64 * 64;
+
+    if (seed_in != NULL) {
+        if (seed_len < (size_t)qsize) {
+            DSAerr(DSA_F_DSA_BUILTIN_PARAMGEN, DSA_R_SEED_LEN_SMALL);
+            return 0;
+        }
+        if (seed_len > (size_t)qsize) {
+            /* Only consume as much seed as is expected. */
+            seed_len = qsize;
+        }
+        memcpy(seed, seed_in, seed_len);
+    }
+
+    if ((mont = BN_MONT_CTX_new()) == NULL)
+        goto err;
+
+    if ((ctx = BN_CTX_new()) == NULL)
+        goto err;
+
+    BN_CTX_start(ctx);
+
+    r0 = BN_CTX_get(ctx);
+    g = BN_CTX_get(ctx);
+    W = BN_CTX_get(ctx);
+    q = BN_CTX_get(ctx);
+    X = BN_CTX_get(ctx);
+    c = BN_CTX_get(ctx);
+    p = BN_CTX_get(ctx);
+    test = BN_CTX_get(ctx);
+
+    if (test == NULL)
+        goto err;
+
+    if (!BN_lshift(test, BN_value_one(), bits - 1))
+        goto err;
+
+    for (;;) {
+        for (;;) {              /* find q */
+            int use_random_seed = (seed_in == NULL);
+
+            /* step 1 */
+            if (!BN_GENCB_call(cb, 0, m++))
+                goto err;
+
+            if (use_random_seed) {
+                if (RAND_bytes(seed, qsize) <= 0)
+                    goto err;
+            } else {
+                /* If we come back through, use random seed next time. */
+                seed_in = NULL;
+            }
+            memcpy(buf, seed, qsize);
+            memcpy(buf2, seed, qsize);
+            /* precompute "SEED + 1" for step 7: */
+            for (i = qsize - 1; i >= 0; i--) {
+                buf[i]++;
+                if (buf[i] != 0)
+                    break;
+            }
+
+            /* step 2 */
+            if (!EVP_Digest(seed, qsize, md, NULL, evpmd, NULL))
+                goto err;
+            if (!EVP_Digest(buf, qsize, buf2, NULL, evpmd, NULL))
+                goto err;
+            for (i = 0; i < qsize; i++)
+                md[i] ^= buf2[i];
+
+            /* step 3 */
+            md[0] |= 0x80;
+            md[qsize - 1] |= 0x01;
+            if (!BN_bin2bn(md, qsize, q))
+                goto err;
+
+            /* step 4 */
+            r = BN_is_prime_fasttest_ex(q, DSS_prime_checks, ctx,
+                                        use_random_seed, cb);
+            if (r > 0)
+                break;
+            if (r != 0)
+                goto err;
+
+            /* do a callback call */
+            /* step 5 */
+        }
+
+        if (!BN_GENCB_call(cb, 2, 0))
+            goto err;
+        if (!BN_GENCB_call(cb, 3, 0))
+            goto err;
+
+        /* step 6 */
+        counter = 0;
+        /* "offset = 2" */
+
+        n = (bits - 1) / 160;
+
+        for (;;) {
+            if ((counter != 0) && !BN_GENCB_call(cb, 0, counter))
+                goto err;
+
+            /* step 7 */
+            BN_zero(W);
+            /* now 'buf' contains "SEED + offset - 1" */
+            for (k = 0; k <= n; k++) {
+                /*
+                 * obtain "SEED + offset + k" by incrementing:
+                 */
+                for (i = qsize - 1; i >= 0; i--) {
+                    buf[i]++;
+                    if (buf[i] != 0)
+                        break;
+                }
+
+                if (!EVP_Digest(buf, qsize, md, NULL, evpmd, NULL))
+                    goto err;
+
+                /* step 8 */
+                if (!BN_bin2bn(md, qsize, r0))
+                    goto err;
+                if (!BN_lshift(r0, r0, (qsize << 3) * k))
+                    goto err;
+                if (!BN_add(W, W, r0))
+                    goto err;
+            }
+
+            /* more of step 8 */
+            if (!BN_mask_bits(W, bits - 1))
+                goto err;
+            if (!BN_copy(X, W))
+                goto err;
+            if (!BN_add(X, X, test))
+                goto err;
+
+            /* step 9 */
+            if (!BN_lshift1(r0, q))
+                goto err;
+            if (!BN_mod(c, X, r0, ctx))
+                goto err;
+            if (!BN_sub(r0, c, BN_value_one()))
+                goto err;
+            if (!BN_sub(p, X, r0))
+                goto err;
+
+            /* step 10 */
+            if (BN_cmp(p, test) >= 0) {
+                /* step 11 */
+                r = BN_is_prime_fasttest_ex(p, DSS_prime_checks, ctx, 1, cb);
+                if (r > 0)
+                    goto end;   /* found it */
+                if (r != 0)
+                    goto err;
+            }
+
+            /* step 13 */
+            counter++;
+            /* "offset = offset + n + 1" */
+
+            /* step 14 */
+            if (counter >= 4096)
+                break;
+        }
+    }
+ end:
+    if (!BN_GENCB_call(cb, 2, 1))
+        goto err;
+
+    /* We now need to generate g */
+    /* Set r0=(p-1)/q */
+    if (!BN_sub(test, p, BN_value_one()))
+        goto err;
+    if (!BN_div(r0, NULL, test, q, ctx))
+        goto err;
+
+    if (!BN_set_word(test, h))
+        goto err;
+    if (!BN_MONT_CTX_set(mont, p, ctx))
+        goto err;
+
+    for (;;) {
+        /* g=test^r0%p */
+        if (!BN_mod_exp_mont(g, test, r0, p, ctx, mont))
+            goto err;
+        if (!BN_is_one(g))
+            break;
+        if (!BN_add(test, test, BN_value_one()))
+            goto err;
+        h++;
+    }
+
+    if (!BN_GENCB_call(cb, 3, 1))
+        goto err;
+
+    ok = 1;
+ err:
+    if (ok) {
+        BN_free(ret->p);
+        BN_free(ret->q);
+        BN_free(ret->g);
+        ret->p = BN_dup(p);
+        ret->q = BN_dup(q);
+        ret->g = BN_dup(g);
+        if (ret->p == NULL || ret->q == NULL || ret->g == NULL) {
+            ok = 0;
+            goto err;
+        }
+        if (counter_ret != NULL)
+            *counter_ret = counter;
+        if (h_ret != NULL)
+            *h_ret = h;
+        if (seed_out)
+            memcpy(seed_out, seed, qsize);
+    }
+    if (ctx)
+        BN_CTX_end(ctx);
+    BN_CTX_free(ctx);
+    BN_MONT_CTX_free(mont);
+    return ok;
+}
+
+/*
+ * This is a parameter generation algorithm for the DSA2 algorithm as
+ * described in FIPS 186-3.
+ */
+
+int dsa_builtin_paramgen2(DSA *ret, size_t L, size_t N,
+                          const EVP_MD *evpmd, const unsigned char *seed_in,
+                          size_t seed_len, int idx, unsigned char *seed_out,
+                          int *counter_ret, unsigned long *h_ret,
+                          BN_GENCB *cb)
+{
+    int ok = -1;
+    unsigned char *seed = NULL, *seed_tmp = NULL;
+    unsigned char md[EVP_MAX_MD_SIZE];
+    int mdsize;
+    BIGNUM *r0, *W, *X, *c, *test;
+    BIGNUM *g = NULL, *q = NULL, *p = NULL;
+    BN_MONT_CTX *mont = NULL;
+    int i, k, n = 0, m = 0, qsize = N >> 3;
+    int counter = 0;
+    int r = 0;
+    BN_CTX *ctx = NULL;
+    EVP_MD_CTX *mctx = EVP_MD_CTX_new();
+    unsigned int h = 2;
+
+    if (mctx == NULL)
+        goto err;
+
+    if (evpmd == NULL) {
+        if (N == 160)
+            evpmd = EVP_sha1();
+        else if (N == 224)
+            evpmd = EVP_sha224();
+        else
+            evpmd = EVP_sha256();
+    }
+
+    mdsize = EVP_MD_size(evpmd);
+    /* If unverifiable g generation only don't need seed */
+    if (!ret->p || !ret->q || idx >= 0) {
+        if (seed_len == 0)
+            seed_len = mdsize;
+
+        seed = OPENSSL_malloc(seed_len);
+
+        if (seed_out)
+            seed_tmp = seed_out;
+        else
+            seed_tmp = OPENSSL_malloc(seed_len);
+
+        if (seed == NULL || seed_tmp == NULL)
+            goto err;
+
+        if (seed_in)
+            memcpy(seed, seed_in, seed_len);
+
+    }
+
+    if ((ctx = BN_CTX_new()) == NULL)
+        goto err;
+
+    if ((mont = BN_MONT_CTX_new()) == NULL)
+        goto err;
+
+    BN_CTX_start(ctx);
+    r0 = BN_CTX_get(ctx);
+    g = BN_CTX_get(ctx);
+    W = BN_CTX_get(ctx);
+    X = BN_CTX_get(ctx);
+    c = BN_CTX_get(ctx);
+    test = BN_CTX_get(ctx);
+    if (test == NULL)
+        goto err;
+
+    /* if p, q already supplied generate g only */
+    if (ret->p && ret->q) {
+        p = ret->p;
+        q = ret->q;
+        if (idx >= 0)
+            memcpy(seed_tmp, seed, seed_len);
+        goto g_only;
+    } else {
+        p = BN_CTX_get(ctx);
+        q = BN_CTX_get(ctx);
+    }
+
+    if (!BN_lshift(test, BN_value_one(), L - 1))
+        goto err;
+    for (;;) {
+        for (;;) {              /* find q */
+            unsigned char *pmd;
+            /* step 1 */
+            if (!BN_GENCB_call(cb, 0, m++))
+                goto err;
+
+            if (!seed_in) {
+                if (RAND_bytes(seed, seed_len) <= 0)
+                    goto err;
+            }
+            /* step 2 */
+            if (!EVP_Digest(seed, seed_len, md, NULL, evpmd, NULL))
+                goto err;
+            /* Take least significant bits of md */
+            if (mdsize > qsize)
+                pmd = md + mdsize - qsize;
+            else
+                pmd = md;
+
+            if (mdsize < qsize)
+                memset(md + mdsize, 0, qsize - mdsize);
+
+            /* step 3 */
+            pmd[0] |= 0x80;
+            pmd[qsize - 1] |= 0x01;
+            if (!BN_bin2bn(pmd, qsize, q))
+                goto err;
+
+            /* step 4 */
+            r = BN_is_prime_fasttest_ex(q, DSS_prime_checks, ctx,
+                                        seed_in ? 1 : 0, cb);
+            if (r > 0)
+                break;
+            if (r != 0)
+                goto err;
+            /* Provided seed didn't produce a prime: error */
+            if (seed_in) {
+                ok = 0;
+                DSAerr(DSA_F_DSA_BUILTIN_PARAMGEN2, DSA_R_Q_NOT_PRIME);
+                goto err;
+            }
+
+            /* do a callback call */
+            /* step 5 */
+        }
+        /* Copy seed to seed_out before we mess with it */
+        if (seed_out)
+            memcpy(seed_out, seed, seed_len);
+
+        if (!BN_GENCB_call(cb, 2, 0))
+            goto err;
+        if (!BN_GENCB_call(cb, 3, 0))
+            goto err;
+
+        /* step 6 */
+        counter = 0;
+        /* "offset = 1" */
+
+        n = (L - 1) / (mdsize << 3);
+
+        for (;;) {
+            if ((counter != 0) && !BN_GENCB_call(cb, 0, counter))
+                goto err;
+
+            /* step 7 */
+            BN_zero(W);
+            /* now 'buf' contains "SEED + offset - 1" */
+            for (k = 0; k <= n; k++) {
+                /*
+                 * obtain "SEED + offset + k" by incrementing:
+                 */
+                for (i = seed_len - 1; i >= 0; i--) {
+                    seed[i]++;
+                    if (seed[i] != 0)
+                        break;
+                }
+
+                if (!EVP_Digest(seed, seed_len, md, NULL, evpmd, NULL))
+                    goto err;
+
+                /* step 8 */
+                if (!BN_bin2bn(md, mdsize, r0))
+                    goto err;
+                if (!BN_lshift(r0, r0, (mdsize << 3) * k))
+                    goto err;
+                if (!BN_add(W, W, r0))
+                    goto err;
+            }
+
+            /* more of step 8 */
+            if (!BN_mask_bits(W, L - 1))
+                goto err;
+            if (!BN_copy(X, W))
+                goto err;
+            if (!BN_add(X, X, test))
+                goto err;
+
+            /* step 9 */
+            if (!BN_lshift1(r0, q))
+                goto err;
+            if (!BN_mod(c, X, r0, ctx))
+                goto err;
+            if (!BN_sub(r0, c, BN_value_one()))
+                goto err;
+            if (!BN_sub(p, X, r0))
+                goto err;
+
+            /* step 10 */
+            if (BN_cmp(p, test) >= 0) {
+                /* step 11 */
+                r = BN_is_prime_fasttest_ex(p, DSS_prime_checks, ctx, 1, cb);
+                if (r > 0)
+                    goto end;   /* found it */
+                if (r != 0)
+                    goto err;
+            }
+
+            /* step 13 */
+            counter++;
+            /* "offset = offset + n + 1" */
+
+            /* step 14 */
+            if (counter >= (int)(4 * L))
+                break;
+        }
+        if (seed_in) {
+            ok = 0;
+            DSAerr(DSA_F_DSA_BUILTIN_PARAMGEN2, DSA_R_INVALID_PARAMETERS);
+            goto err;
+        }
+    }
+ end:
+    if (!BN_GENCB_call(cb, 2, 1))
+        goto err;
+
+ g_only:
+
+    /* We now need to generate g */
+    /* Set r0=(p-1)/q */
+    if (!BN_sub(test, p, BN_value_one()))
+        goto err;
+    if (!BN_div(r0, NULL, test, q, ctx))
+        goto err;
+
+    if (idx < 0) {
+        if (!BN_set_word(test, h))
+            goto err;
+    } else
+        h = 1;
+    if (!BN_MONT_CTX_set(mont, p, ctx))
+        goto err;
+
+    for (;;) {
+        static const unsigned char ggen[4] = { 0x67, 0x67, 0x65, 0x6e };
+        if (idx >= 0) {
+            md[0] = idx & 0xff;
+            md[1] = (h >> 8) & 0xff;
+            md[2] = h & 0xff;
+            if (!EVP_DigestInit_ex(mctx, evpmd, NULL))
+                goto err;
+            if (!EVP_DigestUpdate(mctx, seed_tmp, seed_len))
+                goto err;
+            if (!EVP_DigestUpdate(mctx, ggen, sizeof(ggen)))
+                goto err;
+            if (!EVP_DigestUpdate(mctx, md, 3))
+                goto err;
+            if (!EVP_DigestFinal_ex(mctx, md, NULL))
+                goto err;
+            if (!BN_bin2bn(md, mdsize, test))
+                goto err;
+        }
+        /* g=test^r0%p */
+        if (!BN_mod_exp_mont(g, test, r0, p, ctx, mont))
+            goto err;
+        if (!BN_is_one(g))
+            break;
+        if (idx < 0 && !BN_add(test, test, BN_value_one()))
+            goto err;
+        h++;
+        if (idx >= 0 && h > 0xffff)
+            goto err;
+    }
+
+    if (!BN_GENCB_call(cb, 3, 1))
+        goto err;
+
+    ok = 1;
+ err:
+    if (ok == 1) {
+        if (p != ret->p) {
+            BN_free(ret->p);
+            ret->p = BN_dup(p);
+        }
+        if (q != ret->q) {
+            BN_free(ret->q);
+            ret->q = BN_dup(q);
+        }
+        BN_free(ret->g);
+        ret->g = BN_dup(g);
+        if (ret->p == NULL || ret->q == NULL || ret->g == NULL) {
+            ok = -1;
+            goto err;
+        }
+        if (counter_ret != NULL)
+            *counter_ret = counter;
+        if (h_ret != NULL)
+            *h_ret = h;
+    }
+    OPENSSL_free(seed);
+    if (seed_out != seed_tmp)
+        OPENSSL_free(seed_tmp);
+    if (ctx)
+        BN_CTX_end(ctx);
+    BN_CTX_free(ctx);
+    BN_MONT_CTX_free(mont);
+    EVP_MD_CTX_free(mctx);
+    return ok;
+}
diff --git a/crypto/dsa/dsa_key.c b/crypto/dsa/dsa_key.c
new file mode 100644
index 000000000..31442b1cf
--- /dev/null
+++ b/crypto/dsa/dsa_key.c
@@ -0,0 +1,77 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include "dsa_locl.h"
+
+static int dsa_builtin_keygen(DSA *dsa);
+
+int DSA_generate_key(DSA *dsa)
+{
+    if (dsa->meth->dsa_keygen)
+        return dsa->meth->dsa_keygen(dsa);
+    return dsa_builtin_keygen(dsa);
+}
+
+static int dsa_builtin_keygen(DSA *dsa)
+{
+    int ok = 0;
+    BN_CTX *ctx = NULL;
+    BIGNUM *pub_key = NULL, *priv_key = NULL;
+
+    if ((ctx = BN_CTX_new()) == NULL)
+        goto err;
+
+    if (dsa->priv_key == NULL) {
+        if ((priv_key = BN_secure_new()) == NULL)
+            goto err;
+    } else
+        priv_key = dsa->priv_key;
+
+    do
+        if (!BN_rand_range(priv_key, dsa->q))
+            goto err;
+    while (BN_is_zero(priv_key)) ;
+
+    if (dsa->pub_key == NULL) {
+        if ((pub_key = BN_new()) == NULL)
+            goto err;
+    } else
+        pub_key = dsa->pub_key;
+
+    {
+        BIGNUM *prk = BN_new();
+
+        if (prk == NULL)
+            goto err;
+        BN_with_flags(prk, priv_key, BN_FLG_CONSTTIME);
+
+        if (!BN_mod_exp(pub_key, dsa->g, prk, dsa->p, ctx)) {
+            BN_free(prk);
+            goto err;
+        }
+        /* We MUST free prk before any further use of priv_key */
+        BN_free(prk);
+    }
+
+    dsa->priv_key = priv_key;
+    dsa->pub_key = pub_key;
+    ok = 1;
+
+ err:
+    if (pub_key != dsa->pub_key)
+        BN_free(pub_key);
+    if (priv_key != dsa->priv_key)
+        BN_free(priv_key);
+    BN_CTX_free(ctx);
+    return (ok);
+}
diff --git a/crypto/dsa/dsa_lib.c b/crypto/dsa/dsa_lib.c
new file mode 100644
index 000000000..42324c70f
--- /dev/null
+++ b/crypto/dsa/dsa_lib.c
@@ -0,0 +1,346 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/* Original version from Steven Schoch  */
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include "dsa_locl.h"
+#include 
+#include 
+#include 
+
+static const DSA_METHOD *default_DSA_method = NULL;
+
+void DSA_set_default_method(const DSA_METHOD *meth)
+{
+    default_DSA_method = meth;
+}
+
+const DSA_METHOD *DSA_get_default_method(void)
+{
+    if (!default_DSA_method)
+        default_DSA_method = DSA_OpenSSL();
+    return default_DSA_method;
+}
+
+DSA *DSA_new(void)
+{
+    return DSA_new_method(NULL);
+}
+
+int DSA_set_method(DSA *dsa, const DSA_METHOD *meth)
+{
+    /*
+     * NB: The caller is specifically setting a method, so it's not up to us
+     * to deal with which ENGINE it comes from.
+     */
+    const DSA_METHOD *mtmp;
+    mtmp = dsa->meth;
+    if (mtmp->finish)
+        mtmp->finish(dsa);
+#ifndef OPENSSL_NO_ENGINE
+    ENGINE_finish(dsa->engine);
+    dsa->engine = NULL;
+#endif
+    dsa->meth = meth;
+    if (meth->init)
+        meth->init(dsa);
+    return 1;
+}
+
+const DSA_METHOD *DSA_get_method(DSA *d)
+{
+    return d->meth;
+}
+
+DSA *DSA_new_method(ENGINE *engine)
+{
+    DSA *ret = OPENSSL_zalloc(sizeof(*ret));
+
+    if (ret == NULL) {
+        DSAerr(DSA_F_DSA_NEW_METHOD, ERR_R_MALLOC_FAILURE);
+        return NULL;
+    }
+
+    ret->references = 1;
+    ret->lock = CRYPTO_THREAD_lock_new();
+    if (ret->lock == NULL) {
+        DSAerr(DSA_F_DSA_NEW_METHOD, ERR_R_MALLOC_FAILURE);
+        OPENSSL_free(ret);
+        return NULL;
+    }
+
+    ret->meth = DSA_get_default_method();
+#ifndef OPENSSL_NO_ENGINE
+    ret->flags = ret->meth->flags & ~DSA_FLAG_NON_FIPS_ALLOW; /* early default init */
+    if (engine) {
+        if (!ENGINE_init(engine)) {
+            DSAerr(DSA_F_DSA_NEW_METHOD, ERR_R_ENGINE_LIB);
+            goto err;
+        }
+        ret->engine = engine;
+    } else
+        ret->engine = ENGINE_get_default_DSA();
+    if (ret->engine) {
+        ret->meth = ENGINE_get_DSA(ret->engine);
+        if (ret->meth == NULL) {
+            DSAerr(DSA_F_DSA_NEW_METHOD, ERR_R_ENGINE_LIB);
+            goto err;
+        }
+    }
+#endif
+
+    ret->flags = ret->meth->flags & ~DSA_FLAG_NON_FIPS_ALLOW;
+
+    if (!CRYPTO_new_ex_data(CRYPTO_EX_INDEX_DSA, ret, &ret->ex_data))
+        goto err;
+
+    if ((ret->meth->init != NULL) && !ret->meth->init(ret)) {
+        DSAerr(DSA_F_DSA_NEW_METHOD, ERR_R_INIT_FAIL);
+err:
+        DSA_free(ret);
+        ret = NULL;
+    }
+
+    return ret;
+}
+
+void DSA_free(DSA *r)
+{
+    int i;
+
+    if (r == NULL)
+        return;
+
+    CRYPTO_atomic_add(&r->references, -1, &i, r->lock);
+    REF_PRINT_COUNT("DSA", r);
+    if (i > 0)
+        return;
+    REF_ASSERT_ISNT(i < 0);
+
+    if (r->meth->finish)
+        r->meth->finish(r);
+#ifndef OPENSSL_NO_ENGINE
+    ENGINE_finish(r->engine);
+#endif
+
+    CRYPTO_free_ex_data(CRYPTO_EX_INDEX_DSA, r, &r->ex_data);
+
+    CRYPTO_THREAD_lock_free(r->lock);
+
+    BN_clear_free(r->p);
+    BN_clear_free(r->q);
+    BN_clear_free(r->g);
+    BN_clear_free(r->pub_key);
+    BN_clear_free(r->priv_key);
+    OPENSSL_free(r);
+}
+
+int DSA_up_ref(DSA *r)
+{
+    int i;
+
+    if (CRYPTO_atomic_add(&r->references, 1, &i, r->lock) <= 0)
+        return 0;
+
+    REF_PRINT_COUNT("DSA", r);
+    REF_ASSERT_ISNT(i < 2);
+    return ((i > 1) ? 1 : 0);
+}
+
+int DSA_size(const DSA *r)
+{
+    int ret, i;
+    ASN1_INTEGER bs;
+    unsigned char buf[4];       /* 4 bytes looks really small. However,
+                                 * i2d_ASN1_INTEGER() will not look beyond
+                                 * the first byte, as long as the second
+                                 * parameter is NULL. */
+
+    i = BN_num_bits(r->q);
+    bs.length = (i + 7) / 8;
+    bs.data = buf;
+    bs.type = V_ASN1_INTEGER;
+    /* If the top bit is set the asn1 encoding is 1 larger. */
+    buf[0] = 0xff;
+
+    i = i2d_ASN1_INTEGER(&bs, NULL);
+    i += i;                     /* r and s */
+    ret = ASN1_object_size(1, i, V_ASN1_SEQUENCE);
+    return (ret);
+}
+
+int DSA_set_ex_data(DSA *d, int idx, void *arg)
+{
+    return (CRYPTO_set_ex_data(&d->ex_data, idx, arg));
+}
+
+void *DSA_get_ex_data(DSA *d, int idx)
+{
+    return (CRYPTO_get_ex_data(&d->ex_data, idx));
+}
+
+int DSA_security_bits(const DSA *d)
+{
+    if (d->p && d->q)
+        return BN_security_bits(BN_num_bits(d->p), BN_num_bits(d->q));
+    return -1;
+}
+
+#ifndef OPENSSL_NO_DH
+DH *DSA_dup_DH(const DSA *r)
+{
+    /*
+     * DSA has p, q, g, optional pub_key, optional priv_key. DH has p,
+     * optional length, g, optional pub_key, optional priv_key, optional q.
+     */
+
+    DH *ret = NULL;
+    BIGNUM *p = NULL, *q = NULL, *g = NULL, *pub_key = NULL, *priv_key = NULL;
+
+    if (r == NULL)
+        goto err;
+    ret = DH_new();
+    if (ret == NULL)
+        goto err;
+    if (r->p != NULL || r->g != NULL || r->q != NULL) {
+        if (r->p == NULL || r->g == NULL || r->q == NULL) {
+            /* Shouldn't happen */
+            goto err;
+        }
+        p = BN_dup(r->p);
+        g = BN_dup(r->g);
+        q = BN_dup(r->q);
+        if (p == NULL || g == NULL || q == NULL || !DH_set0_pqg(ret, p, q, g))
+            goto err;
+        p = g = q = NULL;
+    }
+
+    if (r->pub_key != NULL) {
+        pub_key = BN_dup(r->pub_key);
+        if (pub_key == NULL)
+            goto err;
+        if (r->priv_key != NULL) {
+            priv_key = BN_dup(r->priv_key);
+            if (priv_key == NULL)
+                goto err;
+        }
+        if (!DH_set0_key(ret, pub_key, priv_key))
+            goto err;
+    } else if (r->priv_key != NULL) {
+        /* Shouldn't happen */
+        goto err;
+    }
+
+    return ret;
+
+ err:
+    BN_free(p);
+    BN_free(g);
+    BN_free(q);
+    BN_free(pub_key);
+    BN_free(priv_key);
+    DH_free(ret);
+    return NULL;
+}
+#endif
+
+void DSA_get0_pqg(const DSA *d,
+                  const BIGNUM **p, const BIGNUM **q, const BIGNUM **g)
+{
+    if (p != NULL)
+        *p = d->p;
+    if (q != NULL)
+        *q = d->q;
+    if (g != NULL)
+        *g = d->g;
+}
+
+int DSA_set0_pqg(DSA *d, BIGNUM *p, BIGNUM *q, BIGNUM *g)
+{
+    /* If the fields p, q and g in d are NULL, the corresponding input
+     * parameters MUST be non-NULL.
+     */
+    if ((d->p == NULL && p == NULL)
+        || (d->q == NULL && q == NULL)
+        || (d->g == NULL && g == NULL))
+        return 0;
+
+    if (p != NULL) {
+        BN_free(d->p);
+        d->p = p;
+    }
+    if (q != NULL) {
+        BN_free(d->q);
+        d->q = q;
+    }
+    if (g != NULL) {
+        BN_free(d->g);
+        d->g = g;
+    }
+
+    return 1;
+}
+
+void DSA_get0_key(const DSA *d,
+                  const BIGNUM **pub_key, const BIGNUM **priv_key)
+{
+    if (pub_key != NULL)
+        *pub_key = d->pub_key;
+    if (priv_key != NULL)
+        *priv_key = d->priv_key;
+}
+
+int DSA_set0_key(DSA *d, BIGNUM *pub_key, BIGNUM *priv_key)
+{
+    /* If the field pub_key in d is NULL, the corresponding input
+     * parameters MUST be non-NULL.  The priv_key field may
+     * be left NULL.
+     */
+    if (d->pub_key == NULL && pub_key == NULL)
+        return 0;
+
+    if (pub_key != NULL) {
+        BN_free(d->pub_key);
+        d->pub_key = pub_key;
+    }
+    if (priv_key != NULL) {
+        BN_free(d->priv_key);
+        d->priv_key = priv_key;
+    }
+
+    return 1;
+}
+
+void DSA_clear_flags(DSA *d, int flags)
+{
+    d->flags &= ~flags;
+}
+
+int DSA_test_flags(const DSA *d, int flags)
+{
+    return d->flags & flags;
+}
+
+void DSA_set_flags(DSA *d, int flags)
+{
+    d->flags |= flags;
+}
+
+ENGINE *DSA_get0_engine(DSA *d)
+{
+    return d->engine;
+}
+
+int DSA_bits(const DSA *dsa)
+{
+    return BN_num_bits(dsa->p);
+}
diff --git a/crypto/dsa/dsa_locl.h b/crypto/dsa/dsa_locl.h
new file mode 100644
index 000000000..9021fce0b
--- /dev/null
+++ b/crypto/dsa/dsa_locl.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2007-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+
+struct dsa_st {
+    /*
+     * This first variable is used to pick up errors where a DSA is passed
+     * instead of of a EVP_PKEY
+     */
+    int pad;
+    long version;
+    BIGNUM *p;
+    BIGNUM *q;                  /* == 20 */
+    BIGNUM *g;
+    BIGNUM *pub_key;            /* y public key */
+    BIGNUM *priv_key;           /* x private key */
+    int flags;
+    /* Normally used to cache montgomery values */
+    BN_MONT_CTX *method_mont_p;
+    int references;
+    CRYPTO_EX_DATA ex_data;
+    const DSA_METHOD *meth;
+    /* functional reference if 'meth' is ENGINE-provided */
+    ENGINE *engine;
+    CRYPTO_RWLOCK *lock;
+};
+
+struct DSA_SIG_st {
+    BIGNUM *r;
+    BIGNUM *s;
+};
+
+struct dsa_method {
+    char *name;
+    DSA_SIG *(*dsa_do_sign) (const unsigned char *dgst, int dlen, DSA *dsa);
+    int (*dsa_sign_setup) (DSA *dsa, BN_CTX *ctx_in, BIGNUM **kinvp,
+                           BIGNUM **rp);
+    int (*dsa_do_verify) (const unsigned char *dgst, int dgst_len,
+                          DSA_SIG *sig, DSA *dsa);
+    int (*dsa_mod_exp) (DSA *dsa, BIGNUM *rr, const BIGNUM *a1,
+                        const BIGNUM *p1, const BIGNUM *a2, const BIGNUM *p2,
+                        const BIGNUM *m, BN_CTX *ctx, BN_MONT_CTX *in_mont);
+    /* Can be null */
+    int (*bn_mod_exp) (DSA *dsa, BIGNUM *r, const BIGNUM *a, const BIGNUM *p,
+                       const BIGNUM *m, BN_CTX *ctx, BN_MONT_CTX *m_ctx);
+    int (*init) (DSA *dsa);
+    int (*finish) (DSA *dsa);
+    int flags;
+    void *app_data;
+    /* If this is non-NULL, it is used to generate DSA parameters */
+    int (*dsa_paramgen) (DSA *dsa, int bits,
+                         const unsigned char *seed, int seed_len,
+                         int *counter_ret, unsigned long *h_ret,
+                         BN_GENCB *cb);
+    /* If this is non-NULL, it is used to generate DSA keys */
+    int (*dsa_keygen) (DSA *dsa);
+};
+
+int dsa_builtin_paramgen(DSA *ret, size_t bits, size_t qbits,
+                         const EVP_MD *evpmd, const unsigned char *seed_in,
+                         size_t seed_len, unsigned char *seed_out,
+                         int *counter_ret, unsigned long *h_ret,
+                         BN_GENCB *cb);
+
+int dsa_builtin_paramgen2(DSA *ret, size_t L, size_t N,
+                          const EVP_MD *evpmd, const unsigned char *seed_in,
+                          size_t seed_len, int idx, unsigned char *seed_out,
+                          int *counter_ret, unsigned long *h_ret,
+                          BN_GENCB *cb);
diff --git a/crypto/dsa/dsa_meth.c b/crypto/dsa/dsa_meth.c
new file mode 100644
index 000000000..f0188f200
--- /dev/null
+++ b/crypto/dsa/dsa_meth.c
@@ -0,0 +1,224 @@
+/*
+ * Copyright 2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * Licensed under the OpenSSL licenses, (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * https://www.openssl.org/source/license.html
+ * or in the file LICENSE in the source distribution.
+ */
+
+#include "dsa_locl.h"
+#include 
+#include 
+
+DSA_METHOD *DSA_meth_new(const char *name, int flags)
+{
+    DSA_METHOD *dsam = OPENSSL_zalloc(sizeof(*dsam));
+
+    if (dsam != NULL) {
+        dsam->flags = flags;
+
+        dsam->name = OPENSSL_strdup(name);
+        if (dsam->name != NULL)
+            return dsam;
+
+        OPENSSL_free(dsam);
+    }
+
+    DSAerr(DSA_F_DSA_METH_NEW, ERR_R_MALLOC_FAILURE);
+    return NULL;
+}
+
+void DSA_meth_free(DSA_METHOD *dsam)
+{
+    if (dsam != NULL) {
+        OPENSSL_free(dsam->name);
+        OPENSSL_free(dsam);
+    }
+}
+
+DSA_METHOD *DSA_meth_dup(const DSA_METHOD *dsam)
+{
+    DSA_METHOD *ret = OPENSSL_malloc(sizeof(*ret));
+
+    if (ret != NULL) {
+        memcpy(ret, dsam, sizeof(*dsam));
+
+        ret->name = OPENSSL_strdup(dsam->name);
+        if (ret->name != NULL)
+            return ret;
+
+        OPENSSL_free(ret);
+    }
+
+    DSAerr(DSA_F_DSA_METH_DUP, ERR_R_MALLOC_FAILURE);
+    return NULL;
+}
+
+const char *DSA_meth_get0_name(const DSA_METHOD *dsam)
+{
+    return dsam->name;
+}
+
+int DSA_meth_set1_name(DSA_METHOD *dsam, const char *name)
+{
+    char *tmpname = OPENSSL_strdup(name);
+
+    if (tmpname == NULL) {
+        DSAerr(DSA_F_DSA_METH_SET1_NAME, ERR_R_MALLOC_FAILURE);
+        return 0;
+    }
+
+    OPENSSL_free(dsam->name);
+    dsam->name = tmpname;
+
+    return 1;
+}
+
+int DSA_meth_get_flags(DSA_METHOD *dsam)
+{
+    return dsam->flags;
+}
+
+int DSA_meth_set_flags(DSA_METHOD *dsam, int flags)
+{
+    dsam->flags = flags;
+    return 1;
+}
+
+void *DSA_meth_get0_app_data(const DSA_METHOD *dsam)
+{
+    return dsam->app_data;
+}
+
+int DSA_meth_set0_app_data(DSA_METHOD *dsam, void *app_data)
+{
+    dsam->app_data = app_data;
+    return 1;
+}
+
+DSA_SIG *(*DSA_meth_get_sign(const DSA_METHOD *dsam))
+        (const unsigned char *, int, DSA *)
+{
+    return dsam->dsa_do_sign;
+}
+
+int DSA_meth_set_sign(DSA_METHOD *dsam,
+                       DSA_SIG *(*sign) (const unsigned char *, int, DSA *))
+{
+    dsam->dsa_do_sign = sign;
+    return 1;
+}
+
+int (*DSA_meth_get_sign_setup(const DSA_METHOD *dsam))
+        (DSA *, BN_CTX *, BIGNUM **, BIGNUM **)
+{
+    return dsam->dsa_sign_setup;
+}
+
+int DSA_meth_set_sign_setup(DSA_METHOD *dsam,
+        int (*sign_setup) (DSA *, BN_CTX *, BIGNUM **, BIGNUM **))
+{
+    dsam->dsa_sign_setup = sign_setup;
+    return 1;
+}
+
+int (*DSA_meth_get_verify(const DSA_METHOD *dsam))
+        (const unsigned char *, int , DSA_SIG *, DSA *)
+{
+    return dsam->dsa_do_verify;
+}
+
+int DSA_meth_set_verify(DSA_METHOD *dsam,
+    int (*verify) (const unsigned char *, int, DSA_SIG *, DSA *))
+{
+    dsam->dsa_do_verify = verify;
+    return 1;
+}
+
+int (*DSA_meth_get_mod_exp(const DSA_METHOD *dsam))
+        (DSA *, BIGNUM *, const BIGNUM *, const BIGNUM *, const BIGNUM *,
+         const BIGNUM *, const BIGNUM *, BN_CTX *, BN_MONT_CTX *)
+{
+    return dsam->dsa_mod_exp;
+}
+
+int DSA_meth_set_mod_exp(DSA_METHOD *dsam,
+    int (*mod_exp) (DSA *, BIGNUM *, const BIGNUM *, const BIGNUM *,
+                    const BIGNUM *, const BIGNUM *, const BIGNUM *, BN_CTX *,
+                    BN_MONT_CTX *))
+{
+    dsam->dsa_mod_exp = mod_exp;
+    return 1;
+}
+
+int (*DSA_meth_get_bn_mod_exp(const DSA_METHOD *dsam))
+    (DSA *, BIGNUM *, const BIGNUM *, const BIGNUM *, const BIGNUM *, BN_CTX *,
+     BN_MONT_CTX *)
+{
+    return dsam->bn_mod_exp;
+}
+
+int DSA_meth_set_bn_mod_exp(DSA_METHOD *dsam,
+    int (*bn_mod_exp) (DSA *, BIGNUM *, const BIGNUM *, const BIGNUM *,
+                       const BIGNUM *, BN_CTX *, BN_MONT_CTX *))
+{
+    dsam->bn_mod_exp = bn_mod_exp;
+    return 1;
+}
+
+int (*DSA_meth_get_init(const DSA_METHOD *dsam))(DSA *)
+{
+    return dsam->init;
+}
+
+int DSA_meth_set_init(DSA_METHOD *dsam, int (*init)(DSA *))
+{
+    dsam->init = init;
+    return 1;
+}
+
+int (*DSA_meth_get_finish(const DSA_METHOD *dsam)) (DSA *)
+{
+    return dsam->finish;
+}
+
+int DSA_meth_set_finish(DSA_METHOD *dsam, int (*finish) (DSA *))
+{
+    dsam->finish = finish;
+    return 1;
+}
+
+int (*DSA_meth_get_paramgen(const DSA_METHOD *dsam))
+        (DSA *, int, const unsigned char *, int, int *, unsigned long *,
+         BN_GENCB *)
+{
+    return dsam->dsa_paramgen;
+}
+
+int DSA_meth_set_paramgen(DSA_METHOD *dsam,
+        int (*paramgen) (DSA *, int, const unsigned char *, int, int *,
+                         unsigned long *, BN_GENCB *))
+{
+    dsam->dsa_paramgen = paramgen;
+    return 1;
+}
+
+int (*DSA_meth_get_keygen(const DSA_METHOD *dsam)) (DSA *)
+{
+    return dsam->dsa_keygen;
+}
+
+int DSA_meth_set_keygen(DSA_METHOD *dsam, int (*keygen) (DSA *))
+{
+    dsam->dsa_keygen = keygen;
+    return 1;
+}
diff --git a/crypto/dsa/dsa_ossl.c b/crypto/dsa/dsa_ossl.c
new file mode 100644
index 000000000..f9f6a136f
--- /dev/null
+++ b/crypto/dsa/dsa_ossl.c
@@ -0,0 +1,338 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/* Original version from Steven Schoch  */
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+#include "dsa_locl.h"
+#include 
+
+static DSA_SIG *dsa_do_sign(const unsigned char *dgst, int dlen, DSA *dsa);
+static int dsa_sign_setup_no_digest(DSA *dsa, BN_CTX *ctx_in, BIGNUM **kinvp,
+                                    BIGNUM **rp);
+static int dsa_sign_setup(DSA *dsa, BN_CTX *ctx_in, BIGNUM **kinvp,
+                          BIGNUM **rp, const unsigned char *dgst, int dlen);
+static int dsa_do_verify(const unsigned char *dgst, int dgst_len,
+                         DSA_SIG *sig, DSA *dsa);
+static int dsa_init(DSA *dsa);
+static int dsa_finish(DSA *dsa);
+
+static DSA_METHOD openssl_dsa_meth = {
+    "OpenSSL DSA method",
+    dsa_do_sign,
+    dsa_sign_setup_no_digest,
+    dsa_do_verify,
+    NULL,                       /* dsa_mod_exp, */
+    NULL,                       /* dsa_bn_mod_exp, */
+    dsa_init,
+    dsa_finish,
+    DSA_FLAG_FIPS_METHOD,
+    NULL,
+    NULL,
+    NULL
+};
+
+const DSA_METHOD *DSA_OpenSSL(void)
+{
+    return &openssl_dsa_meth;
+}
+
+static DSA_SIG *dsa_do_sign(const unsigned char *dgst, int dlen, DSA *dsa)
+{
+    BIGNUM *kinv = NULL;
+    BIGNUM *m;
+    BIGNUM *xr;
+    BN_CTX *ctx = NULL;
+    int reason = ERR_R_BN_LIB;
+    DSA_SIG *ret = NULL;
+    int rv = 0;
+
+    m = BN_new();
+    xr = BN_new();
+    if (m == NULL || xr == NULL)
+        goto err;
+
+    if (!dsa->p || !dsa->q || !dsa->g) {
+        reason = DSA_R_MISSING_PARAMETERS;
+        goto err;
+    }
+
+    ret = DSA_SIG_new();
+    if (ret == NULL)
+        goto err;
+    ret->r = BN_new();
+    ret->s = BN_new();
+    if (ret->r == NULL || ret->s == NULL)
+        goto err;
+
+    ctx = BN_CTX_new();
+    if (ctx == NULL)
+        goto err;
+ redo:
+    if (!dsa_sign_setup(dsa, ctx, &kinv, &ret->r, dgst, dlen))
+        goto err;
+
+    if (dlen > BN_num_bytes(dsa->q))
+        /*
+         * if the digest length is greater than the size of q use the
+         * BN_num_bits(dsa->q) leftmost bits of the digest, see fips 186-3,
+         * 4.2
+         */
+        dlen = BN_num_bytes(dsa->q);
+    if (BN_bin2bn(dgst, dlen, m) == NULL)
+        goto err;
+
+    /* Compute  s = inv(k) (m + xr) mod q */
+    if (!BN_mod_mul(xr, dsa->priv_key, ret->r, dsa->q, ctx))
+        goto err;               /* s = xr */
+    if (!BN_add(ret->s, xr, m))
+        goto err;               /* s = m + xr */
+    if (BN_cmp(ret->s, dsa->q) > 0)
+        if (!BN_sub(ret->s, ret->s, dsa->q))
+            goto err;
+    if (!BN_mod_mul(ret->s, ret->s, kinv, dsa->q, ctx))
+        goto err;
+
+    /*
+     * Redo if r or s is zero as required by FIPS 186-3: this is very
+     * unlikely.
+     */
+    if (BN_is_zero(ret->r) || BN_is_zero(ret->s))
+        goto redo;
+
+    rv = 1;
+
+ err:
+    if (rv == 0) {
+        DSAerr(DSA_F_DSA_DO_SIGN, reason);
+        DSA_SIG_free(ret);
+        ret = NULL;
+    }
+    BN_CTX_free(ctx);
+    BN_clear_free(m);
+    BN_clear_free(xr);
+    BN_clear_free(kinv);
+    return ret;
+}
+
+static int dsa_sign_setup_no_digest(DSA *dsa, BN_CTX *ctx_in,
+                                    BIGNUM **kinvp, BIGNUM **rp)
+{
+    return dsa_sign_setup(dsa, ctx_in, kinvp, rp, NULL, 0);
+}
+
+static int dsa_sign_setup(DSA *dsa, BN_CTX *ctx_in,
+                          BIGNUM **kinvp, BIGNUM **rp,
+                          const unsigned char *dgst, int dlen)
+{
+    BN_CTX *ctx = NULL;
+    BIGNUM *k, *kinv = NULL, *r = *rp;
+    int ret = 0;
+
+    if (!dsa->p || !dsa->q || !dsa->g) {
+        DSAerr(DSA_F_DSA_SIGN_SETUP, DSA_R_MISSING_PARAMETERS);
+        return 0;
+    }
+
+    k = BN_new();
+    if (k == NULL)
+        goto err;
+
+    if (ctx_in == NULL) {
+        if ((ctx = BN_CTX_new()) == NULL)
+            goto err;
+    } else
+        ctx = ctx_in;
+
+    /* Get random k */
+    do {
+        if (dgst != NULL) {
+            /*
+             * We calculate k from SHA512(private_key + H(message) + random).
+             * This protects the private key from a weak PRNG.
+             */
+            if (!BN_generate_dsa_nonce(k, dsa->q, dsa->priv_key, dgst,
+                                       dlen, ctx))
+                goto err;
+        } else if (!BN_rand_range(k, dsa->q))
+            goto err;
+    } while (BN_is_zero(k));
+
+    BN_set_flags(k, BN_FLG_CONSTTIME);
+
+    if (dsa->flags & DSA_FLAG_CACHE_MONT_P) {
+        if (!BN_MONT_CTX_set_locked(&dsa->method_mont_p,
+                                    dsa->lock, dsa->p, ctx))
+            goto err;
+    }
+
+    /* Compute r = (g^k mod p) mod q */
+
+    /*
+     * We do not want timing information to leak the length of k, so we
+     * compute g^k using an equivalent exponent of fixed length. (This
+     * is a kludge that we need because the BN_mod_exp_mont() does not
+     * let us specify the desired timing behaviour.)
+     */
+
+    if (!BN_add(k, k, dsa->q))
+        goto err;
+    if (BN_num_bits(k) <= BN_num_bits(dsa->q)) {
+        if (!BN_add(k, k, dsa->q))
+            goto err;
+    }
+
+    if ((dsa)->meth->bn_mod_exp != NULL) {
+            if (!dsa->meth->bn_mod_exp(dsa, r, dsa->g, k, dsa->p, ctx,
+                                       dsa->method_mont_p))
+                goto err;
+    } else {
+            if (!BN_mod_exp_mont(r, dsa->g, k, dsa->p, ctx, dsa->method_mont_p))
+                goto err;
+    }
+
+    if (!BN_mod(r, r, dsa->q, ctx))
+        goto err;
+
+    /* Compute  part of 's = inv(k) (m + xr) mod q' */
+    if ((kinv = BN_mod_inverse(NULL, k, dsa->q, ctx)) == NULL)
+        goto err;
+
+    BN_clear_free(*kinvp);
+    *kinvp = kinv;
+    kinv = NULL;
+    ret = 1;
+ err:
+    if (!ret)
+        DSAerr(DSA_F_DSA_SIGN_SETUP, ERR_R_BN_LIB);
+    if (ctx != ctx_in)
+        BN_CTX_free(ctx);
+    BN_clear_free(k);
+    return ret;
+}
+
+static int dsa_do_verify(const unsigned char *dgst, int dgst_len,
+                         DSA_SIG *sig, DSA *dsa)
+{
+    BN_CTX *ctx;
+    BIGNUM *u1, *u2, *t1;
+    BN_MONT_CTX *mont = NULL;
+    const BIGNUM *r, *s;
+    int ret = -1, i;
+    if (!dsa->p || !dsa->q || !dsa->g) {
+        DSAerr(DSA_F_DSA_DO_VERIFY, DSA_R_MISSING_PARAMETERS);
+        return -1;
+    }
+
+    i = BN_num_bits(dsa->q);
+    /* fips 186-3 allows only different sizes for q */
+    if (i != 160 && i != 224 && i != 256) {
+        DSAerr(DSA_F_DSA_DO_VERIFY, DSA_R_BAD_Q_VALUE);
+        return -1;
+    }
+
+    if (BN_num_bits(dsa->p) > OPENSSL_DSA_MAX_MODULUS_BITS) {
+        DSAerr(DSA_F_DSA_DO_VERIFY, DSA_R_MODULUS_TOO_LARGE);
+        return -1;
+    }
+    u1 = BN_new();
+    u2 = BN_new();
+    t1 = BN_new();
+    ctx = BN_CTX_new();
+    if (u1 == NULL || u2 == NULL || t1 == NULL || ctx == NULL)
+        goto err;
+
+    DSA_SIG_get0(sig, &r, &s);
+
+    if (BN_is_zero(r) || BN_is_negative(r) ||
+        BN_ucmp(r, dsa->q) >= 0) {
+        ret = 0;
+        goto err;
+    }
+    if (BN_is_zero(s) || BN_is_negative(s) ||
+        BN_ucmp(s, dsa->q) >= 0) {
+        ret = 0;
+        goto err;
+    }
+
+    /*
+     * Calculate W = inv(S) mod Q save W in u2
+     */
+    if ((BN_mod_inverse(u2, s, dsa->q, ctx)) == NULL)
+        goto err;
+
+    /* save M in u1 */
+    if (dgst_len > (i >> 3))
+        /*
+         * if the digest length is greater than the size of q use the
+         * BN_num_bits(dsa->q) leftmost bits of the digest, see fips 186-3,
+         * 4.2
+         */
+        dgst_len = (i >> 3);
+    if (BN_bin2bn(dgst, dgst_len, u1) == NULL)
+        goto err;
+
+    /* u1 = M * w mod q */
+    if (!BN_mod_mul(u1, u1, u2, dsa->q, ctx))
+        goto err;
+
+    /* u2 = r * w mod q */
+    if (!BN_mod_mul(u2, r, u2, dsa->q, ctx))
+        goto err;
+
+    if (dsa->flags & DSA_FLAG_CACHE_MONT_P) {
+        mont = BN_MONT_CTX_set_locked(&dsa->method_mont_p,
+                                      dsa->lock, dsa->p, ctx);
+        if (!mont)
+            goto err;
+    }
+
+    if (dsa->meth->dsa_mod_exp != NULL) {
+        if (!dsa->meth->dsa_mod_exp(dsa, t1, dsa->g, u1, dsa->pub_key, u2,
+                                    dsa->p, ctx, mont))
+            goto err;
+    } else {
+        if (!BN_mod_exp2_mont(t1, dsa->g, u1, dsa->pub_key, u2, dsa->p, ctx,
+                              mont))
+            goto err;
+    }
+
+    /* let u1 = u1 mod q */
+    if (!BN_mod(u1, t1, dsa->q, ctx))
+        goto err;
+
+    /*
+     * V is now in u1.  If the signature is correct, it will be equal to R.
+     */
+    ret = (BN_ucmp(u1, r) == 0);
+
+ err:
+    if (ret < 0)
+        DSAerr(DSA_F_DSA_DO_VERIFY, ERR_R_BN_LIB);
+    BN_CTX_free(ctx);
+    BN_free(u1);
+    BN_free(u2);
+    BN_free(t1);
+    return (ret);
+}
+
+static int dsa_init(DSA *dsa)
+{
+    dsa->flags |= DSA_FLAG_CACHE_MONT_P;
+    return (1);
+}
+
+static int dsa_finish(DSA *dsa)
+{
+    BN_MONT_CTX_free(dsa->method_mont_p);
+    return (1);
+}
diff --git a/crypto/dsa/dsa_pmeth.c b/crypto/dsa/dsa_pmeth.c
new file mode 100644
index 000000000..95f088a5e
--- /dev/null
+++ b/crypto/dsa/dsa_pmeth.c
@@ -0,0 +1,273 @@
+/*
+ * Copyright 2006-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+#include 
+#include 
+#include "internal/evp_int.h"
+#include "dsa_locl.h"
+
+/* DSA pkey context structure */
+
+typedef struct {
+    /* Parameter gen parameters */
+    int nbits;                  /* size of p in bits (default: 1024) */
+    int qbits;                  /* size of q in bits (default: 160) */
+    const EVP_MD *pmd;          /* MD for parameter generation */
+    /* Keygen callback info */
+    int gentmp[2];
+    /* message digest */
+    const EVP_MD *md;           /* MD for the signature */
+} DSA_PKEY_CTX;
+
+static int pkey_dsa_init(EVP_PKEY_CTX *ctx)
+{
+    DSA_PKEY_CTX *dctx;
+    dctx = OPENSSL_malloc(sizeof(*dctx));
+    if (dctx == NULL)
+        return 0;
+    dctx->nbits = 1024;
+    dctx->qbits = 160;
+    dctx->pmd = NULL;
+    dctx->md = NULL;
+
+    ctx->data = dctx;
+    ctx->keygen_info = dctx->gentmp;
+    ctx->keygen_info_count = 2;
+
+    return 1;
+}
+
+static int pkey_dsa_copy(EVP_PKEY_CTX *dst, EVP_PKEY_CTX *src)
+{
+    DSA_PKEY_CTX *dctx, *sctx;
+    if (!pkey_dsa_init(dst))
+        return 0;
+    sctx = src->data;
+    dctx = dst->data;
+    dctx->nbits = sctx->nbits;
+    dctx->qbits = sctx->qbits;
+    dctx->pmd = sctx->pmd;
+    dctx->md = sctx->md;
+    return 1;
+}
+
+static void pkey_dsa_cleanup(EVP_PKEY_CTX *ctx)
+{
+    DSA_PKEY_CTX *dctx = ctx->data;
+    OPENSSL_free(dctx);
+}
+
+static int pkey_dsa_sign(EVP_PKEY_CTX *ctx, unsigned char *sig,
+                         size_t *siglen, const unsigned char *tbs,
+                         size_t tbslen)
+{
+    int ret;
+    unsigned int sltmp;
+    DSA_PKEY_CTX *dctx = ctx->data;
+    DSA *dsa = ctx->pkey->pkey.dsa;
+
+    if (dctx->md) {
+        if (tbslen != (size_t)EVP_MD_size(dctx->md))
+            return 0;
+    } else {
+        if (tbslen != SHA_DIGEST_LENGTH)
+            return 0;
+    }
+
+    ret = DSA_sign(0, tbs, tbslen, sig, &sltmp, dsa);
+
+    if (ret <= 0)
+        return ret;
+    *siglen = sltmp;
+    return 1;
+}
+
+static int pkey_dsa_verify(EVP_PKEY_CTX *ctx,
+                           const unsigned char *sig, size_t siglen,
+                           const unsigned char *tbs, size_t tbslen)
+{
+    int ret;
+    DSA_PKEY_CTX *dctx = ctx->data;
+    DSA *dsa = ctx->pkey->pkey.dsa;
+
+    if (dctx->md) {
+        if (tbslen != (size_t)EVP_MD_size(dctx->md))
+            return 0;
+    } else {
+        if (tbslen != SHA_DIGEST_LENGTH)
+            return 0;
+    }
+
+    ret = DSA_verify(0, tbs, tbslen, sig, siglen, dsa);
+
+    return ret;
+}
+
+static int pkey_dsa_ctrl(EVP_PKEY_CTX *ctx, int type, int p1, void *p2)
+{
+    DSA_PKEY_CTX *dctx = ctx->data;
+    switch (type) {
+    case EVP_PKEY_CTRL_DSA_PARAMGEN_BITS:
+        if (p1 < 256)
+            return -2;
+        dctx->nbits = p1;
+        return 1;
+
+    case EVP_PKEY_CTRL_DSA_PARAMGEN_Q_BITS:
+        if (p1 != 160 && p1 != 224 && p1 && p1 != 256)
+            return -2;
+        dctx->qbits = p1;
+        return 1;
+
+    case EVP_PKEY_CTRL_DSA_PARAMGEN_MD:
+        if (EVP_MD_type((const EVP_MD *)p2) != NID_sha1 &&
+            EVP_MD_type((const EVP_MD *)p2) != NID_sha224 &&
+            EVP_MD_type((const EVP_MD *)p2) != NID_sha256) {
+            DSAerr(DSA_F_PKEY_DSA_CTRL, DSA_R_INVALID_DIGEST_TYPE);
+            return 0;
+        }
+        dctx->pmd = p2;
+        return 1;
+
+    case EVP_PKEY_CTRL_MD:
+        if (EVP_MD_type((const EVP_MD *)p2) != NID_sha1 &&
+            EVP_MD_type((const EVP_MD *)p2) != NID_dsa &&
+            EVP_MD_type((const EVP_MD *)p2) != NID_dsaWithSHA &&
+            EVP_MD_type((const EVP_MD *)p2) != NID_sha224 &&
+            EVP_MD_type((const EVP_MD *)p2) != NID_sha256 &&
+            EVP_MD_type((const EVP_MD *)p2) != NID_sha384 &&
+            EVP_MD_type((const EVP_MD *)p2) != NID_sha512) {
+            DSAerr(DSA_F_PKEY_DSA_CTRL, DSA_R_INVALID_DIGEST_TYPE);
+            return 0;
+        }
+        dctx->md = p2;
+        return 1;
+
+    case EVP_PKEY_CTRL_GET_MD:
+        *(const EVP_MD **)p2 = dctx->md;
+        return 1;
+
+    case EVP_PKEY_CTRL_DIGESTINIT:
+    case EVP_PKEY_CTRL_PKCS7_SIGN:
+    case EVP_PKEY_CTRL_CMS_SIGN:
+        return 1;
+
+    case EVP_PKEY_CTRL_PEER_KEY:
+        DSAerr(DSA_F_PKEY_DSA_CTRL,
+               EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE);
+        return -2;
+    default:
+        return -2;
+
+    }
+}
+
+static int pkey_dsa_ctrl_str(EVP_PKEY_CTX *ctx,
+                             const char *type, const char *value)
+{
+    if (strcmp(type, "dsa_paramgen_bits") == 0) {
+        int nbits;
+        nbits = atoi(value);
+        return EVP_PKEY_CTX_set_dsa_paramgen_bits(ctx, nbits);
+    }
+    if (strcmp(type, "dsa_paramgen_q_bits") == 0) {
+        int qbits = atoi(value);
+        return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_DSA, EVP_PKEY_OP_PARAMGEN,
+                                 EVP_PKEY_CTRL_DSA_PARAMGEN_Q_BITS, qbits,
+                                 NULL);
+    }
+    if (strcmp(type, "dsa_paramgen_md") == 0) {
+        return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_DSA, EVP_PKEY_OP_PARAMGEN,
+                                 EVP_PKEY_CTRL_DSA_PARAMGEN_MD, 0,
+                                 (void *)EVP_get_digestbyname(value));
+    }
+    return -2;
+}
+
+static int pkey_dsa_paramgen(EVP_PKEY_CTX *ctx, EVP_PKEY *pkey)
+{
+    DSA *dsa = NULL;
+    DSA_PKEY_CTX *dctx = ctx->data;
+    BN_GENCB *pcb;
+    int ret;
+    if (ctx->pkey_gencb) {
+        pcb = BN_GENCB_new();
+        if (pcb == NULL)
+            return 0;
+        evp_pkey_set_cb_translate(pcb, ctx);
+    } else
+        pcb = NULL;
+    dsa = DSA_new();
+    if (dsa == NULL) {
+        BN_GENCB_free(pcb);
+        return 0;
+    }
+    ret = dsa_builtin_paramgen(dsa, dctx->nbits, dctx->qbits, dctx->pmd,
+                               NULL, 0, NULL, NULL, NULL, pcb);
+    BN_GENCB_free(pcb);
+    if (ret)
+        EVP_PKEY_assign_DSA(pkey, dsa);
+    else
+        DSA_free(dsa);
+    return ret;
+}
+
+static int pkey_dsa_keygen(EVP_PKEY_CTX *ctx, EVP_PKEY *pkey)
+{
+    DSA *dsa = NULL;
+    if (ctx->pkey == NULL) {
+        DSAerr(DSA_F_PKEY_DSA_KEYGEN, DSA_R_NO_PARAMETERS_SET);
+        return 0;
+    }
+    dsa = DSA_new();
+    if (dsa == NULL)
+        return 0;
+    EVP_PKEY_assign_DSA(pkey, dsa);
+    /* Note: if error return, pkey is freed by parent routine */
+    if (!EVP_PKEY_copy_parameters(pkey, ctx->pkey))
+        return 0;
+    return DSA_generate_key(pkey->pkey.dsa);
+}
+
+const EVP_PKEY_METHOD dsa_pkey_meth = {
+    EVP_PKEY_DSA,
+    EVP_PKEY_FLAG_AUTOARGLEN,
+    pkey_dsa_init,
+    pkey_dsa_copy,
+    pkey_dsa_cleanup,
+
+    0,
+    pkey_dsa_paramgen,
+
+    0,
+    pkey_dsa_keygen,
+
+    0,
+    pkey_dsa_sign,
+
+    0,
+    pkey_dsa_verify,
+
+    0, 0,
+
+    0, 0, 0, 0,
+
+    0, 0,
+
+    0, 0,
+
+    0, 0,
+
+    pkey_dsa_ctrl,
+    pkey_dsa_ctrl_str
+};
diff --git a/crypto/dsa/dsa_prn.c b/crypto/dsa/dsa_prn.c
new file mode 100644
index 000000000..f3c20ea0a
--- /dev/null
+++ b/crypto/dsa/dsa_prn.c
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2006-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+
+#ifndef OPENSSL_NO_STDIO
+int DSA_print_fp(FILE *fp, const DSA *x, int off)
+{
+    BIO *b;
+    int ret;
+
+    if ((b = BIO_new(BIO_s_file())) == NULL) {
+        DSAerr(DSA_F_DSA_PRINT_FP, ERR_R_BUF_LIB);
+        return (0);
+    }
+    BIO_set_fp(b, fp, BIO_NOCLOSE);
+    ret = DSA_print(b, x, off);
+    BIO_free(b);
+    return (ret);
+}
+
+int DSAparams_print_fp(FILE *fp, const DSA *x)
+{
+    BIO *b;
+    int ret;
+
+    if ((b = BIO_new(BIO_s_file())) == NULL) {
+        DSAerr(DSA_F_DSAPARAMS_PRINT_FP, ERR_R_BUF_LIB);
+        return (0);
+    }
+    BIO_set_fp(b, fp, BIO_NOCLOSE);
+    ret = DSAparams_print(b, x);
+    BIO_free(b);
+    return (ret);
+}
+#endif
+
+int DSA_print(BIO *bp, const DSA *x, int off)
+{
+    EVP_PKEY *pk;
+    int ret;
+    pk = EVP_PKEY_new();
+    if (pk == NULL || !EVP_PKEY_set1_DSA(pk, (DSA *)x))
+        return 0;
+    ret = EVP_PKEY_print_private(bp, pk, off, NULL);
+    EVP_PKEY_free(pk);
+    return ret;
+}
+
+int DSAparams_print(BIO *bp, const DSA *x)
+{
+    EVP_PKEY *pk;
+    int ret;
+    pk = EVP_PKEY_new();
+    if (pk == NULL || !EVP_PKEY_set1_DSA(pk, (DSA *)x))
+        return 0;
+    ret = EVP_PKEY_print_params(bp, pk, 4, NULL);
+    EVP_PKEY_free(pk);
+    return ret;
+}
diff --git a/crypto/dsa/dsa_sign.c b/crypto/dsa/dsa_sign.c
new file mode 100644
index 000000000..2e29d4008
--- /dev/null
+++ b/crypto/dsa/dsa_sign.c
@@ -0,0 +1,24 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/* Original version from Steven Schoch  */
+
+#include "internal/cryptlib.h"
+#include "dsa_locl.h"
+#include 
+
+DSA_SIG *DSA_do_sign(const unsigned char *dgst, int dlen, DSA *dsa)
+{
+    return dsa->meth->dsa_do_sign(dgst, dlen, dsa);
+}
+
+int DSA_sign_setup(DSA *dsa, BN_CTX *ctx_in, BIGNUM **kinvp, BIGNUM **rp)
+{
+    return dsa->meth->dsa_sign_setup(dsa, ctx_in, kinvp, rp);
+}
diff --git a/crypto/dsa/dsa_vrf.c b/crypto/dsa/dsa_vrf.c
new file mode 100644
index 000000000..a84d52128
--- /dev/null
+++ b/crypto/dsa/dsa_vrf.c
@@ -0,0 +1,19 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/* Original version from Steven Schoch  */
+
+#include "internal/cryptlib.h"
+#include "dsa_locl.h"
+
+int DSA_do_verify(const unsigned char *dgst, int dgst_len, DSA_SIG *sig,
+                  DSA *dsa)
+{
+    return dsa->meth->dsa_do_verify(dgst, dgst_len, sig, dsa);
+}
diff --git a/crypto/dso/build.info b/crypto/dso/build.info
new file mode 100644
index 000000000..82b592d9a
--- /dev/null
+++ b/crypto/dso/build.info
@@ -0,0 +1,4 @@
+LIBS=../../libcrypto
+SOURCE[../../libcrypto]=\
+        dso_dl.c dso_dlfcn.c dso_err.c dso_lib.c \
+        dso_openssl.c dso_win32.c dso_vms.c
diff --git a/crypto/dso/dso_dl.c b/crypto/dso/dso_dl.c
new file mode 100644
index 000000000..d80bf562c
--- /dev/null
+++ b/crypto/dso/dso_dl.c
@@ -0,0 +1,279 @@
+/*
+ * Copyright 2000-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "dso_locl.h"
+
+#ifdef DSO_DL
+
+# include 
+
+/* Part of the hack in "dl_load" ... */
+# define DSO_MAX_TRANSLATED_SIZE 256
+
+static int dl_load(DSO *dso);
+static int dl_unload(DSO *dso);
+static DSO_FUNC_TYPE dl_bind_func(DSO *dso, const char *symname);
+static char *dl_name_converter(DSO *dso, const char *filename);
+static char *dl_merger(DSO *dso, const char *filespec1,
+                       const char *filespec2);
+static int dl_pathbyaddr(void *addr, char *path, int sz);
+static void *dl_globallookup(const char *name);
+
+static DSO_METHOD dso_meth_dl = {
+    "OpenSSL 'dl' shared library method",
+    dl_load,
+    dl_unload,
+    dl_bind_func,
+    NULL,                       /* ctrl */
+    dl_name_converter,
+    dl_merger,
+    NULL,                       /* init */
+    NULL,                       /* finish */
+    dl_pathbyaddr,
+    dl_globallookup
+};
+
+DSO_METHOD *DSO_METHOD_openssl(void)
+{
+    return &dso_meth_dl;
+}
+
+/*
+ * For this DSO_METHOD, our meth_data STACK will contain; (i) the handle
+ * (shl_t) returned from shl_load(). NB: I checked on HPUX11 and shl_t is
+ * itself a pointer type so the cast is safe.
+ */
+
+static int dl_load(DSO *dso)
+{
+    shl_t ptr = NULL;
+    /*
+     * We don't do any fancy retries or anything, just take the method's (or
+     * DSO's if it has the callback set) best translation of the
+     * platform-independent filename and try once with that.
+     */
+    char *filename = DSO_convert_filename(dso, NULL);
+
+    if (filename == NULL) {
+        DSOerr(DSO_F_DL_LOAD, DSO_R_NO_FILENAME);
+        goto err;
+    }
+    ptr = shl_load(filename, BIND_IMMEDIATE |
+                   (dso->flags & DSO_FLAG_NO_NAME_TRANSLATION ? 0 :
+                    DYNAMIC_PATH), 0L);
+    if (ptr == NULL) {
+        char errbuf[160];
+        DSOerr(DSO_F_DL_LOAD, DSO_R_LOAD_FAILED);
+        if (openssl_strerror_r(errno, errbuf, sizeof(errbuf)))
+            ERR_add_error_data(4, "filename(", filename, "): ", errbuf);
+        goto err;
+    }
+    if (!sk_push(dso->meth_data, (char *)ptr)) {
+        DSOerr(DSO_F_DL_LOAD, DSO_R_STACK_ERROR);
+        goto err;
+    }
+    /*
+     * Success, stick the converted filename we've loaded under into the DSO
+     * (it also serves as the indicator that we are currently loaded).
+     */
+    dso->loaded_filename = filename;
+    return (1);
+ err:
+    /* Cleanup! */
+    OPENSSL_free(filename);
+    if (ptr != NULL)
+        shl_unload(ptr);
+    return (0);
+}
+
+static int dl_unload(DSO *dso)
+{
+    shl_t ptr;
+    if (dso == NULL) {
+        DSOerr(DSO_F_DL_UNLOAD, ERR_R_PASSED_NULL_PARAMETER);
+        return (0);
+    }
+    if (sk_num(dso->meth_data) < 1)
+        return (1);
+    /* Is this statement legal? */
+    ptr = (shl_t) sk_pop(dso->meth_data);
+    if (ptr == NULL) {
+        DSOerr(DSO_F_DL_UNLOAD, DSO_R_NULL_HANDLE);
+        /*
+         * Should push the value back onto the stack in case of a retry.
+         */
+        sk_push(dso->meth_data, (char *)ptr);
+        return (0);
+    }
+    shl_unload(ptr);
+    return (1);
+}
+
+static DSO_FUNC_TYPE dl_bind_func(DSO *dso, const char *symname)
+{
+    shl_t ptr;
+    void *sym;
+
+    if ((dso == NULL) || (symname == NULL)) {
+        DSOerr(DSO_F_DL_BIND_FUNC, ERR_R_PASSED_NULL_PARAMETER);
+        return (NULL);
+    }
+    if (sk_num(dso->meth_data) < 1) {
+        DSOerr(DSO_F_DL_BIND_FUNC, DSO_R_STACK_ERROR);
+        return (NULL);
+    }
+    ptr = (shl_t) sk_value(dso->meth_data, sk_num(dso->meth_data) - 1);
+    if (ptr == NULL) {
+        DSOerr(DSO_F_DL_BIND_FUNC, DSO_R_NULL_HANDLE);
+        return (NULL);
+    }
+    if (shl_findsym(&ptr, symname, TYPE_UNDEFINED, &sym) < 0) {
+        char errbuf[160];
+        DSOerr(DSO_F_DL_BIND_FUNC, DSO_R_SYM_FAILURE);
+        if (openssl_strerror_r(errno, errbuf, sizeof(errbuf)))
+            ERR_add_error_data(4, "symname(", symname, "): ", errbuf);
+        return (NULL);
+    }
+    return ((DSO_FUNC_TYPE)sym);
+}
+
+static char *dl_merger(DSO *dso, const char *filespec1, const char *filespec2)
+{
+    char *merged;
+
+    if (!filespec1 && !filespec2) {
+        DSOerr(DSO_F_DL_MERGER, ERR_R_PASSED_NULL_PARAMETER);
+        return (NULL);
+    }
+    /*
+     * If the first file specification is a rooted path, it rules. same goes
+     * if the second file specification is missing.
+     */
+    if (!filespec2 || filespec1[0] == '/') {
+        merged = OPENSSL_strdup(filespec1);
+        if (merged == NULL) {
+            DSOerr(DSO_F_DL_MERGER, ERR_R_MALLOC_FAILURE);
+            return (NULL);
+        }
+    }
+    /*
+     * If the first file specification is missing, the second one rules.
+     */
+    else if (!filespec1) {
+        merged = OPENSSL_strdup(filespec2);
+        if (merged == NULL) {
+            DSOerr(DSO_F_DL_MERGER, ERR_R_MALLOC_FAILURE);
+            return (NULL);
+        }
+    } else
+        /*
+         * This part isn't as trivial as it looks.  It assumes that the
+         * second file specification really is a directory, and makes no
+         * checks whatsoever.  Therefore, the result becomes the
+         * concatenation of filespec2 followed by a slash followed by
+         * filespec1.
+         */
+    {
+        int spec2len, len;
+
+        spec2len = (filespec2 ? strlen(filespec2) : 0);
+        len = spec2len + (filespec1 ? strlen(filespec1) : 0);
+
+        if (spec2len && filespec2[spec2len - 1] == '/') {
+            spec2len--;
+            len--;
+        }
+        merged = OPENSSL_malloc(len + 2);
+        if (merged == NULL) {
+            DSOerr(DSO_F_DL_MERGER, ERR_R_MALLOC_FAILURE);
+            return (NULL);
+        }
+        strcpy(merged, filespec2);
+        merged[spec2len] = '/';
+        strcpy(&merged[spec2len + 1], filespec1);
+    }
+    return (merged);
+}
+
+/*
+ * This function is identical to the one in dso_dlfcn.c, but as it is highly
+ * unlikely that both the "dl" *and* "dlfcn" variants are being compiled at
+ * the same time, there's no great duplicating the code. Figuring out an
+ * elegant way to share one copy of the code would be more difficult and
+ * would not leave the implementations independent.
+ */
+static char *dl_name_converter(DSO *dso, const char *filename)
+{
+    char *translated;
+    int len, rsize, transform;
+
+    len = strlen(filename);
+    rsize = len + 1;
+    transform = (strstr(filename, "/") == NULL);
+    {
+        /* We will convert this to "%s.s?" or "lib%s.s?" */
+        rsize += strlen(DSO_EXTENSION); /* The length of ".s?" */
+        if ((DSO_flags(dso) & DSO_FLAG_NAME_TRANSLATION_EXT_ONLY) == 0)
+            rsize += 3;         /* The length of "lib" */
+    }
+    translated = OPENSSL_malloc(rsize);
+    if (translated == NULL) {
+        DSOerr(DSO_F_DL_NAME_CONVERTER, DSO_R_NAME_TRANSLATION_FAILED);
+        return (NULL);
+    }
+    if (transform) {
+        if ((DSO_flags(dso) & DSO_FLAG_NAME_TRANSLATION_EXT_ONLY) == 0)
+            sprintf(translated, "lib%s%s", filename, DSO_EXTENSION);
+        else
+            sprintf(translated, "%s%s", filename, DSO_EXTENSION);
+    } else
+        sprintf(translated, "%s", filename);
+    return (translated);
+}
+
+static int dl_pathbyaddr(void *addr, char *path, int sz)
+{
+    struct shl_descriptor inf;
+    int i, len;
+
+    if (addr == NULL) {
+        union {
+            int (*f) (void *, char *, int);
+            void *p;
+        } t = {
+            dl_pathbyaddr
+        };
+        addr = t.p;
+    }
+
+    for (i = -1; shl_get_r(i, &inf) == 0; i++) {
+        if (((size_t)addr >= inf.tstart && (size_t)addr < inf.tend) ||
+            ((size_t)addr >= inf.dstart && (size_t)addr < inf.dend)) {
+            len = (int)strlen(inf.filename);
+            if (sz <= 0)
+                return len + 1;
+            if (len >= sz)
+                len = sz - 1;
+            memcpy(path, inf.filename, len);
+            path[len++] = 0;
+            return len;
+        }
+    }
+
+    return -1;
+}
+
+static void *dl_globallookup(const char *name)
+{
+    void *ret;
+    shl_t h = NULL;
+
+    return shl_findsym(&h, name, TYPE_UNDEFINED, &ret) ? NULL : ret;
+}
+#endif                          /* DSO_DL */
diff --git a/crypto/dso/dso_dlfcn.c b/crypto/dso/dso_dlfcn.c
new file mode 100644
index 000000000..a4b0cdd95
--- /dev/null
+++ b/crypto/dso/dso_dlfcn.c
@@ -0,0 +1,354 @@
+/*
+ * Copyright 2000-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * We need to do this early, because stdio.h includes the header files that
+ * handle _GNU_SOURCE and other similar macros.  Defining it later is simply
+ * too late, because those headers are protected from re- inclusion.
+ */
+#ifndef _GNU_SOURCE
+# define _GNU_SOURCE            /* make sure dladdr is declared */
+#endif
+
+#include "dso_locl.h"
+
+#ifdef DSO_DLFCN
+
+# ifdef HAVE_DLFCN_H
+#  ifdef __osf__
+#   define __EXTENSIONS__
+#  endif
+#  include 
+#  define HAVE_DLINFO 1
+#  if defined(_AIX) || defined(__CYGWIN__) || \
+     defined(__SCO_VERSION__) || defined(_SCO_ELF) || \
+     (defined(__osf__) && !defined(RTLD_NEXT))     || \
+     (defined(__OpenBSD__) && !defined(RTLD_SELF)) || \
+        defined(__ANDROID__)
+#   undef HAVE_DLINFO
+#  endif
+# endif
+
+/* Part of the hack in "dlfcn_load" ... */
+# define DSO_MAX_TRANSLATED_SIZE 256
+
+static int dlfcn_load(DSO *dso);
+static int dlfcn_unload(DSO *dso);
+static DSO_FUNC_TYPE dlfcn_bind_func(DSO *dso, const char *symname);
+static char *dlfcn_name_converter(DSO *dso, const char *filename);
+static char *dlfcn_merger(DSO *dso, const char *filespec1,
+                          const char *filespec2);
+static int dlfcn_pathbyaddr(void *addr, char *path, int sz);
+static void *dlfcn_globallookup(const char *name);
+
+static DSO_METHOD dso_meth_dlfcn = {
+    "OpenSSL 'dlfcn' shared library method",
+    dlfcn_load,
+    dlfcn_unload,
+    dlfcn_bind_func,
+    NULL,                       /* ctrl */
+    dlfcn_name_converter,
+    dlfcn_merger,
+    NULL,                       /* init */
+    NULL,                       /* finish */
+    dlfcn_pathbyaddr,
+    dlfcn_globallookup
+};
+
+DSO_METHOD *DSO_METHOD_openssl(void)
+{
+    return &dso_meth_dlfcn;
+}
+
+/*
+ * Prior to using the dlopen() function, we should decide on the flag we
+ * send. There's a few different ways of doing this and it's a messy
+ * venn-diagram to match up which platforms support what. So as we don't have
+ * autoconf yet, I'm implementing a hack that could be hacked further
+ * relatively easily to deal with cases as we find them. Initially this is to
+ * cope with OpenBSD.
+ */
+# if defined(__OpenBSD__) || defined(__NetBSD__)
+#  ifdef DL_LAZY
+#   define DLOPEN_FLAG DL_LAZY
+#  else
+#   ifdef RTLD_NOW
+#    define DLOPEN_FLAG RTLD_NOW
+#   else
+#    define DLOPEN_FLAG 0
+#   endif
+#  endif
+# else
+#  define DLOPEN_FLAG RTLD_NOW  /* Hope this works everywhere else */
+# endif
+
+/*
+ * For this DSO_METHOD, our meth_data STACK will contain; (i) the handle
+ * (void*) returned from dlopen().
+ */
+
+static int dlfcn_load(DSO *dso)
+{
+    void *ptr = NULL;
+    /* See applicable comments in dso_dl.c */
+    char *filename = DSO_convert_filename(dso, NULL);
+    int flags = DLOPEN_FLAG;
+
+    if (filename == NULL) {
+        DSOerr(DSO_F_DLFCN_LOAD, DSO_R_NO_FILENAME);
+        goto err;
+    }
+# ifdef RTLD_GLOBAL
+    if (dso->flags & DSO_FLAG_GLOBAL_SYMBOLS)
+        flags |= RTLD_GLOBAL;
+# endif
+    ptr = dlopen(filename, flags);
+    if (ptr == NULL) {
+        DSOerr(DSO_F_DLFCN_LOAD, DSO_R_LOAD_FAILED);
+        ERR_add_error_data(4, "filename(", filename, "): ", dlerror());
+        goto err;
+    }
+    if (!sk_void_push(dso->meth_data, (char *)ptr)) {
+        DSOerr(DSO_F_DLFCN_LOAD, DSO_R_STACK_ERROR);
+        goto err;
+    }
+    /* Success */
+    dso->loaded_filename = filename;
+    return (1);
+ err:
+    /* Cleanup! */
+    OPENSSL_free(filename);
+    if (ptr != NULL)
+        dlclose(ptr);
+    return (0);
+}
+
+static int dlfcn_unload(DSO *dso)
+{
+    void *ptr;
+    if (dso == NULL) {
+        DSOerr(DSO_F_DLFCN_UNLOAD, ERR_R_PASSED_NULL_PARAMETER);
+        return (0);
+    }
+    if (sk_void_num(dso->meth_data) < 1)
+        return (1);
+    ptr = sk_void_pop(dso->meth_data);
+    if (ptr == NULL) {
+        DSOerr(DSO_F_DLFCN_UNLOAD, DSO_R_NULL_HANDLE);
+        /*
+         * Should push the value back onto the stack in case of a retry.
+         */
+        sk_void_push(dso->meth_data, ptr);
+        return (0);
+    }
+    /* For now I'm not aware of any errors associated with dlclose() */
+    dlclose(ptr);
+    return (1);
+}
+
+static DSO_FUNC_TYPE dlfcn_bind_func(DSO *dso, const char *symname)
+{
+    void *ptr;
+    union {
+        DSO_FUNC_TYPE sym;
+        void *dlret;
+    } u;
+
+    if ((dso == NULL) || (symname == NULL)) {
+        DSOerr(DSO_F_DLFCN_BIND_FUNC, ERR_R_PASSED_NULL_PARAMETER);
+        return (NULL);
+    }
+    if (sk_void_num(dso->meth_data) < 1) {
+        DSOerr(DSO_F_DLFCN_BIND_FUNC, DSO_R_STACK_ERROR);
+        return (NULL);
+    }
+    ptr = sk_void_value(dso->meth_data, sk_void_num(dso->meth_data) - 1);
+    if (ptr == NULL) {
+        DSOerr(DSO_F_DLFCN_BIND_FUNC, DSO_R_NULL_HANDLE);
+        return (NULL);
+    }
+    u.dlret = dlsym(ptr, symname);
+    if (u.dlret == NULL) {
+        DSOerr(DSO_F_DLFCN_BIND_FUNC, DSO_R_SYM_FAILURE);
+        ERR_add_error_data(4, "symname(", symname, "): ", dlerror());
+        return (NULL);
+    }
+    return u.sym;
+}
+
+static char *dlfcn_merger(DSO *dso, const char *filespec1,
+                          const char *filespec2)
+{
+    char *merged;
+
+    if (!filespec1 && !filespec2) {
+        DSOerr(DSO_F_DLFCN_MERGER, ERR_R_PASSED_NULL_PARAMETER);
+        return (NULL);
+    }
+    /*
+     * If the first file specification is a rooted path, it rules. same goes
+     * if the second file specification is missing.
+     */
+    if (!filespec2 || (filespec1 != NULL && filespec1[0] == '/')) {
+        merged = OPENSSL_strdup(filespec1);
+        if (merged == NULL) {
+            DSOerr(DSO_F_DLFCN_MERGER, ERR_R_MALLOC_FAILURE);
+            return (NULL);
+        }
+    }
+    /*
+     * If the first file specification is missing, the second one rules.
+     */
+    else if (!filespec1) {
+        merged = OPENSSL_strdup(filespec2);
+        if (merged == NULL) {
+            DSOerr(DSO_F_DLFCN_MERGER, ERR_R_MALLOC_FAILURE);
+            return (NULL);
+        }
+    } else {
+        /*
+         * This part isn't as trivial as it looks.  It assumes that the
+         * second file specification really is a directory, and makes no
+         * checks whatsoever.  Therefore, the result becomes the
+         * concatenation of filespec2 followed by a slash followed by
+         * filespec1.
+         */
+        int spec2len, len;
+
+        spec2len = strlen(filespec2);
+        len = spec2len + strlen(filespec1);
+
+        if (spec2len && filespec2[spec2len - 1] == '/') {
+            spec2len--;
+            len--;
+        }
+        merged = OPENSSL_malloc(len + 2);
+        if (merged == NULL) {
+            DSOerr(DSO_F_DLFCN_MERGER, ERR_R_MALLOC_FAILURE);
+            return (NULL);
+        }
+        strcpy(merged, filespec2);
+        merged[spec2len] = '/';
+        strcpy(&merged[spec2len + 1], filespec1);
+    }
+    return (merged);
+}
+
+static char *dlfcn_name_converter(DSO *dso, const char *filename)
+{
+    char *translated;
+    int len, rsize, transform;
+
+    len = strlen(filename);
+    rsize = len + 1;
+    transform = (strstr(filename, "/") == NULL);
+    if (transform) {
+        /* We will convert this to "%s.so" or "lib%s.so" etc */
+        rsize += strlen(DSO_EXTENSION);    /* The length of ".so" */
+        if ((DSO_flags(dso) & DSO_FLAG_NAME_TRANSLATION_EXT_ONLY) == 0)
+            rsize += 3;         /* The length of "lib" */
+    }
+    translated = OPENSSL_malloc(rsize);
+    if (translated == NULL) {
+        DSOerr(DSO_F_DLFCN_NAME_CONVERTER, DSO_R_NAME_TRANSLATION_FAILED);
+        return (NULL);
+    }
+    if (transform) {
+        if ((DSO_flags(dso) & DSO_FLAG_NAME_TRANSLATION_EXT_ONLY) == 0)
+            sprintf(translated, "lib%s" DSO_EXTENSION, filename);
+        else
+            sprintf(translated, "%s" DSO_EXTENSION, filename);
+    } else
+        sprintf(translated, "%s", filename);
+    return (translated);
+}
+
+# ifdef __sgi
+/*-
+This is a quote from IRIX manual for dladdr(3c):
+
+      does not contain a prototype for dladdr or definition of
+     Dl_info.  The #include   in the SYNOPSIS line is traditional,
+     but contains no dladdr prototype and no IRIX library contains an
+     implementation.  Write your own declaration based on the code below.
+
+     The following code is dependent on internal interfaces that are not
+     part of the IRIX compatibility guarantee; however, there is no future
+     intention to change this interface, so on a practical level, the code
+     below is safe to use on IRIX.
+*/
+#  include 
+#  ifndef _RLD_INTERFACE_DLFCN_H_DLADDR
+#   define _RLD_INTERFACE_DLFCN_H_DLADDR
+typedef struct Dl_info {
+    const char *dli_fname;
+    void *dli_fbase;
+    const char *dli_sname;
+    void *dli_saddr;
+    int dli_version;
+    int dli_reserved1;
+    long dli_reserved[4];
+} Dl_info;
+#  else
+typedef struct Dl_info Dl_info;
+#  endif
+#  define _RLD_DLADDR             14
+
+static int dladdr(void *address, Dl_info *dl)
+{
+    void *v;
+    v = _rld_new_interface(_RLD_DLADDR, address, dl);
+    return (int)v;
+}
+# endif                         /* __sgi */
+
+static int dlfcn_pathbyaddr(void *addr, char *path, int sz)
+{
+# ifdef HAVE_DLINFO
+    Dl_info dli;
+    int len;
+
+    if (addr == NULL) {
+        union {
+            int (*f) (void *, char *, int);
+            void *p;
+        } t = {
+            dlfcn_pathbyaddr
+        };
+        addr = t.p;
+    }
+
+    if (dladdr(addr, &dli)) {
+        len = (int)strlen(dli.dli_fname);
+        if (sz <= 0)
+            return len + 1;
+        if (len >= sz)
+            len = sz - 1;
+        memcpy(path, dli.dli_fname, len);
+        path[len++] = 0;
+        return len;
+    }
+
+    ERR_add_error_data(2, "dlfcn_pathbyaddr(): ", dlerror());
+# endif
+    return -1;
+}
+
+static void *dlfcn_globallookup(const char *name)
+{
+    void *ret = NULL, *handle = dlopen(NULL, RTLD_LAZY);
+
+    if (handle) {
+        ret = dlsym(handle, name);
+        dlclose(handle);
+    }
+
+    return ret;
+}
+#endif                          /* DSO_DLFCN */
diff --git a/crypto/dso/dso_err.c b/crypto/dso/dso_err.c
new file mode 100644
index 000000000..07588d5c3
--- /dev/null
+++ b/crypto/dso/dso_err.c
@@ -0,0 +1,93 @@
+/*
+ * Generated by util/mkerr.pl DO NOT EDIT
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include "internal/dso.h"
+
+/* BEGIN ERROR CODES */
+#ifndef OPENSSL_NO_ERR
+
+# define ERR_FUNC(func) ERR_PACK(ERR_LIB_DSO,func,0)
+# define ERR_REASON(reason) ERR_PACK(ERR_LIB_DSO,0,reason)
+
+static ERR_STRING_DATA DSO_str_functs[] = {
+    {ERR_FUNC(DSO_F_DLFCN_BIND_FUNC), "dlfcn_bind_func"},
+    {ERR_FUNC(DSO_F_DLFCN_LOAD), "dlfcn_load"},
+    {ERR_FUNC(DSO_F_DLFCN_MERGER), "dlfcn_merger"},
+    {ERR_FUNC(DSO_F_DLFCN_NAME_CONVERTER), "dlfcn_name_converter"},
+    {ERR_FUNC(DSO_F_DLFCN_UNLOAD), "dlfcn_unload"},
+    {ERR_FUNC(DSO_F_DL_BIND_FUNC), "dl_bind_func"},
+    {ERR_FUNC(DSO_F_DL_LOAD), "dl_load"},
+    {ERR_FUNC(DSO_F_DL_MERGER), "dl_merger"},
+    {ERR_FUNC(DSO_F_DL_NAME_CONVERTER), "dl_name_converter"},
+    {ERR_FUNC(DSO_F_DL_UNLOAD), "dl_unload"},
+    {ERR_FUNC(DSO_F_DSO_BIND_FUNC), "DSO_bind_func"},
+    {ERR_FUNC(DSO_F_DSO_CONVERT_FILENAME), "DSO_convert_filename"},
+    {ERR_FUNC(DSO_F_DSO_CTRL), "DSO_ctrl"},
+    {ERR_FUNC(DSO_F_DSO_FREE), "DSO_free"},
+    {ERR_FUNC(DSO_F_DSO_GET_FILENAME), "DSO_get_filename"},
+    {ERR_FUNC(DSO_F_DSO_GLOBAL_LOOKUP), "DSO_global_lookup"},
+    {ERR_FUNC(DSO_F_DSO_LOAD), "DSO_load"},
+    {ERR_FUNC(DSO_F_DSO_MERGE), "DSO_merge"},
+    {ERR_FUNC(DSO_F_DSO_NEW_METHOD), "DSO_new_method"},
+    {ERR_FUNC(DSO_F_DSO_PATHBYADDR), "DSO_pathbyaddr"},
+    {ERR_FUNC(DSO_F_DSO_SET_FILENAME), "DSO_set_filename"},
+    {ERR_FUNC(DSO_F_DSO_UP_REF), "DSO_up_ref"},
+    {ERR_FUNC(DSO_F_VMS_BIND_SYM), "vms_bind_sym"},
+    {ERR_FUNC(DSO_F_VMS_LOAD), "vms_load"},
+    {ERR_FUNC(DSO_F_VMS_MERGER), "vms_merger"},
+    {ERR_FUNC(DSO_F_VMS_UNLOAD), "vms_unload"},
+    {ERR_FUNC(DSO_F_WIN32_BIND_FUNC), "win32_bind_func"},
+    {ERR_FUNC(DSO_F_WIN32_GLOBALLOOKUP), "win32_globallookup"},
+    {ERR_FUNC(DSO_F_WIN32_JOINER), "win32_joiner"},
+    {ERR_FUNC(DSO_F_WIN32_LOAD), "win32_load"},
+    {ERR_FUNC(DSO_F_WIN32_MERGER), "win32_merger"},
+    {ERR_FUNC(DSO_F_WIN32_NAME_CONVERTER), "win32_name_converter"},
+    {ERR_FUNC(DSO_F_WIN32_PATHBYADDR), "win32_pathbyaddr"},
+    {ERR_FUNC(DSO_F_WIN32_SPLITTER), "win32_splitter"},
+    {ERR_FUNC(DSO_F_WIN32_UNLOAD), "win32_unload"},
+    {0, NULL}
+};
+
+static ERR_STRING_DATA DSO_str_reasons[] = {
+    {ERR_REASON(DSO_R_CTRL_FAILED), "control command failed"},
+    {ERR_REASON(DSO_R_DSO_ALREADY_LOADED), "dso already loaded"},
+    {ERR_REASON(DSO_R_EMPTY_FILE_STRUCTURE), "empty file structure"},
+    {ERR_REASON(DSO_R_FAILURE), "failure"},
+    {ERR_REASON(DSO_R_FILENAME_TOO_BIG), "filename too big"},
+    {ERR_REASON(DSO_R_FINISH_FAILED), "cleanup method function failed"},
+    {ERR_REASON(DSO_R_INCORRECT_FILE_SYNTAX), "incorrect file syntax"},
+    {ERR_REASON(DSO_R_LOAD_FAILED), "could not load the shared library"},
+    {ERR_REASON(DSO_R_NAME_TRANSLATION_FAILED), "name translation failed"},
+    {ERR_REASON(DSO_R_NO_FILENAME), "no filename"},
+    {ERR_REASON(DSO_R_NULL_HANDLE), "a null shared library handle was used"},
+    {ERR_REASON(DSO_R_SET_FILENAME_FAILED), "set filename failed"},
+    {ERR_REASON(DSO_R_STACK_ERROR), "the meth_data stack is corrupt"},
+    {ERR_REASON(DSO_R_SYM_FAILURE),
+     "could not bind to the requested symbol name"},
+    {ERR_REASON(DSO_R_UNLOAD_FAILED), "could not unload the shared library"},
+    {ERR_REASON(DSO_R_UNSUPPORTED), "functionality not supported"},
+    {0, NULL}
+};
+
+#endif
+
+int ERR_load_DSO_strings(void)
+{
+#ifndef OPENSSL_NO_ERR
+
+    if (ERR_func_error_string(DSO_str_functs[0].error) == NULL) {
+        ERR_load_strings(0, DSO_str_functs);
+        ERR_load_strings(0, DSO_str_reasons);
+    }
+#endif
+    return 1;
+}
diff --git a/crypto/dso/dso_lib.c b/crypto/dso/dso_lib.c
new file mode 100644
index 000000000..f58237d64
--- /dev/null
+++ b/crypto/dso/dso_lib.c
@@ -0,0 +1,349 @@
+/*
+ * Copyright 2000-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "dso_locl.h"
+
+static DSO_METHOD *default_DSO_meth = NULL;
+
+static DSO *DSO_new_method(DSO_METHOD *meth)
+{
+    DSO *ret;
+
+    if (default_DSO_meth == NULL) {
+        /*
+         * We default to DSO_METH_openssl() which in turn defaults to
+         * stealing the "best available" method. Will fallback to
+         * DSO_METH_null() in the worst case.
+         */
+        default_DSO_meth = DSO_METHOD_openssl();
+    }
+    ret = OPENSSL_zalloc(sizeof(*ret));
+    if (ret == NULL) {
+        DSOerr(DSO_F_DSO_NEW_METHOD, ERR_R_MALLOC_FAILURE);
+        return (NULL);
+    }
+    ret->meth_data = sk_void_new_null();
+    if (ret->meth_data == NULL) {
+        /* sk_new doesn't generate any errors so we do */
+        DSOerr(DSO_F_DSO_NEW_METHOD, ERR_R_MALLOC_FAILURE);
+        OPENSSL_free(ret);
+        return (NULL);
+    }
+    ret->meth = default_DSO_meth;
+    ret->references = 1;
+    ret->lock = CRYPTO_THREAD_lock_new();
+    if (ret->lock == NULL) {
+        DSOerr(DSO_F_DSO_NEW_METHOD, ERR_R_MALLOC_FAILURE);
+        sk_void_free(ret->meth_data);
+        OPENSSL_free(ret);
+        return NULL;
+    }
+
+    if ((ret->meth->init != NULL) && !ret->meth->init(ret)) {
+        DSO_free(ret);
+        ret = NULL;
+    }
+
+    return ret;
+}
+
+DSO *DSO_new(void)
+{
+    return DSO_new_method(NULL);
+}
+
+int DSO_free(DSO *dso)
+{
+    int i;
+
+    if (dso == NULL)
+        return (1);
+
+    if (CRYPTO_atomic_add(&dso->references, -1, &i, dso->lock) <= 0)
+        return 0;
+
+    REF_PRINT_COUNT("DSO", dso);
+    if (i > 0)
+        return 1;
+    REF_ASSERT_ISNT(i < 0);
+
+    if ((dso->flags & DSO_FLAG_NO_UNLOAD_ON_FREE) == 0) {
+        if ((dso->meth->dso_unload != NULL) && !dso->meth->dso_unload(dso)) {
+            DSOerr(DSO_F_DSO_FREE, DSO_R_UNLOAD_FAILED);
+            return 0;
+        }
+    }
+
+    if ((dso->meth->finish != NULL) && !dso->meth->finish(dso)) {
+        DSOerr(DSO_F_DSO_FREE, DSO_R_FINISH_FAILED);
+        return 0;
+    }
+
+    sk_void_free(dso->meth_data);
+    OPENSSL_free(dso->filename);
+    OPENSSL_free(dso->loaded_filename);
+    CRYPTO_THREAD_lock_free(dso->lock);
+    OPENSSL_free(dso);
+    return 1;
+}
+
+int DSO_flags(DSO *dso)
+{
+    return ((dso == NULL) ? 0 : dso->flags);
+}
+
+int DSO_up_ref(DSO *dso)
+{
+    int i;
+
+    if (dso == NULL) {
+        DSOerr(DSO_F_DSO_UP_REF, ERR_R_PASSED_NULL_PARAMETER);
+        return 0;
+    }
+
+    if (CRYPTO_atomic_add(&dso->references, 1, &i, dso->lock) <= 0)
+        return 0;
+
+    REF_PRINT_COUNT("DSO", r);
+    REF_ASSERT_ISNT(i < 2);
+    return ((i > 1) ? 1 : 0);
+}
+
+DSO *DSO_load(DSO *dso, const char *filename, DSO_METHOD *meth, int flags)
+{
+    DSO *ret;
+    int allocated = 0;
+
+    if (dso == NULL) {
+        ret = DSO_new_method(meth);
+        if (ret == NULL) {
+            DSOerr(DSO_F_DSO_LOAD, ERR_R_MALLOC_FAILURE);
+            goto err;
+        }
+        allocated = 1;
+        /* Pass the provided flags to the new DSO object */
+        if (DSO_ctrl(ret, DSO_CTRL_SET_FLAGS, flags, NULL) < 0) {
+            DSOerr(DSO_F_DSO_LOAD, DSO_R_CTRL_FAILED);
+            goto err;
+        }
+    } else
+        ret = dso;
+    /* Don't load if we're currently already loaded */
+    if (ret->filename != NULL) {
+        DSOerr(DSO_F_DSO_LOAD, DSO_R_DSO_ALREADY_LOADED);
+        goto err;
+    }
+    /*
+     * filename can only be NULL if we were passed a dso that already has one
+     * set.
+     */
+    if (filename != NULL)
+        if (!DSO_set_filename(ret, filename)) {
+            DSOerr(DSO_F_DSO_LOAD, DSO_R_SET_FILENAME_FAILED);
+            goto err;
+        }
+    filename = ret->filename;
+    if (filename == NULL) {
+        DSOerr(DSO_F_DSO_LOAD, DSO_R_NO_FILENAME);
+        goto err;
+    }
+    if (ret->meth->dso_load == NULL) {
+        DSOerr(DSO_F_DSO_LOAD, DSO_R_UNSUPPORTED);
+        goto err;
+    }
+    if (!ret->meth->dso_load(ret)) {
+        DSOerr(DSO_F_DSO_LOAD, DSO_R_LOAD_FAILED);
+        goto err;
+    }
+    /* Load succeeded */
+    return (ret);
+ err:
+    if (allocated)
+        DSO_free(ret);
+    return (NULL);
+}
+
+DSO_FUNC_TYPE DSO_bind_func(DSO *dso, const char *symname)
+{
+    DSO_FUNC_TYPE ret = NULL;
+
+    if ((dso == NULL) || (symname == NULL)) {
+        DSOerr(DSO_F_DSO_BIND_FUNC, ERR_R_PASSED_NULL_PARAMETER);
+        return (NULL);
+    }
+    if (dso->meth->dso_bind_func == NULL) {
+        DSOerr(DSO_F_DSO_BIND_FUNC, DSO_R_UNSUPPORTED);
+        return (NULL);
+    }
+    if ((ret = dso->meth->dso_bind_func(dso, symname)) == NULL) {
+        DSOerr(DSO_F_DSO_BIND_FUNC, DSO_R_SYM_FAILURE);
+        return (NULL);
+    }
+    /* Success */
+    return (ret);
+}
+
+/*
+ * I don't really like these *_ctrl functions very much to be perfectly
+ * honest. For one thing, I think I have to return a negative value for any
+ * error because possible DSO_ctrl() commands may return values such as
+ * "size"s that can legitimately be zero (making the standard
+ * "if (DSO_cmd(...))" form that works almost everywhere else fail at odd
+ * times. I'd prefer "output" values to be passed by reference and the return
+ * value as success/failure like usual ... but we conform when we must... :-)
+ */
+long DSO_ctrl(DSO *dso, int cmd, long larg, void *parg)
+{
+    if (dso == NULL) {
+        DSOerr(DSO_F_DSO_CTRL, ERR_R_PASSED_NULL_PARAMETER);
+        return (-1);
+    }
+    /*
+     * We should intercept certain generic commands and only pass control to
+     * the method-specific ctrl() function if it's something we don't handle.
+     */
+    switch (cmd) {
+    case DSO_CTRL_GET_FLAGS:
+        return dso->flags;
+    case DSO_CTRL_SET_FLAGS:
+        dso->flags = (int)larg;
+        return (0);
+    case DSO_CTRL_OR_FLAGS:
+        dso->flags |= (int)larg;
+        return (0);
+    default:
+        break;
+    }
+    if ((dso->meth == NULL) || (dso->meth->dso_ctrl == NULL)) {
+        DSOerr(DSO_F_DSO_CTRL, DSO_R_UNSUPPORTED);
+        return (-1);
+    }
+    return (dso->meth->dso_ctrl(dso, cmd, larg, parg));
+}
+
+const char *DSO_get_filename(DSO *dso)
+{
+    if (dso == NULL) {
+        DSOerr(DSO_F_DSO_GET_FILENAME, ERR_R_PASSED_NULL_PARAMETER);
+        return (NULL);
+    }
+    return (dso->filename);
+}
+
+int DSO_set_filename(DSO *dso, const char *filename)
+{
+    char *copied;
+
+    if ((dso == NULL) || (filename == NULL)) {
+        DSOerr(DSO_F_DSO_SET_FILENAME, ERR_R_PASSED_NULL_PARAMETER);
+        return (0);
+    }
+    if (dso->loaded_filename) {
+        DSOerr(DSO_F_DSO_SET_FILENAME, DSO_R_DSO_ALREADY_LOADED);
+        return (0);
+    }
+    /* We'll duplicate filename */
+    copied = OPENSSL_strdup(filename);
+    if (copied == NULL) {
+        DSOerr(DSO_F_DSO_SET_FILENAME, ERR_R_MALLOC_FAILURE);
+        return (0);
+    }
+    OPENSSL_free(dso->filename);
+    dso->filename = copied;
+    return (1);
+}
+
+char *DSO_merge(DSO *dso, const char *filespec1, const char *filespec2)
+{
+    char *result = NULL;
+
+    if (dso == NULL || filespec1 == NULL) {
+        DSOerr(DSO_F_DSO_MERGE, ERR_R_PASSED_NULL_PARAMETER);
+        return (NULL);
+    }
+    if ((dso->flags & DSO_FLAG_NO_NAME_TRANSLATION) == 0) {
+        if (dso->merger != NULL)
+            result = dso->merger(dso, filespec1, filespec2);
+        else if (dso->meth->dso_merger != NULL)
+            result = dso->meth->dso_merger(dso, filespec1, filespec2);
+    }
+    return (result);
+}
+
+char *DSO_convert_filename(DSO *dso, const char *filename)
+{
+    char *result = NULL;
+
+    if (dso == NULL) {
+        DSOerr(DSO_F_DSO_CONVERT_FILENAME, ERR_R_PASSED_NULL_PARAMETER);
+        return (NULL);
+    }
+    if (filename == NULL)
+        filename = dso->filename;
+    if (filename == NULL) {
+        DSOerr(DSO_F_DSO_CONVERT_FILENAME, DSO_R_NO_FILENAME);
+        return (NULL);
+    }
+    if ((dso->flags & DSO_FLAG_NO_NAME_TRANSLATION) == 0) {
+        if (dso->name_converter != NULL)
+            result = dso->name_converter(dso, filename);
+        else if (dso->meth->dso_name_converter != NULL)
+            result = dso->meth->dso_name_converter(dso, filename);
+    }
+    if (result == NULL) {
+        result = OPENSSL_strdup(filename);
+        if (result == NULL) {
+            DSOerr(DSO_F_DSO_CONVERT_FILENAME, ERR_R_MALLOC_FAILURE);
+            return (NULL);
+        }
+    }
+    return (result);
+}
+
+int DSO_pathbyaddr(void *addr, char *path, int sz)
+{
+    DSO_METHOD *meth = default_DSO_meth;
+    if (meth == NULL)
+        meth = DSO_METHOD_openssl();
+    if (meth->pathbyaddr == NULL) {
+        DSOerr(DSO_F_DSO_PATHBYADDR, DSO_R_UNSUPPORTED);
+        return -1;
+    }
+    return (*meth->pathbyaddr) (addr, path, sz);
+}
+
+DSO *DSO_dsobyaddr(void *addr, int flags)
+{
+    DSO *ret = NULL;
+    char *filename = NULL;
+    int len = DSO_pathbyaddr(addr, NULL, 0);
+
+    if (len < 0)
+        return NULL;
+
+    filename = OPENSSL_malloc(len);
+    if (filename != NULL
+            && DSO_pathbyaddr(addr, filename, len) == len)
+        ret = DSO_load(NULL, filename, NULL, flags);
+
+    OPENSSL_free(filename);
+    return ret;
+}
+
+void *DSO_global_lookup(const char *name)
+{
+    DSO_METHOD *meth = default_DSO_meth;
+    if (meth == NULL)
+        meth = DSO_METHOD_openssl();
+    if (meth->globallookup == NULL) {
+        DSOerr(DSO_F_DSO_GLOBAL_LOOKUP, DSO_R_UNSUPPORTED);
+        return NULL;
+    }
+    return (*meth->globallookup) (name);
+}
diff --git a/crypto/dso/dso_locl.h b/crypto/dso/dso_locl.h
new file mode 100644
index 000000000..fbfad0544
--- /dev/null
+++ b/crypto/dso/dso_locl.h
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include "internal/dso.h"
+#include "internal/dso_conf.h"
+
+/**********************************************************************/
+/* The low-level handle type used to refer to a loaded shared library */
+
+struct dso_st {
+    DSO_METHOD *meth;
+    /*
+     * Standard dlopen uses a (void *). Win32 uses a HANDLE. VMS doesn't use
+     * anything but will need to cache the filename for use in the dso_bind
+     * handler. All in all, let each method control its own destiny.
+     * "Handles" and such go in a STACK.
+     */
+    STACK_OF(void) *meth_data;
+    int references;
+    int flags;
+    /*
+     * For use by applications etc ... use this for your bits'n'pieces, don't
+     * touch meth_data!
+     */
+    CRYPTO_EX_DATA ex_data;
+    /*
+     * If this callback function pointer is set to non-NULL, then it will be
+     * used in DSO_load() in place of meth->dso_name_converter. NB: This
+     * should normally set using DSO_set_name_converter().
+     */
+    DSO_NAME_CONVERTER_FUNC name_converter;
+    /*
+     * If this callback function pointer is set to non-NULL, then it will be
+     * used in DSO_load() in place of meth->dso_merger. NB: This should
+     * normally set using DSO_set_merger().
+     */
+    DSO_MERGER_FUNC merger;
+    /*
+     * This is populated with (a copy of) the platform-independent filename
+     * used for this DSO.
+     */
+    char *filename;
+    /*
+     * This is populated with (a copy of) the translated filename by which
+     * the DSO was actually loaded. It is NULL iff the DSO is not currently
+     * loaded. NB: This is here because the filename translation process may
+     * involve a callback being invoked more than once not only to convert to
+     * a platform-specific form, but also to try different filenames in the
+     * process of trying to perform a load. As such, this variable can be
+     * used to indicate (a) whether this DSO structure corresponds to a
+     * loaded library or not, and (b) the filename with which it was actually
+     * loaded.
+     */
+    char *loaded_filename;
+    CRYPTO_RWLOCK *lock;
+};
+
+struct dso_meth_st {
+    const char *name;
+    /*
+     * Loads a shared library, NB: new DSO_METHODs must ensure that a
+     * successful load populates the loaded_filename field, and likewise a
+     * successful unload OPENSSL_frees and NULLs it out.
+     */
+    int (*dso_load) (DSO *dso);
+    /* Unloads a shared library */
+    int (*dso_unload) (DSO *dso);
+    /*
+     * Binds a function - assumes a return type of DSO_FUNC_TYPE. This should
+     * be cast to the real function prototype by the caller. Platforms that
+     * don't have compatible representations for different prototypes (this
+     * is possible within ANSI C) are highly unlikely to have shared
+     * libraries at all, let alone a DSO_METHOD implemented for them.
+     */
+    DSO_FUNC_TYPE (*dso_bind_func) (DSO *dso, const char *symname);
+    /*
+     * The generic (yuck) "ctrl()" function. NB: Negative return values
+     * (rather than zero) indicate errors.
+     */
+    long (*dso_ctrl) (DSO *dso, int cmd, long larg, void *parg);
+    /*
+     * The default DSO_METHOD-specific function for converting filenames to a
+     * canonical native form.
+     */
+    DSO_NAME_CONVERTER_FUNC dso_name_converter;
+    /*
+     * The default DSO_METHOD-specific function for converting filenames to a
+     * canonical native form.
+     */
+    DSO_MERGER_FUNC dso_merger;
+    /* [De]Initialisation handlers. */
+    int (*init) (DSO *dso);
+    int (*finish) (DSO *dso);
+    /* Return pathname of the module containing location */
+    int (*pathbyaddr) (void *addr, char *path, int sz);
+    /* Perform global symbol lookup, i.e. among *all* modules */
+    void *(*globallookup) (const char *symname);
+};
diff --git a/crypto/dso/dso_openssl.c b/crypto/dso/dso_openssl.c
new file mode 100644
index 000000000..6626331e9
--- /dev/null
+++ b/crypto/dso/dso_openssl.c
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2000-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "dso_locl.h"
+
+#if !defined(DSO_VMS) && !defined(DSO_DLCFN) && !defined(DSO_DL) && !defined(DSO_WIN32) && !defined(DSO_DLFCN)
+
+static DSO_METHOD dso_meth_null = {
+    "NULL shared library method"
+};
+
+DSO_METHOD *DSO_METHOD_openssl(void)
+{
+    return &dso_meth_null;
+}
+#endif
diff --git a/crypto/dso/dso_vms.c b/crypto/dso/dso_vms.c
new file mode 100644
index 000000000..b9a98ddd1
--- /dev/null
+++ b/crypto/dso/dso_vms.c
@@ -0,0 +1,468 @@
+/*
+ * Copyright 2000-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "dso_locl.h"
+
+#ifdef OPENSSL_SYS_VMS
+
+# pragma message disable DOLLARID
+# include 
+# include 
+# include 
+# include 
+# include 
+# include 
+# include 
+# include "../vms_rms.h"
+
+/* Some compiler options may mask the declaration of "_malloc32". */
+# if __INITIAL_POINTER_SIZE && defined _ANSI_C_SOURCE
+#  if __INITIAL_POINTER_SIZE == 64
+#   pragma pointer_size save
+#   pragma pointer_size 32
+void *_malloc32(__size_t);
+#   pragma pointer_size restore
+#  endif                        /* __INITIAL_POINTER_SIZE == 64 */
+# endif                         /* __INITIAL_POINTER_SIZE && defined
+                                 * _ANSI_C_SOURCE */
+
+# pragma message disable DOLLARID
+
+static int vms_load(DSO *dso);
+static int vms_unload(DSO *dso);
+static DSO_FUNC_TYPE vms_bind_func(DSO *dso, const char *symname);
+static char *vms_name_converter(DSO *dso, const char *filename);
+static char *vms_merger(DSO *dso, const char *filespec1,
+                        const char *filespec2);
+
+static DSO_METHOD dso_meth_vms = {
+    "OpenSSL 'VMS' shared library method",
+    vms_load,
+    NULL,                       /* unload */
+    vms_bind_func,
+    NULL,                       /* ctrl */
+    vms_name_converter,
+    vms_merger,
+    NULL,                       /* init */
+    NULL,                       /* finish */
+    NULL,                       /* pathbyaddr */
+    NULL                        /* globallookup */
+};
+
+/*
+ * On VMS, the only "handle" is the file name.  LIB$FIND_IMAGE_SYMBOL depends
+ * on the reference to the file name being the same for all calls regarding
+ * one shared image, so we'll just store it in an instance of the following
+ * structure and put a pointer to that instance in the meth_data stack.
+ */
+typedef struct dso_internal_st {
+    /*
+     * This should contain the name only, no directory, no extension, nothing
+     * but a name.
+     */
+    struct dsc$descriptor_s filename_dsc;
+    char filename[NAMX_MAXRSS + 1];
+    /*
+     * This contains whatever is not in filename, if needed. Normally not
+     * defined.
+     */
+    struct dsc$descriptor_s imagename_dsc;
+    char imagename[NAMX_MAXRSS + 1];
+} DSO_VMS_INTERNAL;
+
+DSO_METHOD *DSO_METHOD_openssl(void)
+{
+    return &dso_meth_vms;
+}
+
+static int vms_load(DSO *dso)
+{
+    void *ptr = NULL;
+    /* See applicable comments in dso_dl.c */
+    char *filename = DSO_convert_filename(dso, NULL);
+
+/* Ensure 32-bit pointer for "p", and appropriate malloc() function. */
+# if __INITIAL_POINTER_SIZE == 64
+#  define DSO_MALLOC _malloc32
+#  pragma pointer_size save
+#  pragma pointer_size 32
+# else                          /* __INITIAL_POINTER_SIZE == 64 */
+#  define DSO_MALLOC OPENSSL_malloc
+# endif                         /* __INITIAL_POINTER_SIZE == 64 [else] */
+
+    DSO_VMS_INTERNAL *p = NULL;
+
+# if __INITIAL_POINTER_SIZE == 64
+#  pragma pointer_size restore
+# endif                         /* __INITIAL_POINTER_SIZE == 64 */
+
+    const char *sp1, *sp2;      /* Search result */
+    const char *ext = NULL;     /* possible extension to add */
+
+    if (filename == NULL) {
+        DSOerr(DSO_F_VMS_LOAD, DSO_R_NO_FILENAME);
+        goto err;
+    }
+
+    /*-
+     * A file specification may look like this:
+     *
+     *      node::dev:[dir-spec]name.type;ver
+     *
+     * or (for compatibility with TOPS-20):
+     *
+     *      node::dev:name.type;ver
+     *
+     * and the dir-spec uses '.' as separator.  Also, a dir-spec
+     * may consist of several parts, with mixed use of [] and <>:
+     *
+     *      [dir1.]
+     *
+     * We need to split the file specification into the name and
+     * the rest (both before and after the name itself).
+     */
+    /*
+     * Start with trying to find the end of a dir-spec, and save the position
+     * of the byte after in sp1
+     */
+    sp1 = strrchr(filename, ']');
+    sp2 = strrchr(filename, '>');
+    if (sp1 == NULL)
+        sp1 = sp2;
+    if (sp2 != NULL && sp2 > sp1)
+        sp1 = sp2;
+    if (sp1 == NULL)
+        sp1 = strrchr(filename, ':');
+    if (sp1 == NULL)
+        sp1 = filename;
+    else
+        sp1++;                  /* The byte after the found character */
+    /* Now, let's see if there's a type, and save the position in sp2 */
+    sp2 = strchr(sp1, '.');
+    /*
+     * If there is a period and the next character is a semi-colon,
+     * we need to add an extension
+     */
+    if (sp2 != NULL && sp2[1] == ';')
+        ext = ".EXE";
+    /*
+     * If we found it, that's where we'll cut.  Otherwise, look for a version
+     * number and save the position in sp2
+     */
+    if (sp2 == NULL) {
+        sp2 = strchr(sp1, ';');
+        ext = ".EXE";
+    }
+    /*
+     * If there was still nothing to find, set sp2 to point at the end of the
+     * string
+     */
+    if (sp2 == NULL)
+        sp2 = sp1 + strlen(sp1);
+
+    /* Check that we won't get buffer overflows */
+    if (sp2 - sp1 > FILENAME_MAX
+        || (sp1 - filename) + strlen(sp2) > FILENAME_MAX) {
+        DSOerr(DSO_F_VMS_LOAD, DSO_R_FILENAME_TOO_BIG);
+        goto err;
+    }
+
+    p = DSO_MALLOC(sizeof(*p));
+    if (p == NULL) {
+        DSOerr(DSO_F_VMS_LOAD, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+
+    strncpy(p->filename, sp1, sp2 - sp1);
+    p->filename[sp2 - sp1] = '\0';
+
+    strncpy(p->imagename, filename, sp1 - filename);
+    p->imagename[sp1 - filename] = '\0';
+    if (ext) {
+        strcat(p->imagename, ext);
+        if (*sp2 == '.')
+            sp2++;
+    }
+    strcat(p->imagename, sp2);
+
+    p->filename_dsc.dsc$w_length = strlen(p->filename);
+    p->filename_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
+    p->filename_dsc.dsc$b_class = DSC$K_CLASS_S;
+    p->filename_dsc.dsc$a_pointer = p->filename;
+    p->imagename_dsc.dsc$w_length = strlen(p->imagename);
+    p->imagename_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
+    p->imagename_dsc.dsc$b_class = DSC$K_CLASS_S;
+    p->imagename_dsc.dsc$a_pointer = p->imagename;
+
+    if (!sk_void_push(dso->meth_data, (char *)p)) {
+        DSOerr(DSO_F_VMS_LOAD, DSO_R_STACK_ERROR);
+        goto err;
+    }
+
+    /* Success (for now, we lie.  We actually do not know...) */
+    dso->loaded_filename = filename;
+    return (1);
+ err:
+    /* Cleanup! */
+    OPENSSL_free(p);
+    OPENSSL_free(filename);
+    return (0);
+}
+
+/*
+ * Note that this doesn't actually unload the shared image, as there is no
+ * such thing in VMS.  Next time it get loaded again, a new copy will
+ * actually be loaded.
+ */
+static int vms_unload(DSO *dso)
+{
+    DSO_VMS_INTERNAL *p;
+    if (dso == NULL) {
+        DSOerr(DSO_F_VMS_UNLOAD, ERR_R_PASSED_NULL_PARAMETER);
+        return (0);
+    }
+    if (sk_void_num(dso->meth_data) < 1)
+        return (1);
+    p = (DSO_VMS_INTERNAL *)sk_void_pop(dso->meth_data);
+    if (p == NULL) {
+        DSOerr(DSO_F_VMS_UNLOAD, DSO_R_NULL_HANDLE);
+        return (0);
+    }
+    /* Cleanup */
+    OPENSSL_free(p);
+    return (1);
+}
+
+/*
+ * We must do this in a separate function because of the way the exception
+ * handler works (it makes this function return
+ */
+static int do_find_symbol(DSO_VMS_INTERNAL *ptr,
+                          struct dsc$descriptor_s *symname_dsc, void **sym,
+                          unsigned long flags)
+{
+    /*
+     * Make sure that signals are caught and returned instead of aborting the
+     * program.  The exception handler gets unestablished automatically on
+     * return from this function.
+     */
+    lib$establish(lib$sig_to_ret);
+
+    if (ptr->imagename_dsc.dsc$w_length)
+        return lib$find_image_symbol(&ptr->filename_dsc,
+                                     symname_dsc, sym,
+                                     &ptr->imagename_dsc, flags);
+    else
+        return lib$find_image_symbol(&ptr->filename_dsc,
+                                     symname_dsc, sym, 0, flags);
+}
+
+void vms_bind_sym(DSO *dso, const char *symname, void **sym)
+{
+    DSO_VMS_INTERNAL *ptr;
+    int status;
+# ifdef LIB$M_FIS_MIXEDCASE
+    int flags = LIB$M_FIS_MIXEDCASE;
+# else
+    int flags = (1 << 4);
+# endif
+    struct dsc$descriptor_s symname_dsc;
+
+/* Arrange 32-bit pointer to (copied) string storage, if needed. */
+# if __INITIAL_POINTER_SIZE == 64
+#  define SYMNAME symname_32p
+#  pragma pointer_size save
+#  pragma pointer_size 32
+    char *symname_32p;
+#  pragma pointer_size restore
+    char symname_32[NAMX_MAXRSS + 1];
+# else                          /* __INITIAL_POINTER_SIZE == 64 */
+#  define SYMNAME ((char *) symname)
+# endif                         /* __INITIAL_POINTER_SIZE == 64 [else] */
+
+    *sym = NULL;
+
+    if ((dso == NULL) || (symname == NULL)) {
+        DSOerr(DSO_F_VMS_BIND_SYM, ERR_R_PASSED_NULL_PARAMETER);
+        return;
+    }
+# if __INITIAL_POINTER_SIZE == 64
+    /* Copy the symbol name to storage with a 32-bit pointer. */
+    symname_32p = symname_32;
+    strcpy(symname_32p, symname);
+# endif                         /* __INITIAL_POINTER_SIZE == 64 [else] */
+
+    symname_dsc.dsc$w_length = strlen(SYMNAME);
+    symname_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
+    symname_dsc.dsc$b_class = DSC$K_CLASS_S;
+    symname_dsc.dsc$a_pointer = SYMNAME;
+
+    if (sk_void_num(dso->meth_data) < 1) {
+        DSOerr(DSO_F_VMS_BIND_SYM, DSO_R_STACK_ERROR);
+        return;
+    }
+    ptr = (DSO_VMS_INTERNAL *)sk_void_value(dso->meth_data,
+                                            sk_void_num(dso->meth_data) - 1);
+    if (ptr == NULL) {
+        DSOerr(DSO_F_VMS_BIND_SYM, DSO_R_NULL_HANDLE);
+        return;
+    }
+
+    if (dso->flags & DSO_FLAG_UPCASE_SYMBOL)
+        flags = 0;
+
+    status = do_find_symbol(ptr, &symname_dsc, sym, flags);
+
+    if (!$VMS_STATUS_SUCCESS(status)) {
+        unsigned short length;
+        char errstring[257];
+        struct dsc$descriptor_s errstring_dsc;
+
+        errstring_dsc.dsc$w_length = sizeof(errstring);
+        errstring_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
+        errstring_dsc.dsc$b_class = DSC$K_CLASS_S;
+        errstring_dsc.dsc$a_pointer = errstring;
+
+        *sym = NULL;
+
+        status = sys$getmsg(status, &length, &errstring_dsc, 1, 0);
+
+        if (!$VMS_STATUS_SUCCESS(status))
+            lib$signal(status); /* This is really bad.  Abort! */
+        else {
+            errstring[length] = '\0';
+
+            DSOerr(DSO_F_VMS_BIND_SYM, DSO_R_SYM_FAILURE);
+            if (ptr->imagename_dsc.dsc$w_length)
+                ERR_add_error_data(9,
+                                   "Symbol ", symname,
+                                   " in ", ptr->filename,
+                                   " (", ptr->imagename, ")",
+                                   ": ", errstring);
+            else
+                ERR_add_error_data(6,
+                                   "Symbol ", symname,
+                                   " in ", ptr->filename, ": ", errstring);
+        }
+        return;
+    }
+    return;
+}
+
+static DSO_FUNC_TYPE vms_bind_func(DSO *dso, const char *symname)
+{
+    DSO_FUNC_TYPE sym = 0;
+    vms_bind_sym(dso, symname, (void **)&sym);
+    return sym;
+}
+
+static char *vms_merger(DSO *dso, const char *filespec1,
+                        const char *filespec2)
+{
+    int status;
+    int filespec1len, filespec2len;
+    struct FAB fab;
+    struct NAMX_STRUCT nam;
+    char esa[NAMX_MAXRSS + 1];
+    char *merged;
+
+/* Arrange 32-bit pointer to (copied) string storage, if needed. */
+# if __INITIAL_POINTER_SIZE == 64
+#  define FILESPEC1 filespec1_32p;
+#  define FILESPEC2 filespec2_32p;
+#  pragma pointer_size save
+#  pragma pointer_size 32
+    char *filespec1_32p;
+    char *filespec2_32p;
+#  pragma pointer_size restore
+    char filespec1_32[NAMX_MAXRSS + 1];
+    char filespec2_32[NAMX_MAXRSS + 1];
+# else                          /* __INITIAL_POINTER_SIZE == 64 */
+#  define FILESPEC1 ((char *) filespec1)
+#  define FILESPEC2 ((char *) filespec2)
+# endif                         /* __INITIAL_POINTER_SIZE == 64 [else] */
+
+    if (!filespec1)
+        filespec1 = "";
+    if (!filespec2)
+        filespec2 = "";
+    filespec1len = strlen(filespec1);
+    filespec2len = strlen(filespec2);
+
+# if __INITIAL_POINTER_SIZE == 64
+    /* Copy the file names to storage with a 32-bit pointer. */
+    filespec1_32p = filespec1_32;
+    filespec2_32p = filespec2_32;
+    strcpy(filespec1_32p, filespec1);
+    strcpy(filespec2_32p, filespec2);
+# endif                         /* __INITIAL_POINTER_SIZE == 64 [else] */
+
+    fab = cc$rms_fab;
+    nam = CC_RMS_NAMX;
+
+    FAB_OR_NAML(fab, nam).FAB_OR_NAML_FNA = FILESPEC1;
+    FAB_OR_NAML(fab, nam).FAB_OR_NAML_FNS = filespec1len;
+    FAB_OR_NAML(fab, nam).FAB_OR_NAML_DNA = FILESPEC2;
+    FAB_OR_NAML(fab, nam).FAB_OR_NAML_DNS = filespec2len;
+    NAMX_DNA_FNA_SET(fab)
+
+        nam.NAMX_ESA = esa;
+    nam.NAMX_ESS = NAMX_MAXRSS;
+    nam.NAMX_NOP = NAM$M_SYNCHK | NAM$M_PWD;
+    SET_NAMX_NO_SHORT_UPCASE(nam);
+
+    fab.FAB_NAMX = &nam;
+
+    status = sys$parse(&fab, 0, 0);
+
+    if (!$VMS_STATUS_SUCCESS(status)) {
+        unsigned short length;
+        char errstring[257];
+        struct dsc$descriptor_s errstring_dsc;
+
+        errstring_dsc.dsc$w_length = sizeof(errstring);
+        errstring_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
+        errstring_dsc.dsc$b_class = DSC$K_CLASS_S;
+        errstring_dsc.dsc$a_pointer = errstring;
+
+        status = sys$getmsg(status, &length, &errstring_dsc, 1, 0);
+
+        if (!$VMS_STATUS_SUCCESS(status))
+            lib$signal(status); /* This is really bad.  Abort! */
+        else {
+            errstring[length] = '\0';
+
+            DSOerr(DSO_F_VMS_MERGER, DSO_R_FAILURE);
+            ERR_add_error_data(7,
+                               "filespec \"", filespec1, "\", ",
+                               "defaults \"", filespec2, "\": ", errstring);
+        }
+        return (NULL);
+    }
+
+    merged = OPENSSL_malloc(nam.NAMX_ESL + 1);
+    if (merged == NULL)
+        goto malloc_err;
+    strncpy(merged, nam.NAMX_ESA, nam.NAMX_ESL);
+    merged[nam.NAMX_ESL] = '\0';
+    return (merged);
+ malloc_err:
+    DSOerr(DSO_F_VMS_MERGER, ERR_R_MALLOC_FAILURE);
+}
+
+static char *vms_name_converter(DSO *dso, const char *filename)
+{
+    int len = strlen(filename);
+    char *not_translated = OPENSSL_malloc(len + 1);
+    if (not_translated != NULL)
+        strcpy(not_translated, filename);
+    return (not_translated);
+}
+
+#endif                          /* OPENSSL_SYS_VMS */
diff --git a/crypto/dso/dso_win32.c b/crypto/dso/dso_win32.c
new file mode 100644
index 000000000..4a4c34abb
--- /dev/null
+++ b/crypto/dso/dso_win32.c
@@ -0,0 +1,573 @@
+/*
+ * Copyright 2000-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "dso_locl.h"
+
+#if defined(DSO_WIN32)
+
+# ifdef _WIN32_WCE
+#  if _WIN32_WCE < 300
+static FARPROC GetProcAddressA(HMODULE hModule, LPCSTR lpProcName)
+{
+    WCHAR lpProcNameW[64];
+    int i;
+
+    for (i = 0; lpProcName[i] && i < 64; i++)
+        lpProcNameW[i] = (WCHAR)lpProcName[i];
+    if (i == 64)
+        return NULL;
+    lpProcNameW[i] = 0;
+
+    return GetProcAddressW(hModule, lpProcNameW);
+}
+#  endif
+#  undef GetProcAddress
+#  define GetProcAddress GetProcAddressA
+
+static HINSTANCE LoadLibraryA(LPCSTR lpLibFileName)
+{
+    WCHAR *fnamw;
+    size_t len_0 = strlen(lpLibFileName) + 1, i;
+
+#  ifdef _MSC_VER
+    fnamw = (WCHAR *)_alloca(len_0 * sizeof(WCHAR));
+#  else
+    fnamw = (WCHAR *)alloca(len_0 * sizeof(WCHAR));
+#  endif
+    if (fnamw == NULL) {
+        SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+        return NULL;
+    }
+#  if defined(_WIN32_WCE) && _WIN32_WCE>=101
+    if (!MultiByteToWideChar(CP_ACP, 0, lpLibFileName, len_0, fnamw, len_0))
+#  endif
+        for (i = 0; i < len_0; i++)
+            fnamw[i] = (WCHAR)lpLibFileName[i];
+
+    return LoadLibraryW(fnamw);
+}
+# endif
+
+/* Part of the hack in "win32_load" ... */
+# define DSO_MAX_TRANSLATED_SIZE 256
+
+static int win32_load(DSO *dso);
+static int win32_unload(DSO *dso);
+static DSO_FUNC_TYPE win32_bind_func(DSO *dso, const char *symname);
+static char *win32_name_converter(DSO *dso, const char *filename);
+static char *win32_merger(DSO *dso, const char *filespec1,
+                          const char *filespec2);
+static void *win32_globallookup(const char *name);
+
+static const char *openssl_strnchr(const char *string, int c, size_t len);
+
+static DSO_METHOD dso_meth_win32 = {
+    "OpenSSL 'win32' shared library method",
+    win32_load,
+    win32_unload,
+    win32_bind_func,
+    NULL,                       /* ctrl */
+    win32_name_converter,
+    win32_merger,
+    NULL,                       /* init */
+    NULL,                       /* finish */
+    NULL,                       /* pathbyaddr */
+    win32_globallookup
+};
+
+DSO_METHOD *DSO_METHOD_openssl(void)
+{
+    return &dso_meth_win32;
+}
+
+/*
+ * For this DSO_METHOD, our meth_data STACK will contain; (i) a pointer to
+ * the handle (HINSTANCE) returned from LoadLibrary(), and copied.
+ */
+
+static int win32_load(DSO *dso)
+{
+    HINSTANCE h = NULL, *p = NULL;
+    /* See applicable comments from dso_dl.c */
+    char *filename = DSO_convert_filename(dso, NULL);
+
+    if (filename == NULL) {
+        DSOerr(DSO_F_WIN32_LOAD, DSO_R_NO_FILENAME);
+        goto err;
+    }
+    h = LoadLibraryA(filename);
+    if (h == NULL) {
+        DSOerr(DSO_F_WIN32_LOAD, DSO_R_LOAD_FAILED);
+        ERR_add_error_data(3, "filename(", filename, ")");
+        goto err;
+    }
+    p = OPENSSL_malloc(sizeof(*p));
+    if (p == NULL) {
+        DSOerr(DSO_F_WIN32_LOAD, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+    *p = h;
+    if (!sk_void_push(dso->meth_data, p)) {
+        DSOerr(DSO_F_WIN32_LOAD, DSO_R_STACK_ERROR);
+        goto err;
+    }
+    /* Success */
+    dso->loaded_filename = filename;
+    return (1);
+ err:
+    /* Cleanup ! */
+    OPENSSL_free(filename);
+    OPENSSL_free(p);
+    if (h != NULL)
+        FreeLibrary(h);
+    return (0);
+}
+
+static int win32_unload(DSO *dso)
+{
+    HINSTANCE *p;
+    if (dso == NULL) {
+        DSOerr(DSO_F_WIN32_UNLOAD, ERR_R_PASSED_NULL_PARAMETER);
+        return (0);
+    }
+    if (sk_void_num(dso->meth_data) < 1)
+        return (1);
+    p = sk_void_pop(dso->meth_data);
+    if (p == NULL) {
+        DSOerr(DSO_F_WIN32_UNLOAD, DSO_R_NULL_HANDLE);
+        return (0);
+    }
+    if (!FreeLibrary(*p)) {
+        DSOerr(DSO_F_WIN32_UNLOAD, DSO_R_UNLOAD_FAILED);
+        /*
+         * We should push the value back onto the stack in case of a retry.
+         */
+        sk_void_push(dso->meth_data, p);
+        return (0);
+    }
+    /* Cleanup */
+    OPENSSL_free(p);
+    return (1);
+}
+
+static DSO_FUNC_TYPE win32_bind_func(DSO *dso, const char *symname)
+{
+    HINSTANCE *ptr;
+    union {
+        void *p;
+        FARPROC f;
+    } sym;
+
+    if ((dso == NULL) || (symname == NULL)) {
+        DSOerr(DSO_F_WIN32_BIND_FUNC, ERR_R_PASSED_NULL_PARAMETER);
+        return (NULL);
+    }
+    if (sk_void_num(dso->meth_data) < 1) {
+        DSOerr(DSO_F_WIN32_BIND_FUNC, DSO_R_STACK_ERROR);
+        return (NULL);
+    }
+    ptr = sk_void_value(dso->meth_data, sk_void_num(dso->meth_data) - 1);
+    if (ptr == NULL) {
+        DSOerr(DSO_F_WIN32_BIND_FUNC, DSO_R_NULL_HANDLE);
+        return (NULL);
+    }
+    sym.f = GetProcAddress(*ptr, symname);
+    if (sym.p == NULL) {
+        DSOerr(DSO_F_WIN32_BIND_FUNC, DSO_R_SYM_FAILURE);
+        ERR_add_error_data(3, "symname(", symname, ")");
+        return (NULL);
+    }
+    return ((DSO_FUNC_TYPE)sym.f);
+}
+
+struct file_st {
+    const char *node;
+    int nodelen;
+    const char *device;
+    int devicelen;
+    const char *predir;
+    int predirlen;
+    const char *dir;
+    int dirlen;
+    const char *file;
+    int filelen;
+};
+
+static struct file_st *win32_splitter(DSO *dso, const char *filename,
+                                      int assume_last_is_dir)
+{
+    struct file_st *result = NULL;
+    enum { IN_NODE, IN_DEVICE, IN_FILE } position;
+    const char *start = filename;
+    char last;
+
+    if (!filename) {
+        DSOerr(DSO_F_WIN32_SPLITTER, DSO_R_NO_FILENAME);
+        /*
+         * goto err;
+         */
+        return (NULL);
+    }
+
+    result = OPENSSL_zalloc(sizeof(*result));
+    if (result == NULL) {
+        DSOerr(DSO_F_WIN32_SPLITTER, ERR_R_MALLOC_FAILURE);
+        return (NULL);
+    }
+
+    position = IN_DEVICE;
+
+    if ((filename[0] == '\\' && filename[1] == '\\')
+        || (filename[0] == '/' && filename[1] == '/')) {
+        position = IN_NODE;
+        filename += 2;
+        start = filename;
+        result->node = start;
+    }
+
+    do {
+        last = filename[0];
+        switch (last) {
+        case ':':
+            if (position != IN_DEVICE) {
+                DSOerr(DSO_F_WIN32_SPLITTER, DSO_R_INCORRECT_FILE_SYNTAX);
+                /*
+                 * goto err;
+                 */
+                OPENSSL_free(result);
+                return (NULL);
+            }
+            result->device = start;
+            result->devicelen = (int)(filename - start);
+            position = IN_FILE;
+            start = ++filename;
+            result->dir = start;
+            break;
+        case '\\':
+        case '/':
+            if (position == IN_NODE) {
+                result->nodelen = (int)(filename - start);
+                position = IN_FILE;
+                start = ++filename;
+                result->dir = start;
+            } else if (position == IN_DEVICE) {
+                position = IN_FILE;
+                filename++;
+                result->dir = start;
+                result->dirlen = (int)(filename - start);
+                start = filename;
+            } else {
+                filename++;
+                result->dirlen += (int)(filename - start);
+                start = filename;
+            }
+            break;
+        case '\0':
+            if (position == IN_NODE) {
+                result->nodelen = (int)(filename - start);
+            } else {
+                if (filename - start > 0) {
+                    if (assume_last_is_dir) {
+                        if (position == IN_DEVICE) {
+                            result->dir = start;
+                            result->dirlen = 0;
+                        }
+                        result->dirlen += (int)(filename - start);
+                    } else {
+                        result->file = start;
+                        result->filelen = (int)(filename - start);
+                    }
+                }
+            }
+            break;
+        default:
+            filename++;
+            break;
+        }
+    }
+    while (last);
+
+    if (!result->nodelen)
+        result->node = NULL;
+    if (!result->devicelen)
+        result->device = NULL;
+    if (!result->dirlen)
+        result->dir = NULL;
+    if (!result->filelen)
+        result->file = NULL;
+
+    return (result);
+}
+
+static char *win32_joiner(DSO *dso, const struct file_st *file_split)
+{
+    int len = 0, offset = 0;
+    char *result = NULL;
+    const char *start;
+
+    if (!file_split) {
+        DSOerr(DSO_F_WIN32_JOINER, ERR_R_PASSED_NULL_PARAMETER);
+        return (NULL);
+    }
+    if (file_split->node) {
+        len += 2 + file_split->nodelen; /* 2 for starting \\ */
+        if (file_split->predir || file_split->dir || file_split->file)
+            len++;              /* 1 for ending \ */
+    } else if (file_split->device) {
+        len += file_split->devicelen + 1; /* 1 for ending : */
+    }
+    len += file_split->predirlen;
+    if (file_split->predir && (file_split->dir || file_split->file)) {
+        len++;                  /* 1 for ending \ */
+    }
+    len += file_split->dirlen;
+    if (file_split->dir && file_split->file) {
+        len++;                  /* 1 for ending \ */
+    }
+    len += file_split->filelen;
+
+    if (!len) {
+        DSOerr(DSO_F_WIN32_JOINER, DSO_R_EMPTY_FILE_STRUCTURE);
+        return (NULL);
+    }
+
+    result = OPENSSL_malloc(len + 1);
+    if (result == NULL) {
+        DSOerr(DSO_F_WIN32_JOINER, ERR_R_MALLOC_FAILURE);
+        return (NULL);
+    }
+
+    if (file_split->node) {
+        strcpy(&result[offset], "\\\\");
+        offset += 2;
+        strncpy(&result[offset], file_split->node, file_split->nodelen);
+        offset += file_split->nodelen;
+        if (file_split->predir || file_split->dir || file_split->file) {
+            result[offset] = '\\';
+            offset++;
+        }
+    } else if (file_split->device) {
+        strncpy(&result[offset], file_split->device, file_split->devicelen);
+        offset += file_split->devicelen;
+        result[offset] = ':';
+        offset++;
+    }
+    start = file_split->predir;
+    while (file_split->predirlen > (start - file_split->predir)) {
+        const char *end = openssl_strnchr(start, '/',
+                                          file_split->predirlen - (start -
+                                                                   file_split->predir));
+        if (!end)
+            end = start
+                + file_split->predirlen - (start - file_split->predir);
+        strncpy(&result[offset], start, end - start);
+        offset += (int)(end - start);
+        result[offset] = '\\';
+        offset++;
+        start = end + 1;
+    }
+    start = file_split->dir;
+    while (file_split->dirlen > (start - file_split->dir)) {
+        const char *end = openssl_strnchr(start, '/',
+                                          file_split->dirlen - (start -
+                                                                file_split->dir));
+        if (!end)
+            end = start + file_split->dirlen - (start - file_split->dir);
+        strncpy(&result[offset], start, end - start);
+        offset += (int)(end - start);
+        result[offset] = '\\';
+        offset++;
+        start = end + 1;
+    }
+    strncpy(&result[offset], file_split->file, file_split->filelen);
+    offset += file_split->filelen;
+    result[offset] = '\0';
+    return (result);
+}
+
+static char *win32_merger(DSO *dso, const char *filespec1,
+                          const char *filespec2)
+{
+    char *merged = NULL;
+    struct file_st *filespec1_split = NULL;
+    struct file_st *filespec2_split = NULL;
+
+    if (!filespec1 && !filespec2) {
+        DSOerr(DSO_F_WIN32_MERGER, ERR_R_PASSED_NULL_PARAMETER);
+        return (NULL);
+    }
+    if (!filespec2) {
+        merged = OPENSSL_malloc(strlen(filespec1) + 1);
+        if (merged == NULL) {
+            DSOerr(DSO_F_WIN32_MERGER, ERR_R_MALLOC_FAILURE);
+            return (NULL);
+        }
+        strcpy(merged, filespec1);
+    } else if (!filespec1) {
+        merged = OPENSSL_malloc(strlen(filespec2) + 1);
+        if (merged == NULL) {
+            DSOerr(DSO_F_WIN32_MERGER, ERR_R_MALLOC_FAILURE);
+            return (NULL);
+        }
+        strcpy(merged, filespec2);
+    } else {
+        filespec1_split = win32_splitter(dso, filespec1, 0);
+        if (!filespec1_split) {
+            DSOerr(DSO_F_WIN32_MERGER, ERR_R_MALLOC_FAILURE);
+            return (NULL);
+        }
+        filespec2_split = win32_splitter(dso, filespec2, 1);
+        if (!filespec2_split) {
+            DSOerr(DSO_F_WIN32_MERGER, ERR_R_MALLOC_FAILURE);
+            OPENSSL_free(filespec1_split);
+            return (NULL);
+        }
+
+        /* Fill in into filespec1_split */
+        if (!filespec1_split->node && !filespec1_split->device) {
+            filespec1_split->node = filespec2_split->node;
+            filespec1_split->nodelen = filespec2_split->nodelen;
+            filespec1_split->device = filespec2_split->device;
+            filespec1_split->devicelen = filespec2_split->devicelen;
+        }
+        if (!filespec1_split->dir) {
+            filespec1_split->dir = filespec2_split->dir;
+            filespec1_split->dirlen = filespec2_split->dirlen;
+        } else if (filespec1_split->dir[0] != '\\'
+                   && filespec1_split->dir[0] != '/') {
+            filespec1_split->predir = filespec2_split->dir;
+            filespec1_split->predirlen = filespec2_split->dirlen;
+        }
+        if (!filespec1_split->file) {
+            filespec1_split->file = filespec2_split->file;
+            filespec1_split->filelen = filespec2_split->filelen;
+        }
+
+        merged = win32_joiner(dso, filespec1_split);
+    }
+    OPENSSL_free(filespec1_split);
+    OPENSSL_free(filespec2_split);
+    return (merged);
+}
+
+static char *win32_name_converter(DSO *dso, const char *filename)
+{
+    char *translated;
+    int len, transform;
+
+    len = strlen(filename);
+    transform = ((strstr(filename, "/") == NULL) &&
+                 (strstr(filename, "\\") == NULL) &&
+                 (strstr(filename, ":") == NULL));
+    if (transform)
+        /* We will convert this to "%s.dll" */
+        translated = OPENSSL_malloc(len + 5);
+    else
+        /* We will simply duplicate filename */
+        translated = OPENSSL_malloc(len + 1);
+    if (translated == NULL) {
+        DSOerr(DSO_F_WIN32_NAME_CONVERTER, DSO_R_NAME_TRANSLATION_FAILED);
+        return (NULL);
+    }
+    if (transform)
+        sprintf(translated, "%s.dll", filename);
+    else
+        sprintf(translated, "%s", filename);
+    return (translated);
+}
+
+static const char *openssl_strnchr(const char *string, int c, size_t len)
+{
+    size_t i;
+    const char *p;
+    for (i = 0, p = string; i < len && *p; i++, p++) {
+        if (*p == c)
+            return p;
+    }
+    return NULL;
+}
+
+# include 
+# ifdef _WIN32_WCE
+#  define DLLNAME "TOOLHELP.DLL"
+# else
+#  ifdef MODULEENTRY32
+#   undef MODULEENTRY32         /* unmask the ASCII version! */
+#  endif
+#  define DLLNAME "KERNEL32.DLL"
+# endif
+
+typedef HANDLE(WINAPI *CREATETOOLHELP32SNAPSHOT) (DWORD, DWORD);
+typedef BOOL(WINAPI *CLOSETOOLHELP32SNAPSHOT) (HANDLE);
+typedef BOOL(WINAPI *MODULE32) (HANDLE, MODULEENTRY32 *);
+
+static void *win32_globallookup(const char *name)
+{
+    HMODULE dll;
+    HANDLE hModuleSnap = INVALID_HANDLE_VALUE;
+    MODULEENTRY32 me32;
+    CREATETOOLHELP32SNAPSHOT create_snap;
+    CLOSETOOLHELP32SNAPSHOT close_snap;
+    MODULE32 module_first, module_next;
+    union {
+        void *p;
+        FARPROC f;
+    } ret = { NULL };
+
+    dll = LoadLibrary(TEXT(DLLNAME));
+    if (dll == NULL) {
+        DSOerr(DSO_F_WIN32_GLOBALLOOKUP, DSO_R_UNSUPPORTED);
+        return NULL;
+    }
+
+    create_snap = (CREATETOOLHELP32SNAPSHOT)
+        GetProcAddress(dll, "CreateToolhelp32Snapshot");
+    if (create_snap == NULL) {
+        FreeLibrary(dll);
+        DSOerr(DSO_F_WIN32_GLOBALLOOKUP, DSO_R_UNSUPPORTED);
+        return NULL;
+    }
+    /* We take the rest for granted... */
+# ifdef _WIN32_WCE
+    close_snap = (CLOSETOOLHELP32SNAPSHOT)
+        GetProcAddress(dll, "CloseToolhelp32Snapshot");
+# else
+    close_snap = (CLOSETOOLHELP32SNAPSHOT) CloseHandle;
+# endif
+    module_first = (MODULE32) GetProcAddress(dll, "Module32First");
+    module_next = (MODULE32) GetProcAddress(dll, "Module32Next");
+
+    hModuleSnap = (*create_snap) (TH32CS_SNAPMODULE, 0);
+    if (hModuleSnap == INVALID_HANDLE_VALUE) {
+        FreeLibrary(dll);
+        DSOerr(DSO_F_WIN32_GLOBALLOOKUP, DSO_R_UNSUPPORTED);
+        return NULL;
+    }
+
+    me32.dwSize = sizeof(me32);
+
+    if (!(*module_first) (hModuleSnap, &me32)) {
+        (*close_snap) (hModuleSnap);
+        FreeLibrary(dll);
+        return NULL;
+    }
+
+    do {
+        if ((ret.f = GetProcAddress(me32.hModule, name))) {
+            (*close_snap) (hModuleSnap);
+            FreeLibrary(dll);
+            return ret.p;
+        }
+    } while ((*module_next) (hModuleSnap, &me32));
+
+    (*close_snap) (hModuleSnap);
+    FreeLibrary(dll);
+    return NULL;
+}
+#endif                          /* DSO_WIN32 */
diff --git a/crypto/ebcdic.c b/crypto/ebcdic.c
new file mode 100644
index 000000000..68719538f
--- /dev/null
+++ b/crypto/ebcdic.c
@@ -0,0 +1,366 @@
+/*
+ * Copyright 2000-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+# include 
+#ifndef CHARSET_EBCDIC
+NON_EMPTY_TRANSLATION_UNIT
+#else
+
+# include 
+
+/*-
+ *      Initial Port for  Apache-1.3     by 
+ *      Adapted for       OpenSSL-0.9.4  by 
+ */
+
+# ifdef CHARSET_EBCDIC_TEST
+/*
+ * Here we're looking to test the EBCDIC code on an ASCII system so we don't do
+ * any translation in these tables at all.
+ */
+
+/* The ebcdic-to-ascii table: */
+const unsigned char os_toascii[256] = {
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+    0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+    0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
+    0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
+    0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
+    0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
+    0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
+    0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
+    0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
+    0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
+    0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
+    0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
+    0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
+    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
+    0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
+    0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
+    0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
+    0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
+    0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
+    0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
+    0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
+    0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
+    0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
+    0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
+    0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
+    0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
+    0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
+    0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
+    0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
+};
+
+/* The ascii-to-ebcdic table: */
+const unsigned char os_toebcdic[256] = {
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+    0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+    0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
+    0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
+    0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
+    0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
+    0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
+    0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
+    0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
+    0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
+    0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
+    0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
+    0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
+    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
+    0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
+    0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
+    0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
+    0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
+    0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
+    0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
+    0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
+    0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
+    0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
+    0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
+    0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
+    0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
+    0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
+    0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
+    0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
+};
+
+# elif defined(_OSD_POSIX)
+/*
+ * "BS2000 OSD" is a POSIX subsystem on a main frame. It is made by Siemens
+ * AG, Germany, for their BS2000 mainframe machines. Within the POSIX
+ * subsystem, the same character set was chosen as in "native BS2000", namely
+ * EBCDIC. (EDF04)
+ *
+ * The name "ASCII" in these routines is misleading: actually, conversion is
+ * not between EBCDIC and ASCII, but EBCDIC(EDF04) and ISO-8859.1; that means
+ * that (western european) national characters are preserved.
+ *
+ * This table is identical to the one used by rsh/rcp/ftp and other POSIX
+ * tools.
+ */
+
+/* Here's the bijective ebcdic-to-ascii table: */
+const unsigned char os_toascii[256] = {
+    /*
+     * 00
+     */ 0x00, 0x01, 0x02, 0x03, 0x85, 0x09, 0x86, 0x7f,
+    0x87, 0x8d, 0x8e, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* ................ */
+    /*
+     * 10
+     */ 0x10, 0x11, 0x12, 0x13, 0x8f, 0x0a, 0x08, 0x97,
+    0x18, 0x19, 0x9c, 0x9d, 0x1c, 0x1d, 0x1e, 0x1f, /* ................ */
+    /*
+     * 20
+     */ 0x80, 0x81, 0x82, 0x83, 0x84, 0x92, 0x17, 0x1b,
+    0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x05, 0x06, 0x07, /* ................ */
+    /*
+     * 30
+     */ 0x90, 0x91, 0x16, 0x93, 0x94, 0x95, 0x96, 0x04,
+    0x98, 0x99, 0x9a, 0x9b, 0x14, 0x15, 0x9e, 0x1a, /* ................ */
+    /*
+     * 40
+     */ 0x20, 0xa0, 0xe2, 0xe4, 0xe0, 0xe1, 0xe3, 0xe5,
+    0xe7, 0xf1, 0x60, 0x2e, 0x3c, 0x28, 0x2b, 0x7c, /* .........`.<(+| */
+    /*
+     * 50
+     */ 0x26, 0xe9, 0xea, 0xeb, 0xe8, 0xed, 0xee, 0xef,
+    0xec, 0xdf, 0x21, 0x24, 0x2a, 0x29, 0x3b, 0x9f, /* &.........!$*);. */
+    /*
+     * 60
+     */ 0x2d, 0x2f, 0xc2, 0xc4, 0xc0, 0xc1, 0xc3, 0xc5,
+    0xc7, 0xd1, 0x5e, 0x2c, 0x25, 0x5f, 0x3e, 0x3f,    /*-/........^,%_>?*/
+    /*
+     * 70
+     */ 0xf8, 0xc9, 0xca, 0xcb, 0xc8, 0xcd, 0xce, 0xcf,
+    0xcc, 0xa8, 0x3a, 0x23, 0x40, 0x27, 0x3d, 0x22, /* ..........:#@'=" */
+    /*
+     * 80
+     */ 0xd8, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
+    0x68, 0x69, 0xab, 0xbb, 0xf0, 0xfd, 0xfe, 0xb1, /* .abcdefghi...... */
+    /*
+     * 90
+     */ 0xb0, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70,
+    0x71, 0x72, 0xaa, 0xba, 0xe6, 0xb8, 0xc6, 0xa4, /* .jklmnopqr...... */
+    /*
+     * a0
+     */ 0xb5, 0xaf, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
+    0x79, 0x7a, 0xa1, 0xbf, 0xd0, 0xdd, 0xde, 0xae, /* ..stuvwxyz...... */
+    /*
+     * b0
+     */ 0xa2, 0xa3, 0xa5, 0xb7, 0xa9, 0xa7, 0xb6, 0xbc,
+    0xbd, 0xbe, 0xac, 0x5b, 0x5c, 0x5d, 0xb4, 0xd7, /* ...........[\].. */
+    /*
+     * c0
+     */ 0xf9, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
+    0x48, 0x49, 0xad, 0xf4, 0xf6, 0xf2, 0xf3, 0xf5, /* .ABCDEFGHI...... */
+    /*
+     * d0
+     */ 0xa6, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50,
+    0x51, 0x52, 0xb9, 0xfb, 0xfc, 0xdb, 0xfa, 0xff, /* .JKLMNOPQR...... */
+    /*
+     * e0
+     */ 0xd9, 0xf7, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
+    0x59, 0x5a, 0xb2, 0xd4, 0xd6, 0xd2, 0xd3, 0xd5, /* ..STUVWXYZ...... */
+    /*
+     * f0
+     */ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
+    0x38, 0x39, 0xb3, 0x7b, 0xdc, 0x7d, 0xda, 0x7e /* 0123456789.{.}.~ */
+};
+
+/* The ascii-to-ebcdic table: */
+const unsigned char os_toebcdic[256] = {
+    /*
+     * 00
+     */ 0x00, 0x01, 0x02, 0x03, 0x37, 0x2d, 0x2e, 0x2f,
+    0x16, 0x05, 0x15, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* ................ */
+    /*
+     * 10
+     */ 0x10, 0x11, 0x12, 0x13, 0x3c, 0x3d, 0x32, 0x26,
+    0x18, 0x19, 0x3f, 0x27, 0x1c, 0x1d, 0x1e, 0x1f, /* ................ */
+    /*
+     * 20
+     */ 0x40, 0x5a, 0x7f, 0x7b, 0x5b, 0x6c, 0x50, 0x7d,
+    0x4d, 0x5d, 0x5c, 0x4e, 0x6b, 0x60, 0x4b, 0x61, /* !"#$%&'()*+,-./ */
+    /*
+     * 30
+     */ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
+    0xf8, 0xf9, 0x7a, 0x5e, 0x4c, 0x7e, 0x6e, 0x6f, /* 0123456789:;<=>? */
+    /*
+     * 40
+     */ 0x7c, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
+    0xc8, 0xc9, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, /* @ABCDEFGHIJKLMNO */
+    /*
+     * 50
+     */ 0xd7, 0xd8, 0xd9, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6,
+    0xe7, 0xe8, 0xe9, 0xbb, 0xbc, 0xbd, 0x6a, 0x6d, /* PQRSTUVWXYZ[\]^_ */
+    /*
+     * 60
+     */ 0x4a, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
+    0x88, 0x89, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, /* `abcdefghijklmno */
+    /*
+     * 70
+     */ 0x97, 0x98, 0x99, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6,
+    0xa7, 0xa8, 0xa9, 0xfb, 0x4f, 0xfd, 0xff, 0x07, /* pqrstuvwxyz{|}~. */
+    /*
+     * 80
+     */ 0x20, 0x21, 0x22, 0x23, 0x24, 0x04, 0x06, 0x08,
+    0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x09, 0x0a, 0x14, /* ................ */
+    /*
+     * 90
+     */ 0x30, 0x31, 0x25, 0x33, 0x34, 0x35, 0x36, 0x17,
+    0x38, 0x39, 0x3a, 0x3b, 0x1a, 0x1b, 0x3e, 0x5f, /* ................ */
+    /*
+     * a0
+     */ 0x41, 0xaa, 0xb0, 0xb1, 0x9f, 0xb2, 0xd0, 0xb5,
+    0x79, 0xb4, 0x9a, 0x8a, 0xba, 0xca, 0xaf, 0xa1, /* ................ */
+    /*
+     * b0
+     */ 0x90, 0x8f, 0xea, 0xfa, 0xbe, 0xa0, 0xb6, 0xb3,
+    0x9d, 0xda, 0x9b, 0x8b, 0xb7, 0xb8, 0xb9, 0xab, /* ................ */
+    /*
+     * c0
+     */ 0x64, 0x65, 0x62, 0x66, 0x63, 0x67, 0x9e, 0x68,
+    0x74, 0x71, 0x72, 0x73, 0x78, 0x75, 0x76, 0x77, /* ................ */
+    /*
+     * d0
+     */ 0xac, 0x69, 0xed, 0xee, 0xeb, 0xef, 0xec, 0xbf,
+    0x80, 0xe0, 0xfe, 0xdd, 0xfc, 0xad, 0xae, 0x59, /* ................ */
+    /*
+     * e0
+     */ 0x44, 0x45, 0x42, 0x46, 0x43, 0x47, 0x9c, 0x48,
+    0x54, 0x51, 0x52, 0x53, 0x58, 0x55, 0x56, 0x57, /* ................ */
+    /*
+     * f0
+     */ 0x8c, 0x49, 0xcd, 0xce, 0xcb, 0xcf, 0xcc, 0xe1,
+    0x70, 0xc0, 0xde, 0xdb, 0xdc, 0x8d, 0x8e, 0xdf /* ................ */
+};
+
+# else /*_OSD_POSIX*/
+
+/*
+ * This code does basic character mapping for IBM's TPF and OS/390 operating
+ * systems. It is a modified version of the BS2000 table.
+ *
+ * Bijective EBCDIC (character set IBM-1047) to US-ASCII table: This table is
+ * bijective - there are no ambiguous or duplicate characters.
+ */
+const unsigned char os_toascii[256] = {
+    0x00, 0x01, 0x02, 0x03, 0x85, 0x09, 0x86, 0x7f, /* 00-0f: */
+    0x87, 0x8d, 0x8e, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* ................ */
+    0x10, 0x11, 0x12, 0x13, 0x8f, 0x0a, 0x08, 0x97, /* 10-1f: */
+    0x18, 0x19, 0x9c, 0x9d, 0x1c, 0x1d, 0x1e, 0x1f, /* ................ */
+    0x80, 0x81, 0x82, 0x83, 0x84, 0x92, 0x17, 0x1b, /* 20-2f: */
+    0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x05, 0x06, 0x07, /* ................ */
+    0x90, 0x91, 0x16, 0x93, 0x94, 0x95, 0x96, 0x04, /* 30-3f: */
+    0x98, 0x99, 0x9a, 0x9b, 0x14, 0x15, 0x9e, 0x1a, /* ................ */
+    0x20, 0xa0, 0xe2, 0xe4, 0xe0, 0xe1, 0xe3, 0xe5, /* 40-4f: */
+    0xe7, 0xf1, 0xa2, 0x2e, 0x3c, 0x28, 0x2b, 0x7c, /* ...........<(+| */
+    0x26, 0xe9, 0xea, 0xeb, 0xe8, 0xed, 0xee, 0xef, /* 50-5f: */
+    0xec, 0xdf, 0x21, 0x24, 0x2a, 0x29, 0x3b, 0x5e, /* &.........!$*);^ */
+    0x2d, 0x2f, 0xc2, 0xc4, 0xc0, 0xc1, 0xc3, 0xc5, /* 60-6f: */
+    0xc7, 0xd1, 0xa6, 0x2c, 0x25, 0x5f, 0x3e, 0x3f, /* -/.........,%_>? */
+    0xf8, 0xc9, 0xca, 0xcb, 0xc8, 0xcd, 0xce, 0xcf, /* 70-7f: */
+    0xcc, 0x60, 0x3a, 0x23, 0x40, 0x27, 0x3d, 0x22, /* .........`:#@'=" */
+    0xd8, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 80-8f: */
+    0x68, 0x69, 0xab, 0xbb, 0xf0, 0xfd, 0xfe, 0xb1, /* .abcdefghi...... */
+    0xb0, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, /* 90-9f: */
+    0x71, 0x72, 0xaa, 0xba, 0xe6, 0xb8, 0xc6, 0xa4, /* .jklmnopqr...... */
+    0xb5, 0x7e, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, /* a0-af: */
+    0x79, 0x7a, 0xa1, 0xbf, 0xd0, 0x5b, 0xde, 0xae, /* .~stuvwxyz...[.. */
+    0xac, 0xa3, 0xa5, 0xb7, 0xa9, 0xa7, 0xb6, 0xbc, /* b0-bf: */
+    0xbd, 0xbe, 0xdd, 0xa8, 0xaf, 0x5d, 0xb4, 0xd7, /* .............].. */
+    0x7b, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* c0-cf: */
+    0x48, 0x49, 0xad, 0xf4, 0xf6, 0xf2, 0xf3, 0xf5, /* {ABCDEFGHI...... */
+    0x7d, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, /* d0-df: */
+    0x51, 0x52, 0xb9, 0xfb, 0xfc, 0xf9, 0xfa, 0xff, /* }JKLMNOPQR...... */
+    0x5c, 0xf7, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, /* e0-ef: */
+    0x59, 0x5a, 0xb2, 0xd4, 0xd6, 0xd2, 0xd3, 0xd5, /* \.STUVWXYZ...... */
+    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* f0-ff: */
+    0x38, 0x39, 0xb3, 0xdb, 0xdc, 0xd9, 0xda, 0x9f /* 0123456789...... */
+};
+
+/*
+ * The US-ASCII to EBCDIC (character set IBM-1047) table: This table is
+ * bijective (no ambiguous or duplicate characters)
+ */
+const unsigned char os_toebcdic[256] = {
+    0x00, 0x01, 0x02, 0x03, 0x37, 0x2d, 0x2e, 0x2f, /* 00-0f: */
+    0x16, 0x05, 0x15, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* ................ */
+    0x10, 0x11, 0x12, 0x13, 0x3c, 0x3d, 0x32, 0x26, /* 10-1f: */
+    0x18, 0x19, 0x3f, 0x27, 0x1c, 0x1d, 0x1e, 0x1f, /* ................ */
+    0x40, 0x5a, 0x7f, 0x7b, 0x5b, 0x6c, 0x50, 0x7d, /* 20-2f: */
+    0x4d, 0x5d, 0x5c, 0x4e, 0x6b, 0x60, 0x4b, 0x61, /* !"#$%&'()*+,-./ */
+    0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* 30-3f: */
+    0xf8, 0xf9, 0x7a, 0x5e, 0x4c, 0x7e, 0x6e, 0x6f, /* 0123456789:;<=>? */
+    0x7c, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, /* 40-4f: */
+    0xc8, 0xc9, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, /* @ABCDEFGHIJKLMNO */
+    0xd7, 0xd8, 0xd9, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, /* 50-5f: */
+    0xe7, 0xe8, 0xe9, 0xad, 0xe0, 0xbd, 0x5f, 0x6d, /* PQRSTUVWXYZ[\]^_ */
+    0x79, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, /* 60-6f: */
+    0x88, 0x89, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, /* `abcdefghijklmno */
+    0x97, 0x98, 0x99, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, /* 70-7f: */
+    0xa7, 0xa8, 0xa9, 0xc0, 0x4f, 0xd0, 0xa1, 0x07, /* pqrstuvwxyz{|}~. */
+    0x20, 0x21, 0x22, 0x23, 0x24, 0x04, 0x06, 0x08, /* 80-8f: */
+    0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x09, 0x0a, 0x14, /* ................ */
+    0x30, 0x31, 0x25, 0x33, 0x34, 0x35, 0x36, 0x17, /* 90-9f: */
+    0x38, 0x39, 0x3a, 0x3b, 0x1a, 0x1b, 0x3e, 0xff, /* ................ */
+    0x41, 0xaa, 0x4a, 0xb1, 0x9f, 0xb2, 0x6a, 0xb5, /* a0-af: */
+    0xbb, 0xb4, 0x9a, 0x8a, 0xb0, 0xca, 0xaf, 0xbc, /* ................ */
+    0x90, 0x8f, 0xea, 0xfa, 0xbe, 0xa0, 0xb6, 0xb3, /* b0-bf: */
+    0x9d, 0xda, 0x9b, 0x8b, 0xb7, 0xb8, 0xb9, 0xab, /* ................ */
+    0x64, 0x65, 0x62, 0x66, 0x63, 0x67, 0x9e, 0x68, /* c0-cf: */
+    0x74, 0x71, 0x72, 0x73, 0x78, 0x75, 0x76, 0x77, /* ................ */
+    0xac, 0x69, 0xed, 0xee, 0xeb, 0xef, 0xec, 0xbf, /* d0-df: */
+    0x80, 0xfd, 0xfe, 0xfb, 0xfc, 0xba, 0xae, 0x59, /* ................ */
+    0x44, 0x45, 0x42, 0x46, 0x43, 0x47, 0x9c, 0x48, /* e0-ef: */
+    0x54, 0x51, 0x52, 0x53, 0x58, 0x55, 0x56, 0x57, /* ................ */
+    0x8c, 0x49, 0xcd, 0xce, 0xcb, 0xcf, 0xcc, 0xe1, /* f0-ff: */
+    0x70, 0xdd, 0xde, 0xdb, 0xdc, 0x8d, 0x8e, 0xdf /* ................ */
+};
+# endif/*_OSD_POSIX*/
+
+/*
+ * Translate a memory block from EBCDIC (host charset) to ASCII (net charset)
+ * dest and srce may be identical, or separate memory blocks, but should not
+ * overlap. These functions intentionally have an interface compatible to
+ * memcpy(3).
+ */
+
+void *ebcdic2ascii(void *dest, const void *srce, size_t count)
+{
+    unsigned char *udest = dest;
+    const unsigned char *usrce = srce;
+
+    while (count-- != 0) {
+        *udest++ = os_toascii[*usrce++];
+    }
+
+    return dest;
+}
+
+void *ascii2ebcdic(void *dest, const void *srce, size_t count)
+{
+    unsigned char *udest = dest;
+    const unsigned char *usrce = srce;
+
+    while (count-- != 0) {
+        *udest++ = os_toebcdic[*usrce++];
+    }
+
+    return dest;
+}
+
+#endif
diff --git a/crypto/ec/asm/ecp_nistz256-armv4.pl b/crypto/ec/asm/ecp_nistz256-armv4.pl
new file mode 100755
index 000000000..2314b7524
--- /dev/null
+++ b/crypto/ec/asm/ecp_nistz256-armv4.pl
@@ -0,0 +1,1865 @@
+#! /usr/bin/env perl
+# Copyright 2015-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+
+# ====================================================================
+# Written by Andy Polyakov  for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+#
+# ECP_NISTZ256 module for ARMv4.
+#
+# October 2014.
+#
+# Original ECP_NISTZ256 submission targeting x86_64 is detailed in
+# http://eprint.iacr.org/2013/816. In the process of adaptation
+# original .c module was made 32-bit savvy in order to make this
+# implementation possible.
+#
+#			with/without -DECP_NISTZ256_ASM
+# Cortex-A8		+53-170%
+# Cortex-A9		+76-205%
+# Cortex-A15		+100-316%
+# Snapdragon S4		+66-187%
+#
+# Ranges denote minimum and maximum improvement coefficients depending
+# on benchmark. Lower coefficients are for ECDSA sign, server-side
+# operation. Keep in mind that +200% means 3x improvement.
+
+$flavour = shift;
+if ($flavour=~/\w[\w\-]*\.\w+$/) { $output=$flavour; undef $flavour; }
+else { while (($output=shift) && ($output!~/\w[\w\-]*\.\w+$/)) {} }
+
+if ($flavour && $flavour ne "void") {
+    $0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+    ( $xlate="${dir}arm-xlate.pl" and -f $xlate ) or
+    ( $xlate="${dir}../../perlasm/arm-xlate.pl" and -f $xlate) or
+    die "can't locate arm-xlate.pl";
+
+    open STDOUT,"| \"$^X\" $xlate $flavour $output";
+} else {
+    open STDOUT,">$output";
+}
+
+$code.=<<___;
+#include "arm_arch.h"
+
+.text
+#if defined(__thumb2__)
+.syntax	unified
+.thumb
+#else
+.code	32
+#endif
+___
+########################################################################
+# Convert ecp_nistz256_table.c to layout expected by ecp_nistz_gather_w7
+#
+$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+open TABLE,") {
+	s/TOBN\(\s*(0x[0-9a-f]+),\s*(0x[0-9a-f]+)\s*\)/push @arr,hex($2),hex($1)/geo;
+}
+close TABLE;
+
+# See ecp_nistz256_table.c for explanation for why it's 64*16*37.
+# 64*16*37-1 is because $#arr returns last valid index or @arr, not
+# amount of elements.
+die "insane number of elements" if ($#arr != 64*16*37-1);
+
+$code.=<<___;
+.globl	ecp_nistz256_precomputed
+.type	ecp_nistz256_precomputed,%object
+.align	12
+ecp_nistz256_precomputed:
+___
+########################################################################
+# this conversion smashes P256_POINT_AFFINE by individual bytes with
+# 64 byte interval, similar to
+#	1111222233334444
+#	1234123412341234
+for(1..37) {
+	@tbl = splice(@arr,0,64*16);
+	for($i=0;$i<64;$i++) {
+		undef @line;
+		for($j=0;$j<64;$j++) {
+			push @line,(@tbl[$j*16+$i/4]>>(($i%4)*8))&0xff;
+		}
+		$code.=".byte\t";
+		$code.=join(',',map { sprintf "0x%02x",$_} @line);
+		$code.="\n";
+	}
+}
+$code.=<<___;
+.size	ecp_nistz256_precomputed,.-ecp_nistz256_precomputed
+.align	5
+.LRR:	@ 2^512 mod P precomputed for NIST P256 polynomial
+.long	0x00000003, 0x00000000, 0xffffffff, 0xfffffffb
+.long	0xfffffffe, 0xffffffff, 0xfffffffd, 0x00000004
+.Lone:
+.long	1,0,0,0,0,0,0,0
+.asciz	"ECP_NISTZ256 for ARMv4, CRYPTOGAMS by "
+.align	6
+___
+
+########################################################################
+# common register layout, note that $t2 is link register, so that if
+# internal subroutine uses $t2, then it has to offload lr...
+
+($r_ptr,$a_ptr,$b_ptr,$ff,$a0,$a1,$a2,$a3,$a4,$a5,$a6,$a7,$t1,$t2)=
+		map("r$_",(0..12,14));
+($t0,$t3)=($ff,$a_ptr);
+
+$code.=<<___;
+@ void	ecp_nistz256_to_mont(BN_ULONG r0[8],const BN_ULONG r1[8]);
+.globl	ecp_nistz256_to_mont
+.type	ecp_nistz256_to_mont,%function
+ecp_nistz256_to_mont:
+	adr	$b_ptr,.LRR
+	b	.Lecp_nistz256_mul_mont
+.size	ecp_nistz256_to_mont,.-ecp_nistz256_to_mont
+
+@ void	ecp_nistz256_from_mont(BN_ULONG r0[8],const BN_ULONG r1[8]);
+.globl	ecp_nistz256_from_mont
+.type	ecp_nistz256_from_mont,%function
+ecp_nistz256_from_mont:
+	adr	$b_ptr,.Lone
+	b	.Lecp_nistz256_mul_mont
+.size	ecp_nistz256_from_mont,.-ecp_nistz256_from_mont
+
+@ void	ecp_nistz256_mul_by_2(BN_ULONG r0[8],const BN_ULONG r1[8]);
+.globl	ecp_nistz256_mul_by_2
+.type	ecp_nistz256_mul_by_2,%function
+.align	4
+ecp_nistz256_mul_by_2:
+	stmdb	sp!,{r4-r12,lr}
+	bl	__ecp_nistz256_mul_by_2
+#if __ARM_ARCH__>=5 || !defined(__thumb__)
+	ldmia	sp!,{r4-r12,pc}
+#else
+	ldmia	sp!,{r4-r12,lr}
+	bx	lr			@ interoperable with Thumb ISA:-)
+#endif
+.size	ecp_nistz256_mul_by_2,.-ecp_nistz256_mul_by_2
+
+.type	__ecp_nistz256_mul_by_2,%function
+.align	4
+__ecp_nistz256_mul_by_2:
+	ldr	$a0,[$a_ptr,#0]
+	ldr	$a1,[$a_ptr,#4]
+	ldr	$a2,[$a_ptr,#8]
+	adds	$a0,$a0,$a0		@ a[0:7]+=a[0:7], i.e. add with itself
+	ldr	$a3,[$a_ptr,#12]
+	adcs	$a1,$a1,$a1
+	ldr	$a4,[$a_ptr,#16]
+	adcs	$a2,$a2,$a2
+	ldr	$a5,[$a_ptr,#20]
+	adcs	$a3,$a3,$a3
+	ldr	$a6,[$a_ptr,#24]
+	adcs	$a4,$a4,$a4
+	ldr	$a7,[$a_ptr,#28]
+	adcs	$a5,$a5,$a5
+	adcs	$a6,$a6,$a6
+	mov	$ff,#0
+	adcs	$a7,$a7,$a7
+	adc	$ff,$ff,#0
+
+	b	.Lreduce_by_sub
+.size	__ecp_nistz256_mul_by_2,.-__ecp_nistz256_mul_by_2
+
+@ void	ecp_nistz256_add(BN_ULONG r0[8],const BN_ULONG r1[8],
+@					const BN_ULONG r2[8]);
+.globl	ecp_nistz256_add
+.type	ecp_nistz256_add,%function
+.align	4
+ecp_nistz256_add:
+	stmdb	sp!,{r4-r12,lr}
+	bl	__ecp_nistz256_add
+#if __ARM_ARCH__>=5 || !defined(__thumb__)
+	ldmia	sp!,{r4-r12,pc}
+#else
+	ldmia	sp!,{r4-r12,lr}
+	bx	lr			@ interoperable with Thumb ISA:-)
+#endif
+.size	ecp_nistz256_add,.-ecp_nistz256_add
+
+.type	__ecp_nistz256_add,%function
+.align	4
+__ecp_nistz256_add:
+	str	lr,[sp,#-4]!		@ push lr
+
+	ldr	$a0,[$a_ptr,#0]
+	ldr	$a1,[$a_ptr,#4]
+	ldr	$a2,[$a_ptr,#8]
+	ldr	$a3,[$a_ptr,#12]
+	ldr	$a4,[$a_ptr,#16]
+	 ldr	$t0,[$b_ptr,#0]
+	ldr	$a5,[$a_ptr,#20]
+	 ldr	$t1,[$b_ptr,#4]
+	ldr	$a6,[$a_ptr,#24]
+	 ldr	$t2,[$b_ptr,#8]
+	ldr	$a7,[$a_ptr,#28]
+	 ldr	$t3,[$b_ptr,#12]
+	adds	$a0,$a0,$t0
+	 ldr	$t0,[$b_ptr,#16]
+	adcs	$a1,$a1,$t1
+	 ldr	$t1,[$b_ptr,#20]
+	adcs	$a2,$a2,$t2
+	 ldr	$t2,[$b_ptr,#24]
+	adcs	$a3,$a3,$t3
+	 ldr	$t3,[$b_ptr,#28]
+	adcs	$a4,$a4,$t0
+	adcs	$a5,$a5,$t1
+	adcs	$a6,$a6,$t2
+	mov	$ff,#0
+	adcs	$a7,$a7,$t3
+	adc	$ff,$ff,#0
+	ldr	lr,[sp],#4		@ pop lr
+
+.Lreduce_by_sub:
+
+	@ if a+b >= modulus, subtract modulus.
+	@
+	@ But since comparison implies subtraction, we subtract
+	@ modulus and then add it back if subraction borrowed.
+
+	subs	$a0,$a0,#-1
+	sbcs	$a1,$a1,#-1
+	sbcs	$a2,$a2,#-1
+	sbcs	$a3,$a3,#0
+	sbcs	$a4,$a4,#0
+	sbcs	$a5,$a5,#0
+	sbcs	$a6,$a6,#1
+	sbcs	$a7,$a7,#-1
+	sbc	$ff,$ff,#0
+
+	@ Note that because mod has special form, i.e. consists of
+	@ 0xffffffff, 1 and 0s, we can conditionally synthesize it by
+	@ using value of borrow as a whole or extracting single bit.
+	@ Follow $ff register...
+
+	adds	$a0,$a0,$ff		@ add synthesized modulus
+	adcs	$a1,$a1,$ff
+	str	$a0,[$r_ptr,#0]
+	adcs	$a2,$a2,$ff
+	str	$a1,[$r_ptr,#4]
+	adcs	$a3,$a3,#0
+	str	$a2,[$r_ptr,#8]
+	adcs	$a4,$a4,#0
+	str	$a3,[$r_ptr,#12]
+	adcs	$a5,$a5,#0
+	str	$a4,[$r_ptr,#16]
+	adcs	$a6,$a6,$ff,lsr#31
+	str	$a5,[$r_ptr,#20]
+	adcs	$a7,$a7,$ff
+	str	$a6,[$r_ptr,#24]
+	str	$a7,[$r_ptr,#28]
+
+	mov	pc,lr
+.size	__ecp_nistz256_add,.-__ecp_nistz256_add
+
+@ void	ecp_nistz256_mul_by_3(BN_ULONG r0[8],const BN_ULONG r1[8]);
+.globl	ecp_nistz256_mul_by_3
+.type	ecp_nistz256_mul_by_3,%function
+.align	4
+ecp_nistz256_mul_by_3:
+	stmdb	sp!,{r4-r12,lr}
+	bl	__ecp_nistz256_mul_by_3
+#if __ARM_ARCH__>=5 || !defined(__thumb__)
+	ldmia	sp!,{r4-r12,pc}
+#else
+	ldmia	sp!,{r4-r12,lr}
+	bx	lr			@ interoperable with Thumb ISA:-)
+#endif
+.size	ecp_nistz256_mul_by_3,.-ecp_nistz256_mul_by_3
+
+.type	__ecp_nistz256_mul_by_3,%function
+.align	4
+__ecp_nistz256_mul_by_3:
+	str	lr,[sp,#-4]!		@ push lr
+
+	@ As multiplication by 3 is performed as 2*n+n, below are inline
+	@ copies of __ecp_nistz256_mul_by_2 and __ecp_nistz256_add, see
+	@ corresponding subroutines for details.
+
+	ldr	$a0,[$a_ptr,#0]
+	ldr	$a1,[$a_ptr,#4]
+	ldr	$a2,[$a_ptr,#8]
+	adds	$a0,$a0,$a0		@ a[0:7]+=a[0:7]
+	ldr	$a3,[$a_ptr,#12]
+	adcs	$a1,$a1,$a1
+	ldr	$a4,[$a_ptr,#16]
+	adcs	$a2,$a2,$a2
+	ldr	$a5,[$a_ptr,#20]
+	adcs	$a3,$a3,$a3
+	ldr	$a6,[$a_ptr,#24]
+	adcs	$a4,$a4,$a4
+	ldr	$a7,[$a_ptr,#28]
+	adcs	$a5,$a5,$a5
+	adcs	$a6,$a6,$a6
+	mov	$ff,#0
+	adcs	$a7,$a7,$a7
+	adc	$ff,$ff,#0
+
+	subs	$a0,$a0,#-1		@ .Lreduce_by_sub but without stores
+	sbcs	$a1,$a1,#-1
+	sbcs	$a2,$a2,#-1
+	sbcs	$a3,$a3,#0
+	sbcs	$a4,$a4,#0
+	sbcs	$a5,$a5,#0
+	sbcs	$a6,$a6,#1
+	sbcs	$a7,$a7,#-1
+	sbc	$ff,$ff,#0
+
+	adds	$a0,$a0,$ff		@ add synthesized modulus
+	adcs	$a1,$a1,$ff
+	adcs	$a2,$a2,$ff
+	adcs	$a3,$a3,#0
+	adcs	$a4,$a4,#0
+	 ldr	$b_ptr,[$a_ptr,#0]
+	adcs	$a5,$a5,#0
+	 ldr	$t1,[$a_ptr,#4]
+	adcs	$a6,$a6,$ff,lsr#31
+	 ldr	$t2,[$a_ptr,#8]
+	adc	$a7,$a7,$ff
+
+	ldr	$t0,[$a_ptr,#12]
+	adds	$a0,$a0,$b_ptr		@ 2*a[0:7]+=a[0:7]
+	ldr	$b_ptr,[$a_ptr,#16]
+	adcs	$a1,$a1,$t1
+	ldr	$t1,[$a_ptr,#20]
+	adcs	$a2,$a2,$t2
+	ldr	$t2,[$a_ptr,#24]
+	adcs	$a3,$a3,$t0
+	ldr	$t3,[$a_ptr,#28]
+	adcs	$a4,$a4,$b_ptr
+	adcs	$a5,$a5,$t1
+	adcs	$a6,$a6,$t2
+	mov	$ff,#0
+	adcs	$a7,$a7,$t3
+	adc	$ff,$ff,#0
+	ldr	lr,[sp],#4		@ pop lr
+
+	b	.Lreduce_by_sub
+.size	ecp_nistz256_mul_by_3,.-ecp_nistz256_mul_by_3
+
+@ void	ecp_nistz256_div_by_2(BN_ULONG r0[8],const BN_ULONG r1[8]);
+.globl	ecp_nistz256_div_by_2
+.type	ecp_nistz256_div_by_2,%function
+.align	4
+ecp_nistz256_div_by_2:
+	stmdb	sp!,{r4-r12,lr}
+	bl	__ecp_nistz256_div_by_2
+#if __ARM_ARCH__>=5 || !defined(__thumb__)
+	ldmia	sp!,{r4-r12,pc}
+#else
+	ldmia	sp!,{r4-r12,lr}
+	bx	lr			@ interoperable with Thumb ISA:-)
+#endif
+.size	ecp_nistz256_div_by_2,.-ecp_nistz256_div_by_2
+
+.type	__ecp_nistz256_div_by_2,%function
+.align	4
+__ecp_nistz256_div_by_2:
+	@ ret = (a is odd ? a+mod : a) >> 1
+
+	ldr	$a0,[$a_ptr,#0]
+	ldr	$a1,[$a_ptr,#4]
+	ldr	$a2,[$a_ptr,#8]
+	mov	$ff,$a0,lsl#31		@ place least significant bit to most
+					@ significant position, now arithmetic
+					@ right shift by 31 will produce -1 or
+					@ 0, while logical right shift 1 or 0,
+					@ this is how modulus is conditionally
+					@ synthesized in this case...
+	ldr	$a3,[$a_ptr,#12]
+	adds	$a0,$a0,$ff,asr#31
+	ldr	$a4,[$a_ptr,#16]
+	adcs	$a1,$a1,$ff,asr#31
+	ldr	$a5,[$a_ptr,#20]
+	adcs	$a2,$a2,$ff,asr#31
+	ldr	$a6,[$a_ptr,#24]
+	adcs	$a3,$a3,#0
+	ldr	$a7,[$a_ptr,#28]
+	adcs	$a4,$a4,#0
+	 mov	$a0,$a0,lsr#1		@ a[0:7]>>=1, we can start early
+					@ because it doesn't affect flags
+	adcs	$a5,$a5,#0
+	 orr	$a0,$a0,$a1,lsl#31
+	adcs	$a6,$a6,$ff,lsr#31
+	mov	$b_ptr,#0
+	adcs	$a7,$a7,$ff,asr#31
+	 mov	$a1,$a1,lsr#1
+	adc	$b_ptr,$b_ptr,#0	@ top-most carry bit from addition
+
+	orr	$a1,$a1,$a2,lsl#31
+	mov	$a2,$a2,lsr#1
+	str	$a0,[$r_ptr,#0]
+	orr	$a2,$a2,$a3,lsl#31
+	mov	$a3,$a3,lsr#1
+	str	$a1,[$r_ptr,#4]
+	orr	$a3,$a3,$a4,lsl#31
+	mov	$a4,$a4,lsr#1
+	str	$a2,[$r_ptr,#8]
+	orr	$a4,$a4,$a5,lsl#31
+	mov	$a5,$a5,lsr#1
+	str	$a3,[$r_ptr,#12]
+	orr	$a5,$a5,$a6,lsl#31
+	mov	$a6,$a6,lsr#1
+	str	$a4,[$r_ptr,#16]
+	orr	$a6,$a6,$a7,lsl#31
+	mov	$a7,$a7,lsr#1
+	str	$a5,[$r_ptr,#20]
+	orr	$a7,$a7,$b_ptr,lsl#31	@ don't forget the top-most carry bit
+	str	$a6,[$r_ptr,#24]
+	str	$a7,[$r_ptr,#28]
+
+	mov	pc,lr
+.size	__ecp_nistz256_div_by_2,.-__ecp_nistz256_div_by_2
+
+@ void	ecp_nistz256_sub(BN_ULONG r0[8],const BN_ULONG r1[8],
+@				        const BN_ULONG r2[8]);
+.globl	ecp_nistz256_sub
+.type	ecp_nistz256_sub,%function
+.align	4
+ecp_nistz256_sub:
+	stmdb	sp!,{r4-r12,lr}
+	bl	__ecp_nistz256_sub
+#if __ARM_ARCH__>=5 || !defined(__thumb__)
+	ldmia	sp!,{r4-r12,pc}
+#else
+	ldmia	sp!,{r4-r12,lr}
+	bx	lr			@ interoperable with Thumb ISA:-)
+#endif
+.size	ecp_nistz256_sub,.-ecp_nistz256_sub
+
+.type	__ecp_nistz256_sub,%function
+.align	4
+__ecp_nistz256_sub:
+	str	lr,[sp,#-4]!		@ push lr
+
+	ldr	$a0,[$a_ptr,#0]
+	ldr	$a1,[$a_ptr,#4]
+	ldr	$a2,[$a_ptr,#8]
+	ldr	$a3,[$a_ptr,#12]
+	ldr	$a4,[$a_ptr,#16]
+	 ldr	$t0,[$b_ptr,#0]
+	ldr	$a5,[$a_ptr,#20]
+	 ldr	$t1,[$b_ptr,#4]
+	ldr	$a6,[$a_ptr,#24]
+	 ldr	$t2,[$b_ptr,#8]
+	ldr	$a7,[$a_ptr,#28]
+	 ldr	$t3,[$b_ptr,#12]
+	subs	$a0,$a0,$t0
+	 ldr	$t0,[$b_ptr,#16]
+	sbcs	$a1,$a1,$t1
+	 ldr	$t1,[$b_ptr,#20]
+	sbcs	$a2,$a2,$t2
+	 ldr	$t2,[$b_ptr,#24]
+	sbcs	$a3,$a3,$t3
+	 ldr	$t3,[$b_ptr,#28]
+	sbcs	$a4,$a4,$t0
+	sbcs	$a5,$a5,$t1
+	sbcs	$a6,$a6,$t2
+	sbcs	$a7,$a7,$t3
+	sbc	$ff,$ff,$ff		@ broadcast borrow bit
+	ldr	lr,[sp],#4		@ pop lr
+
+.Lreduce_by_add:
+
+	@ if a-b borrows, add modulus.
+	@
+	@ Note that because mod has special form, i.e. consists of
+	@ 0xffffffff, 1 and 0s, we can conditionally synthesize it by
+	@ broadcasting borrow bit to a register, $ff, and using it as
+	@ a whole or extracting single bit.
+
+	adds	$a0,$a0,$ff		@ add synthesized modulus
+	adcs	$a1,$a1,$ff
+	str	$a0,[$r_ptr,#0]
+	adcs	$a2,$a2,$ff
+	str	$a1,[$r_ptr,#4]
+	adcs	$a3,$a3,#0
+	str	$a2,[$r_ptr,#8]
+	adcs	$a4,$a4,#0
+	str	$a3,[$r_ptr,#12]
+	adcs	$a5,$a5,#0
+	str	$a4,[$r_ptr,#16]
+	adcs	$a6,$a6,$ff,lsr#31
+	str	$a5,[$r_ptr,#20]
+	adcs	$a7,$a7,$ff
+	str	$a6,[$r_ptr,#24]
+	str	$a7,[$r_ptr,#28]
+
+	mov	pc,lr
+.size	__ecp_nistz256_sub,.-__ecp_nistz256_sub
+
+@ void	ecp_nistz256_neg(BN_ULONG r0[8],const BN_ULONG r1[8]);
+.globl	ecp_nistz256_neg
+.type	ecp_nistz256_neg,%function
+.align	4
+ecp_nistz256_neg:
+	stmdb	sp!,{r4-r12,lr}
+	bl	__ecp_nistz256_neg
+#if __ARM_ARCH__>=5 || !defined(__thumb__)
+	ldmia	sp!,{r4-r12,pc}
+#else
+	ldmia	sp!,{r4-r12,lr}
+	bx	lr			@ interoperable with Thumb ISA:-)
+#endif
+.size	ecp_nistz256_neg,.-ecp_nistz256_neg
+
+.type	__ecp_nistz256_neg,%function
+.align	4
+__ecp_nistz256_neg:
+	ldr	$a0,[$a_ptr,#0]
+	eor	$ff,$ff,$ff
+	ldr	$a1,[$a_ptr,#4]
+	ldr	$a2,[$a_ptr,#8]
+	subs	$a0,$ff,$a0
+	ldr	$a3,[$a_ptr,#12]
+	sbcs	$a1,$ff,$a1
+	ldr	$a4,[$a_ptr,#16]
+	sbcs	$a2,$ff,$a2
+	ldr	$a5,[$a_ptr,#20]
+	sbcs	$a3,$ff,$a3
+	ldr	$a6,[$a_ptr,#24]
+	sbcs	$a4,$ff,$a4
+	ldr	$a7,[$a_ptr,#28]
+	sbcs	$a5,$ff,$a5
+	sbcs	$a6,$ff,$a6
+	sbcs	$a7,$ff,$a7
+	sbc	$ff,$ff,$ff
+
+	b	.Lreduce_by_add
+.size	__ecp_nistz256_neg,.-__ecp_nistz256_neg
+___
+{
+my @acc=map("r$_",(3..11));
+my ($t0,$t1,$bj,$t2,$t3)=map("r$_",(0,1,2,12,14));
+
+$code.=<<___;
+@ void	ecp_nistz256_sqr_mont(BN_ULONG r0[8],const BN_ULONG r1[8]);
+.globl	ecp_nistz256_sqr_mont
+.type	ecp_nistz256_sqr_mont,%function
+.align	4
+ecp_nistz256_sqr_mont:
+	mov	$b_ptr,$a_ptr
+	b	.Lecp_nistz256_mul_mont
+.size	ecp_nistz256_sqr_mont,.-ecp_nistz256_sqr_mont
+
+@ void	ecp_nistz256_mul_mont(BN_ULONG r0[8],const BN_ULONG r1[8],
+@					     const BN_ULONG r2[8]);
+.globl	ecp_nistz256_mul_mont
+.type	ecp_nistz256_mul_mont,%function
+.align	4
+ecp_nistz256_mul_mont:
+.Lecp_nistz256_mul_mont:
+	stmdb	sp!,{r4-r12,lr}
+	bl	__ecp_nistz256_mul_mont
+#if __ARM_ARCH__>=5 || !defined(__thumb__)
+	ldmia	sp!,{r4-r12,pc}
+#else
+	ldmia	sp!,{r4-r12,lr}
+	bx	lr			@ interoperable with Thumb ISA:-)
+#endif
+.size	ecp_nistz256_mul_mont,.-ecp_nistz256_mul_mont
+
+.type	__ecp_nistz256_mul_mont,%function
+.align	4
+__ecp_nistz256_mul_mont:
+	stmdb	sp!,{r0-r2,lr}			@ make a copy of arguments too
+
+	ldr	$bj,[$b_ptr,#0]			@ b[0]
+	ldmia	$a_ptr,{@acc[1]-@acc[8]}
+
+	umull	@acc[0],$t3,@acc[1],$bj		@ r[0]=a[0]*b[0]
+	stmdb	sp!,{$acc[1]-@acc[8]}		@ copy a[0-7] to stack, so
+						@ that it can be addressed
+						@ without spending register
+						@ on address
+	umull	@acc[1],$t0,@acc[2],$bj		@ r[1]=a[1]*b[0]
+	umull	@acc[2],$t1,@acc[3],$bj
+	adds	@acc[1],@acc[1],$t3		@ accumulate high part of mult
+	umull	@acc[3],$t2,@acc[4],$bj
+	adcs	@acc[2],@acc[2],$t0
+	umull	@acc[4],$t3,@acc[5],$bj
+	adcs	@acc[3],@acc[3],$t1
+	umull	@acc[5],$t0,@acc[6],$bj
+	adcs	@acc[4],@acc[4],$t2
+	umull	@acc[6],$t1,@acc[7],$bj
+	adcs	@acc[5],@acc[5],$t3
+	umull	@acc[7],$t2,@acc[8],$bj
+	adcs	@acc[6],@acc[6],$t0
+	adcs	@acc[7],@acc[7],$t1
+	eor	$t3,$t3,$t3			@ first overflow bit is zero
+	adc	@acc[8],$t2,#0
+___
+for(my $i=1;$i<8;$i++) {
+my $t4=@acc[0];
+
+	# Reduction iteration is normally performed by accumulating
+	# result of multiplication of modulus by "magic" digit [and
+	# omitting least significant word, which is guaranteed to
+	# be 0], but thanks to special form of modulus and "magic"
+	# digit being equal to least significant word, it can be
+	# performed with additions and subtractions alone. Indeed:
+	#
+	#        ffff.0001.0000.0000.0000.ffff.ffff.ffff
+	# *                                         abcd
+	# + xxxx.xxxx.xxxx.xxxx.xxxx.xxxx.xxxx.xxxx.abcd
+	#
+	# Now observing that ff..ff*x = (2^n-1)*x = 2^n*x-x, we
+	# rewrite above as:
+	#
+	#   xxxx.xxxx.xxxx.xxxx.xxxx.xxxx.xxxx.xxxx.abcd
+	# + abcd.0000.abcd.0000.0000.abcd.0000.0000.0000
+	# -      abcd.0000.0000.0000.0000.0000.0000.abcd
+	#
+	# or marking redundant operations:
+	#
+	#   xxxx.xxxx.xxxx.xxxx.xxxx.xxxx.xxxx.xxxx.----
+	# + abcd.0000.abcd.0000.0000.abcd.----.----.----
+	# -      abcd.----.----.----.----.----.----.----
+
+$code.=<<___;
+	@ multiplication-less reduction $i
+	adds	@acc[3],@acc[3],@acc[0]		@ r[3]+=r[0]
+	 ldr	$bj,[sp,#40]			@ restore b_ptr
+	adcs	@acc[4],@acc[4],#0		@ r[4]+=0
+	adcs	@acc[5],@acc[5],#0		@ r[5]+=0
+	adcs	@acc[6],@acc[6],@acc[0]		@ r[6]+=r[0]
+	 ldr	$t1,[sp,#0]			@ load a[0]
+	adcs	@acc[7],@acc[7],#0		@ r[7]+=0
+	 ldr	$bj,[$bj,#4*$i]			@ load b[i]
+	adcs	@acc[8],@acc[8],@acc[0]		@ r[8]+=r[0]
+	 eor	$t0,$t0,$t0
+	adc	$t3,$t3,#0			@ overflow bit
+	subs	@acc[7],@acc[7],@acc[0]		@ r[7]-=r[0]
+	 ldr	$t2,[sp,#4]			@ a[1]
+	sbcs	@acc[8],@acc[8],#0		@ r[8]-=0
+	 umlal	@acc[1],$t0,$t1,$bj		@ "r[0]"+=a[0]*b[i]
+	 eor	$t1,$t1,$t1
+	sbc	@acc[0],$t3,#0			@ overflow bit, keep in mind
+						@ that netto result is
+						@ addition of a value which
+						@ makes underflow impossible
+
+	ldr	$t3,[sp,#8]			@ a[2]
+	umlal	@acc[2],$t1,$t2,$bj		@ "r[1]"+=a[1]*b[i]
+	 str	@acc[0],[sp,#36]		@ temporarily offload overflow
+	eor	$t2,$t2,$t2
+	ldr	$t4,[sp,#12]			@ a[3], $t4 is alias @acc[0]
+	umlal	@acc[3],$t2,$t3,$bj		@ "r[2]"+=a[2]*b[i]
+	eor	$t3,$t3,$t3
+	adds	@acc[2],@acc[2],$t0		@ accumulate high part of mult
+	ldr	$t0,[sp,#16]			@ a[4]
+	umlal	@acc[4],$t3,$t4,$bj		@ "r[3]"+=a[3]*b[i]
+	eor	$t4,$t4,$t4
+	adcs	@acc[3],@acc[3],$t1
+	ldr	$t1,[sp,#20]			@ a[5]
+	umlal	@acc[5],$t4,$t0,$bj		@ "r[4]"+=a[4]*b[i]
+	eor	$t0,$t0,$t0
+	adcs	@acc[4],@acc[4],$t2
+	ldr	$t2,[sp,#24]			@ a[6]
+	umlal	@acc[6],$t0,$t1,$bj		@ "r[5]"+=a[5]*b[i]
+	eor	$t1,$t1,$t1
+	adcs	@acc[5],@acc[5],$t3
+	ldr	$t3,[sp,#28]			@ a[7]
+	umlal	@acc[7],$t1,$t2,$bj		@ "r[6]"+=a[6]*b[i]
+	eor	$t2,$t2,$t2
+	adcs	@acc[6],@acc[6],$t4
+	 ldr	@acc[0],[sp,#36]		@ restore overflow bit
+	umlal	@acc[8],$t2,$t3,$bj		@ "r[7]"+=a[7]*b[i]
+	eor	$t3,$t3,$t3
+	adcs	@acc[7],@acc[7],$t0
+	adcs	@acc[8],@acc[8],$t1
+	adcs	@acc[0],$acc[0],$t2
+	adc	$t3,$t3,#0			@ new overflow bit
+___
+	push(@acc,shift(@acc));			# rotate registers, so that
+						# "r[i]" becomes r[i]
+}
+$code.=<<___;
+	@ last multiplication-less reduction
+	adds	@acc[3],@acc[3],@acc[0]
+	ldr	$r_ptr,[sp,#32]			@ restore r_ptr
+	adcs	@acc[4],@acc[4],#0
+	adcs	@acc[5],@acc[5],#0
+	adcs	@acc[6],@acc[6],@acc[0]
+	adcs	@acc[7],@acc[7],#0
+	adcs	@acc[8],@acc[8],@acc[0]
+	adc	$t3,$t3,#0
+	subs	@acc[7],@acc[7],@acc[0]
+	sbcs	@acc[8],@acc[8],#0
+	sbc	@acc[0],$t3,#0			@ overflow bit
+
+	@ Final step is "if result > mod, subtract mod", but we do it
+	@ "other way around", namely subtract modulus from result
+	@ and if it borrowed, add modulus back.
+
+	adds	@acc[1],@acc[1],#1		@ subs	@acc[1],@acc[1],#-1
+	adcs	@acc[2],@acc[2],#0		@ sbcs	@acc[2],@acc[2],#-1
+	adcs	@acc[3],@acc[3],#0		@ sbcs	@acc[3],@acc[3],#-1
+	sbcs	@acc[4],@acc[4],#0
+	sbcs	@acc[5],@acc[5],#0
+	sbcs	@acc[6],@acc[6],#0
+	sbcs	@acc[7],@acc[7],#1
+	adcs	@acc[8],@acc[8],#0		@ sbcs	@acc[8],@acc[8],#-1
+	ldr	lr,[sp,#44]			@ restore lr
+	sbc	@acc[0],@acc[0],#0		@ broadcast borrow bit
+	add	sp,sp,#48
+
+	@ Note that because mod has special form, i.e. consists of
+	@ 0xffffffff, 1 and 0s, we can conditionally synthesize it by
+	@ broadcasting borrow bit to a register, @acc[0], and using it as
+	@ a whole or extracting single bit.
+
+	adds	@acc[1],@acc[1],@acc[0]		@ add modulus or zero
+	adcs	@acc[2],@acc[2],@acc[0]
+	str	@acc[1],[$r_ptr,#0]
+	adcs	@acc[3],@acc[3],@acc[0]
+	str	@acc[2],[$r_ptr,#4]
+	adcs	@acc[4],@acc[4],#0
+	str	@acc[3],[$r_ptr,#8]
+	adcs	@acc[5],@acc[5],#0
+	str	@acc[4],[$r_ptr,#12]
+	adcs	@acc[6],@acc[6],#0
+	str	@acc[5],[$r_ptr,#16]
+	adcs	@acc[7],@acc[7],@acc[0],lsr#31
+	str	@acc[6],[$r_ptr,#20]
+	adc	@acc[8],@acc[8],@acc[0]
+	str	@acc[7],[$r_ptr,#24]
+	str	@acc[8],[$r_ptr,#28]
+
+	mov	pc,lr
+.size	__ecp_nistz256_mul_mont,.-__ecp_nistz256_mul_mont
+___
+}
+
+{
+my ($out,$inp,$index,$mask)=map("r$_",(0..3));
+$code.=<<___;
+@ void	ecp_nistz256_scatter_w5(void *r0,const P256_POINT *r1,
+@					 int r2);
+.globl	ecp_nistz256_scatter_w5
+.type	ecp_nistz256_scatter_w5,%function
+.align	5
+ecp_nistz256_scatter_w5:
+	stmdb	sp!,{r4-r11}
+
+	add	$out,$out,$index,lsl#2
+
+	ldmia	$inp!,{r4-r11}		@ X
+	str	r4,[$out,#64*0-4]
+	str	r5,[$out,#64*1-4]
+	str	r6,[$out,#64*2-4]
+	str	r7,[$out,#64*3-4]
+	str	r8,[$out,#64*4-4]
+	str	r9,[$out,#64*5-4]
+	str	r10,[$out,#64*6-4]
+	str	r11,[$out,#64*7-4]
+	add	$out,$out,#64*8
+
+	ldmia	$inp!,{r4-r11}		@ Y
+	str	r4,[$out,#64*0-4]
+	str	r5,[$out,#64*1-4]
+	str	r6,[$out,#64*2-4]
+	str	r7,[$out,#64*3-4]
+	str	r8,[$out,#64*4-4]
+	str	r9,[$out,#64*5-4]
+	str	r10,[$out,#64*6-4]
+	str	r11,[$out,#64*7-4]
+	add	$out,$out,#64*8
+
+	ldmia	$inp,{r4-r11}		@ Z
+	str	r4,[$out,#64*0-4]
+	str	r5,[$out,#64*1-4]
+	str	r6,[$out,#64*2-4]
+	str	r7,[$out,#64*3-4]
+	str	r8,[$out,#64*4-4]
+	str	r9,[$out,#64*5-4]
+	str	r10,[$out,#64*6-4]
+	str	r11,[$out,#64*7-4]
+
+	ldmia	sp!,{r4-r11}
+#if __ARM_ARCH__>=5 || defined(__thumb__)
+	bx	lr
+#else
+	mov	pc,lr
+#endif
+.size	ecp_nistz256_scatter_w5,.-ecp_nistz256_scatter_w5
+
+@ void	ecp_nistz256_gather_w5(P256_POINT *r0,const void *r1,
+@					      int r2);
+.globl	ecp_nistz256_gather_w5
+.type	ecp_nistz256_gather_w5,%function
+.align	5
+ecp_nistz256_gather_w5:
+	stmdb	sp!,{r4-r11}
+
+	cmp	$index,#0
+	mov	$mask,#0
+#ifdef	__thumb2__
+	itt	ne
+#endif
+	subne	$index,$index,#1
+	movne	$mask,#-1
+	add	$inp,$inp,$index,lsl#2
+
+	ldr	r4,[$inp,#64*0]
+	ldr	r5,[$inp,#64*1]
+	ldr	r6,[$inp,#64*2]
+	and	r4,r4,$mask
+	ldr	r7,[$inp,#64*3]
+	and	r5,r5,$mask
+	ldr	r8,[$inp,#64*4]
+	and	r6,r6,$mask
+	ldr	r9,[$inp,#64*5]
+	and	r7,r7,$mask
+	ldr	r10,[$inp,#64*6]
+	and	r8,r8,$mask
+	ldr	r11,[$inp,#64*7]
+	add	$inp,$inp,#64*8
+	and	r9,r9,$mask
+	and	r10,r10,$mask
+	and	r11,r11,$mask
+	stmia	$out!,{r4-r11}	@ X
+
+	ldr	r4,[$inp,#64*0]
+	ldr	r5,[$inp,#64*1]
+	ldr	r6,[$inp,#64*2]
+	and	r4,r4,$mask
+	ldr	r7,[$inp,#64*3]
+	and	r5,r5,$mask
+	ldr	r8,[$inp,#64*4]
+	and	r6,r6,$mask
+	ldr	r9,[$inp,#64*5]
+	and	r7,r7,$mask
+	ldr	r10,[$inp,#64*6]
+	and	r8,r8,$mask
+	ldr	r11,[$inp,#64*7]
+	add	$inp,$inp,#64*8
+	and	r9,r9,$mask
+	and	r10,r10,$mask
+	and	r11,r11,$mask
+	stmia	$out!,{r4-r11}	@ Y
+
+	ldr	r4,[$inp,#64*0]
+	ldr	r5,[$inp,#64*1]
+	ldr	r6,[$inp,#64*2]
+	and	r4,r4,$mask
+	ldr	r7,[$inp,#64*3]
+	and	r5,r5,$mask
+	ldr	r8,[$inp,#64*4]
+	and	r6,r6,$mask
+	ldr	r9,[$inp,#64*5]
+	and	r7,r7,$mask
+	ldr	r10,[$inp,#64*6]
+	and	r8,r8,$mask
+	ldr	r11,[$inp,#64*7]
+	and	r9,r9,$mask
+	and	r10,r10,$mask
+	and	r11,r11,$mask
+	stmia	$out,{r4-r11}		@ Z
+
+	ldmia	sp!,{r4-r11}
+#if __ARM_ARCH__>=5 || defined(__thumb__)
+	bx	lr
+#else
+	mov	pc,lr
+#endif
+.size	ecp_nistz256_gather_w5,.-ecp_nistz256_gather_w5
+
+@ void	ecp_nistz256_scatter_w7(void *r0,const P256_POINT_AFFINE *r1,
+@					 int r2);
+.globl	ecp_nistz256_scatter_w7
+.type	ecp_nistz256_scatter_w7,%function
+.align	5
+ecp_nistz256_scatter_w7:
+	add	$out,$out,$index
+	mov	$index,#64/4
+.Loop_scatter_w7:
+	ldr	$mask,[$inp],#4
+	subs	$index,$index,#1
+	strb	$mask,[$out,#64*0-1]
+	mov	$mask,$mask,lsr#8
+	strb	$mask,[$out,#64*1-1]
+	mov	$mask,$mask,lsr#8
+	strb	$mask,[$out,#64*2-1]
+	mov	$mask,$mask,lsr#8
+	strb	$mask,[$out,#64*3-1]
+	add	$out,$out,#64*4
+	bne	.Loop_scatter_w7
+
+#if __ARM_ARCH__>=5 || defined(__thumb__)
+	bx	lr
+#else
+	mov	pc,lr
+#endif
+.size	ecp_nistz256_scatter_w7,.-ecp_nistz256_scatter_w7
+
+@ void	ecp_nistz256_gather_w7(P256_POINT_AFFINE *r0,const void *r1,
+@						     int r2);
+.globl	ecp_nistz256_gather_w7
+.type	ecp_nistz256_gather_w7,%function
+.align	5
+ecp_nistz256_gather_w7:
+	stmdb	sp!,{r4-r7}
+
+	cmp	$index,#0
+	mov	$mask,#0
+#ifdef	__thumb2__
+	itt	ne
+#endif
+	subne	$index,$index,#1
+	movne	$mask,#-1
+	add	$inp,$inp,$index
+	mov	$index,#64/4
+	nop
+.Loop_gather_w7:
+	ldrb	r4,[$inp,#64*0]
+	subs	$index,$index,#1
+	ldrb	r5,[$inp,#64*1]
+	ldrb	r6,[$inp,#64*2]
+	ldrb	r7,[$inp,#64*3]
+	add	$inp,$inp,#64*4
+	orr	r4,r4,r5,lsl#8
+	orr	r4,r4,r6,lsl#16
+	orr	r4,r4,r7,lsl#24
+	and	r4,r4,$mask
+	str	r4,[$out],#4
+	bne	.Loop_gather_w7
+
+	ldmia	sp!,{r4-r7}
+#if __ARM_ARCH__>=5 || defined(__thumb__)
+	bx	lr
+#else
+	mov	pc,lr
+#endif
+.size	ecp_nistz256_gather_w7,.-ecp_nistz256_gather_w7
+___
+}
+if (0) {
+# In comparison to integer-only equivalent of below subroutine:
+#
+# Cortex-A8	+10%
+# Cortex-A9	-10%
+# Snapdragon S4	+5%
+#
+# As not all time is spent in multiplication, overall impact is deemed
+# too low to care about.
+
+my ($A0,$A1,$A2,$A3,$Bi,$zero,$temp)=map("d$_",(0..7));
+my $mask="q4";
+my $mult="q5";
+my @AxB=map("q$_",(8..15));
+
+my ($rptr,$aptr,$bptr,$toutptr)=map("r$_",(0..3));
+
+$code.=<<___;
+#if __ARM_ARCH__>=7
+.fpu	neon
+
+.globl	ecp_nistz256_mul_mont_neon
+.type	ecp_nistz256_mul_mont_neon,%function
+.align	5
+ecp_nistz256_mul_mont_neon:
+	mov	ip,sp
+	stmdb	sp!,{r4-r9}
+	vstmdb	sp!,{q4-q5}		@ ABI specification says so
+
+	sub		$toutptr,sp,#40
+	vld1.32		{${Bi}[0]},[$bptr,:32]!
+	veor		$zero,$zero,$zero
+	vld1.32		{$A0-$A3}, [$aptr]		@ can't specify :32 :-(
+	vzip.16		$Bi,$zero
+	mov		sp,$toutptr			@ alloca
+	vmov.i64	$mask,#0xffff
+
+	vmull.u32	@AxB[0],$Bi,${A0}[0]
+	vmull.u32	@AxB[1],$Bi,${A0}[1]
+	vmull.u32	@AxB[2],$Bi,${A1}[0]
+	vmull.u32	@AxB[3],$Bi,${A1}[1]
+	 vshr.u64	$temp,@AxB[0]#lo,#16
+	vmull.u32	@AxB[4],$Bi,${A2}[0]
+	 vadd.u64	@AxB[0]#hi,@AxB[0]#hi,$temp
+	vmull.u32	@AxB[5],$Bi,${A2}[1]
+	 vshr.u64	$temp,@AxB[0]#hi,#16		@ upper 32 bits of a[0]*b[0]
+	vmull.u32	@AxB[6],$Bi,${A3}[0]
+	 vand.u64	@AxB[0],@AxB[0],$mask		@ lower 32 bits of a[0]*b[0]
+	vmull.u32	@AxB[7],$Bi,${A3}[1]
+___
+for($i=1;$i<8;$i++) {
+$code.=<<___;
+	 vld1.32	{${Bi}[0]},[$bptr,:32]!
+	 veor		$zero,$zero,$zero
+	vadd.u64	@AxB[1]#lo,@AxB[1]#lo,$temp	@ reduction
+	vshl.u64	$mult,@AxB[0],#32
+	vadd.u64	@AxB[3],@AxB[3],@AxB[0]
+	vsub.u64	$mult,$mult,@AxB[0]
+	 vzip.16	$Bi,$zero
+	vadd.u64	@AxB[6],@AxB[6],@AxB[0]
+	vadd.u64	@AxB[7],@AxB[7],$mult
+___
+	push(@AxB,shift(@AxB));
+$code.=<<___;
+	vmlal.u32	@AxB[0],$Bi,${A0}[0]
+	vmlal.u32	@AxB[1],$Bi,${A0}[1]
+	vmlal.u32	@AxB[2],$Bi,${A1}[0]
+	vmlal.u32	@AxB[3],$Bi,${A1}[1]
+	 vshr.u64	$temp,@AxB[0]#lo,#16
+	vmlal.u32	@AxB[4],$Bi,${A2}[0]
+	 vadd.u64	@AxB[0]#hi,@AxB[0]#hi,$temp
+	vmlal.u32	@AxB[5],$Bi,${A2}[1]
+	 vshr.u64	$temp,@AxB[0]#hi,#16		@ upper 33 bits of a[0]*b[i]+t[0]
+	vmlal.u32	@AxB[6],$Bi,${A3}[0]
+	 vand.u64	@AxB[0],@AxB[0],$mask		@ lower 32 bits of a[0]*b[0]
+	vmull.u32	@AxB[7],$Bi,${A3}[1]
+___
+}
+$code.=<<___;
+	vadd.u64	@AxB[1]#lo,@AxB[1]#lo,$temp	@ last reduction
+	vshl.u64	$mult,@AxB[0],#32
+	vadd.u64	@AxB[3],@AxB[3],@AxB[0]
+	vsub.u64	$mult,$mult,@AxB[0]
+	vadd.u64	@AxB[6],@AxB[6],@AxB[0]
+	vadd.u64	@AxB[7],@AxB[7],$mult
+
+	vshr.u64	$temp,@AxB[1]#lo,#16		@ convert
+	vadd.u64	@AxB[1]#hi,@AxB[1]#hi,$temp
+	vshr.u64	$temp,@AxB[1]#hi,#16
+	vzip.16		@AxB[1]#lo,@AxB[1]#hi
+___
+foreach (2..7) {
+$code.=<<___;
+	vadd.u64	@AxB[$_]#lo,@AxB[$_]#lo,$temp
+	vst1.32		{@AxB[$_-1]#lo[0]},[$toutptr,:32]!
+	vshr.u64	$temp,@AxB[$_]#lo,#16
+	vadd.u64	@AxB[$_]#hi,@AxB[$_]#hi,$temp
+	vshr.u64	$temp,@AxB[$_]#hi,#16
+	vzip.16		@AxB[$_]#lo,@AxB[$_]#hi
+___
+}
+$code.=<<___;
+	vst1.32		{@AxB[7]#lo[0]},[$toutptr,:32]!
+	vst1.32		{$temp},[$toutptr]		@ upper 33 bits
+
+	ldr	r1,[sp,#0]
+	ldr	r2,[sp,#4]
+	ldr	r3,[sp,#8]
+	subs	r1,r1,#-1
+	ldr	r4,[sp,#12]
+	sbcs	r2,r2,#-1
+	ldr	r5,[sp,#16]
+	sbcs	r3,r3,#-1
+	ldr	r6,[sp,#20]
+	sbcs	r4,r4,#0
+	ldr	r7,[sp,#24]
+	sbcs	r5,r5,#0
+	ldr	r8,[sp,#28]
+	sbcs	r6,r6,#0
+	ldr	r9,[sp,#32]				@ top-most bit
+	sbcs	r7,r7,#1
+	sub	sp,ip,#40+16
+	sbcs	r8,r8,#-1
+	sbc	r9,r9,#0
+        vldmia  sp!,{q4-q5}
+
+	adds	r1,r1,r9
+	adcs	r2,r2,r9
+	str	r1,[$rptr,#0]
+	adcs	r3,r3,r9
+	str	r2,[$rptr,#4]
+	adcs	r4,r4,#0
+	str	r3,[$rptr,#8]
+	adcs	r5,r5,#0
+	str	r4,[$rptr,#12]
+	adcs	r6,r6,#0
+	str	r5,[$rptr,#16]
+	adcs	r7,r7,r9,lsr#31
+	str	r6,[$rptr,#20]
+	adcs	r8,r8,r9
+	str	r7,[$rptr,#24]
+	str	r8,[$rptr,#28]
+
+        ldmia   sp!,{r4-r9}
+	bx	lr
+.size	ecp_nistz256_mul_mont_neon,.-ecp_nistz256_mul_mont_neon
+#endif
+___
+}
+
+{{{
+########################################################################
+# Below $aN assignment matches order in which 256-bit result appears in
+# register bank at return from __ecp_nistz256_mul_mont, so that we can
+# skip over reloading it from memory. This means that below functions
+# use custom calling sequence accepting 256-bit input in registers,
+# output pointer in r0, $r_ptr, and optional pointer in r2, $b_ptr.
+#
+# See their "normal" counterparts for insights on calculations.
+
+my ($a0,$a1,$a2,$a3,$a4,$a5,$a6,$a7,
+    $t0,$t1,$t2,$t3)=map("r$_",(11,3..10,12,14,1));
+my $ff=$b_ptr;
+
+$code.=<<___;
+.type	__ecp_nistz256_sub_from,%function
+.align	5
+__ecp_nistz256_sub_from:
+	str	lr,[sp,#-4]!		@ push lr
+
+	 ldr	$t0,[$b_ptr,#0]
+	 ldr	$t1,[$b_ptr,#4]
+	 ldr	$t2,[$b_ptr,#8]
+	 ldr	$t3,[$b_ptr,#12]
+	subs	$a0,$a0,$t0
+	 ldr	$t0,[$b_ptr,#16]
+	sbcs	$a1,$a1,$t1
+	 ldr	$t1,[$b_ptr,#20]
+	sbcs	$a2,$a2,$t2
+	 ldr	$t2,[$b_ptr,#24]
+	sbcs	$a3,$a3,$t3
+	 ldr	$t3,[$b_ptr,#28]
+	sbcs	$a4,$a4,$t0
+	sbcs	$a5,$a5,$t1
+	sbcs	$a6,$a6,$t2
+	sbcs	$a7,$a7,$t3
+	sbc	$ff,$ff,$ff		@ broadcast borrow bit
+	ldr	lr,[sp],#4		@ pop lr
+
+	adds	$a0,$a0,$ff		@ add synthesized modulus
+	adcs	$a1,$a1,$ff
+	str	$a0,[$r_ptr,#0]
+	adcs	$a2,$a2,$ff
+	str	$a1,[$r_ptr,#4]
+	adcs	$a3,$a3,#0
+	str	$a2,[$r_ptr,#8]
+	adcs	$a4,$a4,#0
+	str	$a3,[$r_ptr,#12]
+	adcs	$a5,$a5,#0
+	str	$a4,[$r_ptr,#16]
+	adcs	$a6,$a6,$ff,lsr#31
+	str	$a5,[$r_ptr,#20]
+	adcs	$a7,$a7,$ff
+	str	$a6,[$r_ptr,#24]
+	str	$a7,[$r_ptr,#28]
+
+	mov	pc,lr
+.size	__ecp_nistz256_sub_from,.-__ecp_nistz256_sub_from
+
+.type	__ecp_nistz256_sub_morf,%function
+.align	5
+__ecp_nistz256_sub_morf:
+	str	lr,[sp,#-4]!		@ push lr
+
+	 ldr	$t0,[$b_ptr,#0]
+	 ldr	$t1,[$b_ptr,#4]
+	 ldr	$t2,[$b_ptr,#8]
+	 ldr	$t3,[$b_ptr,#12]
+	subs	$a0,$t0,$a0
+	 ldr	$t0,[$b_ptr,#16]
+	sbcs	$a1,$t1,$a1
+	 ldr	$t1,[$b_ptr,#20]
+	sbcs	$a2,$t2,$a2
+	 ldr	$t2,[$b_ptr,#24]
+	sbcs	$a3,$t3,$a3
+	 ldr	$t3,[$b_ptr,#28]
+	sbcs	$a4,$t0,$a4
+	sbcs	$a5,$t1,$a5
+	sbcs	$a6,$t2,$a6
+	sbcs	$a7,$t3,$a7
+	sbc	$ff,$ff,$ff		@ broadcast borrow bit
+	ldr	lr,[sp],#4		@ pop lr
+
+	adds	$a0,$a0,$ff		@ add synthesized modulus
+	adcs	$a1,$a1,$ff
+	str	$a0,[$r_ptr,#0]
+	adcs	$a2,$a2,$ff
+	str	$a1,[$r_ptr,#4]
+	adcs	$a3,$a3,#0
+	str	$a2,[$r_ptr,#8]
+	adcs	$a4,$a4,#0
+	str	$a3,[$r_ptr,#12]
+	adcs	$a5,$a5,#0
+	str	$a4,[$r_ptr,#16]
+	adcs	$a6,$a6,$ff,lsr#31
+	str	$a5,[$r_ptr,#20]
+	adcs	$a7,$a7,$ff
+	str	$a6,[$r_ptr,#24]
+	str	$a7,[$r_ptr,#28]
+
+	mov	pc,lr
+.size	__ecp_nistz256_sub_morf,.-__ecp_nistz256_sub_morf
+
+.type	__ecp_nistz256_add_self,%function
+.align	4
+__ecp_nistz256_add_self:
+	adds	$a0,$a0,$a0		@ a[0:7]+=a[0:7]
+	adcs	$a1,$a1,$a1
+	adcs	$a2,$a2,$a2
+	adcs	$a3,$a3,$a3
+	adcs	$a4,$a4,$a4
+	adcs	$a5,$a5,$a5
+	adcs	$a6,$a6,$a6
+	mov	$ff,#0
+	adcs	$a7,$a7,$a7
+	adc	$ff,$ff,#0
+
+	@ if a+b >= modulus, subtract modulus.
+	@
+	@ But since comparison implies subtraction, we subtract
+	@ modulus and then add it back if subraction borrowed.
+
+	subs	$a0,$a0,#-1
+	sbcs	$a1,$a1,#-1
+	sbcs	$a2,$a2,#-1
+	sbcs	$a3,$a3,#0
+	sbcs	$a4,$a4,#0
+	sbcs	$a5,$a5,#0
+	sbcs	$a6,$a6,#1
+	sbcs	$a7,$a7,#-1
+	sbc	$ff,$ff,#0
+
+	@ Note that because mod has special form, i.e. consists of
+	@ 0xffffffff, 1 and 0s, we can conditionally synthesize it by
+	@ using value of borrow as a whole or extracting single bit.
+	@ Follow $ff register...
+
+	adds	$a0,$a0,$ff		@ add synthesized modulus
+	adcs	$a1,$a1,$ff
+	str	$a0,[$r_ptr,#0]
+	adcs	$a2,$a2,$ff
+	str	$a1,[$r_ptr,#4]
+	adcs	$a3,$a3,#0
+	str	$a2,[$r_ptr,#8]
+	adcs	$a4,$a4,#0
+	str	$a3,[$r_ptr,#12]
+	adcs	$a5,$a5,#0
+	str	$a4,[$r_ptr,#16]
+	adcs	$a6,$a6,$ff,lsr#31
+	str	$a5,[$r_ptr,#20]
+	adcs	$a7,$a7,$ff
+	str	$a6,[$r_ptr,#24]
+	str	$a7,[$r_ptr,#28]
+
+	mov	pc,lr
+.size	__ecp_nistz256_add_self,.-__ecp_nistz256_add_self
+
+___
+
+########################################################################
+# following subroutines are "literal" implementation of those found in
+# ecp_nistz256.c
+#
+########################################################################
+# void ecp_nistz256_point_double(P256_POINT *out,const P256_POINT *inp);
+#
+{
+my ($S,$M,$Zsqr,$in_x,$tmp0)=map(32*$_,(0..4));
+# above map() describes stack layout with 5 temporary
+# 256-bit vectors on top. Then note that we push
+# starting from r0, which means that we have copy of
+# input arguments just below these temporary vectors.
+
+$code.=<<___;
+.globl	ecp_nistz256_point_double
+.type	ecp_nistz256_point_double,%function
+.align	5
+ecp_nistz256_point_double:
+	stmdb	sp!,{r0-r12,lr}		@ push from r0, unusual, but intentional
+	sub	sp,sp,#32*5
+
+.Lpoint_double_shortcut:
+	add	r3,sp,#$in_x
+	ldmia	$a_ptr!,{r4-r11}	@ copy in_x
+	stmia	r3,{r4-r11}
+
+	add	$r_ptr,sp,#$S
+	bl	__ecp_nistz256_mul_by_2	@ p256_mul_by_2(S, in_y);
+
+	add	$b_ptr,$a_ptr,#32
+	add	$a_ptr,$a_ptr,#32
+	add	$r_ptr,sp,#$Zsqr
+	bl	__ecp_nistz256_mul_mont	@ p256_sqr_mont(Zsqr, in_z);
+
+	add	$a_ptr,sp,#$S
+	add	$b_ptr,sp,#$S
+	add	$r_ptr,sp,#$S
+	bl	__ecp_nistz256_mul_mont	@ p256_sqr_mont(S, S);
+
+	ldr	$b_ptr,[sp,#32*5+4]
+	add	$a_ptr,$b_ptr,#32
+	add	$b_ptr,$b_ptr,#64
+	add	$r_ptr,sp,#$tmp0
+	bl	__ecp_nistz256_mul_mont	@ p256_mul_mont(tmp0, in_z, in_y);
+
+	ldr	$r_ptr,[sp,#32*5]
+	add	$r_ptr,$r_ptr,#64
+	bl	__ecp_nistz256_add_self	@ p256_mul_by_2(res_z, tmp0);
+
+	add	$a_ptr,sp,#$in_x
+	add	$b_ptr,sp,#$Zsqr
+	add	$r_ptr,sp,#$M
+	bl	__ecp_nistz256_add	@ p256_add(M, in_x, Zsqr);
+
+	add	$a_ptr,sp,#$in_x
+	add	$b_ptr,sp,#$Zsqr
+	add	$r_ptr,sp,#$Zsqr
+	bl	__ecp_nistz256_sub	@ p256_sub(Zsqr, in_x, Zsqr);
+
+	add	$a_ptr,sp,#$S
+	add	$b_ptr,sp,#$S
+	add	$r_ptr,sp,#$tmp0
+	bl	__ecp_nistz256_mul_mont	@ p256_sqr_mont(tmp0, S);
+
+	add	$a_ptr,sp,#$Zsqr
+	add	$b_ptr,sp,#$M
+	add	$r_ptr,sp,#$M
+	bl	__ecp_nistz256_mul_mont	@ p256_mul_mont(M, M, Zsqr);
+
+	ldr	$r_ptr,[sp,#32*5]
+	add	$a_ptr,sp,#$tmp0
+	add	$r_ptr,$r_ptr,#32
+	bl	__ecp_nistz256_div_by_2	@ p256_div_by_2(res_y, tmp0);
+
+	add	$a_ptr,sp,#$M
+	add	$r_ptr,sp,#$M
+	bl	__ecp_nistz256_mul_by_3	@ p256_mul_by_3(M, M);
+
+	add	$a_ptr,sp,#$in_x
+	add	$b_ptr,sp,#$S
+	add	$r_ptr,sp,#$S
+	bl	__ecp_nistz256_mul_mont	@ p256_mul_mont(S, S, in_x);
+
+	add	$r_ptr,sp,#$tmp0
+	bl	__ecp_nistz256_add_self	@ p256_mul_by_2(tmp0, S);
+
+	ldr	$r_ptr,[sp,#32*5]
+	add	$a_ptr,sp,#$M
+	add	$b_ptr,sp,#$M
+	bl	__ecp_nistz256_mul_mont	@ p256_sqr_mont(res_x, M);
+
+	add	$b_ptr,sp,#$tmp0
+	bl	__ecp_nistz256_sub_from	@ p256_sub(res_x, res_x, tmp0);
+
+	add	$b_ptr,sp,#$S
+	add	$r_ptr,sp,#$S
+	bl	__ecp_nistz256_sub_morf	@ p256_sub(S, S, res_x);
+
+	add	$a_ptr,sp,#$M
+	add	$b_ptr,sp,#$S
+	bl	__ecp_nistz256_mul_mont	@ p256_mul_mont(S, S, M);
+
+	ldr	$r_ptr,[sp,#32*5]
+	add	$b_ptr,$r_ptr,#32
+	add	$r_ptr,$r_ptr,#32
+	bl	__ecp_nistz256_sub_from	@ p256_sub(res_y, S, res_y);
+
+	add	sp,sp,#32*5+16		@ +16 means "skip even over saved r0-r3"
+#if __ARM_ARCH__>=5 || !defined(__thumb__)
+	ldmia	sp!,{r4-r12,pc}
+#else
+	ldmia	sp!,{r4-r12,lr}
+	bx	lr			@ interoperable with Thumb ISA:-)
+#endif
+.size	ecp_nistz256_point_double,.-ecp_nistz256_point_double
+___
+}
+
+########################################################################
+# void ecp_nistz256_point_add(P256_POINT *out,const P256_POINT *in1,
+#			      const P256_POINT *in2);
+{
+my ($res_x,$res_y,$res_z,
+    $in1_x,$in1_y,$in1_z,
+    $in2_x,$in2_y,$in2_z,
+    $H,$Hsqr,$R,$Rsqr,$Hcub,
+    $U1,$U2,$S1,$S2)=map(32*$_,(0..17));
+my ($Z1sqr, $Z2sqr) = ($Hsqr, $Rsqr);
+# above map() describes stack layout with 18 temporary
+# 256-bit vectors on top. Then note that we push
+# starting from r0, which means that we have copy of
+# input arguments just below these temporary vectors.
+# We use three of them for !in1infty, !in2intfy and
+# result of check for zero.
+
+$code.=<<___;
+.globl	ecp_nistz256_point_add
+.type	ecp_nistz256_point_add,%function
+.align	5
+ecp_nistz256_point_add:
+	stmdb	sp!,{r0-r12,lr}		@ push from r0, unusual, but intentional
+	sub	sp,sp,#32*18+16
+
+	ldmia	$b_ptr!,{r4-r11}	@ copy in2_x
+	add	r3,sp,#$in2_x
+	stmia	r3!,{r4-r11}
+	ldmia	$b_ptr!,{r4-r11}	@ copy in2_y
+	stmia	r3!,{r4-r11}
+	ldmia	$b_ptr,{r4-r11}		@ copy in2_z
+	orr	r12,r4,r5
+	orr	r12,r12,r6
+	orr	r12,r12,r7
+	orr	r12,r12,r8
+	orr	r12,r12,r9
+	orr	r12,r12,r10
+	orr	r12,r12,r11
+	cmp	r12,#0
+#ifdef	__thumb2__
+	it	ne
+#endif
+	movne	r12,#-1
+	stmia	r3,{r4-r11}
+	str	r12,[sp,#32*18+8]	@ !in2infty
+
+	ldmia	$a_ptr!,{r4-r11}	@ copy in1_x
+	add	r3,sp,#$in1_x
+	stmia	r3!,{r4-r11}
+	ldmia	$a_ptr!,{r4-r11}	@ copy in1_y
+	stmia	r3!,{r4-r11}
+	ldmia	$a_ptr,{r4-r11}		@ copy in1_z
+	orr	r12,r4,r5
+	orr	r12,r12,r6
+	orr	r12,r12,r7
+	orr	r12,r12,r8
+	orr	r12,r12,r9
+	orr	r12,r12,r10
+	orr	r12,r12,r11
+	cmp	r12,#0
+#ifdef	__thumb2__
+	it	ne
+#endif
+	movne	r12,#-1
+	stmia	r3,{r4-r11}
+	str	r12,[sp,#32*18+4]	@ !in1infty
+
+	add	$a_ptr,sp,#$in2_z
+	add	$b_ptr,sp,#$in2_z
+	add	$r_ptr,sp,#$Z2sqr
+	bl	__ecp_nistz256_mul_mont	@ p256_sqr_mont(Z2sqr, in2_z);
+
+	add	$a_ptr,sp,#$in1_z
+	add	$b_ptr,sp,#$in1_z
+	add	$r_ptr,sp,#$Z1sqr
+	bl	__ecp_nistz256_mul_mont	@ p256_sqr_mont(Z1sqr, in1_z);
+
+	add	$a_ptr,sp,#$in2_z
+	add	$b_ptr,sp,#$Z2sqr
+	add	$r_ptr,sp,#$S1
+	bl	__ecp_nistz256_mul_mont	@ p256_mul_mont(S1, Z2sqr, in2_z);
+
+	add	$a_ptr,sp,#$in1_z
+	add	$b_ptr,sp,#$Z1sqr
+	add	$r_ptr,sp,#$S2
+	bl	__ecp_nistz256_mul_mont	@ p256_mul_mont(S2, Z1sqr, in1_z);
+
+	add	$a_ptr,sp,#$in1_y
+	add	$b_ptr,sp,#$S1
+	add	$r_ptr,sp,#$S1
+	bl	__ecp_nistz256_mul_mont	@ p256_mul_mont(S1, S1, in1_y);
+
+	add	$a_ptr,sp,#$in2_y
+	add	$b_ptr,sp,#$S2
+	add	$r_ptr,sp,#$S2
+	bl	__ecp_nistz256_mul_mont	@ p256_mul_mont(S2, S2, in2_y);
+
+	add	$b_ptr,sp,#$S1
+	add	$r_ptr,sp,#$R
+	bl	__ecp_nistz256_sub_from	@ p256_sub(R, S2, S1);
+
+	orr	$a0,$a0,$a1		@ see if result is zero
+	orr	$a2,$a2,$a3
+	orr	$a4,$a4,$a5
+	orr	$a0,$a0,$a2
+	orr	$a4,$a4,$a6
+	orr	$a0,$a0,$a7
+	 add	$a_ptr,sp,#$in1_x
+	orr	$a0,$a0,$a4
+	 add	$b_ptr,sp,#$Z2sqr
+	str	$a0,[sp,#32*18+12]
+
+	add	$r_ptr,sp,#$U1
+	bl	__ecp_nistz256_mul_mont	@ p256_mul_mont(U1, in1_x, Z2sqr);
+
+	add	$a_ptr,sp,#$in2_x
+	add	$b_ptr,sp,#$Z1sqr
+	add	$r_ptr,sp,#$U2
+	bl	__ecp_nistz256_mul_mont	@ p256_mul_mont(U2, in2_x, Z1sqr);
+
+	add	$b_ptr,sp,#$U1
+	add	$r_ptr,sp,#$H
+	bl	__ecp_nistz256_sub_from	@ p256_sub(H, U2, U1);
+
+	orr	$a0,$a0,$a1		@ see if result is zero
+	orr	$a2,$a2,$a3
+	orr	$a4,$a4,$a5
+	orr	$a0,$a0,$a2
+	orr	$a4,$a4,$a6
+	orr	$a0,$a0,$a7
+	orrs	$a0,$a0,$a4
+
+	bne	.Ladd_proceed		@ is_equal(U1,U2)?
+
+	ldr	$t0,[sp,#32*18+4]
+	ldr	$t1,[sp,#32*18+8]
+	ldr	$t2,[sp,#32*18+12]
+	tst	$t0,$t1
+	beq	.Ladd_proceed		@ (in1infty || in2infty)?
+	tst	$t2,$t2
+	beq	.Ladd_double		@ is_equal(S1,S2)?
+
+	ldr	$r_ptr,[sp,#32*18+16]
+	eor	r4,r4,r4
+	eor	r5,r5,r5
+	eor	r6,r6,r6
+	eor	r7,r7,r7
+	eor	r8,r8,r8
+	eor	r9,r9,r9
+	eor	r10,r10,r10
+	eor	r11,r11,r11
+	stmia	$r_ptr!,{r4-r11}
+	stmia	$r_ptr!,{r4-r11}
+	stmia	$r_ptr!,{r4-r11}
+	b	.Ladd_done
+
+.align	4
+.Ladd_double:
+	ldr	$a_ptr,[sp,#32*18+20]
+	add	sp,sp,#32*(18-5)+16	@ difference in frame sizes
+	b	.Lpoint_double_shortcut
+
+.align	4
+.Ladd_proceed:
+	add	$a_ptr,sp,#$R
+	add	$b_ptr,sp,#$R
+	add	$r_ptr,sp,#$Rsqr
+	bl	__ecp_nistz256_mul_mont	@ p256_sqr_mont(Rsqr, R);
+
+	add	$a_ptr,sp,#$H
+	add	$b_ptr,sp,#$in1_z
+	add	$r_ptr,sp,#$res_z
+	bl	__ecp_nistz256_mul_mont	@ p256_mul_mont(res_z, H, in1_z);
+
+	add	$a_ptr,sp,#$H
+	add	$b_ptr,sp,#$H
+	add	$r_ptr,sp,#$Hsqr
+	bl	__ecp_nistz256_mul_mont	@ p256_sqr_mont(Hsqr, H);
+
+	add	$a_ptr,sp,#$in2_z
+	add	$b_ptr,sp,#$res_z
+	add	$r_ptr,sp,#$res_z
+	bl	__ecp_nistz256_mul_mont	@ p256_mul_mont(res_z, res_z, in2_z);
+
+	add	$a_ptr,sp,#$H
+	add	$b_ptr,sp,#$Hsqr
+	add	$r_ptr,sp,#$Hcub
+	bl	__ecp_nistz256_mul_mont	@ p256_mul_mont(Hcub, Hsqr, H);
+
+	add	$a_ptr,sp,#$Hsqr
+	add	$b_ptr,sp,#$U1
+	add	$r_ptr,sp,#$U2
+	bl	__ecp_nistz256_mul_mont	@ p256_mul_mont(U2, U1, Hsqr);
+
+	add	$r_ptr,sp,#$Hsqr
+	bl	__ecp_nistz256_add_self	@ p256_mul_by_2(Hsqr, U2);
+
+	add	$b_ptr,sp,#$Rsqr
+	add	$r_ptr,sp,#$res_x
+	bl	__ecp_nistz256_sub_morf	@ p256_sub(res_x, Rsqr, Hsqr);
+
+	add	$b_ptr,sp,#$Hcub
+	bl	__ecp_nistz256_sub_from	@  p256_sub(res_x, res_x, Hcub);
+
+	add	$b_ptr,sp,#$U2
+	add	$r_ptr,sp,#$res_y
+	bl	__ecp_nistz256_sub_morf	@ p256_sub(res_y, U2, res_x);
+
+	add	$a_ptr,sp,#$Hcub
+	add	$b_ptr,sp,#$S1
+	add	$r_ptr,sp,#$S2
+	bl	__ecp_nistz256_mul_mont	@ p256_mul_mont(S2, S1, Hcub);
+
+	add	$a_ptr,sp,#$R
+	add	$b_ptr,sp,#$res_y
+	add	$r_ptr,sp,#$res_y
+	bl	__ecp_nistz256_mul_mont	@ p256_mul_mont(res_y, res_y, R);
+
+	add	$b_ptr,sp,#$S2
+	bl	__ecp_nistz256_sub_from	@ p256_sub(res_y, res_y, S2);
+
+	ldr	r11,[sp,#32*18+4]	@ !in1intfy
+	ldr	r12,[sp,#32*18+8]	@ !in2intfy
+	add	r1,sp,#$res_x
+	add	r2,sp,#$in2_x
+	and	r10,r11,r12
+	mvn	r11,r11
+	add	r3,sp,#$in1_x
+	and	r11,r11,r12
+	mvn	r12,r12
+	ldr	$r_ptr,[sp,#32*18+16]
+___
+for($i=0;$i<96;$i+=8) {			# conditional moves
+$code.=<<___;
+	ldmia	r1!,{r4-r5}		@ res_x
+	ldmia	r2!,{r6-r7}		@ in2_x
+	ldmia	r3!,{r8-r9}		@ in1_x
+	and	r4,r4,r10
+	and	r5,r5,r10
+	and	r6,r6,r11
+	and	r7,r7,r11
+	and	r8,r8,r12
+	and	r9,r9,r12
+	orr	r4,r4,r6
+	orr	r5,r5,r7
+	orr	r4,r4,r8
+	orr	r5,r5,r9
+	stmia	$r_ptr!,{r4-r5}
+___
+}
+$code.=<<___;
+.Ladd_done:
+	add	sp,sp,#32*18+16+16	@ +16 means "skip even over saved r0-r3"
+#if __ARM_ARCH__>=5 || defined(__thumb__)
+	ldmia	sp!,{r4-r12,pc}
+#else
+	ldmia	sp!,{r4-r12,lr}
+	bx	lr			@ interoperable with Thumb ISA:-)
+#endif
+.size	ecp_nistz256_point_add,.-ecp_nistz256_point_add
+___
+}
+
+########################################################################
+# void ecp_nistz256_point_add_affine(P256_POINT *out,const P256_POINT *in1,
+#				     const P256_POINT_AFFINE *in2);
+{
+my ($res_x,$res_y,$res_z,
+    $in1_x,$in1_y,$in1_z,
+    $in2_x,$in2_y,
+    $U2,$S2,$H,$R,$Hsqr,$Hcub,$Rsqr)=map(32*$_,(0..14));
+my $Z1sqr = $S2;
+# above map() describes stack layout with 18 temporary
+# 256-bit vectors on top. Then note that we push
+# starting from r0, which means that we have copy of
+# input arguments just below these temporary vectors.
+# We use two of them for !in1infty, !in2intfy.
+
+my @ONE_mont=(1,0,0,-1,-1,-1,-2,0);
+
+$code.=<<___;
+.globl	ecp_nistz256_point_add_affine
+.type	ecp_nistz256_point_add_affine,%function
+.align	5
+ecp_nistz256_point_add_affine:
+	stmdb	sp!,{r0-r12,lr}		@ push from r0, unusual, but intentional
+	sub	sp,sp,#32*15
+
+	ldmia	$a_ptr!,{r4-r11}	@ copy in1_x
+	add	r3,sp,#$in1_x
+	stmia	r3!,{r4-r11}
+	ldmia	$a_ptr!,{r4-r11}	@ copy in1_y
+	stmia	r3!,{r4-r11}
+	ldmia	$a_ptr,{r4-r11}		@ copy in1_z
+	orr	r12,r4,r5
+	orr	r12,r12,r6
+	orr	r12,r12,r7
+	orr	r12,r12,r8
+	orr	r12,r12,r9
+	orr	r12,r12,r10
+	orr	r12,r12,r11
+	cmp	r12,#0
+#ifdef	__thumb2__
+	it	ne
+#endif
+	movne	r12,#-1
+	stmia	r3,{r4-r11}
+	str	r12,[sp,#32*15+4]	@ !in1infty
+
+	ldmia	$b_ptr!,{r4-r11}	@ copy in2_x
+	add	r3,sp,#$in2_x
+	orr	r12,r4,r5
+	orr	r12,r12,r6
+	orr	r12,r12,r7
+	orr	r12,r12,r8
+	orr	r12,r12,r9
+	orr	r12,r12,r10
+	orr	r12,r12,r11
+	stmia	r3!,{r4-r11}
+	ldmia	$b_ptr!,{r4-r11}	@ copy in2_y
+	orr	r12,r12,r4
+	orr	r12,r12,r5
+	orr	r12,r12,r6
+	orr	r12,r12,r7
+	orr	r12,r12,r8
+	orr	r12,r12,r9
+	orr	r12,r12,r10
+	orr	r12,r12,r11
+	stmia	r3!,{r4-r11}
+	cmp	r12,#0
+#ifdef	__thumb2__
+	it	ne
+#endif
+	movne	r12,#-1
+	str	r12,[sp,#32*15+8]	@ !in2infty
+
+	add	$a_ptr,sp,#$in1_z
+	add	$b_ptr,sp,#$in1_z
+	add	$r_ptr,sp,#$Z1sqr
+	bl	__ecp_nistz256_mul_mont	@ p256_sqr_mont(Z1sqr, in1_z);
+
+	add	$a_ptr,sp,#$Z1sqr
+	add	$b_ptr,sp,#$in2_x
+	add	$r_ptr,sp,#$U2
+	bl	__ecp_nistz256_mul_mont	@ p256_mul_mont(U2, Z1sqr, in2_x);
+
+	add	$b_ptr,sp,#$in1_x
+	add	$r_ptr,sp,#$H
+	bl	__ecp_nistz256_sub_from	@ p256_sub(H, U2, in1_x);
+
+	add	$a_ptr,sp,#$Z1sqr
+	add	$b_ptr,sp,#$in1_z
+	add	$r_ptr,sp,#$S2
+	bl	__ecp_nistz256_mul_mont	@ p256_mul_mont(S2, Z1sqr, in1_z);
+
+	add	$a_ptr,sp,#$H
+	add	$b_ptr,sp,#$in1_z
+	add	$r_ptr,sp,#$res_z
+	bl	__ecp_nistz256_mul_mont	@ p256_mul_mont(res_z, H, in1_z);
+
+	add	$a_ptr,sp,#$in2_y
+	add	$b_ptr,sp,#$S2
+	add	$r_ptr,sp,#$S2
+	bl	__ecp_nistz256_mul_mont	@ p256_mul_mont(S2, S2, in2_y);
+
+	add	$b_ptr,sp,#$in1_y
+	add	$r_ptr,sp,#$R
+	bl	__ecp_nistz256_sub_from	@ p256_sub(R, S2, in1_y);
+
+	add	$a_ptr,sp,#$H
+	add	$b_ptr,sp,#$H
+	add	$r_ptr,sp,#$Hsqr
+	bl	__ecp_nistz256_mul_mont	@ p256_sqr_mont(Hsqr, H);
+
+	add	$a_ptr,sp,#$R
+	add	$b_ptr,sp,#$R
+	add	$r_ptr,sp,#$Rsqr
+	bl	__ecp_nistz256_mul_mont	@ p256_sqr_mont(Rsqr, R);
+
+	add	$a_ptr,sp,#$H
+	add	$b_ptr,sp,#$Hsqr
+	add	$r_ptr,sp,#$Hcub
+	bl	__ecp_nistz256_mul_mont	@ p256_mul_mont(Hcub, Hsqr, H);
+
+	add	$a_ptr,sp,#$Hsqr
+	add	$b_ptr,sp,#$in1_x
+	add	$r_ptr,sp,#$U2
+	bl	__ecp_nistz256_mul_mont	@ p256_mul_mont(U2, in1_x, Hsqr);
+
+	add	$r_ptr,sp,#$Hsqr
+	bl	__ecp_nistz256_add_self	@ p256_mul_by_2(Hsqr, U2);
+
+	add	$b_ptr,sp,#$Rsqr
+	add	$r_ptr,sp,#$res_x
+	bl	__ecp_nistz256_sub_morf	@ p256_sub(res_x, Rsqr, Hsqr);
+
+	add	$b_ptr,sp,#$Hcub
+	bl	__ecp_nistz256_sub_from	@  p256_sub(res_x, res_x, Hcub);
+
+	add	$b_ptr,sp,#$U2
+	add	$r_ptr,sp,#$res_y
+	bl	__ecp_nistz256_sub_morf	@ p256_sub(res_y, U2, res_x);
+
+	add	$a_ptr,sp,#$Hcub
+	add	$b_ptr,sp,#$in1_y
+	add	$r_ptr,sp,#$S2
+	bl	__ecp_nistz256_mul_mont	@ p256_mul_mont(S2, in1_y, Hcub);
+
+	add	$a_ptr,sp,#$R
+	add	$b_ptr,sp,#$res_y
+	add	$r_ptr,sp,#$res_y
+	bl	__ecp_nistz256_mul_mont	@ p256_mul_mont(res_y, res_y, R);
+
+	add	$b_ptr,sp,#$S2
+	bl	__ecp_nistz256_sub_from	@ p256_sub(res_y, res_y, S2);
+
+	ldr	r11,[sp,#32*15+4]	@ !in1intfy
+	ldr	r12,[sp,#32*15+8]	@ !in2intfy
+	add	r1,sp,#$res_x
+	add	r2,sp,#$in2_x
+	and	r10,r11,r12
+	mvn	r11,r11
+	add	r3,sp,#$in1_x
+	and	r11,r11,r12
+	mvn	r12,r12
+	ldr	$r_ptr,[sp,#32*15]
+___
+for($i=0;$i<64;$i+=8) {			# conditional moves
+$code.=<<___;
+	ldmia	r1!,{r4-r5}		@ res_x
+	ldmia	r2!,{r6-r7}		@ in2_x
+	ldmia	r3!,{r8-r9}		@ in1_x
+	and	r4,r4,r10
+	and	r5,r5,r10
+	and	r6,r6,r11
+	and	r7,r7,r11
+	and	r8,r8,r12
+	and	r9,r9,r12
+	orr	r4,r4,r6
+	orr	r5,r5,r7
+	orr	r4,r4,r8
+	orr	r5,r5,r9
+	stmia	$r_ptr!,{r4-r5}
+___
+}
+for(;$i<96;$i+=8) {
+my $j=($i-64)/4;
+$code.=<<___;
+	ldmia	r1!,{r4-r5}		@ res_z
+	ldmia	r3!,{r8-r9}		@ in1_z
+	and	r4,r4,r10
+	and	r5,r5,r10
+	and	r6,r11,#@ONE_mont[$j]
+	and	r7,r11,#@ONE_mont[$j+1]
+	and	r8,r8,r12
+	and	r9,r9,r12
+	orr	r4,r4,r6
+	orr	r5,r5,r7
+	orr	r4,r4,r8
+	orr	r5,r5,r9
+	stmia	$r_ptr!,{r4-r5}
+___
+}
+$code.=<<___;
+	add	sp,sp,#32*15+16		@ +16 means "skip even over saved r0-r3"
+#if __ARM_ARCH__>=5 || !defined(__thumb__)
+	ldmia	sp!,{r4-r12,pc}
+#else
+	ldmia	sp!,{r4-r12,lr}
+	bx	lr			@ interoperable with Thumb ISA:-)
+#endif
+.size	ecp_nistz256_point_add_affine,.-ecp_nistz256_point_add_affine
+___
+}					}}}
+
+foreach (split("\n",$code)) {
+	s/\`([^\`]*)\`/eval $1/geo;
+
+	s/\bq([0-9]+)#(lo|hi)/sprintf "d%d",2*$1+($2 eq "hi")/geo;
+
+	print $_,"\n";
+}
+close STDOUT;	# enforce flush
diff --git a/crypto/ec/asm/ecp_nistz256-armv8.pl b/crypto/ec/asm/ecp_nistz256-armv8.pl
new file mode 100644
index 000000000..cdc91617f
--- /dev/null
+++ b/crypto/ec/asm/ecp_nistz256-armv8.pl
@@ -0,0 +1,1558 @@
+#! /usr/bin/env perl
+# Copyright 2015-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+
+# ====================================================================
+# Written by Andy Polyakov  for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+#
+# ECP_NISTZ256 module for ARMv8.
+#
+# February 2015.
+#
+# Original ECP_NISTZ256 submission targeting x86_64 is detailed in
+# http://eprint.iacr.org/2013/816.
+#
+#			with/without -DECP_NISTZ256_ASM
+# Apple A7		+120-360%
+# Cortex-A53		+120-400%
+# Cortex-A57		+120-350%
+# X-Gene		+200-330%
+# Denver		+140-400%
+#
+# Ranges denote minimum and maximum improvement coefficients depending
+# on benchmark. Lower coefficients are for ECDSA sign, server-side
+# operation. Keep in mind that +400% means 5x improvement.
+
+$flavour = shift;
+while (($output=shift) && ($output!~/\w[\w\-]*\.\w+$/)) {}
+
+$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+( $xlate="${dir}arm-xlate.pl" and -f $xlate ) or
+( $xlate="${dir}../../perlasm/arm-xlate.pl" and -f $xlate) or
+die "can't locate arm-xlate.pl";
+
+open OUT,"| \"$^X\" $xlate $flavour $output";
+*STDOUT=*OUT;
+
+{
+my ($rp,$ap,$bp,$bi,$a0,$a1,$a2,$a3,$t0,$t1,$t2,$t3,$poly1,$poly3,
+    $acc0,$acc1,$acc2,$acc3,$acc4,$acc5) =
+    map("x$_",(0..17,19,20));
+
+my ($acc6,$acc7)=($ap,$bp);	# used in __ecp_nistz256_sqr_mont
+
+$code.=<<___;
+#include "arm_arch.h"
+
+.text
+___
+########################################################################
+# Convert ecp_nistz256_table.c to layout expected by ecp_nistz_gather_w7
+#
+$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+open TABLE,") {
+	s/TOBN\(\s*(0x[0-9a-f]+),\s*(0x[0-9a-f]+)\s*\)/push @arr,hex($2),hex($1)/geo;
+}
+close TABLE;
+
+# See ecp_nistz256_table.c for explanation for why it's 64*16*37.
+# 64*16*37-1 is because $#arr returns last valid index or @arr, not
+# amount of elements.
+die "insane number of elements" if ($#arr != 64*16*37-1);
+
+$code.=<<___;
+.globl	ecp_nistz256_precomputed
+.type	ecp_nistz256_precomputed,%object
+.align	12
+ecp_nistz256_precomputed:
+___
+########################################################################
+# this conversion smashes P256_POINT_AFFINE by individual bytes with
+# 64 byte interval, similar to
+#	1111222233334444
+#	1234123412341234
+for(1..37) {
+	@tbl = splice(@arr,0,64*16);
+	for($i=0;$i<64;$i++) {
+		undef @line;
+		for($j=0;$j<64;$j++) {
+			push @line,(@tbl[$j*16+$i/4]>>(($i%4)*8))&0xff;
+		}
+		$code.=".byte\t";
+		$code.=join(',',map { sprintf "0x%02x",$_} @line);
+		$code.="\n";
+	}
+}
+$code.=<<___;
+.size	ecp_nistz256_precomputed,.-ecp_nistz256_precomputed
+.align	5
+.Lpoly:
+.quad	0xffffffffffffffff,0x00000000ffffffff,0x0000000000000000,0xffffffff00000001
+.LRR:	// 2^512 mod P precomputed for NIST P256 polynomial
+.quad	0x0000000000000003,0xfffffffbffffffff,0xfffffffffffffffe,0x00000004fffffffd
+.Lone_mont:
+.quad	0x0000000000000001,0xffffffff00000000,0xffffffffffffffff,0x00000000fffffffe
+.Lone:
+.quad	1,0,0,0
+.asciz	"ECP_NISTZ256 for ARMv8, CRYPTOGAMS by "
+
+// void	ecp_nistz256_to_mont(BN_ULONG x0[4],const BN_ULONG x1[4]);
+.globl	ecp_nistz256_to_mont
+.type	ecp_nistz256_to_mont,%function
+.align	6
+ecp_nistz256_to_mont:
+	stp	x29,x30,[sp,#-32]!
+	add	x29,sp,#0
+	stp	x19,x20,[sp,#16]
+
+	ldr	$bi,.LRR		// bp[0]
+	ldp	$a0,$a1,[$ap]
+	ldp	$a2,$a3,[$ap,#16]
+	ldr	$poly1,.Lpoly+8
+	ldr	$poly3,.Lpoly+24
+	adr	$bp,.LRR		// &bp[0]
+
+	bl	__ecp_nistz256_mul_mont
+
+	ldp	x19,x20,[sp,#16]
+	ldp	x29,x30,[sp],#32
+	ret
+.size	ecp_nistz256_to_mont,.-ecp_nistz256_to_mont
+
+// void	ecp_nistz256_from_mont(BN_ULONG x0[4],const BN_ULONG x1[4]);
+.globl	ecp_nistz256_from_mont
+.type	ecp_nistz256_from_mont,%function
+.align	4
+ecp_nistz256_from_mont:
+	stp	x29,x30,[sp,#-32]!
+	add	x29,sp,#0
+	stp	x19,x20,[sp,#16]
+
+	mov	$bi,#1			// bp[0]
+	ldp	$a0,$a1,[$ap]
+	ldp	$a2,$a3,[$ap,#16]
+	ldr	$poly1,.Lpoly+8
+	ldr	$poly3,.Lpoly+24
+	adr	$bp,.Lone		// &bp[0]
+
+	bl	__ecp_nistz256_mul_mont
+
+	ldp	x19,x20,[sp,#16]
+	ldp	x29,x30,[sp],#32
+	ret
+.size	ecp_nistz256_from_mont,.-ecp_nistz256_from_mont
+
+// void	ecp_nistz256_mul_mont(BN_ULONG x0[4],const BN_ULONG x1[4],
+//					     const BN_ULONG x2[4]);
+.globl	ecp_nistz256_mul_mont
+.type	ecp_nistz256_mul_mont,%function
+.align	4
+ecp_nistz256_mul_mont:
+	stp	x29,x30,[sp,#-32]!
+	add	x29,sp,#0
+	stp	x19,x20,[sp,#16]
+
+	ldr	$bi,[$bp]		// bp[0]
+	ldp	$a0,$a1,[$ap]
+	ldp	$a2,$a3,[$ap,#16]
+	ldr	$poly1,.Lpoly+8
+	ldr	$poly3,.Lpoly+24
+
+	bl	__ecp_nistz256_mul_mont
+
+	ldp	x19,x20,[sp,#16]
+	ldp	x29,x30,[sp],#32
+	ret
+.size	ecp_nistz256_mul_mont,.-ecp_nistz256_mul_mont
+
+// void	ecp_nistz256_sqr_mont(BN_ULONG x0[4],const BN_ULONG x1[4]);
+.globl	ecp_nistz256_sqr_mont
+.type	ecp_nistz256_sqr_mont,%function
+.align	4
+ecp_nistz256_sqr_mont:
+	stp	x29,x30,[sp,#-32]!
+	add	x29,sp,#0
+	stp	x19,x20,[sp,#16]
+
+	ldp	$a0,$a1,[$ap]
+	ldp	$a2,$a3,[$ap,#16]
+	ldr	$poly1,.Lpoly+8
+	ldr	$poly3,.Lpoly+24
+
+	bl	__ecp_nistz256_sqr_mont
+
+	ldp	x19,x20,[sp,#16]
+	ldp	x29,x30,[sp],#32
+	ret
+.size	ecp_nistz256_sqr_mont,.-ecp_nistz256_sqr_mont
+
+// void	ecp_nistz256_add(BN_ULONG x0[4],const BN_ULONG x1[4],
+//					const BN_ULONG x2[4]);
+.globl	ecp_nistz256_add
+.type	ecp_nistz256_add,%function
+.align	4
+ecp_nistz256_add:
+	stp	x29,x30,[sp,#-16]!
+	add	x29,sp,#0
+
+	ldp	$acc0,$acc1,[$ap]
+	ldp	$t0,$t1,[$bp]
+	ldp	$acc2,$acc3,[$ap,#16]
+	ldp	$t2,$t3,[$bp,#16]
+	ldr	$poly1,.Lpoly+8
+	ldr	$poly3,.Lpoly+24
+
+	bl	__ecp_nistz256_add
+
+	ldp	x29,x30,[sp],#16
+	ret
+.size	ecp_nistz256_add,.-ecp_nistz256_add
+
+// void	ecp_nistz256_div_by_2(BN_ULONG x0[4],const BN_ULONG x1[4]);
+.globl	ecp_nistz256_div_by_2
+.type	ecp_nistz256_div_by_2,%function
+.align	4
+ecp_nistz256_div_by_2:
+	stp	x29,x30,[sp,#-16]!
+	add	x29,sp,#0
+
+	ldp	$acc0,$acc1,[$ap]
+	ldp	$acc2,$acc3,[$ap,#16]
+	ldr	$poly1,.Lpoly+8
+	ldr	$poly3,.Lpoly+24
+
+	bl	__ecp_nistz256_div_by_2
+
+	ldp	x29,x30,[sp],#16
+	ret
+.size	ecp_nistz256_div_by_2,.-ecp_nistz256_div_by_2
+
+// void	ecp_nistz256_mul_by_2(BN_ULONG x0[4],const BN_ULONG x1[4]);
+.globl	ecp_nistz256_mul_by_2
+.type	ecp_nistz256_mul_by_2,%function
+.align	4
+ecp_nistz256_mul_by_2:
+	stp	x29,x30,[sp,#-16]!
+	add	x29,sp,#0
+
+	ldp	$acc0,$acc1,[$ap]
+	ldp	$acc2,$acc3,[$ap,#16]
+	ldr	$poly1,.Lpoly+8
+	ldr	$poly3,.Lpoly+24
+	mov	$t0,$acc0
+	mov	$t1,$acc1
+	mov	$t2,$acc2
+	mov	$t3,$acc3
+
+	bl	__ecp_nistz256_add	// ret = a+a	// 2*a
+
+	ldp	x29,x30,[sp],#16
+	ret
+.size	ecp_nistz256_mul_by_2,.-ecp_nistz256_mul_by_2
+
+// void	ecp_nistz256_mul_by_3(BN_ULONG x0[4],const BN_ULONG x1[4]);
+.globl	ecp_nistz256_mul_by_3
+.type	ecp_nistz256_mul_by_3,%function
+.align	4
+ecp_nistz256_mul_by_3:
+	stp	x29,x30,[sp,#-16]!
+	add	x29,sp,#0
+
+	ldp	$acc0,$acc1,[$ap]
+	ldp	$acc2,$acc3,[$ap,#16]
+	ldr	$poly1,.Lpoly+8
+	ldr	$poly3,.Lpoly+24
+	mov	$t0,$acc0
+	mov	$t1,$acc1
+	mov	$t2,$acc2
+	mov	$t3,$acc3
+	mov	$a0,$acc0
+	mov	$a1,$acc1
+	mov	$a2,$acc2
+	mov	$a3,$acc3
+
+	bl	__ecp_nistz256_add	// ret = a+a	// 2*a
+
+	mov	$t0,$a0
+	mov	$t1,$a1
+	mov	$t2,$a2
+	mov	$t3,$a3
+
+	bl	__ecp_nistz256_add	// ret += a	// 2*a+a=3*a
+
+	ldp	x29,x30,[sp],#16
+	ret
+.size	ecp_nistz256_mul_by_3,.-ecp_nistz256_mul_by_3
+
+// void	ecp_nistz256_sub(BN_ULONG x0[4],const BN_ULONG x1[4],
+//				        const BN_ULONG x2[4]);
+.globl	ecp_nistz256_sub
+.type	ecp_nistz256_sub,%function
+.align	4
+ecp_nistz256_sub:
+	stp	x29,x30,[sp,#-16]!
+	add	x29,sp,#0
+
+	ldp	$acc0,$acc1,[$ap]
+	ldp	$acc2,$acc3,[$ap,#16]
+	ldr	$poly1,.Lpoly+8
+	ldr	$poly3,.Lpoly+24
+
+	bl	__ecp_nistz256_sub_from
+
+	ldp	x29,x30,[sp],#16
+	ret
+.size	ecp_nistz256_sub,.-ecp_nistz256_sub
+
+// void	ecp_nistz256_neg(BN_ULONG x0[4],const BN_ULONG x1[4]);
+.globl	ecp_nistz256_neg
+.type	ecp_nistz256_neg,%function
+.align	4
+ecp_nistz256_neg:
+	stp	x29,x30,[sp,#-16]!
+	add	x29,sp,#0
+
+	mov	$bp,$ap
+	mov	$acc0,xzr		// a = 0
+	mov	$acc1,xzr
+	mov	$acc2,xzr
+	mov	$acc3,xzr
+	ldr	$poly1,.Lpoly+8
+	ldr	$poly3,.Lpoly+24
+
+	bl	__ecp_nistz256_sub_from
+
+	ldp	x29,x30,[sp],#16
+	ret
+.size	ecp_nistz256_neg,.-ecp_nistz256_neg
+
+// note that __ecp_nistz256_mul_mont expects a[0-3] input pre-loaded
+// to $a0-$a3 and b[0] - to $bi
+.type	__ecp_nistz256_mul_mont,%function
+.align	4
+__ecp_nistz256_mul_mont:
+	mul	$acc0,$a0,$bi		// a[0]*b[0]
+	umulh	$t0,$a0,$bi
+
+	mul	$acc1,$a1,$bi		// a[1]*b[0]
+	umulh	$t1,$a1,$bi
+
+	mul	$acc2,$a2,$bi		// a[2]*b[0]
+	umulh	$t2,$a2,$bi
+
+	mul	$acc3,$a3,$bi		// a[3]*b[0]
+	umulh	$t3,$a3,$bi
+	ldr	$bi,[$bp,#8]		// b[1]
+
+	adds	$acc1,$acc1,$t0		// accumulate high parts of multiplication
+	 lsl	$t0,$acc0,#32
+	adcs	$acc2,$acc2,$t1
+	 lsr	$t1,$acc0,#32
+	adcs	$acc3,$acc3,$t2
+	adc	$acc4,xzr,$t3
+	mov	$acc5,xzr
+___
+for($i=1;$i<4;$i++) {
+        # Reduction iteration is normally performed by accumulating
+        # result of multiplication of modulus by "magic" digit [and
+        # omitting least significant word, which is guaranteed to
+        # be 0], but thanks to special form of modulus and "magic"
+        # digit being equal to least significant word, it can be
+        # performed with additions and subtractions alone. Indeed:
+        #
+        #            ffff0001.00000000.0000ffff.ffffffff
+        # *                                     abcdefgh
+        # + xxxxxxxx.xxxxxxxx.xxxxxxxx.xxxxxxxx.abcdefgh
+        #
+        # Now observing that ff..ff*x = (2^n-1)*x = 2^n*x-x, we
+        # rewrite above as:
+        #
+        #   xxxxxxxx.xxxxxxxx.xxxxxxxx.xxxxxxxx.abcdefgh
+        # + abcdefgh.abcdefgh.0000abcd.efgh0000.00000000
+        # - 0000abcd.efgh0000.00000000.00000000.abcdefgh
+        #
+        # or marking redundant operations:
+        #
+        #   xxxxxxxx.xxxxxxxx.xxxxxxxx.xxxxxxxx.--------
+        # + abcdefgh.abcdefgh.0000abcd.efgh0000.--------
+        # - 0000abcd.efgh0000.--------.--------.--------
+
+$code.=<<___;
+	subs	$t2,$acc0,$t0		// "*0xffff0001"
+	sbc	$t3,$acc0,$t1
+	adds	$acc0,$acc1,$t0		// +=acc[0]<<96 and omit acc[0]
+	 mul	$t0,$a0,$bi		// lo(a[0]*b[i])
+	adcs	$acc1,$acc2,$t1
+	 mul	$t1,$a1,$bi		// lo(a[1]*b[i])
+	adcs	$acc2,$acc3,$t2		// +=acc[0]*0xffff0001
+	 mul	$t2,$a2,$bi		// lo(a[2]*b[i])
+	adcs	$acc3,$acc4,$t3
+	 mul	$t3,$a3,$bi		// lo(a[3]*b[i])
+	adc	$acc4,$acc5,xzr
+
+	adds	$acc0,$acc0,$t0		// accumulate low parts of multiplication
+	 umulh	$t0,$a0,$bi		// hi(a[0]*b[i])
+	adcs	$acc1,$acc1,$t1
+	 umulh	$t1,$a1,$bi		// hi(a[1]*b[i])
+	adcs	$acc2,$acc2,$t2
+	 umulh	$t2,$a2,$bi		// hi(a[2]*b[i])
+	adcs	$acc3,$acc3,$t3
+	 umulh	$t3,$a3,$bi		// hi(a[3]*b[i])
+	adc	$acc4,$acc4,xzr
+___
+$code.=<<___	if ($i<3);
+	ldr	$bi,[$bp,#8*($i+1)]	// b[$i+1]
+___
+$code.=<<___;
+	adds	$acc1,$acc1,$t0		// accumulate high parts of multiplication
+	 lsl	$t0,$acc0,#32
+	adcs	$acc2,$acc2,$t1
+	 lsr	$t1,$acc0,#32
+	adcs	$acc3,$acc3,$t2
+	adcs	$acc4,$acc4,$t3
+	adc	$acc5,xzr,xzr
+___
+}
+$code.=<<___;
+	// last reduction
+	subs	$t2,$acc0,$t0		// "*0xffff0001"
+	sbc	$t3,$acc0,$t1
+	adds	$acc0,$acc1,$t0		// +=acc[0]<<96 and omit acc[0]
+	adcs	$acc1,$acc2,$t1
+	adcs	$acc2,$acc3,$t2		// +=acc[0]*0xffff0001
+	adcs	$acc3,$acc4,$t3
+	adc	$acc4,$acc5,xzr
+
+	adds	$t0,$acc0,#1		// subs	$t0,$acc0,#-1 // tmp = ret-modulus
+	sbcs	$t1,$acc1,$poly1
+	sbcs	$t2,$acc2,xzr
+	sbcs	$t3,$acc3,$poly3
+	sbcs	xzr,$acc4,xzr		// did it borrow?
+
+	csel	$acc0,$acc0,$t0,lo	// ret = borrow ? ret : ret-modulus
+	csel	$acc1,$acc1,$t1,lo
+	csel	$acc2,$acc2,$t2,lo
+	stp	$acc0,$acc1,[$rp]
+	csel	$acc3,$acc3,$t3,lo
+	stp	$acc2,$acc3,[$rp,#16]
+
+	ret
+.size	__ecp_nistz256_mul_mont,.-__ecp_nistz256_mul_mont
+
+// note that __ecp_nistz256_sqr_mont expects a[0-3] input pre-loaded
+// to $a0-$a3
+.type	__ecp_nistz256_sqr_mont,%function
+.align	4
+__ecp_nistz256_sqr_mont:
+	//  |  |  |  |  |  |a1*a0|  |
+	//  |  |  |  |  |a2*a0|  |  |
+	//  |  |a3*a2|a3*a0|  |  |  |
+	//  |  |  |  |a2*a1|  |  |  |
+	//  |  |  |a3*a1|  |  |  |  |
+	// *|  |  |  |  |  |  |  | 2|
+	// +|a3*a3|a2*a2|a1*a1|a0*a0|
+	//  |--+--+--+--+--+--+--+--|
+	//  |A7|A6|A5|A4|A3|A2|A1|A0|, where Ax is $accx, i.e. follow $accx
+	//
+	//  "can't overflow" below mark carrying into high part of
+	//  multiplication result, which can't overflow, because it
+	//  can never be all ones.
+
+	mul	$acc1,$a1,$a0		// a[1]*a[0]
+	umulh	$t1,$a1,$a0
+	mul	$acc2,$a2,$a0		// a[2]*a[0]
+	umulh	$t2,$a2,$a0
+	mul	$acc3,$a3,$a0		// a[3]*a[0]
+	umulh	$acc4,$a3,$a0
+
+	adds	$acc2,$acc2,$t1		// accumulate high parts of multiplication
+	 mul	$t0,$a2,$a1		// a[2]*a[1]
+	 umulh	$t1,$a2,$a1
+	adcs	$acc3,$acc3,$t2
+	 mul	$t2,$a3,$a1		// a[3]*a[1]
+	 umulh	$t3,$a3,$a1
+	adc	$acc4,$acc4,xzr		// can't overflow
+
+	mul	$acc5,$a3,$a2		// a[3]*a[2]
+	umulh	$acc6,$a3,$a2
+
+	adds	$t1,$t1,$t2		// accumulate high parts of multiplication
+	 mul	$acc0,$a0,$a0		// a[0]*a[0]
+	adc	$t2,$t3,xzr		// can't overflow
+
+	adds	$acc3,$acc3,$t0		// accumulate low parts of multiplication
+	 umulh	$a0,$a0,$a0
+	adcs	$acc4,$acc4,$t1
+	 mul	$t1,$a1,$a1		// a[1]*a[1]
+	adcs	$acc5,$acc5,$t2
+	 umulh	$a1,$a1,$a1
+	adc	$acc6,$acc6,xzr		// can't overflow
+
+	adds	$acc1,$acc1,$acc1	// acc[1-6]*=2
+	 mul	$t2,$a2,$a2		// a[2]*a[2]
+	adcs	$acc2,$acc2,$acc2
+	 umulh	$a2,$a2,$a2
+	adcs	$acc3,$acc3,$acc3
+	 mul	$t3,$a3,$a3		// a[3]*a[3]
+	adcs	$acc4,$acc4,$acc4
+	 umulh	$a3,$a3,$a3
+	adcs	$acc5,$acc5,$acc5
+	adcs	$acc6,$acc6,$acc6
+	adc	$acc7,xzr,xzr
+
+	adds	$acc1,$acc1,$a0		// +a[i]*a[i]
+	adcs	$acc2,$acc2,$t1
+	adcs	$acc3,$acc3,$a1
+	adcs	$acc4,$acc4,$t2
+	adcs	$acc5,$acc5,$a2
+	 lsl	$t0,$acc0,#32
+	adcs	$acc6,$acc6,$t3
+	 lsr	$t1,$acc0,#32
+	adc	$acc7,$acc7,$a3
+___
+for($i=0;$i<3;$i++) {			# reductions, see commentary in
+					# multiplication for details
+$code.=<<___;
+	subs	$t2,$acc0,$t0		// "*0xffff0001"
+	sbc	$t3,$acc0,$t1
+	adds	$acc0,$acc1,$t0		// +=acc[0]<<96 and omit acc[0]
+	adcs	$acc1,$acc2,$t1
+	 lsl	$t0,$acc0,#32
+	adcs	$acc2,$acc3,$t2		// +=acc[0]*0xffff0001
+	 lsr	$t1,$acc0,#32
+	adc	$acc3,$t3,xzr		// can't overflow
+___
+}
+$code.=<<___;
+	subs	$t2,$acc0,$t0		// "*0xffff0001"
+	sbc	$t3,$acc0,$t1
+	adds	$acc0,$acc1,$t0		// +=acc[0]<<96 and omit acc[0]
+	adcs	$acc1,$acc2,$t1
+	adcs	$acc2,$acc3,$t2		// +=acc[0]*0xffff0001
+	adc	$acc3,$t3,xzr		// can't overflow
+
+	adds	$acc0,$acc0,$acc4	// accumulate upper half
+	adcs	$acc1,$acc1,$acc5
+	adcs	$acc2,$acc2,$acc6
+	adcs	$acc3,$acc3,$acc7
+	adc	$acc4,xzr,xzr
+
+	adds	$t0,$acc0,#1		// subs	$t0,$acc0,#-1 // tmp = ret-modulus
+	sbcs	$t1,$acc1,$poly1
+	sbcs	$t2,$acc2,xzr
+	sbcs	$t3,$acc3,$poly3
+	sbcs	xzr,$acc4,xzr		// did it borrow?
+
+	csel	$acc0,$acc0,$t0,lo	// ret = borrow ? ret : ret-modulus
+	csel	$acc1,$acc1,$t1,lo
+	csel	$acc2,$acc2,$t2,lo
+	stp	$acc0,$acc1,[$rp]
+	csel	$acc3,$acc3,$t3,lo
+	stp	$acc2,$acc3,[$rp,#16]
+
+	ret
+.size	__ecp_nistz256_sqr_mont,.-__ecp_nistz256_sqr_mont
+
+// Note that __ecp_nistz256_add expects both input vectors pre-loaded to
+// $a0-$a3 and $t0-$t3. This is done because it's used in multiple
+// contexts, e.g. in multiplication by 2 and 3...
+.type	__ecp_nistz256_add,%function
+.align	4
+__ecp_nistz256_add:
+	adds	$acc0,$acc0,$t0		// ret = a+b
+	adcs	$acc1,$acc1,$t1
+	adcs	$acc2,$acc2,$t2
+	adcs	$acc3,$acc3,$t3
+	adc	$ap,xzr,xzr		// zap $ap
+
+	adds	$t0,$acc0,#1		// subs	$t0,$a0,#-1 // tmp = ret-modulus
+	sbcs	$t1,$acc1,$poly1
+	sbcs	$t2,$acc2,xzr
+	sbcs	$t3,$acc3,$poly3
+	sbcs	xzr,$ap,xzr		// did subtraction borrow?
+
+	csel	$acc0,$acc0,$t0,lo	// ret = borrow ? ret : ret-modulus
+	csel	$acc1,$acc1,$t1,lo
+	csel	$acc2,$acc2,$t2,lo
+	stp	$acc0,$acc1,[$rp]
+	csel	$acc3,$acc3,$t3,lo
+	stp	$acc2,$acc3,[$rp,#16]
+
+	ret
+.size	__ecp_nistz256_add,.-__ecp_nistz256_add
+
+.type	__ecp_nistz256_sub_from,%function
+.align	4
+__ecp_nistz256_sub_from:
+	ldp	$t0,$t1,[$bp]
+	ldp	$t2,$t3,[$bp,#16]
+	subs	$acc0,$acc0,$t0		// ret = a-b
+	sbcs	$acc1,$acc1,$t1
+	sbcs	$acc2,$acc2,$t2
+	sbcs	$acc3,$acc3,$t3
+	sbc	$ap,xzr,xzr		// zap $ap
+
+	subs	$t0,$acc0,#1		// adds	$t0,$a0,#-1 // tmp = ret+modulus
+	adcs	$t1,$acc1,$poly1
+	adcs	$t2,$acc2,xzr
+	adc	$t3,$acc3,$poly3
+	cmp	$ap,xzr			// did subtraction borrow?
+
+	csel	$acc0,$acc0,$t0,eq	// ret = borrow ? ret+modulus : ret
+	csel	$acc1,$acc1,$t1,eq
+	csel	$acc2,$acc2,$t2,eq
+	stp	$acc0,$acc1,[$rp]
+	csel	$acc3,$acc3,$t3,eq
+	stp	$acc2,$acc3,[$rp,#16]
+
+	ret
+.size	__ecp_nistz256_sub_from,.-__ecp_nistz256_sub_from
+
+.type	__ecp_nistz256_sub_morf,%function
+.align	4
+__ecp_nistz256_sub_morf:
+	ldp	$t0,$t1,[$bp]
+	ldp	$t2,$t3,[$bp,#16]
+	subs	$acc0,$t0,$acc0		// ret = b-a
+	sbcs	$acc1,$t1,$acc1
+	sbcs	$acc2,$t2,$acc2
+	sbcs	$acc3,$t3,$acc3
+	sbc	$ap,xzr,xzr		// zap $ap
+
+	subs	$t0,$acc0,#1		// adds	$t0,$a0,#-1 // tmp = ret+modulus
+	adcs	$t1,$acc1,$poly1
+	adcs	$t2,$acc2,xzr
+	adc	$t3,$acc3,$poly3
+	cmp	$ap,xzr			// did subtraction borrow?
+
+	csel	$acc0,$acc0,$t0,eq	// ret = borrow ? ret+modulus : ret
+	csel	$acc1,$acc1,$t1,eq
+	csel	$acc2,$acc2,$t2,eq
+	stp	$acc0,$acc1,[$rp]
+	csel	$acc3,$acc3,$t3,eq
+	stp	$acc2,$acc3,[$rp,#16]
+
+	ret
+.size	__ecp_nistz256_sub_morf,.-__ecp_nistz256_sub_morf
+
+.type	__ecp_nistz256_div_by_2,%function
+.align	4
+__ecp_nistz256_div_by_2:
+	subs	$t0,$acc0,#1		// adds	$t0,$a0,#-1 // tmp = a+modulus
+	adcs	$t1,$acc1,$poly1
+	adcs	$t2,$acc2,xzr
+	adcs	$t3,$acc3,$poly3
+	adc	$ap,xzr,xzr		// zap $ap
+	tst	$acc0,#1		// is a even?
+
+	csel	$acc0,$acc0,$t0,eq	// ret = even ? a : a+modulus 
+	csel	$acc1,$acc1,$t1,eq
+	csel	$acc2,$acc2,$t2,eq
+	csel	$acc3,$acc3,$t3,eq
+	csel	$ap,xzr,$ap,eq
+
+	lsr	$acc0,$acc0,#1		// ret >>= 1
+	orr	$acc0,$acc0,$acc1,lsl#63
+	lsr	$acc1,$acc1,#1
+	orr	$acc1,$acc1,$acc2,lsl#63
+	lsr	$acc2,$acc2,#1
+	orr	$acc2,$acc2,$acc3,lsl#63
+	lsr	$acc3,$acc3,#1
+	stp	$acc0,$acc1,[$rp]
+	orr	$acc3,$acc3,$ap,lsl#63
+	stp	$acc2,$acc3,[$rp,#16]
+
+	ret
+.size	__ecp_nistz256_div_by_2,.-__ecp_nistz256_div_by_2
+___
+########################################################################
+# following subroutines are "literal" implementation of those found in
+# ecp_nistz256.c
+#
+########################################################################
+# void ecp_nistz256_point_double(P256_POINT *out,const P256_POINT *inp);
+#
+{
+my ($S,$M,$Zsqr,$tmp0)=map(32*$_,(0..3));
+# above map() describes stack layout with 4 temporary
+# 256-bit vectors on top.
+my ($rp_real,$ap_real) = map("x$_",(21,22));
+
+$code.=<<___;
+.globl	ecp_nistz256_point_double
+.type	ecp_nistz256_point_double,%function
+.align	5
+ecp_nistz256_point_double:
+	stp	x29,x30,[sp,#-80]!
+	add	x29,sp,#0
+	stp	x19,x20,[sp,#16]
+	stp	x21,x22,[sp,#32]
+	sub	sp,sp,#32*4
+
+.Ldouble_shortcut:
+	ldp	$acc0,$acc1,[$ap,#32]
+	 mov	$rp_real,$rp
+	ldp	$acc2,$acc3,[$ap,#48]
+	 mov	$ap_real,$ap
+	 ldr	$poly1,.Lpoly+8
+	mov	$t0,$acc0
+	 ldr	$poly3,.Lpoly+24
+	mov	$t1,$acc1
+	 ldp	$a0,$a1,[$ap_real,#64]	// forward load for p256_sqr_mont
+	mov	$t2,$acc2
+	mov	$t3,$acc3
+	 ldp	$a2,$a3,[$ap_real,#64+16]
+	add	$rp,sp,#$S
+	bl	__ecp_nistz256_add	// p256_mul_by_2(S, in_y);
+
+	add	$rp,sp,#$Zsqr
+	bl	__ecp_nistz256_sqr_mont	// p256_sqr_mont(Zsqr, in_z);
+
+	ldp	$t0,$t1,[$ap_real]
+	ldp	$t2,$t3,[$ap_real,#16]
+	mov	$a0,$acc0		// put Zsqr aside for p256_sub
+	mov	$a1,$acc1
+	mov	$a2,$acc2
+	mov	$a3,$acc3
+	add	$rp,sp,#$M
+	bl	__ecp_nistz256_add	// p256_add(M, Zsqr, in_x);
+
+	add	$bp,$ap_real,#0
+	mov	$acc0,$a0		// restore Zsqr
+	mov	$acc1,$a1
+	 ldp	$a0,$a1,[sp,#$S]	// forward load for p256_sqr_mont
+	mov	$acc2,$a2
+	mov	$acc3,$a3
+	 ldp	$a2,$a3,[sp,#$S+16]
+	add	$rp,sp,#$Zsqr
+	bl	__ecp_nistz256_sub_morf	// p256_sub(Zsqr, in_x, Zsqr);
+
+	add	$rp,sp,#$S
+	bl	__ecp_nistz256_sqr_mont	// p256_sqr_mont(S, S);
+
+	ldr	$bi,[$ap_real,#32]
+	ldp	$a0,$a1,[$ap_real,#64]
+	ldp	$a2,$a3,[$ap_real,#64+16]
+	add	$bp,$ap_real,#32
+	add	$rp,sp,#$tmp0
+	bl	__ecp_nistz256_mul_mont	// p256_mul_mont(tmp0, in_z, in_y);
+
+	mov	$t0,$acc0
+	mov	$t1,$acc1
+	 ldp	$a0,$a1,[sp,#$S]	// forward load for p256_sqr_mont
+	mov	$t2,$acc2
+	mov	$t3,$acc3
+	 ldp	$a2,$a3,[sp,#$S+16]
+	add	$rp,$rp_real,#64
+	bl	__ecp_nistz256_add	// p256_mul_by_2(res_z, tmp0);
+
+	add	$rp,sp,#$tmp0
+	bl	__ecp_nistz256_sqr_mont	// p256_sqr_mont(tmp0, S);
+
+	 ldr	$bi,[sp,#$Zsqr]		// forward load for p256_mul_mont
+	 ldp	$a0,$a1,[sp,#$M]
+	 ldp	$a2,$a3,[sp,#$M+16]
+	add	$rp,$rp_real,#32
+	bl	__ecp_nistz256_div_by_2	// p256_div_by_2(res_y, tmp0);
+
+	add	$bp,sp,#$Zsqr
+	add	$rp,sp,#$M
+	bl	__ecp_nistz256_mul_mont	// p256_mul_mont(M, M, Zsqr);
+
+	mov	$t0,$acc0		// duplicate M
+	mov	$t1,$acc1
+	mov	$t2,$acc2
+	mov	$t3,$acc3
+	mov	$a0,$acc0		// put M aside
+	mov	$a1,$acc1
+	mov	$a2,$acc2
+	mov	$a3,$acc3
+	add	$rp,sp,#$M
+	bl	__ecp_nistz256_add
+	mov	$t0,$a0			// restore M
+	mov	$t1,$a1
+	 ldr	$bi,[$ap_real]		// forward load for p256_mul_mont
+	mov	$t2,$a2
+	 ldp	$a0,$a1,[sp,#$S]
+	mov	$t3,$a3
+	 ldp	$a2,$a3,[sp,#$S+16]
+	bl	__ecp_nistz256_add	// p256_mul_by_3(M, M);
+
+	add	$bp,$ap_real,#0
+	add	$rp,sp,#$S
+	bl	__ecp_nistz256_mul_mont	// p256_mul_mont(S, S, in_x);
+
+	mov	$t0,$acc0
+	mov	$t1,$acc1
+	 ldp	$a0,$a1,[sp,#$M]	// forward load for p256_sqr_mont
+	mov	$t2,$acc2
+	mov	$t3,$acc3
+	 ldp	$a2,$a3,[sp,#$M+16]
+	add	$rp,sp,#$tmp0
+	bl	__ecp_nistz256_add	// p256_mul_by_2(tmp0, S);
+
+	add	$rp,$rp_real,#0
+	bl	__ecp_nistz256_sqr_mont	// p256_sqr_mont(res_x, M);
+
+	add	$bp,sp,#$tmp0
+	bl	__ecp_nistz256_sub_from	// p256_sub(res_x, res_x, tmp0);
+
+	add	$bp,sp,#$S
+	add	$rp,sp,#$S
+	bl	__ecp_nistz256_sub_morf	// p256_sub(S, S, res_x);
+
+	ldr	$bi,[sp,#$M]
+	mov	$a0,$acc0		// copy S
+	mov	$a1,$acc1
+	mov	$a2,$acc2
+	mov	$a3,$acc3
+	add	$bp,sp,#$M
+	bl	__ecp_nistz256_mul_mont	// p256_mul_mont(S, S, M);
+
+	add	$bp,$rp_real,#32
+	add	$rp,$rp_real,#32
+	bl	__ecp_nistz256_sub_from	// p256_sub(res_y, S, res_y);
+
+	add	sp,x29,#0		// destroy frame
+	ldp	x19,x20,[x29,#16]
+	ldp	x21,x22,[x29,#32]
+	ldp	x29,x30,[sp],#80
+	ret
+.size	ecp_nistz256_point_double,.-ecp_nistz256_point_double
+___
+}
+
+########################################################################
+# void ecp_nistz256_point_add(P256_POINT *out,const P256_POINT *in1,
+#			      const P256_POINT *in2);
+{
+my ($res_x,$res_y,$res_z,
+    $H,$Hsqr,$R,$Rsqr,$Hcub,
+    $U1,$U2,$S1,$S2)=map(32*$_,(0..11));
+my ($Z1sqr, $Z2sqr) = ($Hsqr, $Rsqr);
+# above map() describes stack layout with 12 temporary
+# 256-bit vectors on top.
+my ($rp_real,$ap_real,$bp_real,$in1infty,$in2infty,$temp)=map("x$_",(21..26));
+
+$code.=<<___;
+.globl	ecp_nistz256_point_add
+.type	ecp_nistz256_point_add,%function
+.align	5
+ecp_nistz256_point_add:
+	stp	x29,x30,[sp,#-80]!
+	add	x29,sp,#0
+	stp	x19,x20,[sp,#16]
+	stp	x21,x22,[sp,#32]
+	stp	x23,x24,[sp,#48]
+	stp	x25,x26,[sp,#64]
+	sub	sp,sp,#32*12
+
+	ldp	$a0,$a1,[$bp,#64]	// in2_z
+	ldp	$a2,$a3,[$bp,#64+16]
+	 mov	$rp_real,$rp
+	 mov	$ap_real,$ap
+	 mov	$bp_real,$bp
+	 ldr	$poly1,.Lpoly+8
+	 ldr	$poly3,.Lpoly+24
+	orr	$t0,$a0,$a1
+	orr	$t2,$a2,$a3
+	orr	$in2infty,$t0,$t2
+	cmp	$in2infty,#0
+	csetm	$in2infty,ne		// !in2infty
+	add	$rp,sp,#$Z2sqr
+	bl	__ecp_nistz256_sqr_mont	// p256_sqr_mont(Z2sqr, in2_z);
+
+	ldp	$a0,$a1,[$ap_real,#64]	// in1_z
+	ldp	$a2,$a3,[$ap_real,#64+16]
+	orr	$t0,$a0,$a1
+	orr	$t2,$a2,$a3
+	orr	$in1infty,$t0,$t2
+	cmp	$in1infty,#0
+	csetm	$in1infty,ne		// !in1infty
+	add	$rp,sp,#$Z1sqr
+	bl	__ecp_nistz256_sqr_mont	// p256_sqr_mont(Z1sqr, in1_z);
+
+	ldr	$bi,[$bp_real,#64]
+	ldp	$a0,$a1,[sp,#$Z2sqr]
+	ldp	$a2,$a3,[sp,#$Z2sqr+16]
+	add	$bp,$bp_real,#64
+	add	$rp,sp,#$S1
+	bl	__ecp_nistz256_mul_mont	// p256_mul_mont(S1, Z2sqr, in2_z);
+
+	ldr	$bi,[$ap_real,#64]
+	ldp	$a0,$a1,[sp,#$Z1sqr]
+	ldp	$a2,$a3,[sp,#$Z1sqr+16]
+	add	$bp,$ap_real,#64
+	add	$rp,sp,#$S2
+	bl	__ecp_nistz256_mul_mont	// p256_mul_mont(S2, Z1sqr, in1_z);
+
+	ldr	$bi,[$ap_real,#32]
+	ldp	$a0,$a1,[sp,#$S1]
+	ldp	$a2,$a3,[sp,#$S1+16]
+	add	$bp,$ap_real,#32
+	add	$rp,sp,#$S1
+	bl	__ecp_nistz256_mul_mont	// p256_mul_mont(S1, S1, in1_y);
+
+	ldr	$bi,[$bp_real,#32]
+	ldp	$a0,$a1,[sp,#$S2]
+	ldp	$a2,$a3,[sp,#$S2+16]
+	add	$bp,$bp_real,#32
+	add	$rp,sp,#$S2
+	bl	__ecp_nistz256_mul_mont	// p256_mul_mont(S2, S2, in2_y);
+
+	add	$bp,sp,#$S1
+	 ldr	$bi,[sp,#$Z2sqr]	// forward load for p256_mul_mont
+	 ldp	$a0,$a1,[$ap_real]
+	 ldp	$a2,$a3,[$ap_real,#16]
+	add	$rp,sp,#$R
+	bl	__ecp_nistz256_sub_from	// p256_sub(R, S2, S1);
+
+	orr	$acc0,$acc0,$acc1	// see if result is zero
+	orr	$acc2,$acc2,$acc3
+	orr	$temp,$acc0,$acc2
+
+	add	$bp,sp,#$Z2sqr
+	add	$rp,sp,#$U1
+	bl	__ecp_nistz256_mul_mont	// p256_mul_mont(U1, in1_x, Z2sqr);
+
+	ldr	$bi,[sp,#$Z1sqr]
+	ldp	$a0,$a1,[$bp_real]
+	ldp	$a2,$a3,[$bp_real,#16]
+	add	$bp,sp,#$Z1sqr
+	add	$rp,sp,#$U2
+	bl	__ecp_nistz256_mul_mont	// p256_mul_mont(U2, in2_x, Z1sqr);
+
+	add	$bp,sp,#$U1
+	 ldp	$a0,$a1,[sp,#$R]	// forward load for p256_sqr_mont
+	 ldp	$a2,$a3,[sp,#$R+16]
+	add	$rp,sp,#$H
+	bl	__ecp_nistz256_sub_from	// p256_sub(H, U2, U1);
+
+	orr	$acc0,$acc0,$acc1	// see if result is zero
+	orr	$acc2,$acc2,$acc3
+	orr	$acc0,$acc0,$acc2
+	tst	$acc0,$acc0
+	b.ne	.Ladd_proceed		// is_equal(U1,U2)?
+
+	tst	$in1infty,$in2infty
+	b.eq	.Ladd_proceed		// (in1infty || in2infty)?
+
+	tst	$temp,$temp
+	b.eq	.Ladd_double		// is_equal(S1,S2)?
+
+	eor	$a0,$a0,$a0
+	eor	$a1,$a1,$a1
+	stp	$a0,$a1,[$rp_real]
+	stp	$a0,$a1,[$rp_real,#16]
+	stp	$a0,$a1,[$rp_real,#32]
+	stp	$a0,$a1,[$rp_real,#48]
+	stp	$a0,$a1,[$rp_real,#64]
+	stp	$a0,$a1,[$rp_real,#80]
+	b	.Ladd_done
+
+.align	4
+.Ladd_double:
+	mov	$ap,$ap_real
+	mov	$rp,$rp_real
+	ldp	x23,x24,[x29,#48]
+	ldp	x25,x26,[x29,#64]
+	add	sp,sp,#32*(12-4)	// difference in stack frames
+	b	.Ldouble_shortcut
+
+.align	4
+.Ladd_proceed:
+	add	$rp,sp,#$Rsqr
+	bl	__ecp_nistz256_sqr_mont	// p256_sqr_mont(Rsqr, R);
+
+	ldr	$bi,[$ap_real,#64]
+	ldp	$a0,$a1,[sp,#$H]
+	ldp	$a2,$a3,[sp,#$H+16]
+	add	$bp,$ap_real,#64
+	add	$rp,sp,#$res_z
+	bl	__ecp_nistz256_mul_mont	// p256_mul_mont(res_z, H, in1_z);
+
+	ldp	$a0,$a1,[sp,#$H]
+	ldp	$a2,$a3,[sp,#$H+16]
+	add	$rp,sp,#$Hsqr
+	bl	__ecp_nistz256_sqr_mont	// p256_sqr_mont(Hsqr, H);
+
+	ldr	$bi,[$bp_real,#64]
+	ldp	$a0,$a1,[sp,#$res_z]
+	ldp	$a2,$a3,[sp,#$res_z+16]
+	add	$bp,$bp_real,#64
+	add	$rp,sp,#$res_z
+	bl	__ecp_nistz256_mul_mont	// p256_mul_mont(res_z, res_z, in2_z);
+
+	ldr	$bi,[sp,#$H]
+	ldp	$a0,$a1,[sp,#$Hsqr]
+	ldp	$a2,$a3,[sp,#$Hsqr+16]
+	add	$bp,sp,#$H
+	add	$rp,sp,#$Hcub
+	bl	__ecp_nistz256_mul_mont	// p256_mul_mont(Hcub, Hsqr, H);
+
+	ldr	$bi,[sp,#$Hsqr]
+	ldp	$a0,$a1,[sp,#$U1]
+	ldp	$a2,$a3,[sp,#$U1+16]
+	add	$bp,sp,#$Hsqr
+	add	$rp,sp,#$U2
+	bl	__ecp_nistz256_mul_mont	// p256_mul_mont(U2, U1, Hsqr);
+
+	mov	$t0,$acc0
+	mov	$t1,$acc1
+	mov	$t2,$acc2
+	mov	$t3,$acc3
+	add	$rp,sp,#$Hsqr
+	bl	__ecp_nistz256_add	// p256_mul_by_2(Hsqr, U2);
+
+	add	$bp,sp,#$Rsqr
+	add	$rp,sp,#$res_x
+	bl	__ecp_nistz256_sub_morf	// p256_sub(res_x, Rsqr, Hsqr);
+
+	add	$bp,sp,#$Hcub
+	bl	__ecp_nistz256_sub_from	//  p256_sub(res_x, res_x, Hcub);
+
+	add	$bp,sp,#$U2
+	 ldr	$bi,[sp,#$Hcub]		// forward load for p256_mul_mont
+	 ldp	$a0,$a1,[sp,#$S1]
+	 ldp	$a2,$a3,[sp,#$S1+16]
+	add	$rp,sp,#$res_y
+	bl	__ecp_nistz256_sub_morf	// p256_sub(res_y, U2, res_x);
+
+	add	$bp,sp,#$Hcub
+	add	$rp,sp,#$S2
+	bl	__ecp_nistz256_mul_mont	// p256_mul_mont(S2, S1, Hcub);
+
+	ldr	$bi,[sp,#$R]
+	ldp	$a0,$a1,[sp,#$res_y]
+	ldp	$a2,$a3,[sp,#$res_y+16]
+	add	$bp,sp,#$R
+	add	$rp,sp,#$res_y
+	bl	__ecp_nistz256_mul_mont	// p256_mul_mont(res_y, res_y, R);
+
+	add	$bp,sp,#$S2
+	bl	__ecp_nistz256_sub_from	// p256_sub(res_y, res_y, S2);
+
+	ldp	$a0,$a1,[sp,#$res_x]		// res
+	ldp	$a2,$a3,[sp,#$res_x+16]
+	ldp	$t0,$t1,[$bp_real]		// in2
+	ldp	$t2,$t3,[$bp_real,#16]
+___
+for($i=0;$i<64;$i+=32) {		# conditional moves
+$code.=<<___;
+	ldp	$acc0,$acc1,[$ap_real,#$i]	// in1
+	cmp	$in1infty,#0			// !$in1intfy, remember?
+	ldp	$acc2,$acc3,[$ap_real,#$i+16]
+	csel	$t0,$a0,$t0,ne
+	csel	$t1,$a1,$t1,ne
+	ldp	$a0,$a1,[sp,#$res_x+$i+32]	// res
+	csel	$t2,$a2,$t2,ne
+	csel	$t3,$a3,$t3,ne
+	cmp	$in2infty,#0			// !$in2intfy, remember?
+	ldp	$a2,$a3,[sp,#$res_x+$i+48]
+	csel	$acc0,$t0,$acc0,ne
+	csel	$acc1,$t1,$acc1,ne
+	ldp	$t0,$t1,[$bp_real,#$i+32]	// in2
+	csel	$acc2,$t2,$acc2,ne
+	csel	$acc3,$t3,$acc3,ne
+	ldp	$t2,$t3,[$bp_real,#$i+48]
+	stp	$acc0,$acc1,[$rp_real,#$i]
+	stp	$acc2,$acc3,[$rp_real,#$i+16]
+___
+}
+$code.=<<___;
+	ldp	$acc0,$acc1,[$ap_real,#$i]	// in1
+	cmp	$in1infty,#0			// !$in1intfy, remember?
+	ldp	$acc2,$acc3,[$ap_real,#$i+16]
+	csel	$t0,$a0,$t0,ne
+	csel	$t1,$a1,$t1,ne
+	csel	$t2,$a2,$t2,ne
+	csel	$t3,$a3,$t3,ne
+	cmp	$in2infty,#0			// !$in2intfy, remember?
+	csel	$acc0,$t0,$acc0,ne
+	csel	$acc1,$t1,$acc1,ne
+	csel	$acc2,$t2,$acc2,ne
+	csel	$acc3,$t3,$acc3,ne
+	stp	$acc0,$acc1,[$rp_real,#$i]
+	stp	$acc2,$acc3,[$rp_real,#$i+16]
+
+.Ladd_done:
+	add	sp,x29,#0	// destroy frame
+	ldp	x19,x20,[x29,#16]
+	ldp	x21,x22,[x29,#32]
+	ldp	x23,x24,[x29,#48]
+	ldp	x25,x26,[x29,#64]
+	ldp	x29,x30,[sp],#80
+	ret
+.size	ecp_nistz256_point_add,.-ecp_nistz256_point_add
+___
+}
+
+########################################################################
+# void ecp_nistz256_point_add_affine(P256_POINT *out,const P256_POINT *in1,
+#				     const P256_POINT_AFFINE *in2);
+{
+my ($res_x,$res_y,$res_z,
+    $U2,$S2,$H,$R,$Hsqr,$Hcub,$Rsqr)=map(32*$_,(0..9));
+my $Z1sqr = $S2;
+# above map() describes stack layout with 10 temporary
+# 256-bit vectors on top.
+my ($rp_real,$ap_real,$bp_real,$in1infty,$in2infty,$temp)=map("x$_",(21..26));
+
+$code.=<<___;
+.globl	ecp_nistz256_point_add_affine
+.type	ecp_nistz256_point_add_affine,%function
+.align	5
+ecp_nistz256_point_add_affine:
+	stp	x29,x30,[sp,#-80]!
+	add	x29,sp,#0
+	stp	x19,x20,[sp,#16]
+	stp	x21,x22,[sp,#32]
+	stp	x23,x24,[sp,#48]
+	stp	x25,x26,[sp,#64]
+	sub	sp,sp,#32*10
+
+	mov	$rp_real,$rp
+	mov	$ap_real,$ap
+	mov	$bp_real,$bp
+	ldr	$poly1,.Lpoly+8
+	ldr	$poly3,.Lpoly+24
+
+	ldp	$a0,$a1,[$ap,#64]	// in1_z
+	ldp	$a2,$a3,[$ap,#64+16]
+	orr	$t0,$a0,$a1
+	orr	$t2,$a2,$a3
+	orr	$in1infty,$t0,$t2
+	cmp	$in1infty,#0
+	csetm	$in1infty,ne		// !in1infty
+
+	ldp	$acc0,$acc1,[$bp]	// in2_x
+	ldp	$acc2,$acc3,[$bp,#16]
+	ldp	$t0,$t1,[$bp,#32]	// in2_y
+	ldp	$t2,$t3,[$bp,#48]
+	orr	$acc0,$acc0,$acc1
+	orr	$acc2,$acc2,$acc3
+	orr	$t0,$t0,$t1
+	orr	$t2,$t2,$t3
+	orr	$acc0,$acc0,$acc2
+	orr	$t0,$t0,$t2
+	orr	$in2infty,$acc0,$t0
+	cmp	$in2infty,#0
+	csetm	$in2infty,ne		// !in2infty
+
+	add	$rp,sp,#$Z1sqr
+	bl	__ecp_nistz256_sqr_mont	// p256_sqr_mont(Z1sqr, in1_z);
+
+	mov	$a0,$acc0
+	mov	$a1,$acc1
+	mov	$a2,$acc2
+	mov	$a3,$acc3
+	ldr	$bi,[$bp_real]
+	add	$bp,$bp_real,#0
+	add	$rp,sp,#$U2
+	bl	__ecp_nistz256_mul_mont	// p256_mul_mont(U2, Z1sqr, in2_x);
+
+	add	$bp,$ap_real,#0
+	 ldr	$bi,[$ap_real,#64]	// forward load for p256_mul_mont
+	 ldp	$a0,$a1,[sp,#$Z1sqr]
+	 ldp	$a2,$a3,[sp,#$Z1sqr+16]
+	add	$rp,sp,#$H
+	bl	__ecp_nistz256_sub_from	// p256_sub(H, U2, in1_x);
+
+	add	$bp,$ap_real,#64
+	add	$rp,sp,#$S2
+	bl	__ecp_nistz256_mul_mont	// p256_mul_mont(S2, Z1sqr, in1_z);
+
+	ldr	$bi,[$ap_real,#64]
+	ldp	$a0,$a1,[sp,#$H]
+	ldp	$a2,$a3,[sp,#$H+16]
+	add	$bp,$ap_real,#64
+	add	$rp,sp,#$res_z
+	bl	__ecp_nistz256_mul_mont	// p256_mul_mont(res_z, H, in1_z);
+
+	ldr	$bi,[$bp_real,#32]
+	ldp	$a0,$a1,[sp,#$S2]
+	ldp	$a2,$a3,[sp,#$S2+16]
+	add	$bp,$bp_real,#32
+	add	$rp,sp,#$S2
+	bl	__ecp_nistz256_mul_mont	// p256_mul_mont(S2, S2, in2_y);
+
+	add	$bp,$ap_real,#32
+	 ldp	$a0,$a1,[sp,#$H]	// forward load for p256_sqr_mont
+	 ldp	$a2,$a3,[sp,#$H+16]
+	add	$rp,sp,#$R
+	bl	__ecp_nistz256_sub_from	// p256_sub(R, S2, in1_y);
+
+	add	$rp,sp,#$Hsqr
+	bl	__ecp_nistz256_sqr_mont	// p256_sqr_mont(Hsqr, H);
+
+	ldp	$a0,$a1,[sp,#$R]
+	ldp	$a2,$a3,[sp,#$R+16]
+	add	$rp,sp,#$Rsqr
+	bl	__ecp_nistz256_sqr_mont	// p256_sqr_mont(Rsqr, R);
+
+	ldr	$bi,[sp,#$H]
+	ldp	$a0,$a1,[sp,#$Hsqr]
+	ldp	$a2,$a3,[sp,#$Hsqr+16]
+	add	$bp,sp,#$H
+	add	$rp,sp,#$Hcub
+	bl	__ecp_nistz256_mul_mont	// p256_mul_mont(Hcub, Hsqr, H);
+
+	ldr	$bi,[$ap_real]
+	ldp	$a0,$a1,[sp,#$Hsqr]
+	ldp	$a2,$a3,[sp,#$Hsqr+16]
+	add	$bp,$ap_real,#0
+	add	$rp,sp,#$U2
+	bl	__ecp_nistz256_mul_mont	// p256_mul_mont(U2, in1_x, Hsqr);
+
+	mov	$t0,$acc0
+	mov	$t1,$acc1
+	mov	$t2,$acc2
+	mov	$t3,$acc3
+	add	$rp,sp,#$Hsqr
+	bl	__ecp_nistz256_add	// p256_mul_by_2(Hsqr, U2);
+
+	add	$bp,sp,#$Rsqr
+	add	$rp,sp,#$res_x
+	bl	__ecp_nistz256_sub_morf	// p256_sub(res_x, Rsqr, Hsqr);
+
+	add	$bp,sp,#$Hcub
+	bl	__ecp_nistz256_sub_from	//  p256_sub(res_x, res_x, Hcub);
+
+	add	$bp,sp,#$U2
+	 ldr	$bi,[$ap_real,#32]	// forward load for p256_mul_mont
+	 ldp	$a0,$a1,[sp,#$Hcub]
+	 ldp	$a2,$a3,[sp,#$Hcub+16]
+	add	$rp,sp,#$res_y
+	bl	__ecp_nistz256_sub_morf	// p256_sub(res_y, U2, res_x);
+
+	add	$bp,$ap_real,#32
+	add	$rp,sp,#$S2
+	bl	__ecp_nistz256_mul_mont	// p256_mul_mont(S2, in1_y, Hcub);
+
+	ldr	$bi,[sp,#$R]
+	ldp	$a0,$a1,[sp,#$res_y]
+	ldp	$a2,$a3,[sp,#$res_y+16]
+	add	$bp,sp,#$R
+	add	$rp,sp,#$res_y
+	bl	__ecp_nistz256_mul_mont	// p256_mul_mont(res_y, res_y, R);
+
+	add	$bp,sp,#$S2
+	bl	__ecp_nistz256_sub_from	// p256_sub(res_y, res_y, S2);
+
+	ldp	$a0,$a1,[sp,#$res_x]		// res
+	ldp	$a2,$a3,[sp,#$res_x+16]
+	ldp	$t0,$t1,[$bp_real]		// in2
+	ldp	$t2,$t3,[$bp_real,#16]
+___
+for($i=0;$i<64;$i+=32) {		# conditional moves
+$code.=<<___;
+	ldp	$acc0,$acc1,[$ap_real,#$i]	// in1
+	cmp	$in1infty,#0			// !$in1intfy, remember?
+	ldp	$acc2,$acc3,[$ap_real,#$i+16]
+	csel	$t0,$a0,$t0,ne
+	csel	$t1,$a1,$t1,ne
+	ldp	$a0,$a1,[sp,#$res_x+$i+32]	// res
+	csel	$t2,$a2,$t2,ne
+	csel	$t3,$a3,$t3,ne
+	cmp	$in2infty,#0			// !$in2intfy, remember?
+	ldp	$a2,$a3,[sp,#$res_x+$i+48]
+	csel	$acc0,$t0,$acc0,ne
+	csel	$acc1,$t1,$acc1,ne
+	ldp	$t0,$t1,[$bp_real,#$i+32]	// in2
+	csel	$acc2,$t2,$acc2,ne
+	csel	$acc3,$t3,$acc3,ne
+	ldp	$t2,$t3,[$bp_real,#$i+48]
+	stp	$acc0,$acc1,[$rp_real,#$i]
+	stp	$acc2,$acc3,[$rp_real,#$i+16]
+___
+$code.=<<___	if ($i == 0);
+	adr	$bp_real,.Lone_mont-64
+___
+}
+$code.=<<___;
+	ldp	$acc0,$acc1,[$ap_real,#$i]	// in1
+	cmp	$in1infty,#0			// !$in1intfy, remember?
+	ldp	$acc2,$acc3,[$ap_real,#$i+16]
+	csel	$t0,$a0,$t0,ne
+	csel	$t1,$a1,$t1,ne
+	csel	$t2,$a2,$t2,ne
+	csel	$t3,$a3,$t3,ne
+	cmp	$in2infty,#0			// !$in2intfy, remember?
+	csel	$acc0,$t0,$acc0,ne
+	csel	$acc1,$t1,$acc1,ne
+	csel	$acc2,$t2,$acc2,ne
+	csel	$acc3,$t3,$acc3,ne
+	stp	$acc0,$acc1,[$rp_real,#$i]
+	stp	$acc2,$acc3,[$rp_real,#$i+16]
+
+	add	sp,x29,#0		// destroy frame
+	ldp	x19,x20,[x29,#16]
+	ldp	x21,x22,[x29,#32]
+	ldp	x23,x24,[x29,#48]
+	ldp	x25,x26,[x29,#64]
+	ldp	x29,x30,[sp],#80
+	ret
+.size	ecp_nistz256_point_add_affine,.-ecp_nistz256_point_add_affine
+___
+}	}
+
+########################################################################
+# scatter-gather subroutines
+{
+my ($out,$inp,$index,$mask)=map("x$_",(0..3));
+$code.=<<___;
+// void	ecp_nistz256_scatter_w5(void *x0,const P256_POINT *x1,
+//					 int x2);
+.globl	ecp_nistz256_scatter_w5
+.type	ecp_nistz256_scatter_w5,%function
+.align	4
+ecp_nistz256_scatter_w5:
+	stp	x29,x30,[sp,#-16]!
+	add	x29,sp,#0
+
+	add	$out,$out,$index,lsl#2
+
+	ldp	x4,x5,[$inp]		// X
+	ldp	x6,x7,[$inp,#16]
+	str	w4,[$out,#64*0-4]
+	lsr	x4,x4,#32
+	str	w5,[$out,#64*1-4]
+	lsr	x5,x5,#32
+	str	w6,[$out,#64*2-4]
+	lsr	x6,x6,#32
+	str	w7,[$out,#64*3-4]
+	lsr	x7,x7,#32
+	str	w4,[$out,#64*4-4]
+	str	w5,[$out,#64*5-4]
+	str	w6,[$out,#64*6-4]
+	str	w7,[$out,#64*7-4]
+	add	$out,$out,#64*8
+
+	ldp	x4,x5,[$inp,#32]	// Y
+	ldp	x6,x7,[$inp,#48]
+	str	w4,[$out,#64*0-4]
+	lsr	x4,x4,#32
+	str	w5,[$out,#64*1-4]
+	lsr	x5,x5,#32
+	str	w6,[$out,#64*2-4]
+	lsr	x6,x6,#32
+	str	w7,[$out,#64*3-4]
+	lsr	x7,x7,#32
+	str	w4,[$out,#64*4-4]
+	str	w5,[$out,#64*5-4]
+	str	w6,[$out,#64*6-4]
+	str	w7,[$out,#64*7-4]
+	add	$out,$out,#64*8
+
+	ldp	x4,x5,[$inp,#64]	// Z
+	ldp	x6,x7,[$inp,#80]
+	str	w4,[$out,#64*0-4]
+	lsr	x4,x4,#32
+	str	w5,[$out,#64*1-4]
+	lsr	x5,x5,#32
+	str	w6,[$out,#64*2-4]
+	lsr	x6,x6,#32
+	str	w7,[$out,#64*3-4]
+	lsr	x7,x7,#32
+	str	w4,[$out,#64*4-4]
+	str	w5,[$out,#64*5-4]
+	str	w6,[$out,#64*6-4]
+	str	w7,[$out,#64*7-4]
+
+	ldr	x29,[sp],#16
+	ret
+.size	ecp_nistz256_scatter_w5,.-ecp_nistz256_scatter_w5
+
+// void	ecp_nistz256_gather_w5(P256_POINT *x0,const void *x1,
+//					      int x2);
+.globl	ecp_nistz256_gather_w5
+.type	ecp_nistz256_gather_w5,%function
+.align	4
+ecp_nistz256_gather_w5:
+	stp	x29,x30,[sp,#-16]!
+	add	x29,sp,#0
+
+	cmp	$index,xzr
+	csetm	x3,ne
+	add	$index,$index,x3
+	add	$inp,$inp,$index,lsl#2
+
+	ldr	w4,[$inp,#64*0]
+	ldr	w5,[$inp,#64*1]
+	ldr	w6,[$inp,#64*2]
+	ldr	w7,[$inp,#64*3]
+	ldr	w8,[$inp,#64*4]
+	ldr	w9,[$inp,#64*5]
+	ldr	w10,[$inp,#64*6]
+	ldr	w11,[$inp,#64*7]
+	add	$inp,$inp,#64*8
+	orr	x4,x4,x8,lsl#32
+	orr	x5,x5,x9,lsl#32
+	orr	x6,x6,x10,lsl#32
+	orr	x7,x7,x11,lsl#32
+	csel	x4,x4,xzr,ne
+	csel	x5,x5,xzr,ne
+	csel	x6,x6,xzr,ne
+	csel	x7,x7,xzr,ne
+	stp	x4,x5,[$out]		// X
+	stp	x6,x7,[$out,#16]
+
+	ldr	w4,[$inp,#64*0]
+	ldr	w5,[$inp,#64*1]
+	ldr	w6,[$inp,#64*2]
+	ldr	w7,[$inp,#64*3]
+	ldr	w8,[$inp,#64*4]
+	ldr	w9,[$inp,#64*5]
+	ldr	w10,[$inp,#64*6]
+	ldr	w11,[$inp,#64*7]
+	add	$inp,$inp,#64*8
+	orr	x4,x4,x8,lsl#32
+	orr	x5,x5,x9,lsl#32
+	orr	x6,x6,x10,lsl#32
+	orr	x7,x7,x11,lsl#32
+	csel	x4,x4,xzr,ne
+	csel	x5,x5,xzr,ne
+	csel	x6,x6,xzr,ne
+	csel	x7,x7,xzr,ne
+	stp	x4,x5,[$out,#32]	// Y
+	stp	x6,x7,[$out,#48]
+
+	ldr	w4,[$inp,#64*0]
+	ldr	w5,[$inp,#64*1]
+	ldr	w6,[$inp,#64*2]
+	ldr	w7,[$inp,#64*3]
+	ldr	w8,[$inp,#64*4]
+	ldr	w9,[$inp,#64*5]
+	ldr	w10,[$inp,#64*6]
+	ldr	w11,[$inp,#64*7]
+	orr	x4,x4,x8,lsl#32
+	orr	x5,x5,x9,lsl#32
+	orr	x6,x6,x10,lsl#32
+	orr	x7,x7,x11,lsl#32
+	csel	x4,x4,xzr,ne
+	csel	x5,x5,xzr,ne
+	csel	x6,x6,xzr,ne
+	csel	x7,x7,xzr,ne
+	stp	x4,x5,[$out,#64]	// Z
+	stp	x6,x7,[$out,#80]
+
+	ldr	x29,[sp],#16
+	ret
+.size	ecp_nistz256_gather_w5,.-ecp_nistz256_gather_w5
+
+// void	ecp_nistz256_scatter_w7(void *x0,const P256_POINT_AFFINE *x1,
+//					 int x2);
+.globl	ecp_nistz256_scatter_w7
+.type	ecp_nistz256_scatter_w7,%function
+.align	4
+ecp_nistz256_scatter_w7:
+	stp	x29,x30,[sp,#-16]!
+	add	x29,sp,#0
+
+	add	$out,$out,$index
+	mov	$index,#64/8
+.Loop_scatter_w7:
+	ldr	x3,[$inp],#8
+	subs	$index,$index,#1
+	prfm	pstl1strm,[$out,#4096+64*0]
+	prfm	pstl1strm,[$out,#4096+64*1]
+	prfm	pstl1strm,[$out,#4096+64*2]
+	prfm	pstl1strm,[$out,#4096+64*3]
+	prfm	pstl1strm,[$out,#4096+64*4]
+	prfm	pstl1strm,[$out,#4096+64*5]
+	prfm	pstl1strm,[$out,#4096+64*6]
+	prfm	pstl1strm,[$out,#4096+64*7]
+	strb	w3,[$out,#64*0-1]
+	lsr	x3,x3,#8
+	strb	w3,[$out,#64*1-1]
+	lsr	x3,x3,#8
+	strb	w3,[$out,#64*2-1]
+	lsr	x3,x3,#8
+	strb	w3,[$out,#64*3-1]
+	lsr	x3,x3,#8
+	strb	w3,[$out,#64*4-1]
+	lsr	x3,x3,#8
+	strb	w3,[$out,#64*5-1]
+	lsr	x3,x3,#8
+	strb	w3,[$out,#64*6-1]
+	lsr	x3,x3,#8
+	strb	w3,[$out,#64*7-1]
+	add	$out,$out,#64*8
+	b.ne	.Loop_scatter_w7
+
+	ldr	x29,[sp],#16
+	ret
+.size	ecp_nistz256_scatter_w7,.-ecp_nistz256_scatter_w7
+
+// void	ecp_nistz256_gather_w7(P256_POINT_AFFINE *x0,const void *x1,
+//						     int x2);
+.globl	ecp_nistz256_gather_w7
+.type	ecp_nistz256_gather_w7,%function
+.align	4
+ecp_nistz256_gather_w7:
+	stp	x29,x30,[sp,#-16]!
+	add	x29,sp,#0
+
+	cmp	$index,xzr
+	csetm	x3,ne
+	add	$index,$index,x3
+	add	$inp,$inp,$index
+	mov	$index,#64/8
+	nop
+.Loop_gather_w7:
+	ldrb	w4,[$inp,#64*0]
+	prfm	pldl1strm,[$inp,#4096+64*0]
+	subs	$index,$index,#1
+	ldrb	w5,[$inp,#64*1]
+	prfm	pldl1strm,[$inp,#4096+64*1]
+	ldrb	w6,[$inp,#64*2]
+	prfm	pldl1strm,[$inp,#4096+64*2]
+	ldrb	w7,[$inp,#64*3]
+	prfm	pldl1strm,[$inp,#4096+64*3]
+	ldrb	w8,[$inp,#64*4]
+	prfm	pldl1strm,[$inp,#4096+64*4]
+	ldrb	w9,[$inp,#64*5]
+	prfm	pldl1strm,[$inp,#4096+64*5]
+	ldrb	w10,[$inp,#64*6]
+	prfm	pldl1strm,[$inp,#4096+64*6]
+	ldrb	w11,[$inp,#64*7]
+	prfm	pldl1strm,[$inp,#4096+64*7]
+	add	$inp,$inp,#64*8
+	orr	x4,x4,x5,lsl#8
+	orr	x6,x6,x7,lsl#8
+	orr	x8,x8,x9,lsl#8
+	orr	x4,x4,x6,lsl#16
+	orr	x10,x10,x11,lsl#8
+	orr	x4,x4,x8,lsl#32
+	orr	x4,x4,x10,lsl#48
+	and	x4,x4,x3
+	str	x4,[$out],#8
+	b.ne	.Loop_gather_w7
+
+	ldr	x29,[sp],#16
+	ret
+.size	ecp_nistz256_gather_w7,.-ecp_nistz256_gather_w7
+___
+}
+
+foreach (split("\n",$code)) {
+	s/\`([^\`]*)\`/eval $1/ge;
+
+	print $_,"\n";
+}
+close STDOUT;	# enforce flush
diff --git a/crypto/ec/asm/ecp_nistz256-avx2.pl b/crypto/ec/asm/ecp_nistz256-avx2.pl
new file mode 100755
index 000000000..3bdd2cf13
--- /dev/null
+++ b/crypto/ec/asm/ecp_nistz256-avx2.pl
@@ -0,0 +1,2100 @@
+#! /usr/bin/env perl
+# Copyright 2014-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+
+##############################################################################
+#                                                                            #
+# Copyright 2014 Intel Corporation                                           #
+#                                                                            #
+# Licensed under the Apache License, Version 2.0 (the "License");            #
+# you may not use this file except in compliance with the License.           #
+# You may obtain a copy of the License at                                    #
+#                                                                            #
+#    http://www.apache.org/licenses/LICENSE-2.0                              #
+#                                                                            #
+# Unless required by applicable law or agreed to in writing, software        #
+# distributed under the License is distributed on an "AS IS" BASIS,          #
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   #
+# See the License for the specific language governing permissions and        #
+# limitations under the License.                                             #
+#                                                                            #
+##############################################################################
+#                                                                            #
+#  Developers and authors:                                                   #
+#  Shay Gueron (1, 2), and Vlad Krasnov (1)                                  #
+#  (1) Intel Corporation, Israel Development Center                          #
+#  (2) University of Haifa                                                   #
+#  Reference:                                                                #
+#  S.Gueron and V.Krasnov, "Fast Prime Field Elliptic Curve Cryptography with#
+#                           256 Bit Primes"                                  #
+#                                                                            #
+##############################################################################
+
+$flavour = shift;
+$output  = shift;
+if ($flavour =~ /\./) { $output = $flavour; undef $flavour; }
+
+$win64=0; $win64=1 if ($flavour =~ /[nm]asm|mingw64/ || $output =~ /\.asm$/);
+
+$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+( $xlate="${dir}x86_64-xlate.pl" and -f $xlate ) or
+( $xlate="${dir}../../perlasm/x86_64-xlate.pl" and -f $xlate) or
+die "can't locate x86_64-xlate.pl";
+
+open OUT,"| \"$^X\" $xlate $flavour $output";
+*STDOUT=*OUT;
+
+if (`$ENV{CC} -Wa,-v -c -o /dev/null -x assembler /dev/null 2>&1`
+		=~ /GNU assembler version ([2-9]\.[0-9]+)/) {
+	$avx = ($1>=2.19) + ($1>=2.22);
+	$addx = ($1>=2.23);
+}
+
+if (!$addx && $win64 && ($flavour =~ /nasm/ || $ENV{ASM} =~ /nasm/) &&
+	    `nasm -v 2>&1` =~ /NASM version ([2-9]\.[0-9]+)/) {
+	$avx = ($1>=2.09) + ($1>=2.10);
+	$addx = ($1>=2.10);
+}
+
+if (!$addx && $win64 && ($flavour =~ /masm/ || $ENV{ASM} =~ /ml64/) &&
+	    `ml64 2>&1` =~ /Version ([0-9]+)\./) {
+	$avx = ($1>=10) + ($1>=11);
+	$addx = ($1>=12);
+}
+
+if (!$addx && `$ENV{CC} -v 2>&1` =~ /(^clang version|based on LLVM) ([3-9])\.([0-9]+)/) {
+	my $ver = $2 + $3/100.0;	# 3.1->3.01, 3.10->3.10
+	$avx = ($ver>=3.0) + ($ver>=3.01);
+	$addx = ($ver>=3.03);
+}
+
+if ($avx>=2) {{
+$digit_size = "\$29";
+$n_digits = "\$9";
+
+$code.=<<___;
+.text
+
+.align 64
+.LAVX2_AND_MASK:
+.LAVX2_POLY:
+.quad 0x1fffffff, 0x1fffffff, 0x1fffffff, 0x1fffffff
+.quad 0x1fffffff, 0x1fffffff, 0x1fffffff, 0x1fffffff
+.quad 0x1fffffff, 0x1fffffff, 0x1fffffff, 0x1fffffff
+.quad 0x000001ff, 0x000001ff, 0x000001ff, 0x000001ff
+.quad 0x00000000, 0x00000000, 0x00000000, 0x00000000
+.quad 0x00000000, 0x00000000, 0x00000000, 0x00000000
+.quad 0x00040000, 0x00040000, 0x00040000, 0x00040000
+.quad 0x1fe00000, 0x1fe00000, 0x1fe00000, 0x1fe00000
+.quad 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff
+
+.LAVX2_POLY_x2:
+.quad 0x7FFFFFFC, 0x7FFFFFFC, 0x7FFFFFFC, 0x7FFFFFFC
+.quad 0x7FFFFFFC, 0x7FFFFFFC, 0x7FFFFFFC, 0x7FFFFFFC
+.quad 0x7FFFFFFC, 0x7FFFFFFC, 0x7FFFFFFC, 0x7FFFFFFC
+.quad 0x400007FC, 0x400007FC, 0x400007FC, 0x400007FC
+.quad 0x3FFFFFFE, 0x3FFFFFFE, 0x3FFFFFFE, 0x3FFFFFFE
+.quad 0x3FFFFFFE, 0x3FFFFFFE, 0x3FFFFFFE, 0x3FFFFFFE
+.quad 0x400FFFFE, 0x400FFFFE, 0x400FFFFE, 0x400FFFFE
+.quad 0x7F7FFFFE, 0x7F7FFFFE, 0x7F7FFFFE, 0x7F7FFFFE
+.quad 0x03FFFFFC, 0x03FFFFFC, 0x03FFFFFC, 0x03FFFFFC
+
+.LAVX2_POLY_x8:
+.quad 0xFFFFFFF8, 0xFFFFFFF8, 0xFFFFFFF8, 0xFFFFFFF8
+.quad 0xFFFFFFF8, 0xFFFFFFF8, 0xFFFFFFF8, 0xFFFFFFF8
+.quad 0xFFFFFFF8, 0xFFFFFFF8, 0xFFFFFFF8, 0xFFFFFFF8
+.quad 0x80000FF8, 0x80000FF8, 0x80000FF8, 0x80000FF8
+.quad 0x7FFFFFFC, 0x7FFFFFFC, 0x7FFFFFFC, 0x7FFFFFFC
+.quad 0x7FFFFFFC, 0x7FFFFFFC, 0x7FFFFFFC, 0x7FFFFFFC
+.quad 0x801FFFFC, 0x801FFFFC, 0x801FFFFC, 0x801FFFFC
+.quad 0xFEFFFFFC, 0xFEFFFFFC, 0xFEFFFFFC, 0xFEFFFFFC
+.quad 0x07FFFFF8, 0x07FFFFF8, 0x07FFFFF8, 0x07FFFFF8
+
+.LONE:
+.quad 0x00000020, 0x00000020, 0x00000020, 0x00000020
+.quad 0x00000000, 0x00000000, 0x00000000, 0x00000000
+.quad 0x00000000, 0x00000000, 0x00000000, 0x00000000
+.quad 0x1fffc000, 0x1fffc000, 0x1fffc000, 0x1fffc000
+.quad 0x1fffffff, 0x1fffffff, 0x1fffffff, 0x1fffffff
+.quad 0x1fffffff, 0x1fffffff, 0x1fffffff, 0x1fffffff
+.quad 0x1f7fffff, 0x1f7fffff, 0x1f7fffff, 0x1f7fffff
+.quad 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff
+.quad 0x00000000, 0x00000000, 0x00000000, 0x00000000
+
+# RR = 2^266 mod p in AVX2 format, to transform from the native OpenSSL
+# Montgomery form (*2^256) to our format (*2^261)
+
+.LTO_MONT_AVX2:
+.quad 0x00000400, 0x00000400, 0x00000400, 0x00000400
+.quad 0x00000000, 0x00000000, 0x00000000, 0x00000000
+.quad 0x00000000, 0x00000000, 0x00000000, 0x00000000
+.quad 0x1ff80000, 0x1ff80000, 0x1ff80000, 0x1ff80000
+.quad 0x1fffffff, 0x1fffffff, 0x1fffffff, 0x1fffffff
+.quad 0x1fffffff, 0x1fffffff, 0x1fffffff, 0x1fffffff
+.quad 0x0fffffff, 0x0fffffff, 0x0fffffff, 0x0fffffff
+.quad 0x1fffffff, 0x1fffffff, 0x1fffffff, 0x1fffffff
+.quad 0x00000003, 0x00000003, 0x00000003, 0x00000003
+
+.LFROM_MONT_AVX2:
+.quad 0x00000001, 0x00000001, 0x00000001, 0x00000001
+.quad 0x00000000, 0x00000000, 0x00000000, 0x00000000
+.quad 0x00000000, 0x00000000, 0x00000000, 0x00000000
+.quad 0x1ffffe00, 0x1ffffe00, 0x1ffffe00, 0x1ffffe00
+.quad 0x1fffffff, 0x1fffffff, 0x1fffffff, 0x1fffffff
+.quad 0x1fffffff, 0x1fffffff, 0x1fffffff, 0x1fffffff
+.quad 0x1ffbffff, 0x1ffbffff, 0x1ffbffff, 0x1ffbffff
+.quad 0x001fffff, 0x001fffff, 0x001fffff, 0x001fffff
+.quad 0x00000000, 0x00000000, 0x00000000, 0x00000000
+
+.LIntOne:
+.long 1,1,1,1,1,1,1,1
+___
+
+{
+# This function receives a pointer to an array of four affine points
+# (X, Y, <1>) and rearanges the data for AVX2 execution, while
+# converting it to 2^29 radix redundant form
+
+my ($X0,$X1,$X2,$X3, $Y0,$Y1,$Y2,$Y3,
+    $T0,$T1,$T2,$T3, $T4,$T5,$T6,$T7)=map("%ymm$_",(0..15));
+
+$code.=<<___;
+.globl	ecp_nistz256_avx2_transpose_convert
+.type	ecp_nistz256_avx2_transpose_convert,\@function,2
+.align 64
+ecp_nistz256_avx2_transpose_convert:
+	vzeroupper
+___
+$code.=<<___	if ($win64);
+	lea	-8-16*10(%rsp), %rsp
+	vmovaps	%xmm6, -8-16*10(%rax)
+	vmovaps	%xmm7, -8-16*9(%rax)
+	vmovaps	%xmm8, -8-16*8(%rax)
+	vmovaps	%xmm9, -8-16*7(%rax)
+	vmovaps	%xmm10, -8-16*6(%rax)
+	vmovaps	%xmm11, -8-16*5(%rax)
+	vmovaps	%xmm12, -8-16*4(%rax)
+	vmovaps	%xmm13, -8-16*3(%rax)
+	vmovaps	%xmm14, -8-16*2(%rax)
+	vmovaps	%xmm15, -8-16*1(%rax)
+___
+$code.=<<___;
+	# Load the data
+	vmovdqa		32*0(%rsi), $X0
+	lea		112(%rsi), %rax		# size optimization
+	vmovdqa		32*1(%rsi), $Y0
+	lea		.LAVX2_AND_MASK(%rip), %rdx
+	vmovdqa		32*2(%rsi), $X1
+	vmovdqa		32*3(%rsi), $Y1
+	vmovdqa		32*4-112(%rax), $X2
+	vmovdqa		32*5-112(%rax), $Y2
+	vmovdqa		32*6-112(%rax), $X3
+	vmovdqa		32*7-112(%rax), $Y3
+
+	# Transpose X and Y independently
+	vpunpcklqdq	$X1, $X0, $T0		# T0 = [B2 A2 B0 A0]
+	vpunpcklqdq	$X3, $X2, $T1		# T1 = [D2 C2 D0 C0]
+	vpunpckhqdq	$X1, $X0, $T2		# T2 = [B3 A3 B1 A1]
+	vpunpckhqdq	$X3, $X2, $T3		# T3 = [D3 C3 D1 C1]
+
+	vpunpcklqdq	$Y1, $Y0, $T4
+	vpunpcklqdq	$Y3, $Y2, $T5
+	vpunpckhqdq	$Y1, $Y0, $T6
+	vpunpckhqdq	$Y3, $Y2, $T7
+
+	vperm2i128	\$0x20, $T1, $T0, $X0	# X0 = [D0 C0 B0 A0]
+	vperm2i128	\$0x20, $T3, $T2, $X1	# X1 = [D1 C1 B1 A1]
+	vperm2i128	\$0x31, $T1, $T0, $X2	# X2 = [D2 C2 B2 A2]
+	vperm2i128	\$0x31, $T3, $T2, $X3	# X3 = [D3 C3 B3 A3]
+
+	vperm2i128	\$0x20, $T5, $T4, $Y0
+	vperm2i128	\$0x20, $T7, $T6, $Y1
+	vperm2i128	\$0x31, $T5, $T4, $Y2
+	vperm2i128	\$0x31, $T7, $T6, $Y3
+	vmovdqa		(%rdx), $T7
+
+	vpand		(%rdx), $X0, $T0	# out[0] = in[0] & mask;
+	vpsrlq		\$29, $X0, $X0
+	vpand		$T7, $X0, $T1		# out[1] = (in[0] >> shift) & mask;
+	vpsrlq		\$29, $X0, $X0
+	vpsllq		\$6, $X1, $T2
+	vpxor		$X0, $T2, $T2
+	vpand		$T7, $T2, $T2		# out[2] = ((in[0] >> (shift*2)) ^ (in[1] << (64-shift*2))) & mask;
+	vpsrlq		\$23, $X1, $X1
+	vpand		$T7, $X1, $T3		# out[3] = (in[1] >> ((shift*3)%64)) & mask;
+	vpsrlq		\$29, $X1, $X1
+	vpsllq		\$12, $X2, $T4
+	vpxor		$X1, $T4, $T4
+	vpand		$T7, $T4, $T4		# out[4] = ((in[1] >> ((shift*4)%64)) ^ (in[2] << (64*2-shift*4))) & mask;
+	vpsrlq		\$17, $X2, $X2
+	vpand		$T7, $X2, $T5		# out[5] = (in[2] >> ((shift*5)%64)) & mask;
+	vpsrlq		\$29, $X2, $X2
+	vpsllq		\$18, $X3, $T6
+	vpxor		$X2, $T6, $T6
+	vpand		$T7, $T6, $T6		# out[6] = ((in[2] >> ((shift*6)%64)) ^ (in[3] << (64*3-shift*6))) & mask;
+	vpsrlq		\$11, $X3, $X3
+	 vmovdqa	$T0, 32*0(%rdi)
+	 lea		112(%rdi), %rax		# size optimization
+	vpand		$T7, $X3, $T0		# out[7] = (in[3] >> ((shift*7)%64)) & mask;
+	vpsrlq		\$29, $X3, $X3		# out[8] = (in[3] >> ((shift*8)%64)) & mask;
+
+	vmovdqa		$T1, 32*1(%rdi)
+	vmovdqa		$T2, 32*2(%rdi)
+	vmovdqa		$T3, 32*3(%rdi)
+	vmovdqa		$T4, 32*4-112(%rax)
+	vmovdqa		$T5, 32*5-112(%rax)
+	vmovdqa		$T6, 32*6-112(%rax)
+	vmovdqa		$T0, 32*7-112(%rax)
+	vmovdqa		$X3, 32*8-112(%rax)
+	lea		448(%rdi), %rax		# size optimization
+
+	vpand		$T7, $Y0, $T0		# out[0] = in[0] & mask;
+	vpsrlq		\$29, $Y0, $Y0
+	vpand		$T7, $Y0, $T1		# out[1] = (in[0] >> shift) & mask;
+	vpsrlq		\$29, $Y0, $Y0
+	vpsllq		\$6, $Y1, $T2
+	vpxor		$Y0, $T2, $T2
+	vpand		$T7, $T2, $T2		# out[2] = ((in[0] >> (shift*2)) ^ (in[1] << (64-shift*2))) & mask;
+	vpsrlq		\$23, $Y1, $Y1
+	vpand		$T7, $Y1, $T3		# out[3] = (in[1] >> ((shift*3)%64)) & mask;
+	vpsrlq		\$29, $Y1, $Y1
+	vpsllq		\$12, $Y2, $T4
+	vpxor		$Y1, $T4, $T4
+	vpand		$T7, $T4, $T4		# out[4] = ((in[1] >> ((shift*4)%64)) ^ (in[2] << (64*2-shift*4))) & mask;
+	vpsrlq		\$17, $Y2, $Y2
+	vpand		$T7, $Y2, $T5		# out[5] = (in[2] >> ((shift*5)%64)) & mask;
+	vpsrlq		\$29, $Y2, $Y2
+	vpsllq		\$18, $Y3, $T6
+	vpxor		$Y2, $T6, $T6
+	vpand		$T7, $T6, $T6		# out[6] = ((in[2] >> ((shift*6)%64)) ^ (in[3] << (64*3-shift*6))) & mask;
+	vpsrlq		\$11, $Y3, $Y3
+	 vmovdqa	$T0, 32*9-448(%rax)
+	vpand		$T7, $Y3, $T0		# out[7] = (in[3] >> ((shift*7)%64)) & mask;
+	vpsrlq		\$29, $Y3, $Y3		# out[8] = (in[3] >> ((shift*8)%64)) & mask;
+
+	vmovdqa		$T1, 32*10-448(%rax)
+	vmovdqa		$T2, 32*11-448(%rax)
+	vmovdqa		$T3, 32*12-448(%rax)
+	vmovdqa		$T4, 32*13-448(%rax)
+	vmovdqa		$T5, 32*14-448(%rax)
+	vmovdqa		$T6, 32*15-448(%rax)
+	vmovdqa		$T0, 32*16-448(%rax)
+	vmovdqa		$Y3, 32*17-448(%rax)
+
+	vzeroupper
+___
+$code.=<<___	if ($win64);
+	movaps	16*0(%rsp), %xmm6
+	movaps	16*1(%rsp), %xmm7
+	movaps	16*2(%rsp), %xmm8
+	movaps	16*3(%rsp), %xmm9
+	movaps	16*4(%rsp), %xmm10
+	movaps	16*5(%rsp), %xmm11
+	movaps	16*6(%rsp), %xmm12
+	movaps	16*7(%rsp), %xmm13
+	movaps	16*8(%rsp), %xmm14
+	movaps	16*9(%rsp), %xmm15
+	lea	8+16*10(%rsp), %rsp
+___
+$code.=<<___;
+	ret
+.size	ecp_nistz256_avx2_transpose_convert,.-ecp_nistz256_avx2_transpose_convert
+___
+}
+{
+################################################################################
+# This function receives a pointer to an array of four AVX2 formatted points
+# (X, Y, Z) convert the data to normal representation, and rearanges the data
+
+my ($D0,$D1,$D2,$D3, $D4,$D5,$D6,$D7, $D8)=map("%ymm$_",(0..8));
+my ($T0,$T1,$T2,$T3, $T4,$T5,$T6)=map("%ymm$_",(9..15));
+
+$code.=<<___;
+
+.globl	ecp_nistz256_avx2_convert_transpose_back
+.type	ecp_nistz256_avx2_convert_transpose_back,\@function,2
+.align	32
+ecp_nistz256_avx2_convert_transpose_back:
+	vzeroupper
+___
+$code.=<<___	if ($win64);
+	lea	-8-16*10(%rsp), %rsp
+	vmovaps	%xmm6, -8-16*10(%rax)
+	vmovaps	%xmm7, -8-16*9(%rax)
+	vmovaps	%xmm8, -8-16*8(%rax)
+	vmovaps	%xmm9, -8-16*7(%rax)
+	vmovaps	%xmm10, -8-16*6(%rax)
+	vmovaps	%xmm11, -8-16*5(%rax)
+	vmovaps	%xmm12, -8-16*4(%rax)
+	vmovaps	%xmm13, -8-16*3(%rax)
+	vmovaps	%xmm14, -8-16*2(%rax)
+	vmovaps	%xmm15, -8-16*1(%rax)
+___
+$code.=<<___;
+	mov	\$3, %ecx
+
+.Lconv_loop:
+	vmovdqa		32*0(%rsi), $D0
+	lea		160(%rsi), %rax		# size optimization
+	vmovdqa		32*1(%rsi), $D1
+	vmovdqa		32*2(%rsi), $D2
+	vmovdqa		32*3(%rsi), $D3
+	vmovdqa		32*4-160(%rax), $D4
+	vmovdqa		32*5-160(%rax), $D5
+	vmovdqa		32*6-160(%rax), $D6
+	vmovdqa		32*7-160(%rax), $D7
+	vmovdqa		32*8-160(%rax), $D8
+
+	vpsllq		\$29, $D1, $D1
+	vpsllq		\$58, $D2, $T0
+	vpaddq		$D1, $D0, $D0
+	vpaddq		$T0, $D0, $D0		# out[0] = (in[0]) ^ (in[1] << shift*1) ^ (in[2] << shift*2);
+
+	vpsrlq		\$6, $D2, $D2
+	vpsllq		\$23, $D3, $D3
+	vpsllq		\$52, $D4, $T1
+	vpaddq		$D2, $D3, $D3
+	vpaddq		$D3, $T1, $D1		# out[1] = (in[2] >> (64*1-shift*2)) ^ (in[3] << shift*3%64) ^ (in[4] << shift*4%64);
+
+	vpsrlq		\$12, $D4, $D4
+	vpsllq		\$17, $D5, $D5
+	vpsllq		\$46, $D6, $T2
+	vpaddq		$D4, $D5, $D5
+	vpaddq		$D5, $T2, $D2		# out[2] = (in[4] >> (64*2-shift*4)) ^ (in[5] << shift*5%64) ^ (in[6] << shift*6%64);
+
+	vpsrlq		\$18, $D6, $D6
+	vpsllq		\$11, $D7, $D7
+	vpsllq		\$40, $D8, $T3
+	vpaddq		$D6, $D7, $D7
+	vpaddq		$D7, $T3, $D3		# out[3] = (in[6] >> (64*3-shift*6)) ^ (in[7] << shift*7%64) ^ (in[8] << shift*8%64);
+
+	vpunpcklqdq	$D1, $D0, $T0		# T0 = [B2 A2 B0 A0]
+	vpunpcklqdq	$D3, $D2, $T1		# T1 = [D2 C2 D0 C0]
+	vpunpckhqdq	$D1, $D0, $T2		# T2 = [B3 A3 B1 A1]
+	vpunpckhqdq	$D3, $D2, $T3		# T3 = [D3 C3 D1 C1]
+
+	vperm2i128	\$0x20, $T1, $T0, $D0	# X0 = [D0 C0 B0 A0]
+	vperm2i128	\$0x20, $T3, $T2, $D1	# X1 = [D1 C1 B1 A1]
+	vperm2i128	\$0x31, $T1, $T0, $D2	# X2 = [D2 C2 B2 A2]
+	vperm2i128	\$0x31, $T3, $T2, $D3	# X3 = [D3 C3 B3 A3]
+
+	vmovdqa		$D0, 32*0(%rdi)
+	vmovdqa		$D1, 32*3(%rdi)
+	vmovdqa		$D2, 32*6(%rdi)
+	vmovdqa		$D3, 32*9(%rdi)
+
+	lea		32*9(%rsi), %rsi
+	lea		32*1(%rdi), %rdi
+
+	dec	%ecx
+	jnz	.Lconv_loop
+
+	vzeroupper
+___
+$code.=<<___	if ($win64);
+	movaps	16*0(%rsp), %xmm6
+	movaps	16*1(%rsp), %xmm7
+	movaps	16*2(%rsp), %xmm8
+	movaps	16*3(%rsp), %xmm9
+	movaps	16*4(%rsp), %xmm10
+	movaps	16*5(%rsp), %xmm11
+	movaps	16*6(%rsp), %xmm12
+	movaps	16*7(%rsp), %xmm13
+	movaps	16*8(%rsp), %xmm14
+	movaps	16*9(%rsp), %xmm15
+	lea	8+16*10(%rsp), %rsp
+___
+$code.=<<___;
+	ret
+.size	ecp_nistz256_avx2_convert_transpose_back,.-ecp_nistz256_avx2_convert_transpose_back
+___
+}
+{
+my ($r_ptr,$a_ptr,$b_ptr,$itr)=("%rdi","%rsi","%rdx","%ecx");
+my ($ACC0,$ACC1,$ACC2,$ACC3,$ACC4,$ACC5,$ACC6,$ACC7,$ACC8)=map("%ymm$_",(0..8));
+my ($B,$Y,$T0,$AND_MASK,$OVERFLOW)=map("%ymm$_",(9..13));
+
+sub NORMALIZE {
+my $ret=<<___;
+	vpsrlq		$digit_size, $ACC0, $T0
+	vpand		$AND_MASK, $ACC0, $ACC0
+	vpaddq		$T0, $ACC1, $ACC1
+
+	vpsrlq		$digit_size, $ACC1, $T0
+	vpand		$AND_MASK, $ACC1, $ACC1
+	vpaddq		$T0, $ACC2, $ACC2
+
+	vpsrlq		$digit_size, $ACC2, $T0
+	vpand		$AND_MASK, $ACC2, $ACC2
+	vpaddq		$T0, $ACC3, $ACC3
+
+	vpsrlq		$digit_size, $ACC3, $T0
+	vpand		$AND_MASK, $ACC3, $ACC3
+	vpaddq		$T0, $ACC4, $ACC4
+
+	vpsrlq		$digit_size, $ACC4, $T0
+	vpand		$AND_MASK, $ACC4, $ACC4
+	vpaddq		$T0, $ACC5, $ACC5
+
+	vpsrlq		$digit_size, $ACC5, $T0
+	vpand		$AND_MASK, $ACC5, $ACC5
+	vpaddq		$T0, $ACC6, $ACC6
+
+	vpsrlq		$digit_size, $ACC6, $T0
+	vpand		$AND_MASK, $ACC6, $ACC6
+	vpaddq		$T0, $ACC7, $ACC7
+
+	vpsrlq		$digit_size, $ACC7, $T0
+	vpand		$AND_MASK, $ACC7, $ACC7
+	vpaddq		$T0, $ACC8, $ACC8
+	#vpand		$AND_MASK, $ACC8, $ACC8
+___
+    $ret;
+}
+
+sub STORE {
+my $ret=<<___;
+	vmovdqa		$ACC0, 32*0(%rdi)
+	lea		160(%rdi), %rax		# size optimization
+	vmovdqa		$ACC1, 32*1(%rdi)
+	vmovdqa		$ACC2, 32*2(%rdi)
+	vmovdqa		$ACC3, 32*3(%rdi)
+	vmovdqa		$ACC4, 32*4-160(%rax)
+	vmovdqa		$ACC5, 32*5-160(%rax)
+	vmovdqa		$ACC6, 32*6-160(%rax)
+	vmovdqa		$ACC7, 32*7-160(%rax)
+	vmovdqa		$ACC8, 32*8-160(%rax)
+___
+    $ret;
+}
+
+$code.=<<___;
+.type	avx2_normalize,\@abi-omnipotent
+.align	32
+avx2_normalize:
+	vpsrlq		$digit_size, $ACC0, $T0
+	vpand		$AND_MASK, $ACC0, $ACC0
+	vpaddq		$T0, $ACC1, $ACC1
+
+	vpsrlq		$digit_size, $ACC1, $T0
+	vpand		$AND_MASK, $ACC1, $ACC1
+	vpaddq		$T0, $ACC2, $ACC2
+
+	vpsrlq		$digit_size, $ACC2, $T0
+	vpand		$AND_MASK, $ACC2, $ACC2
+	vpaddq		$T0, $ACC3, $ACC3
+
+	vpsrlq		$digit_size, $ACC3, $T0
+	vpand		$AND_MASK, $ACC3, $ACC3
+	vpaddq		$T0, $ACC4, $ACC4
+
+	vpsrlq		$digit_size, $ACC4, $T0
+	vpand		$AND_MASK, $ACC4, $ACC4
+	vpaddq		$T0, $ACC5, $ACC5
+
+	vpsrlq		$digit_size, $ACC5, $T0
+	vpand		$AND_MASK, $ACC5, $ACC5
+	vpaddq		$T0, $ACC6, $ACC6
+
+	vpsrlq		$digit_size, $ACC6, $T0
+	vpand		$AND_MASK, $ACC6, $ACC6
+	vpaddq		$T0, $ACC7, $ACC7
+
+	vpsrlq		$digit_size, $ACC7, $T0
+	vpand		$AND_MASK, $ACC7, $ACC7
+	vpaddq		$T0, $ACC8, $ACC8
+	#vpand		$AND_MASK, $ACC8, $ACC8
+
+	ret
+.size	avx2_normalize,.-avx2_normalize
+
+.type	avx2_normalize_n_store,\@abi-omnipotent
+.align	32
+avx2_normalize_n_store:
+	vpsrlq		$digit_size, $ACC0, $T0
+	vpand		$AND_MASK, $ACC0, $ACC0
+	vpaddq		$T0, $ACC1, $ACC1
+
+	vpsrlq		$digit_size, $ACC1, $T0
+	vpand		$AND_MASK, $ACC1, $ACC1
+	 vmovdqa	$ACC0, 32*0(%rdi)
+	 lea		160(%rdi), %rax		# size optimization
+	vpaddq		$T0, $ACC2, $ACC2
+
+	vpsrlq		$digit_size, $ACC2, $T0
+	vpand		$AND_MASK, $ACC2, $ACC2
+	 vmovdqa	$ACC1, 32*1(%rdi)
+	vpaddq		$T0, $ACC3, $ACC3
+
+	vpsrlq		$digit_size, $ACC3, $T0
+	vpand		$AND_MASK, $ACC3, $ACC3
+	 vmovdqa	$ACC2, 32*2(%rdi)
+	vpaddq		$T0, $ACC4, $ACC4
+
+	vpsrlq		$digit_size, $ACC4, $T0
+	vpand		$AND_MASK, $ACC4, $ACC4
+	 vmovdqa	$ACC3, 32*3(%rdi)
+	vpaddq		$T0, $ACC5, $ACC5
+
+	vpsrlq		$digit_size, $ACC5, $T0
+	vpand		$AND_MASK, $ACC5, $ACC5
+	 vmovdqa	$ACC4, 32*4-160(%rax)
+	vpaddq		$T0, $ACC6, $ACC6
+
+	vpsrlq		$digit_size, $ACC6, $T0
+	vpand		$AND_MASK, $ACC6, $ACC6
+	 vmovdqa	$ACC5, 32*5-160(%rax)
+	vpaddq		$T0, $ACC7, $ACC7
+
+	vpsrlq		$digit_size, $ACC7, $T0
+	vpand		$AND_MASK, $ACC7, $ACC7
+	 vmovdqa	$ACC6, 32*6-160(%rax)
+	vpaddq		$T0, $ACC8, $ACC8
+	#vpand		$AND_MASK, $ACC8, $ACC8
+	 vmovdqa	$ACC7, 32*7-160(%rax)
+	 vmovdqa	$ACC8, 32*8-160(%rax)
+
+	ret
+.size	avx2_normalize_n_store,.-avx2_normalize_n_store
+
+################################################################################
+# void avx2_mul_x4(void* RESULTx4, void *Ax4, void *Bx4);
+.type	avx2_mul_x4,\@abi-omnipotent
+.align	32
+avx2_mul_x4:
+	lea	.LAVX2_POLY(%rip), %rax
+
+	vpxor	$ACC0, $ACC0, $ACC0
+	vpxor	$ACC1, $ACC1, $ACC1
+	vpxor	$ACC2, $ACC2, $ACC2
+	vpxor	$ACC3, $ACC3, $ACC3
+	vpxor	$ACC4, $ACC4, $ACC4
+	vpxor	$ACC5, $ACC5, $ACC5
+	vpxor	$ACC6, $ACC6, $ACC6
+	vpxor	$ACC7, $ACC7, $ACC7
+
+	vmovdqa	32*7(%rax), %ymm14
+	vmovdqa	32*8(%rax), %ymm15
+
+	mov	$n_digits, $itr
+	lea	-512($a_ptr), $a_ptr	# strategic bias to control u-op density
+	jmp	.Lavx2_mul_x4_loop
+
+.align	32
+.Lavx2_mul_x4_loop:
+	vmovdqa		32*0($b_ptr), $B
+	lea		32*1($b_ptr), $b_ptr
+
+	vpmuludq	32*0+512($a_ptr), $B, $T0
+	vpmuludq	32*1+512($a_ptr), $B, $OVERFLOW	# borrow $OVERFLOW
+	vpaddq		$T0, $ACC0, $ACC0
+	vpmuludq	32*2+512($a_ptr), $B, $T0
+	vpaddq		$OVERFLOW, $ACC1, $ACC1
+	 vpand		$AND_MASK, $ACC0, $Y
+	vpmuludq	32*3+512($a_ptr), $B, $OVERFLOW
+	vpaddq		$T0, $ACC2, $ACC2
+	vpmuludq	32*4+512($a_ptr), $B, $T0
+	vpaddq		$OVERFLOW, $ACC3, $ACC3
+	vpmuludq	32*5+512($a_ptr), $B, $OVERFLOW
+	vpaddq		$T0, $ACC4, $ACC4
+	vpmuludq	32*6+512($a_ptr), $B, $T0
+	vpaddq		$OVERFLOW, $ACC5, $ACC5
+	vpmuludq	32*7+512($a_ptr), $B, $OVERFLOW
+	vpaddq		$T0, $ACC6, $ACC6
+
+	# Skip some multiplications, optimizing for the constant poly
+	vpmuludq	$AND_MASK, $Y, $T0
+	 vpaddq		$OVERFLOW, $ACC7, $ACC7
+	 vpmuludq	32*8+512($a_ptr), $B, $ACC8
+	vpaddq		$T0, $ACC0, $OVERFLOW
+	vpaddq		$T0, $ACC1, $ACC0
+	vpsrlq		$digit_size, $OVERFLOW, $OVERFLOW
+	vpaddq		$T0, $ACC2, $ACC1
+	vpmuludq	32*3(%rax), $Y, $T0
+	vpaddq		$OVERFLOW, $ACC0, $ACC0
+	vpaddq		$T0, $ACC3, $ACC2
+	.byte		0x67
+	vmovdqa		$ACC4, $ACC3
+	vpsllq		\$18, $Y, $OVERFLOW
+	.byte		0x67
+	vmovdqa		$ACC5, $ACC4
+	vpmuludq	%ymm14, $Y, $T0
+	vpaddq		$OVERFLOW, $ACC6, $ACC5
+	vpmuludq	%ymm15, $Y, $OVERFLOW
+	vpaddq		$T0, $ACC7, $ACC6
+	vpaddq		$OVERFLOW, $ACC8, $ACC7
+
+	dec	$itr
+	jnz	.Lavx2_mul_x4_loop
+
+	vpxor	$ACC8, $ACC8, $ACC8
+
+	ret
+.size	avx2_mul_x4,.-avx2_mul_x4
+
+# Function optimized for the constant 1
+################################################################################
+# void avx2_mul_by1_x4(void* RESULTx4, void *Ax4);
+.type	avx2_mul_by1_x4,\@abi-omnipotent
+.align	32
+avx2_mul_by1_x4:
+	lea	.LAVX2_POLY(%rip), %rax
+
+	vpxor	$ACC0, $ACC0, $ACC0
+	vpxor	$ACC1, $ACC1, $ACC1
+	vpxor	$ACC2, $ACC2, $ACC2
+	vpxor	$ACC3, $ACC3, $ACC3
+	vpxor	$ACC4, $ACC4, $ACC4
+	vpxor	$ACC5, $ACC5, $ACC5
+	vpxor	$ACC6, $ACC6, $ACC6
+	vpxor	$ACC7, $ACC7, $ACC7
+	vpxor	$ACC8, $ACC8, $ACC8
+
+	vmovdqa	32*3+.LONE(%rip), %ymm14
+	vmovdqa	32*7+.LONE(%rip), %ymm15
+
+	mov	$n_digits, $itr
+	jmp	.Lavx2_mul_by1_x4_loop
+
+.align	32
+.Lavx2_mul_by1_x4_loop:
+	vmovdqa		32*0($a_ptr), $B
+	.byte		0x48,0x8d,0xb6,0x20,0,0,0	# lea	32*1($a_ptr), $a_ptr
+
+	vpsllq		\$5, $B, $OVERFLOW
+	vpmuludq	%ymm14, $B, $T0
+	vpaddq		$OVERFLOW, $ACC0, $ACC0
+	vpaddq		$T0, $ACC3, $ACC3
+	.byte		0x67
+	vpmuludq	$AND_MASK, $B, $T0
+	vpand		$AND_MASK, $ACC0, $Y
+	vpaddq		$T0, $ACC4, $ACC4
+	vpaddq		$T0, $ACC5, $ACC5
+	vpaddq		$T0, $ACC6, $ACC6
+	vpsllq		\$23, $B, $T0
+
+	.byte		0x67,0x67
+	vpmuludq	%ymm15, $B, $OVERFLOW
+	vpsubq		$T0, $ACC6, $ACC6
+
+	vpmuludq	$AND_MASK, $Y, $T0
+	vpaddq		$OVERFLOW, $ACC7, $ACC7
+	vpaddq		$T0, $ACC0, $OVERFLOW
+	vpaddq		$T0, $ACC1, $ACC0
+	.byte		0x67,0x67
+	vpsrlq		$digit_size, $OVERFLOW, $OVERFLOW
+	vpaddq		$T0, $ACC2, $ACC1
+	vpmuludq	32*3(%rax), $Y, $T0
+	vpaddq		$OVERFLOW, $ACC0, $ACC0
+	vpaddq		$T0, $ACC3, $ACC2
+	vmovdqa		$ACC4, $ACC3
+	vpsllq		\$18, $Y, $OVERFLOW
+	vmovdqa		$ACC5, $ACC4
+	vpmuludq	32*7(%rax), $Y, $T0
+	vpaddq		$OVERFLOW, $ACC6, $ACC5
+	vpaddq		$T0, $ACC7, $ACC6
+	vpmuludq	32*8(%rax), $Y, $ACC7
+
+	dec	$itr
+	jnz	.Lavx2_mul_by1_x4_loop
+
+	ret
+.size	avx2_mul_by1_x4,.-avx2_mul_by1_x4
+
+################################################################################
+# void avx2_sqr_x4(void* RESULTx4, void *Ax4, void *Bx4);
+.type	avx2_sqr_x4,\@abi-omnipotent
+.align	32
+avx2_sqr_x4:
+	lea		.LAVX2_POLY(%rip), %rax
+
+	vmovdqa		32*7(%rax), %ymm14
+	vmovdqa		32*8(%rax), %ymm15
+
+	vmovdqa		32*0($a_ptr), $B
+	vmovdqa		32*1($a_ptr), $ACC1
+	vmovdqa		32*2($a_ptr), $ACC2
+	vmovdqa		32*3($a_ptr), $ACC3
+	vmovdqa		32*4($a_ptr), $ACC4
+	vmovdqa		32*5($a_ptr), $ACC5
+	vmovdqa		32*6($a_ptr), $ACC6
+	vmovdqa		32*7($a_ptr), $ACC7
+	vpaddq		$ACC1, $ACC1, $ACC1	# 2*$ACC0..7
+	vmovdqa		32*8($a_ptr), $ACC8
+	vpaddq		$ACC2, $ACC2, $ACC2
+	vmovdqa		$ACC1, 32*0(%rcx)
+	vpaddq		$ACC3, $ACC3, $ACC3
+	vmovdqa		$ACC2, 32*1(%rcx)
+	vpaddq		$ACC4, $ACC4, $ACC4
+	vmovdqa		$ACC3, 32*2(%rcx)
+	vpaddq		$ACC5, $ACC5, $ACC5
+	vmovdqa		$ACC4, 32*3(%rcx)
+	vpaddq		$ACC6, $ACC6, $ACC6
+	vmovdqa		$ACC5, 32*4(%rcx)
+	vpaddq		$ACC7, $ACC7, $ACC7
+	vmovdqa		$ACC6, 32*5(%rcx)
+	vpaddq		$ACC8, $ACC8, $ACC8
+	vmovdqa		$ACC7, 32*6(%rcx)
+	vmovdqa		$ACC8, 32*7(%rcx)
+
+	#itr		1
+	vpmuludq	$B, $B, $ACC0
+	vpmuludq	$B, $ACC1, $ACC1
+	 vpand		$AND_MASK, $ACC0, $Y
+	vpmuludq	$B, $ACC2, $ACC2
+	vpmuludq	$B, $ACC3, $ACC3
+	vpmuludq	$B, $ACC4, $ACC4
+	vpmuludq	$B, $ACC5, $ACC5
+	vpmuludq	$B, $ACC6, $ACC6
+	 vpmuludq	$AND_MASK, $Y, $T0
+	vpmuludq	$B, $ACC7, $ACC7
+	vpmuludq	$B, $ACC8, $ACC8
+	 vmovdqa	32*1($a_ptr), $B
+
+	vpaddq		$T0, $ACC0, $OVERFLOW
+	vpaddq		$T0, $ACC1, $ACC0
+	vpsrlq		$digit_size, $OVERFLOW, $OVERFLOW
+	vpaddq		$T0, $ACC2, $ACC1
+	vpmuludq	32*3(%rax), $Y, $T0
+	vpaddq		$OVERFLOW, $ACC0, $ACC0
+	vpaddq		$T0, $ACC3, $ACC2
+	vmovdqa		$ACC4, $ACC3
+	vpsllq		\$18, $Y, $T0
+	vmovdqa		$ACC5, $ACC4
+	vpmuludq	%ymm14, $Y, $OVERFLOW
+	vpaddq		$T0, $ACC6, $ACC5
+	vpmuludq	%ymm15, $Y, $T0
+	vpaddq		$OVERFLOW, $ACC7, $ACC6
+	vpaddq		$T0, $ACC8, $ACC7
+
+	#itr		2
+	vpmuludq	$B, $B, $OVERFLOW
+	 vpand		$AND_MASK, $ACC0, $Y
+	vpmuludq	32*1(%rcx), $B, $T0
+	vpaddq		$OVERFLOW, $ACC1, $ACC1
+	vpmuludq	32*2(%rcx), $B, $OVERFLOW
+	vpaddq		$T0, $ACC2, $ACC2
+	vpmuludq	32*3(%rcx), $B, $T0
+	vpaddq		$OVERFLOW, $ACC3, $ACC3
+	vpmuludq	32*4(%rcx), $B, $OVERFLOW
+	vpaddq		$T0, $ACC4, $ACC4
+	vpmuludq	32*5(%rcx), $B, $T0
+	vpaddq		$OVERFLOW, $ACC5, $ACC5
+	vpmuludq	32*6(%rcx), $B, $OVERFLOW
+	vpaddq		$T0, $ACC6, $ACC6
+
+	vpmuludq	$AND_MASK, $Y, $T0
+	 vpaddq		$OVERFLOW, $ACC7, $ACC7
+	 vpmuludq	32*7(%rcx), $B, $ACC8
+	 vmovdqa	32*2($a_ptr), $B
+	vpaddq		$T0, $ACC0, $OVERFLOW
+	vpaddq		$T0, $ACC1, $ACC0
+	vpsrlq		$digit_size, $OVERFLOW, $OVERFLOW
+	vpaddq		$T0, $ACC2, $ACC1
+	vpmuludq	32*3(%rax), $Y, $T0
+	vpaddq		$OVERFLOW, $ACC0, $ACC0
+	vpaddq		$T0, $ACC3, $ACC2
+	vmovdqa		$ACC4, $ACC3
+	vpsllq		\$18, $Y, $T0
+	vmovdqa		$ACC5, $ACC4
+	vpmuludq	%ymm14, $Y, $OVERFLOW
+	vpaddq		$T0, $ACC6, $ACC5
+	vpmuludq	%ymm15, $Y, $T0
+	vpaddq		$OVERFLOW, $ACC7, $ACC6
+	vpaddq		$T0, $ACC8, $ACC7
+
+	#itr		3
+	vpmuludq	$B, $B, $T0
+	 vpand		$AND_MASK, $ACC0, $Y
+	vpmuludq	32*2(%rcx), $B, $OVERFLOW
+	vpaddq		$T0, $ACC2, $ACC2
+	vpmuludq	32*3(%rcx), $B, $T0
+	vpaddq		$OVERFLOW, $ACC3, $ACC3
+	vpmuludq	32*4(%rcx), $B, $OVERFLOW
+	vpaddq		$T0, $ACC4, $ACC4
+	vpmuludq	32*5(%rcx), $B, $T0
+	vpaddq		$OVERFLOW, $ACC5, $ACC5
+	vpmuludq	32*6(%rcx), $B, $OVERFLOW
+	vpaddq		$T0, $ACC6, $ACC6
+
+	vpmuludq	$AND_MASK, $Y, $T0
+	 vpaddq		$OVERFLOW, $ACC7, $ACC7
+	 vpmuludq	32*7(%rcx), $B, $ACC8
+	 vmovdqa	32*3($a_ptr), $B
+	vpaddq		$T0, $ACC0, $OVERFLOW
+	vpaddq		$T0, $ACC1, $ACC0
+	vpsrlq		$digit_size, $OVERFLOW, $OVERFLOW
+	vpaddq		$T0, $ACC2, $ACC1
+	vpmuludq	32*3(%rax), $Y, $T0
+	vpaddq		$OVERFLOW, $ACC0, $ACC0
+	vpaddq		$T0, $ACC3, $ACC2
+	vmovdqa		$ACC4, $ACC3
+	vpsllq		\$18, $Y, $T0
+	vmovdqa		$ACC5, $ACC4
+	vpmuludq	%ymm14, $Y, $OVERFLOW
+	vpaddq		$T0, $ACC6, $ACC5
+	vpmuludq	%ymm15, $Y, $T0
+	 vpand		$AND_MASK, $ACC0, $Y
+	vpaddq		$OVERFLOW, $ACC7, $ACC6
+	vpaddq		$T0, $ACC8, $ACC7
+
+	#itr		4
+	vpmuludq	$B, $B, $OVERFLOW
+	vpmuludq	32*3(%rcx), $B, $T0
+	vpaddq		$OVERFLOW, $ACC3, $ACC3
+	vpmuludq	32*4(%rcx), $B, $OVERFLOW
+	vpaddq		$T0, $ACC4, $ACC4
+	vpmuludq	32*5(%rcx), $B, $T0
+	vpaddq		$OVERFLOW, $ACC5, $ACC5
+	vpmuludq	32*6(%rcx), $B, $OVERFLOW
+	vpaddq		$T0, $ACC6, $ACC6
+
+	vpmuludq	$AND_MASK, $Y, $T0
+	 vpaddq		$OVERFLOW, $ACC7, $ACC7
+	 vpmuludq	32*7(%rcx), $B, $ACC8
+	 vmovdqa	32*4($a_ptr), $B
+	vpaddq		$T0, $ACC0, $OVERFLOW
+	vpaddq		$T0, $ACC1, $ACC0
+	vpsrlq		$digit_size, $OVERFLOW, $OVERFLOW
+	vpaddq		$T0, $ACC2, $ACC1
+	vpmuludq	32*3(%rax), $Y, $T0
+	vpaddq		$OVERFLOW, $ACC0, $ACC0
+	vpaddq		$T0, $ACC3, $ACC2
+	vmovdqa		$ACC4, $ACC3
+	vpsllq		\$18, $Y, $T0
+	vmovdqa		$ACC5, $ACC4
+	vpmuludq	%ymm14, $Y, $OVERFLOW
+	vpaddq		$T0, $ACC6, $ACC5
+	vpmuludq	%ymm15, $Y, $T0
+	 vpand		$AND_MASK, $ACC0, $Y
+	vpaddq		$OVERFLOW, $ACC7, $ACC6
+	vpaddq		$T0, $ACC8, $ACC7
+
+	#itr		5
+	vpmuludq	$B, $B, $T0
+	vpmuludq	32*4(%rcx), $B, $OVERFLOW
+	vpaddq		$T0, $ACC4, $ACC4
+	vpmuludq	32*5(%rcx), $B, $T0
+	vpaddq		$OVERFLOW, $ACC5, $ACC5
+	vpmuludq	32*6(%rcx), $B, $OVERFLOW
+	vpaddq		$T0, $ACC6, $ACC6
+
+	vpmuludq	$AND_MASK, $Y, $T0
+	 vpaddq		$OVERFLOW, $ACC7, $ACC7
+	 vpmuludq	32*7(%rcx), $B, $ACC8
+	 vmovdqa	32*5($a_ptr), $B
+	vpaddq		$T0, $ACC0, $OVERFLOW
+	vpsrlq		$digit_size, $OVERFLOW, $OVERFLOW
+	vpaddq		$T0, $ACC1, $ACC0
+	vpaddq		$T0, $ACC2, $ACC1
+	vpmuludq	32*3+.LAVX2_POLY(%rip), $Y, $T0
+	vpaddq		$OVERFLOW, $ACC0, $ACC0
+	vpaddq		$T0, $ACC3, $ACC2
+	vmovdqa		$ACC4, $ACC3
+	vpsllq		\$18, $Y, $T0
+	vmovdqa		$ACC5, $ACC4
+	vpmuludq	%ymm14, $Y, $OVERFLOW
+	vpaddq		$T0, $ACC6, $ACC5
+	vpmuludq	%ymm15, $Y, $T0
+	 vpand		$AND_MASK, $ACC0, $Y
+	vpaddq		$OVERFLOW, $ACC7, $ACC6
+	vpaddq		$T0, $ACC8, $ACC7
+
+	#itr		6
+	vpmuludq	$B, $B, $OVERFLOW
+	vpmuludq	32*5(%rcx), $B, $T0
+	vpaddq		$OVERFLOW, $ACC5, $ACC5
+	vpmuludq	32*6(%rcx), $B, $OVERFLOW
+	vpaddq		$T0, $ACC6, $ACC6
+
+	vpmuludq	$AND_MASK, $Y, $T0
+	 vpaddq		$OVERFLOW, $ACC7, $ACC7
+	 vpmuludq	32*7(%rcx), $B, $ACC8
+	 vmovdqa	32*6($a_ptr), $B
+	vpaddq		$T0, $ACC0, $OVERFLOW
+	vpaddq		$T0, $ACC1, $ACC0
+	vpsrlq		$digit_size, $OVERFLOW, $OVERFLOW
+	vpaddq		$T0, $ACC2, $ACC1
+	vpmuludq	32*3(%rax), $Y, $T0
+	vpaddq		$OVERFLOW, $ACC0, $ACC0
+	vpaddq		$T0, $ACC3, $ACC2
+	vmovdqa		$ACC4, $ACC3
+	vpsllq		\$18, $Y, $T0
+	vmovdqa		$ACC5, $ACC4
+	vpmuludq	%ymm14, $Y, $OVERFLOW
+	vpaddq		$T0, $ACC6, $ACC5
+	vpmuludq	%ymm15, $Y, $T0
+	 vpand		$AND_MASK, $ACC0, $Y
+	vpaddq		$OVERFLOW, $ACC7, $ACC6
+	vpaddq		$T0, $ACC8, $ACC7
+
+	#itr		7
+	vpmuludq	$B, $B, $T0
+	vpmuludq	32*6(%rcx), $B, $OVERFLOW
+	vpaddq		$T0, $ACC6, $ACC6
+
+	vpmuludq	$AND_MASK, $Y, $T0
+	 vpaddq		$OVERFLOW, $ACC7, $ACC7
+	 vpmuludq	32*7(%rcx), $B, $ACC8
+	 vmovdqa	32*7($a_ptr), $B
+	vpaddq		$T0, $ACC0, $OVERFLOW
+	vpsrlq		$digit_size, $OVERFLOW, $OVERFLOW
+	vpaddq		$T0, $ACC1, $ACC0
+	vpaddq		$T0, $ACC2, $ACC1
+	vpmuludq	32*3(%rax), $Y, $T0
+	vpaddq		$OVERFLOW, $ACC0, $ACC0
+	vpaddq		$T0, $ACC3, $ACC2
+	vmovdqa		$ACC4, $ACC3
+	vpsllq		\$18, $Y, $T0
+	vmovdqa		$ACC5, $ACC4
+	vpmuludq	%ymm14, $Y, $OVERFLOW
+	vpaddq		$T0, $ACC6, $ACC5
+	vpmuludq	%ymm15, $Y, $T0
+	 vpand		$AND_MASK, $ACC0, $Y
+	vpaddq		$OVERFLOW, $ACC7, $ACC6
+	vpaddq		$T0, $ACC8, $ACC7
+
+	#itr		8
+	vpmuludq	$B, $B, $OVERFLOW
+
+	vpmuludq	$AND_MASK, $Y, $T0
+	 vpaddq		$OVERFLOW, $ACC7, $ACC7
+	 vpmuludq	32*7(%rcx), $B, $ACC8
+	 vmovdqa	32*8($a_ptr), $B
+	vpaddq		$T0, $ACC0, $OVERFLOW
+	vpsrlq		$digit_size, $OVERFLOW, $OVERFLOW
+	vpaddq		$T0, $ACC1, $ACC0
+	vpaddq		$T0, $ACC2, $ACC1
+	vpmuludq	32*3(%rax), $Y, $T0
+	vpaddq		$OVERFLOW, $ACC0, $ACC0
+	vpaddq		$T0, $ACC3, $ACC2
+	vmovdqa		$ACC4, $ACC3
+	vpsllq		\$18, $Y, $T0
+	vmovdqa		$ACC5, $ACC4
+	vpmuludq	%ymm14, $Y, $OVERFLOW
+	vpaddq		$T0, $ACC6, $ACC5
+	vpmuludq	%ymm15, $Y, $T0
+	 vpand		$AND_MASK, $ACC0, $Y
+	vpaddq		$OVERFLOW, $ACC7, $ACC6
+	vpaddq		$T0, $ACC8, $ACC7
+
+	#itr		9
+	vpmuludq	$B, $B, $ACC8
+
+	vpmuludq	$AND_MASK, $Y, $T0
+	vpaddq		$T0, $ACC0, $OVERFLOW
+	vpsrlq		$digit_size, $OVERFLOW, $OVERFLOW
+	vpaddq		$T0, $ACC1, $ACC0
+	vpaddq		$T0, $ACC2, $ACC1
+	vpmuludq	32*3(%rax), $Y, $T0
+	vpaddq		$OVERFLOW, $ACC0, $ACC0
+	vpaddq		$T0, $ACC3, $ACC2
+	vmovdqa		$ACC4, $ACC3
+	vpsllq		\$18, $Y, $T0
+	vmovdqa		$ACC5, $ACC4
+	vpmuludq	%ymm14, $Y, $OVERFLOW
+	vpaddq		$T0, $ACC6, $ACC5
+	vpmuludq	%ymm15, $Y, $T0
+	vpaddq		$OVERFLOW, $ACC7, $ACC6
+	vpaddq		$T0, $ACC8, $ACC7
+
+	vpxor		$ACC8, $ACC8, $ACC8
+
+	ret
+.size	avx2_sqr_x4,.-avx2_sqr_x4
+
+################################################################################
+# void avx2_sub_x4(void* RESULTx4, void *Ax4, void *Bx4);
+.type	avx2_sub_x4,\@abi-omnipotent
+.align	32
+avx2_sub_x4:
+	vmovdqa	32*0($a_ptr), $ACC0
+	lea	160($a_ptr), $a_ptr
+	lea	.LAVX2_POLY_x8+128(%rip), %rax
+	lea	128($b_ptr), $b_ptr
+	vmovdqa	32*1-160($a_ptr), $ACC1
+	vmovdqa	32*2-160($a_ptr), $ACC2
+	vmovdqa	32*3-160($a_ptr), $ACC3
+	vmovdqa	32*4-160($a_ptr), $ACC4
+	vmovdqa	32*5-160($a_ptr), $ACC5
+	vmovdqa	32*6-160($a_ptr), $ACC6
+	vmovdqa	32*7-160($a_ptr), $ACC7
+	vmovdqa	32*8-160($a_ptr), $ACC8
+
+	vpaddq	32*0-128(%rax), $ACC0, $ACC0
+	vpaddq	32*1-128(%rax), $ACC1, $ACC1
+	vpaddq	32*2-128(%rax), $ACC2, $ACC2
+	vpaddq	32*3-128(%rax), $ACC3, $ACC3
+	vpaddq	32*4-128(%rax), $ACC4, $ACC4
+	vpaddq	32*5-128(%rax), $ACC5, $ACC5
+	vpaddq	32*6-128(%rax), $ACC6, $ACC6
+	vpaddq	32*7-128(%rax), $ACC7, $ACC7
+	vpaddq	32*8-128(%rax), $ACC8, $ACC8
+
+	vpsubq	32*0-128($b_ptr), $ACC0, $ACC0
+	vpsubq	32*1-128($b_ptr), $ACC1, $ACC1
+	vpsubq	32*2-128($b_ptr), $ACC2, $ACC2
+	vpsubq	32*3-128($b_ptr), $ACC3, $ACC3
+	vpsubq	32*4-128($b_ptr), $ACC4, $ACC4
+	vpsubq	32*5-128($b_ptr), $ACC5, $ACC5
+	vpsubq	32*6-128($b_ptr), $ACC6, $ACC6
+	vpsubq	32*7-128($b_ptr), $ACC7, $ACC7
+	vpsubq	32*8-128($b_ptr), $ACC8, $ACC8
+
+	ret
+.size	avx2_sub_x4,.-avx2_sub_x4
+
+.type	avx2_select_n_store,\@abi-omnipotent
+.align	32
+avx2_select_n_store:
+	vmovdqa	`8+32*9*8`(%rsp), $Y
+	vpor	`8+32*9*8+32`(%rsp), $Y, $Y
+
+	vpandn	$ACC0, $Y, $ACC0
+	vpandn	$ACC1, $Y, $ACC1
+	vpandn	$ACC2, $Y, $ACC2
+	vpandn	$ACC3, $Y, $ACC3
+	vpandn	$ACC4, $Y, $ACC4
+	vpandn	$ACC5, $Y, $ACC5
+	vpandn	$ACC6, $Y, $ACC6
+	vmovdqa	`8+32*9*8+32`(%rsp), $B
+	vpandn	$ACC7, $Y, $ACC7
+	vpandn	`8+32*9*8`(%rsp), $B, $B
+	vpandn	$ACC8, $Y, $ACC8
+
+	vpand	32*0(%rsi), $B, $T0
+	lea	160(%rsi), %rax
+	vpand	32*1(%rsi), $B, $Y
+	vpxor	$T0, $ACC0, $ACC0
+	vpand	32*2(%rsi), $B, $T0
+	vpxor	$Y, $ACC1, $ACC1
+	vpand	32*3(%rsi), $B, $Y
+	vpxor	$T0, $ACC2, $ACC2
+	vpand	32*4-160(%rax), $B, $T0
+	vpxor	$Y, $ACC3, $ACC3
+	vpand	32*5-160(%rax), $B, $Y
+	vpxor	$T0, $ACC4, $ACC4
+	vpand	32*6-160(%rax), $B, $T0
+	vpxor	$Y, $ACC5, $ACC5
+	vpand	32*7-160(%rax), $B, $Y
+	vpxor	$T0, $ACC6, $ACC6
+	vpand	32*8-160(%rax), $B, $T0
+	vmovdqa	`8+32*9*8+32`(%rsp), $B
+	vpxor	$Y, $ACC7, $ACC7
+
+	vpand	32*0(%rdx), $B, $Y
+	lea	160(%rdx), %rax
+	vpxor	$T0, $ACC8, $ACC8
+	vpand	32*1(%rdx), $B, $T0
+	vpxor	$Y, $ACC0, $ACC0
+	vpand	32*2(%rdx), $B, $Y
+	vpxor	$T0, $ACC1, $ACC1
+	vpand	32*3(%rdx), $B, $T0
+	vpxor	$Y, $ACC2, $ACC2
+	vpand	32*4-160(%rax), $B, $Y
+	vpxor	$T0, $ACC3, $ACC3
+	vpand	32*5-160(%rax), $B, $T0
+	vpxor	$Y, $ACC4, $ACC4
+	vpand	32*6-160(%rax), $B, $Y
+	vpxor	$T0, $ACC5, $ACC5
+	vpand	32*7-160(%rax), $B, $T0
+	vpxor	$Y, $ACC6, $ACC6
+	vpand	32*8-160(%rax), $B, $Y
+	vpxor	$T0, $ACC7, $ACC7
+	vpxor	$Y, $ACC8, $ACC8
+	`&STORE`
+
+	ret
+.size	avx2_select_n_store,.-avx2_select_n_store
+___
+$code.=<<___	if (0);				# inlined
+################################################################################
+# void avx2_mul_by2_x4(void* RESULTx4, void *Ax4);
+.type	avx2_mul_by2_x4,\@abi-omnipotent
+.align	32
+avx2_mul_by2_x4:
+	vmovdqa	32*0($a_ptr), $ACC0
+	lea	160($a_ptr), %rax
+	vmovdqa	32*1($a_ptr), $ACC1
+	vmovdqa	32*2($a_ptr), $ACC2
+	vmovdqa	32*3($a_ptr), $ACC3
+	vmovdqa	32*4-160(%rax), $ACC4
+	vmovdqa	32*5-160(%rax), $ACC5
+	vmovdqa	32*6-160(%rax), $ACC6
+	vmovdqa	32*7-160(%rax), $ACC7
+	vmovdqa	32*8-160(%rax), $ACC8
+
+	vpaddq	$ACC0, $ACC0, $ACC0
+	vpaddq	$ACC1, $ACC1, $ACC1
+	vpaddq	$ACC2, $ACC2, $ACC2
+	vpaddq	$ACC3, $ACC3, $ACC3
+	vpaddq	$ACC4, $ACC4, $ACC4
+	vpaddq	$ACC5, $ACC5, $ACC5
+	vpaddq	$ACC6, $ACC6, $ACC6
+	vpaddq	$ACC7, $ACC7, $ACC7
+	vpaddq	$ACC8, $ACC8, $ACC8
+
+	ret
+.size	avx2_mul_by2_x4,.-avx2_mul_by2_x4
+___
+my ($r_ptr_in,$a_ptr_in,$b_ptr_in)=("%rdi","%rsi","%rdx");
+my ($r_ptr,$a_ptr,$b_ptr)=("%r8","%r9","%r10");
+
+$code.=<<___;
+################################################################################
+# void ecp_nistz256_avx2_point_add_affine_x4(void* RESULTx4, void *Ax4, void *Bx4);
+.globl	ecp_nistz256_avx2_point_add_affine_x4
+.type	ecp_nistz256_avx2_point_add_affine_x4,\@function,3
+.align	32
+ecp_nistz256_avx2_point_add_affine_x4:
+	mov	%rsp, %rax
+	push    %rbp
+	vzeroupper
+___
+$code.=<<___	if ($win64);
+	lea	-16*10(%rsp), %rsp
+	vmovaps	%xmm6, -8-16*10(%rax)
+	vmovaps	%xmm7, -8-16*9(%rax)
+	vmovaps	%xmm8, -8-16*8(%rax)
+	vmovaps	%xmm9, -8-16*7(%rax)
+	vmovaps	%xmm10, -8-16*6(%rax)
+	vmovaps	%xmm11, -8-16*5(%rax)
+	vmovaps	%xmm12, -8-16*4(%rax)
+	vmovaps	%xmm13, -8-16*3(%rax)
+	vmovaps	%xmm14, -8-16*2(%rax)
+	vmovaps	%xmm15, -8-16*1(%rax)
+___
+$code.=<<___;
+	lea	-8(%rax), %rbp
+
+# Result + 32*0 = Result.X
+# Result + 32*9 = Result.Y
+# Result + 32*18 = Result.Z
+
+# A + 32*0 = A.X
+# A + 32*9 = A.Y
+# A + 32*18 = A.Z
+
+# B + 32*0 = B.X
+# B + 32*9 = B.Y
+
+	sub	\$`32*9*8+32*2+32*8`, %rsp
+	and	\$-64, %rsp
+
+	mov	$r_ptr_in, $r_ptr
+	mov	$a_ptr_in, $a_ptr
+	mov	$b_ptr_in, $b_ptr
+
+	vmovdqa	32*0($a_ptr_in), %ymm0
+	vmovdqa	.LAVX2_AND_MASK(%rip), $AND_MASK
+	vpxor	%ymm1, %ymm1, %ymm1
+	lea	256($a_ptr_in), %rax		# size optimization
+	vpor	32*1($a_ptr_in), %ymm0, %ymm0
+	vpor	32*2($a_ptr_in), %ymm0, %ymm0
+	vpor	32*3($a_ptr_in), %ymm0, %ymm0
+	vpor	32*4-256(%rax), %ymm0, %ymm0
+	lea	256(%rax), %rcx			# size optimization
+	vpor	32*5-256(%rax), %ymm0, %ymm0
+	vpor	32*6-256(%rax), %ymm0, %ymm0
+	vpor	32*7-256(%rax), %ymm0, %ymm0
+	vpor	32*8-256(%rax), %ymm0, %ymm0
+	vpor	32*9-256(%rax), %ymm0, %ymm0
+	vpor	32*10-256(%rax), %ymm0, %ymm0
+	vpor	32*11-256(%rax), %ymm0, %ymm0
+	vpor	32*12-512(%rcx), %ymm0, %ymm0
+	vpor	32*13-512(%rcx), %ymm0, %ymm0
+	vpor	32*14-512(%rcx), %ymm0, %ymm0
+	vpor	32*15-512(%rcx), %ymm0, %ymm0
+	vpor	32*16-512(%rcx), %ymm0, %ymm0
+	vpor	32*17-512(%rcx), %ymm0, %ymm0
+	vpcmpeqq %ymm1, %ymm0, %ymm0
+	vmovdqa	%ymm0, `32*9*8`(%rsp)
+
+	vpxor	%ymm1, %ymm1, %ymm1
+	vmovdqa	32*0($b_ptr), %ymm0
+	lea	256($b_ptr), %rax		# size optimization
+	vpor	32*1($b_ptr), %ymm0, %ymm0
+	vpor	32*2($b_ptr), %ymm0, %ymm0
+	vpor	32*3($b_ptr), %ymm0, %ymm0
+	vpor	32*4-256(%rax), %ymm0, %ymm0
+	lea	256(%rax), %rcx			# size optimization
+	vpor	32*5-256(%rax), %ymm0, %ymm0
+	vpor	32*6-256(%rax), %ymm0, %ymm0
+	vpor	32*7-256(%rax), %ymm0, %ymm0
+	vpor	32*8-256(%rax), %ymm0, %ymm0
+	vpor	32*9-256(%rax), %ymm0, %ymm0
+	vpor	32*10-256(%rax), %ymm0, %ymm0
+	vpor	32*11-256(%rax), %ymm0, %ymm0
+	vpor	32*12-512(%rcx), %ymm0, %ymm0
+	vpor	32*13-512(%rcx), %ymm0, %ymm0
+	vpor	32*14-512(%rcx), %ymm0, %ymm0
+	vpor	32*15-512(%rcx), %ymm0, %ymm0
+	vpor	32*16-512(%rcx), %ymm0, %ymm0
+	vpor	32*17-512(%rcx), %ymm0, %ymm0
+	vpcmpeqq %ymm1, %ymm0, %ymm0
+	vmovdqa	%ymm0, `32*9*8+32`(%rsp)
+
+	#	Z1^2 = Z1*Z1
+	lea	`32*9*2`($a_ptr), %rsi
+	lea	`32*9*2`(%rsp), %rdi
+	lea	`32*9*8+32*2`(%rsp), %rcx	# temporary vector
+	call	avx2_sqr_x4
+	call	avx2_normalize_n_store
+
+	#	U2 = X2*Z1^2
+	lea	`32*9*0`($b_ptr), %rsi
+	lea	`32*9*2`(%rsp), %rdx
+	lea	`32*9*0`(%rsp), %rdi
+	call	avx2_mul_x4
+	#call	avx2_normalize
+	`&STORE`
+
+	#	S2 = Z1*Z1^2 = Z1^3
+	lea	`32*9*2`($a_ptr), %rsi
+	lea	`32*9*2`(%rsp), %rdx
+	lea	`32*9*1`(%rsp), %rdi
+	call	avx2_mul_x4
+	call	avx2_normalize_n_store
+
+	#	S2 = S2*Y2 = Y2*Z1^3
+	lea	`32*9*1`($b_ptr), %rsi
+	lea	`32*9*1`(%rsp), %rdx
+	lea	`32*9*1`(%rsp), %rdi
+	call	avx2_mul_x4
+	call	avx2_normalize_n_store
+
+	#	H = U2 - U1 = U2 - X1
+	lea	`32*9*0`(%rsp), %rsi
+	lea	`32*9*0`($a_ptr), %rdx
+	lea	`32*9*3`(%rsp), %rdi
+	call	avx2_sub_x4
+	call	avx2_normalize_n_store
+
+	#	R = S2 - S1 = S2 - Y1
+	lea	`32*9*1`(%rsp), %rsi
+	lea	`32*9*1`($a_ptr), %rdx
+	lea	`32*9*4`(%rsp), %rdi
+	call	avx2_sub_x4
+	call	avx2_normalize_n_store
+
+	#	Z3 = H*Z1*Z2
+	lea	`32*9*3`(%rsp), %rsi
+	lea	`32*9*2`($a_ptr), %rdx
+	lea	`32*9*2`($r_ptr), %rdi
+	call	avx2_mul_x4
+	call	avx2_normalize
+
+	lea	.LONE(%rip), %rsi
+	lea	`32*9*2`($a_ptr), %rdx
+	call	avx2_select_n_store
+
+	#	R^2 = R^2
+	lea	`32*9*4`(%rsp), %rsi
+	lea	`32*9*6`(%rsp), %rdi
+	lea	`32*9*8+32*2`(%rsp), %rcx	# temporary vector
+	call	avx2_sqr_x4
+	call	avx2_normalize_n_store
+
+	#	H^2 = H^2
+	lea	`32*9*3`(%rsp), %rsi
+	lea	`32*9*5`(%rsp), %rdi
+	call	avx2_sqr_x4
+	call	avx2_normalize_n_store
+
+	#	H^3 = H^2*H
+	lea	`32*9*3`(%rsp), %rsi
+	lea	`32*9*5`(%rsp), %rdx
+	lea	`32*9*7`(%rsp), %rdi
+	call	avx2_mul_x4
+	call	avx2_normalize_n_store
+
+	#	U2 = U1*H^2
+	lea	`32*9*0`($a_ptr), %rsi
+	lea	`32*9*5`(%rsp), %rdx
+	lea	`32*9*0`(%rsp), %rdi
+	call	avx2_mul_x4
+	#call	avx2_normalize
+	`&STORE`
+
+	#	Hsqr = U2*2
+	#lea	32*9*0(%rsp), %rsi
+	#lea	32*9*5(%rsp), %rdi
+	#call	avx2_mul_by2_x4
+
+	vpaddq	$ACC0, $ACC0, $ACC0	# inlined avx2_mul_by2_x4
+	lea	`32*9*5`(%rsp), %rdi
+	vpaddq	$ACC1, $ACC1, $ACC1
+	vpaddq	$ACC2, $ACC2, $ACC2
+	vpaddq	$ACC3, $ACC3, $ACC3
+	vpaddq	$ACC4, $ACC4, $ACC4
+	vpaddq	$ACC5, $ACC5, $ACC5
+	vpaddq	$ACC6, $ACC6, $ACC6
+	vpaddq	$ACC7, $ACC7, $ACC7
+	vpaddq	$ACC8, $ACC8, $ACC8
+	call	avx2_normalize_n_store
+
+	#	X3 = R^2 - H^3
+	#lea	32*9*6(%rsp), %rsi
+	#lea	32*9*7(%rsp), %rdx
+	#lea	32*9*5(%rsp), %rcx
+	#lea	32*9*0($r_ptr), %rdi
+	#call	avx2_sub_x4
+	#NORMALIZE
+	#STORE
+
+	#	X3 = X3 - U2*2
+	#lea	32*9*0($r_ptr), %rsi
+	#lea	32*9*0($r_ptr), %rdi
+	#call	avx2_sub_x4
+	#NORMALIZE
+	#STORE
+
+	lea	`32*9*6+128`(%rsp), %rsi
+	lea	.LAVX2_POLY_x2+128(%rip), %rax
+	lea	`32*9*7+128`(%rsp), %rdx
+	lea	`32*9*5+128`(%rsp), %rcx
+	lea	`32*9*0`($r_ptr), %rdi
+
+	vmovdqa	32*0-128(%rsi), $ACC0
+	vmovdqa	32*1-128(%rsi), $ACC1
+	vmovdqa	32*2-128(%rsi), $ACC2
+	vmovdqa	32*3-128(%rsi), $ACC3
+	vmovdqa	32*4-128(%rsi), $ACC4
+	vmovdqa	32*5-128(%rsi), $ACC5
+	vmovdqa	32*6-128(%rsi), $ACC6
+	vmovdqa	32*7-128(%rsi), $ACC7
+	vmovdqa	32*8-128(%rsi), $ACC8
+
+	vpaddq	32*0-128(%rax), $ACC0, $ACC0
+	vpaddq	32*1-128(%rax), $ACC1, $ACC1
+	vpaddq	32*2-128(%rax), $ACC2, $ACC2
+	vpaddq	32*3-128(%rax), $ACC3, $ACC3
+	vpaddq	32*4-128(%rax), $ACC4, $ACC4
+	vpaddq	32*5-128(%rax), $ACC5, $ACC5
+	vpaddq	32*6-128(%rax), $ACC6, $ACC6
+	vpaddq	32*7-128(%rax), $ACC7, $ACC7
+	vpaddq	32*8-128(%rax), $ACC8, $ACC8
+
+	vpsubq	32*0-128(%rdx), $ACC0, $ACC0
+	vpsubq	32*1-128(%rdx), $ACC1, $ACC1
+	vpsubq	32*2-128(%rdx), $ACC2, $ACC2
+	vpsubq	32*3-128(%rdx), $ACC3, $ACC3
+	vpsubq	32*4-128(%rdx), $ACC4, $ACC4
+	vpsubq	32*5-128(%rdx), $ACC5, $ACC5
+	vpsubq	32*6-128(%rdx), $ACC6, $ACC6
+	vpsubq	32*7-128(%rdx), $ACC7, $ACC7
+	vpsubq	32*8-128(%rdx), $ACC8, $ACC8
+
+	vpsubq	32*0-128(%rcx), $ACC0, $ACC0
+	vpsubq	32*1-128(%rcx), $ACC1, $ACC1
+	vpsubq	32*2-128(%rcx), $ACC2, $ACC2
+	vpsubq	32*3-128(%rcx), $ACC3, $ACC3
+	vpsubq	32*4-128(%rcx), $ACC4, $ACC4
+	vpsubq	32*5-128(%rcx), $ACC5, $ACC5
+	vpsubq	32*6-128(%rcx), $ACC6, $ACC6
+	vpsubq	32*7-128(%rcx), $ACC7, $ACC7
+	vpsubq	32*8-128(%rcx), $ACC8, $ACC8
+	call	avx2_normalize
+
+	lea	32*0($b_ptr), %rsi
+	lea	32*0($a_ptr), %rdx
+	call	avx2_select_n_store
+
+	#	H = U2 - X3
+	lea	`32*9*0`(%rsp), %rsi
+	lea	`32*9*0`($r_ptr), %rdx
+	lea	`32*9*3`(%rsp), %rdi
+	call	avx2_sub_x4
+	call	avx2_normalize_n_store
+
+	#
+	lea	`32*9*3`(%rsp), %rsi
+	lea	`32*9*4`(%rsp), %rdx
+	lea	`32*9*3`(%rsp), %rdi
+	call	avx2_mul_x4
+	call	avx2_normalize_n_store
+
+	#
+	lea	`32*9*7`(%rsp), %rsi
+	lea	`32*9*1`($a_ptr), %rdx
+	lea	`32*9*1`(%rsp), %rdi
+	call	avx2_mul_x4
+	call	avx2_normalize_n_store
+
+	#
+	lea	`32*9*3`(%rsp), %rsi
+	lea	`32*9*1`(%rsp), %rdx
+	lea	`32*9*1`($r_ptr), %rdi
+	call	avx2_sub_x4
+	call	avx2_normalize
+
+	lea	32*9($b_ptr), %rsi
+	lea	32*9($a_ptr), %rdx
+	call	avx2_select_n_store
+
+	#lea	32*9*0($r_ptr), %rsi
+	#lea	32*9*0($r_ptr), %rdi
+	#call	avx2_mul_by1_x4
+	#NORMALIZE
+	#STORE
+
+	lea	`32*9*1`($r_ptr), %rsi
+	lea	`32*9*1`($r_ptr), %rdi
+	call	avx2_mul_by1_x4
+	call	avx2_normalize_n_store
+
+	vzeroupper
+___
+$code.=<<___	if ($win64);
+	movaps	%xmm6, -16*10(%rbp)
+	movaps	%xmm7, -16*9(%rbp)
+	movaps	%xmm8, -16*8(%rbp)
+	movaps	%xmm9, -16*7(%rbp)
+	movaps	%xmm10, -16*6(%rbp)
+	movaps	%xmm11, -16*5(%rbp)
+	movaps	%xmm12, -16*4(%rbp)
+	movaps	%xmm13, -16*3(%rbp)
+	movaps	%xmm14, -16*2(%rbp)
+	movaps	%xmm15, -16*1(%rbp)
+___
+$code.=<<___;
+	mov	%rbp, %rsp
+	pop	%rbp
+	ret
+.size	ecp_nistz256_avx2_point_add_affine_x4,.-ecp_nistz256_avx2_point_add_affine_x4
+
+################################################################################
+# void ecp_nistz256_avx2_point_add_affines_x4(void* RESULTx4, void *Ax4, void *Bx4);
+.globl	ecp_nistz256_avx2_point_add_affines_x4
+.type	ecp_nistz256_avx2_point_add_affines_x4,\@function,3
+.align	32
+ecp_nistz256_avx2_point_add_affines_x4:
+	mov	%rsp, %rax
+	push    %rbp
+	vzeroupper
+___
+$code.=<<___	if ($win64);
+	lea	-16*10(%rsp), %rsp
+	vmovaps	%xmm6, -8-16*10(%rax)
+	vmovaps	%xmm7, -8-16*9(%rax)
+	vmovaps	%xmm8, -8-16*8(%rax)
+	vmovaps	%xmm9, -8-16*7(%rax)
+	vmovaps	%xmm10, -8-16*6(%rax)
+	vmovaps	%xmm11, -8-16*5(%rax)
+	vmovaps	%xmm12, -8-16*4(%rax)
+	vmovaps	%xmm13, -8-16*3(%rax)
+	vmovaps	%xmm14, -8-16*2(%rax)
+	vmovaps	%xmm15, -8-16*1(%rax)
+___
+$code.=<<___;
+	lea	-8(%rax), %rbp
+
+# Result + 32*0 = Result.X
+# Result + 32*9 = Result.Y
+# Result + 32*18 = Result.Z
+
+# A + 32*0 = A.X
+# A + 32*9 = A.Y
+
+# B + 32*0 = B.X
+# B + 32*9 = B.Y
+
+	sub	\$`32*9*8+32*2+32*8`, %rsp
+	and	\$-64, %rsp
+
+	mov	$r_ptr_in, $r_ptr
+	mov	$a_ptr_in, $a_ptr
+	mov	$b_ptr_in, $b_ptr
+
+	vmovdqa	32*0($a_ptr_in), %ymm0
+	vmovdqa	.LAVX2_AND_MASK(%rip), $AND_MASK
+	vpxor	%ymm1, %ymm1, %ymm1
+	lea	256($a_ptr_in), %rax		# size optimization
+	vpor	32*1($a_ptr_in), %ymm0, %ymm0
+	vpor	32*2($a_ptr_in), %ymm0, %ymm0
+	vpor	32*3($a_ptr_in), %ymm0, %ymm0
+	vpor	32*4-256(%rax), %ymm0, %ymm0
+	lea	256(%rax), %rcx			# size optimization
+	vpor	32*5-256(%rax), %ymm0, %ymm0
+	vpor	32*6-256(%rax), %ymm0, %ymm0
+	vpor	32*7-256(%rax), %ymm0, %ymm0
+	vpor	32*8-256(%rax), %ymm0, %ymm0
+	vpor	32*9-256(%rax), %ymm0, %ymm0
+	vpor	32*10-256(%rax), %ymm0, %ymm0
+	vpor	32*11-256(%rax), %ymm0, %ymm0
+	vpor	32*12-512(%rcx), %ymm0, %ymm0
+	vpor	32*13-512(%rcx), %ymm0, %ymm0
+	vpor	32*14-512(%rcx), %ymm0, %ymm0
+	vpor	32*15-512(%rcx), %ymm0, %ymm0
+	vpor	32*16-512(%rcx), %ymm0, %ymm0
+	vpor	32*17-512(%rcx), %ymm0, %ymm0
+	vpcmpeqq %ymm1, %ymm0, %ymm0
+	vmovdqa	%ymm0, `32*9*8`(%rsp)
+
+	vpxor	%ymm1, %ymm1, %ymm1
+	vmovdqa	32*0($b_ptr), %ymm0
+	lea	256($b_ptr), %rax		# size optimization
+	vpor	32*1($b_ptr), %ymm0, %ymm0
+	vpor	32*2($b_ptr), %ymm0, %ymm0
+	vpor	32*3($b_ptr), %ymm0, %ymm0
+	vpor	32*4-256(%rax), %ymm0, %ymm0
+	lea	256(%rax), %rcx			# size optimization
+	vpor	32*5-256(%rax), %ymm0, %ymm0
+	vpor	32*6-256(%rax), %ymm0, %ymm0
+	vpor	32*7-256(%rax), %ymm0, %ymm0
+	vpor	32*8-256(%rax), %ymm0, %ymm0
+	vpor	32*9-256(%rax), %ymm0, %ymm0
+	vpor	32*10-256(%rax), %ymm0, %ymm0
+	vpor	32*11-256(%rax), %ymm0, %ymm0
+	vpor	32*12-512(%rcx), %ymm0, %ymm0
+	vpor	32*13-512(%rcx), %ymm0, %ymm0
+	vpor	32*14-512(%rcx), %ymm0, %ymm0
+	vpor	32*15-512(%rcx), %ymm0, %ymm0
+	vpor	32*16-512(%rcx), %ymm0, %ymm0
+	vpor	32*17-512(%rcx), %ymm0, %ymm0
+	vpcmpeqq %ymm1, %ymm0, %ymm0
+	vmovdqa	%ymm0, `32*9*8+32`(%rsp)
+
+	#	H = U2 - U1 = X2 - X1
+	lea	`32*9*0`($b_ptr), %rsi
+	lea	`32*9*0`($a_ptr), %rdx
+	lea	`32*9*3`(%rsp), %rdi
+	call	avx2_sub_x4
+	call	avx2_normalize_n_store
+
+	#	R = S2 - S1 = Y2 - Y1
+	lea	`32*9*1`($b_ptr), %rsi
+	lea	`32*9*1`($a_ptr), %rdx
+	lea	`32*9*4`(%rsp), %rdi
+	call	avx2_sub_x4
+	call	avx2_normalize_n_store
+
+	#	Z3 = H*Z1*Z2 = H
+	lea	`32*9*3`(%rsp), %rsi
+	lea	`32*9*2`($r_ptr), %rdi
+	call	avx2_mul_by1_x4
+	call	avx2_normalize
+
+	vmovdqa	`32*9*8`(%rsp), $B
+	vpor	`32*9*8+32`(%rsp), $B, $B
+
+	vpandn	$ACC0, $B, $ACC0
+	lea	.LONE+128(%rip), %rax
+	vpandn	$ACC1, $B, $ACC1
+	vpandn	$ACC2, $B, $ACC2
+	vpandn	$ACC3, $B, $ACC3
+	vpandn	$ACC4, $B, $ACC4
+	vpandn	$ACC5, $B, $ACC5
+	vpandn	$ACC6, $B, $ACC6
+	vpandn	$ACC7, $B, $ACC7
+
+	vpand	32*0-128(%rax), $B, $T0
+	 vpandn	$ACC8, $B, $ACC8
+	vpand	32*1-128(%rax), $B, $Y
+	vpxor	$T0, $ACC0, $ACC0
+	vpand	32*2-128(%rax), $B, $T0
+	vpxor	$Y, $ACC1, $ACC1
+	vpand	32*3-128(%rax), $B, $Y
+	vpxor	$T0, $ACC2, $ACC2
+	vpand	32*4-128(%rax), $B, $T0
+	vpxor	$Y, $ACC3, $ACC3
+	vpand	32*5-128(%rax), $B, $Y
+	vpxor	$T0, $ACC4, $ACC4
+	vpand	32*6-128(%rax), $B, $T0
+	vpxor	$Y, $ACC5, $ACC5
+	vpand	32*7-128(%rax), $B, $Y
+	vpxor	$T0, $ACC6, $ACC6
+	vpand	32*8-128(%rax), $B, $T0
+	vpxor	$Y, $ACC7, $ACC7
+	vpxor	$T0, $ACC8, $ACC8
+	`&STORE`
+
+	#	R^2 = R^2
+	lea	`32*9*4`(%rsp), %rsi
+	lea	`32*9*6`(%rsp), %rdi
+	lea	`32*9*8+32*2`(%rsp), %rcx	# temporary vector
+	call	avx2_sqr_x4
+	call	avx2_normalize_n_store
+
+	#	H^2 = H^2
+	lea	`32*9*3`(%rsp), %rsi
+	lea	`32*9*5`(%rsp), %rdi
+	call	avx2_sqr_x4
+	call	avx2_normalize_n_store
+
+	#	H^3 = H^2*H
+	lea	`32*9*3`(%rsp), %rsi
+	lea	`32*9*5`(%rsp), %rdx
+	lea	`32*9*7`(%rsp), %rdi
+	call	avx2_mul_x4
+	call	avx2_normalize_n_store
+
+	#	U2 = U1*H^2
+	lea	`32*9*0`($a_ptr), %rsi
+	lea	`32*9*5`(%rsp), %rdx
+	lea	`32*9*0`(%rsp), %rdi
+	call	avx2_mul_x4
+	#call	avx2_normalize
+	`&STORE`
+
+	#	Hsqr = U2*2
+	#lea	32*9*0(%rsp), %rsi
+	#lea	32*9*5(%rsp), %rdi
+	#call	avx2_mul_by2_x4
+
+	vpaddq	$ACC0, $ACC0, $ACC0	# inlined avx2_mul_by2_x4
+	lea	`32*9*5`(%rsp), %rdi
+	vpaddq	$ACC1, $ACC1, $ACC1
+	vpaddq	$ACC2, $ACC2, $ACC2
+	vpaddq	$ACC3, $ACC3, $ACC3
+	vpaddq	$ACC4, $ACC4, $ACC4
+	vpaddq	$ACC5, $ACC5, $ACC5
+	vpaddq	$ACC6, $ACC6, $ACC6
+	vpaddq	$ACC7, $ACC7, $ACC7
+	vpaddq	$ACC8, $ACC8, $ACC8
+	call	avx2_normalize_n_store
+
+	#	X3 = R^2 - H^3
+	#lea	32*9*6(%rsp), %rsi
+	#lea	32*9*7(%rsp), %rdx
+	#lea	32*9*5(%rsp), %rcx
+	#lea	32*9*0($r_ptr), %rdi
+	#call	avx2_sub_x4
+	#NORMALIZE
+	#STORE
+
+	#	X3 = X3 - U2*2
+	#lea	32*9*0($r_ptr), %rsi
+	#lea	32*9*0($r_ptr), %rdi
+	#call	avx2_sub_x4
+	#NORMALIZE
+	#STORE
+
+	lea	`32*9*6+128`(%rsp), %rsi
+	lea	.LAVX2_POLY_x2+128(%rip), %rax
+	lea	`32*9*7+128`(%rsp), %rdx
+	lea	`32*9*5+128`(%rsp), %rcx
+	lea	`32*9*0`($r_ptr), %rdi
+
+	vmovdqa	32*0-128(%rsi), $ACC0
+	vmovdqa	32*1-128(%rsi), $ACC1
+	vmovdqa	32*2-128(%rsi), $ACC2
+	vmovdqa	32*3-128(%rsi), $ACC3
+	vmovdqa	32*4-128(%rsi), $ACC4
+	vmovdqa	32*5-128(%rsi), $ACC5
+	vmovdqa	32*6-128(%rsi), $ACC6
+	vmovdqa	32*7-128(%rsi), $ACC7
+	vmovdqa	32*8-128(%rsi), $ACC8
+
+	vpaddq	32*0-128(%rax), $ACC0, $ACC0
+	vpaddq	32*1-128(%rax), $ACC1, $ACC1
+	vpaddq	32*2-128(%rax), $ACC2, $ACC2
+	vpaddq	32*3-128(%rax), $ACC3, $ACC3
+	vpaddq	32*4-128(%rax), $ACC4, $ACC4
+	vpaddq	32*5-128(%rax), $ACC5, $ACC5
+	vpaddq	32*6-128(%rax), $ACC6, $ACC6
+	vpaddq	32*7-128(%rax), $ACC7, $ACC7
+	vpaddq	32*8-128(%rax), $ACC8, $ACC8
+
+	vpsubq	32*0-128(%rdx), $ACC0, $ACC0
+	vpsubq	32*1-128(%rdx), $ACC1, $ACC1
+	vpsubq	32*2-128(%rdx), $ACC2, $ACC2
+	vpsubq	32*3-128(%rdx), $ACC3, $ACC3
+	vpsubq	32*4-128(%rdx), $ACC4, $ACC4
+	vpsubq	32*5-128(%rdx), $ACC5, $ACC5
+	vpsubq	32*6-128(%rdx), $ACC6, $ACC6
+	vpsubq	32*7-128(%rdx), $ACC7, $ACC7
+	vpsubq	32*8-128(%rdx), $ACC8, $ACC8
+
+	vpsubq	32*0-128(%rcx), $ACC0, $ACC0
+	vpsubq	32*1-128(%rcx), $ACC1, $ACC1
+	vpsubq	32*2-128(%rcx), $ACC2, $ACC2
+	vpsubq	32*3-128(%rcx), $ACC3, $ACC3
+	vpsubq	32*4-128(%rcx), $ACC4, $ACC4
+	vpsubq	32*5-128(%rcx), $ACC5, $ACC5
+	vpsubq	32*6-128(%rcx), $ACC6, $ACC6
+	vpsubq	32*7-128(%rcx), $ACC7, $ACC7
+	vpsubq	32*8-128(%rcx), $ACC8, $ACC8
+	call	avx2_normalize
+
+	lea	32*0($b_ptr), %rsi
+	lea	32*0($a_ptr), %rdx
+	call	avx2_select_n_store
+
+	#	H = U2 - X3
+	lea	`32*9*0`(%rsp), %rsi
+	lea	`32*9*0`($r_ptr), %rdx
+	lea	`32*9*3`(%rsp), %rdi
+	call	avx2_sub_x4
+	call	avx2_normalize_n_store
+
+	#	H = H*R
+	lea	`32*9*3`(%rsp), %rsi
+	lea	`32*9*4`(%rsp), %rdx
+	lea	`32*9*3`(%rsp), %rdi
+	call	avx2_mul_x4
+	call	avx2_normalize_n_store
+
+	#	S2 = S1 * H^3
+	lea	`32*9*7`(%rsp), %rsi
+	lea	`32*9*1`($a_ptr), %rdx
+	lea	`32*9*1`(%rsp), %rdi
+	call	avx2_mul_x4
+	call	avx2_normalize_n_store
+
+	#
+	lea	`32*9*3`(%rsp), %rsi
+	lea	`32*9*1`(%rsp), %rdx
+	lea	`32*9*1`($r_ptr), %rdi
+	call	avx2_sub_x4
+	call	avx2_normalize
+
+	lea	32*9($b_ptr), %rsi
+	lea	32*9($a_ptr), %rdx
+	call	avx2_select_n_store
+
+	#lea	32*9*0($r_ptr), %rsi
+	#lea	32*9*0($r_ptr), %rdi
+	#call	avx2_mul_by1_x4
+	#NORMALIZE
+	#STORE
+
+	lea	`32*9*1`($r_ptr), %rsi
+	lea	`32*9*1`($r_ptr), %rdi
+	call	avx2_mul_by1_x4
+	call	avx2_normalize_n_store
+
+	vzeroupper
+___
+$code.=<<___	if ($win64);
+	movaps	%xmm6, -16*10(%rbp)
+	movaps	%xmm7, -16*9(%rbp)
+	movaps	%xmm8, -16*8(%rbp)
+	movaps	%xmm9, -16*7(%rbp)
+	movaps	%xmm10, -16*6(%rbp)
+	movaps	%xmm11, -16*5(%rbp)
+	movaps	%xmm12, -16*4(%rbp)
+	movaps	%xmm13, -16*3(%rbp)
+	movaps	%xmm14, -16*2(%rbp)
+	movaps	%xmm15, -16*1(%rbp)
+___
+$code.=<<___;
+	mov	%rbp, %rsp
+	pop	%rbp
+	ret
+.size	ecp_nistz256_avx2_point_add_affines_x4,.-ecp_nistz256_avx2_point_add_affines_x4
+
+################################################################################
+# void ecp_nistz256_avx2_to_mont(void* RESULTx4, void *Ax4);
+.globl	ecp_nistz256_avx2_to_mont
+.type	ecp_nistz256_avx2_to_mont,\@function,2
+.align	32
+ecp_nistz256_avx2_to_mont:
+	vzeroupper
+___
+$code.=<<___	if ($win64);
+	lea	-8-16*10(%rsp), %rsp
+	vmovaps	%xmm6, -8-16*10(%rax)
+	vmovaps	%xmm7, -8-16*9(%rax)
+	vmovaps	%xmm8, -8-16*8(%rax)
+	vmovaps	%xmm9, -8-16*7(%rax)
+	vmovaps	%xmm10, -8-16*6(%rax)
+	vmovaps	%xmm11, -8-16*5(%rax)
+	vmovaps	%xmm12, -8-16*4(%rax)
+	vmovaps	%xmm13, -8-16*3(%rax)
+	vmovaps	%xmm14, -8-16*2(%rax)
+	vmovaps	%xmm15, -8-16*1(%rax)
+___
+$code.=<<___;
+	vmovdqa	.LAVX2_AND_MASK(%rip), $AND_MASK
+	lea	.LTO_MONT_AVX2(%rip), %rdx
+	call	avx2_mul_x4
+	call	avx2_normalize_n_store
+
+	vzeroupper
+___
+$code.=<<___	if ($win64);
+	movaps	16*0(%rsp), %xmm6
+	movaps	16*1(%rsp), %xmm7
+	movaps	16*2(%rsp), %xmm8
+	movaps	16*3(%rsp), %xmm9
+	movaps	16*4(%rsp), %xmm10
+	movaps	16*5(%rsp), %xmm11
+	movaps	16*6(%rsp), %xmm12
+	movaps	16*7(%rsp), %xmm13
+	movaps	16*8(%rsp), %xmm14
+	movaps	16*9(%rsp), %xmm15
+	lea	8+16*10(%rsp), %rsp
+___
+$code.=<<___;
+	ret
+.size	ecp_nistz256_avx2_to_mont,.-ecp_nistz256_avx2_to_mont
+
+################################################################################
+# void ecp_nistz256_avx2_from_mont(void* RESULTx4, void *Ax4);
+.globl	ecp_nistz256_avx2_from_mont
+.type	ecp_nistz256_avx2_from_mont,\@function,2
+.align	32
+ecp_nistz256_avx2_from_mont:
+	vzeroupper
+___
+$code.=<<___	if ($win64);
+	lea	-8-16*10(%rsp), %rsp
+	vmovaps	%xmm6, -8-16*10(%rax)
+	vmovaps	%xmm7, -8-16*9(%rax)
+	vmovaps	%xmm8, -8-16*8(%rax)
+	vmovaps	%xmm9, -8-16*7(%rax)
+	vmovaps	%xmm10, -8-16*6(%rax)
+	vmovaps	%xmm11, -8-16*5(%rax)
+	vmovaps	%xmm12, -8-16*4(%rax)
+	vmovaps	%xmm13, -8-16*3(%rax)
+	vmovaps	%xmm14, -8-16*2(%rax)
+	vmovaps	%xmm15, -8-16*1(%rax)
+___
+$code.=<<___;
+	vmovdqa	.LAVX2_AND_MASK(%rip), $AND_MASK
+	lea	.LFROM_MONT_AVX2(%rip), %rdx
+	call	avx2_mul_x4
+	call	avx2_normalize_n_store
+
+	vzeroupper
+___
+$code.=<<___	if ($win64);
+	movaps	16*0(%rsp), %xmm6
+	movaps	16*1(%rsp), %xmm7
+	movaps	16*2(%rsp), %xmm8
+	movaps	16*3(%rsp), %xmm9
+	movaps	16*4(%rsp), %xmm10
+	movaps	16*5(%rsp), %xmm11
+	movaps	16*6(%rsp), %xmm12
+	movaps	16*7(%rsp), %xmm13
+	movaps	16*8(%rsp), %xmm14
+	movaps	16*9(%rsp), %xmm15
+	lea	8+16*10(%rsp), %rsp
+___
+$code.=<<___;
+	ret
+.size	ecp_nistz256_avx2_from_mont,.-ecp_nistz256_avx2_from_mont
+
+################################################################################
+# void ecp_nistz256_avx2_set1(void* RESULTx4);
+.globl	ecp_nistz256_avx2_set1
+.type	ecp_nistz256_avx2_set1,\@function,1
+.align	32
+ecp_nistz256_avx2_set1:
+	lea	.LONE+128(%rip), %rax
+	lea	128(%rdi), %rdi
+	vzeroupper
+	vmovdqa	32*0-128(%rax), %ymm0
+	vmovdqa	32*1-128(%rax), %ymm1
+	vmovdqa	32*2-128(%rax), %ymm2
+	vmovdqa	32*3-128(%rax), %ymm3
+	vmovdqa	32*4-128(%rax), %ymm4
+	vmovdqa	32*5-128(%rax), %ymm5
+	vmovdqa	%ymm0, 32*0-128(%rdi)
+	vmovdqa	32*6-128(%rax), %ymm0
+	vmovdqa	%ymm1, 32*1-128(%rdi)
+	vmovdqa	32*7-128(%rax), %ymm1
+	vmovdqa	%ymm2, 32*2-128(%rdi)
+	vmovdqa	32*8-128(%rax), %ymm2
+	vmovdqa	%ymm3, 32*3-128(%rdi)
+	vmovdqa	%ymm4, 32*4-128(%rdi)
+	vmovdqa	%ymm5, 32*5-128(%rdi)
+	vmovdqa	%ymm0, 32*6-128(%rdi)
+	vmovdqa	%ymm1, 32*7-128(%rdi)
+	vmovdqa	%ymm2, 32*8-128(%rdi)
+
+	vzeroupper
+	ret
+.size	ecp_nistz256_avx2_set1,.-ecp_nistz256_avx2_set1
+___
+}
+{
+################################################################################
+# void ecp_nistz256_avx2_multi_gather_w7(void* RESULT, void *in,
+#			    int index0, int index1, int index2, int index3);
+################################################################################
+
+my ($val,$in_t,$index0,$index1,$index2,$index3)=("%rdi","%rsi","%edx","%ecx","%r8d","%r9d");
+my ($INDEX0,$INDEX1,$INDEX2,$INDEX3)=map("%ymm$_",(0..3));
+my ($R0a,$R0b,$R1a,$R1b,$R2a,$R2b,$R3a,$R3b)=map("%ymm$_",(4..11));
+my ($M0,$T0,$T1,$TMP0)=map("%ymm$_",(12..15));
+
+$code.=<<___;
+.globl	ecp_nistz256_avx2_multi_gather_w7
+.type	ecp_nistz256_avx2_multi_gather_w7,\@function,6
+.align	32
+ecp_nistz256_avx2_multi_gather_w7:
+	vzeroupper
+___
+$code.=<<___	if ($win64);
+	lea	-8-16*10(%rsp), %rsp
+	vmovaps	%xmm6, -8-16*10(%rax)
+	vmovaps	%xmm7, -8-16*9(%rax)
+	vmovaps	%xmm8, -8-16*8(%rax)
+	vmovaps	%xmm9, -8-16*7(%rax)
+	vmovaps	%xmm10, -8-16*6(%rax)
+	vmovaps	%xmm11, -8-16*5(%rax)
+	vmovaps	%xmm12, -8-16*4(%rax)
+	vmovaps	%xmm13, -8-16*3(%rax)
+	vmovaps	%xmm14, -8-16*2(%rax)
+	vmovaps	%xmm15, -8-16*1(%rax)
+___
+$code.=<<___;
+	lea	.LIntOne(%rip), %rax
+
+	vmovd	$index0, %xmm0
+	vmovd	$index1, %xmm1
+	vmovd	$index2, %xmm2
+	vmovd	$index3, %xmm3
+
+	vpxor	$R0a, $R0a, $R0a
+	vpxor	$R0b, $R0b, $R0b
+	vpxor	$R1a, $R1a, $R1a
+	vpxor	$R1b, $R1b, $R1b
+	vpxor	$R2a, $R2a, $R2a
+	vpxor	$R2b, $R2b, $R2b
+	vpxor	$R3a, $R3a, $R3a
+	vpxor	$R3b, $R3b, $R3b
+	vmovdqa	(%rax), $M0
+
+	vpermd	$INDEX0, $R0a, $INDEX0
+	vpermd	$INDEX1, $R0a, $INDEX1
+	vpermd	$INDEX2, $R0a, $INDEX2
+	vpermd	$INDEX3, $R0a, $INDEX3
+
+	mov	\$64, %ecx
+	lea	112($val), $val		# size optimization
+	jmp	.Lmulti_select_loop_avx2
+
+# INDEX=0, corresponds to the point at infty (0,0)
+.align	32
+.Lmulti_select_loop_avx2:
+	vpcmpeqd	$INDEX0, $M0, $TMP0
+
+	vmovdqa		`32*0+32*64*2*0`($in_t), $T0
+	vmovdqa		`32*1+32*64*2*0`($in_t), $T1
+	vpand		$TMP0, $T0, $T0
+	vpand		$TMP0, $T1, $T1
+	vpxor		$T0, $R0a, $R0a
+	vpxor		$T1, $R0b, $R0b
+
+	vpcmpeqd	$INDEX1, $M0, $TMP0
+
+	vmovdqa		`32*0+32*64*2*1`($in_t), $T0
+	vmovdqa		`32*1+32*64*2*1`($in_t), $T1
+	vpand		$TMP0, $T0, $T0
+	vpand		$TMP0, $T1, $T1
+	vpxor		$T0, $R1a, $R1a
+	vpxor		$T1, $R1b, $R1b
+
+	vpcmpeqd	$INDEX2, $M0, $TMP0
+
+	vmovdqa		`32*0+32*64*2*2`($in_t), $T0
+	vmovdqa		`32*1+32*64*2*2`($in_t), $T1
+	vpand		$TMP0, $T0, $T0
+	vpand		$TMP0, $T1, $T1
+	vpxor		$T0, $R2a, $R2a
+	vpxor		$T1, $R2b, $R2b
+
+	vpcmpeqd	$INDEX3, $M0, $TMP0
+
+	vmovdqa		`32*0+32*64*2*3`($in_t), $T0
+	vmovdqa		`32*1+32*64*2*3`($in_t), $T1
+	vpand		$TMP0, $T0, $T0
+	vpand		$TMP0, $T1, $T1
+	vpxor		$T0, $R3a, $R3a
+	vpxor		$T1, $R3b, $R3b
+
+	vpaddd		(%rax), $M0, $M0	# increment
+	lea		32*2($in_t), $in_t
+
+        dec	%ecx
+	jnz	.Lmulti_select_loop_avx2
+
+	vmovdqu	$R0a, 32*0-112($val)
+	vmovdqu	$R0b, 32*1-112($val)
+	vmovdqu	$R1a, 32*2-112($val)
+	vmovdqu	$R1b, 32*3-112($val)
+	vmovdqu	$R2a, 32*4-112($val)
+	vmovdqu	$R2b, 32*5-112($val)
+	vmovdqu	$R3a, 32*6-112($val)
+	vmovdqu	$R3b, 32*7-112($val)
+
+	vzeroupper
+___
+$code.=<<___	if ($win64);
+	movaps	16*0(%rsp), %xmm6
+	movaps	16*1(%rsp), %xmm7
+	movaps	16*2(%rsp), %xmm8
+	movaps	16*3(%rsp), %xmm9
+	movaps	16*4(%rsp), %xmm10
+	movaps	16*5(%rsp), %xmm11
+	movaps	16*6(%rsp), %xmm12
+	movaps	16*7(%rsp), %xmm13
+	movaps	16*8(%rsp), %xmm14
+	movaps	16*9(%rsp), %xmm15
+	lea	8+16*10(%rsp), %rsp
+___
+$code.=<<___;
+	ret
+.size	ecp_nistz256_avx2_multi_gather_w7,.-ecp_nistz256_avx2_multi_gather_w7
+
+.extern	OPENSSL_ia32cap_P
+.globl	ecp_nistz_avx2_eligible
+.type	ecp_nistz_avx2_eligible,\@abi-omnipotent
+.align	32
+ecp_nistz_avx2_eligible:
+	mov	OPENSSL_ia32cap_P+8(%rip),%eax
+	shr	\$5,%eax
+	and	\$1,%eax
+	ret
+.size	ecp_nistz_avx2_eligible,.-ecp_nistz_avx2_eligible
+___
+}
+}} else {{	# assembler is too old
+$code.=<<___;
+.text
+
+.globl	ecp_nistz256_avx2_transpose_convert
+.globl	ecp_nistz256_avx2_convert_transpose_back
+.globl	ecp_nistz256_avx2_point_add_affine_x4
+.globl	ecp_nistz256_avx2_point_add_affines_x4
+.globl	ecp_nistz256_avx2_to_mont
+.globl	ecp_nistz256_avx2_from_mont
+.globl	ecp_nistz256_avx2_set1
+.globl	ecp_nistz256_avx2_multi_gather_w7
+.type	ecp_nistz256_avx2_multi_gather_w7,\@abi-omnipotent
+ecp_nistz256_avx2_transpose_convert:
+ecp_nistz256_avx2_convert_transpose_back:
+ecp_nistz256_avx2_point_add_affine_x4:
+ecp_nistz256_avx2_point_add_affines_x4:
+ecp_nistz256_avx2_to_mont:
+ecp_nistz256_avx2_from_mont:
+ecp_nistz256_avx2_set1:
+ecp_nistz256_avx2_multi_gather_w7:
+	.byte	0x0f,0x0b	# ud2
+	ret
+.size	ecp_nistz256_avx2_multi_gather_w7,.-ecp_nistz256_avx2_multi_gather_w7
+
+.globl	ecp_nistz_avx2_eligible
+.type	ecp_nistz_avx2_eligible,\@abi-omnipotent
+ecp_nistz_avx2_eligible:
+	xor	%eax,%eax
+	ret
+.size	ecp_nistz_avx2_eligible,.-ecp_nistz_avx2_eligible
+___
+}}
+
+foreach (split("\n",$code)) {
+	s/\`([^\`]*)\`/eval($1)/geo;
+
+	print $_,"\n";
+}
+
+close STDOUT;
diff --git a/crypto/ec/asm/ecp_nistz256-sparcv9.pl b/crypto/ec/asm/ecp_nistz256-sparcv9.pl
new file mode 100755
index 000000000..97201cb27
--- /dev/null
+++ b/crypto/ec/asm/ecp_nistz256-sparcv9.pl
@@ -0,0 +1,3061 @@
+#! /usr/bin/env perl
+# Copyright 2015-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+
+# ====================================================================
+# Written by Andy Polyakov  for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+#
+# ECP_NISTZ256 module for SPARCv9.
+#
+# February 2015.
+#
+# Original ECP_NISTZ256 submission targeting x86_64 is detailed in
+# http://eprint.iacr.org/2013/816. In the process of adaptation
+# original .c module was made 32-bit savvy in order to make this
+# implementation possible.
+#
+#			with/without -DECP_NISTZ256_ASM
+# UltraSPARC III	+12-18%
+# SPARC T4		+99-550% (+66-150% on 32-bit Solaris)
+#
+# Ranges denote minimum and maximum improvement coefficients depending
+# on benchmark. Lower coefficients are for ECDSA sign, server-side
+# operation. Keep in mind that +200% means 3x improvement.
+
+$output = pop;
+open STDOUT,">$output";
+
+$code.=<<___;
+#include "sparc_arch.h"
+
+#define LOCALS	(STACK_BIAS+STACK_FRAME)
+#ifdef	__arch64__
+.register	%g2,#scratch
+.register	%g3,#scratch
+# define STACK64_FRAME	STACK_FRAME
+# define LOCALS64	LOCALS
+#else
+# define STACK64_FRAME	(2047+192)
+# define LOCALS64	STACK64_FRAME
+#endif
+
+.section	".text",#alloc,#execinstr
+___
+########################################################################
+# Convert ecp_nistz256_table.c to layout expected by ecp_nistz_gather_w7
+#
+$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+open TABLE,") {
+	s/TOBN\(\s*(0x[0-9a-f]+),\s*(0x[0-9a-f]+)\s*\)/push @arr,hex($2),hex($1)/geo;
+}
+close TABLE;
+
+# See ecp_nistz256_table.c for explanation for why it's 64*16*37.
+# 64*16*37-1 is because $#arr returns last valid index or @arr, not
+# amount of elements.
+die "insane number of elements" if ($#arr != 64*16*37-1);
+
+$code.=<<___;
+.globl	ecp_nistz256_precomputed
+.align	4096
+ecp_nistz256_precomputed:
+___
+########################################################################
+# this conversion smashes P256_POINT_AFFINE by individual bytes with
+# 64 byte interval, similar to
+#	1111222233334444
+#	1234123412341234
+for(1..37) {
+	@tbl = splice(@arr,0,64*16);
+	for($i=0;$i<64;$i++) {
+		undef @line;
+		for($j=0;$j<64;$j++) {
+			push @line,(@tbl[$j*16+$i/4]>>(($i%4)*8))&0xff;
+		}
+		$code.=".byte\t";
+		$code.=join(',',map { sprintf "0x%02x",$_} @line);
+		$code.="\n";
+	}
+}
+
+{{{
+my ($rp,$ap,$bp)=map("%i$_",(0..2));
+my @acc=map("%l$_",(0..7));
+my ($t0,$t1,$t2,$t3,$t4,$t5,$t6,$t7)=(map("%o$_",(0..5)),"%g4","%g5");
+my ($bi,$a0,$mask,$carry)=(map("%i$_",(3..5)),"%g1");
+my ($rp_real,$ap_real)=("%g2","%g3");
+
+$code.=<<___;
+.type	ecp_nistz256_precomputed,#object
+.size	ecp_nistz256_precomputed,.-ecp_nistz256_precomputed
+.align	64
+.LRR:	! 2^512 mod P precomputed for NIST P256 polynomial
+.long	0x00000003, 0x00000000, 0xffffffff, 0xfffffffb
+.long	0xfffffffe, 0xffffffff, 0xfffffffd, 0x00000004
+.Lone:
+.long	1,0,0,0,0,0,0,0
+.asciz	"ECP_NISTZ256 for SPARCv9, CRYPTOGAMS by "
+
+! void	ecp_nistz256_to_mont(BN_ULONG %i0[8],const BN_ULONG %i1[8]);
+.globl	ecp_nistz256_to_mont
+.align	64
+ecp_nistz256_to_mont:
+	save	%sp,-STACK_FRAME,%sp
+	nop
+1:	call	.+8
+	add	%o7,.LRR-1b,$bp
+	call	__ecp_nistz256_mul_mont
+	nop
+	ret
+	restore
+.type	ecp_nistz256_to_mont,#function
+.size	ecp_nistz256_to_mont,.-ecp_nistz256_to_mont
+
+! void	ecp_nistz256_from_mont(BN_ULONG %i0[8],const BN_ULONG %i1[8]);
+.globl	ecp_nistz256_from_mont
+.align	32
+ecp_nistz256_from_mont:
+	save	%sp,-STACK_FRAME,%sp
+	nop
+1:	call	.+8
+	add	%o7,.Lone-1b,$bp
+	call	__ecp_nistz256_mul_mont
+	nop
+	ret
+	restore
+.type	ecp_nistz256_from_mont,#function
+.size	ecp_nistz256_from_mont,.-ecp_nistz256_from_mont
+
+! void	ecp_nistz256_mul_mont(BN_ULONG %i0[8],const BN_ULONG %i1[8],
+!					      const BN_ULONG %i2[8]);
+.globl	ecp_nistz256_mul_mont
+.align	32
+ecp_nistz256_mul_mont:
+	save	%sp,-STACK_FRAME,%sp
+	nop
+	call	__ecp_nistz256_mul_mont
+	nop
+	ret
+	restore
+.type	ecp_nistz256_mul_mont,#function
+.size	ecp_nistz256_mul_mont,.-ecp_nistz256_mul_mont
+
+! void	ecp_nistz256_sqr_mont(BN_ULONG %i0[8],const BN_ULONG %i2[8]);
+.globl	ecp_nistz256_sqr_mont
+.align	32
+ecp_nistz256_sqr_mont:
+	save	%sp,-STACK_FRAME,%sp
+	mov	$ap,$bp
+	call	__ecp_nistz256_mul_mont
+	nop
+	ret
+	restore
+.type	ecp_nistz256_sqr_mont,#function
+.size	ecp_nistz256_sqr_mont,.-ecp_nistz256_sqr_mont
+___
+
+########################################################################
+# Special thing to keep in mind is that $t0-$t7 hold 64-bit values,
+# while all others are meant to keep 32. "Meant to" means that additions
+# to @acc[0-7] do "contaminate" upper bits, but they are cleared before
+# they can affect outcome (follow 'and' with $mask). Also keep in mind
+# that addition with carry is addition with 32-bit carry, even though
+# CPU is 64-bit. [Addition with 64-bit carry was introduced in T3, see
+# below for VIS3 code paths.]
+
+$code.=<<___;
+.align	32
+__ecp_nistz256_mul_mont:
+	ld	[$bp+0],$bi		! b[0]
+	mov	-1,$mask
+	ld	[$ap+0],$a0
+	srl	$mask,0,$mask		! 0xffffffff
+	ld	[$ap+4],$t1
+	ld	[$ap+8],$t2
+	ld	[$ap+12],$t3
+	ld	[$ap+16],$t4
+	ld	[$ap+20],$t5
+	ld	[$ap+24],$t6
+	ld	[$ap+28],$t7
+	mulx	$a0,$bi,$t0		! a[0-7]*b[0], 64-bit results
+	mulx	$t1,$bi,$t1
+	mulx	$t2,$bi,$t2
+	mulx	$t3,$bi,$t3
+	mulx	$t4,$bi,$t4
+	mulx	$t5,$bi,$t5
+	mulx	$t6,$bi,$t6
+	mulx	$t7,$bi,$t7
+	srlx	$t0,32,@acc[1]		! extract high parts
+	srlx	$t1,32,@acc[2]
+	srlx	$t2,32,@acc[3]
+	srlx	$t3,32,@acc[4]
+	srlx	$t4,32,@acc[5]
+	srlx	$t5,32,@acc[6]
+	srlx	$t6,32,@acc[7]
+	srlx	$t7,32,@acc[0]		! "@acc[8]"
+	mov	0,$carry
+___
+for($i=1;$i<8;$i++) {
+$code.=<<___;
+	addcc	@acc[1],$t1,@acc[1]	! accumulate high parts
+	ld	[$bp+4*$i],$bi		! b[$i]
+	ld	[$ap+4],$t1		! re-load a[1-7]
+	addccc	@acc[2],$t2,@acc[2]
+	addccc	@acc[3],$t3,@acc[3]
+	ld	[$ap+8],$t2
+	ld	[$ap+12],$t3
+	addccc	@acc[4],$t4,@acc[4]
+	addccc	@acc[5],$t5,@acc[5]
+	ld	[$ap+16],$t4
+	ld	[$ap+20],$t5
+	addccc	@acc[6],$t6,@acc[6]
+	addccc	@acc[7],$t7,@acc[7]
+	ld	[$ap+24],$t6
+	ld	[$ap+28],$t7
+	addccc	@acc[0],$carry,@acc[0]	! "@acc[8]"
+	addc	%g0,%g0,$carry
+___
+	# Reduction iteration is normally performed by accumulating
+	# result of multiplication of modulus by "magic" digit [and
+	# omitting least significant word, which is guaranteed to
+	# be 0], but thanks to special form of modulus and "magic"
+	# digit being equal to least significant word, it can be
+	# performed with additions and subtractions alone. Indeed:
+	#
+	#        ffff.0001.0000.0000.0000.ffff.ffff.ffff
+	# *                                         abcd
+	# + xxxx.xxxx.xxxx.xxxx.xxxx.xxxx.xxxx.xxxx.abcd
+	#
+	# Now observing that ff..ff*x = (2^n-1)*x = 2^n*x-x, we
+	# rewrite above as:
+	#
+	#   xxxx.xxxx.xxxx.xxxx.xxxx.xxxx.xxxx.xxxx.abcd
+	# + abcd.0000.abcd.0000.0000.abcd.0000.0000.0000
+	# -      abcd.0000.0000.0000.0000.0000.0000.abcd
+	#
+	# or marking redundant operations:
+	#
+	#   xxxx.xxxx.xxxx.xxxx.xxxx.xxxx.xxxx.xxxx.----
+	# + abcd.0000.abcd.0000.0000.abcd.----.----.----
+	# -      abcd.----.----.----.----.----.----.----
+
+$code.=<<___;
+	! multiplication-less reduction
+	addcc	@acc[3],$t0,@acc[3]	! r[3]+=r[0]
+	addccc	@acc[4],%g0,@acc[4]	! r[4]+=0
+	 and	@acc[1],$mask,@acc[1]
+	 and	@acc[2],$mask,@acc[2]
+	addccc	@acc[5],%g0,@acc[5]	! r[5]+=0
+	addccc	@acc[6],$t0,@acc[6]	! r[6]+=r[0]
+	 and	@acc[3],$mask,@acc[3]
+	 and	@acc[4],$mask,@acc[4]
+	addccc	@acc[7],%g0,@acc[7]	! r[7]+=0
+	addccc	@acc[0],$t0,@acc[0]	! r[8]+=r[0]	"@acc[8]"
+	 and	@acc[5],$mask,@acc[5]
+	 and	@acc[6],$mask,@acc[6]
+	addc	$carry,%g0,$carry	! top-most carry
+	subcc	@acc[7],$t0,@acc[7]	! r[7]-=r[0]
+	subccc	@acc[0],%g0,@acc[0]	! r[8]-=0	"@acc[8]"
+	subc	$carry,%g0,$carry	! top-most carry
+	 and	@acc[7],$mask,@acc[7]
+	 and	@acc[0],$mask,@acc[0]	! "@acc[8]"
+___
+	push(@acc,shift(@acc));		# rotate registers to "omit" acc[0]
+$code.=<<___;
+	mulx	$a0,$bi,$t0		! a[0-7]*b[$i], 64-bit results
+	mulx	$t1,$bi,$t1
+	mulx	$t2,$bi,$t2
+	mulx	$t3,$bi,$t3
+	mulx	$t4,$bi,$t4
+	mulx	$t5,$bi,$t5
+	mulx	$t6,$bi,$t6
+	mulx	$t7,$bi,$t7
+	add	@acc[0],$t0,$t0		! accumulate low parts, can't overflow
+	add	@acc[1],$t1,$t1
+	srlx	$t0,32,@acc[1]		! extract high parts
+	add	@acc[2],$t2,$t2
+	srlx	$t1,32,@acc[2]
+	add	@acc[3],$t3,$t3
+	srlx	$t2,32,@acc[3]
+	add	@acc[4],$t4,$t4
+	srlx	$t3,32,@acc[4]
+	add	@acc[5],$t5,$t5
+	srlx	$t4,32,@acc[5]
+	add	@acc[6],$t6,$t6
+	srlx	$t5,32,@acc[6]
+	add	@acc[7],$t7,$t7
+	srlx	$t6,32,@acc[7]
+	srlx	$t7,32,@acc[0]		! "@acc[8]"
+___
+}
+$code.=<<___;
+	addcc	@acc[1],$t1,@acc[1]	! accumulate high parts
+	addccc	@acc[2],$t2,@acc[2]
+	addccc	@acc[3],$t3,@acc[3]
+	addccc	@acc[4],$t4,@acc[4]
+	addccc	@acc[5],$t5,@acc[5]
+	addccc	@acc[6],$t6,@acc[6]
+	addccc	@acc[7],$t7,@acc[7]
+	addccc	@acc[0],$carry,@acc[0]	! "@acc[8]"
+	addc	%g0,%g0,$carry
+
+	addcc	@acc[3],$t0,@acc[3]	! multiplication-less reduction
+	addccc	@acc[4],%g0,@acc[4]
+	addccc	@acc[5],%g0,@acc[5]
+	addccc	@acc[6],$t0,@acc[6]
+	addccc	@acc[7],%g0,@acc[7]
+	addccc	@acc[0],$t0,@acc[0]	! "@acc[8]"
+	addc	$carry,%g0,$carry
+	subcc	@acc[7],$t0,@acc[7]
+	subccc	@acc[0],%g0,@acc[0]	! "@acc[8]"
+	subc	$carry,%g0,$carry	! top-most carry
+___
+	push(@acc,shift(@acc));		# rotate registers to omit acc[0]
+$code.=<<___;
+	! Final step is "if result > mod, subtract mod", but we do it
+	! "other way around", namely subtract modulus from result
+	! and if it borrowed, add modulus back.
+
+	subcc	@acc[0],-1,@acc[0]	! subtract modulus
+	subccc	@acc[1],-1,@acc[1]
+	subccc	@acc[2],-1,@acc[2]
+	subccc	@acc[3],0,@acc[3]
+	subccc	@acc[4],0,@acc[4]
+	subccc	@acc[5],0,@acc[5]
+	subccc	@acc[6],1,@acc[6]
+	subccc	@acc[7],-1,@acc[7]
+	subc	$carry,0,$carry		! broadcast borrow bit
+
+	! Note that because mod has special form, i.e. consists of
+	! 0xffffffff, 1 and 0s, we can conditionally synthesize it by
+	! using value of broadcasted borrow and the borrow bit itself.
+	! To minimize dependency chain we first broadcast and then
+	! extract the bit by negating (follow $bi).
+
+	addcc	@acc[0],$carry,@acc[0]	! add modulus or zero
+	addccc	@acc[1],$carry,@acc[1]
+	neg	$carry,$bi
+	st	@acc[0],[$rp]
+	addccc	@acc[2],$carry,@acc[2]
+	st	@acc[1],[$rp+4]
+	addccc	@acc[3],0,@acc[3]
+	st	@acc[2],[$rp+8]
+	addccc	@acc[4],0,@acc[4]
+	st	@acc[3],[$rp+12]
+	addccc	@acc[5],0,@acc[5]
+	st	@acc[4],[$rp+16]
+	addccc	@acc[6],$bi,@acc[6]
+	st	@acc[5],[$rp+20]
+	addc	@acc[7],$carry,@acc[7]
+	st	@acc[6],[$rp+24]
+	retl
+	st	@acc[7],[$rp+28]
+.type	__ecp_nistz256_mul_mont,#function
+.size	__ecp_nistz256_mul_mont,.-__ecp_nistz256_mul_mont
+
+! void	ecp_nistz256_add(BN_ULONG %i0[8],const BN_ULONG %i1[8],
+!					 const BN_ULONG %i2[8]);
+.globl	ecp_nistz256_add
+.align	32
+ecp_nistz256_add:
+	save	%sp,-STACK_FRAME,%sp
+	ld	[$ap],@acc[0]
+	ld	[$ap+4],@acc[1]
+	ld	[$ap+8],@acc[2]
+	ld	[$ap+12],@acc[3]
+	ld	[$ap+16],@acc[4]
+	ld	[$ap+20],@acc[5]
+	ld	[$ap+24],@acc[6]
+	call	__ecp_nistz256_add
+	ld	[$ap+28],@acc[7]
+	ret
+	restore
+.type	ecp_nistz256_add,#function
+.size	ecp_nistz256_add,.-ecp_nistz256_add
+
+.align	32
+__ecp_nistz256_add:
+	ld	[$bp+0],$t0		! b[0]
+	ld	[$bp+4],$t1
+	ld	[$bp+8],$t2
+	ld	[$bp+12],$t3
+	addcc	@acc[0],$t0,@acc[0]
+	ld	[$bp+16],$t4
+	ld	[$bp+20],$t5
+	addccc	@acc[1],$t1,@acc[1]
+	ld	[$bp+24],$t6
+	ld	[$bp+28],$t7
+	addccc	@acc[2],$t2,@acc[2]
+	addccc	@acc[3],$t3,@acc[3]
+	addccc	@acc[4],$t4,@acc[4]
+	addccc	@acc[5],$t5,@acc[5]
+	addccc	@acc[6],$t6,@acc[6]
+	addccc	@acc[7],$t7,@acc[7]
+	addc	%g0,%g0,$carry
+
+.Lreduce_by_sub:
+
+	! if a+b >= modulus, subtract modulus.
+	!
+	! But since comparison implies subtraction, we subtract
+	! modulus and then add it back if subraction borrowed.
+
+	subcc	@acc[0],-1,@acc[0]
+	subccc	@acc[1],-1,@acc[1]
+	subccc	@acc[2],-1,@acc[2]
+	subccc	@acc[3], 0,@acc[3]
+	subccc	@acc[4], 0,@acc[4]
+	subccc	@acc[5], 0,@acc[5]
+	subccc	@acc[6], 1,@acc[6]
+	subccc	@acc[7],-1,@acc[7]
+	subc	$carry,0,$carry
+
+	! Note that because mod has special form, i.e. consists of
+	! 0xffffffff, 1 and 0s, we can conditionally synthesize it by
+	! using value of borrow and its negative.
+
+	addcc	@acc[0],$carry,@acc[0]	! add synthesized modulus
+	addccc	@acc[1],$carry,@acc[1]
+	neg	$carry,$bi
+	st	@acc[0],[$rp]
+	addccc	@acc[2],$carry,@acc[2]
+	st	@acc[1],[$rp+4]
+	addccc	@acc[3],0,@acc[3]
+	st	@acc[2],[$rp+8]
+	addccc	@acc[4],0,@acc[4]
+	st	@acc[3],[$rp+12]
+	addccc	@acc[5],0,@acc[5]
+	st	@acc[4],[$rp+16]
+	addccc	@acc[6],$bi,@acc[6]
+	st	@acc[5],[$rp+20]
+	addc	@acc[7],$carry,@acc[7]
+	st	@acc[6],[$rp+24]
+	retl
+	st	@acc[7],[$rp+28]
+.type	__ecp_nistz256_add,#function
+.size	__ecp_nistz256_add,.-__ecp_nistz256_add
+
+! void	ecp_nistz256_mul_by_2(BN_ULONG %i0[8],const BN_ULONG %i1[8]);
+.globl	ecp_nistz256_mul_by_2
+.align	32
+ecp_nistz256_mul_by_2:
+	save	%sp,-STACK_FRAME,%sp
+	ld	[$ap],@acc[0]
+	ld	[$ap+4],@acc[1]
+	ld	[$ap+8],@acc[2]
+	ld	[$ap+12],@acc[3]
+	ld	[$ap+16],@acc[4]
+	ld	[$ap+20],@acc[5]
+	ld	[$ap+24],@acc[6]
+	call	__ecp_nistz256_mul_by_2
+	ld	[$ap+28],@acc[7]
+	ret
+	restore
+.type	ecp_nistz256_mul_by_2,#function
+.size	ecp_nistz256_mul_by_2,.-ecp_nistz256_mul_by_2
+
+.align	32
+__ecp_nistz256_mul_by_2:
+	addcc	@acc[0],@acc[0],@acc[0]	! a+a=2*a
+	addccc	@acc[1],@acc[1],@acc[1]
+	addccc	@acc[2],@acc[2],@acc[2]
+	addccc	@acc[3],@acc[3],@acc[3]
+	addccc	@acc[4],@acc[4],@acc[4]
+	addccc	@acc[5],@acc[5],@acc[5]
+	addccc	@acc[6],@acc[6],@acc[6]
+	addccc	@acc[7],@acc[7],@acc[7]
+	b	.Lreduce_by_sub
+	addc	%g0,%g0,$carry
+.type	__ecp_nistz256_mul_by_2,#function
+.size	__ecp_nistz256_mul_by_2,.-__ecp_nistz256_mul_by_2
+
+! void	ecp_nistz256_mul_by_3(BN_ULONG %i0[8],const BN_ULONG %i1[8]);
+.globl	ecp_nistz256_mul_by_3
+.align	32
+ecp_nistz256_mul_by_3:
+	save	%sp,-STACK_FRAME,%sp
+	ld	[$ap],@acc[0]
+	ld	[$ap+4],@acc[1]
+	ld	[$ap+8],@acc[2]
+	ld	[$ap+12],@acc[3]
+	ld	[$ap+16],@acc[4]
+	ld	[$ap+20],@acc[5]
+	ld	[$ap+24],@acc[6]
+	call	__ecp_nistz256_mul_by_3
+	ld	[$ap+28],@acc[7]
+	ret
+	restore
+.type	ecp_nistz256_mul_by_3,#function
+.size	ecp_nistz256_mul_by_3,.-ecp_nistz256_mul_by_3
+
+.align	32
+__ecp_nistz256_mul_by_3:
+	addcc	@acc[0],@acc[0],$t0	! a+a=2*a
+	addccc	@acc[1],@acc[1],$t1
+	addccc	@acc[2],@acc[2],$t2
+	addccc	@acc[3],@acc[3],$t3
+	addccc	@acc[4],@acc[4],$t4
+	addccc	@acc[5],@acc[5],$t5
+	addccc	@acc[6],@acc[6],$t6
+	addccc	@acc[7],@acc[7],$t7
+	addc	%g0,%g0,$carry
+
+	subcc	$t0,-1,$t0		! .Lreduce_by_sub but without stores
+	subccc	$t1,-1,$t1
+	subccc	$t2,-1,$t2
+	subccc	$t3, 0,$t3
+	subccc	$t4, 0,$t4
+	subccc	$t5, 0,$t5
+	subccc	$t6, 1,$t6
+	subccc	$t7,-1,$t7
+	subc	$carry,0,$carry
+
+	addcc	$t0,$carry,$t0		! add synthesized modulus
+	addccc	$t1,$carry,$t1
+	neg	$carry,$bi
+	addccc	$t2,$carry,$t2
+	addccc	$t3,0,$t3
+	addccc	$t4,0,$t4
+	addccc	$t5,0,$t5
+	addccc	$t6,$bi,$t6
+	addc	$t7,$carry,$t7
+
+	addcc	$t0,@acc[0],@acc[0]	! 2*a+a=3*a
+	addccc	$t1,@acc[1],@acc[1]
+	addccc	$t2,@acc[2],@acc[2]
+	addccc	$t3,@acc[3],@acc[3]
+	addccc	$t4,@acc[4],@acc[4]
+	addccc	$t5,@acc[5],@acc[5]
+	addccc	$t6,@acc[6],@acc[6]
+	addccc	$t7,@acc[7],@acc[7]
+	b	.Lreduce_by_sub
+	addc	%g0,%g0,$carry
+.type	__ecp_nistz256_mul_by_3,#function
+.size	__ecp_nistz256_mul_by_3,.-__ecp_nistz256_mul_by_3
+
+! void	ecp_nistz256_sub(BN_ULONG %i0[8],const BN_ULONG %i1[8],
+!				         const BN_ULONG %i2[8]);
+.globl	ecp_nistz256_sub
+.align	32
+ecp_nistz256_sub:
+	save	%sp,-STACK_FRAME,%sp
+	ld	[$ap],@acc[0]
+	ld	[$ap+4],@acc[1]
+	ld	[$ap+8],@acc[2]
+	ld	[$ap+12],@acc[3]
+	ld	[$ap+16],@acc[4]
+	ld	[$ap+20],@acc[5]
+	ld	[$ap+24],@acc[6]
+	call	__ecp_nistz256_sub_from
+	ld	[$ap+28],@acc[7]
+	ret
+	restore
+.type	ecp_nistz256_sub,#function
+.size	ecp_nistz256_sub,.-ecp_nistz256_sub
+
+! void	ecp_nistz256_neg(BN_ULONG %i0[8],const BN_ULONG %i1[8]);
+.globl	ecp_nistz256_neg
+.align	32
+ecp_nistz256_neg:
+	save	%sp,-STACK_FRAME,%sp
+	mov	$ap,$bp
+	mov	0,@acc[0]
+	mov	0,@acc[1]
+	mov	0,@acc[2]
+	mov	0,@acc[3]
+	mov	0,@acc[4]
+	mov	0,@acc[5]
+	mov	0,@acc[6]
+	call	__ecp_nistz256_sub_from
+	mov	0,@acc[7]
+	ret
+	restore
+.type	ecp_nistz256_neg,#function
+.size	ecp_nistz256_neg,.-ecp_nistz256_neg
+
+.align	32
+__ecp_nistz256_sub_from:
+	ld	[$bp+0],$t0		! b[0]
+	ld	[$bp+4],$t1
+	ld	[$bp+8],$t2
+	ld	[$bp+12],$t3
+	subcc	@acc[0],$t0,@acc[0]
+	ld	[$bp+16],$t4
+	ld	[$bp+20],$t5
+	subccc	@acc[1],$t1,@acc[1]
+	subccc	@acc[2],$t2,@acc[2]
+	ld	[$bp+24],$t6
+	ld	[$bp+28],$t7
+	subccc	@acc[3],$t3,@acc[3]
+	subccc	@acc[4],$t4,@acc[4]
+	subccc	@acc[5],$t5,@acc[5]
+	subccc	@acc[6],$t6,@acc[6]
+	subccc	@acc[7],$t7,@acc[7]
+	subc	%g0,%g0,$carry		! broadcast borrow bit
+
+.Lreduce_by_add:
+
+	! if a-b borrows, add modulus.
+	!
+	! Note that because mod has special form, i.e. consists of
+	! 0xffffffff, 1 and 0s, we can conditionally synthesize it by
+	! using value of broadcasted borrow and the borrow bit itself.
+	! To minimize dependency chain we first broadcast and then
+	! extract the bit by negating (follow $bi).
+
+	addcc	@acc[0],$carry,@acc[0]	! add synthesized modulus
+	addccc	@acc[1],$carry,@acc[1]
+	neg	$carry,$bi
+	st	@acc[0],[$rp]
+	addccc	@acc[2],$carry,@acc[2]
+	st	@acc[1],[$rp+4]
+	addccc	@acc[3],0,@acc[3]
+	st	@acc[2],[$rp+8]
+	addccc	@acc[4],0,@acc[4]
+	st	@acc[3],[$rp+12]
+	addccc	@acc[5],0,@acc[5]
+	st	@acc[4],[$rp+16]
+	addccc	@acc[6],$bi,@acc[6]
+	st	@acc[5],[$rp+20]
+	addc	@acc[7],$carry,@acc[7]
+	st	@acc[6],[$rp+24]
+	retl
+	st	@acc[7],[$rp+28]
+.type	__ecp_nistz256_sub_from,#function
+.size	__ecp_nistz256_sub_from,.-__ecp_nistz256_sub_from
+
+.align	32
+__ecp_nistz256_sub_morf:
+	ld	[$bp+0],$t0		! b[0]
+	ld	[$bp+4],$t1
+	ld	[$bp+8],$t2
+	ld	[$bp+12],$t3
+	subcc	$t0,@acc[0],@acc[0]
+	ld	[$bp+16],$t4
+	ld	[$bp+20],$t5
+	subccc	$t1,@acc[1],@acc[1]
+	subccc	$t2,@acc[2],@acc[2]
+	ld	[$bp+24],$t6
+	ld	[$bp+28],$t7
+	subccc	$t3,@acc[3],@acc[3]
+	subccc	$t4,@acc[4],@acc[4]
+	subccc	$t5,@acc[5],@acc[5]
+	subccc	$t6,@acc[6],@acc[6]
+	subccc	$t7,@acc[7],@acc[7]
+	b	.Lreduce_by_add
+	subc	%g0,%g0,$carry		! broadcast borrow bit
+.type	__ecp_nistz256_sub_morf,#function
+.size	__ecp_nistz256_sub_morf,.-__ecp_nistz256_sub_morf
+
+! void	ecp_nistz256_div_by_2(BN_ULONG %i0[8],const BN_ULONG %i1[8]);
+.globl	ecp_nistz256_div_by_2
+.align	32
+ecp_nistz256_div_by_2:
+	save	%sp,-STACK_FRAME,%sp
+	ld	[$ap],@acc[0]
+	ld	[$ap+4],@acc[1]
+	ld	[$ap+8],@acc[2]
+	ld	[$ap+12],@acc[3]
+	ld	[$ap+16],@acc[4]
+	ld	[$ap+20],@acc[5]
+	ld	[$ap+24],@acc[6]
+	call	__ecp_nistz256_div_by_2
+	ld	[$ap+28],@acc[7]
+	ret
+	restore
+.type	ecp_nistz256_div_by_2,#function
+.size	ecp_nistz256_div_by_2,.-ecp_nistz256_div_by_2
+
+.align	32
+__ecp_nistz256_div_by_2:
+	! ret = (a is odd ? a+mod : a) >> 1
+
+	and	@acc[0],1,$bi
+	neg	$bi,$carry
+	addcc	@acc[0],$carry,@acc[0]
+	addccc	@acc[1],$carry,@acc[1]
+	addccc	@acc[2],$carry,@acc[2]
+	addccc	@acc[3],0,@acc[3]
+	addccc	@acc[4],0,@acc[4]
+	addccc	@acc[5],0,@acc[5]
+	addccc	@acc[6],$bi,@acc[6]
+	addccc	@acc[7],$carry,@acc[7]
+	addc	%g0,%g0,$carry
+
+	! ret >>= 1
+
+	srl	@acc[0],1,@acc[0]
+	sll	@acc[1],31,$t0
+	srl	@acc[1],1,@acc[1]
+	or	@acc[0],$t0,@acc[0]
+	sll	@acc[2],31,$t1
+	srl	@acc[2],1,@acc[2]
+	or	@acc[1],$t1,@acc[1]
+	sll	@acc[3],31,$t2
+	st	@acc[0],[$rp]
+	srl	@acc[3],1,@acc[3]
+	or	@acc[2],$t2,@acc[2]
+	sll	@acc[4],31,$t3
+	st	@acc[1],[$rp+4]
+	srl	@acc[4],1,@acc[4]
+	or	@acc[3],$t3,@acc[3]
+	sll	@acc[5],31,$t4
+	st	@acc[2],[$rp+8]
+	srl	@acc[5],1,@acc[5]
+	or	@acc[4],$t4,@acc[4]
+	sll	@acc[6],31,$t5
+	st	@acc[3],[$rp+12]
+	srl	@acc[6],1,@acc[6]
+	or	@acc[5],$t5,@acc[5]
+	sll	@acc[7],31,$t6
+	st	@acc[4],[$rp+16]
+	srl	@acc[7],1,@acc[7]
+	or	@acc[6],$t6,@acc[6]
+	sll	$carry,31,$t7
+	st	@acc[5],[$rp+20]
+	or	@acc[7],$t7,@acc[7]
+	st	@acc[6],[$rp+24]
+	retl
+	st	@acc[7],[$rp+28]
+.type	__ecp_nistz256_div_by_2,#function
+.size	__ecp_nistz256_div_by_2,.-__ecp_nistz256_div_by_2
+___
+
+########################################################################
+# following subroutines are "literal" implementation of those found in
+# ecp_nistz256.c
+#
+########################################################################
+# void ecp_nistz256_point_double(P256_POINT *out,const P256_POINT *inp);
+#
+{
+my ($S,$M,$Zsqr,$tmp0)=map(32*$_,(0..3));
+# above map() describes stack layout with 4 temporary
+# 256-bit vectors on top.
+
+$code.=<<___;
+#ifdef __PIC__
+SPARC_PIC_THUNK(%g1)
+#endif
+
+.globl	ecp_nistz256_point_double
+.align	32
+ecp_nistz256_point_double:
+	SPARC_LOAD_ADDRESS_LEAF(OPENSSL_sparcv9cap_P,%g1,%g5)
+	ld	[%g1],%g1		! OPENSSL_sparcv9cap_P[0]
+	and	%g1,(SPARCV9_VIS3|SPARCV9_64BIT_STACK),%g1
+	cmp	%g1,(SPARCV9_VIS3|SPARCV9_64BIT_STACK)
+	be	ecp_nistz256_point_double_vis3
+	nop
+
+	save	%sp,-STACK_FRAME-32*4,%sp
+
+	mov	$rp,$rp_real
+	mov	$ap,$ap_real
+
+.Lpoint_double_shortcut:
+	ld	[$ap+32],@acc[0]
+	ld	[$ap+32+4],@acc[1]
+	ld	[$ap+32+8],@acc[2]
+	ld	[$ap+32+12],@acc[3]
+	ld	[$ap+32+16],@acc[4]
+	ld	[$ap+32+20],@acc[5]
+	ld	[$ap+32+24],@acc[6]
+	ld	[$ap+32+28],@acc[7]
+	call	__ecp_nistz256_mul_by_2	! p256_mul_by_2(S, in_y);
+	add	%sp,LOCALS+$S,$rp
+
+	add	$ap_real,64,$bp
+	add	$ap_real,64,$ap
+	call	__ecp_nistz256_mul_mont	! p256_sqr_mont(Zsqr, in_z);
+	add	%sp,LOCALS+$Zsqr,$rp
+
+	add	$ap_real,0,$bp
+	call	__ecp_nistz256_add	! p256_add(M, Zsqr, in_x);
+	add	%sp,LOCALS+$M,$rp
+
+	add	%sp,LOCALS+$S,$bp
+	add	%sp,LOCALS+$S,$ap
+	call	__ecp_nistz256_mul_mont	! p256_sqr_mont(S, S);
+	add	%sp,LOCALS+$S,$rp
+
+	ld	[$ap_real],@acc[0]
+	add	%sp,LOCALS+$Zsqr,$bp
+	ld	[$ap_real+4],@acc[1]
+	ld	[$ap_real+8],@acc[2]
+	ld	[$ap_real+12],@acc[3]
+	ld	[$ap_real+16],@acc[4]
+	ld	[$ap_real+20],@acc[5]
+	ld	[$ap_real+24],@acc[6]
+	ld	[$ap_real+28],@acc[7]
+	call	__ecp_nistz256_sub_from	! p256_sub(Zsqr, in_x, Zsqr);
+	add	%sp,LOCALS+$Zsqr,$rp
+
+	add	$ap_real,32,$bp
+	add	$ap_real,64,$ap
+	call	__ecp_nistz256_mul_mont	! p256_mul_mont(tmp0, in_z, in_y);
+	add	%sp,LOCALS+$tmp0,$rp
+
+	call	__ecp_nistz256_mul_by_2	! p256_mul_by_2(res_z, tmp0);
+	add	$rp_real,64,$rp
+
+	add	%sp,LOCALS+$Zsqr,$bp
+	add	%sp,LOCALS+$M,$ap
+	call	__ecp_nistz256_mul_mont	! p256_mul_mont(M, M, Zsqr);
+	add	%sp,LOCALS+$M,$rp
+
+	call	__ecp_nistz256_mul_by_3	! p256_mul_by_3(M, M);
+	add	%sp,LOCALS+$M,$rp
+
+	add	%sp,LOCALS+$S,$bp
+	add	%sp,LOCALS+$S,$ap
+	call	__ecp_nistz256_mul_mont	! p256_sqr_mont(tmp0, S);
+	add	%sp,LOCALS+$tmp0,$rp
+
+	call	__ecp_nistz256_div_by_2	! p256_div_by_2(res_y, tmp0);
+	add	$rp_real,32,$rp
+
+	add	$ap_real,0,$bp
+	add	%sp,LOCALS+$S,$ap
+	call	__ecp_nistz256_mul_mont	! p256_mul_mont(S, S, in_x);
+	add	%sp,LOCALS+$S,$rp
+
+	call	__ecp_nistz256_mul_by_2	! p256_mul_by_2(tmp0, S);
+	add	%sp,LOCALS+$tmp0,$rp
+
+	add	%sp,LOCALS+$M,$bp
+	add	%sp,LOCALS+$M,$ap
+	call	__ecp_nistz256_mul_mont	! p256_sqr_mont(res_x, M);
+	add	$rp_real,0,$rp
+
+	add	%sp,LOCALS+$tmp0,$bp
+	call	__ecp_nistz256_sub_from	! p256_sub(res_x, res_x, tmp0);
+	add	$rp_real,0,$rp
+
+	add	%sp,LOCALS+$S,$bp
+	call	__ecp_nistz256_sub_morf	! p256_sub(S, S, res_x);
+	add	%sp,LOCALS+$S,$rp
+
+	add	%sp,LOCALS+$M,$bp
+	add	%sp,LOCALS+$S,$ap
+	call	__ecp_nistz256_mul_mont	! p256_mul_mont(S, S, M);
+	add	%sp,LOCALS+$S,$rp
+
+	add	$rp_real,32,$bp
+	call	__ecp_nistz256_sub_from	! p256_sub(res_y, S, res_y);
+	add	$rp_real,32,$rp
+
+	ret
+	restore
+.type	ecp_nistz256_point_double,#function
+.size	ecp_nistz256_point_double,.-ecp_nistz256_point_double
+___
+}
+
+########################################################################
+# void ecp_nistz256_point_add(P256_POINT *out,const P256_POINT *in1,
+#			      const P256_POINT *in2);
+{
+my ($res_x,$res_y,$res_z,
+    $H,$Hsqr,$R,$Rsqr,$Hcub,
+    $U1,$U2,$S1,$S2)=map(32*$_,(0..11));
+my ($Z1sqr, $Z2sqr) = ($Hsqr, $Rsqr);
+
+# above map() describes stack layout with 12 temporary
+# 256-bit vectors on top. Then we reserve some space for
+# !in1infty, !in2infty, result of check for zero and return pointer.
+
+my $bp_real=$rp_real;
+
+$code.=<<___;
+.globl	ecp_nistz256_point_add
+.align	32
+ecp_nistz256_point_add:
+	SPARC_LOAD_ADDRESS_LEAF(OPENSSL_sparcv9cap_P,%g1,%g5)
+	ld	[%g1],%g1		! OPENSSL_sparcv9cap_P[0]
+	and	%g1,(SPARCV9_VIS3|SPARCV9_64BIT_STACK),%g1
+	cmp	%g1,(SPARCV9_VIS3|SPARCV9_64BIT_STACK)
+	be	ecp_nistz256_point_add_vis3
+	nop
+
+	save	%sp,-STACK_FRAME-32*12-32,%sp
+
+	stx	$rp,[%fp+STACK_BIAS-8]	! off-load $rp
+	mov	$ap,$ap_real
+	mov	$bp,$bp_real
+
+	ld	[$bp+64],$t0		! in2_z
+	ld	[$bp+64+4],$t1
+	ld	[$bp+64+8],$t2
+	ld	[$bp+64+12],$t3
+	ld	[$bp+64+16],$t4
+	ld	[$bp+64+20],$t5
+	ld	[$bp+64+24],$t6
+	ld	[$bp+64+28],$t7
+	or	$t1,$t0,$t0
+	or	$t3,$t2,$t2
+	or	$t5,$t4,$t4
+	or	$t7,$t6,$t6
+	or	$t2,$t0,$t0
+	or	$t6,$t4,$t4
+	or	$t4,$t0,$t0		! !in2infty
+	movrnz	$t0,-1,$t0
+	st	$t0,[%fp+STACK_BIAS-12]
+
+	ld	[$ap+64],$t0		! in1_z
+	ld	[$ap+64+4],$t1
+	ld	[$ap+64+8],$t2
+	ld	[$ap+64+12],$t3
+	ld	[$ap+64+16],$t4
+	ld	[$ap+64+20],$t5
+	ld	[$ap+64+24],$t6
+	ld	[$ap+64+28],$t7
+	or	$t1,$t0,$t0
+	or	$t3,$t2,$t2
+	or	$t5,$t4,$t4
+	or	$t7,$t6,$t6
+	or	$t2,$t0,$t0
+	or	$t6,$t4,$t4
+	or	$t4,$t0,$t0		! !in1infty
+	movrnz	$t0,-1,$t0
+	st	$t0,[%fp+STACK_BIAS-16]
+
+	add	$bp_real,64,$bp
+	add	$bp_real,64,$ap
+	call	__ecp_nistz256_mul_mont	! p256_sqr_mont(Z2sqr, in2_z);
+	add	%sp,LOCALS+$Z2sqr,$rp
+
+	add	$ap_real,64,$bp
+	add	$ap_real,64,$ap
+	call	__ecp_nistz256_mul_mont	! p256_sqr_mont(Z1sqr, in1_z);
+	add	%sp,LOCALS+$Z1sqr,$rp
+
+	add	$bp_real,64,$bp
+	add	%sp,LOCALS+$Z2sqr,$ap
+	call	__ecp_nistz256_mul_mont	! p256_mul_mont(S1, Z2sqr, in2_z);
+	add	%sp,LOCALS+$S1,$rp
+
+	add	$ap_real,64,$bp
+	add	%sp,LOCALS+$Z1sqr,$ap
+	call	__ecp_nistz256_mul_mont	! p256_mul_mont(S2, Z1sqr, in1_z);
+	add	%sp,LOCALS+$S2,$rp
+
+	add	$ap_real,32,$bp
+	add	%sp,LOCALS+$S1,$ap
+	call	__ecp_nistz256_mul_mont	! p256_mul_mont(S1, S1, in1_y);
+	add	%sp,LOCALS+$S1,$rp
+
+	add	$bp_real,32,$bp
+	add	%sp,LOCALS+$S2,$ap
+	call	__ecp_nistz256_mul_mont	! p256_mul_mont(S2, S2, in2_y);
+	add	%sp,LOCALS+$S2,$rp
+
+	add	%sp,LOCALS+$S1,$bp
+	call	__ecp_nistz256_sub_from	! p256_sub(R, S2, S1);
+	add	%sp,LOCALS+$R,$rp
+
+	or	@acc[1],@acc[0],@acc[0]	! see if result is zero
+	or	@acc[3],@acc[2],@acc[2]
+	or	@acc[5],@acc[4],@acc[4]
+	or	@acc[7],@acc[6],@acc[6]
+	or	@acc[2],@acc[0],@acc[0]
+	or	@acc[6],@acc[4],@acc[4]
+	or	@acc[4],@acc[0],@acc[0]
+	st	@acc[0],[%fp+STACK_BIAS-20]
+
+	add	$ap_real,0,$bp
+	add	%sp,LOCALS+$Z2sqr,$ap
+	call	__ecp_nistz256_mul_mont	! p256_mul_mont(U1, in1_x, Z2sqr);
+	add	%sp,LOCALS+$U1,$rp
+
+	add	$bp_real,0,$bp
+	add	%sp,LOCALS+$Z1sqr,$ap
+	call	__ecp_nistz256_mul_mont	! p256_mul_mont(U2, in2_x, Z1sqr);
+	add	%sp,LOCALS+$U2,$rp
+
+	add	%sp,LOCALS+$U1,$bp
+	call	__ecp_nistz256_sub_from	! p256_sub(H, U2, U1);
+	add	%sp,LOCALS+$H,$rp
+
+	or	@acc[1],@acc[0],@acc[0]	! see if result is zero
+	or	@acc[3],@acc[2],@acc[2]
+	or	@acc[5],@acc[4],@acc[4]
+	or	@acc[7],@acc[6],@acc[6]
+	or	@acc[2],@acc[0],@acc[0]
+	or	@acc[6],@acc[4],@acc[4]
+	orcc	@acc[4],@acc[0],@acc[0]
+
+	bne,pt	%icc,.Ladd_proceed	! is_equal(U1,U2)?
+	nop
+
+	ld	[%fp+STACK_BIAS-12],$t0
+	ld	[%fp+STACK_BIAS-16],$t1
+	ld	[%fp+STACK_BIAS-20],$t2
+	andcc	$t0,$t1,%g0
+	be,pt	%icc,.Ladd_proceed	! (in1infty || in2infty)?
+	nop
+	andcc	$t2,$t2,%g0
+	be,pt	%icc,.Ladd_double	! is_equal(S1,S2)?
+	nop
+
+	ldx	[%fp+STACK_BIAS-8],$rp
+	st	%g0,[$rp]
+	st	%g0,[$rp+4]
+	st	%g0,[$rp+8]
+	st	%g0,[$rp+12]
+	st	%g0,[$rp+16]
+	st	%g0,[$rp+20]
+	st	%g0,[$rp+24]
+	st	%g0,[$rp+28]
+	st	%g0,[$rp+32]
+	st	%g0,[$rp+32+4]
+	st	%g0,[$rp+32+8]
+	st	%g0,[$rp+32+12]
+	st	%g0,[$rp+32+16]
+	st	%g0,[$rp+32+20]
+	st	%g0,[$rp+32+24]
+	st	%g0,[$rp+32+28]
+	st	%g0,[$rp+64]
+	st	%g0,[$rp+64+4]
+	st	%g0,[$rp+64+8]
+	st	%g0,[$rp+64+12]
+	st	%g0,[$rp+64+16]
+	st	%g0,[$rp+64+20]
+	st	%g0,[$rp+64+24]
+	st	%g0,[$rp+64+28]
+	b	.Ladd_done
+	nop
+
+.align	16
+.Ladd_double:
+	ldx	[%fp+STACK_BIAS-8],$rp_real
+	mov	$ap_real,$ap
+	b	.Lpoint_double_shortcut
+	add	%sp,32*(12-4)+32,%sp	! difference in frame sizes
+
+.align	16
+.Ladd_proceed:
+	add	%sp,LOCALS+$R,$bp
+	add	%sp,LOCALS+$R,$ap
+	call	__ecp_nistz256_mul_mont	! p256_sqr_mont(Rsqr, R);
+	add	%sp,LOCALS+$Rsqr,$rp
+
+	add	$ap_real,64,$bp
+	add	%sp,LOCALS+$H,$ap
+	call	__ecp_nistz256_mul_mont	! p256_mul_mont(res_z, H, in1_z);
+	add	%sp,LOCALS+$res_z,$rp
+
+	add	%sp,LOCALS+$H,$bp
+	add	%sp,LOCALS+$H,$ap
+	call	__ecp_nistz256_mul_mont	! p256_sqr_mont(Hsqr, H);
+	add	%sp,LOCALS+$Hsqr,$rp
+
+	add	$bp_real,64,$bp
+	add	%sp,LOCALS+$res_z,$ap
+	call	__ecp_nistz256_mul_mont	! p256_mul_mont(res_z, res_z, in2_z);
+	add	%sp,LOCALS+$res_z,$rp
+
+	add	%sp,LOCALS+$H,$bp
+	add	%sp,LOCALS+$Hsqr,$ap
+	call	__ecp_nistz256_mul_mont	! p256_mul_mont(Hcub, Hsqr, H);
+	add	%sp,LOCALS+$Hcub,$rp
+
+	add	%sp,LOCALS+$U1,$bp
+	add	%sp,LOCALS+$Hsqr,$ap
+	call	__ecp_nistz256_mul_mont	! p256_mul_mont(U2, U1, Hsqr);
+	add	%sp,LOCALS+$U2,$rp
+
+	call	__ecp_nistz256_mul_by_2	! p256_mul_by_2(Hsqr, U2);
+	add	%sp,LOCALS+$Hsqr,$rp
+
+	add	%sp,LOCALS+$Rsqr,$bp
+	call	__ecp_nistz256_sub_morf	! p256_sub(res_x, Rsqr, Hsqr);
+	add	%sp,LOCALS+$res_x,$rp
+
+	add	%sp,LOCALS+$Hcub,$bp
+	call	__ecp_nistz256_sub_from	!  p256_sub(res_x, res_x, Hcub);
+	add	%sp,LOCALS+$res_x,$rp
+
+	add	%sp,LOCALS+$U2,$bp
+	call	__ecp_nistz256_sub_morf	! p256_sub(res_y, U2, res_x);
+	add	%sp,LOCALS+$res_y,$rp
+
+	add	%sp,LOCALS+$Hcub,$bp
+	add	%sp,LOCALS+$S1,$ap
+	call	__ecp_nistz256_mul_mont	! p256_mul_mont(S2, S1, Hcub);
+	add	%sp,LOCALS+$S2,$rp
+
+	add	%sp,LOCALS+$R,$bp
+	add	%sp,LOCALS+$res_y,$ap
+	call	__ecp_nistz256_mul_mont	! p256_mul_mont(res_y, res_y, R);
+	add	%sp,LOCALS+$res_y,$rp
+
+	add	%sp,LOCALS+$S2,$bp
+	call	__ecp_nistz256_sub_from	! p256_sub(res_y, res_y, S2);
+	add	%sp,LOCALS+$res_y,$rp
+
+	ld	[%fp+STACK_BIAS-16],$t1	! !in1infty
+	ld	[%fp+STACK_BIAS-12],$t2	! !in2infty
+	ldx	[%fp+STACK_BIAS-8],$rp
+___
+for($i=0;$i<96;$i+=8) {			# conditional moves
+$code.=<<___;
+	ld	[%sp+LOCALS+$i],@acc[0]		! res
+	ld	[%sp+LOCALS+$i+4],@acc[1]
+	ld	[$bp_real+$i],@acc[2]		! in2
+	ld	[$bp_real+$i+4],@acc[3]
+	ld	[$ap_real+$i],@acc[4]		! in1
+	ld	[$ap_real+$i+4],@acc[5]
+	movrz	$t1,@acc[2],@acc[0]
+	movrz	$t1,@acc[3],@acc[1]
+	movrz	$t2,@acc[4],@acc[0]
+	movrz	$t2,@acc[5],@acc[1]
+	st	@acc[0],[$rp+$i]
+	st	@acc[1],[$rp+$i+4]
+___
+}
+$code.=<<___;
+.Ladd_done:
+	ret
+	restore
+.type	ecp_nistz256_point_add,#function
+.size	ecp_nistz256_point_add,.-ecp_nistz256_point_add
+___
+}
+
+########################################################################
+# void ecp_nistz256_point_add_affine(P256_POINT *out,const P256_POINT *in1,
+#				     const P256_POINT_AFFINE *in2);
+{
+my ($res_x,$res_y,$res_z,
+    $U2,$S2,$H,$R,$Hsqr,$Hcub,$Rsqr)=map(32*$_,(0..9));
+my $Z1sqr = $S2;
+# above map() describes stack layout with 10 temporary
+# 256-bit vectors on top. Then we reserve some space for
+# !in1infty, !in2infty, result of check for zero and return pointer.
+
+my @ONE_mont=(1,0,0,-1,-1,-1,-2,0);
+my $bp_real=$rp_real;
+
+$code.=<<___;
+.globl	ecp_nistz256_point_add_affine
+.align	32
+ecp_nistz256_point_add_affine:
+	SPARC_LOAD_ADDRESS_LEAF(OPENSSL_sparcv9cap_P,%g1,%g5)
+	ld	[%g1],%g1		! OPENSSL_sparcv9cap_P[0]
+	and	%g1,(SPARCV9_VIS3|SPARCV9_64BIT_STACK),%g1
+	cmp	%g1,(SPARCV9_VIS3|SPARCV9_64BIT_STACK)
+	be	ecp_nistz256_point_add_affine_vis3
+	nop
+
+	save	%sp,-STACK_FRAME-32*10-32,%sp
+
+	stx	$rp,[%fp+STACK_BIAS-8]	! off-load $rp
+	mov	$ap,$ap_real
+	mov	$bp,$bp_real
+
+	ld	[$ap+64],$t0		! in1_z
+	ld	[$ap+64+4],$t1
+	ld	[$ap+64+8],$t2
+	ld	[$ap+64+12],$t3
+	ld	[$ap+64+16],$t4
+	ld	[$ap+64+20],$t5
+	ld	[$ap+64+24],$t6
+	ld	[$ap+64+28],$t7
+	or	$t1,$t0,$t0
+	or	$t3,$t2,$t2
+	or	$t5,$t4,$t4
+	or	$t7,$t6,$t6
+	or	$t2,$t0,$t0
+	or	$t6,$t4,$t4
+	or	$t4,$t0,$t0		! !in1infty
+	movrnz	$t0,-1,$t0
+	st	$t0,[%fp+STACK_BIAS-16]
+
+	ld	[$bp],@acc[0]		! in2_x
+	ld	[$bp+4],@acc[1]
+	ld	[$bp+8],@acc[2]
+	ld	[$bp+12],@acc[3]
+	ld	[$bp+16],@acc[4]
+	ld	[$bp+20],@acc[5]
+	ld	[$bp+24],@acc[6]
+	ld	[$bp+28],@acc[7]
+	ld	[$bp+32],$t0		! in2_y
+	ld	[$bp+32+4],$t1
+	ld	[$bp+32+8],$t2
+	ld	[$bp+32+12],$t3
+	ld	[$bp+32+16],$t4
+	ld	[$bp+32+20],$t5
+	ld	[$bp+32+24],$t6
+	ld	[$bp+32+28],$t7
+	or	@acc[1],@acc[0],@acc[0]
+	or	@acc[3],@acc[2],@acc[2]
+	or	@acc[5],@acc[4],@acc[4]
+	or	@acc[7],@acc[6],@acc[6]
+	or	@acc[2],@acc[0],@acc[0]
+	or	@acc[6],@acc[4],@acc[4]
+	or	@acc[4],@acc[0],@acc[0]
+	or	$t1,$t0,$t0
+	or	$t3,$t2,$t2
+	or	$t5,$t4,$t4
+	or	$t7,$t6,$t6
+	or	$t2,$t0,$t0
+	or	$t6,$t4,$t4
+	or	$t4,$t0,$t0
+	or	@acc[0],$t0,$t0		! !in2infty
+	movrnz	$t0,-1,$t0
+	st	$t0,[%fp+STACK_BIAS-12]
+
+	add	$ap_real,64,$bp
+	add	$ap_real,64,$ap
+	call	__ecp_nistz256_mul_mont	! p256_sqr_mont(Z1sqr, in1_z);
+	add	%sp,LOCALS+$Z1sqr,$rp
+
+	add	$bp_real,0,$bp
+	add	%sp,LOCALS+$Z1sqr,$ap
+	call	__ecp_nistz256_mul_mont	! p256_mul_mont(U2, Z1sqr, in2_x);
+	add	%sp,LOCALS+$U2,$rp
+
+	add	$ap_real,0,$bp
+	call	__ecp_nistz256_sub_from	! p256_sub(H, U2, in1_x);
+	add	%sp,LOCALS+$H,$rp
+
+	add	$ap_real,64,$bp
+	add	%sp,LOCALS+$Z1sqr,$ap
+	call	__ecp_nistz256_mul_mont	! p256_mul_mont(S2, Z1sqr, in1_z);
+	add	%sp,LOCALS+$S2,$rp
+
+	add	$ap_real,64,$bp
+	add	%sp,LOCALS+$H,$ap
+	call	__ecp_nistz256_mul_mont	! p256_mul_mont(res_z, H, in1_z);
+	add	%sp,LOCALS+$res_z,$rp
+
+	add	$bp_real,32,$bp
+	add	%sp,LOCALS+$S2,$ap
+	call	__ecp_nistz256_mul_mont	! p256_mul_mont(S2, S2, in2_y);
+	add	%sp,LOCALS+$S2,$rp
+
+	add	$ap_real,32,$bp
+	call	__ecp_nistz256_sub_from	! p256_sub(R, S2, in1_y);
+	add	%sp,LOCALS+$R,$rp
+
+	add	%sp,LOCALS+$H,$bp
+	add	%sp,LOCALS+$H,$ap
+	call	__ecp_nistz256_mul_mont	! p256_sqr_mont(Hsqr, H);
+	add	%sp,LOCALS+$Hsqr,$rp
+
+	add	%sp,LOCALS+$R,$bp
+	add	%sp,LOCALS+$R,$ap
+	call	__ecp_nistz256_mul_mont	! p256_sqr_mont(Rsqr, R);
+	add	%sp,LOCALS+$Rsqr,$rp
+
+	add	%sp,LOCALS+$H,$bp
+	add	%sp,LOCALS+$Hsqr,$ap
+	call	__ecp_nistz256_mul_mont	! p256_mul_mont(Hcub, Hsqr, H);
+	add	%sp,LOCALS+$Hcub,$rp
+
+	add	$ap_real,0,$bp
+	add	%sp,LOCALS+$Hsqr,$ap
+	call	__ecp_nistz256_mul_mont	! p256_mul_mont(U2, in1_x, Hsqr);
+	add	%sp,LOCALS+$U2,$rp
+
+	call	__ecp_nistz256_mul_by_2	! p256_mul_by_2(Hsqr, U2);
+	add	%sp,LOCALS+$Hsqr,$rp
+
+	add	%sp,LOCALS+$Rsqr,$bp
+	call	__ecp_nistz256_sub_morf	! p256_sub(res_x, Rsqr, Hsqr);
+	add	%sp,LOCALS+$res_x,$rp
+
+	add	%sp,LOCALS+$Hcub,$bp
+	call	__ecp_nistz256_sub_from	!  p256_sub(res_x, res_x, Hcub);
+	add	%sp,LOCALS+$res_x,$rp
+
+	add	%sp,LOCALS+$U2,$bp
+	call	__ecp_nistz256_sub_morf	! p256_sub(res_y, U2, res_x);
+	add	%sp,LOCALS+$res_y,$rp
+
+	add	$ap_real,32,$bp
+	add	%sp,LOCALS+$Hcub,$ap
+	call	__ecp_nistz256_mul_mont	! p256_mul_mont(S2, in1_y, Hcub);
+	add	%sp,LOCALS+$S2,$rp
+
+	add	%sp,LOCALS+$R,$bp
+	add	%sp,LOCALS+$res_y,$ap
+	call	__ecp_nistz256_mul_mont	! p256_mul_mont(res_y, res_y, R);
+	add	%sp,LOCALS+$res_y,$rp
+
+	add	%sp,LOCALS+$S2,$bp
+	call	__ecp_nistz256_sub_from	! p256_sub(res_y, res_y, S2);
+	add	%sp,LOCALS+$res_y,$rp
+
+	ld	[%fp+STACK_BIAS-16],$t1	! !in1infty
+	ld	[%fp+STACK_BIAS-12],$t2	! !in2infty
+	ldx	[%fp+STACK_BIAS-8],$rp
+___
+for($i=0;$i<64;$i+=8) {			# conditional moves
+$code.=<<___;
+	ld	[%sp+LOCALS+$i],@acc[0]		! res
+	ld	[%sp+LOCALS+$i+4],@acc[1]
+	ld	[$bp_real+$i],@acc[2]		! in2
+	ld	[$bp_real+$i+4],@acc[3]
+	ld	[$ap_real+$i],@acc[4]		! in1
+	ld	[$ap_real+$i+4],@acc[5]
+	movrz	$t1,@acc[2],@acc[0]
+	movrz	$t1,@acc[3],@acc[1]
+	movrz	$t2,@acc[4],@acc[0]
+	movrz	$t2,@acc[5],@acc[1]
+	st	@acc[0],[$rp+$i]
+	st	@acc[1],[$rp+$i+4]
+___
+}
+for(;$i<96;$i+=8) {
+my $j=($i-64)/4;
+$code.=<<___;
+	ld	[%sp+LOCALS+$i],@acc[0]		! res
+	ld	[%sp+LOCALS+$i+4],@acc[1]
+	ld	[$ap_real+$i],@acc[4]		! in1
+	ld	[$ap_real+$i+4],@acc[5]
+	movrz	$t1,@ONE_mont[$j],@acc[0]
+	movrz	$t1,@ONE_mont[$j+1],@acc[1]
+	movrz	$t2,@acc[4],@acc[0]
+	movrz	$t2,@acc[5],@acc[1]
+	st	@acc[0],[$rp+$i]
+	st	@acc[1],[$rp+$i+4]
+___
+}
+$code.=<<___;
+	ret
+	restore
+.type	ecp_nistz256_point_add_affine,#function
+.size	ecp_nistz256_point_add_affine,.-ecp_nistz256_point_add_affine
+___
+}								}}}
+{{{
+my ($out,$inp,$index)=map("%i$_",(0..2));
+my $mask="%o0";
+
+$code.=<<___;
+! void	ecp_nistz256_scatter_w5(void *%i0,const P256_POINT *%i1,
+!					  int %i2);
+.globl	ecp_nistz256_scatter_w5
+.align	32
+ecp_nistz256_scatter_w5:
+	save	%sp,-STACK_FRAME,%sp
+
+	sll	$index,2,$index
+	add	$out,$index,$out
+
+	ld	[$inp],%l0		! X
+	ld	[$inp+4],%l1
+	ld	[$inp+8],%l2
+	ld	[$inp+12],%l3
+	ld	[$inp+16],%l4
+	ld	[$inp+20],%l5
+	ld	[$inp+24],%l6
+	ld	[$inp+28],%l7
+	add	$inp,32,$inp
+	st	%l0,[$out+64*0-4]
+	st	%l1,[$out+64*1-4]
+	st	%l2,[$out+64*2-4]
+	st	%l3,[$out+64*3-4]
+	st	%l4,[$out+64*4-4]
+	st	%l5,[$out+64*5-4]
+	st	%l6,[$out+64*6-4]
+	st	%l7,[$out+64*7-4]
+	add	$out,64*8,$out
+
+	ld	[$inp],%l0		! Y
+	ld	[$inp+4],%l1
+	ld	[$inp+8],%l2
+	ld	[$inp+12],%l3
+	ld	[$inp+16],%l4
+	ld	[$inp+20],%l5
+	ld	[$inp+24],%l6
+	ld	[$inp+28],%l7
+	add	$inp,32,$inp
+	st	%l0,[$out+64*0-4]
+	st	%l1,[$out+64*1-4]
+	st	%l2,[$out+64*2-4]
+	st	%l3,[$out+64*3-4]
+	st	%l4,[$out+64*4-4]
+	st	%l5,[$out+64*5-4]
+	st	%l6,[$out+64*6-4]
+	st	%l7,[$out+64*7-4]
+	add	$out,64*8,$out
+
+	ld	[$inp],%l0		! Z
+	ld	[$inp+4],%l1
+	ld	[$inp+8],%l2
+	ld	[$inp+12],%l3
+	ld	[$inp+16],%l4
+	ld	[$inp+20],%l5
+	ld	[$inp+24],%l6
+	ld	[$inp+28],%l7
+	st	%l0,[$out+64*0-4]
+	st	%l1,[$out+64*1-4]
+	st	%l2,[$out+64*2-4]
+	st	%l3,[$out+64*3-4]
+	st	%l4,[$out+64*4-4]
+	st	%l5,[$out+64*5-4]
+	st	%l6,[$out+64*6-4]
+	st	%l7,[$out+64*7-4]
+
+	ret
+	restore
+.type	ecp_nistz256_scatter_w5,#function
+.size	ecp_nistz256_scatter_w5,.-ecp_nistz256_scatter_w5
+
+! void	ecp_nistz256_gather_w5(P256_POINT *%i0,const void *%i1,
+!					       int %i2);
+.globl	ecp_nistz256_gather_w5
+.align	32
+ecp_nistz256_gather_w5:
+	save	%sp,-STACK_FRAME,%sp
+
+	neg	$index,$mask
+	srax	$mask,63,$mask
+
+	add	$index,$mask,$index
+	sll	$index,2,$index
+	add	$inp,$index,$inp
+
+	ld	[$inp+64*0],%l0
+	ld	[$inp+64*1],%l1
+	ld	[$inp+64*2],%l2
+	ld	[$inp+64*3],%l3
+	ld	[$inp+64*4],%l4
+	ld	[$inp+64*5],%l5
+	ld	[$inp+64*6],%l6
+	ld	[$inp+64*7],%l7
+	add	$inp,64*8,$inp
+	and	%l0,$mask,%l0
+	and	%l1,$mask,%l1
+	st	%l0,[$out]		! X
+	and	%l2,$mask,%l2
+	st	%l1,[$out+4]
+	and	%l3,$mask,%l3
+	st	%l2,[$out+8]
+	and	%l4,$mask,%l4
+	st	%l3,[$out+12]
+	and	%l5,$mask,%l5
+	st	%l4,[$out+16]
+	and	%l6,$mask,%l6
+	st	%l5,[$out+20]
+	and	%l7,$mask,%l7
+	st	%l6,[$out+24]
+	st	%l7,[$out+28]
+	add	$out,32,$out
+
+	ld	[$inp+64*0],%l0
+	ld	[$inp+64*1],%l1
+	ld	[$inp+64*2],%l2
+	ld	[$inp+64*3],%l3
+	ld	[$inp+64*4],%l4
+	ld	[$inp+64*5],%l5
+	ld	[$inp+64*6],%l6
+	ld	[$inp+64*7],%l7
+	add	$inp,64*8,$inp
+	and	%l0,$mask,%l0
+	and	%l1,$mask,%l1
+	st	%l0,[$out]		! Y
+	and	%l2,$mask,%l2
+	st	%l1,[$out+4]
+	and	%l3,$mask,%l3
+	st	%l2,[$out+8]
+	and	%l4,$mask,%l4
+	st	%l3,[$out+12]
+	and	%l5,$mask,%l5
+	st	%l4,[$out+16]
+	and	%l6,$mask,%l6
+	st	%l5,[$out+20]
+	and	%l7,$mask,%l7
+	st	%l6,[$out+24]
+	st	%l7,[$out+28]
+	add	$out,32,$out
+
+	ld	[$inp+64*0],%l0
+	ld	[$inp+64*1],%l1
+	ld	[$inp+64*2],%l2
+	ld	[$inp+64*3],%l3
+	ld	[$inp+64*4],%l4
+	ld	[$inp+64*5],%l5
+	ld	[$inp+64*6],%l6
+	ld	[$inp+64*7],%l7
+	and	%l0,$mask,%l0
+	and	%l1,$mask,%l1
+	st	%l0,[$out]		! Z
+	and	%l2,$mask,%l2
+	st	%l1,[$out+4]
+	and	%l3,$mask,%l3
+	st	%l2,[$out+8]
+	and	%l4,$mask,%l4
+	st	%l3,[$out+12]
+	and	%l5,$mask,%l5
+	st	%l4,[$out+16]
+	and	%l6,$mask,%l6
+	st	%l5,[$out+20]
+	and	%l7,$mask,%l7
+	st	%l6,[$out+24]
+	st	%l7,[$out+28]
+
+	ret
+	restore
+.type	ecp_nistz256_gather_w5,#function
+.size	ecp_nistz256_gather_w5,.-ecp_nistz256_gather_w5
+
+! void	ecp_nistz256_scatter_w7(void *%i0,const P256_POINT_AFFINE *%i1,
+!					  int %i2);
+.globl	ecp_nistz256_scatter_w7
+.align	32
+ecp_nistz256_scatter_w7:
+	save	%sp,-STACK_FRAME,%sp
+	nop
+	add	$out,$index,$out
+	mov	64/4,$index
+.Loop_scatter_w7:
+	ld	[$inp],%l0
+	add	$inp,4,$inp
+	subcc	$index,1,$index
+	stb	%l0,[$out+64*0-1]
+	srl	%l0,8,%l1
+	stb	%l1,[$out+64*1-1]
+	srl	%l0,16,%l2
+	stb	%l2,[$out+64*2-1]
+	srl	%l0,24,%l3
+	stb	%l3,[$out+64*3-1]
+	bne	.Loop_scatter_w7
+	add	$out,64*4,$out
+
+	ret
+	restore
+.type	ecp_nistz256_scatter_w7,#function
+.size	ecp_nistz256_scatter_w7,.-ecp_nistz256_scatter_w7
+
+! void	ecp_nistz256_gather_w7(P256_POINT_AFFINE *%i0,const void *%i1,
+!						      int %i2);
+.globl	ecp_nistz256_gather_w7
+.align	32
+ecp_nistz256_gather_w7:
+	save	%sp,-STACK_FRAME,%sp
+
+	neg	$index,$mask
+	srax	$mask,63,$mask
+
+	add	$index,$mask,$index
+	add	$inp,$index,$inp
+	mov	64/4,$index
+
+.Loop_gather_w7:
+	ldub	[$inp+64*0],%l0
+	prefetch [$inp+3840+64*0],1
+	subcc	$index,1,$index
+	ldub	[$inp+64*1],%l1
+	prefetch [$inp+3840+64*1],1
+	ldub	[$inp+64*2],%l2
+	prefetch [$inp+3840+64*2],1
+	ldub	[$inp+64*3],%l3
+	prefetch [$inp+3840+64*3],1
+	add	$inp,64*4,$inp
+	sll	%l1,8,%l1
+	sll	%l2,16,%l2
+	or	%l0,%l1,%l0
+	sll	%l3,24,%l3
+	or	%l0,%l2,%l0
+	or	%l0,%l3,%l0
+	and	%l0,$mask,%l0
+	st	%l0,[$out]
+	bne	.Loop_gather_w7
+	add	$out,4,$out
+
+	ret
+	restore
+.type	ecp_nistz256_gather_w7,#function
+.size	ecp_nistz256_gather_w7,.-ecp_nistz256_gather_w7
+___
+}}}
+{{{
+########################################################################
+# Following subroutines are VIS3 counterparts of those above that
+# implement ones found in ecp_nistz256.c. Key difference is that they
+# use 128-bit muliplication and addition with 64-bit carry, and in order
+# to do that they perform conversion from uin32_t[8] to uint64_t[4] upon
+# entry and vice versa on return.
+#
+my ($rp,$ap,$bp)=map("%i$_",(0..2));
+my ($t0,$t1,$t2,$t3,$a0,$a1,$a2,$a3)=map("%l$_",(0..7));
+my ($acc0,$acc1,$acc2,$acc3,$acc4,$acc5)=map("%o$_",(0..5));
+my ($bi,$poly1,$poly3,$minus1)=(map("%i$_",(3..5)),"%g1");
+my ($rp_real,$ap_real)=("%g2","%g3");
+my ($acc6,$acc7)=($bp,$bi);	# used in squaring
+
+$code.=<<___;
+.align	32
+__ecp_nistz256_mul_by_2_vis3:
+	addcc	$acc0,$acc0,$acc0
+	addxccc	$acc1,$acc1,$acc1
+	addxccc	$acc2,$acc2,$acc2
+	addxccc	$acc3,$acc3,$acc3
+	b	.Lreduce_by_sub_vis3
+	addxc	%g0,%g0,$acc4		! did it carry?
+.type	__ecp_nistz256_mul_by_2_vis3,#function
+.size	__ecp_nistz256_mul_by_2_vis3,.-__ecp_nistz256_mul_by_2_vis3
+
+.align	32
+__ecp_nistz256_add_vis3:
+	ldx	[$bp+0],$t0
+	ldx	[$bp+8],$t1
+	ldx	[$bp+16],$t2
+	ldx	[$bp+24],$t3
+
+__ecp_nistz256_add_noload_vis3:
+
+	addcc	$t0,$acc0,$acc0
+	addxccc	$t1,$acc1,$acc1
+	addxccc	$t2,$acc2,$acc2
+	addxccc	$t3,$acc3,$acc3
+	addxc	%g0,%g0,$acc4		! did it carry?
+
+.Lreduce_by_sub_vis3:
+
+	addcc	$acc0,1,$t0		! add -modulus, i.e. subtract
+	addxccc	$acc1,$poly1,$t1
+	addxccc	$acc2,$minus1,$t2
+	addxccc	$acc3,$poly3,$t3
+	addxc	$acc4,$minus1,$acc4
+
+	movrz	$acc4,$t0,$acc0		! ret = borrow ? ret : ret-modulus
+	movrz	$acc4,$t1,$acc1
+	stx	$acc0,[$rp]
+	movrz	$acc4,$t2,$acc2
+	stx	$acc1,[$rp+8]
+	movrz	$acc4,$t3,$acc3
+	stx	$acc2,[$rp+16]
+	retl
+	stx	$acc3,[$rp+24]
+.type	__ecp_nistz256_add_vis3,#function
+.size	__ecp_nistz256_add_vis3,.-__ecp_nistz256_add_vis3
+
+! Trouble with subtraction is that there is no subtraction with 64-bit
+! borrow, only with 32-bit one. For this reason we "decompose" 64-bit
+! $acc0-$acc3 to 32-bit values and pick b[4] in 32-bit pieces. But
+! recall that SPARC is big-endian, which is why you'll observe that
+! b[4] is accessed as 4-0-12-8-20-16-28-24. And prior reduction we
+! "collect" result back to 64-bit $acc0-$acc3.
+.align	32
+__ecp_nistz256_sub_from_vis3:
+	ld	[$bp+4],$t0
+	ld	[$bp+0],$t1
+	ld	[$bp+12],$t2
+	ld	[$bp+8],$t3
+
+	srlx	$acc0,32,$acc4
+	not	$poly1,$poly1
+	srlx	$acc1,32,$acc5
+	subcc	$acc0,$t0,$acc0
+	ld	[$bp+20],$t0
+	subccc	$acc4,$t1,$acc4
+	ld	[$bp+16],$t1
+	subccc	$acc1,$t2,$acc1
+	ld	[$bp+28],$t2
+	and	$acc0,$poly1,$acc0
+	subccc	$acc5,$t3,$acc5
+	ld	[$bp+24],$t3
+	sllx	$acc4,32,$acc4
+	and	$acc1,$poly1,$acc1
+	sllx	$acc5,32,$acc5
+	or	$acc0,$acc4,$acc0
+	srlx	$acc2,32,$acc4
+	or	$acc1,$acc5,$acc1
+	srlx	$acc3,32,$acc5
+	subccc	$acc2,$t0,$acc2
+	subccc	$acc4,$t1,$acc4
+	subccc	$acc3,$t2,$acc3
+	and	$acc2,$poly1,$acc2
+	subccc	$acc5,$t3,$acc5
+	sllx	$acc4,32,$acc4
+	and	$acc3,$poly1,$acc3
+	sllx	$acc5,32,$acc5
+	or	$acc2,$acc4,$acc2
+	subc	%g0,%g0,$acc4		! did it borrow?
+	b	.Lreduce_by_add_vis3
+	or	$acc3,$acc5,$acc3
+.type	__ecp_nistz256_sub_from_vis3,#function
+.size	__ecp_nistz256_sub_from_vis3,.-__ecp_nistz256_sub_from_vis3
+
+.align	32
+__ecp_nistz256_sub_morf_vis3:
+	ld	[$bp+4],$t0
+	ld	[$bp+0],$t1
+	ld	[$bp+12],$t2
+	ld	[$bp+8],$t3
+
+	srlx	$acc0,32,$acc4
+	not	$poly1,$poly1
+	srlx	$acc1,32,$acc5
+	subcc	$t0,$acc0,$acc0
+	ld	[$bp+20],$t0
+	subccc	$t1,$acc4,$acc4
+	ld	[$bp+16],$t1
+	subccc	$t2,$acc1,$acc1
+	ld	[$bp+28],$t2
+	and	$acc0,$poly1,$acc0
+	subccc	$t3,$acc5,$acc5
+	ld	[$bp+24],$t3
+	sllx	$acc4,32,$acc4
+	and	$acc1,$poly1,$acc1
+	sllx	$acc5,32,$acc5
+	or	$acc0,$acc4,$acc0
+	srlx	$acc2,32,$acc4
+	or	$acc1,$acc5,$acc1
+	srlx	$acc3,32,$acc5
+	subccc	$t0,$acc2,$acc2
+	subccc	$t1,$acc4,$acc4
+	subccc	$t2,$acc3,$acc3
+	and	$acc2,$poly1,$acc2
+	subccc	$t3,$acc5,$acc5
+	sllx	$acc4,32,$acc4
+	and	$acc3,$poly1,$acc3
+	sllx	$acc5,32,$acc5
+	or	$acc2,$acc4,$acc2
+	subc	%g0,%g0,$acc4		! did it borrow?
+	or	$acc3,$acc5,$acc3
+
+.Lreduce_by_add_vis3:
+
+	addcc	$acc0,-1,$t0		! add modulus
+	not	$poly3,$t3
+	addxccc	$acc1,$poly1,$t1
+	not	$poly1,$poly1		! restore $poly1
+	addxccc	$acc2,%g0,$t2
+	addxc	$acc3,$t3,$t3
+
+	movrnz	$acc4,$t0,$acc0		! if a-b borrowed, ret = ret+mod
+	movrnz	$acc4,$t1,$acc1
+	stx	$acc0,[$rp]
+	movrnz	$acc4,$t2,$acc2
+	stx	$acc1,[$rp+8]
+	movrnz	$acc4,$t3,$acc3
+	stx	$acc2,[$rp+16]
+	retl
+	stx	$acc3,[$rp+24]
+.type	__ecp_nistz256_sub_morf_vis3,#function
+.size	__ecp_nistz256_sub_morf_vis3,.-__ecp_nistz256_sub_morf_vis3
+
+.align	32
+__ecp_nistz256_div_by_2_vis3:
+	! ret = (a is odd ? a+mod : a) >> 1
+
+	not	$poly1,$t1
+	not	$poly3,$t3
+	and	$acc0,1,$acc5
+	addcc	$acc0,-1,$t0		! add modulus
+	addxccc	$acc1,$t1,$t1
+	addxccc	$acc2,%g0,$t2
+	addxccc	$acc3,$t3,$t3
+	addxc	%g0,%g0,$acc4		! carry bit
+
+	movrnz	$acc5,$t0,$acc0
+	movrnz	$acc5,$t1,$acc1
+	movrnz	$acc5,$t2,$acc2
+	movrnz	$acc5,$t3,$acc3
+	movrz	$acc5,%g0,$acc4
+
+	! ret >>= 1
+
+	srlx	$acc0,1,$acc0
+	sllx	$acc1,63,$t0
+	srlx	$acc1,1,$acc1
+	or	$acc0,$t0,$acc0
+	sllx	$acc2,63,$t1
+	srlx	$acc2,1,$acc2
+	or	$acc1,$t1,$acc1
+	sllx	$acc3,63,$t2
+	stx	$acc0,[$rp]
+	srlx	$acc3,1,$acc3
+	or	$acc2,$t2,$acc2
+	sllx	$acc4,63,$t3		! don't forget carry bit
+	stx	$acc1,[$rp+8]
+	or	$acc3,$t3,$acc3
+	stx	$acc2,[$rp+16]
+	retl
+	stx	$acc3,[$rp+24]
+.type	__ecp_nistz256_div_by_2_vis3,#function
+.size	__ecp_nistz256_div_by_2_vis3,.-__ecp_nistz256_div_by_2_vis3
+
+! compared to __ecp_nistz256_mul_mont it's almost 4x smaller and
+! 4x faster [on T4]...
+.align	32
+__ecp_nistz256_mul_mont_vis3:
+	mulx	$a0,$bi,$acc0
+	not	$poly3,$poly3		! 0xFFFFFFFF00000001
+	umulxhi	$a0,$bi,$t0
+	mulx	$a1,$bi,$acc1
+	umulxhi	$a1,$bi,$t1
+	mulx	$a2,$bi,$acc2
+	umulxhi	$a2,$bi,$t2
+	mulx	$a3,$bi,$acc3
+	umulxhi	$a3,$bi,$t3
+	ldx	[$bp+8],$bi		! b[1]
+
+	addcc	$acc1,$t0,$acc1		! accumulate high parts of multiplication
+	 sllx	$acc0,32,$t0
+	addxccc	$acc2,$t1,$acc2
+	 srlx	$acc0,32,$t1
+	addxccc	$acc3,$t2,$acc3
+	addxc	%g0,$t3,$acc4
+	mov	0,$acc5
+___
+for($i=1;$i<4;$i++) {
+	# Reduction iteration is normally performed by accumulating
+	# result of multiplication of modulus by "magic" digit [and
+	# omitting least significant word, which is guaranteed to
+	# be 0], but thanks to special form of modulus and "magic"
+	# digit being equal to least significant word, it can be
+	# performed with additions and subtractions alone. Indeed:
+	#
+	#            ffff0001.00000000.0000ffff.ffffffff
+	# *                                     abcdefgh
+	# + xxxxxxxx.xxxxxxxx.xxxxxxxx.xxxxxxxx.abcdefgh
+	#
+	# Now observing that ff..ff*x = (2^n-1)*x = 2^n*x-x, we
+	# rewrite above as:
+	#
+	#   xxxxxxxx.xxxxxxxx.xxxxxxxx.xxxxxxxx.abcdefgh
+	# + abcdefgh.abcdefgh.0000abcd.efgh0000.00000000
+	# - 0000abcd.efgh0000.00000000.00000000.abcdefgh
+	#
+	# or marking redundant operations:
+	#
+	#   xxxxxxxx.xxxxxxxx.xxxxxxxx.xxxxxxxx.--------
+	# + abcdefgh.abcdefgh.0000abcd.efgh0000.--------
+	# - 0000abcd.efgh0000.--------.--------.--------
+	#   ^^^^^^^^ but this word is calculated with umulxhi, because
+	#            there is no subtract with 64-bit borrow:-(
+
+$code.=<<___;
+	sub	$acc0,$t0,$t2		! acc0*0xFFFFFFFF00000001, low part
+	umulxhi	$acc0,$poly3,$t3	! acc0*0xFFFFFFFF00000001, high part
+	addcc	$acc1,$t0,$acc0		! +=acc[0]<<96 and omit acc[0]
+	mulx	$a0,$bi,$t0
+	addxccc	$acc2,$t1,$acc1
+	mulx	$a1,$bi,$t1
+	addxccc	$acc3,$t2,$acc2		! +=acc[0]*0xFFFFFFFF00000001
+	mulx	$a2,$bi,$t2
+	addxccc	$acc4,$t3,$acc3
+	mulx	$a3,$bi,$t3
+	addxc	$acc5,%g0,$acc4
+
+	addcc	$acc0,$t0,$acc0		! accumulate low parts of multiplication
+	umulxhi	$a0,$bi,$t0
+	addxccc	$acc1,$t1,$acc1
+	umulxhi	$a1,$bi,$t1
+	addxccc	$acc2,$t2,$acc2
+	umulxhi	$a2,$bi,$t2
+	addxccc	$acc3,$t3,$acc3
+	umulxhi	$a3,$bi,$t3
+	addxc	$acc4,%g0,$acc4
+___
+$code.=<<___	if ($i<3);
+	ldx	[$bp+8*($i+1)],$bi	! bp[$i+1]
+___
+$code.=<<___;
+	addcc	$acc1,$t0,$acc1		! accumulate high parts of multiplication 
+	 sllx	$acc0,32,$t0
+	addxccc	$acc2,$t1,$acc2
+	 srlx	$acc0,32,$t1
+	addxccc	$acc3,$t2,$acc3
+	addxccc	$acc4,$t3,$acc4
+	addxc	%g0,%g0,$acc5
+___
+}
+$code.=<<___;
+	sub	$acc0,$t0,$t2		! acc0*0xFFFFFFFF00000001, low part
+	umulxhi	$acc0,$poly3,$t3	! acc0*0xFFFFFFFF00000001, high part
+	addcc	$acc1,$t0,$acc0		! +=acc[0]<<96 and omit acc[0]
+	addxccc	$acc2,$t1,$acc1
+	addxccc	$acc3,$t2,$acc2		! +=acc[0]*0xFFFFFFFF00000001
+	addxccc	$acc4,$t3,$acc3
+	b	.Lmul_final_vis3	! see below
+	addxc	$acc5,%g0,$acc4
+.type	__ecp_nistz256_mul_mont_vis3,#function
+.size	__ecp_nistz256_mul_mont_vis3,.-__ecp_nistz256_mul_mont_vis3
+
+! compared to above __ecp_nistz256_mul_mont_vis3 it's 21% less
+! instructions, but only 14% faster [on T4]...
+.align	32
+__ecp_nistz256_sqr_mont_vis3:
+	!  |  |  |  |  |  |a1*a0|  |
+	!  |  |  |  |  |a2*a0|  |  |
+	!  |  |a3*a2|a3*a0|  |  |  |
+	!  |  |  |  |a2*a1|  |  |  |
+	!  |  |  |a3*a1|  |  |  |  |
+	! *|  |  |  |  |  |  |  | 2|
+	! +|a3*a3|a2*a2|a1*a1|a0*a0|
+	!  |--+--+--+--+--+--+--+--|
+	!  |A7|A6|A5|A4|A3|A2|A1|A0|, where Ax is $accx, i.e. follow $accx
+	!
+	!  "can't overflow" below mark carrying into high part of
+	!  multiplication result, which can't overflow, because it
+	!  can never be all ones.
+
+	mulx	$a1,$a0,$acc1		! a[1]*a[0]
+	umulxhi	$a1,$a0,$t1
+	mulx	$a2,$a0,$acc2		! a[2]*a[0]
+	umulxhi	$a2,$a0,$t2
+	mulx	$a3,$a0,$acc3		! a[3]*a[0]
+	umulxhi	$a3,$a0,$acc4
+
+	addcc	$acc2,$t1,$acc2		! accumulate high parts of multiplication
+	mulx	$a2,$a1,$t0		! a[2]*a[1]
+	umulxhi	$a2,$a1,$t1
+	addxccc	$acc3,$t2,$acc3
+	mulx	$a3,$a1,$t2		! a[3]*a[1]
+	umulxhi	$a3,$a1,$t3
+	addxc	$acc4,%g0,$acc4		! can't overflow
+
+	mulx	$a3,$a2,$acc5		! a[3]*a[2]
+	not	$poly3,$poly3		! 0xFFFFFFFF00000001
+	umulxhi	$a3,$a2,$acc6
+
+	addcc	$t2,$t1,$t1		! accumulate high parts of multiplication
+	mulx	$a0,$a0,$acc0		! a[0]*a[0]
+	addxc	$t3,%g0,$t2		! can't overflow
+
+	addcc	$acc3,$t0,$acc3		! accumulate low parts of multiplication
+	umulxhi	$a0,$a0,$a0
+	addxccc	$acc4,$t1,$acc4
+	mulx	$a1,$a1,$t1		! a[1]*a[1]
+	addxccc	$acc5,$t2,$acc5
+	umulxhi	$a1,$a1,$a1
+	addxc	$acc6,%g0,$acc6		! can't overflow
+
+	addcc	$acc1,$acc1,$acc1	! acc[1-6]*=2
+	mulx	$a2,$a2,$t2		! a[2]*a[2]
+	addxccc	$acc2,$acc2,$acc2
+	umulxhi	$a2,$a2,$a2
+	addxccc	$acc3,$acc3,$acc3
+	mulx	$a3,$a3,$t3		! a[3]*a[3]
+	addxccc	$acc4,$acc4,$acc4
+	umulxhi	$a3,$a3,$a3
+	addxccc	$acc5,$acc5,$acc5
+	addxccc	$acc6,$acc6,$acc6
+	addxc	%g0,%g0,$acc7
+
+	addcc	$acc1,$a0,$acc1		! +a[i]*a[i]
+	addxccc	$acc2,$t1,$acc2
+	addxccc	$acc3,$a1,$acc3
+	addxccc	$acc4,$t2,$acc4
+	 sllx	$acc0,32,$t0
+	addxccc	$acc5,$a2,$acc5
+	 srlx	$acc0,32,$t1
+	addxccc	$acc6,$t3,$acc6
+	 sub	$acc0,$t0,$t2		! acc0*0xFFFFFFFF00000001, low part
+	addxc	$acc7,$a3,$acc7
+___
+for($i=0;$i<3;$i++) {			# reductions, see commentary
+					# in multiplication for details
+$code.=<<___;
+	umulxhi	$acc0,$poly3,$t3	! acc0*0xFFFFFFFF00000001, high part
+	addcc	$acc1,$t0,$acc0		! +=acc[0]<<96 and omit acc[0]
+	 sllx	$acc0,32,$t0
+	addxccc	$acc2,$t1,$acc1
+	 srlx	$acc0,32,$t1
+	addxccc	$acc3,$t2,$acc2		! +=acc[0]*0xFFFFFFFF00000001
+	 sub	$acc0,$t0,$t2		! acc0*0xFFFFFFFF00000001, low part
+	addxc	%g0,$t3,$acc3		! cant't overflow
+___
+}
+$code.=<<___;
+	umulxhi	$acc0,$poly3,$t3	! acc0*0xFFFFFFFF00000001, high part
+	addcc	$acc1,$t0,$acc0		! +=acc[0]<<96 and omit acc[0]
+	addxccc	$acc2,$t1,$acc1
+	addxccc	$acc3,$t2,$acc2		! +=acc[0]*0xFFFFFFFF00000001
+	addxc	%g0,$t3,$acc3		! can't overflow
+
+	addcc	$acc0,$acc4,$acc0	! accumulate upper half
+	addxccc	$acc1,$acc5,$acc1
+	addxccc	$acc2,$acc6,$acc2
+	addxccc	$acc3,$acc7,$acc3
+	addxc	%g0,%g0,$acc4
+
+.Lmul_final_vis3:
+
+	! Final step is "if result > mod, subtract mod", but as comparison
+	! means subtraction, we do the subtraction and then copy outcome
+	! if it didn't borrow. But note that as we [have to] replace
+	! subtraction with addition with negative, carry/borrow logic is
+	! inverse.
+
+	addcc	$acc0,1,$t0		! add -modulus, i.e. subtract
+	not	$poly3,$poly3		! restore 0x00000000FFFFFFFE
+	addxccc	$acc1,$poly1,$t1
+	addxccc	$acc2,$minus1,$t2
+	addxccc	$acc3,$poly3,$t3
+	addxccc	$acc4,$minus1,%g0	! did it carry?
+
+	movcs	%xcc,$t0,$acc0
+	movcs	%xcc,$t1,$acc1
+	stx	$acc0,[$rp]
+	movcs	%xcc,$t2,$acc2
+	stx	$acc1,[$rp+8]
+	movcs	%xcc,$t3,$acc3
+	stx	$acc2,[$rp+16]
+	retl
+	stx	$acc3,[$rp+24]
+.type	__ecp_nistz256_sqr_mont_vis3,#function
+.size	__ecp_nistz256_sqr_mont_vis3,.-__ecp_nistz256_sqr_mont_vis3
+___
+
+########################################################################
+# void ecp_nistz256_point_double(P256_POINT *out,const P256_POINT *inp);
+#
+{
+my ($res_x,$res_y,$res_z,
+    $in_x,$in_y,$in_z,
+    $S,$M,$Zsqr,$tmp0)=map(32*$_,(0..9));
+# above map() describes stack layout with 10 temporary
+# 256-bit vectors on top.
+
+$code.=<<___;
+.align	32
+ecp_nistz256_point_double_vis3:
+	save	%sp,-STACK64_FRAME-32*10,%sp
+
+	mov	$rp,$rp_real
+.Ldouble_shortcut_vis3:
+	mov	-1,$minus1
+	mov	-2,$poly3
+	sllx	$minus1,32,$poly1		! 0xFFFFFFFF00000000
+	srl	$poly3,0,$poly3			! 0x00000000FFFFFFFE
+
+	! convert input to uint64_t[4]
+	ld	[$ap],$a0			! in_x
+	ld	[$ap+4],$t0
+	ld	[$ap+8],$a1
+	ld	[$ap+12],$t1
+	ld	[$ap+16],$a2
+	ld	[$ap+20],$t2
+	ld	[$ap+24],$a3
+	ld	[$ap+28],$t3
+	sllx	$t0,32,$t0
+	sllx	$t1,32,$t1
+	ld	[$ap+32],$acc0			! in_y
+	or	$a0,$t0,$a0
+	ld	[$ap+32+4],$t0
+	sllx	$t2,32,$t2
+	ld	[$ap+32+8],$acc1
+	or	$a1,$t1,$a1
+	ld	[$ap+32+12],$t1
+	sllx	$t3,32,$t3
+	ld	[$ap+32+16],$acc2
+	or	$a2,$t2,$a2
+	ld	[$ap+32+20],$t2
+	or	$a3,$t3,$a3
+	ld	[$ap+32+24],$acc3
+	sllx	$t0,32,$t0
+	ld	[$ap+32+28],$t3
+	sllx	$t1,32,$t1
+	stx	$a0,[%sp+LOCALS64+$in_x]
+	sllx	$t2,32,$t2
+	stx	$a1,[%sp+LOCALS64+$in_x+8]
+	sllx	$t3,32,$t3
+	stx	$a2,[%sp+LOCALS64+$in_x+16]
+	or	$acc0,$t0,$acc0
+	stx	$a3,[%sp+LOCALS64+$in_x+24]
+	or	$acc1,$t1,$acc1
+	stx	$acc0,[%sp+LOCALS64+$in_y]
+	or	$acc2,$t2,$acc2
+	stx	$acc1,[%sp+LOCALS64+$in_y+8]
+	or	$acc3,$t3,$acc3
+	stx	$acc2,[%sp+LOCALS64+$in_y+16]
+	stx	$acc3,[%sp+LOCALS64+$in_y+24]
+
+	ld	[$ap+64],$a0			! in_z
+	ld	[$ap+64+4],$t0
+	ld	[$ap+64+8],$a1
+	ld	[$ap+64+12],$t1
+	ld	[$ap+64+16],$a2
+	ld	[$ap+64+20],$t2
+	ld	[$ap+64+24],$a3
+	ld	[$ap+64+28],$t3
+	sllx	$t0,32,$t0
+	sllx	$t1,32,$t1
+	or	$a0,$t0,$a0
+	sllx	$t2,32,$t2
+	or	$a1,$t1,$a1
+	sllx	$t3,32,$t3
+	or	$a2,$t2,$a2
+	or	$a3,$t3,$a3
+	sllx	$t0,32,$t0
+	sllx	$t1,32,$t1
+	stx	$a0,[%sp+LOCALS64+$in_z]
+	sllx	$t2,32,$t2
+	stx	$a1,[%sp+LOCALS64+$in_z+8]
+	sllx	$t3,32,$t3
+	stx	$a2,[%sp+LOCALS64+$in_z+16]
+	stx	$a3,[%sp+LOCALS64+$in_z+24]
+
+	! in_y is still in $acc0-$acc3
+	call	__ecp_nistz256_mul_by_2_vis3	! p256_mul_by_2(S, in_y);
+	add	%sp,LOCALS64+$S,$rp
+
+	! in_z is still in $a0-$a3
+	call	__ecp_nistz256_sqr_mont_vis3	! p256_sqr_mont(Zsqr, in_z);
+	add	%sp,LOCALS64+$Zsqr,$rp
+
+	mov	$acc0,$a0			! put Zsqr aside
+	mov	$acc1,$a1
+	mov	$acc2,$a2
+	mov	$acc3,$a3
+
+	add	%sp,LOCALS64+$in_x,$bp
+	call	__ecp_nistz256_add_vis3		! p256_add(M, Zsqr, in_x);
+	add	%sp,LOCALS64+$M,$rp
+
+	mov	$a0,$acc0			! restore Zsqr
+	ldx	[%sp+LOCALS64+$S],$a0		! forward load
+	mov	$a1,$acc1
+	ldx	[%sp+LOCALS64+$S+8],$a1
+	mov	$a2,$acc2
+	ldx	[%sp+LOCALS64+$S+16],$a2
+	mov	$a3,$acc3
+	ldx	[%sp+LOCALS64+$S+24],$a3
+
+	add	%sp,LOCALS64+$in_x,$bp
+	call	__ecp_nistz256_sub_morf_vis3	! p256_sub(Zsqr, in_x, Zsqr);
+	add	%sp,LOCALS64+$Zsqr,$rp
+
+	call	__ecp_nistz256_sqr_mont_vis3	! p256_sqr_mont(S, S);
+	add	%sp,LOCALS64+$S,$rp
+
+	ldx	[%sp+LOCALS64+$in_z],$bi
+	ldx	[%sp+LOCALS64+$in_y],$a0
+	ldx	[%sp+LOCALS64+$in_y+8],$a1
+	ldx	[%sp+LOCALS64+$in_y+16],$a2
+	ldx	[%sp+LOCALS64+$in_y+24],$a3
+	add	%sp,LOCALS64+$in_z,$bp
+	call	__ecp_nistz256_mul_mont_vis3	! p256_mul_mont(tmp0, in_z, in_y);
+	add	%sp,LOCALS64+$tmp0,$rp
+
+	ldx	[%sp+LOCALS64+$M],$bi		! forward load
+	ldx	[%sp+LOCALS64+$Zsqr],$a0
+	ldx	[%sp+LOCALS64+$Zsqr+8],$a1
+	ldx	[%sp+LOCALS64+$Zsqr+16],$a2
+	ldx	[%sp+LOCALS64+$Zsqr+24],$a3
+
+	call	__ecp_nistz256_mul_by_2_vis3	! p256_mul_by_2(res_z, tmp0);
+	add	%sp,LOCALS64+$res_z,$rp
+
+	add	%sp,LOCALS64+$M,$bp
+	call	__ecp_nistz256_mul_mont_vis3	! p256_mul_mont(M, M, Zsqr);
+	add	%sp,LOCALS64+$M,$rp
+
+	mov	$acc0,$a0			! put aside M
+	mov	$acc1,$a1
+	mov	$acc2,$a2
+	mov	$acc3,$a3
+	call	__ecp_nistz256_mul_by_2_vis3
+	add	%sp,LOCALS64+$M,$rp
+	mov	$a0,$t0				! copy M
+	ldx	[%sp+LOCALS64+$S],$a0		! forward load
+	mov	$a1,$t1
+	ldx	[%sp+LOCALS64+$S+8],$a1
+	mov	$a2,$t2
+	ldx	[%sp+LOCALS64+$S+16],$a2
+	mov	$a3,$t3
+	ldx	[%sp+LOCALS64+$S+24],$a3
+	call	__ecp_nistz256_add_noload_vis3	! p256_mul_by_3(M, M);
+	add	%sp,LOCALS64+$M,$rp
+
+	call	__ecp_nistz256_sqr_mont_vis3	! p256_sqr_mont(tmp0, S);
+	add	%sp,LOCALS64+$tmp0,$rp
+
+	ldx	[%sp+LOCALS64+$S],$bi		! forward load
+	ldx	[%sp+LOCALS64+$in_x],$a0
+	ldx	[%sp+LOCALS64+$in_x+8],$a1
+	ldx	[%sp+LOCALS64+$in_x+16],$a2
+	ldx	[%sp+LOCALS64+$in_x+24],$a3
+
+	call	__ecp_nistz256_div_by_2_vis3	! p256_div_by_2(res_y, tmp0);
+	add	%sp,LOCALS64+$res_y,$rp
+
+	add	%sp,LOCALS64+$S,$bp
+	call	__ecp_nistz256_mul_mont_vis3	! p256_mul_mont(S, S, in_x);
+	add	%sp,LOCALS64+$S,$rp
+
+	ldx	[%sp+LOCALS64+$M],$a0		! forward load
+	ldx	[%sp+LOCALS64+$M+8],$a1
+	ldx	[%sp+LOCALS64+$M+16],$a2
+	ldx	[%sp+LOCALS64+$M+24],$a3
+
+	call	__ecp_nistz256_mul_by_2_vis3	! p256_mul_by_2(tmp0, S);
+	add	%sp,LOCALS64+$tmp0,$rp
+
+	call	__ecp_nistz256_sqr_mont_vis3	! p256_sqr_mont(res_x, M);
+	add	%sp,LOCALS64+$res_x,$rp
+
+	add	%sp,LOCALS64+$tmp0,$bp
+	call	__ecp_nistz256_sub_from_vis3	! p256_sub(res_x, res_x, tmp0);
+	add	%sp,LOCALS64+$res_x,$rp
+
+	ldx	[%sp+LOCALS64+$M],$a0		! forward load
+	ldx	[%sp+LOCALS64+$M+8],$a1
+	ldx	[%sp+LOCALS64+$M+16],$a2
+	ldx	[%sp+LOCALS64+$M+24],$a3
+
+	add	%sp,LOCALS64+$S,$bp
+	call	__ecp_nistz256_sub_morf_vis3	! p256_sub(S, S, res_x);
+	add	%sp,LOCALS64+$S,$rp
+
+	mov	$acc0,$bi
+	call	__ecp_nistz256_mul_mont_vis3	! p256_mul_mont(S, S, M);
+	add	%sp,LOCALS64+$S,$rp
+
+	ldx	[%sp+LOCALS64+$res_x],$a0	! forward load
+	ldx	[%sp+LOCALS64+$res_x+8],$a1
+	ldx	[%sp+LOCALS64+$res_x+16],$a2
+	ldx	[%sp+LOCALS64+$res_x+24],$a3
+
+	add	%sp,LOCALS64+$res_y,$bp
+	call	__ecp_nistz256_sub_from_vis3	! p256_sub(res_y, S, res_y);
+	add	%sp,LOCALS64+$res_y,$bp
+
+	! convert output to uint_32[8]
+	srlx	$a0,32,$t0
+	srlx	$a1,32,$t1
+	st	$a0,[$rp_real]			! res_x
+	srlx	$a2,32,$t2
+	st	$t0,[$rp_real+4]
+	srlx	$a3,32,$t3
+	st	$a1,[$rp_real+8]
+	st	$t1,[$rp_real+12]
+	st	$a2,[$rp_real+16]
+	st	$t2,[$rp_real+20]
+	st	$a3,[$rp_real+24]
+	st	$t3,[$rp_real+28]
+
+	ldx	[%sp+LOCALS64+$res_z],$a0	! forward load
+	srlx	$acc0,32,$t0
+	ldx	[%sp+LOCALS64+$res_z+8],$a1
+	srlx	$acc1,32,$t1
+	ldx	[%sp+LOCALS64+$res_z+16],$a2
+	srlx	$acc2,32,$t2
+	ldx	[%sp+LOCALS64+$res_z+24],$a3
+	srlx	$acc3,32,$t3
+	st	$acc0,[$rp_real+32]		! res_y
+	st	$t0,  [$rp_real+32+4]
+	st	$acc1,[$rp_real+32+8]
+	st	$t1,  [$rp_real+32+12]
+	st	$acc2,[$rp_real+32+16]
+	st	$t2,  [$rp_real+32+20]
+	st	$acc3,[$rp_real+32+24]
+	st	$t3,  [$rp_real+32+28]
+
+	srlx	$a0,32,$t0
+	srlx	$a1,32,$t1
+	st	$a0,[$rp_real+64]		! res_z
+	srlx	$a2,32,$t2
+	st	$t0,[$rp_real+64+4]
+	srlx	$a3,32,$t3
+	st	$a1,[$rp_real+64+8]
+	st	$t1,[$rp_real+64+12]
+	st	$a2,[$rp_real+64+16]
+	st	$t2,[$rp_real+64+20]
+	st	$a3,[$rp_real+64+24]
+	st	$t3,[$rp_real+64+28]
+
+	ret
+	restore
+.type	ecp_nistz256_point_double_vis3,#function
+.size	ecp_nistz256_point_double_vis3,.-ecp_nistz256_point_double_vis3
+___
+}
+########################################################################
+# void ecp_nistz256_point_add(P256_POINT *out,const P256_POINT *in1,
+#			      const P256_POINT *in2);
+{
+my ($res_x,$res_y,$res_z,
+    $in1_x,$in1_y,$in1_z,
+    $in2_x,$in2_y,$in2_z,
+    $H,$Hsqr,$R,$Rsqr,$Hcub,
+    $U1,$U2,$S1,$S2)=map(32*$_,(0..17));
+my ($Z1sqr, $Z2sqr) = ($Hsqr, $Rsqr);
+
+# above map() describes stack layout with 18 temporary
+# 256-bit vectors on top. Then we reserve some space for
+# !in1infty, !in2infty and result of check for zero.
+
+$code.=<<___;
+.globl	ecp_nistz256_point_add_vis3
+.align	32
+ecp_nistz256_point_add_vis3:
+	save	%sp,-STACK64_FRAME-32*18-32,%sp
+
+	mov	$rp,$rp_real
+	mov	-1,$minus1
+	mov	-2,$poly3
+	sllx	$minus1,32,$poly1		! 0xFFFFFFFF00000000
+	srl	$poly3,0,$poly3			! 0x00000000FFFFFFFE
+
+	! convert input to uint64_t[4]
+	ld	[$bp],$a0			! in2_x
+	ld	[$bp+4],$t0
+	ld	[$bp+8],$a1
+	ld	[$bp+12],$t1
+	ld	[$bp+16],$a2
+	ld	[$bp+20],$t2
+	ld	[$bp+24],$a3
+	ld	[$bp+28],$t3
+	sllx	$t0,32,$t0
+	sllx	$t1,32,$t1
+	ld	[$bp+32],$acc0			! in2_y
+	or	$a0,$t0,$a0
+	ld	[$bp+32+4],$t0
+	sllx	$t2,32,$t2
+	ld	[$bp+32+8],$acc1
+	or	$a1,$t1,$a1
+	ld	[$bp+32+12],$t1
+	sllx	$t3,32,$t3
+	ld	[$bp+32+16],$acc2
+	or	$a2,$t2,$a2
+	ld	[$bp+32+20],$t2
+	or	$a3,$t3,$a3
+	ld	[$bp+32+24],$acc3
+	sllx	$t0,32,$t0
+	ld	[$bp+32+28],$t3
+	sllx	$t1,32,$t1
+	stx	$a0,[%sp+LOCALS64+$in2_x]
+	sllx	$t2,32,$t2
+	stx	$a1,[%sp+LOCALS64+$in2_x+8]
+	sllx	$t3,32,$t3
+	stx	$a2,[%sp+LOCALS64+$in2_x+16]
+	or	$acc0,$t0,$acc0
+	stx	$a3,[%sp+LOCALS64+$in2_x+24]
+	or	$acc1,$t1,$acc1
+	stx	$acc0,[%sp+LOCALS64+$in2_y]
+	or	$acc2,$t2,$acc2
+	stx	$acc1,[%sp+LOCALS64+$in2_y+8]
+	or	$acc3,$t3,$acc3
+	stx	$acc2,[%sp+LOCALS64+$in2_y+16]
+	stx	$acc3,[%sp+LOCALS64+$in2_y+24]
+
+	ld	[$bp+64],$acc0			! in2_z
+	ld	[$bp+64+4],$t0
+	ld	[$bp+64+8],$acc1
+	ld	[$bp+64+12],$t1
+	ld	[$bp+64+16],$acc2
+	ld	[$bp+64+20],$t2
+	ld	[$bp+64+24],$acc3
+	ld	[$bp+64+28],$t3
+	sllx	$t0,32,$t0
+	sllx	$t1,32,$t1
+	ld	[$ap],$a0			! in1_x
+	or	$acc0,$t0,$acc0
+	ld	[$ap+4],$t0
+	sllx	$t2,32,$t2
+	ld	[$ap+8],$a1
+	or	$acc1,$t1,$acc1
+	ld	[$ap+12],$t1
+	sllx	$t3,32,$t3
+	ld	[$ap+16],$a2
+	or	$acc2,$t2,$acc2
+	ld	[$ap+20],$t2
+	or	$acc3,$t3,$acc3
+	ld	[$ap+24],$a3
+	sllx	$t0,32,$t0
+	ld	[$ap+28],$t3
+	sllx	$t1,32,$t1
+	stx	$acc0,[%sp+LOCALS64+$in2_z]
+	sllx	$t2,32,$t2
+	stx	$acc1,[%sp+LOCALS64+$in2_z+8]
+	sllx	$t3,32,$t3
+	stx	$acc2,[%sp+LOCALS64+$in2_z+16]
+	stx	$acc3,[%sp+LOCALS64+$in2_z+24]
+
+	or	$acc1,$acc0,$acc0
+	or	$acc3,$acc2,$acc2
+	or	$acc2,$acc0,$acc0
+	movrnz	$acc0,-1,$acc0			! !in2infty
+	stx	$acc0,[%fp+STACK_BIAS-8]
+
+	or	$a0,$t0,$a0
+	ld	[$ap+32],$acc0			! in1_y
+	or	$a1,$t1,$a1
+	ld	[$ap+32+4],$t0
+	or	$a2,$t2,$a2
+	ld	[$ap+32+8],$acc1
+	or	$a3,$t3,$a3
+	ld	[$ap+32+12],$t1
+	ld	[$ap+32+16],$acc2
+	ld	[$ap+32+20],$t2
+	ld	[$ap+32+24],$acc3
+	sllx	$t0,32,$t0
+	ld	[$ap+32+28],$t3
+	sllx	$t1,32,$t1
+	stx	$a0,[%sp+LOCALS64+$in1_x]
+	sllx	$t2,32,$t2
+	stx	$a1,[%sp+LOCALS64+$in1_x+8]
+	sllx	$t3,32,$t3
+	stx	$a2,[%sp+LOCALS64+$in1_x+16]
+	or	$acc0,$t0,$acc0
+	stx	$a3,[%sp+LOCALS64+$in1_x+24]
+	or	$acc1,$t1,$acc1
+	stx	$acc0,[%sp+LOCALS64+$in1_y]
+	or	$acc2,$t2,$acc2
+	stx	$acc1,[%sp+LOCALS64+$in1_y+8]
+	or	$acc3,$t3,$acc3
+	stx	$acc2,[%sp+LOCALS64+$in1_y+16]
+	stx	$acc3,[%sp+LOCALS64+$in1_y+24]
+
+	ldx	[%sp+LOCALS64+$in2_z],$a0	! forward load
+	ldx	[%sp+LOCALS64+$in2_z+8],$a1
+	ldx	[%sp+LOCALS64+$in2_z+16],$a2
+	ldx	[%sp+LOCALS64+$in2_z+24],$a3
+
+	ld	[$ap+64],$acc0			! in1_z
+	ld	[$ap+64+4],$t0
+	ld	[$ap+64+8],$acc1
+	ld	[$ap+64+12],$t1
+	ld	[$ap+64+16],$acc2
+	ld	[$ap+64+20],$t2
+	ld	[$ap+64+24],$acc3
+	ld	[$ap+64+28],$t3
+	sllx	$t0,32,$t0
+	sllx	$t1,32,$t1
+	or	$acc0,$t0,$acc0
+	sllx	$t2,32,$t2
+	or	$acc1,$t1,$acc1
+	sllx	$t3,32,$t3
+	stx	$acc0,[%sp+LOCALS64+$in1_z]
+	or	$acc2,$t2,$acc2
+	stx	$acc1,[%sp+LOCALS64+$in1_z+8]
+	or	$acc3,$t3,$acc3
+	stx	$acc2,[%sp+LOCALS64+$in1_z+16]
+	stx	$acc3,[%sp+LOCALS64+$in1_z+24]
+
+	or	$acc1,$acc0,$acc0
+	or	$acc3,$acc2,$acc2
+	or	$acc2,$acc0,$acc0
+	movrnz	$acc0,-1,$acc0			! !in1infty
+	stx	$acc0,[%fp+STACK_BIAS-16]
+
+	call	__ecp_nistz256_sqr_mont_vis3	! p256_sqr_mont(Z2sqr, in2_z);
+	add	%sp,LOCALS64+$Z2sqr,$rp
+
+	ldx	[%sp+LOCALS64+$in1_z],$a0
+	ldx	[%sp+LOCALS64+$in1_z+8],$a1
+	ldx	[%sp+LOCALS64+$in1_z+16],$a2
+	ldx	[%sp+LOCALS64+$in1_z+24],$a3
+	call	__ecp_nistz256_sqr_mont_vis3	! p256_sqr_mont(Z1sqr, in1_z);
+	add	%sp,LOCALS64+$Z1sqr,$rp
+
+	ldx	[%sp+LOCALS64+$Z2sqr],$bi
+	ldx	[%sp+LOCALS64+$in2_z],$a0
+	ldx	[%sp+LOCALS64+$in2_z+8],$a1
+	ldx	[%sp+LOCALS64+$in2_z+16],$a2
+	ldx	[%sp+LOCALS64+$in2_z+24],$a3
+	add	%sp,LOCALS64+$Z2sqr,$bp
+	call	__ecp_nistz256_mul_mont_vis3	! p256_mul_mont(S1, Z2sqr, in2_z);
+	add	%sp,LOCALS64+$S1,$rp
+
+	ldx	[%sp+LOCALS64+$Z1sqr],$bi
+	ldx	[%sp+LOCALS64+$in1_z],$a0
+	ldx	[%sp+LOCALS64+$in1_z+8],$a1
+	ldx	[%sp+LOCALS64+$in1_z+16],$a2
+	ldx	[%sp+LOCALS64+$in1_z+24],$a3
+	add	%sp,LOCALS64+$Z1sqr,$bp
+	call	__ecp_nistz256_mul_mont_vis3	! p256_mul_mont(S2, Z1sqr, in1_z);
+	add	%sp,LOCALS64+$S2,$rp
+
+	ldx	[%sp+LOCALS64+$S1],$bi
+	ldx	[%sp+LOCALS64+$in1_y],$a0
+	ldx	[%sp+LOCALS64+$in1_y+8],$a1
+	ldx	[%sp+LOCALS64+$in1_y+16],$a2
+	ldx	[%sp+LOCALS64+$in1_y+24],$a3
+	add	%sp,LOCALS64+$S1,$bp
+	call	__ecp_nistz256_mul_mont_vis3	! p256_mul_mont(S1, S1, in1_y);
+	add	%sp,LOCALS64+$S1,$rp
+
+	ldx	[%sp+LOCALS64+$S2],$bi
+	ldx	[%sp+LOCALS64+$in2_y],$a0
+	ldx	[%sp+LOCALS64+$in2_y+8],$a1
+	ldx	[%sp+LOCALS64+$in2_y+16],$a2
+	ldx	[%sp+LOCALS64+$in2_y+24],$a3
+	add	%sp,LOCALS64+$S2,$bp
+	call	__ecp_nistz256_mul_mont_vis3	! p256_mul_mont(S2, S2, in2_y);
+	add	%sp,LOCALS64+$S2,$rp
+
+	ldx	[%sp+LOCALS64+$Z2sqr],$bi	! forward load
+	ldx	[%sp+LOCALS64+$in1_x],$a0
+	ldx	[%sp+LOCALS64+$in1_x+8],$a1
+	ldx	[%sp+LOCALS64+$in1_x+16],$a2
+	ldx	[%sp+LOCALS64+$in1_x+24],$a3
+
+	add	%sp,LOCALS64+$S1,$bp
+	call	__ecp_nistz256_sub_from_vis3	! p256_sub(R, S2, S1);
+	add	%sp,LOCALS64+$R,$rp
+
+	or	$acc1,$acc0,$acc0		! see if result is zero
+	or	$acc3,$acc2,$acc2
+	or	$acc2,$acc0,$acc0
+	stx	$acc0,[%fp+STACK_BIAS-24]
+
+	add	%sp,LOCALS64+$Z2sqr,$bp
+	call	__ecp_nistz256_mul_mont_vis3	! p256_mul_mont(U1, in1_x, Z2sqr);
+	add	%sp,LOCALS64+$U1,$rp
+
+	ldx	[%sp+LOCALS64+$Z1sqr],$bi
+	ldx	[%sp+LOCALS64+$in2_x],$a0
+	ldx	[%sp+LOCALS64+$in2_x+8],$a1
+	ldx	[%sp+LOCALS64+$in2_x+16],$a2
+	ldx	[%sp+LOCALS64+$in2_x+24],$a3
+	add	%sp,LOCALS64+$Z1sqr,$bp
+	call	__ecp_nistz256_mul_mont_vis3	! p256_mul_mont(U2, in2_x, Z1sqr);
+	add	%sp,LOCALS64+$U2,$rp
+
+	ldx	[%sp+LOCALS64+$R],$a0		! forward load
+	ldx	[%sp+LOCALS64+$R+8],$a1
+	ldx	[%sp+LOCALS64+$R+16],$a2
+	ldx	[%sp+LOCALS64+$R+24],$a3
+
+	add	%sp,LOCALS64+$U1,$bp
+	call	__ecp_nistz256_sub_from_vis3	! p256_sub(H, U2, U1);
+	add	%sp,LOCALS64+$H,$rp
+
+	or	$acc1,$acc0,$acc0		! see if result is zero
+	or	$acc3,$acc2,$acc2
+	orcc	$acc2,$acc0,$acc0
+
+	bne,pt	%xcc,.Ladd_proceed_vis3		! is_equal(U1,U2)?
+	nop
+
+	ldx	[%fp+STACK_BIAS-8],$t0
+	ldx	[%fp+STACK_BIAS-16],$t1
+	ldx	[%fp+STACK_BIAS-24],$t2
+	andcc	$t0,$t1,%g0
+	be,pt	%xcc,.Ladd_proceed_vis3		! (in1infty || in2infty)?
+	nop
+	andcc	$t2,$t2,%g0
+	be,a,pt	%xcc,.Ldouble_shortcut_vis3	! is_equal(S1,S2)?
+	add	%sp,32*(12-10)+32,%sp		! difference in frame sizes
+
+	st	%g0,[$rp_real]
+	st	%g0,[$rp_real+4]
+	st	%g0,[$rp_real+8]
+	st	%g0,[$rp_real+12]
+	st	%g0,[$rp_real+16]
+	st	%g0,[$rp_real+20]
+	st	%g0,[$rp_real+24]
+	st	%g0,[$rp_real+28]
+	st	%g0,[$rp_real+32]
+	st	%g0,[$rp_real+32+4]
+	st	%g0,[$rp_real+32+8]
+	st	%g0,[$rp_real+32+12]
+	st	%g0,[$rp_real+32+16]
+	st	%g0,[$rp_real+32+20]
+	st	%g0,[$rp_real+32+24]
+	st	%g0,[$rp_real+32+28]
+	st	%g0,[$rp_real+64]
+	st	%g0,[$rp_real+64+4]
+	st	%g0,[$rp_real+64+8]
+	st	%g0,[$rp_real+64+12]
+	st	%g0,[$rp_real+64+16]
+	st	%g0,[$rp_real+64+20]
+	st	%g0,[$rp_real+64+24]
+	st	%g0,[$rp_real+64+28]
+	b	.Ladd_done_vis3
+	nop
+
+.align	16
+.Ladd_proceed_vis3:
+	call	__ecp_nistz256_sqr_mont_vis3	! p256_sqr_mont(Rsqr, R);
+	add	%sp,LOCALS64+$Rsqr,$rp
+
+	ldx	[%sp+LOCALS64+$H],$bi
+	ldx	[%sp+LOCALS64+$in1_z],$a0
+	ldx	[%sp+LOCALS64+$in1_z+8],$a1
+	ldx	[%sp+LOCALS64+$in1_z+16],$a2
+	ldx	[%sp+LOCALS64+$in1_z+24],$a3
+	add	%sp,LOCALS64+$H,$bp
+	call	__ecp_nistz256_mul_mont_vis3	! p256_mul_mont(res_z, H, in1_z);
+	add	%sp,LOCALS64+$res_z,$rp
+
+	ldx	[%sp+LOCALS64+$H],$a0
+	ldx	[%sp+LOCALS64+$H+8],$a1
+	ldx	[%sp+LOCALS64+$H+16],$a2
+	ldx	[%sp+LOCALS64+$H+24],$a3
+	call	__ecp_nistz256_sqr_mont_vis3	! p256_sqr_mont(Hsqr, H);
+	add	%sp,LOCALS64+$Hsqr,$rp
+
+	ldx	[%sp+LOCALS64+$res_z],$bi
+	ldx	[%sp+LOCALS64+$in2_z],$a0
+	ldx	[%sp+LOCALS64+$in2_z+8],$a1
+	ldx	[%sp+LOCALS64+$in2_z+16],$a2
+	ldx	[%sp+LOCALS64+$in2_z+24],$a3
+	add	%sp,LOCALS64+$res_z,$bp
+	call	__ecp_nistz256_mul_mont_vis3	! p256_mul_mont(res_z, res_z, in2_z);
+	add	%sp,LOCALS64+$res_z,$rp
+
+	ldx	[%sp+LOCALS64+$H],$bi
+	ldx	[%sp+LOCALS64+$Hsqr],$a0
+	ldx	[%sp+LOCALS64+$Hsqr+8],$a1
+	ldx	[%sp+LOCALS64+$Hsqr+16],$a2
+	ldx	[%sp+LOCALS64+$Hsqr+24],$a3
+	add	%sp,LOCALS64+$H,$bp
+	call	__ecp_nistz256_mul_mont_vis3	! p256_mul_mont(Hcub, Hsqr, H);
+	add	%sp,LOCALS64+$Hcub,$rp
+
+	ldx	[%sp+LOCALS64+$U1],$bi
+	ldx	[%sp+LOCALS64+$Hsqr],$a0
+	ldx	[%sp+LOCALS64+$Hsqr+8],$a1
+	ldx	[%sp+LOCALS64+$Hsqr+16],$a2
+	ldx	[%sp+LOCALS64+$Hsqr+24],$a3
+	add	%sp,LOCALS64+$U1,$bp
+	call	__ecp_nistz256_mul_mont_vis3	! p256_mul_mont(U2, U1, Hsqr);
+	add	%sp,LOCALS64+$U2,$rp
+
+	call	__ecp_nistz256_mul_by_2_vis3	! p256_mul_by_2(Hsqr, U2);
+	add	%sp,LOCALS64+$Hsqr,$rp
+
+	add	%sp,LOCALS64+$Rsqr,$bp
+	call	__ecp_nistz256_sub_morf_vis3	! p256_sub(res_x, Rsqr, Hsqr);
+	add	%sp,LOCALS64+$res_x,$rp
+
+	add	%sp,LOCALS64+$Hcub,$bp
+	call	__ecp_nistz256_sub_from_vis3	!  p256_sub(res_x, res_x, Hcub);
+	add	%sp,LOCALS64+$res_x,$rp
+
+	ldx	[%sp+LOCALS64+$S1],$bi		! forward load
+	ldx	[%sp+LOCALS64+$Hcub],$a0
+	ldx	[%sp+LOCALS64+$Hcub+8],$a1
+	ldx	[%sp+LOCALS64+$Hcub+16],$a2
+	ldx	[%sp+LOCALS64+$Hcub+24],$a3
+
+	add	%sp,LOCALS64+$U2,$bp
+	call	__ecp_nistz256_sub_morf_vis3	! p256_sub(res_y, U2, res_x);
+	add	%sp,LOCALS64+$res_y,$rp
+
+	add	%sp,LOCALS64+$S1,$bp
+	call	__ecp_nistz256_mul_mont_vis3	! p256_mul_mont(S2, S1, Hcub);
+	add	%sp,LOCALS64+$S2,$rp
+
+	ldx	[%sp+LOCALS64+$R],$bi
+	ldx	[%sp+LOCALS64+$res_y],$a0
+	ldx	[%sp+LOCALS64+$res_y+8],$a1
+	ldx	[%sp+LOCALS64+$res_y+16],$a2
+	ldx	[%sp+LOCALS64+$res_y+24],$a3
+	add	%sp,LOCALS64+$R,$bp
+	call	__ecp_nistz256_mul_mont_vis3	! p256_mul_mont(res_y, res_y, R);
+	add	%sp,LOCALS64+$res_y,$rp
+
+	add	%sp,LOCALS64+$S2,$bp
+	call	__ecp_nistz256_sub_from_vis3	! p256_sub(res_y, res_y, S2);
+	add	%sp,LOCALS64+$res_y,$rp
+
+	ldx	[%fp+STACK_BIAS-16],$t1		! !in1infty
+	ldx	[%fp+STACK_BIAS-8],$t2		! !in2infty
+___
+for($i=0;$i<96;$i+=16) {			# conditional moves
+$code.=<<___;
+	ldx	[%sp+LOCALS64+$res_x+$i],$acc0	! res
+	ldx	[%sp+LOCALS64+$res_x+$i+8],$acc1
+	ldx	[%sp+LOCALS64+$in2_x+$i],$acc2	! in2
+	ldx	[%sp+LOCALS64+$in2_x+$i+8],$acc3
+	ldx	[%sp+LOCALS64+$in1_x+$i],$acc4	! in1
+	ldx	[%sp+LOCALS64+$in1_x+$i+8],$acc5
+	movrz	$t1,$acc2,$acc0
+	movrz	$t1,$acc3,$acc1
+	movrz	$t2,$acc4,$acc0
+	movrz	$t2,$acc5,$acc1
+	srlx	$acc0,32,$acc2
+	srlx	$acc1,32,$acc3
+	st	$acc0,[$rp_real+$i]
+	st	$acc2,[$rp_real+$i+4]
+	st	$acc1,[$rp_real+$i+8]
+	st	$acc3,[$rp_real+$i+12]
+___
+}
+$code.=<<___;
+.Ladd_done_vis3:
+	ret
+	restore
+.type	ecp_nistz256_point_add_vis3,#function
+.size	ecp_nistz256_point_add_vis3,.-ecp_nistz256_point_add_vis3
+___
+}
+########################################################################
+# void ecp_nistz256_point_add_affine(P256_POINT *out,const P256_POINT *in1,
+#				     const P256_POINT_AFFINE *in2);
+{
+my ($res_x,$res_y,$res_z,
+    $in1_x,$in1_y,$in1_z,
+    $in2_x,$in2_y,
+    $U2,$S2,$H,$R,$Hsqr,$Hcub,$Rsqr)=map(32*$_,(0..14));
+my $Z1sqr = $S2;
+# above map() describes stack layout with 15 temporary
+# 256-bit vectors on top. Then we reserve some space for
+# !in1infty and !in2infty.
+
+$code.=<<___;
+.align	32
+ecp_nistz256_point_add_affine_vis3:
+	save	%sp,-STACK64_FRAME-32*15-32,%sp
+
+	mov	$rp,$rp_real
+	mov	-1,$minus1
+	mov	-2,$poly3
+	sllx	$minus1,32,$poly1		! 0xFFFFFFFF00000000
+	srl	$poly3,0,$poly3			! 0x00000000FFFFFFFE
+
+	! convert input to uint64_t[4]
+	ld	[$bp],$a0			! in2_x
+	ld	[$bp+4],$t0
+	ld	[$bp+8],$a1
+	ld	[$bp+12],$t1
+	ld	[$bp+16],$a2
+	ld	[$bp+20],$t2
+	ld	[$bp+24],$a3
+	ld	[$bp+28],$t3
+	sllx	$t0,32,$t0
+	sllx	$t1,32,$t1
+	ld	[$bp+32],$acc0			! in2_y
+	or	$a0,$t0,$a0
+	ld	[$bp+32+4],$t0
+	sllx	$t2,32,$t2
+	ld	[$bp+32+8],$acc1
+	or	$a1,$t1,$a1
+	ld	[$bp+32+12],$t1
+	sllx	$t3,32,$t3
+	ld	[$bp+32+16],$acc2
+	or	$a2,$t2,$a2
+	ld	[$bp+32+20],$t2
+	or	$a3,$t3,$a3
+	ld	[$bp+32+24],$acc3
+	sllx	$t0,32,$t0
+	ld	[$bp+32+28],$t3
+	sllx	$t1,32,$t1
+	stx	$a0,[%sp+LOCALS64+$in2_x]
+	sllx	$t2,32,$t2
+	stx	$a1,[%sp+LOCALS64+$in2_x+8]
+	sllx	$t3,32,$t3
+	stx	$a2,[%sp+LOCALS64+$in2_x+16]
+	or	$acc0,$t0,$acc0
+	stx	$a3,[%sp+LOCALS64+$in2_x+24]
+	or	$acc1,$t1,$acc1
+	stx	$acc0,[%sp+LOCALS64+$in2_y]
+	or	$acc2,$t2,$acc2
+	stx	$acc1,[%sp+LOCALS64+$in2_y+8]
+	or	$acc3,$t3,$acc3
+	stx	$acc2,[%sp+LOCALS64+$in2_y+16]
+	stx	$acc3,[%sp+LOCALS64+$in2_y+24]
+
+	or	$a1,$a0,$a0
+	or	$a3,$a2,$a2
+	or	$acc1,$acc0,$acc0
+	or	$acc3,$acc2,$acc2
+	or	$a2,$a0,$a0
+	or	$acc2,$acc0,$acc0
+	or	$acc0,$a0,$a0
+	movrnz	$a0,-1,$a0			! !in2infty
+	stx	$a0,[%fp+STACK_BIAS-8]
+
+	ld	[$ap],$a0			! in1_x
+	ld	[$ap+4],$t0
+	ld	[$ap+8],$a1
+	ld	[$ap+12],$t1
+	ld	[$ap+16],$a2
+	ld	[$ap+20],$t2
+	ld	[$ap+24],$a3
+	ld	[$ap+28],$t3
+	sllx	$t0,32,$t0
+	sllx	$t1,32,$t1
+	ld	[$ap+32],$acc0			! in1_y
+	or	$a0,$t0,$a0
+	ld	[$ap+32+4],$t0
+	sllx	$t2,32,$t2
+	ld	[$ap+32+8],$acc1
+	or	$a1,$t1,$a1
+	ld	[$ap+32+12],$t1
+	sllx	$t3,32,$t3
+	ld	[$ap+32+16],$acc2
+	or	$a2,$t2,$a2
+	ld	[$ap+32+20],$t2
+	or	$a3,$t3,$a3
+	ld	[$ap+32+24],$acc3
+	sllx	$t0,32,$t0
+	ld	[$ap+32+28],$t3
+	sllx	$t1,32,$t1
+	stx	$a0,[%sp+LOCALS64+$in1_x]
+	sllx	$t2,32,$t2
+	stx	$a1,[%sp+LOCALS64+$in1_x+8]
+	sllx	$t3,32,$t3
+	stx	$a2,[%sp+LOCALS64+$in1_x+16]
+	or	$acc0,$t0,$acc0
+	stx	$a3,[%sp+LOCALS64+$in1_x+24]
+	or	$acc1,$t1,$acc1
+	stx	$acc0,[%sp+LOCALS64+$in1_y]
+	or	$acc2,$t2,$acc2
+	stx	$acc1,[%sp+LOCALS64+$in1_y+8]
+	or	$acc3,$t3,$acc3
+	stx	$acc2,[%sp+LOCALS64+$in1_y+16]
+	stx	$acc3,[%sp+LOCALS64+$in1_y+24]
+
+	ld	[$ap+64],$a0			! in1_z
+	ld	[$ap+64+4],$t0
+	ld	[$ap+64+8],$a1
+	ld	[$ap+64+12],$t1
+	ld	[$ap+64+16],$a2
+	ld	[$ap+64+20],$t2
+	ld	[$ap+64+24],$a3
+	ld	[$ap+64+28],$t3
+	sllx	$t0,32,$t0
+	sllx	$t1,32,$t1
+	or	$a0,$t0,$a0
+	sllx	$t2,32,$t2
+	or	$a1,$t1,$a1
+	sllx	$t3,32,$t3
+	stx	$a0,[%sp+LOCALS64+$in1_z]
+	or	$a2,$t2,$a2
+	stx	$a1,[%sp+LOCALS64+$in1_z+8]
+	or	$a3,$t3,$a3
+	stx	$a2,[%sp+LOCALS64+$in1_z+16]
+	stx	$a3,[%sp+LOCALS64+$in1_z+24]
+
+	or	$a1,$a0,$t0
+	or	$a3,$a2,$t2
+	or	$t2,$t0,$t0
+	movrnz	$t0,-1,$t0			! !in1infty
+	stx	$t0,[%fp+STACK_BIAS-16]
+
+	call	__ecp_nistz256_sqr_mont_vis3	! p256_sqr_mont(Z1sqr, in1_z);
+	add	%sp,LOCALS64+$Z1sqr,$rp
+
+	ldx	[%sp+LOCALS64+$in2_x],$bi
+	mov	$acc0,$a0
+	mov	$acc1,$a1
+	mov	$acc2,$a2
+	mov	$acc3,$a3
+	add	%sp,LOCALS64+$in2_x,$bp
+	call	__ecp_nistz256_mul_mont_vis3	! p256_mul_mont(U2, Z1sqr, in2_x);
+	add	%sp,LOCALS64+$U2,$rp
+
+	ldx	[%sp+LOCALS64+$Z1sqr],$bi	! forward load
+	ldx	[%sp+LOCALS64+$in1_z],$a0
+	ldx	[%sp+LOCALS64+$in1_z+8],$a1
+	ldx	[%sp+LOCALS64+$in1_z+16],$a2
+	ldx	[%sp+LOCALS64+$in1_z+24],$a3
+
+	add	%sp,LOCALS64+$in1_x,$bp
+	call	__ecp_nistz256_sub_from_vis3	! p256_sub(H, U2, in1_x);
+	add	%sp,LOCALS64+$H,$rp
+
+	add	%sp,LOCALS64+$Z1sqr,$bp
+	call	__ecp_nistz256_mul_mont_vis3	! p256_mul_mont(S2, Z1sqr, in1_z);
+	add	%sp,LOCALS64+$S2,$rp
+
+	ldx	[%sp+LOCALS64+$H],$bi
+	ldx	[%sp+LOCALS64+$in1_z],$a0
+	ldx	[%sp+LOCALS64+$in1_z+8],$a1
+	ldx	[%sp+LOCALS64+$in1_z+16],$a2
+	ldx	[%sp+LOCALS64+$in1_z+24],$a3
+	add	%sp,LOCALS64+$H,$bp
+	call	__ecp_nistz256_mul_mont_vis3	! p256_mul_mont(res_z, H, in1_z);
+	add	%sp,LOCALS64+$res_z,$rp
+
+	ldx	[%sp+LOCALS64+$S2],$bi
+	ldx	[%sp+LOCALS64+$in2_y],$a0
+	ldx	[%sp+LOCALS64+$in2_y+8],$a1
+	ldx	[%sp+LOCALS64+$in2_y+16],$a2
+	ldx	[%sp+LOCALS64+$in2_y+24],$a3
+	add	%sp,LOCALS64+$S2,$bp
+	call	__ecp_nistz256_mul_mont_vis3	! p256_mul_mont(S2, S2, in2_y);
+	add	%sp,LOCALS64+$S2,$rp
+
+	ldx	[%sp+LOCALS64+$H],$a0		! forward load
+	ldx	[%sp+LOCALS64+$H+8],$a1
+	ldx	[%sp+LOCALS64+$H+16],$a2
+	ldx	[%sp+LOCALS64+$H+24],$a3
+
+	add	%sp,LOCALS64+$in1_y,$bp
+	call	__ecp_nistz256_sub_from_vis3	! p256_sub(R, S2, in1_y);
+	add	%sp,LOCALS64+$R,$rp
+
+	call	__ecp_nistz256_sqr_mont_vis3	! p256_sqr_mont(Hsqr, H);
+	add	%sp,LOCALS64+$Hsqr,$rp
+
+	ldx	[%sp+LOCALS64+$R],$a0
+	ldx	[%sp+LOCALS64+$R+8],$a1
+	ldx	[%sp+LOCALS64+$R+16],$a2
+	ldx	[%sp+LOCALS64+$R+24],$a3
+	call	__ecp_nistz256_sqr_mont_vis3	! p256_sqr_mont(Rsqr, R);
+	add	%sp,LOCALS64+$Rsqr,$rp
+
+	ldx	[%sp+LOCALS64+$H],$bi
+	ldx	[%sp+LOCALS64+$Hsqr],$a0
+	ldx	[%sp+LOCALS64+$Hsqr+8],$a1
+	ldx	[%sp+LOCALS64+$Hsqr+16],$a2
+	ldx	[%sp+LOCALS64+$Hsqr+24],$a3
+	add	%sp,LOCALS64+$H,$bp
+	call	__ecp_nistz256_mul_mont_vis3	! p256_mul_mont(Hcub, Hsqr, H);
+	add	%sp,LOCALS64+$Hcub,$rp
+
+	ldx	[%sp+LOCALS64+$Hsqr],$bi
+	ldx	[%sp+LOCALS64+$in1_x],$a0
+	ldx	[%sp+LOCALS64+$in1_x+8],$a1
+	ldx	[%sp+LOCALS64+$in1_x+16],$a2
+	ldx	[%sp+LOCALS64+$in1_x+24],$a3
+	add	%sp,LOCALS64+$Hsqr,$bp
+	call	__ecp_nistz256_mul_mont_vis3	! p256_mul_mont(U2, in1_x, Hsqr);
+	add	%sp,LOCALS64+$U2,$rp
+
+	call	__ecp_nistz256_mul_by_2_vis3	! p256_mul_by_2(Hsqr, U2);
+	add	%sp,LOCALS64+$Hsqr,$rp
+
+	add	%sp,LOCALS64+$Rsqr,$bp
+	call	__ecp_nistz256_sub_morf_vis3	! p256_sub(res_x, Rsqr, Hsqr);
+	add	%sp,LOCALS64+$res_x,$rp
+
+	add	%sp,LOCALS64+$Hcub,$bp
+	call	__ecp_nistz256_sub_from_vis3	!  p256_sub(res_x, res_x, Hcub);
+	add	%sp,LOCALS64+$res_x,$rp
+
+	ldx	[%sp+LOCALS64+$Hcub],$bi	! forward load
+	ldx	[%sp+LOCALS64+$in1_y],$a0
+	ldx	[%sp+LOCALS64+$in1_y+8],$a1
+	ldx	[%sp+LOCALS64+$in1_y+16],$a2
+	ldx	[%sp+LOCALS64+$in1_y+24],$a3
+
+	add	%sp,LOCALS64+$U2,$bp
+	call	__ecp_nistz256_sub_morf_vis3	! p256_sub(res_y, U2, res_x);
+	add	%sp,LOCALS64+$res_y,$rp
+
+	add	%sp,LOCALS64+$Hcub,$bp
+	call	__ecp_nistz256_mul_mont_vis3	! p256_mul_mont(S2, in1_y, Hcub);
+	add	%sp,LOCALS64+$S2,$rp
+
+	ldx	[%sp+LOCALS64+$R],$bi
+	ldx	[%sp+LOCALS64+$res_y],$a0
+	ldx	[%sp+LOCALS64+$res_y+8],$a1
+	ldx	[%sp+LOCALS64+$res_y+16],$a2
+	ldx	[%sp+LOCALS64+$res_y+24],$a3
+	add	%sp,LOCALS64+$R,$bp
+	call	__ecp_nistz256_mul_mont_vis3	! p256_mul_mont(res_y, res_y, R);
+	add	%sp,LOCALS64+$res_y,$rp
+
+	add	%sp,LOCALS64+$S2,$bp
+	call	__ecp_nistz256_sub_from_vis3	! p256_sub(res_y, res_y, S2);
+	add	%sp,LOCALS64+$res_y,$rp
+
+	ldx	[%fp+STACK_BIAS-16],$t1		! !in1infty
+	ldx	[%fp+STACK_BIAS-8],$t2		! !in2infty
+1:	call	.+8
+	add	%o7,.Lone_mont_vis3-1b,$bp
+___
+for($i=0;$i<64;$i+=16) {			# conditional moves
+$code.=<<___;
+	ldx	[%sp+LOCALS64+$res_x+$i],$acc0	! res
+	ldx	[%sp+LOCALS64+$res_x+$i+8],$acc1
+	ldx	[%sp+LOCALS64+$in2_x+$i],$acc2	! in2
+	ldx	[%sp+LOCALS64+$in2_x+$i+8],$acc3
+	ldx	[%sp+LOCALS64+$in1_x+$i],$acc4	! in1
+	ldx	[%sp+LOCALS64+$in1_x+$i+8],$acc5
+	movrz	$t1,$acc2,$acc0
+	movrz	$t1,$acc3,$acc1
+	movrz	$t2,$acc4,$acc0
+	movrz	$t2,$acc5,$acc1
+	srlx	$acc0,32,$acc2
+	srlx	$acc1,32,$acc3
+	st	$acc0,[$rp_real+$i]
+	st	$acc2,[$rp_real+$i+4]
+	st	$acc1,[$rp_real+$i+8]
+	st	$acc3,[$rp_real+$i+12]
+___
+}
+for(;$i<96;$i+=16) {
+$code.=<<___;
+	ldx	[%sp+LOCALS64+$res_x+$i],$acc0	! res
+	ldx	[%sp+LOCALS64+$res_x+$i+8],$acc1
+	ldx	[$bp+$i-64],$acc2		! "in2"
+	ldx	[$bp+$i-64+8],$acc3
+	ldx	[%sp+LOCALS64+$in1_x+$i],$acc4	! in1
+	ldx	[%sp+LOCALS64+$in1_x+$i+8],$acc5
+	movrz	$t1,$acc2,$acc0
+	movrz	$t1,$acc3,$acc1
+	movrz	$t2,$acc4,$acc0
+	movrz	$t2,$acc5,$acc1
+	srlx	$acc0,32,$acc2
+	srlx	$acc1,32,$acc3
+	st	$acc0,[$rp_real+$i]
+	st	$acc2,[$rp_real+$i+4]
+	st	$acc1,[$rp_real+$i+8]
+	st	$acc3,[$rp_real+$i+12]
+___
+}
+$code.=<<___;
+	ret
+	restore
+.type	ecp_nistz256_point_add_affine_vis3,#function
+.size	ecp_nistz256_point_add_affine_vis3,.-ecp_nistz256_point_add_affine_vis3
+.align	64
+.Lone_mont_vis3:
+.long	0x00000000,0x00000001, 0xffffffff,0x00000000
+.long	0xffffffff,0xffffffff, 0x00000000,0xfffffffe
+.align	64
+___
+}								}}}
+
+# Purpose of these subroutines is to explicitly encode VIS instructions,
+# so that one can compile the module without having to specify VIS
+# extensions on compiler command line, e.g. -xarch=v9 vs. -xarch=v9a.
+# Idea is to reserve for option to produce "universal" binary and let
+# programmer detect if current CPU is VIS capable at run-time.
+sub unvis3 {
+my ($mnemonic,$rs1,$rs2,$rd)=@_;
+my %bias = ( "g" => 0, "o" => 8, "l" => 16, "i" => 24 );
+my ($ref,$opf);
+my %visopf = (	"addxc"		=> 0x011,
+		"addxccc"	=> 0x013,
+		"umulxhi"	=> 0x016	);
+
+    $ref = "$mnemonic\t$rs1,$rs2,$rd";
+
+    if ($opf=$visopf{$mnemonic}) {
+	foreach ($rs1,$rs2,$rd) {
+	    return $ref if (!/%([goli])([0-9])/);
+	    $_=$bias{$1}+$2;
+	}
+
+	return	sprintf ".word\t0x%08x !%s",
+			0x81b00000|$rd<<25|$rs1<<14|$opf<<5|$rs2,
+			$ref;
+    } else {
+	return $ref;
+    }
+}
+
+foreach (split("\n",$code)) {
+	s/\`([^\`]*)\`/eval $1/ge;
+
+	s/\b(umulxhi|addxc[c]{0,2})\s+(%[goli][0-7]),\s*(%[goli][0-7]),\s*(%[goli][0-7])/
+		&unvis3($1,$2,$3,$4)
+	 /ge;
+
+	print $_,"\n";
+}
+
+close STDOUT;
diff --git a/crypto/ec/asm/ecp_nistz256-x86.pl b/crypto/ec/asm/ecp_nistz256-x86.pl
new file mode 100755
index 000000000..1d9e00616
--- /dev/null
+++ b/crypto/ec/asm/ecp_nistz256-x86.pl
@@ -0,0 +1,1866 @@
+#! /usr/bin/env perl
+# Copyright 2015-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+
+# ====================================================================
+# Written by Andy Polyakov  for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+#
+# ECP_NISTZ256 module for x86/SSE2.
+#
+# October 2014.
+#
+# Original ECP_NISTZ256 submission targeting x86_64 is detailed in
+# http://eprint.iacr.org/2013/816. In the process of adaptation
+# original .c module was made 32-bit savvy in order to make this
+# implementation possible.
+#
+#		with/without -DECP_NISTZ256_ASM
+# Pentium	+66-163%
+# PIII		+72-172%
+# P4		+65-132%
+# Core2		+90-215%
+# Sandy Bridge	+105-265% (contemporary i[57]-* are all close to this)
+# Atom		+65-155%
+# Opteron	+54-110%
+# Bulldozer	+99-240%
+# VIA Nano	+93-290%
+#
+# Ranges denote minimum and maximum improvement coefficients depending
+# on benchmark. Lower coefficients are for ECDSA sign, server-side
+# operation. Keep in mind that +200% means 3x improvement.
+
+$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+push(@INC,"${dir}","${dir}../../perlasm");
+require "x86asm.pl";
+
+$output=pop;
+open STDOUT,">$output";
+
+&asm_init($ARGV[0],"ecp_nistz256-x86.pl",$ARGV[$#ARGV] eq "386");
+
+$sse2=0;
+for (@ARGV) { $sse2=1 if (/-DOPENSSL_IA32_SSE2/); }
+
+&external_label("OPENSSL_ia32cap_P") if ($sse2);
+
+
+########################################################################
+# Convert ecp_nistz256_table.c to layout expected by ecp_nistz_gather_w7
+#
+open TABLE,") {
+	s/TOBN\(\s*(0x[0-9a-f]+),\s*(0x[0-9a-f]+)\s*\)/push @arr,hex($2),hex($1)/geo;
+}
+close TABLE;
+
+# See ecp_nistz256_table.c for explanation for why it's 64*16*37.
+# 64*16*37-1 is because $#arr returns last valid index or @arr, not
+# amount of elements.
+die "insane number of elements" if ($#arr != 64*16*37-1);
+
+&public_label("ecp_nistz256_precomputed");
+&align(4096);
+&set_label("ecp_nistz256_precomputed");
+
+########################################################################
+# this conversion smashes P256_POINT_AFFINE by individual bytes with
+# 64 byte interval, similar to
+#	1111222233334444
+#	1234123412341234
+for(1..37) {
+	@tbl = splice(@arr,0,64*16);
+	for($i=0;$i<64;$i++) {
+		undef @line;
+		for($j=0;$j<64;$j++) {
+			push @line,(@tbl[$j*16+$i/4]>>(($i%4)*8))&0xff;
+		}
+		&data_byte(join(',',map { sprintf "0x%02x",$_} @line));
+	}
+}
+
+########################################################################
+# Keep in mind that constants are stored least to most significant word
+&static_label("RR");
+&set_label("RR",64);
+&data_word(3,0,-1,-5,-2,-1,-3,4);	# 2^512 mod P-256
+
+&static_label("ONE_mont");
+&set_label("ONE_mont");
+&data_word(1,0,0,-1,-1,-1,-2,0);
+
+&static_label("ONE");
+&set_label("ONE");
+&data_word(1,0,0,0,0,0,0,0);
+&asciz("ECP_NISZ256 for x86/SSE2, CRYPTOGAMS by ");
+&align(64);
+
+########################################################################
+# void ecp_nistz256_mul_by_2(BN_ULONG edi[8],const BN_ULONG esi[8]);
+&function_begin("ecp_nistz256_mul_by_2");
+	&mov	("esi",&wparam(1));
+	&mov	("edi",&wparam(0));
+	&mov	("ebp","esi");
+########################################################################
+# common pattern for internal functions is that %edi is result pointer,
+# %esi and %ebp are input ones, %ebp being optional. %edi is preserved.
+	&call	("_ecp_nistz256_add");
+&function_end("ecp_nistz256_mul_by_2");
+
+########################################################################
+# void ecp_nistz256_mul_by_3(BN_ULONG edi[8],const BN_ULONG esi[8]);
+&function_begin("ecp_nistz256_mul_by_3");
+	&mov	("esi",&wparam(1));
+					# multiplication by 3 is performed
+					# as 2*n+n, but we can't use output
+					# to store 2*n, because if output
+					# pointer equals to input, then
+					# we'll get 2*n+2*n.
+	&stack_push(8);			# therefore we need to allocate
+					# 256-bit intermediate buffer.
+	&mov	("edi","esp");
+	&mov	("ebp","esi");
+	&call	("_ecp_nistz256_add");
+	&lea	("esi",&DWP(0,"edi"));
+	&mov	("ebp",&wparam(1));
+	&mov	("edi",&wparam(0));
+	&call	("_ecp_nistz256_add");
+	&stack_pop(8);
+&function_end("ecp_nistz256_mul_by_3");
+
+########################################################################
+# void ecp_nistz256_div_by_2(BN_ULONG edi[8],const BN_ULONG esi[8]);
+&function_begin("ecp_nistz256_div_by_2");
+	&mov	("esi",&wparam(1));
+	&mov	("edi",&wparam(0));
+	&call	("_ecp_nistz256_div_by_2");
+&function_end("ecp_nistz256_div_by_2");
+
+&function_begin_B("_ecp_nistz256_div_by_2");
+	# tmp = a is odd ? a+mod : a
+	#
+	# note that because mod has special form, i.e. consists of
+	# 0xffffffff, 1 and 0s, we can conditionally synthesize it by
+	# assigning least significant bit of input to one register,
+	# %ebp, and its negative to another, %edx.
+
+	&mov	("ebp",&DWP(0,"esi"));
+	&xor	("edx","edx");
+	&mov	("ebx",&DWP(4,"esi"));
+	&mov	("eax","ebp");
+	&and	("ebp",1);
+	&mov	("ecx",&DWP(8,"esi"));
+	&sub	("edx","ebp");
+
+	&add	("eax","edx");
+	&adc	("ebx","edx");
+	&mov	(&DWP(0,"edi"),"eax");
+	&adc	("ecx","edx");
+	&mov	(&DWP(4,"edi"),"ebx");
+	&mov	(&DWP(8,"edi"),"ecx");
+
+	&mov	("eax",&DWP(12,"esi"));
+	&mov	("ebx",&DWP(16,"esi"));
+	&adc	("eax",0);
+	&mov	("ecx",&DWP(20,"esi"));
+	&adc	("ebx",0);
+	&mov	(&DWP(12,"edi"),"eax");
+	&adc	("ecx",0);
+	&mov	(&DWP(16,"edi"),"ebx");
+	&mov	(&DWP(20,"edi"),"ecx");
+
+	&mov	("eax",&DWP(24,"esi"));
+	&mov	("ebx",&DWP(28,"esi"));
+	&adc	("eax","ebp");
+	&adc	("ebx","edx");
+	&mov	(&DWP(24,"edi"),"eax");
+	&sbb	("esi","esi");			# broadcast carry bit
+	&mov	(&DWP(28,"edi"),"ebx");
+
+	# ret = tmp >> 1
+
+	&mov	("eax",&DWP(0,"edi"));
+	&mov	("ebx",&DWP(4,"edi"));
+	&mov	("ecx",&DWP(8,"edi"));
+	&mov	("edx",&DWP(12,"edi"));
+
+	&shr	("eax",1);
+	&mov	("ebp","ebx");
+	&shl	("ebx",31);
+	&or	("eax","ebx");
+
+	&shr	("ebp",1);
+	&mov	("ebx","ecx");
+	&shl	("ecx",31);
+	&mov	(&DWP(0,"edi"),"eax");
+	&or	("ebp","ecx");
+	&mov	("eax",&DWP(16,"edi"));
+
+	&shr	("ebx",1);
+	&mov	("ecx","edx");
+	&shl	("edx",31);
+	&mov	(&DWP(4,"edi"),"ebp");
+	&or	("ebx","edx");
+	&mov	("ebp",&DWP(20,"edi"));
+
+	&shr	("ecx",1);
+	&mov	("edx","eax");
+	&shl	("eax",31);
+	&mov	(&DWP(8,"edi"),"ebx");
+	&or	("ecx","eax");
+	&mov	("ebx",&DWP(24,"edi"));
+
+	&shr	("edx",1);
+	&mov	("eax","ebp");
+	&shl	("ebp",31);
+	&mov	(&DWP(12,"edi"),"ecx");
+	&or	("edx","ebp");
+	&mov	("ecx",&DWP(28,"edi"));
+
+	&shr	("eax",1);
+	&mov	("ebp","ebx");
+	&shl	("ebx",31);
+	&mov	(&DWP(16,"edi"),"edx");
+	&or	("eax","ebx");
+
+	&shr	("ebp",1);
+	&mov	("ebx","ecx");
+	&shl	("ecx",31);
+	&mov	(&DWP(20,"edi"),"eax");
+	&or	("ebp","ecx");
+
+	&shr	("ebx",1);
+	&shl	("esi",31);
+	&mov	(&DWP(24,"edi"),"ebp");
+	&or	("ebx","esi");			# handle top-most carry bit
+	&mov	(&DWP(28,"edi"),"ebx");
+
+	&ret	();
+&function_end_B("_ecp_nistz256_div_by_2");
+
+########################################################################
+# void ecp_nistz256_add(BN_ULONG edi[8],const BN_ULONG esi[8],
+#					const BN_ULONG ebp[8]);
+&function_begin("ecp_nistz256_add");
+	&mov	("esi",&wparam(1));
+	&mov	("ebp",&wparam(2));
+	&mov	("edi",&wparam(0));
+	&call	("_ecp_nistz256_add");
+&function_end("ecp_nistz256_add");
+
+&function_begin_B("_ecp_nistz256_add");
+	&mov	("eax",&DWP(0,"esi"));
+	&mov	("ebx",&DWP(4,"esi"));
+	&mov	("ecx",&DWP(8,"esi"));
+	&add	("eax",&DWP(0,"ebp"));
+	&mov	("edx",&DWP(12,"esi"));
+	&adc	("ebx",&DWP(4,"ebp"));
+	&mov	(&DWP(0,"edi"),"eax");
+	&adc	("ecx",&DWP(8,"ebp"));
+	&mov	(&DWP(4,"edi"),"ebx");
+	&adc	("edx",&DWP(12,"ebp"));
+	&mov	(&DWP(8,"edi"),"ecx");
+	&mov	(&DWP(12,"edi"),"edx");
+
+	&mov	("eax",&DWP(16,"esi"));
+	&mov	("ebx",&DWP(20,"esi"));
+	&mov	("ecx",&DWP(24,"esi"));
+	&adc	("eax",&DWP(16,"ebp"));
+	&mov	("edx",&DWP(28,"esi"));
+	&adc	("ebx",&DWP(20,"ebp"));
+	&mov	(&DWP(16,"edi"),"eax");
+	&adc	("ecx",&DWP(24,"ebp"));
+	&mov	(&DWP(20,"edi"),"ebx");
+	&mov	("esi",0);
+	&adc	("edx",&DWP(28,"ebp"));
+	&mov	(&DWP(24,"edi"),"ecx");
+	&adc	("esi",0);
+	&mov	(&DWP(28,"edi"),"edx");
+
+	# if a+b >= modulus, subtract modulus.
+	#
+	# But since comparison implies subtraction, we subtract modulus
+	# to see if it borrows, and then subtract it for real if
+	# subtraction didn't borrow.
+
+	&mov	("eax",&DWP(0,"edi"));
+	&mov	("ebx",&DWP(4,"edi"));
+	&mov	("ecx",&DWP(8,"edi"));
+	&sub	("eax",-1);
+	&mov	("edx",&DWP(12,"edi"));
+	&sbb	("ebx",-1);
+	&mov	("eax",&DWP(16,"edi"));
+	&sbb	("ecx",-1);
+	&mov	("ebx",&DWP(20,"edi"));
+	&sbb	("edx",0);
+	&mov	("ecx",&DWP(24,"edi"));
+	&sbb	("eax",0);
+	&mov	("edx",&DWP(28,"edi"));
+	&sbb	("ebx",0);
+	&sbb	("ecx",1);
+	&sbb	("edx",-1);
+	&sbb	("esi",0);
+
+	# Note that because mod has special form, i.e. consists of
+	# 0xffffffff, 1 and 0s, we can conditionally synthesize it by
+	# by using borrow.
+
+	¬	("esi");
+	&mov	("eax",&DWP(0,"edi"));
+	&mov	("ebp","esi");
+	&mov	("ebx",&DWP(4,"edi"));
+	&shr	("ebp",31);
+	&mov	("ecx",&DWP(8,"edi"));
+	&sub	("eax","esi");
+	&mov	("edx",&DWP(12,"edi"));
+	&sbb	("ebx","esi");
+	&mov	(&DWP(0,"edi"),"eax");
+	&sbb	("ecx","esi");
+	&mov	(&DWP(4,"edi"),"ebx");
+	&sbb	("edx",0);
+	&mov	(&DWP(8,"edi"),"ecx");
+	&mov	(&DWP(12,"edi"),"edx");
+
+	&mov	("eax",&DWP(16,"edi"));
+	&mov	("ebx",&DWP(20,"edi"));
+	&mov	("ecx",&DWP(24,"edi"));
+	&sbb	("eax",0);
+	&mov	("edx",&DWP(28,"edi"));
+	&sbb	("ebx",0);
+	&mov	(&DWP(16,"edi"),"eax");
+	&sbb	("ecx","ebp");
+	&mov	(&DWP(20,"edi"),"ebx");
+	&sbb	("edx","esi");
+	&mov	(&DWP(24,"edi"),"ecx");
+	&mov	(&DWP(28,"edi"),"edx");
+
+	&ret	();
+&function_end_B("_ecp_nistz256_add");
+
+########################################################################
+# void ecp_nistz256_sub(BN_ULONG edi[8],const BN_ULONG esi[8],
+#					const BN_ULONG ebp[8]);
+&function_begin("ecp_nistz256_sub");
+	&mov	("esi",&wparam(1));
+	&mov	("ebp",&wparam(2));
+	&mov	("edi",&wparam(0));
+	&call	("_ecp_nistz256_sub");
+&function_end("ecp_nistz256_sub");
+
+&function_begin_B("_ecp_nistz256_sub");
+	&mov	("eax",&DWP(0,"esi"));
+	&mov	("ebx",&DWP(4,"esi"));
+	&mov	("ecx",&DWP(8,"esi"));
+	&sub	("eax",&DWP(0,"ebp"));
+	&mov	("edx",&DWP(12,"esi"));
+	&sbb	("ebx",&DWP(4,"ebp"));
+	&mov	(&DWP(0,"edi"),"eax");
+	&sbb	("ecx",&DWP(8,"ebp"));
+	&mov	(&DWP(4,"edi"),"ebx");
+	&sbb	("edx",&DWP(12,"ebp"));
+	&mov	(&DWP(8,"edi"),"ecx");
+	&mov	(&DWP(12,"edi"),"edx");
+
+	&mov	("eax",&DWP(16,"esi"));
+	&mov	("ebx",&DWP(20,"esi"));
+	&mov	("ecx",&DWP(24,"esi"));
+	&sbb	("eax",&DWP(16,"ebp"));
+	&mov	("edx",&DWP(28,"esi"));
+	&sbb	("ebx",&DWP(20,"ebp"));
+	&sbb	("ecx",&DWP(24,"ebp"));
+	&mov	(&DWP(16,"edi"),"eax");
+	&sbb	("edx",&DWP(28,"ebp"));
+	&mov	(&DWP(20,"edi"),"ebx");
+	&sbb	("esi","esi");			# broadcast borrow bit
+	&mov	(&DWP(24,"edi"),"ecx");
+	&mov	(&DWP(28,"edi"),"edx");
+
+	# if a-b borrows, add modulus.
+	#
+	# Note that because mod has special form, i.e. consists of
+	# 0xffffffff, 1 and 0s, we can conditionally synthesize it by
+	# assigning borrow bit to one register, %ebp, and its negative
+	# to another, %esi. But we started by calculating %esi...
+
+	&mov	("eax",&DWP(0,"edi"));
+	&mov	("ebp","esi");
+	&mov	("ebx",&DWP(4,"edi"));
+	&shr	("ebp",31);
+	&mov	("ecx",&DWP(8,"edi"));
+	&add	("eax","esi");
+	&mov	("edx",&DWP(12,"edi"));
+	&adc	("ebx","esi");
+	&mov	(&DWP(0,"edi"),"eax");
+	&adc	("ecx","esi");
+	&mov	(&DWP(4,"edi"),"ebx");
+	&adc	("edx",0);
+	&mov	(&DWP(8,"edi"),"ecx");
+	&mov	(&DWP(12,"edi"),"edx");
+
+	&mov	("eax",&DWP(16,"edi"));
+	&mov	("ebx",&DWP(20,"edi"));
+	&mov	("ecx",&DWP(24,"edi"));
+	&adc	("eax",0);
+	&mov	("edx",&DWP(28,"edi"));
+	&adc	("ebx",0);
+	&mov	(&DWP(16,"edi"),"eax");
+	&adc	("ecx","ebp");
+	&mov	(&DWP(20,"edi"),"ebx");
+	&adc	("edx","esi");
+	&mov	(&DWP(24,"edi"),"ecx");
+	&mov	(&DWP(28,"edi"),"edx");
+
+	&ret	();
+&function_end_B("_ecp_nistz256_sub");
+
+########################################################################
+# void ecp_nistz256_neg(BN_ULONG edi[8],const BN_ULONG esi[8]);
+&function_begin("ecp_nistz256_neg");
+	&mov	("ebp",&wparam(1));
+	&mov	("edi",&wparam(0));
+
+	&xor	("eax","eax");
+	&stack_push(8);
+	&mov	(&DWP(0,"esp"),"eax");
+	&mov	("esi","esp");
+	&mov	(&DWP(4,"esp"),"eax");
+	&mov	(&DWP(8,"esp"),"eax");
+	&mov	(&DWP(12,"esp"),"eax");
+	&mov	(&DWP(16,"esp"),"eax");
+	&mov	(&DWP(20,"esp"),"eax");
+	&mov	(&DWP(24,"esp"),"eax");
+	&mov	(&DWP(28,"esp"),"eax");
+	
+	&call	("_ecp_nistz256_sub");
+
+	&stack_pop(8);
+&function_end("ecp_nistz256_neg");
+
+&function_begin_B("_picup_eax");
+	&mov	("eax",&DWP(0,"esp"));
+	&ret	();
+&function_end_B("_picup_eax");
+
+########################################################################
+# void ecp_nistz256_to_mont(BN_ULONG edi[8],const BN_ULONG esi[8]);
+&function_begin("ecp_nistz256_to_mont");
+	&mov	("esi",&wparam(1));
+	&call	("_picup_eax");
+    &set_label("pic");
+	&lea	("ebp",&DWP(&label("RR")."-".&label("pic"),"eax"));
+						if ($sse2) {
+	&picmeup("eax","OPENSSL_ia32cap_P","eax",&label("pic"));
+	&mov	("eax",&DWP(0,"eax"));		}
+	&mov	("edi",&wparam(0));
+	&call	("_ecp_nistz256_mul_mont");
+&function_end("ecp_nistz256_to_mont");
+
+########################################################################
+# void ecp_nistz256_from_mont(BN_ULONG edi[8],const BN_ULONG esi[8]);
+&function_begin("ecp_nistz256_from_mont");
+	&mov	("esi",&wparam(1));
+	&call	("_picup_eax");
+    &set_label("pic");
+	&lea	("ebp",&DWP(&label("ONE")."-".&label("pic"),"eax"));
+						if ($sse2) {
+	&picmeup("eax","OPENSSL_ia32cap_P","eax",&label("pic"));
+	&mov	("eax",&DWP(0,"eax"));		}
+	&mov	("edi",&wparam(0));
+	&call	("_ecp_nistz256_mul_mont");
+&function_end("ecp_nistz256_from_mont");
+
+########################################################################
+# void ecp_nistz256_mul_mont(BN_ULONG edi[8],const BN_ULONG esi[8],
+#					     const BN_ULONG ebp[8]);
+&function_begin("ecp_nistz256_mul_mont");
+	&mov	("esi",&wparam(1));
+	&mov	("ebp",&wparam(2));
+						if ($sse2) {
+	&call	("_picup_eax");
+    &set_label("pic");
+	&picmeup("eax","OPENSSL_ia32cap_P","eax",&label("pic"));
+	&mov	("eax",&DWP(0,"eax"));		}
+	&mov	("edi",&wparam(0));
+	&call	("_ecp_nistz256_mul_mont");
+&function_end("ecp_nistz256_mul_mont");
+
+########################################################################
+# void ecp_nistz256_sqr_mont(BN_ULONG edi[8],const BN_ULONG esi[8]);
+&function_begin("ecp_nistz256_sqr_mont");
+	&mov	("esi",&wparam(1));
+						if ($sse2) {
+	&call	("_picup_eax");
+    &set_label("pic");
+	&picmeup("eax","OPENSSL_ia32cap_P","eax",&label("pic"));
+	&mov	("eax",&DWP(0,"eax"));		}
+	&mov	("edi",&wparam(0));
+	&mov	("ebp","esi");
+	&call	("_ecp_nistz256_mul_mont");
+&function_end("ecp_nistz256_sqr_mont");
+
+&function_begin_B("_ecp_nistz256_mul_mont");
+						if ($sse2) {
+	&and	("eax",1<<24|1<<26);
+	&cmp	("eax",1<<24|1<<26);		# see if XMM+SSE2 is on
+	&jne	(&label("mul_mont_ialu"));
+
+	########################################
+	# SSE2 code path featuring 32x16-bit
+	# multiplications is ~2x faster than
+	# IALU counterpart (except on Atom)...
+	########################################
+	# stack layout:
+	# +------------------------------------+< %esp
+	# | 7 16-byte temporary XMM words,     |
+	# | "sliding" toward lower address     |
+	# .                                    .
+	# +------------------------------------+
+	# | unused XMM word                    |
+	# +------------------------------------+< +128,%ebx
+	# | 8 16-byte XMM words holding copies |
+	# | of a[i]<<64|a[i]                   |
+	# .                                    .
+	# .                                    .
+	# +------------------------------------+< +256
+	&mov	("edx","esp");
+	&sub	("esp",0x100);
+
+	&movd	("xmm7",&DWP(0,"ebp"));		# b[0] -> 0000.00xy
+	&lea	("ebp",&DWP(4,"ebp"));
+	&pcmpeqd("xmm6","xmm6");
+	&psrlq	("xmm6",48);			# compose 0xffff<<64|0xffff
+
+	&pshuflw("xmm7","xmm7",0b11011100);	# 0000.00xy -> 0000.0x0y
+	&and	("esp",-64);
+	&pshufd	("xmm7","xmm7",0b11011100);	# 0000.0x0y -> 000x.000y
+	&lea	("ebx",&DWP(0x80,"esp"));
+
+	&movd	("xmm0",&DWP(4*0,"esi"));	# a[0] -> 0000.00xy
+	&pshufd	("xmm0","xmm0",0b11001100);	# 0000.00xy -> 00xy.00xy
+	&movd	("xmm1",&DWP(4*1,"esi"));	# a[1] -> ...
+	&movdqa	(&QWP(0x00,"ebx"),"xmm0");	# offload converted a[0]
+	&pmuludq("xmm0","xmm7");		# a[0]*b[0]
+
+	&movd	("xmm2",&DWP(4*2,"esi"));
+	&pshufd	("xmm1","xmm1",0b11001100);
+	&movdqa	(&QWP(0x10,"ebx"),"xmm1");
+	&pmuludq("xmm1","xmm7");		# a[1]*b[0]
+
+	 &movq	("xmm4","xmm0");		# clear upper 64 bits
+	 &pslldq("xmm4",6);
+	 &paddq	("xmm4","xmm0");
+	 &movdqa("xmm5","xmm4");
+	 &psrldq("xmm4",10);			# upper 32 bits of a[0]*b[0]
+	 &pand	("xmm5","xmm6");		# lower 32 bits of a[0]*b[0]
+
+	# Upper half of a[0]*b[i] is carried into next multiplication
+	# iteration, while lower one "participates" in actual reduction.
+	# Normally latter is done by accumulating result of multiplication
+	# of modulus by "magic" digit, but thanks to special form of modulus
+	# and "magic" digit it can be performed only with additions and
+	# subtractions (see note in IALU section below). Note that we are
+	# not bothered with carry bits, they are accumulated in "flatten"
+	# phase after all multiplications and reductions.
+
+	&movd	("xmm3",&DWP(4*3,"esi"));
+	&pshufd	("xmm2","xmm2",0b11001100);
+	&movdqa	(&QWP(0x20,"ebx"),"xmm2");
+	&pmuludq("xmm2","xmm7");		# a[2]*b[0]
+	 &paddq	("xmm1","xmm4");		# a[1]*b[0]+hw(a[0]*b[0]), carry
+	&movdqa	(&QWP(0x00,"esp"),"xmm1");	# t[0]
+
+	&movd	("xmm0",&DWP(4*4,"esi"));
+	&pshufd	("xmm3","xmm3",0b11001100);
+	&movdqa	(&QWP(0x30,"ebx"),"xmm3");
+	&pmuludq("xmm3","xmm7");		# a[3]*b[0]
+	&movdqa	(&QWP(0x10,"esp"),"xmm2");
+
+	&movd	("xmm1",&DWP(4*5,"esi"));
+	&pshufd	("xmm0","xmm0",0b11001100);
+	&movdqa	(&QWP(0x40,"ebx"),"xmm0");
+	&pmuludq("xmm0","xmm7");		# a[4]*b[0]
+	 &paddq	("xmm3","xmm5");		# a[3]*b[0]+lw(a[0]*b[0]), reduction step
+	&movdqa	(&QWP(0x20,"esp"),"xmm3");
+
+	&movd	("xmm2",&DWP(4*6,"esi"));
+	&pshufd	("xmm1","xmm1",0b11001100);
+	&movdqa	(&QWP(0x50,"ebx"),"xmm1");
+	&pmuludq("xmm1","xmm7");		# a[5]*b[0]
+	&movdqa	(&QWP(0x30,"esp"),"xmm0");
+	 &pshufd("xmm4","xmm5",0b10110001);	# xmm4 = xmm5<<32, reduction step
+
+	&movd	("xmm3",&DWP(4*7,"esi"));
+	&pshufd	("xmm2","xmm2",0b11001100);
+	&movdqa	(&QWP(0x60,"ebx"),"xmm2");
+	&pmuludq("xmm2","xmm7");		# a[6]*b[0]
+	&movdqa	(&QWP(0x40,"esp"),"xmm1");
+	 &psubq	("xmm4","xmm5");		# xmm4 = xmm5*0xffffffff, reduction step
+
+	&movd	("xmm0",&DWP(0,"ebp"));		# b[1] -> 0000.00xy
+	&pshufd	("xmm3","xmm3",0b11001100);
+	&movdqa	(&QWP(0x70,"ebx"),"xmm3");
+	&pmuludq("xmm3","xmm7");		# a[7]*b[0]
+
+	&pshuflw("xmm7","xmm0",0b11011100);	# 0000.00xy -> 0000.0x0y
+	&movdqa	("xmm0",&QWP(0x00,"ebx"));	# pre-load converted a[0]
+	&pshufd	("xmm7","xmm7",0b11011100);	# 0000.0x0y -> 000x.000y
+
+	&mov	("ecx",6);
+	&lea	("ebp",&DWP(4,"ebp"));
+	&jmp	(&label("madd_sse2"));
+
+&set_label("madd_sse2",16);
+	 &paddq	("xmm2","xmm5");		# a[6]*b[i-1]+lw(a[0]*b[i-1]), reduction step [modulo-scheduled]
+	 &paddq	("xmm3","xmm4");		# a[7]*b[i-1]+lw(a[0]*b[i-1])*0xffffffff, reduction step [modulo-scheduled]
+	&movdqa	("xmm1",&QWP(0x10,"ebx"));
+	&pmuludq("xmm0","xmm7");		# a[0]*b[i]
+	 &movdqa(&QWP(0x50,"esp"),"xmm2");
+
+	&movdqa	("xmm2",&QWP(0x20,"ebx"));
+	&pmuludq("xmm1","xmm7");		# a[1]*b[i]
+	 &movdqa(&QWP(0x60,"esp"),"xmm3");
+	&paddq	("xmm0",&QWP(0x00,"esp"));
+
+	&movdqa	("xmm3",&QWP(0x30,"ebx"));
+	&pmuludq("xmm2","xmm7");		# a[2]*b[i]
+	 &movq	("xmm4","xmm0");		# clear upper 64 bits
+	 &pslldq("xmm4",6);
+	&paddq	("xmm1",&QWP(0x10,"esp"));
+	 &paddq	("xmm4","xmm0");
+	 &movdqa("xmm5","xmm4");
+	 &psrldq("xmm4",10);			# upper 33 bits of a[0]*b[i]+t[0]
+
+	&movdqa	("xmm0",&QWP(0x40,"ebx"));
+	&pmuludq("xmm3","xmm7");		# a[3]*b[i]
+	 &paddq	("xmm1","xmm4");		# a[1]*b[i]+hw(a[0]*b[i]), carry
+	&paddq	("xmm2",&QWP(0x20,"esp"));
+	&movdqa	(&QWP(0x00,"esp"),"xmm1");
+
+	&movdqa	("xmm1",&QWP(0x50,"ebx"));
+	&pmuludq("xmm0","xmm7");		# a[4]*b[i]
+	&paddq	("xmm3",&QWP(0x30,"esp"));
+	&movdqa	(&QWP(0x10,"esp"),"xmm2");
+	 &pand	("xmm5","xmm6");		# lower 32 bits of a[0]*b[i]
+
+	&movdqa	("xmm2",&QWP(0x60,"ebx"));
+	&pmuludq("xmm1","xmm7");		# a[5]*b[i]
+	 &paddq	("xmm3","xmm5");		# a[3]*b[i]+lw(a[0]*b[i]), reduction step
+	&paddq	("xmm0",&QWP(0x40,"esp"));
+	&movdqa	(&QWP(0x20,"esp"),"xmm3");
+	 &pshufd("xmm4","xmm5",0b10110001);	# xmm4 = xmm5<<32, reduction step
+
+	&movdqa	("xmm3","xmm7");
+	&pmuludq("xmm2","xmm7");		# a[6]*b[i]
+	 &movd	("xmm7",&DWP(0,"ebp"));		# b[i++] -> 0000.00xy
+	 &lea	("ebp",&DWP(4,"ebp"));
+	&paddq	("xmm1",&QWP(0x50,"esp"));
+	 &psubq	("xmm4","xmm5");		# xmm4 = xmm5*0xffffffff, reduction step
+	&movdqa	(&QWP(0x30,"esp"),"xmm0");
+	 &pshuflw("xmm7","xmm7",0b11011100);	# 0000.00xy -> 0000.0x0y
+
+	&pmuludq("xmm3",&QWP(0x70,"ebx"));	# a[7]*b[i]
+	 &pshufd("xmm7","xmm7",0b11011100);	# 0000.0x0y -> 000x.000y
+	 &movdqa("xmm0",&QWP(0x00,"ebx"));	# pre-load converted a[0]
+	&movdqa	(&QWP(0x40,"esp"),"xmm1");
+	&paddq	("xmm2",&QWP(0x60,"esp"));
+
+	&dec	("ecx");
+	&jnz	(&label("madd_sse2"));
+
+	 &paddq	("xmm2","xmm5");		# a[6]*b[6]+lw(a[0]*b[6]), reduction step [modulo-scheduled]
+	 &paddq	("xmm3","xmm4");		# a[7]*b[6]+lw(a[0]*b[6])*0xffffffff, reduction step [modulo-scheduled]
+	&movdqa	("xmm1",&QWP(0x10,"ebx"));
+	&pmuludq("xmm0","xmm7");		# a[0]*b[7]
+	 &movdqa(&QWP(0x50,"esp"),"xmm2");
+
+	&movdqa	("xmm2",&QWP(0x20,"ebx"));
+	&pmuludq("xmm1","xmm7");		# a[1]*b[7]
+	 &movdqa(&QWP(0x60,"esp"),"xmm3");
+	&paddq	("xmm0",&QWP(0x00,"esp"));
+
+	&movdqa	("xmm3",&QWP(0x30,"ebx"));
+	&pmuludq("xmm2","xmm7");		# a[2]*b[7]
+	 &movq	("xmm4","xmm0");		# clear upper 64 bits
+	 &pslldq("xmm4",6);
+	&paddq	("xmm1",&QWP(0x10,"esp"));
+	 &paddq	("xmm4","xmm0");
+	 &movdqa("xmm5","xmm4");
+	 &psrldq("xmm4",10);			# upper 33 bits of a[0]*b[i]+t[0]
+
+	&movdqa	("xmm0",&QWP(0x40,"ebx"));
+	&pmuludq("xmm3","xmm7");		# a[3]*b[7]
+	 &paddq	("xmm1","xmm4");		# a[1]*b[7]+hw(a[0]*b[7]), carry
+	&paddq	("xmm2",&QWP(0x20,"esp"));
+	&movdqa	(&QWP(0x00,"esp"),"xmm1");
+
+	&movdqa	("xmm1",&QWP(0x50,"ebx"));
+	&pmuludq("xmm0","xmm7");		# a[4]*b[7]
+	&paddq	("xmm3",&QWP(0x30,"esp"));
+	&movdqa	(&QWP(0x10,"esp"),"xmm2");
+	 &pand	("xmm5","xmm6");		# lower 32 bits of a[0]*b[i]
+
+	&movdqa	("xmm2",&QWP(0x60,"ebx"));
+	&pmuludq("xmm1","xmm7");		# a[5]*b[7]
+	 &paddq	("xmm3","xmm5");		# reduction step
+	&paddq	("xmm0",&QWP(0x40,"esp"));
+	&movdqa	(&QWP(0x20,"esp"),"xmm3");
+	 &pshufd("xmm4","xmm5",0b10110001);	# xmm4 = xmm5<<32, reduction step
+
+	&movdqa	("xmm3",&QWP(0x70,"ebx"));
+	&pmuludq("xmm2","xmm7");		# a[6]*b[7]
+	&paddq	("xmm1",&QWP(0x50,"esp"));
+	 &psubq	("xmm4","xmm5");		# xmm4 = xmm5*0xffffffff, reduction step
+	&movdqa	(&QWP(0x30,"esp"),"xmm0");
+
+	&pmuludq("xmm3","xmm7");		# a[7]*b[7]
+	&pcmpeqd("xmm7","xmm7");
+	&movdqa	("xmm0",&QWP(0x00,"esp"));
+	&pslldq	("xmm7",8);
+	&movdqa	(&QWP(0x40,"esp"),"xmm1");
+	&paddq	("xmm2",&QWP(0x60,"esp"));
+
+	 &paddq	("xmm2","xmm5");		# a[6]*b[7]+lw(a[0]*b[7]), reduction step
+	 &paddq	("xmm3","xmm4");		# a[6]*b[7]+lw(a[0]*b[7])*0xffffffff, reduction step
+	 &movdqa(&QWP(0x50,"esp"),"xmm2");
+	 &movdqa(&QWP(0x60,"esp"),"xmm3");
+
+	&movdqa	("xmm1",&QWP(0x10,"esp"));
+	&movdqa	("xmm2",&QWP(0x20,"esp"));
+	&movdqa	("xmm3",&QWP(0x30,"esp"));
+
+	&movq	("xmm4","xmm0");		# "flatten"
+	&pand	("xmm0","xmm7");
+	&xor	("ebp","ebp");
+	&pslldq	("xmm4",6);
+	 &movq	("xmm5","xmm1");
+	&paddq	("xmm0","xmm4");
+	 &pand	("xmm1","xmm7");
+	&psrldq	("xmm0",6);
+	&movd	("eax","xmm0");
+	&psrldq	("xmm0",4);
+
+	&paddq	("xmm5","xmm0");
+	&movdqa	("xmm0",&QWP(0x40,"esp"));
+	&sub	("eax",-1);			# start subtracting modulus,
+						# this is used to determine
+						# if result is larger/smaller
+						# than modulus (see below)
+	&pslldq	("xmm5",6);
+	 &movq	("xmm4","xmm2");
+	&paddq	("xmm1","xmm5");
+	 &pand	("xmm2","xmm7");
+	&psrldq	("xmm1",6);
+	&mov	(&DWP(4*0,"edi"),"eax");
+	&movd	("eax","xmm1");
+	&psrldq	("xmm1",4);
+
+	&paddq	("xmm4","xmm1");
+	&movdqa	("xmm1",&QWP(0x50,"esp"));
+	&sbb	("eax",-1);
+	&pslldq	("xmm4",6);
+	 &movq	("xmm5","xmm3");
+	&paddq	("xmm2","xmm4");
+	 &pand	("xmm3","xmm7");
+	&psrldq	("xmm2",6);
+	&mov	(&DWP(4*1,"edi"),"eax");
+	&movd	("eax","xmm2");
+	&psrldq	("xmm2",4);
+
+	&paddq	("xmm5","xmm2");
+	&movdqa	("xmm2",&QWP(0x60,"esp"));
+	&sbb	("eax",-1);
+	&pslldq	("xmm5",6);
+	 &movq	("xmm4","xmm0");
+	&paddq	("xmm3","xmm5");
+	 &pand	("xmm0","xmm7");
+	&psrldq	("xmm3",6);
+	&mov	(&DWP(4*2,"edi"),"eax");
+	&movd	("eax","xmm3");
+	&psrldq	("xmm3",4);
+
+	&paddq	("xmm4","xmm3");
+	&sbb	("eax",0);
+	&pslldq	("xmm4",6);
+	 &movq	("xmm5","xmm1");
+	&paddq	("xmm0","xmm4");
+	 &pand	("xmm1","xmm7");
+	&psrldq	("xmm0",6);
+	&mov	(&DWP(4*3,"edi"),"eax");
+	&movd	("eax","xmm0");
+	&psrldq	("xmm0",4);
+
+	&paddq	("xmm5","xmm0");
+	&sbb	("eax",0);
+	&pslldq	("xmm5",6);
+	 &movq	("xmm4","xmm2");
+	&paddq	("xmm1","xmm5");
+	 &pand	("xmm2","xmm7");
+	&psrldq	("xmm1",6);
+	&movd	("ebx","xmm1");
+	&psrldq	("xmm1",4);
+	&mov	("esp","edx");
+
+	&paddq	("xmm4","xmm1");
+	&pslldq	("xmm4",6);
+	&paddq	("xmm2","xmm4");
+	&psrldq	("xmm2",6);
+	&movd	("ecx","xmm2");
+	&psrldq	("xmm2",4);
+	&sbb	("ebx",0);
+	&movd	("edx","xmm2");
+	&pextrw	("esi","xmm2",2);		# top-most overflow bit
+	&sbb	("ecx",1);
+	&sbb	("edx",-1);
+	&sbb	("esi",0);			# borrow from subtraction
+
+	# Final step is "if result > mod, subtract mod", and at this point
+	# we have result - mod written to output buffer, as well as borrow
+	# bit from this subtraction, and if borrow bit is set, we add
+	# modulus back.
+	#
+	# Note that because mod has special form, i.e. consists of
+	# 0xffffffff, 1 and 0s, we can conditionally synthesize it by
+	# assigning borrow bit to one register, %ebp, and its negative
+	# to another, %esi. But we started by calculating %esi...
+
+	&sub	("ebp","esi");
+	&add	(&DWP(4*0,"edi"),"esi");	# add modulus or zero
+	&adc	(&DWP(4*1,"edi"),"esi");
+	&adc	(&DWP(4*2,"edi"),"esi");
+	&adc	(&DWP(4*3,"edi"),0);
+	&adc	("eax",0);
+	&adc	("ebx",0);
+	&mov	(&DWP(4*4,"edi"),"eax");
+	&adc	("ecx","ebp");
+	&mov	(&DWP(4*5,"edi"),"ebx");
+	&adc	("edx","esi");
+	&mov	(&DWP(4*6,"edi"),"ecx");
+	&mov	(&DWP(4*7,"edi"),"edx");
+
+	&ret	();
+
+&set_label("mul_mont_ialu",16);			}
+
+	########################################
+	# IALU code path suitable for all CPUs.
+	########################################
+	# stack layout:
+	# +------------------------------------+< %esp
+	# | 8 32-bit temporary words, accessed |
+	# | as circular buffer                 |
+	# .                                    .
+	# .                                    .
+	# +------------------------------------+< +32
+	# | offloaded destination pointer      |
+	# +------------------------------------+
+	# | unused                             |
+	# +------------------------------------+< +40
+	&sub	("esp",10*4);
+
+	&mov	("eax",&DWP(0*4,"esi"));		# a[0]
+	&mov	("ebx",&DWP(0*4,"ebp"));		# b[0]
+	&mov	(&DWP(8*4,"esp"),"edi");		# off-load dst ptr
+
+	&mul	("ebx");				# a[0]*b[0]
+	&mov	(&DWP(0*4,"esp"),"eax");		# t[0]
+	&mov	("eax",&DWP(1*4,"esi"));
+	&mov	("ecx","edx")
+
+	&mul	("ebx");				# a[1]*b[0]
+	&add	("ecx","eax");
+	&mov	("eax",&DWP(2*4,"esi"));
+	&adc	("edx",0);
+	&mov	(&DWP(1*4,"esp"),"ecx");		# t[1]
+	&mov	("ecx","edx");
+
+	&mul	("ebx");				# a[2]*b[0]
+	&add	("ecx","eax");
+	&mov	("eax",&DWP(3*4,"esi"));
+	&adc	("edx",0);
+	&mov	(&DWP(2*4,"esp"),"ecx");		# t[2]
+	&mov	("ecx","edx");
+
+	&mul	("ebx");				# a[3]*b[0]
+	&add	("ecx","eax");
+	&mov	("eax",&DWP(4*4,"esi"));
+	&adc	("edx",0);
+	&mov	(&DWP(3*4,"esp"),"ecx");		# t[3]
+	&mov	("ecx","edx");
+
+	&mul	("ebx");				# a[4]*b[0]
+	&add	("ecx","eax");
+	&mov	("eax",&DWP(5*4,"esi"));
+	&adc	("edx",0);
+	&mov	(&DWP(4*4,"esp"),"ecx");		# t[4]
+	&mov	("ecx","edx");
+
+	&mul	("ebx");				# a[5]*b[0]
+	&add	("ecx","eax");
+	&mov	("eax",&DWP(6*4,"esi"));
+	&adc	("edx",0);
+	&mov	(&DWP(5*4,"esp"),"ecx");		# t[5]
+	&mov	("ecx","edx");
+
+	&mul	("ebx");				# a[6]*b[0]
+	&add	("ecx","eax");
+	&mov	("eax",&DWP(7*4,"esi"));
+	&adc	("edx",0);
+	&mov	(&DWP(6*4,"esp"),"ecx");		# t[6]
+	&mov	("ecx","edx");
+
+	&xor	("edi","edi");				# initial top-most carry
+	&mul	("ebx");				# a[7]*b[0]
+	&add	("ecx","eax");				# t[7]
+	&mov	("eax",&DWP(0*4,"esp"));		# t[0]
+	&adc	("edx",0);				# t[8]
+
+for ($i=0;$i<7;$i++) {
+	my $j=$i+1;
+
+	# Reduction iteration is normally performed by accumulating
+	# result of multiplication of modulus by "magic" digit [and
+	# omitting least significant word, which is guaranteed to
+	# be 0], but thanks to special form of modulus and "magic"
+	# digit being equal to least significant word, it can be
+	# performed with additions and subtractions alone. Indeed:
+	#
+	#        ffff.0001.0000.0000.0000.ffff.ffff.ffff
+	# *                                         abcd
+	# + xxxx.xxxx.xxxx.xxxx.xxxx.xxxx.xxxx.xxxx.abcd
+	#
+	# Now observing that ff..ff*x = (2^n-1)*x = 2^n*x-x, we
+	# rewrite above as:
+	#
+	#   xxxx.xxxx.xxxx.xxxx.xxxx.xxxx.xxxx.xxxx.abcd
+	# + abcd.0000.abcd.0000.0000.abcd.0000.0000.0000
+	# -      abcd.0000.0000.0000.0000.0000.0000.abcd
+	#
+	# or marking redundant operations:
+	#
+	#   xxxx.xxxx.xxxx.xxxx.xxxx.xxxx.xxxx.xxxx.----
+	# + abcd.0000.abcd.0000.0000.abcd.----.----.----
+	# -      abcd.----.----.----.----.----.----.----
+
+	&add	(&DWP((($i+3)%8)*4,"esp"),"eax");	# t[3]+=t[0]
+	&adc	(&DWP((($i+4)%8)*4,"esp"),0);		# t[4]+=0
+	&adc	(&DWP((($i+5)%8)*4,"esp"),0);		# t[5]+=0
+	&adc	(&DWP((($i+6)%8)*4,"esp"),"eax");	# t[6]+=t[0]
+	&adc	("ecx",0);				# t[7]+=0
+	&adc	("edx","eax");				# t[8]+=t[0]
+	&adc	("edi",0);				# top-most carry
+	 &mov	("ebx",&DWP($j*4,"ebp"));		# b[i]
+	&sub	("ecx","eax");				# t[7]-=t[0]
+	 &mov	("eax",&DWP(0*4,"esi"));		# a[0]
+	&sbb	("edx",0);				# t[8]-=0
+	&mov	(&DWP((($i+7)%8)*4,"esp"),"ecx");
+	&sbb	("edi",0);				# top-most carry,
+							# keep in mind that
+							# netto result is
+							# *addition* of value
+							# with (abcd<<32)-abcd
+							# on top, so that
+							# underflow is
+							# impossible, because
+							# (abcd<<32)-abcd
+							# doesn't underflow
+	&mov	(&DWP((($i+8)%8)*4,"esp"),"edx");
+
+	&mul	("ebx");				# a[0]*b[i]
+	&add	("eax",&DWP((($j+0)%8)*4,"esp"));
+	&adc	("edx",0);
+	&mov	(&DWP((($j+0)%8)*4,"esp"),"eax");
+	&mov	("eax",&DWP(1*4,"esi"));
+	&mov	("ecx","edx")
+
+	&mul	("ebx");				# a[1]*b[i]
+	&add	("ecx",&DWP((($j+1)%8)*4,"esp"));
+	&adc	("edx",0);
+	&add	("ecx","eax");
+	&adc	("edx",0);
+	&mov	("eax",&DWP(2*4,"esi"));
+	&mov	(&DWP((($j+1)%8)*4,"esp"),"ecx");
+	&mov	("ecx","edx");
+
+	&mul	("ebx");				# a[2]*b[i]
+	&add	("ecx",&DWP((($j+2)%8)*4,"esp"));
+	&adc	("edx",0);
+	&add	("ecx","eax");
+	&adc	("edx",0);
+	&mov	("eax",&DWP(3*4,"esi"));
+	&mov	(&DWP((($j+2)%8)*4,"esp"),"ecx");
+	&mov	("ecx","edx");
+
+	&mul	("ebx");				# a[3]*b[i]
+	&add	("ecx",&DWP((($j+3)%8)*4,"esp"));
+	&adc	("edx",0);
+	&add	("ecx","eax");
+	&adc	("edx",0);
+	&mov	("eax",&DWP(4*4,"esi"));
+	&mov	(&DWP((($j+3)%8)*4,"esp"),"ecx");
+	&mov	("ecx","edx");
+
+	&mul	("ebx");				# a[4]*b[i]
+	&add	("ecx",&DWP((($j+4)%8)*4,"esp"));
+	&adc	("edx",0);
+	&add	("ecx","eax");
+	&adc	("edx",0);
+	&mov	("eax",&DWP(5*4,"esi"));
+	&mov	(&DWP((($j+4)%8)*4,"esp"),"ecx");
+	&mov	("ecx","edx");
+
+	&mul	("ebx");				# a[5]*b[i]
+	&add	("ecx",&DWP((($j+5)%8)*4,"esp"));
+	&adc	("edx",0);
+	&add	("ecx","eax");
+	&adc	("edx",0);
+	&mov	("eax",&DWP(6*4,"esi"));
+	&mov	(&DWP((($j+5)%8)*4,"esp"),"ecx");
+	&mov	("ecx","edx");
+
+	&mul	("ebx");				# a[6]*b[i]
+	&add	("ecx",&DWP((($j+6)%8)*4,"esp"));
+	&adc	("edx",0);
+	&add	("ecx","eax");
+	&adc	("edx",0);
+	&mov	("eax",&DWP(7*4,"esi"));
+	&mov	(&DWP((($j+6)%8)*4,"esp"),"ecx");
+	&mov	("ecx","edx");
+
+	&mul	("ebx");				# a[7]*b[i]
+	&add	("ecx",&DWP((($j+7)%8)*4,"esp"));
+	&adc	("edx",0);
+	&add	("ecx","eax");				# t[7]
+	&mov	("eax",&DWP((($j+0)%8)*4,"esp"));	# t[0]
+	&adc	("edx","edi");				# t[8]
+	&mov	("edi",0);
+	&adc	("edi",0);				# top-most carry
+}
+	&mov	("ebp",&DWP(8*4,"esp"));		# restore dst ptr
+	&xor	("esi","esi");
+	my $j=$i+1;
+
+	# last multiplication-less reduction
+	&add	(&DWP((($i+3)%8)*4,"esp"),"eax");	# t[3]+=t[0]
+	&adc	(&DWP((($i+4)%8)*4,"esp"),0);		# t[4]+=0
+	&adc	(&DWP((($i+5)%8)*4,"esp"),0);		# t[5]+=0
+	&adc	(&DWP((($i+6)%8)*4,"esp"),"eax");	# t[6]+=t[0]
+	&adc	("ecx",0);				# t[7]+=0
+	&adc	("edx","eax");				# t[8]+=t[0]
+	&adc	("edi",0);				# top-most carry
+	 &mov	("ebx",&DWP((($j+1)%8)*4,"esp"));
+	&sub	("ecx","eax");				# t[7]-=t[0]
+	 &mov	("eax",&DWP((($j+0)%8)*4,"esp"));
+	&sbb	("edx",0);				# t[8]-=0
+	&mov	(&DWP((($i+7)%8)*4,"esp"),"ecx");
+	&sbb	("edi",0);				# top-most carry
+	&mov	(&DWP((($i+8)%8)*4,"esp"),"edx");
+
+	# Final step is "if result > mod, subtract mod", but we do it
+	# "other way around", namely write result - mod to output buffer
+	# and if subtraction borrowed, add modulus back.
+
+	&mov	("ecx",&DWP((($j+2)%8)*4,"esp"));
+	&sub	("eax",-1);
+	&mov	("edx",&DWP((($j+3)%8)*4,"esp"));
+	&sbb	("ebx",-1);
+	&mov	(&DWP(0*4,"ebp"),"eax");
+	&sbb	("ecx",-1);
+	&mov	(&DWP(1*4,"ebp"),"ebx");
+	&sbb	("edx",0);
+	&mov	(&DWP(2*4,"ebp"),"ecx");
+	&mov	(&DWP(3*4,"ebp"),"edx");
+
+	&mov	("eax",&DWP((($j+4)%8)*4,"esp"));
+	&mov	("ebx",&DWP((($j+5)%8)*4,"esp"));
+	&mov	("ecx",&DWP((($j+6)%8)*4,"esp"));
+	&sbb	("eax",0);
+	&mov	("edx",&DWP((($j+7)%8)*4,"esp"));
+	&sbb	("ebx",0);
+	&sbb	("ecx",1);
+	&sbb	("edx",-1);
+	&sbb	("edi",0);
+
+	# Note that because mod has special form, i.e. consists of
+	# 0xffffffff, 1 and 0s, we can conditionally synthesize it by
+	# assigning borrow bit to one register, %ebp, and its negative
+	# to another, %esi. But we started by calculating %esi...
+
+	&sub	("esi","edi");
+	&add	(&DWP(0*4,"ebp"),"edi");		# add modulus or zero
+	&adc	(&DWP(1*4,"ebp"),"edi");
+	&adc	(&DWP(2*4,"ebp"),"edi");
+	&adc	(&DWP(3*4,"ebp"),0);
+	&adc	("eax",0);
+	&adc	("ebx",0);
+	&mov	(&DWP(4*4,"ebp"),"eax");
+	&adc	("ecx","esi");
+	&mov	(&DWP(5*4,"ebp"),"ebx");
+	&adc	("edx","edi");
+	&mov	(&DWP(6*4,"ebp"),"ecx");
+	&mov	("edi","ebp");				# fulfill contract
+	&mov	(&DWP(7*4,"ebp"),"edx");
+
+	&add	("esp",10*4);
+	&ret	();
+&function_end_B("_ecp_nistz256_mul_mont");
+
+########################################################################
+# void ecp_nistz256_scatter_w5(void *edi,const P256_POINT *esi,
+#					 int ebp);
+&function_begin("ecp_nistz256_scatter_w5");
+	&mov	("edi",&wparam(0));
+	&mov	("esi",&wparam(1));
+	&mov	("ebp",&wparam(2));
+
+	&lea	("edi",&DWP(128-4,"edi","ebp",4));
+	&mov	("ebp",96/16);
+&set_label("scatter_w5_loop");
+	&mov	("eax",&DWP(0,"esi"));
+	&mov	("ebx",&DWP(4,"esi"));
+	&mov	("ecx",&DWP(8,"esi"));
+	&mov	("edx",&DWP(12,"esi"));
+	&lea	("esi",&DWP(16,"esi"));
+	&mov	(&DWP(64*0-128,"edi"),"eax");
+	&mov	(&DWP(64*1-128,"edi"),"ebx");
+	&mov	(&DWP(64*2-128,"edi"),"ecx");
+	&mov	(&DWP(64*3-128,"edi"),"edx");
+	&lea	("edi",&DWP(64*4,"edi"));
+	&dec	("ebp");
+	&jnz	(&label("scatter_w5_loop"));
+&function_end("ecp_nistz256_scatter_w5");
+
+########################################################################
+# void ecp_nistz256_gather_w5(P256_POINT *edi,const void *esi,
+#					      int ebp);
+&function_begin("ecp_nistz256_gather_w5");
+	&mov	("esi",&wparam(1));
+	&mov	("ebp",&wparam(2));
+
+	&lea	("esi",&DWP(0,"esi","ebp",4));
+	&neg	("ebp");
+	&sar	("ebp",31);
+	&mov	("edi",&wparam(0));
+	&lea	("esi",&DWP(0,"esi","ebp",4));
+
+    for($i=0;$i<24;$i+=4) {
+	&mov	("eax",&DWP(64*($i+0),"esi"));
+	&mov	("ebx",&DWP(64*($i+1),"esi"));
+	&mov	("ecx",&DWP(64*($i+2),"esi"));
+	&mov	("edx",&DWP(64*($i+3),"esi"));
+	&and	("eax","ebp");
+	&and	("ebx","ebp");
+	&and	("ecx","ebp");
+	&and	("edx","ebp");
+	&mov	(&DWP(4*($i+0),"edi"),"eax");
+	&mov	(&DWP(4*($i+1),"edi"),"ebx");
+	&mov	(&DWP(4*($i+2),"edi"),"ecx");
+	&mov	(&DWP(4*($i+3),"edi"),"edx");
+    }
+&function_end("ecp_nistz256_gather_w5");
+
+########################################################################
+# void ecp_nistz256_scatter_w7(void *edi,const P256_POINT_AFFINE *esi,
+#					 int ebp);
+&function_begin("ecp_nistz256_scatter_w7");
+	&mov	("edi",&wparam(0));
+	&mov	("esi",&wparam(1));
+	&mov	("ebp",&wparam(2));
+
+	&lea	("edi",&DWP(-1,"edi","ebp"));
+	&mov	("ebp",64/4);
+&set_label("scatter_w7_loop");
+	&mov	("eax",&DWP(0,"esi"));
+	&lea	("esi",&DWP(4,"esi"));
+	&mov	(&BP(64*0,"edi"),"al");
+	&mov	(&BP(64*1,"edi"),"ah");
+	&shr	("eax",16);
+	&mov	(&BP(64*2,"edi"),"al");
+	&mov	(&BP(64*3,"edi"),"ah");
+	&lea	("edi",&DWP(64*4,"edi"));
+	&dec	("ebp");
+	&jnz	(&label("scatter_w7_loop"));
+&function_end("ecp_nistz256_scatter_w7");
+
+########################################################################
+# void ecp_nistz256_gather_w7(P256_POINT_AFFINE *edi,const void *esi,
+#						     int ebp);
+&function_begin("ecp_nistz256_gather_w7");
+	&mov	("esi",&wparam(1));
+	&mov	("ebp",&wparam(2));
+
+	&add	("esi","ebp");
+	&neg	("ebp"),
+	&sar	("ebp",31);
+	&mov	("edi",&wparam(0));
+	&lea	("esi",&DWP(0,"esi","ebp"));
+
+    for($i=0;$i<64;$i+=4) {
+	&movz	("eax",&BP(64*($i+0),"esi"));
+	&movz	("ebx",&BP(64*($i+1),"esi"));
+	&movz	("ecx",&BP(64*($i+2),"esi"));
+	&and	("eax","ebp");
+	&movz	("edx",&BP(64*($i+3),"esi"));
+	&and	("ebx","ebp");
+	&mov	(&BP($i+0,"edi"),"al");
+	&and	("ecx","ebp");
+	&mov	(&BP($i+1,"edi"),"bl");
+	&and	("edx","ebp");
+	&mov	(&BP($i+2,"edi"),"cl");
+	&mov	(&BP($i+3,"edi"),"dl");
+    }
+&function_end("ecp_nistz256_gather_w7");
+
+########################################################################
+# following subroutines are "literal" implementation of those found in
+# ecp_nistz256.c
+#
+########################################################################
+# void ecp_nistz256_point_double(P256_POINT *out,const P256_POINT *inp);
+#
+&static_label("point_double_shortcut");
+&function_begin("ecp_nistz256_point_double");
+{   my ($S,$M,$Zsqr,$in_x,$tmp0)=map(32*$_,(0..4));
+
+	&mov	("esi",&wparam(1));
+
+	# above map() describes stack layout with 5 temporary
+	# 256-bit vectors on top, then we take extra word for
+	# OPENSSL_ia32cap_P copy.
+	&stack_push(8*5+1);
+						if ($sse2) {
+	&call	("_picup_eax");
+    &set_label("pic");
+	&picmeup("edx","OPENSSL_ia32cap_P","eax",&label("pic"));
+	&mov	("ebp",&DWP(0,"edx"));		}
+
+&set_label("point_double_shortcut");
+	&mov	("eax",&DWP(0,"esi"));		# copy in_x
+	&mov	("ebx",&DWP(4,"esi"));
+	&mov	("ecx",&DWP(8,"esi"));
+	&mov	("edx",&DWP(12,"esi"));
+	&mov	(&DWP($in_x+0,"esp"),"eax");
+	&mov	(&DWP($in_x+4,"esp"),"ebx");
+	&mov	(&DWP($in_x+8,"esp"),"ecx");
+	&mov	(&DWP($in_x+12,"esp"),"edx");
+	&mov	("eax",&DWP(16,"esi"));
+	&mov	("ebx",&DWP(20,"esi"));
+	&mov	("ecx",&DWP(24,"esi"));
+	&mov	("edx",&DWP(28,"esi"));
+	&mov	(&DWP($in_x+16,"esp"),"eax");
+	&mov	(&DWP($in_x+20,"esp"),"ebx");
+	&mov	(&DWP($in_x+24,"esp"),"ecx");
+	&mov	(&DWP($in_x+28,"esp"),"edx");
+	&mov	(&DWP(32*5,"esp"),"ebp");	# OPENSSL_ia32cap_P copy
+
+	&lea	("ebp",&DWP(32,"esi"));
+	&lea	("esi",&DWP(32,"esi"));
+	&lea	("edi",&DWP($S,"esp"));
+	&call	("_ecp_nistz256_add");		# p256_mul_by_2(S, in_y);
+
+	&mov	("eax",&DWP(32*5,"esp"));	# OPENSSL_ia32cap_P copy
+	&mov	("esi",64);
+	&add	("esi",&wparam(1));
+	&lea	("edi",&DWP($Zsqr,"esp"));
+	&mov	("ebp","esi");
+	&call	("_ecp_nistz256_mul_mont");	# p256_sqr_mont(Zsqr, in_z);
+
+	&mov	("eax",&DWP(32*5,"esp"));	# OPENSSL_ia32cap_P copy
+	&lea	("esi",&DWP($S,"esp"));
+	&lea	("ebp",&DWP($S,"esp"));
+	&lea	("edi",&DWP($S,"esp"));
+	&call	("_ecp_nistz256_mul_mont");	# p256_sqr_mont(S, S);
+
+	&mov	("eax",&DWP(32*5,"esp"));	# OPENSSL_ia32cap_P copy
+	&mov	("ebp",&wparam(1));
+	&lea	("esi",&DWP(32,"ebp"));
+	&lea	("ebp",&DWP(64,"ebp"));
+	&lea	("edi",&DWP($tmp0,"esp"));
+	&call	("_ecp_nistz256_mul_mont");	# p256_mul_mont(tmp0, in_z, in_y);
+
+	&lea	("esi",&DWP($in_x,"esp"));
+	&lea	("ebp",&DWP($Zsqr,"esp"));
+	&lea	("edi",&DWP($M,"esp"));
+	&call	("_ecp_nistz256_add");		# p256_add(M, in_x, Zsqr);
+
+	&mov	("edi",64);
+	&lea	("esi",&DWP($tmp0,"esp"));
+	&lea	("ebp",&DWP($tmp0,"esp"));
+	&add	("edi",&wparam(0));
+	&call	("_ecp_nistz256_add");		# p256_mul_by_2(res_z, tmp0);
+
+	&lea	("esi",&DWP($in_x,"esp"));
+	&lea	("ebp",&DWP($Zsqr,"esp"));
+	&lea	("edi",&DWP($Zsqr,"esp"));
+	&call	("_ecp_nistz256_sub");		# p256_sub(Zsqr, in_x, Zsqr);
+
+	&mov	("eax",&DWP(32*5,"esp"));	# OPENSSL_ia32cap_P copy
+	&lea	("esi",&DWP($S,"esp"));
+	&lea	("ebp",&DWP($S,"esp"));
+	&lea	("edi",&DWP($tmp0,"esp"));
+	&call	("_ecp_nistz256_mul_mont");	# p256_sqr_mont(tmp0, S);
+
+	&mov	("eax",&DWP(32*5,"esp"));	# OPENSSL_ia32cap_P copy
+	&lea	("esi",&DWP($M,"esp"));
+	&lea	("ebp",&DWP($Zsqr,"esp"));
+	&lea	("edi",&DWP($M,"esp"));
+	&call	("_ecp_nistz256_mul_mont");	# p256_mul_mont(M, M, Zsqr);
+
+	&mov	("edi",32);
+	&lea	("esi",&DWP($tmp0,"esp"));
+	&add	("edi",&wparam(0));
+	&call	("_ecp_nistz256_div_by_2");	# p256_div_by_2(res_y, tmp0);
+
+	&lea	("esi",&DWP($M,"esp"));
+	&lea	("ebp",&DWP($M,"esp"));
+	&lea	("edi",&DWP($tmp0,"esp"));
+	&call	("_ecp_nistz256_add");		# 1/2 p256_mul_by_3(M, M);
+
+	&mov	("eax",&DWP(32*5,"esp"));	# OPENSSL_ia32cap_P copy
+	&lea	("esi",&DWP($in_x,"esp"));
+	&lea	("ebp",&DWP($S,"esp"));
+	&lea	("edi",&DWP($S,"esp"));
+	&call	("_ecp_nistz256_mul_mont");	# p256_mul_mont(S, S, in_x);
+
+	&lea	("esi",&DWP($tmp0,"esp"));
+	&lea	("ebp",&DWP($M,"esp"));
+	&lea	("edi",&DWP($M,"esp"));
+	&call	("_ecp_nistz256_add");		# 2/2 p256_mul_by_3(M, M);
+
+	&lea	("esi",&DWP($S,"esp"));
+	&lea	("ebp",&DWP($S,"esp"));
+	&lea	("edi",&DWP($tmp0,"esp"));
+	&call	("_ecp_nistz256_add");		# p256_mul_by_2(tmp0, S);
+
+	&mov	("eax",&DWP(32*5,"esp"));	# OPENSSL_ia32cap_P copy
+	&lea	("esi",&DWP($M,"esp"));
+	&lea	("ebp",&DWP($M,"esp"));
+	&mov	("edi",&wparam(0));
+	&call	("_ecp_nistz256_mul_mont");	# p256_sqr_mont(res_x, M);
+
+	&mov	("esi","edi");			# %edi is still res_x here
+	&lea	("ebp",&DWP($tmp0,"esp"));
+	&call	("_ecp_nistz256_sub");		# p256_sub(res_x, res_x, tmp0);
+
+	&lea	("esi",&DWP($S,"esp"));
+	&mov	("ebp","edi");			# %edi is still res_x
+	&lea	("edi",&DWP($S,"esp"));
+	&call	("_ecp_nistz256_sub");		# p256_sub(S, S, res_x);
+
+	&mov	("eax",&DWP(32*5,"esp"));	# OPENSSL_ia32cap_P copy
+	&mov	("esi","edi");			# %edi is still &S
+	&lea	("ebp",&DWP($M,"esp"));
+	&call	("_ecp_nistz256_mul_mont");	# p256_mul_mont(S, S, M);
+
+	&mov	("ebp",32);
+	&lea	("esi",&DWP($S,"esp"));
+	&add	("ebp",&wparam(0));
+	&mov	("edi","ebp");
+	&call	("_ecp_nistz256_sub");		# p256_sub(res_y, S, res_y);
+
+	&stack_pop(8*5+1);
+} &function_end("ecp_nistz256_point_double");
+
+########################################################################
+# void ecp_nistz256_point_add(P256_POINT *out,const P256_POINT *in1,
+#					      const P256_POINT *in2);
+&function_begin("ecp_nistz256_point_add");
+{   my ($res_x,$res_y,$res_z,
+	$in1_x,$in1_y,$in1_z,
+	$in2_x,$in2_y,$in2_z,
+	$H,$Hsqr,$R,$Rsqr,$Hcub,
+	$U1,$U2,$S1,$S2)=map(32*$_,(0..17));
+    my ($Z1sqr, $Z2sqr) = ($Hsqr, $Rsqr);
+
+	&mov	("esi",&wparam(2));
+
+	# above map() describes stack layout with 18 temporary
+	# 256-bit vectors on top, then we take extra words for
+	# !in1infty, !in2infty, result of check for zero and
+	# OPENSSL_ia32cap_P copy. [one unused word for padding]
+	&stack_push(8*18+5);
+						if ($sse2) {
+	&call	("_picup_eax");
+    &set_label("pic");
+	&picmeup("edx","OPENSSL_ia32cap_P","eax",&label("pic"));
+	&mov	("ebp",&DWP(0,"edx"));		}
+
+	&lea	("edi",&DWP($in2_x,"esp"));
+    for($i=0;$i<96;$i+=16) {
+	&mov	("eax",&DWP($i+0,"esi"));	# copy in2
+	&mov	("ebx",&DWP($i+4,"esi"));
+	&mov	("ecx",&DWP($i+8,"esi"));
+	&mov	("edx",&DWP($i+12,"esi"));
+	&mov	(&DWP($i+0,"edi"),"eax");
+	&mov	(&DWP(32*18+12,"esp"),"ebp")	if ($i==0);
+	&mov	("ebp","eax")			if ($i==64);
+	&or	("ebp","eax")			if ($i>64);
+	&mov	(&DWP($i+4,"edi"),"ebx");
+	&or	("ebp","ebx")			if ($i>=64);
+	&mov	(&DWP($i+8,"edi"),"ecx");
+	&or	("ebp","ecx")			if ($i>=64);
+	&mov	(&DWP($i+12,"edi"),"edx");
+	&or	("ebp","edx")			if ($i>=64);
+    }
+	&xor	("eax","eax");
+	&mov	("esi",&wparam(1));
+	&sub	("eax","ebp");
+	&or	("ebp","eax");
+	&sar	("ebp",31);
+	&mov	(&DWP(32*18+4,"esp"),"ebp");	# !in2infty
+
+	&lea	("edi",&DWP($in1_x,"esp"));
+    for($i=0;$i<96;$i+=16) {
+	&mov	("eax",&DWP($i+0,"esi"));	# copy in1
+	&mov	("ebx",&DWP($i+4,"esi"));
+	&mov	("ecx",&DWP($i+8,"esi"));
+	&mov	("edx",&DWP($i+12,"esi"));
+	&mov	(&DWP($i+0,"edi"),"eax");
+	&mov	("ebp","eax")			if ($i==64);
+	&or	("ebp","eax")			if ($i>64);
+	&mov	(&DWP($i+4,"edi"),"ebx");
+	&or	("ebp","ebx")			if ($i>=64);
+	&mov	(&DWP($i+8,"edi"),"ecx");
+	&or	("ebp","ecx")			if ($i>=64);
+	&mov	(&DWP($i+12,"edi"),"edx");
+	&or	("ebp","edx")			if ($i>=64);
+    }
+	&xor	("eax","eax");
+	&sub	("eax","ebp");
+	&or	("ebp","eax");
+	&sar	("ebp",31);
+	&mov	(&DWP(32*18+0,"esp"),"ebp");	# !in1infty
+
+	&mov	("eax",&DWP(32*18+12,"esp"));	# OPENSSL_ia32cap_P copy
+	&lea	("esi",&DWP($in2_z,"esp"));
+	&lea	("ebp",&DWP($in2_z,"esp"));
+	&lea	("edi",&DWP($Z2sqr,"esp"));
+	&call	("_ecp_nistz256_mul_mont");	# p256_sqr_mont(Z2sqr, in2_z);
+
+	&mov	("eax",&DWP(32*18+12,"esp"));	# OPENSSL_ia32cap_P copy
+	&lea	("esi",&DWP($in1_z,"esp"));
+	&lea	("ebp",&DWP($in1_z,"esp"));
+	&lea	("edi",&DWP($Z1sqr,"esp"));
+	&call	("_ecp_nistz256_mul_mont");	# p256_sqr_mont(Z1sqr, in1_z);
+
+	&mov	("eax",&DWP(32*18+12,"esp"));	# OPENSSL_ia32cap_P copy
+	&lea	("esi",&DWP($Z2sqr,"esp"));
+	&lea	("ebp",&DWP($in2_z,"esp"));
+	&lea	("edi",&DWP($S1,"esp"));
+	&call	("_ecp_nistz256_mul_mont");	# p256_mul_mont(S1, Z2sqr, in2_z);
+
+	&mov	("eax",&DWP(32*18+12,"esp"));	# OPENSSL_ia32cap_P copy
+	&lea	("esi",&DWP($Z1sqr,"esp"));
+	&lea	("ebp",&DWP($in1_z,"esp"));
+	&lea	("edi",&DWP($S2,"esp"));
+	&call	("_ecp_nistz256_mul_mont");	# p256_mul_mont(S2, Z1sqr, in1_z);
+
+	&mov	("eax",&DWP(32*18+12,"esp"));	# OPENSSL_ia32cap_P copy
+	&lea	("esi",&DWP($in1_y,"esp"));
+	&lea	("ebp",&DWP($S1,"esp"));
+	&lea	("edi",&DWP($S1,"esp"));
+	&call	("_ecp_nistz256_mul_mont");	# p256_mul_mont(S1, S1, in1_y);
+
+	&mov	("eax",&DWP(32*18+12,"esp"));	# OPENSSL_ia32cap_P copy
+	&lea	("esi",&DWP($in2_y,"esp"));
+	&lea	("ebp",&DWP($S2,"esp"));
+	&lea	("edi",&DWP($S2,"esp"));
+	&call	("_ecp_nistz256_mul_mont");	# p256_mul_mont(S2, S2, in2_y);
+
+	&lea	("esi",&DWP($S2,"esp"));
+	&lea	("ebp",&DWP($S1,"esp"));
+	&lea	("edi",&DWP($R,"esp"));
+	&call	("_ecp_nistz256_sub");		# p256_sub(R, S2, S1);
+
+	&or	("ebx","eax");			# see if result is zero
+	&mov	("eax",&DWP(32*18+12,"esp"));	# OPENSSL_ia32cap_P copy
+	&or	("ebx","ecx");
+	&or	("ebx","edx");
+	&or	("ebx",&DWP(0,"edi"));
+	&or	("ebx",&DWP(4,"edi"));
+	 &lea	("esi",&DWP($in1_x,"esp"));
+	&or	("ebx",&DWP(8,"edi"));
+	 &lea	("ebp",&DWP($Z2sqr,"esp"));
+	&or	("ebx",&DWP(12,"edi"));
+	 &lea	("edi",&DWP($U1,"esp"));
+	&mov	(&DWP(32*18+8,"esp"),"ebx");
+
+	&call	("_ecp_nistz256_mul_mont");	# p256_mul_mont(U1, in1_x, Z2sqr);
+
+	&mov	("eax",&DWP(32*18+12,"esp"));	# OPENSSL_ia32cap_P copy
+	&lea	("esi",&DWP($in2_x,"esp"));
+	&lea	("ebp",&DWP($Z1sqr,"esp"));
+	&lea	("edi",&DWP($U2,"esp"));
+	&call	("_ecp_nistz256_mul_mont");	# p256_mul_mont(U2, in2_x, Z1sqr);
+
+	&lea	("esi",&DWP($U2,"esp"));
+	&lea	("ebp",&DWP($U1,"esp"));
+	&lea	("edi",&DWP($H,"esp"));
+	&call	("_ecp_nistz256_sub");		# p256_sub(H, U2, U1);
+
+	&or	("eax","ebx");			# see if result is zero
+	&or	("eax","ecx");
+	&or	("eax","edx");
+	&or	("eax",&DWP(0,"edi"));
+	&or	("eax",&DWP(4,"edi"));
+	&or	("eax",&DWP(8,"edi"));
+	&or	("eax",&DWP(12,"edi"));
+
+	&data_byte(0x3e);			# predict taken
+	&jnz	(&label("add_proceed"));	# is_equal(U1,U2)?
+
+	&mov	("eax",&DWP(32*18+0,"esp"));
+	&and	("eax",&DWP(32*18+4,"esp"));
+	&mov	("ebx",&DWP(32*18+8,"esp"));
+	&jz	(&label("add_proceed"));	# (in1infty || in2infty)?
+	&test	("ebx","ebx");
+	&jz	(&label("add_double"));		# is_equal(S1,S2)?
+
+	&mov	("edi",&wparam(0));
+	&xor	("eax","eax");
+	&mov	("ecx",96/4);
+	&data_byte(0xfc,0xf3,0xab);		# cld; stosd
+	&jmp	(&label("add_done"));
+
+&set_label("add_double",16);
+	&mov	("esi",&wparam(1));
+	&mov	("ebp",&DWP(32*18+12,"esp"));	# OPENSSL_ia32cap_P copy
+	&add	("esp",4*((8*18+5)-(8*5+1)));	# difference in frame sizes
+	&jmp	(&label("point_double_shortcut"));
+
+&set_label("add_proceed",16);
+	&mov	("eax",&DWP(32*18+12,"esp"));	# OPENSSL_ia32cap_P copy
+	&lea	("esi",&DWP($R,"esp"));
+	&lea	("ebp",&DWP($R,"esp"));
+	&lea	("edi",&DWP($Rsqr,"esp"));
+	&call	("_ecp_nistz256_mul_mont");	# p256_sqr_mont(Rsqr, R);
+
+	&mov	("eax",&DWP(32*18+12,"esp"));	# OPENSSL_ia32cap_P copy
+	&lea	("esi",&DWP($H,"esp"));
+	&lea	("ebp",&DWP($in1_z,"esp"));
+	&lea	("edi",&DWP($res_z,"esp"));
+	&call	("_ecp_nistz256_mul_mont");	# p256_mul_mont(res_z, H, in1_z);
+
+	&mov	("eax",&DWP(32*18+12,"esp"));	# OPENSSL_ia32cap_P copy
+	&lea	("esi",&DWP($H,"esp"));
+	&lea	("ebp",&DWP($H,"esp"));
+	&lea	("edi",&DWP($Hsqr,"esp"));
+	&call	("_ecp_nistz256_mul_mont");	# p256_sqr_mont(Hsqr, H);
+
+	&mov	("eax",&DWP(32*18+12,"esp"));	# OPENSSL_ia32cap_P copy
+	&lea	("esi",&DWP($in2_z,"esp"));
+	&lea	("ebp",&DWP($res_z,"esp"));
+	&lea	("edi",&DWP($res_z,"esp"));
+	&call	("_ecp_nistz256_mul_mont");	# p256_mul_mont(res_z, res_z, in2_z);
+
+	&mov	("eax",&DWP(32*18+12,"esp"));	# OPENSSL_ia32cap_P copy
+	&lea	("esi",&DWP($Hsqr,"esp"));
+	&lea	("ebp",&DWP($U1,"esp"));
+	&lea	("edi",&DWP($U2,"esp"));
+	&call	("_ecp_nistz256_mul_mont");	# p256_mul_mont(U2, U1, Hsqr);
+
+	&mov	("eax",&DWP(32*18+12,"esp"));	# OPENSSL_ia32cap_P copy
+	&lea	("esi",&DWP($H,"esp"));
+	&lea	("ebp",&DWP($Hsqr,"esp"));
+	&lea	("edi",&DWP($Hcub,"esp"));
+	&call	("_ecp_nistz256_mul_mont");	# p256_mul_mont(Hcub, Hsqr, H);
+
+	&lea	("esi",&DWP($U2,"esp"));
+	&lea	("ebp",&DWP($U2,"esp"));
+	&lea	("edi",&DWP($Hsqr,"esp"));
+	&call	("_ecp_nistz256_add");		# p256_mul_by_2(Hsqr, U2);
+
+	&lea	("esi",&DWP($Rsqr,"esp"));
+	&lea	("ebp",&DWP($Hsqr,"esp"));
+	&lea	("edi",&DWP($res_x,"esp"));
+	&call	("_ecp_nistz256_sub");		# p256_sub(res_x, Rsqr, Hsqr);
+
+	&lea	("esi",&DWP($res_x,"esp"));
+	&lea	("ebp",&DWP($Hcub,"esp"));
+	&lea	("edi",&DWP($res_x,"esp"));
+	&call	("_ecp_nistz256_sub");		# p256_sub(res_x, res_x, Hcub);
+
+	&lea	("esi",&DWP($U2,"esp"));
+	&lea	("ebp",&DWP($res_x,"esp"));
+	&lea	("edi",&DWP($res_y,"esp"));
+	&call	("_ecp_nistz256_sub");		# p256_sub(res_y, U2, res_x);
+
+	&mov	("eax",&DWP(32*18+12,"esp"));	# OPENSSL_ia32cap_P copy
+	&lea	("esi",&DWP($Hcub,"esp"));
+	&lea	("ebp",&DWP($S1,"esp"));
+	&lea	("edi",&DWP($S2,"esp"));
+	&call	("_ecp_nistz256_mul_mont");	# p256_mul_mont(S2, S1, Hcub);
+
+	&mov	("eax",&DWP(32*18+12,"esp"));	# OPENSSL_ia32cap_P copy
+	&lea	("esi",&DWP($R,"esp"));
+	&lea	("ebp",&DWP($res_y,"esp"));
+	&lea	("edi",&DWP($res_y,"esp"));
+	&call	("_ecp_nistz256_mul_mont");	# p256_mul_mont(res_y, R, res_y);
+
+	&lea	("esi",&DWP($res_y,"esp"));
+	&lea	("ebp",&DWP($S2,"esp"));
+	&lea	("edi",&DWP($res_y,"esp"));
+	&call	("_ecp_nistz256_sub");		# p256_sub(res_y, res_y, S2);
+
+	&mov	("ebp",&DWP(32*18+0,"esp"));	# !in1infty
+	&mov	("esi",&DWP(32*18+4,"esp"));	# !in2infty
+	&mov	("edi",&wparam(0));
+	&mov	("edx","ebp");
+	¬	("ebp");
+	&and	("edx","esi");
+	&and	("ebp","esi");
+	¬	("esi");
+
+	########################################
+	# conditional moves
+    for($i=64;$i<96;$i+=4) {
+	&mov	("eax","edx");
+	&and	("eax",&DWP($res_x+$i,"esp"));
+	&mov	("ebx","ebp");
+	&and	("ebx",&DWP($in2_x+$i,"esp"));
+	&mov	("ecx","esi");
+	&and	("ecx",&DWP($in1_x+$i,"esp"));
+	&or	("eax","ebx");
+	&or	("eax","ecx");
+	&mov	(&DWP($i,"edi"),"eax");
+    }
+    for($i=0;$i<64;$i+=4) {
+	&mov	("eax","edx");
+	&and	("eax",&DWP($res_x+$i,"esp"));
+	&mov	("ebx","ebp");
+	&and	("ebx",&DWP($in2_x+$i,"esp"));
+	&mov	("ecx","esi");
+	&and	("ecx",&DWP($in1_x+$i,"esp"));
+	&or	("eax","ebx");
+	&or	("eax","ecx");
+	&mov	(&DWP($i,"edi"),"eax");
+    }
+    &set_label("add_done");
+	&stack_pop(8*18+5);
+} &function_end("ecp_nistz256_point_add");
+
+########################################################################
+# void ecp_nistz256_point_add_affine(P256_POINT *out,
+#				     const P256_POINT *in1,
+#				     const P256_POINT_AFFINE *in2);
+&function_begin("ecp_nistz256_point_add_affine");
+{
+    my ($res_x,$res_y,$res_z,
+	$in1_x,$in1_y,$in1_z,
+	$in2_x,$in2_y,
+	$U2,$S2,$H,$R,$Hsqr,$Hcub,$Rsqr)=map(32*$_,(0..14));
+    my $Z1sqr = $S2;
+    my @ONE_mont=(1,0,0,-1,-1,-1,-2,0);
+
+	&mov	("esi",&wparam(1));
+
+	# above map() describes stack layout with 15 temporary
+	# 256-bit vectors on top, then we take extra words for
+	# !in1infty, !in2infty, and OPENSSL_ia32cap_P copy.
+	&stack_push(8*15+3);
+						if ($sse2) {
+	&call	("_picup_eax");
+    &set_label("pic");
+	&picmeup("edx","OPENSSL_ia32cap_P","eax",&label("pic"));
+	&mov	("ebp",&DWP(0,"edx"));		}
+
+	&lea	("edi",&DWP($in1_x,"esp"));
+    for($i=0;$i<96;$i+=16) {
+	&mov	("eax",&DWP($i+0,"esi"));	# copy in1
+	&mov	("ebx",&DWP($i+4,"esi"));
+	&mov	("ecx",&DWP($i+8,"esi"));
+	&mov	("edx",&DWP($i+12,"esi"));
+	&mov	(&DWP($i+0,"edi"),"eax");
+	&mov	(&DWP(32*15+8,"esp"),"ebp")	if ($i==0);
+	&mov	("ebp","eax")			if ($i==64);
+	&or	("ebp","eax")			if ($i>64);
+	&mov	(&DWP($i+4,"edi"),"ebx");
+	&or	("ebp","ebx")			if ($i>=64);
+	&mov	(&DWP($i+8,"edi"),"ecx");
+	&or	("ebp","ecx")			if ($i>=64);
+	&mov	(&DWP($i+12,"edi"),"edx");
+	&or	("ebp","edx")			if ($i>=64);
+    }
+	&xor	("eax","eax");
+	&mov	("esi",&wparam(2));
+	&sub	("eax","ebp");
+	&or	("ebp","eax");
+	&sar	("ebp",31);
+	&mov	(&DWP(32*15+0,"esp"),"ebp");	# !in1infty
+
+	&lea	("edi",&DWP($in2_x,"esp"));
+    for($i=0;$i<64;$i+=16) {
+	&mov	("eax",&DWP($i+0,"esi"));	# copy in2
+	&mov	("ebx",&DWP($i+4,"esi"));
+	&mov	("ecx",&DWP($i+8,"esi"));
+	&mov	("edx",&DWP($i+12,"esi"));
+	&mov	(&DWP($i+0,"edi"),"eax");
+	&mov	("ebp","eax")			if ($i==0);
+	&or	("ebp","eax")			if ($i!=0);
+	&mov	(&DWP($i+4,"edi"),"ebx");
+	&or	("ebp","ebx");
+	&mov	(&DWP($i+8,"edi"),"ecx");
+	&or	("ebp","ecx");
+	&mov	(&DWP($i+12,"edi"),"edx");
+	&or	("ebp","edx");
+    }
+	&xor	("ebx","ebx");
+	&mov	("eax",&DWP(32*15+8,"esp"));	# OPENSSL_ia32cap_P copy
+	&sub	("ebx","ebp");
+	 &lea	("esi",&DWP($in1_z,"esp"));
+	&or	("ebx","ebp");
+	 &lea	("ebp",&DWP($in1_z,"esp"));
+	&sar	("ebx",31);
+	 &lea	("edi",&DWP($Z1sqr,"esp"));
+	&mov	(&DWP(32*15+4,"esp"),"ebx");	# !in2infty
+
+	&call	("_ecp_nistz256_mul_mont");	# p256_sqr_mont(Z1sqr, in1_z);
+
+	&mov	("eax",&DWP(32*15+8,"esp"));	# OPENSSL_ia32cap_P copy
+	&lea	("esi",&DWP($in2_x,"esp"));
+	&mov	("ebp","edi");			# %esi is stull &Z1sqr
+	&lea	("edi",&DWP($U2,"esp"));
+	&call	("_ecp_nistz256_mul_mont");	# p256_mul_mont(U2, Z1sqr, in2_x);
+
+	&mov	("eax",&DWP(32*15+8,"esp"));	# OPENSSL_ia32cap_P copy
+	&lea	("esi",&DWP($in1_z,"esp"));
+	&lea	("ebp",&DWP($Z1sqr,"esp"));
+	&lea	("edi",&DWP($S2,"esp"));
+	&call	("_ecp_nistz256_mul_mont");	# p256_mul_mont(S2, Z1sqr, in1_z);
+
+	&lea	("esi",&DWP($U2,"esp"));
+	&lea	("ebp",&DWP($in1_x,"esp"));
+	&lea	("edi",&DWP($H,"esp"));
+	&call	("_ecp_nistz256_sub");		# p256_sub(H, U2, in1_x);
+
+	&mov	("eax",&DWP(32*15+8,"esp"));	# OPENSSL_ia32cap_P copy
+	&lea	("esi",&DWP($in2_y,"esp"));
+	&lea	("ebp",&DWP($S2,"esp"));
+	&lea	("edi",&DWP($S2,"esp"));
+	&call	("_ecp_nistz256_mul_mont");	# p256_mul_mont(S2, S2, in2_y);
+
+	&mov	("eax",&DWP(32*15+8,"esp"));	# OPENSSL_ia32cap_P copy
+	&lea	("esi",&DWP($in1_z,"esp"));
+	&lea	("ebp",&DWP($H,"esp"));
+	&lea	("edi",&DWP($res_z,"esp"));
+	&call	("_ecp_nistz256_mul_mont");	# p256_mul_mont(res_z, H, in1_z);
+
+	&lea	("esi",&DWP($S2,"esp"));
+	&lea	("ebp",&DWP($in1_y,"esp"));
+	&lea	("edi",&DWP($R,"esp"));
+	&call	("_ecp_nistz256_sub");		# p256_sub(R, S2, in1_y);
+
+	&mov	("eax",&DWP(32*15+8,"esp"));	# OPENSSL_ia32cap_P copy
+	&lea	("esi",&DWP($H,"esp"));
+	&lea	("ebp",&DWP($H,"esp"));
+	&lea	("edi",&DWP($Hsqr,"esp"));
+	&call	("_ecp_nistz256_mul_mont");	# p256_sqr_mont(Hsqr, H);
+
+	&mov	("eax",&DWP(32*15+8,"esp"));	# OPENSSL_ia32cap_P copy
+	&lea	("esi",&DWP($R,"esp"));
+	&lea	("ebp",&DWP($R,"esp"));
+	&lea	("edi",&DWP($Rsqr,"esp"));
+	&call	("_ecp_nistz256_mul_mont");	# p256_sqr_mont(Rsqr, R);
+
+	&mov	("eax",&DWP(32*15+8,"esp"));	# OPENSSL_ia32cap_P copy
+	&lea	("esi",&DWP($in1_x,"esp"));
+	&lea	("ebp",&DWP($Hsqr,"esp"));
+	&lea	("edi",&DWP($U2,"esp"));
+	&call	("_ecp_nistz256_mul_mont");	# p256_mul_mont(U2, in1_x, Hsqr);
+
+	&mov	("eax",&DWP(32*15+8,"esp"));	# OPENSSL_ia32cap_P copy
+	&lea	("esi",&DWP($H,"esp"));
+	&lea	("ebp",&DWP($Hsqr,"esp"));
+	&lea	("edi",&DWP($Hcub,"esp"));
+	&call	("_ecp_nistz256_mul_mont");	# p256_mul_mont(Hcub, Hsqr, H);
+
+	&lea	("esi",&DWP($U2,"esp"));
+	&lea	("ebp",&DWP($U2,"esp"));
+	&lea	("edi",&DWP($Hsqr,"esp"));
+	&call	("_ecp_nistz256_add");		# p256_mul_by_2(Hsqr, U2);
+
+	&lea	("esi",&DWP($Rsqr,"esp"));
+	&lea	("ebp",&DWP($Hsqr,"esp"));
+	&lea	("edi",&DWP($res_x,"esp"));
+	&call	("_ecp_nistz256_sub");		# p256_sub(res_x, Rsqr, Hsqr);
+
+	&lea	("esi",&DWP($res_x,"esp"));
+	&lea	("ebp",&DWP($Hcub,"esp"));
+	&lea	("edi",&DWP($res_x,"esp"));
+	&call	("_ecp_nistz256_sub");		# p256_sub(res_x, res_x, Hcub);
+
+	&lea	("esi",&DWP($U2,"esp"));
+	&lea	("ebp",&DWP($res_x,"esp"));
+	&lea	("edi",&DWP($res_y,"esp"));
+	&call	("_ecp_nistz256_sub");		# p256_sub(res_y, U2, res_x);
+
+	&mov	("eax",&DWP(32*15+8,"esp"));	# OPENSSL_ia32cap_P copy
+	&lea	("esi",&DWP($Hcub,"esp"));
+	&lea	("ebp",&DWP($in1_y,"esp"));
+	&lea	("edi",&DWP($S2,"esp"));
+	&call	("_ecp_nistz256_mul_mont");	# p256_mul_mont(S2, Hcub, in1_y);
+
+	&mov	("eax",&DWP(32*15+8,"esp"));	# OPENSSL_ia32cap_P copy
+	&lea	("esi",&DWP($R,"esp"));
+	&lea	("ebp",&DWP($res_y,"esp"));
+	&lea	("edi",&DWP($res_y,"esp"));
+	&call	("_ecp_nistz256_mul_mont");	# p256_mul_mont(res_y, res_y, R);
+
+	&lea	("esi",&DWP($res_y,"esp"));
+	&lea	("ebp",&DWP($S2,"esp"));
+	&lea	("edi",&DWP($res_y,"esp"));
+	&call	("_ecp_nistz256_sub");		# p256_sub(res_y, res_y, S2);
+
+	&mov	("ebp",&DWP(32*15+0,"esp"));	# !in1infty
+	&mov	("esi",&DWP(32*15+4,"esp"));	# !in2infty
+	&mov	("edi",&wparam(0));
+	&mov	("edx","ebp");
+	¬	("ebp");
+	&and	("edx","esi");
+	&and	("ebp","esi");
+	¬	("esi");
+
+	########################################
+	# conditional moves
+    for($i=64;$i<96;$i+=4) {
+	my $one=@ONE_mont[($i-64)/4];
+
+	&mov	("eax","edx");
+	&and	("eax",&DWP($res_x+$i,"esp"));
+	&mov	("ebx","ebp")			if ($one && $one!=-1);
+	&and	("ebx",$one)			if ($one && $one!=-1);
+	&mov	("ecx","esi");
+	&and	("ecx",&DWP($in1_x+$i,"esp"));
+	&or	("eax",$one==-1?"ebp":"ebx")	if ($one);
+	&or	("eax","ecx");
+	&mov	(&DWP($i,"edi"),"eax");
+    }
+    for($i=0;$i<64;$i+=4) {
+	&mov	("eax","edx");
+	&and	("eax",&DWP($res_x+$i,"esp"));
+	&mov	("ebx","ebp");
+	&and	("ebx",&DWP($in2_x+$i,"esp"));
+	&mov	("ecx","esi");
+	&and	("ecx",&DWP($in1_x+$i,"esp"));
+	&or	("eax","ebx");
+	&or	("eax","ecx");
+	&mov	(&DWP($i,"edi"),"eax");
+    }
+	&stack_pop(8*15+3);
+} &function_end("ecp_nistz256_point_add_affine");
+
+&asm_finish();
+
+close STDOUT;
diff --git a/crypto/ec/asm/ecp_nistz256-x86_64.pl b/crypto/ec/asm/ecp_nistz256-x86_64.pl
new file mode 100755
index 000000000..16b6639b5
--- /dev/null
+++ b/crypto/ec/asm/ecp_nistz256-x86_64.pl
@@ -0,0 +1,3087 @@
+#! /usr/bin/env perl
+# Copyright 2014-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+
+##############################################################################
+#                                                                            #
+# Copyright 2014 Intel Corporation                                           #
+#                                                                            #
+# Licensed under the Apache License, Version 2.0 (the "License");            #
+# you may not use this file except in compliance with the License.           #
+# You may obtain a copy of the License at                                    #
+#                                                                            #
+#    http://www.apache.org/licenses/LICENSE-2.0                              #
+#                                                                            #
+# Unless required by applicable law or agreed to in writing, software        #
+# distributed under the License is distributed on an "AS IS" BASIS,          #
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   #
+# See the License for the specific language governing permissions and        #
+# limitations under the License.                                             #
+#                                                                            #
+##############################################################################
+#                                                                            #
+#  Developers and authors:                                                   #
+#  Shay Gueron (1, 2), and Vlad Krasnov (1)                                  #
+#  (1) Intel Corporation, Israel Development Center                          #
+#  (2) University of Haifa                                                   #
+#  Reference:                                                                #
+#  S.Gueron and V.Krasnov, "Fast Prime Field Elliptic Curve Cryptography with#
+#                           256 Bit Primes"                                  #
+#                                                                            #
+##############################################################################
+
+# Further optimization by :
+#
+#		this/original	with/without -DECP_NISTZ256_ASM(*)
+# Opteron	+12-49%		+110-150%
+# Bulldozer	+14-45%		+175-210%
+# P4		+18-46%		n/a :-(
+# Westmere	+12-34%		+80-87%
+# Sandy Bridge	+9-35%		+110-120%
+# Ivy Bridge	+9-35%		+110-125%
+# Haswell	+8-37%		+140-160%
+# Broadwell	+18-58%		+145-210%
+# Atom		+15-50%		+130-180%
+# VIA Nano	+43-160%	+300-480%
+#
+# (*)	"without -DECP_NISTZ256_ASM" refers to build with
+#	"enable-ec_nistp_64_gcc_128";
+#
+# Ranges denote minimum and maximum improvement coefficients depending
+# on benchmark. Lower coefficients are for ECDSA sign, relatively fastest
+# server-side operation. Keep in mind that +100% means 2x improvement.
+
+$flavour = shift;
+$output  = shift;
+if ($flavour =~ /\./) { $output = $flavour; undef $flavour; }
+
+$win64=0; $win64=1 if ($flavour =~ /[nm]asm|mingw64/ || $output =~ /\.asm$/);
+
+$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+( $xlate="${dir}x86_64-xlate.pl" and -f $xlate ) or
+( $xlate="${dir}../../perlasm/x86_64-xlate.pl" and -f $xlate) or
+die "can't locate x86_64-xlate.pl";
+
+open OUT,"| \"$^X\" \"$xlate\" $flavour \"$output\"";
+*STDOUT=*OUT;
+
+if (`$ENV{CC} -Wa,-v -c -o /dev/null -x assembler /dev/null 2>&1`
+		=~ /GNU assembler version ([2-9]\.[0-9]+)/) {
+	$avx = ($1>=2.19) + ($1>=2.22);
+	$addx = ($1>=2.23);
+}
+
+if (!$addx && $win64 && ($flavour =~ /nasm/ || $ENV{ASM} =~ /nasm/) &&
+	    `nasm -v 2>&1` =~ /NASM version ([2-9]\.[0-9]+)/) {
+	$avx = ($1>=2.09) + ($1>=2.10);
+	$addx = ($1>=2.10);
+}
+
+if (!$addx && $win64 && ($flavour =~ /masm/ || $ENV{ASM} =~ /ml64/) &&
+	    `ml64 2>&1` =~ /Version ([0-9]+)\./) {
+	$avx = ($1>=10) + ($1>=11);
+	$addx = ($1>=12);
+}
+
+if (!$addx && `$ENV{CC} -v 2>&1` =~ /((?:^clang|LLVM) version|.*based on LLVM) ([3-9])\.([0-9]+)/) {
+	my $ver = $2 + $3/100.0;	# 3.1->3.01, 3.10->3.10
+	$avx = ($ver>=3.0) + ($ver>=3.01);
+	$addx = ($ver>=3.03);
+}
+
+$code.=<<___;
+.text
+.extern	OPENSSL_ia32cap_P
+
+# The polynomial
+.align 64
+.Lpoly:
+.quad 0xffffffffffffffff, 0x00000000ffffffff, 0x0000000000000000, 0xffffffff00000001
+
+# 2^512 mod P precomputed for NIST P256 polynomial
+.LRR:
+.quad 0x0000000000000003, 0xfffffffbffffffff, 0xfffffffffffffffe, 0x00000004fffffffd
+
+.LOne:
+.long 1,1,1,1,1,1,1,1
+.LTwo:
+.long 2,2,2,2,2,2,2,2
+.LThree:
+.long 3,3,3,3,3,3,3,3
+.LONE_mont:
+.quad 0x0000000000000001, 0xffffffff00000000, 0xffffffffffffffff, 0x00000000fffffffe
+___
+
+{
+################################################################################
+# void ecp_nistz256_mul_by_2(uint64_t res[4], uint64_t a[4]);
+
+my ($a0,$a1,$a2,$a3)=map("%r$_",(8..11));
+my ($t0,$t1,$t2,$t3,$t4)=("%rax","%rdx","%rcx","%r12","%r13");
+my ($r_ptr,$a_ptr,$b_ptr)=("%rdi","%rsi","%rdx");
+
+$code.=<<___;
+
+.globl	ecp_nistz256_mul_by_2
+.type	ecp_nistz256_mul_by_2,\@function,2
+.align	64
+ecp_nistz256_mul_by_2:
+	push	%r12
+	push	%r13
+
+	mov	8*0($a_ptr), $a0
+	xor	$t4,$t4
+	mov	8*1($a_ptr), $a1
+	add	$a0, $a0		# a0:a3+a0:a3
+	mov	8*2($a_ptr), $a2
+	adc	$a1, $a1
+	mov	8*3($a_ptr), $a3
+	lea	.Lpoly(%rip), $a_ptr
+	 mov	$a0, $t0
+	adc	$a2, $a2
+	adc	$a3, $a3
+	 mov	$a1, $t1
+	adc	\$0, $t4
+
+	sub	8*0($a_ptr), $a0
+	 mov	$a2, $t2
+	sbb	8*1($a_ptr), $a1
+	sbb	8*2($a_ptr), $a2
+	 mov	$a3, $t3
+	sbb	8*3($a_ptr), $a3
+	sbb	\$0, $t4
+
+	cmovc	$t0, $a0
+	cmovc	$t1, $a1
+	mov	$a0, 8*0($r_ptr)
+	cmovc	$t2, $a2
+	mov	$a1, 8*1($r_ptr)
+	cmovc	$t3, $a3
+	mov	$a2, 8*2($r_ptr)
+	mov	$a3, 8*3($r_ptr)
+
+	pop	%r13
+	pop	%r12
+	ret
+.size	ecp_nistz256_mul_by_2,.-ecp_nistz256_mul_by_2
+
+################################################################################
+# void ecp_nistz256_div_by_2(uint64_t res[4], uint64_t a[4]);
+.globl	ecp_nistz256_div_by_2
+.type	ecp_nistz256_div_by_2,\@function,2
+.align	32
+ecp_nistz256_div_by_2:
+	push	%r12
+	push	%r13
+
+	mov	8*0($a_ptr), $a0
+	mov	8*1($a_ptr), $a1
+	mov	8*2($a_ptr), $a2
+	 mov	$a0, $t0
+	mov	8*3($a_ptr), $a3
+	lea	.Lpoly(%rip), $a_ptr
+
+	 mov	$a1, $t1
+	xor	$t4, $t4
+	add	8*0($a_ptr), $a0
+	 mov	$a2, $t2
+	adc	8*1($a_ptr), $a1
+	adc	8*2($a_ptr), $a2
+	 mov	$a3, $t3
+	adc	8*3($a_ptr), $a3
+	adc	\$0, $t4
+	xor	$a_ptr, $a_ptr		# borrow $a_ptr
+	test	\$1, $t0
+
+	cmovz	$t0, $a0
+	cmovz	$t1, $a1
+	cmovz	$t2, $a2
+	cmovz	$t3, $a3
+	cmovz	$a_ptr, $t4
+
+	mov	$a1, $t0		# a0:a3>>1
+	shr	\$1, $a0
+	shl	\$63, $t0
+	mov	$a2, $t1
+	shr	\$1, $a1
+	or	$t0, $a0
+	shl	\$63, $t1
+	mov	$a3, $t2
+	shr	\$1, $a2
+	or	$t1, $a1
+	shl	\$63, $t2
+	shr	\$1, $a3
+	shl	\$63, $t4
+	or	$t2, $a2
+	or	$t4, $a3
+
+	mov	$a0, 8*0($r_ptr)
+	mov	$a1, 8*1($r_ptr)
+	mov	$a2, 8*2($r_ptr)
+	mov	$a3, 8*3($r_ptr)
+
+	pop	%r13
+	pop	%r12
+	ret
+.size	ecp_nistz256_div_by_2,.-ecp_nistz256_div_by_2
+
+################################################################################
+# void ecp_nistz256_mul_by_3(uint64_t res[4], uint64_t a[4]);
+.globl	ecp_nistz256_mul_by_3
+.type	ecp_nistz256_mul_by_3,\@function,2
+.align	32
+ecp_nistz256_mul_by_3:
+	push	%r12
+	push	%r13
+
+	mov	8*0($a_ptr), $a0
+	xor	$t4, $t4
+	mov	8*1($a_ptr), $a1
+	add	$a0, $a0		# a0:a3+a0:a3
+	mov	8*2($a_ptr), $a2
+	adc	$a1, $a1
+	mov	8*3($a_ptr), $a3
+	 mov	$a0, $t0
+	adc	$a2, $a2
+	adc	$a3, $a3
+	 mov	$a1, $t1
+	adc	\$0, $t4
+
+	sub	\$-1, $a0
+	 mov	$a2, $t2
+	sbb	.Lpoly+8*1(%rip), $a1
+	sbb	\$0, $a2
+	 mov	$a3, $t3
+	sbb	.Lpoly+8*3(%rip), $a3
+	sbb	\$0, $t4
+
+	cmovc	$t0, $a0
+	cmovc	$t1, $a1
+	cmovc	$t2, $a2
+	cmovc	$t3, $a3
+
+	xor	$t4, $t4
+	add	8*0($a_ptr), $a0	# a0:a3+=a_ptr[0:3]
+	adc	8*1($a_ptr), $a1
+	 mov	$a0, $t0
+	adc	8*2($a_ptr), $a2
+	adc	8*3($a_ptr), $a3
+	 mov	$a1, $t1
+	adc	\$0, $t4
+
+	sub	\$-1, $a0
+	 mov	$a2, $t2
+	sbb	.Lpoly+8*1(%rip), $a1
+	sbb	\$0, $a2
+	 mov	$a3, $t3
+	sbb	.Lpoly+8*3(%rip), $a3
+	sbb	\$0, $t4
+
+	cmovc	$t0, $a0
+	cmovc	$t1, $a1
+	mov	$a0, 8*0($r_ptr)
+	cmovc	$t2, $a2
+	mov	$a1, 8*1($r_ptr)
+	cmovc	$t3, $a3
+	mov	$a2, 8*2($r_ptr)
+	mov	$a3, 8*3($r_ptr)
+
+	pop %r13
+	pop %r12
+	ret
+.size	ecp_nistz256_mul_by_3,.-ecp_nistz256_mul_by_3
+
+################################################################################
+# void ecp_nistz256_add(uint64_t res[4], uint64_t a[4], uint64_t b[4]);
+.globl	ecp_nistz256_add
+.type	ecp_nistz256_add,\@function,3
+.align	32
+ecp_nistz256_add:
+	push	%r12
+	push	%r13
+
+	mov	8*0($a_ptr), $a0
+	xor	$t4, $t4
+	mov	8*1($a_ptr), $a1
+	mov	8*2($a_ptr), $a2
+	mov	8*3($a_ptr), $a3
+	lea	.Lpoly(%rip), $a_ptr
+
+	add	8*0($b_ptr), $a0
+	adc	8*1($b_ptr), $a1
+	 mov	$a0, $t0
+	adc	8*2($b_ptr), $a2
+	adc	8*3($b_ptr), $a3
+	 mov	$a1, $t1
+	adc	\$0, $t4
+
+	sub	8*0($a_ptr), $a0
+	 mov	$a2, $t2
+	sbb	8*1($a_ptr), $a1
+	sbb	8*2($a_ptr), $a2
+	 mov	$a3, $t3
+	sbb	8*3($a_ptr), $a3
+	sbb	\$0, $t4
+
+	cmovc	$t0, $a0
+	cmovc	$t1, $a1
+	mov	$a0, 8*0($r_ptr)
+	cmovc	$t2, $a2
+	mov	$a1, 8*1($r_ptr)
+	cmovc	$t3, $a3
+	mov	$a2, 8*2($r_ptr)
+	mov	$a3, 8*3($r_ptr)
+
+	pop %r13
+	pop %r12
+	ret
+.size	ecp_nistz256_add,.-ecp_nistz256_add
+
+################################################################################
+# void ecp_nistz256_sub(uint64_t res[4], uint64_t a[4], uint64_t b[4]);
+.globl	ecp_nistz256_sub
+.type	ecp_nistz256_sub,\@function,3
+.align	32
+ecp_nistz256_sub:
+	push	%r12
+	push	%r13
+
+	mov	8*0($a_ptr), $a0
+	xor	$t4, $t4
+	mov	8*1($a_ptr), $a1
+	mov	8*2($a_ptr), $a2
+	mov	8*3($a_ptr), $a3
+	lea	.Lpoly(%rip), $a_ptr
+
+	sub	8*0($b_ptr), $a0
+	sbb	8*1($b_ptr), $a1
+	 mov	$a0, $t0
+	sbb	8*2($b_ptr), $a2
+	sbb	8*3($b_ptr), $a3
+	 mov	$a1, $t1
+	sbb	\$0, $t4
+
+	add	8*0($a_ptr), $a0
+	 mov	$a2, $t2
+	adc	8*1($a_ptr), $a1
+	adc	8*2($a_ptr), $a2
+	 mov	$a3, $t3
+	adc	8*3($a_ptr), $a3
+	test	$t4, $t4
+
+	cmovz	$t0, $a0
+	cmovz	$t1, $a1
+	mov	$a0, 8*0($r_ptr)
+	cmovz	$t2, $a2
+	mov	$a1, 8*1($r_ptr)
+	cmovz	$t3, $a3
+	mov	$a2, 8*2($r_ptr)
+	mov	$a3, 8*3($r_ptr)
+
+	pop %r13
+	pop %r12
+	ret
+.size	ecp_nistz256_sub,.-ecp_nistz256_sub
+
+################################################################################
+# void ecp_nistz256_neg(uint64_t res[4], uint64_t a[4]);
+.globl	ecp_nistz256_neg
+.type	ecp_nistz256_neg,\@function,2
+.align	32
+ecp_nistz256_neg:
+	push	%r12
+	push	%r13
+
+	xor	$a0, $a0
+	xor	$a1, $a1
+	xor	$a2, $a2
+	xor	$a3, $a3
+	xor	$t4, $t4
+
+	sub	8*0($a_ptr), $a0
+	sbb	8*1($a_ptr), $a1
+	sbb	8*2($a_ptr), $a2
+	 mov	$a0, $t0
+	sbb	8*3($a_ptr), $a3
+	lea	.Lpoly(%rip), $a_ptr
+	 mov	$a1, $t1
+	sbb	\$0, $t4
+
+	add	8*0($a_ptr), $a0
+	 mov	$a2, $t2
+	adc	8*1($a_ptr), $a1
+	adc	8*2($a_ptr), $a2
+	 mov	$a3, $t3
+	adc	8*3($a_ptr), $a3
+	test	$t4, $t4
+
+	cmovz	$t0, $a0
+	cmovz	$t1, $a1
+	mov	$a0, 8*0($r_ptr)
+	cmovz	$t2, $a2
+	mov	$a1, 8*1($r_ptr)
+	cmovz	$t3, $a3
+	mov	$a2, 8*2($r_ptr)
+	mov	$a3, 8*3($r_ptr)
+
+	pop %r13
+	pop %r12
+	ret
+.size	ecp_nistz256_neg,.-ecp_nistz256_neg
+___
+}
+{
+my ($r_ptr,$a_ptr,$b_org,$b_ptr)=("%rdi","%rsi","%rdx","%rbx");
+my ($acc0,$acc1,$acc2,$acc3,$acc4,$acc5,$acc6,$acc7)=map("%r$_",(8..15));
+my ($t0,$t1,$t2,$t3,$t4)=("%rcx","%rbp","%rbx","%rdx","%rax");
+my ($poly1,$poly3)=($acc6,$acc7);
+
+$code.=<<___;
+################################################################################
+# void ecp_nistz256_to_mont(
+#   uint64_t res[4],
+#   uint64_t in[4]);
+.globl	ecp_nistz256_to_mont
+.type	ecp_nistz256_to_mont,\@function,2
+.align	32
+ecp_nistz256_to_mont:
+___
+$code.=<<___	if ($addx);
+	mov	\$0x80100, %ecx
+	and	OPENSSL_ia32cap_P+8(%rip), %ecx
+___
+$code.=<<___;
+	lea	.LRR(%rip), $b_org
+	jmp	.Lmul_mont
+.size	ecp_nistz256_to_mont,.-ecp_nistz256_to_mont
+
+################################################################################
+# void ecp_nistz256_mul_mont(
+#   uint64_t res[4],
+#   uint64_t a[4],
+#   uint64_t b[4]);
+
+.globl	ecp_nistz256_mul_mont
+.type	ecp_nistz256_mul_mont,\@function,3
+.align	32
+ecp_nistz256_mul_mont:
+___
+$code.=<<___	if ($addx);
+	mov	\$0x80100, %ecx
+	and	OPENSSL_ia32cap_P+8(%rip), %ecx
+___
+$code.=<<___;
+.Lmul_mont:
+	push	%rbp
+	push	%rbx
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+___
+$code.=<<___	if ($addx);
+	cmp	\$0x80100, %ecx
+	je	.Lmul_montx
+___
+$code.=<<___;
+	mov	$b_org, $b_ptr
+	mov	8*0($b_org), %rax
+	mov	8*0($a_ptr), $acc1
+	mov	8*1($a_ptr), $acc2
+	mov	8*2($a_ptr), $acc3
+	mov	8*3($a_ptr), $acc4
+
+	call	__ecp_nistz256_mul_montq
+___
+$code.=<<___	if ($addx);
+	jmp	.Lmul_mont_done
+
+.align	32
+.Lmul_montx:
+	mov	$b_org, $b_ptr
+	mov	8*0($b_org), %rdx
+	mov	8*0($a_ptr), $acc1
+	mov	8*1($a_ptr), $acc2
+	mov	8*2($a_ptr), $acc3
+	mov	8*3($a_ptr), $acc4
+	lea	-128($a_ptr), $a_ptr	# control u-op density
+
+	call	__ecp_nistz256_mul_montx
+___
+$code.=<<___;
+.Lmul_mont_done:
+	pop	%r15
+	pop	%r14
+	pop	%r13
+	pop	%r12
+	pop	%rbx
+	pop	%rbp
+	ret
+.size	ecp_nistz256_mul_mont,.-ecp_nistz256_mul_mont
+
+.type	__ecp_nistz256_mul_montq,\@abi-omnipotent
+.align	32
+__ecp_nistz256_mul_montq:
+	########################################################################
+	# Multiply a by b[0]
+	mov	%rax, $t1
+	mulq	$acc1
+	mov	.Lpoly+8*1(%rip),$poly1
+	mov	%rax, $acc0
+	mov	$t1, %rax
+	mov	%rdx, $acc1
+
+	mulq	$acc2
+	mov	.Lpoly+8*3(%rip),$poly3
+	add	%rax, $acc1
+	mov	$t1, %rax
+	adc	\$0, %rdx
+	mov	%rdx, $acc2
+
+	mulq	$acc3
+	add	%rax, $acc2
+	mov	$t1, %rax
+	adc	\$0, %rdx
+	mov	%rdx, $acc3
+
+	mulq	$acc4
+	add	%rax, $acc3
+	 mov	$acc0, %rax
+	adc	\$0, %rdx
+	xor	$acc5, $acc5
+	mov	%rdx, $acc4
+
+	########################################################################
+	# First reduction step
+	# Basically now we want to multiply acc[0] by p256,
+	# and add the result to the acc.
+	# Due to the special form of p256 we do some optimizations
+	#
+	# acc[0] x p256[0..1] = acc[0] x 2^96 - acc[0]
+	# then we add acc[0] and get acc[0] x 2^96
+
+	mov	$acc0, $t1
+	shl	\$32, $acc0
+	mulq	$poly3
+	shr	\$32, $t1
+	add	$acc0, $acc1		# +=acc[0]<<96
+	adc	$t1, $acc2
+	adc	%rax, $acc3
+	 mov	8*1($b_ptr), %rax
+	adc	%rdx, $acc4
+	adc	\$0, $acc5
+	xor	$acc0, $acc0
+
+	########################################################################
+	# Multiply by b[1]
+	mov	%rax, $t1
+	mulq	8*0($a_ptr)
+	add	%rax, $acc1
+	mov	$t1, %rax
+	adc	\$0, %rdx
+	mov	%rdx, $t0
+
+	mulq	8*1($a_ptr)
+	add	$t0, $acc2
+	adc	\$0, %rdx
+	add	%rax, $acc2
+	mov	$t1, %rax
+	adc	\$0, %rdx
+	mov	%rdx, $t0
+
+	mulq	8*2($a_ptr)
+	add	$t0, $acc3
+	adc	\$0, %rdx
+	add	%rax, $acc3
+	mov	$t1, %rax
+	adc	\$0, %rdx
+	mov	%rdx, $t0
+
+	mulq	8*3($a_ptr)
+	add	$t0, $acc4
+	adc	\$0, %rdx
+	add	%rax, $acc4
+	 mov	$acc1, %rax
+	adc	%rdx, $acc5
+	adc	\$0, $acc0
+
+	########################################################################
+	# Second reduction step	
+	mov	$acc1, $t1
+	shl	\$32, $acc1
+	mulq	$poly3
+	shr	\$32, $t1
+	add	$acc1, $acc2
+	adc	$t1, $acc3
+	adc	%rax, $acc4
+	 mov	8*2($b_ptr), %rax
+	adc	%rdx, $acc5
+	adc	\$0, $acc0
+	xor	$acc1, $acc1
+
+	########################################################################
+	# Multiply by b[2]
+	mov	%rax, $t1
+	mulq	8*0($a_ptr)
+	add	%rax, $acc2
+	mov	$t1, %rax
+	adc	\$0, %rdx
+	mov	%rdx, $t0
+
+	mulq	8*1($a_ptr)
+	add	$t0, $acc3
+	adc	\$0, %rdx
+	add	%rax, $acc3
+	mov	$t1, %rax
+	adc	\$0, %rdx
+	mov	%rdx, $t0
+
+	mulq	8*2($a_ptr)
+	add	$t0, $acc4
+	adc	\$0, %rdx
+	add	%rax, $acc4
+	mov	$t1, %rax
+	adc	\$0, %rdx
+	mov	%rdx, $t0
+
+	mulq	8*3($a_ptr)
+	add	$t0, $acc5
+	adc	\$0, %rdx
+	add	%rax, $acc5
+	 mov	$acc2, %rax
+	adc	%rdx, $acc0
+	adc	\$0, $acc1
+
+	########################################################################
+	# Third reduction step	
+	mov	$acc2, $t1
+	shl	\$32, $acc2
+	mulq	$poly3
+	shr	\$32, $t1
+	add	$acc2, $acc3
+	adc	$t1, $acc4
+	adc	%rax, $acc5
+	 mov	8*3($b_ptr), %rax
+	adc	%rdx, $acc0
+	adc	\$0, $acc1
+	xor	$acc2, $acc2
+
+	########################################################################
+	# Multiply by b[3]
+	mov	%rax, $t1
+	mulq	8*0($a_ptr)
+	add	%rax, $acc3
+	mov	$t1, %rax
+	adc	\$0, %rdx
+	mov	%rdx, $t0
+
+	mulq	8*1($a_ptr)
+	add	$t0, $acc4
+	adc	\$0, %rdx
+	add	%rax, $acc4
+	mov	$t1, %rax
+	adc	\$0, %rdx
+	mov	%rdx, $t0
+
+	mulq	8*2($a_ptr)
+	add	$t0, $acc5
+	adc	\$0, %rdx
+	add	%rax, $acc5
+	mov	$t1, %rax
+	adc	\$0, %rdx
+	mov	%rdx, $t0
+
+	mulq	8*3($a_ptr)
+	add	$t0, $acc0
+	adc	\$0, %rdx
+	add	%rax, $acc0
+	 mov	$acc3, %rax
+	adc	%rdx, $acc1
+	adc	\$0, $acc2
+
+	########################################################################
+	# Final reduction step	
+	mov	$acc3, $t1
+	shl	\$32, $acc3
+	mulq	$poly3
+	shr	\$32, $t1
+	add	$acc3, $acc4
+	adc	$t1, $acc5
+	 mov	$acc4, $t0
+	adc	%rax, $acc0
+	adc	%rdx, $acc1
+	 mov	$acc5, $t1
+	adc	\$0, $acc2
+
+	########################################################################	
+	# Branch-less conditional subtraction of P
+	sub	\$-1, $acc4		# .Lpoly[0]
+	 mov	$acc0, $t2
+	sbb	$poly1, $acc5		# .Lpoly[1]
+	sbb	\$0, $acc0		# .Lpoly[2]
+	 mov	$acc1, $t3
+	sbb	$poly3, $acc1		# .Lpoly[3]
+	sbb	\$0, $acc2
+
+	cmovc	$t0, $acc4
+	cmovc	$t1, $acc5
+	mov	$acc4, 8*0($r_ptr)
+	cmovc	$t2, $acc0
+	mov	$acc5, 8*1($r_ptr)
+	cmovc	$t3, $acc1
+	mov	$acc0, 8*2($r_ptr)
+	mov	$acc1, 8*3($r_ptr)
+
+	ret
+.size	__ecp_nistz256_mul_montq,.-__ecp_nistz256_mul_montq
+
+################################################################################
+# void ecp_nistz256_sqr_mont(
+#   uint64_t res[4],
+#   uint64_t a[4]);
+
+# we optimize the square according to S.Gueron and V.Krasnov,
+# "Speeding up Big-Number Squaring"
+.globl	ecp_nistz256_sqr_mont
+.type	ecp_nistz256_sqr_mont,\@function,2
+.align	32
+ecp_nistz256_sqr_mont:
+___
+$code.=<<___	if ($addx);
+	mov	\$0x80100, %ecx
+	and	OPENSSL_ia32cap_P+8(%rip), %ecx
+___
+$code.=<<___;
+	push	%rbp
+	push	%rbx
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+___
+$code.=<<___	if ($addx);
+	cmp	\$0x80100, %ecx
+	je	.Lsqr_montx
+___
+$code.=<<___;
+	mov	8*0($a_ptr), %rax
+	mov	8*1($a_ptr), $acc6
+	mov	8*2($a_ptr), $acc7
+	mov	8*3($a_ptr), $acc0
+
+	call	__ecp_nistz256_sqr_montq
+___
+$code.=<<___	if ($addx);
+	jmp	.Lsqr_mont_done
+
+.align	32
+.Lsqr_montx:
+	mov	8*0($a_ptr), %rdx
+	mov	8*1($a_ptr), $acc6
+	mov	8*2($a_ptr), $acc7
+	mov	8*3($a_ptr), $acc0
+	lea	-128($a_ptr), $a_ptr	# control u-op density
+
+	call	__ecp_nistz256_sqr_montx
+___
+$code.=<<___;
+.Lsqr_mont_done:
+	pop	%r15
+	pop	%r14
+	pop	%r13
+	pop	%r12
+	pop	%rbx
+	pop	%rbp
+	ret
+.size	ecp_nistz256_sqr_mont,.-ecp_nistz256_sqr_mont
+
+.type	__ecp_nistz256_sqr_montq,\@abi-omnipotent
+.align	32
+__ecp_nistz256_sqr_montq:
+	mov	%rax, $acc5
+	mulq	$acc6			# a[1]*a[0]
+	mov	%rax, $acc1
+	mov	$acc7, %rax
+	mov	%rdx, $acc2
+
+	mulq	$acc5			# a[0]*a[2]
+	add	%rax, $acc2
+	mov	$acc0, %rax
+	adc	\$0, %rdx
+	mov	%rdx, $acc3
+
+	mulq	$acc5			# a[0]*a[3]
+	add	%rax, $acc3
+	 mov	$acc7, %rax
+	adc	\$0, %rdx
+	mov	%rdx, $acc4
+
+	#################################
+	mulq	$acc6			# a[1]*a[2]
+	add	%rax, $acc3
+	mov	$acc0, %rax
+	adc	\$0, %rdx
+	mov	%rdx, $t1
+
+	mulq	$acc6			# a[1]*a[3]
+	add	%rax, $acc4
+	 mov	$acc0, %rax
+	adc	\$0, %rdx
+	add	$t1, $acc4
+	mov	%rdx, $acc5
+	adc	\$0, $acc5
+
+	#################################
+	mulq	$acc7			# a[2]*a[3]
+	xor	$acc7, $acc7
+	add	%rax, $acc5
+	 mov	8*0($a_ptr), %rax
+	mov	%rdx, $acc6
+	adc	\$0, $acc6
+
+	add	$acc1, $acc1		# acc1:6<<1
+	adc	$acc2, $acc2
+	adc	$acc3, $acc3
+	adc	$acc4, $acc4
+	adc	$acc5, $acc5
+	adc	$acc6, $acc6
+	adc	\$0, $acc7
+
+	mulq	%rax
+	mov	%rax, $acc0
+	mov	8*1($a_ptr), %rax
+	mov	%rdx, $t0
+
+	mulq	%rax
+	add	$t0, $acc1
+	adc	%rax, $acc2
+	mov	8*2($a_ptr), %rax
+	adc	\$0, %rdx
+	mov	%rdx, $t0
+
+	mulq	%rax
+	add	$t0, $acc3
+	adc	%rax, $acc4
+	mov	8*3($a_ptr), %rax
+	adc	\$0, %rdx
+	mov	%rdx, $t0
+
+	mulq	%rax
+	add	$t0, $acc5
+	adc	%rax, $acc6
+	 mov	$acc0, %rax
+	adc	%rdx, $acc7
+
+	mov	.Lpoly+8*1(%rip), $a_ptr
+	mov	.Lpoly+8*3(%rip), $t1
+
+	##########################################
+	# Now the reduction
+	# First iteration
+	mov	$acc0, $t0
+	shl	\$32, $acc0
+	mulq	$t1
+	shr	\$32, $t0
+	add	$acc0, $acc1		# +=acc[0]<<96
+	adc	$t0, $acc2
+	adc	%rax, $acc3
+	 mov	$acc1, %rax
+	adc	\$0, %rdx
+
+	##########################################
+	# Second iteration
+	mov	$acc1, $t0
+	shl	\$32, $acc1
+	mov	%rdx, $acc0
+	mulq	$t1
+	shr	\$32, $t0
+	add	$acc1, $acc2
+	adc	$t0, $acc3
+	adc	%rax, $acc0
+	 mov	$acc2, %rax
+	adc	\$0, %rdx
+
+	##########################################
+	# Third iteration
+	mov	$acc2, $t0
+	shl	\$32, $acc2
+	mov	%rdx, $acc1
+	mulq	$t1
+	shr	\$32, $t0
+	add	$acc2, $acc3
+	adc	$t0, $acc0
+	adc	%rax, $acc1
+	 mov	$acc3, %rax
+	adc	\$0, %rdx
+
+	###########################################
+	# Last iteration
+	mov	$acc3, $t0
+	shl	\$32, $acc3
+	mov	%rdx, $acc2
+	mulq	$t1
+	shr	\$32, $t0
+	add	$acc3, $acc0
+	adc	$t0, $acc1
+	adc	%rax, $acc2
+	adc	\$0, %rdx
+	xor	$acc3, $acc3
+
+	############################################
+	# Add the rest of the acc
+	add	$acc0, $acc4
+	adc	$acc1, $acc5
+	 mov	$acc4, $acc0
+	adc	$acc2, $acc6
+	adc	%rdx, $acc7
+	 mov	$acc5, $acc1
+	adc	\$0, $acc3
+
+	sub	\$-1, $acc4		# .Lpoly[0]
+	 mov	$acc6, $acc2
+	sbb	$a_ptr, $acc5		# .Lpoly[1]
+	sbb	\$0, $acc6		# .Lpoly[2]
+	 mov	$acc7, $t0
+	sbb	$t1, $acc7		# .Lpoly[3]
+	sbb	\$0, $acc3
+
+	cmovc	$acc0, $acc4
+	cmovc	$acc1, $acc5
+	mov	$acc4, 8*0($r_ptr)
+	cmovc	$acc2, $acc6
+	mov	$acc5, 8*1($r_ptr)
+	cmovc	$t0, $acc7
+	mov	$acc6, 8*2($r_ptr)
+	mov	$acc7, 8*3($r_ptr)
+
+	ret
+.size	__ecp_nistz256_sqr_montq,.-__ecp_nistz256_sqr_montq
+___
+
+if ($addx) {
+$code.=<<___;
+.type	__ecp_nistz256_mul_montx,\@abi-omnipotent
+.align	32
+__ecp_nistz256_mul_montx:
+	########################################################################
+	# Multiply by b[0]
+	mulx	$acc1, $acc0, $acc1
+	mulx	$acc2, $t0, $acc2
+	mov	\$32, $poly1
+	xor	$acc5, $acc5		# cf=0
+	mulx	$acc3, $t1, $acc3
+	mov	.Lpoly+8*3(%rip), $poly3
+	adc	$t0, $acc1
+	mulx	$acc4, $t0, $acc4
+	 mov	$acc0, %rdx
+	adc	$t1, $acc2
+	 shlx	$poly1,$acc0,$t1
+	adc	$t0, $acc3
+	 shrx	$poly1,$acc0,$t0
+	adc	\$0, $acc4
+
+	########################################################################
+	# First reduction step
+	add	$t1, $acc1
+	adc	$t0, $acc2
+
+	mulx	$poly3, $t0, $t1
+	 mov	8*1($b_ptr), %rdx
+	adc	$t0, $acc3
+	adc	$t1, $acc4
+	adc	\$0, $acc5
+	xor	$acc0, $acc0		# $acc0=0,cf=0,of=0
+
+	########################################################################
+	# Multiply by b[1]
+	mulx	8*0+128($a_ptr), $t0, $t1
+	adcx	$t0, $acc1
+	adox	$t1, $acc2
+
+	mulx	8*1+128($a_ptr), $t0, $t1
+	adcx	$t0, $acc2
+	adox	$t1, $acc3
+
+	mulx	8*2+128($a_ptr), $t0, $t1
+	adcx	$t0, $acc3
+	adox	$t1, $acc4
+
+	mulx	8*3+128($a_ptr), $t0, $t1
+	 mov	$acc1, %rdx
+	adcx	$t0, $acc4
+	 shlx	$poly1, $acc1, $t0
+	adox	$t1, $acc5
+	 shrx	$poly1, $acc1, $t1
+
+	adcx	$acc0, $acc5
+	adox	$acc0, $acc0
+	adc	\$0, $acc0
+
+	########################################################################
+	# Second reduction step
+	add	$t0, $acc2
+	adc	$t1, $acc3
+
+	mulx	$poly3, $t0, $t1
+	 mov	8*2($b_ptr), %rdx
+	adc	$t0, $acc4
+	adc	$t1, $acc5
+	adc	\$0, $acc0
+	xor	$acc1 ,$acc1		# $acc1=0,cf=0,of=0
+
+	########################################################################
+	# Multiply by b[2]
+	mulx	8*0+128($a_ptr), $t0, $t1
+	adcx	$t0, $acc2
+	adox	$t1, $acc3
+
+	mulx	8*1+128($a_ptr), $t0, $t1
+	adcx	$t0, $acc3
+	adox	$t1, $acc4
+
+	mulx	8*2+128($a_ptr), $t0, $t1
+	adcx	$t0, $acc4
+	adox	$t1, $acc5
+
+	mulx	8*3+128($a_ptr), $t0, $t1
+	 mov	$acc2, %rdx
+	adcx	$t0, $acc5
+	 shlx	$poly1, $acc2, $t0
+	adox	$t1, $acc0
+	 shrx	$poly1, $acc2, $t1
+
+	adcx	$acc1, $acc0
+	adox	$acc1, $acc1
+	adc	\$0, $acc1
+
+	########################################################################
+	# Third reduction step
+	add	$t0, $acc3
+	adc	$t1, $acc4
+
+	mulx	$poly3, $t0, $t1
+	 mov	8*3($b_ptr), %rdx
+	adc	$t0, $acc5
+	adc	$t1, $acc0
+	adc	\$0, $acc1
+	xor	$acc2, $acc2		# $acc2=0,cf=0,of=0
+
+	########################################################################
+	# Multiply by b[3]
+	mulx	8*0+128($a_ptr), $t0, $t1
+	adcx	$t0, $acc3
+	adox	$t1, $acc4
+
+	mulx	8*1+128($a_ptr), $t0, $t1
+	adcx	$t0, $acc4
+	adox	$t1, $acc5
+
+	mulx	8*2+128($a_ptr), $t0, $t1
+	adcx	$t0, $acc5
+	adox	$t1, $acc0
+
+	mulx	8*3+128($a_ptr), $t0, $t1
+	 mov	$acc3, %rdx
+	adcx	$t0, $acc0
+	 shlx	$poly1, $acc3, $t0
+	adox	$t1, $acc1
+	 shrx	$poly1, $acc3, $t1
+
+	adcx	$acc2, $acc1
+	adox	$acc2, $acc2
+	adc	\$0, $acc2
+
+	########################################################################
+	# Fourth reduction step
+	add	$t0, $acc4
+	adc	$t1, $acc5
+
+	mulx	$poly3, $t0, $t1
+	 mov	$acc4, $t2
+	mov	.Lpoly+8*1(%rip), $poly1
+	adc	$t0, $acc0
+	 mov	$acc5, $t3
+	adc	$t1, $acc1
+	adc	\$0, $acc2
+
+	########################################################################
+	# Branch-less conditional subtraction of P
+	xor	%eax, %eax
+	 mov	$acc0, $t0
+	sbb	\$-1, $acc4		# .Lpoly[0]
+	sbb	$poly1, $acc5		# .Lpoly[1]
+	sbb	\$0, $acc0		# .Lpoly[2]
+	 mov	$acc1, $t1
+	sbb	$poly3, $acc1		# .Lpoly[3]
+	sbb	\$0, $acc2
+
+	cmovc	$t2, $acc4
+	cmovc	$t3, $acc5
+	mov	$acc4, 8*0($r_ptr)
+	cmovc	$t0, $acc0
+	mov	$acc5, 8*1($r_ptr)
+	cmovc	$t1, $acc1
+	mov	$acc0, 8*2($r_ptr)
+	mov	$acc1, 8*3($r_ptr)
+
+	ret
+.size	__ecp_nistz256_mul_montx,.-__ecp_nistz256_mul_montx
+
+.type	__ecp_nistz256_sqr_montx,\@abi-omnipotent
+.align	32
+__ecp_nistz256_sqr_montx:
+	mulx	$acc6, $acc1, $acc2	# a[0]*a[1]
+	mulx	$acc7, $t0, $acc3	# a[0]*a[2]
+	xor	%eax, %eax
+	adc	$t0, $acc2
+	mulx	$acc0, $t1, $acc4	# a[0]*a[3]
+	 mov	$acc6, %rdx
+	adc	$t1, $acc3
+	adc	\$0, $acc4
+	xor	$acc5, $acc5		# $acc5=0,cf=0,of=0
+
+	#################################
+	mulx	$acc7, $t0, $t1		# a[1]*a[2]
+	adcx	$t0, $acc3
+	adox	$t1, $acc4
+
+	mulx	$acc0, $t0, $t1		# a[1]*a[3]
+	 mov	$acc7, %rdx
+	adcx	$t0, $acc4
+	adox	$t1, $acc5
+	adc	\$0, $acc5
+
+	#################################
+	mulx	$acc0, $t0, $acc6	# a[2]*a[3]
+	 mov	8*0+128($a_ptr), %rdx
+	xor	$acc7, $acc7		# $acc7=0,cf=0,of=0
+	 adcx	$acc1, $acc1		# acc1:6<<1
+	adox	$t0, $acc5
+	 adcx	$acc2, $acc2
+	adox	$acc7, $acc6		# of=0
+
+	mulx	%rdx, $acc0, $t1
+	mov	8*1+128($a_ptr), %rdx
+	 adcx	$acc3, $acc3
+	adox	$t1, $acc1
+	 adcx	$acc4, $acc4
+	mulx	%rdx, $t0, $t4
+	mov	8*2+128($a_ptr), %rdx
+	 adcx	$acc5, $acc5
+	adox	$t0, $acc2
+	 adcx	$acc6, $acc6
+	.byte	0x67
+	mulx	%rdx, $t0, $t1
+	mov	8*3+128($a_ptr), %rdx
+	adox	$t4, $acc3
+	 adcx	$acc7, $acc7
+	adox	$t0, $acc4
+	 mov	\$32, $a_ptr
+	adox	$t1, $acc5
+	.byte	0x67,0x67
+	mulx	%rdx, $t0, $t4
+	 mov	$acc0, %rdx
+	adox	$t0, $acc6
+	 shlx	$a_ptr, $acc0, $t0
+	adox	$t4, $acc7
+	 shrx	$a_ptr, $acc0, $t4
+	 mov	.Lpoly+8*3(%rip), $t1
+
+	# reduction step 1
+	add	$t0, $acc1
+	adc	$t4, $acc2
+
+	mulx	$t1, $t0, $acc0
+	 mov	$acc1, %rdx
+	adc	$t0, $acc3
+	 shlx	$a_ptr, $acc1, $t0
+	adc	\$0, $acc0
+	 shrx	$a_ptr, $acc1, $t4
+
+	# reduction step 2
+	add	$t0, $acc2
+	adc	$t4, $acc3
+
+	mulx	$t1, $t0, $acc1
+	 mov	$acc2, %rdx
+	adc	$t0, $acc0
+	 shlx	$a_ptr, $acc2, $t0
+	adc	\$0, $acc1
+	 shrx	$a_ptr, $acc2, $t4
+
+	# reduction step 3
+	add	$t0, $acc3
+	adc	$t4, $acc0
+
+	mulx	$t1, $t0, $acc2
+	 mov	$acc3, %rdx
+	adc	$t0, $acc1
+	 shlx	$a_ptr, $acc3, $t0
+	adc	\$0, $acc2
+	 shrx	$a_ptr, $acc3, $t4
+
+	# reduction step 4
+	add	$t0, $acc0
+	adc	$t4, $acc1
+
+	mulx	$t1, $t0, $acc3
+	adc	$t0, $acc2
+	adc	\$0, $acc3
+
+	xor	$t3, $t3		# cf=0
+	adc	$acc0, $acc4		# accumulate upper half
+	 mov	.Lpoly+8*1(%rip), $a_ptr
+	adc	$acc1, $acc5
+	 mov	$acc4, $acc0
+	adc	$acc2, $acc6
+	adc	$acc3, $acc7
+	 mov	$acc5, $acc1
+	adc	\$0, $t3
+
+	xor	%eax, %eax		# cf=0
+	sbb	\$-1, $acc4		# .Lpoly[0]
+	 mov	$acc6, $acc2
+	sbb	$a_ptr, $acc5		# .Lpoly[1]
+	sbb	\$0, $acc6		# .Lpoly[2]
+	 mov	$acc7, $acc3
+	sbb	$t1, $acc7		# .Lpoly[3]
+	sbb	\$0, $t3
+
+	cmovc	$acc0, $acc4
+	cmovc	$acc1, $acc5
+	mov	$acc4, 8*0($r_ptr)
+	cmovc	$acc2, $acc6
+	mov	$acc5, 8*1($r_ptr)
+	cmovc	$acc3, $acc7
+	mov	$acc6, 8*2($r_ptr)
+	mov	$acc7, 8*3($r_ptr)
+
+	ret
+.size	__ecp_nistz256_sqr_montx,.-__ecp_nistz256_sqr_montx
+___
+}
+}
+{
+my ($r_ptr,$in_ptr)=("%rdi","%rsi");
+my ($acc0,$acc1,$acc2,$acc3)=map("%r$_",(8..11));
+my ($t0,$t1,$t2)=("%rcx","%r12","%r13");
+
+$code.=<<___;
+################################################################################
+# void ecp_nistz256_from_mont(
+#   uint64_t res[4],
+#   uint64_t in[4]);
+# This one performs Montgomery multiplication by 1, so we only need the reduction
+
+.globl	ecp_nistz256_from_mont
+.type	ecp_nistz256_from_mont,\@function,2
+.align	32
+ecp_nistz256_from_mont:
+	push	%r12
+	push	%r13
+
+	mov	8*0($in_ptr), %rax
+	mov	.Lpoly+8*3(%rip), $t2
+	mov	8*1($in_ptr), $acc1
+	mov	8*2($in_ptr), $acc2
+	mov	8*3($in_ptr), $acc3
+	mov	%rax, $acc0
+	mov	.Lpoly+8*1(%rip), $t1
+
+	#########################################
+	# First iteration
+	mov	%rax, $t0
+	shl	\$32, $acc0
+	mulq	$t2
+	shr	\$32, $t0
+	add	$acc0, $acc1
+	adc	$t0, $acc2
+	adc	%rax, $acc3
+	 mov	$acc1, %rax
+	adc	\$0, %rdx
+
+	#########################################
+	# Second iteration
+	mov	$acc1, $t0
+	shl	\$32, $acc1
+	mov	%rdx, $acc0
+	mulq	$t2
+	shr	\$32, $t0
+	add	$acc1, $acc2
+	adc	$t0, $acc3
+	adc	%rax, $acc0
+	 mov	$acc2, %rax
+	adc	\$0, %rdx
+
+	##########################################
+	# Third iteration
+	mov	$acc2, $t0
+	shl	\$32, $acc2
+	mov	%rdx, $acc1
+	mulq	$t2
+	shr	\$32, $t0
+	add	$acc2, $acc3
+	adc	$t0, $acc0
+	adc	%rax, $acc1
+	 mov	$acc3, %rax
+	adc	\$0, %rdx
+
+	###########################################
+	# Last iteration
+	mov	$acc3, $t0
+	shl	\$32, $acc3
+	mov	%rdx, $acc2
+	mulq	$t2
+	shr	\$32, $t0
+	add	$acc3, $acc0
+	adc	$t0, $acc1
+	 mov	$acc0, $t0
+	adc	%rax, $acc2
+	 mov	$acc1, $in_ptr
+	adc	\$0, %rdx
+
+	###########################################
+	# Branch-less conditional subtraction
+	sub	\$-1, $acc0
+	 mov	$acc2, %rax
+	sbb	$t1, $acc1
+	sbb	\$0, $acc2
+	 mov	%rdx, $acc3
+	sbb	$t2, %rdx
+	sbb	$t2, $t2
+
+	cmovnz	$t0, $acc0
+	cmovnz	$in_ptr, $acc1
+	mov	$acc0, 8*0($r_ptr)
+	cmovnz	%rax, $acc2
+	mov	$acc1, 8*1($r_ptr)
+	cmovz	%rdx, $acc3
+	mov	$acc2, 8*2($r_ptr)
+	mov	$acc3, 8*3($r_ptr)
+
+	pop	%r13
+	pop	%r12
+	ret
+.size	ecp_nistz256_from_mont,.-ecp_nistz256_from_mont
+___
+}
+{
+my ($val,$in_t,$index)=$win64?("%rcx","%rdx","%r8d"):("%rdi","%rsi","%edx");
+my ($ONE,$INDEX,$Ra,$Rb,$Rc,$Rd,$Re,$Rf)=map("%xmm$_",(0..7));
+my ($M0,$T0a,$T0b,$T0c,$T0d,$T0e,$T0f,$TMP0)=map("%xmm$_",(8..15));
+my ($M1,$T2a,$T2b,$TMP2,$M2,$T2a,$T2b,$TMP2)=map("%xmm$_",(8..15));
+
+$code.=<<___;
+################################################################################
+# void ecp_nistz256_scatter_w5(uint64_t *val, uint64_t *in_t, int index);
+.globl	ecp_nistz256_scatter_w5
+.type	ecp_nistz256_scatter_w5,\@abi-omnipotent
+.align	32
+ecp_nistz256_scatter_w5:
+	lea	-3($index,$index,2), $index
+	movdqa	0x00($in_t), %xmm0
+	shl	\$5, $index
+	movdqa	0x10($in_t), %xmm1
+	movdqa	0x20($in_t), %xmm2
+	movdqa	0x30($in_t), %xmm3
+	movdqa	0x40($in_t), %xmm4
+	movdqa	0x50($in_t), %xmm5
+	movdqa	%xmm0, 0x00($val,$index)
+	movdqa	%xmm1, 0x10($val,$index)
+	movdqa	%xmm2, 0x20($val,$index)
+	movdqa	%xmm3, 0x30($val,$index)
+	movdqa	%xmm4, 0x40($val,$index)
+	movdqa	%xmm5, 0x50($val,$index)
+
+	ret
+.size	ecp_nistz256_scatter_w5,.-ecp_nistz256_scatter_w5
+
+################################################################################
+# void ecp_nistz256_gather_w5(uint64_t *val, uint64_t *in_t, int index);
+.globl	ecp_nistz256_gather_w5
+.type	ecp_nistz256_gather_w5,\@abi-omnipotent
+.align	32
+ecp_nistz256_gather_w5:
+___
+$code.=<<___	if ($avx>1);
+	mov	OPENSSL_ia32cap_P+8(%rip), %eax
+	test	\$`1<<5`, %eax
+	jnz	.Lavx2_gather_w5
+___
+$code.=<<___	if ($win64);
+	lea	-0x88(%rsp), %rax
+.LSEH_begin_ecp_nistz256_gather_w5:
+	.byte	0x48,0x8d,0x60,0xe0		#lea	-0x20(%rax), %rsp
+	.byte	0x0f,0x29,0x70,0xe0		#movaps	%xmm6, -0x20(%rax)
+	.byte	0x0f,0x29,0x78,0xf0		#movaps	%xmm7, -0x10(%rax)
+	.byte	0x44,0x0f,0x29,0x00		#movaps	%xmm8, 0(%rax)
+	.byte	0x44,0x0f,0x29,0x48,0x10	#movaps	%xmm9, 0x10(%rax)
+	.byte	0x44,0x0f,0x29,0x50,0x20	#movaps	%xmm10, 0x20(%rax)
+	.byte	0x44,0x0f,0x29,0x58,0x30	#movaps	%xmm11, 0x30(%rax)
+	.byte	0x44,0x0f,0x29,0x60,0x40	#movaps	%xmm12, 0x40(%rax)
+	.byte	0x44,0x0f,0x29,0x68,0x50	#movaps	%xmm13, 0x50(%rax)
+	.byte	0x44,0x0f,0x29,0x70,0x60	#movaps	%xmm14, 0x60(%rax)
+	.byte	0x44,0x0f,0x29,0x78,0x70	#movaps	%xmm15, 0x70(%rax)
+___
+$code.=<<___;
+	movdqa	.LOne(%rip), $ONE
+	movd	$index, $INDEX
+
+	pxor	$Ra, $Ra
+	pxor	$Rb, $Rb
+	pxor	$Rc, $Rc
+	pxor	$Rd, $Rd
+	pxor	$Re, $Re
+	pxor	$Rf, $Rf
+
+	movdqa	$ONE, $M0
+	pshufd	\$0, $INDEX, $INDEX
+
+	mov	\$16, %rax
+.Lselect_loop_sse_w5:
+
+	movdqa	$M0, $TMP0
+	paddd	$ONE, $M0
+	pcmpeqd $INDEX, $TMP0
+
+	movdqa	16*0($in_t), $T0a
+	movdqa	16*1($in_t), $T0b
+	movdqa	16*2($in_t), $T0c
+	movdqa	16*3($in_t), $T0d
+	movdqa	16*4($in_t), $T0e
+	movdqa	16*5($in_t), $T0f
+	lea 16*6($in_t), $in_t
+
+	pand	$TMP0, $T0a
+	pand	$TMP0, $T0b
+	por	$T0a, $Ra
+	pand	$TMP0, $T0c
+	por	$T0b, $Rb
+	pand	$TMP0, $T0d
+	por	$T0c, $Rc
+	pand	$TMP0, $T0e
+	por	$T0d, $Rd
+	pand	$TMP0, $T0f
+	por	$T0e, $Re
+	por	$T0f, $Rf
+
+	dec	%rax
+	jnz	.Lselect_loop_sse_w5
+
+	movdqu	$Ra, 16*0($val)
+	movdqu	$Rb, 16*1($val)
+	movdqu	$Rc, 16*2($val)
+	movdqu	$Rd, 16*3($val)
+	movdqu	$Re, 16*4($val)
+	movdqu	$Rf, 16*5($val)
+___
+$code.=<<___	if ($win64);
+	movaps	(%rsp), %xmm6
+	movaps	0x10(%rsp), %xmm7
+	movaps	0x20(%rsp), %xmm8
+	movaps	0x30(%rsp), %xmm9
+	movaps	0x40(%rsp), %xmm10
+	movaps	0x50(%rsp), %xmm11
+	movaps	0x60(%rsp), %xmm12
+	movaps	0x70(%rsp), %xmm13
+	movaps	0x80(%rsp), %xmm14
+	movaps	0x90(%rsp), %xmm15
+	lea	0xa8(%rsp), %rsp
+.LSEH_end_ecp_nistz256_gather_w5:
+___
+$code.=<<___;
+	ret
+.size	ecp_nistz256_gather_w5,.-ecp_nistz256_gather_w5
+
+################################################################################
+# void ecp_nistz256_scatter_w7(uint64_t *val, uint64_t *in_t, int index);
+.globl	ecp_nistz256_scatter_w7
+.type	ecp_nistz256_scatter_w7,\@abi-omnipotent
+.align	32
+ecp_nistz256_scatter_w7:
+	movdqu	0x00($in_t), %xmm0
+	shl	\$6, $index
+	movdqu	0x10($in_t), %xmm1
+	movdqu	0x20($in_t), %xmm2
+	movdqu	0x30($in_t), %xmm3
+	movdqa	%xmm0, 0x00($val,$index)
+	movdqa	%xmm1, 0x10($val,$index)
+	movdqa	%xmm2, 0x20($val,$index)
+	movdqa	%xmm3, 0x30($val,$index)
+
+	ret
+.size	ecp_nistz256_scatter_w7,.-ecp_nistz256_scatter_w7
+
+################################################################################
+# void ecp_nistz256_gather_w7(uint64_t *val, uint64_t *in_t, int index);
+.globl	ecp_nistz256_gather_w7
+.type	ecp_nistz256_gather_w7,\@abi-omnipotent
+.align	32
+ecp_nistz256_gather_w7:
+___
+$code.=<<___	if ($avx>1);
+	mov	OPENSSL_ia32cap_P+8(%rip), %eax
+	test	\$`1<<5`, %eax
+	jnz	.Lavx2_gather_w7
+___
+$code.=<<___	if ($win64);
+	lea	-0x88(%rsp), %rax
+.LSEH_begin_ecp_nistz256_gather_w7:
+	.byte	0x48,0x8d,0x60,0xe0		#lea	-0x20(%rax), %rsp
+	.byte	0x0f,0x29,0x70,0xe0		#movaps	%xmm6, -0x20(%rax)
+	.byte	0x0f,0x29,0x78,0xf0		#movaps	%xmm7, -0x10(%rax)
+	.byte	0x44,0x0f,0x29,0x00		#movaps	%xmm8, 0(%rax)
+	.byte	0x44,0x0f,0x29,0x48,0x10	#movaps	%xmm9, 0x10(%rax)
+	.byte	0x44,0x0f,0x29,0x50,0x20	#movaps	%xmm10, 0x20(%rax)
+	.byte	0x44,0x0f,0x29,0x58,0x30	#movaps	%xmm11, 0x30(%rax)
+	.byte	0x44,0x0f,0x29,0x60,0x40	#movaps	%xmm12, 0x40(%rax)
+	.byte	0x44,0x0f,0x29,0x68,0x50	#movaps	%xmm13, 0x50(%rax)
+	.byte	0x44,0x0f,0x29,0x70,0x60	#movaps	%xmm14, 0x60(%rax)
+	.byte	0x44,0x0f,0x29,0x78,0x70	#movaps	%xmm15, 0x70(%rax)
+___
+$code.=<<___;
+	movdqa	.LOne(%rip), $M0
+	movd	$index, $INDEX
+
+	pxor	$Ra, $Ra
+	pxor	$Rb, $Rb
+	pxor	$Rc, $Rc
+	pxor	$Rd, $Rd
+
+	movdqa	$M0, $ONE
+	pshufd	\$0, $INDEX, $INDEX
+	mov	\$64, %rax
+
+.Lselect_loop_sse_w7:
+	movdqa	$M0, $TMP0
+	paddd	$ONE, $M0
+	movdqa	16*0($in_t), $T0a
+	movdqa	16*1($in_t), $T0b
+	pcmpeqd	$INDEX, $TMP0
+	movdqa	16*2($in_t), $T0c
+	movdqa	16*3($in_t), $T0d
+	lea	16*4($in_t), $in_t
+
+	pand	$TMP0, $T0a
+	pand	$TMP0, $T0b
+	por	$T0a, $Ra
+	pand	$TMP0, $T0c
+	por	$T0b, $Rb
+	pand	$TMP0, $T0d
+	por	$T0c, $Rc
+	prefetcht0	255($in_t)
+	por	$T0d, $Rd
+
+	dec	%rax
+	jnz	.Lselect_loop_sse_w7
+
+	movdqu	$Ra, 16*0($val)
+	movdqu	$Rb, 16*1($val)
+	movdqu	$Rc, 16*2($val)
+	movdqu	$Rd, 16*3($val)
+___
+$code.=<<___	if ($win64);
+	movaps	(%rsp), %xmm6
+	movaps	0x10(%rsp), %xmm7
+	movaps	0x20(%rsp), %xmm8
+	movaps	0x30(%rsp), %xmm9
+	movaps	0x40(%rsp), %xmm10
+	movaps	0x50(%rsp), %xmm11
+	movaps	0x60(%rsp), %xmm12
+	movaps	0x70(%rsp), %xmm13
+	movaps	0x80(%rsp), %xmm14
+	movaps	0x90(%rsp), %xmm15
+	lea	0xa8(%rsp), %rsp
+.LSEH_end_ecp_nistz256_gather_w7:
+___
+$code.=<<___;
+	ret
+.size	ecp_nistz256_gather_w7,.-ecp_nistz256_gather_w7
+___
+}
+if ($avx>1) {
+my ($val,$in_t,$index)=$win64?("%rcx","%rdx","%r8d"):("%rdi","%rsi","%edx");
+my ($TWO,$INDEX,$Ra,$Rb,$Rc)=map("%ymm$_",(0..4));
+my ($M0,$T0a,$T0b,$T0c,$TMP0)=map("%ymm$_",(5..9));
+my ($M1,$T1a,$T1b,$T1c,$TMP1)=map("%ymm$_",(10..14));
+
+$code.=<<___;
+################################################################################
+# void ecp_nistz256_avx2_gather_w5(uint64_t *val, uint64_t *in_t, int index);
+.type	ecp_nistz256_avx2_gather_w5,\@abi-omnipotent
+.align	32
+ecp_nistz256_avx2_gather_w5:
+.Lavx2_gather_w5:
+	vzeroupper
+___
+$code.=<<___	if ($win64);
+	lea	-0x88(%rsp), %rax
+.LSEH_begin_ecp_nistz256_avx2_gather_w5:
+	.byte	0x48,0x8d,0x60,0xe0		#lea	-0x20(%rax), %rsp
+	.byte	0xc5,0xf8,0x29,0x70,0xe0	#vmovaps %xmm6, -0x20(%rax)
+	.byte	0xc5,0xf8,0x29,0x78,0xf0	#vmovaps %xmm7, -0x10(%rax)
+	.byte	0xc5,0x78,0x29,0x40,0x00	#vmovaps %xmm8, 8(%rax)
+	.byte	0xc5,0x78,0x29,0x48,0x10	#vmovaps %xmm9, 0x10(%rax)
+	.byte	0xc5,0x78,0x29,0x50,0x20	#vmovaps %xmm10, 0x20(%rax)
+	.byte	0xc5,0x78,0x29,0x58,0x30	#vmovaps %xmm11, 0x30(%rax)
+	.byte	0xc5,0x78,0x29,0x60,0x40	#vmovaps %xmm12, 0x40(%rax)
+	.byte	0xc5,0x78,0x29,0x68,0x50	#vmovaps %xmm13, 0x50(%rax)
+	.byte	0xc5,0x78,0x29,0x70,0x60	#vmovaps %xmm14, 0x60(%rax)
+	.byte	0xc5,0x78,0x29,0x78,0x70	#vmovaps %xmm15, 0x70(%rax)
+___
+$code.=<<___;
+	vmovdqa	.LTwo(%rip), $TWO
+
+	vpxor	$Ra, $Ra, $Ra
+	vpxor	$Rb, $Rb, $Rb
+	vpxor	$Rc, $Rc, $Rc
+
+	vmovdqa .LOne(%rip), $M0
+	vmovdqa .LTwo(%rip), $M1
+
+	vmovd	$index, %xmm1
+	vpermd	$INDEX, $Ra, $INDEX
+
+	mov	\$8, %rax
+.Lselect_loop_avx2_w5:
+
+	vmovdqa	32*0($in_t), $T0a
+	vmovdqa	32*1($in_t), $T0b
+	vmovdqa	32*2($in_t), $T0c
+
+	vmovdqa	32*3($in_t), $T1a
+	vmovdqa	32*4($in_t), $T1b
+	vmovdqa	32*5($in_t), $T1c
+
+	vpcmpeqd	$INDEX, $M0, $TMP0
+	vpcmpeqd	$INDEX, $M1, $TMP1
+
+	vpaddd	$TWO, $M0, $M0
+	vpaddd	$TWO, $M1, $M1
+	lea	32*6($in_t), $in_t
+
+	vpand	$TMP0, $T0a, $T0a
+	vpand	$TMP0, $T0b, $T0b
+	vpand	$TMP0, $T0c, $T0c
+	vpand	$TMP1, $T1a, $T1a
+	vpand	$TMP1, $T1b, $T1b
+	vpand	$TMP1, $T1c, $T1c
+
+	vpxor	$T0a, $Ra, $Ra
+	vpxor	$T0b, $Rb, $Rb
+	vpxor	$T0c, $Rc, $Rc
+	vpxor	$T1a, $Ra, $Ra
+	vpxor	$T1b, $Rb, $Rb
+	vpxor	$T1c, $Rc, $Rc
+
+	dec %rax
+	jnz .Lselect_loop_avx2_w5
+
+	vmovdqu $Ra, 32*0($val)
+	vmovdqu $Rb, 32*1($val)
+	vmovdqu $Rc, 32*2($val)
+	vzeroupper
+___
+$code.=<<___	if ($win64);
+	movaps	(%rsp), %xmm6
+	movaps	0x10(%rsp), %xmm7
+	movaps	0x20(%rsp), %xmm8
+	movaps	0x30(%rsp), %xmm9
+	movaps	0x40(%rsp), %xmm10
+	movaps	0x50(%rsp), %xmm11
+	movaps	0x60(%rsp), %xmm12
+	movaps	0x70(%rsp), %xmm13
+	movaps	0x80(%rsp), %xmm14
+	movaps	0x90(%rsp), %xmm15
+	lea	0xa8(%rsp), %rsp
+.LSEH_end_ecp_nistz256_avx2_gather_w5:
+___
+$code.=<<___;
+	ret
+.size	ecp_nistz256_avx2_gather_w5,.-ecp_nistz256_avx2_gather_w5
+___
+}
+if ($avx>1) {
+my ($val,$in_t,$index)=$win64?("%rcx","%rdx","%r8d"):("%rdi","%rsi","%edx");
+my ($THREE,$INDEX,$Ra,$Rb)=map("%ymm$_",(0..3));
+my ($M0,$T0a,$T0b,$TMP0)=map("%ymm$_",(4..7));
+my ($M1,$T1a,$T1b,$TMP1)=map("%ymm$_",(8..11));
+my ($M2,$T2a,$T2b,$TMP2)=map("%ymm$_",(12..15));
+
+$code.=<<___;
+
+################################################################################
+# void ecp_nistz256_avx2_gather_w7(uint64_t *val, uint64_t *in_t, int index);
+.globl	ecp_nistz256_avx2_gather_w7
+.type	ecp_nistz256_avx2_gather_w7,\@abi-omnipotent
+.align	32
+ecp_nistz256_avx2_gather_w7:
+.Lavx2_gather_w7:
+	vzeroupper
+___
+$code.=<<___	if ($win64);
+	lea	-0x88(%rsp), %rax
+.LSEH_begin_ecp_nistz256_avx2_gather_w7:
+	.byte	0x48,0x8d,0x60,0xe0		#lea	-0x20(%rax), %rsp
+	.byte	0xc5,0xf8,0x29,0x70,0xe0	#vmovaps %xmm6, -0x20(%rax)
+	.byte	0xc5,0xf8,0x29,0x78,0xf0	#vmovaps %xmm7, -0x10(%rax)
+	.byte	0xc5,0x78,0x29,0x40,0x00	#vmovaps %xmm8, 8(%rax)
+	.byte	0xc5,0x78,0x29,0x48,0x10	#vmovaps %xmm9, 0x10(%rax)
+	.byte	0xc5,0x78,0x29,0x50,0x20	#vmovaps %xmm10, 0x20(%rax)
+	.byte	0xc5,0x78,0x29,0x58,0x30	#vmovaps %xmm11, 0x30(%rax)
+	.byte	0xc5,0x78,0x29,0x60,0x40	#vmovaps %xmm12, 0x40(%rax)
+	.byte	0xc5,0x78,0x29,0x68,0x50	#vmovaps %xmm13, 0x50(%rax)
+	.byte	0xc5,0x78,0x29,0x70,0x60	#vmovaps %xmm14, 0x60(%rax)
+	.byte	0xc5,0x78,0x29,0x78,0x70	#vmovaps %xmm15, 0x70(%rax)
+___
+$code.=<<___;
+	vmovdqa	.LThree(%rip), $THREE
+
+	vpxor	$Ra, $Ra, $Ra
+	vpxor	$Rb, $Rb, $Rb
+
+	vmovdqa .LOne(%rip), $M0
+	vmovdqa .LTwo(%rip), $M1
+	vmovdqa .LThree(%rip), $M2
+
+	vmovd	$index, %xmm1
+	vpermd	$INDEX, $Ra, $INDEX
+	# Skip index = 0, because it is implicitly the point at infinity
+
+	mov	\$21, %rax
+.Lselect_loop_avx2_w7:
+
+	vmovdqa	32*0($in_t), $T0a
+	vmovdqa	32*1($in_t), $T0b
+
+	vmovdqa	32*2($in_t), $T1a
+	vmovdqa	32*3($in_t), $T1b
+
+	vmovdqa	32*4($in_t), $T2a
+	vmovdqa	32*5($in_t), $T2b
+
+	vpcmpeqd	$INDEX, $M0, $TMP0
+	vpcmpeqd	$INDEX, $M1, $TMP1
+	vpcmpeqd	$INDEX, $M2, $TMP2
+
+	vpaddd	$THREE, $M0, $M0
+	vpaddd	$THREE, $M1, $M1
+	vpaddd	$THREE, $M2, $M2
+	lea	32*6($in_t), $in_t
+
+	vpand	$TMP0, $T0a, $T0a
+	vpand	$TMP0, $T0b, $T0b
+	vpand	$TMP1, $T1a, $T1a
+	vpand	$TMP1, $T1b, $T1b
+	vpand	$TMP2, $T2a, $T2a
+	vpand	$TMP2, $T2b, $T2b
+
+	vpxor	$T0a, $Ra, $Ra
+	vpxor	$T0b, $Rb, $Rb
+	vpxor	$T1a, $Ra, $Ra
+	vpxor	$T1b, $Rb, $Rb
+	vpxor	$T2a, $Ra, $Ra
+	vpxor	$T2b, $Rb, $Rb
+
+	dec %rax
+	jnz .Lselect_loop_avx2_w7
+
+
+	vmovdqa	32*0($in_t), $T0a
+	vmovdqa	32*1($in_t), $T0b
+
+	vpcmpeqd	$INDEX, $M0, $TMP0
+
+	vpand	$TMP0, $T0a, $T0a
+	vpand	$TMP0, $T0b, $T0b
+
+	vpxor	$T0a, $Ra, $Ra
+	vpxor	$T0b, $Rb, $Rb
+
+	vmovdqu $Ra, 32*0($val)
+	vmovdqu $Rb, 32*1($val)
+	vzeroupper
+___
+$code.=<<___	if ($win64);
+	movaps	(%rsp), %xmm6
+	movaps	0x10(%rsp), %xmm7
+	movaps	0x20(%rsp), %xmm8
+	movaps	0x30(%rsp), %xmm9
+	movaps	0x40(%rsp), %xmm10
+	movaps	0x50(%rsp), %xmm11
+	movaps	0x60(%rsp), %xmm12
+	movaps	0x70(%rsp), %xmm13
+	movaps	0x80(%rsp), %xmm14
+	movaps	0x90(%rsp), %xmm15
+	lea	0xa8(%rsp), %rsp
+.LSEH_end_ecp_nistz256_avx2_gather_w7:
+___
+$code.=<<___;
+	ret
+.size	ecp_nistz256_avx2_gather_w7,.-ecp_nistz256_avx2_gather_w7
+___
+} else {
+$code.=<<___;
+.globl	ecp_nistz256_avx2_gather_w7
+.type	ecp_nistz256_avx2_gather_w7,\@function,3
+.align	32
+ecp_nistz256_avx2_gather_w7:
+	.byte	0x0f,0x0b	# ud2
+	ret
+.size	ecp_nistz256_avx2_gather_w7,.-ecp_nistz256_avx2_gather_w7
+___
+}
+{{{
+########################################################################
+# This block implements higher level point_double, point_add and
+# point_add_affine. The key to performance in this case is to allow
+# out-of-order execution logic to overlap computations from next step
+# with tail processing from current step. By using tailored calling
+# sequence we minimize inter-step overhead to give processor better
+# shot at overlapping operations...
+#
+# You will notice that input data is copied to stack. Trouble is that
+# there are no registers to spare for holding original pointers and
+# reloading them, pointers, would create undesired dependencies on
+# effective addresses calculation paths. In other words it's too done
+# to favour out-of-order execution logic.
+#						
+
+my ($r_ptr,$a_ptr,$b_org,$b_ptr)=("%rdi","%rsi","%rdx","%rbx");
+my ($acc0,$acc1,$acc2,$acc3,$acc4,$acc5,$acc6,$acc7)=map("%r$_",(8..15));
+my ($t0,$t1,$t2,$t3,$t4)=("%rax","%rbp","%rcx",$acc4,$acc4);
+my ($poly1,$poly3)=($acc6,$acc7);
+
+sub load_for_mul () {
+my ($a,$b,$src0) = @_;
+my $bias = $src0 eq "%rax" ? 0 : -128;
+
+"	mov	$b, $src0
+	lea	$b, $b_ptr
+	mov	8*0+$a, $acc1
+	mov	8*1+$a, $acc2
+	lea	$bias+$a, $a_ptr
+	mov	8*2+$a, $acc3
+	mov	8*3+$a, $acc4"
+}
+
+sub load_for_sqr () {
+my ($a,$src0) = @_;
+my $bias = $src0 eq "%rax" ? 0 : -128;
+
+"	mov	8*0+$a, $src0
+	mov	8*1+$a, $acc6
+	lea	$bias+$a, $a_ptr
+	mov	8*2+$a, $acc7
+	mov	8*3+$a, $acc0"
+}
+
+									{
+########################################################################
+# operate in 4-5-0-1 "name space" that matches multiplication output
+#
+my ($a0,$a1,$a2,$a3,$t3,$t4)=($acc4,$acc5,$acc0,$acc1,$acc2,$acc3);
+
+$code.=<<___;
+.type	__ecp_nistz256_add_toq,\@abi-omnipotent
+.align	32
+__ecp_nistz256_add_toq:
+	xor	$t4,$t4
+	add	8*0($b_ptr), $a0
+	adc	8*1($b_ptr), $a1
+	 mov	$a0, $t0
+	adc	8*2($b_ptr), $a2
+	adc	8*3($b_ptr), $a3
+	 mov	$a1, $t1
+	adc	\$0, $t4
+
+	sub	\$-1, $a0
+	 mov	$a2, $t2
+	sbb	$poly1, $a1
+	sbb	\$0, $a2
+	 mov	$a3, $t3
+	sbb	$poly3, $a3
+	sbb	\$0, $t4
+
+	cmovc	$t0, $a0
+	cmovc	$t1, $a1
+	mov	$a0, 8*0($r_ptr)
+	cmovc	$t2, $a2
+	mov	$a1, 8*1($r_ptr)
+	cmovc	$t3, $a3
+	mov	$a2, 8*2($r_ptr)
+	mov	$a3, 8*3($r_ptr)
+
+	ret
+.size	__ecp_nistz256_add_toq,.-__ecp_nistz256_add_toq
+
+.type	__ecp_nistz256_sub_fromq,\@abi-omnipotent
+.align	32
+__ecp_nistz256_sub_fromq:
+	sub	8*0($b_ptr), $a0
+	sbb	8*1($b_ptr), $a1
+	 mov	$a0, $t0
+	sbb	8*2($b_ptr), $a2
+	sbb	8*3($b_ptr), $a3
+	 mov	$a1, $t1
+	sbb	$t4, $t4
+
+	add	\$-1, $a0
+	 mov	$a2, $t2
+	adc	$poly1, $a1
+	adc	\$0, $a2
+	 mov	$a3, $t3
+	adc	$poly3, $a3
+	test	$t4, $t4
+
+	cmovz	$t0, $a0
+	cmovz	$t1, $a1
+	mov	$a0, 8*0($r_ptr)
+	cmovz	$t2, $a2
+	mov	$a1, 8*1($r_ptr)
+	cmovz	$t3, $a3
+	mov	$a2, 8*2($r_ptr)
+	mov	$a3, 8*3($r_ptr)
+
+	ret
+.size	__ecp_nistz256_sub_fromq,.-__ecp_nistz256_sub_fromq
+
+.type	__ecp_nistz256_subq,\@abi-omnipotent
+.align	32
+__ecp_nistz256_subq:
+	sub	$a0, $t0
+	sbb	$a1, $t1
+	 mov	$t0, $a0
+	sbb	$a2, $t2
+	sbb	$a3, $t3
+	 mov	$t1, $a1
+	sbb	$t4, $t4
+
+	add	\$-1, $t0
+	 mov	$t2, $a2
+	adc	$poly1, $t1
+	adc	\$0, $t2
+	 mov	$t3, $a3
+	adc	$poly3, $t3
+	test	$t4, $t4
+
+	cmovnz	$t0, $a0
+	cmovnz	$t1, $a1
+	cmovnz	$t2, $a2
+	cmovnz	$t3, $a3
+
+	ret
+.size	__ecp_nistz256_subq,.-__ecp_nistz256_subq
+
+.type	__ecp_nistz256_mul_by_2q,\@abi-omnipotent
+.align	32
+__ecp_nistz256_mul_by_2q:
+	xor	$t4, $t4
+	add	$a0, $a0		# a0:a3+a0:a3
+	adc	$a1, $a1
+	 mov	$a0, $t0
+	adc	$a2, $a2
+	adc	$a3, $a3
+	 mov	$a1, $t1
+	adc	\$0, $t4
+
+	sub	\$-1, $a0
+	 mov	$a2, $t2
+	sbb	$poly1, $a1
+	sbb	\$0, $a2
+	 mov	$a3, $t3
+	sbb	$poly3, $a3
+	sbb	\$0, $t4
+
+	cmovc	$t0, $a0
+	cmovc	$t1, $a1
+	mov	$a0, 8*0($r_ptr)
+	cmovc	$t2, $a2
+	mov	$a1, 8*1($r_ptr)
+	cmovc	$t3, $a3
+	mov	$a2, 8*2($r_ptr)
+	mov	$a3, 8*3($r_ptr)
+
+	ret
+.size	__ecp_nistz256_mul_by_2q,.-__ecp_nistz256_mul_by_2q
+___
+									}
+sub gen_double () {
+    my $x = shift;
+    my ($src0,$sfx,$bias);
+    my ($S,$M,$Zsqr,$in_x,$tmp0)=map(32*$_,(0..4));
+
+    if ($x ne "x") {
+	$src0 = "%rax";
+	$sfx  = "";
+	$bias = 0;
+
+$code.=<<___;
+.globl	ecp_nistz256_point_double
+.type	ecp_nistz256_point_double,\@function,2
+.align	32
+ecp_nistz256_point_double:
+___
+$code.=<<___	if ($addx);
+	mov	\$0x80100, %ecx
+	and	OPENSSL_ia32cap_P+8(%rip), %ecx
+	cmp	\$0x80100, %ecx
+	je	.Lpoint_doublex
+___
+    } else {
+	$src0 = "%rdx";
+	$sfx  = "x";
+	$bias = 128;
+
+$code.=<<___;
+.type	ecp_nistz256_point_doublex,\@function,2
+.align	32
+ecp_nistz256_point_doublex:
+.Lpoint_doublex:
+___
+    }
+$code.=<<___;
+	push	%rbp
+	push	%rbx
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+	sub	\$32*5+8, %rsp
+
+.Lpoint_double_shortcut$x:
+	movdqu	0x00($a_ptr), %xmm0		# copy	*(P256_POINT *)$a_ptr.x
+	mov	$a_ptr, $b_ptr			# backup copy
+	movdqu	0x10($a_ptr), %xmm1
+	 mov	0x20+8*0($a_ptr), $acc4		# load in_y in "5-4-0-1" order
+	 mov	0x20+8*1($a_ptr), $acc5
+	 mov	0x20+8*2($a_ptr), $acc0
+	 mov	0x20+8*3($a_ptr), $acc1
+	 mov	.Lpoly+8*1(%rip), $poly1
+	 mov	.Lpoly+8*3(%rip), $poly3
+	movdqa	%xmm0, $in_x(%rsp)
+	movdqa	%xmm1, $in_x+0x10(%rsp)
+	lea	0x20($r_ptr), $acc2
+	lea	0x40($r_ptr), $acc3
+	movq	$r_ptr, %xmm0
+	movq	$acc2, %xmm1
+	movq	$acc3, %xmm2
+
+	lea	$S(%rsp), $r_ptr
+	call	__ecp_nistz256_mul_by_2$x	# p256_mul_by_2(S, in_y);
+
+	mov	0x40+8*0($a_ptr), $src0
+	mov	0x40+8*1($a_ptr), $acc6
+	mov	0x40+8*2($a_ptr), $acc7
+	mov	0x40+8*3($a_ptr), $acc0
+	lea	0x40-$bias($a_ptr), $a_ptr
+	lea	$Zsqr(%rsp), $r_ptr
+	call	__ecp_nistz256_sqr_mont$x	# p256_sqr_mont(Zsqr, in_z);
+
+	`&load_for_sqr("$S(%rsp)", "$src0")`
+	lea	$S(%rsp), $r_ptr
+	call	__ecp_nistz256_sqr_mont$x	# p256_sqr_mont(S, S);
+
+	mov	0x20($b_ptr), $src0		# $b_ptr is still valid
+	mov	0x40+8*0($b_ptr), $acc1
+	mov	0x40+8*1($b_ptr), $acc2
+	mov	0x40+8*2($b_ptr), $acc3
+	mov	0x40+8*3($b_ptr), $acc4
+	lea	0x40-$bias($b_ptr), $a_ptr
+	lea	0x20($b_ptr), $b_ptr
+	movq	%xmm2, $r_ptr
+	call	__ecp_nistz256_mul_mont$x	# p256_mul_mont(res_z, in_z, in_y);
+	call	__ecp_nistz256_mul_by_2$x	# p256_mul_by_2(res_z, res_z);
+
+	mov	$in_x+8*0(%rsp), $acc4		# "5-4-0-1" order
+	mov	$in_x+8*1(%rsp), $acc5
+	lea	$Zsqr(%rsp), $b_ptr
+	mov	$in_x+8*2(%rsp), $acc0
+	mov	$in_x+8*3(%rsp), $acc1
+	lea	$M(%rsp), $r_ptr
+	call	__ecp_nistz256_add_to$x		# p256_add(M, in_x, Zsqr);
+
+	mov	$in_x+8*0(%rsp), $acc4		# "5-4-0-1" order
+	mov	$in_x+8*1(%rsp), $acc5
+	lea	$Zsqr(%rsp), $b_ptr
+	mov	$in_x+8*2(%rsp), $acc0
+	mov	$in_x+8*3(%rsp), $acc1
+	lea	$Zsqr(%rsp), $r_ptr
+	call	__ecp_nistz256_sub_from$x	# p256_sub(Zsqr, in_x, Zsqr);
+
+	`&load_for_sqr("$S(%rsp)", "$src0")`
+	movq	%xmm1, $r_ptr
+	call	__ecp_nistz256_sqr_mont$x	# p256_sqr_mont(res_y, S);
+___
+{	
+######## ecp_nistz256_div_by_2(res_y, res_y); ##########################
+# operate in 4-5-6-7 "name space" that matches squaring output
+#
+my ($poly1,$poly3)=($a_ptr,$t1);
+my ($a0,$a1,$a2,$a3,$t3,$t4,$t1)=($acc4,$acc5,$acc6,$acc7,$acc0,$acc1,$acc2);
+
+$code.=<<___;
+	xor	$t4, $t4
+	mov	$a0, $t0
+	add	\$-1, $a0
+	mov	$a1, $t1
+	adc	$poly1, $a1
+	mov	$a2, $t2
+	adc	\$0, $a2
+	mov	$a3, $t3
+	adc	$poly3, $a3
+	adc	\$0, $t4
+	xor	$a_ptr, $a_ptr		# borrow $a_ptr
+	test	\$1, $t0
+
+	cmovz	$t0, $a0
+	cmovz	$t1, $a1
+	cmovz	$t2, $a2
+	cmovz	$t3, $a3
+	cmovz	$a_ptr, $t4
+
+	mov	$a1, $t0		# a0:a3>>1
+	shr	\$1, $a0
+	shl	\$63, $t0
+	mov	$a2, $t1
+	shr	\$1, $a1
+	or	$t0, $a0
+	shl	\$63, $t1
+	mov	$a3, $t2
+	shr	\$1, $a2
+	or	$t1, $a1
+	shl	\$63, $t2
+	mov	$a0, 8*0($r_ptr)
+	shr	\$1, $a3
+	mov	$a1, 8*1($r_ptr)
+	shl	\$63, $t4
+	or	$t2, $a2
+	or	$t4, $a3
+	mov	$a2, 8*2($r_ptr)
+	mov	$a3, 8*3($r_ptr)
+___
+}
+$code.=<<___;
+	`&load_for_mul("$M(%rsp)", "$Zsqr(%rsp)", "$src0")`
+	lea	$M(%rsp), $r_ptr
+	call	__ecp_nistz256_mul_mont$x	# p256_mul_mont(M, M, Zsqr);
+
+	lea	$tmp0(%rsp), $r_ptr
+	call	__ecp_nistz256_mul_by_2$x
+
+	lea	$M(%rsp), $b_ptr
+	lea	$M(%rsp), $r_ptr
+	call	__ecp_nistz256_add_to$x		# p256_mul_by_3(M, M);
+
+	`&load_for_mul("$S(%rsp)", "$in_x(%rsp)", "$src0")`
+	lea	$S(%rsp), $r_ptr
+	call	__ecp_nistz256_mul_mont$x	# p256_mul_mont(S, S, in_x);
+
+	lea	$tmp0(%rsp), $r_ptr
+	call	__ecp_nistz256_mul_by_2$x	# p256_mul_by_2(tmp0, S);
+
+	`&load_for_sqr("$M(%rsp)", "$src0")`
+	movq	%xmm0, $r_ptr
+	call	__ecp_nistz256_sqr_mont$x	# p256_sqr_mont(res_x, M);
+
+	lea	$tmp0(%rsp), $b_ptr
+	mov	$acc6, $acc0			# harmonize sqr output and sub input
+	mov	$acc7, $acc1
+	mov	$a_ptr, $poly1
+	mov	$t1, $poly3
+	call	__ecp_nistz256_sub_from$x	# p256_sub(res_x, res_x, tmp0);
+
+	mov	$S+8*0(%rsp), $t0
+	mov	$S+8*1(%rsp), $t1
+	mov	$S+8*2(%rsp), $t2
+	mov	$S+8*3(%rsp), $acc2		# "4-5-0-1" order
+	lea	$S(%rsp), $r_ptr
+	call	__ecp_nistz256_sub$x		# p256_sub(S, S, res_x);
+
+	mov	$M(%rsp), $src0
+	lea	$M(%rsp), $b_ptr
+	mov	$acc4, $acc6			# harmonize sub output and mul input
+	xor	%ecx, %ecx
+	mov	$acc4, $S+8*0(%rsp)		# have to save:-(	
+	mov	$acc5, $acc2
+	mov	$acc5, $S+8*1(%rsp)
+	cmovz	$acc0, $acc3
+	mov	$acc0, $S+8*2(%rsp)
+	lea	$S-$bias(%rsp), $a_ptr
+	cmovz	$acc1, $acc4
+	mov	$acc1, $S+8*3(%rsp)
+	mov	$acc6, $acc1
+	lea	$S(%rsp), $r_ptr
+	call	__ecp_nistz256_mul_mont$x	# p256_mul_mont(S, S, M);
+
+	movq	%xmm1, $b_ptr
+	movq	%xmm1, $r_ptr
+	call	__ecp_nistz256_sub_from$x	# p256_sub(res_y, S, res_y);
+
+	add	\$32*5+8, %rsp
+	pop	%r15
+	pop	%r14
+	pop	%r13
+	pop	%r12
+	pop	%rbx
+	pop	%rbp
+	ret
+.size	ecp_nistz256_point_double$sfx,.-ecp_nistz256_point_double$sfx
+___
+}
+&gen_double("q");
+
+sub gen_add () {
+    my $x = shift;
+    my ($src0,$sfx,$bias);
+    my ($H,$Hsqr,$R,$Rsqr,$Hcub,
+	$U1,$U2,$S1,$S2,
+	$res_x,$res_y,$res_z,
+	$in1_x,$in1_y,$in1_z,
+	$in2_x,$in2_y,$in2_z)=map(32*$_,(0..17));
+    my ($Z1sqr, $Z2sqr) = ($Hsqr, $Rsqr);
+
+    if ($x ne "x") {
+	$src0 = "%rax";
+	$sfx  = "";
+	$bias = 0;
+
+$code.=<<___;
+.globl	ecp_nistz256_point_add
+.type	ecp_nistz256_point_add,\@function,3
+.align	32
+ecp_nistz256_point_add:
+___
+$code.=<<___	if ($addx);
+	mov	\$0x80100, %ecx
+	and	OPENSSL_ia32cap_P+8(%rip), %ecx
+	cmp	\$0x80100, %ecx
+	je	.Lpoint_addx
+___
+    } else {
+	$src0 = "%rdx";
+	$sfx  = "x";
+	$bias = 128;
+
+$code.=<<___;
+.type	ecp_nistz256_point_addx,\@function,3
+.align	32
+ecp_nistz256_point_addx:
+.Lpoint_addx:
+___
+    }
+$code.=<<___;
+	push	%rbp
+	push	%rbx
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+	sub	\$32*18+8, %rsp
+
+	movdqu	0x00($a_ptr), %xmm0		# copy	*(P256_POINT *)$a_ptr
+	movdqu	0x10($a_ptr), %xmm1
+	movdqu	0x20($a_ptr), %xmm2
+	movdqu	0x30($a_ptr), %xmm3
+	movdqu	0x40($a_ptr), %xmm4
+	movdqu	0x50($a_ptr), %xmm5
+	mov	$a_ptr, $b_ptr			# reassign
+	mov	$b_org, $a_ptr			# reassign
+	movdqa	%xmm0, $in1_x(%rsp)
+	movdqa	%xmm1, $in1_x+0x10(%rsp)
+	movdqa	%xmm2, $in1_y(%rsp)
+	movdqa	%xmm3, $in1_y+0x10(%rsp)
+	movdqa	%xmm4, $in1_z(%rsp)
+	movdqa	%xmm5, $in1_z+0x10(%rsp)
+	por	%xmm4, %xmm5
+
+	movdqu	0x00($a_ptr), %xmm0		# copy	*(P256_POINT *)$b_ptr
+	 pshufd	\$0xb1, %xmm5, %xmm3
+	movdqu	0x10($a_ptr), %xmm1
+	movdqu	0x20($a_ptr), %xmm2
+	 por	%xmm3, %xmm5
+	movdqu	0x30($a_ptr), %xmm3
+	 mov	0x40+8*0($a_ptr), $src0		# load original in2_z
+	 mov	0x40+8*1($a_ptr), $acc6
+	 mov	0x40+8*2($a_ptr), $acc7
+	 mov	0x40+8*3($a_ptr), $acc0
+	movdqa	%xmm0, $in2_x(%rsp)
+	 pshufd	\$0x1e, %xmm5, %xmm4
+	movdqa	%xmm1, $in2_x+0x10(%rsp)
+	movdqu	0x40($a_ptr),%xmm0		# in2_z again
+	movdqu	0x50($a_ptr),%xmm1
+	movdqa	%xmm2, $in2_y(%rsp)
+	movdqa	%xmm3, $in2_y+0x10(%rsp)
+	 por	%xmm4, %xmm5
+	 pxor	%xmm4, %xmm4
+	por	%xmm0, %xmm1
+	 movq	$r_ptr, %xmm0			# save $r_ptr
+
+	lea	0x40-$bias($a_ptr), $a_ptr	# $a_ptr is still valid
+	 mov	$src0, $in2_z+8*0(%rsp)		# make in2_z copy
+	 mov	$acc6, $in2_z+8*1(%rsp)
+	 mov	$acc7, $in2_z+8*2(%rsp)
+	 mov	$acc0, $in2_z+8*3(%rsp)
+	lea	$Z2sqr(%rsp), $r_ptr		# Z2^2
+	call	__ecp_nistz256_sqr_mont$x	# p256_sqr_mont(Z2sqr, in2_z);
+
+	pcmpeqd	%xmm4, %xmm5
+	pshufd	\$0xb1, %xmm1, %xmm4
+	por	%xmm1, %xmm4
+	pshufd	\$0, %xmm5, %xmm5		# in1infty
+	pshufd	\$0x1e, %xmm4, %xmm3
+	por	%xmm3, %xmm4
+	pxor	%xmm3, %xmm3
+	pcmpeqd	%xmm3, %xmm4
+	pshufd	\$0, %xmm4, %xmm4		# in2infty
+	 mov	0x40+8*0($b_ptr), $src0		# load original in1_z
+	 mov	0x40+8*1($b_ptr), $acc6
+	 mov	0x40+8*2($b_ptr), $acc7
+	 mov	0x40+8*3($b_ptr), $acc0
+	movq	$b_ptr, %xmm1
+
+	lea	0x40-$bias($b_ptr), $a_ptr
+	lea	$Z1sqr(%rsp), $r_ptr		# Z1^2
+	call	__ecp_nistz256_sqr_mont$x	# p256_sqr_mont(Z1sqr, in1_z);
+
+	`&load_for_mul("$Z2sqr(%rsp)", "$in2_z(%rsp)", "$src0")`
+	lea	$S1(%rsp), $r_ptr		# S1 = Z2^3
+	call	__ecp_nistz256_mul_mont$x	# p256_mul_mont(S1, Z2sqr, in2_z);
+
+	`&load_for_mul("$Z1sqr(%rsp)", "$in1_z(%rsp)", "$src0")`
+	lea	$S2(%rsp), $r_ptr		# S2 = Z1^3
+	call	__ecp_nistz256_mul_mont$x	# p256_mul_mont(S2, Z1sqr, in1_z);
+
+	`&load_for_mul("$S1(%rsp)", "$in1_y(%rsp)", "$src0")`
+	lea	$S1(%rsp), $r_ptr		# S1 = Y1*Z2^3
+	call	__ecp_nistz256_mul_mont$x	# p256_mul_mont(S1, S1, in1_y);
+
+	`&load_for_mul("$S2(%rsp)", "$in2_y(%rsp)", "$src0")`
+	lea	$S2(%rsp), $r_ptr		# S2 = Y2*Z1^3
+	call	__ecp_nistz256_mul_mont$x	# p256_mul_mont(S2, S2, in2_y);
+
+	lea	$S1(%rsp), $b_ptr
+	lea	$R(%rsp), $r_ptr		# R = S2 - S1
+	call	__ecp_nistz256_sub_from$x	# p256_sub(R, S2, S1);
+
+	or	$acc5, $acc4			# see if result is zero
+	movdqa	%xmm4, %xmm2
+	or	$acc0, $acc4
+	or	$acc1, $acc4
+	por	%xmm5, %xmm2			# in1infty || in2infty
+	movq	$acc4, %xmm3
+
+	`&load_for_mul("$Z2sqr(%rsp)", "$in1_x(%rsp)", "$src0")`
+	lea	$U1(%rsp), $r_ptr		# U1 = X1*Z2^2
+	call	__ecp_nistz256_mul_mont$x	# p256_mul_mont(U1, in1_x, Z2sqr);
+
+	`&load_for_mul("$Z1sqr(%rsp)", "$in2_x(%rsp)", "$src0")`
+	lea	$U2(%rsp), $r_ptr		# U2 = X2*Z1^2
+	call	__ecp_nistz256_mul_mont$x	# p256_mul_mont(U2, in2_x, Z1sqr);
+
+	lea	$U1(%rsp), $b_ptr
+	lea	$H(%rsp), $r_ptr		# H = U2 - U1
+	call	__ecp_nistz256_sub_from$x	# p256_sub(H, U2, U1);
+
+	or	$acc5, $acc4			# see if result is zero
+	or	$acc0, $acc4
+	or	$acc1, $acc4
+
+	.byte	0x3e				# predict taken
+	jnz	.Ladd_proceed$x			# is_equal(U1,U2)?
+	movq	%xmm2, $acc0
+	movq	%xmm3, $acc1
+	test	$acc0, $acc0
+	jnz	.Ladd_proceed$x			# (in1infty || in2infty)?
+	test	$acc1, $acc1
+	jz	.Ladd_double$x			# is_equal(S1,S2)?
+
+	movq	%xmm0, $r_ptr			# restore $r_ptr
+	pxor	%xmm0, %xmm0
+	movdqu	%xmm0, 0x00($r_ptr)
+	movdqu	%xmm0, 0x10($r_ptr)
+	movdqu	%xmm0, 0x20($r_ptr)
+	movdqu	%xmm0, 0x30($r_ptr)
+	movdqu	%xmm0, 0x40($r_ptr)
+	movdqu	%xmm0, 0x50($r_ptr)
+	jmp	.Ladd_done$x
+
+.align	32
+.Ladd_double$x:
+	movq	%xmm1, $a_ptr			# restore $a_ptr
+	movq	%xmm0, $r_ptr			# restore $r_ptr
+	add	\$`32*(18-5)`, %rsp		# difference in frame sizes
+	jmp	.Lpoint_double_shortcut$x
+
+.align	32
+.Ladd_proceed$x:
+	`&load_for_sqr("$R(%rsp)", "$src0")`
+	lea	$Rsqr(%rsp), $r_ptr		# R^2
+	call	__ecp_nistz256_sqr_mont$x	# p256_sqr_mont(Rsqr, R);
+
+	`&load_for_mul("$H(%rsp)", "$in1_z(%rsp)", "$src0")`
+	lea	$res_z(%rsp), $r_ptr		# Z3 = H*Z1*Z2
+	call	__ecp_nistz256_mul_mont$x	# p256_mul_mont(res_z, H, in1_z);
+
+	`&load_for_sqr("$H(%rsp)", "$src0")`
+	lea	$Hsqr(%rsp), $r_ptr		# H^2
+	call	__ecp_nistz256_sqr_mont$x	# p256_sqr_mont(Hsqr, H);
+
+	`&load_for_mul("$res_z(%rsp)", "$in2_z(%rsp)", "$src0")`
+	lea	$res_z(%rsp), $r_ptr		# Z3 = H*Z1*Z2
+	call	__ecp_nistz256_mul_mont$x	# p256_mul_mont(res_z, res_z, in2_z);
+
+	`&load_for_mul("$Hsqr(%rsp)", "$H(%rsp)", "$src0")`
+	lea	$Hcub(%rsp), $r_ptr		# H^3
+	call	__ecp_nistz256_mul_mont$x	# p256_mul_mont(Hcub, Hsqr, H);
+
+	`&load_for_mul("$Hsqr(%rsp)", "$U1(%rsp)", "$src0")`
+	lea	$U2(%rsp), $r_ptr		# U1*H^2
+	call	__ecp_nistz256_mul_mont$x	# p256_mul_mont(U2, U1, Hsqr);
+___
+{
+#######################################################################
+# operate in 4-5-0-1 "name space" that matches multiplication output
+#
+my ($acc0,$acc1,$acc2,$acc3,$t3,$t4)=($acc4,$acc5,$acc0,$acc1,$acc2,$acc3);
+my ($poly1, $poly3)=($acc6,$acc7);
+
+$code.=<<___;
+	#lea	$U2(%rsp), $a_ptr
+	#lea	$Hsqr(%rsp), $r_ptr	# 2*U1*H^2
+	#call	__ecp_nistz256_mul_by_2	# ecp_nistz256_mul_by_2(Hsqr, U2);
+
+	xor	$t4, $t4
+	add	$acc0, $acc0		# a0:a3+a0:a3
+	lea	$Rsqr(%rsp), $a_ptr
+	adc	$acc1, $acc1
+	 mov	$acc0, $t0
+	adc	$acc2, $acc2
+	adc	$acc3, $acc3
+	 mov	$acc1, $t1
+	adc	\$0, $t4
+
+	sub	\$-1, $acc0
+	 mov	$acc2, $t2
+	sbb	$poly1, $acc1
+	sbb	\$0, $acc2
+	 mov	$acc3, $t3
+	sbb	$poly3, $acc3
+	sbb	\$0, $t4
+
+	cmovc	$t0, $acc0
+	mov	8*0($a_ptr), $t0
+	cmovc	$t1, $acc1
+	mov	8*1($a_ptr), $t1
+	cmovc	$t2, $acc2
+	mov	8*2($a_ptr), $t2
+	cmovc	$t3, $acc3
+	mov	8*3($a_ptr), $t3
+
+	call	__ecp_nistz256_sub$x		# p256_sub(res_x, Rsqr, Hsqr);
+
+	lea	$Hcub(%rsp), $b_ptr
+	lea	$res_x(%rsp), $r_ptr
+	call	__ecp_nistz256_sub_from$x	# p256_sub(res_x, res_x, Hcub);
+
+	mov	$U2+8*0(%rsp), $t0
+	mov	$U2+8*1(%rsp), $t1
+	mov	$U2+8*2(%rsp), $t2
+	mov	$U2+8*3(%rsp), $t3
+	lea	$res_y(%rsp), $r_ptr
+
+	call	__ecp_nistz256_sub$x		# p256_sub(res_y, U2, res_x);
+
+	mov	$acc0, 8*0($r_ptr)		# save the result, as
+	mov	$acc1, 8*1($r_ptr)		# __ecp_nistz256_sub doesn't
+	mov	$acc2, 8*2($r_ptr)
+	mov	$acc3, 8*3($r_ptr)
+___
+}
+$code.=<<___;
+	`&load_for_mul("$S1(%rsp)", "$Hcub(%rsp)", "$src0")`
+	lea	$S2(%rsp), $r_ptr
+	call	__ecp_nistz256_mul_mont$x	# p256_mul_mont(S2, S1, Hcub);
+
+	`&load_for_mul("$R(%rsp)", "$res_y(%rsp)", "$src0")`
+	lea	$res_y(%rsp), $r_ptr
+	call	__ecp_nistz256_mul_mont$x	# p256_mul_mont(res_y, R, res_y);
+
+	lea	$S2(%rsp), $b_ptr
+	lea	$res_y(%rsp), $r_ptr
+	call	__ecp_nistz256_sub_from$x	# p256_sub(res_y, res_y, S2);
+
+	movq	%xmm0, $r_ptr		# restore $r_ptr
+
+	movdqa	%xmm5, %xmm0		# copy_conditional(res_z, in2_z, in1infty);
+	movdqa	%xmm5, %xmm1
+	pandn	$res_z(%rsp), %xmm0
+	movdqa	%xmm5, %xmm2
+	pandn	$res_z+0x10(%rsp), %xmm1
+	movdqa	%xmm5, %xmm3
+	pand	$in2_z(%rsp), %xmm2
+	pand	$in2_z+0x10(%rsp), %xmm3
+	por	%xmm0, %xmm2
+	por	%xmm1, %xmm3
+
+	movdqa	%xmm4, %xmm0		# copy_conditional(res_z, in1_z, in2infty);
+	movdqa	%xmm4, %xmm1
+	pandn	%xmm2, %xmm0
+	movdqa	%xmm4, %xmm2
+	pandn	%xmm3, %xmm1
+	movdqa	%xmm4, %xmm3
+	pand	$in1_z(%rsp), %xmm2
+	pand	$in1_z+0x10(%rsp), %xmm3
+	por	%xmm0, %xmm2
+	por	%xmm1, %xmm3
+	movdqu	%xmm2, 0x40($r_ptr)
+	movdqu	%xmm3, 0x50($r_ptr)
+
+	movdqa	%xmm5, %xmm0		# copy_conditional(res_x, in2_x, in1infty);
+	movdqa	%xmm5, %xmm1
+	pandn	$res_x(%rsp), %xmm0
+	movdqa	%xmm5, %xmm2
+	pandn	$res_x+0x10(%rsp), %xmm1
+	movdqa	%xmm5, %xmm3
+	pand	$in2_x(%rsp), %xmm2
+	pand	$in2_x+0x10(%rsp), %xmm3
+	por	%xmm0, %xmm2
+	por	%xmm1, %xmm3
+
+	movdqa	%xmm4, %xmm0		# copy_conditional(res_x, in1_x, in2infty);
+	movdqa	%xmm4, %xmm1
+	pandn	%xmm2, %xmm0
+	movdqa	%xmm4, %xmm2
+	pandn	%xmm3, %xmm1
+	movdqa	%xmm4, %xmm3
+	pand	$in1_x(%rsp), %xmm2
+	pand	$in1_x+0x10(%rsp), %xmm3
+	por	%xmm0, %xmm2
+	por	%xmm1, %xmm3
+	movdqu	%xmm2, 0x00($r_ptr)
+	movdqu	%xmm3, 0x10($r_ptr)
+
+	movdqa	%xmm5, %xmm0		# copy_conditional(res_y, in2_y, in1infty);
+	movdqa	%xmm5, %xmm1
+	pandn	$res_y(%rsp), %xmm0
+	movdqa	%xmm5, %xmm2
+	pandn	$res_y+0x10(%rsp), %xmm1
+	movdqa	%xmm5, %xmm3
+	pand	$in2_y(%rsp), %xmm2
+	pand	$in2_y+0x10(%rsp), %xmm3
+	por	%xmm0, %xmm2
+	por	%xmm1, %xmm3
+
+	movdqa	%xmm4, %xmm0		# copy_conditional(res_y, in1_y, in2infty);
+	movdqa	%xmm4, %xmm1
+	pandn	%xmm2, %xmm0
+	movdqa	%xmm4, %xmm2
+	pandn	%xmm3, %xmm1
+	movdqa	%xmm4, %xmm3
+	pand	$in1_y(%rsp), %xmm2
+	pand	$in1_y+0x10(%rsp), %xmm3
+	por	%xmm0, %xmm2
+	por	%xmm1, %xmm3
+	movdqu	%xmm2, 0x20($r_ptr)
+	movdqu	%xmm3, 0x30($r_ptr)
+
+.Ladd_done$x:
+	add	\$32*18+8, %rsp
+	pop	%r15
+	pop	%r14
+	pop	%r13
+	pop	%r12
+	pop	%rbx
+	pop	%rbp
+	ret
+.size	ecp_nistz256_point_add$sfx,.-ecp_nistz256_point_add$sfx
+___
+}
+&gen_add("q");
+
+sub gen_add_affine () {
+    my $x = shift;
+    my ($src0,$sfx,$bias);
+    my ($U2,$S2,$H,$R,$Hsqr,$Hcub,$Rsqr,
+	$res_x,$res_y,$res_z,
+	$in1_x,$in1_y,$in1_z,
+	$in2_x,$in2_y)=map(32*$_,(0..14));
+    my $Z1sqr = $S2;
+
+    if ($x ne "x") {
+	$src0 = "%rax";
+	$sfx  = "";
+	$bias = 0;
+
+$code.=<<___;
+.globl	ecp_nistz256_point_add_affine
+.type	ecp_nistz256_point_add_affine,\@function,3
+.align	32
+ecp_nistz256_point_add_affine:
+___
+$code.=<<___	if ($addx);
+	mov	\$0x80100, %ecx
+	and	OPENSSL_ia32cap_P+8(%rip), %ecx
+	cmp	\$0x80100, %ecx
+	je	.Lpoint_add_affinex
+___
+    } else {
+	$src0 = "%rdx";
+	$sfx  = "x";
+	$bias = 128;
+
+$code.=<<___;
+.type	ecp_nistz256_point_add_affinex,\@function,3
+.align	32
+ecp_nistz256_point_add_affinex:
+.Lpoint_add_affinex:
+___
+    }
+$code.=<<___;
+	push	%rbp
+	push	%rbx
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+	sub	\$32*15+8, %rsp
+
+	movdqu	0x00($a_ptr), %xmm0	# copy	*(P256_POINT *)$a_ptr
+	mov	$b_org, $b_ptr		# reassign
+	movdqu	0x10($a_ptr), %xmm1
+	movdqu	0x20($a_ptr), %xmm2
+	movdqu	0x30($a_ptr), %xmm3
+	movdqu	0x40($a_ptr), %xmm4
+	movdqu	0x50($a_ptr), %xmm5
+	 mov	0x40+8*0($a_ptr), $src0	# load original in1_z
+	 mov	0x40+8*1($a_ptr), $acc6
+	 mov	0x40+8*2($a_ptr), $acc7
+	 mov	0x40+8*3($a_ptr), $acc0
+	movdqa	%xmm0, $in1_x(%rsp)
+	movdqa	%xmm1, $in1_x+0x10(%rsp)
+	movdqa	%xmm2, $in1_y(%rsp)
+	movdqa	%xmm3, $in1_y+0x10(%rsp)
+	movdqa	%xmm4, $in1_z(%rsp)
+	movdqa	%xmm5, $in1_z+0x10(%rsp)
+	por	%xmm4, %xmm5
+
+	movdqu	0x00($b_ptr), %xmm0	# copy	*(P256_POINT_AFFINE *)$b_ptr
+	 pshufd	\$0xb1, %xmm5, %xmm3
+	movdqu	0x10($b_ptr), %xmm1
+	movdqu	0x20($b_ptr), %xmm2
+	 por	%xmm3, %xmm5
+	movdqu	0x30($b_ptr), %xmm3
+	movdqa	%xmm0, $in2_x(%rsp)
+	 pshufd	\$0x1e, %xmm5, %xmm4
+	movdqa	%xmm1, $in2_x+0x10(%rsp)
+	por	%xmm0, %xmm1
+	 movq	$r_ptr, %xmm0		# save $r_ptr
+	movdqa	%xmm2, $in2_y(%rsp)
+	movdqa	%xmm3, $in2_y+0x10(%rsp)
+	por	%xmm2, %xmm3
+	 por	%xmm4, %xmm5
+	 pxor	%xmm4, %xmm4
+	por	%xmm1, %xmm3
+
+	lea	0x40-$bias($a_ptr), $a_ptr	# $a_ptr is still valid
+	lea	$Z1sqr(%rsp), $r_ptr		# Z1^2
+	call	__ecp_nistz256_sqr_mont$x	# p256_sqr_mont(Z1sqr, in1_z);
+
+	pcmpeqd	%xmm4, %xmm5
+	pshufd	\$0xb1, %xmm3, %xmm4
+	 mov	0x00($b_ptr), $src0		# $b_ptr is still valid
+	 #lea	0x00($b_ptr), $b_ptr
+	 mov	$acc4, $acc1			# harmonize sqr output and mul input
+	por	%xmm3, %xmm4
+	pshufd	\$0, %xmm5, %xmm5		# in1infty
+	pshufd	\$0x1e, %xmm4, %xmm3
+	 mov	$acc5, $acc2
+	por	%xmm3, %xmm4
+	pxor	%xmm3, %xmm3
+	 mov	$acc6, $acc3
+	pcmpeqd	%xmm3, %xmm4
+	pshufd	\$0, %xmm4, %xmm4		# in2infty
+
+	lea	$Z1sqr-$bias(%rsp), $a_ptr
+	mov	$acc7, $acc4
+	lea	$U2(%rsp), $r_ptr		# U2 = X2*Z1^2
+	call	__ecp_nistz256_mul_mont$x	# p256_mul_mont(U2, Z1sqr, in2_x);
+
+	lea	$in1_x(%rsp), $b_ptr
+	lea	$H(%rsp), $r_ptr		# H = U2 - U1
+	call	__ecp_nistz256_sub_from$x	# p256_sub(H, U2, in1_x);
+
+	`&load_for_mul("$Z1sqr(%rsp)", "$in1_z(%rsp)", "$src0")`
+	lea	$S2(%rsp), $r_ptr		# S2 = Z1^3
+	call	__ecp_nistz256_mul_mont$x	# p256_mul_mont(S2, Z1sqr, in1_z);
+
+	`&load_for_mul("$H(%rsp)", "$in1_z(%rsp)", "$src0")`
+	lea	$res_z(%rsp), $r_ptr		# Z3 = H*Z1*Z2
+	call	__ecp_nistz256_mul_mont$x	# p256_mul_mont(res_z, H, in1_z);
+
+	`&load_for_mul("$S2(%rsp)", "$in2_y(%rsp)", "$src0")`
+	lea	$S2(%rsp), $r_ptr		# S2 = Y2*Z1^3
+	call	__ecp_nistz256_mul_mont$x	# p256_mul_mont(S2, S2, in2_y);
+
+	lea	$in1_y(%rsp), $b_ptr
+	lea	$R(%rsp), $r_ptr		# R = S2 - S1
+	call	__ecp_nistz256_sub_from$x	# p256_sub(R, S2, in1_y);
+
+	`&load_for_sqr("$H(%rsp)", "$src0")`
+	lea	$Hsqr(%rsp), $r_ptr		# H^2
+	call	__ecp_nistz256_sqr_mont$x	# p256_sqr_mont(Hsqr, H);
+
+	`&load_for_sqr("$R(%rsp)", "$src0")`
+	lea	$Rsqr(%rsp), $r_ptr		# R^2
+	call	__ecp_nistz256_sqr_mont$x	# p256_sqr_mont(Rsqr, R);
+
+	`&load_for_mul("$H(%rsp)", "$Hsqr(%rsp)", "$src0")`
+	lea	$Hcub(%rsp), $r_ptr		# H^3
+	call	__ecp_nistz256_mul_mont$x	# p256_mul_mont(Hcub, Hsqr, H);
+
+	`&load_for_mul("$Hsqr(%rsp)", "$in1_x(%rsp)", "$src0")`
+	lea	$U2(%rsp), $r_ptr		# U1*H^2
+	call	__ecp_nistz256_mul_mont$x	# p256_mul_mont(U2, in1_x, Hsqr);
+___
+{
+#######################################################################
+# operate in 4-5-0-1 "name space" that matches multiplication output
+#
+my ($acc0,$acc1,$acc2,$acc3,$t3,$t4)=($acc4,$acc5,$acc0,$acc1,$acc2,$acc3);
+my ($poly1, $poly3)=($acc6,$acc7);
+
+$code.=<<___;
+	#lea	$U2(%rsp), $a_ptr
+	#lea	$Hsqr(%rsp), $r_ptr	# 2*U1*H^2
+	#call	__ecp_nistz256_mul_by_2	# ecp_nistz256_mul_by_2(Hsqr, U2);
+
+	xor	$t4, $t4
+	add	$acc0, $acc0		# a0:a3+a0:a3
+	lea	$Rsqr(%rsp), $a_ptr
+	adc	$acc1, $acc1
+	 mov	$acc0, $t0
+	adc	$acc2, $acc2
+	adc	$acc3, $acc3
+	 mov	$acc1, $t1
+	adc	\$0, $t4
+
+	sub	\$-1, $acc0
+	 mov	$acc2, $t2
+	sbb	$poly1, $acc1
+	sbb	\$0, $acc2
+	 mov	$acc3, $t3
+	sbb	$poly3, $acc3
+	sbb	\$0, $t4
+
+	cmovc	$t0, $acc0
+	mov	8*0($a_ptr), $t0
+	cmovc	$t1, $acc1
+	mov	8*1($a_ptr), $t1
+	cmovc	$t2, $acc2
+	mov	8*2($a_ptr), $t2
+	cmovc	$t3, $acc3
+	mov	8*3($a_ptr), $t3
+
+	call	__ecp_nistz256_sub$x		# p256_sub(res_x, Rsqr, Hsqr);
+
+	lea	$Hcub(%rsp), $b_ptr
+	lea	$res_x(%rsp), $r_ptr
+	call	__ecp_nistz256_sub_from$x	# p256_sub(res_x, res_x, Hcub);
+
+	mov	$U2+8*0(%rsp), $t0
+	mov	$U2+8*1(%rsp), $t1
+	mov	$U2+8*2(%rsp), $t2
+	mov	$U2+8*3(%rsp), $t3
+	lea	$H(%rsp), $r_ptr
+
+	call	__ecp_nistz256_sub$x		# p256_sub(H, U2, res_x);
+
+	mov	$acc0, 8*0($r_ptr)		# save the result, as
+	mov	$acc1, 8*1($r_ptr)		# __ecp_nistz256_sub doesn't
+	mov	$acc2, 8*2($r_ptr)
+	mov	$acc3, 8*3($r_ptr)
+___
+}
+$code.=<<___;
+	`&load_for_mul("$Hcub(%rsp)", "$in1_y(%rsp)", "$src0")`
+	lea	$S2(%rsp), $r_ptr
+	call	__ecp_nistz256_mul_mont$x	# p256_mul_mont(S2, Hcub, in1_y);
+
+	`&load_for_mul("$H(%rsp)", "$R(%rsp)", "$src0")`
+	lea	$H(%rsp), $r_ptr
+	call	__ecp_nistz256_mul_mont$x	# p256_mul_mont(H, H, R);
+
+	lea	$S2(%rsp), $b_ptr
+	lea	$res_y(%rsp), $r_ptr
+	call	__ecp_nistz256_sub_from$x	# p256_sub(res_y, H, S2);
+
+	movq	%xmm0, $r_ptr		# restore $r_ptr
+
+	movdqa	%xmm5, %xmm0		# copy_conditional(res_z, ONE, in1infty);
+	movdqa	%xmm5, %xmm1
+	pandn	$res_z(%rsp), %xmm0
+	movdqa	%xmm5, %xmm2
+	pandn	$res_z+0x10(%rsp), %xmm1
+	movdqa	%xmm5, %xmm3
+	pand	.LONE_mont(%rip), %xmm2
+	pand	.LONE_mont+0x10(%rip), %xmm3
+	por	%xmm0, %xmm2
+	por	%xmm1, %xmm3
+
+	movdqa	%xmm4, %xmm0		# copy_conditional(res_z, in1_z, in2infty);
+	movdqa	%xmm4, %xmm1
+	pandn	%xmm2, %xmm0
+	movdqa	%xmm4, %xmm2
+	pandn	%xmm3, %xmm1
+	movdqa	%xmm4, %xmm3
+	pand	$in1_z(%rsp), %xmm2
+	pand	$in1_z+0x10(%rsp), %xmm3
+	por	%xmm0, %xmm2
+	por	%xmm1, %xmm3
+	movdqu	%xmm2, 0x40($r_ptr)
+	movdqu	%xmm3, 0x50($r_ptr)
+
+	movdqa	%xmm5, %xmm0		# copy_conditional(res_x, in2_x, in1infty);
+	movdqa	%xmm5, %xmm1
+	pandn	$res_x(%rsp), %xmm0
+	movdqa	%xmm5, %xmm2
+	pandn	$res_x+0x10(%rsp), %xmm1
+	movdqa	%xmm5, %xmm3
+	pand	$in2_x(%rsp), %xmm2
+	pand	$in2_x+0x10(%rsp), %xmm3
+	por	%xmm0, %xmm2
+	por	%xmm1, %xmm3
+
+	movdqa	%xmm4, %xmm0		# copy_conditional(res_x, in1_x, in2infty);
+	movdqa	%xmm4, %xmm1
+	pandn	%xmm2, %xmm0
+	movdqa	%xmm4, %xmm2
+	pandn	%xmm3, %xmm1
+	movdqa	%xmm4, %xmm3
+	pand	$in1_x(%rsp), %xmm2
+	pand	$in1_x+0x10(%rsp), %xmm3
+	por	%xmm0, %xmm2
+	por	%xmm1, %xmm3
+	movdqu	%xmm2, 0x00($r_ptr)
+	movdqu	%xmm3, 0x10($r_ptr)
+
+	movdqa	%xmm5, %xmm0		# copy_conditional(res_y, in2_y, in1infty);
+	movdqa	%xmm5, %xmm1
+	pandn	$res_y(%rsp), %xmm0
+	movdqa	%xmm5, %xmm2
+	pandn	$res_y+0x10(%rsp), %xmm1
+	movdqa	%xmm5, %xmm3
+	pand	$in2_y(%rsp), %xmm2
+	pand	$in2_y+0x10(%rsp), %xmm3
+	por	%xmm0, %xmm2
+	por	%xmm1, %xmm3
+
+	movdqa	%xmm4, %xmm0		# copy_conditional(res_y, in1_y, in2infty);
+	movdqa	%xmm4, %xmm1
+	pandn	%xmm2, %xmm0
+	movdqa	%xmm4, %xmm2
+	pandn	%xmm3, %xmm1
+	movdqa	%xmm4, %xmm3
+	pand	$in1_y(%rsp), %xmm2
+	pand	$in1_y+0x10(%rsp), %xmm3
+	por	%xmm0, %xmm2
+	por	%xmm1, %xmm3
+	movdqu	%xmm2, 0x20($r_ptr)
+	movdqu	%xmm3, 0x30($r_ptr)
+
+	add	\$32*15+8, %rsp
+	pop	%r15
+	pop	%r14
+	pop	%r13
+	pop	%r12
+	pop	%rbx
+	pop	%rbp
+	ret
+.size	ecp_nistz256_point_add_affine$sfx,.-ecp_nistz256_point_add_affine$sfx
+___
+}
+&gen_add_affine("q");
+
+########################################################################
+# AD*X magic
+#
+if ($addx) {								{
+########################################################################
+# operate in 4-5-0-1 "name space" that matches multiplication output
+#
+my ($a0,$a1,$a2,$a3,$t3,$t4)=($acc4,$acc5,$acc0,$acc1,$acc2,$acc3);
+
+$code.=<<___;
+.type	__ecp_nistz256_add_tox,\@abi-omnipotent
+.align	32
+__ecp_nistz256_add_tox:
+	xor	$t4, $t4
+	adc	8*0($b_ptr), $a0
+	adc	8*1($b_ptr), $a1
+	 mov	$a0, $t0
+	adc	8*2($b_ptr), $a2
+	adc	8*3($b_ptr), $a3
+	 mov	$a1, $t1
+	adc	\$0, $t4
+
+	xor	$t3, $t3
+	sbb	\$-1, $a0
+	 mov	$a2, $t2
+	sbb	$poly1, $a1
+	sbb	\$0, $a2
+	 mov	$a3, $t3
+	sbb	$poly3, $a3
+	sbb	\$0, $t4
+
+	cmovc	$t0, $a0
+	cmovc	$t1, $a1
+	mov	$a0, 8*0($r_ptr)
+	cmovc	$t2, $a2
+	mov	$a1, 8*1($r_ptr)
+	cmovc	$t3, $a3
+	mov	$a2, 8*2($r_ptr)
+	mov	$a3, 8*3($r_ptr)
+
+	ret
+.size	__ecp_nistz256_add_tox,.-__ecp_nistz256_add_tox
+
+.type	__ecp_nistz256_sub_fromx,\@abi-omnipotent
+.align	32
+__ecp_nistz256_sub_fromx:
+	xor	$t4, $t4
+	sbb	8*0($b_ptr), $a0
+	sbb	8*1($b_ptr), $a1
+	 mov	$a0, $t0
+	sbb	8*2($b_ptr), $a2
+	sbb	8*3($b_ptr), $a3
+	 mov	$a1, $t1
+	sbb	\$0, $t4
+
+	xor	$t3, $t3
+	adc	\$-1, $a0
+	 mov	$a2, $t2
+	adc	$poly1, $a1
+	adc	\$0, $a2
+	 mov	$a3, $t3
+	adc	$poly3, $a3
+
+	bt	\$0, $t4
+	cmovnc	$t0, $a0
+	cmovnc	$t1, $a1
+	mov	$a0, 8*0($r_ptr)
+	cmovnc	$t2, $a2
+	mov	$a1, 8*1($r_ptr)
+	cmovnc	$t3, $a3
+	mov	$a2, 8*2($r_ptr)
+	mov	$a3, 8*3($r_ptr)
+
+	ret
+.size	__ecp_nistz256_sub_fromx,.-__ecp_nistz256_sub_fromx
+
+.type	__ecp_nistz256_subx,\@abi-omnipotent
+.align	32
+__ecp_nistz256_subx:
+	xor	$t4, $t4
+	sbb	$a0, $t0
+	sbb	$a1, $t1
+	 mov	$t0, $a0
+	sbb	$a2, $t2
+	sbb	$a3, $t3
+	 mov	$t1, $a1
+	sbb	\$0, $t4
+
+	xor	$a3 ,$a3
+	adc	\$-1, $t0
+	 mov	$t2, $a2
+	adc	$poly1, $t1
+	adc	\$0, $t2
+	 mov	$t3, $a3
+	adc	$poly3, $t3
+
+	bt	\$0, $t4
+	cmovc	$t0, $a0
+	cmovc	$t1, $a1
+	cmovc	$t2, $a2
+	cmovc	$t3, $a3
+
+	ret
+.size	__ecp_nistz256_subx,.-__ecp_nistz256_subx
+
+.type	__ecp_nistz256_mul_by_2x,\@abi-omnipotent
+.align	32
+__ecp_nistz256_mul_by_2x:
+	xor	$t4, $t4
+	adc	$a0, $a0		# a0:a3+a0:a3
+	adc	$a1, $a1
+	 mov	$a0, $t0
+	adc	$a2, $a2
+	adc	$a3, $a3
+	 mov	$a1, $t1
+	adc	\$0, $t4
+
+	xor	$t3, $t3
+	sbb	\$-1, $a0
+	 mov	$a2, $t2
+	sbb	$poly1, $a1
+	sbb	\$0, $a2
+	 mov	$a3, $t3
+	sbb	$poly3, $a3
+	sbb	\$0, $t4
+
+	cmovc	$t0, $a0
+	cmovc	$t1, $a1
+	mov	$a0, 8*0($r_ptr)
+	cmovc	$t2, $a2
+	mov	$a1, 8*1($r_ptr)
+	cmovc	$t3, $a3
+	mov	$a2, 8*2($r_ptr)
+	mov	$a3, 8*3($r_ptr)
+
+	ret
+.size	__ecp_nistz256_mul_by_2x,.-__ecp_nistz256_mul_by_2x
+___
+									}
+&gen_double("x");
+&gen_add("x");
+&gen_add_affine("x");
+}
+}}}
+
+########################################################################
+# Convert ecp_nistz256_table.c to layout expected by ecp_nistz_gather_w7
+#
+open TABLE,") {
+	s/TOBN\(\s*(0x[0-9a-f]+),\s*(0x[0-9a-f]+)\s*\)/push @arr,hex($2),hex($1)/geo;
+}
+close TABLE;
+
+die "insane number of elements" if ($#arr != 64*16*37-1);
+
+print <<___;
+.text
+.globl	ecp_nistz256_precomputed
+.type	ecp_nistz256_precomputed,\@object
+.align	4096
+ecp_nistz256_precomputed:
+___
+while (@line=splice(@arr,0,16)) {
+	print ".long\t",join(',',map { sprintf "0x%08x",$_} @line),"\n";
+}
+print <<___;
+.size	ecp_nistz256_precomputed,.-ecp_nistz256_precomputed
+___
+
+$code =~ s/\`([^\`]*)\`/eval $1/gem;
+print $code;
+close STDOUT;
diff --git a/crypto/ec/asm/ecp_sm2z256-x86_64.pl b/crypto/ec/asm/ecp_sm2z256-x86_64.pl
new file mode 100755
index 000000000..d3183e6e3
--- /dev/null
+++ b/crypto/ec/asm/ecp_sm2z256-x86_64.pl
@@ -0,0 +1,3193 @@
+#! /usr/bin/env perl
+# Copyright 2018 The GmSSL Project. All Rights Reserved.
+#
+# This work is supported by the National Key Research and Development Program
+# of China NO.2018YFB0803601 and Intel.
+#
+# Copyright 2014-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+
+##############################################################################
+#                                                                            #
+# Copyright 2014 Intel Corporation                                           #
+#                                                                            #
+# Licensed under the Apache License, Version 2.0 (the "License");            #
+# you may not use this file except in compliance with the License.           #
+# You may obtain a copy of the License at                                    #
+#                                                                            #
+#    http://www.apache.org/licenses/LICENSE-2.0                              #
+#                                                                            #
+# Unless required by applicable law or agreed to in writing, software        #
+# distributed under the License is distributed on an "AS IS" BASIS,          #
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   #
+# See the License for the specific language governing permissions and        #
+# limitations under the License.                                             #
+#                                                                            #
+##############################################################################
+#                                                                            #
+#  Developers and authors:                                                   #
+#  Shay Gueron (1, 2), and Vlad Krasnov (1)                                  #
+#  (1) Intel Corporation, Israel Development Center                          #
+#  (2) University of Haifa                                                   #
+#  Reference:                                                                #
+#  S.Gueron and V.Krasnov, "Fast Prime Field Elliptic Curve Cryptography with#
+#                           256 Bit Primes"                                  #
+#                                                                            #
+##############################################################################
+
+# Further optimization by :
+#
+#		this/original	with/without -DECP_NISTZ256_ASM(*)
+# Opteron	+12-49%		+110-150%
+# Bulldozer	+14-45%		+175-210%
+# P4		+18-46%		n/a :-(
+# Westmere	+12-34%		+80-87%
+# Sandy Bridge	+9-35%		+110-120%
+# Ivy Bridge	+9-35%		+110-125%
+# Haswell	+8-37%		+140-160%
+# Broadwell	+18-58%		+145-210%
+# Atom		+15-50%		+130-180%
+# VIA Nano	+43-160%	+300-480%
+#
+# (*)	"without -DECP_NISTZ256_ASM" refers to build with
+#	"enable-ec_nistp_64_gcc_128";
+#
+# Ranges denote minimum and maximum improvement coefficients depending
+# on benchmark. Lower coefficients are for ECDSA sign, relatively fastest
+# server-side operation. Keep in mind that +100% means 2x improvement.
+
+$flavour = shift;
+$output  = shift;
+if ($flavour =~ /\./) { $output = $flavour; undef $flavour; }
+
+$win64=0; $win64=1 if ($flavour =~ /[nm]asm|mingw64/ || $output =~ /\.asm$/);
+
+$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+( $xlate="${dir}x86_64-xlate.pl" and -f $xlate ) or
+( $xlate="${dir}../../perlasm/x86_64-xlate.pl" and -f $xlate) or
+die "can't locate x86_64-xlate.pl";
+
+open OUT,"| \"$^X\" \"$xlate\" $flavour \"$output\"";
+*STDOUT=*OUT;
+
+if (`$ENV{CC} -Wa,-v -c -o /dev/null -x assembler /dev/null 2>&1`
+		=~ /GNU assembler version ([2-9]\.[0-9]+)/) {
+	$avx = ($1>=2.19) + ($1>=2.22);
+	$addx = ($1>=2.23);
+}
+
+if (!$addx && $win64 && ($flavour =~ /nasm/ || $ENV{ASM} =~ /nasm/) &&
+	    `nasm -v 2>&1` =~ /NASM version ([2-9]\.[0-9]+)/) {
+	$avx = ($1>=2.09) + ($1>=2.10);
+	$addx = ($1>=2.10);
+}
+
+if (!$addx && $win64 && ($flavour =~ /masm/ || $ENV{ASM} =~ /ml64/) &&
+	    `ml64 2>&1` =~ /Version ([0-9]+)\./) {
+	$avx = ($1>=10) + ($1>=11);
+	$addx = ($1>=12);
+}
+
+if (!$addx && `$ENV{CC} -v 2>&1` =~ /((?:^clang|LLVM) version|.*based on LLVM) ([3-9])\.([0-9]+)/) {
+	my $ver = $2 + $3/100.0;	# 3.1->3.01, 3.10->3.10
+	$avx = ($ver>=3.0) + ($ver>=3.01);
+	$addx = ($ver>=3.03);
+}
+
+$code.=<<___;
+.text
+.extern	OPENSSL_ia32cap_P
+
+# The polynomial
+.align 64
+.Lpoly:
+.quad 0xffffffffffffffff, 0xffffffff00000000, 0xffffffffffffffff, 0xfffffffeffffffff
+
+# 2^512 mod P precomputed for SM2 P256 polynomial
+.LRR:
+.quad 0x0000000200000003, 0x00000002ffffffff, 0x0000000100000001, 0x0000000400000002
+
+.LOne:
+.long 1,1,1,1,1,1,1,1
+.LTwo:
+.long 2,2,2,2,2,2,2,2
+.LThree:
+.long 3,3,3,3,3,3,3,3
+.LONE_mont:
+.quad 0x0000000000000001, 0x00000000ffffffff, 0x0000000000000000, 0x0000000100000000
+___
+
+{
+################################################################################
+# void ecp_sm2z256_mul_by_2(uint64_t res[4], uint64_t a[4]);
+
+my ($a0,$a1,$a2,$a3)=map("%r$_",(8..11));
+my ($t0,$t1,$t2,$t3,$t4)=("%rax","%rdx","%rcx","%r12","%r13");
+my ($r_ptr,$a_ptr,$b_ptr)=("%rdi","%rsi","%rdx");
+
+$code.=<<___;
+
+.globl	ecp_sm2z256_mul_by_2
+.type	ecp_sm2z256_mul_by_2,\@function,2
+.align	64
+ecp_sm2z256_mul_by_2:
+	push	%r12
+	push	%r13
+
+	mov	8*0($a_ptr), $a0
+	xor	$t4,$t4
+	mov	8*1($a_ptr), $a1
+	add	$a0, $a0		# a0:a3+a0:a3
+	mov	8*2($a_ptr), $a2
+	adc	$a1, $a1
+	mov	8*3($a_ptr), $a3
+	lea	.Lpoly(%rip), $a_ptr
+	 mov	$a0, $t0
+	adc	$a2, $a2
+	adc	$a3, $a3
+	 mov	$a1, $t1
+	adc	\$0, $t4
+
+	sub	8*0($a_ptr), $a0
+	 mov	$a2, $t2
+	sbb	8*1($a_ptr), $a1
+	sbb	8*2($a_ptr), $a2
+	 mov	$a3, $t3
+	sbb	8*3($a_ptr), $a3
+	sbb	\$0, $t4
+
+	cmovc	$t0, $a0
+	cmovc	$t1, $a1
+	mov	$a0, 8*0($r_ptr)
+	cmovc	$t2, $a2
+	mov	$a1, 8*1($r_ptr)
+	cmovc	$t3, $a3
+	mov	$a2, 8*2($r_ptr)
+	mov	$a3, 8*3($r_ptr)
+
+	pop	%r13
+	pop	%r12
+	ret
+.size	ecp_sm2z256_mul_by_2,.-ecp_sm2z256_mul_by_2
+
+################################################################################
+# void ecp_sm2z256_div_by_2(uint64_t res[4], uint64_t a[4]);
+.globl	ecp_sm2z256_div_by_2
+.type	ecp_sm2z256_div_by_2,\@function,2
+.align	32
+ecp_sm2z256_div_by_2:
+	push	%r12
+	push	%r13
+
+	mov	8*0($a_ptr), $a0
+	mov	8*1($a_ptr), $a1
+	mov	8*2($a_ptr), $a2
+	 mov	$a0, $t0
+	mov	8*3($a_ptr), $a3
+	lea	.Lpoly(%rip), $a_ptr
+
+	 mov	$a1, $t1
+	xor	$t4, $t4
+	add	8*0($a_ptr), $a0
+	 mov	$a2, $t2
+	adc	8*1($a_ptr), $a1
+	adc	8*2($a_ptr), $a2
+	 mov	$a3, $t3
+	adc	8*3($a_ptr), $a3
+	adc	\$0, $t4
+	xor	$a_ptr, $a_ptr		# borrow $a_ptr
+	test	\$1, $t0
+
+	cmovz	$t0, $a0
+	cmovz	$t1, $a1
+	cmovz	$t2, $a2
+	cmovz	$t3, $a3
+	cmovz	$a_ptr, $t4
+
+	mov	$a1, $t0		# a0:a3>>1
+	shr	\$1, $a0
+	shl	\$63, $t0
+	mov	$a2, $t1
+	shr	\$1, $a1
+	or	$t0, $a0
+	shl	\$63, $t1
+	mov	$a3, $t2
+	shr	\$1, $a2
+	or	$t1, $a1
+	shl	\$63, $t2
+	shr	\$1, $a3
+	shl	\$63, $t4
+	or	$t2, $a2
+	or	$t4, $a3
+
+	mov	$a0, 8*0($r_ptr)
+	mov	$a1, 8*1($r_ptr)
+	mov	$a2, 8*2($r_ptr)
+	mov	$a3, 8*3($r_ptr)
+
+	pop	%r13
+	pop	%r12
+	ret
+.size	ecp_sm2z256_div_by_2,.-ecp_sm2z256_div_by_2
+
+################################################################################
+# void ecp_sm2z256_mul_by_3(uint64_t res[4], uint64_t a[4]);
+.globl	ecp_sm2z256_mul_by_3
+.type	ecp_sm2z256_mul_by_3,\@function,2
+.align	32
+ecp_sm2z256_mul_by_3:
+	push	%r12
+	push	%r13
+
+	mov	8*0($a_ptr), $a0
+	xor	$t4, $t4
+	mov	8*1($a_ptr), $a1
+	add	$a0, $a0		# a0:a3+a0:a3
+	mov	8*2($a_ptr), $a2
+	adc	$a1, $a1
+	mov	8*3($a_ptr), $a3
+	 mov	$a0, $t0
+	adc	$a2, $a2
+	adc	$a3, $a3
+	 mov	$a1, $t1
+	adc	\$0, $t4
+
+	sub	.Lpoly+8*0(%rip), $a0
+	 mov	$a2, $t2
+	sbb	.Lpoly+8*1(%rip), $a1
+	sbb	.Lpoly+8*2(%rip), $a2
+	 mov	$a3, $t3
+	sbb	.Lpoly+8*3(%rip), $a3
+	sbb	\$0, $t4
+
+	cmovc	$t0, $a0
+	cmovc	$t1, $a1
+	cmovc	$t2, $a2
+	cmovc	$t3, $a3
+
+	xor	$t4, $t4
+	add	8*0($a_ptr), $a0	# a0:a3+=a_ptr[0:3]
+	adc	8*1($a_ptr), $a1
+	 mov	$a0, $t0
+	adc	8*2($a_ptr), $a2
+	adc	8*3($a_ptr), $a3
+	 mov	$a1, $t1
+	adc	\$0, $t4
+
+	sub	.Lpoly+8*0(%rip), $a0
+	 mov	$a2, $t2
+	sbb	.Lpoly+8*1(%rip), $a1
+	sbb	.Lpoly+8*2(%rip), $a2
+	 mov	$a3, $t3
+	sbb	.Lpoly+8*3(%rip), $a3
+	sbb	\$0, $t4
+
+	cmovc	$t0, $a0
+	cmovc	$t1, $a1
+	mov	$a0, 8*0($r_ptr)
+	cmovc	$t2, $a2
+	mov	$a1, 8*1($r_ptr)
+	cmovc	$t3, $a3
+	mov	$a2, 8*2($r_ptr)
+	mov	$a3, 8*3($r_ptr)
+
+	pop %r13
+	pop %r12
+	ret
+.size	ecp_sm2z256_mul_by_3,.-ecp_sm2z256_mul_by_3
+
+################################################################################
+# void ecp_sm2z256_add(uint64_t res[4], uint64_t a[4], uint64_t b[4]);
+.globl	ecp_sm2z256_add
+.type	ecp_sm2z256_add,\@function,3
+.align	32
+ecp_sm2z256_add:
+	push	%r12
+	push	%r13
+
+	mov	8*0($a_ptr), $a0
+	xor	$t4, $t4
+	mov	8*1($a_ptr), $a1
+	mov	8*2($a_ptr), $a2
+	mov	8*3($a_ptr), $a3
+	lea	.Lpoly(%rip), $a_ptr
+
+	add	8*0($b_ptr), $a0
+	adc	8*1($b_ptr), $a1
+	 mov	$a0, $t0
+	adc	8*2($b_ptr), $a2
+	adc	8*3($b_ptr), $a3
+	 mov	$a1, $t1
+	adc	\$0, $t4
+
+	sub	8*0($a_ptr), $a0
+	 mov	$a2, $t2
+	sbb	8*1($a_ptr), $a1
+	sbb	8*2($a_ptr), $a2
+	 mov	$a3, $t3
+	sbb	8*3($a_ptr), $a3
+	sbb	\$0, $t4
+
+	cmovc	$t0, $a0
+	cmovc	$t1, $a1
+	mov	$a0, 8*0($r_ptr)
+	cmovc	$t2, $a2
+	mov	$a1, 8*1($r_ptr)
+	cmovc	$t3, $a3
+	mov	$a2, 8*2($r_ptr)
+	mov	$a3, 8*3($r_ptr)
+
+	pop %r13
+	pop %r12
+	ret
+.size	ecp_sm2z256_add,.-ecp_sm2z256_add
+
+################################################################################
+# void ecp_sm2z256_sub(uint64_t res[4], uint64_t a[4], uint64_t b[4]);
+.globl	ecp_sm2z256_sub
+.type	ecp_sm2z256_sub,\@function,3
+.align	32
+ecp_sm2z256_sub:
+	push	%r12
+	push	%r13
+
+	mov	8*0($a_ptr), $a0
+	xor	$t4, $t4
+	mov	8*1($a_ptr), $a1
+	mov	8*2($a_ptr), $a2
+	mov	8*3($a_ptr), $a3
+	lea	.Lpoly(%rip), $a_ptr
+
+	sub	8*0($b_ptr), $a0
+	sbb	8*1($b_ptr), $a1
+	 mov	$a0, $t0
+	sbb	8*2($b_ptr), $a2
+	sbb	8*3($b_ptr), $a3
+	 mov	$a1, $t1
+	sbb	\$0, $t4
+
+	add	8*0($a_ptr), $a0
+	 mov	$a2, $t2
+	adc	8*1($a_ptr), $a1
+	adc	8*2($a_ptr), $a2
+	 mov	$a3, $t3
+	adc	8*3($a_ptr), $a3
+	test	$t4, $t4
+
+	cmovz	$t0, $a0
+	cmovz	$t1, $a1
+	mov	$a0, 8*0($r_ptr)
+	cmovz	$t2, $a2
+	mov	$a1, 8*1($r_ptr)
+	cmovz	$t3, $a3
+	mov	$a2, 8*2($r_ptr)
+	mov	$a3, 8*3($r_ptr)
+
+	pop %r13
+	pop %r12
+	ret
+.size	ecp_sm2z256_sub,.-ecp_sm2z256_sub
+
+################################################################################
+# void ecp_sm2z256_neg(uint64_t res[4], uint64_t a[4]);
+.globl	ecp_sm2z256_neg
+.type	ecp_sm2z256_neg,\@function,2
+.align	32
+ecp_sm2z256_neg:
+	push	%r12
+	push	%r13
+
+	xor	$a0, $a0
+	xor	$a1, $a1
+	xor	$a2, $a2
+	xor	$a3, $a3
+	xor	$t4, $t4
+
+	sub	8*0($a_ptr), $a0
+	sbb	8*1($a_ptr), $a1
+	sbb	8*2($a_ptr), $a2
+	 mov	$a0, $t0
+	sbb	8*3($a_ptr), $a3
+	lea	.Lpoly(%rip), $a_ptr
+	 mov	$a1, $t1
+	sbb	\$0, $t4
+
+	add	8*0($a_ptr), $a0
+	 mov	$a2, $t2
+	adc	8*1($a_ptr), $a1
+	adc	8*2($a_ptr), $a2
+	 mov	$a3, $t3
+	adc	8*3($a_ptr), $a3
+	test	$t4, $t4
+
+	cmovz	$t0, $a0
+	cmovz	$t1, $a1
+	mov	$a0, 8*0($r_ptr)
+	cmovz	$t2, $a2
+	mov	$a1, 8*1($r_ptr)
+	cmovz	$t3, $a3
+	mov	$a2, 8*2($r_ptr)
+	mov	$a3, 8*3($r_ptr)
+
+	pop %r13
+	pop %r12
+	ret
+.size	ecp_sm2z256_neg,.-ecp_sm2z256_neg
+___
+}
+{
+my ($r_ptr,$a_ptr,$b_org,$b_ptr)=("%rdi","%rsi","%rdx","%rbx");
+my ($acc0,$acc1,$acc2,$acc3,$acc4,$acc5,$acc6,$acc7)=map("%r$_",(8..15));
+my ($t0,$t1,$t2,$t3,$t4)=("%rcx","%rbp","%rbx","%rdx","%rax");
+my ($poly1,$poly3)=($acc6,$acc7);
+
+$code.=<<___;
+################################################################################
+# void ecp_sm2z256_to_mont(
+#   uint64_t res[4],
+#   uint64_t in[4]);
+.globl	ecp_sm2z256_to_mont
+.type	ecp_sm2z256_to_mont,\@function,2
+.align	32
+ecp_sm2z256_to_mont:
+___
+$code.=<<___	if ($addx);
+	mov	\$0x80100, %ecx
+	and	OPENSSL_ia32cap_P+8(%rip), %ecx
+___
+$code.=<<___;
+	lea	.LRR(%rip), $b_org
+	jmp	.Lmul_mont
+.size	ecp_sm2z256_to_mont,.-ecp_sm2z256_to_mont
+
+################################################################################
+# void ecp_sm2z256_mul_mont(
+#   uint64_t res[4],
+#   uint64_t a[4],
+#   uint64_t b[4]);
+
+.globl	ecp_sm2z256_mul_mont
+.type	ecp_sm2z256_mul_mont,\@function,3
+.align	32
+ecp_sm2z256_mul_mont:
+___
+$code.=<<___	if ($addx);
+	mov	\$0x80100, %ecx
+	and	OPENSSL_ia32cap_P+8(%rip), %ecx
+___
+$code.=<<___;
+.Lmul_mont:
+	push	%rbp
+	push	%rbx
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+___
+$code.=<<___	if ($addx);
+	cmp	\$0x80100, %ecx
+	je	.Lmul_montx
+___
+$code.=<<___;
+	mov	$b_org, $b_ptr
+	mov	8*0($b_org), %rax
+	mov	8*0($a_ptr), $acc1
+	mov	8*1($a_ptr), $acc2
+	mov	8*2($a_ptr), $acc3
+	mov	8*3($a_ptr), $acc4
+
+	call	__ecp_sm2z256_mul_montq
+___
+$code.=<<___	if ($addx);
+	jmp	.Lmul_mont_done
+
+.align	32
+.Lmul_montx:
+	mov	$b_org, $b_ptr
+	mov	8*0($b_org), %rdx
+	mov	8*0($a_ptr), $acc1
+	mov	8*1($a_ptr), $acc2
+	mov	8*2($a_ptr), $acc3
+	mov	8*3($a_ptr), $acc4
+	lea	-128($a_ptr), $a_ptr	# control u-op density
+
+	call	__ecp_sm2z256_mul_montx
+___
+$code.=<<___;
+.Lmul_mont_done:
+	pop	%r15
+	pop	%r14
+	pop	%r13
+	pop	%r12
+	pop	%rbx
+	pop	%rbp
+	ret
+.size	ecp_sm2z256_mul_mont,.-ecp_sm2z256_mul_mont
+
+.type	__ecp_sm2z256_mul_montq,\@abi-omnipotent
+.align	32
+__ecp_sm2z256_mul_montq:
+	########################################################################
+	# Multiply a by b[0]
+
+	mov	%rax, $t1
+	mulq	$acc1
+	mov	.Lpoly+8*1(%rip),$poly1
+	mov	%rax, $acc0
+	mov	$t1, %rax
+	mov	%rdx, $acc1
+
+	mulq	$acc2
+	mov	.Lpoly+8*3(%rip),$poly3
+	add	%rax, $acc1
+	mov	$t1, %rax
+	adc	\$0, %rdx
+	mov	%rdx, $acc2
+
+	mulq	$acc3
+	add	%rax, $acc2
+	mov	$t1, %rax
+	adc	\$0, %rdx
+	mov	%rdx, $acc3
+
+	mulq	$acc4
+	add	%rax, $acc3
+	 mov	$acc0, %rax
+	adc	\$0, %rdx
+	xor	$acc5, $acc5
+	mov	%rdx, $acc4
+
+	########################################################################
+	# First reduction step
+
+	mov	$acc0, $t1
+	add	$acc0, $acc1
+	adc	\$0, $acc2
+	adc	\$0, $acc3
+	adc	$acc0, $acc4
+	adc	\$0, $acc5
+	shl	\$32, $acc0
+	shr	\$32, $t1
+	sub	$acc0, $acc1
+	 mov	8*1($b_ptr), %rax
+	sbb	$t1, $acc2
+	sbb	$acc0, $acc3
+	sbb	$t1, $acc4
+	sbb	\$0, $acc5
+	xor	$acc0, $acc0
+
+	########################################################################
+	# Multiply by b[1]
+
+	mov	%rax, $t1
+	mulq	8*0($a_ptr)
+	add	%rax, $acc1
+	mov	$t1, %rax
+	adc	\$0, %rdx
+	mov	%rdx, $t0
+
+	mulq	8*1($a_ptr)
+	add	$t0, $acc2
+	adc	\$0, %rdx
+	add	%rax, $acc2
+	mov	$t1, %rax
+	adc	\$0, %rdx
+	mov	%rdx, $t0
+
+	mulq	8*2($a_ptr)
+	add	$t0, $acc3
+	adc	\$0, %rdx
+	add	%rax, $acc3
+	mov	$t1, %rax
+	adc	\$0, %rdx
+	mov	%rdx, $t0
+
+	mulq	8*3($a_ptr)
+	add	$t0, $acc4
+	adc	\$0, %rdx
+	add	%rax, $acc4
+	 mov	$acc1, %rax
+	adc	%rdx, $acc5
+	adc	\$0, $acc0
+
+	########################################################################
+	# Second reduction step
+
+	mov	$acc1, $t1
+	add	$acc1, $acc2
+	adc	\$0, $acc3
+	adc	\$0, $acc4
+	adc	$acc1, $acc5
+	adc	\$0, $acc0
+	shl	\$32, $acc1
+	shr	\$32, $t1
+	sub	$acc1, $acc2
+	 mov	8*2($b_ptr), %rax
+	sbb	$t1, $acc3
+	sbb	$acc1, $acc4
+	sbb	$t1, $acc5
+	sbb	\$0, $acc0
+	xor	$acc1, $acc1
+
+	########################################################################
+	# Multiply by b[2]
+
+	mov	%rax, $t1
+	mulq	8*0($a_ptr)
+	add	%rax, $acc2
+	mov	$t1, %rax
+	adc	\$0, %rdx
+	mov	%rdx, $t0
+
+	mulq	8*1($a_ptr)
+	add	$t0, $acc3
+	adc	\$0, %rdx
+	add	%rax, $acc3
+	mov	$t1, %rax
+	adc	\$0, %rdx
+	mov	%rdx, $t0
+
+	mulq	8*2($a_ptr)
+	add	$t0, $acc4
+	adc	\$0, %rdx
+	add	%rax, $acc4
+	mov	$t1, %rax
+	adc	\$0, %rdx
+	mov	%rdx, $t0
+
+	mulq	8*3($a_ptr)
+	add	$t0, $acc5
+	adc	\$0, %rdx
+	add	%rax, $acc5
+	 mov	$acc2, %rax
+	adc	%rdx, $acc0
+	adc	\$0, $acc1
+
+	########################################################################
+	# Third reduction step
+
+	mov	$acc2, $t1
+	add	$acc2, $acc3
+	adc	\$0, $acc4
+	adc	\$0, $acc5
+	adc	$acc2, $acc0
+	adc	\$0, $acc1
+	shl	\$32, $acc2
+	shr	\$32, $t1
+	sub	$acc2, $acc3
+	 mov	8*3($b_ptr), %rax
+	sbb	$t1, $acc4
+	sbb	$acc2, $acc5
+	sbb	$t1, $acc0
+	sbb	\$0, $acc1
+	xor	$acc2, $acc2
+
+	########################################################################
+	# Multiply by b[3]
+
+	mov	%rax, $t1
+	mulq	8*0($a_ptr)
+	add	%rax, $acc3
+	mov	$t1, %rax
+	adc	\$0, %rdx
+	mov	%rdx, $t0
+
+	mulq	8*1($a_ptr)
+	add	$t0, $acc4
+	adc	\$0, %rdx
+	add	%rax, $acc4
+	mov	$t1, %rax
+	adc	\$0, %rdx
+	mov	%rdx, $t0
+
+	mulq	8*2($a_ptr)
+	add	$t0, $acc5
+	adc	\$0, %rdx
+	add	%rax, $acc5
+	mov	$t1, %rax
+	adc	\$0, %rdx
+	mov	%rdx, $t0
+
+	mulq	8*3($a_ptr)
+	add	$t0, $acc0
+	adc	\$0, %rdx
+	add	%rax, $acc0
+	 mov	$acc3, %rax
+	adc	%rdx, $acc1
+	adc	\$0, $acc2
+
+	########################################################################
+	# Final reduction step
+
+	mov	$acc3, $t1
+	add	$acc3, $acc4
+	adc	\$0, $acc5
+	adc	\$0, $acc0
+	adc	$acc3, $acc1
+	adc	\$0, $acc2
+	shl	\$32, $acc3
+	shr	\$32, $t1
+	sub	$acc3, $acc4
+	 mov	8*4($b_ptr), %rax
+	sbb	$t1, $acc5
+	sbb	$acc3, $acc0
+	sbb	$t1, $acc1
+	sbb	\$0, $acc2
+
+	mov	$acc4, $t0
+	mov	$acc5, $t1
+
+	########################################################################
+	# Branch-less conditional subtraction of P
+
+	sub	.Lpoly+8*0(%rip), $acc4
+	 mov	$acc0, $t2
+	sbb	.Lpoly+8*1(%rip), $acc5
+	sbb	.Lpoly+8*2(%rip), $acc0
+	 mov	$acc1, $t3
+	sbb	.Lpoly+8*3(%rip), $acc1
+	sbb	\$0, $acc2
+
+	cmovc	$t0, $acc4
+	cmovc	$t1, $acc5
+	mov	$acc4, 8*0($r_ptr)
+	cmovc	$t2, $acc0
+	mov	$acc5, 8*1($r_ptr)
+	cmovc	$t3, $acc1
+	mov	$acc0, 8*2($r_ptr)
+	mov	$acc1, 8*3($r_ptr)
+
+	#mov	\$0, $acc0
+	#mov	$acc0, 8*0($r_ptr)
+	#mov	$acc0, 8*1($r_ptr)
+	#mov	$acc0, 8*2($r_ptr)
+	#mov	$acc0, 8*3($r_ptr)
+
+	ret
+.size	__ecp_sm2z256_mul_montq,.-__ecp_sm2z256_mul_montq
+
+################################################################################
+# void ecp_sm2z256_sqr_mont(
+#   uint64_t res[4],
+#   uint64_t a[4]);
+
+# we optimize the square according to S.Gueron and V.Krasnov,
+# "Speeding up Big-Number Squaring"
+.globl	ecp_sm2z256_sqr_mont
+.type	ecp_sm2z256_sqr_mont,\@function,2
+.align	32
+ecp_sm2z256_sqr_mont:
+___
+$code.=<<___	if ($addx);
+	mov	\$0x80100, %ecx
+	and	OPENSSL_ia32cap_P+8(%rip), %ecx
+___
+$code.=<<___;
+	push	%rbp
+	push	%rbx
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+___
+$code.=<<___	if ($addx);
+	cmp	\$0x80100, %ecx
+	je	.Lsqr_montx
+___
+$code.=<<___;
+	mov	8*0($a_ptr), %rax
+	mov	8*1($a_ptr), $acc6
+	mov	8*2($a_ptr), $acc7
+	mov	8*3($a_ptr), $acc0
+
+	call	__ecp_sm2z256_sqr_montq
+___
+$code.=<<___	if ($addx);
+	jmp	.Lsqr_mont_done
+
+.align	32
+.Lsqr_montx:
+	mov	8*0($a_ptr), %rdx
+	mov	8*1($a_ptr), $acc6
+	mov	8*2($a_ptr), $acc7
+	mov	8*3($a_ptr), $acc0
+	lea	-128($a_ptr), $a_ptr	# control u-op density
+
+	call	__ecp_sm2z256_sqr_montx
+___
+$code.=<<___;
+.Lsqr_mont_done:
+	pop	%r15
+	pop	%r14
+	pop	%r13
+	pop	%r12
+	pop	%rbx
+	pop	%rbp
+	ret
+.size	ecp_sm2z256_sqr_mont,.-ecp_sm2z256_sqr_mont
+
+.type	__ecp_sm2z256_sqr_montq,\@abi-omnipotent
+.align	32
+__ecp_sm2z256_sqr_montq:
+	mov	%rax, $acc5
+	mulq	$acc6			# a[1]*a[0]
+	mov	%rax, $acc1
+	mov	$acc7, %rax
+	mov	%rdx, $acc2
+
+	mulq	$acc5			# a[0]*a[2]
+	add	%rax, $acc2
+	mov	$acc0, %rax
+	adc	\$0, %rdx
+	mov	%rdx, $acc3
+
+	mulq	$acc5			# a[0]*a[3]
+	add	%rax, $acc3
+	 mov	$acc7, %rax
+	adc	\$0, %rdx
+	mov	%rdx, $acc4
+
+	#################################
+	mulq	$acc6			# a[1]*a[2]
+	add	%rax, $acc3
+	mov	$acc0, %rax
+	adc	\$0, %rdx
+	mov	%rdx, $t1
+
+	mulq	$acc6			# a[1]*a[3]
+	add	%rax, $acc4
+	 mov	$acc0, %rax
+	adc	\$0, %rdx
+	add	$t1, $acc4
+	mov	%rdx, $acc5
+	adc	\$0, $acc5
+
+	#################################
+	mulq	$acc7			# a[2]*a[3]
+	xor	$acc7, $acc7
+	add	%rax, $acc5
+	 mov	8*0($a_ptr), %rax
+	mov	%rdx, $acc6
+	adc	\$0, $acc6
+
+	add	$acc1, $acc1		# acc1:6<<1
+	adc	$acc2, $acc2
+	adc	$acc3, $acc3
+	adc	$acc4, $acc4
+	adc	$acc5, $acc5
+	adc	$acc6, $acc6
+	adc	\$0, $acc7
+
+	mulq	%rax
+	mov	%rax, $acc0
+	mov	8*1($a_ptr), %rax
+	mov	%rdx, $t0
+
+	mulq	%rax
+	add	$t0, $acc1
+	adc	%rax, $acc2
+	mov	8*2($a_ptr), %rax
+	adc	\$0, %rdx
+	mov	%rdx, $t0
+
+	mulq	%rax
+	add	$t0, $acc3
+	adc	%rax, $acc4
+	mov	8*3($a_ptr), %rax
+	adc	\$0, %rdx
+	mov	%rdx, $t0
+
+	mulq	%rax
+	add	$t0, $acc5
+	adc	%rax, $acc6
+	 mov	$acc0, %rax
+	adc	%rdx, $acc7
+
+	mov	.Lpoly+8*1(%rip), $a_ptr
+	mov	.Lpoly+8*3(%rip), $t1
+
+	##########################################
+	# Now the reduction
+	# First iteration
+	mov	$acc0, $t0
+	mov	$acc0, %rax
+
+	add	$acc0, $acc1
+	adc	\$0, $acc2
+	adc	\$0, $acc3
+	adc	\$0, $acc0
+	shl	\$32, %rax
+	shr	\$32, $t0
+	sub	%rax, $acc1
+	sbb	$t0, $acc2
+	sbb	%rax, $acc3
+	sbb	$t0, $acc0
+
+	##########################################
+	# Second iteration
+	mov	$acc1, $t0
+	mov	$acc1, %rax
+
+	add	$acc1, $acc2
+	adc	\$0, $acc3
+	adc	\$0, $acc0
+	adc	\$0, $acc1
+	shl	\$32, %rax
+	shr	\$32, $t0
+	sub	%rax, $acc2
+	sbb	$t0, $acc3
+	sbb	%rax, $acc0
+	sbb	$t0, $acc1
+
+	##########################################
+	# Third iteration
+	mov	$acc2, $t0
+	mov	$acc2, %rax
+
+	add	$acc2, $acc3
+	adc	\$0, $acc0
+	adc	\$0, $acc1
+	adc	\$0, $acc2
+	shl	\$32, %rax
+	shr	\$32, $t0
+	sub	%rax, $acc3
+	sbb	$t0, $acc0
+	sbb	%rax, $acc1
+	sbb	$t0, $acc2
+
+	###########################################
+	# Last iteration
+	mov	$acc3, $t0
+	mov	$acc3, %rax
+
+	add	$acc3, $acc0
+	adc	\$0, $acc1
+	adc	\$0, $acc2
+	adc	\$0, $acc3
+	shl	\$32, %rax
+	shr	\$32, $t0
+	sub	%rax, $acc0
+	sbb	$t0, $acc1
+	sbb	%rax, $acc2
+	sbb	$t0, $acc3
+
+	mov	$acc3, %rdx
+	xor	$acc3, $acc3
+
+	############################################
+	# Add the rest of the acc
+	add	$acc0, $acc4
+	adc	$acc1, $acc5
+	 mov	$acc4, $acc0
+	adc	$acc2, $acc6
+	adc	%rdx, $acc7
+	 mov	$acc5, $acc1
+	adc	\$0, $acc3
+
+	sub	.Lpoly+8*0(%rip), $acc4	# .Lpoly[0]
+	 mov	$acc6, $acc2
+	sbb	.Lpoly+8*1(%rip), $acc5	# .Lpoly[1]
+	sbb	.Lpoly+8*2(%rip), $acc6	# .Lpoly[2]
+	 mov	$acc7, $t0
+	sbb	.Lpoly+8*3(%rip), $acc7	# .Lpoly[3]
+	sbb	\$0, $acc3
+
+	cmovc	$acc0, $acc4
+	cmovc	$acc1, $acc5
+	mov	$acc4, 8*0($r_ptr)
+	cmovc	$acc2, $acc6
+	mov	$acc5, 8*1($r_ptr)
+	cmovc	$t0, $acc7
+	mov	$acc6, 8*2($r_ptr)
+	mov	$acc7, 8*3($r_ptr)
+
+	ret
+.size	__ecp_sm2z256_sqr_montq,.-__ecp_sm2z256_sqr_montq
+___
+
+if ($addx) {
+$code.=<<___;
+.type	__ecp_sm2z256_mul_montx,\@abi-omnipotent
+.align	32
+__ecp_sm2z256_mul_montx:
+	########################################################################
+	# Multiply by b[0]
+
+	mov	.Lpoly+8*3(%rip), $poly3
+
+	mulx	$acc1, $acc0, $acc1
+	mulx	$acc2, $t0, $acc2
+	mov	\$32, $poly1
+	xor	$acc5, $acc5		# cf=0
+	mulx	$acc3, $t1, $acc3
+	adc	$t0, $acc1
+	mulx	$acc4, $t0, $acc4
+	 mov	$acc0, %rdx
+	adc	$t1, $acc2
+	 shlx	$poly1,$acc0,$t1
+	adc	$t0, $acc3
+	 shrx	$poly1,$acc0,$t0
+	adc	\$0, $acc4
+
+	########################################################################
+	# First reduction step
+
+	adox	$acc0, $acc1
+	adox	$acc5, $acc2
+	adox	$acc5, $acc3
+	adox	$acc0, $acc4
+	adox	$acc5, $acc5
+	sub	$t1, $acc1
+	sbb	$t0, $acc2
+	sbb	$t1, $acc3
+	sbb	$t0, $acc4
+	sbb	\$0, $acc5
+	xor	$acc0, $acc0 		# $acc0=0,cf=0,of=0
+	mov	8*1($b_ptr), %rdx
+
+	########################################################################
+	# Multiply by b[1]
+	# (acc0,acc5,acc4,acc3,acc2,acc1) = (acc5,acc4,acc3,acc2,acc1) + a * b[1]
+
+	mulx	8*0+128($a_ptr), $t0, $t1
+	adcx	$t0, $acc1
+	adox	$t1, $acc2
+
+	mulx	8*1+128($a_ptr), $t0, $t1
+	adcx	$t0, $acc2
+	adox	$t1, $acc3
+
+	mulx	8*2+128($a_ptr), $t0, $t1
+	adcx	$t0, $acc3
+	adox	$t1, $acc4
+
+	mulx	8*3+128($a_ptr), $t0, $t1
+	adcx	$t0, $acc4
+	 shlx	$poly1, $acc1, $t0
+	adox	$t1, $acc5
+	 shrx	$poly1, $acc1, $t1
+
+	adcx	$acc0, $acc5
+	adox	$acc0, $acc0
+	adc	\$0, $acc0
+
+	########################################################################
+	# Second reduction step
+
+	xor	%rdx, %rdx
+	adox	$acc1, $acc2
+	adox	%rdx, $acc3
+	adox	%rdx, $acc4
+	adox	$acc1, $acc5
+	adox	%rdx, $acc0
+	sub	$t0, $acc2
+	sbb	$t1, $acc3
+	sbb	$t0, $acc4
+	sbb	$t1, $acc5
+	sbb	\$0, $acc0
+
+	mov    8*2($b_ptr), %rdx
+	xor	$acc1, $acc1		# $acc0=0,cf=0,of=0
+
+	########################################################################
+	# Multiply by b[2]
+
+	mulx	8*0+128($a_ptr), $t0, $t1
+	adcx	$t0, $acc2
+	adox	$t1, $acc3
+
+	mulx	8*1+128($a_ptr), $t0, $t1
+	adcx	$t0, $acc3
+	adox	$t1, $acc4
+
+	mulx	8*2+128($a_ptr), $t0, $t1
+	adcx	$t0, $acc4
+	adox	$t1, $acc5
+
+	mulx	8*3+128($a_ptr), $t0, $t1
+	 mov	$acc2, %rdx
+	adcx	$t0, $acc5
+	 shlx	$poly1, $acc2, $t0
+	adox	$t1, $acc0
+	 shrx	$poly1, $acc2, $t1
+
+	adcx	$acc1, $acc0
+	adox	$acc1, $acc1
+	adc	\$0, $acc1
+
+	########################################################################
+	# Third reduction step
+
+	xor	%rdx, %rdx
+	adox	$acc2, $acc3
+	adox	%rdx, $acc4
+	adox	%rdx, $acc5
+	adox	$acc2, $acc0
+	adox	%rdx, $acc1
+	sub	$t0, $acc3
+	sbb	$t1, $acc4
+	sbb	$t0, $acc5
+	sbb	$t1, $acc0
+	sbb	\$0, $acc1
+
+	mov    8*3($b_ptr), %rdx
+	xor	$acc2, $acc2		# $acc0=0,cf=0,of=0
+
+	########################################################################
+	# Multiply by b[3]
+	mulx	8*0+128($a_ptr), $t0, $t1
+	adcx	$t0, $acc3
+	adox	$t1, $acc4
+
+	mulx	8*1+128($a_ptr), $t0, $t1
+	adcx	$t0, $acc4
+	adox	$t1, $acc5
+
+	mulx	8*2+128($a_ptr), $t0, $t1
+	adcx	$t0, $acc5
+	adox	$t1, $acc0
+
+	mulx	8*3+128($a_ptr), $t0, $t1
+	 mov	$acc3, %rdx
+	adcx	$t0, $acc0
+	 shlx	$poly1, $acc3, $t0
+	adox	$t1, $acc1
+	 shrx	$poly1, $acc3, $t1
+
+	adcx	$acc2, $acc1
+	adox	$acc2, $acc2
+	adc	\$0, $acc2
+
+	########################################################################
+	# Fourth reduction step
+
+	xor	%rdx, %rdx
+	adox	$acc3, $acc4
+	adox	%rdx, $acc5
+	adox	%rdx, $acc0
+	adox	$acc3, $acc1
+	adox	%rdx, $acc2
+
+	sub	$t0, $acc4
+	sbb	$t1, $acc5
+	sbb	$t0, $acc0
+	sbb	$t1, $acc1
+	sbb	\$0, $acc2
+
+	xor	$acc3, $acc3		# $acc0=0,cf=0,of=0
+	mov	$acc4, $t2
+	mov	$acc5, $t3
+
+	########################################################################
+	# Branch-less conditional subtraction of P
+	xor	%eax, %eax
+	 mov	$acc0, $t0
+
+	sub	.Lpoly+8*0(%rip), $acc4	# .Lpoly[0]
+	sbb	.Lpoly+8*1(%rip), $acc5	# .Lpoly[1]
+	sbb	.Lpoly+8*2(%rip), $acc0	# .Lpoly[2]
+	 mov	$acc1, $t1
+	sbb	.Lpoly+8*3(%rip), $acc1	# .Lpoly[3]
+	sbb	\$0, $acc2
+
+	cmovc	$t2, $acc4
+	cmovc	$t3, $acc5
+	 mov	$acc4, 8*0($r_ptr)
+	cmovc	$t0, $acc0
+	 mov	$acc5, 8*1($r_ptr)
+	cmovc	$t1, $acc1
+	 mov	$acc0, 8*2($r_ptr)
+	 mov	$acc1, 8*3($r_ptr)
+
+	ret
+.size	__ecp_sm2z256_mul_montx,.-__ecp_sm2z256_mul_montx
+
+.type	__ecp_sm2z256_sqr_montx,\@abi-omnipotent
+.align	32
+__ecp_sm2z256_sqr_montx:
+	mulx	$acc6, $acc1, $acc2
+	mulx	$acc7, $t0, $acc3
+	xor	%eax, %eax
+	adc	$t0, $acc2
+	mulx	$acc0, $t1, $acc4
+	 mov	$acc6, %rdx
+	adc	$t1, $acc3
+	adc	\$0, $acc4
+	xor	$acc5, $acc5		# $acc5=0,cf=0,of=0
+
+	#################################
+	mulx	$acc7, $t0, $t1
+	adcx	$t0, $acc3
+	adox	$t1, $acc4
+
+	mulx	$acc0, $t0, $t1
+	 mov	$acc7, %rdx
+	adcx	$t0, $acc4
+	adox	$t1, $acc5
+	adc	\$0, $acc5
+
+	#################################
+	mulx	$acc0, $t0, $acc6
+	 mov	8*0+128($a_ptr), %rdx
+	xor	$acc7, $acc7		# $acc7=0,cf=0,of=0
+	 adcx	$acc1, $acc1		# acc1:6<<1
+	adox	$t0, $acc5
+	 adcx	$acc2, $acc2
+	adox	$acc7, $acc6		# of=0
+
+	mulx	%rdx, $acc0, $t1
+	mov	8*1+128($a_ptr), %rdx
+	 adcx	$acc3, $acc3
+	adox	$t1, $acc1
+	 adcx	$acc4, $acc4
+	mulx	%rdx, $t0, $t4
+	mov	8*2+128($a_ptr), %rdx
+	 adcx	$acc5, $acc5
+	adox	$t0, $acc2
+	 adcx	$acc6, $acc6
+	.byte	0x67
+	mulx	%rdx, $t0, $t1
+	mov	8*3+128($a_ptr), %rdx
+	adox	$t4, $acc3
+	 adcx	$acc7, $acc7
+	adox	$t0, $acc4
+	 mov	\$32, $a_ptr
+	adox	$t1, $acc5
+	.byte	0x67,0x67
+	mulx	%rdx, $t0, $t4
+	 mov	$acc0, %rdx
+	adox	$t0, $acc6
+	 shlx	$a_ptr, $acc0, $t0
+	adox	$t4, $acc7
+	 shrx	$a_ptr, $acc0, $t4
+	 mov	.Lpoly+8*3(%rip), $t1
+
+    # set of = 0
+    add \$0, $acc2
+
+	# reduction step 1
+	xor	$t1, $t1
+	adox	$acc0, $acc1
+	adox	$t1, $acc2
+	adox	$t1, $acc3
+	adox	$t1, $acc0
+	sub	$t0, $acc1
+	sbb	$t4, $acc2
+	sbb	$t0, $acc3
+	sbb	$t4, $acc0
+
+	shlx	$a_ptr, $acc1, $t0
+	shrx	$a_ptr, $acc1, $t4
+
+    # set of = 0
+    add \$0, $acc2
+
+	# reduction step 2
+	adox	$acc1, $acc2
+	adox	$t1, $acc3
+	adox	$t1, $acc0
+	adox	$t1, $acc1
+	sub	$t0, $acc2
+	sbb	$t4, $acc3
+	sbb	$t0, $acc0
+	sbb	$t4, $acc1
+
+	shlx	$a_ptr, $acc2, $t0
+	shrx	$a_ptr, $acc2, $t4
+
+    # set of = 0
+    add \$0, $acc2
+
+	# reduction step 3
+	adox	$acc2, $acc3
+	adox	$t1, $acc0
+	adox	$t1, $acc1
+	adox	$t1, $acc2
+	sub	$t0, $acc3
+	sbb	$t4, $acc0
+	sbb	$t0, $acc1
+	sbb	$t4, $acc2
+
+	shlx	$a_ptr, $acc3, $t0
+	shrx	$a_ptr, $acc3, $t4
+
+    # set of = 0
+    add \$0, $acc2
+
+	# reduction step 4
+	adox	$acc3, $acc0
+	adox	$t1, $acc1
+	adox	$t1, $acc2
+	adox	$t1, $acc3
+	sub	$t0, $acc0
+	sbb	$t4, $acc1
+	sbb	$t0, $acc2
+	sbb	$t4, $acc3
+
+	###########################
+	xor	$t3, $t3		# cf=0
+	add	$acc0, $acc4		# accumulate upper half
+	 mov	.Lpoly+8*1(%rip), $a_ptr
+	adc	$acc1, $acc5
+	 mov	$acc4, $acc0
+	adc	$acc2, $acc6
+	adc	$acc3, $acc7
+	 mov	$acc5, $acc1
+	adc	\$0, $t3
+
+	xor	%eax, %eax		# cf=0
+	sub	.Lpoly+8*0(%rip), $acc4	# .Lpoly[0]
+	 mov	$acc6, $acc2
+	sbb	.Lpoly+8*1(%rip), $acc5	# .Lpoly[1]
+	sbb	.Lpoly+8*2(%rip), $acc6	# .Lpoly[2]
+	 mov	$acc7, $acc3
+	sbb	.Lpoly+8*3(%rip), $acc7	# .Lpoly[3]
+	sbb	\$0, $t3
+
+	cmovc	$acc0, $acc4
+	cmovc	$acc1, $acc5
+	mov	$acc4, 8*0($r_ptr)
+	cmovc	$acc2, $acc6
+	mov	$acc5, 8*1($r_ptr)
+	cmovc	$acc3, $acc7
+	mov	$acc6, 8*2($r_ptr)
+	mov	$acc7, 8*3($r_ptr)
+
+	ret
+.size	__ecp_sm2z256_sqr_montx,.-__ecp_sm2z256_sqr_montx
+___
+}
+}
+{
+my ($r_ptr,$in_ptr)=("%rdi","%rsi");
+my ($acc0,$acc1,$acc2,$acc3)=map("%r$_",(8..11));
+my ($t0,$t1,$t2)=("%rcx","%r12","%r13");
+
+$code.=<<___;
+################################################################################
+# void ecp_sm2z256_from_mont(
+#   uint64_t res[4],
+#   uint64_t in[4]);
+# This one performs Montgomery multiplication by 1, so we only need the reduction
+
+.globl	ecp_sm2z256_from_mont
+.type	ecp_sm2z256_from_mont,\@function,2
+.align	32
+ecp_sm2z256_from_mont:
+	push	%r12
+	push	%r13
+
+	mov	8*0($in_ptr), $acc0
+	mov	8*1($in_ptr), $acc1
+	mov	8*2($in_ptr), $acc2
+	mov	8*3($in_ptr), $acc3
+
+	#########################################
+	# First iteration
+	mov	$acc0, $t0
+	add	$acc0, $acc1
+	adc	\$0, $acc2
+	adc	\$0, $acc3
+	adc	\$0, $acc0
+	mov	$t0, $t2
+	shl	\$32, $t0
+	shr	\$32, $t2
+	sub	$t0, $acc1
+	sbb	$t2, $acc2
+	sbb	$t0, $acc3
+	sbb	$t2, $acc0
+
+	#########################################
+	# Second iteration
+	mov	$acc1, $t0
+	add	$acc1, $acc2
+	adc	\$0, $acc3
+	adc	\$0, $acc0
+	adc	\$0, $acc1
+	mov	$t0, $t2
+	shl	\$32, $t0
+	shr	\$32, $t2
+	sub	$t0, $acc2
+	sbb	$t2, $acc3
+	sbb	$t0, $acc0
+	sbb	$t2, $acc1
+
+	##########################################
+	# Third iteration
+	mov	$acc2, $t0
+	add	$acc2, $acc3
+	adc	\$0, $acc0
+	adc	\$0, $acc1
+	adc	\$0, $acc2
+	mov	$t0, $t2
+	shl	\$32, $t0
+	shr	\$32, $t2
+	sub	$t0, $acc3
+	sbb	$t2, $acc0
+	sbb	$t0, $acc1
+	sbb	$t2, $acc2
+
+	###########################################
+	# Last iteration
+	mov	$acc3, $t0
+	add	$acc3, $acc0
+	adc	\$0, $acc1
+	adc	\$0, $acc2
+	adc	\$0, $acc3
+	mov	$t0, $t2
+	shl	\$32, $t0
+	shr	\$32, $t2
+	sub	$t0, $acc0
+	sbb	$t2, $acc1
+	sbb	$t0, $acc2
+	sbb	$t2, $acc3
+	# now we have (acc3, acc2, acc1, acc0)
+
+	###########################################
+	# Branch-less conditional subtraction
+	mov	$acc0, $t0
+	mov	$acc1, $in_ptr
+	mov	$acc2, %rax
+	mov	$acc3, %rdx
+
+	sub	.Lpoly+8*0(%rip), $acc0
+	sbb	.Lpoly+8*1(%rip), $acc1
+	sbb	.Lpoly+8*2(%rip), $acc2
+	sbb	.Lpoly+8*3(%rip), $acc3
+	sbb	$t2, $t2
+
+	cmovnz	$t0, $acc0
+	cmovnz	$in_ptr, $acc1
+	 mov	$acc0, 8*0($r_ptr)
+	cmovnz	%rax, $acc2
+	 mov	$acc1, 8*1($r_ptr)
+	cmovnz	%rdx, $acc3
+	 mov	$acc2, 8*2($r_ptr)
+	 mov	$acc3, 8*3($r_ptr)
+
+	pop	%r13
+	pop	%r12
+	ret
+.size	ecp_sm2z256_from_mont,.-ecp_sm2z256_from_mont
+___
+}
+{
+my ($val,$in_t,$index)=$win64?("%rcx","%rdx","%r8d"):("%rdi","%rsi","%edx");
+my ($ONE,$INDEX,$Ra,$Rb,$Rc,$Rd,$Re,$Rf)=map("%xmm$_",(0..7));
+my ($M0,$T0a,$T0b,$T0c,$T0d,$T0e,$T0f,$TMP0)=map("%xmm$_",(8..15));
+my ($M1,$T2a,$T2b,$TMP2,$M2,$T2a,$T2b,$TMP2)=map("%xmm$_",(8..15));
+
+$code.=<<___;
+################################################################################
+# void ecp_sm2z256_scatter_w5(uint64_t *val, uint64_t *in_t, int index);
+.globl	ecp_sm2z256_scatter_w5
+.type	ecp_sm2z256_scatter_w5,\@abi-omnipotent
+.align	32
+ecp_sm2z256_scatter_w5:
+	lea	-3($index,$index,2), $index
+	movdqa	0x00($in_t), %xmm0
+	shl	\$5, $index
+	movdqa	0x10($in_t), %xmm1
+	movdqa	0x20($in_t), %xmm2
+	movdqa	0x30($in_t), %xmm3
+	movdqa	0x40($in_t), %xmm4
+	movdqa	0x50($in_t), %xmm5
+	movdqa	%xmm0, 0x00($val,$index)
+	movdqa	%xmm1, 0x10($val,$index)
+	movdqa	%xmm2, 0x20($val,$index)
+	movdqa	%xmm3, 0x30($val,$index)
+	movdqa	%xmm4, 0x40($val,$index)
+	movdqa	%xmm5, 0x50($val,$index)
+
+	ret
+.size	ecp_sm2z256_scatter_w5,.-ecp_sm2z256_scatter_w5
+
+################################################################################
+# void ecp_sm2z256_gather_w5(uint64_t *val, uint64_t *in_t, int index);
+.globl	ecp_sm2z256_gather_w5
+.type	ecp_sm2z256_gather_w5,\@abi-omnipotent
+.align	32
+ecp_sm2z256_gather_w5:
+___
+$code.=<<___	if ($avx>1);
+	mov	OPENSSL_ia32cap_P+8(%rip), %eax
+	test	\$`1<<5`, %eax
+	jnz	.Lavx2_gather_w5
+___
+$code.=<<___	if ($win64);
+	lea	-0x88(%rsp), %rax
+.LSEH_begin_ecp_sm2z256_gather_w5:
+	.byte	0x48,0x8d,0x60,0xe0		#lea	-0x20(%rax), %rsp
+	.byte	0x0f,0x29,0x70,0xe0		#movaps	%xmm6, -0x20(%rax)
+	.byte	0x0f,0x29,0x78,0xf0		#movaps	%xmm7, -0x10(%rax)
+	.byte	0x44,0x0f,0x29,0x00		#movaps	%xmm8, 0(%rax)
+	.byte	0x44,0x0f,0x29,0x48,0x10	#movaps	%xmm9, 0x10(%rax)
+	.byte	0x44,0x0f,0x29,0x50,0x20	#movaps	%xmm10, 0x20(%rax)
+	.byte	0x44,0x0f,0x29,0x58,0x30	#movaps	%xmm11, 0x30(%rax)
+	.byte	0x44,0x0f,0x29,0x60,0x40	#movaps	%xmm12, 0x40(%rax)
+	.byte	0x44,0x0f,0x29,0x68,0x50	#movaps	%xmm13, 0x50(%rax)
+	.byte	0x44,0x0f,0x29,0x70,0x60	#movaps	%xmm14, 0x60(%rax)
+	.byte	0x44,0x0f,0x29,0x78,0x70	#movaps	%xmm15, 0x70(%rax)
+___
+$code.=<<___;
+	movdqa	.LOne(%rip), $ONE
+	movd	$index, $INDEX
+
+	pxor	$Ra, $Ra
+	pxor	$Rb, $Rb
+	pxor	$Rc, $Rc
+	pxor	$Rd, $Rd
+	pxor	$Re, $Re
+	pxor	$Rf, $Rf
+
+	movdqa	$ONE, $M0
+	pshufd	\$0, $INDEX, $INDEX
+
+	mov	\$16, %rax
+.Lselect_loop_sse_w5:
+
+	movdqa	$M0, $TMP0
+	paddd	$ONE, $M0
+	pcmpeqd $INDEX, $TMP0
+
+	movdqa	16*0($in_t), $T0a
+	movdqa	16*1($in_t), $T0b
+	movdqa	16*2($in_t), $T0c
+	movdqa	16*3($in_t), $T0d
+	movdqa	16*4($in_t), $T0e
+	movdqa	16*5($in_t), $T0f
+	lea 16*6($in_t), $in_t
+
+	pand	$TMP0, $T0a
+	pand	$TMP0, $T0b
+	por	$T0a, $Ra
+	pand	$TMP0, $T0c
+	por	$T0b, $Rb
+	pand	$TMP0, $T0d
+	por	$T0c, $Rc
+	pand	$TMP0, $T0e
+	por	$T0d, $Rd
+	pand	$TMP0, $T0f
+	por	$T0e, $Re
+	por	$T0f, $Rf
+
+	dec	%rax
+	jnz	.Lselect_loop_sse_w5
+
+	movdqu	$Ra, 16*0($val)
+	movdqu	$Rb, 16*1($val)
+	movdqu	$Rc, 16*2($val)
+	movdqu	$Rd, 16*3($val)
+	movdqu	$Re, 16*4($val)
+	movdqu	$Rf, 16*5($val)
+___
+$code.=<<___	if ($win64);
+	movaps	(%rsp), %xmm6
+	movaps	0x10(%rsp), %xmm7
+	movaps	0x20(%rsp), %xmm8
+	movaps	0x30(%rsp), %xmm9
+	movaps	0x40(%rsp), %xmm10
+	movaps	0x50(%rsp), %xmm11
+	movaps	0x60(%rsp), %xmm12
+	movaps	0x70(%rsp), %xmm13
+	movaps	0x80(%rsp), %xmm14
+	movaps	0x90(%rsp), %xmm15
+	lea	0xa8(%rsp), %rsp
+.LSEH_end_ecp_sm2z256_gather_w5:
+___
+$code.=<<___;
+	ret
+.size	ecp_sm2z256_gather_w5,.-ecp_sm2z256_gather_w5
+
+################################################################################
+# void ecp_sm2z256_scatter_w7(uint64_t *val, uint64_t *in_t, int index);
+.globl	ecp_sm2z256_scatter_w7
+.type	ecp_sm2z256_scatter_w7,\@abi-omnipotent
+.align	32
+ecp_sm2z256_scatter_w7:
+	movdqu	0x00($in_t), %xmm0
+	shl	\$6, $index
+	movdqu	0x10($in_t), %xmm1
+	movdqu	0x20($in_t), %xmm2
+	movdqu	0x30($in_t), %xmm3
+	movdqa	%xmm0, 0x00($val,$index)
+	movdqa	%xmm1, 0x10($val,$index)
+	movdqa	%xmm2, 0x20($val,$index)
+	movdqa	%xmm3, 0x30($val,$index)
+
+	ret
+.size	ecp_sm2z256_scatter_w7,.-ecp_sm2z256_scatter_w7
+
+################################################################################
+# void ecp_sm2z256_gather_w7(uint64_t *val, uint64_t *in_t, int index);
+.globl	ecp_sm2z256_gather_w7
+.type	ecp_sm2z256_gather_w7,\@abi-omnipotent
+.align	32
+ecp_sm2z256_gather_w7:
+___
+$code.=<<___	if ($avx>1);
+	mov	OPENSSL_ia32cap_P+8(%rip), %eax
+	test	\$`1<<5`, %eax
+	jnz	.Lavx2_gather_w7
+___
+$code.=<<___	if ($win64);
+	lea	-0x88(%rsp), %rax
+.LSEH_begin_ecp_sm2z256_gather_w7:
+	.byte	0x48,0x8d,0x60,0xe0		#lea	-0x20(%rax), %rsp
+	.byte	0x0f,0x29,0x70,0xe0		#movaps	%xmm6, -0x20(%rax)
+	.byte	0x0f,0x29,0x78,0xf0		#movaps	%xmm7, -0x10(%rax)
+	.byte	0x44,0x0f,0x29,0x00		#movaps	%xmm8, 0(%rax)
+	.byte	0x44,0x0f,0x29,0x48,0x10	#movaps	%xmm9, 0x10(%rax)
+	.byte	0x44,0x0f,0x29,0x50,0x20	#movaps	%xmm10, 0x20(%rax)
+	.byte	0x44,0x0f,0x29,0x58,0x30	#movaps	%xmm11, 0x30(%rax)
+	.byte	0x44,0x0f,0x29,0x60,0x40	#movaps	%xmm12, 0x40(%rax)
+	.byte	0x44,0x0f,0x29,0x68,0x50	#movaps	%xmm13, 0x50(%rax)
+	.byte	0x44,0x0f,0x29,0x70,0x60	#movaps	%xmm14, 0x60(%rax)
+	.byte	0x44,0x0f,0x29,0x78,0x70	#movaps	%xmm15, 0x70(%rax)
+___
+$code.=<<___;
+	movdqa	.LOne(%rip), $M0
+	movd	$index, $INDEX
+
+	pxor	$Ra, $Ra
+	pxor	$Rb, $Rb
+	pxor	$Rc, $Rc
+	pxor	$Rd, $Rd
+
+	movdqa	$M0, $ONE
+	pshufd	\$0, $INDEX, $INDEX
+	mov	\$64, %rax
+
+.Lselect_loop_sse_w7:
+	movdqa	$M0, $TMP0
+	paddd	$ONE, $M0
+	movdqa	16*0($in_t), $T0a
+	movdqa	16*1($in_t), $T0b
+	pcmpeqd	$INDEX, $TMP0
+	movdqa	16*2($in_t), $T0c
+	movdqa	16*3($in_t), $T0d
+	lea	16*4($in_t), $in_t
+
+	pand	$TMP0, $T0a
+	pand	$TMP0, $T0b
+	por	$T0a, $Ra
+	pand	$TMP0, $T0c
+	por	$T0b, $Rb
+	pand	$TMP0, $T0d
+	por	$T0c, $Rc
+	prefetcht0	255($in_t)
+	por	$T0d, $Rd
+
+	dec	%rax
+	jnz	.Lselect_loop_sse_w7
+
+	movdqu	$Ra, 16*0($val)
+	movdqu	$Rb, 16*1($val)
+	movdqu	$Rc, 16*2($val)
+	movdqu	$Rd, 16*3($val)
+___
+$code.=<<___	if ($win64);
+	movaps	(%rsp), %xmm6
+	movaps	0x10(%rsp), %xmm7
+	movaps	0x20(%rsp), %xmm8
+	movaps	0x30(%rsp), %xmm9
+	movaps	0x40(%rsp), %xmm10
+	movaps	0x50(%rsp), %xmm11
+	movaps	0x60(%rsp), %xmm12
+	movaps	0x70(%rsp), %xmm13
+	movaps	0x80(%rsp), %xmm14
+	movaps	0x90(%rsp), %xmm15
+	lea	0xa8(%rsp), %rsp
+.LSEH_end_ecp_sm2z256_gather_w7:
+___
+$code.=<<___;
+	ret
+.size	ecp_sm2z256_gather_w7,.-ecp_sm2z256_gather_w7
+___
+}
+if ($avx>1) {
+my ($val,$in_t,$index)=$win64?("%rcx","%rdx","%r8d"):("%rdi","%rsi","%edx");
+my ($TWO,$INDEX,$Ra,$Rb,$Rc)=map("%ymm$_",(0..4));
+my ($M0,$T0a,$T0b,$T0c,$TMP0)=map("%ymm$_",(5..9));
+my ($M1,$T1a,$T1b,$T1c,$TMP1)=map("%ymm$_",(10..14));
+
+$code.=<<___;
+################################################################################
+# void ecp_sm2z256_avx2_gather_w5(uint64_t *val, uint64_t *in_t, int index);
+.type	ecp_sm2z256_avx2_gather_w5,\@abi-omnipotent
+.align	32
+ecp_sm2z256_avx2_gather_w5:
+.Lavx2_gather_w5:
+	vzeroupper
+___
+$code.=<<___	if ($win64);
+	lea	-0x88(%rsp), %rax
+.LSEH_begin_ecp_sm2z256_avx2_gather_w5:
+	.byte	0x48,0x8d,0x60,0xe0		#lea	-0x20(%rax), %rsp
+	.byte	0xc5,0xf8,0x29,0x70,0xe0	#vmovaps %xmm6, -0x20(%rax)
+	.byte	0xc5,0xf8,0x29,0x78,0xf0	#vmovaps %xmm7, -0x10(%rax)
+	.byte	0xc5,0x78,0x29,0x40,0x00	#vmovaps %xmm8, 8(%rax)
+	.byte	0xc5,0x78,0x29,0x48,0x10	#vmovaps %xmm9, 0x10(%rax)
+	.byte	0xc5,0x78,0x29,0x50,0x20	#vmovaps %xmm10, 0x20(%rax)
+	.byte	0xc5,0x78,0x29,0x58,0x30	#vmovaps %xmm11, 0x30(%rax)
+	.byte	0xc5,0x78,0x29,0x60,0x40	#vmovaps %xmm12, 0x40(%rax)
+	.byte	0xc5,0x78,0x29,0x68,0x50	#vmovaps %xmm13, 0x50(%rax)
+	.byte	0xc5,0x78,0x29,0x70,0x60	#vmovaps %xmm14, 0x60(%rax)
+	.byte	0xc5,0x78,0x29,0x78,0x70	#vmovaps %xmm15, 0x70(%rax)
+___
+$code.=<<___;
+	vmovdqa	.LTwo(%rip), $TWO
+
+	vpxor	$Ra, $Ra, $Ra
+	vpxor	$Rb, $Rb, $Rb
+	vpxor	$Rc, $Rc, $Rc
+
+	vmovdqa .LOne(%rip), $M0
+	vmovdqa .LTwo(%rip), $M1
+
+	vmovd	$index, %xmm1
+	vpermd	$INDEX, $Ra, $INDEX
+
+	mov	\$8, %rax
+.Lselect_loop_avx2_w5:
+
+	vmovdqa	32*0($in_t), $T0a
+	vmovdqa	32*1($in_t), $T0b
+	vmovdqa	32*2($in_t), $T0c
+
+	vmovdqa	32*3($in_t), $T1a
+	vmovdqa	32*4($in_t), $T1b
+	vmovdqa	32*5($in_t), $T1c
+
+	vpcmpeqd	$INDEX, $M0, $TMP0
+	vpcmpeqd	$INDEX, $M1, $TMP1
+
+	vpaddd	$TWO, $M0, $M0
+	vpaddd	$TWO, $M1, $M1
+	lea	32*6($in_t), $in_t
+
+	vpand	$TMP0, $T0a, $T0a
+	vpand	$TMP0, $T0b, $T0b
+	vpand	$TMP0, $T0c, $T0c
+	vpand	$TMP1, $T1a, $T1a
+	vpand	$TMP1, $T1b, $T1b
+	vpand	$TMP1, $T1c, $T1c
+
+	vpxor	$T0a, $Ra, $Ra
+	vpxor	$T0b, $Rb, $Rb
+	vpxor	$T0c, $Rc, $Rc
+	vpxor	$T1a, $Ra, $Ra
+	vpxor	$T1b, $Rb, $Rb
+	vpxor	$T1c, $Rc, $Rc
+
+	dec %rax
+	jnz .Lselect_loop_avx2_w5
+
+	vmovdqu $Ra, 32*0($val)
+	vmovdqu $Rb, 32*1($val)
+	vmovdqu $Rc, 32*2($val)
+	vzeroupper
+___
+$code.=<<___	if ($win64);
+	movaps	(%rsp), %xmm6
+	movaps	0x10(%rsp), %xmm7
+	movaps	0x20(%rsp), %xmm8
+	movaps	0x30(%rsp), %xmm9
+	movaps	0x40(%rsp), %xmm10
+	movaps	0x50(%rsp), %xmm11
+	movaps	0x60(%rsp), %xmm12
+	movaps	0x70(%rsp), %xmm13
+	movaps	0x80(%rsp), %xmm14
+	movaps	0x90(%rsp), %xmm15
+	lea	0xa8(%rsp), %rsp
+.LSEH_end_ecp_sm2z256_avx2_gather_w5:
+___
+$code.=<<___;
+	ret
+.size	ecp_sm2z256_avx2_gather_w5,.-ecp_sm2z256_avx2_gather_w5
+___
+}
+if ($avx>1) {
+my ($val,$in_t,$index)=$win64?("%rcx","%rdx","%r8d"):("%rdi","%rsi","%edx");
+my ($THREE,$INDEX,$Ra,$Rb)=map("%ymm$_",(0..3));
+my ($M0,$T0a,$T0b,$TMP0)=map("%ymm$_",(4..7));
+my ($M1,$T1a,$T1b,$TMP1)=map("%ymm$_",(8..11));
+my ($M2,$T2a,$T2b,$TMP2)=map("%ymm$_",(12..15));
+
+$code.=<<___;
+
+################################################################################
+# void ecp_sm2z256_avx2_gather_w7(uint64_t *val, uint64_t *in_t, int index);
+.globl	ecp_sm2z256_avx2_gather_w7
+.type	ecp_sm2z256_avx2_gather_w7,\@abi-omnipotent
+.align	32
+ecp_sm2z256_avx2_gather_w7:
+.Lavx2_gather_w7:
+	vzeroupper
+___
+$code.=<<___	if ($win64);
+	lea	-0x88(%rsp), %rax
+.LSEH_begin_ecp_sm2z256_avx2_gather_w7:
+	.byte	0x48,0x8d,0x60,0xe0		#lea	-0x20(%rax), %rsp
+	.byte	0xc5,0xf8,0x29,0x70,0xe0	#vmovaps %xmm6, -0x20(%rax)
+	.byte	0xc5,0xf8,0x29,0x78,0xf0	#vmovaps %xmm7, -0x10(%rax)
+	.byte	0xc5,0x78,0x29,0x40,0x00	#vmovaps %xmm8, 8(%rax)
+	.byte	0xc5,0x78,0x29,0x48,0x10	#vmovaps %xmm9, 0x10(%rax)
+	.byte	0xc5,0x78,0x29,0x50,0x20	#vmovaps %xmm10, 0x20(%rax)
+	.byte	0xc5,0x78,0x29,0x58,0x30	#vmovaps %xmm11, 0x30(%rax)
+	.byte	0xc5,0x78,0x29,0x60,0x40	#vmovaps %xmm12, 0x40(%rax)
+	.byte	0xc5,0x78,0x29,0x68,0x50	#vmovaps %xmm13, 0x50(%rax)
+	.byte	0xc5,0x78,0x29,0x70,0x60	#vmovaps %xmm14, 0x60(%rax)
+	.byte	0xc5,0x78,0x29,0x78,0x70	#vmovaps %xmm15, 0x70(%rax)
+___
+$code.=<<___;
+	vmovdqa	.LThree(%rip), $THREE
+
+	vpxor	$Ra, $Ra, $Ra
+	vpxor	$Rb, $Rb, $Rb
+
+	vmovdqa .LOne(%rip), $M0
+	vmovdqa .LTwo(%rip), $M1
+	vmovdqa .LThree(%rip), $M2
+
+	vmovd	$index, %xmm1
+	vpermd	$INDEX, $Ra, $INDEX
+	# Skip index = 0, because it is implicitly the point at infinity
+
+	mov	\$21, %rax
+.Lselect_loop_avx2_w7:
+
+	vmovdqa	32*0($in_t), $T0a
+	vmovdqa	32*1($in_t), $T0b
+
+	vmovdqa	32*2($in_t), $T1a
+	vmovdqa	32*3($in_t), $T1b
+
+	vmovdqa	32*4($in_t), $T2a
+	vmovdqa	32*5($in_t), $T2b
+
+	vpcmpeqd	$INDEX, $M0, $TMP0
+	vpcmpeqd	$INDEX, $M1, $TMP1
+	vpcmpeqd	$INDEX, $M2, $TMP2
+
+	vpaddd	$THREE, $M0, $M0
+	vpaddd	$THREE, $M1, $M1
+	vpaddd	$THREE, $M2, $M2
+	lea	32*6($in_t), $in_t
+
+	vpand	$TMP0, $T0a, $T0a
+	vpand	$TMP0, $T0b, $T0b
+	vpand	$TMP1, $T1a, $T1a
+	vpand	$TMP1, $T1b, $T1b
+	vpand	$TMP2, $T2a, $T2a
+	vpand	$TMP2, $T2b, $T2b
+
+	vpxor	$T0a, $Ra, $Ra
+	vpxor	$T0b, $Rb, $Rb
+	vpxor	$T1a, $Ra, $Ra
+	vpxor	$T1b, $Rb, $Rb
+	vpxor	$T2a, $Ra, $Ra
+	vpxor	$T2b, $Rb, $Rb
+
+	dec %rax
+	jnz .Lselect_loop_avx2_w7
+
+
+	vmovdqa	32*0($in_t), $T0a
+	vmovdqa	32*1($in_t), $T0b
+
+	vpcmpeqd	$INDEX, $M0, $TMP0
+
+	vpand	$TMP0, $T0a, $T0a
+	vpand	$TMP0, $T0b, $T0b
+
+	vpxor	$T0a, $Ra, $Ra
+	vpxor	$T0b, $Rb, $Rb
+
+	vmovdqu $Ra, 32*0($val)
+	vmovdqu $Rb, 32*1($val)
+	vzeroupper
+___
+$code.=<<___	if ($win64);
+	movaps	(%rsp), %xmm6
+	movaps	0x10(%rsp), %xmm7
+	movaps	0x20(%rsp), %xmm8
+	movaps	0x30(%rsp), %xmm9
+	movaps	0x40(%rsp), %xmm10
+	movaps	0x50(%rsp), %xmm11
+	movaps	0x60(%rsp), %xmm12
+	movaps	0x70(%rsp), %xmm13
+	movaps	0x80(%rsp), %xmm14
+	movaps	0x90(%rsp), %xmm15
+	lea	0xa8(%rsp), %rsp
+.LSEH_end_ecp_sm2z256_avx2_gather_w7:
+___
+$code.=<<___;
+	ret
+.size	ecp_sm2z256_avx2_gather_w7,.-ecp_sm2z256_avx2_gather_w7
+___
+} else {
+$code.=<<___;
+.globl	ecp_sm2z256_avx2_gather_w7
+.type	ecp_sm2z256_avx2_gather_w7,\@function,3
+.align	32
+ecp_sm2z256_avx2_gather_w7:
+	.byte	0x0f,0x0b	# ud2
+	ret
+.size	ecp_sm2z256_avx2_gather_w7,.-ecp_sm2z256_avx2_gather_w7
+___
+}
+{{{
+########################################################################
+# This block implements higher level point_double, point_add and
+# point_add_affine. The key to performance in this case is to allow
+# out-of-order execution logic to overlap computations from next step
+# with tail processing from current step. By using tailored calling
+# sequence we minimize inter-step overhead to give processor better
+# shot at overlapping operations...
+#
+# You will notice that input data is copied to stack. Trouble is that
+# there are no registers to spare for holding original pointers and
+# reloading them, pointers, would create undesired dependencies on
+# effective addresses calculation paths. In other words it's too done
+# to favour out-of-order execution logic.
+#						
+
+my ($r_ptr,$a_ptr,$b_org,$b_ptr)=("%rdi","%rsi","%rdx","%rbx");
+my ($acc0,$acc1,$acc2,$acc3,$acc4,$acc5,$acc6,$acc7)=map("%r$_",(8..15));
+my ($t0,$t1,$t2,$t3,$t4)=("%rax","%rbp","%rcx",$acc4,$acc4);
+my ($poly1,$poly3)=($acc6,$acc7);
+
+sub load_for_mul () {
+my ($a,$b,$src0) = @_;
+my $bias = $src0 eq "%rax" ? 0 : -128;
+
+"	mov	$b, $src0
+	lea	$b, $b_ptr
+	mov	8*0+$a, $acc1
+	mov	8*1+$a, $acc2
+	lea	$bias+$a, $a_ptr
+	mov	8*2+$a, $acc3
+	mov	8*3+$a, $acc4"
+}
+
+sub load_for_sqr () {
+my ($a,$src0) = @_;
+my $bias = $src0 eq "%rax" ? 0 : -128;
+
+"	mov	8*0+$a, $src0
+	mov	8*1+$a, $acc6
+	lea	$bias+$a, $a_ptr
+	mov	8*2+$a, $acc7
+	mov	8*3+$a, $acc0"
+}
+
+									{
+########################################################################
+# operate in 4-5-0-1 "name space" that matches multiplication output
+#
+my ($a0,$a1,$a2,$a3,$t3,$t4)=($acc4,$acc5,$acc0,$acc1,$acc2,$acc3);
+
+$code.=<<___;
+.type	__ecp_sm2z256_add_toq,\@abi-omnipotent
+.align	32
+__ecp_sm2z256_add_toq:
+	xor	$t4,$t4
+	add	8*0($b_ptr), $a0
+	adc	8*1($b_ptr), $a1
+	 mov	$a0, $t0
+	adc	8*2($b_ptr), $a2
+	adc	8*3($b_ptr), $a3
+	 mov	$a1, $t1
+	adc	\$0, $t4
+
+	sub	.Lpoly+8*0(%rip), $a0
+	 mov	$a2, $t2
+	sbb	.Lpoly+8*1(%rip), $a1
+	sbb	.Lpoly+8*2(%rip), $a2
+	 mov	$a3, $t3
+	sbb	.Lpoly+8*3(%rip), $a3
+	sbb	\$0, $t4
+
+	cmovc	$t0, $a0
+	cmovc	$t1, $a1
+	mov	$a0, 8*0($r_ptr)
+	cmovc	$t2, $a2
+	mov	$a1, 8*1($r_ptr)
+	cmovc	$t3, $a3
+	mov	$a2, 8*2($r_ptr)
+	mov	$a3, 8*3($r_ptr)
+
+	ret
+.size	__ecp_sm2z256_add_toq,.-__ecp_sm2z256_add_toq
+
+.type	__ecp_sm2z256_sub_fromq,\@abi-omnipotent
+.align	32
+__ecp_sm2z256_sub_fromq:
+	sub	8*0($b_ptr), $a0
+	sbb	8*1($b_ptr), $a1
+	 mov	$a0, $t0
+	sbb	8*2($b_ptr), $a2
+	sbb	8*3($b_ptr), $a3
+	 mov	$a1, $t1
+	sbb	$t4, $t4
+
+	add	.Lpoly+8*0(%rip), $a0
+	 mov	$a2, $t2
+	adc	.Lpoly+8*1(%rip), $a1
+	adc	.Lpoly+8*2(%rip), $a2
+	 mov	$a3, $t3
+	adc	.Lpoly+8*3(%rip), $a3
+	test	$t4, $t4
+
+	cmovz	$t0, $a0
+	cmovz	$t1, $a1
+	mov	$a0, 8*0($r_ptr)
+	cmovz	$t2, $a2
+	mov	$a1, 8*1($r_ptr)
+	cmovz	$t3, $a3
+	mov	$a2, 8*2($r_ptr)
+	mov	$a3, 8*3($r_ptr)
+
+	ret
+.size	__ecp_sm2z256_sub_fromq,.-__ecp_sm2z256_sub_fromq
+
+.type	__ecp_sm2z256_subq,\@abi-omnipotent
+.align	32
+__ecp_sm2z256_subq:
+	sub	$a0, $t0
+	sbb	$a1, $t1
+	 mov	$t0, $a0
+	sbb	$a2, $t2
+	sbb	$a3, $t3
+	 mov	$t1, $a1
+	sbb	$t4, $t4
+
+	add	.Lpoly+8*0(%rip), $t0
+	 mov	$t2, $a2
+	adc	.Lpoly+8*1(%rip), $t1
+	adc	.Lpoly+8*2(%rip), $t2
+	 mov	$t3, $a3
+	adc	.Lpoly+8*3(%rip), $t3
+	test	$t4, $t4
+
+	cmovnz	$t0, $a0
+	cmovnz	$t1, $a1
+	cmovnz	$t2, $a2
+	cmovnz	$t3, $a3
+
+	ret
+.size	__ecp_sm2z256_subq,.-__ecp_sm2z256_subq
+
+.type	__ecp_sm2z256_mul_by_2q,\@abi-omnipotent
+.align	32
+__ecp_sm2z256_mul_by_2q:
+	xor	$t4, $t4
+	add	$a0, $a0		# a0:a3+a0:a3
+	adc	$a1, $a1
+	 mov	$a0, $t0
+	adc	$a2, $a2
+	adc	$a3, $a3
+	 mov	$a1, $t1
+	adc	\$0, $t4
+
+	sub	.Lpoly+8*0(%rip), $a0
+	 mov	$a2, $t2
+	sbb	.Lpoly+8*1(%rip), $a1
+	sbb	.Lpoly+8*2(%rip), $a2
+	 mov	$a3, $t3
+	sbb	.Lpoly+8*3(%rip), $a3
+	sbb	\$0, $t4
+
+	cmovc	$t0, $a0
+	cmovc	$t1, $a1
+	mov	$a0, 8*0($r_ptr)
+	cmovc	$t2, $a2
+	mov	$a1, 8*1($r_ptr)
+	cmovc	$t3, $a3
+	mov	$a2, 8*2($r_ptr)
+	mov	$a3, 8*3($r_ptr)
+
+	ret
+.size	__ecp_sm2z256_mul_by_2q,.-__ecp_sm2z256_mul_by_2q
+___
+									}
+sub gen_double () {
+    my $x = shift;
+    my ($src0,$sfx,$bias);
+    my ($S,$M,$Zsqr,$in_x,$tmp0)=map(32*$_,(0..4));
+
+    if ($x ne "x") {
+	$src0 = "%rax";
+	$sfx  = "";
+	$bias = 0;
+
+$code.=<<___;
+.globl	ecp_sm2z256_point_double
+.type	ecp_sm2z256_point_double,\@function,2
+.align	32
+ecp_sm2z256_point_double:
+___
+$code.=<<___	if ($addx);
+	mov	\$0x80100, %ecx
+	and	OPENSSL_ia32cap_P+8(%rip), %ecx
+	cmp	\$0x80100, %ecx
+	je	.Lpoint_doublex
+___
+    } else {
+	$src0 = "%rdx";
+	$sfx  = "x";
+	$bias = 128;
+
+$code.=<<___;
+.type	ecp_sm2z256_point_doublex,\@function,2
+.align	32
+ecp_sm2z256_point_doublex:
+.Lpoint_doublex:
+___
+    }
+$code.=<<___;
+	push	%rbp
+	push	%rbx
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+	sub	\$32*5+8, %rsp
+
+.Lpoint_double_shortcut$x:
+	movdqu	0x00($a_ptr), %xmm0		# copy	*(P256_POINT *)$a_ptr.x
+	mov	$a_ptr, $b_ptr			# backup copy
+	movdqu	0x10($a_ptr), %xmm1
+	 mov	0x20+8*0($a_ptr), $acc4		# load in_y in "5-4-0-1" order
+	 mov	0x20+8*1($a_ptr), $acc5
+	 mov	0x20+8*2($a_ptr), $acc0
+	 mov	0x20+8*3($a_ptr), $acc1
+	 mov	.Lpoly+8*1(%rip), $poly1
+	 mov	.Lpoly+8*3(%rip), $poly3
+	movdqa	%xmm0, $in_x(%rsp)
+	movdqa	%xmm1, $in_x+0x10(%rsp)
+	lea	0x20($r_ptr), $acc2
+	lea	0x40($r_ptr), $acc3
+	movq	$r_ptr, %xmm0
+	movq	$acc2, %xmm1
+	movq	$acc3, %xmm2
+
+	lea	$S(%rsp), $r_ptr
+	call	__ecp_sm2z256_mul_by_2$x	# p256_mul_by_2(S, in_y);
+
+	mov	0x40+8*0($a_ptr), $src0
+	mov	0x40+8*1($a_ptr), $acc6
+	mov	0x40+8*2($a_ptr), $acc7
+	mov	0x40+8*3($a_ptr), $acc0
+	lea	0x40-$bias($a_ptr), $a_ptr
+	lea	$Zsqr(%rsp), $r_ptr
+	call	__ecp_sm2z256_sqr_mont$x	# p256_sqr_mont(Zsqr, in_z);
+
+	`&load_for_sqr("$S(%rsp)", "$src0")`
+	lea	$S(%rsp), $r_ptr
+	call	__ecp_sm2z256_sqr_mont$x	# p256_sqr_mont(S, S);
+
+	mov	0x20($b_ptr), $src0		# $b_ptr is still valid
+	mov	0x40+8*0($b_ptr), $acc1
+	mov	0x40+8*1($b_ptr), $acc2
+	mov	0x40+8*2($b_ptr), $acc3
+	mov	0x40+8*3($b_ptr), $acc4
+	lea	0x40-$bias($b_ptr), $a_ptr
+	lea	0x20($b_ptr), $b_ptr
+	movq	%xmm2, $r_ptr
+	call	__ecp_sm2z256_mul_mont$x	# p256_mul_mont(res_z, in_z, in_y);
+	call	__ecp_sm2z256_mul_by_2$x	# p256_mul_by_2(res_z, res_z);
+
+	mov	$in_x+8*0(%rsp), $acc4		# "5-4-0-1" order
+	mov	$in_x+8*1(%rsp), $acc5
+	lea	$Zsqr(%rsp), $b_ptr
+	mov	$in_x+8*2(%rsp), $acc0
+	mov	$in_x+8*3(%rsp), $acc1
+	lea	$M(%rsp), $r_ptr
+	call	__ecp_sm2z256_add_to$x		# p256_add(M, in_x, Zsqr);
+
+	mov	$in_x+8*0(%rsp), $acc4		# "5-4-0-1" order
+	mov	$in_x+8*1(%rsp), $acc5
+	lea	$Zsqr(%rsp), $b_ptr
+	mov	$in_x+8*2(%rsp), $acc0
+	mov	$in_x+8*3(%rsp), $acc1
+	lea	$Zsqr(%rsp), $r_ptr
+	call	__ecp_sm2z256_sub_from$x	# p256_sub(Zsqr, in_x, Zsqr);
+
+	`&load_for_sqr("$S(%rsp)", "$src0")`
+	movq	%xmm1, $r_ptr
+	call	__ecp_sm2z256_sqr_mont$x	# p256_sqr_mont(res_y, S);
+___
+{
+######## ecp_sm2z256_div_by_2(res_y, res_y); ##########################
+# operate in 4-5-6-7 "name space" that matches squaring output
+#
+my ($poly1,$poly3)=($a_ptr,$t1);
+my ($a0,$a1,$a2,$a3,$t3,$t4,$t1)=($acc4,$acc5,$acc6,$acc7,$acc0,$acc1,$acc2);
+
+$code.=<<___;
+	xor	$t4, $t4
+	mov	$a0, $t0
+	add	.Lpoly+8*0(%rip), $a0
+	mov	$a1, $t1
+	adc	.Lpoly+8*1(%rip), $a1
+	mov	$a2, $t2
+	adc	.Lpoly+8*2(%rip), $a2
+	mov	$a3, $t3
+	adc	.Lpoly+8*3(%rip), $a3
+	adc	\$0, $t4
+	xor	$a_ptr, $a_ptr		# borrow $a_ptr
+	test	\$1, $t0
+
+	cmovz	$t0, $a0
+	cmovz	$t1, $a1
+	cmovz	$t2, $a2
+	cmovz	$t3, $a3
+	cmovz	$a_ptr, $t4
+
+	mov	$a1, $t0		# a0:a3>>1
+	shr	\$1, $a0
+	shl	\$63, $t0
+	mov	$a2, $t1
+	shr	\$1, $a1
+	or	$t0, $a0
+	shl	\$63, $t1
+	mov	$a3, $t2
+	shr	\$1, $a2
+	or	$t1, $a1
+	shl	\$63, $t2
+	mov	$a0, 8*0($r_ptr)
+	shr	\$1, $a3
+	mov	$a1, 8*1($r_ptr)
+	shl	\$63, $t4
+	or	$t2, $a2
+	or	$t4, $a3
+	mov	$a2, 8*2($r_ptr)
+	mov	$a3, 8*3($r_ptr)
+___
+}
+$code.=<<___;
+	`&load_for_mul("$M(%rsp)", "$Zsqr(%rsp)", "$src0")`
+	lea	$M(%rsp), $r_ptr
+	call	__ecp_sm2z256_mul_mont$x	# p256_mul_mont(M, M, Zsqr);
+
+	lea	$tmp0(%rsp), $r_ptr
+	call	__ecp_sm2z256_mul_by_2$x
+
+	lea	$M(%rsp), $b_ptr
+	lea	$M(%rsp), $r_ptr
+	call	__ecp_sm2z256_add_to$x		# p256_mul_by_3(M, M);
+
+	`&load_for_mul("$S(%rsp)", "$in_x(%rsp)", "$src0")`
+	lea	$S(%rsp), $r_ptr
+	call	__ecp_sm2z256_mul_mont$x	# p256_mul_mont(S, S, in_x);
+
+	lea	$tmp0(%rsp), $r_ptr
+	call	__ecp_sm2z256_mul_by_2$x	# p256_mul_by_2(tmp0, S);
+
+	`&load_for_sqr("$M(%rsp)", "$src0")`
+	movq	%xmm0, $r_ptr
+	call	__ecp_sm2z256_sqr_mont$x	# p256_sqr_mont(res_x, M);
+
+	lea	$tmp0(%rsp), $b_ptr
+	mov	$acc6, $acc0			# harmonize sqr output and sub input
+	mov	$acc7, $acc1
+	mov	$a_ptr, $poly1
+	mov	$t1, $poly3
+	call	__ecp_sm2z256_sub_from$x	# p256_sub(res_x, res_x, tmp0);
+
+	mov	$S+8*0(%rsp), $t0
+	mov	$S+8*1(%rsp), $t1
+	mov	$S+8*2(%rsp), $t2
+	mov	$S+8*3(%rsp), $acc2		# "4-5-0-1" order
+	lea	$S(%rsp), $r_ptr
+	call	__ecp_sm2z256_sub$x		# p256_sub(S, S, res_x);
+
+	mov	$M(%rsp), $src0
+	lea	$M(%rsp), $b_ptr
+	mov	$acc4, $acc6			# harmonize sub output and mul input
+	xor	%ecx, %ecx
+	mov	$acc4, $S+8*0(%rsp)		# have to save:-(
+	mov	$acc5, $acc2
+	mov	$acc5, $S+8*1(%rsp)
+	cmovz	$acc0, $acc3
+	mov	$acc0, $S+8*2(%rsp)
+	lea	$S-$bias(%rsp), $a_ptr
+	cmovz	$acc1, $acc4
+	mov	$acc1, $S+8*3(%rsp)
+	mov	$acc6, $acc1
+	lea	$S(%rsp), $r_ptr
+	call	__ecp_sm2z256_mul_mont$x	# p256_mul_mont(S, S, M);
+
+	movq	%xmm1, $b_ptr
+	movq	%xmm1, $r_ptr
+	call	__ecp_sm2z256_sub_from$x	# p256_sub(res_y, S, res_y);
+
+	add	\$32*5+8, %rsp
+	pop	%r15
+	pop	%r14
+	pop	%r13
+	pop	%r12
+	pop	%rbx
+	pop	%rbp
+	ret
+.size	ecp_sm2z256_point_double$sfx,.-ecp_sm2z256_point_double$sfx
+___
+}
+&gen_double("q");
+
+sub gen_add () {
+    my $x = shift;
+    my ($src0,$sfx,$bias);
+    my ($H,$Hsqr,$R,$Rsqr,$Hcub,
+	$U1,$U2,$S1,$S2,
+	$res_x,$res_y,$res_z,
+	$in1_x,$in1_y,$in1_z,
+	$in2_x,$in2_y,$in2_z)=map(32*$_,(0..17));
+    my ($Z1sqr, $Z2sqr) = ($Hsqr, $Rsqr);
+
+    if ($x ne "x") {
+	$src0 = "%rax";
+	$sfx  = "";
+	$bias = 0;
+
+$code.=<<___;
+.globl	ecp_sm2z256_point_add
+.type	ecp_sm2z256_point_add,\@function,3
+.align	32
+ecp_sm2z256_point_add:
+___
+$code.=<<___	if ($addx);
+	mov	\$0x80100, %ecx
+	and	OPENSSL_ia32cap_P+8(%rip), %ecx
+	cmp	\$0x80100, %ecx
+	je	.Lpoint_addx
+___
+    } else {
+	$src0 = "%rdx";
+	$sfx  = "x";
+	$bias = 128;
+
+$code.=<<___;
+.type	ecp_sm2z256_point_addx,\@function,3
+.align	32
+ecp_sm2z256_point_addx:
+.Lpoint_addx:
+___
+    }
+$code.=<<___;
+	push	%rbp
+	push	%rbx
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+	sub	\$32*18+8, %rsp
+
+	movdqu	0x00($a_ptr), %xmm0		# copy	*(P256_POINT *)$a_ptr
+	movdqu	0x10($a_ptr), %xmm1
+	movdqu	0x20($a_ptr), %xmm2
+	movdqu	0x30($a_ptr), %xmm3
+	movdqu	0x40($a_ptr), %xmm4
+	movdqu	0x50($a_ptr), %xmm5
+	mov	$a_ptr, $b_ptr			# reassign
+	mov	$b_org, $a_ptr			# reassign
+	movdqa	%xmm0, $in1_x(%rsp)
+	movdqa	%xmm1, $in1_x+0x10(%rsp)
+	movdqa	%xmm2, $in1_y(%rsp)
+	movdqa	%xmm3, $in1_y+0x10(%rsp)
+	movdqa	%xmm4, $in1_z(%rsp)
+	movdqa	%xmm5, $in1_z+0x10(%rsp)
+	por	%xmm4, %xmm5
+
+	movdqu	0x00($a_ptr), %xmm0		# copy	*(P256_POINT *)$b_ptr
+	 pshufd	\$0xb1, %xmm5, %xmm3
+	movdqu	0x10($a_ptr), %xmm1
+	movdqu	0x20($a_ptr), %xmm2
+	 por	%xmm3, %xmm5
+	movdqu	0x30($a_ptr), %xmm3
+	 mov	0x40+8*0($a_ptr), $src0		# load original in2_z
+	 mov	0x40+8*1($a_ptr), $acc6
+	 mov	0x40+8*2($a_ptr), $acc7
+	 mov	0x40+8*3($a_ptr), $acc0
+	movdqa	%xmm0, $in2_x(%rsp)
+	 pshufd	\$0x1e, %xmm5, %xmm4
+	movdqa	%xmm1, $in2_x+0x10(%rsp)
+	movdqu	0x40($a_ptr),%xmm0		# in2_z again
+	movdqu	0x50($a_ptr),%xmm1
+	movdqa	%xmm2, $in2_y(%rsp)
+	movdqa	%xmm3, $in2_y+0x10(%rsp)
+	 por	%xmm4, %xmm5
+	 pxor	%xmm4, %xmm4
+	por	%xmm0, %xmm1
+	 movq	$r_ptr, %xmm0			# save $r_ptr
+
+	lea	0x40-$bias($a_ptr), $a_ptr	# $a_ptr is still valid
+	 mov	$src0, $in2_z+8*0(%rsp)		# make in2_z copy
+	 mov	$acc6, $in2_z+8*1(%rsp)
+	 mov	$acc7, $in2_z+8*2(%rsp)
+	 mov	$acc0, $in2_z+8*3(%rsp)
+	lea	$Z2sqr(%rsp), $r_ptr		# Z2^2
+	call	__ecp_sm2z256_sqr_mont$x	# p256_sqr_mont(Z2sqr, in2_z);
+
+	pcmpeqd	%xmm4, %xmm5
+	pshufd	\$0xb1, %xmm1, %xmm4
+	por	%xmm1, %xmm4
+	pshufd	\$0, %xmm5, %xmm5		# in1infty
+	pshufd	\$0x1e, %xmm4, %xmm3
+	por	%xmm3, %xmm4
+	pxor	%xmm3, %xmm3
+	pcmpeqd	%xmm3, %xmm4
+	pshufd	\$0, %xmm4, %xmm4		# in2infty
+	 mov	0x40+8*0($b_ptr), $src0		# load original in1_z
+	 mov	0x40+8*1($b_ptr), $acc6
+	 mov	0x40+8*2($b_ptr), $acc7
+	 mov	0x40+8*3($b_ptr), $acc0
+	movq	$b_ptr, %xmm1
+
+	lea	0x40-$bias($b_ptr), $a_ptr
+	lea	$Z1sqr(%rsp), $r_ptr		# Z1^2
+	call	__ecp_sm2z256_sqr_mont$x	# p256_sqr_mont(Z1sqr, in1_z);
+
+	`&load_for_mul("$Z2sqr(%rsp)", "$in2_z(%rsp)", "$src0")`
+	lea	$S1(%rsp), $r_ptr		# S1 = Z2^3
+	call	__ecp_sm2z256_mul_mont$x	# p256_mul_mont(S1, Z2sqr, in2_z);
+
+	`&load_for_mul("$Z1sqr(%rsp)", "$in1_z(%rsp)", "$src0")`
+	lea	$S2(%rsp), $r_ptr		# S2 = Z1^3
+	call	__ecp_sm2z256_mul_mont$x	# p256_mul_mont(S2, Z1sqr, in1_z);
+
+	`&load_for_mul("$S1(%rsp)", "$in1_y(%rsp)", "$src0")`
+	lea	$S1(%rsp), $r_ptr		# S1 = Y1*Z2^3
+	call	__ecp_sm2z256_mul_mont$x	# p256_mul_mont(S1, S1, in1_y);
+
+	`&load_for_mul("$S2(%rsp)", "$in2_y(%rsp)", "$src0")`
+	lea	$S2(%rsp), $r_ptr		# S2 = Y2*Z1^3
+	call	__ecp_sm2z256_mul_mont$x	# p256_mul_mont(S2, S2, in2_y);
+
+	lea	$S1(%rsp), $b_ptr
+	lea	$R(%rsp), $r_ptr		# R = S2 - S1
+	call	__ecp_sm2z256_sub_from$x	# p256_sub(R, S2, S1);
+
+	or	$acc5, $acc4			# see if result is zero
+	movdqa	%xmm4, %xmm2
+	or	$acc0, $acc4
+	or	$acc1, $acc4
+	por	%xmm5, %xmm2			# in1infty || in2infty
+	movq	$acc4, %xmm3
+
+	`&load_for_mul("$Z2sqr(%rsp)", "$in1_x(%rsp)", "$src0")`
+	lea	$U1(%rsp), $r_ptr		# U1 = X1*Z2^2
+	call	__ecp_sm2z256_mul_mont$x	# p256_mul_mont(U1, in1_x, Z2sqr);
+
+	`&load_for_mul("$Z1sqr(%rsp)", "$in2_x(%rsp)", "$src0")`
+	lea	$U2(%rsp), $r_ptr		# U2 = X2*Z1^2
+	call	__ecp_sm2z256_mul_mont$x	# p256_mul_mont(U2, in2_x, Z1sqr);
+
+	lea	$U1(%rsp), $b_ptr
+	lea	$H(%rsp), $r_ptr		# H = U2 - U1
+	call	__ecp_sm2z256_sub_from$x	# p256_sub(H, U2, U1);
+
+	or	$acc5, $acc4			# see if result is zero
+	or	$acc0, $acc4
+	or	$acc1, $acc4
+
+	.byte	0x3e				# predict taken
+	jnz	.Ladd_proceed$x			# is_equal(U1,U2)?
+	movq	%xmm2, $acc0
+	movq	%xmm3, $acc1
+	test	$acc0, $acc0
+	jnz	.Ladd_proceed$x			# (in1infty || in2infty)?
+	test	$acc1, $acc1
+	jz	.Ladd_double$x			# is_equal(S1,S2)?
+
+	movq	%xmm0, $r_ptr			# restore $r_ptr
+	pxor	%xmm0, %xmm0
+	movdqu	%xmm0, 0x00($r_ptr)
+	movdqu	%xmm0, 0x10($r_ptr)
+	movdqu	%xmm0, 0x20($r_ptr)
+	movdqu	%xmm0, 0x30($r_ptr)
+	movdqu	%xmm0, 0x40($r_ptr)
+	movdqu	%xmm0, 0x50($r_ptr)
+	jmp	.Ladd_done$x
+
+.align	32
+.Ladd_double$x:
+	movq	%xmm1, $a_ptr			# restore $a_ptr
+	movq	%xmm0, $r_ptr			# restore $r_ptr
+	add	\$`32*(18-5)`, %rsp		# difference in frame sizes
+	jmp	.Lpoint_double_shortcut$x
+
+.align	32
+.Ladd_proceed$x:
+	`&load_for_sqr("$R(%rsp)", "$src0")`
+	lea	$Rsqr(%rsp), $r_ptr		# R^2
+	call	__ecp_sm2z256_sqr_mont$x	# p256_sqr_mont(Rsqr, R);
+
+	`&load_for_mul("$H(%rsp)", "$in1_z(%rsp)", "$src0")`
+	lea	$res_z(%rsp), $r_ptr		# Z3 = H*Z1*Z2
+	call	__ecp_sm2z256_mul_mont$x	# p256_mul_mont(res_z, H, in1_z);
+
+	`&load_for_sqr("$H(%rsp)", "$src0")`
+	lea	$Hsqr(%rsp), $r_ptr		# H^2
+	call	__ecp_sm2z256_sqr_mont$x	# p256_sqr_mont(Hsqr, H);
+
+	`&load_for_mul("$res_z(%rsp)", "$in2_z(%rsp)", "$src0")`
+	lea	$res_z(%rsp), $r_ptr		# Z3 = H*Z1*Z2
+	call	__ecp_sm2z256_mul_mont$x	# p256_mul_mont(res_z, res_z, in2_z);
+
+	`&load_for_mul("$Hsqr(%rsp)", "$H(%rsp)", "$src0")`
+	lea	$Hcub(%rsp), $r_ptr		# H^3
+	call	__ecp_sm2z256_mul_mont$x	# p256_mul_mont(Hcub, Hsqr, H);
+
+	`&load_for_mul("$Hsqr(%rsp)", "$U1(%rsp)", "$src0")`
+	lea	$U2(%rsp), $r_ptr		# U1*H^2
+	call	__ecp_sm2z256_mul_mont$x	# p256_mul_mont(U2, U1, Hsqr);
+___
+{
+#######################################################################
+# operate in 4-5-0-1 "name space" that matches multiplication output
+#
+my ($acc0,$acc1,$acc2,$acc3,$t3,$t4)=($acc4,$acc5,$acc0,$acc1,$acc2,$acc3);
+my ($poly1, $poly3)=($acc6,$acc7);
+
+$code.=<<___;
+	#lea	$U2(%rsp), $a_ptr
+	#lea	$Hsqr(%rsp), $r_ptr	# 2*U1*H^2
+	#call	__ecp_sm2z256_mul_by_2	# ecp_sm2z256_mul_by_2(Hsqr, U2);
+
+	xor	$t4, $t4
+	add	$acc0, $acc0		# a0:a3+a0:a3
+	lea	$Rsqr(%rsp), $a_ptr
+	adc	$acc1, $acc1
+	 mov	$acc0, $t0
+	adc	$acc2, $acc2
+	adc	$acc3, $acc3
+	 mov	$acc1, $t1
+	adc	\$0, $t4
+
+	sub	.Lpoly+8*0(%rip), $acc0
+	 mov	$acc2, $t2
+	sbb	.Lpoly+8*1(%rip), $acc1
+	sbb	.Lpoly+8*2(%rip), $acc2
+	 mov	$acc3, $t3
+	sbb	.Lpoly+8*3(%rip), $acc3
+	sbb	\$0, $t4
+
+	cmovc	$t0, $acc0
+	mov	8*0($a_ptr), $t0
+	cmovc	$t1, $acc1
+	mov	8*1($a_ptr), $t1
+	cmovc	$t2, $acc2
+	mov	8*2($a_ptr), $t2
+	cmovc	$t3, $acc3
+	mov	8*3($a_ptr), $t3
+
+	call	__ecp_sm2z256_sub$x		# p256_sub(res_x, Rsqr, Hsqr);
+
+	lea	$Hcub(%rsp), $b_ptr
+	lea	$res_x(%rsp), $r_ptr
+	call	__ecp_sm2z256_sub_from$x	# p256_sub(res_x, res_x, Hcub);
+
+	mov	$U2+8*0(%rsp), $t0
+	mov	$U2+8*1(%rsp), $t1
+	mov	$U2+8*2(%rsp), $t2
+	mov	$U2+8*3(%rsp), $t3
+	lea	$res_y(%rsp), $r_ptr
+
+	call	__ecp_sm2z256_sub$x		# p256_sub(res_y, U2, res_x);
+
+	mov	$acc0, 8*0($r_ptr)		# save the result, as
+	mov	$acc1, 8*1($r_ptr)		# __ecp_sm2z256_sub doesn't
+	mov	$acc2, 8*2($r_ptr)
+	mov	$acc3, 8*3($r_ptr)
+___
+}
+$code.=<<___;
+	`&load_for_mul("$S1(%rsp)", "$Hcub(%rsp)", "$src0")`
+	lea	$S2(%rsp), $r_ptr
+	call	__ecp_sm2z256_mul_mont$x	# p256_mul_mont(S2, S1, Hcub);
+
+	`&load_for_mul("$R(%rsp)", "$res_y(%rsp)", "$src0")`
+	lea	$res_y(%rsp), $r_ptr
+	call	__ecp_sm2z256_mul_mont$x	# p256_mul_mont(res_y, R, res_y);
+
+	lea	$S2(%rsp), $b_ptr
+	lea	$res_y(%rsp), $r_ptr
+	call	__ecp_sm2z256_sub_from$x	# p256_sub(res_y, res_y, S2);
+
+	movq	%xmm0, $r_ptr		# restore $r_ptr
+
+	movdqa	%xmm5, %xmm0		# copy_conditional(res_z, in2_z, in1infty);
+	movdqa	%xmm5, %xmm1
+	pandn	$res_z(%rsp), %xmm0
+	movdqa	%xmm5, %xmm2
+	pandn	$res_z+0x10(%rsp), %xmm1
+	movdqa	%xmm5, %xmm3
+	pand	$in2_z(%rsp), %xmm2
+	pand	$in2_z+0x10(%rsp), %xmm3
+	por	%xmm0, %xmm2
+	por	%xmm1, %xmm3
+
+	movdqa	%xmm4, %xmm0		# copy_conditional(res_z, in1_z, in2infty);
+	movdqa	%xmm4, %xmm1
+	pandn	%xmm2, %xmm0
+	movdqa	%xmm4, %xmm2
+	pandn	%xmm3, %xmm1
+	movdqa	%xmm4, %xmm3
+	pand	$in1_z(%rsp), %xmm2
+	pand	$in1_z+0x10(%rsp), %xmm3
+	por	%xmm0, %xmm2
+	por	%xmm1, %xmm3
+	movdqu	%xmm2, 0x40($r_ptr)
+	movdqu	%xmm3, 0x50($r_ptr)
+
+	movdqa	%xmm5, %xmm0		# copy_conditional(res_x, in2_x, in1infty);
+	movdqa	%xmm5, %xmm1
+	pandn	$res_x(%rsp), %xmm0
+	movdqa	%xmm5, %xmm2
+	pandn	$res_x+0x10(%rsp), %xmm1
+	movdqa	%xmm5, %xmm3
+	pand	$in2_x(%rsp), %xmm2
+	pand	$in2_x+0x10(%rsp), %xmm3
+	por	%xmm0, %xmm2
+	por	%xmm1, %xmm3
+
+	movdqa	%xmm4, %xmm0		# copy_conditional(res_x, in1_x, in2infty);
+	movdqa	%xmm4, %xmm1
+	pandn	%xmm2, %xmm0
+	movdqa	%xmm4, %xmm2
+	pandn	%xmm3, %xmm1
+	movdqa	%xmm4, %xmm3
+	pand	$in1_x(%rsp), %xmm2
+	pand	$in1_x+0x10(%rsp), %xmm3
+	por	%xmm0, %xmm2
+	por	%xmm1, %xmm3
+	movdqu	%xmm2, 0x00($r_ptr)
+	movdqu	%xmm3, 0x10($r_ptr)
+
+	movdqa	%xmm5, %xmm0		# copy_conditional(res_y, in2_y, in1infty);
+	movdqa	%xmm5, %xmm1
+	pandn	$res_y(%rsp), %xmm0
+	movdqa	%xmm5, %xmm2
+	pandn	$res_y+0x10(%rsp), %xmm1
+	movdqa	%xmm5, %xmm3
+	pand	$in2_y(%rsp), %xmm2
+	pand	$in2_y+0x10(%rsp), %xmm3
+	por	%xmm0, %xmm2
+	por	%xmm1, %xmm3
+
+	movdqa	%xmm4, %xmm0		# copy_conditional(res_y, in1_y, in2infty);
+	movdqa	%xmm4, %xmm1
+	pandn	%xmm2, %xmm0
+	movdqa	%xmm4, %xmm2
+	pandn	%xmm3, %xmm1
+	movdqa	%xmm4, %xmm3
+	pand	$in1_y(%rsp), %xmm2
+	pand	$in1_y+0x10(%rsp), %xmm3
+	por	%xmm0, %xmm2
+	por	%xmm1, %xmm3
+	movdqu	%xmm2, 0x20($r_ptr)
+	movdqu	%xmm3, 0x30($r_ptr)
+
+.Ladd_done$x:
+	add	\$32*18+8, %rsp
+	pop	%r15
+	pop	%r14
+	pop	%r13
+	pop	%r12
+	pop	%rbx
+	pop	%rbp
+	ret
+.size	ecp_sm2z256_point_add$sfx,.-ecp_sm2z256_point_add$sfx
+___
+}
+&gen_add("q");
+
+sub gen_add_affine () {
+    my $x = shift;
+    my ($src0,$sfx,$bias);
+    my ($U2,$S2,$H,$R,$Hsqr,$Hcub,$Rsqr,
+	$res_x,$res_y,$res_z,
+	$in1_x,$in1_y,$in1_z,
+	$in2_x,$in2_y)=map(32*$_,(0..14));
+    my $Z1sqr = $S2;
+
+    if ($x ne "x") {
+	$src0 = "%rax";
+	$sfx  = "";
+	$bias = 0;
+
+$code.=<<___;
+.globl	ecp_sm2z256_point_add_affine
+.type	ecp_sm2z256_point_add_affine,\@function,3
+.align	32
+ecp_sm2z256_point_add_affine:
+___
+$code.=<<___	if ($addx);
+	mov	\$0x80100, %ecx
+	and	OPENSSL_ia32cap_P+8(%rip), %ecx
+	cmp	\$0x80100, %ecx
+	je	.Lpoint_add_affinex
+___
+    } else {
+	$src0 = "%rdx";
+	$sfx  = "x";
+	$bias = 128;
+
+$code.=<<___;
+.type	ecp_sm2z256_point_add_affinex,\@function,3
+.align	32
+ecp_sm2z256_point_add_affinex:
+.Lpoint_add_affinex:
+___
+    }
+$code.=<<___;
+	push	%rbp
+	push	%rbx
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+	sub	\$32*15+8, %rsp
+
+	movdqu	0x00($a_ptr), %xmm0	# copy	*(P256_POINT *)$a_ptr
+	mov	$b_org, $b_ptr		# reassign
+	movdqu	0x10($a_ptr), %xmm1
+	movdqu	0x20($a_ptr), %xmm2
+	movdqu	0x30($a_ptr), %xmm3
+	movdqu	0x40($a_ptr), %xmm4
+	movdqu	0x50($a_ptr), %xmm5
+	 mov	0x40+8*0($a_ptr), $src0	# load original in1_z
+	 mov	0x40+8*1($a_ptr), $acc6
+	 mov	0x40+8*2($a_ptr), $acc7
+	 mov	0x40+8*3($a_ptr), $acc0
+	movdqa	%xmm0, $in1_x(%rsp)
+	movdqa	%xmm1, $in1_x+0x10(%rsp)
+	movdqa	%xmm2, $in1_y(%rsp)
+	movdqa	%xmm3, $in1_y+0x10(%rsp)
+	movdqa	%xmm4, $in1_z(%rsp)
+	movdqa	%xmm5, $in1_z+0x10(%rsp)
+	por	%xmm4, %xmm5
+
+	movdqu	0x00($b_ptr), %xmm0	# copy	*(P256_POINT_AFFINE *)$b_ptr
+	 pshufd	\$0xb1, %xmm5, %xmm3
+	movdqu	0x10($b_ptr), %xmm1
+	movdqu	0x20($b_ptr), %xmm2
+	 por	%xmm3, %xmm5
+	movdqu	0x30($b_ptr), %xmm3
+	movdqa	%xmm0, $in2_x(%rsp)
+	 pshufd	\$0x1e, %xmm5, %xmm4
+	movdqa	%xmm1, $in2_x+0x10(%rsp)
+	por	%xmm0, %xmm1
+	 movq	$r_ptr, %xmm0		# save $r_ptr
+	movdqa	%xmm2, $in2_y(%rsp)
+	movdqa	%xmm3, $in2_y+0x10(%rsp)
+	por	%xmm2, %xmm3
+	 por	%xmm4, %xmm5
+	 pxor	%xmm4, %xmm4
+	por	%xmm1, %xmm3
+
+	lea	0x40-$bias($a_ptr), $a_ptr	# $a_ptr is still valid
+	lea	$Z1sqr(%rsp), $r_ptr		# Z1^2
+	call	__ecp_sm2z256_sqr_mont$x	# p256_sqr_mont(Z1sqr, in1_z);
+
+	pcmpeqd	%xmm4, %xmm5
+	pshufd	\$0xb1, %xmm3, %xmm4
+	 mov	0x00($b_ptr), $src0		# $b_ptr is still valid
+	 #lea	0x00($b_ptr), $b_ptr
+	 mov	$acc4, $acc1			# harmonize sqr output and mul input
+	por	%xmm3, %xmm4
+	pshufd	\$0, %xmm5, %xmm5		# in1infty
+	pshufd	\$0x1e, %xmm4, %xmm3
+	 mov	$acc5, $acc2
+	por	%xmm3, %xmm4
+	pxor	%xmm3, %xmm3
+	 mov	$acc6, $acc3
+	pcmpeqd	%xmm3, %xmm4
+	pshufd	\$0, %xmm4, %xmm4		# in2infty
+
+	lea	$Z1sqr-$bias(%rsp), $a_ptr
+	mov	$acc7, $acc4
+	lea	$U2(%rsp), $r_ptr		# U2 = X2*Z1^2
+	call	__ecp_sm2z256_mul_mont$x	# p256_mul_mont(U2, Z1sqr, in2_x);
+
+	lea	$in1_x(%rsp), $b_ptr
+	lea	$H(%rsp), $r_ptr		# H = U2 - U1
+	call	__ecp_sm2z256_sub_from$x	# p256_sub(H, U2, in1_x);
+
+	`&load_for_mul("$Z1sqr(%rsp)", "$in1_z(%rsp)", "$src0")`
+	lea	$S2(%rsp), $r_ptr		# S2 = Z1^3
+	call	__ecp_sm2z256_mul_mont$x	# p256_mul_mont(S2, Z1sqr, in1_z);
+
+	`&load_for_mul("$H(%rsp)", "$in1_z(%rsp)", "$src0")`
+	lea	$res_z(%rsp), $r_ptr		# Z3 = H*Z1*Z2
+	call	__ecp_sm2z256_mul_mont$x	# p256_mul_mont(res_z, H, in1_z);
+
+	`&load_for_mul("$S2(%rsp)", "$in2_y(%rsp)", "$src0")`
+	lea	$S2(%rsp), $r_ptr		# S2 = Y2*Z1^3
+	call	__ecp_sm2z256_mul_mont$x	# p256_mul_mont(S2, S2, in2_y);
+
+	lea	$in1_y(%rsp), $b_ptr
+	lea	$R(%rsp), $r_ptr		# R = S2 - S1
+	call	__ecp_sm2z256_sub_from$x	# p256_sub(R, S2, in1_y);
+
+	`&load_for_sqr("$H(%rsp)", "$src0")`
+	lea	$Hsqr(%rsp), $r_ptr		# H^2
+	call	__ecp_sm2z256_sqr_mont$x	# p256_sqr_mont(Hsqr, H);
+
+	`&load_for_sqr("$R(%rsp)", "$src0")`
+	lea	$Rsqr(%rsp), $r_ptr		# R^2
+	call	__ecp_sm2z256_sqr_mont$x	# p256_sqr_mont(Rsqr, R);
+
+	`&load_for_mul("$H(%rsp)", "$Hsqr(%rsp)", "$src0")`
+	lea	$Hcub(%rsp), $r_ptr		# H^3
+	call	__ecp_sm2z256_mul_mont$x	# p256_mul_mont(Hcub, Hsqr, H);
+
+	`&load_for_mul("$Hsqr(%rsp)", "$in1_x(%rsp)", "$src0")`
+	lea	$U2(%rsp), $r_ptr		# U1*H^2
+	call	__ecp_sm2z256_mul_mont$x	# p256_mul_mont(U2, in1_x, Hsqr);
+___
+{
+#######################################################################
+# operate in 4-5-0-1 "name space" that matches multiplication output
+#
+my ($acc0,$acc1,$acc2,$acc3,$t3,$t4)=($acc4,$acc5,$acc0,$acc1,$acc2,$acc3);
+my ($poly1, $poly3)=($acc6,$acc7);
+
+$code.=<<___;
+	#lea	$U2(%rsp), $a_ptr
+	#lea	$Hsqr(%rsp), $r_ptr	# 2*U1*H^2
+	#call	__ecp_sm2z256_mul_by_2	# ecp_sm2z256_mul_by_2(Hsqr, U2);
+
+	xor	$t4, $t4
+	add	$acc0, $acc0		# a0:a3+a0:a3
+	lea	$Rsqr(%rsp), $a_ptr
+	adc	$acc1, $acc1
+	 mov	$acc0, $t0
+	adc	$acc2, $acc2
+	adc	$acc3, $acc3
+	 mov	$acc1, $t1
+	adc	\$0, $t4
+
+	sub	.Lpoly+8*0(%rip), $acc0
+	 mov	$acc2, $t2
+	sbb	.Lpoly+8*1(%rip), $acc1
+	sbb	.Lpoly+8*2(%rip), $acc2
+	 mov	$acc3, $t3
+	sbb	.Lpoly+8*3(%rip), $acc3
+	sbb	\$0, $t4
+
+	cmovc	$t0, $acc0
+	mov	8*0($a_ptr), $t0
+	cmovc	$t1, $acc1
+	mov	8*1($a_ptr), $t1
+	cmovc	$t2, $acc2
+	mov	8*2($a_ptr), $t2
+	cmovc	$t3, $acc3
+	mov	8*3($a_ptr), $t3
+
+	call	__ecp_sm2z256_sub$x		# p256_sub(res_x, Rsqr, Hsqr);
+
+	lea	$Hcub(%rsp), $b_ptr
+	lea	$res_x(%rsp), $r_ptr
+	call	__ecp_sm2z256_sub_from$x	# p256_sub(res_x, res_x, Hcub);
+
+	mov	$U2+8*0(%rsp), $t0
+	mov	$U2+8*1(%rsp), $t1
+	mov	$U2+8*2(%rsp), $t2
+	mov	$U2+8*3(%rsp), $t3
+	lea	$H(%rsp), $r_ptr
+
+	call	__ecp_sm2z256_sub$x		# p256_sub(H, U2, res_x);
+
+	mov	$acc0, 8*0($r_ptr)		# save the result, as
+	mov	$acc1, 8*1($r_ptr)		# __ecp_sm2z256_sub doesn't
+	mov	$acc2, 8*2($r_ptr)
+	mov	$acc3, 8*3($r_ptr)
+___
+}
+$code.=<<___;
+	`&load_for_mul("$Hcub(%rsp)", "$in1_y(%rsp)", "$src0")`
+	lea	$S2(%rsp), $r_ptr
+	call	__ecp_sm2z256_mul_mont$x	# p256_mul_mont(S2, Hcub, in1_y);
+
+	`&load_for_mul("$H(%rsp)", "$R(%rsp)", "$src0")`
+	lea	$H(%rsp), $r_ptr
+	call	__ecp_sm2z256_mul_mont$x	# p256_mul_mont(H, H, R);
+
+	lea	$S2(%rsp), $b_ptr
+	lea	$res_y(%rsp), $r_ptr
+	call	__ecp_sm2z256_sub_from$x	# p256_sub(res_y, H, S2);
+
+	movq	%xmm0, $r_ptr		# restore $r_ptr
+
+	movdqa	%xmm5, %xmm0		# copy_conditional(res_z, ONE, in1infty);
+	movdqa	%xmm5, %xmm1
+	pandn	$res_z(%rsp), %xmm0
+	movdqa	%xmm5, %xmm2
+	pandn	$res_z+0x10(%rsp), %xmm1
+	movdqa	%xmm5, %xmm3
+	pand	.LONE_mont(%rip), %xmm2
+	pand	.LONE_mont+0x10(%rip), %xmm3
+	por	%xmm0, %xmm2
+	por	%xmm1, %xmm3
+
+	movdqa	%xmm4, %xmm0		# copy_conditional(res_z, in1_z, in2infty);
+	movdqa	%xmm4, %xmm1
+	pandn	%xmm2, %xmm0
+	movdqa	%xmm4, %xmm2
+	pandn	%xmm3, %xmm1
+	movdqa	%xmm4, %xmm3
+	pand	$in1_z(%rsp), %xmm2
+	pand	$in1_z+0x10(%rsp), %xmm3
+	por	%xmm0, %xmm2
+	por	%xmm1, %xmm3
+	movdqu	%xmm2, 0x40($r_ptr)
+	movdqu	%xmm3, 0x50($r_ptr)
+
+	movdqa	%xmm5, %xmm0		# copy_conditional(res_x, in2_x, in1infty);
+	movdqa	%xmm5, %xmm1
+	pandn	$res_x(%rsp), %xmm0
+	movdqa	%xmm5, %xmm2
+	pandn	$res_x+0x10(%rsp), %xmm1
+	movdqa	%xmm5, %xmm3
+	pand	$in2_x(%rsp), %xmm2
+	pand	$in2_x+0x10(%rsp), %xmm3
+	por	%xmm0, %xmm2
+	por	%xmm1, %xmm3
+
+	movdqa	%xmm4, %xmm0		# copy_conditional(res_x, in1_x, in2infty);
+	movdqa	%xmm4, %xmm1
+	pandn	%xmm2, %xmm0
+	movdqa	%xmm4, %xmm2
+	pandn	%xmm3, %xmm1
+	movdqa	%xmm4, %xmm3
+	pand	$in1_x(%rsp), %xmm2
+	pand	$in1_x+0x10(%rsp), %xmm3
+	por	%xmm0, %xmm2
+	por	%xmm1, %xmm3
+	movdqu	%xmm2, 0x00($r_ptr)
+	movdqu	%xmm3, 0x10($r_ptr)
+
+	movdqa	%xmm5, %xmm0		# copy_conditional(res_y, in2_y, in1infty);
+	movdqa	%xmm5, %xmm1
+	pandn	$res_y(%rsp), %xmm0
+	movdqa	%xmm5, %xmm2
+	pandn	$res_y+0x10(%rsp), %xmm1
+	movdqa	%xmm5, %xmm3
+	pand	$in2_y(%rsp), %xmm2
+	pand	$in2_y+0x10(%rsp), %xmm3
+	por	%xmm0, %xmm2
+	por	%xmm1, %xmm3
+
+	movdqa	%xmm4, %xmm0		# copy_conditional(res_y, in1_y, in2infty);
+	movdqa	%xmm4, %xmm1
+	pandn	%xmm2, %xmm0
+	movdqa	%xmm4, %xmm2
+	pandn	%xmm3, %xmm1
+	movdqa	%xmm4, %xmm3
+	pand	$in1_y(%rsp), %xmm2
+	pand	$in1_y+0x10(%rsp), %xmm3
+	por	%xmm0, %xmm2
+	por	%xmm1, %xmm3
+	movdqu	%xmm2, 0x20($r_ptr)
+	movdqu	%xmm3, 0x30($r_ptr)
+
+	add	\$32*15+8, %rsp
+	pop	%r15
+	pop	%r14
+	pop	%r13
+	pop	%r12
+	pop	%rbx
+	pop	%rbp
+	ret
+.size	ecp_sm2z256_point_add_affine$sfx,.-ecp_sm2z256_point_add_affine$sfx
+___
+}
+&gen_add_affine("q");
+
+########################################################################
+# AD*X magic
+#
+if ($addx) {								{
+########################################################################
+# operate in 4-5-0-1 "name space" that matches multiplication output
+#
+my ($a0,$a1,$a2,$a3,$t3,$t4)=($acc4,$acc5,$acc0,$acc1,$acc2,$acc3);
+
+$code.=<<___;
+.type	__ecp_sm2z256_add_tox,\@abi-omnipotent
+.align	32
+__ecp_sm2z256_add_tox:
+	xor	$t4, $t4
+	adc	8*0($b_ptr), $a0
+	adc	8*1($b_ptr), $a1
+	 mov	$a0, $t0
+	adc	8*2($b_ptr), $a2
+	adc	8*3($b_ptr), $a3
+	 mov	$a1, $t1
+	adc	\$0, $t4
+
+	xor	$t3, $t3
+	sub	.Lpoly+8*0(%rip), $a0
+	 mov	$a2, $t2
+	sbb	.Lpoly+8*1(%rip), $a1
+	sbb	.Lpoly+8*2(%rip), $a2
+	 mov	$a3, $t3
+	sbb	.Lpoly+8*3(%rip), $a3
+	sbb	\$0, $t4
+
+	cmovc	$t0, $a0
+	cmovc	$t1, $a1
+	mov	$a0, 8*0($r_ptr)
+	cmovc	$t2, $a2
+	mov	$a1, 8*1($r_ptr)
+	cmovc	$t3, $a3
+	mov	$a2, 8*2($r_ptr)
+	mov	$a3, 8*3($r_ptr)
+
+	ret
+.size	__ecp_sm2z256_add_tox,.-__ecp_sm2z256_add_tox
+
+.type	__ecp_sm2z256_sub_fromx,\@abi-omnipotent
+.align	32
+__ecp_sm2z256_sub_fromx:
+	xor	$t4, $t4
+	sbb	8*0($b_ptr), $a0
+	sbb	8*1($b_ptr), $a1
+	 mov	$a0, $t0
+	sbb	8*2($b_ptr), $a2
+	sbb	8*3($b_ptr), $a3
+	 mov	$a1, $t1
+	sbb	\$0, $t4
+
+	xor	$t3, $t3
+	add	.Lpoly+8*0(%rip), $a0
+	 mov	$a2, $t2
+	adc	.Lpoly+8*1(%rip), $a1
+	adc	.Lpoly+8*2(%rip), $a2
+	 mov	$a3, $t3
+	adc	.Lpoly+8*3(%rip), $a3
+
+	bt	\$0, $t4
+	cmovnc	$t0, $a0
+	cmovnc	$t1, $a1
+	mov	$a0, 8*0($r_ptr)
+	cmovnc	$t2, $a2
+	mov	$a1, 8*1($r_ptr)
+	cmovnc	$t3, $a3
+	mov	$a2, 8*2($r_ptr)
+	mov	$a3, 8*3($r_ptr)
+
+	ret
+.size	__ecp_sm2z256_sub_fromx,.-__ecp_sm2z256_sub_fromx
+
+.type	__ecp_sm2z256_subx,\@abi-omnipotent
+.align	32
+__ecp_sm2z256_subx:
+	xor	$t4, $t4
+	sbb	$a0, $t0
+	sbb	$a1, $t1
+	 mov	$t0, $a0
+	sbb	$a2, $t2
+	sbb	$a3, $t3
+	 mov	$t1, $a1
+	sbb	\$0, $t4
+
+	xor	$a3 ,$a3
+	add	.Lpoly+8*0(%rip), $t0
+	 mov	$t2, $a2
+	adc	.Lpoly+8*1(%rip), $t1
+	adc	.Lpoly+8*2(%rip), $t2
+	 mov	$t3, $a3
+	adc	.Lpoly+8*3(%rip), $t3
+
+	bt	\$0, $t4
+	cmovc	$t0, $a0
+	cmovc	$t1, $a1
+	cmovc	$t2, $a2
+	cmovc	$t3, $a3
+
+	ret
+.size	__ecp_sm2z256_subx,.-__ecp_sm2z256_subx
+
+.type	__ecp_sm2z256_mul_by_2x,\@abi-omnipotent
+.align	32
+__ecp_sm2z256_mul_by_2x:
+	xor	$t4, $t4
+	adc	$a0, $a0		# a0:a3+a0:a3
+	adc	$a1, $a1
+	 mov	$a0, $t0
+	adc	$a2, $a2
+	adc	$a3, $a3
+	 mov	$a1, $t1
+	adc	\$0, $t4
+
+	xor	$t3, $t3
+	sub	.Lpoly+8*0(%rip), $a0
+	 mov	$a2, $t2
+	sbb	.Lpoly+8*1(%rip), $a1
+	sbb	.Lpoly+8*2(%rip), $a2
+	 mov	$a3, $t3
+	sbb	.Lpoly+8*3(%rip), $a3
+	sbb	\$0, $t4
+
+	cmovc	$t0, $a0
+	cmovc	$t1, $a1
+	mov	$a0, 8*0($r_ptr)
+	cmovc	$t2, $a2
+	mov	$a1, 8*1($r_ptr)
+	cmovc	$t3, $a3
+	mov	$a2, 8*2($r_ptr)
+	mov	$a3, 8*3($r_ptr)
+
+	ret
+.size	__ecp_sm2z256_mul_by_2x,.-__ecp_sm2z256_mul_by_2x
+___
+									}
+&gen_double("x");
+&gen_add("x");
+&gen_add_affine("x");
+}
+}}}
+
+########################################################################
+# Convert ecp_sm2z256_table.c to layout expected by ecp_sm2z_gather_w7
+#
+open TABLE,") {
+	s/TOBN\(\s*(0x[0-9a-f]+),\s*(0x[0-9a-f]+)\s*\)/push @arr,hex($2),hex($1)/geo;
+}
+close TABLE;
+
+die "insane number of elements" if ($#arr != 64*16*37-1);
+
+print <<___;
+.text
+.globl	ecp_sm2z256_precomputed
+.type	ecp_sm2z256_precomputed,\@object
+.align	4096
+ecp_sm2z256_precomputed:
+___
+while (@line=splice(@arr,0,16)) {
+	print ".long\t",join(',',map { sprintf "0x%08x",$_} @line),"\n";
+}
+print <<___;
+.size	ecp_sm2z256_precomputed,.-ecp_sm2z256_precomputed
+___
+
+$code =~ s/\`([^\`]*)\`/eval $1/gem;
+print $code;
+close STDOUT;
diff --git a/crypto/ec/build.info b/crypto/ec/build.info
new file mode 100644
index 000000000..863e4d6a9
--- /dev/null
+++ b/crypto/ec/build.info
@@ -0,0 +1,32 @@
+LIBS=../../libcrypto
+SOURCE[../../libcrypto]=\
+        ec_lib.c ecp_smpl.c ecp_mont.c ecp_nist.c ec_cvt.c ec_mult.c \
+        ec_err.c ec_curve.c ec_check.c ec_print.c ec_asn1.c ec_key.c \
+        ec2_smpl.c ec2_mult.c ec_ameth.c ec_pmeth.c eck_prn.c ecp_sm2p256.c \
+        ecp_nistp224.c ecp_nistp256.c ecp_nistp521.c ecp_nistputil.c \
+        ecp_oct.c ec2_oct.c ec_oct.c ec_kmeth.c ecdh_ossl.c ecdh_kdf.c \
+        ecdsa_ossl.c ecdsa_sign.c ecdsa_vrf.c curve25519.c ecx_meth.c \
+        {- $target{ec_asm_src} -}
+
+GENERATE[ecp_nistz256-x86.s]=asm/ecp_nistz256-x86.pl $(PERLASM_SCHEME) $(CFLAGS) $(LIB_CFLAGS) $(PROCESSOR)
+
+GENERATE[ecp_nistz256-x86_64.s]=asm/ecp_nistz256-x86_64.pl $(PERLASM_SCHEME)
+
+GENERATE[ecp_nistz256-avx2.s]=asm/ecp_nistz256-avx2.pl $(PERLASM_SCHEME)
+
+GENERATE[ecp_nistz256-sparcv9.S]=asm/ecp_nistz256-sparcv9.pl $(PERLASM_SCHEME)
+INCLUDE[ecp_nistz256-sparcv9.o]=..
+
+GENERATE[ecp_nistz256-armv4.S]=asm/ecp_nistz256-armv4.pl $(PERLASM_SCHEME)
+INCLUDE[ecp_nistz256-armv4.o]=..
+GENERATE[ecp_nistz256-armv8.S]=asm/ecp_nistz256-armv8.pl $(PERLASM_SCHEME)
+INCLUDE[ecp_nistz256-armv8.o]=..
+
+GENERATE[ecp_sm2z256-x86_64.s]=asm/ecp_sm2z256-x86_64.pl $(PERLASM_SCHEME)
+
+BEGINRAW[Makefile]
+{- $builddir -}/ecp_nistz256-%.S:	{- $sourcedir -}/asm/ecp_nistz256-%.pl
+	CC="$(CC)" $(PERL) $< $(PERLASM_SCHEME) $@
+{- $builddir -}/ecp_sm2z256-%.S:	{- $sourcedir -}/asm/ecp_sm2z256-%.pl
+	CC="$(CC)" $(PERL) $< $(PERLASM_SCHEME) $@
+ENDRAW[Makefile]
diff --git a/crypto/ec/curve25519.c b/crypto/ec/curve25519.c
new file mode 100644
index 000000000..e535823c0
--- /dev/null
+++ b/crypto/ec/curve25519.c
@@ -0,0 +1,3394 @@
+/*
+ * Copyright 2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/* This code is mostly taken from the ref10 version of Ed25519 in SUPERCOP
+ * 20141124 (http://bench.cr.yp.to/supercop.html).
+ *
+ * The field functions are shared by Ed25519 and X25519 where possible. */
+
+#include 
+#include "ec_lcl.h"
+
+
+/* fe means field element. Here the field is \Z/(2^255-19). An element t,
+ * entries t[0]...t[9], represents the integer t[0]+2^26 t[1]+2^51 t[2]+2^77
+ * t[3]+2^102 t[4]+...+2^230 t[9]. Bounds on each t[i] vary depending on
+ * context.  */
+typedef int32_t fe[10];
+
+static const int64_t kBottom25Bits = 0x1ffffffLL;
+static const int64_t kBottom26Bits = 0x3ffffffLL;
+static const int64_t kTop39Bits = 0xfffffffffe000000LL;
+static const int64_t kTop38Bits = 0xfffffffffc000000LL;
+
+static uint64_t load_3(const uint8_t *in) {
+  uint64_t result;
+  result = (uint64_t)in[0];
+  result |= ((uint64_t)in[1]) << 8;
+  result |= ((uint64_t)in[2]) << 16;
+  return result;
+}
+
+static uint64_t load_4(const uint8_t *in) {
+  uint64_t result;
+  result = (uint64_t)in[0];
+  result |= ((uint64_t)in[1]) << 8;
+  result |= ((uint64_t)in[2]) << 16;
+  result |= ((uint64_t)in[3]) << 24;
+  return result;
+}
+
+static void fe_frombytes(fe h, const uint8_t *s) {
+  /* Ignores top bit of h. */
+  int64_t h0 = load_4(s);
+  int64_t h1 = load_3(s + 4) << 6;
+  int64_t h2 = load_3(s + 7) << 5;
+  int64_t h3 = load_3(s + 10) << 3;
+  int64_t h4 = load_3(s + 13) << 2;
+  int64_t h5 = load_4(s + 16);
+  int64_t h6 = load_3(s + 20) << 7;
+  int64_t h7 = load_3(s + 23) << 5;
+  int64_t h8 = load_3(s + 26) << 4;
+  int64_t h9 = (load_3(s + 29) & 8388607) << 2;
+  int64_t carry0;
+  int64_t carry1;
+  int64_t carry2;
+  int64_t carry3;
+  int64_t carry4;
+  int64_t carry5;
+  int64_t carry6;
+  int64_t carry7;
+  int64_t carry8;
+  int64_t carry9;
+
+  carry9 = h9 + (1 << 24); h0 += (carry9 >> 25) * 19; h9 -= carry9 & kTop39Bits;
+  carry1 = h1 + (1 << 24); h2 += carry1 >> 25; h1 -= carry1 & kTop39Bits;
+  carry3 = h3 + (1 << 24); h4 += carry3 >> 25; h3 -= carry3 & kTop39Bits;
+  carry5 = h5 + (1 << 24); h6 += carry5 >> 25; h5 -= carry5 & kTop39Bits;
+  carry7 = h7 + (1 << 24); h8 += carry7 >> 25; h7 -= carry7 & kTop39Bits;
+
+  carry0 = h0 + (1 << 25); h1 += carry0 >> 26; h0 -= carry0 & kTop38Bits;
+  carry2 = h2 + (1 << 25); h3 += carry2 >> 26; h2 -= carry2 & kTop38Bits;
+  carry4 = h4 + (1 << 25); h5 += carry4 >> 26; h4 -= carry4 & kTop38Bits;
+  carry6 = h6 + (1 << 25); h7 += carry6 >> 26; h6 -= carry6 & kTop38Bits;
+  carry8 = h8 + (1 << 25); h9 += carry8 >> 26; h8 -= carry8 & kTop38Bits;
+
+  h[0] = h0;
+  h[1] = h1;
+  h[2] = h2;
+  h[3] = h3;
+  h[4] = h4;
+  h[5] = h5;
+  h[6] = h6;
+  h[7] = h7;
+  h[8] = h8;
+  h[9] = h9;
+}
+
+/* Preconditions:
+ *  |h| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc.
+ *
+ * Write p=2^255-19; q=floor(h/p).
+ * Basic claim: q = floor(2^(-255)(h + 19 2^(-25)h9 + 2^(-1))).
+ *
+ * Proof:
+ *   Have |h|<=p so |q|<=1 so |19^2 2^(-255) q|<1/4.
+ *   Also have |h-2^230 h9|<2^231 so |19 2^(-255)(h-2^230 h9)|<1/4.
+ *
+ *   Write y=2^(-1)-19^2 2^(-255)q-19 2^(-255)(h-2^230 h9).
+ *   Then 0> 25;
+  q = (h0 + q) >> 26;
+  q = (h1 + q) >> 25;
+  q = (h2 + q) >> 26;
+  q = (h3 + q) >> 25;
+  q = (h4 + q) >> 26;
+  q = (h5 + q) >> 25;
+  q = (h6 + q) >> 26;
+  q = (h7 + q) >> 25;
+  q = (h8 + q) >> 26;
+  q = (h9 + q) >> 25;
+
+  /* Goal: Output h-(2^255-19)q, which is between 0 and 2^255-20. */
+  h0 += 19 * q;
+  /* Goal: Output h-2^255 q, which is between 0 and 2^255-20. */
+
+  h1 += h0 >> 26; h0 &= kBottom26Bits;
+  h2 += h1 >> 25; h1 &= kBottom25Bits;
+  h3 += h2 >> 26; h2 &= kBottom26Bits;
+  h4 += h3 >> 25; h3 &= kBottom25Bits;
+  h5 += h4 >> 26; h4 &= kBottom26Bits;
+  h6 += h5 >> 25; h5 &= kBottom25Bits;
+  h7 += h6 >> 26; h6 &= kBottom26Bits;
+  h8 += h7 >> 25; h7 &= kBottom25Bits;
+  h9 += h8 >> 26; h8 &= kBottom26Bits;
+                  h9 &= kBottom25Bits;
+                  /* h10 = carry9 */
+
+  /* Goal: Output h0+...+2^255 h10-2^255 q, which is between 0 and 2^255-20.
+   * Have h0+...+2^230 h9 between 0 and 2^255-1;
+   * evidently 2^255 h10-2^255 q = 0.
+   * Goal: Output h0+...+2^230 h9.  */
+
+  s[0] = h0 >> 0;
+  s[1] = h0 >> 8;
+  s[2] = h0 >> 16;
+  s[3] = (h0 >> 24) | ((uint32_t)(h1) << 2);
+  s[4] = h1 >> 6;
+  s[5] = h1 >> 14;
+  s[6] = (h1 >> 22) | ((uint32_t)(h2) << 3);
+  s[7] = h2 >> 5;
+  s[8] = h2 >> 13;
+  s[9] = (h2 >> 21) | ((uint32_t)(h3) << 5);
+  s[10] = h3 >> 3;
+  s[11] = h3 >> 11;
+  s[12] = (h3 >> 19) | ((uint32_t)(h4) << 6);
+  s[13] = h4 >> 2;
+  s[14] = h4 >> 10;
+  s[15] = h4 >> 18;
+  s[16] = h5 >> 0;
+  s[17] = h5 >> 8;
+  s[18] = h5 >> 16;
+  s[19] = (h5 >> 24) | ((uint32_t)(h6) << 1);
+  s[20] = h6 >> 7;
+  s[21] = h6 >> 15;
+  s[22] = (h6 >> 23) | ((uint32_t)(h7) << 3);
+  s[23] = h7 >> 5;
+  s[24] = h7 >> 13;
+  s[25] = (h7 >> 21) | ((uint32_t)(h8) << 4);
+  s[26] = h8 >> 4;
+  s[27] = h8 >> 12;
+  s[28] = (h8 >> 20) | ((uint32_t)(h9) << 6);
+  s[29] = h9 >> 2;
+  s[30] = h9 >> 10;
+  s[31] = h9 >> 18;
+}
+
+/* h = f */
+static void fe_copy(fe h, const fe f) {
+  memmove(h, f, sizeof(int32_t) * 10);
+}
+
+/* h = 0 */
+static void fe_0(fe h) { memset(h, 0, sizeof(int32_t) * 10); }
+
+/* h = 1 */
+static void fe_1(fe h) {
+  memset(h, 0, sizeof(int32_t) * 10);
+  h[0] = 1;
+}
+
+/* h = f + g
+ * Can overlap h with f or g.
+ *
+ * Preconditions:
+ *    |f| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc.
+ *    |g| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc.
+ *
+ * Postconditions:
+ *    |h| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. */
+static void fe_add(fe h, const fe f, const fe g) {
+  unsigned i;
+  for (i = 0; i < 10; i++) {
+    h[i] = f[i] + g[i];
+  }
+}
+
+/* h = f - g
+ * Can overlap h with f or g.
+ *
+ * Preconditions:
+ *    |f| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc.
+ *    |g| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc.
+ *
+ * Postconditions:
+ *    |h| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. */
+static void fe_sub(fe h, const fe f, const fe g) {
+  unsigned i;
+  for (i = 0; i < 10; i++) {
+    h[i] = f[i] - g[i];
+  }
+}
+
+/* h = f * g
+ * Can overlap h with f or g.
+ *
+ * Preconditions:
+ *    |f| bounded by 1.65*2^26,1.65*2^25,1.65*2^26,1.65*2^25,etc.
+ *    |g| bounded by 1.65*2^26,1.65*2^25,1.65*2^26,1.65*2^25,etc.
+ *
+ * Postconditions:
+ *    |h| bounded by 1.01*2^25,1.01*2^24,1.01*2^25,1.01*2^24,etc.
+ *
+ * Notes on implementation strategy:
+ *
+ * Using schoolbook multiplication.
+ * Karatsuba would save a little in some cost models.
+ *
+ * Most multiplications by 2 and 19 are 32-bit precomputations;
+ * cheaper than 64-bit postcomputations.
+ *
+ * There is one remaining multiplication by 19 in the carry chain;
+ * one *19 precomputation can be merged into this,
+ * but the resulting data flow is considerably less clean.
+ *
+ * There are 12 carries below.
+ * 10 of them are 2-way parallelizable and vectorizable.
+ * Can get away with 11 carries, but then data flow is much deeper.
+ *
+ * With tighter constraints on inputs can squeeze carries into int32. */
+static void fe_mul(fe h, const fe f, const fe g) {
+  int32_t f0 = f[0];
+  int32_t f1 = f[1];
+  int32_t f2 = f[2];
+  int32_t f3 = f[3];
+  int32_t f4 = f[4];
+  int32_t f5 = f[5];
+  int32_t f6 = f[6];
+  int32_t f7 = f[7];
+  int32_t f8 = f[8];
+  int32_t f9 = f[9];
+  int32_t g0 = g[0];
+  int32_t g1 = g[1];
+  int32_t g2 = g[2];
+  int32_t g3 = g[3];
+  int32_t g4 = g[4];
+  int32_t g5 = g[5];
+  int32_t g6 = g[6];
+  int32_t g7 = g[7];
+  int32_t g8 = g[8];
+  int32_t g9 = g[9];
+  int32_t g1_19 = 19 * g1; /* 1.959375*2^29 */
+  int32_t g2_19 = 19 * g2; /* 1.959375*2^30; still ok */
+  int32_t g3_19 = 19 * g3;
+  int32_t g4_19 = 19 * g4;
+  int32_t g5_19 = 19 * g5;
+  int32_t g6_19 = 19 * g6;
+  int32_t g7_19 = 19 * g7;
+  int32_t g8_19 = 19 * g8;
+  int32_t g9_19 = 19 * g9;
+  int32_t f1_2 = 2 * f1;
+  int32_t f3_2 = 2 * f3;
+  int32_t f5_2 = 2 * f5;
+  int32_t f7_2 = 2 * f7;
+  int32_t f9_2 = 2 * f9;
+  int64_t f0g0    = f0   * (int64_t) g0;
+  int64_t f0g1    = f0   * (int64_t) g1;
+  int64_t f0g2    = f0   * (int64_t) g2;
+  int64_t f0g3    = f0   * (int64_t) g3;
+  int64_t f0g4    = f0   * (int64_t) g4;
+  int64_t f0g5    = f0   * (int64_t) g5;
+  int64_t f0g6    = f0   * (int64_t) g6;
+  int64_t f0g7    = f0   * (int64_t) g7;
+  int64_t f0g8    = f0   * (int64_t) g8;
+  int64_t f0g9    = f0   * (int64_t) g9;
+  int64_t f1g0    = f1   * (int64_t) g0;
+  int64_t f1g1_2  = f1_2 * (int64_t) g1;
+  int64_t f1g2    = f1   * (int64_t) g2;
+  int64_t f1g3_2  = f1_2 * (int64_t) g3;
+  int64_t f1g4    = f1   * (int64_t) g4;
+  int64_t f1g5_2  = f1_2 * (int64_t) g5;
+  int64_t f1g6    = f1   * (int64_t) g6;
+  int64_t f1g7_2  = f1_2 * (int64_t) g7;
+  int64_t f1g8    = f1   * (int64_t) g8;
+  int64_t f1g9_38 = f1_2 * (int64_t) g9_19;
+  int64_t f2g0    = f2   * (int64_t) g0;
+  int64_t f2g1    = f2   * (int64_t) g1;
+  int64_t f2g2    = f2   * (int64_t) g2;
+  int64_t f2g3    = f2   * (int64_t) g3;
+  int64_t f2g4    = f2   * (int64_t) g4;
+  int64_t f2g5    = f2   * (int64_t) g5;
+  int64_t f2g6    = f2   * (int64_t) g6;
+  int64_t f2g7    = f2   * (int64_t) g7;
+  int64_t f2g8_19 = f2   * (int64_t) g8_19;
+  int64_t f2g9_19 = f2   * (int64_t) g9_19;
+  int64_t f3g0    = f3   * (int64_t) g0;
+  int64_t f3g1_2  = f3_2 * (int64_t) g1;
+  int64_t f3g2    = f3   * (int64_t) g2;
+  int64_t f3g3_2  = f3_2 * (int64_t) g3;
+  int64_t f3g4    = f3   * (int64_t) g4;
+  int64_t f3g5_2  = f3_2 * (int64_t) g5;
+  int64_t f3g6    = f3   * (int64_t) g6;
+  int64_t f3g7_38 = f3_2 * (int64_t) g7_19;
+  int64_t f3g8_19 = f3   * (int64_t) g8_19;
+  int64_t f3g9_38 = f3_2 * (int64_t) g9_19;
+  int64_t f4g0    = f4   * (int64_t) g0;
+  int64_t f4g1    = f4   * (int64_t) g1;
+  int64_t f4g2    = f4   * (int64_t) g2;
+  int64_t f4g3    = f4   * (int64_t) g3;
+  int64_t f4g4    = f4   * (int64_t) g4;
+  int64_t f4g5    = f4   * (int64_t) g5;
+  int64_t f4g6_19 = f4   * (int64_t) g6_19;
+  int64_t f4g7_19 = f4   * (int64_t) g7_19;
+  int64_t f4g8_19 = f4   * (int64_t) g8_19;
+  int64_t f4g9_19 = f4   * (int64_t) g9_19;
+  int64_t f5g0    = f5   * (int64_t) g0;
+  int64_t f5g1_2  = f5_2 * (int64_t) g1;
+  int64_t f5g2    = f5   * (int64_t) g2;
+  int64_t f5g3_2  = f5_2 * (int64_t) g3;
+  int64_t f5g4    = f5   * (int64_t) g4;
+  int64_t f5g5_38 = f5_2 * (int64_t) g5_19;
+  int64_t f5g6_19 = f5   * (int64_t) g6_19;
+  int64_t f5g7_38 = f5_2 * (int64_t) g7_19;
+  int64_t f5g8_19 = f5   * (int64_t) g8_19;
+  int64_t f5g9_38 = f5_2 * (int64_t) g9_19;
+  int64_t f6g0    = f6   * (int64_t) g0;
+  int64_t f6g1    = f6   * (int64_t) g1;
+  int64_t f6g2    = f6   * (int64_t) g2;
+  int64_t f6g3    = f6   * (int64_t) g3;
+  int64_t f6g4_19 = f6   * (int64_t) g4_19;
+  int64_t f6g5_19 = f6   * (int64_t) g5_19;
+  int64_t f6g6_19 = f6   * (int64_t) g6_19;
+  int64_t f6g7_19 = f6   * (int64_t) g7_19;
+  int64_t f6g8_19 = f6   * (int64_t) g8_19;
+  int64_t f6g9_19 = f6   * (int64_t) g9_19;
+  int64_t f7g0    = f7   * (int64_t) g0;
+  int64_t f7g1_2  = f7_2 * (int64_t) g1;
+  int64_t f7g2    = f7   * (int64_t) g2;
+  int64_t f7g3_38 = f7_2 * (int64_t) g3_19;
+  int64_t f7g4_19 = f7   * (int64_t) g4_19;
+  int64_t f7g5_38 = f7_2 * (int64_t) g5_19;
+  int64_t f7g6_19 = f7   * (int64_t) g6_19;
+  int64_t f7g7_38 = f7_2 * (int64_t) g7_19;
+  int64_t f7g8_19 = f7   * (int64_t) g8_19;
+  int64_t f7g9_38 = f7_2 * (int64_t) g9_19;
+  int64_t f8g0    = f8   * (int64_t) g0;
+  int64_t f8g1    = f8   * (int64_t) g1;
+  int64_t f8g2_19 = f8   * (int64_t) g2_19;
+  int64_t f8g3_19 = f8   * (int64_t) g3_19;
+  int64_t f8g4_19 = f8   * (int64_t) g4_19;
+  int64_t f8g5_19 = f8   * (int64_t) g5_19;
+  int64_t f8g6_19 = f8   * (int64_t) g6_19;
+  int64_t f8g7_19 = f8   * (int64_t) g7_19;
+  int64_t f8g8_19 = f8   * (int64_t) g8_19;
+  int64_t f8g9_19 = f8   * (int64_t) g9_19;
+  int64_t f9g0    = f9   * (int64_t) g0;
+  int64_t f9g1_38 = f9_2 * (int64_t) g1_19;
+  int64_t f9g2_19 = f9   * (int64_t) g2_19;
+  int64_t f9g3_38 = f9_2 * (int64_t) g3_19;
+  int64_t f9g4_19 = f9   * (int64_t) g4_19;
+  int64_t f9g5_38 = f9_2 * (int64_t) g5_19;
+  int64_t f9g6_19 = f9   * (int64_t) g6_19;
+  int64_t f9g7_38 = f9_2 * (int64_t) g7_19;
+  int64_t f9g8_19 = f9   * (int64_t) g8_19;
+  int64_t f9g9_38 = f9_2 * (int64_t) g9_19;
+  int64_t h0 = f0g0+f1g9_38+f2g8_19+f3g7_38+f4g6_19+f5g5_38+f6g4_19+f7g3_38+f8g2_19+f9g1_38;
+  int64_t h1 = f0g1+f1g0   +f2g9_19+f3g8_19+f4g7_19+f5g6_19+f6g5_19+f7g4_19+f8g3_19+f9g2_19;
+  int64_t h2 = f0g2+f1g1_2 +f2g0   +f3g9_38+f4g8_19+f5g7_38+f6g6_19+f7g5_38+f8g4_19+f9g3_38;
+  int64_t h3 = f0g3+f1g2   +f2g1   +f3g0   +f4g9_19+f5g8_19+f6g7_19+f7g6_19+f8g5_19+f9g4_19;
+  int64_t h4 = f0g4+f1g3_2 +f2g2   +f3g1_2 +f4g0   +f5g9_38+f6g8_19+f7g7_38+f8g6_19+f9g5_38;
+  int64_t h5 = f0g5+f1g4   +f2g3   +f3g2   +f4g1   +f5g0   +f6g9_19+f7g8_19+f8g7_19+f9g6_19;
+  int64_t h6 = f0g6+f1g5_2 +f2g4   +f3g3_2 +f4g2   +f5g1_2 +f6g0   +f7g9_38+f8g8_19+f9g7_38;
+  int64_t h7 = f0g7+f1g6   +f2g5   +f3g4   +f4g3   +f5g2   +f6g1   +f7g0   +f8g9_19+f9g8_19;
+  int64_t h8 = f0g8+f1g7_2 +f2g6   +f3g5_2 +f4g4   +f5g3_2 +f6g2   +f7g1_2 +f8g0   +f9g9_38;
+  int64_t h9 = f0g9+f1g8   +f2g7   +f3g6   +f4g5   +f5g4   +f6g3   +f7g2   +f8g1   +f9g0   ;
+  int64_t carry0;
+  int64_t carry1;
+  int64_t carry2;
+  int64_t carry3;
+  int64_t carry4;
+  int64_t carry5;
+  int64_t carry6;
+  int64_t carry7;
+  int64_t carry8;
+  int64_t carry9;
+
+  /* |h0| <= (1.65*1.65*2^52*(1+19+19+19+19)+1.65*1.65*2^50*(38+38+38+38+38))
+   *   i.e. |h0| <= 1.4*2^60; narrower ranges for h2, h4, h6, h8
+   * |h1| <= (1.65*1.65*2^51*(1+1+19+19+19+19+19+19+19+19))
+   *   i.e. |h1| <= 1.7*2^59; narrower ranges for h3, h5, h7, h9 */
+
+  carry0 = h0 + (1 << 25); h1 += carry0 >> 26; h0 -= carry0 & kTop38Bits;
+  carry4 = h4 + (1 << 25); h5 += carry4 >> 26; h4 -= carry4 & kTop38Bits;
+  /* |h0| <= 2^25 */
+  /* |h4| <= 2^25 */
+  /* |h1| <= 1.71*2^59 */
+  /* |h5| <= 1.71*2^59 */
+
+  carry1 = h1 + (1 << 24); h2 += carry1 >> 25; h1 -= carry1 & kTop39Bits;
+  carry5 = h5 + (1 << 24); h6 += carry5 >> 25; h5 -= carry5 & kTop39Bits;
+  /* |h1| <= 2^24; from now on fits into int32 */
+  /* |h5| <= 2^24; from now on fits into int32 */
+  /* |h2| <= 1.41*2^60 */
+  /* |h6| <= 1.41*2^60 */
+
+  carry2 = h2 + (1 << 25); h3 += carry2 >> 26; h2 -= carry2 & kTop38Bits;
+  carry6 = h6 + (1 << 25); h7 += carry6 >> 26; h6 -= carry6 & kTop38Bits;
+  /* |h2| <= 2^25; from now on fits into int32 unchanged */
+  /* |h6| <= 2^25; from now on fits into int32 unchanged */
+  /* |h3| <= 1.71*2^59 */
+  /* |h7| <= 1.71*2^59 */
+
+  carry3 = h3 + (1 << 24); h4 += carry3 >> 25; h3 -= carry3 & kTop39Bits;
+  carry7 = h7 + (1 << 24); h8 += carry7 >> 25; h7 -= carry7 & kTop39Bits;
+  /* |h3| <= 2^24; from now on fits into int32 unchanged */
+  /* |h7| <= 2^24; from now on fits into int32 unchanged */
+  /* |h4| <= 1.72*2^34 */
+  /* |h8| <= 1.41*2^60 */
+
+  carry4 = h4 + (1 << 25); h5 += carry4 >> 26; h4 -= carry4 & kTop38Bits;
+  carry8 = h8 + (1 << 25); h9 += carry8 >> 26; h8 -= carry8 & kTop38Bits;
+  /* |h4| <= 2^25; from now on fits into int32 unchanged */
+  /* |h8| <= 2^25; from now on fits into int32 unchanged */
+  /* |h5| <= 1.01*2^24 */
+  /* |h9| <= 1.71*2^59 */
+
+  carry9 = h9 + (1 << 24); h0 += (carry9 >> 25) * 19; h9 -= carry9 & kTop39Bits;
+  /* |h9| <= 2^24; from now on fits into int32 unchanged */
+  /* |h0| <= 1.1*2^39 */
+
+  carry0 = h0 + (1 << 25); h1 += carry0 >> 26; h0 -= carry0 & kTop38Bits;
+  /* |h0| <= 2^25; from now on fits into int32 unchanged */
+  /* |h1| <= 1.01*2^24 */
+
+  h[0] = h0;
+  h[1] = h1;
+  h[2] = h2;
+  h[3] = h3;
+  h[4] = h4;
+  h[5] = h5;
+  h[6] = h6;
+  h[7] = h7;
+  h[8] = h8;
+  h[9] = h9;
+}
+
+/* h = f * f
+ * Can overlap h with f.
+ *
+ * Preconditions:
+ *    |f| bounded by 1.65*2^26,1.65*2^25,1.65*2^26,1.65*2^25,etc.
+ *
+ * Postconditions:
+ *    |h| bounded by 1.01*2^25,1.01*2^24,1.01*2^25,1.01*2^24,etc.
+ *
+ * See fe_mul.c for discussion of implementation strategy. */
+static void fe_sq(fe h, const fe f) {
+  int32_t f0 = f[0];
+  int32_t f1 = f[1];
+  int32_t f2 = f[2];
+  int32_t f3 = f[3];
+  int32_t f4 = f[4];
+  int32_t f5 = f[5];
+  int32_t f6 = f[6];
+  int32_t f7 = f[7];
+  int32_t f8 = f[8];
+  int32_t f9 = f[9];
+  int32_t f0_2 = 2 * f0;
+  int32_t f1_2 = 2 * f1;
+  int32_t f2_2 = 2 * f2;
+  int32_t f3_2 = 2 * f3;
+  int32_t f4_2 = 2 * f4;
+  int32_t f5_2 = 2 * f5;
+  int32_t f6_2 = 2 * f6;
+  int32_t f7_2 = 2 * f7;
+  int32_t f5_38 = 38 * f5; /* 1.959375*2^30 */
+  int32_t f6_19 = 19 * f6; /* 1.959375*2^30 */
+  int32_t f7_38 = 38 * f7; /* 1.959375*2^30 */
+  int32_t f8_19 = 19 * f8; /* 1.959375*2^30 */
+  int32_t f9_38 = 38 * f9; /* 1.959375*2^30 */
+  int64_t f0f0    = f0   * (int64_t) f0;
+  int64_t f0f1_2  = f0_2 * (int64_t) f1;
+  int64_t f0f2_2  = f0_2 * (int64_t) f2;
+  int64_t f0f3_2  = f0_2 * (int64_t) f3;
+  int64_t f0f4_2  = f0_2 * (int64_t) f4;
+  int64_t f0f5_2  = f0_2 * (int64_t) f5;
+  int64_t f0f6_2  = f0_2 * (int64_t) f6;
+  int64_t f0f7_2  = f0_2 * (int64_t) f7;
+  int64_t f0f8_2  = f0_2 * (int64_t) f8;
+  int64_t f0f9_2  = f0_2 * (int64_t) f9;
+  int64_t f1f1_2  = f1_2 * (int64_t) f1;
+  int64_t f1f2_2  = f1_2 * (int64_t) f2;
+  int64_t f1f3_4  = f1_2 * (int64_t) f3_2;
+  int64_t f1f4_2  = f1_2 * (int64_t) f4;
+  int64_t f1f5_4  = f1_2 * (int64_t) f5_2;
+  int64_t f1f6_2  = f1_2 * (int64_t) f6;
+  int64_t f1f7_4  = f1_2 * (int64_t) f7_2;
+  int64_t f1f8_2  = f1_2 * (int64_t) f8;
+  int64_t f1f9_76 = f1_2 * (int64_t) f9_38;
+  int64_t f2f2    = f2   * (int64_t) f2;
+  int64_t f2f3_2  = f2_2 * (int64_t) f3;
+  int64_t f2f4_2  = f2_2 * (int64_t) f4;
+  int64_t f2f5_2  = f2_2 * (int64_t) f5;
+  int64_t f2f6_2  = f2_2 * (int64_t) f6;
+  int64_t f2f7_2  = f2_2 * (int64_t) f7;
+  int64_t f2f8_38 = f2_2 * (int64_t) f8_19;
+  int64_t f2f9_38 = f2   * (int64_t) f9_38;
+  int64_t f3f3_2  = f3_2 * (int64_t) f3;
+  int64_t f3f4_2  = f3_2 * (int64_t) f4;
+  int64_t f3f5_4  = f3_2 * (int64_t) f5_2;
+  int64_t f3f6_2  = f3_2 * (int64_t) f6;
+  int64_t f3f7_76 = f3_2 * (int64_t) f7_38;
+  int64_t f3f8_38 = f3_2 * (int64_t) f8_19;
+  int64_t f3f9_76 = f3_2 * (int64_t) f9_38;
+  int64_t f4f4    = f4   * (int64_t) f4;
+  int64_t f4f5_2  = f4_2 * (int64_t) f5;
+  int64_t f4f6_38 = f4_2 * (int64_t) f6_19;
+  int64_t f4f7_38 = f4   * (int64_t) f7_38;
+  int64_t f4f8_38 = f4_2 * (int64_t) f8_19;
+  int64_t f4f9_38 = f4   * (int64_t) f9_38;
+  int64_t f5f5_38 = f5   * (int64_t) f5_38;
+  int64_t f5f6_38 = f5_2 * (int64_t) f6_19;
+  int64_t f5f7_76 = f5_2 * (int64_t) f7_38;
+  int64_t f5f8_38 = f5_2 * (int64_t) f8_19;
+  int64_t f5f9_76 = f5_2 * (int64_t) f9_38;
+  int64_t f6f6_19 = f6   * (int64_t) f6_19;
+  int64_t f6f7_38 = f6   * (int64_t) f7_38;
+  int64_t f6f8_38 = f6_2 * (int64_t) f8_19;
+  int64_t f6f9_38 = f6   * (int64_t) f9_38;
+  int64_t f7f7_38 = f7   * (int64_t) f7_38;
+  int64_t f7f8_38 = f7_2 * (int64_t) f8_19;
+  int64_t f7f9_76 = f7_2 * (int64_t) f9_38;
+  int64_t f8f8_19 = f8   * (int64_t) f8_19;
+  int64_t f8f9_38 = f8   * (int64_t) f9_38;
+  int64_t f9f9_38 = f9   * (int64_t) f9_38;
+  int64_t h0 = f0f0  +f1f9_76+f2f8_38+f3f7_76+f4f6_38+f5f5_38;
+  int64_t h1 = f0f1_2+f2f9_38+f3f8_38+f4f7_38+f5f6_38;
+  int64_t h2 = f0f2_2+f1f1_2 +f3f9_76+f4f8_38+f5f7_76+f6f6_19;
+  int64_t h3 = f0f3_2+f1f2_2 +f4f9_38+f5f8_38+f6f7_38;
+  int64_t h4 = f0f4_2+f1f3_4 +f2f2   +f5f9_76+f6f8_38+f7f7_38;
+  int64_t h5 = f0f5_2+f1f4_2 +f2f3_2 +f6f9_38+f7f8_38;
+  int64_t h6 = f0f6_2+f1f5_4 +f2f4_2 +f3f3_2 +f7f9_76+f8f8_19;
+  int64_t h7 = f0f7_2+f1f6_2 +f2f5_2 +f3f4_2 +f8f9_38;
+  int64_t h8 = f0f8_2+f1f7_4 +f2f6_2 +f3f5_4 +f4f4   +f9f9_38;
+  int64_t h9 = f0f9_2+f1f8_2 +f2f7_2 +f3f6_2 +f4f5_2;
+  int64_t carry0;
+  int64_t carry1;
+  int64_t carry2;
+  int64_t carry3;
+  int64_t carry4;
+  int64_t carry5;
+  int64_t carry6;
+  int64_t carry7;
+  int64_t carry8;
+  int64_t carry9;
+
+  carry0 = h0 + (1 << 25); h1 += carry0 >> 26; h0 -= carry0 & kTop38Bits;
+  carry4 = h4 + (1 << 25); h5 += carry4 >> 26; h4 -= carry4 & kTop38Bits;
+
+  carry1 = h1 + (1 << 24); h2 += carry1 >> 25; h1 -= carry1 & kTop39Bits;
+  carry5 = h5 + (1 << 24); h6 += carry5 >> 25; h5 -= carry5 & kTop39Bits;
+
+  carry2 = h2 + (1 << 25); h3 += carry2 >> 26; h2 -= carry2 & kTop38Bits;
+  carry6 = h6 + (1 << 25); h7 += carry6 >> 26; h6 -= carry6 & kTop38Bits;
+
+  carry3 = h3 + (1 << 24); h4 += carry3 >> 25; h3 -= carry3 & kTop39Bits;
+  carry7 = h7 + (1 << 24); h8 += carry7 >> 25; h7 -= carry7 & kTop39Bits;
+
+  carry4 = h4 + (1 << 25); h5 += carry4 >> 26; h4 -= carry4 & kTop38Bits;
+  carry8 = h8 + (1 << 25); h9 += carry8 >> 26; h8 -= carry8 & kTop38Bits;
+
+  carry9 = h9 + (1 << 24); h0 += (carry9 >> 25) * 19; h9 -= carry9 & kTop39Bits;
+
+  carry0 = h0 + (1 << 25); h1 += carry0 >> 26; h0 -= carry0 & kTop38Bits;
+
+  h[0] = h0;
+  h[1] = h1;
+  h[2] = h2;
+  h[3] = h3;
+  h[4] = h4;
+  h[5] = h5;
+  h[6] = h6;
+  h[7] = h7;
+  h[8] = h8;
+  h[9] = h9;
+}
+
+static void fe_invert(fe out, const fe z) {
+  fe t0;
+  fe t1;
+  fe t2;
+  fe t3;
+  int i;
+
+  /*
+   * Compute z ** -1 = z ** (2 ** 255 - 19 - 2) with the exponent as
+   * 2 ** 255 - 21 = (2 ** 5) * (2 ** 250 - 1) + 11.
+   */
+
+  /* t0 = z ** 2 */
+  fe_sq(t0, z);
+
+  /* t1 = t0 ** (2 ** 2) = z ** 8 */
+  fe_sq(t1, t0);
+  fe_sq(t1, t1);
+
+  /* t1 = z * t1 = z ** 9 */
+  fe_mul(t1, z, t1);
+  /* t0 = t0 * t1 = z ** 11 -- stash t0 away for the end. */
+  fe_mul(t0, t0, t1);
+
+  /* t2 = t0 ** 2 = z ** 22 */
+  fe_sq(t2, t0);
+
+  /* t1 = t1 * t2 = z ** (2 ** 5 - 1) */
+  fe_mul(t1, t1, t2);
+
+  /* t2 = t1 ** (2 ** 5) = z ** ((2 ** 5) * (2 ** 5 - 1)) */
+  fe_sq(t2, t1);
+  for (i = 1; i < 5; ++i) {
+    fe_sq(t2, t2);
+  }
+
+  /* t1 = t1 * t2 = z ** ((2 ** 5 + 1) * (2 ** 5 - 1)) = z ** (2 ** 10 - 1) */
+  fe_mul(t1, t2, t1);
+
+  /* Continuing similarly... */
+
+  /* t2 = z ** (2 ** 20 - 1) */
+  fe_sq(t2, t1);
+  for (i = 1; i < 10; ++i) {
+    fe_sq(t2, t2);
+  }
+  fe_mul(t2, t2, t1);
+
+  /* t2 = z ** (2 ** 40 - 1) */
+  fe_sq(t3, t2);
+  for (i = 1; i < 20; ++i) {
+    fe_sq(t3, t3);
+  }
+  fe_mul(t2, t3, t2);
+
+  /* t2 = z ** (2 ** 10) * (2 ** 40 - 1) */
+  for (i = 0; i < 10; ++i) {
+    fe_sq(t2, t2);
+  }
+  /* t1 = z ** (2 ** 50 - 1) */
+  fe_mul(t1, t2, t1);
+
+  /* t2 = z ** (2 ** 100 - 1) */
+  fe_sq(t2, t1);
+  for (i = 1; i < 50; ++i) {
+    fe_sq(t2, t2);
+  }
+  fe_mul(t2, t2, t1);
+
+  /* t2 = z ** (2 ** 200 - 1) */
+  fe_sq(t3, t2);
+  for (i = 1; i < 100; ++i) {
+    fe_sq(t3, t3);
+  }
+  fe_mul(t2, t3, t2);
+
+  /* t2 = z ** ((2 ** 50) * (2 ** 200 - 1) */
+  fe_sq(t2, t2);
+  for (i = 1; i < 50; ++i) {
+    fe_sq(t2, t2);
+  }
+
+  /* t1 = z ** (2 ** 250 - 1) */
+  fe_mul(t1, t2, t1);
+
+  /* t1 = z ** ((2 ** 5) * (2 ** 250 - 1)) */
+  fe_sq(t1, t1);
+  for (i = 1; i < 5; ++i) {
+    fe_sq(t1, t1);
+  }
+
+  /* Recall t0 = z ** 11; out = z ** (2 ** 255 - 21) */
+  fe_mul(out, t1, t0);
+}
+
+/* h = -f
+ *
+ * Preconditions:
+ *    |f| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc.
+ *
+ * Postconditions:
+ *    |h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. */
+static void fe_neg(fe h, const fe f) {
+  unsigned i;
+  for (i = 0; i < 10; i++) {
+    h[i] = -f[i];
+  }
+}
+
+/* Replace (f,g) with (g,g) if b == 1;
+ * replace (f,g) with (f,g) if b == 0.
+ *
+ * Preconditions: b in {0,1}. */
+static void fe_cmov(fe f, const fe g, unsigned b) {
+  size_t i;
+  b = 0-b;
+  for (i = 0; i < 10; i++) {
+    int32_t x = f[i] ^ g[i];
+    x &= b;
+    f[i] ^= x;
+  }
+}
+
+/* h = 2 * f * f
+ * Can overlap h with f.
+ *
+ * Preconditions:
+ *    |f| bounded by 1.65*2^26,1.65*2^25,1.65*2^26,1.65*2^25,etc.
+ *
+ * Postconditions:
+ *    |h| bounded by 1.01*2^25,1.01*2^24,1.01*2^25,1.01*2^24,etc.
+ *
+ * See fe_mul.c for discussion of implementation strategy. */
+static void fe_sq2(fe h, const fe f) {
+  int32_t f0 = f[0];
+  int32_t f1 = f[1];
+  int32_t f2 = f[2];
+  int32_t f3 = f[3];
+  int32_t f4 = f[4];
+  int32_t f5 = f[5];
+  int32_t f6 = f[6];
+  int32_t f7 = f[7];
+  int32_t f8 = f[8];
+  int32_t f9 = f[9];
+  int32_t f0_2 = 2 * f0;
+  int32_t f1_2 = 2 * f1;
+  int32_t f2_2 = 2 * f2;
+  int32_t f3_2 = 2 * f3;
+  int32_t f4_2 = 2 * f4;
+  int32_t f5_2 = 2 * f5;
+  int32_t f6_2 = 2 * f6;
+  int32_t f7_2 = 2 * f7;
+  int32_t f5_38 = 38 * f5; /* 1.959375*2^30 */
+  int32_t f6_19 = 19 * f6; /* 1.959375*2^30 */
+  int32_t f7_38 = 38 * f7; /* 1.959375*2^30 */
+  int32_t f8_19 = 19 * f8; /* 1.959375*2^30 */
+  int32_t f9_38 = 38 * f9; /* 1.959375*2^30 */
+  int64_t f0f0    = f0   * (int64_t) f0;
+  int64_t f0f1_2  = f0_2 * (int64_t) f1;
+  int64_t f0f2_2  = f0_2 * (int64_t) f2;
+  int64_t f0f3_2  = f0_2 * (int64_t) f3;
+  int64_t f0f4_2  = f0_2 * (int64_t) f4;
+  int64_t f0f5_2  = f0_2 * (int64_t) f5;
+  int64_t f0f6_2  = f0_2 * (int64_t) f6;
+  int64_t f0f7_2  = f0_2 * (int64_t) f7;
+  int64_t f0f8_2  = f0_2 * (int64_t) f8;
+  int64_t f0f9_2  = f0_2 * (int64_t) f9;
+  int64_t f1f1_2  = f1_2 * (int64_t) f1;
+  int64_t f1f2_2  = f1_2 * (int64_t) f2;
+  int64_t f1f3_4  = f1_2 * (int64_t) f3_2;
+  int64_t f1f4_2  = f1_2 * (int64_t) f4;
+  int64_t f1f5_4  = f1_2 * (int64_t) f5_2;
+  int64_t f1f6_2  = f1_2 * (int64_t) f6;
+  int64_t f1f7_4  = f1_2 * (int64_t) f7_2;
+  int64_t f1f8_2  = f1_2 * (int64_t) f8;
+  int64_t f1f9_76 = f1_2 * (int64_t) f9_38;
+  int64_t f2f2    = f2   * (int64_t) f2;
+  int64_t f2f3_2  = f2_2 * (int64_t) f3;
+  int64_t f2f4_2  = f2_2 * (int64_t) f4;
+  int64_t f2f5_2  = f2_2 * (int64_t) f5;
+  int64_t f2f6_2  = f2_2 * (int64_t) f6;
+  int64_t f2f7_2  = f2_2 * (int64_t) f7;
+  int64_t f2f8_38 = f2_2 * (int64_t) f8_19;
+  int64_t f2f9_38 = f2   * (int64_t) f9_38;
+  int64_t f3f3_2  = f3_2 * (int64_t) f3;
+  int64_t f3f4_2  = f3_2 * (int64_t) f4;
+  int64_t f3f5_4  = f3_2 * (int64_t) f5_2;
+  int64_t f3f6_2  = f3_2 * (int64_t) f6;
+  int64_t f3f7_76 = f3_2 * (int64_t) f7_38;
+  int64_t f3f8_38 = f3_2 * (int64_t) f8_19;
+  int64_t f3f9_76 = f3_2 * (int64_t) f9_38;
+  int64_t f4f4    = f4   * (int64_t) f4;
+  int64_t f4f5_2  = f4_2 * (int64_t) f5;
+  int64_t f4f6_38 = f4_2 * (int64_t) f6_19;
+  int64_t f4f7_38 = f4   * (int64_t) f7_38;
+  int64_t f4f8_38 = f4_2 * (int64_t) f8_19;
+  int64_t f4f9_38 = f4   * (int64_t) f9_38;
+  int64_t f5f5_38 = f5   * (int64_t) f5_38;
+  int64_t f5f6_38 = f5_2 * (int64_t) f6_19;
+  int64_t f5f7_76 = f5_2 * (int64_t) f7_38;
+  int64_t f5f8_38 = f5_2 * (int64_t) f8_19;
+  int64_t f5f9_76 = f5_2 * (int64_t) f9_38;
+  int64_t f6f6_19 = f6   * (int64_t) f6_19;
+  int64_t f6f7_38 = f6   * (int64_t) f7_38;
+  int64_t f6f8_38 = f6_2 * (int64_t) f8_19;
+  int64_t f6f9_38 = f6   * (int64_t) f9_38;
+  int64_t f7f7_38 = f7   * (int64_t) f7_38;
+  int64_t f7f8_38 = f7_2 * (int64_t) f8_19;
+  int64_t f7f9_76 = f7_2 * (int64_t) f9_38;
+  int64_t f8f8_19 = f8   * (int64_t) f8_19;
+  int64_t f8f9_38 = f8   * (int64_t) f9_38;
+  int64_t f9f9_38 = f9   * (int64_t) f9_38;
+  int64_t h0 = f0f0  +f1f9_76+f2f8_38+f3f7_76+f4f6_38+f5f5_38;
+  int64_t h1 = f0f1_2+f2f9_38+f3f8_38+f4f7_38+f5f6_38;
+  int64_t h2 = f0f2_2+f1f1_2 +f3f9_76+f4f8_38+f5f7_76+f6f6_19;
+  int64_t h3 = f0f3_2+f1f2_2 +f4f9_38+f5f8_38+f6f7_38;
+  int64_t h4 = f0f4_2+f1f3_4 +f2f2   +f5f9_76+f6f8_38+f7f7_38;
+  int64_t h5 = f0f5_2+f1f4_2 +f2f3_2 +f6f9_38+f7f8_38;
+  int64_t h6 = f0f6_2+f1f5_4 +f2f4_2 +f3f3_2 +f7f9_76+f8f8_19;
+  int64_t h7 = f0f7_2+f1f6_2 +f2f5_2 +f3f4_2 +f8f9_38;
+  int64_t h8 = f0f8_2+f1f7_4 +f2f6_2 +f3f5_4 +f4f4   +f9f9_38;
+  int64_t h9 = f0f9_2+f1f8_2 +f2f7_2 +f3f6_2 +f4f5_2;
+  int64_t carry0;
+  int64_t carry1;
+  int64_t carry2;
+  int64_t carry3;
+  int64_t carry4;
+  int64_t carry5;
+  int64_t carry6;
+  int64_t carry7;
+  int64_t carry8;
+  int64_t carry9;
+
+  h0 += h0;
+  h1 += h1;
+  h2 += h2;
+  h3 += h3;
+  h4 += h4;
+  h5 += h5;
+  h6 += h6;
+  h7 += h7;
+  h8 += h8;
+  h9 += h9;
+
+  carry0 = h0 + (1 << 25); h1 += carry0 >> 26; h0 -= carry0 & kTop38Bits;
+  carry4 = h4 + (1 << 25); h5 += carry4 >> 26; h4 -= carry4 & kTop38Bits;
+
+  carry1 = h1 + (1 << 24); h2 += carry1 >> 25; h1 -= carry1 & kTop39Bits;
+  carry5 = h5 + (1 << 24); h6 += carry5 >> 25; h5 -= carry5 & kTop39Bits;
+
+  carry2 = h2 + (1 << 25); h3 += carry2 >> 26; h2 -= carry2 & kTop38Bits;
+  carry6 = h6 + (1 << 25); h7 += carry6 >> 26; h6 -= carry6 & kTop38Bits;
+
+  carry3 = h3 + (1 << 24); h4 += carry3 >> 25; h3 -= carry3 & kTop39Bits;
+  carry7 = h7 + (1 << 24); h8 += carry7 >> 25; h7 -= carry7 & kTop39Bits;
+
+  carry4 = h4 + (1 << 25); h5 += carry4 >> 26; h4 -= carry4 & kTop38Bits;
+  carry8 = h8 + (1 << 25); h9 += carry8 >> 26; h8 -= carry8 & kTop38Bits;
+
+  carry9 = h9 + (1 << 24); h0 += (carry9 >> 25) * 19; h9 -= carry9 & kTop39Bits;
+
+  carry0 = h0 + (1 << 25); h1 += carry0 >> 26; h0 -= carry0 & kTop38Bits;
+
+  h[0] = h0;
+  h[1] = h1;
+  h[2] = h2;
+  h[3] = h3;
+  h[4] = h4;
+  h[5] = h5;
+  h[6] = h6;
+  h[7] = h7;
+  h[8] = h8;
+  h[9] = h9;
+}
+
+/* ge means group element.
+
+ * Here the group is the set of pairs (x,y) of field elements (see fe.h)
+ * satisfying -x^2 + y^2 = 1 + d x^2y^2
+ * where d = -121665/121666.
+ *
+ * Representations:
+ *   ge_p2 (projective): (X:Y:Z) satisfying x=X/Z, y=Y/Z
+ *   ge_p3 (extended): (X:Y:Z:T) satisfying x=X/Z, y=Y/Z, XY=ZT
+ *   ge_p1p1 (completed): ((X:Z),(Y:T)) satisfying x=X/Z, y=Y/T
+ *   ge_precomp (Duif): (y+x,y-x,2dxy) */
+
+typedef struct {
+  fe X;
+  fe Y;
+  fe Z;
+} ge_p2;
+
+typedef struct {
+  fe X;
+  fe Y;
+  fe Z;
+  fe T;
+} ge_p3;
+
+typedef struct {
+  fe X;
+  fe Y;
+  fe Z;
+  fe T;
+} ge_p1p1;
+
+typedef struct {
+  fe yplusx;
+  fe yminusx;
+  fe xy2d;
+} ge_precomp;
+
+typedef struct {
+  fe YplusX;
+  fe YminusX;
+  fe Z;
+  fe T2d;
+} ge_cached;
+
+static void ge_p3_0(ge_p3 *h) {
+  fe_0(h->X);
+  fe_1(h->Y);
+  fe_1(h->Z);
+  fe_0(h->T);
+}
+
+static void ge_precomp_0(ge_precomp *h) {
+  fe_1(h->yplusx);
+  fe_1(h->yminusx);
+  fe_0(h->xy2d);
+}
+
+/* r = p */
+static void ge_p3_to_p2(ge_p2 *r, const ge_p3 *p) {
+  fe_copy(r->X, p->X);
+  fe_copy(r->Y, p->Y);
+  fe_copy(r->Z, p->Z);
+}
+
+/* r = p */
+static void ge_p1p1_to_p2(ge_p2 *r, const ge_p1p1 *p) {
+  fe_mul(r->X, p->X, p->T);
+  fe_mul(r->Y, p->Y, p->Z);
+  fe_mul(r->Z, p->Z, p->T);
+}
+
+/* r = p */
+static void ge_p1p1_to_p3(ge_p3 *r, const ge_p1p1 *p) {
+  fe_mul(r->X, p->X, p->T);
+  fe_mul(r->Y, p->Y, p->Z);
+  fe_mul(r->Z, p->Z, p->T);
+  fe_mul(r->T, p->X, p->Y);
+}
+
+/* r = 2 * p */
+static void ge_p2_dbl(ge_p1p1 *r, const ge_p2 *p) {
+  fe t0;
+
+  fe_sq(r->X, p->X);
+  fe_sq(r->Z, p->Y);
+  fe_sq2(r->T, p->Z);
+  fe_add(r->Y, p->X, p->Y);
+  fe_sq(t0, r->Y);
+  fe_add(r->Y, r->Z, r->X);
+  fe_sub(r->Z, r->Z, r->X);
+  fe_sub(r->X, t0, r->Y);
+  fe_sub(r->T, r->T, r->Z);
+}
+
+/* r = 2 * p */
+static void ge_p3_dbl(ge_p1p1 *r, const ge_p3 *p) {
+  ge_p2 q;
+  ge_p3_to_p2(&q, p);
+  ge_p2_dbl(r, &q);
+}
+
+/* r = p + q */
+static void ge_madd(ge_p1p1 *r, const ge_p3 *p, const ge_precomp *q) {
+  fe t0;
+
+  fe_add(r->X, p->Y, p->X);
+  fe_sub(r->Y, p->Y, p->X);
+  fe_mul(r->Z, r->X, q->yplusx);
+  fe_mul(r->Y, r->Y, q->yminusx);
+  fe_mul(r->T, q->xy2d, p->T);
+  fe_add(t0, p->Z, p->Z);
+  fe_sub(r->X, r->Z, r->Y);
+  fe_add(r->Y, r->Z, r->Y);
+  fe_add(r->Z, t0, r->T);
+  fe_sub(r->T, t0, r->T);
+}
+
+static uint8_t equal(signed char b, signed char c) {
+  uint8_t ub = b;
+  uint8_t uc = c;
+  uint8_t x = ub ^ uc; /* 0: yes; 1..255: no */
+  uint32_t y = x;      /* 0: yes; 1..255: no */
+  y -= 1;              /* 4294967295: yes; 0..254: no */
+  y >>= 31;            /* 1: yes; 0: no */
+  return y;
+}
+
+static void cmov(ge_precomp *t, const ge_precomp *u, uint8_t b) {
+  fe_cmov(t->yplusx, u->yplusx, b);
+  fe_cmov(t->yminusx, u->yminusx, b);
+  fe_cmov(t->xy2d, u->xy2d, b);
+}
+
+/* k25519Precomp[i][j] = (j+1)*256^i*B */
+static const ge_precomp k25519Precomp[32][8] = {
+    {
+        {
+            {25967493, -14356035, 29566456, 3660896, -12694345, 4014787,
+             27544626, -11754271, -6079156, 2047605},
+            {-12545711, 934262, -2722910, 3049990, -727428, 9406986, 12720692,
+             5043384, 19500929, -15469378},
+            {-8738181, 4489570, 9688441, -14785194, 10184609, -12363380,
+             29287919, 11864899, -24514362, -4438546},
+        },
+        {
+            {-12815894, -12976347, -21581243, 11784320, -25355658, -2750717,
+             -11717903, -3814571, -358445, -10211303},
+            {-21703237, 6903825, 27185491, 6451973, -29577724, -9554005,
+             -15616551, 11189268, -26829678, -5319081},
+            {26966642, 11152617, 32442495, 15396054, 14353839, -12752335,
+             -3128826, -9541118, -15472047, -4166697},
+        },
+        {
+            {15636291, -9688557, 24204773, -7912398, 616977, -16685262,
+             27787600, -14772189, 28944400, -1550024},
+            {16568933, 4717097, -11556148, -1102322, 15682896, -11807043,
+             16354577, -11775962, 7689662, 11199574},
+            {30464156, -5976125, -11779434, -15670865, 23220365, 15915852,
+             7512774, 10017326, -17749093, -9920357},
+        },
+        {
+            {-17036878, 13921892, 10945806, -6033431, 27105052, -16084379,
+             -28926210, 15006023, 3284568, -6276540},
+            {23599295, -8306047, -11193664, -7687416, 13236774, 10506355,
+             7464579, 9656445, 13059162, 10374397},
+            {7798556, 16710257, 3033922, 2874086, 28997861, 2835604, 32406664,
+             -3839045, -641708, -101325},
+        },
+        {
+            {10861363, 11473154, 27284546, 1981175, -30064349, 12577861,
+             32867885, 14515107, -15438304, 10819380},
+            {4708026, 6336745, 20377586, 9066809, -11272109, 6594696, -25653668,
+             12483688, -12668491, 5581306},
+            {19563160, 16186464, -29386857, 4097519, 10237984, -4348115,
+             28542350, 13850243, -23678021, -15815942},
+        },
+        {
+            {-15371964, -12862754, 32573250, 4720197, -26436522, 5875511,
+             -19188627, -15224819, -9818940, -12085777},
+            {-8549212, 109983, 15149363, 2178705, 22900618, 4543417, 3044240,
+             -15689887, 1762328, 14866737},
+            {-18199695, -15951423, -10473290, 1707278, -17185920, 3916101,
+             -28236412, 3959421, 27914454, 4383652},
+        },
+        {
+            {5153746, 9909285, 1723747, -2777874, 30523605, 5516873, 19480852,
+             5230134, -23952439, -15175766},
+            {-30269007, -3463509, 7665486, 10083793, 28475525, 1649722,
+             20654025, 16520125, 30598449, 7715701},
+            {28881845, 14381568, 9657904, 3680757, -20181635, 7843316,
+             -31400660, 1370708, 29794553, -1409300},
+        },
+        {
+            {14499471, -2729599, -33191113, -4254652, 28494862, 14271267,
+             30290735, 10876454, -33154098, 2381726},
+            {-7195431, -2655363, -14730155, 462251, -27724326, 3941372,
+             -6236617, 3696005, -32300832, 15351955},
+            {27431194, 8222322, 16448760, -3907995, -18707002, 11938355,
+             -32961401, -2970515, 29551813, 10109425},
+        },
+    },
+    {
+        {
+            {-13657040, -13155431, -31283750, 11777098, 21447386, 6519384,
+             -2378284, -1627556, 10092783, -4764171},
+            {27939166, 14210322, 4677035, 16277044, -22964462, -12398139,
+             -32508754, 12005538, -17810127, 12803510},
+            {17228999, -15661624, -1233527, 300140, -1224870, -11714777,
+             30364213, -9038194, 18016357, 4397660},
+        },
+        {
+            {-10958843, -7690207, 4776341, -14954238, 27850028, -15602212,
+             -26619106, 14544525, -17477504, 982639},
+            {29253598, 15796703, -2863982, -9908884, 10057023, 3163536, 7332899,
+             -4120128, -21047696, 9934963},
+            {5793303, 16271923, -24131614, -10116404, 29188560, 1206517,
+             -14747930, 4559895, -30123922, -10897950},
+        },
+        {
+            {-27643952, -11493006, 16282657, -11036493, 28414021, -15012264,
+             24191034, 4541697, -13338309, 5500568},
+            {12650548, -1497113, 9052871, 11355358, -17680037, -8400164,
+             -17430592, 12264343, 10874051, 13524335},
+            {25556948, -3045990, 714651, 2510400, 23394682, -10415330, 33119038,
+             5080568, -22528059, 5376628},
+        },
+        {
+            {-26088264, -4011052, -17013699, -3537628, -6726793, 1920897,
+             -22321305, -9447443, 4535768, 1569007},
+            {-2255422, 14606630, -21692440, -8039818, 28430649, 8775819,
+             -30494562, 3044290, 31848280, 12543772},
+            {-22028579, 2943893, -31857513, 6777306, 13784462, -4292203,
+             -27377195, -2062731, 7718482, 14474653},
+        },
+        {
+            {2385315, 2454213, -22631320, 46603, -4437935, -15680415, 656965,
+             -7236665, 24316168, -5253567},
+            {13741529, 10911568, -33233417, -8603737, -20177830, -1033297,
+             33040651, -13424532, -20729456, 8321686},
+            {21060490, -2212744, 15712757, -4336099, 1639040, 10656336,
+             23845965, -11874838, -9984458, 608372},
+        },
+        {
+            {-13672732, -15087586, -10889693, -7557059, -6036909, 11305547,
+             1123968, -6780577, 27229399, 23887},
+            {-23244140, -294205, -11744728, 14712571, -29465699, -2029617,
+             12797024, -6440308, -1633405, 16678954},
+            {-29500620, 4770662, -16054387, 14001338, 7830047, 9564805,
+             -1508144, -4795045, -17169265, 4904953},
+        },
+        {
+            {24059557, 14617003, 19037157, -15039908, 19766093, -14906429,
+             5169211, 16191880, 2128236, -4326833},
+            {-16981152, 4124966, -8540610, -10653797, 30336522, -14105247,
+             -29806336, 916033, -6882542, -2986532},
+            {-22630907, 12419372, -7134229, -7473371, -16478904, 16739175,
+             285431, 2763829, 15736322, 4143876},
+        },
+        {
+            {2379352, 11839345, -4110402, -5988665, 11274298, 794957, 212801,
+             -14594663, 23527084, -16458268},
+            {33431127, -11130478, -17838966, -15626900, 8909499, 8376530,
+             -32625340, 4087881, -15188911, -14416214},
+            {1767683, 7197987, -13205226, -2022635, -13091350, 448826, 5799055,
+             4357868, -4774191, -16323038},
+        },
+    },
+    {
+        {
+            {6721966, 13833823, -23523388, -1551314, 26354293, -11863321,
+             23365147, -3949732, 7390890, 2759800},
+            {4409041, 2052381, 23373853, 10530217, 7676779, -12885954, 21302353,
+             -4264057, 1244380, -12919645},
+            {-4421239, 7169619, 4982368, -2957590, 30256825, -2777540, 14086413,
+             9208236, 15886429, 16489664},
+        },
+        {
+            {1996075, 10375649, 14346367, 13311202, -6874135, -16438411,
+             -13693198, 398369, -30606455, -712933},
+            {-25307465, 9795880, -2777414, 14878809, -33531835, 14780363,
+             13348553, 12076947, -30836462, 5113182},
+            {-17770784, 11797796, 31950843, 13929123, -25888302, 12288344,
+             -30341101, -7336386, 13847711, 5387222},
+        },
+        {
+            {-18582163, -3416217, 17824843, -2340966, 22744343, -10442611,
+             8763061, 3617786, -19600662, 10370991},
+            {20246567, -14369378, 22358229, -543712, 18507283, -10413996,
+             14554437, -8746092, 32232924, 16763880},
+            {9648505, 10094563, 26416693, 14745928, -30374318, -6472621,
+             11094161, 15689506, 3140038, -16510092},
+        },
+        {
+            {-16160072, 5472695, 31895588, 4744994, 8823515, 10365685,
+             -27224800, 9448613, -28774454, 366295},
+            {19153450, 11523972, -11096490, -6503142, -24647631, 5420647,
+             28344573, 8041113, 719605, 11671788},
+            {8678025, 2694440, -6808014, 2517372, 4964326, 11152271, -15432916,
+             -15266516, 27000813, -10195553},
+        },
+        {
+            {-15157904, 7134312, 8639287, -2814877, -7235688, 10421742, 564065,
+             5336097, 6750977, -14521026},
+            {11836410, -3979488, 26297894, 16080799, 23455045, 15735944,
+             1695823, -8819122, 8169720, 16220347},
+            {-18115838, 8653647, 17578566, -6092619, -8025777, -16012763,
+             -11144307, -2627664, -5990708, -14166033},
+        },
+        {
+            {-23308498, -10968312, 15213228, -10081214, -30853605, -11050004,
+             27884329, 2847284, 2655861, 1738395},
+            {-27537433, -14253021, -25336301, -8002780, -9370762, 8129821,
+             21651608, -3239336, -19087449, -11005278},
+            {1533110, 3437855, 23735889, 459276, 29970501, 11335377, 26030092,
+             5821408, 10478196, 8544890},
+        },
+        {
+            {32173121, -16129311, 24896207, 3921497, 22579056, -3410854,
+             19270449, 12217473, 17789017, -3395995},
+            {-30552961, -2228401, -15578829, -10147201, 13243889, 517024,
+             15479401, -3853233, 30460520, 1052596},
+            {-11614875, 13323618, 32618793, 8175907, -15230173, 12596687,
+             27491595, -4612359, 3179268, -9478891},
+        },
+        {
+            {31947069, -14366651, -4640583, -15339921, -15125977, -6039709,
+             -14756777, -16411740, 19072640, -9511060},
+            {11685058, 11822410, 3158003, -13952594, 33402194, -4165066,
+             5977896, -5215017, 473099, 5040608},
+            {-20290863, 8198642, -27410132, 11602123, 1290375, -2799760,
+             28326862, 1721092, -19558642, -3131606},
+        },
+    },
+    {
+        {
+            {7881532, 10687937, 7578723, 7738378, -18951012, -2553952, 21820786,
+             8076149, -27868496, 11538389},
+            {-19935666, 3899861, 18283497, -6801568, -15728660, -11249211,
+             8754525, 7446702, -5676054, 5797016},
+            {-11295600, -3793569, -15782110, -7964573, 12708869, -8456199,
+             2014099, -9050574, -2369172, -5877341},
+        },
+        {
+            {-22472376, -11568741, -27682020, 1146375, 18956691, 16640559,
+             1192730, -3714199, 15123619, 10811505},
+            {14352098, -3419715, -18942044, 10822655, 32750596, 4699007, -70363,
+             15776356, -28886779, -11974553},
+            {-28241164, -8072475, -4978962, -5315317, 29416931, 1847569,
+             -20654173, -16484855, 4714547, -9600655},
+        },
+        {
+            {15200332, 8368572, 19679101, 15970074, -31872674, 1959451,
+             24611599, -4543832, -11745876, 12340220},
+            {12876937, -10480056, 33134381, 6590940, -6307776, 14872440,
+             9613953, 8241152, 15370987, 9608631},
+            {-4143277, -12014408, 8446281, -391603, 4407738, 13629032, -7724868,
+             15866074, -28210621, -8814099},
+        },
+        {
+            {26660628, -15677655, 8393734, 358047, -7401291, 992988, -23904233,
+             858697, 20571223, 8420556},
+            {14620715, 13067227, -15447274, 8264467, 14106269, 15080814,
+             33531827, 12516406, -21574435, -12476749},
+            {236881, 10476226, 57258, -14677024, 6472998, 2466984, 17258519,
+             7256740, 8791136, 15069930},
+        },
+        {
+            {1276410, -9371918, 22949635, -16322807, -23493039, -5702186,
+             14711875, 4874229, -30663140, -2331391},
+            {5855666, 4990204, -13711848, 7294284, -7804282, 1924647, -1423175,
+             -7912378, -33069337, 9234253},
+            {20590503, -9018988, 31529744, -7352666, -2706834, 10650548,
+             31559055, -11609587, 18979186, 13396066},
+        },
+        {
+            {24474287, 4968103, 22267082, 4407354, 24063882, -8325180,
+             -18816887, 13594782, 33514650, 7021958},
+            {-11566906, -6565505, -21365085, 15928892, -26158305, 4315421,
+             -25948728, -3916677, -21480480, 12868082},
+            {-28635013, 13504661, 19988037, -2132761, 21078225, 6443208,
+             -21446107, 2244500, -12455797, -8089383},
+        },
+        {
+            {-30595528, 13793479, -5852820, 319136, -25723172, -6263899,
+             33086546, 8957937, -15233648, 5540521},
+            {-11630176, -11503902, -8119500, -7643073, 2620056, 1022908,
+             -23710744, -1568984, -16128528, -14962807},
+            {23152971, 775386, 27395463, 14006635, -9701118, 4649512, 1689819,
+             892185, -11513277, -15205948},
+        },
+        {
+            {9770129, 9586738, 26496094, 4324120, 1556511, -3550024, 27453819,
+             4763127, -19179614, 5867134},
+            {-32765025, 1927590, 31726409, -4753295, 23962434, -16019500,
+             27846559, 5931263, -29749703, -16108455},
+            {27461885, -2977536, 22380810, 1815854, -23033753, -3031938,
+             7283490, -15148073, -19526700, 7734629},
+        },
+    },
+    {
+        {
+            {-8010264, -9590817, -11120403, 6196038, 29344158, -13430885,
+             7585295, -3176626, 18549497, 15302069},
+            {-32658337, -6171222, -7672793, -11051681, 6258878, 13504381,
+             10458790, -6418461, -8872242, 8424746},
+            {24687205, 8613276, -30667046, -3233545, 1863892, -1830544,
+             19206234, 7134917, -11284482, -828919},
+        },
+        {
+            {11334899, -9218022, 8025293, 12707519, 17523892, -10476071,
+             10243738, -14685461, -5066034, 16498837},
+            {8911542, 6887158, -9584260, -6958590, 11145641, -9543680, 17303925,
+             -14124238, 6536641, 10543906},
+            {-28946384, 15479763, -17466835, 568876, -1497683, 11223454,
+             -2669190, -16625574, -27235709, 8876771},
+        },
+        {
+            {-25742899, -12566864, -15649966, -846607, -33026686, -796288,
+             -33481822, 15824474, -604426, -9039817},
+            {10330056, 70051, 7957388, -9002667, 9764902, 15609756, 27698697,
+             -4890037, 1657394, 3084098},
+            {10477963, -7470260, 12119566, -13250805, 29016247, -5365589,
+             31280319, 14396151, -30233575, 15272409},
+        },
+        {
+            {-12288309, 3169463, 28813183, 16658753, 25116432, -5630466,
+             -25173957, -12636138, -25014757, 1950504},
+            {-26180358, 9489187, 11053416, -14746161, -31053720, 5825630,
+             -8384306, -8767532, 15341279, 8373727},
+            {28685821, 7759505, -14378516, -12002860, -31971820, 4079242,
+             298136, -10232602, -2878207, 15190420},
+        },
+        {
+            {-32932876, 13806336, -14337485, -15794431, -24004620, 10940928,
+             8669718, 2742393, -26033313, -6875003},
+            {-1580388, -11729417, -25979658, -11445023, -17411874, -10912854,
+             9291594, -16247779, -12154742, 6048605},
+            {-30305315, 14843444, 1539301, 11864366, 20201677, 1900163,
+             13934231, 5128323, 11213262, 9168384},
+        },
+        {
+            {-26280513, 11007847, 19408960, -940758, -18592965, -4328580,
+             -5088060, -11105150, 20470157, -16398701},
+            {-23136053, 9282192, 14855179, -15390078, -7362815, -14408560,
+             -22783952, 14461608, 14042978, 5230683},
+            {29969567, -2741594, -16711867, -8552442, 9175486, -2468974,
+             21556951, 3506042, -5933891, -12449708},
+        },
+        {
+            {-3144746, 8744661, 19704003, 4581278, -20430686, 6830683,
+             -21284170, 8971513, -28539189, 15326563},
+            {-19464629, 10110288, -17262528, -3503892, -23500387, 1355669,
+             -15523050, 15300988, -20514118, 9168260},
+            {-5353335, 4488613, -23803248, 16314347, 7780487, -15638939,
+             -28948358, 9601605, 33087103, -9011387},
+        },
+        {
+            {-19443170, -15512900, -20797467, -12445323, -29824447, 10229461,
+             -27444329, -15000531, -5996870, 15664672},
+            {23294591, -16632613, -22650781, -8470978, 27844204, 11461195,
+             13099750, -2460356, 18151676, 13417686},
+            {-24722913, -4176517, -31150679, 5988919, -26858785, 6685065,
+             1661597, -12551441, 15271676, -15452665},
+        },
+    },
+    {
+        {
+            {11433042, -13228665, 8239631, -5279517, -1985436, -725718,
+             -18698764, 2167544, -6921301, -13440182},
+            {-31436171, 15575146, 30436815, 12192228, -22463353, 9395379,
+             -9917708, -8638997, 12215110, 12028277},
+            {14098400, 6555944, 23007258, 5757252, -15427832, -12950502,
+             30123440, 4617780, -16900089, -655628},
+        },
+        {
+            {-4026201, -15240835, 11893168, 13718664, -14809462, 1847385,
+             -15819999, 10154009, 23973261, -12684474},
+            {-26531820, -3695990, -1908898, 2534301, -31870557, -16550355,
+             18341390, -11419951, 32013174, -10103539},
+            {-25479301, 10876443, -11771086, -14625140, -12369567, 1838104,
+             21911214, 6354752, 4425632, -837822},
+        },
+        {
+            {-10433389, -14612966, 22229858, -3091047, -13191166, 776729,
+             -17415375, -12020462, 4725005, 14044970},
+            {19268650, -7304421, 1555349, 8692754, -21474059, -9910664, 6347390,
+             -1411784, -19522291, -16109756},
+            {-24864089, 12986008, -10898878, -5558584, -11312371, -148526,
+             19541418, 8180106, 9282262, 10282508},
+        },
+        {
+            {-26205082, 4428547, -8661196, -13194263, 4098402, -14165257,
+             15522535, 8372215, 5542595, -10702683},
+            {-10562541, 14895633, 26814552, -16673850, -17480754, -2489360,
+             -2781891, 6993761, -18093885, 10114655},
+            {-20107055, -929418, 31422704, 10427861, -7110749, 6150669,
+             -29091755, -11529146, 25953725, -106158},
+        },
+        {
+            {-4234397, -8039292, -9119125, 3046000, 2101609, -12607294,
+             19390020, 6094296, -3315279, 12831125},
+            {-15998678, 7578152, 5310217, 14408357, -33548620, -224739,
+             31575954, 6326196, 7381791, -2421839},
+            {-20902779, 3296811, 24736065, -16328389, 18374254, 7318640,
+             6295303, 8082724, -15362489, 12339664},
+        },
+        {
+            {27724736, 2291157, 6088201, -14184798, 1792727, 5857634, 13848414,
+             15768922, 25091167, 14856294},
+            {-18866652, 8331043, 24373479, 8541013, -701998, -9269457, 12927300,
+             -12695493, -22182473, -9012899},
+            {-11423429, -5421590, 11632845, 3405020, 30536730, -11674039,
+             -27260765, 13866390, 30146206, 9142070},
+        },
+        {
+            {3924129, -15307516, -13817122, -10054960, 12291820, -668366,
+             -27702774, 9326384, -8237858, 4171294},
+            {-15921940, 16037937, 6713787, 16606682, -21612135, 2790944,
+             26396185, 3731949, 345228, -5462949},
+            {-21327538, 13448259, 25284571, 1143661, 20614966, -8849387,
+             2031539, -12391231, -16253183, -13582083},
+        },
+        {
+            {31016211, -16722429, 26371392, -14451233, -5027349, 14854137,
+             17477601, 3842657, 28012650, -16405420},
+            {-5075835, 9368966, -8562079, -4600902, -15249953, 6970560,
+             -9189873, 16292057, -8867157, 3507940},
+            {29439664, 3537914, 23333589, 6997794, -17555561, -11018068,
+             -15209202, -15051267, -9164929, 6580396},
+        },
+    },
+    {
+        {
+            {-12185861, -7679788, 16438269, 10826160, -8696817, -6235611,
+             17860444, -9273846, -2095802, 9304567},
+            {20714564, -4336911, 29088195, 7406487, 11426967, -5095705,
+             14792667, -14608617, 5289421, -477127},
+            {-16665533, -10650790, -6160345, -13305760, 9192020, -1802462,
+             17271490, 12349094, 26939669, -3752294},
+        },
+        {
+            {-12889898, 9373458, 31595848, 16374215, 21471720, 13221525,
+             -27283495, -12348559, -3698806, 117887},
+            {22263325, -6560050, 3984570, -11174646, -15114008, -566785,
+             28311253, 5358056, -23319780, 541964},
+            {16259219, 3261970, 2309254, -15534474, -16885711, -4581916,
+             24134070, -16705829, -13337066, -13552195},
+        },
+        {
+            {9378160, -13140186, -22845982, -12745264, 28198281, -7244098,
+             -2399684, -717351, 690426, 14876244},
+            {24977353, -314384, -8223969, -13465086, 28432343, -1176353,
+             -13068804, -12297348, -22380984, 6618999},
+            {-1538174, 11685646, 12944378, 13682314, -24389511, -14413193,
+             8044829, -13817328, 32239829, -5652762},
+        },
+        {
+            {-18603066, 4762990, -926250, 8885304, -28412480, -3187315, 9781647,
+             -10350059, 32779359, 5095274},
+            {-33008130, -5214506, -32264887, -3685216, 9460461, -9327423,
+             -24601656, 14506724, 21639561, -2630236},
+            {-16400943, -13112215, 25239338, 15531969, 3987758, -4499318,
+             -1289502, -6863535, 17874574, 558605},
+        },
+        {
+            {-13600129, 10240081, 9171883, 16131053, -20869254, 9599700,
+             33499487, 5080151, 2085892, 5119761},
+            {-22205145, -2519528, -16381601, 414691, -25019550, 2170430,
+             30634760, -8363614, -31999993, -5759884},
+            {-6845704, 15791202, 8550074, -1312654, 29928809, -12092256,
+             27534430, -7192145, -22351378, 12961482},
+        },
+        {
+            {-24492060, -9570771, 10368194, 11582341, -23397293, -2245287,
+             16533930, 8206996, -30194652, -5159638},
+            {-11121496, -3382234, 2307366, 6362031, -135455, 8868177, -16835630,
+             7031275, 7589640, 8945490},
+            {-32152748, 8917967, 6661220, -11677616, -1192060, -15793393,
+             7251489, -11182180, 24099109, -14456170},
+        },
+        {
+            {5019558, -7907470, 4244127, -14714356, -26933272, 6453165,
+             -19118182, -13289025, -6231896, -10280736},
+            {10853594, 10721687, 26480089, 5861829, -22995819, 1972175,
+             -1866647, -10557898, -3363451, -6441124},
+            {-17002408, 5906790, 221599, -6563147, 7828208, -13248918, 24362661,
+             -2008168, -13866408, 7421392},
+        },
+        {
+            {8139927, -6546497, 32257646, -5890546, 30375719, 1886181,
+             -21175108, 15441252, 28826358, -4123029},
+            {6267086, 9695052, 7709135, -16603597, -32869068, -1886135,
+             14795160, -7840124, 13746021, -1742048},
+            {28584902, 7787108, -6732942, -15050729, 22846041, -7571236,
+             -3181936, -363524, 4771362, -8419958},
+        },
+    },
+    {
+        {
+            {24949256, 6376279, -27466481, -8174608, -18646154, -9930606,
+             33543569, -12141695, 3569627, 11342593},
+            {26514989, 4740088, 27912651, 3697550, 19331575, -11472339, 6809886,
+             4608608, 7325975, -14801071},
+            {-11618399, -14554430, -24321212, 7655128, -1369274, 5214312,
+             -27400540, 10258390, -17646694, -8186692},
+        },
+        {
+            {11431204, 15823007, 26570245, 14329124, 18029990, 4796082,
+             -31446179, 15580664, 9280358, -3973687},
+            {-160783, -10326257, -22855316, -4304997, -20861367, -13621002,
+             -32810901, -11181622, -15545091, 4387441},
+            {-20799378, 12194512, 3937617, -5805892, -27154820, 9340370,
+             -24513992, 8548137, 20617071, -7482001},
+        },
+        {
+            {-938825, -3930586, -8714311, 16124718, 24603125, -6225393,
+             -13775352, -11875822, 24345683, 10325460},
+            {-19855277, -1568885, -22202708, 8714034, 14007766, 6928528,
+             16318175, -1010689, 4766743, 3552007},
+            {-21751364, -16730916, 1351763, -803421, -4009670, 3950935, 3217514,
+             14481909, 10988822, -3994762},
+        },
+        {
+            {15564307, -14311570, 3101243, 5684148, 30446780, -8051356,
+             12677127, -6505343, -8295852, 13296005},
+            {-9442290, 6624296, -30298964, -11913677, -4670981, -2057379,
+             31521204, 9614054, -30000824, 12074674},
+            {4771191, -135239, 14290749, -13089852, 27992298, 14998318,
+             -1413936, -1556716, 29832613, -16391035},
+        },
+        {
+            {7064884, -7541174, -19161962, -5067537, -18891269, -2912736,
+             25825242, 5293297, -27122660, 13101590},
+            {-2298563, 2439670, -7466610, 1719965, -27267541, -16328445,
+             32512469, -5317593, -30356070, -4190957},
+            {-30006540, 10162316, -33180176, 3981723, -16482138, -13070044,
+             14413974, 9515896, 19568978, 9628812},
+        },
+        {
+            {33053803, 199357, 15894591, 1583059, 27380243, -4580435, -17838894,
+             -6106839, -6291786, 3437740},
+            {-18978877, 3884493, 19469877, 12726490, 15913552, 13614290,
+             -22961733, 70104, 7463304, 4176122},
+            {-27124001, 10659917, 11482427, -16070381, 12771467, -6635117,
+             -32719404, -5322751, 24216882, 5944158},
+        },
+        {
+            {8894125, 7450974, -2664149, -9765752, -28080517, -12389115,
+             19345746, 14680796, 11632993, 5847885},
+            {26942781, -2315317, 9129564, -4906607, 26024105, 11769399,
+             -11518837, 6367194, -9727230, 4782140},
+            {19916461, -4828410, -22910704, -11414391, 25606324, -5972441,
+             33253853, 8220911, 6358847, -1873857},
+        },
+        {
+            {801428, -2081702, 16569428, 11065167, 29875704, 96627, 7908388,
+             -4480480, -13538503, 1387155},
+            {19646058, 5720633, -11416706, 12814209, 11607948, 12749789,
+             14147075, 15156355, -21866831, 11835260},
+            {19299512, 1155910, 28703737, 14890794, 2925026, 7269399, 26121523,
+             15467869, -26560550, 5052483},
+        },
+    },
+    {
+        {
+            {-3017432, 10058206, 1980837, 3964243, 22160966, 12322533, -6431123,
+             -12618185, 12228557, -7003677},
+            {32944382, 14922211, -22844894, 5188528, 21913450, -8719943,
+             4001465, 13238564, -6114803, 8653815},
+            {22865569, -4652735, 27603668, -12545395, 14348958, 8234005,
+             24808405, 5719875, 28483275, 2841751},
+        },
+        {
+            {-16420968, -1113305, -327719, -12107856, 21886282, -15552774,
+             -1887966, -315658, 19932058, -12739203},
+            {-11656086, 10087521, -8864888, -5536143, -19278573, -3055912,
+             3999228, 13239134, -4777469, -13910208},
+            {1382174, -11694719, 17266790, 9194690, -13324356, 9720081,
+             20403944, 11284705, -14013818, 3093230},
+        },
+        {
+            {16650921, -11037932, -1064178, 1570629, -8329746, 7352753, -302424,
+             16271225, -24049421, -6691850},
+            {-21911077, -5927941, -4611316, -5560156, -31744103, -10785293,
+             24123614, 15193618, -21652117, -16739389},
+            {-9935934, -4289447, -25279823, 4372842, 2087473, 10399484,
+             31870908, 14690798, 17361620, 11864968},
+        },
+        {
+            {-11307610, 6210372, 13206574, 5806320, -29017692, -13967200,
+             -12331205, -7486601, -25578460, -16240689},
+            {14668462, -12270235, 26039039, 15305210, 25515617, 4542480,
+             10453892, 6577524, 9145645, -6443880},
+            {5974874, 3053895, -9433049, -10385191, -31865124, 3225009,
+             -7972642, 3936128, -5652273, -3050304},
+        },
+        {
+            {30625386, -4729400, -25555961, -12792866, -20484575, 7695099,
+             17097188, -16303496, -27999779, 1803632},
+            {-3553091, 9865099, -5228566, 4272701, -5673832, -16689700,
+             14911344, 12196514, -21405489, 7047412},
+            {20093277, 9920966, -11138194, -5343857, 13161587, 12044805,
+             -32856851, 4124601, -32343828, -10257566},
+        },
+        {
+            {-20788824, 14084654, -13531713, 7842147, 19119038, -13822605,
+             4752377, -8714640, -21679658, 2288038},
+            {-26819236, -3283715, 29965059, 3039786, -14473765, 2540457,
+             29457502, 14625692, -24819617, 12570232},
+            {-1063558, -11551823, 16920318, 12494842, 1278292, -5869109,
+             -21159943, -3498680, -11974704, 4724943},
+        },
+        {
+            {17960970, -11775534, -4140968, -9702530, -8876562, -1410617,
+             -12907383, -8659932, -29576300, 1903856},
+            {23134274, -14279132, -10681997, -1611936, 20684485, 15770816,
+             -12989750, 3190296, 26955097, 14109738},
+            {15308788, 5320727, -30113809, -14318877, 22902008, 7767164,
+             29425325, -11277562, 31960942, 11934971},
+        },
+        {
+            {-27395711, 8435796, 4109644, 12222639, -24627868, 14818669,
+             20638173, 4875028, 10491392, 1379718},
+            {-13159415, 9197841, 3875503, -8936108, -1383712, -5879801,
+             33518459, 16176658, 21432314, 12180697},
+            {-11787308, 11500838, 13787581, -13832590, -22430679, 10140205,
+             1465425, 12689540, -10301319, -13872883},
+        },
+    },
+    {
+        {
+            {5414091, -15386041, -21007664, 9643570, 12834970, 1186149,
+             -2622916, -1342231, 26128231, 6032912},
+            {-26337395, -13766162, 32496025, -13653919, 17847801, -12669156,
+             3604025, 8316894, -25875034, -10437358},
+            {3296484, 6223048, 24680646, -12246460, -23052020, 5903205,
+             -8862297, -4639164, 12376617, 3188849},
+        },
+        {
+            {29190488, -14659046, 27549113, -1183516, 3520066, -10697301,
+             32049515, -7309113, -16109234, -9852307},
+            {-14744486, -9309156, 735818, -598978, -20407687, -5057904,
+             25246078, -15795669, 18640741, -960977},
+            {-6928835, -16430795, 10361374, 5642961, 4910474, 12345252,
+             -31638386, -494430, 10530747, 1053335},
+        },
+        {
+            {-29265967, -14186805, -13538216, -12117373, -19457059, -10655384,
+             -31462369, -2948985, 24018831, 15026644},
+            {-22592535, -3145277, -2289276, 5953843, -13440189, 9425631,
+             25310643, 13003497, -2314791, -15145616},
+            {-27419985, -603321, -8043984, -1669117, -26092265, 13987819,
+             -27297622, 187899, -23166419, -2531735},
+        },
+        {
+            {-21744398, -13810475, 1844840, 5021428, -10434399, -15911473,
+             9716667, 16266922, -5070217, 726099},
+            {29370922, -6053998, 7334071, -15342259, 9385287, 2247707,
+             -13661962, -4839461, 30007388, -15823341},
+            {-936379, 16086691, 23751945, -543318, -1167538, -5189036, 9137109,
+             730663, 9835848, 4555336},
+        },
+        {
+            {-23376435, 1410446, -22253753, -12899614, 30867635, 15826977,
+             17693930, 544696, -11985298, 12422646},
+            {31117226, -12215734, -13502838, 6561947, -9876867, -12757670,
+             -5118685, -4096706, 29120153, 13924425},
+            {-17400879, -14233209, 19675799, -2734756, -11006962, -5858820,
+             -9383939, -11317700, 7240931, -237388},
+        },
+        {
+            {-31361739, -11346780, -15007447, -5856218, -22453340, -12152771,
+             1222336, 4389483, 3293637, -15551743},
+            {-16684801, -14444245, 11038544, 11054958, -13801175, -3338533,
+             -24319580, 7733547, 12796905, -6335822},
+            {-8759414, -10817836, -25418864, 10783769, -30615557, -9746811,
+             -28253339, 3647836, 3222231, -11160462},
+        },
+        {
+            {18606113, 1693100, -25448386, -15170272, 4112353, 10045021,
+             23603893, -2048234, -7550776, 2484985},
+            {9255317, -3131197, -12156162, -1004256, 13098013, -9214866,
+             16377220, -2102812, -19802075, -3034702},
+            {-22729289, 7496160, -5742199, 11329249, 19991973, -3347502,
+             -31718148, 9936966, -30097688, -10618797},
+        },
+        {
+            {21878590, -5001297, 4338336, 13643897, -3036865, 13160960,
+             19708896, 5415497, -7360503, -4109293},
+            {27736861, 10103576, 12500508, 8502413, -3413016, -9633558,
+             10436918, -1550276, -23659143, -8132100},
+            {19492550, -12104365, -29681976, -852630, -3208171, 12403437,
+             30066266, 8367329, 13243957, 8709688},
+        },
+    },
+    {
+        {
+            {12015105, 2801261, 28198131, 10151021, 24818120, -4743133,
+             -11194191, -5645734, 5150968, 7274186},
+            {2831366, -12492146, 1478975, 6122054, 23825128, -12733586,
+             31097299, 6083058, 31021603, -9793610},
+            {-2529932, -2229646, 445613, 10720828, -13849527, -11505937,
+             -23507731, 16354465, 15067285, -14147707},
+        },
+        {
+            {7840942, 14037873, -33364863, 15934016, -728213, -3642706,
+             21403988, 1057586, -19379462, -12403220},
+            {915865, -16469274, 15608285, -8789130, -24357026, 6060030,
+             -17371319, 8410997, -7220461, 16527025},
+            {32922597, -556987, 20336074, -16184568, 10903705, -5384487,
+             16957574, 52992, 23834301, 6588044},
+        },
+        {
+            {32752030, 11232950, 3381995, -8714866, 22652988, -10744103,
+             17159699, 16689107, -20314580, -1305992},
+            {-4689649, 9166776, -25710296, -10847306, 11576752, 12733943,
+             7924251, -2752281, 1976123, -7249027},
+            {21251222, 16309901, -2983015, -6783122, 30810597, 12967303, 156041,
+             -3371252, 12331345, -8237197},
+        },
+        {
+            {8651614, -4477032, -16085636, -4996994, 13002507, 2950805,
+             29054427, -5106970, 10008136, -4667901},
+            {31486080, 15114593, -14261250, 12951354, 14369431, -7387845,
+             16347321, -13662089, 8684155, -10532952},
+            {19443825, 11385320, 24468943, -9659068, -23919258, 2187569,
+             -26263207, -6086921, 31316348, 14219878},
+        },
+        {
+            {-28594490, 1193785, 32245219, 11392485, 31092169, 15722801,
+             27146014, 6992409, 29126555, 9207390},
+            {32382935, 1110093, 18477781, 11028262, -27411763, -7548111,
+             -4980517, 10843782, -7957600, -14435730},
+            {2814918, 7836403, 27519878, -7868156, -20894015, -11553689,
+             -21494559, 8550130, 28346258, 1994730},
+        },
+        {
+            {-19578299, 8085545, -14000519, -3948622, 2785838, -16231307,
+             -19516951, 7174894, 22628102, 8115180},
+            {-30405132, 955511, -11133838, -15078069, -32447087, -13278079,
+             -25651578, 3317160, -9943017, 930272},
+            {-15303681, -6833769, 28856490, 1357446, 23421993, 1057177,
+             24091212, -1388970, -22765376, -10650715},
+        },
+        {
+            {-22751231, -5303997, -12907607, -12768866, -15811511, -7797053,
+             -14839018, -16554220, -1867018, 8398970},
+            {-31969310, 2106403, -4736360, 1362501, 12813763, 16200670,
+             22981545, -6291273, 18009408, -15772772},
+            {-17220923, -9545221, -27784654, 14166835, 29815394, 7444469,
+             29551787, -3727419, 19288549, 1325865},
+        },
+        {
+            {15100157, -15835752, -23923978, -1005098, -26450192, 15509408,
+             12376730, -3479146, 33166107, -8042750},
+            {20909231, 13023121, -9209752, 16251778, -5778415, -8094914,
+             12412151, 10018715, 2213263, -13878373},
+            {32529814, -11074689, 30361439, -16689753, -9135940, 1513226,
+             22922121, 6382134, -5766928, 8371348},
+        },
+    },
+    {
+        {
+            {9923462, 11271500, 12616794, 3544722, -29998368, -1721626,
+             12891687, -8193132, -26442943, 10486144},
+            {-22597207, -7012665, 8587003, -8257861, 4084309, -12970062, 361726,
+             2610596, -23921530, -11455195},
+            {5408411, -1136691, -4969122, 10561668, 24145918, 14240566,
+             31319731, -4235541, 19985175, -3436086},
+        },
+        {
+            {-13994457, 16616821, 14549246, 3341099, 32155958, 13648976,
+             -17577068, 8849297, 65030, 8370684},
+            {-8320926, -12049626, 31204563, 5839400, -20627288, -1057277,
+             -19442942, 6922164, 12743482, -9800518},
+            {-2361371, 12678785, 28815050, 4759974, -23893047, 4884717,
+             23783145, 11038569, 18800704, 255233},
+        },
+        {
+            {-5269658, -1773886, 13957886, 7990715, 23132995, 728773, 13393847,
+             9066957, 19258688, -14753793},
+            {-2936654, -10827535, -10432089, 14516793, -3640786, 4372541,
+             -31934921, 2209390, -1524053, 2055794},
+            {580882, 16705327, 5468415, -2683018, -30926419, -14696000,
+             -7203346, -8994389, -30021019, 7394435},
+        },
+        {
+            {23838809, 1822728, -15738443, 15242727, 8318092, -3733104,
+             -21672180, -3492205, -4821741, 14799921},
+            {13345610, 9759151, 3371034, -16137791, 16353039, 8577942, 31129804,
+             13496856, -9056018, 7402518},
+            {2286874, -4435931, -20042458, -2008336, -13696227, 5038122,
+             11006906, -15760352, 8205061, 1607563},
+        },
+        {
+            {14414086, -8002132, 3331830, -3208217, 22249151, -5594188,
+             18364661, -2906958, 30019587, -9029278},
+            {-27688051, 1585953, -10775053, 931069, -29120221, -11002319,
+             -14410829, 12029093, 9944378, 8024},
+            {4368715, -3709630, 29874200, -15022983, -20230386, -11410704,
+             -16114594, -999085, -8142388, 5640030},
+        },
+        {
+            {10299610, 13746483, 11661824, 16234854, 7630238, 5998374, 9809887,
+             -16694564, 15219798, -14327783},
+            {27425505, -5719081, 3055006, 10660664, 23458024, 595578, -15398605,
+             -1173195, -18342183, 9742717},
+            {6744077, 2427284, 26042789, 2720740, -847906, 1118974, 32324614,
+             7406442, 12420155, 1994844},
+        },
+        {
+            {14012521, -5024720, -18384453, -9578469, -26485342, -3936439,
+             -13033478, -10909803, 24319929, -6446333},
+            {16412690, -4507367, 10772641, 15929391, -17068788, -4658621,
+             10555945, -10484049, -30102368, -4739048},
+            {22397382, -7767684, -9293161, -12792868, 17166287, -9755136,
+             -27333065, 6199366, 21880021, -12250760},
+        },
+        {
+            {-4283307, 5368523, -31117018, 8163389, -30323063, 3209128,
+             16557151, 8890729, 8840445, 4957760},
+            {-15447727, 709327, -6919446, -10870178, -29777922, 6522332,
+             -21720181, 12130072, -14796503, 5005757},
+            {-2114751, -14308128, 23019042, 15765735, -25269683, 6002752,
+             10183197, -13239326, -16395286, -2176112},
+        },
+    },
+    {
+        {
+            {-19025756, 1632005, 13466291, -7995100, -23640451, 16573537,
+             -32013908, -3057104, 22208662, 2000468},
+            {3065073, -1412761, -25598674, -361432, -17683065, -5703415,
+             -8164212, 11248527, -3691214, -7414184},
+            {10379208, -6045554, 8877319, 1473647, -29291284, -12507580,
+             16690915, 2553332, -3132688, 16400289},
+        },
+        {
+            {15716668, 1254266, -18472690, 7446274, -8448918, 6344164,
+             -22097271, -7285580, 26894937, 9132066},
+            {24158887, 12938817, 11085297, -8177598, -28063478, -4457083,
+             -30576463, 64452, -6817084, -2692882},
+            {13488534, 7794716, 22236231, 5989356, 25426474, -12578208, 2350710,
+             -3418511, -4688006, 2364226},
+        },
+        {
+            {16335052, 9132434, 25640582, 6678888, 1725628, 8517937, -11807024,
+             -11697457, 15445875, -7798101},
+            {29004207, -7867081, 28661402, -640412, -12794003, -7943086,
+             31863255, -4135540, -278050, -15759279},
+            {-6122061, -14866665, -28614905, 14569919, -10857999, -3591829,
+             10343412, -6976290, -29828287, -10815811},
+        },
+        {
+            {27081650, 3463984, 14099042, -4517604, 1616303, -6205604, 29542636,
+             15372179, 17293797, 960709},
+            {20263915, 11434237, -5765435, 11236810, 13505955, -10857102,
+             -16111345, 6493122, -19384511, 7639714},
+            {-2830798, -14839232, 25403038, -8215196, -8317012, -16173699,
+             18006287, -16043750, 29994677, -15808121},
+        },
+        {
+            {9769828, 5202651, -24157398, -13631392, -28051003, -11561624,
+             -24613141, -13860782, -31184575, 709464},
+            {12286395, 13076066, -21775189, -1176622, -25003198, 4057652,
+             -32018128, -8890874, 16102007, 13205847},
+            {13733362, 5599946, 10557076, 3195751, -5557991, 8536970, -25540170,
+             8525972, 10151379, 10394400},
+        },
+        {
+            {4024660, -16137551, 22436262, 12276534, -9099015, -2686099,
+             19698229, 11743039, -33302334, 8934414},
+            {-15879800, -4525240, -8580747, -2934061, 14634845, -698278,
+             -9449077, 3137094, -11536886, 11721158},
+            {17555939, -5013938, 8268606, 2331751, -22738815, 9761013, 9319229,
+             8835153, -9205489, -1280045},
+        },
+        {
+            {-461409, -7830014, 20614118, 16688288, -7514766, -4807119,
+             22300304, 505429, 6108462, -6183415},
+            {-5070281, 12367917, -30663534, 3234473, 32617080, -8422642,
+             29880583, -13483331, -26898490, -7867459},
+            {-31975283, 5726539, 26934134, 10237677, -3173717, -605053,
+             24199304, 3795095, 7592688, -14992079},
+        },
+        {
+            {21594432, -14964228, 17466408, -4077222, 32537084, 2739898,
+             6407723, 12018833, -28256052, 4298412},
+            {-20650503, -11961496, -27236275, 570498, 3767144, -1717540,
+             13891942, -1569194, 13717174, 10805743},
+            {-14676630, -15644296, 15287174, 11927123, 24177847, -8175568,
+             -796431, 14860609, -26938930, -5863836},
+        },
+    },
+    {
+        {
+            {12962541, 5311799, -10060768, 11658280, 18855286, -7954201,
+             13286263, -12808704, -4381056, 9882022},
+            {18512079, 11319350, -20123124, 15090309, 18818594, 5271736,
+             -22727904, 3666879, -23967430, -3299429},
+            {-6789020, -3146043, 16192429, 13241070, 15898607, -14206114,
+             -10084880, -6661110, -2403099, 5276065},
+        },
+        {
+            {30169808, -5317648, 26306206, -11750859, 27814964, 7069267,
+             7152851, 3684982, 1449224, 13082861},
+            {10342826, 3098505, 2119311, 193222, 25702612, 12233820, 23697382,
+             15056736, -21016438, -8202000},
+            {-33150110, 3261608, 22745853, 7948688, 19370557, -15177665,
+             -26171976, 6482814, -10300080, -11060101},
+        },
+        {
+            {32869458, -5408545, 25609743, 15678670, -10687769, -15471071,
+             26112421, 2521008, -22664288, 6904815},
+            {29506923, 4457497, 3377935, -9796444, -30510046, 12935080, 1561737,
+             3841096, -29003639, -6657642},
+            {10340844, -6630377, -18656632, -2278430, 12621151, -13339055,
+             30878497, -11824370, -25584551, 5181966},
+        },
+        {
+            {25940115, -12658025, 17324188, -10307374, -8671468, 15029094,
+             24396252, -16450922, -2322852, -12388574},
+            {-21765684, 9916823, -1300409, 4079498, -1028346, 11909559, 1782390,
+             12641087, 20603771, -6561742},
+            {-18882287, -11673380, 24849422, 11501709, 13161720, -4768874,
+             1925523, 11914390, 4662781, 7820689},
+        },
+        {
+            {12241050, -425982, 8132691, 9393934, 32846760, -1599620, 29749456,
+             12172924, 16136752, 15264020},
+            {-10349955, -14680563, -8211979, 2330220, -17662549, -14545780,
+             10658213, 6671822, 19012087, 3772772},
+            {3753511, -3421066, 10617074, 2028709, 14841030, -6721664, 28718732,
+             -15762884, 20527771, 12988982},
+        },
+        {
+            {-14822485, -5797269, -3707987, 12689773, -898983, -10914866,
+             -24183046, -10564943, 3299665, -12424953},
+            {-16777703, -15253301, -9642417, 4978983, 3308785, 8755439, 6943197,
+             6461331, -25583147, 8991218},
+            {-17226263, 1816362, -1673288, -6086439, 31783888, -8175991,
+             -32948145, 7417950, -30242287, 1507265},
+        },
+        {
+            {29692663, 6829891, -10498800, 4334896, 20945975, -11906496,
+             -28887608, 8209391, 14606362, -10647073},
+            {-3481570, 8707081, 32188102, 5672294, 22096700, 1711240, -33020695,
+             9761487, 4170404, -2085325},
+            {-11587470, 14855945, -4127778, -1531857, -26649089, 15084046,
+             22186522, 16002000, -14276837, -8400798},
+        },
+        {
+            {-4811456, 13761029, -31703877, -2483919, -3312471, 7869047,
+             -7113572, -9620092, 13240845, 10965870},
+            {-7742563, -8256762, -14768334, -13656260, -23232383, 12387166,
+             4498947, 14147411, 29514390, 4302863},
+            {-13413405, -12407859, 20757302, -13801832, 14785143, 8976368,
+             -5061276, -2144373, 17846988, -13971927},
+        },
+    },
+    {
+        {
+            {-2244452, -754728, -4597030, -1066309, -6247172, 1455299,
+             -21647728, -9214789, -5222701, 12650267},
+            {-9906797, -16070310, 21134160, 12198166, -27064575, 708126, 387813,
+             13770293, -19134326, 10958663},
+            {22470984, 12369526, 23446014, -5441109, -21520802, -9698723,
+             -11772496, -11574455, -25083830, 4271862},
+        },
+        {
+            {-25169565, -10053642, -19909332, 15361595, -5984358, 2159192,
+             75375, -4278529, -32526221, 8469673},
+            {15854970, 4148314, -8893890, 7259002, 11666551, 13824734,
+             -30531198, 2697372, 24154791, -9460943},
+            {15446137, -15806644, 29759747, 14019369, 30811221, -9610191,
+             -31582008, 12840104, 24913809, 9815020},
+        },
+        {
+            {-4709286, -5614269, -31841498, -12288893, -14443537, 10799414,
+             -9103676, 13438769, 18735128, 9466238},
+            {11933045, 9281483, 5081055, -5183824, -2628162, -4905629, -7727821,
+             -10896103, -22728655, 16199064},
+            {14576810, 379472, -26786533, -8317236, -29426508, -10812974,
+             -102766, 1876699, 30801119, 2164795},
+        },
+        {
+            {15995086, 3199873, 13672555, 13712240, -19378835, -4647646,
+             -13081610, -15496269, -13492807, 1268052},
+            {-10290614, -3659039, -3286592, 10948818, 23037027, 3794475,
+             -3470338, -12600221, -17055369, 3565904},
+            {29210088, -9419337, -5919792, -4952785, 10834811, -13327726,
+             -16512102, -10820713, -27162222, -14030531},
+        },
+        {
+            {-13161890, 15508588, 16663704, -8156150, -28349942, 9019123,
+             -29183421, -3769423, 2244111, -14001979},
+            {-5152875, -3800936, -9306475, -6071583, 16243069, 14684434,
+             -25673088, -16180800, 13491506, 4641841},
+            {10813417, 643330, -19188515, -728916, 30292062, -16600078,
+             27548447, -7721242, 14476989, -12767431},
+        },
+        {
+            {10292079, 9984945, 6481436, 8279905, -7251514, 7032743, 27282937,
+             -1644259, -27912810, 12651324},
+            {-31185513, -813383, 22271204, 11835308, 10201545, 15351028,
+             17099662, 3988035, 21721536, -3148940},
+            {10202177, -6545839, -31373232, -9574638, -32150642, -8119683,
+             -12906320, 3852694, 13216206, 14842320},
+        },
+        {
+            {-15815640, -10601066, -6538952, -7258995, -6984659, -6581778,
+             -31500847, 13765824, -27434397, 9900184},
+            {14465505, -13833331, -32133984, -14738873, -27443187, 12990492,
+             33046193, 15796406, -7051866, -8040114},
+            {30924417, -8279620, 6359016, -12816335, 16508377, 9071735,
+             -25488601, 15413635, 9524356, -7018878},
+        },
+        {
+            {12274201, -13175547, 32627641, -1785326, 6736625, 13267305,
+             5237659, -5109483, 15663516, 4035784},
+            {-2951309, 8903985, 17349946, 601635, -16432815, -4612556,
+             -13732739, -15889334, -22258478, 4659091},
+            {-16916263, -4952973, -30393711, -15158821, 20774812, 15897498,
+             5736189, 15026997, -2178256, -13455585},
+        },
+    },
+    {
+        {
+            {-8858980, -2219056, 28571666, -10155518, -474467, -10105698,
+             -3801496, 278095, 23440562, -290208},
+            {10226241, -5928702, 15139956, 120818, -14867693, 5218603, 32937275,
+             11551483, -16571960, -7442864},
+            {17932739, -12437276, -24039557, 10749060, 11316803, 7535897,
+             22503767, 5561594, -3646624, 3898661},
+        },
+        {
+            {7749907, -969567, -16339731, -16464, -25018111, 15122143, -1573531,
+             7152530, 21831162, 1245233},
+            {26958459, -14658026, 4314586, 8346991, -5677764, 11960072,
+             -32589295, -620035, -30402091, -16716212},
+            {-12165896, 9166947, 33491384, 13673479, 29787085, 13096535,
+             6280834, 14587357, -22338025, 13987525},
+        },
+        {
+            {-24349909, 7778775, 21116000, 15572597, -4833266, -5357778,
+             -4300898, -5124639, -7469781, -2858068},
+            {9681908, -6737123, -31951644, 13591838, -6883821, 386950, 31622781,
+             6439245, -14581012, 4091397},
+            {-8426427, 1470727, -28109679, -1596990, 3978627, -5123623,
+             -19622683, 12092163, 29077877, -14741988},
+        },
+        {
+            {5269168, -6859726, -13230211, -8020715, 25932563, 1763552,
+             -5606110, -5505881, -20017847, 2357889},
+            {32264008, -15407652, -5387735, -1160093, -2091322, -3946900,
+             23104804, -12869908, 5727338, 189038},
+            {14609123, -8954470, -6000566, -16622781, -14577387, -7743898,
+             -26745169, 10942115, -25888931, -14884697},
+        },
+        {
+            {20513500, 5557931, -15604613, 7829531, 26413943, -2019404,
+             -21378968, 7471781, 13913677, -5137875},
+            {-25574376, 11967826, 29233242, 12948236, -6754465, 4713227,
+             -8940970, 14059180, 12878652, 8511905},
+            {-25656801, 3393631, -2955415, -7075526, -2250709, 9366908,
+             -30223418, 6812974, 5568676, -3127656},
+        },
+        {
+            {11630004, 12144454, 2116339, 13606037, 27378885, 15676917,
+             -17408753, -13504373, -14395196, 8070818},
+            {27117696, -10007378, -31282771, -5570088, 1127282, 12772488,
+             -29845906, 10483306, -11552749, -1028714},
+            {10637467, -5688064, 5674781, 1072708, -26343588, -6982302,
+             -1683975, 9177853, -27493162, 15431203},
+        },
+        {
+            {20525145, 10892566, -12742472, 12779443, -29493034, 16150075,
+             -28240519, 14943142, -15056790, -7935931},
+            {-30024462, 5626926, -551567, -9981087, 753598, 11981191, 25244767,
+             -3239766, -3356550, 9594024},
+            {-23752644, 2636870, -5163910, -10103818, 585134, 7877383, 11345683,
+             -6492290, 13352335, -10977084},
+        },
+        {
+            {-1931799, -5407458, 3304649, -12884869, 17015806, -4877091,
+             -29783850, -7752482, -13215537, -319204},
+            {20239939, 6607058, 6203985, 3483793, -18386976, -779229, -20723742,
+             15077870, -22750759, 14523817},
+            {27406042, -6041657, 27423596, -4497394, 4996214, 10002360,
+             -28842031, -4545494, -30172742, -4805667},
+        },
+    },
+    {
+        {
+            {11374242, 12660715, 17861383, -12540833, 10935568, 1099227,
+             -13886076, -9091740, -27727044, 11358504},
+            {-12730809, 10311867, 1510375, 10778093, -2119455, -9145702,
+             32676003, 11149336, -26123651, 4985768},
+            {-19096303, 341147, -6197485, -239033, 15756973, -8796662, -983043,
+             13794114, -19414307, -15621255},
+        },
+        {
+            {6490081, 11940286, 25495923, -7726360, 8668373, -8751316, 3367603,
+             6970005, -1691065, -9004790},
+            {1656497, 13457317, 15370807, 6364910, 13605745, 8362338, -19174622,
+             -5475723, -16796596, -5031438},
+            {-22273315, -13524424, -64685, -4334223, -18605636, -10921968,
+             -20571065, -7007978, -99853, -10237333},
+        },
+        {
+            {17747465, 10039260, 19368299, -4050591, -20630635, -16041286,
+             31992683, -15857976, -29260363, -5511971},
+            {31932027, -4986141, -19612382, 16366580, 22023614, 88450, 11371999,
+             -3744247, 4882242, -10626905},
+            {29796507, 37186, 19818052, 10115756, -11829032, 3352736, 18551198,
+             3272828, -5190932, -4162409},
+        },
+        {
+            {12501286, 4044383, -8612957, -13392385, -32430052, 5136599,
+             -19230378, -3529697, 330070, -3659409},
+            {6384877, 2899513, 17807477, 7663917, -2358888, 12363165, 25366522,
+             -8573892, -271295, 12071499},
+            {-8365515, -4042521, 25133448, -4517355, -6211027, 2265927,
+             -32769618, 1936675, -5159697, 3829363},
+        },
+        {
+            {28425966, -5835433, -577090, -4697198, -14217555, 6870930, 7921550,
+             -6567787, 26333140, 14267664},
+            {-11067219, 11871231, 27385719, -10559544, -4585914, -11189312,
+             10004786, -8709488, -21761224, 8930324},
+            {-21197785, -16396035, 25654216, -1725397, 12282012, 11008919,
+             1541940, 4757911, -26491501, -16408940},
+        },
+        {
+            {13537262, -7759490, -20604840, 10961927, -5922820, -13218065,
+             -13156584, 6217254, -15943699, 13814990},
+            {-17422573, 15157790, 18705543, 29619, 24409717, -260476, 27361681,
+             9257833, -1956526, -1776914},
+            {-25045300, -10191966, 15366585, 15166509, -13105086, 8423556,
+             -29171540, 12361135, -18685978, 4578290},
+        },
+        {
+            {24579768, 3711570, 1342322, -11180126, -27005135, 14124956,
+             -22544529, 14074919, 21964432, 8235257},
+            {-6528613, -2411497, 9442966, -5925588, 12025640, -1487420,
+             -2981514, -1669206, 13006806, 2355433},
+            {-16304899, -13605259, -6632427, -5142349, 16974359, -10911083,
+             27202044, 1719366, 1141648, -12796236},
+        },
+        {
+            {-12863944, -13219986, -8318266, -11018091, -6810145, -4843894,
+             13475066, -3133972, 32674895, 13715045},
+            {11423335, -5468059, 32344216, 8962751, 24989809, 9241752,
+             -13265253, 16086212, -28740881, -15642093},
+            {-1409668, 12530728, -6368726, 10847387, 19531186, -14132160,
+             -11709148, 7791794, -27245943, 4383347},
+        },
+    },
+    {
+        {
+            {-28970898, 5271447, -1266009, -9736989, -12455236, 16732599,
+             -4862407, -4906449, 27193557, 6245191},
+            {-15193956, 5362278, -1783893, 2695834, 4960227, 12840725, 23061898,
+             3260492, 22510453, 8577507},
+            {-12632451, 11257346, -32692994, 13548177, -721004, 10879011,
+             31168030, 13952092, -29571492, -3635906},
+        },
+        {
+            {3877321, -9572739, 32416692, 5405324, -11004407, -13656635,
+             3759769, 11935320, 5611860, 8164018},
+            {-16275802, 14667797, 15906460, 12155291, -22111149, -9039718,
+             32003002, -8832289, 5773085, -8422109},
+            {-23788118, -8254300, 1950875, 8937633, 18686727, 16459170, -905725,
+             12376320, 31632953, 190926},
+        },
+        {
+            {-24593607, -16138885, -8423991, 13378746, 14162407, 6901328,
+             -8288749, 4508564, -25341555, -3627528},
+            {8884438, -5884009, 6023974, 10104341, -6881569, -4941533, 18722941,
+             -14786005, -1672488, 827625},
+            {-32720583, -16289296, -32503547, 7101210, 13354605, 2659080,
+             -1800575, -14108036, -24878478, 1541286},
+        },
+        {
+            {2901347, -1117687, 3880376, -10059388, -17620940, -3612781,
+             -21802117, -3567481, 20456845, -1885033},
+            {27019610, 12299467, -13658288, -1603234, -12861660, -4861471,
+             -19540150, -5016058, 29439641, 15138866},
+            {21536104, -6626420, -32447818, -10690208, -22408077, 5175814,
+             -5420040, -16361163, 7779328, 109896},
+        },
+        {
+            {30279744, 14648750, -8044871, 6425558, 13639621, -743509, 28698390,
+             12180118, 23177719, -554075},
+            {26572847, 3405927, -31701700, 12890905, -19265668, 5335866,
+             -6493768, 2378492, 4439158, -13279347},
+            {-22716706, 3489070, -9225266, -332753, 18875722, -1140095,
+             14819434, -12731527, -17717757, -5461437},
+        },
+        {
+            {-5056483, 16566551, 15953661, 3767752, -10436499, 15627060,
+             -820954, 2177225, 8550082, -15114165},
+            {-18473302, 16596775, -381660, 15663611, 22860960, 15585581,
+             -27844109, -3582739, -23260460, -8428588},
+            {-32480551, 15707275, -8205912, -5652081, 29464558, 2713815,
+             -22725137, 15860482, -21902570, 1494193},
+        },
+        {
+            {-19562091, -14087393, -25583872, -9299552, 13127842, 759709,
+             21923482, 16529112, 8742704, 12967017},
+            {-28464899, 1553205, 32536856, -10473729, -24691605, -406174,
+             -8914625, -2933896, -29903758, 15553883},
+            {21877909, 3230008, 9881174, 10539357, -4797115, 2841332, 11543572,
+             14513274, 19375923, -12647961},
+        },
+        {
+            {8832269, -14495485, 13253511, 5137575, 5037871, 4078777, 24880818,
+             -6222716, 2862653, 9455043},
+            {29306751, 5123106, 20245049, -14149889, 9592566, 8447059, -2077124,
+             -2990080, 15511449, 4789663},
+            {-20679756, 7004547, 8824831, -9434977, -4045704, -3750736,
+             -5754762, 108893, 23513200, 16652362},
+        },
+    },
+    {
+        {
+            {-33256173, 4144782, -4476029, -6579123, 10770039, -7155542,
+             -6650416, -12936300, -18319198, 10212860},
+            {2756081, 8598110, 7383731, -6859892, 22312759, -1105012, 21179801,
+             2600940, -9988298, -12506466},
+            {-24645692, 13317462, -30449259, -15653928, 21365574, -10869657,
+             11344424, 864440, -2499677, -16710063},
+        },
+        {
+            {-26432803, 6148329, -17184412, -14474154, 18782929, -275997,
+             -22561534, 211300, 2719757, 4940997},
+            {-1323882, 3911313, -6948744, 14759765, -30027150, 7851207,
+             21690126, 8518463, 26699843, 5276295},
+            {-13149873, -6429067, 9396249, 365013, 24703301, -10488939, 1321586,
+             149635, -15452774, 7159369},
+        },
+        {
+            {9987780, -3404759, 17507962, 9505530, 9731535, -2165514, 22356009,
+             8312176, 22477218, -8403385},
+            {18155857, -16504990, 19744716, 9006923, 15154154, -10538976,
+             24256460, -4864995, -22548173, 9334109},
+            {2986088, -4911893, 10776628, -3473844, 10620590, -7083203,
+             -21413845, 14253545, -22587149, 536906},
+        },
+        {
+            {4377756, 8115836, 24567078, 15495314, 11625074, 13064599, 7390551,
+             10589625, 10838060, -15420424},
+            {-19342404, 867880, 9277171, -3218459, -14431572, -1986443,
+             19295826, -15796950, 6378260, 699185},
+            {7895026, 4057113, -7081772, -13077756, -17886831, -323126, -716039,
+             15693155, -5045064, -13373962},
+        },
+        {
+            {-7737563, -5869402, -14566319, -7406919, 11385654, 13201616,
+             31730678, -10962840, -3918636, -9669325},
+            {10188286, -15770834, -7336361, 13427543, 22223443, 14896287,
+             30743455, 7116568, -21786507, 5427593},
+            {696102, 13206899, 27047647, -10632082, 15285305, -9853179,
+             10798490, -4578720, 19236243, 12477404},
+        },
+        {
+            {-11229439, 11243796, -17054270, -8040865, -788228, -8167967,
+             -3897669, 11180504, -23169516, 7733644},
+            {17800790, -14036179, -27000429, -11766671, 23887827, 3149671,
+             23466177, -10538171, 10322027, 15313801},
+            {26246234, 11968874, 32263343, -5468728, 6830755, -13323031,
+             -15794704, -101982, -24449242, 10890804},
+        },
+        {
+            {-31365647, 10271363, -12660625, -6267268, 16690207, -13062544,
+             -14982212, 16484931, 25180797, -5334884},
+            {-586574, 10376444, -32586414, -11286356, 19801893, 10997610,
+             2276632, 9482883, 316878, 13820577},
+            {-9882808, -4510367, -2115506, 16457136, -11100081, 11674996,
+             30756178, -7515054, 30696930, -3712849},
+        },
+        {
+            {32988917, -9603412, 12499366, 7910787, -10617257, -11931514,
+             -7342816, -9985397, -32349517, 7392473},
+            {-8855661, 15927861, 9866406, -3649411, -2396914, -16655781,
+             -30409476, -9134995, 25112947, -2926644},
+            {-2504044, -436966, 25621774, -5678772, 15085042, -5479877,
+             -24884878, -13526194, 5537438, -13914319},
+        },
+    },
+    {
+        {
+            {-11225584, 2320285, -9584280, 10149187, -33444663, 5808648,
+             -14876251, -1729667, 31234590, 6090599},
+            {-9633316, 116426, 26083934, 2897444, -6364437, -2688086, 609721,
+             15878753, -6970405, -9034768},
+            {-27757857, 247744, -15194774, -9002551, 23288161, -10011936,
+             -23869595, 6503646, 20650474, 1804084},
+        },
+        {
+            {-27589786, 15456424, 8972517, 8469608, 15640622, 4439847, 3121995,
+             -10329713, 27842616, -202328},
+            {-15306973, 2839644, 22530074, 10026331, 4602058, 5048462, 28248656,
+             5031932, -11375082, 12714369},
+            {20807691, -7270825, 29286141, 11421711, -27876523, -13868230,
+             -21227475, 1035546, -19733229, 12796920},
+        },
+        {
+            {12076899, -14301286, -8785001, -11848922, -25012791, 16400684,
+             -17591495, -12899438, 3480665, -15182815},
+            {-32361549, 5457597, 28548107, 7833186, 7303070, -11953545,
+             -24363064, -15921875, -33374054, 2771025},
+            {-21389266, 421932, 26597266, 6860826, 22486084, -6737172,
+             -17137485, -4210226, -24552282, 15673397},
+        },
+        {
+            {-20184622, 2338216, 19788685, -9620956, -4001265, -8740893,
+             -20271184, 4733254, 3727144, -12934448},
+            {6120119, 814863, -11794402, -622716, 6812205, -15747771, 2019594,
+             7975683, 31123697, -10958981},
+            {30069250, -11435332, 30434654, 2958439, 18399564, -976289,
+             12296869, 9204260, -16432438, 9648165},
+        },
+        {
+            {32705432, -1550977, 30705658, 7451065, -11805606, 9631813, 3305266,
+             5248604, -26008332, -11377501},
+            {17219865, 2375039, -31570947, -5575615, -19459679, 9219903, 294711,
+             15298639, 2662509, -16297073},
+            {-1172927, -7558695, -4366770, -4287744, -21346413, -8434326,
+             32087529, -1222777, 32247248, -14389861},
+        },
+        {
+            {14312628, 1221556, 17395390, -8700143, -4945741, -8684635,
+             -28197744, -9637817, -16027623, -13378845},
+            {-1428825, -9678990, -9235681, 6549687, -7383069, -468664, 23046502,
+             9803137, 17597934, 2346211},
+            {18510800, 15337574, 26171504, 981392, -22241552, 7827556,
+             -23491134, -11323352, 3059833, -11782870},
+        },
+        {
+            {10141598, 6082907, 17829293, -1947643, 9830092, 13613136,
+             -25556636, -5544586, -33502212, 3592096},
+            {33114168, -15889352, -26525686, -13343397, 33076705, 8716171,
+             1151462, 1521897, -982665, -6837803},
+            {-32939165, -4255815, 23947181, -324178, -33072974, -12305637,
+             -16637686, 3891704, 26353178, 693168},
+        },
+        {
+            {30374239, 1595580, -16884039, 13186931, 4600344, 406904, 9585294,
+             -400668, 31375464, 14369965},
+            {-14370654, -7772529, 1510301, 6434173, -18784789, -6262728,
+             32732230, -13108839, 17901441, 16011505},
+            {18171223, -11934626, -12500402, 15197122, -11038147, -15230035,
+             -19172240, -16046376, 8764035, 12309598},
+        },
+    },
+    {
+        {
+            {5975908, -5243188, -19459362, -9681747, -11541277, 14015782,
+             -23665757, 1228319, 17544096, -10593782},
+            {5811932, -1715293, 3442887, -2269310, -18367348, -8359541,
+             -18044043, -15410127, -5565381, 12348900},
+            {-31399660, 11407555, 25755363, 6891399, -3256938, 14872274,
+             -24849353, 8141295, -10632534, -585479},
+        },
+        {
+            {-12675304, 694026, -5076145, 13300344, 14015258, -14451394,
+             -9698672, -11329050, 30944593, 1130208},
+            {8247766, -6710942, -26562381, -7709309, -14401939, -14648910,
+             4652152, 2488540, 23550156, -271232},
+            {17294316, -3788438, 7026748, 15626851, 22990044, 113481, 2267737,
+             -5908146, -408818, -137719},
+        },
+        {
+            {16091085, -16253926, 18599252, 7340678, 2137637, -1221657,
+             -3364161, 14550936, 3260525, -7166271},
+            {-4910104, -13332887, 18550887, 10864893, -16459325, -7291596,
+             -23028869, -13204905, -12748722, 2701326},
+            {-8574695, 16099415, 4629974, -16340524, -20786213, -6005432,
+             -10018363, 9276971, 11329923, 1862132},
+        },
+        {
+            {14763076, -15903608, -30918270, 3689867, 3511892, 10313526,
+             -21951088, 12219231, -9037963, -940300},
+            {8894987, -3446094, 6150753, 3013931, 301220, 15693451, -31981216,
+             -2909717, -15438168, 11595570},
+            {15214962, 3537601, -26238722, -14058872, 4418657, -15230761,
+             13947276, 10730794, -13489462, -4363670},
+        },
+        {
+            {-2538306, 7682793, 32759013, 263109, -29984731, -7955452,
+             -22332124, -10188635, 977108, 699994},
+            {-12466472, 4195084, -9211532, 550904, -15565337, 12917920,
+             19118110, -439841, -30534533, -14337913},
+            {31788461, -14507657, 4799989, 7372237, 8808585, -14747943, 9408237,
+             -10051775, 12493932, -5409317},
+        },
+        {
+            {-25680606, 5260744, -19235809, -6284470, -3695942, 16566087,
+             27218280, 2607121, 29375955, 6024730},
+            {842132, -2794693, -4763381, -8722815, 26332018, -12405641,
+             11831880, 6985184, -9940361, 2854096},
+            {-4847262, -7969331, 2516242, -5847713, 9695691, -7221186, 16512645,
+             960770, 12121869, 16648078},
+        },
+        {
+            {-15218652, 14667096, -13336229, 2013717, 30598287, -464137,
+             -31504922, -7882064, 20237806, 2838411},
+            {-19288047, 4453152, 15298546, -16178388, 22115043, -15972604,
+             12544294, -13470457, 1068881, -12499905},
+            {-9558883, -16518835, 33238498, 13506958, 30505848, -1114596,
+             -8486907, -2630053, 12521378, 4845654},
+        },
+        {
+            {-28198521, 10744108, -2958380, 10199664, 7759311, -13088600,
+             3409348, -873400, -6482306, -12885870},
+            {-23561822, 6230156, -20382013, 10655314, -24040585, -11621172,
+             10477734, -1240216, -3113227, 13974498},
+            {12966261, 15550616, -32038948, -1615346, 21025980, -629444,
+             5642325, 7188737, 18895762, 12629579},
+        },
+    },
+    {
+        {
+            {14741879, -14946887, 22177208, -11721237, 1279741, 8058600,
+             11758140, 789443, 32195181, 3895677},
+            {10758205, 15755439, -4509950, 9243698, -4879422, 6879879, -2204575,
+             -3566119, -8982069, 4429647},
+            {-2453894, 15725973, -20436342, -10410672, -5803908, -11040220,
+             -7135870, -11642895, 18047436, -15281743},
+        },
+        {
+            {-25173001, -11307165, 29759956, 11776784, -22262383, -15820455,
+             10993114, -12850837, -17620701, -9408468},
+            {21987233, 700364, -24505048, 14972008, -7774265, -5718395,
+             32155026, 2581431, -29958985, 8773375},
+            {-25568350, 454463, -13211935, 16126715, 25240068, 8594567,
+             20656846, 12017935, -7874389, -13920155},
+        },
+        {
+            {6028182, 6263078, -31011806, -11301710, -818919, 2461772,
+             -31841174, -5468042, -1721788, -2776725},
+            {-12278994, 16624277, 987579, -5922598, 32908203, 1248608, 7719845,
+             -4166698, 28408820, 6816612},
+            {-10358094, -8237829, 19549651, -12169222, 22082623, 16147817,
+             20613181, 13982702, -10339570, 5067943},
+        },
+        {
+            {-30505967, -3821767, 12074681, 13582412, -19877972, 2443951,
+             -19719286, 12746132, 5331210, -10105944},
+            {30528811, 3601899, -1957090, 4619785, -27361822, -15436388,
+             24180793, -12570394, 27679908, -1648928},
+            {9402404, -13957065, 32834043, 10838634, -26580150, -13237195,
+             26653274, -8685565, 22611444, -12715406},
+        },
+        {
+            {22190590, 1118029, 22736441, 15130463, -30460692, -5991321,
+             19189625, -4648942, 4854859, 6622139},
+            {-8310738, -2953450, -8262579, -3388049, -10401731, -271929,
+             13424426, -3567227, 26404409, 13001963},
+            {-31241838, -15415700, -2994250, 8939346, 11562230, -12840670,
+             -26064365, -11621720, -15405155, 11020693},
+        },
+        {
+            {1866042, -7949489, -7898649, -10301010, 12483315, 13477547,
+             3175636, -12424163, 28761762, 1406734},
+            {-448555, -1777666, 13018551, 3194501, -9580420, -11161737,
+             24760585, -4347088, 25577411, -13378680},
+            {-24290378, 4759345, -690653, -1852816, 2066747, 10693769,
+             -29595790, 9884936, -9368926, 4745410},
+        },
+        {
+            {-9141284, 6049714, -19531061, -4341411, -31260798, 9944276,
+             -15462008, -11311852, 10931924, -11931931},
+            {-16561513, 14112680, -8012645, 4817318, -8040464, -11414606,
+             -22853429, 10856641, -20470770, 13434654},
+            {22759489, -10073434, -16766264, -1871422, 13637442, -10168091,
+             1765144, -12654326, 28445307, -5364710},
+        },
+        {
+            {29875063, 12493613, 2795536, -3786330, 1710620, 15181182,
+             -10195717, -8788675, 9074234, 1167180},
+            {-26205683, 11014233, -9842651, -2635485, -26908120, 7532294,
+             -18716888, -9535498, 3843903, 9367684},
+            {-10969595, -6403711, 9591134, 9582310, 11349256, 108879, 16235123,
+             8601684, -139197, 4242895},
+        },
+    },
+    {
+        {
+            {22092954, -13191123, -2042793, -11968512, 32186753, -11517388,
+             -6574341, 2470660, -27417366, 16625501},
+            {-11057722, 3042016, 13770083, -9257922, 584236, -544855, -7770857,
+             2602725, -27351616, 14247413},
+            {6314175, -10264892, -32772502, 15957557, -10157730, 168750,
+             -8618807, 14290061, 27108877, -1180880},
+        },
+        {
+            {-8586597, -7170966, 13241782, 10960156, -32991015, -13794596,
+             33547976, -11058889, -27148451, 981874},
+            {22833440, 9293594, -32649448, -13618667, -9136966, 14756819,
+             -22928859, -13970780, -10479804, -16197962},
+            {-7768587, 3326786, -28111797, 10783824, 19178761, 14905060,
+             22680049, 13906969, -15933690, 3797899},
+        },
+        {
+            {21721356, -4212746, -12206123, 9310182, -3882239, -13653110,
+             23740224, -2709232, 20491983, -8042152},
+            {9209270, -15135055, -13256557, -6167798, -731016, 15289673,
+             25947805, 15286587, 30997318, -6703063},
+            {7392032, 16618386, 23946583, -8039892, -13265164, -1533858,
+             -14197445, -2321576, 17649998, -250080},
+        },
+        {
+            {-9301088, -14193827, 30609526, -3049543, -25175069, -1283752,
+             -15241566, -9525724, -2233253, 7662146},
+            {-17558673, 1763594, -33114336, 15908610, -30040870, -12174295,
+             7335080, -8472199, -3174674, 3440183},
+            {-19889700, -5977008, -24111293, -9688870, 10799743, -16571957,
+             40450, -4431835, 4862400, 1133},
+        },
+        {
+            {-32856209, -7873957, -5422389, 14860950, -16319031, 7956142,
+             7258061, 311861, -30594991, -7379421},
+            {-3773428, -1565936, 28985340, 7499440, 24445838, 9325937, 29727763,
+             16527196, 18278453, 15405622},
+            {-4381906, 8508652, -19898366, -3674424, -5984453, 15149970,
+             -13313598, 843523, -21875062, 13626197},
+        },
+        {
+            {2281448, -13487055, -10915418, -2609910, 1879358, 16164207,
+             -10783882, 3953792, 13340839, 15928663},
+            {31727126, -7179855, -18437503, -8283652, 2875793, -16390330,
+             -25269894, -7014826, -23452306, 5964753},
+            {4100420, -5959452, -17179337, 6017714, -18705837, 12227141,
+             -26684835, 11344144, 2538215, -7570755},
+        },
+        {
+            {-9433605, 6123113, 11159803, -2156608, 30016280, 14966241,
+             -20474983, 1485421, -629256, -15958862},
+            {-26804558, 4260919, 11851389, 9658551, -32017107, 16367492,
+             -20205425, -13191288, 11659922, -11115118},
+            {26180396, 10015009, -30844224, -8581293, 5418197, 9480663, 2231568,
+             -10170080, 33100372, -1306171},
+        },
+        {
+            {15121113, -5201871, -10389905, 15427821, -27509937, -15992507,
+             21670947, 4486675, -5931810, -14466380},
+            {16166486, -9483733, -11104130, 6023908, -31926798, -1364923,
+             2340060, -16254968, -10735770, -10039824},
+            {28042865, -3557089, -12126526, 12259706, -3717498, -6945899,
+             6766453, -8689599, 18036436, 5803270},
+        },
+    },
+    {
+        {
+            {-817581, 6763912, 11803561, 1585585, 10958447, -2671165, 23855391,
+             4598332, -6159431, -14117438},
+            {-31031306, -14256194, 17332029, -2383520, 31312682, -5967183,
+             696309, 50292, -20095739, 11763584},
+            {-594563, -2514283, -32234153, 12643980, 12650761, 14811489, 665117,
+             -12613632, -19773211, -10713562},
+        },
+        {
+            {30464590, -11262872, -4127476, -12734478, 19835327, -7105613,
+             -24396175, 2075773, -17020157, 992471},
+            {18357185, -6994433, 7766382, 16342475, -29324918, 411174, 14578841,
+             8080033, -11574335, -10601610},
+            {19598397, 10334610, 12555054, 2555664, 18821899, -10339780,
+             21873263, 16014234, 26224780, 16452269},
+        },
+        {
+            {-30223925, 5145196, 5944548, 16385966, 3976735, 2009897, -11377804,
+             -7618186, -20533829, 3698650},
+            {14187449, 3448569, -10636236, -10810935, -22663880, -3433596,
+             7268410, -10890444, 27394301, 12015369},
+            {19695761, 16087646, 28032085, 12999827, 6817792, 11427614,
+             20244189, -1312777, -13259127, -3402461},
+        },
+        {
+            {30860103, 12735208, -1888245, -4699734, -16974906, 2256940,
+             -8166013, 12298312, -8550524, -10393462},
+            {-5719826, -11245325, -1910649, 15569035, 26642876, -7587760,
+             -5789354, -15118654, -4976164, 12651793},
+            {-2848395, 9953421, 11531313, -5282879, 26895123, -12697089,
+             -13118820, -16517902, 9768698, -2533218},
+        },
+        {
+            {-24719459, 1894651, -287698, -4704085, 15348719, -8156530,
+             32767513, 12765450, 4940095, 10678226},
+            {18860224, 15980149, -18987240, -1562570, -26233012, -11071856,
+             -7843882, 13944024, -24372348, 16582019},
+            {-15504260, 4970268, -29893044, 4175593, -20993212, -2199756,
+             -11704054, 15444560, -11003761, 7989037},
+        },
+        {
+            {31490452, 5568061, -2412803, 2182383, -32336847, 4531686,
+             -32078269, 6200206, -19686113, -14800171},
+            {-17308668, -15879940, -31522777, -2831, -32887382, 16375549,
+             8680158, -16371713, 28550068, -6857132},
+            {-28126887, -5688091, 16837845, -1820458, -6850681, 12700016,
+             -30039981, 4364038, 1155602, 5988841},
+        },
+        {
+            {21890435, -13272907, -12624011, 12154349, -7831873, 15300496,
+             23148983, -4470481, 24618407, 8283181},
+            {-33136107, -10512751, 9975416, 6841041, -31559793, 16356536,
+             3070187, -7025928, 1466169, 10740210},
+            {-1509399, -15488185, -13503385, -10655916, 32799044, 909394,
+             -13938903, -5779719, -32164649, -15327040},
+        },
+        {
+            {3960823, -14267803, -28026090, -15918051, -19404858, 13146868,
+             15567327, 951507, -3260321, -573935},
+            {24740841, 5052253, -30094131, 8961361, 25877428, 6165135,
+             -24368180, 14397372, -7380369, -6144105},
+            {-28888365, 3510803, -28103278, -1158478, -11238128, -10631454,
+             -15441463, -14453128, -1625486, -6494814},
+        },
+    },
+    {
+        {
+            {793299, -9230478, 8836302, -6235707, -27360908, -2369593, 33152843,
+             -4885251, -9906200, -621852},
+            {5666233, 525582, 20782575, -8038419, -24538499, 14657740, 16099374,
+             1468826, -6171428, -15186581},
+            {-4859255, -3779343, -2917758, -6748019, 7778750, 11688288,
+             -30404353, -9871238, -1558923, -9863646},
+        },
+        {
+            {10896332, -7719704, 824275, 472601, -19460308, 3009587, 25248958,
+             14783338, -30581476, -15757844},
+            {10566929, 12612572, -31944212, 11118703, -12633376, 12362879,
+             21752402, 8822496, 24003793, 14264025},
+            {27713862, -7355973, -11008240, 9227530, 27050101, 2504721,
+             23886875, -13117525, 13958495, -5732453},
+        },
+        {
+            {-23481610, 4867226, -27247128, 3900521, 29838369, -8212291,
+             -31889399, -10041781, 7340521, -15410068},
+            {4646514, -8011124, -22766023, -11532654, 23184553, 8566613,
+             31366726, -1381061, -15066784, -10375192},
+            {-17270517, 12723032, -16993061, 14878794, 21619651, -6197576,
+             27584817, 3093888, -8843694, 3849921},
+        },
+        {
+            {-9064912, 2103172, 25561640, -15125738, -5239824, 9582958,
+             32477045, -9017955, 5002294, -15550259},
+            {-12057553, -11177906, 21115585, -13365155, 8808712, -12030708,
+             16489530, 13378448, -25845716, 12741426},
+            {-5946367, 10645103, -30911586, 15390284, -3286982, -7118677,
+             24306472, 15852464, 28834118, -7646072},
+        },
+        {
+            {-17335748, -9107057, -24531279, 9434953, -8472084, -583362,
+             -13090771, 455841, 20461858, 5491305},
+            {13669248, -16095482, -12481974, -10203039, -14569770, -11893198,
+             -24995986, 11293807, -28588204, -9421832},
+            {28497928, 6272777, -33022994, 14470570, 8906179, -1225630,
+             18504674, -14165166, 29867745, -8795943},
+        },
+        {
+            {-16207023, 13517196, -27799630, -13697798, 24009064, -6373891,
+             -6367600, -13175392, 22853429, -4012011},
+            {24191378, 16712145, -13931797, 15217831, 14542237, 1646131,
+             18603514, -11037887, 12876623, -2112447},
+            {17902668, 4518229, -411702, -2829247, 26878217, 5258055, -12860753,
+             608397, 16031844, 3723494},
+        },
+        {
+            {-28632773, 12763728, -20446446, 7577504, 33001348, -13017745,
+             17558842, -7872890, 23896954, -4314245},
+            {-20005381, -12011952, 31520464, 605201, 2543521, 5991821, -2945064,
+             7229064, -9919646, -8826859},
+            {28816045, 298879, -28165016, -15920938, 19000928, -1665890,
+             -12680833, -2949325, -18051778, -2082915},
+        },
+        {
+            {16000882, -344896, 3493092, -11447198, -29504595, -13159789,
+             12577740, 16041268, -19715240, 7847707},
+            {10151868, 10572098, 27312476, 7922682, 14825339, 4723128,
+             -32855931, -6519018, -10020567, 3852848},
+            {-11430470, 15697596, -21121557, -4420647, 5386314, 15063598,
+             16514493, -15932110, 29330899, -15076224},
+        },
+    },
+    {
+        {
+            {-25499735, -4378794, -15222908, -6901211, 16615731, 2051784,
+             3303702, 15490, -27548796, 12314391},
+            {15683520, -6003043, 18109120, -9980648, 15337968, -5997823,
+             -16717435, 15921866, 16103996, -3731215},
+            {-23169824, -10781249, 13588192, -1628807, -3798557, -1074929,
+             -19273607, 5402699, -29815713, -9841101},
+        },
+        {
+            {23190676, 2384583, -32714340, 3462154, -29903655, -1529132,
+             -11266856, 8911517, -25205859, 2739713},
+            {21374101, -3554250, -33524649, 9874411, 15377179, 11831242,
+             -33529904, 6134907, 4931255, 11987849},
+            {-7732, -2978858, -16223486, 7277597, 105524, -322051, -31480539,
+             13861388, -30076310, 10117930},
+        },
+        {
+            {-29501170, -10744872, -26163768, 13051539, -25625564, 5089643,
+             -6325503, 6704079, 12890019, 15728940},
+            {-21972360, -11771379, -951059, -4418840, 14704840, 2695116, 903376,
+             -10428139, 12885167, 8311031},
+            {-17516482, 5352194, 10384213, -13811658, 7506451, 13453191,
+             26423267, 4384730, 1888765, -5435404},
+        },
+        {
+            {-25817338, -3107312, -13494599, -3182506, 30896459, -13921729,
+             -32251644, -12707869, -19464434, -3340243},
+            {-23607977, -2665774, -526091, 4651136, 5765089, 4618330, 6092245,
+             14845197, 17151279, -9854116},
+            {-24830458, -12733720, -15165978, 10367250, -29530908, -265356,
+             22825805, -7087279, -16866484, 16176525},
+        },
+        {
+            {-23583256, 6564961, 20063689, 3798228, -4740178, 7359225, 2006182,
+             -10363426, -28746253, -10197509},
+            {-10626600, -4486402, -13320562, -5125317, 3432136, -6393229,
+             23632037, -1940610, 32808310, 1099883},
+            {15030977, 5768825, -27451236, -2887299, -6427378, -15361371,
+             -15277896, -6809350, 2051441, -15225865},
+        },
+        {
+            {-3362323, -7239372, 7517890, 9824992, 23555850, 295369, 5148398,
+             -14154188, -22686354, 16633660},
+            {4577086, -16752288, 13249841, -15304328, 19958763, -14537274,
+             18559670, -10759549, 8402478, -9864273},
+            {-28406330, -1051581, -26790155, -907698, -17212414, -11030789,
+             9453451, -14980072, 17983010, 9967138},
+        },
+        {
+            {-25762494, 6524722, 26585488, 9969270, 24709298, 1220360, -1677990,
+             7806337, 17507396, 3651560},
+            {-10420457, -4118111, 14584639, 15971087, -15768321, 8861010,
+             26556809, -5574557, -18553322, -11357135},
+            {2839101, 14284142, 4029895, 3472686, 14402957, 12689363, -26642121,
+             8459447, -5605463, -7621941},
+        },
+        {
+            {-4839289, -3535444, 9744961, 2871048, 25113978, 3187018, -25110813,
+             -849066, 17258084, -7977739},
+            {18164541, -10595176, -17154882, -1542417, 19237078, -9745295,
+             23357533, -15217008, 26908270, 12150756},
+            {-30264870, -7647865, 5112249, -7036672, -1499807, -6974257, 43168,
+             -5537701, -32302074, 16215819},
+        },
+    },
+    {
+        {
+            {-6898905, 9824394, -12304779, -4401089, -31397141, -6276835,
+             32574489, 12532905, -7503072, -8675347},
+            {-27343522, -16515468, -27151524, -10722951, 946346, 16291093,
+             254968, 7168080, 21676107, -1943028},
+            {21260961, -8424752, -16831886, -11920822, -23677961, 3968121,
+             -3651949, -6215466, -3556191, -7913075},
+        },
+        {
+            {16544754, 13250366, -16804428, 15546242, -4583003, 12757258,
+             -2462308, -8680336, -18907032, -9662799},
+            {-2415239, -15577728, 18312303, 4964443, -15272530, -12653564,
+             26820651, 16690659, 25459437, -4564609},
+            {-25144690, 11425020, 28423002, -11020557, -6144921, -15826224,
+             9142795, -2391602, -6432418, -1644817},
+        },
+        {
+            {-23104652, 6253476, 16964147, -3768872, -25113972, -12296437,
+             -27457225, -16344658, 6335692, 7249989},
+            {-30333227, 13979675, 7503222, -12368314, -11956721, -4621693,
+             -30272269, 2682242, 25993170, -12478523},
+            {4364628, 5930691, 32304656, -10044554, -8054781, 15091131,
+             22857016, -10598955, 31820368, 15075278},
+        },
+        {
+            {31879134, -8918693, 17258761, 90626, -8041836, -4917709, 24162788,
+             -9650886, -17970238, 12833045},
+            {19073683, 14851414, -24403169, -11860168, 7625278, 11091125,
+             -19619190, 2074449, -9413939, 14905377},
+            {24483667, -11935567, -2518866, -11547418, -1553130, 15355506,
+             -25282080, 9253129, 27628530, -7555480},
+        },
+        {
+            {17597607, 8340603, 19355617, 552187, 26198470, -3176583, 4593324,
+             -9157582, -14110875, 15297016},
+            {510886, 14337390, -31785257, 16638632, 6328095, 2713355, -20217417,
+             -11864220, 8683221, 2921426},
+            {18606791, 11874196, 27155355, -5281482, -24031742, 6265446,
+             -25178240, -1278924, 4674690, 13890525},
+        },
+        {
+            {13609624, 13069022, -27372361, -13055908, 24360586, 9592974,
+             14977157, 9835105, 4389687, 288396},
+            {9922506, -519394, 13613107, 5883594, -18758345, -434263, -12304062,
+             8317628, 23388070, 16052080},
+            {12720016, 11937594, -31970060, -5028689, 26900120, 8561328,
+             -20155687, -11632979, -14754271, -10812892},
+        },
+        {
+            {15961858, 14150409, 26716931, -665832, -22794328, 13603569,
+             11829573, 7467844, -28822128, 929275},
+            {11038231, -11582396, -27310482, -7316562, -10498527, -16307831,
+             -23479533, -9371869, -21393143, 2465074},
+            {20017163, -4323226, 27915242, 1529148, 12396362, 15675764,
+             13817261, -9658066, 2463391, -4622140},
+        },
+        {
+            {-16358878, -12663911, -12065183, 4996454, -1256422, 1073572,
+             9583558, 12851107, 4003896, 12673717},
+            {-1731589, -15155870, -3262930, 16143082, 19294135, 13385325,
+             14741514, -9103726, 7903886, 2348101},
+            {24536016, -16515207, 12715592, -3862155, 1511293, 10047386,
+             -3842346, -7129159, -28377538, 10048127},
+        },
+    },
+    {
+        {
+            {-12622226, -6204820, 30718825, 2591312, -10617028, 12192840,
+             18873298, -7297090, -32297756, 15221632},
+            {-26478122, -11103864, 11546244, -1852483, 9180880, 7656409,
+             -21343950, 2095755, 29769758, 6593415},
+            {-31994208, -2907461, 4176912, 3264766, 12538965, -868111, 26312345,
+             -6118678, 30958054, 8292160},
+        },
+        {
+            {31429822, -13959116, 29173532, 15632448, 12174511, -2760094,
+             32808831, 3977186, 26143136, -3148876},
+            {22648901, 1402143, -22799984, 13746059, 7936347, 365344, -8668633,
+             -1674433, -3758243, -2304625},
+            {-15491917, 8012313, -2514730, -12702462, -23965846, -10254029,
+             -1612713, -1535569, -16664475, 8194478},
+        },
+        {
+            {27338066, -7507420, -7414224, 10140405, -19026427, -6589889,
+             27277191, 8855376, 28572286, 3005164},
+            {26287124, 4821776, 25476601, -4145903, -3764513, -15788984,
+             -18008582, 1182479, -26094821, -13079595},
+            {-7171154, 3178080, 23970071, 6201893, -17195577, -4489192,
+             -21876275, -13982627, 32208683, -1198248},
+        },
+        {
+            {-16657702, 2817643, -10286362, 14811298, 6024667, 13349505,
+             -27315504, -10497842, -27672585, -11539858},
+            {15941029, -9405932, -21367050, 8062055, 31876073, -238629,
+             -15278393, -1444429, 15397331, -4130193},
+            {8934485, -13485467, -23286397, -13423241, -32446090, 14047986,
+             31170398, -1441021, -27505566, 15087184},
+        },
+        {
+            {-18357243, -2156491, 24524913, -16677868, 15520427, -6360776,
+             -15502406, 11461896, 16788528, -5868942},
+            {-1947386, 16013773, 21750665, 3714552, -17401782, -16055433,
+             -3770287, -10323320, 31322514, -11615635},
+            {21426655, -5650218, -13648287, -5347537, -28812189, -4920970,
+             -18275391, -14621414, 13040862, -12112948},
+        },
+        {
+            {11293895, 12478086, -27136401, 15083750, -29307421, 14748872,
+             14555558, -13417103, 1613711, 4896935},
+            {-25894883, 15323294, -8489791, -8057900, 25967126, -13425460,
+             2825960, -4897045, -23971776, -11267415},
+            {-15924766, -5229880, -17443532, 6410664, 3622847, 10243618,
+             20615400, 12405433, -23753030, -8436416},
+        },
+        {
+            {-7091295, 12556208, -20191352, 9025187, -17072479, 4333801,
+             4378436, 2432030, 23097949, -566018},
+            {4565804, -16025654, 20084412, -7842817, 1724999, 189254, 24767264,
+             10103221, -18512313, 2424778},
+            {366633, -11976806, 8173090, -6890119, 30788634, 5745705, -7168678,
+             1344109, -3642553, 12412659},
+        },
+        {
+            {-24001791, 7690286, 14929416, -168257, -32210835, -13412986,
+             24162697, -15326504, -3141501, 11179385},
+            {18289522, -14724954, 8056945, 16430056, -21729724, 7842514,
+             -6001441, -1486897, -18684645, -11443503},
+            {476239, 6601091, -6152790, -9723375, 17503545, -4863900, 27672959,
+             13403813, 11052904, 5219329},
+        },
+    },
+    {
+        {
+            {20678546, -8375738, -32671898, 8849123, -5009758, 14574752,
+             31186971, -3973730, 9014762, -8579056},
+            {-13644050, -10350239, -15962508, 5075808, -1514661, -11534600,
+             -33102500, 9160280, 8473550, -3256838},
+            {24900749, 14435722, 17209120, -15292541, -22592275, 9878983,
+             -7689309, -16335821, -24568481, 11788948},
+        },
+        {
+            {-3118155, -11395194, -13802089, 14797441, 9652448, -6845904,
+             -20037437, 10410733, -24568470, -1458691},
+            {-15659161, 16736706, -22467150, 10215878, -9097177, 7563911,
+             11871841, -12505194, -18513325, 8464118},
+            {-23400612, 8348507, -14585951, -861714, -3950205, -6373419,
+             14325289, 8628612, 33313881, -8370517},
+        },
+        {
+            {-20186973, -4967935, 22367356, 5271547, -1097117, -4788838,
+             -24805667, -10236854, -8940735, -5818269},
+            {-6948785, -1795212, -32625683, -16021179, 32635414, -7374245,
+             15989197, -12838188, 28358192, -4253904},
+            {-23561781, -2799059, -32351682, -1661963, -9147719, 10429267,
+             -16637684, 4072016, -5351664, 5596589},
+        },
+        {
+            {-28236598, -3390048, 12312896, 6213178, 3117142, 16078565,
+             29266239, 2557221, 1768301, 15373193},
+            {-7243358, -3246960, -4593467, -7553353, -127927, -912245, -1090902,
+             -4504991, -24660491, 3442910},
+            {-30210571, 5124043, 14181784, 8197961, 18964734, -11939093,
+             22597931, 7176455, -18585478, 13365930},
+        },
+        {
+            {-7877390, -1499958, 8324673, 4690079, 6261860, 890446, 24538107,
+             -8570186, -9689599, -3031667},
+            {25008904, -10771599, -4305031, -9638010, 16265036, 15721635,
+             683793, -11823784, 15723479, -15163481},
+            {-9660625, 12374379, -27006999, -7026148, -7724114, -12314514,
+             11879682, 5400171, 519526, -1235876},
+        },
+        {
+            {22258397, -16332233, -7869817, 14613016, -22520255, -2950923,
+             -20353881, 7315967, 16648397, 7605640},
+            {-8081308, -8464597, -8223311, 9719710, 19259459, -15348212,
+             23994942, -5281555, -9468848, 4763278},
+            {-21699244, 9220969, -15730624, 1084137, -25476107, -2852390,
+             31088447, -7764523, -11356529, 728112},
+        },
+        {
+            {26047220, -11751471, -6900323, -16521798, 24092068, 9158119,
+             -4273545, -12555558, -29365436, -5498272},
+            {17510331, -322857, 5854289, 8403524, 17133918, -3112612, -28111007,
+             12327945, 10750447, 10014012},
+            {-10312768, 3936952, 9156313, -8897683, 16498692, -994647,
+             -27481051, -666732, 3424691, 7540221},
+        },
+        {
+            {30322361, -6964110, 11361005, -4143317, 7433304, 4989748, -7071422,
+             -16317219, -9244265, 15258046},
+            {13054562, -2779497, 19155474, 469045, -12482797, 4566042, 5631406,
+             2711395, 1062915, -5136345},
+            {-19240248, -11254599, -29509029, -7499965, -5835763, 13005411,
+             -6066489, 12194497, 32960380, 1459310},
+        },
+    },
+    {
+        {
+            {19852034, 7027924, 23669353, 10020366, 8586503, -6657907, 394197,
+             -6101885, 18638003, -11174937},
+            {31395534, 15098109, 26581030, 8030562, -16527914, -5007134,
+             9012486, -7584354, -6643087, -5442636},
+            {-9192165, -2347377, -1997099, 4529534, 25766844, 607986, -13222,
+             9677543, -32294889, -6456008},
+        },
+        {
+            {-2444496, -149937, 29348902, 8186665, 1873760, 12489863, -30934579,
+             -7839692, -7852844, -8138429},
+            {-15236356, -15433509, 7766470, 746860, 26346930, -10221762,
+             -27333451, 10754588, -9431476, 5203576},
+            {31834314, 14135496, -770007, 5159118, 20917671, -16768096,
+             -7467973, -7337524, 31809243, 7347066},
+        },
+        {
+            {-9606723, -11874240, 20414459, 13033986, 13716524, -11691881,
+             19797970, -12211255, 15192876, -2087490},
+            {-12663563, -2181719, 1168162, -3804809, 26747877, -14138091,
+             10609330, 12694420, 33473243, -13382104},
+            {33184999, 11180355, 15832085, -11385430, -1633671, 225884,
+             15089336, -11023903, -6135662, 14480053},
+        },
+        {
+            {31308717, -5619998, 31030840, -1897099, 15674547, -6582883,
+             5496208, 13685227, 27595050, 8737275},
+            {-20318852, -15150239, 10933843, -16178022, 8335352, -7546022,
+             -31008351, -12610604, 26498114, 66511},
+            {22644454, -8761729, -16671776, 4884562, -3105614, -13559366,
+             30540766, -4286747, -13327787, -7515095},
+        },
+        {
+            {-28017847, 9834845, 18617207, -2681312, -3401956, -13307506,
+             8205540, 13585437, -17127465, 15115439},
+            {23711543, -672915, 31206561, -8362711, 6164647, -9709987,
+             -33535882, -1426096, 8236921, 16492939},
+            {-23910559, -13515526, -26299483, -4503841, 25005590, -7687270,
+             19574902, 10071562, 6708380, -6222424},
+        },
+        {
+            {2101391, -4930054, 19702731, 2367575, -15427167, 1047675, 5301017,
+             9328700, 29955601, -11678310},
+            {3096359, 9271816, -21620864, -15521844, -14847996, -7592937,
+             -25892142, -12635595, -9917575, 6216608},
+            {-32615849, 338663, -25195611, 2510422, -29213566, -13820213,
+             24822830, -6146567, -26767480, 7525079},
+        },
+        {
+            {-23066649, -13985623, 16133487, -7896178, -3389565, 778788,
+             -910336, -2782495, -19386633, 11994101},
+            {21691500, -13624626, -641331, -14367021, 3285881, -3483596,
+             -25064666, 9718258, -7477437, 13381418},
+            {18445390, -4202236, 14979846, 11622458, -1727110, -3582980,
+             23111648, -6375247, 28535282, 15779576},
+        },
+        {
+            {30098053, 3089662, -9234387, 16662135, -21306940, 11308411,
+             -14068454, 12021730, 9955285, -16303356},
+            {9734894, -14576830, -7473633, -9138735, 2060392, 11313496,
+             -18426029, 9924399, 20194861, 13380996},
+            {-26378102, -7965207, -22167821, 15789297, -18055342, -6168792,
+             -1984914, 15707771, 26342023, 10146099},
+        },
+    },
+    {
+        {
+            {-26016874, -219943, 21339191, -41388, 19745256, -2878700,
+             -29637280, 2227040, 21612326, -545728},
+            {-13077387, 1184228, 23562814, -5970442, -20351244, -6348714,
+             25764461, 12243797, -20856566, 11649658},
+            {-10031494, 11262626, 27384172, 2271902, 26947504, -15997771, 39944,
+             6114064, 33514190, 2333242},
+        },
+        {
+            {-21433588, -12421821, 8119782, 7219913, -21830522, -9016134,
+             -6679750, -12670638, 24350578, -13450001},
+            {-4116307, -11271533, -23886186, 4843615, -30088339, 690623,
+             -31536088, -10406836, 8317860, 12352766},
+            {18200138, -14475911, -33087759, -2696619, -23702521, -9102511,
+             -23552096, -2287550, 20712163, 6719373},
+        },
+        {
+            {26656208, 6075253, -7858556, 1886072, -28344043, 4262326, 11117530,
+             -3763210, 26224235, -3297458},
+            {-17168938, -14854097, -3395676, -16369877, -19954045, 14050420,
+             21728352, 9493610, 18620611, -16428628},
+            {-13323321, 13325349, 11432106, 5964811, 18609221, 6062965,
+             -5269471, -9725556, -30701573, -16479657},
+        },
+        {
+            {-23860538, -11233159, 26961357, 1640861, -32413112, -16737940,
+             12248509, -5240639, 13735342, 1934062},
+            {25089769, 6742589, 17081145, -13406266, 21909293, -16067981,
+             -15136294, -3765346, -21277997, 5473616},
+            {31883677, -7961101, 1083432, -11572403, 22828471, 13290673,
+             -7125085, 12469656, 29111212, -5451014},
+        },
+        {
+            {24244947, -15050407, -26262976, 2791540, -14997599, 16666678,
+             24367466, 6388839, -10295587, 452383},
+            {-25640782, -3417841, 5217916, 16224624, 19987036, -4082269,
+             -24236251, -5915248, 15766062, 8407814},
+            {-20406999, 13990231, 15495425, 16395525, 5377168, 15166495,
+             -8917023, -4388953, -8067909, 2276718},
+        },
+        {
+            {30157918, 12924066, -17712050, 9245753, 19895028, 3368142,
+             -23827587, 5096219, 22740376, -7303417},
+            {2041139, -14256350, 7783687, 13876377, -25946985, -13352459,
+             24051124, 13742383, -15637599, 13295222},
+            {33338237, -8505733, 12532113, 7977527, 9106186, -1715251,
+             -17720195, -4612972, -4451357, -14669444},
+        },
+        {
+            {-20045281, 5454097, -14346548, 6447146, 28862071, 1883651,
+             -2469266, -4141880, 7770569, 9620597},
+            {23208068, 7979712, 33071466, 8149229, 1758231, -10834995, 30945528,
+             -1694323, -33502340, -14767970},
+            {1439958, -16270480, -1079989, -793782, 4625402, 10647766, -5043801,
+             1220118, 30494170, -11440799},
+        },
+        {
+            {-5037580, -13028295, -2970559, -3061767, 15640974, -6701666,
+             -26739026, 926050, -1684339, -13333647},
+            {13908495, -3549272, 30919928, -6273825, -21521863, 7989039,
+             9021034, 9078865, 3353509, 4033511},
+            {-29663431, -15113610, 32259991, -344482, 24295849, -12912123,
+             23161163, 8839127, 27485041, 7356032},
+        },
+    },
+    {
+        {
+            {9661027, 705443, 11980065, -5370154, -1628543, 14661173, -6346142,
+             2625015, 28431036, -16771834},
+            {-23839233, -8311415, -25945511, 7480958, -17681669, -8354183,
+             -22545972, 14150565, 15970762, 4099461},
+            {29262576, 16756590, 26350592, -8793563, 8529671, -11208050,
+             13617293, -9937143, 11465739, 8317062},
+        },
+        {
+            {-25493081, -6962928, 32500200, -9419051, -23038724, -2302222,
+             14898637, 3848455, 20969334, -5157516},
+            {-20384450, -14347713, -18336405, 13884722, -33039454, 2842114,
+             -21610826, -3649888, 11177095, 14989547},
+            {-24496721, -11716016, 16959896, 2278463, 12066309, 10137771,
+             13515641, 2581286, -28487508, 9930240},
+        },
+        {
+            {-17751622, -2097826, 16544300, -13009300, -15914807, -14949081,
+             18345767, -13403753, 16291481, -5314038},
+            {-33229194, 2553288, 32678213, 9875984, 8534129, 6889387, -9676774,
+             6957617, 4368891, 9788741},
+            {16660756, 7281060, -10830758, 12911820, 20108584, -8101676,
+             -21722536, -8613148, 16250552, -11111103},
+        },
+        {
+            {-19765507, 2390526, -16551031, 14161980, 1905286, 6414907, 4689584,
+             10604807, -30190403, 4782747},
+            {-1354539, 14736941, -7367442, -13292886, 7710542, -14155590,
+             -9981571, 4383045, 22546403, 437323},
+            {31665577, -12180464, -16186830, 1491339, -18368625, 3294682,
+             27343084, 2786261, -30633590, -14097016},
+        },
+        {
+            {-14467279, -683715, -33374107, 7448552, 19294360, 14334329,
+             -19690631, 2355319, -19284671, -6114373},
+            {15121312, -15796162, 6377020, -6031361, -10798111, -12957845,
+             18952177, 15496498, -29380133, 11754228},
+            {-2637277, -13483075, 8488727, -14303896, 12728761, -1622493,
+             7141596, 11724556, 22761615, -10134141},
+        },
+        {
+            {16918416, 11729663, -18083579, 3022987, -31015732, -13339659,
+             -28741185, -12227393, 32851222, 11717399},
+            {11166634, 7338049, -6722523, 4531520, -29468672, -7302055,
+             31474879, 3483633, -1193175, -4030831},
+            {-185635, 9921305, 31456609, -13536438, -12013818, 13348923,
+             33142652, 6546660, -19985279, -3948376},
+        },
+        {
+            {-32460596, 11266712, -11197107, -7899103, 31703694, 3855903,
+             -8537131, -12833048, -30772034, -15486313},
+            {-18006477, 12709068, 3991746, -6479188, -21491523, -10550425,
+             -31135347, -16049879, 10928917, 3011958},
+            {-6957757, -15594337, 31696059, 334240, 29576716, 14796075,
+             -30831056, -12805180, 18008031, 10258577},
+        },
+        {
+            {-22448644, 15655569, 7018479, -4410003, -30314266, -1201591,
+             -1853465, 1367120, 25127874, 6671743},
+            {29701166, -14373934, -10878120, 9279288, -17568, 13127210,
+             21382910, 11042292, 25838796, 4642684},
+            {-20430234, 14955537, -24126347, 8124619, -5369288, -5990470,
+             30468147, -13900640, 18423289, 4177476},
+        },
+    },
+};
+
+static uint8_t negative(signed char b) {
+  uint32_t x = b;
+  x >>= 31; /* 1: yes; 0: no */
+  return x;
+}
+
+static void table_select(ge_precomp *t, int pos, signed char b) {
+  ge_precomp minust;
+  uint8_t bnegative = negative(b);
+  uint8_t babs = b - ((uint8_t)((-bnegative) & b) << 1);
+
+  ge_precomp_0(t);
+  cmov(t, &k25519Precomp[pos][0], equal(babs, 1));
+  cmov(t, &k25519Precomp[pos][1], equal(babs, 2));
+  cmov(t, &k25519Precomp[pos][2], equal(babs, 3));
+  cmov(t, &k25519Precomp[pos][3], equal(babs, 4));
+  cmov(t, &k25519Precomp[pos][4], equal(babs, 5));
+  cmov(t, &k25519Precomp[pos][5], equal(babs, 6));
+  cmov(t, &k25519Precomp[pos][6], equal(babs, 7));
+  cmov(t, &k25519Precomp[pos][7], equal(babs, 8));
+  fe_copy(minust.yplusx, t->yminusx);
+  fe_copy(minust.yminusx, t->yplusx);
+  fe_neg(minust.xy2d, t->xy2d);
+  cmov(t, &minust, bnegative);
+}
+
+/* h = a * B
+ * where a = a[0]+256*a[1]+...+256^31 a[31]
+ * B is the Ed25519 base point (x,4/5) with x positive.
+ *
+ * Preconditions:
+ *   a[31] <= 127 */
+static void ge_scalarmult_base(ge_p3 *h, const uint8_t *a) {
+  signed char e[64];
+  signed char carry;
+  ge_p1p1 r;
+  ge_p2 s;
+  ge_precomp t;
+  int i;
+
+  for (i = 0; i < 32; ++i) {
+    e[2 * i + 0] = (a[i] >> 0) & 15;
+    e[2 * i + 1] = (a[i] >> 4) & 15;
+  }
+  /* each e[i] is between 0 and 15 */
+  /* e[63] is between 0 and 7 */
+
+  carry = 0;
+  for (i = 0; i < 63; ++i) {
+    e[i] += carry;
+    carry = e[i] + 8;
+    carry >>= 4;
+    e[i] -= carry << 4;
+  }
+  e[63] += carry;
+  /* each e[i] is between -8 and 8 */
+
+  ge_p3_0(h);
+  for (i = 1; i < 64; i += 2) {
+    table_select(&t, i / 2, e[i]);
+    ge_madd(&r, h, &t);
+    ge_p1p1_to_p3(h, &r);
+  }
+
+  ge_p3_dbl(&r, h);
+  ge_p1p1_to_p2(&s, &r);
+  ge_p2_dbl(&r, &s);
+  ge_p1p1_to_p2(&s, &r);
+  ge_p2_dbl(&r, &s);
+  ge_p1p1_to_p2(&s, &r);
+  ge_p2_dbl(&r, &s);
+  ge_p1p1_to_p3(h, &r);
+
+  for (i = 0; i < 64; i += 2) {
+    table_select(&t, i / 2, e[i]);
+    ge_madd(&r, h, &t);
+    ge_p1p1_to_p3(h, &r);
+  }
+}
+
+/* Replace (f,g) with (g,f) if b == 1;
+ * replace (f,g) with (f,g) if b == 0.
+ *
+ * Preconditions: b in {0,1}. */
+static void fe_cswap(fe f, fe g, unsigned int b) {
+  size_t i;
+  b = 0-b;
+  for (i = 0; i < 10; i++) {
+    int32_t x = f[i] ^ g[i];
+    x &= b;
+    f[i] ^= x;
+    g[i] ^= x;
+  }
+}
+
+/* h = f * 121666
+ * Can overlap h with f.
+ *
+ * Preconditions:
+ *    |f| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc.
+ *
+ * Postconditions:
+ *    |h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. */
+static void fe_mul121666(fe h, fe f) {
+  int32_t f0 = f[0];
+  int32_t f1 = f[1];
+  int32_t f2 = f[2];
+  int32_t f3 = f[3];
+  int32_t f4 = f[4];
+  int32_t f5 = f[5];
+  int32_t f6 = f[6];
+  int32_t f7 = f[7];
+  int32_t f8 = f[8];
+  int32_t f9 = f[9];
+  int64_t h0 = f0 * (int64_t) 121666;
+  int64_t h1 = f1 * (int64_t) 121666;
+  int64_t h2 = f2 * (int64_t) 121666;
+  int64_t h3 = f3 * (int64_t) 121666;
+  int64_t h4 = f4 * (int64_t) 121666;
+  int64_t h5 = f5 * (int64_t) 121666;
+  int64_t h6 = f6 * (int64_t) 121666;
+  int64_t h7 = f7 * (int64_t) 121666;
+  int64_t h8 = f8 * (int64_t) 121666;
+  int64_t h9 = f9 * (int64_t) 121666;
+  int64_t carry0;
+  int64_t carry1;
+  int64_t carry2;
+  int64_t carry3;
+  int64_t carry4;
+  int64_t carry5;
+  int64_t carry6;
+  int64_t carry7;
+  int64_t carry8;
+  int64_t carry9;
+
+  carry9 = h9 + (1 << 24); h0 += (carry9 >> 25) * 19; h9 -= carry9 & kTop39Bits;
+  carry1 = h1 + (1 << 24); h2 += carry1 >> 25; h1 -= carry1 & kTop39Bits;
+  carry3 = h3 + (1 << 24); h4 += carry3 >> 25; h3 -= carry3 & kTop39Bits;
+  carry5 = h5 + (1 << 24); h6 += carry5 >> 25; h5 -= carry5 & kTop39Bits;
+  carry7 = h7 + (1 << 24); h8 += carry7 >> 25; h7 -= carry7 & kTop39Bits;
+
+  carry0 = h0 + (1 << 25); h1 += carry0 >> 26; h0 -= carry0 & kTop38Bits;
+  carry2 = h2 + (1 << 25); h3 += carry2 >> 26; h2 -= carry2 & kTop38Bits;
+  carry4 = h4 + (1 << 25); h5 += carry4 >> 26; h4 -= carry4 & kTop38Bits;
+  carry6 = h6 + (1 << 25); h7 += carry6 >> 26; h6 -= carry6 & kTop38Bits;
+  carry8 = h8 + (1 << 25); h9 += carry8 >> 26; h8 -= carry8 & kTop38Bits;
+
+  h[0] = h0;
+  h[1] = h1;
+  h[2] = h2;
+  h[3] = h3;
+  h[4] = h4;
+  h[5] = h5;
+  h[6] = h6;
+  h[7] = h7;
+  h[8] = h8;
+  h[9] = h9;
+}
+
+static void x25519_scalar_mult_generic(uint8_t out[32],
+                                       const uint8_t scalar[32],
+                                       const uint8_t point[32]) {
+  fe x1, x2, z2, x3, z3, tmp0, tmp1;
+  uint8_t e[32];
+  unsigned swap = 0;
+  int pos;
+
+  memcpy(e, scalar, 32);
+  e[0] &= 248;
+  e[31] &= 127;
+  e[31] |= 64;
+  fe_frombytes(x1, point);
+  fe_1(x2);
+  fe_0(z2);
+  fe_copy(x3, x1);
+  fe_1(z3);
+
+  for (pos = 254; pos >= 0; --pos) {
+    unsigned b = 1 & (e[pos / 8] >> (pos & 7));
+    swap ^= b;
+    fe_cswap(x2, x3, swap);
+    fe_cswap(z2, z3, swap);
+    swap = b;
+    fe_sub(tmp0, x3, z3);
+    fe_sub(tmp1, x2, z2);
+    fe_add(x2, x2, z2);
+    fe_add(z2, x3, z3);
+    fe_mul(z3, tmp0, x2);
+    fe_mul(z2, z2, tmp1);
+    fe_sq(tmp0, tmp1);
+    fe_sq(tmp1, x2);
+    fe_add(x3, z3, z2);
+    fe_sub(z2, z3, z2);
+    fe_mul(x2, tmp1, tmp0);
+    fe_sub(tmp1, tmp1, tmp0);
+    fe_sq(z2, z2);
+    fe_mul121666(z3, tmp1);
+    fe_sq(x3, x3);
+    fe_add(tmp0, tmp0, z3);
+    fe_mul(z3, x1, z2);
+    fe_mul(z2, tmp1, tmp0);
+  }
+  fe_cswap(x2, x3, swap);
+  fe_cswap(z2, z3, swap);
+
+  fe_invert(z2, z2);
+  fe_mul(x2, x2, z2);
+  fe_tobytes(out, x2);
+}
+
+static void x25519_scalar_mult(uint8_t out[32], const uint8_t scalar[32],
+                               const uint8_t point[32]) {
+  x25519_scalar_mult_generic(out, scalar, point);
+}
+
+int X25519(uint8_t out_shared_key[32], const uint8_t private_key[32],
+           const uint8_t peer_public_value[32]) {
+  static const uint8_t kZeros[32] = {0};
+  x25519_scalar_mult(out_shared_key, private_key, peer_public_value);
+  /* The all-zero output results when the input is a point of small order. */
+  return CRYPTO_memcmp(kZeros, out_shared_key, 32) != 0;
+}
+
+void X25519_public_from_private(uint8_t out_public_value[32],
+                                const uint8_t private_key[32]) {
+  uint8_t e[32];
+  ge_p3 A;
+  fe zplusy, zminusy, zminusy_inv;
+
+  memcpy(e, private_key, 32);
+  e[0] &= 248;
+  e[31] &= 127;
+  e[31] |= 64;
+
+  ge_scalarmult_base(&A, e);
+
+  /* We only need the u-coordinate of the curve25519 point. The map is
+   * u=(y+1)/(1-y). Since y=Y/Z, this gives u=(Z+Y)/(Z-Y). */
+  fe_add(zplusy, A.Z, A.Y);
+  fe_sub(zminusy, A.Z, A.Y);
+  fe_invert(zminusy_inv, zminusy);
+  fe_mul(zplusy, zplusy, zminusy_inv);
+  fe_tobytes(out_public_value, zplusy);
+}
diff --git a/crypto/ec/ec2_mult.c b/crypto/ec/ec2_mult.c
new file mode 100644
index 000000000..e4a1ec573
--- /dev/null
+++ b/crypto/ec/ec2_mult.c
@@ -0,0 +1,418 @@
+/*
+ * Copyright 2002-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/* ====================================================================
+ * Copyright 2002 Sun Microsystems, Inc. ALL RIGHTS RESERVED.
+ *
+ * The Elliptic Curve Public-Key Crypto Library (ECC Code) included
+ * herein is developed by SUN MICROSYSTEMS, INC., and is contributed
+ * to the OpenSSL project.
+ *
+ * The ECC Code is licensed pursuant to the OpenSSL open source
+ * license provided below.
+ *
+ * The software is originally written by Sheueling Chang Shantz and
+ * Douglas Stebila of Sun Microsystems Laboratories.
+ *
+ */
+
+#include 
+
+#include "internal/bn_int.h"
+#include "ec_lcl.h"
+
+#ifndef OPENSSL_NO_EC2M
+
+/*-
+ * Compute the x-coordinate x/z for the point 2*(x/z) in Montgomery projective
+ * coordinates.
+ * Uses algorithm Mdouble in appendix of
+ *     Lopez, J. and Dahab, R.  "Fast multiplication on elliptic curves over
+ *     GF(2^m) without precomputation" (CHES '99, LNCS 1717).
+ * modified to not require precomputation of c=b^{2^{m-1}}.
+ */
+static int gf2m_Mdouble(const EC_GROUP *group, BIGNUM *x, BIGNUM *z,
+                        BN_CTX *ctx)
+{
+    BIGNUM *t1;
+    int ret = 0;
+
+    /* Since Mdouble is static we can guarantee that ctx != NULL. */
+    BN_CTX_start(ctx);
+    t1 = BN_CTX_get(ctx);
+    if (t1 == NULL)
+        goto err;
+
+    if (!group->meth->field_sqr(group, x, x, ctx))
+        goto err;
+    if (!group->meth->field_sqr(group, t1, z, ctx))
+        goto err;
+    if (!group->meth->field_mul(group, z, x, t1, ctx))
+        goto err;
+    if (!group->meth->field_sqr(group, x, x, ctx))
+        goto err;
+    if (!group->meth->field_sqr(group, t1, t1, ctx))
+        goto err;
+    if (!group->meth->field_mul(group, t1, group->b, t1, ctx))
+        goto err;
+    if (!BN_GF2m_add(x, x, t1))
+        goto err;
+
+    ret = 1;
+
+ err:
+    BN_CTX_end(ctx);
+    return ret;
+}
+
+/*-
+ * Compute the x-coordinate x1/z1 for the point (x1/z1)+(x2/x2) in Montgomery
+ * projective coordinates.
+ * Uses algorithm Madd in appendix of
+ *     Lopez, J. and Dahab, R.  "Fast multiplication on elliptic curves over
+ *     GF(2^m) without precomputation" (CHES '99, LNCS 1717).
+ */
+static int gf2m_Madd(const EC_GROUP *group, const BIGNUM *x, BIGNUM *x1,
+                     BIGNUM *z1, const BIGNUM *x2, const BIGNUM *z2,
+                     BN_CTX *ctx)
+{
+    BIGNUM *t1, *t2;
+    int ret = 0;
+
+    /* Since Madd is static we can guarantee that ctx != NULL. */
+    BN_CTX_start(ctx);
+    t1 = BN_CTX_get(ctx);
+    t2 = BN_CTX_get(ctx);
+    if (t2 == NULL)
+        goto err;
+
+    if (!BN_copy(t1, x))
+        goto err;
+    if (!group->meth->field_mul(group, x1, x1, z2, ctx))
+        goto err;
+    if (!group->meth->field_mul(group, z1, z1, x2, ctx))
+        goto err;
+    if (!group->meth->field_mul(group, t2, x1, z1, ctx))
+        goto err;
+    if (!BN_GF2m_add(z1, z1, x1))
+        goto err;
+    if (!group->meth->field_sqr(group, z1, z1, ctx))
+        goto err;
+    if (!group->meth->field_mul(group, x1, z1, t1, ctx))
+        goto err;
+    if (!BN_GF2m_add(x1, x1, t2))
+        goto err;
+
+    ret = 1;
+
+ err:
+    BN_CTX_end(ctx);
+    return ret;
+}
+
+/*-
+ * Compute the x, y affine coordinates from the point (x1, z1) (x2, z2)
+ * using Montgomery point multiplication algorithm Mxy() in appendix of
+ *     Lopez, J. and Dahab, R.  "Fast multiplication on elliptic curves over
+ *     GF(2^m) without precomputation" (CHES '99, LNCS 1717).
+ * Returns:
+ *     0 on error
+ *     1 if return value should be the point at infinity
+ *     2 otherwise
+ */
+static int gf2m_Mxy(const EC_GROUP *group, const BIGNUM *x, const BIGNUM *y,
+                    BIGNUM *x1, BIGNUM *z1, BIGNUM *x2, BIGNUM *z2,
+                    BN_CTX *ctx)
+{
+    BIGNUM *t3, *t4, *t5;
+    int ret = 0;
+
+    if (BN_is_zero(z1)) {
+        BN_zero(x2);
+        BN_zero(z2);
+        return 1;
+    }
+
+    if (BN_is_zero(z2)) {
+        if (!BN_copy(x2, x))
+            return 0;
+        if (!BN_GF2m_add(z2, x, y))
+            return 0;
+        return 2;
+    }
+
+    /* Since Mxy is static we can guarantee that ctx != NULL. */
+    BN_CTX_start(ctx);
+    t3 = BN_CTX_get(ctx);
+    t4 = BN_CTX_get(ctx);
+    t5 = BN_CTX_get(ctx);
+    if (t5 == NULL)
+        goto err;
+
+    if (!BN_one(t5))
+        goto err;
+
+    if (!group->meth->field_mul(group, t3, z1, z2, ctx))
+        goto err;
+
+    if (!group->meth->field_mul(group, z1, z1, x, ctx))
+        goto err;
+    if (!BN_GF2m_add(z1, z1, x1))
+        goto err;
+    if (!group->meth->field_mul(group, z2, z2, x, ctx))
+        goto err;
+    if (!group->meth->field_mul(group, x1, z2, x1, ctx))
+        goto err;
+    if (!BN_GF2m_add(z2, z2, x2))
+        goto err;
+
+    if (!group->meth->field_mul(group, z2, z2, z1, ctx))
+        goto err;
+    if (!group->meth->field_sqr(group, t4, x, ctx))
+        goto err;
+    if (!BN_GF2m_add(t4, t4, y))
+        goto err;
+    if (!group->meth->field_mul(group, t4, t4, t3, ctx))
+        goto err;
+    if (!BN_GF2m_add(t4, t4, z2))
+        goto err;
+
+    if (!group->meth->field_mul(group, t3, t3, x, ctx))
+        goto err;
+    if (!group->meth->field_div(group, t3, t5, t3, ctx))
+        goto err;
+    if (!group->meth->field_mul(group, t4, t3, t4, ctx))
+        goto err;
+    if (!group->meth->field_mul(group, x2, x1, t3, ctx))
+        goto err;
+    if (!BN_GF2m_add(z2, x2, x))
+        goto err;
+
+    if (!group->meth->field_mul(group, z2, z2, t4, ctx))
+        goto err;
+    if (!BN_GF2m_add(z2, z2, y))
+        goto err;
+
+    ret = 2;
+
+ err:
+    BN_CTX_end(ctx);
+    return ret;
+}
+
+/*-
+ * Computes scalar*point and stores the result in r.
+ * point can not equal r.
+ * Uses a modified algorithm 2P of
+ *     Lopez, J. and Dahab, R.  "Fast multiplication on elliptic curves over
+ *     GF(2^m) without precomputation" (CHES '99, LNCS 1717).
+ *
+ * To protect against side-channel attack the function uses constant time swap,
+ * avoiding conditional branches.
+ */
+static int ec_GF2m_montgomery_point_multiply(const EC_GROUP *group,
+                                             EC_POINT *r,
+                                             const BIGNUM *scalar,
+                                             const EC_POINT *point,
+                                             BN_CTX *ctx)
+{
+    BIGNUM *x1, *x2, *z1, *z2;
+    int ret = 0, i, group_top;
+    BN_ULONG mask, word;
+
+    if (r == point) {
+        ECerr(EC_F_EC_GF2M_MONTGOMERY_POINT_MULTIPLY, EC_R_INVALID_ARGUMENT);
+        return 0;
+    }
+
+    /* if result should be point at infinity */
+    if ((scalar == NULL) || BN_is_zero(scalar) || (point == NULL) ||
+        EC_POINT_is_at_infinity(group, point)) {
+        return EC_POINT_set_to_infinity(group, r);
+    }
+
+    /* only support affine coordinates */
+    if (!point->Z_is_one)
+        return 0;
+
+    /*
+     * Since point_multiply is static we can guarantee that ctx != NULL.
+     */
+    BN_CTX_start(ctx);
+    x1 = BN_CTX_get(ctx);
+    z1 = BN_CTX_get(ctx);
+    if (z1 == NULL)
+        goto err;
+
+    x2 = r->X;
+    z2 = r->Y;
+
+    group_top = bn_get_top(group->field);
+    if (bn_wexpand(x1, group_top) == NULL
+        || bn_wexpand(z1, group_top) == NULL
+        || bn_wexpand(x2, group_top) == NULL
+        || bn_wexpand(z2, group_top) == NULL)
+        goto err;
+
+    if (!BN_GF2m_mod_arr(x1, point->X, group->poly))
+        goto err;               /* x1 = x */
+    if (!BN_one(z1))
+        goto err;               /* z1 = 1 */
+    if (!group->meth->field_sqr(group, z2, x1, ctx))
+        goto err;               /* z2 = x1^2 = x^2 */
+    if (!group->meth->field_sqr(group, x2, z2, ctx))
+        goto err;
+    if (!BN_GF2m_add(x2, x2, group->b))
+        goto err;               /* x2 = x^4 + b */
+
+    /* find top most bit and go one past it */
+    i = bn_get_top(scalar) - 1;
+    mask = BN_TBIT;
+    word = bn_get_words(scalar)[i];
+    while (!(word & mask))
+        mask >>= 1;
+    mask >>= 1;
+    /* if top most bit was at word break, go to next word */
+    if (!mask) {
+        i--;
+        mask = BN_TBIT;
+    }
+
+    for (; i >= 0; i--) {
+        word = bn_get_words(scalar)[i];
+        while (mask) {
+            BN_consttime_swap(word & mask, x1, x2, group_top);
+            BN_consttime_swap(word & mask, z1, z2, group_top);
+            if (!gf2m_Madd(group, point->X, x2, z2, x1, z1, ctx))
+                goto err;
+            if (!gf2m_Mdouble(group, x1, z1, ctx))
+                goto err;
+            BN_consttime_swap(word & mask, x1, x2, group_top);
+            BN_consttime_swap(word & mask, z1, z2, group_top);
+            mask >>= 1;
+        }
+        mask = BN_TBIT;
+    }
+
+    /* convert out of "projective" coordinates */
+    i = gf2m_Mxy(group, point->X, point->Y, x1, z1, x2, z2, ctx);
+    if (i == 0)
+        goto err;
+    else if (i == 1) {
+        if (!EC_POINT_set_to_infinity(group, r))
+            goto err;
+    } else {
+        if (!BN_one(r->Z))
+            goto err;
+        r->Z_is_one = 1;
+    }
+
+    /* GF(2^m) field elements should always have BIGNUM::neg = 0 */
+    BN_set_negative(r->X, 0);
+    BN_set_negative(r->Y, 0);
+
+    ret = 1;
+
+ err:
+    BN_CTX_end(ctx);
+    return ret;
+}
+
+/*-
+ * Computes the sum
+ *     scalar*group->generator + scalars[0]*points[0] + ... + scalars[num-1]*points[num-1]
+ * gracefully ignoring NULL scalar values.
+ */
+int ec_GF2m_simple_mul(const EC_GROUP *group, EC_POINT *r,
+                       const BIGNUM *scalar, size_t num,
+                       const EC_POINT *points[], const BIGNUM *scalars[],
+                       BN_CTX *ctx)
+{
+    BN_CTX *new_ctx = NULL;
+    int ret = 0;
+    size_t i;
+    EC_POINT *p = NULL;
+    EC_POINT *acc = NULL;
+
+    if (ctx == NULL) {
+        ctx = new_ctx = BN_CTX_new();
+        if (ctx == NULL)
+            return 0;
+    }
+
+    /*
+     * This implementation is more efficient than the wNAF implementation for
+     * 2 or fewer points.  Use the ec_wNAF_mul implementation for 3 or more
+     * points, or if we can perform a fast multiplication based on
+     * precomputation.
+     */
+    if ((scalar && (num > 1)) || (num > 2)
+        || (num == 0 && EC_GROUP_have_precompute_mult(group))) {
+        ret = ec_wNAF_mul(group, r, scalar, num, points, scalars, ctx);
+        goto err;
+    }
+
+    if ((p = EC_POINT_new(group)) == NULL)
+        goto err;
+    if ((acc = EC_POINT_new(group)) == NULL)
+        goto err;
+
+    if (!EC_POINT_set_to_infinity(group, acc))
+        goto err;
+
+    if (scalar) {
+        if (!ec_GF2m_montgomery_point_multiply
+            (group, p, scalar, group->generator, ctx))
+            goto err;
+        if (BN_is_negative(scalar))
+            if (!group->meth->invert(group, p, ctx))
+                goto err;
+        if (!group->meth->add(group, acc, acc, p, ctx))
+            goto err;
+    }
+
+    for (i = 0; i < num; i++) {
+        if (!ec_GF2m_montgomery_point_multiply
+            (group, p, scalars[i], points[i], ctx))
+            goto err;
+        if (BN_is_negative(scalars[i]))
+            if (!group->meth->invert(group, p, ctx))
+                goto err;
+        if (!group->meth->add(group, acc, acc, p, ctx))
+            goto err;
+    }
+
+    if (!EC_POINT_copy(r, acc))
+        goto err;
+
+    ret = 1;
+
+ err:
+    EC_POINT_free(p);
+    EC_POINT_free(acc);
+    BN_CTX_free(new_ctx);
+    return ret;
+}
+
+/*
+ * Precomputation for point multiplication: fall back to wNAF methods because
+ * ec_GF2m_simple_mul() uses ec_wNAF_mul() if appropriate
+ */
+
+int ec_GF2m_precompute_mult(EC_GROUP *group, BN_CTX *ctx)
+{
+    return ec_wNAF_precompute_mult(group, ctx);
+}
+
+int ec_GF2m_have_precompute_mult(const EC_GROUP *group)
+{
+    return ec_wNAF_have_precompute_mult(group);
+}
+
+#endif
diff --git a/crypto/ec/ec2_oct.c b/crypto/ec/ec2_oct.c
new file mode 100644
index 000000000..ea88ce860
--- /dev/null
+++ b/crypto/ec/ec2_oct.c
@@ -0,0 +1,352 @@
+/*
+ * Copyright 2011-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/* ====================================================================
+ * Copyright 2002 Sun Microsystems, Inc. ALL RIGHTS RESERVED.
+ *
+ * The Elliptic Curve Public-Key Crypto Library (ECC Code) included
+ * herein is developed by SUN MICROSYSTEMS, INC., and is contributed
+ * to the OpenSSL project.
+ *
+ * The ECC Code is licensed pursuant to the OpenSSL open source
+ * license provided below.
+ *
+ * The software is originally written by Sheueling Chang Shantz and
+ * Douglas Stebila of Sun Microsystems Laboratories.
+ *
+ */
+
+#include 
+
+#include "ec_lcl.h"
+
+#ifndef OPENSSL_NO_EC2M
+
+/*-
+ * Calculates and sets the affine coordinates of an EC_POINT from the given
+ * compressed coordinates.  Uses algorithm 2.3.4 of SEC 1.
+ * Note that the simple implementation only uses affine coordinates.
+ *
+ * The method is from the following publication:
+ *
+ *     Harper, Menezes, Vanstone:
+ *     "Public-Key Cryptosystems with Very Small Key Lengths",
+ *     EUROCRYPT '92, Springer-Verlag LNCS 658,
+ *     published February 1993
+ *
+ * US Patents 6,141,420 and 6,618,483 (Vanstone, Mullin, Agnew) describe
+ * the same method, but claim no priority date earlier than July 29, 1994
+ * (and additionally fail to cite the EUROCRYPT '92 publication as prior art).
+ */
+int ec_GF2m_simple_set_compressed_coordinates(const EC_GROUP *group,
+                                              EC_POINT *point,
+                                              const BIGNUM *x_, int y_bit,
+                                              BN_CTX *ctx)
+{
+    BN_CTX *new_ctx = NULL;
+    BIGNUM *tmp, *x, *y, *z;
+    int ret = 0, z0;
+
+    /* clear error queue */
+    ERR_clear_error();
+
+    if (ctx == NULL) {
+        ctx = new_ctx = BN_CTX_new();
+        if (ctx == NULL)
+            return 0;
+    }
+
+    y_bit = (y_bit != 0) ? 1 : 0;
+
+    BN_CTX_start(ctx);
+    tmp = BN_CTX_get(ctx);
+    x = BN_CTX_get(ctx);
+    y = BN_CTX_get(ctx);
+    z = BN_CTX_get(ctx);
+    if (z == NULL)
+        goto err;
+
+    if (!BN_GF2m_mod_arr(x, x_, group->poly))
+        goto err;
+    if (BN_is_zero(x)) {
+        if (!BN_GF2m_mod_sqrt_arr(y, group->b, group->poly, ctx))
+            goto err;
+    } else {
+        if (!group->meth->field_sqr(group, tmp, x, ctx))
+            goto err;
+        if (!group->meth->field_div(group, tmp, group->b, tmp, ctx))
+            goto err;
+        if (!BN_GF2m_add(tmp, group->a, tmp))
+            goto err;
+        if (!BN_GF2m_add(tmp, x, tmp))
+            goto err;
+        if (!BN_GF2m_mod_solve_quad_arr(z, tmp, group->poly, ctx)) {
+            unsigned long err = ERR_peek_last_error();
+
+            if (ERR_GET_LIB(err) == ERR_LIB_BN
+                && ERR_GET_REASON(err) == BN_R_NO_SOLUTION) {
+                ERR_clear_error();
+                ECerr(EC_F_EC_GF2M_SIMPLE_SET_COMPRESSED_COORDINATES,
+                      EC_R_INVALID_COMPRESSED_POINT);
+            } else
+                ECerr(EC_F_EC_GF2M_SIMPLE_SET_COMPRESSED_COORDINATES,
+                      ERR_R_BN_LIB);
+            goto err;
+        }
+        z0 = (BN_is_odd(z)) ? 1 : 0;
+        if (!group->meth->field_mul(group, y, x, z, ctx))
+            goto err;
+        if (z0 != y_bit) {
+            if (!BN_GF2m_add(y, y, x))
+                goto err;
+        }
+    }
+
+    if (!EC_POINT_set_affine_coordinates_GF2m(group, point, x, y, ctx))
+        goto err;
+
+    ret = 1;
+
+ err:
+    BN_CTX_end(ctx);
+    BN_CTX_free(new_ctx);
+    return ret;
+}
+
+/*
+ * Converts an EC_POINT to an octet string. If buf is NULL, the encoded
+ * length will be returned. If the length len of buf is smaller than required
+ * an error will be returned.
+ */
+size_t ec_GF2m_simple_point2oct(const EC_GROUP *group, const EC_POINT *point,
+                                point_conversion_form_t form,
+                                unsigned char *buf, size_t len, BN_CTX *ctx)
+{
+    size_t ret;
+    BN_CTX *new_ctx = NULL;
+    int used_ctx = 0;
+    BIGNUM *x, *y, *yxi;
+    size_t field_len, i, skip;
+
+    if ((form != POINT_CONVERSION_COMPRESSED)
+        && (form != POINT_CONVERSION_UNCOMPRESSED)
+        && (form != POINT_CONVERSION_HYBRID)) {
+        ECerr(EC_F_EC_GF2M_SIMPLE_POINT2OCT, EC_R_INVALID_FORM);
+        goto err;
+    }
+
+    if (EC_POINT_is_at_infinity(group, point)) {
+        /* encodes to a single 0 octet */
+        if (buf != NULL) {
+            if (len < 1) {
+                ECerr(EC_F_EC_GF2M_SIMPLE_POINT2OCT, EC_R_BUFFER_TOO_SMALL);
+                return 0;
+            }
+            buf[0] = 0;
+        }
+        return 1;
+    }
+
+    /* ret := required output buffer length */
+    field_len = (EC_GROUP_get_degree(group) + 7) / 8;
+    ret =
+        (form ==
+         POINT_CONVERSION_COMPRESSED) ? 1 + field_len : 1 + 2 * field_len;
+
+    /* if 'buf' is NULL, just return required length */
+    if (buf != NULL) {
+        if (len < ret) {
+            ECerr(EC_F_EC_GF2M_SIMPLE_POINT2OCT, EC_R_BUFFER_TOO_SMALL);
+            goto err;
+        }
+
+        if (ctx == NULL) {
+            ctx = new_ctx = BN_CTX_new();
+            if (ctx == NULL)
+                return 0;
+        }
+
+        BN_CTX_start(ctx);
+        used_ctx = 1;
+        x = BN_CTX_get(ctx);
+        y = BN_CTX_get(ctx);
+        yxi = BN_CTX_get(ctx);
+        if (yxi == NULL)
+            goto err;
+
+        if (!EC_POINT_get_affine_coordinates_GF2m(group, point, x, y, ctx))
+            goto err;
+
+        buf[0] = form;
+        if ((form != POINT_CONVERSION_UNCOMPRESSED) && !BN_is_zero(x)) {
+            if (!group->meth->field_div(group, yxi, y, x, ctx))
+                goto err;
+            if (BN_is_odd(yxi))
+                buf[0]++;
+        }
+
+        i = 1;
+
+        skip = field_len - BN_num_bytes(x);
+        if (skip > field_len) {
+            ECerr(EC_F_EC_GF2M_SIMPLE_POINT2OCT, ERR_R_INTERNAL_ERROR);
+            goto err;
+        }
+        while (skip > 0) {
+            buf[i++] = 0;
+            skip--;
+        }
+        skip = BN_bn2bin(x, buf + i);
+        i += skip;
+        if (i != 1 + field_len) {
+            ECerr(EC_F_EC_GF2M_SIMPLE_POINT2OCT, ERR_R_INTERNAL_ERROR);
+            goto err;
+        }
+
+        if (form == POINT_CONVERSION_UNCOMPRESSED
+            || form == POINT_CONVERSION_HYBRID) {
+            skip = field_len - BN_num_bytes(y);
+            if (skip > field_len) {
+                ECerr(EC_F_EC_GF2M_SIMPLE_POINT2OCT, ERR_R_INTERNAL_ERROR);
+                goto err;
+            }
+            while (skip > 0) {
+                buf[i++] = 0;
+                skip--;
+            }
+            skip = BN_bn2bin(y, buf + i);
+            i += skip;
+        }
+
+        if (i != ret) {
+            ECerr(EC_F_EC_GF2M_SIMPLE_POINT2OCT, ERR_R_INTERNAL_ERROR);
+            goto err;
+        }
+    }
+
+    if (used_ctx)
+        BN_CTX_end(ctx);
+    BN_CTX_free(new_ctx);
+    return ret;
+
+ err:
+    if (used_ctx)
+        BN_CTX_end(ctx);
+    BN_CTX_free(new_ctx);
+    return 0;
+}
+
+/*
+ * Converts an octet string representation to an EC_POINT. Note that the
+ * simple implementation only uses affine coordinates.
+ */
+int ec_GF2m_simple_oct2point(const EC_GROUP *group, EC_POINT *point,
+                             const unsigned char *buf, size_t len,
+                             BN_CTX *ctx)
+{
+    point_conversion_form_t form;
+    int y_bit;
+    BN_CTX *new_ctx = NULL;
+    BIGNUM *x, *y, *yxi;
+    size_t field_len, enc_len;
+    int ret = 0;
+
+    if (len == 0) {
+        ECerr(EC_F_EC_GF2M_SIMPLE_OCT2POINT, EC_R_BUFFER_TOO_SMALL);
+        return 0;
+    }
+    form = buf[0];
+    y_bit = form & 1;
+    form = form & ~1U;
+    if ((form != 0) && (form != POINT_CONVERSION_COMPRESSED)
+        && (form != POINT_CONVERSION_UNCOMPRESSED)
+        && (form != POINT_CONVERSION_HYBRID)) {
+        ECerr(EC_F_EC_GF2M_SIMPLE_OCT2POINT, EC_R_INVALID_ENCODING);
+        return 0;
+    }
+    if ((form == 0 || form == POINT_CONVERSION_UNCOMPRESSED) && y_bit) {
+        ECerr(EC_F_EC_GF2M_SIMPLE_OCT2POINT, EC_R_INVALID_ENCODING);
+        return 0;
+    }
+
+    if (form == 0) {
+        if (len != 1) {
+            ECerr(EC_F_EC_GF2M_SIMPLE_OCT2POINT, EC_R_INVALID_ENCODING);
+            return 0;
+        }
+
+        return EC_POINT_set_to_infinity(group, point);
+    }
+
+    field_len = (EC_GROUP_get_degree(group) + 7) / 8;
+    enc_len =
+        (form ==
+         POINT_CONVERSION_COMPRESSED) ? 1 + field_len : 1 + 2 * field_len;
+
+    if (len != enc_len) {
+        ECerr(EC_F_EC_GF2M_SIMPLE_OCT2POINT, EC_R_INVALID_ENCODING);
+        return 0;
+    }
+
+    if (ctx == NULL) {
+        ctx = new_ctx = BN_CTX_new();
+        if (ctx == NULL)
+            return 0;
+    }
+
+    BN_CTX_start(ctx);
+    x = BN_CTX_get(ctx);
+    y = BN_CTX_get(ctx);
+    yxi = BN_CTX_get(ctx);
+    if (yxi == NULL)
+        goto err;
+
+    if (!BN_bin2bn(buf + 1, field_len, x))
+        goto err;
+    if (BN_ucmp(x, group->field) >= 0) {
+        ECerr(EC_F_EC_GF2M_SIMPLE_OCT2POINT, EC_R_INVALID_ENCODING);
+        goto err;
+    }
+
+    if (form == POINT_CONVERSION_COMPRESSED) {
+        if (!EC_POINT_set_compressed_coordinates_GF2m
+            (group, point, x, y_bit, ctx))
+            goto err;
+    } else {
+        if (!BN_bin2bn(buf + 1 + field_len, field_len, y))
+            goto err;
+        if (BN_ucmp(y, group->field) >= 0) {
+            ECerr(EC_F_EC_GF2M_SIMPLE_OCT2POINT, EC_R_INVALID_ENCODING);
+            goto err;
+        }
+        if (form == POINT_CONVERSION_HYBRID) {
+            if (!group->meth->field_div(group, yxi, y, x, ctx))
+                goto err;
+            if (y_bit != BN_is_odd(yxi)) {
+                ECerr(EC_F_EC_GF2M_SIMPLE_OCT2POINT, EC_R_INVALID_ENCODING);
+                goto err;
+            }
+        }
+
+        /*
+         * EC_POINT_set_affine_coordinates_GF2m is responsible for checking that
+         * the point is on the curve.
+         */
+        if (!EC_POINT_set_affine_coordinates_GF2m(group, point, x, y, ctx))
+            goto err;
+    }
+
+    ret = 1;
+
+ err:
+    BN_CTX_end(ctx);
+    BN_CTX_free(new_ctx);
+    return ret;
+}
+#endif
diff --git a/crypto/ec/ec2_smpl.c b/crypto/ec/ec2_smpl.c
new file mode 100644
index 000000000..cfeef5fc7
--- /dev/null
+++ b/crypto/ec/ec2_smpl.c
@@ -0,0 +1,758 @@
+/*
+ * Copyright 2002-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/* ====================================================================
+ * Copyright 2002 Sun Microsystems, Inc. ALL RIGHTS RESERVED.
+ *
+ * The Elliptic Curve Public-Key Crypto Library (ECC Code) included
+ * herein is developed by SUN MICROSYSTEMS, INC., and is contributed
+ * to the OpenSSL project.
+ *
+ * The ECC Code is licensed pursuant to the OpenSSL open source
+ * license provided below.
+ *
+ * The software is originally written by Sheueling Chang Shantz and
+ * Douglas Stebila of Sun Microsystems Laboratories.
+ *
+ */
+
+#include 
+
+#include "internal/bn_int.h"
+#include "ec_lcl.h"
+
+#ifndef OPENSSL_NO_EC2M
+
+const EC_METHOD *EC_GF2m_simple_method(void)
+{
+    static const EC_METHOD ret = {
+        EC_FLAGS_DEFAULT_OCT,
+        NID_X9_62_characteristic_two_field,
+        ec_GF2m_simple_group_init,
+        ec_GF2m_simple_group_finish,
+        ec_GF2m_simple_group_clear_finish,
+        ec_GF2m_simple_group_copy,
+        ec_GF2m_simple_group_set_curve,
+        ec_GF2m_simple_group_get_curve,
+        ec_GF2m_simple_group_get_degree,
+        ec_group_simple_order_bits,
+        ec_GF2m_simple_group_check_discriminant,
+        ec_GF2m_simple_point_init,
+        ec_GF2m_simple_point_finish,
+        ec_GF2m_simple_point_clear_finish,
+        ec_GF2m_simple_point_copy,
+        ec_GF2m_simple_point_set_to_infinity,
+        0 /* set_Jprojective_coordinates_GFp */ ,
+        0 /* get_Jprojective_coordinates_GFp */ ,
+        ec_GF2m_simple_point_set_affine_coordinates,
+        ec_GF2m_simple_point_get_affine_coordinates,
+        0, 0, 0,
+        ec_GF2m_simple_add,
+        ec_GF2m_simple_dbl,
+        ec_GF2m_simple_invert,
+        ec_GF2m_simple_is_at_infinity,
+        ec_GF2m_simple_is_on_curve,
+        ec_GF2m_simple_cmp,
+        ec_GF2m_simple_make_affine,
+        ec_GF2m_simple_points_make_affine,
+
+        /*
+         * the following three method functions are defined in ec2_mult.c
+         */
+        ec_GF2m_simple_mul,
+        ec_GF2m_precompute_mult,
+        ec_GF2m_have_precompute_mult,
+
+        ec_GF2m_simple_field_mul,
+        ec_GF2m_simple_field_sqr,
+        ec_GF2m_simple_field_div,
+        0 /* field_encode */ ,
+        0 /* field_decode */ ,
+        0,                      /* field_set_to_one */
+        ec_key_simple_priv2oct,
+        ec_key_simple_oct2priv,
+        0, /* set private */
+        ec_key_simple_generate_key,
+        ec_key_simple_check_key,
+        ec_key_simple_generate_public_key,
+        0, /* keycopy */
+        0, /* keyfinish */
+        ecdh_simple_compute_key
+    };
+
+    return &ret;
+}
+
+/*
+ * Initialize a GF(2^m)-based EC_GROUP structure. Note that all other members
+ * are handled by EC_GROUP_new.
+ */
+int ec_GF2m_simple_group_init(EC_GROUP *group)
+{
+    group->field = BN_new();
+    group->a = BN_new();
+    group->b = BN_new();
+
+    if (group->field == NULL || group->a == NULL || group->b == NULL) {
+        BN_free(group->field);
+        BN_free(group->a);
+        BN_free(group->b);
+        return 0;
+    }
+    return 1;
+}
+
+/*
+ * Free a GF(2^m)-based EC_GROUP structure. Note that all other members are
+ * handled by EC_GROUP_free.
+ */
+void ec_GF2m_simple_group_finish(EC_GROUP *group)
+{
+    BN_free(group->field);
+    BN_free(group->a);
+    BN_free(group->b);
+}
+
+/*
+ * Clear and free a GF(2^m)-based EC_GROUP structure. Note that all other
+ * members are handled by EC_GROUP_clear_free.
+ */
+void ec_GF2m_simple_group_clear_finish(EC_GROUP *group)
+{
+    BN_clear_free(group->field);
+    BN_clear_free(group->a);
+    BN_clear_free(group->b);
+    group->poly[0] = 0;
+    group->poly[1] = 0;
+    group->poly[2] = 0;
+    group->poly[3] = 0;
+    group->poly[4] = 0;
+    group->poly[5] = -1;
+}
+
+/*
+ * Copy a GF(2^m)-based EC_GROUP structure. Note that all other members are
+ * handled by EC_GROUP_copy.
+ */
+int ec_GF2m_simple_group_copy(EC_GROUP *dest, const EC_GROUP *src)
+{
+    if (!BN_copy(dest->field, src->field))
+        return 0;
+    if (!BN_copy(dest->a, src->a))
+        return 0;
+    if (!BN_copy(dest->b, src->b))
+        return 0;
+    dest->poly[0] = src->poly[0];
+    dest->poly[1] = src->poly[1];
+    dest->poly[2] = src->poly[2];
+    dest->poly[3] = src->poly[3];
+    dest->poly[4] = src->poly[4];
+    dest->poly[5] = src->poly[5];
+    if (bn_wexpand(dest->a, (int)(dest->poly[0] + BN_BITS2 - 1) / BN_BITS2) ==
+        NULL)
+        return 0;
+    if (bn_wexpand(dest->b, (int)(dest->poly[0] + BN_BITS2 - 1) / BN_BITS2) ==
+        NULL)
+        return 0;
+    bn_set_all_zero(dest->a);
+    bn_set_all_zero(dest->b);
+    return 1;
+}
+
+/* Set the curve parameters of an EC_GROUP structure. */
+int ec_GF2m_simple_group_set_curve(EC_GROUP *group,
+                                   const BIGNUM *p, const BIGNUM *a,
+                                   const BIGNUM *b, BN_CTX *ctx)
+{
+    int ret = 0, i;
+
+    /* group->field */
+    if (!BN_copy(group->field, p))
+        goto err;
+    i = BN_GF2m_poly2arr(group->field, group->poly, 6) - 1;
+    if ((i != 5) && (i != 3)) {
+        ECerr(EC_F_EC_GF2M_SIMPLE_GROUP_SET_CURVE, EC_R_UNSUPPORTED_FIELD);
+        goto err;
+    }
+
+    /* group->a */
+    if (!BN_GF2m_mod_arr(group->a, a, group->poly))
+        goto err;
+    if (bn_wexpand(group->a, (int)(group->poly[0] + BN_BITS2 - 1) / BN_BITS2)
+        == NULL)
+        goto err;
+    bn_set_all_zero(group->a);
+
+    /* group->b */
+    if (!BN_GF2m_mod_arr(group->b, b, group->poly))
+        goto err;
+    if (bn_wexpand(group->b, (int)(group->poly[0] + BN_BITS2 - 1) / BN_BITS2)
+        == NULL)
+        goto err;
+    bn_set_all_zero(group->b);
+
+    ret = 1;
+ err:
+    return ret;
+}
+
+/*
+ * Get the curve parameters of an EC_GROUP structure. If p, a, or b are NULL
+ * then there values will not be set but the method will return with success.
+ */
+int ec_GF2m_simple_group_get_curve(const EC_GROUP *group, BIGNUM *p,
+                                   BIGNUM *a, BIGNUM *b, BN_CTX *ctx)
+{
+    int ret = 0;
+
+    if (p != NULL) {
+        if (!BN_copy(p, group->field))
+            return 0;
+    }
+
+    if (a != NULL) {
+        if (!BN_copy(a, group->a))
+            goto err;
+    }
+
+    if (b != NULL) {
+        if (!BN_copy(b, group->b))
+            goto err;
+    }
+
+    ret = 1;
+
+ err:
+    return ret;
+}
+
+/*
+ * Gets the degree of the field.  For a curve over GF(2^m) this is the value
+ * m.
+ */
+int ec_GF2m_simple_group_get_degree(const EC_GROUP *group)
+{
+    return BN_num_bits(group->field) - 1;
+}
+
+/*
+ * Checks the discriminant of the curve. y^2 + x*y = x^3 + a*x^2 + b is an
+ * elliptic curve <=> b != 0 (mod p)
+ */
+int ec_GF2m_simple_group_check_discriminant(const EC_GROUP *group,
+                                            BN_CTX *ctx)
+{
+    int ret = 0;
+    BIGNUM *b;
+    BN_CTX *new_ctx = NULL;
+
+    if (ctx == NULL) {
+        ctx = new_ctx = BN_CTX_new();
+        if (ctx == NULL) {
+            ECerr(EC_F_EC_GF2M_SIMPLE_GROUP_CHECK_DISCRIMINANT,
+                  ERR_R_MALLOC_FAILURE);
+            goto err;
+        }
+    }
+    BN_CTX_start(ctx);
+    b = BN_CTX_get(ctx);
+    if (b == NULL)
+        goto err;
+
+    if (!BN_GF2m_mod_arr(b, group->b, group->poly))
+        goto err;
+
+    /*
+     * check the discriminant: y^2 + x*y = x^3 + a*x^2 + b is an elliptic
+     * curve <=> b != 0 (mod p)
+     */
+    if (BN_is_zero(b))
+        goto err;
+
+    ret = 1;
+
+ err:
+    if (ctx != NULL)
+        BN_CTX_end(ctx);
+    BN_CTX_free(new_ctx);
+    return ret;
+}
+
+/* Initializes an EC_POINT. */
+int ec_GF2m_simple_point_init(EC_POINT *point)
+{
+    point->X = BN_new();
+    point->Y = BN_new();
+    point->Z = BN_new();
+
+    if (point->X == NULL || point->Y == NULL || point->Z == NULL) {
+        BN_free(point->X);
+        BN_free(point->Y);
+        BN_free(point->Z);
+        return 0;
+    }
+    return 1;
+}
+
+/* Frees an EC_POINT. */
+void ec_GF2m_simple_point_finish(EC_POINT *point)
+{
+    BN_free(point->X);
+    BN_free(point->Y);
+    BN_free(point->Z);
+}
+
+/* Clears and frees an EC_POINT. */
+void ec_GF2m_simple_point_clear_finish(EC_POINT *point)
+{
+    BN_clear_free(point->X);
+    BN_clear_free(point->Y);
+    BN_clear_free(point->Z);
+    point->Z_is_one = 0;
+}
+
+/*
+ * Copy the contents of one EC_POINT into another.  Assumes dest is
+ * initialized.
+ */
+int ec_GF2m_simple_point_copy(EC_POINT *dest, const EC_POINT *src)
+{
+    if (!BN_copy(dest->X, src->X))
+        return 0;
+    if (!BN_copy(dest->Y, src->Y))
+        return 0;
+    if (!BN_copy(dest->Z, src->Z))
+        return 0;
+    dest->Z_is_one = src->Z_is_one;
+
+    return 1;
+}
+
+/*
+ * Set an EC_POINT to the point at infinity. A point at infinity is
+ * represented by having Z=0.
+ */
+int ec_GF2m_simple_point_set_to_infinity(const EC_GROUP *group,
+                                         EC_POINT *point)
+{
+    point->Z_is_one = 0;
+    BN_zero(point->Z);
+    return 1;
+}
+
+/*
+ * Set the coordinates of an EC_POINT using affine coordinates. Note that
+ * the simple implementation only uses affine coordinates.
+ */
+int ec_GF2m_simple_point_set_affine_coordinates(const EC_GROUP *group,
+                                                EC_POINT *point,
+                                                const BIGNUM *x,
+                                                const BIGNUM *y, BN_CTX *ctx)
+{
+    int ret = 0;
+    if (x == NULL || y == NULL) {
+        ECerr(EC_F_EC_GF2M_SIMPLE_POINT_SET_AFFINE_COORDINATES,
+              ERR_R_PASSED_NULL_PARAMETER);
+        return 0;
+    }
+
+    if (!BN_copy(point->X, x))
+        goto err;
+    BN_set_negative(point->X, 0);
+    if (!BN_copy(point->Y, y))
+        goto err;
+    BN_set_negative(point->Y, 0);
+    if (!BN_copy(point->Z, BN_value_one()))
+        goto err;
+    BN_set_negative(point->Z, 0);
+    point->Z_is_one = 1;
+    ret = 1;
+
+ err:
+    return ret;
+}
+
+/*
+ * Gets the affine coordinates of an EC_POINT. Note that the simple
+ * implementation only uses affine coordinates.
+ */
+int ec_GF2m_simple_point_get_affine_coordinates(const EC_GROUP *group,
+                                                const EC_POINT *point,
+                                                BIGNUM *x, BIGNUM *y,
+                                                BN_CTX *ctx)
+{
+    int ret = 0;
+
+    if (EC_POINT_is_at_infinity(group, point)) {
+        ECerr(EC_F_EC_GF2M_SIMPLE_POINT_GET_AFFINE_COORDINATES,
+              EC_R_POINT_AT_INFINITY);
+        return 0;
+    }
+
+    if (BN_cmp(point->Z, BN_value_one())) {
+        ECerr(EC_F_EC_GF2M_SIMPLE_POINT_GET_AFFINE_COORDINATES,
+              ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
+        return 0;
+    }
+    if (x != NULL) {
+        if (!BN_copy(x, point->X))
+            goto err;
+        BN_set_negative(x, 0);
+    }
+    if (y != NULL) {
+        if (!BN_copy(y, point->Y))
+            goto err;
+        BN_set_negative(y, 0);
+    }
+    ret = 1;
+
+ err:
+    return ret;
+}
+
+/*
+ * Computes a + b and stores the result in r.  r could be a or b, a could be
+ * b. Uses algorithm A.10.2 of IEEE P1363.
+ */
+int ec_GF2m_simple_add(const EC_GROUP *group, EC_POINT *r, const EC_POINT *a,
+                       const EC_POINT *b, BN_CTX *ctx)
+{
+    BN_CTX *new_ctx = NULL;
+    BIGNUM *x0, *y0, *x1, *y1, *x2, *y2, *s, *t;
+    int ret = 0;
+
+    if (EC_POINT_is_at_infinity(group, a)) {
+        if (!EC_POINT_copy(r, b))
+            return 0;
+        return 1;
+    }
+
+    if (EC_POINT_is_at_infinity(group, b)) {
+        if (!EC_POINT_copy(r, a))
+            return 0;
+        return 1;
+    }
+
+    if (ctx == NULL) {
+        ctx = new_ctx = BN_CTX_new();
+        if (ctx == NULL)
+            return 0;
+    }
+
+    BN_CTX_start(ctx);
+    x0 = BN_CTX_get(ctx);
+    y0 = BN_CTX_get(ctx);
+    x1 = BN_CTX_get(ctx);
+    y1 = BN_CTX_get(ctx);
+    x2 = BN_CTX_get(ctx);
+    y2 = BN_CTX_get(ctx);
+    s = BN_CTX_get(ctx);
+    t = BN_CTX_get(ctx);
+    if (t == NULL)
+        goto err;
+
+    if (a->Z_is_one) {
+        if (!BN_copy(x0, a->X))
+            goto err;
+        if (!BN_copy(y0, a->Y))
+            goto err;
+    } else {
+        if (!EC_POINT_get_affine_coordinates_GF2m(group, a, x0, y0, ctx))
+            goto err;
+    }
+    if (b->Z_is_one) {
+        if (!BN_copy(x1, b->X))
+            goto err;
+        if (!BN_copy(y1, b->Y))
+            goto err;
+    } else {
+        if (!EC_POINT_get_affine_coordinates_GF2m(group, b, x1, y1, ctx))
+            goto err;
+    }
+
+    if (BN_GF2m_cmp(x0, x1)) {
+        if (!BN_GF2m_add(t, x0, x1))
+            goto err;
+        if (!BN_GF2m_add(s, y0, y1))
+            goto err;
+        if (!group->meth->field_div(group, s, s, t, ctx))
+            goto err;
+        if (!group->meth->field_sqr(group, x2, s, ctx))
+            goto err;
+        if (!BN_GF2m_add(x2, x2, group->a))
+            goto err;
+        if (!BN_GF2m_add(x2, x2, s))
+            goto err;
+        if (!BN_GF2m_add(x2, x2, t))
+            goto err;
+    } else {
+        if (BN_GF2m_cmp(y0, y1) || BN_is_zero(x1)) {
+            if (!EC_POINT_set_to_infinity(group, r))
+                goto err;
+            ret = 1;
+            goto err;
+        }
+        if (!group->meth->field_div(group, s, y1, x1, ctx))
+            goto err;
+        if (!BN_GF2m_add(s, s, x1))
+            goto err;
+
+        if (!group->meth->field_sqr(group, x2, s, ctx))
+            goto err;
+        if (!BN_GF2m_add(x2, x2, s))
+            goto err;
+        if (!BN_GF2m_add(x2, x2, group->a))
+            goto err;
+    }
+
+    if (!BN_GF2m_add(y2, x1, x2))
+        goto err;
+    if (!group->meth->field_mul(group, y2, y2, s, ctx))
+        goto err;
+    if (!BN_GF2m_add(y2, y2, x2))
+        goto err;
+    if (!BN_GF2m_add(y2, y2, y1))
+        goto err;
+
+    if (!EC_POINT_set_affine_coordinates_GF2m(group, r, x2, y2, ctx))
+        goto err;
+
+    ret = 1;
+
+ err:
+    BN_CTX_end(ctx);
+    BN_CTX_free(new_ctx);
+    return ret;
+}
+
+/*
+ * Computes 2 * a and stores the result in r.  r could be a. Uses algorithm
+ * A.10.2 of IEEE P1363.
+ */
+int ec_GF2m_simple_dbl(const EC_GROUP *group, EC_POINT *r, const EC_POINT *a,
+                       BN_CTX *ctx)
+{
+    return ec_GF2m_simple_add(group, r, a, a, ctx);
+}
+
+int ec_GF2m_simple_invert(const EC_GROUP *group, EC_POINT *point, BN_CTX *ctx)
+{
+    if (EC_POINT_is_at_infinity(group, point) || BN_is_zero(point->Y))
+        /* point is its own inverse */
+        return 1;
+
+    if (!EC_POINT_make_affine(group, point, ctx))
+        return 0;
+    return BN_GF2m_add(point->Y, point->X, point->Y);
+}
+
+/* Indicates whether the given point is the point at infinity. */
+int ec_GF2m_simple_is_at_infinity(const EC_GROUP *group,
+                                  const EC_POINT *point)
+{
+    return BN_is_zero(point->Z);
+}
+
+/*-
+ * Determines whether the given EC_POINT is an actual point on the curve defined
+ * in the EC_GROUP.  A point is valid if it satisfies the Weierstrass equation:
+ *      y^2 + x*y = x^3 + a*x^2 + b.
+ */
+int ec_GF2m_simple_is_on_curve(const EC_GROUP *group, const EC_POINT *point,
+                               BN_CTX *ctx)
+{
+    int ret = -1;
+    BN_CTX *new_ctx = NULL;
+    BIGNUM *lh, *y2;
+    int (*field_mul) (const EC_GROUP *, BIGNUM *, const BIGNUM *,
+                      const BIGNUM *, BN_CTX *);
+    int (*field_sqr) (const EC_GROUP *, BIGNUM *, const BIGNUM *, BN_CTX *);
+
+    if (EC_POINT_is_at_infinity(group, point))
+        return 1;
+
+    field_mul = group->meth->field_mul;
+    field_sqr = group->meth->field_sqr;
+
+    /* only support affine coordinates */
+    if (!point->Z_is_one)
+        return -1;
+
+    if (ctx == NULL) {
+        ctx = new_ctx = BN_CTX_new();
+        if (ctx == NULL)
+            return -1;
+    }
+
+    BN_CTX_start(ctx);
+    y2 = BN_CTX_get(ctx);
+    lh = BN_CTX_get(ctx);
+    if (lh == NULL)
+        goto err;
+
+    /*-
+     * We have a curve defined by a Weierstrass equation
+     *      y^2 + x*y = x^3 + a*x^2 + b.
+     *  <=> x^3 + a*x^2 + x*y + b + y^2 = 0
+     *  <=> ((x + a) * x + y ) * x + b + y^2 = 0
+     */
+    if (!BN_GF2m_add(lh, point->X, group->a))
+        goto err;
+    if (!field_mul(group, lh, lh, point->X, ctx))
+        goto err;
+    if (!BN_GF2m_add(lh, lh, point->Y))
+        goto err;
+    if (!field_mul(group, lh, lh, point->X, ctx))
+        goto err;
+    if (!BN_GF2m_add(lh, lh, group->b))
+        goto err;
+    if (!field_sqr(group, y2, point->Y, ctx))
+        goto err;
+    if (!BN_GF2m_add(lh, lh, y2))
+        goto err;
+    ret = BN_is_zero(lh);
+ err:
+    if (ctx)
+        BN_CTX_end(ctx);
+    BN_CTX_free(new_ctx);
+    return ret;
+}
+
+/*-
+ * Indicates whether two points are equal.
+ * Return values:
+ *  -1   error
+ *   0   equal (in affine coordinates)
+ *   1   not equal
+ */
+int ec_GF2m_simple_cmp(const EC_GROUP *group, const EC_POINT *a,
+                       const EC_POINT *b, BN_CTX *ctx)
+{
+    BIGNUM *aX, *aY, *bX, *bY;
+    BN_CTX *new_ctx = NULL;
+    int ret = -1;
+
+    if (EC_POINT_is_at_infinity(group, a)) {
+        return EC_POINT_is_at_infinity(group, b) ? 0 : 1;
+    }
+
+    if (EC_POINT_is_at_infinity(group, b))
+        return 1;
+
+    if (a->Z_is_one && b->Z_is_one) {
+        return ((BN_cmp(a->X, b->X) == 0) && BN_cmp(a->Y, b->Y) == 0) ? 0 : 1;
+    }
+
+    if (ctx == NULL) {
+        ctx = new_ctx = BN_CTX_new();
+        if (ctx == NULL)
+            return -1;
+    }
+
+    BN_CTX_start(ctx);
+    aX = BN_CTX_get(ctx);
+    aY = BN_CTX_get(ctx);
+    bX = BN_CTX_get(ctx);
+    bY = BN_CTX_get(ctx);
+    if (bY == NULL)
+        goto err;
+
+    if (!EC_POINT_get_affine_coordinates_GF2m(group, a, aX, aY, ctx))
+        goto err;
+    if (!EC_POINT_get_affine_coordinates_GF2m(group, b, bX, bY, ctx))
+        goto err;
+    ret = ((BN_cmp(aX, bX) == 0) && BN_cmp(aY, bY) == 0) ? 0 : 1;
+
+ err:
+    if (ctx)
+        BN_CTX_end(ctx);
+    BN_CTX_free(new_ctx);
+    return ret;
+}
+
+/* Forces the given EC_POINT to internally use affine coordinates. */
+int ec_GF2m_simple_make_affine(const EC_GROUP *group, EC_POINT *point,
+                               BN_CTX *ctx)
+{
+    BN_CTX *new_ctx = NULL;
+    BIGNUM *x, *y;
+    int ret = 0;
+
+    if (point->Z_is_one || EC_POINT_is_at_infinity(group, point))
+        return 1;
+
+    if (ctx == NULL) {
+        ctx = new_ctx = BN_CTX_new();
+        if (ctx == NULL)
+            return 0;
+    }
+
+    BN_CTX_start(ctx);
+    x = BN_CTX_get(ctx);
+    y = BN_CTX_get(ctx);
+    if (y == NULL)
+        goto err;
+
+    if (!EC_POINT_get_affine_coordinates_GF2m(group, point, x, y, ctx))
+        goto err;
+    if (!BN_copy(point->X, x))
+        goto err;
+    if (!BN_copy(point->Y, y))
+        goto err;
+    if (!BN_one(point->Z))
+        goto err;
+    point->Z_is_one = 1;
+
+    ret = 1;
+
+ err:
+    if (ctx)
+        BN_CTX_end(ctx);
+    BN_CTX_free(new_ctx);
+    return ret;
+}
+
+/*
+ * Forces each of the EC_POINTs in the given array to use affine coordinates.
+ */
+int ec_GF2m_simple_points_make_affine(const EC_GROUP *group, size_t num,
+                                      EC_POINT *points[], BN_CTX *ctx)
+{
+    size_t i;
+
+    for (i = 0; i < num; i++) {
+        if (!group->meth->make_affine(group, points[i], ctx))
+            return 0;
+    }
+
+    return 1;
+}
+
+/* Wrapper to simple binary polynomial field multiplication implementation. */
+int ec_GF2m_simple_field_mul(const EC_GROUP *group, BIGNUM *r,
+                             const BIGNUM *a, const BIGNUM *b, BN_CTX *ctx)
+{
+    return BN_GF2m_mod_mul_arr(r, a, b, group->poly, ctx);
+}
+
+/* Wrapper to simple binary polynomial field squaring implementation. */
+int ec_GF2m_simple_field_sqr(const EC_GROUP *group, BIGNUM *r,
+                             const BIGNUM *a, BN_CTX *ctx)
+{
+    return BN_GF2m_mod_sqr_arr(r, a, group->poly, ctx);
+}
+
+/* Wrapper to simple binary polynomial field division implementation. */
+int ec_GF2m_simple_field_div(const EC_GROUP *group, BIGNUM *r,
+                             const BIGNUM *a, const BIGNUM *b, BN_CTX *ctx)
+{
+    return BN_GF2m_mod_div(r, a, b, group->field, ctx);
+}
+
+#endif
diff --git a/crypto/ec/ec_ameth.c b/crypto/ec/ec_ameth.c
new file mode 100644
index 000000000..82ed34384
--- /dev/null
+++ b/crypto/ec/ec_ameth.c
@@ -0,0 +1,951 @@
+/* ====================================================================
+ * Copyright (c) 2016 - 2019 The GmSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the GmSSL Project.
+ *    (http://gmssl.org/)"
+ *
+ * 4. The name "GmSSL Project" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    guanzhi1980@gmail.com.
+ *
+ * 5. Products derived from this software may not be called "GmSSL"
+ *    nor may "GmSSL" appear in their names without prior written
+ *    permission of the GmSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the GmSSL Project
+ *    (http://gmssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE GmSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE GmSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+/*
+ * Copyright 2006-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+#include 
+#ifndef OPENSSL_NO_CMS
+# include 
+#endif
+#include 
+#include "internal/asn1_int.h"
+#include "internal/evp_int.h"
+#include "ec_lcl.h"
+#ifndef OPENSSL_NO_SM2
+# include 
+#endif
+
+#ifndef OPENSSL_NO_CMS
+static int ecdh_cms_decrypt(CMS_RecipientInfo *ri);
+static int ecdh_cms_encrypt(CMS_RecipientInfo *ri);
+#endif
+
+static int eckey_param2type(int *pptype, void **ppval, EC_KEY *ec_key)
+{
+    const EC_GROUP *group;
+    int nid;
+    if (ec_key == NULL || (group = EC_KEY_get0_group(ec_key)) == NULL) {
+        ECerr(EC_F_ECKEY_PARAM2TYPE, EC_R_MISSING_PARAMETERS);
+        return 0;
+    }
+    if (EC_GROUP_get_asn1_flag(group)
+        && (nid = EC_GROUP_get_curve_name(group)))
+        /* we have a 'named curve' => just set the OID */
+    {
+        *ppval = OBJ_nid2obj(nid);
+        *pptype = V_ASN1_OBJECT;
+    } else {                    /* explicit parameters */
+
+        ASN1_STRING *pstr = NULL;
+        pstr = ASN1_STRING_new();
+        if (pstr == NULL)
+            return 0;
+        pstr->length = i2d_ECParameters(ec_key, &pstr->data);
+        if (pstr->length <= 0) {
+            ASN1_STRING_free(pstr);
+            ECerr(EC_F_ECKEY_PARAM2TYPE, ERR_R_EC_LIB);
+            return 0;
+        }
+        *ppval = pstr;
+        *pptype = V_ASN1_SEQUENCE;
+    }
+    return 1;
+}
+
+static int eckey_pub_encode(X509_PUBKEY *pk, const EVP_PKEY *pkey)
+{
+    EC_KEY *ec_key = pkey->pkey.ec;
+    void *pval = NULL;
+    int ptype;
+    unsigned char *penc = NULL, *p;
+    int penclen;
+
+    if (!eckey_param2type(&ptype, &pval, ec_key)) {
+        ECerr(EC_F_ECKEY_PUB_ENCODE, ERR_R_EC_LIB);
+        return 0;
+    }
+    penclen = i2o_ECPublicKey(ec_key, NULL);
+    if (penclen <= 0)
+        goto err;
+    penc = OPENSSL_malloc(penclen);
+    if (penc == NULL)
+        goto err;
+    p = penc;
+    penclen = i2o_ECPublicKey(ec_key, &p);
+    if (penclen <= 0)
+        goto err;
+    if (X509_PUBKEY_set0_param(pk, OBJ_nid2obj(EVP_PKEY_EC),
+                               ptype, pval, penc, penclen))
+        return 1;
+ err:
+    if (ptype == V_ASN1_OBJECT)
+        ASN1_OBJECT_free(pval);
+    else
+        ASN1_STRING_free(pval);
+    OPENSSL_free(penc);
+    return 0;
+}
+
+static EC_KEY *eckey_type2param(int ptype, const void *pval)
+{
+    EC_KEY *eckey = NULL;
+    if (ptype == V_ASN1_SEQUENCE) {
+        const ASN1_STRING *pstr = pval;
+        const unsigned char *pm = NULL;
+        int pmlen;
+        pm = pstr->data;
+        pmlen = pstr->length;
+        if ((eckey = d2i_ECParameters(NULL, &pm, pmlen)) == NULL) {
+            ECerr(EC_F_ECKEY_TYPE2PARAM, EC_R_DECODE_ERROR);
+            goto ecerr;
+        }
+    } else if (ptype == V_ASN1_OBJECT) {
+        const ASN1_OBJECT *poid = pval;
+        EC_GROUP *group;
+
+        /*
+         * type == V_ASN1_OBJECT => the parameters are given by an asn1 OID
+         */
+        if ((eckey = EC_KEY_new()) == NULL) {
+            ECerr(EC_F_ECKEY_TYPE2PARAM, ERR_R_MALLOC_FAILURE);
+            goto ecerr;
+        }
+        group = EC_GROUP_new_by_curve_name(OBJ_obj2nid(poid));
+        if (group == NULL)
+            goto ecerr;
+        EC_GROUP_set_asn1_flag(group, OPENSSL_EC_NAMED_CURVE);
+        if (EC_KEY_set_group(eckey, group) == 0)
+            goto ecerr;
+        EC_GROUP_free(group);
+    } else {
+        ECerr(EC_F_ECKEY_TYPE2PARAM, EC_R_DECODE_ERROR);
+        goto ecerr;
+    }
+
+    return eckey;
+
+ ecerr:
+    EC_KEY_free(eckey);
+    return NULL;
+}
+
+static int eckey_pub_decode(EVP_PKEY *pkey, X509_PUBKEY *pubkey)
+{
+    const unsigned char *p = NULL;
+    const void *pval;
+    int ptype, pklen;
+    EC_KEY *eckey = NULL;
+    X509_ALGOR *palg;
+
+    if (!X509_PUBKEY_get0_param(NULL, &p, &pklen, &palg, pubkey))
+        return 0;
+    X509_ALGOR_get0(NULL, &ptype, &pval, palg);
+
+    eckey = eckey_type2param(ptype, pval);
+
+    if (!eckey) {
+        ECerr(EC_F_ECKEY_PUB_DECODE, ERR_R_EC_LIB);
+        return 0;
+    }
+
+    /* We have parameters now set public key */
+    if (!o2i_ECPublicKey(&eckey, &p, pklen)) {
+        ECerr(EC_F_ECKEY_PUB_DECODE, EC_R_DECODE_ERROR);
+        goto ecerr;
+    }
+
+    EVP_PKEY_assign_EC_KEY(pkey, eckey);
+    return 1;
+
+ ecerr:
+    EC_KEY_free(eckey);
+    return 0;
+}
+
+static int eckey_pub_cmp(const EVP_PKEY *a, const EVP_PKEY *b)
+{
+    int r;
+    const EC_GROUP *group = EC_KEY_get0_group(b->pkey.ec);
+    const EC_POINT *pa = EC_KEY_get0_public_key(a->pkey.ec),
+        *pb = EC_KEY_get0_public_key(b->pkey.ec);
+    if (group == NULL || pa == NULL || pb == NULL)
+        return -2;
+    r = EC_POINT_cmp(group, pa, pb, NULL);
+    if (r == 0)
+        return 1;
+    if (r == 1)
+        return 0;
+    return -2;
+}
+
+static int eckey_priv_decode(EVP_PKEY *pkey, const PKCS8_PRIV_KEY_INFO *p8)
+{
+    const unsigned char *p = NULL;
+    const void *pval;
+    int ptype, pklen;
+    EC_KEY *eckey = NULL;
+    const X509_ALGOR *palg;
+
+    if (!PKCS8_pkey_get0(NULL, &p, &pklen, &palg, p8))
+        return 0;
+    X509_ALGOR_get0(NULL, &ptype, &pval, palg);
+
+    eckey = eckey_type2param(ptype, pval);
+
+    if (!eckey)
+        goto ecliberr;
+
+    /* We have parameters now set private key */
+    if (!d2i_ECPrivateKey(&eckey, &p, pklen)) {
+        ECerr(EC_F_ECKEY_PRIV_DECODE, EC_R_DECODE_ERROR);
+        goto ecerr;
+    }
+
+    EVP_PKEY_assign_EC_KEY(pkey, eckey);
+    return 1;
+
+ ecliberr:
+    ECerr(EC_F_ECKEY_PRIV_DECODE, ERR_R_EC_LIB);
+ ecerr:
+    EC_KEY_free(eckey);
+    return 0;
+}
+
+static int eckey_priv_encode(PKCS8_PRIV_KEY_INFO *p8, const EVP_PKEY *pkey)
+{
+    EC_KEY ec_key = *(pkey->pkey.ec);
+    unsigned char *ep, *p;
+    int eplen, ptype;
+    void *pval;
+    unsigned int old_flags;
+
+    if (!eckey_param2type(&ptype, &pval, &ec_key)) {
+        ECerr(EC_F_ECKEY_PRIV_ENCODE, EC_R_DECODE_ERROR);
+        return 0;
+    }
+
+    /* set the private key */
+
+    /*
+     * do not include the parameters in the SEC1 private key see PKCS#11
+     * 12.11
+     */
+    old_flags = EC_KEY_get_enc_flags(&ec_key);
+    EC_KEY_set_enc_flags(&ec_key, old_flags | EC_PKEY_NO_PARAMETERS);
+
+    eplen = i2d_ECPrivateKey(&ec_key, NULL);
+    if (!eplen) {
+        ECerr(EC_F_ECKEY_PRIV_ENCODE, ERR_R_EC_LIB);
+        return 0;
+    }
+    ep = OPENSSL_malloc(eplen);
+    if (ep == NULL) {
+        ECerr(EC_F_ECKEY_PRIV_ENCODE, ERR_R_MALLOC_FAILURE);
+        return 0;
+    }
+    p = ep;
+    if (!i2d_ECPrivateKey(&ec_key, &p)) {
+        OPENSSL_free(ep);
+        ECerr(EC_F_ECKEY_PRIV_ENCODE, ERR_R_EC_LIB);
+        return 0;
+    }
+
+    if (!PKCS8_pkey_set0(p8, OBJ_nid2obj(NID_X9_62_id_ecPublicKey), 0,
+                         ptype, pval, ep, eplen))
+        return 0;
+
+    return 1;
+}
+
+static int int_ec_size(const EVP_PKEY *pkey)
+{
+    return ECDSA_size(pkey->pkey.ec);
+}
+
+static int ec_bits(const EVP_PKEY *pkey)
+{
+    return EC_GROUP_order_bits(EC_KEY_get0_group(pkey->pkey.ec));
+}
+
+static int ec_security_bits(const EVP_PKEY *pkey)
+{
+    int ecbits = ec_bits(pkey);
+    if (ecbits >= 512)
+        return 256;
+    if (ecbits >= 384)
+        return 192;
+    if (ecbits >= 256)
+        return 128;
+    if (ecbits >= 224)
+        return 112;
+    if (ecbits >= 160)
+        return 80;
+    return ecbits / 2;
+}
+
+static int ec_missing_parameters(const EVP_PKEY *pkey)
+{
+    if (pkey->pkey.ec == NULL || EC_KEY_get0_group(pkey->pkey.ec) == NULL)
+        return 1;
+    return 0;
+}
+
+static int ec_copy_parameters(EVP_PKEY *to, const EVP_PKEY *from)
+{
+    EC_GROUP *group = EC_GROUP_dup(EC_KEY_get0_group(from->pkey.ec));
+    if (group == NULL)
+        return 0;
+    if (to->pkey.ec == NULL) {
+        to->pkey.ec = EC_KEY_new();
+        if (to->pkey.ec == NULL)
+            return 0;
+    }
+    if (EC_KEY_set_group(to->pkey.ec, group) == 0)
+        return 0;
+    EC_GROUP_free(group);
+    return 1;
+}
+
+static int ec_cmp_parameters(const EVP_PKEY *a, const EVP_PKEY *b)
+{
+    const EC_GROUP *group_a = EC_KEY_get0_group(a->pkey.ec),
+        *group_b = EC_KEY_get0_group(b->pkey.ec);
+    if (group_a == NULL || group_b == NULL)
+        return -2;
+    if (EC_GROUP_cmp(group_a, group_b, NULL))
+        return 0;
+    else
+        return 1;
+}
+
+static void int_ec_free(EVP_PKEY *pkey)
+{
+    EC_KEY_free(pkey->pkey.ec);
+}
+
+typedef enum {
+    EC_KEY_PRINT_PRIVATE,
+    EC_KEY_PRINT_PUBLIC,
+    EC_KEY_PRINT_PARAM
+} ec_print_t;
+
+static int do_EC_KEY_print(BIO *bp, const EC_KEY *x, int off, ec_print_t ktype)
+{
+    const char *ecstr;
+    unsigned char *priv = NULL, *pub = NULL;
+    size_t privlen = 0, publen = 0;
+    int ret = 0;
+    const EC_GROUP *group;
+
+    if (x == NULL || (group = EC_KEY_get0_group(x)) == NULL) {
+        ECerr(EC_F_DO_EC_KEY_PRINT, ERR_R_PASSED_NULL_PARAMETER);
+        return 0;
+    }
+
+    if (ktype != EC_KEY_PRINT_PARAM && EC_KEY_get0_public_key(x) != NULL) {
+        publen = EC_KEY_key2buf(x, EC_KEY_get_conv_form(x), &pub, NULL);
+        if (publen == 0)
+            goto err;
+    }
+
+    if (ktype == EC_KEY_PRINT_PRIVATE && EC_KEY_get0_private_key(x) != NULL) {
+        privlen = EC_KEY_priv2buf(x, &priv);
+        if (privlen == 0)
+            goto err;
+    }
+
+    if (ktype == EC_KEY_PRINT_PRIVATE)
+        ecstr = "Private-Key";
+    else if (ktype == EC_KEY_PRINT_PUBLIC)
+        ecstr = "Public-Key";
+    else
+        ecstr = "ECDSA-Parameters";
+
+    if (!BIO_indent(bp, off, 128))
+        goto err;
+    if (BIO_printf(bp, "%s: (%d bit)\n", ecstr,
+                   EC_GROUP_order_bits(group)) <= 0)
+        goto err;
+
+    if (privlen != 0) {
+        if (BIO_printf(bp, "%*spriv:\n", off, "") <= 0)
+            goto err;
+        if (ASN1_buf_print(bp, priv, privlen, off + 4) == 0)
+            goto err;
+    }
+
+    if (publen != 0) {
+        if (BIO_printf(bp, "%*spub:\n", off, "") <= 0)
+            goto err;
+        if (ASN1_buf_print(bp, pub, publen, off + 4) == 0)
+            goto err;
+    }
+
+    if (!ECPKParameters_print(bp, group, off))
+        goto err;
+    ret = 1;
+ err:
+    if (!ret)
+        ECerr(EC_F_DO_EC_KEY_PRINT, ERR_R_EC_LIB);
+    OPENSSL_clear_free(priv, privlen);
+    OPENSSL_free(pub);
+    return ret;
+}
+
+static int eckey_param_decode(EVP_PKEY *pkey,
+                              const unsigned char **pder, int derlen)
+{
+    EC_KEY *eckey;
+
+    if ((eckey = d2i_ECParameters(NULL, pder, derlen)) == NULL) {
+        ECerr(EC_F_ECKEY_PARAM_DECODE, ERR_R_EC_LIB);
+        return 0;
+    }
+    EVP_PKEY_assign_EC_KEY(pkey, eckey);
+    return 1;
+}
+
+static int eckey_param_encode(const EVP_PKEY *pkey, unsigned char **pder)
+{
+    return i2d_ECParameters(pkey->pkey.ec, pder);
+}
+
+static int eckey_param_print(BIO *bp, const EVP_PKEY *pkey, int indent,
+                             ASN1_PCTX *ctx)
+{
+    return do_EC_KEY_print(bp, pkey->pkey.ec, indent, EC_KEY_PRINT_PARAM);
+}
+
+static int eckey_pub_print(BIO *bp, const EVP_PKEY *pkey, int indent,
+                           ASN1_PCTX *ctx)
+{
+    return do_EC_KEY_print(bp, pkey->pkey.ec, indent, EC_KEY_PRINT_PUBLIC);
+}
+
+static int eckey_priv_print(BIO *bp, const EVP_PKEY *pkey, int indent,
+                            ASN1_PCTX *ctx)
+{
+    return do_EC_KEY_print(bp, pkey->pkey.ec, indent, EC_KEY_PRINT_PRIVATE);
+}
+
+static int old_ec_priv_decode(EVP_PKEY *pkey,
+                              const unsigned char **pder, int derlen)
+{
+    EC_KEY *ec;
+
+    if ((ec = d2i_ECPrivateKey(NULL, pder, derlen)) == NULL) {
+        ECerr(EC_F_OLD_EC_PRIV_DECODE, EC_R_DECODE_ERROR);
+        return 0;
+    }
+    EVP_PKEY_assign_EC_KEY(pkey, ec);
+    return 1;
+}
+
+static int old_ec_priv_encode(const EVP_PKEY *pkey, unsigned char **pder)
+{
+    return i2d_ECPrivateKey(pkey->pkey.ec, pder);
+}
+
+static int ec_pkey_ctrl(EVP_PKEY *pkey, int op, long arg1, void *arg2)
+{
+    switch (op) {
+    case ASN1_PKEY_CTRL_PKCS7_SIGN:
+        if (arg1 == 0) {
+            int snid, hnid;
+            X509_ALGOR *alg1, *alg2;
+            PKCS7_SIGNER_INFO_get0_algs(arg2, NULL, &alg1, &alg2);
+            if (alg1 == NULL || alg1->algorithm == NULL)
+                return -1;
+            hnid = OBJ_obj2nid(alg1->algorithm);
+            if (hnid == NID_undef)
+                return -1;
+            if (!OBJ_find_sigid_by_algs(&snid, hnid, EVP_PKEY_id(pkey)))
+                return -1;
+            X509_ALGOR_set0(alg2, OBJ_nid2obj(snid), V_ASN1_UNDEF, 0);
+        }
+        return 1;
+
+#ifndef OPENSSL_NO_SM2
+    case ASN1_PKEY_CTRL_PKCS7_ENCRYPT:
+        if (arg1 == 0) {
+            X509_ALGOR *alg = NULL;
+            PKCS7_RECIP_INFO_get0_alg(arg2, &alg);
+            if (alg) {
+                X509_ALGOR_set0(alg, OBJ_nid2obj(NID_sm2encrypt_with_sm3), V_ASN1_NULL, 0);
+            }
+        }
+        return 1;
+#endif
+
+#ifndef OPENSSL_NO_CMS
+    case ASN1_PKEY_CTRL_CMS_SIGN:
+        if (arg1 == 0) {
+            int snid, hnid;
+            X509_ALGOR *alg1, *alg2;
+            CMS_SignerInfo_get0_algs(arg2, NULL, NULL, &alg1, &alg2);
+            if (alg1 == NULL || alg1->algorithm == NULL)
+                return -1;
+            hnid = OBJ_obj2nid(alg1->algorithm);
+            if (hnid == NID_undef)
+                return -1;
+            if (!OBJ_find_sigid_by_algs(&snid, hnid, EVP_PKEY_id(pkey)))
+                return -1;
+            X509_ALGOR_set0(alg2, OBJ_nid2obj(snid), V_ASN1_UNDEF, 0);
+        }
+        return 1;
+
+    case ASN1_PKEY_CTRL_CMS_ENVELOPE:
+        if (arg1 == 1)
+            return ecdh_cms_decrypt(arg2);
+        else if (arg1 == 0)
+            return ecdh_cms_encrypt(arg2);
+        return -2;
+
+    case ASN1_PKEY_CTRL_CMS_RI_TYPE:
+        *(int *)arg2 = CMS_RECIPINFO_AGREE;
+        return 1;
+#endif
+
+    case ASN1_PKEY_CTRL_DEFAULT_MD_NID:
+#if !defined(OPENSSL_NO_SM2) && !defined(OPENSSL_NO_SM3)
+        if (EC_KEY_is_sm2p256v1(EVP_PKEY_get0_EC_KEY(pkey))) {
+            *(int *)arg2 = NID_sm3;
+            return 2;
+        }
+#endif
+        *(int *)arg2 = NID_sha256;
+        return 2;
+
+    case ASN1_PKEY_CTRL_SET1_TLS_ENCPT:
+        return EC_KEY_oct2key(EVP_PKEY_get0_EC_KEY(pkey), arg2, arg1, NULL);
+
+    case ASN1_PKEY_CTRL_GET1_TLS_ENCPT:
+        return EC_KEY_key2buf(EVP_PKEY_get0_EC_KEY(pkey),
+                              POINT_CONVERSION_UNCOMPRESSED, arg2, NULL);
+
+    default:
+        return -2;
+
+    }
+
+}
+
+const EVP_PKEY_ASN1_METHOD eckey_asn1_meth = {
+    EVP_PKEY_EC,
+    EVP_PKEY_EC,
+    0,
+    "EC",
+    "OpenSSL EC algorithm",
+
+    eckey_pub_decode,
+    eckey_pub_encode,
+    eckey_pub_cmp,
+    eckey_pub_print,
+
+    eckey_priv_decode,
+    eckey_priv_encode,
+    eckey_priv_print,
+
+    int_ec_size,
+    ec_bits,
+    ec_security_bits,
+
+    eckey_param_decode,
+    eckey_param_encode,
+    ec_missing_parameters,
+    ec_copy_parameters,
+    ec_cmp_parameters,
+    eckey_param_print,
+    0,
+
+    int_ec_free,
+    ec_pkey_ctrl,
+    old_ec_priv_decode,
+    old_ec_priv_encode
+};
+
+int EC_KEY_print(BIO *bp, const EC_KEY *x, int off)
+{
+    int private = EC_KEY_get0_private_key(x) != NULL;
+
+    return do_EC_KEY_print(bp, x, off,
+                private ? EC_KEY_PRINT_PRIVATE : EC_KEY_PRINT_PUBLIC);
+}
+
+int ECParameters_print(BIO *bp, const EC_KEY *x)
+{
+    return do_EC_KEY_print(bp, x, 4, EC_KEY_PRINT_PARAM);
+}
+
+#ifndef OPENSSL_NO_CMS
+
+static int ecdh_cms_set_peerkey(EVP_PKEY_CTX *pctx,
+                                X509_ALGOR *alg, ASN1_BIT_STRING *pubkey)
+{
+    const ASN1_OBJECT *aoid;
+    int atype;
+    const void *aval;
+    int rv = 0;
+    EVP_PKEY *pkpeer = NULL;
+    EC_KEY *ecpeer = NULL;
+    const unsigned char *p;
+    int plen;
+    X509_ALGOR_get0(&aoid, &atype, &aval, alg);
+    if (OBJ_obj2nid(aoid) != NID_X9_62_id_ecPublicKey)
+        goto err;
+    /* If absent parameters get group from main key */
+    if (atype == V_ASN1_UNDEF || atype == V_ASN1_NULL) {
+        const EC_GROUP *grp;
+        EVP_PKEY *pk;
+        pk = EVP_PKEY_CTX_get0_pkey(pctx);
+        if (!pk)
+            goto err;
+        grp = EC_KEY_get0_group(pk->pkey.ec);
+        ecpeer = EC_KEY_new();
+        if (ecpeer == NULL)
+            goto err;
+        if (!EC_KEY_set_group(ecpeer, grp))
+            goto err;
+    } else {
+        ecpeer = eckey_type2param(atype, aval);
+        if (!ecpeer)
+            goto err;
+    }
+    /* We have parameters now set public key */
+    plen = ASN1_STRING_length(pubkey);
+    p = ASN1_STRING_get0_data(pubkey);
+    if (!p || !plen)
+        goto err;
+    if (!o2i_ECPublicKey(&ecpeer, &p, plen))
+        goto err;
+    pkpeer = EVP_PKEY_new();
+    if (pkpeer == NULL)
+        goto err;
+    EVP_PKEY_set1_EC_KEY(pkpeer, ecpeer);
+    if (EVP_PKEY_derive_set_peer(pctx, pkpeer) > 0)
+        rv = 1;
+ err:
+    EC_KEY_free(ecpeer);
+    EVP_PKEY_free(pkpeer);
+    return rv;
+}
+
+/* Set KDF parameters based on KDF NID */
+static int ecdh_cms_set_kdf_param(EVP_PKEY_CTX *pctx, int eckdf_nid)
+{
+    int kdf_nid, kdfmd_nid, cofactor;
+    const EVP_MD *kdf_md;
+    if (eckdf_nid == NID_undef)
+        return 0;
+
+    /* Lookup KDF type, cofactor mode and digest */
+    if (!OBJ_find_sigid_algs(eckdf_nid, &kdfmd_nid, &kdf_nid))
+        return 0;
+
+    if (kdf_nid == NID_dh_std_kdf)
+        cofactor = 0;
+    else if (kdf_nid == NID_dh_cofactor_kdf)
+        cofactor = 1;
+    else
+        return 0;
+
+    if (EVP_PKEY_CTX_set_ecdh_cofactor_mode(pctx, cofactor) <= 0)
+        return 0;
+
+    if (EVP_PKEY_CTX_set_ecdh_kdf_type(pctx, EVP_PKEY_ECDH_KDF_X9_62) <= 0)
+        return 0;
+
+    kdf_md = EVP_get_digestbynid(kdfmd_nid);
+    if (!kdf_md)
+        return 0;
+
+    if (EVP_PKEY_CTX_set_ecdh_kdf_md(pctx, kdf_md) <= 0)
+        return 0;
+    return 1;
+}
+
+static int ecdh_cms_set_shared_info(EVP_PKEY_CTX *pctx, CMS_RecipientInfo *ri)
+{
+    int rv = 0;
+
+    X509_ALGOR *alg, *kekalg = NULL;
+    ASN1_OCTET_STRING *ukm;
+    const unsigned char *p;
+    unsigned char *der = NULL;
+    int plen, keylen;
+    const EVP_CIPHER *kekcipher;
+    EVP_CIPHER_CTX *kekctx;
+
+    if (!CMS_RecipientInfo_kari_get0_alg(ri, &alg, &ukm))
+        return 0;
+
+    if (!ecdh_cms_set_kdf_param(pctx, OBJ_obj2nid(alg->algorithm))) {
+        ECerr(EC_F_ECDH_CMS_SET_SHARED_INFO, EC_R_KDF_PARAMETER_ERROR);
+        return 0;
+    }
+
+    if (alg->parameter->type != V_ASN1_SEQUENCE)
+        return 0;
+
+    p = alg->parameter->value.sequence->data;
+    plen = alg->parameter->value.sequence->length;
+    kekalg = d2i_X509_ALGOR(NULL, &p, plen);
+    if (!kekalg)
+        goto err;
+    kekctx = CMS_RecipientInfo_kari_get0_ctx(ri);
+    if (!kekctx)
+        goto err;
+    kekcipher = EVP_get_cipherbyobj(kekalg->algorithm);
+    if (!kekcipher || EVP_CIPHER_mode(kekcipher) != EVP_CIPH_WRAP_MODE)
+        goto err;
+    if (!EVP_EncryptInit_ex(kekctx, kekcipher, NULL, NULL, NULL))
+        goto err;
+    if (EVP_CIPHER_asn1_to_param(kekctx, kekalg->parameter) <= 0)
+        goto err;
+
+    keylen = EVP_CIPHER_CTX_key_length(kekctx);
+    if (EVP_PKEY_CTX_set_ecdh_kdf_outlen(pctx, keylen) <= 0)
+        goto err;
+
+    plen = CMS_SharedInfo_encode(&der, kekalg, ukm, keylen);
+
+    if (!plen)
+        goto err;
+
+    if (EVP_PKEY_CTX_set0_ecdh_kdf_ukm(pctx, der, plen) <= 0)
+        goto err;
+    der = NULL;
+
+    rv = 1;
+ err:
+    X509_ALGOR_free(kekalg);
+    OPENSSL_free(der);
+    return rv;
+}
+
+static int ecdh_cms_decrypt(CMS_RecipientInfo *ri)
+{
+    EVP_PKEY_CTX *pctx;
+    pctx = CMS_RecipientInfo_get0_pkey_ctx(ri);
+    if (!pctx)
+        return 0;
+    /* See if we need to set peer key */
+    if (!EVP_PKEY_CTX_get0_peerkey(pctx)) {
+        X509_ALGOR *alg;
+        ASN1_BIT_STRING *pubkey;
+        if (!CMS_RecipientInfo_kari_get0_orig_id(ri, &alg, &pubkey,
+                                                 NULL, NULL, NULL))
+            return 0;
+        if (!alg || !pubkey)
+            return 0;
+        if (!ecdh_cms_set_peerkey(pctx, alg, pubkey)) {
+            ECerr(EC_F_ECDH_CMS_DECRYPT, EC_R_PEER_KEY_ERROR);
+            return 0;
+        }
+    }
+    /* Set ECDH derivation parameters and initialise unwrap context */
+    if (!ecdh_cms_set_shared_info(pctx, ri)) {
+        ECerr(EC_F_ECDH_CMS_DECRYPT, EC_R_SHARED_INFO_ERROR);
+        return 0;
+    }
+    return 1;
+}
+
+static int ecdh_cms_encrypt(CMS_RecipientInfo *ri)
+{
+    EVP_PKEY_CTX *pctx;
+    EVP_PKEY *pkey;
+    EVP_CIPHER_CTX *ctx;
+    int keylen;
+    X509_ALGOR *talg, *wrap_alg = NULL;
+    const ASN1_OBJECT *aoid;
+    ASN1_BIT_STRING *pubkey;
+    ASN1_STRING *wrap_str;
+    ASN1_OCTET_STRING *ukm;
+    unsigned char *penc = NULL;
+    int penclen;
+    int rv = 0;
+    int ecdh_nid, kdf_type, kdf_nid, wrap_nid;
+    const EVP_MD *kdf_md;
+    pctx = CMS_RecipientInfo_get0_pkey_ctx(ri);
+    if (!pctx)
+        return 0;
+    /* Get ephemeral key */
+    pkey = EVP_PKEY_CTX_get0_pkey(pctx);
+    if (!CMS_RecipientInfo_kari_get0_orig_id(ri, &talg, &pubkey,
+                                             NULL, NULL, NULL))
+        goto err;
+    X509_ALGOR_get0(&aoid, NULL, NULL, talg);
+    /* Is everything uninitialised? */
+    if (aoid == OBJ_nid2obj(NID_undef)) {
+
+        EC_KEY *eckey = pkey->pkey.ec;
+        /* Set the key */
+        unsigned char *p;
+
+        penclen = i2o_ECPublicKey(eckey, NULL);
+        if (penclen <= 0)
+            goto err;
+        penc = OPENSSL_malloc(penclen);
+        if (penc == NULL)
+            goto err;
+        p = penc;
+        penclen = i2o_ECPublicKey(eckey, &p);
+        if (penclen <= 0)
+            goto err;
+        ASN1_STRING_set0(pubkey, penc, penclen);
+        pubkey->flags &= ~(ASN1_STRING_FLAG_BITS_LEFT | 0x07);
+        pubkey->flags |= ASN1_STRING_FLAG_BITS_LEFT;
+
+        penc = NULL;
+        X509_ALGOR_set0(talg, OBJ_nid2obj(NID_X9_62_id_ecPublicKey),
+                        V_ASN1_UNDEF, NULL);
+    }
+
+    /* See if custom parameters set */
+    kdf_type = EVP_PKEY_CTX_get_ecdh_kdf_type(pctx);
+    if (kdf_type <= 0)
+        goto err;
+    if (!EVP_PKEY_CTX_get_ecdh_kdf_md(pctx, &kdf_md))
+        goto err;
+    ecdh_nid = EVP_PKEY_CTX_get_ecdh_cofactor_mode(pctx);
+    if (ecdh_nid < 0)
+        goto err;
+    else if (ecdh_nid == 0)
+        ecdh_nid = NID_dh_std_kdf;
+    else if (ecdh_nid == 1)
+        ecdh_nid = NID_dh_cofactor_kdf;
+
+    if (kdf_type == EVP_PKEY_ECDH_KDF_NONE) {
+        kdf_type = EVP_PKEY_ECDH_KDF_X9_62;
+        if (EVP_PKEY_CTX_set_ecdh_kdf_type(pctx, kdf_type) <= 0)
+            goto err;
+    } else
+        /* Unknown KDF */
+        goto err;
+    if (kdf_md == NULL) {
+        /* Fixme later for better MD */
+        kdf_md = EVP_get_default_digest();
+        if (EVP_PKEY_CTX_set_ecdh_kdf_md(pctx, kdf_md) <= 0)
+            goto err;
+    }
+
+    if (!CMS_RecipientInfo_kari_get0_alg(ri, &talg, &ukm))
+        goto err;
+
+    /* Lookup NID for KDF+cofactor+digest */
+
+    if (!OBJ_find_sigid_by_algs(&kdf_nid, EVP_MD_type(kdf_md), ecdh_nid))
+        goto err;
+    /* Get wrap NID */
+    ctx = CMS_RecipientInfo_kari_get0_ctx(ri);
+    wrap_nid = EVP_CIPHER_CTX_type(ctx);
+    keylen = EVP_CIPHER_CTX_key_length(ctx);
+
+    /* Package wrap algorithm in an AlgorithmIdentifier */
+
+    wrap_alg = X509_ALGOR_new();
+    if (wrap_alg == NULL)
+        goto err;
+    wrap_alg->algorithm = OBJ_nid2obj(wrap_nid);
+    wrap_alg->parameter = ASN1_TYPE_new();
+    if (wrap_alg->parameter == NULL)
+        goto err;
+    if (EVP_CIPHER_param_to_asn1(ctx, wrap_alg->parameter) <= 0)
+        goto err;
+    if (ASN1_TYPE_get(wrap_alg->parameter) == NID_undef) {
+        ASN1_TYPE_free(wrap_alg->parameter);
+        wrap_alg->parameter = NULL;
+    }
+
+    if (EVP_PKEY_CTX_set_ecdh_kdf_outlen(pctx, keylen) <= 0)
+        goto err;
+
+    penclen = CMS_SharedInfo_encode(&penc, wrap_alg, ukm, keylen);
+
+    if (!penclen)
+        goto err;
+
+    if (EVP_PKEY_CTX_set0_ecdh_kdf_ukm(pctx, penc, penclen) <= 0)
+        goto err;
+    penc = NULL;
+
+    /*
+     * Now need to wrap encoding of wrap AlgorithmIdentifier into parameter
+     * of another AlgorithmIdentifier.
+     */
+    penclen = i2d_X509_ALGOR(wrap_alg, &penc);
+    if (!penc || !penclen)
+        goto err;
+    wrap_str = ASN1_STRING_new();
+    if (wrap_str == NULL)
+        goto err;
+    ASN1_STRING_set0(wrap_str, penc, penclen);
+    penc = NULL;
+    X509_ALGOR_set0(talg, OBJ_nid2obj(kdf_nid), V_ASN1_SEQUENCE, wrap_str);
+
+    rv = 1;
+
+ err:
+    OPENSSL_free(penc);
+    X509_ALGOR_free(wrap_alg);
+    return rv;
+}
+
+#endif
diff --git a/crypto/ec/ec_asn1.c b/crypto/ec/ec_asn1.c
new file mode 100644
index 000000000..5d2c4cf42
--- /dev/null
+++ b/crypto/ec/ec_asn1.c
@@ -0,0 +1,1246 @@
+/*
+ * Copyright 2002-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "ec_lcl.h"
+#include 
+#include 
+#include 
+
+int EC_GROUP_get_basis_type(const EC_GROUP *group)
+{
+    int i = 0;
+
+    if (EC_METHOD_get_field_type(EC_GROUP_method_of(group)) !=
+        NID_X9_62_characteristic_two_field)
+        /* everything else is currently not supported */
+        return 0;
+
+    while (group->poly[i] != 0)
+        i++;
+
+    if (i == 4)
+        return NID_X9_62_ppBasis;
+    else if (i == 2)
+        return NID_X9_62_tpBasis;
+    else
+        /* everything else is currently not supported */
+        return 0;
+}
+
+#ifndef OPENSSL_NO_EC2M
+int EC_GROUP_get_trinomial_basis(const EC_GROUP *group, unsigned int *k)
+{
+    if (group == NULL)
+        return 0;
+
+    if (EC_METHOD_get_field_type(EC_GROUP_method_of(group)) !=
+        NID_X9_62_characteristic_two_field
+        || !((group->poly[0] != 0) && (group->poly[1] != 0)
+             && (group->poly[2] == 0))) {
+        ECerr(EC_F_EC_GROUP_GET_TRINOMIAL_BASIS,
+              ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
+        return 0;
+    }
+
+    if (k)
+        *k = group->poly[1];
+
+    return 1;
+}
+
+int EC_GROUP_get_pentanomial_basis(const EC_GROUP *group, unsigned int *k1,
+                                   unsigned int *k2, unsigned int *k3)
+{
+    if (group == NULL)
+        return 0;
+
+    if (EC_METHOD_get_field_type(EC_GROUP_method_of(group)) !=
+        NID_X9_62_characteristic_two_field
+        || !((group->poly[0] != 0) && (group->poly[1] != 0)
+             && (group->poly[2] != 0) && (group->poly[3] != 0)
+             && (group->poly[4] == 0))) {
+        ECerr(EC_F_EC_GROUP_GET_PENTANOMIAL_BASIS,
+              ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
+        return 0;
+    }
+
+    if (k1)
+        *k1 = group->poly[3];
+    if (k2)
+        *k2 = group->poly[2];
+    if (k3)
+        *k3 = group->poly[1];
+
+    return 1;
+}
+#endif
+
+/* some structures needed for the asn1 encoding */
+typedef struct x9_62_pentanomial_st {
+    long k1;
+    long k2;
+    long k3;
+} X9_62_PENTANOMIAL;
+
+typedef struct x9_62_characteristic_two_st {
+    long m;
+    ASN1_OBJECT *type;
+    union {
+        char *ptr;
+        /* NID_X9_62_onBasis */
+        ASN1_NULL *onBasis;
+        /* NID_X9_62_tpBasis */
+        ASN1_INTEGER *tpBasis;
+        /* NID_X9_62_ppBasis */
+        X9_62_PENTANOMIAL *ppBasis;
+        /* anything else */
+        ASN1_TYPE *other;
+    } p;
+} X9_62_CHARACTERISTIC_TWO;
+
+typedef struct x9_62_fieldid_st {
+    ASN1_OBJECT *fieldType;
+    union {
+        char *ptr;
+        /* NID_X9_62_prime_field */
+        ASN1_INTEGER *prime;
+        /* NID_X9_62_characteristic_two_field */
+        X9_62_CHARACTERISTIC_TWO *char_two;
+        /* anything else */
+        ASN1_TYPE *other;
+    } p;
+} X9_62_FIELDID;
+
+typedef struct x9_62_curve_st {
+    ASN1_OCTET_STRING *a;
+    ASN1_OCTET_STRING *b;
+    ASN1_BIT_STRING *seed;
+} X9_62_CURVE;
+
+struct ec_parameters_st {
+    long version;
+    X9_62_FIELDID *fieldID;
+    X9_62_CURVE *curve;
+    ASN1_OCTET_STRING *base;
+    ASN1_INTEGER *order;
+    ASN1_INTEGER *cofactor;
+} /* ECPARAMETERS */ ;
+
+struct ecpk_parameters_st {
+    int type;
+    union {
+        ASN1_OBJECT *named_curve;
+        ECPARAMETERS *parameters;
+        ASN1_NULL *implicitlyCA;
+    } value;
+} /* ECPKPARAMETERS */ ;
+
+/* SEC1 ECPrivateKey */
+typedef struct ec_privatekey_st {
+    long version;
+    ASN1_OCTET_STRING *privateKey;
+    ECPKPARAMETERS *parameters;
+    ASN1_BIT_STRING *publicKey;
+} EC_PRIVATEKEY;
+
+/* the OpenSSL ASN.1 definitions */
+ASN1_SEQUENCE(X9_62_PENTANOMIAL) = {
+        ASN1_SIMPLE(X9_62_PENTANOMIAL, k1, LONG),
+        ASN1_SIMPLE(X9_62_PENTANOMIAL, k2, LONG),
+        ASN1_SIMPLE(X9_62_PENTANOMIAL, k3, LONG)
+} static_ASN1_SEQUENCE_END(X9_62_PENTANOMIAL)
+
+DECLARE_ASN1_ALLOC_FUNCTIONS(X9_62_PENTANOMIAL)
+IMPLEMENT_ASN1_ALLOC_FUNCTIONS(X9_62_PENTANOMIAL)
+
+ASN1_ADB_TEMPLATE(char_two_def) = ASN1_SIMPLE(X9_62_CHARACTERISTIC_TWO, p.other, ASN1_ANY);
+
+ASN1_ADB(X9_62_CHARACTERISTIC_TWO) = {
+        ADB_ENTRY(NID_X9_62_onBasis, ASN1_SIMPLE(X9_62_CHARACTERISTIC_TWO, p.onBasis, ASN1_NULL)),
+        ADB_ENTRY(NID_X9_62_tpBasis, ASN1_SIMPLE(X9_62_CHARACTERISTIC_TWO, p.tpBasis, ASN1_INTEGER)),
+        ADB_ENTRY(NID_X9_62_ppBasis, ASN1_SIMPLE(X9_62_CHARACTERISTIC_TWO, p.ppBasis, X9_62_PENTANOMIAL))
+} ASN1_ADB_END(X9_62_CHARACTERISTIC_TWO, 0, type, 0, &char_two_def_tt, NULL);
+
+ASN1_SEQUENCE(X9_62_CHARACTERISTIC_TWO) = {
+        ASN1_SIMPLE(X9_62_CHARACTERISTIC_TWO, m, LONG),
+        ASN1_SIMPLE(X9_62_CHARACTERISTIC_TWO, type, ASN1_OBJECT),
+        ASN1_ADB_OBJECT(X9_62_CHARACTERISTIC_TWO)
+} static_ASN1_SEQUENCE_END(X9_62_CHARACTERISTIC_TWO)
+
+DECLARE_ASN1_ALLOC_FUNCTIONS(X9_62_CHARACTERISTIC_TWO)
+IMPLEMENT_ASN1_ALLOC_FUNCTIONS(X9_62_CHARACTERISTIC_TWO)
+
+ASN1_ADB_TEMPLATE(fieldID_def) = ASN1_SIMPLE(X9_62_FIELDID, p.other, ASN1_ANY);
+
+ASN1_ADB(X9_62_FIELDID) = {
+        ADB_ENTRY(NID_X9_62_prime_field, ASN1_SIMPLE(X9_62_FIELDID, p.prime, ASN1_INTEGER)),
+        ADB_ENTRY(NID_X9_62_characteristic_two_field, ASN1_SIMPLE(X9_62_FIELDID, p.char_two, X9_62_CHARACTERISTIC_TWO))
+} ASN1_ADB_END(X9_62_FIELDID, 0, fieldType, 0, &fieldID_def_tt, NULL);
+
+ASN1_SEQUENCE(X9_62_FIELDID) = {
+        ASN1_SIMPLE(X9_62_FIELDID, fieldType, ASN1_OBJECT),
+        ASN1_ADB_OBJECT(X9_62_FIELDID)
+} static_ASN1_SEQUENCE_END(X9_62_FIELDID)
+
+ASN1_SEQUENCE(X9_62_CURVE) = {
+        ASN1_SIMPLE(X9_62_CURVE, a, ASN1_OCTET_STRING),
+        ASN1_SIMPLE(X9_62_CURVE, b, ASN1_OCTET_STRING),
+        ASN1_OPT(X9_62_CURVE, seed, ASN1_BIT_STRING)
+} static_ASN1_SEQUENCE_END(X9_62_CURVE)
+
+ASN1_SEQUENCE(ECPARAMETERS) = {
+        ASN1_SIMPLE(ECPARAMETERS, version, LONG),
+        ASN1_SIMPLE(ECPARAMETERS, fieldID, X9_62_FIELDID),
+        ASN1_SIMPLE(ECPARAMETERS, curve, X9_62_CURVE),
+        ASN1_SIMPLE(ECPARAMETERS, base, ASN1_OCTET_STRING),
+        ASN1_SIMPLE(ECPARAMETERS, order, ASN1_INTEGER),
+        ASN1_OPT(ECPARAMETERS, cofactor, ASN1_INTEGER)
+} ASN1_SEQUENCE_END(ECPARAMETERS)
+
+DECLARE_ASN1_ALLOC_FUNCTIONS(ECPARAMETERS)
+IMPLEMENT_ASN1_ALLOC_FUNCTIONS(ECPARAMETERS)
+
+ASN1_CHOICE(ECPKPARAMETERS) = {
+        ASN1_SIMPLE(ECPKPARAMETERS, value.named_curve, ASN1_OBJECT),
+        ASN1_SIMPLE(ECPKPARAMETERS, value.parameters, ECPARAMETERS),
+        ASN1_SIMPLE(ECPKPARAMETERS, value.implicitlyCA, ASN1_NULL)
+} ASN1_CHOICE_END(ECPKPARAMETERS)
+
+DECLARE_ASN1_FUNCTIONS_const(ECPKPARAMETERS)
+DECLARE_ASN1_ENCODE_FUNCTIONS_const(ECPKPARAMETERS, ECPKPARAMETERS)
+IMPLEMENT_ASN1_FUNCTIONS_const(ECPKPARAMETERS)
+
+ASN1_SEQUENCE(EC_PRIVATEKEY) = {
+        ASN1_SIMPLE(EC_PRIVATEKEY, version, LONG),
+        ASN1_SIMPLE(EC_PRIVATEKEY, privateKey, ASN1_OCTET_STRING),
+        ASN1_EXP_OPT(EC_PRIVATEKEY, parameters, ECPKPARAMETERS, 0),
+        ASN1_EXP_OPT(EC_PRIVATEKEY, publicKey, ASN1_BIT_STRING, 1)
+} static_ASN1_SEQUENCE_END(EC_PRIVATEKEY)
+
+DECLARE_ASN1_FUNCTIONS_const(EC_PRIVATEKEY)
+DECLARE_ASN1_ENCODE_FUNCTIONS_const(EC_PRIVATEKEY, EC_PRIVATEKEY)
+IMPLEMENT_ASN1_FUNCTIONS_const(EC_PRIVATEKEY)
+
+/* some declarations of internal function */
+
+/* ec_asn1_group2field() sets the values in a X9_62_FIELDID object */
+static int ec_asn1_group2fieldid(const EC_GROUP *, X9_62_FIELDID *);
+/* ec_asn1_group2curve() sets the values in a X9_62_CURVE object */
+static int ec_asn1_group2curve(const EC_GROUP *, X9_62_CURVE *);
+
+/* the function definitions */
+
+static int ec_asn1_group2fieldid(const EC_GROUP *group, X9_62_FIELDID *field)
+{
+    int ok = 0, nid;
+    BIGNUM *tmp = NULL;
+
+    if (group == NULL || field == NULL)
+        return 0;
+
+    /* clear the old values (if necessary) */
+    ASN1_OBJECT_free(field->fieldType);
+    ASN1_TYPE_free(field->p.other);
+
+    nid = EC_METHOD_get_field_type(EC_GROUP_method_of(group));
+    /* set OID for the field */
+    if ((field->fieldType = OBJ_nid2obj(nid)) == NULL) {
+        ECerr(EC_F_EC_ASN1_GROUP2FIELDID, ERR_R_OBJ_LIB);
+        goto err;
+    }
+
+    if (nid == NID_X9_62_prime_field) {
+        if ((tmp = BN_new()) == NULL) {
+            ECerr(EC_F_EC_ASN1_GROUP2FIELDID, ERR_R_MALLOC_FAILURE);
+            goto err;
+        }
+        /* the parameters are specified by the prime number p */
+        if (!EC_GROUP_get_curve_GFp(group, tmp, NULL, NULL, NULL)) {
+            ECerr(EC_F_EC_ASN1_GROUP2FIELDID, ERR_R_EC_LIB);
+            goto err;
+        }
+        /* set the prime number */
+        field->p.prime = BN_to_ASN1_INTEGER(tmp, NULL);
+        if (field->p.prime == NULL) {
+            ECerr(EC_F_EC_ASN1_GROUP2FIELDID, ERR_R_ASN1_LIB);
+            goto err;
+        }
+    } else if (nid == NID_X9_62_characteristic_two_field)
+#ifdef OPENSSL_NO_EC2M
+    {
+        ECerr(EC_F_EC_ASN1_GROUP2FIELDID, EC_R_GF2M_NOT_SUPPORTED);
+        goto err;
+    }
+#else
+    {
+        int field_type;
+        X9_62_CHARACTERISTIC_TWO *char_two;
+
+        field->p.char_two = X9_62_CHARACTERISTIC_TWO_new();
+        char_two = field->p.char_two;
+
+        if (char_two == NULL) {
+            ECerr(EC_F_EC_ASN1_GROUP2FIELDID, ERR_R_MALLOC_FAILURE);
+            goto err;
+        }
+
+        char_two->m = (long)EC_GROUP_get_degree(group);
+
+        field_type = EC_GROUP_get_basis_type(group);
+
+        if (field_type == 0) {
+            ECerr(EC_F_EC_ASN1_GROUP2FIELDID, ERR_R_EC_LIB);
+            goto err;
+        }
+        /* set base type OID */
+        if ((char_two->type = OBJ_nid2obj(field_type)) == NULL) {
+            ECerr(EC_F_EC_ASN1_GROUP2FIELDID, ERR_R_OBJ_LIB);
+            goto err;
+        }
+
+        if (field_type == NID_X9_62_tpBasis) {
+            unsigned int k;
+
+            if (!EC_GROUP_get_trinomial_basis(group, &k))
+                goto err;
+
+            char_two->p.tpBasis = ASN1_INTEGER_new();
+            if (char_two->p.tpBasis == NULL) {
+                ECerr(EC_F_EC_ASN1_GROUP2FIELDID, ERR_R_MALLOC_FAILURE);
+                goto err;
+            }
+            if (!ASN1_INTEGER_set(char_two->p.tpBasis, (long)k)) {
+                ECerr(EC_F_EC_ASN1_GROUP2FIELDID, ERR_R_ASN1_LIB);
+                goto err;
+            }
+        } else if (field_type == NID_X9_62_ppBasis) {
+            unsigned int k1, k2, k3;
+
+            if (!EC_GROUP_get_pentanomial_basis(group, &k1, &k2, &k3))
+                goto err;
+
+            char_two->p.ppBasis = X9_62_PENTANOMIAL_new();
+            if (char_two->p.ppBasis == NULL) {
+                ECerr(EC_F_EC_ASN1_GROUP2FIELDID, ERR_R_MALLOC_FAILURE);
+                goto err;
+            }
+
+            /* set k? values */
+            char_two->p.ppBasis->k1 = (long)k1;
+            char_two->p.ppBasis->k2 = (long)k2;
+            char_two->p.ppBasis->k3 = (long)k3;
+        } else {                /* field_type == NID_X9_62_onBasis */
+
+            /* for ONB the parameters are (asn1) NULL */
+            char_two->p.onBasis = ASN1_NULL_new();
+            if (char_two->p.onBasis == NULL) {
+                ECerr(EC_F_EC_ASN1_GROUP2FIELDID, ERR_R_MALLOC_FAILURE);
+                goto err;
+            }
+        }
+    }
+#endif
+    else {
+        ECerr(EC_F_EC_ASN1_GROUP2FIELDID, EC_R_UNSUPPORTED_FIELD);
+        goto err;
+    }
+
+    ok = 1;
+
+ err:
+    BN_free(tmp);
+    return (ok);
+}
+
+static int ec_asn1_group2curve(const EC_GROUP *group, X9_62_CURVE *curve)
+{
+    int ok = 0, nid;
+    BIGNUM *tmp_1 = NULL, *tmp_2 = NULL;
+    unsigned char *buffer_1 = NULL, *buffer_2 = NULL,
+        *a_buf = NULL, *b_buf = NULL;
+    size_t len_1, len_2;
+    unsigned char char_zero = 0;
+
+    if (!group || !curve || !curve->a || !curve->b)
+        return 0;
+
+    if ((tmp_1 = BN_new()) == NULL || (tmp_2 = BN_new()) == NULL) {
+        ECerr(EC_F_EC_ASN1_GROUP2CURVE, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+
+    nid = EC_METHOD_get_field_type(EC_GROUP_method_of(group));
+
+    /* get a and b */
+    if (nid == NID_X9_62_prime_field) {
+        if (!EC_GROUP_get_curve_GFp(group, NULL, tmp_1, tmp_2, NULL)) {
+            ECerr(EC_F_EC_ASN1_GROUP2CURVE, ERR_R_EC_LIB);
+            goto err;
+        }
+    }
+#ifndef OPENSSL_NO_EC2M
+    else {                      /* nid == NID_X9_62_characteristic_two_field */
+
+        if (!EC_GROUP_get_curve_GF2m(group, NULL, tmp_1, tmp_2, NULL)) {
+            ECerr(EC_F_EC_ASN1_GROUP2CURVE, ERR_R_EC_LIB);
+            goto err;
+        }
+    }
+#endif
+    len_1 = (size_t)BN_num_bytes(tmp_1);
+    len_2 = (size_t)BN_num_bytes(tmp_2);
+
+    if (len_1 == 0) {
+        /* len_1 == 0 => a == 0 */
+        a_buf = &char_zero;
+        len_1 = 1;
+    } else {
+        if ((buffer_1 = OPENSSL_malloc(len_1)) == NULL) {
+            ECerr(EC_F_EC_ASN1_GROUP2CURVE, ERR_R_MALLOC_FAILURE);
+            goto err;
+        }
+        if ((len_1 = BN_bn2bin(tmp_1, buffer_1)) == 0) {
+            ECerr(EC_F_EC_ASN1_GROUP2CURVE, ERR_R_BN_LIB);
+            goto err;
+        }
+        a_buf = buffer_1;
+    }
+
+    if (len_2 == 0) {
+        /* len_2 == 0 => b == 0 */
+        b_buf = &char_zero;
+        len_2 = 1;
+    } else {
+        if ((buffer_2 = OPENSSL_malloc(len_2)) == NULL) {
+            ECerr(EC_F_EC_ASN1_GROUP2CURVE, ERR_R_MALLOC_FAILURE);
+            goto err;
+        }
+        if ((len_2 = BN_bn2bin(tmp_2, buffer_2)) == 0) {
+            ECerr(EC_F_EC_ASN1_GROUP2CURVE, ERR_R_BN_LIB);
+            goto err;
+        }
+        b_buf = buffer_2;
+    }
+
+    /* set a and b */
+    if (!ASN1_OCTET_STRING_set(curve->a, a_buf, len_1) ||
+        !ASN1_OCTET_STRING_set(curve->b, b_buf, len_2)) {
+        ECerr(EC_F_EC_ASN1_GROUP2CURVE, ERR_R_ASN1_LIB);
+        goto err;
+    }
+
+    /* set the seed (optional) */
+    if (group->seed) {
+        if (!curve->seed)
+            if ((curve->seed = ASN1_BIT_STRING_new()) == NULL) {
+                ECerr(EC_F_EC_ASN1_GROUP2CURVE, ERR_R_MALLOC_FAILURE);
+                goto err;
+            }
+        curve->seed->flags &= ~(ASN1_STRING_FLAG_BITS_LEFT | 0x07);
+        curve->seed->flags |= ASN1_STRING_FLAG_BITS_LEFT;
+        if (!ASN1_BIT_STRING_set(curve->seed, group->seed,
+                                 (int)group->seed_len)) {
+            ECerr(EC_F_EC_ASN1_GROUP2CURVE, ERR_R_ASN1_LIB);
+            goto err;
+        }
+    } else {
+        ASN1_BIT_STRING_free(curve->seed);
+        curve->seed = NULL;
+    }
+
+    ok = 1;
+
+ err:
+    OPENSSL_free(buffer_1);
+    OPENSSL_free(buffer_2);
+    BN_free(tmp_1);
+    BN_free(tmp_2);
+    return (ok);
+}
+
+ECPARAMETERS *EC_GROUP_get_ecparameters(const EC_GROUP *group,
+                                               ECPARAMETERS *params)
+{
+    size_t len = 0;
+    ECPARAMETERS *ret = NULL;
+    const BIGNUM *tmp;
+    unsigned char *buffer = NULL;
+    const EC_POINT *point = NULL;
+    point_conversion_form_t form;
+
+    if (params == NULL) {
+        if ((ret = ECPARAMETERS_new()) == NULL) {
+            ECerr(EC_F_EC_GROUP_GET_ECPARAMETERS, ERR_R_MALLOC_FAILURE);
+            goto err;
+        }
+    } else
+        ret = params;
+
+    /* set the version (always one) */
+    ret->version = (long)0x1;
+
+    /* set the fieldID */
+    if (!ec_asn1_group2fieldid(group, ret->fieldID)) {
+        ECerr(EC_F_EC_GROUP_GET_ECPARAMETERS, ERR_R_EC_LIB);
+        goto err;
+    }
+
+    /* set the curve */
+    if (!ec_asn1_group2curve(group, ret->curve)) {
+        ECerr(EC_F_EC_GROUP_GET_ECPARAMETERS, ERR_R_EC_LIB);
+        goto err;
+    }
+
+    /* set the base point */
+    if ((point = EC_GROUP_get0_generator(group)) == NULL) {
+        ECerr(EC_F_EC_GROUP_GET_ECPARAMETERS, EC_R_UNDEFINED_GENERATOR);
+        goto err;
+    }
+
+    form = EC_GROUP_get_point_conversion_form(group);
+
+    len = EC_POINT_point2buf(group, point, form, &buffer, NULL);
+    if (len == 0) {
+        ECerr(EC_F_EC_GROUP_GET_ECPARAMETERS, ERR_R_EC_LIB);
+        goto err;
+    }
+    if (ret->base == NULL && (ret->base = ASN1_OCTET_STRING_new()) == NULL) {
+        OPENSSL_free(buffer);
+        ECerr(EC_F_EC_GROUP_GET_ECPARAMETERS, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+    ASN1_STRING_set0(ret->base, buffer, len);
+
+    /* set the order */
+    tmp = EC_GROUP_get0_order(group);
+    if (tmp == NULL) {
+        ECerr(EC_F_EC_GROUP_GET_ECPARAMETERS, ERR_R_EC_LIB);
+        goto err;
+    }
+    ret->order = BN_to_ASN1_INTEGER(tmp, ret->order);
+    if (ret->order == NULL) {
+        ECerr(EC_F_EC_GROUP_GET_ECPARAMETERS, ERR_R_ASN1_LIB);
+        goto err;
+    }
+
+    /* set the cofactor (optional) */
+    tmp = EC_GROUP_get0_cofactor(group);
+    if (tmp != NULL) {
+        ret->cofactor = BN_to_ASN1_INTEGER(tmp, ret->cofactor);
+        if (ret->cofactor == NULL) {
+            ECerr(EC_F_EC_GROUP_GET_ECPARAMETERS, ERR_R_ASN1_LIB);
+            goto err;
+        }
+    }
+
+    return ret;
+
+ err:
+    if (params == NULL)
+        ECPARAMETERS_free(ret);
+    return NULL;
+}
+
+ECPKPARAMETERS *EC_GROUP_get_ecpkparameters(const EC_GROUP *group,
+                                            ECPKPARAMETERS *params)
+{
+    int ok = 1, tmp;
+    ECPKPARAMETERS *ret = params;
+
+    if (ret == NULL) {
+        if ((ret = ECPKPARAMETERS_new()) == NULL) {
+            ECerr(EC_F_EC_GROUP_GET_ECPKPARAMETERS, ERR_R_MALLOC_FAILURE);
+            return NULL;
+        }
+    } else {
+        if (ret->type == 0)
+            ASN1_OBJECT_free(ret->value.named_curve);
+        else if (ret->type == 1 && ret->value.parameters)
+            ECPARAMETERS_free(ret->value.parameters);
+    }
+
+    if (EC_GROUP_get_asn1_flag(group)) {
+        /*
+         * use the asn1 OID to describe the the elliptic curve parameters
+         */
+        tmp = EC_GROUP_get_curve_name(group);
+        if (tmp) {
+            ret->type = 0;
+            if ((ret->value.named_curve = OBJ_nid2obj(tmp)) == NULL)
+                ok = 0;
+        } else
+            /* we don't know the nid => ERROR */
+            ok = 0;
+    } else {
+        /* use the ECPARAMETERS structure */
+        ret->type = 1;
+        if ((ret->value.parameters =
+             EC_GROUP_get_ecparameters(group, NULL)) == NULL)
+            ok = 0;
+    }
+
+    if (!ok) {
+        ECPKPARAMETERS_free(ret);
+        return NULL;
+    }
+    return ret;
+}
+
+EC_GROUP *EC_GROUP_new_from_ecparameters(const ECPARAMETERS *params)
+{
+    int ok = 0, tmp;
+    EC_GROUP *ret = NULL;
+    BIGNUM *p = NULL, *a = NULL, *b = NULL;
+    EC_POINT *point = NULL;
+    long field_bits;
+
+    if (!params->fieldID || !params->fieldID->fieldType ||
+        !params->fieldID->p.ptr) {
+        ECerr(EC_F_EC_GROUP_NEW_FROM_ECPARAMETERS, EC_R_ASN1_ERROR);
+        goto err;
+    }
+
+    /* now extract the curve parameters a and b */
+    if (!params->curve || !params->curve->a ||
+        !params->curve->a->data || !params->curve->b ||
+        !params->curve->b->data) {
+        ECerr(EC_F_EC_GROUP_NEW_FROM_ECPARAMETERS, EC_R_ASN1_ERROR);
+        goto err;
+    }
+    a = BN_bin2bn(params->curve->a->data, params->curve->a->length, NULL);
+    if (a == NULL) {
+        ECerr(EC_F_EC_GROUP_NEW_FROM_ECPARAMETERS, ERR_R_BN_LIB);
+        goto err;
+    }
+    b = BN_bin2bn(params->curve->b->data, params->curve->b->length, NULL);
+    if (b == NULL) {
+        ECerr(EC_F_EC_GROUP_NEW_FROM_ECPARAMETERS, ERR_R_BN_LIB);
+        goto err;
+    }
+
+    /* get the field parameters */
+    tmp = OBJ_obj2nid(params->fieldID->fieldType);
+    if (tmp == NID_X9_62_characteristic_two_field)
+#ifdef OPENSSL_NO_EC2M
+    {
+        ECerr(EC_F_EC_GROUP_NEW_FROM_ECPARAMETERS, EC_R_GF2M_NOT_SUPPORTED);
+        goto err;
+    }
+#else
+    {
+        X9_62_CHARACTERISTIC_TWO *char_two;
+
+        char_two = params->fieldID->p.char_two;
+
+        field_bits = char_two->m;
+        if (field_bits > OPENSSL_ECC_MAX_FIELD_BITS) {
+            ECerr(EC_F_EC_GROUP_NEW_FROM_ECPARAMETERS, EC_R_FIELD_TOO_LARGE);
+            goto err;
+        }
+
+        if ((p = BN_new()) == NULL) {
+            ECerr(EC_F_EC_GROUP_NEW_FROM_ECPARAMETERS, ERR_R_MALLOC_FAILURE);
+            goto err;
+        }
+
+        /* get the base type */
+        tmp = OBJ_obj2nid(char_two->type);
+
+        if (tmp == NID_X9_62_tpBasis) {
+            long tmp_long;
+
+            if (!char_two->p.tpBasis) {
+                ECerr(EC_F_EC_GROUP_NEW_FROM_ECPARAMETERS, EC_R_ASN1_ERROR);
+                goto err;
+            }
+
+            tmp_long = ASN1_INTEGER_get(char_two->p.tpBasis);
+
+            if (!(char_two->m > tmp_long && tmp_long > 0)) {
+                ECerr(EC_F_EC_GROUP_NEW_FROM_ECPARAMETERS,
+                      EC_R_INVALID_TRINOMIAL_BASIS);
+                goto err;
+            }
+
+            /* create the polynomial */
+            if (!BN_set_bit(p, (int)char_two->m))
+                goto err;
+            if (!BN_set_bit(p, (int)tmp_long))
+                goto err;
+            if (!BN_set_bit(p, 0))
+                goto err;
+        } else if (tmp == NID_X9_62_ppBasis) {
+            X9_62_PENTANOMIAL *penta;
+
+            penta = char_two->p.ppBasis;
+            if (!penta) {
+                ECerr(EC_F_EC_GROUP_NEW_FROM_ECPARAMETERS, EC_R_ASN1_ERROR);
+                goto err;
+            }
+
+            if (!
+                (char_two->m > penta->k3 && penta->k3 > penta->k2
+                 && penta->k2 > penta->k1 && penta->k1 > 0)) {
+                ECerr(EC_F_EC_GROUP_NEW_FROM_ECPARAMETERS,
+                      EC_R_INVALID_PENTANOMIAL_BASIS);
+                goto err;
+            }
+
+            /* create the polynomial */
+            if (!BN_set_bit(p, (int)char_two->m))
+                goto err;
+            if (!BN_set_bit(p, (int)penta->k1))
+                goto err;
+            if (!BN_set_bit(p, (int)penta->k2))
+                goto err;
+            if (!BN_set_bit(p, (int)penta->k3))
+                goto err;
+            if (!BN_set_bit(p, 0))
+                goto err;
+        } else if (tmp == NID_X9_62_onBasis) {
+            ECerr(EC_F_EC_GROUP_NEW_FROM_ECPARAMETERS, EC_R_NOT_IMPLEMENTED);
+            goto err;
+        } else {                /* error */
+
+            ECerr(EC_F_EC_GROUP_NEW_FROM_ECPARAMETERS, EC_R_ASN1_ERROR);
+            goto err;
+        }
+
+        /* create the EC_GROUP structure */
+        ret = EC_GROUP_new_curve_GF2m(p, a, b, NULL);
+    }
+#endif
+    else if (tmp == NID_X9_62_prime_field) {
+        /* we have a curve over a prime field */
+        /* extract the prime number */
+        if (!params->fieldID->p.prime) {
+            ECerr(EC_F_EC_GROUP_NEW_FROM_ECPARAMETERS, EC_R_ASN1_ERROR);
+            goto err;
+        }
+        p = ASN1_INTEGER_to_BN(params->fieldID->p.prime, NULL);
+        if (p == NULL) {
+            ECerr(EC_F_EC_GROUP_NEW_FROM_ECPARAMETERS, ERR_R_ASN1_LIB);
+            goto err;
+        }
+
+        if (BN_is_negative(p) || BN_is_zero(p)) {
+            ECerr(EC_F_EC_GROUP_NEW_FROM_ECPARAMETERS, EC_R_INVALID_FIELD);
+            goto err;
+        }
+
+        field_bits = BN_num_bits(p);
+        if (field_bits > OPENSSL_ECC_MAX_FIELD_BITS) {
+            ECerr(EC_F_EC_GROUP_NEW_FROM_ECPARAMETERS, EC_R_FIELD_TOO_LARGE);
+            goto err;
+        }
+
+        /* create the EC_GROUP structure */
+        ret = EC_GROUP_new_curve_GFp(p, a, b, NULL);
+    } else {
+        ECerr(EC_F_EC_GROUP_NEW_FROM_ECPARAMETERS, EC_R_INVALID_FIELD);
+        goto err;
+    }
+
+    if (ret == NULL) {
+        ECerr(EC_F_EC_GROUP_NEW_FROM_ECPARAMETERS, ERR_R_EC_LIB);
+        goto err;
+    }
+
+    /* extract seed (optional) */
+    if (params->curve->seed != NULL) {
+        OPENSSL_free(ret->seed);
+        if ((ret->seed = OPENSSL_malloc(params->curve->seed->length)) == NULL) {
+            ECerr(EC_F_EC_GROUP_NEW_FROM_ECPARAMETERS, ERR_R_MALLOC_FAILURE);
+            goto err;
+        }
+        memcpy(ret->seed, params->curve->seed->data,
+               params->curve->seed->length);
+        ret->seed_len = params->curve->seed->length;
+    }
+
+    if (!params->order || !params->base || !params->base->data) {
+        ECerr(EC_F_EC_GROUP_NEW_FROM_ECPARAMETERS, EC_R_ASN1_ERROR);
+        goto err;
+    }
+
+    if ((point = EC_POINT_new(ret)) == NULL)
+        goto err;
+
+    /* set the point conversion form */
+    EC_GROUP_set_point_conversion_form(ret, (point_conversion_form_t)
+                                       (params->base->data[0] & ~0x01));
+
+    /* extract the ec point */
+    if (!EC_POINT_oct2point(ret, point, params->base->data,
+                            params->base->length, NULL)) {
+        ECerr(EC_F_EC_GROUP_NEW_FROM_ECPARAMETERS, ERR_R_EC_LIB);
+        goto err;
+    }
+
+    /* extract the order */
+    if ((a = ASN1_INTEGER_to_BN(params->order, a)) == NULL) {
+        ECerr(EC_F_EC_GROUP_NEW_FROM_ECPARAMETERS, ERR_R_ASN1_LIB);
+        goto err;
+    }
+    if (BN_is_negative(a) || BN_is_zero(a)) {
+        ECerr(EC_F_EC_GROUP_NEW_FROM_ECPARAMETERS, EC_R_INVALID_GROUP_ORDER);
+        goto err;
+    }
+    if (BN_num_bits(a) > (int)field_bits + 1) { /* Hasse bound */
+        ECerr(EC_F_EC_GROUP_NEW_FROM_ECPARAMETERS, EC_R_INVALID_GROUP_ORDER);
+        goto err;
+    }
+
+    /* extract the cofactor (optional) */
+    if (params->cofactor == NULL) {
+        BN_free(b);
+        b = NULL;
+    } else if ((b = ASN1_INTEGER_to_BN(params->cofactor, b)) == NULL) {
+        ECerr(EC_F_EC_GROUP_NEW_FROM_ECPARAMETERS, ERR_R_ASN1_LIB);
+        goto err;
+    }
+    /* set the generator, order and cofactor (if present) */
+    if (!EC_GROUP_set_generator(ret, point, a, b)) {
+        ECerr(EC_F_EC_GROUP_NEW_FROM_ECPARAMETERS, ERR_R_EC_LIB);
+        goto err;
+    }
+
+    ok = 1;
+
+ err:
+    if (!ok) {
+        EC_GROUP_clear_free(ret);
+        ret = NULL;
+    }
+
+    BN_free(p);
+    BN_free(a);
+    BN_free(b);
+    EC_POINT_free(point);
+    return (ret);
+}
+
+EC_GROUP *EC_GROUP_new_from_ecpkparameters(const ECPKPARAMETERS *params)
+{
+    EC_GROUP *ret = NULL;
+    int tmp = 0;
+
+    if (params == NULL) {
+        ECerr(EC_F_EC_GROUP_NEW_FROM_ECPKPARAMETERS, EC_R_MISSING_PARAMETERS);
+        return NULL;
+    }
+
+    if (params->type == 0) {    /* the curve is given by an OID */
+        tmp = OBJ_obj2nid(params->value.named_curve);
+        if ((ret = EC_GROUP_new_by_curve_name(tmp)) == NULL) {
+            ECerr(EC_F_EC_GROUP_NEW_FROM_ECPKPARAMETERS,
+                  EC_R_EC_GROUP_NEW_BY_NAME_FAILURE);
+            return NULL;
+        }
+        EC_GROUP_set_asn1_flag(ret, OPENSSL_EC_NAMED_CURVE);
+    } else if (params->type == 1) { /* the parameters are given by a
+                                     * ECPARAMETERS structure */
+        ret = EC_GROUP_new_from_ecparameters(params->value.parameters);
+        if (!ret) {
+            ECerr(EC_F_EC_GROUP_NEW_FROM_ECPKPARAMETERS, ERR_R_EC_LIB);
+            return NULL;
+        }
+        EC_GROUP_set_asn1_flag(ret, 0x0);
+    } else if (params->type == 2) { /* implicitlyCA */
+        return NULL;
+    } else {
+        ECerr(EC_F_EC_GROUP_NEW_FROM_ECPKPARAMETERS, EC_R_ASN1_ERROR);
+        return NULL;
+    }
+
+    return ret;
+}
+
+/* EC_GROUP <-> DER encoding of ECPKPARAMETERS */
+
+EC_GROUP *d2i_ECPKParameters(EC_GROUP **a, const unsigned char **in, long len)
+{
+    EC_GROUP *group = NULL;
+    ECPKPARAMETERS *params = NULL;
+    const unsigned char *p = *in;
+
+    if ((params = d2i_ECPKPARAMETERS(NULL, &p, len)) == NULL) {
+        ECerr(EC_F_D2I_ECPKPARAMETERS, EC_R_D2I_ECPKPARAMETERS_FAILURE);
+        ECPKPARAMETERS_free(params);
+        return NULL;
+    }
+
+    if ((group = EC_GROUP_new_from_ecpkparameters(params)) == NULL) {
+        ECerr(EC_F_D2I_ECPKPARAMETERS, EC_R_PKPARAMETERS2GROUP_FAILURE);
+        ECPKPARAMETERS_free(params);
+        return NULL;
+    }
+
+    if (a) {
+        EC_GROUP_clear_free(*a);
+        *a = group;
+    }
+
+    ECPKPARAMETERS_free(params);
+    *in = p;
+    return (group);
+}
+
+int i2d_ECPKParameters(const EC_GROUP *a, unsigned char **out)
+{
+    int ret = 0;
+    ECPKPARAMETERS *tmp = EC_GROUP_get_ecpkparameters(a, NULL);
+    if (tmp == NULL) {
+        ECerr(EC_F_I2D_ECPKPARAMETERS, EC_R_GROUP2PKPARAMETERS_FAILURE);
+        return 0;
+    }
+    if ((ret = i2d_ECPKPARAMETERS(tmp, out)) == 0) {
+        ECerr(EC_F_I2D_ECPKPARAMETERS, EC_R_I2D_ECPKPARAMETERS_FAILURE);
+        ECPKPARAMETERS_free(tmp);
+        return 0;
+    }
+    ECPKPARAMETERS_free(tmp);
+    return (ret);
+}
+
+/* some EC_KEY functions */
+
+EC_KEY *d2i_ECPrivateKey(EC_KEY **a, const unsigned char **in, long len)
+{
+    EC_KEY *ret = NULL;
+    EC_PRIVATEKEY *priv_key = NULL;
+    const unsigned char *p = *in;
+
+    if ((priv_key = d2i_EC_PRIVATEKEY(NULL, &p, len)) == NULL) {
+        ECerr(EC_F_D2I_ECPRIVATEKEY, ERR_R_EC_LIB);
+        return NULL;
+    }
+
+    if (a == NULL || *a == NULL) {
+        if ((ret = EC_KEY_new()) == NULL) {
+            ECerr(EC_F_D2I_ECPRIVATEKEY, ERR_R_MALLOC_FAILURE);
+            goto err;
+        }
+    } else
+        ret = *a;
+
+    if (priv_key->parameters) {
+        EC_GROUP_clear_free(ret->group);
+        ret->group = EC_GROUP_new_from_ecpkparameters(priv_key->parameters);
+    }
+
+    if (ret->group == NULL) {
+        ECerr(EC_F_D2I_ECPRIVATEKEY, ERR_R_EC_LIB);
+        goto err;
+    }
+
+    ret->version = priv_key->version;
+
+    if (priv_key->privateKey) {
+        ASN1_OCTET_STRING *pkey = priv_key->privateKey;
+        if (EC_KEY_oct2priv(ret, ASN1_STRING_get0_data(pkey),
+                            ASN1_STRING_length(pkey)) == 0)
+            goto err;
+    } else {
+        ECerr(EC_F_D2I_ECPRIVATEKEY, EC_R_MISSING_PRIVATE_KEY);
+        goto err;
+    }
+
+    EC_POINT_clear_free(ret->pub_key);
+    ret->pub_key = EC_POINT_new(ret->group);
+    if (ret->pub_key == NULL) {
+        ECerr(EC_F_D2I_ECPRIVATEKEY, ERR_R_EC_LIB);
+        goto err;
+    }
+
+    if (priv_key->publicKey) {
+        const unsigned char *pub_oct;
+        int pub_oct_len;
+
+        pub_oct = ASN1_STRING_get0_data(priv_key->publicKey);
+        pub_oct_len = ASN1_STRING_length(priv_key->publicKey);
+        if (!EC_KEY_oct2key(ret, pub_oct, pub_oct_len, NULL)) {
+            ECerr(EC_F_D2I_ECPRIVATEKEY, ERR_R_EC_LIB);
+            goto err;
+        }
+    } else {
+        if (ret->group->meth->keygenpub == NULL
+            || ret->group->meth->keygenpub(ret) == 0)
+                goto err;
+        /* Remember the original private-key-only encoding. */
+        ret->enc_flag |= EC_PKEY_NO_PUBKEY;
+    }
+
+    if (a)
+        *a = ret;
+    EC_PRIVATEKEY_free(priv_key);
+    *in = p;
+    return (ret);
+
+ err:
+    if (a == NULL || *a != ret)
+        EC_KEY_free(ret);
+    EC_PRIVATEKEY_free(priv_key);
+    return NULL;
+}
+
+int i2d_ECPrivateKey(EC_KEY *a, unsigned char **out)
+{
+    int ret = 0, ok = 0;
+    unsigned char *priv= NULL, *pub= NULL;
+    size_t privlen = 0, publen = 0;
+
+    EC_PRIVATEKEY *priv_key = NULL;
+
+    if (a == NULL || a->group == NULL ||
+        (!(a->enc_flag & EC_PKEY_NO_PUBKEY) && a->pub_key == NULL)) {
+        ECerr(EC_F_I2D_ECPRIVATEKEY, ERR_R_PASSED_NULL_PARAMETER);
+        goto err;
+    }
+
+    if ((priv_key = EC_PRIVATEKEY_new()) == NULL) {
+        ECerr(EC_F_I2D_ECPRIVATEKEY, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+
+    priv_key->version = a->version;
+
+    privlen = EC_KEY_priv2buf(a, &priv);
+
+    if (privlen == 0) {
+        ECerr(EC_F_I2D_ECPRIVATEKEY, ERR_R_EC_LIB);
+        goto err;
+    }
+
+    ASN1_STRING_set0(priv_key->privateKey, priv, privlen);
+    priv = NULL;
+
+    if (!(a->enc_flag & EC_PKEY_NO_PARAMETERS)) {
+        if ((priv_key->parameters =
+             EC_GROUP_get_ecpkparameters(a->group,
+                                        priv_key->parameters)) == NULL) {
+            ECerr(EC_F_I2D_ECPRIVATEKEY, ERR_R_EC_LIB);
+            goto err;
+        }
+    }
+
+    if (!(a->enc_flag & EC_PKEY_NO_PUBKEY)) {
+        priv_key->publicKey = ASN1_BIT_STRING_new();
+        if (priv_key->publicKey == NULL) {
+            ECerr(EC_F_I2D_ECPRIVATEKEY, ERR_R_MALLOC_FAILURE);
+            goto err;
+        }
+
+        publen = EC_KEY_key2buf(a, a->conv_form, &pub, NULL);
+
+        if (publen == 0) {
+            ECerr(EC_F_I2D_ECPRIVATEKEY, ERR_R_EC_LIB);
+            goto err;
+        }
+
+        priv_key->publicKey->flags &= ~(ASN1_STRING_FLAG_BITS_LEFT | 0x07);
+        priv_key->publicKey->flags |= ASN1_STRING_FLAG_BITS_LEFT;
+        ASN1_STRING_set0(priv_key->publicKey, pub, publen);
+        pub = NULL;
+    }
+
+    if ((ret = i2d_EC_PRIVATEKEY(priv_key, out)) == 0) {
+        ECerr(EC_F_I2D_ECPRIVATEKEY, ERR_R_EC_LIB);
+        goto err;
+    }
+    ok = 1;
+ err:
+    OPENSSL_clear_free(priv, privlen);
+    OPENSSL_free(pub);
+    EC_PRIVATEKEY_free(priv_key);
+    return (ok ? ret : 0);
+}
+
+int i2d_ECParameters(EC_KEY *a, unsigned char **out)
+{
+    if (a == NULL) {
+        ECerr(EC_F_I2D_ECPARAMETERS, ERR_R_PASSED_NULL_PARAMETER);
+        return 0;
+    }
+    return i2d_ECPKParameters(a->group, out);
+}
+
+EC_KEY *d2i_ECParameters(EC_KEY **a, const unsigned char **in, long len)
+{
+    EC_KEY *ret;
+
+    if (in == NULL || *in == NULL) {
+        ECerr(EC_F_D2I_ECPARAMETERS, ERR_R_PASSED_NULL_PARAMETER);
+        return NULL;
+    }
+
+    if (a == NULL || *a == NULL) {
+        if ((ret = EC_KEY_new()) == NULL) {
+            ECerr(EC_F_D2I_ECPARAMETERS, ERR_R_MALLOC_FAILURE);
+            return NULL;
+        }
+    } else
+        ret = *a;
+
+    if (!d2i_ECPKParameters(&ret->group, in, len)) {
+        ECerr(EC_F_D2I_ECPARAMETERS, ERR_R_EC_LIB);
+        if (a == NULL || *a != ret)
+             EC_KEY_free(ret);
+        return NULL;
+    }
+
+    if (a)
+        *a = ret;
+
+    return ret;
+}
+
+EC_KEY *o2i_ECPublicKey(EC_KEY **a, const unsigned char **in, long len)
+{
+    EC_KEY *ret = NULL;
+
+    if (a == NULL || (*a) == NULL || (*a)->group == NULL) {
+        /*
+         * sorry, but a EC_GROUP-structure is necessary to set the public key
+         */
+        ECerr(EC_F_O2I_ECPUBLICKEY, ERR_R_PASSED_NULL_PARAMETER);
+        return 0;
+    }
+    ret = *a;
+    if (!EC_KEY_oct2key(ret, *in, len, NULL)) {
+        ECerr(EC_F_O2I_ECPUBLICKEY, ERR_R_EC_LIB);
+        return 0;
+    }
+    *in += len;
+    return ret;
+}
+
+int i2o_ECPublicKey(const EC_KEY *a, unsigned char **out)
+{
+    size_t buf_len = 0;
+    int new_buffer = 0;
+
+    if (a == NULL) {
+        ECerr(EC_F_I2O_ECPUBLICKEY, ERR_R_PASSED_NULL_PARAMETER);
+        return 0;
+    }
+
+    buf_len = EC_POINT_point2oct(a->group, a->pub_key,
+                                 a->conv_form, NULL, 0, NULL);
+
+    if (out == NULL || buf_len == 0)
+        /* out == NULL => just return the length of the octet string */
+        return buf_len;
+
+    if (*out == NULL) {
+        if ((*out = OPENSSL_malloc(buf_len)) == NULL) {
+            ECerr(EC_F_I2O_ECPUBLICKEY, ERR_R_MALLOC_FAILURE);
+            return 0;
+        }
+        new_buffer = 1;
+    }
+    if (!EC_POINT_point2oct(a->group, a->pub_key, a->conv_form,
+                            *out, buf_len, NULL)) {
+        ECerr(EC_F_I2O_ECPUBLICKEY, ERR_R_EC_LIB);
+        if (new_buffer) {
+            OPENSSL_free(*out);
+            *out = NULL;
+        }
+        return 0;
+    }
+    if (!new_buffer)
+        *out += buf_len;
+    return buf_len;
+}
+
+ASN1_SEQUENCE(ECDSA_SIG) = {
+        ASN1_SIMPLE(ECDSA_SIG, r, CBIGNUM),
+        ASN1_SIMPLE(ECDSA_SIG, s, CBIGNUM)
+} static_ASN1_SEQUENCE_END(ECDSA_SIG)
+
+DECLARE_ASN1_FUNCTIONS_const(ECDSA_SIG)
+DECLARE_ASN1_ENCODE_FUNCTIONS_const(ECDSA_SIG, ECDSA_SIG)
+IMPLEMENT_ASN1_ENCODE_FUNCTIONS_const_fname(ECDSA_SIG, ECDSA_SIG, ECDSA_SIG)
+
+ECDSA_SIG *ECDSA_SIG_new(void)
+{
+    ECDSA_SIG *sig = OPENSSL_zalloc(sizeof(*sig));
+    if (sig == NULL)
+        ECerr(EC_F_ECDSA_SIG_NEW, ERR_R_MALLOC_FAILURE);
+    return sig;
+}
+
+void ECDSA_SIG_free(ECDSA_SIG *sig)
+{
+    if (sig == NULL)
+        return;
+    BN_clear_free(sig->r);
+    BN_clear_free(sig->s);
+    OPENSSL_free(sig);
+}
+
+void ECDSA_SIG_get0(const ECDSA_SIG *sig, const BIGNUM **pr, const BIGNUM **ps)
+{
+    if (pr != NULL)
+        *pr = sig->r;
+    if (ps != NULL)
+        *ps = sig->s;
+}
+
+int ECDSA_SIG_set0(ECDSA_SIG *sig, BIGNUM *r, BIGNUM *s)
+{
+    if (r == NULL || s == NULL)
+        return 0;
+    BN_clear_free(sig->r);
+    BN_clear_free(sig->s);
+    sig->r = r;
+    sig->s = s;
+    return 1;
+}
+
+int ECDSA_size(const EC_KEY *r)
+{
+    int ret, i;
+    ASN1_INTEGER bs;
+    unsigned char buf[4];
+    const EC_GROUP *group;
+
+    if (r == NULL)
+        return 0;
+    group = EC_KEY_get0_group(r);
+    if (group == NULL)
+        return 0;
+
+    i = EC_GROUP_order_bits(group);
+    if (i == 0)
+        return 0;
+    bs.length = (i + 7) / 8;
+    bs.data = buf;
+    bs.type = V_ASN1_INTEGER;
+    /* If the top bit is set the asn1 encoding is 1 larger. */
+    buf[0] = 0xff;
+
+    i = i2d_ASN1_INTEGER(&bs, NULL);
+    i += i;                     /* r and s */
+    ret = ASN1_object_size(1, i, V_ASN1_SEQUENCE);
+    return (ret);
+}
+
+#ifndef OPENSSL_NO_STDIO
+ECDSA_SIG *d2i_ECDSA_SIG_fp(FILE *fp, ECDSA_SIG **a)
+{
+	return ASN1_item_d2i_fp(ASN1_ITEM_rptr(ECDSA_SIG), fp, a);
+}
+
+int i2d_ECDSA_SIG_fp(FILE *fp, ECDSA_SIG *sig)
+{
+	return ASN1_item_i2d_fp(ASN1_ITEM_rptr(ECDSA_SIG), fp, sig);
+}
+#endif
diff --git a/crypto/ec/ec_check.c b/crypto/ec/ec_check.c
new file mode 100644
index 000000000..eeb06ec1c
--- /dev/null
+++ b/crypto/ec/ec_check.c
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2002-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "ec_lcl.h"
+#include 
+
+int EC_GROUP_check(const EC_GROUP *group, BN_CTX *ctx)
+{
+    int ret = 0;
+    const BIGNUM *order;
+    BN_CTX *new_ctx = NULL;
+    EC_POINT *point = NULL;
+
+    /* Custom curves assumed to be correct */
+    if ((group->meth->flags & EC_FLAGS_CUSTOM_CURVE) != 0)
+        return 1;
+
+    if (ctx == NULL) {
+        ctx = new_ctx = BN_CTX_new();
+        if (ctx == NULL) {
+            ECerr(EC_F_EC_GROUP_CHECK, ERR_R_MALLOC_FAILURE);
+            goto err;
+        }
+    }
+
+    /* check the discriminant */
+    if (!EC_GROUP_check_discriminant(group, ctx)) {
+        ECerr(EC_F_EC_GROUP_CHECK, EC_R_DISCRIMINANT_IS_ZERO);
+        goto err;
+    }
+
+    /* check the generator */
+    if (group->generator == NULL) {
+        ECerr(EC_F_EC_GROUP_CHECK, EC_R_UNDEFINED_GENERATOR);
+        goto err;
+    }
+    if (EC_POINT_is_on_curve(group, group->generator, ctx) <= 0) {
+        ECerr(EC_F_EC_GROUP_CHECK, EC_R_POINT_IS_NOT_ON_CURVE);
+        goto err;
+    }
+
+    /* check the order of the generator */
+    if ((point = EC_POINT_new(group)) == NULL)
+        goto err;
+    order = EC_GROUP_get0_order(group);
+    if (order == NULL)
+        goto err;
+    if (BN_is_zero(order)) {
+        ECerr(EC_F_EC_GROUP_CHECK, EC_R_UNDEFINED_ORDER);
+        goto err;
+    }
+
+    if (!EC_POINT_mul(group, point, order, NULL, NULL, ctx))
+        goto err;
+    if (!EC_POINT_is_at_infinity(group, point)) {
+        ECerr(EC_F_EC_GROUP_CHECK, EC_R_INVALID_GROUP_ORDER);
+        goto err;
+    }
+
+    ret = 1;
+
+ err:
+    BN_CTX_free(new_ctx);
+    EC_POINT_free(point);
+    return ret;
+}
diff --git a/crypto/ec/ec_curve.c b/crypto/ec/ec_curve.c
new file mode 100644
index 000000000..8d76bcef1
--- /dev/null
+++ b/crypto/ec/ec_curve.c
@@ -0,0 +1,3341 @@
+/* ====================================================================
+ * Copyright (c) 2014 - 2018 The GmSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the GmSSL Project.
+ *    (http://gmssl.org/)"
+ *
+ * 4. The name "GmSSL Project" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    guanzhi1980@gmail.com.
+ *
+ * 5. Products derived from this software may not be called "GmSSL"
+ *    nor may "GmSSL" appear in their names without prior written
+ *    permission of the GmSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the GmSSL Project
+ *    (http://gmssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE GmSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE GmSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+/*
+ * Copyright 2002-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/* ====================================================================
+ * Copyright 2002 Sun Microsystems, Inc. ALL RIGHTS RESERVED.
+ *
+ * Portions of the attached software ("Contribution") are developed by
+ * SUN MICROSYSTEMS, INC., and are contributed to the OpenSSL project.
+ *
+ * The Contribution is licensed pursuant to the OpenSSL open source
+ * license provided above.
+ *
+ * The elliptic curve binary polynomial software is originally written by
+ * Sheueling Chang Shantz and Douglas Stebila of Sun Microsystems Laboratories.
+ *
+ */
+
+#include 
+#include "ec_lcl.h"
+#include 
+#include 
+#include 
+#include "e_os.h"
+
+typedef struct {
+    int field_type,             /* either NID_X9_62_prime_field or
+                                 * NID_X9_62_characteristic_two_field */
+     seed_len, param_len;
+    unsigned int cofactor;      /* promoted to BN_ULONG */
+} EC_CURVE_DATA;
+
+/* the nist prime curves */
+static const struct {
+    EC_CURVE_DATA h;
+    unsigned char data[20 + 24 * 6];
+} _EC_NIST_PRIME_192 = {
+    {
+        NID_X9_62_prime_field, 20, 24, 1
+    },
+    {
+        /* seed */
+        0x30, 0x45, 0xAE, 0x6F, 0xC8, 0x42, 0x2F, 0x64, 0xED, 0x57, 0x95, 0x28,
+        0xD3, 0x81, 0x20, 0xEA, 0xE1, 0x21, 0x96, 0xD5,
+        /* p */
+        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+        0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+        /* a */
+        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+        0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC,
+        /* b */
+        0x64, 0x21, 0x05, 0x19, 0xE5, 0x9C, 0x80, 0xE7, 0x0F, 0xA7, 0xE9, 0xAB,
+        0x72, 0x24, 0x30, 0x49, 0xFE, 0xB8, 0xDE, 0xEC, 0xC1, 0x46, 0xB9, 0xB1,
+        /* x */
+        0x18, 0x8D, 0xA8, 0x0E, 0xB0, 0x30, 0x90, 0xF6, 0x7C, 0xBF, 0x20, 0xEB,
+        0x43, 0xA1, 0x88, 0x00, 0xF4, 0xFF, 0x0A, 0xFD, 0x82, 0xFF, 0x10, 0x12,
+        /* y */
+        0x07, 0x19, 0x2b, 0x95, 0xff, 0xc8, 0xda, 0x78, 0x63, 0x10, 0x11, 0xed,
+        0x6b, 0x24, 0xcd, 0xd5, 0x73, 0xf9, 0x77, 0xa1, 0x1e, 0x79, 0x48, 0x11,
+        /* order */
+        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+        0x99, 0xDE, 0xF8, 0x36, 0x14, 0x6B, 0xC9, 0xB1, 0xB4, 0xD2, 0x28, 0x31
+    }
+};
+
+static const struct {
+    EC_CURVE_DATA h;
+    unsigned char data[20 + 28 * 6];
+} _EC_NIST_PRIME_224 = {
+    {
+        NID_X9_62_prime_field, 20, 28, 1
+    },
+    {
+        /* seed */
+        0xBD, 0x71, 0x34, 0x47, 0x99, 0xD5, 0xC7, 0xFC, 0xDC, 0x45, 0xB5, 0x9F,
+        0xA3, 0xB9, 0xAB, 0x8F, 0x6A, 0x94, 0x8B, 0xC5,
+        /* p */
+        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+        0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x01,
+        /* a */
+        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+        0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+        0xFF, 0xFF, 0xFF, 0xFE,
+        /* b */
+        0xB4, 0x05, 0x0A, 0x85, 0x0C, 0x04, 0xB3, 0xAB, 0xF5, 0x41, 0x32, 0x56,
+        0x50, 0x44, 0xB0, 0xB7, 0xD7, 0xBF, 0xD8, 0xBA, 0x27, 0x0B, 0x39, 0x43,
+        0x23, 0x55, 0xFF, 0xB4,
+        /* x */
+        0xB7, 0x0E, 0x0C, 0xBD, 0x6B, 0xB4, 0xBF, 0x7F, 0x32, 0x13, 0x90, 0xB9,
+        0x4A, 0x03, 0xC1, 0xD3, 0x56, 0xC2, 0x11, 0x22, 0x34, 0x32, 0x80, 0xD6,
+        0x11, 0x5C, 0x1D, 0x21,
+        /* y */
+        0xbd, 0x37, 0x63, 0x88, 0xb5, 0xf7, 0x23, 0xfb, 0x4c, 0x22, 0xdf, 0xe6,
+        0xcd, 0x43, 0x75, 0xa0, 0x5a, 0x07, 0x47, 0x64, 0x44, 0xd5, 0x81, 0x99,
+        0x85, 0x00, 0x7e, 0x34,
+        /* order */
+        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+        0xFF, 0xFF, 0x16, 0xA2, 0xE0, 0xB8, 0xF0, 0x3E, 0x13, 0xDD, 0x29, 0x45,
+        0x5C, 0x5C, 0x2A, 0x3D
+    }
+};
+
+static const struct {
+    EC_CURVE_DATA h;
+    unsigned char data[20 + 48 * 6];
+} _EC_NIST_PRIME_384 = {
+    {
+        NID_X9_62_prime_field, 20, 48, 1
+    },
+    {
+        /* seed */
+        0xA3, 0x35, 0x92, 0x6A, 0xA3, 0x19, 0xA2, 0x7A, 0x1D, 0x00, 0x89, 0x6A,
+        0x67, 0x73, 0xA4, 0x82, 0x7A, 0xCD, 0xAC, 0x73,
+        /* p */
+        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
+        /* a */
+        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFC,
+        /* b */
+        0xB3, 0x31, 0x2F, 0xA7, 0xE2, 0x3E, 0xE7, 0xE4, 0x98, 0x8E, 0x05, 0x6B,
+        0xE3, 0xF8, 0x2D, 0x19, 0x18, 0x1D, 0x9C, 0x6E, 0xFE, 0x81, 0x41, 0x12,
+        0x03, 0x14, 0x08, 0x8F, 0x50, 0x13, 0x87, 0x5A, 0xC6, 0x56, 0x39, 0x8D,
+        0x8A, 0x2E, 0xD1, 0x9D, 0x2A, 0x85, 0xC8, 0xED, 0xD3, 0xEC, 0x2A, 0xEF,
+        /* x */
+        0xAA, 0x87, 0xCA, 0x22, 0xBE, 0x8B, 0x05, 0x37, 0x8E, 0xB1, 0xC7, 0x1E,
+        0xF3, 0x20, 0xAD, 0x74, 0x6E, 0x1D, 0x3B, 0x62, 0x8B, 0xA7, 0x9B, 0x98,
+        0x59, 0xF7, 0x41, 0xE0, 0x82, 0x54, 0x2A, 0x38, 0x55, 0x02, 0xF2, 0x5D,
+        0xBF, 0x55, 0x29, 0x6C, 0x3A, 0x54, 0x5E, 0x38, 0x72, 0x76, 0x0A, 0xB7,
+        /* y */
+        0x36, 0x17, 0xde, 0x4a, 0x96, 0x26, 0x2c, 0x6f, 0x5d, 0x9e, 0x98, 0xbf,
+        0x92, 0x92, 0xdc, 0x29, 0xf8, 0xf4, 0x1d, 0xbd, 0x28, 0x9a, 0x14, 0x7c,
+        0xe9, 0xda, 0x31, 0x13, 0xb5, 0xf0, 0xb8, 0xc0, 0x0a, 0x60, 0xb1, 0xce,
+        0x1d, 0x7e, 0x81, 0x9d, 0x7a, 0x43, 0x1d, 0x7c, 0x90, 0xea, 0x0e, 0x5f,
+        /* order */
+        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+        0xC7, 0x63, 0x4D, 0x81, 0xF4, 0x37, 0x2D, 0xDF, 0x58, 0x1A, 0x0D, 0xB2,
+        0x48, 0xB0, 0xA7, 0x7A, 0xEC, 0xEC, 0x19, 0x6A, 0xCC, 0xC5, 0x29, 0x73
+    }
+};
+
+static const struct {
+    EC_CURVE_DATA h;
+    unsigned char data[20 + 66 * 6];
+} _EC_NIST_PRIME_521 = {
+    {
+        NID_X9_62_prime_field, 20, 66, 1
+    },
+    {
+        /* seed */
+        0xD0, 0x9E, 0x88, 0x00, 0x29, 0x1C, 0xB8, 0x53, 0x96, 0xCC, 0x67, 0x17,
+        0x39, 0x32, 0x84, 0xAA, 0xA0, 0xDA, 0x64, 0xBA,
+        /* p */
+        0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+        /* a */
+        0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC,
+        /* b */
+        0x00, 0x51, 0x95, 0x3E, 0xB9, 0x61, 0x8E, 0x1C, 0x9A, 0x1F, 0x92, 0x9A,
+        0x21, 0xA0, 0xB6, 0x85, 0x40, 0xEE, 0xA2, 0xDA, 0x72, 0x5B, 0x99, 0xB3,
+        0x15, 0xF3, 0xB8, 0xB4, 0x89, 0x91, 0x8E, 0xF1, 0x09, 0xE1, 0x56, 0x19,
+        0x39, 0x51, 0xEC, 0x7E, 0x93, 0x7B, 0x16, 0x52, 0xC0, 0xBD, 0x3B, 0xB1,
+        0xBF, 0x07, 0x35, 0x73, 0xDF, 0x88, 0x3D, 0x2C, 0x34, 0xF1, 0xEF, 0x45,
+        0x1F, 0xD4, 0x6B, 0x50, 0x3F, 0x00,
+        /* x */
+        0x00, 0xC6, 0x85, 0x8E, 0x06, 0xB7, 0x04, 0x04, 0xE9, 0xCD, 0x9E, 0x3E,
+        0xCB, 0x66, 0x23, 0x95, 0xB4, 0x42, 0x9C, 0x64, 0x81, 0x39, 0x05, 0x3F,
+        0xB5, 0x21, 0xF8, 0x28, 0xAF, 0x60, 0x6B, 0x4D, 0x3D, 0xBA, 0xA1, 0x4B,
+        0x5E, 0x77, 0xEF, 0xE7, 0x59, 0x28, 0xFE, 0x1D, 0xC1, 0x27, 0xA2, 0xFF,
+        0xA8, 0xDE, 0x33, 0x48, 0xB3, 0xC1, 0x85, 0x6A, 0x42, 0x9B, 0xF9, 0x7E,
+        0x7E, 0x31, 0xC2, 0xE5, 0xBD, 0x66,
+        /* y */
+        0x01, 0x18, 0x39, 0x29, 0x6a, 0x78, 0x9a, 0x3b, 0xc0, 0x04, 0x5c, 0x8a,
+        0x5f, 0xb4, 0x2c, 0x7d, 0x1b, 0xd9, 0x98, 0xf5, 0x44, 0x49, 0x57, 0x9b,
+        0x44, 0x68, 0x17, 0xaf, 0xbd, 0x17, 0x27, 0x3e, 0x66, 0x2c, 0x97, 0xee,
+        0x72, 0x99, 0x5e, 0xf4, 0x26, 0x40, 0xc5, 0x50, 0xb9, 0x01, 0x3f, 0xad,
+        0x07, 0x61, 0x35, 0x3c, 0x70, 0x86, 0xa2, 0x72, 0xc2, 0x40, 0x88, 0xbe,
+        0x94, 0x76, 0x9f, 0xd1, 0x66, 0x50,
+        /* order */
+        0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFA, 0x51, 0x86,
+        0x87, 0x83, 0xBF, 0x2F, 0x96, 0x6B, 0x7F, 0xCC, 0x01, 0x48, 0xF7, 0x09,
+        0xA5, 0xD0, 0x3B, 0xB5, 0xC9, 0xB8, 0x89, 0x9C, 0x47, 0xAE, 0xBB, 0x6F,
+        0xB7, 0x1E, 0x91, 0x38, 0x64, 0x09
+    }
+};
+
+/* the x9.62 prime curves (minus the nist prime curves) */
+static const struct {
+    EC_CURVE_DATA h;
+    unsigned char data[20 + 24 * 6];
+} _EC_X9_62_PRIME_192V2 = {
+    {
+        NID_X9_62_prime_field, 20, 24, 1
+    },
+    {
+        /* seed */
+        0x31, 0xA9, 0x2E, 0xE2, 0x02, 0x9F, 0xD1, 0x0D, 0x90, 0x1B, 0x11, 0x3E,
+        0x99, 0x07, 0x10, 0xF0, 0xD2, 0x1A, 0xC6, 0xB6,
+        /* p */
+        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+        0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+        /* a */
+        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+        0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC,
+        /* b */
+        0xCC, 0x22, 0xD6, 0xDF, 0xB9, 0x5C, 0x6B, 0x25, 0xE4, 0x9C, 0x0D, 0x63,
+        0x64, 0xA4, 0xE5, 0x98, 0x0C, 0x39, 0x3A, 0xA2, 0x16, 0x68, 0xD9, 0x53,
+        /* x */
+        0xEE, 0xA2, 0xBA, 0xE7, 0xE1, 0x49, 0x78, 0x42, 0xF2, 0xDE, 0x77, 0x69,
+        0xCF, 0xE9, 0xC9, 0x89, 0xC0, 0x72, 0xAD, 0x69, 0x6F, 0x48, 0x03, 0x4A,
+        /* y */
+        0x65, 0x74, 0xd1, 0x1d, 0x69, 0xb6, 0xec, 0x7a, 0x67, 0x2b, 0xb8, 0x2a,
+        0x08, 0x3d, 0xf2, 0xf2, 0xb0, 0x84, 0x7d, 0xe9, 0x70, 0xb2, 0xde, 0x15,
+        /* order */
+        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE,
+        0x5F, 0xB1, 0xA7, 0x24, 0xDC, 0x80, 0x41, 0x86, 0x48, 0xD8, 0xDD, 0x31
+    }
+};
+
+static const struct {
+    EC_CURVE_DATA h;
+    unsigned char data[20 + 24 * 6];
+} _EC_X9_62_PRIME_192V3 = {
+    {
+        NID_X9_62_prime_field, 20, 24, 1
+    },
+    {
+        /* seed */
+        0xC4, 0x69, 0x68, 0x44, 0x35, 0xDE, 0xB3, 0x78, 0xC4, 0xB6, 0x5C, 0xA9,
+        0x59, 0x1E, 0x2A, 0x57, 0x63, 0x05, 0x9A, 0x2E,
+        /* p */
+        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+        0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+        /* a */
+        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+        0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC,
+        /* b */
+        0x22, 0x12, 0x3D, 0xC2, 0x39, 0x5A, 0x05, 0xCA, 0xA7, 0x42, 0x3D, 0xAE,
+        0xCC, 0xC9, 0x47, 0x60, 0xA7, 0xD4, 0x62, 0x25, 0x6B, 0xD5, 0x69, 0x16,
+        /* x */
+        0x7D, 0x29, 0x77, 0x81, 0x00, 0xC6, 0x5A, 0x1D, 0xA1, 0x78, 0x37, 0x16,
+        0x58, 0x8D, 0xCE, 0x2B, 0x8B, 0x4A, 0xEE, 0x8E, 0x22, 0x8F, 0x18, 0x96,
+        /* y */
+        0x38, 0xa9, 0x0f, 0x22, 0x63, 0x73, 0x37, 0x33, 0x4b, 0x49, 0xdc, 0xb6,
+        0x6a, 0x6d, 0xc8, 0xf9, 0x97, 0x8a, 0xca, 0x76, 0x48, 0xa9, 0x43, 0xb0,
+        /* order */
+        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+        0x7A, 0x62, 0xD0, 0x31, 0xC8, 0x3F, 0x42, 0x94, 0xF6, 0x40, 0xEC, 0x13
+    }
+};
+
+static const struct {
+    EC_CURVE_DATA h;
+    unsigned char data[20 + 30 * 6];
+} _EC_X9_62_PRIME_239V1 = {
+    {
+        NID_X9_62_prime_field, 20, 30, 1
+    },
+    {
+        /* seed */
+        0xE4, 0x3B, 0xB4, 0x60, 0xF0, 0xB8, 0x0C, 0xC0, 0xC0, 0xB0, 0x75, 0x79,
+        0x8E, 0x94, 0x80, 0x60, 0xF8, 0x32, 0x1B, 0x7D,
+        /* p */
+        0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+        0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+        /* a */
+        0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+        0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC,
+        /* b */
+        0x6B, 0x01, 0x6C, 0x3B, 0xDC, 0xF1, 0x89, 0x41, 0xD0, 0xD6, 0x54, 0x92,
+        0x14, 0x75, 0xCA, 0x71, 0xA9, 0xDB, 0x2F, 0xB2, 0x7D, 0x1D, 0x37, 0x79,
+        0x61, 0x85, 0xC2, 0x94, 0x2C, 0x0A,
+        /* x */
+        0x0F, 0xFA, 0x96, 0x3C, 0xDC, 0xA8, 0x81, 0x6C, 0xCC, 0x33, 0xB8, 0x64,
+        0x2B, 0xED, 0xF9, 0x05, 0xC3, 0xD3, 0x58, 0x57, 0x3D, 0x3F, 0x27, 0xFB,
+        0xBD, 0x3B, 0x3C, 0xB9, 0xAA, 0xAF,
+        /* y */
+        0x7d, 0xeb, 0xe8, 0xe4, 0xe9, 0x0a, 0x5d, 0xae, 0x6e, 0x40, 0x54, 0xca,
+        0x53, 0x0b, 0xa0, 0x46, 0x54, 0xb3, 0x68, 0x18, 0xce, 0x22, 0x6b, 0x39,
+        0xfc, 0xcb, 0x7b, 0x02, 0xf1, 0xae,
+        /* order */
+        0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+        0x7F, 0xFF, 0xFF, 0x9E, 0x5E, 0x9A, 0x9F, 0x5D, 0x90, 0x71, 0xFB, 0xD1,
+        0x52, 0x26, 0x88, 0x90, 0x9D, 0x0B
+    }
+};
+
+static const struct {
+    EC_CURVE_DATA h;
+    unsigned char data[20 + 30 * 6];
+} _EC_X9_62_PRIME_239V2 = {
+    {
+        NID_X9_62_prime_field, 20, 30, 1
+    },
+    {
+        /* seed */
+        0xE8, 0xB4, 0x01, 0x16, 0x04, 0x09, 0x53, 0x03, 0xCA, 0x3B, 0x80, 0x99,
+        0x98, 0x2B, 0xE0, 0x9F, 0xCB, 0x9A, 0xE6, 0x16,
+        /* p */
+        0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+        0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+        /* a */
+        0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+        0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC,
+        /* b */
+        0x61, 0x7F, 0xAB, 0x68, 0x32, 0x57, 0x6C, 0xBB, 0xFE, 0xD5, 0x0D, 0x99,
+        0xF0, 0x24, 0x9C, 0x3F, 0xEE, 0x58, 0xB9, 0x4B, 0xA0, 0x03, 0x8C, 0x7A,
+        0xE8, 0x4C, 0x8C, 0x83, 0x2F, 0x2C,
+        /* x */
+        0x38, 0xAF, 0x09, 0xD9, 0x87, 0x27, 0x70, 0x51, 0x20, 0xC9, 0x21, 0xBB,
+        0x5E, 0x9E, 0x26, 0x29, 0x6A, 0x3C, 0xDC, 0xF2, 0xF3, 0x57, 0x57, 0xA0,
+        0xEA, 0xFD, 0x87, 0xB8, 0x30, 0xE7,
+        /* y */
+        0x5b, 0x01, 0x25, 0xe4, 0xdb, 0xea, 0x0e, 0xc7, 0x20, 0x6d, 0xa0, 0xfc,
+        0x01, 0xd9, 0xb0, 0x81, 0x32, 0x9f, 0xb5, 0x55, 0xde, 0x6e, 0xf4, 0x60,
+        0x23, 0x7d, 0xff, 0x8b, 0xe4, 0xba,
+        /* order */
+        0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+        0x80, 0x00, 0x00, 0xCF, 0xA7, 0xE8, 0x59, 0x43, 0x77, 0xD4, 0x14, 0xC0,
+        0x38, 0x21, 0xBC, 0x58, 0x20, 0x63
+    }
+};
+
+static const struct {
+    EC_CURVE_DATA h;
+    unsigned char data[20 + 30 * 6];
+} _EC_X9_62_PRIME_239V3 = {
+    {
+        NID_X9_62_prime_field, 20, 30, 1
+    },
+    {
+        /* seed */
+        0x7D, 0x73, 0x74, 0x16, 0x8F, 0xFE, 0x34, 0x71, 0xB6, 0x0A, 0x85, 0x76,
+        0x86, 0xA1, 0x94, 0x75, 0xD3, 0xBF, 0xA2, 0xFF,
+        /* p */
+        0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+        0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+        /* a */
+        0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+        0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC,
+        /* b */
+        0x25, 0x57, 0x05, 0xFA, 0x2A, 0x30, 0x66, 0x54, 0xB1, 0xF4, 0xCB, 0x03,
+        0xD6, 0xA7, 0x50, 0xA3, 0x0C, 0x25, 0x01, 0x02, 0xD4, 0x98, 0x87, 0x17,
+        0xD9, 0xBA, 0x15, 0xAB, 0x6D, 0x3E,
+        /* x */
+        0x67, 0x68, 0xAE, 0x8E, 0x18, 0xBB, 0x92, 0xCF, 0xCF, 0x00, 0x5C, 0x94,
+        0x9A, 0xA2, 0xC6, 0xD9, 0x48, 0x53, 0xD0, 0xE6, 0x60, 0xBB, 0xF8, 0x54,
+        0xB1, 0xC9, 0x50, 0x5F, 0xE9, 0x5A,
+        /* y */
+        0x16, 0x07, 0xe6, 0x89, 0x8f, 0x39, 0x0c, 0x06, 0xbc, 0x1d, 0x55, 0x2b,
+        0xad, 0x22, 0x6f, 0x3b, 0x6f, 0xcf, 0xe4, 0x8b, 0x6e, 0x81, 0x84, 0x99,
+        0xaf, 0x18, 0xe3, 0xed, 0x6c, 0xf3,
+        /* order */
+        0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+        0x7F, 0xFF, 0xFF, 0x97, 0x5D, 0xEB, 0x41, 0xB3, 0xA6, 0x05, 0x7C, 0x3C,
+        0x43, 0x21, 0x46, 0x52, 0x65, 0x51
+    }
+};
+
+static const struct {
+    EC_CURVE_DATA h;
+    unsigned char data[20 + 32 * 6];
+} _EC_X9_62_PRIME_256V1 = {
+    {
+        NID_X9_62_prime_field, 20, 32, 1
+    },
+    {
+        /* seed */
+        0xC4, 0x9D, 0x36, 0x08, 0x86, 0xE7, 0x04, 0x93, 0x6A, 0x66, 0x78, 0xE1,
+        0x13, 0x9D, 0x26, 0xB7, 0x81, 0x9F, 0x7E, 0x90,
+        /* p */
+        0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
+        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+        /* a */
+        0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
+        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC,
+        /* b */
+        0x5A, 0xC6, 0x35, 0xD8, 0xAA, 0x3A, 0x93, 0xE7, 0xB3, 0xEB, 0xBD, 0x55,
+        0x76, 0x98, 0x86, 0xBC, 0x65, 0x1D, 0x06, 0xB0, 0xCC, 0x53, 0xB0, 0xF6,
+        0x3B, 0xCE, 0x3C, 0x3E, 0x27, 0xD2, 0x60, 0x4B,
+        /* x */
+        0x6B, 0x17, 0xD1, 0xF2, 0xE1, 0x2C, 0x42, 0x47, 0xF8, 0xBC, 0xE6, 0xE5,
+        0x63, 0xA4, 0x40, 0xF2, 0x77, 0x03, 0x7D, 0x81, 0x2D, 0xEB, 0x33, 0xA0,
+        0xF4, 0xA1, 0x39, 0x45, 0xD8, 0x98, 0xC2, 0x96,
+        /* y */
+        0x4f, 0xe3, 0x42, 0xe2, 0xfe, 0x1a, 0x7f, 0x9b, 0x8e, 0xe7, 0xeb, 0x4a,
+        0x7c, 0x0f, 0x9e, 0x16, 0x2b, 0xce, 0x33, 0x57, 0x6b, 0x31, 0x5e, 0xce,
+        0xcb, 0xb6, 0x40, 0x68, 0x37, 0xbf, 0x51, 0xf5,
+        /* order */
+        0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
+        0xFF, 0xFF, 0xFF, 0xFF, 0xBC, 0xE6, 0xFA, 0xAD, 0xA7, 0x17, 0x9E, 0x84,
+        0xF3, 0xB9, 0xCA, 0xC2, 0xFC, 0x63, 0x25, 0x51
+    }
+};
+
+/* the secg prime curves (minus the nist and x9.62 prime curves) */
+static const struct {
+    EC_CURVE_DATA h;
+    unsigned char data[20 + 14 * 6];
+} _EC_SECG_PRIME_112R1 = {
+    {
+        NID_X9_62_prime_field, 20, 14, 1
+    },
+    {
+        /* seed */
+        0x00, 0xF5, 0x0B, 0x02, 0x8E, 0x4D, 0x69, 0x6E, 0x67, 0x68, 0x75, 0x61,
+        0x51, 0x75, 0x29, 0x04, 0x72, 0x78, 0x3F, 0xB1,
+        /* p */
+        0xDB, 0x7C, 0x2A, 0xBF, 0x62, 0xE3, 0x5E, 0x66, 0x80, 0x76, 0xBE, 0xAD,
+        0x20, 0x8B,
+        /* a */
+        0xDB, 0x7C, 0x2A, 0xBF, 0x62, 0xE3, 0x5E, 0x66, 0x80, 0x76, 0xBE, 0xAD,
+        0x20, 0x88,
+        /* b */
+        0x65, 0x9E, 0xF8, 0xBA, 0x04, 0x39, 0x16, 0xEE, 0xDE, 0x89, 0x11, 0x70,
+        0x2B, 0x22,
+        /* x */
+        0x09, 0x48, 0x72, 0x39, 0x99, 0x5A, 0x5E, 0xE7, 0x6B, 0x55, 0xF9, 0xC2,
+        0xF0, 0x98,
+        /* y */
+        0xa8, 0x9c, 0xe5, 0xaf, 0x87, 0x24, 0xc0, 0xa2, 0x3e, 0x0e, 0x0f, 0xf7,
+        0x75, 0x00,
+        /* order */
+        0xDB, 0x7C, 0x2A, 0xBF, 0x62, 0xE3, 0x5E, 0x76, 0x28, 0xDF, 0xAC, 0x65,
+        0x61, 0xC5
+    }
+};
+
+static const struct {
+    EC_CURVE_DATA h;
+    unsigned char data[20 + 14 * 6];
+} _EC_SECG_PRIME_112R2 = {
+    {
+        NID_X9_62_prime_field, 20, 14, 4
+    },
+    {
+        /* seed */
+        0x00, 0x27, 0x57, 0xA1, 0x11, 0x4D, 0x69, 0x6E, 0x67, 0x68, 0x75, 0x61,
+        0x51, 0x75, 0x53, 0x16, 0xC0, 0x5E, 0x0B, 0xD4,
+        /* p */
+        0xDB, 0x7C, 0x2A, 0xBF, 0x62, 0xE3, 0x5E, 0x66, 0x80, 0x76, 0xBE, 0xAD,
+        0x20, 0x8B,
+        /* a */
+        0x61, 0x27, 0xC2, 0x4C, 0x05, 0xF3, 0x8A, 0x0A, 0xAA, 0xF6, 0x5C, 0x0E,
+        0xF0, 0x2C,
+        /* b */
+        0x51, 0xDE, 0xF1, 0x81, 0x5D, 0xB5, 0xED, 0x74, 0xFC, 0xC3, 0x4C, 0x85,
+        0xD7, 0x09,
+        /* x */
+        0x4B, 0xA3, 0x0A, 0xB5, 0xE8, 0x92, 0xB4, 0xE1, 0x64, 0x9D, 0xD0, 0x92,
+        0x86, 0x43,
+        /* y */
+        0xad, 0xcd, 0x46, 0xf5, 0x88, 0x2e, 0x37, 0x47, 0xde, 0xf3, 0x6e, 0x95,
+        0x6e, 0x97,
+        /* order */
+        0x36, 0xDF, 0x0A, 0xAF, 0xD8, 0xB8, 0xD7, 0x59, 0x7C, 0xA1, 0x05, 0x20,
+        0xD0, 0x4B
+    }
+};
+
+static const struct {
+    EC_CURVE_DATA h;
+    unsigned char data[20 + 16 * 6];
+} _EC_SECG_PRIME_128R1 = {
+    {
+        NID_X9_62_prime_field, 20, 16, 1
+    },
+    {
+        /* seed */
+        0x00, 0x0E, 0x0D, 0x4D, 0x69, 0x6E, 0x67, 0x68, 0x75, 0x61, 0x51, 0x75,
+        0x0C, 0xC0, 0x3A, 0x44, 0x73, 0xD0, 0x36, 0x79,
+        /* p */
+        0xFF, 0xFF, 0xFF, 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+        0xFF, 0xFF, 0xFF, 0xFF,
+        /* a */
+        0xFF, 0xFF, 0xFF, 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+        0xFF, 0xFF, 0xFF, 0xFC,
+        /* b */
+        0xE8, 0x75, 0x79, 0xC1, 0x10, 0x79, 0xF4, 0x3D, 0xD8, 0x24, 0x99, 0x3C,
+        0x2C, 0xEE, 0x5E, 0xD3,
+        /* x */
+        0x16, 0x1F, 0xF7, 0x52, 0x8B, 0x89, 0x9B, 0x2D, 0x0C, 0x28, 0x60, 0x7C,
+        0xA5, 0x2C, 0x5B, 0x86,
+        /* y */
+        0xcf, 0x5a, 0xc8, 0x39, 0x5b, 0xaf, 0xeb, 0x13, 0xc0, 0x2d, 0xa2, 0x92,
+        0xdd, 0xed, 0x7a, 0x83,
+        /* order */
+        0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x75, 0xA3, 0x0D, 0x1B,
+        0x90, 0x38, 0xA1, 0x15
+    }
+};
+
+static const struct {
+    EC_CURVE_DATA h;
+    unsigned char data[20 + 16 * 6];
+} _EC_SECG_PRIME_128R2 = {
+    {
+        NID_X9_62_prime_field, 20, 16, 4
+    },
+    {
+        /* seed */
+        0x00, 0x4D, 0x69, 0x6E, 0x67, 0x68, 0x75, 0x61, 0x51, 0x75, 0x12, 0xD8,
+        0xF0, 0x34, 0x31, 0xFC, 0xE6, 0x3B, 0x88, 0xF4,
+        /* p */
+        0xFF, 0xFF, 0xFF, 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+        0xFF, 0xFF, 0xFF, 0xFF,
+        /* a */
+        0xD6, 0x03, 0x19, 0x98, 0xD1, 0xB3, 0xBB, 0xFE, 0xBF, 0x59, 0xCC, 0x9B,
+        0xBF, 0xF9, 0xAE, 0xE1,
+        /* b */
+        0x5E, 0xEE, 0xFC, 0xA3, 0x80, 0xD0, 0x29, 0x19, 0xDC, 0x2C, 0x65, 0x58,
+        0xBB, 0x6D, 0x8A, 0x5D,
+        /* x */
+        0x7B, 0x6A, 0xA5, 0xD8, 0x5E, 0x57, 0x29, 0x83, 0xE6, 0xFB, 0x32, 0xA7,
+        0xCD, 0xEB, 0xC1, 0x40,
+        /* y */
+        0x27, 0xb6, 0x91, 0x6a, 0x89, 0x4d, 0x3a, 0xee, 0x71, 0x06, 0xfe, 0x80,
+        0x5f, 0xc3, 0x4b, 0x44,
+        /* order */
+        0x3F, 0xFF, 0xFF, 0xFF, 0x7F, 0xFF, 0xFF, 0xFF, 0xBE, 0x00, 0x24, 0x72,
+        0x06, 0x13, 0xB5, 0xA3
+    }
+};
+
+static const struct {
+    EC_CURVE_DATA h;
+    unsigned char data[0 + 21 * 6];
+} _EC_SECG_PRIME_160K1 = {
+    {
+        NID_X9_62_prime_field, 0, 21, 1
+    },
+    {
+        /* no seed */
+        /* p */
+        0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+        0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xAC, 0x73,
+        /* a */
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        /* b */
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07,
+        /* x */
+        0x00, 0x3B, 0x4C, 0x38, 0x2C, 0xE3, 0x7A, 0xA1, 0x92, 0xA4, 0x01, 0x9E,
+        0x76, 0x30, 0x36, 0xF4, 0xF5, 0xDD, 0x4D, 0x7E, 0xBB,
+        /* y */
+        0x00, 0x93, 0x8c, 0xf9, 0x35, 0x31, 0x8f, 0xdc, 0xed, 0x6b, 0xc2, 0x82,
+        0x86, 0x53, 0x17, 0x33, 0xc3, 0xf0, 0x3c, 0x4f, 0xee,
+        /* order */
+        0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xB8,
+        0xFA, 0x16, 0xDF, 0xAB, 0x9A, 0xCA, 0x16, 0xB6, 0xB3
+    }
+};
+
+static const struct {
+    EC_CURVE_DATA h;
+    unsigned char data[20 + 21 * 6];
+} _EC_SECG_PRIME_160R1 = {
+    {
+        NID_X9_62_prime_field, 20, 21, 1
+    },
+    {
+        /* seed */
+        0x10, 0x53, 0xCD, 0xE4, 0x2C, 0x14, 0xD6, 0x96, 0xE6, 0x76, 0x87, 0x56,
+        0x15, 0x17, 0x53, 0x3B, 0xF3, 0xF8, 0x33, 0x45,
+        /* p */
+        0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0xFF, 0xFF, 0xFF,
+        /* a */
+        0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0xFF, 0xFF, 0xFC,
+        /* b */
+        0x00, 0x1C, 0x97, 0xBE, 0xFC, 0x54, 0xBD, 0x7A, 0x8B, 0x65, 0xAC, 0xF8,
+        0x9F, 0x81, 0xD4, 0xD4, 0xAD, 0xC5, 0x65, 0xFA, 0x45,
+        /* x */
+        0x00, 0x4A, 0x96, 0xB5, 0x68, 0x8E, 0xF5, 0x73, 0x28, 0x46, 0x64, 0x69,
+        0x89, 0x68, 0xC3, 0x8B, 0xB9, 0x13, 0xCB, 0xFC, 0x82,
+        /* y */
+        0x00, 0x23, 0xa6, 0x28, 0x55, 0x31, 0x68, 0x94, 0x7d, 0x59, 0xdc, 0xc9,
+        0x12, 0x04, 0x23, 0x51, 0x37, 0x7a, 0xc5, 0xfb, 0x32,
+        /* order */
+        0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xF4,
+        0xC8, 0xF9, 0x27, 0xAE, 0xD3, 0xCA, 0x75, 0x22, 0x57
+    }
+};
+
+static const struct {
+    EC_CURVE_DATA h;
+    unsigned char data[20 + 21 * 6];
+} _EC_SECG_PRIME_160R2 = {
+    {
+        NID_X9_62_prime_field, 20, 21, 1
+    },
+    {
+        /* seed */
+        0xB9, 0x9B, 0x99, 0xB0, 0x99, 0xB3, 0x23, 0xE0, 0x27, 0x09, 0xA4, 0xD6,
+        0x96, 0xE6, 0x76, 0x87, 0x56, 0x15, 0x17, 0x51,
+        /* p */
+        0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+        0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xAC, 0x73,
+        /* a */
+        0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+        0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xAC, 0x70,
+        /* b */
+        0x00, 0xB4, 0xE1, 0x34, 0xD3, 0xFB, 0x59, 0xEB, 0x8B, 0xAB, 0x57, 0x27,
+        0x49, 0x04, 0x66, 0x4D, 0x5A, 0xF5, 0x03, 0x88, 0xBA,
+        /* x */
+        0x00, 0x52, 0xDC, 0xB0, 0x34, 0x29, 0x3A, 0x11, 0x7E, 0x1F, 0x4F, 0xF1,
+        0x1B, 0x30, 0xF7, 0x19, 0x9D, 0x31, 0x44, 0xCE, 0x6D,
+        /* y */
+        0x00, 0xfe, 0xaf, 0xfe, 0xf2, 0xe3, 0x31, 0xf2, 0x96, 0xe0, 0x71, 0xfa,
+        0x0d, 0xf9, 0x98, 0x2c, 0xfe, 0xa7, 0xd4, 0x3f, 0x2e,
+        /* order */
+        0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35,
+        0x1E, 0xE7, 0x86, 0xA8, 0x18, 0xF3, 0xA1, 0xA1, 0x6B
+    }
+};
+
+static const struct {
+    EC_CURVE_DATA h;
+    unsigned char data[0 + 24 * 6];
+} _EC_SECG_PRIME_192K1 = {
+    {
+        NID_X9_62_prime_field, 0, 24, 1
+    },
+    {
+        /* no seed */
+        /* p */
+        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xEE, 0x37,
+        /* a */
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        /* b */
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
+        /* x */
+        0xDB, 0x4F, 0xF1, 0x0E, 0xC0, 0x57, 0xE9, 0xAE, 0x26, 0xB0, 0x7D, 0x02,
+        0x80, 0xB7, 0xF4, 0x34, 0x1D, 0xA5, 0xD1, 0xB1, 0xEA, 0xE0, 0x6C, 0x7D,
+        /* y */
+        0x9b, 0x2f, 0x2f, 0x6d, 0x9c, 0x56, 0x28, 0xa7, 0x84, 0x41, 0x63, 0xd0,
+        0x15, 0xbe, 0x86, 0x34, 0x40, 0x82, 0xaa, 0x88, 0xd9, 0x5e, 0x2f, 0x9d,
+        /* order */
+        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE,
+        0x26, 0xF2, 0xFC, 0x17, 0x0F, 0x69, 0x46, 0x6A, 0x74, 0xDE, 0xFD, 0x8D
+    }
+};
+
+static const struct {
+    EC_CURVE_DATA h;
+    unsigned char data[0 + 29 * 6];
+} _EC_SECG_PRIME_224K1 = {
+    {
+        NID_X9_62_prime_field, 0, 29, 1
+    },
+    {
+        /* no seed */
+        /* p */
+        0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+        0xFE, 0xFF, 0xFF, 0xE5, 0x6D,
+        /* a */
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00,
+        /* b */
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x05,
+        /* x */
+        0x00, 0xA1, 0x45, 0x5B, 0x33, 0x4D, 0xF0, 0x99, 0xDF, 0x30, 0xFC, 0x28,
+        0xA1, 0x69, 0xA4, 0x67, 0xE9, 0xE4, 0x70, 0x75, 0xA9, 0x0F, 0x7E, 0x65,
+        0x0E, 0xB6, 0xB7, 0xA4, 0x5C,
+        /* y */
+        0x00, 0x7e, 0x08, 0x9f, 0xed, 0x7f, 0xba, 0x34, 0x42, 0x82, 0xca, 0xfb,
+        0xd6, 0xf7, 0xe3, 0x19, 0xf7, 0xc0, 0xb0, 0xbd, 0x59, 0xe2, 0xca, 0x4b,
+        0xdb, 0x55, 0x6d, 0x61, 0xa5,
+        /* order */
+        0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x01, 0xDC, 0xE8, 0xD2, 0xEC, 0x61, 0x84, 0xCA, 0xF0, 0xA9,
+        0x71, 0x76, 0x9F, 0xB1, 0xF7
+    }
+};
+
+static const struct {
+    EC_CURVE_DATA h;
+    unsigned char data[0 + 32 * 6];
+} _EC_SECG_PRIME_256K1 = {
+    {
+        NID_X9_62_prime_field, 0, 32, 1
+    },
+    {
+        /* no seed */
+        /* p */
+        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+        0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFC, 0x2F,
+        /* a */
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        /* b */
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07,
+        /* x */
+        0x79, 0xBE, 0x66, 0x7E, 0xF9, 0xDC, 0xBB, 0xAC, 0x55, 0xA0, 0x62, 0x95,
+        0xCE, 0x87, 0x0B, 0x07, 0x02, 0x9B, 0xFC, 0xDB, 0x2D, 0xCE, 0x28, 0xD9,
+        0x59, 0xF2, 0x81, 0x5B, 0x16, 0xF8, 0x17, 0x98,
+        /* y */
+        0x48, 0x3a, 0xda, 0x77, 0x26, 0xa3, 0xc4, 0x65, 0x5d, 0xa4, 0xfb, 0xfc,
+        0x0e, 0x11, 0x08, 0xa8, 0xfd, 0x17, 0xb4, 0x48, 0xa6, 0x85, 0x54, 0x19,
+        0x9c, 0x47, 0xd0, 0x8f, 0xfb, 0x10, 0xd4, 0xb8,
+        /* order */
+        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+        0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B,
+        0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x41
+    }
+};
+
+/* some wap/wtls curves */
+static const struct {
+    EC_CURVE_DATA h;
+    unsigned char data[0 + 15 * 6];
+} _EC_WTLS_8 = {
+    {
+        NID_X9_62_prime_field, 0, 15, 1
+    },
+    {
+        /* no seed */
+        /* p */
+        0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+        0xFF, 0xFD, 0xE7,
+        /* a */
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00,
+        /* b */
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x03,
+        /* x */
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x01,
+        /* y */
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x02,
+        /* order */
+        0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xEC, 0xEA, 0x55, 0x1A,
+        0xD8, 0x37, 0xE9
+    }
+};
+
+static const struct {
+    EC_CURVE_DATA h;
+    unsigned char data[0 + 21 * 6];
+} _EC_WTLS_9 = {
+    {
+        NID_X9_62_prime_field, 0, 21, 1
+    },
+    {
+        /* no seed */
+        /* p */
+        0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x80, 0x8F,
+        /* a */
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        /* b */
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
+        /* x */
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+        /* y */
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+        /* order */
+        0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xCD,
+        0xC9, 0x8A, 0xE0, 0xE2, 0xDE, 0x57, 0x4A, 0xBF, 0x33
+    }
+};
+
+static const struct {
+    EC_CURVE_DATA h;
+    unsigned char data[0 + 28 * 6];
+} _EC_WTLS_12 = {
+    {
+        NID_X9_62_prime_field, 0, 28, 1
+    },
+    {
+        /* no seed */
+        /* p */
+        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+        0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x01,
+        /* a */
+        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+        0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+        0xFF, 0xFF, 0xFF, 0xFE,
+        /* b */
+        0xB4, 0x05, 0x0A, 0x85, 0x0C, 0x04, 0xB3, 0xAB, 0xF5, 0x41, 0x32, 0x56,
+        0x50, 0x44, 0xB0, 0xB7, 0xD7, 0xBF, 0xD8, 0xBA, 0x27, 0x0B, 0x39, 0x43,
+        0x23, 0x55, 0xFF, 0xB4,
+        /* x */
+        0xB7, 0x0E, 0x0C, 0xBD, 0x6B, 0xB4, 0xBF, 0x7F, 0x32, 0x13, 0x90, 0xB9,
+        0x4A, 0x03, 0xC1, 0xD3, 0x56, 0xC2, 0x11, 0x22, 0x34, 0x32, 0x80, 0xD6,
+        0x11, 0x5C, 0x1D, 0x21,
+        /* y */
+        0xbd, 0x37, 0x63, 0x88, 0xb5, 0xf7, 0x23, 0xfb, 0x4c, 0x22, 0xdf, 0xe6,
+        0xcd, 0x43, 0x75, 0xa0, 0x5a, 0x07, 0x47, 0x64, 0x44, 0xd5, 0x81, 0x99,
+        0x85, 0x00, 0x7e, 0x34,
+        /* order */
+        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+        0xFF, 0xFF, 0x16, 0xA2, 0xE0, 0xB8, 0xF0, 0x3E, 0x13, 0xDD, 0x29, 0x45,
+        0x5C, 0x5C, 0x2A, 0x3D
+    }
+};
+
+#ifndef OPENSSL_NO_EC2M
+
+/* characteristic two curves */
+static const struct {
+    EC_CURVE_DATA h;
+    unsigned char data[20 + 15 * 6];
+} _EC_SECG_CHAR2_113R1 = {
+    {
+        NID_X9_62_characteristic_two_field, 20, 15, 2
+    },
+    {
+        /* seed */
+        0x10, 0xE7, 0x23, 0xAB, 0x14, 0xD6, 0x96, 0xE6, 0x76, 0x87, 0x56, 0x15,
+        0x17, 0x56, 0xFE, 0xBF, 0x8F, 0xCB, 0x49, 0xA9,
+        /* p */
+        0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x02, 0x01,
+        /* a */
+        0x00, 0x30, 0x88, 0x25, 0x0C, 0xA6, 0xE7, 0xC7, 0xFE, 0x64, 0x9C, 0xE8,
+        0x58, 0x20, 0xF7,
+        /* b */
+        0x00, 0xE8, 0xBE, 0xE4, 0xD3, 0xE2, 0x26, 0x07, 0x44, 0x18, 0x8B, 0xE0,
+        0xE9, 0xC7, 0x23,
+        /* x */
+        0x00, 0x9D, 0x73, 0x61, 0x6F, 0x35, 0xF4, 0xAB, 0x14, 0x07, 0xD7, 0x35,
+        0x62, 0xC1, 0x0F,
+        /* y */
+        0x00, 0xA5, 0x28, 0x30, 0x27, 0x79, 0x58, 0xEE, 0x84, 0xD1, 0x31, 0x5E,
+        0xD3, 0x18, 0x86,
+        /* order */
+        0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD9, 0xCC, 0xEC, 0x8A,
+        0x39, 0xE5, 0x6F
+    }
+};
+
+static const struct {
+    EC_CURVE_DATA h;
+    unsigned char data[20 + 15 * 6];
+} _EC_SECG_CHAR2_113R2 = {
+    {
+        NID_X9_62_characteristic_two_field, 20, 15, 2
+    },
+    {
+        /* seed */
+        0x10, 0xC0, 0xFB, 0x15, 0x76, 0x08, 0x60, 0xDE, 0xF1, 0xEE, 0xF4, 0xD6,
+        0x96, 0xE6, 0x76, 0x87, 0x56, 0x15, 0x17, 0x5D,
+        /* p */
+        0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x02, 0x01,
+        /* a */
+        0x00, 0x68, 0x99, 0x18, 0xDB, 0xEC, 0x7E, 0x5A, 0x0D, 0xD6, 0xDF, 0xC0,
+        0xAA, 0x55, 0xC7,
+        /* b */
+        0x00, 0x95, 0xE9, 0xA9, 0xEC, 0x9B, 0x29, 0x7B, 0xD4, 0xBF, 0x36, 0xE0,
+        0x59, 0x18, 0x4F,
+        /* x */
+        0x01, 0xA5, 0x7A, 0x6A, 0x7B, 0x26, 0xCA, 0x5E, 0xF5, 0x2F, 0xCD, 0xB8,
+        0x16, 0x47, 0x97,
+        /* y */
+        0x00, 0xB3, 0xAD, 0xC9, 0x4E, 0xD1, 0xFE, 0x67, 0x4C, 0x06, 0xE6, 0x95,
+        0xBA, 0xBA, 0x1D,
+        /* order */
+        0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x08, 0x78, 0x9B, 0x24,
+        0x96, 0xAF, 0x93
+    }
+};
+
+static const struct {
+    EC_CURVE_DATA h;
+    unsigned char data[20 + 17 * 6];
+} _EC_SECG_CHAR2_131R1 = {
+    {
+        NID_X9_62_characteristic_two_field, 20, 17, 2
+    },
+    {
+        /* seed */
+        0x4D, 0x69, 0x6E, 0x67, 0x68, 0x75, 0x61, 0x51, 0x75, 0x98, 0x5B, 0xD3,
+        0xAD, 0xBA, 0xDA, 0x21, 0xB4, 0x3A, 0x97, 0xE2,
+        /* p */
+        0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x01, 0x0D,
+        /* a */
+        0x07, 0xA1, 0x1B, 0x09, 0xA7, 0x6B, 0x56, 0x21, 0x44, 0x41, 0x8F, 0xF3,
+        0xFF, 0x8C, 0x25, 0x70, 0xB8,
+        /* b */
+        0x02, 0x17, 0xC0, 0x56, 0x10, 0x88, 0x4B, 0x63, 0xB9, 0xC6, 0xC7, 0x29,
+        0x16, 0x78, 0xF9, 0xD3, 0x41,
+        /* x */
+        0x00, 0x81, 0xBA, 0xF9, 0x1F, 0xDF, 0x98, 0x33, 0xC4, 0x0F, 0x9C, 0x18,
+        0x13, 0x43, 0x63, 0x83, 0x99,
+        /* y */
+        0x07, 0x8C, 0x6E, 0x7E, 0xA3, 0x8C, 0x00, 0x1F, 0x73, 0xC8, 0x13, 0x4B,
+        0x1B, 0x4E, 0xF9, 0xE1, 0x50,
+        /* order */
+        0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x31, 0x23, 0x95,
+        0x3A, 0x94, 0x64, 0xB5, 0x4D
+    }
+};
+
+static const struct {
+    EC_CURVE_DATA h;
+    unsigned char data[20 + 17 * 6];
+} _EC_SECG_CHAR2_131R2 = {
+    {
+        NID_X9_62_characteristic_two_field, 20, 17, 2
+    },
+    {
+        /* seed */
+        0x98, 0x5B, 0xD3, 0xAD, 0xBA, 0xD4, 0xD6, 0x96, 0xE6, 0x76, 0x87, 0x56,
+        0x15, 0x17, 0x5A, 0x21, 0xB4, 0x3A, 0x97, 0xE3,
+        /* p */
+        0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x01, 0x0D,
+        /* a */
+        0x03, 0xE5, 0xA8, 0x89, 0x19, 0xD7, 0xCA, 0xFC, 0xBF, 0x41, 0x5F, 0x07,
+        0xC2, 0x17, 0x65, 0x73, 0xB2,
+        /* b */
+        0x04, 0xB8, 0x26, 0x6A, 0x46, 0xC5, 0x56, 0x57, 0xAC, 0x73, 0x4C, 0xE3,
+        0x8F, 0x01, 0x8F, 0x21, 0x92,
+        /* x */
+        0x03, 0x56, 0xDC, 0xD8, 0xF2, 0xF9, 0x50, 0x31, 0xAD, 0x65, 0x2D, 0x23,
+        0x95, 0x1B, 0xB3, 0x66, 0xA8,
+        /* y */
+        0x06, 0x48, 0xF0, 0x6D, 0x86, 0x79, 0x40, 0xA5, 0x36, 0x6D, 0x9E, 0x26,
+        0x5D, 0xE9, 0xEB, 0x24, 0x0F,
+        /* order */
+        0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x69, 0x54, 0xA2,
+        0x33, 0x04, 0x9B, 0xA9, 0x8F
+    }
+};
+
+static const struct {
+    EC_CURVE_DATA h;
+    unsigned char data[0 + 21 * 6];
+} _EC_NIST_CHAR2_163K = {
+    {
+        NID_X9_62_characteristic_two_field, 0, 21, 2
+    },
+    {
+        /* no seed */
+        /* p */
+        0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC9,
+        /* a */
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+        /* b */
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+        /* x */
+        0x02, 0xFE, 0x13, 0xC0, 0x53, 0x7B, 0xBC, 0x11, 0xAC, 0xAA, 0x07, 0xD7,
+        0x93, 0xDE, 0x4E, 0x6D, 0x5E, 0x5C, 0x94, 0xEE, 0xE8,
+        /* y */
+        0x02, 0x89, 0x07, 0x0F, 0xB0, 0x5D, 0x38, 0xFF, 0x58, 0x32, 0x1F, 0x2E,
+        0x80, 0x05, 0x36, 0xD5, 0x38, 0xCC, 0xDA, 0xA3, 0xD9,
+        /* order */
+        0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x01,
+        0x08, 0xA2, 0xE0, 0xCC, 0x0D, 0x99, 0xF8, 0xA5, 0xEF
+    }
+};
+
+static const struct {
+    EC_CURVE_DATA h;
+    unsigned char data[0 + 21 * 6];
+} _EC_SECG_CHAR2_163R1 = {
+    {
+        NID_X9_62_characteristic_two_field, 0, 21, 2
+    },
+    {
+        /* p */
+        0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC9,
+        /* a */
+        0x07, 0xB6, 0x88, 0x2C, 0xAA, 0xEF, 0xA8, 0x4F, 0x95, 0x54, 0xFF, 0x84,
+        0x28, 0xBD, 0x88, 0xE2, 0x46, 0xD2, 0x78, 0x2A, 0xE2,
+        /* b */
+        0x07, 0x13, 0x61, 0x2D, 0xCD, 0xDC, 0xB4, 0x0A, 0xAB, 0x94, 0x6B, 0xDA,
+        0x29, 0xCA, 0x91, 0xF7, 0x3A, 0xF9, 0x58, 0xAF, 0xD9,
+        /* x */
+        0x03, 0x69, 0x97, 0x96, 0x97, 0xAB, 0x43, 0x89, 0x77, 0x89, 0x56, 0x67,
+        0x89, 0x56, 0x7F, 0x78, 0x7A, 0x78, 0x76, 0xA6, 0x54,
+        /* y */
+        0x00, 0x43, 0x5E, 0xDB, 0x42, 0xEF, 0xAF, 0xB2, 0x98, 0x9D, 0x51, 0xFE,
+        0xFC, 0xE3, 0xC8, 0x09, 0x88, 0xF4, 0x1F, 0xF8, 0x83,
+        /* order */
+        0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x48,
+        0xAA, 0xB6, 0x89, 0xC2, 0x9C, 0xA7, 0x10, 0x27, 0x9B
+    }
+};
+
+static const struct {
+    EC_CURVE_DATA h;
+    unsigned char data[0 + 21 * 6];
+} _EC_NIST_CHAR2_163B = {
+    {
+        NID_X9_62_characteristic_two_field, 0, 21, 2
+    },
+    {
+        /* p */
+        0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC9,
+        /* a */
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+        /* b */
+        0x02, 0x0A, 0x60, 0x19, 0x07, 0xB8, 0xC9, 0x53, 0xCA, 0x14, 0x81, 0xEB,
+        0x10, 0x51, 0x2F, 0x78, 0x74, 0x4A, 0x32, 0x05, 0xFD,
+        /* x */
+        0x03, 0xF0, 0xEB, 0xA1, 0x62, 0x86, 0xA2, 0xD5, 0x7E, 0xA0, 0x99, 0x11,
+        0x68, 0xD4, 0x99, 0x46, 0x37, 0xE8, 0x34, 0x3E, 0x36,
+        /* y */
+        0x00, 0xD5, 0x1F, 0xBC, 0x6C, 0x71, 0xA0, 0x09, 0x4F, 0xA2, 0xCD, 0xD5,
+        0x45, 0xB1, 0x1C, 0x5C, 0x0C, 0x79, 0x73, 0x24, 0xF1,
+        /* order */
+        0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x92,
+        0xFE, 0x77, 0xE7, 0x0C, 0x12, 0xA4, 0x23, 0x4C, 0x33
+    }
+};
+
+static const struct {
+    EC_CURVE_DATA h;
+    unsigned char data[20 + 25 * 6];
+} _EC_SECG_CHAR2_193R1 = {
+    {
+        NID_X9_62_characteristic_two_field, 20, 25, 2
+    },
+    {
+        /* seed */
+        0x10, 0x3F, 0xAE, 0xC7, 0x4D, 0x69, 0x6E, 0x67, 0x68, 0x75, 0x61, 0x51,
+        0x75, 0x77, 0x7F, 0xC5, 0xB1, 0x91, 0xEF, 0x30,
+        /* p */
+        0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
+        0x01,
+        /* a */
+        0x00, 0x17, 0x85, 0x8F, 0xEB, 0x7A, 0x98, 0x97, 0x51, 0x69, 0xE1, 0x71,
+        0xF7, 0x7B, 0x40, 0x87, 0xDE, 0x09, 0x8A, 0xC8, 0xA9, 0x11, 0xDF, 0x7B,
+        0x01,
+        /* b */
+        0x00, 0xFD, 0xFB, 0x49, 0xBF, 0xE6, 0xC3, 0xA8, 0x9F, 0xAC, 0xAD, 0xAA,
+        0x7A, 0x1E, 0x5B, 0xBC, 0x7C, 0xC1, 0xC2, 0xE5, 0xD8, 0x31, 0x47, 0x88,
+        0x14,
+        /* x */
+        0x01, 0xF4, 0x81, 0xBC, 0x5F, 0x0F, 0xF8, 0x4A, 0x74, 0xAD, 0x6C, 0xDF,
+        0x6F, 0xDE, 0xF4, 0xBF, 0x61, 0x79, 0x62, 0x53, 0x72, 0xD8, 0xC0, 0xC5,
+        0xE1,
+        /* y */
+        0x00, 0x25, 0xE3, 0x99, 0xF2, 0x90, 0x37, 0x12, 0xCC, 0xF3, 0xEA, 0x9E,
+        0x3A, 0x1A, 0xD1, 0x7F, 0xB0, 0xB3, 0x20, 0x1B, 0x6A, 0xF7, 0xCE, 0x1B,
+        0x05,
+        /* order */
+        0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0xC7, 0xF3, 0x4A, 0x77, 0x8F, 0x44, 0x3A, 0xCC, 0x92, 0x0E, 0xBA,
+        0x49
+    }
+};
+
+static const struct {
+    EC_CURVE_DATA h;
+    unsigned char data[20 + 25 * 6];
+} _EC_SECG_CHAR2_193R2 = {
+    {
+        NID_X9_62_characteristic_two_field, 20, 25, 2
+    },
+    {
+        /* seed */
+        0x10, 0xB7, 0xB4, 0xD6, 0x96, 0xE6, 0x76, 0x87, 0x56, 0x15, 0x17, 0x51,
+        0x37, 0xC8, 0xA1, 0x6F, 0xD0, 0xDA, 0x22, 0x11,
+        /* p */
+        0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
+        0x01,
+        /* a */
+        0x01, 0x63, 0xF3, 0x5A, 0x51, 0x37, 0xC2, 0xCE, 0x3E, 0xA6, 0xED, 0x86,
+        0x67, 0x19, 0x0B, 0x0B, 0xC4, 0x3E, 0xCD, 0x69, 0x97, 0x77, 0x02, 0x70,
+        0x9B,
+        /* b */
+        0x00, 0xC9, 0xBB, 0x9E, 0x89, 0x27, 0xD4, 0xD6, 0x4C, 0x37, 0x7E, 0x2A,
+        0xB2, 0x85, 0x6A, 0x5B, 0x16, 0xE3, 0xEF, 0xB7, 0xF6, 0x1D, 0x43, 0x16,
+        0xAE,
+        /* x */
+        0x00, 0xD9, 0xB6, 0x7D, 0x19, 0x2E, 0x03, 0x67, 0xC8, 0x03, 0xF3, 0x9E,
+        0x1A, 0x7E, 0x82, 0xCA, 0x14, 0xA6, 0x51, 0x35, 0x0A, 0xAE, 0x61, 0x7E,
+        0x8F,
+        /* y */
+        0x01, 0xCE, 0x94, 0x33, 0x56, 0x07, 0xC3, 0x04, 0xAC, 0x29, 0xE7, 0xDE,
+        0xFB, 0xD9, 0xCA, 0x01, 0xF5, 0x96, 0xF9, 0x27, 0x22, 0x4C, 0xDE, 0xCF,
+        0x6C,
+        /* order */
+        0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x01, 0x5A, 0xAB, 0x56, 0x1B, 0x00, 0x54, 0x13, 0xCC, 0xD4, 0xEE, 0x99,
+        0xD5
+    }
+};
+
+static const struct {
+    EC_CURVE_DATA h;
+    unsigned char data[0 + 30 * 6];
+} _EC_NIST_CHAR2_233K = {
+    {
+        NID_X9_62_characteristic_two_field, 0, 30, 4
+    },
+    {
+        /* no seed */
+        0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+        /* a */
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        /* b */
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+        /* x */
+        0x01, 0x72, 0x32, 0xBA, 0x85, 0x3A, 0x7E, 0x73, 0x1A, 0xF1, 0x29, 0xF2,
+        0x2F, 0xF4, 0x14, 0x95, 0x63, 0xA4, 0x19, 0xC2, 0x6B, 0xF5, 0x0A, 0x4C,
+        0x9D, 0x6E, 0xEF, 0xAD, 0x61, 0x26,
+        /* y */
+        0x01, 0xDB, 0x53, 0x7D, 0xEC, 0xE8, 0x19, 0xB7, 0xF7, 0x0F, 0x55, 0x5A,
+        0x67, 0xC4, 0x27, 0xA8, 0xCD, 0x9B, 0xF1, 0x8A, 0xEB, 0x9B, 0x56, 0xE0,
+        0xC1, 0x10, 0x56, 0xFA, 0xE6, 0xA3,
+        /* order */
+        0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x06, 0x9D, 0x5B, 0xB9, 0x15, 0xBC, 0xD4, 0x6E, 0xFB,
+        0x1A, 0xD5, 0xF1, 0x73, 0xAB, 0xDF
+    }
+};
+
+static const struct {
+    EC_CURVE_DATA h;
+    unsigned char data[20 + 30 * 6];
+} _EC_NIST_CHAR2_233B = {
+    {
+        NID_X9_62_characteristic_two_field, 20, 30, 2
+    },
+    {
+        /* seed */
+        0x74, 0xD5, 0x9F, 0xF0, 0x7F, 0x6B, 0x41, 0x3D, 0x0E, 0xA1, 0x4B, 0x34,
+        0x4B, 0x20, 0xA2, 0xDB, 0x04, 0x9B, 0x50, 0xC3,
+        /* p */
+        0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+        /* a */
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+        /* b */
+        0x00, 0x66, 0x64, 0x7E, 0xDE, 0x6C, 0x33, 0x2C, 0x7F, 0x8C, 0x09, 0x23,
+        0xBB, 0x58, 0x21, 0x3B, 0x33, 0x3B, 0x20, 0xE9, 0xCE, 0x42, 0x81, 0xFE,
+        0x11, 0x5F, 0x7D, 0x8F, 0x90, 0xAD,
+        /* x */
+        0x00, 0xFA, 0xC9, 0xDF, 0xCB, 0xAC, 0x83, 0x13, 0xBB, 0x21, 0x39, 0xF1,
+        0xBB, 0x75, 0x5F, 0xEF, 0x65, 0xBC, 0x39, 0x1F, 0x8B, 0x36, 0xF8, 0xF8,
+        0xEB, 0x73, 0x71, 0xFD, 0x55, 0x8B,
+        /* y */
+        0x01, 0x00, 0x6A, 0x08, 0xA4, 0x19, 0x03, 0x35, 0x06, 0x78, 0xE5, 0x85,
+        0x28, 0xBE, 0xBF, 0x8A, 0x0B, 0xEF, 0xF8, 0x67, 0xA7, 0xCA, 0x36, 0x71,
+        0x6F, 0x7E, 0x01, 0xF8, 0x10, 0x52,
+        /* order */
+        0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x13, 0xE9, 0x74, 0xE7, 0x2F, 0x8A, 0x69, 0x22, 0x03,
+        0x1D, 0x26, 0x03, 0xCF, 0xE0, 0xD7
+    }
+};
+
+static const struct {
+    EC_CURVE_DATA h;
+    unsigned char data[0 + 30 * 6];
+} _EC_SECG_CHAR2_239K1 = {
+    {
+        NID_X9_62_characteristic_two_field, 0, 30, 4
+    },
+    {
+        /* no seed */
+        0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+        /* a */
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        /* b */
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+        /* x */
+        0x29, 0xA0, 0xB6, 0xA8, 0x87, 0xA9, 0x83, 0xE9, 0x73, 0x09, 0x88, 0xA6,
+        0x87, 0x27, 0xA8, 0xB2, 0xD1, 0x26, 0xC4, 0x4C, 0xC2, 0xCC, 0x7B, 0x2A,
+        0x65, 0x55, 0x19, 0x30, 0x35, 0xDC,
+        /* y */
+        0x76, 0x31, 0x08, 0x04, 0xF1, 0x2E, 0x54, 0x9B, 0xDB, 0x01, 0x1C, 0x10,
+        0x30, 0x89, 0xE7, 0x35, 0x10, 0xAC, 0xB2, 0x75, 0xFC, 0x31, 0x2A, 0x5D,
+        0xC6, 0xB7, 0x65, 0x53, 0xF0, 0xCA,
+        /* order */
+        0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x5A, 0x79, 0xFE, 0xC6, 0x7C, 0xB6, 0xE9, 0x1F, 0x1C,
+        0x1D, 0xA8, 0x00, 0xE4, 0x78, 0xA5
+    }
+};
+
+static const struct {
+    EC_CURVE_DATA h;
+    unsigned char data[0 + 36 * 6];
+} _EC_NIST_CHAR2_283K = {
+    {
+        NID_X9_62_characteristic_two_field, 0, 36, 4
+    },
+    {
+        /* no seed */
+        0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0xA1,
+        /* a */
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        /* b */
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+        /* x */
+        0x05, 0x03, 0x21, 0x3F, 0x78, 0xCA, 0x44, 0x88, 0x3F, 0x1A, 0x3B, 0x81,
+        0x62, 0xF1, 0x88, 0xE5, 0x53, 0xCD, 0x26, 0x5F, 0x23, 0xC1, 0x56, 0x7A,
+        0x16, 0x87, 0x69, 0x13, 0xB0, 0xC2, 0xAC, 0x24, 0x58, 0x49, 0x28, 0x36,
+        /* y */
+        0x01, 0xCC, 0xDA, 0x38, 0x0F, 0x1C, 0x9E, 0x31, 0x8D, 0x90, 0xF9, 0x5D,
+        0x07, 0xE5, 0x42, 0x6F, 0xE8, 0x7E, 0x45, 0xC0, 0xE8, 0x18, 0x46, 0x98,
+        0xE4, 0x59, 0x62, 0x36, 0x4E, 0x34, 0x11, 0x61, 0x77, 0xDD, 0x22, 0x59,
+        /* order */
+        0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE9, 0xAE, 0x2E, 0xD0, 0x75, 0x77,
+        0x26, 0x5D, 0xFF, 0x7F, 0x94, 0x45, 0x1E, 0x06, 0x1E, 0x16, 0x3C, 0x61
+    }
+};
+
+static const struct {
+    EC_CURVE_DATA h;
+    unsigned char data[20 + 36 * 6];
+} _EC_NIST_CHAR2_283B = {
+    {
+        NID_X9_62_characteristic_two_field, 20, 36, 2
+    },
+    {
+        /* no seed */
+        0x77, 0xE2, 0xB0, 0x73, 0x70, 0xEB, 0x0F, 0x83, 0x2A, 0x6D, 0xD5, 0xB6,
+        0x2D, 0xFC, 0x88, 0xCD, 0x06, 0xBB, 0x84, 0xBE,
+        /* p */
+        0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0xA1,
+        /* a */
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+        /* b */
+        0x02, 0x7B, 0x68, 0x0A, 0xC8, 0xB8, 0x59, 0x6D, 0xA5, 0xA4, 0xAF, 0x8A,
+        0x19, 0xA0, 0x30, 0x3F, 0xCA, 0x97, 0xFD, 0x76, 0x45, 0x30, 0x9F, 0xA2,
+        0xA5, 0x81, 0x48, 0x5A, 0xF6, 0x26, 0x3E, 0x31, 0x3B, 0x79, 0xA2, 0xF5,
+        /* x */
+        0x05, 0xF9, 0x39, 0x25, 0x8D, 0xB7, 0xDD, 0x90, 0xE1, 0x93, 0x4F, 0x8C,
+        0x70, 0xB0, 0xDF, 0xEC, 0x2E, 0xED, 0x25, 0xB8, 0x55, 0x7E, 0xAC, 0x9C,
+        0x80, 0xE2, 0xE1, 0x98, 0xF8, 0xCD, 0xBE, 0xCD, 0x86, 0xB1, 0x20, 0x53,
+        /* y */
+        0x03, 0x67, 0x68, 0x54, 0xFE, 0x24, 0x14, 0x1C, 0xB9, 0x8F, 0xE6, 0xD4,
+        0xB2, 0x0D, 0x02, 0xB4, 0x51, 0x6F, 0xF7, 0x02, 0x35, 0x0E, 0xDD, 0xB0,
+        0x82, 0x67, 0x79, 0xC8, 0x13, 0xF0, 0xDF, 0x45, 0xBE, 0x81, 0x12, 0xF4,
+        /* order */
+        0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0x90, 0x39, 0x96, 0x60, 0xFC,
+        0x93, 0x8A, 0x90, 0x16, 0x5B, 0x04, 0x2A, 0x7C, 0xEF, 0xAD, 0xB3, 0x07
+    }
+};
+
+static const struct {
+    EC_CURVE_DATA h;
+    unsigned char data[0 + 52 * 6];
+} _EC_NIST_CHAR2_409K = {
+    {
+        NID_X9_62_characteristic_two_field, 0, 52, 4
+    },
+    {
+        /* no seed */
+        /* p */
+        0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x01,
+        /* a */
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00,
+        /* b */
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x01,
+        /* x */
+        0x00, 0x60, 0xF0, 0x5F, 0x65, 0x8F, 0x49, 0xC1, 0xAD, 0x3A, 0xB1, 0x89,
+        0x0F, 0x71, 0x84, 0x21, 0x0E, 0xFD, 0x09, 0x87, 0xE3, 0x07, 0xC8, 0x4C,
+        0x27, 0xAC, 0xCF, 0xB8, 0xF9, 0xF6, 0x7C, 0xC2, 0xC4, 0x60, 0x18, 0x9E,
+        0xB5, 0xAA, 0xAA, 0x62, 0xEE, 0x22, 0x2E, 0xB1, 0xB3, 0x55, 0x40, 0xCF,
+        0xE9, 0x02, 0x37, 0x46,
+        /* y */
+        0x01, 0xE3, 0x69, 0x05, 0x0B, 0x7C, 0x4E, 0x42, 0xAC, 0xBA, 0x1D, 0xAC,
+        0xBF, 0x04, 0x29, 0x9C, 0x34, 0x60, 0x78, 0x2F, 0x91, 0x8E, 0xA4, 0x27,
+        0xE6, 0x32, 0x51, 0x65, 0xE9, 0xEA, 0x10, 0xE3, 0xDA, 0x5F, 0x6C, 0x42,
+        0xE9, 0xC5, 0x52, 0x15, 0xAA, 0x9C, 0xA2, 0x7A, 0x58, 0x63, 0xEC, 0x48,
+        0xD8, 0xE0, 0x28, 0x6B,
+        /* order */
+        0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+        0xFF, 0xFF, 0xFE, 0x5F, 0x83, 0xB2, 0xD4, 0xEA, 0x20, 0x40, 0x0E, 0xC4,
+        0x55, 0x7D, 0x5E, 0xD3, 0xE3, 0xE7, 0xCA, 0x5B, 0x4B, 0x5C, 0x83, 0xB8,
+        0xE0, 0x1E, 0x5F, 0xCF
+    }
+};
+
+static const struct {
+    EC_CURVE_DATA h;
+    unsigned char data[20 + 52 * 6];
+} _EC_NIST_CHAR2_409B = {
+    {
+        NID_X9_62_characteristic_two_field, 20, 52, 2
+    },
+    {
+        /* seed */
+        0x40, 0x99, 0xB5, 0xA4, 0x57, 0xF9, 0xD6, 0x9F, 0x79, 0x21, 0x3D, 0x09,
+        0x4C, 0x4B, 0xCD, 0x4D, 0x42, 0x62, 0x21, 0x0B,
+        /* p */
+        0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x01,
+        /* a */
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x01,
+        /* b */
+        0x00, 0x21, 0xA5, 0xC2, 0xC8, 0xEE, 0x9F, 0xEB, 0x5C, 0x4B, 0x9A, 0x75,
+        0x3B, 0x7B, 0x47, 0x6B, 0x7F, 0xD6, 0x42, 0x2E, 0xF1, 0xF3, 0xDD, 0x67,
+        0x47, 0x61, 0xFA, 0x99, 0xD6, 0xAC, 0x27, 0xC8, 0xA9, 0xA1, 0x97, 0xB2,
+        0x72, 0x82, 0x2F, 0x6C, 0xD5, 0x7A, 0x55, 0xAA, 0x4F, 0x50, 0xAE, 0x31,
+        0x7B, 0x13, 0x54, 0x5F,
+        /* x */
+        0x01, 0x5D, 0x48, 0x60, 0xD0, 0x88, 0xDD, 0xB3, 0x49, 0x6B, 0x0C, 0x60,
+        0x64, 0x75, 0x62, 0x60, 0x44, 0x1C, 0xDE, 0x4A, 0xF1, 0x77, 0x1D, 0x4D,
+        0xB0, 0x1F, 0xFE, 0x5B, 0x34, 0xE5, 0x97, 0x03, 0xDC, 0x25, 0x5A, 0x86,
+        0x8A, 0x11, 0x80, 0x51, 0x56, 0x03, 0xAE, 0xAB, 0x60, 0x79, 0x4E, 0x54,
+        0xBB, 0x79, 0x96, 0xA7,
+        /* y */
+        0x00, 0x61, 0xB1, 0xCF, 0xAB, 0x6B, 0xE5, 0xF3, 0x2B, 0xBF, 0xA7, 0x83,
+        0x24, 0xED, 0x10, 0x6A, 0x76, 0x36, 0xB9, 0xC5, 0xA7, 0xBD, 0x19, 0x8D,
+        0x01, 0x58, 0xAA, 0x4F, 0x54, 0x88, 0xD0, 0x8F, 0x38, 0x51, 0x4F, 0x1F,
+        0xDF, 0x4B, 0x4F, 0x40, 0xD2, 0x18, 0x1B, 0x36, 0x81, 0xC3, 0x64, 0xBA,
+        0x02, 0x73, 0xC7, 0x06,
+        /* order */
+        0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x01, 0xE2, 0xAA, 0xD6, 0xA6, 0x12, 0xF3, 0x33, 0x07, 0xBE,
+        0x5F, 0xA4, 0x7C, 0x3C, 0x9E, 0x05, 0x2F, 0x83, 0x81, 0x64, 0xCD, 0x37,
+        0xD9, 0xA2, 0x11, 0x73
+    }
+};
+
+static const struct {
+    EC_CURVE_DATA h;
+    unsigned char data[0 + 72 * 6];
+} _EC_NIST_CHAR2_571K = {
+    {
+        NID_X9_62_characteristic_two_field, 0, 72, 4
+    },
+    {
+        /* no seed */
+        /* p */
+        0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x25,
+        /* a */
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        /* b */
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+        /* x */
+        0x02, 0x6E, 0xB7, 0xA8, 0x59, 0x92, 0x3F, 0xBC, 0x82, 0x18, 0x96, 0x31,
+        0xF8, 0x10, 0x3F, 0xE4, 0xAC, 0x9C, 0xA2, 0x97, 0x00, 0x12, 0xD5, 0xD4,
+        0x60, 0x24, 0x80, 0x48, 0x01, 0x84, 0x1C, 0xA4, 0x43, 0x70, 0x95, 0x84,
+        0x93, 0xB2, 0x05, 0xE6, 0x47, 0xDA, 0x30, 0x4D, 0xB4, 0xCE, 0xB0, 0x8C,
+        0xBB, 0xD1, 0xBA, 0x39, 0x49, 0x47, 0x76, 0xFB, 0x98, 0x8B, 0x47, 0x17,
+        0x4D, 0xCA, 0x88, 0xC7, 0xE2, 0x94, 0x52, 0x83, 0xA0, 0x1C, 0x89, 0x72,
+        /* y */
+        0x03, 0x49, 0xDC, 0x80, 0x7F, 0x4F, 0xBF, 0x37, 0x4F, 0x4A, 0xEA, 0xDE,
+        0x3B, 0xCA, 0x95, 0x31, 0x4D, 0xD5, 0x8C, 0xEC, 0x9F, 0x30, 0x7A, 0x54,
+        0xFF, 0xC6, 0x1E, 0xFC, 0x00, 0x6D, 0x8A, 0x2C, 0x9D, 0x49, 0x79, 0xC0,
+        0xAC, 0x44, 0xAE, 0xA7, 0x4F, 0xBE, 0xBB, 0xB9, 0xF7, 0x72, 0xAE, 0xDC,
+        0xB6, 0x20, 0xB0, 0x1A, 0x7B, 0xA7, 0xAF, 0x1B, 0x32, 0x04, 0x30, 0xC8,
+        0x59, 0x19, 0x84, 0xF6, 0x01, 0xCD, 0x4C, 0x14, 0x3E, 0xF1, 0xC7, 0xA3,
+        /* order */
+        0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x13, 0x18, 0x50, 0xE1, 0xF1, 0x9A, 0x63, 0xE4, 0xB3, 0x91, 0xA8, 0xDB,
+        0x91, 0x7F, 0x41, 0x38, 0xB6, 0x30, 0xD8, 0x4B, 0xE5, 0xD6, 0x39, 0x38,
+        0x1E, 0x91, 0xDE, 0xB4, 0x5C, 0xFE, 0x77, 0x8F, 0x63, 0x7C, 0x10, 0x01
+    }
+};
+
+static const struct {
+    EC_CURVE_DATA h;
+    unsigned char data[20 + 72 * 6];
+} _EC_NIST_CHAR2_571B = {
+    {
+        NID_X9_62_characteristic_two_field, 20, 72, 2
+    },
+    {
+        /* seed */
+        0x2A, 0xA0, 0x58, 0xF7, 0x3A, 0x0E, 0x33, 0xAB, 0x48, 0x6B, 0x0F, 0x61,
+        0x04, 0x10, 0xC5, 0x3A, 0x7F, 0x13, 0x23, 0x10,
+        /* p */
+        0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x25,
+        /* a */
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+        /* b */
+        0x02, 0xF4, 0x0E, 0x7E, 0x22, 0x21, 0xF2, 0x95, 0xDE, 0x29, 0x71, 0x17,
+        0xB7, 0xF3, 0xD6, 0x2F, 0x5C, 0x6A, 0x97, 0xFF, 0xCB, 0x8C, 0xEF, 0xF1,
+        0xCD, 0x6B, 0xA8, 0xCE, 0x4A, 0x9A, 0x18, 0xAD, 0x84, 0xFF, 0xAB, 0xBD,
+        0x8E, 0xFA, 0x59, 0x33, 0x2B, 0xE7, 0xAD, 0x67, 0x56, 0xA6, 0x6E, 0x29,
+        0x4A, 0xFD, 0x18, 0x5A, 0x78, 0xFF, 0x12, 0xAA, 0x52, 0x0E, 0x4D, 0xE7,
+        0x39, 0xBA, 0xCA, 0x0C, 0x7F, 0xFE, 0xFF, 0x7F, 0x29, 0x55, 0x72, 0x7A,
+        /* x */
+        0x03, 0x03, 0x00, 0x1D, 0x34, 0xB8, 0x56, 0x29, 0x6C, 0x16, 0xC0, 0xD4,
+        0x0D, 0x3C, 0xD7, 0x75, 0x0A, 0x93, 0xD1, 0xD2, 0x95, 0x5F, 0xA8, 0x0A,
+        0xA5, 0xF4, 0x0F, 0xC8, 0xDB, 0x7B, 0x2A, 0xBD, 0xBD, 0xE5, 0x39, 0x50,
+        0xF4, 0xC0, 0xD2, 0x93, 0xCD, 0xD7, 0x11, 0xA3, 0x5B, 0x67, 0xFB, 0x14,
+        0x99, 0xAE, 0x60, 0x03, 0x86, 0x14, 0xF1, 0x39, 0x4A, 0xBF, 0xA3, 0xB4,
+        0xC8, 0x50, 0xD9, 0x27, 0xE1, 0xE7, 0x76, 0x9C, 0x8E, 0xEC, 0x2D, 0x19,
+        /* y */
+        0x03, 0x7B, 0xF2, 0x73, 0x42, 0xDA, 0x63, 0x9B, 0x6D, 0xCC, 0xFF, 0xFE,
+        0xB7, 0x3D, 0x69, 0xD7, 0x8C, 0x6C, 0x27, 0xA6, 0x00, 0x9C, 0xBB, 0xCA,
+        0x19, 0x80, 0xF8, 0x53, 0x39, 0x21, 0xE8, 0xA6, 0x84, 0x42, 0x3E, 0x43,
+        0xBA, 0xB0, 0x8A, 0x57, 0x62, 0x91, 0xAF, 0x8F, 0x46, 0x1B, 0xB2, 0xA8,
+        0xB3, 0x53, 0x1D, 0x2F, 0x04, 0x85, 0xC1, 0x9B, 0x16, 0xE2, 0xF1, 0x51,
+        0x6E, 0x23, 0xDD, 0x3C, 0x1A, 0x48, 0x27, 0xAF, 0x1B, 0x8A, 0xC1, 0x5B,
+        /* order */
+        0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+        0xE6, 0x61, 0xCE, 0x18, 0xFF, 0x55, 0x98, 0x73, 0x08, 0x05, 0x9B, 0x18,
+        0x68, 0x23, 0x85, 0x1E, 0xC7, 0xDD, 0x9C, 0xA1, 0x16, 0x1D, 0xE9, 0x3D,
+        0x51, 0x74, 0xD6, 0x6E, 0x83, 0x82, 0xE9, 0xBB, 0x2F, 0xE8, 0x4E, 0x47
+    }
+};
+
+static const struct {
+    EC_CURVE_DATA h;
+    unsigned char data[20 + 21 * 6];
+} _EC_X9_62_CHAR2_163V1 = {
+    {
+        NID_X9_62_characteristic_two_field, 20, 21, 2
+    },
+    {
+        /* seed */
+        0xD2, 0xC0, 0xFB, 0x15, 0x76, 0x08, 0x60, 0xDE, 0xF1, 0xEE, 0xF4, 0xD6,
+        0x96, 0xE6, 0x76, 0x87, 0x56, 0x15, 0x17, 0x54,
+        /* p */
+        0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x07,
+        /* a */
+        0x07, 0x25, 0x46, 0xB5, 0x43, 0x52, 0x34, 0xA4, 0x22, 0xE0, 0x78, 0x96,
+        0x75, 0xF4, 0x32, 0xC8, 0x94, 0x35, 0xDE, 0x52, 0x42,
+        /* b */
+        0x00, 0xC9, 0x51, 0x7D, 0x06, 0xD5, 0x24, 0x0D, 0x3C, 0xFF, 0x38, 0xC7,
+        0x4B, 0x20, 0xB6, 0xCD, 0x4D, 0x6F, 0x9D, 0xD4, 0xD9,
+        /* x */
+        0x07, 0xAF, 0x69, 0x98, 0x95, 0x46, 0x10, 0x3D, 0x79, 0x32, 0x9F, 0xCC,
+        0x3D, 0x74, 0x88, 0x0F, 0x33, 0xBB, 0xE8, 0x03, 0xCB,
+        /* y */
+        0x01, 0xEC, 0x23, 0x21, 0x1B, 0x59, 0x66, 0xAD, 0xEA, 0x1D, 0x3F, 0x87,
+        0xF7, 0xEA, 0x58, 0x48, 0xAE, 0xF0, 0xB7, 0xCA, 0x9F,
+        /* order */
+        0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xE6,
+        0x0F, 0xC8, 0x82, 0x1C, 0xC7, 0x4D, 0xAE, 0xAF, 0xC1
+    }
+};
+
+static const struct {
+    EC_CURVE_DATA h;
+    unsigned char data[20 + 21 * 6];
+} _EC_X9_62_CHAR2_163V2 = {
+    {
+        NID_X9_62_characteristic_two_field, 20, 21, 2
+    },
+    {
+        /* seed */
+        0x53, 0x81, 0x4C, 0x05, 0x0D, 0x44, 0xD6, 0x96, 0xE6, 0x76, 0x87, 0x56,
+        0x15, 0x17, 0x58, 0x0C, 0xA4, 0xE2, 0x9F, 0xFD,
+        /* p */
+        0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x07,
+        /* a */
+        0x01, 0x08, 0xB3, 0x9E, 0x77, 0xC4, 0xB1, 0x08, 0xBE, 0xD9, 0x81, 0xED,
+        0x0E, 0x89, 0x0E, 0x11, 0x7C, 0x51, 0x1C, 0xF0, 0x72,
+        /* b */
+        0x06, 0x67, 0xAC, 0xEB, 0x38, 0xAF, 0x4E, 0x48, 0x8C, 0x40, 0x74, 0x33,
+        0xFF, 0xAE, 0x4F, 0x1C, 0x81, 0x16, 0x38, 0xDF, 0x20,
+        /* x */
+        0x00, 0x24, 0x26, 0x6E, 0x4E, 0xB5, 0x10, 0x6D, 0x0A, 0x96, 0x4D, 0x92,
+        0xC4, 0x86, 0x0E, 0x26, 0x71, 0xDB, 0x9B, 0x6C, 0xC5,
+        /* y */
+        0x07, 0x9F, 0x68, 0x4D, 0xDF, 0x66, 0x84, 0xC5, 0xCD, 0x25, 0x8B, 0x38,
+        0x90, 0x02, 0x1B, 0x23, 0x86, 0xDF, 0xD1, 0x9F, 0xC5,
+        /* order */
+        0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFD, 0xF6,
+        0x4D, 0xE1, 0x15, 0x1A, 0xDB, 0xB7, 0x8F, 0x10, 0xA7
+    }
+};
+
+static const struct {
+    EC_CURVE_DATA h;
+    unsigned char data[20 + 21 * 6];
+} _EC_X9_62_CHAR2_163V3 = {
+    {
+        NID_X9_62_characteristic_two_field, 20, 21, 2
+    },
+    {
+        /* seed */
+        0x50, 0xCB, 0xF1, 0xD9, 0x5C, 0xA9, 0x4D, 0x69, 0x6E, 0x67, 0x68, 0x75,
+        0x61, 0x51, 0x75, 0xF1, 0x6A, 0x36, 0xA3, 0xB8,
+        /* p */
+        0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x07,
+        /* a */
+        0x07, 0xA5, 0x26, 0xC6, 0x3D, 0x3E, 0x25, 0xA2, 0x56, 0xA0, 0x07, 0x69,
+        0x9F, 0x54, 0x47, 0xE3, 0x2A, 0xE4, 0x56, 0xB5, 0x0E,
+        /* b */
+        0x03, 0xF7, 0x06, 0x17, 0x98, 0xEB, 0x99, 0xE2, 0x38, 0xFD, 0x6F, 0x1B,
+        0xF9, 0x5B, 0x48, 0xFE, 0xEB, 0x48, 0x54, 0x25, 0x2B,
+        /* x */
+        0x02, 0xF9, 0xF8, 0x7B, 0x7C, 0x57, 0x4D, 0x0B, 0xDE, 0xCF, 0x8A, 0x22,
+        0xE6, 0x52, 0x47, 0x75, 0xF9, 0x8C, 0xDE, 0xBD, 0xCB,
+        /* y */
+        0x05, 0xB9, 0x35, 0x59, 0x0C, 0x15, 0x5E, 0x17, 0xEA, 0x48, 0xEB, 0x3F,
+        0xF3, 0x71, 0x8B, 0x89, 0x3D, 0xF5, 0x9A, 0x05, 0xD0,
+        /* order */
+        0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x1A,
+        0xEE, 0x14, 0x0F, 0x11, 0x0A, 0xFF, 0x96, 0x13, 0x09
+    }
+};
+
+static const struct {
+    EC_CURVE_DATA h;
+    unsigned char data[0 + 23 * 6];
+} _EC_X9_62_CHAR2_176V1 = {
+    {
+        NID_X9_62_characteristic_two_field, 0, 23, 0xFF6E
+    },
+    {
+        /* no seed */
+        /* p */
+        0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x07,
+        /* a */
+        0x00, 0xE4, 0xE6, 0xDB, 0x29, 0x95, 0x06, 0x5C, 0x40, 0x7D, 0x9D, 0x39,
+        0xB8, 0xD0, 0x96, 0x7B, 0x96, 0x70, 0x4B, 0xA8, 0xE9, 0xC9, 0x0B,
+        /* b */
+        0x00, 0x5D, 0xDA, 0x47, 0x0A, 0xBE, 0x64, 0x14, 0xDE, 0x8E, 0xC1, 0x33,
+        0xAE, 0x28, 0xE9, 0xBB, 0xD7, 0xFC, 0xEC, 0x0A, 0xE0, 0xFF, 0xF2,
+        /* x */
+        0x00, 0x8D, 0x16, 0xC2, 0x86, 0x67, 0x98, 0xB6, 0x00, 0xF9, 0xF0, 0x8B,
+        0xB4, 0xA8, 0xE8, 0x60, 0xF3, 0x29, 0x8C, 0xE0, 0x4A, 0x57, 0x98,
+        /* y */
+        0x00, 0x6F, 0xA4, 0x53, 0x9C, 0x2D, 0xAD, 0xDD, 0xD6, 0xBA, 0xB5, 0x16,
+        0x7D, 0x61, 0xB4, 0x36, 0xE1, 0xD9, 0x2B, 0xB1, 0x6A, 0x56, 0x2C,
+        /* order */
+        0x00, 0x00, 0x01, 0x00, 0x92, 0x53, 0x73, 0x97, 0xEC, 0xA4, 0xF6, 0x14,
+        0x57, 0x99, 0xD6, 0x2B, 0x0A, 0x19, 0xCE, 0x06, 0xFE, 0x26, 0xAD
+    }
+};
+
+static const struct {
+    EC_CURVE_DATA h;
+    unsigned char data[20 + 24 * 6];
+} _EC_X9_62_CHAR2_191V1 = {
+    {
+        NID_X9_62_characteristic_two_field, 20, 24, 2
+    },
+    {
+        /* seed */
+        0x4E, 0x13, 0xCA, 0x54, 0x27, 0x44, 0xD6, 0x96, 0xE6, 0x76, 0x87, 0x56,
+        0x15, 0x17, 0x55, 0x2F, 0x27, 0x9A, 0x8C, 0x84,
+        /* p */
+        0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x01,
+        /* a */
+        0x28, 0x66, 0x53, 0x7B, 0x67, 0x67, 0x52, 0x63, 0x6A, 0x68, 0xF5, 0x65,
+        0x54, 0xE1, 0x26, 0x40, 0x27, 0x6B, 0x64, 0x9E, 0xF7, 0x52, 0x62, 0x67,
+        /* b */
+        0x2E, 0x45, 0xEF, 0x57, 0x1F, 0x00, 0x78, 0x6F, 0x67, 0xB0, 0x08, 0x1B,
+        0x94, 0x95, 0xA3, 0xD9, 0x54, 0x62, 0xF5, 0xDE, 0x0A, 0xA1, 0x85, 0xEC,
+        /* x */
+        0x36, 0xB3, 0xDA, 0xF8, 0xA2, 0x32, 0x06, 0xF9, 0xC4, 0xF2, 0x99, 0xD7,
+        0xB2, 0x1A, 0x9C, 0x36, 0x91, 0x37, 0xF2, 0xC8, 0x4A, 0xE1, 0xAA, 0x0D,
+        /* y */
+        0x76, 0x5B, 0xE7, 0x34, 0x33, 0xB3, 0xF9, 0x5E, 0x33, 0x29, 0x32, 0xE7,
+        0x0E, 0xA2, 0x45, 0xCA, 0x24, 0x18, 0xEA, 0x0E, 0xF9, 0x80, 0x18, 0xFB,
+        /* order */
+        0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x04, 0xA2, 0x0E, 0x90, 0xC3, 0x90, 0x67, 0xC8, 0x93, 0xBB, 0xB9, 0xA5
+    }
+};
+
+static const struct {
+    EC_CURVE_DATA h;
+    unsigned char data[20 + 24 * 6];
+} _EC_X9_62_CHAR2_191V2 = {
+    {
+        NID_X9_62_characteristic_two_field, 20, 24, 4
+    },
+    {
+        /* seed */
+        0x08, 0x71, 0xEF, 0x2F, 0xEF, 0x24, 0xD6, 0x96, 0xE6, 0x76, 0x87, 0x56,
+        0x15, 0x17, 0x58, 0xBE, 0xE0, 0xD9, 0x5C, 0x15,
+        /* p */
+        0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x01,
+        /* a */
+        0x40, 0x10, 0x28, 0x77, 0x4D, 0x77, 0x77, 0xC7, 0xB7, 0x66, 0x6D, 0x13,
+        0x66, 0xEA, 0x43, 0x20, 0x71, 0x27, 0x4F, 0x89, 0xFF, 0x01, 0xE7, 0x18,
+        /* b */
+        0x06, 0x20, 0x04, 0x8D, 0x28, 0xBC, 0xBD, 0x03, 0xB6, 0x24, 0x9C, 0x99,
+        0x18, 0x2B, 0x7C, 0x8C, 0xD1, 0x97, 0x00, 0xC3, 0x62, 0xC4, 0x6A, 0x01,
+        /* x */
+        0x38, 0x09, 0xB2, 0xB7, 0xCC, 0x1B, 0x28, 0xCC, 0x5A, 0x87, 0x92, 0x6A,
+        0xAD, 0x83, 0xFD, 0x28, 0x78, 0x9E, 0x81, 0xE2, 0xC9, 0xE3, 0xBF, 0x10,
+        /* y */
+        0x17, 0x43, 0x43, 0x86, 0x62, 0x6D, 0x14, 0xF3, 0xDB, 0xF0, 0x17, 0x60,
+        0xD9, 0x21, 0x3A, 0x3E, 0x1C, 0xF3, 0x7A, 0xEC, 0x43, 0x7D, 0x66, 0x8A,
+        /* order */
+        0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x50, 0x50, 0x8C, 0xB8, 0x9F, 0x65, 0x28, 0x24, 0xE0, 0x6B, 0x81, 0x73
+    }
+};
+
+static const struct {
+    EC_CURVE_DATA h;
+    unsigned char data[20 + 24 * 6];
+} _EC_X9_62_CHAR2_191V3 = {
+    {
+        NID_X9_62_characteristic_two_field, 20, 24, 6
+    },
+    {
+        /* seed */
+        0xE0, 0x53, 0x51, 0x2D, 0xC6, 0x84, 0xD6, 0x96, 0xE6, 0x76, 0x87, 0x56,
+        0x15, 0x17, 0x50, 0x67, 0xAE, 0x78, 0x6D, 0x1F,
+        /* p */
+        0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x01,
+        /* a */
+        0x6C, 0x01, 0x07, 0x47, 0x56, 0x09, 0x91, 0x22, 0x22, 0x10, 0x56, 0x91,
+        0x1C, 0x77, 0xD7, 0x7E, 0x77, 0xA7, 0x77, 0xE7, 0xE7, 0xE7, 0x7F, 0xCB,
+        /* b */
+        0x71, 0xFE, 0x1A, 0xF9, 0x26, 0xCF, 0x84, 0x79, 0x89, 0xEF, 0xEF, 0x8D,
+        0xB4, 0x59, 0xF6, 0x63, 0x94, 0xD9, 0x0F, 0x32, 0xAD, 0x3F, 0x15, 0xE8,
+        /* x */
+        0x37, 0x5D, 0x4C, 0xE2, 0x4F, 0xDE, 0x43, 0x44, 0x89, 0xDE, 0x87, 0x46,
+        0xE7, 0x17, 0x86, 0x01, 0x50, 0x09, 0xE6, 0x6E, 0x38, 0xA9, 0x26, 0xDD,
+        /* y */
+        0x54, 0x5A, 0x39, 0x17, 0x61, 0x96, 0x57, 0x5D, 0x98, 0x59, 0x99, 0x36,
+        0x6E, 0x6A, 0xD3, 0x4C, 0xE0, 0xA7, 0x7C, 0xD7, 0x12, 0x7B, 0x06, 0xBE,
+        /* order */
+        0x15, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
+        0x61, 0x0C, 0x0B, 0x19, 0x68, 0x12, 0xBF, 0xB6, 0x28, 0x8A, 0x3E, 0xA3
+    }
+};
+
+static const struct {
+    EC_CURVE_DATA h;
+    unsigned char data[0 + 27 * 6];
+} _EC_X9_62_CHAR2_208W1 = {
+    {
+        NID_X9_62_characteristic_two_field, 0, 27, 0xFE48
+    },
+    {
+        /* no seed */
+        /* p */
+        0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x07,
+        /* a */
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00,
+        /* b */
+        0x00, 0xC8, 0x61, 0x9E, 0xD4, 0x5A, 0x62, 0xE6, 0x21, 0x2E, 0x11, 0x60,
+        0x34, 0x9E, 0x2B, 0xFA, 0x84, 0x44, 0x39, 0xFA, 0xFC, 0x2A, 0x3F, 0xD1,
+        0x63, 0x8F, 0x9E,
+        /* x */
+        0x00, 0x89, 0xFD, 0xFB, 0xE4, 0xAB, 0xE1, 0x93, 0xDF, 0x95, 0x59, 0xEC,
+        0xF0, 0x7A, 0xC0, 0xCE, 0x78, 0x55, 0x4E, 0x27, 0x84, 0xEB, 0x8C, 0x1E,
+        0xD1, 0xA5, 0x7A,
+        /* y */
+        0x00, 0x0F, 0x55, 0xB5, 0x1A, 0x06, 0xE7, 0x8E, 0x9A, 0xC3, 0x8A, 0x03,
+        0x5F, 0xF5, 0x20, 0xD8, 0xB0, 0x17, 0x81, 0xBE, 0xB1, 0xA6, 0xBB, 0x08,
+        0x61, 0x7D, 0xE3,
+        /* order */
+        0x00, 0x00, 0x01, 0x01, 0xBA, 0xF9, 0x5C, 0x97, 0x23, 0xC5, 0x7B, 0x6C,
+        0x21, 0xDA, 0x2E, 0xFF, 0x2D, 0x5E, 0xD5, 0x88, 0xBD, 0xD5, 0x71, 0x7E,
+        0x21, 0x2F, 0x9D
+    }
+};
+
+static const struct {
+    EC_CURVE_DATA h;
+    unsigned char data[20 + 30 * 6];
+} _EC_X9_62_CHAR2_239V1 = {
+    {
+        NID_X9_62_characteristic_two_field, 20, 30, 4
+    },
+    {
+        /* seed */
+        0xD3, 0x4B, 0x9A, 0x4D, 0x69, 0x6E, 0x67, 0x68, 0x75, 0x61, 0x51, 0x75,
+        0xCA, 0x71, 0xB9, 0x20, 0xBF, 0xEF, 0xB0, 0x5D,
+        /* p */
+        0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x10, 0x00, 0x00, 0x00, 0x01,
+        /* a */
+        0x32, 0x01, 0x08, 0x57, 0x07, 0x7C, 0x54, 0x31, 0x12, 0x3A, 0x46, 0xB8,
+        0x08, 0x90, 0x67, 0x56, 0xF5, 0x43, 0x42, 0x3E, 0x8D, 0x27, 0x87, 0x75,
+        0x78, 0x12, 0x57, 0x78, 0xAC, 0x76,
+        /* b */
+        0x79, 0x04, 0x08, 0xF2, 0xEE, 0xDA, 0xF3, 0x92, 0xB0, 0x12, 0xED, 0xEF,
+        0xB3, 0x39, 0x2F, 0x30, 0xF4, 0x32, 0x7C, 0x0C, 0xA3, 0xF3, 0x1F, 0xC3,
+        0x83, 0xC4, 0x22, 0xAA, 0x8C, 0x16,
+        /* x */
+        0x57, 0x92, 0x70, 0x98, 0xFA, 0x93, 0x2E, 0x7C, 0x0A, 0x96, 0xD3, 0xFD,
+        0x5B, 0x70, 0x6E, 0xF7, 0xE5, 0xF5, 0xC1, 0x56, 0xE1, 0x6B, 0x7E, 0x7C,
+        0x86, 0x03, 0x85, 0x52, 0xE9, 0x1D,
+        /* y */
+        0x61, 0xD8, 0xEE, 0x50, 0x77, 0xC3, 0x3F, 0xEC, 0xF6, 0xF1, 0xA1, 0x6B,
+        0x26, 0x8D, 0xE4, 0x69, 0xC3, 0xC7, 0x74, 0x4E, 0xA9, 0xA9, 0x71, 0x64,
+        0x9F, 0xC7, 0xA9, 0x61, 0x63, 0x05,
+        /* order */
+        0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x0F, 0x4D, 0x42, 0xFF, 0xE1, 0x49, 0x2A, 0x49, 0x93,
+        0xF1, 0xCA, 0xD6, 0x66, 0xE4, 0x47
+    }
+};
+
+static const struct {
+    EC_CURVE_DATA h;
+    unsigned char data[20 + 30 * 6];
+} _EC_X9_62_CHAR2_239V2 = {
+    {
+        NID_X9_62_characteristic_two_field, 20, 30, 6
+    },
+    {
+        /* seed */
+        0x2A, 0xA6, 0x98, 0x2F, 0xDF, 0xA4, 0xD6, 0x96, 0xE6, 0x76, 0x87, 0x56,
+        0x15, 0x17, 0x5D, 0x26, 0x67, 0x27, 0x27, 0x7D,
+        /* p */
+        0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x10, 0x00, 0x00, 0x00, 0x01,
+        /* a */
+        0x42, 0x30, 0x01, 0x77, 0x57, 0xA7, 0x67, 0xFA, 0xE4, 0x23, 0x98, 0x56,
+        0x9B, 0x74, 0x63, 0x25, 0xD4, 0x53, 0x13, 0xAF, 0x07, 0x66, 0x26, 0x64,
+        0x79, 0xB7, 0x56, 0x54, 0xE6, 0x5F,
+        /* b */
+        0x50, 0x37, 0xEA, 0x65, 0x41, 0x96, 0xCF, 0xF0, 0xCD, 0x82, 0xB2, 0xC1,
+        0x4A, 0x2F, 0xCF, 0x2E, 0x3F, 0xF8, 0x77, 0x52, 0x85, 0xB5, 0x45, 0x72,
+        0x2F, 0x03, 0xEA, 0xCD, 0xB7, 0x4B,
+        /* x */
+        0x28, 0xF9, 0xD0, 0x4E, 0x90, 0x00, 0x69, 0xC8, 0xDC, 0x47, 0xA0, 0x85,
+        0x34, 0xFE, 0x76, 0xD2, 0xB9, 0x00, 0xB7, 0xD7, 0xEF, 0x31, 0xF5, 0x70,
+        0x9F, 0x20, 0x0C, 0x4C, 0xA2, 0x05,
+        /* y */
+        0x56, 0x67, 0x33, 0x4C, 0x45, 0xAF, 0xF3, 0xB5, 0xA0, 0x3B, 0xAD, 0x9D,
+        0xD7, 0x5E, 0x2C, 0x71, 0xA9, 0x93, 0x62, 0x56, 0x7D, 0x54, 0x53, 0xF7,
+        0xFA, 0x6E, 0x22, 0x7E, 0xC8, 0x33,
+        /* order */
+        0x15, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
+        0x55, 0x55, 0x55, 0x3C, 0x6F, 0x28, 0x85, 0x25, 0x9C, 0x31, 0xE3, 0xFC,
+        0xDF, 0x15, 0x46, 0x24, 0x52, 0x2D
+    }
+};
+
+static const struct {
+    EC_CURVE_DATA h;
+    unsigned char data[20 + 30 * 6];
+} _EC_X9_62_CHAR2_239V3 = {
+    {
+        NID_X9_62_characteristic_two_field, 20, 30, 0xA
+    },
+    {
+        /* seed */
+        0x9E, 0x07, 0x6F, 0x4D, 0x69, 0x6E, 0x67, 0x68, 0x75, 0x61, 0x51, 0x75,
+        0xE1, 0x1E, 0x9F, 0xDD, 0x77, 0xF9, 0x20, 0x41,
+        /* p */
+        0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x10, 0x00, 0x00, 0x00, 0x01,
+        /* a */
+        0x01, 0x23, 0x87, 0x74, 0x66, 0x6A, 0x67, 0x76, 0x6D, 0x66, 0x76, 0xF7,
+        0x78, 0xE6, 0x76, 0xB6, 0x69, 0x99, 0x17, 0x66, 0x66, 0xE6, 0x87, 0x66,
+        0x6D, 0x87, 0x66, 0xC6, 0x6A, 0x9F,
+        /* b */
+        0x6A, 0x94, 0x19, 0x77, 0xBA, 0x9F, 0x6A, 0x43, 0x51, 0x99, 0xAC, 0xFC,
+        0x51, 0x06, 0x7E, 0xD5, 0x87, 0xF5, 0x19, 0xC5, 0xEC, 0xB5, 0x41, 0xB8,
+        0xE4, 0x41, 0x11, 0xDE, 0x1D, 0x40,
+        /* x */
+        0x70, 0xF6, 0xE9, 0xD0, 0x4D, 0x28, 0x9C, 0x4E, 0x89, 0x91, 0x3C, 0xE3,
+        0x53, 0x0B, 0xFD, 0xE9, 0x03, 0x97, 0x7D, 0x42, 0xB1, 0x46, 0xD5, 0x39,
+        0xBF, 0x1B, 0xDE, 0x4E, 0x9C, 0x92,
+        /* y */
+        0x2E, 0x5A, 0x0E, 0xAF, 0x6E, 0x5E, 0x13, 0x05, 0xB9, 0x00, 0x4D, 0xCE,
+        0x5C, 0x0E, 0xD7, 0xFE, 0x59, 0xA3, 0x56, 0x08, 0xF3, 0x38, 0x37, 0xC8,
+        0x16, 0xD8, 0x0B, 0x79, 0xF4, 0x61,
+        /* order */
+        0x0C, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC,
+        0xCC, 0xCC, 0xCC, 0xAC, 0x49, 0x12, 0xD2, 0xD9, 0xDF, 0x90, 0x3E, 0xF9,
+        0x88, 0x8B, 0x8A, 0x0E, 0x4C, 0xFF
+    }
+};
+
+static const struct {
+    EC_CURVE_DATA h;
+    unsigned char data[0 + 35 * 6];
+} _EC_X9_62_CHAR2_272W1 = {
+    {
+        NID_X9_62_characteristic_two_field, 0, 35, 0xFF06
+    },
+    {
+        /* no seed */
+        /* p */
+        0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0B,
+        /* a */
+        0x00, 0x91, 0xA0, 0x91, 0xF0, 0x3B, 0x5F, 0xBA, 0x4A, 0xB2, 0xCC, 0xF4,
+        0x9C, 0x4E, 0xDD, 0x22, 0x0F, 0xB0, 0x28, 0x71, 0x2D, 0x42, 0xBE, 0x75,
+        0x2B, 0x2C, 0x40, 0x09, 0x4D, 0xBA, 0xCD, 0xB5, 0x86, 0xFB, 0x20,
+        /* b */
+        0x00, 0x71, 0x67, 0xEF, 0xC9, 0x2B, 0xB2, 0xE3, 0xCE, 0x7C, 0x8A, 0xAA,
+        0xFF, 0x34, 0xE1, 0x2A, 0x9C, 0x55, 0x70, 0x03, 0xD7, 0xC7, 0x3A, 0x6F,
+        0xAF, 0x00, 0x3F, 0x99, 0xF6, 0xCC, 0x84, 0x82, 0xE5, 0x40, 0xF7,
+        /* x */
+        0x00, 0x61, 0x08, 0xBA, 0xBB, 0x2C, 0xEE, 0xBC, 0xF7, 0x87, 0x05, 0x8A,
+        0x05, 0x6C, 0xBE, 0x0C, 0xFE, 0x62, 0x2D, 0x77, 0x23, 0xA2, 0x89, 0xE0,
+        0x8A, 0x07, 0xAE, 0x13, 0xEF, 0x0D, 0x10, 0xD1, 0x71, 0xDD, 0x8D,
+        /* y */
+        0x00, 0x10, 0xC7, 0x69, 0x57, 0x16, 0x85, 0x1E, 0xEF, 0x6B, 0xA7, 0xF6,
+        0x87, 0x2E, 0x61, 0x42, 0xFB, 0xD2, 0x41, 0xB8, 0x30, 0xFF, 0x5E, 0xFC,
+        0xAC, 0xEC, 0xCA, 0xB0, 0x5E, 0x02, 0x00, 0x5D, 0xDE, 0x9D, 0x23,
+        /* order */
+        0x00, 0x00, 0x01, 0x00, 0xFA, 0xF5, 0x13, 0x54, 0xE0, 0xE3, 0x9E, 0x48,
+        0x92, 0xDF, 0x6E, 0x31, 0x9C, 0x72, 0xC8, 0x16, 0x16, 0x03, 0xFA, 0x45,
+        0xAA, 0x7B, 0x99, 0x8A, 0x16, 0x7B, 0x8F, 0x1E, 0x62, 0x95, 0x21
+    }
+};
+
+static const struct {
+    EC_CURVE_DATA h;
+    unsigned char data[0 + 39 * 6];
+} _EC_X9_62_CHAR2_304W1 = {
+    {
+        NID_X9_62_characteristic_two_field, 0, 39, 0xFE2E
+    },
+    {
+        /* no seed */
+        /* p */
+        0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x08, 0x07,
+        /* a */
+        0x00, 0xFD, 0x0D, 0x69, 0x31, 0x49, 0xA1, 0x18, 0xF6, 0x51, 0xE6, 0xDC,
+        0xE6, 0x80, 0x20, 0x85, 0x37, 0x7E, 0x5F, 0x88, 0x2D, 0x1B, 0x51, 0x0B,
+        0x44, 0x16, 0x00, 0x74, 0xC1, 0x28, 0x80, 0x78, 0x36, 0x5A, 0x03, 0x96,
+        0xC8, 0xE6, 0x81,
+        /* b */
+        0x00, 0xBD, 0xDB, 0x97, 0xE5, 0x55, 0xA5, 0x0A, 0x90, 0x8E, 0x43, 0xB0,
+        0x1C, 0x79, 0x8E, 0xA5, 0xDA, 0xA6, 0x78, 0x8F, 0x1E, 0xA2, 0x79, 0x4E,
+        0xFC, 0xF5, 0x71, 0x66, 0xB8, 0xC1, 0x40, 0x39, 0x60, 0x1E, 0x55, 0x82,
+        0x73, 0x40, 0xBE,
+        /* x */
+        0x00, 0x19, 0x7B, 0x07, 0x84, 0x5E, 0x9B, 0xE2, 0xD9, 0x6A, 0xDB, 0x0F,
+        0x5F, 0x3C, 0x7F, 0x2C, 0xFF, 0xBD, 0x7A, 0x3E, 0xB8, 0xB6, 0xFE, 0xC3,
+        0x5C, 0x7F, 0xD6, 0x7F, 0x26, 0xDD, 0xF6, 0x28, 0x5A, 0x64, 0x4F, 0x74,
+        0x0A, 0x26, 0x14,
+        /* y */
+        0x00, 0xE1, 0x9F, 0xBE, 0xB7, 0x6E, 0x0D, 0xA1, 0x71, 0x51, 0x7E, 0xCF,
+        0x40, 0x1B, 0x50, 0x28, 0x9B, 0xF0, 0x14, 0x10, 0x32, 0x88, 0x52, 0x7A,
+        0x9B, 0x41, 0x6A, 0x10, 0x5E, 0x80, 0x26, 0x0B, 0x54, 0x9F, 0xDC, 0x1B,
+        0x92, 0xC0, 0x3B,
+        /* order */
+        0x00, 0x00, 0x01, 0x01, 0xD5, 0x56, 0x57, 0x2A, 0xAB, 0xAC, 0x80, 0x01,
+        0x01, 0xD5, 0x56, 0x57, 0x2A, 0xAB, 0xAC, 0x80, 0x01, 0x02, 0x2D, 0x5C,
+        0x91, 0xDD, 0x17, 0x3F, 0x8F, 0xB5, 0x61, 0xDA, 0x68, 0x99, 0x16, 0x44,
+        0x43, 0x05, 0x1D
+    }
+};
+
+static const struct {
+    EC_CURVE_DATA h;
+    unsigned char data[20 + 45 * 6];
+} _EC_X9_62_CHAR2_359V1 = {
+    {
+        NID_X9_62_characteristic_two_field, 20, 45, 0x4C
+    },
+    {
+        /* seed */
+        0x2B, 0x35, 0x49, 0x20, 0xB7, 0x24, 0xD6, 0x96, 0xE6, 0x76, 0x87, 0x56,
+        0x15, 0x17, 0x58, 0x5B, 0xA1, 0x33, 0x2D, 0xC6,
+        /* p */
+        0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+        /* a */
+        0x56, 0x67, 0x67, 0x6A, 0x65, 0x4B, 0x20, 0x75, 0x4F, 0x35, 0x6E, 0xA9,
+        0x20, 0x17, 0xD9, 0x46, 0x56, 0x7C, 0x46, 0x67, 0x55, 0x56, 0xF1, 0x95,
+        0x56, 0xA0, 0x46, 0x16, 0xB5, 0x67, 0xD2, 0x23, 0xA5, 0xE0, 0x56, 0x56,
+        0xFB, 0x54, 0x90, 0x16, 0xA9, 0x66, 0x56, 0xA5, 0x57,
+        /* b */
+        0x24, 0x72, 0xE2, 0xD0, 0x19, 0x7C, 0x49, 0x36, 0x3F, 0x1F, 0xE7, 0xF5,
+        0xB6, 0xDB, 0x07, 0x5D, 0x52, 0xB6, 0x94, 0x7D, 0x13, 0x5D, 0x8C, 0xA4,
+        0x45, 0x80, 0x5D, 0x39, 0xBC, 0x34, 0x56, 0x26, 0x08, 0x96, 0x87, 0x74,
+        0x2B, 0x63, 0x29, 0xE7, 0x06, 0x80, 0x23, 0x19, 0x88,
+        /* x */
+        0x3C, 0x25, 0x8E, 0xF3, 0x04, 0x77, 0x67, 0xE7, 0xED, 0xE0, 0xF1, 0xFD,
+        0xAA, 0x79, 0xDA, 0xEE, 0x38, 0x41, 0x36, 0x6A, 0x13, 0x2E, 0x16, 0x3A,
+        0xCE, 0xD4, 0xED, 0x24, 0x01, 0xDF, 0x9C, 0x6B, 0xDC, 0xDE, 0x98, 0xE8,
+        0xE7, 0x07, 0xC0, 0x7A, 0x22, 0x39, 0xB1, 0xB0, 0x97,
+        /* y */
+        0x53, 0xD7, 0xE0, 0x85, 0x29, 0x54, 0x70, 0x48, 0x12, 0x1E, 0x9C, 0x95,
+        0xF3, 0x79, 0x1D, 0xD8, 0x04, 0x96, 0x39, 0x48, 0xF3, 0x4F, 0xAE, 0x7B,
+        0xF4, 0x4E, 0xA8, 0x23, 0x65, 0xDC, 0x78, 0x68, 0xFE, 0x57, 0xE4, 0xAE,
+        0x2D, 0xE2, 0x11, 0x30, 0x5A, 0x40, 0x71, 0x04, 0xBD,
+        /* order */
+        0x01, 0xAF, 0x28, 0x6B, 0xCA, 0x1A, 0xF2, 0x86, 0xBC, 0xA1, 0xAF, 0x28,
+        0x6B, 0xCA, 0x1A, 0xF2, 0x86, 0xBC, 0xA1, 0xAF, 0x28, 0x6B, 0xC9, 0xFB,
+        0x8F, 0x6B, 0x85, 0xC5, 0x56, 0x89, 0x2C, 0x20, 0xA7, 0xEB, 0x96, 0x4F,
+        0xE7, 0x71, 0x9E, 0x74, 0xF4, 0x90, 0x75, 0x8D, 0x3B
+    }
+};
+
+static const struct {
+    EC_CURVE_DATA h;
+    unsigned char data[0 + 47 * 6];
+} _EC_X9_62_CHAR2_368W1 = {
+    {
+        NID_X9_62_characteristic_two_field, 0, 47, 0xFF70
+    },
+    {
+        /* no seed */
+        /* p */
+        0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07,
+        /* a */
+        0x00, 0xE0, 0xD2, 0xEE, 0x25, 0x09, 0x52, 0x06, 0xF5, 0xE2, 0xA4, 0xF9,
+        0xED, 0x22, 0x9F, 0x1F, 0x25, 0x6E, 0x79, 0xA0, 0xE2, 0xB4, 0x55, 0x97,
+        0x0D, 0x8D, 0x0D, 0x86, 0x5B, 0xD9, 0x47, 0x78, 0xC5, 0x76, 0xD6, 0x2F,
+        0x0A, 0xB7, 0x51, 0x9C, 0xCD, 0x2A, 0x1A, 0x90, 0x6A, 0xE3, 0x0D,
+        /* b */
+        0x00, 0xFC, 0x12, 0x17, 0xD4, 0x32, 0x0A, 0x90, 0x45, 0x2C, 0x76, 0x0A,
+        0x58, 0xED, 0xCD, 0x30, 0xC8, 0xDD, 0x06, 0x9B, 0x3C, 0x34, 0x45, 0x38,
+        0x37, 0xA3, 0x4E, 0xD5, 0x0C, 0xB5, 0x49, 0x17, 0xE1, 0xC2, 0x11, 0x2D,
+        0x84, 0xD1, 0x64, 0xF4, 0x44, 0xF8, 0xF7, 0x47, 0x86, 0x04, 0x6A,
+        /* x */
+        0x00, 0x10, 0x85, 0xE2, 0x75, 0x53, 0x81, 0xDC, 0xCC, 0xE3, 0xC1, 0x55,
+        0x7A, 0xFA, 0x10, 0xC2, 0xF0, 0xC0, 0xC2, 0x82, 0x56, 0x46, 0xC5, 0xB3,
+        0x4A, 0x39, 0x4C, 0xBC, 0xFA, 0x8B, 0xC1, 0x6B, 0x22, 0xE7, 0xE7, 0x89,
+        0xE9, 0x27, 0xBE, 0x21, 0x6F, 0x02, 0xE1, 0xFB, 0x13, 0x6A, 0x5F,
+        /* y */
+        0x00, 0x7B, 0x3E, 0xB1, 0xBD, 0xDC, 0xBA, 0x62, 0xD5, 0xD8, 0xB2, 0x05,
+        0x9B, 0x52, 0x57, 0x97, 0xFC, 0x73, 0x82, 0x2C, 0x59, 0x05, 0x9C, 0x62,
+        0x3A, 0x45, 0xFF, 0x38, 0x43, 0xCE, 0xE8, 0xF8, 0x7C, 0xD1, 0x85, 0x5A,
+        0xDA, 0xA8, 0x1E, 0x2A, 0x07, 0x50, 0xB8, 0x0F, 0xDA, 0x23, 0x10,
+        /* order */
+        0x00, 0x00, 0x01, 0x00, 0x90, 0x51, 0x2D, 0xA9, 0xAF, 0x72, 0xB0, 0x83,
+        0x49, 0xD9, 0x8A, 0x5D, 0xD4, 0xC7, 0xB0, 0x53, 0x2E, 0xCA, 0x51, 0xCE,
+        0x03, 0xE2, 0xD1, 0x0F, 0x3B, 0x7A, 0xC5, 0x79, 0xBD, 0x87, 0xE9, 0x09,
+        0xAE, 0x40, 0xA6, 0xF1, 0x31, 0xE9, 0xCF, 0xCE, 0x5B, 0xD9, 0x67
+    }
+};
+
+static const struct {
+    EC_CURVE_DATA h;
+    unsigned char data[0 + 54 * 6];
+} _EC_X9_62_CHAR2_431R1 = {
+    {
+        NID_X9_62_characteristic_two_field, 0, 54, 0x2760
+    },
+    {
+        /* no seed */
+        /* p */
+        0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+        /* a */
+        0x1A, 0x82, 0x7E, 0xF0, 0x0D, 0xD6, 0xFC, 0x0E, 0x23, 0x4C, 0xAF, 0x04,
+        0x6C, 0x6A, 0x5D, 0x8A, 0x85, 0x39, 0x5B, 0x23, 0x6C, 0xC4, 0xAD, 0x2C,
+        0xF3, 0x2A, 0x0C, 0xAD, 0xBD, 0xC9, 0xDD, 0xF6, 0x20, 0xB0, 0xEB, 0x99,
+        0x06, 0xD0, 0x95, 0x7F, 0x6C, 0x6F, 0xEA, 0xCD, 0x61, 0x54, 0x68, 0xDF,
+        0x10, 0x4D, 0xE2, 0x96, 0xCD, 0x8F,
+        /* b */
+        0x10, 0xD9, 0xB4, 0xA3, 0xD9, 0x04, 0x7D, 0x8B, 0x15, 0x43, 0x59, 0xAB,
+        0xFB, 0x1B, 0x7F, 0x54, 0x85, 0xB0, 0x4C, 0xEB, 0x86, 0x82, 0x37, 0xDD,
+        0xC9, 0xDE, 0xDA, 0x98, 0x2A, 0x67, 0x9A, 0x5A, 0x91, 0x9B, 0x62, 0x6D,
+        0x4E, 0x50, 0xA8, 0xDD, 0x73, 0x1B, 0x10, 0x7A, 0x99, 0x62, 0x38, 0x1F,
+        0xB5, 0xD8, 0x07, 0xBF, 0x26, 0x18,
+        /* x */
+        0x12, 0x0F, 0xC0, 0x5D, 0x3C, 0x67, 0xA9, 0x9D, 0xE1, 0x61, 0xD2, 0xF4,
+        0x09, 0x26, 0x22, 0xFE, 0xCA, 0x70, 0x1B, 0xE4, 0xF5, 0x0F, 0x47, 0x58,
+        0x71, 0x4E, 0x8A, 0x87, 0xBB, 0xF2, 0xA6, 0x58, 0xEF, 0x8C, 0x21, 0xE7,
+        0xC5, 0xEF, 0xE9, 0x65, 0x36, 0x1F, 0x6C, 0x29, 0x99, 0xC0, 0xC2, 0x47,
+        0xB0, 0xDB, 0xD7, 0x0C, 0xE6, 0xB7,
+        /* y */
+        0x20, 0xD0, 0xAF, 0x89, 0x03, 0xA9, 0x6F, 0x8D, 0x5F, 0xA2, 0xC2, 0x55,
+        0x74, 0x5D, 0x3C, 0x45, 0x1B, 0x30, 0x2C, 0x93, 0x46, 0xD9, 0xB7, 0xE4,
+        0x85, 0xE7, 0xBC, 0xE4, 0x1F, 0x6B, 0x59, 0x1F, 0x3E, 0x8F, 0x6A, 0xDD,
+        0xCB, 0xB0, 0xBC, 0x4C, 0x2F, 0x94, 0x7A, 0x7D, 0xE1, 0xA8, 0x9B, 0x62,
+        0x5D, 0x6A, 0x59, 0x8B, 0x37, 0x60,
+        /* order */
+        0x00, 0x03, 0x40, 0x34, 0x03, 0x40, 0x34, 0x03, 0x40, 0x34, 0x03, 0x40,
+        0x34, 0x03, 0x40, 0x34, 0x03, 0x40, 0x34, 0x03, 0x40, 0x34, 0x03, 0x40,
+        0x34, 0x03, 0x40, 0x34, 0x03, 0x23, 0xC3, 0x13, 0xFA, 0xB5, 0x05, 0x89,
+        0x70, 0x3B, 0x5E, 0xC6, 0x8D, 0x35, 0x87, 0xFE, 0xC6, 0x0D, 0x16, 0x1C,
+        0xC1, 0x49, 0xC1, 0xAD, 0x4A, 0x91
+    }
+};
+
+static const struct {
+    EC_CURVE_DATA h;
+    unsigned char data[0 + 15 * 6];
+} _EC_WTLS_1 = {
+    {
+        NID_X9_62_characteristic_two_field, 0, 15, 2
+    },
+    {
+        /* no seed */
+        /* p */
+        0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x02, 0x01,
+        /* a */
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x01,
+        /* b */
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x01,
+        /* x */
+        0x01, 0x66, 0x79, 0x79, 0xA4, 0x0B, 0xA4, 0x97, 0xE5, 0xD5, 0xC2, 0x70,
+        0x78, 0x06, 0x17,
+        /* y */
+        0x00, 0xF4, 0x4B, 0x4A, 0xF1, 0xEC, 0xC2, 0x63, 0x0E, 0x08, 0x78, 0x5C,
+        0xEB, 0xCC, 0x15,
+        /* order */
+        0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFD, 0xBF, 0x91, 0xAF,
+        0x6D, 0xEA, 0x73
+    }
+};
+
+/* IPSec curves */
+/*
+ * NOTE: The of curves over a extension field of non prime degree is not
+ * recommended (Weil-descent). As the group order is not a prime this curve
+ * is not suitable for ECDSA.
+ */
+static const struct {
+    EC_CURVE_DATA h;
+    unsigned char data[0 + 20 * 6];
+} _EC_IPSEC_155_ID3 = {
+    {
+        NID_X9_62_characteristic_two_field, 0, 20, 3
+    },
+    {
+        /* no seed */
+        /* p */
+        0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+        /* a */
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        /* b */
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x33, 0x8f,
+        /* x */
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7b,
+        /* y */
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xc8,
+        /* order */
+        0x02, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xC7, 0xF3,
+        0xC7, 0x88, 0x1B, 0xD0, 0x86, 0x8F, 0xA8, 0x6C
+    }
+};
+
+/*
+ * NOTE: The of curves over a extension field of non prime degree is not
+ * recommended (Weil-descent). As the group order is not a prime this curve
+ * is not suitable for ECDSA.
+ */
+static const struct {
+    EC_CURVE_DATA h;
+    unsigned char data[0 + 24 * 6];
+} _EC_IPSEC_185_ID4 = {
+    {
+        NID_X9_62_characteristic_two_field, 0, 24, 2
+    },
+    {
+        /* no seed */
+        /* p */
+        0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+        /* a */
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        /* b */
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0xe9,
+        /* x */
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18,
+        /* y */
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d,
+        /* order */
+        0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+        0xED, 0xF9, 0x7C, 0x44, 0xDB, 0x9F, 0x24, 0x20, 0xBA, 0xFC, 0xA7, 0x5E
+    }
+};
+
+#endif
+
+/*
+ * These curves were added by Annie Yousar 
+ * For the definition of RFC 5639 curves see
+ * http://www.ietf.org/rfc/rfc5639.txt These curves are generated verifiable
+ * at random, nevertheless the seed is omitted as parameter because the
+ * generation mechanism is different from those defined in ANSI X9.62.
+ */
+
+static const struct {
+    EC_CURVE_DATA h;
+    unsigned char data[0 + 20 * 6];
+} _EC_brainpoolP160r1 = {
+    {
+        NID_X9_62_prime_field, 0, 20, 1
+    },
+    {
+        /* no seed */
+        /* p */
+        0xE9, 0x5E, 0x4A, 0x5F, 0x73, 0x70, 0x59, 0xDC, 0x60, 0xDF, 0xC7, 0xAD,
+        0x95, 0xB3, 0xD8, 0x13, 0x95, 0x15, 0x62, 0x0F,
+        /* a */
+        0x34, 0x0E, 0x7B, 0xE2, 0xA2, 0x80, 0xEB, 0x74, 0xE2, 0xBE, 0x61, 0xBA,
+        0xDA, 0x74, 0x5D, 0x97, 0xE8, 0xF7, 0xC3, 0x00,
+        /* b */
+        0x1E, 0x58, 0x9A, 0x85, 0x95, 0x42, 0x34, 0x12, 0x13, 0x4F, 0xAA, 0x2D,
+        0xBD, 0xEC, 0x95, 0xC8, 0xD8, 0x67, 0x5E, 0x58,
+        /* x */
+        0xBE, 0xD5, 0xAF, 0x16, 0xEA, 0x3F, 0x6A, 0x4F, 0x62, 0x93, 0x8C, 0x46,
+        0x31, 0xEB, 0x5A, 0xF7, 0xBD, 0xBC, 0xDB, 0xC3,
+        /* y */
+        0x16, 0x67, 0xCB, 0x47, 0x7A, 0x1A, 0x8E, 0xC3, 0x38, 0xF9, 0x47, 0x41,
+        0x66, 0x9C, 0x97, 0x63, 0x16, 0xDA, 0x63, 0x21,
+        /* order */
+        0xE9, 0x5E, 0x4A, 0x5F, 0x73, 0x70, 0x59, 0xDC, 0x60, 0xDF, 0x59, 0x91,
+        0xD4, 0x50, 0x29, 0x40, 0x9E, 0x60, 0xFC, 0x09
+    }
+};
+
+static const struct {
+    EC_CURVE_DATA h;
+    unsigned char data[0 + 20 * 6];
+} _EC_brainpoolP160t1 = {
+    {
+        NID_X9_62_prime_field, 0, 20, 1
+    },
+    {
+        /* no seed */
+        /* p */
+        0xE9, 0x5E, 0x4A, 0x5F, 0x73, 0x70, 0x59, 0xDC, 0x60, 0xDF, 0xC7, 0xAD,
+        0x95, 0xB3, 0xD8, 0x13, 0x95, 0x15, 0x62, 0x0F,
+        /* a */
+        0xE9, 0x5E, 0x4A, 0x5F, 0x73, 0x70, 0x59, 0xDC, 0x60, 0xDF, 0xC7, 0xAD,
+        0x95, 0xB3, 0xD8, 0x13, 0x95, 0x15, 0x62, 0x0C,
+        /* b */
+        0x7A, 0x55, 0x6B, 0x6D, 0xAE, 0x53, 0x5B, 0x7B, 0x51, 0xED, 0x2C, 0x4D,
+        0x7D, 0xAA, 0x7A, 0x0B, 0x5C, 0x55, 0xF3, 0x80,
+        /* x */
+        0xB1, 0x99, 0xB1, 0x3B, 0x9B, 0x34, 0xEF, 0xC1, 0x39, 0x7E, 0x64, 0xBA,
+        0xEB, 0x05, 0xAC, 0xC2, 0x65, 0xFF, 0x23, 0x78,
+        /* y */
+        0xAD, 0xD6, 0x71, 0x8B, 0x7C, 0x7C, 0x19, 0x61, 0xF0, 0x99, 0x1B, 0x84,
+        0x24, 0x43, 0x77, 0x21, 0x52, 0xC9, 0xE0, 0xAD,
+        /* order */
+        0xE9, 0x5E, 0x4A, 0x5F, 0x73, 0x70, 0x59, 0xDC, 0x60, 0xDF, 0x59, 0x91,
+        0xD4, 0x50, 0x29, 0x40, 0x9E, 0x60, 0xFC, 0x09
+    }
+};
+
+static const struct {
+    EC_CURVE_DATA h;
+    unsigned char data[0 + 24 * 6];
+} _EC_brainpoolP192r1 = {
+    {
+        NID_X9_62_prime_field, 0, 24, 1
+    },
+    {
+        /* no seed */
+        /* p */
+        0xC3, 0x02, 0xF4, 0x1D, 0x93, 0x2A, 0x36, 0xCD, 0xA7, 0xA3, 0x46, 0x30,
+        0x93, 0xD1, 0x8D, 0xB7, 0x8F, 0xCE, 0x47, 0x6D, 0xE1, 0xA8, 0x62, 0x97,
+        /* a */
+        0x6A, 0x91, 0x17, 0x40, 0x76, 0xB1, 0xE0, 0xE1, 0x9C, 0x39, 0xC0, 0x31,
+        0xFE, 0x86, 0x85, 0xC1, 0xCA, 0xE0, 0x40, 0xE5, 0xC6, 0x9A, 0x28, 0xEF,
+        /* b */
+        0x46, 0x9A, 0x28, 0xEF, 0x7C, 0x28, 0xCC, 0xA3, 0xDC, 0x72, 0x1D, 0x04,
+        0x4F, 0x44, 0x96, 0xBC, 0xCA, 0x7E, 0xF4, 0x14, 0x6F, 0xBF, 0x25, 0xC9,
+        /* x */
+        0xC0, 0xA0, 0x64, 0x7E, 0xAA, 0xB6, 0xA4, 0x87, 0x53, 0xB0, 0x33, 0xC5,
+        0x6C, 0xB0, 0xF0, 0x90, 0x0A, 0x2F, 0x5C, 0x48, 0x53, 0x37, 0x5F, 0xD6,
+        /* y */
+        0x14, 0xB6, 0x90, 0x86, 0x6A, 0xBD, 0x5B, 0xB8, 0x8B, 0x5F, 0x48, 0x28,
+        0xC1, 0x49, 0x00, 0x02, 0xE6, 0x77, 0x3F, 0xA2, 0xFA, 0x29, 0x9B, 0x8F,
+        /* order */
+        0xC3, 0x02, 0xF4, 0x1D, 0x93, 0x2A, 0x36, 0xCD, 0xA7, 0xA3, 0x46, 0x2F,
+        0x9E, 0x9E, 0x91, 0x6B, 0x5B, 0xE8, 0xF1, 0x02, 0x9A, 0xC4, 0xAC, 0xC1
+    }
+};
+
+static const struct {
+    EC_CURVE_DATA h;
+    unsigned char data[0 + 24 * 6];
+} _EC_brainpoolP192t1 = {
+    {
+        NID_X9_62_prime_field, 0, 24, 1
+    },
+    {
+        /* no seed */
+        /* p */
+        0xC3, 0x02, 0xF4, 0x1D, 0x93, 0x2A, 0x36, 0xCD, 0xA7, 0xA3, 0x46, 0x30,
+        0x93, 0xD1, 0x8D, 0xB7, 0x8F, 0xCE, 0x47, 0x6D, 0xE1, 0xA8, 0x62, 0x97,
+        /* a */
+        0xC3, 0x02, 0xF4, 0x1D, 0x93, 0x2A, 0x36, 0xCD, 0xA7, 0xA3, 0x46, 0x30,
+        0x93, 0xD1, 0x8D, 0xB7, 0x8F, 0xCE, 0x47, 0x6D, 0xE1, 0xA8, 0x62, 0x94,
+        /* b */
+        0x13, 0xD5, 0x6F, 0xFA, 0xEC, 0x78, 0x68, 0x1E, 0x68, 0xF9, 0xDE, 0xB4,
+        0x3B, 0x35, 0xBE, 0xC2, 0xFB, 0x68, 0x54, 0x2E, 0x27, 0x89, 0x7B, 0x79,
+        /* x */
+        0x3A, 0xE9, 0xE5, 0x8C, 0x82, 0xF6, 0x3C, 0x30, 0x28, 0x2E, 0x1F, 0xE7,
+        0xBB, 0xF4, 0x3F, 0xA7, 0x2C, 0x44, 0x6A, 0xF6, 0xF4, 0x61, 0x81, 0x29,
+        /* y */
+        0x09, 0x7E, 0x2C, 0x56, 0x67, 0xC2, 0x22, 0x3A, 0x90, 0x2A, 0xB5, 0xCA,
+        0x44, 0x9D, 0x00, 0x84, 0xB7, 0xE5, 0xB3, 0xDE, 0x7C, 0xCC, 0x01, 0xC9,
+        /* order */
+        0xC3, 0x02, 0xF4, 0x1D, 0x93, 0x2A, 0x36, 0xCD, 0xA7, 0xA3, 0x46, 0x2F,
+        0x9E, 0x9E, 0x91, 0x6B, 0x5B, 0xE8, 0xF1, 0x02, 0x9A, 0xC4, 0xAC, 0xC1
+    }
+};
+
+static const struct {
+    EC_CURVE_DATA h;
+    unsigned char data[0 + 28 * 6];
+} _EC_brainpoolP224r1 = {
+    {
+        NID_X9_62_prime_field, 0, 28, 1
+    },
+    {
+        /* no seed */
+        /* p */
+        0xD7, 0xC1, 0x34, 0xAA, 0x26, 0x43, 0x66, 0x86, 0x2A, 0x18, 0x30, 0x25,
+        0x75, 0xD1, 0xD7, 0x87, 0xB0, 0x9F, 0x07, 0x57, 0x97, 0xDA, 0x89, 0xF5,
+        0x7E, 0xC8, 0xC0, 0xFF,
+        /* a */
+        0x68, 0xA5, 0xE6, 0x2C, 0xA9, 0xCE, 0x6C, 0x1C, 0x29, 0x98, 0x03, 0xA6,
+        0xC1, 0x53, 0x0B, 0x51, 0x4E, 0x18, 0x2A, 0xD8, 0xB0, 0x04, 0x2A, 0x59,
+        0xCA, 0xD2, 0x9F, 0x43,
+        /* b */
+        0x25, 0x80, 0xF6, 0x3C, 0xCF, 0xE4, 0x41, 0x38, 0x87, 0x07, 0x13, 0xB1,
+        0xA9, 0x23, 0x69, 0xE3, 0x3E, 0x21, 0x35, 0xD2, 0x66, 0xDB, 0xB3, 0x72,
+        0x38, 0x6C, 0x40, 0x0B,
+        /* x */
+        0x0D, 0x90, 0x29, 0xAD, 0x2C, 0x7E, 0x5C, 0xF4, 0x34, 0x08, 0x23, 0xB2,
+        0xA8, 0x7D, 0xC6, 0x8C, 0x9E, 0x4C, 0xE3, 0x17, 0x4C, 0x1E, 0x6E, 0xFD,
+        0xEE, 0x12, 0xC0, 0x7D,
+        /* y */
+        0x58, 0xAA, 0x56, 0xF7, 0x72, 0xC0, 0x72, 0x6F, 0x24, 0xC6, 0xB8, 0x9E,
+        0x4E, 0xCD, 0xAC, 0x24, 0x35, 0x4B, 0x9E, 0x99, 0xCA, 0xA3, 0xF6, 0xD3,
+        0x76, 0x14, 0x02, 0xCD,
+        /* order */
+        0xD7, 0xC1, 0x34, 0xAA, 0x26, 0x43, 0x66, 0x86, 0x2A, 0x18, 0x30, 0x25,
+        0x75, 0xD0, 0xFB, 0x98, 0xD1, 0x16, 0xBC, 0x4B, 0x6D, 0xDE, 0xBC, 0xA3,
+        0xA5, 0xA7, 0x93, 0x9F
+    }
+};
+
+static const struct {
+    EC_CURVE_DATA h;
+    unsigned char data[0 + 28 * 6];
+} _EC_brainpoolP224t1 = {
+    {
+        NID_X9_62_prime_field, 0, 28, 1
+    },
+    {
+        /* no seed */
+        /* p */
+        0xD7, 0xC1, 0x34, 0xAA, 0x26, 0x43, 0x66, 0x86, 0x2A, 0x18, 0x30, 0x25,
+        0x75, 0xD1, 0xD7, 0x87, 0xB0, 0x9F, 0x07, 0x57, 0x97, 0xDA, 0x89, 0xF5,
+        0x7E, 0xC8, 0xC0, 0xFF,
+        /* a */
+        0xD7, 0xC1, 0x34, 0xAA, 0x26, 0x43, 0x66, 0x86, 0x2A, 0x18, 0x30, 0x25,
+        0x75, 0xD1, 0xD7, 0x87, 0xB0, 0x9F, 0x07, 0x57, 0x97, 0xDA, 0x89, 0xF5,
+        0x7E, 0xC8, 0xC0, 0xFC,
+        /* b */
+        0x4B, 0x33, 0x7D, 0x93, 0x41, 0x04, 0xCD, 0x7B, 0xEF, 0x27, 0x1B, 0xF6,
+        0x0C, 0xED, 0x1E, 0xD2, 0x0D, 0xA1, 0x4C, 0x08, 0xB3, 0xBB, 0x64, 0xF1,
+        0x8A, 0x60, 0x88, 0x8D,
+        /* x */
+        0x6A, 0xB1, 0xE3, 0x44, 0xCE, 0x25, 0xFF, 0x38, 0x96, 0x42, 0x4E, 0x7F,
+        0xFE, 0x14, 0x76, 0x2E, 0xCB, 0x49, 0xF8, 0x92, 0x8A, 0xC0, 0xC7, 0x60,
+        0x29, 0xB4, 0xD5, 0x80,
+        /* y */
+        0x03, 0x74, 0xE9, 0xF5, 0x14, 0x3E, 0x56, 0x8C, 0xD2, 0x3F, 0x3F, 0x4D,
+        0x7C, 0x0D, 0x4B, 0x1E, 0x41, 0xC8, 0xCC, 0x0D, 0x1C, 0x6A, 0xBD, 0x5F,
+        0x1A, 0x46, 0xDB, 0x4C,
+        /* order */
+        0xD7, 0xC1, 0x34, 0xAA, 0x26, 0x43, 0x66, 0x86, 0x2A, 0x18, 0x30, 0x25,
+        0x75, 0xD0, 0xFB, 0x98, 0xD1, 0x16, 0xBC, 0x4B, 0x6D, 0xDE, 0xBC, 0xA3,
+        0xA5, 0xA7, 0x93, 0x9F
+    }
+};
+
+static const struct {
+    EC_CURVE_DATA h;
+    unsigned char data[0 + 32 * 6];
+} _EC_brainpoolP256r1 = {
+    {
+        NID_X9_62_prime_field, 0, 32, 1
+    },
+    {
+        /* no seed */
+        /* p */
+        0xA9, 0xFB, 0x57, 0xDB, 0xA1, 0xEE, 0xA9, 0xBC, 0x3E, 0x66, 0x0A, 0x90,
+        0x9D, 0x83, 0x8D, 0x72, 0x6E, 0x3B, 0xF6, 0x23, 0xD5, 0x26, 0x20, 0x28,
+        0x20, 0x13, 0x48, 0x1D, 0x1F, 0x6E, 0x53, 0x77,
+        /* a */
+        0x7D, 0x5A, 0x09, 0x75, 0xFC, 0x2C, 0x30, 0x57, 0xEE, 0xF6, 0x75, 0x30,
+        0x41, 0x7A, 0xFF, 0xE7, 0xFB, 0x80, 0x55, 0xC1, 0x26, 0xDC, 0x5C, 0x6C,
+        0xE9, 0x4A, 0x4B, 0x44, 0xF3, 0x30, 0xB5, 0xD9,
+        /* b */
+        0x26, 0xDC, 0x5C, 0x6C, 0xE9, 0x4A, 0x4B, 0x44, 0xF3, 0x30, 0xB5, 0xD9,
+        0xBB, 0xD7, 0x7C, 0xBF, 0x95, 0x84, 0x16, 0x29, 0x5C, 0xF7, 0xE1, 0xCE,
+        0x6B, 0xCC, 0xDC, 0x18, 0xFF, 0x8C, 0x07, 0xB6,
+        /* x */
+        0x8B, 0xD2, 0xAE, 0xB9, 0xCB, 0x7E, 0x57, 0xCB, 0x2C, 0x4B, 0x48, 0x2F,
+        0xFC, 0x81, 0xB7, 0xAF, 0xB9, 0xDE, 0x27, 0xE1, 0xE3, 0xBD, 0x23, 0xC2,
+        0x3A, 0x44, 0x53, 0xBD, 0x9A, 0xCE, 0x32, 0x62,
+        /* y */
+        0x54, 0x7E, 0xF8, 0x35, 0xC3, 0xDA, 0xC4, 0xFD, 0x97, 0xF8, 0x46, 0x1A,
+        0x14, 0x61, 0x1D, 0xC9, 0xC2, 0x77, 0x45, 0x13, 0x2D, 0xED, 0x8E, 0x54,
+        0x5C, 0x1D, 0x54, 0xC7, 0x2F, 0x04, 0x69, 0x97,
+        /* order */
+        0xA9, 0xFB, 0x57, 0xDB, 0xA1, 0xEE, 0xA9, 0xBC, 0x3E, 0x66, 0x0A, 0x90,
+        0x9D, 0x83, 0x8D, 0x71, 0x8C, 0x39, 0x7A, 0xA3, 0xB5, 0x61, 0xA6, 0xF7,
+        0x90, 0x1E, 0x0E, 0x82, 0x97, 0x48, 0x56, 0xA7
+    }
+};
+
+static const struct {
+    EC_CURVE_DATA h;
+    unsigned char data[0 + 32 * 6];
+} _EC_brainpoolP256t1 = {
+    {
+        NID_X9_62_prime_field, 0, 32, 1
+    },
+    {
+        /* no seed */
+        /* p */
+        0xA9, 0xFB, 0x57, 0xDB, 0xA1, 0xEE, 0xA9, 0xBC, 0x3E, 0x66, 0x0A, 0x90,
+        0x9D, 0x83, 0x8D, 0x72, 0x6E, 0x3B, 0xF6, 0x23, 0xD5, 0x26, 0x20, 0x28,
+        0x20, 0x13, 0x48, 0x1D, 0x1F, 0x6E, 0x53, 0x77,
+        /* a */
+        0xA9, 0xFB, 0x57, 0xDB, 0xA1, 0xEE, 0xA9, 0xBC, 0x3E, 0x66, 0x0A, 0x90,
+        0x9D, 0x83, 0x8D, 0x72, 0x6E, 0x3B, 0xF6, 0x23, 0xD5, 0x26, 0x20, 0x28,
+        0x20, 0x13, 0x48, 0x1D, 0x1F, 0x6E, 0x53, 0x74,
+        /* b */
+        0x66, 0x2C, 0x61, 0xC4, 0x30, 0xD8, 0x4E, 0xA4, 0xFE, 0x66, 0xA7, 0x73,
+        0x3D, 0x0B, 0x76, 0xB7, 0xBF, 0x93, 0xEB, 0xC4, 0xAF, 0x2F, 0x49, 0x25,
+        0x6A, 0xE5, 0x81, 0x01, 0xFE, 0xE9, 0x2B, 0x04,
+        /* x */
+        0xA3, 0xE8, 0xEB, 0x3C, 0xC1, 0xCF, 0xE7, 0xB7, 0x73, 0x22, 0x13, 0xB2,
+        0x3A, 0x65, 0x61, 0x49, 0xAF, 0xA1, 0x42, 0xC4, 0x7A, 0xAF, 0xBC, 0x2B,
+        0x79, 0xA1, 0x91, 0x56, 0x2E, 0x13, 0x05, 0xF4,
+        /* y */
+        0x2D, 0x99, 0x6C, 0x82, 0x34, 0x39, 0xC5, 0x6D, 0x7F, 0x7B, 0x22, 0xE1,
+        0x46, 0x44, 0x41, 0x7E, 0x69, 0xBC, 0xB6, 0xDE, 0x39, 0xD0, 0x27, 0x00,
+        0x1D, 0xAB, 0xE8, 0xF3, 0x5B, 0x25, 0xC9, 0xBE,
+        /* order */
+        0xA9, 0xFB, 0x57, 0xDB, 0xA1, 0xEE, 0xA9, 0xBC, 0x3E, 0x66, 0x0A, 0x90,
+        0x9D, 0x83, 0x8D, 0x71, 0x8C, 0x39, 0x7A, 0xA3, 0xB5, 0x61, 0xA6, 0xF7,
+        0x90, 0x1E, 0x0E, 0x82, 0x97, 0x48, 0x56, 0xA7
+    }
+};
+
+static const struct {
+    EC_CURVE_DATA h;
+    unsigned char data[0 + 40 * 6];
+} _EC_brainpoolP320r1 = {
+    {
+        NID_X9_62_prime_field, 0, 40, 1
+    },
+    {
+        /* no seed */
+        /* p */
+        0xD3, 0x5E, 0x47, 0x20, 0x36, 0xBC, 0x4F, 0xB7, 0xE1, 0x3C, 0x78, 0x5E,
+        0xD2, 0x01, 0xE0, 0x65, 0xF9, 0x8F, 0xCF, 0xA6, 0xF6, 0xF4, 0x0D, 0xEF,
+        0x4F, 0x92, 0xB9, 0xEC, 0x78, 0x93, 0xEC, 0x28, 0xFC, 0xD4, 0x12, 0xB1,
+        0xF1, 0xB3, 0x2E, 0x27,
+        /* a */
+        0x3E, 0xE3, 0x0B, 0x56, 0x8F, 0xBA, 0xB0, 0xF8, 0x83, 0xCC, 0xEB, 0xD4,
+        0x6D, 0x3F, 0x3B, 0xB8, 0xA2, 0xA7, 0x35, 0x13, 0xF5, 0xEB, 0x79, 0xDA,
+        0x66, 0x19, 0x0E, 0xB0, 0x85, 0xFF, 0xA9, 0xF4, 0x92, 0xF3, 0x75, 0xA9,
+        0x7D, 0x86, 0x0E, 0xB4,
+        /* b */
+        0x52, 0x08, 0x83, 0x94, 0x9D, 0xFD, 0xBC, 0x42, 0xD3, 0xAD, 0x19, 0x86,
+        0x40, 0x68, 0x8A, 0x6F, 0xE1, 0x3F, 0x41, 0x34, 0x95, 0x54, 0xB4, 0x9A,
+        0xCC, 0x31, 0xDC, 0xCD, 0x88, 0x45, 0x39, 0x81, 0x6F, 0x5E, 0xB4, 0xAC,
+        0x8F, 0xB1, 0xF1, 0xA6,
+        /* x */
+        0x43, 0xBD, 0x7E, 0x9A, 0xFB, 0x53, 0xD8, 0xB8, 0x52, 0x89, 0xBC, 0xC4,
+        0x8E, 0xE5, 0xBF, 0xE6, 0xF2, 0x01, 0x37, 0xD1, 0x0A, 0x08, 0x7E, 0xB6,
+        0xE7, 0x87, 0x1E, 0x2A, 0x10, 0xA5, 0x99, 0xC7, 0x10, 0xAF, 0x8D, 0x0D,
+        0x39, 0xE2, 0x06, 0x11,
+        /* y */
+        0x14, 0xFD, 0xD0, 0x55, 0x45, 0xEC, 0x1C, 0xC8, 0xAB, 0x40, 0x93, 0x24,
+        0x7F, 0x77, 0x27, 0x5E, 0x07, 0x43, 0xFF, 0xED, 0x11, 0x71, 0x82, 0xEA,
+        0xA9, 0xC7, 0x78, 0x77, 0xAA, 0xAC, 0x6A, 0xC7, 0xD3, 0x52, 0x45, 0xD1,
+        0x69, 0x2E, 0x8E, 0xE1,
+        /* order */
+        0xD3, 0x5E, 0x47, 0x20, 0x36, 0xBC, 0x4F, 0xB7, 0xE1, 0x3C, 0x78, 0x5E,
+        0xD2, 0x01, 0xE0, 0x65, 0xF9, 0x8F, 0xCF, 0xA5, 0xB6, 0x8F, 0x12, 0xA3,
+        0x2D, 0x48, 0x2E, 0xC7, 0xEE, 0x86, 0x58, 0xE9, 0x86, 0x91, 0x55, 0x5B,
+        0x44, 0xC5, 0x93, 0x11
+    }
+};
+
+static const struct {
+    EC_CURVE_DATA h;
+    unsigned char data[0 + 40 * 6];
+} _EC_brainpoolP320t1 = {
+    {
+        NID_X9_62_prime_field, 0, 40, 1
+    },
+    {
+        /* no seed */
+        /* p */
+        0xD3, 0x5E, 0x47, 0x20, 0x36, 0xBC, 0x4F, 0xB7, 0xE1, 0x3C, 0x78, 0x5E,
+        0xD2, 0x01, 0xE0, 0x65, 0xF9, 0x8F, 0xCF, 0xA6, 0xF6, 0xF4, 0x0D, 0xEF,
+        0x4F, 0x92, 0xB9, 0xEC, 0x78, 0x93, 0xEC, 0x28, 0xFC, 0xD4, 0x12, 0xB1,
+        0xF1, 0xB3, 0x2E, 0x27,
+        /* a */
+        0xD3, 0x5E, 0x47, 0x20, 0x36, 0xBC, 0x4F, 0xB7, 0xE1, 0x3C, 0x78, 0x5E,
+        0xD2, 0x01, 0xE0, 0x65, 0xF9, 0x8F, 0xCF, 0xA6, 0xF6, 0xF4, 0x0D, 0xEF,
+        0x4F, 0x92, 0xB9, 0xEC, 0x78, 0x93, 0xEC, 0x28, 0xFC, 0xD4, 0x12, 0xB1,
+        0xF1, 0xB3, 0x2E, 0x24,
+        /* b */
+        0xA7, 0xF5, 0x61, 0xE0, 0x38, 0xEB, 0x1E, 0xD5, 0x60, 0xB3, 0xD1, 0x47,
+        0xDB, 0x78, 0x20, 0x13, 0x06, 0x4C, 0x19, 0xF2, 0x7E, 0xD2, 0x7C, 0x67,
+        0x80, 0xAA, 0xF7, 0x7F, 0xB8, 0xA5, 0x47, 0xCE, 0xB5, 0xB4, 0xFE, 0xF4,
+        0x22, 0x34, 0x03, 0x53,
+        /* x */
+        0x92, 0x5B, 0xE9, 0xFB, 0x01, 0xAF, 0xC6, 0xFB, 0x4D, 0x3E, 0x7D, 0x49,
+        0x90, 0x01, 0x0F, 0x81, 0x34, 0x08, 0xAB, 0x10, 0x6C, 0x4F, 0x09, 0xCB,
+        0x7E, 0xE0, 0x78, 0x68, 0xCC, 0x13, 0x6F, 0xFF, 0x33, 0x57, 0xF6, 0x24,
+        0xA2, 0x1B, 0xED, 0x52,
+        /* y */
+        0x63, 0xBA, 0x3A, 0x7A, 0x27, 0x48, 0x3E, 0xBF, 0x66, 0x71, 0xDB, 0xEF,
+        0x7A, 0xBB, 0x30, 0xEB, 0xEE, 0x08, 0x4E, 0x58, 0xA0, 0xB0, 0x77, 0xAD,
+        0x42, 0xA5, 0xA0, 0x98, 0x9D, 0x1E, 0xE7, 0x1B, 0x1B, 0x9B, 0xC0, 0x45,
+        0x5F, 0xB0, 0xD2, 0xC3,
+        /* order */
+        0xD3, 0x5E, 0x47, 0x20, 0x36, 0xBC, 0x4F, 0xB7, 0xE1, 0x3C, 0x78, 0x5E,
+        0xD2, 0x01, 0xE0, 0x65, 0xF9, 0x8F, 0xCF, 0xA5, 0xB6, 0x8F, 0x12, 0xA3,
+        0x2D, 0x48, 0x2E, 0xC7, 0xEE, 0x86, 0x58, 0xE9, 0x86, 0x91, 0x55, 0x5B,
+        0x44, 0xC5, 0x93, 0x11
+    }
+};
+
+static const struct {
+    EC_CURVE_DATA h;
+    unsigned char data[0 + 48 * 6];
+} _EC_brainpoolP384r1 = {
+    {
+        NID_X9_62_prime_field, 0, 48, 1
+    },
+    {
+        /* no seed */
+        /* p */
+        0x8C, 0xB9, 0x1E, 0x82, 0xA3, 0x38, 0x6D, 0x28, 0x0F, 0x5D, 0x6F, 0x7E,
+        0x50, 0xE6, 0x41, 0xDF, 0x15, 0x2F, 0x71, 0x09, 0xED, 0x54, 0x56, 0xB4,
+        0x12, 0xB1, 0xDA, 0x19, 0x7F, 0xB7, 0x11, 0x23, 0xAC, 0xD3, 0xA7, 0x29,
+        0x90, 0x1D, 0x1A, 0x71, 0x87, 0x47, 0x00, 0x13, 0x31, 0x07, 0xEC, 0x53,
+        /* a */
+        0x7B, 0xC3, 0x82, 0xC6, 0x3D, 0x8C, 0x15, 0x0C, 0x3C, 0x72, 0x08, 0x0A,
+        0xCE, 0x05, 0xAF, 0xA0, 0xC2, 0xBE, 0xA2, 0x8E, 0x4F, 0xB2, 0x27, 0x87,
+        0x13, 0x91, 0x65, 0xEF, 0xBA, 0x91, 0xF9, 0x0F, 0x8A, 0xA5, 0x81, 0x4A,
+        0x50, 0x3A, 0xD4, 0xEB, 0x04, 0xA8, 0xC7, 0xDD, 0x22, 0xCE, 0x28, 0x26,
+        /* b */
+        0x04, 0xA8, 0xC7, 0xDD, 0x22, 0xCE, 0x28, 0x26, 0x8B, 0x39, 0xB5, 0x54,
+        0x16, 0xF0, 0x44, 0x7C, 0x2F, 0xB7, 0x7D, 0xE1, 0x07, 0xDC, 0xD2, 0xA6,
+        0x2E, 0x88, 0x0E, 0xA5, 0x3E, 0xEB, 0x62, 0xD5, 0x7C, 0xB4, 0x39, 0x02,
+        0x95, 0xDB, 0xC9, 0x94, 0x3A, 0xB7, 0x86, 0x96, 0xFA, 0x50, 0x4C, 0x11,
+        /* x */
+        0x1D, 0x1C, 0x64, 0xF0, 0x68, 0xCF, 0x45, 0xFF, 0xA2, 0xA6, 0x3A, 0x81,
+        0xB7, 0xC1, 0x3F, 0x6B, 0x88, 0x47, 0xA3, 0xE7, 0x7E, 0xF1, 0x4F, 0xE3,
+        0xDB, 0x7F, 0xCA, 0xFE, 0x0C, 0xBD, 0x10, 0xE8, 0xE8, 0x26, 0xE0, 0x34,
+        0x36, 0xD6, 0x46, 0xAA, 0xEF, 0x87, 0xB2, 0xE2, 0x47, 0xD4, 0xAF, 0x1E,
+        /* y */
+        0x8A, 0xBE, 0x1D, 0x75, 0x20, 0xF9, 0xC2, 0xA4, 0x5C, 0xB1, 0xEB, 0x8E,
+        0x95, 0xCF, 0xD5, 0x52, 0x62, 0xB7, 0x0B, 0x29, 0xFE, 0xEC, 0x58, 0x64,
+        0xE1, 0x9C, 0x05, 0x4F, 0xF9, 0x91, 0x29, 0x28, 0x0E, 0x46, 0x46, 0x21,
+        0x77, 0x91, 0x81, 0x11, 0x42, 0x82, 0x03, 0x41, 0x26, 0x3C, 0x53, 0x15,
+        /* order */
+        0x8C, 0xB9, 0x1E, 0x82, 0xA3, 0x38, 0x6D, 0x28, 0x0F, 0x5D, 0x6F, 0x7E,
+        0x50, 0xE6, 0x41, 0xDF, 0x15, 0x2F, 0x71, 0x09, 0xED, 0x54, 0x56, 0xB3,
+        0x1F, 0x16, 0x6E, 0x6C, 0xAC, 0x04, 0x25, 0xA7, 0xCF, 0x3A, 0xB6, 0xAF,
+        0x6B, 0x7F, 0xC3, 0x10, 0x3B, 0x88, 0x32, 0x02, 0xE9, 0x04, 0x65, 0x65
+    }
+};
+
+static const struct {
+    EC_CURVE_DATA h;
+    unsigned char data[0 + 48 * 6];
+} _EC_brainpoolP384t1 = {
+    {
+        NID_X9_62_prime_field, 0, 48, 1
+    },
+    {
+        /* no seed */
+        /* p */
+        0x8C, 0xB9, 0x1E, 0x82, 0xA3, 0x38, 0x6D, 0x28, 0x0F, 0x5D, 0x6F, 0x7E,
+        0x50, 0xE6, 0x41, 0xDF, 0x15, 0x2F, 0x71, 0x09, 0xED, 0x54, 0x56, 0xB4,
+        0x12, 0xB1, 0xDA, 0x19, 0x7F, 0xB7, 0x11, 0x23, 0xAC, 0xD3, 0xA7, 0x29,
+        0x90, 0x1D, 0x1A, 0x71, 0x87, 0x47, 0x00, 0x13, 0x31, 0x07, 0xEC, 0x53,
+        /* a */
+        0x8C, 0xB9, 0x1E, 0x82, 0xA3, 0x38, 0x6D, 0x28, 0x0F, 0x5D, 0x6F, 0x7E,
+        0x50, 0xE6, 0x41, 0xDF, 0x15, 0x2F, 0x71, 0x09, 0xED, 0x54, 0x56, 0xB4,
+        0x12, 0xB1, 0xDA, 0x19, 0x7F, 0xB7, 0x11, 0x23, 0xAC, 0xD3, 0xA7, 0x29,
+        0x90, 0x1D, 0x1A, 0x71, 0x87, 0x47, 0x00, 0x13, 0x31, 0x07, 0xEC, 0x50,
+        /* b */
+        0x7F, 0x51, 0x9E, 0xAD, 0xA7, 0xBD, 0xA8, 0x1B, 0xD8, 0x26, 0xDB, 0xA6,
+        0x47, 0x91, 0x0F, 0x8C, 0x4B, 0x93, 0x46, 0xED, 0x8C, 0xCD, 0xC6, 0x4E,
+        0x4B, 0x1A, 0xBD, 0x11, 0x75, 0x6D, 0xCE, 0x1D, 0x20, 0x74, 0xAA, 0x26,
+        0x3B, 0x88, 0x80, 0x5C, 0xED, 0x70, 0x35, 0x5A, 0x33, 0xB4, 0x71, 0xEE,
+        /* x */
+        0x18, 0xDE, 0x98, 0xB0, 0x2D, 0xB9, 0xA3, 0x06, 0xF2, 0xAF, 0xCD, 0x72,
+        0x35, 0xF7, 0x2A, 0x81, 0x9B, 0x80, 0xAB, 0x12, 0xEB, 0xD6, 0x53, 0x17,
+        0x24, 0x76, 0xFE, 0xCD, 0x46, 0x2A, 0xAB, 0xFF, 0xC4, 0xFF, 0x19, 0x1B,
+        0x94, 0x6A, 0x5F, 0x54, 0xD8, 0xD0, 0xAA, 0x2F, 0x41, 0x88, 0x08, 0xCC,
+        /* y */
+        0x25, 0xAB, 0x05, 0x69, 0x62, 0xD3, 0x06, 0x51, 0xA1, 0x14, 0xAF, 0xD2,
+        0x75, 0x5A, 0xD3, 0x36, 0x74, 0x7F, 0x93, 0x47, 0x5B, 0x7A, 0x1F, 0xCA,
+        0x3B, 0x88, 0xF2, 0xB6, 0xA2, 0x08, 0xCC, 0xFE, 0x46, 0x94, 0x08, 0x58,
+        0x4D, 0xC2, 0xB2, 0x91, 0x26, 0x75, 0xBF, 0x5B, 0x9E, 0x58, 0x29, 0x28,
+        /* order */
+        0x8C, 0xB9, 0x1E, 0x82, 0xA3, 0x38, 0x6D, 0x28, 0x0F, 0x5D, 0x6F, 0x7E,
+        0x50, 0xE6, 0x41, 0xDF, 0x15, 0x2F, 0x71, 0x09, 0xED, 0x54, 0x56, 0xB3,
+        0x1F, 0x16, 0x6E, 0x6C, 0xAC, 0x04, 0x25, 0xA7, 0xCF, 0x3A, 0xB6, 0xAF,
+        0x6B, 0x7F, 0xC3, 0x10, 0x3B, 0x88, 0x32, 0x02, 0xE9, 0x04, 0x65, 0x65
+    }
+};
+
+static const struct {
+    EC_CURVE_DATA h;
+    unsigned char data[0 + 64 * 6];
+} _EC_brainpoolP512r1 = {
+    {
+        NID_X9_62_prime_field, 0, 64, 1
+    },
+    {
+        /* no seed */
+        /* p */
+        0xAA, 0xDD, 0x9D, 0xB8, 0xDB, 0xE9, 0xC4, 0x8B, 0x3F, 0xD4, 0xE6, 0xAE,
+        0x33, 0xC9, 0xFC, 0x07, 0xCB, 0x30, 0x8D, 0xB3, 0xB3, 0xC9, 0xD2, 0x0E,
+        0xD6, 0x63, 0x9C, 0xCA, 0x70, 0x33, 0x08, 0x71, 0x7D, 0x4D, 0x9B, 0x00,
+        0x9B, 0xC6, 0x68, 0x42, 0xAE, 0xCD, 0xA1, 0x2A, 0xE6, 0xA3, 0x80, 0xE6,
+        0x28, 0x81, 0xFF, 0x2F, 0x2D, 0x82, 0xC6, 0x85, 0x28, 0xAA, 0x60, 0x56,
+        0x58, 0x3A, 0x48, 0xF3,
+        /* a */
+        0x78, 0x30, 0xA3, 0x31, 0x8B, 0x60, 0x3B, 0x89, 0xE2, 0x32, 0x71, 0x45,
+        0xAC, 0x23, 0x4C, 0xC5, 0x94, 0xCB, 0xDD, 0x8D, 0x3D, 0xF9, 0x16, 0x10,
+        0xA8, 0x34, 0x41, 0xCA, 0xEA, 0x98, 0x63, 0xBC, 0x2D, 0xED, 0x5D, 0x5A,
+        0xA8, 0x25, 0x3A, 0xA1, 0x0A, 0x2E, 0xF1, 0xC9, 0x8B, 0x9A, 0xC8, 0xB5,
+        0x7F, 0x11, 0x17, 0xA7, 0x2B, 0xF2, 0xC7, 0xB9, 0xE7, 0xC1, 0xAC, 0x4D,
+        0x77, 0xFC, 0x94, 0xCA,
+        /* b */
+        0x3D, 0xF9, 0x16, 0x10, 0xA8, 0x34, 0x41, 0xCA, 0xEA, 0x98, 0x63, 0xBC,
+        0x2D, 0xED, 0x5D, 0x5A, 0xA8, 0x25, 0x3A, 0xA1, 0x0A, 0x2E, 0xF1, 0xC9,
+        0x8B, 0x9A, 0xC8, 0xB5, 0x7F, 0x11, 0x17, 0xA7, 0x2B, 0xF2, 0xC7, 0xB9,
+        0xE7, 0xC1, 0xAC, 0x4D, 0x77, 0xFC, 0x94, 0xCA, 0xDC, 0x08, 0x3E, 0x67,
+        0x98, 0x40, 0x50, 0xB7, 0x5E, 0xBA, 0xE5, 0xDD, 0x28, 0x09, 0xBD, 0x63,
+        0x80, 0x16, 0xF7, 0x23,
+        /* x */
+        0x81, 0xAE, 0xE4, 0xBD, 0xD8, 0x2E, 0xD9, 0x64, 0x5A, 0x21, 0x32, 0x2E,
+        0x9C, 0x4C, 0x6A, 0x93, 0x85, 0xED, 0x9F, 0x70, 0xB5, 0xD9, 0x16, 0xC1,
+        0xB4, 0x3B, 0x62, 0xEE, 0xF4, 0xD0, 0x09, 0x8E, 0xFF, 0x3B, 0x1F, 0x78,
+        0xE2, 0xD0, 0xD4, 0x8D, 0x50, 0xD1, 0x68, 0x7B, 0x93, 0xB9, 0x7D, 0x5F,
+        0x7C, 0x6D, 0x50, 0x47, 0x40, 0x6A, 0x5E, 0x68, 0x8B, 0x35, 0x22, 0x09,
+        0xBC, 0xB9, 0xF8, 0x22,
+        /* y */
+        0x7D, 0xDE, 0x38, 0x5D, 0x56, 0x63, 0x32, 0xEC, 0xC0, 0xEA, 0xBF, 0xA9,
+        0xCF, 0x78, 0x22, 0xFD, 0xF2, 0x09, 0xF7, 0x00, 0x24, 0xA5, 0x7B, 0x1A,
+        0xA0, 0x00, 0xC5, 0x5B, 0x88, 0x1F, 0x81, 0x11, 0xB2, 0xDC, 0xDE, 0x49,
+        0x4A, 0x5F, 0x48, 0x5E, 0x5B, 0xCA, 0x4B, 0xD8, 0x8A, 0x27, 0x63, 0xAE,
+        0xD1, 0xCA, 0x2B, 0x2F, 0xA8, 0xF0, 0x54, 0x06, 0x78, 0xCD, 0x1E, 0x0F,
+        0x3A, 0xD8, 0x08, 0x92,
+        /* order */
+        0xAA, 0xDD, 0x9D, 0xB8, 0xDB, 0xE9, 0xC4, 0x8B, 0x3F, 0xD4, 0xE6, 0xAE,
+        0x33, 0xC9, 0xFC, 0x07, 0xCB, 0x30, 0x8D, 0xB3, 0xB3, 0xC9, 0xD2, 0x0E,
+        0xD6, 0x63, 0x9C, 0xCA, 0x70, 0x33, 0x08, 0x70, 0x55, 0x3E, 0x5C, 0x41,
+        0x4C, 0xA9, 0x26, 0x19, 0x41, 0x86, 0x61, 0x19, 0x7F, 0xAC, 0x10, 0x47,
+        0x1D, 0xB1, 0xD3, 0x81, 0x08, 0x5D, 0xDA, 0xDD, 0xB5, 0x87, 0x96, 0x82,
+        0x9C, 0xA9, 0x00, 0x69
+    }
+};
+
+static const struct {
+    EC_CURVE_DATA h;
+    unsigned char data[0 + 64 * 6];
+} _EC_brainpoolP512t1 = {
+    {
+        NID_X9_62_prime_field, 0, 64, 1
+    },
+    {
+        /* no seed */
+        /* p */
+        0xAA, 0xDD, 0x9D, 0xB8, 0xDB, 0xE9, 0xC4, 0x8B, 0x3F, 0xD4, 0xE6, 0xAE,
+        0x33, 0xC9, 0xFC, 0x07, 0xCB, 0x30, 0x8D, 0xB3, 0xB3, 0xC9, 0xD2, 0x0E,
+        0xD6, 0x63, 0x9C, 0xCA, 0x70, 0x33, 0x08, 0x71, 0x7D, 0x4D, 0x9B, 0x00,
+        0x9B, 0xC6, 0x68, 0x42, 0xAE, 0xCD, 0xA1, 0x2A, 0xE6, 0xA3, 0x80, 0xE6,
+        0x28, 0x81, 0xFF, 0x2F, 0x2D, 0x82, 0xC6, 0x85, 0x28, 0xAA, 0x60, 0x56,
+        0x58, 0x3A, 0x48, 0xF3,
+        /* a */
+        0xAA, 0xDD, 0x9D, 0xB8, 0xDB, 0xE9, 0xC4, 0x8B, 0x3F, 0xD4, 0xE6, 0xAE,
+        0x33, 0xC9, 0xFC, 0x07, 0xCB, 0x30, 0x8D, 0xB3, 0xB3, 0xC9, 0xD2, 0x0E,
+        0xD6, 0x63, 0x9C, 0xCA, 0x70, 0x33, 0x08, 0x71, 0x7D, 0x4D, 0x9B, 0x00,
+        0x9B, 0xC6, 0x68, 0x42, 0xAE, 0xCD, 0xA1, 0x2A, 0xE6, 0xA3, 0x80, 0xE6,
+        0x28, 0x81, 0xFF, 0x2F, 0x2D, 0x82, 0xC6, 0x85, 0x28, 0xAA, 0x60, 0x56,
+        0x58, 0x3A, 0x48, 0xF0,
+        /* b */
+        0x7C, 0xBB, 0xBC, 0xF9, 0x44, 0x1C, 0xFA, 0xB7, 0x6E, 0x18, 0x90, 0xE4,
+        0x68, 0x84, 0xEA, 0xE3, 0x21, 0xF7, 0x0C, 0x0B, 0xCB, 0x49, 0x81, 0x52,
+        0x78, 0x97, 0x50, 0x4B, 0xEC, 0x3E, 0x36, 0xA6, 0x2B, 0xCD, 0xFA, 0x23,
+        0x04, 0x97, 0x65, 0x40, 0xF6, 0x45, 0x00, 0x85, 0xF2, 0xDA, 0xE1, 0x45,
+        0xC2, 0x25, 0x53, 0xB4, 0x65, 0x76, 0x36, 0x89, 0x18, 0x0E, 0xA2, 0x57,
+        0x18, 0x67, 0x42, 0x3E,
+        /* x */
+        0x64, 0x0E, 0xCE, 0x5C, 0x12, 0x78, 0x87, 0x17, 0xB9, 0xC1, 0xBA, 0x06,
+        0xCB, 0xC2, 0xA6, 0xFE, 0xBA, 0x85, 0x84, 0x24, 0x58, 0xC5, 0x6D, 0xDE,
+        0x9D, 0xB1, 0x75, 0x8D, 0x39, 0xC0, 0x31, 0x3D, 0x82, 0xBA, 0x51, 0x73,
+        0x5C, 0xDB, 0x3E, 0xA4, 0x99, 0xAA, 0x77, 0xA7, 0xD6, 0x94, 0x3A, 0x64,
+        0xF7, 0xA3, 0xF2, 0x5F, 0xE2, 0x6F, 0x06, 0xB5, 0x1B, 0xAA, 0x26, 0x96,
+        0xFA, 0x90, 0x35, 0xDA,
+        /* y */
+        0x5B, 0x53, 0x4B, 0xD5, 0x95, 0xF5, 0xAF, 0x0F, 0xA2, 0xC8, 0x92, 0x37,
+        0x6C, 0x84, 0xAC, 0xE1, 0xBB, 0x4E, 0x30, 0x19, 0xB7, 0x16, 0x34, 0xC0,
+        0x11, 0x31, 0x15, 0x9C, 0xAE, 0x03, 0xCE, 0xE9, 0xD9, 0x93, 0x21, 0x84,
+        0xBE, 0xEF, 0x21, 0x6B, 0xD7, 0x1D, 0xF2, 0xDA, 0xDF, 0x86, 0xA6, 0x27,
+        0x30, 0x6E, 0xCF, 0xF9, 0x6D, 0xBB, 0x8B, 0xAC, 0xE1, 0x98, 0xB6, 0x1E,
+        0x00, 0xF8, 0xB3, 0x32,
+        /* order */
+        0xAA, 0xDD, 0x9D, 0xB8, 0xDB, 0xE9, 0xC4, 0x8B, 0x3F, 0xD4, 0xE6, 0xAE,
+        0x33, 0xC9, 0xFC, 0x07, 0xCB, 0x30, 0x8D, 0xB3, 0xB3, 0xC9, 0xD2, 0x0E,
+        0xD6, 0x63, 0x9C, 0xCA, 0x70, 0x33, 0x08, 0x70, 0x55, 0x3E, 0x5C, 0x41,
+        0x4C, 0xA9, 0x26, 0x19, 0x41, 0x86, 0x61, 0x19, 0x7F, 0xAC, 0x10, 0x47,
+        0x1D, 0xB1, 0xD3, 0x81, 0x08, 0x5D, 0xDA, 0xDD, 0xB5, 0x87, 0x96, 0x82,
+        0x9C, 0xA9, 0x00, 0x69
+    }
+};
+
+#ifndef OPENSSL_NO_SM2
+static const struct {
+    EC_CURVE_DATA h;
+    unsigned char data[0 + 32 * 6];
+} _EC_SM2_PRIME_256V1 = {
+    {
+        NID_X9_62_prime_field, 0, 32, 1
+    },
+    {
+        /* no seed */
+        /* p */
+        0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
+        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+        /* a */
+        0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
+        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC,
+        /* b */
+        0x28, 0xE9, 0xFA, 0x9E, 0x9D, 0x9F, 0x5E, 0x34, 0x4D, 0x5A, 0x9E, 0x4B,
+        0xCF, 0x65, 0x09, 0xA7, 0xF3, 0x97, 0x89, 0xF5, 0x15, 0xAB, 0x8F, 0x92,
+        0xDD, 0xBC, 0xBD, 0x41, 0x4D, 0x94, 0x0E, 0x93,
+        /* x */
+        0x32, 0xC4, 0xAE, 0x2C, 0x1F, 0x19, 0x81, 0x19, 0x5F, 0x99, 0x04, 0x46,
+        0x6A, 0x39, 0xC9, 0x94, 0x8F, 0xE3, 0x0B, 0xBF, 0xF2, 0x66, 0x0B, 0xE1,
+        0x71, 0x5A, 0x45, 0x89, 0x33, 0x4C, 0x74, 0xC7,
+        /* y */
+        0xBC, 0x37, 0x36, 0xA2, 0xF4, 0xF6, 0x77, 0x9C, 0x59, 0xBD, 0xCE, 0xE3,
+        0x6B, 0x69, 0x21, 0x53, 0xD0, 0xA9, 0x87, 0x7C, 0xC6, 0x2A, 0x47, 0x40,
+        0x02, 0xDF, 0x32, 0xE5, 0x21, 0x39, 0xF0, 0xA0,
+        /* order */
+        0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+        0xFF, 0xFF, 0xFF, 0xFF, 0x72, 0x03, 0xDF, 0x6B, 0x21, 0xC6, 0x05, 0x2B,
+        0x53, 0xBB, 0xF4, 0x09, 0x39, 0xD5, 0x41, 0x23
+    }
+};
+
+static const struct {
+    EC_CURVE_DATA h;
+    unsigned char data[0 + 24 * 6];
+} _EC_WAPI_PRIME_192V1 = {
+    {
+        NID_X9_62_prime_field, 0, 24, 1
+    },
+    {
+        /* no seed */
+        /* p */
+        0xBD, 0xB6, 0xF4, 0xFE, 0x3E, 0x8B, 0x1D, 0x9E, 0x0D, 0xA8, 0xC0, 0xD4,
+        0x6F, 0x4C, 0x31, 0x8C, 0xEF, 0xE4, 0xAF, 0xE3, 0xB6, 0xB8, 0x55, 0x1F,
+        /* a */
+        0xBB, 0x8E, 0x5E, 0x8F, 0xBC, 0x11, 0x5E, 0x13, 0x9F, 0xE6, 0xA8, 0x14,
+        0xFE, 0x48, 0xAA, 0xA6, 0xF0, 0xAD, 0xA1, 0xAA, 0x5D, 0xF9, 0x19, 0x85,
+        /* b */
+        0x18, 0x54, 0xBE, 0xBD, 0xC3, 0x1B, 0x21, 0xB7, 0xAE, 0xFC, 0x80, 0xAB,
+        0x0E, 0xCD, 0x10, 0xD5, 0xB1, 0xB3, 0x30, 0x8E, 0x6D, 0xBF, 0x11, 0xC1,
+        /* x */
+        0x4A, 0xD5, 0xF7, 0x04, 0x8D, 0xE7, 0x09, 0xAD, 0x51, 0x23, 0x6D, 0xE6,
+        0x5E, 0x4D, 0x4B, 0x48, 0x2C, 0x83, 0x6D, 0xC6, 0xE4, 0x10, 0x66, 0x40,
+        /* y */
+        0x02, 0xBB, 0x3A, 0x02, 0xD4, 0xAA, 0xAD, 0xAC, 0xAE, 0x24, 0x81, 0x7A,
+        0x4C, 0xA3, 0xA1, 0xB0, 0x14, 0xB5, 0x27, 0x04, 0x32, 0xDB, 0x27, 0xD2,
+        /* order */
+        0xBD, 0xB6, 0xF4, 0xFE, 0x3E, 0x8B, 0x1D, 0x9E, 0x0D, 0xA8, 0xC0, 0xD4,
+        0x0F, 0xC9, 0x62, 0x19, 0x5D, 0xFA, 0xE7, 0x6F, 0x56, 0x56, 0x46, 0x77,
+    }
+};
+#endif
+
+#ifndef OPENSSL_NO_SM9
+static const struct {
+    EC_CURVE_DATA h;
+    unsigned char data[0 + 32 * 6];
+} _EC_SM9_BN_256V1 = {
+    {
+        NID_X9_62_prime_field, 0, 32, 1
+    },
+    {
+        /* no seed */
+        /* p */
+        0xB6, 0x40, 0x00, 0x00, 0x02, 0xA3, 0xA6, 0xF1, 0xD6, 0x03, 0xAB, 0x4F,
+        0xF5, 0x8E, 0xC7, 0x45, 0x21, 0xF2, 0x93, 0x4B, 0x1A, 0x7A, 0xEE, 0xDB,
+        0xE5, 0x6F, 0x9B, 0x27, 0xE3, 0x51, 0x45, 0x7D,
+        /* a */
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	/* b */
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05,
+	/* x */
+	0x93, 0xDE, 0x05, 0x1D, 0x62, 0xBF, 0x71, 0x8F, 0xF5, 0xED, 0x07, 0x04,
+        0x48, 0x7D, 0x01, 0xD6, 0xE1, 0xE4, 0x08, 0x69, 0x09, 0xDC, 0x32, 0x80,
+        0xE8, 0xC4, 0xE4, 0x81, 0x7C, 0x66, 0xDD, 0xDD,
+	/* y */
+	0x21, 0xFE, 0x8D, 0xDA, 0x4F, 0x21, 0xE6, 0x07, 0x63, 0x10, 0x65, 0x12,
+	0x5C, 0x39, 0x5B, 0xBC, 0x1C, 0x1C, 0x00, 0xCB, 0xFA, 0x60, 0x24, 0x35,
+	0x0C, 0x46, 0x4C, 0xD7, 0x0A, 0x3E, 0xA6, 0x16,
+	/* order */
+        0xB6, 0x40, 0x00, 0x00, 0x02, 0xA3, 0xA6, 0xF1, 0xD6, 0x03, 0xAB, 0x4F,
+        0xF5, 0x8E, 0xC7, 0x44, 0x49, 0xF2, 0x93, 0x4B, 0x18, 0xEA, 0x8B, 0xEE,
+        0xE5, 0x6E, 0xE1, 0x9C, 0xD6, 0x9E, 0xCF, 0x25,
+   }
+};
+#endif
+
+typedef struct _ec_list_element_st {
+    int nid;
+    const EC_CURVE_DATA *data;
+    const EC_METHOD *(*meth) (void);
+    const char *comment;
+} ec_list_element;
+
+static const ec_list_element curve_list[] = {
+    /* prime field curves */
+    /* secg curves */
+    {NID_secp112r1, &_EC_SECG_PRIME_112R1.h, 0,
+     "SECG/WTLS curve over a 112 bit prime field"},
+    {NID_secp112r2, &_EC_SECG_PRIME_112R2.h, 0,
+     "SECG curve over a 112 bit prime field"},
+    {NID_secp128r1, &_EC_SECG_PRIME_128R1.h, 0,
+     "SECG curve over a 128 bit prime field"},
+    {NID_secp128r2, &_EC_SECG_PRIME_128R2.h, 0,
+     "SECG curve over a 128 bit prime field"},
+    {NID_secp160k1, &_EC_SECG_PRIME_160K1.h, 0,
+     "SECG curve over a 160 bit prime field"},
+    {NID_secp160r1, &_EC_SECG_PRIME_160R1.h, 0,
+     "SECG curve over a 160 bit prime field"},
+    {NID_secp160r2, &_EC_SECG_PRIME_160R2.h, 0,
+     "SECG/WTLS curve over a 160 bit prime field"},
+    /* SECG secp192r1 is the same as X9.62 prime192v1 and hence omitted */
+    {NID_secp192k1, &_EC_SECG_PRIME_192K1.h, 0,
+     "SECG curve over a 192 bit prime field"},
+    {NID_secp224k1, &_EC_SECG_PRIME_224K1.h, 0,
+     "SECG curve over a 224 bit prime field"},
+#ifndef OPENSSL_NO_EC_NISTP_64_GCC_128
+    {NID_secp224r1, &_EC_NIST_PRIME_224.h, EC_GFp_nistp224_method,
+     "NIST/SECG curve over a 224 bit prime field"},
+#else
+    {NID_secp224r1, &_EC_NIST_PRIME_224.h, 0,
+     "NIST/SECG curve over a 224 bit prime field"},
+#endif
+    {NID_secp256k1, &_EC_SECG_PRIME_256K1.h, 0,
+     "SECG curve over a 256 bit prime field"},
+    /* SECG secp256r1 is the same as X9.62 prime256v1 and hence omitted */
+    {NID_secp384r1, &_EC_NIST_PRIME_384.h, 0,
+     "NIST/SECG curve over a 384 bit prime field"},
+#ifndef OPENSSL_NO_EC_NISTP_64_GCC_128
+    {NID_secp521r1, &_EC_NIST_PRIME_521.h, EC_GFp_nistp521_method,
+     "NIST/SECG curve over a 521 bit prime field"},
+#else
+    {NID_secp521r1, &_EC_NIST_PRIME_521.h, 0,
+     "NIST/SECG curve over a 521 bit prime field"},
+#endif
+    /* X9.62 curves */
+    {NID_X9_62_prime192v1, &_EC_NIST_PRIME_192.h, 0,
+     "NIST/X9.62/SECG curve over a 192 bit prime field"},
+    {NID_X9_62_prime192v2, &_EC_X9_62_PRIME_192V2.h, 0,
+     "X9.62 curve over a 192 bit prime field"},
+    {NID_X9_62_prime192v3, &_EC_X9_62_PRIME_192V3.h, 0,
+     "X9.62 curve over a 192 bit prime field"},
+    {NID_X9_62_prime239v1, &_EC_X9_62_PRIME_239V1.h, 0,
+     "X9.62 curve over a 239 bit prime field"},
+    {NID_X9_62_prime239v2, &_EC_X9_62_PRIME_239V2.h, 0,
+     "X9.62 curve over a 239 bit prime field"},
+    {NID_X9_62_prime239v3, &_EC_X9_62_PRIME_239V3.h, 0,
+     "X9.62 curve over a 239 bit prime field"},
+    {NID_X9_62_prime256v1, &_EC_X9_62_PRIME_256V1.h,
+#if defined(ECP_NISTZ256_ASM)
+     EC_GFp_nistz256_method,
+#elif !defined(OPENSSL_NO_EC_NISTP_64_GCC_128)
+     EC_GFp_nistp256_method,
+#else
+     0,
+#endif
+     "X9.62/SECG curve over a 256 bit prime field"},
+#ifndef OPENSSL_NO_EC2M
+    /* characteristic two field curves */
+    /* NIST/SECG curves */
+    {NID_sect113r1, &_EC_SECG_CHAR2_113R1.h, 0,
+     "SECG curve over a 113 bit binary field"},
+    {NID_sect113r2, &_EC_SECG_CHAR2_113R2.h, 0,
+     "SECG curve over a 113 bit binary field"},
+    {NID_sect131r1, &_EC_SECG_CHAR2_131R1.h, 0,
+     "SECG/WTLS curve over a 131 bit binary field"},
+    {NID_sect131r2, &_EC_SECG_CHAR2_131R2.h, 0,
+     "SECG curve over a 131 bit binary field"},
+    {NID_sect163k1, &_EC_NIST_CHAR2_163K.h, 0,
+     "NIST/SECG/WTLS curve over a 163 bit binary field"},
+    {NID_sect163r1, &_EC_SECG_CHAR2_163R1.h, 0,
+     "SECG curve over a 163 bit binary field"},
+    {NID_sect163r2, &_EC_NIST_CHAR2_163B.h, 0,
+     "NIST/SECG curve over a 163 bit binary field"},
+    {NID_sect193r1, &_EC_SECG_CHAR2_193R1.h, 0,
+     "SECG curve over a 193 bit binary field"},
+    {NID_sect193r2, &_EC_SECG_CHAR2_193R2.h, 0,
+     "SECG curve over a 193 bit binary field"},
+    {NID_sect233k1, &_EC_NIST_CHAR2_233K.h, 0,
+     "NIST/SECG/WTLS curve over a 233 bit binary field"},
+    {NID_sect233r1, &_EC_NIST_CHAR2_233B.h, 0,
+     "NIST/SECG/WTLS curve over a 233 bit binary field"},
+    {NID_sect239k1, &_EC_SECG_CHAR2_239K1.h, 0,
+     "SECG curve over a 239 bit binary field"},
+    {NID_sect283k1, &_EC_NIST_CHAR2_283K.h, 0,
+     "NIST/SECG curve over a 283 bit binary field"},
+    {NID_sect283r1, &_EC_NIST_CHAR2_283B.h, 0,
+     "NIST/SECG curve over a 283 bit binary field"},
+    {NID_sect409k1, &_EC_NIST_CHAR2_409K.h, 0,
+     "NIST/SECG curve over a 409 bit binary field"},
+    {NID_sect409r1, &_EC_NIST_CHAR2_409B.h, 0,
+     "NIST/SECG curve over a 409 bit binary field"},
+    {NID_sect571k1, &_EC_NIST_CHAR2_571K.h, 0,
+     "NIST/SECG curve over a 571 bit binary field"},
+    {NID_sect571r1, &_EC_NIST_CHAR2_571B.h, 0,
+     "NIST/SECG curve over a 571 bit binary field"},
+    /* X9.62 curves */
+    {NID_X9_62_c2pnb163v1, &_EC_X9_62_CHAR2_163V1.h, 0,
+     "X9.62 curve over a 163 bit binary field"},
+    {NID_X9_62_c2pnb163v2, &_EC_X9_62_CHAR2_163V2.h, 0,
+     "X9.62 curve over a 163 bit binary field"},
+    {NID_X9_62_c2pnb163v3, &_EC_X9_62_CHAR2_163V3.h, 0,
+     "X9.62 curve over a 163 bit binary field"},
+    {NID_X9_62_c2pnb176v1, &_EC_X9_62_CHAR2_176V1.h, 0,
+     "X9.62 curve over a 176 bit binary field"},
+    {NID_X9_62_c2tnb191v1, &_EC_X9_62_CHAR2_191V1.h, 0,
+     "X9.62 curve over a 191 bit binary field"},
+    {NID_X9_62_c2tnb191v2, &_EC_X9_62_CHAR2_191V2.h, 0,
+     "X9.62 curve over a 191 bit binary field"},
+    {NID_X9_62_c2tnb191v3, &_EC_X9_62_CHAR2_191V3.h, 0,
+     "X9.62 curve over a 191 bit binary field"},
+    {NID_X9_62_c2pnb208w1, &_EC_X9_62_CHAR2_208W1.h, 0,
+     "X9.62 curve over a 208 bit binary field"},
+    {NID_X9_62_c2tnb239v1, &_EC_X9_62_CHAR2_239V1.h, 0,
+     "X9.62 curve over a 239 bit binary field"},
+    {NID_X9_62_c2tnb239v2, &_EC_X9_62_CHAR2_239V2.h, 0,
+     "X9.62 curve over a 239 bit binary field"},
+    {NID_X9_62_c2tnb239v3, &_EC_X9_62_CHAR2_239V3.h, 0,
+     "X9.62 curve over a 239 bit binary field"},
+    {NID_X9_62_c2pnb272w1, &_EC_X9_62_CHAR2_272W1.h, 0,
+     "X9.62 curve over a 272 bit binary field"},
+    {NID_X9_62_c2pnb304w1, &_EC_X9_62_CHAR2_304W1.h, 0,
+     "X9.62 curve over a 304 bit binary field"},
+    {NID_X9_62_c2tnb359v1, &_EC_X9_62_CHAR2_359V1.h, 0,
+     "X9.62 curve over a 359 bit binary field"},
+    {NID_X9_62_c2pnb368w1, &_EC_X9_62_CHAR2_368W1.h, 0,
+     "X9.62 curve over a 368 bit binary field"},
+    {NID_X9_62_c2tnb431r1, &_EC_X9_62_CHAR2_431R1.h, 0,
+     "X9.62 curve over a 431 bit binary field"},
+    /*
+     * the WAP/WTLS curves [unlike SECG, spec has its own OIDs for curves
+     * from X9.62]
+     */
+    {NID_wap_wsg_idm_ecid_wtls1, &_EC_WTLS_1.h, 0,
+     "WTLS curve over a 113 bit binary field"},
+    {NID_wap_wsg_idm_ecid_wtls3, &_EC_NIST_CHAR2_163K.h, 0,
+     "NIST/SECG/WTLS curve over a 163 bit binary field"},
+    {NID_wap_wsg_idm_ecid_wtls4, &_EC_SECG_CHAR2_113R1.h, 0,
+     "SECG curve over a 113 bit binary field"},
+    {NID_wap_wsg_idm_ecid_wtls5, &_EC_X9_62_CHAR2_163V1.h, 0,
+     "X9.62 curve over a 163 bit binary field"},
+#endif
+    {NID_wap_wsg_idm_ecid_wtls6, &_EC_SECG_PRIME_112R1.h, 0,
+     "SECG/WTLS curve over a 112 bit prime field"},
+    {NID_wap_wsg_idm_ecid_wtls7, &_EC_SECG_PRIME_160R2.h, 0,
+     "SECG/WTLS curve over a 160 bit prime field"},
+    {NID_wap_wsg_idm_ecid_wtls8, &_EC_WTLS_8.h, 0,
+     "WTLS curve over a 112 bit prime field"},
+    {NID_wap_wsg_idm_ecid_wtls9, &_EC_WTLS_9.h, 0,
+     "WTLS curve over a 160 bit prime field"},
+#ifndef OPENSSL_NO_EC2M
+    {NID_wap_wsg_idm_ecid_wtls10, &_EC_NIST_CHAR2_233K.h, 0,
+     "NIST/SECG/WTLS curve over a 233 bit binary field"},
+    {NID_wap_wsg_idm_ecid_wtls11, &_EC_NIST_CHAR2_233B.h, 0,
+     "NIST/SECG/WTLS curve over a 233 bit binary field"},
+#endif
+    {NID_wap_wsg_idm_ecid_wtls12, &_EC_WTLS_12.h, 0,
+     "WTLS curve over a 224 bit prime field"},
+#ifndef OPENSSL_NO_EC2M
+    /* IPSec curves */
+    {NID_ipsec3, &_EC_IPSEC_155_ID3.h, 0,
+     "\n\tIPSec/IKE/Oakley curve #3 over a 155 bit binary field.\n"
+     "\tNot suitable for ECDSA.\n\tQuestionable extension field!"},
+    {NID_ipsec4, &_EC_IPSEC_185_ID4.h, 0,
+     "\n\tIPSec/IKE/Oakley curve #4 over a 185 bit binary field.\n"
+     "\tNot suitable for ECDSA.\n\tQuestionable extension field!"},
+#endif
+    /* brainpool curves */
+    {NID_brainpoolP160r1, &_EC_brainpoolP160r1.h, 0,
+     "RFC 5639 curve over a 160 bit prime field"},
+    {NID_brainpoolP160t1, &_EC_brainpoolP160t1.h, 0,
+     "RFC 5639 curve over a 160 bit prime field"},
+    {NID_brainpoolP192r1, &_EC_brainpoolP192r1.h, 0,
+     "RFC 5639 curve over a 192 bit prime field"},
+    {NID_brainpoolP192t1, &_EC_brainpoolP192t1.h, 0,
+     "RFC 5639 curve over a 192 bit prime field"},
+    {NID_brainpoolP224r1, &_EC_brainpoolP224r1.h, 0,
+     "RFC 5639 curve over a 224 bit prime field"},
+    {NID_brainpoolP224t1, &_EC_brainpoolP224t1.h, 0,
+     "RFC 5639 curve over a 224 bit prime field"},
+    {NID_brainpoolP256r1, &_EC_brainpoolP256r1.h, 0,
+     "RFC 5639 curve over a 256 bit prime field"},
+    {NID_brainpoolP256t1, &_EC_brainpoolP256t1.h, 0,
+     "RFC 5639 curve over a 256 bit prime field"},
+    {NID_brainpoolP320r1, &_EC_brainpoolP320r1.h, 0,
+     "RFC 5639 curve over a 320 bit prime field"},
+    {NID_brainpoolP320t1, &_EC_brainpoolP320t1.h, 0,
+     "RFC 5639 curve over a 320 bit prime field"},
+    {NID_brainpoolP384r1, &_EC_brainpoolP384r1.h, 0,
+     "RFC 5639 curve over a 384 bit prime field"},
+    {NID_brainpoolP384t1, &_EC_brainpoolP384t1.h, 0,
+     "RFC 5639 curve over a 384 bit prime field"},
+    {NID_brainpoolP512r1, &_EC_brainpoolP512r1.h, 0,
+     "RFC 5639 curve over a 512 bit prime field"},
+    {NID_brainpoolP512t1, &_EC_brainpoolP512t1.h, 0,
+     "RFC 5639 curve over a 512 bit prime field"},
+#ifndef OPENSSL_NO_SM2
+    {NID_sm2p256v1, &_EC_SM2_PRIME_256V1.h,
+# if defined(ECP_NISTZ256_ASM) && BN_BITS2 == 64 && !defined(GMSSL_NO_TURBO)
+     EC_GFp_sm2z256_method,
+# elif !defined(OPENSSL_NO_EC_NISTP_64_GCC_128)
+     EC_GFp_sm2p256_method,
+# else
+     0,
+# endif
+     "SM2 curve over a 256 bit prime field"},
+    {NID_wapip192v1, &_EC_WAPI_PRIME_192V1.h, 0,
+     "WAPI curve over a 192 bit prime field"},
+#endif
+#ifndef OPENSSL_NO_SM9
+    {NID_sm9bn256v1, &_EC_SM9_BN_256V1.h, 0,
+     "SM9 BN curve over a 256 bit prime field"},
+#endif
+};
+
+#define curve_list_length OSSL_NELEM(curve_list)
+
+static EC_GROUP *ec_group_new_from_data(const ec_list_element curve)
+{
+    EC_GROUP *group = NULL;
+    EC_POINT *P = NULL;
+    BN_CTX *ctx = NULL;
+    BIGNUM *p = NULL, *a = NULL, *b = NULL, *x = NULL, *y = NULL, *order =
+        NULL;
+    int ok = 0;
+    int seed_len, param_len;
+    const EC_METHOD *meth;
+    const EC_CURVE_DATA *data;
+    const unsigned char *params;
+
+    /* If no curve data curve method must handle everything */
+    if (curve.data == NULL)
+        return EC_GROUP_new(curve.meth != NULL ? curve.meth() : NULL);
+
+    if ((ctx = BN_CTX_new()) == NULL) {
+        ECerr(EC_F_EC_GROUP_NEW_FROM_DATA, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+
+    data = curve.data;
+    seed_len = data->seed_len;
+    param_len = data->param_len;
+    params = (const unsigned char *)(data + 1); /* skip header */
+    params += seed_len;         /* skip seed */
+
+    if ((p = BN_bin2bn(params + 0 * param_len, param_len, NULL)) == NULL
+        || (a = BN_bin2bn(params + 1 * param_len, param_len, NULL)) == NULL
+        || (b = BN_bin2bn(params + 2 * param_len, param_len, NULL)) == NULL) {
+        ECerr(EC_F_EC_GROUP_NEW_FROM_DATA, ERR_R_BN_LIB);
+        goto err;
+    }
+
+    if (curve.meth != 0) {
+        meth = curve.meth();
+        if (((group = EC_GROUP_new(meth)) == NULL) ||
+            (!(group->meth->group_set_curve(group, p, a, b, ctx)))) {
+            ECerr(EC_F_EC_GROUP_NEW_FROM_DATA, ERR_R_EC_LIB);
+            goto err;
+        }
+    } else if (data->field_type == NID_X9_62_prime_field) {
+        if ((group = EC_GROUP_new_curve_GFp(p, a, b, ctx)) == NULL) {
+            ECerr(EC_F_EC_GROUP_NEW_FROM_DATA, ERR_R_EC_LIB);
+            goto err;
+        }
+    }
+#ifndef OPENSSL_NO_EC2M
+    else {                      /* field_type ==
+                                 * NID_X9_62_characteristic_two_field */
+
+        if ((group = EC_GROUP_new_curve_GF2m(p, a, b, ctx)) == NULL) {
+            ECerr(EC_F_EC_GROUP_NEW_FROM_DATA, ERR_R_EC_LIB);
+            goto err;
+        }
+    }
+#endif
+
+    if ((P = EC_POINT_new(group)) == NULL) {
+        ECerr(EC_F_EC_GROUP_NEW_FROM_DATA, ERR_R_EC_LIB);
+        goto err;
+    }
+
+    if ((x = BN_bin2bn(params + 3 * param_len, param_len, NULL)) == NULL
+        || (y = BN_bin2bn(params + 4 * param_len, param_len, NULL)) == NULL) {
+        ECerr(EC_F_EC_GROUP_NEW_FROM_DATA, ERR_R_BN_LIB);
+        goto err;
+    }
+    if (!EC_POINT_set_affine_coordinates_GFp(group, P, x, y, ctx)) {
+        ECerr(EC_F_EC_GROUP_NEW_FROM_DATA, ERR_R_EC_LIB);
+        goto err;
+    }
+    if ((order = BN_bin2bn(params + 5 * param_len, param_len, NULL)) == NULL
+        || !BN_set_word(x, (BN_ULONG)data->cofactor)) {
+        ECerr(EC_F_EC_GROUP_NEW_FROM_DATA, ERR_R_BN_LIB);
+        goto err;
+    }
+    if (!EC_GROUP_set_generator(group, P, order, x)) {
+        ECerr(EC_F_EC_GROUP_NEW_FROM_DATA, ERR_R_EC_LIB);
+        goto err;
+    }
+    if (seed_len) {
+        if (!EC_GROUP_set_seed(group, params - seed_len, seed_len)) {
+            ECerr(EC_F_EC_GROUP_NEW_FROM_DATA, ERR_R_EC_LIB);
+            goto err;
+        }
+    }
+    ok = 1;
+ err:
+    if (!ok) {
+        EC_GROUP_free(group);
+        group = NULL;
+    }
+    EC_POINT_free(P);
+    BN_CTX_free(ctx);
+    BN_free(p);
+    BN_free(a);
+    BN_free(b);
+    BN_free(order);
+    BN_free(x);
+    BN_free(y);
+    return group;
+}
+
+EC_GROUP *EC_GROUP_new_by_curve_name(int nid)
+{
+    size_t i;
+    EC_GROUP *ret = NULL;
+
+    if (nid <= 0)
+        return NULL;
+
+    for (i = 0; i < curve_list_length; i++)
+        if (curve_list[i].nid == nid) {
+            ret = ec_group_new_from_data(curve_list[i]);
+            break;
+        }
+
+    if (ret == NULL) {
+        ECerr(EC_F_EC_GROUP_NEW_BY_CURVE_NAME, EC_R_UNKNOWN_GROUP);
+        return NULL;
+    }
+
+    EC_GROUP_set_curve_name(ret, nid);
+
+    return ret;
+}
+
+size_t EC_get_builtin_curves(EC_builtin_curve *r, size_t nitems)
+{
+    size_t i, min;
+
+    if (r == NULL || nitems == 0)
+        return curve_list_length;
+
+    min = nitems < curve_list_length ? nitems : curve_list_length;
+
+    for (i = 0; i < min; i++) {
+        r[i].nid = curve_list[i].nid;
+        r[i].comment = curve_list[i].comment;
+    }
+
+    return curve_list_length;
+}
+
+/* Functions to translate between common NIST curve names and NIDs */
+
+typedef struct {
+    const char *name;           /* NIST Name of curve */
+    int nid;                    /* Curve NID */
+} EC_NIST_NAME;
+
+static EC_NIST_NAME nist_curves[] = {
+    {"B-163", NID_sect163r2},
+    {"B-233", NID_sect233r1},
+    {"B-283", NID_sect283r1},
+    {"B-409", NID_sect409r1},
+    {"B-571", NID_sect571r1},
+    {"K-163", NID_sect163k1},
+    {"K-233", NID_sect233k1},
+    {"K-283", NID_sect283k1},
+    {"K-409", NID_sect409k1},
+    {"K-571", NID_sect571k1},
+    {"P-192", NID_X9_62_prime192v1},
+    {"P-224", NID_secp224r1},
+    {"P-256", NID_X9_62_prime256v1},
+    {"P-384", NID_secp384r1},
+    {"P-521", NID_secp521r1},
+    {"SM2",   NID_sm2p256v1}
+};
+
+const char *EC_curve_nid2nist(int nid)
+{
+    size_t i;
+    for (i = 0; i < OSSL_NELEM(nist_curves); i++) {
+        if (nist_curves[i].nid == nid)
+            return nist_curves[i].name;
+    }
+    return NULL;
+}
+
+int EC_curve_nist2nid(const char *name)
+{
+    size_t i;
+    for (i = 0; i < OSSL_NELEM(nist_curves); i++) {
+        if (strcmp(nist_curves[i].name, name) == 0)
+            return nist_curves[i].nid;
+    }
+    return NID_undef;
+}
diff --git a/crypto/ec/ec_cvt.c b/crypto/ec/ec_cvt.c
new file mode 100644
index 000000000..bfff6d65f
--- /dev/null
+++ b/crypto/ec/ec_cvt.c
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2001-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/* ====================================================================
+ * Copyright 2002 Sun Microsystems, Inc. ALL RIGHTS RESERVED.
+ *
+ * Portions of the attached software ("Contribution") are developed by
+ * SUN MICROSYSTEMS, INC., and are contributed to the OpenSSL project.
+ *
+ * The Contribution is licensed pursuant to the OpenSSL open source
+ * license provided above.
+ *
+ * The elliptic curve binary polynomial software is originally written by
+ * Sheueling Chang Shantz and Douglas Stebila of Sun Microsystems Laboratories.
+ *
+ */
+
+#include 
+#include "ec_lcl.h"
+
+EC_GROUP *EC_GROUP_new_curve_GFp(const BIGNUM *p, const BIGNUM *a,
+                                 const BIGNUM *b, BN_CTX *ctx)
+{
+    const EC_METHOD *meth;
+    EC_GROUP *ret;
+
+#if defined(OPENSSL_BN_ASM_MONT)
+    /*
+     * This might appear controversial, but the fact is that generic
+     * prime method was observed to deliver better performance even
+     * for NIST primes on a range of platforms, e.g.: 60%-15%
+     * improvement on IA-64, ~25% on ARM, 30%-90% on P4, 20%-25%
+     * in 32-bit build and 35%--12% in 64-bit build on Core2...
+     * Coefficients are relative to optimized bn_nist.c for most
+     * intensive ECDSA verify and ECDH operations for 192- and 521-
+     * bit keys respectively. Choice of these boundary values is
+     * arguable, because the dependency of improvement coefficient
+     * from key length is not a "monotone" curve. For example while
+     * 571-bit result is 23% on ARM, 384-bit one is -1%. But it's
+     * generally faster, sometimes "respectfully" faster, sometimes
+     * "tolerably" slower... What effectively happens is that loop
+     * with bn_mul_add_words is put against bn_mul_mont, and the
+     * latter "wins" on short vectors. Correct solution should be
+     * implementing dedicated NxN multiplication subroutines for
+     * small N. But till it materializes, let's stick to generic
+     * prime method...
+     *                                              
+     */
+    meth = EC_GFp_mont_method();
+#else
+    if (BN_nist_mod_func(p))
+        meth = EC_GFp_nist_method();
+    else
+        meth = EC_GFp_mont_method();
+#endif
+
+    ret = EC_GROUP_new(meth);
+    if (ret == NULL)
+        return NULL;
+
+    if (!EC_GROUP_set_curve_GFp(ret, p, a, b, ctx)) {
+        EC_GROUP_clear_free(ret);
+        return NULL;
+    }
+
+    return ret;
+}
+
+#ifndef OPENSSL_NO_EC2M
+EC_GROUP *EC_GROUP_new_curve_GF2m(const BIGNUM *p, const BIGNUM *a,
+                                  const BIGNUM *b, BN_CTX *ctx)
+{
+    const EC_METHOD *meth;
+    EC_GROUP *ret;
+
+    meth = EC_GF2m_simple_method();
+
+    ret = EC_GROUP_new(meth);
+    if (ret == NULL)
+        return NULL;
+
+    if (!EC_GROUP_set_curve_GF2m(ret, p, a, b, ctx)) {
+        EC_GROUP_clear_free(ret);
+        return NULL;
+    }
+
+    return ret;
+}
+#endif
diff --git a/crypto/ec/ec_err.c b/crypto/ec/ec_err.c
new file mode 100644
index 000000000..4f624e4f4
--- /dev/null
+++ b/crypto/ec/ec_err.c
@@ -0,0 +1,365 @@
+/*
+ * Generated by util/mkerr.pl DO NOT EDIT
+ * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include 
+
+/* BEGIN ERROR CODES */
+#ifndef OPENSSL_NO_ERR
+
+# define ERR_FUNC(func) ERR_PACK(ERR_LIB_EC,func,0)
+# define ERR_REASON(reason) ERR_PACK(ERR_LIB_EC,0,reason)
+
+static ERR_STRING_DATA EC_str_functs[] = {
+    {ERR_FUNC(EC_F_BN_TO_FELEM), "BN_to_felem"},
+    {ERR_FUNC(EC_F_D2I_ECIESPARAMETERS), "d2i_ECIESParameters"},
+    {ERR_FUNC(EC_F_D2I_ECPARAMETERS), "d2i_ECParameters"},
+    {ERR_FUNC(EC_F_D2I_ECPKPARAMETERS), "d2i_ECPKParameters"},
+    {ERR_FUNC(EC_F_D2I_ECPRIVATEKEY), "d2i_ECPrivateKey"},
+    {ERR_FUNC(EC_F_DO_EC_KEY_PRINT), "do_EC_KEY_print"},
+    {ERR_FUNC(EC_F_ECDH_CMS_DECRYPT), "ecdh_cms_decrypt"},
+    {ERR_FUNC(EC_F_ECDH_CMS_SET_SHARED_INFO), "ecdh_cms_set_shared_info"},
+    {ERR_FUNC(EC_F_ECDH_COMPUTE_KEY), "ECDH_compute_key"},
+    {ERR_FUNC(EC_F_ECDH_SIMPLE_COMPUTE_KEY), "ecdh_simple_compute_key"},
+    {ERR_FUNC(EC_F_ECDSA_DO_SIGN_EX), "ECDSA_do_sign_ex"},
+    {ERR_FUNC(EC_F_ECDSA_DO_VERIFY), "ECDSA_do_verify"},
+    {ERR_FUNC(EC_F_ECDSA_SIGN_EX), "ECDSA_sign_ex"},
+    {ERR_FUNC(EC_F_ECDSA_SIGN_SETUP), "ECDSA_sign_setup"},
+    {ERR_FUNC(EC_F_ECDSA_SIG_NEW), "ECDSA_SIG_new"},
+    {ERR_FUNC(EC_F_ECDSA_VERIFY), "ECDSA_verify"},
+    {ERR_FUNC(EC_F_ECIES_DECRYPT), "ECIES_decrypt"},
+    {ERR_FUNC(EC_F_ECIES_DO_DECRYPT), "ECIES_do_decrypt"},
+    {ERR_FUNC(EC_F_ECIES_DO_ENCRYPT), "ECIES_do_encrypt"},
+    {ERR_FUNC(EC_F_ECIES_ENCRYPT), "ECIES_encrypt"},
+    {ERR_FUNC(EC_F_ECIES_PARAMS_GET_ENC), "ECIES_PARAMS_get_enc"},
+    {ERR_FUNC(EC_F_ECIES_PARAMS_GET_KDF), "ECIES_PARAMS_get_kdf"},
+    {ERR_FUNC(EC_F_ECIES_PARAMS_GET_MAC), "ECIES_PARAMS_get_mac"},
+    {ERR_FUNC(EC_F_ECIES_PARAMS_INIT_WITH_RECOMMENDED),
+     "ECIES_PARAMS_init_with_recommended"},
+    {ERR_FUNC(EC_F_ECIES_PARAMS_INIT_WITH_TYPE),
+     "ECIES_PARAMS_init_with_type"},
+    {ERR_FUNC(EC_F_ECKEY_PARAM2TYPE), "eckey_param2type"},
+    {ERR_FUNC(EC_F_ECKEY_PARAM_DECODE), "eckey_param_decode"},
+    {ERR_FUNC(EC_F_ECKEY_PRIV_DECODE), "eckey_priv_decode"},
+    {ERR_FUNC(EC_F_ECKEY_PRIV_ENCODE), "eckey_priv_encode"},
+    {ERR_FUNC(EC_F_ECKEY_PUB_DECODE), "eckey_pub_decode"},
+    {ERR_FUNC(EC_F_ECKEY_PUB_ENCODE), "eckey_pub_encode"},
+    {ERR_FUNC(EC_F_ECKEY_TYPE2PARAM), "eckey_type2param"},
+    {ERR_FUNC(EC_F_ECPARAMETERS_PRINT_FP), "ECParameters_print_fp"},
+    {ERR_FUNC(EC_F_ECPKPARAMETERS_PRINT), "ECPKParameters_print"},
+    {ERR_FUNC(EC_F_ECPKPARAMETERS_PRINT_FP), "ECPKParameters_print_fp"},
+    {ERR_FUNC(EC_F_ECP_NISTZ256_GET_AFFINE), "ecp_nistz256_get_affine"},
+    {ERR_FUNC(EC_F_ECP_NISTZ256_MULT_PRECOMPUTE),
+     "ecp_nistz256_mult_precompute"},
+    {ERR_FUNC(EC_F_ECP_NISTZ256_POINTS_MUL), "ecp_nistz256_points_mul"},
+    {ERR_FUNC(EC_F_ECP_NISTZ256_PRE_COMP_NEW), "ecp_nistz256_pre_comp_new"},
+    {ERR_FUNC(EC_F_ECP_NISTZ256_WINDOWED_MUL), "ecp_nistz256_windowed_mul"},
+    {ERR_FUNC(EC_F_ECP_SM2Z256_GET_AFFINE), "ecp_sm2z256_get_affine"},
+    {ERR_FUNC(EC_F_ECP_SM2Z256_MULT_PRECOMPUTE),
+     "ecp_sm2z256_mult_precompute"},
+    {ERR_FUNC(EC_F_ECP_SM2Z256_POINTS_MUL), "ecp_sm2z256_points_mul"},
+    {ERR_FUNC(EC_F_ECP_SM2Z256_PRE_COMP_NEW), "ecp_sm2z256_pre_comp_new"},
+    {ERR_FUNC(EC_F_ECP_SM2Z256_WINDOWED_MUL), "ecp_sm2z256_windowed_mul"},
+    {ERR_FUNC(EC_F_ECX_KEY_OP), "ecx_key_op"},
+    {ERR_FUNC(EC_F_ECX_PRIV_ENCODE), "ecx_priv_encode"},
+    {ERR_FUNC(EC_F_ECX_PUB_ENCODE), "ecx_pub_encode"},
+    {ERR_FUNC(EC_F_EC_ASN1_GROUP2CURVE), "ec_asn1_group2curve"},
+    {ERR_FUNC(EC_F_EC_ASN1_GROUP2FIELDID), "ec_asn1_group2fieldid"},
+    {ERR_FUNC(EC_F_EC_GF2M_MONTGOMERY_POINT_MULTIPLY),
+     "ec_GF2m_montgomery_point_multiply"},
+    {ERR_FUNC(EC_F_EC_GF2M_SIMPLE_GROUP_CHECK_DISCRIMINANT),
+     "ec_GF2m_simple_group_check_discriminant"},
+    {ERR_FUNC(EC_F_EC_GF2M_SIMPLE_GROUP_SET_CURVE),
+     "ec_GF2m_simple_group_set_curve"},
+    {ERR_FUNC(EC_F_EC_GF2M_SIMPLE_OCT2POINT), "ec_GF2m_simple_oct2point"},
+    {ERR_FUNC(EC_F_EC_GF2M_SIMPLE_POINT2OCT), "ec_GF2m_simple_point2oct"},
+    {ERR_FUNC(EC_F_EC_GF2M_SIMPLE_POINT_GET_AFFINE_COORDINATES),
+     "ec_GF2m_simple_point_get_affine_coordinates"},
+    {ERR_FUNC(EC_F_EC_GF2M_SIMPLE_POINT_SET_AFFINE_COORDINATES),
+     "ec_GF2m_simple_point_set_affine_coordinates"},
+    {ERR_FUNC(EC_F_EC_GF2M_SIMPLE_SET_COMPRESSED_COORDINATES),
+     "ec_GF2m_simple_set_compressed_coordinates"},
+    {ERR_FUNC(EC_F_EC_GFP_MONT_FIELD_DECODE), "ec_GFp_mont_field_decode"},
+    {ERR_FUNC(EC_F_EC_GFP_MONT_FIELD_ENCODE), "ec_GFp_mont_field_encode"},
+    {ERR_FUNC(EC_F_EC_GFP_MONT_FIELD_MUL), "ec_GFp_mont_field_mul"},
+    {ERR_FUNC(EC_F_EC_GFP_MONT_FIELD_SET_TO_ONE),
+     "ec_GFp_mont_field_set_to_one"},
+    {ERR_FUNC(EC_F_EC_GFP_MONT_FIELD_SQR), "ec_GFp_mont_field_sqr"},
+    {ERR_FUNC(EC_F_EC_GFP_MONT_GROUP_SET_CURVE),
+     "ec_GFp_mont_group_set_curve"},
+    {ERR_FUNC(EC_F_EC_GFP_NISTP224_GROUP_SET_CURVE),
+     "ec_GFp_nistp224_group_set_curve"},
+    {ERR_FUNC(EC_F_EC_GFP_NISTP224_POINTS_MUL), "ec_GFp_nistp224_points_mul"},
+    {ERR_FUNC(EC_F_EC_GFP_NISTP224_POINT_GET_AFFINE_COORDINATES),
+     "ec_GFp_nistp224_point_get_affine_coordinates"},
+    {ERR_FUNC(EC_F_EC_GFP_NISTP256_GROUP_SET_CURVE),
+     "ec_GFp_nistp256_group_set_curve"},
+    {ERR_FUNC(EC_F_EC_GFP_NISTP256_POINTS_MUL), "ec_GFp_nistp256_points_mul"},
+    {ERR_FUNC(EC_F_EC_GFP_NISTP256_POINT_GET_AFFINE_COORDINATES),
+     "ec_GFp_nistp256_point_get_affine_coordinates"},
+    {ERR_FUNC(EC_F_EC_GFP_NISTP521_GROUP_SET_CURVE),
+     "ec_GFp_nistp521_group_set_curve"},
+    {ERR_FUNC(EC_F_EC_GFP_NISTP521_POINTS_MUL), "ec_GFp_nistp521_points_mul"},
+    {ERR_FUNC(EC_F_EC_GFP_NISTP521_POINT_GET_AFFINE_COORDINATES),
+     "ec_GFp_nistp521_point_get_affine_coordinates"},
+    {ERR_FUNC(EC_F_EC_GFP_NIST_FIELD_MUL), "ec_GFp_nist_field_mul"},
+    {ERR_FUNC(EC_F_EC_GFP_NIST_FIELD_SQR), "ec_GFp_nist_field_sqr"},
+    {ERR_FUNC(EC_F_EC_GFP_NIST_GROUP_SET_CURVE),
+     "ec_GFp_nist_group_set_curve"},
+    {ERR_FUNC(EC_F_EC_GFP_SIMPLE_GROUP_CHECK_DISCRIMINANT),
+     "ec_GFp_simple_group_check_discriminant"},
+    {ERR_FUNC(EC_F_EC_GFP_SIMPLE_GROUP_SET_CURVE),
+     "ec_GFp_simple_group_set_curve"},
+    {ERR_FUNC(EC_F_EC_GFP_SIMPLE_MAKE_AFFINE), "ec_GFp_simple_make_affine"},
+    {ERR_FUNC(EC_F_EC_GFP_SIMPLE_OCT2POINT), "ec_GFp_simple_oct2point"},
+    {ERR_FUNC(EC_F_EC_GFP_SIMPLE_POINT2OCT), "ec_GFp_simple_point2oct"},
+    {ERR_FUNC(EC_F_EC_GFP_SIMPLE_POINTS_MAKE_AFFINE),
+     "ec_GFp_simple_points_make_affine"},
+    {ERR_FUNC(EC_F_EC_GFP_SIMPLE_POINT_GET_AFFINE_COORDINATES),
+     "ec_GFp_simple_point_get_affine_coordinates"},
+    {ERR_FUNC(EC_F_EC_GFP_SIMPLE_POINT_SET_AFFINE_COORDINATES),
+     "ec_GFp_simple_point_set_affine_coordinates"},
+    {ERR_FUNC(EC_F_EC_GFP_SIMPLE_SET_COMPRESSED_COORDINATES),
+     "ec_GFp_simple_set_compressed_coordinates"},
+    {ERR_FUNC(EC_F_EC_GFP_SM2P256_GROUP_SET_CURVE),
+     "ec_GFp_sm2p256_group_set_curve"},
+    {ERR_FUNC(EC_F_EC_GFP_SM2P256_POINTS_MUL), "ec_GFp_sm2p256_points_mul"},
+    {ERR_FUNC(EC_F_EC_GFP_SM2P256_POINT_GET_AFFINE_COORDINATES),
+     "ec_GFp_sm2p256_point_get_affine_coordinates"},
+    {ERR_FUNC(EC_F_EC_GROUP_CHECK), "EC_GROUP_check"},
+    {ERR_FUNC(EC_F_EC_GROUP_CHECK_DISCRIMINANT),
+     "EC_GROUP_check_discriminant"},
+    {ERR_FUNC(EC_F_EC_GROUP_COPY), "EC_GROUP_copy"},
+    {ERR_FUNC(EC_F_EC_GROUP_GET_CURVE_GF2M), "EC_GROUP_get_curve_GF2m"},
+    {ERR_FUNC(EC_F_EC_GROUP_GET_CURVE_GFP), "EC_GROUP_get_curve_GFp"},
+    {ERR_FUNC(EC_F_EC_GROUP_GET_DEGREE), "EC_GROUP_get_degree"},
+    {ERR_FUNC(EC_F_EC_GROUP_GET_ECPARAMETERS), "EC_GROUP_get_ecparameters"},
+    {ERR_FUNC(EC_F_EC_GROUP_GET_ECPKPARAMETERS),
+     "EC_GROUP_get_ecpkparameters"},
+    {ERR_FUNC(EC_F_EC_GROUP_GET_PENTANOMIAL_BASIS),
+     "EC_GROUP_get_pentanomial_basis"},
+    {ERR_FUNC(EC_F_EC_GROUP_GET_TRINOMIAL_BASIS),
+     "EC_GROUP_get_trinomial_basis"},
+    {ERR_FUNC(EC_F_EC_GROUP_NEW), "EC_GROUP_new"},
+    {ERR_FUNC(EC_F_EC_GROUP_NEW_BY_CURVE_NAME), "EC_GROUP_new_by_curve_name"},
+    {ERR_FUNC(EC_F_EC_GROUP_NEW_FROM_DATA), "ec_group_new_from_data"},
+    {ERR_FUNC(EC_F_EC_GROUP_NEW_FROM_ECPARAMETERS),
+     "EC_GROUP_new_from_ecparameters"},
+    {ERR_FUNC(EC_F_EC_GROUP_NEW_FROM_ECPKPARAMETERS),
+     "EC_GROUP_new_from_ecpkparameters"},
+    {ERR_FUNC(EC_F_EC_GROUP_SET_CURVE_GF2M), "EC_GROUP_set_curve_GF2m"},
+    {ERR_FUNC(EC_F_EC_GROUP_SET_CURVE_GFP), "EC_GROUP_set_curve_GFp"},
+    {ERR_FUNC(EC_F_EC_GROUP_SET_GENERATOR), "EC_GROUP_set_generator"},
+    {ERR_FUNC(EC_F_EC_KEY_CHECK_KEY), "EC_KEY_check_key"},
+    {ERR_FUNC(EC_F_EC_KEY_COPY), "EC_KEY_copy"},
+    {ERR_FUNC(EC_F_EC_KEY_GENERATE_KEY), "EC_KEY_generate_key"},
+    {ERR_FUNC(EC_F_EC_KEY_MERGE), "EC_KEY_merge"},
+    {ERR_FUNC(EC_F_EC_KEY_NEW_METHOD), "EC_KEY_new_method"},
+    {ERR_FUNC(EC_F_EC_KEY_OCT2PRIV), "EC_KEY_oct2priv"},
+    {ERR_FUNC(EC_F_EC_KEY_PRINT_FP), "EC_KEY_print_fp"},
+    {ERR_FUNC(EC_F_EC_KEY_PRIV2OCT), "EC_KEY_priv2oct"},
+    {ERR_FUNC(EC_F_EC_KEY_SET_PUBLIC_KEY_AFFINE_COORDINATES),
+     "EC_KEY_set_public_key_affine_coordinates"},
+    {ERR_FUNC(EC_F_EC_KEY_SIMPLE_CHECK_KEY), "ec_key_simple_check_key"},
+    {ERR_FUNC(EC_F_EC_KEY_SIMPLE_OCT2PRIV), "ec_key_simple_oct2priv"},
+    {ERR_FUNC(EC_F_EC_KEY_SIMPLE_PRIV2OCT), "ec_key_simple_priv2oct"},
+    {ERR_FUNC(EC_F_EC_KEY_SPLIT), "EC_KEY_split"},
+    {ERR_FUNC(EC_F_EC_POINTS_MAKE_AFFINE), "EC_POINTs_make_affine"},
+    {ERR_FUNC(EC_F_EC_POINT_ADD), "EC_POINT_add"},
+    {ERR_FUNC(EC_F_EC_POINT_CMP), "EC_POINT_cmp"},
+    {ERR_FUNC(EC_F_EC_POINT_COPY), "EC_POINT_copy"},
+    {ERR_FUNC(EC_F_EC_POINT_DBL), "EC_POINT_dbl"},
+    {ERR_FUNC(EC_F_EC_POINT_GET_AFFINE_COORDINATES_GF2M),
+     "EC_POINT_get_affine_coordinates_GF2m"},
+    {ERR_FUNC(EC_F_EC_POINT_GET_AFFINE_COORDINATES_GFP),
+     "EC_POINT_get_affine_coordinates_GFp"},
+    {ERR_FUNC(EC_F_EC_POINT_GET_JPROJECTIVE_COORDINATES_GFP),
+     "EC_POINT_get_Jprojective_coordinates_GFp"},
+    {ERR_FUNC(EC_F_EC_POINT_INVERT), "EC_POINT_invert"},
+    {ERR_FUNC(EC_F_EC_POINT_IS_AT_INFINITY), "EC_POINT_is_at_infinity"},
+    {ERR_FUNC(EC_F_EC_POINT_IS_ON_CURVE), "EC_POINT_is_on_curve"},
+    {ERR_FUNC(EC_F_EC_POINT_MAKE_AFFINE), "EC_POINT_make_affine"},
+    {ERR_FUNC(EC_F_EC_POINT_NEW), "EC_POINT_new"},
+    {ERR_FUNC(EC_F_EC_POINT_OCT2POINT), "EC_POINT_oct2point"},
+    {ERR_FUNC(EC_F_EC_POINT_POINT2OCT), "EC_POINT_point2oct"},
+    {ERR_FUNC(EC_F_EC_POINT_SET_AFFINE_COORDINATES_GF2M),
+     "EC_POINT_set_affine_coordinates_GF2m"},
+    {ERR_FUNC(EC_F_EC_POINT_SET_AFFINE_COORDINATES_GFP),
+     "EC_POINT_set_affine_coordinates_GFp"},
+    {ERR_FUNC(EC_F_EC_POINT_SET_COMPRESSED_COORDINATES_GF2M),
+     "EC_POINT_set_compressed_coordinates_GF2m"},
+    {ERR_FUNC(EC_F_EC_POINT_SET_COMPRESSED_COORDINATES_GFP),
+     "EC_POINT_set_compressed_coordinates_GFp"},
+    {ERR_FUNC(EC_F_EC_POINT_SET_JPROJECTIVE_COORDINATES_GFP),
+     "EC_POINT_set_Jprojective_coordinates_GFp"},
+    {ERR_FUNC(EC_F_EC_POINT_SET_TO_INFINITY), "EC_POINT_set_to_infinity"},
+    {ERR_FUNC(EC_F_EC_PRE_COMP_NEW), "ec_pre_comp_new"},
+    {ERR_FUNC(EC_F_EC_WNAF_MUL), "ec_wNAF_mul"},
+    {ERR_FUNC(EC_F_EC_WNAF_PRECOMPUTE_MULT), "ec_wNAF_precompute_mult"},
+    {ERR_FUNC(EC_F_I2D_ECIESPARAMETERS), "i2d_ECIESParameters"},
+    {ERR_FUNC(EC_F_I2D_ECPARAMETERS), "i2d_ECParameters"},
+    {ERR_FUNC(EC_F_I2D_ECPKPARAMETERS), "i2d_ECPKParameters"},
+    {ERR_FUNC(EC_F_I2D_ECPRIVATEKEY), "i2d_ECPrivateKey"},
+    {ERR_FUNC(EC_F_I2O_ECPUBLICKEY), "i2o_ECPublicKey"},
+    {ERR_FUNC(EC_F_NISTP224_PRE_COMP_NEW), "nistp224_pre_comp_new"},
+    {ERR_FUNC(EC_F_NISTP256_PRE_COMP_NEW), "nistp256_pre_comp_new"},
+    {ERR_FUNC(EC_F_NISTP521_PRE_COMP_NEW), "nistp521_pre_comp_new"},
+    {ERR_FUNC(EC_F_O2I_ECPUBLICKEY), "o2i_ECPublicKey"},
+    {ERR_FUNC(EC_F_OLD_EC_PRIV_DECODE), "old_ec_priv_decode"},
+    {ERR_FUNC(EC_F_OSSL_ECDH_COMPUTE_KEY), "ossl_ecdh_compute_key"},
+    {ERR_FUNC(EC_F_OSSL_ECDSA_SIGN_SIG), "ossl_ecdsa_sign_sig"},
+    {ERR_FUNC(EC_F_OSSL_ECDSA_VERIFY_SIG), "ossl_ecdsa_verify_sig"},
+    {ERR_FUNC(EC_F_PKEY_ECX_DERIVE), "pkey_ecx_derive"},
+    {ERR_FUNC(EC_F_PKEY_EC_CTRL), "pkey_ec_ctrl"},
+    {ERR_FUNC(EC_F_PKEY_EC_CTRL_STR), "pkey_ec_ctrl_str"},
+    {ERR_FUNC(EC_F_PKEY_EC_DECRYPT), "pkey_ec_decrypt"},
+    {ERR_FUNC(EC_F_PKEY_EC_DERIVE), "pkey_ec_derive"},
+    {ERR_FUNC(EC_F_PKEY_EC_ENCRYPT), "pkey_ec_encrypt"},
+    {ERR_FUNC(EC_F_PKEY_EC_KEYGEN), "pkey_ec_keygen"},
+    {ERR_FUNC(EC_F_PKEY_EC_PARAMGEN), "pkey_ec_paramgen"},
+    {ERR_FUNC(EC_F_PKEY_EC_SIGN), "pkey_ec_sign"},
+    {ERR_FUNC(EC_F_SM2P256_PRE_COMP_NEW), "sm2p256_pre_comp_new"},
+    {ERR_FUNC(EC_F_SM2_COMPUTE_ID_DIGEST), "SM2_compute_id_digest"},
+    {ERR_FUNC(EC_F_SM2_COMPUTE_MESSAGE_DIGEST), "SM2_compute_message_digest"},
+    {ERR_FUNC(EC_F_SM2_COMPUTE_SHARE_KEY), "SM2_compute_share_key"},
+    {ERR_FUNC(EC_F_SM2_DO_ENCRYPT), "SM2_do_encrypt"},
+    {ERR_FUNC(EC_F_SM2_GET_PUBLIC_KEY_DATA), "SM2_get_public_key_data"},
+    {ERR_FUNC(EC_F_SM2_KAP_COMPUTE_KEY), "SM2_KAP_compute_key"},
+    {ERR_FUNC(EC_F_SM2_KAP_CTX_INIT), "SM2_KAP_CTX_init"},
+    {ERR_FUNC(EC_F_SM2_KAP_FINAL_CHECK), "SM2_KAP_final_check"},
+    {ERR_FUNC(EC_F_SM2_KAP_PREPARE), "SM2_KAP_prepare"},
+    {0, NULL}
+};
+
+static ERR_STRING_DATA EC_str_reasons[] = {
+    {ERR_REASON(EC_R_ASN1_ERROR), "asn1 error"},
+    {ERR_REASON(EC_R_BAD_SIGNATURE), "bad signature"},
+    {ERR_REASON(EC_R_BIGNUM_OUT_OF_RANGE), "bignum out of range"},
+    {ERR_REASON(EC_R_BUFFER_TOO_SMALL), "buffer too small"},
+    {ERR_REASON(EC_R_CMAC_FINAL_FAILURE), "cmac final failure"},
+    {ERR_REASON(EC_R_CMAC_INIT_FAILURE), "cmac init failure"},
+    {ERR_REASON(EC_R_CMAC_UPDATE_FAILURE), "cmac update failure"},
+    {ERR_REASON(EC_R_COORDINATES_OUT_OF_RANGE), "coordinates out of range"},
+    {ERR_REASON(EC_R_CURVE_DOES_NOT_SUPPORT_ECDH),
+     "curve does not support ecdh"},
+    {ERR_REASON(EC_R_CURVE_DOES_NOT_SUPPORT_SIGNING),
+     "curve does not support signing"},
+    {ERR_REASON(EC_R_D2I_ECPKPARAMETERS_FAILURE),
+     "d2i ecpkparameters failure"},
+    {ERR_REASON(EC_R_DECODE_ERROR), "decode error"},
+    {ERR_REASON(EC_R_DECRYPT_FAILED), "decrypt failed"},
+    {ERR_REASON(EC_R_DISCRIMINANT_IS_ZERO), "discriminant is zero"},
+    {ERR_REASON(EC_R_ECDH_FAILED), "ecdh failed"},
+    {ERR_REASON(EC_R_ECDH_FAILURE), "ecdh failure"},
+    {ERR_REASON(EC_R_ECIES_DECRYPT_FAILED), "ecies decrypt failed"},
+    {ERR_REASON(EC_R_ECIES_DECRYPT_INIT_FAILURE),
+     "ecies decrypt init failure"},
+    {ERR_REASON(EC_R_ECIES_ENCRYPT_FAILED), "ecies encrypt failed"},
+    {ERR_REASON(EC_R_ECIES_VERIFY_MAC_FAILURE), "ecies verify mac failure"},
+    {ERR_REASON(EC_R_EC_GROUP_NEW_BY_NAME_FAILURE),
+     "ec group new by name failure"},
+    {ERR_REASON(EC_R_ENCRYPT_FAILED), "encrypt failed"},
+    {ERR_REASON(EC_R_ENCRYPT_FAILURE), "encrypt failure"},
+    {ERR_REASON(EC_R_ERROR), "error"},
+    {ERR_REASON(EC_R_FIELD_TOO_LARGE), "field too large"},
+    {ERR_REASON(EC_R_GEN_MAC_FAILED), "gen mac failed"},
+    {ERR_REASON(EC_R_GET_PUBLIC_KEY_DATA_FAILURE),
+     "get public key data failure"},
+    {ERR_REASON(EC_R_GF2M_NOT_SUPPORTED), "gf2m not supported"},
+    {ERR_REASON(EC_R_GROUP2PKPARAMETERS_FAILURE),
+     "group2pkparameters failure"},
+    {ERR_REASON(EC_R_HMAC_FAILURE), "hmac failure"},
+    {ERR_REASON(EC_R_I2D_ECPKPARAMETERS_FAILURE),
+     "i2d ecpkparameters failure"},
+    {ERR_REASON(EC_R_INCOMPATIBLE_OBJECTS), "incompatible objects"},
+    {ERR_REASON(EC_R_INVALID_ARGUMENT), "invalid argument"},
+    {ERR_REASON(EC_R_INVALID_COMPRESSED_POINT), "invalid compressed point"},
+    {ERR_REASON(EC_R_INVALID_COMPRESSION_BIT), "invalid compression bit"},
+    {ERR_REASON(EC_R_INVALID_CURVE), "invalid curve"},
+    {ERR_REASON(EC_R_INVALID_DIGEST), "invalid digest"},
+    {ERR_REASON(EC_R_INVALID_DIGEST_ALGOR), "invalid digest algor"},
+    {ERR_REASON(EC_R_INVALID_DIGEST_TYPE), "invalid digest type"},
+    {ERR_REASON(EC_R_INVALID_ECIES_CIPHERTEXT), "invalid ecies ciphertext"},
+    {ERR_REASON(EC_R_INVALID_ECIES_PARAMETERS), "invalid ecies parameters"},
+    {ERR_REASON(EC_R_INVALID_ECIES_PARAMS), "invalid ecies params"},
+    {ERR_REASON(EC_R_INVALID_EC_ENCRYPT_PARAM), "invalid ec encrypt param"},
+    {ERR_REASON(EC_R_INVALID_EC_SCHEME), "invalid ec scheme"},
+    {ERR_REASON(EC_R_INVALID_ENCODING), "invalid encoding"},
+    {ERR_REASON(EC_R_INVALID_ENC_PARAM), "invalid enc param"},
+    {ERR_REASON(EC_R_INVALID_ENC_TYPE), "invalid enc type"},
+    {ERR_REASON(EC_R_INVALID_FIELD), "invalid field"},
+    {ERR_REASON(EC_R_INVALID_FORM), "invalid form"},
+    {ERR_REASON(EC_R_INVALID_GROUP_ORDER), "invalid group order"},
+    {ERR_REASON(EC_R_INVALID_ID_LENGTH), "invalid id length"},
+    {ERR_REASON(EC_R_INVALID_INPUT_LENGTH), "invalid input length"},
+    {ERR_REASON(EC_R_INVALID_KDF_MD), "invalid kdf md"},
+    {ERR_REASON(EC_R_INVALID_KEY), "invalid key"},
+    {ERR_REASON(EC_R_INVALID_MD), "invalid md"},
+    {ERR_REASON(EC_R_INVALID_OUTPUT_LENGTH), "invalid output length"},
+    {ERR_REASON(EC_R_INVALID_PEER_KEY), "invalid peer key"},
+    {ERR_REASON(EC_R_INVALID_PENTANOMIAL_BASIS), "invalid pentanomial basis"},
+    {ERR_REASON(EC_R_INVALID_PRIVATE_KEY), "invalid private key"},
+    {ERR_REASON(EC_R_INVALID_SIGNER_ID), "invalid signer id"},
+    {ERR_REASON(EC_R_INVALID_SM2_ID), "invalid sm2 id"},
+    {ERR_REASON(EC_R_INVALID_SM2_KAP_CHECKSUM_LENGTH),
+     "invalid sm2 kap checksum length"},
+    {ERR_REASON(EC_R_INVALID_SM2_KAP_CHECKSUM_VALUE),
+     "invalid sm2 kap checksum value"},
+    {ERR_REASON(EC_R_INVALID_SPLIT_PARAMETER), "invalid split parameter"},
+    {ERR_REASON(EC_R_INVALID_TRINOMIAL_BASIS), "invalid trinomial basis"},
+    {ERR_REASON(EC_R_KDF_PARAMETER_ERROR), "kdf parameter error"},
+    {ERR_REASON(EC_R_KEYS_NOT_SET), "keys not set"},
+    {ERR_REASON(EC_R_MISSING_PARAMETERS), "missing parameters"},
+    {ERR_REASON(EC_R_MISSING_PRIVATE_KEY), "missing private key"},
+    {ERR_REASON(EC_R_NEED_NEW_SETUP_VALUES), "need new setup values"},
+    {ERR_REASON(EC_R_NOT_A_NIST_PRIME), "not a nist prime"},
+    {ERR_REASON(EC_R_NOT_IMPLEMENTED), "not implemented"},
+    {ERR_REASON(EC_R_NOT_INITIALIZED), "not initialized"},
+    {ERR_REASON(EC_R_NO_PARAMETERS_SET), "no parameters set"},
+    {ERR_REASON(EC_R_NO_PRIVATE_VALUE), "no private value"},
+    {ERR_REASON(EC_R_OPERATION_NOT_SUPPORTED), "operation not supported"},
+    {ERR_REASON(EC_R_PASSED_NULL_PARAMETER), "passed null parameter"},
+    {ERR_REASON(EC_R_PEER_KEY_ERROR), "peer key error"},
+    {ERR_REASON(EC_R_PKPARAMETERS2GROUP_FAILURE),
+     "pkparameters2group failure"},
+    {ERR_REASON(EC_R_POINT_ARITHMETIC_FAILURE), "point arithmetic failure"},
+    {ERR_REASON(EC_R_POINT_AT_INFINITY), "point at infinity"},
+    {ERR_REASON(EC_R_POINT_IS_NOT_ON_CURVE), "point is not on curve"},
+    {ERR_REASON(EC_R_RANDOM_NUMBER_GENERATION_FAILED),
+     "random number generation failed"},
+    {ERR_REASON(EC_R_SHARED_INFO_ERROR), "shared info error"},
+    {ERR_REASON(EC_R_SLOT_FULL), "slot full"},
+    {ERR_REASON(EC_R_SM2_DECRYPT_FAILED), "sm2 decrypt failed"},
+    {ERR_REASON(EC_R_SM2_ENCRYPT_FAILED), "sm2 encrypt failed"},
+    {ERR_REASON(EC_R_SM2_KAP_NOT_INITED), "sm2 kap not inited"},
+    {ERR_REASON(EC_R_UNDEFINED_GENERATOR), "undefined generator"},
+    {ERR_REASON(EC_R_UNDEFINED_ORDER), "undefined order"},
+    {ERR_REASON(EC_R_UNKNOWN_GROUP), "unknown group"},
+    {ERR_REASON(EC_R_UNKNOWN_ORDER), "unknown order"},
+    {ERR_REASON(EC_R_UNSUPPORTED_FIELD), "unsupported field"},
+    {ERR_REASON(EC_R_WRONG_CURVE_PARAMETERS), "wrong curve parameters"},
+    {ERR_REASON(EC_R_WRONG_ORDER), "wrong order"},
+    {0, NULL}
+};
+
+#endif
+
+int ERR_load_EC_strings(void)
+{
+#ifndef OPENSSL_NO_ERR
+
+    if (ERR_func_error_string(EC_str_functs[0].error) == NULL) {
+        ERR_load_strings(0, EC_str_functs);
+        ERR_load_strings(0, EC_str_reasons);
+    }
+#endif
+    return 1;
+}
diff --git a/crypto/ec/ec_key.c b/crypto/ec/ec_key.c
new file mode 100644
index 000000000..9ce454b12
--- /dev/null
+++ b/crypto/ec/ec_key.c
@@ -0,0 +1,745 @@
+/*
+ * Copyright 2002-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/* ====================================================================
+ * Copyright 2002 Sun Microsystems, Inc. ALL RIGHTS RESERVED.
+ * Portions originally developed by SUN MICROSYSTEMS, INC., and
+ * contributed to the OpenSSL project.
+ */
+
+#include 
+#include 
+#include "ec_lcl.h"
+#include 
+#ifndef OPENSSL_NO_ENGINE
+# include 
+#endif
+
+EC_KEY *EC_KEY_new(void)
+{
+    return EC_KEY_new_method(NULL);
+}
+
+EC_KEY *EC_KEY_new_by_curve_name(int nid)
+{
+    EC_KEY *ret = EC_KEY_new();
+    if (ret == NULL)
+        return NULL;
+    ret->group = EC_GROUP_new_by_curve_name(nid);
+    if (ret->group == NULL) {
+        EC_KEY_free(ret);
+        return NULL;
+    }
+    if (ret->meth->set_group != NULL
+        && ret->meth->set_group(ret, ret->group) == 0) {
+        EC_KEY_free(ret);
+        return NULL;
+    }
+    return ret;
+}
+
+void EC_KEY_free(EC_KEY *r)
+{
+    int i;
+
+    if (r == NULL)
+        return;
+
+    CRYPTO_atomic_add(&r->references, -1, &i, r->lock);
+    REF_PRINT_COUNT("EC_KEY", r);
+    if (i > 0)
+        return;
+    REF_ASSERT_ISNT(i < 0);
+
+    if (r->meth->finish != NULL)
+        r->meth->finish(r);
+
+#ifndef OPENSSL_NO_ENGINE
+    ENGINE_finish(r->engine);
+#endif
+
+    if (r->group && r->group->meth->keyfinish)
+        r->group->meth->keyfinish(r);
+
+    CRYPTO_free_ex_data(CRYPTO_EX_INDEX_EC_KEY, r, &r->ex_data);
+    CRYPTO_THREAD_lock_free(r->lock);
+    EC_GROUP_free(r->group);
+    EC_POINT_free(r->pub_key);
+    BN_clear_free(r->priv_key);
+
+    OPENSSL_clear_free((void *)r, sizeof(EC_KEY));
+}
+
+EC_KEY *EC_KEY_copy(EC_KEY *dest, const EC_KEY *src)
+{
+    if (dest == NULL || src == NULL) {
+        ECerr(EC_F_EC_KEY_COPY, ERR_R_PASSED_NULL_PARAMETER);
+        return NULL;
+    }
+    if (src->meth != dest->meth) {
+        if (dest->meth->finish != NULL)
+            dest->meth->finish(dest);
+        if (dest->group && dest->group->meth->keyfinish)
+            dest->group->meth->keyfinish(dest);
+#ifndef OPENSSL_NO_ENGINE
+        if (ENGINE_finish(dest->engine) == 0)
+            return 0;
+        dest->engine = NULL;
+#endif
+    }
+    /* copy the parameters */
+    if (src->group != NULL) {
+        const EC_METHOD *meth = EC_GROUP_method_of(src->group);
+        /* clear the old group */
+        EC_GROUP_free(dest->group);
+        dest->group = EC_GROUP_new(meth);
+        if (dest->group == NULL)
+            return NULL;
+        if (!EC_GROUP_copy(dest->group, src->group))
+            return NULL;
+
+        /*  copy the public key */
+        if (src->pub_key != NULL) {
+            EC_POINT_free(dest->pub_key);
+            dest->pub_key = EC_POINT_new(src->group);
+            if (dest->pub_key == NULL)
+                return NULL;
+            if (!EC_POINT_copy(dest->pub_key, src->pub_key))
+                return NULL;
+        }
+        /* copy the private key */
+        if (src->priv_key != NULL) {
+            if (dest->priv_key == NULL) {
+                dest->priv_key = BN_new();
+                if (dest->priv_key == NULL)
+                    return NULL;
+            }
+            if (!BN_copy(dest->priv_key, src->priv_key))
+                return NULL;
+            if (src->group->meth->keycopy
+                && src->group->meth->keycopy(dest, src) == 0)
+                return NULL;
+        }
+    }
+
+
+    /* copy the rest */
+    dest->enc_flag = src->enc_flag;
+    dest->conv_form = src->conv_form;
+    dest->version = src->version;
+    dest->flags = src->flags;
+    if (!CRYPTO_dup_ex_data(CRYPTO_EX_INDEX_EC_KEY,
+                            &dest->ex_data, &src->ex_data))
+        return NULL;
+
+    if (src->meth != dest->meth) {
+#ifndef OPENSSL_NO_ENGINE
+        if (src->engine != NULL && ENGINE_init(src->engine) == 0)
+            return NULL;
+        dest->engine = src->engine;
+#endif
+        dest->meth = src->meth;
+    }
+
+    if (src->meth->copy != NULL && src->meth->copy(dest, src) == 0)
+        return NULL;
+
+    return dest;
+}
+
+EC_KEY *EC_KEY_dup(const EC_KEY *ec_key)
+{
+    EC_KEY *ret = EC_KEY_new_method(ec_key->engine);
+
+    if (ret == NULL)
+        return NULL;
+
+    if (EC_KEY_copy(ret, ec_key) == NULL) {
+        EC_KEY_free(ret);
+        return NULL;
+    }
+    return ret;
+}
+
+int EC_KEY_up_ref(EC_KEY *r)
+{
+    int i;
+
+    if (CRYPTO_atomic_add(&r->references, 1, &i, r->lock) <= 0)
+        return 0;
+
+    REF_PRINT_COUNT("EC_KEY", r);
+    REF_ASSERT_ISNT(i < 2);
+    return ((i > 1) ? 1 : 0);
+}
+
+int EC_KEY_generate_key(EC_KEY *eckey)
+{
+    if (eckey == NULL || eckey->group == NULL) {
+        ECerr(EC_F_EC_KEY_GENERATE_KEY, ERR_R_PASSED_NULL_PARAMETER);
+        return 0;
+    }
+    if (eckey->meth->keygen != NULL)
+        return eckey->meth->keygen(eckey);
+    ECerr(EC_F_EC_KEY_GENERATE_KEY, EC_R_OPERATION_NOT_SUPPORTED);
+    return 0;
+}
+
+int ossl_ec_key_gen(EC_KEY *eckey)
+{
+    OPENSSL_assert(eckey->group->meth->keygen != NULL);
+    return eckey->group->meth->keygen(eckey);
+}
+
+int ec_key_simple_generate_key(EC_KEY *eckey)
+{
+    int ok = 0;
+    BN_CTX *ctx = NULL;
+    BIGNUM *priv_key = NULL;
+    const BIGNUM *order = NULL;
+    EC_POINT *pub_key = NULL;
+
+    if ((ctx = BN_CTX_new()) == NULL)
+        goto err;
+
+    if (eckey->priv_key == NULL) {
+        priv_key = BN_new();
+        if (priv_key == NULL)
+            goto err;
+    } else
+        priv_key = eckey->priv_key;
+
+    order = EC_GROUP_get0_order(eckey->group);
+    if (order == NULL)
+        goto err;
+
+    do
+        if (!BN_rand_range(priv_key, order))
+            goto err;
+    while (BN_is_zero(priv_key)) ;
+
+    if (eckey->pub_key == NULL) {
+        pub_key = EC_POINT_new(eckey->group);
+        if (pub_key == NULL)
+            goto err;
+    } else
+        pub_key = eckey->pub_key;
+
+    if (!EC_POINT_mul(eckey->group, pub_key, priv_key, NULL, NULL, ctx))
+        goto err;
+
+    eckey->priv_key = priv_key;
+    eckey->pub_key = pub_key;
+
+    ok = 1;
+
+ err:
+    if (eckey->pub_key == NULL)
+        EC_POINT_free(pub_key);
+    if (eckey->priv_key != priv_key)
+        BN_free(priv_key);
+    BN_CTX_free(ctx);
+    return ok;
+}
+
+int ec_key_simple_generate_public_key(EC_KEY *eckey)
+{
+    if (eckey->pub_key == NULL)
+        eckey->pub_key = EC_POINT_new(eckey->group);
+    return EC_POINT_mul(eckey->group, eckey->pub_key, eckey->priv_key, NULL,
+                        NULL, NULL);
+}
+
+int EC_KEY_check_key(const EC_KEY *eckey)
+{
+    if (eckey == NULL || eckey->group == NULL || eckey->pub_key == NULL) {
+        ECerr(EC_F_EC_KEY_CHECK_KEY, ERR_R_PASSED_NULL_PARAMETER);
+        return 0;
+    }
+
+    if (eckey->group->meth->keycheck == NULL) {
+        ECerr(EC_F_EC_KEY_CHECK_KEY, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
+        return 0;
+    }
+
+    return eckey->group->meth->keycheck(eckey);
+}
+
+int ec_key_simple_check_key(const EC_KEY *eckey)
+{
+    int ok = 0;
+    BN_CTX *ctx = NULL;
+    const BIGNUM *order = NULL;
+    EC_POINT *point = NULL;
+
+    if (eckey == NULL || eckey->group == NULL || eckey->pub_key == NULL) {
+        ECerr(EC_F_EC_KEY_SIMPLE_CHECK_KEY, ERR_R_PASSED_NULL_PARAMETER);
+        return 0;
+    }
+
+    if (EC_POINT_is_at_infinity(eckey->group, eckey->pub_key)) {
+        ECerr(EC_F_EC_KEY_SIMPLE_CHECK_KEY, EC_R_POINT_AT_INFINITY);
+        goto err;
+    }
+
+    if ((ctx = BN_CTX_new()) == NULL)
+        goto err;
+    if ((point = EC_POINT_new(eckey->group)) == NULL)
+        goto err;
+
+    /* testing whether the pub_key is on the elliptic curve */
+    if (EC_POINT_is_on_curve(eckey->group, eckey->pub_key, ctx) <= 0) {
+        ECerr(EC_F_EC_KEY_SIMPLE_CHECK_KEY, EC_R_POINT_IS_NOT_ON_CURVE);
+        goto err;
+    }
+    /* testing whether pub_key * order is the point at infinity */
+    order = eckey->group->order;
+    if (BN_is_zero(order)) {
+        ECerr(EC_F_EC_KEY_SIMPLE_CHECK_KEY, EC_R_INVALID_GROUP_ORDER);
+        goto err;
+    }
+    if (!EC_POINT_mul(eckey->group, point, NULL, eckey->pub_key, order, ctx)) {
+        ECerr(EC_F_EC_KEY_SIMPLE_CHECK_KEY, ERR_R_EC_LIB);
+        goto err;
+    }
+    if (!EC_POINT_is_at_infinity(eckey->group, point)) {
+        ECerr(EC_F_EC_KEY_SIMPLE_CHECK_KEY, EC_R_WRONG_ORDER);
+        goto err;
+    }
+    /*
+     * in case the priv_key is present : check if generator * priv_key ==
+     * pub_key
+     */
+    if (eckey->priv_key != NULL) {
+        if (BN_cmp(eckey->priv_key, order) >= 0) {
+            ECerr(EC_F_EC_KEY_SIMPLE_CHECK_KEY, EC_R_WRONG_ORDER);
+            goto err;
+        }
+        if (!EC_POINT_mul(eckey->group, point, eckey->priv_key,
+                          NULL, NULL, ctx)) {
+            ECerr(EC_F_EC_KEY_SIMPLE_CHECK_KEY, ERR_R_EC_LIB);
+            goto err;
+        }
+        if (EC_POINT_cmp(eckey->group, point, eckey->pub_key, ctx) != 0) {
+            ECerr(EC_F_EC_KEY_SIMPLE_CHECK_KEY, EC_R_INVALID_PRIVATE_KEY);
+            goto err;
+        }
+    }
+    ok = 1;
+ err:
+    BN_CTX_free(ctx);
+    EC_POINT_free(point);
+    return ok;
+}
+
+int EC_KEY_set_public_key_affine_coordinates(EC_KEY *key, BIGNUM *x,
+                                             BIGNUM *y)
+{
+    BN_CTX *ctx = NULL;
+    BIGNUM *tx, *ty;
+    EC_POINT *point = NULL;
+    int ok = 0;
+#ifndef OPENSSL_NO_EC2M
+    int tmp_nid, is_char_two = 0;
+#endif
+
+    if (key == NULL || key->group == NULL || x == NULL || y == NULL) {
+        ECerr(EC_F_EC_KEY_SET_PUBLIC_KEY_AFFINE_COORDINATES,
+              ERR_R_PASSED_NULL_PARAMETER);
+        return 0;
+    }
+    ctx = BN_CTX_new();
+    if (ctx == NULL)
+        return 0;
+
+    BN_CTX_start(ctx);
+    point = EC_POINT_new(key->group);
+
+    if (point == NULL)
+        goto err;
+
+    tx = BN_CTX_get(ctx);
+    ty = BN_CTX_get(ctx);
+    if (ty == NULL)
+        goto err;
+
+#ifndef OPENSSL_NO_EC2M
+    tmp_nid = EC_METHOD_get_field_type(EC_GROUP_method_of(key->group));
+
+    if (tmp_nid == NID_X9_62_characteristic_two_field)
+        is_char_two = 1;
+
+    if (is_char_two) {
+        if (!EC_POINT_set_affine_coordinates_GF2m(key->group, point,
+                                                  x, y, ctx))
+            goto err;
+        if (!EC_POINT_get_affine_coordinates_GF2m(key->group, point,
+                                                  tx, ty, ctx))
+            goto err;
+    } else
+#endif
+    {
+        if (!EC_POINT_set_affine_coordinates_GFp(key->group, point,
+                                                 x, y, ctx))
+            goto err;
+        if (!EC_POINT_get_affine_coordinates_GFp(key->group, point,
+                                                 tx, ty, ctx))
+            goto err;
+    }
+    /*
+     * Check if retrieved coordinates match originals and are less than field
+     * order: if not values are out of range.
+     */
+    if (BN_cmp(x, tx) || BN_cmp(y, ty)
+        || (BN_cmp(x, key->group->field) >= 0)
+        || (BN_cmp(y, key->group->field) >= 0)) {
+        ECerr(EC_F_EC_KEY_SET_PUBLIC_KEY_AFFINE_COORDINATES,
+              EC_R_COORDINATES_OUT_OF_RANGE);
+        goto err;
+    }
+
+    if (!EC_KEY_set_public_key(key, point))
+        goto err;
+
+    if (EC_KEY_check_key(key) == 0)
+        goto err;
+
+    ok = 1;
+
+ err:
+    BN_CTX_end(ctx);
+    BN_CTX_free(ctx);
+    EC_POINT_free(point);
+    return ok;
+
+}
+
+const EC_GROUP *EC_KEY_get0_group(const EC_KEY *key)
+{
+    return key->group;
+}
+
+int EC_KEY_set_group(EC_KEY *key, const EC_GROUP *group)
+{
+    if (key->meth->set_group != NULL && key->meth->set_group(key, group) == 0)
+        return 0;
+    EC_GROUP_free(key->group);
+    key->group = EC_GROUP_dup(group);
+    return (key->group == NULL) ? 0 : 1;
+}
+
+const BIGNUM *EC_KEY_get0_private_key(const EC_KEY *key)
+{
+    return key->priv_key;
+}
+
+int EC_KEY_set_private_key(EC_KEY *key, const BIGNUM *priv_key)
+{
+    if (key->group == NULL || key->group->meth == NULL)
+        return 0;
+    if (key->group->meth->set_private != NULL
+        && key->group->meth->set_private(key, priv_key) == 0)
+        return 0;
+    if (key->meth->set_private != NULL
+        && key->meth->set_private(key, priv_key) == 0)
+        return 0;
+    BN_clear_free(key->priv_key);
+    key->priv_key = BN_dup(priv_key);
+    return (key->priv_key == NULL) ? 0 : 1;
+}
+
+const EC_POINT *EC_KEY_get0_public_key(const EC_KEY *key)
+{
+    return key->pub_key;
+}
+
+int EC_KEY_set_public_key(EC_KEY *key, const EC_POINT *pub_key)
+{
+    if (key->meth->set_public != NULL
+        && key->meth->set_public(key, pub_key) == 0)
+        return 0;
+    EC_POINT_free(key->pub_key);
+    key->pub_key = EC_POINT_dup(pub_key, key->group);
+    return (key->pub_key == NULL) ? 0 : 1;
+}
+
+unsigned int EC_KEY_get_enc_flags(const EC_KEY *key)
+{
+    return key->enc_flag;
+}
+
+void EC_KEY_set_enc_flags(EC_KEY *key, unsigned int flags)
+{
+    key->enc_flag = flags;
+}
+
+point_conversion_form_t EC_KEY_get_conv_form(const EC_KEY *key)
+{
+    return key->conv_form;
+}
+
+void EC_KEY_set_conv_form(EC_KEY *key, point_conversion_form_t cform)
+{
+    key->conv_form = cform;
+    if (key->group != NULL)
+        EC_GROUP_set_point_conversion_form(key->group, cform);
+}
+
+void EC_KEY_set_asn1_flag(EC_KEY *key, int flag)
+{
+    if (key->group != NULL)
+        EC_GROUP_set_asn1_flag(key->group, flag);
+}
+
+int EC_KEY_precompute_mult(EC_KEY *key, BN_CTX *ctx)
+{
+    if (key->group == NULL)
+        return 0;
+    return EC_GROUP_precompute_mult(key->group, ctx);
+}
+
+int EC_KEY_get_flags(const EC_KEY *key)
+{
+    return key->flags;
+}
+
+void EC_KEY_set_flags(EC_KEY *key, int flags)
+{
+    key->flags |= flags;
+}
+
+void EC_KEY_clear_flags(EC_KEY *key, int flags)
+{
+    key->flags &= ~flags;
+}
+
+size_t EC_KEY_key2buf(const EC_KEY *key, point_conversion_form_t form,
+                        unsigned char **pbuf, BN_CTX *ctx)
+{
+    if (key == NULL || key->pub_key == NULL || key->group == NULL)
+        return 0;
+    return EC_POINT_point2buf(key->group, key->pub_key, form, pbuf, ctx);
+}
+
+int EC_KEY_oct2key(EC_KEY *key, const unsigned char *buf, size_t len,
+                   BN_CTX *ctx)
+{
+    if (key == NULL || key->group == NULL)
+        return 0;
+    if (key->pub_key == NULL)
+        key->pub_key = EC_POINT_new(key->group);
+    if (key->pub_key == NULL)
+        return 0;
+    if (EC_POINT_oct2point(key->group, key->pub_key, buf, len, ctx) == 0)
+        return 0;
+    /*
+     * Save the point conversion form.
+     * For non-custom curves the first octet of the buffer (excluding
+     * the last significant bit) contains the point conversion form.
+     * EC_POINT_oct2point() has already performed sanity checking of
+     * the buffer so we know it is valid.
+     */
+    if ((key->group->meth->flags & EC_FLAGS_CUSTOM_CURVE) == 0)
+        key->conv_form = (point_conversion_form_t)(buf[0] & ~0x01);
+    return 1;
+}
+
+size_t EC_KEY_priv2oct(const EC_KEY *eckey,
+                       unsigned char *buf, size_t len)
+{
+    if (eckey->group == NULL || eckey->group->meth == NULL)
+        return 0;
+    if (eckey->group->meth->priv2oct == NULL) {
+        ECerr(EC_F_EC_KEY_PRIV2OCT, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
+        return 0;
+    }
+
+    return eckey->group->meth->priv2oct(eckey, buf, len);
+}
+
+size_t ec_key_simple_priv2oct(const EC_KEY *eckey,
+                              unsigned char *buf, size_t len)
+{
+    size_t buf_len;
+
+    buf_len = (EC_GROUP_order_bits(eckey->group) + 7) / 8;
+    if (eckey->priv_key == NULL)
+        return 0;
+    if (buf == NULL)
+        return buf_len;
+    else if (len < buf_len)
+        return 0;
+
+    /* Octetstring may need leading zeros if BN is to short */
+
+    if (BN_bn2binpad(eckey->priv_key, buf, buf_len) == -1) {
+        ECerr(EC_F_EC_KEY_SIMPLE_PRIV2OCT, EC_R_BUFFER_TOO_SMALL);
+        return 0;
+    }
+
+    return buf_len;
+}
+
+int EC_KEY_oct2priv(EC_KEY *eckey, const unsigned char *buf, size_t len)
+{
+    if (eckey->group == NULL || eckey->group->meth == NULL)
+        return 0;
+    if (eckey->group->meth->oct2priv == NULL) {
+        ECerr(EC_F_EC_KEY_OCT2PRIV, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
+        return 0;
+    }
+    return eckey->group->meth->oct2priv(eckey, buf, len);
+}
+
+int ec_key_simple_oct2priv(EC_KEY *eckey, const unsigned char *buf, size_t len)
+{
+    if (eckey->priv_key == NULL)
+        eckey->priv_key = BN_secure_new();
+    if (eckey->priv_key == NULL) {
+        ECerr(EC_F_EC_KEY_SIMPLE_OCT2PRIV, ERR_R_MALLOC_FAILURE);
+        return 0;
+    }
+    eckey->priv_key = BN_bin2bn(buf, len, eckey->priv_key);
+    if (eckey->priv_key == NULL) {
+        ECerr(EC_F_EC_KEY_SIMPLE_OCT2PRIV, ERR_R_BN_LIB);
+        return 0;
+    }
+    return 1;
+}
+
+size_t EC_KEY_priv2buf(const EC_KEY *eckey, unsigned char **pbuf)
+{
+    size_t len;
+    unsigned char *buf;
+    len = EC_KEY_priv2oct(eckey, NULL, 0);
+    if (len == 0)
+        return 0;
+    buf = OPENSSL_malloc(len);
+    if (buf == NULL)
+        return 0;
+    len = EC_KEY_priv2oct(eckey, buf, len);
+    if (len == 0) {
+        OPENSSL_free(buf);
+        return 0;
+    }
+    *pbuf = buf;
+    return len;
+}
+
+int EC_KEY_can_sign(const EC_KEY *eckey)
+{
+    if (eckey->group == NULL || eckey->group->meth == NULL
+        || (eckey->group->meth->flags & EC_FLAGS_NO_SIGN))
+        return 0;
+    return 1;
+}
+
+#define EC_KEY_MIN_SHARES	2
+#define EC_KEY_MAX_SHARES	5
+
+STACK_OF(EC_KEY) *EC_KEY_split(EC_KEY *ec_key, int k, int n)
+{
+	STACK_OF(EC_KEY) *ret = NULL;
+	STACK_OF(EC_KEY) *keys = NULL;
+	STACK_OF(BIGNUM) *as = NULL;
+	const BIGNUM *order;
+	const BIGNUM *d;
+	EC_KEY *key = NULL; /* for stack */
+	BIGNUM *a = NULL; /* for stack */
+	BIGNUM *fx = NULL;
+	BIGNUM *ax = NULL;
+	BN_CTX *bn_ctx = NULL;
+	int i, x, xpow;
+
+	if (!(d = EC_KEY_get0_private_key(ec_key))
+		|| !(order = EC_GROUP_get0_order(EC_KEY_get0_group(ec_key)))) {
+		ECerr(EC_F_EC_KEY_SPLIT, EC_R_INVALID_PRIVATE_KEY);
+		return NULL;
+	}
+	if (k < EC_KEY_MIN_SHARES || k > n || n > EC_KEY_MAX_SHARES) {
+		ECerr(EC_F_EC_KEY_SPLIT, EC_R_INVALID_SPLIT_PARAMETER);
+		return NULL;
+	}
+
+	if (!(keys = sk_EC_KEY_new_null())
+		|| !(as = sk_BIGNUM_new_null())
+		|| !(fx = BN_new())
+		|| !(ax = BN_new())
+		|| !(bn_ctx = BN_CTX_new())) {
+		ECerr(EC_F_EC_KEY_SPLIT, ERR_R_MALLOC_FAILURE);
+		goto end;
+	}
+
+	/* a_i = rand(1, order), i = 0 .. k-1 */
+	for (i = 0; i < k; i++) {
+		if (!(a = BN_new())) {
+			ECerr(EC_F_EC_KEY_SPLIT, ERR_R_MALLOC_FAILURE);
+			goto end;
+		}
+
+		do {
+			if (!BN_rand_range(a, order)) {
+				ECerr(EC_F_EC_KEY_SPLIT, ERR_R_BN_LIB);
+				goto end;
+			}
+		} while (BN_is_zero(a));
+
+		if (!sk_BIGNUM_push(as, a)) {
+			ECerr(EC_F_EC_KEY_SPLIT, ERR_R_BN_LIB);
+			goto end;
+		}
+		a = NULL;
+	}
+
+	/* f(x) = d + a_0 * x^1 + ... + a_{k-1} * x^k, x = 1 .. n */
+	for (x = 1; x <= n; x++) {
+		if (!(key = EC_KEY_dup(ec_key))
+			|| !BN_copy(fx, d)) {
+			ECerr(EC_F_EC_KEY_SPLIT, ERR_R_MALLOC_FAILURE);
+			goto end;
+		}
+
+		xpow = x;
+		for (i = 0; i < k; i++) {
+			if (!BN_copy(ax, sk_BIGNUM_value(as, i))
+				|| !BN_mul_word(ax, xpow)
+				|| !BN_mod_add(fx, fx, ax, order, bn_ctx)) {
+				ECerr(EC_F_EC_KEY_SPLIT, ERR_R_BN_LIB);
+				goto end;
+			}
+			xpow *= x;
+		}
+
+		if (!EC_KEY_set_private_key(key, fx)
+			|| !sk_EC_KEY_push(keys, key)) {
+			ECerr(EC_F_EC_KEY_SPLIT, ERR_R_EC_LIB);
+			goto end;
+		}
+		key = NULL;
+	}
+
+	ret = keys;
+	keys = NULL;
+
+end:
+	sk_EC_KEY_free(keys);
+	sk_BIGNUM_free(as);
+	EC_KEY_free(key);
+	BN_free(a);
+	BN_free(fx);
+	BN_free(ax);
+	BN_CTX_free(bn_ctx);
+	return ret;
+}
+
+EC_KEY *EC_KEY_merge(STACK_OF(EC_KEY) *ec_keys)
+{
+	ECerr(EC_F_EC_KEY_MERGE, EC_R_NOT_IMPLEMENTED);
+	return NULL;
+}
diff --git a/crypto/ec/ec_kmeth.c b/crypto/ec/ec_kmeth.c
new file mode 100644
index 000000000..a7933cd5b
--- /dev/null
+++ b/crypto/ec/ec_kmeth.c
@@ -0,0 +1,348 @@
+/*
+ * Copyright 2015-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#ifndef OPENSSL_NO_ENGINE
+# include 
+#endif
+#include 
+#include "ec_lcl.h"
+#ifndef OPENSSL_NO_SM2
+# include "../ecies/ecies_lcl.h"
+#endif
+
+static const EC_KEY_METHOD openssl_ec_key_method = {
+    "OpenSSL EC_KEY method",
+    0, /* flags */
+    0, /* init */
+    0, /* finish */
+    0, /* copy */
+    0, /* set_group */
+    0, /* set_private */
+    0, /* set_public */
+    ossl_ec_key_gen,
+    ossl_ecdh_compute_key,
+    ossl_ecdsa_sign,
+    ossl_ecdsa_sign_setup,
+    ossl_ecdsa_sign_sig,
+    ossl_ecdsa_verify,
+    ossl_ecdsa_verify_sig,
+#ifndef OPENSSL_NO_SM2
+    ECIES_encrypt,
+    NULL,
+    ECIES_decrypt,
+    NULL,
+#endif
+};
+
+static const EC_KEY_METHOD *default_ec_key_meth = &openssl_ec_key_method;
+
+const EC_KEY_METHOD *EC_KEY_OpenSSL(void)
+{
+    return &openssl_ec_key_method;
+}
+
+const EC_KEY_METHOD *EC_KEY_get_default_method(void)
+{
+    return default_ec_key_meth;
+}
+
+void EC_KEY_set_default_method(const EC_KEY_METHOD *meth)
+{
+    if (meth == NULL)
+        default_ec_key_meth = &openssl_ec_key_method;
+    else
+        default_ec_key_meth = meth;
+}
+
+#ifndef OPENSSL_NO_SM2
+void EC_KEY_set_default_secg_method(void)
+{
+    default_ec_key_meth = EC_KEY_OpenSSL();
+}
+
+/* defained in sm2/sm2_kmeth.c */
+extern const EC_KEY_METHOD *EC_KEY_GmSSL(void);
+
+void EC_KEY_set_default_sm_method(void)
+{
+    default_ec_key_meth = EC_KEY_GmSSL();
+}
+#endif
+
+const EC_KEY_METHOD *EC_KEY_get_method(const EC_KEY *key)
+{
+    return key->meth;
+}
+
+int EC_KEY_set_method(EC_KEY *key, const EC_KEY_METHOD *meth)
+{
+    void (*finish)(EC_KEY *key) = key->meth->finish;
+
+    if (finish != NULL)
+        finish(key);
+
+#ifndef OPENSSL_NO_ENGINE
+    ENGINE_finish(key->engine);
+    key->engine = NULL;
+#endif
+
+    key->meth = meth;
+    if (meth->init != NULL)
+        return meth->init(key);
+    return 1;
+}
+
+EC_KEY *EC_KEY_new_method(ENGINE *engine)
+{
+    EC_KEY *ret = OPENSSL_zalloc(sizeof(*ret));
+
+    if (ret == NULL) {
+        ECerr(EC_F_EC_KEY_NEW_METHOD, ERR_R_MALLOC_FAILURE);
+        return NULL;
+    }
+
+    ret->references = 1;
+    ret->lock = CRYPTO_THREAD_lock_new();
+    if (ret->lock == NULL) {
+        ECerr(EC_F_EC_KEY_NEW_METHOD, ERR_R_MALLOC_FAILURE);
+        OPENSSL_free(ret);
+        return NULL;
+    }
+
+    ret->meth = EC_KEY_get_default_method();
+#ifndef OPENSSL_NO_ENGINE
+    if (engine != NULL) {
+        if (!ENGINE_init(engine)) {
+            ECerr(EC_F_EC_KEY_NEW_METHOD, ERR_R_ENGINE_LIB);
+            goto err;
+        }
+        ret->engine = engine;
+    } else
+        ret->engine = ENGINE_get_default_EC();
+    if (ret->engine != NULL) {
+        ret->meth = ENGINE_get_EC(ret->engine);
+        if (ret->meth == NULL) {
+            ECerr(EC_F_EC_KEY_NEW_METHOD, ERR_R_ENGINE_LIB);
+            goto err;
+        }
+    }
+#endif
+
+    ret->version = 1;
+    ret->conv_form = POINT_CONVERSION_UNCOMPRESSED;
+
+    if (!CRYPTO_new_ex_data(CRYPTO_EX_INDEX_EC_KEY, ret, &ret->ex_data)) {
+        goto err;
+    }
+
+    if (ret->meth->init != NULL && ret->meth->init(ret) == 0) {
+        ECerr(EC_F_EC_KEY_NEW_METHOD, ERR_R_INIT_FAIL);
+        goto err;
+    }
+    return ret;
+
+err:
+    EC_KEY_free(ret);
+    return NULL;
+}
+
+int ECDH_compute_key(void *out, size_t outlen, const EC_POINT *pub_key,
+                     const EC_KEY *eckey,
+                     void *(*KDF) (const void *in, size_t inlen, void *out,
+                                   size_t *outlen))
+{
+    unsigned char *sec = NULL;
+    size_t seclen;
+    if (eckey->meth->compute_key == NULL) {
+        ECerr(EC_F_ECDH_COMPUTE_KEY, EC_R_OPERATION_NOT_SUPPORTED);
+        return 0;
+    }
+    if (outlen > INT_MAX) {
+        ECerr(EC_F_ECDH_COMPUTE_KEY, EC_R_INVALID_OUTPUT_LENGTH);
+        return 0;
+    }
+    if (!eckey->meth->compute_key(&sec, &seclen, pub_key, eckey))
+        return 0;
+    if (KDF != NULL) {
+        KDF(sec, seclen, out, &outlen);
+    } else {
+        if (outlen > seclen)
+            outlen = seclen;
+        memcpy(out, sec, outlen);
+    }
+    OPENSSL_clear_free(sec, seclen);
+    return outlen;
+}
+
+EC_KEY_METHOD *EC_KEY_METHOD_new(const EC_KEY_METHOD *meth)
+{
+    EC_KEY_METHOD *ret = OPENSSL_zalloc(sizeof(*meth));
+
+    if (ret == NULL)
+        return NULL;
+    if (meth != NULL)
+        *ret = *meth;
+    ret->flags |= EC_KEY_METHOD_DYNAMIC;
+    return ret;
+}
+
+void EC_KEY_METHOD_free(EC_KEY_METHOD *meth)
+{
+    if (meth->flags & EC_KEY_METHOD_DYNAMIC)
+        OPENSSL_free(meth);
+}
+
+void EC_KEY_METHOD_set_init(EC_KEY_METHOD *meth,
+                            int (*init)(EC_KEY *key),
+                            void (*finish)(EC_KEY *key),
+                            int (*copy)(EC_KEY *dest, const EC_KEY *src),
+                            int (*set_group)(EC_KEY *key, const EC_GROUP *grp),
+                            int (*set_private)(EC_KEY *key,
+                                               const BIGNUM *priv_key),
+                            int (*set_public)(EC_KEY *key,
+                                              const EC_POINT *pub_key))
+{
+    meth->init = init;
+    meth->finish = finish;
+    meth->copy = copy;
+    meth->set_group = set_group;
+    meth->set_private = set_private;
+    meth->set_public = set_public;
+}
+
+void EC_KEY_METHOD_set_keygen(EC_KEY_METHOD *meth,
+                              int (*keygen)(EC_KEY *key))
+{
+    meth->keygen = keygen;
+}
+
+void EC_KEY_METHOD_set_compute_key(EC_KEY_METHOD *meth,
+                                   int (*ckey)(unsigned char **psec,
+                                               size_t *pseclen,
+                                               const EC_POINT *pub_key,
+                                               const EC_KEY *ecdh))
+{
+    meth->compute_key = ckey;
+}
+
+void EC_KEY_METHOD_set_sign(EC_KEY_METHOD *meth,
+                            int (*sign)(int type, const unsigned char *dgst,
+                                        int dlen, unsigned char *sig,
+                                        unsigned int *siglen,
+                                        const BIGNUM *kinv, const BIGNUM *r,
+                                        EC_KEY *eckey),
+                            int (*sign_setup)(EC_KEY *eckey, BN_CTX *ctx_in,
+                                              BIGNUM **kinvp, BIGNUM **rp),
+                            ECDSA_SIG *(*sign_sig)(const unsigned char *dgst,
+                                                   int dgst_len,
+                                                   const BIGNUM *in_kinv,
+                                                   const BIGNUM *in_r,
+                                                   EC_KEY *eckey))
+{
+    meth->sign = sign;
+    meth->sign_setup = sign_setup;
+    meth->sign_sig = sign_sig;
+}
+
+void EC_KEY_METHOD_set_verify(EC_KEY_METHOD *meth,
+                              int (*verify)(int type, const unsigned
+                                            char *dgst, int dgst_len,
+                                            const unsigned char *sigbuf,
+                                            int sig_len, EC_KEY *eckey),
+                              int (*verify_sig)(const unsigned char *dgst,
+                                                int dgst_len,
+                                                const ECDSA_SIG *sig,
+                                                EC_KEY *eckey))
+{
+    meth->verify = verify;
+    meth->verify_sig = verify_sig;
+}
+
+void EC_KEY_METHOD_get_init(EC_KEY_METHOD *meth,
+                            int (**pinit)(EC_KEY *key),
+                            void (**pfinish)(EC_KEY *key),
+                            int (**pcopy)(EC_KEY *dest, const EC_KEY *src),
+                            int (**pset_group)(EC_KEY *key,
+                                               const EC_GROUP *grp),
+                            int (**pset_private)(EC_KEY *key,
+                                                 const BIGNUM *priv_key),
+                            int (**pset_public)(EC_KEY *key,
+                                                const EC_POINT *pub_key))
+{
+    if (pinit != NULL)
+        *pinit = meth->init;
+    if (pfinish != NULL)
+        *pfinish = meth->finish;
+    if (pcopy != NULL)
+        *pcopy = meth->copy;
+    if (pset_group != NULL)
+        *pset_group = meth->set_group;
+    if (pset_private != NULL)
+        *pset_private = meth->set_private;
+    if (pset_public != NULL)
+        *pset_public = meth->set_public;
+}
+
+void EC_KEY_METHOD_get_keygen(EC_KEY_METHOD *meth,
+                              int (**pkeygen)(EC_KEY *key))
+{
+    if (pkeygen != NULL)
+        *pkeygen = meth->keygen;
+}
+
+void EC_KEY_METHOD_get_compute_key(EC_KEY_METHOD *meth,
+                                   int (**pck)(unsigned char **pout,
+                                               size_t *poutlen,
+                                               const EC_POINT *pub_key,
+                                               const EC_KEY *ecdh))
+{
+    if (pck != NULL)
+        *pck = meth->compute_key;
+}
+
+void EC_KEY_METHOD_get_sign(EC_KEY_METHOD *meth,
+                            int (**psign)(int type, const unsigned char *dgst,
+                                          int dlen, unsigned char *sig,
+                                          unsigned int *siglen,
+                                          const BIGNUM *kinv, const BIGNUM *r,
+                                          EC_KEY *eckey),
+                            int (**psign_setup)(EC_KEY *eckey, BN_CTX *ctx_in,
+                                                BIGNUM **kinvp, BIGNUM **rp),
+                            ECDSA_SIG *(**psign_sig)(const unsigned char *dgst,
+                                                     int dgst_len,
+                                                     const BIGNUM *in_kinv,
+                                                     const BIGNUM *in_r,
+                                                     EC_KEY *eckey))
+{
+    if (psign != NULL)
+        *psign = meth->sign;
+    if (psign_setup != NULL)
+        *psign_setup = meth->sign_setup;
+    if (psign_sig != NULL)
+        *psign_sig = meth->sign_sig;
+}
+
+void EC_KEY_METHOD_get_verify(EC_KEY_METHOD *meth,
+                              int (**pverify)(int type, const unsigned
+                                              char *dgst, int dgst_len,
+                                              const unsigned char *sigbuf,
+                                              int sig_len, EC_KEY *eckey),
+                              int (**pverify_sig)(const unsigned char *dgst,
+                                                  int dgst_len,
+                                                  const ECDSA_SIG *sig,
+                                                  EC_KEY *eckey))
+{
+    if (pverify != NULL)
+        *pverify = meth->verify;
+    if (pverify_sig != NULL)
+        *pverify_sig = meth->verify_sig;
+}
+
diff --git a/crypto/ec/ec_lcl.h b/crypto/ec/ec_lcl.h
new file mode 100644
index 000000000..1adc07dc3
--- /dev/null
+++ b/crypto/ec/ec_lcl.h
@@ -0,0 +1,661 @@
+/*
+ * Copyright 2001-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/* ====================================================================
+ * Copyright 2002 Sun Microsystems, Inc. ALL RIGHTS RESERVED.
+ *
+ * Portions of the attached software ("Contribution") are developed by
+ * SUN MICROSYSTEMS, INC., and are contributed to the OpenSSL project.
+ *
+ * The Contribution is licensed pursuant to the OpenSSL open source
+ * license provided above.
+ *
+ * The elliptic curve binary polynomial software is originally written by
+ * Sheueling Chang Shantz and Douglas Stebila of Sun Microsystems Laboratories.
+ *
+ */
+
+#include 
+
+#include 
+#include 
+#include 
+#ifndef OPENSSL_NO_SM2
+#include 
+#endif
+
+#include "../../e_os.h"
+
+#if defined(__SUNPRO_C)
+# if __SUNPRO_C >= 0x520
+#  pragma error_messages (off,E_ARRAY_OF_INCOMPLETE_NONAME,E_ARRAY_OF_INCOMPLETE)
+# endif
+#endif
+
+/* Use default functions for poin2oct, oct2point and compressed coordinates */
+#define EC_FLAGS_DEFAULT_OCT    0x1
+
+/* Use custom formats for EC_GROUP, EC_POINT and EC_KEY */
+#define EC_FLAGS_CUSTOM_CURVE   0x2
+
+/* Curve does not support signing operations */
+#define EC_FLAGS_NO_SIGN        0x4
+
+/*
+ * Structure details are not part of the exported interface, so all this may
+ * change in future versions.
+ */
+
+struct ec_method_st {
+    /* Various method flags */
+    int flags;
+    /* used by EC_METHOD_get_field_type: */
+    int field_type;             /* a NID */
+    /*
+     * used by EC_GROUP_new, EC_GROUP_free, EC_GROUP_clear_free,
+     * EC_GROUP_copy:
+     */
+    int (*group_init) (EC_GROUP *);
+    void (*group_finish) (EC_GROUP *);
+    void (*group_clear_finish) (EC_GROUP *);
+    int (*group_copy) (EC_GROUP *, const EC_GROUP *);
+    /* used by EC_GROUP_set_curve_GFp, EC_GROUP_get_curve_GFp, */
+    /* EC_GROUP_set_curve_GF2m, and EC_GROUP_get_curve_GF2m: */
+    int (*group_set_curve) (EC_GROUP *, const BIGNUM *p, const BIGNUM *a,
+                            const BIGNUM *b, BN_CTX *);
+    int (*group_get_curve) (const EC_GROUP *, BIGNUM *p, BIGNUM *a, BIGNUM *b,
+                            BN_CTX *);
+    /* used by EC_GROUP_get_degree: */
+    int (*group_get_degree) (const EC_GROUP *);
+    int (*group_order_bits) (const EC_GROUP *);
+    /* used by EC_GROUP_check: */
+    int (*group_check_discriminant) (const EC_GROUP *, BN_CTX *);
+    /*
+     * used by EC_POINT_new, EC_POINT_free, EC_POINT_clear_free,
+     * EC_POINT_copy:
+     */
+    int (*point_init) (EC_POINT *);
+    void (*point_finish) (EC_POINT *);
+    void (*point_clear_finish) (EC_POINT *);
+    int (*point_copy) (EC_POINT *, const EC_POINT *);
+    /*-
+     * used by EC_POINT_set_to_infinity,
+     * EC_POINT_set_Jprojective_coordinates_GFp,
+     * EC_POINT_get_Jprojective_coordinates_GFp,
+     * EC_POINT_set_affine_coordinates_GFp,     ..._GF2m,
+     * EC_POINT_get_affine_coordinates_GFp,     ..._GF2m,
+     * EC_POINT_set_compressed_coordinates_GFp, ..._GF2m:
+     */
+    int (*point_set_to_infinity) (const EC_GROUP *, EC_POINT *);
+    int (*point_set_Jprojective_coordinates_GFp) (const EC_GROUP *,
+                                                  EC_POINT *, const BIGNUM *x,
+                                                  const BIGNUM *y,
+                                                  const BIGNUM *z, BN_CTX *);
+    int (*point_get_Jprojective_coordinates_GFp) (const EC_GROUP *,
+                                                  const EC_POINT *, BIGNUM *x,
+                                                  BIGNUM *y, BIGNUM *z,
+                                                  BN_CTX *);
+    int (*point_set_affine_coordinates) (const EC_GROUP *, EC_POINT *,
+                                         const BIGNUM *x, const BIGNUM *y,
+                                         BN_CTX *);
+    int (*point_get_affine_coordinates) (const EC_GROUP *, const EC_POINT *,
+                                         BIGNUM *x, BIGNUM *y, BN_CTX *);
+    int (*point_set_compressed_coordinates) (const EC_GROUP *, EC_POINT *,
+                                             const BIGNUM *x, int y_bit,
+                                             BN_CTX *);
+    /* used by EC_POINT_point2oct, EC_POINT_oct2point: */
+    size_t (*point2oct) (const EC_GROUP *, const EC_POINT *,
+                         point_conversion_form_t form, unsigned char *buf,
+                         size_t len, BN_CTX *);
+    int (*oct2point) (const EC_GROUP *, EC_POINT *, const unsigned char *buf,
+                      size_t len, BN_CTX *);
+    /* used by EC_POINT_add, EC_POINT_dbl, ECP_POINT_invert: */
+    int (*add) (const EC_GROUP *, EC_POINT *r, const EC_POINT *a,
+                const EC_POINT *b, BN_CTX *);
+    int (*dbl) (const EC_GROUP *, EC_POINT *r, const EC_POINT *a, BN_CTX *);
+    int (*invert) (const EC_GROUP *, EC_POINT *, BN_CTX *);
+    /*
+     * used by EC_POINT_is_at_infinity, EC_POINT_is_on_curve, EC_POINT_cmp:
+     */
+    int (*is_at_infinity) (const EC_GROUP *, const EC_POINT *);
+    int (*is_on_curve) (const EC_GROUP *, const EC_POINT *, BN_CTX *);
+    int (*point_cmp) (const EC_GROUP *, const EC_POINT *a, const EC_POINT *b,
+                      BN_CTX *);
+    /* used by EC_POINT_make_affine, EC_POINTs_make_affine: */
+    int (*make_affine) (const EC_GROUP *, EC_POINT *, BN_CTX *);
+    int (*points_make_affine) (const EC_GROUP *, size_t num, EC_POINT *[],
+                               BN_CTX *);
+    /*
+     * used by EC_POINTs_mul, EC_POINT_mul, EC_POINT_precompute_mult,
+     * EC_POINT_have_precompute_mult (default implementations are used if the
+     * 'mul' pointer is 0):
+     */
+    int (*mul) (const EC_GROUP *group, EC_POINT *r, const BIGNUM *scalar,
+                size_t num, const EC_POINT *points[], const BIGNUM *scalars[],
+                BN_CTX *);
+    int (*precompute_mult) (EC_GROUP *group, BN_CTX *);
+    int (*have_precompute_mult) (const EC_GROUP *group);
+    /* internal functions */
+    /*
+     * 'field_mul', 'field_sqr', and 'field_div' can be used by 'add' and
+     * 'dbl' so that the same implementations of point operations can be used
+     * with different optimized implementations of expensive field
+     * operations:
+     */
+    int (*field_mul) (const EC_GROUP *, BIGNUM *r, const BIGNUM *a,
+                      const BIGNUM *b, BN_CTX *);
+    int (*field_sqr) (const EC_GROUP *, BIGNUM *r, const BIGNUM *a, BN_CTX *);
+    int (*field_div) (const EC_GROUP *, BIGNUM *r, const BIGNUM *a,
+                      const BIGNUM *b, BN_CTX *);
+    /* e.g. to Montgomery */
+    int (*field_encode) (const EC_GROUP *, BIGNUM *r, const BIGNUM *a,
+                         BN_CTX *);
+    /* e.g. from Montgomery */
+    int (*field_decode) (const EC_GROUP *, BIGNUM *r, const BIGNUM *a,
+                         BN_CTX *);
+    int (*field_set_to_one) (const EC_GROUP *, BIGNUM *r, BN_CTX *);
+    /* private key operations */
+    size_t (*priv2oct)(const EC_KEY *eckey, unsigned char *buf, size_t len);
+    int (*oct2priv)(EC_KEY *eckey, const unsigned char *buf, size_t len);
+    int (*set_private)(EC_KEY *eckey, const BIGNUM *priv_key);
+    int (*keygen)(EC_KEY *eckey);
+    int (*keycheck)(const EC_KEY *eckey);
+    int (*keygenpub)(EC_KEY *eckey);
+    int (*keycopy)(EC_KEY *dst, const EC_KEY *src);
+    void (*keyfinish)(EC_KEY *eckey);
+    /* custom ECDH operation */
+    int (*ecdh_compute_key)(unsigned char **pout, size_t *poutlen,
+                            const EC_POINT *pub_key, const EC_KEY *ecdh);
+};
+
+/*
+ * Types and functions to manipulate pre-computed values.
+ */
+typedef struct nistp224_pre_comp_st NISTP224_PRE_COMP;
+typedef struct nistp256_pre_comp_st NISTP256_PRE_COMP;
+typedef struct nistp521_pre_comp_st NISTP521_PRE_COMP;
+typedef struct nistz256_pre_comp_st NISTZ256_PRE_COMP;
+typedef struct sm2p256_pre_comp_st SM2P256_PRE_COMP;
+typedef struct sm2z256_pre_comp_st SM2Z256_PRE_COMP;
+typedef struct ec_pre_comp_st EC_PRE_COMP;
+
+struct ec_group_st {
+    const EC_METHOD *meth;
+    EC_POINT *generator;        /* optional */
+    BIGNUM *order, *cofactor;
+    int curve_name;             /* optional NID for named curve */
+    int asn1_flag;              /* flag to control the asn1 encoding */
+    point_conversion_form_t asn1_form;
+    unsigned char *seed;        /* optional seed for parameters (appears in
+                                 * ASN1) */
+    size_t seed_len;
+    /*
+     * The following members are handled by the method functions, even if
+     * they appear generic
+     */
+    /*
+     * Field specification. For curves over GF(p), this is the modulus; for
+     * curves over GF(2^m), this is the irreducible polynomial defining the
+     * field.
+     */
+    BIGNUM *field;
+    /*
+     * Field specification for curves over GF(2^m). The irreducible f(t) is
+     * then of the form: t^poly[0] + t^poly[1] + ... + t^poly[k] where m =
+     * poly[0] > poly[1] > ... > poly[k] = 0. The array is terminated with
+     * poly[k+1]=-1. All elliptic curve irreducibles have at most 5 non-zero
+     * terms.
+     */
+    int poly[6];
+    /*
+     * Curve coefficients. (Here the assumption is that BIGNUMs can be used
+     * or abused for all kinds of fields, not just GF(p).) For characteristic
+     * > 3, the curve is defined by a Weierstrass equation of the form y^2 =
+     * x^3 + a*x + b. For characteristic 2, the curve is defined by an
+     * equation of the form y^2 + x*y = x^3 + a*x^2 + b.
+     */
+    BIGNUM *a, *b;
+    /* enable optimized point arithmetics for special case */
+    int a_is_minus3;
+    /* method-specific (e.g., Montgomery structure) */
+    void *field_data1;
+    /* method-specific */
+    void *field_data2;
+    /* method-specific */
+    int (*field_mod_func) (BIGNUM *, const BIGNUM *, const BIGNUM *,
+                           BN_CTX *);
+    /* data for ECDSA inverse */
+    BN_MONT_CTX *mont_data;
+
+    /*
+     * Precomputed values for speed. The PCT_xxx names match the
+     * pre_comp.xxx union names; see the SETPRECOMP and HAVEPRECOMP
+     * macros, below.
+     */
+    enum {
+        PCT_none,
+        PCT_nistp224, PCT_nistp256, PCT_nistp521, PCT_nistz256,
+        PCT_sm2p256,
+	PCT_sm2z256,
+        PCT_ec
+    } pre_comp_type;
+    union {
+        NISTP224_PRE_COMP *nistp224;
+        NISTP256_PRE_COMP *nistp256;
+        NISTP521_PRE_COMP *nistp521;
+        NISTZ256_PRE_COMP *nistz256;
+        SM2P256_PRE_COMP *sm2p256;
+        SM2Z256_PRE_COMP *sm2z256;
+        EC_PRE_COMP *ec;
+    } pre_comp;
+};
+
+#define SETPRECOMP(g, type, pre) \
+    g->pre_comp_type = PCT_##type, g->pre_comp.type = pre
+#define HAVEPRECOMP(g, type) \
+    g->pre_comp_type == PCT_##type && g->pre_comp.type != NULL
+
+struct ec_key_st {
+    const EC_KEY_METHOD *meth;
+    ENGINE *engine;
+    int version;
+    EC_GROUP *group;
+    EC_POINT *pub_key;
+    BIGNUM *priv_key;
+    unsigned int enc_flag;
+    point_conversion_form_t conv_form;
+    int references;
+    int flags;
+    CRYPTO_EX_DATA ex_data;
+    CRYPTO_RWLOCK *lock;
+};
+
+struct ec_point_st {
+    const EC_METHOD *meth;
+    /*
+     * All members except 'meth' are handled by the method functions, even if
+     * they appear generic
+     */
+    BIGNUM *X;
+    BIGNUM *Y;
+    BIGNUM *Z;                  /* Jacobian projective coordinates: * (X, Y,
+                                 * Z) represents (X/Z^2, Y/Z^3) if Z != 0 */
+    int Z_is_one;               /* enable optimized point arithmetics for
+                                 * special case */
+};
+
+NISTP224_PRE_COMP *EC_nistp224_pre_comp_dup(NISTP224_PRE_COMP *);
+NISTP256_PRE_COMP *EC_nistp256_pre_comp_dup(NISTP256_PRE_COMP *);
+NISTP521_PRE_COMP *EC_nistp521_pre_comp_dup(NISTP521_PRE_COMP *);
+NISTZ256_PRE_COMP *EC_nistz256_pre_comp_dup(NISTZ256_PRE_COMP *);
+SM2P256_PRE_COMP *EC_sm2p256_pre_comp_dup(SM2P256_PRE_COMP *);
+SM2Z256_PRE_COMP *EC_sm2z256_pre_comp_dup(SM2Z256_PRE_COMP *);
+EC_PRE_COMP *EC_ec_pre_comp_dup(EC_PRE_COMP *);
+
+void EC_pre_comp_free(EC_GROUP *group);
+void EC_nistp224_pre_comp_free(NISTP224_PRE_COMP *);
+void EC_nistp256_pre_comp_free(NISTP256_PRE_COMP *);
+void EC_nistp521_pre_comp_free(NISTP521_PRE_COMP *);
+void EC_nistz256_pre_comp_free(NISTZ256_PRE_COMP *);
+void EC_sm2p256_pre_comp_free(SM2P256_PRE_COMP *);
+void EC_sm2z256_pre_comp_free(SM2Z256_PRE_COMP *);
+void EC_ec_pre_comp_free(EC_PRE_COMP *);
+
+/*
+ * method functions in ec_mult.c (ec_lib.c uses these as defaults if
+ * group->method->mul is 0)
+ */
+int ec_wNAF_mul(const EC_GROUP *group, EC_POINT *r, const BIGNUM *scalar,
+                size_t num, const EC_POINT *points[], const BIGNUM *scalars[],
+                BN_CTX *);
+int ec_wNAF_precompute_mult(EC_GROUP *group, BN_CTX *);
+int ec_wNAF_have_precompute_mult(const EC_GROUP *group);
+
+/* method functions in ecp_smpl.c */
+int ec_GFp_simple_group_init(EC_GROUP *);
+void ec_GFp_simple_group_finish(EC_GROUP *);
+void ec_GFp_simple_group_clear_finish(EC_GROUP *);
+int ec_GFp_simple_group_copy(EC_GROUP *, const EC_GROUP *);
+int ec_GFp_simple_group_set_curve(EC_GROUP *, const BIGNUM *p,
+                                  const BIGNUM *a, const BIGNUM *b, BN_CTX *);
+int ec_GFp_simple_group_get_curve(const EC_GROUP *, BIGNUM *p, BIGNUM *a,
+                                  BIGNUM *b, BN_CTX *);
+int ec_GFp_simple_group_get_degree(const EC_GROUP *);
+int ec_GFp_simple_group_check_discriminant(const EC_GROUP *, BN_CTX *);
+int ec_GFp_simple_point_init(EC_POINT *);
+void ec_GFp_simple_point_finish(EC_POINT *);
+void ec_GFp_simple_point_clear_finish(EC_POINT *);
+int ec_GFp_simple_point_copy(EC_POINT *, const EC_POINT *);
+int ec_GFp_simple_point_set_to_infinity(const EC_GROUP *, EC_POINT *);
+int ec_GFp_simple_set_Jprojective_coordinates_GFp(const EC_GROUP *,
+                                                  EC_POINT *, const BIGNUM *x,
+                                                  const BIGNUM *y,
+                                                  const BIGNUM *z, BN_CTX *);
+int ec_GFp_simple_get_Jprojective_coordinates_GFp(const EC_GROUP *,
+                                                  const EC_POINT *, BIGNUM *x,
+                                                  BIGNUM *y, BIGNUM *z,
+                                                  BN_CTX *);
+int ec_GFp_simple_point_set_affine_coordinates(const EC_GROUP *, EC_POINT *,
+                                               const BIGNUM *x,
+                                               const BIGNUM *y, BN_CTX *);
+int ec_GFp_simple_point_get_affine_coordinates(const EC_GROUP *,
+                                               const EC_POINT *, BIGNUM *x,
+                                               BIGNUM *y, BN_CTX *);
+int ec_GFp_simple_set_compressed_coordinates(const EC_GROUP *, EC_POINT *,
+                                             const BIGNUM *x, int y_bit,
+                                             BN_CTX *);
+size_t ec_GFp_simple_point2oct(const EC_GROUP *, const EC_POINT *,
+                               point_conversion_form_t form,
+                               unsigned char *buf, size_t len, BN_CTX *);
+int ec_GFp_simple_oct2point(const EC_GROUP *, EC_POINT *,
+                            const unsigned char *buf, size_t len, BN_CTX *);
+int ec_GFp_simple_add(const EC_GROUP *, EC_POINT *r, const EC_POINT *a,
+                      const EC_POINT *b, BN_CTX *);
+int ec_GFp_simple_dbl(const EC_GROUP *, EC_POINT *r, const EC_POINT *a,
+                      BN_CTX *);
+int ec_GFp_simple_invert(const EC_GROUP *, EC_POINT *, BN_CTX *);
+int ec_GFp_simple_is_at_infinity(const EC_GROUP *, const EC_POINT *);
+int ec_GFp_simple_is_on_curve(const EC_GROUP *, const EC_POINT *, BN_CTX *);
+int ec_GFp_simple_cmp(const EC_GROUP *, const EC_POINT *a, const EC_POINT *b,
+                      BN_CTX *);
+int ec_GFp_simple_make_affine(const EC_GROUP *, EC_POINT *, BN_CTX *);
+int ec_GFp_simple_points_make_affine(const EC_GROUP *, size_t num,
+                                     EC_POINT *[], BN_CTX *);
+int ec_GFp_simple_field_mul(const EC_GROUP *, BIGNUM *r, const BIGNUM *a,
+                            const BIGNUM *b, BN_CTX *);
+int ec_GFp_simple_field_sqr(const EC_GROUP *, BIGNUM *r, const BIGNUM *a,
+                            BN_CTX *);
+
+/* method functions in ecp_mont.c */
+int ec_GFp_mont_group_init(EC_GROUP *);
+int ec_GFp_mont_group_set_curve(EC_GROUP *, const BIGNUM *p, const BIGNUM *a,
+                                const BIGNUM *b, BN_CTX *);
+void ec_GFp_mont_group_finish(EC_GROUP *);
+void ec_GFp_mont_group_clear_finish(EC_GROUP *);
+int ec_GFp_mont_group_copy(EC_GROUP *, const EC_GROUP *);
+int ec_GFp_mont_field_mul(const EC_GROUP *, BIGNUM *r, const BIGNUM *a,
+                          const BIGNUM *b, BN_CTX *);
+int ec_GFp_mont_field_sqr(const EC_GROUP *, BIGNUM *r, const BIGNUM *a,
+                          BN_CTX *);
+int ec_GFp_mont_field_encode(const EC_GROUP *, BIGNUM *r, const BIGNUM *a,
+                             BN_CTX *);
+int ec_GFp_mont_field_decode(const EC_GROUP *, BIGNUM *r, const BIGNUM *a,
+                             BN_CTX *);
+int ec_GFp_mont_field_set_to_one(const EC_GROUP *, BIGNUM *r, BN_CTX *);
+
+/* method functions in ecp_nist.c */
+int ec_GFp_nist_group_copy(EC_GROUP *dest, const EC_GROUP *src);
+int ec_GFp_nist_group_set_curve(EC_GROUP *, const BIGNUM *p, const BIGNUM *a,
+                                const BIGNUM *b, BN_CTX *);
+int ec_GFp_nist_field_mul(const EC_GROUP *, BIGNUM *r, const BIGNUM *a,
+                          const BIGNUM *b, BN_CTX *);
+int ec_GFp_nist_field_sqr(const EC_GROUP *, BIGNUM *r, const BIGNUM *a,
+                          BN_CTX *);
+
+/* method functions in ec2_smpl.c */
+int ec_GF2m_simple_group_init(EC_GROUP *);
+void ec_GF2m_simple_group_finish(EC_GROUP *);
+void ec_GF2m_simple_group_clear_finish(EC_GROUP *);
+int ec_GF2m_simple_group_copy(EC_GROUP *, const EC_GROUP *);
+int ec_GF2m_simple_group_set_curve(EC_GROUP *, const BIGNUM *p,
+                                   const BIGNUM *a, const BIGNUM *b,
+                                   BN_CTX *);
+int ec_GF2m_simple_group_get_curve(const EC_GROUP *, BIGNUM *p, BIGNUM *a,
+                                   BIGNUM *b, BN_CTX *);
+int ec_GF2m_simple_group_get_degree(const EC_GROUP *);
+int ec_GF2m_simple_group_check_discriminant(const EC_GROUP *, BN_CTX *);
+int ec_GF2m_simple_point_init(EC_POINT *);
+void ec_GF2m_simple_point_finish(EC_POINT *);
+void ec_GF2m_simple_point_clear_finish(EC_POINT *);
+int ec_GF2m_simple_point_copy(EC_POINT *, const EC_POINT *);
+int ec_GF2m_simple_point_set_to_infinity(const EC_GROUP *, EC_POINT *);
+int ec_GF2m_simple_point_set_affine_coordinates(const EC_GROUP *, EC_POINT *,
+                                                const BIGNUM *x,
+                                                const BIGNUM *y, BN_CTX *);
+int ec_GF2m_simple_point_get_affine_coordinates(const EC_GROUP *,
+                                                const EC_POINT *, BIGNUM *x,
+                                                BIGNUM *y, BN_CTX *);
+int ec_GF2m_simple_set_compressed_coordinates(const EC_GROUP *, EC_POINT *,
+                                              const BIGNUM *x, int y_bit,
+                                              BN_CTX *);
+size_t ec_GF2m_simple_point2oct(const EC_GROUP *, const EC_POINT *,
+                                point_conversion_form_t form,
+                                unsigned char *buf, size_t len, BN_CTX *);
+int ec_GF2m_simple_oct2point(const EC_GROUP *, EC_POINT *,
+                             const unsigned char *buf, size_t len, BN_CTX *);
+int ec_GF2m_simple_add(const EC_GROUP *, EC_POINT *r, const EC_POINT *a,
+                       const EC_POINT *b, BN_CTX *);
+int ec_GF2m_simple_dbl(const EC_GROUP *, EC_POINT *r, const EC_POINT *a,
+                       BN_CTX *);
+int ec_GF2m_simple_invert(const EC_GROUP *, EC_POINT *, BN_CTX *);
+int ec_GF2m_simple_is_at_infinity(const EC_GROUP *, const EC_POINT *);
+int ec_GF2m_simple_is_on_curve(const EC_GROUP *, const EC_POINT *, BN_CTX *);
+int ec_GF2m_simple_cmp(const EC_GROUP *, const EC_POINT *a, const EC_POINT *b,
+                       BN_CTX *);
+int ec_GF2m_simple_make_affine(const EC_GROUP *, EC_POINT *, BN_CTX *);
+int ec_GF2m_simple_points_make_affine(const EC_GROUP *, size_t num,
+                                      EC_POINT *[], BN_CTX *);
+int ec_GF2m_simple_field_mul(const EC_GROUP *, BIGNUM *r, const BIGNUM *a,
+                             const BIGNUM *b, BN_CTX *);
+int ec_GF2m_simple_field_sqr(const EC_GROUP *, BIGNUM *r, const BIGNUM *a,
+                             BN_CTX *);
+int ec_GF2m_simple_field_div(const EC_GROUP *, BIGNUM *r, const BIGNUM *a,
+                             const BIGNUM *b, BN_CTX *);
+
+/* method functions in ec2_mult.c */
+int ec_GF2m_simple_mul(const EC_GROUP *group, EC_POINT *r,
+                       const BIGNUM *scalar, size_t num,
+                       const EC_POINT *points[], const BIGNUM *scalars[],
+                       BN_CTX *);
+int ec_GF2m_precompute_mult(EC_GROUP *group, BN_CTX *ctx);
+int ec_GF2m_have_precompute_mult(const EC_GROUP *group);
+
+#ifndef OPENSSL_NO_EC_NISTP_64_GCC_128
+/* method functions in ecp_nistp224.c */
+int ec_GFp_nistp224_group_init(EC_GROUP *group);
+int ec_GFp_nistp224_group_set_curve(EC_GROUP *group, const BIGNUM *p,
+                                    const BIGNUM *a, const BIGNUM *n,
+                                    BN_CTX *);
+int ec_GFp_nistp224_point_get_affine_coordinates(const EC_GROUP *group,
+                                                 const EC_POINT *point,
+                                                 BIGNUM *x, BIGNUM *y,
+                                                 BN_CTX *ctx);
+int ec_GFp_nistp224_mul(const EC_GROUP *group, EC_POINT *r,
+                        const BIGNUM *scalar, size_t num,
+                        const EC_POINT *points[], const BIGNUM *scalars[],
+                        BN_CTX *);
+int ec_GFp_nistp224_points_mul(const EC_GROUP *group, EC_POINT *r,
+                               const BIGNUM *scalar, size_t num,
+                               const EC_POINT *points[],
+                               const BIGNUM *scalars[], BN_CTX *ctx);
+int ec_GFp_nistp224_precompute_mult(EC_GROUP *group, BN_CTX *ctx);
+int ec_GFp_nistp224_have_precompute_mult(const EC_GROUP *group);
+
+/* method functions in ecp_nistp256.c */
+int ec_GFp_nistp256_group_init(EC_GROUP *group);
+int ec_GFp_nistp256_group_set_curve(EC_GROUP *group, const BIGNUM *p,
+                                    const BIGNUM *a, const BIGNUM *n,
+                                    BN_CTX *);
+int ec_GFp_nistp256_point_get_affine_coordinates(const EC_GROUP *group,
+                                                 const EC_POINT *point,
+                                                 BIGNUM *x, BIGNUM *y,
+                                                 BN_CTX *ctx);
+int ec_GFp_nistp256_mul(const EC_GROUP *group, EC_POINT *r,
+                        const BIGNUM *scalar, size_t num,
+                        const EC_POINT *points[], const BIGNUM *scalars[],
+                        BN_CTX *);
+int ec_GFp_nistp256_points_mul(const EC_GROUP *group, EC_POINT *r,
+                               const BIGNUM *scalar, size_t num,
+                               const EC_POINT *points[],
+                               const BIGNUM *scalars[], BN_CTX *ctx);
+int ec_GFp_nistp256_precompute_mult(EC_GROUP *group, BN_CTX *ctx);
+int ec_GFp_nistp256_have_precompute_mult(const EC_GROUP *group);
+
+/* method functions in ecp_nistp521.c */
+int ec_GFp_nistp521_group_init(EC_GROUP *group);
+int ec_GFp_nistp521_group_set_curve(EC_GROUP *group, const BIGNUM *p,
+                                    const BIGNUM *a, const BIGNUM *n,
+                                    BN_CTX *);
+int ec_GFp_nistp521_point_get_affine_coordinates(const EC_GROUP *group,
+                                                 const EC_POINT *point,
+                                                 BIGNUM *x, BIGNUM *y,
+                                                 BN_CTX *ctx);
+int ec_GFp_nistp521_mul(const EC_GROUP *group, EC_POINT *r,
+                        const BIGNUM *scalar, size_t num,
+                        const EC_POINT *points[], const BIGNUM *scalars[],
+                        BN_CTX *);
+int ec_GFp_nistp521_points_mul(const EC_GROUP *group, EC_POINT *r,
+                               const BIGNUM *scalar, size_t num,
+                               const EC_POINT *points[],
+                               const BIGNUM *scalars[], BN_CTX *ctx);
+int ec_GFp_nistp521_precompute_mult(EC_GROUP *group, BN_CTX *ctx);
+int ec_GFp_nistp521_have_precompute_mult(const EC_GROUP *group);
+
+/* method functions in ecp_sm2p256.c */
+int ec_GFp_sm2p256_group_init(EC_GROUP *group);
+int ec_GFp_sm2p256_group_set_curve(EC_GROUP *group, const BIGNUM *p,
+                                   const BIGNUM *a, const BIGNUM *n,
+                                   BN_CTX *);
+int ec_GFp_sm2p256_point_get_affine_coordinates(const EC_GROUP *group,
+                                                const EC_POINT *point,
+                                                BIGNUM *x, BIGNUM *y,
+                                                BN_CTX *ctx);
+int ec_GFp_sm2p256_mul(const EC_GROUP *group, EC_POINT *r,
+                       const BIGNUM *scalar, size_t num,
+                       const EC_POINT *points[], const BIGNUM *scalars[],
+                       BN_CTX *);
+int ec_GFp_sm2p256_points_mul(const EC_GROUP *group, EC_POINT *r,
+                              const BIGNUM *scalar, size_t num,
+                              const EC_POINT *points[],
+                              const BIGNUM *scalars[], BN_CTX *ctx);
+int ec_GFp_sm2p256_precompute_mult(EC_GROUP *group, BN_CTX *ctx);
+int ec_GFp_sm2p256_have_precompute_mult(const EC_GROUP *group);
+
+/* utility functions in ecp_nistputil.c */
+void ec_GFp_nistp_points_make_affine_internal(size_t num, void *point_array,
+                                              size_t felem_size,
+                                              void *tmp_felems,
+                                              void (*felem_one) (void *out),
+                                              int (*felem_is_zero) (const void
+                                                                    *in),
+                                              void (*felem_assign) (void *out,
+                                                                    const void
+                                                                    *in),
+                                              void (*felem_square) (void *out,
+                                                                    const void
+                                                                    *in),
+                                              void (*felem_mul) (void *out,
+                                                                 const void
+                                                                 *in1,
+                                                                 const void
+                                                                 *in2),
+                                              void (*felem_inv) (void *out,
+                                                                 const void
+                                                                 *in),
+                                              void (*felem_contract) (void
+                                                                      *out,
+                                                                      const
+                                                                      void
+                                                                      *in));
+void ec_GFp_nistp_recode_scalar_bits(unsigned char *sign,
+                                     unsigned char *digit, unsigned char in);
+#endif
+int ec_precompute_mont_data(EC_GROUP *);
+int ec_group_simple_order_bits(const EC_GROUP *group);
+
+#ifdef ECP_NISTZ256_ASM
+/** Returns GFp methods using montgomery multiplication, with x86-64 optimized
+ * P256. See http://eprint.iacr.org/2013/816.
+ *  \return  EC_METHOD object
+ */
+const EC_METHOD *EC_GFp_nistz256_method(void);
+#endif
+
+#ifndef OPENSSL_NO_SM2
+# if defined(ECP_NISTZ256_ASM) && BN_BITS2 == 64 && !defined(GMSSL_NO_TURBO)
+const EC_METHOD *EC_GFp_sm2z256_method(void);
+# endif
+#endif
+
+size_t ec_key_simple_priv2oct(const EC_KEY *eckey,
+                              unsigned char *buf, size_t len);
+int ec_key_simple_oct2priv(EC_KEY *eckey, const unsigned char *buf, size_t len);
+int ec_key_simple_generate_key(EC_KEY *eckey);
+int ec_key_simple_generate_public_key(EC_KEY *eckey);
+int ec_key_simple_check_key(const EC_KEY *eckey);
+
+/* EC_METHOD definitions */
+
+struct ec_key_method_st {
+    const char *name;
+    int32_t flags;
+    int (*init)(EC_KEY *key);
+    void (*finish)(EC_KEY *key);
+    int (*copy)(EC_KEY *dest, const EC_KEY *src);
+    int (*set_group)(EC_KEY *key, const EC_GROUP *grp);
+    int (*set_private)(EC_KEY *key, const BIGNUM *priv_key);
+    int (*set_public)(EC_KEY *key, const EC_POINT *pub_key);
+    int (*keygen)(EC_KEY *key);
+    int (*compute_key)(unsigned char **pout, size_t *poutlen,
+                       const EC_POINT *pub_key, const EC_KEY *ecdh);
+    int (*sign)(int type, const unsigned char *dgst, int dlen, unsigned char
+                *sig, unsigned int *siglen, const BIGNUM *kinv,
+                const BIGNUM *r, EC_KEY *eckey);
+    int (*sign_setup)(EC_KEY *eckey, BN_CTX *ctx_in, BIGNUM **kinvp,
+                      BIGNUM **rp);
+    ECDSA_SIG *(*sign_sig)(const unsigned char *dgst, int dgst_len,
+                           const BIGNUM *in_kinv, const BIGNUM *in_r,
+                           EC_KEY *eckey);
+
+    int (*verify)(int type, const unsigned char *dgst, int dgst_len,
+                  const unsigned char *sigbuf, int sig_len, EC_KEY *eckey);
+    int (*verify_sig)(const unsigned char *dgst, int dgst_len,
+                      const ECDSA_SIG *sig, EC_KEY *eckey);
+#ifndef OPENSSL_NO_SM2
+    int (*encrypt)(int type, const unsigned char *in, size_t inlen,
+                   unsigned char *out, size_t *outlen, EC_KEY *ec_key);
+    ECIES_CIPHERTEXT_VALUE *(*do_encrypt)(int type, const unsigned char *in,
+                                          size_t inlen, EC_KEY *ec_key);
+    int (*decrypt)(int type, const unsigned char *in, size_t inlen,
+                   unsigned char *out, size_t *outlen, EC_KEY *ec_key);
+    int (*do_decrypt)(int type, const ECIES_CIPHERTEXT_VALUE *in,
+                      unsigned char *out, size_t *outlen, EC_KEY *ec_key);
+#endif
+};
+
+#define EC_KEY_METHOD_DYNAMIC   1
+
+int ossl_ec_key_gen(EC_KEY *eckey);
+int ossl_ecdh_compute_key(unsigned char **pout, size_t *poutlen,
+                          const EC_POINT *pub_key, const EC_KEY *ecdh);
+int ecdh_simple_compute_key(unsigned char **pout, size_t *poutlen,
+                            const EC_POINT *pub_key, const EC_KEY *ecdh);
+
+struct ECDSA_SIG_st {
+    BIGNUM *r;
+    BIGNUM *s;
+};
+
+int ossl_ecdsa_sign_setup(EC_KEY *eckey, BN_CTX *ctx_in, BIGNUM **kinvp,
+                          BIGNUM **rp);
+int ossl_ecdsa_sign(int type, const unsigned char *dgst, int dlen,
+                    unsigned char *sig, unsigned int *siglen,
+                    const BIGNUM *kinv, const BIGNUM *r, EC_KEY *eckey);
+ECDSA_SIG *ossl_ecdsa_sign_sig(const unsigned char *dgst, int dgst_len,
+                               const BIGNUM *in_kinv, const BIGNUM *in_r,
+                               EC_KEY *eckey);
+int ossl_ecdsa_verify(int type, const unsigned char *dgst, int dgst_len,
+                      const unsigned char *sigbuf, int sig_len, EC_KEY *eckey);
+int ossl_ecdsa_verify_sig(const unsigned char *dgst, int dgst_len,
+                          const ECDSA_SIG *sig, EC_KEY *eckey);
+
+int X25519(uint8_t out_shared_key[32], const uint8_t private_key[32],
+           const uint8_t peer_public_value[32]);
+void X25519_public_from_private(uint8_t out_public_value[32],
+                                const uint8_t private_key[32]);
diff --git a/crypto/ec/ec_lib.c b/crypto/ec/ec_lib.c
new file mode 100644
index 000000000..7cb4bfee2
--- /dev/null
+++ b/crypto/ec/ec_lib.c
@@ -0,0 +1,1004 @@
+/*
+ * Copyright 2001-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/* ====================================================================
+ * Copyright 2002 Sun Microsystems, Inc. ALL RIGHTS RESERVED.
+ * Binary polynomial ECC support in OpenSSL originally developed by
+ * SUN MICROSYSTEMS, INC., and contributed to the OpenSSL project.
+ */
+
+#include 
+
+#include 
+#include 
+
+#include "ec_lcl.h"
+
+/* functions for EC_GROUP objects */
+
+EC_GROUP *EC_GROUP_new(const EC_METHOD *meth)
+{
+    EC_GROUP *ret;
+
+    if (meth == NULL) {
+        ECerr(EC_F_EC_GROUP_NEW, EC_R_SLOT_FULL);
+        return NULL;
+    }
+    if (meth->group_init == 0) {
+        ECerr(EC_F_EC_GROUP_NEW, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
+        return NULL;
+    }
+
+    ret = OPENSSL_zalloc(sizeof(*ret));
+    if (ret == NULL) {
+        ECerr(EC_F_EC_GROUP_NEW, ERR_R_MALLOC_FAILURE);
+        return NULL;
+    }
+
+    ret->meth = meth;
+    if ((ret->meth->flags & EC_FLAGS_CUSTOM_CURVE) == 0) {
+        ret->order = BN_new();
+        if (ret->order == NULL)
+            goto err;
+        ret->cofactor = BN_new();
+        if (ret->cofactor == NULL)
+            goto err;
+    }
+    ret->asn1_flag = OPENSSL_EC_NAMED_CURVE;
+    ret->asn1_form = POINT_CONVERSION_UNCOMPRESSED;
+    if (!meth->group_init(ret))
+        goto err;
+    return ret;
+
+ err:
+    BN_free(ret->order);
+    BN_free(ret->cofactor);
+    OPENSSL_free(ret);
+    return NULL;
+}
+
+void EC_pre_comp_free(EC_GROUP *group)
+{
+    switch (group->pre_comp_type) {
+    default:
+        break;
+#ifdef ECP_NISTZ256_REFERENCE_IMPLEMENTATION
+    case PCT_nistz256:
+        EC_nistz256_pre_comp_free(group->pre_comp.nistz256);
+        break;
+#endif
+#ifndef OPENSSL_NO_EC_NISTP_64_GCC_128
+    case PCT_nistp224:
+        EC_nistp224_pre_comp_free(group->pre_comp.nistp224);
+        break;
+    case PCT_nistp256:
+        EC_nistp256_pre_comp_free(group->pre_comp.nistp256);
+        break;
+    case PCT_nistp521:
+        EC_nistp521_pre_comp_free(group->pre_comp.nistp521);
+        break;
+#endif
+    case PCT_ec:
+        EC_ec_pre_comp_free(group->pre_comp.ec);
+        break;
+    }
+    group->pre_comp.ec = NULL;
+}
+
+void EC_GROUP_free(EC_GROUP *group)
+{
+    if (!group)
+        return;
+
+    if (group->meth->group_finish != 0)
+        group->meth->group_finish(group);
+
+    EC_pre_comp_free(group);
+    BN_MONT_CTX_free(group->mont_data);
+    EC_POINT_free(group->generator);
+    BN_free(group->order);
+    BN_free(group->cofactor);
+    OPENSSL_free(group->seed);
+    OPENSSL_free(group);
+}
+
+void EC_GROUP_clear_free(EC_GROUP *group)
+{
+    if (!group)
+        return;
+
+    if (group->meth->group_clear_finish != 0)
+        group->meth->group_clear_finish(group);
+    else if (group->meth->group_finish != 0)
+        group->meth->group_finish(group);
+
+    EC_pre_comp_free(group);
+    BN_MONT_CTX_free(group->mont_data);
+    EC_POINT_clear_free(group->generator);
+    BN_clear_free(group->order);
+    BN_clear_free(group->cofactor);
+    OPENSSL_clear_free(group->seed, group->seed_len);
+    OPENSSL_clear_free(group, sizeof(*group));
+}
+
+int EC_GROUP_copy(EC_GROUP *dest, const EC_GROUP *src)
+{
+    if (dest->meth->group_copy == 0) {
+        ECerr(EC_F_EC_GROUP_COPY, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
+        return 0;
+    }
+    if (dest->meth != src->meth) {
+        ECerr(EC_F_EC_GROUP_COPY, EC_R_INCOMPATIBLE_OBJECTS);
+        return 0;
+    }
+    if (dest == src)
+        return 1;
+
+    /* Copy precomputed */
+    dest->pre_comp_type = src->pre_comp_type;
+    switch (src->pre_comp_type) {
+    default:
+        dest->pre_comp.ec = NULL;
+        break;
+#ifdef ECP_NISTZ256_REFERENCE_IMPLEMENTATION
+    case PCT_nistz256:
+        dest->pre_comp.nistz256 = EC_nistz256_pre_comp_dup(src->pre_comp.nistz256);
+        break;
+#endif
+#ifndef OPENSSL_NO_EC_NISTP_64_GCC_128
+    case PCT_nistp224:
+        dest->pre_comp.nistp224 = EC_nistp224_pre_comp_dup(src->pre_comp.nistp224);
+        break;
+    case PCT_nistp256:
+        dest->pre_comp.nistp256 = EC_nistp256_pre_comp_dup(src->pre_comp.nistp256);
+        break;
+    case PCT_nistp521:
+        dest->pre_comp.nistp521 = EC_nistp521_pre_comp_dup(src->pre_comp.nistp521);
+        break;
+#endif
+    case PCT_ec:
+        dest->pre_comp.ec = EC_ec_pre_comp_dup(src->pre_comp.ec);
+        break;
+    }
+
+    if (src->mont_data != NULL) {
+        if (dest->mont_data == NULL) {
+            dest->mont_data = BN_MONT_CTX_new();
+            if (dest->mont_data == NULL)
+                return 0;
+        }
+        if (!BN_MONT_CTX_copy(dest->mont_data, src->mont_data))
+            return 0;
+    } else {
+        /* src->generator == NULL */
+        BN_MONT_CTX_free(dest->mont_data);
+        dest->mont_data = NULL;
+    }
+
+    if (src->generator != NULL) {
+        if (dest->generator == NULL) {
+            dest->generator = EC_POINT_new(dest);
+            if (dest->generator == NULL)
+                return 0;
+        }
+        if (!EC_POINT_copy(dest->generator, src->generator))
+            return 0;
+    } else {
+        /* src->generator == NULL */
+        EC_POINT_clear_free(dest->generator);
+        dest->generator = NULL;
+    }
+
+    if ((src->meth->flags & EC_FLAGS_CUSTOM_CURVE) == 0) {
+        if (!BN_copy(dest->order, src->order))
+            return 0;
+        if (!BN_copy(dest->cofactor, src->cofactor))
+            return 0;
+    }
+
+    dest->curve_name = src->curve_name;
+    dest->asn1_flag = src->asn1_flag;
+    dest->asn1_form = src->asn1_form;
+
+    if (src->seed) {
+        OPENSSL_free(dest->seed);
+        dest->seed = OPENSSL_malloc(src->seed_len);
+        if (dest->seed == NULL)
+            return 0;
+        if (!memcpy(dest->seed, src->seed, src->seed_len))
+            return 0;
+        dest->seed_len = src->seed_len;
+    } else {
+        OPENSSL_free(dest->seed);
+        dest->seed = NULL;
+        dest->seed_len = 0;
+    }
+
+    return dest->meth->group_copy(dest, src);
+}
+
+EC_GROUP *EC_GROUP_dup(const EC_GROUP *a)
+{
+    EC_GROUP *t = NULL;
+    int ok = 0;
+
+    if (a == NULL)
+        return NULL;
+
+    if ((t = EC_GROUP_new(a->meth)) == NULL)
+        return (NULL);
+    if (!EC_GROUP_copy(t, a))
+        goto err;
+
+    ok = 1;
+
+ err:
+    if (!ok) {
+        EC_GROUP_free(t);
+        return NULL;
+    }
+        return t;
+}
+
+const EC_METHOD *EC_GROUP_method_of(const EC_GROUP *group)
+{
+    return group->meth;
+}
+
+int EC_METHOD_get_field_type(const EC_METHOD *meth)
+{
+    return meth->field_type;
+}
+
+int EC_GROUP_set_generator(EC_GROUP *group, const EC_POINT *generator,
+                           const BIGNUM *order, const BIGNUM *cofactor)
+{
+    if (generator == NULL) {
+        ECerr(EC_F_EC_GROUP_SET_GENERATOR, ERR_R_PASSED_NULL_PARAMETER);
+        return 0;
+    }
+
+    if (group->generator == NULL) {
+        group->generator = EC_POINT_new(group);
+        if (group->generator == NULL)
+            return 0;
+    }
+    if (!EC_POINT_copy(group->generator, generator))
+        return 0;
+
+    if (order != NULL) {
+        if (!BN_copy(group->order, order))
+            return 0;
+    } else
+        BN_zero(group->order);
+
+    if (cofactor != NULL) {
+        if (!BN_copy(group->cofactor, cofactor))
+            return 0;
+    } else
+        BN_zero(group->cofactor);
+
+    /*
+     * Some groups have an order with
+     * factors of two, which makes the Montgomery setup fail.
+     * |group->mont_data| will be NULL in this case.
+     */
+    if (BN_is_odd(group->order)) {
+        return ec_precompute_mont_data(group);
+    }
+
+    BN_MONT_CTX_free(group->mont_data);
+    group->mont_data = NULL;
+    return 1;
+}
+
+const EC_POINT *EC_GROUP_get0_generator(const EC_GROUP *group)
+{
+    return group->generator;
+}
+
+BN_MONT_CTX *EC_GROUP_get_mont_data(const EC_GROUP *group)
+{
+    return group->mont_data;
+}
+
+int EC_GROUP_get_order(const EC_GROUP *group, BIGNUM *order, BN_CTX *ctx)
+{
+    if (group->order == NULL)
+        return 0;
+    if (!BN_copy(order, group->order))
+        return 0;
+
+    return !BN_is_zero(order);
+}
+
+const BIGNUM *EC_GROUP_get0_order(const EC_GROUP *group)
+{
+    return group->order;
+}
+
+int EC_GROUP_order_bits(const EC_GROUP *group)
+{
+    OPENSSL_assert(group->meth->group_order_bits != NULL);
+    return group->meth->group_order_bits(group);
+}
+
+int EC_GROUP_get_cofactor(const EC_GROUP *group, BIGNUM *cofactor,
+                          BN_CTX *ctx)
+{
+
+    if (group->cofactor == NULL)
+        return 0;
+    if (!BN_copy(cofactor, group->cofactor))
+        return 0;
+
+    return !BN_is_zero(group->cofactor);
+}
+
+const BIGNUM *EC_GROUP_get0_cofactor(const EC_GROUP *group)
+{
+    return group->cofactor;
+}
+
+void EC_GROUP_set_curve_name(EC_GROUP *group, int nid)
+{
+    group->curve_name = nid;
+}
+
+int EC_GROUP_get_curve_name(const EC_GROUP *group)
+{
+    return group->curve_name;
+}
+
+void EC_GROUP_set_asn1_flag(EC_GROUP *group, int flag)
+{
+    group->asn1_flag = flag;
+}
+
+int EC_GROUP_get_asn1_flag(const EC_GROUP *group)
+{
+    return group->asn1_flag;
+}
+
+void EC_GROUP_set_point_conversion_form(EC_GROUP *group,
+                                        point_conversion_form_t form)
+{
+    group->asn1_form = form;
+}
+
+point_conversion_form_t EC_GROUP_get_point_conversion_form(const EC_GROUP
+                                                           *group)
+{
+    return group->asn1_form;
+}
+
+size_t EC_GROUP_set_seed(EC_GROUP *group, const unsigned char *p, size_t len)
+{
+    OPENSSL_free(group->seed);
+    group->seed = NULL;
+    group->seed_len = 0;
+
+    if (!len || !p)
+        return 1;
+
+    if ((group->seed = OPENSSL_malloc(len)) == NULL)
+        return 0;
+    memcpy(group->seed, p, len);
+    group->seed_len = len;
+
+    return len;
+}
+
+unsigned char *EC_GROUP_get0_seed(const EC_GROUP *group)
+{
+    return group->seed;
+}
+
+size_t EC_GROUP_get_seed_len(const EC_GROUP *group)
+{
+    return group->seed_len;
+}
+
+int EC_GROUP_set_curve_GFp(EC_GROUP *group, const BIGNUM *p, const BIGNUM *a,
+                           const BIGNUM *b, BN_CTX *ctx)
+{
+    if (group->meth->group_set_curve == 0) {
+        ECerr(EC_F_EC_GROUP_SET_CURVE_GFP, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
+        return 0;
+    }
+    return group->meth->group_set_curve(group, p, a, b, ctx);
+}
+
+int EC_GROUP_get_curve_GFp(const EC_GROUP *group, BIGNUM *p, BIGNUM *a,
+                           BIGNUM *b, BN_CTX *ctx)
+{
+    if (group->meth->group_get_curve == 0) {
+        ECerr(EC_F_EC_GROUP_GET_CURVE_GFP, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
+        return 0;
+    }
+    return group->meth->group_get_curve(group, p, a, b, ctx);
+}
+
+#ifndef OPENSSL_NO_EC2M
+int EC_GROUP_set_curve_GF2m(EC_GROUP *group, const BIGNUM *p, const BIGNUM *a,
+                            const BIGNUM *b, BN_CTX *ctx)
+{
+    if (group->meth->group_set_curve == 0) {
+        ECerr(EC_F_EC_GROUP_SET_CURVE_GF2M,
+              ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
+        return 0;
+    }
+    return group->meth->group_set_curve(group, p, a, b, ctx);
+}
+
+int EC_GROUP_get_curve_GF2m(const EC_GROUP *group, BIGNUM *p, BIGNUM *a,
+                            BIGNUM *b, BN_CTX *ctx)
+{
+    if (group->meth->group_get_curve == 0) {
+        ECerr(EC_F_EC_GROUP_GET_CURVE_GF2M,
+              ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
+        return 0;
+    }
+    return group->meth->group_get_curve(group, p, a, b, ctx);
+}
+#endif
+
+int EC_GROUP_get_degree(const EC_GROUP *group)
+{
+    if (group->meth->group_get_degree == 0) {
+        ECerr(EC_F_EC_GROUP_GET_DEGREE, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
+        return 0;
+    }
+    return group->meth->group_get_degree(group);
+}
+
+int EC_GROUP_check_discriminant(const EC_GROUP *group, BN_CTX *ctx)
+{
+    if (group->meth->group_check_discriminant == 0) {
+        ECerr(EC_F_EC_GROUP_CHECK_DISCRIMINANT,
+              ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
+        return 0;
+    }
+    return group->meth->group_check_discriminant(group, ctx);
+}
+
+int EC_GROUP_cmp(const EC_GROUP *a, const EC_GROUP *b, BN_CTX *ctx)
+{
+    int r = 0;
+    BIGNUM *a1, *a2, *a3, *b1, *b2, *b3;
+    BN_CTX *ctx_new = NULL;
+
+    /* compare the field types */
+    if (EC_METHOD_get_field_type(EC_GROUP_method_of(a)) !=
+        EC_METHOD_get_field_type(EC_GROUP_method_of(b)))
+        return 1;
+    /* compare the curve name (if present in both) */
+    if (EC_GROUP_get_curve_name(a) && EC_GROUP_get_curve_name(b) &&
+        EC_GROUP_get_curve_name(a) != EC_GROUP_get_curve_name(b))
+        return 1;
+    if (a->meth->flags & EC_FLAGS_CUSTOM_CURVE)
+        return 0;
+
+    if (ctx == NULL)
+        ctx_new = ctx = BN_CTX_new();
+    if (ctx == NULL)
+        return -1;
+
+    BN_CTX_start(ctx);
+    a1 = BN_CTX_get(ctx);
+    a2 = BN_CTX_get(ctx);
+    a3 = BN_CTX_get(ctx);
+    b1 = BN_CTX_get(ctx);
+    b2 = BN_CTX_get(ctx);
+    b3 = BN_CTX_get(ctx);
+    if (b3 == NULL) {
+        BN_CTX_end(ctx);
+        BN_CTX_free(ctx_new);
+        return -1;
+    }
+
+    /*
+     * XXX This approach assumes that the external representation of curves
+     * over the same field type is the same.
+     */
+    if (!a->meth->group_get_curve(a, a1, a2, a3, ctx) ||
+        !b->meth->group_get_curve(b, b1, b2, b3, ctx))
+        r = 1;
+
+    if (r || BN_cmp(a1, b1) || BN_cmp(a2, b2) || BN_cmp(a3, b3))
+        r = 1;
+
+    /* XXX EC_POINT_cmp() assumes that the methods are equal */
+    if (r || EC_POINT_cmp(a, EC_GROUP_get0_generator(a),
+                          EC_GROUP_get0_generator(b), ctx))
+        r = 1;
+
+    if (!r) {
+        const BIGNUM *ao, *bo, *ac, *bc;
+        /* compare the order and cofactor */
+        ao = EC_GROUP_get0_order(a);
+        bo = EC_GROUP_get0_order(b);
+        ac = EC_GROUP_get0_cofactor(a);
+        bc = EC_GROUP_get0_cofactor(b);
+        if (ao == NULL || bo == NULL) {
+            BN_CTX_end(ctx);
+            BN_CTX_free(ctx_new);
+            return -1;
+        }
+        if (BN_cmp(ao, bo) || BN_cmp(ac, bc))
+            r = 1;
+    }
+
+    BN_CTX_end(ctx);
+    BN_CTX_free(ctx_new);
+
+    return r;
+}
+
+/* functions for EC_POINT objects */
+
+EC_POINT *EC_POINT_new(const EC_GROUP *group)
+{
+    EC_POINT *ret;
+
+    if (group == NULL) {
+        ECerr(EC_F_EC_POINT_NEW, ERR_R_PASSED_NULL_PARAMETER);
+        return NULL;
+    }
+    if (group->meth->point_init == 0) {
+        ECerr(EC_F_EC_POINT_NEW, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
+        return NULL;
+    }
+
+    ret = OPENSSL_zalloc(sizeof(*ret));
+    if (ret == NULL) {
+        ECerr(EC_F_EC_POINT_NEW, ERR_R_MALLOC_FAILURE);
+        return NULL;
+    }
+
+    ret->meth = group->meth;
+
+    if (!ret->meth->point_init(ret)) {
+        OPENSSL_free(ret);
+        return NULL;
+    }
+
+    return ret;
+}
+
+void EC_POINT_free(EC_POINT *point)
+{
+    if (!point)
+        return;
+
+    if (point->meth->point_finish != 0)
+        point->meth->point_finish(point);
+    OPENSSL_free(point);
+}
+
+void EC_POINT_clear_free(EC_POINT *point)
+{
+    if (!point)
+        return;
+
+    if (point->meth->point_clear_finish != 0)
+        point->meth->point_clear_finish(point);
+    else if (point->meth->point_finish != 0)
+        point->meth->point_finish(point);
+    OPENSSL_clear_free(point, sizeof(*point));
+}
+
+int EC_POINT_copy(EC_POINT *dest, const EC_POINT *src)
+{
+    if (dest->meth->point_copy == 0) {
+        ECerr(EC_F_EC_POINT_COPY, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
+        return 0;
+    }
+    if (dest->meth != src->meth) {
+        ECerr(EC_F_EC_POINT_COPY, EC_R_INCOMPATIBLE_OBJECTS);
+        return 0;
+    }
+    if (dest == src)
+        return 1;
+    return dest->meth->point_copy(dest, src);
+}
+
+EC_POINT *EC_POINT_dup(const EC_POINT *a, const EC_GROUP *group)
+{
+    EC_POINT *t;
+    int r;
+
+    if (a == NULL)
+        return NULL;
+
+    t = EC_POINT_new(group);
+    if (t == NULL)
+        return (NULL);
+    r = EC_POINT_copy(t, a);
+    if (!r) {
+        EC_POINT_free(t);
+        return NULL;
+    }
+    return t;
+}
+
+const EC_METHOD *EC_POINT_method_of(const EC_POINT *point)
+{
+    return point->meth;
+}
+
+int EC_POINT_set_to_infinity(const EC_GROUP *group, EC_POINT *point)
+{
+    if (group->meth->point_set_to_infinity == 0) {
+        ECerr(EC_F_EC_POINT_SET_TO_INFINITY,
+              ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
+        return 0;
+    }
+    if (group->meth != point->meth) {
+        ECerr(EC_F_EC_POINT_SET_TO_INFINITY, EC_R_INCOMPATIBLE_OBJECTS);
+        return 0;
+    }
+    return group->meth->point_set_to_infinity(group, point);
+}
+
+int EC_POINT_set_Jprojective_coordinates_GFp(const EC_GROUP *group,
+                                             EC_POINT *point, const BIGNUM *x,
+                                             const BIGNUM *y, const BIGNUM *z,
+                                             BN_CTX *ctx)
+{
+    if (group->meth->point_set_Jprojective_coordinates_GFp == 0) {
+        ECerr(EC_F_EC_POINT_SET_JPROJECTIVE_COORDINATES_GFP,
+              ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
+        return 0;
+    }
+    if (group->meth != point->meth) {
+        ECerr(EC_F_EC_POINT_SET_JPROJECTIVE_COORDINATES_GFP,
+              EC_R_INCOMPATIBLE_OBJECTS);
+        return 0;
+    }
+    return group->meth->point_set_Jprojective_coordinates_GFp(group, point, x,
+                                                              y, z, ctx);
+}
+
+int EC_POINT_get_Jprojective_coordinates_GFp(const EC_GROUP *group,
+                                             const EC_POINT *point, BIGNUM *x,
+                                             BIGNUM *y, BIGNUM *z,
+                                             BN_CTX *ctx)
+{
+    if (group->meth->point_get_Jprojective_coordinates_GFp == 0) {
+        ECerr(EC_F_EC_POINT_GET_JPROJECTIVE_COORDINATES_GFP,
+              ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
+        return 0;
+    }
+    if (group->meth != point->meth) {
+        ECerr(EC_F_EC_POINT_GET_JPROJECTIVE_COORDINATES_GFP,
+              EC_R_INCOMPATIBLE_OBJECTS);
+        return 0;
+    }
+    return group->meth->point_get_Jprojective_coordinates_GFp(group, point, x,
+                                                              y, z, ctx);
+}
+
+int EC_POINT_set_affine_coordinates_GFp(const EC_GROUP *group,
+                                        EC_POINT *point, const BIGNUM *x,
+                                        const BIGNUM *y, BN_CTX *ctx)
+{
+    if (group->meth->point_set_affine_coordinates == 0) {
+        ECerr(EC_F_EC_POINT_SET_AFFINE_COORDINATES_GFP,
+              ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
+        return 0;
+    }
+    if (group->meth != point->meth) {
+        ECerr(EC_F_EC_POINT_SET_AFFINE_COORDINATES_GFP,
+              EC_R_INCOMPATIBLE_OBJECTS);
+        return 0;
+    }
+    if (!group->meth->point_set_affine_coordinates(group, point, x, y, ctx))
+        return 0;
+
+    if (EC_POINT_is_on_curve(group, point, ctx) <= 0) {
+        ECerr(EC_F_EC_POINT_SET_AFFINE_COORDINATES_GFP,
+              EC_R_POINT_IS_NOT_ON_CURVE);
+        return 0;
+    }
+    return 1;
+}
+
+#ifndef OPENSSL_NO_EC2M
+int EC_POINT_set_affine_coordinates_GF2m(const EC_GROUP *group,
+                                         EC_POINT *point, const BIGNUM *x,
+                                         const BIGNUM *y, BN_CTX *ctx)
+{
+    if (group->meth->point_set_affine_coordinates == 0) {
+        ECerr(EC_F_EC_POINT_SET_AFFINE_COORDINATES_GF2M,
+              ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
+        return 0;
+    }
+    if (group->meth != point->meth) {
+        ECerr(EC_F_EC_POINT_SET_AFFINE_COORDINATES_GF2M,
+              EC_R_INCOMPATIBLE_OBJECTS);
+        return 0;
+    }
+    if (!group->meth->point_set_affine_coordinates(group, point, x, y, ctx))
+        return 0;
+
+    if (EC_POINT_is_on_curve(group, point, ctx) <= 0) {
+        ECerr(EC_F_EC_POINT_SET_AFFINE_COORDINATES_GF2M,
+              EC_R_POINT_IS_NOT_ON_CURVE);
+        return 0;
+    }
+    return 1;
+}
+#endif
+
+int EC_POINT_get_affine_coordinates_GFp(const EC_GROUP *group,
+                                        const EC_POINT *point, BIGNUM *x,
+                                        BIGNUM *y, BN_CTX *ctx)
+{
+    if (group->meth->point_get_affine_coordinates == 0) {
+        ECerr(EC_F_EC_POINT_GET_AFFINE_COORDINATES_GFP,
+              ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
+        return 0;
+    }
+    if (group->meth != point->meth) {
+        ECerr(EC_F_EC_POINT_GET_AFFINE_COORDINATES_GFP,
+              EC_R_INCOMPATIBLE_OBJECTS);
+        return 0;
+    }
+    return group->meth->point_get_affine_coordinates(group, point, x, y, ctx);
+}
+
+#ifndef OPENSSL_NO_EC2M
+int EC_POINT_get_affine_coordinates_GF2m(const EC_GROUP *group,
+                                         const EC_POINT *point, BIGNUM *x,
+                                         BIGNUM *y, BN_CTX *ctx)
+{
+    if (group->meth->point_get_affine_coordinates == 0) {
+        ECerr(EC_F_EC_POINT_GET_AFFINE_COORDINATES_GF2M,
+              ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
+        return 0;
+    }
+    if (group->meth != point->meth) {
+        ECerr(EC_F_EC_POINT_GET_AFFINE_COORDINATES_GF2M,
+              EC_R_INCOMPATIBLE_OBJECTS);
+        return 0;
+    }
+    return group->meth->point_get_affine_coordinates(group, point, x, y, ctx);
+}
+#endif
+
+int EC_POINT_add(const EC_GROUP *group, EC_POINT *r, const EC_POINT *a,
+                 const EC_POINT *b, BN_CTX *ctx)
+{
+    if (group->meth->add == 0) {
+        ECerr(EC_F_EC_POINT_ADD, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
+        return 0;
+    }
+    if ((group->meth != r->meth) || (r->meth != a->meth)
+        || (a->meth != b->meth)) {
+        ECerr(EC_F_EC_POINT_ADD, EC_R_INCOMPATIBLE_OBJECTS);
+        return 0;
+    }
+    return group->meth->add(group, r, a, b, ctx);
+}
+
+int EC_POINT_dbl(const EC_GROUP *group, EC_POINT *r, const EC_POINT *a,
+                 BN_CTX *ctx)
+{
+    if (group->meth->dbl == 0) {
+        ECerr(EC_F_EC_POINT_DBL, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
+        return 0;
+    }
+    if ((group->meth != r->meth) || (r->meth != a->meth)) {
+        ECerr(EC_F_EC_POINT_DBL, EC_R_INCOMPATIBLE_OBJECTS);
+        return 0;
+    }
+    return group->meth->dbl(group, r, a, ctx);
+}
+
+int EC_POINT_invert(const EC_GROUP *group, EC_POINT *a, BN_CTX *ctx)
+{
+    if (group->meth->invert == 0) {
+        ECerr(EC_F_EC_POINT_INVERT, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
+        return 0;
+    }
+    if (group->meth != a->meth) {
+        ECerr(EC_F_EC_POINT_INVERT, EC_R_INCOMPATIBLE_OBJECTS);
+        return 0;
+    }
+    return group->meth->invert(group, a, ctx);
+}
+
+int EC_POINT_is_at_infinity(const EC_GROUP *group, const EC_POINT *point)
+{
+    if (group->meth->is_at_infinity == 0) {
+        ECerr(EC_F_EC_POINT_IS_AT_INFINITY,
+              ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
+        return 0;
+    }
+    if (group->meth != point->meth) {
+        ECerr(EC_F_EC_POINT_IS_AT_INFINITY, EC_R_INCOMPATIBLE_OBJECTS);
+        return 0;
+    }
+    return group->meth->is_at_infinity(group, point);
+}
+
+/*
+ * Check whether an EC_POINT is on the curve or not. Note that the return
+ * value for this function should NOT be treated as a boolean. Return values:
+ *  1: The point is on the curve
+ *  0: The point is not on the curve
+ * -1: An error occurred
+ */
+int EC_POINT_is_on_curve(const EC_GROUP *group, const EC_POINT *point,
+                         BN_CTX *ctx)
+{
+    if (group->meth->is_on_curve == 0) {
+        ECerr(EC_F_EC_POINT_IS_ON_CURVE, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
+        return 0;
+    }
+    if (group->meth != point->meth) {
+        ECerr(EC_F_EC_POINT_IS_ON_CURVE, EC_R_INCOMPATIBLE_OBJECTS);
+        return 0;
+    }
+    return group->meth->is_on_curve(group, point, ctx);
+}
+
+int EC_POINT_cmp(const EC_GROUP *group, const EC_POINT *a, const EC_POINT *b,
+                 BN_CTX *ctx)
+{
+    if (group->meth->point_cmp == 0) {
+        ECerr(EC_F_EC_POINT_CMP, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
+        return -1;
+    }
+    if ((group->meth != a->meth) || (a->meth != b->meth)) {
+        ECerr(EC_F_EC_POINT_CMP, EC_R_INCOMPATIBLE_OBJECTS);
+        return -1;
+    }
+    return group->meth->point_cmp(group, a, b, ctx);
+}
+
+int EC_POINT_make_affine(const EC_GROUP *group, EC_POINT *point, BN_CTX *ctx)
+{
+    if (group->meth->make_affine == 0) {
+        ECerr(EC_F_EC_POINT_MAKE_AFFINE, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
+        return 0;
+    }
+    if (group->meth != point->meth) {
+        ECerr(EC_F_EC_POINT_MAKE_AFFINE, EC_R_INCOMPATIBLE_OBJECTS);
+        return 0;
+    }
+    return group->meth->make_affine(group, point, ctx);
+}
+
+int EC_POINTs_make_affine(const EC_GROUP *group, size_t num,
+                          EC_POINT *points[], BN_CTX *ctx)
+{
+    size_t i;
+
+    if (group->meth->points_make_affine == 0) {
+        ECerr(EC_F_EC_POINTS_MAKE_AFFINE, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
+        return 0;
+    }
+    for (i = 0; i < num; i++) {
+        if (group->meth != points[i]->meth) {
+            ECerr(EC_F_EC_POINTS_MAKE_AFFINE, EC_R_INCOMPATIBLE_OBJECTS);
+            return 0;
+        }
+    }
+    return group->meth->points_make_affine(group, num, points, ctx);
+}
+
+/*
+ * Functions for point multiplication. If group->meth->mul is 0, we use the
+ * wNAF-based implementations in ec_mult.c; otherwise we dispatch through
+ * methods.
+ */
+
+int EC_POINTs_mul(const EC_GROUP *group, EC_POINT *r, const BIGNUM *scalar,
+                  size_t num, const EC_POINT *points[],
+                  const BIGNUM *scalars[], BN_CTX *ctx)
+{
+    if (group->meth->mul == 0)
+        /* use default */
+        return ec_wNAF_mul(group, r, scalar, num, points, scalars, ctx);
+
+    return group->meth->mul(group, r, scalar, num, points, scalars, ctx);
+}
+
+int EC_POINT_mul(const EC_GROUP *group, EC_POINT *r, const BIGNUM *g_scalar,
+                 const EC_POINT *point, const BIGNUM *p_scalar, BN_CTX *ctx)
+{
+    /* just a convenient interface to EC_POINTs_mul() */
+
+    const EC_POINT *points[1];
+    const BIGNUM *scalars[1];
+
+    points[0] = point;
+    scalars[0] = p_scalar;
+
+    return EC_POINTs_mul(group, r, g_scalar,
+                         (point != NULL
+                          && p_scalar != NULL), points, scalars, ctx);
+}
+
+int EC_GROUP_precompute_mult(EC_GROUP *group, BN_CTX *ctx)
+{
+    if (group->meth->mul == 0)
+        /* use default */
+        return ec_wNAF_precompute_mult(group, ctx);
+
+    if (group->meth->precompute_mult != 0)
+        return group->meth->precompute_mult(group, ctx);
+    else
+        return 1;               /* nothing to do, so report success */
+}
+
+int EC_GROUP_have_precompute_mult(const EC_GROUP *group)
+{
+    if (group->meth->mul == 0)
+        /* use default */
+        return ec_wNAF_have_precompute_mult(group);
+
+    if (group->meth->have_precompute_mult != 0)
+        return group->meth->have_precompute_mult(group);
+    else
+        return 0;               /* cannot tell whether precomputation has
+                                 * been performed */
+}
+
+/*
+ * ec_precompute_mont_data sets |group->mont_data| from |group->order| and
+ * returns one on success. On error it returns zero.
+ */
+int ec_precompute_mont_data(EC_GROUP *group)
+{
+    BN_CTX *ctx = BN_CTX_new();
+    int ret = 0;
+
+    BN_MONT_CTX_free(group->mont_data);
+    group->mont_data = NULL;
+
+    if (ctx == NULL)
+        goto err;
+
+    group->mont_data = BN_MONT_CTX_new();
+    if (group->mont_data == NULL)
+        goto err;
+
+    if (!BN_MONT_CTX_set(group->mont_data, group->order, ctx)) {
+        BN_MONT_CTX_free(group->mont_data);
+        group->mont_data = NULL;
+        goto err;
+    }
+
+    ret = 1;
+
+ err:
+
+    BN_CTX_free(ctx);
+    return ret;
+}
+
+int EC_KEY_set_ex_data(EC_KEY *key, int idx, void *arg)
+{
+    return CRYPTO_set_ex_data(&key->ex_data, idx, arg);
+}
+
+void *EC_KEY_get_ex_data(const EC_KEY *key, int idx)
+{
+    return CRYPTO_get_ex_data(&key->ex_data, idx);
+}
+
+int ec_group_simple_order_bits(const EC_GROUP *group)
+{
+    if (group->order == NULL)
+        return 0;
+    return BN_num_bits(group->order);
+}
diff --git a/crypto/ec/ec_mult.c b/crypto/ec/ec_mult.c
new file mode 100644
index 000000000..036cdde49
--- /dev/null
+++ b/crypto/ec/ec_mult.c
@@ -0,0 +1,680 @@
+/*
+ * Copyright 2001-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/* ====================================================================
+ * Copyright 2002 Sun Microsystems, Inc. ALL RIGHTS RESERVED.
+ * Portions of this software developed by SUN MICROSYSTEMS, INC.,
+ * and contributed to the OpenSSL project.
+ */
+
+#include 
+#include 
+
+#include "internal/cryptlib.h"
+#include "internal/bn_int.h"
+#include "ec_lcl.h"
+
+/*
+ * This file implements the wNAF-based interleaving multi-exponentiation method
+ * ();
+ * for multiplication with precomputation, we use wNAF splitting
+ * ().
+ */
+
+/* structure for precomputed multiples of the generator */
+struct ec_pre_comp_st {
+    const EC_GROUP *group;      /* parent EC_GROUP object */
+    size_t blocksize;           /* block size for wNAF splitting */
+    size_t numblocks;           /* max. number of blocks for which we have
+                                 * precomputation */
+    size_t w;                   /* window size */
+    EC_POINT **points;          /* array with pre-calculated multiples of
+                                 * generator: 'num' pointers to EC_POINT
+                                 * objects followed by a NULL */
+    size_t num;                 /* numblocks * 2^(w-1) */
+    int references;
+    CRYPTO_RWLOCK *lock;
+};
+
+static EC_PRE_COMP *ec_pre_comp_new(const EC_GROUP *group)
+{
+    EC_PRE_COMP *ret = NULL;
+
+    if (!group)
+        return NULL;
+
+    ret = OPENSSL_zalloc(sizeof(*ret));
+    if (ret == NULL) {
+        ECerr(EC_F_EC_PRE_COMP_NEW, ERR_R_MALLOC_FAILURE);
+        return ret;
+    }
+
+    ret->group = group;
+    ret->blocksize = 8;         /* default */
+    ret->w = 4;                 /* default */
+    ret->references = 1;
+
+    ret->lock = CRYPTO_THREAD_lock_new();
+    if (ret->lock == NULL) {
+        ECerr(EC_F_EC_PRE_COMP_NEW, ERR_R_MALLOC_FAILURE);
+        OPENSSL_free(ret);
+        return NULL;
+    }
+    return ret;
+}
+
+EC_PRE_COMP *EC_ec_pre_comp_dup(EC_PRE_COMP *pre)
+{
+    int i;
+    if (pre != NULL)
+        CRYPTO_atomic_add(&pre->references, 1, &i, pre->lock);
+    return pre;
+}
+
+void EC_ec_pre_comp_free(EC_PRE_COMP *pre)
+{
+    int i;
+
+    if (pre == NULL)
+        return;
+
+    CRYPTO_atomic_add(&pre->references, -1, &i, pre->lock);
+    REF_PRINT_COUNT("EC_ec", pre);
+    if (i > 0)
+        return;
+    REF_ASSERT_ISNT(i < 0);
+
+    if (pre->points != NULL) {
+        EC_POINT **pts;
+
+        for (pts = pre->points; *pts != NULL; pts++)
+            EC_POINT_free(*pts);
+        OPENSSL_free(pre->points);
+    }
+    CRYPTO_THREAD_lock_free(pre->lock);
+    OPENSSL_free(pre);
+}
+
+/*
+ * TODO: table should be optimised for the wNAF-based implementation,
+ * sometimes smaller windows will give better performance (thus the
+ * boundaries should be increased)
+ */
+#define EC_window_bits_for_scalar_size(b) \
+                ((size_t) \
+                 ((b) >= 2000 ? 6 : \
+                  (b) >=  800 ? 5 : \
+                  (b) >=  300 ? 4 : \
+                  (b) >=   70 ? 3 : \
+                  (b) >=   20 ? 2 : \
+                  1))
+
+/*-
+ * Compute
+ *      \sum scalars[i]*points[i],
+ * also including
+ *      scalar*generator
+ * in the addition if scalar != NULL
+ */
+int ec_wNAF_mul(const EC_GROUP *group, EC_POINT *r, const BIGNUM *scalar,
+                size_t num, const EC_POINT *points[], const BIGNUM *scalars[],
+                BN_CTX *ctx)
+{
+    BN_CTX *new_ctx = NULL;
+    const EC_POINT *generator = NULL;
+    EC_POINT *tmp = NULL;
+    size_t totalnum;
+    size_t blocksize = 0, numblocks = 0; /* for wNAF splitting */
+    size_t pre_points_per_block = 0;
+    size_t i, j;
+    int k;
+    int r_is_inverted = 0;
+    int r_is_at_infinity = 1;
+    size_t *wsize = NULL;       /* individual window sizes */
+    signed char **wNAF = NULL;  /* individual wNAFs */
+    size_t *wNAF_len = NULL;
+    size_t max_len = 0;
+    size_t num_val;
+    EC_POINT **val = NULL;      /* precomputation */
+    EC_POINT **v;
+    EC_POINT ***val_sub = NULL; /* pointers to sub-arrays of 'val' or
+                                 * 'pre_comp->points' */
+    const EC_PRE_COMP *pre_comp = NULL;
+    int num_scalar = 0;         /* flag: will be set to 1 if 'scalar' must be
+                                 * treated like other scalars, i.e.
+                                 * precomputation is not available */
+    int ret = 0;
+
+    if (group->meth != r->meth) {
+        ECerr(EC_F_EC_WNAF_MUL, EC_R_INCOMPATIBLE_OBJECTS);
+        return 0;
+    }
+
+    if ((scalar == NULL) && (num == 0)) {
+        return EC_POINT_set_to_infinity(group, r);
+    }
+
+    for (i = 0; i < num; i++) {
+        if (group->meth != points[i]->meth) {
+            ECerr(EC_F_EC_WNAF_MUL, EC_R_INCOMPATIBLE_OBJECTS);
+            return 0;
+        }
+    }
+
+    if (ctx == NULL) {
+        ctx = new_ctx = BN_CTX_new();
+        if (ctx == NULL)
+            goto err;
+    }
+
+    if (scalar != NULL) {
+        generator = EC_GROUP_get0_generator(group);
+        if (generator == NULL) {
+            ECerr(EC_F_EC_WNAF_MUL, EC_R_UNDEFINED_GENERATOR);
+            goto err;
+        }
+
+        /* look if we can use precomputed multiples of generator */
+
+        pre_comp = group->pre_comp.ec;
+        if (pre_comp && pre_comp->numblocks
+            && (EC_POINT_cmp(group, generator, pre_comp->points[0], ctx) ==
+                0)) {
+            blocksize = pre_comp->blocksize;
+
+            /*
+             * determine maximum number of blocks that wNAF splitting may
+             * yield (NB: maximum wNAF length is bit length plus one)
+             */
+            numblocks = (BN_num_bits(scalar) / blocksize) + 1;
+
+            /*
+             * we cannot use more blocks than we have precomputation for
+             */
+            if (numblocks > pre_comp->numblocks)
+                numblocks = pre_comp->numblocks;
+
+            pre_points_per_block = (size_t)1 << (pre_comp->w - 1);
+
+            /* check that pre_comp looks sane */
+            if (pre_comp->num != (pre_comp->numblocks * pre_points_per_block)) {
+                ECerr(EC_F_EC_WNAF_MUL, ERR_R_INTERNAL_ERROR);
+                goto err;
+            }
+        } else {
+            /* can't use precomputation */
+            pre_comp = NULL;
+            numblocks = 1;
+            num_scalar = 1;     /* treat 'scalar' like 'num'-th element of
+                                 * 'scalars' */
+        }
+    }
+
+    totalnum = num + numblocks;
+
+    wsize = OPENSSL_malloc(totalnum * sizeof wsize[0]);
+    wNAF_len = OPENSSL_malloc(totalnum * sizeof wNAF_len[0]);
+    wNAF = OPENSSL_malloc((totalnum + 1) * sizeof wNAF[0]); /* includes space
+                                                             * for pivot */
+    val_sub = OPENSSL_malloc(totalnum * sizeof val_sub[0]);
+
+    /* Ensure wNAF is initialised in case we end up going to err */
+    if (wNAF != NULL)
+        wNAF[0] = NULL;         /* preliminary pivot */
+
+    if (wsize == NULL || wNAF_len == NULL || wNAF == NULL || val_sub == NULL) {
+        ECerr(EC_F_EC_WNAF_MUL, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+
+    /*
+     * num_val will be the total number of temporarily precomputed points
+     */
+    num_val = 0;
+
+    for (i = 0; i < num + num_scalar; i++) {
+        size_t bits;
+
+        bits = i < num ? BN_num_bits(scalars[i]) : BN_num_bits(scalar);
+        wsize[i] = EC_window_bits_for_scalar_size(bits);
+        num_val += (size_t)1 << (wsize[i] - 1);
+        wNAF[i + 1] = NULL;     /* make sure we always have a pivot */
+        wNAF[i] =
+            bn_compute_wNAF((i < num ? scalars[i] : scalar), wsize[i],
+                            &wNAF_len[i]);
+        if (wNAF[i] == NULL)
+            goto err;
+        if (wNAF_len[i] > max_len)
+            max_len = wNAF_len[i];
+    }
+
+    if (numblocks) {
+        /* we go here iff scalar != NULL */
+
+        if (pre_comp == NULL) {
+            if (num_scalar != 1) {
+                ECerr(EC_F_EC_WNAF_MUL, ERR_R_INTERNAL_ERROR);
+                goto err;
+            }
+            /* we have already generated a wNAF for 'scalar' */
+        } else {
+            signed char *tmp_wNAF = NULL;
+            size_t tmp_len = 0;
+
+            if (num_scalar != 0) {
+                ECerr(EC_F_EC_WNAF_MUL, ERR_R_INTERNAL_ERROR);
+                goto err;
+            }
+
+            /*
+             * use the window size for which we have precomputation
+             */
+            wsize[num] = pre_comp->w;
+            tmp_wNAF = bn_compute_wNAF(scalar, wsize[num], &tmp_len);
+            if (!tmp_wNAF)
+                goto err;
+
+            if (tmp_len <= max_len) {
+                /*
+                 * One of the other wNAFs is at least as long as the wNAF
+                 * belonging to the generator, so wNAF splitting will not buy
+                 * us anything.
+                 */
+
+                numblocks = 1;
+                totalnum = num + 1; /* don't use wNAF splitting */
+                wNAF[num] = tmp_wNAF;
+                wNAF[num + 1] = NULL;
+                wNAF_len[num] = tmp_len;
+                /*
+                 * pre_comp->points starts with the points that we need here:
+                 */
+                val_sub[num] = pre_comp->points;
+            } else {
+                /*
+                 * don't include tmp_wNAF directly into wNAF array - use wNAF
+                 * splitting and include the blocks
+                 */
+
+                signed char *pp;
+                EC_POINT **tmp_points;
+
+                if (tmp_len < numblocks * blocksize) {
+                    /*
+                     * possibly we can do with fewer blocks than estimated
+                     */
+                    numblocks = (tmp_len + blocksize - 1) / blocksize;
+                    if (numblocks > pre_comp->numblocks) {
+                        ECerr(EC_F_EC_WNAF_MUL, ERR_R_INTERNAL_ERROR);
+                        OPENSSL_free(tmp_wNAF);
+                        goto err;
+                    }
+                    totalnum = num + numblocks;
+                }
+
+                /* split wNAF in 'numblocks' parts */
+                pp = tmp_wNAF;
+                tmp_points = pre_comp->points;
+
+                for (i = num; i < totalnum; i++) {
+                    if (i < totalnum - 1) {
+                        wNAF_len[i] = blocksize;
+                        if (tmp_len < blocksize) {
+                            ECerr(EC_F_EC_WNAF_MUL, ERR_R_INTERNAL_ERROR);
+                            OPENSSL_free(tmp_wNAF);
+                            goto err;
+                        }
+                        tmp_len -= blocksize;
+                    } else
+                        /*
+                         * last block gets whatever is left (this could be
+                         * more or less than 'blocksize'!)
+                         */
+                        wNAF_len[i] = tmp_len;
+
+                    wNAF[i + 1] = NULL;
+                    wNAF[i] = OPENSSL_malloc(wNAF_len[i]);
+                    if (wNAF[i] == NULL) {
+                        ECerr(EC_F_EC_WNAF_MUL, ERR_R_MALLOC_FAILURE);
+                        OPENSSL_free(tmp_wNAF);
+                        goto err;
+                    }
+                    memcpy(wNAF[i], pp, wNAF_len[i]);
+                    if (wNAF_len[i] > max_len)
+                        max_len = wNAF_len[i];
+
+                    if (*tmp_points == NULL) {
+                        ECerr(EC_F_EC_WNAF_MUL, ERR_R_INTERNAL_ERROR);
+                        OPENSSL_free(tmp_wNAF);
+                        goto err;
+                    }
+                    val_sub[i] = tmp_points;
+                    tmp_points += pre_points_per_block;
+                    pp += blocksize;
+                }
+                OPENSSL_free(tmp_wNAF);
+            }
+        }
+    }
+
+    /*
+     * All points we precompute now go into a single array 'val'.
+     * 'val_sub[i]' is a pointer to the subarray for the i-th point, or to a
+     * subarray of 'pre_comp->points' if we already have precomputation.
+     */
+    val = OPENSSL_malloc((num_val + 1) * sizeof val[0]);
+    if (val == NULL) {
+        ECerr(EC_F_EC_WNAF_MUL, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+    val[num_val] = NULL;        /* pivot element */
+
+    /* allocate points for precomputation */
+    v = val;
+    for (i = 0; i < num + num_scalar; i++) {
+        val_sub[i] = v;
+        for (j = 0; j < ((size_t)1 << (wsize[i] - 1)); j++) {
+            *v = EC_POINT_new(group);
+            if (*v == NULL)
+                goto err;
+            v++;
+        }
+    }
+    if (!(v == val + num_val)) {
+        ECerr(EC_F_EC_WNAF_MUL, ERR_R_INTERNAL_ERROR);
+        goto err;
+    }
+
+    if ((tmp = EC_POINT_new(group)) == NULL)
+        goto err;
+
+    /*-
+     * prepare precomputed values:
+     *    val_sub[i][0] :=     points[i]
+     *    val_sub[i][1] := 3 * points[i]
+     *    val_sub[i][2] := 5 * points[i]
+     *    ...
+     */
+    for (i = 0; i < num + num_scalar; i++) {
+        if (i < num) {
+            if (!EC_POINT_copy(val_sub[i][0], points[i]))
+                goto err;
+        } else {
+            if (!EC_POINT_copy(val_sub[i][0], generator))
+                goto err;
+        }
+
+        if (wsize[i] > 1) {
+            if (!EC_POINT_dbl(group, tmp, val_sub[i][0], ctx))
+                goto err;
+            for (j = 1; j < ((size_t)1 << (wsize[i] - 1)); j++) {
+                if (!EC_POINT_add
+                    (group, val_sub[i][j], val_sub[i][j - 1], tmp, ctx))
+                    goto err;
+            }
+        }
+    }
+
+    if (!EC_POINTs_make_affine(group, num_val, val, ctx))
+        goto err;
+
+    r_is_at_infinity = 1;
+
+    for (k = max_len - 1; k >= 0; k--) {
+        if (!r_is_at_infinity) {
+            if (!EC_POINT_dbl(group, r, r, ctx))
+                goto err;
+        }
+
+        for (i = 0; i < totalnum; i++) {
+            if (wNAF_len[i] > (size_t)k) {
+                int digit = wNAF[i][k];
+                int is_neg;
+
+                if (digit) {
+                    is_neg = digit < 0;
+
+                    if (is_neg)
+                        digit = -digit;
+
+                    if (is_neg != r_is_inverted) {
+                        if (!r_is_at_infinity) {
+                            if (!EC_POINT_invert(group, r, ctx))
+                                goto err;
+                        }
+                        r_is_inverted = !r_is_inverted;
+                    }
+
+                    /* digit > 0 */
+
+                    if (r_is_at_infinity) {
+                        if (!EC_POINT_copy(r, val_sub[i][digit >> 1]))
+                            goto err;
+                        r_is_at_infinity = 0;
+                    } else {
+                        if (!EC_POINT_add
+                            (group, r, r, val_sub[i][digit >> 1], ctx))
+                            goto err;
+                    }
+                }
+            }
+        }
+    }
+
+    if (r_is_at_infinity) {
+        if (!EC_POINT_set_to_infinity(group, r))
+            goto err;
+    } else {
+        if (r_is_inverted)
+            if (!EC_POINT_invert(group, r, ctx))
+                goto err;
+    }
+
+    ret = 1;
+
+ err:
+    BN_CTX_free(new_ctx);
+    EC_POINT_free(tmp);
+    OPENSSL_free(wsize);
+    OPENSSL_free(wNAF_len);
+    if (wNAF != NULL) {
+        signed char **w;
+
+        for (w = wNAF; *w != NULL; w++)
+            OPENSSL_free(*w);
+
+        OPENSSL_free(wNAF);
+    }
+    if (val != NULL) {
+        for (v = val; *v != NULL; v++)
+            EC_POINT_clear_free(*v);
+
+        OPENSSL_free(val);
+    }
+    OPENSSL_free(val_sub);
+    return ret;
+}
+
+/*-
+ * ec_wNAF_precompute_mult()
+ * creates an EC_PRE_COMP object with preprecomputed multiples of the generator
+ * for use with wNAF splitting as implemented in ec_wNAF_mul().
+ *
+ * 'pre_comp->points' is an array of multiples of the generator
+ * of the following form:
+ * points[0] =     generator;
+ * points[1] = 3 * generator;
+ * ...
+ * points[2^(w-1)-1] =     (2^(w-1)-1) * generator;
+ * points[2^(w-1)]   =     2^blocksize * generator;
+ * points[2^(w-1)+1] = 3 * 2^blocksize * generator;
+ * ...
+ * points[2^(w-1)*(numblocks-1)-1] = (2^(w-1)) *  2^(blocksize*(numblocks-2)) * generator
+ * points[2^(w-1)*(numblocks-1)]   =              2^(blocksize*(numblocks-1)) * generator
+ * ...
+ * points[2^(w-1)*numblocks-1]     = (2^(w-1)) *  2^(blocksize*(numblocks-1)) * generator
+ * points[2^(w-1)*numblocks]       = NULL
+ */
+int ec_wNAF_precompute_mult(EC_GROUP *group, BN_CTX *ctx)
+{
+    const EC_POINT *generator;
+    EC_POINT *tmp_point = NULL, *base = NULL, **var;
+    BN_CTX *new_ctx = NULL;
+    const BIGNUM *order;
+    size_t i, bits, w, pre_points_per_block, blocksize, numblocks, num;
+    EC_POINT **points = NULL;
+    EC_PRE_COMP *pre_comp;
+    int ret = 0;
+
+    /* if there is an old EC_PRE_COMP object, throw it away */
+    EC_pre_comp_free(group);
+    if ((pre_comp = ec_pre_comp_new(group)) == NULL)
+        return 0;
+
+    generator = EC_GROUP_get0_generator(group);
+    if (generator == NULL) {
+        ECerr(EC_F_EC_WNAF_PRECOMPUTE_MULT, EC_R_UNDEFINED_GENERATOR);
+        goto err;
+    }
+
+    if (ctx == NULL) {
+        ctx = new_ctx = BN_CTX_new();
+        if (ctx == NULL)
+            goto err;
+    }
+
+    BN_CTX_start(ctx);
+
+    order = EC_GROUP_get0_order(group);
+    if (order == NULL)
+        goto err;
+    if (BN_is_zero(order)) {
+        ECerr(EC_F_EC_WNAF_PRECOMPUTE_MULT, EC_R_UNKNOWN_ORDER);
+        goto err;
+    }
+
+    bits = BN_num_bits(order);
+    /*
+     * The following parameters mean we precompute (approximately) one point
+     * per bit. TBD: The combination 8, 4 is perfect for 160 bits; for other
+     * bit lengths, other parameter combinations might provide better
+     * efficiency.
+     */
+    blocksize = 8;
+    w = 4;
+    if (EC_window_bits_for_scalar_size(bits) > w) {
+        /* let's not make the window too small ... */
+        w = EC_window_bits_for_scalar_size(bits);
+    }
+
+    numblocks = (bits + blocksize - 1) / blocksize; /* max. number of blocks
+                                                     * to use for wNAF
+                                                     * splitting */
+
+    pre_points_per_block = (size_t)1 << (w - 1);
+    num = pre_points_per_block * numblocks; /* number of points to compute
+                                             * and store */
+
+    points = OPENSSL_malloc(sizeof(*points) * (num + 1));
+    if (points == NULL) {
+        ECerr(EC_F_EC_WNAF_PRECOMPUTE_MULT, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+
+    var = points;
+    var[num] = NULL;            /* pivot */
+    for (i = 0; i < num; i++) {
+        if ((var[i] = EC_POINT_new(group)) == NULL) {
+            ECerr(EC_F_EC_WNAF_PRECOMPUTE_MULT, ERR_R_MALLOC_FAILURE);
+            goto err;
+        }
+    }
+
+    if ((tmp_point = EC_POINT_new(group)) == NULL
+        || (base = EC_POINT_new(group)) == NULL) {
+        ECerr(EC_F_EC_WNAF_PRECOMPUTE_MULT, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+
+    if (!EC_POINT_copy(base, generator))
+        goto err;
+
+    /* do the precomputation */
+    for (i = 0; i < numblocks; i++) {
+        size_t j;
+
+        if (!EC_POINT_dbl(group, tmp_point, base, ctx))
+            goto err;
+
+        if (!EC_POINT_copy(*var++, base))
+            goto err;
+
+        for (j = 1; j < pre_points_per_block; j++, var++) {
+            /*
+             * calculate odd multiples of the current base point
+             */
+            if (!EC_POINT_add(group, *var, tmp_point, *(var - 1), ctx))
+                goto err;
+        }
+
+        if (i < numblocks - 1) {
+            /*
+             * get the next base (multiply current one by 2^blocksize)
+             */
+            size_t k;
+
+            if (blocksize <= 2) {
+                ECerr(EC_F_EC_WNAF_PRECOMPUTE_MULT, ERR_R_INTERNAL_ERROR);
+                goto err;
+            }
+
+            if (!EC_POINT_dbl(group, base, tmp_point, ctx))
+                goto err;
+            for (k = 2; k < blocksize; k++) {
+                if (!EC_POINT_dbl(group, base, base, ctx))
+                    goto err;
+            }
+        }
+    }
+
+    if (!EC_POINTs_make_affine(group, num, points, ctx))
+        goto err;
+
+    pre_comp->group = group;
+    pre_comp->blocksize = blocksize;
+    pre_comp->numblocks = numblocks;
+    pre_comp->w = w;
+    pre_comp->points = points;
+    points = NULL;
+    pre_comp->num = num;
+    SETPRECOMP(group, ec, pre_comp);
+    pre_comp = NULL;
+    ret = 1;
+
+ err:
+    if (ctx != NULL)
+        BN_CTX_end(ctx);
+    BN_CTX_free(new_ctx);
+    EC_ec_pre_comp_free(pre_comp);
+    if (points) {
+        EC_POINT **p;
+
+        for (p = points; *p != NULL; p++)
+            EC_POINT_free(*p);
+        OPENSSL_free(points);
+    }
+    EC_POINT_free(tmp_point);
+    EC_POINT_free(base);
+    return ret;
+}
+
+int ec_wNAF_have_precompute_mult(const EC_GROUP *group)
+{
+    return HAVEPRECOMP(group, ec);
+}
diff --git a/crypto/ec/ec_oct.c b/crypto/ec/ec_oct.c
new file mode 100644
index 000000000..effc42a34
--- /dev/null
+++ b/crypto/ec/ec_oct.c
@@ -0,0 +1,165 @@
+/*
+ * Copyright 2011-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/* ====================================================================
+ * Copyright 2002 Sun Microsystems, Inc. ALL RIGHTS RESERVED.
+ * Binary polynomial ECC support in OpenSSL originally developed by
+ * SUN MICROSYSTEMS, INC., and contributed to the OpenSSL project.
+ */
+
+#include 
+
+#include 
+#include 
+
+#include "ec_lcl.h"
+
+int EC_POINT_set_compressed_coordinates_GFp(const EC_GROUP *group,
+                                            EC_POINT *point, const BIGNUM *x,
+                                            int y_bit, BN_CTX *ctx)
+{
+    if (group->meth->point_set_compressed_coordinates == 0
+        && !(group->meth->flags & EC_FLAGS_DEFAULT_OCT)) {
+        ECerr(EC_F_EC_POINT_SET_COMPRESSED_COORDINATES_GFP,
+              ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
+        return 0;
+    }
+    if (group->meth != point->meth) {
+        ECerr(EC_F_EC_POINT_SET_COMPRESSED_COORDINATES_GFP,
+              EC_R_INCOMPATIBLE_OBJECTS);
+        return 0;
+    }
+    if (group->meth->flags & EC_FLAGS_DEFAULT_OCT) {
+        if (group->meth->field_type == NID_X9_62_prime_field)
+            return ec_GFp_simple_set_compressed_coordinates(group, point, x,
+                                                            y_bit, ctx);
+        else
+#ifdef OPENSSL_NO_EC2M
+        {
+            ECerr(EC_F_EC_POINT_SET_COMPRESSED_COORDINATES_GFP,
+                  EC_R_GF2M_NOT_SUPPORTED);
+            return 0;
+        }
+#else
+            return ec_GF2m_simple_set_compressed_coordinates(group, point, x,
+                                                             y_bit, ctx);
+#endif
+    }
+    return group->meth->point_set_compressed_coordinates(group, point, x,
+                                                         y_bit, ctx);
+}
+
+#ifndef OPENSSL_NO_EC2M
+int EC_POINT_set_compressed_coordinates_GF2m(const EC_GROUP *group,
+                                             EC_POINT *point, const BIGNUM *x,
+                                             int y_bit, BN_CTX *ctx)
+{
+    if (group->meth->point_set_compressed_coordinates == 0
+        && !(group->meth->flags & EC_FLAGS_DEFAULT_OCT)) {
+        ECerr(EC_F_EC_POINT_SET_COMPRESSED_COORDINATES_GF2M,
+              ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
+        return 0;
+    }
+    if (group->meth != point->meth) {
+        ECerr(EC_F_EC_POINT_SET_COMPRESSED_COORDINATES_GF2M,
+              EC_R_INCOMPATIBLE_OBJECTS);
+        return 0;
+    }
+    if (group->meth->flags & EC_FLAGS_DEFAULT_OCT) {
+        if (group->meth->field_type == NID_X9_62_prime_field)
+            return ec_GFp_simple_set_compressed_coordinates(group, point, x,
+                                                            y_bit, ctx);
+        else
+            return ec_GF2m_simple_set_compressed_coordinates(group, point, x,
+                                                             y_bit, ctx);
+    }
+    return group->meth->point_set_compressed_coordinates(group, point, x,
+                                                         y_bit, ctx);
+}
+#endif
+
+size_t EC_POINT_point2oct(const EC_GROUP *group, const EC_POINT *point,
+                          point_conversion_form_t form, unsigned char *buf,
+                          size_t len, BN_CTX *ctx)
+{
+    if (group->meth->point2oct == 0
+        && !(group->meth->flags & EC_FLAGS_DEFAULT_OCT)) {
+        ECerr(EC_F_EC_POINT_POINT2OCT, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
+        return 0;
+    }
+    if (group->meth != point->meth) {
+        ECerr(EC_F_EC_POINT_POINT2OCT, EC_R_INCOMPATIBLE_OBJECTS);
+        return 0;
+    }
+    if (group->meth->flags & EC_FLAGS_DEFAULT_OCT) {
+        if (group->meth->field_type == NID_X9_62_prime_field)
+            return ec_GFp_simple_point2oct(group, point, form, buf, len, ctx);
+        else
+#ifdef OPENSSL_NO_EC2M
+        {
+            ECerr(EC_F_EC_POINT_POINT2OCT, EC_R_GF2M_NOT_SUPPORTED);
+            return 0;
+        }
+#else
+            return ec_GF2m_simple_point2oct(group, point,
+                                            form, buf, len, ctx);
+#endif
+    }
+
+    return group->meth->point2oct(group, point, form, buf, len, ctx);
+}
+
+int EC_POINT_oct2point(const EC_GROUP *group, EC_POINT *point,
+                       const unsigned char *buf, size_t len, BN_CTX *ctx)
+{
+    if (group->meth->oct2point == 0
+        && !(group->meth->flags & EC_FLAGS_DEFAULT_OCT)) {
+        ECerr(EC_F_EC_POINT_OCT2POINT, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
+        return 0;
+    }
+    if (group->meth != point->meth) {
+        ECerr(EC_F_EC_POINT_OCT2POINT, EC_R_INCOMPATIBLE_OBJECTS);
+        return 0;
+    }
+    if (group->meth->flags & EC_FLAGS_DEFAULT_OCT) {
+        if (group->meth->field_type == NID_X9_62_prime_field)
+            return ec_GFp_simple_oct2point(group, point, buf, len, ctx);
+        else
+#ifdef OPENSSL_NO_EC2M
+        {
+            ECerr(EC_F_EC_POINT_OCT2POINT, EC_R_GF2M_NOT_SUPPORTED);
+            return 0;
+        }
+#else
+            return ec_GF2m_simple_oct2point(group, point, buf, len, ctx);
+#endif
+    }
+    return group->meth->oct2point(group, point, buf, len, ctx);
+}
+
+size_t EC_POINT_point2buf(const EC_GROUP *group, const EC_POINT *point,
+                          point_conversion_form_t form,
+                          unsigned char **pbuf, BN_CTX *ctx)
+{
+    size_t len;
+    unsigned char *buf;
+    len = EC_POINT_point2oct(group, point, form, NULL, 0, NULL);
+    if (len == 0)
+        return 0;
+    buf = OPENSSL_malloc(len);
+    if (buf == NULL)
+        return 0;
+    len = EC_POINT_point2oct(group, point, form, buf, len, ctx);
+    if (len == 0) {
+        OPENSSL_free(buf);
+        return 0;
+    }
+    *pbuf = buf;
+    return len;
+}
diff --git a/crypto/ec/ec_pmeth.c b/crypto/ec/ec_pmeth.c
new file mode 100644
index 000000000..36981fc2c
--- /dev/null
+++ b/crypto/ec/ec_pmeth.c
@@ -0,0 +1,751 @@
+/* ====================================================================
+ * Copyright (c) 2016 - 2019 The GmSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the GmSSL Project.
+ *    (http://gmssl.org/)"
+ *
+ * 4. The name "GmSSL Project" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    guanzhi1980@gmail.com.
+ *
+ * 5. Products derived from this software may not be called "GmSSL"
+ *    nor may "GmSSL" appear in their names without prior written
+ *    permission of the GmSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the GmSSL Project
+ *    (http://gmssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE GmSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE GmSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+/*
+ * Copyright 2006-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+#include 
+#include "ec_lcl.h"
+#include 
+#include "internal/evp_int.h"
+#ifndef OPENSSL_NO_SM2
+# include 
+# include 
+#endif
+
+/* EC pkey context structure */
+
+typedef struct {
+    /* Key and paramgen group */
+    EC_GROUP *gen_group;
+    /* message digest */
+    const EVP_MD *md;
+    /* Duplicate key if custom cofactor needed */
+    EC_KEY *co_key;
+    /* Cofactor mode */
+    signed char cofactor_mode;
+    /* KDF (if any) to use for ECDH */
+    char kdf_type;
+    /* Message digest to use for key derivation */
+    const EVP_MD *kdf_md;
+    /* User key material */
+    unsigned char *kdf_ukm;
+    size_t kdf_ukmlen;
+    /* KDF output length */
+    size_t kdf_outlen;
+#ifndef OPENSSL_NO_SM2
+    int ec_scheme;
+    char *signer_id;
+    unsigned char *signer_zid;
+    int ec_encrypt_param;
+#endif
+} EC_PKEY_CTX;
+
+static int pkey_ec_init(EVP_PKEY_CTX *ctx)
+{
+    EC_PKEY_CTX *dctx;
+
+    dctx = OPENSSL_zalloc(sizeof(*dctx));
+    if (dctx == NULL)
+        return 0;
+
+    dctx->cofactor_mode = -1;
+    dctx->kdf_type = EVP_PKEY_ECDH_KDF_NONE;
+#ifndef OPENSSL_NO_SM2
+    dctx->ec_scheme = NID_secg_scheme;
+    dctx->signer_id = NULL;
+    dctx->signer_zid = NULL;
+    dctx->ec_encrypt_param = NID_undef;
+#endif
+    ctx->data = dctx;
+    return 1;
+}
+
+static int pkey_ec_copy(EVP_PKEY_CTX *dst, EVP_PKEY_CTX *src)
+{
+    EC_PKEY_CTX *dctx, *sctx;
+    if (!pkey_ec_init(dst))
+        return 0;
+    sctx = src->data;
+    dctx = dst->data;
+    if (sctx->gen_group) {
+        dctx->gen_group = EC_GROUP_dup(sctx->gen_group);
+        if (!dctx->gen_group)
+            return 0;
+    }
+    dctx->md = sctx->md;
+
+    if (sctx->co_key) {
+        dctx->co_key = EC_KEY_dup(sctx->co_key);
+        if (!dctx->co_key)
+            return 0;
+    }
+    dctx->kdf_type = sctx->kdf_type;
+    dctx->kdf_md = sctx->kdf_md;
+    dctx->kdf_outlen = sctx->kdf_outlen;
+    if (sctx->kdf_ukm) {
+        dctx->kdf_ukm = OPENSSL_memdup(sctx->kdf_ukm, sctx->kdf_ukmlen);
+        if (!dctx->kdf_ukm)
+            return 0;
+    } else
+        dctx->kdf_ukm = NULL;
+    dctx->kdf_ukmlen = sctx->kdf_ukmlen;
+#ifndef OPENSSL_NO_SM2
+    dctx->ec_scheme = sctx->ec_scheme;
+    if (sctx->signer_id) {
+        dctx->signer_id = OPENSSL_strdup(sctx->signer_id);
+        if (!dctx->signer_id)
+            return 0;
+    }
+    dctx->signer_zid = NULL;
+    dctx->ec_encrypt_param = sctx->ec_encrypt_param;
+#endif
+    return 1;
+}
+
+static void pkey_ec_cleanup(EVP_PKEY_CTX *ctx)
+{
+    EC_PKEY_CTX *dctx = ctx->data;
+    if (dctx) {
+        EC_GROUP_free(dctx->gen_group);
+        EC_KEY_free(dctx->co_key);
+        OPENSSL_free(dctx->kdf_ukm);
+#ifndef OPENSSL_NO_SM2
+        OPENSSL_free(dctx->signer_id);
+        OPENSSL_free(dctx->signer_zid);
+#endif
+        OPENSSL_free(dctx);
+    }
+}
+
+static int pkey_ec_sign(EVP_PKEY_CTX *ctx, unsigned char *sig, size_t *siglen,
+                        const unsigned char *tbs, size_t tbslen)
+{
+    int ret, type;
+    unsigned int sltmp;
+    EC_PKEY_CTX *dctx = ctx->data;
+    EC_KEY *ec = ctx->pkey->pkey.ec;
+
+    if (!sig) {
+        *siglen = ECDSA_size(ec);
+        return 1;
+    } else if (*siglen < (size_t)ECDSA_size(ec)) {
+        ECerr(EC_F_PKEY_EC_SIGN, EC_R_BUFFER_TOO_SMALL);
+        return 0;
+    }
+
+    if (dctx->md)
+        type = EVP_MD_type(dctx->md);
+    else
+        type = NID_sha1;
+
+#ifndef OPENSSL_NO_SM2
+    if (dctx->ec_scheme == NID_sm_scheme)
+        ret = SM2_sign(NID_undef, tbs, tbslen, sig, &sltmp, ec);
+    else
+#endif
+
+    ret = ECDSA_sign(type, tbs, tbslen, sig, &sltmp, ec);
+
+    if (ret <= 0)
+        return ret;
+    *siglen = (size_t)sltmp;
+    return 1;
+}
+
+static int pkey_ec_verify(EVP_PKEY_CTX *ctx,
+                          const unsigned char *sig, size_t siglen,
+                          const unsigned char *tbs, size_t tbslen)
+{
+    int ret, type;
+    EC_PKEY_CTX *dctx = ctx->data;
+    EC_KEY *ec = ctx->pkey->pkey.ec;
+
+    if (dctx->md)
+        type = EVP_MD_type(dctx->md);
+    else
+        type = NID_sha1;
+
+#ifndef OPENSSL_NO_SM2
+    if (dctx->ec_scheme == NID_sm_scheme)
+        ret = SM2_verify(NID_undef, tbs, tbslen, sig, siglen, ec);
+    else
+#endif
+
+    ret = ECDSA_verify(type, tbs, tbslen, sig, siglen, ec);
+
+    return ret;
+}
+
+#ifndef OPENSSL_NO_SM2
+static int pkey_ec_encrypt(EVP_PKEY_CTX *ctx, unsigned char *out, size_t *outlen,
+    const unsigned char *in, size_t inlen)
+{
+    EC_PKEY_CTX *dctx = ctx->data;
+    EC_KEY *ec_key = ctx->pkey->pkey.ec;
+
+    switch (dctx->ec_scheme) {
+    case NID_sm_scheme:
+        if (!SM2_encrypt(dctx->ec_encrypt_param, in, inlen, out, outlen, ec_key)) {
+            ECerr(EC_F_PKEY_EC_ENCRYPT, EC_R_SM2_ENCRYPT_FAILED);
+            return 0;
+        }
+        break;
+    case NID_secg_scheme:
+        if (!ECIES_encrypt(dctx->ec_encrypt_param, in, inlen, out, outlen, ec_key)) {
+            ECerr(EC_F_PKEY_EC_ENCRYPT, EC_R_ECIES_ENCRYPT_FAILED);
+            return 0;
+        }
+        break;
+    default:
+        ECerr(EC_F_PKEY_EC_ENCRYPT, EC_R_INVALID_ENC_TYPE);
+        return 0;
+    }
+
+    return 1;
+}
+
+static int pkey_ec_decrypt(EVP_PKEY_CTX *ctx, unsigned char *out, size_t *outlen,
+    const unsigned char *in, size_t inlen)
+{
+    EC_PKEY_CTX *dctx = ctx->data;
+    EC_KEY *ec_key = ctx->pkey->pkey.ec;
+
+    switch (dctx->ec_scheme) {
+    case  NID_sm_scheme:
+        if (!SM2_decrypt(dctx->ec_encrypt_param, in, inlen, out, outlen, ec_key)) {
+            ECerr(EC_F_PKEY_EC_DECRYPT, EC_R_SM2_DECRYPT_FAILED);
+            return 0;
+        }
+        break;
+    case NID_secg_scheme:
+        if (!ECIES_decrypt(dctx->ec_encrypt_param, in, inlen, out, outlen, ec_key)) {
+            ECerr(EC_F_PKEY_EC_DECRYPT, EC_R_ECIES_DECRYPT_FAILED);
+            return 0;
+        }
+        break;
+
+    default:
+        ECerr(EC_F_PKEY_EC_DECRYPT, EC_R_INVALID_ENC_TYPE);
+        return 0;
+    }
+
+    return 1;
+}
+#endif
+
+#ifndef OPENSSL_NO_EC
+static int pkey_ec_derive(EVP_PKEY_CTX *ctx, unsigned char *key,
+                          size_t *keylen)
+{
+    int ret;
+    size_t outlen;
+    const EC_POINT *pubkey = NULL;
+    EC_KEY *eckey;
+    EC_PKEY_CTX *dctx = ctx->data;
+    if (!ctx->pkey || !ctx->peerkey) {
+        ECerr(EC_F_PKEY_EC_DERIVE, EC_R_KEYS_NOT_SET);
+        return 0;
+    }
+
+    eckey = dctx->co_key ? dctx->co_key : ctx->pkey->pkey.ec;
+
+    if (!key) {
+        const EC_GROUP *group;
+        group = EC_KEY_get0_group(eckey);
+        *keylen = (EC_GROUP_get_degree(group) + 7) / 8;
+        return 1;
+    }
+    pubkey = EC_KEY_get0_public_key(ctx->peerkey->pkey.ec);
+
+    /*
+     * NB: unlike PKCS#3 DH, if *outlen is less than maximum size this is not
+     * an error, the result is truncated.
+     */
+
+    outlen = *keylen;
+
+#ifndef OPENSSL_NO_SM2
+                      
+/*
+    if (dctx->ec_scheme == NID_sm_scheme)
+        ret = SM2_compute_key(key, outlen, pubkey, eckey, 0);
+    else
+*/
+#endif
+
+    ret = ECDH_compute_key(key, outlen, pubkey, eckey, 0);
+    if (ret <= 0)
+        return 0;
+    *keylen = ret;
+    return 1;
+}
+
+static int pkey_ec_kdf_derive(EVP_PKEY_CTX *ctx,
+                              unsigned char *key, size_t *keylen)
+{
+    EC_PKEY_CTX *dctx = ctx->data;
+    unsigned char *ktmp = NULL;
+    size_t ktmplen;
+    int rv = 0;
+    if (dctx->kdf_type == EVP_PKEY_ECDH_KDF_NONE)
+        return pkey_ec_derive(ctx, key, keylen);
+    if (!key) {
+        *keylen = dctx->kdf_outlen;
+        return 1;
+    }
+    if (*keylen != dctx->kdf_outlen)
+        return 0;
+    if (!pkey_ec_derive(ctx, NULL, &ktmplen))
+        return 0;
+    ktmp = OPENSSL_malloc(ktmplen);
+    if (ktmp == NULL)
+        return 0;
+    if (!pkey_ec_derive(ctx, ktmp, &ktmplen))
+        goto err;
+    /* Do KDF stuff */
+    if (!ECDH_KDF_X9_62(key, *keylen, ktmp, ktmplen,
+                        dctx->kdf_ukm, dctx->kdf_ukmlen, dctx->kdf_md))
+        goto err;
+    rv = 1;
+
+ err:
+    OPENSSL_clear_free(ktmp, ktmplen);
+    return rv;
+}
+#endif
+
+static int pkey_ec_ctrl(EVP_PKEY_CTX *ctx, int type, int p1, void *p2)
+{
+    EC_PKEY_CTX *dctx = ctx->data;
+    EC_GROUP *group;
+    switch (type) {
+    case EVP_PKEY_CTRL_EC_PARAMGEN_CURVE_NID:
+        group = EC_GROUP_new_by_curve_name(p1);
+        if (group == NULL) {
+            ECerr(EC_F_PKEY_EC_CTRL, EC_R_INVALID_CURVE);
+            return 0;
+        }
+        EC_GROUP_free(dctx->gen_group);
+        dctx->gen_group = group;
+        return 1;
+
+    case EVP_PKEY_CTRL_EC_PARAM_ENC:
+        if (!dctx->gen_group) {
+            ECerr(EC_F_PKEY_EC_CTRL, EC_R_NO_PARAMETERS_SET);
+            return 0;
+        }
+        EC_GROUP_set_asn1_flag(dctx->gen_group, p1);
+        return 1;
+
+#ifndef OPENSSL_NO_EC
+    case EVP_PKEY_CTRL_EC_ECDH_COFACTOR:
+        if (p1 == -2) {
+            if (dctx->cofactor_mode != -1)
+                return dctx->cofactor_mode;
+            else {
+                EC_KEY *ec_key = ctx->pkey->pkey.ec;
+                return EC_KEY_get_flags(ec_key) & EC_FLAG_COFACTOR_ECDH ? 1 :
+                    0;
+            }
+        } else if (p1 < -1 || p1 > 1)
+            return -2;
+        dctx->cofactor_mode = p1;
+        if (p1 != -1) {
+            EC_KEY *ec_key = ctx->pkey->pkey.ec;
+            if (!ec_key->group)
+                return -2;
+            /* If cofactor is 1 cofactor mode does nothing */
+            if (BN_is_one(ec_key->group->cofactor))
+                return 1;
+            if (!dctx->co_key) {
+                dctx->co_key = EC_KEY_dup(ec_key);
+                if (!dctx->co_key)
+                    return 0;
+            }
+            if (p1)
+                EC_KEY_set_flags(dctx->co_key, EC_FLAG_COFACTOR_ECDH);
+            else
+                EC_KEY_clear_flags(dctx->co_key, EC_FLAG_COFACTOR_ECDH);
+        } else {
+            EC_KEY_free(dctx->co_key);
+            dctx->co_key = NULL;
+        }
+        return 1;
+#endif
+
+    case EVP_PKEY_CTRL_EC_KDF_TYPE:
+        if (p1 == -2)
+            return dctx->kdf_type;
+        if (p1 != EVP_PKEY_ECDH_KDF_NONE && p1 != EVP_PKEY_ECDH_KDF_X9_62)
+            return -2;
+        dctx->kdf_type = p1;
+        return 1;
+
+#ifndef OPENSSL_NO_SM2
+    case EVP_PKEY_CTRL_EC_SCHEME:
+        if (p1 == -2) {
+            return dctx->ec_scheme;
+        }
+        if (p1 != NID_secg_scheme && p1 != NID_sm_scheme) {
+            ECerr(EC_F_PKEY_EC_CTRL, EC_R_INVALID_EC_SCHEME);
+            return 0;
+        }
+        dctx->ec_scheme = p1;
+# ifdef SM2_DEBUG
+        fprintf(stderr, "[SM2_DEBUG] EVP_PKEY_CTX_set_ec_scheme(%s)\n",
+            p1 == NID_secg_scheme ? "NID_secg_scheme" : "NID_sm_scheme");
+# endif
+        return 1;
+
+    case EVP_PKEY_CTRL_SIGNER_ID:
+        if (!p2 || !strlen((char *)p2) || strlen((char *)p2) > SM2_MAX_ID_LENGTH) {
+            ECerr(EC_F_PKEY_EC_CTRL, EC_R_INVALID_SIGNER_ID);
+            return 0;
+        } else {
+            char *id = NULL;
+            if (!(id = OPENSSL_strdup((char *)p2))) {
+                ECerr(EC_F_PKEY_EC_CTRL, ERR_R_MALLOC_FAILURE);
+                return 0;
+            }
+            if (dctx->signer_id)
+                OPENSSL_free(dctx->signer_id);
+            dctx->signer_id = id;
+            if (dctx->ec_scheme == NID_sm_scheme) {
+                EC_KEY *ec_key = ctx->pkey->pkey.ec;
+                unsigned char zid[SM3_DIGEST_LENGTH];
+                size_t zidlen = SM3_DIGEST_LENGTH;
+                if (!SM2_compute_id_digest(EVP_sm3(), dctx->signer_id,
+                    strlen(dctx->signer_id), zid, &zidlen, ec_key)) {
+                    ECerr(EC_F_PKEY_EC_CTRL, ERR_R_SM2_LIB);
+                    return 0;
+                }
+                if (!dctx->signer_zid) {
+                    if (!(dctx->signer_zid = OPENSSL_malloc(zidlen))) {
+                        ECerr(EC_F_PKEY_EC_CTRL, ERR_R_MALLOC_FAILURE);
+                        return 0;
+                    }
+                }
+                memcpy(dctx->signer_zid, zid, zidlen);
+# ifdef SM2_DEBUG
+                fprintf(stderr, "[SM2_DEBUG] EVP_PKEY_CTX_set_signer_id(\"%s\")\n", id);
+# endif
+            }
+        }
+        return 1;
+
+    case EVP_PKEY_CTRL_GET_SIGNER_ID:
+        *(const char **)p2 = dctx->signer_id;
+        return 1;
+
+    case EVP_PKEY_CTRL_GET_SIGNER_ZID:
+        if (dctx->ec_scheme != NID_sm_scheme) {
+            *(const unsigned char **)p2 = NULL;
+            return -2;
+        }
+        if (!dctx->signer_zid) {
+            EC_KEY *ec_key = ctx->pkey->pkey.ec;
+            unsigned char *zid;
+            size_t zidlen = SM3_DIGEST_LENGTH;
+            if (!(zid = OPENSSL_malloc(zidlen))) {
+                ECerr(EC_F_PKEY_EC_CTRL, ERR_R_MALLOC_FAILURE);
+                return 0;
+            }
+            if (!SM2_compute_id_digest(EVP_sm3(), SM2_DEFAULT_ID,
+                SM2_DEFAULT_ID_LENGTH, zid, &zidlen, ec_key)) {
+                ECerr(EC_F_PKEY_EC_CTRL, ERR_R_SM2_LIB);
+                OPENSSL_free(zid);
+                return 0;
+            }
+            dctx->signer_zid = zid;
+# ifdef SM2_DEBUG
+            fprintf(stderr, "[SM2_DEBUG] EVP_PKEY_CTX_get_signer_zid() "
+                "init zid with default id\n");
+# endif
+        }
+        *(const unsigned char **)p2 = dctx->signer_zid;
+        return 1;
+
+    case EVP_PKEY_CTRL_EC_ENCRYPT_PARAM:
+        if (p1 == -2) {
+            return dctx->ec_encrypt_param;
+        }
+        dctx->ec_encrypt_param = p1;
+        return 1;
+#endif
+
+    case EVP_PKEY_CTRL_EC_KDF_MD:
+        dctx->kdf_md = p2;
+        return 1;
+
+    case EVP_PKEY_CTRL_GET_EC_KDF_MD:
+        *(const EVP_MD **)p2 = dctx->kdf_md;
+        return 1;
+
+    case EVP_PKEY_CTRL_EC_KDF_OUTLEN:
+        if (p1 <= 0)
+            return -2;
+        dctx->kdf_outlen = (size_t)p1;
+        return 1;
+
+    case EVP_PKEY_CTRL_GET_EC_KDF_OUTLEN:
+        *(int *)p2 = dctx->kdf_outlen;
+        return 1;
+
+    case EVP_PKEY_CTRL_EC_KDF_UKM:
+        OPENSSL_free(dctx->kdf_ukm);
+        dctx->kdf_ukm = p2;
+        if (p2)
+            dctx->kdf_ukmlen = p1;
+        else
+            dctx->kdf_ukmlen = 0;
+        return 1;
+
+    case EVP_PKEY_CTRL_GET_EC_KDF_UKM:
+        *(unsigned char **)p2 = dctx->kdf_ukm;
+        return dctx->kdf_ukmlen;
+
+    case EVP_PKEY_CTRL_MD:
+        if (EVP_MD_type((const EVP_MD *)p2) != NID_sha1 &&
+#ifndef OPENSSL_NO_SM3
+            EVP_MD_type((const EVP_MD *)p2) != NID_sm3 &&
+#endif
+            EVP_MD_type((const EVP_MD *)p2) != NID_ecdsa_with_SHA1 &&
+            EVP_MD_type((const EVP_MD *)p2) != NID_sha224 &&
+            EVP_MD_type((const EVP_MD *)p2) != NID_sha256 &&
+            EVP_MD_type((const EVP_MD *)p2) != NID_sha384 &&
+            EVP_MD_type((const EVP_MD *)p2) != NID_sha512) {
+            ECerr(EC_F_PKEY_EC_CTRL, EC_R_INVALID_DIGEST_TYPE);
+            return 0;
+        }
+        dctx->md = p2;
+        return 1;
+
+    case EVP_PKEY_CTRL_GET_MD:
+        *(const EVP_MD **)p2 = dctx->md;
+        return 1;
+
+    case EVP_PKEY_CTRL_PEER_KEY:
+        /* Default behaviour is OK */
+    case EVP_PKEY_CTRL_DIGESTINIT:
+    case EVP_PKEY_CTRL_PKCS7_SIGN:
+    case EVP_PKEY_CTRL_CMS_SIGN:
+        return 1;
+
+#ifndef OPENSSL_NO_SM2
+    case EVP_PKEY_CTRL_PKCS7_ENCRYPT:
+    case EVP_PKEY_CTRL_PKCS7_DECRYPT:
+    case EVP_PKEY_CTRL_CMS_DECRYPT:
+    case EVP_PKEY_CTRL_CMS_ENCRYPT:
+        return 1;
+#endif
+
+    default:
+        return -2;
+
+    }
+}
+
+static int pkey_ec_ctrl_str(EVP_PKEY_CTX *ctx,
+                            const char *type, const char *value)
+{
+    if (strcmp(type, "ec_paramgen_curve") == 0) {
+        int nid;
+        nid = EC_curve_nist2nid(value);
+        if (nid == NID_undef)
+            nid = OBJ_sn2nid(value);
+        if (nid == NID_undef)
+            nid = OBJ_ln2nid(value);
+        if (nid == NID_undef) {
+            ECerr(EC_F_PKEY_EC_CTRL_STR, EC_R_INVALID_CURVE);
+            return 0;
+        }
+        return EVP_PKEY_CTX_set_ec_paramgen_curve_nid(ctx, nid);
+#ifndef OPENSSL_NO_SM2
+    } else if (!strcmp(type, "ec_scheme")) {
+        int scheme;
+        if (!strcmp(value, "secg"))
+            scheme = NID_secg_scheme;
+        else if (!strcmp(value, "sm2"))
+            scheme = NID_sm_scheme;
+        else
+            return -2;
+        return EVP_PKEY_CTX_set_ec_scheme(ctx, scheme);
+    } else if (!strcmp(type, "signer_id")) {
+        return EVP_PKEY_CTX_set_signer_id(ctx, value);
+    } else if (!strcmp(type, "ec_encrypt_param")) {
+        int encrypt_param;
+        if (!(encrypt_param = OBJ_txt2nid(value))) {
+            ECerr(EC_F_PKEY_EC_CTRL_STR, EC_R_INVALID_EC_ENCRYPT_PARAM);
+            return 0;
+        }
+        return EVP_PKEY_CTX_set_ec_encrypt_param(ctx, encrypt_param);
+#endif
+    } else if (strcmp(type, "ec_param_enc") == 0) {
+        int param_enc;
+        if (strcmp(value, "explicit") == 0)
+            param_enc = 0;
+        else if (strcmp(value, "named_curve") == 0)
+            param_enc = OPENSSL_EC_NAMED_CURVE;
+        else
+            return -2;
+        return EVP_PKEY_CTX_set_ec_param_enc(ctx, param_enc);
+    } else if (strcmp(type, "ecdh_kdf_md") == 0) {
+        const EVP_MD *md;
+        if ((md = EVP_get_digestbyname(value)) == NULL) {
+            ECerr(EC_F_PKEY_EC_CTRL_STR, EC_R_INVALID_DIGEST);
+            return 0;
+        }
+        return EVP_PKEY_CTX_set_ecdh_kdf_md(ctx, md);
+    } else if (strcmp(type, "ecdh_cofactor_mode") == 0) {
+        int co_mode;
+        co_mode = atoi(value);
+        return EVP_PKEY_CTX_set_ecdh_cofactor_mode(ctx, co_mode);
+    }
+
+    return -2;
+}
+
+static int pkey_ec_paramgen(EVP_PKEY_CTX *ctx, EVP_PKEY *pkey)
+{
+    EC_KEY *ec = NULL;
+    EC_PKEY_CTX *dctx = ctx->data;
+    int ret = 0;
+    if (dctx->gen_group == NULL) {
+        ECerr(EC_F_PKEY_EC_PARAMGEN, EC_R_NO_PARAMETERS_SET);
+        return 0;
+    }
+    ec = EC_KEY_new();
+    if (ec == NULL)
+        return 0;
+    ret = EC_KEY_set_group(ec, dctx->gen_group);
+    if (ret)
+        EVP_PKEY_assign_EC_KEY(pkey, ec);
+    else
+        EC_KEY_free(ec);
+    return ret;
+}
+
+static int pkey_ec_keygen(EVP_PKEY_CTX *ctx, EVP_PKEY *pkey)
+{
+    EC_KEY *ec = NULL;
+    EC_PKEY_CTX *dctx = ctx->data;
+    if (ctx->pkey == NULL && dctx->gen_group == NULL) {
+        ECerr(EC_F_PKEY_EC_KEYGEN, EC_R_NO_PARAMETERS_SET);
+        return 0;
+    }
+    ec = EC_KEY_new();
+    if (!ec)
+        return 0;
+    EVP_PKEY_assign_EC_KEY(pkey, ec);
+    if (ctx->pkey) {
+        /* Note: if error return, pkey is freed by parent routine */
+        if (!EVP_PKEY_copy_parameters(pkey, ctx->pkey))
+            return 0;
+    } else {
+        if (!EC_KEY_set_group(ec, dctx->gen_group))
+            return 0;
+    }
+    return EC_KEY_generate_key(pkey->pkey.ec);
+}
+
+const EVP_PKEY_METHOD ec_pkey_meth = {
+    EVP_PKEY_EC,
+    0,
+    pkey_ec_init,
+    pkey_ec_copy,
+    pkey_ec_cleanup,
+
+    0,
+    pkey_ec_paramgen,
+
+    0,
+    pkey_ec_keygen,
+
+    0,
+    pkey_ec_sign,
+
+    0,
+    pkey_ec_verify,
+
+    0, 0,
+
+    0, 0, 0, 0,
+
+#ifndef OPENSSL_NO_SM2
+    0,
+    pkey_ec_encrypt,
+
+    0,
+    pkey_ec_decrypt,
+#else
+    0, 0,
+
+    0, 0,
+#endif
+
+    0,
+#ifndef OPENSSL_NO_EC
+    pkey_ec_kdf_derive,
+#else
+    0,
+#endif
+    pkey_ec_ctrl,
+    pkey_ec_ctrl_str
+};
diff --git a/crypto/ec/ec_print.c b/crypto/ec/ec_print.c
new file mode 100644
index 000000000..1afa2ce87
--- /dev/null
+++ b/crypto/ec/ec_print.c
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2002-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "ec_lcl.h"
+
+BIGNUM *EC_POINT_point2bn(const EC_GROUP *group,
+                          const EC_POINT *point,
+                          point_conversion_form_t form,
+                          BIGNUM *ret, BN_CTX *ctx)
+{
+    size_t buf_len = 0;
+    unsigned char *buf;
+
+    buf_len = EC_POINT_point2buf(group, point, form, &buf, ctx);
+
+    if (buf_len == 0)
+        return NULL;
+
+    ret = BN_bin2bn(buf, buf_len, ret);
+
+    OPENSSL_free(buf);
+
+    return ret;
+}
+
+EC_POINT *EC_POINT_bn2point(const EC_GROUP *group,
+                            const BIGNUM *bn, EC_POINT *point, BN_CTX *ctx)
+{
+    size_t buf_len = 0;
+    unsigned char *buf;
+    EC_POINT *ret;
+
+    if ((buf_len = BN_num_bytes(bn)) == 0)
+        return NULL;
+    buf = OPENSSL_malloc(buf_len);
+    if (buf == NULL)
+        return NULL;
+
+    if (!BN_bn2bin(bn, buf)) {
+        OPENSSL_free(buf);
+        return NULL;
+    }
+
+    if (point == NULL) {
+        if ((ret = EC_POINT_new(group)) == NULL) {
+            OPENSSL_free(buf);
+            return NULL;
+        }
+    } else
+        ret = point;
+
+    if (!EC_POINT_oct2point(group, ret, buf, buf_len, ctx)) {
+        if (ret != point)
+            EC_POINT_clear_free(ret);
+        OPENSSL_free(buf);
+        return NULL;
+    }
+
+    OPENSSL_free(buf);
+    return ret;
+}
+
+static const char *HEX_DIGITS = "0123456789ABCDEF";
+
+/* the return value must be freed (using OPENSSL_free()) */
+char *EC_POINT_point2hex(const EC_GROUP *group,
+                         const EC_POINT *point,
+                         point_conversion_form_t form, BN_CTX *ctx)
+{
+    char *ret, *p;
+    size_t buf_len = 0, i;
+    unsigned char *buf = NULL, *pbuf;
+
+    buf_len = EC_POINT_point2buf(group, point, form, &buf, ctx);
+
+    if (buf_len == 0)
+        return NULL;
+
+    ret = OPENSSL_malloc(buf_len * 2 + 2);
+    if (ret == NULL) {
+        OPENSSL_free(buf);
+        return NULL;
+    }
+    p = ret;
+    pbuf = buf;
+    for (i = buf_len; i > 0; i--) {
+        int v = (int)*(pbuf++);
+        *(p++) = HEX_DIGITS[v >> 4];
+        *(p++) = HEX_DIGITS[v & 0x0F];
+    }
+    *p = '\0';
+
+    OPENSSL_free(buf);
+
+    return ret;
+}
+
+EC_POINT *EC_POINT_hex2point(const EC_GROUP *group,
+                             const char *buf, EC_POINT *point, BN_CTX *ctx)
+{
+    EC_POINT *ret = NULL;
+    BIGNUM *tmp_bn = NULL;
+
+    if (!BN_hex2bn(&tmp_bn, buf))
+        return NULL;
+
+    ret = EC_POINT_bn2point(group, tmp_bn, point, ctx);
+
+    BN_clear_free(tmp_bn);
+
+    return ret;
+}
diff --git a/crypto/ec/ecdh_kdf.c b/crypto/ec/ecdh_kdf.c
new file mode 100644
index 000000000..d47486eb3
--- /dev/null
+++ b/crypto/ec/ecdh_kdf.c
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2015-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include 
+
+/* Key derivation function from X9.62/SECG */
+/* Way more than we will ever need */
+#define ECDH_KDF_MAX    (1 << 30)
+
+int ECDH_KDF_X9_62(unsigned char *out, size_t outlen,
+                   const unsigned char *Z, size_t Zlen,
+                   const unsigned char *sinfo, size_t sinfolen,
+                   const EVP_MD *md)
+{
+    EVP_MD_CTX *mctx = NULL;
+    int rv = 0;
+    unsigned int i;
+    size_t mdlen;
+    unsigned char ctr[4];
+    if (sinfolen > ECDH_KDF_MAX || outlen > ECDH_KDF_MAX
+        || Zlen > ECDH_KDF_MAX)
+        return 0;
+    mctx = EVP_MD_CTX_new();
+    if (mctx == NULL)
+        return 0;
+    mdlen = EVP_MD_size(md);
+    for (i = 1;; i++) {
+        unsigned char mtmp[EVP_MAX_MD_SIZE];
+        if (!EVP_DigestInit_ex(mctx, md, NULL))
+            goto err;
+        ctr[3] = i & 0xFF;
+        ctr[2] = (i >> 8) & 0xFF;
+        ctr[1] = (i >> 16) & 0xFF;
+        ctr[0] = (i >> 24) & 0xFF;
+        if (!EVP_DigestUpdate(mctx, Z, Zlen))
+            goto err;
+        if (!EVP_DigestUpdate(mctx, ctr, sizeof(ctr)))
+            goto err;
+        if (!EVP_DigestUpdate(mctx, sinfo, sinfolen))
+            goto err;
+        if (outlen >= mdlen) {
+            if (!EVP_DigestFinal(mctx, out, NULL))
+                goto err;
+            outlen -= mdlen;
+            if (outlen == 0)
+                break;
+            out += mdlen;
+        } else {
+            if (!EVP_DigestFinal(mctx, mtmp, NULL))
+                goto err;
+            memcpy(out, mtmp, outlen);
+            OPENSSL_cleanse(mtmp, mdlen);
+            break;
+        }
+    }
+    rv = 1;
+ err:
+    EVP_MD_CTX_free(mctx);
+    return rv;
+}
diff --git a/crypto/ec/ecdh_ossl.c b/crypto/ec/ecdh_ossl.c
new file mode 100644
index 000000000..caf65de8c
--- /dev/null
+++ b/crypto/ec/ecdh_ossl.c
@@ -0,0 +1,143 @@
+/*
+ * Copyright 2002-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/* ====================================================================
+ * Copyright 2002 Sun Microsystems, Inc. ALL RIGHTS RESERVED.
+ *
+ * The Elliptic Curve Public-Key Crypto Library (ECC Code) included
+ * herein is developed by SUN MICROSYSTEMS, INC., and is contributed
+ * to the OpenSSL project.
+ *
+ * The ECC Code is licensed pursuant to the OpenSSL open source
+ * license provided below.
+ *
+ * The ECDH software is originally written by Douglas Stebila of
+ * Sun Microsystems Laboratories.
+ *
+ */
+
+#include 
+#include 
+
+#include "internal/cryptlib.h"
+
+#include 
+#include 
+#include 
+#include 
+#include "ec_lcl.h"
+
+int ossl_ecdh_compute_key(unsigned char **psec, size_t *pseclen,
+                          const EC_POINT *pub_key, const EC_KEY *ecdh)
+{
+    if (ecdh->group->meth->ecdh_compute_key == NULL) {
+        ECerr(EC_F_OSSL_ECDH_COMPUTE_KEY, EC_R_CURVE_DOES_NOT_SUPPORT_ECDH);
+        return 0;
+    }
+
+    return ecdh->group->meth->ecdh_compute_key(psec, pseclen, pub_key, ecdh);
+}
+
+/*-
+ * This implementation is based on the following primitives in the IEEE 1363 standard:
+ *  - ECKAS-DH1
+ *  - ECSVDP-DH
+ */
+int ecdh_simple_compute_key(unsigned char **pout, size_t *poutlen,
+                            const EC_POINT *pub_key, const EC_KEY *ecdh)
+{
+    BN_CTX *ctx;
+    EC_POINT *tmp = NULL;
+    BIGNUM *x = NULL, *y = NULL;
+    const BIGNUM *priv_key;
+    const EC_GROUP *group;
+    int ret = 0;
+    size_t buflen, len;
+    unsigned char *buf = NULL;
+
+    if ((ctx = BN_CTX_new()) == NULL)
+        goto err;
+    BN_CTX_start(ctx);
+    x = BN_CTX_get(ctx);
+    y = BN_CTX_get(ctx);
+
+    priv_key = EC_KEY_get0_private_key(ecdh);
+    if (priv_key == NULL) {
+        ECerr(EC_F_ECDH_SIMPLE_COMPUTE_KEY, EC_R_NO_PRIVATE_VALUE);
+        goto err;
+    }
+
+    group = EC_KEY_get0_group(ecdh);
+
+    if (EC_KEY_get_flags(ecdh) & EC_FLAG_COFACTOR_ECDH) {
+        if (!EC_GROUP_get_cofactor(group, x, NULL) ||
+            !BN_mul(x, x, priv_key, ctx)) {
+            ECerr(EC_F_ECDH_SIMPLE_COMPUTE_KEY, ERR_R_MALLOC_FAILURE);
+            goto err;
+        }
+        priv_key = x;
+    }
+
+    if ((tmp = EC_POINT_new(group)) == NULL) {
+        ECerr(EC_F_ECDH_SIMPLE_COMPUTE_KEY, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+
+    if (!EC_POINT_mul(group, tmp, NULL, pub_key, priv_key, ctx)) {
+        ECerr(EC_F_ECDH_SIMPLE_COMPUTE_KEY, EC_R_POINT_ARITHMETIC_FAILURE);
+        goto err;
+    }
+
+    if (EC_METHOD_get_field_type(EC_GROUP_method_of(group)) ==
+        NID_X9_62_prime_field) {
+        if (!EC_POINT_get_affine_coordinates_GFp(group, tmp, x, y, ctx)) {
+            ECerr(EC_F_ECDH_SIMPLE_COMPUTE_KEY, EC_R_POINT_ARITHMETIC_FAILURE);
+            goto err;
+        }
+    }
+#ifndef OPENSSL_NO_EC2M
+    else {
+        if (!EC_POINT_get_affine_coordinates_GF2m(group, tmp, x, y, ctx)) {
+            ECerr(EC_F_ECDH_SIMPLE_COMPUTE_KEY, EC_R_POINT_ARITHMETIC_FAILURE);
+            goto err;
+        }
+    }
+#endif
+
+    buflen = (EC_GROUP_get_degree(group) + 7) / 8;
+    len = BN_num_bytes(x);
+    if (len > buflen) {
+        ECerr(EC_F_ECDH_SIMPLE_COMPUTE_KEY, ERR_R_INTERNAL_ERROR);
+        goto err;
+    }
+    if ((buf = OPENSSL_malloc(buflen)) == NULL) {
+        ECerr(EC_F_ECDH_SIMPLE_COMPUTE_KEY, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+
+    memset(buf, 0, buflen - len);
+    if (len != (size_t)BN_bn2bin(x, buf + buflen - len)) {
+        ECerr(EC_F_ECDH_SIMPLE_COMPUTE_KEY, ERR_R_BN_LIB);
+        goto err;
+    }
+
+    *pout = buf;
+    *poutlen = buflen;
+    buf = NULL;
+
+    ret = 1;
+
+ err:
+    EC_POINT_free(tmp);
+    if (ctx)
+        BN_CTX_end(ctx);
+    BN_CTX_free(ctx);
+    OPENSSL_free(buf);
+    return ret;
+}
diff --git a/crypto/ec/ecdsa_ossl.c b/crypto/ec/ecdsa_ossl.c
new file mode 100644
index 000000000..a483bdfa4
--- /dev/null
+++ b/crypto/ec/ecdsa_ossl.c
@@ -0,0 +1,487 @@
+/*
+ * Copyright 2002-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include "ec_lcl.h"
+
+int ossl_ecdsa_sign(int type, const unsigned char *dgst, int dlen,
+                    unsigned char *sig, unsigned int *siglen,
+                    const BIGNUM *kinv, const BIGNUM *r, EC_KEY *eckey)
+{
+    ECDSA_SIG *s;
+    RAND_seed(dgst, dlen);
+    s = ECDSA_do_sign_ex(dgst, dlen, kinv, r, eckey);
+    if (s == NULL) {
+        *siglen = 0;
+        return 0;
+    }
+    *siglen = i2d_ECDSA_SIG(s, &sig);
+    ECDSA_SIG_free(s);
+    return 1;
+}
+
+static int ecdsa_sign_setup(EC_KEY *eckey, BN_CTX *ctx_in,
+                            BIGNUM **kinvp, BIGNUM **rp,
+                            const unsigned char *dgst, int dlen)
+{
+    BN_CTX *ctx = NULL;
+    BIGNUM *k = NULL, *r = NULL, *X = NULL;
+    const BIGNUM *order;
+    EC_POINT *tmp_point = NULL;
+    const EC_GROUP *group;
+    int ret = 0;
+
+    if (eckey == NULL || (group = EC_KEY_get0_group(eckey)) == NULL) {
+        ECerr(EC_F_ECDSA_SIGN_SETUP, ERR_R_PASSED_NULL_PARAMETER);
+        return 0;
+    }
+
+    if (!EC_KEY_can_sign(eckey)) {
+        ECerr(EC_F_ECDSA_SIGN_SETUP, EC_R_CURVE_DOES_NOT_SUPPORT_SIGNING);
+        return 0;
+    }
+
+    if (ctx_in == NULL) {
+        if ((ctx = BN_CTX_new()) == NULL) {
+            ECerr(EC_F_ECDSA_SIGN_SETUP, ERR_R_MALLOC_FAILURE);
+            return 0;
+        }
+    } else
+        ctx = ctx_in;
+
+    k = BN_new();               /* this value is later returned in *kinvp */
+    r = BN_new();               /* this value is later returned in *rp */
+    X = BN_new();
+    if (k == NULL || r == NULL || X == NULL) {
+        ECerr(EC_F_ECDSA_SIGN_SETUP, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+    if ((tmp_point = EC_POINT_new(group)) == NULL) {
+        ECerr(EC_F_ECDSA_SIGN_SETUP, ERR_R_EC_LIB);
+        goto err;
+    }
+    order = EC_GROUP_get0_order(group);
+    if (order == NULL) {
+        ECerr(EC_F_ECDSA_SIGN_SETUP, ERR_R_EC_LIB);
+        goto err;
+    }
+
+    do {
+        /* get random k */
+        do
+            if (dgst != NULL) {
+                if (!BN_generate_dsa_nonce
+                    (k, order, EC_KEY_get0_private_key(eckey), dgst, dlen,
+                     ctx)) {
+                    ECerr(EC_F_ECDSA_SIGN_SETUP,
+                             EC_R_RANDOM_NUMBER_GENERATION_FAILED);
+                    goto err;
+                }
+            } else {
+                if (!BN_rand_range(k, order)) {
+                    ECerr(EC_F_ECDSA_SIGN_SETUP,
+                             EC_R_RANDOM_NUMBER_GENERATION_FAILED);
+                    goto err;
+                }
+            }
+        while (BN_is_zero(k));
+
+        /*
+         * We do not want timing information to leak the length of k, so we
+         * compute G*k using an equivalent scalar of fixed bit-length.
+         */
+
+        if (!BN_add(k, k, order))
+            goto err;
+        if (BN_num_bits(k) <= BN_num_bits(order))
+            if (!BN_add(k, k, order))
+                goto err;
+
+        /* compute r the x-coordinate of generator * k */
+        if (!EC_POINT_mul(group, tmp_point, k, NULL, NULL, ctx)) {
+            ECerr(EC_F_ECDSA_SIGN_SETUP, ERR_R_EC_LIB);
+            goto err;
+        }
+        if (EC_METHOD_get_field_type(EC_GROUP_method_of(group)) ==
+            NID_X9_62_prime_field) {
+            if (!EC_POINT_get_affine_coordinates_GFp
+                (group, tmp_point, X, NULL, ctx)) {
+                ECerr(EC_F_ECDSA_SIGN_SETUP, ERR_R_EC_LIB);
+                goto err;
+            }
+        }
+#ifndef OPENSSL_NO_EC2M
+        else {                  /* NID_X9_62_characteristic_two_field */
+
+            if (!EC_POINT_get_affine_coordinates_GF2m(group,
+                                                      tmp_point, X, NULL,
+                                                      ctx)) {
+                ECerr(EC_F_ECDSA_SIGN_SETUP, ERR_R_EC_LIB);
+                goto err;
+            }
+        }
+#endif
+        if (!BN_nnmod(r, X, order, ctx)) {
+            ECerr(EC_F_ECDSA_SIGN_SETUP, ERR_R_BN_LIB);
+            goto err;
+        }
+    }
+    while (BN_is_zero(r));
+
+    /* compute the inverse of k */
+    if (EC_GROUP_get_mont_data(group) != NULL) {
+        /*
+         * We want inverse in constant time, therefore we utilize the fact
+         * order must be prime and use Fermats Little Theorem instead.
+         */
+        if (!BN_set_word(X, 2)) {
+            ECerr(EC_F_ECDSA_SIGN_SETUP, ERR_R_BN_LIB);
+            goto err;
+        }
+        if (!BN_mod_sub(X, order, X, order, ctx)) {
+            ECerr(EC_F_ECDSA_SIGN_SETUP, ERR_R_BN_LIB);
+            goto err;
+        }
+        BN_set_flags(X, BN_FLG_CONSTTIME);
+        if (!BN_mod_exp_mont_consttime
+            (k, k, X, order, ctx, EC_GROUP_get_mont_data(group))) {
+            ECerr(EC_F_ECDSA_SIGN_SETUP, ERR_R_BN_LIB);
+            goto err;
+        }
+    } else {
+        if (!BN_mod_inverse(k, k, order, ctx)) {
+            ECerr(EC_F_ECDSA_SIGN_SETUP, ERR_R_BN_LIB);
+            goto err;
+        }
+    }
+
+    /* clear old values if necessary */
+    BN_clear_free(*rp);
+    BN_clear_free(*kinvp);
+    /* save the pre-computed values  */
+    *rp = r;
+    *kinvp = k;
+    ret = 1;
+ err:
+    if (!ret) {
+        BN_clear_free(k);
+        BN_clear_free(r);
+    }
+    if (ctx != ctx_in)
+        BN_CTX_free(ctx);
+    EC_POINT_free(tmp_point);
+    BN_clear_free(X);
+    return (ret);
+}
+
+int ossl_ecdsa_sign_setup(EC_KEY *eckey, BN_CTX *ctx_in, BIGNUM **kinvp,
+                          BIGNUM **rp)
+{
+    return ecdsa_sign_setup(eckey, ctx_in, kinvp, rp, NULL, 0);
+}
+
+ECDSA_SIG *ossl_ecdsa_sign_sig(const unsigned char *dgst, int dgst_len,
+                               const BIGNUM *in_kinv, const BIGNUM *in_r,
+                               EC_KEY *eckey)
+{
+    int ok = 0, i;
+    BIGNUM *kinv = NULL, *s, *m = NULL, *tmp = NULL;
+    const BIGNUM *order, *ckinv;
+    BN_CTX *ctx = NULL;
+    const EC_GROUP *group;
+    ECDSA_SIG *ret;
+    const BIGNUM *priv_key;
+
+    group = EC_KEY_get0_group(eckey);
+    priv_key = EC_KEY_get0_private_key(eckey);
+
+    if (group == NULL || priv_key == NULL) {
+        ECerr(EC_F_OSSL_ECDSA_SIGN_SIG, ERR_R_PASSED_NULL_PARAMETER);
+        return NULL;
+    }
+
+    if (!EC_KEY_can_sign(eckey)) {
+        ECerr(EC_F_OSSL_ECDSA_SIGN_SIG, EC_R_CURVE_DOES_NOT_SUPPORT_SIGNING);
+        return NULL;
+    }
+
+    ret = ECDSA_SIG_new();
+    if (ret == NULL) {
+        ECerr(EC_F_OSSL_ECDSA_SIGN_SIG, ERR_R_MALLOC_FAILURE);
+        return NULL;
+    }
+    ret->r = BN_new();
+    ret->s = BN_new();
+    if (ret->r == NULL || ret->s == NULL) {
+        ECerr(EC_F_OSSL_ECDSA_SIGN_SIG, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+    s = ret->s;
+
+    if ((ctx = BN_CTX_new()) == NULL ||
+        (tmp = BN_new()) == NULL || (m = BN_new()) == NULL) {
+        ECerr(EC_F_OSSL_ECDSA_SIGN_SIG, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+
+    order = EC_GROUP_get0_order(group);
+    if (order == NULL) {
+        ECerr(EC_F_OSSL_ECDSA_SIGN_SIG, ERR_R_EC_LIB);
+        goto err;
+    }
+    i = BN_num_bits(order);
+    /*
+     * Need to truncate digest if it is too long: first truncate whole bytes.
+     */
+    if (8 * dgst_len > i)
+        dgst_len = (i + 7) / 8;
+    if (!BN_bin2bn(dgst, dgst_len, m)) {
+        ECerr(EC_F_OSSL_ECDSA_SIGN_SIG, ERR_R_BN_LIB);
+        goto err;
+    }
+    /* If still too long truncate remaining bits with a shift */
+    if ((8 * dgst_len > i) && !BN_rshift(m, m, 8 - (i & 0x7))) {
+        ECerr(EC_F_OSSL_ECDSA_SIGN_SIG, ERR_R_BN_LIB);
+        goto err;
+    }
+    do {
+        if (in_kinv == NULL || in_r == NULL) {
+            if (!ecdsa_sign_setup(eckey, ctx, &kinv, &ret->r, dgst, dgst_len)) {
+                ECerr(EC_F_OSSL_ECDSA_SIGN_SIG, ERR_R_ECDSA_LIB);
+                goto err;
+            }
+            ckinv = kinv;
+        } else {
+            ckinv = in_kinv;
+            if (BN_copy(ret->r, in_r) == NULL) {
+                ECerr(EC_F_OSSL_ECDSA_SIGN_SIG, ERR_R_MALLOC_FAILURE);
+                goto err;
+            }
+        }
+
+        if (!BN_mod_mul(tmp, priv_key, ret->r, order, ctx)) {
+            ECerr(EC_F_OSSL_ECDSA_SIGN_SIG, ERR_R_BN_LIB);
+            goto err;
+        }
+        if (!BN_mod_add_quick(s, tmp, m, order)) {
+            ECerr(EC_F_OSSL_ECDSA_SIGN_SIG, ERR_R_BN_LIB);
+            goto err;
+        }
+        if (!BN_mod_mul(s, s, ckinv, order, ctx)) {
+            ECerr(EC_F_OSSL_ECDSA_SIGN_SIG, ERR_R_BN_LIB);
+            goto err;
+        }
+        if (BN_is_zero(s)) {
+            /*
+             * if kinv and r have been supplied by the caller don't to
+             * generate new kinv and r values
+             */
+            if (in_kinv != NULL && in_r != NULL) {
+                ECerr(EC_F_OSSL_ECDSA_SIGN_SIG, EC_R_NEED_NEW_SETUP_VALUES);
+                goto err;
+            }
+        } else
+            /* s != 0 => we have a valid signature */
+            break;
+    }
+    while (1);
+
+#if 0
+    if (!BN_rshift1(tmp, order)) {
+        ECerr(EC_F_OSSL_ECDSA_SIGN_SIG, ERR_R_BN_LIB);
+        goto err;
+    }
+    if (BN_cmp(ret->s, tmp) <= 0) {
+        if (!BN_sub(ret->s, order, ret->s)) {
+            ECerr(EC_F_OSSL_ECDSA_SIGN_SIG, ERR_R_BN_LIB);
+            goto err;
+        }
+    }
+#endif
+
+    ok = 1;
+ err:
+    if (!ok) {
+        ECDSA_SIG_free(ret);
+        ret = NULL;
+    }
+    BN_CTX_free(ctx);
+    BN_clear_free(m);
+    BN_clear_free(tmp);
+    BN_clear_free(kinv);
+    return ret;
+}
+
+/*-
+ * returns
+ *      1: correct signature
+ *      0: incorrect signature
+ *     -1: error
+ */
+int ossl_ecdsa_verify(int type, const unsigned char *dgst, int dgst_len,
+                      const unsigned char *sigbuf, int sig_len, EC_KEY *eckey)
+{
+    ECDSA_SIG *s;
+    const unsigned char *p = sigbuf;
+    unsigned char *der = NULL;
+    int derlen = -1;
+    int ret = -1;
+
+    s = ECDSA_SIG_new();
+    if (s == NULL)
+        return (ret);
+    if (d2i_ECDSA_SIG(&s, &p, sig_len) == NULL)
+        goto err;
+    /* Ensure signature uses DER and doesn't have trailing garbage */
+    derlen = i2d_ECDSA_SIG(s, &der);
+    if (derlen != sig_len || memcmp(sigbuf, der, derlen) != 0)
+        goto err;
+    ret = ECDSA_do_verify(dgst, dgst_len, s, eckey);
+ err:
+    OPENSSL_clear_free(der, derlen);
+    ECDSA_SIG_free(s);
+    return (ret);
+}
+
+int ossl_ecdsa_verify_sig(const unsigned char *dgst, int dgst_len,
+                          const ECDSA_SIG *sig, EC_KEY *eckey)
+{
+    int ret = -1, i;
+    BN_CTX *ctx;
+    const BIGNUM *order;
+    BIGNUM *u1, *u2, *m, *X;
+    EC_POINT *point = NULL;
+    const EC_GROUP *group;
+    const EC_POINT *pub_key;
+
+    /* check input values */
+    if (eckey == NULL || (group = EC_KEY_get0_group(eckey)) == NULL ||
+        (pub_key = EC_KEY_get0_public_key(eckey)) == NULL || sig == NULL) {
+        ECerr(EC_F_OSSL_ECDSA_VERIFY_SIG, EC_R_MISSING_PARAMETERS);
+        return -1;
+    }
+
+    if (!EC_KEY_can_sign(eckey)) {
+        ECerr(EC_F_OSSL_ECDSA_VERIFY_SIG, EC_R_CURVE_DOES_NOT_SUPPORT_SIGNING);
+        return -1;
+    }
+
+    ctx = BN_CTX_new();
+    if (ctx == NULL) {
+        ECerr(EC_F_OSSL_ECDSA_VERIFY_SIG, ERR_R_MALLOC_FAILURE);
+        return -1;
+    }
+    BN_CTX_start(ctx);
+    u1 = BN_CTX_get(ctx);
+    u2 = BN_CTX_get(ctx);
+    m = BN_CTX_get(ctx);
+    X = BN_CTX_get(ctx);
+    if (X == NULL) {
+        ECerr(EC_F_OSSL_ECDSA_VERIFY_SIG, ERR_R_BN_LIB);
+        goto err;
+    }
+
+    order = EC_GROUP_get0_order(group);
+    if (order == NULL) {
+        ECerr(EC_F_OSSL_ECDSA_VERIFY_SIG, ERR_R_EC_LIB);
+        goto err;
+    }
+
+    if (BN_is_zero(sig->r) || BN_is_negative(sig->r) ||
+        BN_ucmp(sig->r, order) >= 0 || BN_is_zero(sig->s) ||
+        BN_is_negative(sig->s) || BN_ucmp(sig->s, order) >= 0) {
+        ECerr(EC_F_OSSL_ECDSA_VERIFY_SIG, EC_R_BAD_SIGNATURE);
+        ret = 0;                /* signature is invalid */
+        goto err;
+    }
+#if 0
+    if (!BN_rshift1(m, order)) {
+        ECerr(EC_F_OSSL_ECDSA_VERIFY_SIG, ERR_R_BN_LIB);
+        goto err;
+    }
+    if (BN_cmp(sig->s, m) <= 0) {
+        ECerr(EC_F_OSSL_ECDSA_VERIFY_SIG, EC_R_BAD_SIGNATURE);
+        ret = 0;
+        goto err;
+    }
+#endif
+
+    /* calculate tmp1 = inv(S) mod order */
+    if (!BN_mod_inverse(u2, sig->s, order, ctx)) {
+        ECerr(EC_F_OSSL_ECDSA_VERIFY_SIG, ERR_R_BN_LIB);
+        goto err;
+    }
+    /* digest -> m */
+    i = BN_num_bits(order);
+    /*
+     * Need to truncate digest if it is too long: first truncate whole bytes.
+     */
+    if (8 * dgst_len > i)
+        dgst_len = (i + 7) / 8;
+    if (!BN_bin2bn(dgst, dgst_len, m)) {
+        ECerr(EC_F_OSSL_ECDSA_VERIFY_SIG, ERR_R_BN_LIB);
+        goto err;
+    }
+    /* If still too long truncate remaining bits with a shift */
+    if ((8 * dgst_len > i) && !BN_rshift(m, m, 8 - (i & 0x7))) {
+        ECerr(EC_F_OSSL_ECDSA_VERIFY_SIG, ERR_R_BN_LIB);
+        goto err;
+    }
+    /* u1 = m * tmp mod order */
+    if (!BN_mod_mul(u1, m, u2, order, ctx)) {
+        ECerr(EC_F_OSSL_ECDSA_VERIFY_SIG, ERR_R_BN_LIB);
+        goto err;
+    }
+    /* u2 = r * w mod q */
+    if (!BN_mod_mul(u2, sig->r, u2, order, ctx)) {
+        ECerr(EC_F_OSSL_ECDSA_VERIFY_SIG, ERR_R_BN_LIB);
+        goto err;
+    }
+
+    if ((point = EC_POINT_new(group)) == NULL) {
+        ECerr(EC_F_OSSL_ECDSA_VERIFY_SIG, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+    if (!EC_POINT_mul(group, point, u1, pub_key, u2, ctx)) {
+        ECerr(EC_F_OSSL_ECDSA_VERIFY_SIG, ERR_R_EC_LIB);
+        goto err;
+    }
+    if (EC_METHOD_get_field_type(EC_GROUP_method_of(group)) ==
+        NID_X9_62_prime_field) {
+        if (!EC_POINT_get_affine_coordinates_GFp(group, point, X, NULL, ctx)) {
+            ECerr(EC_F_OSSL_ECDSA_VERIFY_SIG, ERR_R_EC_LIB);
+            goto err;
+        }
+    }
+#ifndef OPENSSL_NO_EC2M
+    else {                      /* NID_X9_62_characteristic_two_field */
+
+        if (!EC_POINT_get_affine_coordinates_GF2m(group, point, X, NULL, ctx)) {
+            ECerr(EC_F_OSSL_ECDSA_VERIFY_SIG, ERR_R_EC_LIB);
+            goto err;
+        }
+    }
+#endif
+    if (!BN_nnmod(u1, X, order, ctx)) {
+        ECerr(EC_F_OSSL_ECDSA_VERIFY_SIG, ERR_R_BN_LIB);
+        goto err;
+    }
+    /*  if the signature is correct u1 is equal to sig->r */
+    ret = (BN_ucmp(u1, sig->r) == 0);
+ err:
+    BN_CTX_end(ctx);
+    BN_CTX_free(ctx);
+    EC_POINT_free(point);
+    return ret;
+}
diff --git a/crypto/ec/ecdsa_sign.c b/crypto/ec/ecdsa_sign.c
new file mode 100644
index 000000000..aee06e991
--- /dev/null
+++ b/crypto/ec/ecdsa_sign.c
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2015-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "ec_lcl.h"
+#include 
+
+ECDSA_SIG *ECDSA_do_sign(const unsigned char *dgst, int dlen, EC_KEY *eckey)
+{
+    return ECDSA_do_sign_ex(dgst, dlen, NULL, NULL, eckey);
+}
+
+ECDSA_SIG *ECDSA_do_sign_ex(const unsigned char *dgst, int dlen,
+                            const BIGNUM *kinv, const BIGNUM *rp,
+                            EC_KEY *eckey)
+{
+    if (eckey->meth->sign_sig != NULL)
+        return eckey->meth->sign_sig(dgst, dlen, kinv, rp, eckey);
+    ECerr(EC_F_ECDSA_DO_SIGN_EX, EC_R_OPERATION_NOT_SUPPORTED);
+    return NULL;
+}
+
+int ECDSA_sign(int type, const unsigned char *dgst, int dlen, unsigned char
+               *sig, unsigned int *siglen, EC_KEY *eckey)
+{
+    return ECDSA_sign_ex(type, dgst, dlen, sig, siglen, NULL, NULL, eckey);
+}
+
+int ECDSA_sign_ex(int type, const unsigned char *dgst, int dlen,
+                  unsigned char *sig, unsigned int *siglen, const BIGNUM *kinv,
+                  const BIGNUM *r, EC_KEY *eckey)
+{
+    if (eckey->meth->sign != NULL)
+        return eckey->meth->sign(type, dgst, dlen, sig, siglen, kinv, r, eckey);
+    ECerr(EC_F_ECDSA_SIGN_EX, EC_R_OPERATION_NOT_SUPPORTED);
+    return 0;
+}
+
+int ECDSA_sign_setup(EC_KEY *eckey, BN_CTX *ctx_in, BIGNUM **kinvp,
+                     BIGNUM **rp)
+{
+    if (eckey->meth->sign_setup != NULL)
+        return eckey->meth->sign_setup(eckey, ctx_in, kinvp, rp);
+    ECerr(EC_F_ECDSA_SIGN_SETUP, EC_R_OPERATION_NOT_SUPPORTED);
+    return 0;
+}
diff --git a/crypto/ec/ecdsa_vrf.c b/crypto/ec/ecdsa_vrf.c
new file mode 100644
index 000000000..f61a20063
--- /dev/null
+++ b/crypto/ec/ecdsa_vrf.c
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2002-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "ec_lcl.h"
+#include 
+
+/*-
+ * returns
+ *      1: correct signature
+ *      0: incorrect signature
+ *     -1: error
+ */
+int ECDSA_do_verify(const unsigned char *dgst, int dgst_len,
+                    const ECDSA_SIG *sig, EC_KEY *eckey)
+{
+    if (eckey->meth->verify_sig != NULL)
+        return eckey->meth->verify_sig(dgst, dgst_len, sig, eckey);
+    ECerr(EC_F_ECDSA_DO_VERIFY, EC_R_OPERATION_NOT_SUPPORTED);
+    return 0;
+}
+
+/*-
+ * returns
+ *      1: correct signature
+ *      0: incorrect signature
+ *     -1: error
+ */
+int ECDSA_verify(int type, const unsigned char *dgst, int dgst_len,
+                 const unsigned char *sigbuf, int sig_len, EC_KEY *eckey)
+{
+    if (eckey->meth->verify != NULL)
+        return eckey->meth->verify(type, dgst, dgst_len, sigbuf, sig_len,
+                                   eckey);
+    ECerr(EC_F_ECDSA_VERIFY, EC_R_OPERATION_NOT_SUPPORTED);
+    return 0;
+}
diff --git a/crypto/ec/eck_prn.c b/crypto/ec/eck_prn.c
new file mode 100644
index 000000000..dd3f8579c
--- /dev/null
+++ b/crypto/ec/eck_prn.c
@@ -0,0 +1,273 @@
+/*
+ * Copyright 2006-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/* ====================================================================
+ * Copyright 2002 Sun Microsystems, Inc. ALL RIGHTS RESERVED.
+ * Portions originally developed by SUN MICROSYSTEMS, INC., and
+ * contributed to the OpenSSL project.
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+#include 
+
+#ifndef OPENSSL_NO_STDIO
+int ECPKParameters_print_fp(FILE *fp, const EC_GROUP *x, int off)
+{
+    BIO *b;
+    int ret;
+
+    if ((b = BIO_new(BIO_s_file())) == NULL) {
+        ECerr(EC_F_ECPKPARAMETERS_PRINT_FP, ERR_R_BUF_LIB);
+        return (0);
+    }
+    BIO_set_fp(b, fp, BIO_NOCLOSE);
+    ret = ECPKParameters_print(b, x, off);
+    BIO_free(b);
+    return (ret);
+}
+
+int EC_KEY_print_fp(FILE *fp, const EC_KEY *x, int off)
+{
+    BIO *b;
+    int ret;
+
+    if ((b = BIO_new(BIO_s_file())) == NULL) {
+        ECerr(EC_F_EC_KEY_PRINT_FP, ERR_R_BIO_LIB);
+        return (0);
+    }
+    BIO_set_fp(b, fp, BIO_NOCLOSE);
+    ret = EC_KEY_print(b, x, off);
+    BIO_free(b);
+    return (ret);
+}
+
+int ECParameters_print_fp(FILE *fp, const EC_KEY *x)
+{
+    BIO *b;
+    int ret;
+
+    if ((b = BIO_new(BIO_s_file())) == NULL) {
+        ECerr(EC_F_ECPARAMETERS_PRINT_FP, ERR_R_BIO_LIB);
+        return (0);
+    }
+    BIO_set_fp(b, fp, BIO_NOCLOSE);
+    ret = ECParameters_print(b, x);
+    BIO_free(b);
+    return (ret);
+}
+#endif
+
+static int print_bin(BIO *fp, const char *str, const unsigned char *num,
+                     size_t len, int off);
+
+int ECPKParameters_print(BIO *bp, const EC_GROUP *x, int off)
+{
+    int ret = 0, reason = ERR_R_BIO_LIB;
+    BN_CTX *ctx = NULL;
+    const EC_POINT *point = NULL;
+    BIGNUM *p = NULL, *a = NULL, *b = NULL, *gen = NULL;
+    const BIGNUM *order = NULL, *cofactor = NULL;
+    const unsigned char *seed;
+    size_t seed_len = 0;
+
+    static const char *gen_compressed = "Generator (compressed):";
+    static const char *gen_uncompressed = "Generator (uncompressed):";
+    static const char *gen_hybrid = "Generator (hybrid):";
+
+    if (!x) {
+        reason = ERR_R_PASSED_NULL_PARAMETER;
+        goto err;
+    }
+
+    ctx = BN_CTX_new();
+    if (ctx == NULL) {
+        reason = ERR_R_MALLOC_FAILURE;
+        goto err;
+    }
+
+    if (EC_GROUP_get_asn1_flag(x)) {
+        /* the curve parameter are given by an asn1 OID */
+        int nid;
+        const char *nname;
+
+        if (!BIO_indent(bp, off, 128))
+            goto err;
+
+        nid = EC_GROUP_get_curve_name(x);
+        if (nid == 0)
+            goto err;
+        if (BIO_printf(bp, "ASN1 OID: %s", OBJ_nid2sn(nid)) <= 0)
+            goto err;
+        if (BIO_printf(bp, "\n") <= 0)
+            goto err;
+        nname = EC_curve_nid2nist(nid);
+        if (nname) {
+            if (!BIO_indent(bp, off, 128))
+                goto err;
+            if (BIO_printf(bp, "NIST CURVE: %s\n", nname) <= 0)
+                goto err;
+        }
+    } else {
+        /* explicit parameters */
+        int is_char_two = 0;
+        point_conversion_form_t form;
+        int tmp_nid = EC_METHOD_get_field_type(EC_GROUP_method_of(x));
+
+        if (tmp_nid == NID_X9_62_characteristic_two_field)
+            is_char_two = 1;
+
+        if ((p = BN_new()) == NULL || (a = BN_new()) == NULL ||
+            (b = BN_new()) == NULL) {
+            reason = ERR_R_MALLOC_FAILURE;
+            goto err;
+        }
+#ifndef OPENSSL_NO_EC2M
+        if (is_char_two) {
+            if (!EC_GROUP_get_curve_GF2m(x, p, a, b, ctx)) {
+                reason = ERR_R_EC_LIB;
+                goto err;
+            }
+        } else                  /* prime field */
+#endif
+        {
+            if (!EC_GROUP_get_curve_GFp(x, p, a, b, ctx)) {
+                reason = ERR_R_EC_LIB;
+                goto err;
+            }
+        }
+
+        if ((point = EC_GROUP_get0_generator(x)) == NULL) {
+            reason = ERR_R_EC_LIB;
+            goto err;
+        }
+        order = EC_GROUP_get0_order(x);
+        cofactor = EC_GROUP_get0_cofactor(x);
+        if (order == NULL) {
+            reason = ERR_R_EC_LIB;
+            goto err;
+        }
+
+        form = EC_GROUP_get_point_conversion_form(x);
+
+        if ((gen = EC_POINT_point2bn(x, point, form, NULL, ctx)) == NULL) {
+            reason = ERR_R_EC_LIB;
+            goto err;
+        }
+
+        if ((seed = EC_GROUP_get0_seed(x)) != NULL)
+            seed_len = EC_GROUP_get_seed_len(x);
+
+        if (!BIO_indent(bp, off, 128))
+            goto err;
+
+        /* print the 'short name' of the field type */
+        if (BIO_printf(bp, "Field Type: %s\n", OBJ_nid2sn(tmp_nid))
+            <= 0)
+            goto err;
+
+        if (is_char_two) {
+            /* print the 'short name' of the base type OID */
+            int basis_type = EC_GROUP_get_basis_type(x);
+            if (basis_type == 0)
+                goto err;
+
+            if (!BIO_indent(bp, off, 128))
+                goto err;
+
+            if (BIO_printf(bp, "Basis Type: %s\n",
+                           OBJ_nid2sn(basis_type)) <= 0)
+                goto err;
+
+            /* print the polynomial */
+            if ((p != NULL) && !ASN1_bn_print(bp, "Polynomial:", p, NULL,
+                                              off))
+                goto err;
+        } else {
+            if ((p != NULL) && !ASN1_bn_print(bp, "Prime:", p, NULL, off))
+                goto err;
+        }
+        if ((a != NULL) && !ASN1_bn_print(bp, "A:   ", a, NULL, off))
+            goto err;
+        if ((b != NULL) && !ASN1_bn_print(bp, "B:   ", b, NULL, off))
+            goto err;
+        if (form == POINT_CONVERSION_COMPRESSED) {
+            if ((gen != NULL) && !ASN1_bn_print(bp, gen_compressed, gen,
+                                                NULL, off))
+                goto err;
+        } else if (form == POINT_CONVERSION_UNCOMPRESSED) {
+            if ((gen != NULL) && !ASN1_bn_print(bp, gen_uncompressed, gen,
+                                                NULL, off))
+                goto err;
+        } else {                /* form == POINT_CONVERSION_HYBRID */
+
+            if ((gen != NULL) && !ASN1_bn_print(bp, gen_hybrid, gen,
+                                                NULL, off))
+                goto err;
+        }
+        if ((order != NULL) && !ASN1_bn_print(bp, "Order: ", order,
+                                              NULL, off))
+            goto err;
+        if ((cofactor != NULL) && !ASN1_bn_print(bp, "Cofactor: ", cofactor,
+                                                 NULL, off))
+            goto err;
+        if (seed && !print_bin(bp, "Seed:", seed, seed_len, off))
+            goto err;
+    }
+    ret = 1;
+ err:
+    if (!ret)
+        ECerr(EC_F_ECPKPARAMETERS_PRINT, reason);
+    BN_free(p);
+    BN_free(a);
+    BN_free(b);
+    BN_free(gen);
+    BN_CTX_free(ctx);
+    return (ret);
+}
+
+static int print_bin(BIO *fp, const char *name, const unsigned char *buf,
+                     size_t len, int off)
+{
+    size_t i;
+    char str[128];
+
+    if (buf == NULL)
+        return 1;
+    if (off > 0) {
+        if (off > 128)
+            off = 128;
+        memset(str, ' ', off);
+        if (BIO_write(fp, str, off) <= 0)
+            return 0;
+    } else {
+        off = 0;
+    }
+
+    if (BIO_printf(fp, "%s", name) <= 0)
+        return 0;
+
+    for (i = 0; i < len; i++) {
+        if ((i % 15) == 0) {
+            str[0] = '\n';
+            memset(&(str[1]), ' ', off + 4);
+            if (BIO_write(fp, str, off + 1 + 4) <= 0)
+                return 0;
+        }
+        if (BIO_printf(fp, "%02x%s", buf[i], ((i + 1) == len) ? "" : ":") <=
+            0)
+            return 0;
+    }
+    if (BIO_write(fp, "\n", 1) <= 0)
+        return 0;
+
+    return 1;
+}
diff --git a/crypto/ec/ecp_mont.c b/crypto/ec/ecp_mont.c
new file mode 100644
index 000000000..994cc1d0f
--- /dev/null
+++ b/crypto/ec/ecp_mont.c
@@ -0,0 +1,242 @@
+/*
+ * Copyright 2001-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/* ====================================================================
+ * Copyright 2002 Sun Microsystems, Inc. ALL RIGHTS RESERVED.
+ * Portions of this software developed by SUN MICROSYSTEMS, INC.,
+ * and contributed to the OpenSSL project.
+ */
+
+#include 
+
+#include "ec_lcl.h"
+
+const EC_METHOD *EC_GFp_mont_method(void)
+{
+    static const EC_METHOD ret = {
+        EC_FLAGS_DEFAULT_OCT,
+        NID_X9_62_prime_field,
+        ec_GFp_mont_group_init,
+        ec_GFp_mont_group_finish,
+        ec_GFp_mont_group_clear_finish,
+        ec_GFp_mont_group_copy,
+        ec_GFp_mont_group_set_curve,
+        ec_GFp_simple_group_get_curve,
+        ec_GFp_simple_group_get_degree,
+        ec_group_simple_order_bits,
+        ec_GFp_simple_group_check_discriminant,
+        ec_GFp_simple_point_init,
+        ec_GFp_simple_point_finish,
+        ec_GFp_simple_point_clear_finish,
+        ec_GFp_simple_point_copy,
+        ec_GFp_simple_point_set_to_infinity,
+        ec_GFp_simple_set_Jprojective_coordinates_GFp,
+        ec_GFp_simple_get_Jprojective_coordinates_GFp,
+        ec_GFp_simple_point_set_affine_coordinates,
+        ec_GFp_simple_point_get_affine_coordinates,
+        0, 0, 0,
+        ec_GFp_simple_add,
+        ec_GFp_simple_dbl,
+        ec_GFp_simple_invert,
+        ec_GFp_simple_is_at_infinity,
+        ec_GFp_simple_is_on_curve,
+        ec_GFp_simple_cmp,
+        ec_GFp_simple_make_affine,
+        ec_GFp_simple_points_make_affine,
+        0 /* mul */ ,
+        0 /* precompute_mult */ ,
+        0 /* have_precompute_mult */ ,
+        ec_GFp_mont_field_mul,
+        ec_GFp_mont_field_sqr,
+        0 /* field_div */ ,
+        ec_GFp_mont_field_encode,
+        ec_GFp_mont_field_decode,
+        ec_GFp_mont_field_set_to_one,
+        ec_key_simple_priv2oct,
+        ec_key_simple_oct2priv,
+        0, /* set private */
+        ec_key_simple_generate_key,
+        ec_key_simple_check_key,
+        ec_key_simple_generate_public_key,
+        0, /* keycopy */
+        0, /* keyfinish */
+        ecdh_simple_compute_key
+    };
+
+    return &ret;
+}
+
+int ec_GFp_mont_group_init(EC_GROUP *group)
+{
+    int ok;
+
+    ok = ec_GFp_simple_group_init(group);
+    group->field_data1 = NULL;
+    group->field_data2 = NULL;
+    return ok;
+}
+
+void ec_GFp_mont_group_finish(EC_GROUP *group)
+{
+    BN_MONT_CTX_free(group->field_data1);
+    group->field_data1 = NULL;
+    BN_free(group->field_data2);
+    group->field_data2 = NULL;
+    ec_GFp_simple_group_finish(group);
+}
+
+void ec_GFp_mont_group_clear_finish(EC_GROUP *group)
+{
+    BN_MONT_CTX_free(group->field_data1);
+    group->field_data1 = NULL;
+    BN_clear_free(group->field_data2);
+    group->field_data2 = NULL;
+    ec_GFp_simple_group_clear_finish(group);
+}
+
+int ec_GFp_mont_group_copy(EC_GROUP *dest, const EC_GROUP *src)
+{
+    BN_MONT_CTX_free(dest->field_data1);
+    dest->field_data1 = NULL;
+    BN_clear_free(dest->field_data2);
+    dest->field_data2 = NULL;
+
+    if (!ec_GFp_simple_group_copy(dest, src))
+        return 0;
+
+    if (src->field_data1 != NULL) {
+        dest->field_data1 = BN_MONT_CTX_new();
+        if (dest->field_data1 == NULL)
+            return 0;
+        if (!BN_MONT_CTX_copy(dest->field_data1, src->field_data1))
+            goto err;
+    }
+    if (src->field_data2 != NULL) {
+        dest->field_data2 = BN_dup(src->field_data2);
+        if (dest->field_data2 == NULL)
+            goto err;
+    }
+
+    return 1;
+
+ err:
+    BN_MONT_CTX_free(dest->field_data1);
+    dest->field_data1 = NULL;
+    return 0;
+}
+
+int ec_GFp_mont_group_set_curve(EC_GROUP *group, const BIGNUM *p,
+                                const BIGNUM *a, const BIGNUM *b, BN_CTX *ctx)
+{
+    BN_CTX *new_ctx = NULL;
+    BN_MONT_CTX *mont = NULL;
+    BIGNUM *one = NULL;
+    int ret = 0;
+
+    BN_MONT_CTX_free(group->field_data1);
+    group->field_data1 = NULL;
+    BN_free(group->field_data2);
+    group->field_data2 = NULL;
+
+    if (ctx == NULL) {
+        ctx = new_ctx = BN_CTX_new();
+        if (ctx == NULL)
+            return 0;
+    }
+
+    mont = BN_MONT_CTX_new();
+    if (mont == NULL)
+        goto err;
+    if (!BN_MONT_CTX_set(mont, p, ctx)) {
+        ECerr(EC_F_EC_GFP_MONT_GROUP_SET_CURVE, ERR_R_BN_LIB);
+        goto err;
+    }
+    one = BN_new();
+    if (one == NULL)
+        goto err;
+    if (!BN_to_montgomery(one, BN_value_one(), mont, ctx))
+        goto err;
+
+    group->field_data1 = mont;
+    mont = NULL;
+    group->field_data2 = one;
+    one = NULL;
+
+    ret = ec_GFp_simple_group_set_curve(group, p, a, b, ctx);
+
+    if (!ret) {
+        BN_MONT_CTX_free(group->field_data1);
+        group->field_data1 = NULL;
+        BN_free(group->field_data2);
+        group->field_data2 = NULL;
+    }
+
+ err:
+    BN_free(one);
+    BN_CTX_free(new_ctx);
+    BN_MONT_CTX_free(mont);
+    return ret;
+}
+
+int ec_GFp_mont_field_mul(const EC_GROUP *group, BIGNUM *r, const BIGNUM *a,
+                          const BIGNUM *b, BN_CTX *ctx)
+{
+    if (group->field_data1 == NULL) {
+        ECerr(EC_F_EC_GFP_MONT_FIELD_MUL, EC_R_NOT_INITIALIZED);
+        return 0;
+    }
+
+    return BN_mod_mul_montgomery(r, a, b, group->field_data1, ctx);
+}
+
+int ec_GFp_mont_field_sqr(const EC_GROUP *group, BIGNUM *r, const BIGNUM *a,
+                          BN_CTX *ctx)
+{
+    if (group->field_data1 == NULL) {
+        ECerr(EC_F_EC_GFP_MONT_FIELD_SQR, EC_R_NOT_INITIALIZED);
+        return 0;
+    }
+
+    return BN_mod_mul_montgomery(r, a, a, group->field_data1, ctx);
+}
+
+int ec_GFp_mont_field_encode(const EC_GROUP *group, BIGNUM *r,
+                             const BIGNUM *a, BN_CTX *ctx)
+{
+    if (group->field_data1 == NULL) {
+        ECerr(EC_F_EC_GFP_MONT_FIELD_ENCODE, EC_R_NOT_INITIALIZED);
+        return 0;
+    }
+
+    return BN_to_montgomery(r, a, (BN_MONT_CTX *)group->field_data1, ctx);
+}
+
+int ec_GFp_mont_field_decode(const EC_GROUP *group, BIGNUM *r,
+                             const BIGNUM *a, BN_CTX *ctx)
+{
+    if (group->field_data1 == NULL) {
+        ECerr(EC_F_EC_GFP_MONT_FIELD_DECODE, EC_R_NOT_INITIALIZED);
+        return 0;
+    }
+
+    return BN_from_montgomery(r, a, group->field_data1, ctx);
+}
+
+int ec_GFp_mont_field_set_to_one(const EC_GROUP *group, BIGNUM *r,
+                                 BN_CTX *ctx)
+{
+    if (group->field_data2 == NULL) {
+        ECerr(EC_F_EC_GFP_MONT_FIELD_SET_TO_ONE, EC_R_NOT_INITIALIZED);
+        return 0;
+    }
+
+    if (!BN_copy(r, group->field_data2))
+        return 0;
+    return 1;
+}
diff --git a/crypto/ec/ecp_nist.c b/crypto/ec/ecp_nist.c
new file mode 100644
index 000000000..615563bc3
--- /dev/null
+++ b/crypto/ec/ecp_nist.c
@@ -0,0 +1,167 @@
+/*
+ * Copyright 2001-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/* ====================================================================
+ * Copyright 2002 Sun Microsystems, Inc. ALL RIGHTS RESERVED.
+ * Portions of this software developed by SUN MICROSYSTEMS, INC.,
+ * and contributed to the OpenSSL project.
+ */
+
+#include 
+
+#include 
+#include 
+#include "ec_lcl.h"
+
+const EC_METHOD *EC_GFp_nist_method(void)
+{
+    static const EC_METHOD ret = {
+        EC_FLAGS_DEFAULT_OCT,
+        NID_X9_62_prime_field,
+        ec_GFp_simple_group_init,
+        ec_GFp_simple_group_finish,
+        ec_GFp_simple_group_clear_finish,
+        ec_GFp_nist_group_copy,
+        ec_GFp_nist_group_set_curve,
+        ec_GFp_simple_group_get_curve,
+        ec_GFp_simple_group_get_degree,
+        ec_group_simple_order_bits,
+        ec_GFp_simple_group_check_discriminant,
+        ec_GFp_simple_point_init,
+        ec_GFp_simple_point_finish,
+        ec_GFp_simple_point_clear_finish,
+        ec_GFp_simple_point_copy,
+        ec_GFp_simple_point_set_to_infinity,
+        ec_GFp_simple_set_Jprojective_coordinates_GFp,
+        ec_GFp_simple_get_Jprojective_coordinates_GFp,
+        ec_GFp_simple_point_set_affine_coordinates,
+        ec_GFp_simple_point_get_affine_coordinates,
+        0, 0, 0,
+        ec_GFp_simple_add,
+        ec_GFp_simple_dbl,
+        ec_GFp_simple_invert,
+        ec_GFp_simple_is_at_infinity,
+        ec_GFp_simple_is_on_curve,
+        ec_GFp_simple_cmp,
+        ec_GFp_simple_make_affine,
+        ec_GFp_simple_points_make_affine,
+        0 /* mul */ ,
+        0 /* precompute_mult */ ,
+        0 /* have_precompute_mult */ ,
+        ec_GFp_nist_field_mul,
+        ec_GFp_nist_field_sqr,
+        0 /* field_div */ ,
+        0 /* field_encode */ ,
+        0 /* field_decode */ ,
+        0,                      /* field_set_to_one */
+        ec_key_simple_priv2oct,
+        ec_key_simple_oct2priv,
+        0, /* set private */
+        ec_key_simple_generate_key,
+        ec_key_simple_check_key,
+        ec_key_simple_generate_public_key,
+        0, /* keycopy */
+        0, /* keyfinish */
+        ecdh_simple_compute_key
+    };
+
+    return &ret;
+}
+
+int ec_GFp_nist_group_copy(EC_GROUP *dest, const EC_GROUP *src)
+{
+    dest->field_mod_func = src->field_mod_func;
+
+    return ec_GFp_simple_group_copy(dest, src);
+}
+
+int ec_GFp_nist_group_set_curve(EC_GROUP *group, const BIGNUM *p,
+                                const BIGNUM *a, const BIGNUM *b, BN_CTX *ctx)
+{
+    int ret = 0;
+    BN_CTX *new_ctx = NULL;
+
+    if (ctx == NULL)
+        if ((ctx = new_ctx = BN_CTX_new()) == NULL)
+            return 0;
+
+    BN_CTX_start(ctx);
+
+    if (BN_ucmp(BN_get0_nist_prime_192(), p) == 0)
+        group->field_mod_func = BN_nist_mod_192;
+    else if (BN_ucmp(BN_get0_nist_prime_224(), p) == 0)
+        group->field_mod_func = BN_nist_mod_224;
+    else if (BN_ucmp(BN_get0_nist_prime_256(), p) == 0)
+        group->field_mod_func = BN_nist_mod_256;
+    else if (BN_ucmp(BN_get0_nist_prime_384(), p) == 0)
+        group->field_mod_func = BN_nist_mod_384;
+    else if (BN_ucmp(BN_get0_nist_prime_521(), p) == 0)
+        group->field_mod_func = BN_nist_mod_521;
+    else {
+        ECerr(EC_F_EC_GFP_NIST_GROUP_SET_CURVE, EC_R_NOT_A_NIST_PRIME);
+        goto err;
+    }
+
+    ret = ec_GFp_simple_group_set_curve(group, p, a, b, ctx);
+
+ err:
+    BN_CTX_end(ctx);
+    BN_CTX_free(new_ctx);
+    return ret;
+}
+
+int ec_GFp_nist_field_mul(const EC_GROUP *group, BIGNUM *r, const BIGNUM *a,
+                          const BIGNUM *b, BN_CTX *ctx)
+{
+    int ret = 0;
+    BN_CTX *ctx_new = NULL;
+
+    if (!group || !r || !a || !b) {
+        ECerr(EC_F_EC_GFP_NIST_FIELD_MUL, ERR_R_PASSED_NULL_PARAMETER);
+        goto err;
+    }
+    if (!ctx)
+        if ((ctx_new = ctx = BN_CTX_new()) == NULL)
+            goto err;
+
+    if (!BN_mul(r, a, b, ctx))
+        goto err;
+    if (!group->field_mod_func(r, r, group->field, ctx))
+        goto err;
+
+    ret = 1;
+ err:
+    BN_CTX_free(ctx_new);
+    return ret;
+}
+
+int ec_GFp_nist_field_sqr(const EC_GROUP *group, BIGNUM *r, const BIGNUM *a,
+                          BN_CTX *ctx)
+{
+    int ret = 0;
+    BN_CTX *ctx_new = NULL;
+
+    if (!group || !r || !a) {
+        ECerr(EC_F_EC_GFP_NIST_FIELD_SQR, EC_R_PASSED_NULL_PARAMETER);
+        goto err;
+    }
+    if (!ctx)
+        if ((ctx_new = ctx = BN_CTX_new()) == NULL)
+            goto err;
+
+    if (!BN_sqr(r, a, ctx))
+        goto err;
+    if (!group->field_mod_func(r, r, group->field, ctx))
+        goto err;
+
+    ret = 1;
+ err:
+    BN_CTX_free(ctx_new);
+    return ret;
+}
diff --git a/crypto/ec/ecp_nistp224.c b/crypto/ec/ecp_nistp224.c
new file mode 100644
index 000000000..0c11abc08
--- /dev/null
+++ b/crypto/ec/ecp_nistp224.c
@@ -0,0 +1,1717 @@
+/*
+ * Copyright 2010-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/* Copyright 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ *
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/*
+ * A 64-bit implementation of the NIST P-224 elliptic curve point multiplication
+ *
+ * Inspired by Daniel J. Bernstein's public domain nistp224 implementation
+ * and Adam Langley's public domain 64-bit C implementation of curve25519
+ */
+
+#include 
+#ifdef OPENSSL_NO_EC_NISTP_64_GCC_128
+NON_EMPTY_TRANSLATION_UNIT
+#else
+
+# include 
+# include 
+# include 
+# include "ec_lcl.h"
+
+# if defined(__GNUC__) && (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1))
+  /* even with gcc, the typedef won't work for 32-bit platforms */
+typedef __uint128_t uint128_t;  /* nonstandard; implemented by gcc on 64-bit
+                                 * platforms */
+# else
+#  error "Need GCC 3.1 or later to define type uint128_t"
+# endif
+
+typedef uint8_t u8;
+typedef uint64_t u64;
+typedef int64_t s64;
+
+/******************************************************************************/
+/*-
+ * INTERNAL REPRESENTATION OF FIELD ELEMENTS
+ *
+ * Field elements are represented as a_0 + 2^56*a_1 + 2^112*a_2 + 2^168*a_3
+ * using 64-bit coefficients called 'limbs',
+ * and sometimes (for multiplication results) as
+ * b_0 + 2^56*b_1 + 2^112*b_2 + 2^168*b_3 + 2^224*b_4 + 2^280*b_5 + 2^336*b_6
+ * using 128-bit coefficients called 'widelimbs'.
+ * A 4-limb representation is an 'felem';
+ * a 7-widelimb representation is a 'widefelem'.
+ * Even within felems, bits of adjacent limbs overlap, and we don't always
+ * reduce the representations: we ensure that inputs to each felem
+ * multiplication satisfy a_i < 2^60, so outputs satisfy b_i < 4*2^60*2^60,
+ * and fit into a 128-bit word without overflow. The coefficients are then
+ * again partially reduced to obtain an felem satisfying a_i < 2^57.
+ * We only reduce to the unique minimal representation at the end of the
+ * computation.
+ */
+
+typedef uint64_t limb;
+typedef uint128_t widelimb;
+
+typedef limb felem[4];
+typedef widelimb widefelem[7];
+
+/*
+ * Field element represented as a byte arrary. 28*8 = 224 bits is also the
+ * group order size for the elliptic curve, and we also use this type for
+ * scalars for point multiplication.
+ */
+typedef u8 felem_bytearray[28];
+
+static const felem_bytearray nistp224_curve_params[5] = {
+    {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* p */
+     0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
+     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01},
+    {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* a */
+     0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF,
+     0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE},
+    {0xB4, 0x05, 0x0A, 0x85, 0x0C, 0x04, 0xB3, 0xAB, 0xF5, 0x41, /* b */
+     0x32, 0x56, 0x50, 0x44, 0xB0, 0xB7, 0xD7, 0xBF, 0xD8, 0xBA,
+     0x27, 0x0B, 0x39, 0x43, 0x23, 0x55, 0xFF, 0xB4},
+    {0xB7, 0x0E, 0x0C, 0xBD, 0x6B, 0xB4, 0xBF, 0x7F, 0x32, 0x13, /* x */
+     0x90, 0xB9, 0x4A, 0x03, 0xC1, 0xD3, 0x56, 0xC2, 0x11, 0x22,
+     0x34, 0x32, 0x80, 0xD6, 0x11, 0x5C, 0x1D, 0x21},
+    {0xbd, 0x37, 0x63, 0x88, 0xb5, 0xf7, 0x23, 0xfb, 0x4c, 0x22, /* y */
+     0xdf, 0xe6, 0xcd, 0x43, 0x75, 0xa0, 0x5a, 0x07, 0x47, 0x64,
+     0x44, 0xd5, 0x81, 0x99, 0x85, 0x00, 0x7e, 0x34}
+};
+
+/*-
+ * Precomputed multiples of the standard generator
+ * Points are given in coordinates (X, Y, Z) where Z normally is 1
+ * (0 for the point at infinity).
+ * For each field element, slice a_0 is word 0, etc.
+ *
+ * The table has 2 * 16 elements, starting with the following:
+ * index | bits    | point
+ * ------+---------+------------------------------
+ *     0 | 0 0 0 0 | 0G
+ *     1 | 0 0 0 1 | 1G
+ *     2 | 0 0 1 0 | 2^56G
+ *     3 | 0 0 1 1 | (2^56 + 1)G
+ *     4 | 0 1 0 0 | 2^112G
+ *     5 | 0 1 0 1 | (2^112 + 1)G
+ *     6 | 0 1 1 0 | (2^112 + 2^56)G
+ *     7 | 0 1 1 1 | (2^112 + 2^56 + 1)G
+ *     8 | 1 0 0 0 | 2^168G
+ *     9 | 1 0 0 1 | (2^168 + 1)G
+ *    10 | 1 0 1 0 | (2^168 + 2^56)G
+ *    11 | 1 0 1 1 | (2^168 + 2^56 + 1)G
+ *    12 | 1 1 0 0 | (2^168 + 2^112)G
+ *    13 | 1 1 0 1 | (2^168 + 2^112 + 1)G
+ *    14 | 1 1 1 0 | (2^168 + 2^112 + 2^56)G
+ *    15 | 1 1 1 1 | (2^168 + 2^112 + 2^56 + 1)G
+ * followed by a copy of this with each element multiplied by 2^28.
+ *
+ * The reason for this is so that we can clock bits into four different
+ * locations when doing simple scalar multiplies against the base point,
+ * and then another four locations using the second 16 elements.
+ */
+static const felem gmul[2][16][3] = {
+{{{0, 0, 0, 0},
+  {0, 0, 0, 0},
+  {0, 0, 0, 0}},
+ {{0x3280d6115c1d21, 0xc1d356c2112234, 0x7f321390b94a03, 0xb70e0cbd6bb4bf},
+  {0xd5819985007e34, 0x75a05a07476444, 0xfb4c22dfe6cd43, 0xbd376388b5f723},
+  {1, 0, 0, 0}},
+ {{0xfd9675666ebbe9, 0xbca7664d40ce5e, 0x2242df8d8a2a43, 0x1f49bbb0f99bc5},
+  {0x29e0b892dc9c43, 0xece8608436e662, 0xdc858f185310d0, 0x9812dd4eb8d321},
+  {1, 0, 0, 0}},
+ {{0x6d3e678d5d8eb8, 0x559eed1cb362f1, 0x16e9a3bbce8a3f, 0xeedcccd8c2a748},
+  {0xf19f90ed50266d, 0xabf2b4bf65f9df, 0x313865468fafec, 0x5cb379ba910a17},
+  {1, 0, 0, 0}},
+ {{0x0641966cab26e3, 0x91fb2991fab0a0, 0xefec27a4e13a0b, 0x0499aa8a5f8ebe},
+  {0x7510407766af5d, 0x84d929610d5450, 0x81d77aae82f706, 0x6916f6d4338c5b},
+  {1, 0, 0, 0}},
+ {{0xea95ac3b1f15c6, 0x086000905e82d4, 0xdd323ae4d1c8b1, 0x932b56be7685a3},
+  {0x9ef93dea25dbbf, 0x41665960f390f0, 0xfdec76dbe2a8a7, 0x523e80f019062a},
+  {1, 0, 0, 0}},
+ {{0x822fdd26732c73, 0xa01c83531b5d0f, 0x363f37347c1ba4, 0xc391b45c84725c},
+  {0xbbd5e1b2d6ad24, 0xddfbcde19dfaec, 0xc393da7e222a7f, 0x1efb7890ede244},
+  {1, 0, 0, 0}},
+ {{0x4c9e90ca217da1, 0xd11beca79159bb, 0xff8d33c2c98b7c, 0x2610b39409f849},
+  {0x44d1352ac64da0, 0xcdbb7b2c46b4fb, 0x966c079b753c89, 0xfe67e4e820b112},
+  {1, 0, 0, 0}},
+ {{0xe28cae2df5312d, 0xc71b61d16f5c6e, 0x79b7619a3e7c4c, 0x05c73240899b47},
+  {0x9f7f6382c73e3a, 0x18615165c56bda, 0x641fab2116fd56, 0x72855882b08394},
+  {1, 0, 0, 0}},
+ {{0x0469182f161c09, 0x74a98ca8d00fb5, 0xb89da93489a3e0, 0x41c98768fb0c1d},
+  {0xe5ea05fb32da81, 0x3dce9ffbca6855, 0x1cfe2d3fbf59e6, 0x0e5e03408738a7},
+  {1, 0, 0, 0}},
+ {{0xdab22b2333e87f, 0x4430137a5dd2f6, 0xe03ab9f738beb8, 0xcb0c5d0dc34f24},
+  {0x764a7df0c8fda5, 0x185ba5c3fa2044, 0x9281d688bcbe50, 0xc40331df893881},
+  {1, 0, 0, 0}},
+ {{0xb89530796f0f60, 0xade92bd26909a3, 0x1a0c83fb4884da, 0x1765bf22a5a984},
+  {0x772a9ee75db09e, 0x23bc6c67cec16f, 0x4c1edba8b14e2f, 0xe2a215d9611369},
+  {1, 0, 0, 0}},
+ {{0x571e509fb5efb3, 0xade88696410552, 0xc8ae85fada74fe, 0x6c7e4be83bbde3},
+  {0xff9f51160f4652, 0xb47ce2495a6539, 0xa2946c53b582f4, 0x286d2db3ee9a60},
+  {1, 0, 0, 0}},
+ {{0x40bbd5081a44af, 0x0995183b13926c, 0xbcefba6f47f6d0, 0x215619e9cc0057},
+  {0x8bc94d3b0df45e, 0xf11c54a3694f6f, 0x8631b93cdfe8b5, 0xe7e3f4b0982db9},
+  {1, 0, 0, 0}},
+ {{0xb17048ab3e1c7b, 0xac38f36ff8a1d8, 0x1c29819435d2c6, 0xc813132f4c07e9},
+  {0x2891425503b11f, 0x08781030579fea, 0xf5426ba5cc9674, 0x1e28ebf18562bc},
+  {1, 0, 0, 0}},
+ {{0x9f31997cc864eb, 0x06cd91d28b5e4c, 0xff17036691a973, 0xf1aef351497c58},
+  {0xdd1f2d600564ff, 0xdead073b1402db, 0x74a684435bd693, 0xeea7471f962558},
+  {1, 0, 0, 0}}},
+{{{0, 0, 0, 0},
+  {0, 0, 0, 0},
+  {0, 0, 0, 0}},
+ {{0x9665266dddf554, 0x9613d78b60ef2d, 0xce27a34cdba417, 0xd35ab74d6afc31},
+  {0x85ccdd22deb15e, 0x2137e5783a6aab, 0xa141cffd8c93c6, 0x355a1830e90f2d},
+  {1, 0, 0, 0}},
+ {{0x1a494eadaade65, 0xd6da4da77fe53c, 0xe7992996abec86, 0x65c3553c6090e3},
+  {0xfa610b1fb09346, 0xf1c6540b8a4aaf, 0xc51a13ccd3cbab, 0x02995b1b18c28a},
+  {1, 0, 0, 0}},
+ {{0x7874568e7295ef, 0x86b419fbe38d04, 0xdc0690a7550d9a, 0xd3966a44beac33},
+  {0x2b7280ec29132f, 0xbeaa3b6a032df3, 0xdc7dd88ae41200, 0xd25e2513e3a100},
+  {1, 0, 0, 0}},
+ {{0x924857eb2efafd, 0xac2bce41223190, 0x8edaa1445553fc, 0x825800fd3562d5},
+  {0x8d79148ea96621, 0x23a01c3dd9ed8d, 0xaf8b219f9416b5, 0xd8db0cc277daea},
+  {1, 0, 0, 0}},
+ {{0x76a9c3b1a700f0, 0xe9acd29bc7e691, 0x69212d1a6b0327, 0x6322e97fe154be},
+  {0x469fc5465d62aa, 0x8d41ed18883b05, 0x1f8eae66c52b88, 0xe4fcbe9325be51},
+  {1, 0, 0, 0}},
+ {{0x825fdf583cac16, 0x020b857c7b023a, 0x683c17744b0165, 0x14ffd0a2daf2f1},
+  {0x323b36184218f9, 0x4944ec4e3b47d4, 0xc15b3080841acf, 0x0bced4b01a28bb},
+  {1, 0, 0, 0}},
+ {{0x92ac22230df5c4, 0x52f33b4063eda8, 0xcb3f19870c0c93, 0x40064f2ba65233},
+  {0xfe16f0924f8992, 0x012da25af5b517, 0x1a57bb24f723a6, 0x06f8bc76760def},
+  {1, 0, 0, 0}},
+ {{0x4a7084f7817cb9, 0xbcab0738ee9a78, 0x3ec11e11d9c326, 0xdc0fe90e0f1aae},
+  {0xcf639ea5f98390, 0x5c350aa22ffb74, 0x9afae98a4047b7, 0x956ec2d617fc45},
+  {1, 0, 0, 0}},
+ {{0x4306d648c1be6a, 0x9247cd8bc9a462, 0xf5595e377d2f2e, 0xbd1c3caff1a52e},
+  {0x045e14472409d0, 0x29f3e17078f773, 0x745a602b2d4f7d, 0x191837685cdfbb},
+  {1, 0, 0, 0}},
+ {{0x5b6ee254a8cb79, 0x4953433f5e7026, 0xe21faeb1d1def4, 0xc4c225785c09de},
+  {0x307ce7bba1e518, 0x31b125b1036db8, 0x47e91868839e8f, 0xc765866e33b9f3},
+  {1, 0, 0, 0}},
+ {{0x3bfece24f96906, 0x4794da641e5093, 0xde5df64f95db26, 0x297ecd89714b05},
+  {0x701bd3ebb2c3aa, 0x7073b4f53cb1d5, 0x13c5665658af16, 0x9895089d66fe58},
+  {1, 0, 0, 0}},
+ {{0x0fef05f78c4790, 0x2d773633b05d2e, 0x94229c3a951c94, 0xbbbd70df4911bb},
+  {0xb2c6963d2c1168, 0x105f47a72b0d73, 0x9fdf6111614080, 0x7b7e94b39e67b0},
+  {1, 0, 0, 0}},
+ {{0xad1a7d6efbe2b3, 0xf012482c0da69d, 0x6b3bdf12438345, 0x40d7558d7aa4d9},
+  {0x8a09fffb5c6d3d, 0x9a356e5d9ffd38, 0x5973f15f4f9b1c, 0xdcd5f59f63c3ea},
+  {1, 0, 0, 0}},
+ {{0xacf39f4c5ca7ab, 0x4c8071cc5fd737, 0xc64e3602cd1184, 0x0acd4644c9abba},
+  {0x6c011a36d8bf6e, 0xfecd87ba24e32a, 0x19f6f56574fad8, 0x050b204ced9405},
+  {1, 0, 0, 0}},
+ {{0xed4f1cae7d9a96, 0x5ceef7ad94c40a, 0x778e4a3bf3ef9b, 0x7405783dc3b55e},
+  {0x32477c61b6e8c6, 0xb46a97570f018b, 0x91176d0a7e95d1, 0x3df90fbc4c7d0e},
+  {1, 0, 0, 0}}}
+};
+
+/* Precomputation for the group generator. */
+struct nistp224_pre_comp_st {
+    felem g_pre_comp[2][16][3];
+    int references;
+    CRYPTO_RWLOCK *lock;
+};
+
+const EC_METHOD *EC_GFp_nistp224_method(void)
+{
+    static const EC_METHOD ret = {
+        EC_FLAGS_DEFAULT_OCT,
+        NID_X9_62_prime_field,
+        ec_GFp_nistp224_group_init,
+        ec_GFp_simple_group_finish,
+        ec_GFp_simple_group_clear_finish,
+        ec_GFp_nist_group_copy,
+        ec_GFp_nistp224_group_set_curve,
+        ec_GFp_simple_group_get_curve,
+        ec_GFp_simple_group_get_degree,
+        ec_group_simple_order_bits,
+        ec_GFp_simple_group_check_discriminant,
+        ec_GFp_simple_point_init,
+        ec_GFp_simple_point_finish,
+        ec_GFp_simple_point_clear_finish,
+        ec_GFp_simple_point_copy,
+        ec_GFp_simple_point_set_to_infinity,
+        ec_GFp_simple_set_Jprojective_coordinates_GFp,
+        ec_GFp_simple_get_Jprojective_coordinates_GFp,
+        ec_GFp_simple_point_set_affine_coordinates,
+        ec_GFp_nistp224_point_get_affine_coordinates,
+        0 /* point_set_compressed_coordinates */ ,
+        0 /* point2oct */ ,
+        0 /* oct2point */ ,
+        ec_GFp_simple_add,
+        ec_GFp_simple_dbl,
+        ec_GFp_simple_invert,
+        ec_GFp_simple_is_at_infinity,
+        ec_GFp_simple_is_on_curve,
+        ec_GFp_simple_cmp,
+        ec_GFp_simple_make_affine,
+        ec_GFp_simple_points_make_affine,
+        ec_GFp_nistp224_points_mul,
+        ec_GFp_nistp224_precompute_mult,
+        ec_GFp_nistp224_have_precompute_mult,
+        ec_GFp_nist_field_mul,
+        ec_GFp_nist_field_sqr,
+        0 /* field_div */ ,
+        0 /* field_encode */ ,
+        0 /* field_decode */ ,
+        0,                      /* field_set_to_one */
+        ec_key_simple_priv2oct,
+        ec_key_simple_oct2priv,
+        0, /* set private */
+        ec_key_simple_generate_key,
+        ec_key_simple_check_key,
+        ec_key_simple_generate_public_key,
+        0, /* keycopy */
+        0, /* keyfinish */
+        ecdh_simple_compute_key
+    };
+
+    return &ret;
+}
+
+/*
+ * Helper functions to convert field elements to/from internal representation
+ */
+static void bin28_to_felem(felem out, const u8 in[28])
+{
+    out[0] = *((const uint64_t *)(in)) & 0x00ffffffffffffff;
+    out[1] = (*((const uint64_t *)(in + 7))) & 0x00ffffffffffffff;
+    out[2] = (*((const uint64_t *)(in + 14))) & 0x00ffffffffffffff;
+    out[3] = (*((const uint64_t *)(in+20))) >> 8;
+}
+
+static void felem_to_bin28(u8 out[28], const felem in)
+{
+    unsigned i;
+    for (i = 0; i < 7; ++i) {
+        out[i] = in[0] >> (8 * i);
+        out[i + 7] = in[1] >> (8 * i);
+        out[i + 14] = in[2] >> (8 * i);
+        out[i + 21] = in[3] >> (8 * i);
+    }
+}
+
+/* To preserve endianness when using BN_bn2bin and BN_bin2bn */
+static void flip_endian(u8 *out, const u8 *in, unsigned len)
+{
+    unsigned i;
+    for (i = 0; i < len; ++i)
+        out[i] = in[len - 1 - i];
+}
+
+/* From OpenSSL BIGNUM to internal representation */
+static int BN_to_felem(felem out, const BIGNUM *bn)
+{
+    felem_bytearray b_in;
+    felem_bytearray b_out;
+    unsigned num_bytes;
+
+    /* BN_bn2bin eats leading zeroes */
+    memset(b_out, 0, sizeof(b_out));
+    num_bytes = BN_num_bytes(bn);
+    if (num_bytes > sizeof b_out) {
+        ECerr(EC_F_BN_TO_FELEM, EC_R_BIGNUM_OUT_OF_RANGE);
+        return 0;
+    }
+    if (BN_is_negative(bn)) {
+        ECerr(EC_F_BN_TO_FELEM, EC_R_BIGNUM_OUT_OF_RANGE);
+        return 0;
+    }
+    num_bytes = BN_bn2bin(bn, b_in);
+    flip_endian(b_out, b_in, num_bytes);
+    bin28_to_felem(out, b_out);
+    return 1;
+}
+
+/* From internal representation to OpenSSL BIGNUM */
+static BIGNUM *felem_to_BN(BIGNUM *out, const felem in)
+{
+    felem_bytearray b_in, b_out;
+    felem_to_bin28(b_in, in);
+    flip_endian(b_out, b_in, sizeof b_out);
+    return BN_bin2bn(b_out, sizeof b_out, out);
+}
+
+/******************************************************************************/
+/*-
+ *                              FIELD OPERATIONS
+ *
+ * Field operations, using the internal representation of field elements.
+ * NB! These operations are specific to our point multiplication and cannot be
+ * expected to be correct in general - e.g., multiplication with a large scalar
+ * will cause an overflow.
+ *
+ */
+
+static void felem_one(felem out)
+{
+    out[0] = 1;
+    out[1] = 0;
+    out[2] = 0;
+    out[3] = 0;
+}
+
+static void felem_assign(felem out, const felem in)
+{
+    out[0] = in[0];
+    out[1] = in[1];
+    out[2] = in[2];
+    out[3] = in[3];
+}
+
+/* Sum two field elements: out += in */
+static void felem_sum(felem out, const felem in)
+{
+    out[0] += in[0];
+    out[1] += in[1];
+    out[2] += in[2];
+    out[3] += in[3];
+}
+
+/* Get negative value: out = -in */
+/* Assumes in[i] < 2^57 */
+static void felem_neg(felem out, const felem in)
+{
+    static const limb two58p2 = (((limb) 1) << 58) + (((limb) 1) << 2);
+    static const limb two58m2 = (((limb) 1) << 58) - (((limb) 1) << 2);
+    static const limb two58m42m2 = (((limb) 1) << 58) -
+        (((limb) 1) << 42) - (((limb) 1) << 2);
+
+    /* Set to 0 mod 2^224-2^96+1 to ensure out > in */
+    out[0] = two58p2 - in[0];
+    out[1] = two58m42m2 - in[1];
+    out[2] = two58m2 - in[2];
+    out[3] = two58m2 - in[3];
+}
+
+/* Subtract field elements: out -= in */
+/* Assumes in[i] < 2^57 */
+static void felem_diff(felem out, const felem in)
+{
+    static const limb two58p2 = (((limb) 1) << 58) + (((limb) 1) << 2);
+    static const limb two58m2 = (((limb) 1) << 58) - (((limb) 1) << 2);
+    static const limb two58m42m2 = (((limb) 1) << 58) -
+        (((limb) 1) << 42) - (((limb) 1) << 2);
+
+    /* Add 0 mod 2^224-2^96+1 to ensure out > in */
+    out[0] += two58p2;
+    out[1] += two58m42m2;
+    out[2] += two58m2;
+    out[3] += two58m2;
+
+    out[0] -= in[0];
+    out[1] -= in[1];
+    out[2] -= in[2];
+    out[3] -= in[3];
+}
+
+/* Subtract in unreduced 128-bit mode: out -= in */
+/* Assumes in[i] < 2^119 */
+static void widefelem_diff(widefelem out, const widefelem in)
+{
+    static const widelimb two120 = ((widelimb) 1) << 120;
+    static const widelimb two120m64 = (((widelimb) 1) << 120) -
+        (((widelimb) 1) << 64);
+    static const widelimb two120m104m64 = (((widelimb) 1) << 120) -
+        (((widelimb) 1) << 104) - (((widelimb) 1) << 64);
+
+    /* Add 0 mod 2^224-2^96+1 to ensure out > in */
+    out[0] += two120;
+    out[1] += two120m64;
+    out[2] += two120m64;
+    out[3] += two120;
+    out[4] += two120m104m64;
+    out[5] += two120m64;
+    out[6] += two120m64;
+
+    out[0] -= in[0];
+    out[1] -= in[1];
+    out[2] -= in[2];
+    out[3] -= in[3];
+    out[4] -= in[4];
+    out[5] -= in[5];
+    out[6] -= in[6];
+}
+
+/* Subtract in mixed mode: out128 -= in64 */
+/* in[i] < 2^63 */
+static void felem_diff_128_64(widefelem out, const felem in)
+{
+    static const widelimb two64p8 = (((widelimb) 1) << 64) +
+        (((widelimb) 1) << 8);
+    static const widelimb two64m8 = (((widelimb) 1) << 64) -
+        (((widelimb) 1) << 8);
+    static const widelimb two64m48m8 = (((widelimb) 1) << 64) -
+        (((widelimb) 1) << 48) - (((widelimb) 1) << 8);
+
+    /* Add 0 mod 2^224-2^96+1 to ensure out > in */
+    out[0] += two64p8;
+    out[1] += two64m48m8;
+    out[2] += two64m8;
+    out[3] += two64m8;
+
+    out[0] -= in[0];
+    out[1] -= in[1];
+    out[2] -= in[2];
+    out[3] -= in[3];
+}
+
+/*
+ * Multiply a field element by a scalar: out = out * scalar The scalars we
+ * actually use are small, so results fit without overflow
+ */
+static void felem_scalar(felem out, const limb scalar)
+{
+    out[0] *= scalar;
+    out[1] *= scalar;
+    out[2] *= scalar;
+    out[3] *= scalar;
+}
+
+/*
+ * Multiply an unreduced field element by a scalar: out = out * scalar The
+ * scalars we actually use are small, so results fit without overflow
+ */
+static void widefelem_scalar(widefelem out, const widelimb scalar)
+{
+    out[0] *= scalar;
+    out[1] *= scalar;
+    out[2] *= scalar;
+    out[3] *= scalar;
+    out[4] *= scalar;
+    out[5] *= scalar;
+    out[6] *= scalar;
+}
+
+/* Square a field element: out = in^2 */
+static void felem_square(widefelem out, const felem in)
+{
+    limb tmp0, tmp1, tmp2;
+    tmp0 = 2 * in[0];
+    tmp1 = 2 * in[1];
+    tmp2 = 2 * in[2];
+    out[0] = ((widelimb) in[0]) * in[0];
+    out[1] = ((widelimb) in[0]) * tmp1;
+    out[2] = ((widelimb) in[0]) * tmp2 + ((widelimb) in[1]) * in[1];
+    out[3] = ((widelimb) in[3]) * tmp0 + ((widelimb) in[1]) * tmp2;
+    out[4] = ((widelimb) in[3]) * tmp1 + ((widelimb) in[2]) * in[2];
+    out[5] = ((widelimb) in[3]) * tmp2;
+    out[6] = ((widelimb) in[3]) * in[3];
+}
+
+/* Multiply two field elements: out = in1 * in2 */
+static void felem_mul(widefelem out, const felem in1, const felem in2)
+{
+    out[0] = ((widelimb) in1[0]) * in2[0];
+    out[1] = ((widelimb) in1[0]) * in2[1] + ((widelimb) in1[1]) * in2[0];
+    out[2] = ((widelimb) in1[0]) * in2[2] + ((widelimb) in1[1]) * in2[1] +
+             ((widelimb) in1[2]) * in2[0];
+    out[3] = ((widelimb) in1[0]) * in2[3] + ((widelimb) in1[1]) * in2[2] +
+             ((widelimb) in1[2]) * in2[1] + ((widelimb) in1[3]) * in2[0];
+    out[4] = ((widelimb) in1[1]) * in2[3] + ((widelimb) in1[2]) * in2[2] +
+             ((widelimb) in1[3]) * in2[1];
+    out[5] = ((widelimb) in1[2]) * in2[3] + ((widelimb) in1[3]) * in2[2];
+    out[6] = ((widelimb) in1[3]) * in2[3];
+}
+
+/*-
+ * Reduce seven 128-bit coefficients to four 64-bit coefficients.
+ * Requires in[i] < 2^126,
+ * ensures out[0] < 2^56, out[1] < 2^56, out[2] < 2^56, out[3] <= 2^56 + 2^16 */
+static void felem_reduce(felem out, const widefelem in)
+{
+    static const widelimb two127p15 = (((widelimb) 1) << 127) +
+        (((widelimb) 1) << 15);
+    static const widelimb two127m71 = (((widelimb) 1) << 127) -
+        (((widelimb) 1) << 71);
+    static const widelimb two127m71m55 = (((widelimb) 1) << 127) -
+        (((widelimb) 1) << 71) - (((widelimb) 1) << 55);
+    widelimb output[5];
+
+    /* Add 0 mod 2^224-2^96+1 to ensure all differences are positive */
+    output[0] = in[0] + two127p15;
+    output[1] = in[1] + two127m71m55;
+    output[2] = in[2] + two127m71;
+    output[3] = in[3];
+    output[4] = in[4];
+
+    /* Eliminate in[4], in[5], in[6] */
+    output[4] += in[6] >> 16;
+    output[3] += (in[6] & 0xffff) << 40;
+    output[2] -= in[6];
+
+    output[3] += in[5] >> 16;
+    output[2] += (in[5] & 0xffff) << 40;
+    output[1] -= in[5];
+
+    output[2] += output[4] >> 16;
+    output[1] += (output[4] & 0xffff) << 40;
+    output[0] -= output[4];
+
+    /* Carry 2 -> 3 -> 4 */
+    output[3] += output[2] >> 56;
+    output[2] &= 0x00ffffffffffffff;
+
+    output[4] = output[3] >> 56;
+    output[3] &= 0x00ffffffffffffff;
+
+    /* Now output[2] < 2^56, output[3] < 2^56, output[4] < 2^72 */
+
+    /* Eliminate output[4] */
+    output[2] += output[4] >> 16;
+    /* output[2] < 2^56 + 2^56 = 2^57 */
+    output[1] += (output[4] & 0xffff) << 40;
+    output[0] -= output[4];
+
+    /* Carry 0 -> 1 -> 2 -> 3 */
+    output[1] += output[0] >> 56;
+    out[0] = output[0] & 0x00ffffffffffffff;
+
+    output[2] += output[1] >> 56;
+    /* output[2] < 2^57 + 2^72 */
+    out[1] = output[1] & 0x00ffffffffffffff;
+    output[3] += output[2] >> 56;
+    /* output[3] <= 2^56 + 2^16 */
+    out[2] = output[2] & 0x00ffffffffffffff;
+
+    /*-
+     * out[0] < 2^56, out[1] < 2^56, out[2] < 2^56,
+     * out[3] <= 2^56 + 2^16 (due to final carry),
+     * so out < 2*p
+     */
+    out[3] = output[3];
+}
+
+static void felem_square_reduce(felem out, const felem in)
+{
+    widefelem tmp;
+    felem_square(tmp, in);
+    felem_reduce(out, tmp);
+}
+
+static void felem_mul_reduce(felem out, const felem in1, const felem in2)
+{
+    widefelem tmp;
+    felem_mul(tmp, in1, in2);
+    felem_reduce(out, tmp);
+}
+
+/*
+ * Reduce to unique minimal representation. Requires 0 <= in < 2*p (always
+ * call felem_reduce first)
+ */
+static void felem_contract(felem out, const felem in)
+{
+    static const int64_t two56 = ((limb) 1) << 56;
+    /* 0 <= in < 2*p, p = 2^224 - 2^96 + 1 */
+    /* if in > p , reduce in = in - 2^224 + 2^96 - 1 */
+    int64_t tmp[4], a;
+    tmp[0] = in[0];
+    tmp[1] = in[1];
+    tmp[2] = in[2];
+    tmp[3] = in[3];
+    /* Case 1: a = 1 iff in >= 2^224 */
+    a = (in[3] >> 56);
+    tmp[0] -= a;
+    tmp[1] += a << 40;
+    tmp[3] &= 0x00ffffffffffffff;
+    /*
+     * Case 2: a = 0 iff p <= in < 2^224, i.e., the high 128 bits are all 1
+     * and the lower part is non-zero
+     */
+    a = ((in[3] & in[2] & (in[1] | 0x000000ffffffffff)) + 1) |
+        (((int64_t) (in[0] + (in[1] & 0x000000ffffffffff)) - 1) >> 63);
+    a &= 0x00ffffffffffffff;
+    /* turn a into an all-one mask (if a = 0) or an all-zero mask */
+    a = (a - 1) >> 63;
+    /* subtract 2^224 - 2^96 + 1 if a is all-one */
+    tmp[3] &= a ^ 0xffffffffffffffff;
+    tmp[2] &= a ^ 0xffffffffffffffff;
+    tmp[1] &= (a ^ 0xffffffffffffffff) | 0x000000ffffffffff;
+    tmp[0] -= 1 & a;
+
+    /*
+     * eliminate negative coefficients: if tmp[0] is negative, tmp[1] must be
+     * non-zero, so we only need one step
+     */
+    a = tmp[0] >> 63;
+    tmp[0] += two56 & a;
+    tmp[1] -= 1 & a;
+
+    /* carry 1 -> 2 -> 3 */
+    tmp[2] += tmp[1] >> 56;
+    tmp[1] &= 0x00ffffffffffffff;
+
+    tmp[3] += tmp[2] >> 56;
+    tmp[2] &= 0x00ffffffffffffff;
+
+    /* Now 0 <= out < p */
+    out[0] = tmp[0];
+    out[1] = tmp[1];
+    out[2] = tmp[2];
+    out[3] = tmp[3];
+}
+
+/*
+ * Zero-check: returns 1 if input is 0, and 0 otherwise. We know that field
+ * elements are reduced to in < 2^225, so we only need to check three cases:
+ * 0, 2^224 - 2^96 + 1, and 2^225 - 2^97 + 2
+ */
+static limb felem_is_zero(const felem in)
+{
+    limb zero, two224m96p1, two225m97p2;
+
+    zero = in[0] | in[1] | in[2] | in[3];
+    zero = (((int64_t) (zero) - 1) >> 63) & 1;
+    two224m96p1 = (in[0] ^ 1) | (in[1] ^ 0x00ffff0000000000)
+        | (in[2] ^ 0x00ffffffffffffff) | (in[3] ^ 0x00ffffffffffffff);
+    two224m96p1 = (((int64_t) (two224m96p1) - 1) >> 63) & 1;
+    two225m97p2 = (in[0] ^ 2) | (in[1] ^ 0x00fffe0000000000)
+        | (in[2] ^ 0x00ffffffffffffff) | (in[3] ^ 0x01ffffffffffffff);
+    two225m97p2 = (((int64_t) (two225m97p2) - 1) >> 63) & 1;
+    return (zero | two224m96p1 | two225m97p2);
+}
+
+static limb felem_is_zero_int(const felem in)
+{
+    return (int)(felem_is_zero(in) & ((limb) 1));
+}
+
+/* Invert a field element */
+/* Computation chain copied from djb's code */
+static void felem_inv(felem out, const felem in)
+{
+    felem ftmp, ftmp2, ftmp3, ftmp4;
+    widefelem tmp;
+    unsigned i;
+
+    felem_square(tmp, in);
+    felem_reduce(ftmp, tmp);    /* 2 */
+    felem_mul(tmp, in, ftmp);
+    felem_reduce(ftmp, tmp);    /* 2^2 - 1 */
+    felem_square(tmp, ftmp);
+    felem_reduce(ftmp, tmp);    /* 2^3 - 2 */
+    felem_mul(tmp, in, ftmp);
+    felem_reduce(ftmp, tmp);    /* 2^3 - 1 */
+    felem_square(tmp, ftmp);
+    felem_reduce(ftmp2, tmp);   /* 2^4 - 2 */
+    felem_square(tmp, ftmp2);
+    felem_reduce(ftmp2, tmp);   /* 2^5 - 4 */
+    felem_square(tmp, ftmp2);
+    felem_reduce(ftmp2, tmp);   /* 2^6 - 8 */
+    felem_mul(tmp, ftmp2, ftmp);
+    felem_reduce(ftmp, tmp);    /* 2^6 - 1 */
+    felem_square(tmp, ftmp);
+    felem_reduce(ftmp2, tmp);   /* 2^7 - 2 */
+    for (i = 0; i < 5; ++i) {   /* 2^12 - 2^6 */
+        felem_square(tmp, ftmp2);
+        felem_reduce(ftmp2, tmp);
+    }
+    felem_mul(tmp, ftmp2, ftmp);
+    felem_reduce(ftmp2, tmp);   /* 2^12 - 1 */
+    felem_square(tmp, ftmp2);
+    felem_reduce(ftmp3, tmp);   /* 2^13 - 2 */
+    for (i = 0; i < 11; ++i) {  /* 2^24 - 2^12 */
+        felem_square(tmp, ftmp3);
+        felem_reduce(ftmp3, tmp);
+    }
+    felem_mul(tmp, ftmp3, ftmp2);
+    felem_reduce(ftmp2, tmp);   /* 2^24 - 1 */
+    felem_square(tmp, ftmp2);
+    felem_reduce(ftmp3, tmp);   /* 2^25 - 2 */
+    for (i = 0; i < 23; ++i) {  /* 2^48 - 2^24 */
+        felem_square(tmp, ftmp3);
+        felem_reduce(ftmp3, tmp);
+    }
+    felem_mul(tmp, ftmp3, ftmp2);
+    felem_reduce(ftmp3, tmp);   /* 2^48 - 1 */
+    felem_square(tmp, ftmp3);
+    felem_reduce(ftmp4, tmp);   /* 2^49 - 2 */
+    for (i = 0; i < 47; ++i) {  /* 2^96 - 2^48 */
+        felem_square(tmp, ftmp4);
+        felem_reduce(ftmp4, tmp);
+    }
+    felem_mul(tmp, ftmp3, ftmp4);
+    felem_reduce(ftmp3, tmp);   /* 2^96 - 1 */
+    felem_square(tmp, ftmp3);
+    felem_reduce(ftmp4, tmp);   /* 2^97 - 2 */
+    for (i = 0; i < 23; ++i) {  /* 2^120 - 2^24 */
+        felem_square(tmp, ftmp4);
+        felem_reduce(ftmp4, tmp);
+    }
+    felem_mul(tmp, ftmp2, ftmp4);
+    felem_reduce(ftmp2, tmp);   /* 2^120 - 1 */
+    for (i = 0; i < 6; ++i) {   /* 2^126 - 2^6 */
+        felem_square(tmp, ftmp2);
+        felem_reduce(ftmp2, tmp);
+    }
+    felem_mul(tmp, ftmp2, ftmp);
+    felem_reduce(ftmp, tmp);    /* 2^126 - 1 */
+    felem_square(tmp, ftmp);
+    felem_reduce(ftmp, tmp);    /* 2^127 - 2 */
+    felem_mul(tmp, ftmp, in);
+    felem_reduce(ftmp, tmp);    /* 2^127 - 1 */
+    for (i = 0; i < 97; ++i) {  /* 2^224 - 2^97 */
+        felem_square(tmp, ftmp);
+        felem_reduce(ftmp, tmp);
+    }
+    felem_mul(tmp, ftmp, ftmp3);
+    felem_reduce(out, tmp);     /* 2^224 - 2^96 - 1 */
+}
+
+/*
+ * Copy in constant time: if icopy == 1, copy in to out, if icopy == 0, copy
+ * out to itself.
+ */
+static void copy_conditional(felem out, const felem in, limb icopy)
+{
+    unsigned i;
+    /*
+     * icopy is a (64-bit) 0 or 1, so copy is either all-zero or all-one
+     */
+    const limb copy = -icopy;
+    for (i = 0; i < 4; ++i) {
+        const limb tmp = copy & (in[i] ^ out[i]);
+        out[i] ^= tmp;
+    }
+}
+
+/******************************************************************************/
+/*-
+ *                       ELLIPTIC CURVE POINT OPERATIONS
+ *
+ * Points are represented in Jacobian projective coordinates:
+ * (X, Y, Z) corresponds to the affine point (X/Z^2, Y/Z^3),
+ * or to the point at infinity if Z == 0.
+ *
+ */
+
+/*-
+ * Double an elliptic curve point:
+ * (X', Y', Z') = 2 * (X, Y, Z), where
+ * X' = (3 * (X - Z^2) * (X + Z^2))^2 - 8 * X * Y^2
+ * Y' = 3 * (X - Z^2) * (X + Z^2) * (4 * X * Y^2 - X') - 8 * Y^2
+ * Z' = (Y + Z)^2 - Y^2 - Z^2 = 2 * Y * Z
+ * Outputs can equal corresponding inputs, i.e., x_out == x_in is allowed,
+ * while x_out == y_in is not (maybe this works, but it's not tested).
+ */
+static void
+point_double(felem x_out, felem y_out, felem z_out,
+             const felem x_in, const felem y_in, const felem z_in)
+{
+    widefelem tmp, tmp2;
+    felem delta, gamma, beta, alpha, ftmp, ftmp2;
+
+    felem_assign(ftmp, x_in);
+    felem_assign(ftmp2, x_in);
+
+    /* delta = z^2 */
+    felem_square(tmp, z_in);
+    felem_reduce(delta, tmp);
+
+    /* gamma = y^2 */
+    felem_square(tmp, y_in);
+    felem_reduce(gamma, tmp);
+
+    /* beta = x*gamma */
+    felem_mul(tmp, x_in, gamma);
+    felem_reduce(beta, tmp);
+
+    /* alpha = 3*(x-delta)*(x+delta) */
+    felem_diff(ftmp, delta);
+    /* ftmp[i] < 2^57 + 2^58 + 2 < 2^59 */
+    felem_sum(ftmp2, delta);
+    /* ftmp2[i] < 2^57 + 2^57 = 2^58 */
+    felem_scalar(ftmp2, 3);
+    /* ftmp2[i] < 3 * 2^58 < 2^60 */
+    felem_mul(tmp, ftmp, ftmp2);
+    /* tmp[i] < 2^60 * 2^59 * 4 = 2^121 */
+    felem_reduce(alpha, tmp);
+
+    /* x' = alpha^2 - 8*beta */
+    felem_square(tmp, alpha);
+    /* tmp[i] < 4 * 2^57 * 2^57 = 2^116 */
+    felem_assign(ftmp, beta);
+    felem_scalar(ftmp, 8);
+    /* ftmp[i] < 8 * 2^57 = 2^60 */
+    felem_diff_128_64(tmp, ftmp);
+    /* tmp[i] < 2^116 + 2^64 + 8 < 2^117 */
+    felem_reduce(x_out, tmp);
+
+    /* z' = (y + z)^2 - gamma - delta */
+    felem_sum(delta, gamma);
+    /* delta[i] < 2^57 + 2^57 = 2^58 */
+    felem_assign(ftmp, y_in);
+    felem_sum(ftmp, z_in);
+    /* ftmp[i] < 2^57 + 2^57 = 2^58 */
+    felem_square(tmp, ftmp);
+    /* tmp[i] < 4 * 2^58 * 2^58 = 2^118 */
+    felem_diff_128_64(tmp, delta);
+    /* tmp[i] < 2^118 + 2^64 + 8 < 2^119 */
+    felem_reduce(z_out, tmp);
+
+    /* y' = alpha*(4*beta - x') - 8*gamma^2 */
+    felem_scalar(beta, 4);
+    /* beta[i] < 4 * 2^57 = 2^59 */
+    felem_diff(beta, x_out);
+    /* beta[i] < 2^59 + 2^58 + 2 < 2^60 */
+    felem_mul(tmp, alpha, beta);
+    /* tmp[i] < 4 * 2^57 * 2^60 = 2^119 */
+    felem_square(tmp2, gamma);
+    /* tmp2[i] < 4 * 2^57 * 2^57 = 2^116 */
+    widefelem_scalar(tmp2, 8);
+    /* tmp2[i] < 8 * 2^116 = 2^119 */
+    widefelem_diff(tmp, tmp2);
+    /* tmp[i] < 2^119 + 2^120 < 2^121 */
+    felem_reduce(y_out, tmp);
+}
+
+/*-
+ * Add two elliptic curve points:
+ * (X_1, Y_1, Z_1) + (X_2, Y_2, Z_2) = (X_3, Y_3, Z_3), where
+ * X_3 = (Z_1^3 * Y_2 - Z_2^3 * Y_1)^2 - (Z_1^2 * X_2 - Z_2^2 * X_1)^3 -
+ * 2 * Z_2^2 * X_1 * (Z_1^2 * X_2 - Z_2^2 * X_1)^2
+ * Y_3 = (Z_1^3 * Y_2 - Z_2^3 * Y_1) * (Z_2^2 * X_1 * (Z_1^2 * X_2 - Z_2^2 * X_1)^2 - X_3) -
+ *        Z_2^3 * Y_1 * (Z_1^2 * X_2 - Z_2^2 * X_1)^3
+ * Z_3 = (Z_1^2 * X_2 - Z_2^2 * X_1) * (Z_1 * Z_2)
+ *
+ * This runs faster if 'mixed' is set, which requires Z_2 = 1 or Z_2 = 0.
+ */
+
+/*
+ * This function is not entirely constant-time: it includes a branch for
+ * checking whether the two input points are equal, (while not equal to the
+ * point at infinity). This case never happens during single point
+ * multiplication, so there is no timing leak for ECDH or ECDSA signing.
+ */
+static void point_add(felem x3, felem y3, felem z3,
+                      const felem x1, const felem y1, const felem z1,
+                      const int mixed, const felem x2, const felem y2,
+                      const felem z2)
+{
+    felem ftmp, ftmp2, ftmp3, ftmp4, ftmp5, x_out, y_out, z_out;
+    widefelem tmp, tmp2;
+    limb z1_is_zero, z2_is_zero, x_equal, y_equal;
+
+    if (!mixed) {
+        /* ftmp2 = z2^2 */
+        felem_square(tmp, z2);
+        felem_reduce(ftmp2, tmp);
+
+        /* ftmp4 = z2^3 */
+        felem_mul(tmp, ftmp2, z2);
+        felem_reduce(ftmp4, tmp);
+
+        /* ftmp4 = z2^3*y1 */
+        felem_mul(tmp2, ftmp4, y1);
+        felem_reduce(ftmp4, tmp2);
+
+        /* ftmp2 = z2^2*x1 */
+        felem_mul(tmp2, ftmp2, x1);
+        felem_reduce(ftmp2, tmp2);
+    } else {
+        /*
+         * We'll assume z2 = 1 (special case z2 = 0 is handled later)
+         */
+
+        /* ftmp4 = z2^3*y1 */
+        felem_assign(ftmp4, y1);
+
+        /* ftmp2 = z2^2*x1 */
+        felem_assign(ftmp2, x1);
+    }
+
+    /* ftmp = z1^2 */
+    felem_square(tmp, z1);
+    felem_reduce(ftmp, tmp);
+
+    /* ftmp3 = z1^3 */
+    felem_mul(tmp, ftmp, z1);
+    felem_reduce(ftmp3, tmp);
+
+    /* tmp = z1^3*y2 */
+    felem_mul(tmp, ftmp3, y2);
+    /* tmp[i] < 4 * 2^57 * 2^57 = 2^116 */
+
+    /* ftmp3 = z1^3*y2 - z2^3*y1 */
+    felem_diff_128_64(tmp, ftmp4);
+    /* tmp[i] < 2^116 + 2^64 + 8 < 2^117 */
+    felem_reduce(ftmp3, tmp);
+
+    /* tmp = z1^2*x2 */
+    felem_mul(tmp, ftmp, x2);
+    /* tmp[i] < 4 * 2^57 * 2^57 = 2^116 */
+
+    /* ftmp = z1^2*x2 - z2^2*x1 */
+    felem_diff_128_64(tmp, ftmp2);
+    /* tmp[i] < 2^116 + 2^64 + 8 < 2^117 */
+    felem_reduce(ftmp, tmp);
+
+    /*
+     * the formulae are incorrect if the points are equal so we check for
+     * this and do doubling if this happens
+     */
+    x_equal = felem_is_zero(ftmp);
+    y_equal = felem_is_zero(ftmp3);
+    z1_is_zero = felem_is_zero(z1);
+    z2_is_zero = felem_is_zero(z2);
+    /* In affine coordinates, (X_1, Y_1) == (X_2, Y_2) */
+    if (x_equal && y_equal && !z1_is_zero && !z2_is_zero) {
+        point_double(x3, y3, z3, x1, y1, z1);
+        return;
+    }
+
+    /* ftmp5 = z1*z2 */
+    if (!mixed) {
+        felem_mul(tmp, z1, z2);
+        felem_reduce(ftmp5, tmp);
+    } else {
+        /* special case z2 = 0 is handled later */
+        felem_assign(ftmp5, z1);
+    }
+
+    /* z_out = (z1^2*x2 - z2^2*x1)*(z1*z2) */
+    felem_mul(tmp, ftmp, ftmp5);
+    felem_reduce(z_out, tmp);
+
+    /* ftmp = (z1^2*x2 - z2^2*x1)^2 */
+    felem_assign(ftmp5, ftmp);
+    felem_square(tmp, ftmp);
+    felem_reduce(ftmp, tmp);
+
+    /* ftmp5 = (z1^2*x2 - z2^2*x1)^3 */
+    felem_mul(tmp, ftmp, ftmp5);
+    felem_reduce(ftmp5, tmp);
+
+    /* ftmp2 = z2^2*x1*(z1^2*x2 - z2^2*x1)^2 */
+    felem_mul(tmp, ftmp2, ftmp);
+    felem_reduce(ftmp2, tmp);
+
+    /* tmp = z2^3*y1*(z1^2*x2 - z2^2*x1)^3 */
+    felem_mul(tmp, ftmp4, ftmp5);
+    /* tmp[i] < 4 * 2^57 * 2^57 = 2^116 */
+
+    /* tmp2 = (z1^3*y2 - z2^3*y1)^2 */
+    felem_square(tmp2, ftmp3);
+    /* tmp2[i] < 4 * 2^57 * 2^57 < 2^116 */
+
+    /* tmp2 = (z1^3*y2 - z2^3*y1)^2 - (z1^2*x2 - z2^2*x1)^3 */
+    felem_diff_128_64(tmp2, ftmp5);
+    /* tmp2[i] < 2^116 + 2^64 + 8 < 2^117 */
+
+    /* ftmp5 = 2*z2^2*x1*(z1^2*x2 - z2^2*x1)^2 */
+    felem_assign(ftmp5, ftmp2);
+    felem_scalar(ftmp5, 2);
+    /* ftmp5[i] < 2 * 2^57 = 2^58 */
+
+    /*-
+     * x_out = (z1^3*y2 - z2^3*y1)^2 - (z1^2*x2 - z2^2*x1)^3 -
+     *  2*z2^2*x1*(z1^2*x2 - z2^2*x1)^2
+     */
+    felem_diff_128_64(tmp2, ftmp5);
+    /* tmp2[i] < 2^117 + 2^64 + 8 < 2^118 */
+    felem_reduce(x_out, tmp2);
+
+    /* ftmp2 = z2^2*x1*(z1^2*x2 - z2^2*x1)^2 - x_out */
+    felem_diff(ftmp2, x_out);
+    /* ftmp2[i] < 2^57 + 2^58 + 2 < 2^59 */
+
+    /*
+     * tmp2 = (z1^3*y2 - z2^3*y1)*(z2^2*x1*(z1^2*x2 - z2^2*x1)^2 - x_out)
+     */
+    felem_mul(tmp2, ftmp3, ftmp2);
+    /* tmp2[i] < 4 * 2^57 * 2^59 = 2^118 */
+
+    /*-
+     * y_out = (z1^3*y2 - z2^3*y1)*(z2^2*x1*(z1^2*x2 - z2^2*x1)^2 - x_out) -
+     *  z2^3*y1*(z1^2*x2 - z2^2*x1)^3
+     */
+    widefelem_diff(tmp2, tmp);
+    /* tmp2[i] < 2^118 + 2^120 < 2^121 */
+    felem_reduce(y_out, tmp2);
+
+    /*
+     * the result (x_out, y_out, z_out) is incorrect if one of the inputs is
+     * the point at infinity, so we need to check for this separately
+     */
+
+    /*
+     * if point 1 is at infinity, copy point 2 to output, and vice versa
+     */
+    copy_conditional(x_out, x2, z1_is_zero);
+    copy_conditional(x_out, x1, z2_is_zero);
+    copy_conditional(y_out, y2, z1_is_zero);
+    copy_conditional(y_out, y1, z2_is_zero);
+    copy_conditional(z_out, z2, z1_is_zero);
+    copy_conditional(z_out, z1, z2_is_zero);
+    felem_assign(x3, x_out);
+    felem_assign(y3, y_out);
+    felem_assign(z3, z_out);
+}
+
+/*
+ * select_point selects the |idx|th point from a precomputation table and
+ * copies it to out.
+ * The pre_comp array argument should be size of |size| argument
+ */
+static void select_point(const u64 idx, unsigned int size,
+                         const felem pre_comp[][3], felem out[3])
+{
+    unsigned i, j;
+    limb *outlimbs = &out[0][0];
+
+    memset(out, 0, sizeof(*out) * 3);
+    for (i = 0; i < size; i++) {
+        const limb *inlimbs = &pre_comp[i][0][0];
+        u64 mask = i ^ idx;
+        mask |= mask >> 4;
+        mask |= mask >> 2;
+        mask |= mask >> 1;
+        mask &= 1;
+        mask--;
+        for (j = 0; j < 4 * 3; j++)
+            outlimbs[j] |= inlimbs[j] & mask;
+    }
+}
+
+/* get_bit returns the |i|th bit in |in| */
+static char get_bit(const felem_bytearray in, unsigned i)
+{
+    if (i >= 224)
+        return 0;
+    return (in[i >> 3] >> (i & 7)) & 1;
+}
+
+/*
+ * Interleaved point multiplication using precomputed point multiples: The
+ * small point multiples 0*P, 1*P, ..., 16*P are in pre_comp[], the scalars
+ * in scalars[]. If g_scalar is non-NULL, we also add this multiple of the
+ * generator, using certain (large) precomputed multiples in g_pre_comp.
+ * Output point (X, Y, Z) is stored in x_out, y_out, z_out
+ */
+static void batch_mul(felem x_out, felem y_out, felem z_out,
+                      const felem_bytearray scalars[],
+                      const unsigned num_points, const u8 *g_scalar,
+                      const int mixed, const felem pre_comp[][17][3],
+                      const felem g_pre_comp[2][16][3])
+{
+    int i, skip;
+    unsigned num;
+    unsigned gen_mul = (g_scalar != NULL);
+    felem nq[3], tmp[4];
+    u64 bits;
+    u8 sign, digit;
+
+    /* set nq to the point at infinity */
+    memset(nq, 0, sizeof(nq));
+
+    /*
+     * Loop over all scalars msb-to-lsb, interleaving additions of multiples
+     * of the generator (two in each of the last 28 rounds) and additions of
+     * other points multiples (every 5th round).
+     */
+    skip = 1;                   /* save two point operations in the first
+                                 * round */
+    for (i = (num_points ? 220 : 27); i >= 0; --i) {
+        /* double */
+        if (!skip)
+            point_double(nq[0], nq[1], nq[2], nq[0], nq[1], nq[2]);
+
+        /* add multiples of the generator */
+        if (gen_mul && (i <= 27)) {
+            /* first, look 28 bits upwards */
+            bits = get_bit(g_scalar, i + 196) << 3;
+            bits |= get_bit(g_scalar, i + 140) << 2;
+            bits |= get_bit(g_scalar, i + 84) << 1;
+            bits |= get_bit(g_scalar, i + 28);
+            /* select the point to add, in constant time */
+            select_point(bits, 16, g_pre_comp[1], tmp);
+
+            if (!skip) {
+                /* value 1 below is argument for "mixed" */
+                point_add(nq[0], nq[1], nq[2],
+                          nq[0], nq[1], nq[2], 1, tmp[0], tmp[1], tmp[2]);
+            } else {
+                memcpy(nq, tmp, 3 * sizeof(felem));
+                skip = 0;
+            }
+
+            /* second, look at the current position */
+            bits = get_bit(g_scalar, i + 168) << 3;
+            bits |= get_bit(g_scalar, i + 112) << 2;
+            bits |= get_bit(g_scalar, i + 56) << 1;
+            bits |= get_bit(g_scalar, i);
+            /* select the point to add, in constant time */
+            select_point(bits, 16, g_pre_comp[0], tmp);
+            point_add(nq[0], nq[1], nq[2],
+                      nq[0], nq[1], nq[2],
+                      1 /* mixed */ , tmp[0], tmp[1], tmp[2]);
+        }
+
+        /* do other additions every 5 doublings */
+        if (num_points && (i % 5 == 0)) {
+            /* loop over all scalars */
+            for (num = 0; num < num_points; ++num) {
+                bits = get_bit(scalars[num], i + 4) << 5;
+                bits |= get_bit(scalars[num], i + 3) << 4;
+                bits |= get_bit(scalars[num], i + 2) << 3;
+                bits |= get_bit(scalars[num], i + 1) << 2;
+                bits |= get_bit(scalars[num], i) << 1;
+                bits |= get_bit(scalars[num], i - 1);
+                ec_GFp_nistp_recode_scalar_bits(&sign, &digit, bits);
+
+                /* select the point to add or subtract */
+                select_point(digit, 17, pre_comp[num], tmp);
+                felem_neg(tmp[3], tmp[1]); /* (X, -Y, Z) is the negative
+                                            * point */
+                copy_conditional(tmp[1], tmp[3], sign);
+
+                if (!skip) {
+                    point_add(nq[0], nq[1], nq[2],
+                              nq[0], nq[1], nq[2],
+                              mixed, tmp[0], tmp[1], tmp[2]);
+                } else {
+                    memcpy(nq, tmp, 3 * sizeof(felem));
+                    skip = 0;
+                }
+            }
+        }
+    }
+    felem_assign(x_out, nq[0]);
+    felem_assign(y_out, nq[1]);
+    felem_assign(z_out, nq[2]);
+}
+
+/******************************************************************************/
+/*
+ * FUNCTIONS TO MANAGE PRECOMPUTATION
+ */
+
+static NISTP224_PRE_COMP *nistp224_pre_comp_new()
+{
+    NISTP224_PRE_COMP *ret = OPENSSL_zalloc(sizeof(*ret));
+
+    if (!ret) {
+        ECerr(EC_F_NISTP224_PRE_COMP_NEW, ERR_R_MALLOC_FAILURE);
+        return ret;
+    }
+
+    ret->references = 1;
+
+    ret->lock = CRYPTO_THREAD_lock_new();
+    if (ret->lock == NULL) {
+        ECerr(EC_F_NISTP224_PRE_COMP_NEW, ERR_R_MALLOC_FAILURE);
+        OPENSSL_free(ret);
+        return NULL;
+    }
+    return ret;
+}
+
+NISTP224_PRE_COMP *EC_nistp224_pre_comp_dup(NISTP224_PRE_COMP *p)
+{
+    int i;
+    if (p != NULL)
+        CRYPTO_atomic_add(&p->references, 1, &i, p->lock);
+    return p;
+}
+
+void EC_nistp224_pre_comp_free(NISTP224_PRE_COMP *p)
+{
+    int i;
+
+    if (p == NULL)
+        return;
+
+    CRYPTO_atomic_add(&p->references, -1, &i, p->lock);
+    REF_PRINT_COUNT("EC_nistp224", x);
+    if (i > 0)
+        return;
+    REF_ASSERT_ISNT(i < 0);
+
+    CRYPTO_THREAD_lock_free(p->lock);
+    OPENSSL_free(p);
+}
+
+/******************************************************************************/
+/*
+ * OPENSSL EC_METHOD FUNCTIONS
+ */
+
+int ec_GFp_nistp224_group_init(EC_GROUP *group)
+{
+    int ret;
+    ret = ec_GFp_simple_group_init(group);
+    group->a_is_minus3 = 1;
+    return ret;
+}
+
+int ec_GFp_nistp224_group_set_curve(EC_GROUP *group, const BIGNUM *p,
+                                    const BIGNUM *a, const BIGNUM *b,
+                                    BN_CTX *ctx)
+{
+    int ret = 0;
+    BN_CTX *new_ctx = NULL;
+    BIGNUM *curve_p, *curve_a, *curve_b;
+
+    if (ctx == NULL)
+        if ((ctx = new_ctx = BN_CTX_new()) == NULL)
+            return 0;
+    BN_CTX_start(ctx);
+    if (((curve_p = BN_CTX_get(ctx)) == NULL) ||
+        ((curve_a = BN_CTX_get(ctx)) == NULL) ||
+        ((curve_b = BN_CTX_get(ctx)) == NULL))
+        goto err;
+    BN_bin2bn(nistp224_curve_params[0], sizeof(felem_bytearray), curve_p);
+    BN_bin2bn(nistp224_curve_params[1], sizeof(felem_bytearray), curve_a);
+    BN_bin2bn(nistp224_curve_params[2], sizeof(felem_bytearray), curve_b);
+    if ((BN_cmp(curve_p, p)) || (BN_cmp(curve_a, a)) || (BN_cmp(curve_b, b))) {
+        ECerr(EC_F_EC_GFP_NISTP224_GROUP_SET_CURVE,
+              EC_R_WRONG_CURVE_PARAMETERS);
+        goto err;
+    }
+    group->field_mod_func = BN_nist_mod_224;
+    ret = ec_GFp_simple_group_set_curve(group, p, a, b, ctx);
+ err:
+    BN_CTX_end(ctx);
+    BN_CTX_free(new_ctx);
+    return ret;
+}
+
+/*
+ * Takes the Jacobian coordinates (X, Y, Z) of a point and returns (X', Y') =
+ * (X/Z^2, Y/Z^3)
+ */
+int ec_GFp_nistp224_point_get_affine_coordinates(const EC_GROUP *group,
+                                                 const EC_POINT *point,
+                                                 BIGNUM *x, BIGNUM *y,
+                                                 BN_CTX *ctx)
+{
+    felem z1, z2, x_in, y_in, x_out, y_out;
+    widefelem tmp;
+
+    if (EC_POINT_is_at_infinity(group, point)) {
+        ECerr(EC_F_EC_GFP_NISTP224_POINT_GET_AFFINE_COORDINATES,
+              EC_R_POINT_AT_INFINITY);
+        return 0;
+    }
+    if ((!BN_to_felem(x_in, point->X)) || (!BN_to_felem(y_in, point->Y)) ||
+        (!BN_to_felem(z1, point->Z)))
+        return 0;
+    felem_inv(z2, z1);
+    felem_square(tmp, z2);
+    felem_reduce(z1, tmp);
+    felem_mul(tmp, x_in, z1);
+    felem_reduce(x_in, tmp);
+    felem_contract(x_out, x_in);
+    if (x != NULL) {
+        if (!felem_to_BN(x, x_out)) {
+            ECerr(EC_F_EC_GFP_NISTP224_POINT_GET_AFFINE_COORDINATES,
+                  ERR_R_BN_LIB);
+            return 0;
+        }
+    }
+    felem_mul(tmp, z1, z2);
+    felem_reduce(z1, tmp);
+    felem_mul(tmp, y_in, z1);
+    felem_reduce(y_in, tmp);
+    felem_contract(y_out, y_in);
+    if (y != NULL) {
+        if (!felem_to_BN(y, y_out)) {
+            ECerr(EC_F_EC_GFP_NISTP224_POINT_GET_AFFINE_COORDINATES,
+                  ERR_R_BN_LIB);
+            return 0;
+        }
+    }
+    return 1;
+}
+
+static void make_points_affine(size_t num, felem points[ /* num */ ][3],
+                               felem tmp_felems[ /* num+1 */ ])
+{
+    /*
+     * Runs in constant time, unless an input is the point at infinity (which
+     * normally shouldn't happen).
+     */
+    ec_GFp_nistp_points_make_affine_internal(num,
+                                             points,
+                                             sizeof(felem),
+                                             tmp_felems,
+                                             (void (*)(void *))felem_one,
+                                             (int (*)(const void *))
+                                             felem_is_zero_int,
+                                             (void (*)(void *, const void *))
+                                             felem_assign,
+                                             (void (*)(void *, const void *))
+                                             felem_square_reduce, (void (*)
+                                                                   (void *,
+                                                                    const void
+                                                                    *,
+                                                                    const void
+                                                                    *))
+                                             felem_mul_reduce,
+                                             (void (*)(void *, const void *))
+                                             felem_inv,
+                                             (void (*)(void *, const void *))
+                                             felem_contract);
+}
+
+/*
+ * Computes scalar*generator + \sum scalars[i]*points[i], ignoring NULL
+ * values Result is stored in r (r can equal one of the inputs).
+ */
+int ec_GFp_nistp224_points_mul(const EC_GROUP *group, EC_POINT *r,
+                               const BIGNUM *scalar, size_t num,
+                               const EC_POINT *points[],
+                               const BIGNUM *scalars[], BN_CTX *ctx)
+{
+    int ret = 0;
+    int j;
+    unsigned i;
+    int mixed = 0;
+    BN_CTX *new_ctx = NULL;
+    BIGNUM *x, *y, *z, *tmp_scalar;
+    felem_bytearray g_secret;
+    felem_bytearray *secrets = NULL;
+    felem (*pre_comp)[17][3] = NULL;
+    felem *tmp_felems = NULL;
+    felem_bytearray tmp;
+    unsigned num_bytes;
+    int have_pre_comp = 0;
+    size_t num_points = num;
+    felem x_in, y_in, z_in, x_out, y_out, z_out;
+    NISTP224_PRE_COMP *pre = NULL;
+    const felem(*g_pre_comp)[16][3] = NULL;
+    EC_POINT *generator = NULL;
+    const EC_POINT *p = NULL;
+    const BIGNUM *p_scalar = NULL;
+
+    if (ctx == NULL)
+        if ((ctx = new_ctx = BN_CTX_new()) == NULL)
+            return 0;
+    BN_CTX_start(ctx);
+    if (((x = BN_CTX_get(ctx)) == NULL) ||
+        ((y = BN_CTX_get(ctx)) == NULL) ||
+        ((z = BN_CTX_get(ctx)) == NULL) ||
+        ((tmp_scalar = BN_CTX_get(ctx)) == NULL))
+        goto err;
+
+    if (scalar != NULL) {
+        pre = group->pre_comp.nistp224;
+        if (pre)
+            /* we have precomputation, try to use it */
+            g_pre_comp = (const felem(*)[16][3])pre->g_pre_comp;
+        else
+            /* try to use the standard precomputation */
+            g_pre_comp = &gmul[0];
+        generator = EC_POINT_new(group);
+        if (generator == NULL)
+            goto err;
+        /* get the generator from precomputation */
+        if (!felem_to_BN(x, g_pre_comp[0][1][0]) ||
+            !felem_to_BN(y, g_pre_comp[0][1][1]) ||
+            !felem_to_BN(z, g_pre_comp[0][1][2])) {
+            ECerr(EC_F_EC_GFP_NISTP224_POINTS_MUL, ERR_R_BN_LIB);
+            goto err;
+        }
+        if (!EC_POINT_set_Jprojective_coordinates_GFp(group,
+                                                      generator, x, y, z,
+                                                      ctx))
+            goto err;
+        if (0 == EC_POINT_cmp(group, generator, group->generator, ctx))
+            /* precomputation matches generator */
+            have_pre_comp = 1;
+        else
+            /*
+             * we don't have valid precomputation: treat the generator as a
+             * random point
+             */
+            num_points = num_points + 1;
+    }
+
+    if (num_points > 0) {
+        if (num_points >= 3) {
+            /*
+             * unless we precompute multiples for just one or two points,
+             * converting those into affine form is time well spent
+             */
+            mixed = 1;
+        }
+        secrets = OPENSSL_zalloc(sizeof(*secrets) * num_points);
+        pre_comp = OPENSSL_zalloc(sizeof(*pre_comp) * num_points);
+        if (mixed)
+            tmp_felems =
+                OPENSSL_malloc(sizeof(felem) * (num_points * 17 + 1));
+        if ((secrets == NULL) || (pre_comp == NULL)
+            || (mixed && (tmp_felems == NULL))) {
+            ECerr(EC_F_EC_GFP_NISTP224_POINTS_MUL, ERR_R_MALLOC_FAILURE);
+            goto err;
+        }
+
+        /*
+         * we treat NULL scalars as 0, and NULL points as points at infinity,
+         * i.e., they contribute nothing to the linear combination
+         */
+        for (i = 0; i < num_points; ++i) {
+            if (i == num)
+                /* the generator */
+            {
+                p = EC_GROUP_get0_generator(group);
+                p_scalar = scalar;
+            } else
+                /* the i^th point */
+            {
+                p = points[i];
+                p_scalar = scalars[i];
+            }
+            if ((p_scalar != NULL) && (p != NULL)) {
+                /* reduce scalar to 0 <= scalar < 2^224 */
+                if ((BN_num_bits(p_scalar) > 224)
+                    || (BN_is_negative(p_scalar))) {
+                    /*
+                     * this is an unusual input, and we don't guarantee
+                     * constant-timeness
+                     */
+                    if (!BN_nnmod(tmp_scalar, p_scalar, group->order, ctx)) {
+                        ECerr(EC_F_EC_GFP_NISTP224_POINTS_MUL, ERR_R_BN_LIB);
+                        goto err;
+                    }
+                    num_bytes = BN_bn2bin(tmp_scalar, tmp);
+                } else
+                    num_bytes = BN_bn2bin(p_scalar, tmp);
+                flip_endian(secrets[i], tmp, num_bytes);
+                /* precompute multiples */
+                if ((!BN_to_felem(x_out, p->X)) ||
+                    (!BN_to_felem(y_out, p->Y)) ||
+                    (!BN_to_felem(z_out, p->Z)))
+                    goto err;
+                felem_assign(pre_comp[i][1][0], x_out);
+                felem_assign(pre_comp[i][1][1], y_out);
+                felem_assign(pre_comp[i][1][2], z_out);
+                for (j = 2; j <= 16; ++j) {
+                    if (j & 1) {
+                        point_add(pre_comp[i][j][0], pre_comp[i][j][1],
+                                  pre_comp[i][j][2], pre_comp[i][1][0],
+                                  pre_comp[i][1][1], pre_comp[i][1][2], 0,
+                                  pre_comp[i][j - 1][0],
+                                  pre_comp[i][j - 1][1],
+                                  pre_comp[i][j - 1][2]);
+                    } else {
+                        point_double(pre_comp[i][j][0], pre_comp[i][j][1],
+                                     pre_comp[i][j][2], pre_comp[i][j / 2][0],
+                                     pre_comp[i][j / 2][1],
+                                     pre_comp[i][j / 2][2]);
+                    }
+                }
+            }
+        }
+        if (mixed)
+            make_points_affine(num_points * 17, pre_comp[0], tmp_felems);
+    }
+
+    /* the scalar for the generator */
+    if ((scalar != NULL) && (have_pre_comp)) {
+        memset(g_secret, 0, sizeof(g_secret));
+        /* reduce scalar to 0 <= scalar < 2^224 */
+        if ((BN_num_bits(scalar) > 224) || (BN_is_negative(scalar))) {
+            /*
+             * this is an unusual input, and we don't guarantee
+             * constant-timeness
+             */
+            if (!BN_nnmod(tmp_scalar, scalar, group->order, ctx)) {
+                ECerr(EC_F_EC_GFP_NISTP224_POINTS_MUL, ERR_R_BN_LIB);
+                goto err;
+            }
+            num_bytes = BN_bn2bin(tmp_scalar, tmp);
+        } else
+            num_bytes = BN_bn2bin(scalar, tmp);
+        flip_endian(g_secret, tmp, num_bytes);
+        /* do the multiplication with generator precomputation */
+        batch_mul(x_out, y_out, z_out,
+                  (const felem_bytearray(*))secrets, num_points,
+                  g_secret,
+                  mixed, (const felem(*)[17][3])pre_comp, g_pre_comp);
+    } else
+        /* do the multiplication without generator precomputation */
+        batch_mul(x_out, y_out, z_out,
+                  (const felem_bytearray(*))secrets, num_points,
+                  NULL, mixed, (const felem(*)[17][3])pre_comp, NULL);
+    /* reduce the output to its unique minimal representation */
+    felem_contract(x_in, x_out);
+    felem_contract(y_in, y_out);
+    felem_contract(z_in, z_out);
+    if ((!felem_to_BN(x, x_in)) || (!felem_to_BN(y, y_in)) ||
+        (!felem_to_BN(z, z_in))) {
+        ECerr(EC_F_EC_GFP_NISTP224_POINTS_MUL, ERR_R_BN_LIB);
+        goto err;
+    }
+    ret = EC_POINT_set_Jprojective_coordinates_GFp(group, r, x, y, z, ctx);
+
+ err:
+    BN_CTX_end(ctx);
+    EC_POINT_free(generator);
+    BN_CTX_free(new_ctx);
+    OPENSSL_free(secrets);
+    OPENSSL_free(pre_comp);
+    OPENSSL_free(tmp_felems);
+    return ret;
+}
+
+int ec_GFp_nistp224_precompute_mult(EC_GROUP *group, BN_CTX *ctx)
+{
+    int ret = 0;
+    NISTP224_PRE_COMP *pre = NULL;
+    int i, j;
+    BN_CTX *new_ctx = NULL;
+    BIGNUM *x, *y;
+    EC_POINT *generator = NULL;
+    felem tmp_felems[32];
+
+    /* throw away old precomputation */
+    EC_pre_comp_free(group);
+    if (ctx == NULL)
+        if ((ctx = new_ctx = BN_CTX_new()) == NULL)
+            return 0;
+    BN_CTX_start(ctx);
+    if (((x = BN_CTX_get(ctx)) == NULL) || ((y = BN_CTX_get(ctx)) == NULL))
+        goto err;
+    /* get the generator */
+    if (group->generator == NULL)
+        goto err;
+    generator = EC_POINT_new(group);
+    if (generator == NULL)
+        goto err;
+    BN_bin2bn(nistp224_curve_params[3], sizeof(felem_bytearray), x);
+    BN_bin2bn(nistp224_curve_params[4], sizeof(felem_bytearray), y);
+    if (!EC_POINT_set_affine_coordinates_GFp(group, generator, x, y, ctx))
+        goto err;
+    if ((pre = nistp224_pre_comp_new()) == NULL)
+        goto err;
+    /*
+     * if the generator is the standard one, use built-in precomputation
+     */
+    if (0 == EC_POINT_cmp(group, generator, group->generator, ctx)) {
+        memcpy(pre->g_pre_comp, gmul, sizeof(pre->g_pre_comp));
+        goto done;
+    }
+    if ((!BN_to_felem(pre->g_pre_comp[0][1][0], group->generator->X)) ||
+        (!BN_to_felem(pre->g_pre_comp[0][1][1], group->generator->Y)) ||
+        (!BN_to_felem(pre->g_pre_comp[0][1][2], group->generator->Z)))
+        goto err;
+    /*
+     * compute 2^56*G, 2^112*G, 2^168*G for the first table, 2^28*G, 2^84*G,
+     * 2^140*G, 2^196*G for the second one
+     */
+    for (i = 1; i <= 8; i <<= 1) {
+        point_double(pre->g_pre_comp[1][i][0], pre->g_pre_comp[1][i][1],
+                     pre->g_pre_comp[1][i][2], pre->g_pre_comp[0][i][0],
+                     pre->g_pre_comp[0][i][1], pre->g_pre_comp[0][i][2]);
+        for (j = 0; j < 27; ++j) {
+            point_double(pre->g_pre_comp[1][i][0], pre->g_pre_comp[1][i][1],
+                         pre->g_pre_comp[1][i][2], pre->g_pre_comp[1][i][0],
+                         pre->g_pre_comp[1][i][1], pre->g_pre_comp[1][i][2]);
+        }
+        if (i == 8)
+            break;
+        point_double(pre->g_pre_comp[0][2 * i][0],
+                     pre->g_pre_comp[0][2 * i][1],
+                     pre->g_pre_comp[0][2 * i][2], pre->g_pre_comp[1][i][0],
+                     pre->g_pre_comp[1][i][1], pre->g_pre_comp[1][i][2]);
+        for (j = 0; j < 27; ++j) {
+            point_double(pre->g_pre_comp[0][2 * i][0],
+                         pre->g_pre_comp[0][2 * i][1],
+                         pre->g_pre_comp[0][2 * i][2],
+                         pre->g_pre_comp[0][2 * i][0],
+                         pre->g_pre_comp[0][2 * i][1],
+                         pre->g_pre_comp[0][2 * i][2]);
+        }
+    }
+    for (i = 0; i < 2; i++) {
+        /* g_pre_comp[i][0] is the point at infinity */
+        memset(pre->g_pre_comp[i][0], 0, sizeof(pre->g_pre_comp[i][0]));
+        /* the remaining multiples */
+        /* 2^56*G + 2^112*G resp. 2^84*G + 2^140*G */
+        point_add(pre->g_pre_comp[i][6][0], pre->g_pre_comp[i][6][1],
+                  pre->g_pre_comp[i][6][2], pre->g_pre_comp[i][4][0],
+                  pre->g_pre_comp[i][4][1], pre->g_pre_comp[i][4][2],
+                  0, pre->g_pre_comp[i][2][0], pre->g_pre_comp[i][2][1],
+                  pre->g_pre_comp[i][2][2]);
+        /* 2^56*G + 2^168*G resp. 2^84*G + 2^196*G */
+        point_add(pre->g_pre_comp[i][10][0], pre->g_pre_comp[i][10][1],
+                  pre->g_pre_comp[i][10][2], pre->g_pre_comp[i][8][0],
+                  pre->g_pre_comp[i][8][1], pre->g_pre_comp[i][8][2],
+                  0, pre->g_pre_comp[i][2][0], pre->g_pre_comp[i][2][1],
+                  pre->g_pre_comp[i][2][2]);
+        /* 2^112*G + 2^168*G resp. 2^140*G + 2^196*G */
+        point_add(pre->g_pre_comp[i][12][0], pre->g_pre_comp[i][12][1],
+                  pre->g_pre_comp[i][12][2], pre->g_pre_comp[i][8][0],
+                  pre->g_pre_comp[i][8][1], pre->g_pre_comp[i][8][2],
+                  0, pre->g_pre_comp[i][4][0], pre->g_pre_comp[i][4][1],
+                  pre->g_pre_comp[i][4][2]);
+        /*
+         * 2^56*G + 2^112*G + 2^168*G resp. 2^84*G + 2^140*G + 2^196*G
+         */
+        point_add(pre->g_pre_comp[i][14][0], pre->g_pre_comp[i][14][1],
+                  pre->g_pre_comp[i][14][2], pre->g_pre_comp[i][12][0],
+                  pre->g_pre_comp[i][12][1], pre->g_pre_comp[i][12][2],
+                  0, pre->g_pre_comp[i][2][0], pre->g_pre_comp[i][2][1],
+                  pre->g_pre_comp[i][2][2]);
+        for (j = 1; j < 8; ++j) {
+            /* odd multiples: add G resp. 2^28*G */
+            point_add(pre->g_pre_comp[i][2 * j + 1][0],
+                      pre->g_pre_comp[i][2 * j + 1][1],
+                      pre->g_pre_comp[i][2 * j + 1][2],
+                      pre->g_pre_comp[i][2 * j][0],
+                      pre->g_pre_comp[i][2 * j][1],
+                      pre->g_pre_comp[i][2 * j][2], 0,
+                      pre->g_pre_comp[i][1][0], pre->g_pre_comp[i][1][1],
+                      pre->g_pre_comp[i][1][2]);
+        }
+    }
+    make_points_affine(31, &(pre->g_pre_comp[0][1]), tmp_felems);
+
+ done:
+    SETPRECOMP(group, nistp224, pre);
+    pre = NULL;
+    ret = 1;
+ err:
+    BN_CTX_end(ctx);
+    EC_POINT_free(generator);
+    BN_CTX_free(new_ctx);
+    EC_nistp224_pre_comp_free(pre);
+    return ret;
+}
+
+int ec_GFp_nistp224_have_precompute_mult(const EC_GROUP *group)
+{
+    return HAVEPRECOMP(group, nistp224);
+}
+
+#endif
diff --git a/crypto/ec/ecp_nistp256.c b/crypto/ec/ecp_nistp256.c
new file mode 100644
index 000000000..8cd722285
--- /dev/null
+++ b/crypto/ec/ecp_nistp256.c
@@ -0,0 +1,2350 @@
+/*
+ * Copyright 2011-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/* Copyright 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ *
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/*
+ * A 64-bit implementation of the NIST P-256 elliptic curve point multiplication
+ *
+ * OpenSSL integration was taken from Emilia Kasper's work in ecp_nistp224.c.
+ * Otherwise based on Emilia's P224 work, which was inspired by my curve25519
+ * work which got its smarts from Daniel J. Bernstein's work on the same.
+ */
+
+#include 
+#ifdef OPENSSL_NO_EC_NISTP_64_GCC_128
+NON_EMPTY_TRANSLATION_UNIT
+#else
+
+# include 
+# include 
+# include 
+# include "ec_lcl.h"
+
+# if defined(__GNUC__) && (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1))
+  /* even with gcc, the typedef won't work for 32-bit platforms */
+typedef __uint128_t uint128_t;  /* nonstandard; implemented by gcc on 64-bit
+                                 * platforms */
+typedef __int128_t int128_t;
+# else
+#  error "Need GCC 3.1 or later to define type uint128_t"
+# endif
+
+typedef uint8_t u8;
+typedef uint32_t u32;
+typedef uint64_t u64;
+typedef int64_t s64;
+
+/*
+ * The underlying field. P256 operates over GF(2^256-2^224+2^192+2^96-1). We
+ * can serialise an element of this field into 32 bytes. We call this an
+ * felem_bytearray.
+ */
+
+typedef u8 felem_bytearray[32];
+
+/*
+ * These are the parameters of P256, taken from FIPS 186-3, page 86. These
+ * values are big-endian.
+ */
+static const felem_bytearray nistp256_curve_params[5] = {
+    {0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, /* p */
+     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+     0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
+     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
+    {0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, /* a = -3 */
+     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+     0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
+     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc}, /* b */
+    {0x5a, 0xc6, 0x35, 0xd8, 0xaa, 0x3a, 0x93, 0xe7,
+     0xb3, 0xeb, 0xbd, 0x55, 0x76, 0x98, 0x86, 0xbc,
+     0x65, 0x1d, 0x06, 0xb0, 0xcc, 0x53, 0xb0, 0xf6,
+     0x3b, 0xce, 0x3c, 0x3e, 0x27, 0xd2, 0x60, 0x4b},
+    {0x6b, 0x17, 0xd1, 0xf2, 0xe1, 0x2c, 0x42, 0x47, /* x */
+     0xf8, 0xbc, 0xe6, 0xe5, 0x63, 0xa4, 0x40, 0xf2,
+     0x77, 0x03, 0x7d, 0x81, 0x2d, 0xeb, 0x33, 0xa0,
+     0xf4, 0xa1, 0x39, 0x45, 0xd8, 0x98, 0xc2, 0x96},
+    {0x4f, 0xe3, 0x42, 0xe2, 0xfe, 0x1a, 0x7f, 0x9b, /* y */
+     0x8e, 0xe7, 0xeb, 0x4a, 0x7c, 0x0f, 0x9e, 0x16,
+     0x2b, 0xce, 0x33, 0x57, 0x6b, 0x31, 0x5e, 0xce,
+     0xcb, 0xb6, 0x40, 0x68, 0x37, 0xbf, 0x51, 0xf5}
+};
+
+/*-
+ * The representation of field elements.
+ * ------------------------------------
+ *
+ * We represent field elements with either four 128-bit values, eight 128-bit
+ * values, or four 64-bit values. The field element represented is:
+ *   v[0]*2^0 + v[1]*2^64 + v[2]*2^128 + v[3]*2^192  (mod p)
+ * or:
+ *   v[0]*2^0 + v[1]*2^64 + v[2]*2^128 + ... + v[8]*2^512  (mod p)
+ *
+ * 128-bit values are called 'limbs'. Since the limbs are spaced only 64 bits
+ * apart, but are 128-bits wide, the most significant bits of each limb overlap
+ * with the least significant bits of the next.
+ *
+ * A field element with four limbs is an 'felem'. One with eight limbs is a
+ * 'longfelem'
+ *
+ * A field element with four, 64-bit values is called a 'smallfelem'. Small
+ * values are used as intermediate values before multiplication.
+ */
+
+# define NLIMBS 4
+
+typedef uint128_t limb;
+typedef limb felem[NLIMBS];
+typedef limb longfelem[NLIMBS * 2];
+typedef u64 smallfelem[NLIMBS];
+
+/* This is the value of the prime as four 64-bit words, little-endian. */
+static const u64 kPrime[4] =
+    { 0xfffffffffffffffful, 0xffffffff, 0, 0xffffffff00000001ul };
+static const u64 bottom63bits = 0x7ffffffffffffffful;
+
+/*
+ * bin32_to_felem takes a little-endian byte array and converts it into felem
+ * form. This assumes that the CPU is little-endian.
+ */
+static void bin32_to_felem(felem out, const u8 in[32])
+{
+    out[0] = *((u64 *)&in[0]);
+    out[1] = *((u64 *)&in[8]);
+    out[2] = *((u64 *)&in[16]);
+    out[3] = *((u64 *)&in[24]);
+}
+
+/*
+ * smallfelem_to_bin32 takes a smallfelem and serialises into a little
+ * endian, 32 byte array. This assumes that the CPU is little-endian.
+ */
+static void smallfelem_to_bin32(u8 out[32], const smallfelem in)
+{
+    *((u64 *)&out[0]) = in[0];
+    *((u64 *)&out[8]) = in[1];
+    *((u64 *)&out[16]) = in[2];
+    *((u64 *)&out[24]) = in[3];
+}
+
+/* To preserve endianness when using BN_bn2bin and BN_bin2bn */
+static void flip_endian(u8 *out, const u8 *in, unsigned len)
+{
+    unsigned i;
+    for (i = 0; i < len; ++i)
+        out[i] = in[len - 1 - i];
+}
+
+/* BN_to_felem converts an OpenSSL BIGNUM into an felem */
+static int BN_to_felem(felem out, const BIGNUM *bn)
+{
+    felem_bytearray b_in;
+    felem_bytearray b_out;
+    unsigned num_bytes;
+
+    /* BN_bn2bin eats leading zeroes */
+    memset(b_out, 0, sizeof(b_out));
+    num_bytes = BN_num_bytes(bn);
+    if (num_bytes > sizeof b_out) {
+        ECerr(EC_F_BN_TO_FELEM, EC_R_BIGNUM_OUT_OF_RANGE);
+        return 0;
+    }
+    if (BN_is_negative(bn)) {
+        ECerr(EC_F_BN_TO_FELEM, EC_R_BIGNUM_OUT_OF_RANGE);
+        return 0;
+    }
+    num_bytes = BN_bn2bin(bn, b_in);
+    flip_endian(b_out, b_in, num_bytes);
+    bin32_to_felem(out, b_out);
+    return 1;
+}
+
+/* felem_to_BN converts an felem into an OpenSSL BIGNUM */
+static BIGNUM *smallfelem_to_BN(BIGNUM *out, const smallfelem in)
+{
+    felem_bytearray b_in, b_out;
+    smallfelem_to_bin32(b_in, in);
+    flip_endian(b_out, b_in, sizeof b_out);
+    return BN_bin2bn(b_out, sizeof b_out, out);
+}
+
+/*-
+ * Field operations
+ * ----------------
+ */
+
+static void smallfelem_one(smallfelem out)
+{
+    out[0] = 1;
+    out[1] = 0;
+    out[2] = 0;
+    out[3] = 0;
+}
+
+static void smallfelem_assign(smallfelem out, const smallfelem in)
+{
+    out[0] = in[0];
+    out[1] = in[1];
+    out[2] = in[2];
+    out[3] = in[3];
+}
+
+static void felem_assign(felem out, const felem in)
+{
+    out[0] = in[0];
+    out[1] = in[1];
+    out[2] = in[2];
+    out[3] = in[3];
+}
+
+/* felem_sum sets out = out + in. */
+static void felem_sum(felem out, const felem in)
+{
+    out[0] += in[0];
+    out[1] += in[1];
+    out[2] += in[2];
+    out[3] += in[3];
+}
+
+/* felem_small_sum sets out = out + in. */
+static void felem_small_sum(felem out, const smallfelem in)
+{
+    out[0] += in[0];
+    out[1] += in[1];
+    out[2] += in[2];
+    out[3] += in[3];
+}
+
+/* felem_scalar sets out = out * scalar */
+static void felem_scalar(felem out, const u64 scalar)
+{
+    out[0] *= scalar;
+    out[1] *= scalar;
+    out[2] *= scalar;
+    out[3] *= scalar;
+}
+
+/* longfelem_scalar sets out = out * scalar */
+static void longfelem_scalar(longfelem out, const u64 scalar)
+{
+    out[0] *= scalar;
+    out[1] *= scalar;
+    out[2] *= scalar;
+    out[3] *= scalar;
+    out[4] *= scalar;
+    out[5] *= scalar;
+    out[6] *= scalar;
+    out[7] *= scalar;
+}
+
+# define two105m41m9 (((limb)1) << 105) - (((limb)1) << 41) - (((limb)1) << 9)
+# define two105 (((limb)1) << 105)
+# define two105m41p9 (((limb)1) << 105) - (((limb)1) << 41) + (((limb)1) << 9)
+
+/* zero105 is 0 mod p */
+static const felem zero105 =
+    { two105m41m9, two105, two105m41p9, two105m41p9 };
+
+/*-
+ * smallfelem_neg sets |out| to |-small|
+ * On exit:
+ *   out[i] < out[i] + 2^105
+ */
+static void smallfelem_neg(felem out, const smallfelem small)
+{
+    /* In order to prevent underflow, we subtract from 0 mod p. */
+    out[0] = zero105[0] - small[0];
+    out[1] = zero105[1] - small[1];
+    out[2] = zero105[2] - small[2];
+    out[3] = zero105[3] - small[3];
+}
+
+/*-
+ * felem_diff subtracts |in| from |out|
+ * On entry:
+ *   in[i] < 2^104
+ * On exit:
+ *   out[i] < out[i] + 2^105
+ */
+static void felem_diff(felem out, const felem in)
+{
+    /*
+     * In order to prevent underflow, we add 0 mod p before subtracting.
+     */
+    out[0] += zero105[0];
+    out[1] += zero105[1];
+    out[2] += zero105[2];
+    out[3] += zero105[3];
+
+    out[0] -= in[0];
+    out[1] -= in[1];
+    out[2] -= in[2];
+    out[3] -= in[3];
+}
+
+# define two107m43m11 (((limb)1) << 107) - (((limb)1) << 43) - (((limb)1) << 11)
+# define two107 (((limb)1) << 107)
+# define two107m43p11 (((limb)1) << 107) - (((limb)1) << 43) + (((limb)1) << 11)
+
+/* zero107 is 0 mod p */
+static const felem zero107 =
+    { two107m43m11, two107, two107m43p11, two107m43p11 };
+
+/*-
+ * An alternative felem_diff for larger inputs |in|
+ * felem_diff_zero107 subtracts |in| from |out|
+ * On entry:
+ *   in[i] < 2^106
+ * On exit:
+ *   out[i] < out[i] + 2^107
+ */
+static void felem_diff_zero107(felem out, const felem in)
+{
+    /*
+     * In order to prevent underflow, we add 0 mod p before subtracting.
+     */
+    out[0] += zero107[0];
+    out[1] += zero107[1];
+    out[2] += zero107[2];
+    out[3] += zero107[3];
+
+    out[0] -= in[0];
+    out[1] -= in[1];
+    out[2] -= in[2];
+    out[3] -= in[3];
+}
+
+/*-
+ * longfelem_diff subtracts |in| from |out|
+ * On entry:
+ *   in[i] < 7*2^67
+ * On exit:
+ *   out[i] < out[i] + 2^70 + 2^40
+ */
+static void longfelem_diff(longfelem out, const longfelem in)
+{
+    static const limb two70m8p6 =
+        (((limb) 1) << 70) - (((limb) 1) << 8) + (((limb) 1) << 6);
+    static const limb two70p40 = (((limb) 1) << 70) + (((limb) 1) << 40);
+    static const limb two70 = (((limb) 1) << 70);
+    static const limb two70m40m38p6 =
+        (((limb) 1) << 70) - (((limb) 1) << 40) - (((limb) 1) << 38) +
+        (((limb) 1) << 6);
+    static const limb two70m6 = (((limb) 1) << 70) - (((limb) 1) << 6);
+
+    /* add 0 mod p to avoid underflow */
+    out[0] += two70m8p6;
+    out[1] += two70p40;
+    out[2] += two70;
+    out[3] += two70m40m38p6;
+    out[4] += two70m6;
+    out[5] += two70m6;
+    out[6] += two70m6;
+    out[7] += two70m6;
+
+    /* in[i] < 7*2^67 < 2^70 - 2^40 - 2^38 + 2^6 */
+    out[0] -= in[0];
+    out[1] -= in[1];
+    out[2] -= in[2];
+    out[3] -= in[3];
+    out[4] -= in[4];
+    out[5] -= in[5];
+    out[6] -= in[6];
+    out[7] -= in[7];
+}
+
+# define two64m0 (((limb)1) << 64) - 1
+# define two110p32m0 (((limb)1) << 110) + (((limb)1) << 32) - 1
+# define two64m46 (((limb)1) << 64) - (((limb)1) << 46)
+# define two64m32 (((limb)1) << 64) - (((limb)1) << 32)
+
+/* zero110 is 0 mod p */
+static const felem zero110 = { two64m0, two110p32m0, two64m46, two64m32 };
+
+/*-
+ * felem_shrink converts an felem into a smallfelem. The result isn't quite
+ * minimal as the value may be greater than p.
+ *
+ * On entry:
+ *   in[i] < 2^109
+ * On exit:
+ *   out[i] < 2^64
+ */
+static void felem_shrink(smallfelem out, const felem in)
+{
+    felem tmp;
+    u64 a, b, mask;
+    s64 high, low;
+    static const u64 kPrime3Test = 0x7fffffff00000001ul; /* 2^63 - 2^32 + 1 */
+
+    /* Carry 2->3 */
+    tmp[3] = zero110[3] + in[3] + ((u64)(in[2] >> 64));
+    /* tmp[3] < 2^110 */
+
+    tmp[2] = zero110[2] + (u64)in[2];
+    tmp[0] = zero110[0] + in[0];
+    tmp[1] = zero110[1] + in[1];
+    /* tmp[0] < 2**110, tmp[1] < 2^111, tmp[2] < 2**65 */
+
+    /*
+     * We perform two partial reductions where we eliminate the high-word of
+     * tmp[3]. We don't update the other words till the end.
+     */
+    a = tmp[3] >> 64;           /* a < 2^46 */
+    tmp[3] = (u64)tmp[3];
+    tmp[3] -= a;
+    tmp[3] += ((limb) a) << 32;
+    /* tmp[3] < 2^79 */
+
+    b = a;
+    a = tmp[3] >> 64;           /* a < 2^15 */
+    b += a;                     /* b < 2^46 + 2^15 < 2^47 */
+    tmp[3] = (u64)tmp[3];
+    tmp[3] -= a;
+    tmp[3] += ((limb) a) << 32;
+    /* tmp[3] < 2^64 + 2^47 */
+
+    /*
+     * This adjusts the other two words to complete the two partial
+     * reductions.
+     */
+    tmp[0] += b;
+    tmp[1] -= (((limb) b) << 32);
+
+    /*
+     * In order to make space in tmp[3] for the carry from 2 -> 3, we
+     * conditionally subtract kPrime if tmp[3] is large enough.
+     */
+    high = tmp[3] >> 64;
+    /* As tmp[3] < 2^65, high is either 1 or 0 */
+    high <<= 63;
+    high >>= 63;
+    /*-
+     * high is:
+     *   all ones   if the high word of tmp[3] is 1
+     *   all zeros  if the high word of tmp[3] if 0 */
+    low = tmp[3];
+    mask = low >> 63;
+    /*-
+     * mask is:
+     *   all ones   if the MSB of low is 1
+     *   all zeros  if the MSB of low if 0 */
+    low &= bottom63bits;
+    low -= kPrime3Test;
+    /* if low was greater than kPrime3Test then the MSB is zero */
+    low = ~low;
+    low >>= 63;
+    /*-
+     * low is:
+     *   all ones   if low was > kPrime3Test
+     *   all zeros  if low was <= kPrime3Test */
+    mask = (mask & low) | high;
+    tmp[0] -= mask & kPrime[0];
+    tmp[1] -= mask & kPrime[1];
+    /* kPrime[2] is zero, so omitted */
+    tmp[3] -= mask & kPrime[3];
+    /* tmp[3] < 2**64 - 2**32 + 1 */
+
+    tmp[1] += ((u64)(tmp[0] >> 64));
+    tmp[0] = (u64)tmp[0];
+    tmp[2] += ((u64)(tmp[1] >> 64));
+    tmp[1] = (u64)tmp[1];
+    tmp[3] += ((u64)(tmp[2] >> 64));
+    tmp[2] = (u64)tmp[2];
+    /* tmp[i] < 2^64 */
+
+    out[0] = tmp[0];
+    out[1] = tmp[1];
+    out[2] = tmp[2];
+    out[3] = tmp[3];
+}
+
+/* smallfelem_expand converts a smallfelem to an felem */
+static void smallfelem_expand(felem out, const smallfelem in)
+{
+    out[0] = in[0];
+    out[1] = in[1];
+    out[2] = in[2];
+    out[3] = in[3];
+}
+
+/*-
+ * smallfelem_square sets |out| = |small|^2
+ * On entry:
+ *   small[i] < 2^64
+ * On exit:
+ *   out[i] < 7 * 2^64 < 2^67
+ */
+static void smallfelem_square(longfelem out, const smallfelem small)
+{
+    limb a;
+    u64 high, low;
+
+    a = ((uint128_t) small[0]) * small[0];
+    low = a;
+    high = a >> 64;
+    out[0] = low;
+    out[1] = high;
+
+    a = ((uint128_t) small[0]) * small[1];
+    low = a;
+    high = a >> 64;
+    out[1] += low;
+    out[1] += low;
+    out[2] = high;
+
+    a = ((uint128_t) small[0]) * small[2];
+    low = a;
+    high = a >> 64;
+    out[2] += low;
+    out[2] *= 2;
+    out[3] = high;
+
+    a = ((uint128_t) small[0]) * small[3];
+    low = a;
+    high = a >> 64;
+    out[3] += low;
+    out[4] = high;
+
+    a = ((uint128_t) small[1]) * small[2];
+    low = a;
+    high = a >> 64;
+    out[3] += low;
+    out[3] *= 2;
+    out[4] += high;
+
+    a = ((uint128_t) small[1]) * small[1];
+    low = a;
+    high = a >> 64;
+    out[2] += low;
+    out[3] += high;
+
+    a = ((uint128_t) small[1]) * small[3];
+    low = a;
+    high = a >> 64;
+    out[4] += low;
+    out[4] *= 2;
+    out[5] = high;
+
+    a = ((uint128_t) small[2]) * small[3];
+    low = a;
+    high = a >> 64;
+    out[5] += low;
+    out[5] *= 2;
+    out[6] = high;
+    out[6] += high;
+
+    a = ((uint128_t) small[2]) * small[2];
+    low = a;
+    high = a >> 64;
+    out[4] += low;
+    out[5] += high;
+
+    a = ((uint128_t) small[3]) * small[3];
+    low = a;
+    high = a >> 64;
+    out[6] += low;
+    out[7] = high;
+}
+
+/*-
+ * felem_square sets |out| = |in|^2
+ * On entry:
+ *   in[i] < 2^109
+ * On exit:
+ *   out[i] < 7 * 2^64 < 2^67
+ */
+static void felem_square(longfelem out, const felem in)
+{
+    u64 small[4];
+    felem_shrink(small, in);
+    smallfelem_square(out, small);
+}
+
+/*-
+ * smallfelem_mul sets |out| = |small1| * |small2|
+ * On entry:
+ *   small1[i] < 2^64
+ *   small2[i] < 2^64
+ * On exit:
+ *   out[i] < 7 * 2^64 < 2^67
+ */
+static void smallfelem_mul(longfelem out, const smallfelem small1,
+                           const smallfelem small2)
+{
+    limb a;
+    u64 high, low;
+
+    a = ((uint128_t) small1[0]) * small2[0];
+    low = a;
+    high = a >> 64;
+    out[0] = low;
+    out[1] = high;
+
+    a = ((uint128_t) small1[0]) * small2[1];
+    low = a;
+    high = a >> 64;
+    out[1] += low;
+    out[2] = high;
+
+    a = ((uint128_t) small1[1]) * small2[0];
+    low = a;
+    high = a >> 64;
+    out[1] += low;
+    out[2] += high;
+
+    a = ((uint128_t) small1[0]) * small2[2];
+    low = a;
+    high = a >> 64;
+    out[2] += low;
+    out[3] = high;
+
+    a = ((uint128_t) small1[1]) * small2[1];
+    low = a;
+    high = a >> 64;
+    out[2] += low;
+    out[3] += high;
+
+    a = ((uint128_t) small1[2]) * small2[0];
+    low = a;
+    high = a >> 64;
+    out[2] += low;
+    out[3] += high;
+
+    a = ((uint128_t) small1[0]) * small2[3];
+    low = a;
+    high = a >> 64;
+    out[3] += low;
+    out[4] = high;
+
+    a = ((uint128_t) small1[1]) * small2[2];
+    low = a;
+    high = a >> 64;
+    out[3] += low;
+    out[4] += high;
+
+    a = ((uint128_t) small1[2]) * small2[1];
+    low = a;
+    high = a >> 64;
+    out[3] += low;
+    out[4] += high;
+
+    a = ((uint128_t) small1[3]) * small2[0];
+    low = a;
+    high = a >> 64;
+    out[3] += low;
+    out[4] += high;
+
+    a = ((uint128_t) small1[1]) * small2[3];
+    low = a;
+    high = a >> 64;
+    out[4] += low;
+    out[5] = high;
+
+    a = ((uint128_t) small1[2]) * small2[2];
+    low = a;
+    high = a >> 64;
+    out[4] += low;
+    out[5] += high;
+
+    a = ((uint128_t) small1[3]) * small2[1];
+    low = a;
+    high = a >> 64;
+    out[4] += low;
+    out[5] += high;
+
+    a = ((uint128_t) small1[2]) * small2[3];
+    low = a;
+    high = a >> 64;
+    out[5] += low;
+    out[6] = high;
+
+    a = ((uint128_t) small1[3]) * small2[2];
+    low = a;
+    high = a >> 64;
+    out[5] += low;
+    out[6] += high;
+
+    a = ((uint128_t) small1[3]) * small2[3];
+    low = a;
+    high = a >> 64;
+    out[6] += low;
+    out[7] = high;
+}
+
+/*-
+ * felem_mul sets |out| = |in1| * |in2|
+ * On entry:
+ *   in1[i] < 2^109
+ *   in2[i] < 2^109
+ * On exit:
+ *   out[i] < 7 * 2^64 < 2^67
+ */
+static void felem_mul(longfelem out, const felem in1, const felem in2)
+{
+    smallfelem small1, small2;
+    felem_shrink(small1, in1);
+    felem_shrink(small2, in2);
+    smallfelem_mul(out, small1, small2);
+}
+
+/*-
+ * felem_small_mul sets |out| = |small1| * |in2|
+ * On entry:
+ *   small1[i] < 2^64
+ *   in2[i] < 2^109
+ * On exit:
+ *   out[i] < 7 * 2^64 < 2^67
+ */
+static void felem_small_mul(longfelem out, const smallfelem small1,
+                            const felem in2)
+{
+    smallfelem small2;
+    felem_shrink(small2, in2);
+    smallfelem_mul(out, small1, small2);
+}
+
+# define two100m36m4 (((limb)1) << 100) - (((limb)1) << 36) - (((limb)1) << 4)
+# define two100 (((limb)1) << 100)
+# define two100m36p4 (((limb)1) << 100) - (((limb)1) << 36) + (((limb)1) << 4)
+/* zero100 is 0 mod p */
+static const felem zero100 =
+    { two100m36m4, two100, two100m36p4, two100m36p4 };
+
+/*-
+ * Internal function for the different flavours of felem_reduce.
+ * felem_reduce_ reduces the higher coefficients in[4]-in[7].
+ * On entry:
+ *   out[0] >= in[6] + 2^32*in[6] + in[7] + 2^32*in[7]
+ *   out[1] >= in[7] + 2^32*in[4]
+ *   out[2] >= in[5] + 2^32*in[5]
+ *   out[3] >= in[4] + 2^32*in[5] + 2^32*in[6]
+ * On exit:
+ *   out[0] <= out[0] + in[4] + 2^32*in[5]
+ *   out[1] <= out[1] + in[5] + 2^33*in[6]
+ *   out[2] <= out[2] + in[7] + 2*in[6] + 2^33*in[7]
+ *   out[3] <= out[3] + 2^32*in[4] + 3*in[7]
+ */
+static void felem_reduce_(felem out, const longfelem in)
+{
+    int128_t c;
+    /* combine common terms from below */
+    c = in[4] + (in[5] << 32);
+    out[0] += c;
+    out[3] -= c;
+
+    c = in[5] - in[7];
+    out[1] += c;
+    out[2] -= c;
+
+    /* the remaining terms */
+    /* 256: [(0,1),(96,-1),(192,-1),(224,1)] */
+    out[1] -= (in[4] << 32);
+    out[3] += (in[4] << 32);
+
+    /* 320: [(32,1),(64,1),(128,-1),(160,-1),(224,-1)] */
+    out[2] -= (in[5] << 32);
+
+    /* 384: [(0,-1),(32,-1),(96,2),(128,2),(224,-1)] */
+    out[0] -= in[6];
+    out[0] -= (in[6] << 32);
+    out[1] += (in[6] << 33);
+    out[2] += (in[6] * 2);
+    out[3] -= (in[6] << 32);
+
+    /* 448: [(0,-1),(32,-1),(64,-1),(128,1),(160,2),(192,3)] */
+    out[0] -= in[7];
+    out[0] -= (in[7] << 32);
+    out[2] += (in[7] << 33);
+    out[3] += (in[7] * 3);
+}
+
+/*-
+ * felem_reduce converts a longfelem into an felem.
+ * To be called directly after felem_square or felem_mul.
+ * On entry:
+ *   in[0] < 2^64, in[1] < 3*2^64, in[2] < 5*2^64, in[3] < 7*2^64
+ *   in[4] < 7*2^64, in[5] < 5*2^64, in[6] < 3*2^64, in[7] < 2*64
+ * On exit:
+ *   out[i] < 2^101
+ */
+static void felem_reduce(felem out, const longfelem in)
+{
+    out[0] = zero100[0] + in[0];
+    out[1] = zero100[1] + in[1];
+    out[2] = zero100[2] + in[2];
+    out[3] = zero100[3] + in[3];
+
+    felem_reduce_(out, in);
+
+    /*-
+     * out[0] > 2^100 - 2^36 - 2^4 - 3*2^64 - 3*2^96 - 2^64 - 2^96 > 0
+     * out[1] > 2^100 - 2^64 - 7*2^96 > 0
+     * out[2] > 2^100 - 2^36 + 2^4 - 5*2^64 - 5*2^96 > 0
+     * out[3] > 2^100 - 2^36 + 2^4 - 7*2^64 - 5*2^96 - 3*2^96 > 0
+     *
+     * out[0] < 2^100 + 2^64 + 7*2^64 + 5*2^96 < 2^101
+     * out[1] < 2^100 + 3*2^64 + 5*2^64 + 3*2^97 < 2^101
+     * out[2] < 2^100 + 5*2^64 + 2^64 + 3*2^65 + 2^97 < 2^101
+     * out[3] < 2^100 + 7*2^64 + 7*2^96 + 3*2^64 < 2^101
+     */
+}
+
+/*-
+ * felem_reduce_zero105 converts a larger longfelem into an felem.
+ * On entry:
+ *   in[0] < 2^71
+ * On exit:
+ *   out[i] < 2^106
+ */
+static void felem_reduce_zero105(felem out, const longfelem in)
+{
+    out[0] = zero105[0] + in[0];
+    out[1] = zero105[1] + in[1];
+    out[2] = zero105[2] + in[2];
+    out[3] = zero105[3] + in[3];
+
+    felem_reduce_(out, in);
+
+    /*-
+     * out[0] > 2^105 - 2^41 - 2^9 - 2^71 - 2^103 - 2^71 - 2^103 > 0
+     * out[1] > 2^105 - 2^71 - 2^103 > 0
+     * out[2] > 2^105 - 2^41 + 2^9 - 2^71 - 2^103 > 0
+     * out[3] > 2^105 - 2^41 + 2^9 - 2^71 - 2^103 - 2^103 > 0
+     *
+     * out[0] < 2^105 + 2^71 + 2^71 + 2^103 < 2^106
+     * out[1] < 2^105 + 2^71 + 2^71 + 2^103 < 2^106
+     * out[2] < 2^105 + 2^71 + 2^71 + 2^71 + 2^103 < 2^106
+     * out[3] < 2^105 + 2^71 + 2^103 + 2^71 < 2^106
+     */
+}
+
+/*
+ * subtract_u64 sets *result = *result - v and *carry to one if the
+ * subtraction underflowed.
+ */
+static void subtract_u64(u64 *result, u64 *carry, u64 v)
+{
+    uint128_t r = *result;
+    r -= v;
+    *carry = (r >> 64) & 1;
+    *result = (u64)r;
+}
+
+/*
+ * felem_contract converts |in| to its unique, minimal representation. On
+ * entry: in[i] < 2^109
+ */
+static void felem_contract(smallfelem out, const felem in)
+{
+    unsigned i;
+    u64 all_equal_so_far = 0, result = 0, carry;
+
+    felem_shrink(out, in);
+    /* small is minimal except that the value might be > p */
+
+    all_equal_so_far--;
+    /*
+     * We are doing a constant time test if out >= kPrime. We need to compare
+     * each u64, from most-significant to least significant. For each one, if
+     * all words so far have been equal (m is all ones) then a non-equal
+     * result is the answer. Otherwise we continue.
+     */
+    for (i = 3; i < 4; i--) {
+        u64 equal;
+        uint128_t a = ((uint128_t) kPrime[i]) - out[i];
+        /*
+         * if out[i] > kPrime[i] then a will underflow and the high 64-bits
+         * will all be set.
+         */
+        result |= all_equal_so_far & ((u64)(a >> 64));
+
+        /*
+         * if kPrime[i] == out[i] then |equal| will be all zeros and the
+         * decrement will make it all ones.
+         */
+        equal = kPrime[i] ^ out[i];
+        equal--;
+        equal &= equal << 32;
+        equal &= equal << 16;
+        equal &= equal << 8;
+        equal &= equal << 4;
+        equal &= equal << 2;
+        equal &= equal << 1;
+        equal = ((s64) equal) >> 63;
+
+        all_equal_so_far &= equal;
+    }
+
+    /*
+     * if all_equal_so_far is still all ones then the two values are equal
+     * and so out >= kPrime is true.
+     */
+    result |= all_equal_so_far;
+
+    /* if out >= kPrime then we subtract kPrime. */
+    subtract_u64(&out[0], &carry, result & kPrime[0]);
+    subtract_u64(&out[1], &carry, carry);
+    subtract_u64(&out[2], &carry, carry);
+    subtract_u64(&out[3], &carry, carry);
+
+    subtract_u64(&out[1], &carry, result & kPrime[1]);
+    subtract_u64(&out[2], &carry, carry);
+    subtract_u64(&out[3], &carry, carry);
+
+    subtract_u64(&out[2], &carry, result & kPrime[2]);
+    subtract_u64(&out[3], &carry, carry);
+
+    subtract_u64(&out[3], &carry, result & kPrime[3]);
+}
+
+static void smallfelem_square_contract(smallfelem out, const smallfelem in)
+{
+    longfelem longtmp;
+    felem tmp;
+
+    smallfelem_square(longtmp, in);
+    felem_reduce(tmp, longtmp);
+    felem_contract(out, tmp);
+}
+
+static void smallfelem_mul_contract(smallfelem out, const smallfelem in1,
+                                    const smallfelem in2)
+{
+    longfelem longtmp;
+    felem tmp;
+
+    smallfelem_mul(longtmp, in1, in2);
+    felem_reduce(tmp, longtmp);
+    felem_contract(out, tmp);
+}
+
+/*-
+ * felem_is_zero returns a limb with all bits set if |in| == 0 (mod p) and 0
+ * otherwise.
+ * On entry:
+ *   small[i] < 2^64
+ */
+static limb smallfelem_is_zero(const smallfelem small)
+{
+    limb result;
+    u64 is_p;
+
+    u64 is_zero = small[0] | small[1] | small[2] | small[3];
+    is_zero--;
+    is_zero &= is_zero << 32;
+    is_zero &= is_zero << 16;
+    is_zero &= is_zero << 8;
+    is_zero &= is_zero << 4;
+    is_zero &= is_zero << 2;
+    is_zero &= is_zero << 1;
+    is_zero = ((s64) is_zero) >> 63;
+
+    is_p = (small[0] ^ kPrime[0]) |
+        (small[1] ^ kPrime[1]) |
+        (small[2] ^ kPrime[2]) | (small[3] ^ kPrime[3]);
+    is_p--;
+    is_p &= is_p << 32;
+    is_p &= is_p << 16;
+    is_p &= is_p << 8;
+    is_p &= is_p << 4;
+    is_p &= is_p << 2;
+    is_p &= is_p << 1;
+    is_p = ((s64) is_p) >> 63;
+
+    is_zero |= is_p;
+
+    result = is_zero;
+    result |= ((limb) is_zero) << 64;
+    return result;
+}
+
+static int smallfelem_is_zero_int(const smallfelem small)
+{
+    return (int)(smallfelem_is_zero(small) & ((limb) 1));
+}
+
+/*-
+ * felem_inv calculates |out| = |in|^{-1}
+ *
+ * Based on Fermat's Little Theorem:
+ *   a^p = a (mod p)
+ *   a^{p-1} = 1 (mod p)
+ *   a^{p-2} = a^{-1} (mod p)
+ */
+static void felem_inv(felem out, const felem in)
+{
+    felem ftmp, ftmp2;
+    /* each e_I will hold |in|^{2^I - 1} */
+    felem e2, e4, e8, e16, e32, e64;
+    longfelem tmp;
+    unsigned i;
+
+    felem_square(tmp, in);
+    felem_reduce(ftmp, tmp);    /* 2^1 */
+    felem_mul(tmp, in, ftmp);
+    felem_reduce(ftmp, tmp);    /* 2^2 - 2^0 */
+    felem_assign(e2, ftmp);
+    felem_square(tmp, ftmp);
+    felem_reduce(ftmp, tmp);    /* 2^3 - 2^1 */
+    felem_square(tmp, ftmp);
+    felem_reduce(ftmp, tmp);    /* 2^4 - 2^2 */
+    felem_mul(tmp, ftmp, e2);
+    felem_reduce(ftmp, tmp);    /* 2^4 - 2^0 */
+    felem_assign(e4, ftmp);
+    felem_square(tmp, ftmp);
+    felem_reduce(ftmp, tmp);    /* 2^5 - 2^1 */
+    felem_square(tmp, ftmp);
+    felem_reduce(ftmp, tmp);    /* 2^6 - 2^2 */
+    felem_square(tmp, ftmp);
+    felem_reduce(ftmp, tmp);    /* 2^7 - 2^3 */
+    felem_square(tmp, ftmp);
+    felem_reduce(ftmp, tmp);    /* 2^8 - 2^4 */
+    felem_mul(tmp, ftmp, e4);
+    felem_reduce(ftmp, tmp);    /* 2^8 - 2^0 */
+    felem_assign(e8, ftmp);
+    for (i = 0; i < 8; i++) {
+        felem_square(tmp, ftmp);
+        felem_reduce(ftmp, tmp);
+    }                           /* 2^16 - 2^8 */
+    felem_mul(tmp, ftmp, e8);
+    felem_reduce(ftmp, tmp);    /* 2^16 - 2^0 */
+    felem_assign(e16, ftmp);
+    for (i = 0; i < 16; i++) {
+        felem_square(tmp, ftmp);
+        felem_reduce(ftmp, tmp);
+    }                           /* 2^32 - 2^16 */
+    felem_mul(tmp, ftmp, e16);
+    felem_reduce(ftmp, tmp);    /* 2^32 - 2^0 */
+    felem_assign(e32, ftmp);
+    for (i = 0; i < 32; i++) {
+        felem_square(tmp, ftmp);
+        felem_reduce(ftmp, tmp);
+    }                           /* 2^64 - 2^32 */
+    felem_assign(e64, ftmp);
+    felem_mul(tmp, ftmp, in);
+    felem_reduce(ftmp, tmp);    /* 2^64 - 2^32 + 2^0 */
+    for (i = 0; i < 192; i++) {
+        felem_square(tmp, ftmp);
+        felem_reduce(ftmp, tmp);
+    }                           /* 2^256 - 2^224 + 2^192 */
+
+    felem_mul(tmp, e64, e32);
+    felem_reduce(ftmp2, tmp);   /* 2^64 - 2^0 */
+    for (i = 0; i < 16; i++) {
+        felem_square(tmp, ftmp2);
+        felem_reduce(ftmp2, tmp);
+    }                           /* 2^80 - 2^16 */
+    felem_mul(tmp, ftmp2, e16);
+    felem_reduce(ftmp2, tmp);   /* 2^80 - 2^0 */
+    for (i = 0; i < 8; i++) {
+        felem_square(tmp, ftmp2);
+        felem_reduce(ftmp2, tmp);
+    }                           /* 2^88 - 2^8 */
+    felem_mul(tmp, ftmp2, e8);
+    felem_reduce(ftmp2, tmp);   /* 2^88 - 2^0 */
+    for (i = 0; i < 4; i++) {
+        felem_square(tmp, ftmp2);
+        felem_reduce(ftmp2, tmp);
+    }                           /* 2^92 - 2^4 */
+    felem_mul(tmp, ftmp2, e4);
+    felem_reduce(ftmp2, tmp);   /* 2^92 - 2^0 */
+    felem_square(tmp, ftmp2);
+    felem_reduce(ftmp2, tmp);   /* 2^93 - 2^1 */
+    felem_square(tmp, ftmp2);
+    felem_reduce(ftmp2, tmp);   /* 2^94 - 2^2 */
+    felem_mul(tmp, ftmp2, e2);
+    felem_reduce(ftmp2, tmp);   /* 2^94 - 2^0 */
+    felem_square(tmp, ftmp2);
+    felem_reduce(ftmp2, tmp);   /* 2^95 - 2^1 */
+    felem_square(tmp, ftmp2);
+    felem_reduce(ftmp2, tmp);   /* 2^96 - 2^2 */
+    felem_mul(tmp, ftmp2, in);
+    felem_reduce(ftmp2, tmp);   /* 2^96 - 3 */
+
+    felem_mul(tmp, ftmp2, ftmp);
+    felem_reduce(out, tmp);     /* 2^256 - 2^224 + 2^192 + 2^96 - 3 */
+}
+
+static void smallfelem_inv_contract(smallfelem out, const smallfelem in)
+{
+    felem tmp;
+
+    smallfelem_expand(tmp, in);
+    felem_inv(tmp, tmp);
+    felem_contract(out, tmp);
+}
+
+/*-
+ * Group operations
+ * ----------------
+ *
+ * Building on top of the field operations we have the operations on the
+ * elliptic curve group itself. Points on the curve are represented in Jacobian
+ * coordinates
+ */
+
+/*-
+ * point_double calculates 2*(x_in, y_in, z_in)
+ *
+ * The method is taken from:
+ *   http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html#doubling-dbl-2001-b
+ *
+ * Outputs can equal corresponding inputs, i.e., x_out == x_in is allowed.
+ * while x_out == y_in is not (maybe this works, but it's not tested).
+ */
+static void
+point_double(felem x_out, felem y_out, felem z_out,
+             const felem x_in, const felem y_in, const felem z_in)
+{
+    longfelem tmp, tmp2;
+    felem delta, gamma, beta, alpha, ftmp, ftmp2;
+    smallfelem small1, small2;
+
+    felem_assign(ftmp, x_in);
+    /* ftmp[i] < 2^106 */
+    felem_assign(ftmp2, x_in);
+    /* ftmp2[i] < 2^106 */
+
+    /* delta = z^2 */
+    felem_square(tmp, z_in);
+    felem_reduce(delta, tmp);
+    /* delta[i] < 2^101 */
+
+    /* gamma = y^2 */
+    felem_square(tmp, y_in);
+    felem_reduce(gamma, tmp);
+    /* gamma[i] < 2^101 */
+    felem_shrink(small1, gamma);
+
+    /* beta = x*gamma */
+    felem_small_mul(tmp, small1, x_in);
+    felem_reduce(beta, tmp);
+    /* beta[i] < 2^101 */
+
+    /* alpha = 3*(x-delta)*(x+delta) */
+    felem_diff(ftmp, delta);
+    /* ftmp[i] < 2^105 + 2^106 < 2^107 */
+    felem_sum(ftmp2, delta);
+    /* ftmp2[i] < 2^105 + 2^106 < 2^107 */
+    felem_scalar(ftmp2, 3);
+    /* ftmp2[i] < 3 * 2^107 < 2^109 */
+    felem_mul(tmp, ftmp, ftmp2);
+    felem_reduce(alpha, tmp);
+    /* alpha[i] < 2^101 */
+    felem_shrink(small2, alpha);
+
+    /* x' = alpha^2 - 8*beta */
+    smallfelem_square(tmp, small2);
+    felem_reduce(x_out, tmp);
+    felem_assign(ftmp, beta);
+    felem_scalar(ftmp, 8);
+    /* ftmp[i] < 8 * 2^101 = 2^104 */
+    felem_diff(x_out, ftmp);
+    /* x_out[i] < 2^105 + 2^101 < 2^106 */
+
+    /* z' = (y + z)^2 - gamma - delta */
+    felem_sum(delta, gamma);
+    /* delta[i] < 2^101 + 2^101 = 2^102 */
+    felem_assign(ftmp, y_in);
+    felem_sum(ftmp, z_in);
+    /* ftmp[i] < 2^106 + 2^106 = 2^107 */
+    felem_square(tmp, ftmp);
+    felem_reduce(z_out, tmp);
+    felem_diff(z_out, delta);
+    /* z_out[i] < 2^105 + 2^101 < 2^106 */
+
+    /* y' = alpha*(4*beta - x') - 8*gamma^2 */
+    felem_scalar(beta, 4);
+    /* beta[i] < 4 * 2^101 = 2^103 */
+    felem_diff_zero107(beta, x_out);
+    /* beta[i] < 2^107 + 2^103 < 2^108 */
+    felem_small_mul(tmp, small2, beta);
+    /* tmp[i] < 7 * 2^64 < 2^67 */
+    smallfelem_square(tmp2, small1);
+    /* tmp2[i] < 7 * 2^64 */
+    longfelem_scalar(tmp2, 8);
+    /* tmp2[i] < 8 * 7 * 2^64 = 7 * 2^67 */
+    longfelem_diff(tmp, tmp2);
+    /* tmp[i] < 2^67 + 2^70 + 2^40 < 2^71 */
+    felem_reduce_zero105(y_out, tmp);
+    /* y_out[i] < 2^106 */
+}
+
+/*
+ * point_double_small is the same as point_double, except that it operates on
+ * smallfelems
+ */
+static void
+point_double_small(smallfelem x_out, smallfelem y_out, smallfelem z_out,
+                   const smallfelem x_in, const smallfelem y_in,
+                   const smallfelem z_in)
+{
+    felem felem_x_out, felem_y_out, felem_z_out;
+    felem felem_x_in, felem_y_in, felem_z_in;
+
+    smallfelem_expand(felem_x_in, x_in);
+    smallfelem_expand(felem_y_in, y_in);
+    smallfelem_expand(felem_z_in, z_in);
+    point_double(felem_x_out, felem_y_out, felem_z_out,
+                 felem_x_in, felem_y_in, felem_z_in);
+    felem_shrink(x_out, felem_x_out);
+    felem_shrink(y_out, felem_y_out);
+    felem_shrink(z_out, felem_z_out);
+}
+
+/* copy_conditional copies in to out iff mask is all ones. */
+static void copy_conditional(felem out, const felem in, limb mask)
+{
+    unsigned i;
+    for (i = 0; i < NLIMBS; ++i) {
+        const limb tmp = mask & (in[i] ^ out[i]);
+        out[i] ^= tmp;
+    }
+}
+
+/* copy_small_conditional copies in to out iff mask is all ones. */
+static void copy_small_conditional(felem out, const smallfelem in, limb mask)
+{
+    unsigned i;
+    const u64 mask64 = mask;
+    for (i = 0; i < NLIMBS; ++i) {
+        out[i] = ((limb) (in[i] & mask64)) | (out[i] & ~mask);
+    }
+}
+
+/*-
+ * point_add calculates (x1, y1, z1) + (x2, y2, z2)
+ *
+ * The method is taken from:
+ *   http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html#addition-add-2007-bl,
+ * adapted for mixed addition (z2 = 1, or z2 = 0 for the point at infinity).
+ *
+ * This function includes a branch for checking whether the two input points
+ * are equal, (while not equal to the point at infinity). This case never
+ * happens during single point multiplication, so there is no timing leak for
+ * ECDH or ECDSA signing.
+ */
+static void point_add(felem x3, felem y3, felem z3,
+                      const felem x1, const felem y1, const felem z1,
+                      const int mixed, const smallfelem x2,
+                      const smallfelem y2, const smallfelem z2)
+{
+    felem ftmp, ftmp2, ftmp3, ftmp4, ftmp5, ftmp6, x_out, y_out, z_out;
+    longfelem tmp, tmp2;
+    smallfelem small1, small2, small3, small4, small5;
+    limb x_equal, y_equal, z1_is_zero, z2_is_zero;
+
+    felem_shrink(small3, z1);
+
+    z1_is_zero = smallfelem_is_zero(small3);
+    z2_is_zero = smallfelem_is_zero(z2);
+
+    /* ftmp = z1z1 = z1**2 */
+    smallfelem_square(tmp, small3);
+    felem_reduce(ftmp, tmp);
+    /* ftmp[i] < 2^101 */
+    felem_shrink(small1, ftmp);
+
+    if (!mixed) {
+        /* ftmp2 = z2z2 = z2**2 */
+        smallfelem_square(tmp, z2);
+        felem_reduce(ftmp2, tmp);
+        /* ftmp2[i] < 2^101 */
+        felem_shrink(small2, ftmp2);
+
+        felem_shrink(small5, x1);
+
+        /* u1 = ftmp3 = x1*z2z2 */
+        smallfelem_mul(tmp, small5, small2);
+        felem_reduce(ftmp3, tmp);
+        /* ftmp3[i] < 2^101 */
+
+        /* ftmp5 = z1 + z2 */
+        felem_assign(ftmp5, z1);
+        felem_small_sum(ftmp5, z2);
+        /* ftmp5[i] < 2^107 */
+
+        /* ftmp5 = (z1 + z2)**2 - (z1z1 + z2z2) = 2z1z2 */
+        felem_square(tmp, ftmp5);
+        felem_reduce(ftmp5, tmp);
+        /* ftmp2 = z2z2 + z1z1 */
+        felem_sum(ftmp2, ftmp);
+        /* ftmp2[i] < 2^101 + 2^101 = 2^102 */
+        felem_diff(ftmp5, ftmp2);
+        /* ftmp5[i] < 2^105 + 2^101 < 2^106 */
+
+        /* ftmp2 = z2 * z2z2 */
+        smallfelem_mul(tmp, small2, z2);
+        felem_reduce(ftmp2, tmp);
+
+        /* s1 = ftmp2 = y1 * z2**3 */
+        felem_mul(tmp, y1, ftmp2);
+        felem_reduce(ftmp6, tmp);
+        /* ftmp6[i] < 2^101 */
+    } else {
+        /*
+         * We'll assume z2 = 1 (special case z2 = 0 is handled later)
+         */
+
+        /* u1 = ftmp3 = x1*z2z2 */
+        felem_assign(ftmp3, x1);
+        /* ftmp3[i] < 2^106 */
+
+        /* ftmp5 = 2z1z2 */
+        felem_assign(ftmp5, z1);
+        felem_scalar(ftmp5, 2);
+        /* ftmp5[i] < 2*2^106 = 2^107 */
+
+        /* s1 = ftmp2 = y1 * z2**3 */
+        felem_assign(ftmp6, y1);
+        /* ftmp6[i] < 2^106 */
+    }
+
+    /* u2 = x2*z1z1 */
+    smallfelem_mul(tmp, x2, small1);
+    felem_reduce(ftmp4, tmp);
+
+    /* h = ftmp4 = u2 - u1 */
+    felem_diff_zero107(ftmp4, ftmp3);
+    /* ftmp4[i] < 2^107 + 2^101 < 2^108 */
+    felem_shrink(small4, ftmp4);
+
+    x_equal = smallfelem_is_zero(small4);
+
+    /* z_out = ftmp5 * h */
+    felem_small_mul(tmp, small4, ftmp5);
+    felem_reduce(z_out, tmp);
+    /* z_out[i] < 2^101 */
+
+    /* ftmp = z1 * z1z1 */
+    smallfelem_mul(tmp, small1, small3);
+    felem_reduce(ftmp, tmp);
+
+    /* s2 = tmp = y2 * z1**3 */
+    felem_small_mul(tmp, y2, ftmp);
+    felem_reduce(ftmp5, tmp);
+
+    /* r = ftmp5 = (s2 - s1)*2 */
+    felem_diff_zero107(ftmp5, ftmp6);
+    /* ftmp5[i] < 2^107 + 2^107 = 2^108 */
+    felem_scalar(ftmp5, 2);
+    /* ftmp5[i] < 2^109 */
+    felem_shrink(small1, ftmp5);
+    y_equal = smallfelem_is_zero(small1);
+
+    if (x_equal && y_equal && !z1_is_zero && !z2_is_zero) {
+        point_double(x3, y3, z3, x1, y1, z1);
+        return;
+    }
+
+    /* I = ftmp = (2h)**2 */
+    felem_assign(ftmp, ftmp4);
+    felem_scalar(ftmp, 2);
+    /* ftmp[i] < 2*2^108 = 2^109 */
+    felem_square(tmp, ftmp);
+    felem_reduce(ftmp, tmp);
+
+    /* J = ftmp2 = h * I */
+    felem_mul(tmp, ftmp4, ftmp);
+    felem_reduce(ftmp2, tmp);
+
+    /* V = ftmp4 = U1 * I */
+    felem_mul(tmp, ftmp3, ftmp);
+    felem_reduce(ftmp4, tmp);
+
+    /* x_out = r**2 - J - 2V */
+    smallfelem_square(tmp, small1);
+    felem_reduce(x_out, tmp);
+    felem_assign(ftmp3, ftmp4);
+    felem_scalar(ftmp4, 2);
+    felem_sum(ftmp4, ftmp2);
+    /* ftmp4[i] < 2*2^101 + 2^101 < 2^103 */
+    felem_diff(x_out, ftmp4);
+    /* x_out[i] < 2^105 + 2^101 */
+
+    /* y_out = r(V-x_out) - 2 * s1 * J */
+    felem_diff_zero107(ftmp3, x_out);
+    /* ftmp3[i] < 2^107 + 2^101 < 2^108 */
+    felem_small_mul(tmp, small1, ftmp3);
+    felem_mul(tmp2, ftmp6, ftmp2);
+    longfelem_scalar(tmp2, 2);
+    /* tmp2[i] < 2*2^67 = 2^68 */
+    longfelem_diff(tmp, tmp2);
+    /* tmp[i] < 2^67 + 2^70 + 2^40 < 2^71 */
+    felem_reduce_zero105(y_out, tmp);
+    /* y_out[i] < 2^106 */
+
+    copy_small_conditional(x_out, x2, z1_is_zero);
+    copy_conditional(x_out, x1, z2_is_zero);
+    copy_small_conditional(y_out, y2, z1_is_zero);
+    copy_conditional(y_out, y1, z2_is_zero);
+    copy_small_conditional(z_out, z2, z1_is_zero);
+    copy_conditional(z_out, z1, z2_is_zero);
+    felem_assign(x3, x_out);
+    felem_assign(y3, y_out);
+    felem_assign(z3, z_out);
+}
+
+/*
+ * point_add_small is the same as point_add, except that it operates on
+ * smallfelems
+ */
+static void point_add_small(smallfelem x3, smallfelem y3, smallfelem z3,
+                            smallfelem x1, smallfelem y1, smallfelem z1,
+                            smallfelem x2, smallfelem y2, smallfelem z2)
+{
+    felem felem_x3, felem_y3, felem_z3;
+    felem felem_x1, felem_y1, felem_z1;
+    smallfelem_expand(felem_x1, x1);
+    smallfelem_expand(felem_y1, y1);
+    smallfelem_expand(felem_z1, z1);
+    point_add(felem_x3, felem_y3, felem_z3, felem_x1, felem_y1, felem_z1, 0,
+              x2, y2, z2);
+    felem_shrink(x3, felem_x3);
+    felem_shrink(y3, felem_y3);
+    felem_shrink(z3, felem_z3);
+}
+
+/*-
+ * Base point pre computation
+ * --------------------------
+ *
+ * Two different sorts of precomputed tables are used in the following code.
+ * Each contain various points on the curve, where each point is three field
+ * elements (x, y, z).
+ *
+ * For the base point table, z is usually 1 (0 for the point at infinity).
+ * This table has 2 * 16 elements, starting with the following:
+ * index | bits    | point
+ * ------+---------+------------------------------
+ *     0 | 0 0 0 0 | 0G
+ *     1 | 0 0 0 1 | 1G
+ *     2 | 0 0 1 0 | 2^64G
+ *     3 | 0 0 1 1 | (2^64 + 1)G
+ *     4 | 0 1 0 0 | 2^128G
+ *     5 | 0 1 0 1 | (2^128 + 1)G
+ *     6 | 0 1 1 0 | (2^128 + 2^64)G
+ *     7 | 0 1 1 1 | (2^128 + 2^64 + 1)G
+ *     8 | 1 0 0 0 | 2^192G
+ *     9 | 1 0 0 1 | (2^192 + 1)G
+ *    10 | 1 0 1 0 | (2^192 + 2^64)G
+ *    11 | 1 0 1 1 | (2^192 + 2^64 + 1)G
+ *    12 | 1 1 0 0 | (2^192 + 2^128)G
+ *    13 | 1 1 0 1 | (2^192 + 2^128 + 1)G
+ *    14 | 1 1 1 0 | (2^192 + 2^128 + 2^64)G
+ *    15 | 1 1 1 1 | (2^192 + 2^128 + 2^64 + 1)G
+ * followed by a copy of this with each element multiplied by 2^32.
+ *
+ * The reason for this is so that we can clock bits into four different
+ * locations when doing simple scalar multiplies against the base point,
+ * and then another four locations using the second 16 elements.
+ *
+ * Tables for other points have table[i] = iG for i in 0 .. 16. */
+
+/* gmul is the table of precomputed base points */
+static const smallfelem gmul[2][16][3] = {
+    {{{0, 0, 0, 0},
+      {0, 0, 0, 0},
+      {0, 0, 0, 0}},
+     {{0xf4a13945d898c296, 0x77037d812deb33a0, 0xf8bce6e563a440f2,
+       0x6b17d1f2e12c4247},
+      {0xcbb6406837bf51f5, 0x2bce33576b315ece, 0x8ee7eb4a7c0f9e16,
+       0x4fe342e2fe1a7f9b},
+      {1, 0, 0, 0}},
+     {{0x90e75cb48e14db63, 0x29493baaad651f7e, 0x8492592e326e25de,
+       0x0fa822bc2811aaa5},
+      {0xe41124545f462ee7, 0x34b1a65050fe82f5, 0x6f4ad4bcb3df188b,
+       0xbff44ae8f5dba80d},
+      {1, 0, 0, 0}},
+     {{0x93391ce2097992af, 0xe96c98fd0d35f1fa, 0xb257c0de95e02789,
+       0x300a4bbc89d6726f},
+      {0xaa54a291c08127a0, 0x5bb1eeada9d806a5, 0x7f1ddb25ff1e3c6f,
+       0x72aac7e0d09b4644},
+      {1, 0, 0, 0}},
+     {{0x57c84fc9d789bd85, 0xfc35ff7dc297eac3, 0xfb982fd588c6766e,
+       0x447d739beedb5e67},
+      {0x0c7e33c972e25b32, 0x3d349b95a7fae500, 0xe12e9d953a4aaff7,
+       0x2d4825ab834131ee},
+      {1, 0, 0, 0}},
+     {{0x13949c932a1d367f, 0xef7fbd2b1a0a11b7, 0xddc6068bb91dfc60,
+       0xef9519328a9c72ff},
+      {0x196035a77376d8a8, 0x23183b0895ca1740, 0xc1ee9807022c219c,
+       0x611e9fc37dbb2c9b},
+      {1, 0, 0, 0}},
+     {{0xcae2b1920b57f4bc, 0x2936df5ec6c9bc36, 0x7dea6482e11238bf,
+       0x550663797b51f5d8},
+      {0x44ffe216348a964c, 0x9fb3d576dbdefbe1, 0x0afa40018d9d50e5,
+       0x157164848aecb851},
+      {1, 0, 0, 0}},
+     {{0xe48ecafffc5cde01, 0x7ccd84e70d715f26, 0xa2e8f483f43e4391,
+       0xeb5d7745b21141ea},
+      {0xcac917e2731a3479, 0x85f22cfe2844b645, 0x0990e6a158006cee,
+       0xeafd72ebdbecc17b},
+      {1, 0, 0, 0}},
+     {{0x6cf20ffb313728be, 0x96439591a3c6b94a, 0x2736ff8344315fc5,
+       0xa6d39677a7849276},
+      {0xf2bab833c357f5f4, 0x824a920c2284059b, 0x66b8babd2d27ecdf,
+       0x674f84749b0b8816},
+      {1, 0, 0, 0}},
+     {{0x2df48c04677c8a3e, 0x74e02f080203a56b, 0x31855f7db8c7fedb,
+       0x4e769e7672c9ddad},
+      {0xa4c36165b824bbb0, 0xfb9ae16f3b9122a5, 0x1ec0057206947281,
+       0x42b99082de830663},
+      {1, 0, 0, 0}},
+     {{0x6ef95150dda868b9, 0xd1f89e799c0ce131, 0x7fdc1ca008a1c478,
+       0x78878ef61c6ce04d},
+      {0x9c62b9121fe0d976, 0x6ace570ebde08d4f, 0xde53142c12309def,
+       0xb6cb3f5d7b72c321},
+      {1, 0, 0, 0}},
+     {{0x7f991ed2c31a3573, 0x5b82dd5bd54fb496, 0x595c5220812ffcae,
+       0x0c88bc4d716b1287},
+      {0x3a57bf635f48aca8, 0x7c8181f4df2564f3, 0x18d1b5b39c04e6aa,
+       0xdd5ddea3f3901dc6},
+      {1, 0, 0, 0}},
+     {{0xe96a79fb3e72ad0c, 0x43a0a28c42ba792f, 0xefe0a423083e49f3,
+       0x68f344af6b317466},
+      {0xcdfe17db3fb24d4a, 0x668bfc2271f5c626, 0x604ed93c24d67ff3,
+       0x31b9c405f8540a20},
+      {1, 0, 0, 0}},
+     {{0xd36b4789a2582e7f, 0x0d1a10144ec39c28, 0x663c62c3edbad7a0,
+       0x4052bf4b6f461db9},
+      {0x235a27c3188d25eb, 0xe724f33999bfcc5b, 0x862be6bd71d70cc8,
+       0xfecf4d5190b0fc61},
+      {1, 0, 0, 0}},
+     {{0x74346c10a1d4cfac, 0xafdf5cc08526a7a4, 0x123202a8f62bff7a,
+       0x1eddbae2c802e41a},
+      {0x8fa0af2dd603f844, 0x36e06b7e4c701917, 0x0c45f45273db33a0,
+       0x43104d86560ebcfc},
+      {1, 0, 0, 0}},
+     {{0x9615b5110d1d78e5, 0x66b0de3225c4744b, 0x0a4a46fb6aaf363a,
+       0xb48e26b484f7a21c},
+      {0x06ebb0f621a01b2d, 0xc004e4048b7b0f98, 0x64131bcdfed6f668,
+       0xfac015404d4d3dab},
+      {1, 0, 0, 0}}},
+    {{{0, 0, 0, 0},
+      {0, 0, 0, 0},
+      {0, 0, 0, 0}},
+     {{0x3a5a9e22185a5943, 0x1ab919365c65dfb6, 0x21656b32262c71da,
+       0x7fe36b40af22af89},
+      {0xd50d152c699ca101, 0x74b3d5867b8af212, 0x9f09f40407dca6f1,
+       0xe697d45825b63624},
+      {1, 0, 0, 0}},
+     {{0xa84aa9397512218e, 0xe9a521b074ca0141, 0x57880b3a18a2e902,
+       0x4a5b506612a677a6},
+      {0x0beada7a4c4f3840, 0x626db15419e26d9d, 0xc42604fbe1627d40,
+       0xeb13461ceac089f1},
+      {1, 0, 0, 0}},
+     {{0xf9faed0927a43281, 0x5e52c4144103ecbc, 0xc342967aa815c857,
+       0x0781b8291c6a220a},
+      {0x5a8343ceeac55f80, 0x88f80eeee54a05e3, 0x97b2a14f12916434,
+       0x690cde8df0151593},
+      {1, 0, 0, 0}},
+     {{0xaee9c75df7f82f2a, 0x9e4c35874afdf43a, 0xf5622df437371326,
+       0x8a535f566ec73617},
+      {0xc5f9a0ac223094b7, 0xcde533864c8c7669, 0x37e02819085a92bf,
+       0x0455c08468b08bd7},
+      {1, 0, 0, 0}},
+     {{0x0c0a6e2c9477b5d9, 0xf9a4bf62876dc444, 0x5050a949b6cdc279,
+       0x06bada7ab77f8276},
+      {0xc8b4aed1ea48dac9, 0xdebd8a4b7ea1070f, 0x427d49101366eb70,
+       0x5b476dfd0e6cb18a},
+      {1, 0, 0, 0}},
+     {{0x7c5c3e44278c340a, 0x4d54606812d66f3b, 0x29a751b1ae23c5d8,
+       0x3e29864e8a2ec908},
+      {0x142d2a6626dbb850, 0xad1744c4765bd780, 0x1f150e68e322d1ed,
+       0x239b90ea3dc31e7e},
+      {1, 0, 0, 0}},
+     {{0x78c416527a53322a, 0x305dde6709776f8e, 0xdbcab759f8862ed4,
+       0x820f4dd949f72ff7},
+      {0x6cc544a62b5debd4, 0x75be5d937b4e8cc4, 0x1b481b1b215c14d3,
+       0x140406ec783a05ec},
+      {1, 0, 0, 0}},
+     {{0x6a703f10e895df07, 0xfd75f3fa01876bd8, 0xeb5b06e70ce08ffe,
+       0x68f6b8542783dfee},
+      {0x90c76f8a78712655, 0xcf5293d2f310bf7f, 0xfbc8044dfda45028,
+       0xcbe1feba92e40ce6},
+      {1, 0, 0, 0}},
+     {{0xe998ceea4396e4c1, 0xfc82ef0b6acea274, 0x230f729f2250e927,
+       0xd0b2f94d2f420109},
+      {0x4305adddb38d4966, 0x10b838f8624c3b45, 0x7db2636658954e7a,
+       0x971459828b0719e5},
+      {1, 0, 0, 0}},
+     {{0x4bd6b72623369fc9, 0x57f2929e53d0b876, 0xc2d5cba4f2340687,
+       0x961610004a866aba},
+      {0x49997bcd2e407a5e, 0x69ab197d92ddcb24, 0x2cf1f2438fe5131c,
+       0x7acb9fadcee75e44},
+      {1, 0, 0, 0}},
+     {{0x254e839423d2d4c0, 0xf57f0c917aea685b, 0xa60d880f6f75aaea,
+       0x24eb9acca333bf5b},
+      {0xe3de4ccb1cda5dea, 0xfeef9341c51a6b4f, 0x743125f88bac4c4d,
+       0x69f891c5acd079cc},
+      {1, 0, 0, 0}},
+     {{0xeee44b35702476b5, 0x7ed031a0e45c2258, 0xb422d1e7bd6f8514,
+       0xe51f547c5972a107},
+      {0xa25bcd6fc9cf343d, 0x8ca922ee097c184e, 0xa62f98b3a9fe9a06,
+       0x1c309a2b25bb1387},
+      {1, 0, 0, 0}},
+     {{0x9295dbeb1967c459, 0xb00148833472c98e, 0xc504977708011828,
+       0x20b87b8aa2c4e503},
+      {0x3063175de057c277, 0x1bd539338fe582dd, 0x0d11adef5f69a044,
+       0xf5c6fa49919776be},
+      {1, 0, 0, 0}},
+     {{0x8c944e760fd59e11, 0x3876cba1102fad5f, 0xa454c3fad83faa56,
+       0x1ed7d1b9332010b9},
+      {0xa1011a270024b889, 0x05e4d0dcac0cd344, 0x52b520f0eb6a2a24,
+       0x3a2b03f03217257a},
+      {1, 0, 0, 0}},
+     {{0xf20fc2afdf1d043d, 0xf330240db58d5a62, 0xfc7d229ca0058c3b,
+       0x15fee545c78dd9f6},
+      {0x501e82885bc98cda, 0x41ef80e5d046ac04, 0x557d9f49461210fb,
+       0x4ab5b6b2b8753f81},
+      {1, 0, 0, 0}}}
+};
+
+/*
+ * select_point selects the |idx|th point from a precomputation table and
+ * copies it to out.
+ */
+static void select_point(const u64 idx, unsigned int size,
+                         const smallfelem pre_comp[16][3], smallfelem out[3])
+{
+    unsigned i, j;
+    u64 *outlimbs = &out[0][0];
+
+    memset(out, 0, sizeof(*out) * 3);
+
+    for (i = 0; i < size; i++) {
+        const u64 *inlimbs = (u64 *)&pre_comp[i][0][0];
+        u64 mask = i ^ idx;
+        mask |= mask >> 4;
+        mask |= mask >> 2;
+        mask |= mask >> 1;
+        mask &= 1;
+        mask--;
+        for (j = 0; j < NLIMBS * 3; j++)
+            outlimbs[j] |= inlimbs[j] & mask;
+    }
+}
+
+/* get_bit returns the |i|th bit in |in| */
+static char get_bit(const felem_bytearray in, int i)
+{
+    if ((i < 0) || (i >= 256))
+        return 0;
+    return (in[i >> 3] >> (i & 7)) & 1;
+}
+
+/*
+ * Interleaved point multiplication using precomputed point multiples: The
+ * small point multiples 0*P, 1*P, ..., 17*P are in pre_comp[], the scalars
+ * in scalars[]. If g_scalar is non-NULL, we also add this multiple of the
+ * generator, using certain (large) precomputed multiples in g_pre_comp.
+ * Output point (X, Y, Z) is stored in x_out, y_out, z_out
+ */
+static void batch_mul(felem x_out, felem y_out, felem z_out,
+                      const felem_bytearray scalars[],
+                      const unsigned num_points, const u8 *g_scalar,
+                      const int mixed, const smallfelem pre_comp[][17][3],
+                      const smallfelem g_pre_comp[2][16][3])
+{
+    int i, skip;
+    unsigned num, gen_mul = (g_scalar != NULL);
+    felem nq[3], ftmp;
+    smallfelem tmp[3];
+    u64 bits;
+    u8 sign, digit;
+
+    /* set nq to the point at infinity */
+    memset(nq, 0, sizeof(nq));
+
+    /*
+     * Loop over all scalars msb-to-lsb, interleaving additions of multiples
+     * of the generator (two in each of the last 32 rounds) and additions of
+     * other points multiples (every 5th round).
+     */
+    skip = 1;                   /* save two point operations in the first
+                                 * round */
+    for (i = (num_points ? 255 : 31); i >= 0; --i) {
+        /* double */
+        if (!skip)
+            point_double(nq[0], nq[1], nq[2], nq[0], nq[1], nq[2]);
+
+        /* add multiples of the generator */
+        if (gen_mul && (i <= 31)) {
+            /* first, look 32 bits upwards */
+            bits = get_bit(g_scalar, i + 224) << 3;
+            bits |= get_bit(g_scalar, i + 160) << 2;
+            bits |= get_bit(g_scalar, i + 96) << 1;
+            bits |= get_bit(g_scalar, i + 32);
+            /* select the point to add, in constant time */
+            select_point(bits, 16, g_pre_comp[1], tmp);
+
+            if (!skip) {
+                /* Arg 1 below is for "mixed" */
+                point_add(nq[0], nq[1], nq[2],
+                          nq[0], nq[1], nq[2], 1, tmp[0], tmp[1], tmp[2]);
+            } else {
+                smallfelem_expand(nq[0], tmp[0]);
+                smallfelem_expand(nq[1], tmp[1]);
+                smallfelem_expand(nq[2], tmp[2]);
+                skip = 0;
+            }
+
+            /* second, look at the current position */
+            bits = get_bit(g_scalar, i + 192) << 3;
+            bits |= get_bit(g_scalar, i + 128) << 2;
+            bits |= get_bit(g_scalar, i + 64) << 1;
+            bits |= get_bit(g_scalar, i);
+            /* select the point to add, in constant time */
+            select_point(bits, 16, g_pre_comp[0], tmp);
+            /* Arg 1 below is for "mixed" */
+            point_add(nq[0], nq[1], nq[2],
+                      nq[0], nq[1], nq[2], 1, tmp[0], tmp[1], tmp[2]);
+        }
+
+        /* do other additions every 5 doublings */
+        if (num_points && (i % 5 == 0)) {
+            /* loop over all scalars */
+            for (num = 0; num < num_points; ++num) {
+                bits = get_bit(scalars[num], i + 4) << 5;
+                bits |= get_bit(scalars[num], i + 3) << 4;
+                bits |= get_bit(scalars[num], i + 2) << 3;
+                bits |= get_bit(scalars[num], i + 1) << 2;
+                bits |= get_bit(scalars[num], i) << 1;
+                bits |= get_bit(scalars[num], i - 1);
+                ec_GFp_nistp_recode_scalar_bits(&sign, &digit, bits);
+
+                /*
+                 * select the point to add or subtract, in constant time
+                 */
+                select_point(digit, 17, pre_comp[num], tmp);
+                smallfelem_neg(ftmp, tmp[1]); /* (X, -Y, Z) is the negative
+                                               * point */
+                copy_small_conditional(ftmp, tmp[1], (((limb) sign) - 1));
+                felem_contract(tmp[1], ftmp);
+
+                if (!skip) {
+                    point_add(nq[0], nq[1], nq[2],
+                              nq[0], nq[1], nq[2],
+                              mixed, tmp[0], tmp[1], tmp[2]);
+                } else {
+                    smallfelem_expand(nq[0], tmp[0]);
+                    smallfelem_expand(nq[1], tmp[1]);
+                    smallfelem_expand(nq[2], tmp[2]);
+                    skip = 0;
+                }
+            }
+        }
+    }
+    felem_assign(x_out, nq[0]);
+    felem_assign(y_out, nq[1]);
+    felem_assign(z_out, nq[2]);
+}
+
+/* Precomputation for the group generator. */
+struct nistp256_pre_comp_st {
+    smallfelem g_pre_comp[2][16][3];
+    int references;
+    CRYPTO_RWLOCK *lock;
+};
+
+const EC_METHOD *EC_GFp_nistp256_method(void)
+{
+    static const EC_METHOD ret = {
+        EC_FLAGS_DEFAULT_OCT,
+        NID_X9_62_prime_field,
+        ec_GFp_nistp256_group_init,
+        ec_GFp_simple_group_finish,
+        ec_GFp_simple_group_clear_finish,
+        ec_GFp_nist_group_copy,
+        ec_GFp_nistp256_group_set_curve,
+        ec_GFp_simple_group_get_curve,
+        ec_GFp_simple_group_get_degree,
+        ec_group_simple_order_bits,
+        ec_GFp_simple_group_check_discriminant,
+        ec_GFp_simple_point_init,
+        ec_GFp_simple_point_finish,
+        ec_GFp_simple_point_clear_finish,
+        ec_GFp_simple_point_copy,
+        ec_GFp_simple_point_set_to_infinity,
+        ec_GFp_simple_set_Jprojective_coordinates_GFp,
+        ec_GFp_simple_get_Jprojective_coordinates_GFp,
+        ec_GFp_simple_point_set_affine_coordinates,
+        ec_GFp_nistp256_point_get_affine_coordinates,
+        0 /* point_set_compressed_coordinates */ ,
+        0 /* point2oct */ ,
+        0 /* oct2point */ ,
+        ec_GFp_simple_add,
+        ec_GFp_simple_dbl,
+        ec_GFp_simple_invert,
+        ec_GFp_simple_is_at_infinity,
+        ec_GFp_simple_is_on_curve,
+        ec_GFp_simple_cmp,
+        ec_GFp_simple_make_affine,
+        ec_GFp_simple_points_make_affine,
+        ec_GFp_nistp256_points_mul,
+        ec_GFp_nistp256_precompute_mult,
+        ec_GFp_nistp256_have_precompute_mult,
+        ec_GFp_nist_field_mul,
+        ec_GFp_nist_field_sqr,
+        0 /* field_div */ ,
+        0 /* field_encode */ ,
+        0 /* field_decode */ ,
+        0,                      /* field_set_to_one */
+        ec_key_simple_priv2oct,
+        ec_key_simple_oct2priv,
+        0, /* set private */
+        ec_key_simple_generate_key,
+        ec_key_simple_check_key,
+        ec_key_simple_generate_public_key,
+        0, /* keycopy */
+        0, /* keyfinish */
+        ecdh_simple_compute_key
+    };
+
+    return &ret;
+}
+
+/******************************************************************************/
+/*
+ * FUNCTIONS TO MANAGE PRECOMPUTATION
+ */
+
+static NISTP256_PRE_COMP *nistp256_pre_comp_new()
+{
+    NISTP256_PRE_COMP *ret = OPENSSL_zalloc(sizeof(*ret));
+
+    if (ret == NULL) {
+        ECerr(EC_F_NISTP256_PRE_COMP_NEW, ERR_R_MALLOC_FAILURE);
+        return ret;
+    }
+
+    ret->references = 1;
+
+    ret->lock = CRYPTO_THREAD_lock_new();
+    if (ret->lock == NULL) {
+        ECerr(EC_F_NISTP256_PRE_COMP_NEW, ERR_R_MALLOC_FAILURE);
+        OPENSSL_free(ret);
+        return NULL;
+    }
+    return ret;
+}
+
+NISTP256_PRE_COMP *EC_nistp256_pre_comp_dup(NISTP256_PRE_COMP *p)
+{
+    int i;
+    if (p != NULL)
+        CRYPTO_atomic_add(&p->references, 1, &i, p->lock);
+    return p;
+}
+
+void EC_nistp256_pre_comp_free(NISTP256_PRE_COMP *pre)
+{
+    int i;
+
+    if (pre == NULL)
+        return;
+
+    CRYPTO_atomic_add(&pre->references, -1, &i, pre->lock);
+    REF_PRINT_COUNT("EC_nistp256", x);
+    if (i > 0)
+        return;
+    REF_ASSERT_ISNT(i < 0);
+
+    CRYPTO_THREAD_lock_free(pre->lock);
+    OPENSSL_free(pre);
+}
+
+/******************************************************************************/
+/*
+ * OPENSSL EC_METHOD FUNCTIONS
+ */
+
+int ec_GFp_nistp256_group_init(EC_GROUP *group)
+{
+    int ret;
+    ret = ec_GFp_simple_group_init(group);
+    group->a_is_minus3 = 1;
+    return ret;
+}
+
+int ec_GFp_nistp256_group_set_curve(EC_GROUP *group, const BIGNUM *p,
+                                    const BIGNUM *a, const BIGNUM *b,
+                                    BN_CTX *ctx)
+{
+    int ret = 0;
+    BN_CTX *new_ctx = NULL;
+    BIGNUM *curve_p, *curve_a, *curve_b;
+
+    if (ctx == NULL)
+        if ((ctx = new_ctx = BN_CTX_new()) == NULL)
+            return 0;
+    BN_CTX_start(ctx);
+    if (((curve_p = BN_CTX_get(ctx)) == NULL) ||
+        ((curve_a = BN_CTX_get(ctx)) == NULL) ||
+        ((curve_b = BN_CTX_get(ctx)) == NULL))
+        goto err;
+    BN_bin2bn(nistp256_curve_params[0], sizeof(felem_bytearray), curve_p);
+    BN_bin2bn(nistp256_curve_params[1], sizeof(felem_bytearray), curve_a);
+    BN_bin2bn(nistp256_curve_params[2], sizeof(felem_bytearray), curve_b);
+    if ((BN_cmp(curve_p, p)) || (BN_cmp(curve_a, a)) || (BN_cmp(curve_b, b))) {
+        ECerr(EC_F_EC_GFP_NISTP256_GROUP_SET_CURVE,
+              EC_R_WRONG_CURVE_PARAMETERS);
+        goto err;
+    }
+    group->field_mod_func = BN_nist_mod_256;
+    ret = ec_GFp_simple_group_set_curve(group, p, a, b, ctx);
+ err:
+    BN_CTX_end(ctx);
+    BN_CTX_free(new_ctx);
+    return ret;
+}
+
+/*
+ * Takes the Jacobian coordinates (X, Y, Z) of a point and returns (X', Y') =
+ * (X/Z^2, Y/Z^3)
+ */
+int ec_GFp_nistp256_point_get_affine_coordinates(const EC_GROUP *group,
+                                                 const EC_POINT *point,
+                                                 BIGNUM *x, BIGNUM *y,
+                                                 BN_CTX *ctx)
+{
+    felem z1, z2, x_in, y_in;
+    smallfelem x_out, y_out;
+    longfelem tmp;
+
+    if (EC_POINT_is_at_infinity(group, point)) {
+        ECerr(EC_F_EC_GFP_NISTP256_POINT_GET_AFFINE_COORDINATES,
+              EC_R_POINT_AT_INFINITY);
+        return 0;
+    }
+    if ((!BN_to_felem(x_in, point->X)) || (!BN_to_felem(y_in, point->Y)) ||
+        (!BN_to_felem(z1, point->Z)))
+        return 0;
+    felem_inv(z2, z1);
+    felem_square(tmp, z2);
+    felem_reduce(z1, tmp);
+    felem_mul(tmp, x_in, z1);
+    felem_reduce(x_in, tmp);
+    felem_contract(x_out, x_in);
+    if (x != NULL) {
+        if (!smallfelem_to_BN(x, x_out)) {
+            ECerr(EC_F_EC_GFP_NISTP256_POINT_GET_AFFINE_COORDINATES,
+                  ERR_R_BN_LIB);
+            return 0;
+        }
+    }
+    felem_mul(tmp, z1, z2);
+    felem_reduce(z1, tmp);
+    felem_mul(tmp, y_in, z1);
+    felem_reduce(y_in, tmp);
+    felem_contract(y_out, y_in);
+    if (y != NULL) {
+        if (!smallfelem_to_BN(y, y_out)) {
+            ECerr(EC_F_EC_GFP_NISTP256_POINT_GET_AFFINE_COORDINATES,
+                  ERR_R_BN_LIB);
+            return 0;
+        }
+    }
+    return 1;
+}
+
+/* points below is of size |num|, and tmp_smallfelems is of size |num+1| */
+static void make_points_affine(size_t num, smallfelem points[][3],
+                               smallfelem tmp_smallfelems[])
+{
+    /*
+     * Runs in constant time, unless an input is the point at infinity (which
+     * normally shouldn't happen).
+     */
+    ec_GFp_nistp_points_make_affine_internal(num,
+                                             points,
+                                             sizeof(smallfelem),
+                                             tmp_smallfelems,
+                                             (void (*)(void *))smallfelem_one,
+                                             (int (*)(const void *))
+                                             smallfelem_is_zero_int,
+                                             (void (*)(void *, const void *))
+                                             smallfelem_assign,
+                                             (void (*)(void *, const void *))
+                                             smallfelem_square_contract,
+                                             (void (*)
+                                              (void *, const void *,
+                                               const void *))
+                                             smallfelem_mul_contract,
+                                             (void (*)(void *, const void *))
+                                             smallfelem_inv_contract,
+                                             /* nothing to contract */
+                                             (void (*)(void *, const void *))
+                                             smallfelem_assign);
+}
+
+/*
+ * Computes scalar*generator + \sum scalars[i]*points[i], ignoring NULL
+ * values Result is stored in r (r can equal one of the inputs).
+ */
+int ec_GFp_nistp256_points_mul(const EC_GROUP *group, EC_POINT *r,
+                               const BIGNUM *scalar, size_t num,
+                               const EC_POINT *points[],
+                               const BIGNUM *scalars[], BN_CTX *ctx)
+{
+    int ret = 0;
+    int j;
+    int mixed = 0;
+    BN_CTX *new_ctx = NULL;
+    BIGNUM *x, *y, *z, *tmp_scalar;
+    felem_bytearray g_secret;
+    felem_bytearray *secrets = NULL;
+    smallfelem (*pre_comp)[17][3] = NULL;
+    smallfelem *tmp_smallfelems = NULL;
+    felem_bytearray tmp;
+    unsigned i, num_bytes;
+    int have_pre_comp = 0;
+    size_t num_points = num;
+    smallfelem x_in, y_in, z_in;
+    felem x_out, y_out, z_out;
+    NISTP256_PRE_COMP *pre = NULL;
+    const smallfelem(*g_pre_comp)[16][3] = NULL;
+    EC_POINT *generator = NULL;
+    const EC_POINT *p = NULL;
+    const BIGNUM *p_scalar = NULL;
+
+    if (ctx == NULL)
+        if ((ctx = new_ctx = BN_CTX_new()) == NULL)
+            return 0;
+    BN_CTX_start(ctx);
+    if (((x = BN_CTX_get(ctx)) == NULL) ||
+        ((y = BN_CTX_get(ctx)) == NULL) ||
+        ((z = BN_CTX_get(ctx)) == NULL) ||
+        ((tmp_scalar = BN_CTX_get(ctx)) == NULL))
+        goto err;
+
+    if (scalar != NULL) {
+        pre = group->pre_comp.nistp256;
+        if (pre)
+            /* we have precomputation, try to use it */
+            g_pre_comp = (const smallfelem(*)[16][3])pre->g_pre_comp;
+        else
+            /* try to use the standard precomputation */
+            g_pre_comp = &gmul[0];
+        generator = EC_POINT_new(group);
+        if (generator == NULL)
+            goto err;
+        /* get the generator from precomputation */
+        if (!smallfelem_to_BN(x, g_pre_comp[0][1][0]) ||
+            !smallfelem_to_BN(y, g_pre_comp[0][1][1]) ||
+            !smallfelem_to_BN(z, g_pre_comp[0][1][2])) {
+            ECerr(EC_F_EC_GFP_NISTP256_POINTS_MUL, ERR_R_BN_LIB);
+            goto err;
+        }
+        if (!EC_POINT_set_Jprojective_coordinates_GFp(group,
+                                                      generator, x, y, z,
+                                                      ctx))
+            goto err;
+        if (0 == EC_POINT_cmp(group, generator, group->generator, ctx))
+            /* precomputation matches generator */
+            have_pre_comp = 1;
+        else
+            /*
+             * we don't have valid precomputation: treat the generator as a
+             * random point
+             */
+            num_points++;
+    }
+    if (num_points > 0) {
+        if (num_points >= 3) {
+            /*
+             * unless we precompute multiples for just one or two points,
+             * converting those into affine form is time well spent
+             */
+            mixed = 1;
+        }
+        secrets = OPENSSL_malloc(sizeof(*secrets) * num_points);
+        pre_comp = OPENSSL_malloc(sizeof(*pre_comp) * num_points);
+        if (mixed)
+            tmp_smallfelems =
+              OPENSSL_malloc(sizeof(*tmp_smallfelems) * (num_points * 17 + 1));
+        if ((secrets == NULL) || (pre_comp == NULL)
+            || (mixed && (tmp_smallfelems == NULL))) {
+            ECerr(EC_F_EC_GFP_NISTP256_POINTS_MUL, ERR_R_MALLOC_FAILURE);
+            goto err;
+        }
+
+        /*
+         * we treat NULL scalars as 0, and NULL points as points at infinity,
+         * i.e., they contribute nothing to the linear combination
+         */
+        memset(secrets, 0, sizeof(*secrets) * num_points);
+        memset(pre_comp, 0, sizeof(*pre_comp) * num_points);
+        for (i = 0; i < num_points; ++i) {
+            if (i == num)
+                /*
+                 * we didn't have a valid precomputation, so we pick the
+                 * generator
+                 */
+            {
+                p = EC_GROUP_get0_generator(group);
+                p_scalar = scalar;
+            } else
+                /* the i^th point */
+            {
+                p = points[i];
+                p_scalar = scalars[i];
+            }
+            if ((p_scalar != NULL) && (p != NULL)) {
+                /* reduce scalar to 0 <= scalar < 2^256 */
+                if ((BN_num_bits(p_scalar) > 256)
+                    || (BN_is_negative(p_scalar))) {
+                    /*
+                     * this is an unusual input, and we don't guarantee
+                     * constant-timeness
+                     */
+                    if (!BN_nnmod(tmp_scalar, p_scalar, group->order, ctx)) {
+                        ECerr(EC_F_EC_GFP_NISTP256_POINTS_MUL, ERR_R_BN_LIB);
+                        goto err;
+                    }
+                    num_bytes = BN_bn2bin(tmp_scalar, tmp);
+                } else
+                    num_bytes = BN_bn2bin(p_scalar, tmp);
+                flip_endian(secrets[i], tmp, num_bytes);
+                /* precompute multiples */
+                if ((!BN_to_felem(x_out, p->X)) ||
+                    (!BN_to_felem(y_out, p->Y)) ||
+                    (!BN_to_felem(z_out, p->Z)))
+                    goto err;
+                felem_shrink(pre_comp[i][1][0], x_out);
+                felem_shrink(pre_comp[i][1][1], y_out);
+                felem_shrink(pre_comp[i][1][2], z_out);
+                for (j = 2; j <= 16; ++j) {
+                    if (j & 1) {
+                        point_add_small(pre_comp[i][j][0], pre_comp[i][j][1],
+                                        pre_comp[i][j][2], pre_comp[i][1][0],
+                                        pre_comp[i][1][1], pre_comp[i][1][2],
+                                        pre_comp[i][j - 1][0],
+                                        pre_comp[i][j - 1][1],
+                                        pre_comp[i][j - 1][2]);
+                    } else {
+                        point_double_small(pre_comp[i][j][0],
+                                           pre_comp[i][j][1],
+                                           pre_comp[i][j][2],
+                                           pre_comp[i][j / 2][0],
+                                           pre_comp[i][j / 2][1],
+                                           pre_comp[i][j / 2][2]);
+                    }
+                }
+            }
+        }
+        if (mixed)
+            make_points_affine(num_points * 17, pre_comp[0], tmp_smallfelems);
+    }
+
+    /* the scalar for the generator */
+    if ((scalar != NULL) && (have_pre_comp)) {
+        memset(g_secret, 0, sizeof(g_secret));
+        /* reduce scalar to 0 <= scalar < 2^256 */
+        if ((BN_num_bits(scalar) > 256) || (BN_is_negative(scalar))) {
+            /*
+             * this is an unusual input, and we don't guarantee
+             * constant-timeness
+             */
+            if (!BN_nnmod(tmp_scalar, scalar, group->order, ctx)) {
+                ECerr(EC_F_EC_GFP_NISTP256_POINTS_MUL, ERR_R_BN_LIB);
+                goto err;
+            }
+            num_bytes = BN_bn2bin(tmp_scalar, tmp);
+        } else
+            num_bytes = BN_bn2bin(scalar, tmp);
+        flip_endian(g_secret, tmp, num_bytes);
+        /* do the multiplication with generator precomputation */
+        batch_mul(x_out, y_out, z_out,
+                  (const felem_bytearray(*))secrets, num_points,
+                  g_secret,
+                  mixed, (const smallfelem(*)[17][3])pre_comp, g_pre_comp);
+    } else
+        /* do the multiplication without generator precomputation */
+        batch_mul(x_out, y_out, z_out,
+                  (const felem_bytearray(*))secrets, num_points,
+                  NULL, mixed, (const smallfelem(*)[17][3])pre_comp, NULL);
+    /* reduce the output to its unique minimal representation */
+    felem_contract(x_in, x_out);
+    felem_contract(y_in, y_out);
+    felem_contract(z_in, z_out);
+    if ((!smallfelem_to_BN(x, x_in)) || (!smallfelem_to_BN(y, y_in)) ||
+        (!smallfelem_to_BN(z, z_in))) {
+        ECerr(EC_F_EC_GFP_NISTP256_POINTS_MUL, ERR_R_BN_LIB);
+        goto err;
+    }
+    ret = EC_POINT_set_Jprojective_coordinates_GFp(group, r, x, y, z, ctx);
+
+ err:
+    BN_CTX_end(ctx);
+    EC_POINT_free(generator);
+    BN_CTX_free(new_ctx);
+    OPENSSL_free(secrets);
+    OPENSSL_free(pre_comp);
+    OPENSSL_free(tmp_smallfelems);
+    return ret;
+}
+
+int ec_GFp_nistp256_precompute_mult(EC_GROUP *group, BN_CTX *ctx)
+{
+    int ret = 0;
+    NISTP256_PRE_COMP *pre = NULL;
+    int i, j;
+    BN_CTX *new_ctx = NULL;
+    BIGNUM *x, *y;
+    EC_POINT *generator = NULL;
+    smallfelem tmp_smallfelems[32];
+    felem x_tmp, y_tmp, z_tmp;
+
+    /* throw away old precomputation */
+    EC_pre_comp_free(group);
+    if (ctx == NULL)
+        if ((ctx = new_ctx = BN_CTX_new()) == NULL)
+            return 0;
+    BN_CTX_start(ctx);
+    if (((x = BN_CTX_get(ctx)) == NULL) || ((y = BN_CTX_get(ctx)) == NULL))
+        goto err;
+    /* get the generator */
+    if (group->generator == NULL)
+        goto err;
+    generator = EC_POINT_new(group);
+    if (generator == NULL)
+        goto err;
+    BN_bin2bn(nistp256_curve_params[3], sizeof(felem_bytearray), x);
+    BN_bin2bn(nistp256_curve_params[4], sizeof(felem_bytearray), y);
+    if (!EC_POINT_set_affine_coordinates_GFp(group, generator, x, y, ctx))
+        goto err;
+    if ((pre = nistp256_pre_comp_new()) == NULL)
+        goto err;
+    /*
+     * if the generator is the standard one, use built-in precomputation
+     */
+    if (0 == EC_POINT_cmp(group, generator, group->generator, ctx)) {
+        memcpy(pre->g_pre_comp, gmul, sizeof(pre->g_pre_comp));
+        goto done;
+    }
+    if ((!BN_to_felem(x_tmp, group->generator->X)) ||
+        (!BN_to_felem(y_tmp, group->generator->Y)) ||
+        (!BN_to_felem(z_tmp, group->generator->Z)))
+        goto err;
+    felem_shrink(pre->g_pre_comp[0][1][0], x_tmp);
+    felem_shrink(pre->g_pre_comp[0][1][1], y_tmp);
+    felem_shrink(pre->g_pre_comp[0][1][2], z_tmp);
+    /*
+     * compute 2^64*G, 2^128*G, 2^192*G for the first table, 2^32*G, 2^96*G,
+     * 2^160*G, 2^224*G for the second one
+     */
+    for (i = 1; i <= 8; i <<= 1) {
+        point_double_small(pre->g_pre_comp[1][i][0], pre->g_pre_comp[1][i][1],
+                           pre->g_pre_comp[1][i][2], pre->g_pre_comp[0][i][0],
+                           pre->g_pre_comp[0][i][1],
+                           pre->g_pre_comp[0][i][2]);
+        for (j = 0; j < 31; ++j) {
+            point_double_small(pre->g_pre_comp[1][i][0],
+                               pre->g_pre_comp[1][i][1],
+                               pre->g_pre_comp[1][i][2],
+                               pre->g_pre_comp[1][i][0],
+                               pre->g_pre_comp[1][i][1],
+                               pre->g_pre_comp[1][i][2]);
+        }
+        if (i == 8)
+            break;
+        point_double_small(pre->g_pre_comp[0][2 * i][0],
+                           pre->g_pre_comp[0][2 * i][1],
+                           pre->g_pre_comp[0][2 * i][2],
+                           pre->g_pre_comp[1][i][0], pre->g_pre_comp[1][i][1],
+                           pre->g_pre_comp[1][i][2]);
+        for (j = 0; j < 31; ++j) {
+            point_double_small(pre->g_pre_comp[0][2 * i][0],
+                               pre->g_pre_comp[0][2 * i][1],
+                               pre->g_pre_comp[0][2 * i][2],
+                               pre->g_pre_comp[0][2 * i][0],
+                               pre->g_pre_comp[0][2 * i][1],
+                               pre->g_pre_comp[0][2 * i][2]);
+        }
+    }
+    for (i = 0; i < 2; i++) {
+        /* g_pre_comp[i][0] is the point at infinity */
+        memset(pre->g_pre_comp[i][0], 0, sizeof(pre->g_pre_comp[i][0]));
+        /* the remaining multiples */
+        /* 2^64*G + 2^128*G resp. 2^96*G + 2^160*G */
+        point_add_small(pre->g_pre_comp[i][6][0], pre->g_pre_comp[i][6][1],
+                        pre->g_pre_comp[i][6][2], pre->g_pre_comp[i][4][0],
+                        pre->g_pre_comp[i][4][1], pre->g_pre_comp[i][4][2],
+                        pre->g_pre_comp[i][2][0], pre->g_pre_comp[i][2][1],
+                        pre->g_pre_comp[i][2][2]);
+        /* 2^64*G + 2^192*G resp. 2^96*G + 2^224*G */
+        point_add_small(pre->g_pre_comp[i][10][0], pre->g_pre_comp[i][10][1],
+                        pre->g_pre_comp[i][10][2], pre->g_pre_comp[i][8][0],
+                        pre->g_pre_comp[i][8][1], pre->g_pre_comp[i][8][2],
+                        pre->g_pre_comp[i][2][0], pre->g_pre_comp[i][2][1],
+                        pre->g_pre_comp[i][2][2]);
+        /* 2^128*G + 2^192*G resp. 2^160*G + 2^224*G */
+        point_add_small(pre->g_pre_comp[i][12][0], pre->g_pre_comp[i][12][1],
+                        pre->g_pre_comp[i][12][2], pre->g_pre_comp[i][8][0],
+                        pre->g_pre_comp[i][8][1], pre->g_pre_comp[i][8][2],
+                        pre->g_pre_comp[i][4][0], pre->g_pre_comp[i][4][1],
+                        pre->g_pre_comp[i][4][2]);
+        /*
+         * 2^64*G + 2^128*G + 2^192*G resp. 2^96*G + 2^160*G + 2^224*G
+         */
+        point_add_small(pre->g_pre_comp[i][14][0], pre->g_pre_comp[i][14][1],
+                        pre->g_pre_comp[i][14][2], pre->g_pre_comp[i][12][0],
+                        pre->g_pre_comp[i][12][1], pre->g_pre_comp[i][12][2],
+                        pre->g_pre_comp[i][2][0], pre->g_pre_comp[i][2][1],
+                        pre->g_pre_comp[i][2][2]);
+        for (j = 1; j < 8; ++j) {
+            /* odd multiples: add G resp. 2^32*G */
+            point_add_small(pre->g_pre_comp[i][2 * j + 1][0],
+                            pre->g_pre_comp[i][2 * j + 1][1],
+                            pre->g_pre_comp[i][2 * j + 1][2],
+                            pre->g_pre_comp[i][2 * j][0],
+                            pre->g_pre_comp[i][2 * j][1],
+                            pre->g_pre_comp[i][2 * j][2],
+                            pre->g_pre_comp[i][1][0],
+                            pre->g_pre_comp[i][1][1],
+                            pre->g_pre_comp[i][1][2]);
+        }
+    }
+    make_points_affine(31, &(pre->g_pre_comp[0][1]), tmp_smallfelems);
+
+ done:
+    SETPRECOMP(group, nistp256, pre);
+    pre = NULL;
+    ret = 1;
+
+ err:
+    BN_CTX_end(ctx);
+    EC_POINT_free(generator);
+    BN_CTX_free(new_ctx);
+    EC_nistp256_pre_comp_free(pre);
+    return ret;
+}
+
+int ec_GFp_nistp256_have_precompute_mult(const EC_GROUP *group)
+{
+    return HAVEPRECOMP(group, nistp256);
+}
+#endif
diff --git a/crypto/ec/ecp_nistp521.c b/crypto/ec/ecp_nistp521.c
new file mode 100644
index 000000000..7207494b8
--- /dev/null
+++ b/crypto/ec/ecp_nistp521.c
@@ -0,0 +1,2146 @@
+/*
+ * Copyright 2011-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/* Copyright 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ *
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/*
+ * A 64-bit implementation of the NIST P-521 elliptic curve point multiplication
+ *
+ * OpenSSL integration was taken from Emilia Kasper's work in ecp_nistp224.c.
+ * Otherwise based on Emilia's P224 work, which was inspired by my curve25519
+ * work which got its smarts from Daniel J. Bernstein's work on the same.
+ */
+
+#include 
+#ifdef OPENSSL_NO_EC_NISTP_64_GCC_128
+NON_EMPTY_TRANSLATION_UNIT
+#else
+
+# ifndef OPENSSL_SYS_VMS
+#  include 
+# else
+#  include 
+# endif
+
+# include 
+# include 
+# include "ec_lcl.h"
+
+# if defined(__GNUC__) && (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1))
+  /* even with gcc, the typedef won't work for 32-bit platforms */
+typedef __uint128_t uint128_t;  /* nonstandard; implemented by gcc on 64-bit
+                                 * platforms */
+# else
+#  error "Need GCC 3.1 or later to define type uint128_t"
+# endif
+
+typedef uint8_t u8;
+typedef uint64_t u64;
+typedef int64_t s64;
+
+/*
+ * The underlying field. P521 operates over GF(2^521-1). We can serialise an
+ * element of this field into 66 bytes where the most significant byte
+ * contains only a single bit. We call this an felem_bytearray.
+ */
+
+typedef u8 felem_bytearray[66];
+
+/*
+ * These are the parameters of P521, taken from FIPS 186-3, section D.1.2.5.
+ * These values are big-endian.
+ */
+static const felem_bytearray nistp521_curve_params[5] = {
+    {0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* p */
+     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+     0xff, 0xff},
+    {0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* a = -3 */
+     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+     0xff, 0xfc},
+    {0x00, 0x51, 0x95, 0x3e, 0xb9, 0x61, 0x8e, 0x1c, /* b */
+     0x9a, 0x1f, 0x92, 0x9a, 0x21, 0xa0, 0xb6, 0x85,
+     0x40, 0xee, 0xa2, 0xda, 0x72, 0x5b, 0x99, 0xb3,
+     0x15, 0xf3, 0xb8, 0xb4, 0x89, 0x91, 0x8e, 0xf1,
+     0x09, 0xe1, 0x56, 0x19, 0x39, 0x51, 0xec, 0x7e,
+     0x93, 0x7b, 0x16, 0x52, 0xc0, 0xbd, 0x3b, 0xb1,
+     0xbf, 0x07, 0x35, 0x73, 0xdf, 0x88, 0x3d, 0x2c,
+     0x34, 0xf1, 0xef, 0x45, 0x1f, 0xd4, 0x6b, 0x50,
+     0x3f, 0x00},
+    {0x00, 0xc6, 0x85, 0x8e, 0x06, 0xb7, 0x04, 0x04, /* x */
+     0xe9, 0xcd, 0x9e, 0x3e, 0xcb, 0x66, 0x23, 0x95,
+     0xb4, 0x42, 0x9c, 0x64, 0x81, 0x39, 0x05, 0x3f,
+     0xb5, 0x21, 0xf8, 0x28, 0xaf, 0x60, 0x6b, 0x4d,
+     0x3d, 0xba, 0xa1, 0x4b, 0x5e, 0x77, 0xef, 0xe7,
+     0x59, 0x28, 0xfe, 0x1d, 0xc1, 0x27, 0xa2, 0xff,
+     0xa8, 0xde, 0x33, 0x48, 0xb3, 0xc1, 0x85, 0x6a,
+     0x42, 0x9b, 0xf9, 0x7e, 0x7e, 0x31, 0xc2, 0xe5,
+     0xbd, 0x66},
+    {0x01, 0x18, 0x39, 0x29, 0x6a, 0x78, 0x9a, 0x3b, /* y */
+     0xc0, 0x04, 0x5c, 0x8a, 0x5f, 0xb4, 0x2c, 0x7d,
+     0x1b, 0xd9, 0x98, 0xf5, 0x44, 0x49, 0x57, 0x9b,
+     0x44, 0x68, 0x17, 0xaf, 0xbd, 0x17, 0x27, 0x3e,
+     0x66, 0x2c, 0x97, 0xee, 0x72, 0x99, 0x5e, 0xf4,
+     0x26, 0x40, 0xc5, 0x50, 0xb9, 0x01, 0x3f, 0xad,
+     0x07, 0x61, 0x35, 0x3c, 0x70, 0x86, 0xa2, 0x72,
+     0xc2, 0x40, 0x88, 0xbe, 0x94, 0x76, 0x9f, 0xd1,
+     0x66, 0x50}
+};
+
+/*-
+ * The representation of field elements.
+ * ------------------------------------
+ *
+ * We represent field elements with nine values. These values are either 64 or
+ * 128 bits and the field element represented is:
+ *   v[0]*2^0 + v[1]*2^58 + v[2]*2^116 + ... + v[8]*2^464  (mod p)
+ * Each of the nine values is called a 'limb'. Since the limbs are spaced only
+ * 58 bits apart, but are greater than 58 bits in length, the most significant
+ * bits of each limb overlap with the least significant bits of the next.
+ *
+ * A field element with 64-bit limbs is an 'felem'. One with 128-bit limbs is a
+ * 'largefelem' */
+
+# define NLIMBS 9
+
+typedef uint64_t limb;
+typedef limb felem[NLIMBS];
+typedef uint128_t largefelem[NLIMBS];
+
+static const limb bottom57bits = 0x1ffffffffffffff;
+static const limb bottom58bits = 0x3ffffffffffffff;
+
+/*
+ * bin66_to_felem takes a little-endian byte array and converts it into felem
+ * form. This assumes that the CPU is little-endian.
+ */
+static void bin66_to_felem(felem out, const u8 in[66])
+{
+    out[0] = (*((limb *) & in[0])) & bottom58bits;
+    out[1] = (*((limb *) & in[7]) >> 2) & bottom58bits;
+    out[2] = (*((limb *) & in[14]) >> 4) & bottom58bits;
+    out[3] = (*((limb *) & in[21]) >> 6) & bottom58bits;
+    out[4] = (*((limb *) & in[29])) & bottom58bits;
+    out[5] = (*((limb *) & in[36]) >> 2) & bottom58bits;
+    out[6] = (*((limb *) & in[43]) >> 4) & bottom58bits;
+    out[7] = (*((limb *) & in[50]) >> 6) & bottom58bits;
+    out[8] = (*((limb *) & in[58])) & bottom57bits;
+}
+
+/*
+ * felem_to_bin66 takes an felem and serialises into a little endian, 66 byte
+ * array. This assumes that the CPU is little-endian.
+ */
+static void felem_to_bin66(u8 out[66], const felem in)
+{
+    memset(out, 0, 66);
+    (*((limb *) & out[0])) = in[0];
+    (*((limb *) & out[7])) |= in[1] << 2;
+    (*((limb *) & out[14])) |= in[2] << 4;
+    (*((limb *) & out[21])) |= in[3] << 6;
+    (*((limb *) & out[29])) = in[4];
+    (*((limb *) & out[36])) |= in[5] << 2;
+    (*((limb *) & out[43])) |= in[6] << 4;
+    (*((limb *) & out[50])) |= in[7] << 6;
+    (*((limb *) & out[58])) = in[8];
+}
+
+/* To preserve endianness when using BN_bn2bin and BN_bin2bn */
+static void flip_endian(u8 *out, const u8 *in, unsigned len)
+{
+    unsigned i;
+    for (i = 0; i < len; ++i)
+        out[i] = in[len - 1 - i];
+}
+
+/* BN_to_felem converts an OpenSSL BIGNUM into an felem */
+static int BN_to_felem(felem out, const BIGNUM *bn)
+{
+    felem_bytearray b_in;
+    felem_bytearray b_out;
+    unsigned num_bytes;
+
+    /* BN_bn2bin eats leading zeroes */
+    memset(b_out, 0, sizeof(b_out));
+    num_bytes = BN_num_bytes(bn);
+    if (num_bytes > sizeof b_out) {
+        ECerr(EC_F_BN_TO_FELEM, EC_R_BIGNUM_OUT_OF_RANGE);
+        return 0;
+    }
+    if (BN_is_negative(bn)) {
+        ECerr(EC_F_BN_TO_FELEM, EC_R_BIGNUM_OUT_OF_RANGE);
+        return 0;
+    }
+    num_bytes = BN_bn2bin(bn, b_in);
+    flip_endian(b_out, b_in, num_bytes);
+    bin66_to_felem(out, b_out);
+    return 1;
+}
+
+/* felem_to_BN converts an felem into an OpenSSL BIGNUM */
+static BIGNUM *felem_to_BN(BIGNUM *out, const felem in)
+{
+    felem_bytearray b_in, b_out;
+    felem_to_bin66(b_in, in);
+    flip_endian(b_out, b_in, sizeof b_out);
+    return BN_bin2bn(b_out, sizeof b_out, out);
+}
+
+/*-
+ * Field operations
+ * ----------------
+ */
+
+static void felem_one(felem out)
+{
+    out[0] = 1;
+    out[1] = 0;
+    out[2] = 0;
+    out[3] = 0;
+    out[4] = 0;
+    out[5] = 0;
+    out[6] = 0;
+    out[7] = 0;
+    out[8] = 0;
+}
+
+static void felem_assign(felem out, const felem in)
+{
+    out[0] = in[0];
+    out[1] = in[1];
+    out[2] = in[2];
+    out[3] = in[3];
+    out[4] = in[4];
+    out[5] = in[5];
+    out[6] = in[6];
+    out[7] = in[7];
+    out[8] = in[8];
+}
+
+/* felem_sum64 sets out = out + in. */
+static void felem_sum64(felem out, const felem in)
+{
+    out[0] += in[0];
+    out[1] += in[1];
+    out[2] += in[2];
+    out[3] += in[3];
+    out[4] += in[4];
+    out[5] += in[5];
+    out[6] += in[6];
+    out[7] += in[7];
+    out[8] += in[8];
+}
+
+/* felem_scalar sets out = in * scalar */
+static void felem_scalar(felem out, const felem in, limb scalar)
+{
+    out[0] = in[0] * scalar;
+    out[1] = in[1] * scalar;
+    out[2] = in[2] * scalar;
+    out[3] = in[3] * scalar;
+    out[4] = in[4] * scalar;
+    out[5] = in[5] * scalar;
+    out[6] = in[6] * scalar;
+    out[7] = in[7] * scalar;
+    out[8] = in[8] * scalar;
+}
+
+/* felem_scalar64 sets out = out * scalar */
+static void felem_scalar64(felem out, limb scalar)
+{
+    out[0] *= scalar;
+    out[1] *= scalar;
+    out[2] *= scalar;
+    out[3] *= scalar;
+    out[4] *= scalar;
+    out[5] *= scalar;
+    out[6] *= scalar;
+    out[7] *= scalar;
+    out[8] *= scalar;
+}
+
+/* felem_scalar128 sets out = out * scalar */
+static void felem_scalar128(largefelem out, limb scalar)
+{
+    out[0] *= scalar;
+    out[1] *= scalar;
+    out[2] *= scalar;
+    out[3] *= scalar;
+    out[4] *= scalar;
+    out[5] *= scalar;
+    out[6] *= scalar;
+    out[7] *= scalar;
+    out[8] *= scalar;
+}
+
+/*-
+ * felem_neg sets |out| to |-in|
+ * On entry:
+ *   in[i] < 2^59 + 2^14
+ * On exit:
+ *   out[i] < 2^62
+ */
+static void felem_neg(felem out, const felem in)
+{
+    /* In order to prevent underflow, we subtract from 0 mod p. */
+    static const limb two62m3 = (((limb) 1) << 62) - (((limb) 1) << 5);
+    static const limb two62m2 = (((limb) 1) << 62) - (((limb) 1) << 4);
+
+    out[0] = two62m3 - in[0];
+    out[1] = two62m2 - in[1];
+    out[2] = two62m2 - in[2];
+    out[3] = two62m2 - in[3];
+    out[4] = two62m2 - in[4];
+    out[5] = two62m2 - in[5];
+    out[6] = two62m2 - in[6];
+    out[7] = two62m2 - in[7];
+    out[8] = two62m2 - in[8];
+}
+
+/*-
+ * felem_diff64 subtracts |in| from |out|
+ * On entry:
+ *   in[i] < 2^59 + 2^14
+ * On exit:
+ *   out[i] < out[i] + 2^62
+ */
+static void felem_diff64(felem out, const felem in)
+{
+    /*
+     * In order to prevent underflow, we add 0 mod p before subtracting.
+     */
+    static const limb two62m3 = (((limb) 1) << 62) - (((limb) 1) << 5);
+    static const limb two62m2 = (((limb) 1) << 62) - (((limb) 1) << 4);
+
+    out[0] += two62m3 - in[0];
+    out[1] += two62m2 - in[1];
+    out[2] += two62m2 - in[2];
+    out[3] += two62m2 - in[3];
+    out[4] += two62m2 - in[4];
+    out[5] += two62m2 - in[5];
+    out[6] += two62m2 - in[6];
+    out[7] += two62m2 - in[7];
+    out[8] += two62m2 - in[8];
+}
+
+/*-
+ * felem_diff_128_64 subtracts |in| from |out|
+ * On entry:
+ *   in[i] < 2^62 + 2^17
+ * On exit:
+ *   out[i] < out[i] + 2^63
+ */
+static void felem_diff_128_64(largefelem out, const felem in)
+{
+    /*
+     * In order to prevent underflow, we add 0 mod p before subtracting.
+     */
+    static const limb two63m6 = (((limb) 1) << 62) - (((limb) 1) << 5);
+    static const limb two63m5 = (((limb) 1) << 62) - (((limb) 1) << 4);
+
+    out[0] += two63m6 - in[0];
+    out[1] += two63m5 - in[1];
+    out[2] += two63m5 - in[2];
+    out[3] += two63m5 - in[3];
+    out[4] += two63m5 - in[4];
+    out[5] += two63m5 - in[5];
+    out[6] += two63m5 - in[6];
+    out[7] += two63m5 - in[7];
+    out[8] += two63m5 - in[8];
+}
+
+/*-
+ * felem_diff_128_64 subtracts |in| from |out|
+ * On entry:
+ *   in[i] < 2^126
+ * On exit:
+ *   out[i] < out[i] + 2^127 - 2^69
+ */
+static void felem_diff128(largefelem out, const largefelem in)
+{
+    /*
+     * In order to prevent underflow, we add 0 mod p before subtracting.
+     */
+    static const uint128_t two127m70 =
+        (((uint128_t) 1) << 127) - (((uint128_t) 1) << 70);
+    static const uint128_t two127m69 =
+        (((uint128_t) 1) << 127) - (((uint128_t) 1) << 69);
+
+    out[0] += (two127m70 - in[0]);
+    out[1] += (two127m69 - in[1]);
+    out[2] += (two127m69 - in[2]);
+    out[3] += (two127m69 - in[3]);
+    out[4] += (two127m69 - in[4]);
+    out[5] += (two127m69 - in[5]);
+    out[6] += (two127m69 - in[6]);
+    out[7] += (two127m69 - in[7]);
+    out[8] += (two127m69 - in[8]);
+}
+
+/*-
+ * felem_square sets |out| = |in|^2
+ * On entry:
+ *   in[i] < 2^62
+ * On exit:
+ *   out[i] < 17 * max(in[i]) * max(in[i])
+ */
+static void felem_square(largefelem out, const felem in)
+{
+    felem inx2, inx4;
+    felem_scalar(inx2, in, 2);
+    felem_scalar(inx4, in, 4);
+
+    /*-
+     * We have many cases were we want to do
+     *   in[x] * in[y] +
+     *   in[y] * in[x]
+     * This is obviously just
+     *   2 * in[x] * in[y]
+     * However, rather than do the doubling on the 128 bit result, we
+     * double one of the inputs to the multiplication by reading from
+     * |inx2|
+     */
+
+    out[0] = ((uint128_t) in[0]) * in[0];
+    out[1] = ((uint128_t) in[0]) * inx2[1];
+    out[2] = ((uint128_t) in[0]) * inx2[2] + ((uint128_t) in[1]) * in[1];
+    out[3] = ((uint128_t) in[0]) * inx2[3] + ((uint128_t) in[1]) * inx2[2];
+    out[4] = ((uint128_t) in[0]) * inx2[4] +
+             ((uint128_t) in[1]) * inx2[3] + ((uint128_t) in[2]) * in[2];
+    out[5] = ((uint128_t) in[0]) * inx2[5] +
+             ((uint128_t) in[1]) * inx2[4] + ((uint128_t) in[2]) * inx2[3];
+    out[6] = ((uint128_t) in[0]) * inx2[6] +
+             ((uint128_t) in[1]) * inx2[5] +
+             ((uint128_t) in[2]) * inx2[4] + ((uint128_t) in[3]) * in[3];
+    out[7] = ((uint128_t) in[0]) * inx2[7] +
+             ((uint128_t) in[1]) * inx2[6] +
+             ((uint128_t) in[2]) * inx2[5] + ((uint128_t) in[3]) * inx2[4];
+    out[8] = ((uint128_t) in[0]) * inx2[8] +
+             ((uint128_t) in[1]) * inx2[7] +
+             ((uint128_t) in[2]) * inx2[6] +
+             ((uint128_t) in[3]) * inx2[5] + ((uint128_t) in[4]) * in[4];
+
+    /*
+     * The remaining limbs fall above 2^521, with the first falling at 2^522.
+     * They correspond to locations one bit up from the limbs produced above
+     * so we would have to multiply by two to align them. Again, rather than
+     * operate on the 128-bit result, we double one of the inputs to the
+     * multiplication. If we want to double for both this reason, and the
+     * reason above, then we end up multiplying by four.
+     */
+
+    /* 9 */
+    out[0] += ((uint128_t) in[1]) * inx4[8] +
+              ((uint128_t) in[2]) * inx4[7] +
+              ((uint128_t) in[3]) * inx4[6] + ((uint128_t) in[4]) * inx4[5];
+
+    /* 10 */
+    out[1] += ((uint128_t) in[2]) * inx4[8] +
+              ((uint128_t) in[3]) * inx4[7] +
+              ((uint128_t) in[4]) * inx4[6] + ((uint128_t) in[5]) * inx2[5];
+
+    /* 11 */
+    out[2] += ((uint128_t) in[3]) * inx4[8] +
+              ((uint128_t) in[4]) * inx4[7] + ((uint128_t) in[5]) * inx4[6];
+
+    /* 12 */
+    out[3] += ((uint128_t) in[4]) * inx4[8] +
+              ((uint128_t) in[5]) * inx4[7] + ((uint128_t) in[6]) * inx2[6];
+
+    /* 13 */
+    out[4] += ((uint128_t) in[5]) * inx4[8] + ((uint128_t) in[6]) * inx4[7];
+
+    /* 14 */
+    out[5] += ((uint128_t) in[6]) * inx4[8] + ((uint128_t) in[7]) * inx2[7];
+
+    /* 15 */
+    out[6] += ((uint128_t) in[7]) * inx4[8];
+
+    /* 16 */
+    out[7] += ((uint128_t) in[8]) * inx2[8];
+}
+
+/*-
+ * felem_mul sets |out| = |in1| * |in2|
+ * On entry:
+ *   in1[i] < 2^64
+ *   in2[i] < 2^63
+ * On exit:
+ *   out[i] < 17 * max(in1[i]) * max(in2[i])
+ */
+static void felem_mul(largefelem out, const felem in1, const felem in2)
+{
+    felem in2x2;
+    felem_scalar(in2x2, in2, 2);
+
+    out[0] = ((uint128_t) in1[0]) * in2[0];
+
+    out[1] = ((uint128_t) in1[0]) * in2[1] +
+             ((uint128_t) in1[1]) * in2[0];
+
+    out[2] = ((uint128_t) in1[0]) * in2[2] +
+             ((uint128_t) in1[1]) * in2[1] +
+             ((uint128_t) in1[2]) * in2[0];
+
+    out[3] = ((uint128_t) in1[0]) * in2[3] +
+             ((uint128_t) in1[1]) * in2[2] +
+             ((uint128_t) in1[2]) * in2[1] +
+             ((uint128_t) in1[3]) * in2[0];
+
+    out[4] = ((uint128_t) in1[0]) * in2[4] +
+             ((uint128_t) in1[1]) * in2[3] +
+             ((uint128_t) in1[2]) * in2[2] +
+             ((uint128_t) in1[3]) * in2[1] +
+             ((uint128_t) in1[4]) * in2[0];
+
+    out[5] = ((uint128_t) in1[0]) * in2[5] +
+             ((uint128_t) in1[1]) * in2[4] +
+             ((uint128_t) in1[2]) * in2[3] +
+             ((uint128_t) in1[3]) * in2[2] +
+             ((uint128_t) in1[4]) * in2[1] +
+             ((uint128_t) in1[5]) * in2[0];
+
+    out[6] = ((uint128_t) in1[0]) * in2[6] +
+             ((uint128_t) in1[1]) * in2[5] +
+             ((uint128_t) in1[2]) * in2[4] +
+             ((uint128_t) in1[3]) * in2[3] +
+             ((uint128_t) in1[4]) * in2[2] +
+             ((uint128_t) in1[5]) * in2[1] +
+             ((uint128_t) in1[6]) * in2[0];
+
+    out[7] = ((uint128_t) in1[0]) * in2[7] +
+             ((uint128_t) in1[1]) * in2[6] +
+             ((uint128_t) in1[2]) * in2[5] +
+             ((uint128_t) in1[3]) * in2[4] +
+             ((uint128_t) in1[4]) * in2[3] +
+             ((uint128_t) in1[5]) * in2[2] +
+             ((uint128_t) in1[6]) * in2[1] +
+             ((uint128_t) in1[7]) * in2[0];
+
+    out[8] = ((uint128_t) in1[0]) * in2[8] +
+             ((uint128_t) in1[1]) * in2[7] +
+             ((uint128_t) in1[2]) * in2[6] +
+             ((uint128_t) in1[3]) * in2[5] +
+             ((uint128_t) in1[4]) * in2[4] +
+             ((uint128_t) in1[5]) * in2[3] +
+             ((uint128_t) in1[6]) * in2[2] +
+             ((uint128_t) in1[7]) * in2[1] +
+             ((uint128_t) in1[8]) * in2[0];
+
+    /* See comment in felem_square about the use of in2x2 here */
+
+    out[0] += ((uint128_t) in1[1]) * in2x2[8] +
+              ((uint128_t) in1[2]) * in2x2[7] +
+              ((uint128_t) in1[3]) * in2x2[6] +
+              ((uint128_t) in1[4]) * in2x2[5] +
+              ((uint128_t) in1[5]) * in2x2[4] +
+              ((uint128_t) in1[6]) * in2x2[3] +
+              ((uint128_t) in1[7]) * in2x2[2] +
+              ((uint128_t) in1[8]) * in2x2[1];
+
+    out[1] += ((uint128_t) in1[2]) * in2x2[8] +
+              ((uint128_t) in1[3]) * in2x2[7] +
+              ((uint128_t) in1[4]) * in2x2[6] +
+              ((uint128_t) in1[5]) * in2x2[5] +
+              ((uint128_t) in1[6]) * in2x2[4] +
+              ((uint128_t) in1[7]) * in2x2[3] +
+              ((uint128_t) in1[8]) * in2x2[2];
+
+    out[2] += ((uint128_t) in1[3]) * in2x2[8] +
+              ((uint128_t) in1[4]) * in2x2[7] +
+              ((uint128_t) in1[5]) * in2x2[6] +
+              ((uint128_t) in1[6]) * in2x2[5] +
+              ((uint128_t) in1[7]) * in2x2[4] +
+              ((uint128_t) in1[8]) * in2x2[3];
+
+    out[3] += ((uint128_t) in1[4]) * in2x2[8] +
+              ((uint128_t) in1[5]) * in2x2[7] +
+              ((uint128_t) in1[6]) * in2x2[6] +
+              ((uint128_t) in1[7]) * in2x2[5] +
+              ((uint128_t) in1[8]) * in2x2[4];
+
+    out[4] += ((uint128_t) in1[5]) * in2x2[8] +
+              ((uint128_t) in1[6]) * in2x2[7] +
+              ((uint128_t) in1[7]) * in2x2[6] +
+              ((uint128_t) in1[8]) * in2x2[5];
+
+    out[5] += ((uint128_t) in1[6]) * in2x2[8] +
+              ((uint128_t) in1[7]) * in2x2[7] +
+              ((uint128_t) in1[8]) * in2x2[6];
+
+    out[6] += ((uint128_t) in1[7]) * in2x2[8] +
+              ((uint128_t) in1[8]) * in2x2[7];
+
+    out[7] += ((uint128_t) in1[8]) * in2x2[8];
+}
+
+static const limb bottom52bits = 0xfffffffffffff;
+
+/*-
+ * felem_reduce converts a largefelem to an felem.
+ * On entry:
+ *   in[i] < 2^128
+ * On exit:
+ *   out[i] < 2^59 + 2^14
+ */
+static void felem_reduce(felem out, const largefelem in)
+{
+    u64 overflow1, overflow2;
+
+    out[0] = ((limb) in[0]) & bottom58bits;
+    out[1] = ((limb) in[1]) & bottom58bits;
+    out[2] = ((limb) in[2]) & bottom58bits;
+    out[3] = ((limb) in[3]) & bottom58bits;
+    out[4] = ((limb) in[4]) & bottom58bits;
+    out[5] = ((limb) in[5]) & bottom58bits;
+    out[6] = ((limb) in[6]) & bottom58bits;
+    out[7] = ((limb) in[7]) & bottom58bits;
+    out[8] = ((limb) in[8]) & bottom58bits;
+
+    /* out[i] < 2^58 */
+
+    out[1] += ((limb) in[0]) >> 58;
+    out[1] += (((limb) (in[0] >> 64)) & bottom52bits) << 6;
+    /*-
+     * out[1] < 2^58 + 2^6 + 2^58
+     *        = 2^59 + 2^6
+     */
+    out[2] += ((limb) (in[0] >> 64)) >> 52;
+
+    out[2] += ((limb) in[1]) >> 58;
+    out[2] += (((limb) (in[1] >> 64)) & bottom52bits) << 6;
+    out[3] += ((limb) (in[1] >> 64)) >> 52;
+
+    out[3] += ((limb) in[2]) >> 58;
+    out[3] += (((limb) (in[2] >> 64)) & bottom52bits) << 6;
+    out[4] += ((limb) (in[2] >> 64)) >> 52;
+
+    out[4] += ((limb) in[3]) >> 58;
+    out[4] += (((limb) (in[3] >> 64)) & bottom52bits) << 6;
+    out[5] += ((limb) (in[3] >> 64)) >> 52;
+
+    out[5] += ((limb) in[4]) >> 58;
+    out[5] += (((limb) (in[4] >> 64)) & bottom52bits) << 6;
+    out[6] += ((limb) (in[4] >> 64)) >> 52;
+
+    out[6] += ((limb) in[5]) >> 58;
+    out[6] += (((limb) (in[5] >> 64)) & bottom52bits) << 6;
+    out[7] += ((limb) (in[5] >> 64)) >> 52;
+
+    out[7] += ((limb) in[6]) >> 58;
+    out[7] += (((limb) (in[6] >> 64)) & bottom52bits) << 6;
+    out[8] += ((limb) (in[6] >> 64)) >> 52;
+
+    out[8] += ((limb) in[7]) >> 58;
+    out[8] += (((limb) (in[7] >> 64)) & bottom52bits) << 6;
+    /*-
+     * out[x > 1] < 2^58 + 2^6 + 2^58 + 2^12
+     *            < 2^59 + 2^13
+     */
+    overflow1 = ((limb) (in[7] >> 64)) >> 52;
+
+    overflow1 += ((limb) in[8]) >> 58;
+    overflow1 += (((limb) (in[8] >> 64)) & bottom52bits) << 6;
+    overflow2 = ((limb) (in[8] >> 64)) >> 52;
+
+    overflow1 <<= 1;            /* overflow1 < 2^13 + 2^7 + 2^59 */
+    overflow2 <<= 1;            /* overflow2 < 2^13 */
+
+    out[0] += overflow1;        /* out[0] < 2^60 */
+    out[1] += overflow2;        /* out[1] < 2^59 + 2^6 + 2^13 */
+
+    out[1] += out[0] >> 58;
+    out[0] &= bottom58bits;
+    /*-
+     * out[0] < 2^58
+     * out[1] < 2^59 + 2^6 + 2^13 + 2^2
+     *        < 2^59 + 2^14
+     */
+}
+
+static void felem_square_reduce(felem out, const felem in)
+{
+    largefelem tmp;
+    felem_square(tmp, in);
+    felem_reduce(out, tmp);
+}
+
+static void felem_mul_reduce(felem out, const felem in1, const felem in2)
+{
+    largefelem tmp;
+    felem_mul(tmp, in1, in2);
+    felem_reduce(out, tmp);
+}
+
+/*-
+ * felem_inv calculates |out| = |in|^{-1}
+ *
+ * Based on Fermat's Little Theorem:
+ *   a^p = a (mod p)
+ *   a^{p-1} = 1 (mod p)
+ *   a^{p-2} = a^{-1} (mod p)
+ */
+static void felem_inv(felem out, const felem in)
+{
+    felem ftmp, ftmp2, ftmp3, ftmp4;
+    largefelem tmp;
+    unsigned i;
+
+    felem_square(tmp, in);
+    felem_reduce(ftmp, tmp);    /* 2^1 */
+    felem_mul(tmp, in, ftmp);
+    felem_reduce(ftmp, tmp);    /* 2^2 - 2^0 */
+    felem_assign(ftmp2, ftmp);
+    felem_square(tmp, ftmp);
+    felem_reduce(ftmp, tmp);    /* 2^3 - 2^1 */
+    felem_mul(tmp, in, ftmp);
+    felem_reduce(ftmp, tmp);    /* 2^3 - 2^0 */
+    felem_square(tmp, ftmp);
+    felem_reduce(ftmp, tmp);    /* 2^4 - 2^1 */
+
+    felem_square(tmp, ftmp2);
+    felem_reduce(ftmp3, tmp);   /* 2^3 - 2^1 */
+    felem_square(tmp, ftmp3);
+    felem_reduce(ftmp3, tmp);   /* 2^4 - 2^2 */
+    felem_mul(tmp, ftmp3, ftmp2);
+    felem_reduce(ftmp3, tmp);   /* 2^4 - 2^0 */
+
+    felem_assign(ftmp2, ftmp3);
+    felem_square(tmp, ftmp3);
+    felem_reduce(ftmp3, tmp);   /* 2^5 - 2^1 */
+    felem_square(tmp, ftmp3);
+    felem_reduce(ftmp3, tmp);   /* 2^6 - 2^2 */
+    felem_square(tmp, ftmp3);
+    felem_reduce(ftmp3, tmp);   /* 2^7 - 2^3 */
+    felem_square(tmp, ftmp3);
+    felem_reduce(ftmp3, tmp);   /* 2^8 - 2^4 */
+    felem_assign(ftmp4, ftmp3);
+    felem_mul(tmp, ftmp3, ftmp);
+    felem_reduce(ftmp4, tmp);   /* 2^8 - 2^1 */
+    felem_square(tmp, ftmp4);
+    felem_reduce(ftmp4, tmp);   /* 2^9 - 2^2 */
+    felem_mul(tmp, ftmp3, ftmp2);
+    felem_reduce(ftmp3, tmp);   /* 2^8 - 2^0 */
+    felem_assign(ftmp2, ftmp3);
+
+    for (i = 0; i < 8; i++) {
+        felem_square(tmp, ftmp3);
+        felem_reduce(ftmp3, tmp); /* 2^16 - 2^8 */
+    }
+    felem_mul(tmp, ftmp3, ftmp2);
+    felem_reduce(ftmp3, tmp);   /* 2^16 - 2^0 */
+    felem_assign(ftmp2, ftmp3);
+
+    for (i = 0; i < 16; i++) {
+        felem_square(tmp, ftmp3);
+        felem_reduce(ftmp3, tmp); /* 2^32 - 2^16 */
+    }
+    felem_mul(tmp, ftmp3, ftmp2);
+    felem_reduce(ftmp3, tmp);   /* 2^32 - 2^0 */
+    felem_assign(ftmp2, ftmp3);
+
+    for (i = 0; i < 32; i++) {
+        felem_square(tmp, ftmp3);
+        felem_reduce(ftmp3, tmp); /* 2^64 - 2^32 */
+    }
+    felem_mul(tmp, ftmp3, ftmp2);
+    felem_reduce(ftmp3, tmp);   /* 2^64 - 2^0 */
+    felem_assign(ftmp2, ftmp3);
+
+    for (i = 0; i < 64; i++) {
+        felem_square(tmp, ftmp3);
+        felem_reduce(ftmp3, tmp); /* 2^128 - 2^64 */
+    }
+    felem_mul(tmp, ftmp3, ftmp2);
+    felem_reduce(ftmp3, tmp);   /* 2^128 - 2^0 */
+    felem_assign(ftmp2, ftmp3);
+
+    for (i = 0; i < 128; i++) {
+        felem_square(tmp, ftmp3);
+        felem_reduce(ftmp3, tmp); /* 2^256 - 2^128 */
+    }
+    felem_mul(tmp, ftmp3, ftmp2);
+    felem_reduce(ftmp3, tmp);   /* 2^256 - 2^0 */
+    felem_assign(ftmp2, ftmp3);
+
+    for (i = 0; i < 256; i++) {
+        felem_square(tmp, ftmp3);
+        felem_reduce(ftmp3, tmp); /* 2^512 - 2^256 */
+    }
+    felem_mul(tmp, ftmp3, ftmp2);
+    felem_reduce(ftmp3, tmp);   /* 2^512 - 2^0 */
+
+    for (i = 0; i < 9; i++) {
+        felem_square(tmp, ftmp3);
+        felem_reduce(ftmp3, tmp); /* 2^521 - 2^9 */
+    }
+    felem_mul(tmp, ftmp3, ftmp4);
+    felem_reduce(ftmp3, tmp);   /* 2^512 - 2^2 */
+    felem_mul(tmp, ftmp3, in);
+    felem_reduce(out, tmp);     /* 2^512 - 3 */
+}
+
+/* This is 2^521-1, expressed as an felem */
+static const felem kPrime = {
+    0x03ffffffffffffff, 0x03ffffffffffffff, 0x03ffffffffffffff,
+    0x03ffffffffffffff, 0x03ffffffffffffff, 0x03ffffffffffffff,
+    0x03ffffffffffffff, 0x03ffffffffffffff, 0x01ffffffffffffff
+};
+
+/*-
+ * felem_is_zero returns a limb with all bits set if |in| == 0 (mod p) and 0
+ * otherwise.
+ * On entry:
+ *   in[i] < 2^59 + 2^14
+ */
+static limb felem_is_zero(const felem in)
+{
+    felem ftmp;
+    limb is_zero, is_p;
+    felem_assign(ftmp, in);
+
+    ftmp[0] += ftmp[8] >> 57;
+    ftmp[8] &= bottom57bits;
+    /* ftmp[8] < 2^57 */
+    ftmp[1] += ftmp[0] >> 58;
+    ftmp[0] &= bottom58bits;
+    ftmp[2] += ftmp[1] >> 58;
+    ftmp[1] &= bottom58bits;
+    ftmp[3] += ftmp[2] >> 58;
+    ftmp[2] &= bottom58bits;
+    ftmp[4] += ftmp[3] >> 58;
+    ftmp[3] &= bottom58bits;
+    ftmp[5] += ftmp[4] >> 58;
+    ftmp[4] &= bottom58bits;
+    ftmp[6] += ftmp[5] >> 58;
+    ftmp[5] &= bottom58bits;
+    ftmp[7] += ftmp[6] >> 58;
+    ftmp[6] &= bottom58bits;
+    ftmp[8] += ftmp[7] >> 58;
+    ftmp[7] &= bottom58bits;
+    /* ftmp[8] < 2^57 + 4 */
+
+    /*
+     * The ninth limb of 2*(2^521-1) is 0x03ffffffffffffff, which is greater
+     * than our bound for ftmp[8]. Therefore we only have to check if the
+     * zero is zero or 2^521-1.
+     */
+
+    is_zero = 0;
+    is_zero |= ftmp[0];
+    is_zero |= ftmp[1];
+    is_zero |= ftmp[2];
+    is_zero |= ftmp[3];
+    is_zero |= ftmp[4];
+    is_zero |= ftmp[5];
+    is_zero |= ftmp[6];
+    is_zero |= ftmp[7];
+    is_zero |= ftmp[8];
+
+    is_zero--;
+    /*
+     * We know that ftmp[i] < 2^63, therefore the only way that the top bit
+     * can be set is if is_zero was 0 before the decrement.
+     */
+    is_zero = ((s64) is_zero) >> 63;
+
+    is_p = ftmp[0] ^ kPrime[0];
+    is_p |= ftmp[1] ^ kPrime[1];
+    is_p |= ftmp[2] ^ kPrime[2];
+    is_p |= ftmp[3] ^ kPrime[3];
+    is_p |= ftmp[4] ^ kPrime[4];
+    is_p |= ftmp[5] ^ kPrime[5];
+    is_p |= ftmp[6] ^ kPrime[6];
+    is_p |= ftmp[7] ^ kPrime[7];
+    is_p |= ftmp[8] ^ kPrime[8];
+
+    is_p--;
+    is_p = ((s64) is_p) >> 63;
+
+    is_zero |= is_p;
+    return is_zero;
+}
+
+static int felem_is_zero_int(const felem in)
+{
+    return (int)(felem_is_zero(in) & ((limb) 1));
+}
+
+/*-
+ * felem_contract converts |in| to its unique, minimal representation.
+ * On entry:
+ *   in[i] < 2^59 + 2^14
+ */
+static void felem_contract(felem out, const felem in)
+{
+    limb is_p, is_greater, sign;
+    static const limb two58 = ((limb) 1) << 58;
+
+    felem_assign(out, in);
+
+    out[0] += out[8] >> 57;
+    out[8] &= bottom57bits;
+    /* out[8] < 2^57 */
+    out[1] += out[0] >> 58;
+    out[0] &= bottom58bits;
+    out[2] += out[1] >> 58;
+    out[1] &= bottom58bits;
+    out[3] += out[2] >> 58;
+    out[2] &= bottom58bits;
+    out[4] += out[3] >> 58;
+    out[3] &= bottom58bits;
+    out[5] += out[4] >> 58;
+    out[4] &= bottom58bits;
+    out[6] += out[5] >> 58;
+    out[5] &= bottom58bits;
+    out[7] += out[6] >> 58;
+    out[6] &= bottom58bits;
+    out[8] += out[7] >> 58;
+    out[7] &= bottom58bits;
+    /* out[8] < 2^57 + 4 */
+
+    /*
+     * If the value is greater than 2^521-1 then we have to subtract 2^521-1
+     * out. See the comments in felem_is_zero regarding why we don't test for
+     * other multiples of the prime.
+     */
+
+    /*
+     * First, if |out| is equal to 2^521-1, we subtract it out to get zero.
+     */
+
+    is_p = out[0] ^ kPrime[0];
+    is_p |= out[1] ^ kPrime[1];
+    is_p |= out[2] ^ kPrime[2];
+    is_p |= out[3] ^ kPrime[3];
+    is_p |= out[4] ^ kPrime[4];
+    is_p |= out[5] ^ kPrime[5];
+    is_p |= out[6] ^ kPrime[6];
+    is_p |= out[7] ^ kPrime[7];
+    is_p |= out[8] ^ kPrime[8];
+
+    is_p--;
+    is_p &= is_p << 32;
+    is_p &= is_p << 16;
+    is_p &= is_p << 8;
+    is_p &= is_p << 4;
+    is_p &= is_p << 2;
+    is_p &= is_p << 1;
+    is_p = ((s64) is_p) >> 63;
+    is_p = ~is_p;
+
+    /* is_p is 0 iff |out| == 2^521-1 and all ones otherwise */
+
+    out[0] &= is_p;
+    out[1] &= is_p;
+    out[2] &= is_p;
+    out[3] &= is_p;
+    out[4] &= is_p;
+    out[5] &= is_p;
+    out[6] &= is_p;
+    out[7] &= is_p;
+    out[8] &= is_p;
+
+    /*
+     * In order to test that |out| >= 2^521-1 we need only test if out[8] >>
+     * 57 is greater than zero as (2^521-1) + x >= 2^522
+     */
+    is_greater = out[8] >> 57;
+    is_greater |= is_greater << 32;
+    is_greater |= is_greater << 16;
+    is_greater |= is_greater << 8;
+    is_greater |= is_greater << 4;
+    is_greater |= is_greater << 2;
+    is_greater |= is_greater << 1;
+    is_greater = ((s64) is_greater) >> 63;
+
+    out[0] -= kPrime[0] & is_greater;
+    out[1] -= kPrime[1] & is_greater;
+    out[2] -= kPrime[2] & is_greater;
+    out[3] -= kPrime[3] & is_greater;
+    out[4] -= kPrime[4] & is_greater;
+    out[5] -= kPrime[5] & is_greater;
+    out[6] -= kPrime[6] & is_greater;
+    out[7] -= kPrime[7] & is_greater;
+    out[8] -= kPrime[8] & is_greater;
+
+    /* Eliminate negative coefficients */
+    sign = -(out[0] >> 63);
+    out[0] += (two58 & sign);
+    out[1] -= (1 & sign);
+    sign = -(out[1] >> 63);
+    out[1] += (two58 & sign);
+    out[2] -= (1 & sign);
+    sign = -(out[2] >> 63);
+    out[2] += (two58 & sign);
+    out[3] -= (1 & sign);
+    sign = -(out[3] >> 63);
+    out[3] += (two58 & sign);
+    out[4] -= (1 & sign);
+    sign = -(out[4] >> 63);
+    out[4] += (two58 & sign);
+    out[5] -= (1 & sign);
+    sign = -(out[0] >> 63);
+    out[5] += (two58 & sign);
+    out[6] -= (1 & sign);
+    sign = -(out[6] >> 63);
+    out[6] += (two58 & sign);
+    out[7] -= (1 & sign);
+    sign = -(out[7] >> 63);
+    out[7] += (two58 & sign);
+    out[8] -= (1 & sign);
+    sign = -(out[5] >> 63);
+    out[5] += (two58 & sign);
+    out[6] -= (1 & sign);
+    sign = -(out[6] >> 63);
+    out[6] += (two58 & sign);
+    out[7] -= (1 & sign);
+    sign = -(out[7] >> 63);
+    out[7] += (two58 & sign);
+    out[8] -= (1 & sign);
+}
+
+/*-
+ * Group operations
+ * ----------------
+ *
+ * Building on top of the field operations we have the operations on the
+ * elliptic curve group itself. Points on the curve are represented in Jacobian
+ * coordinates */
+
+/*-
+ * point_double calculates 2*(x_in, y_in, z_in)
+ *
+ * The method is taken from:
+ *   http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html#doubling-dbl-2001-b
+ *
+ * Outputs can equal corresponding inputs, i.e., x_out == x_in is allowed.
+ * while x_out == y_in is not (maybe this works, but it's not tested). */
+static void
+point_double(felem x_out, felem y_out, felem z_out,
+             const felem x_in, const felem y_in, const felem z_in)
+{
+    largefelem tmp, tmp2;
+    felem delta, gamma, beta, alpha, ftmp, ftmp2;
+
+    felem_assign(ftmp, x_in);
+    felem_assign(ftmp2, x_in);
+
+    /* delta = z^2 */
+    felem_square(tmp, z_in);
+    felem_reduce(delta, tmp);   /* delta[i] < 2^59 + 2^14 */
+
+    /* gamma = y^2 */
+    felem_square(tmp, y_in);
+    felem_reduce(gamma, tmp);   /* gamma[i] < 2^59 + 2^14 */
+
+    /* beta = x*gamma */
+    felem_mul(tmp, x_in, gamma);
+    felem_reduce(beta, tmp);    /* beta[i] < 2^59 + 2^14 */
+
+    /* alpha = 3*(x-delta)*(x+delta) */
+    felem_diff64(ftmp, delta);
+    /* ftmp[i] < 2^61 */
+    felem_sum64(ftmp2, delta);
+    /* ftmp2[i] < 2^60 + 2^15 */
+    felem_scalar64(ftmp2, 3);
+    /* ftmp2[i] < 3*2^60 + 3*2^15 */
+    felem_mul(tmp, ftmp, ftmp2);
+    /*-
+     * tmp[i] < 17(3*2^121 + 3*2^76)
+     *        = 61*2^121 + 61*2^76
+     *        < 64*2^121 + 64*2^76
+     *        = 2^127 + 2^82
+     *        < 2^128
+     */
+    felem_reduce(alpha, tmp);
+
+    /* x' = alpha^2 - 8*beta */
+    felem_square(tmp, alpha);
+    /*
+     * tmp[i] < 17*2^120 < 2^125
+     */
+    felem_assign(ftmp, beta);
+    felem_scalar64(ftmp, 8);
+    /* ftmp[i] < 2^62 + 2^17 */
+    felem_diff_128_64(tmp, ftmp);
+    /* tmp[i] < 2^125 + 2^63 + 2^62 + 2^17 */
+    felem_reduce(x_out, tmp);
+
+    /* z' = (y + z)^2 - gamma - delta */
+    felem_sum64(delta, gamma);
+    /* delta[i] < 2^60 + 2^15 */
+    felem_assign(ftmp, y_in);
+    felem_sum64(ftmp, z_in);
+    /* ftmp[i] < 2^60 + 2^15 */
+    felem_square(tmp, ftmp);
+    /*
+     * tmp[i] < 17(2^122) < 2^127
+     */
+    felem_diff_128_64(tmp, delta);
+    /* tmp[i] < 2^127 + 2^63 */
+    felem_reduce(z_out, tmp);
+
+    /* y' = alpha*(4*beta - x') - 8*gamma^2 */
+    felem_scalar64(beta, 4);
+    /* beta[i] < 2^61 + 2^16 */
+    felem_diff64(beta, x_out);
+    /* beta[i] < 2^61 + 2^60 + 2^16 */
+    felem_mul(tmp, alpha, beta);
+    /*-
+     * tmp[i] < 17*((2^59 + 2^14)(2^61 + 2^60 + 2^16))
+     *        = 17*(2^120 + 2^75 + 2^119 + 2^74 + 2^75 + 2^30)
+     *        = 17*(2^120 + 2^119 + 2^76 + 2^74 + 2^30)
+     *        < 2^128
+     */
+    felem_square(tmp2, gamma);
+    /*-
+     * tmp2[i] < 17*(2^59 + 2^14)^2
+     *         = 17*(2^118 + 2^74 + 2^28)
+     */
+    felem_scalar128(tmp2, 8);
+    /*-
+     * tmp2[i] < 8*17*(2^118 + 2^74 + 2^28)
+     *         = 2^125 + 2^121 + 2^81 + 2^77 + 2^35 + 2^31
+     *         < 2^126
+     */
+    felem_diff128(tmp, tmp2);
+    /*-
+     * tmp[i] < 2^127 - 2^69 + 17(2^120 + 2^119 + 2^76 + 2^74 + 2^30)
+     *        = 2^127 + 2^124 + 2^122 + 2^120 + 2^118 + 2^80 + 2^78 + 2^76 +
+     *          2^74 + 2^69 + 2^34 + 2^30
+     *        < 2^128
+     */
+    felem_reduce(y_out, tmp);
+}
+
+/* copy_conditional copies in to out iff mask is all ones. */
+static void copy_conditional(felem out, const felem in, limb mask)
+{
+    unsigned i;
+    for (i = 0; i < NLIMBS; ++i) {
+        const limb tmp = mask & (in[i] ^ out[i]);
+        out[i] ^= tmp;
+    }
+}
+
+/*-
+ * point_add calculates (x1, y1, z1) + (x2, y2, z2)
+ *
+ * The method is taken from
+ *   http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html#addition-add-2007-bl,
+ * adapted for mixed addition (z2 = 1, or z2 = 0 for the point at infinity).
+ *
+ * This function includes a branch for checking whether the two input points
+ * are equal (while not equal to the point at infinity). This case never
+ * happens during single point multiplication, so there is no timing leak for
+ * ECDH or ECDSA signing. */
+static void point_add(felem x3, felem y3, felem z3,
+                      const felem x1, const felem y1, const felem z1,
+                      const int mixed, const felem x2, const felem y2,
+                      const felem z2)
+{
+    felem ftmp, ftmp2, ftmp3, ftmp4, ftmp5, ftmp6, x_out, y_out, z_out;
+    largefelem tmp, tmp2;
+    limb x_equal, y_equal, z1_is_zero, z2_is_zero;
+
+    z1_is_zero = felem_is_zero(z1);
+    z2_is_zero = felem_is_zero(z2);
+
+    /* ftmp = z1z1 = z1**2 */
+    felem_square(tmp, z1);
+    felem_reduce(ftmp, tmp);
+
+    if (!mixed) {
+        /* ftmp2 = z2z2 = z2**2 */
+        felem_square(tmp, z2);
+        felem_reduce(ftmp2, tmp);
+
+        /* u1 = ftmp3 = x1*z2z2 */
+        felem_mul(tmp, x1, ftmp2);
+        felem_reduce(ftmp3, tmp);
+
+        /* ftmp5 = z1 + z2 */
+        felem_assign(ftmp5, z1);
+        felem_sum64(ftmp5, z2);
+        /* ftmp5[i] < 2^61 */
+
+        /* ftmp5 = (z1 + z2)**2 - z1z1 - z2z2 = 2*z1z2 */
+        felem_square(tmp, ftmp5);
+        /* tmp[i] < 17*2^122 */
+        felem_diff_128_64(tmp, ftmp);
+        /* tmp[i] < 17*2^122 + 2^63 */
+        felem_diff_128_64(tmp, ftmp2);
+        /* tmp[i] < 17*2^122 + 2^64 */
+        felem_reduce(ftmp5, tmp);
+
+        /* ftmp2 = z2 * z2z2 */
+        felem_mul(tmp, ftmp2, z2);
+        felem_reduce(ftmp2, tmp);
+
+        /* s1 = ftmp6 = y1 * z2**3 */
+        felem_mul(tmp, y1, ftmp2);
+        felem_reduce(ftmp6, tmp);
+    } else {
+        /*
+         * We'll assume z2 = 1 (special case z2 = 0 is handled later)
+         */
+
+        /* u1 = ftmp3 = x1*z2z2 */
+        felem_assign(ftmp3, x1);
+
+        /* ftmp5 = 2*z1z2 */
+        felem_scalar(ftmp5, z1, 2);
+
+        /* s1 = ftmp6 = y1 * z2**3 */
+        felem_assign(ftmp6, y1);
+    }
+
+    /* u2 = x2*z1z1 */
+    felem_mul(tmp, x2, ftmp);
+    /* tmp[i] < 17*2^120 */
+
+    /* h = ftmp4 = u2 - u1 */
+    felem_diff_128_64(tmp, ftmp3);
+    /* tmp[i] < 17*2^120 + 2^63 */
+    felem_reduce(ftmp4, tmp);
+
+    x_equal = felem_is_zero(ftmp4);
+
+    /* z_out = ftmp5 * h */
+    felem_mul(tmp, ftmp5, ftmp4);
+    felem_reduce(z_out, tmp);
+
+    /* ftmp = z1 * z1z1 */
+    felem_mul(tmp, ftmp, z1);
+    felem_reduce(ftmp, tmp);
+
+    /* s2 = tmp = y2 * z1**3 */
+    felem_mul(tmp, y2, ftmp);
+    /* tmp[i] < 17*2^120 */
+
+    /* r = ftmp5 = (s2 - s1)*2 */
+    felem_diff_128_64(tmp, ftmp6);
+    /* tmp[i] < 17*2^120 + 2^63 */
+    felem_reduce(ftmp5, tmp);
+    y_equal = felem_is_zero(ftmp5);
+    felem_scalar64(ftmp5, 2);
+    /* ftmp5[i] < 2^61 */
+
+    if (x_equal && y_equal && !z1_is_zero && !z2_is_zero) {
+        point_double(x3, y3, z3, x1, y1, z1);
+        return;
+    }
+
+    /* I = ftmp = (2h)**2 */
+    felem_assign(ftmp, ftmp4);
+    felem_scalar64(ftmp, 2);
+    /* ftmp[i] < 2^61 */
+    felem_square(tmp, ftmp);
+    /* tmp[i] < 17*2^122 */
+    felem_reduce(ftmp, tmp);
+
+    /* J = ftmp2 = h * I */
+    felem_mul(tmp, ftmp4, ftmp);
+    felem_reduce(ftmp2, tmp);
+
+    /* V = ftmp4 = U1 * I */
+    felem_mul(tmp, ftmp3, ftmp);
+    felem_reduce(ftmp4, tmp);
+
+    /* x_out = r**2 - J - 2V */
+    felem_square(tmp, ftmp5);
+    /* tmp[i] < 17*2^122 */
+    felem_diff_128_64(tmp, ftmp2);
+    /* tmp[i] < 17*2^122 + 2^63 */
+    felem_assign(ftmp3, ftmp4);
+    felem_scalar64(ftmp4, 2);
+    /* ftmp4[i] < 2^61 */
+    felem_diff_128_64(tmp, ftmp4);
+    /* tmp[i] < 17*2^122 + 2^64 */
+    felem_reduce(x_out, tmp);
+
+    /* y_out = r(V-x_out) - 2 * s1 * J */
+    felem_diff64(ftmp3, x_out);
+    /*
+     * ftmp3[i] < 2^60 + 2^60 = 2^61
+     */
+    felem_mul(tmp, ftmp5, ftmp3);
+    /* tmp[i] < 17*2^122 */
+    felem_mul(tmp2, ftmp6, ftmp2);
+    /* tmp2[i] < 17*2^120 */
+    felem_scalar128(tmp2, 2);
+    /* tmp2[i] < 17*2^121 */
+    felem_diff128(tmp, tmp2);
+        /*-
+         * tmp[i] < 2^127 - 2^69 + 17*2^122
+         *        = 2^126 - 2^122 - 2^6 - 2^2 - 1
+         *        < 2^127
+         */
+    felem_reduce(y_out, tmp);
+
+    copy_conditional(x_out, x2, z1_is_zero);
+    copy_conditional(x_out, x1, z2_is_zero);
+    copy_conditional(y_out, y2, z1_is_zero);
+    copy_conditional(y_out, y1, z2_is_zero);
+    copy_conditional(z_out, z2, z1_is_zero);
+    copy_conditional(z_out, z1, z2_is_zero);
+    felem_assign(x3, x_out);
+    felem_assign(y3, y_out);
+    felem_assign(z3, z_out);
+}
+
+/*-
+ * Base point pre computation
+ * --------------------------
+ *
+ * Two different sorts of precomputed tables are used in the following code.
+ * Each contain various points on the curve, where each point is three field
+ * elements (x, y, z).
+ *
+ * For the base point table, z is usually 1 (0 for the point at infinity).
+ * This table has 16 elements:
+ * index | bits    | point
+ * ------+---------+------------------------------
+ *     0 | 0 0 0 0 | 0G
+ *     1 | 0 0 0 1 | 1G
+ *     2 | 0 0 1 0 | 2^130G
+ *     3 | 0 0 1 1 | (2^130 + 1)G
+ *     4 | 0 1 0 0 | 2^260G
+ *     5 | 0 1 0 1 | (2^260 + 1)G
+ *     6 | 0 1 1 0 | (2^260 + 2^130)G
+ *     7 | 0 1 1 1 | (2^260 + 2^130 + 1)G
+ *     8 | 1 0 0 0 | 2^390G
+ *     9 | 1 0 0 1 | (2^390 + 1)G
+ *    10 | 1 0 1 0 | (2^390 + 2^130)G
+ *    11 | 1 0 1 1 | (2^390 + 2^130 + 1)G
+ *    12 | 1 1 0 0 | (2^390 + 2^260)G
+ *    13 | 1 1 0 1 | (2^390 + 2^260 + 1)G
+ *    14 | 1 1 1 0 | (2^390 + 2^260 + 2^130)G
+ *    15 | 1 1 1 1 | (2^390 + 2^260 + 2^130 + 1)G
+ *
+ * The reason for this is so that we can clock bits into four different
+ * locations when doing simple scalar multiplies against the base point.
+ *
+ * Tables for other points have table[i] = iG for i in 0 .. 16. */
+
+/* gmul is the table of precomputed base points */
+static const felem gmul[16][3] = {
+{{0, 0, 0, 0, 0, 0, 0, 0, 0},
+ {0, 0, 0, 0, 0, 0, 0, 0, 0},
+ {0, 0, 0, 0, 0, 0, 0, 0, 0}},
+{{0x017e7e31c2e5bd66, 0x022cf0615a90a6fe, 0x00127a2ffa8de334,
+  0x01dfbf9d64a3f877, 0x006b4d3dbaa14b5e, 0x014fed487e0a2bd8,
+  0x015b4429c6481390, 0x03a73678fb2d988e, 0x00c6858e06b70404},
+ {0x00be94769fd16650, 0x031c21a89cb09022, 0x039013fad0761353,
+  0x02657bd099031542, 0x03273e662c97ee72, 0x01e6d11a05ebef45,
+  0x03d1bd998f544495, 0x03001172297ed0b1, 0x011839296a789a3b},
+ {1, 0, 0, 0, 0, 0, 0, 0, 0}},
+{{0x0373faacbc875bae, 0x00f325023721c671, 0x00f666fd3dbde5ad,
+  0x01a6932363f88ea7, 0x01fc6d9e13f9c47b, 0x03bcbffc2bbf734e,
+  0x013ee3c3647f3a92, 0x029409fefe75d07d, 0x00ef9199963d85e5},
+ {0x011173743ad5b178, 0x02499c7c21bf7d46, 0x035beaeabb8b1a58,
+  0x00f989c4752ea0a3, 0x0101e1de48a9c1a3, 0x01a20076be28ba6c,
+  0x02f8052e5eb2de95, 0x01bfe8f82dea117c, 0x0160074d3c36ddb7},
+ {1, 0, 0, 0, 0, 0, 0, 0, 0}},
+{{0x012f3fc373393b3b, 0x03d3d6172f1419fa, 0x02adc943c0b86873,
+  0x00d475584177952b, 0x012a4d1673750ee2, 0x00512517a0f13b0c,
+  0x02b184671a7b1734, 0x0315b84236f1a50a, 0x00a4afc472edbdb9},
+ {0x00152a7077f385c4, 0x03044007d8d1c2ee, 0x0065829d61d52b52,
+  0x00494ff6b6631d0d, 0x00a11d94d5f06bcf, 0x02d2f89474d9282e,
+  0x0241c5727c06eeb9, 0x0386928710fbdb9d, 0x01f883f727b0dfbe},
+ {1, 0, 0, 0, 0, 0, 0, 0, 0}},
+{{0x019b0c3c9185544d, 0x006243a37c9d97db, 0x02ee3cbe030a2ad2,
+  0x00cfdd946bb51e0d, 0x0271c00932606b91, 0x03f817d1ec68c561,
+  0x03f37009806a369c, 0x03c1f30baf184fd5, 0x01091022d6d2f065},
+ {0x0292c583514c45ed, 0x0316fca51f9a286c, 0x00300af507c1489a,
+  0x0295f69008298cf1, 0x02c0ed8274943d7b, 0x016509b9b47a431e,
+  0x02bc9de9634868ce, 0x005b34929bffcb09, 0x000c1a0121681524},
+ {1, 0, 0, 0, 0, 0, 0, 0, 0}},
+{{0x0286abc0292fb9f2, 0x02665eee9805b3f7, 0x01ed7455f17f26d6,
+  0x0346355b83175d13, 0x006284944cd0a097, 0x0191895bcdec5e51,
+  0x02e288370afda7d9, 0x03b22312bfefa67a, 0x01d104d3fc0613fe},
+ {0x0092421a12f7e47f, 0x0077a83fa373c501, 0x03bd25c5f696bd0d,
+  0x035c41e4d5459761, 0x01ca0d1742b24f53, 0x00aaab27863a509c,
+  0x018b6de47df73917, 0x025c0b771705cd01, 0x01fd51d566d760a7},
+ {1, 0, 0, 0, 0, 0, 0, 0, 0}},
+{{0x01dd92ff6b0d1dbd, 0x039c5e2e8f8afa69, 0x0261ed13242c3b27,
+  0x0382c6e67026e6a0, 0x01d60b10be2089f9, 0x03c15f3dce86723f,
+  0x03c764a32d2a062d, 0x017307eac0fad056, 0x018207c0b96c5256},
+ {0x0196a16d60e13154, 0x03e6ce74c0267030, 0x00ddbf2b4e52a5aa,
+  0x012738241bbf31c8, 0x00ebe8dc04685a28, 0x024c2ad6d380d4a2,
+  0x035ee062a6e62d0e, 0x0029ed74af7d3a0f, 0x00eef32aec142ebd},
+ {1, 0, 0, 0, 0, 0, 0, 0, 0}},
+{{0x00c31ec398993b39, 0x03a9f45bcda68253, 0x00ac733c24c70890,
+  0x00872b111401ff01, 0x01d178c23195eafb, 0x03bca2c816b87f74,
+  0x0261a9af46fbad7a, 0x0324b2a8dd3d28f9, 0x00918121d8f24e23},
+ {0x032bc8c1ca983cd7, 0x00d869dfb08fc8c6, 0x01693cb61fce1516,
+  0x012a5ea68f4e88a8, 0x010869cab88d7ae3, 0x009081ad277ceee1,
+  0x033a77166d064cdc, 0x03955235a1fb3a95, 0x01251a4a9b25b65e},
+ {1, 0, 0, 0, 0, 0, 0, 0, 0}},
+{{0x00148a3a1b27f40b, 0x0123186df1b31fdc, 0x00026e7beaad34ce,
+  0x01db446ac1d3dbba, 0x0299c1a33437eaec, 0x024540610183cbb7,
+  0x0173bb0e9ce92e46, 0x02b937e43921214b, 0x01ab0436a9bf01b5},
+ {0x0383381640d46948, 0x008dacbf0e7f330f, 0x03602122bcc3f318,
+  0x01ee596b200620d6, 0x03bd0585fda430b3, 0x014aed77fd123a83,
+  0x005ace749e52f742, 0x0390fe041da2b842, 0x0189a8ceb3299242},
+ {1, 0, 0, 0, 0, 0, 0, 0, 0}},
+{{0x012a19d6b3282473, 0x00c0915918b423ce, 0x023a954eb94405ae,
+  0x00529f692be26158, 0x0289fa1b6fa4b2aa, 0x0198ae4ceea346ef,
+  0x0047d8cdfbdedd49, 0x00cc8c8953f0f6b8, 0x001424abbff49203},
+ {0x0256732a1115a03a, 0x0351bc38665c6733, 0x03f7b950fb4a6447,
+  0x000afffa94c22155, 0x025763d0a4dab540, 0x000511e92d4fc283,
+  0x030a7e9eda0ee96c, 0x004c3cd93a28bf0a, 0x017edb3a8719217f},
+ {1, 0, 0, 0, 0, 0, 0, 0, 0}},
+{{0x011de5675a88e673, 0x031d7d0f5e567fbe, 0x0016b2062c970ae5,
+  0x03f4a2be49d90aa7, 0x03cef0bd13822866, 0x03f0923dcf774a6c,
+  0x0284bebc4f322f72, 0x016ab2645302bb2c, 0x01793f95dace0e2a},
+ {0x010646e13527a28f, 0x01ca1babd59dc5e7, 0x01afedfd9a5595df,
+  0x01f15785212ea6b1, 0x0324e5d64f6ae3f4, 0x02d680f526d00645,
+  0x0127920fadf627a7, 0x03b383f75df4f684, 0x0089e0057e783b0a},
+ {1, 0, 0, 0, 0, 0, 0, 0, 0}},
+{{0x00f334b9eb3c26c6, 0x0298fdaa98568dce, 0x01c2d24843a82292,
+  0x020bcb24fa1b0711, 0x02cbdb3d2b1875e6, 0x0014907598f89422,
+  0x03abe3aa43b26664, 0x02cbf47f720bc168, 0x0133b5e73014b79b},
+ {0x034aab5dab05779d, 0x00cdc5d71fee9abb, 0x0399f16bd4bd9d30,
+  0x03582fa592d82647, 0x02be1cdfb775b0e9, 0x0034f7cea32e94cb,
+  0x0335a7f08f56f286, 0x03b707e9565d1c8b, 0x0015c946ea5b614f},
+ {1, 0, 0, 0, 0, 0, 0, 0, 0}},
+{{0x024676f6cff72255, 0x00d14625cac96378, 0x00532b6008bc3767,
+  0x01fc16721b985322, 0x023355ea1b091668, 0x029de7afdc0317c3,
+  0x02fc8a7ca2da037c, 0x02de1217d74a6f30, 0x013f7173175b73bf},
+ {0x0344913f441490b5, 0x0200f9e272b61eca, 0x0258a246b1dd55d2,
+  0x03753db9ea496f36, 0x025e02937a09c5ef, 0x030cbd3d14012692,
+  0x01793a67e70dc72a, 0x03ec1d37048a662e, 0x006550f700c32a8d},
+ {1, 0, 0, 0, 0, 0, 0, 0, 0}},
+{{0x00d3f48a347eba27, 0x008e636649b61bd8, 0x00d3b93716778fb3,
+  0x004d1915757bd209, 0x019d5311a3da44e0, 0x016d1afcbbe6aade,
+  0x0241bf5f73265616, 0x0384672e5d50d39b, 0x005009fee522b684},
+ {0x029b4fab064435fe, 0x018868ee095bbb07, 0x01ea3d6936cc92b8,
+  0x000608b00f78a2f3, 0x02db911073d1c20f, 0x018205938470100a,
+  0x01f1e4964cbe6ff2, 0x021a19a29eed4663, 0x01414485f42afa81},
+ {1, 0, 0, 0, 0, 0, 0, 0, 0}},
+{{0x01612b3a17f63e34, 0x03813992885428e6, 0x022b3c215b5a9608,
+  0x029b4057e19f2fcb, 0x0384059a587af7e6, 0x02d6400ace6fe610,
+  0x029354d896e8e331, 0x00c047ee6dfba65e, 0x0037720542e9d49d},
+ {0x02ce9eed7c5e9278, 0x0374ed703e79643b, 0x01316c54c4072006,
+  0x005aaa09054b2ee8, 0x002824000c840d57, 0x03d4eba24771ed86,
+  0x0189c50aabc3bdae, 0x0338c01541e15510, 0x00466d56e38eed42},
+ {1, 0, 0, 0, 0, 0, 0, 0, 0}},
+{{0x007efd8330ad8bd6, 0x02465ed48047710b, 0x0034c6606b215e0c,
+  0x016ae30c53cbf839, 0x01fa17bd37161216, 0x018ead4e61ce8ab9,
+  0x005482ed5f5dee46, 0x037543755bba1d7f, 0x005e5ac7e70a9d0f},
+ {0x0117e1bb2fdcb2a2, 0x03deea36249f40c4, 0x028d09b4a6246cb7,
+  0x03524b8855bcf756, 0x023d7d109d5ceb58, 0x0178e43e3223ef9c,
+  0x0154536a0c6e966a, 0x037964d1286ee9fe, 0x0199bcd90e125055},
+ {1, 0, 0, 0, 0, 0, 0, 0, 0}}
+};
+
+/*
+ * select_point selects the |idx|th point from a precomputation table and
+ * copies it to out.
+ */
+ /* pre_comp below is of the size provided in |size| */
+static void select_point(const limb idx, unsigned int size,
+                         const felem pre_comp[][3], felem out[3])
+{
+    unsigned i, j;
+    limb *outlimbs = &out[0][0];
+
+    memset(out, 0, sizeof(*out) * 3);
+
+    for (i = 0; i < size; i++) {
+        const limb *inlimbs = &pre_comp[i][0][0];
+        limb mask = i ^ idx;
+        mask |= mask >> 4;
+        mask |= mask >> 2;
+        mask |= mask >> 1;
+        mask &= 1;
+        mask--;
+        for (j = 0; j < NLIMBS * 3; j++)
+            outlimbs[j] |= inlimbs[j] & mask;
+    }
+}
+
+/* get_bit returns the |i|th bit in |in| */
+static char get_bit(const felem_bytearray in, int i)
+{
+    if (i < 0)
+        return 0;
+    return (in[i >> 3] >> (i & 7)) & 1;
+}
+
+/*
+ * Interleaved point multiplication using precomputed point multiples: The
+ * small point multiples 0*P, 1*P, ..., 16*P are in pre_comp[], the scalars
+ * in scalars[]. If g_scalar is non-NULL, we also add this multiple of the
+ * generator, using certain (large) precomputed multiples in g_pre_comp.
+ * Output point (X, Y, Z) is stored in x_out, y_out, z_out
+ */
+static void batch_mul(felem x_out, felem y_out, felem z_out,
+                      const felem_bytearray scalars[],
+                      const unsigned num_points, const u8 *g_scalar,
+                      const int mixed, const felem pre_comp[][17][3],
+                      const felem g_pre_comp[16][3])
+{
+    int i, skip;
+    unsigned num, gen_mul = (g_scalar != NULL);
+    felem nq[3], tmp[4];
+    limb bits;
+    u8 sign, digit;
+
+    /* set nq to the point at infinity */
+    memset(nq, 0, sizeof(nq));
+
+    /*
+     * Loop over all scalars msb-to-lsb, interleaving additions of multiples
+     * of the generator (last quarter of rounds) and additions of other
+     * points multiples (every 5th round).
+     */
+    skip = 1;                   /* save two point operations in the first
+                                 * round */
+    for (i = (num_points ? 520 : 130); i >= 0; --i) {
+        /* double */
+        if (!skip)
+            point_double(nq[0], nq[1], nq[2], nq[0], nq[1], nq[2]);
+
+        /* add multiples of the generator */
+        if (gen_mul && (i <= 130)) {
+            bits = get_bit(g_scalar, i + 390) << 3;
+            if (i < 130) {
+                bits |= get_bit(g_scalar, i + 260) << 2;
+                bits |= get_bit(g_scalar, i + 130) << 1;
+                bits |= get_bit(g_scalar, i);
+            }
+            /* select the point to add, in constant time */
+            select_point(bits, 16, g_pre_comp, tmp);
+            if (!skip) {
+                /* The 1 argument below is for "mixed" */
+                point_add(nq[0], nq[1], nq[2],
+                          nq[0], nq[1], nq[2], 1, tmp[0], tmp[1], tmp[2]);
+            } else {
+                memcpy(nq, tmp, 3 * sizeof(felem));
+                skip = 0;
+            }
+        }
+
+        /* do other additions every 5 doublings */
+        if (num_points && (i % 5 == 0)) {
+            /* loop over all scalars */
+            for (num = 0; num < num_points; ++num) {
+                bits = get_bit(scalars[num], i + 4) << 5;
+                bits |= get_bit(scalars[num], i + 3) << 4;
+                bits |= get_bit(scalars[num], i + 2) << 3;
+                bits |= get_bit(scalars[num], i + 1) << 2;
+                bits |= get_bit(scalars[num], i) << 1;
+                bits |= get_bit(scalars[num], i - 1);
+                ec_GFp_nistp_recode_scalar_bits(&sign, &digit, bits);
+
+                /*
+                 * select the point to add or subtract, in constant time
+                 */
+                select_point(digit, 17, pre_comp[num], tmp);
+                felem_neg(tmp[3], tmp[1]); /* (X, -Y, Z) is the negative
+                                            * point */
+                copy_conditional(tmp[1], tmp[3], (-(limb) sign));
+
+                if (!skip) {
+                    point_add(nq[0], nq[1], nq[2],
+                              nq[0], nq[1], nq[2],
+                              mixed, tmp[0], tmp[1], tmp[2]);
+                } else {
+                    memcpy(nq, tmp, 3 * sizeof(felem));
+                    skip = 0;
+                }
+            }
+        }
+    }
+    felem_assign(x_out, nq[0]);
+    felem_assign(y_out, nq[1]);
+    felem_assign(z_out, nq[2]);
+}
+
+/* Precomputation for the group generator. */
+struct nistp521_pre_comp_st {
+    felem g_pre_comp[16][3];
+    int references;
+    CRYPTO_RWLOCK *lock;
+};
+
+const EC_METHOD *EC_GFp_nistp521_method(void)
+{
+    static const EC_METHOD ret = {
+        EC_FLAGS_DEFAULT_OCT,
+        NID_X9_62_prime_field,
+        ec_GFp_nistp521_group_init,
+        ec_GFp_simple_group_finish,
+        ec_GFp_simple_group_clear_finish,
+        ec_GFp_nist_group_copy,
+        ec_GFp_nistp521_group_set_curve,
+        ec_GFp_simple_group_get_curve,
+        ec_GFp_simple_group_get_degree,
+        ec_group_simple_order_bits,
+        ec_GFp_simple_group_check_discriminant,
+        ec_GFp_simple_point_init,
+        ec_GFp_simple_point_finish,
+        ec_GFp_simple_point_clear_finish,
+        ec_GFp_simple_point_copy,
+        ec_GFp_simple_point_set_to_infinity,
+        ec_GFp_simple_set_Jprojective_coordinates_GFp,
+        ec_GFp_simple_get_Jprojective_coordinates_GFp,
+        ec_GFp_simple_point_set_affine_coordinates,
+        ec_GFp_nistp521_point_get_affine_coordinates,
+        0 /* point_set_compressed_coordinates */ ,
+        0 /* point2oct */ ,
+        0 /* oct2point */ ,
+        ec_GFp_simple_add,
+        ec_GFp_simple_dbl,
+        ec_GFp_simple_invert,
+        ec_GFp_simple_is_at_infinity,
+        ec_GFp_simple_is_on_curve,
+        ec_GFp_simple_cmp,
+        ec_GFp_simple_make_affine,
+        ec_GFp_simple_points_make_affine,
+        ec_GFp_nistp521_points_mul,
+        ec_GFp_nistp521_precompute_mult,
+        ec_GFp_nistp521_have_precompute_mult,
+        ec_GFp_nist_field_mul,
+        ec_GFp_nist_field_sqr,
+        0 /* field_div */ ,
+        0 /* field_encode */ ,
+        0 /* field_decode */ ,
+        0,                      /* field_set_to_one */
+        ec_key_simple_priv2oct,
+        ec_key_simple_oct2priv,
+        0, /* set private */
+        ec_key_simple_generate_key,
+        ec_key_simple_check_key,
+        ec_key_simple_generate_public_key,
+        0, /* keycopy */
+        0, /* keyfinish */
+        ecdh_simple_compute_key
+    };
+
+    return &ret;
+}
+
+/******************************************************************************/
+/*
+ * FUNCTIONS TO MANAGE PRECOMPUTATION
+ */
+
+static NISTP521_PRE_COMP *nistp521_pre_comp_new()
+{
+    NISTP521_PRE_COMP *ret = OPENSSL_zalloc(sizeof(*ret));
+
+    if (ret == NULL) {
+        ECerr(EC_F_NISTP521_PRE_COMP_NEW, ERR_R_MALLOC_FAILURE);
+        return ret;
+    }
+
+    ret->references = 1;
+
+    ret->lock = CRYPTO_THREAD_lock_new();
+    if (ret->lock == NULL) {
+        ECerr(EC_F_NISTP521_PRE_COMP_NEW, ERR_R_MALLOC_FAILURE);
+        OPENSSL_free(ret);
+        return NULL;
+    }
+    return ret;
+}
+
+NISTP521_PRE_COMP *EC_nistp521_pre_comp_dup(NISTP521_PRE_COMP *p)
+{
+    int i;
+    if (p != NULL)
+        CRYPTO_atomic_add(&p->references, 1, &i, p->lock);
+    return p;
+}
+
+void EC_nistp521_pre_comp_free(NISTP521_PRE_COMP *p)
+{
+    int i;
+
+    if (p == NULL)
+        return;
+
+    CRYPTO_atomic_add(&p->references, -1, &i, p->lock);
+    REF_PRINT_COUNT("EC_nistp521", x);
+    if (i > 0)
+        return;
+    REF_ASSERT_ISNT(i < 0);
+
+    CRYPTO_THREAD_lock_free(p->lock);
+    OPENSSL_free(p);
+}
+
+/******************************************************************************/
+/*
+ * OPENSSL EC_METHOD FUNCTIONS
+ */
+
+int ec_GFp_nistp521_group_init(EC_GROUP *group)
+{
+    int ret;
+    ret = ec_GFp_simple_group_init(group);
+    group->a_is_minus3 = 1;
+    return ret;
+}
+
+int ec_GFp_nistp521_group_set_curve(EC_GROUP *group, const BIGNUM *p,
+                                    const BIGNUM *a, const BIGNUM *b,
+                                    BN_CTX *ctx)
+{
+    int ret = 0;
+    BN_CTX *new_ctx = NULL;
+    BIGNUM *curve_p, *curve_a, *curve_b;
+
+    if (ctx == NULL)
+        if ((ctx = new_ctx = BN_CTX_new()) == NULL)
+            return 0;
+    BN_CTX_start(ctx);
+    if (((curve_p = BN_CTX_get(ctx)) == NULL) ||
+        ((curve_a = BN_CTX_get(ctx)) == NULL) ||
+        ((curve_b = BN_CTX_get(ctx)) == NULL))
+        goto err;
+    BN_bin2bn(nistp521_curve_params[0], sizeof(felem_bytearray), curve_p);
+    BN_bin2bn(nistp521_curve_params[1], sizeof(felem_bytearray), curve_a);
+    BN_bin2bn(nistp521_curve_params[2], sizeof(felem_bytearray), curve_b);
+    if ((BN_cmp(curve_p, p)) || (BN_cmp(curve_a, a)) || (BN_cmp(curve_b, b))) {
+        ECerr(EC_F_EC_GFP_NISTP521_GROUP_SET_CURVE,
+              EC_R_WRONG_CURVE_PARAMETERS);
+        goto err;
+    }
+    group->field_mod_func = BN_nist_mod_521;
+    ret = ec_GFp_simple_group_set_curve(group, p, a, b, ctx);
+ err:
+    BN_CTX_end(ctx);
+    BN_CTX_free(new_ctx);
+    return ret;
+}
+
+/*
+ * Takes the Jacobian coordinates (X, Y, Z) of a point and returns (X', Y') =
+ * (X/Z^2, Y/Z^3)
+ */
+int ec_GFp_nistp521_point_get_affine_coordinates(const EC_GROUP *group,
+                                                 const EC_POINT *point,
+                                                 BIGNUM *x, BIGNUM *y,
+                                                 BN_CTX *ctx)
+{
+    felem z1, z2, x_in, y_in, x_out, y_out;
+    largefelem tmp;
+
+    if (EC_POINT_is_at_infinity(group, point)) {
+        ECerr(EC_F_EC_GFP_NISTP521_POINT_GET_AFFINE_COORDINATES,
+              EC_R_POINT_AT_INFINITY);
+        return 0;
+    }
+    if ((!BN_to_felem(x_in, point->X)) || (!BN_to_felem(y_in, point->Y)) ||
+        (!BN_to_felem(z1, point->Z)))
+        return 0;
+    felem_inv(z2, z1);
+    felem_square(tmp, z2);
+    felem_reduce(z1, tmp);
+    felem_mul(tmp, x_in, z1);
+    felem_reduce(x_in, tmp);
+    felem_contract(x_out, x_in);
+    if (x != NULL) {
+        if (!felem_to_BN(x, x_out)) {
+            ECerr(EC_F_EC_GFP_NISTP521_POINT_GET_AFFINE_COORDINATES,
+                  ERR_R_BN_LIB);
+            return 0;
+        }
+    }
+    felem_mul(tmp, z1, z2);
+    felem_reduce(z1, tmp);
+    felem_mul(tmp, y_in, z1);
+    felem_reduce(y_in, tmp);
+    felem_contract(y_out, y_in);
+    if (y != NULL) {
+        if (!felem_to_BN(y, y_out)) {
+            ECerr(EC_F_EC_GFP_NISTP521_POINT_GET_AFFINE_COORDINATES,
+                  ERR_R_BN_LIB);
+            return 0;
+        }
+    }
+    return 1;
+}
+
+/* points below is of size |num|, and tmp_felems is of size |num+1/ */
+static void make_points_affine(size_t num, felem points[][3],
+                               felem tmp_felems[])
+{
+    /*
+     * Runs in constant time, unless an input is the point at infinity (which
+     * normally shouldn't happen).
+     */
+    ec_GFp_nistp_points_make_affine_internal(num,
+                                             points,
+                                             sizeof(felem),
+                                             tmp_felems,
+                                             (void (*)(void *))felem_one,
+                                             (int (*)(const void *))
+                                             felem_is_zero_int,
+                                             (void (*)(void *, const void *))
+                                             felem_assign,
+                                             (void (*)(void *, const void *))
+                                             felem_square_reduce, (void (*)
+                                                                   (void *,
+                                                                    const void
+                                                                    *,
+                                                                    const void
+                                                                    *))
+                                             felem_mul_reduce,
+                                             (void (*)(void *, const void *))
+                                             felem_inv,
+                                             (void (*)(void *, const void *))
+                                             felem_contract);
+}
+
+/*
+ * Computes scalar*generator + \sum scalars[i]*points[i], ignoring NULL
+ * values Result is stored in r (r can equal one of the inputs).
+ */
+int ec_GFp_nistp521_points_mul(const EC_GROUP *group, EC_POINT *r,
+                               const BIGNUM *scalar, size_t num,
+                               const EC_POINT *points[],
+                               const BIGNUM *scalars[], BN_CTX *ctx)
+{
+    int ret = 0;
+    int j;
+    int mixed = 0;
+    BN_CTX *new_ctx = NULL;
+    BIGNUM *x, *y, *z, *tmp_scalar;
+    felem_bytearray g_secret;
+    felem_bytearray *secrets = NULL;
+    felem (*pre_comp)[17][3] = NULL;
+    felem *tmp_felems = NULL;
+    felem_bytearray tmp;
+    unsigned i, num_bytes;
+    int have_pre_comp = 0;
+    size_t num_points = num;
+    felem x_in, y_in, z_in, x_out, y_out, z_out;
+    NISTP521_PRE_COMP *pre = NULL;
+    felem(*g_pre_comp)[3] = NULL;
+    EC_POINT *generator = NULL;
+    const EC_POINT *p = NULL;
+    const BIGNUM *p_scalar = NULL;
+
+    if (ctx == NULL)
+        if ((ctx = new_ctx = BN_CTX_new()) == NULL)
+            return 0;
+    BN_CTX_start(ctx);
+    if (((x = BN_CTX_get(ctx)) == NULL) ||
+        ((y = BN_CTX_get(ctx)) == NULL) ||
+        ((z = BN_CTX_get(ctx)) == NULL) ||
+        ((tmp_scalar = BN_CTX_get(ctx)) == NULL))
+        goto err;
+
+    if (scalar != NULL) {
+        pre = group->pre_comp.nistp521;
+        if (pre)
+            /* we have precomputation, try to use it */
+            g_pre_comp = &pre->g_pre_comp[0];
+        else
+            /* try to use the standard precomputation */
+            g_pre_comp = (felem(*)[3]) gmul;
+        generator = EC_POINT_new(group);
+        if (generator == NULL)
+            goto err;
+        /* get the generator from precomputation */
+        if (!felem_to_BN(x, g_pre_comp[1][0]) ||
+            !felem_to_BN(y, g_pre_comp[1][1]) ||
+            !felem_to_BN(z, g_pre_comp[1][2])) {
+            ECerr(EC_F_EC_GFP_NISTP521_POINTS_MUL, ERR_R_BN_LIB);
+            goto err;
+        }
+        if (!EC_POINT_set_Jprojective_coordinates_GFp(group,
+                                                      generator, x, y, z,
+                                                      ctx))
+            goto err;
+        if (0 == EC_POINT_cmp(group, generator, group->generator, ctx))
+            /* precomputation matches generator */
+            have_pre_comp = 1;
+        else
+            /*
+             * we don't have valid precomputation: treat the generator as a
+             * random point
+             */
+            num_points++;
+    }
+
+    if (num_points > 0) {
+        if (num_points >= 2) {
+            /*
+             * unless we precompute multiples for just one point, converting
+             * those into affine form is time well spent
+             */
+            mixed = 1;
+        }
+        secrets = OPENSSL_zalloc(sizeof(*secrets) * num_points);
+        pre_comp = OPENSSL_zalloc(sizeof(*pre_comp) * num_points);
+        if (mixed)
+            tmp_felems =
+                OPENSSL_malloc(sizeof(*tmp_felems) * (num_points * 17 + 1));
+        if ((secrets == NULL) || (pre_comp == NULL)
+            || (mixed && (tmp_felems == NULL))) {
+            ECerr(EC_F_EC_GFP_NISTP521_POINTS_MUL, ERR_R_MALLOC_FAILURE);
+            goto err;
+        }
+
+        /*
+         * we treat NULL scalars as 0, and NULL points as points at infinity,
+         * i.e., they contribute nothing to the linear combination
+         */
+        for (i = 0; i < num_points; ++i) {
+            if (i == num)
+                /*
+                 * we didn't have a valid precomputation, so we pick the
+                 * generator
+                 */
+            {
+                p = EC_GROUP_get0_generator(group);
+                p_scalar = scalar;
+            } else
+                /* the i^th point */
+            {
+                p = points[i];
+                p_scalar = scalars[i];
+            }
+            if ((p_scalar != NULL) && (p != NULL)) {
+                /* reduce scalar to 0 <= scalar < 2^521 */
+                if ((BN_num_bits(p_scalar) > 521)
+                    || (BN_is_negative(p_scalar))) {
+                    /*
+                     * this is an unusual input, and we don't guarantee
+                     * constant-timeness
+                     */
+                    if (!BN_nnmod(tmp_scalar, p_scalar, group->order, ctx)) {
+                        ECerr(EC_F_EC_GFP_NISTP521_POINTS_MUL, ERR_R_BN_LIB);
+                        goto err;
+                    }
+                    num_bytes = BN_bn2bin(tmp_scalar, tmp);
+                } else
+                    num_bytes = BN_bn2bin(p_scalar, tmp);
+                flip_endian(secrets[i], tmp, num_bytes);
+                /* precompute multiples */
+                if ((!BN_to_felem(x_out, p->X)) ||
+                    (!BN_to_felem(y_out, p->Y)) ||
+                    (!BN_to_felem(z_out, p->Z)))
+                    goto err;
+                memcpy(pre_comp[i][1][0], x_out, sizeof(felem));
+                memcpy(pre_comp[i][1][1], y_out, sizeof(felem));
+                memcpy(pre_comp[i][1][2], z_out, sizeof(felem));
+                for (j = 2; j <= 16; ++j) {
+                    if (j & 1) {
+                        point_add(pre_comp[i][j][0], pre_comp[i][j][1],
+                                  pre_comp[i][j][2], pre_comp[i][1][0],
+                                  pre_comp[i][1][1], pre_comp[i][1][2], 0,
+                                  pre_comp[i][j - 1][0],
+                                  pre_comp[i][j - 1][1],
+                                  pre_comp[i][j - 1][2]);
+                    } else {
+                        point_double(pre_comp[i][j][0], pre_comp[i][j][1],
+                                     pre_comp[i][j][2], pre_comp[i][j / 2][0],
+                                     pre_comp[i][j / 2][1],
+                                     pre_comp[i][j / 2][2]);
+                    }
+                }
+            }
+        }
+        if (mixed)
+            make_points_affine(num_points * 17, pre_comp[0], tmp_felems);
+    }
+
+    /* the scalar for the generator */
+    if ((scalar != NULL) && (have_pre_comp)) {
+        memset(g_secret, 0, sizeof(g_secret));
+        /* reduce scalar to 0 <= scalar < 2^521 */
+        if ((BN_num_bits(scalar) > 521) || (BN_is_negative(scalar))) {
+            /*
+             * this is an unusual input, and we don't guarantee
+             * constant-timeness
+             */
+            if (!BN_nnmod(tmp_scalar, scalar, group->order, ctx)) {
+                ECerr(EC_F_EC_GFP_NISTP521_POINTS_MUL, ERR_R_BN_LIB);
+                goto err;
+            }
+            num_bytes = BN_bn2bin(tmp_scalar, tmp);
+        } else
+            num_bytes = BN_bn2bin(scalar, tmp);
+        flip_endian(g_secret, tmp, num_bytes);
+        /* do the multiplication with generator precomputation */
+        batch_mul(x_out, y_out, z_out,
+                  (const felem_bytearray(*))secrets, num_points,
+                  g_secret,
+                  mixed, (const felem(*)[17][3])pre_comp,
+                  (const felem(*)[3])g_pre_comp);
+    } else
+        /* do the multiplication without generator precomputation */
+        batch_mul(x_out, y_out, z_out,
+                  (const felem_bytearray(*))secrets, num_points,
+                  NULL, mixed, (const felem(*)[17][3])pre_comp, NULL);
+    /* reduce the output to its unique minimal representation */
+    felem_contract(x_in, x_out);
+    felem_contract(y_in, y_out);
+    felem_contract(z_in, z_out);
+    if ((!felem_to_BN(x, x_in)) || (!felem_to_BN(y, y_in)) ||
+        (!felem_to_BN(z, z_in))) {
+        ECerr(EC_F_EC_GFP_NISTP521_POINTS_MUL, ERR_R_BN_LIB);
+        goto err;
+    }
+    ret = EC_POINT_set_Jprojective_coordinates_GFp(group, r, x, y, z, ctx);
+
+ err:
+    BN_CTX_end(ctx);
+    EC_POINT_free(generator);
+    BN_CTX_free(new_ctx);
+    OPENSSL_free(secrets);
+    OPENSSL_free(pre_comp);
+    OPENSSL_free(tmp_felems);
+    return ret;
+}
+
+int ec_GFp_nistp521_precompute_mult(EC_GROUP *group, BN_CTX *ctx)
+{
+    int ret = 0;
+    NISTP521_PRE_COMP *pre = NULL;
+    int i, j;
+    BN_CTX *new_ctx = NULL;
+    BIGNUM *x, *y;
+    EC_POINT *generator = NULL;
+    felem tmp_felems[16];
+
+    /* throw away old precomputation */
+    EC_pre_comp_free(group);
+    if (ctx == NULL)
+        if ((ctx = new_ctx = BN_CTX_new()) == NULL)
+            return 0;
+    BN_CTX_start(ctx);
+    if (((x = BN_CTX_get(ctx)) == NULL) || ((y = BN_CTX_get(ctx)) == NULL))
+        goto err;
+    /* get the generator */
+    if (group->generator == NULL)
+        goto err;
+    generator = EC_POINT_new(group);
+    if (generator == NULL)
+        goto err;
+    BN_bin2bn(nistp521_curve_params[3], sizeof(felem_bytearray), x);
+    BN_bin2bn(nistp521_curve_params[4], sizeof(felem_bytearray), y);
+    if (!EC_POINT_set_affine_coordinates_GFp(group, generator, x, y, ctx))
+        goto err;
+    if ((pre = nistp521_pre_comp_new()) == NULL)
+        goto err;
+    /*
+     * if the generator is the standard one, use built-in precomputation
+     */
+    if (0 == EC_POINT_cmp(group, generator, group->generator, ctx)) {
+        memcpy(pre->g_pre_comp, gmul, sizeof(pre->g_pre_comp));
+        goto done;
+    }
+    if ((!BN_to_felem(pre->g_pre_comp[1][0], group->generator->X)) ||
+        (!BN_to_felem(pre->g_pre_comp[1][1], group->generator->Y)) ||
+        (!BN_to_felem(pre->g_pre_comp[1][2], group->generator->Z)))
+        goto err;
+    /* compute 2^130*G, 2^260*G, 2^390*G */
+    for (i = 1; i <= 4; i <<= 1) {
+        point_double(pre->g_pre_comp[2 * i][0], pre->g_pre_comp[2 * i][1],
+                     pre->g_pre_comp[2 * i][2], pre->g_pre_comp[i][0],
+                     pre->g_pre_comp[i][1], pre->g_pre_comp[i][2]);
+        for (j = 0; j < 129; ++j) {
+            point_double(pre->g_pre_comp[2 * i][0],
+                         pre->g_pre_comp[2 * i][1],
+                         pre->g_pre_comp[2 * i][2],
+                         pre->g_pre_comp[2 * i][0],
+                         pre->g_pre_comp[2 * i][1],
+                         pre->g_pre_comp[2 * i][2]);
+        }
+    }
+    /* g_pre_comp[0] is the point at infinity */
+    memset(pre->g_pre_comp[0], 0, sizeof(pre->g_pre_comp[0]));
+    /* the remaining multiples */
+    /* 2^130*G + 2^260*G */
+    point_add(pre->g_pre_comp[6][0], pre->g_pre_comp[6][1],
+              pre->g_pre_comp[6][2], pre->g_pre_comp[4][0],
+              pre->g_pre_comp[4][1], pre->g_pre_comp[4][2],
+              0, pre->g_pre_comp[2][0], pre->g_pre_comp[2][1],
+              pre->g_pre_comp[2][2]);
+    /* 2^130*G + 2^390*G */
+    point_add(pre->g_pre_comp[10][0], pre->g_pre_comp[10][1],
+              pre->g_pre_comp[10][2], pre->g_pre_comp[8][0],
+              pre->g_pre_comp[8][1], pre->g_pre_comp[8][2],
+              0, pre->g_pre_comp[2][0], pre->g_pre_comp[2][1],
+              pre->g_pre_comp[2][2]);
+    /* 2^260*G + 2^390*G */
+    point_add(pre->g_pre_comp[12][0], pre->g_pre_comp[12][1],
+              pre->g_pre_comp[12][2], pre->g_pre_comp[8][0],
+              pre->g_pre_comp[8][1], pre->g_pre_comp[8][2],
+              0, pre->g_pre_comp[4][0], pre->g_pre_comp[4][1],
+              pre->g_pre_comp[4][2]);
+    /* 2^130*G + 2^260*G + 2^390*G */
+    point_add(pre->g_pre_comp[14][0], pre->g_pre_comp[14][1],
+              pre->g_pre_comp[14][2], pre->g_pre_comp[12][0],
+              pre->g_pre_comp[12][1], pre->g_pre_comp[12][2],
+              0, pre->g_pre_comp[2][0], pre->g_pre_comp[2][1],
+              pre->g_pre_comp[2][2]);
+    for (i = 1; i < 8; ++i) {
+        /* odd multiples: add G */
+        point_add(pre->g_pre_comp[2 * i + 1][0],
+                  pre->g_pre_comp[2 * i + 1][1],
+                  pre->g_pre_comp[2 * i + 1][2], pre->g_pre_comp[2 * i][0],
+                  pre->g_pre_comp[2 * i][1], pre->g_pre_comp[2 * i][2], 0,
+                  pre->g_pre_comp[1][0], pre->g_pre_comp[1][1],
+                  pre->g_pre_comp[1][2]);
+    }
+    make_points_affine(15, &(pre->g_pre_comp[1]), tmp_felems);
+
+ done:
+    SETPRECOMP(group, nistp521, pre);
+    ret = 1;
+    pre = NULL;
+ err:
+    BN_CTX_end(ctx);
+    EC_POINT_free(generator);
+    BN_CTX_free(new_ctx);
+    EC_nistp521_pre_comp_free(pre);
+    return ret;
+}
+
+int ec_GFp_nistp521_have_precompute_mult(const EC_GROUP *group)
+{
+    return HAVEPRECOMP(group, nistp521);
+}
+
+#endif
diff --git a/crypto/ec/ecp_nistputil.c b/crypto/ec/ecp_nistputil.c
new file mode 100644
index 000000000..97fb63100
--- /dev/null
+++ b/crypto/ec/ecp_nistputil.c
@@ -0,0 +1,223 @@
+/*
+ * Copyright 2011-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/* Copyright 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ *
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+#include 
+#ifdef OPENSSL_NO_EC_NISTP_64_GCC_128
+NON_EMPTY_TRANSLATION_UNIT
+#else
+
+/*
+ * Common utility functions for ecp_nistp224.c, ecp_nistp256.c, ecp_nistp521.c.
+ */
+
+# include 
+# include "ec_lcl.h"
+
+/*
+ * Convert an array of points into affine coordinates. (If the point at
+ * infinity is found (Z = 0), it remains unchanged.) This function is
+ * essentially an equivalent to EC_POINTs_make_affine(), but works with the
+ * internal representation of points as used by ecp_nistp###.c rather than
+ * with (BIGNUM-based) EC_POINT data structures. point_array is the
+ * input/output buffer ('num' points in projective form, i.e. three
+ * coordinates each), based on an internal representation of field elements
+ * of size 'felem_size'. tmp_felems needs to point to a temporary array of
+ * 'num'+1 field elements for storage of intermediate values.
+ */
+void ec_GFp_nistp_points_make_affine_internal(size_t num, void *point_array,
+                                              size_t felem_size,
+                                              void *tmp_felems,
+                                              void (*felem_one) (void *out),
+                                              int (*felem_is_zero) (const void
+                                                                    *in),
+                                              void (*felem_assign) (void *out,
+                                                                    const void
+                                                                    *in),
+                                              void (*felem_square) (void *out,
+                                                                    const void
+                                                                    *in),
+                                              void (*felem_mul) (void *out,
+                                                                 const void
+                                                                 *in1,
+                                                                 const void
+                                                                 *in2),
+                                              void (*felem_inv) (void *out,
+                                                                 const void
+                                                                 *in),
+                                              void (*felem_contract) (void
+                                                                      *out,
+                                                                      const
+                                                                      void
+                                                                      *in))
+{
+    int i = 0;
+
+# define tmp_felem(I) (&((char *)tmp_felems)[(I) * felem_size])
+# define X(I) (&((char *)point_array)[3*(I) * felem_size])
+# define Y(I) (&((char *)point_array)[(3*(I) + 1) * felem_size])
+# define Z(I) (&((char *)point_array)[(3*(I) + 2) * felem_size])
+
+    if (!felem_is_zero(Z(0)))
+        felem_assign(tmp_felem(0), Z(0));
+    else
+        felem_one(tmp_felem(0));
+    for (i = 1; i < (int)num; i++) {
+        if (!felem_is_zero(Z(i)))
+            felem_mul(tmp_felem(i), tmp_felem(i - 1), Z(i));
+        else
+            felem_assign(tmp_felem(i), tmp_felem(i - 1));
+    }
+    /*
+     * Now each tmp_felem(i) is the product of Z(0) .. Z(i), skipping any
+     * zero-valued factors: if Z(i) = 0, we essentially pretend that Z(i) = 1
+     */
+
+    felem_inv(tmp_felem(num - 1), tmp_felem(num - 1));
+    for (i = num - 1; i >= 0; i--) {
+        if (i > 0)
+            /*
+             * tmp_felem(i-1) is the product of Z(0) .. Z(i-1), tmp_felem(i)
+             * is the inverse of the product of Z(0) .. Z(i)
+             */
+            /* 1/Z(i) */
+            felem_mul(tmp_felem(num), tmp_felem(i - 1), tmp_felem(i));
+        else
+            felem_assign(tmp_felem(num), tmp_felem(0)); /* 1/Z(0) */
+
+        if (!felem_is_zero(Z(i))) {
+            if (i > 0)
+                /*
+                 * For next iteration, replace tmp_felem(i-1) by its inverse
+                 */
+                felem_mul(tmp_felem(i - 1), tmp_felem(i), Z(i));
+
+            /*
+             * Convert point (X, Y, Z) into affine form (X/(Z^2), Y/(Z^3), 1)
+             */
+            felem_square(Z(i), tmp_felem(num)); /* 1/(Z^2) */
+            felem_mul(X(i), X(i), Z(i)); /* X/(Z^2) */
+            felem_mul(Z(i), Z(i), tmp_felem(num)); /* 1/(Z^3) */
+            felem_mul(Y(i), Y(i), Z(i)); /* Y/(Z^3) */
+            felem_contract(X(i), X(i));
+            felem_contract(Y(i), Y(i));
+            felem_one(Z(i));
+        } else {
+            if (i > 0)
+                /*
+                 * For next iteration, replace tmp_felem(i-1) by its inverse
+                 */
+                felem_assign(tmp_felem(i - 1), tmp_felem(i));
+        }
+    }
+}
+
+/*-
+ * This function looks at 5+1 scalar bits (5 current, 1 adjacent less
+ * significant bit), and recodes them into a signed digit for use in fast point
+ * multiplication: the use of signed rather than unsigned digits means that
+ * fewer points need to be precomputed, given that point inversion is easy
+ * (a precomputed point dP makes -dP available as well).
+ *
+ * BACKGROUND:
+ *
+ * Signed digits for multiplication were introduced by Booth ("A signed binary
+ * multiplication technique", Quart. Journ. Mech. and Applied Math., vol. IV,
+ * pt. 2 (1951), pp. 236-240), in that case for multiplication of integers.
+ * Booth's original encoding did not generally improve the density of nonzero
+ * digits over the binary representation, and was merely meant to simplify the
+ * handling of signed factors given in two's complement; but it has since been
+ * shown to be the basis of various signed-digit representations that do have
+ * further advantages, including the wNAF, using the following general approach:
+ *
+ * (1) Given a binary representation
+ *
+ *       b_k  ...  b_2  b_1  b_0,
+ *
+ *     of a nonnegative integer (b_k in {0, 1}), rewrite it in digits 0, 1, -1
+ *     by using bit-wise subtraction as follows:
+ *
+ *        b_k b_(k-1)  ...  b_2  b_1  b_0
+ *      -     b_k      ...  b_3  b_2  b_1  b_0
+ *       -------------------------------------
+ *        s_k b_(k-1)  ...  s_3  s_2  s_1  s_0
+ *
+ *     A left-shift followed by subtraction of the original value yields a new
+ *     representation of the same value, using signed bits s_i = b_(i+1) - b_i.
+ *     This representation from Booth's paper has since appeared in the
+ *     literature under a variety of different names including "reversed binary
+ *     form", "alternating greedy expansion", "mutual opposite form", and
+ *     "sign-alternating {+-1}-representation".
+ *
+ *     An interesting property is that among the nonzero bits, values 1 and -1
+ *     strictly alternate.
+ *
+ * (2) Various window schemes can be applied to the Booth representation of
+ *     integers: for example, right-to-left sliding windows yield the wNAF
+ *     (a signed-digit encoding independently discovered by various researchers
+ *     in the 1990s), and left-to-right sliding windows yield a left-to-right
+ *     equivalent of the wNAF (independently discovered by various researchers
+ *     around 2004).
+ *
+ * To prevent leaking information through side channels in point multiplication,
+ * we need to recode the given integer into a regular pattern: sliding windows
+ * as in wNAFs won't do, we need their fixed-window equivalent -- which is a few
+ * decades older: we'll be using the so-called "modified Booth encoding" due to
+ * MacSorley ("High-speed arithmetic in binary computers", Proc. IRE, vol. 49
+ * (1961), pp. 67-91), in a radix-2^5 setting.  That is, we always combine five
+ * signed bits into a signed digit:
+ *
+ *       s_(4j + 4) s_(4j + 3) s_(4j + 2) s_(4j + 1) s_(4j)
+ *
+ * The sign-alternating property implies that the resulting digit values are
+ * integers from -16 to 16.
+ *
+ * Of course, we don't actually need to compute the signed digits s_i as an
+ * intermediate step (that's just a nice way to see how this scheme relates
+ * to the wNAF): a direct computation obtains the recoded digit from the
+ * six bits b_(4j + 4) ... b_(4j - 1).
+ *
+ * This function takes those five bits as an integer (0 .. 63), writing the
+ * recoded digit to *sign (0 for positive, 1 for negative) and *digit (absolute
+ * value, in the range 0 .. 8).  Note that this integer essentially provides the
+ * input bits "shifted to the left" by one position: for example, the input to
+ * compute the least significant recoded digit, given that there's no bit b_-1,
+ * has to be b_4 b_3 b_2 b_1 b_0 0.
+ *
+ */
+void ec_GFp_nistp_recode_scalar_bits(unsigned char *sign,
+                                     unsigned char *digit, unsigned char in)
+{
+    unsigned char s, d;
+
+    s = ~((in >> 5) - 1);       /* sets all bits to MSB(in), 'in' seen as
+                                 * 6-bit value */
+    d = (1 << 6) - in - 1;
+    d = (d & s) | (in & ~s);
+    d = (d >> 1) + (d & 1);
+
+    *sign = s & 1;
+    *digit = d;
+}
+#endif
diff --git a/crypto/ec/ecp_nistz256.c b/crypto/ec/ecp_nistz256.c
new file mode 100644
index 000000000..dca3a2dde
--- /dev/null
+++ b/crypto/ec/ecp_nistz256.c
@@ -0,0 +1,1559 @@
+/*
+ * Copyright 2014-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/******************************************************************************
+ *                                                                            *
+ * Copyright 2014 Intel Corporation                                           *
+ *                                                                            *
+ * Licensed under the Apache License, Version 2.0 (the "License");            *
+ * you may not use this file except in compliance with the License.           *
+ * You may obtain a copy of the License at                                    *
+ *                                                                            *
+ *    http://www.apache.org/licenses/LICENSE-2.0                              *
+ *                                                                            *
+ * Unless required by applicable law or agreed to in writing, software        *
+ * distributed under the License is distributed on an "AS IS" BASIS,          *
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   *
+ * See the License for the specific language governing permissions and        *
+ * limitations under the License.                                             *
+ *                                                                            *
+ ******************************************************************************
+ *                                                                            *
+ * Developers and authors:                                                    *
+ * Shay Gueron (1, 2), and Vlad Krasnov (1)                                   *
+ * (1) Intel Corporation, Israel Development Center                           *
+ * (2) University of Haifa                                                    *
+ * Reference:                                                                 *
+ * S.Gueron and V.Krasnov, "Fast Prime Field Elliptic Curve Cryptography with *
+ *                          256 Bit Primes"                                   *
+ *                                                                            *
+ ******************************************************************************/
+
+#include 
+
+#include "internal/cryptlib.h"
+#include "internal/bn_int.h"
+#include "ec_lcl.h"
+
+#if BN_BITS2 != 64
+# define TOBN(hi,lo)    lo,hi
+#else
+# define TOBN(hi,lo)    ((BN_ULONG)hi<<32|lo)
+#endif
+
+#if defined(__GNUC__)
+# define ALIGN32        __attribute((aligned(32)))
+#elif defined(_MSC_VER)
+# define ALIGN32        __declspec(align(32))
+#else
+# define ALIGN32
+#endif
+
+#define ALIGNPTR(p,N)   ((unsigned char *)p+N-(size_t)p%N)
+#define P256_LIMBS      (256/BN_BITS2)
+
+typedef unsigned short u16;
+
+typedef struct {
+    BN_ULONG X[P256_LIMBS];
+    BN_ULONG Y[P256_LIMBS];
+    BN_ULONG Z[P256_LIMBS];
+} P256_POINT;
+
+typedef struct {
+    BN_ULONG X[P256_LIMBS];
+    BN_ULONG Y[P256_LIMBS];
+} P256_POINT_AFFINE;
+
+typedef P256_POINT_AFFINE PRECOMP256_ROW[64];
+
+/* structure for precomputed multiples of the generator */
+struct nistz256_pre_comp_st {
+    const EC_GROUP *group;      /* Parent EC_GROUP object */
+    size_t w;                   /* Window size */
+    /*
+     * Constant time access to the X and Y coordinates of the pre-computed,
+     * generator multiplies, in the Montgomery domain. Pre-calculated
+     * multiplies are stored in affine form.
+     */
+    PRECOMP256_ROW *precomp;
+    void *precomp_storage;
+    int references;
+    CRYPTO_RWLOCK *lock;
+};
+
+/* Functions implemented in assembly */
+/*
+ * Most of below mentioned functions *preserve* the property of inputs
+ * being fully reduced, i.e. being in [0, modulus) range. Simply put if
+ * inputs are fully reduced, then output is too. Note that reverse is
+ * not true, in sense that given partially reduced inputs output can be
+ * either, not unlikely reduced. And "most" in first sentence refers to
+ * the fact that given the calculations flow one can tolerate that
+ * addition, 1st function below, produces partially reduced result *if*
+ * multiplications by 2 and 3, which customarily use addition, fully
+ * reduce it. This effectively gives two options: a) addition produces
+ * fully reduced result [as long as inputs are, just like remaining
+ * functions]; b) addition is allowed to produce partially reduced
+ * result, but multiplications by 2 and 3 perform additional reduction
+ * step. Choice between the two can be platform-specific, but it was a)
+ * in all cases so far...
+ */
+/* Modular add: res = a+b mod P   */
+void ecp_nistz256_add(BN_ULONG res[P256_LIMBS],
+                      const BN_ULONG a[P256_LIMBS],
+                      const BN_ULONG b[P256_LIMBS]);
+/* Modular mul by 2: res = 2*a mod P */
+void ecp_nistz256_mul_by_2(BN_ULONG res[P256_LIMBS],
+                           const BN_ULONG a[P256_LIMBS]);
+/* Modular mul by 3: res = 3*a mod P */
+void ecp_nistz256_mul_by_3(BN_ULONG res[P256_LIMBS],
+                           const BN_ULONG a[P256_LIMBS]);
+
+/* Modular div by 2: res = a/2 mod P */
+void ecp_nistz256_div_by_2(BN_ULONG res[P256_LIMBS],
+                           const BN_ULONG a[P256_LIMBS]);
+/* Modular sub: res = a-b mod P   */
+void ecp_nistz256_sub(BN_ULONG res[P256_LIMBS],
+                      const BN_ULONG a[P256_LIMBS],
+                      const BN_ULONG b[P256_LIMBS]);
+/* Modular neg: res = -a mod P    */
+void ecp_nistz256_neg(BN_ULONG res[P256_LIMBS], const BN_ULONG a[P256_LIMBS]);
+/* Montgomery mul: res = a*b*2^-256 mod P */
+void ecp_nistz256_mul_mont(BN_ULONG res[P256_LIMBS],
+                           const BN_ULONG a[P256_LIMBS],
+                           const BN_ULONG b[P256_LIMBS]);
+/* Montgomery sqr: res = a*a*2^-256 mod P */
+void ecp_nistz256_sqr_mont(BN_ULONG res[P256_LIMBS],
+                           const BN_ULONG a[P256_LIMBS]);
+/* Convert a number from Montgomery domain, by multiplying with 1 */
+void ecp_nistz256_from_mont(BN_ULONG res[P256_LIMBS],
+                            const BN_ULONG in[P256_LIMBS]);
+/* Convert a number to Montgomery domain, by multiplying with 2^512 mod P*/
+void ecp_nistz256_to_mont(BN_ULONG res[P256_LIMBS],
+                          const BN_ULONG in[P256_LIMBS]);
+/* Functions that perform constant time access to the precomputed tables */
+void ecp_nistz256_scatter_w5(P256_POINT *val,
+                             const P256_POINT *in_t, int idx);
+void ecp_nistz256_gather_w5(P256_POINT *val,
+                            const P256_POINT *in_t, int idx);
+void ecp_nistz256_scatter_w7(P256_POINT_AFFINE *val,
+                             const P256_POINT_AFFINE *in_t, int idx);
+void ecp_nistz256_gather_w7(P256_POINT_AFFINE *val,
+                            const P256_POINT_AFFINE *in_t, int idx);
+
+/* One converted into the Montgomery domain */
+static const BN_ULONG ONE[P256_LIMBS] = {
+    TOBN(0x00000000, 0x00000001), TOBN(0xffffffff, 0x00000000),
+    TOBN(0xffffffff, 0xffffffff), TOBN(0x00000000, 0xfffffffe)
+};
+
+static NISTZ256_PRE_COMP *ecp_nistz256_pre_comp_new(const EC_GROUP *group);
+
+/* Precomputed tables for the default generator */
+extern const PRECOMP256_ROW ecp_nistz256_precomputed[37];
+
+/* Recode window to a signed digit, see ecp_nistputil.c for details */
+static unsigned int _booth_recode_w5(unsigned int in)
+{
+    unsigned int s, d;
+
+    s = ~((in >> 5) - 1);
+    d = (1 << 6) - in - 1;
+    d = (d & s) | (in & ~s);
+    d = (d >> 1) + (d & 1);
+
+    return (d << 1) + (s & 1);
+}
+
+static unsigned int _booth_recode_w7(unsigned int in)
+{
+    unsigned int s, d;
+
+    s = ~((in >> 7) - 1);
+    d = (1 << 8) - in - 1;
+    d = (d & s) | (in & ~s);
+    d = (d >> 1) + (d & 1);
+
+    return (d << 1) + (s & 1);
+}
+
+static void copy_conditional(BN_ULONG dst[P256_LIMBS],
+                             const BN_ULONG src[P256_LIMBS], BN_ULONG move)
+{
+    BN_ULONG mask1 = 0-move;
+    BN_ULONG mask2 = ~mask1;
+
+    dst[0] = (src[0] & mask1) ^ (dst[0] & mask2);
+    dst[1] = (src[1] & mask1) ^ (dst[1] & mask2);
+    dst[2] = (src[2] & mask1) ^ (dst[2] & mask2);
+    dst[3] = (src[3] & mask1) ^ (dst[3] & mask2);
+    if (P256_LIMBS == 8) {
+        dst[4] = (src[4] & mask1) ^ (dst[4] & mask2);
+        dst[5] = (src[5] & mask1) ^ (dst[5] & mask2);
+        dst[6] = (src[6] & mask1) ^ (dst[6] & mask2);
+        dst[7] = (src[7] & mask1) ^ (dst[7] & mask2);
+    }
+}
+
+static BN_ULONG is_zero(BN_ULONG in)
+{
+    in |= (0 - in);
+    in = ~in;
+    in >>= BN_BITS2 - 1;
+    return in;
+}
+
+static BN_ULONG is_equal(const BN_ULONG a[P256_LIMBS],
+                         const BN_ULONG b[P256_LIMBS])
+{
+    BN_ULONG res;
+
+    res = a[0] ^ b[0];
+    res |= a[1] ^ b[1];
+    res |= a[2] ^ b[2];
+    res |= a[3] ^ b[3];
+    if (P256_LIMBS == 8) {
+        res |= a[4] ^ b[4];
+        res |= a[5] ^ b[5];
+        res |= a[6] ^ b[6];
+        res |= a[7] ^ b[7];
+    }
+
+    return is_zero(res);
+}
+
+static BN_ULONG is_one(const BIGNUM *z)
+{
+    BN_ULONG res = 0;
+    BN_ULONG *a = bn_get_words(z);
+
+    if (bn_get_top(z) == (P256_LIMBS - P256_LIMBS / 8)) {
+        res = a[0] ^ ONE[0];
+        res |= a[1] ^ ONE[1];
+        res |= a[2] ^ ONE[2];
+        res |= a[3] ^ ONE[3];
+        if (P256_LIMBS == 8) {
+            res |= a[4] ^ ONE[4];
+            res |= a[5] ^ ONE[5];
+            res |= a[6] ^ ONE[6];
+            /*
+             * no check for a[7] (being zero) on 32-bit platforms,
+             * because value of "one" takes only 7 limbs.
+             */
+        }
+        res = is_zero(res);
+    }
+
+    return res;
+}
+
+#ifndef ECP_NISTZ256_REFERENCE_IMPLEMENTATION
+void ecp_nistz256_point_double(P256_POINT *r, const P256_POINT *a);
+void ecp_nistz256_point_add(P256_POINT *r,
+                            const P256_POINT *a, const P256_POINT *b);
+void ecp_nistz256_point_add_affine(P256_POINT *r,
+                                   const P256_POINT *a,
+                                   const P256_POINT_AFFINE *b);
+#else
+/* Point double: r = 2*a */
+static void ecp_nistz256_point_double(P256_POINT *r, const P256_POINT *a)
+{
+    BN_ULONG S[P256_LIMBS];
+    BN_ULONG M[P256_LIMBS];
+    BN_ULONG Zsqr[P256_LIMBS];
+    BN_ULONG tmp0[P256_LIMBS];
+
+    const BN_ULONG *in_x = a->X;
+    const BN_ULONG *in_y = a->Y;
+    const BN_ULONG *in_z = a->Z;
+
+    BN_ULONG *res_x = r->X;
+    BN_ULONG *res_y = r->Y;
+    BN_ULONG *res_z = r->Z;
+
+    ecp_nistz256_mul_by_2(S, in_y);
+
+    ecp_nistz256_sqr_mont(Zsqr, in_z);
+
+    ecp_nistz256_sqr_mont(S, S);
+
+    ecp_nistz256_mul_mont(res_z, in_z, in_y);
+    ecp_nistz256_mul_by_2(res_z, res_z);
+
+    ecp_nistz256_add(M, in_x, Zsqr);
+    ecp_nistz256_sub(Zsqr, in_x, Zsqr);
+
+    ecp_nistz256_sqr_mont(res_y, S);
+    ecp_nistz256_div_by_2(res_y, res_y);
+
+    ecp_nistz256_mul_mont(M, M, Zsqr);
+    ecp_nistz256_mul_by_3(M, M);
+
+    ecp_nistz256_mul_mont(S, S, in_x);
+    ecp_nistz256_mul_by_2(tmp0, S);
+
+    ecp_nistz256_sqr_mont(res_x, M);
+
+    ecp_nistz256_sub(res_x, res_x, tmp0);
+    ecp_nistz256_sub(S, S, res_x);
+
+    ecp_nistz256_mul_mont(S, S, M);
+    ecp_nistz256_sub(res_y, S, res_y);
+}
+
+/* Point addition: r = a+b */
+static void ecp_nistz256_point_add(P256_POINT *r,
+                                   const P256_POINT *a, const P256_POINT *b)
+{
+    BN_ULONG U2[P256_LIMBS], S2[P256_LIMBS];
+    BN_ULONG U1[P256_LIMBS], S1[P256_LIMBS];
+    BN_ULONG Z1sqr[P256_LIMBS];
+    BN_ULONG Z2sqr[P256_LIMBS];
+    BN_ULONG H[P256_LIMBS], R[P256_LIMBS];
+    BN_ULONG Hsqr[P256_LIMBS];
+    BN_ULONG Rsqr[P256_LIMBS];
+    BN_ULONG Hcub[P256_LIMBS];
+
+    BN_ULONG res_x[P256_LIMBS];
+    BN_ULONG res_y[P256_LIMBS];
+    BN_ULONG res_z[P256_LIMBS];
+
+    BN_ULONG in1infty, in2infty;
+
+    const BN_ULONG *in1_x = a->X;
+    const BN_ULONG *in1_y = a->Y;
+    const BN_ULONG *in1_z = a->Z;
+
+    const BN_ULONG *in2_x = b->X;
+    const BN_ULONG *in2_y = b->Y;
+    const BN_ULONG *in2_z = b->Z;
+
+    /*
+     * Infinity in encoded as (,,0)
+     */
+    in1infty = (in1_z[0] | in1_z[1] | in1_z[2] | in1_z[3]);
+    if (P256_LIMBS == 8)
+        in1infty |= (in1_z[4] | in1_z[5] | in1_z[6] | in1_z[7]);
+
+    in2infty = (in2_z[0] | in2_z[1] | in2_z[2] | in2_z[3]);
+    if (P256_LIMBS == 8)
+        in2infty |= (in2_z[4] | in2_z[5] | in2_z[6] | in2_z[7]);
+
+    in1infty = is_zero(in1infty);
+    in2infty = is_zero(in2infty);
+
+    ecp_nistz256_sqr_mont(Z2sqr, in2_z);        /* Z2^2 */
+    ecp_nistz256_sqr_mont(Z1sqr, in1_z);        /* Z1^2 */
+
+    ecp_nistz256_mul_mont(S1, Z2sqr, in2_z);    /* S1 = Z2^3 */
+    ecp_nistz256_mul_mont(S2, Z1sqr, in1_z);    /* S2 = Z1^3 */
+
+    ecp_nistz256_mul_mont(S1, S1, in1_y);       /* S1 = Y1*Z2^3 */
+    ecp_nistz256_mul_mont(S2, S2, in2_y);       /* S2 = Y2*Z1^3 */
+    ecp_nistz256_sub(R, S2, S1);                /* R = S2 - S1 */
+
+    ecp_nistz256_mul_mont(U1, in1_x, Z2sqr);    /* U1 = X1*Z2^2 */
+    ecp_nistz256_mul_mont(U2, in2_x, Z1sqr);    /* U2 = X2*Z1^2 */
+    ecp_nistz256_sub(H, U2, U1);                /* H = U2 - U1 */
+
+    /*
+     * This should not happen during sign/ecdh, so no constant time violation
+     */
+    if (is_equal(U1, U2) && !in1infty && !in2infty) {
+        if (is_equal(S1, S2)) {
+            ecp_nistz256_point_double(r, a);
+            return;
+        } else {
+            memset(r, 0, sizeof(*r));
+            return;
+        }
+    }
+
+    ecp_nistz256_sqr_mont(Rsqr, R);             /* R^2 */
+    ecp_nistz256_mul_mont(res_z, H, in1_z);     /* Z3 = H*Z1*Z2 */
+    ecp_nistz256_sqr_mont(Hsqr, H);             /* H^2 */
+    ecp_nistz256_mul_mont(res_z, res_z, in2_z); /* Z3 = H*Z1*Z2 */
+    ecp_nistz256_mul_mont(Hcub, Hsqr, H);       /* H^3 */
+
+    ecp_nistz256_mul_mont(U2, U1, Hsqr);        /* U1*H^2 */
+    ecp_nistz256_mul_by_2(Hsqr, U2);            /* 2*U1*H^2 */
+
+    ecp_nistz256_sub(res_x, Rsqr, Hsqr);
+    ecp_nistz256_sub(res_x, res_x, Hcub);
+
+    ecp_nistz256_sub(res_y, U2, res_x);
+
+    ecp_nistz256_mul_mont(S2, S1, Hcub);
+    ecp_nistz256_mul_mont(res_y, R, res_y);
+    ecp_nistz256_sub(res_y, res_y, S2);
+
+    copy_conditional(res_x, in2_x, in1infty);
+    copy_conditional(res_y, in2_y, in1infty);
+    copy_conditional(res_z, in2_z, in1infty);
+
+    copy_conditional(res_x, in1_x, in2infty);
+    copy_conditional(res_y, in1_y, in2infty);
+    copy_conditional(res_z, in1_z, in2infty);
+
+    memcpy(r->X, res_x, sizeof(res_x));
+    memcpy(r->Y, res_y, sizeof(res_y));
+    memcpy(r->Z, res_z, sizeof(res_z));
+}
+
+/* Point addition when b is known to be affine: r = a+b */
+static void ecp_nistz256_point_add_affine(P256_POINT *r,
+                                          const P256_POINT *a,
+                                          const P256_POINT_AFFINE *b)
+{
+    BN_ULONG U2[P256_LIMBS], S2[P256_LIMBS];
+    BN_ULONG Z1sqr[P256_LIMBS];
+    BN_ULONG H[P256_LIMBS], R[P256_LIMBS];
+    BN_ULONG Hsqr[P256_LIMBS];
+    BN_ULONG Rsqr[P256_LIMBS];
+    BN_ULONG Hcub[P256_LIMBS];
+
+    BN_ULONG res_x[P256_LIMBS];
+    BN_ULONG res_y[P256_LIMBS];
+    BN_ULONG res_z[P256_LIMBS];
+
+    BN_ULONG in1infty, in2infty;
+
+    const BN_ULONG *in1_x = a->X;
+    const BN_ULONG *in1_y = a->Y;
+    const BN_ULONG *in1_z = a->Z;
+
+    const BN_ULONG *in2_x = b->X;
+    const BN_ULONG *in2_y = b->Y;
+
+    /*
+     * Infinity in encoded as (,,0)
+     */
+    in1infty = (in1_z[0] | in1_z[1] | in1_z[2] | in1_z[3]);
+    if (P256_LIMBS == 8)
+        in1infty |= (in1_z[4] | in1_z[5] | in1_z[6] | in1_z[7]);
+
+    /*
+     * In affine representation we encode infinity as (0,0), which is
+     * not on the curve, so it is OK
+     */
+    in2infty = (in2_x[0] | in2_x[1] | in2_x[2] | in2_x[3] |
+                in2_y[0] | in2_y[1] | in2_y[2] | in2_y[3]);
+    if (P256_LIMBS == 8)
+        in2infty |= (in2_x[4] | in2_x[5] | in2_x[6] | in2_x[7] |
+                     in2_y[4] | in2_y[5] | in2_y[6] | in2_y[7]);
+
+    in1infty = is_zero(in1infty);
+    in2infty = is_zero(in2infty);
+
+    ecp_nistz256_sqr_mont(Z1sqr, in1_z);        /* Z1^2 */
+
+    ecp_nistz256_mul_mont(U2, in2_x, Z1sqr);    /* U2 = X2*Z1^2 */
+    ecp_nistz256_sub(H, U2, in1_x);             /* H = U2 - U1 */
+
+    ecp_nistz256_mul_mont(S2, Z1sqr, in1_z);    /* S2 = Z1^3 */
+
+    ecp_nistz256_mul_mont(res_z, H, in1_z);     /* Z3 = H*Z1*Z2 */
+
+    ecp_nistz256_mul_mont(S2, S2, in2_y);       /* S2 = Y2*Z1^3 */
+    ecp_nistz256_sub(R, S2, in1_y);             /* R = S2 - S1 */
+
+    ecp_nistz256_sqr_mont(Hsqr, H);             /* H^2 */
+    ecp_nistz256_sqr_mont(Rsqr, R);             /* R^2 */
+    ecp_nistz256_mul_mont(Hcub, Hsqr, H);       /* H^3 */
+
+    ecp_nistz256_mul_mont(U2, in1_x, Hsqr);     /* U1*H^2 */
+    ecp_nistz256_mul_by_2(Hsqr, U2);            /* 2*U1*H^2 */
+
+    ecp_nistz256_sub(res_x, Rsqr, Hsqr);
+    ecp_nistz256_sub(res_x, res_x, Hcub);
+    ecp_nistz256_sub(H, U2, res_x);
+
+    ecp_nistz256_mul_mont(S2, in1_y, Hcub);
+    ecp_nistz256_mul_mont(H, H, R);
+    ecp_nistz256_sub(res_y, H, S2);
+
+    copy_conditional(res_x, in2_x, in1infty);
+    copy_conditional(res_x, in1_x, in2infty);
+
+    copy_conditional(res_y, in2_y, in1infty);
+    copy_conditional(res_y, in1_y, in2infty);
+
+    copy_conditional(res_z, ONE, in1infty);
+    copy_conditional(res_z, in1_z, in2infty);
+
+    memcpy(r->X, res_x, sizeof(res_x));
+    memcpy(r->Y, res_y, sizeof(res_y));
+    memcpy(r->Z, res_z, sizeof(res_z));
+}
+#endif
+
+/* r = in^-1 mod p */
+static void ecp_nistz256_mod_inverse(BN_ULONG r[P256_LIMBS],
+                                     const BN_ULONG in[P256_LIMBS])
+{
+    /*
+     * The poly is ffffffff 00000001 00000000 00000000 00000000 ffffffff
+     * ffffffff ffffffff We use FLT and used poly-2 as exponent
+     */
+    BN_ULONG p2[P256_LIMBS];
+    BN_ULONG p4[P256_LIMBS];
+    BN_ULONG p8[P256_LIMBS];
+    BN_ULONG p16[P256_LIMBS];
+    BN_ULONG p32[P256_LIMBS];
+    BN_ULONG res[P256_LIMBS];
+    int i;
+
+    ecp_nistz256_sqr_mont(res, in);
+    ecp_nistz256_mul_mont(p2, res, in);         /* 3*p */
+
+    ecp_nistz256_sqr_mont(res, p2);
+    ecp_nistz256_sqr_mont(res, res);
+    ecp_nistz256_mul_mont(p4, res, p2);         /* f*p */
+
+    ecp_nistz256_sqr_mont(res, p4);
+    ecp_nistz256_sqr_mont(res, res);
+    ecp_nistz256_sqr_mont(res, res);
+    ecp_nistz256_sqr_mont(res, res);
+    ecp_nistz256_mul_mont(p8, res, p4);         /* ff*p */
+
+    ecp_nistz256_sqr_mont(res, p8);
+    for (i = 0; i < 7; i++)
+        ecp_nistz256_sqr_mont(res, res);
+    ecp_nistz256_mul_mont(p16, res, p8);        /* ffff*p */
+
+    ecp_nistz256_sqr_mont(res, p16);
+    for (i = 0; i < 15; i++)
+        ecp_nistz256_sqr_mont(res, res);
+    ecp_nistz256_mul_mont(p32, res, p16);       /* ffffffff*p */
+
+    ecp_nistz256_sqr_mont(res, p32);
+    for (i = 0; i < 31; i++)
+        ecp_nistz256_sqr_mont(res, res);
+    ecp_nistz256_mul_mont(res, res, in);
+
+    for (i = 0; i < 32 * 4; i++)
+        ecp_nistz256_sqr_mont(res, res);
+    ecp_nistz256_mul_mont(res, res, p32);
+
+    for (i = 0; i < 32; i++)
+        ecp_nistz256_sqr_mont(res, res);
+    ecp_nistz256_mul_mont(res, res, p32);
+
+    for (i = 0; i < 16; i++)
+        ecp_nistz256_sqr_mont(res, res);
+    ecp_nistz256_mul_mont(res, res, p16);
+
+    for (i = 0; i < 8; i++)
+        ecp_nistz256_sqr_mont(res, res);
+    ecp_nistz256_mul_mont(res, res, p8);
+
+    ecp_nistz256_sqr_mont(res, res);
+    ecp_nistz256_sqr_mont(res, res);
+    ecp_nistz256_sqr_mont(res, res);
+    ecp_nistz256_sqr_mont(res, res);
+    ecp_nistz256_mul_mont(res, res, p4);
+
+    ecp_nistz256_sqr_mont(res, res);
+    ecp_nistz256_sqr_mont(res, res);
+    ecp_nistz256_mul_mont(res, res, p2);
+
+    ecp_nistz256_sqr_mont(res, res);
+    ecp_nistz256_sqr_mont(res, res);
+    ecp_nistz256_mul_mont(res, res, in);
+
+    memcpy(r, res, sizeof(res));
+}
+
+/*
+ * ecp_nistz256_bignum_to_field_elem copies the contents of |in| to |out| and
+ * returns one if it fits. Otherwise it returns zero.
+ */
+__owur static int ecp_nistz256_bignum_to_field_elem(BN_ULONG out[P256_LIMBS],
+                                                    const BIGNUM *in)
+{
+    return bn_copy_words(out, in, P256_LIMBS);
+}
+
+/* r = sum(scalar[i]*point[i]) */
+__owur static int ecp_nistz256_windowed_mul(const EC_GROUP *group,
+                                            P256_POINT *r,
+                                            const BIGNUM **scalar,
+                                            const EC_POINT **point,
+                                            size_t num, BN_CTX *ctx)
+{
+    size_t i;
+    int j, ret = 0;
+    unsigned int idx;
+    unsigned char (*p_str)[33] = NULL;
+    const unsigned int window_size = 5;
+    const unsigned int mask = (1 << (window_size + 1)) - 1;
+    unsigned int wvalue;
+    P256_POINT *temp;           /* place for 5 temporary points */
+    const BIGNUM **scalars = NULL;
+    P256_POINT (*table)[16] = NULL;
+    void *table_storage = NULL;
+
+    if ((num * 16 + 6) > OPENSSL_MALLOC_MAX_NELEMS(P256_POINT)
+        || (table_storage =
+            OPENSSL_malloc((num * 16 + 5) * sizeof(P256_POINT) + 64)) == NULL
+        || (p_str =
+            OPENSSL_malloc(num * 33 * sizeof(unsigned char))) == NULL
+        || (scalars = OPENSSL_malloc(num * sizeof(BIGNUM *))) == NULL) {
+        ECerr(EC_F_ECP_NISTZ256_WINDOWED_MUL, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+
+    table = (void *)ALIGNPTR(table_storage, 64);
+    temp = (P256_POINT *)(table + num);
+
+    for (i = 0; i < num; i++) {
+        P256_POINT *row = table[i];
+
+        /* This is an unusual input, we don't guarantee constant-timeness. */
+        if ((BN_num_bits(scalar[i]) > 256) || BN_is_negative(scalar[i])) {
+            BIGNUM *mod;
+
+            if ((mod = BN_CTX_get(ctx)) == NULL)
+                goto err;
+            if (!BN_nnmod(mod, scalar[i], group->order, ctx)) {
+                ECerr(EC_F_ECP_NISTZ256_WINDOWED_MUL, ERR_R_BN_LIB);
+                goto err;
+            }
+            scalars[i] = mod;
+        } else
+            scalars[i] = scalar[i];
+
+        for (j = 0; j < bn_get_top(scalars[i]) * BN_BYTES; j += BN_BYTES) {
+            BN_ULONG d = bn_get_words(scalars[i])[j / BN_BYTES];
+
+            p_str[i][j + 0] = (unsigned char)d;
+            p_str[i][j + 1] = (unsigned char)(d >> 8);
+            p_str[i][j + 2] = (unsigned char)(d >> 16);
+            p_str[i][j + 3] = (unsigned char)(d >>= 24);
+            if (BN_BYTES == 8) {
+                d >>= 8;
+                p_str[i][j + 4] = (unsigned char)d;
+                p_str[i][j + 5] = (unsigned char)(d >> 8);
+                p_str[i][j + 6] = (unsigned char)(d >> 16);
+                p_str[i][j + 7] = (unsigned char)(d >> 24);
+            }
+        }
+        for (; j < 33; j++)
+            p_str[i][j] = 0;
+
+        if (!ecp_nistz256_bignum_to_field_elem(temp[0].X, point[i]->X)
+            || !ecp_nistz256_bignum_to_field_elem(temp[0].Y, point[i]->Y)
+            || !ecp_nistz256_bignum_to_field_elem(temp[0].Z, point[i]->Z)) {
+            ECerr(EC_F_ECP_NISTZ256_WINDOWED_MUL,
+                  EC_R_COORDINATES_OUT_OF_RANGE);
+            goto err;
+        }
+
+        /*
+         * row[0] is implicitly (0,0,0) (the point at infinity), therefore it
+         * is not stored. All other values are actually stored with an offset
+         * of -1 in table.
+         */
+
+        ecp_nistz256_scatter_w5  (row, &temp[0], 1);
+        ecp_nistz256_point_double(&temp[1], &temp[0]);              /*1+1=2  */
+        ecp_nistz256_scatter_w5  (row, &temp[1], 2);
+        ecp_nistz256_point_add   (&temp[2], &temp[1], &temp[0]);    /*2+1=3  */
+        ecp_nistz256_scatter_w5  (row, &temp[2], 3);
+        ecp_nistz256_point_double(&temp[1], &temp[1]);              /*2*2=4  */
+        ecp_nistz256_scatter_w5  (row, &temp[1], 4);
+        ecp_nistz256_point_double(&temp[2], &temp[2]);              /*2*3=6  */
+        ecp_nistz256_scatter_w5  (row, &temp[2], 6);
+        ecp_nistz256_point_add   (&temp[3], &temp[1], &temp[0]);    /*4+1=5  */
+        ecp_nistz256_scatter_w5  (row, &temp[3], 5);
+        ecp_nistz256_point_add   (&temp[4], &temp[2], &temp[0]);    /*6+1=7  */
+        ecp_nistz256_scatter_w5  (row, &temp[4], 7);
+        ecp_nistz256_point_double(&temp[1], &temp[1]);              /*2*4=8  */
+        ecp_nistz256_scatter_w5  (row, &temp[1], 8);
+        ecp_nistz256_point_double(&temp[2], &temp[2]);              /*2*6=12 */
+        ecp_nistz256_scatter_w5  (row, &temp[2], 12);
+        ecp_nistz256_point_double(&temp[3], &temp[3]);              /*2*5=10 */
+        ecp_nistz256_scatter_w5  (row, &temp[3], 10);
+        ecp_nistz256_point_double(&temp[4], &temp[4]);              /*2*7=14 */
+        ecp_nistz256_scatter_w5  (row, &temp[4], 14);
+        ecp_nistz256_point_add   (&temp[2], &temp[2], &temp[0]);    /*12+1=13*/
+        ecp_nistz256_scatter_w5  (row, &temp[2], 13);
+        ecp_nistz256_point_add   (&temp[3], &temp[3], &temp[0]);    /*10+1=11*/
+        ecp_nistz256_scatter_w5  (row, &temp[3], 11);
+        ecp_nistz256_point_add   (&temp[4], &temp[4], &temp[0]);    /*14+1=15*/
+        ecp_nistz256_scatter_w5  (row, &temp[4], 15);
+        ecp_nistz256_point_add   (&temp[2], &temp[1], &temp[0]);    /*8+1=9  */
+        ecp_nistz256_scatter_w5  (row, &temp[2], 9);
+        ecp_nistz256_point_double(&temp[1], &temp[1]);              /*2*8=16 */
+        ecp_nistz256_scatter_w5  (row, &temp[1], 16);
+    }
+
+    idx = 255;
+
+    wvalue = p_str[0][(idx - 1) / 8];
+    wvalue = (wvalue >> ((idx - 1) % 8)) & mask;
+
+    /*
+     * We gather to temp[0], because we know it's position relative
+     * to table
+     */
+    ecp_nistz256_gather_w5(&temp[0], table[0], _booth_recode_w5(wvalue) >> 1);
+    memcpy(r, &temp[0], sizeof(temp[0]));
+
+    while (idx >= 5) {
+        for (i = (idx == 255 ? 1 : 0); i < num; i++) {
+            unsigned int off = (idx - 1) / 8;
+
+            wvalue = p_str[i][off] | p_str[i][off + 1] << 8;
+            wvalue = (wvalue >> ((idx - 1) % 8)) & mask;
+
+            wvalue = _booth_recode_w5(wvalue);
+
+            ecp_nistz256_gather_w5(&temp[0], table[i], wvalue >> 1);
+
+            ecp_nistz256_neg(temp[1].Y, temp[0].Y);
+            copy_conditional(temp[0].Y, temp[1].Y, (wvalue & 1));
+
+            ecp_nistz256_point_add(r, r, &temp[0]);
+        }
+
+        idx -= window_size;
+
+        ecp_nistz256_point_double(r, r);
+        ecp_nistz256_point_double(r, r);
+        ecp_nistz256_point_double(r, r);
+        ecp_nistz256_point_double(r, r);
+        ecp_nistz256_point_double(r, r);
+    }
+
+    /* Final window */
+    for (i = 0; i < num; i++) {
+        wvalue = p_str[i][0];
+        wvalue = (wvalue << 1) & mask;
+
+        wvalue = _booth_recode_w5(wvalue);
+
+        ecp_nistz256_gather_w5(&temp[0], table[i], wvalue >> 1);
+
+        ecp_nistz256_neg(temp[1].Y, temp[0].Y);
+        copy_conditional(temp[0].Y, temp[1].Y, wvalue & 1);
+
+        ecp_nistz256_point_add(r, r, &temp[0]);
+    }
+
+    ret = 1;
+ err:
+    OPENSSL_free(table_storage);
+    OPENSSL_free(p_str);
+    OPENSSL_free(scalars);
+    return ret;
+}
+
+/* Coordinates of G, for which we have precomputed tables */
+const static BN_ULONG def_xG[P256_LIMBS] = {
+    TOBN(0x79e730d4, 0x18a9143c), TOBN(0x75ba95fc, 0x5fedb601),
+    TOBN(0x79fb732b, 0x77622510), TOBN(0x18905f76, 0xa53755c6)
+};
+
+const static BN_ULONG def_yG[P256_LIMBS] = {
+    TOBN(0xddf25357, 0xce95560a), TOBN(0x8b4ab8e4, 0xba19e45c),
+    TOBN(0xd2e88688, 0xdd21f325), TOBN(0x8571ff18, 0x25885d85)
+};
+
+/*
+ * ecp_nistz256_is_affine_G returns one if |generator| is the standard, P-256
+ * generator.
+ */
+static int ecp_nistz256_is_affine_G(const EC_POINT *generator)
+{
+    return (bn_get_top(generator->X) == P256_LIMBS) &&
+        (bn_get_top(generator->Y) == P256_LIMBS) &&
+        is_equal(bn_get_words(generator->X), def_xG) &&
+        is_equal(bn_get_words(generator->Y), def_yG) &&
+        is_one(generator->Z);
+}
+
+__owur static int ecp_nistz256_mult_precompute(EC_GROUP *group, BN_CTX *ctx)
+{
+    /*
+     * We precompute a table for a Booth encoded exponent (wNAF) based
+     * computation. Each table holds 64 values for safe access, with an
+     * implicit value of infinity at index zero. We use window of size 7, and
+     * therefore require ceil(256/7) = 37 tables.
+     */
+    const BIGNUM *order;
+    EC_POINT *P = NULL, *T = NULL;
+    const EC_POINT *generator;
+    NISTZ256_PRE_COMP *pre_comp;
+    BN_CTX *new_ctx = NULL;
+    int i, j, k, ret = 0;
+    size_t w;
+
+    PRECOMP256_ROW *preComputedTable = NULL;
+    unsigned char *precomp_storage = NULL;
+
+    /* if there is an old NISTZ256_PRE_COMP object, throw it away */
+    EC_pre_comp_free(group);
+    generator = EC_GROUP_get0_generator(group);
+    if (generator == NULL) {
+        ECerr(EC_F_ECP_NISTZ256_MULT_PRECOMPUTE, EC_R_UNDEFINED_GENERATOR);
+        return 0;
+    }
+
+    if (ecp_nistz256_is_affine_G(generator)) {
+        /*
+         * No need to calculate tables for the standard generator because we
+         * have them statically.
+         */
+        return 1;
+    }
+
+    if ((pre_comp = ecp_nistz256_pre_comp_new(group)) == NULL)
+        return 0;
+
+    if (ctx == NULL) {
+        ctx = new_ctx = BN_CTX_new();
+        if (ctx == NULL)
+            goto err;
+    }
+
+    BN_CTX_start(ctx);
+
+    order = EC_GROUP_get0_order(group);
+    if (order == NULL)
+        goto err;
+
+    if (BN_is_zero(order)) {
+        ECerr(EC_F_ECP_NISTZ256_MULT_PRECOMPUTE, EC_R_UNKNOWN_ORDER);
+        goto err;
+    }
+
+    w = 7;
+
+    if ((precomp_storage =
+         OPENSSL_malloc(37 * 64 * sizeof(P256_POINT_AFFINE) + 64)) == NULL) {
+        ECerr(EC_F_ECP_NISTZ256_MULT_PRECOMPUTE, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+
+    preComputedTable = (void *)ALIGNPTR(precomp_storage, 64);
+
+    P = EC_POINT_new(group);
+    T = EC_POINT_new(group);
+    if (P == NULL || T == NULL)
+        goto err;
+
+    /*
+     * The zero entry is implicitly infinity, and we skip it, storing other
+     * values with -1 offset.
+     */
+    if (!EC_POINT_copy(T, generator))
+        goto err;
+
+    for (k = 0; k < 64; k++) {
+        if (!EC_POINT_copy(P, T))
+            goto err;
+        for (j = 0; j < 37; j++) {
+            P256_POINT_AFFINE temp;
+            /*
+             * It would be faster to use EC_POINTs_make_affine and
+             * make multiple points affine at the same time.
+             */
+            if (!EC_POINT_make_affine(group, P, ctx))
+                goto err;
+            if (!ecp_nistz256_bignum_to_field_elem(temp.X, P->X) ||
+                !ecp_nistz256_bignum_to_field_elem(temp.Y, P->Y)) {
+                ECerr(EC_F_ECP_NISTZ256_MULT_PRECOMPUTE,
+                      EC_R_COORDINATES_OUT_OF_RANGE);
+                goto err;
+            }
+            ecp_nistz256_scatter_w7(preComputedTable[j], &temp, k);
+            for (i = 0; i < 7; i++) {
+                if (!EC_POINT_dbl(group, P, P, ctx))
+                    goto err;
+            }
+        }
+        if (!EC_POINT_add(group, T, T, generator, ctx))
+            goto err;
+    }
+
+    pre_comp->group = group;
+    pre_comp->w = w;
+    pre_comp->precomp = preComputedTable;
+    pre_comp->precomp_storage = precomp_storage;
+    precomp_storage = NULL;
+    SETPRECOMP(group, nistz256, pre_comp);
+    pre_comp = NULL;
+    ret = 1;
+
+ err:
+    if (ctx != NULL)
+        BN_CTX_end(ctx);
+    BN_CTX_free(new_ctx);
+
+    EC_nistz256_pre_comp_free(pre_comp);
+    OPENSSL_free(precomp_storage);
+    EC_POINT_free(P);
+    EC_POINT_free(T);
+    return ret;
+}
+
+/*
+ * Note that by default ECP_NISTZ256_AVX2 is undefined. While it's great
+ * code processing 4 points in parallel, corresponding serial operation
+ * is several times slower, because it uses 29x29=58-bit multiplication
+ * as opposite to 64x64=128-bit in integer-only scalar case. As result
+ * it doesn't provide *significant* performance improvement. Note that
+ * just defining ECP_NISTZ256_AVX2 is not sufficient to make it work,
+ * you'd need to compile even asm/ecp_nistz256-avx.pl module.
+ */
+#if defined(ECP_NISTZ256_AVX2)
+# if !(defined(__x86_64) || defined(__x86_64__) || \
+       defined(_M_AMD64) || defined(_MX64)) || \
+     !(defined(__GNUC__) || defined(_MSC_VER)) /* this is for ALIGN32 */
+#  undef ECP_NISTZ256_AVX2
+# else
+/* Constant time access, loading four values, from four consecutive tables */
+void ecp_nistz256_avx2_multi_gather_w7(void *result, const void *in,
+                                       int index0, int index1, int index2,
+                                       int index3);
+void ecp_nistz256_avx2_transpose_convert(void *RESULTx4, const void *in);
+void ecp_nistz256_avx2_convert_transpose_back(void *result, const void *Ax4);
+void ecp_nistz256_avx2_point_add_affine_x4(void *RESULTx4, const void *Ax4,
+                                           const void *Bx4);
+void ecp_nistz256_avx2_point_add_affines_x4(void *RESULTx4, const void *Ax4,
+                                            const void *Bx4);
+void ecp_nistz256_avx2_to_mont(void *RESULTx4, const void *Ax4);
+void ecp_nistz256_avx2_from_mont(void *RESULTx4, const void *Ax4);
+void ecp_nistz256_avx2_set1(void *RESULTx4);
+int ecp_nistz_avx2_eligible(void);
+
+static void booth_recode_w7(unsigned char *sign,
+                            unsigned char *digit, unsigned char in)
+{
+    unsigned char s, d;
+
+    s = ~((in >> 7) - 1);
+    d = (1 << 8) - in - 1;
+    d = (d & s) | (in & ~s);
+    d = (d >> 1) + (d & 1);
+
+    *sign = s & 1;
+    *digit = d;
+}
+
+/*
+ * ecp_nistz256_avx2_mul_g performs multiplication by G, using only the
+ * precomputed table. It does 4 affine point additions in parallel,
+ * significantly speeding up point multiplication for a fixed value.
+ */
+static void ecp_nistz256_avx2_mul_g(P256_POINT *r,
+                                    unsigned char p_str[33],
+                                    const P256_POINT_AFFINE(*preComputedTable)[64])
+{
+    const unsigned int window_size = 7;
+    const unsigned int mask = (1 << (window_size + 1)) - 1;
+    unsigned int wvalue;
+    /* Using 4 windows at a time */
+    unsigned char sign0, digit0;
+    unsigned char sign1, digit1;
+    unsigned char sign2, digit2;
+    unsigned char sign3, digit3;
+    unsigned int idx = 0;
+    BN_ULONG tmp[P256_LIMBS];
+    int i;
+
+    ALIGN32 BN_ULONG aX4[4 * 9 * 3] = { 0 };
+    ALIGN32 BN_ULONG bX4[4 * 9 * 2] = { 0 };
+    ALIGN32 P256_POINT_AFFINE point_arr[4];
+    ALIGN32 P256_POINT res_point_arr[4];
+
+    /* Initial four windows */
+    wvalue = *((u16 *) & p_str[0]);
+    wvalue = (wvalue << 1) & mask;
+    idx += window_size;
+    booth_recode_w7(&sign0, &digit0, wvalue);
+    wvalue = *((u16 *) & p_str[(idx - 1) / 8]);
+    wvalue = (wvalue >> ((idx - 1) % 8)) & mask;
+    idx += window_size;
+    booth_recode_w7(&sign1, &digit1, wvalue);
+    wvalue = *((u16 *) & p_str[(idx - 1) / 8]);
+    wvalue = (wvalue >> ((idx - 1) % 8)) & mask;
+    idx += window_size;
+    booth_recode_w7(&sign2, &digit2, wvalue);
+    wvalue = *((u16 *) & p_str[(idx - 1) / 8]);
+    wvalue = (wvalue >> ((idx - 1) % 8)) & mask;
+    idx += window_size;
+    booth_recode_w7(&sign3, &digit3, wvalue);
+
+    ecp_nistz256_avx2_multi_gather_w7(point_arr, preComputedTable[0],
+                                      digit0, digit1, digit2, digit3);
+
+    ecp_nistz256_neg(tmp, point_arr[0].Y);
+    copy_conditional(point_arr[0].Y, tmp, sign0);
+    ecp_nistz256_neg(tmp, point_arr[1].Y);
+    copy_conditional(point_arr[1].Y, tmp, sign1);
+    ecp_nistz256_neg(tmp, point_arr[2].Y);
+    copy_conditional(point_arr[2].Y, tmp, sign2);
+    ecp_nistz256_neg(tmp, point_arr[3].Y);
+    copy_conditional(point_arr[3].Y, tmp, sign3);
+
+    ecp_nistz256_avx2_transpose_convert(aX4, point_arr);
+    ecp_nistz256_avx2_to_mont(aX4, aX4);
+    ecp_nistz256_avx2_to_mont(&aX4[4 * 9], &aX4[4 * 9]);
+    ecp_nistz256_avx2_set1(&aX4[4 * 9 * 2]);
+
+    wvalue = *((u16 *) & p_str[(idx - 1) / 8]);
+    wvalue = (wvalue >> ((idx - 1) % 8)) & mask;
+    idx += window_size;
+    booth_recode_w7(&sign0, &digit0, wvalue);
+    wvalue = *((u16 *) & p_str[(idx - 1) / 8]);
+    wvalue = (wvalue >> ((idx - 1) % 8)) & mask;
+    idx += window_size;
+    booth_recode_w7(&sign1, &digit1, wvalue);
+    wvalue = *((u16 *) & p_str[(idx - 1) / 8]);
+    wvalue = (wvalue >> ((idx - 1) % 8)) & mask;
+    idx += window_size;
+    booth_recode_w7(&sign2, &digit2, wvalue);
+    wvalue = *((u16 *) & p_str[(idx - 1) / 8]);
+    wvalue = (wvalue >> ((idx - 1) % 8)) & mask;
+    idx += window_size;
+    booth_recode_w7(&sign3, &digit3, wvalue);
+
+    ecp_nistz256_avx2_multi_gather_w7(point_arr, preComputedTable[4 * 1],
+                                      digit0, digit1, digit2, digit3);
+
+    ecp_nistz256_neg(tmp, point_arr[0].Y);
+    copy_conditional(point_arr[0].Y, tmp, sign0);
+    ecp_nistz256_neg(tmp, point_arr[1].Y);
+    copy_conditional(point_arr[1].Y, tmp, sign1);
+    ecp_nistz256_neg(tmp, point_arr[2].Y);
+    copy_conditional(point_arr[2].Y, tmp, sign2);
+    ecp_nistz256_neg(tmp, point_arr[3].Y);
+    copy_conditional(point_arr[3].Y, tmp, sign3);
+
+    ecp_nistz256_avx2_transpose_convert(bX4, point_arr);
+    ecp_nistz256_avx2_to_mont(bX4, bX4);
+    ecp_nistz256_avx2_to_mont(&bX4[4 * 9], &bX4[4 * 9]);
+    /* Optimized when both inputs are affine */
+    ecp_nistz256_avx2_point_add_affines_x4(aX4, aX4, bX4);
+
+    for (i = 2; i < 9; i++) {
+        wvalue = *((u16 *) & p_str[(idx - 1) / 8]);
+        wvalue = (wvalue >> ((idx - 1) % 8)) & mask;
+        idx += window_size;
+        booth_recode_w7(&sign0, &digit0, wvalue);
+        wvalue = *((u16 *) & p_str[(idx - 1) / 8]);
+        wvalue = (wvalue >> ((idx - 1) % 8)) & mask;
+        idx += window_size;
+        booth_recode_w7(&sign1, &digit1, wvalue);
+        wvalue = *((u16 *) & p_str[(idx - 1) / 8]);
+        wvalue = (wvalue >> ((idx - 1) % 8)) & mask;
+        idx += window_size;
+        booth_recode_w7(&sign2, &digit2, wvalue);
+        wvalue = *((u16 *) & p_str[(idx - 1) / 8]);
+        wvalue = (wvalue >> ((idx - 1) % 8)) & mask;
+        idx += window_size;
+        booth_recode_w7(&sign3, &digit3, wvalue);
+
+        ecp_nistz256_avx2_multi_gather_w7(point_arr,
+                                          preComputedTable[4 * i],
+                                          digit0, digit1, digit2, digit3);
+
+        ecp_nistz256_neg(tmp, point_arr[0].Y);
+        copy_conditional(point_arr[0].Y, tmp, sign0);
+        ecp_nistz256_neg(tmp, point_arr[1].Y);
+        copy_conditional(point_arr[1].Y, tmp, sign1);
+        ecp_nistz256_neg(tmp, point_arr[2].Y);
+        copy_conditional(point_arr[2].Y, tmp, sign2);
+        ecp_nistz256_neg(tmp, point_arr[3].Y);
+        copy_conditional(point_arr[3].Y, tmp, sign3);
+
+        ecp_nistz256_avx2_transpose_convert(bX4, point_arr);
+        ecp_nistz256_avx2_to_mont(bX4, bX4);
+        ecp_nistz256_avx2_to_mont(&bX4[4 * 9], &bX4[4 * 9]);
+
+        ecp_nistz256_avx2_point_add_affine_x4(aX4, aX4, bX4);
+    }
+
+    ecp_nistz256_avx2_from_mont(&aX4[4 * 9 * 0], &aX4[4 * 9 * 0]);
+    ecp_nistz256_avx2_from_mont(&aX4[4 * 9 * 1], &aX4[4 * 9 * 1]);
+    ecp_nistz256_avx2_from_mont(&aX4[4 * 9 * 2], &aX4[4 * 9 * 2]);
+
+    ecp_nistz256_avx2_convert_transpose_back(res_point_arr, aX4);
+    /* Last window is performed serially */
+    wvalue = *((u16 *) & p_str[(idx - 1) / 8]);
+    wvalue = (wvalue >> ((idx - 1) % 8)) & mask;
+    booth_recode_w7(&sign0, &digit0, wvalue);
+    ecp_nistz256_gather_w7((P256_POINT_AFFINE *)r,
+                           preComputedTable[36], digit0);
+    ecp_nistz256_neg(tmp, r->Y);
+    copy_conditional(r->Y, tmp, sign0);
+    memcpy(r->Z, ONE, sizeof(ONE));
+    /* Sum the four windows */
+    ecp_nistz256_point_add(r, r, &res_point_arr[0]);
+    ecp_nistz256_point_add(r, r, &res_point_arr[1]);
+    ecp_nistz256_point_add(r, r, &res_point_arr[2]);
+    ecp_nistz256_point_add(r, r, &res_point_arr[3]);
+}
+# endif
+#endif
+
+__owur static int ecp_nistz256_set_from_affine(EC_POINT *out, const EC_GROUP *group,
+                                               const P256_POINT_AFFINE *in,
+                                               BN_CTX *ctx)
+{
+    BIGNUM *x, *y;
+    BN_ULONG d_x[P256_LIMBS], d_y[P256_LIMBS];
+    int ret = 0;
+
+    x = BN_new();
+    if (x == NULL)
+        return 0;
+    y = BN_new();
+    if (y == NULL) {
+        BN_free(x);
+        return 0;
+    }
+    memcpy(d_x, in->X, sizeof(d_x));
+    bn_set_static_words(x, d_x, P256_LIMBS);
+
+    memcpy(d_y, in->Y, sizeof(d_y));
+    bn_set_static_words(y, d_y, P256_LIMBS);
+
+    ret = EC_POINT_set_affine_coordinates_GFp(group, out, x, y, ctx);
+
+    BN_free(x);
+    BN_free(y);
+
+    return ret;
+}
+
+/* r = scalar*G + sum(scalars[i]*points[i]) */
+__owur static int ecp_nistz256_points_mul(const EC_GROUP *group,
+                                          EC_POINT *r,
+                                          const BIGNUM *scalar,
+                                          size_t num,
+                                          const EC_POINT *points[],
+                                          const BIGNUM *scalars[], BN_CTX *ctx)
+{
+    int i = 0, ret = 0, no_precomp_for_generator = 0, p_is_infinity = 0;
+    size_t j;
+    unsigned char p_str[33] = { 0 };
+    const PRECOMP256_ROW *preComputedTable = NULL;
+    const NISTZ256_PRE_COMP *pre_comp = NULL;
+    const EC_POINT *generator = NULL;
+    BN_CTX *new_ctx = NULL;
+    const BIGNUM **new_scalars = NULL;
+    const EC_POINT **new_points = NULL;
+    unsigned int idx = 0;
+    const unsigned int window_size = 7;
+    const unsigned int mask = (1 << (window_size + 1)) - 1;
+    unsigned int wvalue;
+    ALIGN32 union {
+        P256_POINT p;
+        P256_POINT_AFFINE a;
+    } t, p;
+    BIGNUM *tmp_scalar;
+
+    if ((num + 1) == 0 || (num + 1) > OPENSSL_MALLOC_MAX_NELEMS(void *)) {
+        ECerr(EC_F_ECP_NISTZ256_POINTS_MUL, ERR_R_MALLOC_FAILURE);
+        return 0;
+    }
+
+    if (group->meth != r->meth) {
+        ECerr(EC_F_ECP_NISTZ256_POINTS_MUL, EC_R_INCOMPATIBLE_OBJECTS);
+        return 0;
+    }
+
+    if ((scalar == NULL) && (num == 0))
+        return EC_POINT_set_to_infinity(group, r);
+
+    for (j = 0; j < num; j++) {
+        if (group->meth != points[j]->meth) {
+            ECerr(EC_F_ECP_NISTZ256_POINTS_MUL, EC_R_INCOMPATIBLE_OBJECTS);
+            return 0;
+        }
+    }
+
+    if (ctx == NULL) {
+        ctx = new_ctx = BN_CTX_new();
+        if (ctx == NULL)
+            goto err;
+    }
+
+    BN_CTX_start(ctx);
+
+    if (scalar) {
+        generator = EC_GROUP_get0_generator(group);
+        if (generator == NULL) {
+            ECerr(EC_F_ECP_NISTZ256_POINTS_MUL, EC_R_UNDEFINED_GENERATOR);
+            goto err;
+        }
+
+        /* look if we can use precomputed multiples of generator */
+        pre_comp = group->pre_comp.nistz256;
+
+        if (pre_comp) {
+            /*
+             * If there is a precomputed table for the generator, check that
+             * it was generated with the same generator.
+             */
+            EC_POINT *pre_comp_generator = EC_POINT_new(group);
+            if (pre_comp_generator == NULL)
+                goto err;
+
+            if (!ecp_nistz256_set_from_affine(pre_comp_generator,
+                                              group, pre_comp->precomp[0],
+                                              ctx)) {
+                EC_POINT_free(pre_comp_generator);
+                goto err;
+            }
+
+            if (0 == EC_POINT_cmp(group, generator, pre_comp_generator, ctx))
+                preComputedTable = (const PRECOMP256_ROW *)pre_comp->precomp;
+
+            EC_POINT_free(pre_comp_generator);
+        }
+
+        if (preComputedTable == NULL && ecp_nistz256_is_affine_G(generator)) {
+            /*
+             * If there is no precomputed data, but the generator is the
+             * default, a hardcoded table of precomputed data is used. This
+             * is because applications, such as Apache, do not use
+             * EC_KEY_precompute_mult.
+             */
+            preComputedTable = ecp_nistz256_precomputed;
+        }
+
+        if (preComputedTable) {
+            if ((BN_num_bits(scalar) > 256)
+                || BN_is_negative(scalar)) {
+                if ((tmp_scalar = BN_CTX_get(ctx)) == NULL)
+                    goto err;
+
+                if (!BN_nnmod(tmp_scalar, scalar, group->order, ctx)) {
+                    ECerr(EC_F_ECP_NISTZ256_POINTS_MUL, ERR_R_BN_LIB);
+                    goto err;
+                }
+                scalar = tmp_scalar;
+            }
+
+            for (i = 0; i < bn_get_top(scalar) * BN_BYTES; i += BN_BYTES) {
+                BN_ULONG d = bn_get_words(scalar)[i / BN_BYTES];
+
+                p_str[i + 0] = (unsigned char)d;
+                p_str[i + 1] = (unsigned char)(d >> 8);
+                p_str[i + 2] = (unsigned char)(d >> 16);
+                p_str[i + 3] = (unsigned char)(d >>= 24);
+                if (BN_BYTES == 8) {
+                    d >>= 8;
+                    p_str[i + 4] = (unsigned char)d;
+                    p_str[i + 5] = (unsigned char)(d >> 8);
+                    p_str[i + 6] = (unsigned char)(d >> 16);
+                    p_str[i + 7] = (unsigned char)(d >> 24);
+                }
+            }
+
+            for (; i < 33; i++)
+                p_str[i] = 0;
+
+#if defined(ECP_NISTZ256_AVX2)
+            if (ecp_nistz_avx2_eligible()) {
+                ecp_nistz256_avx2_mul_g(&p.p, p_str, preComputedTable);
+            } else
+#endif
+            {
+                BN_ULONG infty;
+
+                /* First window */
+                wvalue = (p_str[0] << 1) & mask;
+                idx += window_size;
+
+                wvalue = _booth_recode_w7(wvalue);
+
+                ecp_nistz256_gather_w7(&p.a, preComputedTable[0],
+                                       wvalue >> 1);
+
+                ecp_nistz256_neg(p.p.Z, p.p.Y);
+                copy_conditional(p.p.Y, p.p.Z, wvalue & 1);
+
+                /*
+                 * Since affine infinity is encoded as (0,0) and
+                 * Jacobian ias (,,0), we need to harmonize them
+                 * by assigning "one" or zero to Z.
+                 */
+                infty = (p.p.X[0] | p.p.X[1] | p.p.X[2] | p.p.X[3] |
+                         p.p.Y[0] | p.p.Y[1] | p.p.Y[2] | p.p.Y[3]);
+                if (P256_LIMBS == 8)
+                    infty |= (p.p.X[4] | p.p.X[5] | p.p.X[6] | p.p.X[7] |
+                              p.p.Y[4] | p.p.Y[5] | p.p.Y[6] | p.p.Y[7]);
+
+                infty = 0 - is_zero(infty);
+                infty = ~infty;
+
+                p.p.Z[0] = ONE[0] & infty;
+                p.p.Z[1] = ONE[1] & infty;
+                p.p.Z[2] = ONE[2] & infty;
+                p.p.Z[3] = ONE[3] & infty;
+                if (P256_LIMBS == 8) {
+                    p.p.Z[4] = ONE[4] & infty;
+                    p.p.Z[5] = ONE[5] & infty;
+                    p.p.Z[6] = ONE[6] & infty;
+                    p.p.Z[7] = ONE[7] & infty;
+                }
+
+                for (i = 1; i < 37; i++) {
+                    unsigned int off = (idx - 1) / 8;
+                    wvalue = p_str[off] | p_str[off + 1] << 8;
+                    wvalue = (wvalue >> ((idx - 1) % 8)) & mask;
+                    idx += window_size;
+
+                    wvalue = _booth_recode_w7(wvalue);
+
+                    ecp_nistz256_gather_w7(&t.a,
+                                           preComputedTable[i], wvalue >> 1);
+
+                    ecp_nistz256_neg(t.p.Z, t.a.Y);
+                    copy_conditional(t.a.Y, t.p.Z, wvalue & 1);
+
+                    ecp_nistz256_point_add_affine(&p.p, &p.p, &t.a);
+                }
+            }
+        } else {
+            p_is_infinity = 1;
+            no_precomp_for_generator = 1;
+        }
+    } else
+        p_is_infinity = 1;
+
+    if (no_precomp_for_generator) {
+        /*
+         * Without a precomputed table for the generator, it has to be
+         * handled like a normal point.
+         */
+        new_scalars = OPENSSL_malloc((num + 1) * sizeof(BIGNUM *));
+        if (new_scalars == NULL) {
+            ECerr(EC_F_ECP_NISTZ256_POINTS_MUL, ERR_R_MALLOC_FAILURE);
+            goto err;
+        }
+
+        new_points = OPENSSL_malloc((num + 1) * sizeof(EC_POINT *));
+        if (new_points == NULL) {
+            ECerr(EC_F_ECP_NISTZ256_POINTS_MUL, ERR_R_MALLOC_FAILURE);
+            goto err;
+        }
+
+        memcpy(new_scalars, scalars, num * sizeof(BIGNUM *));
+        new_scalars[num] = scalar;
+        memcpy(new_points, points, num * sizeof(EC_POINT *));
+        new_points[num] = generator;
+
+        scalars = new_scalars;
+        points = new_points;
+        num++;
+    }
+
+    if (num) {
+        P256_POINT *out = &t.p;
+        if (p_is_infinity)
+            out = &p.p;
+
+        if (!ecp_nistz256_windowed_mul(group, out, scalars, points, num, ctx))
+            goto err;
+
+        if (!p_is_infinity)
+            ecp_nistz256_point_add(&p.p, &p.p, out);
+    }
+
+    /* Not constant-time, but we're only operating on the public output. */
+    if (!bn_set_words(r->X, p.p.X, P256_LIMBS) ||
+        !bn_set_words(r->Y, p.p.Y, P256_LIMBS) ||
+        !bn_set_words(r->Z, p.p.Z, P256_LIMBS)) {
+        goto err;
+    }
+    r->Z_is_one = is_one(r->Z) & 1;
+
+    ret = 1;
+
+err:
+    if (ctx)
+        BN_CTX_end(ctx);
+    BN_CTX_free(new_ctx);
+    OPENSSL_free(new_points);
+    OPENSSL_free(new_scalars);
+    return ret;
+}
+
+__owur static int ecp_nistz256_get_affine(const EC_GROUP *group,
+                                          const EC_POINT *point,
+                                          BIGNUM *x, BIGNUM *y, BN_CTX *ctx)
+{
+    BN_ULONG z_inv2[P256_LIMBS];
+    BN_ULONG z_inv3[P256_LIMBS];
+    BN_ULONG x_aff[P256_LIMBS];
+    BN_ULONG y_aff[P256_LIMBS];
+    BN_ULONG point_x[P256_LIMBS], point_y[P256_LIMBS], point_z[P256_LIMBS];
+    BN_ULONG x_ret[P256_LIMBS], y_ret[P256_LIMBS];
+
+    if (EC_POINT_is_at_infinity(group, point)) {
+        ECerr(EC_F_ECP_NISTZ256_GET_AFFINE, EC_R_POINT_AT_INFINITY);
+        return 0;
+    }
+
+    if (!ecp_nistz256_bignum_to_field_elem(point_x, point->X) ||
+        !ecp_nistz256_bignum_to_field_elem(point_y, point->Y) ||
+        !ecp_nistz256_bignum_to_field_elem(point_z, point->Z)) {
+        ECerr(EC_F_ECP_NISTZ256_GET_AFFINE, EC_R_COORDINATES_OUT_OF_RANGE);
+        return 0;
+    }
+
+    ecp_nistz256_mod_inverse(z_inv3, point_z);
+    ecp_nistz256_sqr_mont(z_inv2, z_inv3);
+    ecp_nistz256_mul_mont(x_aff, z_inv2, point_x);
+
+    if (x != NULL) {
+        ecp_nistz256_from_mont(x_ret, x_aff);
+        if (!bn_set_words(x, x_ret, P256_LIMBS))
+            return 0;
+    }
+
+    if (y != NULL) {
+        ecp_nistz256_mul_mont(z_inv3, z_inv3, z_inv2);
+        ecp_nistz256_mul_mont(y_aff, z_inv3, point_y);
+        ecp_nistz256_from_mont(y_ret, y_aff);
+        if (!bn_set_words(y, y_ret, P256_LIMBS))
+            return 0;
+    }
+
+    return 1;
+}
+
+static NISTZ256_PRE_COMP *ecp_nistz256_pre_comp_new(const EC_GROUP *group)
+{
+    NISTZ256_PRE_COMP *ret = NULL;
+
+    if (!group)
+        return NULL;
+
+    ret = OPENSSL_zalloc(sizeof(*ret));
+
+    if (ret == NULL) {
+        ECerr(EC_F_ECP_NISTZ256_PRE_COMP_NEW, ERR_R_MALLOC_FAILURE);
+        return ret;
+    }
+
+    ret->group = group;
+    ret->w = 6;                 /* default */
+    ret->references = 1;
+
+    ret->lock = CRYPTO_THREAD_lock_new();
+    if (ret->lock == NULL) {
+        ECerr(EC_F_ECP_NISTZ256_PRE_COMP_NEW, ERR_R_MALLOC_FAILURE);
+        OPENSSL_free(ret);
+        return NULL;
+    }
+    return ret;
+}
+
+NISTZ256_PRE_COMP *EC_nistz256_pre_comp_dup(NISTZ256_PRE_COMP *p)
+{
+    int i;
+    if (p != NULL)
+        CRYPTO_atomic_add(&p->references, 1, &i, p->lock);
+    return p;
+}
+
+void EC_nistz256_pre_comp_free(NISTZ256_PRE_COMP *pre)
+{
+    int i;
+
+    if (pre == NULL)
+        return;
+
+    CRYPTO_atomic_add(&pre->references, -1, &i, pre->lock);
+    REF_PRINT_COUNT("EC_nistz256", x);
+    if (i > 0)
+        return;
+    REF_ASSERT_ISNT(i < 0);
+
+    OPENSSL_free(pre->precomp_storage);
+    CRYPTO_THREAD_lock_free(pre->lock);
+    OPENSSL_free(pre);
+}
+
+
+static int ecp_nistz256_window_have_precompute_mult(const EC_GROUP *group)
+{
+    /* There is a hard-coded table for the default generator. */
+    const EC_POINT *generator = EC_GROUP_get0_generator(group);
+
+    if (generator != NULL && ecp_nistz256_is_affine_G(generator)) {
+        /* There is a hard-coded table for the default generator. */
+        return 1;
+    }
+
+    return HAVEPRECOMP(group, nistz256);
+}
+
+const EC_METHOD *EC_GFp_nistz256_method(void)
+{
+    static const EC_METHOD ret = {
+        EC_FLAGS_DEFAULT_OCT,
+        NID_X9_62_prime_field,
+        ec_GFp_mont_group_init,
+        ec_GFp_mont_group_finish,
+        ec_GFp_mont_group_clear_finish,
+        ec_GFp_mont_group_copy,
+        ec_GFp_mont_group_set_curve,
+        ec_GFp_simple_group_get_curve,
+        ec_GFp_simple_group_get_degree,
+        ec_group_simple_order_bits,
+        ec_GFp_simple_group_check_discriminant,
+        ec_GFp_simple_point_init,
+        ec_GFp_simple_point_finish,
+        ec_GFp_simple_point_clear_finish,
+        ec_GFp_simple_point_copy,
+        ec_GFp_simple_point_set_to_infinity,
+        ec_GFp_simple_set_Jprojective_coordinates_GFp,
+        ec_GFp_simple_get_Jprojective_coordinates_GFp,
+        ec_GFp_simple_point_set_affine_coordinates,
+        ecp_nistz256_get_affine,
+        0, 0, 0,
+        ec_GFp_simple_add,
+        ec_GFp_simple_dbl,
+        ec_GFp_simple_invert,
+        ec_GFp_simple_is_at_infinity,
+        ec_GFp_simple_is_on_curve,
+        ec_GFp_simple_cmp,
+        ec_GFp_simple_make_affine,
+        ec_GFp_simple_points_make_affine,
+        ecp_nistz256_points_mul,                    /* mul */
+        ecp_nistz256_mult_precompute,               /* precompute_mult */
+        ecp_nistz256_window_have_precompute_mult,   /* have_precompute_mult */
+        ec_GFp_mont_field_mul,
+        ec_GFp_mont_field_sqr,
+        0,                                          /* field_div */
+        ec_GFp_mont_field_encode,
+        ec_GFp_mont_field_decode,
+        ec_GFp_mont_field_set_to_one,
+        ec_key_simple_priv2oct,
+        ec_key_simple_oct2priv,
+        0, /* set private */
+        ec_key_simple_generate_key,
+        ec_key_simple_check_key,
+        ec_key_simple_generate_public_key,
+        0, /* keycopy */
+        0, /* keyfinish */
+        ecdh_simple_compute_key
+    };
+
+    return &ret;
+}
diff --git a/crypto/ec/ecp_nistz256_table.c b/crypto/ec/ecp_nistz256_table.c
new file mode 100644
index 000000000..3f5625c6c
--- /dev/null
+++ b/crypto/ec/ecp_nistz256_table.c
@@ -0,0 +1,9542 @@
+/*
+ * Copyright 2014-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * This is the precomputed constant time access table for the code in
+ * ecp_montp256.c, for the default generator. The table consists of 37
+ * subtables, each subtable contains 64 affine points. The affine points are
+ * encoded as eight uint64's, four for the x coordinate and four for the y.
+ * Both values are in little-endian order. There are 37 tables because a
+ * signed, 6-bit wNAF form of the scalar is used and ceil(256/(6 + 1)) = 37.
+ * Within each table there are 64 values because the 6-bit wNAF value can
+ * take 64 values, ignoring the sign bit, which is implemented by performing
+ * a negation of the affine point when required. We would like to align it
+ * to 2MB in order to increase the chances of using a large page but that
+ * appears to lead to invalid ELF files being produced.
+ */
+
+#if defined(__GNUC__)
+__attribute((aligned(4096)))
+#elif defined(_MSC_VER)
+__declspec(align(4096))
+#elif defined(__SUNPRO_C)
+# pragma align 4096(ecp_nistz256_precomputed)
+#endif
+static const BN_ULONG ecp_nistz256_precomputed[37][64 *
+                                                   sizeof(P256_POINT_AFFINE) /
+                                                   sizeof(BN_ULONG)] = {
+    {TOBN(0x79e730d4, 0x18a9143c), TOBN(0x75ba95fc, 0x5fedb601),
+     TOBN(0x79fb732b, 0x77622510), TOBN(0x18905f76, 0xa53755c6),
+     TOBN(0xddf25357, 0xce95560a), TOBN(0x8b4ab8e4, 0xba19e45c),
+     TOBN(0xd2e88688, 0xdd21f325), TOBN(0x8571ff18, 0x25885d85),
+     TOBN(0x850046d4, 0x10ddd64d), TOBN(0xaa6ae3c1, 0xa433827d),
+     TOBN(0x73220503, 0x8d1490d9), TOBN(0xf6bb32e4, 0x3dcf3a3b),
+     TOBN(0x2f3648d3, 0x61bee1a5), TOBN(0x152cd7cb, 0xeb236ff8),
+     TOBN(0x19a8fb0e, 0x92042dbe), TOBN(0x78c57751, 0x0a5b8a3b),
+     TOBN(0xffac3f90, 0x4eebc127), TOBN(0xb027f84a, 0x087d81fb),
+     TOBN(0x66ad77dd, 0x87cbbc98), TOBN(0x26936a3f, 0xb6ff747e),
+     TOBN(0xb04c5c1f, 0xc983a7eb), TOBN(0x583e47ad, 0x0861fe1a),
+     TOBN(0x78820831, 0x1a2ee98e), TOBN(0xd5f06a29, 0xe587cc07),
+     TOBN(0x74b0b50d, 0x46918dcc), TOBN(0x4650a6ed, 0xc623c173),
+     TOBN(0x0cdaacac, 0xe8100af2), TOBN(0x577362f5, 0x41b0176b),
+     TOBN(0x2d96f24c, 0xe4cbaba6), TOBN(0x17628471, 0xfad6f447),
+     TOBN(0x6b6c36de, 0xe5ddd22e), TOBN(0x84b14c39, 0x4c5ab863),
+     TOBN(0xbe1b8aae, 0xc45c61f5), TOBN(0x90ec649a, 0x94b9537d),
+     TOBN(0x941cb5aa, 0xd076c20c), TOBN(0xc9079605, 0x890523c8),
+     TOBN(0xeb309b4a, 0xe7ba4f10), TOBN(0x73c568ef, 0xe5eb882b),
+     TOBN(0x3540a987, 0x7e7a1f68), TOBN(0x73a076bb, 0x2dd1e916),
+     TOBN(0x40394737, 0x3e77664a), TOBN(0x55ae744f, 0x346cee3e),
+     TOBN(0xd50a961a, 0x5b17a3ad), TOBN(0x13074b59, 0x54213673),
+     TOBN(0x93d36220, 0xd377e44b), TOBN(0x299c2b53, 0xadff14b5),
+     TOBN(0xf424d44c, 0xef639f11), TOBN(0xa4c9916d, 0x4a07f75f),
+     TOBN(0x0746354e, 0xa0173b4f), TOBN(0x2bd20213, 0xd23c00f7),
+     TOBN(0xf43eaab5, 0x0c23bb08), TOBN(0x13ba5119, 0xc3123e03),
+     TOBN(0x2847d030, 0x3f5b9d4d), TOBN(0x6742f2f2, 0x5da67bdd),
+     TOBN(0xef933bdc, 0x77c94195), TOBN(0xeaedd915, 0x6e240867),
+     TOBN(0x27f14cd1, 0x9499a78f), TOBN(0x462ab5c5, 0x6f9b3455),
+     TOBN(0x8f90f02a, 0xf02cfc6b), TOBN(0xb763891e, 0xb265230d),
+     TOBN(0xf59da3a9, 0x532d4977), TOBN(0x21e3327d, 0xcf9eba15),
+     TOBN(0x123c7b84, 0xbe60bbf0), TOBN(0x56ec12f2, 0x7706df76),
+     TOBN(0x75c96e8f, 0x264e20e8), TOBN(0xabe6bfed, 0x59a7a841),
+     TOBN(0x2cc09c04, 0x44c8eb00), TOBN(0xe05b3080, 0xf0c4e16b),
+     TOBN(0x1eb7777a, 0xa45f3314), TOBN(0x56af7bed, 0xce5d45e3),
+     TOBN(0x2b6e019a, 0x88b12f1a), TOBN(0x086659cd, 0xfd835f9b),
+     TOBN(0x2c18dbd1, 0x9dc21ec8), TOBN(0x98f9868a, 0x0fcf8139),
+     TOBN(0x737d2cd6, 0x48250b49), TOBN(0xcc61c947, 0x24b3428f),
+     TOBN(0x0c2b4078, 0x80dd9e76), TOBN(0xc43a8991, 0x383fbe08),
+     TOBN(0x5f7d2d65, 0x779be5d2), TOBN(0x78719a54, 0xeb3b4ab5),
+     TOBN(0xea7d260a, 0x6245e404), TOBN(0x9de40795, 0x6e7fdfe0),
+     TOBN(0x1ff3a415, 0x8dac1ab5), TOBN(0x3e7090f1, 0x649c9073),
+     TOBN(0x1a768561, 0x2b944e88), TOBN(0x250f939e, 0xe57f61c8),
+     TOBN(0x0c0daa89, 0x1ead643d), TOBN(0x68930023, 0xe125b88e),
+     TOBN(0x04b71aa7, 0xd2697768), TOBN(0xabdedef5, 0xca345a33),
+     TOBN(0x2409d29d, 0xee37385e), TOBN(0x4ee1df77, 0xcb83e156),
+     TOBN(0x0cac12d9, 0x1cbb5b43), TOBN(0x170ed2f6, 0xca895637),
+     TOBN(0x28228cfa, 0x8ade6d66), TOBN(0x7ff57c95, 0x53238aca),
+     TOBN(0xccc42563, 0x4b2ed709), TOBN(0x0e356769, 0x856fd30d),
+     TOBN(0xbcbcd43f, 0x559e9811), TOBN(0x738477ac, 0x5395b759),
+     TOBN(0x35752b90, 0xc00ee17f), TOBN(0x68748390, 0x742ed2e3),
+     TOBN(0x7cd06422, 0xbd1f5bc1), TOBN(0xfbc08769, 0xc9e7b797),
+     TOBN(0xa242a35b, 0xb0cf664a), TOBN(0x126e48f7, 0x7f9707e3),
+     TOBN(0x1717bf54, 0xc6832660), TOBN(0xfaae7332, 0xfd12c72e),
+     TOBN(0x27b52db7, 0x995d586b), TOBN(0xbe29569e, 0x832237c2),
+     TOBN(0xe8e4193e, 0x2a65e7db), TOBN(0x152706dc, 0x2eaa1bbb),
+     TOBN(0x72bcd8b7, 0xbc60055b), TOBN(0x03cc23ee, 0x56e27e4b),
+     TOBN(0xee337424, 0xe4819370), TOBN(0xe2aa0e43, 0x0ad3da09),
+     TOBN(0x40b8524f, 0x6383c45d), TOBN(0xd7663554, 0x42a41b25),
+     TOBN(0x64efa6de, 0x778a4797), TOBN(0x2042170a, 0x7079adf4),
+     TOBN(0x808b0b65, 0x0bc6fb80), TOBN(0x5882e075, 0x3ffe2e6b),
+     TOBN(0xd5ef2f7c, 0x2c83f549), TOBN(0x54d63c80, 0x9103b723),
+     TOBN(0xf2f11bd6, 0x52a23f9b), TOBN(0x3670c319, 0x4b0b6587),
+     TOBN(0x55c4623b, 0xb1580e9e), TOBN(0x64edf7b2, 0x01efe220),
+     TOBN(0x97091dcb, 0xd53c5c9d), TOBN(0xf17624b6, 0xac0a177b),
+     TOBN(0xb0f13975, 0x2cfe2dff), TOBN(0xc1a35c0a, 0x6c7a574e),
+     TOBN(0x227d3146, 0x93e79987), TOBN(0x0575bf30, 0xe89cb80e),
+     TOBN(0x2f4e247f, 0x0d1883bb), TOBN(0xebd51226, 0x3274c3d0),
+     TOBN(0x5f3e51c8, 0x56ada97a), TOBN(0x4afc964d, 0x8f8b403e),
+     TOBN(0xa6f247ab, 0x412e2979), TOBN(0x675abd1b, 0x6f80ebda),
+     TOBN(0x66a2bd72, 0x5e485a1d), TOBN(0x4b2a5caf, 0x8f4f0b3c),
+     TOBN(0x2626927f, 0x1b847bba), TOBN(0x6c6fc7d9, 0x0502394d),
+     TOBN(0xfea912ba, 0xa5659ae8), TOBN(0x68363aba, 0x25e1a16e),
+     TOBN(0xb8842277, 0x752c41ac), TOBN(0xfe545c28, 0x2897c3fc),
+     TOBN(0x2d36e9e7, 0xdc4c696b), TOBN(0x5806244a, 0xfba977c5),
+     TOBN(0x85665e9b, 0xe39508c1), TOBN(0xf720ee25, 0x6d12597b),
+     TOBN(0x8a979129, 0xd2337a31), TOBN(0x5916868f, 0x0f862bdc),
+     TOBN(0x048099d9, 0x5dd283ba), TOBN(0xe2d1eeb6, 0xfe5bfb4e),
+     TOBN(0x82ef1c41, 0x7884005d), TOBN(0xa2d4ec17, 0xffffcbae),
+     TOBN(0x9161c53f, 0x8aa95e66), TOBN(0x5ee104e1, 0xc5fee0d0),
+     TOBN(0x562e4cec, 0xc135b208), TOBN(0x74e1b265, 0x4783f47d),
+     TOBN(0x6d2a506c, 0x5a3f3b30), TOBN(0xecead9f4, 0xc16762fc),
+     TOBN(0xf29dd4b2, 0xe286e5b9), TOBN(0x1b0fadc0, 0x83bb3c61),
+     TOBN(0x7a75023e, 0x7fac29a4), TOBN(0xc086d5f1, 0xc9477fa3),
+     TOBN(0x0fc61135, 0x2f6f3076), TOBN(0xc99ffa23, 0xe3912a9a),
+     TOBN(0x6a0b0685, 0xd2f8ba3d), TOBN(0xfdc777e8, 0xe93358a4),
+     TOBN(0x94a787bb, 0x35415f04), TOBN(0x640c2d6a, 0x4d23fea4),
+     TOBN(0x9de917da, 0x153a35b5), TOBN(0x793e8d07, 0x5d5cd074),
+     TOBN(0xf4f87653, 0x2de45068), TOBN(0x37c7a7e8, 0x9e2e1f6e),
+     TOBN(0xd0825fa2, 0xa3584069), TOBN(0xaf2cea7c, 0x1727bf42),
+     TOBN(0x0360a4fb, 0x9e4785a9), TOBN(0xe5fda49c, 0x27299f4a),
+     TOBN(0x48068e13, 0x71ac2f71), TOBN(0x83d0687b, 0x9077666f),
+     TOBN(0x6d3883b2, 0x15d02819), TOBN(0x6d0d7550, 0x40dd9a35),
+     TOBN(0x61d7cbf9, 0x1d2b469f), TOBN(0xf97b232f, 0x2efc3115),
+     TOBN(0xa551d750, 0xb24bcbc7), TOBN(0x11ea4949, 0x88a1e356),
+     TOBN(0x7669f031, 0x93cb7501), TOBN(0x595dc55e, 0xca737b8a),
+     TOBN(0xa4a319ac, 0xd837879f), TOBN(0x6fc1b49e, 0xed6b67b0),
+     TOBN(0xe3959933, 0x32f1f3af), TOBN(0x966742eb, 0x65432a2e),
+     TOBN(0x4b8dc9fe, 0xb4966228), TOBN(0x96cc6312, 0x43f43950),
+     TOBN(0x12068859, 0xc9b731ee), TOBN(0x7b948dc3, 0x56f79968),
+     TOBN(0x61e4ad32, 0xed1f8008), TOBN(0xe6c9267a, 0xd8b17538),
+     TOBN(0x1ac7c5eb, 0x857ff6fb), TOBN(0x994baaa8, 0x55f2fb10),
+     TOBN(0x84cf14e1, 0x1d248018), TOBN(0x5a39898b, 0x628ac508),
+     TOBN(0x14fde97b, 0x5fa944f5), TOBN(0xed178030, 0xd12e5ac7),
+     TOBN(0x042c2af4, 0x97e2feb4), TOBN(0xd36a42d7, 0xaebf7313),
+     TOBN(0x49d2c9eb, 0x084ffdd7), TOBN(0x9f8aa54b, 0x2ef7c76a),
+     TOBN(0x9200b7ba, 0x09895e70), TOBN(0x3bd0c66f, 0xddb7fb58),
+     TOBN(0x2d97d108, 0x78eb4cbb), TOBN(0x2d431068, 0xd84bde31),
+     TOBN(0x4b523eb7, 0x172ccd1f), TOBN(0x7323cb28, 0x30a6a892),
+     TOBN(0x97082ec0, 0xcfe153eb), TOBN(0xe97f6b6a, 0xf2aadb97),
+     TOBN(0x1d3d393e, 0xd1a83da1), TOBN(0xa6a7f9c7, 0x804b2a68),
+     TOBN(0x4a688b48, 0x2d0cb71e), TOBN(0xa9b4cc5f, 0x40585278),
+     TOBN(0x5e5db46a, 0xcb66e132), TOBN(0xf1be963a, 0x0d925880),
+     TOBN(0x944a7027, 0x0317b9e2), TOBN(0xe266f959, 0x48603d48),
+     TOBN(0x98db6673, 0x5c208899), TOBN(0x90472447, 0xa2fb18a3),
+     TOBN(0x8a966939, 0x777c619f), TOBN(0x3798142a, 0x2a3be21b),
+     TOBN(0xb4241cb1, 0x3298b343), TOBN(0xa3a14e49, 0xb44f65a1),
+     TOBN(0xc5f4d6cd, 0x3ac77acd), TOBN(0xd0288cb5, 0x52b6fc3c),
+     TOBN(0xd5cc8c2f, 0x1c040abc), TOBN(0xb675511e, 0x06bf9b4a),
+     TOBN(0xd667da37, 0x9b3aa441), TOBN(0x460d45ce, 0x51601f72),
+     TOBN(0xe2f73c69, 0x6755ff89), TOBN(0xdd3cf7e7, 0x473017e6),
+     TOBN(0x8ef5689d, 0x3cf7600d), TOBN(0x948dc4f8, 0xb1fc87b4),
+     TOBN(0xd9e9fe81, 0x4ea53299), TOBN(0x2d921ca2, 0x98eb6028),
+     TOBN(0xfaecedfd, 0x0c9803fc), TOBN(0xf38ae891, 0x4d7b4745),
+     TOBN(0xd8c5fccf, 0xc5e3a3d8), TOBN(0xbefd904c, 0x4079dfbf),
+     TOBN(0xbc6d6a58, 0xfead0197), TOBN(0x39227077, 0x695532a4),
+     TOBN(0x09e23e6d, 0xdbef42f5), TOBN(0x7e449b64, 0x480a9908),
+     TOBN(0x7b969c1a, 0xad9a2e40), TOBN(0x6231d792, 0x9591c2a4),
+     TOBN(0x87151456, 0x0f664534), TOBN(0x85ceae7c, 0x4b68f103),
+     TOBN(0xac09c4ae, 0x65578ab9), TOBN(0x33ec6868, 0xf044b10c),
+     TOBN(0x6ac4832b, 0x3a8ec1f1), TOBN(0x5509d128, 0x5847d5ef),
+     TOBN(0xf909604f, 0x763f1574), TOBN(0xb16c4303, 0xc32f63c4),
+     TOBN(0xb6ab2014, 0x7ca23cd3), TOBN(0xcaa7a5c6, 0xa391849d),
+     TOBN(0x5b0673a3, 0x75678d94), TOBN(0xc982ddd4, 0xdd303e64),
+     TOBN(0xfd7b000b, 0x5db6f971), TOBN(0xbba2cb1f, 0x6f876f92),
+     TOBN(0xc77332a3, 0x3c569426), TOBN(0xa159100c, 0x570d74f8),
+     TOBN(0xfd16847f, 0xdec67ef5), TOBN(0x742ee464, 0x233e76b7),
+     TOBN(0x0b8e4134, 0xefc2b4c8), TOBN(0xca640b86, 0x42a3e521),
+     TOBN(0x653a0190, 0x8ceb6aa9), TOBN(0x313c300c, 0x547852d5),
+     TOBN(0x24e4ab12, 0x6b237af7), TOBN(0x2ba90162, 0x8bb47af8),
+     TOBN(0x3d5e58d6, 0xa8219bb7), TOBN(0xc691d0bd, 0x1b06c57f),
+     TOBN(0x0ae4cb10, 0xd257576e), TOBN(0x3569656c, 0xd54a3dc3),
+     TOBN(0xe5ebaebd, 0x94cda03a), TOBN(0x934e82d3, 0x162bfe13),
+     TOBN(0x450ac0ba, 0xe251a0c6), TOBN(0x480b9e11, 0xdd6da526),
+     TOBN(0x00467bc5, 0x8cce08b5), TOBN(0xb636458c, 0x7f178d55),
+     TOBN(0xc5748bae, 0xa677d806), TOBN(0x2763a387, 0xdfa394eb),
+     TOBN(0xa12b448a, 0x7d3cebb6), TOBN(0xe7adda3e, 0x6f20d850),
+     TOBN(0xf63ebce5, 0x1558462c), TOBN(0x58b36143, 0x620088a8),
+     TOBN(0x8a2cc3ca, 0x4d63c0ee), TOBN(0x51233117, 0x0fe948ce),
+     TOBN(0x7463fd85, 0x222ef33b), TOBN(0xadf0c7dc, 0x7c603d6c),
+     TOBN(0x0ec32d3b, 0xfe7765e5), TOBN(0xccaab359, 0xbf380409),
+     TOBN(0xbdaa84d6, 0x8e59319c), TOBN(0xd9a4c280, 0x9c80c34d),
+     TOBN(0xa9d89488, 0xa059c142), TOBN(0x6f5ae714, 0xff0b9346),
+     TOBN(0x068f237d, 0x16fb3664), TOBN(0x5853e4c4, 0x363186ac),
+     TOBN(0xe2d87d23, 0x63c52f98), TOBN(0x2ec4a766, 0x81828876),
+     TOBN(0x47b864fa, 0xe14e7b1c), TOBN(0x0c0bc0e5, 0x69192408),
+     TOBN(0xe4d7681d, 0xb82e9f3e), TOBN(0x83200f0b, 0xdf25e13c),
+     TOBN(0x8909984c, 0x66f27280), TOBN(0x462d7b00, 0x75f73227),
+     TOBN(0xd90ba188, 0xf2651798), TOBN(0x74c6e18c, 0x36ab1c34),
+     TOBN(0xab256ea3, 0x5ef54359), TOBN(0x03466612, 0xd1aa702f),
+     TOBN(0x624d6049, 0x2ed22e91), TOBN(0x6fdfe0b5, 0x6f072822),
+     TOBN(0xeeca1115, 0x39ce2271), TOBN(0x98100a4f, 0xdb01614f),
+     TOBN(0xb6b0daa2, 0xa35c628f), TOBN(0xb6f94d2e, 0xc87e9a47),
+     TOBN(0xc6773259, 0x1d57d9ce), TOBN(0xf70bfeec, 0x03884a7b),
+     TOBN(0x5fb35ccf, 0xed2bad01), TOBN(0xa155cbe3, 0x1da6a5c7),
+     TOBN(0xc2e2594c, 0x30a92f8f), TOBN(0x649c89ce, 0x5bfafe43),
+     TOBN(0xd158667d, 0xe9ff257a), TOBN(0x9b359611, 0xf32c50ae),
+     TOBN(0x4b00b20b, 0x906014cf), TOBN(0xf3a8cfe3, 0x89bc7d3d),
+     TOBN(0x4ff23ffd, 0x248a7d06), TOBN(0x80c5bfb4, 0x878873fa),
+     TOBN(0xb7d9ad90, 0x05745981), TOBN(0x179c85db, 0x3db01994),
+     TOBN(0xba41b062, 0x61a6966c), TOBN(0x4d82d052, 0xeadce5a8),
+     TOBN(0x9e91cd3b, 0xa5e6a318), TOBN(0x47795f4f, 0x95b2dda0),
+     TOBN(0xecfd7c1f, 0xd55a897c), TOBN(0x009194ab, 0xb29110fb),
+     TOBN(0x5f0e2046, 0xe381d3b0), TOBN(0x5f3425f6, 0xa98dd291),
+     TOBN(0xbfa06687, 0x730d50da), TOBN(0x0423446c, 0x4b083b7f),
+     TOBN(0x397a247d, 0xd69d3417), TOBN(0xeb629f90, 0x387ba42a),
+     TOBN(0x1ee426cc, 0xd5cd79bf), TOBN(0x0032940b, 0x946c6e18),
+     TOBN(0x1b1e8ae0, 0x57477f58), TOBN(0xe94f7d34, 0x6d823278),
+     TOBN(0xc747cb96, 0x782ba21a), TOBN(0xc5254469, 0xf72b33a5),
+     TOBN(0x772ef6de, 0xc7f80c81), TOBN(0xd73acbfe, 0x2cd9e6b5),
+     TOBN(0x4075b5b1, 0x49ee90d9), TOBN(0x785c339a, 0xa06e9eba),
+     TOBN(0xa1030d5b, 0xabf825e0), TOBN(0xcec684c3, 0xa42931dc),
+     TOBN(0x42ab62c9, 0xc1586e63), TOBN(0x45431d66, 0x5ab43f2b),
+     TOBN(0x57c8b2c0, 0x55f7835d), TOBN(0x033da338, 0xc1b7f865),
+     TOBN(0x283c7513, 0xcaa76097), TOBN(0x0a624fa9, 0x36c83906),
+     TOBN(0x6b20afec, 0x715af2c7), TOBN(0x4b969974, 0xeba78bfd),
+     TOBN(0x220755cc, 0xd921d60e), TOBN(0x9b944e10, 0x7baeca13),
+     TOBN(0x04819d51, 0x5ded93d4), TOBN(0x9bbff86e, 0x6dddfd27),
+     TOBN(0x6b344130, 0x77adc612), TOBN(0xa7496529, 0xbbd803a0),
+     TOBN(0x1a1baaa7, 0x6d8805bd), TOBN(0xc8403902, 0x470343ad),
+     TOBN(0x39f59f66, 0x175adff1), TOBN(0x0b26d7fb, 0xb7d8c5b7),
+     TOBN(0xa875f5ce, 0x529d75e3), TOBN(0x85efc7e9, 0x41325cc2),
+     TOBN(0x21950b42, 0x1ff6acd3), TOBN(0xffe70484, 0x53dc6909),
+     TOBN(0xff4cd0b2, 0x28766127), TOBN(0xabdbe608, 0x4fb7db2b),
+     TOBN(0x837c9228, 0x5e1109e8), TOBN(0x26147d27, 0xf4645b5a),
+     TOBN(0x4d78f592, 0xf7818ed8), TOBN(0xd394077e, 0xf247fa36),
+     TOBN(0x0fb9c2d0, 0x488c171a), TOBN(0xa78bfbaa, 0x13685278),
+     TOBN(0xedfbe268, 0xd5b1fa6a), TOBN(0x0dceb8db, 0x2b7eaba7),
+     TOBN(0xbf9e8089, 0x9ae2b710), TOBN(0xefde7ae6, 0xa4449c96),
+     TOBN(0x43b7716b, 0xcc143a46), TOBN(0xd7d34194, 0xc3628c13),
+     TOBN(0x508cec1c, 0x3b3f64c9), TOBN(0xe20bc0ba, 0x1e5edf3f),
+     TOBN(0xda1deb85, 0x2f4318d4), TOBN(0xd20ebe0d, 0x5c3fa443),
+     TOBN(0x370b4ea7, 0x73241ea3), TOBN(0x61f1511c, 0x5e1a5f65),
+     TOBN(0x99a5e23d, 0x82681c62), TOBN(0xd731e383, 0xa2f54c2d),
+     TOBN(0x2692f36e, 0x83445904), TOBN(0x2e0ec469, 0xaf45f9c0),
+     TOBN(0x905a3201, 0xc67528b7), TOBN(0x88f77f34, 0xd0e5e542),
+     TOBN(0xf67a8d29, 0x5864687c), TOBN(0x23b92eae, 0x22df3562),
+     TOBN(0x5c27014b, 0x9bbec39e), TOBN(0x7ef2f226, 0x9c0f0f8d),
+     TOBN(0x97359638, 0x546c4d8d), TOBN(0x5f9c3fc4, 0x92f24679),
+     TOBN(0x912e8bed, 0xa8c8acd9), TOBN(0xec3a318d, 0x306634b0),
+     TOBN(0x80167f41, 0xc31cb264), TOBN(0x3db82f6f, 0x522113f2),
+     TOBN(0xb155bcd2, 0xdcafe197), TOBN(0xfba1da59, 0x43465283),
+     TOBN(0xa0425b8e, 0xb212cf53), TOBN(0x4f2e512e, 0xf8557c5f),
+     TOBN(0xc1286ff9, 0x25c4d56c), TOBN(0xbb8a0fea, 0xee26c851),
+     TOBN(0xc28f70d2, 0xe7d6107e), TOBN(0x7ee0c444, 0xe76265aa),
+     TOBN(0x3df277a4, 0x1d1936b1), TOBN(0x1a556e3f, 0xea9595eb),
+     TOBN(0x258bbbf9, 0xe7305683), TOBN(0x31eea5bf, 0x07ef5be6),
+     TOBN(0x0deb0e4a, 0x46c814c1), TOBN(0x5cee8449, 0xa7b730dd),
+     TOBN(0xeab495c5, 0xa0182bde), TOBN(0xee759f87, 0x9e27a6b4),
+     TOBN(0xc2cf6a68, 0x80e518ca), TOBN(0x25e8013f, 0xf14cf3f4),
+     TOBN(0x8fc44140, 0x7e8d7a14), TOBN(0xbb1ff3ca, 0x9556f36a),
+     TOBN(0x6a844385, 0x14600044), TOBN(0xba3f0c4a, 0x7451ae63),
+     TOBN(0xdfcac25b, 0x1f9af32a), TOBN(0x01e0db86, 0xb1f2214b),
+     TOBN(0x4e9a5bc2, 0xa4b596ac), TOBN(0x83927681, 0x026c2c08),
+     TOBN(0x3ec832e7, 0x7acaca28), TOBN(0x1bfeea57, 0xc7385b29),
+     TOBN(0x068212e3, 0xfd1eaf38), TOBN(0xc1329830, 0x6acf8ccc),
+     TOBN(0xb909f2db, 0x2aac9e59), TOBN(0x5748060d, 0xb661782a),
+     TOBN(0xc5ab2632, 0xc79b7a01), TOBN(0xda44c6c6, 0x00017626),
+     TOBN(0xf26c00e8, 0xa7ea82f0), TOBN(0x99cac80d, 0xe4299aaf),
+     TOBN(0xd66fe3b6, 0x7ed78be1), TOBN(0x305f725f, 0x648d02cd),
+     TOBN(0x33ed1bc4, 0x623fb21b), TOBN(0xfa70533e, 0x7a6319ad),
+     TOBN(0x17ab562d, 0xbe5ffb3e), TOBN(0x06374994, 0x56674741),
+     TOBN(0x69d44ed6, 0x5c46aa8e), TOBN(0x2100d5d3, 0xa8d063d1),
+     TOBN(0xcb9727ea, 0xa2d17c36), TOBN(0x4c2bab1b, 0x8add53b7),
+     TOBN(0xa084e90c, 0x15426704), TOBN(0x778afcd3, 0xa837ebea),
+     TOBN(0x6651f701, 0x7ce477f8), TOBN(0xa0624998, 0x46fb7a8b),
+     TOBN(0xdc1e6828, 0xed8a6e19), TOBN(0x33fc2336, 0x4189d9c7),
+     TOBN(0x026f8fe2, 0x671c39bc), TOBN(0xd40c4ccd, 0xbc6f9915),
+     TOBN(0xafa135bb, 0xf80e75ca), TOBN(0x12c651a0, 0x22adff2c),
+     TOBN(0xc40a04bd, 0x4f51ad96), TOBN(0x04820109, 0xbbe4e832),
+     TOBN(0x3667eb1a, 0x7f4c04cc), TOBN(0x59556621, 0xa9404f84),
+     TOBN(0x71cdf653, 0x7eceb50a), TOBN(0x994a44a6, 0x9b8335fa),
+     TOBN(0xd7faf819, 0xdbeb9b69), TOBN(0x473c5680, 0xeed4350d),
+     TOBN(0xb6658466, 0xda44bba2), TOBN(0x0d1bc780, 0x872bdbf3),
+     TOBN(0xe535f175, 0xa1962f91), TOBN(0x6ed7e061, 0xed58f5a7),
+     TOBN(0x177aa4c0, 0x2089a233), TOBN(0x0dbcb03a, 0xe539b413),
+     TOBN(0xe3dc424e, 0xbb32e38e), TOBN(0x6472e5ef, 0x6806701e),
+     TOBN(0xdd47ff98, 0x814be9ee), TOBN(0x6b60cfff, 0x35ace009),
+     TOBN(0xb8d3d931, 0x9ff91fe5), TOBN(0x039c4800, 0xf0518eed),
+     TOBN(0x95c37632, 0x9182cb26), TOBN(0x0763a434, 0x82fc568d),
+     TOBN(0x707c04d5, 0x383e76ba), TOBN(0xac98b930, 0x824e8197),
+     TOBN(0x92bf7c8f, 0x91230de0), TOBN(0x90876a01, 0x40959b70),
+     TOBN(0xdb6d96f3, 0x05968b80), TOBN(0x380a0913, 0x089f73b9),
+     TOBN(0x7da70b83, 0xc2c61e01), TOBN(0x95fb8394, 0x569b38c7),
+     TOBN(0x9a3c6512, 0x80edfe2f), TOBN(0x8f726bb9, 0x8faeaf82),
+     TOBN(0x8010a4a0, 0x78424bf8), TOBN(0x29672044, 0x0e844970)}
+    ,
+    {TOBN(0x63c5cb81, 0x7a2ad62a), TOBN(0x7ef2b6b9, 0xac62ff54),
+     TOBN(0x3749bba4, 0xb3ad9db5), TOBN(0xad311f2c, 0x46d5a617),
+     TOBN(0xb77a8087, 0xc2ff3b6d), TOBN(0xb46feaf3, 0x367834ff),
+     TOBN(0xf8aa266d, 0x75d6b138), TOBN(0xfa38d320, 0xec008188),
+     TOBN(0x486d8ffa, 0x696946fc), TOBN(0x50fbc6d8, 0xb9cba56d),
+     TOBN(0x7e3d423e, 0x90f35a15), TOBN(0x7c3da195, 0xc0dd962c),
+     TOBN(0xe673fdb0, 0x3cfd5d8b), TOBN(0x0704b7c2, 0x889dfca5),
+     TOBN(0xf6ce581f, 0xf52305aa), TOBN(0x399d49eb, 0x914d5e53),
+     TOBN(0x380a496d, 0x6ec293cd), TOBN(0x733dbda7, 0x8e7051f5),
+     TOBN(0x037e388d, 0xb849140a), TOBN(0xee4b32b0, 0x5946dbf6),
+     TOBN(0xb1c4fda9, 0xcae368d1), TOBN(0x5001a7b0, 0xfdb0b2f3),
+     TOBN(0x6df59374, 0x2e3ac46e), TOBN(0x4af675f2, 0x39b3e656),
+     TOBN(0x44e38110, 0x39949296), TOBN(0x5b63827b, 0x361db1b5),
+     TOBN(0x3e5323ed, 0x206eaff5), TOBN(0x942370d2, 0xc21f4290),
+     TOBN(0xf2caaf2e, 0xe0d985a1), TOBN(0x192cc64b, 0x7239846d),
+     TOBN(0x7c0b8f47, 0xae6312f8), TOBN(0x7dc61f91, 0x96620108),
+     TOBN(0xb830fb5b, 0xc2da7de9), TOBN(0xd0e643df, 0x0ff8d3be),
+     TOBN(0x31ee77ba, 0x188a9641), TOBN(0x4e8aa3aa, 0xbcf6d502),
+     TOBN(0xf9fb6532, 0x9a49110f), TOBN(0xd18317f6, 0x2dd6b220),
+     TOBN(0x7e3ced41, 0x52c3ea5a), TOBN(0x0d296a14, 0x7d579c4a),
+     TOBN(0x35d6a53e, 0xed4c3717), TOBN(0x9f8240cf, 0x3d0ed2a3),
+     TOBN(0x8c0d4d05, 0xe5543aa5), TOBN(0x45d5bbfb, 0xdd33b4b4),
+     TOBN(0xfa04cc73, 0x137fd28e), TOBN(0x862ac6ef, 0xc73b3ffd),
+     TOBN(0x403ff9f5, 0x31f51ef2), TOBN(0x34d5e0fc, 0xbc73f5a2),
+     TOBN(0xf2526820, 0x08913f4f), TOBN(0xea20ed61, 0xeac93d95),
+     TOBN(0x51ed38b4, 0x6ca6b26c), TOBN(0x8662dcbc, 0xea4327b0),
+     TOBN(0x6daf295c, 0x725d2aaa), TOBN(0xbad2752f, 0x8e52dcda),
+     TOBN(0x2210e721, 0x0b17dacc), TOBN(0xa37f7912, 0xd51e8232),
+     TOBN(0x4f7081e1, 0x44cc3add), TOBN(0xd5ffa1d6, 0x87be82cf),
+     TOBN(0x89890b6c, 0x0edd6472), TOBN(0xada26e1a, 0x3ed17863),
+     TOBN(0x276f2715, 0x63483caa), TOBN(0xe6924cd9, 0x2f6077fd),
+     TOBN(0x05a7fe98, 0x0a466e3c), TOBN(0xf1c794b0, 0xb1902d1f),
+     TOBN(0xe5213688, 0x82a8042c), TOBN(0xd931cfaf, 0xcd278298),
+     TOBN(0x069a0ae0, 0xf597a740), TOBN(0x0adbb3f3, 0xeb59107c),
+     TOBN(0x983e951e, 0x5eaa8eb8), TOBN(0xe663a8b5, 0x11b48e78),
+     TOBN(0x1631cc0d, 0x8a03f2c5), TOBN(0x7577c11e, 0x11e271e2),
+     TOBN(0x33b2385c, 0x08369a90), TOBN(0x2990c59b, 0x190eb4f8),
+     TOBN(0x819a6145, 0xc68eac80), TOBN(0x7a786d62, 0x2ec4a014),
+     TOBN(0x33faadbe, 0x20ac3a8d), TOBN(0x31a21781, 0x5aba2d30),
+     TOBN(0x209d2742, 0xdba4f565), TOBN(0xdb2ce9e3, 0x55aa0fbb),
+     TOBN(0x8cef334b, 0x168984df), TOBN(0xe81dce17, 0x33879638),
+     TOBN(0xf6e6949c, 0x263720f0), TOBN(0x5c56feaf, 0xf593cbec),
+     TOBN(0x8bff5601, 0xfde58c84), TOBN(0x74e24117, 0x2eccb314),
+     TOBN(0xbcf01b61, 0x4c9a8a78), TOBN(0xa233e35e, 0x544c9868),
+     TOBN(0xb3156bf3, 0x8bd7aff1), TOBN(0x1b5ee4cb, 0x1d81b146),
+     TOBN(0x7ba1ac41, 0xd628a915), TOBN(0x8f3a8f9c, 0xfd89699e),
+     TOBN(0x7329b9c9, 0xa0748be7), TOBN(0x1d391c95, 0xa92e621f),
+     TOBN(0xe51e6b21, 0x4d10a837), TOBN(0xd255f53a, 0x4947b435),
+     TOBN(0x07669e04, 0xf1788ee3), TOBN(0xc14f27af, 0xa86938a2),
+     TOBN(0x8b47a334, 0xe93a01c0), TOBN(0xff627438, 0xd9366808),
+     TOBN(0x7a0985d8, 0xca2a5965), TOBN(0x3d9a5542, 0xd6e9b9b3),
+     TOBN(0xc23eb80b, 0x4cf972e8), TOBN(0x5c1c33bb, 0x4fdf72fd),
+     TOBN(0x0c4a58d4, 0x74a86108), TOBN(0xf8048a8f, 0xee4c5d90),
+     TOBN(0xe3c7c924, 0xe86d4c80), TOBN(0x28c889de, 0x056a1e60),
+     TOBN(0x57e2662e, 0xb214a040), TOBN(0xe8c48e98, 0x37e10347),
+     TOBN(0x87742862, 0x80ac748a), TOBN(0xf1c24022, 0x186b06f2),
+     TOBN(0xac2dd4c3, 0x5f74040a), TOBN(0x409aeb71, 0xfceac957),
+     TOBN(0x4fbad782, 0x55c4ec23), TOBN(0xb359ed61, 0x8a7b76ec),
+     TOBN(0x12744926, 0xed6f4a60), TOBN(0xe21e8d7f, 0x4b912de3),
+     TOBN(0xe2575a59, 0xfc705a59), TOBN(0x72f1d4de, 0xed2dbc0e),
+     TOBN(0x3d2b24b9, 0xeb7926b8), TOBN(0xbff88cb3, 0xcdbe5509),
+     TOBN(0xd0f399af, 0xe4dd640b), TOBN(0x3c5fe130, 0x2f76ed45),
+     TOBN(0x6f3562f4, 0x3764fb3d), TOBN(0x7b5af318, 0x3151b62d),
+     TOBN(0xd5bd0bc7, 0xd79ce5f3), TOBN(0xfdaf6b20, 0xec66890f),
+     TOBN(0x735c67ec, 0x6063540c), TOBN(0x50b259c2, 0xe5f9cb8f),
+     TOBN(0xb8734f9a, 0x3f99c6ab), TOBN(0xf8cc13d5, 0xa3a7bc85),
+     TOBN(0x80c1b305, 0xc5217659), TOBN(0xfe5364d4, 0x4ec12a54),
+     TOBN(0xbd87045e, 0x681345fe), TOBN(0x7f8efeb1, 0x582f897f),
+     TOBN(0xe8cbf1e5, 0xd5923359), TOBN(0xdb0cea9d, 0x539b9fb0),
+     TOBN(0x0c5b34cf, 0x49859b98), TOBN(0x5e583c56, 0xa4403cc6),
+     TOBN(0x11fc1a2d, 0xd48185b7), TOBN(0xc93fbc7e, 0x6e521787),
+     TOBN(0x47e7a058, 0x05105b8b), TOBN(0x7b4d4d58, 0xdb8260c8),
+     TOBN(0xe33930b0, 0x46eb842a), TOBN(0x8e844a9a, 0x7bdae56d),
+     TOBN(0x34ef3a9e, 0x13f7fdfc), TOBN(0xb3768f82, 0x636ca176),
+     TOBN(0x2821f4e0, 0x4e09e61c), TOBN(0x414dc3a1, 0xa0c7cddc),
+     TOBN(0xd5379437, 0x54945fcd), TOBN(0x151b6eef, 0xb3555ff1),
+     TOBN(0xb31bd613, 0x6339c083), TOBN(0x39ff8155, 0xdfb64701),
+     TOBN(0x7c3388d2, 0xe29604ab), TOBN(0x1e19084b, 0xa6b10442),
+     TOBN(0x17cf54c0, 0xeccd47ef), TOBN(0x89693385, 0x4a5dfb30),
+     TOBN(0x69d023fb, 0x47daf9f6), TOBN(0x9222840b, 0x7d91d959),
+     TOBN(0x439108f5, 0x803bac62), TOBN(0x0b7dd91d, 0x379bd45f),
+     TOBN(0xd651e827, 0xca63c581), TOBN(0x5c5d75f6, 0x509c104f),
+     TOBN(0x7d5fc738, 0x1f2dc308), TOBN(0x20faa7bf, 0xd98454be),
+     TOBN(0x95374bee, 0xa517b031), TOBN(0xf036b9b1, 0x642692ac),
+     TOBN(0xc5106109, 0x39842194), TOBN(0xb7e2353e, 0x49d05295),
+     TOBN(0xfc8c1d5c, 0xefb42ee0), TOBN(0xe04884eb, 0x08ce811c),
+     TOBN(0xf1f75d81, 0x7419f40e), TOBN(0x5b0ac162, 0xa995c241),
+     TOBN(0x120921bb, 0xc4c55646), TOBN(0x713520c2, 0x8d33cf97),
+     TOBN(0xb4a65a5c, 0xe98c5100), TOBN(0x6cec871d, 0x2ddd0f5a),
+     TOBN(0x251f0b7f, 0x9ba2e78b), TOBN(0x224a8434, 0xce3a2a5f),
+     TOBN(0x26827f61, 0x25f5c46f), TOBN(0x6a22bedc, 0x48545ec0),
+     TOBN(0x25ae5fa0, 0xb1bb5cdc), TOBN(0xd693682f, 0xfcb9b98f),
+     TOBN(0x32027fe8, 0x91e5d7d3), TOBN(0xf14b7d17, 0x73a07678),
+     TOBN(0xf88497b3, 0xc0dfdd61), TOBN(0xf7c2eec0, 0x2a8c4f48),
+     TOBN(0xaa5573f4, 0x3756e621), TOBN(0xc013a240, 0x1825b948),
+     TOBN(0x1c03b345, 0x63878572), TOBN(0xa0472bea, 0x653a4184),
+     TOBN(0xf4222e27, 0x0ac69a80), TOBN(0x34096d25, 0xf51e54f6),
+     TOBN(0x00a648cb, 0x8fffa591), TOBN(0x4e87acdc, 0x69b6527f),
+     TOBN(0x0575e037, 0xe285ccb4), TOBN(0x188089e4, 0x50ddcf52),
+     TOBN(0xaa96c9a8, 0x870ff719), TOBN(0x74a56cd8, 0x1fc7e369),
+     TOBN(0x41d04ee2, 0x1726931a), TOBN(0x0bbbb2c8, 0x3660ecfd),
+     TOBN(0xa6ef6de5, 0x24818e18), TOBN(0xe421cc51, 0xe7d57887),
+     TOBN(0xf127d208, 0xbea87be6), TOBN(0x16a475d3, 0xb1cdd682),
+     TOBN(0x9db1b684, 0x439b63f7), TOBN(0x5359b3db, 0xf0f113b6),
+     TOBN(0xdfccf1de, 0x8bf06e31), TOBN(0x1fdf8f44, 0xdd383901),
+     TOBN(0x10775cad, 0x5017e7d2), TOBN(0xdfc3a597, 0x58d11eef),
+     TOBN(0x6ec9c8a0, 0xb1ecff10), TOBN(0xee6ed6cc, 0x28400549),
+     TOBN(0xb5ad7bae, 0x1b4f8d73), TOBN(0x61b4f11d, 0xe00aaab9),
+     TOBN(0x7b32d69b, 0xd4eff2d7), TOBN(0x88ae6771, 0x4288b60f),
+     TOBN(0x159461b4, 0x37a1e723), TOBN(0x1f3d4789, 0x570aae8c),
+     TOBN(0x869118c0, 0x7f9871da), TOBN(0x35fbda78, 0xf635e278),
+     TOBN(0x738f3641, 0xe1541dac), TOBN(0x6794b13a, 0xc0dae45f),
+     TOBN(0x065064ac, 0x09cc0917), TOBN(0x27c53729, 0xc68540fd),
+     TOBN(0x0d2d4c8e, 0xef227671), TOBN(0xd23a9f80, 0xa1785a04),
+     TOBN(0x98c59528, 0x52650359), TOBN(0xfa09ad01, 0x74a1acad),
+     TOBN(0x082d5a29, 0x0b55bf5c), TOBN(0xa40f1c67, 0x419b8084),
+     TOBN(0x3a5c752e, 0xdcc18770), TOBN(0x4baf1f2f, 0x8825c3a5),
+     TOBN(0xebd63f74, 0x21b153ed), TOBN(0xa2383e47, 0xb2f64723),
+     TOBN(0xe7bf620a, 0x2646d19a), TOBN(0x56cb44ec, 0x03c83ffd),
+     TOBN(0xaf7267c9, 0x4f6be9f1), TOBN(0x8b2dfd7b, 0xc06bb5e9),
+     TOBN(0xb87072f2, 0xa672c5c7), TOBN(0xeacb11c8, 0x0d53c5e2),
+     TOBN(0x22dac29d, 0xff435932), TOBN(0x37bdb99d, 0x4408693c),
+     TOBN(0xf6e62fb6, 0x2899c20f), TOBN(0x3535d512, 0x447ece24),
+     TOBN(0xfbdc6b88, 0xff577ce3), TOBN(0x726693bd, 0x190575f2),
+     TOBN(0x6772b0e5, 0xab4b35a2), TOBN(0x1d8b6001, 0xf5eeaacf),
+     TOBN(0x728f7ce4, 0x795b9580), TOBN(0x4a20ed2a, 0x41fb81da),
+     TOBN(0x9f685cd4, 0x4fec01e6), TOBN(0x3ed7ddcc, 0xa7ff50ad),
+     TOBN(0x460fd264, 0x0c2d97fd), TOBN(0x3a241426, 0xeb82f4f9),
+     TOBN(0x17d1df2c, 0x6a8ea820), TOBN(0xb2b50d3b, 0xf22cc254),
+     TOBN(0x03856cba, 0xb7291426), TOBN(0x87fd26ae, 0x04f5ee39),
+     TOBN(0x9cb696cc, 0x02bee4ba), TOBN(0x53121804, 0x06820fd6),
+     TOBN(0xa5dfc269, 0x0212e985), TOBN(0x666f7ffa, 0x160f9a09),
+     TOBN(0xc503cd33, 0xbccd9617), TOBN(0x365dede4, 0xba7730a3),
+     TOBN(0x798c6355, 0x5ddb0786), TOBN(0xa6c3200e, 0xfc9cd3bc),
+     TOBN(0x060ffb2c, 0xe5e35efd), TOBN(0x99a4e25b, 0x5555a1c1),
+     TOBN(0x11d95375, 0xf70b3751), TOBN(0x0a57354a, 0x160e1bf6),
+     TOBN(0xecb3ae4b, 0xf8e4b065), TOBN(0x07a834c4, 0x2e53022b),
+     TOBN(0x1cd300b3, 0x8692ed96), TOBN(0x16a6f792, 0x61ee14ec),
+     TOBN(0x8f1063c6, 0x6a8649ed), TOBN(0xfbcdfcfe, 0x869f3e14),
+     TOBN(0x2cfb97c1, 0x00a7b3ec), TOBN(0xcea49b3c, 0x7130c2f1),
+     TOBN(0x462d044f, 0xe9d96488), TOBN(0x4b53d52e, 0x8182a0c1),
+     TOBN(0x84b6ddd3, 0x0391e9e9), TOBN(0x80ab7b48, 0xb1741a09),
+     TOBN(0xec0e15d4, 0x27d3317f), TOBN(0x8dfc1ddb, 0x1a64671e),
+     TOBN(0x93cc5d5f, 0xd49c5b92), TOBN(0xc995d53d, 0x3674a331),
+     TOBN(0x302e41ec, 0x090090ae), TOBN(0x2278a0cc, 0xedb06830),
+     TOBN(0x1d025932, 0xfbc99690), TOBN(0x0c32fbd2, 0xb80d68da),
+     TOBN(0xd79146da, 0xf341a6c1), TOBN(0xae0ba139, 0x1bef68a0),
+     TOBN(0xc6b8a563, 0x8d774b3a), TOBN(0x1cf307bd, 0x880ba4d7),
+     TOBN(0xc033bdc7, 0x19803511), TOBN(0xa9f97b3b, 0x8888c3be),
+     TOBN(0x3d68aebc, 0x85c6d05e), TOBN(0xc3b88a9d, 0x193919eb),
+     TOBN(0x2d300748, 0xc48b0ee3), TOBN(0x7506bc7c, 0x07a746c1),
+     TOBN(0xfc48437c, 0x6e6d57f3), TOBN(0x5bd71587, 0xcfeaa91a),
+     TOBN(0xa4ed0408, 0xc1bc5225), TOBN(0xd0b946db, 0x2719226d),
+     TOBN(0x109ecd62, 0x758d2d43), TOBN(0x75c8485a, 0x2751759b),
+     TOBN(0xb0b75f49, 0x9ce4177a), TOBN(0x4fa61a1e, 0x79c10c3d),
+     TOBN(0xc062d300, 0xa167fcd7), TOBN(0x4df3874c, 0x750f0fa8),
+     TOBN(0x29ae2cf9, 0x83dfedc9), TOBN(0xf8437134, 0x8d87631a),
+     TOBN(0xaf571711, 0x7429c8d2), TOBN(0x18d15867, 0x146d9272),
+     TOBN(0x83053ecf, 0x69769bb7), TOBN(0xc55eb856, 0xc479ab82),
+     TOBN(0x5ef7791c, 0x21b0f4b2), TOBN(0xaa5956ba, 0x3d491525),
+     TOBN(0x407a96c2, 0x9fe20eba), TOBN(0xf27168bb, 0xe52a5ad3),
+     TOBN(0x43b60ab3, 0xbf1d9d89), TOBN(0xe45c51ef, 0x710e727a),
+     TOBN(0xdfca5276, 0x099b4221), TOBN(0x8dc6407c, 0x2557a159),
+     TOBN(0x0ead8335, 0x91035895), TOBN(0x0a9db957, 0x9c55dc32),
+     TOBN(0xe40736d3, 0xdf61bc76), TOBN(0x13a619c0, 0x3f778cdb),
+     TOBN(0x6dd921a4, 0xc56ea28f), TOBN(0x76a52433, 0x2fa647b4),
+     TOBN(0x23591891, 0xac5bdc5d), TOBN(0xff4a1a72, 0xbac7dc01),
+     TOBN(0x9905e261, 0x62df8453), TOBN(0x3ac045df, 0xe63b265f),
+     TOBN(0x8a3f341b, 0xad53dba7), TOBN(0x8ec269cc, 0x837b625a),
+     TOBN(0xd71a2782, 0x3ae31189), TOBN(0x8fb4f9a3, 0x55e96120),
+     TOBN(0x804af823, 0xff9875cf), TOBN(0x23224f57, 0x5d442a9b),
+     TOBN(0x1c4d3b9e, 0xecc62679), TOBN(0x91da22fb, 0xa0e7ddb1),
+     TOBN(0xa370324d, 0x6c04a661), TOBN(0x9710d3b6, 0x5e376d17),
+     TOBN(0xed8c98f0, 0x3044e357), TOBN(0xc364ebbe, 0x6422701c),
+     TOBN(0x347f5d51, 0x7733d61c), TOBN(0xd55644b9, 0xcea826c3),
+     TOBN(0x80c6e0ad, 0x55a25548), TOBN(0x0aa7641d, 0x844220a7),
+     TOBN(0x1438ec81, 0x31810660), TOBN(0x9dfa6507, 0xde4b4043),
+     TOBN(0x10b515d8, 0xcc3e0273), TOBN(0x1b6066dd, 0x28d8cfb2),
+     TOBN(0xd3b04591, 0x9c9efebd), TOBN(0x425d4bdf, 0xa21c1ff4),
+     TOBN(0x5fe5af19, 0xd57607d3), TOBN(0xbbf773f7, 0x54481084),
+     TOBN(0x8435bd69, 0x94b03ed1), TOBN(0xd9ad1de3, 0x634cc546),
+     TOBN(0x2cf423fc, 0x00e420ca), TOBN(0xeed26d80, 0xa03096dd),
+     TOBN(0xd7f60be7, 0xa4db09d2), TOBN(0xf47f569d, 0x960622f7),
+     TOBN(0xe5925fd7, 0x7296c729), TOBN(0xeff2db26, 0x26ca2715),
+     TOBN(0xa6fcd014, 0xb913e759), TOBN(0x53da4786, 0x8ff4de93),
+     TOBN(0x14616d79, 0xc32068e1), TOBN(0xb187d664, 0xccdf352e),
+     TOBN(0xf7afb650, 0x1dc90b59), TOBN(0x8170e943, 0x7daa1b26),
+     TOBN(0xc8e3bdd8, 0x700c0a84), TOBN(0x6e8d345f, 0x6482bdfa),
+     TOBN(0x84cfbfa1, 0xc5c5ea50), TOBN(0xd3baf14c, 0x67960681),
+     TOBN(0x26398403, 0x0dd50942), TOBN(0xe4b7839c, 0x4716a663),
+     TOBN(0xd5f1f794, 0xe7de6dc0), TOBN(0x5cd0f4d4, 0x622aa7ce),
+     TOBN(0x5295f3f1, 0x59acfeec), TOBN(0x8d933552, 0x953e0607),
+     TOBN(0xc7db8ec5, 0x776c5722), TOBN(0xdc467e62, 0x2b5f290c),
+     TOBN(0xd4297e70, 0x4ff425a9), TOBN(0x4be924c1, 0x0cf7bb72),
+     TOBN(0x0d5dc5ae, 0xa1892131), TOBN(0x8bf8a8e3, 0xa705c992),
+     TOBN(0x73a0b064, 0x7a305ac5), TOBN(0x00c9ca4e, 0x9a8c77a8),
+     TOBN(0x5dfee80f, 0x83774bdd), TOBN(0x63131602, 0x85734485),
+     TOBN(0xa1b524ae, 0x914a69a9), TOBN(0xebc2ffaf, 0xd4e300d7),
+     TOBN(0x52c93db7, 0x7cfa46a5), TOBN(0x71e6161f, 0x21653b50),
+     TOBN(0x3574fc57, 0xa4bc580a), TOBN(0xc09015dd, 0xe1bc1253),
+     TOBN(0x4b7b47b2, 0xd174d7aa), TOBN(0x4072d8e8, 0xf3a15d04),
+     TOBN(0xeeb7d47f, 0xd6fa07ed), TOBN(0x6f2b9ff9, 0xedbdafb1),
+     TOBN(0x18c51615, 0x3760fe8a), TOBN(0x7a96e6bf, 0xf06c6c13),
+     TOBN(0x4d7a0410, 0x0ea2d071), TOBN(0xa1914e9b, 0x0be2a5ce),
+     TOBN(0x5726e357, 0xd8a3c5cf), TOBN(0x1197ecc3, 0x2abb2b13),
+     TOBN(0x6c0d7f7f, 0x31ae88dd), TOBN(0x15b20d1a, 0xfdbb3efe),
+     TOBN(0xcd06aa26, 0x70584039), TOBN(0x2277c969, 0xa7dc9747),
+     TOBN(0xbca69587, 0x7855d815), TOBN(0x899ea238, 0x5188b32a),
+     TOBN(0x37d9228b, 0x760c1c9d), TOBN(0xc7efbb11, 0x9b5c18da),
+     TOBN(0x7f0d1bc8, 0x19f6dbc5), TOBN(0x4875384b, 0x07e6905b),
+     TOBN(0xc7c50baa, 0x3ba8cd86), TOBN(0xb0ce40fb, 0xc2905de0),
+     TOBN(0x70840673, 0x7a231952), TOBN(0xa912a262, 0xcf43de26),
+     TOBN(0x9c38ddcc, 0xeb5b76c1), TOBN(0x746f5285, 0x26fc0ab4),
+     TOBN(0x52a63a50, 0xd62c269f), TOBN(0x60049c55, 0x99458621),
+     TOBN(0xe7f48f82, 0x3c2f7c9e), TOBN(0x6bd99043, 0x917d5cf3),
+     TOBN(0xeb1317a8, 0x8701f469), TOBN(0xbd3fe2ed, 0x9a449fe0),
+     TOBN(0x421e79ca, 0x12ef3d36), TOBN(0x9ee3c36c, 0x3e7ea5de),
+     TOBN(0xe48198b5, 0xcdff36f7), TOBN(0xaff4f967, 0xc6b82228),
+     TOBN(0x15e19dd0, 0xc47adb7e), TOBN(0x45699b23, 0x032e7dfa),
+     TOBN(0x40680c8b, 0x1fae026a), TOBN(0x5a347a48, 0x550dbf4d),
+     TOBN(0xe652533b, 0x3cef0d7d), TOBN(0xd94f7b18, 0x2bbb4381),
+     TOBN(0x838752be, 0x0e80f500), TOBN(0x8e6e2488, 0x9e9c9bfb),
+     TOBN(0xc9751697, 0x16caca6a), TOBN(0x866c49d8, 0x38531ad9),
+     TOBN(0xc917e239, 0x7151ade1), TOBN(0x2d016ec1, 0x6037c407),
+     TOBN(0xa407ccc9, 0x00eac3f9), TOBN(0x835f6280, 0xe2ed4748),
+     TOBN(0xcc54c347, 0x1cc98e0d), TOBN(0x0e969937, 0xdcb572eb),
+     TOBN(0x1b16c8e8, 0x8f30c9cb), TOBN(0xa606ae75, 0x373c4661),
+     TOBN(0x47aa689b, 0x35502cab), TOBN(0xf89014ae, 0x4d9bb64f),
+     TOBN(0x202f6a9c, 0x31c71f7b), TOBN(0x01f95aa3, 0x296ffe5c),
+     TOBN(0x5fc06014, 0x53cec3a3), TOBN(0xeb991237, 0x5f498a45),
+     TOBN(0xae9a935e, 0x5d91ba87), TOBN(0xc6ac6281, 0x0b564a19),
+     TOBN(0x8a8fe81c, 0x3bd44e69), TOBN(0x7c8b467f, 0x9dd11d45),
+     TOBN(0xf772251f, 0xea5b8e69), TOBN(0xaeecb3bd, 0xc5b75fbc),
+     TOBN(0x1aca3331, 0x887ff0e5), TOBN(0xbe5d49ff, 0x19f0a131),
+     TOBN(0x582c13aa, 0xe5c8646f), TOBN(0xdbaa12e8, 0x20e19980),
+     TOBN(0x8f40f31a, 0xf7abbd94), TOBN(0x1f13f5a8, 0x1dfc7663),
+     TOBN(0x5d81f1ee, 0xaceb4fc0), TOBN(0x36256002, 0x5e6f0f42),
+     TOBN(0x4b67d6d7, 0x751370c8), TOBN(0x2608b698, 0x03e80589),
+     TOBN(0xcfc0d2fc, 0x05268301), TOBN(0xa6943d39, 0x40309212),
+     TOBN(0x192a90c2, 0x1fd0e1c2), TOBN(0xb209f113, 0x37f1dc76),
+     TOBN(0xefcc5e06, 0x97bf1298), TOBN(0xcbdb6730, 0x219d639e),
+     TOBN(0xd009c116, 0xb81e8c6f), TOBN(0xa3ffdde3, 0x1a7ce2e5),
+     TOBN(0xc53fbaaa, 0xa914d3ba), TOBN(0x836d500f, 0x88df85ee),
+     TOBN(0xd98dc71b, 0x66ee0751), TOBN(0x5a3d7005, 0x714516fd),
+     TOBN(0x21d3634d, 0x39eedbba), TOBN(0x35cd2e68, 0x0455a46d),
+     TOBN(0xc8cafe65, 0xf9d7eb0c), TOBN(0xbda3ce9e, 0x00cefb3e),
+     TOBN(0xddc17a60, 0x2c9cf7a4), TOBN(0x01572ee4, 0x7bcb8773),
+     TOBN(0xa92b2b01, 0x8c7548df), TOBN(0x732fd309, 0xa84600e3),
+     TOBN(0xe22109c7, 0x16543a40), TOBN(0x9acafd36, 0xfede3c6c),
+     TOBN(0xfb206852, 0x6824e614), TOBN(0x2a4544a9, 0xda25dca0),
+     TOBN(0x25985262, 0x91d60b06), TOBN(0x281b7be9, 0x28753545),
+     TOBN(0xec667b1a, 0x90f13b27), TOBN(0x33a83aff, 0x940e2eb4),
+     TOBN(0x80009862, 0xd5d721d5), TOBN(0x0c3357a3, 0x5bd3a182),
+     TOBN(0x27f3a83b, 0x7aa2cda4), TOBN(0xb58ae74e, 0xf6f83085),
+     TOBN(0x2a911a81, 0x2e6dad6b), TOBN(0xde286051, 0xf43d6c5b),
+     TOBN(0x4bdccc41, 0xf996c4d8), TOBN(0xe7312ec0, 0x0ae1e24e)}
+    ,
+    {TOBN(0xf8d112e7, 0x6e6485b3), TOBN(0x4d3e24db, 0x771c52f8),
+     TOBN(0x48e3ee41, 0x684a2f6d), TOBN(0x7161957d, 0x21d95551),
+     TOBN(0x19631283, 0xcdb12a6c), TOBN(0xbf3fa882, 0x2e50e164),
+     TOBN(0xf6254b63, 0x3166cc73), TOBN(0x3aefa7ae, 0xaee8cc38),
+     TOBN(0x79b0fe62, 0x3b36f9fd), TOBN(0x26543b23, 0xfde19fc0),
+     TOBN(0x136e64a0, 0x958482ef), TOBN(0x23f63771, 0x9b095825),
+     TOBN(0x14cfd596, 0xb6a1142e), TOBN(0x5ea6aac6, 0x335aac0b),
+     TOBN(0x86a0e8bd, 0xf3081dd5), TOBN(0x5fb89d79, 0x003dc12a),
+     TOBN(0xf615c33a, 0xf72e34d4), TOBN(0x0bd9ea40, 0x110eec35),
+     TOBN(0x1c12bc5b, 0xc1dea34e), TOBN(0x686584c9, 0x49ae4699),
+     TOBN(0x13ad95d3, 0x8c97b942), TOBN(0x4609561a, 0x4e5c7562),
+     TOBN(0x9e94a4ae, 0xf2737f89), TOBN(0xf57594c6, 0x371c78b6),
+     TOBN(0x0f0165fc, 0xe3779ee3), TOBN(0xe00e7f9d, 0xbd495d9e),
+     TOBN(0x1fa4efa2, 0x20284e7a), TOBN(0x4564bade, 0x47ac6219),
+     TOBN(0x90e6312a, 0xc4708e8e), TOBN(0x4f5725fb, 0xa71e9adf),
+     TOBN(0xe95f55ae, 0x3d684b9f), TOBN(0x47f7ccb1, 0x1e94b415),
+     TOBN(0x7322851b, 0x8d946581), TOBN(0xf0d13133, 0xbdf4a012),
+     TOBN(0xa3510f69, 0x6584dae0), TOBN(0x03a7c171, 0x3c9f6c6d),
+     TOBN(0x5be97f38, 0xe475381a), TOBN(0xca1ba422, 0x85823334),
+     TOBN(0xf83cc5c7, 0x0be17dda), TOBN(0x158b1494, 0x0b918c0f),
+     TOBN(0xda3a77e5, 0x522e6b69), TOBN(0x69c908c3, 0xbbcd6c18),
+     TOBN(0x1f1b9e48, 0xd924fd56), TOBN(0x37c64e36, 0xaa4bb3f7),
+     TOBN(0x5a4fdbdf, 0xee478d7d), TOBN(0xba75c8bc, 0x0193f7a0),
+     TOBN(0x84bc1e84, 0x56cd16df), TOBN(0x1fb08f08, 0x46fad151),
+     TOBN(0x8a7cabf9, 0x842e9f30), TOBN(0xa331d4bf, 0x5eab83af),
+     TOBN(0xd272cfba, 0x017f2a6a), TOBN(0x27560abc, 0x83aba0e3),
+     TOBN(0x94b83387, 0x0e3a6b75), TOBN(0x25c6aea2, 0x6b9f50f5),
+     TOBN(0x803d691d, 0xb5fdf6d0), TOBN(0x03b77509, 0xe6333514),
+     TOBN(0x36178903, 0x61a341c1), TOBN(0x3604dc60, 0x0cfd6142),
+     TOBN(0x022295eb, 0x8533316c), TOBN(0x3dbde4ac, 0x44af2922),
+     TOBN(0x898afc5d, 0x1c7eef69), TOBN(0x58896805, 0xd14f4fa1),
+     TOBN(0x05002160, 0x203c21ca), TOBN(0x6f0d1f30, 0x40ef730b),
+     TOBN(0x8e8c44d4, 0x196224f8), TOBN(0x75a4ab95, 0x374d079d),
+     TOBN(0x79085ecc, 0x7d48f123), TOBN(0x56f04d31, 0x1bf65ad8),
+     TOBN(0xe220bf1c, 0xbda602b2), TOBN(0x73ee1742, 0xf9612c69),
+     TOBN(0x76008fc8, 0x084fd06b), TOBN(0x4000ef9f, 0xf11380d1),
+     TOBN(0x48201b4b, 0x12cfe297), TOBN(0x3eee129c, 0x292f74e5),
+     TOBN(0xe1fe114e, 0xc9e874e8), TOBN(0x899b055c, 0x92c5fc41),
+     TOBN(0x4e477a64, 0x3a39c8cf), TOBN(0x82f09efe, 0x78963cc9),
+     TOBN(0x6fd3fd8f, 0xd333f863), TOBN(0x85132b2a, 0xdc949c63),
+     TOBN(0x7e06a3ab, 0x516eb17b), TOBN(0x73bec06f, 0xd2c7372b),
+     TOBN(0xe4f74f55, 0xba896da6), TOBN(0xbb4afef8, 0x8e9eb40f),
+     TOBN(0x2d75bec8, 0xe61d66b0), TOBN(0x02bda4b4, 0xef29300b),
+     TOBN(0x8bbaa8de, 0x026baa5a), TOBN(0xff54befd, 0xa07f4440),
+     TOBN(0xbd9b8b1d, 0xbe7a2af3), TOBN(0xec51caa9, 0x4fb74a72),
+     TOBN(0xb9937a4b, 0x63879697), TOBN(0x7c9a9d20, 0xec2687d5),
+     TOBN(0x1773e44f, 0x6ef5f014), TOBN(0x8abcf412, 0xe90c6900),
+     TOBN(0x387bd022, 0x8142161e), TOBN(0x50393755, 0xfcb6ff2a),
+     TOBN(0x9813fd56, 0xed6def63), TOBN(0x53cf6482, 0x7d53106c),
+     TOBN(0x991a35bd, 0x431f7ac1), TOBN(0xf1e274dd, 0x63e65faf),
+     TOBN(0xf63ffa3c, 0x44cc7880), TOBN(0x411a426b, 0x7c256981),
+     TOBN(0xb698b9fd, 0x93a420e0), TOBN(0x89fdddc0, 0xae53f8fe),
+     TOBN(0x766e0722, 0x32398baa), TOBN(0x205fee42, 0x5cfca031),
+     TOBN(0xa49f5341, 0x7a029cf2), TOBN(0xa88c68b8, 0x4023890d),
+     TOBN(0xbc275041, 0x7337aaa8), TOBN(0x9ed364ad, 0x0eb384f4),
+     TOBN(0xe0816f85, 0x29aba92f), TOBN(0x2e9e1941, 0x04e38a88),
+     TOBN(0x57eef44a, 0x3dafd2d5), TOBN(0x35d1fae5, 0x97ed98d8),
+     TOBN(0x50628c09, 0x2307f9b1), TOBN(0x09d84aae, 0xd6cba5c6),
+     TOBN(0x67071bc7, 0x88aaa691), TOBN(0x2dea57a9, 0xafe6cb03),
+     TOBN(0xdfe11bb4, 0x3d78ac01), TOBN(0x7286418c, 0x7fd7aa51),
+     TOBN(0xfabf7709, 0x77f7195a), TOBN(0x8ec86167, 0xadeb838f),
+     TOBN(0xea1285a8, 0xbb4f012d), TOBN(0xd6883503, 0x9a3eab3f),
+     TOBN(0xee5d24f8, 0x309004c2), TOBN(0xa96e4b76, 0x13ffe95e),
+     TOBN(0x0cdffe12, 0xbd223ea4), TOBN(0x8f5c2ee5, 0xb6739a53),
+     TOBN(0x5cb4aaa5, 0xdd968198), TOBN(0xfa131c52, 0x72413a6c),
+     TOBN(0x53d46a90, 0x9536d903), TOBN(0xb270f0d3, 0x48606d8e),
+     TOBN(0x518c7564, 0xa053a3bc), TOBN(0x088254b7, 0x1a86caef),
+     TOBN(0xb3ba8cb4, 0x0ab5efd0), TOBN(0x5c59900e, 0x4605945d),
+     TOBN(0xecace1dd, 0xa1887395), TOBN(0x40960f36, 0x932a65de),
+     TOBN(0x9611ff5c, 0x3aa95529), TOBN(0xc58215b0, 0x7c1e5a36),
+     TOBN(0xd48c9b58, 0xf0e1a524), TOBN(0xb406856b, 0xf590dfb8),
+     TOBN(0xc7605e04, 0x9cd95662), TOBN(0x0dd036ee, 0xa33ecf82),
+     TOBN(0xa50171ac, 0xc33156b3), TOBN(0xf09d24ea, 0x4a80172e),
+     TOBN(0x4e1f72c6, 0x76dc8eef), TOBN(0xe60caadc, 0x5e3d44ee),
+     TOBN(0x006ef8a6, 0x979b1d8f), TOBN(0x60908a1c, 0x97788d26),
+     TOBN(0x6e08f95b, 0x266feec0), TOBN(0x618427c2, 0x22e8c94e),
+     TOBN(0x3d613339, 0x59145a65), TOBN(0xcd9bc368, 0xfa406337),
+     TOBN(0x82d11be3, 0x2d8a52a0), TOBN(0xf6877b27, 0x97a1c590),
+     TOBN(0x837a819b, 0xf5cbdb25), TOBN(0x2a4fd1d8, 0xde090249),
+     TOBN(0x622a7de7, 0x74990e5f), TOBN(0x840fa5a0, 0x7945511b),
+     TOBN(0x30b974be, 0x6558842d), TOBN(0x70df8c64, 0x17f3d0a6),
+     TOBN(0x7c803520, 0x7542e46d), TOBN(0x7251fe7f, 0xe4ecc823),
+     TOBN(0xe59134cb, 0x5e9aac9a), TOBN(0x11bb0934, 0xf0045d71),
+     TOBN(0x53e5d9b5, 0xdbcb1d4e), TOBN(0x8d97a905, 0x92defc91),
+     TOBN(0xfe289327, 0x7946d3f9), TOBN(0xe132bd24, 0x07472273),
+     TOBN(0xeeeb510c, 0x1eb6ae86), TOBN(0x777708c5, 0xf0595067),
+     TOBN(0x18e2c8cd, 0x1297029e), TOBN(0x2c61095c, 0xbbf9305e),
+     TOBN(0xe466c258, 0x6b85d6d9), TOBN(0x8ac06c36, 0xda1ea530),
+     TOBN(0xa365dc39, 0xa1304668), TOBN(0xe4a9c885, 0x07f89606),
+     TOBN(0x65a4898f, 0xacc7228d), TOBN(0x3e2347ff, 0x84ca8303),
+     TOBN(0xa5f6fb77, 0xea7d23a3), TOBN(0x2fac257d, 0x672a71cd),
+     TOBN(0x6908bef8, 0x7e6a44d3), TOBN(0x8ff87566, 0x891d3d7a),
+     TOBN(0xe58e90b3, 0x6b0cf82e), TOBN(0x6438d246, 0x2615b5e7),
+     TOBN(0x07b1f8fc, 0x669c145a), TOBN(0xb0d8b2da, 0x36f1e1cb),
+     TOBN(0x54d5dadb, 0xd9184c4d), TOBN(0x3dbb18d5, 0xf93d9976),
+     TOBN(0x0a3e0f56, 0xd1147d47), TOBN(0x2afa8c8d, 0xa0a48609),
+     TOBN(0x275353e8, 0xbc36742c), TOBN(0x898f427e, 0xeea0ed90),
+     TOBN(0x26f4947e, 0x3e477b00), TOBN(0x8ad8848a, 0x308741e3),
+     TOBN(0x6c703c38, 0xd74a2a46), TOBN(0x5e3e05a9, 0x9ba17ba2),
+     TOBN(0xc1fa6f66, 0x4ab9a9e4), TOBN(0x474a2d9a, 0x3841d6ec),
+     TOBN(0x871239ad, 0x653ae326), TOBN(0x14bcf72a, 0xa74cbb43),
+     TOBN(0x8737650e, 0x20d4c083), TOBN(0x3df86536, 0x110ed4af),
+     TOBN(0xd2d86fe7, 0xb53ca555), TOBN(0x688cb00d, 0xabd5d538),
+     TOBN(0xcf81bda3, 0x1ad38468), TOBN(0x7ccfe3cc, 0xf01167b6),
+     TOBN(0xcf4f47e0, 0x6c4c1fe6), TOBN(0x557e1f1a, 0x298bbb79),
+     TOBN(0xf93b974f, 0x30d45a14), TOBN(0x174a1d2d, 0x0baf97c4),
+     TOBN(0x7a003b30, 0xc51fbf53), TOBN(0xd8940991, 0xee68b225),
+     TOBN(0x5b0aa7b7, 0x1c0f4173), TOBN(0x975797c9, 0xa20a7153),
+     TOBN(0x26e08c07, 0xe3533d77), TOBN(0xd7222e6a, 0x2e341c99),
+     TOBN(0x9d60ec3d, 0x8d2dc4ed), TOBN(0xbdfe0d8f, 0x7c476cf8),
+     TOBN(0x1fe59ab6, 0x1d056605), TOBN(0xa9ea9df6, 0x86a8551f),
+     TOBN(0x8489941e, 0x47fb8d8c), TOBN(0xfeb874eb, 0x4a7f1b10),
+     TOBN(0xfe5fea86, 0x7ee0d98f), TOBN(0x201ad34b, 0xdbf61864),
+     TOBN(0x45d8fe47, 0x37c031d4), TOBN(0xd5f49fae, 0x795f0822),
+     TOBN(0xdb0fb291, 0xc7f4a40c), TOBN(0x2e69d9c1, 0x730ddd92),
+     TOBN(0x754e1054, 0x49d76987), TOBN(0x8a24911d, 0x7662db87),
+     TOBN(0x61fc1810, 0x60a71676), TOBN(0xe852d1a8, 0xf66a8ad1),
+     TOBN(0x172bbd65, 0x6417231e), TOBN(0x0d6de7bd, 0x3babb11f),
+     TOBN(0x6fde6f88, 0xc8e347f8), TOBN(0x1c587547, 0x9bd99cc3),
+     TOBN(0x78e54ed0, 0x34076950), TOBN(0x97f0f334, 0x796e83ba),
+     TOBN(0xe4dbe1ce, 0x4924867a), TOBN(0xbd5f51b0, 0x60b84917),
+     TOBN(0x37530040, 0x3cb09a79), TOBN(0xdb3fe0f8, 0xff1743d8),
+     TOBN(0xed7894d8, 0x556fa9db), TOBN(0xfa262169, 0x23412fbf),
+     TOBN(0x563be0db, 0xba7b9291), TOBN(0x6ca8b8c0, 0x0c9fb234),
+     TOBN(0xed406aa9, 0xbd763802), TOBN(0xc21486a0, 0x65303da1),
+     TOBN(0x61ae291e, 0xc7e62ec4), TOBN(0x622a0492, 0xdf99333e),
+     TOBN(0x7fd80c9d, 0xbb7a8ee0), TOBN(0xdc2ed3bc, 0x6c01aedb),
+     TOBN(0x35c35a12, 0x08be74ec), TOBN(0xd540cb1a, 0x469f671f),
+     TOBN(0xd16ced4e, 0xcf84f6c7), TOBN(0x8561fb9c, 0x2d090f43),
+     TOBN(0x7e693d79, 0x6f239db4), TOBN(0xa736f928, 0x77bd0d94),
+     TOBN(0x07b4d929, 0x2c1950ee), TOBN(0xda177543, 0x56dc11b3),
+     TOBN(0xa5dfbbaa, 0x7a6a878e), TOBN(0x1c70cb29, 0x4decb08a),
+     TOBN(0xfba28c8b, 0x6f0f7c50), TOBN(0xa8eba2b8, 0x854dcc6d),
+     TOBN(0x5ff8e89a, 0x36b78642), TOBN(0x070c1c8e, 0xf6873adf),
+     TOBN(0xbbd3c371, 0x6484d2e4), TOBN(0xfb78318f, 0x0d414129),
+     TOBN(0x2621a39c, 0x6ad93b0b), TOBN(0x979d74c2, 0xa9e917f7),
+     TOBN(0xfc195647, 0x61fb0428), TOBN(0x4d78954a, 0xbee624d4),
+     TOBN(0xb94896e0, 0xb8ae86fd), TOBN(0x6667ac0c, 0xc91c8b13),
+     TOBN(0x9f180512, 0x43bcf832), TOBN(0xfbadf8b7, 0xa0010137),
+     TOBN(0xc69b4089, 0xb3ba8aa7), TOBN(0xfac4bacd, 0xe687ce85),
+     TOBN(0x9164088d, 0x977eab40), TOBN(0x51f4c5b6, 0x2760b390),
+     TOBN(0xd238238f, 0x340dd553), TOBN(0x358566c3, 0xdb1d31c9),
+     TOBN(0x3a5ad69e, 0x5068f5ff), TOBN(0xf31435fc, 0xdaff6b06),
+     TOBN(0xae549a5b, 0xd6debff0), TOBN(0x59e5f0b7, 0x75e01331),
+     TOBN(0x5d492fb8, 0x98559acf), TOBN(0x96018c2e, 0x4db79b50),
+     TOBN(0x55f4a48f, 0x609f66aa), TOBN(0x1943b3af, 0x4900a14f),
+     TOBN(0xc22496df, 0x15a40d39), TOBN(0xb2a44684, 0x4c20f7c5),
+     TOBN(0x76a35afa, 0x3b98404c), TOBN(0xbec75725, 0xff5d1b77),
+     TOBN(0xb67aa163, 0xbea06444), TOBN(0x27e95bb2, 0xf724b6f2),
+     TOBN(0x3c20e3e9, 0xd238c8ab), TOBN(0x1213754e, 0xddd6ae17),
+     TOBN(0x8c431020, 0x716e0f74), TOBN(0x6679c82e, 0xffc095c2),
+     TOBN(0x2eb3adf4, 0xd0ac2932), TOBN(0x2cc970d3, 0x01bb7a76),
+     TOBN(0x70c71f2f, 0x740f0e66), TOBN(0x545c616b, 0x2b6b23cc),
+     TOBN(0x4528cfcb, 0xb40a8bd7), TOBN(0xff839633, 0x2ab27722),
+     TOBN(0x049127d9, 0x025ac99a), TOBN(0xd314d4a0, 0x2b63e33b),
+     TOBN(0xc8c310e7, 0x28d84519), TOBN(0x0fcb8983, 0xb3bc84ba),
+     TOBN(0x2cc52261, 0x38634818), TOBN(0x501814f4, 0xb44c2e0b),
+     TOBN(0xf7e181aa, 0x54dfdba3), TOBN(0xcfd58ff0, 0xe759718c),
+     TOBN(0xf90cdb14, 0xd3b507a8), TOBN(0x57bd478e, 0xc50bdad8),
+     TOBN(0x29c197e2, 0x50e5f9aa), TOBN(0x4db6eef8, 0xe40bc855),
+     TOBN(0x2cc8f21a, 0xd1fc0654), TOBN(0xc71cc963, 0x81269d73),
+     TOBN(0xecfbb204, 0x077f49f9), TOBN(0xdde92571, 0xca56b793),
+     TOBN(0x9abed6a3, 0xf97ad8f7), TOBN(0xe6c19d3f, 0x924de3bd),
+     TOBN(0x8dce92f4, 0xa140a800), TOBN(0x85f44d1e, 0x1337af07),
+     TOBN(0x5953c08b, 0x09d64c52), TOBN(0xa1b5e49f, 0xf5df9749),
+     TOBN(0x336a8fb8, 0x52735f7d), TOBN(0xb332b6db, 0x9add676b),
+     TOBN(0x558b88a0, 0xb4511aa4), TOBN(0x09788752, 0xdbd5cc55),
+     TOBN(0x16b43b9c, 0xd8cd52bd), TOBN(0x7f0bc5a0, 0xc2a2696b),
+     TOBN(0x146e12d4, 0xc11f61ef), TOBN(0x9ce10754, 0x3a83e79e),
+     TOBN(0x08ec73d9, 0x6cbfca15), TOBN(0x09ff29ad, 0x5b49653f),
+     TOBN(0xe31b72bd, 0xe7da946e), TOBN(0xebf9eb3b, 0xee80a4f2),
+     TOBN(0xd1aabd08, 0x17598ce4), TOBN(0x18b5fef4, 0x53f37e80),
+     TOBN(0xd5d5cdd3, 0x5958cd79), TOBN(0x3580a1b5, 0x1d373114),
+     TOBN(0xa36e4c91, 0xfa935726), TOBN(0xa38c534d, 0xef20d760),
+     TOBN(0x7088e40a, 0x2ff5845b), TOBN(0xe5bb40bd, 0xbd78177f),
+     TOBN(0x4f06a7a8, 0x857f9920), TOBN(0xe3cc3e50, 0xe968f05d),
+     TOBN(0x1d68b7fe, 0xe5682d26), TOBN(0x5206f76f, 0xaec7f87c),
+     TOBN(0x41110530, 0x041951ab), TOBN(0x58ec52c1, 0xd4b5a71a),
+     TOBN(0xf3488f99, 0x0f75cf9a), TOBN(0xf411951f, 0xba82d0d5),
+     TOBN(0x27ee75be, 0x618895ab), TOBN(0xeae060d4, 0x6d8aab14),
+     TOBN(0x9ae1df73, 0x7fb54dc2), TOBN(0x1f3e391b, 0x25963649),
+     TOBN(0x242ec32a, 0xfe055081), TOBN(0x5bd450ef, 0x8491c9bd),
+     TOBN(0x367efc67, 0x981eb389), TOBN(0xed7e1928, 0x3a0550d5),
+     TOBN(0x362e776b, 0xab3ce75c), TOBN(0xe890e308, 0x1f24c523),
+     TOBN(0xb961b682, 0xfeccef76), TOBN(0x8b8e11f5, 0x8bba6d92),
+     TOBN(0x8f2ccc4c, 0x2b2375c4), TOBN(0x0d7f7a52, 0xe2f86cfa),
+     TOBN(0xfd94d30a, 0x9efe5633), TOBN(0x2d8d246b, 0x5451f934),
+     TOBN(0x2234c6e3, 0x244e6a00), TOBN(0xde2b5b0d, 0xddec8c50),
+     TOBN(0x2ce53c5a, 0xbf776f5b), TOBN(0x6f724071, 0x60357b05),
+     TOBN(0xb2593717, 0x71bf3f7a), TOBN(0x87d2501c, 0x440c4a9f),
+     TOBN(0x440552e1, 0x87b05340), TOBN(0xb7bf7cc8, 0x21624c32),
+     TOBN(0x4155a6ce, 0x22facddb), TOBN(0x5a4228cb, 0x889837ef),
+     TOBN(0xef87d6d6, 0xfd4fd671), TOBN(0xa233687e, 0xc2daa10e),
+     TOBN(0x75622244, 0x03c0eb96), TOBN(0x7632d184, 0x8bf19be6),
+     TOBN(0x05d0f8e9, 0x40735ff4), TOBN(0x3a3e6e13, 0xc00931f1),
+     TOBN(0x31ccde6a, 0xdafe3f18), TOBN(0xf381366a, 0xcfe51207),
+     TOBN(0x24c222a9, 0x60167d92), TOBN(0x62f9d6f8, 0x7529f18c),
+     TOBN(0x412397c0, 0x0353b114), TOBN(0x334d89dc, 0xef808043),
+     TOBN(0xd9ec63ba, 0x2a4383ce), TOBN(0xcec8e937, 0x5cf92ba0),
+     TOBN(0xfb8b4288, 0xc8be74c0), TOBN(0x67d6912f, 0x105d4391),
+     TOBN(0x7b996c46, 0x1b913149), TOBN(0x36aae2ef, 0x3a4e02da),
+     TOBN(0xb68aa003, 0x972de594), TOBN(0x284ec70d, 0x4ec6d545),
+     TOBN(0xf3d2b2d0, 0x61391d54), TOBN(0x69c5d5d6, 0xfe114e92),
+     TOBN(0xbe0f00b5, 0xb4482dff), TOBN(0xe1596fa5, 0xf5bf33c5),
+     TOBN(0x10595b56, 0x96a71cba), TOBN(0x944938b2, 0xfdcadeb7),
+     TOBN(0xa282da4c, 0xfccd8471), TOBN(0x98ec05f3, 0x0d37bfe1),
+     TOBN(0xe171ce1b, 0x0698304a), TOBN(0x2d691444, 0x21bdf79b),
+     TOBN(0xd0cd3b74, 0x1b21dec1), TOBN(0x712ecd8b, 0x16a15f71),
+     TOBN(0x8d4c00a7, 0x00fd56e1), TOBN(0x02ec9692, 0xf9527c18),
+     TOBN(0x21c44937, 0x4a3e42e1), TOBN(0x9176fbab, 0x1392ae0a),
+     TOBN(0x8726f1ba, 0x44b7b618), TOBN(0xb4d7aae9, 0xf1de491c),
+     TOBN(0xf91df7b9, 0x07b582c0), TOBN(0x7e116c30, 0xef60aa3a),
+     TOBN(0x99270f81, 0x466265d7), TOBN(0xb15b6fe2, 0x4df7adf0),
+     TOBN(0xfe33b2d3, 0xf9738f7f), TOBN(0x48553ab9, 0xd6d70f95),
+     TOBN(0x2cc72ac8, 0xc21e94db), TOBN(0x795ac38d, 0xbdc0bbee),
+     TOBN(0x0a1be449, 0x2e40478f), TOBN(0x81bd3394, 0x052bde55),
+     TOBN(0x63c8dbe9, 0x56b3c4f2), TOBN(0x017a99cf, 0x904177cc),
+     TOBN(0x947bbddb, 0x4d010fc1), TOBN(0xacf9b00b, 0xbb2c9b21),
+     TOBN(0x2970bc8d, 0x47173611), TOBN(0x1a4cbe08, 0xac7d756f),
+     TOBN(0x06d9f4aa, 0x67d541a2), TOBN(0xa3e8b689, 0x59c2cf44),
+     TOBN(0xaad066da, 0x4d88f1dd), TOBN(0xc604f165, 0x7ad35dea),
+     TOBN(0x7edc0720, 0x4478ca67), TOBN(0xa10dfae0, 0xba02ce06),
+     TOBN(0xeceb1c76, 0xaf36f4e4), TOBN(0x994b2292, 0xaf3f8f48),
+     TOBN(0xbf9ed77b, 0x77c8a68c), TOBN(0x74f544ea, 0x51744c9d),
+     TOBN(0x82d05bb9, 0x8113a757), TOBN(0x4ef2d2b4, 0x8a9885e4),
+     TOBN(0x1e332be5, 0x1aa7865f), TOBN(0x22b76b18, 0x290d1a52),
+     TOBN(0x308a2310, 0x44351683), TOBN(0x9d861896, 0xa3f22840),
+     TOBN(0x5959ddcd, 0x841ed947), TOBN(0x0def0c94, 0x154b73bf),
+     TOBN(0xf0105417, 0x4c7c15e0), TOBN(0x539bfb02, 0x3a277c32),
+     TOBN(0xe699268e, 0xf9dccf5f), TOBN(0x9f5796a5, 0x0247a3bd),
+     TOBN(0x8b839de8, 0x4f157269), TOBN(0xc825c1e5, 0x7a30196b),
+     TOBN(0x6ef0aabc, 0xdc8a5a91), TOBN(0xf4a8ce6c, 0x498b7fe6),
+     TOBN(0x1cce35a7, 0x70cbac78), TOBN(0x83488e9b, 0xf6b23958),
+     TOBN(0x0341a070, 0xd76cb011), TOBN(0xda6c9d06, 0xae1b2658),
+     TOBN(0xb701fb30, 0xdd648c52), TOBN(0x994ca02c, 0x52fb9fd1),
+     TOBN(0x06933117, 0x6f563086), TOBN(0x3d2b8100, 0x17856bab),
+     TOBN(0xe89f48c8, 0x5963a46e), TOBN(0x658ab875, 0xa99e61c7),
+     TOBN(0x6e296f87, 0x4b8517b4), TOBN(0x36c4fcdc, 0xfc1bc656),
+     TOBN(0xde5227a1, 0xa3906def), TOBN(0x9fe95f57, 0x62418945),
+     TOBN(0x20c91e81, 0xfdd96cde), TOBN(0x5adbe47e, 0xda4480de),
+     TOBN(0xa009370f, 0x396de2b6), TOBN(0x98583d4b, 0xf0ecc7bd),
+     TOBN(0xf44f6b57, 0xe51d0672), TOBN(0x03d6b078, 0x556b1984),
+     TOBN(0x27dbdd93, 0xb0b64912), TOBN(0x9b3a3434, 0x15687b09),
+     TOBN(0x0dba6461, 0x51ec20a9), TOBN(0xec93db7f, 0xff28187c),
+     TOBN(0x00ff8c24, 0x66e48bdd), TOBN(0x2514f2f9, 0x11ccd78e),
+     TOBN(0xeba11f4f, 0xe1250603), TOBN(0x8a22cd41, 0x243fa156),
+     TOBN(0xa4e58df4, 0xb283e4c6), TOBN(0x78c29859, 0x8b39783f),
+     TOBN(0x5235aee2, 0xa5259809), TOBN(0xc16284b5, 0x0e0227dd),
+     TOBN(0xa5f57916, 0x1338830d), TOBN(0x6d4b8a6b, 0xd2123fca),
+     TOBN(0x236ea68a, 0xf9c546f8), TOBN(0xc1d36873, 0xfa608d36),
+     TOBN(0xcd76e495, 0x8d436d13), TOBN(0xd4d9c221, 0x8fb080af),
+     TOBN(0x665c1728, 0xe8ad3fb5), TOBN(0xcf1ebe4d, 0xb3d572e0),
+     TOBN(0xa7a8746a, 0x584c5e20), TOBN(0x267e4ea1, 0xb9dc7035),
+     TOBN(0x593a15cf, 0xb9548c9b), TOBN(0x5e6e2135, 0x4bd012f3),
+     TOBN(0xdf31cc6a, 0x8c8f936e), TOBN(0x8af84d04, 0xb5c241dc),
+     TOBN(0x63990a6f, 0x345efb86), TOBN(0x6fef4e61, 0xb9b962cb)}
+    ,
+    {TOBN(0xf6368f09, 0x25722608), TOBN(0x131260db, 0x131cf5c6),
+     TOBN(0x40eb353b, 0xfab4f7ac), TOBN(0x85c78880, 0x37eee829),
+     TOBN(0x4c1581ff, 0xc3bdf24e), TOBN(0x5bff75cb, 0xf5c3c5a8),
+     TOBN(0x35e8c83f, 0xa14e6f40), TOBN(0xb81d1c0f, 0x0295e0ca),
+     TOBN(0xfcde7cc8, 0xf43a730f), TOBN(0xe89b6f3c, 0x33ab590e),
+     TOBN(0xc823f529, 0xad03240b), TOBN(0x82b79afe, 0x98bea5db),
+     TOBN(0x568f2856, 0x962fe5de), TOBN(0x0c590adb, 0x60c591f3),
+     TOBN(0x1fc74a14, 0x4a28a858), TOBN(0x3b662498, 0xb3203f4c),
+     TOBN(0x91e3cf0d, 0x6c39765a), TOBN(0xa2db3acd, 0xac3cca0b),
+     TOBN(0x288f2f08, 0xcb953b50), TOBN(0x2414582c, 0xcf43cf1a),
+     TOBN(0x8dec8bbc, 0x60eee9a8), TOBN(0x54c79f02, 0x729aa042),
+     TOBN(0xd81cd5ec, 0x6532f5d5), TOBN(0xa672303a, 0xcf82e15f),
+     TOBN(0x376aafa8, 0x719c0563), TOBN(0xcd8ad2dc, 0xbc5fc79f),
+     TOBN(0x303fdb9f, 0xcb750cd3), TOBN(0x14ff052f, 0x4418b08e),
+     TOBN(0xf75084cf, 0x3e2d6520), TOBN(0x7ebdf0f8, 0x144ed509),
+     TOBN(0xf43bf0f2, 0xd3f25b98), TOBN(0x86ad71cf, 0xa354d837),
+     TOBN(0xb827fe92, 0x26f43572), TOBN(0xdfd3ab5b, 0x5d824758),
+     TOBN(0x315dd23a, 0x539094c1), TOBN(0x85c0e37a, 0x66623d68),
+     TOBN(0x575c7972, 0x7be19ae0), TOBN(0x616a3396, 0xdf0d36b5),
+     TOBN(0xa1ebb3c8, 0x26b1ff7e), TOBN(0x635b9485, 0x140ad453),
+     TOBN(0x92bf3cda, 0xda430c0b), TOBN(0x4702850e, 0x3a96dac6),
+     TOBN(0xc91cf0a5, 0x15ac326a), TOBN(0x95de4f49, 0xab8c25e4),
+     TOBN(0xb01bad09, 0xe265c17c), TOBN(0x24e45464, 0x087b3881),
+     TOBN(0xd43e583c, 0xe1fac5ca), TOBN(0xe17cb318, 0x6ead97a6),
+     TOBN(0x6cc39243, 0x74dcec46), TOBN(0x33cfc02d, 0x54c2b73f),
+     TOBN(0x82917844, 0xf26cd99c), TOBN(0x8819dd95, 0xd1773f89),
+     TOBN(0x09572aa6, 0x0871f427), TOBN(0x8e0cf365, 0xf6f01c34),
+     TOBN(0x7fa52988, 0xbff1f5af), TOBN(0x4eb357ea, 0xe75e8e50),
+     TOBN(0xd9d0c8c4, 0x868af75d), TOBN(0xd7325cff, 0x45c8c7ea),
+     TOBN(0xab471996, 0xcc81ecb0), TOBN(0xff5d55f3, 0x611824ed),
+     TOBN(0xbe314541, 0x1977a0ee), TOBN(0x5085c4c5, 0x722038c6),
+     TOBN(0x2d5335bf, 0xf94bb495), TOBN(0x894ad8a6, 0xc8e2a082),
+     TOBN(0x5c3e2341, 0xada35438), TOBN(0xf4a9fc89, 0x049b8c4e),
+     TOBN(0xbeeb355a, 0x9f17cf34), TOBN(0x3f311e0e, 0x6c91fe10),
+     TOBN(0xc2d20038, 0x92ab9891), TOBN(0x257bdcc1, 0x3e8ce9a9),
+     TOBN(0x1b2d9789, 0x88c53bee), TOBN(0x927ce89a, 0xcdba143a),
+     TOBN(0xb0a32cca, 0x523db280), TOBN(0x5c889f8a, 0x50d43783),
+     TOBN(0x503e04b3, 0x4897d16f), TOBN(0x8cdb6e78, 0x08f5f2e8),
+     TOBN(0x6ab91cf0, 0x179c8e74), TOBN(0xd8874e52, 0x48211d60),
+     TOBN(0xf948d4d5, 0xea851200), TOBN(0x4076d41e, 0xe6f9840a),
+     TOBN(0xc20e263c, 0x47b517ea), TOBN(0x79a448fd, 0x30685e5e),
+     TOBN(0xe55f6f78, 0xf90631a0), TOBN(0x88a790b1, 0xa79e6346),
+     TOBN(0x62160c7d, 0x80969fe8), TOBN(0x54f92fd4, 0x41491bb9),
+     TOBN(0xa6645c23, 0x5c957526), TOBN(0xf44cc5ae, 0xbea3ce7b),
+     TOBN(0xf7628327, 0x8b1e68b7), TOBN(0xc731ad7a, 0x303f29d3),
+     TOBN(0xfe5a9ca9, 0x57d03ecb), TOBN(0x96c0d50c, 0x41bc97a7),
+     TOBN(0xc4669fe7, 0x9b4f7f24), TOBN(0xfdd781d8, 0x3d9967ef),
+     TOBN(0x7892c7c3, 0x5d2c208d), TOBN(0x8bf64f7c, 0xae545cb3),
+     TOBN(0xc01f862c, 0x467be912), TOBN(0xf4c85ee9, 0xc73d30cc),
+     TOBN(0x1fa6f4be, 0x6ab83ec7), TOBN(0xa07a3c1c, 0x4e3e3cf9),
+     TOBN(0x87f8ef45, 0x0c00beb3), TOBN(0x30e2c2b3, 0x000d4c3e),
+     TOBN(0x1aa00b94, 0xfe08bf5b), TOBN(0x32c133aa, 0x9224ef52),
+     TOBN(0x38df16bb, 0x32e5685d), TOBN(0x68a9e069, 0x58e6f544),
+     TOBN(0x495aaff7, 0xcdc5ebc6), TOBN(0xf894a645, 0x378b135f),
+     TOBN(0xf316350a, 0x09e27ecf), TOBN(0xeced201e, 0x58f7179d),
+     TOBN(0x2eec273c, 0xe97861ba), TOBN(0x47ec2cae, 0xd693be2e),
+     TOBN(0xfa4c97c4, 0xf68367ce), TOBN(0xe4f47d0b, 0xbe5a5755),
+     TOBN(0x17de815d, 0xb298a979), TOBN(0xd7eca659, 0xc177dc7d),
+     TOBN(0x20fdbb71, 0x49ded0a3), TOBN(0x4cb2aad4, 0xfb34d3c5),
+     TOBN(0x2cf31d28, 0x60858a33), TOBN(0x3b6873ef, 0xa24aa40f),
+     TOBN(0x540234b2, 0x2c11bb37), TOBN(0x2d0366dd, 0xed4c74a3),
+     TOBN(0xf9a968da, 0xeec5f25d), TOBN(0x36601068, 0x67b63142),
+     TOBN(0x07cd6d2c, 0x68d7b6d4), TOBN(0xa8f74f09, 0x0c842942),
+     TOBN(0xe2751404, 0x7768b1ee), TOBN(0x4b5f7e89, 0xfe62aee4),
+     TOBN(0xc6a77177, 0x89070d26), TOBN(0xa1f28e4e, 0xdd1c8bc7),
+     TOBN(0xea5f4f06, 0x469e1f17), TOBN(0x78fc242a, 0xfbdb78e0),
+     TOBN(0xc9c7c592, 0x8b0588f1), TOBN(0xb6b7a0fd, 0x1535921e),
+     TOBN(0xcc5bdb91, 0xbde5ae35), TOBN(0xb42c485e, 0x12ff1864),
+     TOBN(0xa1113e13, 0xdbab98aa), TOBN(0xde9d469b, 0xa17b1024),
+     TOBN(0x23f48b37, 0xc0462d3a), TOBN(0x3752e537, 0x7c5c078d),
+     TOBN(0xe3a86add, 0x15544eb9), TOBN(0xf013aea7, 0x80fba279),
+     TOBN(0x8b5bb76c, 0xf22001b5), TOBN(0xe617ba14, 0xf02891ab),
+     TOBN(0xd39182a6, 0x936219d3), TOBN(0x5ce1f194, 0xae51cb19),
+     TOBN(0xc78f8598, 0xbf07a74c), TOBN(0x6d7158f2, 0x22cbf1bc),
+     TOBN(0x3b846b21, 0xe300ce18), TOBN(0x35fba630, 0x2d11275d),
+     TOBN(0x5fe25c36, 0xa0239b9b), TOBN(0xd8beb35d, 0xdf05d940),
+     TOBN(0x4db02bb0, 0x1f7e320d), TOBN(0x0641c364, 0x6da320ea),
+     TOBN(0x6d95fa5d, 0x821389a3), TOBN(0x92699748, 0x8fcd8e3d),
+     TOBN(0x316fef17, 0xceb6c143), TOBN(0x67fcb841, 0xd933762b),
+     TOBN(0xbb837e35, 0x118b17f8), TOBN(0x4b92552f, 0x9fd24821),
+     TOBN(0xae6bc70e, 0x46aca793), TOBN(0x1cf0b0e4, 0xe579311b),
+     TOBN(0x8dc631be, 0x5802f716), TOBN(0x099bdc6f, 0xbddbee4d),
+     TOBN(0xcc352bb2, 0x0caf8b05), TOBN(0xf74d505a, 0x72d63df2),
+     TOBN(0xb9876d4b, 0x91c4f408), TOBN(0x1ce18473, 0x9e229b2d),
+     TOBN(0x49507597, 0x83abdb4a), TOBN(0x850fbcb6, 0xdee84b18),
+     TOBN(0x6325236e, 0x609e67dc), TOBN(0x04d831d9, 0x9336c6d8),
+     TOBN(0x8deaae3b, 0xfa12d45d), TOBN(0xe425f8ce, 0x4746e246),
+     TOBN(0x8004c175, 0x24f5f31e), TOBN(0xaca16d8f, 0xad62c3b7),
+     TOBN(0x0dc15a6a, 0x9152f934), TOBN(0xf1235e5d, 0xed0e12c1),
+     TOBN(0xc33c06ec, 0xda477dac), TOBN(0x76be8732, 0xb2ea0006),
+     TOBN(0xcf3f7831, 0x0c0cd313), TOBN(0x3c524553, 0xa614260d),
+     TOBN(0x31a756f8, 0xcab22d15), TOBN(0x03ee10d1, 0x77827a20),
+     TOBN(0xd1e059b2, 0x1994ef20), TOBN(0x2a653b69, 0x638ae318),
+     TOBN(0x70d5eb58, 0x2f699010), TOBN(0x279739f7, 0x09f5f84a),
+     TOBN(0x5da4663c, 0x8b799336), TOBN(0xfdfdf14d, 0x203c37eb),
+     TOBN(0x32d8a9dc, 0xa1dbfb2d), TOBN(0xab40cff0, 0x77d48f9b),
+     TOBN(0xc018b383, 0xd20b42d5), TOBN(0xf9a810ef, 0x9f78845f),
+     TOBN(0x40af3753, 0xbdba9df0), TOBN(0xb90bdcfc, 0x131dfdf9),
+     TOBN(0x18720591, 0xf01ab782), TOBN(0xc823f211, 0x6af12a88),
+     TOBN(0xa51b80f3, 0x0dc14401), TOBN(0xde248f77, 0xfb2dfbe3),
+     TOBN(0xef5a44e5, 0x0cafe751), TOBN(0x73997c9c, 0xd4dcd221),
+     TOBN(0x32fd86d1, 0xde854024), TOBN(0xd5b53adc, 0xa09b84bb),
+     TOBN(0x008d7a11, 0xdcedd8d1), TOBN(0x406bd1c8, 0x74b32c84),
+     TOBN(0x5d4472ff, 0x05dde8b1), TOBN(0x2e25f2cd, 0xfce2b32f),
+     TOBN(0xbec0dd5e, 0x29dfc254), TOBN(0x4455fcf6, 0x2b98b267),
+     TOBN(0x0b4d43a5, 0xc72df2ad), TOBN(0xea70e6be, 0x48a75397),
+     TOBN(0x2aad6169, 0x5820f3bf), TOBN(0xf410d2dd, 0x9e37f68f),
+     TOBN(0x70fb7dba, 0x7be5ac83), TOBN(0x636bb645, 0x36ec3eec),
+     TOBN(0x27104ea3, 0x9754e21c), TOBN(0xbc87a3e6, 0x8d63c373),
+     TOBN(0x483351d7, 0x4109db9a), TOBN(0x0fa724e3, 0x60134da7),
+     TOBN(0x9ff44c29, 0xb0720b16), TOBN(0x2dd0cf13, 0x06aceead),
+     TOBN(0x5942758c, 0xe26929a6), TOBN(0x96c5db92, 0xb766a92b),
+     TOBN(0xcec7d4c0, 0x5f18395e), TOBN(0xd3f22744, 0x1f80d032),
+     TOBN(0x7a68b37a, 0xcb86075b), TOBN(0x074764dd, 0xafef92db),
+     TOBN(0xded1e950, 0x7bc7f389), TOBN(0xc580c850, 0xb9756460),
+     TOBN(0xaeeec2a4, 0x7da48157), TOBN(0x3f0b4e7f, 0x82c587b3),
+     TOBN(0x231c6de8, 0xa9f19c53), TOBN(0x5717bd73, 0x6974e34e),
+     TOBN(0xd9e1d216, 0xf1508fa9), TOBN(0x9f112361, 0xdadaa124),
+     TOBN(0x80145e31, 0x823b7348), TOBN(0x4dd8f0d5, 0xac634069),
+     TOBN(0xe3d82fc7, 0x2297c258), TOBN(0x276fcfee, 0x9cee7431),
+     TOBN(0x8eb61b5e, 0x2bc0aea9), TOBN(0x4f668fd5, 0xde329431),
+     TOBN(0x03a32ab1, 0x38e4b87e), TOBN(0xe1374517, 0x73d0ef0b),
+     TOBN(0x1a46f7e6, 0x853ac983), TOBN(0xc3bdf42e, 0x68e78a57),
+     TOBN(0xacf20785, 0x2ea96dd1), TOBN(0xa10649b9, 0xf1638460),
+     TOBN(0xf2369f0b, 0x879fbbed), TOBN(0x0ff0ae86, 0xda9d1869),
+     TOBN(0x5251d759, 0x56766f45), TOBN(0x4984d8c0, 0x2be8d0fc),
+     TOBN(0x7ecc95a6, 0xd21008f0), TOBN(0x29bd54a0, 0x3a1a1c49),
+     TOBN(0xab9828c5, 0xd26c50f3), TOBN(0x32c0087c, 0x51d0d251),
+     TOBN(0x9bac3ce6, 0x0c1cdb26), TOBN(0xcd94d947, 0x557ca205),
+     TOBN(0x1b1bd598, 0x9db1fdcd), TOBN(0x0eda0108, 0xa3d8b149),
+     TOBN(0x95066610, 0x56152fcc), TOBN(0xc2f037e6, 0xe7192b33),
+     TOBN(0xdeffb41a, 0xc92e05a4), TOBN(0x1105f6c2, 0xc2f6c62e),
+     TOBN(0x68e73500, 0x8733913c), TOBN(0xcce86163, 0x3f3adc40),
+     TOBN(0xf407a942, 0x38a278e9), TOBN(0xd13c1b9d, 0x2ab21292),
+     TOBN(0x93ed7ec7, 0x1c74cf5c), TOBN(0x8887dc48, 0xf1a4c1b4),
+     TOBN(0x3830ff30, 0x4b3a11f1), TOBN(0x358c5a3c, 0x58937cb6),
+     TOBN(0x027dc404, 0x89022829), TOBN(0x40e93977, 0x3b798f79),
+     TOBN(0x90ad3337, 0x38be6ead), TOBN(0x9c23f6bc, 0xf34c0a5d),
+     TOBN(0xd1711a35, 0xfbffd8bb), TOBN(0x60fcfb49, 0x1949d3dd),
+     TOBN(0x09c8ef4b, 0x7825d93a), TOBN(0x24233cff, 0xa0a8c968),
+     TOBN(0x67ade46c, 0xe6d982af), TOBN(0xebb6bf3e, 0xe7544d7c),
+     TOBN(0xd6b9ba76, 0x3d8bd087), TOBN(0x46fe382d, 0x4dc61280),
+     TOBN(0xbd39a7e8, 0xb5bdbd75), TOBN(0xab381331, 0xb8f228fe),
+     TOBN(0x0709a77c, 0xce1c4300), TOBN(0x6a247e56, 0xf337ceac),
+     TOBN(0x8f34f21b, 0x636288be), TOBN(0x9dfdca74, 0xc8a7c305),
+     TOBN(0x6decfd1b, 0xea919e04), TOBN(0xcdf2688d, 0x8e1991f8),
+     TOBN(0xe607df44, 0xd0f8a67e), TOBN(0xd985df4b, 0x0b58d010),
+     TOBN(0x57f834c5, 0x0c24f8f4), TOBN(0xe976ef56, 0xa0bf01ae),
+     TOBN(0x536395ac, 0xa1c32373), TOBN(0x351027aa, 0x734c0a13),
+     TOBN(0xd2f1b5d6, 0x5e6bd5bc), TOBN(0x2b539e24, 0x223debed),
+     TOBN(0xd4994cec, 0x0eaa1d71), TOBN(0x2a83381d, 0x661dcf65),
+     TOBN(0x5f1aed2f, 0x7b54c740), TOBN(0x0bea3fa5, 0xd6dda5ee),
+     TOBN(0x9d4fb684, 0x36cc6134), TOBN(0x8eb9bbf3, 0xc0a443dd),
+     TOBN(0xfc500e2e, 0x383b7d2a), TOBN(0x7aad621c, 0x5b775257),
+     TOBN(0x69284d74, 0x0a8f7cc0), TOBN(0xe820c2ce, 0x07562d65),
+     TOBN(0xbf9531b9, 0x499758ee), TOBN(0x73e95ca5, 0x6ee0cc2d),
+     TOBN(0xf61790ab, 0xfbaf50a5), TOBN(0xdf55e76b, 0x684e0750),
+     TOBN(0xec516da7, 0xf176b005), TOBN(0x575553bb, 0x7a2dddc7),
+     TOBN(0x37c87ca3, 0x553afa73), TOBN(0x315f3ffc, 0x4d55c251),
+     TOBN(0xe846442a, 0xaf3e5d35), TOBN(0x61b91149, 0x6495ff28),
+     TOBN(0x23cc95d3, 0xfa326dc3), TOBN(0x1df4da1f, 0x18fc2cea),
+     TOBN(0x24bf9adc, 0xd0a37d59), TOBN(0xb6710053, 0x320d6e1e),
+     TOBN(0x96f9667e, 0x618344d1), TOBN(0xcc7ce042, 0xa06445af),
+     TOBN(0xa02d8514, 0xd68dbc3a), TOBN(0x4ea109e4, 0x280b5a5b),
+     TOBN(0x5741a7ac, 0xb40961bf), TOBN(0x4ada5937, 0x6aa56bfa),
+     TOBN(0x7feb9145, 0x02b765d1), TOBN(0x561e97be, 0xe6ad1582),
+     TOBN(0xbbc4a5b6, 0xda3982f5), TOBN(0x0c2659ed, 0xb546f468),
+     TOBN(0xb8e7e6aa, 0x59612d20), TOBN(0xd83dfe20, 0xac19e8e0),
+     TOBN(0x8530c45f, 0xb835398c), TOBN(0x6106a8bf, 0xb38a41c2),
+     TOBN(0x21e8f9a6, 0x35f5dcdb), TOBN(0x39707137, 0xcae498ed),
+     TOBN(0x70c23834, 0xd8249f00), TOBN(0x9f14b58f, 0xab2537a0),
+     TOBN(0xd043c365, 0x5f61c0c2), TOBN(0xdc5926d6, 0x09a194a7),
+     TOBN(0xddec0339, 0x8e77738a), TOBN(0xd07a63ef, 0xfba46426),
+     TOBN(0x2e58e79c, 0xee7f6e86), TOBN(0xe59b0459, 0xff32d241),
+     TOBN(0xc5ec84e5, 0x20fa0338), TOBN(0x97939ac8, 0xeaff5ace),
+     TOBN(0x0310a4e3, 0xb4a38313), TOBN(0x9115fba2, 0x8f9d9885),
+     TOBN(0x8dd710c2, 0x5fadf8c3), TOBN(0x66be38a2, 0xce19c0e2),
+     TOBN(0xd42a279c, 0x4cfe5022), TOBN(0x597bb530, 0x0e24e1b8),
+     TOBN(0x3cde86b7, 0xc153ca7f), TOBN(0xa8d30fb3, 0x707d63bd),
+     TOBN(0xac905f92, 0xbd60d21e), TOBN(0x98e7ffb6, 0x7b9a54ab),
+     TOBN(0xd7147df8, 0xe9726a30), TOBN(0xb5e216ff, 0xafce3533),
+     TOBN(0xb550b799, 0x2ff1ec40), TOBN(0x6b613b87, 0xa1e953fd),
+     TOBN(0x87b88dba, 0x792d5610), TOBN(0x2ee1270a, 0xa190fbe1),
+     TOBN(0x02f4e2dc, 0x2ef581da), TOBN(0x016530e4, 0xeff82a95),
+     TOBN(0xcbb93dfd, 0x8fd6ee89), TOBN(0x16d3d986, 0x46848fff),
+     TOBN(0x600eff24, 0x1da47adf), TOBN(0x1b9754a0, 0x0ad47a71),
+     TOBN(0x8f9266df, 0x70c33b98), TOBN(0xaadc87ae, 0xdf34186e),
+     TOBN(0x0d2ce8e1, 0x4ad24132), TOBN(0x8a47cbfc, 0x19946eba),
+     TOBN(0x47feeb66, 0x62b5f3af), TOBN(0xcefab561, 0x0abb3734),
+     TOBN(0x449de60e, 0x19f35cb1), TOBN(0x39f8db14, 0x157f0eb9),
+     TOBN(0xffaecc5b, 0x3c61bfd6), TOBN(0xa5a4d41d, 0x41216703),
+     TOBN(0x7f8fabed, 0x224e1cc2), TOBN(0x0d5a8186, 0x871ad953),
+     TOBN(0xf10774f7, 0xd22da9a9), TOBN(0x45b8a678, 0xcc8a9b0d),
+     TOBN(0xd9c2e722, 0xbdc32cff), TOBN(0xbf71b5f5, 0x337202a5),
+     TOBN(0x95c57f2f, 0x69fc4db9), TOBN(0xb6dad34c, 0x765d01e1),
+     TOBN(0x7e0bd13f, 0xcb904635), TOBN(0x61751253, 0x763a588c),
+     TOBN(0xd85c2997, 0x81af2c2d), TOBN(0xc0f7d9c4, 0x81b9d7da),
+     TOBN(0x838a34ae, 0x08533e8d), TOBN(0x15c4cb08, 0x311d8311),
+     TOBN(0x97f83285, 0x8e121e14), TOBN(0xeea7dc1e, 0x85000a5f),
+     TOBN(0x0c6059b6, 0x5d256274), TOBN(0xec9beace, 0xb95075c0),
+     TOBN(0x173daad7, 0x1df97828), TOBN(0xbf851cb5, 0xa8937877),
+     TOBN(0xb083c594, 0x01646f3c), TOBN(0x3bad30cf, 0x50c6d352),
+     TOBN(0xfeb2b202, 0x496bbcea), TOBN(0x3cf9fd4f, 0x18a1e8ba),
+     TOBN(0xd26de7ff, 0x1c066029), TOBN(0x39c81e9e, 0x4e9ed4f8),
+     TOBN(0xd8be0cb9, 0x7b390d35), TOBN(0x01df2bbd, 0x964aab27),
+     TOBN(0x3e8c1a65, 0xc3ef64f8), TOBN(0x567291d1, 0x716ed1dd),
+     TOBN(0x95499c6c, 0x5f5406d3), TOBN(0x71fdda39, 0x5ba8e23f),
+     TOBN(0xcfeb320e, 0xd5096ece), TOBN(0xbe7ba92b, 0xca66dd16),
+     TOBN(0x4608d36b, 0xc6fb5a7d), TOBN(0xe3eea15a, 0x6d2dd0e0),
+     TOBN(0x75b0a3eb, 0x8f97a36a), TOBN(0xf59814cc, 0x1c83de1e),
+     TOBN(0x56c9c5b0, 0x1c33c23f), TOBN(0xa96c1da4, 0x6faa4136),
+     TOBN(0x46bf2074, 0xde316551), TOBN(0x3b866e7b, 0x1f756c8f),
+     TOBN(0x727727d8, 0x1495ed6b), TOBN(0xb2394243, 0xb682dce7),
+     TOBN(0x8ab8454e, 0x758610f3), TOBN(0xc243ce84, 0x857d72a4),
+     TOBN(0x7b320d71, 0xdbbf370f), TOBN(0xff9afa37, 0x78e0f7ca),
+     TOBN(0x0119d1e0, 0xea7b523f), TOBN(0xb997f8cb, 0x058c7d42),
+     TOBN(0x285bcd2a, 0x37bbb184), TOBN(0x51dcec49, 0xa45d1fa6),
+     TOBN(0x6ade3b64, 0xe29634cb), TOBN(0x080c94a7, 0x26b86ef1),
+     TOBN(0xba583db1, 0x2283fbe3), TOBN(0x902bddc8, 0x5a9315ed),
+     TOBN(0x07c1ccb3, 0x86964bec), TOBN(0x78f4eacf, 0xb6258301),
+     TOBN(0x4bdf3a49, 0x56f90823), TOBN(0xba0f5080, 0x741d777b),
+     TOBN(0x091d71c3, 0xf38bf760), TOBN(0x9633d50f, 0x9b625b02),
+     TOBN(0x03ecb743, 0xb8c9de61), TOBN(0xb4751254, 0x5de74720),
+     TOBN(0x9f9defc9, 0x74ce1cb2), TOBN(0x774a4f6a, 0x00bd32ef),
+     TOBN(0xaca385f7, 0x73848f22), TOBN(0x53dad716, 0xf3f8558e),
+     TOBN(0xab7b34b0, 0x93c471f9), TOBN(0xf530e069, 0x19644bc7),
+     TOBN(0x3d9fb1ff, 0xdd59d31a), TOBN(0x4382e0df, 0x08daa795),
+     TOBN(0x165c6f4b, 0xd5cc88d7), TOBN(0xeaa392d5, 0x4a18c900),
+     TOBN(0x94203c67, 0x648024ee), TOBN(0x188763f2, 0x8c2fabcd),
+     TOBN(0xa80f87ac, 0xbbaec835), TOBN(0x632c96e0, 0xf29d8d54),
+     TOBN(0x29b0a60e, 0x4c00a95e), TOBN(0x2ef17f40, 0xe011e9fa),
+     TOBN(0xf6c0e1d1, 0x15b77223), TOBN(0xaaec2c62, 0x14b04e32),
+     TOBN(0xd35688d8, 0x3d84e58c), TOBN(0x2af5094c, 0x958571db),
+     TOBN(0x4fff7e19, 0x760682a6), TOBN(0x4cb27077, 0xe39a407c),
+     TOBN(0x0f59c547, 0x4ff0e321), TOBN(0x169f34a6, 0x1b34c8ff),
+     TOBN(0x2bff1096, 0x52bc1ba7), TOBN(0xa25423b7, 0x83583544),
+     TOBN(0x5d55d5d5, 0x0ac8b782), TOBN(0xff6622ec, 0x2db3c892),
+     TOBN(0x48fce741, 0x6b8bb642), TOBN(0x31d6998c, 0x69d7e3dc),
+     TOBN(0xdbaf8004, 0xcadcaed0), TOBN(0x801b0142, 0xd81d053c),
+     TOBN(0x94b189fc, 0x59630ec6), TOBN(0x120e9934, 0xaf762c8e),
+     TOBN(0x53a29aa4, 0xfdc6a404), TOBN(0x19d8e01e, 0xa1909948),
+     TOBN(0x3cfcabf1, 0xd7e89681), TOBN(0x3321a50d, 0x4e132d37),
+     TOBN(0xd0496863, 0xe9a86111), TOBN(0x8c0cde61, 0x06a3bc65),
+     TOBN(0xaf866c49, 0xfc9f8eef), TOBN(0x2066350e, 0xff7f5141),
+     TOBN(0x4f8a4689, 0xe56ddfbd), TOBN(0xea1b0c07, 0xfe32983a),
+     TOBN(0x2b317462, 0x873cb8cb), TOBN(0x658deddc, 0x2d93229f),
+     TOBN(0x65efaf4d, 0x0f64ef58), TOBN(0xfe43287d, 0x730cc7a8),
+     TOBN(0xaebc0c72, 0x3d047d70), TOBN(0x92efa539, 0xd92d26c9),
+     TOBN(0x06e78457, 0x94b56526), TOBN(0x415cb80f, 0x0961002d),
+     TOBN(0x89e5c565, 0x76dcb10f), TOBN(0x8bbb6982, 0xff9259fe),
+     TOBN(0x4fe8795b, 0x9abc2668), TOBN(0xb5d4f534, 0x1e678fb1),
+     TOBN(0x6601f3be, 0x7b7da2b9), TOBN(0x98da59e2, 0xa13d6805),
+     TOBN(0x190d8ea6, 0x01799a52), TOBN(0xa20cec41, 0xb86d2952),
+     TOBN(0x3062ffb2, 0x7fff2a7c), TOBN(0x741b32e5, 0x79f19d37),
+     TOBN(0xf80d8181, 0x4eb57d47), TOBN(0x7a2d0ed4, 0x16aef06b),
+     TOBN(0x09735fb0, 0x1cecb588), TOBN(0x1641caaa, 0xc6061f5b)}
+    ,
+    {TOBN(0x7f99824f, 0x20151427), TOBN(0x206828b6, 0x92430206),
+     TOBN(0xaa9097d7, 0xe1112357), TOBN(0xacf9a2f2, 0x09e414ec),
+     TOBN(0xdbdac9da, 0x27915356), TOBN(0x7e0734b7, 0x001efee3),
+     TOBN(0x54fab5bb, 0xd2b288e2), TOBN(0x4c630fc4, 0xf62dd09c),
+     TOBN(0x8537107a, 0x1ac2703b), TOBN(0xb49258d8, 0x6bc857b5),
+     TOBN(0x57df14de, 0xbcdaccd1), TOBN(0x24ab68d7, 0xc4ae8529),
+     TOBN(0x7ed8b5d4, 0x734e59d0), TOBN(0x5f8740c8, 0xc495cc80),
+     TOBN(0x84aedd5a, 0x291db9b3), TOBN(0x80b360f8, 0x4fb995be),
+     TOBN(0xae915f5d, 0x5fa067d1), TOBN(0x4134b57f, 0x9668960c),
+     TOBN(0xbd3656d6, 0xa48edaac), TOBN(0xdac1e3e4, 0xfc1d7436),
+     TOBN(0x674ff869, 0xd81fbb26), TOBN(0x449ed3ec, 0xb26c33d4),
+     TOBN(0x85138705, 0xd94203e8), TOBN(0xccde538b, 0xbeeb6f4a),
+     TOBN(0x55d5c68d, 0xa61a76fa), TOBN(0x598b441d, 0xca1554dc),
+     TOBN(0xd39923b9, 0x773b279c), TOBN(0x33331d3c, 0x36bf9efc),
+     TOBN(0x2d4c848e, 0x298de399), TOBN(0xcfdb8e77, 0xa1a27f56),
+     TOBN(0x94c855ea, 0x57b8ab70), TOBN(0xdcdb9dae, 0x6f7879ba),
+     TOBN(0x7bdff8c2, 0x019f2a59), TOBN(0xb3ce5bb3, 0xcb4fbc74),
+     TOBN(0xea907f68, 0x8a9173dd), TOBN(0x6cd3d0d3, 0x95a75439),
+     TOBN(0x92ecc4d6, 0xefed021c), TOBN(0x09a9f9b0, 0x6a77339a),
+     TOBN(0x87ca6b15, 0x7188c64a), TOBN(0x10c29968, 0x44899158),
+     TOBN(0x5859a229, 0xed6e82ef), TOBN(0x16f338e3, 0x65ebaf4e),
+     TOBN(0x0cd31387, 0x5ead67ae), TOBN(0x1c73d228, 0x54ef0bb4),
+     TOBN(0x4cb55131, 0x74a5c8c7), TOBN(0x01cd2970, 0x7f69ad6a),
+     TOBN(0xa04d00dd, 0xe966f87e), TOBN(0xd96fe447, 0x0b7b0321),
+     TOBN(0x342ac06e, 0x88fbd381), TOBN(0x02cd4a84, 0x5c35a493),
+     TOBN(0xe8fa89de, 0x54f1bbcd), TOBN(0x341d6367, 0x2575ed4c),
+     TOBN(0xebe357fb, 0xd238202b), TOBN(0x600b4d1a, 0xa984ead9),
+     TOBN(0xc35c9f44, 0x52436ea0), TOBN(0x96fe0a39, 0xa370751b),
+     TOBN(0x4c4f0736, 0x7f636a38), TOBN(0x9f943fb7, 0x0e76d5cb),
+     TOBN(0xb03510ba, 0xa8b68b8b), TOBN(0xc246780a, 0x9ed07a1f),
+     TOBN(0x3c051415, 0x6d549fc2), TOBN(0xc2953f31, 0x607781ca),
+     TOBN(0x955e2c69, 0xd8d95413), TOBN(0xb300fadc, 0x7bd282e3),
+     TOBN(0x81fe7b50, 0x87e9189f), TOBN(0xdb17375c, 0xf42dda27),
+     TOBN(0x22f7d896, 0xcf0a5904), TOBN(0xa0e57c5a, 0xebe348e6),
+     TOBN(0xa61011d3, 0xf40e3c80), TOBN(0xb1189321, 0x8db705c5),
+     TOBN(0x4ed9309e, 0x50fedec3), TOBN(0xdcf14a10, 0x4d6d5c1d),
+     TOBN(0x056c265b, 0x55691342), TOBN(0xe8e08504, 0x91049dc7),
+     TOBN(0x131329f5, 0xc9bae20a), TOBN(0x96c8b3e8, 0xd9dccdb4),
+     TOBN(0x8c5ff838, 0xfb4ee6b4), TOBN(0xfc5a9aeb, 0x41e8ccf0),
+     TOBN(0x7417b764, 0xfae050c6), TOBN(0x0953c3d7, 0x00452080),
+     TOBN(0x21372682, 0x38dfe7e8), TOBN(0xea417e15, 0x2bb79d4b),
+     TOBN(0x59641f1c, 0x76e7cf2d), TOBN(0x271e3059, 0xea0bcfcc),
+     TOBN(0x624c7dfd, 0x7253ecbd), TOBN(0x2f552e25, 0x4fca6186),
+     TOBN(0xcbf84ecd, 0x4d866e9c), TOBN(0x73967709, 0xf68d4610),
+     TOBN(0xa14b1163, 0xc27901b4), TOBN(0xfd9236e0, 0x899b8bf3),
+     TOBN(0x42b091ec, 0xcbc6da0a), TOBN(0xbb1dac6f, 0x5ad1d297),
+     TOBN(0x80e61d53, 0xa91cf76e), TOBN(0x4110a412, 0xd31f1ee7),
+     TOBN(0x2d87c3ba, 0x13efcf77), TOBN(0x1f374bb4, 0xdf450d76),
+     TOBN(0x5e78e2f2, 0x0d188dab), TOBN(0xe3968ed0, 0xf4b885ef),
+     TOBN(0x46c0568e, 0x7314570f), TOBN(0x31616338, 0x01170521),
+     TOBN(0x18e1e7e2, 0x4f0c8afe), TOBN(0x4caa75ff, 0xdeea78da),
+     TOBN(0x82db67f2, 0x7c5d8a51), TOBN(0x36a44d86, 0x6f505370),
+     TOBN(0xd72c5bda, 0x0333974f), TOBN(0x5db516ae, 0x27a70146),
+     TOBN(0x34705281, 0x210ef921), TOBN(0xbff17a8f, 0x0c9c38e5),
+     TOBN(0x78f4814e, 0x12476da1), TOBN(0xc1e16613, 0x33c16980),
+     TOBN(0x9e5b386f, 0x424d4bca), TOBN(0x4c274e87, 0xc85740de),
+     TOBN(0xb6a9b88d, 0x6c2f5226), TOBN(0x14d1b944, 0x550d7ca8),
+     TOBN(0x580c85fc, 0x1fc41709), TOBN(0xc1da368b, 0x54c6d519),
+     TOBN(0x2b0785ce, 0xd5113cf7), TOBN(0x0670f633, 0x5a34708f),
+     TOBN(0x46e23767, 0x15cc3f88), TOBN(0x1b480cfa, 0x50c72c8f),
+     TOBN(0x20288602, 0x4147519a), TOBN(0xd0981eac, 0x26b372f0),
+     TOBN(0xa9d4a7ca, 0xa785ebc8), TOBN(0xd953c50d, 0xdbdf58e9),
+     TOBN(0x9d6361cc, 0xfd590f8f), TOBN(0x72e9626b, 0x44e6c917),
+     TOBN(0x7fd96110, 0x22eb64cf), TOBN(0x863ebb7e, 0x9eb288f3),
+     TOBN(0x6e6ab761, 0x6aca8ee7), TOBN(0x97d10b39, 0xd7b40358),
+     TOBN(0x1687d377, 0x1e5feb0d), TOBN(0xc83e50e4, 0x8265a27a),
+     TOBN(0x8f75a9fe, 0xc954b313), TOBN(0xcc2e8f47, 0x310d1f61),
+     TOBN(0xf5ba81c5, 0x6557d0e0), TOBN(0x25f9680c, 0x3eaf6207),
+     TOBN(0xf95c6609, 0x4354080b), TOBN(0x5225bfa5, 0x7bf2fe1c),
+     TOBN(0xc5c004e2, 0x5c7d98fa), TOBN(0x3561bf1c, 0x019aaf60),
+     TOBN(0x5e6f9f17, 0xba151474), TOBN(0xdec2f934, 0xb04f6eca),
+     TOBN(0x64e368a1, 0x269acb1e), TOBN(0x1332d9e4, 0x0cdda493),
+     TOBN(0x60d6cf69, 0xdf23de05), TOBN(0x66d17da2, 0x009339a0),
+     TOBN(0x9fcac985, 0x0a693923), TOBN(0xbcf057fc, 0xed7c6a6d),
+     TOBN(0xc3c5c8c5, 0xf0b5662c), TOBN(0x25318dd8, 0xdcba4f24),
+     TOBN(0x60e8cb75, 0x082b69ff), TOBN(0x7c23b3ee, 0x1e728c01),
+     TOBN(0x15e10a0a, 0x097e4403), TOBN(0xcb3d0a86, 0x19854665),
+     TOBN(0x88d8e211, 0xd67d4826), TOBN(0xb39af66e, 0x0b9d2839),
+     TOBN(0xa5f94588, 0xbd475ca8), TOBN(0xe06b7966, 0xc077b80b),
+     TOBN(0xfedb1485, 0xda27c26c), TOBN(0xd290d33a, 0xfe0fd5e0),
+     TOBN(0xa40bcc47, 0xf34fb0fa), TOBN(0xb4760cc8, 0x1fb1ab09),
+     TOBN(0x8fca0993, 0xa273bfe3), TOBN(0x13e4fe07, 0xf70b213c),
+     TOBN(0x3bcdb992, 0xfdb05163), TOBN(0x8c484b11, 0x0c2b19b6),
+     TOBN(0x1acb815f, 0xaaf2e3e2), TOBN(0xc6905935, 0xb89ff1b4),
+     TOBN(0xb2ad6f9d, 0x586e74e1), TOBN(0x488883ad, 0x67b80484),
+     TOBN(0x758aa2c7, 0x369c3ddb), TOBN(0x8ab74e69, 0x9f9afd31),
+     TOBN(0x10fc2d28, 0x5e21beb1), TOBN(0x3484518a, 0x318c42f9),
+     TOBN(0x377427dc, 0x53cf40c3), TOBN(0x9de0781a, 0x391bc1d9),
+     TOBN(0x8faee858, 0x693807e1), TOBN(0xa3865327, 0x4e81ccc7),
+     TOBN(0x02c30ff2, 0x6f835b84), TOBN(0xb604437b, 0x0d3d38d4),
+     TOBN(0xb3fc8a98, 0x5ca1823d), TOBN(0xb82f7ec9, 0x03be0324),
+     TOBN(0xee36d761, 0xcf684a33), TOBN(0x5a01df0e, 0x9f29bf7d),
+     TOBN(0x686202f3, 0x1306583d), TOBN(0x05b10da0, 0x437c622e),
+     TOBN(0xbf9aaa0f, 0x076a7bc8), TOBN(0x25e94efb, 0x8f8f4e43),
+     TOBN(0x8a35c9b7, 0xfa3dc26d), TOBN(0xe0e5fb93, 0x96ff03c5),
+     TOBN(0xa77e3843, 0xebc394ce), TOBN(0xcede6595, 0x8361de60),
+     TOBN(0xd27c22f6, 0xa1993545), TOBN(0xab01cc36, 0x24d671ba),
+     TOBN(0x63fa2877, 0xa169c28e), TOBN(0x925ef904, 0x2eb08376),
+     TOBN(0x3b2fa3cf, 0x53aa0b32), TOBN(0xb27beb5b, 0x71c49d7a),
+     TOBN(0xb60e1834, 0xd105e27f), TOBN(0xd6089788, 0x4f68570d),
+     TOBN(0x23094ce0, 0xd6fbc2ac), TOBN(0x738037a1, 0x815ff551),
+     TOBN(0xda73b1bb, 0x6bef119c), TOBN(0xdcf6c430, 0xeef506ba),
+     TOBN(0x00e4fe7b, 0xe3ef104a), TOBN(0xebdd9a2c, 0x0a065628),
+     TOBN(0x853a81c3, 0x8792043e), TOBN(0x22ad6ece, 0xb3b59108),
+     TOBN(0x9fb813c0, 0x39cd297d), TOBN(0x8ec7e16e, 0x05bda5d9),
+     TOBN(0x2834797c, 0x0d104b96), TOBN(0xcc11a2e7, 0x7c511510),
+     TOBN(0x96ca5a53, 0x96ee6380), TOBN(0x054c8655, 0xcea38742),
+     TOBN(0xb5946852, 0xd54dfa7d), TOBN(0x97c422e7, 0x1f4ab207),
+     TOBN(0xbf907509, 0x0c22b540), TOBN(0x2cde42aa, 0xb7c267d4),
+     TOBN(0xba18f9ed, 0x5ab0d693), TOBN(0x3ba62aa6, 0x6e4660d9),
+     TOBN(0xb24bf97b, 0xab9ea96a), TOBN(0x5d039642, 0xe3b60e32),
+     TOBN(0x4e6a4506, 0x7c4d9bd5), TOBN(0x666c5b9e, 0x7ed4a6a4),
+     TOBN(0xfa3fdcd9, 0x8edbd7cc), TOBN(0x4660bb87, 0xc6ccd753),
+     TOBN(0x9ae90820, 0x21e6b64f), TOBN(0x8a56a713, 0xb36bfb3f),
+     TOBN(0xabfce096, 0x5726d47f), TOBN(0x9eed01b2, 0x0b1a9a7f),
+     TOBN(0x30e9cad4, 0x4eb74a37), TOBN(0x7b2524cc, 0x53e9666d),
+     TOBN(0x6a29683b, 0x8f4b002f), TOBN(0xc2200d7a, 0x41f4fc20),
+     TOBN(0xcf3af47a, 0x3a338acc), TOBN(0x6539a4fb, 0xe7128975),
+     TOBN(0xcec31c14, 0xc33c7fcf), TOBN(0x7eb6799b, 0xc7be322b),
+     TOBN(0x119ef4e9, 0x6646f623), TOBN(0x7b7a26a5, 0x54d7299b),
+     TOBN(0xcb37f08d, 0x403f46f2), TOBN(0x94b8fc43, 0x1a0ec0c7),
+     TOBN(0xbb8514e3, 0xc332142f), TOBN(0xf3ed2c33, 0xe80d2a7a),
+     TOBN(0x8d2080af, 0xb639126c), TOBN(0xf7b6be60, 0xe3553ade),
+     TOBN(0x3950aa9f, 0x1c7e2b09), TOBN(0x847ff958, 0x6410f02b),
+     TOBN(0x877b7cf5, 0x678a31b0), TOBN(0xd50301ae, 0x3998b620),
+     TOBN(0x734257c5, 0xc00fb396), TOBN(0xf9fb18a0, 0x04e672a6),
+     TOBN(0xff8bd8eb, 0xe8758851), TOBN(0x1e64e4c6, 0x5d99ba44),
+     TOBN(0x4b8eaedf, 0x7dfd93b7), TOBN(0xba2f2a98, 0x04e76b8c),
+     TOBN(0x7d790cba, 0xe8053433), TOBN(0xc8e725a0, 0x3d2c9585),
+     TOBN(0x58c5c476, 0xcdd8f5ed), TOBN(0xd106b952, 0xefa9fe1d),
+     TOBN(0x3c5c775b, 0x0eff13a9), TOBN(0x242442ba, 0xe057b930),
+     TOBN(0xe9f458d4, 0xc9b70cbd), TOBN(0x69b71448, 0xa3cdb89a),
+     TOBN(0x41ee46f6, 0x0e2ed742), TOBN(0x573f1045, 0x40067493),
+     TOBN(0xb1e154ff, 0x9d54c304), TOBN(0x2ad0436a, 0x8d3a7502),
+     TOBN(0xee4aaa2d, 0x431a8121), TOBN(0xcd38b3ab, 0x886f11ed),
+     TOBN(0x57d49ea6, 0x034a0eb7), TOBN(0xd2b773bd, 0xf7e85e58),
+     TOBN(0x4a559ac4, 0x9b5c1f14), TOBN(0xc444be1a, 0x3e54df2b),
+     TOBN(0x13aad704, 0xeda41891), TOBN(0xcd927bec, 0x5eb5c788),
+     TOBN(0xeb3c8516, 0xe48c8a34), TOBN(0x1b7ac812, 0x4b546669),
+     TOBN(0x1815f896, 0x594df8ec), TOBN(0x87c6a79c, 0x79227865),
+     TOBN(0xae02a2f0, 0x9b56ddbd), TOBN(0x1339b5ac, 0x8a2f1cf3),
+     TOBN(0xf2b569c7, 0x839dff0d), TOBN(0xb0b9e864, 0xfee9a43d),
+     TOBN(0x4ff8ca41, 0x77bb064e), TOBN(0x145a2812, 0xfd249f63),
+     TOBN(0x3ab7beac, 0xf86f689a), TOBN(0x9bafec27, 0x01d35f5e),
+     TOBN(0x28054c65, 0x4265aa91), TOBN(0xa4b18304, 0x035efe42),
+     TOBN(0x6887b0e6, 0x9639dec7), TOBN(0xf4b8f6ad, 0x3d52aea5),
+     TOBN(0xfb9293cc, 0x971a8a13), TOBN(0x3f159e5d, 0x4c934d07),
+     TOBN(0x2c50e9b1, 0x09acbc29), TOBN(0x08eb65e6, 0x7154d129),
+     TOBN(0x4feff589, 0x30b75c3e), TOBN(0x0bb82fe2, 0x94491c93),
+     TOBN(0xd8ac377a, 0x89af62bb), TOBN(0xd7b51490, 0x9685e49f),
+     TOBN(0xabca9a7b, 0x04497f19), TOBN(0x1b35ed0a, 0x1a7ad13f),
+     TOBN(0x6b601e21, 0x3ec86ed6), TOBN(0xda91fcb9, 0xce0c76f1),
+     TOBN(0x9e28507b, 0xd7ab27e1), TOBN(0x7c19a555, 0x63945b7b),
+     TOBN(0x6b43f0a1, 0xaafc9827), TOBN(0x443b4fbd, 0x3aa55b91),
+     TOBN(0x962b2e65, 0x6962c88f), TOBN(0x139da8d4, 0xce0db0ca),
+     TOBN(0xb93f05dd, 0x1b8d6c4f), TOBN(0x779cdff7, 0x180b9824),
+     TOBN(0xbba23fdd, 0xae57c7b7), TOBN(0x345342f2, 0x1b932522),
+     TOBN(0xfd9c80fe, 0x556d4aa3), TOBN(0xa03907ba, 0x6525bb61),
+     TOBN(0x38b010e1, 0xff218933), TOBN(0xc066b654, 0xaa52117b),
+     TOBN(0x8e141920, 0x94f2e6ea), TOBN(0x66a27dca, 0x0d32f2b2),
+     TOBN(0x69c7f993, 0x048b3717), TOBN(0xbf5a989a, 0xb178ae1c),
+     TOBN(0x49fa9058, 0x564f1d6b), TOBN(0x27ec6e15, 0xd31fde4e),
+     TOBN(0x4cce0373, 0x7276e7fc), TOBN(0x64086d79, 0x89d6bf02),
+     TOBN(0x5a72f046, 0x4ccdd979), TOBN(0x909c3566, 0x47775631),
+     TOBN(0x1c07bc6b, 0x75dd7125), TOBN(0xb4c6bc97, 0x87a0428d),
+     TOBN(0x507ece52, 0xfdeb6b9d), TOBN(0xfca56512, 0xb2c95432),
+     TOBN(0x15d97181, 0xd0e8bd06), TOBN(0x384dd317, 0xc6bb46ea),
+     TOBN(0x5441ea20, 0x3952b624), TOBN(0xbcf70dee, 0x4e7dc2fb),
+     TOBN(0x372b016e, 0x6628e8c3), TOBN(0x07a0d667, 0xb60a7522),
+     TOBN(0xcf05751b, 0x0a344ee2), TOBN(0x0ec09a48, 0x118bdeec),
+     TOBN(0x6e4b3d4e, 0xd83dce46), TOBN(0x43a6316d, 0x99d2fc6e),
+     TOBN(0xa99d8989, 0x56cf044c), TOBN(0x7c7f4454, 0xae3e5fb7),
+     TOBN(0xb2e6b121, 0xfbabbe92), TOBN(0x281850fb, 0xe1330076),
+     TOBN(0x093581ec, 0x97890015), TOBN(0x69b1dded, 0x75ff77f5),
+     TOBN(0x7cf0b18f, 0xab105105), TOBN(0x953ced31, 0xa89ccfef),
+     TOBN(0x3151f85f, 0xeb914009), TOBN(0x3c9f1b87, 0x88ed48ad),
+     TOBN(0xc9aba1a1, 0x4a7eadcb), TOBN(0x928e7501, 0x522e71cf),
+     TOBN(0xeaede727, 0x3a2e4f83), TOBN(0x467e10d1, 0x1ce3bbd3),
+     TOBN(0xf3442ac3, 0xb955dcf0), TOBN(0xba96307d, 0xd3d5e527),
+     TOBN(0xf763a10e, 0xfd77f474), TOBN(0x5d744bd0, 0x6a6e1ff0),
+     TOBN(0xd287282a, 0xa777899e), TOBN(0xe20eda8f, 0xd03f3cde),
+     TOBN(0x6a7e75bb, 0x50b07d31), TOBN(0x0b7e2a94, 0x6f379de4),
+     TOBN(0x31cb64ad, 0x19f593cf), TOBN(0x7b1a9e4f, 0x1e76ef1d),
+     TOBN(0xe18c9c9d, 0xb62d609c), TOBN(0x439bad6d, 0xe779a650),
+     TOBN(0x219d9066, 0xe032f144), TOBN(0x1db632b8, 0xe8b2ec6a),
+     TOBN(0xff0d0fd4, 0xfda12f78), TOBN(0x56fb4c2d, 0x2a25d265),
+     TOBN(0x5f4e2ee1, 0x255a03f1), TOBN(0x61cd6af2, 0xe96af176),
+     TOBN(0xe0317ba8, 0xd068bc97), TOBN(0x927d6bab, 0x264b988e),
+     TOBN(0xa18f07e0, 0xe90fb21e), TOBN(0x00fd2b80, 0xbba7fca1),
+     TOBN(0x20387f27, 0x95cd67b5), TOBN(0x5b89a4e7, 0xd39707f7),
+     TOBN(0x8f83ad3f, 0x894407ce), TOBN(0xa0025b94, 0x6c226132),
+     TOBN(0xc79563c7, 0xf906c13b), TOBN(0x5f548f31, 0x4e7bb025),
+     TOBN(0x2b4c6b8f, 0xeac6d113), TOBN(0xa67e3f9c, 0x0e813c76),
+     TOBN(0x3982717c, 0x3fe1f4b9), TOBN(0x58865819, 0x26d8050e),
+     TOBN(0x99f3640c, 0xf7f06f20), TOBN(0xdc610216, 0x2a66ebc2),
+     TOBN(0x52f2c175, 0x767a1e08), TOBN(0x05660e1a, 0x5999871b),
+     TOBN(0x6b0f1762, 0x6d3c4693), TOBN(0xf0e7d627, 0x37ed7bea),
+     TOBN(0xc51758c7, 0xb75b226d), TOBN(0x40a88628, 0x1f91613b),
+     TOBN(0x889dbaa7, 0xbbb38ce0), TOBN(0xe0404b65, 0xbddcad81),
+     TOBN(0xfebccd3a, 0x8bc9671f), TOBN(0xfbf9a357, 0xee1f5375),
+     TOBN(0x5dc169b0, 0x28f33398), TOBN(0xb07ec11d, 0x72e90f65),
+     TOBN(0xae7f3b4a, 0xfaab1eb1), TOBN(0xd970195e, 0x5f17538a),
+     TOBN(0x52b05cbe, 0x0181e640), TOBN(0xf5debd62, 0x2643313d),
+     TOBN(0x76148154, 0x5df31f82), TOBN(0x23e03b33, 0x3a9e13c5),
+     TOBN(0xff758949, 0x4fde0c1f), TOBN(0xbf8a1abe, 0xe5b6ec20),
+     TOBN(0x702278fb, 0x87e1db6c), TOBN(0xc447ad7a, 0x35ed658f),
+     TOBN(0x48d4aa38, 0x03d0ccf2), TOBN(0x80acb338, 0x819a7c03),
+     TOBN(0x9bc7c89e, 0x6e17cecc), TOBN(0x46736b8b, 0x03be1d82),
+     TOBN(0xd65d7b60, 0xc0432f96), TOBN(0xddebe7a3, 0xdeb5442f),
+     TOBN(0x79a25307, 0x7dff69a2), TOBN(0x37a56d94, 0x02cf3122),
+     TOBN(0x8bab8aed, 0xf2350d0a), TOBN(0x13c3f276, 0x037b0d9a),
+     TOBN(0xc664957c, 0x44c65cae), TOBN(0x88b44089, 0xc2e71a88),
+     TOBN(0xdb88e5a3, 0x5cb02664), TOBN(0x5d4c0bf1, 0x8686c72e),
+     TOBN(0xea3d9b62, 0xa682d53e), TOBN(0x9b605ef4, 0x0b2ad431),
+     TOBN(0x71bac202, 0xc69645d0), TOBN(0xa115f03a, 0x6a1b66e7),
+     TOBN(0xfe2c563a, 0x158f4dc4), TOBN(0xf715b3a0, 0x4d12a78c),
+     TOBN(0x8f7f0a48, 0xd413213a), TOBN(0x2035806d, 0xc04becdb),
+     TOBN(0xecd34a99, 0x5d8587f5), TOBN(0x4d8c3079, 0x9f6d3a71),
+     TOBN(0x1b2a2a67, 0x8d95a8f6), TOBN(0xc58c9d7d, 0xf2110d0d),
+     TOBN(0xdeee81d5, 0xcf8fba3f), TOBN(0xa42be3c0, 0x0c7cdf68),
+     TOBN(0x2126f742, 0xd43b5eaa), TOBN(0x054a0766, 0xdfa59b85),
+     TOBN(0x9d0d5e36, 0x126bfd45), TOBN(0xa1f8fbd7, 0x384f8a8f),
+     TOBN(0x317680f5, 0xd563fccc), TOBN(0x48ca5055, 0xf280a928),
+     TOBN(0xe00b81b2, 0x27b578cf), TOBN(0x10aad918, 0x2994a514),
+     TOBN(0xd9e07b62, 0xb7bdc953), TOBN(0x9f0f6ff2, 0x5bc086dd),
+     TOBN(0x09d1ccff, 0x655eee77), TOBN(0x45475f79, 0x5bef7df1),
+     TOBN(0x3faa28fa, 0x86f702cc), TOBN(0x92e60905, 0x0f021f07),
+     TOBN(0xe9e62968, 0x7f8fa8c6), TOBN(0xbd71419a, 0xf036ea2c),
+     TOBN(0x171ee1cc, 0x6028da9a), TOBN(0x5352fe1a, 0xc251f573),
+     TOBN(0xf8ff236e, 0x3fa997f4), TOBN(0xd831b6c9, 0xa5749d5f),
+     TOBN(0x7c872e1d, 0xe350e2c2), TOBN(0xc56240d9, 0x1e0ce403),
+     TOBN(0xf9deb077, 0x6974f5cb), TOBN(0x7d50ba87, 0x961c3728),
+     TOBN(0xd6f89426, 0x5a3a2518), TOBN(0xcf817799, 0xc6303d43),
+     TOBN(0x510a0471, 0x619e5696), TOBN(0xab049ff6, 0x3a5e307b),
+     TOBN(0xe4cdf9b0, 0xfeb13ec7), TOBN(0xd5e97117, 0x9d8ff90c),
+     TOBN(0xf6f64d06, 0x9afa96af), TOBN(0x00d0bf5e, 0x9d2012a2),
+     TOBN(0xe63f301f, 0x358bcdc0), TOBN(0x07689e99, 0x0a9d47f8),
+     TOBN(0x1f689e2f, 0x4f43d43a), TOBN(0x4d542a16, 0x90920904),
+     TOBN(0xaea293d5, 0x9ca0a707), TOBN(0xd061fe45, 0x8ac68065),
+     TOBN(0x1033bf1b, 0x0090008c), TOBN(0x29749558, 0xc08a6db6),
+     TOBN(0x74b5fc59, 0xc1d5d034), TOBN(0xf712e9f6, 0x67e215e0),
+     TOBN(0xfd520cbd, 0x860200e6), TOBN(0x0229acb4, 0x3ea22588),
+     TOBN(0x9cd1e14c, 0xfff0c82e), TOBN(0x87684b62, 0x59c69e73),
+     TOBN(0xda85e61c, 0x96ccb989), TOBN(0x2d5dbb02, 0xa3d06493),
+     TOBN(0xf22ad33a, 0xe86b173c), TOBN(0xe8e41ea5, 0xa79ff0e3),
+     TOBN(0x01d2d725, 0xdd0d0c10), TOBN(0x31f39088, 0x032d28f9),
+     TOBN(0x7b3f71e1, 0x7829839e), TOBN(0x0cf691b4, 0x4502ae58),
+     TOBN(0xef658dbd, 0xbefc6115), TOBN(0xa5cd6ee5, 0xb3ab5314),
+     TOBN(0x206c8d7b, 0x5f1d2347), TOBN(0x794645ba, 0x4cc2253a),
+     TOBN(0xd517d8ff, 0x58389e08), TOBN(0x4fa20dee, 0x9f847288),
+     TOBN(0xeba072d8, 0xd797770a), TOBN(0x7360c91d, 0xbf429e26),
+     TOBN(0x7200a3b3, 0x80af8279), TOBN(0x6a1c9150, 0x82dadce3),
+     TOBN(0x0ee6d3a7, 0xc35d8794), TOBN(0x042e6558, 0x0356bae5),
+     TOBN(0x9f59698d, 0x643322fd), TOBN(0x9379ae15, 0x50a61967),
+     TOBN(0x64b9ae62, 0xfcc9981e), TOBN(0xaed3d631, 0x6d2934c6),
+     TOBN(0x2454b302, 0x5e4e65eb), TOBN(0xab09f647, 0xf9950428)}
+    ,
+    {TOBN(0xb2083a12, 0x22248acc), TOBN(0x1f6ec0ef, 0x3264e366),
+     TOBN(0x5659b704, 0x5afdee28), TOBN(0x7a823a40, 0xe6430bb5),
+     TOBN(0x24592a04, 0xe1900a79), TOBN(0xcde09d4a, 0xc9ee6576),
+     TOBN(0x52b6463f, 0x4b5ea54a), TOBN(0x1efe9ed3, 0xd3ca65a7),
+     TOBN(0xe27a6dbe, 0x305406dd), TOBN(0x8eb7dc7f, 0xdd5d1957),
+     TOBN(0xf54a6876, 0x387d4d8f), TOBN(0x9c479409, 0xc7762de4),
+     TOBN(0xbe4d5b5d, 0x99b30778), TOBN(0x25380c56, 0x6e793682),
+     TOBN(0x602d37f3, 0xdac740e3), TOBN(0x140deabe, 0x1566e4ae),
+     TOBN(0x4481d067, 0xafd32acf), TOBN(0xd8f0fcca, 0xe1f71ccf),
+     TOBN(0xd208dd0c, 0xb596f2da), TOBN(0xd049d730, 0x9aad93f9),
+     TOBN(0xc79f263d, 0x42ab580e), TOBN(0x09411bb1, 0x23f707b4),
+     TOBN(0x8cfde1ff, 0x835e0eda), TOBN(0x72707490, 0x90f03402),
+     TOBN(0xeaee6126, 0xc49a861e), TOBN(0x024f3b65, 0xe14f0d06),
+     TOBN(0x51a3f1e8, 0xc69bfc17), TOBN(0xc3c3a8e9, 0xa7686381),
+     TOBN(0x3400752c, 0xb103d4c8), TOBN(0x02bc4613, 0x9218b36b),
+     TOBN(0xc67f75eb, 0x7651504a), TOBN(0xd6848b56, 0xd02aebfa),
+     TOBN(0xbd9802e6, 0xc30fa92b), TOBN(0x5a70d96d, 0x9a552784),
+     TOBN(0x9085c4ea, 0x3f83169b), TOBN(0xfa9423bb, 0x06908228),
+     TOBN(0x2ffebe12, 0xfe97a5b9), TOBN(0x85da6049, 0x71b99118),
+     TOBN(0x9cbc2f7f, 0x63178846), TOBN(0xfd96bc70, 0x9153218e),
+     TOBN(0x958381db, 0x1782269b), TOBN(0xae34bf79, 0x2597e550),
+     TOBN(0xbb5c6064, 0x5f385153), TOBN(0x6f0e96af, 0xe3088048),
+     TOBN(0xbf6a0215, 0x77884456), TOBN(0xb3b5688c, 0x69310ea7),
+     TOBN(0x17c94295, 0x04fad2de), TOBN(0xe020f0e5, 0x17896d4d),
+     TOBN(0x730ba0ab, 0x0976505f), TOBN(0x567f6813, 0x095e2ec5),
+     TOBN(0x47062010, 0x6331ab71), TOBN(0x72cfa977, 0x41d22b9f),
+     TOBN(0x33e55ead, 0x8a2373da), TOBN(0xa8d0d5f4, 0x7ba45a68),
+     TOBN(0xba1d8f9c, 0x03029d15), TOBN(0x8f34f1cc, 0xfc55b9f3),
+     TOBN(0xcca4428d, 0xbbe5a1a9), TOBN(0x8187fd5f, 0x3126bd67),
+     TOBN(0x0036973a, 0x48105826), TOBN(0xa39b6663, 0xb8bd61a0),
+     TOBN(0x6d42deef, 0x2d65a808), TOBN(0x4969044f, 0x94636b19),
+     TOBN(0xf611ee47, 0xdd5d564c), TOBN(0x7b2f3a49, 0xd2873077),
+     TOBN(0x94157d45, 0x300eb294), TOBN(0x2b2a656e, 0x169c1494),
+     TOBN(0xc000dd76, 0xd3a47aa9), TOBN(0xa2864e4f, 0xa6243ea4),
+     TOBN(0x82716c47, 0xdb89842e), TOBN(0x12dfd7d7, 0x61479fb7),
+     TOBN(0x3b9a2c56, 0xe0b2f6dc), TOBN(0x46be862a, 0xd7f85d67),
+     TOBN(0x03b0d8dd, 0x0f82b214), TOBN(0x460c34f9, 0xf103cbc6),
+     TOBN(0xf32e5c03, 0x18d79e19), TOBN(0x8b8888ba, 0xa84117f8),
+     TOBN(0x8f3c37dc, 0xc0722677), TOBN(0x10d21be9, 0x1c1c0f27),
+     TOBN(0xd47c8468, 0xe0f7a0c6), TOBN(0x9bf02213, 0xadecc0e0),
+     TOBN(0x0baa7d12, 0x42b48b99), TOBN(0x1bcb665d, 0x48424096),
+     TOBN(0x8b847cd6, 0xebfb5cfb), TOBN(0x87c2ae56, 0x9ad4d10d),
+     TOBN(0xf1cbb122, 0x0de36726), TOBN(0xe7043c68, 0x3fdfbd21),
+     TOBN(0x4bd0826a, 0x4e79d460), TOBN(0x11f5e598, 0x4bd1a2cb),
+     TOBN(0x97554160, 0xb7fe7b6e), TOBN(0x7d16189a, 0x400a3fb2),
+     TOBN(0xd73e9bea, 0xe328ca1e), TOBN(0x0dd04b97, 0xe793d8cc),
+     TOBN(0xa9c83c9b, 0x506db8cc), TOBN(0x5cd47aae, 0xcf38814c),
+     TOBN(0x26fc430d, 0xb64b45e6), TOBN(0x079b5499, 0xd818ea84),
+     TOBN(0xebb01102, 0xc1c24a3b), TOBN(0xca24e568, 0x1c161c1a),
+     TOBN(0x103eea69, 0x36f00a4a), TOBN(0x9ad76ee8, 0x76176c7b),
+     TOBN(0x97451fc2, 0x538e0ff7), TOBN(0x94f89809, 0x6604b3b0),
+     TOBN(0x6311436e, 0x3249cfd7), TOBN(0x27b4a7bd, 0x41224f69),
+     TOBN(0x03b5d21a, 0xe0ac2941), TOBN(0x279b0254, 0xc2d31937),
+     TOBN(0x3307c052, 0xcac992d0), TOBN(0x6aa7cb92, 0xefa8b1f3),
+     TOBN(0x5a182580, 0x0d37c7a5), TOBN(0x13380c37, 0x342d5422),
+     TOBN(0x92ac2d66, 0xd5d2ef92), TOBN(0x035a70c9, 0x030c63c6),
+     TOBN(0xc16025dd, 0x4ce4f152), TOBN(0x1f419a71, 0xf9df7c06),
+     TOBN(0x6d5b2214, 0x91e4bb14), TOBN(0xfc43c6cc, 0x839fb4ce),
+     TOBN(0x49f06591, 0x925d6b2d), TOBN(0x4b37d9d3, 0x62186598),
+     TOBN(0x8c54a971, 0xd01b1629), TOBN(0xe1a9c29f, 0x51d50e05),
+     TOBN(0x5109b785, 0x71ba1861), TOBN(0x48b22d5c, 0xd0c8f93d),
+     TOBN(0xe8fa84a7, 0x8633bb93), TOBN(0x53fba6ba, 0x5aebbd08),
+     TOBN(0x7ff27df3, 0xe5eea7d8), TOBN(0x521c8796, 0x68ca7158),
+     TOBN(0xb9d5133b, 0xce6f1a05), TOBN(0x2d50cd53, 0xfd0ebee4),
+     TOBN(0xc82115d6, 0xc5a3ef16), TOBN(0x993eff9d, 0xba079221),
+     TOBN(0xe4da2c5e, 0x4b5da81c), TOBN(0x9a89dbdb, 0x8033fd85),
+     TOBN(0x60819ebf, 0x2b892891), TOBN(0x53902b21, 0x5d14a4d5),
+     TOBN(0x6ac35051, 0xd7fda421), TOBN(0xcc6ab885, 0x61c83284),
+     TOBN(0x14eba133, 0xf74cff17), TOBN(0x240aaa03, 0xecb813f2),
+     TOBN(0xcfbb6540, 0x6f665bee), TOBN(0x084b1fe4, 0xa425ad73),
+     TOBN(0x009d5d16, 0xd081f6a6), TOBN(0x35304fe8, 0xeef82c90),
+     TOBN(0xf20346d5, 0xaa9eaa22), TOBN(0x0ada9f07, 0xac1c91e3),
+     TOBN(0xa6e21678, 0x968a6144), TOBN(0x54c1f77c, 0x07b31a1e),
+     TOBN(0xd6bb787e, 0x5781fbe1), TOBN(0x61bd2ee0, 0xe31f1c4a),
+     TOBN(0xf25aa1e9, 0x781105fc), TOBN(0x9cf2971f, 0x7b2f8e80),
+     TOBN(0x26d15412, 0xcdff919b), TOBN(0x01db4ebe, 0x34bc896e),
+     TOBN(0x7d9b3e23, 0xb40df1cf), TOBN(0x59337373, 0x94e971b4),
+     TOBN(0xbf57bd14, 0x669cf921), TOBN(0x865daedf, 0x0c1a1064),
+     TOBN(0x3eb70bd3, 0x83279125), TOBN(0xbc3d5b9f, 0x34ecdaab),
+     TOBN(0x91e3ed7e, 0x5f755caf), TOBN(0x49699f54, 0xd41e6f02),
+     TOBN(0x185770e1, 0xd4a7a15b), TOBN(0x08f3587a, 0xeaac87e7),
+     TOBN(0x352018db, 0x473133ea), TOBN(0x674ce719, 0x04fd30fc),
+     TOBN(0x7b8d9835, 0x088b3e0e), TOBN(0x7a0356a9, 0x5d0d47a1),
+     TOBN(0x9d9e7659, 0x6474a3c4), TOBN(0x61ea48a7, 0xff66966c),
+     TOBN(0x30417758, 0x0f3e4834), TOBN(0xfdbb21c2, 0x17a9afcb),
+     TOBN(0x756fa17f, 0x2f9a67b3), TOBN(0x2a6b2421, 0xa245c1a8),
+     TOBN(0x64be2794, 0x4af02291), TOBN(0xade465c6, 0x2a5804fe),
+     TOBN(0x8dffbd39, 0xa6f08fd7), TOBN(0xc4efa84c, 0xaa14403b),
+     TOBN(0xa1b91b2a, 0x442b0f5c), TOBN(0xb748e317, 0xcf997736),
+     TOBN(0x8d1b62bf, 0xcee90e16), TOBN(0x907ae271, 0x0b2078c0),
+     TOBN(0xdf31534b, 0x0c9bcddd), TOBN(0x043fb054, 0x39adce83),
+     TOBN(0x99031043, 0xd826846a), TOBN(0x61a9c0d6, 0xb144f393),
+     TOBN(0xdab48046, 0x47718427), TOBN(0xdf17ff9b, 0x6e830f8b),
+     TOBN(0x408d7ee8, 0xe49a1347), TOBN(0x6ac71e23, 0x91c1d4ae),
+     TOBN(0xc8cbb9fd, 0x1defd73c), TOBN(0x19840657, 0xbbbbfec5),
+     TOBN(0x39db1cb5, 0x9e7ef8ea), TOBN(0x78aa8296, 0x64105f30),
+     TOBN(0xa3d9b7f0, 0xa3738c29), TOBN(0x0a2f235a, 0xbc3250a3),
+     TOBN(0x55e506f6, 0x445e4caf), TOBN(0x0974f73d, 0x33475f7a),
+     TOBN(0xd37dbba3, 0x5ba2f5a8), TOBN(0x542c6e63, 0x6af40066),
+     TOBN(0x26d99b53, 0xc5d73e2c), TOBN(0x06060d7d, 0x6c3ca33e),
+     TOBN(0xcdbef1c2, 0x065fef4a), TOBN(0x77e60f7d, 0xfd5b92e3),
+     TOBN(0xd7c549f0, 0x26708350), TOBN(0x201b3ad0, 0x34f121bf),
+     TOBN(0x5fcac2a1, 0x0334fc14), TOBN(0x8a9a9e09, 0x344552f6),
+     TOBN(0x7dd8a1d3, 0x97653082), TOBN(0x5fc0738f, 0x79d4f289),
+     TOBN(0x787d244d, 0x17d2d8c3), TOBN(0xeffc6345, 0x70830684),
+     TOBN(0x5ddb96dd, 0xe4f73ae5), TOBN(0x8efb14b1, 0x172549a5),
+     TOBN(0x6eb73eee, 0x2245ae7a), TOBN(0xbca4061e, 0xea11f13e),
+     TOBN(0xb577421d, 0x30b01f5d), TOBN(0xaa688b24, 0x782e152c),
+     TOBN(0x67608e71, 0xbd3502ba), TOBN(0x4ef41f24, 0xb4de75a0),
+     TOBN(0xb08dde5e, 0xfd6125e5), TOBN(0xde484825, 0xa409543f),
+     TOBN(0x1f198d98, 0x65cc2295), TOBN(0x428a3771, 0x6e0edfa2),
+     TOBN(0x4f9697a2, 0xadf35fc7), TOBN(0x01a43c79, 0xf7cac3c7),
+     TOBN(0xb05d7059, 0x0fd3659a), TOBN(0x8927f30c, 0xbb7f2d9a),
+     TOBN(0x4023d1ac, 0x8cf984d3), TOBN(0x32125ed3, 0x02897a45),
+     TOBN(0xfb572dad, 0x3d414205), TOBN(0x73000ef2, 0xe3fa82a9),
+     TOBN(0x4c0868e9, 0xf10a5581), TOBN(0x5b61fc67, 0x6b0b3ca5),
+     TOBN(0xc1258d5b, 0x7cae440c), TOBN(0x21c08b41, 0x402b7531),
+     TOBN(0xf61a8955, 0xde932321), TOBN(0x3568faf8, 0x2d1408af),
+     TOBN(0x71b15e99, 0x9ecf965b), TOBN(0xf14ed248, 0xe917276f),
+     TOBN(0xc6f4caa1, 0x820cf9e2), TOBN(0x681b20b2, 0x18d83c7e),
+     TOBN(0x6cde738d, 0xc6c01120), TOBN(0x71db0813, 0xae70e0db),
+     TOBN(0x95fc0644, 0x74afe18c), TOBN(0x34619053, 0x129e2be7),
+     TOBN(0x80615cea, 0xdb2a3b15), TOBN(0x0a49a19e, 0xdb4c7073),
+     TOBN(0x0e1b84c8, 0x8fd2d367), TOBN(0xd74bf462, 0x033fb8aa),
+     TOBN(0x889f6d65, 0x533ef217), TOBN(0x7158c7e4, 0xc3ca2e87),
+     TOBN(0xfb670dfb, 0xdc2b4167), TOBN(0x75910a01, 0x844c257f),
+     TOBN(0xf336bf07, 0xcf88577d), TOBN(0x22245250, 0xe45e2ace),
+     TOBN(0x2ed92e8d, 0x7ca23d85), TOBN(0x29f8be4c, 0x2b812f58),
+     TOBN(0xdd9ebaa7, 0x076fe12b), TOBN(0x3f2400cb, 0xae1537f9),
+     TOBN(0x1aa93528, 0x17bdfb46), TOBN(0xc0f98430, 0x67883b41),
+     TOBN(0x5590ede1, 0x0170911d), TOBN(0x7562f5bb, 0x34d4b17f),
+     TOBN(0xe1fa1df2, 0x1826b8d2), TOBN(0xb40b796a, 0x6bd80d59),
+     TOBN(0xd65bf197, 0x3467ba92), TOBN(0x8c9b46db, 0xf70954b0),
+     TOBN(0x97c8a0f3, 0x0e78f15d), TOBN(0xa8f3a69a, 0x85a4c961),
+     TOBN(0x4242660f, 0x61e4ce9b), TOBN(0xbf06aab3, 0x6ea6790c),
+     TOBN(0xc6706f8e, 0xec986416), TOBN(0x9e56dec1, 0x9a9fc225),
+     TOBN(0x527c46f4, 0x9a9898d9), TOBN(0xd799e77b, 0x5633cdef),
+     TOBN(0x24eacc16, 0x7d9e4297), TOBN(0xabb61cea, 0x6b1cb734),
+     TOBN(0xbee2e8a7, 0xf778443c), TOBN(0x3bb42bf1, 0x29de2fe6),
+     TOBN(0xcbed86a1, 0x3003bb6f), TOBN(0xd3918e6c, 0xd781cdf6),
+     TOBN(0x4bee3271, 0x9a5103f1), TOBN(0x5243efc6, 0xf50eac06),
+     TOBN(0xb8e122cb, 0x6adcc119), TOBN(0x1b7faa84, 0xc0b80a08),
+     TOBN(0x32c3d1bd, 0x6dfcd08c), TOBN(0x129dec4e, 0x0be427de),
+     TOBN(0x98ab679c, 0x1d263c83), TOBN(0xafc83cb7, 0xcef64eff),
+     TOBN(0x85eb6088, 0x2fa6be76), TOBN(0x892585fb, 0x1328cbfe),
+     TOBN(0xc154d3ed, 0xcf618dda), TOBN(0xc44f601b, 0x3abaf26e),
+     TOBN(0x7bf57d0b, 0x2be1fdfd), TOBN(0xa833bd2d, 0x21137fee),
+     TOBN(0x9353af36, 0x2db591a8), TOBN(0xc76f26dc, 0x5562a056),
+     TOBN(0x1d87e47d, 0x3fdf5a51), TOBN(0x7afb5f93, 0x55c9cab0),
+     TOBN(0x91bbf58f, 0x89e0586e), TOBN(0x7c72c018, 0x0d843709),
+     TOBN(0xa9a5aafb, 0x99b5c3dc), TOBN(0xa48a0f1d, 0x3844aeb0),
+     TOBN(0x7178b7dd, 0xb667e482), TOBN(0x453985e9, 0x6e23a59a),
+     TOBN(0x4a54c860, 0x01b25dd8), TOBN(0x0dd37f48, 0xfb897c8a),
+     TOBN(0x5f8aa610, 0x0ea90cd9), TOBN(0xc8892c68, 0x16d5830d),
+     TOBN(0xeb4befc0, 0xef514ca5), TOBN(0x478eb679, 0xe72c9ee6),
+     TOBN(0x9bca20da, 0xdbc40d5f), TOBN(0xf015de21, 0xdde4f64a),
+     TOBN(0xaa6a4de0, 0xeaf4b8a5), TOBN(0x68cfd9ca, 0x4bc60e32),
+     TOBN(0x668a4b01, 0x7fd15e70), TOBN(0xd9f0694a, 0xf27dc09d),
+     TOBN(0xf6c3cad5, 0xba708bcd), TOBN(0x5cd2ba69, 0x5bb95c2a),
+     TOBN(0xaa28c1d3, 0x33c0a58f), TOBN(0x23e274e3, 0xabc77870),
+     TOBN(0x44c3692d, 0xdfd20a4a), TOBN(0x091c5fd3, 0x81a66653),
+     TOBN(0x6c0bb691, 0x09a0757d), TOBN(0x9072e8b9, 0x667343ea),
+     TOBN(0x31d40eb0, 0x80848bec), TOBN(0x95bd480a, 0x79fd36cc),
+     TOBN(0x01a77c61, 0x65ed43f5), TOBN(0xafccd127, 0x2e0d40bf),
+     TOBN(0xeccfc82d, 0x1cc1884b), TOBN(0xc85ac201, 0x5d4753b4),
+     TOBN(0xc7a6caac, 0x658e099f), TOBN(0xcf46369e, 0x04b27390),
+     TOBN(0xe2e7d049, 0x506467ea), TOBN(0x481b63a2, 0x37cdeccc),
+     TOBN(0x4029abd8, 0xed80143a), TOBN(0x28bfe3c7, 0xbcb00b88),
+     TOBN(0x3bec1009, 0x0643d84a), TOBN(0x885f3668, 0xabd11041),
+     TOBN(0xdb02432c, 0xf83a34d6), TOBN(0x32f7b360, 0x719ceebe),
+     TOBN(0xf06c7837, 0xdad1fe7a), TOBN(0x60a157a9, 0x5441a0b0),
+     TOBN(0x704970e9, 0xe2d47550), TOBN(0xcd2bd553, 0x271b9020),
+     TOBN(0xff57f82f, 0x33e24a0b), TOBN(0x9cbee23f, 0xf2565079),
+     TOBN(0x16353427, 0xeb5f5825), TOBN(0x276feec4, 0xe948d662),
+     TOBN(0xd1b62bc6, 0xda10032b), TOBN(0x718351dd, 0xf0e72a53),
+     TOBN(0x93452076, 0x2420e7ba), TOBN(0x96368fff, 0x3a00118d),
+     TOBN(0x00ce2d26, 0x150a49e4), TOBN(0x0c28b636, 0x3f04706b),
+     TOBN(0xbad65a46, 0x58b196d0), TOBN(0x6c8455fc, 0xec9f8b7c),
+     TOBN(0xe90c895f, 0x2d71867e), TOBN(0x5c0be31b, 0xedf9f38c),
+     TOBN(0x2a37a15e, 0xd8f6ec04), TOBN(0x239639e7, 0x8cd85251),
+     TOBN(0xd8975315, 0x9c7c4c6b), TOBN(0x603aa3c0, 0xd7409af7),
+     TOBN(0xb8d53d0c, 0x007132fb), TOBN(0x68d12af7, 0xa6849238),
+     TOBN(0xbe0607e7, 0xbf5d9279), TOBN(0x9aa50055, 0xaada74ce),
+     TOBN(0xe81079cb, 0xba7e8ccb), TOBN(0x610c71d1, 0xa5f4ff5e),
+     TOBN(0x9e2ee1a7, 0x5aa07093), TOBN(0xca84004b, 0xa75da47c),
+     TOBN(0x074d3951, 0x3de75401), TOBN(0xf938f756, 0xbb311592),
+     TOBN(0x96197618, 0x00a43421), TOBN(0x39a25362, 0x07bc78c8),
+     TOBN(0x278f710a, 0x0a171276), TOBN(0xb28446ea, 0x8d1a8f08),
+     TOBN(0x184781bf, 0xe3b6a661), TOBN(0x7751cb1d, 0xe6d279f7),
+     TOBN(0xf8ff95d6, 0xc59eb662), TOBN(0x186d90b7, 0x58d3dea7),
+     TOBN(0x0e4bb6c1, 0xdfb4f754), TOBN(0x5c5cf56b, 0x2b2801dc),
+     TOBN(0xc561e452, 0x1f54564d), TOBN(0xb4fb8c60, 0xf0dd7f13),
+     TOBN(0xf8849630, 0x33ff98c7), TOBN(0x9619fffa, 0xcf17769c),
+     TOBN(0xf8090bf6, 0x1bfdd80a), TOBN(0x14d9a149, 0x422cfe63),
+     TOBN(0xb354c360, 0x6f6df9ea), TOBN(0xdbcf770d, 0x218f17ea),
+     TOBN(0x207db7c8, 0x79eb3480), TOBN(0x213dbda8, 0x559b6a26),
+     TOBN(0xac4c200b, 0x29fc81b3), TOBN(0xebc3e09f, 0x171d87c1),
+     TOBN(0x91799530, 0x1481aa9e), TOBN(0x051b92e1, 0x92e114fa),
+     TOBN(0xdf8f92e9, 0xecb5537f), TOBN(0x44b1b2cc, 0x290c7483),
+     TOBN(0xa711455a, 0x2adeb016), TOBN(0x964b6856, 0x81a10c2c),
+     TOBN(0x4f159d99, 0xcec03623), TOBN(0x05532225, 0xef3271ea),
+     TOBN(0xb231bea3, 0xc5ee4849), TOBN(0x57a54f50, 0x7094f103),
+     TOBN(0x3e2d421d, 0x9598b352), TOBN(0xe865a49c, 0x67412ab4),
+     TOBN(0xd2998a25, 0x1cc3a912), TOBN(0x5d092808, 0x0c74d65d),
+     TOBN(0x73f45908, 0x4088567a), TOBN(0xeb6b280e, 0x1f214a61),
+     TOBN(0x8c9adc34, 0xcaf0c13d), TOBN(0x39d12938, 0xf561fb80),
+     TOBN(0xb2dc3a5e, 0xbc6edfb4), TOBN(0x7485b1b1, 0xfe4d210e),
+     TOBN(0x062e0400, 0xe186ae72), TOBN(0x91e32d5c, 0x6eeb3b88),
+     TOBN(0x6df574d7, 0x4be59224), TOBN(0xebc88ccc, 0x716d55f3),
+     TOBN(0x26c2e6d0, 0xcad6ed33), TOBN(0xc6e21e7d, 0x0d3e8b10),
+     TOBN(0x2cc5840e, 0x5bcc36bb), TOBN(0x9292445e, 0x7da74f69),
+     TOBN(0x8be8d321, 0x4e5193a8), TOBN(0x3ec23629, 0x8df06413),
+     TOBN(0xc7e9ae85, 0xb134defa), TOBN(0x6073b1d0, 0x1bb2d475),
+     TOBN(0xb9ad615e, 0x2863c00d), TOBN(0x9e29493d, 0x525f4ac4),
+     TOBN(0xc32b1dea, 0x4e9acf4f), TOBN(0x3e1f01c8, 0xa50db88d),
+     TOBN(0xb05d70ea, 0x04da916c), TOBN(0x714b0d0a, 0xd865803e),
+     TOBN(0x4bd493fc, 0x9920cb5e), TOBN(0x5b44b1f7, 0x92c7a3ac),
+     TOBN(0xa2a77293, 0xbcec9235), TOBN(0x5ee06e87, 0xcd378553),
+     TOBN(0xceff8173, 0xda621607), TOBN(0x2bb03e4c, 0x99f5d290),
+     TOBN(0x2945106a, 0xa6f734ac), TOBN(0xb5056604, 0xd25c4732),
+     TOBN(0x5945920c, 0xe079afee), TOBN(0x686e17a0, 0x6789831f),
+     TOBN(0x5966bee8, 0xb74a5ae5), TOBN(0x38a673a2, 0x1e258d46),
+     TOBN(0xbd1cc1f2, 0x83141c95), TOBN(0x3b2ecf4f, 0x0e96e486),
+     TOBN(0xcd3aa896, 0x74e5fc78), TOBN(0x415ec10c, 0x2482fa7a),
+     TOBN(0x15234419, 0x80503380), TOBN(0x513d917a, 0xd314b392),
+     TOBN(0xb0b52f4e, 0x63caecae), TOBN(0x07bf22ad, 0x2dc7780b),
+     TOBN(0xe761e8a1, 0xe4306839), TOBN(0x1b3be962, 0x5dd7feaa),
+     TOBN(0x4fe728de, 0x74c778f1), TOBN(0xf1fa0bda, 0x5e0070f6),
+     TOBN(0x85205a31, 0x6ec3f510), TOBN(0x2c7e4a14, 0xd2980475),
+     TOBN(0xde3c19c0, 0x6f30ebfd), TOBN(0xdb1c1f38, 0xd4b7e644),
+     TOBN(0xfe291a75, 0x5dce364a), TOBN(0xb7b22a3c, 0x058f5be3),
+     TOBN(0x2cd2c302, 0x37fea38c), TOBN(0x2930967a, 0x2e17be17),
+     TOBN(0x87f009de, 0x0c061c65), TOBN(0xcb014aac, 0xedc6ed44),
+     TOBN(0x49bd1cb4, 0x3bafb1eb), TOBN(0x81bd8b5c, 0x282d3688),
+     TOBN(0x1cdab87e, 0xf01a17af), TOBN(0x21f37ac4, 0xe710063b),
+     TOBN(0x5a6c5676, 0x42fc8193), TOBN(0xf4753e70, 0x56a6015c),
+     TOBN(0x020f795e, 0xa15b0a44), TOBN(0x8f37c8d7, 0x8958a958),
+     TOBN(0x63b7e89b, 0xa4b675b5), TOBN(0xb4fb0c0c, 0x0fc31aea),
+     TOBN(0xed95e639, 0xa7ff1f2e), TOBN(0x9880f5a3, 0x619614fb),
+     TOBN(0xdeb6ff02, 0x947151ab), TOBN(0x5bc5118c, 0xa868dcdb),
+     TOBN(0xd8da2055, 0x4c20cea5), TOBN(0xcac2776e, 0x14c4d69a),
+     TOBN(0xcccb22c1, 0x622d599b), TOBN(0xa4ddb653, 0x68a9bb50),
+     TOBN(0x2c4ff151, 0x1b4941b4), TOBN(0xe1ff19b4, 0x6efba588),
+     TOBN(0x35034363, 0xc48345e0), TOBN(0x45542e3d, 0x1e29dfc4),
+     TOBN(0xf197cb91, 0x349f7aed), TOBN(0x3b2b5a00, 0x8fca8420),
+     TOBN(0x7c175ee8, 0x23aaf6d8), TOBN(0x54dcf421, 0x35af32b6),
+     TOBN(0x0ba14307, 0x27d6561e), TOBN(0x879d5ee4, 0xd175b1e2),
+     TOBN(0xc7c43673, 0x99807db5), TOBN(0x77a54455, 0x9cd55bcd),
+     TOBN(0xe6c2ff13, 0x0105c072), TOBN(0x18f7a99f, 0x8dda7da4),
+     TOBN(0x4c301820, 0x0e2d35c1), TOBN(0x06a53ca0, 0xd9cc6c82),
+     TOBN(0xaa21cc1e, 0xf1aa1d9e), TOBN(0x32414334, 0x4a75b1e8),
+     TOBN(0x2a6d1328, 0x0ebe9fdc), TOBN(0x16bd173f, 0x98a4755a),
+     TOBN(0xfbb9b245, 0x2133ffd9), TOBN(0x39a8b2f1, 0x830f1a20),
+     TOBN(0x484bc97d, 0xd5a1f52a), TOBN(0xd6aebf56, 0xa40eddf8),
+     TOBN(0x32257acb, 0x76ccdac6), TOBN(0xaf4d36ec, 0x1586ff27),
+     TOBN(0x8eaa8863, 0xf8de7dd1), TOBN(0x0045d5cf, 0x88647c16)}
+    ,
+    {TOBN(0xa6f3d574, 0xc005979d), TOBN(0xc2072b42, 0x6a40e350),
+     TOBN(0xfca5c156, 0x8de2ecf9), TOBN(0xa8c8bf5b, 0xa515344e),
+     TOBN(0x97aee555, 0x114df14a), TOBN(0xd4374a4d, 0xfdc5ec6b),
+     TOBN(0x754cc28f, 0x2ca85418), TOBN(0x71cb9e27, 0xd3c41f78),
+     TOBN(0x89105079, 0x03605c39), TOBN(0xf0843d9e, 0xa142c96c),
+     TOBN(0xf3744934, 0x16923684), TOBN(0x732caa2f, 0xfa0a2893),
+     TOBN(0xb2e8c270, 0x61160170), TOBN(0xc32788cc, 0x437fbaa3),
+     TOBN(0x39cd818e, 0xa6eda3ac), TOBN(0xe2e94239, 0x9e2b2e07),
+     TOBN(0x6967d39b, 0x0260e52a), TOBN(0xd42585cc, 0x90653325),
+     TOBN(0x0d9bd605, 0x21ca7954), TOBN(0x4fa20877, 0x81ed57b3),
+     TOBN(0x60c1eff8, 0xe34a0bbe), TOBN(0x56b0040c, 0x84f6ef64),
+     TOBN(0x28be2b24, 0xb1af8483), TOBN(0xb2278163, 0xf5531614),
+     TOBN(0x8df27545, 0x5922ac1c), TOBN(0xa7b3ef5c, 0xa52b3f63),
+     TOBN(0x8e77b214, 0x71de57c4), TOBN(0x31682c10, 0x834c008b),
+     TOBN(0xc76824f0, 0x4bd55d31), TOBN(0xb6d1c086, 0x17b61c71),
+     TOBN(0x31db0903, 0xc2a5089d), TOBN(0x9c092172, 0x184e5d3f),
+     TOBN(0xdd7ced5b, 0xc00cc638), TOBN(0x1a2015eb, 0x61278fc2),
+     TOBN(0x2e8e5288, 0x6a37f8d6), TOBN(0xc457786f, 0xe79933ad),
+     TOBN(0xb3fe4cce, 0x2c51211a), TOBN(0xad9b10b2, 0x24c20498),
+     TOBN(0x90d87a4f, 0xd28db5e5), TOBN(0x698cd105, 0x3aca2fc3),
+     TOBN(0x4f112d07, 0xe91b536d), TOBN(0xceb982f2, 0x9eba09d6),
+     TOBN(0x3c157b2c, 0x197c396f), TOBN(0xe23c2d41, 0x7b66eb24),
+     TOBN(0x480c57d9, 0x3f330d37), TOBN(0xb3a4c8a1, 0x79108deb),
+     TOBN(0x702388de, 0xcb199ce5), TOBN(0x0b019211, 0xb944a8d4),
+     TOBN(0x24f2a692, 0x840bb336), TOBN(0x7c353bdc, 0xa669fa7b),
+     TOBN(0xda20d6fc, 0xdec9c300), TOBN(0x625fbe2f, 0xa13a4f17),
+     TOBN(0xa2b1b61a, 0xdbc17328), TOBN(0x008965bf, 0xa9515621),
+     TOBN(0x49690939, 0xc620ff46), TOBN(0x182dd27d, 0x8717e91c),
+     TOBN(0x5ace5035, 0xea6c3997), TOBN(0x54259aaa, 0xc2610bef),
+     TOBN(0xef18bb3f, 0x3c80dd39), TOBN(0x6910b95b, 0x5fc3fa39),
+     TOBN(0xfce2f510, 0x43e09aee), TOBN(0xced56c9f, 0xa7675665),
+     TOBN(0x10e265ac, 0xd872db61), TOBN(0x6982812e, 0xae9fce69),
+     TOBN(0x29be11c6, 0xce800998), TOBN(0x72bb1752, 0xb90360d9),
+     TOBN(0x2c193197, 0x5a4ad590), TOBN(0x2ba2f548, 0x9fc1dbc0),
+     TOBN(0x7fe4eebb, 0xe490ebe0), TOBN(0x12a0a4cd, 0x7fae11c0),
+     TOBN(0x7197cf81, 0xe903ba37), TOBN(0xcf7d4aa8, 0xde1c6dd8),
+     TOBN(0x92af6bf4, 0x3fd5684c), TOBN(0x2b26eecf, 0x80360aa1),
+     TOBN(0xbd960f30, 0x00546a82), TOBN(0x407b3c43, 0xf59ad8fe),
+     TOBN(0x86cae5fe, 0x249c82ba), TOBN(0x9e0faec7, 0x2463744c),
+     TOBN(0x87f551e8, 0x94916272), TOBN(0x033f9344, 0x6ceb0615),
+     TOBN(0x1e5eb0d1, 0x8be82e84), TOBN(0x89967f0e, 0x7a582fef),
+     TOBN(0xbcf687d5, 0xa6e921fa), TOBN(0xdfee4cf3, 0xd37a09ba),
+     TOBN(0x94f06965, 0xb493c465), TOBN(0x638b9a1c, 0x7635c030),
+     TOBN(0x76667864, 0x66f05e9f), TOBN(0xccaf6808, 0xc04da725),
+     TOBN(0xca2eb690, 0x768fccfc), TOBN(0xf402d37d, 0xb835b362),
+     TOBN(0x0efac0d0, 0xe2fdfcce), TOBN(0xefc9cdef, 0xb638d990),
+     TOBN(0x2af12b72, 0xd1669a8b), TOBN(0x33c536bc, 0x5774ccbd),
+     TOBN(0x30b21909, 0xfb34870e), TOBN(0xc38fa2f7, 0x7df25aca),
+     TOBN(0x74c5f02b, 0xbf81f3f5), TOBN(0x0525a5ae, 0xaf7e4581),
+     TOBN(0x88d2aaba, 0x433c54ae), TOBN(0xed9775db, 0x806a56c5),
+     TOBN(0xd320738a, 0xc0edb37d), TOBN(0x25fdb6ee, 0x66cc1f51),
+     TOBN(0xac661d17, 0x10600d76), TOBN(0x931ec1f3, 0xbdd1ed76),
+     TOBN(0x65c11d62, 0x19ee43f1), TOBN(0x5cd57c3e, 0x60829d97),
+     TOBN(0xd26c91a3, 0x984be6e8), TOBN(0xf08d9309, 0x8b0c53bd),
+     TOBN(0x94bc9e5b, 0xc016e4ea), TOBN(0xd3916839, 0x11d43d2b),
+     TOBN(0x886c5ad7, 0x73701155), TOBN(0xe0377626, 0x20b00715),
+     TOBN(0x7f01c9ec, 0xaa80ba59), TOBN(0x3083411a, 0x68538e51),
+     TOBN(0x970370f1, 0xe88128af), TOBN(0x625cc3db, 0x91dec14b),
+     TOBN(0xfef9666c, 0x01ac3107), TOBN(0xb2a8d577, 0xd5057ac3),
+     TOBN(0xb0f26299, 0x92be5df7), TOBN(0xf579c8e5, 0x00353924),
+     TOBN(0xb8fa3d93, 0x1341ed7a), TOBN(0x4223272c, 0xa7b59d49),
+     TOBN(0x3dcb1947, 0x83b8c4a4), TOBN(0x4e413c01, 0xed1302e4),
+     TOBN(0x6d999127, 0xe17e44ce), TOBN(0xee86bf75, 0x33b3adfb),
+     TOBN(0xf6902fe6, 0x25aa96ca), TOBN(0xb73540e4, 0xe5aae47d),
+     TOBN(0x32801d7b, 0x1b4a158c), TOBN(0xe571c99e, 0x27e2a369),
+     TOBN(0x40cb76c0, 0x10d9f197), TOBN(0xc308c289, 0x3167c0ae),
+     TOBN(0xa6ef9dd3, 0xeb7958f2), TOBN(0xa7226dfc, 0x300879b1),
+     TOBN(0x6cd0b362, 0x7edf0636), TOBN(0x4efbce6c, 0x7bc37eed),
+     TOBN(0x75f92a05, 0x8d699021), TOBN(0x586d4c79, 0x772566e3),
+     TOBN(0x378ca5f1, 0x761ad23a), TOBN(0x650d86fc, 0x1465a8ac),
+     TOBN(0x7a4ed457, 0x842ba251), TOBN(0x6b65e3e6, 0x42234933),
+     TOBN(0xaf1543b7, 0x31aad657), TOBN(0xa4cefe98, 0xcbfec369),
+     TOBN(0xb587da90, 0x9f47befb), TOBN(0x6562e9fb, 0x41312d13),
+     TOBN(0xa691ea59, 0xeff1cefe), TOBN(0xcc30477a, 0x05fc4cf6),
+     TOBN(0xa1632461, 0x0b0ffd3d), TOBN(0xa1f16f3b, 0x5b355956),
+     TOBN(0x5b148d53, 0x4224ec24), TOBN(0xdc834e7b, 0xf977012a),
+     TOBN(0x7bfc5e75, 0xb2c69dbc), TOBN(0x3aa77a29, 0x03c3da6c),
+     TOBN(0xde0df03c, 0xca910271), TOBN(0xcbd5ca4a, 0x7806dc55),
+     TOBN(0xe1ca5807, 0x6db476cb), TOBN(0xfde15d62, 0x5f37a31e),
+     TOBN(0xf49af520, 0xf41af416), TOBN(0x96c5c5b1, 0x7d342db5),
+     TOBN(0x155c43b7, 0xeb4ceb9b), TOBN(0x2e993010, 0x4e77371a),
+     TOBN(0x1d2987da, 0x675d43af), TOBN(0xef2bc1c0, 0x8599fd72),
+     TOBN(0x96894b7b, 0x9342f6b2), TOBN(0x201eadf2, 0x7c8e71f0),
+     TOBN(0xf3479d9f, 0x4a1f3efc), TOBN(0xe0f8a742, 0x702a9704),
+     TOBN(0xeafd44b6, 0xb3eba40c), TOBN(0xf9739f29, 0xc1c1e0d0),
+     TOBN(0x0091471a, 0x619d505e), TOBN(0xc15f9c96, 0x9d7c263e),
+     TOBN(0x5be47285, 0x83afbe33), TOBN(0xa3b6d6af, 0x04f1e092),
+     TOBN(0xe76526b9, 0x751a9d11), TOBN(0x2ec5b26d, 0x9a4ae4d2),
+     TOBN(0xeb66f4d9, 0x02f6fb8d), TOBN(0x4063c561, 0x96912164),
+     TOBN(0xeb7050c1, 0x80ef3000), TOBN(0x288d1c33, 0xeaa5b3f0),
+     TOBN(0xe87c68d6, 0x07806fd8), TOBN(0xb2f7f9d5, 0x4bbbf50f),
+     TOBN(0x25972f3a, 0xac8d6627), TOBN(0xf8547774, 0x10e8c13b),
+     TOBN(0xcc50ef6c, 0x872b4a60), TOBN(0xab2a34a4, 0x4613521b),
+     TOBN(0x39c5c190, 0x983e15d1), TOBN(0x61dde5df, 0x59905512),
+     TOBN(0xe417f621, 0x9f2275f3), TOBN(0x0750c8b6, 0x451d894b),
+     TOBN(0x75b04ab9, 0x78b0bdaa), TOBN(0x3bfd9fd4, 0x458589bd),
+     TOBN(0xf1013e30, 0xee9120b6), TOBN(0x2b51af93, 0x23a4743e),
+     TOBN(0xea96ffae, 0x48d14d9e), TOBN(0x71dc0dbe, 0x698a1d32),
+     TOBN(0x914962d2, 0x0180cca4), TOBN(0x1ae60677, 0xc3568963),
+     TOBN(0x8cf227b1, 0x437bc444), TOBN(0xc650c83b, 0xc9962c7a),
+     TOBN(0x23c2c7dd, 0xfe7ccfc4), TOBN(0xf925c89d, 0x1b929d48),
+     TOBN(0x4460f74b, 0x06783c33), TOBN(0xac2c8d49, 0xa590475a),
+     TOBN(0xfb40b407, 0xb807bba0), TOBN(0x9d1e362d, 0x69ff8f3a),
+     TOBN(0xa33e9681, 0xcbef64a4), TOBN(0x67ece5fa, 0x332fb4b2),
+     TOBN(0x6900a99b, 0x739f10e3), TOBN(0xc3341ca9, 0xff525925),
+     TOBN(0xee18a626, 0xa9e2d041), TOBN(0xa5a83685, 0x29580ddd),
+     TOBN(0xf3470c81, 0x9d7de3cd), TOBN(0xedf02586, 0x2062cf9c),
+     TOBN(0xf43522fa, 0xc010edb0), TOBN(0x30314135, 0x13a4b1ae),
+     TOBN(0xc792e02a, 0xdb22b94b), TOBN(0x993d8ae9, 0xa1eaa45b),
+     TOBN(0x8aad6cd3, 0xcd1e1c63), TOBN(0x89529ca7, 0xc5ce688a),
+     TOBN(0x2ccee3aa, 0xe572a253), TOBN(0xe02b6438, 0x02a21efb),
+     TOBN(0xa7091b6e, 0xc9430358), TOBN(0x06d1b1fa, 0x9d7db504),
+     TOBN(0x58846d32, 0xc4744733), TOBN(0x40517c71, 0x379f9e34),
+     TOBN(0x2f65655f, 0x130ef6ca), TOBN(0x526e4488, 0xf1f3503f),
+     TOBN(0x8467bd17, 0x7ee4a976), TOBN(0x1d9dc913, 0x921363d1),
+     TOBN(0xd8d24c33, 0xb069e041), TOBN(0x5eb5da0a, 0x2cdf7f51),
+     TOBN(0x1c0f3cb1, 0x197b994f), TOBN(0x3c95a6c5, 0x2843eae9),
+     TOBN(0x7766ffc9, 0xa6097ea5), TOBN(0x7bea4093, 0xd723b867),
+     TOBN(0xb48e1f73, 0x4db378f9), TOBN(0x70025b00, 0xe37b77ac),
+     TOBN(0x943dc8e7, 0xaf24ad46), TOBN(0xb98a15ac, 0x16d00a85),
+     TOBN(0x3adc38ba, 0x2743b004), TOBN(0xb1c7f4f7, 0x334415ee),
+     TOBN(0xea43df8f, 0x1e62d05a), TOBN(0x32618905, 0x9d76a3b6),
+     TOBN(0x2fbd0bb5, 0xa23a0f46), TOBN(0x5bc971db, 0x6a01918c),
+     TOBN(0x7801d94a, 0xb4743f94), TOBN(0xb94df65e, 0x676ae22b),
+     TOBN(0xaafcbfab, 0xaf95894c), TOBN(0x7b9bdc07, 0x276b2241),
+     TOBN(0xeaf98362, 0x5bdda48b), TOBN(0x5977faf2, 0xa3fcb4df),
+     TOBN(0xbed042ef, 0x052c4b5b), TOBN(0x9fe87f71, 0x067591f0),
+     TOBN(0xc89c73ca, 0x22f24ec7), TOBN(0x7d37fa9e, 0xe64a9f1b),
+     TOBN(0x2710841a, 0x15562627), TOBN(0x2c01a613, 0xc243b034),
+     TOBN(0x1d135c56, 0x2bc68609), TOBN(0xc2ca1715, 0x8b03f1f6),
+     TOBN(0xc9966c2d, 0x3eb81d82), TOBN(0xc02abf4a, 0x8f6df13e),
+     TOBN(0x77b34bd7, 0x8f72b43b), TOBN(0xaff6218f, 0x360c82b0),
+     TOBN(0x0aa5726c, 0x8d55b9d2), TOBN(0xdc0adbe9, 0x99e9bffb),
+     TOBN(0x9097549c, 0xefb9e72a), TOBN(0x16755712, 0x9dfb3111),
+     TOBN(0xdd8bf984, 0xf26847f9), TOBN(0xbcb8e387, 0xdfb30cb7),
+     TOBN(0xc1fd32a7, 0x5171ef9c), TOBN(0x977f3fc7, 0x389b363f),
+     TOBN(0x116eaf2b, 0xf4babda0), TOBN(0xfeab68bd, 0xf7113c8e),
+     TOBN(0xd1e3f064, 0xb7def526), TOBN(0x1ac30885, 0xe0b3fa02),
+     TOBN(0x1c5a6e7b, 0x40142d9d), TOBN(0x839b5603, 0x30921c0b),
+     TOBN(0x48f301fa, 0x36a116a3), TOBN(0x380e1107, 0xcfd9ee6d),
+     TOBN(0x7945ead8, 0x58854be1), TOBN(0x4111c12e, 0xcbd4d49d),
+     TOBN(0xece3b1ec, 0x3a29c2ef), TOBN(0x6356d404, 0x8d3616f5),
+     TOBN(0x9f0d6a8f, 0x594d320e), TOBN(0x0989316d, 0xf651ccd2),
+     TOBN(0x6c32117a, 0x0f8fdde4), TOBN(0x9abe5cc5, 0xa26a9bbc),
+     TOBN(0xcff560fb, 0x9723f671), TOBN(0x21b2a12d, 0x7f3d593c),
+     TOBN(0xe4cb18da, 0x24ba0696), TOBN(0x186e2220, 0xc3543384),
+     TOBN(0x722f64e0, 0x88312c29), TOBN(0x94282a99, 0x17dc7752),
+     TOBN(0x62467bbf, 0x5a85ee89), TOBN(0xf435c650, 0xf10076a0),
+     TOBN(0xc9ff1539, 0x43b3a50b), TOBN(0x7132130c, 0x1a53efbc),
+     TOBN(0x31bfe063, 0xf7b0c5b7), TOBN(0xb0179a7d, 0x4ea994cc),
+     TOBN(0x12d064b3, 0xc85f455b), TOBN(0x47259328, 0x8f6e0062),
+     TOBN(0xf64e590b, 0xb875d6d9), TOBN(0x22dd6225, 0xad92bcc7),
+     TOBN(0xb658038e, 0xb9c3bd6d), TOBN(0x00cdb0d6, 0xfbba27c8),
+     TOBN(0x0c681337, 0x1062c45d), TOBN(0xd8515b8c, 0x2d33407d),
+     TOBN(0xcb8f699e, 0x8cbb5ecf), TOBN(0x8c4347f8, 0xc608d7d8),
+     TOBN(0x2c11850a, 0xbb3e00db), TOBN(0x20a8dafd, 0xecb49d19),
+     TOBN(0xbd781480, 0x45ee2f40), TOBN(0x75e354af, 0x416b60cf),
+     TOBN(0xde0b58a1, 0x8d49a8c4), TOBN(0xe40e94e2, 0xfa359536),
+     TOBN(0xbd4fa59f, 0x62accd76), TOBN(0x05cf466a, 0x8c762837),
+     TOBN(0xb5abda99, 0x448c277b), TOBN(0x5a9e01bf, 0x48b13740),
+     TOBN(0x9d457798, 0x326aad8d), TOBN(0xbdef4954, 0xc396f7e7),
+     TOBN(0x6fb274a2, 0xc253e292), TOBN(0x2800bf0a, 0x1cfe53e7),
+     TOBN(0x22426d31, 0x44438fd4), TOBN(0xef233923, 0x5e259f9a),
+     TOBN(0x4188503c, 0x03f66264), TOBN(0x9e5e7f13, 0x7f9fdfab),
+     TOBN(0x565eb76c, 0x5fcc1aba), TOBN(0xea632548, 0x59b5bff8),
+     TOBN(0x5587c087, 0xaab6d3fa), TOBN(0x92b639ea, 0x6ce39c1b),
+     TOBN(0x0706e782, 0x953b135c), TOBN(0x7308912e, 0x425268ef),
+     TOBN(0x599e92c7, 0x090e7469), TOBN(0x83b90f52, 0x9bc35e75),
+     TOBN(0x4750b3d0, 0x244975b3), TOBN(0xf3a44358, 0x11965d72),
+     TOBN(0x179c6774, 0x9c8dc751), TOBN(0xff18cdfe, 0xd23d9ff0),
+     TOBN(0xc4013833, 0x2028e247), TOBN(0x96e280e2, 0xf3bfbc79),
+     TOBN(0xf60417bd, 0xd0880a84), TOBN(0x263c9f3d, 0x2a568151),
+     TOBN(0x36be15b3, 0x2d2ce811), TOBN(0x846dc0c2, 0xf8291d21),
+     TOBN(0x5cfa0ecb, 0x789fcfdb), TOBN(0x45a0beed, 0xd7535b9a),
+     TOBN(0xec8e9f07, 0x96d69af1), TOBN(0x31a7c5b8, 0x599ab6dc),
+     TOBN(0xd36d45ef, 0xf9e2e09f), TOBN(0x3cf49ef1, 0xdcee954b),
+     TOBN(0x6be34cf3, 0x086cff9b), TOBN(0x88dbd491, 0x39a3360f),
+     TOBN(0x1e96b8cc, 0x0dbfbd1d), TOBN(0xc1e5f7bf, 0xcb7e2552),
+     TOBN(0x0547b214, 0x28819d98), TOBN(0xc770dd9c, 0x7aea9dcb),
+     TOBN(0xaef0d4c7, 0x041d68c8), TOBN(0xcc2b9818, 0x13cb9ba8),
+     TOBN(0x7fc7bc76, 0xfe86c607), TOBN(0x6b7b9337, 0x502a9a95),
+     TOBN(0x1948dc27, 0xd14dab63), TOBN(0x249dd198, 0xdae047be),
+     TOBN(0xe8356584, 0xa981a202), TOBN(0x3531dd18, 0x3a893387),
+     TOBN(0x1be11f90, 0xc85c7209), TOBN(0x93d2fe1e, 0xe2a52b5a),
+     TOBN(0x8225bfe2, 0xec6d6b97), TOBN(0x9cf6d6f4, 0xbd0aa5de),
+     TOBN(0x911459cb, 0x54779f5f), TOBN(0x5649cddb, 0x86aeb1f3),
+     TOBN(0x32133579, 0x3f26ce5a), TOBN(0xc289a102, 0x550f431e),
+     TOBN(0x559dcfda, 0x73b84c6f), TOBN(0x84973819, 0xee3ac4d7),
+     TOBN(0xb51e55e6, 0xf2606a82), TOBN(0xe25f7061, 0x90f2fb57),
+     TOBN(0xacef6c2a, 0xb1a4e37c), TOBN(0x864e359d, 0x5dcf2706),
+     TOBN(0x479e6b18, 0x7ce57316), TOBN(0x2cab2500, 0x3a96b23d),
+     TOBN(0xed489862, 0x8ef16df7), TOBN(0x2056538c, 0xef3758b5),
+     TOBN(0xa7df865e, 0xf15d3101), TOBN(0x80c5533a, 0x61b553d7),
+     TOBN(0x366e1997, 0x4ed14294), TOBN(0x6620741f, 0xb3c0bcd6),
+     TOBN(0x21d1d9c4, 0xedc45418), TOBN(0x005b859e, 0xc1cc4a9d),
+     TOBN(0xdf01f630, 0xa1c462f0), TOBN(0x15d06cf3, 0xf26820c7),
+     TOBN(0x9f7f24ee, 0x3484be47), TOBN(0x2ff33e96, 0x4a0c902f),
+     TOBN(0x00bdf457, 0x5a0bc453), TOBN(0x2378dfaf, 0x1aa238db),
+     TOBN(0x272420ec, 0x856720f2), TOBN(0x2ad9d95b, 0x96797291),
+     TOBN(0xd1242cc6, 0x768a1558), TOBN(0x2e287f8b, 0x5cc86aa8),
+     TOBN(0x796873d0, 0x990cecaa), TOBN(0xade55f81, 0x675d4080),
+     TOBN(0x2645eea3, 0x21f0cd84), TOBN(0x7a1efa0f, 0xb4e17d02),
+     TOBN(0xf6858420, 0x037cc061), TOBN(0x682e05f0, 0xd5d43e12),
+     TOBN(0x59c36994, 0x27218710), TOBN(0x85cbba4d, 0x3f7cd2fc),
+     TOBN(0x726f9729, 0x7a3cd22a), TOBN(0x9f8cd5dc, 0x4a628397),
+     TOBN(0x17b93ab9, 0xc23165ed), TOBN(0xff5f5dbf, 0x122823d4),
+     TOBN(0xc1e4e4b5, 0x654a446d), TOBN(0xd1a9496f, 0x677257ba),
+     TOBN(0x6387ba94, 0xde766a56), TOBN(0x23608bc8, 0x521ec74a),
+     TOBN(0x16a522d7, 0x6688c4d4), TOBN(0x9d6b4282, 0x07373abd),
+     TOBN(0xa62f07ac, 0xb42efaa3), TOBN(0xf73e00f7, 0xe3b90180),
+     TOBN(0x36175fec, 0x49421c3e), TOBN(0xc4e44f9b, 0x3dcf2678),
+     TOBN(0x76df436b, 0x7220f09f), TOBN(0x172755fb, 0x3aa8b6cf),
+     TOBN(0xbab89d57, 0x446139cc), TOBN(0x0a0a6e02, 0x5fe0208f),
+     TOBN(0xcdbb63e2, 0x11e5d399), TOBN(0x33ecaa12, 0xa8977f0b),
+     TOBN(0x59598b21, 0xf7c42664), TOBN(0xb3e91b32, 0xab65d08a),
+     TOBN(0x035822ee, 0xf4502526), TOBN(0x1dcf0176, 0x720a82a9),
+     TOBN(0x50f8598f, 0x3d589e02), TOBN(0xdf0478ff, 0xb1d63d2c),
+     TOBN(0x8b8068bd, 0x1571cd07), TOBN(0x30c3aa4f, 0xd79670cd),
+     TOBN(0x25e8fd4b, 0x941ade7f), TOBN(0x3d1debdc, 0x32790011),
+     TOBN(0x65b6dcbd, 0x3a3f9ff0), TOBN(0x282736a4, 0x793de69c),
+     TOBN(0xef69a0c3, 0xd41d3bd3), TOBN(0xb533b8c9, 0x07a26bde),
+     TOBN(0xe2801d97, 0xdb2edf9f), TOBN(0xdc4a8269, 0xe1877af0),
+     TOBN(0x6c1c5851, 0x3d590dbe), TOBN(0x84632f6b, 0xee4e9357),
+     TOBN(0xd36d36b7, 0x79b33374), TOBN(0xb46833e3, 0x9bbca2e6),
+     TOBN(0x37893913, 0xf7fc0586), TOBN(0x385315f7, 0x66bf4719),
+     TOBN(0x72c56293, 0xb31855dc), TOBN(0xd1416d4e, 0x849061fe),
+     TOBN(0xbeb3ab78, 0x51047213), TOBN(0x447f6e61, 0xf040c996),
+     TOBN(0xd06d310d, 0x638b1d0c), TOBN(0xe28a413f, 0xbad1522e),
+     TOBN(0x685a76cb, 0x82003f86), TOBN(0x610d07f7, 0x0bcdbca3),
+     TOBN(0x6ff66021, 0x9ca4c455), TOBN(0x7df39b87, 0xcea10eec),
+     TOBN(0xb9255f96, 0xe22db218), TOBN(0x8cc6d9eb, 0x08a34c44),
+     TOBN(0xcd4ffb86, 0x859f9276), TOBN(0x8fa15eb2, 0x50d07335),
+     TOBN(0xdf553845, 0xcf2c24b5), TOBN(0x89f66a9f, 0x52f9c3ba),
+     TOBN(0x8f22b5b9, 0xe4a7ceb3), TOBN(0xaffef809, 0x0e134686),
+     TOBN(0x3e53e1c6, 0x8eb8fac2), TOBN(0x93c1e4eb, 0x28aec98e),
+     TOBN(0xb6b91ec5, 0x32a43bcb), TOBN(0x2dbfa947, 0xb2d74a51),
+     TOBN(0xe065d190, 0xca84bad7), TOBN(0xfb13919f, 0xad58e65c),
+     TOBN(0x3c41718b, 0xf1cb6e31), TOBN(0x688969f0, 0x06d05c3f),
+     TOBN(0xd4f94ce7, 0x21264d45), TOBN(0xfdfb65e9, 0x7367532b),
+     TOBN(0x5b1be8b1, 0x0945a39d), TOBN(0x229f789c, 0x2b8baf3b),
+     TOBN(0xd8f41f3e, 0x6f49f15d), TOBN(0x678ce828, 0x907f0792),
+     TOBN(0xc69ace82, 0xfca6e867), TOBN(0x106451ae, 0xd01dcc89),
+     TOBN(0x1bb4f7f0, 0x19fc32d2), TOBN(0x64633dfc, 0xb00c52d2),
+     TOBN(0x8f13549a, 0xad9ea445), TOBN(0x99a3bf50, 0xfb323705),
+     TOBN(0x0c9625a2, 0x534d4dbc), TOBN(0x45b8f1d1, 0xc2a2fea3),
+     TOBN(0x76ec21a1, 0xa530fc1a), TOBN(0x4bac9c2a, 0x9e5bd734),
+     TOBN(0x5996d76a, 0x7b4e3587), TOBN(0x0045cdee, 0x1182d9e3),
+     TOBN(0x1aee24b9, 0x1207f13d), TOBN(0x66452e97, 0x97345a41),
+     TOBN(0x16e5b054, 0x9f950cd0), TOBN(0x9cc72fb1, 0xd7fdd075),
+     TOBN(0x6edd61e7, 0x66249663), TOBN(0xde4caa4d, 0xf043cccb),
+     TOBN(0x11b1f57a, 0x55c7ac17), TOBN(0x779cbd44, 0x1a85e24d),
+     TOBN(0x78030f86, 0xe46081e7), TOBN(0xfd4a6032, 0x8e20f643),
+     TOBN(0xcc7a6488, 0x0a750c0f), TOBN(0x39bacfe3, 0x4e548e83),
+     TOBN(0x3d418c76, 0x0c110f05), TOBN(0x3e4daa4c, 0xb1f11588),
+     TOBN(0x2733e7b5, 0x5ffc69ff), TOBN(0x46f147bc, 0x92053127),
+     TOBN(0x885b2434, 0xd722df94), TOBN(0x6a444f65, 0xe6fc6b7c)}
+    ,
+    {TOBN(0x7a1a465a, 0xc3f16ea8), TOBN(0x115a461d, 0xb2f1d11c),
+     TOBN(0x4767dd95, 0x6c68a172), TOBN(0x3392f2eb, 0xd13a4698),
+     TOBN(0xc7a99ccd, 0xe526cdc7), TOBN(0x8e537fdc, 0x22292b81),
+     TOBN(0x76d8cf69, 0xa6d39198), TOBN(0xffc5ff43, 0x2446852d),
+     TOBN(0x97b14f7e, 0xa90567e6), TOBN(0x513257b7, 0xb6ae5cb7),
+     TOBN(0x85454a3c, 0x9f10903d), TOBN(0xd8d2c9ad, 0x69bc3724),
+     TOBN(0x38da9324, 0x6b29cb44), TOBN(0xb540a21d, 0x77c8cbac),
+     TOBN(0x9bbfe435, 0x01918e42), TOBN(0xfffa707a, 0x56c3614e),
+     TOBN(0x0ce4e3f1, 0xd4e353b7), TOBN(0x062d8a14, 0xef46b0a0),
+     TOBN(0x6408d5ab, 0x574b73fd), TOBN(0xbc41d1c9, 0xd3273ffd),
+     TOBN(0x3538e1e7, 0x6be77800), TOBN(0x71fe8b37, 0xc5655031),
+     TOBN(0x1cd91621, 0x6b9b331a), TOBN(0xad825d0b, 0xbb388f73),
+     TOBN(0x56c2e05b, 0x1cb76219), TOBN(0x0ec0bf91, 0x71567e7e),
+     TOBN(0xe7076f86, 0x61c4c910), TOBN(0xd67b085b, 0xbabc04d9),
+     TOBN(0x9fb90459, 0x5e93a96a), TOBN(0x7526c1ea, 0xfbdc249a),
+     TOBN(0x0d44d367, 0xecdd0bb7), TOBN(0x95399917, 0x9dc0d695),
+     TOBN(0x61360ee9, 0x9e240d18), TOBN(0x057cdcac, 0xb4b94466),
+     TOBN(0xe7667cd1, 0x2fe5325c), TOBN(0x1fa297b5, 0x21974e3b),
+     TOBN(0xfa4081e7, 0xdb083d76), TOBN(0x31993be6, 0xf206bd15),
+     TOBN(0x8949269b, 0x14c19f8c), TOBN(0x21468d72, 0xa9d92357),
+     TOBN(0x2ccbc583, 0xa4c506ec), TOBN(0x957ed188, 0xd1acfe97),
+     TOBN(0x8baed833, 0x12f1aea2), TOBN(0xef2a6cb4, 0x8325362d),
+     TOBN(0x130dde42, 0x8e195c43), TOBN(0xc842025a, 0x0e6050c6),
+     TOBN(0x2da972a7, 0x08686a5d), TOBN(0xb52999a1, 0xe508b4a8),
+     TOBN(0xd9f090b9, 0x10a5a8bd), TOBN(0xca91d249, 0x096864da),
+     TOBN(0x8e6a93be, 0x3f67dbc1), TOBN(0xacae6fba, 0xf5f4764c),
+     TOBN(0x1563c6e0, 0xd21411a0), TOBN(0x28fa787f, 0xda0a4ad8),
+     TOBN(0xd524491c, 0x908c8030), TOBN(0x1257ba0e, 0x4c795f07),
+     TOBN(0x83f49167, 0xceca9754), TOBN(0x426d2cf6, 0x4b7939a0),
+     TOBN(0x2555e355, 0x723fd0bf), TOBN(0xa96e6d06, 0xc4f144e2),
+     TOBN(0x4768a8dd, 0x87880e61), TOBN(0x15543815, 0xe508e4d5),
+     TOBN(0x09d7e772, 0xb1b65e15), TOBN(0x63439dd6, 0xac302fa0),
+     TOBN(0xb93f802f, 0xc14e35c2), TOBN(0x71735b7c, 0x4341333c),
+     TOBN(0x03a25104, 0x16d4f362), TOBN(0x3f4d069b, 0xbf433c8e),
+     TOBN(0x0d83ae01, 0xf78f5a7c), TOBN(0x50a8ffbe, 0x7c4eed07),
+     TOBN(0xc74f8906, 0x76e10f83), TOBN(0x7d080966, 0x9ddaf8e1),
+     TOBN(0xb11df8e1, 0x698e04cc), TOBN(0x877be203, 0x169005c8),
+     TOBN(0x32749e8c, 0x4f3c6179), TOBN(0x2dbc9d0a, 0x7853fc05),
+     TOBN(0x187d4f93, 0x9454d937), TOBN(0xe682ce9d, 0xb4800e1b),
+     TOBN(0xa9129ad8, 0x165e68e8), TOBN(0x0fe29735, 0xbe7f785b),
+     TOBN(0x5303f40c, 0x5b9e02b7), TOBN(0xa37c9692, 0x35ee04e8),
+     TOBN(0x5f46cc20, 0x34d6632b), TOBN(0x55ef72b2, 0x96ac545b),
+     TOBN(0xabec5c1f, 0x7b91b062), TOBN(0x0a79e1c7, 0xbb33e821),
+     TOBN(0xbb04b428, 0x3a9f4117), TOBN(0x0de1f28f, 0xfd2a475a),
+     TOBN(0x31019ccf, 0x3a4434b4), TOBN(0xa3458111, 0x1a7954dc),
+     TOBN(0xa9dac80d, 0xe34972a7), TOBN(0xb043d054, 0x74f6b8dd),
+     TOBN(0x021c319e, 0x11137b1a), TOBN(0x00a754ce, 0xed5cc03f),
+     TOBN(0x0aa2c794, 0xcbea5ad4), TOBN(0x093e67f4, 0x70c015b6),
+     TOBN(0x72cdfee9, 0xc97e3f6b), TOBN(0xc10bcab4, 0xb6da7461),
+     TOBN(0x3b02d2fc, 0xb59806b9), TOBN(0x85185e89, 0xa1de6f47),
+     TOBN(0x39e6931f, 0x0eb6c4d4), TOBN(0x4d4440bd, 0xd4fa5b04),
+     TOBN(0x5418786e, 0x34be7eb8), TOBN(0x6380e521, 0x9d7259bc),
+     TOBN(0x20ac0351, 0xd598d710), TOBN(0x272c4166, 0xcb3a4da4),
+     TOBN(0xdb82fe1a, 0xca71de1f), TOBN(0x746e79f2, 0xd8f54b0f),
+     TOBN(0x6e7fc736, 0x4b573e9b), TOBN(0x75d03f46, 0xfd4b5040),
+     TOBN(0x5c1cc36d, 0x0b98d87b), TOBN(0x513ba3f1, 0x1f472da1),
+     TOBN(0x79d0af26, 0xabb177dd), TOBN(0xf82ab568, 0x7891d564),
+     TOBN(0x2b6768a9, 0x72232173), TOBN(0xefbb3bb0, 0x8c1f6619),
+     TOBN(0xb29c11db, 0xa6d18358), TOBN(0x519e2797, 0xb0916d3a),
+     TOBN(0xd4dc18f0, 0x9188e290), TOBN(0x648e86e3, 0x98b0ca7f),
+     TOBN(0x859d3145, 0x983c38b5), TOBN(0xb14f176c, 0x637abc8b),
+     TOBN(0x2793fb9d, 0xcaff7be6), TOBN(0xebe5a55f, 0x35a66a5a),
+     TOBN(0x7cec1dcd, 0x9f87dc59), TOBN(0x7c595cd3, 0xfbdbf560),
+     TOBN(0x5b543b22, 0x26eb3257), TOBN(0x69080646, 0xc4c935fd),
+     TOBN(0x7f2e4403, 0x81e9ede3), TOBN(0x243c3894, 0xcaf6df0a),
+     TOBN(0x7c605bb1, 0x1c073b11), TOBN(0xcd06a541, 0xba6a4a62),
+     TOBN(0x29168949, 0x49d4e2e5), TOBN(0x33649d07, 0x4af66880),
+     TOBN(0xbfc0c885, 0xe9a85035), TOBN(0xb4e52113, 0xfc410f4b),
+     TOBN(0xdca3b706, 0x78a6513b), TOBN(0x92ea4a2a, 0x9edb1943),
+     TOBN(0x02642216, 0xdb6e2dd8), TOBN(0x9b45d0b4, 0x9fd57894),
+     TOBN(0x114e70db, 0xc69d11ae), TOBN(0x1477dd19, 0x4c57595f),
+     TOBN(0xbc2208b4, 0xec77c272), TOBN(0x95c5b4d7, 0xdb68f59c),
+     TOBN(0xb8c4fc63, 0x42e532b7), TOBN(0x386ba422, 0x9ae35290),
+     TOBN(0xfb5dda42, 0xd201ecbc), TOBN(0x2353dc8b, 0xa0e38fd6),
+     TOBN(0x9a0b85ea, 0x68f7e978), TOBN(0x96ec5682, 0x2ad6d11f),
+     TOBN(0x5e279d6c, 0xe5f6886d), TOBN(0xd3fe03cd, 0x3cb1914d),
+     TOBN(0xfe541fa4, 0x7ea67c77), TOBN(0x952bd2af, 0xe3ea810c),
+     TOBN(0x791fef56, 0x8d01d374), TOBN(0xa3a1c621, 0x0f11336e),
+     TOBN(0x5ad0d5a9, 0xc7ec6d79), TOBN(0xff7038af, 0x3225c342),
+     TOBN(0x003c6689, 0xbc69601b), TOBN(0x25059bc7, 0x45e8747d),
+     TOBN(0xfa4965b2, 0xf2086fbf), TOBN(0xf6840ea6, 0x86916078),
+     TOBN(0xd7ac7620, 0x70081d6c), TOBN(0xe600da31, 0xb5328645),
+     TOBN(0x01916f63, 0x529b8a80), TOBN(0xe80e4858, 0x2d7d6f3e),
+     TOBN(0x29eb0fe8, 0xd664ca7c), TOBN(0xf017637b, 0xe7b43b0c),
+     TOBN(0x9a75c806, 0x76cb2566), TOBN(0x8f76acb1, 0xb24892d9),
+     TOBN(0x7ae7b9cc, 0x1f08fe45), TOBN(0x19ef7329, 0x6a4907d8),
+     TOBN(0x2db4ab71, 0x5f228bf0), TOBN(0xf3cdea39, 0x817032d7),
+     TOBN(0x0b1f482e, 0xdcabe3c0), TOBN(0x3baf76b4, 0xbb86325c),
+     TOBN(0xd49065e0, 0x10089465), TOBN(0x3bab5d29, 0x8e77c596),
+     TOBN(0x7636c3a6, 0x193dbd95), TOBN(0xdef5d294, 0xb246e499),
+     TOBN(0xb22c58b9, 0x286b2475), TOBN(0xa0b93939, 0xcd80862b),
+     TOBN(0x3002c83a, 0xf0992388), TOBN(0x6de01f9b, 0xeacbe14c),
+     TOBN(0x6aac688e, 0xadd70482), TOBN(0x708de92a, 0x7b4a4e8a),
+     TOBN(0x75b6dd73, 0x758a6eef), TOBN(0xea4bf352, 0x725b3c43),
+     TOBN(0x10041f2c, 0x87912868), TOBN(0xb1b1be95, 0xef09297a),
+     TOBN(0x19ae23c5, 0xa9f3860a), TOBN(0xc4f0f839, 0x515dcf4b),
+     TOBN(0x3c7ecca3, 0x97f6306a), TOBN(0x744c44ae, 0x68a3a4b0),
+     TOBN(0x69cd13a0, 0xb3a1d8a2), TOBN(0x7cad0a1e, 0x5256b578),
+     TOBN(0xea653fcd, 0x33791d9e), TOBN(0x9cc2a05d, 0x74b2e05f),
+     TOBN(0x73b391dc, 0xfd7affa2), TOBN(0xddb7091e, 0xb6b05442),
+     TOBN(0xc71e27bf, 0x8538a5c6), TOBN(0x195c63dd, 0x89abff17),
+     TOBN(0xfd315285, 0x1b71e3da), TOBN(0x9cbdfda7, 0xfa680fa0),
+     TOBN(0x9db876ca, 0x849d7eab), TOBN(0xebe2764b, 0x3c273271),
+     TOBN(0x663357e3, 0xf208dcea), TOBN(0x8c5bd833, 0x565b1b70),
+     TOBN(0xccc3b4f5, 0x9837fc0d), TOBN(0x9b641ba8, 0xa79cf00f),
+     TOBN(0x7428243d, 0xdfdf3990), TOBN(0x83a594c4, 0x020786b1),
+     TOBN(0xb712451a, 0x526c4502), TOBN(0x9d39438e, 0x6adb3f93),
+     TOBN(0xfdb261e3, 0xe9ff0ccd), TOBN(0x80344e3c, 0xe07af4c3),
+     TOBN(0x75900d7c, 0x2fa4f126), TOBN(0x08a3b865, 0x5c99a232),
+     TOBN(0x2478b6bf, 0xdb25e0c3), TOBN(0x482cc2c2, 0x71db2edf),
+     TOBN(0x37df7e64, 0x5f321bb8), TOBN(0x8a93821b, 0x9a8005b4),
+     TOBN(0x3fa2f10c, 0xcc8c1958), TOBN(0x0d332218, 0x2c269d0a),
+     TOBN(0x20ab8119, 0xe246b0e6), TOBN(0xb39781e4, 0xd349fd17),
+     TOBN(0xd293231e, 0xb31aa100), TOBN(0x4b779c97, 0xbb032168),
+     TOBN(0x4b3f19e1, 0xc8470500), TOBN(0x45b7efe9, 0x0c4c869d),
+     TOBN(0xdb84f38a, 0xa1a6bbcc), TOBN(0x3b59cb15, 0xb2fddbc1),
+     TOBN(0xba5514df, 0x3fd165e8), TOBN(0x499fd6a9, 0x061f8811),
+     TOBN(0x72cd1fe0, 0xbfef9f00), TOBN(0x120a4bb9, 0x79ad7e8a),
+     TOBN(0xf2ffd095, 0x5f4a5ac5), TOBN(0xcfd174f1, 0x95a7a2f0),
+     TOBN(0xd42301ba, 0x9d17baf1), TOBN(0xd2fa487a, 0x77f22089),
+     TOBN(0x9cb09efe, 0xb1dc77e1), TOBN(0xe9566939, 0x21c99682),
+     TOBN(0x8c546901, 0x6c6067bb), TOBN(0xfd378574, 0x61c24456),
+     TOBN(0x2b6a6cbe, 0x81796b33), TOBN(0x62d550f6, 0x58e87f8b),
+     TOBN(0x1b763e1c, 0x7f1b01b4), TOBN(0x4b93cfea, 0x1b1b5e12),
+     TOBN(0xb9345238, 0x1d531696), TOBN(0x57201c00, 0x88cdde69),
+     TOBN(0xdde92251, 0x9a86afc7), TOBN(0xe3043895, 0xbd35cea8),
+     TOBN(0x7608c1e1, 0x8555970d), TOBN(0x8267dfa9, 0x2535935e),
+     TOBN(0xd4c60a57, 0x322ea38b), TOBN(0xe0bf7977, 0x804ef8b5),
+     TOBN(0x1a0dab28, 0xc06fece4), TOBN(0xd405991e, 0x94e7b49d),
+     TOBN(0xc542b6d2, 0x706dab28), TOBN(0xcb228da3, 0xa91618fb),
+     TOBN(0x224e4164, 0x107d1cea), TOBN(0xeb9fdab3, 0xd0f5d8f1),
+     TOBN(0xc02ba386, 0x0d6e41cd), TOBN(0x676a72c5, 0x9b1f7146),
+     TOBN(0xffd6dd98, 0x4d6cb00b), TOBN(0xcef9c5ca, 0xde2e8d7c),
+     TOBN(0xa1bbf5d7, 0x641c7936), TOBN(0x1b95b230, 0xee8f772e),
+     TOBN(0xf765a92e, 0xe8ac25b1), TOBN(0xceb04cfc, 0x3a18b7c6),
+     TOBN(0x27944cef, 0x0acc8966), TOBN(0xcbb3c957, 0x434c1004),
+     TOBN(0x9c9971a1, 0xa43ff93c), TOBN(0x5bc2db17, 0xa1e358a9),
+     TOBN(0x45b4862e, 0xa8d9bc82), TOBN(0x70ebfbfb, 0x2201e052),
+     TOBN(0xafdf64c7, 0x92871591), TOBN(0xea5bcae6, 0xb42d0219),
+     TOBN(0xde536c55, 0x2ad8f03c), TOBN(0xcd6c3f4d, 0xa76aa33c),
+     TOBN(0xbeb5f623, 0x0bca6de3), TOBN(0xdd20dd99, 0xb1e706fd),
+     TOBN(0x90b3ff9d, 0xac9059d4), TOBN(0x2d7b2902, 0x7ccccc4e),
+     TOBN(0x8a090a59, 0xce98840f), TOBN(0xa5d947e0, 0x8410680a),
+     TOBN(0x49ae346a, 0x923379a5), TOBN(0x7dbc84f9, 0xb28a3156),
+     TOBN(0xfd40d916, 0x54a1aff2), TOBN(0xabf318ba, 0x3a78fb9b),
+     TOBN(0x50152ed8, 0x3029f95e), TOBN(0x9fc1dd77, 0xc58ad7fa),
+     TOBN(0x5fa57915, 0x13595c17), TOBN(0xb9504668, 0x8f62b3a9),
+     TOBN(0x907b5b24, 0xff3055b0), TOBN(0x2e995e35, 0x9a84f125),
+     TOBN(0x87dacf69, 0x7e9bbcfb), TOBN(0x95d0c1d6, 0xe86d96e3),
+     TOBN(0x65726e3c, 0x2d95a75c), TOBN(0x2c3c9001, 0xacd27f21),
+     TOBN(0x1deab561, 0x6c973f57), TOBN(0x108b7e2c, 0xa5221643),
+     TOBN(0x5fee9859, 0xc4ef79d4), TOBN(0xbd62b88a, 0x40d4b8c6),
+     TOBN(0xb4dd29c4, 0x197c75d6), TOBN(0x266a6df2, 0xb7076feb),
+     TOBN(0x9512d0ea, 0x4bf2df11), TOBN(0x1320c24f, 0x6b0cc9ec),
+     TOBN(0x6bb1e0e1, 0x01a59596), TOBN(0x8317c5bb, 0xeff9aaac),
+     TOBN(0x65bb405e, 0x385aa6c9), TOBN(0x613439c1, 0x8f07988f),
+     TOBN(0xd730049f, 0x16a66e91), TOBN(0xe97f2820, 0xfa1b0e0d),
+     TOBN(0x4131e003, 0x304c28ea), TOBN(0x820ab732, 0x526bac62),
+     TOBN(0xb2ac9ef9, 0x28714423), TOBN(0x54ecfffa, 0xadb10cb2),
+     TOBN(0x8781476e, 0xf886a4cc), TOBN(0x4b2c87b5, 0xdb2f8d49),
+     TOBN(0xe857cd20, 0x0a44295d), TOBN(0x707d7d21, 0x58c6b044),
+     TOBN(0xae8521f9, 0xf596757c), TOBN(0x87448f03, 0x67b2b714),
+     TOBN(0x13a9bc45, 0x5ebcd58d), TOBN(0x79bcced9, 0x9122d3c1),
+     TOBN(0x3c644247, 0x9e076642), TOBN(0x0cf22778, 0x2df4767d),
+     TOBN(0x5e61aee4, 0x71d444b6), TOBN(0x211236bf, 0xc5084a1d),
+     TOBN(0x7e15bc9a, 0x4fd3eaf6), TOBN(0x68df2c34, 0xab622bf5),
+     TOBN(0x9e674f0f, 0x59bf4f36), TOBN(0xf883669b, 0xd7f34d73),
+     TOBN(0xc48ac1b8, 0x31497b1d), TOBN(0x323b925d, 0x5106703b),
+     TOBN(0x22156f42, 0x74082008), TOBN(0xeffc521a, 0xc8482bcb),
+     TOBN(0x5c6831bf, 0x12173479), TOBN(0xcaa2528f, 0xc4739490),
+     TOBN(0x84d2102a, 0x8f1b3c4d), TOBN(0xcf64dfc1, 0x2d9bec0d),
+     TOBN(0x433febad, 0x78a546ef), TOBN(0x1f621ec3, 0x7b73cef1),
+     TOBN(0x6aecd627, 0x37338615), TOBN(0x162082ab, 0x01d8edf6),
+     TOBN(0x833a8119, 0x19e86b66), TOBN(0x6023a251, 0xd299b5db),
+     TOBN(0xf5bb0c3a, 0xbbf04b89), TOBN(0x6735eb69, 0xae749a44),
+     TOBN(0xd0e058c5, 0x4713de3b), TOBN(0xfdf2593e, 0x2c3d4ccd),
+     TOBN(0x1b8f414e, 0xfdd23667), TOBN(0xdd52aaca, 0xfa2015ee),
+     TOBN(0x3e31b517, 0xbd9625ff), TOBN(0x5ec9322d, 0x8db5918c),
+     TOBN(0xbc73ac85, 0xa96f5294), TOBN(0x82aa5bf3, 0x61a0666a),
+     TOBN(0x49755810, 0xbf08ac42), TOBN(0xd21cdfd5, 0x891cedfc),
+     TOBN(0x918cb57b, 0x67f8be10), TOBN(0x365d1a7c, 0x56ffa726),
+     TOBN(0x2435c504, 0x6532de93), TOBN(0xc0fc5e10, 0x2674cd02),
+     TOBN(0x6e51fcf8, 0x9cbbb142), TOBN(0x1d436e5a, 0xafc50692),
+     TOBN(0x766bffff, 0x3fbcae22), TOBN(0x3148c2fd, 0xfd55d3b8),
+     TOBN(0x52c7fdc9, 0x233222fa), TOBN(0x89ff1092, 0xe419fb6b),
+     TOBN(0x3cd6db99, 0x25254977), TOBN(0x2e85a161, 0x1cf12ca7),
+     TOBN(0xadd2547c, 0xdc810bc9), TOBN(0xea3f458f, 0x9d257c22),
+     TOBN(0x642c1fbe, 0x27d6b19b), TOBN(0xed07e6b5, 0x140481a6),
+     TOBN(0x6ada1d42, 0x86d2e0f8), TOBN(0xe5920122, 0x0e8a9fd5),
+     TOBN(0x02c936af, 0x708c1b49), TOBN(0x60f30fee, 0x2b4bfaff),
+     TOBN(0x6637ad06, 0x858e6a61), TOBN(0xce4c7767, 0x3fd374d0),
+     TOBN(0x39d54b2d, 0x7188defb), TOBN(0xa8c9d250, 0xf56a6b66),
+     TOBN(0x58fc0f5e, 0xb24fe1dc), TOBN(0x9eaf9dee, 0x6b73f24c),
+     TOBN(0xa90d588b, 0x33650705), TOBN(0xde5b62c5, 0xaf2ec729),
+     TOBN(0x5c72cfae, 0xd3c2b36e), TOBN(0x868c19d5, 0x034435da),
+     TOBN(0x88605f93, 0xe17ee145), TOBN(0xaa60c4ee, 0x77a5d5b1),
+     TOBN(0xbcf5bfd2, 0x3b60c472), TOBN(0xaf4ef13c, 0xeb1d3049),
+     TOBN(0x373f44fc, 0xe13895c9), TOBN(0xf29b382f, 0x0cbc9822),
+     TOBN(0x1bfcb853, 0x73efaef6), TOBN(0xcf56ac9c, 0xa8c96f40),
+     TOBN(0xd7adf109, 0x7a191e24), TOBN(0x98035f44, 0xbf8a8dc2),
+     TOBN(0xf40a71b9, 0x1e750c84), TOBN(0xc57f7b0c, 0x5dc6c469),
+     TOBN(0x49a0e79c, 0x6fbc19c1), TOBN(0x6b0f5889, 0xa48ebdb8),
+     TOBN(0x5d3fd084, 0xa07c4e9f), TOBN(0xc3830111, 0xab27de14),
+     TOBN(0x0e4929fe, 0x33e08dcc), TOBN(0xf4a5ad24, 0x40bb73a3),
+     TOBN(0xde86c2bf, 0x490f97ca), TOBN(0x288f09c6, 0x67a1ce18),
+     TOBN(0x364bb886, 0x1844478d), TOBN(0x7840fa42, 0xceedb040),
+     TOBN(0x1269fdd2, 0x5a631b37), TOBN(0x94761f1e, 0xa47c8b7d),
+     TOBN(0xfc0c2e17, 0x481c6266), TOBN(0x85e16ea2, 0x3daa5fa7),
+     TOBN(0xccd86033, 0x92491048), TOBN(0x0c2f6963, 0xf4d402d7),
+     TOBN(0x6336f7df, 0xdf6a865c), TOBN(0x0a2a463c, 0xb5c02a87),
+     TOBN(0xb0e29be7, 0xbf2f12ee), TOBN(0xf0a22002, 0x66bad988),
+     TOBN(0x27f87e03, 0x9123c1d7), TOBN(0x21669c55, 0x328a8c98),
+     TOBN(0x186b9803, 0x92f14529), TOBN(0xd3d056cc, 0x63954df3),
+     TOBN(0x2f03fd58, 0x175a46f6), TOBN(0x63e34ebe, 0x11558558),
+     TOBN(0xe13fedee, 0x5b80cfa5), TOBN(0xe872a120, 0xd401dbd1),
+     TOBN(0x52657616, 0xe8a9d667), TOBN(0xbc8da4b6, 0xe08d6693),
+     TOBN(0x370fb9bb, 0x1b703e75), TOBN(0x6773b186, 0xd4338363),
+     TOBN(0x18dad378, 0xecef7bff), TOBN(0xaac787ed, 0x995677da),
+     TOBN(0x4801ea8b, 0x0437164b), TOBN(0xf430ad20, 0x73fe795e),
+     TOBN(0xb164154d, 0x8ee5eb73), TOBN(0x0884ecd8, 0x108f7c0e),
+     TOBN(0x0e6ec096, 0x5f520698), TOBN(0x640631fe, 0x44f7b8d9),
+     TOBN(0x92fd34fc, 0xa35a68b9), TOBN(0x9c5a4b66, 0x4d40cf4e),
+     TOBN(0x949454bf, 0x80b6783d), TOBN(0x80e701fe, 0x3a320a10),
+     TOBN(0x8d1a564a, 0x1a0a39b2), TOBN(0x1436d53d, 0x320587db),
+     TOBN(0xf5096e6d, 0x6556c362), TOBN(0xbc23a3c0, 0xe2455d7e),
+     TOBN(0x3a7aee54, 0x807230f9), TOBN(0x9ba1cfa6, 0x22ae82fd),
+     TOBN(0x833a057a, 0x99c5d706), TOBN(0x8be85f4b, 0x842315c9),
+     TOBN(0xd083179a, 0x66a72f12), TOBN(0x2fc77d5d, 0xcdcc73cd),
+     TOBN(0x22b88a80, 0x5616ee30), TOBN(0xfb09548f, 0xe7ab1083),
+     TOBN(0x8ad6ab0d, 0x511270cd), TOBN(0x61f6c57a, 0x6924d9ab),
+     TOBN(0xa0f7bf72, 0x90aecb08), TOBN(0x849f87c9, 0x0df784a4),
+     TOBN(0x27c79c15, 0xcfaf1d03), TOBN(0xbbf9f675, 0xc463face),
+     TOBN(0x91502c65, 0x765ba543), TOBN(0x18ce3cac, 0x42ea60dd),
+     TOBN(0xe5cee6ac, 0x6e43ecb3), TOBN(0x63e4e910, 0x68f2aeeb),
+     TOBN(0x26234fa3, 0xc85932ee), TOBN(0x96883e8b, 0x4c90c44d),
+     TOBN(0x29b9e738, 0xa18a50f6), TOBN(0xbfc62b2a, 0x3f0420df),
+     TOBN(0xd22a7d90, 0x6d3e1fa9), TOBN(0x17115618, 0xfe05b8a3),
+     TOBN(0x2a0c9926, 0xbb2b9c01), TOBN(0xc739fcc6, 0xe07e76a2),
+     TOBN(0x540e9157, 0x165e439a), TOBN(0x06353a62, 0x6a9063d8),
+     TOBN(0x84d95594, 0x61e927a3), TOBN(0x013b9b26, 0xe2e0be7f),
+     TOBN(0x4feaec3b, 0x973497f1), TOBN(0x15c0f94e, 0x093ebc2d),
+     TOBN(0x6af5f227, 0x33af0583), TOBN(0x0c2af206, 0xc61f3340),
+     TOBN(0xd25dbdf1, 0x4457397c), TOBN(0x2e8ed017, 0xcabcbae0),
+     TOBN(0xe3010938, 0xc2815306), TOBN(0xbaa99337, 0xe8c6cd68),
+     TOBN(0x08513182, 0x3b0ec7de), TOBN(0x1e1b822b, 0x58df05df),
+     TOBN(0x5c14842f, 0xa5c3b683), TOBN(0x98fe977e, 0x3eba34ce),
+     TOBN(0xfd2316c2, 0x0d5e8873), TOBN(0xe48d839a, 0xbd0d427d),
+     TOBN(0x495b2218, 0x623fc961), TOBN(0x24ee56e7, 0xb46fba5e),
+     TOBN(0x9184a55b, 0x91e4de58), TOBN(0xa7488ca5, 0xdfdea288),
+     TOBN(0xa723862e, 0xa8dcc943), TOBN(0x92d762b2, 0x849dc0fc),
+     TOBN(0x3c444a12, 0x091ff4a9), TOBN(0x581113fa, 0x0cada274),
+     TOBN(0xb9de0a45, 0x30d8eae2), TOBN(0x5e0fcd85, 0xdf6b41ea),
+     TOBN(0x6233ea68, 0xc094dbb5), TOBN(0xb77d062e, 0xd968d410),
+     TOBN(0x3e719bbc, 0x58b3002d), TOBN(0x68e7dd3d, 0x3dc49d58),
+     TOBN(0x8d825740, 0x013a5e58), TOBN(0x21311747, 0x3c9e3c1b),
+     TOBN(0x0cb0a2a7, 0x7c99b6ab), TOBN(0x5c48a3b3, 0xc2f888f2)}
+    ,
+    {TOBN(0xc7913e91, 0x991724f3), TOBN(0x5eda799c, 0x39cbd686),
+     TOBN(0xddb595c7, 0x63d4fc1e), TOBN(0x6b63b80b, 0xac4fed54),
+     TOBN(0x6ea0fc69, 0x7e5fb516), TOBN(0x737708ba, 0xd0f1c964),
+     TOBN(0x9628745f, 0x11a92ca5), TOBN(0x61f37958, 0x9a86967a),
+     TOBN(0x9af39b2c, 0xaa665072), TOBN(0x78322fa4, 0xefd324ef),
+     TOBN(0x3d153394, 0xc327bd31), TOBN(0x81d5f271, 0x3129dab0),
+     TOBN(0xc72e0c42, 0xf48027f5), TOBN(0xaa40cdbc, 0x8536e717),
+     TOBN(0xf45a657a, 0x2d369d0f), TOBN(0xb03bbfc4, 0xea7f74e6),
+     TOBN(0x46a8c418, 0x0d738ded), TOBN(0x6f1a5bb0, 0xe0de5729),
+     TOBN(0xf10230b9, 0x8ba81675), TOBN(0x32c6f30c, 0x112b33d4),
+     TOBN(0x7559129d, 0xd8fffb62), TOBN(0x6a281b47, 0xb459bf05),
+     TOBN(0x77c1bd3a, 0xfa3b6776), TOBN(0x0709b380, 0x7829973a),
+     TOBN(0x8c26b232, 0xa3326505), TOBN(0x38d69272, 0xee1d41bf),
+     TOBN(0x0459453e, 0xffe32afa), TOBN(0xce8143ad, 0x7cb3ea87),
+     TOBN(0x932ec1fa, 0x7e6ab666), TOBN(0x6cd2d230, 0x22286264),
+     TOBN(0x459a46fe, 0x6736f8ed), TOBN(0x50bf0d00, 0x9eca85bb),
+     TOBN(0x0b825852, 0x877a21ec), TOBN(0x300414a7, 0x0f537a94),
+     TOBN(0x3f1cba40, 0x21a9a6a2), TOBN(0x50824eee, 0x76943c00),
+     TOBN(0xa0dbfcec, 0xf83cba5d), TOBN(0xf9538148, 0x93b4f3c0),
+     TOBN(0x61744162, 0x48f24dd7), TOBN(0x5322d64d, 0xe4fb09dd),
+     TOBN(0x57447384, 0x3d9325f3), TOBN(0xa9bef2d0, 0xf371cb84),
+     TOBN(0x77d2188b, 0xa61e36c5), TOBN(0xbbd6a7d7, 0xc602df72),
+     TOBN(0xba3aa902, 0x8f61bc0b), TOBN(0xf49085ed, 0x6ed0b6a1),
+     TOBN(0x8bc625d6, 0xae6e8298), TOBN(0x832b0b1d, 0xa2e9c01d),
+     TOBN(0xa337c447, 0xf1f0ced1), TOBN(0x800cc793, 0x9492dd2b),
+     TOBN(0x4b93151d, 0xbea08efa), TOBN(0x820cf3f8, 0xde0a741e),
+     TOBN(0xff1982dc, 0x1c0f7d13), TOBN(0xef921960, 0x84dde6ca),
+     TOBN(0x1ad7d972, 0x45f96ee3), TOBN(0x319c8dbe, 0x29dea0c7),
+     TOBN(0xd3ea3871, 0x7b82b99b), TOBN(0x75922d4d, 0x470eb624),
+     TOBN(0x8f66ec54, 0x3b95d466), TOBN(0x66e673cc, 0xbee1e346),
+     TOBN(0x6afe67c4, 0xb5f2b89a), TOBN(0x3de9c1e6, 0x290e5cd3),
+     TOBN(0x8c278bb6, 0x310a2ada), TOBN(0x420fa384, 0x0bdb323b),
+     TOBN(0x0ae1d63b, 0x0eb919b0), TOBN(0xd74ee51d, 0xa74b9620),
+     TOBN(0x395458d0, 0xa674290c), TOBN(0x324c930f, 0x4620a510),
+     TOBN(0x2d1f4d19, 0xfbac27d4), TOBN(0x4086e8ca, 0x9bedeeac),
+     TOBN(0x0cdd211b, 0x9b679ab8), TOBN(0x5970167d, 0x7090fec4),
+     TOBN(0x3420f2c9, 0xfaf1fc63), TOBN(0x616d333a, 0x328c8bb4),
+     TOBN(0x7d65364c, 0x57f1fe4a), TOBN(0x9343e877, 0x55e5c73a),
+     TOBN(0x5795176b, 0xe970e78c), TOBN(0xa36ccebf, 0x60533627),
+     TOBN(0xfc7c7380, 0x09cdfc1b), TOBN(0xb39a2afe, 0xb3fec326),
+     TOBN(0xb7ff1ba1, 0x6224408a), TOBN(0xcc856e92, 0x247cfc5e),
+     TOBN(0x01f102e7, 0xc18bc493), TOBN(0x4613ab74, 0x2091c727),
+     TOBN(0xaa25e89c, 0xc420bf2b), TOBN(0x00a53176, 0x90337ec2),
+     TOBN(0xd2be9f43, 0x7d025fc7), TOBN(0x3316fb85, 0x6e6fe3dc),
+     TOBN(0x27520af5, 0x9ac50814), TOBN(0xfdf95e78, 0x9a8e4223),
+     TOBN(0xb7e7df2a, 0x56bec5a0), TOBN(0xf7022f7d, 0xdf159e5d),
+     TOBN(0x93eeeab1, 0xcac1fe8f), TOBN(0x8040188c, 0x37451168),
+     TOBN(0x7ee8aa8a, 0xd967dce6), TOBN(0xfa0e79e7, 0x3abc9299),
+     TOBN(0x67332cfc, 0x2064cfd1), TOBN(0x339c31de, 0xb0651934),
+     TOBN(0x719b28d5, 0x2a3bcbea), TOBN(0xee74c82b, 0x9d6ae5c6),
+     TOBN(0x0927d05e, 0xbaf28ee6), TOBN(0x82cecf2c, 0x9d719028),
+     TOBN(0x0b0d353e, 0xddb30289), TOBN(0xfe4bb977, 0xfddb2e29),
+     TOBN(0xbb5bb990, 0x640bfd9e), TOBN(0xd226e277, 0x82f62108),
+     TOBN(0x4bf00985, 0x02ffdd56), TOBN(0x7756758a, 0x2ca1b1b5),
+     TOBN(0xc32b62a3, 0x5285fe91), TOBN(0xedbc546a, 0x8c9cd140),
+     TOBN(0x1e47a013, 0xaf5cb008), TOBN(0xbca7e720, 0x073ce8f2),
+     TOBN(0xe10b2ab8, 0x17a91cae), TOBN(0xb89aab65, 0x08e27f63),
+     TOBN(0x7b3074a7, 0xdba3ddf9), TOBN(0x1c20ce09, 0x330c2972),
+     TOBN(0x6b9917b4, 0x5fcf7e33), TOBN(0xe6793743, 0x945ceb42),
+     TOBN(0x18fc2215, 0x5c633d19), TOBN(0xad1adb3c, 0xc7485474),
+     TOBN(0x646f9679, 0x6424c49b), TOBN(0xf888dfe8, 0x67c241c9),
+     TOBN(0xe12d4b93, 0x24f68b49), TOBN(0x9a6b62d8, 0xa571df20),
+     TOBN(0x81b4b26d, 0x179483cb), TOBN(0x666f9632, 0x9511fae2),
+     TOBN(0xd281b3e4, 0xd53aa51f), TOBN(0x7f96a765, 0x7f3dbd16),
+     TOBN(0xa7f8b5bf, 0x074a30ce), TOBN(0xd7f52107, 0x005a32e6),
+     TOBN(0x6f9e0907, 0x50237ed4), TOBN(0x2f21da47, 0x8096fa2b),
+     TOBN(0xf3e19cb4, 0xeec863a0), TOBN(0xd18f77fd, 0x9527620a),
+     TOBN(0x9505c81c, 0x407c1cf8), TOBN(0x9998db4e, 0x1b6ec284),
+     TOBN(0x7e3389e5, 0xc247d44d), TOBN(0x12507141, 0x3f4f3d80),
+     TOBN(0xd4ba0110, 0x4a78a6c7), TOBN(0x312874a0, 0x767720be),
+     TOBN(0xded059a6, 0x75944370), TOBN(0xd6123d90, 0x3b2c0bdd),
+     TOBN(0xa56b717b, 0x51c108e3), TOBN(0x9bb7940e, 0x070623e9),
+     TOBN(0x794e2d59, 0x84ac066c), TOBN(0xf5954a92, 0xe68c69a0),
+     TOBN(0x28c52458, 0x4fd99dcc), TOBN(0x60e639fc, 0xb1012517),
+     TOBN(0xc2e60125, 0x7de79248), TOBN(0xe9ef6404, 0xf12fc6d7),
+     TOBN(0x4c4f2808, 0x2a3b5d32), TOBN(0x865ad32e, 0xc768eb8a),
+     TOBN(0xac02331b, 0x13fb70b6), TOBN(0x037b44c1, 0x95599b27),
+     TOBN(0x1a860fc4, 0x60bd082c), TOBN(0xa2e25745, 0xc980cd01),
+     TOBN(0xee3387a8, 0x1da0263e), TOBN(0x931bfb95, 0x2d10f3d6),
+     TOBN(0x5b687270, 0xa1f24a32), TOBN(0xf140e65d, 0xca494b86),
+     TOBN(0x4f4ddf91, 0xb2f1ac7a), TOBN(0xf99eaabb, 0x760fee27),
+     TOBN(0x57f4008a, 0x49c228e5), TOBN(0x090be440, 0x1cf713bb),
+     TOBN(0xac91fbe4, 0x5004f022), TOBN(0xd838c2c2, 0x569e1af6),
+     TOBN(0xd6c7d20b, 0x0f1daaa5), TOBN(0xaa063ac1, 0x1bbb02c0),
+     TOBN(0x0938a422, 0x59558a78), TOBN(0x5343c669, 0x8435da2f),
+     TOBN(0x96f67b18, 0x034410dc), TOBN(0x7cc1e424, 0x84510804),
+     TOBN(0x86a1543f, 0x16dfbb7d), TOBN(0x921fa942, 0x5b5bd592),
+     TOBN(0x9dcccb6e, 0xb33dd03c), TOBN(0x8581ddd9, 0xb843f51e),
+     TOBN(0x54935fcb, 0x81d73c9e), TOBN(0x6d07e979, 0x0a5e97ab),
+     TOBN(0x4dc7b30a, 0xcf3a6bab), TOBN(0x147ab1f3, 0x170bee11),
+     TOBN(0x0aaf8e3d, 0x9fafdee4), TOBN(0xfab3dbcb, 0x538a8b95),
+     TOBN(0x405df4b3, 0x6ef13871), TOBN(0xf1f4e9cb, 0x088d5a49),
+     TOBN(0x9bcd24d3, 0x66b33f1d), TOBN(0x3b97b820, 0x5ce445c0),
+     TOBN(0xe2926549, 0xba93ff61), TOBN(0xd9c341ce, 0x4dafe616),
+     TOBN(0xfb30a76e, 0x16efb6f3), TOBN(0xdf24b8ca, 0x605b953c),
+     TOBN(0x8bd52afe, 0xc2fffb9f), TOBN(0xbbac5ff7, 0xe19d0b96),
+     TOBN(0x43c01b87, 0x459afccd), TOBN(0x6bd45143, 0xb7432652),
+     TOBN(0x84734530, 0x55b5d78e), TOBN(0x81088fdb, 0x1554ba7d),
+     TOBN(0xada0a52c, 0x1e269375), TOBN(0xf9f037c4, 0x2dc5ec10),
+     TOBN(0xc0660607, 0x94bfbc11), TOBN(0xc0a630bb, 0xc9c40d2f),
+     TOBN(0x5efc797e, 0xab64c31e), TOBN(0xffdb1dab, 0x74507144),
+     TOBN(0xf6124287, 0x1ca6790c), TOBN(0xe9609d81, 0xe69bf1bf),
+     TOBN(0xdb898595, 0x00d24fc9), TOBN(0x9c750333, 0xe51fb417),
+     TOBN(0x51830a91, 0xfef7bbde), TOBN(0x0ce67dc8, 0x945f585c),
+     TOBN(0x9a730ed4, 0x4763eb50), TOBN(0x24a0e221, 0xc1ab0d66),
+     TOBN(0x643b6393, 0x648748f3), TOBN(0x1982daa1, 0x6d3c6291),
+     TOBN(0x6f00a9f7, 0x8bbc5549), TOBN(0x7a1783e1, 0x7f36384e),
+     TOBN(0xe8346323, 0xde977f50), TOBN(0x91ab688d, 0xb245502a),
+     TOBN(0x331ab6b5, 0x6d0bdd66), TOBN(0x0a6ef32e, 0x64b71229),
+     TOBN(0x1028150e, 0xfe7c352f), TOBN(0x27e04350, 0xce7b39d3),
+     TOBN(0x2a3c8acd, 0xc1070c82), TOBN(0xfb2034d3, 0x80c9feef),
+     TOBN(0x2d729621, 0x709f3729), TOBN(0x8df290bf, 0x62cb4549),
+     TOBN(0x02f99f33, 0xfc2e4326), TOBN(0x3b30076d, 0x5eddf032),
+     TOBN(0xbb21f8cf, 0x0c652fb5), TOBN(0x314fb49e, 0xed91cf7b),
+     TOBN(0xa013eca5, 0x2f700750), TOBN(0x2b9e3c23, 0x712a4575),
+     TOBN(0xe5355557, 0xaf30fbb0), TOBN(0x1ada3516, 0x7c77e771),
+     TOBN(0x45f6ecb2, 0x7b135670), TOBN(0xe85d19df, 0x7cfc202e),
+     TOBN(0x0f1b50c7, 0x58d1be9f), TOBN(0x5ebf2c0a, 0xead2e344),
+     TOBN(0x1531fe4e, 0xabc199c9), TOBN(0xc7032592, 0x56bab0ae),
+     TOBN(0x16ab2e48, 0x6c1fec54), TOBN(0x0f87fda8, 0x04280188),
+     TOBN(0xdc9f46fc, 0x609e4a74), TOBN(0x2a44a143, 0xba667f91),
+     TOBN(0xbc3d8b95, 0xb4d83436), TOBN(0xa01e4bd0, 0xc7bd2958),
+     TOBN(0x7b182932, 0x73483c90), TOBN(0xa79c6aa1, 0xa7c7b598),
+     TOBN(0xbf3983c6, 0xeaaac07e), TOBN(0x8f18181e, 0x96e0d4e6),
+     TOBN(0x8553d37c, 0x051af62b), TOBN(0xe9a998eb, 0x0bf94496),
+     TOBN(0xe0844f9f, 0xb0d59aa1), TOBN(0x983fd558, 0xe6afb813),
+     TOBN(0x9670c0ca, 0x65d69804), TOBN(0x732b22de, 0x6ea5ff2d),
+     TOBN(0xd7640ba9, 0x5fd8623b), TOBN(0x9f619163, 0xa6351782),
+     TOBN(0x0bfc27ee, 0xacee5043), TOBN(0xae419e73, 0x2eb10f02),
+     TOBN(0x19c028d1, 0x8943fb05), TOBN(0x71f01cf7, 0xff13aa2a),
+     TOBN(0x7790737e, 0x8887a132), TOBN(0x67513309, 0x66318410),
+     TOBN(0x9819e8a3, 0x7ddb795e), TOBN(0xfecb8ef5, 0xdad100b2),
+     TOBN(0x59f74a22, 0x3021926a), TOBN(0xb7c28a49, 0x6f9b4c1c),
+     TOBN(0xed1a733f, 0x912ad0ab), TOBN(0x42a910af, 0x01a5659c),
+     TOBN(0x3842c6e0, 0x7bd68cab), TOBN(0x2b57fa38, 0x76d70ac8),
+     TOBN(0x8a6707a8, 0x3c53aaeb), TOBN(0x62c1c510, 0x65b4db18),
+     TOBN(0x8de2c1fb, 0xb2d09dc7), TOBN(0xc3dfed12, 0x266bd23b),
+     TOBN(0x927d039b, 0xd5b27db6), TOBN(0x2fb2f0f1, 0x103243da),
+     TOBN(0xf855a07b, 0x80be7399), TOBN(0xed9327ce, 0x1f9f27a8),
+     TOBN(0xa0bd99c7, 0x729bdef7), TOBN(0x2b67125e, 0x28250d88),
+     TOBN(0x784b26e8, 0x8670ced7), TOBN(0xe3dfe41f, 0xc31bd3b4),
+     TOBN(0x9e353a06, 0xbcc85cbc), TOBN(0x302e2909, 0x60178a9d),
+     TOBN(0x860abf11, 0xa6eac16e), TOBN(0x76447000, 0xaa2b3aac),
+     TOBN(0x46ff9d19, 0x850afdab), TOBN(0x35bdd6a5, 0xfdb2d4c1),
+     TOBN(0xe82594b0, 0x7e5c9ce9), TOBN(0x0f379e53, 0x20af346e),
+     TOBN(0x608b31e3, 0xbc65ad4a), TOBN(0x710c6b12, 0x267c4826),
+     TOBN(0x51c966f9, 0x71954cf1), TOBN(0xb1cec793, 0x0d0aa215),
+     TOBN(0x1f155989, 0x86bd23a8), TOBN(0xae2ff99c, 0xf9452e86),
+     TOBN(0xd8dd953c, 0x340ceaa2), TOBN(0x26355275, 0x2e2e9333),
+     TOBN(0x15d4e5f9, 0x8586f06d), TOBN(0xd6bf94a8, 0xf7cab546),
+     TOBN(0x33c59a0a, 0xb76a9af0), TOBN(0x52740ab3, 0xba095af7),
+     TOBN(0xc444de8a, 0x24389ca0), TOBN(0xcc6f9863, 0x706da0cb),
+     TOBN(0xb5a741a7, 0x6b2515cf), TOBN(0x71c41601, 0x9585c749),
+     TOBN(0x78350d4f, 0xe683de97), TOBN(0x31d61524, 0x63d0b5f5),
+     TOBN(0x7a0cc5e1, 0xfbce090b), TOBN(0xaac927ed, 0xfbcb2a5b),
+     TOBN(0xe920de49, 0x20d84c35), TOBN(0x8c06a0b6, 0x22b4de26),
+     TOBN(0xd34dd58b, 0xafe7ddf3), TOBN(0x55851fed, 0xc1e6e55b),
+     TOBN(0xd1395616, 0x960696e7), TOBN(0x940304b2, 0x5f22705f),
+     TOBN(0x6f43f861, 0xb0a2a860), TOBN(0xcf121282, 0x0e7cc981),
+     TOBN(0x12186212, 0x0ab64a96), TOBN(0x09215b9a, 0xb789383c),
+     TOBN(0x311eb305, 0x37387c09), TOBN(0xc5832fce, 0xf03ee760),
+     TOBN(0x30358f58, 0x32f7ea19), TOBN(0xe01d3c34, 0x91d53551),
+     TOBN(0x1ca5ee41, 0xda48ea80), TOBN(0x34e71e8e, 0xcf4fa4c1),
+     TOBN(0x312abd25, 0x7af1e1c7), TOBN(0xe3afcdeb, 0x2153f4a5),
+     TOBN(0x9d5c84d7, 0x00235e9a), TOBN(0x0308d3f4, 0x8c4c836f),
+     TOBN(0xc0a66b04, 0x89332de5), TOBN(0x610dd399, 0x89e566ef),
+     TOBN(0xf8eea460, 0xd1ac1635), TOBN(0x84cbb3fb, 0x20a2c0df),
+     TOBN(0x40afb488, 0xe74a48c5), TOBN(0x29738198, 0xd326b150),
+     TOBN(0x2a17747f, 0xa6d74081), TOBN(0x60ea4c05, 0x55a26214),
+     TOBN(0x53514bb4, 0x1f88c5fe), TOBN(0xedd64567, 0x7e83426c),
+     TOBN(0xd5d6cbec, 0x96460b25), TOBN(0xa12fd0ce, 0x68dc115e),
+     TOBN(0xc5bc3ed2, 0x697840ea), TOBN(0x969876a8, 0xa6331e31),
+     TOBN(0x60c36217, 0x472ff580), TOBN(0xf4229705, 0x4ad41393),
+     TOBN(0x4bd99ef0, 0xa03b8b92), TOBN(0x501c7317, 0xc144f4f6),
+     TOBN(0x159009b3, 0x18464945), TOBN(0x6d5e594c, 0x74c5c6be),
+     TOBN(0x2d587011, 0x321a3660), TOBN(0xd1e184b1, 0x3898d022),
+     TOBN(0x5ba04752, 0x4c6a7e04), TOBN(0x47fa1e2b, 0x45550b65),
+     TOBN(0x9419daf0, 0x48c0a9a5), TOBN(0x66362953, 0x7c243236),
+     TOBN(0xcd0744b1, 0x5cb12a88), TOBN(0x561b6f9a, 0x2b646188),
+     TOBN(0x599415a5, 0x66c2c0c0), TOBN(0xbe3f0859, 0x0f83f09a),
+     TOBN(0x9141c5be, 0xb92041b8), TOBN(0x01ae38c7, 0x26477d0d),
+     TOBN(0xca8b71f3, 0xd12c7a94), TOBN(0xfab5b31f, 0x765c70db),
+     TOBN(0x76ae7492, 0x487443e9), TOBN(0x8595a310, 0x990d1349),
+     TOBN(0xf8dbeda8, 0x7d460a37), TOBN(0x7f7ad082, 0x1e45a38f),
+     TOBN(0xed1d4db6, 0x1059705a), TOBN(0xa3dd492a, 0xe6b9c697),
+     TOBN(0x4b92ee3a, 0x6eb38bd5), TOBN(0xbab2609d, 0x67cc0bb7),
+     TOBN(0x7fc4fe89, 0x6e70ee82), TOBN(0xeff2c56e, 0x13e6b7e3),
+     TOBN(0x9b18959e, 0x34d26fca), TOBN(0x2517ab66, 0x889d6b45),
+     TOBN(0xf167b4e0, 0xbdefdd4f), TOBN(0x69958465, 0xf366e401),
+     TOBN(0x5aa368ab, 0xa73bbec0), TOBN(0x12148709, 0x7b240c21),
+     TOBN(0x378c3233, 0x18969006), TOBN(0xcb4d73ce, 0xe1fe53d1),
+     TOBN(0x5f50a80e, 0x130c4361), TOBN(0xd67f5951, 0x7ef5212b),
+     TOBN(0xf145e21e, 0x9e70c72e), TOBN(0xb2e52e29, 0x5566d2fb),
+     TOBN(0x44eaba4a, 0x032397f5), TOBN(0x5e56937b, 0x7e31a7de),
+     TOBN(0x68dcf517, 0x456c61e1), TOBN(0xbc2e954a, 0xa8b0a388),
+     TOBN(0xe3552fa7, 0x60a8b755), TOBN(0x03442dae, 0x73ad0cde),
+     TOBN(0x37ffe747, 0xceb26210), TOBN(0x983545e8, 0x787baef9),
+     TOBN(0x8b8c8535, 0x86a3de31), TOBN(0xc621dbcb, 0xfacd46db),
+     TOBN(0x82e442e9, 0x59266fbb), TOBN(0xa3514c37, 0x339d471c),
+     TOBN(0x3a11b771, 0x62cdad96), TOBN(0xf0cb3b3c, 0xecf9bdf0),
+     TOBN(0x3fcbdbce, 0x478e2135), TOBN(0x7547b5cf, 0xbda35342),
+     TOBN(0xa97e81f1, 0x8a677af6), TOBN(0xc8c2bf83, 0x28817987),
+     TOBN(0xdf07eaaf, 0x45580985), TOBN(0xc68d1f05, 0xc93b45cb),
+     TOBN(0x106aa2fe, 0xc77b4cac), TOBN(0x4c1d8afc, 0x04a7ae86),
+     TOBN(0xdb41c3fd, 0x9eb45ab2), TOBN(0x5b234b5b, 0xd4b22e74),
+     TOBN(0xda253dec, 0xf215958a), TOBN(0x67e0606e, 0xa04edfa0),
+     TOBN(0xabbbf070, 0xef751b11), TOBN(0xf352f175, 0xf6f06dce),
+     TOBN(0xdfc4b6af, 0x6839f6b4), TOBN(0x53ddf9a8, 0x9959848e),
+     TOBN(0xda49c379, 0xc21520b0), TOBN(0x90864ff0, 0xdbd5d1b6),
+     TOBN(0x2f055d23, 0x5f49c7f7), TOBN(0xe51e4e6a, 0xa796b2d8),
+     TOBN(0xc361a67f, 0x5c9dc340), TOBN(0x5ad53c37, 0xbca7c620),
+     TOBN(0xda1d6588, 0x32c756d0), TOBN(0xad60d911, 0x8bb67e13),
+     TOBN(0xd6c47bdf, 0x0eeec8c6), TOBN(0x4a27fec1, 0x078a1821),
+     TOBN(0x081f7415, 0xc3099524), TOBN(0x8effdf0b, 0x82cd8060),
+     TOBN(0xdb70ec1c, 0x65842df8), TOBN(0x8821b358, 0xd319a901),
+     TOBN(0x72ee56ee, 0xde42b529), TOBN(0x5bb39592, 0x236e4286),
+     TOBN(0xd1183316, 0xfd6f7140), TOBN(0xf9fadb5b, 0xbd8e81f7),
+     TOBN(0x701d5e0c, 0x5a02d962), TOBN(0xfdee4dbf, 0x1b601324),
+     TOBN(0xbed17407, 0x35d7620e), TOBN(0x04e3c2c3, 0xf48c0012),
+     TOBN(0x9ee29da7, 0x3455449a), TOBN(0x562cdef4, 0x91a836c4),
+     TOBN(0x8f682a5f, 0x47701097), TOBN(0x617125d8, 0xff88d0c2),
+     TOBN(0x948fda24, 0x57bb86dd), TOBN(0x348abb8f, 0x289f7286),
+     TOBN(0xeb10eab5, 0x99d94bbd), TOBN(0xd51ba28e, 0x4684d160),
+     TOBN(0xabe0e51c, 0x30c8f41a), TOBN(0x66588b45, 0x13254f4a),
+     TOBN(0x147ebf01, 0xfad097a5), TOBN(0x49883ea8, 0x610e815d),
+     TOBN(0xe44d60ba, 0x8a11de56), TOBN(0xa970de6e, 0x827a7a6d),
+     TOBN(0x2be41424, 0x5e17fc19), TOBN(0xd833c657, 0x01214057),
+     TOBN(0x1375813b, 0x363e723f), TOBN(0x6820bb88, 0xe6a52e9b),
+     TOBN(0x7e7f6970, 0xd875d56a), TOBN(0xd6a0a9ac, 0x51fbf6bf),
+     TOBN(0x54ba8790, 0xa3083c12), TOBN(0xebaeb23d, 0x6ae7eb64),
+     TOBN(0xa8685c3a, 0xb99a907a), TOBN(0xf1e74550, 0x026bf40b),
+     TOBN(0x7b73a027, 0xc802cd9e), TOBN(0x9a8a927c, 0x4fef4635),
+     TOBN(0xe1b6f60c, 0x08191224), TOBN(0xc4126ebb, 0xde4ec091),
+     TOBN(0xe1dff4dc, 0x4ae38d84), TOBN(0xde3f57db, 0x4f2ef985),
+     TOBN(0x34964337, 0xd446a1dd), TOBN(0x7bf217a0, 0x859e77f6),
+     TOBN(0x8ff10527, 0x8e1d13f5), TOBN(0xa304ef03, 0x74eeae27),
+     TOBN(0xfc6f5e47, 0xd19dfa5a), TOBN(0xdb007de3, 0x7fad982b),
+     TOBN(0x28205ad1, 0x613715f5), TOBN(0x251e6729, 0x7889529e),
+     TOBN(0x72705184, 0x1ae98e78), TOBN(0xf818537d, 0x271cac32),
+     TOBN(0xc8a15b7e, 0xb7f410f5), TOBN(0xc474356f, 0x81f62393),
+     TOBN(0x92dbdc5a, 0xc242316b), TOBN(0xabe060ac, 0xdbf4aff5),
+     TOBN(0x6e8c38fe, 0x909a8ec6), TOBN(0x43e514e5, 0x6116cb94),
+     TOBN(0x2078fa38, 0x07d784f9), TOBN(0x1161a880, 0xf4b5b357),
+     TOBN(0x5283ce79, 0x13adea3d), TOBN(0x0756c3e6, 0xcc6a910b),
+     TOBN(0x60bcfe01, 0xaaa79697), TOBN(0x04a73b29, 0x56391db1),
+     TOBN(0xdd8dad47, 0x189b45a0), TOBN(0xbfac0dd0, 0x48d5b8d9),
+     TOBN(0x34ab3af5, 0x7d3d2ec2), TOBN(0x6fa2fc2d, 0x207bd3af),
+     TOBN(0x9ff40092, 0x66550ded), TOBN(0x719b3e87, 0x1fd5b913),
+     TOBN(0xa573a496, 0x6d17fbc7), TOBN(0x0cd1a70a, 0x73d2b24e),
+     TOBN(0x34e2c5ca, 0xb2676937), TOBN(0xe7050b06, 0xbf669f21),
+     TOBN(0xfbe948b6, 0x1ede9046), TOBN(0xa0530051, 0x97662659),
+     TOBN(0x58cbd4ed, 0xf10124c5), TOBN(0xde2646e4, 0xdd6c06c8),
+     TOBN(0x332f8108, 0x8cad38c0), TOBN(0x471b7e90, 0x6bd68ae2),
+     TOBN(0x56ac3fb2, 0x0d8e27a3), TOBN(0xb54660db, 0x136b4b0d),
+     TOBN(0x123a1e11, 0xa6fd8de4), TOBN(0x44dbffea, 0xa37799ef),
+     TOBN(0x4540b977, 0xce6ac17c), TOBN(0x495173a8, 0xaf60acef)}
+    ,
+    {TOBN(0x9ebb284d, 0x391c2a82), TOBN(0xbcdd4863, 0x158308e8),
+     TOBN(0x006f16ec, 0x83f1edca), TOBN(0xa13e2c37, 0x695dc6c8),
+     TOBN(0x2ab756f0, 0x4a057a87), TOBN(0xa8765500, 0xa6b48f98),
+     TOBN(0x4252face, 0x68651c44), TOBN(0xa52b540b, 0xe1765e02),
+     TOBN(0x4f922fc5, 0x16a0d2bb), TOBN(0x0d5cc16c, 0x1a623499),
+     TOBN(0x9241cf3a, 0x57c62c8b), TOBN(0x2f5e6961, 0xfd1b667f),
+     TOBN(0x5c15c70b, 0xf5a01797), TOBN(0x3d20b44d, 0x60956192),
+     TOBN(0x04911b37, 0x071fdb52), TOBN(0xf648f916, 0x8d6f0f7b),
+     TOBN(0x6dc1acaf, 0xe60b7cf7), TOBN(0x25860a50, 0x84a9d869),
+     TOBN(0x56fc6f09, 0xe7ba8ac4), TOBN(0x828c5bd0, 0x6148d29e),
+     TOBN(0xac6b435e, 0xdc55ae5f), TOBN(0xa527f56c, 0xc0117411),
+     TOBN(0x94d5045e, 0xfd24342c), TOBN(0x2c4c0a35, 0x70b67c0d),
+     TOBN(0x027cc8b8, 0xfac61d9a), TOBN(0x7d25e062, 0xe3c6fe8a),
+     TOBN(0xe08805bf, 0xe5bff503), TOBN(0x13271e6c, 0x6ff632f7),
+     TOBN(0x55dca6c0, 0x232f76a5), TOBN(0x8957c32d, 0x701ef426),
+     TOBN(0xee728bcb, 0xa10a5178), TOBN(0x5ea60411, 0xb62c5173),
+     TOBN(0xfc4e964e, 0xd0b8892b), TOBN(0x9ea17683, 0x9301bb74),
+     TOBN(0x6265c5ae, 0xfcc48626), TOBN(0xe60cf82e, 0xbb3e9102),
+     TOBN(0x57adf797, 0xd4df5531), TOBN(0x235b59a1, 0x8deeefe2),
+     TOBN(0x60adcf58, 0x3f306eb1), TOBN(0x105c2753, 0x3d09492d),
+     TOBN(0x4090914b, 0xb5def996), TOBN(0x1cb69c83, 0x233dd1e7),
+     TOBN(0xc1e9c1d3, 0x9b3d5e76), TOBN(0x1f3338ed, 0xfccf6012),
+     TOBN(0xb1e95d0d, 0x2f5378a8), TOBN(0xacf4c2c7, 0x2f00cd21),
+     TOBN(0x6e984240, 0xeb5fe290), TOBN(0xd66c038d, 0x248088ae),
+     TOBN(0x804d264a, 0xf94d70cf), TOBN(0xbdb802ef, 0x7314bf7e),
+     TOBN(0x8fb54de2, 0x4333ed02), TOBN(0x740461e0, 0x285635d9),
+     TOBN(0x4113b2c8, 0x365e9383), TOBN(0xea762c83, 0x3fdef652),
+     TOBN(0x4eec6e2e, 0x47b956c1), TOBN(0xa3d814be, 0x65620fa4),
+     TOBN(0x9ad5462b, 0xb4d8bc50), TOBN(0x181c0b16, 0xa9195770),
+     TOBN(0xebd4fe1c, 0x78412a68), TOBN(0xae0341bc, 0xc0dff48c),
+     TOBN(0xb6bc45cf, 0x7003e866), TOBN(0xf11a6dea, 0x8a24a41b),
+     TOBN(0x5407151a, 0xd04c24c2), TOBN(0x62c9d27d, 0xda5b7b68),
+     TOBN(0x2e964235, 0x88cceff6), TOBN(0x8594c54f, 0x8b07ed69),
+     TOBN(0x1578e73c, 0xc84d0d0d), TOBN(0x7b4e1055, 0xff532868),
+     TOBN(0xa348c0d5, 0xb5ec995a), TOBN(0xbf4b9d55, 0x14289a54),
+     TOBN(0x9ba155a6, 0x58fbd777), TOBN(0x186ed7a8, 0x1a84491d),
+     TOBN(0xd4992b30, 0x614c0900), TOBN(0xda98d121, 0xbd00c24b),
+     TOBN(0x7f534dc8, 0x7ec4bfa1), TOBN(0x4a5ff674, 0x37dc34bc),
+     TOBN(0x68c196b8, 0x1d7ea1d7), TOBN(0x38cf2893, 0x80a6d208),
+     TOBN(0xfd56cd09, 0xe3cbbd6e), TOBN(0xec72e27e, 0x4205a5b6),
+     TOBN(0x15ea68f5, 0xa44f77f7), TOBN(0x7aa5f9fd, 0xb43c52bc),
+     TOBN(0x86ff676f, 0x94f0e609), TOBN(0xa4cde963, 0x2e2d432b),
+     TOBN(0x8cafa0c0, 0xeee470af), TOBN(0x84137d0e, 0x8a3f5ec8),
+     TOBN(0xebb40411, 0xfaa31231), TOBN(0xa239c13f, 0x6f7f7ccf),
+     TOBN(0x32865719, 0xa8afd30b), TOBN(0x86798328, 0x8a826dce),
+     TOBN(0xdf04e891, 0xc4a8fbe0), TOBN(0xbb6b6e1b, 0xebf56ad3),
+     TOBN(0x0a695b11, 0x471f1ff0), TOBN(0xd76c3389, 0xbe15baf0),
+     TOBN(0x018edb95, 0xbe96c43e), TOBN(0xf2beaaf4, 0x90794158),
+     TOBN(0x152db09e, 0xc3076a27), TOBN(0x5e82908e, 0xe416545d),
+     TOBN(0xa2c41272, 0x356d6f2e), TOBN(0xdc9c9642, 0x31fd74e1),
+     TOBN(0x66ceb88d, 0x519bf615), TOBN(0xe29ecd76, 0x05a2274e),
+     TOBN(0x3a0473c4, 0xbf5e2fa0), TOBN(0x6b6eb671, 0x64284e67),
+     TOBN(0xe8b97932, 0xb88756dd), TOBN(0xed4e8652, 0xf17e3e61),
+     TOBN(0xc2dd1499, 0x3ee1c4a4), TOBN(0xc0aaee17, 0x597f8c0e),
+     TOBN(0x15c4edb9, 0x6c168af3), TOBN(0x6563c7bf, 0xb39ae875),
+     TOBN(0xadfadb6f, 0x20adb436), TOBN(0xad55e8c9, 0x9a042ac0),
+     TOBN(0x975a1ed8, 0xb76da1f5), TOBN(0x10dfa466, 0xa58acb94),
+     TOBN(0x8dd7f7e3, 0xac060282), TOBN(0x6813e66a, 0x572a051e),
+     TOBN(0xb4ccae1e, 0x350cb901), TOBN(0xb653d656, 0x50cb7822),
+     TOBN(0x42484710, 0xdfab3b87), TOBN(0xcd7ee537, 0x9b670fd0),
+     TOBN(0x0a50b12e, 0x523b8bf6), TOBN(0x8009eb5b, 0x8f910c1b),
+     TOBN(0xf535af82, 0x4a167588), TOBN(0x0f835f9c, 0xfb2a2abd),
+     TOBN(0xf59b2931, 0x2afceb62), TOBN(0xc797df2a, 0x169d383f),
+     TOBN(0xeb3f5fb0, 0x66ac02b0), TOBN(0x029d4c6f, 0xdaa2d0ca),
+     TOBN(0xd4059bc1, 0xafab4bc5), TOBN(0x833f5c6f, 0x56783247),
+     TOBN(0xb5346630, 0x8d2d3605), TOBN(0x83387891, 0xd34d8433),
+     TOBN(0xd973b30f, 0xadd9419a), TOBN(0xbcca1099, 0xafe3fce8),
+     TOBN(0x08178315, 0x0809aac6), TOBN(0x01b7f21a, 0x540f0f11),
+     TOBN(0x65c29219, 0x909523c8), TOBN(0xa62f648f, 0xa3a1c741),
+     TOBN(0x88598d4f, 0x60c9e55a), TOBN(0xbce9141b, 0x0e4f347a),
+     TOBN(0x9af97d84, 0x35f9b988), TOBN(0x0210da62, 0x320475b6),
+     TOBN(0x3c076e22, 0x9191476c), TOBN(0x7520dbd9, 0x44fc7834),
+     TOBN(0x6a6b2cfe, 0xc1ab1bbd), TOBN(0xef8a65be, 0xdc650938),
+     TOBN(0x72855540, 0x805d7bc4), TOBN(0xda389396, 0xed11fdfd),
+     TOBN(0xa9d5bd36, 0x74660876), TOBN(0x11d67c54, 0xb45dff35),
+     TOBN(0x6af7d148, 0xa4f5da94), TOBN(0xbb8d4c3f, 0xc0bbeb31),
+     TOBN(0x87a7ebd1, 0xe0a1b12a), TOBN(0x1e4ef88d, 0x770ba95f),
+     TOBN(0x8c33345c, 0xdc2ae9cb), TOBN(0xcecf1276, 0x01cc8403),
+     TOBN(0x687c012e, 0x1b39b80f), TOBN(0xfd90d0ad, 0x35c33ba4),
+     TOBN(0xa3ef5a67, 0x5c9661c2), TOBN(0x368fc88e, 0xe017429e),
+     TOBN(0xd30c6761, 0x196a2fa2), TOBN(0x931b9817, 0xbd5b312e),
+     TOBN(0xba01000c, 0x72f54a31), TOBN(0xa203d2c8, 0x66eaa541),
+     TOBN(0xf2abdee0, 0x98939db3), TOBN(0xe37d6c2c, 0x3e606c02),
+     TOBN(0xf2921574, 0x521ff643), TOBN(0x2781b3c4, 0xd7e2fca3),
+     TOBN(0x664300b0, 0x7850ec06), TOBN(0xac5a38b9, 0x7d3a10cf),
+     TOBN(0x9233188d, 0xe34ab39d), TOBN(0xe77057e4, 0x5072cbb9),
+     TOBN(0xbcf0c042, 0xb59e78df), TOBN(0x4cfc91e8, 0x1d97de52),
+     TOBN(0x4661a26c, 0x3ee0ca4a), TOBN(0x5620a4c1, 0xfb8507bc),
+     TOBN(0x4b44d4aa, 0x049f842c), TOBN(0xceabc5d5, 0x1540e82b),
+     TOBN(0x306710fd, 0x15c6f156), TOBN(0xbe5ae52b, 0x63db1d72),
+     TOBN(0x06f1e7e6, 0x334957f1), TOBN(0x57e388f0, 0x31144a70),
+     TOBN(0xfb69bb2f, 0xdf96447b), TOBN(0x0f78ebd3, 0x73e38a12),
+     TOBN(0xb8222605, 0x2b7ce542), TOBN(0xe6d4ce99, 0x7472bde1),
+     TOBN(0x53e16ebe, 0x09d2f4da), TOBN(0x180ff42e, 0x53b92b2e),
+     TOBN(0xc59bcc02, 0x2c34a1c6), TOBN(0x3803d6f9, 0x422c46c2),
+     TOBN(0x18aff74f, 0x5c14a8a2), TOBN(0x55aebf80, 0x10a08b28),
+     TOBN(0x66097d58, 0x7135593f), TOBN(0x32e6eff7, 0x2be570cd),
+     TOBN(0x584e6a10, 0x2a8c860d), TOBN(0xcd185890, 0xa2eb4163),
+     TOBN(0x7ceae99d, 0x6d97e134), TOBN(0xd42c6b70, 0xdd8447ce),
+     TOBN(0x59ddbb4a, 0xb8c50273), TOBN(0x03c612df, 0x3cf34e1e),
+     TOBN(0x84b9ca15, 0x04b6c5a0), TOBN(0x35216f39, 0x18f0e3a3),
+     TOBN(0x3ec2d2bc, 0xbd986c00), TOBN(0x8bf546d9, 0xd19228fe),
+     TOBN(0xd1c655a4, 0x4cd623c3), TOBN(0x366ce718, 0x502b8e5a),
+     TOBN(0x2cfc84b4, 0xeea0bfe7), TOBN(0xe01d5cee, 0xcf443e8e),
+     TOBN(0x8ec045d9, 0x036520f8), TOBN(0xdfb3c3d1, 0x92d40e98),
+     TOBN(0x0bac4cce, 0xcc559a04), TOBN(0x35eccae5, 0x240ea6b1),
+     TOBN(0x180b32db, 0xf8a5a0ac), TOBN(0x547972a5, 0xeb699700),
+     TOBN(0xa3765801, 0xca26bca0), TOBN(0x57e09d0e, 0xa647f25a),
+     TOBN(0xb956970e, 0x2fdd23cc), TOBN(0xb80288bc, 0x5682e971),
+     TOBN(0xe6e6d91e, 0x9ae86ebc), TOBN(0x0564c83f, 0x8c9f1939),
+     TOBN(0x551932a2, 0x39560368), TOBN(0xe893752b, 0x049c28e2),
+     TOBN(0x0b03cee5, 0xa6a158c3), TOBN(0xe12d656b, 0x04964263),
+     TOBN(0x4b47554e, 0x63e3bc1d), TOBN(0xc719b6a2, 0x45044ff7),
+     TOBN(0x4f24d30a, 0xe48daa07), TOBN(0xa3f37556, 0xc8c1edc3),
+     TOBN(0x9a47bf76, 0x0700d360), TOBN(0xbb1a1824, 0x822ae4e2),
+     TOBN(0x22e275a3, 0x89f1fb4c), TOBN(0x72b1aa23, 0x9968c5f5),
+     TOBN(0xa75feaca, 0xbe063f64), TOBN(0x9b392f43, 0xbce47a09),
+     TOBN(0xd4241509, 0x1ad07aca), TOBN(0x4b0c591b, 0x8d26cd0f),
+     TOBN(0x2d42ddfd, 0x92f1169a), TOBN(0x63aeb1ac, 0x4cbf2392),
+     TOBN(0x1de9e877, 0x0691a2af), TOBN(0xebe79af7, 0xd98021da),
+     TOBN(0xcfdf2a4e, 0x40e50acf), TOBN(0xf0a98ad7, 0xaf01d665),
+     TOBN(0xefb640bf, 0x1831be1f), TOBN(0x6fe8bd2f, 0x80e9ada0),
+     TOBN(0x94c103a1, 0x6cafbc91), TOBN(0x170f8759, 0x8308e08c),
+     TOBN(0x5de2d2ab, 0x9780ff4f), TOBN(0x666466bc, 0x45b201f2),
+     TOBN(0x58af2010, 0xf5b343bc), TOBN(0x0f2e400a, 0xf2f142fe),
+     TOBN(0x3483bfde, 0xa85f4bdf), TOBN(0xf0b1d093, 0x03bfeaa9),
+     TOBN(0x2ea01b95, 0xc7081603), TOBN(0xe943e4c9, 0x3dba1097),
+     TOBN(0x47be92ad, 0xb438f3a6), TOBN(0x00bb7742, 0xe5bf6636),
+     TOBN(0x136b7083, 0x824297b4), TOBN(0x9d0e5580, 0x5584455f),
+     TOBN(0xab48cedc, 0xf1c7d69e), TOBN(0x53a9e481, 0x2a256e76),
+     TOBN(0x0402b0e0, 0x65eb2413), TOBN(0xdadbbb84, 0x8fc407a7),
+     TOBN(0xa65cd5a4, 0x8d7f5492), TOBN(0x21d44293, 0x74bae294),
+     TOBN(0x66917ce6, 0x3b5f1cc4), TOBN(0x37ae52ea, 0xce872e62),
+     TOBN(0xbb087b72, 0x2905f244), TOBN(0x12077086, 0x1e6af74f),
+     TOBN(0x4b644e49, 0x1058edea), TOBN(0x827510e3, 0xb638ca1d),
+     TOBN(0x8cf2b704, 0x6038591c), TOBN(0xffc8b47a, 0xfe635063),
+     TOBN(0x3ae220e6, 0x1b4d5e63), TOBN(0xbd864742, 0x9d961b4b),
+     TOBN(0x610c107e, 0x9bd16bed), TOBN(0x4270352a, 0x1127147b),
+     TOBN(0x7d17ffe6, 0x64cfc50e), TOBN(0x50dee01a, 0x1e36cb42),
+     TOBN(0x068a7622, 0x35dc5f9a), TOBN(0x9a08d536, 0xdf53f62c),
+     TOBN(0x4ed71457, 0x6be5f7de), TOBN(0xd93006f8, 0xc2263c9e),
+     TOBN(0xe073694c, 0xcacacb36), TOBN(0x2ff7a5b4, 0x3ae118ab),
+     TOBN(0x3cce53f1, 0xcd871236), TOBN(0xf156a39d, 0xc2aa6d52),
+     TOBN(0x9cc5f271, 0xb198d76d), TOBN(0xbc615b6f, 0x81383d39),
+     TOBN(0xa54538e8, 0xde3eee6b), TOBN(0x58c77538, 0xab910d91),
+     TOBN(0x31e5bdbc, 0x58d278bd), TOBN(0x3cde4adf, 0xb963acae),
+     TOBN(0xb1881fd2, 0x5302169c), TOBN(0x8ca60fa0, 0xa989ed8b),
+     TOBN(0xa1999458, 0xff96a0ee), TOBN(0xc1141f03, 0xac6c283d),
+     TOBN(0x7677408d, 0x6dfafed3), TOBN(0x33a01653, 0x39661588),
+     TOBN(0x3c9c15ec, 0x0b726fa0), TOBN(0x090cfd93, 0x6c9b56da),
+     TOBN(0xe34f4bae, 0xa3c40af5), TOBN(0x3469eadb, 0xd21129f1),
+     TOBN(0xcc51674a, 0x1e207ce8), TOBN(0x1e293b24, 0xc83b1ef9),
+     TOBN(0x17173d13, 0x1e6c0bb4), TOBN(0x19004695, 0x90776d35),
+     TOBN(0xe7980e34, 0x6de6f922), TOBN(0x873554cb, 0xf4dd9a22),
+     TOBN(0x0316c627, 0xcbf18a51), TOBN(0x4d93651b, 0x3032c081),
+     TOBN(0x207f2771, 0x3946834d), TOBN(0x2c08d7b4, 0x30cdbf80),
+     TOBN(0x137a4fb4, 0x86df2a61), TOBN(0xa1ed9c07, 0xecf7b4a2),
+     TOBN(0xb2e460e2, 0x7bd042ff), TOBN(0xb7f5e2fa, 0x5f62f5ec),
+     TOBN(0x7aa6ec6b, 0xcc2423b7), TOBN(0x75ce0a7f, 0xba63eea7),
+     TOBN(0x67a45fb1, 0xf250a6e1), TOBN(0x93bc919c, 0xe53cdc9f),
+     TOBN(0x9271f56f, 0x871942df), TOBN(0x2372ff6f, 0x7859ad66),
+     TOBN(0x5f4c2b96, 0x33cb1a78), TOBN(0xe3e29101, 0x5838aa83),
+     TOBN(0xa7ed1611, 0xe4e8110c), TOBN(0x2a2d70d5, 0x330198ce),
+     TOBN(0xbdf132e8, 0x6720efe0), TOBN(0xe61a8962, 0x66a471bf),
+     TOBN(0x796d3a85, 0x825808bd), TOBN(0x51dc3cb7, 0x3fd6e902),
+     TOBN(0x643c768a, 0x916219d1), TOBN(0x36cd7685, 0xa2ad7d32),
+     TOBN(0xe3db9d05, 0xb22922a4), TOBN(0x6494c87e, 0xdba29660),
+     TOBN(0xf0ac91df, 0xbcd2ebc7), TOBN(0x4deb57a0, 0x45107f8d),
+     TOBN(0x42271f59, 0xc3d12a73), TOBN(0x5f71687c, 0xa5c2c51d),
+     TOBN(0xcb1f50c6, 0x05797bcb), TOBN(0x29ed0ed9, 0xd6d34eb0),
+     TOBN(0xe5fe5b47, 0x4683c2eb), TOBN(0x4956eeb5, 0x97447c46),
+     TOBN(0x5b163a43, 0x71207167), TOBN(0x93fa2fed, 0x0248c5ef),
+     TOBN(0x67930af2, 0x31f63950), TOBN(0xa77797c1, 0x14caa2c9),
+     TOBN(0x526e80ee, 0x27ac7e62), TOBN(0xe1e6e626, 0x58b28aec),
+     TOBN(0x636178b0, 0xb3c9fef0), TOBN(0xaf7752e0, 0x6d5f90be),
+     TOBN(0x94ecaf18, 0xeece51cf), TOBN(0x2864d0ed, 0xca806e1f),
+     TOBN(0x6de2e383, 0x97c69134), TOBN(0x5a42c316, 0xeb291293),
+     TOBN(0xc7779219, 0x6a60bae0), TOBN(0xa24de346, 0x6b7599d1),
+     TOBN(0x49d374aa, 0xb75d4941), TOBN(0x98900586, 0x2d501ff0),
+     TOBN(0x9f16d40e, 0xeb7974cf), TOBN(0x1033860b, 0xcdd8c115),
+     TOBN(0xb6c69ac8, 0x2094cec3), TOBN(0x9976fb88, 0x403b770c),
+     TOBN(0x1dea026c, 0x4859590d), TOBN(0xb6acbb46, 0x8562d1fd),
+     TOBN(0x7cd6c461, 0x44569d85), TOBN(0xc3190a36, 0x97f0891d),
+     TOBN(0xc6f53195, 0x48d5a17d), TOBN(0x7d919966, 0xd749abc8),
+     TOBN(0x65104837, 0xdd1c8a20), TOBN(0x7e5410c8, 0x2f683419),
+     TOBN(0x958c3ca8, 0xbe94022e), TOBN(0x605c3197, 0x6145dac2),
+     TOBN(0x3fc07501, 0x01683d54), TOBN(0x1d7127c5, 0x595b1234),
+     TOBN(0x10b8f87c, 0x9481277f), TOBN(0x677db2a8, 0xe65a1adb),
+     TOBN(0xec2fccaa, 0xddce3345), TOBN(0x2a6811b7, 0x012a4350),
+     TOBN(0x96760ff1, 0xac598bdc), TOBN(0x054d652a, 0xd1bf4128),
+     TOBN(0x0a1151d4, 0x92a21005), TOBN(0xad7f3971, 0x33110fdf),
+     TOBN(0x8c95928c, 0x1960100f), TOBN(0x6c91c825, 0x7bf03362),
+     TOBN(0xc8c8b2a2, 0xce309f06), TOBN(0xfdb27b59, 0xca27204b),
+     TOBN(0xd223eaa5, 0x0848e32e), TOBN(0xb93e4b2e, 0xe7bfaf1e),
+     TOBN(0xc5308ae6, 0x44aa3ded), TOBN(0x317a666a, 0xc015d573),
+     TOBN(0xc888ce23, 0x1a979707), TOBN(0xf141c1e6, 0x0d5c4958),
+     TOBN(0xb53b7de5, 0x61906373), TOBN(0x858dbade, 0xeb999595),
+     TOBN(0x8cbb47b2, 0xa59e5c36), TOBN(0x660318b3, 0xdcf4e842),
+     TOBN(0xbd161ccd, 0x12ba4b7a), TOBN(0xf399daab, 0xf8c8282a),
+     TOBN(0x1587633a, 0xeeb2130d), TOBN(0xa465311a, 0xda38dd7d),
+     TOBN(0x5f75eec8, 0x64d3779b), TOBN(0x3c5d0476, 0xad64c171),
+     TOBN(0x87410371, 0x2a914428), TOBN(0x8096a891, 0x90e2fc29),
+     TOBN(0xd3d2ae9d, 0x23b3ebc2), TOBN(0x90bdd6db, 0xa580cfd6),
+     TOBN(0x52dbb7f3, 0xc5b01f6c), TOBN(0xe68eded4, 0xe102a2dc),
+     TOBN(0x17785b77, 0x99eb6df0), TOBN(0x26c3cc51, 0x7386b779),
+     TOBN(0x345ed988, 0x6417a48e), TOBN(0xe990b4e4, 0x07d6ef31),
+     TOBN(0x0f456b7e, 0x2586abba), TOBN(0x239ca6a5, 0x59c96e9a),
+     TOBN(0xe327459c, 0xe2eb4206), TOBN(0x3a4c3313, 0xa002b90a),
+     TOBN(0x2a114806, 0xf6a3f6fb), TOBN(0xad5cad2f, 0x85c251dd),
+     TOBN(0x92c1f613, 0xf5a784d3), TOBN(0xec7bfacf, 0x349766d5),
+     TOBN(0x04b3cd33, 0x3e23cb3b), TOBN(0x3979fe84, 0xc5a64b2d),
+     TOBN(0x192e2720, 0x7e589106), TOBN(0xa60c43d1, 0xa15b527f),
+     TOBN(0x2dae9082, 0xbe7cf3a6), TOBN(0xcc86ba92, 0xbc967274),
+     TOBN(0xf28a2ce8, 0xaea0a8a9), TOBN(0x404ca6d9, 0x6ee988b3),
+     TOBN(0xfd7e9c5d, 0x005921b8), TOBN(0xf56297f1, 0x44e79bf9),
+     TOBN(0xa163b460, 0x0d75ddc2), TOBN(0x30b23616, 0xa1f2be87),
+     TOBN(0x4b070d21, 0xbfe50e2b), TOBN(0x7ef8cfd0, 0xe1bfede1),
+     TOBN(0xadba0011, 0x2aac4ae0), TOBN(0x2a3e7d01, 0xb9ebd033),
+     TOBN(0x995277ec, 0xe38d9d1c), TOBN(0xb500249e, 0x9c5d2de3),
+     TOBN(0x8912b820, 0xf13ca8c9), TOBN(0xc8798114, 0x877793af),
+     TOBN(0x19e6125d, 0xec3f1dec), TOBN(0x07b1f040, 0x911178da),
+     TOBN(0xd93ededa, 0x904a6738), TOBN(0x55187a5a, 0x0bebedcd),
+     TOBN(0xf7d04722, 0xeb329d41), TOBN(0xf449099e, 0xf170b391),
+     TOBN(0xfd317a69, 0xca99f828), TOBN(0x50c3db2b, 0x34a4976d),
+     TOBN(0xe9ba7784, 0x3757b392), TOBN(0x326caefd, 0xaa3ca05a),
+     TOBN(0x78e5293b, 0xf1e593d4), TOBN(0x7842a937, 0x0d98fd13),
+     TOBN(0xe694bf96, 0x5f96b10d), TOBN(0x373a9df6, 0x06a8cd05),
+     TOBN(0x997d1e51, 0xe8f0c7fc), TOBN(0x1d019790, 0x63fd972e),
+     TOBN(0x0064d858, 0x5499fb32), TOBN(0x7b67bad9, 0x77a8aeb7),
+     TOBN(0x1d3eb977, 0x2d08eec5), TOBN(0x5fc047a6, 0xcbabae1d),
+     TOBN(0x0577d159, 0xe54a64bb), TOBN(0x8862201b, 0xc43497e4),
+     TOBN(0xad6b4e28, 0x2ce0608d), TOBN(0x8b687b7d, 0x0b167aac),
+     TOBN(0x6ed4d367, 0x8b2ecfa9), TOBN(0x24dfe62d, 0xa90c3c38),
+     TOBN(0xa1862e10, 0x3fe5c42b), TOBN(0x1ca73dca, 0xd5732a9f),
+     TOBN(0x35f038b7, 0x76bb87ad), TOBN(0x674976ab, 0xf242b81f),
+     TOBN(0x4f2bde7e, 0xb0fd90cd), TOBN(0x6efc172e, 0xa7fdf092),
+     TOBN(0x3806b69b, 0x92222f1f), TOBN(0x5a2459ca, 0x6cf7ae70),
+     TOBN(0x6789f69c, 0xa85217ee), TOBN(0x5f232b5e, 0xe3dc85ac),
+     TOBN(0x660e3ec5, 0x48e9e516), TOBN(0x124b4e47, 0x3197eb31),
+     TOBN(0x10a0cb13, 0xaafcca23), TOBN(0x7bd63ba4, 0x8213224f),
+     TOBN(0xaffad7cc, 0x290a7f4f), TOBN(0x6b409c9e, 0x0286b461),
+     TOBN(0x58ab809f, 0xffa407af), TOBN(0xc3122eed, 0xc68ac073),
+     TOBN(0x17bf9e50, 0x4ef24d7e), TOBN(0x5d929794, 0x3e2a5811),
+     TOBN(0x519bc867, 0x02902e01), TOBN(0x76bba5da, 0x39c8a851),
+     TOBN(0xe9f9669c, 0xda94951e), TOBN(0x4b6af58d, 0x66b8d418),
+     TOBN(0xfa321074, 0x17d426a4), TOBN(0xc78e66a9, 0x9dde6027),
+     TOBN(0x0516c083, 0x4a53b964), TOBN(0xfc659d38, 0xff602330),
+     TOBN(0x0ab55e5c, 0x58c5c897), TOBN(0x985099b2, 0x838bc5df),
+     TOBN(0x061d9efc, 0xc52fc238), TOBN(0x712b2728, 0x6ac1da3f),
+     TOBN(0xfb658149, 0x9283fe08), TOBN(0x4954ac94, 0xb8aaa2f7),
+     TOBN(0x85c0ada4, 0x7fb2e74f), TOBN(0xee8ba98e, 0xb89926b0),
+     TOBN(0xe4f9d37d, 0x23d1af5b), TOBN(0x14ccdbf9, 0xba9b015e),
+     TOBN(0xb674481b, 0x7bfe7178), TOBN(0x4e1debae, 0x65405868),
+     TOBN(0x061b2821, 0xc48c867d), TOBN(0x69c15b35, 0x513b30ea),
+     TOBN(0x3b4a1666, 0x36871088), TOBN(0xe5e29f5d, 0x1220b1ff),
+     TOBN(0x4b82bb35, 0x233d9f4d), TOBN(0x4e076333, 0x18cdc675)}
+    ,
+    {TOBN(0x0d53f5c7, 0xa3e6fced), TOBN(0xe8cbbdd5, 0xf45fbdeb),
+     TOBN(0xf85c01df, 0x13339a70), TOBN(0x0ff71880, 0x142ceb81),
+     TOBN(0x4c4e8774, 0xbd70437a), TOBN(0x5fb32891, 0xba0bda6a),
+     TOBN(0x1cdbebd2, 0xf18bd26e), TOBN(0x2f9526f1, 0x03a9d522),
+     TOBN(0x40ce3051, 0x92c4d684), TOBN(0x8b04d725, 0x7612efcd),
+     TOBN(0xb9dcda36, 0x6f9cae20), TOBN(0x0edc4d24, 0xf058856c),
+     TOBN(0x64f2e6bf, 0x85427900), TOBN(0x3de81295, 0xdc09dfea),
+     TOBN(0xd41b4487, 0x379bf26c), TOBN(0x50b62c6d, 0x6df135a9),
+     TOBN(0xd4f8e3b4, 0xc72dfe67), TOBN(0xc416b0f6, 0x90e19fdf),
+     TOBN(0x18b9098d, 0x4c13bd35), TOBN(0xac11118a, 0x15b8cb9e),
+     TOBN(0xf598a318, 0xf0062841), TOBN(0xbfe0602f, 0x89f356f4),
+     TOBN(0x7ae3637e, 0x30177a0c), TOBN(0x34097747, 0x61136537),
+     TOBN(0x0db2fb5e, 0xd005832a), TOBN(0x5f5efd3b, 0x91042e4f),
+     TOBN(0x8c4ffdc6, 0xed70f8ca), TOBN(0xe4645d0b, 0xb52da9cc),
+     TOBN(0x9596f58b, 0xc9001d1f), TOBN(0x52c8f0bc, 0x4e117205),
+     TOBN(0xfd4aa0d2, 0xe398a084), TOBN(0x815bfe3a, 0x104f49de),
+     TOBN(0x97e5443f, 0x23885e5f), TOBN(0xf72f8f99, 0xe8433aab),
+     TOBN(0xbd00b154, 0xe4d4e604), TOBN(0xd0b35e6a, 0xe5e173ff),
+     TOBN(0x57b2a048, 0x9164722d), TOBN(0x3e3c665b, 0x88761ec8),
+     TOBN(0x6bdd1397, 0x3da83832), TOBN(0x3c8b1a1e, 0x73dafe3b),
+     TOBN(0x4497ace6, 0x54317cac), TOBN(0xbe600ab9, 0x521771b3),
+     TOBN(0xb42e409e, 0xb0dfe8b8), TOBN(0x386a67d7, 0x3942310f),
+     TOBN(0x25548d8d, 0x4431cc28), TOBN(0xa7cff142, 0x985dc524),
+     TOBN(0x4d60f5a1, 0x93c4be32), TOBN(0x83ebd5c8, 0xd071c6e1),
+     TOBN(0xba3a80a7, 0xb1fd2b0b), TOBN(0x9b3ad396, 0x5bec33e8),
+     TOBN(0xb3868d61, 0x79743fb3), TOBN(0xcfd169fc, 0xfdb462fa),
+     TOBN(0xd3b499d7, 0x9ce0a6af), TOBN(0x55dc1cf1, 0xe42d3ff8),
+     TOBN(0x04fb9e6c, 0xc6c3e1b2), TOBN(0x47e6961d, 0x6f69a474),
+     TOBN(0x54eb3acc, 0xe548b37b), TOBN(0xb38e7542, 0x84d40549),
+     TOBN(0x8c3daa51, 0x7b341b4f), TOBN(0x2f6928ec, 0x690bf7fa),
+     TOBN(0x0496b323, 0x86ce6c41), TOBN(0x01be1c55, 0x10adadcd),
+     TOBN(0xc04e67e7, 0x4bb5faf9), TOBN(0x3cbaf678, 0xe15c9985),
+     TOBN(0x8cd12145, 0x50ca4247), TOBN(0xba1aa47a, 0xe7dd30aa),
+     TOBN(0x2f81ddf1, 0xe58fee24), TOBN(0x03452936, 0xeec9b0e8),
+     TOBN(0x8bdc3b81, 0x243aea96), TOBN(0x9a2919af, 0x15c3d0e5),
+     TOBN(0x9ea640ec, 0x10948361), TOBN(0x5ac86d5b, 0x6e0bcccf),
+     TOBN(0xf892d918, 0xc36cf440), TOBN(0xaed3e837, 0xc939719c),
+     TOBN(0xb07b08d2, 0xc0218b64), TOBN(0x6f1bcbba, 0xce9790dd),
+     TOBN(0x4a84d6ed, 0x60919b8e), TOBN(0xd8900791, 0x8ac1f9eb),
+     TOBN(0xf84941aa, 0x0dd5daef), TOBN(0xb22fe40a, 0x67fd62c5),
+     TOBN(0x97e15ba2, 0x157f2db3), TOBN(0xbda2fc8f, 0x8e28ca9c),
+     TOBN(0x5d050da4, 0x37b9f454), TOBN(0x3d57eb57, 0x2379d72e),
+     TOBN(0xe9b5eba2, 0xfb5ee997), TOBN(0x01648ca2, 0xe11538ca),
+     TOBN(0x32bb76f6, 0xf6327974), TOBN(0x338f14b8, 0xff3f4bb7),
+     TOBN(0x524d226a, 0xd7ab9a2d), TOBN(0x9c00090d, 0x7dfae958),
+     TOBN(0x0ba5f539, 0x8751d8c2), TOBN(0x8afcbcdd, 0x3ab8262d),
+     TOBN(0x57392729, 0xe99d043b), TOBN(0xef51263b, 0xaebc943a),
+     TOBN(0x9feace93, 0x20862935), TOBN(0x639efc03, 0xb06c817b),
+     TOBN(0x1fe054b3, 0x66b4be7a), TOBN(0x3f25a9de, 0x84a37a1e),
+     TOBN(0xf39ef1ad, 0x78d75cd9), TOBN(0xd7b58f49, 0x5062c1b5),
+     TOBN(0x6f74f9a9, 0xff563436), TOBN(0xf718ff29, 0xe8af51e7),
+     TOBN(0x5234d313, 0x15e97fec), TOBN(0xb6a8e2b1, 0x292f1c0a),
+     TOBN(0xa7f53aa8, 0x327720c1), TOBN(0x956ca322, 0xba092cc8),
+     TOBN(0x8f03d64a, 0x28746c4d), TOBN(0x51fe1782, 0x66d0d392),
+     TOBN(0xd19b34db, 0x3c832c80), TOBN(0x60dccc5c, 0x6da2e3b4),
+     TOBN(0x245dd62e, 0x0a104ccc), TOBN(0xa7ab1de1, 0x620b21fd),
+     TOBN(0xb293ae0b, 0x3893d123), TOBN(0xf7b75783, 0xb15ee71c),
+     TOBN(0x5aa3c614, 0x42a9468b), TOBN(0xd686123c, 0xdb15d744),
+     TOBN(0x8c616891, 0xa7ab4116), TOBN(0x6fcd72c8, 0xa4e6a459),
+     TOBN(0xac219110, 0x77e5fad7), TOBN(0xfb6a20e7, 0x704fa46b),
+     TOBN(0xe839be7d, 0x341d81dc), TOBN(0xcddb6889, 0x32148379),
+     TOBN(0xda6211a1, 0xf7026ead), TOBN(0xf3b2575f, 0xf4d1cc5e),
+     TOBN(0x40cfc8f6, 0xa7a73ae6), TOBN(0x83879a5e, 0x61d5b483),
+     TOBN(0xc5acb1ed, 0x41a50ebc), TOBN(0x59a60cc8, 0x3c07d8fa),
+     TOBN(0x1b73bdce, 0xb1876262), TOBN(0x2b0d79f0, 0x12af4ee9),
+     TOBN(0x8bcf3b0b, 0xd46e1d07), TOBN(0x17d6af9d, 0xe45d152f),
+     TOBN(0x73520461, 0x6d736451), TOBN(0x43cbbd97, 0x56b0bf5a),
+     TOBN(0xb0833a5b, 0xd5999b9d), TOBN(0x702614f0, 0xeb72e398),
+     TOBN(0x0aadf01a, 0x59c3e9f8), TOBN(0x40200e77, 0xce6b3d16),
+     TOBN(0xda22bdd3, 0xdeddafad), TOBN(0x76dedaf4, 0x310d72e1),
+     TOBN(0x49ef807c, 0x4bc2e88f), TOBN(0x6ba81291, 0x146dd5a5),
+     TOBN(0xa1a4077a, 0x7d8d59e9), TOBN(0x87b6a2e7, 0x802db349),
+     TOBN(0xd5679997, 0x1b4e598e), TOBN(0xf499ef1f, 0x06fe4b1d),
+     TOBN(0x3978d3ae, 0xfcb267c5), TOBN(0xb582b557, 0x235786d0),
+     TOBN(0x32b3b2ca, 0x1715cb07), TOBN(0x4c3de6a2, 0x8480241d),
+     TOBN(0x63b5ffed, 0xcb571ecd), TOBN(0xeaf53900, 0xed2fe9a9),
+     TOBN(0xdec98d4a, 0xc3b81990), TOBN(0x1cb83722, 0x9e0cc8fe),
+     TOBN(0xfe0b0491, 0xd2b427b9), TOBN(0x0f2386ac, 0xe983a66c),
+     TOBN(0x930c4d1e, 0xb3291213), TOBN(0xa2f82b2e, 0x59a62ae4),
+     TOBN(0x77233853, 0xf93e89e3), TOBN(0x7f8063ac, 0x11777c7f),
+     TOBN(0xff0eb567, 0x59ad2877), TOBN(0x6f454642, 0x9865c754),
+     TOBN(0xe6fe701a, 0x236e9a84), TOBN(0xc586ef16, 0x06e40fc3),
+     TOBN(0x3f62b6e0, 0x24bafad9), TOBN(0xc8b42bd2, 0x64da906a),
+     TOBN(0xc98e1eb4, 0xda3276a0), TOBN(0x30d0e5fc, 0x06cbf852),
+     TOBN(0x1b6b2ae1, 0xe8b4dfd4), TOBN(0xd754d5c7, 0x8301cbac),
+     TOBN(0x66097629, 0x112a39ac), TOBN(0xf86b5999, 0x93ba4ab9),
+     TOBN(0x26c9dea7, 0x99f9d581), TOBN(0x0473b1a8, 0xc2fafeaa),
+     TOBN(0x1469af55, 0x3b2505a5), TOBN(0x227d16d7, 0xd6a43323),
+     TOBN(0x3316f73c, 0xad3d97f9), TOBN(0x52bf3bb5, 0x1f137455),
+     TOBN(0x953eafeb, 0x09954e7c), TOBN(0xa721dfed, 0xdd732411),
+     TOBN(0xb4929821, 0x141d4579), TOBN(0x3411321c, 0xaa3bd435),
+     TOBN(0xafb355aa, 0x17fa6015), TOBN(0xb4e7ef4a, 0x18e42f0e),
+     TOBN(0x604ac97c, 0x59371000), TOBN(0xe1c48c70, 0x7f759c18),
+     TOBN(0x3f62ecc5, 0xa5db6b65), TOBN(0x0a78b173, 0x38a21495),
+     TOBN(0x6be1819d, 0xbcc8ad94), TOBN(0x70dc04f6, 0xd89c3400),
+     TOBN(0x462557b4, 0xa6b4840a), TOBN(0x544c6ade, 0x60bd21c0),
+     TOBN(0x6a00f24e, 0x907a544b), TOBN(0xa7520dcb, 0x313da210),
+     TOBN(0xfe939b75, 0x11e4994b), TOBN(0x918b6ba6, 0xbc275d70),
+     TOBN(0xd3e5e0fc, 0x644be892), TOBN(0x707a9816, 0xfdaf6c42),
+     TOBN(0x60145567, 0xf15c13fe), TOBN(0x4818ebaa, 0xe130a54a),
+     TOBN(0x28aad3ad, 0x58d2f767), TOBN(0xdc5267fd, 0xd7e7c773),
+     TOBN(0x4919cc88, 0xc3afcc98), TOBN(0xaa2e6ab0, 0x2db8cd4b),
+     TOBN(0xd46fec04, 0xd0c63eaa), TOBN(0xa1cb92c5, 0x19ffa832),
+     TOBN(0x678dd178, 0xe43a631f), TOBN(0xfb5ae1cd, 0x3dc788b3),
+     TOBN(0x68b4fb90, 0x6e77de04), TOBN(0x7992bcf0, 0xf06dbb97),
+     TOBN(0x896e6a13, 0xc417c01d), TOBN(0x8d96332c, 0xb956be01),
+     TOBN(0x902fc93a, 0x413aa2b9), TOBN(0x99a4d915, 0xfc98c8a5),
+     TOBN(0x52c29407, 0x565f1137), TOBN(0x4072690f, 0x21e4f281),
+     TOBN(0x36e607cf, 0x02ff6072), TOBN(0xa47d2ca9, 0x8ad98cdc),
+     TOBN(0xbf471d1e, 0xf5f56609), TOBN(0xbcf86623, 0xf264ada0),
+     TOBN(0xb70c0687, 0xaa9e5cb6), TOBN(0xc98124f2, 0x17401c6c),
+     TOBN(0x8189635f, 0xd4a61435), TOBN(0xd28fb8af, 0xa9d98ea6),
+     TOBN(0xb9a67c2a, 0x40c251f8), TOBN(0x88cd5d87, 0xa2da44be),
+     TOBN(0x437deb96, 0xe09b5423), TOBN(0x150467db, 0x64287dc1),
+     TOBN(0xe161debb, 0xcdabb839), TOBN(0xa79e9742, 0xf1839a3e),
+     TOBN(0xbb8dd3c2, 0x652d202b), TOBN(0x7b3e67f7, 0xe9f97d96),
+     TOBN(0x5aa5d78f, 0xb1cb6ac9), TOBN(0xffa13e8e, 0xca1d0d45),
+     TOBN(0x369295dd, 0x2ba5bf95), TOBN(0xd68bd1f8, 0x39aff05e),
+     TOBN(0xaf0d86f9, 0x26d783f2), TOBN(0x543a59b3, 0xfc3aafc1),
+     TOBN(0x3fcf81d2, 0x7b7da97c), TOBN(0xc990a056, 0xd25dee46),
+     TOBN(0x3e6775b8, 0x519cce2c), TOBN(0xfc9af71f, 0xae13d863),
+     TOBN(0x774a4a6f, 0x47c1605c), TOBN(0x46ba4245, 0x2fd205e8),
+     TOBN(0xa06feea4, 0xd3fd524d), TOBN(0x1e724641, 0x6de1acc2),
+     TOBN(0xf53816f1, 0x334e2b42), TOBN(0x49e5918e, 0x922f0024),
+     TOBN(0x439530b6, 0x65c7322d), TOBN(0xcf12cc01, 0xb3c1b3fb),
+     TOBN(0xc70b0186, 0x0172f685), TOBN(0xb915ee22, 0x1b58391d),
+     TOBN(0x9afdf03b, 0xa317db24), TOBN(0x87dec659, 0x17b8ffc4),
+     TOBN(0x7f46597b, 0xe4d3d050), TOBN(0x80a1c1ed, 0x006500e7),
+     TOBN(0x84902a96, 0x78bf030e), TOBN(0xfb5e9c9a, 0x50560148),
+     TOBN(0x6dae0a92, 0x63362426), TOBN(0xdcaeecf4, 0xa9e30c40),
+     TOBN(0xc0d887bb, 0x518d0c6b), TOBN(0x99181152, 0xcb985b9d),
+     TOBN(0xad186898, 0xef7bc381), TOBN(0x18168ffb, 0x9ee46201),
+     TOBN(0x9a04cdaa, 0x2502753c), TOBN(0xbb279e26, 0x51407c41),
+     TOBN(0xeacb03aa, 0xf23564e5), TOBN(0x18336582, 0x71e61016),
+     TOBN(0x8684b8c4, 0xeb809877), TOBN(0xb336e18d, 0xea0e672e),
+     TOBN(0xefb601f0, 0x34ee5867), TOBN(0x2733edbe, 0x1341cfd1),
+     TOBN(0xb15e809a, 0x26025c3c), TOBN(0xe6e981a6, 0x9350df88),
+     TOBN(0x92376237, 0x8502fd8e), TOBN(0x4791f216, 0x0c12be9b),
+     TOBN(0xb7256789, 0x25f02425), TOBN(0xec863194, 0x7a974443),
+     TOBN(0x7c0ce882, 0xfb41cc52), TOBN(0xc266ff7e, 0xf25c07f2),
+     TOBN(0x3d4da8c3, 0x017025f3), TOBN(0xefcf628c, 0xfb9579b4),
+     TOBN(0x5c4d0016, 0x1f3716ec), TOBN(0x9c27ebc4, 0x6801116e),
+     TOBN(0x5eba0ea1, 0x1da1767e), TOBN(0xfe151452, 0x47004c57),
+     TOBN(0x3ace6df6, 0x8c2373b7), TOBN(0x75c3dffe, 0x5dbc37ac),
+     TOBN(0x3dc32a73, 0xddc925fc), TOBN(0xb679c841, 0x2f65ee0b),
+     TOBN(0x715a3295, 0x451cbfeb), TOBN(0xd9889768, 0xf76e9a29),
+     TOBN(0xec20ce7f, 0xb28ad247), TOBN(0xe99146c4, 0x00894d79),
+     TOBN(0x71457d7c, 0x9f5e3ea7), TOBN(0x097b2662, 0x38030031),
+     TOBN(0xdb7f6ae6, 0xcf9f82a8), TOBN(0x319decb9, 0x438f473a),
+     TOBN(0xa63ab386, 0x283856c3), TOBN(0x13e3172f, 0xb06a361b),
+     TOBN(0x2959f8dc, 0x7d5a006c), TOBN(0x2dbc27c6, 0x75fba752),
+     TOBN(0xc1227ab2, 0x87c22c9e), TOBN(0x06f61f75, 0x71a268b2),
+     TOBN(0x1b6bb971, 0x04779ce2), TOBN(0xaca83812, 0x0aadcb1d),
+     TOBN(0x297ae0bc, 0xaeaab2d5), TOBN(0xa5c14ee7, 0x5bfb9f13),
+     TOBN(0xaa00c583, 0xf17a62c7), TOBN(0x39eb962c, 0x173759f6),
+     TOBN(0x1eeba1d4, 0x86c9a88f), TOBN(0x0ab6c37a, 0xdf016c5e),
+     TOBN(0xa2a147db, 0xa28a0749), TOBN(0x246c20d6, 0xee519165),
+     TOBN(0x5068d1b1, 0xd3810715), TOBN(0xb1e7018c, 0x748160b9),
+     TOBN(0x03f5b1fa, 0xf380ff62), TOBN(0xef7fb1dd, 0xf3cb2c1e),
+     TOBN(0xeab539a8, 0xfc91a7da), TOBN(0x83ddb707, 0xf3f9b561),
+     TOBN(0xc550e211, 0xfe7df7a4), TOBN(0xa7cd07f2, 0x063f6f40),
+     TOBN(0xb0de3635, 0x2976879c), TOBN(0xb5f83f85, 0xe55741da),
+     TOBN(0x4ea9d25e, 0xf3d8ac3d), TOBN(0x6fe2066f, 0x62819f02),
+     TOBN(0x4ab2b9c2, 0xcef4a564), TOBN(0x1e155d96, 0x5ffa2de3),
+     TOBN(0x0eb0a19b, 0xc3a72d00), TOBN(0x4037665b, 0x8513c31b),
+     TOBN(0x2fb2b6bf, 0x04c64637), TOBN(0x45c34d6e, 0x08cdc639),
+     TOBN(0x56f1e10f, 0xf01fd796), TOBN(0x4dfb8101, 0xfe3667b8),
+     TOBN(0xe0eda253, 0x9021d0c0), TOBN(0x7a94e9ff, 0x8a06c6ab),
+     TOBN(0x2d3bb0d9, 0xbb9aa882), TOBN(0xea20e4e5, 0xec05fd10),
+     TOBN(0xed7eeb5f, 0x1a1ca64e), TOBN(0x2fa6b43c, 0xc6327cbd),
+     TOBN(0xb577e3cf, 0x3aa91121), TOBN(0x8c6bd5ea, 0x3a34079b),
+     TOBN(0xd7e5ba39, 0x60e02fc0), TOBN(0xf16dd2c3, 0x90141bf8),
+     TOBN(0xb57276d9, 0x80101b98), TOBN(0x760883fd, 0xb82f0f66),
+     TOBN(0x89d7de75, 0x4bc3eff3), TOBN(0x03b60643, 0x5dc2ab40),
+     TOBN(0xcd6e53df, 0xe05beeac), TOBN(0xf2f1e862, 0xbc3325cd),
+     TOBN(0xdd0f7921, 0x774f03c3), TOBN(0x97ca7221, 0x4552cc1b),
+     TOBN(0x5a0d6afe, 0x1cd19f72), TOBN(0xa20915dc, 0xf183fbeb),
+     TOBN(0x9fda4b40, 0x832c403c), TOBN(0x32738edd, 0xbe425442),
+     TOBN(0x469a1df6, 0xb5eccf1a), TOBN(0x4b5aff42, 0x28bbe1f0),
+     TOBN(0x31359d7f, 0x570dfc93), TOBN(0xa18be235, 0xf0088628),
+     TOBN(0xa5b30fba, 0xb00ed3a9), TOBN(0x34c61374, 0x73cdf8be),
+     TOBN(0x2c5c5f46, 0xabc56797), TOBN(0x5cecf93d, 0xb82a8ae2),
+     TOBN(0x7d3dbe41, 0xa968fbf0), TOBN(0xd23d4583, 0x1a5c7f3d),
+     TOBN(0xf28f69a0, 0xc087a9c7), TOBN(0xc2d75471, 0x474471ca),
+     TOBN(0x36ec9f4a, 0x4eb732ec), TOBN(0x6c943bbd, 0xb1ca6bed),
+     TOBN(0xd64535e1, 0xf2457892), TOBN(0x8b84a8ea, 0xf7e2ac06),
+     TOBN(0xe0936cd3, 0x2499dd5f), TOBN(0x12053d7e, 0x0ed04e57),
+     TOBN(0x4bdd0076, 0xe4305d9d), TOBN(0x34a527b9, 0x1f67f0a2),
+     TOBN(0xe79a4af0, 0x9cec46ea), TOBN(0xb15347a1, 0x658b9bc7),
+     TOBN(0x6bd2796f, 0x35af2f75), TOBN(0xac957990, 0x4051c435),
+     TOBN(0x2669dda3, 0xc33a655d), TOBN(0x5d503c2e, 0x88514aa3),
+     TOBN(0xdfa11337, 0x3753dd41), TOBN(0x3f054673, 0x0b754f78),
+     TOBN(0xbf185677, 0x496125bd), TOBN(0xfb0023c8, 0x3775006c),
+     TOBN(0xfa0f072f, 0x3a037899), TOBN(0x4222b6eb, 0x0e4aea57),
+     TOBN(0x3dde5e76, 0x7866d25a), TOBN(0xb6eb04f8, 0x4837aa6f),
+     TOBN(0x5315591a, 0x2cf1cdb8), TOBN(0x6dfb4f41, 0x2d4e683c),
+     TOBN(0x7e923ea4, 0x48ee1f3a), TOBN(0x9604d9f7, 0x05a2afd5),
+     TOBN(0xbe1d4a33, 0x40ea4948), TOBN(0x5b45f1f4, 0xb44cbd2f),
+     TOBN(0x5faf8376, 0x4acc757e), TOBN(0xa7cf9ab8, 0x63d68ff7),
+     TOBN(0x8ad62f69, 0xdf0e404b), TOBN(0xd65f33c2, 0x12bdafdf),
+     TOBN(0xc365de15, 0xa377b14e), TOBN(0x6bf5463b, 0x8e39f60c),
+     TOBN(0x62030d2d, 0x2ce68148), TOBN(0xd95867ef, 0xe6f843a8),
+     TOBN(0xd39a0244, 0xef5ab017), TOBN(0x0bd2d8c1, 0x4ab55d12),
+     TOBN(0xc9503db3, 0x41639169), TOBN(0x2d4e25b0, 0xf7660c8a),
+     TOBN(0x760cb3b5, 0xe224c5d7), TOBN(0xfa3baf8c, 0x68616919),
+     TOBN(0x9fbca113, 0x8d142552), TOBN(0x1ab18bf1, 0x7669ebf5),
+     TOBN(0x55e6f53e, 0x9bdf25dd), TOBN(0x04cc0bf3, 0xcb6cd154),
+     TOBN(0x595bef49, 0x95e89080), TOBN(0xfe9459a8, 0x104a9ac1),
+     TOBN(0xad2d89ca, 0xcce9bb32), TOBN(0xddea65e1, 0xf7de8285),
+     TOBN(0x62ed8c35, 0xb351bd4b), TOBN(0x4150ff36, 0x0c0e19a7),
+     TOBN(0x86e3c801, 0x345f4e47), TOBN(0x3bf21f71, 0x203a266c),
+     TOBN(0x7ae110d4, 0x855b1f13), TOBN(0x5d6aaf6a, 0x07262517),
+     TOBN(0x1e0f12e1, 0x813d28f1), TOBN(0x6000e11d, 0x7ad7a523),
+     TOBN(0xc7d8deef, 0xc744a17b), TOBN(0x1e990b48, 0x14c05a00),
+     TOBN(0x68fddaee, 0x93e976d5), TOBN(0x696241d1, 0x46610d63),
+     TOBN(0xb204e7c3, 0x893dda88), TOBN(0x8bccfa65, 0x6a3a6946),
+     TOBN(0xb59425b4, 0xc5cd1411), TOBN(0x701b4042, 0xff3658b1),
+     TOBN(0xe3e56bca, 0x4784cf93), TOBN(0x27de5f15, 0x8fe68d60),
+     TOBN(0x4ab9cfce, 0xf8d53f19), TOBN(0xddb10311, 0xa40a730d),
+     TOBN(0x6fa73cd1, 0x4eee0a8a), TOBN(0xfd548748, 0x5249719d),
+     TOBN(0x49d66316, 0xa8123ef0), TOBN(0x73c32db4, 0xe7f95438),
+     TOBN(0x2e2ed209, 0x0d9e7854), TOBN(0xf98a9329, 0x9d9f0507),
+     TOBN(0xc5d33cf6, 0x0c6aa20a), TOBN(0x9a32ba14, 0x75279bb2),
+     TOBN(0x7e3202cb, 0x774a7307), TOBN(0x64ed4bc4, 0xe8c42dbd),
+     TOBN(0xc20f1a06, 0xd4caed0d), TOBN(0xb8021407, 0x171d22b3),
+     TOBN(0xd426ca04, 0xd13268d7), TOBN(0x92377007, 0x25f4d126),
+     TOBN(0x4204cbc3, 0x71f21a85), TOBN(0x18461b7a, 0xf82369ba),
+     TOBN(0xc0c07d31, 0x3fc858f9), TOBN(0x5deb5a50, 0xe2bab569),
+     TOBN(0xd5959d46, 0xd5eea89e), TOBN(0xfdff8424, 0x08437f4b),
+     TOBN(0xf21071e4, 0x3cfe254f), TOBN(0x72417696, 0x95468321),
+     TOBN(0x5d8288b9, 0x102cae3e), TOBN(0x2d143e3d, 0xf1965dff),
+     TOBN(0x00c9a376, 0xa078d847), TOBN(0x6fc0da31, 0x26028731),
+     TOBN(0xa2baeadf, 0xe45083a2), TOBN(0x66bc7218, 0x5e5b4bcd),
+     TOBN(0x2c826442, 0xd04b8e7f), TOBN(0xc19f5451, 0x6c4b586b),
+     TOBN(0x60182c49, 0x5b7eeed5), TOBN(0xd9954ecd, 0x7aa9dfa1),
+     TOBN(0xa403a8ec, 0xc73884ad), TOBN(0x7fb17de2, 0x9bb39041),
+     TOBN(0x694b64c5, 0xabb020e8), TOBN(0x3d18c184, 0x19c4eec7),
+     TOBN(0x9c4673ef, 0x1c4793e5), TOBN(0xc7b8aeb5, 0x056092e6),
+     TOBN(0x3aa1ca43, 0xf0f8c16b), TOBN(0x224ed5ec, 0xd679b2f6),
+     TOBN(0x0d56eeaf, 0x55a205c9), TOBN(0xbfe115ba, 0x4b8e028b),
+     TOBN(0x97e60849, 0x3927f4fe), TOBN(0xf91fbf94, 0x759aa7c5),
+     TOBN(0x985af769, 0x6be90a51), TOBN(0xc1277b78, 0x78ccb823),
+     TOBN(0x395b656e, 0xe7a75952), TOBN(0x00df7de0, 0x928da5f5),
+     TOBN(0x09c23175, 0x4ca4454f), TOBN(0x4ec971f4, 0x7aa2d3c1),
+     TOBN(0x45c3c507, 0xe75d9ccc), TOBN(0x63b7be8a, 0x3dc90306),
+     TOBN(0x37e09c66, 0x5db44bdc), TOBN(0x50d60da1, 0x6841c6a2),
+     TOBN(0x6f9b65ee, 0x08df1b12), TOBN(0x38734879, 0x7ff089df),
+     TOBN(0x9c331a66, 0x3fe8013d), TOBN(0x017f5de9, 0x5f42fcc8),
+     TOBN(0x43077866, 0xe8e57567), TOBN(0xc9f781ce, 0xf9fcdb18),
+     TOBN(0x38131dda, 0x9b12e174), TOBN(0x25d84aa3, 0x8a03752a),
+     TOBN(0x45e09e09, 0x4d0c0ce2), TOBN(0x1564008b, 0x92bebba5),
+     TOBN(0xf7e8ad31, 0xa87284c7), TOBN(0xb7c4b46c, 0x97e7bbaa),
+     TOBN(0x3e22a7b3, 0x97acf4ec), TOBN(0x0426c400, 0x5ea8b640),
+     TOBN(0x5e3295a6, 0x4e969285), TOBN(0x22aabc59, 0xa6a45670),
+     TOBN(0xb929714c, 0x5f5942bc), TOBN(0x9a6168bd, 0xfa3182ed),
+     TOBN(0x2216a665, 0x104152ba), TOBN(0x46908d03, 0xb6926368)}
+    ,
+    {TOBN(0xa9f5d874, 0x5a1251fb), TOBN(0x967747a8, 0xc72725c7),
+     TOBN(0x195c33e5, 0x31ffe89e), TOBN(0x609d210f, 0xe964935e),
+     TOBN(0xcafd6ca8, 0x2fe12227), TOBN(0xaf9b5b96, 0x0426469d),
+     TOBN(0x2e9ee04c, 0x5693183c), TOBN(0x1084a333, 0xc8146fef),
+     TOBN(0x96649933, 0xaed1d1f7), TOBN(0x566eaff3, 0x50563090),
+     TOBN(0x345057f0, 0xad2e39cf), TOBN(0x148ff65b, 0x1f832124),
+     TOBN(0x042e89d4, 0xcf94cf0d), TOBN(0x319bec84, 0x520c58b3),
+     TOBN(0x2a267626, 0x5361aa0d), TOBN(0xc86fa302, 0x8fbc87ad),
+     TOBN(0xfc83d2ab, 0x5c8b06d5), TOBN(0xb1a785a2, 0xfe4eac46),
+     TOBN(0xb99315bc, 0x846f7779), TOBN(0xcf31d816, 0xef9ea505),
+     TOBN(0x2391fe6a, 0x15d7dc85), TOBN(0x2f132b04, 0xb4016b33),
+     TOBN(0x29547fe3, 0x181cb4c7), TOBN(0xdb66d8a6, 0x650155a1),
+     TOBN(0x6b66d7e1, 0xadc1696f), TOBN(0x98ebe593, 0x0acd72d0),
+     TOBN(0x65f24550, 0xcc1b7435), TOBN(0xce231393, 0xb4b9a5ec),
+     TOBN(0x234a22d4, 0xdb067df9), TOBN(0x98dda095, 0xcaff9b00),
+     TOBN(0x1bbc75a0, 0x6100c9c1), TOBN(0x1560a9c8, 0x939cf695),
+     TOBN(0xcf006d3e, 0x99e0925f), TOBN(0x2dd74a96, 0x6322375a),
+     TOBN(0xc58b446a, 0xb56af5ba), TOBN(0x50292683, 0xe0b9b4f1),
+     TOBN(0xe2c34cb4, 0x1aeaffa3), TOBN(0x8b17203f, 0x9b9587c1),
+     TOBN(0x6d559207, 0xead1350c), TOBN(0x2b66a215, 0xfb7f9604),
+     TOBN(0x0850325e, 0xfe51bf74), TOBN(0x9c4f579e, 0x5e460094),
+     TOBN(0x5c87b92a, 0x76da2f25), TOBN(0x889de4e0, 0x6febef33),
+     TOBN(0x6900ec06, 0x646083ce), TOBN(0xbe2a0335, 0xbfe12773),
+     TOBN(0xadd1da35, 0xc5344110), TOBN(0x757568b7, 0xb802cd20),
+     TOBN(0x75559779, 0x00f7e6c8), TOBN(0x38e8b94f, 0x0facd2f0),
+     TOBN(0xfea1f3af, 0x03fde375), TOBN(0x5e11a1d8, 0x75881dfc),
+     TOBN(0xb3a6b02e, 0xc1e2f2ef), TOBN(0x193d2bbb, 0xc605a6c5),
+     TOBN(0x325ffeee, 0x339a0b2d), TOBN(0x27b6a724, 0x9e0c8846),
+     TOBN(0xe4050f1c, 0xf1c367ca), TOBN(0x9bc85a9b, 0xc90fbc7d),
+     TOBN(0xa373c4a2, 0xe1a11032), TOBN(0xb64232b7, 0xad0393a9),
+     TOBN(0xf5577eb0, 0x167dad29), TOBN(0x1604f301, 0x94b78ab2),
+     TOBN(0x0baa94af, 0xe829348b), TOBN(0x77fbd8dd, 0x41654342),
+     TOBN(0xdab50ea5, 0xb964e39a), TOBN(0xd4c29e3c, 0xd0d3c76e),
+     TOBN(0x80dae67c, 0x56d11964), TOBN(0x7307a8bf, 0xe5ffcc2f),
+     TOBN(0x65bbc1aa, 0x91708c3b), TOBN(0xa151e62c, 0x28bf0eeb),
+     TOBN(0x6cb53381, 0x6fa34db7), TOBN(0x5139e05c, 0xa29403a8),
+     TOBN(0x6ff651b4, 0x94a7cd2e), TOBN(0x5671ffd1, 0x0699336c),
+     TOBN(0x6f5fd2cc, 0x979a896a), TOBN(0x11e893a8, 0xd8148cef),
+     TOBN(0x988906a1, 0x65cf7b10), TOBN(0x81b67178, 0xc50d8485),
+     TOBN(0x7c0deb35, 0x8a35b3de), TOBN(0x423ac855, 0xc1d29799),
+     TOBN(0xaf580d87, 0xdac50b74), TOBN(0x28b2b89f, 0x5869734c),
+     TOBN(0x99a3b936, 0x874e28fb), TOBN(0xbb2c9190, 0x25f3f73a),
+     TOBN(0x199f6918, 0x84a9d5b7), TOBN(0x7ebe2325, 0x7e770374),
+     TOBN(0xf442e107, 0x0738efe2), TOBN(0xcf9f3f56, 0xcf9082d2),
+     TOBN(0x719f69e1, 0x09618708), TOBN(0xcc9e8364, 0xc183f9b1),
+     TOBN(0xec203a95, 0x366a21af), TOBN(0x6aec5d6d, 0x068b141f),
+     TOBN(0xee2df78a, 0x994f04e9), TOBN(0xb39ccae8, 0x271245b0),
+     TOBN(0xb875a4a9, 0x97e43f4f), TOBN(0x507dfe11, 0xdb2cea98),
+     TOBN(0x4fbf81cb, 0x489b03e9), TOBN(0xdb86ec5b, 0x6ec414fa),
+     TOBN(0xfad444f9, 0xf51b3ae5), TOBN(0xca7d33d6, 0x1914e3fe),
+     TOBN(0xa9c32f5c, 0x0ae6c4d0), TOBN(0xa9ca1d1e, 0x73969568),
+     TOBN(0x98043c31, 0x1aa7467e), TOBN(0xe832e75c, 0xe21b5ac6),
+     TOBN(0x314b7aea, 0x5232123d), TOBN(0x08307c8c, 0x65ae86db),
+     TOBN(0x06e7165c, 0xaa4668ed), TOBN(0xb170458b, 0xb4d3ec39),
+     TOBN(0x4d2e3ec6, 0xc19bb986), TOBN(0xc5f34846, 0xae0304ed),
+     TOBN(0x917695a0, 0x6c9f9722), TOBN(0x6c7f7317, 0x4cab1c0a),
+     TOBN(0x6295940e, 0x9d6d2e8b), TOBN(0xd318b8c1, 0x549f7c97),
+     TOBN(0x22453204, 0x97713885), TOBN(0x468d834b, 0xa8a440fe),
+     TOBN(0xd81fe5b2, 0xbfba796e), TOBN(0x152364db, 0x6d71f116),
+     TOBN(0xbb8c7c59, 0xb5b66e53), TOBN(0x0b12c61b, 0x2641a192),
+     TOBN(0x31f14802, 0xfcf0a7fd), TOBN(0x42fd0789, 0x5488b01e),
+     TOBN(0x71d78d6d, 0x9952b498), TOBN(0x8eb572d9, 0x07ac5201),
+     TOBN(0xe0a2a44c, 0x4d194a88), TOBN(0xd2b63fd9, 0xba017e66),
+     TOBN(0x78efc6c8, 0xf888aefc), TOBN(0xb76f6bda, 0x4a881a11),
+     TOBN(0x187f314b, 0xb46c2397), TOBN(0x004cf566, 0x5ded2819),
+     TOBN(0xa9ea5704, 0x38764d34), TOBN(0xbba45217, 0x78084709),
+     TOBN(0x06474571, 0x1171121e), TOBN(0xad7b7eb1, 0xe7c9b671),
+     TOBN(0xdacfbc40, 0x730f7507), TOBN(0x178cd8c6, 0xc7ad7bd1),
+     TOBN(0xbf0be101, 0xb2a67238), TOBN(0x3556d367, 0xaf9c14f2),
+     TOBN(0x104b7831, 0xa5662075), TOBN(0x58ca59bb, 0x79d9e60a),
+     TOBN(0x4bc45392, 0xa569a73b), TOBN(0x517a52e8, 0x5698f6c9),
+     TOBN(0x85643da5, 0xaeadd755), TOBN(0x1aed0cd5, 0x2a581b84),
+     TOBN(0xb9b4ff84, 0x80af1372), TOBN(0x244c3113, 0xf1ba5d1f),
+     TOBN(0x2a5dacbe, 0xf5f98d31), TOBN(0x2c3323e8, 0x4375bc2a),
+     TOBN(0x17a3ab4a, 0x5594b1dd), TOBN(0xa1928bfb, 0xceb4797e),
+     TOBN(0xe83af245, 0xe4886a19), TOBN(0x8979d546, 0x72b5a74a),
+     TOBN(0xa0f726bc, 0x19f9e967), TOBN(0xd9d03152, 0xe8fbbf4e),
+     TOBN(0xcfd6f51d, 0xb7707d40), TOBN(0x633084d9, 0x63f6e6e0),
+     TOBN(0xedcd9cdc, 0x55667eaf), TOBN(0x73b7f92b, 0x2e44d56f),
+     TOBN(0xfb2e39b6, 0x4e962b14), TOBN(0x7d408f6e, 0xf671fcbf),
+     TOBN(0xcc634ddc, 0x164a89bb), TOBN(0x74a42bb2, 0x3ef3bd05),
+     TOBN(0x1280dbb2, 0x428decbb), TOBN(0x6103f6bb, 0x402c8596),
+     TOBN(0xfa2bf581, 0x355a5752), TOBN(0x562f96a8, 0x00946674),
+     TOBN(0x4e4ca16d, 0x6da0223b), TOBN(0xfe47819f, 0x28d3aa25),
+     TOBN(0x9eea3075, 0xf8dfcf8a), TOBN(0xa284f0aa, 0x95669825),
+     TOBN(0xb3fca250, 0x867d3fd8), TOBN(0x20757b5f, 0x269d691e),
+     TOBN(0xf2c24020, 0x93b8a5de), TOBN(0xd3f93359, 0xebc06da6),
+     TOBN(0x1178293e, 0xb2739c33), TOBN(0xd2a3e770, 0xbcd686e5),
+     TOBN(0xa76f49f4, 0xcd941534), TOBN(0x0d37406b, 0xe3c71c0e),
+     TOBN(0x172d9397, 0x3b97f7e3), TOBN(0xec17e239, 0xbd7fd0de),
+     TOBN(0xe3290551, 0x6f496ba2), TOBN(0x6a693172, 0x36ad50e7),
+     TOBN(0xc4e539a2, 0x83e7eff5), TOBN(0x752737e7, 0x18e1b4cf),
+     TOBN(0xa2f7932c, 0x68af43ee), TOBN(0x5502468e, 0x703d00bd),
+     TOBN(0xe5dc978f, 0x2fb061f5), TOBN(0xc9a1904a, 0x28c815ad),
+     TOBN(0xd3af538d, 0x470c56a4), TOBN(0x159abc5f, 0x193d8ced),
+     TOBN(0x2a37245f, 0x20108ef3), TOBN(0xfa17081e, 0x223f7178),
+     TOBN(0x27b0fb2b, 0x10c8c0f5), TOBN(0x2102c3ea, 0x40650547),
+     TOBN(0x594564df, 0x8ac3bfa7), TOBN(0x98102033, 0x509dad96),
+     TOBN(0x6989643f, 0xf1d18a13), TOBN(0x35eebd91, 0xd7fc5af0),
+     TOBN(0x078d096a, 0xfaeaafd8), TOBN(0xb7a89341, 0xdef3de98),
+     TOBN(0x2a206e8d, 0xecf2a73a), TOBN(0x066a6397, 0x8e551994),
+     TOBN(0x3a6a088a, 0xb98d53a2), TOBN(0x0ce7c67c, 0x2d1124aa),
+     TOBN(0x48cec671, 0x759a113c), TOBN(0xe3b373d3, 0x4f6f67fa),
+     TOBN(0x5455d479, 0xfd36727b), TOBN(0xe5a428ee, 0xa13c0d81),
+     TOBN(0xb853dbc8, 0x1c86682b), TOBN(0xb78d2727, 0xb8d02b2a),
+     TOBN(0xaaf69bed, 0x8ebc329a), TOBN(0xdb6b40b3, 0x293b2148),
+     TOBN(0xe42ea77d, 0xb8c4961f), TOBN(0xb1a12f7c, 0x20e5e0ab),
+     TOBN(0xa0ec5274, 0x79e8b05e), TOBN(0x68027391, 0xfab60a80),
+     TOBN(0x6bfeea5f, 0x16b1bd5e), TOBN(0xf957e420, 0x4de30ad3),
+     TOBN(0xcbaf664e, 0x6a353b9e), TOBN(0x5c873312, 0x26d14feb),
+     TOBN(0x4e87f98c, 0xb65f57cb), TOBN(0xdb60a621, 0x5e0cdd41),
+     TOBN(0x67c16865, 0xa6881440), TOBN(0x1093ef1a, 0x46ab52aa),
+     TOBN(0xc095afb5, 0x3f4ece64), TOBN(0x6a6bb02e, 0x7604551a),
+     TOBN(0x55d44b4e, 0x0b26b8cd), TOBN(0xe5f9a999, 0xf971268a),
+     TOBN(0xc08ec425, 0x11a7de84), TOBN(0x83568095, 0xfda469dd),
+     TOBN(0x737bfba1, 0x6c6c90a2), TOBN(0x1cb9c4a0, 0xbe229831),
+     TOBN(0x93bccbba, 0xbb2eec64), TOBN(0xa0c23b64, 0xda03adbe),
+     TOBN(0x5f7aa00a, 0xe0e86ac4), TOBN(0x470b941e, 0xfc1401e6),
+     TOBN(0x5ad8d679, 0x9df43574), TOBN(0x4ccfb8a9, 0x0f65d810),
+     TOBN(0x1bce80e3, 0xaa7fbd81), TOBN(0x273291ad, 0x9508d20a),
+     TOBN(0xf5c4b46b, 0x42a92806), TOBN(0x810684ec, 0xa86ab44a),
+     TOBN(0x4591640b, 0xca0bc9f8), TOBN(0xb5efcdfc, 0x5c4b6054),
+     TOBN(0x16fc8907, 0x6e9edd12), TOBN(0xe29d0b50, 0xd4d792f9),
+     TOBN(0xa45fd01c, 0x9b03116d), TOBN(0x85035235, 0xc81765a4),
+     TOBN(0x1fe2a9b2, 0xb4b4b67c), TOBN(0xc1d10df0, 0xe8020604),
+     TOBN(0x9d64abfc, 0xbc8058d8), TOBN(0x8943b9b2, 0x712a0fbb),
+     TOBN(0x90eed914, 0x3b3def04), TOBN(0x85ab3aa2, 0x4ce775ff),
+     TOBN(0x605fd4ca, 0x7bbc9040), TOBN(0x8b34a564, 0xe2c75dfb),
+     TOBN(0x41ffc94a, 0x10358560), TOBN(0x2d8a5072, 0x9e5c28aa),
+     TOBN(0xe915a0fc, 0x4cc7eb15), TOBN(0xe9efab05, 0x8f6d0f5d),
+     TOBN(0xdbab47a9, 0xd19e9b91), TOBN(0x8cfed745, 0x0276154c),
+     TOBN(0x154357ae, 0x2cfede0d), TOBN(0x520630df, 0x19f5a4ef),
+     TOBN(0x25759f7c, 0xe382360f), TOBN(0xb6db05c9, 0x88bf5857),
+     TOBN(0x2917d61d, 0x6c58d46c), TOBN(0x14f8e491, 0xfd20cb7a),
+     TOBN(0xb68a727a, 0x11c20340), TOBN(0x0386f86f, 0xaf7ccbb6),
+     TOBN(0x5c8bc6cc, 0xfee09a20), TOBN(0x7d76ff4a, 0xbb7eea35),
+     TOBN(0xa7bdebe7, 0xdb15be7a), TOBN(0x67a08054, 0xd89f0302),
+     TOBN(0x56bf0ea9, 0xc1193364), TOBN(0xc8244467, 0x62837ebe),
+     TOBN(0x32bd8e8b, 0x20d841b8), TOBN(0x127a0548, 0xdbb8a54f),
+     TOBN(0x83dd4ca6, 0x63b20236), TOBN(0x87714718, 0x203491fa),
+     TOBN(0x4dabcaaa, 0xaa8a5288), TOBN(0x91cc0c8a, 0xaf23a1c9),
+     TOBN(0x34c72c6a, 0x3f220e0c), TOBN(0xbcc20bdf, 0x1232144a),
+     TOBN(0x6e2f42da, 0xa20ede1b), TOBN(0xc441f00c, 0x74a00515),
+     TOBN(0xbf46a5b6, 0x734b8c4b), TOBN(0x57409503, 0x7b56c9a4),
+     TOBN(0x9f735261, 0xe4585d45), TOBN(0x9231faed, 0x6734e642),
+     TOBN(0x1158a176, 0xbe70ee6c), TOBN(0x35f1068d, 0x7c3501bf),
+     TOBN(0x6beef900, 0xa2d26115), TOBN(0x649406f2, 0xef0afee3),
+     TOBN(0x3f43a60a, 0xbc2420a1), TOBN(0x509002a7, 0xd5aee4ac),
+     TOBN(0xb46836a5, 0x3ff3571b), TOBN(0x24f98b78, 0x837927c1),
+     TOBN(0x6254256a, 0x4533c716), TOBN(0xf27abb0b, 0xd07ee196),
+     TOBN(0xd7cf64fc, 0x5c6d5bfd), TOBN(0x6915c751, 0xf0cd7a77),
+     TOBN(0xd9f59012, 0x8798f534), TOBN(0x772b0da8, 0xf81d8b5f),
+     TOBN(0x1244260c, 0x2e03fa69), TOBN(0x36cf0e3a, 0x3be1a374),
+     TOBN(0x6e7c1633, 0xef06b960), TOBN(0xa71a4c55, 0x671f90f6),
+     TOBN(0x7a941251, 0x33c673db), TOBN(0xc0bea510, 0x73e8c131),
+     TOBN(0x61a8a699, 0xd4f6c734), TOBN(0x25e78c88, 0x341ed001),
+     TOBN(0x5c18acf8, 0x8e2f7d90), TOBN(0xfdbf33d7, 0x77be32cd),
+     TOBN(0x0a085cd7, 0xd2eb5ee9), TOBN(0x2d702cfb, 0xb3201115),
+     TOBN(0xb6e0ebdb, 0x85c88ce8), TOBN(0x23a3ce3c, 0x1e01d617),
+     TOBN(0x3041618e, 0x567333ac), TOBN(0x9dd0fd8f, 0x157edb6b),
+     TOBN(0x27f74702, 0xb57872b8), TOBN(0x2ef26b4f, 0x657d5fe1),
+     TOBN(0x95426f0a, 0x57cf3d40), TOBN(0x847e2ad1, 0x65a6067a),
+     TOBN(0xd474d9a0, 0x09996a74), TOBN(0x16a56acd, 0x2a26115c),
+     TOBN(0x02a615c3, 0xd16f4d43), TOBN(0xcc3fc965, 0xaadb85b7),
+     TOBN(0x386bda73, 0xce07d1b0), TOBN(0xd82910c2, 0x58ad4178),
+     TOBN(0x124f82cf, 0xcd2617f4), TOBN(0xcc2f5e8d, 0xef691770),
+     TOBN(0x82702550, 0xb8c30ccc), TOBN(0x7b856aea, 0x1a8e575a),
+     TOBN(0xbb822fef, 0xb1ab9459), TOBN(0x085928bc, 0xec24e38e),
+     TOBN(0x5d0402ec, 0xba8f4b4d), TOBN(0xc07cd4ba, 0x00b4d58b),
+     TOBN(0x5d8dffd5, 0x29227e7a), TOBN(0x61d44d0c, 0x31bf386f),
+     TOBN(0xe486dc2b, 0x135e6f4d), TOBN(0x680962eb, 0xe79410ef),
+     TOBN(0xa61bd343, 0xf10088b5), TOBN(0x6aa76076, 0xe2e28686),
+     TOBN(0x80463d11, 0x8fb98871), TOBN(0xcb26f5c3, 0xbbc76aff),
+     TOBN(0xd4ab8edd, 0xfbe03614), TOBN(0xc8eb579b, 0xc0cf2dee),
+     TOBN(0xcc004c15, 0xc93bae41), TOBN(0x46fbae5d, 0x3aeca3b2),
+     TOBN(0x671235cf, 0x0f1e9ab1), TOBN(0xadfba934, 0x9ec285c1),
+     TOBN(0x88ded013, 0xf216c980), TOBN(0xc8ac4fb8, 0xf79e0bc1),
+     TOBN(0xa29b89c6, 0xfb97a237), TOBN(0xb697b780, 0x9922d8e7),
+     TOBN(0x3142c639, 0xddb945b5), TOBN(0x447b06c7, 0xe094c3a9),
+     TOBN(0xcdcb3642, 0x72266c90), TOBN(0x633aad08, 0xa9385046),
+     TOBN(0xa36c936b, 0xb57c6477), TOBN(0x871f8b64, 0xe94dbcc6),
+     TOBN(0x28d0fb62, 0xa591a67b), TOBN(0x9d40e081, 0xc1d926f5),
+     TOBN(0x3111eaf6, 0xf2d84b5a), TOBN(0x228993f9, 0xa565b644),
+     TOBN(0x0ccbf592, 0x2c83188b), TOBN(0xf87b30ab, 0x3df3e197),
+     TOBN(0xb8658b31, 0x7642bca8), TOBN(0x1a032d7f, 0x52800f17),
+     TOBN(0x051dcae5, 0x79bf9445), TOBN(0xeba6b8ee, 0x54a2e253),
+     TOBN(0x5c8b9cad, 0xd4485692), TOBN(0x84bda40e, 0x8986e9be),
+     TOBN(0xd16d16a4, 0x2f0db448), TOBN(0x8ec80050, 0xa14d4188),
+     TOBN(0xb2b26107, 0x98fa7aaa), TOBN(0x41209ee4, 0xf073aa4e),
+     TOBN(0xf1570359, 0xf2d6b19b), TOBN(0xcbe6868c, 0xfc577caf),
+     TOBN(0x186c4bdc, 0x32c04dd3), TOBN(0xa6c35fae, 0xcfeee397),
+     TOBN(0xb4a1b312, 0xf086c0cf), TOBN(0xe0a5ccc6, 0xd9461fe2),
+     TOBN(0xc32278aa, 0x1536189f), TOBN(0x1126c55f, 0xba6df571),
+     TOBN(0x0f71a602, 0xb194560e), TOBN(0x8b2d7405, 0x324bd6e1),
+     TOBN(0x8481939e, 0x3738be71), TOBN(0xb5090b1a, 0x1a4d97a9),
+     TOBN(0x116c65a3, 0xf05ba915), TOBN(0x21863ad3, 0xaae448aa),
+     TOBN(0xd24e2679, 0xa7aae5d3), TOBN(0x7076013d, 0x0de5c1c4),
+     TOBN(0x2d50f8ba, 0xbb05b629), TOBN(0x73c1abe2, 0x6e66efbb),
+     TOBN(0xefd4b422, 0xf2488af7), TOBN(0xe4105d02, 0x663ba575),
+     TOBN(0x7eb60a8b, 0x53a69457), TOBN(0x62210008, 0xc945973b),
+     TOBN(0xfb255478, 0x77a50ec6), TOBN(0xbf0392f7, 0x0a37a72c),
+     TOBN(0xa0a7a19c, 0x4be18e7a), TOBN(0x90d8ea16, 0x25b1e0af),
+     TOBN(0x7582a293, 0xef953f57), TOBN(0x90a64d05, 0xbdc5465a),
+     TOBN(0xca79c497, 0xe2510717), TOBN(0x560dbb7c, 0x18cb641f),
+     TOBN(0x1d8e3286, 0x4b66abfb), TOBN(0xd26f52e5, 0x59030900),
+     TOBN(0x1ee3f643, 0x5584941a), TOBN(0x6d3b3730, 0x569f5958),
+     TOBN(0x9ff2a62f, 0x4789dba5), TOBN(0x91fcb815, 0x72b5c9b7),
+     TOBN(0xf446cb7d, 0x6c8f9a0e), TOBN(0x48f625c1, 0x39b7ecb5),
+     TOBN(0xbabae801, 0x1c6219b8), TOBN(0xe7a562d9, 0x28ac2f23),
+     TOBN(0xe1b48732, 0x26e20588), TOBN(0x06ee1cad, 0x775af051),
+     TOBN(0xda29ae43, 0xfaff79f7), TOBN(0xc141a412, 0x652ee9e0),
+     TOBN(0x1e127f6f, 0x195f4bd0), TOBN(0x29c6ab4f, 0x072f34f8),
+     TOBN(0x7b7c1477, 0x30448112), TOBN(0x82b51af1, 0xe4a38656),
+     TOBN(0x2bf2028a, 0x2f315010), TOBN(0xc9a4a01f, 0x6ea88cd4),
+     TOBN(0xf63e95d8, 0x257e5818), TOBN(0xdd8efa10, 0xb4519b16),
+     TOBN(0xed8973e0, 0x0da910bf), TOBN(0xed49d077, 0x5c0fe4a9),
+     TOBN(0xac3aac5e, 0xb7caee1e), TOBN(0x1033898d, 0xa7f4da57),
+     TOBN(0x42145c0e, 0x5c6669b9), TOBN(0x42daa688, 0xc1aa2aa0),
+     TOBN(0x629cc15c, 0x1a1d885a), TOBN(0x25572ec0, 0xf4b76817),
+     TOBN(0x8312e435, 0x9c8f8f28), TOBN(0x8107f8cd, 0x81965490),
+     TOBN(0x516ff3a3, 0x6fa6110c), TOBN(0x74fb1eb1, 0xfb93561f),
+     TOBN(0x6c0c9047, 0x8457522b), TOBN(0xcfd32104, 0x6bb8bdc6),
+     TOBN(0x2d6884a2, 0xcc80ad57), TOBN(0x7c27fc35, 0x86a9b637),
+     TOBN(0x3461baed, 0xadf4e8cd), TOBN(0x1d56251a, 0x617242f0),
+     TOBN(0x0b80d209, 0xc955bef4), TOBN(0xdf02cad2, 0x06adb047),
+     TOBN(0xf0d7cb91, 0x5ec74fee), TOBN(0xd2503375, 0x1111ba44),
+     TOBN(0x9671755e, 0xdf53cb36), TOBN(0x54dcb612, 0x3368551b),
+     TOBN(0x66d69aac, 0xc8a025a4), TOBN(0x6be946c6, 0xe77ef445),
+     TOBN(0x719946d1, 0xa995e094), TOBN(0x65e848f6, 0xe51e04d8),
+     TOBN(0xe62f3300, 0x6a1e3113), TOBN(0x1541c7c1, 0x501de503),
+     TOBN(0x4daac9fa, 0xf4acfade), TOBN(0x0e585897, 0x44cd0b71),
+     TOBN(0x544fd869, 0x0a51cd77), TOBN(0x60fc20ed, 0x0031016d),
+     TOBN(0x58b404ec, 0xa4276867), TOBN(0x46f6c3cc, 0x34f34993),
+     TOBN(0x477ca007, 0xc636e5bd), TOBN(0x8018f5e5, 0x7c458b47),
+     TOBN(0xa1202270, 0xe47b668f), TOBN(0xcef48ccd, 0xee14f203),
+     TOBN(0x23f98bae, 0x62ff9b4d), TOBN(0x55acc035, 0xc589eddd),
+     TOBN(0x3fe712af, 0x64db4444), TOBN(0x19e9d634, 0xbecdd480),
+     TOBN(0xe08bc047, 0xa930978a), TOBN(0x2dbf24ec, 0xa1280733),
+     TOBN(0x3c0ae38c, 0x2cd706b2), TOBN(0x5b012a5b, 0x359017b9),
+     TOBN(0x3943c38c, 0x72e0f5ae), TOBN(0x786167ea, 0x57176fa3),
+     TOBN(0xe5f9897d, 0x594881dc), TOBN(0x6b5efad8, 0xcfb820c1),
+     TOBN(0xb2179093, 0xd55018de), TOBN(0x39ad7d32, 0x0bac56ce),
+     TOBN(0xb55122e0, 0x2cfc0e81), TOBN(0x117c4661, 0xf6d89daa),
+     TOBN(0x362d01e1, 0xcb64fa09), TOBN(0x6a309b4e, 0x3e9c4ddd),
+     TOBN(0xfa979fb7, 0xabea49b1), TOBN(0xb4b1d27d, 0x10e2c6c5),
+     TOBN(0xbd61c2c4, 0x23afde7a), TOBN(0xeb6614f8, 0x9786d358),
+     TOBN(0x4a5d816b, 0x7f6f7459), TOBN(0xe431a44f, 0x09360e7b),
+     TOBN(0x8c27a032, 0xc309914c), TOBN(0xcea5d68a, 0xcaede3d8),
+     TOBN(0x3668f665, 0x3a0a3f95), TOBN(0x89369416, 0x7ceba27b),
+     TOBN(0x89981fad, 0xe4728fe9), TOBN(0x7102c8a0, 0x8a093562),
+     TOBN(0xbb80310e, 0x235d21c8), TOBN(0x505e55d1, 0xbefb7f7b),
+     TOBN(0xa0a90811, 0x12958a67), TOBN(0xd67e106a, 0x4d851fef),
+     TOBN(0xb84011a9, 0x431dd80e), TOBN(0xeb7c7cca, 0x73306cd9),
+     TOBN(0x20fadd29, 0xd1b3b730), TOBN(0x83858b5b, 0xfe37b3d3),
+     TOBN(0xbf4cd193, 0xb6251d5c), TOBN(0x1cca1fd3, 0x1352d952),
+     TOBN(0xc66157a4, 0x90fbc051), TOBN(0x7990a638, 0x89b98636),}
+    ,
+    {TOBN(0xe5aa692a, 0x87dec0e1), TOBN(0x010ded8d, 0xf7b39d00),
+     TOBN(0x7b1b80c8, 0x54cfa0b5), TOBN(0x66beb876, 0xa0f8ea28),
+     TOBN(0x50d7f531, 0x3476cd0e), TOBN(0xa63d0e65, 0xb08d3949),
+     TOBN(0x1a09eea9, 0x53479fc6), TOBN(0x82ae9891, 0xf499e742),
+     TOBN(0xab58b910, 0x5ca7d866), TOBN(0x582967e2, 0x3adb3b34),
+     TOBN(0x89ae4447, 0xcceac0bc), TOBN(0x919c667c, 0x7bf56af5),
+     TOBN(0x9aec17b1, 0x60f5dcd7), TOBN(0xec697b9f, 0xddcaadbc),
+     TOBN(0x0b98f341, 0x463467f5), TOBN(0xb187f1f7, 0xa967132f),
+     TOBN(0x90fe7a1d, 0x214aeb18), TOBN(0x1506af3c, 0x741432f7),
+     TOBN(0xbb5565f9, 0xe591a0c4), TOBN(0x10d41a77, 0xb44f1bc3),
+     TOBN(0xa09d65e4, 0xa84bde96), TOBN(0x42f060d8, 0xf20a6a1c),
+     TOBN(0x652a3bfd, 0xf27f9ce7), TOBN(0xb6bdb65c, 0x3b3d739f),
+     TOBN(0xeb5ddcb6, 0xec7fae9f), TOBN(0x995f2714, 0xefb66e5a),
+     TOBN(0xdee95d8e, 0x69445d52), TOBN(0x1b6c2d46, 0x09e27620),
+     TOBN(0x32621c31, 0x8129d716), TOBN(0xb03909f1, 0x0958c1aa),
+     TOBN(0x8c468ef9, 0x1af4af63), TOBN(0x162c429f, 0xfba5cdf6),
+     TOBN(0x2f682343, 0x753b9371), TOBN(0x29cab45a, 0x5f1f9cd7),
+     TOBN(0x571623ab, 0xb245db96), TOBN(0xc507db09, 0x3fd79999),
+     TOBN(0x4e2ef652, 0xaf036c32), TOBN(0x86f0cc78, 0x05018e5c),
+     TOBN(0xc10a73d4, 0xab8be350), TOBN(0x6519b397, 0x7e826327),
+     TOBN(0xe8cb5eef, 0x9c053df7), TOBN(0x8de25b37, 0xb300ea6f),
+     TOBN(0xdb03fa92, 0xc849cffb), TOBN(0x242e43a7, 0xe84169bb),
+     TOBN(0xe4fa51f4, 0xdd6f958e), TOBN(0x6925a77f, 0xf4445a8d),
+     TOBN(0xe6e72a50, 0xe90d8949), TOBN(0xc66648e3, 0x2b1f6390),
+     TOBN(0xb2ab1957, 0x173e460c), TOBN(0x1bbbce75, 0x30704590),
+     TOBN(0xc0a90dbd, 0xdb1c7162), TOBN(0x505e399e, 0x15cdd65d),
+     TOBN(0x68434dcb, 0x57797ab7), TOBN(0x60ad35ba, 0x6a2ca8e8),
+     TOBN(0x4bfdb1e0, 0xde3336c1), TOBN(0xbbef99eb, 0xd8b39015),
+     TOBN(0x6c3b96f3, 0x1711ebec), TOBN(0x2da40f1f, 0xce98fdc4),
+     TOBN(0xb99774d3, 0x57b4411f), TOBN(0x87c8bdf4, 0x15b65bb6),
+     TOBN(0xda3a89e3, 0xc2eef12d), TOBN(0xde95bb9b, 0x3c7471f3),
+     TOBN(0x600f225b, 0xd812c594), TOBN(0x54907c5d, 0x2b75a56b),
+     TOBN(0xa93cc5f0, 0x8db60e35), TOBN(0x743e3cd6, 0xfa833319),
+     TOBN(0x7dad5c41, 0xf81683c9), TOBN(0x70c1e7d9, 0x9c34107e),
+     TOBN(0x0edc4a39, 0xa6be0907), TOBN(0x36d47035, 0x86d0b7d3),
+     TOBN(0x8c76da03, 0x272bfa60), TOBN(0x0b4a07ea, 0x0f08a414),
+     TOBN(0x699e4d29, 0x45c1dd53), TOBN(0xcadc5898, 0x231debb5),
+     TOBN(0xdf49fcc7, 0xa77f00e0), TOBN(0x93057bbf, 0xa73e5a0e),
+     TOBN(0x2f8b7ecd, 0x027a4cd1), TOBN(0x114734b3, 0xc614011a),
+     TOBN(0xe7a01db7, 0x67677c68), TOBN(0x89d9be5e, 0x7e273f4f),
+     TOBN(0xd225cb2e, 0x089808ef), TOBN(0xf1f7a27d, 0xd59e4107),
+     TOBN(0x53afc761, 0x8211b9c9), TOBN(0x0361bc67, 0xe6819159),
+     TOBN(0x2a865d0b, 0x7f071426), TOBN(0x6a3c1810, 0xe7072567),
+     TOBN(0x3e3bca1e, 0x0d6bcabd), TOBN(0xa1b02bc1, 0x408591bc),
+     TOBN(0xe0deee59, 0x31fba239), TOBN(0xf47424d3, 0x98bd91d1),
+     TOBN(0x0f8886f4, 0x071a3c1d), TOBN(0x3f7d41e8, 0xa819233b),
+     TOBN(0x708623c2, 0xcf6eb998), TOBN(0x86bb49af, 0x609a287f),
+     TOBN(0x942bb249, 0x63c90762), TOBN(0x0ef6eea5, 0x55a9654b),
+     TOBN(0x5f6d2d72, 0x36f5defe), TOBN(0xfa9922dc, 0x56f99176),
+     TOBN(0x6c8c5ece, 0xf78ce0c7), TOBN(0x7b44589d, 0xbe09b55e),
+     TOBN(0xe11b3bca, 0x9ea83770), TOBN(0xd7fa2c7f, 0x2ab71547),
+     TOBN(0x2a3dd6fa, 0x2a1ddcc0), TOBN(0x09acb430, 0x5a7b7707),
+     TOBN(0x4add4a2e, 0x649d4e57), TOBN(0xcd53a2b0, 0x1917526e),
+     TOBN(0xc5262330, 0x20b44ac4), TOBN(0x4028746a, 0xbaa2c31d),
+     TOBN(0x51318390, 0x64291d4c), TOBN(0xbf48f151, 0xee5ad909),
+     TOBN(0xcce57f59, 0x7b185681), TOBN(0x7c3ac1b0, 0x4854d442),
+     TOBN(0x65587dc3, 0xc093c171), TOBN(0xae7acb24, 0x24f42b65),
+     TOBN(0x5a338adb, 0x955996cb), TOBN(0xc8e65675, 0x6051f91b),
+     TOBN(0x66711fba, 0x28b8d0b1), TOBN(0x15d74137, 0xb6c10a90),
+     TOBN(0x70cdd7eb, 0x3a232a80), TOBN(0xc9e2f07f, 0x6191ed24),
+     TOBN(0xa80d1db6, 0xf79588c0), TOBN(0xfa52fc69, 0xb55768cc),
+     TOBN(0x0b4df1ae, 0x7f54438a), TOBN(0x0cadd1a7, 0xf9b46a4f),
+     TOBN(0xb40ea6b3, 0x1803dd6f), TOBN(0x488e4fa5, 0x55eaae35),
+     TOBN(0x9f047d55, 0x382e4e16), TOBN(0xc9b5b7e0, 0x2f6e0c98),
+     TOBN(0x6b1bd2d3, 0x95762649), TOBN(0xa9604ee7, 0xc7aea3f6),
+     TOBN(0x3646ff27, 0x6dc6f896), TOBN(0x9bf0e7f5, 0x2860bad1),
+     TOBN(0x2d92c821, 0x7cb44b92), TOBN(0xa2f5ce63, 0xaea9c182),
+     TOBN(0xd0a2afb1, 0x9154a5fd), TOBN(0x482e474c, 0x95801da6),
+     TOBN(0xc19972d0, 0xb611c24b), TOBN(0x1d468e65, 0x60a8f351),
+     TOBN(0xeb758069, 0x7bcf6421), TOBN(0xec9dd0ee, 0x88fbc491),
+     TOBN(0x5b59d2bf, 0x956c2e32), TOBN(0x73dc6864, 0xdcddf94e),
+     TOBN(0xfd5e2321, 0xbcee7665), TOBN(0xa7b4f8ef, 0x5e9a06c4),
+     TOBN(0xfba918dd, 0x7280f855), TOBN(0xbbaac260, 0x8baec688),
+     TOBN(0xa3b3f00f, 0x33400f42), TOBN(0x3d2dba29, 0x66f2e6e4),
+     TOBN(0xb6f71a94, 0x98509375), TOBN(0x8f33031f, 0xcea423cc),
+     TOBN(0x009b8dd0, 0x4807e6fb), TOBN(0x5163cfe5, 0x5cdb954c),
+     TOBN(0x03cc8f17, 0xcf41c6e8), TOBN(0xf1f03c2a, 0x037b925c),
+     TOBN(0xc39c19cc, 0x66d2427c), TOBN(0x823d24ba, 0x7b6c18e4),
+     TOBN(0x32ef9013, 0x901f0b4f), TOBN(0x684360f1, 0xf8941c2e),
+     TOBN(0x0ebaff52, 0x2c28092e), TOBN(0x7891e4e3, 0x256c932f),
+     TOBN(0x51264319, 0xac445e3d), TOBN(0x553432e7, 0x8ea74381),
+     TOBN(0xe6eeaa69, 0x67e9c50a), TOBN(0x27ced284, 0x62e628c7),
+     TOBN(0x3f96d375, 0x7a4afa57), TOBN(0xde0a14c3, 0xe484c150),
+     TOBN(0x364a24eb, 0x38bd9923), TOBN(0x1df18da0, 0xe5177422),
+     TOBN(0x174e8f82, 0xd8d38a9b), TOBN(0x2e97c600, 0xe7de1391),
+     TOBN(0xc5709850, 0xa1c175dd), TOBN(0x969041a0, 0x32ae5035),
+     TOBN(0xcbfd533b, 0x76a2086b), TOBN(0xd6bba71b, 0xd7c2e8fe),
+     TOBN(0xb2d58ee6, 0x099dfb67), TOBN(0x3a8b342d, 0x064a85d9),
+     TOBN(0x3bc07649, 0x522f9be3), TOBN(0x690c075b, 0xdf1f49a8),
+     TOBN(0x80e1aee8, 0x3854ec42), TOBN(0x2a7dbf44, 0x17689dc7),
+     TOBN(0xc004fc0e, 0x3faf4078), TOBN(0xb2f02e9e, 0xdf11862c),
+     TOBN(0xf10a5e0f, 0xa0a1b7b3), TOBN(0x30aca623, 0x8936ec80),
+     TOBN(0xf83cbf05, 0x02f40d9a), TOBN(0x4681c468, 0x2c318a4d),
+     TOBN(0x98575618, 0x0e9c2674), TOBN(0xbe79d046, 0x1847092e),
+     TOBN(0xaf1e480a, 0x78bd01e0), TOBN(0x6dd359e4, 0x72a51db9),
+     TOBN(0x62ce3821, 0xe3afbab6), TOBN(0xc5cee5b6, 0x17733199),
+     TOBN(0xe08b30d4, 0x6ffd9fbb), TOBN(0x6e5bc699, 0x36c610b7),
+     TOBN(0xf343cff2, 0x9ce262cf), TOBN(0xca2e4e35, 0x68b914c1),
+     TOBN(0x011d64c0, 0x16de36c5), TOBN(0xe0b10fdd, 0x42e2b829),
+     TOBN(0x78942981, 0x6685aaf8), TOBN(0xe7511708, 0x230ede97),
+     TOBN(0x671ed8fc, 0x3b922bf8), TOBN(0xe4d8c0a0, 0x4c29b133),
+     TOBN(0x87eb1239, 0x3b6e99c4), TOBN(0xaff3974c, 0x8793beba),
+     TOBN(0x03749405, 0x2c18df9b), TOBN(0xc5c3a293, 0x91007139),
+     TOBN(0x6a77234f, 0xe37a0b95), TOBN(0x02c29a21, 0xb661c96b),
+     TOBN(0xc3aaf1d6, 0x141ecf61), TOBN(0x9195509e, 0x3bb22f53),
+     TOBN(0x29597404, 0x22d51357), TOBN(0x1b083822, 0x537bed60),
+     TOBN(0xcd7d6e35, 0xe07289f0), TOBN(0x1f94c48c, 0x6dd86eff),
+     TOBN(0xc8bb1f82, 0xeb0f9cfa), TOBN(0x9ee0b7e6, 0x1b2eb97d),
+     TOBN(0x5a52fe2e, 0x34d74e31), TOBN(0xa352c310, 0x3bf79ab6),
+     TOBN(0x97ff6c5a, 0xabfeeb8f), TOBN(0xbfbe8fef, 0xf5c97305),
+     TOBN(0xd6081ce6, 0xa7904608), TOBN(0x1f812f3a, 0xc4fca249),
+     TOBN(0x9b24bc9a, 0xb9e5e200), TOBN(0x91022c67, 0x38012ee8),
+     TOBN(0xe83d9c5d, 0x30a713a1), TOBN(0x4876e3f0, 0x84ef0f93),
+     TOBN(0xc9777029, 0xc1fbf928), TOBN(0xef7a6bb3, 0xbce7d2a4),
+     TOBN(0xb8067228, 0xdfa2a659), TOBN(0xd5cd3398, 0xd877a48f),
+     TOBN(0xbea4fd8f, 0x025d0f3f), TOBN(0xd67d2e35, 0x2eae7c2b),
+     TOBN(0x184de7d7, 0xcc5f4394), TOBN(0xb5551b5c, 0x4536e142),
+     TOBN(0x2e89b212, 0xd34aa60a), TOBN(0x14a96fea, 0xf50051d5),
+     TOBN(0x4e21ef74, 0x0d12bb0b), TOBN(0xc522f020, 0x60b9677e),
+     TOBN(0x8b12e467, 0x2df7731d), TOBN(0x39f80382, 0x7b326d31),
+     TOBN(0xdfb8630c, 0x39024a94), TOBN(0xaacb96a8, 0x97319452),
+     TOBN(0xd68a3961, 0xeda3867c), TOBN(0x0c58e2b0, 0x77c4ffca),
+     TOBN(0x3d545d63, 0x4da919fa), TOBN(0xef79b69a, 0xf15e2289),
+     TOBN(0x54bc3d3d, 0x808bab10), TOBN(0xc8ab3007, 0x45f82c37),
+     TOBN(0xc12738b6, 0x7c4a658a), TOBN(0xb3c47639, 0x40e72182),
+     TOBN(0x3b77be46, 0x8798e44f), TOBN(0xdc047df2, 0x17a7f85f),
+     TOBN(0x2439d4c5, 0x5e59d92d), TOBN(0xcedca475, 0xe8e64d8d),
+     TOBN(0xa724cd0d, 0x87ca9b16), TOBN(0x35e4fd59, 0xa5540dfe),
+     TOBN(0xf8c1ff18, 0xe4bcf6b1), TOBN(0x856d6285, 0x295018fa),
+     TOBN(0x433f665c, 0x3263c949), TOBN(0xa6a76dd6, 0xa1f21409),
+     TOBN(0x17d32334, 0xcc7b4f79), TOBN(0xa1d03122, 0x06720e4a),
+     TOBN(0xadb6661d, 0x81d9bed5), TOBN(0xf0d6fb02, 0x11db15d1),
+     TOBN(0x7fd11ad5, 0x1fb747d2), TOBN(0xab50f959, 0x3033762b),
+     TOBN(0x2a7e711b, 0xfbefaf5a), TOBN(0xc7393278, 0x3fef2bbf),
+     TOBN(0xe29fa244, 0x0df6f9be), TOBN(0x9092757b, 0x71efd215),
+     TOBN(0xee60e311, 0x4f3d6fd9), TOBN(0x338542d4, 0x0acfb78b),
+     TOBN(0x44a23f08, 0x38961a0f), TOBN(0x1426eade, 0x986987ca),
+     TOBN(0x36e6ee2e, 0x4a863cc6), TOBN(0x48059420, 0x628b8b79),
+     TOBN(0x30303ad8, 0x7396e1de), TOBN(0x5c8bdc48, 0x38c5aad1),
+     TOBN(0x3e40e11f, 0x5c8f5066), TOBN(0xabd6e768, 0x8d246bbd),
+     TOBN(0x68aa40bb, 0x23330a01), TOBN(0xd23f5ee4, 0xc34eafa0),
+     TOBN(0x3bbee315, 0x5de02c21), TOBN(0x18dd4397, 0xd1d8dd06),
+     TOBN(0x3ba1939a, 0x122d7b44), TOBN(0xe6d3b40a, 0xa33870d6),
+     TOBN(0x8e620f70, 0x1c4fe3f8), TOBN(0xf6bba1a5, 0xd3a50cbf),
+     TOBN(0x4a78bde5, 0xcfc0aee0), TOBN(0x847edc46, 0xc08c50bd),
+     TOBN(0xbaa2439c, 0xad63c9b2), TOBN(0xceb4a728, 0x10fc2acb),
+     TOBN(0xa419e40e, 0x26da033d), TOBN(0x6cc3889d, 0x03e02683),
+     TOBN(0x1cd28559, 0xfdccf725), TOBN(0x0fd7e0f1, 0x8d13d208),
+     TOBN(0x01b9733b, 0x1f0df9d4), TOBN(0x8cc2c5f3, 0xa2b5e4f3),
+     TOBN(0x43053bfa, 0x3a304fd4), TOBN(0x8e87665c, 0x0a9f1aa7),
+     TOBN(0x087f29ec, 0xd73dc965), TOBN(0x15ace455, 0x3e9023db),
+     TOBN(0x2370e309, 0x2bce28b4), TOBN(0xf9723442, 0xb6b1e84a),
+     TOBN(0xbeee662e, 0xb72d9f26), TOBN(0xb19396de, 0xf0e47109),
+     TOBN(0x85b1fa73, 0xe13289d0), TOBN(0x436cf77e, 0x54e58e32),
+     TOBN(0x0ec833b3, 0xe990ef77), TOBN(0x7373e3ed, 0x1b11fc25),
+     TOBN(0xbe0eda87, 0x0fc332ce), TOBN(0xced04970, 0x8d7ea856),
+     TOBN(0xf85ff785, 0x7e977ca0), TOBN(0xb66ee8da, 0xdfdd5d2b),
+     TOBN(0xf5e37950, 0x905af461), TOBN(0x587b9090, 0x966d487c),
+     TOBN(0x6a198a1b, 0x32ba0127), TOBN(0xa7720e07, 0x141615ac),
+     TOBN(0xa23f3499, 0x996ef2f2), TOBN(0xef5f64b4, 0x470bcb3d),
+     TOBN(0xa526a962, 0x92b8c559), TOBN(0x0c14aac0, 0x69740a0f),
+     TOBN(0x0d41a9e3, 0xa6bdc0a5), TOBN(0x97d52106, 0x9c48aef4),
+     TOBN(0xcf16bd30, 0x3e7c253b), TOBN(0xcc834b1a, 0x47fdedc1),
+     TOBN(0x7362c6e5, 0x373aab2e), TOBN(0x264ed85e, 0xc5f590ff),
+     TOBN(0x7a46d9c0, 0x66d41870), TOBN(0xa50c20b1, 0x4787ba09),
+     TOBN(0x185e7e51, 0xe3d44635), TOBN(0xb3b3e080, 0x31e2d8dc),
+     TOBN(0xbed1e558, 0xa179e9d9), TOBN(0x2daa3f79, 0x74a76781),
+     TOBN(0x4372baf2, 0x3a40864f), TOBN(0x46900c54, 0x4fe75cb5),
+     TOBN(0xb95f171e, 0xf76765d0), TOBN(0x4ad726d2, 0x95c87502),
+     TOBN(0x2ec769da, 0x4d7c99bd), TOBN(0x5e2ddd19, 0xc36cdfa8),
+     TOBN(0xc22117fc, 0xa93e6dea), TOBN(0xe8a2583b, 0x93771123),
+     TOBN(0xbe2f6089, 0xfa08a3a2), TOBN(0x4809d5ed, 0x8f0e1112),
+     TOBN(0x3b414aa3, 0xda7a095e), TOBN(0x9049acf1, 0x26f5aadd),
+     TOBN(0x78d46a4d, 0x6be8b84a), TOBN(0xd66b1963, 0xb732b9b3),
+     TOBN(0x5c2ac2a0, 0xde6e9555), TOBN(0xcf52d098, 0xb5bd8770),
+     TOBN(0x15a15fa6, 0x0fd28921), TOBN(0x56ccb81e, 0x8b27536d),
+     TOBN(0x0f0d8ab8, 0x9f4ccbb8), TOBN(0xed5f44d2, 0xdb221729),
+     TOBN(0x43141988, 0x00bed10c), TOBN(0xc94348a4, 0x1d735b8b),
+     TOBN(0x79f3e9c4, 0x29ef8479), TOBN(0x4c13a4e3, 0x614c693f),
+     TOBN(0x32c9af56, 0x8e143a14), TOBN(0xbc517799, 0xe29ac5c4),
+     TOBN(0x05e17992, 0x2774856f), TOBN(0x6e52fb05, 0x6c1bf55f),
+     TOBN(0xaeda4225, 0xe4f19e16), TOBN(0x70f4728a, 0xaf5ccb26),
+     TOBN(0x5d2118d1, 0xb2947f22), TOBN(0xc827ea16, 0x281d6fb9),
+     TOBN(0x8412328d, 0x8cf0eabd), TOBN(0x45ee9fb2, 0x03ef9dcf),
+     TOBN(0x8e700421, 0xbb937d63), TOBN(0xdf8ff2d5, 0xcc4b37a6),
+     TOBN(0xa4c0d5b2, 0x5ced7b68), TOBN(0x6537c1ef, 0xc7308f59),
+     TOBN(0x25ce6a26, 0x3b37f8e8), TOBN(0x170e9a9b, 0xdeebc6ce),
+     TOBN(0xdd037952, 0x8728d72c), TOBN(0x445b0e55, 0x850154bc),
+     TOBN(0x4b7d0e06, 0x83a7337b), TOBN(0x1e3416d4, 0xffecf249),
+     TOBN(0x24840eff, 0x66a2b71f), TOBN(0xd0d9a50a, 0xb37cc26d),
+     TOBN(0xe2198150, 0x6fe28ef7), TOBN(0x3cc5ef16, 0x23324c7f),
+     TOBN(0x220f3455, 0x769b5263), TOBN(0xe2ade2f1, 0xa10bf475),
+     TOBN(0x28cd20fa, 0x458d3671), TOBN(0x1549722c, 0x2dc4847b),
+     TOBN(0x6dd01e55, 0x591941e3), TOBN(0x0e6fbcea, 0x27128ccb),
+     TOBN(0xae1a1e6b, 0x3bef0262), TOBN(0xfa8c472c, 0x8f54e103),
+     TOBN(0x7539c0a8, 0x72c052ec), TOBN(0xd7b27369, 0x5a3490e9),
+     TOBN(0x143fe1f1, 0x71684349), TOBN(0x36b4722e, 0x32e19b97),
+     TOBN(0xdc059227, 0x90980aff), TOBN(0x175c9c88, 0x9e13d674),
+     TOBN(0xa7de5b22, 0x6e6bfdb1), TOBN(0x5ea5b7b2, 0xbedb4b46),
+     TOBN(0xd5570191, 0xd34a6e44), TOBN(0xfcf60d2e, 0xa24ff7e6),
+     TOBN(0x614a392d, 0x677819e1), TOBN(0x7be74c7e, 0xaa5a29e8),
+     TOBN(0xab50fece, 0x63c85f3f), TOBN(0xaca2e2a9, 0x46cab337),
+     TOBN(0x7f700388, 0x122a6fe3), TOBN(0xdb69f703, 0x882a04a8),
+     TOBN(0x9a77935d, 0xcf7aed57), TOBN(0xdf16207c, 0x8d91c86f),
+     TOBN(0x2fca49ab, 0x63ed9998), TOBN(0xa3125c44, 0xa77ddf96),
+     TOBN(0x05dd8a86, 0x24344072), TOBN(0xa023dda2, 0xfec3fb56),
+     TOBN(0x421b41fc, 0x0c743032), TOBN(0x4f2120c1, 0x5e438639),
+     TOBN(0xfb7cae51, 0xc83c1b07), TOBN(0xb2370caa, 0xcac2171a),
+     TOBN(0x2eb2d962, 0x6cc820fb), TOBN(0x59feee5c, 0xb85a44bf),
+     TOBN(0x94620fca, 0x5b6598f0), TOBN(0x6b922cae, 0x7e314051),
+     TOBN(0xff8745ad, 0x106bed4e), TOBN(0x546e71f5, 0xdfa1e9ab),
+     TOBN(0x935c1e48, 0x1ec29487), TOBN(0x9509216c, 0x4d936530),
+     TOBN(0xc7ca3067, 0x85c9a2db), TOBN(0xd6ae5152, 0x6be8606f),
+     TOBN(0x09dbcae6, 0xe14c651d), TOBN(0xc9536e23, 0x9bc32f96),
+     TOBN(0xa90535a9, 0x34521b03), TOBN(0xf39c526c, 0x878756ff),
+     TOBN(0x383172ec, 0x8aedf03c), TOBN(0x20a8075e, 0xefe0c034),
+     TOBN(0xf22f9c62, 0x64026422), TOBN(0x8dd10780, 0x24b9d076),
+     TOBN(0x944c742a, 0x3bef2950), TOBN(0x55b9502e, 0x88a2b00b),
+     TOBN(0xa59e14b4, 0x86a09817), TOBN(0xa39dd3ac, 0x47bb4071),
+     TOBN(0x55137f66, 0x3be0592f), TOBN(0x07fcafd4, 0xc9e63f5b),
+     TOBN(0x963652ee, 0x346eb226), TOBN(0x7dfab085, 0xec2facb7),
+     TOBN(0x273bf2b8, 0x691add26), TOBN(0x30d74540, 0xf2b46c44),
+     TOBN(0x05e8e73e, 0xf2c2d065), TOBN(0xff9b8a00, 0xd42eeac9),
+     TOBN(0x2fcbd205, 0x97209d22), TOBN(0xeb740ffa, 0xde14ea2c),
+     TOBN(0xc71ff913, 0xa8aef518), TOBN(0x7bfc74bb, 0xfff4cfa2),
+     TOBN(0x1716680c, 0xb6b36048), TOBN(0x121b2cce, 0x9ef79af1),
+     TOBN(0xbff3c836, 0xa01eb3d3), TOBN(0x50eb1c6a, 0x5f79077b),
+     TOBN(0xa48c32d6, 0xa004bbcf), TOBN(0x47a59316, 0x7d64f61d),
+     TOBN(0x6068147f, 0x93102016), TOBN(0x12c5f654, 0x94d12576),
+     TOBN(0xefb071a7, 0xc9bc6b91), TOBN(0x7c2da0c5, 0x6e23ea95),
+     TOBN(0xf4fd45b6, 0xd4a1dd5d), TOBN(0x3e7ad9b6, 0x9122b13c),
+     TOBN(0x342ca118, 0xe6f57a48), TOBN(0x1c2e94a7, 0x06f8288f),
+     TOBN(0x99e68f07, 0x5a97d231), TOBN(0x7c80de97, 0x4d838758),
+     TOBN(0xbce0f5d0, 0x05872727), TOBN(0xbe5d95c2, 0x19c4d016),
+     TOBN(0x921d5cb1, 0x9c2492ee), TOBN(0x42192dc1, 0x404d6fb3),
+     TOBN(0x4c84dcd1, 0x32f988d3), TOBN(0xde26d61f, 0xa17b8e85),
+     TOBN(0xc466dcb6, 0x137c7408), TOBN(0x9a38d7b6, 0x36a266da),
+     TOBN(0x7ef5cb06, 0x83bebf1b), TOBN(0xe5cdcbbf, 0x0fd014e3),
+     TOBN(0x30aa376d, 0xf65965a0), TOBN(0x60fe88c2, 0xebb3e95e),
+     TOBN(0x33fd0b61, 0x66ee6f20), TOBN(0x8827dcdb, 0x3f41f0a0),
+     TOBN(0xbf8a9d24, 0x0c56c690), TOBN(0x40265dad, 0xddb7641d),
+     TOBN(0x522b05bf, 0x3a6b662b), TOBN(0x466d1dfe, 0xb1478c9b),
+     TOBN(0xaa616962, 0x1484469b), TOBN(0x0db60549, 0x02df8f9f),
+     TOBN(0xc37bca02, 0x3cb8bf51), TOBN(0x5effe346, 0x21371ce8),
+     TOBN(0xe8f65264, 0xff112c32), TOBN(0x8a9c736d, 0x7b971fb2),
+     TOBN(0xa4f19470, 0x7b75080d), TOBN(0xfc3f2c5a, 0x8839c59b),
+     TOBN(0x1d6c777e, 0x5aeb49c2), TOBN(0xf3db034d, 0xda1addfe),
+     TOBN(0xd76fee5a, 0x5535affc), TOBN(0x0853ac70, 0xb92251fd),
+     TOBN(0x37e3d594, 0x8b2a29d5), TOBN(0x28f1f457, 0x4de00ddb),
+     TOBN(0x8083c1b5, 0xf42c328b), TOBN(0xd8ef1d8f, 0xe493c73b),
+     TOBN(0x96fb6260, 0x41dc61bd), TOBN(0xf74e8a9d, 0x27ee2f8a),
+     TOBN(0x7c605a80, 0x2c946a5d), TOBN(0xeed48d65, 0x3839ccfd),
+     TOBN(0x9894344f, 0x3a29467a), TOBN(0xde81e949, 0xc51eba6d),
+     TOBN(0xdaea066b, 0xa5e5c2f2), TOBN(0x3fc8a614, 0x08c8c7b3),
+     TOBN(0x7adff88f, 0x06d0de9f), TOBN(0xbbc11cf5, 0x3b75ce0a),
+     TOBN(0x9fbb7acc, 0xfbbc87d5), TOBN(0xa1458e26, 0x7badfde2)}
+    ,
+    {TOBN(0x1cb43668, 0xe039c256), TOBN(0x5f26fb8b, 0x7c17fd5d),
+     TOBN(0xeee426af, 0x79aa062b), TOBN(0x072002d0, 0xd78fbf04),
+     TOBN(0x4c9ca237, 0xe84fb7e3), TOBN(0xb401d8a1, 0x0c82133d),
+     TOBN(0xaaa52592, 0x6d7e4181), TOBN(0xe9430833, 0x73dbb152),
+     TOBN(0xf92dda31, 0xbe24319a), TOBN(0x03f7d28b, 0xe095a8e7),
+     TOBN(0xa52fe840, 0x98782185), TOBN(0x276ddafe, 0x29c24dbc),
+     TOBN(0x80cd5496, 0x1d7a64eb), TOBN(0xe4360889, 0x7f1dbe42),
+     TOBN(0x2f81a877, 0x8438d2d5), TOBN(0x7e4d52a8, 0x85169036),
+     TOBN(0x19e3d5b1, 0x1d59715d), TOBN(0xc7eaa762, 0xd788983e),
+     TOBN(0xe5a730b0, 0xabf1f248), TOBN(0xfbab8084, 0xfae3fd83),
+     TOBN(0x65e50d21, 0x53765b2f), TOBN(0xbdd4e083, 0xfa127f3d),
+     TOBN(0x9cf3c074, 0x397b1b10), TOBN(0x59f8090c, 0xb1b59fd3),
+     TOBN(0x7b15fd9d, 0x615faa8f), TOBN(0x8fa1eb40, 0x968554ed),
+     TOBN(0x7bb4447e, 0x7aa44882), TOBN(0x2bb2d0d1, 0x029fff32),
+     TOBN(0x075e2a64, 0x6caa6d2f), TOBN(0x8eb879de, 0x22e7351b),
+     TOBN(0xbcd5624e, 0x9a506c62), TOBN(0x218eaef0, 0xa87e24dc),
+     TOBN(0x37e56847, 0x44ddfa35), TOBN(0x9ccfc5c5, 0xdab3f747),
+     TOBN(0x9ac1df3f, 0x1ee96cf4), TOBN(0x0c0571a1, 0x3b480b8f),
+     TOBN(0x2fbeb3d5, 0x4b3a7b3c), TOBN(0x35c03669, 0x5dcdbb99),
+     TOBN(0x52a0f5dc, 0xb2415b3a), TOBN(0xd57759b4, 0x4413ed9a),
+     TOBN(0x1fe647d8, 0x3d30a2c5), TOBN(0x0857f77e, 0xf78a81dc),
+     TOBN(0x11d5a334, 0x131a4a9b), TOBN(0xc0a94af9, 0x29d393f5),
+     TOBN(0xbc3a5c0b, 0xdaa6ec1a), TOBN(0xba9fe493, 0x88d2d7ed),
+     TOBN(0xbb4335b4, 0xbb614797), TOBN(0x991c4d68, 0x72f83533),
+     TOBN(0x53258c28, 0xd2f01cb3), TOBN(0x93d6eaa3, 0xd75db0b1),
+     TOBN(0x419a2b0d, 0xe87d0db4), TOBN(0xa1e48f03, 0xd8fe8493),
+     TOBN(0xf747faf6, 0xc508b23a), TOBN(0xf137571a, 0x35d53549),
+     TOBN(0x9f5e58e2, 0xfcf9b838), TOBN(0xc7186cee, 0xa7fd3cf5),
+     TOBN(0x77b868ce, 0xe978a1d3), TOBN(0xe3a68b33, 0x7ab92d04),
+     TOBN(0x51029794, 0x87a5b862), TOBN(0x5f0606c3, 0x3a61d41d),
+     TOBN(0x2814be27, 0x6f9326f1), TOBN(0x2f521c14, 0xc6fe3c2e),
+     TOBN(0x17464d7d, 0xacdf7351), TOBN(0x10f5f9d3, 0x777f7e44),
+     TOBN(0xce8e616b, 0x269fb37d), TOBN(0xaaf73804, 0x7de62de5),
+     TOBN(0xaba11175, 0x4fdd4153), TOBN(0x515759ba, 0x3770b49b),
+     TOBN(0x8b09ebf8, 0xaa423a61), TOBN(0x592245a1, 0xcd41fb92),
+     TOBN(0x1cba8ec1, 0x9b4c8936), TOBN(0xa87e91e3, 0xaf36710e),
+     TOBN(0x1fd84ce4, 0x3d34a2e3), TOBN(0xee3759ce, 0xb43b5d61),
+     TOBN(0x895bc78c, 0x619186c7), TOBN(0xf19c3809, 0xcbb9725a),
+     TOBN(0xc0be21aa, 0xde744b1f), TOBN(0xa7d222b0, 0x60f8056b),
+     TOBN(0x74be6157, 0xb23efe11), TOBN(0x6fab2b4f, 0x0cd68253),
+     TOBN(0xad33ea5f, 0x4bf1d725), TOBN(0x9c1d8ee2, 0x4f6c950f),
+     TOBN(0x544ee78a, 0xa377af06), TOBN(0x54f489bb, 0x94a113e1),
+     TOBN(0x8f11d634, 0x992fb7e8), TOBN(0x0169a7aa, 0xa2a44347),
+     TOBN(0x1d49d4af, 0x95020e00), TOBN(0x95945722, 0xe08e120b),
+     TOBN(0xb6e33878, 0xa4d32282), TOBN(0xe36e029d, 0x48020ae7),
+     TOBN(0xe05847fb, 0x37a9b750), TOBN(0xf876812c, 0xb29e3819),
+     TOBN(0x84ad138e, 0xd23a17f0), TOBN(0x6d7b4480, 0xf0b3950e),
+     TOBN(0xdfa8aef4, 0x2fd67ae0), TOBN(0x8d3eea24, 0x52333af6),
+     TOBN(0x0d052075, 0xb15d5acc), TOBN(0xc6d9c79f, 0xbd815bc4),
+     TOBN(0x8dcafd88, 0xdfa36cf2), TOBN(0x908ccbe2, 0x38aa9070),
+     TOBN(0x638722c4, 0xba35afce), TOBN(0x5a3da8b0, 0xfd6abf0b),
+     TOBN(0x2dce252c, 0xc9c335c1), TOBN(0x84e7f0de, 0x65aa799b),
+     TOBN(0x2101a522, 0xb99a72cb), TOBN(0x06de6e67, 0x87618016),
+     TOBN(0x5ff8c7cd, 0xe6f3653e), TOBN(0x0a821ab5, 0xc7a6754a),
+     TOBN(0x7e3fa52b, 0x7cb0b5a2), TOBN(0xa7fb121c, 0xc9048790),
+     TOBN(0x1a725020, 0x06ce053a), TOBN(0xb490a31f, 0x04e929b0),
+     TOBN(0xe17be47d, 0x62dd61ad), TOBN(0x781a961c, 0x6be01371),
+     TOBN(0x1063bfd3, 0xdae3cbba), TOBN(0x35647406, 0x7f73c9ba),
+     TOBN(0xf50e957b, 0x2736a129), TOBN(0xa6313702, 0xed13f256),
+     TOBN(0x9436ee65, 0x3a19fcc5), TOBN(0xcf2bdb29, 0xe7a4c8b6),
+     TOBN(0xb06b1244, 0xc5f95cd8), TOBN(0xda8c8af0, 0xf4ab95f4),
+     TOBN(0x1bae59c2, 0xb9e5836d), TOBN(0x07d51e7e, 0x3acffffc),
+     TOBN(0x01e15e6a, 0xc2ccbcda), TOBN(0x3bc1923f, 0x8528c3e0),
+     TOBN(0x43324577, 0xa49fead4), TOBN(0x61a1b884, 0x2aa7a711),
+     TOBN(0xf9a86e08, 0x700230ef), TOBN(0x0af585a1, 0xbd19adf8),
+     TOBN(0x7645f361, 0xf55ad8f2), TOBN(0x6e676223, 0x46c3614c),
+     TOBN(0x23cb257c, 0x4e774d3f), TOBN(0x82a38513, 0xac102d1b),
+     TOBN(0x9bcddd88, 0x7b126aa5), TOBN(0xe716998b, 0xeefd3ee4),
+     TOBN(0x4239d571, 0xfb167583), TOBN(0xdd011c78, 0xd16c8f8a),
+     TOBN(0x271c2895, 0x69a27519), TOBN(0x9ce0a3b7, 0xd2d64b6a),
+     TOBN(0x8c977289, 0xd5ec6738), TOBN(0xa3b49f9a, 0x8840ef6b),
+     TOBN(0x808c14c9, 0x9a453419), TOBN(0x5c00295b, 0x0cf0a2d5),
+     TOBN(0x524414fb, 0x1d4bcc76), TOBN(0xb07691d2, 0x459a88f1),
+     TOBN(0x77f43263, 0xf70d110f), TOBN(0x64ada5e0, 0xb7abf9f3),
+     TOBN(0xafd0f94e, 0x5b544cf5), TOBN(0xb4a13a15, 0xfd2713fe),
+     TOBN(0xb99b7d6e, 0x250c74f4), TOBN(0x097f2f73, 0x20324e45),
+     TOBN(0x994b37d8, 0xaffa8208), TOBN(0xc3c31b0b, 0xdc29aafc),
+     TOBN(0x3da74651, 0x7a3a607f), TOBN(0xd8e1b8c1, 0xfe6955d6),
+     TOBN(0x716e1815, 0xc8418682), TOBN(0x541d487f, 0x7dc91d97),
+     TOBN(0x48a04669, 0xc6996982), TOBN(0xf39cab15, 0x83a6502e),
+     TOBN(0x025801a0, 0xe68db055), TOBN(0xf3569758, 0xba3338d5),
+     TOBN(0xb0c8c0aa, 0xee2afa84), TOBN(0x4f6985d3, 0xfb6562d1),
+     TOBN(0x351f1f15, 0x132ed17a), TOBN(0x510ed0b4, 0xc04365fe),
+     TOBN(0xa3f98138, 0xe5b1f066), TOBN(0xbc9d95d6, 0x32df03dc),
+     TOBN(0xa83ccf6e, 0x19abd09e), TOBN(0x0b4097c1, 0x4ff17edb),
+     TOBN(0x58a5c478, 0xd64a06ce), TOBN(0x2ddcc3fd, 0x544a58fd),
+     TOBN(0xd449503d, 0x9e8153b8), TOBN(0x3324fd02, 0x7774179b),
+     TOBN(0xaf5d47c8, 0xdbd9120c), TOBN(0xeb860162, 0x34fa94db),
+     TOBN(0x5817bdd1, 0x972f07f4), TOBN(0xe5579e2e, 0xd27bbceb),
+     TOBN(0x86847a1f, 0x5f11e5a6), TOBN(0xb39ed255, 0x7c3cf048),
+     TOBN(0xe1076417, 0xa2f62e55), TOBN(0x6b9ab38f, 0x1bcf82a2),
+     TOBN(0x4bb7c319, 0x7aeb29f9), TOBN(0xf6d17da3, 0x17227a46),
+     TOBN(0xab53ddbd, 0x0f968c00), TOBN(0xa03da7ec, 0x000c880b),
+     TOBN(0x7b239624, 0x6a9ad24d), TOBN(0x612c0401, 0x01ec60d0),
+     TOBN(0x70d10493, 0x109f5df1), TOBN(0xfbda4030, 0x80af7550),
+     TOBN(0x30b93f95, 0xc6b9a9b3), TOBN(0x0c74ec71, 0x007d9418),
+     TOBN(0x94175564, 0x6edb951f), TOBN(0x5f4a9d78, 0x7f22c282),
+     TOBN(0xb7870895, 0xb38d1196), TOBN(0xbc593df3, 0xa228ce7c),
+     TOBN(0xc78c5bd4, 0x6af3641a), TOBN(0x7802200b, 0x3d9b3dcc),
+     TOBN(0x0dc73f32, 0x8be33304), TOBN(0x847ed87d, 0x61ffb79a),
+     TOBN(0xf85c974e, 0x6d671192), TOBN(0x1e14100a, 0xde16f60f),
+     TOBN(0x45cb0d5a, 0x95c38797), TOBN(0x18923bba, 0x9b022da4),
+     TOBN(0xef2be899, 0xbbe7e86e), TOBN(0x4a1510ee, 0x216067bf),
+     TOBN(0xd98c8154, 0x84d5ce3e), TOBN(0x1af777f0, 0xf92a2b90),
+     TOBN(0x9fbcb400, 0x4ef65724), TOBN(0x3e04a4c9, 0x3c0ca6fe),
+     TOBN(0xfb3e2cb5, 0x55002994), TOBN(0x1f3a93c5, 0x5363ecab),
+     TOBN(0x1fe00efe, 0x3923555b), TOBN(0x744bedd9, 0x1e1751ea),
+     TOBN(0x3fb2db59, 0x6ab69357), TOBN(0x8dbd7365, 0xf5e6618b),
+     TOBN(0x99d53099, 0xdf1ea40e), TOBN(0xb3f24a0b, 0x57d61e64),
+     TOBN(0xd088a198, 0x596eb812), TOBN(0x22c8361b, 0x5762940b),
+     TOBN(0x66f01f97, 0xf9c0d95c), TOBN(0x88461172, 0x8e43cdae),
+     TOBN(0x11599a7f, 0xb72b15c3), TOBN(0x135a7536, 0x420d95cc),
+     TOBN(0x2dcdf0f7, 0x5f7ae2f6), TOBN(0x15fc6e1d, 0xd7fa6da2),
+     TOBN(0x81ca829a, 0xd1d441b6), TOBN(0x84c10cf8, 0x04a106b6),
+     TOBN(0xa9b26c95, 0xa73fbbd0), TOBN(0x7f24e0cb, 0x4d8f6ee8),
+     TOBN(0x48b45937, 0x1e25a043), TOBN(0xf8a74fca, 0x036f3dfe),
+     TOBN(0x1ed46585, 0xc9f84296), TOBN(0x7fbaa8fb, 0x3bc278b0),
+     TOBN(0xa8e96cd4, 0x6c4fcbd0), TOBN(0x940a1202, 0x73b60a5f),
+     TOBN(0x34aae120, 0x55a4aec8), TOBN(0x550e9a74, 0xdbd742f0),
+     TOBN(0x794456d7, 0x228c68ab), TOBN(0x492f8868, 0xa4e25ec6),
+     TOBN(0x682915ad, 0xb2d8f398), TOBN(0xf13b51cc, 0x5b84c953),
+     TOBN(0xcda90ab8, 0x5bb917d6), TOBN(0x4b615560, 0x4ea3dee1),
+     TOBN(0x578b4e85, 0x0a52c1c8), TOBN(0xeab1a695, 0x20b75fc4),
+     TOBN(0x60c14f3c, 0xaa0bb3c6), TOBN(0x220f448a, 0xb8216094),
+     TOBN(0x4fe7ee31, 0xb0e63d34), TOBN(0xf4600572, 0xa9e54fab),
+     TOBN(0xc0493334, 0xd5e7b5a4), TOBN(0x8589fb92, 0x06d54831),
+     TOBN(0xaa70f5cc, 0x6583553a), TOBN(0x0879094a, 0xe25649e5),
+     TOBN(0xcc904507, 0x10044652), TOBN(0xebb0696d, 0x02541c4f),
+     TOBN(0x5a171fde, 0xb9718710), TOBN(0x38f1bed8, 0xf374a9f5),
+     TOBN(0xc8c582e1, 0xba39bdc1), TOBN(0xfc457b0a, 0x908cc0ce),
+     TOBN(0x9a187fd4, 0x883841e2), TOBN(0x8ec25b39, 0x38725381),
+     TOBN(0x2553ed05, 0x96f84395), TOBN(0x095c7661, 0x6f6c6897),
+     TOBN(0x917ac85c, 0x4bdc5610), TOBN(0xb2885fe4, 0x179eb301),
+     TOBN(0x5fc65547, 0x8b78bdcc), TOBN(0x4a9fc893, 0xe59e4699),
+     TOBN(0xbb7ff0cd, 0x3ce299af), TOBN(0x195be9b3, 0xadf38b20),
+     TOBN(0x6a929c87, 0xd38ddb8f), TOBN(0x55fcc99c, 0xb21a51b9),
+     TOBN(0x2b695b4c, 0x721a4593), TOBN(0xed1e9a15, 0x768eaac2),
+     TOBN(0xfb63d71c, 0x7489f914), TOBN(0xf98ba31c, 0x78118910),
+     TOBN(0x80291373, 0x9b128eb4), TOBN(0x7801214e, 0xd448af4a),
+     TOBN(0xdbd2e22b, 0x55418dd3), TOBN(0xeffb3c0d, 0xd3998242),
+     TOBN(0xdfa6077c, 0xc7bf3827), TOBN(0xf2165bcb, 0x47f8238f),
+     TOBN(0xfe37cf68, 0x8564d554), TOBN(0xe5f825c4, 0x0a81fb98),
+     TOBN(0x43cc4f67, 0xffed4d6f), TOBN(0xbc609578, 0xb50a34b0),
+     TOBN(0x8aa8fcf9, 0x5041faf1), TOBN(0x5659f053, 0x651773b6),
+     TOBN(0xe87582c3, 0x6044d63b), TOBN(0xa6089409, 0x0cdb0ca0),
+     TOBN(0x8c993e0f, 0xbfb2bcf6), TOBN(0xfc64a719, 0x45985cfc),
+     TOBN(0x15c4da80, 0x83dbedba), TOBN(0x804ae112, 0x2be67df7),
+     TOBN(0xda4c9658, 0xa23defde), TOBN(0x12002ddd, 0x5156e0d3),
+     TOBN(0xe68eae89, 0x5dd21b96), TOBN(0x8b99f28b, 0xcf44624d),
+     TOBN(0x0ae00808, 0x1ec8897a), TOBN(0xdd0a9303, 0x6712f76e),
+     TOBN(0x96237522, 0x4e233de4), TOBN(0x192445b1, 0x2b36a8a5),
+     TOBN(0xabf9ff74, 0x023993d9), TOBN(0x21f37bf4, 0x2aad4a8f),
+     TOBN(0x340a4349, 0xf8bd2bbd), TOBN(0x1d902cd9, 0x4868195d),
+     TOBN(0x3d27bbf1, 0xe5fdb6f1), TOBN(0x7a5ab088, 0x124f9f1c),
+     TOBN(0xc466ab06, 0xf7a09e03), TOBN(0x2f8a1977, 0x31f2c123),
+     TOBN(0xda355dc7, 0x041b6657), TOBN(0xcb840d12, 0x8ece2a7c),
+     TOBN(0xb600ad9f, 0x7db32675), TOBN(0x78fea133, 0x07a06f1b),
+     TOBN(0x5d032269, 0xb31f6094), TOBN(0x07753ef5, 0x83ec37aa),
+     TOBN(0x03485aed, 0x9c0bea78), TOBN(0x41bb3989, 0xbc3f4524),
+     TOBN(0x09403761, 0x697f726d), TOBN(0x6109beb3, 0xdf394820),
+     TOBN(0x804111ea, 0x3b6d1145), TOBN(0xb6271ea9, 0xa8582654),
+     TOBN(0x619615e6, 0x24e66562), TOBN(0xa2554945, 0xd7b6ad9c),
+     TOBN(0xd9c4985e, 0x99bfe35f), TOBN(0x9770ccc0, 0x7b51cdf6),
+     TOBN(0x7c327013, 0x92881832), TOBN(0x8777d45f, 0x286b26d1),
+     TOBN(0x9bbeda22, 0xd847999d), TOBN(0x03aa33b6, 0xc3525d32),
+     TOBN(0x4b7b96d4, 0x28a959a1), TOBN(0xbb3786e5, 0x31e5d234),
+     TOBN(0xaeb5d3ce, 0x6961f247), TOBN(0x20aa85af, 0x02f93d3f),
+     TOBN(0x9cd1ad3d, 0xd7a7ae4f), TOBN(0xbf6688f0, 0x781adaa8),
+     TOBN(0xb1b40e86, 0x7469cead), TOBN(0x1904c524, 0x309fca48),
+     TOBN(0x9b7312af, 0x4b54bbc7), TOBN(0xbe24bf8f, 0x593affa2),
+     TOBN(0xbe5e0790, 0xbd98764b), TOBN(0xa0f45f17, 0xa26e299e),
+     TOBN(0x4af0d2c2, 0x6b8fe4c7), TOBN(0xef170db1, 0x8ae8a3e6),
+     TOBN(0x0e8d61a0, 0x29e0ccc1), TOBN(0xcd53e87e, 0x60ad36ca),
+     TOBN(0x328c6623, 0xc8173822), TOBN(0x7ee1767d, 0xa496be55),
+     TOBN(0x89f13259, 0x648945af), TOBN(0x9e45a5fd, 0x25c8009c),
+     TOBN(0xaf2febd9, 0x1f61ab8c), TOBN(0x43f6bc86, 0x8a275385),
+     TOBN(0x87792348, 0xf2142e79), TOBN(0x17d89259, 0xc6e6238a),
+     TOBN(0x7536d2f6, 0x4a839d9b), TOBN(0x1f428fce, 0x76a1fbdc),
+     TOBN(0x1c109601, 0x0db06dfe), TOBN(0xbfc16bc1, 0x50a3a3cc),
+     TOBN(0xf9cbd9ec, 0x9b30f41b), TOBN(0x5b5da0d6, 0x00138cce),
+     TOBN(0xec1d0a48, 0x56ef96a7), TOBN(0xb47eb848, 0x982bf842),
+     TOBN(0x66deae32, 0xec3f700d), TOBN(0x4e43c42c, 0xaa1181e0),
+     TOBN(0xa1d72a31, 0xd1a4aa2a), TOBN(0x440d4668, 0xc004f3ce),
+     TOBN(0x0d6a2d3b, 0x45fe8a7a), TOBN(0x820e52e2, 0xfb128365),
+     TOBN(0x29ac5fcf, 0x25e51b09), TOBN(0x180cd2bf, 0x2023d159),
+     TOBN(0xa9892171, 0xa1ebf90e), TOBN(0xf97c4c87, 0x7c132181),
+     TOBN(0x9f1dc724, 0xc03dbb7e), TOBN(0xae043765, 0x018cbbe4),
+     TOBN(0xfb0b2a36, 0x0767d153), TOBN(0xa8e2f4d6, 0x249cbaeb),
+     TOBN(0x172a5247, 0xd95ea168), TOBN(0x1758fada, 0x2970764a),
+     TOBN(0xac803a51, 0x1d978169), TOBN(0x299cfe2e, 0xde77e01b),
+     TOBN(0x652a1e17, 0xb0a98927), TOBN(0x2e26e1d1, 0x20014495),
+     TOBN(0x7ae0af9f, 0x7175b56a), TOBN(0xc2e22a80, 0xd64b9f95),
+     TOBN(0x4d0ff9fb, 0xd90a060a), TOBN(0x496a27db, 0xbaf38085),
+     TOBN(0x32305401, 0xda776bcf), TOBN(0xb8cdcef6, 0x725f209e),
+     TOBN(0x61ba0f37, 0x436a0bba), TOBN(0x263fa108, 0x76860049),
+     TOBN(0x92beb98e, 0xda3542cf), TOBN(0xa2d4d14a, 0xd5849538),
+     TOBN(0x989b9d68, 0x12e9a1bc), TOBN(0x61d9075c, 0x5f6e3268),
+     TOBN(0x352c6aa9, 0x99ace638), TOBN(0xde4e4a55, 0x920f43ff),
+     TOBN(0xe5e4144a, 0xd673c017), TOBN(0x667417ae, 0x6f6e05ea),
+     TOBN(0x613416ae, 0xdcd1bd56), TOBN(0x5eb36201, 0x86693711),
+     TOBN(0x2d7bc504, 0x3a1aa914), TOBN(0x175a1299, 0x76dc5975),
+     TOBN(0xe900e0f2, 0x3fc8125c), TOBN(0x569ef68c, 0x11198875),
+     TOBN(0x9012db63, 0x63a113b4), TOBN(0xe3bd3f56, 0x98835766),
+     TOBN(0xa5c94a52, 0x76412dea), TOBN(0xad9e2a09, 0xaa735e5c),
+     TOBN(0x405a984c, 0x508b65e9), TOBN(0xbde4a1d1, 0x6df1a0d1),
+     TOBN(0x1a9433a1, 0xdfba80da), TOBN(0xe9192ff9, 0x9440ad2e),
+     TOBN(0x9f649696, 0x5099fe92), TOBN(0x25ddb65c, 0x0b27a54a),
+     TOBN(0x178279dd, 0xc590da61), TOBN(0x5479a999, 0xfbde681a),
+     TOBN(0xd0e84e05, 0x013fe162), TOBN(0xbe11dc92, 0x632d471b),
+     TOBN(0xdf0b0c45, 0xfc0e089f), TOBN(0x04fb15b0, 0x4c144025),
+     TOBN(0xa61d5fc2, 0x13c99927), TOBN(0xa033e9e0, 0x3de2eb35),
+     TOBN(0xf8185d5c, 0xb8dacbb4), TOBN(0x9a88e265, 0x8644549d),
+     TOBN(0xf717af62, 0x54671ff6), TOBN(0x4bd4241b, 0x5fa58603),
+     TOBN(0x06fba40b, 0xe67773c0), TOBN(0xc1d933d2, 0x6a2847e9),
+     TOBN(0xf4f5acf3, 0x689e2c70), TOBN(0x92aab0e7, 0x46bafd31),
+     TOBN(0x798d76aa, 0x3473f6e5), TOBN(0xcc6641db, 0x93141934),
+     TOBN(0xcae27757, 0xd31e535e), TOBN(0x04cc43b6, 0x87c2ee11),
+     TOBN(0x8d1f9675, 0x2e029ffa), TOBN(0xc2150672, 0xe4cc7a2c),
+     TOBN(0x3b03c1e0, 0x8d68b013), TOBN(0xa9d6816f, 0xedf298f3),
+     TOBN(0x1bfbb529, 0xa2804464), TOBN(0x95a52fae, 0x5db22125),
+     TOBN(0x55b32160, 0x0e1cb64e), TOBN(0x004828f6, 0x7e7fc9fe),
+     TOBN(0x13394b82, 0x1bb0fb93), TOBN(0xb6293a2d, 0x35f1a920),
+     TOBN(0xde35ef21, 0xd145d2d9), TOBN(0xbe6225b3, 0xbb8fa603),
+     TOBN(0x00fc8f6b, 0x32cf252d), TOBN(0xa28e52e6, 0x117cf8c2),
+     TOBN(0x9d1dc89b, 0x4c371e6d), TOBN(0xcebe0675, 0x36ef0f28),
+     TOBN(0x5de05d09, 0xa4292f81), TOBN(0xa8303593, 0x353e3083),
+     TOBN(0xa1715b0a, 0x7e37a9bb), TOBN(0x8c56f61e, 0x2b8faec3),
+     TOBN(0x52507431, 0x33c9b102), TOBN(0x0130cefc, 0xa44431f0),
+     TOBN(0x56039fa0, 0xbd865cfb), TOBN(0x4b03e578, 0xbc5f1dd7),
+     TOBN(0x40edf2e4, 0xbabe7224), TOBN(0xc752496d, 0x3a1988f6),
+     TOBN(0xd1572d3b, 0x564beb6b), TOBN(0x0db1d110, 0x39a1c608),
+     TOBN(0x568d1934, 0x16f60126), TOBN(0x05ae9668, 0xf354af33),
+     TOBN(0x19de6d37, 0xc92544f2), TOBN(0xcc084353, 0xa35837d5),
+     TOBN(0xcbb6869c, 0x1a514ece), TOBN(0xb633e728, 0x2e1d1066),
+     TOBN(0xf15dd69f, 0x936c581c), TOBN(0x96e7b8ce, 0x7439c4f9),
+     TOBN(0x5e676f48, 0x2e448a5b), TOBN(0xb2ca7d5b, 0xfd916bbb),
+     TOBN(0xd55a2541, 0xf5024025), TOBN(0x47bc5769, 0xe4c2d937),
+     TOBN(0x7d31b92a, 0x0362189f), TOBN(0x83f3086e, 0xef7816f9),
+     TOBN(0xf9f46d94, 0xb587579a), TOBN(0xec2d22d8, 0x30e76c5f),
+     TOBN(0x27d57461, 0xb000ffcf), TOBN(0xbb7e65f9, 0x364ffc2c),
+     TOBN(0x7c7c9477, 0x6652a220), TOBN(0x61618f89, 0xd696c981),
+     TOBN(0x5021701d, 0x89effff3), TOBN(0xf2c8ff8e, 0x7c314163),
+     TOBN(0x2da413ad, 0x8efb4d3e), TOBN(0x937b5adf, 0xce176d95),
+     TOBN(0x22867d34, 0x2a67d51c), TOBN(0x262b9b10, 0x18eb3ac9),
+     TOBN(0x4e314fe4, 0xc43ff28b), TOBN(0x76476627, 0x6a664e7a),
+     TOBN(0x3e90e40b, 0xb7a565c2), TOBN(0x8588993a, 0xc1acf831),
+     TOBN(0xd7b501d6, 0x8f938829), TOBN(0x996627ee, 0x3edd7d4c),
+     TOBN(0x37d44a62, 0x90cd34c7), TOBN(0xa8327499, 0xf3833e8d),
+     TOBN(0x2e18917d, 0x4bf50353), TOBN(0x85dd726b, 0x556765fb),
+     TOBN(0x54fe65d6, 0x93d5ab66), TOBN(0x3ddbaced, 0x915c25fe),
+     TOBN(0xa799d9a4, 0x12f22e85), TOBN(0xe2a24867, 0x6d06f6bc),
+     TOBN(0xf4f1ee56, 0x43ca1637), TOBN(0xfda2828b, 0x61ece30a),
+     TOBN(0x758c1a3e, 0xa2dee7a6), TOBN(0xdcde2f3c, 0x734b2284),
+     TOBN(0xaba445d2, 0x4eaba6ad), TOBN(0x35aaf668, 0x76cee0a7),
+     TOBN(0x7e0b04a9, 0xe5aa049a), TOBN(0xe74083ad, 0x91103e84),
+     TOBN(0xbeb183ce, 0x40afecc3), TOBN(0x6b89de9f, 0xea043f7a),}
+    ,
+    {TOBN(0x0e299d23, 0xfe67ba66), TOBN(0x91450760, 0x93cf2f34),
+     TOBN(0xf45b5ea9, 0x97fcf913), TOBN(0x5be00843, 0x8bd7ddda),
+     TOBN(0x358c3e05, 0xd53ff04d), TOBN(0xbf7ccdc3, 0x5de91ef7),
+     TOBN(0xad684dbf, 0xb69ec1a0), TOBN(0x367e7cf2, 0x801fd997),
+     TOBN(0x0ca1f3b7, 0xb0dc8595), TOBN(0x27de4608, 0x9f1d9f2e),
+     TOBN(0x1af3bf39, 0xbadd82a7), TOBN(0x79356a79, 0x65862448),
+     TOBN(0xc0602345, 0xf5f9a052), TOBN(0x1a8b0f89, 0x139a42f9),
+     TOBN(0xb53eee42, 0x844d40fc), TOBN(0x93b0bfe5, 0x4e5b6368),
+     TOBN(0x5434dd02, 0xc024789c), TOBN(0x90dca9ea, 0x41b57bfc),
+     TOBN(0x8aa898e2, 0x243398df), TOBN(0xf607c834, 0x894a94bb),
+     TOBN(0xbb07be97, 0xc2c99b76), TOBN(0x6576ba67, 0x18c29302),
+     TOBN(0x3d79efcc, 0xe703a88c), TOBN(0xf259ced7, 0xb6a0d106),
+     TOBN(0x0f893a5d, 0xc8de610b), TOBN(0xe8c515fb, 0x67e223ce),
+     TOBN(0x7774bfa6, 0x4ead6dc5), TOBN(0x89d20f95, 0x925c728f),
+     TOBN(0x7a1e0966, 0x098583ce), TOBN(0xa2eedb94, 0x93f2a7d7),
+     TOBN(0x1b282097, 0x4c304d4a), TOBN(0x0842e3da, 0xc077282d),
+     TOBN(0xe4d972a3, 0x3b9e2d7b), TOBN(0x7cc60b27, 0xc48218ff),
+     TOBN(0x8fc70838, 0x84149d91), TOBN(0x5c04346f, 0x2f461ecc),
+     TOBN(0xebe9fdf2, 0x614650a9), TOBN(0x5e35b537, 0xc1f666ac),
+     TOBN(0x645613d1, 0x88babc83), TOBN(0x88cace3a, 0xc5e1c93e),
+     TOBN(0x209ca375, 0x3de92e23), TOBN(0xccb03cc8, 0x5fbbb6e3),
+     TOBN(0xccb90f03, 0xd7b1487e), TOBN(0xfa9c2a38, 0xc710941f),
+     TOBN(0x756c3823, 0x6724ceed), TOBN(0x3a902258, 0x192d0323),
+     TOBN(0xb150e519, 0xea5e038e), TOBN(0xdcba2865, 0xc7427591),
+     TOBN(0xe549237f, 0x78890732), TOBN(0xc443bef9, 0x53fcb4d9),
+     TOBN(0x9884d8a6, 0xeb3480d6), TOBN(0x8a35b6a1, 0x3048b186),
+     TOBN(0xb4e44716, 0x65e9a90a), TOBN(0x45bf380d, 0x653006c0),
+     TOBN(0x8f3f820d, 0x4fe9ae3b), TOBN(0x244a35a0, 0x979a3b71),
+     TOBN(0xa1010e9d, 0x74cd06ff), TOBN(0x9c17c7df, 0xaca3eeac),
+     TOBN(0x74c86cd3, 0x8063aa2b), TOBN(0x8595c4b3, 0x734614ff),
+     TOBN(0xa3de00ca, 0x990f62cc), TOBN(0xd9bed213, 0xca0c3be5),
+     TOBN(0x7886078a, 0xdf8ce9f5), TOBN(0xddb27ce3, 0x5cd44444),
+     TOBN(0xed374a66, 0x58926ddd), TOBN(0x138b2d49, 0x908015b8),
+     TOBN(0x886c6579, 0xde1f7ab8), TOBN(0x888b9aa0, 0xc3020b7a),
+     TOBN(0xd3ec034e, 0x3a96e355), TOBN(0xba65b0b8, 0xf30fbe9a),
+     TOBN(0x064c8e50, 0xff21367a), TOBN(0x1f508ea4, 0x0b04b46e),
+     TOBN(0x98561a49, 0x747c866c), TOBN(0xbbb1e5fe, 0x0518a062),
+     TOBN(0x20ff4e8b, 0xecdc3608), TOBN(0x7f55cded, 0x20184027),
+     TOBN(0x8d73ec95, 0xf38c85f0), TOBN(0x5b589fdf, 0x8bc3b8c3),
+     TOBN(0xbe95dd98, 0x0f12b66f), TOBN(0xf5bd1a09, 0x0e338e01),
+     TOBN(0x65163ae5, 0x5e915918), TOBN(0x6158d6d9, 0x86f8a46b),
+     TOBN(0x8466b538, 0xeeebf99c), TOBN(0xca8761f6, 0xbca477ef),
+     TOBN(0xaf3449c2, 0x9ebbc601), TOBN(0xef3b0f41, 0xe0c3ae2f),
+     TOBN(0xaa6c577d, 0x5de63752), TOBN(0xe9166601, 0x64682a51),
+     TOBN(0x5a3097be, 0xfc15aa1e), TOBN(0x40d12548, 0xb54b0745),
+     TOBN(0x5bad4706, 0x519a5f12), TOBN(0xed03f717, 0xa439dee6),
+     TOBN(0x0794bb6c, 0x4a02c499), TOBN(0xf725083d, 0xcffe71d2),
+     TOBN(0x2cad7519, 0x0f3adcaf), TOBN(0x7f68ea1c, 0x43729310),
+     TOBN(0xe747c8c7, 0xb7ffd977), TOBN(0xec104c35, 0x80761a22),
+     TOBN(0x8395ebaf, 0x5a3ffb83), TOBN(0xfb3261f4, 0xe4b63db7),
+     TOBN(0x53544960, 0xd883e544), TOBN(0x13520d70, 0x8cc2eeb8),
+     TOBN(0x08f6337b, 0xd3d65f99), TOBN(0x83997db2, 0x781cf95b),
+     TOBN(0xce6ff106, 0x0dbd2c01), TOBN(0x4f8eea6b, 0x1f9ce934),
+     TOBN(0x546f7c4b, 0x0e993921), TOBN(0x6236a324, 0x5e753fc7),
+     TOBN(0x65a41f84, 0xa16022e9), TOBN(0x0c18d878, 0x43d1dbb2),
+     TOBN(0x73c55640, 0x2d4cef9c), TOBN(0xa0428108, 0x70444c74),
+     TOBN(0x68e4f15e, 0x9afdfb3c), TOBN(0x49a56143, 0x5bdfb6df),
+     TOBN(0xa9bc1bd4, 0x5f823d97), TOBN(0xbceb5970, 0xea111c2a),
+     TOBN(0x366b455f, 0xb269bbc4), TOBN(0x7cd85e1e, 0xe9bc5d62),
+     TOBN(0xc743c41c, 0x4f18b086), TOBN(0xa4b40990, 0x95294fb9),
+     TOBN(0x9c7c581d, 0x26ee8382), TOBN(0xcf17dcc5, 0x359d638e),
+     TOBN(0xee8273ab, 0xb728ae3d), TOBN(0x1d112926, 0xf821f047),
+     TOBN(0x11498477, 0x50491a74), TOBN(0x687fa761, 0xfde0dfb9),
+     TOBN(0x2c258022, 0x7ea435ab), TOBN(0x6b8bdb94, 0x91ce7e3f),
+     TOBN(0x4c5b5dc9, 0x3bf834aa), TOBN(0x04371819, 0x4f6c7e4b),
+     TOBN(0xc284e00a, 0x3736bcad), TOBN(0x0d881118, 0x21ae8f8d),
+     TOBN(0xf9cf0f82, 0xf48c8e33), TOBN(0xa11fd075, 0xa1bf40db),
+     TOBN(0xdceab0de, 0xdc2733e5), TOBN(0xc560a8b5, 0x8e986bd7),
+     TOBN(0x48dd1fe2, 0x3929d097), TOBN(0x3885b290, 0x92f188f1),
+     TOBN(0x0f2ae613, 0xda6fcdac), TOBN(0x9054303e, 0xb662a46c),
+     TOBN(0xb6871e44, 0x0738042a), TOBN(0x98e6a977, 0xbdaf6449),
+     TOBN(0xd8bc0650, 0xd1c9df1b), TOBN(0xef3d6451, 0x36e098f9),
+     TOBN(0x03fbae82, 0xb6d72d28), TOBN(0x77ca9db1, 0xf5d84080),
+     TOBN(0x8a112cff, 0xa58efc1c), TOBN(0x518d761c, 0xc564cb4a),
+     TOBN(0x69b5740e, 0xf0d1b5ce), TOBN(0x717039cc, 0xe9eb1785),
+     TOBN(0x3fe29f90, 0x22f53382), TOBN(0x8e54ba56, 0x6bc7c95c),
+     TOBN(0x9c806d8a, 0xf7f91d0f), TOBN(0x3b61b0f1, 0xa82a5728),
+     TOBN(0x4640032d, 0x94d76754), TOBN(0x273eb5de, 0x47d834c6),
+     TOBN(0x2988abf7, 0x7b4e4d53), TOBN(0xb7ce66bf, 0xde401777),
+     TOBN(0x9fba6b32, 0x715071b3), TOBN(0x82413c24, 0xad3a1a98),
+     TOBN(0x5b7fc8c4, 0xe0e8ad93), TOBN(0xb5679aee, 0x5fab868d),
+     TOBN(0xb1f9d2fa, 0x2b3946f3), TOBN(0x458897dc, 0x5685b50a),
+     TOBN(0x1e98c930, 0x89d0caf3), TOBN(0x39564c5f, 0x78642e92),
+     TOBN(0x1b77729a, 0x0dbdaf18), TOBN(0xf9170722, 0x579e82e6),
+     TOBN(0x680c0317, 0xe4515fa5), TOBN(0xf85cff84, 0xfb0c790f),
+     TOBN(0xc7a82aab, 0x6d2e0765), TOBN(0x7446bca9, 0x35c82b32),
+     TOBN(0x5de607aa, 0x6d63184f), TOBN(0x7c1a46a8, 0x262803a6),
+     TOBN(0xd218313d, 0xaebe8035), TOBN(0x92113ffd, 0xc73c51f8),
+     TOBN(0x4b38e083, 0x12e7e46c), TOBN(0x69d0a37a, 0x56126bd5),
+     TOBN(0xfb3f324b, 0x73c07e04), TOBN(0xa0c22f67, 0x8fda7267),
+     TOBN(0x8f2c0051, 0x4d2c7d8f), TOBN(0xbc45ced3, 0xcbe2cae5),
+     TOBN(0xe1c6cf07, 0xa8f0f277), TOBN(0xbc392312, 0x1eb99a98),
+     TOBN(0x75537b7e, 0x3cc8ac85), TOBN(0x8d725f57, 0xdd02753b),
+     TOBN(0xfd05ff64, 0xb737df2f), TOBN(0x55fe8712, 0xf6d2531d),
+     TOBN(0x57ce04a9, 0x6ab6b01c), TOBN(0x69a02a89, 0x7cd93724),
+     TOBN(0x4f82ac35, 0xcf86699b), TOBN(0x8242d3ad, 0x9cb4b232),
+     TOBN(0x713d0f65, 0xd62105e5), TOBN(0xbb222bfa, 0x2d29be61),
+     TOBN(0xf2f9a79e, 0x6cfbef09), TOBN(0xfc24d8d3, 0xd5d6782f),
+     TOBN(0x5db77085, 0xd4129967), TOBN(0xdb81c3cc, 0xdc3c2a43),
+     TOBN(0x9d655fc0, 0x05d8d9a3), TOBN(0x3f5d057a, 0x54298026),
+     TOBN(0x1157f56d, 0x88c54694), TOBN(0xb26baba5, 0x9b09573e),
+     TOBN(0x2cab03b0, 0x22adffd1), TOBN(0x60a412c8, 0xdd69f383),
+     TOBN(0xed76e98b, 0x54b25039), TOBN(0xd4ee67d3, 0x687e714d),
+     TOBN(0x87739648, 0x7b00b594), TOBN(0xce419775, 0xc9ef709b),
+     TOBN(0x40f76f85, 0x1c203a40), TOBN(0x30d352d6, 0xeafd8f91),
+     TOBN(0xaf196d3d, 0x95578dd2), TOBN(0xea4bb3d7, 0x77cc3f3d),
+     TOBN(0x42a5bd03, 0xb98e782b), TOBN(0xac958c40, 0x0624920d),
+     TOBN(0xb838134c, 0xfc56fcc8), TOBN(0x86ec4ccf, 0x89572e5e),
+     TOBN(0x69c43526, 0x9be47be0), TOBN(0x323b7dd8, 0xcb28fea1),
+     TOBN(0xfa5538ba, 0x3a6c67e5), TOBN(0xef921d70, 0x1d378e46),
+     TOBN(0xf92961fc, 0x3c4b880e), TOBN(0x3f6f914e, 0x98940a67),
+     TOBN(0xa990eb0a, 0xfef0ff39), TOBN(0xa6c2920f, 0xf0eeff9c),
+     TOBN(0xca804166, 0x51b8d9a3), TOBN(0x42531bc9, 0x0ffb0db1),
+     TOBN(0x72ce4718, 0xaa82e7ce), TOBN(0x6e199913, 0xdf574741),
+     TOBN(0xd5f1b13d, 0xd5d36946), TOBN(0x8255dc65, 0xf68f0194),
+     TOBN(0xdc9df4cd, 0x8710d230), TOBN(0x3453c20f, 0x138c1988),
+     TOBN(0x9af98dc0, 0x89a6ef01), TOBN(0x4dbcc3f0, 0x9857df85),
+     TOBN(0x34805601, 0x5c1ad924), TOBN(0x40448da5, 0xd0493046),
+     TOBN(0xf629926d, 0x4ee343e2), TOBN(0x6343f1bd, 0x90e8a301),
+     TOBN(0xefc93491, 0x40815b3f), TOBN(0xf882a423, 0xde8f66fb),
+     TOBN(0x3a12d5f4, 0xe7db9f57), TOBN(0x7dfba38a, 0x3c384c27),
+     TOBN(0x7a904bfd, 0x6fc660b1), TOBN(0xeb6c5db3, 0x2773b21c),
+     TOBN(0xc350ee66, 0x1cdfe049), TOBN(0x9baac0ce, 0x44540f29),
+     TOBN(0xbc57b6ab, 0xa5ec6aad), TOBN(0x167ce8c3, 0x0a7c1baa),
+     TOBN(0xb23a03a5, 0x53fb2b56), TOBN(0x6ce141e7, 0x4e057f78),
+     TOBN(0x796525c3, 0x89e490d9), TOBN(0x0bc95725, 0xa31a7e75),
+     TOBN(0x1ec56791, 0x1220fd06), TOBN(0x716e3a3c, 0x408b0bd6),
+     TOBN(0x31cd6bf7, 0xe8ebeba9), TOBN(0xa7326ca6, 0xbee6b670),
+     TOBN(0x3d9f851c, 0xcd090c43), TOBN(0x561e8f13, 0xf12c3988),
+     TOBN(0x50490b6a, 0x904b7be4), TOBN(0x61690ce1, 0x0410737b),
+     TOBN(0x299e9a37, 0x0f009052), TOBN(0x258758f0, 0xf026092e),
+     TOBN(0x9fa255f3, 0xfdfcdc0f), TOBN(0xdbc9fb1f, 0xc0e1bcd2),
+     TOBN(0x35f9dd6e, 0x24651840), TOBN(0xdca45a84, 0xa5c59abc),
+     TOBN(0x103d396f, 0xecca4938), TOBN(0x4532da0a, 0xb97b3f29),
+     TOBN(0xc4135ea5, 0x1999a6bf), TOBN(0x3aa9505a, 0x5e6bf2ee),
+     TOBN(0xf77cef06, 0x3f5be093), TOBN(0x97d1a0f8, 0xa943152e),
+     TOBN(0x2cb0ebba, 0x2e1c21dd), TOBN(0xf41b29fc, 0x2c6797c4),
+     TOBN(0xc6e17321, 0xb300101f), TOBN(0x4422b0e9, 0xd0d79a89),
+     TOBN(0x49e4901c, 0x92f1bfc4), TOBN(0x06ab1f8f, 0xe1e10ed9),
+     TOBN(0x84d35577, 0xdb2926b8), TOBN(0xca349d39, 0x356e8ec2),
+     TOBN(0x70b63d32, 0x343bf1a9), TOBN(0x8fd3bd28, 0x37d1a6b1),
+     TOBN(0x0454879c, 0x316865b4), TOBN(0xee959ff6, 0xc458efa2),
+     TOBN(0x0461dcf8, 0x9706dc3f), TOBN(0x737db0e2, 0x164e4b2e),
+     TOBN(0x09262680, 0x2f8843c8), TOBN(0x54498bbc, 0x7745e6f6),
+     TOBN(0x359473fa, 0xa29e24af), TOBN(0xfcc3c454, 0x70aa87a1),
+     TOBN(0xfd2c4bf5, 0x00573ace), TOBN(0xb65b514e, 0x28dd1965),
+     TOBN(0xe46ae7cf, 0x2193e393), TOBN(0x60e9a4e1, 0xf5444d97),
+     TOBN(0xe7594e96, 0x00ff38ed), TOBN(0x43d84d2f, 0x0a0e0f02),
+     TOBN(0x8b6db141, 0xee398a21), TOBN(0xb88a56ae, 0xe3bcc5be),
+     TOBN(0x0a1aa52f, 0x373460ea), TOBN(0x20da1a56, 0x160bb19b),
+     TOBN(0xfb54999d, 0x65bf0384), TOBN(0x71a14d24, 0x5d5a180e),
+     TOBN(0xbc44db7b, 0x21737b04), TOBN(0xd84fcb18, 0x01dd8e92),
+     TOBN(0x80de937b, 0xfa44b479), TOBN(0x53505499, 0x5c98fd4f),
+     TOBN(0x1edb12ab, 0x28f08727), TOBN(0x4c58b582, 0xa5f3ef53),
+     TOBN(0xbfb236d8, 0x8327f246), TOBN(0xc3a3bfaa, 0x4d7df320),
+     TOBN(0xecd96c59, 0xb96024f2), TOBN(0xfc293a53, 0x7f4e0433),
+     TOBN(0x5341352b, 0x5acf6e10), TOBN(0xc50343fd, 0xafe652c3),
+     TOBN(0x4af3792d, 0x18577a7f), TOBN(0xe1a4c617, 0xaf16823d),
+     TOBN(0x9b26d0cd, 0x33425d0a), TOBN(0x306399ed, 0x9b7bc47f),
+     TOBN(0x2a792f33, 0x706bb20b), TOBN(0x31219614, 0x98111055),
+     TOBN(0x864ec064, 0x87f5d28b), TOBN(0x11392d91, 0x962277fd),
+     TOBN(0xb5aa7942, 0xbb6aed5f), TOBN(0x080094dc, 0x47e799d9),
+     TOBN(0x4afa588c, 0x208ba19b), TOBN(0xd3e7570f, 0x8512f284),
+     TOBN(0xcbae64e6, 0x02f5799a), TOBN(0xdeebe7ef, 0x514b9492),
+     TOBN(0x30300f98, 0xe5c298ff), TOBN(0x17f561be, 0x3678361f),
+     TOBN(0xf52ff312, 0x98cb9a16), TOBN(0x6233c3bc, 0x5562d490),
+     TOBN(0x7bfa15a1, 0x92e3a2cb), TOBN(0x961bcfd1, 0xe6365119),
+     TOBN(0x3bdd29bf, 0x2c8c53b1), TOBN(0x739704df, 0x822844ba),
+     TOBN(0x7dacfb58, 0x7e7b754b), TOBN(0x23360791, 0xa806c9b9),
+     TOBN(0xe7eb88c9, 0x23504452), TOBN(0x2983e996, 0x852c1783),
+     TOBN(0xdd4ae529, 0x958d881d), TOBN(0x026bae03, 0x262c7b3c),
+     TOBN(0x3a6f9193, 0x960b52d1), TOBN(0xd0980f90, 0x92696cfb),
+     TOBN(0x4c1f428c, 0xd5f30851), TOBN(0x94dfed27, 0x2a4f6630),
+     TOBN(0x4df53772, 0xfc5d48a4), TOBN(0xdd2d5a2f, 0x933260ce),
+     TOBN(0x574115bd, 0xd44cc7a5), TOBN(0x4ba6b20d, 0xbd12533a),
+     TOBN(0x30e93cb8, 0x243057c9), TOBN(0x794c486a, 0x14de320e),
+     TOBN(0xe925d4ce, 0xf21496e4), TOBN(0xf951d198, 0xec696331),
+     TOBN(0x9810e2de, 0x3e8d812f), TOBN(0xd0a47259, 0x389294ab),
+     TOBN(0x513ba2b5, 0x0e3bab66), TOBN(0x462caff5, 0xabad306f),
+     TOBN(0xe2dc6d59, 0xaf04c49e), TOBN(0x1aeb8750, 0xe0b84b0b),
+     TOBN(0xc034f12f, 0x2f7d0ca2), TOBN(0x6d2e8128, 0xe06acf2f),
+     TOBN(0x801f4f83, 0x21facc2f), TOBN(0xa1170c03, 0xf40ef607),
+     TOBN(0xfe0a1d4f, 0x7805a99c), TOBN(0xbde56a36, 0xcc26aba5),
+     TOBN(0x5b1629d0, 0x35531f40), TOBN(0xac212c2b, 0x9afa6108),
+     TOBN(0x30a06bf3, 0x15697be5), TOBN(0x6f0545dc, 0x2c63c7c1),
+     TOBN(0x5d8cb842, 0x7ccdadaf), TOBN(0xd52e379b, 0xac7015bb),
+     TOBN(0xc4f56147, 0xf462c23e), TOBN(0xd44a4298, 0x46bc24b0),
+     TOBN(0xbc73d23a, 0xe2856d4f), TOBN(0x61cedd8c, 0x0832bcdf),
+     TOBN(0x60953556, 0x99f241d7), TOBN(0xee4adbd7, 0x001a349d),
+     TOBN(0x0b35bf6a, 0xaa89e491), TOBN(0x7f0076f4, 0x136f7546),
+     TOBN(0xd19a18ba, 0x9264da3d), TOBN(0x6eb2d2cd, 0x62a7a28b),
+     TOBN(0xcdba941f, 0x8761c971), TOBN(0x1550518b, 0xa3be4a5d),
+     TOBN(0xd0e8e2f0, 0x57d0b70c), TOBN(0xeea8612e, 0xcd133ba3),
+     TOBN(0x814670f0, 0x44416aec), TOBN(0x424db6c3, 0x30775061),
+     TOBN(0xd96039d1, 0x16213fd1), TOBN(0xc61e7fa5, 0x18a3478f),
+     TOBN(0xa805bdcc, 0xcb0c5021), TOBN(0xbdd6f3a8, 0x0cc616dd),
+     TOBN(0x06009667, 0x5d97f7e2), TOBN(0x31db0fc1, 0xaf0bf4b6),
+     TOBN(0x23680ed4, 0x5491627a), TOBN(0xb99a3c66, 0x7d741fb1),
+     TOBN(0xe9bb5f55, 0x36b1ff92), TOBN(0x29738577, 0x512b388d),
+     TOBN(0xdb8a2ce7, 0x50fcf263), TOBN(0x385346d4, 0x6c4f7b47),
+     TOBN(0xbe86c5ef, 0x31631f9e), TOBN(0xbf91da21, 0x03a57a29),
+     TOBN(0xc3b1f796, 0x7b23f821), TOBN(0x0f7d00d2, 0x770db354),
+     TOBN(0x8ffc6c3b, 0xd8fe79da), TOBN(0xcc5e8c40, 0xd525c996),
+     TOBN(0x4640991d, 0xcfff632a), TOBN(0x64d97e8c, 0x67112528),
+     TOBN(0xc232d973, 0x02f1cd1e), TOBN(0xce87eacb, 0x1dd212a4),
+     TOBN(0x6e4c8c73, 0xe69802f7), TOBN(0x12ef0290, 0x1fffddbd),
+     TOBN(0x941ec74e, 0x1bcea6e2), TOBN(0xd0b54024, 0x3cb92cbb),
+     TOBN(0x809fb9d4, 0x7e8f9d05), TOBN(0x3bf16159, 0xf2992aae),
+     TOBN(0xad40f279, 0xf8a7a838), TOBN(0x11aea631, 0x05615660),
+     TOBN(0xbf52e6f1, 0xa01f6fa1), TOBN(0xef046995, 0x3dc2aec9),
+     TOBN(0x785dbec9, 0xd8080711), TOBN(0xe1aec60a, 0x9fdedf76),
+     TOBN(0xece797b5, 0xfa21c126), TOBN(0xc66e898f, 0x05e52732),
+     TOBN(0x39bb69c4, 0x08811fdb), TOBN(0x8bfe1ef8, 0x2fc7f082),
+     TOBN(0xc8e7a393, 0x174f4138), TOBN(0xfba8ad1d, 0xd58d1f98),
+     TOBN(0xbc21d0ce, 0xbfd2fd5b), TOBN(0x0b839a82, 0x6ee60d61),
+     TOBN(0xaacf7658, 0xafd22253), TOBN(0xb526bed8, 0xaae396b3),
+     TOBN(0xccc1bbc2, 0x38564464), TOBN(0x9e3ff947, 0x8c45bc73),
+     TOBN(0xcde9bca3, 0x58188a78), TOBN(0x138b8ee0, 0xd73bf8f7),
+     TOBN(0x5c7e234c, 0x4123c489), TOBN(0x66e69368, 0xfa643297),
+     TOBN(0x0629eeee, 0x39a15fa3), TOBN(0x95fab881, 0xa9e2a927),
+     TOBN(0xb2497007, 0xeafbb1e1), TOBN(0xd75c9ce6, 0xe75b7a93),
+     TOBN(0x3558352d, 0xefb68d78), TOBN(0xa2f26699, 0x223f6396),
+     TOBN(0xeb911ecf, 0xe469b17a), TOBN(0x62545779, 0xe72d3ec2),
+     TOBN(0x8ea47de7, 0x82cb113f), TOBN(0xebe4b086, 0x4e1fa98d),
+     TOBN(0xec2d5ed7, 0x8cdfedb1), TOBN(0xa535c077, 0xfe211a74),
+     TOBN(0x9678109b, 0x11d244c5), TOBN(0xf17c8bfb, 0xbe299a76),
+     TOBN(0xb651412e, 0xfb11fbc4), TOBN(0xea0b5482, 0x94ab3f65),
+     TOBN(0xd8dffd95, 0x0cf78243), TOBN(0x2e719e57, 0xce0361d4),
+     TOBN(0x9007f085, 0x304ddc5b), TOBN(0x095e8c6d, 0x4daba2ea),
+     TOBN(0x5a33cdb4, 0x3f9d28a9), TOBN(0x85b95cd8, 0xe2283003),
+     TOBN(0xbcd6c819, 0xb9744733), TOBN(0x29c5f538, 0xfc7f5783),
+     TOBN(0x6c49b2fa, 0xd59038e4), TOBN(0x68349cc1, 0x3bbe1018),
+     TOBN(0xcc490c1d, 0x21830ee5), TOBN(0x36f9c4ee, 0xe9bfa297),
+     TOBN(0x58fd7294, 0x48de1a94), TOBN(0xaadb13a8, 0x4e8f2cdc),
+     TOBN(0x515eaaa0, 0x81313dba), TOBN(0xc76bb468, 0xc2152dd8),
+     TOBN(0x357f8d75, 0xa653dbf8), TOBN(0xe4d8c4d1, 0xb14ac143),
+     TOBN(0xbdb8e675, 0xb055cb40), TOBN(0x898f8e7b, 0x977b5167),
+     TOBN(0xecc65651, 0xb82fb863), TOBN(0x56544814, 0x6d88f01f),
+     TOBN(0xb0928e95, 0x263a75a9), TOBN(0xcfb6836f, 0x1a22fcda),
+     TOBN(0x651d14db, 0x3f3bd37c), TOBN(0x1d3837fb, 0xb6ad4664),
+     TOBN(0x7c5fb538, 0xff4f94ab), TOBN(0x7243c712, 0x6d7fb8f2),
+     TOBN(0xef13d60c, 0xa85c5287), TOBN(0x18cfb7c7, 0x4bb8dd1b),
+     TOBN(0x82f9bfe6, 0x72908219), TOBN(0x35c4592b, 0x9d5144ab),
+     TOBN(0x52734f37, 0x9cf4b42f), TOBN(0x6bac55e7, 0x8c60ddc4),
+     TOBN(0xb5cd811e, 0x94dea0f6), TOBN(0x259ecae4, 0xe18cc1a3),
+     TOBN(0x6a0e836e, 0x15e660f8), TOBN(0x6c639ea6, 0x0e02bff2),
+     TOBN(0x8721b8cb, 0x7e1026fd), TOBN(0x9e73b50b, 0x63261942),
+     TOBN(0xb8c70974, 0x77f01da3), TOBN(0x1839e6a6, 0x8268f57f),
+     TOBN(0x571b9415, 0x5150b805), TOBN(0x1892389e, 0xf92c7097),
+     TOBN(0x8d69c18e, 0x4a084b95), TOBN(0x7014c512, 0xbe5b495c),
+     TOBN(0x4780db36, 0x1b07523c), TOBN(0x2f6219ce, 0x2c1c64fa),
+     TOBN(0xc38b81b0, 0x602c105a), TOBN(0xab4f4f20, 0x5dc8e360),
+     TOBN(0x20d3c982, 0xcf7d62d2), TOBN(0x1f36e29d, 0x23ba8150),
+     TOBN(0x48ae0bf0, 0x92763f9e), TOBN(0x7a527e6b, 0x1d3a7007),
+     TOBN(0xb4a89097, 0x581a85e3), TOBN(0x1f1a520f, 0xdc158be5),
+     TOBN(0xf98db37d, 0x167d726e), TOBN(0x8802786e, 0x1113e862)}
+    ,
+    {TOBN(0xefb2149e, 0x36f09ab0), TOBN(0x03f163ca, 0x4a10bb5b),
+     TOBN(0xd0297045, 0x06e20998), TOBN(0x56f0af00, 0x1b5a3bab),
+     TOBN(0x7af4cfec, 0x70880e0d), TOBN(0x7332a66f, 0xbe3d913f),
+     TOBN(0x32e6c84a, 0x7eceb4bd), TOBN(0xedc4a79a, 0x9c228f55),
+     TOBN(0xc37c7dd0, 0xc55c4496), TOBN(0xa6a96357, 0x25bbabd2),
+     TOBN(0x5b7e63f2, 0xadd7f363), TOBN(0x9dce3782, 0x2e73f1df),
+     TOBN(0xe1e5a16a, 0xb2b91f71), TOBN(0xe4489823, 0x5ba0163c),
+     TOBN(0xf2759c32, 0xf6e515ad), TOBN(0xa5e2f1f8, 0x8615eecf),
+     TOBN(0x74519be7, 0xabded551), TOBN(0x03d358b8, 0xc8b74410),
+     TOBN(0x4d00b10b, 0x0e10d9a9), TOBN(0x6392b0b1, 0x28da52b7),
+     TOBN(0x6744a298, 0x0b75c904), TOBN(0xc305b0ae, 0xa8f7f96c),
+     TOBN(0x042e421d, 0x182cf932), TOBN(0xf6fc5d50, 0x9e4636ca),
+     TOBN(0x795847c9, 0xd64cc78c), TOBN(0x6c50621b, 0x9b6cb27b),
+     TOBN(0x07099bf8, 0xdf8022ab), TOBN(0x48f862eb, 0xc04eda1d),
+     TOBN(0xd12732ed, 0xe1603c16), TOBN(0x19a80e0f, 0x5c9a9450),
+     TOBN(0xe2257f54, 0xb429b4fc), TOBN(0x66d3b2c6, 0x45460515),
+     TOBN(0x6ca4f87e, 0x822e37be), TOBN(0x73f237b4, 0x253bda4e),
+     TOBN(0xf747f3a2, 0x41190aeb), TOBN(0xf06fa36f, 0x804cf284),
+     TOBN(0x0a6bbb6e, 0xfc621c12), TOBN(0x5d624b64, 0x40b80ec6),
+     TOBN(0x4b072425, 0x7ba556f3), TOBN(0x7fa0c354, 0x3e2d20a8),
+     TOBN(0xe921fa31, 0xe3229d41), TOBN(0xa929c652, 0x94531bd4),
+     TOBN(0x84156027, 0xa6d38209), TOBN(0xf3d69f73, 0x6bdb97bd),
+     TOBN(0x8906d19a, 0x16833631), TOBN(0x68a34c2e, 0x03d51be3),
+     TOBN(0xcb59583b, 0x0e511cd8), TOBN(0x99ce6bfd, 0xfdc132a8),
+     TOBN(0x3facdaaa, 0xffcdb463), TOBN(0x658bbc1a, 0x34a38b08),
+     TOBN(0x12a801f8, 0xf1a9078d), TOBN(0x1567bcf9, 0x6ab855de),
+     TOBN(0xe08498e0, 0x3572359b), TOBN(0xcf0353e5, 0x8659e68b),
+     TOBN(0xbb86e9c8, 0x7d23807c), TOBN(0xbc08728d, 0x2198e8a2),
+     TOBN(0x8de2b7bc, 0x453cadd6), TOBN(0x203900a7, 0xbc0bc1f8),
+     TOBN(0xbcd86e47, 0xa6abd3af), TOBN(0x911cac12, 0x8502effb),
+     TOBN(0x2d550242, 0xec965469), TOBN(0x0e9f7692, 0x29e0017e),
+     TOBN(0x633f078f, 0x65979885), TOBN(0xfb87d449, 0x4cf751ef),
+     TOBN(0xe1790e4b, 0xfc25419a), TOBN(0x36467203, 0x4bff3cfd),
+     TOBN(0xc8db6386, 0x25b6e83f), TOBN(0x6cc69f23, 0x6cad6fd2),
+     TOBN(0x0219e45a, 0x6bc68bb9), TOBN(0xe43d79b6, 0x297f7334),
+     TOBN(0x7d445368, 0x465dc97c), TOBN(0x4b9eea32, 0x2a0b949a),
+     TOBN(0x1b96c6ba, 0x6102d021), TOBN(0xeaafac78, 0x2f4461ea),
+     TOBN(0xd4b85c41, 0xc49f19a8), TOBN(0x275c28e4, 0xcf538875),
+     TOBN(0x35451a9d, 0xdd2e54e0), TOBN(0x6991adb5, 0x0605618b),
+     TOBN(0x5b8b4bcd, 0x7b36cd24), TOBN(0x372a4f8c, 0x56f37216),
+     TOBN(0xc890bd73, 0xa6a5da60), TOBN(0x6f083da0, 0xdc4c9ff0),
+     TOBN(0xf4e14d94, 0xf0536e57), TOBN(0xf9ee1eda, 0xaaec8243),
+     TOBN(0x571241ec, 0x8bdcf8e7), TOBN(0xa5db8271, 0x0b041e26),
+     TOBN(0x9a0b9a99, 0xe3fff040), TOBN(0xcaaf21dd, 0x7c271202),
+     TOBN(0xb4e2b2e1, 0x4f0dd2e8), TOBN(0xe77e7c4f, 0x0a377ac7),
+     TOBN(0x69202c3f, 0x0d7a2198), TOBN(0xf759b7ff, 0x28200eb8),
+     TOBN(0xc87526ed, 0xdcfe314e), TOBN(0xeb84c524, 0x53d5cf99),
+     TOBN(0xb1b52ace, 0x515138b6), TOBN(0x5aa7ff8c, 0x23fca3f4),
+     TOBN(0xff0b13c3, 0xb9791a26), TOBN(0x960022da, 0xcdd58b16),
+     TOBN(0xdbd55c92, 0x57aad2de), TOBN(0x3baaaaa3, 0xf30fe619),
+     TOBN(0x9a4b2346, 0x0d881efd), TOBN(0x506416c0, 0x46325e2a),
+     TOBN(0x91381e76, 0x035c18d4), TOBN(0xb3bb68be, 0xf27817b0),
+     TOBN(0x15bfb8bf, 0x5116f937), TOBN(0x7c64a586, 0xc1268943),
+     TOBN(0x71e25cc3, 0x8419a2c8), TOBN(0x9fd6b0c4, 0x8335f463),
+     TOBN(0x4bf0ba3c, 0xe8ee0e0e), TOBN(0x6f6fba60, 0x298c21fa),
+     TOBN(0x57d57b39, 0xae66bee0), TOBN(0x292d5130, 0x22672544),
+     TOBN(0xf451105d, 0xbab093b3), TOBN(0x012f59b9, 0x02839986),
+     TOBN(0x8a915802, 0x3474a89c), TOBN(0x048c919c, 0x2de03e97),
+     TOBN(0xc476a2b5, 0x91071cd5), TOBN(0x791ed89a, 0x034970a5),
+     TOBN(0x89bd9042, 0xe1b7994b), TOBN(0x8eaf5179, 0xa1057ffd),
+     TOBN(0x6066e2a2, 0xd551ee10), TOBN(0x87a8f1d8, 0x727e09a6),
+     TOBN(0x00d08bab, 0x2c01148d), TOBN(0x6da8e4f1, 0x424f33fe),
+     TOBN(0x466d17f0, 0xcf9a4e71), TOBN(0xff502010, 0x3bf5cb19),
+     TOBN(0xdccf97d8, 0xd062ecc0), TOBN(0x80c0d9af, 0x81d80ac4),
+     TOBN(0xe87771d8, 0x033f2876), TOBN(0xb0186ec6, 0x7d5cc3db),
+     TOBN(0x58e8bb80, 0x3bc9bc1d), TOBN(0x4d1395cc, 0x6f6ef60e),
+     TOBN(0xa73c62d6, 0x186244a0), TOBN(0x918e5f23, 0x110a5b53),
+     TOBN(0xed4878ca, 0x741b7eab), TOBN(0x3038d71a, 0xdbe03e51),
+     TOBN(0x840204b7, 0xa93c3246), TOBN(0x21ab6069, 0xa0b9b4cd),
+     TOBN(0xf5fa6e2b, 0xb1d64218), TOBN(0x1de6ad0e, 0xf3d56191),
+     TOBN(0x570aaa88, 0xff1929c7), TOBN(0xc6df4c6b, 0x640e87b5),
+     TOBN(0xde8a74f2, 0xc65f0ccc), TOBN(0x8b972fd5, 0xe6f6cc01),
+     TOBN(0x3fff36b6, 0x0b846531), TOBN(0xba7e45e6, 0x10a5e475),
+     TOBN(0x84a1d10e, 0x4145b6c5), TOBN(0xf1f7f91a, 0x5e046d9d),
+     TOBN(0x0317a692, 0x44de90d7), TOBN(0x951a1d4a, 0xf199c15e),
+     TOBN(0x91f78046, 0xc9d73deb), TOBN(0x74c82828, 0xfab8224f),
+     TOBN(0xaa6778fc, 0xe7560b90), TOBN(0xb4073e61, 0xa7e824ce),
+     TOBN(0xff0d693c, 0xd642eba8), TOBN(0x7ce2e57a, 0x5dccef38),
+     TOBN(0x89c2c789, 0x1df1ad46), TOBN(0x83a06922, 0x098346fd),
+     TOBN(0x2d715d72, 0xda2fc177), TOBN(0x7b6dd71d, 0x85b6cf1d),
+     TOBN(0xc60a6d0a, 0x73fa9cb0), TOBN(0xedd3992e, 0x328bf5a9),
+     TOBN(0xc380ddd0, 0x832c8c82), TOBN(0xd182d410, 0xa2a0bf50),
+     TOBN(0x7d9d7438, 0xd9a528db), TOBN(0xe8b1a0e9, 0xcaf53994),
+     TOBN(0xddd6e5fe, 0x0e19987c), TOBN(0xacb8df03, 0x190b059d),
+     TOBN(0x53703a32, 0x8300129f), TOBN(0x1f637662, 0x68c43bfd),
+     TOBN(0xbcbd1913, 0x00e54051), TOBN(0x812fcc62, 0x7bf5a8c5),
+     TOBN(0x3f969d5f, 0x29fb85da), TOBN(0x72f4e00a, 0x694759e8),
+     TOBN(0x426b6e52, 0x790726b7), TOBN(0x617bbc87, 0x3bdbb209),
+     TOBN(0x511f8bb9, 0x97aee317), TOBN(0x812a4096, 0xe81536a8),
+     TOBN(0x137dfe59, 0x3ac09b9b), TOBN(0x0682238f, 0xba8c9a7a),
+     TOBN(0x7072ead6, 0xaeccb4bd), TOBN(0x6a34e9aa, 0x692ba633),
+     TOBN(0xc82eaec2, 0x6fff9d33), TOBN(0xfb753512, 0x1d4d2b62),
+     TOBN(0x1a0445ff, 0x1d7aadab), TOBN(0x65d38260, 0xd5f6a67c),
+     TOBN(0x6e62fb08, 0x91cfb26f), TOBN(0xef1e0fa5, 0x5c7d91d6),
+     TOBN(0x47e7c7ba, 0x33db72cd), TOBN(0x017cbc09, 0xfa7c74b2),
+     TOBN(0x3c931590, 0xf50a503c), TOBN(0xcac54f60, 0x616baa42),
+     TOBN(0x9b6cd380, 0xb2369f0f), TOBN(0x97d3a70d, 0x23c76151),
+     TOBN(0x5f9dd6fc, 0x9862a9c6), TOBN(0x044c4ab2, 0x12312f51),
+     TOBN(0x035ea0fd, 0x834a2ddc), TOBN(0x49e6b862, 0xcc7b826d),
+     TOBN(0xb03d6883, 0x62fce490), TOBN(0x62f2497a, 0xb37e36e9),
+     TOBN(0x04b005b6, 0xc6458293), TOBN(0x36bb5276, 0xe8d10af7),
+     TOBN(0xacf2dc13, 0x8ee617b8), TOBN(0x470d2d35, 0xb004b3d4),
+     TOBN(0x06790832, 0xfeeb1b77), TOBN(0x2bb75c39, 0x85657f9c),
+     TOBN(0xd70bd4ed, 0xc0f60004), TOBN(0xfe797ecc, 0x219b018b),
+     TOBN(0x9b5bec2a, 0x753aebcc), TOBN(0xdaf9f3dc, 0xc939eca5),
+     TOBN(0xd6bc6833, 0xd095ad09), TOBN(0x98abdd51, 0xdaa4d2fc),
+     TOBN(0xd9840a31, 0x8d168be5), TOBN(0xcf7c10e0, 0x2325a23c),
+     TOBN(0xa5c02aa0, 0x7e6ecfaf), TOBN(0x2462e7e6, 0xb5bfdf18),
+     TOBN(0xab2d8a8b, 0xa0cc3f12), TOBN(0x68dd485d, 0xbc672a29),
+     TOBN(0x72039752, 0x596f2cd3), TOBN(0x5d3eea67, 0xa0cf3d8d),
+     TOBN(0x810a1a81, 0xe6602671), TOBN(0x8f144a40, 0x14026c0c),
+     TOBN(0xbc753a6d, 0x76b50f85), TOBN(0xc4dc21e8, 0x645cd4a4),
+     TOBN(0xc5262dea, 0x521d0378), TOBN(0x802b8e0e, 0x05011c6f),
+     TOBN(0x1ba19cbb, 0x0b4c19ea), TOBN(0x21db64b5, 0xebf0aaec),
+     TOBN(0x1f394ee9, 0x70342f9d), TOBN(0x93a10aee, 0x1bc44a14),
+     TOBN(0xa7eed31b, 0x3efd0baa), TOBN(0x6e7c824e, 0x1d154e65),
+     TOBN(0xee23fa81, 0x9966e7ee), TOBN(0x64ec4aa8, 0x05b7920d),
+     TOBN(0x2d44462d, 0x2d90aad4), TOBN(0xf44dd195, 0xdf277ad5),
+     TOBN(0x8d6471f1, 0xbb46b6a1), TOBN(0x1e65d313, 0xfd885090),
+     TOBN(0x33a800f5, 0x13a977b4), TOBN(0xaca9d721, 0x0797e1ef),
+     TOBN(0x9a5a85a0, 0xfcff6a17), TOBN(0x9970a3f3, 0x1eca7cee),
+     TOBN(0xbb9f0d6b, 0xc9504be3), TOBN(0xe0c504be, 0xadd24ee2),
+     TOBN(0x7e09d956, 0x77fcc2f4), TOBN(0xef1a5227, 0x65bb5fc4),
+     TOBN(0x145d4fb1, 0x8b9286aa), TOBN(0x66fd0c5d, 0x6649028b),
+     TOBN(0x98857ceb, 0x1bf4581c), TOBN(0xe635e186, 0xaca7b166),
+     TOBN(0x278ddd22, 0x659722ac), TOBN(0xa0903c4c, 0x1db68007),
+     TOBN(0x366e4589, 0x48f21402), TOBN(0x31b49c14, 0xb96abda2),
+     TOBN(0x329c4b09, 0xe0403190), TOBN(0x97197ca3, 0xd29f43fe),
+     TOBN(0x8073dd1e, 0x274983d8), TOBN(0xda1a3bde, 0x55717c8f),
+     TOBN(0xfd3d4da2, 0x0361f9d1), TOBN(0x1332d081, 0x4c7de1ce),
+     TOBN(0x9b7ef7a3, 0xaa6d0e10), TOBN(0x17db2e73, 0xf54f1c4a),
+     TOBN(0xaf3dffae, 0x4cd35567), TOBN(0xaaa2f406, 0xe56f4e71),
+     TOBN(0x8966759e, 0x7ace3fc7), TOBN(0x9594eacf, 0x45a8d8c6),
+     TOBN(0x8de3bd8b, 0x91834e0e), TOBN(0xafe4ca53, 0x548c0421),
+     TOBN(0xfdd7e856, 0xe6ee81c6), TOBN(0x8f671beb, 0x6b891a3a),
+     TOBN(0xf7a58f2b, 0xfae63829), TOBN(0x9ab186fb, 0x9c11ac9f),
+     TOBN(0x8d6eb369, 0x10b5be76), TOBN(0x046b7739, 0xfb040bcd),
+     TOBN(0xccb4529f, 0xcb73de88), TOBN(0x1df0fefc, 0xcf26be03),
+     TOBN(0xad7757a6, 0xbcfcd027), TOBN(0xa8786c75, 0xbb3165ca),
+     TOBN(0xe9db1e34, 0x7e99a4d9), TOBN(0x99ee86df, 0xb06c504b),
+     TOBN(0x5b7c2ddd, 0xc15c9f0a), TOBN(0xdf87a734, 0x4295989e),
+     TOBN(0x59ece47c, 0x03d08fda), TOBN(0xb074d3dd, 0xad5fc702),
+     TOBN(0x20407903, 0x51a03776), TOBN(0x2bb1f77b, 0x2a608007),
+     TOBN(0x25c58f4f, 0xe1153185), TOBN(0xe6df62f6, 0x766e6447),
+     TOBN(0xefb3d1be, 0xed51275a), TOBN(0x5de47dc7, 0x2f0f483f),
+     TOBN(0x7932d98e, 0x97c2bedf), TOBN(0xd5c11927, 0x0219f8a1),
+     TOBN(0x9d751200, 0xa73a294e), TOBN(0x5f88434a, 0x9dc20172),
+     TOBN(0xd28d9fd3, 0xa26f506a), TOBN(0xa890cd31, 0x9d1dcd48),
+     TOBN(0x0aebaec1, 0x70f4d3b4), TOBN(0xfd1a1369, 0x0ffc8d00),
+     TOBN(0xb9d9c240, 0x57d57838), TOBN(0x45929d26, 0x68bac361),
+     TOBN(0x5a2cd060, 0x25b15ca6), TOBN(0x4b3c83e1, 0x6e474446),
+     TOBN(0x1aac7578, 0xee1e5134), TOBN(0xa418f5d6, 0xc91e2f41),
+     TOBN(0x6936fc8a, 0x213ed68b), TOBN(0x860ae7ed, 0x510a5224),
+     TOBN(0x63660335, 0xdef09b53), TOBN(0x641b2897, 0xcd79c98d),
+     TOBN(0x29bd38e1, 0x01110f35), TOBN(0x79c26f42, 0x648b1937),
+     TOBN(0x64dae519, 0x9d9164f4), TOBN(0xd85a2310, 0x0265c273),
+     TOBN(0x7173dd5d, 0x4b07e2b1), TOBN(0xd144c4cb, 0x8d9ea221),
+     TOBN(0xe8b04ea4, 0x1105ab14), TOBN(0x92dda542, 0xfe80d8f1),
+     TOBN(0xe9982fa8, 0xcf03dce6), TOBN(0x8b5ea965, 0x1a22cffc),
+     TOBN(0xf7f4ea7f, 0x3fad88c4), TOBN(0x62db773e, 0x6a5ba95c),
+     TOBN(0xd20f02fb, 0x93f24567), TOBN(0xfd46c69a, 0x315257ca),
+     TOBN(0x0ac74cc7, 0x8bcab987), TOBN(0x46f31c01, 0x5ceca2f5),
+     TOBN(0x40aedb59, 0x888b219e), TOBN(0xe50ecc37, 0xe1fccd02),
+     TOBN(0x1bcd9dad, 0x911f816c), TOBN(0x583cc1ec, 0x8db9b00c),
+     TOBN(0xf3cd2e66, 0xa483bf11), TOBN(0xfa08a6f5, 0xb1b2c169),
+     TOBN(0xf375e245, 0x4be9fa28), TOBN(0x99a7ffec, 0x5b6d011f),
+     TOBN(0x6a3ebddb, 0xc4ae62da), TOBN(0x6cea00ae, 0x374aef5d),
+     TOBN(0xab5fb98d, 0x9d4d05bc), TOBN(0x7cba1423, 0xd560f252),
+     TOBN(0x49b2cc21, 0x208490de), TOBN(0x1ca66ec3, 0xbcfb2879),
+     TOBN(0x7f1166b7, 0x1b6fb16f), TOBN(0xfff63e08, 0x65fe5db3),
+     TOBN(0xb8345abe, 0x8b2610be), TOBN(0xb732ed80, 0x39de3df4),
+     TOBN(0x0e24ed50, 0x211c32b4), TOBN(0xd10d8a69, 0x848ff27d),
+     TOBN(0xc1074398, 0xed4de248), TOBN(0xd7cedace, 0x10488927),
+     TOBN(0xa4aa6bf8, 0x85673e13), TOBN(0xb46bae91, 0x6daf30af),
+     TOBN(0x07088472, 0xfcef7ad8), TOBN(0x61151608, 0xd4b35e97),
+     TOBN(0xbcfe8f26, 0xdde29986), TOBN(0xeb84c4c7, 0xd5a34c79),
+     TOBN(0xc1eec55c, 0x164e1214), TOBN(0x891be86d, 0xa147bb03),
+     TOBN(0x9fab4d10, 0x0ba96835), TOBN(0xbf01e9b8, 0xa5c1ae9f),
+     TOBN(0x6b4de139, 0xb186ebc0), TOBN(0xd5c74c26, 0x85b91bca),
+     TOBN(0x5086a99c, 0xc2d93854), TOBN(0xeed62a7b, 0xa7a9dfbc),
+     TOBN(0x8778ed6f, 0x76b7618a), TOBN(0xbff750a5, 0x03b66062),
+     TOBN(0x4cb7be22, 0xb65186db), TOBN(0x369dfbf0, 0xcc3a6d13),
+     TOBN(0xc7dab26c, 0x7191a321), TOBN(0x9edac3f9, 0x40ed718e),
+     TOBN(0xbc142b36, 0xd0cfd183), TOBN(0xc8af82f6, 0x7c991693),
+     TOBN(0xb3d1e4d8, 0x97ce0b2a), TOBN(0xe6d7c87f, 0xc3a55cdf),
+     TOBN(0x35846b95, 0x68b81afe), TOBN(0x018d12af, 0xd3c239d8),
+     TOBN(0x2b2c6208, 0x01206e15), TOBN(0xe0e42453, 0xa3b882c6),
+     TOBN(0x854470a3, 0xa50162d5), TOBN(0x08157478, 0x7017a62a),
+     TOBN(0x18bd3fb4, 0x820357c7), TOBN(0x992039ae, 0x6f1458ad),
+     TOBN(0x9a1df3c5, 0x25b44aa1), TOBN(0x2d780357, 0xed3d5281),
+     TOBN(0x58cf7e4d, 0xc77ad4d4), TOBN(0xd49a7998, 0xf9df4fc4),
+     TOBN(0x4465a8b5, 0x1d71205e), TOBN(0xa0ee0ea6, 0x649254aa),
+     TOBN(0x4b5eeecf, 0xab7bd771), TOBN(0x6c873073, 0x35c262b9),
+     TOBN(0xdc5bd648, 0x3c9d61e7), TOBN(0x233d6d54, 0x321460d2),
+     TOBN(0xd20c5626, 0xfc195bcc), TOBN(0x25445958, 0x04d78b63),
+     TOBN(0xe03fcb3d, 0x17ec8ef3), TOBN(0x54b690d1, 0x46b8f781),
+     TOBN(0x82fa2c8a, 0x21230646), TOBN(0xf51aabb9, 0x084f418c),
+     TOBN(0xff4fbec1, 0x1a30ba43), TOBN(0x6a5acf73, 0x743c9df7),
+     TOBN(0x1da2b357, 0xd635b4d5), TOBN(0xc3de68dd, 0xecd5c1da),
+     TOBN(0xa689080b, 0xd61af0dd), TOBN(0xdea5938a, 0xd665bf99),
+     TOBN(0x0231d71a, 0xfe637294), TOBN(0x01968aa6, 0xa5a81cd8),
+     TOBN(0x11252d50, 0x048e63b5), TOBN(0xc446bc52, 0x6ca007e9),
+     TOBN(0xef8c50a6, 0x96d6134b), TOBN(0x9361fbf5, 0x9e09a05c),
+     TOBN(0xf17f85a6, 0xdca3291a), TOBN(0xb178d548, 0xff251a21),
+     TOBN(0x87f6374b, 0xa4df3915), TOBN(0x566ce1bf, 0x2fd5d608),
+     TOBN(0x425cba4d, 0x7de35102), TOBN(0x6b745f8f, 0x58c5d5e2),
+     TOBN(0x88402af6, 0x63122edf), TOBN(0x3190f9ed, 0x3b989a89),
+     TOBN(0x4ad3d387, 0xebba3156), TOBN(0xef385ad9, 0xc7c469a5),
+     TOBN(0xb08281de, 0x3f642c29), TOBN(0x20be0888, 0x910ffb88),
+     TOBN(0xf353dd4a, 0xd5292546), TOBN(0x3f1627de, 0x8377a262),
+     TOBN(0xa5faa013, 0xeefcd638), TOBN(0x8f3bf626, 0x74cc77c3),
+     TOBN(0x32618f65, 0xa348f55e), TOBN(0x5787c0dc, 0x9fefeb9e),
+     TOBN(0xf1673aa2, 0xd9a23e44), TOBN(0x88dfa993, 0x4e10690d),
+     TOBN(0x1ced1b36, 0x2bf91108), TOBN(0x9193ceca, 0x3af48649),
+     TOBN(0xfb34327d, 0x2d738fc5), TOBN(0x6697b037, 0x975fee6c),
+     TOBN(0x2f485da0, 0xc04079a5), TOBN(0x2cdf5735, 0x2feaa1ac),
+     TOBN(0x76944420, 0xbd55659e), TOBN(0x7973e32b, 0x4376090c),
+     TOBN(0x86bb4fe1, 0x163b591a), TOBN(0x10441aed, 0xc196f0ca),
+     TOBN(0x3b431f4a, 0x045ad915), TOBN(0x6c11b437, 0xa4afacb1),
+     TOBN(0x30b0c7db, 0x71fdbbd8), TOBN(0xb642931f, 0xeda65acd),
+     TOBN(0x4baae6e8, 0x9c92b235), TOBN(0xa73bbd0e, 0x6b3993a1),
+     TOBN(0xd06d60ec, 0x693dd031), TOBN(0x03cab91b, 0x7156881c),
+     TOBN(0xd615862f, 0x1db3574b), TOBN(0x485b0185, 0x64bb061a),
+     TOBN(0x27434988, 0xa0181e06), TOBN(0x2cd61ad4, 0xc1c0c757),
+     TOBN(0x3effed5a, 0x2ff9f403), TOBN(0x8dc98d8b, 0x62239029),
+     TOBN(0x2206021e, 0x1f17b70d), TOBN(0xafbec0ca, 0xbf510015),
+     TOBN(0x9fed7164, 0x80130dfa), TOBN(0x306dc2b5, 0x8a02dcf5),
+     TOBN(0x48f06620, 0xfeb10fc0), TOBN(0x78d1e1d5, 0x5a57cf51),
+     TOBN(0xadef8c5a, 0x192ef710), TOBN(0x88afbd4b, 0x3b7431f9),
+     TOBN(0x7e1f7407, 0x64250c9e), TOBN(0x6e31318d, 0xb58bec07),
+     TOBN(0xfd4fc4b8, 0x24f89b4e), TOBN(0x65a5dd88, 0x48c36a2a),
+     TOBN(0x4f1eccff, 0xf024baa7), TOBN(0x22a21cf2, 0xcba94650),
+     TOBN(0x95d29dee, 0x42a554f7), TOBN(0x828983a5, 0x002ec4ba),
+     TOBN(0x8112a1f7, 0x8badb73d), TOBN(0x79ea8897, 0xa27c1839),
+     TOBN(0x8969a5a7, 0xd065fd83), TOBN(0xf49af791, 0xb262a0bc),
+     TOBN(0xfcdea8b6, 0xaf2b5127), TOBN(0x10e913e1, 0x564c2dbc),
+     TOBN(0x51239d14, 0xbc21ef51), TOBN(0xe51c3ceb, 0x4ce57292),
+     TOBN(0x795ff068, 0x47bbcc3b), TOBN(0x86b46e1e, 0xbd7e11e6),
+     TOBN(0x0ea6ba23, 0x80041ef4), TOBN(0xd72fe505, 0x6262342e),
+     TOBN(0x8abc6dfd, 0x31d294d4), TOBN(0xbbe017a2, 0x1278c2c9),
+     TOBN(0xb1fcfa09, 0xb389328a), TOBN(0x322fbc62, 0xd01771b5),
+     TOBN(0x04c0d063, 0x60b045bf), TOBN(0xdb652edc, 0x10e52d01),
+     TOBN(0x50ef932c, 0x03ec6627), TOBN(0xde1b3b2d, 0xc1ee50e3),
+     TOBN(0x5ab7bdc5, 0xdc37a90d), TOBN(0xfea67213, 0x31e33a96),
+     TOBN(0x6482b5cb, 0x4f2999aa), TOBN(0x38476cc6, 0xb8cbf0dd),
+     TOBN(0x93ebfacb, 0x173405bb), TOBN(0x15cdafe7, 0xe52369ec),
+     TOBN(0xd42d5ba4, 0xd935b7db), TOBN(0x648b6004, 0x1c99a4cd),
+     TOBN(0x785101bd, 0xa3b5545b), TOBN(0x4bf2c38a, 0x9dd67faf),
+     TOBN(0xb1aadc63, 0x4442449c), TOBN(0xe0e9921a, 0x33ad4fb8),
+     TOBN(0x5c552313, 0xaa686d82), TOBN(0xdee635fa, 0x465d866c),
+     TOBN(0xbc3c224a, 0x18ee6e8a), TOBN(0xeed748a6, 0xed42e02f),
+     TOBN(0xe70f930a, 0xd474cd08), TOBN(0x774ea6ec, 0xfff24adf),
+     TOBN(0x03e2de1c, 0xf3480d4a), TOBN(0xf0d8edc7, 0xbc8acf1a),
+     TOBN(0xf23e3303, 0x68295a9c), TOBN(0xfadd5f68, 0xc546a97d),
+     TOBN(0x895597ad, 0x96f8acb1), TOBN(0xbddd49d5, 0x671bdae2),
+     TOBN(0x16fcd528, 0x21dd43f4), TOBN(0xa5a45412, 0x6619141a)}
+    ,
+    {TOBN(0x8ce9b6bf, 0xc360e25a), TOBN(0xe6425195, 0x075a1a78),
+     TOBN(0x9dc756a8, 0x481732f4), TOBN(0x83c0440f, 0x5432b57a),
+     TOBN(0xc670b3f1, 0xd720281f), TOBN(0x2205910e, 0xd135e051),
+     TOBN(0xded14b0e, 0xdb052be7), TOBN(0x697b3d27, 0xc568ea39),
+     TOBN(0x2e599b9a, 0xfb3ff9ed), TOBN(0x28c2e0ab, 0x17f6515c),
+     TOBN(0x1cbee4fd, 0x474da449), TOBN(0x071279a4, 0x4f364452),
+     TOBN(0x97abff66, 0x01fbe855), TOBN(0x3ee394e8, 0x5fda51c4),
+     TOBN(0x190385f6, 0x67597c0b), TOBN(0x6e9fccc6, 0xa27ee34b),
+     TOBN(0x0b89de93, 0x14092ebb), TOBN(0xf17256bd, 0x428e240c),
+     TOBN(0xcf89a7f3, 0x93d2f064), TOBN(0x4f57841e, 0xe1ed3b14),
+     TOBN(0x4ee14405, 0xe708d855), TOBN(0x856aae72, 0x03f1c3d0),
+     TOBN(0xc8e5424f, 0xbdd7eed5), TOBN(0x3333e4ef, 0x73ab4270),
+     TOBN(0x3bc77ade, 0xdda492f8), TOBN(0xc11a3aea, 0x78297205),
+     TOBN(0x5e89a3e7, 0x34931b4c), TOBN(0x17512e2e, 0x9f5694bb),
+     TOBN(0x5dc349f3, 0x177bf8b6), TOBN(0x232ea4ba, 0x08c7ff3e),
+     TOBN(0x9c4f9d16, 0xf511145d), TOBN(0xccf109a3, 0x33b379c3),
+     TOBN(0xe75e7a88, 0xa1f25897), TOBN(0x7ac6961f, 0xa1b5d4d8),
+     TOBN(0xe3e10773, 0x08f3ed5c), TOBN(0x208a54ec, 0x0a892dfb),
+     TOBN(0xbe826e19, 0x78660710), TOBN(0x0cf70a97, 0x237df2c8),
+     TOBN(0x418a7340, 0xed704da5), TOBN(0xa3eeb9a9, 0x08ca33fd),
+     TOBN(0x49d96233, 0x169bca96), TOBN(0x04d286d4, 0x2da6aafb),
+     TOBN(0xc09606ec, 0xa0c2fa94), TOBN(0x8869d0d5, 0x23ff0fb3),
+     TOBN(0xa99937e5, 0xd0150d65), TOBN(0xa92e2503, 0x240c14c9),
+     TOBN(0x656bf945, 0x108e2d49), TOBN(0x152a733a, 0xa2f59e2b),
+     TOBN(0xb4323d58, 0x8434a920), TOBN(0xc0af8e93, 0x622103c5),
+     TOBN(0x667518ef, 0x938dbf9a), TOBN(0xa1843073, 0x83a9cdf2),
+     TOBN(0x350a94aa, 0x5447ab80), TOBN(0xe5e5a325, 0xc75a3d61),
+     TOBN(0x74ba507f, 0x68411a9e), TOBN(0x10581fc1, 0x594f70c5),
+     TOBN(0x60e28570, 0x80eb24a9), TOBN(0x7bedfb4d, 0x488e0cfd),
+     TOBN(0x721ebbd7, 0xc259cdb8), TOBN(0x0b0da855, 0xbc6390a9),
+     TOBN(0x2b4d04db, 0xde314c70), TOBN(0xcdbf1fbc, 0x6c32e846),
+     TOBN(0x33833eab, 0xb162fc9e), TOBN(0x9939b48b, 0xb0dd3ab7),
+     TOBN(0x5aaa98a7, 0xcb0c9c8c), TOBN(0x75105f30, 0x81c4375c),
+     TOBN(0xceee5057, 0x5ef1c90f), TOBN(0xb31e065f, 0xc23a17bf),
+     TOBN(0x5364d275, 0xd4b6d45a), TOBN(0xd363f3ad, 0x62ec8996),
+     TOBN(0xb5d21239, 0x4391c65b), TOBN(0x84564765, 0xebb41b47),
+     TOBN(0x20d18ecc, 0x37107c78), TOBN(0xacff3b6b, 0x570c2a66),
+     TOBN(0x22f975d9, 0x9bd0d845), TOBN(0xef0a0c46, 0xba178fa0),
+     TOBN(0x1a419651, 0x76b6028e), TOBN(0xc49ec674, 0x248612d4),
+     TOBN(0x5b6ac4f2, 0x7338af55), TOBN(0x06145e62, 0x7bee5a36),
+     TOBN(0x33e95d07, 0xe75746b5), TOBN(0x1c1e1f6d, 0xc40c78be),
+     TOBN(0x967833ef, 0x222ff8e2), TOBN(0x4bedcf6a, 0xb49180ad),
+     TOBN(0x6b37e9c1, 0x3d7a4c8a), TOBN(0x2748887c, 0x6ddfe760),
+     TOBN(0xf7055123, 0xaa3a5bbc), TOBN(0x954ff225, 0x7bbb8e74),
+     TOBN(0xc42b8ab1, 0x97c3dfb9), TOBN(0x55a549b0, 0xcf168154),
+     TOBN(0xad6748e7, 0xc1b50692), TOBN(0x2775780f, 0x6fc5cbcb),
+     TOBN(0x4eab80b8, 0xe1c9d7c8), TOBN(0x8c69dae1, 0x3fdbcd56),
+     TOBN(0x47e6b4fb, 0x9969eace), TOBN(0x002f1085, 0xa705cb5a),
+     TOBN(0x4e23ca44, 0x6d3fea55), TOBN(0xb4ae9c86, 0xf4810568),
+     TOBN(0x47bfb91b, 0x2a62f27d), TOBN(0x60deb4c9, 0xd9bac28c),
+     TOBN(0xa892d894, 0x7de6c34c), TOBN(0x4ee68259, 0x4494587d),
+     TOBN(0x914ee14e, 0x1a3f8a5b), TOBN(0xbb113eaa, 0x28700385),
+     TOBN(0x81ca03b9, 0x2115b4c9), TOBN(0x7c163d38, 0x8908cad1),
+     TOBN(0xc912a118, 0xaa18179a), TOBN(0xe09ed750, 0x886e3081),
+     TOBN(0xa676e3fa, 0x26f516ca), TOBN(0x753cacf7, 0x8e732f91),
+     TOBN(0x51592aea, 0x833da8b4), TOBN(0xc626f42f, 0x4cbea8aa),
+     TOBN(0xef9dc899, 0xa7b56eaf), TOBN(0x00c0e52c, 0x34ef7316),
+     TOBN(0x5b1e4e24, 0xfe818a86), TOBN(0x9d31e20d, 0xc538be47),
+     TOBN(0x22eb932d, 0x3ed68974), TOBN(0xe44bbc08, 0x7c4e87c4),
+     TOBN(0x4121086e, 0x0dde9aef), TOBN(0x8e6b9cff, 0x134f4345),
+     TOBN(0x96892c1f, 0x711b0eb9), TOBN(0xb905f2c8, 0x780ab954),
+     TOBN(0xace26309, 0xa20792db), TOBN(0xec8ac9b3, 0x0684e126),
+     TOBN(0x486ad8b6, 0xb40a2447), TOBN(0x60121fc1, 0x9fe3fb24),
+     TOBN(0x5626fccf, 0x1a8e3b3f), TOBN(0x4e568622, 0x6ad1f394),
+     TOBN(0xda7aae0d, 0x196aa5a1), TOBN(0xe0df8c77, 0x1041b5fb),
+     TOBN(0x451465d9, 0x26b318b7), TOBN(0xc29b6e55, 0x7ab136e9),
+     TOBN(0x2c2ab48b, 0x71148463), TOBN(0xb5738de3, 0x64454a76),
+     TOBN(0x54ccf9a0, 0x5a03abe4), TOBN(0x377c0296, 0x0427d58e),
+     TOBN(0x73f5f0b9, 0x2bb39c1f), TOBN(0x14373f2c, 0xe608d8c5),
+     TOBN(0xdcbfd314, 0x00fbb805), TOBN(0xdf18fb20, 0x83afdcfb),
+     TOBN(0x81a57f42, 0x42b3523f), TOBN(0xe958532d, 0x87f650fb),
+     TOBN(0xaa8dc8b6, 0x8b0a7d7c), TOBN(0x1b75dfb7, 0x150166be),
+     TOBN(0x90e4f7c9, 0x2d7d1413), TOBN(0x67e2d6b5, 0x9834f597),
+     TOBN(0x4fd4f4f9, 0xa808c3e8), TOBN(0xaf8237e0, 0xd5281ec1),
+     TOBN(0x25ab5fdc, 0x84687cee), TOBN(0xc5ded6b1, 0xa5b26c09),
+     TOBN(0x8e4a5aec, 0xc8ea7650), TOBN(0x23b73e5c, 0x14cc417f),
+     TOBN(0x2bfb4318, 0x3037bf52), TOBN(0xb61e6db5, 0x78c725d7),
+     TOBN(0x8efd4060, 0xbbb3e5d7), TOBN(0x2e014701, 0xdbac488e),
+     TOBN(0xac75cf9a, 0x360aa449), TOBN(0xb70cfd05, 0x79634d08),
+     TOBN(0xa591536d, 0xfffb15ef), TOBN(0xb2c37582, 0xd07c106c),
+     TOBN(0xb4293fdc, 0xf50225f9), TOBN(0xc52e175c, 0xb0e12b03),
+     TOBN(0xf649c3ba, 0xd0a8bf64), TOBN(0x745a8fef, 0xeb8ae3c6),
+     TOBN(0x30d7e5a3, 0x58321bc3), TOBN(0xb1732be7, 0x0bc4df48),
+     TOBN(0x1f217993, 0xe9ea5058), TOBN(0xf7a71cde, 0x3e4fd745),
+     TOBN(0x86cc533e, 0x894c5bbb), TOBN(0x6915c7d9, 0x69d83082),
+     TOBN(0xa6aa2d05, 0x5815c244), TOBN(0xaeeee592, 0x49b22ce5),
+     TOBN(0x89e39d13, 0x78135486), TOBN(0x3a275c1f, 0x16b76f2f),
+     TOBN(0xdb6bcc1b, 0xe036e8f5), TOBN(0x4df69b21, 0x5e4709f5),
+     TOBN(0xa188b250, 0x2d0f39aa), TOBN(0x622118bb, 0x15a85947),
+     TOBN(0x2ebf520f, 0xfde0f4fa), TOBN(0xa40e9f29, 0x4860e539),
+     TOBN(0x7b6a51eb, 0x22b57f0f), TOBN(0x849a33b9, 0x7e80644a),
+     TOBN(0x50e5d16f, 0x1cf095fe), TOBN(0xd754b54e, 0xec55f002),
+     TOBN(0x5cfbbb22, 0x236f4a98), TOBN(0x0b0c59e9, 0x066800bb),
+     TOBN(0x4ac69a8f, 0x5a9a7774), TOBN(0x2b33f804, 0xd6bec948),
+     TOBN(0xb3729295, 0x32e6c466), TOBN(0x68956d0f, 0x4e599c73),
+     TOBN(0xa47a249f, 0x155c31cc), TOBN(0x24d80f0d, 0xe1ce284e),
+     TOBN(0xcd821dfb, 0x988baf01), TOBN(0xe6331a7d, 0xdbb16647),
+     TOBN(0x1eb8ad33, 0x094cb960), TOBN(0x593cca38, 0xc91bbca5),
+     TOBN(0x384aac8d, 0x26567456), TOBN(0x40fa0309, 0xc04b6490),
+     TOBN(0x97834cd6, 0xdab6c8f6), TOBN(0x68a7318d, 0x3f91e55f),
+     TOBN(0xa00fd04e, 0xfc4d3157), TOBN(0xb56f8ab2, 0x2bf3bdea),
+     TOBN(0x014f5648, 0x4fa57172), TOBN(0x948c5860, 0x450abdb3),
+     TOBN(0x342b5df0, 0x0ebd4f08), TOBN(0x3e5168cd, 0x0e82938e),
+     TOBN(0x7aedc1ce, 0xb0df5dd0), TOBN(0x6bbbc6d9, 0xe5732516),
+     TOBN(0xc7bfd486, 0x605daaa6), TOBN(0x46fd72b7, 0xbb9a6c9e),
+     TOBN(0xe4847fb1, 0xa124fb89), TOBN(0x75959cbd, 0xa2d8ffbc),
+     TOBN(0x42579f65, 0xc8a588ee), TOBN(0x368c92e6, 0xb80b499d),
+     TOBN(0xea4ef6cd, 0x999a5df1), TOBN(0xaa73bb7f, 0x936fe604),
+     TOBN(0xf347a70d, 0x6457d188), TOBN(0x86eda86b, 0x8b7a388b),
+     TOBN(0xb7cdff06, 0x0ccd6013), TOBN(0xbeb1b6c7, 0xd0053fb2),
+     TOBN(0x0b022387, 0x99240a9f), TOBN(0x1bbb384f, 0x776189b2),
+     TOBN(0x8695e71e, 0x9066193a), TOBN(0x2eb50097, 0x06ffac7e),
+     TOBN(0x0654a9c0, 0x4a7d2caa), TOBN(0x6f3fb3d1, 0xa5aaa290),
+     TOBN(0x835db041, 0xff476e8f), TOBN(0x540b8b0b, 0xc42295e4),
+     TOBN(0xa5c73ac9, 0x05e214f5), TOBN(0x9a74075a, 0x56a0b638),
+     TOBN(0x2e4b1090, 0xce9e680b), TOBN(0x57a5b479, 0x6b8d9afa),
+     TOBN(0x0dca48e7, 0x26bfe65c), TOBN(0x097e391c, 0x7290c307),
+     TOBN(0x683c462e, 0x6669e72e), TOBN(0xf505be1e, 0x062559ac),
+     TOBN(0x5fbe3ea1, 0xe3a3035a), TOBN(0x6431ebf6, 0x9cd50da8),
+     TOBN(0xfd169d5c, 0x1f6407f2), TOBN(0x8d838a95, 0x60fce6b8),
+     TOBN(0x2a2bfa7f, 0x650006f0), TOBN(0xdfd7dad3, 0x50c0fbb2),
+     TOBN(0x92452495, 0xccf9ad96), TOBN(0x183bf494, 0xd95635f9),
+     TOBN(0x02d5df43, 0x4a7bd989), TOBN(0x505385cc, 0xa5431095),
+     TOBN(0xdd98e67d, 0xfd43f53e), TOBN(0xd61e1a6c, 0x500c34a9),
+     TOBN(0x5a4b46c6, 0x4a8a3d62), TOBN(0x8469c4d0, 0x247743d2),
+     TOBN(0x2bb3a13d, 0x88f7e433), TOBN(0x62b23a10, 0x01be5849),
+     TOBN(0xe83596b4, 0xa63d1a4c), TOBN(0x454e7fea, 0x7d183f3e),
+     TOBN(0x643fce61, 0x17afb01c), TOBN(0x4e65e5e6, 0x1c4c3638),
+     TOBN(0x41d85ea1, 0xef74c45b), TOBN(0x2cfbfa66, 0xae328506),
+     TOBN(0x98b078f5, 0x3ada7da9), TOBN(0xd985fe37, 0xec752fbb),
+     TOBN(0xeece68fe, 0x5a0148b4), TOBN(0x6f9a55c7, 0x2d78136d),
+     TOBN(0x232dccc4, 0xd2b729ce), TOBN(0xa27e0dfd, 0x90aafbc4),
+     TOBN(0x96474452, 0x12b4603e), TOBN(0xa876c551, 0x6b706d14),
+     TOBN(0xdf145fcf, 0x69a9d412), TOBN(0xe2ab75b7, 0x2d479c34),
+     TOBN(0x12df9a76, 0x1a23ff97), TOBN(0xc6138992, 0x5d359d10),
+     TOBN(0x6e51c7ae, 0xfa835f22), TOBN(0x69a79cb1, 0xc0fcc4d9),
+     TOBN(0xf57f350d, 0x594cc7e1), TOBN(0x3079ca63, 0x3350ab79),
+     TOBN(0x226fb614, 0x9aff594a), TOBN(0x35afec02, 0x6d59a62b),
+     TOBN(0x9bee46f4, 0x06ed2c6e), TOBN(0x58da1735, 0x7d939a57),
+     TOBN(0x44c50402, 0x8fd1797e), TOBN(0xd8853e7c, 0x5ccea6ca),
+     TOBN(0x4065508d, 0xa35fcd5f), TOBN(0x8965df8c, 0x495ccaeb),
+     TOBN(0x0f2da850, 0x12e1a962), TOBN(0xee471b94, 0xc1cf1cc4),
+     TOBN(0xcef19bc8, 0x0a08fb75), TOBN(0x704958f5, 0x81de3591),
+     TOBN(0x2867f8b2, 0x3aef4f88), TOBN(0x8d749384, 0xea9f9a5f),
+     TOBN(0x1b385537, 0x8c9049f4), TOBN(0x5be948f3, 0x7b92d8b6),
+     TOBN(0xd96f725d, 0xb6e2bd6b), TOBN(0x37a222bc, 0x958c454d),
+     TOBN(0xe7c61abb, 0x8809bf61), TOBN(0x46f07fbc, 0x1346f18d),
+     TOBN(0xfb567a7a, 0xe87c0d1c), TOBN(0x84a461c8, 0x7ef3d07a),
+     TOBN(0x0a5adce6, 0xd9278d98), TOBN(0x24d94813, 0x9dfc73e1),
+     TOBN(0x4f3528b6, 0x054321c3), TOBN(0x2e03fdde, 0x692ea706),
+     TOBN(0x10e60619, 0x47b533c0), TOBN(0x1a8bc73f, 0x2ca3c055),
+     TOBN(0xae58d4b2, 0x1bb62b8f), TOBN(0xb2045a73, 0x584a24e3),
+     TOBN(0x3ab3d5af, 0xbd76e195), TOBN(0x478dd1ad, 0x6938a810),
+     TOBN(0x6ffab393, 0x6ee3d5cb), TOBN(0xdfb693db, 0x22b361e4),
+     TOBN(0xf9694496, 0x51dbf1a7), TOBN(0xcab4b4ef, 0x08a2e762),
+     TOBN(0xe8c92f25, 0xd39bba9a), TOBN(0x850e61bc, 0xf1464d96),
+     TOBN(0xb7e830e3, 0xdc09508b), TOBN(0xfaf6d2cf, 0x74317655),
+     TOBN(0x72606ceb, 0xdf690355), TOBN(0x48bb92b3, 0xd0c3ded6),
+     TOBN(0x65b75484, 0x5c7cf892), TOBN(0xf6cd7ac9, 0xd5d5f01f),
+     TOBN(0xc2c30a59, 0x96401d69), TOBN(0x91268650, 0xed921878),
+     TOBN(0x380bf913, 0xb78c558f), TOBN(0x43c0baeb, 0xc8afdaa9),
+     TOBN(0x377f61d5, 0x54f169d3), TOBN(0xf8da07e3, 0xae5ff20b),
+     TOBN(0xb676c49d, 0xa8a90ea8), TOBN(0x81c1ff2b, 0x83a29b21),
+     TOBN(0x383297ac, 0x2ad8d276), TOBN(0x3001122f, 0xba89f982),
+     TOBN(0xe1d794be, 0x6718e448), TOBN(0x246c1482, 0x7c3e6e13),
+     TOBN(0x56646ef8, 0x5d26b5ef), TOBN(0x80f5091e, 0x88069cdd),
+     TOBN(0xc5992e2f, 0x724bdd38), TOBN(0x02e915b4, 0x8471e8c7),
+     TOBN(0x96ff320a, 0x0d0ff2a9), TOBN(0xbf886487, 0x4384d1a0),
+     TOBN(0xbbe1e6a6, 0xc93f72d6), TOBN(0xd5f75d12, 0xcad800ea),
+     TOBN(0xfa40a09f, 0xe7acf117), TOBN(0x32c8cdd5, 0x7581a355),
+     TOBN(0x74221992, 0x7023c499), TOBN(0xa8afe5d7, 0x38ec3901),
+     TOBN(0x5691afcb, 0xa90e83f0), TOBN(0x41bcaa03, 0x0b8f8eac),
+     TOBN(0xe38b5ff9, 0x8d2668d5), TOBN(0x0715281a, 0x7ad81965),
+     TOBN(0x1bc8fc7c, 0x03c6ce11), TOBN(0xcbbee6e2, 0x8b650436),
+     TOBN(0x06b00fe8, 0x0cdb9808), TOBN(0x17d6e066, 0xfe3ed315),
+     TOBN(0x2e9d38c6, 0x4d0b5018), TOBN(0xab8bfd56, 0x844dcaef),
+     TOBN(0x42894a59, 0x513aed8b), TOBN(0xf77f3b6d, 0x314bd07a),
+     TOBN(0xbbdecb8f, 0x8e42b582), TOBN(0xf10e2fa8, 0xd2390fe6),
+     TOBN(0xefb95022, 0x62a2f201), TOBN(0x4d59ea50, 0x50ee32b0),
+     TOBN(0xd87f7728, 0x6da789a8), TOBN(0xcf98a2cf, 0xf79492c4),
+     TOBN(0xf9577239, 0x720943c2), TOBN(0xba044cf5, 0x3990b9d0),
+     TOBN(0x5aa8e823, 0x95f2884a), TOBN(0x834de6ed, 0x0278a0af),
+     TOBN(0xc8e1ee9a, 0x5f25bd12), TOBN(0x9259ceaa, 0x6f7ab271),
+     TOBN(0x7e6d97a2, 0x77d00b76), TOBN(0x5c0c6eea, 0xa437832a),
+     TOBN(0x5232c20f, 0x5606b81d), TOBN(0xabd7b375, 0x0d991ee5),
+     TOBN(0x4d2bfe35, 0x8632d951), TOBN(0x78f85146, 0x98ed9364),
+     TOBN(0x951873f0, 0xf30c3282), TOBN(0x0da8ac80, 0xa789230b),
+     TOBN(0x3ac7789c, 0x5398967f), TOBN(0xa69b8f7f, 0xbdda0fb5),
+     TOBN(0xe5db7717, 0x6add8545), TOBN(0x1b71cb66, 0x72c49b66),
+     TOBN(0xd8560739, 0x68421d77), TOBN(0x03840fe8, 0x83e3afea),
+     TOBN(0xb391dad5, 0x1ec69977), TOBN(0xae243fb9, 0x307f6726),
+     TOBN(0xc88ac87b, 0xe8ca160c), TOBN(0x5174cced, 0x4ce355f4),
+     TOBN(0x98a35966, 0xe58ba37d), TOBN(0xfdcc8da2, 0x7817335d),
+     TOBN(0x5b752830, 0x83fbc7bf), TOBN(0x68e419d4, 0xd9c96984),
+     TOBN(0x409a39f4, 0x02a40380), TOBN(0x88940faf, 0x1fe977bc),
+     TOBN(0xc640a94b, 0x8f8edea6), TOBN(0x1e22cd17, 0xed11547d),
+     TOBN(0xe28568ce, 0x59ffc3e2), TOBN(0x60aa1b55, 0xc1dee4e7),
+     TOBN(0xc67497c8, 0x837cb363), TOBN(0x06fb438a, 0x105a2bf2),
+     TOBN(0x30357ec4, 0x500d8e20), TOBN(0x1ad9095d, 0x0670db10),
+     TOBN(0x7f589a05, 0xc73b7cfd), TOBN(0xf544607d, 0x880d6d28),
+     TOBN(0x17ba93b1, 0xa20ef103), TOBN(0xad859130, 0x6ba6577b),
+     TOBN(0x65c91cf6, 0x6fa214a0), TOBN(0xd7d49c6c, 0x27990da5),
+     TOBN(0xecd9ec8d, 0x20bb569d), TOBN(0xbd4b2502, 0xeeffbc33),
+     TOBN(0x2056ca5a, 0x6bed0467), TOBN(0x7916a1f7, 0x5b63728c),
+     TOBN(0xd4f9497d, 0x53a4f566), TOBN(0x89734664, 0x97b56810),
+     TOBN(0xf8e1da74, 0x0494a621), TOBN(0x82546a93, 0x8d011c68),
+     TOBN(0x1f3acb19, 0xc61ac162), TOBN(0x52f8fa9c, 0xabad0d3e),
+     TOBN(0x15356523, 0xb4b7ea43), TOBN(0x5a16ad61, 0xae608125),
+     TOBN(0xb0bcb87f, 0x4faed184), TOBN(0x5f236b1d, 0x5029f45f),
+     TOBN(0xd42c7607, 0x0bc6b1fc), TOBN(0xc644324e, 0x68aefce3),
+     TOBN(0x8e191d59, 0x5c5d8446), TOBN(0xc0208077, 0x13ae1979),
+     TOBN(0xadcaee55, 0x3ba59cc7), TOBN(0x20ed6d6b, 0xa2cb81ba),
+     TOBN(0x0952ba19, 0xb6efcffc), TOBN(0x60f12d68, 0x97c0b87c),
+     TOBN(0x4ee2c7c4, 0x9caa30bc), TOBN(0x767238b7, 0x97fbff4e),
+     TOBN(0xebc73921, 0x501b5d92), TOBN(0x3279e3df, 0xc2a37737),
+     TOBN(0x9fc12bc8, 0x6d197543), TOBN(0xfa94dc6f, 0x0a40db4e),
+     TOBN(0x7392b41a, 0x530ccbbd), TOBN(0x87c82146, 0xea823525),
+     TOBN(0xa52f984c, 0x05d98d0c), TOBN(0x2ae57d73, 0x5ef6974c),
+     TOBN(0x9377f7bf, 0x3042a6dd), TOBN(0xb1a007c0, 0x19647a64),
+     TOBN(0xfaa9079a, 0x0cca9767), TOBN(0x3d81a25b, 0xf68f72d5),
+     TOBN(0x752067f8, 0xff81578e), TOBN(0x78622150, 0x9045447d),
+     TOBN(0xc0c22fcf, 0x0505aa6f), TOBN(0x1030f0a6, 0x6bed1c77),
+     TOBN(0x31f29f15, 0x1f0bd739), TOBN(0x2d7989c7, 0xe6debe85),
+     TOBN(0x5c070e72, 0x8e677e98), TOBN(0x0a817bd3, 0x06e81fd5),
+     TOBN(0xc110d830, 0xb0f2ac95), TOBN(0x48d0995a, 0xab20e64e),
+     TOBN(0x0f3e00e1, 0x7729cd9a), TOBN(0x2a570c20, 0xdd556946),
+     TOBN(0x912dbcfd, 0x4e86214d), TOBN(0x2d014ee2, 0xcf615498),
+     TOBN(0x55e2b1e6, 0x3530d76e), TOBN(0xc5135ae4, 0xfd0fd6d1),
+     TOBN(0x0066273a, 0xd4f3049f), TOBN(0xbb8e9893, 0xe7087477),
+     TOBN(0x2dba1ddb, 0x14c6e5fd), TOBN(0xdba37886, 0x51f57e6c),
+     TOBN(0x5aaee0a6, 0x5a72f2cf), TOBN(0x1208bfbf, 0x7bea5642),
+     TOBN(0xf5c6aa3b, 0x67872c37), TOBN(0xd726e083, 0x43f93224),
+     TOBN(0x1854daa5, 0x061f1658), TOBN(0xc0016df1, 0xdf0cd2b3),
+     TOBN(0xc2a3f23e, 0x833d50de), TOBN(0x73b681d2, 0xbbbd3017),
+     TOBN(0x2f046dc4, 0x3ac343c0), TOBN(0x9c847e7d, 0x85716421),
+     TOBN(0xe1e13c91, 0x0917eed4), TOBN(0x3fc9eebd, 0x63a1b9c6),
+     TOBN(0x0f816a72, 0x7fe02299), TOBN(0x6335ccc2, 0x294f3319),
+     TOBN(0x3820179f, 0x4745c5be), TOBN(0xe647b782, 0x922f066e),
+     TOBN(0xc22e49de, 0x02cafb8a), TOBN(0x299bc2ff, 0xfcc2eccc),
+     TOBN(0x9a8feea2, 0x6e0e8282), TOBN(0xa627278b, 0xfe893205),
+     TOBN(0xa7e19733, 0x7933e47b), TOBN(0xf4ff6b13, 0x2e766402),
+     TOBN(0xa4d8be0a, 0x98440d9f), TOBN(0x658f5c2f, 0x38938808),
+     TOBN(0x90b75677, 0xc95b3b3e), TOBN(0xfa044269, 0x3137b6ff),
+     TOBN(0x077b039b, 0x43c47c29), TOBN(0xcca95dd3, 0x8a6445b2),
+     TOBN(0x0b498ba4, 0x2333fc4c), TOBN(0x274f8e68, 0xf736a1b1),
+     TOBN(0x6ca348fd, 0x5f1d4b2e), TOBN(0x24d3be78, 0xa8f10199),
+     TOBN(0x8535f858, 0xca14f530), TOBN(0xa6e7f163, 0x5b982e51),
+     TOBN(0x847c8512, 0x36e1bf62), TOBN(0xf6a7c58e, 0x03448418),
+     TOBN(0x583f3703, 0xf9374ab6), TOBN(0x864f9195, 0x6e564145),
+     TOBN(0x33bc3f48, 0x22526d50), TOBN(0x9f323c80, 0x1262a496),
+     TOBN(0xaa97a7ae, 0x3f046a9a), TOBN(0x70da183e, 0xdf8a039a),
+     TOBN(0x5b68f71c, 0x52aa0ba6), TOBN(0x9be0fe51, 0x21459c2d),
+     TOBN(0xc1e17eb6, 0xcbc613e5), TOBN(0x33131d55, 0x497ea61c),
+     TOBN(0x2f69d39e, 0xaf7eded5), TOBN(0x73c2f434, 0xde6af11b),
+     TOBN(0x4ca52493, 0xa4a375fa), TOBN(0x5f06787c, 0xb833c5c2),
+     TOBN(0x814e091f, 0x3e6e71cf), TOBN(0x76451f57, 0x8b746666)}
+    ,
+    {TOBN(0x80f9bdef, 0x694db7e0), TOBN(0xedca8787, 0xb9fcddc6),
+     TOBN(0x51981c34, 0x03b8dce1), TOBN(0x4274dcf1, 0x70e10ba1),
+     TOBN(0xf72743b8, 0x6def6d1a), TOBN(0xd25b1670, 0xebdb1866),
+     TOBN(0xc4491e8c, 0x050c6f58), TOBN(0x2be2b2ab, 0x87fbd7f5),
+     TOBN(0x3e0e5c9d, 0xd111f8ec), TOBN(0xbcc33f8d, 0xb7c4e760),
+     TOBN(0x702f9a91, 0xbd392a51), TOBN(0x7da4a795, 0xc132e92d),
+     TOBN(0x1a0b0ae3, 0x0bb1151b), TOBN(0x54febac8, 0x02e32251),
+     TOBN(0xea3a5082, 0x694e9e78), TOBN(0xe58ffec1, 0xe4fe40b8),
+     TOBN(0xf85592fc, 0xd1e0cf9e), TOBN(0xdea75f0d, 0xc0e7b2e8),
+     TOBN(0xc04215cf, 0xc135584e), TOBN(0x174fc727, 0x2f57092a),
+     TOBN(0xe7277877, 0xeb930bea), TOBN(0x504caccb, 0x5eb02a5a),
+     TOBN(0xf9fe08f7, 0xf5241b9b), TOBN(0xe7fb62f4, 0x8d5ca954),
+     TOBN(0xfbb8349d, 0x29c4120b), TOBN(0x9f94391f, 0xc0d0d915),
+     TOBN(0xc4074fa7, 0x5410ba51), TOBN(0xa66adbf6, 0x150a5911),
+     TOBN(0xc164543c, 0x34bfca38), TOBN(0xe0f27560, 0xb9e1ccfc),
+     TOBN(0x99da0f53, 0xe820219c), TOBN(0xe8234498, 0xc6b4997a),
+     TOBN(0xcfb88b76, 0x9d4c5423), TOBN(0x9e56eb10, 0xb0521c49),
+     TOBN(0x418e0b5e, 0xbe8700a1), TOBN(0x00cbaad6, 0xf93cb58a),
+     TOBN(0xe923fbde, 0xd92a5e67), TOBN(0xca4979ac, 0x1f347f11),
+     TOBN(0x89162d85, 0x6bc0585b), TOBN(0xdd6254af, 0xac3c70e3),
+     TOBN(0x7b23c513, 0x516e19e4), TOBN(0x56e2e847, 0xc5c4d593),
+     TOBN(0x9f727d73, 0x5ce71ef6), TOBN(0x5b6304a6, 0xf79a44c5),
+     TOBN(0x6638a736, 0x3ab7e433), TOBN(0x1adea470, 0xfe742f83),
+     TOBN(0xe054b854, 0x5b7fc19f), TOBN(0xf935381a, 0xba1d0698),
+     TOBN(0x546eab2d, 0x799e9a74), TOBN(0x96239e0e, 0xa949f729),
+     TOBN(0xca274c6b, 0x7090055a), TOBN(0x835142c3, 0x9020c9b0),
+     TOBN(0xa405667a, 0xa2e8807f), TOBN(0x29f2c085, 0x1aa3d39e),
+     TOBN(0xcc555d64, 0x42fc72f5), TOBN(0xe856e0e7, 0xfbeacb3c),
+     TOBN(0xb5504f9d, 0x918e4936), TOBN(0x65035ef6, 0xb2513982),
+     TOBN(0x0553a0c2, 0x6f4d9cb9), TOBN(0x6cb10d56, 0xbea85509),
+     TOBN(0x48d957b7, 0xa242da11), TOBN(0x16a4d3dd, 0x672b7268),
+     TOBN(0x3d7e637c, 0x8502a96b), TOBN(0x27c7032b, 0x730d463b),
+     TOBN(0xbdc02b18, 0xe4136a14), TOBN(0xbacf969d, 0x678e32bf),
+     TOBN(0xc98d89a3, 0xdd9c3c03), TOBN(0x7b92420a, 0x23becc4f),
+     TOBN(0xd4b41f78, 0xc64d565c), TOBN(0x9f969d00, 0x10f28295),
+     TOBN(0xec7f7f76, 0xb13d051a), TOBN(0x08945e1e, 0xa92da585),
+     TOBN(0x55366b7d, 0x5846426f), TOBN(0xe7d09e89, 0x247d441d),
+     TOBN(0x510b404d, 0x736fbf48), TOBN(0x7fa003d0, 0xe784bd7d),
+     TOBN(0x25f7614f, 0x17fd9596), TOBN(0x49e0e0a1, 0x35cb98db),
+     TOBN(0x2c65957b, 0x2e83a76a), TOBN(0x5d40da8d, 0xcddbe0f8),
+     TOBN(0xf2b8c405, 0x050bad24), TOBN(0x8918426d, 0xc2aa4823),
+     TOBN(0x2aeab3dd, 0xa38365a7), TOBN(0x72031717, 0x7c91b690),
+     TOBN(0x8b00d699, 0x60a94120), TOBN(0x478a255d, 0xe99eaeec),
+     TOBN(0xbf656a5f, 0x6f60aafd), TOBN(0xdfd7cb75, 0x5dee77b3),
+     TOBN(0x37f68bb4, 0xa595939d), TOBN(0x03556479, 0x28740217),
+     TOBN(0x8e740e7c, 0x84ad7612), TOBN(0xd89bc843, 0x9044695f),
+     TOBN(0xf7f3da5d, 0x85a9184d), TOBN(0x562563bb, 0x9fc0b074),
+     TOBN(0x06d2e6aa, 0xf88a888e), TOBN(0x612d8643, 0x161fbe7c),
+     TOBN(0x465edba7, 0xf64085e7), TOBN(0xb230f304, 0x29aa8511),
+     TOBN(0x53388426, 0xcda2d188), TOBN(0x90885735, 0x4b666649),
+     TOBN(0x6f02ff9a, 0x652f54f6), TOBN(0x65c82294, 0x5fae2bf0),
+     TOBN(0x7816ade0, 0x62f5eee3), TOBN(0xdcdbdf43, 0xfcc56d70),
+     TOBN(0x9fb3bba3, 0x54530bb2), TOBN(0xbde3ef77, 0xcb0869ea),
+     TOBN(0x89bc9046, 0x0b431163), TOBN(0x4d03d7d2, 0xe4819a35),
+     TOBN(0x33ae4f9e, 0x43b6a782), TOBN(0x216db307, 0x9c88a686),
+     TOBN(0x91dd88e0, 0x00ffedd9), TOBN(0xb280da9f, 0x12bd4840),
+     TOBN(0x32a7cb8a, 0x1635e741), TOBN(0xfe14008a, 0x78be02a7),
+     TOBN(0x3fafb334, 0x1b7ae030), TOBN(0x7fd508e7, 0x5add0ce9),
+     TOBN(0x72c83219, 0xd607ad51), TOBN(0x0f229c0a, 0x8d40964a),
+     TOBN(0x1be2c336, 0x1c878da2), TOBN(0xe0c96742, 0xeab2ab86),
+     TOBN(0x458f8691, 0x3e538cd7), TOBN(0xa7001f6c, 0x8e08ad53),
+     TOBN(0x52b8c6e6, 0xbf5d15ff), TOBN(0x548234a4, 0x011215dd),
+     TOBN(0xff5a9d2d, 0x3d5b4045), TOBN(0xb0ffeeb6, 0x4a904190),
+     TOBN(0x55a3aca4, 0x48607f8b), TOBN(0x8cbd665c, 0x30a0672a),
+     TOBN(0x87f834e0, 0x42583068), TOBN(0x02da2aeb, 0xf3f6e683),
+     TOBN(0x6b763e5d, 0x05c12248), TOBN(0x7230378f, 0x65a8aefc),
+     TOBN(0x93bd80b5, 0x71e8e5ca), TOBN(0x53ab041c, 0xb3b62524),
+     TOBN(0x1b860513, 0x6c9c552e), TOBN(0xe84d402c, 0xd5524e66),
+     TOBN(0xa37f3573, 0xf37f5937), TOBN(0xeb0f6c7d, 0xd1e4fca5),
+     TOBN(0x2965a554, 0xac8ab0fc), TOBN(0x17fbf56c, 0x274676ac),
+     TOBN(0x2e2f6bd9, 0xacf7d720), TOBN(0x41fc8f88, 0x10224766),
+     TOBN(0x517a14b3, 0x85d53bef), TOBN(0xdae327a5, 0x7d76a7d1),
+     TOBN(0x6ad0a065, 0xc4818267), TOBN(0x33aa189b, 0x37c1bbc1),
+     TOBN(0x64970b52, 0x27392a92), TOBN(0x21699a1c, 0x2d1535ea),
+     TOBN(0xcd20779c, 0xc2d7a7fd), TOBN(0xe3186059, 0x99c83cf2),
+     TOBN(0x9b69440b, 0x72c0b8c7), TOBN(0xa81497d7, 0x7b9e0e4d),
+     TOBN(0x515d5c89, 0x1f5f82dc), TOBN(0x9a7f67d7, 0x6361079e),
+     TOBN(0xa8da81e3, 0x11a35330), TOBN(0xe44990c4, 0x4b18be1b),
+     TOBN(0xc7d5ed95, 0xaf103e59), TOBN(0xece8aba7, 0x8dac9261),
+     TOBN(0xbe82b099, 0x9394b8d3), TOBN(0x6830f09a, 0x16adfe83),
+     TOBN(0x250a29b4, 0x88172d01), TOBN(0x8b20bd65, 0xcaff9e02),
+     TOBN(0xb8a7661e, 0xe8a6329a), TOBN(0x4520304d, 0xd3fce920),
+     TOBN(0xae45da1f, 0x2b47f7ef), TOBN(0xe07f5288, 0x5bffc540),
+     TOBN(0xf7997009, 0x3464f874), TOBN(0x2244c2cd, 0xa6fa1f38),
+     TOBN(0x43c41ac1, 0x94d7d9b1), TOBN(0x5bafdd82, 0xc82e7f17),
+     TOBN(0xdf0614c1, 0x5fda0fca), TOBN(0x74b043a7, 0xa8ae37ad),
+     TOBN(0x3ba6afa1, 0x9e71734c), TOBN(0x15d5437e, 0x9c450f2e),
+     TOBN(0x4a5883fe, 0x67e242b1), TOBN(0x5143bdc2, 0x2c1953c2),
+     TOBN(0x542b8b53, 0xfc5e8920), TOBN(0x363bf9a8, 0x9a9cee08),
+     TOBN(0x02375f10, 0xc3486e08), TOBN(0x2037543b, 0x8c5e70d2),
+     TOBN(0x7109bccc, 0x625640b4), TOBN(0xcbc1051e, 0x8bc62c3b),
+     TOBN(0xf8455fed, 0x803f26ea), TOBN(0x6badceab, 0xeb372424),
+     TOBN(0xa2a9ce7c, 0x6b53f5f9), TOBN(0x64246595, 0x1b176d99),
+     TOBN(0xb1298d36, 0xb95c081b), TOBN(0x53505bb8, 0x1d9a9ee6),
+     TOBN(0x3f6f9e61, 0xf2ba70b0), TOBN(0xd07e16c9, 0x8afad453),
+     TOBN(0x9f1694bb, 0xe7eb4a6a), TOBN(0xdfebced9, 0x3cb0bc8e),
+     TOBN(0x92d3dcdc, 0x53868c8b), TOBN(0x174311a2, 0x386107a6),
+     TOBN(0x4109e07c, 0x689b4e64), TOBN(0x30e4587f, 0x2df3dcb6),
+     TOBN(0x841aea31, 0x0811b3b2), TOBN(0x6144d41d, 0x0cce43ea),
+     TOBN(0x464c4581, 0x2a9a7803), TOBN(0xd03d371f, 0x3e158930),
+     TOBN(0xc676d7f2, 0xb1f3390b), TOBN(0x9f7a1b8c, 0xa5b61272),
+     TOBN(0x4ebebfc9, 0xc2e127a9), TOBN(0x4602500c, 0x5dd997bf),
+     TOBN(0x7f09771c, 0x4711230f), TOBN(0x058eb37c, 0x020f09c1),
+     TOBN(0xab693d4b, 0xfee5e38b), TOBN(0x9289eb1f, 0x4653cbc0),
+     TOBN(0xbecf46ab, 0xd51b9cf5), TOBN(0xd2aa9c02, 0x9f0121af),
+     TOBN(0x36aaf7d2, 0xe90dc274), TOBN(0x909e4ea0, 0x48b95a3c),
+     TOBN(0xe6b70496, 0x6f32dbdb), TOBN(0x672188a0, 0x8b030b3e),
+     TOBN(0xeeffe5b3, 0xcfb617e2), TOBN(0x87e947de, 0x7c82709e),
+     TOBN(0xa44d2b39, 0x1770f5a7), TOBN(0xe4d4d791, 0x0e44eb82),
+     TOBN(0x42e69d1e, 0x3f69712a), TOBN(0xbf11c4d6, 0xac6a820e),
+     TOBN(0xb5e7f3e5, 0x42c4224c), TOBN(0xd6b4e81c, 0x449d941c),
+     TOBN(0x5d72bd16, 0x5450e878), TOBN(0x6a61e28a, 0xee25ac54),
+     TOBN(0x33272094, 0xe6f1cd95), TOBN(0x7512f30d, 0x0d18673f),
+     TOBN(0x32f7a4ca, 0x5afc1464), TOBN(0x2f095656, 0x6bbb977b),
+     TOBN(0x586f47ca, 0xa8226200), TOBN(0x02c868ad, 0x1ac07369),
+     TOBN(0x4ef2b845, 0xc613acbe), TOBN(0x43d7563e, 0x0386054c),
+     TOBN(0x54da9dc7, 0xab952578), TOBN(0xb5423df2, 0x26e84d0b),
+     TOBN(0xa8b64eeb, 0x9b872042), TOBN(0xac205782, 0x5990f6df),
+     TOBN(0x4ff696eb, 0x21f4c77a), TOBN(0x1a79c3e4, 0xaab273af),
+     TOBN(0x29bc922e, 0x9436b3f1), TOBN(0xff807ef8, 0xd6d9a27a),
+     TOBN(0x82acea3d, 0x778f22a0), TOBN(0xfb10b2e8, 0x5b5e7469),
+     TOBN(0xc0b16980, 0x2818ee7d), TOBN(0x011afff4, 0xc91c1a2f),
+     TOBN(0x95a6d126, 0xad124418), TOBN(0x31c081a5, 0xe72e295f),
+     TOBN(0x36bb283a, 0xf2f4db75), TOBN(0xd115540f, 0x7acef462),
+     TOBN(0xc7f3a8f8, 0x33f6746c), TOBN(0x21e46f65, 0xfea990ca),
+     TOBN(0x915fd5c5, 0xcaddb0a9), TOBN(0xbd41f016, 0x78614555),
+     TOBN(0x346f4434, 0x426ffb58), TOBN(0x80559436, 0x14dbc204),
+     TOBN(0xf3dd20fe, 0x5a969b7f), TOBN(0x9d59e956, 0xe899a39a),
+     TOBN(0xf1b0971c, 0x8ad4cf4b), TOBN(0x03448860, 0x2ffb8fb8),
+     TOBN(0xf071ac3c, 0x65340ba4), TOBN(0x408d0596, 0xb27fd758),
+     TOBN(0xe7c78ea4, 0x98c364b0), TOBN(0xa4aac4a5, 0x051e8ab5),
+     TOBN(0xb9e1d560, 0x485d9002), TOBN(0x9acd518a, 0x88844455),
+     TOBN(0xe4ca688f, 0xd06f56c0), TOBN(0xa48af70d, 0xdf027972),
+     TOBN(0x691f0f04, 0x5e9a609d), TOBN(0xa9dd82cd, 0xee61270e),
+     TOBN(0x8903ca63, 0xa0ef18d3), TOBN(0x9fb7ee35, 0x3d6ca3bd),
+     TOBN(0xa7b4a09c, 0xabf47d03), TOBN(0x4cdada01, 0x1c67de8e),
+     TOBN(0x52003749, 0x9355a244), TOBN(0xe77fd2b6, 0x4f2151a9),
+     TOBN(0x695d6cf6, 0x66b4efcb), TOBN(0xc5a0cacf, 0xda2cfe25),
+     TOBN(0x104efe5c, 0xef811865), TOBN(0xf52813e8, 0x9ea5cc3d),
+     TOBN(0x855683dc, 0x40b58dbc), TOBN(0x0338ecde, 0x175fcb11),
+     TOBN(0xf9a05637, 0x74921592), TOBN(0xb4f1261d, 0xb9bb9d31),
+     TOBN(0x551429b7, 0x4e9c5459), TOBN(0xbe182e6f, 0x6ea71f53),
+     TOBN(0xd3a3b07c, 0xdfc50573), TOBN(0x9ba1afda, 0x62be8d44),
+     TOBN(0x9bcfd2cb, 0x52ab65d3), TOBN(0xdf11d547, 0xa9571802),
+     TOBN(0x099403ee, 0x02a2404a), TOBN(0x497406f4, 0x21088a71),
+     TOBN(0x99479409, 0x5004ae71), TOBN(0xbdb42078, 0xa812c362),
+     TOBN(0x2b72a30f, 0xd8828442), TOBN(0x283add27, 0xfcb5ed1c),
+     TOBN(0xf7c0e200, 0x66a40015), TOBN(0x3e3be641, 0x08b295ef),
+     TOBN(0xac127dc1, 0xe038a675), TOBN(0x729deff3, 0x8c5c6320),
+     TOBN(0xb7df8fd4, 0xa90d2c53), TOBN(0x9b74b0ec, 0x681e7cd3),
+     TOBN(0x5cb5a623, 0xdab407e5), TOBN(0xcdbd3615, 0x76b340c6),
+     TOBN(0xa184415a, 0x7d28392c), TOBN(0xc184c1d8, 0xe96f7830),
+     TOBN(0xc3204f19, 0x81d3a80f), TOBN(0xfde0c841, 0xc8e02432),
+     TOBN(0x78203b3e, 0x8149e0c1), TOBN(0x5904bdbb, 0x08053a73),
+     TOBN(0x30fc1dd1, 0x101b6805), TOBN(0x43c223bc, 0x49aa6d49),
+     TOBN(0x9ed67141, 0x7a174087), TOBN(0x311469a0, 0xd5997008),
+     TOBN(0xb189b684, 0x5e43fc61), TOBN(0xf3282375, 0xe0d3ab57),
+     TOBN(0x4fa34b67, 0xb1181da8), TOBN(0x621ed0b2, 0x99ee52b8),
+     TOBN(0x9b178de1, 0xad990676), TOBN(0xd51de67b, 0x56d54065),
+     TOBN(0x2a2c27c4, 0x7538c201), TOBN(0x33856ec8, 0x38a40f5c),
+     TOBN(0x2522fc15, 0xbe6cdcde), TOBN(0x1e603f33, 0x9f0c6f89),
+     TOBN(0x7994edc3, 0x103e30a6), TOBN(0x033a00db, 0x220c853e),
+     TOBN(0xd3cfa409, 0xf7bb7fd7), TOBN(0x70f8781e, 0x462d18f6),
+     TOBN(0xbbd82980, 0x687fe295), TOBN(0x6eef4c32, 0x595669f3),
+     TOBN(0x86a9303b, 0x2f7e85c3), TOBN(0x5fce4621, 0x71988f9b),
+     TOBN(0x5b935bf6, 0xc138acb5), TOBN(0x30ea7d67, 0x25661212),
+     TOBN(0xef1eb5f4, 0xe51ab9a2), TOBN(0x0587c98a, 0xae067c78),
+     TOBN(0xb3ce1b3c, 0x77ca9ca6), TOBN(0x2a553d4d, 0x54b5f057),
+     TOBN(0xc7898236, 0x4da29ec2), TOBN(0xdbdd5d13, 0xb9c57316),
+     TOBN(0xc57d6e6b, 0x2cd80d47), TOBN(0x80b460cf, 0xfe9e7391),
+     TOBN(0x98648cab, 0xf963c31e), TOBN(0x67f9f633, 0xcc4d32fd),
+     TOBN(0x0af42a9d, 0xfdf7c687), TOBN(0x55f292a3, 0x0b015ea7),
+     TOBN(0x89e468b2, 0xcd21ab3d), TOBN(0xe504f022, 0xc393d392),
+     TOBN(0xab21e1d4, 0xa5013af9), TOBN(0xe3283f78, 0xc2c28acb),
+     TOBN(0xf38b35f6, 0x226bf99f), TOBN(0xe8354274, 0x0e291e69),
+     TOBN(0x61673a15, 0xb20c162d), TOBN(0xc101dc75, 0xb04fbdbe),
+     TOBN(0x8323b4c2, 0x255bd617), TOBN(0x6c969693, 0x6c2a9154),
+     TOBN(0xc6e65860, 0x62679387), TOBN(0x8e01db0c, 0xb8c88e23),
+     TOBN(0x33c42873, 0x893a5559), TOBN(0x7630f04b, 0x47a3e149),
+     TOBN(0xb5d80805, 0xddcf35f8), TOBN(0x582ca080, 0x77dfe732),
+     TOBN(0x2c7156e1, 0x0b1894a0), TOBN(0x92034001, 0xd81c68c0),
+     TOBN(0xed225d00, 0xc8b115b5), TOBN(0x237f9c22, 0x83b907f2),
+     TOBN(0x0ea2f32f, 0x4470e2c0), TOBN(0xb725f7c1, 0x58be4e95),
+     TOBN(0x0f1dcafa, 0xb1ae5463), TOBN(0x59ed5187, 0x1ba2fc04),
+     TOBN(0xf6e0f316, 0xd0115d4d), TOBN(0x5180b12f, 0xd3691599),
+     TOBN(0x157e32c9, 0x527f0a41), TOBN(0x7b0b081d, 0xa8e0ecc0),
+     TOBN(0x6dbaaa8a, 0xbf4f0dd0), TOBN(0x99b289c7, 0x4d252696),
+     TOBN(0x79b7755e, 0xdbf864fe), TOBN(0x6974e2b1, 0x76cad3ab),
+     TOBN(0x35dbbee2, 0x06ddd657), TOBN(0xe7cbdd11, 0x2ff3a96d),
+     TOBN(0x88381968, 0x076be758), TOBN(0x2d737e72, 0x08c91f5d),
+     TOBN(0x5f83ab62, 0x86ec3776), TOBN(0x98aa649d, 0x945fa7a1),
+     TOBN(0xf477ec37, 0x72ef0933), TOBN(0x66f52b1e, 0x098c17b1),
+     TOBN(0x9eec58fb, 0xd803738b), TOBN(0x91aaade7, 0xe4e86aa4),
+     TOBN(0x6b1ae617, 0xa5b51492), TOBN(0x63272121, 0xbbc45974),
+     TOBN(0x7e0e28f0, 0x862c5129), TOBN(0x0a8f79a9, 0x3321a4a0),
+     TOBN(0xe26d1664, 0x5041c88f), TOBN(0x0571b805, 0x53233e3a),
+     TOBN(0xd1b0ccde, 0xc9520711), TOBN(0x55a9e4ed, 0x3c8b84bf),
+     TOBN(0x9426bd39, 0xa1fef314), TOBN(0x4f5f638e, 0x6eb93f2b),
+     TOBN(0xba2a1ed3, 0x2bf9341b), TOBN(0xd63c1321, 0x4d42d5a9),
+     TOBN(0xd2964a89, 0x316dc7c5), TOBN(0xd1759606, 0xca511851),
+     TOBN(0xd8a9201f, 0xf9e6ed35), TOBN(0xb7b5ee45, 0x6736925a),
+     TOBN(0x0a83fbbc, 0x99581af7), TOBN(0x3076bc40, 0x64eeb051),
+     TOBN(0x5511c98c, 0x02dec312), TOBN(0x270de898, 0x238dcb78),
+     TOBN(0x2cf4cf9c, 0x539c08c9), TOBN(0xa70cb65e, 0x38d3b06e),
+     TOBN(0xb12ec10e, 0xcfe57bbd), TOBN(0x82c7b656, 0x35a0c2b5),
+     TOBN(0xddc7d5cd, 0x161c67bd), TOBN(0xe32e8985, 0xae3a32cc),
+     TOBN(0x7aba9444, 0xd11a5529), TOBN(0xe964ed02, 0x2427fa1a),
+     TOBN(0x1528392d, 0x24a1770a), TOBN(0xa152ce2c, 0x12c72fcd),
+     TOBN(0x714553a4, 0x8ec07649), TOBN(0x18b4c290, 0x459dd453),
+     TOBN(0xea32b714, 0x7b64b110), TOBN(0xb871bfa5, 0x2e6f07a2),
+     TOBN(0xb67112e5, 0x9e2e3c9b), TOBN(0xfbf250e5, 0x44aa90f6),
+     TOBN(0xf77aedb8, 0xbd539006), TOBN(0x3b0cdf9a, 0xd172a66f),
+     TOBN(0xedf69fea, 0xf8c51187), TOBN(0x05bb67ec, 0x741e4da7),
+     TOBN(0x47df0f32, 0x08114345), TOBN(0x56facb07, 0xbb9792b1),
+     TOBN(0xf3e007e9, 0x8f6229e4), TOBN(0x62d103f4, 0x526fba0f),
+     TOBN(0x4f33bef7, 0xb0339d79), TOBN(0x9841357b, 0xb59bfec1),
+     TOBN(0xfa8dbb59, 0xc34e6705), TOBN(0xc3c7180b, 0x7fdaa84c),
+     TOBN(0xf95872fc, 0xa4108537), TOBN(0x8750cc3b, 0x932a3e5a),
+     TOBN(0xb61cc69d, 0xb7275d7d), TOBN(0xffa0168b, 0x2e59b2e9),
+     TOBN(0xca032abc, 0x6ecbb493), TOBN(0x1d86dbd3, 0x2c9082d8),
+     TOBN(0xae1e0b67, 0xe28ef5ba), TOBN(0x2c9a4699, 0xcb18e169),
+     TOBN(0x0ecd0e33, 0x1e6bbd20), TOBN(0x571b360e, 0xaf5e81d2),
+     TOBN(0xcd9fea58, 0x101c1d45), TOBN(0x6651788e, 0x18880452),
+     TOBN(0xa9972635, 0x1f8dd446), TOBN(0x44bed022, 0xe37281d0),
+     TOBN(0x094b2b2d, 0x33da525d), TOBN(0xf193678e, 0x13144fd8),
+     TOBN(0xb8ab5ba4, 0xf4c1061d), TOBN(0x4343b5fa, 0xdccbe0f4),
+     TOBN(0xa8702371, 0x63812713), TOBN(0x47bf6d2d, 0xf7611d93),
+     TOBN(0x46729b8c, 0xbd21e1d7), TOBN(0x7484d4e0, 0xd629e77d),
+     TOBN(0x830e6eea, 0x60dbac1f), TOBN(0x23d8c484, 0xda06a2f7),
+     TOBN(0x896714b0, 0x50ca535b), TOBN(0xdc8d3644, 0xebd97a9b),
+     TOBN(0x106ef9fa, 0xb12177b4), TOBN(0xf79bf464, 0x534d5d9c),
+     TOBN(0x2537a349, 0xa6ab360b), TOBN(0xc7c54253, 0xa00c744f),
+     TOBN(0xb3c7a047, 0xe5911a76), TOBN(0x61ffa5c8, 0x647f1ee7),
+     TOBN(0x15aed36f, 0x8f56ab42), TOBN(0x6a0d41b0, 0xa3ff9ac9),
+     TOBN(0x68f469f5, 0xcc30d357), TOBN(0xbe9adf81, 0x6b72be96),
+     TOBN(0x1cd926fe, 0x903ad461), TOBN(0x7e89e38f, 0xcaca441b),
+     TOBN(0xf0f82de5, 0xfacf69d4), TOBN(0x363b7e76, 0x4775344c),
+     TOBN(0x6894f312, 0xb2e36d04), TOBN(0x3c6cb4fe, 0x11d1c9a5),
+     TOBN(0x85d9c339, 0x4008e1f2), TOBN(0x5e9a85ea, 0x249f326c),
+     TOBN(0xdc35c60a, 0x678c5e06), TOBN(0xc08b944f, 0x9f86fba9),
+     TOBN(0xde40c02c, 0x89f71f0f), TOBN(0xad8f3e31, 0xff3da3c0),
+     TOBN(0x3ea5096b, 0x42125ded), TOBN(0x13879cbf, 0xa7379183),
+     TOBN(0x6f4714a5, 0x6b306a0b), TOBN(0x359c2ea6, 0x67646c5e),
+     TOBN(0xfacf8943, 0x07726368), TOBN(0x07a58935, 0x65ff431e),
+     TOBN(0x24d661d1, 0x68754ab0), TOBN(0x801fce1d, 0x6f429a76),
+     TOBN(0xc068a85f, 0xa58ce769), TOBN(0xedc35c54, 0x5d5eca2b),
+     TOBN(0xea31276f, 0xa3f660d1), TOBN(0xa0184ebe, 0xb8fc7167),
+     TOBN(0x0f20f21a, 0x1d8db0ae), TOBN(0xd96d095f, 0x56c35e12),
+     TOBN(0xedf402b5, 0xf8c2a25b), TOBN(0x1bb772b9, 0x059204b6),
+     TOBN(0x50cbeae2, 0x19b4e34c), TOBN(0x93109d80, 0x3fa0845a),
+     TOBN(0x54f7ccf7, 0x8ef59fb5), TOBN(0x3b438fe2, 0x88070963),
+     TOBN(0x9e28c659, 0x31f3ba9b), TOBN(0x9cc31b46, 0xead9da92),
+     TOBN(0x3c2f0ba9, 0xb733aa5f), TOBN(0xdece47cb, 0xf05af235),
+     TOBN(0xf8e3f715, 0xa2ac82a5), TOBN(0xc97ba641, 0x2203f18a),
+     TOBN(0xc3af5504, 0x09c11060), TOBN(0x56ea2c05, 0x46af512d),
+     TOBN(0xfac28daf, 0xf3f28146), TOBN(0x87fab43a, 0x959ef494),}
+    ,
+    {TOBN(0x09891641, 0xd4c5105f), TOBN(0x1ae80f8e, 0x6d7fbd65),
+     TOBN(0x9d67225f, 0xbee6bdb0), TOBN(0x3b433b59, 0x7fc4d860),
+     TOBN(0x44e66db6, 0x93e85638), TOBN(0xf7b59252, 0xe3e9862f),
+     TOBN(0xdb785157, 0x665c32ec), TOBN(0x702fefd7, 0xae362f50),
+     TOBN(0x3754475d, 0x0fefb0c3), TOBN(0xd48fb56b, 0x46d7c35d),
+     TOBN(0xa070b633, 0x363798a4), TOBN(0xae89f3d2, 0x8fdb98e6),
+     TOBN(0x970b89c8, 0x6363d14c), TOBN(0x89817521, 0x67abd27d),
+     TOBN(0x9bf7d474, 0x44d5a021), TOBN(0xb3083baf, 0xcac72aee),
+     TOBN(0x389741de, 0xbe949a44), TOBN(0x638e9388, 0x546a4fa5),
+     TOBN(0x3fe6419c, 0xa0047bdc), TOBN(0x7047f648, 0xaaea57ca),
+     TOBN(0x54e48a90, 0x41fbab17), TOBN(0xda8e0b28, 0x576bdba2),
+     TOBN(0xe807eebc, 0xc72afddc), TOBN(0x07d3336d, 0xf42577bf),
+     TOBN(0x62a8c244, 0xbfe20925), TOBN(0x91c19ac3, 0x8fdce867),
+     TOBN(0x5a96a5d5, 0xdd387063), TOBN(0x61d587d4, 0x21d324f6),
+     TOBN(0xe87673a2, 0xa37173ea), TOBN(0x23848008, 0x53778b65),
+     TOBN(0x10f8441e, 0x05bab43e), TOBN(0xfa11fe12, 0x4621efbe),
+     TOBN(0x047b772e, 0x81685d7b), TOBN(0x23f27d81, 0xbf34a976),
+     TOBN(0xc27608e2, 0x915f48ef), TOBN(0x3b0b43fa, 0xa521d5c3),
+     TOBN(0x7613fb26, 0x63ca7284), TOBN(0x7f5729b4, 0x1d4db837),
+     TOBN(0x87b14898, 0x583b526b), TOBN(0x00b732a6, 0xbbadd3d1),
+     TOBN(0x8e02f426, 0x2048e396), TOBN(0x436b50b6, 0x383d9de4),
+     TOBN(0xf78d3481, 0x471e85ad), TOBN(0x8b01ea6a, 0xd005c8d6),
+     TOBN(0xd3c7afee, 0x97015c07), TOBN(0x46cdf1a9, 0x4e3ba2ae),
+     TOBN(0x7a42e501, 0x83d3a1d2), TOBN(0xd54b5268, 0xb541dff4),
+     TOBN(0x3f24cf30, 0x4e23e9bc), TOBN(0x4387f816, 0x126e3624),
+     TOBN(0x26a46a03, 0x3b0b6d61), TOBN(0xaf1bc845, 0x8b2d777c),
+     TOBN(0x25c401ba, 0x527de79c), TOBN(0x0e1346d4, 0x4261bbb6),
+     TOBN(0x4b96c44b, 0x287b4bc7), TOBN(0x658493c7, 0x5254562f),
+     TOBN(0x23f949fe, 0xb8a24a20), TOBN(0x17ebfed1, 0xf52ca53f),
+     TOBN(0x9b691bbe, 0xbcfb4853), TOBN(0x5617ff6b, 0x6278a05d),
+     TOBN(0x241b34c5, 0xe3c99ebd), TOBN(0xfc64242e, 0x1784156a),
+     TOBN(0x4206482f, 0x695d67df), TOBN(0xb967ce0e, 0xee27c011),
+     TOBN(0x65db3751, 0x21c80b5d), TOBN(0x2e7a563c, 0xa31ecca0),
+     TOBN(0xe56ffc4e, 0x5238a07e), TOBN(0x3d6c2966, 0x32ced854),
+     TOBN(0xe99d7d1a, 0xaf70b885), TOBN(0xafc3bad9, 0x2d686459),
+     TOBN(0x9c78bf46, 0x0cc8ba5b), TOBN(0x5a439519, 0x18955aa3),
+     TOBN(0xf8b517a8, 0x5fe4e314), TOBN(0xe60234d0, 0xfcb8906f),
+     TOBN(0xffe542ac, 0xf2061b23), TOBN(0x287e191f, 0x6b4cb59c),
+     TOBN(0x21857ddc, 0x09d877d8), TOBN(0x1c23478c, 0x14678941),
+     TOBN(0xbbf0c056, 0xb6e05ea4), TOBN(0x82da4b53, 0xb01594fe),
+     TOBN(0xf7526791, 0xfadb8608), TOBN(0x049e832d, 0x7b74cdf6),
+     TOBN(0xa43581cc, 0xc2b90a34), TOBN(0x73639eb8, 0x9360b10c),
+     TOBN(0x4fba331f, 0xe1e4a71b), TOBN(0x6ffd6b93, 0x8072f919),
+     TOBN(0x6e53271c, 0x65679032), TOBN(0x67206444, 0xf14272ce),
+     TOBN(0xc0f734a3, 0xb2335834), TOBN(0x9526205a, 0x90ef6860),
+     TOBN(0xcb8be717, 0x04e2bb0d), TOBN(0x2418871e, 0x02f383fa),
+     TOBN(0xd7177681, 0x4082c157), TOBN(0xcc914ad0, 0x29c20073),
+     TOBN(0xf186c1eb, 0xe587e728), TOBN(0x6fdb3c22, 0x61bcd5fd),
+     TOBN(0x30d014a6, 0xf2f9f8e9), TOBN(0x963ece23, 0x4fec49d2),
+     TOBN(0x862025c5, 0x9605a8d9), TOBN(0x39874445, 0x19f8929a),
+     TOBN(0x01b6ff65, 0x12bf476a), TOBN(0x598a64d8, 0x09cf7d91),
+     TOBN(0xd7ec7749, 0x93be56ca), TOBN(0x10899785, 0xcbb33615),
+     TOBN(0xb8a092fd, 0x02eee3ad), TOBN(0xa86b3d35, 0x30145270),
+     TOBN(0x323d98c6, 0x8512b675), TOBN(0x4b8bc785, 0x62ebb40f),
+     TOBN(0x7d301f54, 0x413f9cde), TOBN(0xa5e4fb4f, 0x2bab5664),
+     TOBN(0x1d2b252d, 0x1cbfec23), TOBN(0xfcd576bb, 0xe177120d),
+     TOBN(0x04427d3e, 0x83731a34), TOBN(0x2bb9028e, 0xed836e8e),
+     TOBN(0xb36acff8, 0xb612ca7c), TOBN(0xb88fe5ef, 0xd3d9c73a),
+     TOBN(0xbe2a6bc6, 0xedea4eb3), TOBN(0x43b93133, 0x488eec77),
+     TOBN(0xf41ff566, 0xb17106e1), TOBN(0x469e9172, 0x654efa32),
+     TOBN(0xb4480f04, 0x41c23fa3), TOBN(0xb4712eb0, 0xc1989a2e),
+     TOBN(0x3ccbba0f, 0x93a29ca7), TOBN(0x6e205c14, 0xd619428c),
+     TOBN(0x90db7957, 0xb3641686), TOBN(0x0432691d, 0x45ac8b4e),
+     TOBN(0x07a759ac, 0xf64e0350), TOBN(0x0514d89c, 0x9c972517),
+     TOBN(0x1701147f, 0xa8e67fc3), TOBN(0x9e2e0b8b, 0xab2085be),
+     TOBN(0xd5651824, 0xac284e57), TOBN(0x890d4325, 0x74893664),
+     TOBN(0x8a7c5e6e, 0xc55e68a3), TOBN(0xbf12e90b, 0x4339c85a),
+     TOBN(0x31846b85, 0xf922b655), TOBN(0x9a54ce4d, 0x0bf4d700),
+     TOBN(0xd7f4e83a, 0xf1a14295), TOBN(0x916f955c, 0xb285d4f9),
+     TOBN(0xe57bb0e0, 0x99ffdaba), TOBN(0x28a43034, 0xeab0d152),
+     TOBN(0x0a36ffa2, 0xb8a9cef8), TOBN(0x5517407e, 0xb9ec051a),
+     TOBN(0x9c796096, 0xea68e672), TOBN(0x853db5fb, 0xfb3c77fb),
+     TOBN(0x21474ba9, 0xe864a51a), TOBN(0x6c267699, 0x6e8a1b8b),
+     TOBN(0x7c823626, 0x94120a28), TOBN(0xe61e9a48, 0x8383a5db),
+     TOBN(0x7dd75003, 0x9f84216d), TOBN(0xab020d07, 0xad43cd85),
+     TOBN(0x9437ae48, 0xda12c659), TOBN(0x6449c2eb, 0xe65452ad),
+     TOBN(0xcc7c4c1c, 0x2cf9d7c1), TOBN(0x1320886a, 0xee95e5ab),
+     TOBN(0xbb7b9056, 0xbeae170c), TOBN(0xc8a5b250, 0xdbc0d662),
+     TOBN(0x4ed81432, 0xc11d2303), TOBN(0x7da66912, 0x1f03769f),
+     TOBN(0x3ac7a5fd, 0x84539828), TOBN(0x14dada94, 0x3bccdd02),
+     TOBN(0x8b84c321, 0x7ef6b0d1), TOBN(0x52a9477a, 0x7c933f22),
+     TOBN(0x5ef6728a, 0xfd440b82), TOBN(0x5c3bd859, 0x6ce4bd5e),
+     TOBN(0x918b80f5, 0xf22c2d3e), TOBN(0x368d5040, 0xb7bb6cc5),
+     TOBN(0xb66142a1, 0x2695a11c), TOBN(0x60ac583a, 0xeb19ea70),
+     TOBN(0x317cbb98, 0x0eab2437), TOBN(0x8cc08c55, 0x5e2654c8),
+     TOBN(0xfe2d6520, 0xe6d8307f), TOBN(0xe9f147f3, 0x57428993),
+     TOBN(0x5f9c7d14, 0xd2fd6cf1), TOBN(0xa3ecd064, 0x2d4fcbb0),
+     TOBN(0xad83fef0, 0x8e7341f7), TOBN(0x643f23a0, 0x3a63115c),
+     TOBN(0xd38a78ab, 0xe65ab743), TOBN(0xbf7c75b1, 0x35edc89c),
+     TOBN(0x3dd8752e, 0x530df568), TOBN(0xf85c4a76, 0xe308c682),
+     TOBN(0x4c9955b2, 0xe68acf37), TOBN(0xa544df3d, 0xab32af85),
+     TOBN(0x4b8ec3f5, 0xa25cf493), TOBN(0x4d8f2764, 0x1a622feb),
+     TOBN(0x7bb4f7aa, 0xf0dcbc49), TOBN(0x7de551f9, 0x70bbb45b),
+     TOBN(0xcfd0f3e4, 0x9f2ca2e5), TOBN(0xece58709, 0x1f5c76ef),
+     TOBN(0x32920edd, 0x167d79ae), TOBN(0x039df8a2, 0xfa7d7ec1),
+     TOBN(0xf46206c0, 0xbb30af91), TOBN(0x1ff5e2f5, 0x22676b59),
+     TOBN(0x11f4a039, 0x6ea51d66), TOBN(0x506c1445, 0x807d7a26),
+     TOBN(0x60da5705, 0x755a9b24), TOBN(0x8fc8cc32, 0x1f1a319e),
+     TOBN(0x83642d4d, 0x9433d67d), TOBN(0x7fa5cb8f, 0x6a7dd296),
+     TOBN(0x576591db, 0x9b7bde07), TOBN(0x13173d25, 0x419716fb),
+     TOBN(0xea30599d, 0xd5b340ff), TOBN(0xfc6b5297, 0xb0fe76c5),
+     TOBN(0x1c6968c8, 0xab8f5adc), TOBN(0xf723c7f5, 0x901c928d),
+     TOBN(0x4203c321, 0x9773d402), TOBN(0xdf7c6aa3, 0x1b51dd47),
+     TOBN(0x3d49e37a, 0x552be23c), TOBN(0x57febee8, 0x0b5a6e87),
+     TOBN(0xc5ecbee4, 0x7bd8e739), TOBN(0x79d44994, 0xae63bf75),
+     TOBN(0x168bd00f, 0x38fb8923), TOBN(0x75d48ee4, 0xd0533130),
+     TOBN(0x554f77aa, 0xdb5cdf33), TOBN(0x3396e896, 0x3c696769),
+     TOBN(0x2fdddbf2, 0xd3fd674e), TOBN(0xbbb8f6ee, 0x99d0e3e5),
+     TOBN(0x51b90651, 0xcbae2f70), TOBN(0xefc4bc05, 0x93aaa8eb),
+     TOBN(0x8ecd8689, 0xdd1df499), TOBN(0x1aee99a8, 0x22f367a5),
+     TOBN(0x95d485b9, 0xae8274c5), TOBN(0x6c14d445, 0x7d30b39c),
+     TOBN(0xbafea90b, 0xbcc1ef81), TOBN(0x7c5f317a, 0xa459a2ed),
+     TOBN(0x01211075, 0x4ef44227), TOBN(0xa17bed6e, 0xdc20f496),
+     TOBN(0x0cdfe424, 0x819853cd), TOBN(0x13793298, 0xf71e2ce7),
+     TOBN(0x3c1f3078, 0xdbbe307b), TOBN(0x6dd1c20e, 0x76ee9936),
+     TOBN(0x23ee4b57, 0x423caa20), TOBN(0x4ac3793b, 0x8efb840e),
+     TOBN(0x934438eb, 0xed1f8ca0), TOBN(0x3e546658, 0x4ebb25a2),
+     TOBN(0xc415af0e, 0xc069896f), TOBN(0xc13eddb0, 0x9a5aa43d),
+     TOBN(0x7a04204f, 0xd49eb8f6), TOBN(0xd0d5bdfc, 0xd74f1670),
+     TOBN(0x3697e286, 0x56fc0558), TOBN(0x10207371, 0x01cebade),
+     TOBN(0x5f87e690, 0x0647a82b), TOBN(0x908e0ed4, 0x8f40054f),
+     TOBN(0xa9f633d4, 0x79853803), TOBN(0x8ed13c9a, 0x4a28b252),
+     TOBN(0x3e2ef676, 0x1f460f64), TOBN(0x53930b9b, 0x36d06336),
+     TOBN(0x347073ac, 0x8fc4979b), TOBN(0x84380e0e, 0x5ecd5597),
+     TOBN(0xe3b22c6b, 0xc4fe3c39), TOBN(0xba4a8153, 0x6c7bebdf),
+     TOBN(0xf23ab6b7, 0x25693459), TOBN(0x53bc3770, 0x14922b11),
+     TOBN(0x4645c8ab, 0x5afc60db), TOBN(0xaa022355, 0x20b9f2a3),
+     TOBN(0x52a2954c, 0xce0fc507), TOBN(0x8c2731bb, 0x7ce1c2e7),
+     TOBN(0xf39608ab, 0x18a0339d), TOBN(0xac7a658d, 0x3735436c),
+     TOBN(0xb22c2b07, 0xcd992b4f), TOBN(0x4e83daec, 0xf40dcfd4),
+     TOBN(0x8a34c7be, 0x2f39ea3e), TOBN(0xef0c005f, 0xb0a56d2e),
+     TOBN(0x62731f6a, 0x6edd8038), TOBN(0x5721d740, 0x4e3cb075),
+     TOBN(0x1ea41511, 0xfbeeee1b), TOBN(0xd1ef5e73, 0xef1d0c05),
+     TOBN(0x42feefd1, 0x73c07d35), TOBN(0xe530a00a, 0x8a329493),
+     TOBN(0x5d55b7fe, 0xf15ebfb0), TOBN(0x549de03c, 0xd322491a),
+     TOBN(0xf7b5f602, 0x745b3237), TOBN(0x3632a3a2, 0x1ab6e2b6),
+     TOBN(0x0d3bba89, 0x0ef59f78), TOBN(0x0dfc6443, 0xc9e52b9a),
+     TOBN(0x1dc79699, 0x72631447), TOBN(0xef033917, 0xb3be20b1),
+     TOBN(0x0c92735d, 0xb1383948), TOBN(0xc1fc29a2, 0xc0dd7d7d),
+     TOBN(0x6485b697, 0x403ed068), TOBN(0x13bfaab3, 0xaac93bdc),
+     TOBN(0x410dc6a9, 0x0deeaf52), TOBN(0xb003fb02, 0x4c641c15),
+     TOBN(0x1384978c, 0x5bc504c4), TOBN(0x37640487, 0x864a6a77),
+     TOBN(0x05991bc6, 0x222a77da), TOBN(0x62260a57, 0x5e47eb11),
+     TOBN(0xc7af6613, 0xf21b432c), TOBN(0x22f3acc9, 0xab4953e9),
+     TOBN(0x52934922, 0x8e41d155), TOBN(0x4d024568, 0x3ac059ef),
+     TOBN(0xb0201755, 0x4d884411), TOBN(0xce8055cf, 0xa59a178f),
+     TOBN(0xcd77d1af, 0xf6204549), TOBN(0xa0a00a3e, 0xc7066759),
+     TOBN(0x471071ef, 0x0272c229), TOBN(0x009bcf6b, 0xd3c4b6b0),
+     TOBN(0x2a2638a8, 0x22305177), TOBN(0xd51d59df, 0x41645bbf),
+     TOBN(0xa81142fd, 0xc0a7a3c0), TOBN(0xa17eca6d, 0x4c7063ee),
+     TOBN(0x0bb887ed, 0x60d9dcec), TOBN(0xd6d28e51, 0x20ad2455),
+     TOBN(0xebed6308, 0xa67102ba), TOBN(0x042c3114, 0x8bffa408),
+     TOBN(0xfd099ac5, 0x8aa68e30), TOBN(0x7a6a3d7c, 0x1483513e),
+     TOBN(0xffcc6b75, 0xba2d8f0c), TOBN(0x54dacf96, 0x1e78b954),
+     TOBN(0xf645696f, 0xa4a9af89), TOBN(0x3a411940, 0x06ac98ec),
+     TOBN(0x41b8b3f6, 0x22a67a20), TOBN(0x2d0b1e0f, 0x99dec626),
+     TOBN(0x27c89192, 0x40be34e8), TOBN(0xc7162b37, 0x91907f35),
+     TOBN(0x90188ec1, 0xa956702b), TOBN(0xca132f7d, 0xdf93769c),
+     TOBN(0x3ece44f9, 0x0e2025b4), TOBN(0x67aaec69, 0x0c62f14c),
+     TOBN(0xad741418, 0x22e3cc11), TOBN(0xcf9b75c3, 0x7ff9a50e),
+     TOBN(0x02fa2b16, 0x4d348272), TOBN(0xbd99d61a, 0x9959d56d),
+     TOBN(0xbc4f19db, 0x18762916), TOBN(0xcc7cce50, 0x49c1ac80),
+     TOBN(0x4d59ebaa, 0xd846bd83), TOBN(0x8775a9dc, 0xa9202849),
+     TOBN(0x07ec4ae1, 0x6e1f4ca9), TOBN(0x27eb5875, 0xba893f11),
+     TOBN(0x00284d51, 0x662cc565), TOBN(0x82353a6b, 0x0db4138d),
+     TOBN(0xd9c7aaaa, 0xaa32a594), TOBN(0xf5528b5e, 0xa5669c47),
+     TOBN(0xf3220231, 0x2f23c5ff), TOBN(0xe3e8147a, 0x6affa3a1),
+     TOBN(0xfb423d5c, 0x202ddda0), TOBN(0x3d6414ac, 0x6b871bd4),
+     TOBN(0x586f82e1, 0xa51a168a), TOBN(0xb712c671, 0x48ae5448),
+     TOBN(0x9a2e4bd1, 0x76233eb8), TOBN(0x0188223a, 0x78811ca9),
+     TOBN(0x553c5e21, 0xf7c18de1), TOBN(0x7682e451, 0xb27bb286),
+     TOBN(0x3ed036b3, 0x0e51e929), TOBN(0xf487211b, 0xec9cb34f),
+     TOBN(0x0d094277, 0x0c24efc8), TOBN(0x0349fd04, 0xbef737a4),
+     TOBN(0x6d1c9dd2, 0x514cdd28), TOBN(0x29c135ff, 0x30da9521),
+     TOBN(0xea6e4508, 0xf78b0b6f), TOBN(0x176f5dd2, 0x678c143c),
+     TOBN(0x08148418, 0x4be21e65), TOBN(0x27f7525c, 0xe7df38c4),
+     TOBN(0x1fb70e09, 0x748ab1a4), TOBN(0x9cba50a0, 0x5efe4433),
+     TOBN(0x7846c7a6, 0x15f75af2), TOBN(0x2a7c2c57, 0x5ee73ea8),
+     TOBN(0x42e566a4, 0x3f0a449a), TOBN(0x45474c3b, 0xad90fc3d),
+     TOBN(0x7447be3d, 0x8b61d057), TOBN(0x3e9d1cf1, 0x3a4ec092),
+     TOBN(0x1603e453, 0xf380a6e6), TOBN(0x0b86e431, 0x9b1437c2),
+     TOBN(0x7a4173f2, 0xef29610a), TOBN(0x8fa729a7, 0xf03d57f7),
+     TOBN(0x3e186f6e, 0x6c9c217e), TOBN(0xbe1d3079, 0x91919524),
+     TOBN(0x92a62a70, 0x153d4fb1), TOBN(0x32ed3e34, 0xd68c2f71),
+     TOBN(0xd785027f, 0x9eb1a8b7), TOBN(0xbc37eb77, 0xc5b22fe8),
+     TOBN(0x466b34f0, 0xb9d6a191), TOBN(0x008a89af, 0x9a05f816),
+     TOBN(0x19b028fb, 0x7d42c10a), TOBN(0x7fe8c92f, 0x49b3f6b8),
+     TOBN(0x58907cc0, 0xa5a0ade3), TOBN(0xb3154f51, 0x559d1a7c),
+     TOBN(0x5066efb6, 0xd9790ed6), TOBN(0xa77a0cbc, 0xa6aa793b),
+     TOBN(0x1a915f3c, 0x223e042e), TOBN(0x1c5def04, 0x69c5874b),
+     TOBN(0x0e830078, 0x73b6c1da), TOBN(0x55cf85d2, 0xfcd8557a),
+     TOBN(0x0f7c7c76, 0x0460f3b1), TOBN(0x87052acb, 0x46e58063),
+     TOBN(0x09212b80, 0x907eae66), TOBN(0x3cb068e0, 0x4d721c89),
+     TOBN(0xa87941ae, 0xdd45ac1c), TOBN(0xde8d5c0d, 0x0daa0dbb),
+     TOBN(0xda421fdc, 0xe3502e6e), TOBN(0xc8944201, 0x4d89a084),
+     TOBN(0x7307ba5e, 0xf0c24bfb), TOBN(0xda212beb, 0x20bde0ef),
+     TOBN(0xea2da24b, 0xf82ce682), TOBN(0x058d3816, 0x07f71fe4),
+     TOBN(0x35a02462, 0x5ffad8de), TOBN(0xcd7b05dc, 0xaadcefab),
+     TOBN(0xd442f8ed, 0x1d9f54ec), TOBN(0x8be3d618, 0xb2d3b5ca),
+     TOBN(0xe2220ed0, 0xe06b2ce2), TOBN(0x82699a5f, 0x1b0da4c0),
+     TOBN(0x3ff106f5, 0x71c0c3a7), TOBN(0x8f580f5a, 0x0d34180c),
+     TOBN(0x4ebb120e, 0x22d7d375), TOBN(0x5e5782cc, 0xe9513675),
+     TOBN(0x2275580c, 0x99c82a70), TOBN(0xe8359fbf, 0x15ea8c4c),
+     TOBN(0x53b48db8, 0x7b415e70), TOBN(0xaacf2240, 0x100c6014),
+     TOBN(0x9faaccf5, 0xe4652f1d), TOBN(0xbd6fdd2a, 0xd56157b2),
+     TOBN(0xa4f4fb1f, 0x6261ec50), TOBN(0x244e55ad, 0x476bcd52),
+     TOBN(0x881c9305, 0x047d320b), TOBN(0x1ca983d5, 0x6181263f),
+     TOBN(0x354e9a44, 0x278fb8ee), TOBN(0xad2dbc0f, 0x396e4964),
+     TOBN(0x723f3aa2, 0x9268b3de), TOBN(0x0d1ca29a, 0xe6e0609a),
+     TOBN(0x794866aa, 0x6cf44252), TOBN(0x0b59f3e3, 0x01af87ed),
+     TOBN(0xe234e5ff, 0x7f4a6c51), TOBN(0xa8768fd2, 0x61dc2f7e),
+     TOBN(0xdafc7332, 0x0a94d81f), TOBN(0xd7f84282, 0x06938ce1),
+     TOBN(0xae0b3c0e, 0x0546063e), TOBN(0x7fbadcb2, 0x5d61abc6),
+     TOBN(0xd5d7a2c9, 0x369ac400), TOBN(0xa5978d09, 0xae67d10c),
+     TOBN(0x290f211e, 0x4f85eaac), TOBN(0xe61e2ad1, 0xfacac681),
+     TOBN(0xae125225, 0x388384cd), TOBN(0xa7fb68e9, 0xccfde30f),
+     TOBN(0x7a59b936, 0x3daed4c2), TOBN(0x80a9aa40, 0x2606f789),
+     TOBN(0xb40c1ea5, 0xf6a6d90a), TOBN(0x948364d3, 0x514d5885),
+     TOBN(0x062ebc60, 0x70985182), TOBN(0xa6db5b0e, 0x33310895),
+     TOBN(0x64a12175, 0xe329c2f5), TOBN(0xc5f25bd2, 0x90ea237e),
+     TOBN(0x7915c524, 0x2d0a4c23), TOBN(0xeb5d26e4, 0x6bb3cc52),
+     TOBN(0x369a9116, 0xc09e2c92), TOBN(0x0c527f92, 0xcf182cf8),
+     TOBN(0x9e591938, 0x2aede0ac), TOBN(0xb2922208, 0x6cc34939),
+     TOBN(0x3c9d8962, 0x99a34361), TOBN(0x3c81836d, 0xc1905fe6),
+     TOBN(0x4bfeb57f, 0xa001ec5a), TOBN(0xe993f5bb, 0xa0dc5dba),
+     TOBN(0x47884109, 0x724a1380), TOBN(0x8a0369ab, 0x32fe9a04),
+     TOBN(0xea068d60, 0x8c927db8), TOBN(0xbf5f37cf, 0x94655741),
+     TOBN(0x47d402a2, 0x04b6c7ea), TOBN(0x4551c295, 0x6af259cb),
+     TOBN(0x698b71e7, 0xed77ee8b), TOBN(0xbddf7bd0, 0xf309d5c7),
+     TOBN(0x6201c22c, 0x34e780ca), TOBN(0xab04f7d8, 0x4c295ef4),
+     TOBN(0x1c947294, 0x4313a8ce), TOBN(0xe532e4ac, 0x92ca4cfe),
+     TOBN(0x89738f80, 0xd0a7a97a), TOBN(0xec088c88, 0xa580fd5b),
+     TOBN(0x612b1ecc, 0x42ce9e51), TOBN(0x8f9840fd, 0xb25fdd2a),
+     TOBN(0x3cda78c0, 0x01e7f839), TOBN(0x546b3d3a, 0xece05480),
+     TOBN(0x271719a9, 0x80d30916), TOBN(0x45497107, 0x584c20c4),
+     TOBN(0xaf8f9478, 0x5bc78608), TOBN(0x28c7d484, 0x277e2a4c),
+     TOBN(0xfce01767, 0x88a2ffe4), TOBN(0xdc506a35, 0x28e169a5),
+     TOBN(0x0ea10861, 0x7af9c93a), TOBN(0x1ed24361, 0x03fa0e08),
+     TOBN(0x96eaaa92, 0xa3d694e7), TOBN(0xc0f43b4d, 0xef50bc74),
+     TOBN(0xce6aa58c, 0x64114db4), TOBN(0x8218e8ea, 0x7c000fd4),
+     TOBN(0xac815dfb, 0x185f8844), TOBN(0xcd7e90cb, 0x1557abfb),
+     TOBN(0x23d16655, 0xafbfecdf), TOBN(0x80f3271f, 0x085cac4a),
+     TOBN(0x7fc39aa7, 0xd0e62f47), TOBN(0x88d519d1, 0x460a48e5),
+     TOBN(0x59559ac4, 0xd28f101e), TOBN(0x7981d9e9, 0xca9ae816),
+     TOBN(0x5c38652c, 0x9ac38203), TOBN(0x86eaf87f, 0x57657fe5),
+     TOBN(0x568fc472, 0xe21f5416), TOBN(0x2afff39c, 0xe7e597b5),
+     TOBN(0x3adbbb07, 0x256d4eab), TOBN(0x22598692, 0x8285ab89),
+     TOBN(0x35f8112a, 0x041caefe), TOBN(0x95df02e3, 0xa5064c8b),
+     TOBN(0x4d63356e, 0xc7004bf3), TOBN(0x230a08f4, 0xdb83c7de),
+     TOBN(0xca27b270, 0x8709a7b7), TOBN(0x0d1c4cc4, 0xcb9abd2d),
+     TOBN(0x8a0bc66e, 0x7550fee8), TOBN(0x369cd4c7, 0x9cf7247e),
+     TOBN(0x75562e84, 0x92b5b7e7), TOBN(0x8fed0da0, 0x5802af7b),
+     TOBN(0x6a7091c2, 0xe48fb889), TOBN(0x26882c13, 0x7b8a9d06),
+     TOBN(0xa2498663, 0x1b82a0e2), TOBN(0x844ed736, 0x3518152d),
+     TOBN(0x282f476f, 0xd86e27c7), TOBN(0xa04edaca, 0x04afefdc),
+     TOBN(0x8b256ebc, 0x6119e34d), TOBN(0x56a413e9, 0x0787d78b),}
+    ,
+    {TOBN(0x82ee061d, 0x5a74be50), TOBN(0xe41781c4, 0xdea16ff5),
+     TOBN(0xe0b0c81e, 0x99bfc8a2), TOBN(0x624f4d69, 0x0b547e2d),
+     TOBN(0x3a83545d, 0xbdcc9ae4), TOBN(0x2573dbb6, 0x409b1e8e),
+     TOBN(0x482960c4, 0xa6c93539), TOBN(0xf01059ad, 0x5ae18798),
+     TOBN(0x715c9f97, 0x3112795f), TOBN(0xe8244437, 0x984e6ee1),
+     TOBN(0x55cb4858, 0xecb66bcd), TOBN(0x7c136735, 0xabaffbee),
+     TOBN(0x54661595, 0x5dbec38e), TOBN(0x51c0782c, 0x388ad153),
+     TOBN(0x9ba4c53a, 0xc6e0952f), TOBN(0x27e6782a, 0x1b21dfa8),
+     TOBN(0x682f903d, 0x4ed2dbc2), TOBN(0x0eba59c8, 0x7c3b2d83),
+     TOBN(0x8e9dc84d, 0x9c7e9335), TOBN(0x5f9b21b0, 0x0eb226d7),
+     TOBN(0xe33bd394, 0xaf267bae), TOBN(0xaa86cc25, 0xbe2e15ae),
+     TOBN(0x4f0bf67d, 0x6a8ec500), TOBN(0x5846aa44, 0xf9630658),
+     TOBN(0xfeb09740, 0xe2c2bf15), TOBN(0x627a2205, 0xa9e99704),
+     TOBN(0xec8d73d0, 0xc2fbc565), TOBN(0x223eed8f, 0xc20c8de8),
+     TOBN(0x1ee32583, 0xa8363b49), TOBN(0x1a0b6cb9, 0xc9c2b0a6),
+     TOBN(0x49f7c3d2, 0x90dbc85c), TOBN(0xa8dfbb97, 0x1ef4c1ac),
+     TOBN(0xafb34d4c, 0x65c7c2ab), TOBN(0x1d4610e7, 0xe2c5ea84),
+     TOBN(0x893f6d1b, 0x973c4ab5), TOBN(0xa3cdd7e9, 0x945ba5c4),
+     TOBN(0x60514983, 0x064417ee), TOBN(0x1459b23c, 0xad6bdf2b),
+     TOBN(0x23b2c341, 0x5cf726c3), TOBN(0x3a829635, 0x32d6354a),
+     TOBN(0x294f901f, 0xab192c18), TOBN(0xec5fcbfe, 0x7030164f),
+     TOBN(0xe2e2fcb7, 0xe2246ba6), TOBN(0x1e7c88b3, 0x221a1a0c),
+     TOBN(0x72c7dd93, 0xc92d88c5), TOBN(0x41c2148e, 0x1106fb59),
+     TOBN(0x547dd4f5, 0xa0f60f14), TOBN(0xed9b52b2, 0x63960f31),
+     TOBN(0x6c8349eb, 0xb0a5b358), TOBN(0xb154c5c2, 0x9e7e2ed6),
+     TOBN(0xcad5eccf, 0xeda462db), TOBN(0xf2d6dbe4, 0x2de66b69),
+     TOBN(0x426aedf3, 0x8665e5b2), TOBN(0x488a8513, 0x7b7f5723),
+     TOBN(0x15cc43b3, 0x8bcbb386), TOBN(0x27ad0af3, 0xd791d879),
+     TOBN(0xc16c236e, 0x846e364f), TOBN(0x7f33527c, 0xdea50ca0),
+     TOBN(0xc4810775, 0x0926b86d), TOBN(0x6c2a3609, 0x0598e70c),
+     TOBN(0xa6755e52, 0xf024e924), TOBN(0xe0fa07a4, 0x9db4afca),
+     TOBN(0x15c3ce7d, 0x66831790), TOBN(0x5b4ef350, 0xa6cbb0d6),
+     TOBN(0x2c4aafc4, 0xb6205969), TOBN(0x42563f02, 0xf6c7854f),
+     TOBN(0x016aced5, 0x1d983b48), TOBN(0xfeb356d8, 0x99949755),
+     TOBN(0x8c2a2c81, 0xd1a39bd7), TOBN(0x8f44340f, 0xe6934ae9),
+     TOBN(0x148cf91c, 0x447904da), TOBN(0x7340185f, 0x0f51a926),
+     TOBN(0x2f8f00fb, 0x7409ab46), TOBN(0x057e78e6, 0x80e289b2),
+     TOBN(0x03e5022c, 0xa888e5d1), TOBN(0x3c87111a, 0x9dede4e2),
+     TOBN(0x5b9b0e1c, 0x7809460b), TOBN(0xe751c852, 0x71c9abc7),
+     TOBN(0x8b944e28, 0xc7cc1dc9), TOBN(0x4f201ffa, 0x1d3cfa08),
+     TOBN(0x02fc905c, 0x3e6721ce), TOBN(0xd52d70da, 0xd0b3674c),
+     TOBN(0x5dc2e5ca, 0x18810da4), TOBN(0xa984b273, 0x5c69dd99),
+     TOBN(0x63b92527, 0x84de5ca4), TOBN(0x2f1c9872, 0xc852dec4),
+     TOBN(0x18b03593, 0xc2e3de09), TOBN(0x19d70b01, 0x9813dc2f),
+     TOBN(0x42806b2d, 0xa6dc1d29), TOBN(0xd3030009, 0xf871e144),
+     TOBN(0xa1feb333, 0xaaf49276), TOBN(0xb5583b9e, 0xc70bc04b),
+     TOBN(0x1db0be78, 0x95695f20), TOBN(0xfc841811, 0x89d012b5),
+     TOBN(0x6409f272, 0x05f61643), TOBN(0x40d34174, 0xd5883128),
+     TOBN(0xd79196f5, 0x67419833), TOBN(0x6059e252, 0x863b7b08),
+     TOBN(0x84da1817, 0x1c56700c), TOBN(0x5758ee56, 0xb28d3ec4),
+     TOBN(0x7da2771d, 0x013b0ea6), TOBN(0xfddf524b, 0x54c5e9b9),
+     TOBN(0x7df4faf8, 0x24305d80), TOBN(0x58f5c1bf, 0x3a97763f),
+     TOBN(0xa5af37f1, 0x7c696042), TOBN(0xd4cba22c, 0x4a2538de),
+     TOBN(0x211cb995, 0x9ea42600), TOBN(0xcd105f41, 0x7b069889),
+     TOBN(0xb1e1cf19, 0xddb81e74), TOBN(0x472f2d89, 0x5157b8ca),
+     TOBN(0x086fb008, 0xee9db885), TOBN(0x365cd570, 0x0f26d131),
+     TOBN(0x284b02bb, 0xa2be7053), TOBN(0xdcbbf7c6, 0x7ab9a6d6),
+     TOBN(0x4425559c, 0x20f7a530), TOBN(0x961f2dfa, 0x188767c8),
+     TOBN(0xe2fd9435, 0x70dc80c4), TOBN(0x104d6b63, 0xf0784120),
+     TOBN(0x7f592bc1, 0x53567122), TOBN(0xf6bc1246, 0xf688ad77),
+     TOBN(0x05214c05, 0x0f15dde9), TOBN(0xa47a76a8, 0x0d5f2b82),
+     TOBN(0xbb254d30, 0x62e82b62), TOBN(0x11a05fe0, 0x3ec955ee),
+     TOBN(0x7eaff46e, 0x9d529b36), TOBN(0x55ab1301, 0x8f9e3df6),
+     TOBN(0xc463e371, 0x99317698), TOBN(0xfd251438, 0xccda47ad),
+     TOBN(0xca9c3547, 0x23d695ea), TOBN(0x48ce626e, 0x16e589b5),
+     TOBN(0x6b5b64c7, 0xb187d086), TOBN(0xd02e1794, 0xb2207948),
+     TOBN(0x8b58e98f, 0x7198111d), TOBN(0x90ca6305, 0xdcf9c3cc),
+     TOBN(0x5691fe72, 0xf34089b0), TOBN(0x60941af1, 0xfc7c80ff),
+     TOBN(0xa09bc0a2, 0x22eb51e5), TOBN(0xc0bb7244, 0xaa9cf09a),
+     TOBN(0x36a8077f, 0x80159f06), TOBN(0x8b5c989e, 0xdddc560e),
+     TOBN(0x19d2f316, 0x512e1f43), TOBN(0x02eac554, 0xad08ff62),
+     TOBN(0x012ab84c, 0x07d20b4e), TOBN(0x37d1e115, 0xd6d4e4e1),
+     TOBN(0xb6443e1a, 0xab7b19a8), TOBN(0xf08d067e, 0xdef8cd45),
+     TOBN(0x63adf3e9, 0x685e03da), TOBN(0xcf15a10e, 0x4792b916),
+     TOBN(0xf44bcce5, 0xb738a425), TOBN(0xebe131d5, 0x9636b2fd),
+     TOBN(0x94068841, 0x7850d605), TOBN(0x09684eaa, 0xb40d749d),
+     TOBN(0x8c3c669c, 0x72ba075b), TOBN(0x89f78b55, 0xba469015),
+     TOBN(0x5706aade, 0x3e9f8ba8), TOBN(0x6d8bd565, 0xb32d7ed7),
+     TOBN(0x25f4e63b, 0x805f08d6), TOBN(0x7f48200d, 0xc3bcc1b5),
+     TOBN(0x4e801968, 0xb025d847), TOBN(0x74afac04, 0x87cbe0a8),
+     TOBN(0x43ed2c2b, 0x7e63d690), TOBN(0xefb6bbf0, 0x0223cdb8),
+     TOBN(0x4fec3cae, 0x2884d3fe), TOBN(0x065ecce6, 0xd75e25a4),
+     TOBN(0x6c2294ce, 0x69f79071), TOBN(0x0d9a8e5f, 0x044b8666),
+     TOBN(0x5009f238, 0x17b69d8f), TOBN(0x3c29f8fe, 0xc5dfdaf7),
+     TOBN(0x9067528f, 0xebae68c4), TOBN(0x5b385632, 0x30c5ba21),
+     TOBN(0x540df119, 0x1fdd1aec), TOBN(0xcf37825b, 0xcfba4c78),
+     TOBN(0x77eff980, 0xbeb11454), TOBN(0x40a1a991, 0x60c1b066),
+     TOBN(0xe8018980, 0xf889a1c7), TOBN(0xb9c52ae9, 0x76c24be0),
+     TOBN(0x05fbbcce, 0x45650ef4), TOBN(0xae000f10, 0x8aa29ac7),
+     TOBN(0x884b7172, 0x4f04c470), TOBN(0x7cd4fde2, 0x19bb5c25),
+     TOBN(0x6477b22a, 0xe8840869), TOBN(0xa8868859, 0x5fbd0686),
+     TOBN(0xf23cc02e, 0x1116dfba), TOBN(0x76cd563f, 0xd87d7776),
+     TOBN(0xe2a37598, 0xa9d82abf), TOBN(0x5f188ccb, 0xe6c170f5),
+     TOBN(0x81682200, 0x5066b087), TOBN(0xda22c212, 0xc7155ada),
+     TOBN(0x151e5d3a, 0xfbddb479), TOBN(0x4b606b84, 0x6d715b99),
+     TOBN(0x4a73b54b, 0xf997cb2e), TOBN(0x9a1bfe43, 0x3ecd8b66),
+     TOBN(0x1c312809, 0x2a67d48a), TOBN(0xcd6a671e, 0x031fa9e2),
+     TOBN(0xbec3312a, 0x0e43a34a), TOBN(0x1d935639, 0x55ef47d3),
+     TOBN(0x5ea02489, 0x8fea73ea), TOBN(0x8247b364, 0xa035afb2),
+     TOBN(0xb58300a6, 0x5265b54c), TOBN(0x3286662f, 0x722c7148),
+     TOBN(0xb77fd76b, 0xb4ec4c20), TOBN(0xf0a12fa7, 0x0f3fe3fd),
+     TOBN(0xf845bbf5, 0x41d8c7e8), TOBN(0xe4d969ca, 0x5ec10aa8),
+     TOBN(0x4c0053b7, 0x43e232a3), TOBN(0xdc7a3fac, 0x37f8a45a),
+     TOBN(0x3c4261c5, 0x20d81c8f), TOBN(0xfd4b3453, 0xb00eab00),
+     TOBN(0x76d48f86, 0xd36e3062), TOBN(0x626c5277, 0xa143ff02),
+     TOBN(0x538174de, 0xaf76f42e), TOBN(0x2267aa86, 0x6407ceac),
+     TOBN(0xfad76351, 0x72e572d5), TOBN(0xab861af7, 0xba7330eb),
+     TOBN(0xa0a1c8c7, 0x418d8657), TOBN(0x988821cb, 0x20289a52),
+     TOBN(0x79732522, 0xcccc18ad), TOBN(0xaadf3f8d, 0xf1a6e027),
+     TOBN(0xf7382c93, 0x17c2354d), TOBN(0x5ce1680c, 0xd818b689),
+     TOBN(0x359ebbfc, 0xd9ecbee9), TOBN(0x4330689c, 0x1cae62ac),
+     TOBN(0xb55ce5b4, 0xc51ac38a), TOBN(0x7921dfea, 0xfe238ee8),
+     TOBN(0x3972bef8, 0x271d1ca5), TOBN(0x3e423bc7, 0xe8aabd18),
+     TOBN(0x57b09f3f, 0x44a3e5e3), TOBN(0x5da886ae, 0x7b444d66),
+     TOBN(0x68206634, 0xa9964375), TOBN(0x356a2fa3, 0x699cd0ff),
+     TOBN(0xaf0faa24, 0xdba515e9), TOBN(0x536e1f5c, 0xb321d79a),
+     TOBN(0xd3b9913a, 0x5c04e4ea), TOBN(0xd549dcfe, 0xd6f11513),
+     TOBN(0xee227bf5, 0x79fd1d94), TOBN(0x9f35afee, 0xb43f2c67),
+     TOBN(0xd2638d24, 0xf1314f53), TOBN(0x62baf948, 0xcabcd822),
+     TOBN(0x5542de29, 0x4ef48db0), TOBN(0xb3eb6a04, 0xfc5f6bb2),
+     TOBN(0x23c110ae, 0x1208e16a), TOBN(0x1a4d15b5, 0xf8363e24),
+     TOBN(0x30716844, 0x164be00b), TOBN(0xa8e24824, 0xf6f4690d),
+     TOBN(0x548773a2, 0x90b170cf), TOBN(0xa1bef331, 0x42f191f4),
+     TOBN(0x70f418d0, 0x9247aa97), TOBN(0xea06028e, 0x48be9147),
+     TOBN(0xe13122f3, 0xdbfb894e), TOBN(0xbe9b79f6, 0xce274b18),
+     TOBN(0x85a49de5, 0xca58aadf), TOBN(0x24957758, 0x11487351),
+     TOBN(0x111def61, 0xbb939099), TOBN(0x1d6a974a, 0x26d13694),
+     TOBN(0x4474b4ce, 0xd3fc253b), TOBN(0x3a1485e6, 0x4c5db15e),
+     TOBN(0xe79667b4, 0x147c15b4), TOBN(0xe34f553b, 0x7bc61301),
+     TOBN(0x032b80f8, 0x17094381), TOBN(0x55d8bafd, 0x723eaa21),
+     TOBN(0x5a987995, 0xf1c0e74e), TOBN(0x5a9b292e, 0xebba289c),
+     TOBN(0x413cd4b2, 0xeb4c8251), TOBN(0x98b5d243, 0xd162db0a),
+     TOBN(0xbb47bf66, 0x68342520), TOBN(0x08d68949, 0xbaa862d1),
+     TOBN(0x11f349c7, 0xe906abcd), TOBN(0x454ce985, 0xed7bf00e),
+     TOBN(0xacab5c9e, 0xb55b803b), TOBN(0xb03468ea, 0x31e3c16d),
+     TOBN(0x5c24213d, 0xd273bf12), TOBN(0x211538eb, 0x71587887),
+     TOBN(0x198e4a2f, 0x731dea2d), TOBN(0xd5856cf2, 0x74ed7b2a),
+     TOBN(0x86a632eb, 0x13a664fe), TOBN(0x932cd909, 0xbda41291),
+     TOBN(0x850e95d4, 0xc0c4ddc0), TOBN(0xc0f422f8, 0x347fc2c9),
+     TOBN(0xe68cbec4, 0x86076bcb), TOBN(0xf9e7c0c0, 0xcd6cd286),
+     TOBN(0x65994ddb, 0x0f5f27ca), TOBN(0xe85461fb, 0xa80d59ff),
+     TOBN(0xff05481a, 0x66601023), TOBN(0xc665427a, 0xfc9ebbfb),
+     TOBN(0xb0571a69, 0x7587fd52), TOBN(0x935289f8, 0x8d49efce),
+     TOBN(0x61becc60, 0xea420688), TOBN(0xb22639d9, 0x13a786af),
+     TOBN(0x1a8e6220, 0x361ecf90), TOBN(0x001f23e0, 0x25506463),
+     TOBN(0xe4ae9b5d, 0x0a5c2b79), TOBN(0xebc9cdad, 0xd8149db5),
+     TOBN(0xb33164a1, 0x934aa728), TOBN(0x750eb00e, 0xae9b60f3),
+     TOBN(0x5a91615b, 0x9b9cfbfd), TOBN(0x97015cbf, 0xef45f7f6),
+     TOBN(0xb462c4a5, 0xbf5151df), TOBN(0x21adcc41, 0xb07118f2),
+     TOBN(0xd60c545b, 0x043fa42c), TOBN(0xfc21aa54, 0xe96be1ab),
+     TOBN(0xe84bc32f, 0x4e51ea80), TOBN(0x3dae45f0, 0x259b5d8d),
+     TOBN(0xbb73c7eb, 0xc38f1b5e), TOBN(0xe405a74a, 0xe8ae617d),
+     TOBN(0xbb1ae9c6, 0x9f1c56bd), TOBN(0x8c176b98, 0x49f196a4),
+     TOBN(0xc448f311, 0x6875092b), TOBN(0xb5afe3de, 0x9f976033),
+     TOBN(0xa8dafd49, 0x145813e5), TOBN(0x687fc4d9, 0xe2b34226),
+     TOBN(0xf2dfc92d, 0x4c7ff57f), TOBN(0x004e3fc1, 0x401f1b46),
+     TOBN(0x5afddab6, 0x1430c9ab), TOBN(0x0bdd41d3, 0x2238e997),
+     TOBN(0xf0947430, 0x418042ae), TOBN(0x71f9adda, 0xcdddc4cb),
+     TOBN(0x7090c016, 0xc52dd907), TOBN(0xd9bdf44d, 0x29e2047f),
+     TOBN(0xe6f1fe80, 0x1b1011a6), TOBN(0xb63accbc, 0xd9acdc78),
+     TOBN(0xcfc7e235, 0x1272a95b), TOBN(0x0c667717, 0xa6276ac8),
+     TOBN(0x3c0d3709, 0xe2d7eef7), TOBN(0x5add2b06, 0x9a685b3e),
+     TOBN(0x363ad32d, 0x14ea5d65), TOBN(0xf8e01f06, 0x8d7dd506),
+     TOBN(0xc9ea2213, 0x75b4aac6), TOBN(0xed2a2bf9, 0x0d353466),
+     TOBN(0x439d79b5, 0xe9d3a7c3), TOBN(0x8e0ee5a6, 0x81b7f34b),
+     TOBN(0xcf3dacf5, 0x1dc4ba75), TOBN(0x1d3d1773, 0xeb3310c7),
+     TOBN(0xa8e67112, 0x7747ae83), TOBN(0x31f43160, 0x197d6b40),
+     TOBN(0x0521ccee, 0xcd961400), TOBN(0x67246f11, 0xf6535768),
+     TOBN(0x702fcc5a, 0xef0c3133), TOBN(0x247cc45d, 0x7e16693b),
+     TOBN(0xfd484e49, 0xc729b749), TOBN(0x522cef7d, 0xb218320f),
+     TOBN(0xe56ef405, 0x59ab93b3), TOBN(0x225fba11, 0x9f181071),
+     TOBN(0x33bd6595, 0x15330ed0), TOBN(0xc4be69d5, 0x1ddb32f7),
+     TOBN(0x264c7668, 0x0448087c), TOBN(0xac30903f, 0x71432dae),
+     TOBN(0x3851b266, 0x00f9bf47), TOBN(0x400ed311, 0x6cdd6d03),
+     TOBN(0x045e79fe, 0xf8fd2424), TOBN(0xfdfd974a, 0xfa6da98b),
+     TOBN(0x45c9f641, 0x0c1e673a), TOBN(0x76f2e733, 0x5b2c5168),
+     TOBN(0x1adaebb5, 0x2a601753), TOBN(0xb286514c, 0xc57c2d49),
+     TOBN(0xd8769670, 0x1e0bfd24), TOBN(0x950c547e, 0x04478922),
+     TOBN(0xd1d41969, 0xe5d32bfe), TOBN(0x30bc1472, 0x750d6c3e),
+     TOBN(0x8f3679fe, 0xe0e27f3a), TOBN(0x8f64a7dc, 0xa4a6ee0c),
+     TOBN(0x2fe59937, 0x633dfb1f), TOBN(0xea82c395, 0x977f2547),
+     TOBN(0xcbdfdf1a, 0x661ea646), TOBN(0xc7ccc591, 0xb9085451),
+     TOBN(0x82177962, 0x81761e13), TOBN(0xda57596f, 0x9196885c),
+     TOBN(0xbc17e849, 0x28ffbd70), TOBN(0x1e6e0a41, 0x2671d36f),
+     TOBN(0x61ae872c, 0x4152fcf5), TOBN(0x441c87b0, 0x9e77e754),
+     TOBN(0xd0799dd5, 0xa34dff09), TOBN(0x766b4e44, 0x88a6b171),
+     TOBN(0xdc06a512, 0x11f1c792), TOBN(0xea02ae93, 0x4be35c3e),
+     TOBN(0xe5ca4d6d, 0xe90c469e), TOBN(0x4df4368e, 0x56e4ff5c),
+     TOBN(0x7817acab, 0x4baef62e), TOBN(0x9f5a2202, 0xa85b91e8),
+     TOBN(0x9666ebe6, 0x6ce57610), TOBN(0x32ad31f3, 0xf73bfe03),
+     TOBN(0x628330a4, 0x25bcf4d6), TOBN(0xea950593, 0x515056e6),
+     TOBN(0x59811c89, 0xe1332156), TOBN(0xc89cf1fe, 0x8c11b2d7),
+     TOBN(0x75b63913, 0x04e60cc0), TOBN(0xce811e8d, 0x4625d375),
+     TOBN(0x030e43fc, 0x2d26e562), TOBN(0xfbb30b4b, 0x608d36a0),
+     TOBN(0x634ff82c, 0x48528118), TOBN(0x7c6fe085, 0xcd285911),
+     TOBN(0x7f2830c0, 0x99358f28), TOBN(0x2e60a95e, 0x665e6c09),
+     TOBN(0x08407d3d, 0x9b785dbf), TOBN(0x530889ab, 0xa759bce7),
+     TOBN(0xf228e0e6, 0x52f61239), TOBN(0x2b6d1461, 0x6879be3c),
+     TOBN(0xe6902c04, 0x51a7bbf7), TOBN(0x30ad99f0, 0x76f24a64),
+     TOBN(0x66d9317a, 0x98bc6da0), TOBN(0xf4f877f3, 0xcb596ac0),
+     TOBN(0xb05ff62d, 0x4c44f119), TOBN(0x4555f536, 0xe9b77416),
+     TOBN(0xc7c0d059, 0x8caed63b), TOBN(0x0cd2b7ce, 0xc358b2a9),
+     TOBN(0x3f33287b, 0x46945fa3), TOBN(0xf8785b20, 0xd67c8791),
+     TOBN(0xc54a7a61, 0x9637bd08), TOBN(0x54d4598c, 0x18be79d7),
+     TOBN(0x889e5acb, 0xc46d7ce1), TOBN(0x9a515bb7, 0x8b085877),
+     TOBN(0xfac1a03d, 0x0b7a5050), TOBN(0x7d3e738a, 0xf2926035),
+     TOBN(0x861cc2ce, 0x2a6cb0eb), TOBN(0x6f2e2955, 0x8f7adc79),
+     TOBN(0x61c4d451, 0x33016376), TOBN(0xd9fd2c80, 0x5ad59090),
+     TOBN(0xe5a83738, 0xb2b836a1), TOBN(0x855b41a0, 0x7c0d6622),
+     TOBN(0x186fe317, 0x7cc19af1), TOBN(0x6465c1ff, 0xfdd99acb),
+     TOBN(0x46e5c23f, 0x6974b99e), TOBN(0x75a7cf8b, 0xa2717cbe),
+     TOBN(0x4d2ebc3f, 0x062be658), TOBN(0x094b4447, 0x5f209c98),
+     TOBN(0x4af285ed, 0xb940cb5a), TOBN(0x6706d792, 0x7cc82f10),
+     TOBN(0xc8c8776c, 0x030526fa), TOBN(0xfa8e6f76, 0xa0da9140),
+     TOBN(0x77ea9d34, 0x591ee4f0), TOBN(0x5f46e337, 0x40274166),
+     TOBN(0x1bdf98bb, 0xea671457), TOBN(0xd7c08b46, 0x862a1fe2),
+     TOBN(0x46cc303c, 0x1c08ad63), TOBN(0x99543440, 0x4c845e7b),
+     TOBN(0x1b8fbdb5, 0x48f36bf7), TOBN(0x5b82c392, 0x8c8273a7),
+     TOBN(0x08f712c4, 0x928435d5), TOBN(0x071cf0f1, 0x79330380),
+     TOBN(0xc74c2d24, 0xa8da054a), TOBN(0xcb0e7201, 0x43c46b5c),
+     TOBN(0x0ad7337a, 0xc0b7eff3), TOBN(0x8552225e, 0xc5e48b3c),
+     TOBN(0xe6f78b0c, 0x73f13a5f), TOBN(0x5e70062e, 0x82349cbe),
+     TOBN(0x6b8d5048, 0xe7073969), TOBN(0x392d2a29, 0xc33cb3d2),
+     TOBN(0xee4f727c, 0x4ecaa20f), TOBN(0xa068c99e, 0x2ccde707),
+     TOBN(0xfcd5651f, 0xb87a2913), TOBN(0xea3e3c15, 0x3cc252f0),
+     TOBN(0x777d92df, 0x3b6cd3e4), TOBN(0x7a414143, 0xc5a732e7),
+     TOBN(0xa895951a, 0xa71ff493), TOBN(0xfe980c92, 0xbbd37cf6),
+     TOBN(0x45bd5e64, 0xdecfeeff), TOBN(0x910dc2a9, 0xa44c43e9),
+     TOBN(0xcb403f26, 0xcca9f54d), TOBN(0x928bbdfb, 0x9303f6db),
+     TOBN(0x3c37951e, 0xa9eee67c), TOBN(0x3bd61a52, 0xf79961c3),
+     TOBN(0x09a238e6, 0x395c9a79), TOBN(0x6940ca2d, 0x61eb352d),
+     TOBN(0x7d1e5c5e, 0xc1875631), TOBN(0x1e19742c, 0x1e1b20d1),
+     TOBN(0x4633d908, 0x23fc2e6e), TOBN(0xa76e29a9, 0x08959149),
+     TOBN(0x61069d9c, 0x84ed7da5), TOBN(0x0baa11cf, 0x5dbcad51),
+     TOBN(0xd01eec64, 0x961849da), TOBN(0x93b75f1f, 0xaf3d8c28),
+     TOBN(0x57bc4f9f, 0x1ca2ee44), TOBN(0x5a26322d, 0x00e00558),
+     TOBN(0x1888d658, 0x61a023ef), TOBN(0x1d72aab4, 0xb9e5246e),
+     TOBN(0xa9a26348, 0xe5563ec0), TOBN(0xa0971963, 0xc3439a43),
+     TOBN(0x567dd54b, 0xadb9b5b7), TOBN(0x73fac1a1, 0xc45a524b),
+     TOBN(0x8fe97ef7, 0xfe38e608), TOBN(0x608748d2, 0x3f384f48),
+     TOBN(0xb0571794, 0xc486094f), TOBN(0x869254a3, 0x8bf3a8d6),
+     TOBN(0x148a8dd1, 0x310b0e25), TOBN(0x99ab9f3f, 0x9aa3f7d8),
+     TOBN(0x0927c68a, 0x6706c02e), TOBN(0x22b5e76c, 0x69790e6c),
+     TOBN(0x6c325260, 0x6c71376c), TOBN(0x53a57690, 0x09ef6657),
+     TOBN(0x8d63f852, 0xedffcf3a), TOBN(0xb4d2ed04, 0x3c0a6f55),
+     TOBN(0xdb3aa8de, 0x12519b9e), TOBN(0x5d38e9c4, 0x1e0a569a),
+     TOBN(0x871528bf, 0x303747e2), TOBN(0xa208e77c, 0xf5b5c18d),
+     TOBN(0x9d129c88, 0xca6bf923), TOBN(0xbcbf197f, 0xbf02839f),
+     TOBN(0x9b9bf030, 0x27323194), TOBN(0x3b055a8b, 0x339ca59d),
+     TOBN(0xb46b2312, 0x0f669520), TOBN(0x19789f1f, 0x497e5f24),
+     TOBN(0x9c499468, 0xaaf01801), TOBN(0x72ee1190, 0x8b69d59c),
+     TOBN(0x8bd39595, 0xacf4c079), TOBN(0x3ee11ece, 0x8e0cd048),
+     TOBN(0xebde86ec, 0x1ed66f18), TOBN(0x225d906b, 0xd61fce43),
+     TOBN(0x5cab07d6, 0xe8bed74d), TOBN(0x16e4617f, 0x27855ab7),
+     TOBN(0x6568aadd, 0xb2fbc3dd), TOBN(0xedb5484f, 0x8aeddf5b),
+     TOBN(0x878f20e8, 0x6dcf2fad), TOBN(0x3516497c, 0x615f5699),}
+    ,
+    {TOBN(0xef0a3fec, 0xfa181e69), TOBN(0x9ea02f81, 0x30d69a98),
+     TOBN(0xb2e9cf8e, 0x66eab95d), TOBN(0x520f2beb, 0x24720021),
+     TOBN(0x621c540a, 0x1df84361), TOBN(0x12037721, 0x71fa6d5d),
+     TOBN(0x6e3c7b51, 0x0ff5f6ff), TOBN(0x817a069b, 0xabb2bef3),
+     TOBN(0x83572fb6, 0xb294cda6), TOBN(0x6ce9bf75, 0xb9039f34),
+     TOBN(0x20e012f0, 0x095cbb21), TOBN(0xa0aecc1b, 0xd063f0da),
+     TOBN(0x57c21c3a, 0xf02909e5), TOBN(0xc7d59ecf, 0x48ce9cdc),
+     TOBN(0x2732b844, 0x8ae336f8), TOBN(0x056e3723, 0x3f4f85f4),
+     TOBN(0x8a10b531, 0x89e800ca), TOBN(0x50fe0c17, 0x145208fd),
+     TOBN(0x9e43c0d3, 0xb714ba37), TOBN(0x427d200e, 0x34189acc),
+     TOBN(0x05dee24f, 0xe616e2c0), TOBN(0x9c25f4c8, 0xee1854c1),
+     TOBN(0x4d3222a5, 0x8f342a73), TOBN(0x0807804f, 0xa027c952),
+     TOBN(0xc222653a, 0x4f0d56f3), TOBN(0x961e4047, 0xca28b805),
+     TOBN(0x2c03f8b0, 0x4a73434b), TOBN(0x4c966787, 0xab712a19),
+     TOBN(0xcc196c42, 0x864fee42), TOBN(0xc1be93da, 0x5b0ece5c),
+     TOBN(0xa87d9f22, 0xc131c159), TOBN(0x2bb6d593, 0xdce45655),
+     TOBN(0x22c49ec9, 0xb809b7ce), TOBN(0x8a41486b, 0xe2c72c2c),
+     TOBN(0x813b9420, 0xfea0bf36), TOBN(0xb3d36ee9, 0xa66dac69),
+     TOBN(0x6fddc08a, 0x328cc987), TOBN(0x0a3bcd2c, 0x3a326461),
+     TOBN(0x7103c49d, 0xd810dbba), TOBN(0xf9d81a28, 0x4b78a4c4),
+     TOBN(0x3de865ad, 0xe4d55941), TOBN(0xdedafa5e, 0x30384087),
+     TOBN(0x6f414abb, 0x4ef18b9b), TOBN(0x9ee9ea42, 0xfaee5268),
+     TOBN(0x260faa16, 0x37a55a4a), TOBN(0xeb19a514, 0x015f93b9),
+     TOBN(0x51d7ebd2, 0x9e9c3598), TOBN(0x523fc56d, 0x1932178e),
+     TOBN(0x501d070c, 0xb98fe684), TOBN(0xd60fbe9a, 0x124a1458),
+     TOBN(0xa45761c8, 0x92bc6b3f), TOBN(0xf5384858, 0xfe6f27cb),
+     TOBN(0x4b0271f7, 0xb59e763b), TOBN(0x3d4606a9, 0x5b5a8e5e),
+     TOBN(0x1eda5d9b, 0x05a48292), TOBN(0xda7731d0, 0xe6fec446),
+     TOBN(0xa3e33693, 0x90d45871), TOBN(0xe9764040, 0x06166d8d),
+     TOBN(0xb5c33682, 0x89a90403), TOBN(0x4bd17983, 0x72f1d637),
+     TOBN(0xa616679e, 0xd5d2c53a), TOBN(0x5ec4bcd8, 0xfdcf3b87),
+     TOBN(0xae6d7613, 0xb66a694e), TOBN(0x7460fc76, 0xe3fc27e5),
+     TOBN(0x70469b82, 0x95caabee), TOBN(0xde024ca5, 0x889501e3),
+     TOBN(0x6bdadc06, 0x076ed265), TOBN(0x0cb1236b, 0x5a0ef8b2),
+     TOBN(0x4065ddbf, 0x0972ebf9), TOBN(0xf1dd3875, 0x22aca432),
+     TOBN(0xa88b97cf, 0x744aff76), TOBN(0xd1359afd, 0xfe8e3d24),
+     TOBN(0x52a3ba2b, 0x91502cf3), TOBN(0x2c3832a8, 0x084db75d),
+     TOBN(0x04a12ddd, 0xde30b1c9), TOBN(0x7802eabc, 0xe31fd60c),
+     TOBN(0x33707327, 0xa37fddab), TOBN(0x65d6f2ab, 0xfaafa973),
+     TOBN(0x3525c5b8, 0x11e6f91a), TOBN(0x76aeb0c9, 0x5f46530b),
+     TOBN(0xe8815ff6, 0x2f93a675), TOBN(0xa6ec9684, 0x05f48679),
+     TOBN(0x6dcbb556, 0x358ae884), TOBN(0x0af61472, 0xe19e3873),
+     TOBN(0x72334372, 0xa5f696be), TOBN(0xc65e57ea, 0x6f22fb70),
+     TOBN(0x268da30c, 0x946cea90), TOBN(0x136a8a87, 0x65681b2a),
+     TOBN(0xad5e81dc, 0x0f9f44d4), TOBN(0xf09a6960, 0x2c46585a),
+     TOBN(0xd1649164, 0xc447d1b1), TOBN(0x3b4b36c8, 0x879dc8b1),
+     TOBN(0x20d4177b, 0x3b6b234c), TOBN(0x096a2505, 0x1730d9d0),
+     TOBN(0x0611b9b8, 0xef80531d), TOBN(0xba904b3b, 0x64bb495d),
+     TOBN(0x1192d9d4, 0x93a3147a), TOBN(0x9f30a5dc, 0x9a565545),
+     TOBN(0x90b1f9cb, 0x6ef07212), TOBN(0x29958546, 0x0d87fc13),
+     TOBN(0xd3323eff, 0xc17db9ba), TOBN(0xcb18548c, 0xcb1644a8),
+     TOBN(0x18a306d4, 0x4f49ffbc), TOBN(0x28d658f1, 0x4c2e8684),
+     TOBN(0x44ba60cd, 0xa99f8c71), TOBN(0x67b7abdb, 0x4bf742ff),
+     TOBN(0x66310f9c, 0x914b3f99), TOBN(0xae430a32, 0xf412c161),
+     TOBN(0x1e6776d3, 0x88ace52f), TOBN(0x4bc0fa24, 0x52d7067d),
+     TOBN(0x03c286aa, 0x8f07cd1b), TOBN(0x4cb8f38c, 0xa985b2c1),
+     TOBN(0x83ccbe80, 0x8c3bff36), TOBN(0x005a0bd2, 0x5263e575),
+     TOBN(0x460d7dda, 0x259bdcd1), TOBN(0x4a1c5642, 0xfa5cab6b),
+     TOBN(0x2b7bdbb9, 0x9fe4fc88), TOBN(0x09418e28, 0xcc97bbb5),
+     TOBN(0xd8274fb4, 0xa12321ae), TOBN(0xb137007d, 0x5c87b64e),
+     TOBN(0x80531fe1, 0xc63c4962), TOBN(0x50541e89, 0x981fdb25),
+     TOBN(0xdc1291a1, 0xfd4c2b6b), TOBN(0xc0693a17, 0xa6df4fca),
+     TOBN(0xb2c4604e, 0x0117f203), TOBN(0x245f1963, 0x0a99b8d0),
+     TOBN(0xaedc20aa, 0xc6212c44), TOBN(0xb1ed4e56, 0x520f52a8),
+     TOBN(0xfe48f575, 0xf8547be3), TOBN(0x0a7033cd, 0xa9e45f98),
+     TOBN(0x4b45d3a9, 0x18c50100), TOBN(0xb2a6cd6a, 0xa61d41da),
+     TOBN(0x60bbb4f5, 0x57933c6b), TOBN(0xa7538ebd, 0x2b0d7ffc),
+     TOBN(0x9ea3ab8d, 0x8cd626b6), TOBN(0x8273a484, 0x3601625a),
+     TOBN(0x88859845, 0x0168e508), TOBN(0x8cbc9bb2, 0x99a94abd),
+     TOBN(0x713ac792, 0xfab0a671), TOBN(0xa3995b19, 0x6c9ebffc),
+     TOBN(0xe711668e, 0x1239e152), TOBN(0x56892558, 0xbbb8dff4),
+     TOBN(0x8bfc7dab, 0xdbf17963), TOBN(0x5b59fe5a, 0xb3de1253),
+     TOBN(0x7e3320eb, 0x34a9f7ae), TOBN(0xe5e8cf72, 0xd751efe4),
+     TOBN(0x7ea003bc, 0xd9be2f37), TOBN(0xc0f551a0, 0xb6c08ef7),
+     TOBN(0x56606268, 0x038f6725), TOBN(0x1dd38e35, 0x6d92d3b6),
+     TOBN(0x07dfce7c, 0xc3cbd686), TOBN(0x4e549e04, 0x651c5da8),
+     TOBN(0x4058f93b, 0x08b19340), TOBN(0xc2fae6f4, 0xcac6d89d),
+     TOBN(0x4bad8a8c, 0x8f159cc7), TOBN(0x0ddba4b3, 0xcb0b601c),
+     TOBN(0xda4fc7b5, 0x1dd95f8c), TOBN(0x1d163cd7, 0xcea5c255),
+     TOBN(0x30707d06, 0x274a8c4c), TOBN(0x79d9e008, 0x2802e9ce),
+     TOBN(0x02a29ebf, 0xe6ddd505), TOBN(0x37064e74, 0xb50bed1a),
+     TOBN(0x3f6bae65, 0xa7327d57), TOBN(0x3846f5f1, 0xf83920bc),
+     TOBN(0x87c37491, 0x60df1b9b), TOBN(0x4cfb2895, 0x2d1da29f),
+     TOBN(0x10a478ca, 0x4ed1743c), TOBN(0x390c6030, 0x3edd47c6),
+     TOBN(0x8f3e5312, 0x8c0a78de), TOBN(0xccd02bda, 0x1e85df70),
+     TOBN(0xd6c75c03, 0xa61b6582), TOBN(0x0762921c, 0xfc0eebd1),
+     TOBN(0xd34d0823, 0xd85010c0), TOBN(0xd73aaacb, 0x0044cf1f),
+     TOBN(0xfb4159bb, 0xa3b5e78a), TOBN(0x2287c7f7, 0xe5826f3f),
+     TOBN(0x4aeaf742, 0x580b1a01), TOBN(0xf080415d, 0x60423b79),
+     TOBN(0xe12622cd, 0xa7dea144), TOBN(0x49ea4996, 0x59d62472),
+     TOBN(0xb42991ef, 0x571f3913), TOBN(0x0610f214, 0xf5b25a8a),
+     TOBN(0x47adc585, 0x30b79e8f), TOBN(0xf90e3df6, 0x07a065a2),
+     TOBN(0x5d0a5deb, 0x43e2e034), TOBN(0x53fb5a34, 0x444024aa),
+     TOBN(0xa8628c68, 0x6b0c9f7f), TOBN(0x9c69c29c, 0xac563656),
+     TOBN(0x5a231feb, 0xbace47b6), TOBN(0xbdce0289, 0x9ea5a2ec),
+     TOBN(0x05da1fac, 0x9463853e), TOBN(0x96812c52, 0x509e78aa),
+     TOBN(0xd3fb5771, 0x57151692), TOBN(0xeb2721f8, 0xd98e1c44),
+     TOBN(0xc0506087, 0x32399be1), TOBN(0xda5a5511, 0xd979d8b8),
+     TOBN(0x737ed55d, 0xc6f56780), TOBN(0xe20d3004, 0x0dc7a7f4),
+     TOBN(0x02ce7301, 0xf5941a03), TOBN(0x91ef5215, 0xed30f83a),
+     TOBN(0x28727fc1, 0x4092d85f), TOBN(0x72d223c6, 0x5c49e41a),
+     TOBN(0xa7cf30a2, 0xba6a4d81), TOBN(0x7c086209, 0xb030d87d),
+     TOBN(0x04844c7d, 0xfc588b09), TOBN(0x728cd499, 0x5874bbb0),
+     TOBN(0xcc1281ee, 0xe84c0495), TOBN(0x0769b5ba, 0xec31958f),
+     TOBN(0x665c228b, 0xf99c2471), TOBN(0xf2d8a11b, 0x191eb110),
+     TOBN(0x4594f494, 0xd36d7024), TOBN(0x482ded8b, 0xcdcb25a1),
+     TOBN(0xc958a9d8, 0xdadd4885), TOBN(0x7004477e, 0xf1d2b547),
+     TOBN(0x0a45f6ef, 0x2a0af550), TOBN(0x4fc739d6, 0x2f8d6351),
+     TOBN(0x75cdaf27, 0x786f08a9), TOBN(0x8700bb26, 0x42c2737f),
+     TOBN(0x855a7141, 0x1c4e2670), TOBN(0x810188c1, 0x15076fef),
+     TOBN(0xc251d0c9, 0xabcd3297), TOBN(0xae4c8967, 0xf48108eb),
+     TOBN(0xbd146de7, 0x18ceed30), TOBN(0xf9d4f07a, 0xc986bced),
+     TOBN(0x5ad98ed5, 0x83fa1e08), TOBN(0x7780d33e, 0xbeabd1fb),
+     TOBN(0xe330513c, 0x903b1196), TOBN(0xba11de9e, 0xa47bc8c4),
+     TOBN(0x684334da, 0x02c2d064), TOBN(0x7ecf360d, 0xa48de23b),
+     TOBN(0x57a1b474, 0x0a9089d8), TOBN(0xf28fa439, 0xff36734c),
+     TOBN(0xf2a482cb, 0xea4570b3), TOBN(0xee65d68b, 0xa5ebcee9),
+     TOBN(0x988d0036, 0xb9694cd5), TOBN(0x53edd0e9, 0x37885d32),
+     TOBN(0xe37e3307, 0xbeb9bc6d), TOBN(0xe9abb907, 0x9f5c6768),
+     TOBN(0x4396ccd5, 0x51f2160f), TOBN(0x2500888c, 0x47336da6),
+     TOBN(0x383f9ed9, 0x926fce43), TOBN(0x809dd1c7, 0x04da2930),
+     TOBN(0x30f6f596, 0x8a4cb227), TOBN(0x0d700c7f, 0x73a56b38),
+     TOBN(0x1825ea33, 0xab64a065), TOBN(0xaab9b735, 0x1338df80),
+     TOBN(0x1516100d, 0x9b63f57f), TOBN(0x2574395a, 0x27a6a634),
+     TOBN(0xb5560fb6, 0x700a1acd), TOBN(0xe823fd73, 0xfd999681),
+     TOBN(0xda915d1f, 0x6cb4e1ba), TOBN(0x0d030118, 0x6ebe00a3),
+     TOBN(0x744fb0c9, 0x89fca8cd), TOBN(0x970d01db, 0xf9da0e0b),
+     TOBN(0x0ad8c564, 0x7931d76f), TOBN(0xb15737bf, 0xf659b96a),
+     TOBN(0xdc9933e8, 0xa8b484e7), TOBN(0xb2fdbdf9, 0x7a26dec7),
+     TOBN(0x2349e9a4, 0x9f1f0136), TOBN(0x7860368e, 0x70fddddb),
+     TOBN(0xd93d2c1c, 0xf9ad3e18), TOBN(0x6d6c5f17, 0x689f4e79),
+     TOBN(0x7a544d91, 0xb24ff1b6), TOBN(0x3e12a5eb, 0xfe16cd8c),
+     TOBN(0x543574e9, 0xa56b872f), TOBN(0xa1ad550c, 0xfcf68ea2),
+     TOBN(0x689e37d2, 0x3f560ef7), TOBN(0x8c54b9ca, 0xc9d47a8b),
+     TOBN(0x46d40a4a, 0x088ac342), TOBN(0xec450c7c, 0x1576c6d0),
+     TOBN(0xb589e31c, 0x1f9689e9), TOBN(0xdacf2602, 0xb8781718),
+     TOBN(0xa89237c6, 0xc8cb6b42), TOBN(0x1326fc93, 0xb96ef381),
+     TOBN(0x55d56c6d, 0xb5f07825), TOBN(0xacba2eea, 0x7449e22d),
+     TOBN(0x74e0887a, 0x633c3000), TOBN(0xcb6cd172, 0xd7cbcf71),
+     TOBN(0x309e81de, 0xc36cf1be), TOBN(0x07a18a6d, 0x60ae399b),
+     TOBN(0xb36c2679, 0x9edce57e), TOBN(0x52b892f4, 0xdf001d41),
+     TOBN(0xd884ae5d, 0x16a1f2c6), TOBN(0x9b329424, 0xefcc370a),
+     TOBN(0x3120daf2, 0xbd2e21df), TOBN(0x55298d2d, 0x02470a99),
+     TOBN(0x0b78af6c, 0xa05db32e), TOBN(0x5c76a331, 0x601f5636),
+     TOBN(0xaae861ff, 0xf8a4f29c), TOBN(0x70dc9240, 0xd68f8d49),
+     TOBN(0x960e649f, 0x81b1321c), TOBN(0x3d2c801b, 0x8792e4ce),
+     TOBN(0xf479f772, 0x42521876), TOBN(0x0bed93bc, 0x416c79b1),
+     TOBN(0xa67fbc05, 0x263e5bc9), TOBN(0x01e8e630, 0x521db049),
+     TOBN(0x76f26738, 0xc6f3431e), TOBN(0xe609cb02, 0xe3267541),
+     TOBN(0xb10cff2d, 0x818c877c), TOBN(0x1f0e75ce, 0x786a13cb),
+     TOBN(0xf4fdca64, 0x1158544d), TOBN(0x5d777e89, 0x6cb71ed0),
+     TOBN(0x3c233737, 0xa9aa4755), TOBN(0x7b453192, 0xe527ab40),
+     TOBN(0xdb59f688, 0x39f05ffe), TOBN(0x8f4f4be0, 0x6d82574e),
+     TOBN(0xcce3450c, 0xee292d1b), TOBN(0xaa448a12, 0x61ccd086),
+     TOBN(0xabce91b3, 0xf7914967), TOBN(0x4537f09b, 0x1908a5ed),
+     TOBN(0xa812421e, 0xf51042e7), TOBN(0xfaf5cebc, 0xec0b3a34),
+     TOBN(0x730ffd87, 0x4ca6b39a), TOBN(0x70fb72ed, 0x02efd342),
+     TOBN(0xeb4735f9, 0xd75c8edb), TOBN(0xc11f2157, 0xc278aa51),
+     TOBN(0xc459f635, 0xbf3bfebf), TOBN(0x3a1ff0b4, 0x6bd9601f),
+     TOBN(0xc9d12823, 0xc420cb73), TOBN(0x3e9af3e2, 0x3c2915a3),
+     TOBN(0xe0c82c72, 0xb41c3440), TOBN(0x175239e5, 0xe3039a5f),
+     TOBN(0xe1084b8a, 0x558795a3), TOBN(0x328d0a1d, 0xd01e5c60),
+     TOBN(0x0a495f2e, 0xd3788a04), TOBN(0x25d8ff16, 0x66c11a9f),
+     TOBN(0xf5155f05, 0x9ed692d6), TOBN(0x954fa107, 0x4f425fe4),
+     TOBN(0xd16aabf2, 0xe98aaa99), TOBN(0x90cd8ba0, 0x96b0f88a),
+     TOBN(0x957f4782, 0xc154026a), TOBN(0x54ee0734, 0x52af56d2),
+     TOBN(0xbcf89e54, 0x45b4147a), TOBN(0x3d102f21, 0x9a52816c),
+     TOBN(0x6808517e, 0x39b62e77), TOBN(0x92e25421, 0x69169ad8),
+     TOBN(0xd721d871, 0xbb608558), TOBN(0x60e4ebae, 0xf6d4ff9b),
+     TOBN(0x0ba10819, 0x41f2763e), TOBN(0xca2e45be, 0x51ee3247),
+     TOBN(0x66d172ec, 0x2bfd7a5f), TOBN(0x528a8f2f, 0x74d0b12d),
+     TOBN(0xe17f1e38, 0xdabe70dc), TOBN(0x1d5d7316, 0x9f93983c),
+     TOBN(0x51b2184a, 0xdf423e31), TOBN(0xcb417291, 0xaedb1a10),
+     TOBN(0x2054ca93, 0x625bcab9), TOBN(0x54396860, 0xa98998f0),
+     TOBN(0x4e53f6c4, 0xa54ae57e), TOBN(0x0ffeb590, 0xee648e9d),
+     TOBN(0xfbbdaadc, 0x6afaf6bc), TOBN(0xf88ae796, 0xaa3bfb8a),
+     TOBN(0x209f1d44, 0xd2359ed9), TOBN(0xac68dd03, 0xf3544ce2),
+     TOBN(0xf378da47, 0xfd51e569), TOBN(0xe1abd860, 0x2cc80097),
+     TOBN(0x23ca18d9, 0x343b6e3a), TOBN(0x480797e8, 0xb40a1bae),
+     TOBN(0xd1f0c717, 0x533f3e67), TOBN(0x44896970, 0x06e6cdfc),
+     TOBN(0x8ca21055, 0x52a82e8d), TOBN(0xb2caf785, 0x78460cdc),
+     TOBN(0x4c1b7b62, 0xe9037178), TOBN(0xefc09d2c, 0xdb514b58),
+     TOBN(0x5f2df9ee, 0x9113be5c), TOBN(0x2fbda78f, 0xb3f9271c),
+     TOBN(0xe09a81af, 0x8f83fc54), TOBN(0x06b13866, 0x8afb5141),
+     TOBN(0x38f6480f, 0x43e3865d), TOBN(0x72dd77a8, 0x1ddf47d9),
+     TOBN(0xf2a8e971, 0x4c205ff7), TOBN(0x46d449d8, 0x9d088ad8),
+     TOBN(0x926619ea, 0x185d706f), TOBN(0xe47e02eb, 0xc7dd7f62),
+     TOBN(0xe7f120a7, 0x8cbc2031), TOBN(0xc18bef00, 0x998d4ac9),
+     TOBN(0x18f37a9c, 0x6bdf22da), TOBN(0xefbc432f, 0x90dc82df),
+     TOBN(0xc52cef8e, 0x5d703651), TOBN(0x82887ba0, 0xd99881a5),
+     TOBN(0x7cec9dda, 0xb920ec1d), TOBN(0xd0d7e8c3, 0xec3e8d3b),
+     TOBN(0x445bc395, 0x4ca88747), TOBN(0xedeaa2e0, 0x9fd53535),
+     TOBN(0x461b1d93, 0x6cc87475), TOBN(0xd92a52e2, 0x6d2383bd),
+     TOBN(0xfabccb59, 0xd7903546), TOBN(0x6111a761, 0x3d14b112),
+     TOBN(0x0ae584fe, 0xb3d5f612), TOBN(0x5ea69b8d, 0x60e828ec),
+     TOBN(0x6c078985, 0x54087030), TOBN(0x649cab04, 0xac4821fe),
+     TOBN(0x25ecedcf, 0x8bdce214), TOBN(0xb5622f72, 0x86af7361),
+     TOBN(0x0e1227aa, 0x7038b9e2), TOBN(0xd0efb273, 0xac20fa77),
+     TOBN(0x817ff88b, 0x79df975b), TOBN(0x856bf286, 0x1999503e),
+     TOBN(0xb4d5351f, 0x5038ec46), TOBN(0x740a52c5, 0xfc42af6e),
+     TOBN(0x2e38bb15, 0x2cbb1a3f), TOBN(0xc3eb99fe, 0x17a83429),
+     TOBN(0xca4fcbf1, 0xdd66bb74), TOBN(0x880784d6, 0xcde5e8fc),
+     TOBN(0xddc84c1c, 0xb4e7a0be), TOBN(0x8780510d, 0xbd15a72f),
+     TOBN(0x44bcf1af, 0x81ec30e1), TOBN(0x141e50a8, 0x0a61073e),
+     TOBN(0x0d955718, 0x47be87ae), TOBN(0x68a61417, 0xf76a4372),
+     TOBN(0xf57e7e87, 0xc607c3d3), TOBN(0x043afaf8, 0x5252f332),
+     TOBN(0xcc14e121, 0x1552a4d2), TOBN(0xb6dee692, 0xbb4d4ab4),
+     TOBN(0xb6ab74c8, 0xa03816a4), TOBN(0x84001ae4, 0x6f394a29),
+     TOBN(0x5bed8344, 0xd795fb45), TOBN(0x57326e7d, 0xb79f55a5),
+     TOBN(0xc9533ce0, 0x4accdffc), TOBN(0x53473caf, 0x3993fa04),
+     TOBN(0x7906eb93, 0xa13df4c8), TOBN(0xa73e51f6, 0x97cbe46f),
+     TOBN(0xd1ab3ae1, 0x0ae4ccf8), TOBN(0x25614508, 0x8a5b3dbc),
+     TOBN(0x61eff962, 0x11a71b27), TOBN(0xdf71412b, 0x6bb7fa39),
+     TOBN(0xb31ba6b8, 0x2bd7f3ef), TOBN(0xb0b9c415, 0x69180d29),
+     TOBN(0xeec14552, 0x014cdde5), TOBN(0x702c624b, 0x227b4bbb),
+     TOBN(0x2b15e8c2, 0xd3e988f3), TOBN(0xee3bcc6d, 0xa4f7fd04),
+     TOBN(0x9d00822a, 0x42ac6c85), TOBN(0x2db0cea6, 0x1df9f2b7),
+     TOBN(0xd7cad2ab, 0x42de1e58), TOBN(0x346ed526, 0x2d6fbb61),
+     TOBN(0xb3962995, 0x1a2faf09), TOBN(0x2fa8a580, 0x7c25612e),
+     TOBN(0x30ae04da, 0x7cf56490), TOBN(0x75662908, 0x0eea3961),
+     TOBN(0x3609f5c5, 0x3d080847), TOBN(0xcb081d39, 0x5241d4f6),
+     TOBN(0xb4fb3810, 0x77961a63), TOBN(0xc20c5984, 0x2abb66fc),
+     TOBN(0x3d40aa7c, 0xf902f245), TOBN(0x9cb12736, 0x4e536b1e),
+     TOBN(0x5eda24da, 0x99b3134f), TOBN(0xafbd9c69, 0x5cd011af),
+     TOBN(0x9a16e30a, 0xc7088c7d), TOBN(0x5ab65710, 0x3207389f),
+     TOBN(0x1b09547f, 0xe7407a53), TOBN(0x2322f9d7, 0x4fdc6eab),
+     TOBN(0xc0f2f22d, 0x7430de4d), TOBN(0x19382696, 0xe68ca9a9),
+     TOBN(0x17f1eff1, 0x918e5868), TOBN(0xe3b5b635, 0x586f4204),
+     TOBN(0x146ef980, 0x3fbc4341), TOBN(0x359f2c80, 0x5b5eed4e),
+     TOBN(0x9f35744e, 0x7482e41d), TOBN(0x9a9ac3ec, 0xf3b224c2),
+     TOBN(0x9161a6fe, 0x91fc50ae), TOBN(0x89ccc66b, 0xc613fa7c),
+     TOBN(0x89268b14, 0xc732f15a), TOBN(0x7cd6f4e2, 0xb467ed03),
+     TOBN(0xfbf79869, 0xce56b40e), TOBN(0xf93e094c, 0xc02dde98),
+     TOBN(0xefe0c3a8, 0xedee2cd7), TOBN(0x90f3ffc0, 0xb268fd42),
+     TOBN(0x81a7fd56, 0x08241aed), TOBN(0x95ab7ad8, 0x00b1afe8),
+     TOBN(0x40127056, 0x3e310d52), TOBN(0xd3ffdeb1, 0x09d9fc43),
+     TOBN(0xc8f85c91, 0xd11a8594), TOBN(0x2e74d258, 0x31cf6db8),
+     TOBN(0x829c7ca3, 0x02b5dfd0), TOBN(0xe389cfbe, 0x69143c86),
+     TOBN(0xd01b6405, 0x941768d8), TOBN(0x45103995, 0x03bf825d),
+     TOBN(0xcc4ee166, 0x56cd17e2), TOBN(0xbea3c283, 0xba037e79),
+     TOBN(0x4e1ac06e, 0xd9a47520), TOBN(0xfbfe18aa, 0xaf852404),
+     TOBN(0x5615f8e2, 0x8087648a), TOBN(0x7301e47e, 0xb9d150d9),
+     TOBN(0x79f9f9dd, 0xb299b977), TOBN(0x76697a7b, 0xa5b78314),
+     TOBN(0x10d67468, 0x7d7c90e7), TOBN(0x7afffe03, 0x937210b5),
+     TOBN(0x5aef3e4b, 0x28c22cee), TOBN(0xefb0ecd8, 0x09fd55ae),
+     TOBN(0x4cea7132, 0x0d2a5d6a), TOBN(0x9cfb5fa1, 0x01db6357),
+     TOBN(0x395e0b57, 0xf36e1ac5), TOBN(0x008fa9ad, 0x36cafb7d),
+     TOBN(0x8f6cdf70, 0x5308c4db), TOBN(0x51527a37, 0x95ed2477),
+     TOBN(0xba0dee30, 0x5bd21311), TOBN(0x6ed41b22, 0x909c90d7),
+     TOBN(0xc5f6b758, 0x7c8696d3), TOBN(0x0db8eaa8, 0x3ce83a80),
+     TOBN(0xd297fe37, 0xb24b4b6f), TOBN(0xfe58afe8, 0x522d1f0d),
+     TOBN(0x97358736, 0x8c98dbd9), TOBN(0x6bc226ca, 0x9454a527),
+     TOBN(0xa12b384e, 0xce53c2d0), TOBN(0x779d897d, 0x5e4606da),
+     TOBN(0xa53e47b0, 0x73ec12b0), TOBN(0x462dbbba, 0x5756f1ad),
+     TOBN(0x69fe09f2, 0xcafe37b6), TOBN(0x273d1ebf, 0xecce2e17),
+     TOBN(0x8ac1d538, 0x3cf607fd), TOBN(0x8035f7ff, 0x12e10c25),}
+    ,
+    {TOBN(0x854d34c7, 0x7e6c5520), TOBN(0xc27df9ef, 0xdcb9ea58),
+     TOBN(0x405f2369, 0xd686666d), TOBN(0x29d1febf, 0x0417aa85),
+     TOBN(0x9846819e, 0x93470afe), TOBN(0x3e6a9669, 0xe2a27f9e),
+     TOBN(0x24d008a2, 0xe31e6504), TOBN(0xdba7cecf, 0x9cb7680a),
+     TOBN(0xecaff541, 0x338d6e43), TOBN(0x56f7dd73, 0x4541d5cc),
+     TOBN(0xb5d426de, 0x96bc88ca), TOBN(0x48d94f6b, 0x9ed3a2c3),
+     TOBN(0x6354a3bb, 0x2ef8279c), TOBN(0xd575465b, 0x0b1867f2),
+     TOBN(0xef99b0ff, 0x95225151), TOBN(0xf3e19d88, 0xf94500d8),
+     TOBN(0x92a83268, 0xe32dd620), TOBN(0x913ec99f, 0x627849a2),
+     TOBN(0xedd8fdfa, 0x2c378882), TOBN(0xaf96f33e, 0xee6f8cfe),
+     TOBN(0xc06737e5, 0xdc3fa8a5), TOBN(0x236bb531, 0xb0b03a1d),
+     TOBN(0x33e59f29, 0x89f037b0), TOBN(0x13f9b5a7, 0xd9a12a53),
+     TOBN(0x0d0df6ce, 0x51efb310), TOBN(0xcb5b2eb4, 0x958df5be),
+     TOBN(0xd6459e29, 0x36158e59), TOBN(0x82aae2b9, 0x1466e336),
+     TOBN(0xfb658a39, 0x411aa636), TOBN(0x7152ecc5, 0xd4c0a933),
+     TOBN(0xf10c758a, 0x49f026b7), TOBN(0xf4837f97, 0xcb09311f),
+     TOBN(0xddfb02c4, 0xc753c45f), TOBN(0x18ca81b6, 0xf9c840fe),
+     TOBN(0x846fd09a, 0xb0f8a3e6), TOBN(0xb1162add, 0xe7733dbc),
+     TOBN(0x7070ad20, 0x236e3ab6), TOBN(0xf88cdaf5, 0xb2a56326),
+     TOBN(0x05fc8719, 0x997cbc7a), TOBN(0x442cd452, 0x4b665272),
+     TOBN(0x7807f364, 0xb71698f5), TOBN(0x6ba418d2, 0x9f7b605e),
+     TOBN(0xfd20b00f, 0xa03b2cbb), TOBN(0x883eca37, 0xda54386f),
+     TOBN(0xff0be43f, 0xf3437f24), TOBN(0xe910b432, 0xa48bb33c),
+     TOBN(0x4963a128, 0x329df765), TOBN(0xac1dd556, 0xbe2fe6f7),
+     TOBN(0x557610f9, 0x24a0a3fc), TOBN(0x38e17bf4, 0xe881c3f9),
+     TOBN(0x6ba84faf, 0xed0dac99), TOBN(0xd4a222c3, 0x59eeb918),
+     TOBN(0xc79c1dbe, 0x13f542b6), TOBN(0x1fc65e0d, 0xe425d457),
+     TOBN(0xeffb754f, 0x1debb779), TOBN(0x638d8fd0, 0x9e08af60),
+     TOBN(0x994f523a, 0x626332d5), TOBN(0x7bc38833, 0x5561bb44),
+     TOBN(0x005ed4b0, 0x3d845ea2), TOBN(0xd39d3ee1, 0xc2a1f08a),
+     TOBN(0x6561fdd3, 0xe7676b0d), TOBN(0x620e35ff, 0xfb706017),
+     TOBN(0x36ce424f, 0xf264f9a8), TOBN(0xc4c3419f, 0xda2681f7),
+     TOBN(0xfb6afd2f, 0x69beb6e8), TOBN(0x3a50b993, 0x6d700d03),
+     TOBN(0xc840b2ad, 0x0c83a14f), TOBN(0x573207be, 0x54085bef),
+     TOBN(0x5af882e3, 0x09fe7e5b), TOBN(0x957678a4, 0x3b40a7e1),
+     TOBN(0x172d4bdd, 0x543056e2), TOBN(0x9c1b26b4, 0x0df13c0a),
+     TOBN(0x1c30861c, 0xf405ff06), TOBN(0xebac86bd, 0x486e828b),
+     TOBN(0xe791a971, 0x636933fc), TOBN(0x50e7c2be, 0x7aeee947),
+     TOBN(0xc3d4a095, 0xfa90d767), TOBN(0xae60eb7b, 0xe670ab7b),
+     TOBN(0x17633a64, 0x397b056d), TOBN(0x93a21f33, 0x105012aa),
+     TOBN(0x663c370b, 0xabb88643), TOBN(0x91df36d7, 0x22e21599),
+     TOBN(0x183ba835, 0x8b761671), TOBN(0x381eea1d, 0x728f3bf1),
+     TOBN(0xb9b2f1ba, 0x39966e6c), TOBN(0x7c464a28, 0xe7295492),
+     TOBN(0x0fd5f70a, 0x09b26b7f), TOBN(0xa9aba1f9, 0xfbe009df),
+     TOBN(0x857c1f22, 0x369b87ad), TOBN(0x3c00e5d9, 0x32fca556),
+     TOBN(0x1ad74cab, 0x90b06466), TOBN(0xa7112386, 0x550faaf2),
+     TOBN(0x7435e198, 0x6d9bd5f5), TOBN(0x2dcc7e38, 0x59c3463f),
+     TOBN(0xdc7df748, 0xca7bd4b2), TOBN(0x13cd4c08, 0x9dec2f31),
+     TOBN(0x0d3b5df8, 0xe3237710), TOBN(0x0dadb26e, 0xcbd2f7b0),
+     TOBN(0x9f5966ab, 0xe4aa082b), TOBN(0x666ec8de, 0x350e966e),
+     TOBN(0x1bfd1ed5, 0xee524216), TOBN(0xcd93c59b, 0x41dab0b6),
+     TOBN(0x658a8435, 0xd186d6ba), TOBN(0x1b7d34d2, 0x159d1195),
+     TOBN(0x5936e460, 0x22caf46b), TOBN(0x6a45dd8f, 0x9a96fe4f),
+     TOBN(0xf7925434, 0xb98f474e), TOBN(0x41410412, 0x0053ef15),
+     TOBN(0x71cf8d12, 0x41de97bf), TOBN(0xb8547b61, 0xbd80bef4),
+     TOBN(0xb47d3970, 0xc4db0037), TOBN(0xf1bcd328, 0xfef20dff),
+     TOBN(0x31a92e09, 0x10caad67), TOBN(0x1f591960, 0x5531a1e1),
+     TOBN(0x3bb852e0, 0x5f4fc840), TOBN(0x63e297ca, 0x93a72c6c),
+     TOBN(0x3c2b0b2e, 0x49abad67), TOBN(0x6ec405fc, 0xed3db0d9),
+     TOBN(0xdc14a530, 0x7fef1d40), TOBN(0xccd19846, 0x280896fc),
+     TOBN(0x00f83176, 0x9bb81648), TOBN(0xd69eb485, 0x653120d0),
+     TOBN(0xd17d75f4, 0x4ccabc62), TOBN(0x34a07f82, 0xb749fcb1),
+     TOBN(0x2c3af787, 0xbbfb5554), TOBN(0xb06ed4d0, 0x62e283f8),
+     TOBN(0x5722889f, 0xa19213a0), TOBN(0x162b085e, 0xdcf3c7b4),
+     TOBN(0xbcaecb31, 0xe0dd3eca), TOBN(0xc6237fbc, 0xe52f13a5),
+     TOBN(0xcc2b6b03, 0x27bac297), TOBN(0x2ae1cac5, 0xb917f54a),
+     TOBN(0x474807d4, 0x7845ae4f), TOBN(0xfec7dd92, 0xce5972e0),
+     TOBN(0xc3bd2541, 0x1d7915bb), TOBN(0x66f85dc4, 0xd94907ca),
+     TOBN(0xd981b888, 0xbdbcf0ca), TOBN(0xd75f5da6, 0xdf279e9f),
+     TOBN(0x128bbf24, 0x7054e934), TOBN(0x3c6ff6e5, 0x81db134b),
+     TOBN(0x795b7cf4, 0x047d26e4), TOBN(0xf370f7b8, 0x5049ec37),
+     TOBN(0xc6712d4d, 0xced945af), TOBN(0xdf30b5ec, 0x095642bc),
+     TOBN(0x9b034c62, 0x4896246e), TOBN(0x5652c016, 0xee90bbd1),
+     TOBN(0xeb38636f, 0x87fedb73), TOBN(0x5e32f847, 0x0135a613),
+     TOBN(0x0703b312, 0xcf933c83), TOBN(0xd05bb76e, 0x1a7f47e6),
+     TOBN(0x825e4f0c, 0x949c2415), TOBN(0x569e5622, 0x7250d6f8),
+     TOBN(0xbbe9eb3a, 0x6568013e), TOBN(0x8dbd203f, 0x22f243fc),
+     TOBN(0x9dbd7694, 0xb342734a), TOBN(0x8f6d12f8, 0x46afa984),
+     TOBN(0xb98610a2, 0xc9eade29), TOBN(0xbab4f323, 0x47dd0f18),
+     TOBN(0x5779737b, 0x671c0d46), TOBN(0x10b6a7c6, 0xd3e0a42a),
+     TOBN(0xfb19ddf3, 0x3035b41c), TOBN(0xd336343f, 0x99c45895),
+     TOBN(0x61fe4938, 0x54c857e5), TOBN(0xc4d506be, 0xae4e57d5),
+     TOBN(0x3cd8c8cb, 0xbbc33f75), TOBN(0x7281f08a, 0x9262c77d),
+     TOBN(0x083f4ea6, 0xf11a2823), TOBN(0x8895041e, 0x9fba2e33),
+     TOBN(0xfcdfea49, 0x9c438edf), TOBN(0x7678dcc3, 0x91edba44),
+     TOBN(0xf07b3b87, 0xe2ba50f0), TOBN(0xc13888ef, 0x43948c1b),
+     TOBN(0xc2135ad4, 0x1140af42), TOBN(0x8e5104f3, 0x926ed1a7),
+     TOBN(0xf24430cb, 0x88f6695f), TOBN(0x0ce0637b, 0x6d73c120),
+     TOBN(0xb2db01e6, 0xfe631e8f), TOBN(0x1c5563d7, 0xd7bdd24b),
+     TOBN(0x8daea3ba, 0x369ad44f), TOBN(0x000c81b6, 0x8187a9f9),
+     TOBN(0x5f48a951, 0xaae1fd9a), TOBN(0xe35626c7, 0x8d5aed8a),
+     TOBN(0x20952763, 0x0498c622), TOBN(0x76d17634, 0x773aa504),
+     TOBN(0x36d90dda, 0xeb300f7a), TOBN(0x9dcf7dfc, 0xedb5e801),
+     TOBN(0x645cb268, 0x74d5244c), TOBN(0xa127ee79, 0x348e3aa2),
+     TOBN(0x488acc53, 0x575f1dbb), TOBN(0x95037e85, 0x80e6161e),
+     TOBN(0x57e59283, 0x292650d0), TOBN(0xabe67d99, 0x14938216),
+     TOBN(0x3c7f944b, 0x3f8e1065), TOBN(0xed908cb6, 0x330e8924),
+     TOBN(0x08ee8fd5, 0x6f530136), TOBN(0x2227b7d5, 0xd7ffc169),
+     TOBN(0x4f55c893, 0xb5cd6dd5), TOBN(0x82225e11, 0xa62796e8),
+     TOBN(0x5c6cead1, 0xcb18e12c), TOBN(0x4381ae0c, 0x84f5a51a),
+     TOBN(0x345913d3, 0x7fafa4c8), TOBN(0x3d918082, 0x0491aac0),
+     TOBN(0x9347871f, 0x3e69264c), TOBN(0xbea9dd3c, 0xb4f4f0cd),
+     TOBN(0xbda5d067, 0x3eadd3e7), TOBN(0x0033c1b8, 0x0573bcd8),
+     TOBN(0x25589379, 0x5da2486c), TOBN(0xcb89ee5b, 0x86abbee7),
+     TOBN(0x8fe0a8f3, 0x22532e5d), TOBN(0xb6410ff0, 0x727dfc4c),
+     TOBN(0x619b9d58, 0x226726db), TOBN(0x5ec25669, 0x7a2b2dc7),
+     TOBN(0xaf4d2e06, 0x4c3beb01), TOBN(0x852123d0, 0x7acea556),
+     TOBN(0x0e9470fa, 0xf783487a), TOBN(0x75a7ea04, 0x5664b3eb),
+     TOBN(0x4ad78f35, 0x6798e4ba), TOBN(0x9214e6e5, 0xc7d0e091),
+     TOBN(0xc420b488, 0xb1290403), TOBN(0x64049e0a, 0xfc295749),
+     TOBN(0x03ef5af1, 0x3ae9841f), TOBN(0xdbe4ca19, 0xb0b662a6),
+     TOBN(0x46845c5f, 0xfa453458), TOBN(0xf8dabf19, 0x10b66722),
+     TOBN(0xb650f0aa, 0xcce2793b), TOBN(0x71db851e, 0xc5ec47c1),
+     TOBN(0x3eb78f3e, 0x3b234fa9), TOBN(0xb0c60f35, 0xfc0106ce),
+     TOBN(0x05427121, 0x774eadbd), TOBN(0x25367faf, 0xce323863),
+     TOBN(0x7541b5c9, 0xcd086976), TOBN(0x4ff069e2, 0xdc507ad1),
+     TOBN(0x74145256, 0x8776e667), TOBN(0x6e76142c, 0xb23c6bb5),
+     TOBN(0xdbf30712, 0x1b3a8a87), TOBN(0x60e7363e, 0x98450836),
+     TOBN(0x5741450e, 0xb7366d80), TOBN(0xe4ee14ca, 0x4837dbdf),
+     TOBN(0xa765eb9b, 0x69d4316f), TOBN(0x04548dca, 0x8ef43825),
+     TOBN(0x9c9f4e4c, 0x5ae888eb), TOBN(0x733abb51, 0x56e9ac99),
+     TOBN(0xdaad3c20, 0xba6ac029), TOBN(0x9b8dd3d3, 0x2ba3e38e),
+     TOBN(0xa9bb4c92, 0x0bc5d11a), TOBN(0xf20127a7, 0x9c5f88a3),
+     TOBN(0x4f52b06e, 0x161d3cb8), TOBN(0x26c1ff09, 0x6afaf0a6),
+     TOBN(0x32670d2f, 0x7189e71f), TOBN(0xc6438748, 0x5ecf91e7),
+     TOBN(0x15758e57, 0xdb757a21), TOBN(0x427d09f8, 0x290a9ce5),
+     TOBN(0x846a308f, 0x38384a7a), TOBN(0xaac3acb4, 0xb0732b99),
+     TOBN(0x9e941009, 0x17845819), TOBN(0x95cba111, 0xa7ce5e03),
+     TOBN(0x6f3d4f7f, 0xb00009c4), TOBN(0xb8396c27, 0x8ff28b5f),
+     TOBN(0xb1a9ae43, 0x1c97975d), TOBN(0x9d7ba8af, 0xe5d9fed5),
+     TOBN(0x338cf09f, 0x34f485b6), TOBN(0xbc0ddacc, 0x64122516),
+     TOBN(0xa450da12, 0x05d471fe), TOBN(0x4c3a6250, 0x628dd8c9),
+     TOBN(0x69c7d103, 0xd1295837), TOBN(0xa2893e50, 0x3807eb2f),
+     TOBN(0xd6e1e1de, 0xbdb41491), TOBN(0xc630745b, 0x5e138235),
+     TOBN(0xc892109e, 0x48661ae1), TOBN(0x8d17e7eb, 0xea2b2674),
+     TOBN(0x00ec0f87, 0xc328d6b5), TOBN(0x6d858645, 0xf079ff9e),
+     TOBN(0x6cdf243e, 0x19115ead), TOBN(0x1ce1393e, 0x4bac4fcf),
+     TOBN(0x2c960ed0, 0x9c29f25b), TOBN(0x59be4d8e, 0x9d388a05),
+     TOBN(0x0d46e06c, 0xd0def72b), TOBN(0xb923db5d, 0xe0342748),
+     TOBN(0xf7d3aacd, 0x936d4a3d), TOBN(0x558519cc, 0x0b0b099e),
+     TOBN(0x3ea8ebf8, 0x827097ef), TOBN(0x259353db, 0xd054f55d),
+     TOBN(0x84c89abc, 0x6d2ed089), TOBN(0x5c548b69, 0x8e096a7c),
+     TOBN(0xd587f616, 0x994b995d), TOBN(0x4d1531f6, 0xa5845601),
+     TOBN(0x792ab31e, 0x451fd9f0), TOBN(0xc8b57bb2, 0x65adf6ca),
+     TOBN(0x68440fcb, 0x1cd5ad73), TOBN(0xb9c860e6, 0x6144da4f),
+     TOBN(0x2ab286aa, 0x8462beb8), TOBN(0xcc6b8fff, 0xef46797f),
+     TOBN(0xac820da4, 0x20c8a471), TOBN(0x69ae05a1, 0x77ff7faf),
+     TOBN(0xb9163f39, 0xbfb5da77), TOBN(0xbd03e590, 0x2c73ab7a),
+     TOBN(0x7e862b5e, 0xb2940d9e), TOBN(0x3c663d86, 0x4b9af564),
+     TOBN(0xd8309031, 0xbde3033d), TOBN(0x298231b2, 0xd42c5bc6),
+     TOBN(0x42090d2c, 0x552ad093), TOBN(0xa4799d1c, 0xff854695),
+     TOBN(0x0a88b5d6, 0xd31f0d00), TOBN(0xf8b40825, 0xa2f26b46),
+     TOBN(0xec29b1ed, 0xf1bd7218), TOBN(0xd491c53b, 0x4b24c86e),
+     TOBN(0xd2fe588f, 0x3395ea65), TOBN(0x6f3764f7, 0x4456ef15),
+     TOBN(0xdb43116d, 0xcdc34800), TOBN(0xcdbcd456, 0xc1e33955),
+     TOBN(0xefdb5540, 0x74ab286b), TOBN(0x948c7a51, 0xd18c5d7c),
+     TOBN(0xeb81aa37, 0x7378058e), TOBN(0x41c746a1, 0x04411154),
+     TOBN(0xa10c73bc, 0xfb828ac7), TOBN(0x6439be91, 0x9d972b29),
+     TOBN(0x4bf3b4b0, 0x43a2fbad), TOBN(0x39e6dadf, 0x82b5e840),
+     TOBN(0x4f716408, 0x6397bd4c), TOBN(0x0f7de568, 0x7f1eeccb),
+     TOBN(0x5865c5a1, 0xd2ffbfc1), TOBN(0xf74211fa, 0x4ccb6451),
+     TOBN(0x66368a88, 0xc0b32558), TOBN(0x5b539dc2, 0x9ad7812e),
+     TOBN(0x579483d0, 0x2f3af6f6), TOBN(0x52132078, 0x99934ece),
+     TOBN(0x50b9650f, 0xdcc9e983), TOBN(0xca989ec9, 0xaee42b8a),
+     TOBN(0x6a44c829, 0xd6f62f99), TOBN(0x8f06a309, 0x4c2a7c0c),
+     TOBN(0x4ea2b3a0, 0x98a0cb0a), TOBN(0x5c547b70, 0xbeee8364),
+     TOBN(0x461d40e1, 0x682afe11), TOBN(0x9e0fc77a, 0x7b41c0a8),
+     TOBN(0x79e4aefd, 0xe20d5d36), TOBN(0x2916e520, 0x32dd9f63),
+     TOBN(0xf59e52e8, 0x3f883faf), TOBN(0x396f9639, 0x2b868d35),
+     TOBN(0xc902a9df, 0x4ca19881), TOBN(0x0fc96822, 0xdb2401a6),
+     TOBN(0x41237587, 0x66f1c68d), TOBN(0x10fc6de3, 0xfb476c0d),
+     TOBN(0xf8b6b579, 0x841f5d90), TOBN(0x2ba8446c, 0xfa24f44a),
+     TOBN(0xa237b920, 0xef4a9975), TOBN(0x60bb6004, 0x2330435f),
+     TOBN(0xd6f4ab5a, 0xcfb7e7b5), TOBN(0xb2ac5097, 0x83435391),
+     TOBN(0xf036ee2f, 0xb0d1ea67), TOBN(0xae779a6a, 0x74c56230),
+     TOBN(0x59bff8c8, 0xab838ae6), TOBN(0xcd83ca99, 0x9b38e6f0),
+     TOBN(0xbb27bef5, 0xe33deed3), TOBN(0xe6356f6f, 0x001892a8),
+     TOBN(0xbf3be6cc, 0x7adfbd3e), TOBN(0xaecbc81c, 0x33d1ac9d),
+     TOBN(0xe4feb909, 0xe6e861dc), TOBN(0x90a247a4, 0x53f5f801),
+     TOBN(0x01c50acb, 0x27346e57), TOBN(0xce29242e, 0x461acc1b),
+     TOBN(0x04dd214a, 0x2f998a91), TOBN(0x271ee9b1, 0xd4baf27b),
+     TOBN(0x7e3027d1, 0xe8c26722), TOBN(0x21d1645c, 0x1820dce5),
+     TOBN(0x086f242c, 0x7501779c), TOBN(0xf0061407, 0xfa0e8009),
+     TOBN(0xf23ce477, 0x60187129), TOBN(0x05bbdedb, 0x0fde9bd0),
+     TOBN(0x682f4832, 0x25d98473), TOBN(0xf207fe85, 0x5c658427),
+     TOBN(0xb6fdd7ba, 0x4166ffa1), TOBN(0x0c314056, 0x9eed799d),
+     TOBN(0x0db8048f, 0x4107e28f), TOBN(0x74ed3871, 0x41216840),
+     TOBN(0x74489f8f, 0x56a3c06e), TOBN(0x1e1c005b, 0x12777134),
+     TOBN(0xdb332a73, 0xf37ec3c3), TOBN(0xc65259bd, 0xdd59eba0),
+     TOBN(0x2291709c, 0xdb4d3257), TOBN(0x9a793b25, 0xbd389390),
+     TOBN(0xf39fe34b, 0xe43756f0), TOBN(0x2f76bdce, 0x9afb56c9),
+     TOBN(0x9f37867a, 0x61208b27), TOBN(0xea1d4307, 0x089972c3),
+     TOBN(0x8c595330, 0x8bdf623a), TOBN(0x5f5accda, 0x8441fb7d),
+     TOBN(0xfafa9418, 0x32ddfd95), TOBN(0x6ad40c5a, 0x0fde9be7),
+     TOBN(0x43faba89, 0xaeca8709), TOBN(0xc64a7cf1, 0x2c248a9d),
+     TOBN(0x16620252, 0x72637a76), TOBN(0xaee1c791, 0x22b8d1bb),
+     TOBN(0xf0f798fd, 0x21a843b2), TOBN(0x56e4ed4d, 0x8d005cb1),
+     TOBN(0x355f7780, 0x1f0d8abe), TOBN(0x197b04cf, 0x34522326),
+     TOBN(0x41f9b31f, 0xfd42c13f), TOBN(0x5ef7feb2, 0xb40f933d),
+     TOBN(0x27326f42, 0x5d60bad4), TOBN(0x027ecdb2, 0x8c92cf89),
+     TOBN(0x04aae4d1, 0x4e3352fe), TOBN(0x08414d2f, 0x73591b90),
+     TOBN(0x5ed6124e, 0xb7da7d60), TOBN(0xb985b931, 0x4d13d4ec),
+     TOBN(0xa592d3ab, 0x96bf36f9), TOBN(0x012dbed5, 0xbbdf51df),
+     TOBN(0xa57963c0, 0xdf6c177d), TOBN(0x010ec869, 0x87ca29cf),
+     TOBN(0xba1700f6, 0xbf926dff), TOBN(0x7c9fdbd1, 0xf4bf6bc2),
+     TOBN(0xdc18dc8f, 0x64da11f5), TOBN(0xa6074b7a, 0xd938ae75),
+     TOBN(0x14270066, 0xe84f44a4), TOBN(0x99998d38, 0xd27b954e),
+     TOBN(0xc1be8ab2, 0xb4f38e9a), TOBN(0x8bb55bbf, 0x15c01016),
+     TOBN(0xf73472b4, 0x0ea2ab30), TOBN(0xd365a340, 0xf73d68dd),
+     TOBN(0xc01a7168, 0x19c2e1eb), TOBN(0x32f49e37, 0x34061719),
+     TOBN(0xb73c57f1, 0x01d8b4d6), TOBN(0x03c8423c, 0x26b47700),
+     TOBN(0x321d0bc8, 0xa4d8826a), TOBN(0x6004213c, 0x4bc0e638),
+     TOBN(0xf78c64a1, 0xc1c06681), TOBN(0x16e0a16f, 0xef018e50),
+     TOBN(0x31cbdf91, 0xdb42b2b3), TOBN(0xf8f4ffce, 0xe0d36f58),
+     TOBN(0xcdcc71cd, 0x4cc5e3e0), TOBN(0xd55c7cfa, 0xa129e3e0),
+     TOBN(0xccdb6ba0, 0x0fb2cbf1), TOBN(0x6aba0005, 0xc4bce3cb),
+     TOBN(0x501cdb30, 0xd232cfc4), TOBN(0x9ddcf12e, 0xd58a3cef),
+     TOBN(0x02d2cf9c, 0x87e09149), TOBN(0xdc5d7ec7, 0x2c976257),
+     TOBN(0x6447986e, 0x0b50d7dd), TOBN(0x88fdbaf7, 0x807f112a),
+     TOBN(0x58c9822a, 0xb00ae9f6), TOBN(0x6abfb950, 0x6d3d27e0),
+     TOBN(0xd0a74487, 0x8a429f4f), TOBN(0x0649712b, 0xdb516609),
+     TOBN(0xb826ba57, 0xe769b5df), TOBN(0x82335df2, 0x1fc7aaf2),
+     TOBN(0x2389f067, 0x5c93d995), TOBN(0x59ac367a, 0x68677be6),
+     TOBN(0xa77985ff, 0x21d9951b), TOBN(0x038956fb, 0x85011cce),
+     TOBN(0x608e48cb, 0xbb734e37), TOBN(0xc08c0bf2, 0x2be5b26f),
+     TOBN(0x17bbdd3b, 0xf9b1a0d9), TOBN(0xeac7d898, 0x10483319),
+     TOBN(0xc95c4baf, 0xbc1a6dea), TOBN(0xfdd0e2bf, 0x172aafdb),
+     TOBN(0x40373cbc, 0x8235c41a), TOBN(0x14303f21, 0xfb6f41d5),
+     TOBN(0xba063621, 0x0408f237), TOBN(0xcad3b09a, 0xecd2d1ed),
+     TOBN(0x4667855a, 0x52abb6a2), TOBN(0xba9157dc, 0xaa8b417b),
+     TOBN(0xfe7f3507, 0x4f013efb), TOBN(0x1b112c4b, 0xaa38c4a2),
+     TOBN(0xa1406a60, 0x9ba64345), TOBN(0xe53cba33, 0x6993c80b),
+     TOBN(0x45466063, 0xded40d23), TOBN(0x3d5f1f4d, 0x54908e25),
+     TOBN(0x9ebefe62, 0x403c3c31), TOBN(0x274ea0b5, 0x0672a624),
+     TOBN(0xff818d99, 0x451d1b71), TOBN(0x80e82643, 0x8f79cf79),
+     TOBN(0xa165df13, 0x73ce37f5), TOBN(0xa744ef4f, 0xfe3a21fd),
+     TOBN(0x73f1e7f5, 0xcf551396), TOBN(0xc616898e, 0x868c676b),
+     TOBN(0x671c28c7, 0x8c442c36), TOBN(0xcfe5e558, 0x5e0a317d),
+     TOBN(0x1242d818, 0x7051f476), TOBN(0x56fad2a6, 0x14f03442),
+     TOBN(0x262068bc, 0x0a44d0f6), TOBN(0xdfa2cd6e, 0xce6edf4e),
+     TOBN(0x0f43813a, 0xd15d1517), TOBN(0x61214cb2, 0x377d44f5),
+     TOBN(0xd399aa29, 0xc639b35f), TOBN(0x42136d71, 0x54c51c19),
+     TOBN(0x9774711b, 0x08417221), TOBN(0x0a5546b3, 0x52545a57),
+     TOBN(0x80624c41, 0x1150582d), TOBN(0x9ec5c418, 0xfbc555bc),
+     TOBN(0x2c87dcad, 0x771849f1), TOBN(0xb0c932c5, 0x01d7bf6f),
+     TOBN(0x6aa5cd3e, 0x89116eb2), TOBN(0xd378c25a, 0x51ca7bd3),
+     TOBN(0xc612a0da, 0x9e6e3e31), TOBN(0x0417a54d, 0xb68ad5d0),
+     TOBN(0x00451e4a, 0x22c6edb8), TOBN(0x9fbfe019, 0xb42827ce),
+     TOBN(0x2fa92505, 0xba9384a2), TOBN(0x21b8596e, 0x64ad69c1),
+     TOBN(0x8f4fcc49, 0x983b35a6), TOBN(0xde093760, 0x72754672),
+     TOBN(0x2f14ccc8, 0xf7bffe6d), TOBN(0x27566bff, 0x5d94263d),
+     TOBN(0xb5b4e9c6, 0x2df3ec30), TOBN(0x94f1d7d5, 0x3e6ea6ba),
+     TOBN(0x97b7851a, 0xaaca5e9b), TOBN(0x518aa521, 0x56713b97),
+     TOBN(0x3357e8c7, 0x150a61f6), TOBN(0x7842e7e2, 0xec2c2b69),
+     TOBN(0x8dffaf65, 0x6868a548), TOBN(0xd963bd82, 0xe068fc81),
+     TOBN(0x64da5c8b, 0x65917733), TOBN(0x927090ff, 0x7b247328),}
+    ,
+    {TOBN(0x214bc9a7, 0xd298c241), TOBN(0xe3b697ba, 0x56807cfd),
+     TOBN(0xef1c7802, 0x4564eadb), TOBN(0xdde8cdcf, 0xb48149c5),
+     TOBN(0x946bf0a7, 0x5a4d2604), TOBN(0x27154d7f, 0x6c1538af),
+     TOBN(0x95cc9230, 0xde5b1fcc), TOBN(0xd88519e9, 0x66864f82),
+     TOBN(0xb828dd1a, 0x7cb1282c), TOBN(0xa08d7626, 0xbe46973a),
+     TOBN(0x6baf8d40, 0xe708d6b2), TOBN(0x72571fa1, 0x4daeb3f3),
+     TOBN(0x85b1732f, 0xf22dfd98), TOBN(0x87ab01a7, 0x0087108d),
+     TOBN(0xaaaafea8, 0x5988207a), TOBN(0xccc832f8, 0x69f00755),
+     TOBN(0x964d950e, 0x36ff3bf0), TOBN(0x8ad20f6f, 0xf0b34638),
+     TOBN(0x4d9177b3, 0xb5d7585f), TOBN(0xcf839760, 0xef3f019f),
+     TOBN(0x582fc5b3, 0x8288c545), TOBN(0x2f8e4e9b, 0x13116bd1),
+     TOBN(0xf91e1b2f, 0x332120ef), TOBN(0xcf568724, 0x2a17dd23),
+     TOBN(0x488f1185, 0xca8d9d1a), TOBN(0xadf2c77d, 0xd987ded2),
+     TOBN(0x5f3039f0, 0x60c46124), TOBN(0xe5d70b75, 0x71e095f4),
+     TOBN(0x82d58650, 0x6260e70f), TOBN(0x39d75ea7, 0xf750d105),
+     TOBN(0x8cf3d0b1, 0x75bac364), TOBN(0xf3a7564d, 0x21d01329),
+     TOBN(0x182f04cd, 0x2f52d2a7), TOBN(0x4fde149a, 0xe2df565a),
+     TOBN(0xb80c5eec, 0xa79fb2f7), TOBN(0xab491d7b, 0x22ddc897),
+     TOBN(0x99d76c18, 0xc6312c7f), TOBN(0xca0d5f3d, 0x6aa41a57),
+     TOBN(0x71207325, 0xd15363a0), TOBN(0xe82aa265, 0xbeb252c2),
+     TOBN(0x94ab4700, 0xec3128c2), TOBN(0x6c76d862, 0x8e383f49),
+     TOBN(0xdc36b150, 0xc03024eb), TOBN(0xfb439477, 0x53daac69),
+     TOBN(0xfc68764a, 0x8dc79623), TOBN(0x5b86995d, 0xb440fbb2),
+     TOBN(0xd66879bf, 0xccc5ee0d), TOBN(0x05228942, 0x95aa8bd3),
+     TOBN(0xb51a40a5, 0x1e6a75c1), TOBN(0x24327c76, 0x0ea7d817),
+     TOBN(0x06630182, 0x07774597), TOBN(0xd6fdbec3, 0x97fa7164),
+     TOBN(0x20c99dfb, 0x13c90f48), TOBN(0xd6ac5273, 0x686ef263),
+     TOBN(0xc6a50bdc, 0xfef64eeb), TOBN(0xcd87b281, 0x86fdfc32),
+     TOBN(0xb24aa43e, 0x3fcd3efc), TOBN(0xdd26c034, 0xb8088e9a),
+     TOBN(0xa5ef4dc9, 0xbd3d46ea), TOBN(0xa2f99d58, 0x8a4c6a6f),
+     TOBN(0xddabd355, 0x2f1da46c), TOBN(0x72c3f8ce, 0x1afacdd1),
+     TOBN(0xd90c4eee, 0x92d40578), TOBN(0xd28bb41f, 0xca623b94),
+     TOBN(0x50fc0711, 0x745edc11), TOBN(0x9dd9ad7d, 0x3dc87558),
+     TOBN(0xce6931fb, 0xb49d1e64), TOBN(0x6c77a0a2, 0xc98bd0f9),
+     TOBN(0x62b9a629, 0x6baf7cb1), TOBN(0xcf065f91, 0xccf72d22),
+     TOBN(0x7203cce9, 0x79639071), TOBN(0x09ae4885, 0xf9cb732f),
+     TOBN(0x5e7c3bec, 0xee8314f3), TOBN(0x1c068aed, 0xdbea298f),
+     TOBN(0x08d381f1, 0x7c80acec), TOBN(0x03b56be8, 0xe330495b),
+     TOBN(0xaeffb8f2, 0x9222882d), TOBN(0x95ff38f6, 0xc4af8bf7),
+     TOBN(0x50e32d35, 0x1fc57d8c), TOBN(0x6635be52, 0x17b444f0),
+     TOBN(0x04d15276, 0xa5177900), TOBN(0x4e1dbb47, 0xf6858752),
+     TOBN(0x5b475622, 0xc615796c), TOBN(0xa6fa0387, 0x691867bf),
+     TOBN(0xed7f5d56, 0x2844c6d0), TOBN(0xc633cf9b, 0x03a2477d),
+     TOBN(0xf6be5c40, 0x2d3721d6), TOBN(0xaf312eb7, 0xe9fd68e6),
+     TOBN(0x242792d2, 0xe7417ce1), TOBN(0xff42bc71, 0x970ee7f5),
+     TOBN(0x1ff4dc6d, 0x5c67a41e), TOBN(0x77709b7b, 0x20882a58),
+     TOBN(0x3554731d, 0xbe217f2c), TOBN(0x2af2a8cd, 0x5bb72177),
+     TOBN(0x58eee769, 0x591dd059), TOBN(0xbb2930c9, 0x4bba6477),
+     TOBN(0x863ee047, 0x7d930cfc), TOBN(0x4c262ad1, 0x396fd1f4),
+     TOBN(0xf4765bc8, 0x039af7e1), TOBN(0x2519834b, 0x5ba104f6),
+     TOBN(0x7cd61b4c, 0xd105f961), TOBN(0xa5415da5, 0xd63bca54),
+     TOBN(0x778280a0, 0x88a1f17c), TOBN(0xc4968949, 0x2329512c),
+     TOBN(0x174a9126, 0xcecdaa7a), TOBN(0xfc8c7e0e, 0x0b13247b),
+     TOBN(0x29c110d2, 0x3484c1c4), TOBN(0xf8eb8757, 0x831dfc3b),
+     TOBN(0x022f0212, 0xc0067452), TOBN(0x3f6f69ee, 0x7b9b926c),
+     TOBN(0x09032da0, 0xef42daf4), TOBN(0x79f00ade, 0x83f80de4),
+     TOBN(0x6210db71, 0x81236c97), TOBN(0x74f7685b, 0x3ee0781f),
+     TOBN(0x4df7da7b, 0xa3e41372), TOBN(0x2aae38b1, 0xb1a1553e),
+     TOBN(0x1688e222, 0xf6dd9d1b), TOBN(0x57695448, 0x5b8b6487),
+     TOBN(0x478d2127, 0x4b2edeaa), TOBN(0xb2818fa5, 0x1e85956a),
+     TOBN(0x1e6addda, 0xf176f2c0), TOBN(0x01ca4604, 0xe2572658),
+     TOBN(0x0a404ded, 0x85342ffb), TOBN(0x8cf60f96, 0x441838d6),
+     TOBN(0x9bbc691c, 0xc9071c4a), TOBN(0xfd588744, 0x34442803),
+     TOBN(0x97101c85, 0x809c0d81), TOBN(0xa7fb754c, 0x8c456f7f),
+     TOBN(0xc95f3c5c, 0xd51805e1), TOBN(0xab4ccd39, 0xb299dca8),
+     TOBN(0x3e03d20b, 0x47eaf500), TOBN(0xfa3165c1, 0xd7b80893),
+     TOBN(0x005e8b54, 0xe160e552), TOBN(0xdc4972ba, 0x9019d11f),
+     TOBN(0x21a6972e, 0x0c9a4a7a), TOBN(0xa52c258f, 0x37840fd7),
+     TOBN(0xf8559ff4, 0xc1e99d81), TOBN(0x08e1a7d6, 0xa3c617c0),
+     TOBN(0xb398fd43, 0x248c6ba7), TOBN(0x6ffedd91, 0xd1283794),
+     TOBN(0x8a6a59d2, 0xd629d208), TOBN(0xa9d141d5, 0x3490530e),
+     TOBN(0x42f6fc18, 0x38505989), TOBN(0x09bf250d, 0x479d94ee),
+     TOBN(0x223ad3b1, 0xb3822790), TOBN(0x6c5926c0, 0x93b8971c),
+     TOBN(0x609efc7e, 0x75f7fa62), TOBN(0x45d66a6d, 0x1ec2d989),
+     TOBN(0x4422d663, 0x987d2792), TOBN(0x4a73caad, 0x3eb31d2b),
+     TOBN(0xf06c2ac1, 0xa32cb9e6), TOBN(0xd9445c5f, 0x91aeba84),
+     TOBN(0x6af7a1d5, 0xaf71013f), TOBN(0xe68216e5, 0x0bedc946),
+     TOBN(0xf4cba30b, 0xd27370a0), TOBN(0x7981afbf, 0x870421cc),
+     TOBN(0x02496a67, 0x9449f0e1), TOBN(0x86cfc4be, 0x0a47edae),
+     TOBN(0x3073c936, 0xb1feca22), TOBN(0xf5694612, 0x03f8f8fb),
+     TOBN(0xd063b723, 0x901515ea), TOBN(0x4c6c77a5, 0x749cf038),
+     TOBN(0x6361e360, 0xab9e5059), TOBN(0x596cf171, 0xa76a37c0),
+     TOBN(0x800f53fa, 0x6530ae7a), TOBN(0x0f5e631e, 0x0792a7a6),
+     TOBN(0x5cc29c24, 0xefdb81c9), TOBN(0xa269e868, 0x3f9c40ba),
+     TOBN(0xec14f9e1, 0x2cb7191e), TOBN(0x78ea1bd8, 0xe5b08ea6),
+     TOBN(0x3c65aa9b, 0x46332bb9), TOBN(0x84cc22b3, 0xbf80ce25),
+     TOBN(0x0098e9e9, 0xd49d5bf1), TOBN(0xcd4ec1c6, 0x19087da4),
+     TOBN(0x3c9d07c5, 0xaef6e357), TOBN(0x839a0268, 0x9f8f64b8),
+     TOBN(0xc5e9eb62, 0xc6d8607f), TOBN(0x759689f5, 0x6aa995e4),
+     TOBN(0x70464669, 0xbbb48317), TOBN(0x921474bf, 0xe402417d),
+     TOBN(0xcabe135b, 0x2a354c8c), TOBN(0xd51e52d2, 0x812fa4b5),
+     TOBN(0xec741096, 0x53311fe8), TOBN(0x4f774535, 0xb864514b),
+     TOBN(0xbcadd671, 0x5bde48f8), TOBN(0xc9703873, 0x2189bc7d),
+     TOBN(0x5d45299e, 0xc709ee8a), TOBN(0xd1287ee2, 0x845aaff8),
+     TOBN(0x7d1f8874, 0xdb1dbf1f), TOBN(0xea46588b, 0x990c88d6),
+     TOBN(0x60ba649a, 0x84368313), TOBN(0xd5fdcbce, 0x60d543ae),
+     TOBN(0x90b46d43, 0x810d5ab0), TOBN(0x6739d8f9, 0x04d7e5cc),
+     TOBN(0x021c1a58, 0x0d337c33), TOBN(0x00a61162, 0x68e67c40),
+     TOBN(0x95ef413b, 0x379f0a1f), TOBN(0xfe126605, 0xe9e2ab95),
+     TOBN(0x67578b85, 0x2f5f199c), TOBN(0xf5c00329, 0x2cb84913),
+     TOBN(0xf7956430, 0x37577dd8), TOBN(0x83b82af4, 0x29c5fe88),
+     TOBN(0x9c1bea26, 0xcdbdc132), TOBN(0x589fa086, 0x9c04339e),
+     TOBN(0x033e9538, 0xb13799df), TOBN(0x85fa8b21, 0xd295d034),
+     TOBN(0xdf17f73f, 0xbd9ddcca), TOBN(0xf32bd122, 0xddb66334),
+     TOBN(0x55ef88a7, 0x858b044c), TOBN(0x1f0d69c2, 0x5aa9e397),
+     TOBN(0x55fd9cc3, 0x40d85559), TOBN(0xc774df72, 0x7785ddb2),
+     TOBN(0x5dcce9f6, 0xd3bd2e1c), TOBN(0xeb30da20, 0xa85dfed0),
+     TOBN(0x5ed7f5bb, 0xd3ed09c4), TOBN(0x7d42a35c, 0x82a9c1bd),
+     TOBN(0xcf3de995, 0x9890272d), TOBN(0x75f3432a, 0x3e713a10),
+     TOBN(0x5e13479f, 0xe28227b8), TOBN(0xb8561ea9, 0xfefacdc8),
+     TOBN(0xa6a297a0, 0x8332aafd), TOBN(0x9b0d8bb5, 0x73809b62),
+     TOBN(0xd2fa1cfd, 0x0c63036f), TOBN(0x7a16eb55, 0xbd64bda8),
+     TOBN(0x3f5cf5f6, 0x78e62ddc), TOBN(0x2267c454, 0x07fd752b),
+     TOBN(0x5e361b6b, 0x5e437bbe), TOBN(0x95c59501, 0x8354e075),
+     TOBN(0xec725f85, 0xf2b254d9), TOBN(0x844b617d, 0x2cb52b4e),
+     TOBN(0xed8554f5, 0xcf425fb5), TOBN(0xab67703e, 0x2af9f312),
+     TOBN(0x4cc34ec1, 0x3cf48283), TOBN(0xb09daa25, 0x9c8a705e),
+     TOBN(0xd1e9d0d0, 0x5b7d4f84), TOBN(0x4df6ef64, 0xdb38929d),
+     TOBN(0xe16b0763, 0xaa21ba46), TOBN(0xc6b1d178, 0xa293f8fb),
+     TOBN(0x0ff5b602, 0xd520aabf), TOBN(0x94d671bd, 0xc339397a),
+     TOBN(0x7c7d98cf, 0x4f5792fa), TOBN(0x7c5e0d67, 0x11215261),
+     TOBN(0x9b19a631, 0xa7c5a6d4), TOBN(0xc8511a62, 0x7a45274d),
+     TOBN(0x0c16621c, 0xa5a60d99), TOBN(0xf7fbab88, 0xcf5e48cb),
+     TOBN(0xab1e6ca2, 0xf7ddee08), TOBN(0x83bd08ce, 0xe7867f3c),
+     TOBN(0xf7e48e8a, 0x2ac13e27), TOBN(0x4494f6df, 0x4eb1a9f5),
+     TOBN(0xedbf84eb, 0x981f0a62), TOBN(0x49badc32, 0x536438f0),
+     TOBN(0x50bea541, 0x004f7571), TOBN(0xbac67d10, 0xdf1c94ee),
+     TOBN(0x253d73a1, 0xb727bc31), TOBN(0xb3d01cf2, 0x30686e28),
+     TOBN(0x51b77b1b, 0x55fd0b8b), TOBN(0xa099d183, 0xfeec3173),
+     TOBN(0x202b1fb7, 0x670e72b7), TOBN(0xadc88b33, 0xa8e1635f),
+     TOBN(0x34e8216a, 0xf989d905), TOBN(0xc2e68d20, 0x29b58d01),
+     TOBN(0x11f81c92, 0x6fe55a93), TOBN(0x15f1462a, 0x8f296f40),
+     TOBN(0x1915d375, 0xea3d62f2), TOBN(0xa17765a3, 0x01c8977d),
+     TOBN(0x7559710a, 0xe47b26f6), TOBN(0xe0bd29c8, 0x535077a5),
+     TOBN(0x615f976d, 0x08d84858), TOBN(0x370dfe85, 0x69ced5c1),
+     TOBN(0xbbc7503c, 0xa734fa56), TOBN(0xfbb9f1ec, 0x91ac4574),
+     TOBN(0x95d7ec53, 0x060dd7ef), TOBN(0xeef2dacd, 0x6e657979),
+     TOBN(0x54511af3, 0xe2a08235), TOBN(0x1e324aa4, 0x1f4aea3d),
+     TOBN(0x550e7e71, 0xe6e67671), TOBN(0xbccd5190, 0xbf52faf7),
+     TOBN(0xf880d316, 0x223cc62a), TOBN(0x0d402c7e, 0x2b32eb5d),
+     TOBN(0xa40bc039, 0x306a5a3b), TOBN(0x4e0a41fd, 0x96783a1b),
+     TOBN(0xa1e8d39a, 0x0253cdd4), TOBN(0x6480be26, 0xc7388638),
+     TOBN(0xee365e1d, 0x2285f382), TOBN(0x188d8d8f, 0xec0b5c36),
+     TOBN(0x34ef1a48, 0x1f0f4d82), TOBN(0x1a8f43e1, 0xa487d29a),
+     TOBN(0x8168226d, 0x77aefb3a), TOBN(0xf69a751e, 0x1e72c253),
+     TOBN(0x8e04359a, 0xe9594df1), TOBN(0x475ffd7d, 0xd14c0467),
+     TOBN(0xb5a2c2b1, 0x3844e95c), TOBN(0x85caf647, 0xdd12ef94),
+     TOBN(0x1ecd2a9f, 0xf1063d00), TOBN(0x1dd2e229, 0x23843311),
+     TOBN(0x38f0e09d, 0x73d17244), TOBN(0x3ede7746, 0x8fc653f1),
+     TOBN(0xae4459f5, 0xdc20e21c), TOBN(0x00db2ffa, 0x6a8599ea),
+     TOBN(0x11682c39, 0x30cfd905), TOBN(0x4934d074, 0xa5c112a6),
+     TOBN(0xbdf063c5, 0x568bfe95), TOBN(0x779a440a, 0x016c441a),
+     TOBN(0x0c23f218, 0x97d6fbdc), TOBN(0xd3a5cd87, 0xe0776aac),
+     TOBN(0xcee37f72, 0xd712e8db), TOBN(0xfb28c70d, 0x26f74e8d),
+     TOBN(0xffe0c728, 0xb61301a0), TOBN(0xa6282168, 0xd3724354),
+     TOBN(0x7ff4cb00, 0x768ffedc), TOBN(0xc51b3088, 0x03b02de9),
+     TOBN(0xa5a8147c, 0x3902dda5), TOBN(0x35d2f706, 0xfe6973b4),
+     TOBN(0x5ac2efcf, 0xc257457e), TOBN(0x933f48d4, 0x8700611b),
+     TOBN(0xc365af88, 0x4912beb2), TOBN(0x7f5a4de6, 0x162edf94),
+     TOBN(0xc646ba7c, 0x0c32f34b), TOBN(0x632c6af3, 0xb2091074),
+     TOBN(0x58d4f2e3, 0x753e43a9), TOBN(0x70e1d217, 0x24d4e23f),
+     TOBN(0xb24bf729, 0xafede6a6), TOBN(0x7f4a94d8, 0x710c8b60),
+     TOBN(0xaad90a96, 0x8d4faa6a), TOBN(0xd9ed0b32, 0xb066b690),
+     TOBN(0x52fcd37b, 0x78b6dbfd), TOBN(0x0b64615e, 0x8bd2b431),
+     TOBN(0x228e2048, 0xcfb9fad5), TOBN(0xbeaa386d, 0x240b76bd),
+     TOBN(0x2d6681c8, 0x90dad7bc), TOBN(0x3e553fc3, 0x06d38f5e),
+     TOBN(0xf27cdb9b, 0x9d5f9750), TOBN(0x3e85c52a, 0xd28c5b0e),
+     TOBN(0x190795af, 0x5247c39b), TOBN(0x547831eb, 0xbddd6828),
+     TOBN(0xf327a227, 0x4a82f424), TOBN(0x36919c78, 0x7e47f89d),
+     TOBN(0xe4783919, 0x43c7392c), TOBN(0xf101b9aa, 0x2316fefe),
+     TOBN(0xbcdc9e9c, 0x1c5009d2), TOBN(0xfb55ea13, 0x9cd18345),
+     TOBN(0xf5b5e231, 0xa3ce77c7), TOBN(0xde6b4527, 0xd2f2cb3d),
+     TOBN(0x10f6a333, 0x9bb26f5f), TOBN(0x1e85db8e, 0x044d85b6),
+     TOBN(0xc3697a08, 0x94197e54), TOBN(0x65e18cc0, 0xa7cb4ea8),
+     TOBN(0xa38c4f50, 0xa471fe6e), TOBN(0xf031747a, 0x2f13439c),
+     TOBN(0x53c4a6ba, 0xc007318b), TOBN(0xa8da3ee5, 0x1deccb3d),
+     TOBN(0x0555b31c, 0x558216b1), TOBN(0x90c7810c, 0x2f79e6c2),
+     TOBN(0x9b669f4d, 0xfe8eed3c), TOBN(0x70398ec8, 0xe0fac126),
+     TOBN(0xa96a449e, 0xf701b235), TOBN(0x0ceecdb3, 0xeb94f395),
+     TOBN(0x285fc368, 0xd0cb7431), TOBN(0x0d37bb52, 0x16a18c64),
+     TOBN(0x05110d38, 0xb880d2dd), TOBN(0xa60f177b, 0x65930d57),
+     TOBN(0x7da34a67, 0xf36235f5), TOBN(0x47f5e17c, 0x183816b9),
+     TOBN(0xc7664b57, 0xdb394af4), TOBN(0x39ba215d, 0x7036f789),
+     TOBN(0x46d2ca0e, 0x2f27b472), TOBN(0xc42647ee, 0xf73a84b7),
+     TOBN(0x44bc7545, 0x64488f1d), TOBN(0xaa922708, 0xf4cf85d5),
+     TOBN(0x721a01d5, 0x53e4df63), TOBN(0x649c0c51, 0x5db46ced),
+     TOBN(0x6bf0d64e, 0x3cffcb6c), TOBN(0xe3bf93fe, 0x50f71d96),
+     TOBN(0x75044558, 0xbcc194a0), TOBN(0x16ae3372, 0x6afdc554),
+     TOBN(0xbfc01adf, 0x5ca48f3f), TOBN(0x64352f06, 0xe22a9b84),
+     TOBN(0xcee54da1, 0xc1099e4a), TOBN(0xbbda54e8, 0xfa1b89c0),
+     TOBN(0x166a3df5, 0x6f6e55fb), TOBN(0x1ca44a24, 0x20176f88),
+     TOBN(0x936afd88, 0xdfb7b5ff), TOBN(0xe34c2437, 0x8611d4a0),
+     TOBN(0x7effbb75, 0x86142103), TOBN(0x6704ba1b, 0x1f34fc4d),
+     TOBN(0x7c2a468f, 0x10c1b122), TOBN(0x36b3a610, 0x8c6aace9),
+     TOBN(0xabfcc0a7, 0x75a0d050), TOBN(0x066f9197, 0x3ce33e32),
+     TOBN(0xce905ef4, 0x29fe09be), TOBN(0x89ee25ba, 0xa8376351),
+     TOBN(0x2a3ede22, 0xfd29dc76), TOBN(0x7fd32ed9, 0x36f17260),
+     TOBN(0x0cadcf68, 0x284b4126), TOBN(0x63422f08, 0xa7951fc8),
+     TOBN(0x562b24f4, 0x0807e199), TOBN(0xfe9ce5d1, 0x22ad4490),
+     TOBN(0xc2f51b10, 0x0db2b1b4), TOBN(0xeb3613ff, 0xe4541d0d),
+     TOBN(0xbd2c4a05, 0x2680813b), TOBN(0x527aa55d, 0x561b08d6),
+     TOBN(0xa9f8a40e, 0xa7205558), TOBN(0xe3eea56f, 0x243d0bec),
+     TOBN(0x7b853817, 0xa0ff58b3), TOBN(0xb67d3f65, 0x1a69e627),
+     TOBN(0x0b76bbb9, 0xa869b5d6), TOBN(0xa3afeb82, 0x546723ed),
+     TOBN(0x5f24416d, 0x3e554892), TOBN(0x8413b53d, 0x430e2a45),
+     TOBN(0x99c56aee, 0x9032a2a0), TOBN(0x09432bf6, 0xeec367b1),
+     TOBN(0x552850c6, 0xdaf0ecc1), TOBN(0x49ebce55, 0x5bc92048),
+     TOBN(0xdfb66ba6, 0x54811307), TOBN(0x1b84f797, 0x6f298597),
+     TOBN(0x79590481, 0x8d1d7a0d), TOBN(0xd9fabe03, 0x3a6fa556),
+     TOBN(0xa40f9c59, 0xba9e5d35), TOBN(0xcb1771c1, 0xf6247577),
+     TOBN(0x542a47ca, 0xe9a6312b), TOBN(0xa34b3560, 0x552dd8c5),
+     TOBN(0xfdf94de0, 0x0d794716), TOBN(0xd46124a9, 0x9c623094),
+     TOBN(0x56b7435d, 0x68afe8b4), TOBN(0x27f20540, 0x6c0d8ea1),
+     TOBN(0x12b77e14, 0x73186898), TOBN(0xdbc3dd46, 0x7479490f),
+     TOBN(0x951a9842, 0xc03b0c05), TOBN(0x8b1b3bb3, 0x7921bc96),
+     TOBN(0xa573b346, 0x2b202e0a), TOBN(0x77e4665d, 0x47254d56),
+     TOBN(0x08b70dfc, 0xd23e3984), TOBN(0xab86e8bc, 0xebd14236),
+     TOBN(0xaa3e07f8, 0x57114ba7), TOBN(0x5ac71689, 0xab0ef4f2),
+     TOBN(0x88fca384, 0x0139d9af), TOBN(0x72733f88, 0x76644af0),
+     TOBN(0xf122f72a, 0x65d74f4a), TOBN(0x13931577, 0xa5626c7a),
+     TOBN(0xd5b5d9eb, 0x70f8d5a4), TOBN(0x375adde7, 0xd7bbb228),
+     TOBN(0x31e88b86, 0x0c1c0b32), TOBN(0xd1f568c4, 0x173edbaa),
+     TOBN(0x1592fc83, 0x5459df02), TOBN(0x2beac0fb, 0x0fcd9a7e),
+     TOBN(0xb0a6fdb8, 0x1b473b0a), TOBN(0xe3224c6f, 0x0fe8fc48),
+     TOBN(0x680bd00e, 0xe87edf5b), TOBN(0x30385f02, 0x20e77cf5),
+     TOBN(0xe9ab98c0, 0x4d42d1b2), TOBN(0x72d191d2, 0xd3816d77),
+     TOBN(0x1564daca, 0x0917d9e5), TOBN(0x394eab59, 0x1f8fed7f),
+     TOBN(0xa209aa8d, 0x7fbb3896), TOBN(0x5564f3b9, 0xbe6ac98e),
+     TOBN(0xead21d05, 0xd73654ef), TOBN(0x68d1a9c4, 0x13d78d74),
+     TOBN(0x61e01708, 0x6d4973a0), TOBN(0x83da3500, 0x46e6d32a),
+     TOBN(0x6a3dfca4, 0x68ae0118), TOBN(0xa1b9a4c9, 0xd02da069),
+     TOBN(0x0b2ff9c7, 0xebab8302), TOBN(0x98af07c3, 0x944ba436),
+     TOBN(0x85997326, 0x995f0f9f), TOBN(0x467fade0, 0x71b58bc6),
+     TOBN(0x47e4495a, 0xbd625a2b), TOBN(0xfdd2d01d, 0x33c3b8cd),
+     TOBN(0x2c38ae28, 0xc693f9fa), TOBN(0x48622329, 0x348f7999),
+     TOBN(0x97bf738e, 0x2161f583), TOBN(0x15ee2fa7, 0x565e8cc9),
+     TOBN(0xa1a5c845, 0x5777e189), TOBN(0xcc10bee0, 0x456f2829),
+     TOBN(0x8ad95c56, 0xda762bd5), TOBN(0x152e2214, 0xe9d91da8),
+     TOBN(0x975b0e72, 0x7cb23c74), TOBN(0xfd5d7670, 0xa90c66df),
+     TOBN(0xb5b5b8ad, 0x225ffc53), TOBN(0xab6dff73, 0xfaded2ae),
+     TOBN(0xebd56781, 0x6f4cbe9d), TOBN(0x0ed8b249, 0x6a574bd7),
+     TOBN(0x41c246fe, 0x81a881fa), TOBN(0x91564805, 0xc3db9c70),
+     TOBN(0xd7c12b08, 0x5b862809), TOBN(0x1facd1f1, 0x55858d7b),
+     TOBN(0x7693747c, 0xaf09e92a), TOBN(0x3b69dcba, 0x189a425f),
+     TOBN(0x0be28e9f, 0x967365ef), TOBN(0x57300eb2, 0xe801f5c9),
+     TOBN(0x93b8ac6a, 0xd583352f), TOBN(0xa2cf1f89, 0xcd05b2b7),
+     TOBN(0x7c0c9b74, 0x4dcc40cc), TOBN(0xfee38c45, 0xada523fb),
+     TOBN(0xb49a4dec, 0x1099cc4d), TOBN(0x325c377f, 0x69f069c6),
+     TOBN(0xe12458ce, 0x476cc9ff), TOBN(0x580e0b6c, 0xc6d4cb63),
+     TOBN(0xd561c8b7, 0x9072289b), TOBN(0x0377f264, 0xa619e6da),
+     TOBN(0x26685362, 0x88e591a5), TOBN(0xa453a7bd, 0x7523ca2b),
+     TOBN(0x8a9536d2, 0xc1df4533), TOBN(0xc8e50f2f, 0xbe972f79),
+     TOBN(0xd433e50f, 0x6d3549cf), TOBN(0x6f33696f, 0xfacd665e),
+     TOBN(0x695bfdac, 0xce11fcb4), TOBN(0x810ee252, 0xaf7c9860),
+     TOBN(0x65450fe1, 0x7159bb2c), TOBN(0xf7dfbebe, 0x758b357b),
+     TOBN(0x2b057e74, 0xd69fea72), TOBN(0xd485717a, 0x92731745),}
+    ,
+    {TOBN(0x896c42e8, 0xee36860c), TOBN(0xdaf04dfd, 0x4113c22d),
+     TOBN(0x1adbb7b7, 0x44104213), TOBN(0xe5fd5fa1, 0x1fd394ea),
+     TOBN(0x68235d94, 0x1a4e0551), TOBN(0x6772cfbe, 0x18d10151),
+     TOBN(0x276071e3, 0x09984523), TOBN(0xe4e879de, 0x5a56ba98),
+     TOBN(0xaaafafb0, 0x285b9491), TOBN(0x01a0be88, 0x1e4c705e),
+     TOBN(0xff1d4f5d, 0x2ad9caab), TOBN(0x6e349a4a, 0xc37a233f),
+     TOBN(0xcf1c1246, 0x4a1c6a16), TOBN(0xd99e6b66, 0x29383260),
+     TOBN(0xea3d4366, 0x5f6d5471), TOBN(0x36974d04, 0xff8cc89b),
+     TOBN(0xc26c49a1, 0xcfe89d80), TOBN(0xb42c026d, 0xda9c8371),
+     TOBN(0xca6c013a, 0xdad066d2), TOBN(0xfb8f7228, 0x56a4f3ee),
+     TOBN(0x08b579ec, 0xd850935b), TOBN(0x34c1a74c, 0xd631e1b3),
+     TOBN(0xcb5fe596, 0xac198534), TOBN(0x39ff21f6, 0xe1f24f25),
+     TOBN(0x27f29e14, 0x8f929057), TOBN(0x7a64ae06, 0xc0c853df),
+     TOBN(0x256cd183, 0x58e9c5ce), TOBN(0x9d9cce82, 0xded092a5),
+     TOBN(0xcc6e5979, 0x6e93b7c7), TOBN(0xe1e47092, 0x31bb9e27),
+     TOBN(0xb70b3083, 0xaa9e29a0), TOBN(0xbf181a75, 0x3785e644),
+     TOBN(0xf53f2c65, 0x8ead09f7), TOBN(0x1335e1d5, 0x9780d14d),
+     TOBN(0x69cc20e0, 0xcd1b66bc), TOBN(0x9b670a37, 0xbbe0bfc8),
+     TOBN(0xce53dc81, 0x28efbeed), TOBN(0x0c74e77c, 0x8326a6e5),
+     TOBN(0x3604e0d2, 0xb88e9a63), TOBN(0xbab38fca, 0x13dc2248),
+     TOBN(0x8ed6e8c8, 0x5c0a3f1e), TOBN(0xbcad2492, 0x7c87c37f),
+     TOBN(0xfdfb62bb, 0x9ee3b78d), TOBN(0xeba8e477, 0xcbceba46),
+     TOBN(0x37d38cb0, 0xeeaede4b), TOBN(0x0bc498e8, 0x7976deb6),
+     TOBN(0xb2944c04, 0x6b6147fb), TOBN(0x8b123f35, 0xf71f9609),
+     TOBN(0xa155dcc7, 0xde79dc24), TOBN(0xf1168a32, 0x558f69cd),
+     TOBN(0xbac21595, 0x0d1850df), TOBN(0x15c8295b, 0xb204c848),
+     TOBN(0xf661aa36, 0x7d8184ff), TOBN(0xc396228e, 0x30447bdb),
+     TOBN(0x11cd5143, 0xbde4a59e), TOBN(0xe3a26e3b, 0x6beab5e6),
+     TOBN(0xd3b3a13f, 0x1402b9d0), TOBN(0x573441c3, 0x2c7bc863),
+     TOBN(0x4b301ec4, 0x578c3e6e), TOBN(0xc26fc9c4, 0x0adaf57e),
+     TOBN(0x96e71bfd, 0x7493cea3), TOBN(0xd05d4b3f, 0x1af81456),
+     TOBN(0xdaca2a8a, 0x6a8c608f), TOBN(0x53ef07f6, 0x0725b276),
+     TOBN(0x07a5fbd2, 0x7824fc56), TOBN(0x34675218, 0x13289077),
+     TOBN(0x5bf69fd5, 0xe0c48349), TOBN(0xa613ddd3, 0xb6aa7875),
+     TOBN(0x7f78c19c, 0x5450d866), TOBN(0x46f4409c, 0x8f84a481),
+     TOBN(0x9f1d1928, 0x90fce239), TOBN(0x016c4168, 0xb2ce44b9),
+     TOBN(0xbae023f0, 0xc7435978), TOBN(0xb152c888, 0x20e30e19),
+     TOBN(0x9c241645, 0xe3fa6faf), TOBN(0x735d95c1, 0x84823e60),
+     TOBN(0x03197573, 0x03955317), TOBN(0x0b4b02a9, 0xf03b4995),
+     TOBN(0x076bf559, 0x70274600), TOBN(0x32c5cc53, 0xaaf57508),
+     TOBN(0xe8af6d1f, 0x60624129), TOBN(0xb7bc5d64, 0x9a5e2b5e),
+     TOBN(0x3814b048, 0x5f082d72), TOBN(0x76f267f2, 0xce19677a),
+     TOBN(0x626c630f, 0xb36eed93), TOBN(0x55230cd7, 0x3bf56803),
+     TOBN(0x78837949, 0xce2736a0), TOBN(0x0d792d60, 0xaa6c55f1),
+     TOBN(0x0318dbfd, 0xd5c7c5d2), TOBN(0xb38f8da7, 0x072b342d),
+     TOBN(0x3569bddc, 0x7b8de38a), TOBN(0xf25b5887, 0xa1c94842),
+     TOBN(0xb2d5b284, 0x2946ad60), TOBN(0x854f29ad, 0xe9d1707e),
+     TOBN(0xaa5159dc, 0x2c6a4509), TOBN(0x899f94c0, 0x57189837),
+     TOBN(0xcf6adc51, 0xf4a55b03), TOBN(0x261762de, 0x35e3b2d5),
+     TOBN(0x4cc43012, 0x04827b51), TOBN(0xcd22a113, 0xc6021442),
+     TOBN(0xce2fd61a, 0x247c9569), TOBN(0x59a50973, 0xd152beca),
+     TOBN(0x6c835a11, 0x63a716d4), TOBN(0xc26455ed, 0x187dedcf),
+     TOBN(0x27f536e0, 0x49ce89e7), TOBN(0x18908539, 0xcc890cb5),
+     TOBN(0x308909ab, 0xd83c2aa1), TOBN(0xecd3142b, 0x1ab73bd3),
+     TOBN(0x6a85bf59, 0xb3f5ab84), TOBN(0x3c320a68, 0xf2bea4c6),
+     TOBN(0xad8dc538, 0x6da4541f), TOBN(0xeaf34eb0, 0xb7c41186),
+     TOBN(0x1c780129, 0x977c97c4), TOBN(0x5ff9beeb, 0xc57eb9fa),
+     TOBN(0xa24d0524, 0xc822c478), TOBN(0xfd8eec2a, 0x461cd415),
+     TOBN(0xfbde194e, 0xf027458c), TOBN(0xb4ff5319, 0x1d1be115),
+     TOBN(0x63f874d9, 0x4866d6f4), TOBN(0x35c75015, 0xb21ad0c9),
+     TOBN(0xa6b5c9d6, 0x46ac49d2), TOBN(0x42c77c0b, 0x83137aa9),
+     TOBN(0x24d000fc, 0x68225a38), TOBN(0x0f63cfc8, 0x2fe1e907),
+     TOBN(0x22d1b01b, 0xc6441f95), TOBN(0x7d38f719, 0xec8e448f),
+     TOBN(0x9b33fa5f, 0x787fb1ba), TOBN(0x94dcfda1, 0x190158df),
+     TOBN(0xc47cb339, 0x5f6d4a09), TOBN(0x6b4f355c, 0xee52b826),
+     TOBN(0x3d100f5d, 0xf51b930a), TOBN(0xf4512fac, 0x9f668f69),
+     TOBN(0x546781d5, 0x206c4c74), TOBN(0xd021d4d4, 0xcb4d2e48),
+     TOBN(0x494a54c2, 0xca085c2d), TOBN(0xf1dbaca4, 0x520850a8),
+     TOBN(0x63c79326, 0x490a1aca), TOBN(0xcb64dd9c, 0x41526b02),
+     TOBN(0xbb772591, 0xa2979258), TOBN(0x3f582970, 0x48d97846),
+     TOBN(0xd66b70d1, 0x7c213ba7), TOBN(0xc28febb5, 0xe8a0ced4),
+     TOBN(0x6b911831, 0xc10338c1), TOBN(0x0d54e389, 0xbf0126f3),
+     TOBN(0x7048d460, 0x4af206ee), TOBN(0x786c88f6, 0x77e97cb9),
+     TOBN(0xd4375ae1, 0xac64802e), TOBN(0x469bcfe1, 0xd53ec11c),
+     TOBN(0xfc9b340d, 0x47062230), TOBN(0xe743bb57, 0xc5b4a3ac),
+     TOBN(0xfe00b4aa, 0x59ef45ac), TOBN(0x29a4ef23, 0x59edf188),
+     TOBN(0x40242efe, 0xb483689b), TOBN(0x2575d3f6, 0x513ac262),
+     TOBN(0xf30037c8, 0x0ca6db72), TOBN(0xc9fcce82, 0x98864be2),
+     TOBN(0x84a112ff, 0x0149362d), TOBN(0x95e57582, 0x1c4ae971),
+     TOBN(0x1fa4b1a8, 0x945cf86c), TOBN(0x4525a734, 0x0b024a2f),
+     TOBN(0xe76c8b62, 0x8f338360), TOBN(0x483ff593, 0x28edf32b),
+     TOBN(0x67e8e90a, 0x298b1aec), TOBN(0x9caab338, 0x736d9a21),
+     TOBN(0x5c09d2fd, 0x66892709), TOBN(0x2496b4dc, 0xb55a1d41),
+     TOBN(0x93f5fb1a, 0xe24a4394), TOBN(0x08c75049, 0x6fa8f6c1),
+     TOBN(0xcaead1c2, 0xc905d85f), TOBN(0xe9d7f790, 0x0733ae57),
+     TOBN(0x24c9a65c, 0xf07cdd94), TOBN(0x7389359c, 0xa4b55931),
+     TOBN(0xf58709b7, 0x367e45f7), TOBN(0x1f203067, 0xcb7e7adc),
+     TOBN(0x82444bff, 0xc7b72818), TOBN(0x07303b35, 0xbaac8033),
+     TOBN(0x1e1ee4e4, 0xd13b7ea1), TOBN(0xe6489b24, 0xe0e74180),
+     TOBN(0xa5f2c610, 0x7e70ef70), TOBN(0xa1655412, 0xbdd10894),
+     TOBN(0x555ebefb, 0x7af4194e), TOBN(0x533c1c3c, 0x8e89bd9c),
+     TOBN(0x735b9b57, 0x89895856), TOBN(0x15fb3cd2, 0x567f5c15),
+     TOBN(0x057fed45, 0x526f09fd), TOBN(0xe8a4f10c, 0x8128240a),
+     TOBN(0x9332efc4, 0xff2bfd8d), TOBN(0x214e77a0, 0xbd35aa31),
+     TOBN(0x32896d73, 0x14faa40e), TOBN(0x767867ec, 0x01e5f186),
+     TOBN(0xc9adf8f1, 0x17a1813e), TOBN(0xcb6cda78, 0x54741795),
+     TOBN(0xb7521b6d, 0x349d51aa), TOBN(0xf56b5a9e, 0xe3c7b8e9),
+     TOBN(0xc6f1e5c9, 0x32a096df), TOBN(0x083667c4, 0xa3635024),
+     TOBN(0x365ea135, 0x18087f2f), TOBN(0xf1b8eaac, 0xd136e45d),
+     TOBN(0xc8a0e484, 0x73aec989), TOBN(0xd75a324b, 0x142c9259),
+     TOBN(0xb7b4d001, 0x01dae185), TOBN(0x45434e0b, 0x9b7a94bc),
+     TOBN(0xf54339af, 0xfbd8cb0b), TOBN(0xdcc4569e, 0xe98ef49e),
+     TOBN(0x7789318a, 0x09a51299), TOBN(0x81b4d206, 0xb2b025d8),
+     TOBN(0xf64aa418, 0xfae85792), TOBN(0x3e50258f, 0xacd7baf7),
+     TOBN(0xdce84cdb, 0x2996864b), TOBN(0xa2e67089, 0x1f485fa4),
+     TOBN(0xb28b2bb6, 0x534c6a5a), TOBN(0x31a7ec6b, 0xc94b9d39),
+     TOBN(0x1d217766, 0xd6bc20da), TOBN(0x4acdb5ec, 0x86761190),
+     TOBN(0x68726328, 0x73701063), TOBN(0x4d24ee7c, 0x2128c29b),
+     TOBN(0xc072ebd3, 0xa19fd868), TOBN(0x612e481c, 0xdb8ddd3b),
+     TOBN(0xb4e1d754, 0x1a64d852), TOBN(0x00ef95ac, 0xc4c6c4ab),
+     TOBN(0x1536d2ed, 0xaa0a6c46), TOBN(0x61294086, 0x43774790),
+     TOBN(0x54af25e8, 0x343fda10), TOBN(0x9ff9d98d, 0xfd25d6f2),
+     TOBN(0x0746af7c, 0x468b8835), TOBN(0x977a31cb, 0x730ecea7),
+     TOBN(0xa5096b80, 0xc2cf4a81), TOBN(0xaa986833, 0x6458c37a),
+     TOBN(0x6af29bf3, 0xa6bd9d34), TOBN(0x6a62fe9b, 0x33c5d854),
+     TOBN(0x50e6c304, 0xb7133b5e), TOBN(0x04b60159, 0x7d6e6848),
+     TOBN(0x4cd296df, 0x5579bea4), TOBN(0x10e35ac8, 0x5ceedaf1),
+     TOBN(0x04c4c5fd, 0xe3bcc5b1), TOBN(0x95f9ee8a, 0x89412cf9),
+     TOBN(0x2c9459ee, 0x82b6eb0f), TOBN(0x2e845765, 0x95c2aadd),
+     TOBN(0x774a84ae, 0xd327fcfe), TOBN(0xd8c93722, 0x0368d476),
+     TOBN(0x0dbd5748, 0xf83e8a3b), TOBN(0xa579aa96, 0x8d2495f3),
+     TOBN(0x535996a0, 0xae496e9b), TOBN(0x07afbfe9, 0xb7f9bcc2),
+     TOBN(0x3ac1dc6d, 0x5b7bd293), TOBN(0x3b592cff, 0x7022323d),
+     TOBN(0xba0deb98, 0x9c0a3e76), TOBN(0x18e78e9f, 0x4b197acb),
+     TOBN(0x211cde10, 0x296c36ef), TOBN(0x7ee89672, 0x82c4da77),
+     TOBN(0xb617d270, 0xa57836da), TOBN(0xf0cd9c31, 0x9cb7560b),
+     TOBN(0x01fdcbf7, 0xe455fe90), TOBN(0x3fb53cbb, 0x7e7334f3),
+     TOBN(0x781e2ea4, 0x4e7de4ec), TOBN(0x8adab3ad, 0x0b384fd0),
+     TOBN(0x129eee2f, 0x53d64829), TOBN(0x7a471e17, 0xa261492b),
+     TOBN(0xe4f9adb9, 0xe4cb4a2c), TOBN(0x3d359f6f, 0x97ba2c2d),
+     TOBN(0x346c6786, 0x0aacd697), TOBN(0x92b444c3, 0x75c2f8a8),
+     TOBN(0xc79fa117, 0xd85df44e), TOBN(0x56782372, 0x398ddf31),
+     TOBN(0x60e690f2, 0xbbbab3b8), TOBN(0x4851f8ae, 0x8b04816b),
+     TOBN(0xc72046ab, 0x9c92e4d2), TOBN(0x518c74a1, 0x7cf3136b),
+     TOBN(0xff4eb50a, 0xf9877d4c), TOBN(0x14578d90, 0xa919cabb),
+     TOBN(0x8218f8c4, 0xac5eb2b6), TOBN(0xa3ccc547, 0x542016e4),
+     TOBN(0x025bf48e, 0x327f8349), TOBN(0xf3e97346, 0xf43cb641),
+     TOBN(0xdc2bafdf, 0x500f1085), TOBN(0x57167876, 0x2f063055),
+     TOBN(0x5bd914b9, 0x411925a6), TOBN(0x7c078d48, 0xa1123de5),
+     TOBN(0xee6bf835, 0x182b165d), TOBN(0xb11b5e5b, 0xba519727),
+     TOBN(0xe33ea76c, 0x1eea7b85), TOBN(0x2352b461, 0x92d4f85e),
+     TOBN(0xf101d334, 0xafe115bb), TOBN(0xfabc1294, 0x889175a3),
+     TOBN(0x7f6bcdc0, 0x5233f925), TOBN(0xe0a802db, 0xe77fec55),
+     TOBN(0xbdb47b75, 0x8069b659), TOBN(0x1c5e12de, 0xf98fbd74),
+     TOBN(0x869c58c6, 0x4b8457ee), TOBN(0xa5360f69, 0x4f7ea9f7),
+     TOBN(0xe576c09f, 0xf460b38f), TOBN(0x6b70d548, 0x22b7fb36),
+     TOBN(0x3fd237f1, 0x3bfae315), TOBN(0x33797852, 0xcbdff369),
+     TOBN(0x97df25f5, 0x25b516f9), TOBN(0x46f388f2, 0xba38ad2d),
+     TOBN(0x656c4658, 0x89d8ddbb), TOBN(0x8830b26e, 0x70f38ee8),
+     TOBN(0x4320fd5c, 0xde1212b0), TOBN(0xc34f30cf, 0xe4a2edb2),
+     TOBN(0xabb131a3, 0x56ab64b8), TOBN(0x7f77f0cc, 0xd99c5d26),
+     TOBN(0x66856a37, 0xbf981d94), TOBN(0x19e76d09, 0x738bd76e),
+     TOBN(0xe76c8ac3, 0x96238f39), TOBN(0xc0a482be, 0xa830b366),
+     TOBN(0xb7b8eaff, 0x0b4eb499), TOBN(0x8ecd83bc, 0x4bfb4865),
+     TOBN(0x971b2cb7, 0xa2f3776f), TOBN(0xb42176a4, 0xf4b88adf),
+     TOBN(0xb9617df5, 0xbe1fa446), TOBN(0x8b32d508, 0xcd031bd2),
+     TOBN(0x1c6bd47d, 0x53b618c0), TOBN(0xc424f46c, 0x6a227923),
+     TOBN(0x7303ffde, 0xdd92d964), TOBN(0xe9712878, 0x71b5abf2),
+     TOBN(0x8f48a632, 0xf815561d), TOBN(0x85f48ff5, 0xd3c055d1),
+     TOBN(0x222a1427, 0x7525684f), TOBN(0xd0d841a0, 0x67360cc3),
+     TOBN(0x4245a926, 0x0b9267c6), TOBN(0xc78913f1, 0xcf07f863),
+     TOBN(0xaa844c8e, 0x4d0d9e24), TOBN(0xa42ad522, 0x3d5f9017),
+     TOBN(0xbd371749, 0xa2c989d5), TOBN(0x928292df, 0xe1f5e78e),
+     TOBN(0x493b383e, 0x0a1ea6da), TOBN(0x5136fd8d, 0x13aee529),
+     TOBN(0x860c44b1, 0xf2c34a99), TOBN(0x3b00aca4, 0xbf5855ac),
+     TOBN(0xabf6aaa0, 0xfaaf37be), TOBN(0x65f43682, 0x2a53ec08),
+     TOBN(0x1d9a5801, 0xa11b12e1), TOBN(0x78a7ab2c, 0xe20ed475),
+     TOBN(0x0de1067e, 0x9a41e0d5), TOBN(0x30473f5f, 0x305023ea),
+     TOBN(0xdd3ae09d, 0x169c7d97), TOBN(0x5cd5baa4, 0xcfaef9cd),
+     TOBN(0x5cd7440b, 0x65a44803), TOBN(0xdc13966a, 0x47f364de),
+     TOBN(0x077b2be8, 0x2b8357c1), TOBN(0x0cb1b4c5, 0xe9d57c2a),
+     TOBN(0x7a4ceb32, 0x05ff363e), TOBN(0xf310fa4d, 0xca35a9ef),
+     TOBN(0xdbb7b352, 0xf97f68c6), TOBN(0x0c773b50, 0x0b02cf58),
+     TOBN(0xea2e4821, 0x3c1f96d9), TOBN(0xffb357b0, 0xeee01815),
+     TOBN(0xb9c924cd, 0xe0f28039), TOBN(0x0b36c95a, 0x46a3fbe4),
+     TOBN(0x1faaaea4, 0x5e46db6c), TOBN(0xcae575c3, 0x1928aaff),
+     TOBN(0x7f671302, 0xa70dab86), TOBN(0xfcbd12a9, 0x71c58cfc),
+     TOBN(0xcbef9acf, 0xbee0cb92), TOBN(0x573da0b9, 0xf8c1b583),
+     TOBN(0x4752fcfe, 0x0d41d550), TOBN(0xe7eec0e3, 0x2155cffe),
+     TOBN(0x0fc39fcb, 0x545ae248), TOBN(0x522cb8d1, 0x8065f44e),
+     TOBN(0x263c962a, 0x70cbb96c), TOBN(0xe034362a, 0xbcd124a9),
+     TOBN(0xf120db28, 0x3c2ae58d), TOBN(0xb9a38d49, 0xfef6d507),
+     TOBN(0xb1fd2a82, 0x1ff140fd), TOBN(0xbd162f30, 0x20aee7e0),
+     TOBN(0x4e17a5d4, 0xcb251949), TOBN(0x2aebcb83, 0x4f7e1c3d),
+     TOBN(0x608eb25f, 0x937b0527), TOBN(0xf42e1e47, 0xeb7d9997),
+     TOBN(0xeba699c4, 0xb8a53a29), TOBN(0x1f921c71, 0xe091b536),
+     TOBN(0xcce29e7b, 0x5b26bbd5), TOBN(0x7a8ef5ed, 0x3b61a680),
+     TOBN(0xe5ef8043, 0xba1f1c7e), TOBN(0x16ea8217, 0x18158dda),
+     TOBN(0x01778a2b, 0x599ff0f9), TOBN(0x68a923d7, 0x8104fc6b),
+     TOBN(0x5bfa44df, 0xda694ff3), TOBN(0x4f7199db, 0xf7667f12),
+     TOBN(0xc06d8ff6, 0xe46f2a79), TOBN(0x08b5dead, 0xe9f8131d),
+     TOBN(0x02519a59, 0xabb4ce7c), TOBN(0xc4f710bc, 0xb42aec3e),
+     TOBN(0x3d77b057, 0x78bde41a), TOBN(0x6474bf80, 0xb4186b5a),
+     TOBN(0x048b3f67, 0x88c65741), TOBN(0xc64519de, 0x03c7c154),
+     TOBN(0xdf073846, 0x0edfcc4f), TOBN(0x319aa737, 0x48f1aa6b),
+     TOBN(0x8b9f8a02, 0xca909f77), TOBN(0x90258139, 0x7580bfef),
+     TOBN(0xd8bfd3ca, 0xc0c22719), TOBN(0xc60209e4, 0xc9ca151e),
+     TOBN(0x7a744ab5, 0xd9a1a69c), TOBN(0x6de5048b, 0x14937f8f),
+     TOBN(0x171938d8, 0xe115ac04), TOBN(0x7df70940, 0x1c6b16d2),
+     TOBN(0xa6aeb663, 0x7f8e94e7), TOBN(0xc130388e, 0x2a2cf094),
+     TOBN(0x1850be84, 0x77f54e6e), TOBN(0x9f258a72, 0x65d60fe5),
+     TOBN(0xff7ff0c0, 0x6c9146d6), TOBN(0x039aaf90, 0xe63a830b),
+     TOBN(0x38f27a73, 0x9460342f), TOBN(0x4703148c, 0x3f795f8a),
+     TOBN(0x1bb5467b, 0x9681a97e), TOBN(0x00931ba5, 0xecaeb594),
+     TOBN(0xcdb6719d, 0x786f337c), TOBN(0xd9c01cd2, 0xe704397d),
+     TOBN(0x0f4a3f20, 0x555c2fef), TOBN(0x00452509, 0x7c0af223),
+     TOBN(0x54a58047, 0x84db8e76), TOBN(0x3bacf1aa, 0x93c8aa06),
+     TOBN(0x11ca957c, 0xf7919422), TOBN(0x50641053, 0x78cdaa40),
+     TOBN(0x7a303874, 0x9f7144ae), TOBN(0x170c963f, 0x43d4acfd),
+     TOBN(0x5e148149, 0x58ddd3ef), TOBN(0xa7bde582, 0x9e72dba8),
+     TOBN(0x0769da8b, 0x6fa68750), TOBN(0xfa64e532, 0x572e0249),
+     TOBN(0xfcaadf9d, 0x2619ad31), TOBN(0x87882daa, 0xa7b349cd),
+     TOBN(0x9f6eb731, 0x6c67a775), TOBN(0xcb10471a, 0xefc5d0b1),
+     TOBN(0xb433750c, 0xe1b806b2), TOBN(0x19c5714d, 0x57b1ae7e),
+     TOBN(0xc0dc8b7b, 0xed03fd3f), TOBN(0xdd03344f, 0x31bc194e),
+     TOBN(0xa66c52a7, 0x8c6320b5), TOBN(0x8bc82ce3, 0xd0b6fd93),
+     TOBN(0xf8e13501, 0xb35f1341), TOBN(0xe53156dd, 0x25a43e42),
+     TOBN(0xd3adf27e, 0x4daeb85c), TOBN(0xb81d8379, 0xbbeddeb5),
+     TOBN(0x1b0b546e, 0x2e435867), TOBN(0x9020eb94, 0xeba5dd60),
+     TOBN(0x37d91161, 0x8210cb9d), TOBN(0x4c596b31, 0x5c91f1cf),
+     TOBN(0xb228a90f, 0x0e0b040d), TOBN(0xbaf02d82, 0x45ff897f),
+     TOBN(0x2aac79e6, 0x00fa6122), TOBN(0x24828817, 0x8e36f557),
+     TOBN(0xb9521d31, 0x113ec356), TOBN(0x9e48861e, 0x15eff1f8),
+     TOBN(0x2aa1d412, 0xe0d41715), TOBN(0x71f86203, 0x53f131b8),
+     TOBN(0xf60da8da, 0x3fd19408), TOBN(0x4aa716dc, 0x278d9d99),
+     TOBN(0x394531f7, 0xa8c51c90), TOBN(0xb560b0e8, 0xf59db51c),
+     TOBN(0xa28fc992, 0xfa34bdad), TOBN(0xf024fa14, 0x9cd4f8bd),
+     TOBN(0x5cf530f7, 0x23a9d0d3), TOBN(0x615ca193, 0xe28c9b56),
+     TOBN(0x6d2a483d, 0x6f73c51e), TOBN(0xa4cb2412, 0xea0dc2dd),
+     TOBN(0x50663c41, 0x1eb917ff), TOBN(0x3d3a74cf, 0xeade299e),
+     TOBN(0x29b3990f, 0x4a7a9202), TOBN(0xa9bccf59, 0xa7b15c3d),
+     TOBN(0x66a3ccdc, 0xa5df9208), TOBN(0x48027c14, 0x43f2f929),
+     TOBN(0xd385377c, 0x40b557f0), TOBN(0xe001c366, 0xcd684660),
+     TOBN(0x1b18ed6b, 0xe2183a27), TOBN(0x879738d8, 0x63210329),
+     TOBN(0xa687c74b, 0xbda94882), TOBN(0xd1bbcc48, 0xa684b299),
+     TOBN(0xaf6f1112, 0x863b3724), TOBN(0x6943d1b4, 0x2c8ce9f8),
+     TOBN(0xe044a3bb, 0x098cafb4), TOBN(0x27ed2310, 0x60d48caf),
+     TOBN(0x542b5675, 0x3a31b84d), TOBN(0xcbf3dd50, 0xfcddbed7),
+     TOBN(0x25031f16, 0x41b1d830), TOBN(0xa7ec851d, 0xcb0c1e27),
+     TOBN(0xac1c8fe0, 0xb5ae75db), TOBN(0xb24c7557, 0x08c52120),
+     TOBN(0x57f811dc, 0x1d4636c3), TOBN(0xf8436526, 0x681a9939),
+     TOBN(0x1f6bc6d9, 0x9c81adb3), TOBN(0x840f8ac3, 0x5b7d80d4),
+     TOBN(0x731a9811, 0xf4387f1a), TOBN(0x7c501cd3, 0xb5156880),
+     TOBN(0xa5ca4a07, 0xdfe68867), TOBN(0xf123d8f0, 0x5fcea120),
+     TOBN(0x1fbb0e71, 0xd607039e), TOBN(0x2b70e215, 0xcd3a4546),
+     TOBN(0x32d2f01d, 0x53324091), TOBN(0xb796ff08, 0x180ab19b),
+     TOBN(0x32d87a86, 0x3c57c4aa), TOBN(0x2aed9caf, 0xb7c49a27),
+     TOBN(0x9fb35eac, 0x31630d98), TOBN(0x338e8cdf, 0x5c3e20a3),
+     TOBN(0x80f16182, 0x66cde8db), TOBN(0x4e159980, 0x2d72fd36),
+     TOBN(0xd7b8f13b, 0x9b6e5072), TOBN(0xf5213907, 0x3b7b5dc1),
+     TOBN(0x4d431f1d, 0x8ce4396e), TOBN(0x37a1a680, 0xa7ed2142),
+     TOBN(0xbf375696, 0xd01aaf6b), TOBN(0xaa1c0c54, 0xe63aab66),
+     TOBN(0x3014368b, 0x4ed80940), TOBN(0x67e6d056, 0x7a6fcedd),
+     TOBN(0x7c208c49, 0xca97579f), TOBN(0xfe3d7a81, 0xa23597f6),
+     TOBN(0x5e203202, 0x7e096ae2), TOBN(0xb1f3e1e7, 0x24b39366),
+     TOBN(0x26da26f3, 0x2fdcdffc), TOBN(0x79422f1d, 0x6097be83),}
+    ,
+    {TOBN(0x263a2cfb, 0x9db3b381), TOBN(0x9c3a2dee, 0xd4df0a4b),
+     TOBN(0x728d06e9, 0x7d04e61f), TOBN(0x8b1adfbc, 0x42449325),
+     TOBN(0x6ec1d939, 0x7e053a1b), TOBN(0xee2be5c7, 0x66daf707),
+     TOBN(0x80ba1e14, 0x810ac7ab), TOBN(0xdd2ae778, 0xf530f174),
+     TOBN(0x0435d97a, 0x205b9d8b), TOBN(0x6eb8f064, 0x056756d4),
+     TOBN(0xd5e88a8b, 0xb6f8210e), TOBN(0x070ef12d, 0xec9fd9ea),
+     TOBN(0x4d849505, 0x3bcc876a), TOBN(0x12a75338, 0xa7404ce3),
+     TOBN(0xd22b49e1, 0xb8a1db5e), TOBN(0xec1f2051, 0x14bfa5ad),
+     TOBN(0xadbaeb79, 0xb6828f36), TOBN(0x9d7a0258, 0x01bd5b9e),
+     TOBN(0xeda01e0d, 0x1e844b0c), TOBN(0x4b625175, 0x887edfc9),
+     TOBN(0x14109fdd, 0x9669b621), TOBN(0x88a2ca56, 0xf6f87b98),
+     TOBN(0xfe2eb788, 0x170df6bc), TOBN(0x0cea06f4, 0xffa473f9),
+     TOBN(0x43ed81b5, 0xc4e83d33), TOBN(0xd9f35879, 0x5efd488b),
+     TOBN(0x164a620f, 0x9deb4d0f), TOBN(0xc6927bdb, 0xac6a7394),
+     TOBN(0x45c28df7, 0x9f9e0f03), TOBN(0x2868661e, 0xfcd7e1a9),
+     TOBN(0x7cf4e8d0, 0xffa348f1), TOBN(0x6bd4c284, 0x398538e0),
+     TOBN(0x2618a091, 0x289a8619), TOBN(0xef796e60, 0x6671b173),
+     TOBN(0x664e46e5, 0x9090c632), TOBN(0xa38062d4, 0x1e66f8fb),
+     TOBN(0x6c744a20, 0x0573274e), TOBN(0xd07b67e4, 0xa9271394),
+     TOBN(0x391223b2, 0x6bdc0e20), TOBN(0xbe2d93f1, 0xeb0a05a7),
+     TOBN(0xf23e2e53, 0x3f36d141), TOBN(0xe84bb3d4, 0x4dfca442),
+     TOBN(0xb804a48d, 0x6b7c023a), TOBN(0x1e16a8fa, 0x76431c3b),
+     TOBN(0x1b5452ad, 0xddd472e0), TOBN(0x7d405ee7, 0x0d1ee127),
+     TOBN(0x50fc6f1d, 0xffa27599), TOBN(0x351ac53c, 0xbf391b35),
+     TOBN(0x7efa14b8, 0x4444896b), TOBN(0x64974d2f, 0xf94027fb),
+     TOBN(0xefdcd0e8, 0xde84487d), TOBN(0x8c45b260, 0x2b48989b),
+     TOBN(0xa8fcbbc2, 0xd8463487), TOBN(0xd1b2b3f7, 0x3fbc476c),
+     TOBN(0x21d005b7, 0xc8f443c0), TOBN(0x518f2e67, 0x40c0139c),
+     TOBN(0x56036e8c, 0x06d75fc1), TOBN(0x2dcf7bb7, 0x3249a89f),
+     TOBN(0x81dd1d3d, 0xe245e7dd), TOBN(0xf578dc4b, 0xebd6e2a7),
+     TOBN(0x4c028903, 0xdf2ce7a0), TOBN(0xaee36288, 0x9c39afac),
+     TOBN(0xdc847c31, 0x146404ab), TOBN(0x6304c0d8, 0xa4e97818),
+     TOBN(0xae51dca2, 0xa91f6791), TOBN(0x2abe4190, 0x9baa9efc),
+     TOBN(0xd9d2e2f4, 0x559c7ac1), TOBN(0xe82f4b51, 0xfc9f773a),
+     TOBN(0xa7713027, 0x4073e81c), TOBN(0xc0276fac, 0xfbb596fc),
+     TOBN(0x1d819fc9, 0xa684f70c), TOBN(0x29b47fdd, 0xc9f7b1e0),
+     TOBN(0x358de103, 0x459b1940), TOBN(0xec881c59, 0x5b013e93),
+     TOBN(0x51574c93, 0x49532ad3), TOBN(0x2db1d445, 0xb37b46de),
+     TOBN(0xc6445b87, 0xdf239fd8), TOBN(0xc718af75, 0x151d24ee),
+     TOBN(0xaea1c4a4, 0xf43c6259), TOBN(0x40c0e5d7, 0x70be02f7),
+     TOBN(0x6a4590f4, 0x721b33f2), TOBN(0x2124f1fb, 0xfedf04ea),
+     TOBN(0xf8e53cde, 0x9745efe7), TOBN(0xe7e10432, 0x65f046d9),
+     TOBN(0xc3fca28e, 0xe4d0c7e6), TOBN(0x847e339a, 0x87253b1b),
+     TOBN(0x9b595348, 0x3743e643), TOBN(0xcb6a0a0b, 0x4fd12fc5),
+     TOBN(0xfb6836c3, 0x27d02dcc), TOBN(0x5ad00982, 0x7a68bcc2),
+     TOBN(0x1b24b44c, 0x005e912d), TOBN(0xcc83d20f, 0x811fdcfe),
+     TOBN(0x36527ec1, 0x666fba0c), TOBN(0x69948197, 0x14754635),
+     TOBN(0xfcdcb1a8, 0x556da9c2), TOBN(0xa5934267, 0x81a732b2),
+     TOBN(0xec1214ed, 0xa714181d), TOBN(0x609ac13b, 0x6067b341),
+     TOBN(0xff4b4c97, 0xa545df1f), TOBN(0xa1240501, 0x34d2076b),
+     TOBN(0x6efa0c23, 0x1409ca97), TOBN(0x254cc1a8, 0x20638c43),
+     TOBN(0xd4e363af, 0xdcfb46cd), TOBN(0x62c2adc3, 0x03942a27),
+     TOBN(0xc67b9df0, 0x56e46483), TOBN(0xa55abb20, 0x63736356),
+     TOBN(0xab93c098, 0xc551bc52), TOBN(0x382b49f9, 0xb15fe64b),
+     TOBN(0x9ec221ad, 0x4dff8d47), TOBN(0x79caf615, 0x437df4d6),
+     TOBN(0x5f13dc64, 0xbb456509), TOBN(0xe4c589d9, 0x191f0714),
+     TOBN(0x27b6a8ab, 0x3fd40e09), TOBN(0xe455842e, 0x77313ea9),
+     TOBN(0x8b51d1e2, 0x1f55988b), TOBN(0x5716dd73, 0x062bbbfc),
+     TOBN(0x633c11e5, 0x4e8bf3de), TOBN(0x9a0e77b6, 0x1b85be3b),
+     TOBN(0x56510729, 0x0911cca6), TOBN(0x27e76495, 0xefa6590f),
+     TOBN(0xe4ac8b33, 0x070d3aab), TOBN(0x2643672b, 0x9a2cd5e5),
+     TOBN(0x52eff79b, 0x1cfc9173), TOBN(0x665ca49b, 0x90a7c13f),
+     TOBN(0x5a8dda59, 0xb3efb998), TOBN(0x8a5b922d, 0x052f1341),
+     TOBN(0xae9ebbab, 0x3cf9a530), TOBN(0x35986e7b, 0xf56da4d7),
+     TOBN(0x3a636b5c, 0xff3513cc), TOBN(0xbb0cf8ba, 0x3198f7dd),
+     TOBN(0xb8d40522, 0x41f16f86), TOBN(0x760575d8, 0xde13a7bf),
+     TOBN(0x36f74e16, 0x9f7aa181), TOBN(0x163a3ecf, 0xf509ed1c),
+     TOBN(0x6aead61f, 0x3c40a491), TOBN(0x158c95fc, 0xdfe8fcaa),
+     TOBN(0xa3991b6e, 0x13cda46f), TOBN(0x79482415, 0x342faed0),
+     TOBN(0xf3ba5bde, 0x666b5970), TOBN(0x1d52e6bc, 0xb26ab6dd),
+     TOBN(0x768ba1e7, 0x8608dd3d), TOBN(0x4930db2a, 0xea076586),
+     TOBN(0xd9575714, 0xe7dc1afa), TOBN(0x1fc7bf7d, 0xf7c58817),
+     TOBN(0x6b47accd, 0xd9eee96c), TOBN(0x0ca277fb, 0xe58cec37),
+     TOBN(0x113fe413, 0xe702c42a), TOBN(0xdd1764ee, 0xc47cbe51),
+     TOBN(0x041e7cde, 0x7b3ed739), TOBN(0x50cb7459, 0x5ce9e1c0),
+     TOBN(0x35568513, 0x2925b212), TOBN(0x7cff95c4, 0x001b081c),
+     TOBN(0x63ee4cbd, 0x8088b454), TOBN(0xdb7f32f7, 0x9a9e0c8a),
+     TOBN(0xb377d418, 0x6b2447cb), TOBN(0xe3e982aa, 0xd370219b),
+     TOBN(0x06ccc1e4, 0xc2a2a593), TOBN(0x72c36865, 0x0773f24f),
+     TOBN(0xa13b4da7, 0x95859423), TOBN(0x8bbf1d33, 0x75040c8f),
+     TOBN(0x726f0973, 0xda50c991), TOBN(0x48afcd5b, 0x822d6ee2),
+     TOBN(0xe5fc718b, 0x20fd7771), TOBN(0xb9e8e77d, 0xfd0807a1),
+     TOBN(0x7f5e0f44, 0x99a7703d), TOBN(0x6972930e, 0x618e36f3),
+     TOBN(0x2b7c77b8, 0x23807bbe), TOBN(0xe5b82405, 0xcb27ff50),
+     TOBN(0xba8b8be3, 0xbd379062), TOBN(0xd64b7a1d, 0x2dce4a92),
+     TOBN(0x040a73c5, 0xb2952e37), TOBN(0x0a9e252e, 0xd438aeca),
+     TOBN(0xdd43956b, 0xc39d3bcb), TOBN(0x1a31ca00, 0xb32b2d63),
+     TOBN(0xd67133b8, 0x5c417a18), TOBN(0xd08e4790, 0x2ef442c8),
+     TOBN(0x98cb1ae9, 0x255c0980), TOBN(0x4bd86381, 0x2b4a739f),
+     TOBN(0x5a5c31e1, 0x1e4a45a1), TOBN(0x1e5d55fe, 0x9cb0db2f),
+     TOBN(0x74661b06, 0x8ff5cc29), TOBN(0x026b389f, 0x0eb8a4f4),
+     TOBN(0x536b21a4, 0x58848c24), TOBN(0x2e5bf8ec, 0x81dc72b0),
+     TOBN(0x03c187d0, 0xad886aac), TOBN(0x5c16878a, 0xb771b645),
+     TOBN(0xb07dfc6f, 0xc74045ab), TOBN(0x2c6360bf, 0x7800caed),
+     TOBN(0x24295bb5, 0xb9c972a3), TOBN(0xc9e6f88e, 0x7c9a6dba),
+     TOBN(0x90ffbf24, 0x92a79aa6), TOBN(0xde29d50a, 0x41c26ac2),
+     TOBN(0x9f0af483, 0xd309cbe6), TOBN(0x5b020d8a, 0xe0bced4f),
+     TOBN(0x606e986d, 0xb38023e3), TOBN(0xad8f2c9d, 0x1abc6933),
+     TOBN(0x19292e1d, 0xe7400e93), TOBN(0xfe3e18a9, 0x52be5e4d),
+     TOBN(0xe8e9771d, 0x2e0680bf), TOBN(0x8c5bec98, 0xc54db063),
+     TOBN(0x2af9662a, 0x74a55d1f), TOBN(0xe3fbf28f, 0x046f66d8),
+     TOBN(0xa3a72ab4, 0xd4dc4794), TOBN(0x09779f45, 0x5c7c2dd8),
+     TOBN(0xd893bdaf, 0xc3d19d8d), TOBN(0xd5a75094, 0x57d6a6df),
+     TOBN(0x8cf8fef9, 0x952e6255), TOBN(0x3da67cfb, 0xda9a8aff),
+     TOBN(0x4c23f62a, 0x2c160dcd), TOBN(0x34e6c5e3, 0x8f90eaef),
+     TOBN(0x35865519, 0xa9a65d5a), TOBN(0x07c48aae, 0x8fd38a3d),
+     TOBN(0xb7e7aeda, 0x50068527), TOBN(0x2c09ef23, 0x1c90936a),
+     TOBN(0x31ecfeb6, 0xe879324c), TOBN(0xa0871f6b, 0xfb0ec938),
+     TOBN(0xb1f0fb68, 0xd84d835d), TOBN(0xc90caf39, 0x861dc1e6),
+     TOBN(0x12e5b046, 0x7594f8d7), TOBN(0x26897ae2, 0x65012b92),
+     TOBN(0xbcf68a08, 0xa4d6755d), TOBN(0x403ee41c, 0x0991fbda),
+     TOBN(0x733e343e, 0x3bbf17e8), TOBN(0xd2c7980d, 0x679b3d65),
+     TOBN(0x33056232, 0xd2e11305), TOBN(0x966be492, 0xf3c07a6f),
+     TOBN(0x6a8878ff, 0xbb15509d), TOBN(0xff221101, 0x0a9b59a4),
+     TOBN(0x6c9f564a, 0xabe30129), TOBN(0xc6f2c940, 0x336e64cf),
+     TOBN(0x0fe75262, 0x8b0c8022), TOBN(0xbe0267e9, 0x6ae8db87),
+     TOBN(0x22e192f1, 0x93bc042b), TOBN(0xf085b534, 0xb237c458),
+     TOBN(0xa0d192bd, 0x832c4168), TOBN(0x7a76e9e3, 0xbdf6271d),
+     TOBN(0x52a882fa, 0xb88911b5), TOBN(0xc85345e4, 0xb4db0eb5),
+     TOBN(0xa3be02a6, 0x81a7c3ff), TOBN(0x51889c8c, 0xf0ec0469),
+     TOBN(0x9d031369, 0xa5e829e5), TOBN(0xcbb4c6fc, 0x1607aa41),
+     TOBN(0x75ac59a6, 0x241d84c1), TOBN(0xc043f2bf, 0x8829e0ee),
+     TOBN(0x82a38f75, 0x8ea5e185), TOBN(0x8bda40b9, 0xd87cbd9f),
+     TOBN(0x9e65e75e, 0x2d8fc601), TOBN(0x3d515f74, 0xa35690b3),
+     TOBN(0x534acf4f, 0xda79e5ac), TOBN(0x68b83b3a, 0x8630215f),
+     TOBN(0x5c748b2e, 0xd085756e), TOBN(0xb0317258, 0xe5d37cb2),
+     TOBN(0x6735841a, 0xc5ccc2c4), TOBN(0x7d7dc96b, 0x3d9d5069),
+     TOBN(0xa147e410, 0xfd1754bd), TOBN(0x65296e94, 0xd399ddd5),
+     TOBN(0xf6b5b2d0, 0xbc8fa5bc), TOBN(0x8a5ead67, 0x500c277b),
+     TOBN(0x214625e6, 0xdfa08a5d), TOBN(0x51fdfedc, 0x959cf047),
+     TOBN(0x6bc9430b, 0x289fca32), TOBN(0xe36ff0cf, 0x9d9bdc3f),
+     TOBN(0x2fe187cb, 0x58ea0ede), TOBN(0xed66af20, 0x5a900b3f),
+     TOBN(0x00e0968b, 0x5fa9f4d6), TOBN(0x2d4066ce, 0x37a362e7),
+     TOBN(0xa99a9748, 0xbd07e772), TOBN(0x710989c0, 0x06a4f1d0),
+     TOBN(0xd5dedf35, 0xce40cbd8), TOBN(0xab55c5f0, 0x1743293d),
+     TOBN(0x766f1144, 0x8aa24e2c), TOBN(0x94d874f8, 0x605fbcb4),
+     TOBN(0xa365f0e8, 0xa518001b), TOBN(0xee605eb6, 0x9d04ef0f),
+     TOBN(0x5a3915cd, 0xba8d4d25), TOBN(0x44c0e1b8, 0xb5113472),
+     TOBN(0xcbb024e8, 0x8b6740dc), TOBN(0x89087a53, 0xee1d4f0c),
+     TOBN(0xa88fa05c, 0x1fc4e372), TOBN(0x8bf395cb, 0xaf8b3af2),
+     TOBN(0x1e71c9a1, 0xdeb8568b), TOBN(0xa35daea0, 0x80fb3d32),
+     TOBN(0xe8b6f266, 0x2cf8fb81), TOBN(0x6d51afe8, 0x9490696a),
+     TOBN(0x81beac6e, 0x51803a19), TOBN(0xe3d24b7f, 0x86219080),
+     TOBN(0x727cfd9d, 0xdf6f463c), TOBN(0x8c6865ca, 0x72284ee8),
+     TOBN(0x32c88b7d, 0xb743f4ef), TOBN(0x3793909b, 0xe7d11dce),
+     TOBN(0xd398f922, 0x2ff2ebe8), TOBN(0x2c70ca44, 0xe5e49796),
+     TOBN(0xdf4d9929, 0xcb1131b1), TOBN(0x7826f298, 0x25888e79),
+     TOBN(0x4d3a112c, 0xf1d8740a), TOBN(0x00384cb6, 0x270afa8b),
+     TOBN(0xcb64125b, 0x3ab48095), TOBN(0x3451c256, 0x62d05106),
+     TOBN(0xd73d577d, 0xa4955845), TOBN(0x39570c16, 0xbf9f4433),
+     TOBN(0xd7dfaad3, 0xadecf263), TOBN(0xf1c3d8d1, 0xdc76e102),
+     TOBN(0x5e774a58, 0x54c6a836), TOBN(0xdad4b672, 0x3e92d47b),
+     TOBN(0xbe7e990f, 0xf0d796a0), TOBN(0x5fc62478, 0xdf0e8b02),
+     TOBN(0x8aae8bf4, 0x030c00ad), TOBN(0x3d2db93b, 0x9004ba0f),
+     TOBN(0xe48c8a79, 0xd85d5ddc), TOBN(0xe907caa7, 0x6bb07f34),
+     TOBN(0x58db343a, 0xa39eaed5), TOBN(0x0ea6e007, 0xadaf5724),
+     TOBN(0xe00df169, 0xd23233f3), TOBN(0x3e322796, 0x77cb637f),
+     TOBN(0x1f897c0e, 0x1da0cf6c), TOBN(0xa651f5d8, 0x31d6bbdd),
+     TOBN(0xdd61af19, 0x1a230c76), TOBN(0xbd527272, 0xcdaa5e4a),
+     TOBN(0xca753636, 0xd0abcd7e), TOBN(0x78bdd37c, 0x370bd8dc),
+     TOBN(0xc23916c2, 0x17cd93fe), TOBN(0x65b97a4d, 0xdadce6e2),
+     TOBN(0xe04ed4eb, 0x174e42f8), TOBN(0x1491ccaa, 0xbb21480a),
+     TOBN(0x145a8280, 0x23196332), TOBN(0x3c3862d7, 0x587b479a),
+     TOBN(0x9f4a88a3, 0x01dcd0ed), TOBN(0x4da2b7ef, 0x3ea12f1f),
+     TOBN(0xf8e7ae33, 0xb126e48e), TOBN(0x404a0b32, 0xf494e237),
+     TOBN(0x9beac474, 0xc55acadb), TOBN(0x4ee5cf3b, 0xcbec9fd9),
+     TOBN(0x336b33b9, 0x7df3c8c3), TOBN(0xbd905fe3, 0xb76808fd),
+     TOBN(0x8f436981, 0xaa45c16a), TOBN(0x255c5bfa, 0x3dd27b62),
+     TOBN(0x71965cbf, 0xc3dd9b4d), TOBN(0xce23edbf, 0xfc068a87),
+     TOBN(0xb78d4725, 0x745b029b), TOBN(0x74610713, 0xcefdd9bd),
+     TOBN(0x7116f75f, 0x1266bf52), TOBN(0x02046722, 0x18e49bb6),
+     TOBN(0xdf43df9f, 0x3d6f19e3), TOBN(0xef1bc7d0, 0xe685cb2f),
+     TOBN(0xcddb27c1, 0x7078c432), TOBN(0xe1961b9c, 0xb77fedb7),
+     TOBN(0x1edc2f5c, 0xc2290570), TOBN(0x2c3fefca, 0x19cbd886),
+     TOBN(0xcf880a36, 0xc2af389a), TOBN(0x96c610fd, 0xbda71cea),
+     TOBN(0xf03977a9, 0x32aa8463), TOBN(0x8eb7763f, 0x8586d90a),
+     TOBN(0x3f342454, 0x2a296e77), TOBN(0xc8718683, 0x42837a35),
+     TOBN(0x7dc71090, 0x6a09c731), TOBN(0x54778ffb, 0x51b816db),
+     TOBN(0x6b33bfec, 0xaf06defd), TOBN(0xfe3c105f, 0x8592b70b),
+     TOBN(0xf937fda4, 0x61da6114), TOBN(0x3c13e651, 0x4c266ad7),
+     TOBN(0xe363a829, 0x855938e8), TOBN(0x2eeb5d9e, 0x9de54b72),
+     TOBN(0xbeb93b0e, 0x20ccfab9), TOBN(0x3dffbb5f, 0x25e61a25),
+     TOBN(0x7f655e43, 0x1acc093d), TOBN(0x0cb6cc3d, 0x3964ce61),
+     TOBN(0x6ab283a1, 0xe5e9b460), TOBN(0x55d787c5, 0xa1c7e72d),
+     TOBN(0x4d2efd47, 0xdeadbf02), TOBN(0x11e80219, 0xac459068),
+     TOBN(0x810c7626, 0x71f311f0), TOBN(0xfa17ef8d, 0x4ab6ef53),
+     TOBN(0xaf47fd25, 0x93e43bff), TOBN(0x5cb5ff3f, 0x0be40632),
+     TOBN(0x54687106, 0x8ee61da3), TOBN(0x7764196e, 0xb08afd0f),
+     TOBN(0x831ab3ed, 0xf0290a8f), TOBN(0xcae81966, 0xcb47c387),
+     TOBN(0xaad7dece, 0x184efb4f), TOBN(0xdcfc53b3, 0x4749110e),
+     TOBN(0x6698f23c, 0x4cb632f9), TOBN(0xc42a1ad6, 0xb91f8067),
+     TOBN(0xb116a81d, 0x6284180a), TOBN(0xebedf5f8, 0xe901326f),
+     TOBN(0xf2274c9f, 0x97e3e044), TOBN(0x42018520, 0x11d09fc9),
+     TOBN(0x56a65f17, 0xd18e6e23), TOBN(0x2ea61e2a, 0x352b683c),
+     TOBN(0x27d291bc, 0x575eaa94), TOBN(0x9e7bc721, 0xb8ff522d),
+     TOBN(0x5f7268bf, 0xa7f04d6f), TOBN(0x5868c73f, 0xaba41748),
+     TOBN(0x9f85c2db, 0x7be0eead), TOBN(0x511e7842, 0xff719135),
+     TOBN(0x5a06b1e9, 0xc5ea90d7), TOBN(0x0c19e283, 0x26fab631),
+     TOBN(0x8af8f0cf, 0xe9206c55), TOBN(0x89389cb4, 0x3553c06a),
+     TOBN(0x39dbed97, 0xf65f8004), TOBN(0x0621b037, 0xc508991d),
+     TOBN(0x1c52e635, 0x96e78cc4), TOBN(0x5385c8b2, 0x0c06b4a8),
+     TOBN(0xd84ddfdb, 0xb0e87d03), TOBN(0xc49dfb66, 0x934bafad),
+     TOBN(0x7071e170, 0x59f70772), TOBN(0x3a073a84, 0x3a1db56b),
+     TOBN(0x03494903, 0x3b8af190), TOBN(0x7d882de3, 0xd32920f0),
+     TOBN(0x91633f0a, 0xb2cf8940), TOBN(0x72b0b178, 0x6f948f51),
+     TOBN(0x2d28dc30, 0x782653c8), TOBN(0x88829849, 0xdb903a05),
+     TOBN(0xb8095d0c, 0x6a19d2bb), TOBN(0x4b9e7f0c, 0x86f782cb),
+     TOBN(0x7af73988, 0x2d907064), TOBN(0xd12be0fe, 0x8b32643c),
+     TOBN(0x358ed23d, 0x0e165dc3), TOBN(0x3d47ce62, 0x4e2378ce),
+     TOBN(0x7e2bb0b9, 0xfeb8a087), TOBN(0x3246e8ae, 0xe29e10b9),
+     TOBN(0x459f4ec7, 0x03ce2b4d), TOBN(0xe9b4ca1b, 0xbbc077cf),
+     TOBN(0x2613b4f2, 0x0e9940c1), TOBN(0xfc598bb9, 0x047d1eb1),
+     TOBN(0x9744c62b, 0x45036099), TOBN(0xa9dee742, 0x167c65d8),
+     TOBN(0x0c511525, 0xdabe1943), TOBN(0xda110554, 0x93c6c624),
+     TOBN(0xae00a52c, 0x651a3be2), TOBN(0xcda5111d, 0x884449a6),
+     TOBN(0x063c06f4, 0xff33bed1), TOBN(0x73baaf9a, 0x0d3d76b4),
+     TOBN(0x52fb0c9d, 0x7fc63668), TOBN(0x6886c9dd, 0x0c039cde),
+     TOBN(0x602bd599, 0x55b22351), TOBN(0xb00cab02, 0x360c7c13),
+     TOBN(0x8cb616bc, 0x81b69442), TOBN(0x41486700, 0xb55c3cee),
+     TOBN(0x71093281, 0xf49ba278), TOBN(0xad956d9c, 0x64a50710),
+     TOBN(0x9561f28b, 0x638a7e81), TOBN(0x54155cdf, 0x5980ddc3),
+     TOBN(0xb2db4a96, 0xd26f247a), TOBN(0x9d774e4e, 0x4787d100),
+     TOBN(0x1a9e6e2e, 0x078637d2), TOBN(0x1c363e2d, 0x5e0ae06a),
+     TOBN(0x7493483e, 0xe9cfa354), TOBN(0x76843cb3, 0x7f74b98d),
+     TOBN(0xbaca6591, 0xd4b66947), TOBN(0xb452ce98, 0x04460a8c),
+     TOBN(0x6830d246, 0x43768f55), TOBN(0xf4197ed8, 0x7dff12df),
+     TOBN(0x6521b472, 0x400dd0f7), TOBN(0x59f5ca8f, 0x4b1e7093),
+     TOBN(0x6feff11b, 0x080338ae), TOBN(0x0ada31f6, 0xa29ca3c6),
+     TOBN(0x24794eb6, 0x94a2c215), TOBN(0xd83a43ab, 0x05a57ab4),
+     TOBN(0x264a543a, 0x2a6f89fe), TOBN(0x2c2a3868, 0xdd5ec7c2),
+     TOBN(0xd3373940, 0x8439d9b2), TOBN(0x715ea672, 0x0acd1f11),
+     TOBN(0x42c1d235, 0xe7e6cc19), TOBN(0x81ce6e96, 0xb990585c),
+     TOBN(0x04e5dfe0, 0xd809c7bd), TOBN(0xd7b2580c, 0x8f1050ab),
+     TOBN(0x6d91ad78, 0xd8a4176f), TOBN(0x0af556ee, 0x4e2e897c),
+     TOBN(0x162a8b73, 0x921de0ac), TOBN(0x52ac9c22, 0x7ea78400),
+     TOBN(0xee2a4eea, 0xefce2174), TOBN(0xbe61844e, 0x6d637f79),
+     TOBN(0x0491f1bc, 0x789a283b), TOBN(0x72d3ac3d, 0x880836f4),
+     TOBN(0xaa1c5ea3, 0x88e5402d), TOBN(0x1b192421, 0xd5cc473d),
+     TOBN(0x5c0b9998, 0x9dc84cac), TOBN(0xb0a8482d, 0x9c6e75b8),
+     TOBN(0x639961d0, 0x3a191ce2), TOBN(0xda3bc865, 0x6d837930),
+     TOBN(0xca990653, 0x056e6f8f), TOBN(0x84861c41, 0x64d133a7),
+     TOBN(0x8b403276, 0x746abe40), TOBN(0xb7b4d51a, 0xebf8e303),
+     TOBN(0x05b43211, 0x220a255d), TOBN(0xc997152c, 0x02419e6e),
+     TOBN(0x76ff47b6, 0x630c2fea), TOBN(0x50518677, 0x281fdade),
+     TOBN(0x3283b8ba, 0xcf902b0b), TOBN(0x8d4b4eb5, 0x37db303b),
+     TOBN(0xcc89f42d, 0x755011bc), TOBN(0xb43d74bb, 0xdd09d19b),
+     TOBN(0x65746bc9, 0x8adba350), TOBN(0x364eaf8c, 0xb51c1927),
+     TOBN(0x13c76596, 0x10ad72ec), TOBN(0x30045121, 0xf8d40c20),
+     TOBN(0x6d2d99b7, 0xea7b979b), TOBN(0xcd78cd74, 0xe6fb3bcd),
+     TOBN(0x11e45a9e, 0x86cffbfe), TOBN(0x78a61cf4, 0x637024f6),
+     TOBN(0xd06bc872, 0x3d502295), TOBN(0xf1376854, 0x458cb288),
+     TOBN(0xb9db26a1, 0x342f8586), TOBN(0xf33effcf, 0x4beee09e),
+     TOBN(0xd7e0c4cd, 0xb30cfb3a), TOBN(0x6d09b8c1, 0x6c9db4c8),
+     TOBN(0x40ba1a42, 0x07c8d9df), TOBN(0x6fd495f7, 0x1c52c66d),
+     TOBN(0xfb0e169f, 0x275264da), TOBN(0x80c2b746, 0xe57d8362),
+     TOBN(0xedd987f7, 0x49ad7222), TOBN(0xfdc229af, 0x4398ec7b),}
+    ,
+    {TOBN(0xb0d1ed84, 0x52666a58), TOBN(0x4bcb6e00, 0xe6a9c3c2),
+     TOBN(0x3c57411c, 0x26906408), TOBN(0xcfc20755, 0x13556400),
+     TOBN(0xa08b1c50, 0x5294dba3), TOBN(0xa30ba286, 0x8b7dd31e),
+     TOBN(0xd70ba90e, 0x991eca74), TOBN(0x094e142c, 0xe762c2b9),
+     TOBN(0xb81d783e, 0x979f3925), TOBN(0x1efd130a, 0xaf4c89a7),
+     TOBN(0x525c2144, 0xfd1bf7fa), TOBN(0x4b296904, 0x1b265a9e),
+     TOBN(0xed8e9634, 0xb9db65b6), TOBN(0x35c82e32, 0x03599d8a),
+     TOBN(0xdaa7a54f, 0x403563f3), TOBN(0x9df088ad, 0x022c38ab),
+     TOBN(0xe5cfb066, 0xbb3fd30a), TOBN(0x429169da, 0xeff0354e),
+     TOBN(0x809cf852, 0x3524e36c), TOBN(0x136f4fb3, 0x0155be1d),
+     TOBN(0x4826af01, 0x1fbba712), TOBN(0x6ef0f0b4, 0x506ba1a1),
+     TOBN(0xd9928b31, 0x77aea73e), TOBN(0xe2bf6af2, 0x5eaa244e),
+     TOBN(0x8d084f12, 0x4237b64b), TOBN(0x688ebe99, 0xe3ecfd07),
+     TOBN(0x57b8a70c, 0xf6845dd8), TOBN(0x808fc59c, 0x5da4a325),
+     TOBN(0xa9032b2b, 0xa3585862), TOBN(0xb66825d5, 0xedf29386),
+     TOBN(0xb5a5a8db, 0x431ec29b), TOBN(0xbb143a98, 0x3a1e8dc8),
+     TOBN(0x35ee94ce, 0x12ae381b), TOBN(0x3a7f176c, 0x86ccda90),
+     TOBN(0xc63a657e, 0x4606eaca), TOBN(0x9ae5a380, 0x43cd04df),
+     TOBN(0x9bec8d15, 0xed251b46), TOBN(0x1f5d6d30, 0xcaca5e64),
+     TOBN(0x347b3b35, 0x9ff20f07), TOBN(0x4d65f034, 0xf7e4b286),
+     TOBN(0x9e93ba24, 0xf111661e), TOBN(0xedced484, 0xb105eb04),
+     TOBN(0x96dc9ba1, 0xf424b578), TOBN(0xbf8f66b7, 0xe83e9069),
+     TOBN(0x872d4df4, 0xd7ed8216), TOBN(0xbf07f377, 0x8e2cbecf),
+     TOBN(0x4281d899, 0x98e73754), TOBN(0xfec85fbb, 0x8aab8708),
+     TOBN(0x9a3c0dee, 0xa5ba5b0b), TOBN(0xe6a116ce, 0x42d05299),
+     TOBN(0xae9775fe, 0xe9b02d42), TOBN(0x72b05200, 0xa1545cb6),
+     TOBN(0xbc506f7d, 0x31a3b4ea), TOBN(0xe5893078, 0x8bbd9b32),
+     TOBN(0xc8bc5f37, 0xe4b12a97), TOBN(0x6b000c06, 0x4a73b671),
+     TOBN(0x13b5bf22, 0x765fa7d0), TOBN(0x59805bf0, 0x1d6a5370),
+     TOBN(0x67a5e29d, 0x4280db98), TOBN(0x4f53916f, 0x776b1ce3),
+     TOBN(0x714ff61f, 0x33ddf626), TOBN(0x4206238e, 0xa085d103),
+     TOBN(0x1c50d4b7, 0xe5809ee3), TOBN(0x999f450d, 0x85f8eb1d),
+     TOBN(0x658a6051, 0xe4c79e9b), TOBN(0x1394cb73, 0xc66a9fea),
+     TOBN(0x27f31ed5, 0xc6be7b23), TOBN(0xf4c88f36, 0x5aa6f8fe),
+     TOBN(0x0fb0721f, 0x4aaa499e), TOBN(0x68b3a7d5, 0xe3fb2a6b),
+     TOBN(0xa788097d, 0x3a92851d), TOBN(0x060e7f8a, 0xe96f4913),
+     TOBN(0x82eebe73, 0x1a3a93bc), TOBN(0x42bbf465, 0xa21adc1a),
+     TOBN(0xc10b6fa4, 0xef030efd), TOBN(0x247aa4c7, 0x87b097bb),
+     TOBN(0x8b8dc632, 0xf60c77da), TOBN(0x6ffbc26a, 0xc223523e),
+     TOBN(0xa4f6ff11, 0x344579cf), TOBN(0x5825653c, 0x980250f6),
+     TOBN(0xb2dd097e, 0xbc1aa2b9), TOBN(0x07889393, 0x37a0333a),
+     TOBN(0x1cf55e71, 0x37a0db38), TOBN(0x2648487f, 0x792c1613),
+     TOBN(0xdad01336, 0x3fcef261), TOBN(0x6239c81d, 0x0eabf129),
+     TOBN(0x8ee761de, 0x9d276be2), TOBN(0x406a7a34, 0x1eda6ad3),
+     TOBN(0x4bf367ba, 0x4a493b31), TOBN(0x54f20a52, 0x9bf7f026),
+     TOBN(0xb696e062, 0x9795914b), TOBN(0xcddab96d, 0x8bf236ac),
+     TOBN(0x4ff2c70a, 0xed25ea13), TOBN(0xfa1d09eb, 0x81cbbbe7),
+     TOBN(0x88fc8c87, 0x468544c5), TOBN(0x847a670d, 0x696b3317),
+     TOBN(0xf133421e, 0x64bcb626), TOBN(0xaea638c8, 0x26dee0b5),
+     TOBN(0xd6e7680b, 0xb310346c), TOBN(0xe06f4097, 0xd5d4ced3),
+     TOBN(0x09961452, 0x7512a30b), TOBN(0xf3d867fd, 0xe589a59a),
+     TOBN(0x2e73254f, 0x52d0c180), TOBN(0x9063d8a3, 0x333c74ac),
+     TOBN(0xeda6c595, 0xd314e7bc), TOBN(0x2ee7464b, 0x467899ed),
+     TOBN(0x1cef423c, 0x0a1ed5d3), TOBN(0x217e76ea, 0x69cc7613),
+     TOBN(0x27ccce1f, 0xe7cda917), TOBN(0x12d8016b, 0x8a893f16),
+     TOBN(0xbcd6de84, 0x9fc74f6b), TOBN(0xfa5817e2, 0xf3144e61),
+     TOBN(0x1f354164, 0x0821ee4c), TOBN(0x1583eab4, 0x0bc61992),
+     TOBN(0x7490caf6, 0x1d72879f), TOBN(0x998ad9f3, 0xf76ae7b2),
+     TOBN(0x1e181950, 0xa41157f7), TOBN(0xa9d7e1e6, 0xe8da3a7e),
+     TOBN(0x963784eb, 0x8426b95f), TOBN(0x0ee4ed6e, 0x542e2a10),
+     TOBN(0xb79d4cc5, 0xac751e7b), TOBN(0x93f96472, 0xfd4211bd),
+     TOBN(0x8c72d3d2, 0xc8de4fc6), TOBN(0x7b69cbf5, 0xdf44f064),
+     TOBN(0x3da90ca2, 0xf4bf94e1), TOBN(0x1a5325f8, 0xf12894e2),
+     TOBN(0x0a437f6c, 0x7917d60b), TOBN(0x9be70486, 0x96c9cb5d),
+     TOBN(0xb4d880bf, 0xe1dc5c05), TOBN(0xd738adda, 0xeebeeb57),
+     TOBN(0x6f0119d3, 0xdf0fe6a3), TOBN(0x5c686e55, 0x66eaaf5a),
+     TOBN(0x9cb10b50, 0xdfd0b7ec), TOBN(0xbdd0264b, 0x6a497c21),
+     TOBN(0xfc093514, 0x8c546c96), TOBN(0x58a947fa, 0x79dbf42a),
+     TOBN(0xc0b48d4e, 0x49ccd6d7), TOBN(0xff8fb02c, 0x88bd5580),
+     TOBN(0xc75235e9, 0x07d473b2), TOBN(0x4fab1ac5, 0xa2188af3),
+     TOBN(0x030fa3bc, 0x97576ec0), TOBN(0xe8c946e8, 0x0b7e7d2f),
+     TOBN(0x40a5c9cc, 0x70305600), TOBN(0x6d8260a9, 0xc8b013b4),
+     TOBN(0x0368304f, 0x70bba85c), TOBN(0xad090da1, 0xa4a0d311),
+     TOBN(0x7170e870, 0x2415eec1), TOBN(0xbfba35fe, 0x8461ea47),
+     TOBN(0x6279019a, 0xc1e91938), TOBN(0xa47638f3, 0x1afc415f),
+     TOBN(0x36c65cbb, 0xbcba0e0f), TOBN(0x02160efb, 0x034e2c48),
+     TOBN(0xe6c51073, 0x615cd9e4), TOBN(0x498ec047, 0xf1243c06),
+     TOBN(0x3e5a8809, 0xb17b3d8c), TOBN(0x5cd99e61, 0x0cc565f1),
+     TOBN(0x81e312df, 0x7851dafe), TOBN(0xf156f5ba, 0xa79061e2),
+     TOBN(0x80d62b71, 0x880c590e), TOBN(0xbec9746f, 0x0a39faa1),
+     TOBN(0x1d98a9c1, 0xc8ed1f7a), TOBN(0x09e43bb5, 0xa81d5ff2),
+     TOBN(0xd5f00f68, 0x0da0794a), TOBN(0x412050d9, 0x661aa836),
+     TOBN(0xa89f7c4e, 0x90747e40), TOBN(0x6dc05ebb, 0xb62a3686),
+     TOBN(0xdf4de847, 0x308e3353), TOBN(0x53868fbb, 0x9fb53bb9),
+     TOBN(0x2b09d2c3, 0xcfdcf7dd), TOBN(0x41a9fce3, 0x723fcab4),
+     TOBN(0x73d905f7, 0x07f57ca3), TOBN(0x080f9fb1, 0xac8e1555),
+     TOBN(0x7c088e84, 0x9ba7a531), TOBN(0x07d35586, 0xed9a147f),
+     TOBN(0x602846ab, 0xaf48c336), TOBN(0x7320fd32, 0x0ccf0e79),
+     TOBN(0xaa780798, 0xb18bd1ff), TOBN(0x52c2e300, 0xafdd2905),
+     TOBN(0xf27ea3d6, 0x434267cd), TOBN(0x8b96d16d, 0x15605b5f),
+     TOBN(0x7bb31049, 0x4b45706b), TOBN(0xe7f58b8e, 0x743d25f8),
+     TOBN(0xe9b5e45b, 0x87f30076), TOBN(0xd19448d6, 0x5d053d5a),
+     TOBN(0x1ecc8cb9, 0xd3210a04), TOBN(0x6bc7d463, 0xdafb5269),
+     TOBN(0x3e59b10a, 0x67c3489f), TOBN(0x1769788c, 0x65641e1b),
+     TOBN(0x8a53b82d, 0xbd6cb838), TOBN(0x7066d6e6, 0x236d5f22),
+     TOBN(0x03aa1c61, 0x6908536e), TOBN(0xc971da0d, 0x66ae9809),
+     TOBN(0x01b3a86b, 0xc49a2fac), TOBN(0x3b8420c0, 0x3092e77a),
+     TOBN(0x02057300, 0x7d6fb556), TOBN(0x6941b2a1, 0xbff40a87),
+     TOBN(0x140b6308, 0x0658ff2a), TOBN(0x87804363, 0x3424ab36),
+     TOBN(0x0253bd51, 0x5751e299), TOBN(0xc75bcd76, 0x449c3e3a),
+     TOBN(0x92eb4090, 0x7f8f875d), TOBN(0x9c9d754e, 0x56c26bbf),
+     TOBN(0x158cea61, 0x8110bbe7), TOBN(0x62a6b802, 0x745f91ea),
+     TOBN(0xa79c41aa, 0xc6e7394b), TOBN(0x445b6a83, 0xad57ef10),
+     TOBN(0x0c5277eb, 0x6ea6f40c), TOBN(0x319fe96b, 0x88633365),
+     TOBN(0x0b0fc61f, 0x385f63cb), TOBN(0x41250c84, 0x22bdd127),
+     TOBN(0x67d153f1, 0x09e942c2), TOBN(0x60920d08, 0xc021ad5d),
+     TOBN(0x229f5746, 0x724d81a5), TOBN(0xb7ffb892, 0x5bba3299),
+     TOBN(0x518c51a1, 0xde413032), TOBN(0x2a9bfe77, 0x3c2fd94c),
+     TOBN(0xcbcde239, 0x3191f4fd), TOBN(0x43093e16, 0xd3d6ada1),
+     TOBN(0x184579f3, 0x58769606), TOBN(0x2c94a8b3, 0xd236625c),
+     TOBN(0x6922b9c0, 0x5c437d8e), TOBN(0x3d4ae423, 0xd8d9f3c8),
+     TOBN(0xf72c31c1, 0x2e7090a2), TOBN(0x4ac3f5f3, 0xd76a55bd),
+     TOBN(0x342508fc, 0x6b6af991), TOBN(0x0d527100, 0x1b5cebbd),
+     TOBN(0xb84740d0, 0xdd440dd7), TOBN(0x748ef841, 0x780162fd),
+     TOBN(0xa8dbfe0e, 0xdfc6fafb), TOBN(0xeadfdf05, 0xf7300f27),
+     TOBN(0x7d06555f, 0xfeba4ec9), TOBN(0x12c56f83, 0x9e25fa97),
+     TOBN(0x77f84203, 0xd39b8c34), TOBN(0xed8b1be6, 0x3125eddb),
+     TOBN(0x5bbf2441, 0xf6e39dc5), TOBN(0xb00f6ee6, 0x6a5d678a),
+     TOBN(0xba456ecf, 0x57d0ea99), TOBN(0xdcae0f58, 0x17e06c43),
+     TOBN(0x01643de4, 0x0f5b4baa), TOBN(0x2c324341, 0xd161b9be),
+     TOBN(0x80177f55, 0xe126d468), TOBN(0xed325f1f, 0x76748e09),
+     TOBN(0x6116004a, 0xcfa9bdc2), TOBN(0x2d8607e6, 0x3a9fb468),
+     TOBN(0x0e573e27, 0x6009d660), TOBN(0x3a525d2e, 0x8d10c5a1),
+     TOBN(0xd26cb45c, 0x3b9009a0), TOBN(0xb6b0cdc0, 0xde9d7448),
+     TOBN(0x949c9976, 0xe1337c26), TOBN(0x6faadebd, 0xd73d68e5),
+     TOBN(0x9e158614, 0xf1b768d9), TOBN(0x22dfa557, 0x9cc4f069),
+     TOBN(0xccd6da17, 0xbe93c6d6), TOBN(0x24866c61, 0xa504f5b9),
+     TOBN(0x2121353c, 0x8d694da1), TOBN(0x1c6ca580, 0x0140b8c6),
+     TOBN(0xc245ad8c, 0xe964021e), TOBN(0xb83bffba, 0x032b82b3),
+     TOBN(0xfaa220c6, 0x47ef9898), TOBN(0x7e8d3ac6, 0x982c948a),
+     TOBN(0x1faa2091, 0xbc2d124a), TOBN(0xbd54c3dd, 0x05b15ff4),
+     TOBN(0x386bf3ab, 0xc87c6fb7), TOBN(0xfb2b0563, 0xfdeb6f66),
+     TOBN(0x4e77c557, 0x5b45afb4), TOBN(0xe9ded649, 0xefb8912d),
+     TOBN(0x7ec9bbf5, 0x42f6e557), TOBN(0x2570dfff, 0x62671f00),
+     TOBN(0x2b3bfb78, 0x88e084bd), TOBN(0xa024b238, 0xf37fe5b4),
+     TOBN(0x44e7dc04, 0x95649aee), TOBN(0x498ca255, 0x5e7ec1d8),
+     TOBN(0x3bc766ea, 0xaaa07e86), TOBN(0x0db6facb, 0xf3608586),
+     TOBN(0xbadd2549, 0xbdc259c8), TOBN(0x95af3c6e, 0x041c649f),
+     TOBN(0xb36a928c, 0x02e30afb), TOBN(0x9b5356ad, 0x008a88b8),
+     TOBN(0x4b67a5f1, 0xcf1d9e9d), TOBN(0xc6542e47, 0xa5d8d8ce),
+     TOBN(0x73061fe8, 0x7adfb6cc), TOBN(0xcc826fd3, 0x98678141),
+     TOBN(0x00e758b1, 0x3c80515a), TOBN(0x6afe3247, 0x41485083),
+     TOBN(0x0fcb08b9, 0xb6ae8a75), TOBN(0xb8cf388d, 0x4acf51e1),
+     TOBN(0x344a5560, 0x6961b9d6), TOBN(0x1a6778b8, 0x6a97fd0c),
+     TOBN(0xd840fdc1, 0xecc4c7e3), TOBN(0xde9fe47d, 0x16db68cc),
+     TOBN(0xe95f89de, 0xa3e216aa), TOBN(0x84f1a6a4, 0x9594a8be),
+     TOBN(0x7ddc7d72, 0x5a7b162b), TOBN(0xc5cfda19, 0xadc817a3),
+     TOBN(0x80a5d350, 0x78b58d46), TOBN(0x93365b13, 0x82978f19),
+     TOBN(0x2e44d225, 0x26a1fc90), TOBN(0x0d6d10d2, 0x4d70705d),
+     TOBN(0xd94b6b10, 0xd70c45f4), TOBN(0x0f201022, 0xb216c079),
+     TOBN(0xcec966c5, 0x658fde41), TOBN(0xa8d2bc7d, 0x7e27601d),
+     TOBN(0xbfcce3e1, 0xff230be7), TOBN(0x3394ff6b, 0x0033ffb5),
+     TOBN(0xd890c509, 0x8132c9af), TOBN(0xaac4b0eb, 0x361e7868),
+     TOBN(0x5194ded3, 0xe82d15aa), TOBN(0x4550bd2e, 0x23ae6b7d),
+     TOBN(0x3fda318e, 0xea5399d4), TOBN(0xd989bffa, 0x91638b80),
+     TOBN(0x5ea124d0, 0xa14aa12d), TOBN(0x1fb1b899, 0x3667b944),
+     TOBN(0x95ec7969, 0x44c44d6a), TOBN(0x91df144a, 0x57e86137),
+     TOBN(0x915fd620, 0x73adac44), TOBN(0x8f01732d, 0x59a83801),
+     TOBN(0xec579d25, 0x3aa0a633), TOBN(0x06de5e7c, 0xc9d6d59c),
+     TOBN(0xc132f958, 0xb1ef8010), TOBN(0x29476f96, 0xe65c1a02),
+     TOBN(0x336a77c0, 0xd34c3565), TOBN(0xef1105b2, 0x1b9f1e9e),
+     TOBN(0x63e6d08b, 0xf9e08002), TOBN(0x9aff2f21, 0xc613809e),
+     TOBN(0xb5754f85, 0x3a80e75d), TOBN(0xde71853e, 0x6bbda681),
+     TOBN(0x86f041df, 0x8197fd7a), TOBN(0x8b332e08, 0x127817fa),
+     TOBN(0x05d99be8, 0xb9c20cda), TOBN(0x89f7aad5, 0xd5cd0c98),
+     TOBN(0x7ef936fe, 0x5bb94183), TOBN(0x92ca0753, 0xb05cd7f2),
+     TOBN(0x9d65db11, 0x74a1e035), TOBN(0x02628cc8, 0x13eaea92),
+     TOBN(0xf2d9e242, 0x49e4fbf2), TOBN(0x94fdfd9b, 0xe384f8b7),
+     TOBN(0x65f56054, 0x63428c6b), TOBN(0x2f7205b2, 0x90b409a5),
+     TOBN(0xf778bb78, 0xff45ae11), TOBN(0xa13045be, 0xc5ee53b2),
+     TOBN(0xe00a14ff, 0x03ef77fe), TOBN(0x689cd59f, 0xffef8bef),
+     TOBN(0x3578f0ed, 0x1e9ade22), TOBN(0xe99f3ec0, 0x6268b6a8),
+     TOBN(0xa2057d91, 0xea1b3c3e), TOBN(0x2d1a7053, 0xb8823a4a),
+     TOBN(0xabbb336a, 0x2cca451e), TOBN(0xcd2466e3, 0x2218bb5d),
+     TOBN(0x3ac1f42f, 0xc8cb762d), TOBN(0x7e312aae, 0x7690211f),
+     TOBN(0xebb9bd73, 0x45d07450), TOBN(0x207c4b82, 0x46c2213f),
+     TOBN(0x99d425c1, 0x375913ec), TOBN(0x94e45e96, 0x67908220),
+     TOBN(0xc08f3087, 0xcd67dbf6), TOBN(0xa5670fbe, 0xc0887056),
+     TOBN(0x6717b64a, 0x66f5b8fc), TOBN(0xd5a56aea, 0x786fec28),
+     TOBN(0xa8c3f55f, 0xc0ff4952), TOBN(0xa77fefae, 0x457ac49b),
+     TOBN(0x29882d7c, 0x98379d44), TOBN(0xd000bdfb, 0x509edc8a),
+     TOBN(0xc6f95979, 0xe66fe464), TOBN(0x504a6115, 0xfa61bde0),
+     TOBN(0x56b3b871, 0xeffea31a), TOBN(0x2d3de26d, 0xf0c21a54),
+     TOBN(0x21dbff31, 0x834753bf), TOBN(0xe67ecf49, 0x69269d86),
+     TOBN(0x7a176952, 0x151fe690), TOBN(0x03515804, 0x7f2adb5f),
+     TOBN(0xee794b15, 0xd1b62a8d), TOBN(0xf004ceec, 0xaae454e6),
+     TOBN(0x0897ea7c, 0xf0386fac), TOBN(0x3b62ff12, 0xd1fca751),
+     TOBN(0x154181df, 0x1b7a04ec), TOBN(0x2008e04a, 0xfb5847ec),
+     TOBN(0xd147148e, 0x41dbd772), TOBN(0x2b419f73, 0x22942654),
+     TOBN(0x669f30d3, 0xe9c544f7), TOBN(0x52a2c223, 0xc8540149),
+     TOBN(0x5da9ee14, 0x634dfb02), TOBN(0x5f074ff0, 0xf47869f3),
+     TOBN(0x74ee878d, 0xa3933acc), TOBN(0xe6510651, 0x4fe35ed1),
+     TOBN(0xb3eb9482, 0xf1012e7a), TOBN(0x51013cc0, 0xa8a566ae),
+     TOBN(0xdd5e9243, 0x47c00d3b), TOBN(0x7fde089d, 0x946bb0e5),
+     TOBN(0x030754fe, 0xc731b4b3), TOBN(0x12a136a4, 0x99fda062),
+     TOBN(0x7c1064b8, 0x5a1a35bc), TOBN(0xbf1f5763, 0x446c84ef),
+     TOBN(0xed29a56d, 0xa16d4b34), TOBN(0x7fba9d09, 0xdca21c4f),
+     TOBN(0x66d7ac00, 0x6d8de486), TOBN(0x60061987, 0x73a2a5e1),
+     TOBN(0x8b400f86, 0x9da28ff0), TOBN(0x3133f708, 0x43c4599c),
+     TOBN(0x9911c9b8, 0xee28cb0d), TOBN(0xcd7e2874, 0x8e0af61d),
+     TOBN(0x5a85f0f2, 0x72ed91fc), TOBN(0x85214f31, 0x9cd4a373),
+     TOBN(0x881fe5be, 0x1925253c), TOBN(0xd8dc98e0, 0x91e8bc76),
+     TOBN(0x7120affe, 0x585cc3a2), TOBN(0x724952ed, 0x735bf97a),
+     TOBN(0x5581e7dc, 0x3eb34581), TOBN(0x5cbff4f2, 0xe52ee57d),
+     TOBN(0x8d320a0e, 0x87d8cc7b), TOBN(0x9beaa7f3, 0xf1d280d0),
+     TOBN(0x7a0b9571, 0x9beec704), TOBN(0x9126332e, 0x5b7f0057),
+     TOBN(0x01fbc1b4, 0x8ed3bd6d), TOBN(0x35bb2c12, 0xd945eb24),
+     TOBN(0x6404694e, 0x9a8ae255), TOBN(0xb6092eec, 0x8d6abfb3),
+     TOBN(0x4d76143f, 0xcc058865), TOBN(0x7b0a5af2, 0x6e249922),
+     TOBN(0x8aef9440, 0x6a50d353), TOBN(0xe11e4bcc, 0x64f0e07a),
+     TOBN(0x4472993a, 0xa14a90fa), TOBN(0x7706e20c, 0xba0c51d4),
+     TOBN(0xf403292f, 0x1532672d), TOBN(0x52573bfa, 0x21829382),
+     TOBN(0x6a7bb6a9, 0x3b5bdb83), TOBN(0x08da65c0, 0xa4a72318),
+     TOBN(0xc58d22aa, 0x63eb065f), TOBN(0x1717596c, 0x1b15d685),
+     TOBN(0x112df0d0, 0xb266d88b), TOBN(0xf688ae97, 0x5941945a),
+     TOBN(0x487386e3, 0x7c292cac), TOBN(0x42f3b50d, 0x57d6985c),
+     TOBN(0x6da4f998, 0x6a90fc34), TOBN(0xc8f257d3, 0x65ca8a8d),
+     TOBN(0xc2feabca, 0x6951f762), TOBN(0xe1bc81d0, 0x74c323ac),
+     TOBN(0x1bc68f67, 0x251a2a12), TOBN(0x10d86587, 0xbe8a70dc),
+     TOBN(0xd648af7f, 0xf0f84d2e), TOBN(0xf0aa9ebc, 0x6a43ac92),
+     TOBN(0x69e3be04, 0x27596893), TOBN(0xb6bb02a6, 0x45bf452b),
+     TOBN(0x0875c11a, 0xf4c698c8), TOBN(0x6652b5c7, 0xbece3794),
+     TOBN(0x7b3755fd, 0x4f5c0499), TOBN(0x6ea16558, 0xb5532b38),
+     TOBN(0xd1c69889, 0xa2e96ef7), TOBN(0x9c773c3a, 0x61ed8f48),
+     TOBN(0x2b653a40, 0x9b323abc), TOBN(0xe26605e1, 0xf0e1d791),
+     TOBN(0x45d41064, 0x4a87157a), TOBN(0x8f9a78b7, 0xcbbce616),
+     TOBN(0xcf1e44aa, 0xc407eddd), TOBN(0x81ddd1d8, 0xa35b964f),
+     TOBN(0x473e339e, 0xfd083999), TOBN(0x6c94bdde, 0x8e796802),
+     TOBN(0x5a304ada, 0x8545d185), TOBN(0x82ae44ea, 0x738bb8cb),
+     TOBN(0x628a35e3, 0xdf87e10e), TOBN(0xd3624f3d, 0xa15b9fe3),
+     TOBN(0xcc44209b, 0x14be4254), TOBN(0x7d0efcbc, 0xbdbc2ea5),
+     TOBN(0x1f603362, 0x04c37bbe), TOBN(0x21f363f5, 0x56a5852c),
+     TOBN(0xa1503d1c, 0xa8501550), TOBN(0x2251e0e1, 0xd8ab10bb),
+     TOBN(0xde129c96, 0x6961c51c), TOBN(0x1f7246a4, 0x81910f68),
+     TOBN(0x2eb744ee, 0x5f2591f2), TOBN(0x3c47d33f, 0x5e627157),
+     TOBN(0x4d6d62c9, 0x22f3bd68), TOBN(0x6120a64b, 0xcb8df856),
+     TOBN(0x3a9ac6c0, 0x7b5d07df), TOBN(0xa92b9558, 0x7ef39783),
+     TOBN(0xe128a134, 0xab3a9b4f), TOBN(0x41c18807, 0xb1252f05),
+     TOBN(0xfc7ed089, 0x80ba9b1c), TOBN(0xac8dc6de, 0xc532a9dd),
+     TOBN(0xbf829cef, 0x55246809), TOBN(0x101b784f, 0x5b4ee80f),
+     TOBN(0xc09945bb, 0xb6f11603), TOBN(0x57b09dbe, 0x41d2801e),
+     TOBN(0xfba5202f, 0xa97534a8), TOBN(0x7fd8ae5f, 0xc17b9614),
+     TOBN(0xa50ba666, 0x78308435), TOBN(0x9572f77c, 0xd3868c4d),
+     TOBN(0x0cef7bfd, 0x2dd7aab0), TOBN(0xe7958e08, 0x2c7c79ff),
+     TOBN(0x81262e42, 0x25346689), TOBN(0x716da290, 0xb07c7004),
+     TOBN(0x35f911ea, 0xb7950ee3), TOBN(0x6fd72969, 0x261d21b5),
+     TOBN(0x52389803, 0x08b640d3), TOBN(0x5b0026ee, 0x887f12a1),
+     TOBN(0x20e21660, 0x742e9311), TOBN(0x0ef6d541, 0x5ff77ff7),
+     TOBN(0x969127f0, 0xf9c41135), TOBN(0xf21d60c9, 0x68a64993),
+     TOBN(0x656e5d0c, 0xe541875c), TOBN(0xf1e0f84e, 0xa1d3c233),
+     TOBN(0x9bcca359, 0x06002d60), TOBN(0xbe2da60c, 0x06191552),
+     TOBN(0x5da8bbae, 0x61181ec3), TOBN(0x9f04b823, 0x65806f19),
+     TOBN(0xf1604a7d, 0xd4b79bb8), TOBN(0xaee806fb, 0x52c878c8),
+     TOBN(0x34144f11, 0x8d47b8e8), TOBN(0x72edf52b, 0x949f9054),
+     TOBN(0xebfca84e, 0x2127015a), TOBN(0x9051d0c0, 0x9cb7cef3),
+     TOBN(0x86e8fe58, 0x296deec8), TOBN(0x33b28188, 0x41010d74),}
+    ,
+    {TOBN(0x01079383, 0x171b445f), TOBN(0x9bcf21e3, 0x8131ad4c),
+     TOBN(0x8cdfe205, 0xc93987e8), TOBN(0xe63f4152, 0xc92e8c8f),
+     TOBN(0x729462a9, 0x30add43d), TOBN(0x62ebb143, 0xc980f05a),
+     TOBN(0x4f3954e5, 0x3b06e968), TOBN(0xfe1d75ad, 0x242cf6b1),
+     TOBN(0x5f95c6c7, 0xaf8685c8), TOBN(0xd4c1c8ce, 0x2f8f01aa),
+     TOBN(0xc44bbe32, 0x2574692a), TOBN(0xb8003478, 0xd4a4a068),
+     TOBN(0x7c8fc6e5, 0x2eca3cdb), TOBN(0xea1db16b, 0xec04d399),
+     TOBN(0xb05bc82e, 0x8f2bc5cf), TOBN(0x763d517f, 0xf44793d2),
+     TOBN(0x4451c1b8, 0x08bd98d0), TOBN(0x644b1cd4, 0x6575f240),
+     TOBN(0x6907eb33, 0x7375d270), TOBN(0x56c8bebd, 0xfa2286bd),
+     TOBN(0xc713d2ac, 0xc4632b46), TOBN(0x17da427a, 0xafd60242),
+     TOBN(0x313065b7, 0xc95c7546), TOBN(0xf8239898, 0xbf17a3de),
+     TOBN(0xf3b7963f, 0x4c830320), TOBN(0x842c7aa0, 0x903203e3),
+     TOBN(0xaf22ca0a, 0xe7327afb), TOBN(0x38e13092, 0x967609b6),
+     TOBN(0x73b8fb62, 0x757558f1), TOBN(0x3cc3e831, 0xf7eca8c1),
+     TOBN(0xe4174474, 0xf6331627), TOBN(0xa77989ca, 0xc3c40234),
+     TOBN(0xe5fd17a1, 0x44a081e0), TOBN(0xd797fb7d, 0xb70e296a),
+     TOBN(0x2b472b30, 0x481f719c), TOBN(0x0e632a98, 0xfe6f8c52),
+     TOBN(0x89ccd116, 0xc5f0c284), TOBN(0xf51088af, 0x2d987c62),
+     TOBN(0x2a2bccda, 0x4c2de6cf), TOBN(0x810f9efe, 0xf679f0f9),
+     TOBN(0xb0f394b9, 0x7ffe4b3e), TOBN(0x0b691d21, 0xe5fa5d21),
+     TOBN(0xb0bd7747, 0x9dfbbc75), TOBN(0xd2830fda, 0xfaf78b00),
+     TOBN(0xf78c249c, 0x52434f57), TOBN(0x4b1f7545, 0x98096dab),
+     TOBN(0x73bf6f94, 0x8ff8c0b3), TOBN(0x34aef03d, 0x454e134c),
+     TOBN(0xf8d151f4, 0xb7ac7ec5), TOBN(0xd6ceb95a, 0xe50da7d5),
+     TOBN(0xa1b492b0, 0xdc3a0eb8), TOBN(0x75157b69, 0xb3dd2863),
+     TOBN(0xe2c4c74e, 0xc5413d62), TOBN(0xbe329ff7, 0xbc5fc4c7),
+     TOBN(0x835a2aea, 0x60fa9dda), TOBN(0xf117f5ad, 0x7445cb87),
+     TOBN(0xae8317f4, 0xb0166f7a), TOBN(0xfbd3e3f7, 0xceec74e6),
+     TOBN(0xfdb516ac, 0xe0874bfd), TOBN(0x3d846019, 0xc681f3a3),
+     TOBN(0x0b12ee5c, 0x7c1620b0), TOBN(0xba68b4dd, 0x2b63c501),
+     TOBN(0xac03cd32, 0x6668c51e), TOBN(0x2a6279f7, 0x4e0bcb5b),
+     TOBN(0x17bd69b0, 0x6ae85c10), TOBN(0x72946979, 0x1dfdd3a6),
+     TOBN(0xd9a03268, 0x2c078bec), TOBN(0x41c6a658, 0xbfd68a52),
+     TOBN(0xcdea1024, 0x0e023900), TOBN(0xbaeec121, 0xb10d144d),
+     TOBN(0x5a600e74, 0x058ab8dc), TOBN(0x1333af21, 0xbb89ccdd),
+     TOBN(0xdf25eae0, 0x3aaba1f1), TOBN(0x2cada16e, 0x3b7144cf),
+     TOBN(0x657ee27d, 0x71ab98bc), TOBN(0x99088b4c, 0x7a6fc96e),
+     TOBN(0x05d5c0a0, 0x3549dbd4), TOBN(0x42cbdf8f, 0xf158c3ac),
+     TOBN(0x3fb6b3b0, 0x87edd685), TOBN(0x22071cf6, 0x86f064d0),
+     TOBN(0xd2d6721f, 0xff2811e5), TOBN(0xdb81b703, 0xfe7fae8c),
+     TOBN(0x3cfb74ef, 0xd3f1f7bb), TOBN(0x0cdbcd76, 0x16cdeb5d),
+     TOBN(0x4f39642a, 0x566a808c), TOBN(0x02b74454, 0x340064d6),
+     TOBN(0xfabbadca, 0x0528fa6f), TOBN(0xe4c3074c, 0xd3fc0bb6),
+     TOBN(0xb32cb8b0, 0xb796d219), TOBN(0xc3e95f4f, 0x34741dd9),
+     TOBN(0x87212125, 0x68edf6f5), TOBN(0x7a03aee4, 0xa2b9cb8e),
+     TOBN(0x0cd3c376, 0xf53a89aa), TOBN(0x0d8af9b1, 0x948a28dc),
+     TOBN(0xcf86a3f4, 0x902ab04f), TOBN(0x8aacb62a, 0x7f42002d),
+     TOBN(0x106985eb, 0xf62ffd52), TOBN(0xe670b54e, 0x5797bf10),
+     TOBN(0x4b405209, 0xc5e30aef), TOBN(0x12c97a20, 0x4365b5e9),
+     TOBN(0x104646ce, 0x1fe32093), TOBN(0x13cb4ff6, 0x3907a8c9),
+     TOBN(0x8b9f30d1, 0xd46e726b), TOBN(0xe1985e21, 0xaba0f499),
+     TOBN(0xc573dea9, 0x10a230cd), TOBN(0x24f46a93, 0xcd30f947),
+     TOBN(0xf2623fcf, 0xabe2010a), TOBN(0x3f278cb2, 0x73f00e4f),
+     TOBN(0xed55c67d, 0x50b920eb), TOBN(0xf1cb9a2d, 0x8e760571),
+     TOBN(0x7c50d109, 0x0895b709), TOBN(0x4207cf07, 0x190d4369),
+     TOBN(0x3b027e81, 0xc4127fe1), TOBN(0xa9f8b9ad, 0x3ae9c566),
+     TOBN(0x5ab10851, 0xacbfbba5), TOBN(0xa747d648, 0x569556f5),
+     TOBN(0xcc172b5c, 0x2ba97bf7), TOBN(0x15e0f77d, 0xbcfa3324),
+     TOBN(0xa345b797, 0x7686279d), TOBN(0x5a723480, 0xe38003d3),
+     TOBN(0xfd8e139f, 0x8f5fcda8), TOBN(0xf3e558c4, 0xbdee5bfd),
+     TOBN(0xd76cbaf4, 0xe33f9f77), TOBN(0x3a4c97a4, 0x71771969),
+     TOBN(0xda27e84b, 0xf6dce6a7), TOBN(0xff373d96, 0x13e6c2d1),
+     TOBN(0xf115193c, 0xd759a6e9), TOBN(0x3f9b7025, 0x63d2262c),
+     TOBN(0xd9764a31, 0x317cd062), TOBN(0x30779d8e, 0x199f8332),
+     TOBN(0xd8074106, 0x16b11b0b), TOBN(0x7917ab9f, 0x78aeaed8),
+     TOBN(0xb67a9cbe, 0x28fb1d8e), TOBN(0x2e313563, 0x136eda33),
+     TOBN(0x010b7069, 0xa371a86c), TOBN(0x44d90fa2, 0x6744e6b7),
+     TOBN(0x68190867, 0xd6b3e243), TOBN(0x9fe6cd9d, 0x59048c48),
+     TOBN(0xb900b028, 0x95731538), TOBN(0xa012062f, 0x32cae04f),
+     TOBN(0x8107c8bc, 0x9399d082), TOBN(0x47e8c54a, 0x41df12e2),
+     TOBN(0x14ba5117, 0xb6ef3f73), TOBN(0x22260bea, 0x81362f0b),
+     TOBN(0x90ea261e, 0x1a18cc20), TOBN(0x2192999f, 0x2321d636),
+     TOBN(0xef64d314, 0xe311b6a0), TOBN(0xd7401e4c, 0x3b54a1f5),
+     TOBN(0x19019983, 0x6fbca2ba), TOBN(0x46ad3293, 0x8fbffc4b),
+     TOBN(0xa142d3f6, 0x3786bf40), TOBN(0xeb5cbc26, 0xb67039fc),
+     TOBN(0x9cb0ae6c, 0x252bd479), TOBN(0x05e0f88a, 0x12b5848f),
+     TOBN(0x78f6d2b2, 0xa5c97663), TOBN(0x6f6e149b, 0xc162225c),
+     TOBN(0xe602235c, 0xde601a89), TOBN(0xd17bbe98, 0xf373be1f),
+     TOBN(0xcaf49a5b, 0xa8471827), TOBN(0x7e1a0a85, 0x18aaa116),
+     TOBN(0x6c833196, 0x270580c3), TOBN(0x1e233839, 0xf1c98a14),
+     TOBN(0x67b2f7b4, 0xae34e0a5), TOBN(0x47ac8745, 0xd8ce7289),
+     TOBN(0x2b74779a, 0x100dd467), TOBN(0x274a4337, 0x4ee50d09),
+     TOBN(0x603dcf13, 0x83608bc9), TOBN(0xcd9da6c3, 0xc89e8388),
+     TOBN(0x2660199f, 0x355116ac), TOBN(0xcc38bb59, 0xb6d18eed),
+     TOBN(0x3075f31f, 0x2f4bc071), TOBN(0x9774457f, 0x265dc57e),
+     TOBN(0x06a6a9c8, 0xc6db88bb), TOBN(0x6429d07f, 0x4ec98e04),
+     TOBN(0x8d05e57b, 0x05ecaa8b), TOBN(0x20f140b1, 0x7872ea7b),
+     TOBN(0xdf8c0f09, 0xca494693), TOBN(0x48d3a020, 0xf252e909),
+     TOBN(0x4c5c29af, 0x57b14b12), TOBN(0x7e6fa37d, 0xbf47ad1c),
+     TOBN(0x66e7b506, 0x49a0c938), TOBN(0xb72c0d48, 0x6be5f41f),
+     TOBN(0x6a6242b8, 0xb2359412), TOBN(0xcd35c774, 0x8e859480),
+     TOBN(0x12536fea, 0x87baa627), TOBN(0x58c1fec1, 0xf72aa680),
+     TOBN(0x6c29b637, 0x601e5dc9), TOBN(0x9e3c3c1c, 0xde9e01b9),
+     TOBN(0xefc8127b, 0x2bcfe0b0), TOBN(0x35107102, 0x2a12f50d),
+     TOBN(0x6ccd6cb1, 0x4879b397), TOBN(0xf792f804, 0xf8a82f21),
+     TOBN(0x509d4804, 0xa9b46402), TOBN(0xedddf85d, 0xc10f0850),
+     TOBN(0x928410dc, 0x4b6208aa), TOBN(0xf6229c46, 0x391012dc),
+     TOBN(0xc5a7c41e, 0x7727b9b6), TOBN(0x289e4e4b, 0xaa444842),
+     TOBN(0x049ba1d9, 0xe9a947ea), TOBN(0x44f9e47f, 0x83c8debc),
+     TOBN(0xfa77a1fe, 0x611f8b8e), TOBN(0xfd2e416a, 0xf518f427),
+     TOBN(0xc5fffa70, 0x114ebac3), TOBN(0xfe57c4e9, 0x5d89697b),
+     TOBN(0xfdd053ac, 0xb1aaf613), TOBN(0x31df210f, 0xea585a45),
+     TOBN(0x318cc10e, 0x24985034), TOBN(0x1a38efd1, 0x5f1d6130),
+     TOBN(0xbf86f237, 0x0b1e9e21), TOBN(0xb258514d, 0x1dbe88aa),
+     TOBN(0x1e38a588, 0x90c1baf9), TOBN(0x2936a01e, 0xbdb9b692),
+     TOBN(0xd576de98, 0x6dd5b20c), TOBN(0xb586bf71, 0x70f98ecf),
+     TOBN(0xcccf0f12, 0xc42d2fd7), TOBN(0x8717e61c, 0xfb35bd7b),
+     TOBN(0x8b1e5722, 0x35e6fc06), TOBN(0x3477728f, 0x0b3e13d5),
+     TOBN(0x150c294d, 0xaa8a7372), TOBN(0xc0291d43, 0x3bfa528a),
+     TOBN(0xc6c8bc67, 0xcec5a196), TOBN(0xdeeb31e4, 0x5c2e8a7c),
+     TOBN(0xba93e244, 0xfb6e1c51), TOBN(0xb9f8b71b, 0x2e28e156),
+     TOBN(0xce65a287, 0x968a2ab9), TOBN(0xe3c5ce69, 0x46bbcb1f),
+     TOBN(0xf8c835b9, 0xe7ae3f30), TOBN(0x16bbee26, 0xff72b82b),
+     TOBN(0x665e2017, 0xfd42cd22), TOBN(0x1e139970, 0xf8b1d2a0),
+     TOBN(0x125cda29, 0x79204932), TOBN(0x7aee94a5, 0x49c3bee5),
+     TOBN(0x68c70160, 0x89821a66), TOBN(0xf7c37678, 0x8f981669),
+     TOBN(0xd90829fc, 0x48cc3645), TOBN(0x346af049, 0xd70addfc),
+     TOBN(0x2057b232, 0x370bf29c), TOBN(0xf90c73ce, 0x42e650ee),
+     TOBN(0xe03386ea, 0xa126ab90), TOBN(0x0e266e7e, 0x975a087b),
+     TOBN(0x80578eb9, 0x0fca65d9), TOBN(0x7e2989ea, 0x16af45b8),
+     TOBN(0x7438212d, 0xcac75a4e), TOBN(0x38c7ca39, 0x4fef36b8),
+     TOBN(0x8650c494, 0xd402676a), TOBN(0x26ab5a66, 0xf72c7c48),
+     TOBN(0x4e6cb426, 0xce3a464e), TOBN(0xf8f99896, 0x2b72f841),
+     TOBN(0x8c318491, 0x1a335cc8), TOBN(0x563459ba, 0x6a5913e4),
+     TOBN(0x1b920d61, 0xc7b32919), TOBN(0x805ab8b6, 0xa02425ad),
+     TOBN(0x2ac512da, 0x8d006086), TOBN(0x6ca4846a, 0xbcf5c0fd),
+     TOBN(0xafea51d8, 0xac2138d7), TOBN(0xcb647545, 0x344cd443),
+     TOBN(0x0429ee8f, 0xbd7d9040), TOBN(0xee66a2de, 0x819b9c96),
+     TOBN(0x54f9ec25, 0xdea7d744), TOBN(0x2ffea642, 0x671721bb),
+     TOBN(0x4f19dbd1, 0x114344ea), TOBN(0x04304536, 0xfd0dbc8b),
+     TOBN(0x014b50aa, 0x29ec7f91), TOBN(0xb5fc22fe, 0xbb06014d),
+     TOBN(0x60d963a9, 0x1ee682e0), TOBN(0xdf48abc0, 0xfe85c727),
+     TOBN(0x0cadba13, 0x2e707c2d), TOBN(0xde608d3a, 0xa645aeff),
+     TOBN(0x05f1c28b, 0xedafd883), TOBN(0x3c362ede, 0xbd94de1f),
+     TOBN(0x8dd0629d, 0x13593e41), TOBN(0x0a5e736f, 0x766d6eaf),
+     TOBN(0xbfa92311, 0xf68cf9d1), TOBN(0xa4f9ef87, 0xc1797556),
+     TOBN(0x10d75a1f, 0x5601c209), TOBN(0x651c374c, 0x09b07361),
+     TOBN(0x49950b58, 0x88b5cead), TOBN(0x0ef00058, 0x6fa9dbaa),
+     TOBN(0xf51ddc26, 0x4e15f33a), TOBN(0x1f8b5ca6, 0x2ef46140),
+     TOBN(0x343ac0a3, 0xee9523f0), TOBN(0xbb75eab2, 0x975ea978),
+     TOBN(0x1bccf332, 0x107387f4), TOBN(0x790f9259, 0x9ab0062e),
+     TOBN(0xf1a363ad, 0x1e4f6a5f), TOBN(0x06e08b84, 0x62519a50),
+     TOBN(0x60915187, 0x7265f1ee), TOBN(0x6a80ca34, 0x93ae985e),
+     TOBN(0x81b29768, 0xaaba4864), TOBN(0xb13cabf2, 0x8d52a7d6),
+     TOBN(0xb5c36348, 0x8ead03f1), TOBN(0xc932ad95, 0x81c7c1c0),
+     TOBN(0x5452708e, 0xcae1e27b), TOBN(0x9dac4269, 0x1b0df648),
+     TOBN(0x233e3f0c, 0xdfcdb8bc), TOBN(0xe6ceccdf, 0xec540174),
+     TOBN(0xbd0d845e, 0x95081181), TOBN(0xcc8a7920, 0x699355d5),
+     TOBN(0x111c0f6d, 0xc3b375a8), TOBN(0xfd95bc6b, 0xfd51e0dc),
+     TOBN(0x4a106a26, 0x6888523a), TOBN(0x4d142bd6, 0xcb01a06d),
+     TOBN(0x79bfd289, 0xadb9b397), TOBN(0x0bdbfb94, 0xe9863914),
+     TOBN(0x29d8a229, 0x1660f6a6), TOBN(0x7f6abcd6, 0x551c042d),
+     TOBN(0x13039deb, 0x0ac3ffe8), TOBN(0xa01be628, 0xec8523fb),
+     TOBN(0x6ea34103, 0x0ca1c328), TOBN(0xc74114bd, 0xb903928e),
+     TOBN(0x8aa4ff4e, 0x9e9144b0), TOBN(0x7064091f, 0x7f9a4b17),
+     TOBN(0xa3f4f521, 0xe447f2c4), TOBN(0x81b8da7a, 0x604291f0),
+     TOBN(0xd680bc46, 0x7d5926de), TOBN(0x84f21fd5, 0x34a1202f),
+     TOBN(0x1d1e3181, 0x4e9df3d8), TOBN(0x1ca4861a, 0x39ab8d34),
+     TOBN(0x809ddeec, 0x5b19aa4a), TOBN(0x59f72f7e, 0x4d329366),
+     TOBN(0xa2f93f41, 0x386d5087), TOBN(0x40bf739c, 0xdd67d64f),
+     TOBN(0xb4494205, 0x66702158), TOBN(0xc33c65be, 0x73b1e178),
+     TOBN(0xcdcd657c, 0x38ca6153), TOBN(0x97f4519a, 0xdc791976),
+     TOBN(0xcc7c7f29, 0xcd6e1f39), TOBN(0x38de9cfb, 0x7e3c3932),
+     TOBN(0xe448eba3, 0x7b793f85), TOBN(0xe9f8dbf9, 0xf067e914),
+     TOBN(0xc0390266, 0xf114ae87), TOBN(0x39ed75a7, 0xcd6a8e2a),
+     TOBN(0xadb14848, 0x7ffba390), TOBN(0x67f8cb8b, 0x6af9bc09),
+     TOBN(0x322c3848, 0x9c7476db), TOBN(0xa320fecf, 0x52a538d6),
+     TOBN(0xe0493002, 0xb2aced2b), TOBN(0xdfba1809, 0x616bd430),
+     TOBN(0x531c4644, 0xc331be70), TOBN(0xbc04d32e, 0x90d2e450),
+     TOBN(0x1805a0d1, 0x0f9f142d), TOBN(0x2c44a0c5, 0x47ee5a23),
+     TOBN(0x31875a43, 0x3989b4e3), TOBN(0x6b1949fd, 0x0c063481),
+     TOBN(0x2dfb9e08, 0xbe0f4492), TOBN(0x3ff0da03, 0xe9d5e517),
+     TOBN(0x03dbe9a1, 0xf79466a8), TOBN(0x0b87bcd0, 0x15ea9932),
+     TOBN(0xeb64fc83, 0xab1f58ab), TOBN(0x6d9598da, 0x817edc8a),
+     TOBN(0x699cff66, 0x1d3b67e5), TOBN(0x645c0f29, 0x92635853),
+     TOBN(0x253cdd82, 0xeabaf21c), TOBN(0x82b9602a, 0x2241659e),
+     TOBN(0x2cae07ec, 0x2d9f7091), TOBN(0xbe4c720c, 0x8b48cd9b),
+     TOBN(0x6ce5bc03, 0x6f08d6c9), TOBN(0x36e8a997, 0xaf10bf40),
+     TOBN(0x83422d21, 0x3e10ff12), TOBN(0x7b26d3eb, 0xbcc12494),
+     TOBN(0xb240d2d0, 0xc9469ad6), TOBN(0xc4a11b4d, 0x30afa05b),
+     TOBN(0x4b604ace, 0xdd6ba286), TOBN(0x18486600, 0x3ee2864c),
+     TOBN(0x5869d6ba, 0x8d9ce5be), TOBN(0x0d8f68c5, 0xff4bfb0d),
+     TOBN(0xb69f210b, 0x5700cf73), TOBN(0x61f6653a, 0x6d37c135),
+     TOBN(0xff3d432b, 0x5aff5a48), TOBN(0x0d81c4b9, 0x72ba3a69),
+     TOBN(0xee879ae9, 0xfa1899ef), TOBN(0xbac7e2a0, 0x2d6acafd),
+     TOBN(0xd6d93f6c, 0x1c664399), TOBN(0x4c288de1, 0x5bcb135d),
+     TOBN(0x83031dab, 0x9dab7cbf), TOBN(0xfe23feb0, 0x3abbf5f0),
+     TOBN(0x9f1b2466, 0xcdedca85), TOBN(0x140bb710, 0x1a09538c),
+     TOBN(0xac8ae851, 0x5e11115d), TOBN(0x0d63ff67, 0x6f03f59e),
+     TOBN(0x755e5551, 0x7d234afb), TOBN(0x61c2db4e, 0x7e208fc1),
+     TOBN(0xaa9859ce, 0xf28a4b5d), TOBN(0xbdd6d4fc, 0x34af030f),
+     TOBN(0xd1c4a26d, 0x3be01cb1), TOBN(0x9ba14ffc, 0x243aa07c),
+     TOBN(0xf95cd3a9, 0xb2503502), TOBN(0xe379bc06, 0x7d2a93ab),
+     TOBN(0x3efc18e9, 0xd4ca8d68), TOBN(0x083558ec, 0x80bb412a),
+     TOBN(0xd903b940, 0x9645a968), TOBN(0xa499f0b6, 0x9ba6054f),
+     TOBN(0x208b573c, 0xb8349abe), TOBN(0x3baab3e5, 0x30b4fc1c),
+     TOBN(0x87e978ba, 0xcb524990), TOBN(0x3524194e, 0xccdf0e80),
+     TOBN(0x62711725, 0x7d4bcc42), TOBN(0xe90a3d9b, 0xb90109ba),
+     TOBN(0x3b1bdd57, 0x1323e1e0), TOBN(0xb78e9bd5, 0x5eae1599),
+     TOBN(0x0794b746, 0x9e03d278), TOBN(0x80178605, 0xd70e6297),
+     TOBN(0x171792f8, 0x99c97855), TOBN(0x11b393ee, 0xf5a86b5c),
+     TOBN(0x48ef6582, 0xd8884f27), TOBN(0xbd44737a, 0xbf19ba5f),
+     TOBN(0x8698de4c, 0xa42062c6), TOBN(0x8975eb80, 0x61ce9c54),
+     TOBN(0xd50e57c7, 0xd7fe71f3), TOBN(0x15342190, 0xbc97ce38),
+     TOBN(0x51bda2de, 0x4df07b63), TOBN(0xba12aeae, 0x200eb87d),
+     TOBN(0xabe135d2, 0xa9b4f8f6), TOBN(0x04619d65, 0xfad6d99c),
+     TOBN(0x4a6683a7, 0x7994937c), TOBN(0x7a778c8b, 0x6f94f09a),
+     TOBN(0x8c508623, 0x20a71b89), TOBN(0x241a2aed, 0x1c229165),
+     TOBN(0x352be595, 0xaaf83a99), TOBN(0x9fbfee7f, 0x1562bac8),
+     TOBN(0xeaf658b9, 0x5c4017e3), TOBN(0x1dc7f9e0, 0x15120b86),
+     TOBN(0xd84f13dd, 0x4c034d6f), TOBN(0x283dd737, 0xeaea3038),
+     TOBN(0x197f2609, 0xcd85d6a2), TOBN(0x6ebbc345, 0xfae60177),
+     TOBN(0xb80f031b, 0x4e12fede), TOBN(0xde55d0c2, 0x07a2186b),
+     TOBN(0x1fb3e37f, 0x24dcdd5a), TOBN(0x8d602da5, 0x7ed191fb),
+     TOBN(0x108fb056, 0x76023e0d), TOBN(0x70178c71, 0x459c20c0),
+     TOBN(0xfad5a386, 0x3fe54cf0), TOBN(0xa4a3ec4f, 0x02bbb475),
+     TOBN(0x1aa5ec20, 0x919d94d7), TOBN(0x5d3b63b5, 0xa81e4ab3),
+     TOBN(0x7fa733d8, 0x5ad3d2af), TOBN(0xfbc586dd, 0xd1ac7a37),
+     TOBN(0x282925de, 0x40779614), TOBN(0xfe0ffffb, 0xe74a242a),
+     TOBN(0x3f39e67f, 0x906151e5), TOBN(0xcea27f5f, 0x55e10649),
+     TOBN(0xdca1d4e1, 0xc17cf7b7), TOBN(0x0c326d12, 0x2fe2362d),
+     TOBN(0x05f7ac33, 0x7dd35df3), TOBN(0x0c3b7639, 0xc396dbdf),
+     TOBN(0x0912f5ac, 0x03b7db1c), TOBN(0x9dea4b70, 0x5c9ed4a9),
+     TOBN(0x475e6e53, 0xaae3f639), TOBN(0xfaba0e7c, 0xfc278bac),
+     TOBN(0x16f9e221, 0x9490375f), TOBN(0xaebf9746, 0xa5a7ed0a),
+     TOBN(0x45f9af3f, 0xf41ad5d6), TOBN(0x03c4623c, 0xb2e99224),
+     TOBN(0x82c5bb5c, 0xb3cf56aa), TOBN(0x64311819, 0x34567ed3),
+     TOBN(0xec57f211, 0x8be489ac), TOBN(0x2821895d, 0xb9a1104b),
+     TOBN(0x610dc875, 0x6064e007), TOBN(0x8e526f3f, 0x5b20d0fe),
+     TOBN(0x6e71ca77, 0x5b645aee), TOBN(0x3d1dcb9f, 0x800e10ff),
+     TOBN(0x36b51162, 0x189cf6de), TOBN(0x2c5a3e30, 0x6bb17353),
+     TOBN(0xc186cd3e, 0x2a6c6fbf), TOBN(0xa74516fa, 0x4bf97906),
+     TOBN(0x5b4b8f4b, 0x279d6901), TOBN(0x0c4e57b4, 0x2b573743),
+     TOBN(0x75fdb229, 0xb6e386b6), TOBN(0xb46793fd, 0x99deac27),
+     TOBN(0xeeec47ea, 0xcf712629), TOBN(0xe965f3c4, 0xcbc3b2dd),
+     TOBN(0x8dd1fb83, 0x425c6559), TOBN(0x7fc00ee6, 0x0af06fda),
+     TOBN(0xe98c9225, 0x33d956df), TOBN(0x0f1ef335, 0x4fbdc8a2),
+     TOBN(0x2abb5145, 0xb79b8ea2), TOBN(0x40fd2945, 0xbdbff288),
+     TOBN(0x6a814ac4, 0xd7185db7), TOBN(0xc4329d6f, 0xc084609a),
+     TOBN(0xc9ba7b52, 0xed1be45d), TOBN(0x891dd20d, 0xe4cd2c74),
+     TOBN(0x5a4d4a7f, 0x824139b1), TOBN(0x66c17716, 0xb873c710),
+     TOBN(0x5e5bc141, 0x2843c4e0), TOBN(0xd5ac4817, 0xb97eb5bf),
+     TOBN(0xc0f8af54, 0x450c95c7), TOBN(0xc91b3fa0, 0x318406c5),
+     TOBN(0x360c340a, 0xab9d97f8), TOBN(0xfb57bd07, 0x90a2d611),
+     TOBN(0x4339ae3c, 0xa6a6f7e5), TOBN(0x9c1fcd2a, 0x2feb8a10),
+     TOBN(0x972bcca9, 0xc7ea7432), TOBN(0x1b0b924c, 0x308076f6),
+     TOBN(0x80b2814a, 0x2a5b4ca5), TOBN(0x2f78f55b, 0x61ef3b29),
+     TOBN(0xf838744a, 0xc18a414f), TOBN(0xc611eaae, 0x903d0a86),
+     TOBN(0x94dabc16, 0x2a453f55), TOBN(0xe6f2e3da, 0x14efb279),
+     TOBN(0x5b7a6017, 0x9320dc3c), TOBN(0x692e382f, 0x8df6b5a4),
+     TOBN(0x3f5e15e0, 0x2d40fa90), TOBN(0xc87883ae, 0x643dd318),
+     TOBN(0x511053e4, 0x53544774), TOBN(0x834d0ecc, 0x3adba2bc),
+     TOBN(0x4215d7f7, 0xbae371f5), TOBN(0xfcfd57bf, 0x6c8663bc),
+     TOBN(0xded2383d, 0xd6901b1d), TOBN(0x3b49fbb4, 0xb5587dc3),
+     TOBN(0xfd44a08d, 0x07625f62), TOBN(0x3ee4d65b, 0x9de9b762),}
+    ,
+    {TOBN(0x64e5137d, 0x0d63d1fa), TOBN(0x658fc052, 0x02a9d89f),
+     TOBN(0x48894874, 0x50436309), TOBN(0xe9ae30f8, 0xd598da61),
+     TOBN(0x2ed710d1, 0x818baf91), TOBN(0xe27e9e06, 0x8b6a0c20),
+     TOBN(0x1e28dcfb, 0x1c1a6b44), TOBN(0x883acb64, 0xd6ac57dc),
+     TOBN(0x8735728d, 0xc2c6ff70), TOBN(0x79d6122f, 0xc5dc2235),
+     TOBN(0x23f5d003, 0x19e277f9), TOBN(0x7ee84e25, 0xdded8cc7),
+     TOBN(0x91a8afb0, 0x63cd880a), TOBN(0x3f3ea7c6, 0x3574af60),
+     TOBN(0x0cfcdc84, 0x02de7f42), TOBN(0x62d0792f, 0xb31aa152),
+     TOBN(0x8e1b4e43, 0x8a5807ce), TOBN(0xad283893, 0xe4109a7e),
+     TOBN(0xc30cc9cb, 0xafd59dda), TOBN(0xf65f36c6, 0x3d8d8093),
+     TOBN(0xdf31469e, 0xa60d32b2), TOBN(0xee93df4b, 0x3e8191c8),
+     TOBN(0x9c1017c5, 0x355bdeb5), TOBN(0xd2623185, 0x8616aa28),
+     TOBN(0xb02c83f9, 0xdec31a21), TOBN(0x988c8b23, 0x6ad9d573),
+     TOBN(0x53e983ae, 0xa57be365), TOBN(0xe968734d, 0x646f834e),
+     TOBN(0x9137ea8f, 0x5da6309b), TOBN(0x10f3a624, 0xc1f1ce16),
+     TOBN(0x782a9ea2, 0xca440921), TOBN(0xdf94739e, 0x5b46f1b5),
+     TOBN(0x9f9be006, 0xcce85c9b), TOBN(0x360e70d6, 0xa4c7c2d3),
+     TOBN(0x2cd5beea, 0xaefa1e60), TOBN(0x64cf63c0, 0x8c3d2b6d),
+     TOBN(0xfb107fa3, 0xe1cf6f90), TOBN(0xb7e937c6, 0xd5e044e6),
+     TOBN(0x74e8ca78, 0xce34db9f), TOBN(0x4f8b36c1, 0x3e210bd0),
+     TOBN(0x1df165a4, 0x34a35ea8), TOBN(0x3418e0f7, 0x4d4412f6),
+     TOBN(0x5af1f8af, 0x518836c3), TOBN(0x42ceef4d, 0x130e1965),
+     TOBN(0x5560ca0b, 0x543a1957), TOBN(0xc33761e5, 0x886cb123),
+     TOBN(0x66624b1f, 0xfe98ed30), TOBN(0xf772f4bf, 0x1090997d),
+     TOBN(0xf4e540bb, 0x4885d410), TOBN(0x7287f810, 0x9ba5f8d7),
+     TOBN(0x22d0d865, 0xde98dfb1), TOBN(0x49ff51a1, 0xbcfbb8a3),
+     TOBN(0xb6b6fa53, 0x6bc3012e), TOBN(0x3d31fd72, 0x170d541d),
+     TOBN(0x8018724f, 0x4b0f4966), TOBN(0x79e7399f, 0x87dbde07),
+     TOBN(0x56f8410e, 0xf4f8b16a), TOBN(0x97241afe, 0xc47b266a),
+     TOBN(0x0a406b8e, 0x6d9c87c1), TOBN(0x803f3e02, 0xcd42ab1b),
+     TOBN(0x7f0309a8, 0x04dbec69), TOBN(0xa83b85f7, 0x3bbad05f),
+     TOBN(0xc6097273, 0xad8e197f), TOBN(0xc097440e, 0x5067adc1),
+     TOBN(0x730eafb6, 0x3524ff16), TOBN(0xd7f9b51e, 0x823fc6ce),
+     TOBN(0x27bd0d32, 0x443e4ac0), TOBN(0x40c59ad9, 0x4d66f217),
+     TOBN(0x6c33136f, 0x17c387a4), TOBN(0x5043b8d5, 0xeb86804d),
+     TOBN(0x74970312, 0x675a73c9), TOBN(0x838fdb31, 0xf16669b6),
+     TOBN(0xc507b6dd, 0x418e7ddd), TOBN(0x39888d93, 0x472f19d6),
+     TOBN(0x7eae26be, 0x0c27eb4d), TOBN(0x17b53ed3, 0xfbabb884),
+     TOBN(0xfc27021b, 0x2b01ae4f), TOBN(0x88462e87, 0xcf488682),
+     TOBN(0xbee096ec, 0x215e2d87), TOBN(0xeb2fea9a, 0xd242e29b),
+     TOBN(0x5d985b5f, 0xb821fc28), TOBN(0x89d2e197, 0xdc1e2ad2),
+     TOBN(0x55b566b8, 0x9030ba62), TOBN(0xe3fd41b5, 0x4f41b1c6),
+     TOBN(0xb738ac2e, 0xb9a96d61), TOBN(0x7f8567ca, 0x369443f4),
+     TOBN(0x8698622d, 0xf803a440), TOBN(0x2b586236, 0x8fe2f4dc),
+     TOBN(0xbbcc00c7, 0x56b95bce), TOBN(0x5ec03906, 0x616da680),
+     TOBN(0x79162ee6, 0x72214252), TOBN(0x43132b63, 0x86a892d2),
+     TOBN(0x4bdd3ff2, 0x2f3263bf), TOBN(0xd5b3733c, 0x9cd0a142),
+     TOBN(0x592eaa82, 0x44415ccb), TOBN(0x663e8924, 0x8d5474ea),
+     TOBN(0x8058a25e, 0x5236344e), TOBN(0x82e8df9d, 0xbda76ee6),
+     TOBN(0xdcf6efd8, 0x11cc3d22), TOBN(0x00089cda, 0x3b4ab529),
+     TOBN(0x91d3a071, 0xbd38a3db), TOBN(0x4ea97fc0, 0xef72b925),
+     TOBN(0x0c9fc15b, 0xea3edf75), TOBN(0x5a6297cd, 0xa4348ed3),
+     TOBN(0x0d38ab35, 0xce7c42d4), TOBN(0x9fd493ef, 0x82feab10),
+     TOBN(0x46056b6d, 0x82111b45), TOBN(0xda11dae1, 0x73efc5c3),
+     TOBN(0xdc740278, 0x5545a7fb), TOBN(0xbdb2601c, 0x40d507e6),
+     TOBN(0x121dfeeb, 0x7066fa58), TOBN(0x214369a8, 0x39ae8c2a),
+     TOBN(0x195709cb, 0x06e0956c), TOBN(0x4c9d254f, 0x010cd34b),
+     TOBN(0xf51e13f7, 0x0471a532), TOBN(0xe19d6791, 0x1e73054d),
+     TOBN(0xf702a628, 0xdb5c7be3), TOBN(0xc7141218, 0xb24dde05),
+     TOBN(0xdc18233c, 0xf29b2e2e), TOBN(0x3a6bd1e8, 0x85342dba),
+     TOBN(0x3f747fa0, 0xb311898c), TOBN(0xe2a272e4, 0xcd0eac65),
+     TOBN(0x4bba5851, 0xf914d0bc), TOBN(0x7a1a9660, 0xc4a43ee3),
+     TOBN(0xe5a367ce, 0xa1c8cde9), TOBN(0x9d958ba9, 0x7271abe3),
+     TOBN(0xf3ff7eb6, 0x3d1615cd), TOBN(0xa2280dce, 0xf5ae20b0),
+     TOBN(0x56dba5c1, 0xcf640147), TOBN(0xea5a2e3d, 0x5e83d118),
+     TOBN(0x04cd6b6d, 0xda24c511), TOBN(0x1c0f4671, 0xe854d214),
+     TOBN(0x91a6b7a9, 0x69565381), TOBN(0xdc966240, 0xdecf1f5b),
+     TOBN(0x1b22d21c, 0xfcf5d009), TOBN(0x2a05f641, 0x9021dbd5),
+     TOBN(0x8c0ed566, 0xd4312483), TOBN(0x5179a95d, 0x643e216f),
+     TOBN(0xcc185fec, 0x17044493), TOBN(0xb3063339, 0x54991a21),
+     TOBN(0xd801ecdb, 0x0081a726), TOBN(0x0149b0c6, 0x4fa89bbb),
+     TOBN(0xafe9065a, 0x4391b6b9), TOBN(0xedc92786, 0xd633f3a3),
+     TOBN(0xe408c24a, 0xae6a8e13), TOBN(0x85833fde, 0x9f3897ab),
+     TOBN(0x43800e7e, 0xd81a0715), TOBN(0xde08e346, 0xb44ffc5f),
+     TOBN(0x7094184c, 0xcdeff2e0), TOBN(0x49f9387b, 0x165eaed1),
+     TOBN(0x635d6129, 0x777c468a), TOBN(0x8c0dcfd1, 0x538c2dd8),
+     TOBN(0xd6d9d9e3, 0x7a6a308b), TOBN(0x62375830, 0x4c2767d3),
+     TOBN(0x874a8bc6, 0xf38cbeb6), TOBN(0xd94d3f1a, 0xccb6fd9e),
+     TOBN(0x92a9735b, 0xba21f248), TOBN(0x272ad0e5, 0x6cd1efb0),
+     TOBN(0x7437b69c, 0x05b03284), TOBN(0xe7f04702, 0x6948c225),
+     TOBN(0x8a56c04a, 0xcba2ecec), TOBN(0x0c181270, 0xe3a73e41),
+     TOBN(0x6cb34e9d, 0x03e93725), TOBN(0xf77c8713, 0x496521a9),
+     TOBN(0x94569183, 0xfa7f9f90), TOBN(0xf2e7aa4c, 0x8c9707ad),
+     TOBN(0xced2c9ba, 0x26c1c9a3), TOBN(0x9109fe96, 0x40197507),
+     TOBN(0x9ae868a9, 0xe9adfe1c), TOBN(0x3984403d, 0x314e39bb),
+     TOBN(0xb5875720, 0xf2fe378f), TOBN(0x33f901e0, 0xba44a628),
+     TOBN(0xea1125fe, 0x3652438c), TOBN(0xae9ec4e6, 0x9dd1f20b),
+     TOBN(0x1e740d9e, 0xbebf7fbd), TOBN(0x6dbd3ddc, 0x42dbe79c),
+     TOBN(0x62082aec, 0xedd36776), TOBN(0xf612c478, 0xe9859039),
+     TOBN(0xa493b201, 0x032f7065), TOBN(0xebd4d8f2, 0x4ff9b211),
+     TOBN(0x3f23a0aa, 0xaac4cb32), TOBN(0xea3aadb7, 0x15ed4005),
+     TOBN(0xacf17ea4, 0xafa27e63), TOBN(0x56125c1a, 0xc11fd66c),
+     TOBN(0x266344a4, 0x3794f8dc), TOBN(0xdcca923a, 0x483c5c36),
+     TOBN(0x2d6b6bbf, 0x3f9d10a0), TOBN(0xb320c5ca, 0x81d9bdf3),
+     TOBN(0x620e28ff, 0x47b50a95), TOBN(0x933e3b01, 0xcef03371),
+     TOBN(0xf081bf85, 0x99100153), TOBN(0x183be9a0, 0xc3a8c8d6),
+     TOBN(0x4e3ddc5a, 0xd6bbe24d), TOBN(0xc6c74630, 0x53843795),
+     TOBN(0x78193dd7, 0x65ec2d4c), TOBN(0xb8df26cc, 0xcd3c89b2),
+     TOBN(0x98dbe399, 0x5a483f8d), TOBN(0x72d8a957, 0x7dd3313a),
+     TOBN(0x65087294, 0xab0bd375), TOBN(0xfcd89248, 0x7c259d16),
+     TOBN(0x8a9443d7, 0x7613aa81), TOBN(0x80100800, 0x85fe6584),
+     TOBN(0x70fc4dbc, 0x7fb10288), TOBN(0xf58280d3, 0xe86beee8),
+     TOBN(0x14fdd82f, 0x7c978c38), TOBN(0xdf1204c1, 0x0de44d7b),
+     TOBN(0xa08a1c84, 0x4160252f), TOBN(0x591554ca, 0xc17646a5),
+     TOBN(0x214a37d6, 0xa05bd525), TOBN(0x48d5f09b, 0x07957b3c),
+     TOBN(0x0247cdcb, 0xd7109bc9), TOBN(0x40f9e4bb, 0x30599ce7),
+     TOBN(0xc325fa03, 0xf46ad2ec), TOBN(0x00f766cf, 0xc3e3f9ee),
+     TOBN(0xab556668, 0xd43a4577), TOBN(0x68d30a61, 0x3ee03b93),
+     TOBN(0x7ddc81ea, 0x77b46a08), TOBN(0xcf5a6477, 0xc7480699),
+     TOBN(0x43a8cb34, 0x6633f683), TOBN(0x1b867e6b, 0x92363c60),
+     TOBN(0x43921114, 0x1f60558e), TOBN(0xcdbcdd63, 0x2f41450e),
+     TOBN(0x7fc04601, 0xcc630e8b), TOBN(0xea7c66d5, 0x97038b43),
+     TOBN(0x7259b8a5, 0x04e99fd8), TOBN(0x98a8dd12, 0x4785549a),
+     TOBN(0x0e459a7c, 0x840552e1), TOBN(0xcdfcf4d0, 0x4bb0909e),
+     TOBN(0x34a86db2, 0x53758da7), TOBN(0xe643bb83, 0xeac997e1),
+     TOBN(0x96400bd7, 0x530c5b7e), TOBN(0x9f97af87, 0xb41c8b52),
+     TOBN(0x34fc8820, 0xfbeee3f9), TOBN(0x93e53490, 0x49091afd),
+     TOBN(0x764b9be5, 0x9a31f35c), TOBN(0x71f37864, 0x57e3d924),
+     TOBN(0x02fb34e0, 0x943aa75e), TOBN(0xa18c9c58, 0xab8ff6e4),
+     TOBN(0x080f31b1, 0x33cf0d19), TOBN(0x5c9682db, 0x083518a7),
+     TOBN(0x873d4ca6, 0xb709c3de), TOBN(0x64a84262, 0x3575b8f0),
+     TOBN(0x6275da1f, 0x020154bb), TOBN(0x97678caa, 0xd17cf1ab),
+     TOBN(0x8779795f, 0x951a95c3), TOBN(0xdd35b163, 0x50fccc08),
+     TOBN(0x32709627, 0x33d8f031), TOBN(0x3c5ab10a, 0x498dd85c),
+     TOBN(0xb6c185c3, 0x41dca566), TOBN(0x7de7feda, 0xd8622aa3),
+     TOBN(0x99e84d92, 0x901b6dfb), TOBN(0x30a02b0e, 0x7c4ad288),
+     TOBN(0xc7c81daa, 0x2fd3cf36), TOBN(0xd1319547, 0xdf89e59f),
+     TOBN(0xb2be8184, 0xcd496733), TOBN(0xd5f449eb, 0x93d3412b),
+     TOBN(0x7ea41b1b, 0x25fe531d), TOBN(0xf9797432, 0x6a1d5646),
+     TOBN(0x86067f72, 0x2bde501a), TOBN(0xf91481c0, 0x0c85e89c),
+     TOBN(0xca8ee465, 0xf8b05bc6), TOBN(0x1844e1cf, 0x02e83cda),
+     TOBN(0xca82114a, 0xb4dbe33b), TOBN(0x0f9f8769, 0x4eabfde2),
+     TOBN(0x4936b1c0, 0x38b27fe2), TOBN(0x63b6359b, 0xaba402df),
+     TOBN(0x40c0ea2f, 0x656bdbab), TOBN(0x9c992a89, 0x6580c39c),
+     TOBN(0x600e8f15, 0x2a60aed1), TOBN(0xeb089ca4, 0xe0bf49df),
+     TOBN(0x9c233d7d, 0x2d42d99a), TOBN(0x648d3f95, 0x4c6bc2fa),
+     TOBN(0xdcc383a8, 0xe1add3f3), TOBN(0xf42c0c6a, 0x4f64a348),
+     TOBN(0x2abd176f, 0x0030dbdb), TOBN(0x4de501a3, 0x7d6c215e),
+     TOBN(0x4a107c1f, 0x4b9a64bc), TOBN(0xa77f0ad3, 0x2496cd59),
+     TOBN(0xfb78ac62, 0x7688dffb), TOBN(0x7025a2ca, 0x67937d8e),
+     TOBN(0xfde8b2d1, 0xd1a8f4e7), TOBN(0xf5b3da47, 0x7354927c),
+     TOBN(0xe48606a3, 0xd9205735), TOBN(0xac477cc6, 0xe177b917),
+     TOBN(0xfb1f73d2, 0xa883239a), TOBN(0xe12572f6, 0xcc8b8357),
+     TOBN(0x9d355e9c, 0xfb1f4f86), TOBN(0x89b795f8, 0xd9f3ec6e),
+     TOBN(0x27be56f1, 0xb54398dc), TOBN(0x1890efd7, 0x3fedeed5),
+     TOBN(0x62f77f1f, 0x9c6d0140), TOBN(0x7ef0e314, 0x596f0ee4),
+     TOBN(0x50ca6631, 0xcc61dab3), TOBN(0x4a39801d, 0xf4866e4f),
+     TOBN(0x66c8d032, 0xae363b39), TOBN(0x22c591e5, 0x2ead66aa),
+     TOBN(0x954ba308, 0xde02a53e), TOBN(0x2a6c060f, 0xd389f357),
+     TOBN(0xe6cfcde8, 0xfbf40b66), TOBN(0x8e02fc56, 0xc6340ce1),
+     TOBN(0xe4957795, 0x73adb4ba), TOBN(0x7b86122c, 0xa7b03805),
+     TOBN(0x63f83512, 0x0c8e6fa6), TOBN(0x83660ea0, 0x057d7804),
+     TOBN(0xbad79105, 0x21ba473c), TOBN(0xb6c50bee, 0xded5389d),
+     TOBN(0xee2caf4d, 0xaa7c9bc0), TOBN(0xd97b8de4, 0x8c4e98a7),
+     TOBN(0xa9f63e70, 0xab3bbddb), TOBN(0x3898aabf, 0x2597815a),
+     TOBN(0x7659af89, 0xac15b3d9), TOBN(0xedf7725b, 0x703ce784),
+     TOBN(0x25470fab, 0xe085116b), TOBN(0x04a43375, 0x87285310),
+     TOBN(0x4e39187e, 0xe2bfd52f), TOBN(0x36166b44, 0x7d9ebc74),
+     TOBN(0x92ad433c, 0xfd4b322c), TOBN(0x726aa817, 0xba79ab51),
+     TOBN(0xf96eacd8, 0xc1db15eb), TOBN(0xfaf71e91, 0x0476be63),
+     TOBN(0xdd69a640, 0x641fad98), TOBN(0xb7995918, 0x29622559),
+     TOBN(0x03c6daa5, 0xde4199dc), TOBN(0x92cadc97, 0xad545eb4),
+     TOBN(0x1028238b, 0x256534e4), TOBN(0x73e80ce6, 0x8595409a),
+     TOBN(0x690d4c66, 0xd05dc59b), TOBN(0xc95f7b8f, 0x981dee80),
+     TOBN(0xf4337014, 0xd856ac25), TOBN(0x441bd9dd, 0xac524dca),
+     TOBN(0x640b3d85, 0x5f0499f5), TOBN(0x39cf84a9, 0xd5fda182),
+     TOBN(0x04e7b055, 0xb2aa95a0), TOBN(0x29e33f0a, 0x0ddf1860),
+     TOBN(0x082e74b5, 0x423f6b43), TOBN(0x217edeb9, 0x0aaa2b0f),
+     TOBN(0x58b83f35, 0x83cbea55), TOBN(0xc485ee4d, 0xbc185d70),
+     TOBN(0x833ff03b, 0x1e5f6992), TOBN(0xb5b9b9cc, 0xcf0c0dd5),
+     TOBN(0x7caaee8e, 0x4e9e8a50), TOBN(0x462e907b, 0x6269dafd),
+     TOBN(0x6ed5cee9, 0xfbe791c6), TOBN(0x68ca3259, 0xed430790),
+     TOBN(0x2b72bdf2, 0x13b5ba88), TOBN(0x60294c8a, 0x35ef0ac4),
+     TOBN(0x9c3230ed, 0x19b99b08), TOBN(0x560fff17, 0x6c2589aa),
+     TOBN(0x552b8487, 0xd6770374), TOBN(0xa373202d, 0x9a56f685),
+     TOBN(0xd3e7f907, 0x45f175d9), TOBN(0x3c2f315f, 0xd080d810),
+     TOBN(0x1130e9dd, 0x7b9520e8), TOBN(0xc078f9e2, 0x0af037b5),
+     TOBN(0x38cd2ec7, 0x1e9c104c), TOBN(0x0f684368, 0xc472fe92),
+     TOBN(0xd3f1b5ed, 0x6247e7ef), TOBN(0xb32d33a9, 0x396dfe21),
+     TOBN(0x46f59cf4, 0x4a9aa2c2), TOBN(0x69cd5168, 0xff0f7e41),
+     TOBN(0x3f59da0f, 0x4b3234da), TOBN(0xcf0b0235, 0xb4579ebe),
+     TOBN(0x6d1cbb25, 0x6d2476c7), TOBN(0x4f0837e6, 0x9dc30f08),
+     TOBN(0x9a4075bb, 0x906f6e98), TOBN(0x253bb434, 0xc761e7d1),
+     TOBN(0xde2e645f, 0x6e73af10), TOBN(0xb89a4060, 0x0c5f131c),
+     TOBN(0xd12840c5, 0xb8cc037f), TOBN(0x3d093a5b, 0x7405bb47),
+     TOBN(0x6202c253, 0x206348b8), TOBN(0xbf5d57fc, 0xc55a3ca7),
+     TOBN(0x89f6c90c, 0x8c3bef48), TOBN(0x23ac7623, 0x5a0a960a),
+     TOBN(0xdfbd3d6b, 0x552b42ab), TOBN(0x3ef22458, 0x132061f6),
+     TOBN(0xd74e9bda, 0xc97e6516), TOBN(0x88779360, 0xc230f49e),
+     TOBN(0xa6ec1de3, 0x1e74ea49), TOBN(0x581dcee5, 0x3fb645a2),
+     TOBN(0xbaef2391, 0x8f483f14), TOBN(0x6d2dddfc, 0xd137d13b),
+     TOBN(0x54cde50e, 0xd2743a42), TOBN(0x89a34fc5, 0xe4d97e67),
+     TOBN(0x13f1f5b3, 0x12e08ce5), TOBN(0xa80540b8, 0xa7f0b2ca),
+     TOBN(0x854bcf77, 0x01982805), TOBN(0xb8653ffd, 0x233bea04),
+     TOBN(0x8e7b8787, 0x02b0b4c9), TOBN(0x2675261f, 0x9acb170a),
+     TOBN(0x061a9d90, 0x930c14e5), TOBN(0xb59b30e0, 0xdef0abea),
+     TOBN(0x1dc19ea6, 0x0200ec7d), TOBN(0xb6f4a3f9, 0x0bce132b),
+     TOBN(0xb8d5de90, 0xf13e27e0), TOBN(0xbaee5ef0, 0x1fade16f),
+     TOBN(0x6f406aaa, 0xe4c6cf38), TOBN(0xab4cfe06, 0xd1369815),
+     TOBN(0x0dcffe87, 0xefd550c6), TOBN(0x9d4f59c7, 0x75ff7d39),
+     TOBN(0xb02553b1, 0x51deb6ad), TOBN(0x812399a4, 0xb1877749),
+     TOBN(0xce90f71f, 0xca6006e1), TOBN(0xc32363a6, 0xb02b6e77),
+     TOBN(0x02284fbe, 0xdc36c64d), TOBN(0x86c81e31, 0xa7e1ae61),
+     TOBN(0x2576c7e5, 0xb909d94a), TOBN(0x8b6f7d02, 0x818b2bb0),
+     TOBN(0xeca3ed07, 0x56faa38a), TOBN(0xa3790e6c, 0x9305bb54),
+     TOBN(0xd784eeda, 0x7bc73061), TOBN(0xbd56d369, 0x6dd50614),
+     TOBN(0xd6575949, 0x229a8aa9), TOBN(0xdcca8f47, 0x4595ec28),
+     TOBN(0x814305c1, 0x06ab4fe6), TOBN(0xc8c39768, 0x24f43f16),
+     TOBN(0xe2a45f36, 0x523f2b36), TOBN(0x995c6493, 0x920d93bb),
+     TOBN(0xf8afdab7, 0x90f1632b), TOBN(0x79ebbecd, 0x1c295954),
+     TOBN(0xc7bb3ddb, 0x79592f48), TOBN(0x67216a7b, 0x5f88e998),
+     TOBN(0xd91f098b, 0xbc01193e), TOBN(0xf7d928a5, 0xb1db83fc),
+     TOBN(0x55e38417, 0xe991f600), TOBN(0x2a91113e, 0x2981a934),
+     TOBN(0xcbc9d648, 0x06b13bde), TOBN(0xb011b6ac, 0x0755ff44),
+     TOBN(0x6f4cb518, 0x045ec613), TOBN(0x522d2d31, 0xc2f5930a),
+     TOBN(0x5acae1af, 0x382e65de), TOBN(0x57643067, 0x27bc966f),
+     TOBN(0x5e12705d, 0x1c7193f0), TOBN(0xf0f32f47, 0x3be8858e),
+     TOBN(0x785c3d7d, 0x96c6dfc7), TOBN(0xd75b4a20, 0xbf31795d),
+     TOBN(0x91acf17b, 0x342659d4), TOBN(0xe596ea34, 0x44f0378f),
+     TOBN(0x4515708f, 0xce52129d), TOBN(0x17387e1e, 0x79f2f585),
+     TOBN(0x72cfd2e9, 0x49dee168), TOBN(0x1ae05223, 0x3e2af239),
+     TOBN(0x009e75be, 0x1d94066a), TOBN(0x6cca31c7, 0x38abf413),
+     TOBN(0xb50bd61d, 0x9bc49908), TOBN(0x4a9b4a8c, 0xf5e2bc1e),
+     TOBN(0xeb6cc5f7, 0x946f83ac), TOBN(0x27da93fc, 0xebffab28),
+     TOBN(0xea314c96, 0x4821c8c5), TOBN(0x8de49ded, 0xa83c15f4),
+     TOBN(0x7a64cf20, 0x7af33004), TOBN(0x45f1bfeb, 0xc9627e10),
+     TOBN(0x878b0626, 0x54b9df60), TOBN(0x5e4fdc3c, 0xa95c0b33),
+     TOBN(0xe54a37ca, 0xc2035d8e), TOBN(0x9087cda9, 0x80f20b8c),
+     TOBN(0x36f61c23, 0x8319ade4), TOBN(0x766f287a, 0xde8cfdf8),
+     TOBN(0x48821948, 0x346f3705), TOBN(0x49a7b853, 0x16e4f4a2),
+     TOBN(0xb9b3f8a7, 0x5cedadfd), TOBN(0x8f562815, 0x8db2a815),
+     TOBN(0xc0b7d554, 0x01f68f95), TOBN(0x12971e27, 0x688a208e),
+     TOBN(0xc9f8b696, 0xd0ff34fc), TOBN(0x20824de2, 0x1222718c),
+     TOBN(0x7213cf9f, 0x0c95284d), TOBN(0xe2ad741b, 0xdc158240),
+     TOBN(0x0ee3a6df, 0x54043ccf), TOBN(0x16ff479b, 0xd84412b3),
+     TOBN(0xf6c74ee0, 0xdfc98af0), TOBN(0xa78a169f, 0x52fcd2fb),
+     TOBN(0xd8ae8746, 0x99c930e9), TOBN(0x1d33e858, 0x49e117a5),
+     TOBN(0x7581fcb4, 0x6624759f), TOBN(0xde50644f, 0x5bedc01d),
+     TOBN(0xbeec5d00, 0xcaf3155e), TOBN(0x672d66ac, 0xbc73e75f),
+     TOBN(0x86b9d8c6, 0x270b01db), TOBN(0xd249ef83, 0x50f55b79),
+     TOBN(0x6131d6d4, 0x73978fe3), TOBN(0xcc4e4542, 0x754b00a1),
+     TOBN(0x4e05df05, 0x57dfcfe9), TOBN(0x94b29cdd, 0x51ef6bf0),
+     TOBN(0xe4530cff, 0x9bc7edf2), TOBN(0x8ac236fd, 0xd3da65f3),
+     TOBN(0x0faf7d5f, 0xc8eb0b48), TOBN(0x4d2de14c, 0x660eb039),
+     TOBN(0xc006bba7, 0x60430e54), TOBN(0x10a2d0d6, 0xda3289ab),
+     TOBN(0x9c037a5d, 0xd7979c59), TOBN(0x04d1f3d3, 0xa116d944),
+     TOBN(0x9ff22473, 0x8a0983cd), TOBN(0x28e25b38, 0xc883cabb),
+     TOBN(0xe968dba5, 0x47a58995), TOBN(0x2c80b505, 0x774eebdf),
+     TOBN(0xee763b71, 0x4a953beb), TOBN(0x502e223f, 0x1642e7f6),
+     TOBN(0x6fe4b641, 0x61d5e722), TOBN(0x9d37c5b0, 0xdbef5316),
+     TOBN(0x0115ed70, 0xf8330bc7), TOBN(0x139850e6, 0x75a72789),
+     TOBN(0x27d7faec, 0xffceccc2), TOBN(0x3016a860, 0x4fd9f7f6),
+     TOBN(0xc492ec64, 0x4cd8f64c), TOBN(0x58a2d790, 0x279d7b51),
+     TOBN(0x0ced1fc5, 0x1fc75256), TOBN(0x3e658aed, 0x8f433017),
+     TOBN(0x0b61942e, 0x05da59eb), TOBN(0xba3d60a3, 0x0ddc3722),
+     TOBN(0x7c311cd1, 0x742e7f87), TOBN(0x6473ffee, 0xf6b01b6e),}
+    ,
+    {TOBN(0x8303604f, 0x692ac542), TOBN(0xf079ffe1, 0x227b91d3),
+     TOBN(0x19f63e63, 0x15aaf9bd), TOBN(0xf99ee565, 0xf1f344fb),
+     TOBN(0x8a1d661f, 0xd6219199), TOBN(0x8c883bc6, 0xd48ce41c),
+     TOBN(0x1065118f, 0x3c74d904), TOBN(0x713889ee, 0x0faf8b1b),
+     TOBN(0x972b3f8f, 0x81a1b3be), TOBN(0x4f3ce145, 0xce2764a0),
+     TOBN(0xe2d0f1cc, 0x28c4f5f7), TOBN(0xdeee0c0d, 0xc7f3985b),
+     TOBN(0x7df4adc0, 0xd39e25c3), TOBN(0x40619820, 0xc467a080),
+     TOBN(0x440ebc93, 0x61cf5a58), TOBN(0x527729a6, 0x422ad600),
+     TOBN(0xca6c0937, 0xb1b76ba6), TOBN(0x1a2eab85, 0x4d2026dc),
+     TOBN(0xb1715e15, 0x19d9ae0a), TOBN(0xf1ad9199, 0xbac4a026),
+     TOBN(0x35b3dfb8, 0x07ea7b0e), TOBN(0xedf5496f, 0x3ed9eb89),
+     TOBN(0x8932e5ff, 0x2d6d08ab), TOBN(0xf314874e, 0x25bd2731),
+     TOBN(0xefb26a75, 0x3f73f449), TOBN(0x1d1c94f8, 0x8d44fc79),
+     TOBN(0x49f0fbc5, 0x3bc0dc4d), TOBN(0xb747ea0b, 0x3698a0d0),
+     TOBN(0x5218c3fe, 0x228d291e), TOBN(0x35b804b5, 0x43c129d6),
+     TOBN(0xfac859b8, 0xd1acc516), TOBN(0x6c10697d, 0x95d6e668),
+     TOBN(0xc38e438f, 0x0876fd4e), TOBN(0x45f0c307, 0x83d2f383),
+     TOBN(0x203cc2ec, 0xb10934cb), TOBN(0x6a8f2439, 0x2c9d46ee),
+     TOBN(0xf16b431b, 0x65ccde7b), TOBN(0x41e2cd18, 0x27e76a6f),
+     TOBN(0xb9c8cf8f, 0x4e3484d7), TOBN(0x64426efd, 0x8315244a),
+     TOBN(0x1c0a8e44, 0xfc94dea3), TOBN(0x34c8cdbf, 0xdad6a0b0),
+     TOBN(0x919c3840, 0x04113cef), TOBN(0xfd32fba4, 0x15490ffa),
+     TOBN(0x58d190f6, 0x795dcfb7), TOBN(0xfef01b03, 0x83588baf),
+     TOBN(0x9e6d1d63, 0xca1fc1c0), TOBN(0x53173f96, 0xf0a41ac9),
+     TOBN(0x2b1d402a, 0xba16f73b), TOBN(0x2fb31014, 0x8cf9b9fc),
+     TOBN(0x2d51e60e, 0x446ef7bf), TOBN(0xc731021b, 0xb91e1745),
+     TOBN(0x9d3b4724, 0x4fee99d4), TOBN(0x4bca48b6, 0xfac5c1ea),
+     TOBN(0x70f5f514, 0xbbea9af7), TOBN(0x751f55a5, 0x974c283a),
+     TOBN(0x6e30251a, 0xcb452fdb), TOBN(0x31ee6965, 0x50f30650),
+     TOBN(0xb0b3e508, 0x933548d9), TOBN(0xb8949a4f, 0xf4b0ef5b),
+     TOBN(0x208b8326, 0x3c88f3bd), TOBN(0xab147c30, 0xdb1d9989),
+     TOBN(0xed6515fd, 0x44d4df03), TOBN(0x17a12f75, 0xe72eb0c5),
+     TOBN(0x3b59796d, 0x36cf69db), TOBN(0x1219eee9, 0x56670c18),
+     TOBN(0xfe3341f7, 0x7a070d8e), TOBN(0x9b70130b, 0xa327f90c),
+     TOBN(0x36a32462, 0x0ae18e0e), TOBN(0x2021a623, 0x46c0a638),
+     TOBN(0x251b5817, 0xc62eb0d4), TOBN(0x87bfbcdf, 0x4c762293),
+     TOBN(0xf78ab505, 0xcdd61d64), TOBN(0x8c7a53fc, 0xc8c18857),
+     TOBN(0xa653ce6f, 0x16147515), TOBN(0x9c923aa5, 0xea7d52d5),
+     TOBN(0xc24709cb, 0x5c18871f), TOBN(0x7d53bec8, 0x73b3cc74),
+     TOBN(0x59264aff, 0xfdd1d4c4), TOBN(0x5555917e, 0x240da582),
+     TOBN(0xcae8bbda, 0x548f5a0e), TOBN(0x1910eaba, 0x3bbfbbe1),
+     TOBN(0xae579685, 0x7677afc3), TOBN(0x49ea61f1, 0x73ff0b5c),
+     TOBN(0x78655478, 0x4f7c3922), TOBN(0x95d337cd, 0x20c68eef),
+     TOBN(0x68f1e1e5, 0xdf779ab9), TOBN(0x14b491b0, 0xb5cf69a8),
+     TOBN(0x7a6cbbe0, 0x28e3fe89), TOBN(0xe7e1fee4, 0xc5aac0eb),
+     TOBN(0x7f47eda5, 0x697e5140), TOBN(0x4f450137, 0xb454921f),
+     TOBN(0xdb625f84, 0x95cd8185), TOBN(0x74be0ba1, 0xcdb2e583),
+     TOBN(0xaee4fd7c, 0xdd5e6de4), TOBN(0x4251437d, 0xe8101739),
+     TOBN(0x686d72a0, 0xac620366), TOBN(0x4be3fb9c, 0xb6d59344),
+     TOBN(0x6e8b44e7, 0xa1eb75b9), TOBN(0x84e39da3, 0x91a5c10c),
+     TOBN(0x37cc1490, 0xb38f0409), TOBN(0x02951943, 0x2c2ade82),
+     TOBN(0x9b688783, 0x1190a2d8), TOBN(0x25627d14, 0x231182ba),
+     TOBN(0x6eb550aa, 0x658a6d87), TOBN(0x1405aaa7, 0xcf9c7325),
+     TOBN(0xd147142e, 0x5c8748c9), TOBN(0x7f637e4f, 0x53ede0e0),
+     TOBN(0xf8ca2776, 0x14ffad2c), TOBN(0xe58fb1bd, 0xbafb6791),
+     TOBN(0x17158c23, 0xbf8f93fc), TOBN(0x7f15b373, 0x0a4a4655),
+     TOBN(0x39d4add2, 0xd842ca72), TOBN(0xa71e4391, 0x3ed96305),
+     TOBN(0x5bb09cbe, 0x6700be14), TOBN(0x68d69d54, 0xd8befcf6),
+     TOBN(0xa45f5367, 0x37183bcf), TOBN(0x7152b7bb, 0x3370dff7),
+     TOBN(0xcf887baa, 0xbf12525b), TOBN(0xe7ac7bdd, 0xd6d1e3cd),
+     TOBN(0x25914f78, 0x81fdad90), TOBN(0xcf638f56, 0x0d2cf6ab),
+     TOBN(0xb90bc03f, 0xcc054de5), TOBN(0x932811a7, 0x18b06350),
+     TOBN(0x2f00b330, 0x9bbd11ff), TOBN(0x76108a6f, 0xb4044974),
+     TOBN(0x801bb9e0, 0xa851d266), TOBN(0x0dd099be, 0xbf8990c1),
+     TOBN(0x58c5aaaa, 0xabe32986), TOBN(0x0fe9dd2a, 0x50d59c27),
+     TOBN(0x84951ff4, 0x8d307305), TOBN(0x6c23f829, 0x86529b78),
+     TOBN(0x50bb2218, 0x0b136a79), TOBN(0x7e2174de, 0x77a20996),
+     TOBN(0x6f00a4b9, 0xc0bb4da6), TOBN(0x89a25a17, 0xefdde8da),
+     TOBN(0xf728a27e, 0xc11ee01d), TOBN(0xf900553a, 0xe5f10dfb),
+     TOBN(0x189a83c8, 0x02ec893c), TOBN(0x3ca5bdc1, 0x23f66d77),
+     TOBN(0x98781537, 0x97eada9f), TOBN(0x59c50ab3, 0x10256230),
+     TOBN(0x346042d9, 0x323c69b3), TOBN(0x1b715a6d, 0x2c460449),
+     TOBN(0xa41dd476, 0x6ae06e0b), TOBN(0xcdd7888e, 0x9d42e25f),
+     TOBN(0x0f395f74, 0x56b25a20), TOBN(0xeadfe0ae, 0x8700e27e),
+     TOBN(0xb09d52a9, 0x69950093), TOBN(0x3525d9cb, 0x327f8d40),
+     TOBN(0xb8235a94, 0x67df886a), TOBN(0x77e4b0dd, 0x035faec2),
+     TOBN(0x115eb20a, 0x517d7061), TOBN(0x77fe3433, 0x6c2df683),
+     TOBN(0x6870ddc7, 0xcdc6fc67), TOBN(0xb1610588, 0x0b87de83),
+     TOBN(0x343584ca, 0xd9c4ddbe), TOBN(0xb3164f1c, 0x3d754be2),
+     TOBN(0x0731ed3a, 0xc1e6c894), TOBN(0x26327dec, 0x4f6b904c),
+     TOBN(0x9d49c6de, 0x97b5cd32), TOBN(0x40835dae, 0xb5eceecd),
+     TOBN(0xc66350ed, 0xd9ded7fe), TOBN(0x8aeebb5c, 0x7a678804),
+     TOBN(0x51d42fb7, 0x5b8ee9ec), TOBN(0xd7a17bdd, 0x8e3ca118),
+     TOBN(0x40d7511a, 0x2ef4400e), TOBN(0xc48990ac, 0x875a66f4),
+     TOBN(0x8de07d2a, 0x2199e347), TOBN(0xbee75556, 0x2a39e051),
+     TOBN(0x56918786, 0x916e51dc), TOBN(0xeb191313, 0x4a2d89ec),
+     TOBN(0x6679610d, 0x37d341ed), TOBN(0x434fbb41, 0x56d51c2b),
+     TOBN(0xe54b7ee7, 0xd7492dba), TOBN(0xaa33a79a, 0x59021493),
+     TOBN(0x49fc5054, 0xe4bd6d3d), TOBN(0x09540f04, 0x5ab551d0),
+     TOBN(0x8acc9085, 0x4942d3a6), TOBN(0x231af02f, 0x2d28323b),
+     TOBN(0x93458cac, 0x0992c163), TOBN(0x1fef8e71, 0x888e3bb4),
+     TOBN(0x27578da5, 0xbe8c268c), TOBN(0xcc8be792, 0xe805ec00),
+     TOBN(0x29267bae, 0xc61c3855), TOBN(0xebff429d, 0x58c1fd3b),
+     TOBN(0x22d886c0, 0x8c0b93b8), TOBN(0xca5e00b2, 0x2ddb8953),
+     TOBN(0xcf330117, 0xc3fed8b7), TOBN(0xd49ac6fa, 0x819c01f6),
+     TOBN(0x6ddaa6bd, 0x3c0fbd54), TOBN(0x91743068, 0x8049a2cf),
+     TOBN(0xd67f981e, 0xaff2ef81), TOBN(0xc3654d35, 0x2818ae80),
+     TOBN(0x81d05044, 0x1b2aa892), TOBN(0x2db067bf, 0x3d099328),
+     TOBN(0xe7c79e86, 0x703dcc97), TOBN(0xe66f9b37, 0xe133e215),
+     TOBN(0xcdf119a6, 0xe39a7a5c), TOBN(0x47c60de3, 0x876f1b61),
+     TOBN(0x6e405939, 0xd860f1b2), TOBN(0x3e9a1dbc, 0xf5ed4d4a),
+     TOBN(0x3f23619e, 0xc9b6bcbd), TOBN(0x5ee790cf, 0x734e4497),
+     TOBN(0xf0a834b1, 0x5bdaf9bb), TOBN(0x02cedda7, 0x4ca295f0),
+     TOBN(0x4619aa2b, 0xcb8e378c), TOBN(0xe5613244, 0xcc987ea4),
+     TOBN(0x0bc022cc, 0x76b23a50), TOBN(0x4a2793ad, 0x0a6c21ce),
+     TOBN(0x38328780, 0x89cac3f5), TOBN(0x29176f1b, 0xcba26d56),
+     TOBN(0x06296187, 0x4f6f59eb), TOBN(0x86e9bca9, 0x8bdc658e),
+     TOBN(0x2ca9c4d3, 0x57e30402), TOBN(0x5438b216, 0x516a09bb),
+     TOBN(0x0a6a063c, 0x7672765a), TOBN(0x37a3ce64, 0x0547b9bf),
+     TOBN(0x42c099c8, 0x98b1a633), TOBN(0xb5ab800d, 0x05ee6961),
+     TOBN(0xf1963f59, 0x11a5acd6), TOBN(0xbaee6157, 0x46201063),
+     TOBN(0x36d9a649, 0xa596210a), TOBN(0xaed04363, 0x1ba7138c),
+     TOBN(0xcf817d1c, 0xa4a82b76), TOBN(0x5586960e, 0xf3806be9),
+     TOBN(0x7ab67c89, 0x09dc6bb5), TOBN(0x52ace7a0, 0x114fe7eb),
+     TOBN(0xcd987618, 0xcbbc9b70), TOBN(0x4f06fd5a, 0x604ca5e1),
+     TOBN(0x90af14ca, 0x6dbde133), TOBN(0x1afe4322, 0x948a3264),
+     TOBN(0xa70d2ca6, 0xc44b2c6c), TOBN(0xab726799, 0x0ef87dfe),
+     TOBN(0x310f64dc, 0x2e696377), TOBN(0x49b42e68, 0x4c8126a0),
+     TOBN(0x0ea444c3, 0xcea0b176), TOBN(0x53a8ddf7, 0xcb269182),
+     TOBN(0xf3e674eb, 0xbbba9dcb), TOBN(0x0d2878a8, 0xd8669d33),
+     TOBN(0x04b935d5, 0xd019b6a3), TOBN(0xbb5cf88e, 0x406f1e46),
+     TOBN(0xa1912d16, 0x5b57c111), TOBN(0x9803fc21, 0x19ebfd78),
+     TOBN(0x4f231c9e, 0xc07764a9), TOBN(0xd93286ee, 0xb75bd055),
+     TOBN(0x83a9457d, 0x8ee6c9de), TOBN(0x04695915, 0x6087ec90),
+     TOBN(0x14c6dd8a, 0x58d6cd46), TOBN(0x9cb633b5, 0x8e6634d2),
+     TOBN(0xc1305047, 0xf81bc328), TOBN(0x12ede0e2, 0x26a177e5),
+     TOBN(0x332cca62, 0x065a6f4f), TOBN(0xc3a47ecd, 0x67be487b),
+     TOBN(0x741eb187, 0x0f47ed1c), TOBN(0x99e66e58, 0xe7598b14),
+     TOBN(0x6f0544ca, 0x63d0ff12), TOBN(0xe5efc784, 0xb610a05f),
+     TOBN(0xf72917b1, 0x7cad7b47), TOBN(0x3ff6ea20, 0xf2cac0c0),
+     TOBN(0xcc23791b, 0xf21db8b7), TOBN(0x7dac70b1, 0xd7d93565),
+     TOBN(0x682cda1d, 0x694bdaad), TOBN(0xeb88bb8c, 0x1023516d),
+     TOBN(0xc4c634b4, 0xdfdbeb1b), TOBN(0x22f5ca72, 0xb4ee4dea),
+     TOBN(0x1045a368, 0xe6524821), TOBN(0xed9e8a3f, 0x052b18b2),
+     TOBN(0x9b7f2cb1, 0xb961f49a), TOBN(0x7fee2ec1, 0x7b009670),
+     TOBN(0x350d8754, 0x22507a6d), TOBN(0x561bd711, 0x4db55f1d),
+     TOBN(0x4c189ccc, 0x320bbcaf), TOBN(0x568434cf, 0xdf1de48c),
+     TOBN(0x6af1b00e, 0x0fa8f128), TOBN(0xf0ba9d02, 0x8907583c),
+     TOBN(0x735a4004, 0x32ff9f60), TOBN(0x3dd8e4b6, 0xc25dcf33),
+     TOBN(0xf2230f16, 0x42c74cef), TOBN(0xd8117623, 0x013fa8ad),
+     TOBN(0x36822876, 0xf51fe76e), TOBN(0x8a6811cc, 0x11d62589),
+     TOBN(0xc3fc7e65, 0x46225718), TOBN(0xb7df2c9f, 0xc82fdbcd),
+     TOBN(0x3b1d4e52, 0xdd7b205b), TOBN(0xb6959478, 0x47a2e414),
+     TOBN(0x05e4d793, 0xefa91148), TOBN(0xb47ed446, 0xfd2e9675),
+     TOBN(0x1a7098b9, 0x04c9d9bf), TOBN(0x661e2881, 0x1b793048),
+     TOBN(0xb1a16966, 0xb01ee461), TOBN(0xbc521308, 0x2954746f),
+     TOBN(0xc909a0fc, 0x2477de50), TOBN(0xd80bb41c, 0x7dbd51ef),
+     TOBN(0xa85be7ec, 0x53294905), TOBN(0x6d465b18, 0x83958f97),
+     TOBN(0x16f6f330, 0xfb6840fd), TOBN(0xfaaeb214, 0x3401e6c8),
+     TOBN(0xaf83d30f, 0xccb5b4f8), TOBN(0x22885739, 0x266dec4b),
+     TOBN(0x51b4367c, 0x7bc467df), TOBN(0x926562e3, 0xd842d27a),
+     TOBN(0xdfcb6614, 0x0fea14a6), TOBN(0xeb394dae, 0xf2734cd9),
+     TOBN(0x3eeae5d2, 0x11c0be98), TOBN(0xb1e6ed11, 0x814e8165),
+     TOBN(0x191086bc, 0xe52bce1c), TOBN(0x14b74cc6, 0xa75a04da),
+     TOBN(0x63cf1186, 0x8c060985), TOBN(0x071047de, 0x2dbd7f7c),
+     TOBN(0x4e433b8b, 0xce0942ca), TOBN(0xecbac447, 0xd8fec61d),
+     TOBN(0x8f0ed0e2, 0xebf3232f), TOBN(0xfff80f9e, 0xc52a2edd),
+     TOBN(0xad9ab433, 0x75b55fdb), TOBN(0x73ca7820, 0xe42e0c11),
+     TOBN(0x6dace0a0, 0xe6251b46), TOBN(0x89bc6b5c, 0x4c0d932d),
+     TOBN(0x3438cd77, 0x095da19a), TOBN(0x2f24a939, 0x8d48bdfb),
+     TOBN(0x99b47e46, 0x766561b7), TOBN(0x736600e6, 0x0ed0322a),
+     TOBN(0x06a47cb1, 0x638e1865), TOBN(0x927c1c2d, 0xcb136000),
+     TOBN(0x29542337, 0x0cc5df69), TOBN(0x99b37c02, 0x09d649a9),
+     TOBN(0xc5f0043c, 0x6aefdb27), TOBN(0x6cdd9987, 0x1be95c27),
+     TOBN(0x69850931, 0x390420d2), TOBN(0x299c40ac, 0x0983efa4),
+     TOBN(0x3a05e778, 0xaf39aead), TOBN(0x84274408, 0x43a45193),
+     TOBN(0x6bcd0fb9, 0x91a711a0), TOBN(0x461592c8, 0x9f52ab17),
+     TOBN(0xb49302b4, 0xda3c6ed6), TOBN(0xc51fddc7, 0x330d7067),
+     TOBN(0x94babeb6, 0xda50d531), TOBN(0x521b840d, 0xa6a7b9da),
+     TOBN(0x5305151e, 0x404bdc89), TOBN(0x1bcde201, 0xd0d07449),
+     TOBN(0xf427a78b, 0x3b76a59a), TOBN(0xf84841ce, 0x07791a1b),
+     TOBN(0xebd314be, 0xbf91ed1c), TOBN(0x8e61d34c, 0xbf172943),
+     TOBN(0x1d5dc451, 0x5541b892), TOBN(0xb186ee41, 0xfc9d9e54),
+     TOBN(0x9d9f345e, 0xd5bf610d), TOBN(0x3e7ba65d, 0xf6acca9f),
+     TOBN(0x9dda787a, 0xa8369486), TOBN(0x09f9dab7, 0x8eb5ba53),
+     TOBN(0x5afb2033, 0xd6481bc3), TOBN(0x76f4ce30, 0xafa62104),
+     TOBN(0xa8fa00cf, 0xf4f066b5), TOBN(0x89ab5143, 0x461dafc2),
+     TOBN(0x44339ed7, 0xa3389998), TOBN(0x2ff862f1, 0xbc214903),
+     TOBN(0x2c88f985, 0xb05556e3), TOBN(0xcd96058e, 0x3467081e),
+     TOBN(0x7d6a4176, 0xedc637ea), TOBN(0xe1743d09, 0x36a5acdc),
+     TOBN(0x66fd72e2, 0x7eb37726), TOBN(0xf7fa264e, 0x1481a037),
+     TOBN(0x9fbd3bde, 0x45f4aa79), TOBN(0xed1e0147, 0x767c3e22),
+     TOBN(0x7621f979, 0x82e7abe2), TOBN(0x19eedc72, 0x45f633f8),
+     TOBN(0xe69b155e, 0x6137bf3a), TOBN(0xa0ad13ce, 0x414ee94e),
+     TOBN(0x93e3d524, 0x1c0e651a), TOBN(0xab1a6e2a, 0x02ce227e),
+     TOBN(0xe7af1797, 0x4ab27eca), TOBN(0x245446de, 0xbd444f39),
+     TOBN(0x59e22a21, 0x56c07613), TOBN(0x43deafce, 0xf4275498),
+     TOBN(0x10834ccb, 0x67fd0946), TOBN(0xa75841e5, 0x47406edf),
+     TOBN(0xebd6a677, 0x7b0ac93d), TOBN(0xa6e37b0d, 0x78f5e0d7),
+     TOBN(0x2516c096, 0x76f5492b), TOBN(0x1e4bf888, 0x9ac05f3a),
+     TOBN(0xcdb42ce0, 0x4df0ba2b), TOBN(0x935d5cfd, 0x5062341b),
+     TOBN(0x8a303333, 0x82acac20), TOBN(0x429438c4, 0x5198b00e),
+     TOBN(0x1d083bc9, 0x049d33fa), TOBN(0x58b82dda, 0x946f67ff),
+     TOBN(0xac3e2db8, 0x67a1d6a3), TOBN(0x62e6bead, 0x1798aac8),
+     TOBN(0xfc85980f, 0xde46c58c), TOBN(0xa7f69379, 0x69c8d7be),
+     TOBN(0x23557927, 0x837b35ec), TOBN(0x06a933d8, 0xe0790c0c),
+     TOBN(0x827c0e9b, 0x077ff55d), TOBN(0x53977798, 0xbb26e680),
+     TOBN(0x59530874, 0x1d9cb54f), TOBN(0xcca3f449, 0x4aac53ef),
+     TOBN(0x11dc5c87, 0xa07eda0f), TOBN(0xc138bccf, 0xfd6400c8),
+     TOBN(0x549680d3, 0x13e5da72), TOBN(0xc93eed82, 0x4540617e),
+     TOBN(0xfd3db157, 0x4d0b75c0), TOBN(0x9716eb42, 0x6386075b),
+     TOBN(0x0639605c, 0x817b2c16), TOBN(0x09915109, 0xf1e4f201),
+     TOBN(0x35c9a928, 0x5cca6c3b), TOBN(0xb25f7d1a, 0x3505c900),
+     TOBN(0xeb9f7d20, 0x630480c4), TOBN(0xc3c7b8c6, 0x2a1a501c),
+     TOBN(0x3f99183c, 0x5a1f8e24), TOBN(0xfdb118fa, 0x9dd255f0),
+     TOBN(0xb9b18b90, 0xc27f62a6), TOBN(0xe8f732f7, 0x396ec191),
+     TOBN(0x524a2d91, 0x0be786ab), TOBN(0x5d32adef, 0x0ac5a0f5),
+     TOBN(0x9b53d4d6, 0x9725f694), TOBN(0x032a76c6, 0x0510ba89),
+     TOBN(0x840391a3, 0xebeb1544), TOBN(0x44b7b88c, 0x3ed73ac3),
+     TOBN(0xd24bae7a, 0x256cb8b3), TOBN(0x7ceb151a, 0xe394cb12),
+     TOBN(0xbd6b66d0, 0x5bc1e6a8), TOBN(0xec70cecb, 0x090f07bf),
+     TOBN(0x270644ed, 0x7d937589), TOBN(0xee9e1a3d, 0x5f1dccfe),
+     TOBN(0xb0d40a84, 0x745b98d2), TOBN(0xda429a21, 0x2556ed40),
+     TOBN(0xf676eced, 0x85148cb9), TOBN(0x5a22d40c, 0xded18936),
+     TOBN(0x3bc4b9e5, 0x70e8a4ce), TOBN(0xbfd1445b, 0x9eae0379),
+     TOBN(0xf23f2c0c, 0x1a0bd47e), TOBN(0xa9c0bb31, 0xe1845531),
+     TOBN(0x9ddc4d60, 0x0a4c3f6b), TOBN(0xbdfaad79, 0x2c15ef44),
+     TOBN(0xce55a236, 0x7f484acc), TOBN(0x08653ca7, 0x055b1f15),
+     TOBN(0x2efa8724, 0x538873a3), TOBN(0x09299e5d, 0xace1c7e7),
+     TOBN(0x07afab66, 0xade332ba), TOBN(0x9be1fdf6, 0x92dd71b7),
+     TOBN(0xa49b5d59, 0x5758b11c), TOBN(0x0b852893, 0xc8654f40),
+     TOBN(0xb63ef6f4, 0x52379447), TOBN(0xd4957d29, 0x105e690c),
+     TOBN(0x7d484363, 0x646559b0), TOBN(0xf4a8273c, 0x49788a8e),
+     TOBN(0xee406cb8, 0x34ce54a9), TOBN(0x1e1c260f, 0xf86fda9b),
+     TOBN(0xe150e228, 0xcf6a4a81), TOBN(0x1fa3b6a3, 0x1b488772),
+     TOBN(0x1e6ff110, 0xc5a9c15b), TOBN(0xc6133b91, 0x8ad6aa47),
+     TOBN(0x8ac5d55c, 0x9dffa978), TOBN(0xba1d1c1d, 0x5f3965f2),
+     TOBN(0xf969f4e0, 0x7732b52f), TOBN(0xfceecdb5, 0xa5172a07),
+     TOBN(0xb0120a5f, 0x10f2b8f5), TOBN(0xc83a6cdf, 0x5c4c2f63),
+     TOBN(0x4d47a491, 0xf8f9c213), TOBN(0xd9e1cce5, 0xd3f1bbd5),
+     TOBN(0x0d91bc7c, 0xaba7e372), TOBN(0xfcdc74c8, 0xdfd1a2db),
+     TOBN(0x05efa800, 0x374618e5), TOBN(0x11216969, 0x15a7925e),
+     TOBN(0xd4c89823, 0xf6021c5d), TOBN(0x880d5e84, 0xeff14423),
+     TOBN(0x6523bc5a, 0x6dcd1396), TOBN(0xd1acfdfc, 0x113c978b),
+     TOBN(0xb0c164e8, 0xbbb66840), TOBN(0xf7f4301e, 0x72b58459),
+     TOBN(0xc29ad4a6, 0xa638e8ec), TOBN(0xf5ab8961, 0x46b78699),
+     TOBN(0x9dbd7974, 0x0e954750), TOBN(0x0121de88, 0x64f9d2c6),
+     TOBN(0x2e597b42, 0xd985232e), TOBN(0x55b6c3c5, 0x53451777),
+     TOBN(0xbb53e547, 0x519cb9fb), TOBN(0xf134019f, 0x8428600d),
+     TOBN(0x5a473176, 0xe081791a), TOBN(0x2f3e2263, 0x35fb0c08),
+     TOBN(0xb28c3017, 0x73d273b0), TOBN(0xccd21076, 0x7721ef9a),
+     TOBN(0x054cc292, 0xb650dc39), TOBN(0x662246de, 0x6188045e),
+     TOBN(0x904b52fa, 0x6b83c0d1), TOBN(0xa72df267, 0x97e9cd46),
+     TOBN(0x886b43cd, 0x899725e4), TOBN(0x2b651688, 0xd849ff22),
+     TOBN(0x60479b79, 0x02f34533), TOBN(0x5e354c14, 0x0c77c148),
+     TOBN(0xb4bb7581, 0xa8537c78), TOBN(0x188043d7, 0xefe1495f),
+     TOBN(0x9ba12f42, 0x8c1d5026), TOBN(0x2e0c8a26, 0x93d4aaab),
+     TOBN(0xbdba7b8b, 0xaa57c450), TOBN(0x140c9ad6, 0x9bbdafef),
+     TOBN(0x2067aa42, 0x25ac0f18), TOBN(0xf7b1295b, 0x04d1fbf3),
+     TOBN(0x14829111, 0xa4b04824), TOBN(0x2ce3f192, 0x33bd5e91),
+     TOBN(0x9c7a1d55, 0x8f2e1b72), TOBN(0xfe932286, 0x302aa243),
+     TOBN(0x497ca7b4, 0xd4be9554), TOBN(0xb8e821b8, 0xe0547a6e),
+     TOBN(0xfb2838be, 0x67e573e0), TOBN(0x05891db9, 0x4084c44b),
+     TOBN(0x91311373, 0x96c1c2c5), TOBN(0x6aebfa3f, 0xd958444b),
+     TOBN(0xac9cdce9, 0xe56e55c1), TOBN(0x7148ced3, 0x2caa46d0),
+     TOBN(0x2e10c7ef, 0xb61fe8eb), TOBN(0x9fd835da, 0xff97cf4d),}
+    ,
+    {TOBN(0xa36da109, 0x081e9387), TOBN(0xfb9780d7, 0x8c935828),
+     TOBN(0xd5940332, 0xe540b015), TOBN(0xc9d7b51b, 0xe0f466fa),
+     TOBN(0xfaadcd41, 0xd6d9f671), TOBN(0xba6c1e28, 0xb1a2ac17),
+     TOBN(0x066a7833, 0xed201e5f), TOBN(0x19d99719, 0xf90f462b),
+     TOBN(0xf431f462, 0x060b5f61), TOBN(0xa56f46b4, 0x7bd057c2),
+     TOBN(0x348dca6c, 0x47e1bf65), TOBN(0x9a38783e, 0x41bcf1ff),
+     TOBN(0x7a5d33a9, 0xda710718), TOBN(0x5a779987, 0x2e0aeaf6),
+     TOBN(0xca87314d, 0x2d29d187), TOBN(0xfa0edc3e, 0xc687d733),
+     TOBN(0x9df33621, 0x6a31e09b), TOBN(0xde89e44d, 0xc1350e35),
+     TOBN(0x29214871, 0x4ca0cf52), TOBN(0xdf379672, 0x0b88a538),
+     TOBN(0xc92a510a, 0x2591d61b), TOBN(0x79aa87d7, 0x585b447b),
+     TOBN(0xf67db604, 0xe5287f77), TOBN(0x1697c8bf, 0x5efe7a80),
+     TOBN(0x1c894849, 0xcb198ac7), TOBN(0xa884a93d, 0x0f264665),
+     TOBN(0x2da964ef, 0x9b200678), TOBN(0x3c351b87, 0x009834e6),
+     TOBN(0xafb2ef9f, 0xe2c4b44b), TOBN(0x580f6c47, 0x3326790c),
+     TOBN(0xb8480521, 0x0b02264a), TOBN(0x8ba6f9e2, 0x42a194e2),
+     TOBN(0xfc87975f, 0x8fb54738), TOBN(0x35160788, 0x27c3ead3),
+     TOBN(0x834116d2, 0xb74a085a), TOBN(0x53c99a73, 0xa62fe996),
+     TOBN(0x87585be0, 0x5b81c51b), TOBN(0x925bafa8, 0xbe0852b7),
+     TOBN(0x76a4fafd, 0xa84d19a7), TOBN(0x39a45982, 0x585206d4),
+     TOBN(0x499b6ab6, 0x5eb03c0e), TOBN(0xf19b7954, 0x72bc3fde),
+     TOBN(0xa86b5b9c, 0x6e3a80d2), TOBN(0xe4377508, 0x6d42819f),
+     TOBN(0xc1663650, 0xbb3ee8a3), TOBN(0x75eb14fc, 0xb132075f),
+     TOBN(0xa8ccc906, 0x7ad834f6), TOBN(0xea6a2474, 0xe6e92ffd),
+     TOBN(0x9d72fd95, 0x0f8d6758), TOBN(0xcb84e101, 0x408c07dd),
+     TOBN(0xb9114bfd, 0xa5e23221), TOBN(0x358b5fe2, 0xe94e742c),
+     TOBN(0x1c0577ec, 0x95f40e75), TOBN(0xf0155451, 0x3d73f3d6),
+     TOBN(0x9d55cd67, 0xbd1b9b66), TOBN(0x63e86e78, 0xaf8d63c7),
+     TOBN(0x39d934ab, 0xd3c095f1), TOBN(0x04b261be, 0xe4b76d71),
+     TOBN(0x1d2e6970, 0xe73e6984), TOBN(0x879fb23b, 0x5e5fcb11),
+     TOBN(0x11506c72, 0xdfd75490), TOBN(0x3a97d085, 0x61bcf1c1),
+     TOBN(0x43201d82, 0xbf5e7007), TOBN(0x7f0ac52f, 0x798232a7),
+     TOBN(0x2715cbc4, 0x6eb564d4), TOBN(0x8d6c752c, 0x9e570e29),
+     TOBN(0xf80247c8, 0x9ef5fd5d), TOBN(0xc3c66b46, 0xd53eb514),
+     TOBN(0x9666b401, 0x0f87de56), TOBN(0xce62c06f, 0xc6c603b5),
+     TOBN(0xae7b4c60, 0x7e4fc942), TOBN(0x38ac0b77, 0x663a9c19),
+     TOBN(0xcb4d20ee, 0x4b049136), TOBN(0x8b63bf12, 0x356a4613),
+     TOBN(0x1221aef6, 0x70e08128), TOBN(0xe62d8c51, 0x4acb6b16),
+     TOBN(0x71f64a67, 0x379e7896), TOBN(0xb25237a2, 0xcafd7fa5),
+     TOBN(0xf077bd98, 0x3841ba6a), TOBN(0xc4ac0244, 0x3cd16e7e),
+     TOBN(0x548ba869, 0x21fea4ca), TOBN(0xd36d0817, 0xf3dfdac1),
+     TOBN(0x09d8d71f, 0xf4685faf), TOBN(0x8eff66be, 0xc52c459a),
+     TOBN(0x182faee7, 0x0b57235e), TOBN(0xee3c39b1, 0x0106712b),
+     TOBN(0x5107331f, 0xc0fcdcb0), TOBN(0x669fb9dc, 0xa51054ba),
+     TOBN(0xb25101fb, 0x319d7682), TOBN(0xb0293129, 0x0a982fee),
+     TOBN(0x51c1c9b9, 0x0261b344), TOBN(0x0e008c5b, 0xbfd371fa),
+     TOBN(0xd866dd1c, 0x0278ca33), TOBN(0x666f76a6, 0xe5aa53b1),
+     TOBN(0xe5cfb779, 0x6013a2cf), TOBN(0x1d3a1aad, 0xa3521836),
+     TOBN(0xcedd2531, 0x73faa485), TOBN(0xc8ee6c4f, 0xc0a76878),
+     TOBN(0xddbccfc9, 0x2a11667d), TOBN(0x1a418ea9, 0x1c2f695a),
+     TOBN(0xdb11bd92, 0x51f73971), TOBN(0x3e4b3c82, 0xda2ed89f),
+     TOBN(0x9a44f3f4, 0xe73e0319), TOBN(0xd1e3de0f, 0x303431af),
+     TOBN(0x3c5604ff, 0x50f75f9c), TOBN(0x1d8eddf3, 0x7e752b22),
+     TOBN(0x0ef074dd, 0x3c9a1118), TOBN(0xd0ffc172, 0xccb86d7b),
+     TOBN(0xabd1ece3, 0x037d90f2), TOBN(0xe3f307d6, 0x6055856c),
+     TOBN(0x422f9328, 0x7e4c6daf), TOBN(0x902aac66, 0x334879a0),
+     TOBN(0xb6a1e7bf, 0x94cdfade), TOBN(0x6c97e1ed, 0x7fc6d634),
+     TOBN(0x662ad24d, 0xa2fb63f8), TOBN(0xf81be1b9, 0xa5928405),
+     TOBN(0x86d765e4, 0xd14b4206), TOBN(0xbecc2e0e, 0x8fa0db65),
+     TOBN(0xa28838e0, 0xb17fc76c), TOBN(0xe49a602a, 0xe37cf24e),
+     TOBN(0x76b4131a, 0x567193ec), TOBN(0xaf3c305a, 0xe5f6e70b),
+     TOBN(0x9587bd39, 0x031eebdd), TOBN(0x5709def8, 0x71bbe831),
+     TOBN(0x57059983, 0x0eb2b669), TOBN(0x4d80ce1b, 0x875b7029),
+     TOBN(0x838a7da8, 0x0364ac16), TOBN(0x2f431d23, 0xbe1c83ab),
+     TOBN(0xe56812a6, 0xf9294dd3), TOBN(0xb448d01f, 0x9b4b0d77),
+     TOBN(0xf3ae6061, 0x04e8305c), TOBN(0x2bead645, 0x94d8c63e),
+     TOBN(0x0a85434d, 0x84fd8b07), TOBN(0x537b983f, 0xf7a9dee5),
+     TOBN(0xedcc5f18, 0xef55bd85), TOBN(0x2041af62, 0x21c6cf8b),
+     TOBN(0x8e52874c, 0xb940c71e), TOBN(0x211935a9, 0xdb5f4b3a),
+     TOBN(0x94350492, 0x301b1dc3), TOBN(0x33d2646d, 0x29958620),
+     TOBN(0x16b0d64b, 0xef911404), TOBN(0x9d1f25ea, 0x9a3c5ef4),
+     TOBN(0x20f200eb, 0x4a352c78), TOBN(0x43929f2c, 0x4bd0b428),
+     TOBN(0xa5656667, 0xc7196e29), TOBN(0x7992c2f0, 0x9391be48),
+     TOBN(0xaaa97cbd, 0x9ee0cd6e), TOBN(0x51b0310c, 0x3dc8c9bf),
+     TOBN(0x237f8acf, 0xdd9f22cb), TOBN(0xbb1d81a1, 0xb585d584),
+     TOBN(0x8d5d85f5, 0x8c416388), TOBN(0x0d6e5a5a, 0x42fe474f),
+     TOBN(0xe7812766, 0x38235d4e), TOBN(0x1c62bd67, 0x496e3298),
+     TOBN(0x8378660c, 0x3f175bc8), TOBN(0x4d04e189, 0x17afdd4d),
+     TOBN(0x32a81601, 0x85a8068c), TOBN(0xdb58e4e1, 0x92b29a85),
+     TOBN(0xe8a65b86, 0xc70d8a3b), TOBN(0x5f0e6f4e, 0x98a0403b),
+     TOBN(0x08129684, 0x69ed2370), TOBN(0x34dc30bd, 0x0871ee26),
+     TOBN(0x3a5ce948, 0x7c9c5b05), TOBN(0x7d487b80, 0x43a90c87),
+     TOBN(0x4089ba37, 0xdd0e7179), TOBN(0x45f80191, 0xb4041811),
+     TOBN(0x1c3e1058, 0x98747ba5), TOBN(0x98c4e13a, 0x6e1ae592),
+     TOBN(0xd44636e6, 0xe82c9f9e), TOBN(0x711db87c, 0xc33a1043),
+     TOBN(0x6f431263, 0xaa8aec05), TOBN(0x43ff120d, 0x2744a4aa),
+     TOBN(0xd3bd892f, 0xae77779b), TOBN(0xf0fe0cc9, 0x8cdc9f82),
+     TOBN(0xca5f7fe6, 0xf1c5b1bc), TOBN(0xcc63a682, 0x44929a72),
+     TOBN(0xc7eaba0c, 0x09dbe19a), TOBN(0x2f3585ad, 0x6b5c73c2),
+     TOBN(0x8ab8924b, 0x0ae50c30), TOBN(0x17fcd27a, 0x638b30ba),
+     TOBN(0xaf414d34, 0x10b3d5a5), TOBN(0x09c107d2, 0x2a9accf1),
+     TOBN(0x15dac49f, 0x946a6242), TOBN(0xaec3df2a, 0xd707d642),
+     TOBN(0x2c2492b7, 0x3f894ae0), TOBN(0xf59df3e5, 0xb75f18ce),
+     TOBN(0x7cb740d2, 0x8f53cad0), TOBN(0x3eb585fb, 0xc4f01294),
+     TOBN(0x17da0c86, 0x32c7f717), TOBN(0xeb8c795b, 0xaf943f4c),
+     TOBN(0x4ee23fb5, 0xf67c51d2), TOBN(0xef187575, 0x68889949),
+     TOBN(0xa6b4bdb2, 0x0389168b), TOBN(0xc4ecd258, 0xea577d03),
+     TOBN(0x3a63782b, 0x55743082), TOBN(0x6f678f4c, 0xc72f08cd),
+     TOBN(0x553511cf, 0x65e58dd8), TOBN(0xd53b4e3e, 0xd402c0cd),
+     TOBN(0x37de3e29, 0xa037c14c), TOBN(0x86b6c516, 0xc05712aa),
+     TOBN(0x2834da3e, 0xb38dff6f), TOBN(0xbe012c52, 0xea636be8),
+     TOBN(0x292d238c, 0x61dd37f8), TOBN(0x0e54523f, 0x8f8142db),
+     TOBN(0xe31eb436, 0x036a05d8), TOBN(0x83e3cdff, 0x1e93c0ff),
+     TOBN(0x3fd2fe0f, 0x50821ddf), TOBN(0xc8e19b0d, 0xff9eb33b),
+     TOBN(0xc8cc943f, 0xb569a5fe), TOBN(0xad0090d4, 0xd4342d75),
+     TOBN(0x82090b4b, 0xcaeca000), TOBN(0xca39687f, 0x1bd410eb),
+     TOBN(0xe7bb0df7, 0x65959d77), TOBN(0x39d78218, 0x9c964999),
+     TOBN(0xd87f62e8, 0xb2415451), TOBN(0xe5efb774, 0xbed76108),
+     TOBN(0x3ea011a4, 0xe822f0d0), TOBN(0xbc647ad1, 0x5a8704f8),
+     TOBN(0xbb315b35, 0x50c6820f), TOBN(0x863dec3d, 0xb7e76bec),
+     TOBN(0x01ff5d3a, 0xf017bfc7), TOBN(0x20054439, 0x976b8229),
+     TOBN(0x067fca37, 0x0bbd0d3b), TOBN(0xf63dde64, 0x7f5e3d0f),
+     TOBN(0x22dbefb3, 0x2a4c94e9), TOBN(0xafbff0fe, 0x96f8278a),
+     TOBN(0x80aea0b1, 0x3503793d), TOBN(0xb2238029, 0x5f06cd29),
+     TOBN(0x65703e57, 0x8ec3feca), TOBN(0x06c38314, 0x393e7053),
+     TOBN(0xa0b751eb, 0x7c6734c4), TOBN(0xd2e8a435, 0xc59f0f1e),
+     TOBN(0x147d9052, 0x5e9ca895), TOBN(0x2f4dd31e, 0x972072df),
+     TOBN(0xa16fda8e, 0xe6c6755c), TOBN(0xc66826ff, 0xcf196558),
+     TOBN(0x1f1a76a3, 0x0cf43895), TOBN(0xa9d604e0, 0x83c3097b),
+     TOBN(0xe1908309, 0x66390e0e), TOBN(0xa50bf753, 0xb3c85eff),
+     TOBN(0x0696bdde, 0xf6a70251), TOBN(0x548b801b, 0x3c6ab16a),
+     TOBN(0x37fcf704, 0xa4d08762), TOBN(0x090b3def, 0xdff76c4e),
+     TOBN(0x87e8cb89, 0x69cb9158), TOBN(0x44a90744, 0x995ece43),
+     TOBN(0xf85395f4, 0x0ad9fbf5), TOBN(0x49b0f6c5, 0x4fb0c82d),
+     TOBN(0x75d9bc15, 0xadf7cccf), TOBN(0x81a3e5d6, 0xdfa1e1b0),
+     TOBN(0x8c39e444, 0x249bc17e), TOBN(0xf37dccb2, 0x8ea7fd43),
+     TOBN(0xda654873, 0x907fba12), TOBN(0x35daa6da, 0x4a372904),
+     TOBN(0x0564cfc6, 0x6283a6c5), TOBN(0xd09fa4f6, 0x4a9395bf),
+     TOBN(0x688e9ec9, 0xaeb19a36), TOBN(0xd913f1ce, 0xc7bfbfb4),
+     TOBN(0x797b9a3c, 0x61c2faa6), TOBN(0x2f979bec, 0x6a0a9c12),
+     TOBN(0xb5969d0f, 0x359679ec), TOBN(0xebcf523d, 0x079b0460),
+     TOBN(0xfd6b0008, 0x10fab870), TOBN(0x3f2edcda, 0x9373a39c),
+     TOBN(0x0d64f9a7, 0x6f568431), TOBN(0xf848c27c, 0x02f8898c),
+     TOBN(0xf418ade1, 0x260b5bd5), TOBN(0xc1f3e323, 0x6973dee8),
+     TOBN(0x46e9319c, 0x26c185dd), TOBN(0x6d85b7d8, 0x546f0ac4),
+     TOBN(0x427965f2, 0x247f9d57), TOBN(0xb519b636, 0xb0035f48),
+     TOBN(0x6b6163a9, 0xab87d59c), TOBN(0xff9f58c3, 0x39caaa11),
+     TOBN(0x4ac39cde, 0x3177387b), TOBN(0x5f6557c2, 0x873e77f9),
+     TOBN(0x67504006, 0x36a83041), TOBN(0x9b1c96ca, 0x75ef196c),
+     TOBN(0xf34283de, 0xb08c7940), TOBN(0x7ea09644, 0x1128c316),
+     TOBN(0xb510b3b5, 0x6aa39dff), TOBN(0x59b43da2, 0x9f8e4d8c),
+     TOBN(0xa8ce31fd, 0x9e4c4b9f), TOBN(0x0e20be26, 0xc1303c01),
+     TOBN(0x18187182, 0xe8ee47c9), TOBN(0xd9687cdb, 0x7db98101),
+     TOBN(0x7a520e4d, 0xa1e14ff6), TOBN(0x429808ba, 0x8836d572),
+     TOBN(0xa37ca60d, 0x4944b663), TOBN(0xf901f7a9, 0xa3f91ae5),
+     TOBN(0xe4e3e76e, 0x9e36e3b1), TOBN(0x9aa219cf, 0x29d93250),
+     TOBN(0x347fe275, 0x056a2512), TOBN(0xa4d643d9, 0xde65d95c),
+     TOBN(0x9669d396, 0x699fc3ed), TOBN(0xb598dee2, 0xcf8c6bbe),
+     TOBN(0x682ac1e5, 0xdda9e5c6), TOBN(0x4e0d3c72, 0xcaa9fc95),
+     TOBN(0x17faaade, 0x772bea44), TOBN(0x5ef8428c, 0xab0009c8),
+     TOBN(0xcc4ce47a, 0x460ff016), TOBN(0xda6d12bf, 0x725281cb),
+     TOBN(0x44c67848, 0x0223aad2), TOBN(0x6e342afa, 0x36256e28),
+     TOBN(0x1400bb0b, 0x93a37c04), TOBN(0x62b1bc9b, 0xdd10bd96),
+     TOBN(0x7251adeb, 0x0dac46b7), TOBN(0x7d33b92e, 0x7be4ef51),
+     TOBN(0x28b2a94b, 0xe61fa29a), TOBN(0x4b2be13f, 0x06422233),
+     TOBN(0x36d6d062, 0x330d8d37), TOBN(0x5ef80e1e, 0xb28ca005),
+     TOBN(0x174d4699, 0x6d16768e), TOBN(0x9fc4ff6a, 0x628bf217),
+     TOBN(0x77705a94, 0x154e490d), TOBN(0x9d96dd28, 0x8d2d997a),
+     TOBN(0x77e2d9d8, 0xce5d72c4), TOBN(0x9d06c5a4, 0xc11c714f),
+     TOBN(0x02aa5136, 0x79e4a03e), TOBN(0x1386b3c2, 0x030ff28b),
+     TOBN(0xfe82e8a6, 0xfb283f61), TOBN(0x7df203e5, 0xf3abc3fb),
+     TOBN(0xeec7c351, 0x3a4d3622), TOBN(0xf7d17dbf, 0xdf762761),
+     TOBN(0xc3956e44, 0x522055f0), TOBN(0xde3012db, 0x8fa748db),
+     TOBN(0xca9fcb63, 0xbf1dcc14), TOBN(0xa56d9dcf, 0xbe4e2f3a),
+     TOBN(0xb86186b6, 0x8bcec9c2), TOBN(0x7cf24df9, 0x680b9f06),
+     TOBN(0xc46b45ea, 0xc0d29281), TOBN(0xfff42bc5, 0x07b10e12),
+     TOBN(0x12263c40, 0x4d289427), TOBN(0x3d5f1899, 0xb4848ec4),
+     TOBN(0x11f97010, 0xd040800c), TOBN(0xb4c5f529, 0x300feb20),
+     TOBN(0xcc543f8f, 0xde94fdcb), TOBN(0xe96af739, 0xc7c2f05e),
+     TOBN(0xaa5e0036, 0x882692e1), TOBN(0x09c75b68, 0x950d4ae9),
+     TOBN(0x62f63df2, 0xb5932a7a), TOBN(0x2658252e, 0xde0979ad),
+     TOBN(0x2a19343f, 0xb5e69631), TOBN(0x718c7501, 0x525b666b),
+     TOBN(0x26a42d69, 0xea40dc3a), TOBN(0xdc84ad22, 0xaecc018f),
+     TOBN(0x25c36c7b, 0x3270f04a), TOBN(0x46ba6d47, 0x50fa72ed),
+     TOBN(0x6c37d1c5, 0x93e58a8e), TOBN(0xa2394731, 0x120c088c),
+     TOBN(0xc3be4263, 0xcb6e86da), TOBN(0x2c417d36, 0x7126d038),
+     TOBN(0x5b70f9c5, 0x8b6f8efa), TOBN(0x671a2faa, 0x37718536),
+     TOBN(0xd3ced3c6, 0xb539c92b), TOBN(0xe56f1bd9, 0xa31203c2),
+     TOBN(0x8b096ec4, 0x9ff3c8eb), TOBN(0x2deae432, 0x43491cea),
+     TOBN(0x2465c6eb, 0x17943794), TOBN(0x5d267e66, 0x20586843),
+     TOBN(0x9d3d116d, 0xb07159d0), TOBN(0xae07a67f, 0xc1896210),
+     TOBN(0x8fc84d87, 0xbb961579), TOBN(0x30009e49, 0x1c1f8dd6),
+     TOBN(0x8a8caf22, 0xe3132819), TOBN(0xcffa197c, 0xf23ab4ff),
+     TOBN(0x58103a44, 0x205dd687), TOBN(0x57b796c3, 0x0ded67a2),
+     TOBN(0x0b9c3a6c, 0xa1779ad7), TOBN(0xa33cfe2e, 0x357c09c5),
+     TOBN(0x2ea29315, 0x3db4a57e), TOBN(0x91959695, 0x8ebeb52e),
+     TOBN(0x118db9a6, 0xe546c879), TOBN(0x8e996df4, 0x6295c8d6),
+     TOBN(0xdd990484, 0x55ec806b), TOBN(0x24f291ca, 0x165c1035),
+     TOBN(0xcca523bb, 0x440e2229), TOBN(0x324673a2, 0x73ef4d04),
+     TOBN(0xaf3adf34, 0x3e11ec39), TOBN(0x6136d7f1, 0xdc5968d3),
+     TOBN(0x7a7b2899, 0xb053a927), TOBN(0x3eaa2661, 0xae067ecd),
+     TOBN(0x8549b9c8, 0x02779cd9), TOBN(0x061d7940, 0xc53385ea),
+     TOBN(0x3e0ba883, 0xf06d18bd), TOBN(0x4ba6de53, 0xb2700843),
+     TOBN(0xb966b668, 0x591a9e4d), TOBN(0x93f67567, 0x7f4fa0ed),
+     TOBN(0x5a02711b, 0x4347237b), TOBN(0xbc041e2f, 0xe794608e),
+     TOBN(0x55af10f5, 0x70f73d8c), TOBN(0xd2d4d4f7, 0xbb7564f7),
+     TOBN(0xd7d27a89, 0xb3e93ce7), TOBN(0xf7b5a875, 0x5d3a2c1b),
+     TOBN(0xb29e68a0, 0x255b218a), TOBN(0xb533837e, 0x8af76754),
+     TOBN(0xd1b05a73, 0x579fab2e), TOBN(0xb41055a1, 0xecd74385),
+     TOBN(0xb2369274, 0x445e9115), TOBN(0x2972a7c4, 0xf520274e),
+     TOBN(0x6c08334e, 0xf678e68a), TOBN(0x4e4160f0, 0x99b057ed),
+     TOBN(0x3cfe11b8, 0x52ccb69a), TOBN(0x2fd1823a, 0x21c8f772),
+     TOBN(0xdf7f072f, 0x3298f055), TOBN(0x8c0566f9, 0xfec74a6e),
+     TOBN(0xe549e019, 0x5bb4d041), TOBN(0x7c3930ba, 0x9208d850),
+     TOBN(0xe07141fc, 0xaaa2902b), TOBN(0x539ad799, 0xe4f69ad3),
+     TOBN(0xa6453f94, 0x813f9ffd), TOBN(0xc58d3c48, 0x375bc2f7),
+     TOBN(0xb3326fad, 0x5dc64e96), TOBN(0x3aafcaa9, 0xb240e354),
+     TOBN(0x1d1b0903, 0xaca1e7a9), TOBN(0x4ceb9767, 0x1211b8a0),
+     TOBN(0xeca83e49, 0xe32a858e), TOBN(0x4c32892e, 0xae907bad),
+     TOBN(0xd5b42ab6, 0x2eb9b494), TOBN(0x7fde3ee2, 0x1eabae1b),
+     TOBN(0x13b5ab09, 0xcaf54957), TOBN(0xbfb028be, 0xe5f5d5d5),
+     TOBN(0x928a0650, 0x2003e2c0), TOBN(0x90793aac, 0x67476843),
+     TOBN(0x5e942e79, 0xc81710a0), TOBN(0x557e4a36, 0x27ccadd4),
+     TOBN(0x72a2bc56, 0x4bcf6d0c), TOBN(0x09ee5f43, 0x26d7b80c),
+     TOBN(0x6b70dbe9, 0xd4292f19), TOBN(0x56f74c26, 0x63f16b18),
+     TOBN(0xc23db0f7, 0x35fbb42a), TOBN(0xb606bdf6, 0x6ae10040),
+     TOBN(0x1eb15d4d, 0x044573ac), TOBN(0x7dc3cf86, 0x556b0ba4),
+     TOBN(0x97af9a33, 0xc60df6f7), TOBN(0x0b1ef85c, 0xa716ce8c),
+     TOBN(0x2922f884, 0xc96958be), TOBN(0x7c32fa94, 0x35690963),
+     TOBN(0x2d7f667c, 0xeaa00061), TOBN(0xeaaf7c17, 0x3547365c),
+     TOBN(0x1eb4de46, 0x87032d58), TOBN(0xc54f3d83, 0x5e2c79e0),
+     TOBN(0x07818df4, 0x5d04ef23), TOBN(0x55faa9c8, 0x673d41b4),
+     TOBN(0xced64f6f, 0x89b95355), TOBN(0x4860d2ea, 0xb7415c84),
+     TOBN(0x5fdb9bd2, 0x050ebad3), TOBN(0xdb53e0cc, 0x6685a5bf),
+     TOBN(0xb830c031, 0x9feb6593), TOBN(0xdd87f310, 0x6accff17),
+     TOBN(0x2303ebab, 0x9f555c10), TOBN(0x94603695, 0x287e7065),
+     TOBN(0xf88311c3, 0x2e83358c), TOBN(0x508dd9b4, 0xeefb0178),
+     TOBN(0x7ca23706, 0x2dba8652), TOBN(0x62aac5a3, 0x0047abe5),
+     TOBN(0x9a61d2a0, 0x8b1ea7b3), TOBN(0xd495ab63, 0xae8b1485),
+     TOBN(0x38740f84, 0x87052f99), TOBN(0x178ebe5b, 0xb2974eea),
+     TOBN(0x030bbcca, 0x5b36d17f), TOBN(0xb5e4cce3, 0xaaf86eea),
+     TOBN(0xb51a0220, 0x68f8e9e0), TOBN(0xa4348796, 0x09eb3e75),
+     TOBN(0xbe592309, 0xeef1a752), TOBN(0x5d7162d7, 0x6f2aa1ed),
+     TOBN(0xaebfb5ed, 0x0f007dd2), TOBN(0x255e14b2, 0xc89edd22),
+     TOBN(0xba85e072, 0x0303b697), TOBN(0xc5d17e25, 0xf05720ff),
+     TOBN(0x02b58d6e, 0x5128ebb6), TOBN(0x2c80242d, 0xd754e113),
+     TOBN(0x919fca5f, 0xabfae1ca), TOBN(0x937afaac, 0x1a21459b),
+     TOBN(0x9e0ca91c, 0x1f66a4d2), TOBN(0x194cc7f3, 0x23ec1331),
+     TOBN(0xad25143a, 0x8aa11690), TOBN(0xbe40ad8d, 0x09b59e08),
+     TOBN(0x37d60d9b, 0xe750860a), TOBN(0x6c53b008, 0xc6bf434c),
+     TOBN(0xb572415d, 0x1356eb80), TOBN(0xb8bf9da3, 0x9578ded8),
+     TOBN(0x22658e36, 0x5e8fb38b), TOBN(0x9b70ce22, 0x5af8cb22),
+     TOBN(0x7c00018a, 0x829a8180), TOBN(0x84329f93, 0xb81ed295),
+     TOBN(0x7c343ea2, 0x5f3cea83), TOBN(0x38f8655f, 0x67586536),
+     TOBN(0xa661a0d0, 0x1d3ec517), TOBN(0x98744652, 0x512321ae),
+     TOBN(0x084ca591, 0xeca92598), TOBN(0xa9bb9dc9, 0x1dcb3feb),
+     TOBN(0x14c54355, 0x78b4c240), TOBN(0x5ed62a3b, 0x610cafdc),
+     TOBN(0x07512f37, 0x1b38846b), TOBN(0x571bb70a, 0xb0e38161),
+     TOBN(0xb556b95b, 0x2da705d2), TOBN(0x3ef8ada6, 0xb1a08f98),
+     TOBN(0x85302ca7, 0xddecfbe5), TOBN(0x0e530573, 0x943105cd),
+     TOBN(0x60554d55, 0x21a9255d), TOBN(0x63a32fa1, 0xf2f3802a),
+     TOBN(0x35c8c5b0, 0xcd477875), TOBN(0x97f458ea, 0x6ad42da1),
+     TOBN(0x832d7080, 0xeb6b242d), TOBN(0xd30bd023, 0x3b71e246),
+     TOBN(0x7027991b, 0xbe31139d), TOBN(0x68797e91, 0x462e4e53),
+     TOBN(0x423fe20a, 0x6b4e185a), TOBN(0x82f2c67e, 0x42d9b707),
+     TOBN(0x25c81768, 0x4cf7811b), TOBN(0xbd53005e, 0x045bb95d),}
+    ,
+    {TOBN(0xe5f649be, 0x9d8e68fd), TOBN(0xdb0f0533, 0x1b044320),
+     TOBN(0xf6fde9b3, 0xe0c33398), TOBN(0x92f4209b, 0x66c8cfae),
+     TOBN(0xe9d1afcc, 0x1a739d4b), TOBN(0x09aea75f, 0xa28ab8de),
+     TOBN(0x14375fb5, 0xeac6f1d0), TOBN(0x6420b560, 0x708f7aa5),
+     TOBN(0x9eae499c, 0x6254dc41), TOBN(0x7e293924, 0x7a837e7e),
+     TOBN(0x74aec08c, 0x090524a7), TOBN(0xf82b9219, 0x8d6f55f2),
+     TOBN(0x493c962e, 0x1402cec5), TOBN(0x9f17ca17, 0xfa2f30e7),
+     TOBN(0xbcd783e8, 0xe9b879cb), TOBN(0xea3d8c14, 0x5a6f145f),
+     TOBN(0xdede15e7, 0x5e0dee6e), TOBN(0x74f24872, 0xdc628aa2),
+     TOBN(0xd3e9c4fe, 0x7861bb93), TOBN(0x56d4822a, 0x6187b2e0),
+     TOBN(0xb66417cf, 0xc59826f9), TOBN(0xca260969, 0x2408169e),
+     TOBN(0xedf69d06, 0xc79ef885), TOBN(0x00031f8a, 0xdc7d138f),
+     TOBN(0x103c46e6, 0x0ebcf726), TOBN(0x4482b831, 0x6231470e),
+     TOBN(0x6f6dfaca, 0x487c2109), TOBN(0x2e0ace97, 0x62e666ef),
+     TOBN(0x3246a9d3, 0x1f8d1f42), TOBN(0x1b1e83f1, 0x574944d2),
+     TOBN(0x13dfa63a, 0xa57f334b), TOBN(0x0cf8daed, 0x9f025d81),
+     TOBN(0x30d78ea8, 0x00ee11c1), TOBN(0xeb053cd4, 0xb5e3dd75),
+     TOBN(0x9b65b13e, 0xd58c43c5), TOBN(0xc3ad49bd, 0xbd151663),
+     TOBN(0x99fd8e41, 0xb6427990), TOBN(0x12cf15bd, 0x707eae1e),
+     TOBN(0x29ad4f1b, 0x1aabb71e), TOBN(0x5143e74d, 0x07545d0e),
+     TOBN(0x30266336, 0xc88bdee1), TOBN(0x25f29306, 0x5876767c),
+     TOBN(0x9c078571, 0xc6731996), TOBN(0xc88690b2, 0xed552951),
+     TOBN(0x274f2c2d, 0x852705b4), TOBN(0xb0bf8d44, 0x4e09552d),
+     TOBN(0x7628beeb, 0x986575d1), TOBN(0x407be238, 0x7f864651),
+     TOBN(0x0e5e3049, 0xa639fc6b), TOBN(0xe75c35d9, 0x86003625),
+     TOBN(0x0cf35bd8, 0x5dcc1646), TOBN(0x8bcaced2, 0x6c26273a),
+     TOBN(0xe22ecf1d, 0xb5536742), TOBN(0x013dd897, 0x1a9e068b),
+     TOBN(0x17f411cb, 0x8a7909c5), TOBN(0x5757ac98, 0x861dd506),
+     TOBN(0x85de1f0d, 0x1e935abb), TOBN(0xdefd10b4, 0x154de37a),
+     TOBN(0xb8d9e392, 0x369cebb5), TOBN(0x54d5ef9b, 0x761324be),
+     TOBN(0x4d6341ba, 0x74f17e26), TOBN(0xc0a0e3c8, 0x78c1dde4),
+     TOBN(0xa6d77581, 0x87d918fd), TOBN(0x66876015, 0x02ca3a13),
+     TOBN(0xc7313e9c, 0xf36658f0), TOBN(0xc433ef1c, 0x71f8057e),
+     TOBN(0x85326246, 0x1b6a835a), TOBN(0xc8f05398, 0x7c86394c),
+     TOBN(0xff398cdf, 0xe983c4a1), TOBN(0xbf5e8162, 0x03b7b931),
+     TOBN(0x93193c46, 0xb7b9045b), TOBN(0x1e4ebf5d, 0xa4a6e46b),
+     TOBN(0xf9942a60, 0x43a24fe7), TOBN(0x29c1191e, 0xffb3492b),
+     TOBN(0x9f662449, 0x902fde05), TOBN(0xc792a7ac, 0x6713c32d),
+     TOBN(0x2fd88ad8, 0xb737982c), TOBN(0x7e3a0319, 0xa21e60e3),
+     TOBN(0x09b0de44, 0x7383591a), TOBN(0x6df141ee, 0x8310a456),
+     TOBN(0xaec1a039, 0xe6d6f471), TOBN(0x14b2ba0f, 0x1198d12e),
+     TOBN(0xebc1a160, 0x3aeee5ac), TOBN(0x401f4836, 0xe0b964ce),
+     TOBN(0x2ee43796, 0x4fd03f66), TOBN(0x3fdb4e49, 0xdd8f3f12),
+     TOBN(0x6ef267f6, 0x29380f18), TOBN(0x3e8e9670, 0x8da64d16),
+     TOBN(0xbc19180c, 0x207674f1), TOBN(0x112e09a7, 0x33ae8fdb),
+     TOBN(0x99667554, 0x6aaeb71e), TOBN(0x79432af1, 0xe101b1c7),
+     TOBN(0xd5eb558f, 0xde2ddec6), TOBN(0x81392d1f, 0x5357753f),
+     TOBN(0xa7a76b97, 0x3ae1158a), TOBN(0x416fbbff, 0x4a899991),
+     TOBN(0x9e65fdfd, 0x0d4a9dcf), TOBN(0x7bc29e48, 0x944ddf12),
+     TOBN(0xbc1a92d9, 0x3c856866), TOBN(0x273c6905, 0x6e98dfe2),
+     TOBN(0x69fce418, 0xcdfaa6b8), TOBN(0x606bd823, 0x5061c69f),
+     TOBN(0x42d495a0, 0x6af75e27), TOBN(0x8ed3d505, 0x6d873a1f),
+     TOBN(0xaf552841, 0x6ab25b6a), TOBN(0xc6c0ffc7, 0x2b1a4523),
+     TOBN(0xab18827b, 0x21c99e03), TOBN(0x060e8648, 0x9034691b),
+     TOBN(0x5207f90f, 0x93c7f398), TOBN(0x9f4a96cb, 0x82f8d10b),
+     TOBN(0xdd71cd79, 0x3ad0f9e3), TOBN(0x84f435d2, 0xfc3a54f5),
+     TOBN(0x4b03c55b, 0x8e33787f), TOBN(0xef42f975, 0xa6384673),
+     TOBN(0xff7304f7, 0x5051b9f0), TOBN(0x18aca1dc, 0x741c87c2),
+     TOBN(0x56f120a7, 0x2d4bfe80), TOBN(0xfd823b3d, 0x053e732c),
+     TOBN(0x11bccfe4, 0x7537ca16), TOBN(0xdf6c9c74, 0x1b5a996b),
+     TOBN(0xee7332c7, 0x904fc3fa), TOBN(0x14a23f45, 0xc7e3636a),
+     TOBN(0xc38659c3, 0xf091d9aa), TOBN(0x4a995e5d, 0xb12d8540),
+     TOBN(0x20a53bec, 0xf3a5598a), TOBN(0x56534b17, 0xb1eaa995),
+     TOBN(0x9ed3dca4, 0xbf04e03c), TOBN(0x716c563a, 0xd8d56268),
+     TOBN(0x27ba77a4, 0x1d6178e7), TOBN(0xe4c80c40, 0x68a1ff8e),
+     TOBN(0x75011099, 0x0a13f63d), TOBN(0x7bf33521, 0xa61d46f3),
+     TOBN(0x0aff218e, 0x10b365bb), TOBN(0x81021804, 0x0fd7ea75),
+     TOBN(0x05a3fd8a, 0xa4b3a925), TOBN(0xb829e75f, 0x9b3db4e6),
+     TOBN(0x6bdc75a5, 0x4d53e5fb), TOBN(0x04a5dc02, 0xd52717e3),
+     TOBN(0x86af502f, 0xe9a42ec2), TOBN(0x8867e8fb, 0x2630e382),
+     TOBN(0xbf845c6e, 0xbec9889b), TOBN(0x54f491f2, 0xcb47c98d),
+     TOBN(0xa3091fba, 0x790c2a12), TOBN(0xd7f6fd78, 0xc20f708b),
+     TOBN(0xa569ac30, 0xacde5e17), TOBN(0xd0f996d0, 0x6852b4d7),
+     TOBN(0xe51d4bb5, 0x4609ae54), TOBN(0x3fa37d17, 0x0daed061),
+     TOBN(0x62a88684, 0x34b8fb41), TOBN(0x99a2acbd, 0x9efb64f1),
+     TOBN(0xb75c1a5e, 0x6448e1f2), TOBN(0xfa99951a, 0x42b5a069),
+     TOBN(0x6d956e89, 0x2f3b26e7), TOBN(0xf4709860, 0xda875247),
+     TOBN(0x3ad15179, 0x2482dda3), TOBN(0xd64110e3, 0x017d82f0),
+     TOBN(0x14928d2c, 0xfad414e4), TOBN(0x2b155f58, 0x2ed02b24),
+     TOBN(0x481a141b, 0xcb821bf1), TOBN(0x12e3c770, 0x4f81f5da),
+     TOBN(0xe49c5de5, 0x9fff8381), TOBN(0x11053232, 0x5bbec894),
+     TOBN(0xa0d051cc, 0x454d88c4), TOBN(0x4f6db89c, 0x1f8e531b),
+     TOBN(0x34fe3fd6, 0xca563a44), TOBN(0x7f5c2215, 0x58da8ab9),
+     TOBN(0x8445016d, 0x9474f0a1), TOBN(0x17d34d61, 0xcb7d8a0a),
+     TOBN(0x8e9d3910, 0x1c474019), TOBN(0xcaff2629, 0xd52ceefb),
+     TOBN(0xf9cf3e32, 0xc1622c2b), TOBN(0xd4b95e3c, 0xe9071a05),
+     TOBN(0xfbbca61f, 0x1594438c), TOBN(0x1eb6e6a6, 0x04aadedf),
+     TOBN(0x853027f4, 0x68e14940), TOBN(0x221d322a, 0xdfabda9c),
+     TOBN(0xed8ea9f6, 0xb7cb179a), TOBN(0xdc7b764d, 0xb7934dcc),
+     TOBN(0xfcb13940, 0x5e09180d), TOBN(0x6629a6bf, 0xb47dc2dd),
+     TOBN(0xbfc55e4e, 0x9f5a915e), TOBN(0xb1db9d37, 0x6204441e),
+     TOBN(0xf82d68cf, 0x930c5f53), TOBN(0x17d3a142, 0xcbb605b1),
+     TOBN(0xdd5944ea, 0x308780f2), TOBN(0xdc8de761, 0x3845f5e4),
+     TOBN(0x6beaba7d, 0x7624d7a3), TOBN(0x1e709afd, 0x304df11e),
+     TOBN(0x95364376, 0x02170456), TOBN(0xbf204b3a, 0xc8f94b64),
+     TOBN(0x4e53af7c, 0x5680ca68), TOBN(0x0526074a, 0xe0c67574),
+     TOBN(0x95d8cef8, 0xecd92af6), TOBN(0xe6b9fa7a, 0x6cd1745a),
+     TOBN(0x3d546d3d, 0xa325c3e4), TOBN(0x1f57691d, 0x9ae93aae),
+     TOBN(0xe891f3fe, 0x9d2e1a33), TOBN(0xd430093f, 0xac063d35),
+     TOBN(0xeda59b12, 0x5513a327), TOBN(0xdc2134f3, 0x5536f18f),
+     TOBN(0xaa51fe2c, 0x5c210286), TOBN(0x3f68aaee, 0x1cab658c),
+     TOBN(0x5a23a00b, 0xf9357292), TOBN(0x9a626f39, 0x7efdabed),
+     TOBN(0xfe2b3bf3, 0x199d78e3), TOBN(0xb7a2af77, 0x71bbc345),
+     TOBN(0x3d19827a, 0x1e59802c), TOBN(0x823bbc15, 0xb487a51c),
+     TOBN(0x856139f2, 0x99d0a422), TOBN(0x9ac3df65, 0xf456c6fb),
+     TOBN(0xaddf65c6, 0x701f8bd6), TOBN(0x149f321e, 0x3758df87),
+     TOBN(0xb1ecf714, 0x721b7eba), TOBN(0xe17df098, 0x31a3312a),
+     TOBN(0xdb2fd6ec, 0xd5c4d581), TOBN(0xfd02996f, 0x8fcea1b3),
+     TOBN(0xe29fa63e, 0x7882f14f), TOBN(0xc9f6dc35, 0x07c6cadc),
+     TOBN(0x46f22d6f, 0xb882bed0), TOBN(0x1a45755b, 0xd118e52c),
+     TOBN(0x9f2c7c27, 0x7c4608cf), TOBN(0x7ccbdf32, 0x568012c2),
+     TOBN(0xfcb0aedd, 0x61729b0e), TOBN(0x7ca2ca9e, 0xf7d75dbf),
+     TOBN(0xf58fecb1, 0x6f640f62), TOBN(0xe274b92b, 0x39f51946),
+     TOBN(0x7f4dfc04, 0x6288af44), TOBN(0x0a91f32a, 0xeac329e5),
+     TOBN(0x43ad274b, 0xd6aaba31), TOBN(0x719a1640, 0x0f6884f9),
+     TOBN(0x685d29f6, 0xdaf91e20), TOBN(0x5ec1cc33, 0x27e49d52),
+     TOBN(0x38f4de96, 0x3b54a059), TOBN(0x0e0015e5, 0xefbcfdb3),
+     TOBN(0x177d23d9, 0x4dbb8da6), TOBN(0x98724aa2, 0x97a617ad),
+     TOBN(0x30f0885b, 0xfdb6558e), TOBN(0xf9f7a28a, 0xc7899a96),
+     TOBN(0xd2ae8ac8, 0x872dc112), TOBN(0xfa0642ca, 0x73c3c459),
+     TOBN(0x15296981, 0xe7dfc8d6), TOBN(0x67cd4450, 0x1fb5b94a),
+     TOBN(0x0ec71cf1, 0x0eddfd37), TOBN(0xc7e5eeb3, 0x9a8eddc7),
+     TOBN(0x02ac8e3d, 0x81d95028), TOBN(0x0088f172, 0x70b0e35d),
+     TOBN(0xec041fab, 0xe1881fe3), TOBN(0x62cf71b8, 0xd99e7faa),
+     TOBN(0x5043dea7, 0xe0f222c2), TOBN(0x309d42ac, 0x72e65142),
+     TOBN(0x94fe9ddd, 0x9216cd30), TOBN(0xd6539c7d, 0x0f87feec),
+     TOBN(0x03c5a57c, 0x432ac7d7), TOBN(0x72692cf0, 0x327fda10),
+     TOBN(0xec28c85f, 0x280698de), TOBN(0x2331fb46, 0x7ec283b1),
+     TOBN(0xd34bfa32, 0x2867e633), TOBN(0x78709a82, 0x0a9cc815),
+     TOBN(0xb7fe6964, 0x875e2fa5), TOBN(0x25cc064f, 0x9e98bfb5),
+     TOBN(0x9eb0151c, 0x493a65c5), TOBN(0x5fb5d941, 0x53182464),
+     TOBN(0x69e6f130, 0xf04618e2), TOBN(0xa8ecec22, 0xf89c8ab6),
+     TOBN(0xcd6ac88b, 0xb96209bd), TOBN(0x65fa8cdb, 0xb3e1c9e0),
+     TOBN(0xa47d22f5, 0x4a8d8eac), TOBN(0x83895cdf, 0x8d33f963),
+     TOBN(0xa8adca59, 0xb56cd3d1), TOBN(0x10c8350b, 0xdaf38232),
+     TOBN(0x2b161fb3, 0xa5080a9f), TOBN(0xbe7f5c64, 0x3af65b3a),
+     TOBN(0x2c754039, 0x97403a11), TOBN(0x94626cf7, 0x121b96af),
+     TOBN(0x431de7c4, 0x6a983ec2), TOBN(0x3780dd3a, 0x52cc3df7),
+     TOBN(0xe28a0e46, 0x2baf8e3b), TOBN(0xabe68aad, 0x51d299ae),
+     TOBN(0x603eb8f9, 0x647a2408), TOBN(0x14c61ed6, 0x5c750981),
+     TOBN(0x88b34414, 0xc53352e7), TOBN(0x5a34889c, 0x1337d46e),
+     TOBN(0x612c1560, 0xf95f2bc8), TOBN(0x8a3f8441, 0xd4807a3a),
+     TOBN(0x680d9e97, 0x5224da68), TOBN(0x60cd6e88, 0xc3eb00e9),
+     TOBN(0x3875a98e, 0x9a6bc375), TOBN(0xdc80f924, 0x4fd554c2),
+     TOBN(0x6c4b3415, 0x6ac77407), TOBN(0xa1e5ea8f, 0x25420681),
+     TOBN(0x541bfa14, 0x4607a458), TOBN(0x5dbc7e7a, 0x96d7fbf9),
+     TOBN(0x646a851b, 0x31590a47), TOBN(0x039e85ba, 0x15ee6df8),
+     TOBN(0xd19fa231, 0xd7b43fc0), TOBN(0x84bc8be8, 0x299a0e04),
+     TOBN(0x2b9d2936, 0xf20df03a), TOBN(0x24054382, 0x8608d472),
+     TOBN(0x76b6ba04, 0x9149202a), TOBN(0xb21c3831, 0x3670e7b7),
+     TOBN(0xddd93059, 0xd6fdee10), TOBN(0x9da47ad3, 0x78488e71),
+     TOBN(0x99cc1dfd, 0xa0fcfb25), TOBN(0x42abde10, 0x64696954),
+     TOBN(0x14cc15fc, 0x17eab9fe), TOBN(0xd6e863e4, 0xd3e70972),
+     TOBN(0x29a7765c, 0x6432112c), TOBN(0x88660001, 0x5b0774d8),
+     TOBN(0x3729175a, 0x2c088eae), TOBN(0x13afbcae, 0x8230b8d4),
+     TOBN(0x44768151, 0x915f4379), TOBN(0xf086431a, 0xd8d22812),
+     TOBN(0x37461955, 0xc298b974), TOBN(0x905fb5f0, 0xf8711e04),
+     TOBN(0x787abf3a, 0xfe969d18), TOBN(0x392167c2, 0x6f6a494e),
+     TOBN(0xfc7a0d2d, 0x28c511da), TOBN(0xf127c7dc, 0xb66a262d),
+     TOBN(0xf9c4bb95, 0xfd63fdf0), TOBN(0x90016589, 0x3913ef46),
+     TOBN(0x74d2a73c, 0x11aa600d), TOBN(0x2f5379bd, 0x9fb5ab52),
+     TOBN(0xe49e53a4, 0x7fb70068), TOBN(0x68dd39e5, 0x404aa9a7),
+     TOBN(0xb9b0cf57, 0x2ecaa9c3), TOBN(0xba0e103b, 0xe824826b),
+     TOBN(0x60c2198b, 0x4631a3c4), TOBN(0xc5ff84ab, 0xfa8966a2),
+     TOBN(0x2d6ebe22, 0xac95aff8), TOBN(0x1c9bb6db, 0xb5a46d09),
+     TOBN(0x419062da, 0x53ee4f8d), TOBN(0x7b9042d0, 0xbb97efef),
+     TOBN(0x0f87f080, 0x830cf6bd), TOBN(0x4861d19a, 0x6ec8a6c6),
+     TOBN(0xd3a0daa1, 0x202f01aa), TOBN(0xb0111674, 0xf25afbd5),
+     TOBN(0x6d00d6cf, 0x1afb20d9), TOBN(0x13695000, 0x40671bc5),
+     TOBN(0x913ab0dc, 0x2485ea9b), TOBN(0x1f2bed06, 0x9eef61ac),
+     TOBN(0x850c8217, 0x6d799e20), TOBN(0x93415f37, 0x3271c2de),
+     TOBN(0x5afb06e9, 0x6c4f5910), TOBN(0x688a52df, 0xc4e9e421),
+     TOBN(0x30495ba3, 0xe2a9a6db), TOBN(0x4601303d, 0x58f9268b),
+     TOBN(0xbe3b0dad, 0x7eb0f04f), TOBN(0x4ea47250, 0x4456936d),
+     TOBN(0x8caf8798, 0xd33fd3e7), TOBN(0x1ccd8a89, 0xeb433708),
+     TOBN(0x9effe3e8, 0x87fd50ad), TOBN(0xbe240a56, 0x6b29c4df),
+     TOBN(0xec4ffd98, 0xca0e7ebd), TOBN(0xf586783a, 0xe748616e),
+     TOBN(0xa5b00d8f, 0xc77baa99), TOBN(0x0acada29, 0xb4f34c9c),
+     TOBN(0x36dad67d, 0x0fe723ac), TOBN(0x1d8e53a5, 0x39c36c1e),
+     TOBN(0xe4dd342d, 0x1f4bea41), TOBN(0x64fd5e35, 0xebc9e4e0),
+     TOBN(0x96f01f90, 0x57908805), TOBN(0xb5b9ea3d, 0x5ed480dd),
+     TOBN(0x366c5dc2, 0x3efd2dd0), TOBN(0xed2fe305, 0x6e9dfa27),
+     TOBN(0x4575e892, 0x6e9197e2), TOBN(0x11719c09, 0xab502a5d),
+     TOBN(0x264c7bec, 0xe81f213f), TOBN(0x741b9241, 0x55f5c457),
+     TOBN(0x78ac7b68, 0x49a5f4f4), TOBN(0xf91d70a2, 0x9fc45b7d),
+     TOBN(0x39b05544, 0xb0f5f355), TOBN(0x11f06bce, 0xeef930d9),
+     TOBN(0xdb84d25d, 0x038d05e1), TOBN(0x04838ee5, 0xbacc1d51),
+     TOBN(0x9da3ce86, 0x9e8ee00b), TOBN(0xc3412057, 0xc36eda1f),
+     TOBN(0xae80b913, 0x64d9c2f4), TOBN(0x7468bac3, 0xa010a8ff),
+     TOBN(0xdfd20037, 0x37359d41), TOBN(0x1a0f5ab8, 0x15efeacc),
+     TOBN(0x7c25ad2f, 0x659d0ce0), TOBN(0x4011bcbb, 0x6785cff1),
+     TOBN(0x128b9912, 0x7e2192c7), TOBN(0xa549d8e1, 0x13ccb0e8),
+     TOBN(0x805588d8, 0xc85438b1), TOBN(0x5680332d, 0xbc25cb27),
+     TOBN(0xdcd1bc96, 0x1a4bfdf4), TOBN(0x779ff428, 0x706f6566),
+     TOBN(0x8bbee998, 0xf059987a), TOBN(0xf6ce8cf2, 0xcc686de7),
+     TOBN(0xf8ad3c4a, 0x953cfdb2), TOBN(0xd1d426d9, 0x2205da36),
+     TOBN(0xb3c0f13f, 0xc781a241), TOBN(0x3e89360e, 0xd75362a8),
+     TOBN(0xccd05863, 0xc8a91184), TOBN(0x9bd0c9b7, 0xefa8a7f4),
+     TOBN(0x97ee4d53, 0x8a912a4b), TOBN(0xde5e15f8, 0xbcf518fd),
+     TOBN(0x6a055bf8, 0xc467e1e0), TOBN(0x10be4b4b, 0x1587e256),
+     TOBN(0xd90c14f2, 0x668621c9), TOBN(0xd5518f51, 0xab9c92c1),
+     TOBN(0x8e6a0100, 0xd6d47b3c), TOBN(0xcbe980dd, 0x66716175),
+     TOBN(0x500d3f10, 0xddd83683), TOBN(0x3b6cb35d, 0x99cac73c),
+     TOBN(0x53730c8b, 0x6083d550), TOBN(0xcf159767, 0xdf0a1987),
+     TOBN(0x84bfcf53, 0x43ad73b3), TOBN(0x1b528c20, 0x4f035a94),
+     TOBN(0x4294edf7, 0x33eeac69), TOBN(0xb6283e83, 0x817f3240),
+     TOBN(0xc3fdc959, 0x0a5f25b1), TOBN(0xefaf8aa5, 0x5844ee22),
+     TOBN(0xde269ba5, 0xdbdde4de), TOBN(0xe3347160, 0xc56133bf),
+     TOBN(0xc1184219, 0x8d9ea9f8), TOBN(0x090de5db, 0xf3fc1ab5),
+     TOBN(0x404c37b1, 0x0bf22cda), TOBN(0x7de20ec8, 0xf5618894),
+     TOBN(0x754c588e, 0xecdaecab), TOBN(0x6ca4b0ed, 0x88342743),
+     TOBN(0x76f08bdd, 0xf4a938ec), TOBN(0xd182de89, 0x91493ccb),
+     TOBN(0xd652c53e, 0xc8a4186a), TOBN(0xb3e878db, 0x946d8e33),
+     TOBN(0x088453c0, 0x5f37663c), TOBN(0x5cd9daaa, 0xb407748b),
+     TOBN(0xa1f5197f, 0x586d5e72), TOBN(0x47500be8, 0xc443ca59),
+     TOBN(0x78ef35b2, 0xe2652424), TOBN(0x09c5d26f, 0x6dd7767d),
+     TOBN(0x7175a79a, 0xa74d3f7b), TOBN(0x0428fd8d, 0xcf5ea459),
+     TOBN(0x511cb97c, 0xa5d1746d), TOBN(0x36363939, 0xe71d1278),
+     TOBN(0xcf2df955, 0x10350bf4), TOBN(0xb3817439, 0x60aae782),
+     TOBN(0xa748c0e4, 0x3e688809), TOBN(0x98021fbf, 0xd7a5a006),
+     TOBN(0x9076a70c, 0x0e367a98), TOBN(0xbea1bc15, 0x0f62b7c2),
+     TOBN(0x2645a68c, 0x30fe0343), TOBN(0xacaffa78, 0x699dc14f),
+     TOBN(0xf4469964, 0x457bf9c4), TOBN(0x0db6407b, 0x0d2ead83),
+     TOBN(0x68d56cad, 0xb2c6f3eb), TOBN(0x3b512e73, 0xf376356c),
+     TOBN(0xe43b0e1f, 0xfce10408), TOBN(0x89ddc003, 0x5a5e257d),
+     TOBN(0xb0ae0d12, 0x0362e5b3), TOBN(0x07f983c7, 0xb0519161),
+     TOBN(0xc2e94d15, 0x5d5231e7), TOBN(0xcff22aed, 0x0b4f9513),
+     TOBN(0xb02588dd, 0x6ad0b0b5), TOBN(0xb967d1ac, 0x11d0dcd5),
+     TOBN(0x8dac6bc6, 0xcf777b6c), TOBN(0x0062bdbd, 0x4c6d1959),
+     TOBN(0x53da71b5, 0x0ef5cc85), TOBN(0x07012c7d, 0x4006f14f),
+     TOBN(0x4617f962, 0xac47800d), TOBN(0x53365f2b, 0xc102ed75),
+     TOBN(0xb422efcb, 0x4ab8c9d3), TOBN(0x195cb26b, 0x34af31c9),
+     TOBN(0x3a926e29, 0x05f2c4ce), TOBN(0xbd2bdecb, 0x9856966c),
+     TOBN(0x5d16ab3a, 0x85527015), TOBN(0x9f81609e, 0x4486c231),
+     TOBN(0xd8b96b2c, 0xda350002), TOBN(0xbd054690, 0xfa1b7d36),
+     TOBN(0xdc90ebf5, 0xe71d79bc), TOBN(0xf241b6f9, 0x08964e4e),
+     TOBN(0x7c838643, 0x2fe3cd4c), TOBN(0xe0f33acb, 0xb4bc633c),
+     TOBN(0xb4a9ecec, 0x3d139f1f), TOBN(0x05ce69cd, 0xdc4a1f49),
+     TOBN(0xa19d1b16, 0xf5f98aaf), TOBN(0x45bb71d6, 0x6f23e0ef),
+     TOBN(0x33789fcd, 0x46cdfdd3), TOBN(0x9b8e2978, 0xcee040ca),
+     TOBN(0x9c69b246, 0xae0a6828), TOBN(0xba533d24, 0x7078d5aa),
+     TOBN(0x7a2e42c0, 0x7bb4fbdb), TOBN(0xcfb4879a, 0x7035385c),
+     TOBN(0x8c3dd30b, 0x3281705b), TOBN(0x7e361c6c, 0x404fe081),
+     TOBN(0x7b21649c, 0x3f604edf), TOBN(0x5dbf6a3f, 0xe52ffe47),
+     TOBN(0xc41b7c23, 0x4b54d9bf), TOBN(0x1374e681, 0x3511c3d9),
+     TOBN(0x1863bf16, 0xc1b2b758), TOBN(0x90e78507, 0x1e9e6a96),
+     TOBN(0xab4bf98d, 0x5d86f174), TOBN(0xd74e0bd3, 0x85e96fe4),
+     TOBN(0x8afde39f, 0xcac5d344), TOBN(0x90946dbc, 0xbd91b847),
+     TOBN(0xf5b42358, 0xfe1a838c), TOBN(0x05aae6c5, 0x620ac9d8),
+     TOBN(0x8e193bd8, 0xa1ce5a0b), TOBN(0x8f710571, 0x4dabfd72),
+     TOBN(0x8d8fdd48, 0x182caaac), TOBN(0x8c4aeefa, 0x040745cf),
+     TOBN(0x73c6c30a, 0xf3b93e6d), TOBN(0x991241f3, 0x16f42011),
+     TOBN(0xa0158eea, 0xe457a477), TOBN(0xd19857db, 0xee6ddc05),
+     TOBN(0xb3265224, 0x18c41671), TOBN(0x3ffdfc7e, 0x3c2c0d58),
+     TOBN(0x3a3a5254, 0x26ee7cda), TOBN(0x341b0869, 0xdf02c3a8),
+     TOBN(0xa023bf42, 0x723bbfc8), TOBN(0x3d15002a, 0x14452691),}
+    ,
+    {TOBN(0x5ef7324c, 0x85edfa30), TOBN(0x25976554, 0x87d4f3da),
+     TOBN(0x352f5bc0, 0xdcb50c86), TOBN(0x8f6927b0, 0x4832a96c),
+     TOBN(0xd08ee1ba, 0x55f2f94c), TOBN(0x6a996f99, 0x344b45fa),
+     TOBN(0xe133cb8d, 0xa8aa455d), TOBN(0x5d0721ec, 0x758dc1f7),
+     TOBN(0x6ba7a920, 0x79e5fb67), TOBN(0xe1331feb, 0x70aa725e),
+     TOBN(0x5080ccf5, 0x7df5d837), TOBN(0xe4cae01d, 0x7ff72e21),
+     TOBN(0xd9243ee6, 0x0412a77d), TOBN(0x06ff7cac, 0xdf449025),
+     TOBN(0xbe75f7cd, 0x23ef5a31), TOBN(0xbc957822, 0x0ddef7a8),
+     TOBN(0x8cf7230c, 0xb0ce1c55), TOBN(0x5b534d05, 0x0bbfb607),
+     TOBN(0xee1ef113, 0x0e16363b), TOBN(0x27e0aa7a, 0xb4999e82),
+     TOBN(0xce1dac2d, 0x79362c41), TOBN(0x67920c90, 0x91bb6cb0),
+     TOBN(0x1e648d63, 0x2223df24), TOBN(0x0f7d9eef, 0xe32e8f28),
+     TOBN(0x6943f39a, 0xfa833834), TOBN(0x22951722, 0xa6328562),
+     TOBN(0x81d63dd5, 0x4170fc10), TOBN(0x9f5fa58f, 0xaecc2e6d),
+     TOBN(0xb66c8725, 0xe77d9a3b), TOBN(0x11235cea, 0x6384ebe0),
+     TOBN(0x06a8c118, 0x5845e24a), TOBN(0x0137b286, 0xebd093b1),
+     TOBN(0xc589e1ce, 0x44ace150), TOBN(0xe0f8d3d9, 0x4381e97c),
+     TOBN(0x59e99b11, 0x62c5a4b8), TOBN(0x90d262f7, 0xfd0ec9f9),
+     TOBN(0xfbc854c9, 0x283e13c9), TOBN(0x2d04fde7, 0xaedc7085),
+     TOBN(0x057d7765, 0x47dcbecb), TOBN(0x8dbdf591, 0x9a76fa5f),
+     TOBN(0xd0150695, 0x0de1e578), TOBN(0x2e1463e7, 0xe9f72bc6),
+     TOBN(0xffa68441, 0x1b39eca5), TOBN(0x673c8530, 0x7c037f2f),
+     TOBN(0xd0d6a600, 0x747f91da), TOBN(0xb08d43e1, 0xc9cb78e9),
+     TOBN(0x0fc0c644, 0x27b5cef5), TOBN(0x5c1d160a, 0xa60a2fd6),
+     TOBN(0xf98cae53, 0x28c8e13b), TOBN(0x375f10c4, 0xb2eddcd1),
+     TOBN(0xd4eb8b7f, 0x5cce06ad), TOBN(0xb4669f45, 0x80a2e1ef),
+     TOBN(0xd593f9d0, 0x5bbd8699), TOBN(0x5528a4c9, 0xe7976d13),
+     TOBN(0x3923e095, 0x1c7e28d3), TOBN(0xb9293790, 0x3f6bb577),
+     TOBN(0xdb567d6a, 0xc42bd6d2), TOBN(0x6df86468, 0xbb1f96ae),
+     TOBN(0x0efe5b1a, 0x4843b28e), TOBN(0x961bbb05, 0x6379b240),
+     TOBN(0xb6caf5f0, 0x70a6a26b), TOBN(0x70686c0d, 0x328e6e39),
+     TOBN(0x80da06cf, 0x895fc8d3), TOBN(0x804d8810, 0xb363fdc9),
+     TOBN(0xbe22877b, 0x207f1670), TOBN(0x9b0dd188, 0x4e615291),
+     TOBN(0x625ae8dc, 0x97a3c2bf), TOBN(0x08584ef7, 0x439b86e8),
+     TOBN(0xde7190a5, 0xdcd898ff), TOBN(0x26286c40, 0x2058ee3d),
+     TOBN(0x3db0b217, 0x5f87b1c1), TOBN(0xcc334771, 0x102a6db5),
+     TOBN(0xd99de954, 0x2f770fb1), TOBN(0x97c1c620, 0x4cd7535e),
+     TOBN(0xd3b6c448, 0x3f09cefc), TOBN(0xd725af15, 0x5a63b4f8),
+     TOBN(0x0c95d24f, 0xc01e20ec), TOBN(0xdfd37494, 0x9ae7121f),
+     TOBN(0x7d6ddb72, 0xec77b7ec), TOBN(0xfe079d3b, 0x0353a4ae),
+     TOBN(0x3066e70a, 0x2e6ac8d2), TOBN(0x9c6b5a43, 0x106e5c05),
+     TOBN(0x52d3c6f5, 0xede59b8c), TOBN(0x30d6a5c3, 0xfccec9ae),
+     TOBN(0xedec7c22, 0x4fc0a9ef), TOBN(0x190ff083, 0x95c16ced),
+     TOBN(0xbe12ec8f, 0x94de0fde), TOBN(0x0d131ab8, 0x852d3433),
+     TOBN(0x42ace07e, 0x85701291), TOBN(0x94793ed9, 0x194061a8),
+     TOBN(0x30e83ed6, 0xd7f4a485), TOBN(0x9eec7269, 0xf9eeff4d),
+     TOBN(0x90acba59, 0x0c9d8005), TOBN(0x5feca458, 0x1e79b9d1),
+     TOBN(0x8fbe5427, 0x1d506a1e), TOBN(0xa32b2c8e, 0x2439cfa7),
+     TOBN(0x1671c173, 0x73dd0b4e), TOBN(0x37a28214, 0x44a054c6),
+     TOBN(0x81760a1b, 0x4e8b53f1), TOBN(0xa6c04224, 0xf9f93b9e),
+     TOBN(0x18784b34, 0xcf671e3c), TOBN(0x81bbecd2, 0xcda9b994),
+     TOBN(0x38831979, 0xb2ab3848), TOBN(0xef54feb7, 0xf2e03c2d),
+     TOBN(0xcf197ca7, 0xfb8088fa), TOBN(0x01427247, 0x4ddc96c5),
+     TOBN(0xa2d2550a, 0x30777176), TOBN(0x53469898, 0x4d0cf71d),
+     TOBN(0x6ce937b8, 0x3a2aaac6), TOBN(0xe9f91dc3, 0x5af38d9b),
+     TOBN(0x2598ad83, 0xc8bf2899), TOBN(0x8e706ac9, 0xb5536c16),
+     TOBN(0x40dc7495, 0xf688dc98), TOBN(0x26490cd7, 0x124c4afc),
+     TOBN(0xe651ec84, 0x1f18775c), TOBN(0x393ea6c3, 0xb4fdaf4a),
+     TOBN(0x1e1f3343, 0x7f338e0d), TOBN(0x39fb832b, 0x6053e7b5),
+     TOBN(0x46e702da, 0x619e14d5), TOBN(0x859cacd1, 0xcdeef6e0),
+     TOBN(0x63b99ce7, 0x4462007d), TOBN(0xb8ab48a5, 0x4cb5f5b7),
+     TOBN(0x9ec673d2, 0xf55edde7), TOBN(0xd1567f74, 0x8cfaefda),
+     TOBN(0x46381b6b, 0x0887bcec), TOBN(0x694497ce, 0xe178f3c2),
+     TOBN(0x5e6525e3, 0x1e6266cb), TOBN(0x5931de26, 0x697d6413),
+     TOBN(0x87f8df7c, 0x0e58d493), TOBN(0xb1ae5ed0, 0x58b73f12),
+     TOBN(0xc368f784, 0xdea0c34d), TOBN(0x9bd0a120, 0x859a91a0),
+     TOBN(0xb00d88b7, 0xcc863c68), TOBN(0x3a1cc11e, 0x3d1f4d65),
+     TOBN(0xea38e0e7, 0x0aa85593), TOBN(0x37f13e98, 0x7dc4aee8),
+     TOBN(0x10d38667, 0xbc947bad), TOBN(0x738e07ce, 0x2a36ee2e),
+     TOBN(0xc93470cd, 0xc577fcac), TOBN(0xdee1b616, 0x2782470d),
+     TOBN(0x36a25e67, 0x2e793d12), TOBN(0xd6aa6cae, 0xe0f186da),
+     TOBN(0x474d0fd9, 0x80e07af7), TOBN(0xf7cdc47d, 0xba8a5cd4),
+     TOBN(0x28af6d9d, 0xab15247f), TOBN(0x7c789c10, 0x493a537f),
+     TOBN(0x7ac9b110, 0x23a334e7), TOBN(0x0236ac09, 0x12c9c277),
+     TOBN(0xa7e5bd25, 0x1d7a5144), TOBN(0x098b9c2a, 0xf13ec4ec),
+     TOBN(0x3639daca, 0xd3f0abca), TOBN(0x642da81a, 0xa23960f9),
+     TOBN(0x7d2e5c05, 0x4f7269b1), TOBN(0xfcf30777, 0xe287c385),
+     TOBN(0x10edc84f, 0xf2a46f21), TOBN(0x35441757, 0x4f43fa36),
+     TOBN(0xf1327899, 0xfd703431), TOBN(0xa438d7a6, 0x16dd587a),
+     TOBN(0x65c34c57, 0xe9c8352d), TOBN(0xa728edab, 0x5cc5a24e),
+     TOBN(0xaed78abc, 0x42531689), TOBN(0x0a51a0e8, 0x010963ef),
+     TOBN(0x5776fa0a, 0xd717d9b3), TOBN(0xf356c239, 0x7dd3428b),
+     TOBN(0x29903fff, 0x8d3a3dac), TOBN(0x409597fa, 0x3d94491f),
+     TOBN(0x4cd7a5ff, 0xbf4a56a4), TOBN(0xe5096474, 0x8adab462),
+     TOBN(0xa97b5126, 0x5c3427b0), TOBN(0x6401405c, 0xd282c9bd),
+     TOBN(0x3629f8d7, 0x222c5c45), TOBN(0xb1c02c16, 0xe8d50aed),
+     TOBN(0xbea2ed75, 0xd9635bc9), TOBN(0x226790c7, 0x6e24552f),
+     TOBN(0x3c33f2a3, 0x65f1d066), TOBN(0x2a43463e, 0x6dfccc2e),
+     TOBN(0x8cc3453a, 0xdb483761), TOBN(0xe7cc6085, 0x65d5672b),
+     TOBN(0x277ed6cb, 0xde3efc87), TOBN(0x19f2f368, 0x69234eaf),
+     TOBN(0x9aaf4317, 0x5c0b800b), TOBN(0x1f1e7c89, 0x8b6da6e2),
+     TOBN(0x6cfb4715, 0xb94ec75e), TOBN(0xd590dd5f, 0x453118c2),
+     TOBN(0x14e49da1, 0x1f17a34c), TOBN(0x5420ab39, 0x235a1456),
+     TOBN(0xb7637241, 0x2f50363b), TOBN(0x7b15d623, 0xc3fabb6e),
+     TOBN(0xa0ef40b1, 0xe274e49c), TOBN(0x5cf50744, 0x96b1860a),
+     TOBN(0xd6583fbf, 0x66afe5a4), TOBN(0x44240510, 0xf47e3e9a),
+     TOBN(0x99254343, 0x11b2d595), TOBN(0xf1367499, 0xeec8df57),
+     TOBN(0x3cb12c61, 0x3e73dd05), TOBN(0xd248c033, 0x7dac102a),
+     TOBN(0xcf154f13, 0xa77739f5), TOBN(0xbf4288cb, 0x23d2af42),
+     TOBN(0xaa64c9b6, 0x32e4a1cf), TOBN(0xee8c07a8, 0xc8a208f3),
+     TOBN(0xe10d4999, 0x6fe8393f), TOBN(0x0f809a3f, 0xe91f3a32),
+     TOBN(0x61096d1c, 0x802f63c8), TOBN(0x289e1462, 0x57750d3d),
+     TOBN(0xed06167e, 0x9889feea), TOBN(0xd5c9c0e2, 0xe0993909),
+     TOBN(0x46fca0d8, 0x56508ac6), TOBN(0x91826047, 0x4f1b8e83),
+     TOBN(0x4f2c877a, 0x9a4a2751), TOBN(0x71bd0072, 0xcae6fead),
+     TOBN(0x38df8dcc, 0x06aa1941), TOBN(0x5a074b4c, 0x63beeaa8),
+     TOBN(0xd6d65934, 0xc1cec8ed), TOBN(0xa6ecb49e, 0xaabc03bd),
+     TOBN(0xaade91c2, 0xde8a8415), TOBN(0xcfb0efdf, 0x691136e0),
+     TOBN(0x11af45ee, 0x23ab3495), TOBN(0xa132df88, 0x0b77463d),
+     TOBN(0x8923c15c, 0x815d06f4), TOBN(0xc3ceb3f5, 0x0d61a436),
+     TOBN(0xaf52291d, 0xe88fb1da), TOBN(0xea057974, 0x1da12179),
+     TOBN(0xb0d7218c, 0xd2fef720), TOBN(0x6c0899c9, 0x8e1d8845),
+     TOBN(0x98157504, 0x752ddad7), TOBN(0xd60bd74f, 0xa1a68a97),
+     TOBN(0x7047a3a9, 0xf658fb99), TOBN(0x1f5d86d6, 0x5f8511e4),
+     TOBN(0xb8a4bc42, 0x4b5a6d88), TOBN(0x69eb2c33, 0x1abefa7d),
+     TOBN(0x95bf39e8, 0x13c9c510), TOBN(0xf571960a, 0xd48aab43),
+     TOBN(0x7e8cfbcf, 0x704e23c6), TOBN(0xc71b7d22, 0x28aaa65b),
+     TOBN(0xa041b2bd, 0x245e3c83), TOBN(0x69b98834, 0xd21854ff),
+     TOBN(0x89d227a3, 0x963bfeec), TOBN(0x99947aaa, 0xde7da7cb),
+     TOBN(0x1d9ee9db, 0xee68a9b1), TOBN(0x0a08f003, 0x698ec368),
+     TOBN(0xe9ea4094, 0x78ef2487), TOBN(0xc8d2d415, 0x02cfec26),
+     TOBN(0xc52f9a6e, 0xb7dcf328), TOBN(0x0ed489e3, 0x85b6a937),
+     TOBN(0x9b94986b, 0xbef3366e), TOBN(0x0de59c70, 0xedddddb8),
+     TOBN(0xffdb748c, 0xeadddbe2), TOBN(0x9b9784bb, 0x8266ea40),
+     TOBN(0x142b5502, 0x1a93507a), TOBN(0xb4cd1187, 0x8d3c06cf),
+     TOBN(0xdf70e76a, 0x91ec3f40), TOBN(0x484e81ad, 0x4e7553c2),
+     TOBN(0x830f87b5, 0x272e9d6e), TOBN(0xea1c93e5, 0xc6ff514a),
+     TOBN(0x67cc2adc, 0xc4192a8e), TOBN(0xc77e27e2, 0x42f4535a),
+     TOBN(0x9cdbab36, 0xd2b713c5), TOBN(0x86274ea0, 0xcf7b0cd3),
+     TOBN(0x784680f3, 0x09af826b), TOBN(0xbfcc837a, 0x0c72dea3),
+     TOBN(0xa8bdfe9d, 0xd6529b73), TOBN(0x708aa228, 0x63a88002),
+     TOBN(0x6c7a9a54, 0xc91d45b9), TOBN(0xdf1a38bb, 0xfd004f56),
+     TOBN(0x2e8c9a26, 0xb8bad853), TOBN(0x2d52cea3, 0x3723eae7),
+     TOBN(0x054d6d81, 0x56ca2830), TOBN(0xa3317d14, 0x9a8dc411),
+     TOBN(0xa08662fe, 0xfd4ddeda), TOBN(0xed2a153a, 0xb55d792b),
+     TOBN(0x7035c16a, 0xbfc6e944), TOBN(0xb6bc5834, 0x00171cf3),
+     TOBN(0xe27152b3, 0x83d102b6), TOBN(0xfe695a47, 0x0646b848),
+     TOBN(0xa5bb09d8, 0x916e6d37), TOBN(0xb4269d64, 0x0d17015e),
+     TOBN(0x8d8156a1, 0x0a1d2285), TOBN(0xfeef6c51, 0x46d26d72),
+     TOBN(0x9dac57c8, 0x4c5434a7), TOBN(0x0282e5be, 0x59d39e31),
+     TOBN(0xedfff181, 0x721c486d), TOBN(0x301baf10, 0xbc58824e),
+     TOBN(0x8136a6aa, 0x00570031), TOBN(0x55aaf78c, 0x1cddde68),
+     TOBN(0x26829371, 0x59c63952), TOBN(0x3a3bd274, 0x8bc25baf),
+     TOBN(0xecdf8657, 0xb7e52dc3), TOBN(0x2dd8c087, 0xfd78e6c8),
+     TOBN(0x20553274, 0xf5531461), TOBN(0x8b4a1281, 0x5d95499b),
+     TOBN(0xe2c8763a, 0x1a80f9d2), TOBN(0xd1dbe32b, 0x4ddec758),
+     TOBN(0xaf12210d, 0x30c34169), TOBN(0xba74a953, 0x78baa533),
+     TOBN(0x3d133c6e, 0xa438f254), TOBN(0xa431531a, 0x201bef5b),
+     TOBN(0x15295e22, 0xf669d7ec), TOBN(0xca374f64, 0x357fb515),
+     TOBN(0x8a8406ff, 0xeaa3fdb3), TOBN(0x106ae448, 0xdf3f2da8),
+     TOBN(0x8f9b0a90, 0x33c8e9a1), TOBN(0x234645e2, 0x71ad5885),
+     TOBN(0x3d083224, 0x1c0aed14), TOBN(0xf10a7d3e, 0x7a942d46),
+     TOBN(0x7c11deee, 0x40d5c9be), TOBN(0xb2bae7ff, 0xba84ed98),
+     TOBN(0x93e97139, 0xaad58ddd), TOBN(0x3d872796, 0x3f6d1fa3),
+     TOBN(0x483aca81, 0x8569ff13), TOBN(0x8b89a5fb, 0x9a600f72),
+     TOBN(0x4cbc27c3, 0xc06f2b86), TOBN(0x22130713, 0x63ad9c0b),
+     TOBN(0xb5358b1e, 0x48ac2840), TOBN(0x18311294, 0xecba9477),
+     TOBN(0xda58f990, 0xa6946b43), TOBN(0x3098baf9, 0x9ab41819),
+     TOBN(0x66c4c158, 0x4198da52), TOBN(0xab4fc17c, 0x146bfd1b),
+     TOBN(0x2f0a4c3c, 0xbf36a908), TOBN(0x2ae9e34b, 0x58cf7838),
+     TOBN(0xf411529e, 0x3fa11b1f), TOBN(0x21e43677, 0x974af2b4),
+     TOBN(0x7c20958e, 0xc230793b), TOBN(0x710ea885, 0x16e840f3),
+     TOBN(0xfc0b21fc, 0xc5dc67cf), TOBN(0x08d51647, 0x88405718),
+     TOBN(0xd955c21f, 0xcfe49eb7), TOBN(0x9722a5d5, 0x56dd4a1f),
+     TOBN(0xc9ef50e2, 0xc861baa5), TOBN(0xc0c21a5d, 0x9505ac3e),
+     TOBN(0xaf6b9a33, 0x8b7c063f), TOBN(0xc6370339, 0x2f4779c1),
+     TOBN(0x22df99c7, 0x638167c3), TOBN(0xfe6ffe76, 0x795db30c),
+     TOBN(0x2b822d33, 0xa4854989), TOBN(0xfef031dd, 0x30563aa5),
+     TOBN(0x16b09f82, 0xd57c667f), TOBN(0xc70312ce, 0xcc0b76f1),
+     TOBN(0xbf04a9e6, 0xc9118aec), TOBN(0x82fcb419, 0x3409d133),
+     TOBN(0x1a8ab385, 0xab45d44d), TOBN(0xfba07222, 0x617b83a3),
+     TOBN(0xb05f50dd, 0x58e81b52), TOBN(0x1d8db553, 0x21ce5aff),
+     TOBN(0x3097b8d4, 0xe344a873), TOBN(0x7d8d116d, 0xfe36d53e),
+     TOBN(0x6db22f58, 0x7875e750), TOBN(0x2dc5e373, 0x43e144ea),
+     TOBN(0xc05f32e6, 0xe799eb95), TOBN(0xe9e5f4df, 0x6899e6ec),
+     TOBN(0xbdc3bd68, 0x1fab23d5), TOBN(0xb72b8ab7, 0x73af60e6),
+     TOBN(0x8db27ae0, 0x2cecc84a), TOBN(0x600016d8, 0x7bdb871c),
+     TOBN(0x42a44b13, 0xd7c46f58), TOBN(0xb8919727, 0xc3a77d39),
+     TOBN(0xcfc6bbbd, 0xdafd6088), TOBN(0x1a740146, 0x6bd20d39),
+     TOBN(0x8c747abd, 0x98c41072), TOBN(0x4c91e765, 0xbdf68ea1),
+     TOBN(0x7c95e5ca, 0x08819a78), TOBN(0xcf48b729, 0xc9587921),
+     TOBN(0x091c7c5f, 0xdebbcc7d), TOBN(0x6f287404, 0xf0e05149),
+     TOBN(0xf83b5ac2, 0x26cd44ec), TOBN(0x88ae32a6, 0xcfea250e),
+     TOBN(0x6ac5047a, 0x1d06ebc5), TOBN(0xc7e550b4, 0xd434f781),
+     TOBN(0x61ab1cf2, 0x5c727bd2), TOBN(0x2e4badb1, 0x1cf915b0),
+     TOBN(0x1b4dadec, 0xf69d3920), TOBN(0xe61b1ca6, 0xf14c1dfe),
+     TOBN(0x90b479cc, 0xbd6bd51f), TOBN(0x8024e401, 0x8045ec30),
+     TOBN(0xcab29ca3, 0x25ef0e62), TOBN(0x4f2e9416, 0x49e4ebc0),
+     TOBN(0x45eb40ec, 0x0ccced58), TOBN(0x25cd4b9c, 0x0da44f98),
+     TOBN(0x43e06458, 0x871812c6), TOBN(0x99f80d55, 0x16cef651),
+     TOBN(0x571340c9, 0xce6dc153), TOBN(0x138d5117, 0xd8665521),
+     TOBN(0xacdb45bc, 0x4e07014d), TOBN(0x2f34bb38, 0x84b60b91),
+     TOBN(0xf44a4fd2, 0x2ae8921e), TOBN(0xb039288e, 0x892ba1e2),
+     TOBN(0x9da50174, 0xb1c180b2), TOBN(0x6b70ab66, 0x1693dc87),
+     TOBN(0x7e9babc9, 0xe7057481), TOBN(0x4581ddef, 0x9c80dc41),
+     TOBN(0x0c890da9, 0x51294682), TOBN(0x0b5629d3, 0x3f4736e5),
+     TOBN(0x2340c79e, 0xb06f5b41), TOBN(0xa42e84ce, 0x4e243469),
+     TOBN(0xf9a20135, 0x045a71a9), TOBN(0xefbfb415, 0xd27b6fb6),
+     TOBN(0x25ebea23, 0x9d33cd6f), TOBN(0x9caedb88, 0xaa6c0af8),
+     TOBN(0x53dc7e9a, 0xd9ce6f96), TOBN(0x3897f9fd, 0x51e0b15a),
+     TOBN(0xf51cb1f8, 0x8e5d788e), TOBN(0x1aec7ba8, 0xe1d490ee),
+     TOBN(0x265991e0, 0xcc58cb3c), TOBN(0x9f306e8c, 0x9fc3ad31),
+     TOBN(0x5fed006e, 0x5040a0ac), TOBN(0xca9d5043, 0xfb476f2e),
+     TOBN(0xa19c06e8, 0xbeea7a23), TOBN(0xd2865801, 0x0edabb63),
+     TOBN(0xdb92293f, 0x6967469a), TOBN(0x2894d839, 0x8d8a8ed8),
+     TOBN(0x87c9e406, 0xbbc77122), TOBN(0x8671c6f1, 0x2ea3a26a),
+     TOBN(0xe42df8d6, 0xd7de9853), TOBN(0x2e3ce346, 0xb1f2bcc7),
+     TOBN(0xda601dfc, 0x899d50cf), TOBN(0xbfc913de, 0xfb1b598f),
+     TOBN(0x81c4909f, 0xe61f7908), TOBN(0x192e304f, 0x9bbc7b29),
+     TOBN(0xc3ed8738, 0xc104b338), TOBN(0xedbe9e47, 0x783f5d61),
+     TOBN(0x0c06e9be, 0x2db30660), TOBN(0xda3e613f, 0xc0eb7d8e),
+     TOBN(0xd8fa3e97, 0x322e096e), TOBN(0xfebd91e8, 0xd336e247),
+     TOBN(0x8f13ccc4, 0xdf655a49), TOBN(0xa9e00dfc, 0x5eb20210),
+     TOBN(0x84631d0f, 0xc656b6ea), TOBN(0x93a058cd, 0xd8c0d947),
+     TOBN(0x6846904a, 0x67bd3448), TOBN(0x4a3d4e1a, 0xf394fd5c),
+     TOBN(0xc102c1a5, 0xdb225f52), TOBN(0xe3455bba, 0xfc4f5e9a),
+     TOBN(0x6b36985b, 0x4b9ad1ce), TOBN(0xa9818536, 0x5bb7f793),
+     TOBN(0x6c25e1d0, 0x48b1a416), TOBN(0x1381dd53, 0x3c81bee7),
+     TOBN(0xd2a30d61, 0x7a4a7620), TOBN(0xc8412926, 0x39b8944c),
+     TOBN(0x3c1c6fbe, 0x7a97c33a), TOBN(0x941e541d, 0x938664e7),
+     TOBN(0x417499e8, 0x4a34f239), TOBN(0x15fdb83c, 0xb90402d5),
+     TOBN(0xb75f46bf, 0x433aa832), TOBN(0xb61e15af, 0x63215db1),
+     TOBN(0xaabe59d4, 0xa127f89a), TOBN(0x5d541e0c, 0x07e816da),
+     TOBN(0xaaba0659, 0xa618b692), TOBN(0x55327733, 0x17266026),
+     TOBN(0xaf53a0fc, 0x95f57552), TOBN(0x32947650, 0x6cacb0c9),
+     TOBN(0x253ff58d, 0xc821be01), TOBN(0xb0309531, 0xa06f1146),
+     TOBN(0x59bbbdf5, 0x05c2e54d), TOBN(0x158f27ad, 0x26e8dd22),
+     TOBN(0xcc5b7ffb, 0x397e1e53), TOBN(0xae03f65b, 0x7fc1e50d),
+     TOBN(0xa9784ebd, 0x9c95f0f9), TOBN(0x5ed9deb2, 0x24640771),
+     TOBN(0x31244af7, 0x035561c4), TOBN(0x87332f3a, 0x7ee857de),
+     TOBN(0x09e16e9e, 0x2b9e0d88), TOBN(0x52d910f4, 0x56a06049),
+     TOBN(0x507ed477, 0xa9592f48), TOBN(0x85cb917b, 0x2365d678),
+     TOBN(0xf8511c93, 0x4c8998d1), TOBN(0x2186a3f1, 0x730ea58f),
+     TOBN(0x50189626, 0xb2029db0), TOBN(0x9137a6d9, 0x02ceb75a),
+     TOBN(0x2fe17f37, 0x748bc82c), TOBN(0x87c2e931, 0x80469f8c),
+     TOBN(0x850f71cd, 0xbf891aa2), TOBN(0x0ca1b89b, 0x75ec3d8d),
+     TOBN(0x516c43aa, 0x5e1cd3cd), TOBN(0x89397808, 0x9a887c28),
+     TOBN(0x0059c699, 0xddea1f9f), TOBN(0x7737d6fa, 0x8e6868f7),
+     TOBN(0x6d93746a, 0x60f1524b), TOBN(0x36985e55, 0xba052aa7),
+     TOBN(0x41b1d322, 0xed923ea5), TOBN(0x3429759f, 0x25852a11),
+     TOBN(0xbeca6ec3, 0x092e9f41), TOBN(0x3a238c66, 0x62256bbd),
+     TOBN(0xd82958ea, 0x70ad487d), TOBN(0x4ac8aaf9, 0x65610d93),
+     TOBN(0x3fa101b1, 0x5e4ccab0), TOBN(0x9bf430f2, 0x9de14bfb),
+     TOBN(0xa10f5cc6, 0x6531899d), TOBN(0x590005fb, 0xea8ce17d),
+     TOBN(0xc437912f, 0x24544cb6), TOBN(0x9987b71a, 0xd79ac2e3),
+     TOBN(0x13e3d9dd, 0xc058a212), TOBN(0x00075aac, 0xd2de9606),
+     TOBN(0x80ab508b, 0x6cac8369), TOBN(0x87842be7, 0xf54f6c89),
+     TOBN(0xa7ad663d, 0x6bc532a4), TOBN(0x67813de7, 0x78a91bc8),
+     TOBN(0x5dcb61ce, 0xc3427239), TOBN(0x5f3c7cf0, 0xc56934d9),
+     TOBN(0xc079e0fb, 0xe3191591), TOBN(0xe40896bd, 0xb01aada7),
+     TOBN(0x8d466791, 0x0492d25f), TOBN(0x8aeb30c9, 0xe7408276),
+     TOBN(0xe9437495, 0x9287aacc), TOBN(0x23d4708d, 0x79fe03d4),
+     TOBN(0x8cda9cf2, 0xd0c05199), TOBN(0x502fbc22, 0xfae78454),
+     TOBN(0xc0bda9df, 0xf572a182), TOBN(0x5f9b71b8, 0x6158b372),
+     TOBN(0xe0f33a59, 0x2b82dd07), TOBN(0x76302735, 0x9523032e),
+     TOBN(0x7fe1a721, 0xc4505a32), TOBN(0x7b6e3e82, 0xf796409f),}
+    ,
+    {TOBN(0xe3417bc0, 0x35d0b34a), TOBN(0x440b386b, 0x8327c0a7),
+     TOBN(0x8fb7262d, 0xac0362d1), TOBN(0x2c41114c, 0xe0cdf943),
+     TOBN(0x2ba5cef1, 0xad95a0b1), TOBN(0xc09b37a8, 0x67d54362),
+     TOBN(0x26d6cdd2, 0x01e486c9), TOBN(0x20477abf, 0x42ff9297),
+     TOBN(0xa004dcb3, 0x292a9287), TOBN(0xddc15cf6, 0x77b092c7),
+     TOBN(0x083a8464, 0x806c0605), TOBN(0x4a68df70, 0x3db997b0),
+     TOBN(0x9c134e45, 0x05bf7dd0), TOBN(0xa4e63d39, 0x8ccf7f8c),
+     TOBN(0xa6e6517f, 0x41b5f8af), TOBN(0xaa8b9342, 0xad7bc1cc),
+     TOBN(0x126f35b5, 0x1e706ad9), TOBN(0xb99cebb4, 0xc3a9ebdf),
+     TOBN(0xa75389af, 0xbf608d90), TOBN(0x76113c4f, 0xc6c89858),
+     TOBN(0x80de8eb0, 0x97e2b5aa), TOBN(0x7e1022cc, 0x63b91304),
+     TOBN(0x3bdab605, 0x6ccc066c), TOBN(0x33cbb144, 0xb2edf900),
+     TOBN(0xc4176471, 0x7af715d2), TOBN(0xe2f7f594, 0xd0134a96),
+     TOBN(0x2c1873ef, 0xa41ec956), TOBN(0xe4e7b4f6, 0x77821304),
+     TOBN(0xe5c8ff97, 0x88d5374a), TOBN(0x2b915e63, 0x80823d5b),
+     TOBN(0xea6bc755, 0xb2ee8fe2), TOBN(0x6657624c, 0xe7112651),
+     TOBN(0x157af101, 0xdace5aca), TOBN(0xc4fdbcf2, 0x11a6a267),
+     TOBN(0xdaddf340, 0xc49c8609), TOBN(0x97e49f52, 0xe9604a65),
+     TOBN(0x9be8e790, 0x937e2ad5), TOBN(0x846e2508, 0x326e17f1),
+     TOBN(0x3f38007a, 0x0bbbc0dc), TOBN(0xcf03603f, 0xb11e16d6),
+     TOBN(0xd6f800e0, 0x7442f1d5), TOBN(0x475607d1, 0x66e0e3ab),
+     TOBN(0x82807f16, 0xb7c64047), TOBN(0x8858e1e3, 0xa749883d),
+     TOBN(0x5859120b, 0x8231ee10), TOBN(0x1b80e7eb, 0x638a1ece),
+     TOBN(0xcb72525a, 0xc6aa73a4), TOBN(0xa7cdea3d, 0x844423ac),
+     TOBN(0x5ed0c007, 0xf8ae7c38), TOBN(0x6db07a5c, 0x3d740192),
+     TOBN(0xbe5e9c2a, 0x5fe36db3), TOBN(0xd5b9d57a, 0x76e95046),
+     TOBN(0x54ac32e7, 0x8eba20f2), TOBN(0xef11ca8f, 0x71b9a352),
+     TOBN(0x305e373e, 0xff98a658), TOBN(0xffe5a100, 0x823eb667),
+     TOBN(0x57477b11, 0xe51732d2), TOBN(0xdfd6eb28, 0x2538fc0e),
+     TOBN(0x5c43b0cc, 0x3b39eec5), TOBN(0x6af12778, 0xcb36cc57),
+     TOBN(0x70b0852d, 0x06c425ae), TOBN(0x6df92f8c, 0x5c221b9b),
+     TOBN(0x6c8d4f9e, 0xce826d9c), TOBN(0xf59aba7b, 0xb49359c3),
+     TOBN(0x5c8ed8d5, 0xda64309d), TOBN(0x61a6de56, 0x91b30704),
+     TOBN(0xd6b52f6a, 0x2f9b5808), TOBN(0x0eee4194, 0x98c958a7),
+     TOBN(0xcddd9aab, 0x771e4caa), TOBN(0x83965dfd, 0x78bc21be),
+     TOBN(0x02affce3, 0xb3b504f5), TOBN(0x30847a21, 0x561c8291),
+     TOBN(0xd2eb2cf1, 0x52bfda05), TOBN(0xe0e4c4e9, 0x6197b98c),
+     TOBN(0x1d35076c, 0xf8a1726f), TOBN(0x6c06085b, 0x2db11e3d),
+     TOBN(0x15c0c4d7, 0x4463ba14), TOBN(0x9d292f83, 0x0030238c),
+     TOBN(0x1311ee8b, 0x3727536d), TOBN(0xfeea86ef, 0xbeaedc1e),
+     TOBN(0xb9d18cd3, 0x66131e2e), TOBN(0xf31d974f, 0x80fe2682),
+     TOBN(0xb6e49e0f, 0xe4160289), TOBN(0x7c48ec0b, 0x08e92799),
+     TOBN(0x818111d8, 0xd1989aa7), TOBN(0xb34fa0aa, 0xebf926f9),
+     TOBN(0xdb5fe2f5, 0xa245474a), TOBN(0xf80a6ebb, 0x3c7ca756),
+     TOBN(0xa7f96054, 0xafa05dd8), TOBN(0x26dfcf21, 0xfcaf119e),
+     TOBN(0xe20ef2e3, 0x0564bb59), TOBN(0xef4dca50, 0x61cb02b8),
+     TOBN(0xcda7838a, 0x65d30672), TOBN(0x8b08d534, 0xfd657e86),
+     TOBN(0x4c5b4395, 0x46d595c8), TOBN(0x39b58725, 0x425cb836),
+     TOBN(0x8ea61059, 0x3de9abe3), TOBN(0x40434881, 0x9cdc03be),
+     TOBN(0x9b261245, 0xcfedce8c), TOBN(0x78c318b4, 0xcf5234a1),
+     TOBN(0x510bcf16, 0xfde24c99), TOBN(0x2a77cb75, 0xa2c2ff5d),
+     TOBN(0x9c895c2b, 0x27960fb4), TOBN(0xd30ce975, 0xb0eda42b),
+     TOBN(0xfda85393, 0x1a62cc26), TOBN(0x23c69b96, 0x50c0e052),
+     TOBN(0xa227df15, 0xbfc633f3), TOBN(0x2ac78848, 0x1bae7d48),
+     TOBN(0x487878f9, 0x187d073d), TOBN(0x6c2be919, 0x967f807d),
+     TOBN(0x765861d8, 0x336e6d8f), TOBN(0x88b8974c, 0xce528a43),
+     TOBN(0x09521177, 0xff57d051), TOBN(0x2ff38037, 0xfb6a1961),
+     TOBN(0xfc0aba74, 0xa3d76ad4), TOBN(0x7c764803, 0x25a7ec17),
+     TOBN(0x7532d75f, 0x48879bc8), TOBN(0xea7eacc0, 0x58ce6bc1),
+     TOBN(0xc82176b4, 0x8e896c16), TOBN(0x9a30e0b2, 0x2c750fed),
+     TOBN(0xc37e2c2e, 0x421d3aa4), TOBN(0xf926407c, 0xe84fa840),
+     TOBN(0x18abc03d, 0x1454e41c), TOBN(0x26605ecd, 0x3f7af644),
+     TOBN(0x242341a6, 0xd6a5eabf), TOBN(0x1edb84f4, 0x216b668e),
+     TOBN(0xd836edb8, 0x04010102), TOBN(0x5b337ce7, 0x945e1d8c),
+     TOBN(0xd2075c77, 0xc055dc14), TOBN(0x2a0ffa25, 0x81d89cdf),
+     TOBN(0x8ce815ea, 0x6ffdcbaf), TOBN(0xa3428878, 0xfb648867),
+     TOBN(0x277699cf, 0x884655fb), TOBN(0xfa5b5bd6, 0x364d3e41),
+     TOBN(0x01f680c6, 0x441e1cb7), TOBN(0x3fd61e66, 0xb70a7d67),
+     TOBN(0x666ba2dc, 0xcc78cf66), TOBN(0xb3018174, 0x6fdbff77),
+     TOBN(0x8d4dd0db, 0x168d4668), TOBN(0x259455d0, 0x1dab3a2a),
+     TOBN(0xf58564c5, 0xcde3acec), TOBN(0x77141925, 0x13adb276),
+     TOBN(0x527d725d, 0x8a303f65), TOBN(0x55deb6c9, 0xe6f38f7b),
+     TOBN(0xfd5bb657, 0xb1fa70fb), TOBN(0xfa07f50f, 0xd8073a00),
+     TOBN(0xf72e3aa7, 0xbca02500), TOBN(0xf68f895d, 0x9975740d),
+     TOBN(0x30112060, 0x5cae2a6a), TOBN(0x01bd7218, 0x02874842),
+     TOBN(0x3d423891, 0x7ce47bd3), TOBN(0xa66663c1, 0x789544f6),
+     TOBN(0x864d05d7, 0x3272d838), TOBN(0xe22924f9, 0xfa6295c5),
+     TOBN(0x8189593f, 0x6c2fda32), TOBN(0x330d7189, 0xb184b544),
+     TOBN(0x79efa62c, 0xbde1f714), TOBN(0x35771c94, 0xe5cb1a63),
+     TOBN(0x2f4826b8, 0x641c8332), TOBN(0x00a894fb, 0xc8cee854),
+     TOBN(0xb4b9a39b, 0x36194d40), TOBN(0xe857a7c5, 0x77612601),
+     TOBN(0xf4209dd2, 0x4ecf2f58), TOBN(0x82b9e66d, 0x5a033487),
+     TOBN(0xc1e36934, 0xe4e8b9dd), TOBN(0xd2372c9d, 0xa42377d7),
+     TOBN(0x51dc94c7, 0x0e3ae43b), TOBN(0x4c57761e, 0x04474f6f),
+     TOBN(0xdcdacd0a, 0x1058a318), TOBN(0x369cf3f5, 0x78053a9a),
+     TOBN(0xc6c3de50, 0x31c68de2), TOBN(0x4653a576, 0x3c4b6d9f),
+     TOBN(0x1688dd5a, 0xaa4e5c97), TOBN(0x5be80aa1, 0xb7ab3c74),
+     TOBN(0x70cefe7c, 0xbc65c283), TOBN(0x57f95f13, 0x06867091),
+     TOBN(0xa39114e2, 0x4415503b), TOBN(0xc08ff7c6, 0x4cbb17e9),
+     TOBN(0x1eff674d, 0xd7dec966), TOBN(0x6d4690af, 0x53376f63),
+     TOBN(0xff6fe32e, 0xea74237b), TOBN(0xc436d17e, 0xcd57508e),
+     TOBN(0x15aa28e1, 0xedcc40fe), TOBN(0x0d769c04, 0x581bbb44),
+     TOBN(0xc240b6de, 0x34eaacda), TOBN(0xd9e116e8, 0x2ba0f1de),
+     TOBN(0xcbe45ec7, 0x79438e55), TOBN(0x91787c9d, 0x96f752d7),
+     TOBN(0x897f532b, 0xf129ac2f), TOBN(0xd307b7c8, 0x5a36e22c),
+     TOBN(0x91940675, 0x749fb8f3), TOBN(0xd14f95d0, 0x157fdb28),
+     TOBN(0xfe51d029, 0x6ae55043), TOBN(0x8931e98f, 0x44a87de1),
+     TOBN(0xe57f1cc6, 0x09e4fee2), TOBN(0x0d063b67, 0x4e072d92),
+     TOBN(0x70a998b9, 0xed0e4316), TOBN(0xe74a736b, 0x306aca46),
+     TOBN(0xecf0fbf2, 0x4fda97c7), TOBN(0xa40f65cb, 0x3e178d93),
+     TOBN(0x16253604, 0x16df4285), TOBN(0xb0c9babb, 0xd0c56ae2),
+     TOBN(0x73032b19, 0xcfc5cfc3), TOBN(0xe497e5c3, 0x09752056),
+     TOBN(0x12096bb4, 0x164bda96), TOBN(0x1ee42419, 0xa0b74da1),
+     TOBN(0x8fc36243, 0x403826ba), TOBN(0x0c8f0069, 0xdc09e660),
+     TOBN(0x8667e981, 0xc27253c9), TOBN(0x05a6aefb, 0x92b36a45),
+     TOBN(0xa62c4b36, 0x9cb7bb46), TOBN(0x8394f375, 0x11f7027b),
+     TOBN(0x747bc79c, 0x5f109d0f), TOBN(0xcad88a76, 0x5b8cc60a),
+     TOBN(0x80c5a66b, 0x58f09e68), TOBN(0xe753d451, 0xf6127eac),
+     TOBN(0xc44b74a1, 0x5b0ec6f5), TOBN(0x47989fe4, 0x5289b2b8),
+     TOBN(0x745f8484, 0x58d6fc73), TOBN(0xec362a6f, 0xf61c70ab),
+     TOBN(0x070c98a7, 0xb3a8ad41), TOBN(0x73a20fc0, 0x7b63db51),
+     TOBN(0xed2c2173, 0xf44c35f4), TOBN(0x8a56149d, 0x9acc9dca),
+     TOBN(0x98f17881, 0x9ac6e0f4), TOBN(0x360fdeaf, 0xa413b5ed),
+     TOBN(0x0625b8f4, 0xa300b0fd), TOBN(0xf1f4d76a, 0x5b3222d3),
+     TOBN(0x9d6f5109, 0x587f76b8), TOBN(0x8b4ee08d, 0x2317fdb5),
+     TOBN(0x88089bb7, 0x8c68b095), TOBN(0x95570e9a, 0x5808d9b9),
+     TOBN(0xa395c36f, 0x35d33ae7), TOBN(0x200ea123, 0x50bb5a94),
+     TOBN(0x20c789bd, 0x0bafe84b), TOBN(0x243ef52d, 0x0919276a),
+     TOBN(0x3934c577, 0xe23ae233), TOBN(0xb93807af, 0xa460d1ec),
+     TOBN(0xb72a53b1, 0xf8fa76a4), TOBN(0xd8914cb0, 0xc3ca4491),
+     TOBN(0x2e128494, 0x3fb42622), TOBN(0x3b2700ac, 0x500907d5),
+     TOBN(0xf370fb09, 0x1a95ec63), TOBN(0xf8f30be2, 0x31b6dfbd),
+     TOBN(0xf2b2f8d2, 0x69e55f15), TOBN(0x1fead851, 0xcc1323e9),
+     TOBN(0xfa366010, 0xd9e5eef6), TOBN(0x64d487b0, 0xe316107e),
+     TOBN(0x4c076b86, 0xd23ddc82), TOBN(0x03fd344c, 0x7e0143f0),
+     TOBN(0xa95362ff, 0x317af2c5), TOBN(0x0add3db7, 0xe18b7a4f),
+     TOBN(0x9c673e3f, 0x8260e01b), TOBN(0xfbeb49e5, 0x54a1cc91),
+     TOBN(0x91351bf2, 0x92f2e433), TOBN(0xc755e7ec, 0x851141eb),
+     TOBN(0xc9a95139, 0x29607745), TOBN(0x0ca07420, 0xa26f2b28),
+     TOBN(0xcb2790e7, 0x4bc6f9dd), TOBN(0x345bbb58, 0xadcaffc0),
+     TOBN(0xc65ea38c, 0xbe0f27a2), TOBN(0x67c24d7c, 0x641fcb56),
+     TOBN(0x2c25f0a7, 0xa9e2c757), TOBN(0x93f5cdb0, 0x16f16c49),
+     TOBN(0x2ca5a9d7, 0xc5ee30a1), TOBN(0xd1593635, 0xb909b729),
+     TOBN(0x804ce9f3, 0xdadeff48), TOBN(0xec464751, 0xb07c30c3),
+     TOBN(0x89d65ff3, 0x9e49af6a), TOBN(0xf2d6238a, 0x6f3d01bc),
+     TOBN(0x1095561e, 0x0bced843), TOBN(0x51789e12, 0xc8a13fd8),
+     TOBN(0xd633f929, 0x763231df), TOBN(0x46df9f7d, 0xe7cbddef),
+     TOBN(0x01c889c0, 0xcb265da8), TOBN(0xfce1ad10, 0xaf4336d2),
+     TOBN(0x8d110df6, 0xfc6a0a7e), TOBN(0xdd431b98, 0x6da425dc),
+     TOBN(0xcdc4aeab, 0x1834aabe), TOBN(0x84deb124, 0x8439b7fc),
+     TOBN(0x8796f169, 0x3c2a5998), TOBN(0x9b9247b4, 0x7947190d),
+     TOBN(0x55b9d9a5, 0x11597014), TOBN(0x7e9dd70d, 0x7b1566ee),
+     TOBN(0x94ad78f7, 0xcbcd5e64), TOBN(0x0359ac17, 0x9bd4c032),
+     TOBN(0x3b11baaf, 0x7cc222ae), TOBN(0xa6a6e284, 0xba78e812),
+     TOBN(0x8392053f, 0x24cea1a0), TOBN(0xc97bce4a, 0x33621491),
+     TOBN(0x7eb1db34, 0x35399ee9), TOBN(0x473f78ef, 0xece81ad1),
+     TOBN(0x41d72fe0, 0xf63d3d0d), TOBN(0xe620b880, 0xafab62fc),
+     TOBN(0x92096bc9, 0x93158383), TOBN(0x41a21357, 0x8f896f6c),
+     TOBN(0x1b5ee2fa, 0xc7dcfcab), TOBN(0x650acfde, 0x9546e007),
+     TOBN(0xc081b749, 0xb1b02e07), TOBN(0xda9e41a0, 0xf9eca03d),
+     TOBN(0x013ba727, 0x175a54ab), TOBN(0xca0cd190, 0xea5d8d10),
+     TOBN(0x85ea52c0, 0x95fd96a9), TOBN(0x2c591b9f, 0xbc5c3940),
+     TOBN(0x6fb4d4e4, 0x2bad4d5f), TOBN(0xfa4c3590, 0xfef0059b),
+     TOBN(0x6a10218a, 0xf5122294), TOBN(0x9a78a81a, 0xa85751d1),
+     TOBN(0x04f20579, 0xa98e84e7), TOBN(0xfe1242c0, 0x4997e5b5),
+     TOBN(0xe77a273b, 0xca21e1e4), TOBN(0xfcc8b1ef, 0x9411939d),
+     TOBN(0xe20ea302, 0x92d0487a), TOBN(0x1442dbec, 0x294b91fe),
+     TOBN(0x1f7a4afe, 0xbb6b0e8f), TOBN(0x1700ef74, 0x6889c318),
+     TOBN(0xf5bbffc3, 0x70f1fc62), TOBN(0x3b31d4b6, 0x69c79cca),
+     TOBN(0xe8bc2aab, 0xa7f6340d), TOBN(0xb0b08ab4, 0xa725e10a),
+     TOBN(0x44f05701, 0xae340050), TOBN(0xba4b3016, 0x1cf0c569),
+     TOBN(0x5aa29f83, 0xfbe19a51), TOBN(0x1b9ed428, 0xb71d752e),
+     TOBN(0x1666e54e, 0xeb4819f5), TOBN(0x616cdfed, 0x9e18b75b),
+     TOBN(0x112ed5be, 0x3ee27b0b), TOBN(0xfbf28319, 0x44c7de4d),
+     TOBN(0xd685ec85, 0xe0e60d84), TOBN(0x68037e30, 0x1db7ee78),
+     TOBN(0x5b65bdcd, 0x003c4d6e), TOBN(0x33e7363a, 0x93e29a6a),
+     TOBN(0x995b3a61, 0x08d0756c), TOBN(0xd727f85c, 0x2faf134b),
+     TOBN(0xfac6edf7, 0x1d337823), TOBN(0x99b9aa50, 0x0439b8b4),
+     TOBN(0x722eb104, 0xe2b4e075), TOBN(0x49987295, 0x437c4926),
+     TOBN(0xb1e4c0e4, 0x46a9b82d), TOBN(0xd0cb3197, 0x57a006f5),
+     TOBN(0xf3de0f7d, 0xd7808c56), TOBN(0xb5c54d8f, 0x51f89772),
+     TOBN(0x500a114a, 0xadbd31aa), TOBN(0x9afaaaa6, 0x295f6cab),
+     TOBN(0x94705e21, 0x04cf667a), TOBN(0xfc2a811b, 0x9d3935d7),
+     TOBN(0x560b0280, 0x6d09267c), TOBN(0xf19ed119, 0xf780e53b),
+     TOBN(0xf0227c09, 0x067b6269), TOBN(0x967b8533, 0x5caef599),
+     TOBN(0x155b9243, 0x68efeebc), TOBN(0xcd6d34f5, 0xc497bae6),
+     TOBN(0x1dd8d5d3, 0x6cceb370), TOBN(0x2aeac579, 0xa78d7bf9),
+     TOBN(0x5d65017d, 0x70b67a62), TOBN(0x70c8e44f, 0x17c53f67),
+     TOBN(0xd1fc0950, 0x86a34d09), TOBN(0xe0fca256, 0xe7134907),
+     TOBN(0xe24fa29c, 0x80fdd315), TOBN(0x2c4acd03, 0xd87499ad),
+     TOBN(0xbaaf7517, 0x3b5a9ba6), TOBN(0xb9cbe1f6, 0x12e51a51),
+     TOBN(0xd88edae3, 0x5e154897), TOBN(0xe4309c3c, 0x77b66ca0),
+     TOBN(0xf5555805, 0xf67f3746), TOBN(0x85fc37ba, 0xa36401ff),
+     TOBN(0xdf86e2ca, 0xd9499a53), TOBN(0x6270b2a3, 0xecbc955b),
+     TOBN(0xafae64f5, 0x974ad33b), TOBN(0x04d85977, 0xfe7b2df1),
+     TOBN(0x2a3db3ff, 0x4ab03f73), TOBN(0x0b87878a, 0x8702740a),
+     TOBN(0x6d263f01, 0x5a061732), TOBN(0xc25430ce, 0xa32a1901),
+     TOBN(0xf7ebab3d, 0xdb155018), TOBN(0x3a86f693, 0x63a9b78e),
+     TOBN(0x349ae368, 0xda9f3804), TOBN(0x470f07fe, 0xa164349c),
+     TOBN(0xd52f4cc9, 0x8562baa5), TOBN(0xc74a9e86, 0x2b290df3),
+     TOBN(0xd3a1aa35, 0x43471a24), TOBN(0x239446be, 0xb8194511),
+     TOBN(0xbec2dd00, 0x81dcd44d), TOBN(0xca3d7f0f, 0xc42ac82d),
+     TOBN(0x1f3db085, 0xfdaf4520), TOBN(0xbb6d3e80, 0x4549daf2),
+     TOBN(0xf5969d8a, 0x19ad5c42), TOBN(0x7052b13d, 0xdbfd1511),
+     TOBN(0x11890d1b, 0x682b9060), TOBN(0xa71d3883, 0xac34452c),
+     TOBN(0xa438055b, 0x783805b4), TOBN(0x43241277, 0x4725b23e),
+     TOBN(0xf20cf96e, 0x4901bbed), TOBN(0x6419c710, 0xf432a2bb),
+     TOBN(0x57a0fbb9, 0xdfa9cd7d), TOBN(0x589111e4, 0x00daa249),
+     TOBN(0x19809a33, 0x7b60554e), TOBN(0xea5f8887, 0xede283a4),
+     TOBN(0x2d713802, 0x503bfd35), TOBN(0x151bb0af, 0x585d2a53),
+     TOBN(0x40b08f74, 0x43b30ca8), TOBN(0xe10b5bba, 0xd9934583),
+     TOBN(0xe8a546d6, 0xb51110ad), TOBN(0x1dd50e66, 0x28e0b6c5),
+     TOBN(0x292e9d54, 0xcff2b821), TOBN(0x3882555d, 0x47281760),
+     TOBN(0x134838f8, 0x3724d6e3), TOBN(0xf2c679e0, 0x22ddcda1),
+     TOBN(0x40ee8815, 0x6d2a5768), TOBN(0x7f227bd2, 0x1c1e7e2d),
+     TOBN(0x487ba134, 0xd04ff443), TOBN(0x76e2ff3d, 0xc614e54b),
+     TOBN(0x36b88d6f, 0xa3177ec7), TOBN(0xbf731d51, 0x2328fff5),
+     TOBN(0x758caea2, 0x49ba158e), TOBN(0x5ab8ff4c, 0x02938188),
+     TOBN(0x33e16056, 0x35edc56d), TOBN(0x5a69d349, 0x7e940d79),
+     TOBN(0x6c4fd001, 0x03866dcb), TOBN(0x20a38f57, 0x4893cdef),
+     TOBN(0xfbf3e790, 0xfac3a15b), TOBN(0x6ed7ea2e, 0x7a4f8e6b),
+     TOBN(0xa663eb4f, 0xbc3aca86), TOBN(0x22061ea5, 0x080d53f7),
+     TOBN(0x2480dfe6, 0xf546783f), TOBN(0xd38bc6da, 0x5a0a641e),
+     TOBN(0xfb093cd1, 0x2ede8965), TOBN(0x89654db4, 0xacb455cf),
+     TOBN(0x413cbf9a, 0x26e1adee), TOBN(0x291f3764, 0x373294d4),
+     TOBN(0x00797257, 0x648083fe), TOBN(0x25f504d3, 0x208cc341),
+     TOBN(0x635a8e5e, 0xc3a0ee43), TOBN(0x70aaebca, 0x679898ff),
+     TOBN(0x9ee9f547, 0x5dc63d56), TOBN(0xce987966, 0xffb34d00),
+     TOBN(0xf9f86b19, 0x5e26310a), TOBN(0x9e435484, 0x382a8ca8),
+     TOBN(0x253bcb81, 0xc2352fe4), TOBN(0xa4eac8b0, 0x4474b571),
+     TOBN(0xc1b97512, 0xc1ad8cf8), TOBN(0x193b4e9e, 0x99e0b697),
+     TOBN(0x939d2716, 0x01e85df0), TOBN(0x4fb265b3, 0xcd44eafd),
+     TOBN(0x321e7dcd, 0xe51e1ae2), TOBN(0x8e3a8ca6, 0xe3d8b096),
+     TOBN(0x8de46cb0, 0x52604998), TOBN(0x91099ad8, 0x39072aa7),
+     TOBN(0x2617f91c, 0x93aa96b8), TOBN(0x0fc8716b, 0x7fca2e13),
+     TOBN(0xa7106f5e, 0x95328723), TOBN(0xd1c9c40b, 0x262e6522),
+     TOBN(0xb9bafe86, 0x42b7c094), TOBN(0x1873439d, 0x1543c021),
+     TOBN(0xe1baa5de, 0x5cbefd5d), TOBN(0xa363fc5e, 0x521e8aff),
+     TOBN(0xefe6320d, 0xf862eaac), TOBN(0x14419c63, 0x22c647dc),
+     TOBN(0x0e06707c, 0x4e46d428), TOBN(0xcb6c834f, 0x4a178f8f),
+     TOBN(0x0f993a45, 0xd30f917c), TOBN(0xd4c4b049, 0x9879afee),
+     TOBN(0xb6142a1e, 0x70500063), TOBN(0x7c9b41c3, 0xa5d9d605),
+     TOBN(0xbc00fc2f, 0x2f8ba2c7), TOBN(0x0966eb2f, 0x7c67aa28),
+     TOBN(0x13f7b516, 0x5a786972), TOBN(0x3bfb7557, 0x8a2fbba0),
+     TOBN(0x131c4f23, 0x5a2b9620), TOBN(0xbff3ed27, 0x6faf46be),
+     TOBN(0x9b4473d1, 0x7e172323), TOBN(0x421e8878, 0x339f6246),
+     TOBN(0x0fa8587a, 0x25a41632), TOBN(0xc0814124, 0xa35b6c93),
+     TOBN(0x2b18a9f5, 0x59ebb8db), TOBN(0x264e3357, 0x76edb29c),
+     TOBN(0xaf245ccd, 0xc87c51e2), TOBN(0x16b3015b, 0x501e6214),
+     TOBN(0xbb31c560, 0x0a3882ce), TOBN(0x6961bb94, 0xfec11e04),
+     TOBN(0x3b825b8d, 0xeff7a3a0), TOBN(0xbec33738, 0xb1df7326),
+     TOBN(0x68ad747c, 0x99604a1f), TOBN(0xd154c934, 0x9a3bd499),
+     TOBN(0xac33506f, 0x1cc7a906), TOBN(0x73bb5392, 0x6c560e8f),
+     TOBN(0x6428fcbe, 0x263e3944), TOBN(0xc11828d5, 0x1c387434),
+     TOBN(0x3cd04be1, 0x3e4b12ff), TOBN(0xc3aad9f9, 0x2d88667c),
+     TOBN(0xc52ddcf8, 0x248120cf), TOBN(0x985a892e, 0x2a389532),
+     TOBN(0xfbb4b21b, 0x3bb85fa0), TOBN(0xf95375e0, 0x8dfc6269),
+     TOBN(0xfb4fb06c, 0x7ee2acea), TOBN(0x6785426e, 0x309c4d1f),
+     TOBN(0x659b17c8, 0xd8ceb147), TOBN(0x9b649eee, 0xb70a5554),
+     TOBN(0x6b7fa0b5, 0xac6bc634), TOBN(0xd99fe2c7, 0x1d6e732f),
+     TOBN(0x30e6e762, 0x8d3abba2), TOBN(0x18fee6e7, 0xa797b799),
+     TOBN(0x5c9d360d, 0xc696464d), TOBN(0xe3baeb48, 0x27bfde12),
+     TOBN(0x2bf5db47, 0xf23206d5), TOBN(0x2f6d3420, 0x1d260152),
+     TOBN(0x17b87653, 0x3f8ff89a), TOBN(0x5157c30c, 0x378fa458),
+     TOBN(0x7517c5c5, 0x2d4fb936), TOBN(0xef22f7ac, 0xe6518cdc),
+     TOBN(0xdeb483e6, 0xbf847a64), TOBN(0xf5084558, 0x92e0fa89),}
+    ,
+    {TOBN(0xab9659d8, 0xdf7304d4), TOBN(0xb71bcf1b, 0xff210e8e),
+     TOBN(0xa9a2438b, 0xd73fbd60), TOBN(0x4595cd1f, 0x5d11b4de),
+     TOBN(0x9c0d329a, 0x4835859d), TOBN(0x4a0f0d2d, 0x7dbb6e56),
+     TOBN(0xc6038e5e, 0xdf928a4e), TOBN(0xc9429621, 0x8f5ad154),
+     TOBN(0x91213462, 0xf23f2d92), TOBN(0x6cab71bd, 0x60b94078),
+     TOBN(0x6bdd0a63, 0x176cde20), TOBN(0x54c9b20c, 0xee4d54bc),
+     TOBN(0x3cd2d8aa, 0x9f2ac02f), TOBN(0x03f8e617, 0x206eedb0),
+     TOBN(0xc7f68e16, 0x93086434), TOBN(0x831469c5, 0x92dd3db9),
+     TOBN(0x8521df24, 0x8f981354), TOBN(0x587e23ec, 0x3588a259),
+     TOBN(0xcbedf281, 0xd7a0992c), TOBN(0x06930a55, 0x38961407),
+     TOBN(0x09320deb, 0xbe5bbe21), TOBN(0xa7ffa5b5, 0x2491817f),
+     TOBN(0xe6c8b4d9, 0x09065160), TOBN(0xac4f3992, 0xfff6d2a9),
+     TOBN(0x7aa7a158, 0x3ae9c1bd), TOBN(0xe0af6d98, 0xe37ce240),
+     TOBN(0xe54342d9, 0x28ab38b4), TOBN(0xe8b75007, 0x0a1c98ca),
+     TOBN(0xefce86af, 0xe02358f2), TOBN(0x31b8b856, 0xea921228),
+     TOBN(0x052a1912, 0x0a1c67fc), TOBN(0xb4069ea4, 0xe3aead59),
+     TOBN(0x3232d6e2, 0x7fa03cb3), TOBN(0xdb938e5b, 0x0fdd7d88),
+     TOBN(0x04c1d2cd, 0x2ccbfc5d), TOBN(0xd2f45c12, 0xaf3a580f),
+     TOBN(0x592620b5, 0x7883e614), TOBN(0x5fd27e68, 0xbe7c5f26),
+     TOBN(0x139e45a9, 0x1567e1e3), TOBN(0x2cc71d2d, 0x44d8aaaf),
+     TOBN(0x4a9090cd, 0xe36d0757), TOBN(0xf722d7b1, 0xd9a29382),
+     TOBN(0xfb7fb04c, 0x04b48ddf), TOBN(0x628ad2a7, 0xebe16f43),
+     TOBN(0xcd3fbfb5, 0x20226040), TOBN(0x6c34ecb1, 0x5104b6c4),
+     TOBN(0x30c0754e, 0xc903c188), TOBN(0xec336b08, 0x2d23cab0),
+     TOBN(0x473d62a2, 0x1e206ee5), TOBN(0xf1e27480, 0x8c49a633),
+     TOBN(0x87ab956c, 0xe9f6b2c3), TOBN(0x61830b48, 0x62b606ea),
+     TOBN(0x67cd6846, 0xe78e815f), TOBN(0xfe40139f, 0x4c02082a),
+     TOBN(0x52bbbfcb, 0x952ec365), TOBN(0x74c11642, 0x6b9836ab),
+     TOBN(0x9f51439e, 0x558df019), TOBN(0x230da4ba, 0xac712b27),
+     TOBN(0x518919e3, 0x55185a24), TOBN(0x4dcefcdd, 0x84b78f50),
+     TOBN(0xa7d90fb2, 0xa47d4c5a), TOBN(0x55ac9abf, 0xb30e009e),
+     TOBN(0xfd2fc359, 0x74eed273), TOBN(0xb72d824c, 0xdbea8faf),
+     TOBN(0xce721a74, 0x4513e2ca), TOBN(0x0b418612, 0x38240b2c),
+     TOBN(0x05199968, 0xd5baa450), TOBN(0xeb1757ed, 0x2b0e8c25),
+     TOBN(0x6ebc3e28, 0x3dfac6d5), TOBN(0xb2431e2e, 0x48a237f5),
+     TOBN(0x2acb5e23, 0x52f61499), TOBN(0x5558a2a7, 0xe06c936b),
+     TOBN(0xd213f923, 0xcbb13d1b), TOBN(0x98799f42, 0x5bfb9bfe),
+     TOBN(0x1ae8ddc9, 0x701144a9), TOBN(0x0b8b3bb6, 0x4c5595ee),
+     TOBN(0x0ea9ef2e, 0x3ecebb21), TOBN(0x17cb6c4b, 0x3671f9a7),
+     TOBN(0x47ef464f, 0x726f1d1f), TOBN(0x171b9484, 0x6943a276),
+     TOBN(0x51a4ae2d, 0x7ef0329c), TOBN(0x08509222, 0x91c4402a),
+     TOBN(0x64a61d35, 0xafd45bbc), TOBN(0x38f096fe, 0x3035a851),
+     TOBN(0xc7468b74, 0xa1dec027), TOBN(0xe8cf10e7, 0x4fc7dcba),
+     TOBN(0xea35ff40, 0xf4a06353), TOBN(0x0b4c0dfa, 0x8b77dd66),
+     TOBN(0x779b8552, 0xde7e5c19), TOBN(0xfab28609, 0xc1c0256c),
+     TOBN(0x64f58eee, 0xabd4743d), TOBN(0x4e8ef838, 0x7b6cc93b),
+     TOBN(0xee650d26, 0x4cb1bf3d), TOBN(0x4c1f9d09, 0x73dedf61),
+     TOBN(0xaef7c9d7, 0xbfb70ced), TOBN(0x1ec0507e, 0x1641de1e),
+     TOBN(0xcd7e5cc7, 0xcde45079), TOBN(0xde173c9a, 0x516ac9e4),
+     TOBN(0x517a8494, 0xc170315c), TOBN(0x438fd905, 0x91d8e8fb),
+     TOBN(0x5145c506, 0xc7d9630b), TOBN(0x6457a87b, 0xf47d4d75),
+     TOBN(0xd31646bf, 0x0d9a80e8), TOBN(0x453add2b, 0xcef3aabe),
+     TOBN(0xc9941109, 0xa607419d), TOBN(0xfaa71e62, 0xbb6bca80),
+     TOBN(0x34158c13, 0x07c431f3), TOBN(0x594abebc, 0x992bc47a),
+     TOBN(0x6dfea691, 0xeb78399f), TOBN(0x48aafb35, 0x3f42cba4),
+     TOBN(0xedcd65af, 0x077c04f0), TOBN(0x1a29a366, 0xe884491a),
+     TOBN(0x023a40e5, 0x1c21f2bf), TOBN(0xf99a513c, 0xa5057aee),
+     TOBN(0xa3fe7e25, 0xbcab072e), TOBN(0x8568d2e1, 0x40e32bcf),
+     TOBN(0x904594eb, 0xd3f69d9f), TOBN(0x181a9733, 0x07affab1),
+     TOBN(0xe4d68d76, 0xb6e330f4), TOBN(0x87a6dafb, 0xc75a7fc1),
+     TOBN(0x549db2b5, 0xef7d9289), TOBN(0x2480d4a8, 0x197f015a),
+     TOBN(0x61d5590b, 0xc40493b6), TOBN(0x3a55b52e, 0x6f780331),
+     TOBN(0x40eb8115, 0x309eadb0), TOBN(0xdea7de5a, 0x92e5c625),
+     TOBN(0x64d631f0, 0xcc6a3d5a), TOBN(0x9d5e9d7c, 0x93e8dd61),
+     TOBN(0xf297bef5, 0x206d3ffc), TOBN(0x23d5e033, 0x7d808bd4),
+     TOBN(0x4a4f6912, 0xd24cf5ba), TOBN(0xe4d8163b, 0x09cdaa8a),
+     TOBN(0x0e0de9ef, 0xd3082e8e), TOBN(0x4fe1246c, 0x0192f360),
+     TOBN(0x1f900150, 0x4b8eee0a), TOBN(0x5219da81, 0xf1da391b),
+     TOBN(0x7bf6a5c1, 0xf7ea25aa), TOBN(0xd165e6bf, 0xfbb07d5f),
+     TOBN(0xe3539361, 0x89e78671), TOBN(0xa3fcac89, 0x2bac4219),
+     TOBN(0xdfab6fd4, 0xf0baa8ab), TOBN(0x5a4adac1, 0xe2c1c2e5),
+     TOBN(0x6cd75e31, 0x40d85849), TOBN(0xce263fea, 0x19b39181),
+     TOBN(0xcb6803d3, 0x07032c72), TOBN(0x7f40d5ce, 0x790968c8),
+     TOBN(0xa6de86bd, 0xdce978f0), TOBN(0x25547c4f, 0x368f751c),
+     TOBN(0xb1e685fd, 0x65fb2a9e), TOBN(0xce69336f, 0x1eb9179c),
+     TOBN(0xb15d1c27, 0x12504442), TOBN(0xb7df465c, 0xb911a06b),
+     TOBN(0xb8d804a3, 0x315980cd), TOBN(0x693bc492, 0xfa3bebf7),
+     TOBN(0x3578aeee, 0x2253c504), TOBN(0x158de498, 0xcd2474a2),
+     TOBN(0x1331f5c7, 0xcfda8368), TOBN(0xd2d7bbb3, 0x78d7177e),
+     TOBN(0xdf61133a, 0xf3c1e46e), TOBN(0x5836ce7d, 0xd30e7be8),
+     TOBN(0x83084f19, 0x94f834cb), TOBN(0xd35653d4, 0x429ed782),
+     TOBN(0xa542f16f, 0x59e58243), TOBN(0xc2b52f65, 0x0470a22d),
+     TOBN(0xe3b6221b, 0x18f23d96), TOBN(0xcb05abac, 0x3f5252b4),
+     TOBN(0xca00938b, 0x87d61402), TOBN(0x2f186cdd, 0x411933e4),
+     TOBN(0xe042ece5, 0x9a29a5c5), TOBN(0xb19b3c07, 0x3b6c8402),
+     TOBN(0xc97667c7, 0x19d92684), TOBN(0xb5624622, 0xebc66372),
+     TOBN(0x0cb96e65, 0x3c04fa02), TOBN(0x83a7176c, 0x8eaa39aa),
+     TOBN(0x2033561d, 0xeaa1633f), TOBN(0x45a9d086, 0x4533df73),
+     TOBN(0xe0542c1d, 0x3dc090bc), TOBN(0x82c996ef, 0xaa59c167),
+     TOBN(0xe3f735e8, 0x0ee7fc4d), TOBN(0x7b179393, 0x7c35db79),
+     TOBN(0xb6419e25, 0xf8c5dbfd), TOBN(0x4d9d7a1e, 0x1f327b04),
+     TOBN(0x979f6f9b, 0x298dfca8), TOBN(0xc7c5dff1, 0x8de9366a),
+     TOBN(0x1b7a588d, 0x04c82bdd), TOBN(0x68005534, 0xf8319dfd),
+     TOBN(0xde8a55b5, 0xd8eb9580), TOBN(0x5ea886da, 0x8d5bca81),
+     TOBN(0xe8530a01, 0x252a0b4d), TOBN(0x1bffb4fe, 0x35eaa0a1),
+     TOBN(0x2ad828b1, 0xd8e99563), TOBN(0x7de96ef5, 0x95f9cd87),
+     TOBN(0x4abb2d0c, 0xd77d970c), TOBN(0x03cfb933, 0xd33ef9cb),
+     TOBN(0xb0547c01, 0x8b211fe9), TOBN(0x2fe64809, 0xa56ed1c6),
+     TOBN(0xcb7d5624, 0xc2ac98cc), TOBN(0x2a1372c0, 0x1a393e33),
+     TOBN(0xc8d1ec1c, 0x29660521), TOBN(0xf3d31b04, 0xb37ac3e9),
+     TOBN(0xa29ae9df, 0x5ece6e7c), TOBN(0x0603ac8f, 0x0facfb55),
+     TOBN(0xcfe85b7a, 0xdda233a5), TOBN(0xe618919f, 0xbd75f0b8),
+     TOBN(0xf555a3d2, 0x99bf1603), TOBN(0x1f43afc9, 0xf184255a),
+     TOBN(0xdcdaf341, 0x319a3e02), TOBN(0xd3b117ef, 0x03903a39),
+     TOBN(0xe095da13, 0x65d1d131), TOBN(0x86f16367, 0xc37ad03e),
+     TOBN(0x5f37389e, 0x462cd8dd), TOBN(0xc103fa04, 0xd67a60e6),
+     TOBN(0x57c34344, 0xf4b478f0), TOBN(0xce91edd8, 0xe117c98d),
+     TOBN(0x001777b0, 0x231fc12e), TOBN(0x11ae47f2, 0xb207bccb),
+     TOBN(0xd983cf8d, 0x20f8a242), TOBN(0x7aff5b1d, 0xf22e1ad8),
+     TOBN(0x68fd11d0, 0x7fc4feb3), TOBN(0x5d53ae90, 0xb0f1c3e1),
+     TOBN(0x50fb7905, 0xec041803), TOBN(0x85e3c977, 0x14404888),
+     TOBN(0x0e67faed, 0xac628d8f), TOBN(0x2e865150, 0x6668532c),
+     TOBN(0x15acaaa4, 0x6a67a6b0), TOBN(0xf4cdee25, 0xb25cec41),
+     TOBN(0x49ee565a, 0xe4c6701e), TOBN(0x2a04ca66, 0xfc7d63d8),
+     TOBN(0xeb105018, 0xef0543fb), TOBN(0xf709a4f5, 0xd1b0d81d),
+     TOBN(0x5b906ee6, 0x2915d333), TOBN(0xf4a87412, 0x96f1f0ab),
+     TOBN(0xb6b82fa7, 0x4d82f4c2), TOBN(0x90725a60, 0x6804efb3),
+     TOBN(0xbc82ec46, 0xadc3425e), TOBN(0xb7b80581, 0x2787843e),
+     TOBN(0xdf46d91c, 0xdd1fc74c), TOBN(0xdc1c62cb, 0xe783a6c4),
+     TOBN(0x59d1b9f3, 0x1a04cbba), TOBN(0xd87f6f72, 0x95e40764),
+     TOBN(0x02b4cfc1, 0x317f4a76), TOBN(0x8d2703eb, 0x91036bce),
+     TOBN(0x98206cc6, 0xa5e72a56), TOBN(0x57be9ed1, 0xcf53fb0f),
+     TOBN(0x09374571, 0xef0b17ac), TOBN(0x74b2655e, 0xd9181b38),
+     TOBN(0xc8f80ea8, 0x89935d0e), TOBN(0xc0d9e942, 0x91529936),
+     TOBN(0x19686041, 0x1e84e0e5), TOBN(0xa5db84d3, 0xaea34c93),
+     TOBN(0xf9d5bb19, 0x7073a732), TOBN(0xb8d2fe56, 0x6bcfd7c0),
+     TOBN(0x45775f36, 0xf3eb82fa), TOBN(0x8cb20ccc, 0xfdff8b58),
+     TOBN(0x1659b65f, 0x8374c110), TOBN(0xb8b4a422, 0x330c789a),
+     TOBN(0x75e3c3ea, 0x6fe8208b), TOBN(0xbd74b9e4, 0x286e78fe),
+     TOBN(0x0be2e81b, 0xd7d93a1a), TOBN(0x7ed06e27, 0xdd0a5aae),
+     TOBN(0x721f5a58, 0x6be8b800), TOBN(0x428299d1, 0xd846db28),
+     TOBN(0x95cb8e6b, 0x5be88ed3), TOBN(0xc3186b23, 0x1c034e11),
+     TOBN(0xa6312c9e, 0x8977d99b), TOBN(0xbe944331, 0x83f531e7),
+     TOBN(0x8232c0c2, 0x18d3b1d4), TOBN(0x617aae8b, 0xe1247b73),
+     TOBN(0x40153fc4, 0x282aec3b), TOBN(0xc6063d2f, 0xf7b8f823),
+     TOBN(0x68f10e58, 0x3304f94c), TOBN(0x31efae74, 0xee676346),
+     TOBN(0xbadb6c6d, 0x40a9b97c), TOBN(0x14702c63, 0x4f666256),
+     TOBN(0xdeb954f1, 0x5184b2e3), TOBN(0x5184a526, 0x94b6ca40),
+     TOBN(0xfff05337, 0x003c32ea), TOBN(0x5aa374dd, 0x205974c7),
+     TOBN(0x9a763854, 0x4b0dd71a), TOBN(0x459cd27f, 0xdeb947ec),
+     TOBN(0xa6e28161, 0x459c2b92), TOBN(0x2f020fa8, 0x75ee8ef5),
+     TOBN(0xb132ec2d, 0x30b06310), TOBN(0xc3e15899, 0xbc6a4530),
+     TOBN(0xdc5f53fe, 0xaa3f451a), TOBN(0x3a3c7f23, 0xc2d9acac),
+     TOBN(0x2ec2f892, 0x6b27e58b), TOBN(0x68466ee7, 0xd742799f),
+     TOBN(0x98324dd4, 0x1fa26613), TOBN(0xa2dc6dab, 0xbdc29d63),
+     TOBN(0xf9675faa, 0xd712d657), TOBN(0x813994be, 0x21fd8d15),
+     TOBN(0x5ccbb722, 0xfd4f7553), TOBN(0x5135ff8b, 0xf3a36b20),
+     TOBN(0x44be28af, 0x69559df5), TOBN(0x40b65bed, 0x9d41bf30),
+     TOBN(0xd98bf2a4, 0x3734e520), TOBN(0x5e3abbe3, 0x209bdcba),
+     TOBN(0x77c76553, 0xbc945b35), TOBN(0x5331c093, 0xc6ef14aa),
+     TOBN(0x518ffe29, 0x76b60c80), TOBN(0x2285593b, 0x7ace16f8),
+     TOBN(0xab1f64cc, 0xbe2b9784), TOBN(0xe8f2c0d9, 0xab2421b6),
+     TOBN(0x617d7174, 0xc1df065c), TOBN(0xafeeb5ab, 0x5f6578fa),
+     TOBN(0x16ff1329, 0x263b54a8), TOBN(0x45c55808, 0xc990dce3),
+     TOBN(0x42eab6c0, 0xecc8c177), TOBN(0x799ea9b5, 0x5982ecaa),
+     TOBN(0xf65da244, 0xb607ef8e), TOBN(0x8ab226ce, 0x32a3fc2c),
+     TOBN(0x745741e5, 0x7ea973dc), TOBN(0x5c00ca70, 0x20888f2e),
+     TOBN(0x7cdce3cf, 0x45fd9cf1), TOBN(0x8a741ef1, 0x5507f872),
+     TOBN(0x47c51c2f, 0x196b4cec), TOBN(0x70d08e43, 0xc97ea618),
+     TOBN(0x930da15c, 0x15b18a2b), TOBN(0x33b6c678, 0x2f610514),
+     TOBN(0xc662e4f8, 0x07ac9794), TOBN(0x1eccf050, 0xba06cb79),
+     TOBN(0x1ff08623, 0xe7d954e5), TOBN(0x6ef2c5fb, 0x24cf71c3),
+     TOBN(0xb2c063d2, 0x67978453), TOBN(0xa0cf3796, 0x1d654af8),
+     TOBN(0x7cb242ea, 0x7ebdaa37), TOBN(0x206e0b10, 0xb86747e0),
+     TOBN(0x481dae5f, 0xd5ecfefc), TOBN(0x07084fd8, 0xc2bff8fc),
+     TOBN(0x8040a01a, 0xea324596), TOBN(0x4c646980, 0xd4de4036),
+     TOBN(0x9eb8ab4e, 0xd65abfc3), TOBN(0xe01cb91f, 0x13541ec7),
+     TOBN(0x8f029adb, 0xfd695012), TOBN(0x9ae28483, 0x3c7569ec),
+     TOBN(0xa5614c9e, 0xa66d80a1), TOBN(0x680a3e44, 0x75f5f911),
+     TOBN(0x0c07b14d, 0xceba4fc1), TOBN(0x891c285b, 0xa13071c1),
+     TOBN(0xcac67ceb, 0x799ece3c), TOBN(0x29b910a9, 0x41e07e27),
+     TOBN(0x66bdb409, 0xf2e43123), TOBN(0x06f8b137, 0x7ac9ecbe),
+     TOBN(0x5981fafd, 0x38547090), TOBN(0x19ab8b9f, 0x85e3415d),
+     TOBN(0xfc28c194, 0xc7e31b27), TOBN(0x843be0aa, 0x6fbcbb42),
+     TOBN(0xf3b1ed43, 0xa6db836c), TOBN(0x2a1330e4, 0x01a45c05),
+     TOBN(0x4f19f3c5, 0x95c1a377), TOBN(0xa85f39d0, 0x44b5ee33),
+     TOBN(0x3da18e6d, 0x4ae52834), TOBN(0x5a403b39, 0x7423dcb0),
+     TOBN(0xbb555e0a, 0xf2374aef), TOBN(0x2ad599c4, 0x1e8ca111),
+     TOBN(0x1b3a2fb9, 0x014b3bf8), TOBN(0x73092684, 0xf66d5007),
+     TOBN(0x079f1426, 0xc4340102), TOBN(0x1827cf81, 0x8fddf4de),
+     TOBN(0xc83605f6, 0xf10ff927), TOBN(0xd3871451, 0x23739fc6),
+     TOBN(0x6d163450, 0xcac1c2cc), TOBN(0x6b521296, 0xa2ec1ac5),
+     TOBN(0x0606c4f9, 0x6e3cb4a5), TOBN(0xe47d3f41, 0x778abff7),
+     TOBN(0x425a8d5e, 0xbe8e3a45), TOBN(0x53ea9e97, 0xa6102160),
+     TOBN(0x477a106e, 0x39cbb688), TOBN(0x532401d2, 0xf3386d32),
+     TOBN(0x8e564f64, 0xb1b9b421), TOBN(0xca9b8388, 0x81dad33f),
+     TOBN(0xb1422b4e, 0x2093913e), TOBN(0x533d2f92, 0x69bc8112),
+     TOBN(0x3fa017be, 0xebe7b2c7), TOBN(0xb2767c4a, 0xcaf197c6),
+     TOBN(0xc925ff87, 0xaedbae9f), TOBN(0x7daf0eb9, 0x36880a54),
+     TOBN(0x9284ddf5, 0x9c4d0e71), TOBN(0x1581cf93, 0x316f8cf5),
+     TOBN(0x3eeca887, 0x3ac1f452), TOBN(0xb417fce9, 0xfb6aeffe),
+     TOBN(0xa5918046, 0xeefb8dc3), TOBN(0x73d318ac, 0x02209400),
+     TOBN(0xe800400f, 0x728693e5), TOBN(0xe87d814b, 0x339927ed),
+     TOBN(0x93e94d3b, 0x57ea9910), TOBN(0xff8a35b6, 0x2245fb69),
+     TOBN(0x043853d7, 0x7f200d34), TOBN(0x470f1e68, 0x0f653ce1),
+     TOBN(0x81ac05bd, 0x59a06379), TOBN(0xa14052c2, 0x03930c29),
+     TOBN(0x6b72fab5, 0x26bc2797), TOBN(0x13670d16, 0x99f16771),
+     TOBN(0x00170052, 0x1e3e48d1), TOBN(0x978fe401, 0xb7adf678),
+     TOBN(0x55ecfb92, 0xd41c5dd4), TOBN(0x5ff8e247, 0xc7b27da5),
+     TOBN(0xe7518272, 0x013fb606), TOBN(0x5768d7e5, 0x2f547a3c),
+     TOBN(0xbb24eaa3, 0x60017a5f), TOBN(0x6b18e6e4, 0x9c64ce9b),
+     TOBN(0xc225c655, 0x103dde07), TOBN(0xfc3672ae, 0x7592f7ea),
+     TOBN(0x9606ad77, 0xd06283a1), TOBN(0x542fc650, 0xe4d59d99),
+     TOBN(0xabb57c49, 0x2a40e7c2), TOBN(0xac948f13, 0xa8db9f55),
+     TOBN(0x6d4c9682, 0xb04465c3), TOBN(0xe3d062fa, 0x6468bd15),
+     TOBN(0xa51729ac, 0x5f318d7e), TOBN(0x1fc87df6, 0x9eb6fc95),
+     TOBN(0x63d146a8, 0x0591f652), TOBN(0xa861b8f7, 0x589621aa),
+     TOBN(0x59f5f15a, 0xce31348c), TOBN(0x8f663391, 0x440da6da),
+     TOBN(0xcfa778ac, 0xb591ffa3), TOBN(0x027ca9c5, 0x4cdfebce),
+     TOBN(0xbe8e05a5, 0x444ea6b3), TOBN(0x8aab4e69, 0xa78d8254),
+     TOBN(0x2437f04f, 0xb474d6b8), TOBN(0x6597ffd4, 0x045b3855),
+     TOBN(0xbb0aea4e, 0xca47ecaa), TOBN(0x568aae83, 0x85c7ebfc),
+     TOBN(0x0e966e64, 0xc73b2383), TOBN(0x49eb3447, 0xd17d8762),
+     TOBN(0xde107821, 0x8da05dab), TOBN(0x443d8baa, 0x016b7236),
+     TOBN(0x163b63a5, 0xea7610d6), TOBN(0xe47e4185, 0xce1ca979),
+     TOBN(0xae648b65, 0x80baa132), TOBN(0xebf53de2, 0x0e0d5b64),
+     TOBN(0x8d3bfcb4, 0xd3c8c1ca), TOBN(0x0d914ef3, 0x5d04b309),
+     TOBN(0x55ef6415, 0x3de7d395), TOBN(0xbde1666f, 0x26b850e8),
+     TOBN(0xdbe1ca6e, 0xd449ab19), TOBN(0x8902b322, 0xe89a2672),
+     TOBN(0xb1674b7e, 0xdacb7a53), TOBN(0x8e9faf6e, 0xf52523ff),
+     TOBN(0x6ba535da, 0x9a85788b), TOBN(0xd21f03ae, 0xbd0626d4),
+     TOBN(0x099f8c47, 0xe873dc64), TOBN(0xcda8564d, 0x018ec97e),
+     TOBN(0x3e8d7a5c, 0xde92c68c), TOBN(0x78e035a1, 0x73323cc4),
+     TOBN(0x3ef26275, 0xf880ff7c), TOBN(0xa4ee3dff, 0x273eedaa),
+     TOBN(0x58823507, 0xaf4e18f8), TOBN(0x967ec9b5, 0x0672f328),
+     TOBN(0x9ded19d9, 0x559d3186), TOBN(0x5e2ab3de, 0x6cdce39c),
+     TOBN(0xabad6e4d, 0x11c226df), TOBN(0xf9783f43, 0x87723014),
+     TOBN(0x9a49a0cf, 0x1a885719), TOBN(0xfc0c1a5a, 0x90da9dbf),
+     TOBN(0x8bbaec49, 0x571d92ac), TOBN(0x569e85fe, 0x4692517f),
+     TOBN(0x8333b014, 0xa14ea4af), TOBN(0x32f2a62f, 0x12e5c5ad),
+     TOBN(0x98c2ce3a, 0x06d89b85), TOBN(0xb90741aa, 0x2ff77a08),
+     TOBN(0x2530defc, 0x01f795a2), TOBN(0xd6e5ba0b, 0x84b3c199),
+     TOBN(0x7d8e8451, 0x12e4c936), TOBN(0xae419f7d, 0xbd0be17b),
+     TOBN(0xa583fc8c, 0x22262bc9), TOBN(0x6b842ac7, 0x91bfe2bd),
+     TOBN(0x33cef4e9, 0x440d6827), TOBN(0x5f69f4de, 0xef81fb14),
+     TOBN(0xf16cf6f6, 0x234fbb92), TOBN(0x76ae3fc3, 0xd9e7e158),
+     TOBN(0x4e89f6c2, 0xe9740b33), TOBN(0x677bc85d, 0x4962d6a1),
+     TOBN(0x6c6d8a7f, 0x68d10d15), TOBN(0x5f9a7224, 0x0257b1cd),
+     TOBN(0x7096b916, 0x4ad85961), TOBN(0x5f8c47f7, 0xe657ab4a),
+     TOBN(0xde57d7d0, 0xf7461d7e), TOBN(0x7eb6094d, 0x80ce5ee2),
+     TOBN(0x0b1e1dfd, 0x34190547), TOBN(0x8a394f43, 0xf05dd150),
+     TOBN(0x0a9eb24d, 0x97df44e6), TOBN(0x78ca06bf, 0x87675719),
+     TOBN(0x6f0b3462, 0x6ffeec22), TOBN(0x9d91bcea, 0x36cdd8fb),
+     TOBN(0xac83363c, 0xa105be47), TOBN(0x81ba76c1, 0x069710e3),
+     TOBN(0x3d1b24cb, 0x28c682c6), TOBN(0x27f25228, 0x8612575b),
+     TOBN(0xb587c779, 0xe8e66e98), TOBN(0x7b0c03e9, 0x405eb1fe),
+     TOBN(0xfdf0d030, 0x15b548e7), TOBN(0xa8be76e0, 0x38b36af7),
+     TOBN(0x4cdab04a, 0x4f310c40), TOBN(0x6287223e, 0xf47ecaec),
+     TOBN(0x678e6055, 0x8b399320), TOBN(0x61fe3fa6, 0xc01e4646),
+     TOBN(0xc482866b, 0x03261a5e), TOBN(0xdfcf45b8, 0x5c2f244a),
+     TOBN(0x8fab9a51, 0x2f684b43), TOBN(0xf796c654, 0xc7220a66),
+     TOBN(0x1d90707e, 0xf5afa58f), TOBN(0x2c421d97, 0x4fdbe0de),
+     TOBN(0xc4f4cda3, 0xaf2ebc2f), TOBN(0xa0af843d, 0xcb4efe24),
+     TOBN(0x53b857c1, 0x9ccd10b1), TOBN(0xddc9d1eb, 0x914d3e04),
+     TOBN(0x7bdec8bb, 0x62771deb), TOBN(0x829277aa, 0x91c5aa81),
+     TOBN(0x7af18dd6, 0x832391ae), TOBN(0x1740f316, 0xc71a84ca),}
+    ,
+    {TOBN(0x8928e99a, 0xeeaf8c49), TOBN(0xee7aa73d, 0x6e24d728),
+     TOBN(0x4c5007c2, 0xe72b156c), TOBN(0x5fcf57c5, 0xed408a1d),
+     TOBN(0x9f719e39, 0xb6057604), TOBN(0x7d343c01, 0xc2868bbf),
+     TOBN(0x2cca254b, 0x7e103e2d), TOBN(0xe6eb38a9, 0xf131bea2),
+     TOBN(0xb33e624f, 0x8be762b4), TOBN(0x2a9ee4d1, 0x058e3413),
+     TOBN(0x968e6369, 0x67d805fa), TOBN(0x9848949b, 0x7db8bfd7),
+     TOBN(0x5308d7e5, 0xd23a8417), TOBN(0x892f3b1d, 0xf3e29da5),
+     TOBN(0xc95c139e, 0x3dee471f), TOBN(0x8631594d, 0xd757e089),
+     TOBN(0xe0c82a3c, 0xde918dcc), TOBN(0x2e7b5994, 0x26fdcf4b),
+     TOBN(0x82c50249, 0x32cb1b2d), TOBN(0xea613a9d, 0x7657ae07),
+     TOBN(0xc2eb5f6c, 0xf1fdc9f7), TOBN(0xb6eae8b8, 0x879fe682),
+     TOBN(0x253dfee0, 0x591cbc7f), TOBN(0x000da713, 0x3e1290e6),
+     TOBN(0x1083e2ea, 0x1f095615), TOBN(0x0a28ad77, 0x14e68c33),
+     TOBN(0x6bfc0252, 0x3d8818be), TOBN(0xb585113a, 0xf35850cd),
+     TOBN(0x7d935f0b, 0x30df8aa1), TOBN(0xaddda07c, 0x4ab7e3ac),
+     TOBN(0x92c34299, 0x552f00cb), TOBN(0xc33ed1de, 0x2909df6c),
+     TOBN(0x22c2195d, 0x80e87766), TOBN(0x9e99e6d8, 0x9ddf4ac0),
+     TOBN(0x09642e4e, 0x65e74934), TOBN(0x2610ffa2, 0xff1ff241),
+     TOBN(0x4d1d47d4, 0x751c8159), TOBN(0x697b4985, 0xaf3a9363),
+     TOBN(0x0318ca46, 0x87477c33), TOBN(0xa90cb565, 0x9441eff3),
+     TOBN(0x58bb3848, 0x36f024cb), TOBN(0x85be1f77, 0x36016168),
+     TOBN(0x6c59587c, 0xdc7e07f1), TOBN(0x191be071, 0xaf1d8f02),
+     TOBN(0xbf169fa5, 0xcca5e55c), TOBN(0x3864ba3c, 0xf7d04eac),
+     TOBN(0x915e367f, 0x8d7d05db), TOBN(0xb48a876d, 0xa6549e5d),
+     TOBN(0xef89c656, 0x580e40a2), TOBN(0xf194ed8c, 0x728068bc),
+     TOBN(0x74528045, 0xa47990c9), TOBN(0xf53fc7d7, 0x5e1a4649),
+     TOBN(0xbec5ae9b, 0x78593e7d), TOBN(0x2cac4ee3, 0x41db65d7),
+     TOBN(0xa8c1eb24, 0x04a3d39b), TOBN(0x53b7d634, 0x03f8f3ef),
+     TOBN(0x2dc40d48, 0x3e07113c), TOBN(0x6e4a5d39, 0x7d8b63ae),
+     TOBN(0x5582a94b, 0x79684c2b), TOBN(0x932b33d4, 0x622da26c),
+     TOBN(0xf534f651, 0x0dbbf08d), TOBN(0x211d07c9, 0x64c23a52),
+     TOBN(0x0eeece0f, 0xee5bdc9b), TOBN(0xdf178168, 0xf7015558),
+     TOBN(0xd4294635, 0x0a712229), TOBN(0x93cbe448, 0x09273f8c),
+     TOBN(0x00b095ef, 0x8f13bc83), TOBN(0xbb741972, 0x8798978c),
+     TOBN(0x9d7309a2, 0x56dbe6e7), TOBN(0xe578ec56, 0x5a5d39ec),
+     TOBN(0x3961151b, 0x851f9a31), TOBN(0x2da7715d, 0xe5709eb4),
+     TOBN(0x867f3017, 0x53dfabf0), TOBN(0x728d2078, 0xb8e39259),
+     TOBN(0x5c75a0cd, 0x815d9958), TOBN(0xf84867a6, 0x16603be1),
+     TOBN(0xc865b13d, 0x70e35b1c), TOBN(0x02414468, 0x19b03e2c),
+     TOBN(0xe46041da, 0xac1f3121), TOBN(0x7c9017ad, 0x6f028a7c),
+     TOBN(0xabc96de9, 0x0a482873), TOBN(0x4265d6b1, 0xb77e54d4),
+     TOBN(0x68c38e79, 0xa57d88e7), TOBN(0xd461d766, 0x9ce82de3),
+     TOBN(0x817a9ec5, 0x64a7e489), TOBN(0xcc5675cd, 0xa0def5f2),
+     TOBN(0x9a00e785, 0x985d494e), TOBN(0xc626833f, 0x1b03514a),
+     TOBN(0xabe7905a, 0x83cdd60e), TOBN(0x50602fb5, 0xa1170184),
+     TOBN(0x689886cd, 0xb023642a), TOBN(0xd568d090, 0xa6e1fb00),
+     TOBN(0x5b1922c7, 0x0259217f), TOBN(0x93831cd9, 0xc43141e4),
+     TOBN(0xdfca3587, 0x0c95f86e), TOBN(0xdec2057a, 0x568ae828),
+     TOBN(0xc44ea599, 0xf98a759a), TOBN(0x55a0a7a2, 0xf7c23c1d),
+     TOBN(0xd5ffb6e6, 0x94c4f687), TOBN(0x3563cce2, 0x12848478),
+     TOBN(0x812b3517, 0xe7b1fbe1), TOBN(0x8a7dc979, 0x4f7338e0),
+     TOBN(0x211ecee9, 0x52d048db), TOBN(0x2eea4056, 0xc86ea3b8),
+     TOBN(0xd8cb68a7, 0xba772b34), TOBN(0xe16ed341, 0x5f4e2541),
+     TOBN(0x9b32f6a6, 0x0fec14db), TOBN(0xeee376f7, 0x391698be),
+     TOBN(0xe9a7aa17, 0x83674c02), TOBN(0x65832f97, 0x5843022a),
+     TOBN(0x29f3a8da, 0x5ba4990f), TOBN(0x79a59c3a, 0xfb8e3216),
+     TOBN(0x9cdc4d2e, 0xbd19bb16), TOBN(0xc6c7cfd0, 0xb3262d86),
+     TOBN(0xd4ce14d0, 0x969c0b47), TOBN(0x1fa352b7, 0x13e56128),
+     TOBN(0x383d55b8, 0x973db6d3), TOBN(0x71836850, 0xe8e5b7bf),
+     TOBN(0xc7714596, 0xe6bb571f), TOBN(0x259df31f, 0x2d5b2dd2),
+     TOBN(0x568f8925, 0x913cc16d), TOBN(0x18bc5b6d, 0xe1a26f5a),
+     TOBN(0xdfa413be, 0xf5f499ae), TOBN(0xf8835dec, 0xc3f0ae84),
+     TOBN(0xb6e60bd8, 0x65a40ab0), TOBN(0x65596439, 0x194b377e),
+     TOBN(0xbcd85625, 0x92084a69), TOBN(0x5ce433b9, 0x4f23ede0),
+     TOBN(0xe8e8f04f, 0x6ad65143), TOBN(0x11511827, 0xd6e14af6),
+     TOBN(0x3d390a10, 0x8295c0c7), TOBN(0x71e29ee4, 0x621eba16),
+     TOBN(0xa588fc09, 0x63717b46), TOBN(0x02be02fe, 0xe06ad4a2),
+     TOBN(0x931558c6, 0x04c22b22), TOBN(0xbb4d4bd6, 0x12f3c849),
+     TOBN(0x54a4f496, 0x20efd662), TOBN(0x92ba6d20, 0xc5952d14),
+     TOBN(0x2db8ea1e, 0xcc9784c2), TOBN(0x81cc10ca, 0x4b353644),
+     TOBN(0x40b570ad, 0x4b4d7f6c), TOBN(0x5c9f1d96, 0x84a1dcd2),
+     TOBN(0x01379f81, 0x3147e797), TOBN(0xe5c6097b, 0x2bd499f5),
+     TOBN(0x40dcafa6, 0x328e5e20), TOBN(0xf7b5244a, 0x54815550),
+     TOBN(0xb9a4f118, 0x47bfc978), TOBN(0x0ea0e79f, 0xd25825b1),
+     TOBN(0xa50f96eb, 0x646c7ecf), TOBN(0xeb811493, 0x446dea9d),
+     TOBN(0x2af04677, 0xdfabcf69), TOBN(0xbe3a068f, 0xc713f6e8),
+     TOBN(0x860d523d, 0x42e06189), TOBN(0xbf077941, 0x4e3aff13),
+     TOBN(0x0b616dca, 0xc1b20650), TOBN(0xe66dd6d1, 0x2131300d),
+     TOBN(0xd4a0fd67, 0xff99abde), TOBN(0xc9903550, 0xc7aac50d),
+     TOBN(0x022ecf8b, 0x7c46b2d7), TOBN(0x3333b1e8, 0x3abf92af),
+     TOBN(0x11cc113c, 0x6c491c14), TOBN(0x05976688, 0x80dd3f88),
+     TOBN(0xf5b4d9e7, 0x29d932ed), TOBN(0xe982aad8, 0xa2c38b6d),
+     TOBN(0x6f925347, 0x8be0dcf0), TOBN(0x700080ae, 0x65ca53f2),
+     TOBN(0xd8131156, 0x443ca77f), TOBN(0xe92d6942, 0xec51f984),
+     TOBN(0xd2a08af8, 0x85dfe9ae), TOBN(0xd825d9a5, 0x4d2a86ca),
+     TOBN(0x2c53988d, 0x39dff020), TOBN(0xf38b135a, 0x430cdc40),
+     TOBN(0x0c918ae0, 0x62a7150b), TOBN(0xf31fd8de, 0x0c340e9b),
+     TOBN(0xafa0e7ae, 0x4dbbf02e), TOBN(0x5847fb2a, 0x5eba6239),
+     TOBN(0x6b1647dc, 0xdccbac8b), TOBN(0xb642aa78, 0x06f485c8),
+     TOBN(0x873f3765, 0x7038ecdf), TOBN(0x2ce5e865, 0xfa49d3fe),
+     TOBN(0xea223788, 0xc98c4400), TOBN(0x8104a8cd, 0xf1fa5279),
+     TOBN(0xbcf7cc7a, 0x06becfd7), TOBN(0x49424316, 0xc8f974ae),
+     TOBN(0xc0da65e7, 0x84d6365d), TOBN(0xbcb7443f, 0x8f759fb8),
+     TOBN(0x35c712b1, 0x7ae81930), TOBN(0x80428dff, 0x4c6e08ab),
+     TOBN(0xf19dafef, 0xa4faf843), TOBN(0xced8538d, 0xffa9855f),
+     TOBN(0x20ac409c, 0xbe3ac7ce), TOBN(0x358c1fb6, 0x882da71e),
+     TOBN(0xafa9c0e5, 0xfd349961), TOBN(0x2b2cfa51, 0x8421c2fc),
+     TOBN(0x2a80db17, 0xf3a28d38), TOBN(0xa8aba539, 0x5d138e7e),
+     TOBN(0x52012d1d, 0x6e96eb8d), TOBN(0x65d8dea0, 0xcbaf9622),
+     TOBN(0x57735447, 0xb264f56c), TOBN(0xbeebef3f, 0x1b6c8da2),
+     TOBN(0xfc346d98, 0xce785254), TOBN(0xd50e8d72, 0xbb64a161),
+     TOBN(0xc03567c7, 0x49794add), TOBN(0x15a76065, 0x752c7ef6),
+     TOBN(0x59f3a222, 0x961f23d6), TOBN(0x378e4438, 0x73ecc0b0),
+     TOBN(0xc74be434, 0x5a82fde4), TOBN(0xae509af2, 0xd8b9cf34),
+     TOBN(0x4a61ee46, 0x577f44a1), TOBN(0xe09b748c, 0xb611deeb),
+     TOBN(0xc0481b2c, 0xf5f7b884), TOBN(0x35626678, 0x61acfa6b),
+     TOBN(0x37f4c518, 0xbf8d21e6), TOBN(0x22d96531, 0xb205a76d),
+     TOBN(0x37fb85e1, 0x954073c0), TOBN(0xbceafe4f, 0x65b3a567),
+     TOBN(0xefecdef7, 0xbe42a582), TOBN(0xd3fc6080, 0x65046be6),
+     TOBN(0xc9af13c8, 0x09e8dba9), TOBN(0x1e6c9847, 0x641491ff),
+     TOBN(0x3b574925, 0xd30c31f7), TOBN(0xb7eb72ba, 0xac2a2122),
+     TOBN(0x776a0dac, 0xef0859e7), TOBN(0x06fec314, 0x21900942),
+     TOBN(0x2464bc10, 0xf8c22049), TOBN(0x9bfbcce7, 0x875ebf69),
+     TOBN(0xd7a88e2a, 0x4336326b), TOBN(0xda05261c, 0x5bc2acfa),
+     TOBN(0xc29f5bdc, 0xeba7efc8), TOBN(0x471237ca, 0x25dbbf2e),
+     TOBN(0xa72773f2, 0x2975f127), TOBN(0xdc744e8e, 0x04d0b326),
+     TOBN(0x38a7ed16, 0xa56edb73), TOBN(0x64357e37, 0x2c007e70),
+     TOBN(0xa167d15b, 0x5080b400), TOBN(0x07b41164, 0x23de4be1),
+     TOBN(0xb2d91e32, 0x74c89883), TOBN(0x3c162821, 0x2882e7ed),
+     TOBN(0xad6b36ba, 0x7503e482), TOBN(0x48434e8e, 0x0ea34331),
+     TOBN(0x79f4f24f, 0x2c7ae0b9), TOBN(0xc46fbf81, 0x1939b44a),
+     TOBN(0x76fefae8, 0x56595eb1), TOBN(0x417b66ab, 0xcd5f29c7),
+     TOBN(0x5f2332b2, 0xc5ceec20), TOBN(0xd69661ff, 0xe1a1cae2),
+     TOBN(0x5ede7e52, 0x9b0286e6), TOBN(0x9d062529, 0xe276b993),
+     TOBN(0x324794b0, 0x7e50122b), TOBN(0xdd744f8b, 0x4af07ca5),
+     TOBN(0x30a12f08, 0xd63fc97b), TOBN(0x39650f1a, 0x76626d9d),
+     TOBN(0x101b47f7, 0x1fa38477), TOBN(0x3d815f19, 0xd4dc124f),
+     TOBN(0x1569ae95, 0xb26eb58a), TOBN(0xc3cde188, 0x95fb1887),
+     TOBN(0x54e9f37b, 0xf9539a48), TOBN(0xb0100e06, 0x7408c1a5),
+     TOBN(0x821d9811, 0xea580cbb), TOBN(0x8af52d35, 0x86e50c56),
+     TOBN(0xdfbd9d47, 0xdbbf698b), TOBN(0x2961a1ea, 0x03dc1c73),
+     TOBN(0x203d38f8, 0xe76a5df8), TOBN(0x08a53a68, 0x6def707a),
+     TOBN(0x26eefb48, 0x1bee45d4), TOBN(0xb3cee346, 0x3c688036),
+     TOBN(0x463c5315, 0xc42f2469), TOBN(0x19d84d2e, 0x81378162),
+     TOBN(0x22d7c3c5, 0x1c4d349f), TOBN(0x65965844, 0x163d59c5),
+     TOBN(0xcf198c56, 0xb8abceae), TOBN(0x6fb1fb1b, 0x628559d5),
+     TOBN(0x8bbffd06, 0x07bf8fe3), TOBN(0x46259c58, 0x3467734b),
+     TOBN(0xd8953cea, 0x35f7f0d3), TOBN(0x1f0bece2, 0xd65b0ff1),
+     TOBN(0xf7d5b4b3, 0xf3c72914), TOBN(0x29e8ea95, 0x3cb53389),
+     TOBN(0x4a365626, 0x836b6d46), TOBN(0xe849f910, 0xea174fde),
+     TOBN(0x7ec62fbb, 0xf4737f21), TOBN(0xd8dba5ab, 0x6209f5ac),
+     TOBN(0x24b5d7a9, 0xa5f9adbe), TOBN(0x707d28f7, 0xa61dc768),
+     TOBN(0x7711460b, 0xcaa999ea), TOBN(0xba7b174d, 0x1c92e4cc),
+     TOBN(0x3c4bab66, 0x18d4bf2d), TOBN(0xb8f0c980, 0xeb8bd279),
+     TOBN(0x024bea9a, 0x324b4737), TOBN(0xfba9e423, 0x32a83bca),
+     TOBN(0x6e635643, 0xa232dced), TOBN(0x99619367, 0x2571c8ba),
+     TOBN(0xe8c9f357, 0x54b7032b), TOBN(0xf936b3ba, 0x2442d54a),
+     TOBN(0x2263f0f0, 0x8290c65a), TOBN(0x48989780, 0xee2c7fdb),
+     TOBN(0xadc5d55a, 0x13d4f95e), TOBN(0x737cff85, 0xad9b8500),
+     TOBN(0x271c557b, 0x8a73f43d), TOBN(0xbed617a4, 0xe18bc476),
+     TOBN(0x66245401, 0x7dfd8ab2), TOBN(0xae7b89ae, 0x3a2870aa),
+     TOBN(0x1b555f53, 0x23a7e545), TOBN(0x6791e247, 0xbe057e4c),
+     TOBN(0x860136ad, 0x324fa34d), TOBN(0xea111447, 0x4cbeae28),
+     TOBN(0x023a4270, 0xbedd3299), TOBN(0x3d5c3a7f, 0xc1c35c34),
+     TOBN(0xb0f6db67, 0x8d0412d2), TOBN(0xd92625e2, 0xfcdc6b9a),
+     TOBN(0x92ae5ccc, 0x4e28a982), TOBN(0xea251c36, 0x47a3ce7e),
+     TOBN(0x9d658932, 0x790691bf), TOBN(0xed610589, 0x06b736ae),
+     TOBN(0x712c2f04, 0xc0d63b6e), TOBN(0x5cf06fd5, 0xc63d488f),
+     TOBN(0x97363fac, 0xd9588e41), TOBN(0x1f9bf762, 0x2b93257e),
+     TOBN(0xa9d1ffc4, 0x667acace), TOBN(0x1cf4a1aa, 0x0a061ecf),
+     TOBN(0x40e48a49, 0xdc1818d0), TOBN(0x0643ff39, 0xa3621ab0),
+     TOBN(0x5768640c, 0xe39ef639), TOBN(0x1fc099ea, 0x04d86854),
+     TOBN(0x9130b9c3, 0xeccd28fd), TOBN(0xd743cbd2, 0x7eec54ab),
+     TOBN(0x052b146f, 0xe5b475b6), TOBN(0x058d9a82, 0x900a7d1f),
+     TOBN(0x65e02292, 0x91262b72), TOBN(0x96f924f9, 0xbb0edf03),
+     TOBN(0x5cfa59c8, 0xfe206842), TOBN(0xf6037004, 0x5eafa720),
+     TOBN(0x5f30699e, 0x18d7dd96), TOBN(0x381e8782, 0xcbab2495),
+     TOBN(0x91669b46, 0xdd8be949), TOBN(0xb40606f5, 0x26aae8ef),
+     TOBN(0x2812b839, 0xfc6751a4), TOBN(0x16196214, 0xfba800ef),
+     TOBN(0x4398d5ca, 0x4c1a2875), TOBN(0x720c00ee, 0x653d8349),
+     TOBN(0xc2699eb0, 0xd820007c), TOBN(0x880ee660, 0xa39b5825),
+     TOBN(0x70694694, 0x471f6984), TOBN(0xf7d16ea8, 0xe3dda99a),
+     TOBN(0x28d675b2, 0xc0519a23), TOBN(0x9ebf94fe, 0x4f6952e3),
+     TOBN(0xf28bb767, 0xa2294a8a), TOBN(0x85512b4d, 0xfe0af3f5),
+     TOBN(0x18958ba8, 0x99b16a0d), TOBN(0x95c2430c, 0xba7548a7),
+     TOBN(0xb30d1b10, 0xa16be615), TOBN(0xe3ebbb97, 0x85bfb74c),
+     TOBN(0xa3273cfe, 0x18549fdb), TOBN(0xf6e200bf, 0x4fcdb792),
+     TOBN(0x54a76e18, 0x83aba56c), TOBN(0x73ec66f6, 0x89ef6aa2),
+     TOBN(0x8d17add7, 0xd1b9a305), TOBN(0xa959c5b9, 0xb7ae1b9d),
+     TOBN(0x88643522, 0x6bcc094a), TOBN(0xcc5616c4, 0xd7d429b9),
+     TOBN(0xa6dada01, 0xe6a33f7c), TOBN(0xc6217a07, 0x9d4e70ad),
+     TOBN(0xd619a818, 0x09c15b7c), TOBN(0xea06b329, 0x0e80c854),
+     TOBN(0x174811ce, 0xa5f5e7b9), TOBN(0x66dfc310, 0x787c65f4),
+     TOBN(0x4ea7bd69, 0x3316ab54), TOBN(0xc12c4acb, 0x1dcc0f70),
+     TOBN(0xe4308d1a, 0x1e407dd9), TOBN(0xe8a3587c, 0x91afa997),
+     TOBN(0xea296c12, 0xab77b7a5), TOBN(0xb5ad49e4, 0x673c0d52),
+     TOBN(0x40f9b2b2, 0x7006085a), TOBN(0xa88ff340, 0x87bf6ec2),
+     TOBN(0x978603b1, 0x4e3066a6), TOBN(0xb3f99fc2, 0xb5e486e2),
+     TOBN(0x07b53f5e, 0xb2e63645), TOBN(0xbe57e547, 0x84c84232),
+     TOBN(0xd779c216, 0x7214d5cf), TOBN(0x617969cd, 0x029a3aca),
+     TOBN(0xd17668cd, 0x8a7017a0), TOBN(0x77b4d19a, 0xbe9b7ee8),
+     TOBN(0x58fd0e93, 0x9c161776), TOBN(0xa8c4f4ef, 0xd5968a72),
+     TOBN(0x296071cc, 0x67b3de77), TOBN(0xae3c0b8e, 0x634f7905),
+     TOBN(0x67e440c2, 0x8a7100c9), TOBN(0xbb8c3c1b, 0xeb4b9b42),
+     TOBN(0x6d71e8ea, 0xc51b3583), TOBN(0x7591f5af, 0x9525e642),
+     TOBN(0xf73a2f7b, 0x13f509f3), TOBN(0x618487aa, 0x5619ac9b),
+     TOBN(0x3a72e5f7, 0x9d61718a), TOBN(0x00413bcc, 0x7592d28c),
+     TOBN(0x7d9b11d3, 0x963c35cf), TOBN(0x77623bcf, 0xb90a46ed),
+     TOBN(0xdeef273b, 0xdcdd2a50), TOBN(0x4a741f9b, 0x0601846e),
+     TOBN(0x33b89e51, 0x0ec6e929), TOBN(0xcb02319f, 0x8b7f22cd),
+     TOBN(0xbbe1500d, 0x084bae24), TOBN(0x2f0ae8d7, 0x343d2693),
+     TOBN(0xacffb5f2, 0x7cdef811), TOBN(0xaa0c030a, 0x263fb94f),
+     TOBN(0x6eef0d61, 0xa0f442de), TOBN(0xf92e1817, 0x27b139d3),
+     TOBN(0x1ae6deb7, 0x0ad8bc28), TOBN(0xa89e38dc, 0xc0514130),
+     TOBN(0x81eeb865, 0xd2fdca23), TOBN(0x5a15ee08, 0xcc8ef895),
+     TOBN(0x768fa10a, 0x01905614), TOBN(0xeff5b8ef, 0x880ee19b),
+     TOBN(0xf0c0cabb, 0xcb1c8a0e), TOBN(0x2e1ee9cd, 0xb8c838f9),
+     TOBN(0x0587d8b8, 0x8a4a14c0), TOBN(0xf6f27896, 0x2ff698e5),
+     TOBN(0xed38ef1c, 0x89ee6256), TOBN(0xf44ee1fe, 0x6b353b45),
+     TOBN(0x9115c0c7, 0x70e903b3), TOBN(0xc78ec0a1, 0x818f31df),
+     TOBN(0x6c003324, 0xb7dccbc6), TOBN(0xd96dd1f3, 0x163bbc25),
+     TOBN(0x33aa82dd, 0x5cedd805), TOBN(0x123aae4f, 0x7f7eb2f1),
+     TOBN(0x1723fcf5, 0xa26262cd), TOBN(0x1f7f4d5d, 0x0060ebd5),
+     TOBN(0xf19c5c01, 0xb2eaa3af), TOBN(0x2ccb9b14, 0x9790accf),
+     TOBN(0x1f9c1cad, 0x52324aa6), TOBN(0x63200526, 0x7247df54),
+     TOBN(0x5732fe42, 0xbac96f82), TOBN(0x52fe771f, 0x01a1c384),
+     TOBN(0x546ca13d, 0xb1001684), TOBN(0xb56b4eee, 0xa1709f75),
+     TOBN(0x266545a9, 0xd5db8672), TOBN(0xed971c90, 0x1e8f3cfb),
+     TOBN(0x4e7d8691, 0xe3a07b29), TOBN(0x7570d9ec, 0xe4b696b9),
+     TOBN(0xdc5fa067, 0x7bc7e9ae), TOBN(0x68b44caf, 0xc82c4844),
+     TOBN(0x519d34b3, 0xbf44da80), TOBN(0x283834f9, 0x5ab32e66),
+     TOBN(0x6e608797, 0x6278a000), TOBN(0x1e62960e, 0x627312f6),
+     TOBN(0x9b87b27b, 0xe6901c55), TOBN(0x80e78538, 0x24fdbc1f),
+     TOBN(0xbbbc0951, 0x2facc27d), TOBN(0x06394239, 0xac143b5a),
+     TOBN(0x35bb4a40, 0x376c1944), TOBN(0x7cb62694, 0x63da1511),
+     TOBN(0xafd29161, 0xb7148a3b), TOBN(0xa6f9d9ed, 0x4e2ea2ee),
+     TOBN(0x15dc2ca2, 0x880dd212), TOBN(0x903c3813, 0xa61139a9),
+     TOBN(0x2aa7b46d, 0x6c0f8785), TOBN(0x36ce2871, 0x901c60ff),
+     TOBN(0xc683b028, 0xe10d9c12), TOBN(0x7573baa2, 0x032f33d3),
+     TOBN(0x87a9b1f6, 0x67a31b58), TOBN(0xfd3ed11a, 0xf4ffae12),
+     TOBN(0x83dcaa9a, 0x0cb2748e), TOBN(0x8239f018, 0x5d6fdf16),
+     TOBN(0xba67b49c, 0x72753941), TOBN(0x2beec455, 0xc321cb36),
+     TOBN(0x88015606, 0x3f8b84ce), TOBN(0x76417083, 0x8d38c86f),
+     TOBN(0x054f1ca7, 0x598953dd), TOBN(0xc939e110, 0x4e8e7429),
+     TOBN(0x9b1ac2b3, 0x5a914f2f), TOBN(0x39e35ed3, 0xe74b8f9c),
+     TOBN(0xd0debdb2, 0x781b2fb0), TOBN(0x1585638f, 0x2d997ba2),
+     TOBN(0x9c4b646e, 0x9e2fce99), TOBN(0x68a21081, 0x1e80857f),
+     TOBN(0x06d54e44, 0x3643b52a), TOBN(0xde8d6d63, 0x0d8eb843),
+     TOBN(0x70321563, 0x42146a0a), TOBN(0x8ba826f2, 0x5eaa3622),
+     TOBN(0x227a58bd, 0x86138787), TOBN(0x43b6c03c, 0x10281d37),
+     TOBN(0x6326afbb, 0xb54dde39), TOBN(0x744e5e8a, 0xdb6f2d5f),
+     TOBN(0x48b2a99a, 0xcff158e1), TOBN(0xa93c8fa0, 0xef87918f),
+     TOBN(0x2182f956, 0xde058c5c), TOBN(0x216235d2, 0x936f9e7a),
+     TOBN(0xace0c0db, 0xd2e31e67), TOBN(0xc96449bf, 0xf23ac3e7),
+     TOBN(0x7e9a2874, 0x170693bd), TOBN(0xa28e14fd, 0xa45e6335),
+     TOBN(0x5757f6b3, 0x56427344), TOBN(0x822e4556, 0xacf8edf9),
+     TOBN(0x2b7a6ee2, 0xe6a285cd), TOBN(0x5866f211, 0xa9df3af0),
+     TOBN(0x40dde2dd, 0xf845b844), TOBN(0x986c3726, 0x110e5e49),
+     TOBN(0x73680c2a, 0xf7172277), TOBN(0x57b94f0f, 0x0cccb244),
+     TOBN(0xbdff7267, 0x2d438ca7), TOBN(0xbad1ce11, 0xcf4663fd),
+     TOBN(0x9813ed9d, 0xd8f71cae), TOBN(0xf43272a6, 0x961fdaa6),
+     TOBN(0xbeff0119, 0xbd6d1637), TOBN(0xfebc4f91, 0x30361978),
+     TOBN(0x02b37a95, 0x2f41deff), TOBN(0x0e44a59a, 0xe63b89b7),
+     TOBN(0x673257dc, 0x143ff951), TOBN(0x19c02205, 0xd752baf4),
+     TOBN(0x46c23069, 0xc4b7d692), TOBN(0x2e6392c3, 0xfd1502ac),
+     TOBN(0x6057b1a2, 0x1b220846), TOBN(0xe51ff946, 0x0c1b5b63),}
+    ,
+    {TOBN(0x6e85cb51, 0x566c5c43), TOBN(0xcff9c919, 0x3597f046),
+     TOBN(0x9354e90c, 0x4994d94a), TOBN(0xe0a39332, 0x2147927d),
+     TOBN(0x8427fac1, 0x0dc1eb2b), TOBN(0x88cfd8c2, 0x2ff319fa),
+     TOBN(0xe2d4e684, 0x01965274), TOBN(0xfa2e067d, 0x67aaa746),
+     TOBN(0xb6d92a7f, 0x3e5f9f11), TOBN(0x9afe153a, 0xd6cb3b8e),
+     TOBN(0x4d1a6dd7, 0xddf800bd), TOBN(0xf6c13cc0, 0xcaf17e19),
+     TOBN(0x15f6c58e, 0x325fc3ee), TOBN(0x71095400, 0xa31dc3b2),
+     TOBN(0x168e7c07, 0xafa3d3e7), TOBN(0x3f8417a1, 0x94c7ae2d),
+     TOBN(0xec234772, 0x813b230d), TOBN(0x634d0f5f, 0x17344427),
+     TOBN(0x11548ab1, 0xd77fc56a), TOBN(0x7fab1750, 0xce06af77),
+     TOBN(0xb62c10a7, 0x4f7c4f83), TOBN(0xa7d2edc4, 0x220a67d9),
+     TOBN(0x1c404170, 0x921209a0), TOBN(0x0b9815a0, 0xface59f0),
+     TOBN(0x2842589b, 0x319540c3), TOBN(0x18490f59, 0xa283d6f8),
+     TOBN(0xa2731f84, 0xdaae9fcb), TOBN(0x3db6d960, 0xc3683ba0),
+     TOBN(0xc85c63bb, 0x14611069), TOBN(0xb19436af, 0x0788bf05),
+     TOBN(0x905459df, 0x347460d2), TOBN(0x73f6e094, 0xe11a7db1),
+     TOBN(0xdc7f938e, 0xb6357f37), TOBN(0xc5d00f79, 0x2bd8aa62),
+     TOBN(0xc878dcb9, 0x2ca979fc), TOBN(0x37e83ed9, 0xeb023a99),
+     TOBN(0x6b23e273, 0x1560bf3d), TOBN(0x1086e459, 0x1d0fae61),
+     TOBN(0x78248316, 0x9a9414bd), TOBN(0x1b956bc0, 0xf0ea9ea1),
+     TOBN(0x7b85bb91, 0xc31b9c38), TOBN(0x0c5aa90b, 0x48ef57b5),
+     TOBN(0xdedeb169, 0xaf3bab6f), TOBN(0xe610ad73, 0x2d373685),
+     TOBN(0xf13870df, 0x02ba8e15), TOBN(0x0337edb6, 0x8ca7f771),
+     TOBN(0xe4acf747, 0xb62c036c), TOBN(0xd921d576, 0xb6b94e81),
+     TOBN(0xdbc86439, 0x2c422f7a), TOBN(0xfb635362, 0xed348898),
+     TOBN(0x83084668, 0xc45bfcd1), TOBN(0xc357c9e3, 0x2b315e11),
+     TOBN(0xb173b540, 0x5b2e5b8c), TOBN(0x7e946931, 0xe102b9a4),
+     TOBN(0x17c890eb, 0x7b0fb199), TOBN(0xec225a83, 0xd61b662b),
+     TOBN(0xf306a3c8, 0xee3c76cb), TOBN(0x3cf11623, 0xd32a1f6e),
+     TOBN(0xe6d5ab64, 0x6863e956), TOBN(0x3b8a4cbe, 0x5c005c26),
+     TOBN(0xdcd529a5, 0x9ce6bb27), TOBN(0xc4afaa52, 0x04d4b16f),
+     TOBN(0xb0624a26, 0x7923798d), TOBN(0x85e56df6, 0x6b307fab),
+     TOBN(0x0281893c, 0x2bf29698), TOBN(0x91fc19a4, 0xd7ce7603),
+     TOBN(0x75a5dca3, 0xad9a558f), TOBN(0x40ceb3fa, 0x4d50bf77),
+     TOBN(0x1baf6060, 0xbc9ba369), TOBN(0x927e1037, 0x597888c2),
+     TOBN(0xd936bf19, 0x86a34c07), TOBN(0xd4cf10c1, 0xc34ae980),
+     TOBN(0x3a3e5334, 0x859dd614), TOBN(0x9c475b5b, 0x18d0c8ee),
+     TOBN(0x63080d1f, 0x07cd51d5), TOBN(0xc9c0d0a6, 0xb88b4326),
+     TOBN(0x1ac98691, 0xc234296f), TOBN(0x2a0a83a4, 0x94887fb6),
+     TOBN(0x56511427, 0x0cea9cf2), TOBN(0x5230a6e8, 0xa24802f5),
+     TOBN(0xf7a2bf0f, 0x72e3d5c1), TOBN(0x37717446, 0x4f21439e),
+     TOBN(0xfedcbf25, 0x9ce30334), TOBN(0xe0030a78, 0x7ce202f9),
+     TOBN(0x6f2d9ebf, 0x1202e9ca), TOBN(0xe79dde6c, 0x75e6e591),
+     TOBN(0xf52072af, 0xf1dac4f8), TOBN(0x6c8d087e, 0xbb9b404d),
+     TOBN(0xad0fc73d, 0xbce913af), TOBN(0x909e587b, 0x458a07cb),
+     TOBN(0x1300da84, 0xd4f00c8a), TOBN(0x425cd048, 0xb54466ac),
+     TOBN(0xb59cb9be, 0x90e9d8bf), TOBN(0x991616db, 0x3e431b0e),
+     TOBN(0xd3aa117a, 0x531aecff), TOBN(0x91af92d3, 0x59f4dc3b),
+     TOBN(0x9b1ec292, 0xe93fda29), TOBN(0x76bb6c17, 0xe97d91bc),
+     TOBN(0x7509d95f, 0xaface1e6), TOBN(0x3653fe47, 0xbe855ae3),
+     TOBN(0x73180b28, 0x0f680e75), TOBN(0x75eefd1b, 0xeeb6c26c),
+     TOBN(0xa4cdf29f, 0xb66d4236), TOBN(0x2d70a997, 0x6b5821d8),
+     TOBN(0x7a3ee207, 0x20445c36), TOBN(0x71d1ac82, 0x59877174),
+     TOBN(0x0fc539f7, 0x949f73e9), TOBN(0xd05cf3d7, 0x982e3081),
+     TOBN(0x8758e20b, 0x7b1c7129), TOBN(0xffadcc20, 0x569e61f2),
+     TOBN(0xb05d3a2f, 0x59544c2d), TOBN(0xbe16f5c1, 0x9fff5e53),
+     TOBN(0x73cf65b8, 0xaad58135), TOBN(0x622c2119, 0x037aa5be),
+     TOBN(0x79373b3f, 0x646fd6a0), TOBN(0x0e029db5, 0x0d3978cf),
+     TOBN(0x8bdfc437, 0x94fba037), TOBN(0xaefbd687, 0x620797a6),
+     TOBN(0x3fa5382b, 0xbd30d38e), TOBN(0x7627cfbf, 0x585d7464),
+     TOBN(0xb2330fef, 0x4e4ca463), TOBN(0xbcef7287, 0x3566cc63),
+     TOBN(0xd161d2ca, 0xcf780900), TOBN(0x135dc539, 0x5b54827d),
+     TOBN(0x638f052e, 0x27bf1bc6), TOBN(0x10a224f0, 0x07dfa06c),
+     TOBN(0xe973586d, 0x6d3321da), TOBN(0x8b0c5738, 0x26152c8f),
+     TOBN(0x07ef4f2a, 0x34606074), TOBN(0x80fe7fe8, 0xa0f7047a),
+     TOBN(0x3d1a8152, 0xe1a0e306), TOBN(0x32cf43d8, 0x88da5222),
+     TOBN(0xbf89a95f, 0x5f02ffe6), TOBN(0x3d9eb9a4, 0x806ad3ea),
+     TOBN(0x012c17bb, 0x79c8e55e), TOBN(0xfdcd1a74, 0x99c81dac),
+     TOBN(0x7043178b, 0xb9556098), TOBN(0x4090a1df, 0x801c3886),
+     TOBN(0x759800ff, 0x9b67b912), TOBN(0x3e5c0304, 0x232620c8),
+     TOBN(0x4b9d3c4b, 0x70dceeca), TOBN(0xbb2d3c15, 0x181f648e),
+     TOBN(0xf981d837, 0x6e33345c), TOBN(0xb626289b, 0x0cf2297a),
+     TOBN(0x766ac659, 0x8baebdcf), TOBN(0x1a28ae09, 0x75df01e5),
+     TOBN(0xb71283da, 0x375876d8), TOBN(0x4865a96d, 0x607b9800),
+     TOBN(0x25dd1bcd, 0x237936b2), TOBN(0x332f4f4b, 0x60417494),
+     TOBN(0xd0923d68, 0x370a2147), TOBN(0x497f5dfb, 0xdc842203),
+     TOBN(0x9dc74cbd, 0x32be5e0f), TOBN(0x7475bcb7, 0x17a01375),
+     TOBN(0x438477c9, 0x50d872b1), TOBN(0xcec67879, 0xffe1d63d),
+     TOBN(0x9b006014, 0xd8578c70), TOBN(0xc9ad99a8, 0x78bb6b8b),
+     TOBN(0x6799008e, 0x11fb3806), TOBN(0xcfe81435, 0xcd44cab3),
+     TOBN(0xa2ee1582, 0x2f4fb344), TOBN(0xb8823450, 0x483fa6eb),
+     TOBN(0x622d323d, 0x652c7749), TOBN(0xd8474a98, 0xbeb0a15b),
+     TOBN(0xe43c154d, 0x5d1c00d0), TOBN(0x7fd581d9, 0x0e3e7aac),
+     TOBN(0x2b44c619, 0x2525ddf8), TOBN(0x67a033eb, 0xb8ae9739),
+     TOBN(0x113ffec1, 0x9ef2d2e4), TOBN(0x1bf6767e, 0xd5a0ea7f),
+     TOBN(0x57fff75e, 0x03714c0a), TOBN(0xa23c422e, 0x0a23e9ee),
+     TOBN(0xdd5f6b2d, 0x540f83af), TOBN(0xc2c2c27e, 0x55ea46a7),
+     TOBN(0xeb6b4246, 0x672a1208), TOBN(0xd13599f7, 0xae634f7a),
+     TOBN(0xcf914b5c, 0xd7b32c6e), TOBN(0x61a5a640, 0xeaf61814),
+     TOBN(0x8dc3df8b, 0x208a1bbb), TOBN(0xef627fd6, 0xb6d79aa5),
+     TOBN(0x44232ffc, 0xc4c86bc8), TOBN(0xe6f9231b, 0x061539fe),
+     TOBN(0x1d04f25a, 0x958b9533), TOBN(0x180cf934, 0x49e8c885),
+     TOBN(0x89689595, 0x9884aaf7), TOBN(0xb1959be3, 0x07b348a6),
+     TOBN(0x96250e57, 0x3c147c87), TOBN(0xae0efb3a, 0xdd0c61f8),
+     TOBN(0xed00745e, 0xca8c325e), TOBN(0x3c911696, 0xecff3f70),
+     TOBN(0x73acbc65, 0x319ad41d), TOBN(0x7b01a020, 0xf0b1c7ef),
+     TOBN(0xea32b293, 0x63a1483f), TOBN(0x89eabe71, 0x7a248f96),
+     TOBN(0x9c6231d3, 0x343157e5), TOBN(0x93a375e5, 0xdf3c546d),
+     TOBN(0xe76e9343, 0x6a2afe69), TOBN(0xc4f89100, 0xe166c88e),
+     TOBN(0x248efd0d, 0x4f872093), TOBN(0xae0eb3ea, 0x8fe0ea61),
+     TOBN(0xaf89790d, 0x9d79046e), TOBN(0x4d650f2d, 0x6cee0976),
+     TOBN(0xa3935d9a, 0x43071eca), TOBN(0x66fcd2c9, 0x283b0bfe),
+     TOBN(0x0e665eb5, 0x696605f1), TOBN(0xe77e5d07, 0xa54cd38d),
+     TOBN(0x90ee050a, 0x43d950cf), TOBN(0x86ddebda, 0xd32e69b5),
+     TOBN(0x6ad94a3d, 0xfddf7415), TOBN(0xf7fa1309, 0x3f6e8d5a),
+     TOBN(0xc4831d1d, 0xe9957f75), TOBN(0x7de28501, 0xd5817447),
+     TOBN(0x6f1d7078, 0x9e2aeb6b), TOBN(0xba2b9ff4, 0xf67a53c2),
+     TOBN(0x36963767, 0xdf9defc3), TOBN(0x479deed3, 0x0d38022c),
+     TOBN(0xd2edb89b, 0x3a8631e8), TOBN(0x8de855de, 0x7a213746),
+     TOBN(0xb2056cb7, 0xb00c5f11), TOBN(0xdeaefbd0, 0x2c9b85e4),
+     TOBN(0x03f39a8d, 0xd150892d), TOBN(0x37b84686, 0x218b7985),
+     TOBN(0x36296dd8, 0xb7375f1a), TOBN(0x472cd4b1, 0xb78e898e),
+     TOBN(0x15dff651, 0xe9f05de9), TOBN(0xd4045069, 0x2ce98ba9),
+     TOBN(0x8466a7ae, 0x9b38024c), TOBN(0xb910e700, 0xe5a6b5ef),
+     TOBN(0xae1c56ea, 0xb3aa8f0d), TOBN(0xbab2a507, 0x7eee74a6),
+     TOBN(0x0dca11e2, 0x4b4c4620), TOBN(0xfd896e2e, 0x4c47d1f4),
+     TOBN(0xeb45ae53, 0x308fbd93), TOBN(0x46cd5a2e, 0x02c36fda),
+     TOBN(0x6a3d4e90, 0xbaa48385), TOBN(0xdd55e62e, 0x9dbe9960),
+     TOBN(0xa1406aa0, 0x2a81ede7), TOBN(0x6860dd14, 0xf9274ea7),
+     TOBN(0xcfdcb0c2, 0x80414f86), TOBN(0xff410b10, 0x22f94327),
+     TOBN(0x5a33cc38, 0x49ad467b), TOBN(0xefb48b6c, 0x0a7335f1),
+     TOBN(0x14fb54a4, 0xb153a360), TOBN(0x604aa9d2, 0xb52469cc),
+     TOBN(0x5e9dc486, 0x754e48e9), TOBN(0x693cb455, 0x37471e8e),
+     TOBN(0xfb2fd7cd, 0x8d3b37b6), TOBN(0x63345e16, 0xcf09ff07),
+     TOBN(0x9910ba6b, 0x23a5d896), TOBN(0x1fe19e35, 0x7fe4364e),
+     TOBN(0x6e1da8c3, 0x9a33c677), TOBN(0x15b4488b, 0x29fd9fd0),
+     TOBN(0x1f439254, 0x1a1f22bf), TOBN(0x920a8a70, 0xab8163e8),
+     TOBN(0x3fd1b249, 0x07e5658e), TOBN(0xf2c4f79c, 0xb6ec839b),
+     TOBN(0x1abbc3d0, 0x4aa38d1b), TOBN(0x3b0db35c, 0xb5d9510e),
+     TOBN(0x1754ac78, 0x3e60dec0), TOBN(0x53272fd7, 0xea099b33),
+     TOBN(0x5fb0494f, 0x07a8e107), TOBN(0x4a89e137, 0x6a8191fa),
+     TOBN(0xa113b7f6, 0x3c4ad544), TOBN(0x88a2e909, 0x6cb9897b),
+     TOBN(0x17d55de3, 0xb44a3f84), TOBN(0xacb2f344, 0x17c6c690),
+     TOBN(0x32088168, 0x10232390), TOBN(0xf2e8a61f, 0x6c733bf7),
+     TOBN(0xa774aab6, 0x9c2d7652), TOBN(0xfb5307e3, 0xed95c5bc),
+     TOBN(0xa05c73c2, 0x4981f110), TOBN(0x1baae31c, 0xa39458c9),
+     TOBN(0x1def185b, 0xcbea62e7), TOBN(0xe8ac9eae, 0xeaf63059),
+     TOBN(0x098a8cfd, 0x9921851c), TOBN(0xd959c3f1, 0x3abe2f5b),
+     TOBN(0xa4f19525, 0x20e40ae5), TOBN(0x320789e3, 0x07a24aa1),
+     TOBN(0x259e6927, 0x7392b2bc), TOBN(0x58f6c667, 0x1918668b),
+     TOBN(0xce1db2bb, 0xc55d2d8b), TOBN(0x41d58bb7, 0xf4f6ca56),
+     TOBN(0x7650b680, 0x8f877614), TOBN(0x905e16ba, 0xf4c349ed),
+     TOBN(0xed415140, 0xf661acac), TOBN(0x3b8784f0, 0xcb2270af),
+     TOBN(0x3bc280ac, 0x8a402cba), TOBN(0xd53f7146, 0x0937921a),
+     TOBN(0xc03c8ee5, 0xe5681e83), TOBN(0x62126105, 0xf6ac9e4a),
+     TOBN(0x9503a53f, 0x936b1a38), TOBN(0x3d45e2d4, 0x782fecbd),
+     TOBN(0x69a5c439, 0x76e8ae98), TOBN(0xb53b2eeb, 0xbfb4b00e),
+     TOBN(0xf1674712, 0x72386c89), TOBN(0x30ca34a2, 0x4268bce4),
+     TOBN(0x7f1ed86c, 0x78341730), TOBN(0x8ef5beb8, 0xb525e248),
+     TOBN(0xbbc489fd, 0xb74fbf38), TOBN(0x38a92a0e, 0x91a0b382),
+     TOBN(0x7a77ba3f, 0x22433ccf), TOBN(0xde8362d6, 0xa29f05a9),
+     TOBN(0x7f6a30ea, 0x61189afc), TOBN(0x693b5505, 0x59ef114f),
+     TOBN(0x50266bc0, 0xcd1797a1), TOBN(0xea17b47e, 0xf4b7af2d),
+     TOBN(0xd6c4025c, 0x3df9483e), TOBN(0x8cbb9d9f, 0xa37b18c9),
+     TOBN(0x91cbfd9c, 0x4d8424cf), TOBN(0xdb7048f1, 0xab1c3506),
+     TOBN(0x9eaf641f, 0x028206a3), TOBN(0xf986f3f9, 0x25bdf6ce),
+     TOBN(0x262143b5, 0x224c08dc), TOBN(0x2bbb09b4, 0x81b50c91),
+     TOBN(0xc16ed709, 0xaca8c84f), TOBN(0xa6210d9d, 0xb2850ca8),
+     TOBN(0x6d8df67a, 0x09cb54d6), TOBN(0x91eef6e0, 0x500919a4),
+     TOBN(0x90f61381, 0x0f132857), TOBN(0x9acede47, 0xf8d5028b),
+     TOBN(0x844d1b71, 0x90b771c3), TOBN(0x563b71e4, 0xba6426be),
+     TOBN(0x2efa2e83, 0xbdb802ff), TOBN(0x3410cbab, 0xab5b4a41),
+     TOBN(0x555b2d26, 0x30da84dd), TOBN(0xd0711ae9, 0xee1cc29a),
+     TOBN(0xcf3e8c60, 0x2f547792), TOBN(0x03d7d5de, 0xdc678b35),
+     TOBN(0x071a2fa8, 0xced806b8), TOBN(0x222e6134, 0x697f1478),
+     TOBN(0xdc16fd5d, 0xabfcdbbf), TOBN(0x44912ebf, 0x121b53b8),
+     TOBN(0xac943674, 0x2496c27c), TOBN(0x8ea3176c, 0x1ffc26b0),
+     TOBN(0xb6e224ac, 0x13debf2c), TOBN(0x524cc235, 0xf372a832),
+     TOBN(0xd706e1d8, 0x9f6f1b18), TOBN(0x2552f005, 0x44cce35b),
+     TOBN(0x8c8326c2, 0xa88e31fc), TOBN(0xb5468b2c, 0xf9552047),
+     TOBN(0xce683e88, 0x3ff90f2b), TOBN(0x77947bdf, 0x2f0a5423),
+     TOBN(0xd0a1b28b, 0xed56e328), TOBN(0xaee35253, 0xc20134ac),
+     TOBN(0x7e98367d, 0x3567962f), TOBN(0x379ed61f, 0x8188bffb),
+     TOBN(0x73bba348, 0xfaf130a1), TOBN(0x6c1f75e1, 0x904ed734),
+     TOBN(0x18956642, 0x3b4a79fc), TOBN(0xf20bc83d, 0x54ef4493),
+     TOBN(0x836d425d, 0x9111eca1), TOBN(0xe5b5c318, 0x009a8dcf),
+     TOBN(0x3360b25d, 0x13221bc5), TOBN(0x707baad2, 0x6b3eeaf7),
+     TOBN(0xd7279ed8, 0x743a95a1), TOBN(0x7450a875, 0x969e809f),
+     TOBN(0x32b6bd53, 0xe5d0338f), TOBN(0x1e77f7af, 0x2b883bbc),
+     TOBN(0x90da12cc, 0x1063ecd0), TOBN(0xe2697b58, 0xc315be47),
+     TOBN(0x2771a5bd, 0xda85d534), TOBN(0x53e78c1f, 0xff980eea),
+     TOBN(0xadf1cf84, 0x900385e7), TOBN(0x7d3b14f6, 0xc9387b62),
+     TOBN(0x170e74b0, 0xcb8f2bd2), TOBN(0x2d50b486, 0x827fa993),
+     TOBN(0xcdbe8c9a, 0xf6f32bab), TOBN(0x55e906b0, 0xc3b93ab8),
+     TOBN(0x747f22fc, 0x8fe280d1), TOBN(0xcd8e0de5, 0xb2e114ab),
+     TOBN(0x5ab7dbeb, 0xe10b68b0), TOBN(0x9dc63a9c, 0xa480d4b2),
+     TOBN(0x78d4bc3b, 0x4be1495f), TOBN(0x25eb3db8, 0x9359122d),
+     TOBN(0x3f8ac05b, 0x0809cbdc), TOBN(0xbf4187bb, 0xd37c702f),
+     TOBN(0x84cea069, 0x1416a6a5), TOBN(0x8f860c79, 0x43ef881c),
+     TOBN(0x41311f8a, 0x38038a5d), TOBN(0xe78c2ec0, 0xfc612067),
+     TOBN(0x494d2e81, 0x5ad73581), TOBN(0xb4cc9e00, 0x59604097),
+     TOBN(0xff558aec, 0xf3612cba), TOBN(0x35beef7a, 0x9e36c39e),
+     TOBN(0x1845c7cf, 0xdbcf41b9), TOBN(0x5703662a, 0xaea997c0),
+     TOBN(0x8b925afe, 0xe402f6d8), TOBN(0xd0a1b1ae, 0x4dd72162),
+     TOBN(0x9f47b375, 0x03c41c4b), TOBN(0xa023829b, 0x0391d042),
+     TOBN(0x5f5045c3, 0x503b8b0a), TOBN(0x123c2688, 0x98c010e5),
+     TOBN(0x324ec0cc, 0x36ba06ee), TOBN(0xface3115, 0x3dd2cc0c),
+     TOBN(0xb364f3be, 0xf333e91f), TOBN(0xef8aff73, 0x28e832b0),
+     TOBN(0x1e9bad04, 0x2d05841b), TOBN(0x42f0e3df, 0x356a21e2),
+     TOBN(0xa3270bcb, 0x4add627e), TOBN(0xb09a8158, 0xd322e711),
+     TOBN(0x86e326a1, 0x0fee104a), TOBN(0xad7788f8, 0x3703f65d),
+     TOBN(0x7e765430, 0x47bc4833), TOBN(0x6cee582b, 0x2b9b893a),
+     TOBN(0x9cd2a167, 0xe8f55a7b), TOBN(0xefbee3c6, 0xd9e4190d),
+     TOBN(0x33ee7185, 0xd40c2e9d), TOBN(0x844cc9c5, 0xa380b548),
+     TOBN(0x323f8ecd, 0x66926e04), TOBN(0x0001e38f, 0x8110c1ba),
+     TOBN(0x8dbcac12, 0xfc6a7f07), TOBN(0xd65e1d58, 0x0cec0827),
+     TOBN(0xd2cd4141, 0xbe76ca2d), TOBN(0x7895cf5c, 0xe892f33a),
+     TOBN(0x956d230d, 0x367139d2), TOBN(0xa91abd3e, 0xd012c4c1),
+     TOBN(0x34fa4883, 0x87eb36bf), TOBN(0xc5f07102, 0x914b8fb4),
+     TOBN(0x90f0e579, 0xadb9c95f), TOBN(0xfe6ea8cb, 0x28888195),
+     TOBN(0x7b9b5065, 0xedfa9284), TOBN(0x6c510bd2, 0x2b8c8d65),
+     TOBN(0xd7b8ebef, 0xcbe8aafd), TOBN(0xedb3af98, 0x96b1da07),
+     TOBN(0x28ff779d, 0x6295d426), TOBN(0x0c4f6ac7, 0x3fa3ad7b),
+     TOBN(0xec44d054, 0x8b8e2604), TOBN(0x9b32a66d, 0x8b0050e1),
+     TOBN(0x1f943366, 0xf0476ce2), TOBN(0x7554d953, 0xa602c7b4),
+     TOBN(0xbe35aca6, 0x524f2809), TOBN(0xb6881229, 0xfd4edbea),
+     TOBN(0xe8cd0c8f, 0x508efb63), TOBN(0x9eb5b5c8, 0x6abcefc7),
+     TOBN(0xf5621f5f, 0xb441ab4f), TOBN(0x79e6c046, 0xb76a2b22),
+     TOBN(0x74a4792c, 0xe37a1f69), TOBN(0xcbd252cb, 0x03542b60),
+     TOBN(0x785f65d5, 0xb3c20bd3), TOBN(0x8dea6143, 0x4fabc60c),
+     TOBN(0x45e21446, 0xde673629), TOBN(0x57f7aa1e, 0x703c2d21),
+     TOBN(0xa0e99b7f, 0x98c868c7), TOBN(0x4e42f66d, 0x8b641676),
+     TOBN(0x602884dc, 0x91077896), TOBN(0xa0d690cf, 0xc2c9885b),
+     TOBN(0xfeb4da33, 0x3b9a5187), TOBN(0x5f789598, 0x153c87ee),
+     TOBN(0x2192dd47, 0x52b16dba), TOBN(0xdeefc0e6, 0x3524c1b1),
+     TOBN(0x465ea76e, 0xe4383693), TOBN(0x79401711, 0x361b8d98),
+     TOBN(0xa5f9ace9, 0xf21a15cb), TOBN(0x73d26163, 0xefee9aeb),
+     TOBN(0xcca844b3, 0xe677016c), TOBN(0x6c122b07, 0x57eaee06),
+     TOBN(0xb782dce7, 0x15f09690), TOBN(0x508b9b12, 0x2dfc0fc9),
+     TOBN(0x9015ab4b, 0x65d89fc6), TOBN(0x5e79dab7, 0xd6d5bb0f),
+     TOBN(0x64f021f0, 0x6c775aa2), TOBN(0xdf09d8cc, 0x37c7eca1),
+     TOBN(0x9a761367, 0xef2fa506), TOBN(0xed4ca476, 0x5b81eec6),
+     TOBN(0x262ede36, 0x10bbb8b5), TOBN(0x0737ce83, 0x0641ada3),
+     TOBN(0x4c94288a, 0xe9831ccc), TOBN(0x487fc1ce, 0x8065e635),
+     TOBN(0xb13d7ab3, 0xb8bb3659), TOBN(0xdea5df3e, 0x855e4120),
+     TOBN(0xb9a18573, 0x85eb0244), TOBN(0x1a1b8ea3, 0xa7cfe0a3),
+     TOBN(0x3b837119, 0x67b0867c), TOBN(0x8d5e0d08, 0x9d364520),
+     TOBN(0x52dccc1e, 0xd930f0e3), TOBN(0xefbbcec7, 0xbf20bbaf),
+     TOBN(0x99cffcab, 0x0263ad10), TOBN(0xd8199e6d, 0xfcd18f8a),
+     TOBN(0x64e2773f, 0xe9f10617), TOBN(0x0079e8e1, 0x08704848),
+     TOBN(0x1169989f, 0x8a342283), TOBN(0x8097799c, 0xa83012e6),
+     TOBN(0xece966cb, 0x8a6a9001), TOBN(0x93b3afef, 0x072ac7fc),
+     TOBN(0xe6893a2a, 0x2db3d5ba), TOBN(0x263dc462, 0x89bf4fdc),
+     TOBN(0x8852dfc9, 0xe0396673), TOBN(0x7ac70895, 0x3af362b6),
+     TOBN(0xbb9cce4d, 0x5c2f342b), TOBN(0xbf80907a, 0xb52d7aae),
+     TOBN(0x97f3d3cd, 0x2161bcd0), TOBN(0xb25b0834, 0x0962744d),
+     TOBN(0xc5b18ea5, 0x6c3a1dda), TOBN(0xfe4ec7eb, 0x06c92317),
+     TOBN(0xb787b890, 0xad1c4afe), TOBN(0xdccd9a92, 0x0ede801a),
+     TOBN(0x9ac6ddda, 0xdb58da1f), TOBN(0x22bbc12f, 0xb8cae6ee),
+     TOBN(0xc6f8bced, 0x815c4a43), TOBN(0x8105a92c, 0xf96480c7),
+     TOBN(0x0dc3dbf3, 0x7a859d51), TOBN(0xe3ec7ce6, 0x3041196b),
+     TOBN(0xd9f64b25, 0x0d1067c9), TOBN(0xf2321321, 0x3d1f8dd8),
+     TOBN(0x8b5c619c, 0x76497ee8), TOBN(0x5d2b0ac6, 0xc717370e),
+     TOBN(0x98204cb6, 0x4fcf68e1), TOBN(0x0bdec211, 0x62bc6792),
+     TOBN(0x6973ccef, 0xa63b1011), TOBN(0xf9e3fa97, 0xe0de1ac5),
+     TOBN(0x5efb693e, 0x3d0e0c8b), TOBN(0x037248e9, 0xd2d4fcb4),}
+    ,
+    {TOBN(0x80802dc9, 0x1ec34f9e), TOBN(0xd8772d35, 0x33810603),
+     TOBN(0x3f06d66c, 0x530cb4f3), TOBN(0x7be5ed0d, 0xc475c129),
+     TOBN(0xcb9e3c19, 0x31e82b10), TOBN(0xc63d2857, 0xc9ff6b4c),
+     TOBN(0xb92118c6, 0x92a1b45e), TOBN(0x0aec4414, 0x7285bbca),
+     TOBN(0xfc189ae7, 0x1e29a3ef), TOBN(0xcbe906f0, 0x4c93302e),
+     TOBN(0xd0107914, 0xceaae10e), TOBN(0xb7a23f34, 0xb68e19f8),
+     TOBN(0xe9d875c2, 0xefd2119d), TOBN(0x03198c6e, 0xfcadc9c8),
+     TOBN(0x65591bf6, 0x4da17113), TOBN(0x3cf0bbf8, 0x3d443038),
+     TOBN(0xae485bb7, 0x2b724759), TOBN(0x945353e1, 0xb2d4c63a),
+     TOBN(0x82159d07, 0xde7d6f2c), TOBN(0x389caef3, 0x4ec5b109),
+     TOBN(0x4a8ebb53, 0xdb65ef14), TOBN(0x2dc2cb7e, 0xdd99de43),
+     TOBN(0x816fa3ed, 0x83f2405f), TOBN(0x73429bb9, 0xc14208a3),
+     TOBN(0xb618d590, 0xb01e6e27), TOBN(0x047e2ccd, 0xe180b2dc),
+     TOBN(0xd1b299b5, 0x04aea4a9), TOBN(0x412c9e1e, 0x9fa403a4),
+     TOBN(0x88d28a36, 0x79407552), TOBN(0x49c50136, 0xf332b8e3),
+     TOBN(0x3a1b6fcc, 0xe668de19), TOBN(0x178851bc, 0x75122b97),
+     TOBN(0xb1e13752, 0xfb85fa4c), TOBN(0xd61257ce, 0x383c8ce9),
+     TOBN(0xd43da670, 0xd2f74dae), TOBN(0xa35aa23f, 0xbf846bbb),
+     TOBN(0x5e74235d, 0x4421fc83), TOBN(0xf6df8ee0, 0xc363473b),
+     TOBN(0x34d7f52a, 0x3c4aa158), TOBN(0x50d05aab, 0x9bc6d22e),
+     TOBN(0x8c56e735, 0xa64785f4), TOBN(0xbc56637b, 0x5f29cd07),
+     TOBN(0x53b2bb80, 0x3ee35067), TOBN(0x50235a0f, 0xdc919270),
+     TOBN(0x191ab6d8, 0xf2c4aa65), TOBN(0xc3475831, 0x8396023b),
+     TOBN(0x80400ba5, 0xf0f805ba), TOBN(0x8881065b, 0x5ec0f80f),
+     TOBN(0xc370e522, 0xcc1b5e83), TOBN(0xde2d4ad1, 0x860b8bfb),
+     TOBN(0xad364df0, 0x67b256df), TOBN(0x8f12502e, 0xe0138997),
+     TOBN(0x503fa0dc, 0x7783920a), TOBN(0xe80014ad, 0xc0bc866a),
+     TOBN(0x3f89b744, 0xd3064ba6), TOBN(0x03511dcd, 0xcba5dba5),
+     TOBN(0x197dd46d, 0x95a7b1a2), TOBN(0x9c4e7ad6, 0x3c6341fb),
+     TOBN(0x426eca29, 0x484c2ece), TOBN(0x9211e489, 0xde7f4f8a),
+     TOBN(0x14997f6e, 0xc78ef1f4), TOBN(0x2b2c0910, 0x06574586),
+     TOBN(0x17286a6e, 0x1c3eede8), TOBN(0x25f92e47, 0x0f60e018),
+     TOBN(0x805c5646, 0x31890a36), TOBN(0x703ef600, 0x57feea5b),
+     TOBN(0x389f747c, 0xaf3c3030), TOBN(0xe0e5daeb, 0x54dd3739),
+     TOBN(0xfe24a4c3, 0xc9c9f155), TOBN(0x7e4bf176, 0xb5393962),
+     TOBN(0x37183de2, 0xaf20bf29), TOBN(0x4a1bd7b5, 0xf95a8c3b),
+     TOBN(0xa83b9699, 0x46191d3d), TOBN(0x281fc8dd, 0x7b87f257),
+     TOBN(0xb18e2c13, 0x54107588), TOBN(0x6372def7, 0x9b2bafe8),
+     TOBN(0xdaf4bb48, 0x0d8972ca), TOBN(0x3f2dd4b7, 0x56167a3f),
+     TOBN(0x1eace32d, 0x84310cf4), TOBN(0xe3bcefaf, 0xe42700aa),
+     TOBN(0x5fe5691e, 0xd785e73d), TOBN(0xa5db5ab6, 0x2ea60467),
+     TOBN(0x02e23d41, 0xdfc6514a), TOBN(0x35e8048e, 0xe03c3665),
+     TOBN(0x3f8b118f, 0x1adaa0f8), TOBN(0x28ec3b45, 0x84ce1a5a),
+     TOBN(0xe8cacc6e, 0x2c6646b8), TOBN(0x1343d185, 0xdbd0e40f),
+     TOBN(0xe5d7f844, 0xcaaa358c), TOBN(0x1a1db7e4, 0x9924182a),
+     TOBN(0xd64cd42d, 0x9c875d9a), TOBN(0xb37b515f, 0x042eeec8),
+     TOBN(0x4d4dd409, 0x7b165fbe), TOBN(0xfc322ed9, 0xe206eff3),
+     TOBN(0x7dee4102, 0x59b7e17e), TOBN(0x55a481c0, 0x8236ca00),
+     TOBN(0x8c885312, 0xc23fc975), TOBN(0x15715806, 0x05d6297b),
+     TOBN(0xa078868e, 0xf78edd39), TOBN(0x956b31e0, 0x03c45e52),
+     TOBN(0x470275d5, 0xff7b33a6), TOBN(0xc8d5dc3a, 0x0c7e673f),
+     TOBN(0x419227b4, 0x7e2f2598), TOBN(0x8b37b634, 0x4c14a975),
+     TOBN(0xd0667ed6, 0x8b11888c), TOBN(0x5e0e8c3e, 0x803e25dc),
+     TOBN(0x34e5d0dc, 0xb987a24a), TOBN(0x9f40ac3b, 0xae920323),
+     TOBN(0x5463de95, 0x34e0f63a), TOBN(0xa128bf92, 0x6b6328f9),
+     TOBN(0x491ccd7c, 0xda64f1b7), TOBN(0x7ef1ec27, 0xc47bde35),
+     TOBN(0xa857240f, 0xa36a2737), TOBN(0x35dc1366, 0x63621bc1),
+     TOBN(0x7a3a6453, 0xd4fb6897), TOBN(0x80f1a439, 0xc929319d),
+     TOBN(0xfc18274b, 0xf8cb0ba0), TOBN(0xb0b53766, 0x8078c5eb),
+     TOBN(0xfb0d4924, 0x1e01d0ef), TOBN(0x50d7c67d, 0x372ab09c),
+     TOBN(0xb4e370af, 0x3aeac968), TOBN(0xe4f7fee9, 0xc4b63266),
+     TOBN(0xb4acd4c2, 0xe3ac5664), TOBN(0xf8910bd2, 0xceb38cbf),
+     TOBN(0x1c3ae50c, 0xc9c0726e), TOBN(0x15309569, 0xd97b40bf),
+     TOBN(0x70884b7f, 0xfd5a5a1b), TOBN(0x3890896a, 0xef8314cd),
+     TOBN(0x58e1515c, 0xa5618c93), TOBN(0xe665432b, 0x77d942d1),
+     TOBN(0xb32181bf, 0xb6f767a8), TOBN(0x753794e8, 0x3a604110),
+     TOBN(0x09afeb7c, 0xe8c0dbcc), TOBN(0x31e02613, 0x598673a3),
+     TOBN(0x5d98e557, 0x7d46db00), TOBN(0xfc21fb8c, 0x9d985b28),
+     TOBN(0xc9040116, 0xb0843e0b), TOBN(0x53b1b3a8, 0x69b04531),
+     TOBN(0xdd1649f0, 0x85d7d830), TOBN(0xbb3bcc87, 0xcb7427e8),
+     TOBN(0x77261100, 0xc93dce83), TOBN(0x7e79da61, 0xa1922a2a),
+     TOBN(0x587a2b02, 0xf3149ce8), TOBN(0x147e1384, 0xde92ec83),
+     TOBN(0x484c83d3, 0xaf077f30), TOBN(0xea78f844, 0x0658b53a),
+     TOBN(0x912076c2, 0x027aec53), TOBN(0xf34714e3, 0x93c8177d),
+     TOBN(0x37ef5d15, 0xc2376c84), TOBN(0x8315b659, 0x3d1aa783),
+     TOBN(0x3a75c484, 0xef852a90), TOBN(0x0ba0c58a, 0x16086bd4),
+     TOBN(0x29688d7a, 0x529a6d48), TOBN(0x9c7f250d, 0xc2f19203),
+     TOBN(0x123042fb, 0x682e2df9), TOBN(0x2b7587e7, 0xad8121bc),
+     TOBN(0x30fc0233, 0xe0182a65), TOBN(0xb82ecf87, 0xe3e1128a),
+     TOBN(0x71682861, 0x93fb098f), TOBN(0x043e21ae, 0x85e9e6a7),
+     TOBN(0xab5b49d6, 0x66c834ea), TOBN(0x3be43e18, 0x47414287),
+     TOBN(0xf40fb859, 0x219a2a47), TOBN(0x0e6559e9, 0xcc58df3c),
+     TOBN(0xfe1dfe8e, 0x0c6615b4), TOBN(0x14abc8fd, 0x56459d70),
+     TOBN(0x7be0fa8e, 0x05de0386), TOBN(0x8e63ef68, 0xe9035c7c),
+     TOBN(0x116401b4, 0x53b31e91), TOBN(0x0cba7ad4, 0x4436b4d8),
+     TOBN(0x9151f9a0, 0x107afd66), TOBN(0xafaca8d0, 0x1f0ee4c4),
+     TOBN(0x75fe5c1d, 0x9ee9761c), TOBN(0x3497a16b, 0xf0c0588f),
+     TOBN(0x3ee2bebd, 0x0304804c), TOBN(0xa8fb9a60, 0xc2c990b9),
+     TOBN(0xd14d32fe, 0x39251114), TOBN(0x36bf25bc, 0xcac73366),
+     TOBN(0xc9562c66, 0xdba7495c), TOBN(0x324d301b, 0x46ad348b),
+     TOBN(0x9f46620c, 0xd670407e), TOBN(0x0ea8d4f1, 0xe3733a01),
+     TOBN(0xd396d532, 0xb0c324e0), TOBN(0x5b211a0e, 0x03c317cd),
+     TOBN(0x090d7d20, 0x5ffe7b37), TOBN(0x3b7f3efb, 0x1747d2da),
+     TOBN(0xa2cb525f, 0xb54fc519), TOBN(0x6e220932, 0xf66a971e),
+     TOBN(0xddc160df, 0xb486d440), TOBN(0x7fcfec46, 0x3fe13465),
+     TOBN(0x83da7e4e, 0x76e4c151), TOBN(0xd6fa48a1, 0xd8d302b5),
+     TOBN(0xc6304f26, 0x5872cd88), TOBN(0x806c1d3c, 0x278b90a1),
+     TOBN(0x3553e725, 0xcaf0bc1c), TOBN(0xff59e603, 0xbb9d8d5c),
+     TOBN(0xa4550f32, 0x7a0b85dd), TOBN(0xdec5720a, 0x93ecc217),
+     TOBN(0x0b88b741, 0x69d62213), TOBN(0x7212f245, 0x5b365955),
+     TOBN(0x20764111, 0xb5cae787), TOBN(0x13cb7f58, 0x1dfd3124),
+     TOBN(0x2dca77da, 0x1175aefb), TOBN(0xeb75466b, 0xffaae775),
+     TOBN(0x74d76f3b, 0xdb6cff32), TOBN(0x7440f37a, 0x61fcda9a),
+     TOBN(0x1bb3ac92, 0xb525028b), TOBN(0x20fbf8f7, 0xa1975f29),
+     TOBN(0x982692e1, 0xdf83097f), TOBN(0x28738f6c, 0x554b0800),
+     TOBN(0xdc703717, 0xa2ce2f2f), TOBN(0x7913b93c, 0x40814194),
+     TOBN(0x04924593, 0x1fe89636), TOBN(0x7b98443f, 0xf78834a6),
+     TOBN(0x11c6ab01, 0x5114a5a1), TOBN(0x60deb383, 0xffba5f4c),
+     TOBN(0x4caa54c6, 0x01a982e6), TOBN(0x1dd35e11, 0x3491cd26),
+     TOBN(0x973c315f, 0x7cbd6b05), TOBN(0xcab00775, 0x52494724),
+     TOBN(0x04659b1f, 0x6565e15a), TOBN(0xbf30f529, 0x8c8fb026),
+     TOBN(0xfc21641b, 0xa8a0de37), TOBN(0xe9c7a366, 0xfa5e5114),
+     TOBN(0xdb849ca5, 0x52f03ad8), TOBN(0xc7e8dbe9, 0x024e35c0),
+     TOBN(0xa1a2bbac, 0xcfc3c789), TOBN(0xbf733e7d, 0x9c26f262),
+     TOBN(0x882ffbf5, 0xb8444823), TOBN(0xb7224e88, 0x6bf8483b),
+     TOBN(0x53023b8b, 0x65bef640), TOBN(0xaabfec91, 0xd4d5f8cd),
+     TOBN(0xa40e1510, 0x079ea1bd), TOBN(0x1ad9addc, 0xd05d5d26),
+     TOBN(0xdb3f2eab, 0x13e68d4f), TOBN(0x1cff1ae2, 0x640f803f),
+     TOBN(0xe0e7b749, 0xd4cee117), TOBN(0x8e9f275b, 0x4036d909),
+     TOBN(0xce34e31d, 0x8f4d4c38), TOBN(0x22b37f69, 0xd75130fc),
+     TOBN(0x83e0f1fd, 0xb4014604), TOBN(0xa8ce9919, 0x89415078),
+     TOBN(0x82375b75, 0x41792efe), TOBN(0x4f59bf5c, 0x97d4515b),
+     TOBN(0xac4f324f, 0x923a277d), TOBN(0xd9bc9b7d, 0x650f3406),
+     TOBN(0xc6fa87d1, 0x8a39bc51), TOBN(0x82588530, 0x5ccc108f),
+     TOBN(0x5ced3c9f, 0x82e4c634), TOBN(0x8efb8314, 0x3a4464f8),
+     TOBN(0xe706381b, 0x7a1dca25), TOBN(0x6cd15a3c, 0x5a2a412b),
+     TOBN(0x9347a8fd, 0xbfcd8fb5), TOBN(0x31db2eef, 0x6e54cd22),
+     TOBN(0xc4aeb11e, 0xf8d8932f), TOBN(0x11e7c1ed, 0x344411af),
+     TOBN(0x2653050c, 0xdc9a151e), TOBN(0x9edbfc08, 0x3bb0a859),
+     TOBN(0x926c81c7, 0xfd5691e7), TOBN(0x9c1b2342, 0x6f39019a),
+     TOBN(0x64a81c8b, 0x7f8474b9), TOBN(0x90657c07, 0x01761819),
+     TOBN(0x390b3331, 0x55e0375a), TOBN(0xc676c626, 0xb6ebc47d),
+     TOBN(0x51623247, 0xb7d6dee8), TOBN(0x0948d927, 0x79659313),
+     TOBN(0x99700161, 0xe9ab35ed), TOBN(0x06cc32b4, 0x8ddde408),
+     TOBN(0x6f2fd664, 0x061ef338), TOBN(0x1606fa02, 0xc202e9ed),
+     TOBN(0x55388bc1, 0x929ba99b), TOBN(0xc4428c5e, 0x1e81df69),
+     TOBN(0xce2028ae, 0xf91b0b2a), TOBN(0xce870a23, 0xf03dfd3f),
+     TOBN(0x66ec2c87, 0x0affe8ed), TOBN(0xb205fb46, 0x284d0c00),
+     TOBN(0xbf5dffe7, 0x44cefa48), TOBN(0xb6fc37a8, 0xa19876d7),
+     TOBN(0xbecfa84c, 0x08b72863), TOBN(0xd7205ff5, 0x2576374f),
+     TOBN(0x80330d32, 0x8887de41), TOBN(0x5de0df0c, 0x869ea534),
+     TOBN(0x13f42753, 0x3c56ea17), TOBN(0xeb1f6069, 0x452b1a78),
+     TOBN(0x50474396, 0xe30ea15c), TOBN(0x575816a1, 0xc1494125),
+     TOBN(0xbe1ce55b, 0xfe6bb38f), TOBN(0xb901a948, 0x96ae30f7),
+     TOBN(0xe5af0f08, 0xd8fc3548), TOBN(0x5010b5d0, 0xd73bfd08),
+     TOBN(0x993d2880, 0x53fe655a), TOBN(0x99f2630b, 0x1c1309fd),
+     TOBN(0xd8677baf, 0xb4e3b76f), TOBN(0x14e51ddc, 0xb840784b),
+     TOBN(0x326c750c, 0xbf0092ce), TOBN(0xc83d306b, 0xf528320f),
+     TOBN(0xc4456715, 0x77d4715c), TOBN(0xd30019f9, 0x6b703235),
+     TOBN(0x207ccb2e, 0xd669e986), TOBN(0x57c824af, 0xf6dbfc28),
+     TOBN(0xf0eb532f, 0xd8f92a23), TOBN(0x4a557fd4, 0x9bb98fd2),
+     TOBN(0xa57acea7, 0xc1e6199a), TOBN(0x0c663820, 0x8b94b1ed),
+     TOBN(0x9b42be8f, 0xf83a9266), TOBN(0xc7741c97, 0x0101bd45),
+     TOBN(0x95770c11, 0x07bd9ceb), TOBN(0x1f50250a, 0x8b2e0744),
+     TOBN(0xf762eec8, 0x1477b654), TOBN(0xc65b900e, 0x15efe59a),
+     TOBN(0x88c96148, 0x9546a897), TOBN(0x7e8025b3, 0xc30b4d7c),
+     TOBN(0xae4065ef, 0x12045cf9), TOBN(0x6fcb2caf, 0x9ccce8bd),
+     TOBN(0x1fa0ba4e, 0xf2cf6525), TOBN(0xf683125d, 0xcb72c312),
+     TOBN(0xa01da4ea, 0xe312410e), TOBN(0x67e28677, 0x6cd8e830),
+     TOBN(0xabd95752, 0x98fb3f07), TOBN(0x05f11e11, 0xeef649a5),
+     TOBN(0xba47faef, 0x9d3472c2), TOBN(0x3adff697, 0xc77d1345),
+     TOBN(0x4761fa04, 0xdd15afee), TOBN(0x64f1f61a, 0xb9e69462),
+     TOBN(0xfa691fab, 0x9bfb9093), TOBN(0x3df8ae8f, 0xa1133dfe),
+     TOBN(0xcd5f8967, 0x58cc710d), TOBN(0xfbb88d50, 0x16c7fe79),
+     TOBN(0x8e011b4c, 0xe88c50d1), TOBN(0x7532e807, 0xa8771c4f),
+     TOBN(0x64c78a48, 0xe2278ee4), TOBN(0x0b283e83, 0x3845072a),
+     TOBN(0x98a6f291, 0x49e69274), TOBN(0xb96e9668, 0x1868b21c),
+     TOBN(0x38f0adc2, 0xb1a8908e), TOBN(0x90afcff7, 0x1feb829d),
+     TOBN(0x9915a383, 0x210b0856), TOBN(0xa5a80602, 0xdef04889),
+     TOBN(0x800e9af9, 0x7c64d509), TOBN(0x81382d0b, 0xb8996f6f),
+     TOBN(0x490eba53, 0x81927e27), TOBN(0x46c63b32, 0x4af50182),
+     TOBN(0x784c5fd9, 0xd3ad62ce), TOBN(0xe4fa1870, 0xf8ae8736),
+     TOBN(0x4ec9d0bc, 0xd7466b25), TOBN(0x84ddbe1a, 0xdb235c65),
+     TOBN(0x5e2645ee, 0x163c1688), TOBN(0x570bd00e, 0x00eba747),
+     TOBN(0xfa51b629, 0x128bfa0f), TOBN(0x92fce1bd, 0x6c1d3b68),
+     TOBN(0x3e7361dc, 0xb66778b1), TOBN(0x9c7d249d, 0x5561d2bb),
+     TOBN(0xa40b28bf, 0x0bbc6229), TOBN(0x1c83c05e, 0xdfd91497),
+     TOBN(0x5f9f5154, 0xf083df05), TOBN(0xbac38b3c, 0xeee66c9d),
+     TOBN(0xf71db7e3, 0xec0dfcfd), TOBN(0xf2ecda8e, 0x8b0a8416),
+     TOBN(0x52fddd86, 0x7812aa66), TOBN(0x2896ef10, 0x4e6f4272),
+     TOBN(0xff27186a, 0x0fe9a745), TOBN(0x08249fcd, 0x49ca70db),
+     TOBN(0x7425a2e6, 0x441cac49), TOBN(0xf4a0885a, 0xece5ff57),
+     TOBN(0x6e2cb731, 0x7d7ead58), TOBN(0xf96cf7d6, 0x1898d104),
+     TOBN(0xafe67c9d, 0x4f2c9a89), TOBN(0x89895a50, 0x1c7bf5bc),
+     TOBN(0xdc7cb8e5, 0x573cecfa), TOBN(0x66497eae, 0xd15f03e6),
+     TOBN(0x6bc0de69, 0x3f084420), TOBN(0x323b9b36, 0xacd532b0),
+     TOBN(0xcfed390a, 0x0115a3c1), TOBN(0x9414c40b, 0x2d65ca0e),
+     TOBN(0x641406bd, 0x2f530c78), TOBN(0x29369a44, 0x833438f2),
+     TOBN(0x996884f5, 0x903fa271), TOBN(0xe6da0fd2, 0xb9da921e),
+     TOBN(0xa6f2f269, 0x5db01e54), TOBN(0x1ee3e9bd, 0x6876214e),
+     TOBN(0xa26e181c, 0xe27a9497), TOBN(0x36d254e4, 0x8e215e04),
+     TOBN(0x42f32a6c, 0x252cabca), TOBN(0x99481487, 0x80b57614),
+     TOBN(0x4c4dfe69, 0x40d9cae1), TOBN(0x05869580, 0x11a10f09),
+     TOBN(0xca287b57, 0x3491b64b), TOBN(0x77862d5d, 0x3fd4a53b),
+     TOBN(0xbf94856e, 0x50349126), TOBN(0x2be30bd1, 0x71c5268f),
+     TOBN(0x10393f19, 0xcbb650a6), TOBN(0x639531fe, 0x778cf9fd),
+     TOBN(0x02556a11, 0xb2935359), TOBN(0xda38aa96, 0xaf8c126e),
+     TOBN(0x47dbe6c2, 0x0960167f), TOBN(0x37bbabb6, 0x501901cd),
+     TOBN(0xb6e979e0, 0x2c947778), TOBN(0xd69a5175, 0x7a1a1dc6),
+     TOBN(0xc3ed5095, 0x9d9faf0c), TOBN(0x4dd9c096, 0x1d5fa5f0),
+     TOBN(0xa0c4304d, 0x64f16ea8), TOBN(0x8b1cac16, 0x7e718623),
+     TOBN(0x0b576546, 0x7c67f03e), TOBN(0x559cf5ad, 0xcbd88c01),
+     TOBN(0x074877bb, 0x0e2af19a), TOBN(0x1f717ec1, 0xa1228c92),
+     TOBN(0x70bcb800, 0x326e8920), TOBN(0xec6e2c5c, 0x4f312804),
+     TOBN(0x426aea7d, 0x3fca4752), TOBN(0xf12c0949, 0x2211f62a),
+     TOBN(0x24beecd8, 0x7be7b6b5), TOBN(0xb77eaf4c, 0x36d7a27d),
+     TOBN(0x154c2781, 0xfda78fd3), TOBN(0x848a83b0, 0x264eeabe),
+     TOBN(0x81287ef0, 0x4ffe2bc4), TOBN(0x7b6d88c6, 0xb6b6fc2a),
+     TOBN(0x805fb947, 0xce417d99), TOBN(0x4b93dcc3, 0x8b916cc4),
+     TOBN(0x72e65bb3, 0x21273323), TOBN(0xbcc1badd, 0x6ea9886e),
+     TOBN(0x0e223011, 0x4bc5ee85), TOBN(0xa561be74, 0xc18ee1e4),
+     TOBN(0x762fd2d4, 0xa6bcf1f1), TOBN(0x50e6a5a4, 0x95231489),
+     TOBN(0xca96001f, 0xa00b500b), TOBN(0x5c098cfc, 0x5d7dcdf5),
+     TOBN(0xa64e2d2e, 0x8c446a85), TOBN(0xbae9bcf1, 0x971f3c62),
+     TOBN(0x4ec22683, 0x8435a2c5), TOBN(0x8ceaed6c, 0x4bad4643),
+     TOBN(0xe9f8fb47, 0xccccf4e3), TOBN(0xbd4f3fa4, 0x1ce3b21e),
+     TOBN(0xd79fb110, 0xa3db3292), TOBN(0xe28a37da, 0xb536c66a),
+     TOBN(0x279ce87b, 0x8e49e6a9), TOBN(0x70ccfe8d, 0xfdcec8e3),
+     TOBN(0x2193e4e0, 0x3ba464b2), TOBN(0x0f39d60e, 0xaca9a398),
+     TOBN(0x7d7932af, 0xf82c12ab), TOBN(0xd8ff50ed, 0x91e7e0f7),
+     TOBN(0xea961058, 0xfa28a7e0), TOBN(0xc726cf25, 0x0bf5ec74),
+     TOBN(0xe74d55c8, 0xdb229666), TOBN(0x0bd9abbf, 0xa57f5799),
+     TOBN(0x7479ef07, 0x4dfc47b3), TOBN(0xd9c65fc3, 0x0c52f91d),
+     TOBN(0x8e0283fe, 0x36a8bde2), TOBN(0xa32a8b5e, 0x7d4b7280),
+     TOBN(0x6a677c61, 0x12e83233), TOBN(0x0fbb3512, 0xdcc9bf28),
+     TOBN(0x562e8ea5, 0x0d780f61), TOBN(0x0db8b22b, 0x1dc4e89c),
+     TOBN(0x0a6fd1fb, 0x89be0144), TOBN(0x8c77d246, 0xca57113b),
+     TOBN(0x4639075d, 0xff09c91c), TOBN(0x5b47b17f, 0x5060824c),
+     TOBN(0x58aea2b0, 0x16287b52), TOBN(0xa1343520, 0xd0cd8eb0),
+     TOBN(0x6148b4d0, 0xc5d58573), TOBN(0xdd2b6170, 0x291c68ae),
+     TOBN(0xa61b3929, 0x1da3b3b7), TOBN(0x5f946d79, 0x08c4ac10),
+     TOBN(0x4105d4a5, 0x7217d583), TOBN(0x5061da3d, 0x25e6de5e),
+     TOBN(0x3113940d, 0xec1b4991), TOBN(0xf12195e1, 0x36f485ae),
+     TOBN(0xa7507fb2, 0x731a2ee0), TOBN(0x95057a8e, 0x6e9e196e),
+     TOBN(0xa3c2c911, 0x2e130136), TOBN(0x97dfbb36, 0x33c60d15),
+     TOBN(0xcaf3c581, 0xb300ee2b), TOBN(0x77f25d90, 0xf4bac8b8),
+     TOBN(0xdb1c4f98, 0x6d840cd6), TOBN(0x471d62c0, 0xe634288c),
+     TOBN(0x8ec2f85e, 0xcec8a161), TOBN(0x41f37cbc, 0xfa6f4ae2),
+     TOBN(0x6793a20f, 0x4b709985), TOBN(0x7a7bd33b, 0xefa8985b),
+     TOBN(0x2c6a3fbd, 0x938e6446), TOBN(0x19042619, 0x2a8d47c1),
+     TOBN(0x16848667, 0xcc36975f), TOBN(0x02acf168, 0x9d5f1dfb),
+     TOBN(0x62d41ad4, 0x613baa94), TOBN(0xb56fbb92, 0x9f684670),
+     TOBN(0xce610d0d, 0xe9e40569), TOBN(0x7b99c65f, 0x35489fef),
+     TOBN(0x0c88ad1b, 0x3df18b97), TOBN(0x81b7d9be, 0x5d0e9edb),
+     TOBN(0xd85218c0, 0xc716cc0a), TOBN(0xf4b5ff90, 0x85691c49),
+     TOBN(0xa4fd666b, 0xce356ac6), TOBN(0x17c72895, 0x4b327a7a),
+     TOBN(0xf93d5085, 0xda6be7de), TOBN(0xff71530e, 0x3301d34e),
+     TOBN(0x4cd96442, 0xd8f448e8), TOBN(0x9283d331, 0x2ed18ffa),
+     TOBN(0x4d33dd99, 0x2a849870), TOBN(0xa716964b, 0x41576335),
+     TOBN(0xff5e3a9b, 0x179be0e5), TOBN(0x5b9d6b1b, 0x83b13632),
+     TOBN(0x3b8bd7d4, 0xa52f313b), TOBN(0xc9dd95a0, 0x637a4660),
+     TOBN(0x30035962, 0x0b3e218f), TOBN(0xce1481a3, 0xc7b28a3c),
+     TOBN(0xab41b43a, 0x43228d83), TOBN(0x24ae1c30, 0x4ad63f99),
+     TOBN(0x8e525f1a, 0x46a51229), TOBN(0x14af860f, 0xcd26d2b4),
+     TOBN(0xd6baef61, 0x3f714aa1), TOBN(0xf51865ad, 0xeb78795e),
+     TOBN(0xd3e21fce, 0xe6a9d694), TOBN(0x82ceb1dd, 0x8a37b527)}
+};
diff --git a/crypto/ec/ecp_oct.c b/crypto/ec/ecp_oct.c
new file mode 100644
index 000000000..4d142a4ab
--- /dev/null
+++ b/crypto/ec/ecp_oct.c
@@ -0,0 +1,372 @@
+/*
+ * Copyright 2011-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/* ====================================================================
+ * Copyright 2002 Sun Microsystems, Inc. ALL RIGHTS RESERVED.
+ * Portions of this software developed by SUN MICROSYSTEMS, INC.,
+ * and contributed to the OpenSSL project.
+ */
+
+#include 
+#include 
+
+#include "ec_lcl.h"
+
+int ec_GFp_simple_set_compressed_coordinates(const EC_GROUP *group,
+                                             EC_POINT *point,
+                                             const BIGNUM *x_, int y_bit,
+                                             BN_CTX *ctx)
+{
+    BN_CTX *new_ctx = NULL;
+    BIGNUM *tmp1, *tmp2, *x, *y;
+    int ret = 0;
+
+    /* clear error queue */
+    ERR_clear_error();
+
+    if (ctx == NULL) {
+        ctx = new_ctx = BN_CTX_new();
+        if (ctx == NULL)
+            return 0;
+    }
+
+    y_bit = (y_bit != 0);
+
+    BN_CTX_start(ctx);
+    tmp1 = BN_CTX_get(ctx);
+    tmp2 = BN_CTX_get(ctx);
+    x = BN_CTX_get(ctx);
+    y = BN_CTX_get(ctx);
+    if (y == NULL)
+        goto err;
+
+    /*-
+     * Recover y.  We have a Weierstrass equation
+     *     y^2 = x^3 + a*x + b,
+     * so  y  is one of the square roots of  x^3 + a*x + b.
+     */
+
+    /* tmp1 := x^3 */
+    if (!BN_nnmod(x, x_, group->field, ctx))
+        goto err;
+    if (group->meth->field_decode == 0) {
+        /* field_{sqr,mul} work on standard representation */
+        if (!group->meth->field_sqr(group, tmp2, x_, ctx))
+            goto err;
+        if (!group->meth->field_mul(group, tmp1, tmp2, x_, ctx))
+            goto err;
+    } else {
+        if (!BN_mod_sqr(tmp2, x_, group->field, ctx))
+            goto err;
+        if (!BN_mod_mul(tmp1, tmp2, x_, group->field, ctx))
+            goto err;
+    }
+
+    /* tmp1 := tmp1 + a*x */
+    if (group->a_is_minus3) {
+        if (!BN_mod_lshift1_quick(tmp2, x, group->field))
+            goto err;
+        if (!BN_mod_add_quick(tmp2, tmp2, x, group->field))
+            goto err;
+        if (!BN_mod_sub_quick(tmp1, tmp1, tmp2, group->field))
+            goto err;
+    } else {
+        if (group->meth->field_decode) {
+            if (!group->meth->field_decode(group, tmp2, group->a, ctx))
+                goto err;
+            if (!BN_mod_mul(tmp2, tmp2, x, group->field, ctx))
+                goto err;
+        } else {
+            /* field_mul works on standard representation */
+            if (!group->meth->field_mul(group, tmp2, group->a, x, ctx))
+                goto err;
+        }
+
+        if (!BN_mod_add_quick(tmp1, tmp1, tmp2, group->field))
+            goto err;
+    }
+
+    /* tmp1 := tmp1 + b */
+    if (group->meth->field_decode) {
+        if (!group->meth->field_decode(group, tmp2, group->b, ctx))
+            goto err;
+        if (!BN_mod_add_quick(tmp1, tmp1, tmp2, group->field))
+            goto err;
+    } else {
+        if (!BN_mod_add_quick(tmp1, tmp1, group->b, group->field))
+            goto err;
+    }
+
+    if (!BN_mod_sqrt(y, tmp1, group->field, ctx)) {
+        unsigned long err = ERR_peek_last_error();
+
+        if (ERR_GET_LIB(err) == ERR_LIB_BN
+            && ERR_GET_REASON(err) == BN_R_NOT_A_SQUARE) {
+            ERR_clear_error();
+            ECerr(EC_F_EC_GFP_SIMPLE_SET_COMPRESSED_COORDINATES,
+                  EC_R_INVALID_COMPRESSED_POINT);
+        } else
+            ECerr(EC_F_EC_GFP_SIMPLE_SET_COMPRESSED_COORDINATES,
+                  ERR_R_BN_LIB);
+        goto err;
+    }
+
+    if (y_bit != BN_is_odd(y)) {
+        if (BN_is_zero(y)) {
+            int kron;
+
+            kron = BN_kronecker(x, group->field, ctx);
+            if (kron == -2)
+                goto err;
+
+            if (kron == 1)
+                ECerr(EC_F_EC_GFP_SIMPLE_SET_COMPRESSED_COORDINATES,
+                      EC_R_INVALID_COMPRESSION_BIT);
+            else
+                /*
+                 * BN_mod_sqrt() should have cought this error (not a square)
+                 */
+                ECerr(EC_F_EC_GFP_SIMPLE_SET_COMPRESSED_COORDINATES,
+                      EC_R_INVALID_COMPRESSED_POINT);
+            goto err;
+        }
+        if (!BN_usub(y, group->field, y))
+            goto err;
+    }
+    if (y_bit != BN_is_odd(y)) {
+        ECerr(EC_F_EC_GFP_SIMPLE_SET_COMPRESSED_COORDINATES,
+              ERR_R_INTERNAL_ERROR);
+        goto err;
+    }
+
+    if (!EC_POINT_set_affine_coordinates_GFp(group, point, x, y, ctx))
+        goto err;
+
+    ret = 1;
+
+ err:
+    BN_CTX_end(ctx);
+    BN_CTX_free(new_ctx);
+    return ret;
+}
+
+size_t ec_GFp_simple_point2oct(const EC_GROUP *group, const EC_POINT *point,
+                               point_conversion_form_t form,
+                               unsigned char *buf, size_t len, BN_CTX *ctx)
+{
+    size_t ret;
+    BN_CTX *new_ctx = NULL;
+    int used_ctx = 0;
+    BIGNUM *x, *y;
+    size_t field_len, i, skip;
+
+    if ((form != POINT_CONVERSION_COMPRESSED)
+        && (form != POINT_CONVERSION_UNCOMPRESSED)
+        && (form != POINT_CONVERSION_HYBRID)) {
+        ECerr(EC_F_EC_GFP_SIMPLE_POINT2OCT, EC_R_INVALID_FORM);
+        goto err;
+    }
+
+    if (EC_POINT_is_at_infinity(group, point)) {
+        /* encodes to a single 0 octet */
+        if (buf != NULL) {
+            if (len < 1) {
+                ECerr(EC_F_EC_GFP_SIMPLE_POINT2OCT, EC_R_BUFFER_TOO_SMALL);
+                return 0;
+            }
+            buf[0] = 0;
+        }
+        return 1;
+    }
+
+    /* ret := required output buffer length */
+    field_len = BN_num_bytes(group->field);
+    ret =
+        (form ==
+         POINT_CONVERSION_COMPRESSED) ? 1 + field_len : 1 + 2 * field_len;
+
+    /* if 'buf' is NULL, just return required length */
+    if (buf != NULL) {
+        if (len < ret) {
+            ECerr(EC_F_EC_GFP_SIMPLE_POINT2OCT, EC_R_BUFFER_TOO_SMALL);
+            goto err;
+        }
+
+        if (ctx == NULL) {
+            ctx = new_ctx = BN_CTX_new();
+            if (ctx == NULL)
+                return 0;
+        }
+
+        BN_CTX_start(ctx);
+        used_ctx = 1;
+        x = BN_CTX_get(ctx);
+        y = BN_CTX_get(ctx);
+        if (y == NULL)
+            goto err;
+
+        if (!EC_POINT_get_affine_coordinates_GFp(group, point, x, y, ctx))
+            goto err;
+
+        if ((form == POINT_CONVERSION_COMPRESSED
+             || form == POINT_CONVERSION_HYBRID) && BN_is_odd(y))
+            buf[0] = form + 1;
+        else
+            buf[0] = form;
+
+        i = 1;
+
+        skip = field_len - BN_num_bytes(x);
+        if (skip > field_len) {
+            ECerr(EC_F_EC_GFP_SIMPLE_POINT2OCT, ERR_R_INTERNAL_ERROR);
+            goto err;
+        }
+        while (skip > 0) {
+            buf[i++] = 0;
+            skip--;
+        }
+        skip = BN_bn2bin(x, buf + i);
+        i += skip;
+        if (i != 1 + field_len) {
+            ECerr(EC_F_EC_GFP_SIMPLE_POINT2OCT, ERR_R_INTERNAL_ERROR);
+            goto err;
+        }
+
+        if (form == POINT_CONVERSION_UNCOMPRESSED
+            || form == POINT_CONVERSION_HYBRID) {
+            skip = field_len - BN_num_bytes(y);
+            if (skip > field_len) {
+                ECerr(EC_F_EC_GFP_SIMPLE_POINT2OCT, ERR_R_INTERNAL_ERROR);
+                goto err;
+            }
+            while (skip > 0) {
+                buf[i++] = 0;
+                skip--;
+            }
+            skip = BN_bn2bin(y, buf + i);
+            i += skip;
+        }
+
+        if (i != ret) {
+            ECerr(EC_F_EC_GFP_SIMPLE_POINT2OCT, ERR_R_INTERNAL_ERROR);
+            goto err;
+        }
+    }
+
+    if (used_ctx)
+        BN_CTX_end(ctx);
+    BN_CTX_free(new_ctx);
+    return ret;
+
+ err:
+    if (used_ctx)
+        BN_CTX_end(ctx);
+    BN_CTX_free(new_ctx);
+    return 0;
+}
+
+int ec_GFp_simple_oct2point(const EC_GROUP *group, EC_POINT *point,
+                            const unsigned char *buf, size_t len, BN_CTX *ctx)
+{
+    point_conversion_form_t form;
+    int y_bit;
+    BN_CTX *new_ctx = NULL;
+    BIGNUM *x, *y;
+    size_t field_len, enc_len;
+    int ret = 0;
+
+    if (len == 0) {
+        ECerr(EC_F_EC_GFP_SIMPLE_OCT2POINT, EC_R_BUFFER_TOO_SMALL);
+        return 0;
+    }
+    form = buf[0];
+    y_bit = form & 1;
+    form = form & ~1U;
+    if ((form != 0) && (form != POINT_CONVERSION_COMPRESSED)
+        && (form != POINT_CONVERSION_UNCOMPRESSED)
+        && (form != POINT_CONVERSION_HYBRID)) {
+        ECerr(EC_F_EC_GFP_SIMPLE_OCT2POINT, EC_R_INVALID_ENCODING);
+        return 0;
+    }
+    if ((form == 0 || form == POINT_CONVERSION_UNCOMPRESSED) && y_bit) {
+        ECerr(EC_F_EC_GFP_SIMPLE_OCT2POINT, EC_R_INVALID_ENCODING);
+        return 0;
+    }
+
+    if (form == 0) {
+        if (len != 1) {
+            ECerr(EC_F_EC_GFP_SIMPLE_OCT2POINT, EC_R_INVALID_ENCODING);
+            return 0;
+        }
+
+        return EC_POINT_set_to_infinity(group, point);
+    }
+
+    field_len = BN_num_bytes(group->field);
+    enc_len =
+        (form ==
+         POINT_CONVERSION_COMPRESSED) ? 1 + field_len : 1 + 2 * field_len;
+
+    if (len != enc_len) {
+        ECerr(EC_F_EC_GFP_SIMPLE_OCT2POINT, EC_R_INVALID_ENCODING);
+        return 0;
+    }
+
+    if (ctx == NULL) {
+        ctx = new_ctx = BN_CTX_new();
+        if (ctx == NULL)
+            return 0;
+    }
+
+    BN_CTX_start(ctx);
+    x = BN_CTX_get(ctx);
+    y = BN_CTX_get(ctx);
+    if (y == NULL)
+        goto err;
+
+    if (!BN_bin2bn(buf + 1, field_len, x))
+        goto err;
+    if (BN_ucmp(x, group->field) >= 0) {
+        ECerr(EC_F_EC_GFP_SIMPLE_OCT2POINT, EC_R_INVALID_ENCODING);
+        goto err;
+    }
+
+    if (form == POINT_CONVERSION_COMPRESSED) {
+        if (!EC_POINT_set_compressed_coordinates_GFp
+            (group, point, x, y_bit, ctx))
+            goto err;
+    } else {
+        if (!BN_bin2bn(buf + 1 + field_len, field_len, y))
+            goto err;
+        if (BN_ucmp(y, group->field) >= 0) {
+            ECerr(EC_F_EC_GFP_SIMPLE_OCT2POINT, EC_R_INVALID_ENCODING);
+            goto err;
+        }
+        if (form == POINT_CONVERSION_HYBRID) {
+            if (y_bit != BN_is_odd(y)) {
+                ECerr(EC_F_EC_GFP_SIMPLE_OCT2POINT, EC_R_INVALID_ENCODING);
+                goto err;
+            }
+        }
+
+        /*
+         * EC_POINT_set_affine_coordinates_GFp is responsible for checking that
+         * the point is on the curve.
+         */
+        if (!EC_POINT_set_affine_coordinates_GFp(group, point, x, y, ctx))
+            goto err;
+    }
+
+    ret = 1;
+
+ err:
+    BN_CTX_end(ctx);
+    BN_CTX_free(new_ctx);
+    return ret;
+}
diff --git a/crypto/ec/ecp_sm2p256.c b/crypto/ec/ecp_sm2p256.c
new file mode 100644
index 000000000..7eff225b9
--- /dev/null
+++ b/crypto/ec/ecp_sm2p256.c
@@ -0,0 +1,2507 @@
+/* ====================================================================
+ * Copyright (c) 2014 - 2018 The GmSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the GmSSL Project.
+ *    (http://gmssl.org/)"
+ *
+ * 4. The name "GmSSL Project" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    guanzhi1980@gmail.com.
+ *
+ * 5. Products derived from this software may not be called "GmSSL"
+ *    nor may "GmSSL" appear in their names without prior written
+ *    permission of the GmSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the GmSSL Project
+ *    (http://gmssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE GmSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE GmSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+/*
+ * Copyright 2011-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/* Copyright 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ *
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/*
+ * A 64-bit implementation of the NIST P-256 elliptic curve point multiplication
+ *
+ * OpenSSL integration was taken from Emilia Kasper's work in ecp_nistp224.c.
+ * Otherwise based on Emilia's P224 work, which was inspired by my curve25519
+ * work which got its smarts from Daniel J. Bernstein's work on the same.
+ */
+
+#include 
+#ifdef OPENSSL_NO_EC_NISTP_64_GCC_128
+NON_EMPTY_TRANSLATION_UNIT
+#else
+
+# include 
+# include 
+# include 
+# include "ec_lcl.h"
+
+# if defined(__GNUC__) && (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1))
+  /* even with gcc, the typedef won't work for 32-bit platforms */
+typedef __uint128_t uint128_t;  /* nonstandard; implemented by gcc on 64-bit
+                                 * platforms */
+typedef __int128_t int128_t;
+# else
+#  error "Need GCC 3.1 or later to define type uint128_t"
+# endif
+
+typedef uint8_t u8;
+typedef uint32_t u32;
+typedef uint64_t u64;
+typedef int64_t s64;
+
+/*
+ * The underlying field. SM2-P256 operates over GF(2^256-2^224-2^96+2^64-1).
+ * We can serialise an element of this field into 32 bytes. We call this an
+ * felem_bytearray.
+ */
+
+typedef u8 felem_bytearray[32];
+
+/*
+ * These are the parameters of SM2, taken from GM/T 0003.5-2012. These
+ * values are big-endian.
+ */
+static const felem_bytearray sm2p256v1_curve_params[5] = {
+    {0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, /* p */
+     0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+     0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
+     0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},
+    {0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, /* a = -3 */
+     0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+     0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
+     0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC},
+    {0x28, 0xE9, 0xFA, 0x9E, 0x9D, 0x9F, 0x5E, 0x34, /* b */
+     0x4D, 0x5A, 0x9E, 0x4B, 0xCF, 0x65, 0x09, 0xA7,
+     0xF3, 0x97, 0x89, 0xF5, 0x15, 0xAB, 0x8F, 0x92,
+     0xDD, 0xBC, 0xBD, 0x41, 0x4D, 0x94, 0x0E, 0x93},
+    {0x32, 0xC4, 0xAE, 0x2C, 0x1F, 0x19, 0x81, 0x19, /* x */
+     0x5F, 0x99, 0x04, 0x46, 0x6A, 0x39, 0xC9, 0x94,
+     0x8F, 0xE3, 0x0B, 0xBF, 0xF2, 0x66, 0x0B, 0xE1,
+     0x71, 0x5A, 0x45, 0x89, 0x33, 0x4C, 0x74, 0xC7},
+    {0xBC, 0x37, 0x36, 0xA2, 0xF4, 0xF6, 0x77, 0x9C, /* y */
+     0x59, 0xBD, 0xCE, 0xE3, 0x6B, 0x69, 0x21, 0x53,
+     0xD0, 0xA9, 0x87, 0x7C, 0xC6, 0x2A, 0x47, 0x40,
+     0x02, 0xDF, 0x32, 0xE5, 0x21, 0x39, 0xF0, 0xA0}
+};
+
+/*-
+ * The representation of field elements.
+ * ------------------------------------
+ *
+ * We represent field elements with either four 128-bit values, eight 128-bit
+ * values, or four 64-bit values. The field element represented is:
+ *   v[0]*2^0 + v[1]*2^64 + v[2]*2^128 + v[3]*2^192  (mod p)
+ * or:
+ *   v[0]*2^0 + v[1]*2^64 + v[2]*2^128 + ... + v[8]*2^512  (mod p)
+ *
+ * 128-bit values are called 'limbs'. Since the limbs are spaced only 64 bits
+ * apart, but are 128-bits wide, the most significant bits of each limb overlap
+ * with the least significant bits of the next.
+ *
+ * A field element with four limbs is an 'felem'. One with eight limbs is a
+ * 'longfelem'
+ *
+ * A field element with four, 64-bit values is called a 'smallfelem'. Small
+ * values are used as intermediate values before multiplication.
+ */
+
+# define NLIMBS 4
+
+typedef uint128_t limb;
+typedef limb felem[NLIMBS];
+typedef limb longfelem[NLIMBS * 2];
+typedef u64 smallfelem[NLIMBS];
+
+/* This is the value of the prime as four 64-bit words, little-endian. */
+static const u64 kPrime[4] = {
+    0xfffffffffffffffful, 0xffffffff00000000ul,
+    0xfffffffffffffffful, 0xfffffffefffffffful};
+static const u64 bottom63bits = 0x7ffffffffffffffful;
+
+/*
+ * bin32_to_felem takes a little-endian byte array and converts it into felem
+ * form. This assumes that the CPU is little-endian.
+ */
+static void bin32_to_felem(felem out, const u8 in[32])
+{
+    out[0] = *((u64 *)&in[0]);
+    out[1] = *((u64 *)&in[8]);
+    out[2] = *((u64 *)&in[16]);
+    out[3] = *((u64 *)&in[24]);
+}
+
+/*
+ * smallfelem_to_bin32 takes a smallfelem and serialises into a little
+ * endian, 32 byte array. This assumes that the CPU is little-endian.
+ */
+static void smallfelem_to_bin32(u8 out[32], const smallfelem in)
+{
+    *((u64 *)&out[0]) = in[0];
+    *((u64 *)&out[8]) = in[1];
+    *((u64 *)&out[16]) = in[2];
+    *((u64 *)&out[24]) = in[3];
+}
+
+/* To preserve endianness when using BN_bn2bin and BN_bin2bn */
+static void flip_endian(u8 *out, const u8 *in, unsigned len)
+{
+    unsigned i;
+    for (i = 0; i < len; ++i)
+        out[i] = in[len - 1 - i];
+}
+
+/* BN_to_felem converts an OpenSSL BIGNUM into an felem */
+static int BN_to_felem(felem out, const BIGNUM *bn)
+{
+    felem_bytearray b_in;
+    felem_bytearray b_out;
+    unsigned num_bytes;
+
+    /* BN_bn2bin eats leading zeroes */
+    memset(b_out, 0, sizeof(b_out));
+    num_bytes = BN_num_bytes(bn);
+    if (num_bytes > sizeof b_out) {
+        ECerr(EC_F_BN_TO_FELEM, EC_R_BIGNUM_OUT_OF_RANGE);
+        return 0;
+    }
+    if (BN_is_negative(bn)) {
+        ECerr(EC_F_BN_TO_FELEM, EC_R_BIGNUM_OUT_OF_RANGE);
+        return 0;
+    }
+    num_bytes = BN_bn2bin(bn, b_in);
+    flip_endian(b_out, b_in, num_bytes);
+    bin32_to_felem(out, b_out);
+    return 1;
+}
+
+
+/* felem_to_BN converts an felem into an OpenSSL BIGNUM */
+static BIGNUM *smallfelem_to_BN(BIGNUM *out, const smallfelem in)
+{
+    felem_bytearray b_in, b_out;
+    smallfelem_to_bin32(b_in, in);
+    flip_endian(b_out, b_in, sizeof b_out);
+    return BN_bin2bn(b_out, sizeof b_out, out);
+}
+
+
+/*-
+ * Field operations
+ * ----------------
+ */
+
+static void smallfelem_one(smallfelem out)
+{
+    out[0] = 1;
+    out[1] = 0;
+    out[2] = 0;
+    out[3] = 0;
+}
+
+static void smallfelem_assign(smallfelem out, const smallfelem in)
+{
+    out[0] = in[0];
+    out[1] = in[1];
+    out[2] = in[2];
+    out[3] = in[3];
+}
+
+static void felem_assign(felem out, const felem in)
+{
+    out[0] = in[0];
+    out[1] = in[1];
+    out[2] = in[2];
+    out[3] = in[3];
+}
+
+/* felem_sum sets out = out + in. */
+static void felem_sum(felem out, const felem in)
+{
+    out[0] += in[0];
+    out[1] += in[1];
+    out[2] += in[2];
+    out[3] += in[3];
+}
+
+/* felem_small_sum sets out = out + in. */
+static void felem_small_sum(felem out, const smallfelem in)
+{
+    out[0] += in[0];
+    out[1] += in[1];
+    out[2] += in[2];
+    out[3] += in[3];
+}
+
+/* felem_scalar sets out = out * scalar */
+static void felem_scalar(felem out, const u64 scalar)
+{
+    out[0] *= scalar;
+    out[1] *= scalar;
+    out[2] *= scalar;
+    out[3] *= scalar;
+}
+
+/* longfelem_scalar sets out = out * scalar */
+static void longfelem_scalar(longfelem out, const u64 scalar)
+{
+    out[0] *= scalar;
+    out[1] *= scalar;
+    out[2] *= scalar;
+    out[3] *= scalar;
+    out[4] *= scalar;
+    out[5] *= scalar;
+    out[6] *= scalar;
+    out[7] *= scalar;
+}
+
+# define two105m73m41 (((limb)1) << 105) - (((limb)1) << 73) - (((limb)1) << 41)
+# define two105m41 (((limb)1) << 105) - (((limb)1) << 41)
+# define two105m73 (((limb)1) << 105) - (((limb)1) << 73)
+
+/* zero105 is 0 mod p */
+static const felem zero105 =
+    { two105m41, two105m73, two105m41, two105m73m41 };
+
+/*-
+ * smallfelem_neg sets |out| to |-small|
+ * On exit:
+ *   out[i] < out[i] + 2^105
+ */
+static void smallfelem_neg(felem out, const smallfelem small)
+{
+    /* In order to prevent underflow, we subtract from 0 mod p. */
+    out[0] = zero105[0] - small[0];
+    out[1] = zero105[1] - small[1];
+    out[2] = zero105[2] - small[2];
+    out[3] = zero105[3] - small[3];
+}
+
+/*-
+ * felem_diff subtracts |in| from |out|
+ * On entry:
+ *   in[i] < 2^104
+ * On exit:
+ *   out[i] < out[i] + 2^105
+ */
+static void felem_diff(felem out, const felem in)
+{
+    /*
+     * In order to prevent underflow, we add 0 mod p before subtracting.
+     */
+    out[0] += zero105[0];
+    out[1] += zero105[1];
+    out[2] += zero105[2];
+    out[3] += zero105[3];
+
+    out[0] -= in[0];
+    out[1] -= in[1];
+    out[2] -= in[2];
+    out[3] -= in[3];
+}
+
+# define two107m75m43 (((limb)1) << 107) - (((limb)1) << 75) - (((limb)1) << 43)
+# define two107m43 (((limb)1) << 107) - (((limb)1) << 43)
+# define two107m75 (((limb)1) << 107) - (((limb)1) << 75)
+
+/* zero107 is 0 mod p */
+static const felem zero107 =
+    { two107m43, two107m75, two107m43, two107m75m43 };
+
+/*-
+ * An alternative felem_diff for larger inputs |in|
+ * felem_diff_zero107 subtracts |in| from |out|
+ * On entry:
+ *   in[i] < 2^106
+ * On exit:
+ *   out[i] < out[i] + 2^107
+ */
+static void felem_diff_zero107(felem out, const felem in)
+{
+    /*
+     * In order to prevent underflow, we add 0 mod p before subtracting.
+     */
+    out[0] += zero107[0];
+    out[1] += zero107[1];
+    out[2] += zero107[2];
+    out[3] += zero107[3];
+
+    out[0] -= in[0];
+    out[1] -= in[1];
+    out[2] -= in[2];
+    out[3] -= in[3];
+}
+
+/*-
+ * longfelem_diff subtracts |in| from |out|
+ * On entry:
+ *   in[i] < 7*2^67
+ * On exit:
+ *   out[i] < out[i] + 2^70 + 2^40
+ */
+static void longfelem_diff(longfelem out, const longfelem in)
+{
+    static const limb two70m39m7m6 =
+        (((limb) 1) << 70) - (((limb) 1) << 39)- (((limb) 1) << 7) -
+        (((limb) 1) << 6);
+    static const limb two70m40p38 =
+        (((limb) 1) << 70) - (((limb) 1) << 40) + (((limb) 1) << 38);
+    static const limb two70m38m7 =
+        (((limb) 1) << 70) - (((limb) 1) << 38) - (((limb) 1) << 7);
+    static const limb two70m40m7m6 =
+        (((limb) 1) << 70) - (((limb) 1) << 40) - (((limb) 1) << 7) -
+        (((limb) 1) << 6);
+    static const limb two70m6 = (((limb) 1) << 70) - (((limb) 1) << 6);
+
+    /* add 0 mod p to avoid underflow */
+    out[0] += two70m39m7m6;
+    out[1] += two70m40p38;
+    out[2] += two70m38m7;
+    out[3] += two70m40m7m6;
+    out[4] += two70m6;
+    out[5] += two70m6;
+    out[6] += two70m6;
+    out[7] += two70m6;
+
+    out[0] -= in[0];
+    out[1] -= in[1];
+    out[2] -= in[2];
+    out[3] -= in[3];
+    out[4] -= in[4];
+    out[5] -= in[5];
+    out[6] -= in[6];
+    out[7] -= in[7];
+}
+
+# define two64m32m0 (((limb)1) << 64) - (((limb)1) << 32) - 1
+# define two64m0 (((limb)1) << 64) - 1
+# define two64m32 (((limb)1) << 64) - (((limb)1) << 32)
+
+/* zero64 == p */
+static const felem zero64 = { two64m0, two64m32, two64m0, two64m32m0 };
+
+/*-
+ * felem_shrink converts an felem into a smallfelem. The result isn't quite
+ * minimal as the value may be greater than p.
+ *
+ * On entry:
+ *   in[i] < 2^109
+ * On exit:
+ *   out[i] < 2^64
+ */
+static void felem_shrink(smallfelem out, const felem in)
+{
+    felem tmp;
+    u64 a, b, mask;
+    s64 high, low;
+
+    static const u64 kPrime3Test = 0x7ffffffefffffffful;
+
+    /* Carry 2->3 */
+    tmp[3] = zero64[3] + in[3] + ((u64)(in[2] >> 64));
+    /* tmp[3] < 2^110 */
+
+    tmp[2] = zero64[2] + (u64)in[2];
+    tmp[1] = zero64[1] + in[1];
+    tmp[0] = zero64[0] + in[0];
+    /* tmp[0] < 2**110, tmp[1] < 2^111, tmp[2] < 2**65 */
+
+    /*
+     * We perform two partial reductions where we eliminate the high-word of
+     * tmp[3]. We don't update the other words till the end.
+     */
+    a = tmp[3] >> 64;           /* a < 2^46 */
+    tmp[3] = (u64)tmp[3];
+    tmp[3] += ((limb) a) << 32;
+    /* tmp[3] < 2^79 */
+
+    b = a;
+    a = tmp[3] >> 64;           /* a < 2^15 */
+    b += a;                     /* b < 2^46 + 2^15 < 2^47 */
+    tmp[3] = (u64)tmp[3];
+    tmp[3] += ((limb) a) << 32;
+    /* tmp[3] < 2^64 + 2^47 */
+
+    /*
+     * This adjusts the other two words to complete the two partial
+     * reductions.
+     */
+    tmp[1] += (((limb) b) << 32);
+    tmp[1] -= b;
+    tmp[0] += b;
+
+    /*
+     * In order to make space in tmp[3] for the carry from 2 -> 3, we
+     * conditionally subtract kPrime if tmp[3] is large enough.
+     */
+    high = tmp[3] >> 64;
+    /* As tmp[3] < 2^65, high is either 1 or 0 */
+    high <<= 63;
+    high >>= 63;
+    /*-
+     * high is:
+     *   all ones   if the high word of tmp[3] is 1
+     *   all zeros  if the high word of tmp[3] if 0 */
+    low = tmp[3];
+    mask = low >> 63;
+    /*-
+     * mask is:
+     *   all ones   if the MSB of low is 1
+     *   all zeros  if the MSB of low if 0 */
+    low &= bottom63bits;
+    low -= kPrime3Test;
+    /* if low was greater than kPrime3Test then the MSB is zero */
+    low = ~low;
+    low >>= 63;
+    /*-
+     * low is:
+     *   all ones   if low was > kPrime3Test
+     *   all zeros  if low was <= kPrime3Test */
+    mask = (mask & low) | high;
+    tmp[0] -= mask & kPrime[0];
+    tmp[1] -= mask & kPrime[1];
+    tmp[2] -= mask & kPrime[2];
+    tmp[3] -= mask & kPrime[3];
+    /* tmp[3] < 2**64 - 2**32 + 1 */
+
+    tmp[1] += ((u64)(tmp[0] >> 64));
+    tmp[0] = (u64)tmp[0];
+    tmp[2] += ((u64)(tmp[1] >> 64));
+    tmp[1] = (u64)tmp[1];
+    tmp[3] += ((u64)(tmp[2] >> 64));
+    tmp[2] = (u64)tmp[2];
+    /* tmp[i] < 2^64 */
+
+    out[0] = tmp[0];
+    out[1] = tmp[1];
+    out[2] = tmp[2];
+    out[3] = tmp[3];
+}
+
+/* smallfelem_expand converts a smallfelem to an felem */
+static void smallfelem_expand(felem out, const smallfelem in)
+{
+    out[0] = in[0];
+    out[1] = in[1];
+    out[2] = in[2];
+    out[3] = in[3];
+}
+
+/*-
+ * smallfelem_square sets |out| = |small|^2
+ * On entry:
+ *   small[i] < 2^64
+ * On exit:
+ *   out[i] < 7 * 2^64 < 2^67
+ */
+static void smallfelem_square(longfelem out, const smallfelem small)
+{
+    limb a;
+    u64 high, low;
+
+    a = ((uint128_t) small[0]) * small[0];
+    low = a;
+    high = a >> 64;
+    out[0] = low;
+    out[1] = high;
+
+    a = ((uint128_t) small[0]) * small[1];
+    low = a;
+    high = a >> 64;
+    out[1] += low;
+    out[1] += low;
+    out[2] = high;
+
+    a = ((uint128_t) small[0]) * small[2];
+    low = a;
+    high = a >> 64;
+    out[2] += low;
+    out[2] *= 2;
+    out[3] = high;
+
+    a = ((uint128_t) small[0]) * small[3];
+    low = a;
+    high = a >> 64;
+    out[3] += low;
+    out[4] = high;
+
+    a = ((uint128_t) small[1]) * small[2];
+    low = a;
+    high = a >> 64;
+    out[3] += low;
+    out[3] *= 2;
+    out[4] += high;
+
+    a = ((uint128_t) small[1]) * small[1];
+    low = a;
+    high = a >> 64;
+    out[2] += low;
+    out[3] += high;
+
+    a = ((uint128_t) small[1]) * small[3];
+    low = a;
+    high = a >> 64;
+    out[4] += low;
+    out[4] *= 2;
+    out[5] = high;
+
+    a = ((uint128_t) small[2]) * small[3];
+    low = a;
+    high = a >> 64;
+    out[5] += low;
+    out[5] *= 2;
+    out[6] = high;
+    out[6] += high;
+
+    a = ((uint128_t) small[2]) * small[2];
+    low = a;
+    high = a >> 64;
+    out[4] += low;
+    out[5] += high;
+
+    a = ((uint128_t) small[3]) * small[3];
+    low = a;
+    high = a >> 64;
+    out[6] += low;
+    out[7] = high;
+}
+
+/*-
+ * felem_square sets |out| = |in|^2
+ * On entry:
+ *   in[i] < 2^109
+ * On exit:
+ *   out[i] < 7 * 2^64 < 2^67
+ */
+static void felem_square(longfelem out, const felem in)
+{
+    u64 small[4];
+    felem_shrink(small, in);
+    smallfelem_square(out, small);
+}
+
+/*-
+ * smallfelem_mul sets |out| = |small1| * |small2|
+ * On entry:
+ *   small1[i] < 2^64
+ *   small2[i] < 2^64
+ * On exit:
+ *   out[i] < 7 * 2^64 < 2^67
+ */
+static void smallfelem_mul(longfelem out, const smallfelem small1,
+                           const smallfelem small2)
+{
+    limb a;
+    u64 high, low;
+
+    a = ((uint128_t) small1[0]) * small2[0];
+    low = a;
+    high = a >> 64;
+    out[0] = low;
+    out[1] = high;
+
+    a = ((uint128_t) small1[0]) * small2[1];
+    low = a;
+    high = a >> 64;
+    out[1] += low;
+    out[2] = high;
+
+    a = ((uint128_t) small1[1]) * small2[0];
+    low = a;
+    high = a >> 64;
+    out[1] += low;
+    out[2] += high;
+
+    a = ((uint128_t) small1[0]) * small2[2];
+    low = a;
+    high = a >> 64;
+    out[2] += low;
+    out[3] = high;
+
+    a = ((uint128_t) small1[1]) * small2[1];
+    low = a;
+    high = a >> 64;
+    out[2] += low;
+    out[3] += high;
+
+    a = ((uint128_t) small1[2]) * small2[0];
+    low = a;
+    high = a >> 64;
+    out[2] += low;
+    out[3] += high;
+
+    a = ((uint128_t) small1[0]) * small2[3];
+    low = a;
+    high = a >> 64;
+    out[3] += low;
+    out[4] = high;
+
+    a = ((uint128_t) small1[1]) * small2[2];
+    low = a;
+    high = a >> 64;
+    out[3] += low;
+    out[4] += high;
+
+    a = ((uint128_t) small1[2]) * small2[1];
+    low = a;
+    high = a >> 64;
+    out[3] += low;
+    out[4] += high;
+
+    a = ((uint128_t) small1[3]) * small2[0];
+    low = a;
+    high = a >> 64;
+    out[3] += low;
+    out[4] += high;
+
+    a = ((uint128_t) small1[1]) * small2[3];
+    low = a;
+    high = a >> 64;
+    out[4] += low;
+    out[5] = high;
+
+    a = ((uint128_t) small1[2]) * small2[2];
+    low = a;
+    high = a >> 64;
+    out[4] += low;
+    out[5] += high;
+
+    a = ((uint128_t) small1[3]) * small2[1];
+    low = a;
+    high = a >> 64;
+    out[4] += low;
+    out[5] += high;
+
+    a = ((uint128_t) small1[2]) * small2[3];
+    low = a;
+    high = a >> 64;
+    out[5] += low;
+    out[6] = high;
+
+    a = ((uint128_t) small1[3]) * small2[2];
+    low = a;
+    high = a >> 64;
+    out[5] += low;
+    out[6] += high;
+
+    a = ((uint128_t) small1[3]) * small2[3];
+    low = a;
+    high = a >> 64;
+    out[6] += low;
+    out[7] = high;
+}
+
+/*-
+ * felem_mul sets |out| = |in1| * |in2|
+ * On entry:
+ *   in1[i] < 2^109
+ *   in2[i] < 2^109
+ * On exit:
+ *   out[i] < 7 * 2^64 < 2^67
+ */
+static void felem_mul(longfelem out, const felem in1, const felem in2)
+{
+    smallfelem small1, small2;
+    felem_shrink(small1, in1);
+    felem_shrink(small2, in2);
+    smallfelem_mul(out, small1, small2);
+}
+
+/*-
+ * felem_small_mul sets |out| = |small1| * |in2|
+ * On entry:
+ *   small1[i] < 2^64
+ *   in2[i] < 2^109
+ * On exit:
+ *   out[i] < 7 * 2^64 < 2^67
+ */
+static void felem_small_mul(longfelem out, const smallfelem small1,
+                            const felem in2)
+{
+    smallfelem small2;
+    felem_shrink(small2, in2);
+    smallfelem_mul(out, small1, small2);
+}
+
+/*-
+ * Internal function for the different flavours of felem_reduce.
+ * felem_reduce_ reduces the higher coefficients in[4]-in[7].
+ * On entry:
+ *   out[0] >= in[6] + 2^32*in[6] + in[7] + 2^32*in[7]
+ *   out[1] >= in[7] + 2^32*in[4]
+ *   out[2] >= in[5] + 2^32*in[5]
+ *   out[3] >= in[4] + 2^32*in[5] + 2^32*in[6]
+ * On exit:
+ *   out[0] <= out[0] + in[4] + 2^32*in[5]
+ *   out[1] <= out[1] + in[5] + 2^33*in[6]
+ *   out[2] <= out[2] + in[7] + 2*in[6] + 2^33*in[7]
+ *   out[3] <= out[3] + 2^32*in[4] + 3*in[7]
+ */
+static void felem_reduce(felem out, const longfelem in)
+{
+    uint128_t a, b, c, d;
+    a = in[6] + in[7];
+    b = in[5] + in[7];
+    c = in[4] + in[7];
+    d = a + b;
+
+    out[3] = in[3] + ((in[4] + in[5] + a * 2) << 32) + in[7];
+    out[2] = in[2] + (b << 32) + a + in[7];
+    out[1] = in[1] + ((c + in[6]) << 32) - c;
+    out[0] = in[0] + (d << 32) + d + in[4];
+}
+
+/*
+ * subtract_u64 sets *result = *result - v and *carry to one if the
+ * subtraction underflowed.
+ */
+static void subtract_u64(u64 *result, u64 *carry, u64 v)
+{
+    uint128_t r = *result;
+    r -= v;
+    *carry = (r >> 64) & 1;
+    *result = (u64)r;
+}
+
+/*
+ * felem_contract converts |in| to its unique, minimal representation. On
+ * entry: in[i] < 2^109
+ */
+static void felem_contract(smallfelem out, const felem in)
+{
+    unsigned i;
+    u64 all_equal_so_far = 0, result = 0, carry;
+
+    felem_shrink(out, in);
+    /* small is minimal except that the value might be > p */
+
+    all_equal_so_far--;
+    /*
+     * We are doing a constant time test if out >= kPrime. We need to compare
+     * each u64, from most-significant to least significant. For each one, if
+     * all words so far have been equal (m is all ones) then a non-equal
+     * result is the answer. Otherwise we continue.
+     */
+    for (i = 3; i < 4; i--) {
+        u64 equal;
+        uint128_t a = ((uint128_t) kPrime[i]) - out[i];
+        /*
+         * if out[i] > kPrime[i] then a will underflow and the high 64-bits
+         * will all be set.
+         */
+        result |= all_equal_so_far & ((u64)(a >> 64));
+
+        /*
+         * if kPrime[i] == out[i] then |equal| will be all zeros and the
+         * decrement will make it all ones.
+         */
+        equal = kPrime[i] ^ out[i];
+        equal--;
+        equal &= equal << 32;
+        equal &= equal << 16;
+        equal &= equal << 8;
+        equal &= equal << 4;
+        equal &= equal << 2;
+        equal &= equal << 1;
+        equal = ((s64) equal) >> 63;
+
+        all_equal_so_far &= equal;
+    }
+
+    /*
+     * if all_equal_so_far is still all ones then the two values are equal
+     * and so out >= kPrime is true.
+     */
+    result |= all_equal_so_far;
+
+    /* if out >= kPrime then we subtract kPrime. */
+    subtract_u64(&out[0], &carry, result & kPrime[0]);
+    subtract_u64(&out[1], &carry, carry);
+    subtract_u64(&out[2], &carry, carry);
+    subtract_u64(&out[3], &carry, carry);
+
+    subtract_u64(&out[1], &carry, result & kPrime[1]);
+    subtract_u64(&out[2], &carry, carry);
+    subtract_u64(&out[3], &carry, carry);
+
+    subtract_u64(&out[2], &carry, result & kPrime[2]);
+    subtract_u64(&out[3], &carry, carry);
+
+    subtract_u64(&out[3], &carry, result & kPrime[3]);
+}
+
+static void smallfelem_square_contract(smallfelem out, const smallfelem in)
+{
+    longfelem longtmp;
+    felem tmp;
+
+    smallfelem_square(longtmp, in);
+    felem_reduce(tmp, longtmp);
+    felem_contract(out, tmp);
+}
+
+static void smallfelem_mul_contract(smallfelem out, const smallfelem in1,
+                                    const smallfelem in2)
+{
+    longfelem longtmp;
+    felem tmp;
+
+    smallfelem_mul(longtmp, in1, in2);
+    felem_reduce(tmp, longtmp);
+    felem_contract(out, tmp);
+}
+
+/*-
+ * felem_is_zero returns a limb with all bits set if |in| == 0 (mod p) and 0
+ * otherwise.
+ * On entry:
+ *   small[i] < 2^64
+ */
+static limb smallfelem_is_zero(const smallfelem small)
+{
+    limb result;
+    u64 is_p;
+
+    u64 is_zero = small[0] | small[1] | small[2] | small[3];
+    is_zero--;
+    is_zero &= is_zero << 32;
+    is_zero &= is_zero << 16;
+    is_zero &= is_zero << 8;
+    is_zero &= is_zero << 4;
+    is_zero &= is_zero << 2;
+    is_zero &= is_zero << 1;
+    is_zero = ((s64) is_zero) >> 63;
+
+    is_p = (small[0] ^ kPrime[0]) |
+        (small[1] ^ kPrime[1]) |
+        (small[2] ^ kPrime[2]) | (small[3] ^ kPrime[3]);
+    is_p--;
+    is_p &= is_p << 32;
+    is_p &= is_p << 16;
+    is_p &= is_p << 8;
+    is_p &= is_p << 4;
+    is_p &= is_p << 2;
+    is_p &= is_p << 1;
+    is_p = ((s64) is_p) >> 63;
+
+    is_zero |= is_p;
+
+    result = is_zero;
+    result |= ((limb) is_zero) << 64;
+    return result;
+}
+
+static int smallfelem_is_zero_int(const smallfelem small)
+{
+    return (int)(smallfelem_is_zero(small) & ((limb) 1));
+}
+
+/*-
+ * felem_inv calculates |out| = |in|^{-1}
+ */
+static void felem_inv(felem out, const felem in)
+{
+    felem ftmp;
+    felem a1, a2, a3, a4, a5;
+    longfelem tmp;
+    unsigned i;
+
+    felem_square(tmp, in);
+    felem_reduce(a1, tmp);
+    felem_mul(tmp, a1, in);
+    felem_reduce(a2, tmp);
+    felem_square(tmp, a2);
+    felem_reduce(ftmp, tmp);
+    felem_square(tmp, ftmp);
+    felem_reduce(ftmp, tmp);
+    felem_mul(tmp, ftmp, a2);
+    felem_reduce(a3, tmp);
+    felem_square(tmp, a3);
+    felem_reduce(ftmp, tmp);
+    felem_square(tmp, ftmp);
+    felem_reduce(ftmp, tmp);
+    felem_square(tmp, ftmp);
+    felem_reduce(ftmp, tmp);
+    felem_square(tmp, ftmp);
+    felem_reduce(ftmp, tmp);
+    felem_mul(tmp, ftmp, a3);
+    felem_reduce(a4, tmp);
+    felem_square(tmp, a4);
+    felem_reduce(a5, tmp);
+    for (i = 1; i < 8; i++) {
+        felem_square(tmp, a5);
+        felem_reduce(a5, tmp);
+    }
+    felem_mul(tmp, a5, a4);
+    felem_reduce(a5, tmp);
+    for (i = 0; i < 8; i++) {
+        felem_square(tmp, a5);
+        felem_reduce(a5, tmp);
+    }
+    felem_mul(tmp, a5, a4);
+    felem_reduce(a5, tmp);
+    for (i = 0; i < 4; i++) {
+        felem_square(tmp, a5);
+        felem_reduce(a5, tmp);
+    }
+    felem_mul(tmp, a5, a3);
+    felem_reduce(a5, tmp);
+    felem_square(tmp, a5);
+    felem_reduce(a5, tmp);
+    felem_square(tmp, a5);
+    felem_reduce(a5, tmp);
+    felem_mul(tmp, a5, a2);
+    felem_reduce(a5, tmp);
+    felem_square(tmp, a5);
+    felem_reduce(ftmp, tmp);
+    felem_mul(tmp, ftmp, in);
+    felem_reduce(a5, tmp);
+    felem_square(tmp, a5);
+    felem_reduce(a4, tmp);
+    felem_mul(tmp, a4, a1);
+    felem_reduce(a3, tmp);
+    felem_square(tmp, a4);
+    felem_reduce(a5, tmp);
+    for (i = 0; i < 30; i++) {
+        felem_square(tmp, a5);
+        felem_reduce(a5, tmp);
+    }
+    felem_mul(tmp, a5, a4);
+    felem_reduce(a4, tmp);
+    felem_square(tmp, a4);
+    felem_reduce(a4, tmp);
+    felem_mul(tmp, a4, in);
+    felem_reduce(a4, tmp);
+    felem_mul(tmp, a4, a2);
+    felem_reduce(a3, tmp);
+    for (i = 0; i < 33; i++) {
+        felem_square(tmp, a5);
+        felem_reduce(a5, tmp);
+    }
+    felem_mul(tmp, a5, a3);
+    felem_reduce(a2, tmp);
+    felem_mul(tmp, a2, a3);
+    felem_reduce(a3, tmp);
+    for (i = 0; i < 32; i++) {
+        felem_square(tmp, a5);
+        felem_reduce(a5, tmp);
+    }
+    felem_mul(tmp, a5, a3);
+    felem_reduce(a2, tmp);
+    felem_mul(tmp, a2, a3);
+    felem_reduce(a3, tmp);
+    felem_mul(tmp, a2, a4);
+    felem_reduce(a4, tmp);
+    for (i = 0; i < 32; i++) {
+        felem_square(tmp, a5);
+        felem_reduce(a5, tmp);
+    }
+    felem_mul(tmp, a5, a3);
+    felem_reduce(a2, tmp);
+    felem_mul(tmp, a2, a3);
+    felem_reduce(a3, tmp);
+    felem_mul(tmp, a2, a4);
+    felem_reduce(a4, tmp);
+    for (i = 0; i < 32; i++) {
+        felem_square(tmp, a5);
+        felem_reduce(a5, tmp);
+    }
+    felem_mul(tmp, a5, a3);
+    felem_reduce(a2, tmp);
+    felem_mul(tmp, a2, a3);
+    felem_reduce(a3, tmp);
+    felem_mul(tmp, a2, a4);
+    felem_reduce(a4, tmp);
+    for (i = 0; i < 32; i++) {
+        felem_square(tmp, a5);
+        felem_reduce(a5, tmp);
+    }
+    felem_mul(tmp, a5, a3);
+    felem_reduce(a2, tmp);
+    felem_mul(tmp, a2, a3);
+    felem_reduce(a3, tmp);
+    felem_mul(tmp, a2, a4);
+    felem_reduce(a4, tmp);
+    for (i = 0; i < 32; i++) {
+        felem_square(tmp, a5);
+        felem_reduce(a5, tmp);
+    }
+    felem_mul(tmp, a4, a5);
+    felem_reduce(out, tmp);
+}
+
+#ifdef SM2_USE_INV_SQR
+static void felem_inv_sqr(felem out, const felem in)
+{
+    felem ftmp;
+    felem a1, a2, a3, a4, a5;
+    longfelem tmp;
+    unsigned i;
+
+    felem_square(tmp, in);
+    felem_reduce(a1, tmp);
+    felem_mul(tmp, a1, in);
+    felem_reduce(a2, tmp);
+    felem_square(tmp, a2);
+    felem_reduce(ftmp, tmp);
+    felem_square(tmp, ftmp);
+    felem_reduce(ftmp, tmp);
+    felem_mul(tmp, ftmp, a2);
+    felem_reduce(a3, tmp);
+    felem_square(tmp, a3);
+    felem_reduce(ftmp, tmp);
+    felem_square(tmp, ftmp);
+    felem_reduce(ftmp, tmp);
+    felem_square(tmp, ftmp);
+    felem_reduce(ftmp, tmp);
+    felem_square(tmp, ftmp);
+    felem_reduce(ftmp, tmp);
+    felem_mul(tmp, ftmp, a3);
+    felem_reduce(a4, tmp);
+    felem_square(tmp, a4);
+    felem_reduce(a5, tmp);
+    for (i = 1; i < 8; i++) {
+        felem_square(tmp, a5);
+        felem_reduce(a5, tmp);
+    }
+    felem_mul(tmp, a5, a4);
+    felem_reduce(a5, tmp);
+    for (i = 0; i < 8; i++) {
+        felem_square(tmp, a5);
+        felem_reduce(a5, tmp);
+    }
+    felem_mul(tmp, a5, a4);
+    felem_reduce(a5, tmp);
+    for (i = 0; i < 4; i++) {
+        felem_square(tmp, a5);
+        felem_reduce(a5, tmp);
+    }
+    felem_mul(tmp, a5, a3);
+    felem_reduce(a5, tmp);
+    felem_square(tmp, a5);
+    felem_reduce(a5, tmp);
+    felem_square(tmp, a5);
+    felem_reduce(a5, tmp);
+    felem_mul(tmp, a5, a2);
+    felem_reduce(a5, tmp);
+    felem_square(tmp, a5);
+    felem_reduce(ftmp, tmp);
+    felem_mul(tmp, ftmp, in);
+    felem_reduce(a2, tmp);
+    felem_mul(tmp, a2, in);
+    felem_reduce(a4, tmp);
+    felem_square(tmp, a2);
+    felem_reduce(a5, tmp);
+    for (i = 0; i < 30; i++) {
+        felem_square(tmp, a5);
+        felem_reduce(a5, tmp);
+    }
+    felem_mul(tmp, a5, a2);
+    felem_reduce(a3, tmp);
+    felem_mul(tmp, a3, in);
+    felem_reduce(a4, tmp);
+    felem_square(tmp, a4);
+    felem_reduce(a4, tmp);
+    for (i = 0;  i < 32; i++) {
+        felem_square(tmp, a5);
+        felem_reduce(a5, tmp);
+    }
+    felem_mul(tmp, a5, a4);
+    felem_reduce(a4, tmp);
+    for (i = 0; i < 32; i++) {
+        felem_square(tmp, a5);
+        felem_reduce(a5, tmp);
+    }
+    felem_mul(tmp, a5, a4);
+    felem_reduce(a2, tmp);
+    felem_mul(tmp, a3, a2);
+    felem_reduce(a3, tmp);
+    felem_mul(tmp, a2, a4);
+    felem_reduce(a4, tmp);
+    for (i = 0; i < 32; i++) {
+        felem_square(tmp, a5);
+        felem_reduce(a5, tmp);
+    }
+    felem_mul(tmp, a5, a4);
+    felem_reduce(a2, tmp);
+    felem_mul(tmp, a3, a2);
+    felem_reduce(a3, tmp);
+    felem_mul(tmp, a2, a4);
+    felem_reduce(a4, tmp);
+    for (i = 0; i < 32; i++) {
+        felem_square(tmp, a5);
+        felem_reduce(a5, tmp);
+    }
+    felem_mul(tmp, a5, a4);
+    felem_reduce(a2, tmp);
+    felem_mul(tmp, a3, a2);
+    felem_reduce(a3, tmp);
+    felem_mul(tmp, a2, a4);
+    felem_reduce(a4, tmp);
+    for (i = 0; i < 32; i++) {
+        felem_square(tmp, a5);
+        felem_reduce(a5, tmp);
+    }
+    felem_mul(tmp, a5, a4);
+    felem_reduce(a2, tmp);
+    felem_mul(tmp, a3, a2);
+    felem_reduce(a3, tmp);
+    felem_mul(tmp, a2, a4);
+    felem_reduce(a4, tmp);
+    for (i = 0; i < 32; i++) {
+        felem_square(tmp, a5);
+        felem_reduce(a5, tmp);
+    }
+    felem_mul(tmp, a5, a3);
+    felem_reduce(a3, tmp);
+    felem_square(tmp, a3);
+    felem_reduce(a3, tmp);
+    felem_square(tmp, a3);
+    felem_reduce(out, tmp);
+}
+#endif
+
+static void smallfelem_inv_contract(smallfelem out, const smallfelem in)
+{
+    felem tmp;
+
+    smallfelem_expand(tmp, in);
+    felem_inv(tmp, tmp);
+    felem_contract(out, tmp);
+}
+
+/*-
+ * Group operations
+ * ----------------
+ *
+ * Building on top of the field operations we have the operations on the
+ * elliptic curve group itself. Points on the curve are represented in Jacobian
+ * coordinates
+ */
+
+/*-
+ * point_double calculates 2*(x_in, y_in, z_in)
+ *
+ * The method is taken from:
+ *   http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html#doubling-dbl-2001-b
+ *
+ * Outputs can equal corresponding inputs, i.e., x_out == x_in is allowed.
+ * while x_out == y_in is not (maybe this works, but it's not tested).
+ */
+static void
+point_double(felem x_out, felem y_out, felem z_out,
+             const felem x_in, const felem y_in, const felem z_in)
+{
+    longfelem tmp, tmp2;
+    felem delta, gamma, beta, alpha, ftmp, ftmp2;
+    smallfelem small1, small2;
+
+    felem_assign(ftmp, x_in);
+    /* ftmp[i] < 2^106 */
+    felem_assign(ftmp2, x_in);
+    /* ftmp2[i] < 2^106 */
+
+    /* delta = z^2 */
+    felem_square(tmp, z_in);
+    felem_reduce(delta, tmp);
+    /* delta[i] < 2^101 */
+
+    /* gamma = y^2 */
+    felem_square(tmp, y_in);
+    felem_reduce(gamma, tmp);
+
+    /* gamma[i] < 2^101 */
+    felem_shrink(small1, gamma);
+
+    /* beta = x*gamma */
+    felem_small_mul(tmp, small1, x_in);
+    felem_reduce(beta, tmp);
+    /* beta[i] < 2^101 */
+
+    /* alpha = 3*(x-delta)*(x+delta) */
+    felem_diff(ftmp, delta);
+    /* ftmp[i] < 2^105 + 2^106 < 2^107 */
+
+    felem_sum(ftmp2, delta);
+    /* ftmp2[i] < 2^105 + 2^106 < 2^107 */
+
+    felem_scalar(ftmp2, 3);
+    /* ftmp2[i] < 3 * 2^107 < 2^109 */
+
+    felem_mul(tmp, ftmp, ftmp2);
+    felem_reduce(alpha, tmp);
+    felem_shrink(small2, alpha);
+    /* alpha[i] < 2^101 */
+
+    /* x' = alpha^2 - 8*beta */
+    smallfelem_square(tmp, small2);
+    felem_reduce(x_out, tmp);
+    felem_assign(ftmp, beta);
+    felem_scalar(ftmp, 8);
+    /* ftmp[i] < 8 * 2^101 = 2^104 */
+    felem_diff(x_out, ftmp);
+    /* x_out[i] < 2^105 + 2^101 < 2^106 */
+
+    /* z' = (y + z)^2 - gamma - delta */
+    felem_sum(delta, gamma);
+    /* delta[i] < 2^101 + 2^101 = 2^102 */
+    felem_assign(ftmp, y_in);
+    felem_sum(ftmp, z_in);
+    /* ftmp[i] < 2^106 + 2^106 = 2^107 */
+    felem_square(tmp, ftmp);
+    felem_reduce(z_out, tmp);
+    felem_diff(z_out, delta);
+    /* z_out[i] < 2^105 + 2^101 < 2^106 */
+
+    /* y' = alpha*(4*beta - x') - 8*gamma^2 */
+    felem_scalar(beta, 4);
+    /* beta[i] < 4 * 2^101 = 2^103 */
+    felem_diff_zero107(beta, x_out);
+    /* beta[i] < 2^107 + 2^103 < 2^108 */
+    felem_small_mul(tmp, small2, beta);
+    /* tmp[i] < 7 * 2^64 < 2^67 */
+    smallfelem_square(tmp2, small1);
+    /* tmp2[i] < 7 * 2^64 */
+    longfelem_scalar(tmp2, 8);
+    /* tmp2[i] < 8 * 7 * 2^64 = 7 * 2^67 */
+
+    longfelem_diff(tmp, tmp2);
+    /* tmp[i] < 2^67 + 2^70 + 2^40 < 2^71 */
+    felem_reduce(y_out, tmp);
+    /* y_out[i] < 2^106 */
+}
+
+/*
+ * point_double_small is the same as point_double, except that it operates on
+ * smallfelems
+ */
+static void
+point_double_small(smallfelem x_out, smallfelem y_out, smallfelem z_out,
+                   const smallfelem x_in, const smallfelem y_in,
+                   const smallfelem z_in)
+{
+    felem felem_x_out, felem_y_out, felem_z_out;
+    felem felem_x_in, felem_y_in, felem_z_in;
+
+    smallfelem_expand(felem_x_in, x_in);
+    smallfelem_expand(felem_y_in, y_in);
+    smallfelem_expand(felem_z_in, z_in);
+    point_double(felem_x_out, felem_y_out, felem_z_out,
+                 felem_x_in, felem_y_in, felem_z_in);
+    felem_shrink(x_out, felem_x_out);
+    felem_shrink(y_out, felem_y_out);
+    felem_shrink(z_out, felem_z_out);
+}
+
+/* copy_conditional copies in to out iff mask is all ones. */
+static void copy_conditional(felem out, const felem in, limb mask)
+{
+    unsigned i;
+    for (i = 0; i < NLIMBS; ++i) {
+        const limb tmp = mask & (in[i] ^ out[i]);
+        out[i] ^= tmp;
+    }
+}
+
+/* copy_small_conditional copies in to out iff mask is all ones. */
+static void copy_small_conditional(felem out, const smallfelem in, limb mask)
+{
+    unsigned i;
+    const u64 mask64 = mask;
+    for (i = 0; i < NLIMBS; ++i) {
+        out[i] = ((limb) (in[i] & mask64)) | (out[i] & ~mask);
+    }
+}
+
+/*-
+ * point_add calculates (x1, y1, z1) + (x2, y2, z2)
+ *
+ * The method is taken from:
+ *   http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html#addition-add-2007-bl,
+ * adapted for mixed addition (z2 = 1, or z2 = 0 for the point at infinity).
+ *
+ * This function includes a branch for checking whether the two input points
+ * are equal, (while not equal to the point at infinity). This case never
+ * happens during single point multiplication, so there is no timing leak for
+ * ECDH or ECDSA signing.
+ */
+static void point_add(felem x3, felem y3, felem z3,
+                      const felem x1, const felem y1, const felem z1,
+                      const int mixed, const smallfelem x2,
+                      const smallfelem y2, const smallfelem z2)
+{
+    felem ftmp, ftmp2, ftmp3, ftmp4, ftmp5, ftmp6, x_out, y_out, z_out;
+    longfelem tmp, tmp2;
+    smallfelem small1, small2, small3, small4, small5;
+    limb x_equal, y_equal, z1_is_zero, z2_is_zero;
+
+
+    felem_shrink(small3, z1);
+
+    z1_is_zero = smallfelem_is_zero(small3);
+    z2_is_zero = smallfelem_is_zero(z2);
+
+    /* ftmp = z1z1 = z1**2 */
+    smallfelem_square(tmp, small3);
+    felem_reduce(ftmp, tmp);
+    /* ftmp[i] < 2^101 */
+    felem_shrink(small1, ftmp);
+
+    if (!mixed) {
+        /* ftmp2 = z2z2 = z2**2 */
+        smallfelem_square(tmp, z2);
+        felem_reduce(ftmp2, tmp);
+        /* ftmp2[i] < 2^101 */
+        felem_shrink(small2, ftmp2);
+
+        felem_shrink(small5, x1);
+
+        /* u1 = ftmp3 = x1*z2z2 */
+        smallfelem_mul(tmp, small5, small2);
+        felem_reduce(ftmp3, tmp);
+        /* ftmp3[i] < 2^101 */
+
+        /* ftmp5 = z1 + z2 */
+        felem_assign(ftmp5, z1);
+        felem_small_sum(ftmp5, z2);
+        /* ftmp5[i] < 2^107 */
+
+        /* ftmp5 = (z1 + z2)**2 - (z1z1 + z2z2) = 2z1z2 */
+        felem_square(tmp, ftmp5);
+        felem_reduce(ftmp5, tmp);
+        /* ftmp2 = z2z2 + z1z1 */
+        felem_sum(ftmp2, ftmp);
+        /* ftmp2[i] < 2^101 + 2^101 = 2^102 */
+        felem_diff(ftmp5, ftmp2);
+        /* ftmp5[i] < 2^105 + 2^101 < 2^106 */
+
+        /* ftmp2 = z2 * z2z2 */
+        smallfelem_mul(tmp, small2, z2);
+        felem_reduce(ftmp2, tmp);
+
+        /* s1 = ftmp2 = y1 * z2**3 */
+        felem_mul(tmp, y1, ftmp2);
+        felem_reduce(ftmp6, tmp);
+        /* ftmp6[i] < 2^101 */
+    } else {
+        /*
+         * We'll assume z2 = 1 (special case z2 = 0 is handled later)
+         */
+
+        /* u1 = ftmp3 = x1*z2z2 */
+        felem_assign(ftmp3, x1);
+        /* ftmp3[i] < 2^106 */
+
+        /* ftmp5 = 2z1z2 */
+        felem_assign(ftmp5, z1);
+        felem_scalar(ftmp5, 2);
+        /* ftmp5[i] < 2*2^106 = 2^107 */
+
+        /* s1 = ftmp2 = y1 * z2**3 */
+        felem_assign(ftmp6, y1);
+        /* ftmp6[i] < 2^106 */
+    }
+
+    /* u2 = x2*z1z1 */
+    smallfelem_mul(tmp, x2, small1);
+    felem_reduce(ftmp4, tmp);
+
+    /* h = ftmp4 = u2 - u1 */
+    felem_diff_zero107(ftmp4, ftmp3);
+    /* ftmp4[i] < 2^107 + 2^101 < 2^108 */
+    felem_shrink(small4, ftmp4);
+
+    x_equal = smallfelem_is_zero(small4);
+
+    /* z_out = ftmp5 * h */
+    felem_small_mul(tmp, small4, ftmp5);
+    felem_reduce(z_out, tmp);
+    /* z_out[i] < 2^101 */
+
+    /* ftmp = z1 * z1z1 */
+    smallfelem_mul(tmp, small1, small3);
+    felem_reduce(ftmp, tmp);
+
+    /* s2 = tmp = y2 * z1**3 */
+    felem_small_mul(tmp, y2, ftmp);
+    felem_reduce(ftmp5, tmp);
+
+    /* r = ftmp5 = (s2 - s1)*2 */
+    felem_diff_zero107(ftmp5, ftmp6);
+    /* ftmp5[i] < 2^107 + 2^107 = 2^108 */
+    felem_scalar(ftmp5, 2);
+    /* ftmp5[i] < 2^109 */
+    felem_shrink(small1, ftmp5);
+    y_equal = smallfelem_is_zero(small1);
+
+    if (x_equal && y_equal && !z1_is_zero && !z2_is_zero) {
+        point_double(x3, y3, z3, x1, y1, z1);
+        return;
+    }
+
+    /* I = ftmp = (2h)**2 */
+    felem_assign(ftmp, ftmp4);
+    felem_scalar(ftmp, 2);
+    /* ftmp[i] < 2*2^108 = 2^109 */
+    felem_square(tmp, ftmp);
+    felem_reduce(ftmp, tmp);
+
+    /* J = ftmp2 = h * I */
+    felem_mul(tmp, ftmp4, ftmp);
+    felem_reduce(ftmp2, tmp);
+
+    /* V = ftmp4 = U1 * I */
+    felem_mul(tmp, ftmp3, ftmp);
+    felem_reduce(ftmp4, tmp);
+
+    /* x_out = r**2 - J - 2V */
+    smallfelem_square(tmp, small1);
+    felem_reduce(x_out, tmp);
+    felem_assign(ftmp3, ftmp4);
+    felem_scalar(ftmp4, 2);
+    felem_sum(ftmp4, ftmp2);
+    /* ftmp4[i] < 2*2^101 + 2^101 < 2^103 */
+    felem_diff(x_out, ftmp4);
+    /* x_out[i] < 2^105 + 2^101 */
+
+    /* y_out = r(V-x_out) - 2 * s1 * J */
+    felem_diff_zero107(ftmp3, x_out);
+    /* ftmp3[i] < 2^107 + 2^101 < 2^108 */
+    felem_small_mul(tmp, small1, ftmp3);
+    felem_mul(tmp2, ftmp6, ftmp2);
+    longfelem_scalar(tmp2, 2);
+    /* tmp2[i] < 2*2^67 = 2^68 */
+    longfelem_diff(tmp, tmp2);
+    /* tmp[i] < 2^67 + 2^70 + 2^40 < 2^71 */
+    felem_reduce(y_out, tmp);
+    /* y_out[i] < 2^106 */
+
+    copy_small_conditional(x_out, x2, z1_is_zero);
+    copy_conditional(x_out, x1, z2_is_zero);
+    copy_small_conditional(y_out, y2, z1_is_zero);
+    copy_conditional(y_out, y1, z2_is_zero);
+    copy_small_conditional(z_out, z2, z1_is_zero);
+    copy_conditional(z_out, z1, z2_is_zero);
+    felem_assign(x3, x_out);
+    felem_assign(y3, y_out);
+    felem_assign(z3, z_out);
+}
+
+/*
+ * point_add_small is the same as point_add, except that it operates on
+ * smallfelems
+ */
+static void point_add_small(smallfelem x3, smallfelem y3, smallfelem z3,
+                            smallfelem x1, smallfelem y1, smallfelem z1,
+                            smallfelem x2, smallfelem y2, smallfelem z2)
+{
+    felem felem_x3, felem_y3, felem_z3;
+    felem felem_x1, felem_y1, felem_z1;
+    smallfelem_expand(felem_x1, x1);
+    smallfelem_expand(felem_y1, y1);
+    smallfelem_expand(felem_z1, z1);
+    point_add(felem_x3, felem_y3, felem_z3, felem_x1, felem_y1, felem_z1, 0,
+              x2, y2, z2);
+    felem_shrink(x3, felem_x3);
+    felem_shrink(y3, felem_y3);
+    felem_shrink(z3, felem_z3);
+}
+
+/*-
+ * Base point pre computation
+ * --------------------------
+ *
+ * Two different sorts of precomputed tables are used in the following code.
+ * Each contain various points on the curve, where each point is three field
+ * elements (x, y, z).
+ *
+ * For the base point table, z is usually 1 (0 for the point at infinity).
+ * This table has 2 * 16 elements, starting with the following:
+ * index | bits    | point
+ * ------+---------+------------------------------
+ *     0 | 0 0 0 0 | 0G
+ *     1 | 0 0 0 1 | 1G
+ *     2 | 0 0 1 0 | 2^64G
+ *     3 | 0 0 1 1 | (2^64 + 1)G
+ *     4 | 0 1 0 0 | 2^128G
+ *     5 | 0 1 0 1 | (2^128 + 1)G
+ *     6 | 0 1 1 0 | (2^128 + 2^64)G
+ *     7 | 0 1 1 1 | (2^128 + 2^64 + 1)G
+ *     8 | 1 0 0 0 | 2^192G
+ *     9 | 1 0 0 1 | (2^192 + 1)G
+ *    10 | 1 0 1 0 | (2^192 + 2^64)G
+ *    11 | 1 0 1 1 | (2^192 + 2^64 + 1)G
+ *    12 | 1 1 0 0 | (2^192 + 2^128)G
+ *    13 | 1 1 0 1 | (2^192 + 2^128 + 1)G
+ *    14 | 1 1 1 0 | (2^192 + 2^128 + 2^64)G
+ *    15 | 1 1 1 1 | (2^192 + 2^128 + 2^64 + 1)G
+ * followed by a copy of this with each element multiplied by 2^32.
+ *
+ * The reason for this is so that we can clock bits into four different
+ * locations when doing simple scalar multiplies against the base point,
+ * and then another four locations using the second 16 elements.
+ *
+ * Tables for other points have table[i] = iG for i in 0 .. 16. */
+
+/* gmul is the table of precomputed base points */
+static const smallfelem gmul[2][16][3] = {
+    {{{0, 0, 0, 0},
+      {0, 0, 0, 0},
+      {0, 0, 0, 0}},
+     {{0x715a4589334c74c7, 0x8fe30bbff2660be1, 0x5f9904466a39c994,
+       0x32c4ae2c1f198119},
+      {0x2df32e52139f0a0, 0xd0a9877cc62a4740, 0x59bdcee36b692153,
+       0xbc3736a2f4f6779c},
+      {1, 0, 0, 0}},
+     {{0xe18bd546b5824517, 0x673891d791caa486, 0xba220b99df9f9a14,
+       0x95afbd1155c1da54},
+      {0x8e4450eb334acdcb, 0xc3c7d1898a53f20d, 0x2eee750f4053017c,
+       0xe8a6d82c517388c2},
+      {1, 0, 0, 0}},
+     {{0xf81c8da9b99fba55, 0x137f6c6149feef6e, 0xcb129aa494da9ad4,
+       0x82a0f5407d123db6},
+      {0xfdeca00772c4dbc9, 0xa961b58f0cf58373, 0xecacab94e973f9c3,
+       0xf12fa4696a22ca3f},
+      {1, 0, 0, 0}},
+     {{0xeae3d9a9d13a42ed, 0x2b2308f6484e1b38, 0x3db7b24888c21f3a,
+       0xb692e5b574d55da9},
+      {0xd186469de295e5ab, 0xdb61ac1773438e6d, 0x5a924f85544926f9,
+       0xa175051b0f3fb613},
+      {1, 0, 0, 0}},
+     {{0xa72d084f62c8d58b, 0xe3d6467deaf48fd7, 0x8fe75e5a128a56a7,
+       0xc0023fe7ff2b68bd},
+      {0x64f67782316815f9, 0xb52b6d9b19a69cd2, 0x5d1ed6fa89cbbade,
+       0x796c910ee7f4ccdb},
+      {1, 0, 0, 0}},
+     {{0x1b2150c1c5f13015, 0xdaaba91b5d952c9b, 0xe8cc24c3f546142,
+       0x75a34b243705f260},
+      {0x77d195421cef1339, 0x636644aa0c3a0623, 0x4683df176eeb2444,
+       0x642ce3bd3535e74d},
+      {1, 0, 0, 0}},
+     {{0x4a59ac2c6e7ecc08, 0xaf2b71164f191d63, 0x3622a87fb284554f,
+       0xd9eb397b441e9cd0},
+      {0xa66b8a4893b6a54d, 0x26fb89a40b4a663a, 0xafa87501eedfc9f4,
+       0xf3f000bc66f98108},
+      {1, 0, 0, 0}},
+     {{0xad8bc68ce031d616, 0x16888d8ee4003187, 0x44c0757f3bb8b600,
+       0x793fae7af0164245},
+      {0x210cd042973f333b, 0x8666ff52dbd25f9, 0x65c5b129f5f7ad5d,
+       0xe03d7a8d19b3219a},
+      {1, 0, 0, 0}},
+     {{0xd68bfbace0e00392, 0x261014f7d3445dc7, 0xd9f46b2714a071ee,
+       0x1b200af30810b682},
+      {0xd91d8b12ae69bcd, 0x74a08f17bf8cd981, 0xd822913cf0d2b82d,
+       0x248b7af0b05bfad2},
+      {1, 0, 0, 0}},
+     {{0xba119a049e62f2e2, 0xf278e8a34df05ae5, 0xd269f3564eb5d180,
+       0x8e74ad0f4f957cb1},
+      {0x112ff4dabd76e2dd, 0x91373f20630fdb7f, 0xf43eab474992904c,
+       0x55a5ccc7af3b6db4},
+      {1, 0, 0, 0}},
+     {{0x5ad104a8bdd23de9, 0xf5a9e515eb71c2c1, 0x390542a0ba95c174,
+       0x4c55fb20426491bf},
+      {0x91525735ef626289, 0xd2ed977f88f09635, 0xfd48731b7a8a8521,
+       0x8f89a03b8fdebea},
+      {1, 0, 0, 0}},
+     {{0x7e8e61ea35eb8e2e, 0x1bb2700db98a762c, 0xd81ea23b7738c17c,
+       0xf9def2a46dba26a3},
+      {0x183a7912d05e329f, 0x34664a0896ccde0e, 0x56c22652614283bb,
+       0x91692899d5ff0513},
+      {1, 0, 0, 0}},
+     {{0x449d48d8f3bdbe19, 0xab95de03cc8510cb, 0xaef159463f8bfb25,
+       0xda72c379dae3ca8b},
+      {0xcba9315ce82cc3ea, 0x4e524bac38a58020, 0x36ba2752538e348c,
+       0xb170d0da75ed450f},
+      {1, 0, 0, 0}},
+     {{0x947af0f52b4f8da6, 0x7eda17d917827976, 0x5ba79a0c705853a0,
+       0xa5d9873b3fb2ddc7},
+      {0xc2a48162a5fd9ce9, 0x80ee8ae526f25f02, 0xf60c8ef6633be6a9,
+       0xe2e23f0229a84a35},
+      {1, 0, 0, 0}},
+     {{0xbc4945bd86bb6afb, 0x237eb711eba46fee, 0x7c1db58b7b86eb33,
+       0xd94eb728273b3ac7},
+      {0xbe1717e59568d0a4, 0x4a6067cc45f70212, 0x19b32eb5afc2fb17,
+       0xbe3c1e7ac3ac9d3c},
+      {1, 0, 0, 0}}},
+    {{{0, 0, 0, 0},
+      {0, 0, 0, 0},
+      {0, 0, 0, 0}},
+     {{0x68a88405ae53c1e9, 0x51e46707fd558656, 0x71e834cf86896c10,
+       0x3d251b54e10d581f},
+      {0x1884d5b0eeb19032, 0xeeaf729853e526fe, 0x5931f6831a8d8c11,
+       0x87891d33fb98b4d8},
+      {1, 0, 0, 0}},
+     {{0x9047673fcac14893, 0xf5df5d83bfb58659, 0xa6230c81642e71a,
+       0xef14b33800777791},
+      {0xcf1e99afa3386fca, 0x7ace937791313d53, 0x36fe159b6dcd01bb,
+       0xc9bc50d02e2b960a},
+      {1, 0, 0, 0}},
+     {{0x716e5a7ee12e162d, 0xbbf9bb2c62dd5a00, 0xca235ccb4144dd05,
+       0xbcb7de0f8f70520e},
+      {0x981e8964947cb8eb, 0x53c7102ea04de08d, 0xe9076332afc6a10d,
+       0x93d90f776b58c35d},
+      {1, 0, 0, 0}},
+     {{0x834dbff6678337ee, 0xc607e811fef0785a, 0xaaefc62be30a298b,
+       0xeb5ca335326afad3},
+      {0x9774fe1384af54a8, 0xca4b6ef5785388b4, 0x1346c82d66f6c642,
+       0xedcc0c2aaa2d53ce},
+      {1, 0, 0, 0}},
+     {{0xb896b3f764b9e6f4, 0x47e4018c736fb3d0, 0xfc2fc86707413920,
+       0x1a8526428e1aeae7},
+      {0x1386802650e2ae60, 0x7474dedc995384d0, 0x2c4cc396dd43b011,
+       0x63b0e9c7141de1b0},
+      {1, 0, 0, 0}},
+     {{0xeb5fb3b369d17771, 0x1fe07b18933ed257, 0xdfc4c81ce3673912,
+       0x913614c66a91a647},
+      {0x18aee853c0ba877f, 0x3109c2deceff091, 0x8532307e7e4ee08c,
+       0xcef0791a6e6ce0bb},
+      {1, 0, 0, 0}},
+     {{0xf0e9f5d8057a4a0f, 0xbbf7f8b49f125aa9, 0x51e8fdd6283187c2,
+       0xe0997d4759d36298},
+      {0x67ec3c5c6f4221c3, 0x3ea275dbc860722f, 0x152d01e23859f5e2,
+       0xfb57404312680f44},
+      {1, 0, 0, 0}},
+     {{0x21ac3df849be2a1f, 0x11006e9fc51d112f, 0x9151aa584775c857,
+       0x5159d218ba04a8d9},
+      {0x98b7d1a925fd1866, 0x8f4753cafc2ad9d8, 0x8eb91ec1569c05a9,
+       0x4abbd1ae27e13f11},
+      {1, 0, 0, 0}},
+     {{0x616f6644b2c11f4c, 0x251cd7140e540758, 0xf927a40110f02017,
+       0x92ff3cc3c1c941b6},
+      {0x3249906213f565fe, 0x4633e3ddeb9dbd4e, 0xea9a9d1ec402e6c2,
+       0xdc84ce34b14bb7cf},
+      {1, 0, 0, 0}},
+     {{0xa93e23e5436ff69a, 0x52dcb0a79b63efce, 0x34f6538a9e90cb41,
+       0x9cac08f200234bc0},
+      {0x6661825b5174a02d, 0x7d4d06de036be57, 0x589d74610ae6bd27,
+       0xa296f5577fc91a93},
+      {1, 0, 0, 0}},
+     {{0x10acefa9d29721d0, 0x8b0f6b8bb5bcd340, 0x921d318c3d86785c,
+       0xd6916f3bc16aa378},
+      {0x2a0d646a7ad84a0e, 0x7b93256c2fe7e97a, 0x5765e27626479e41,
+       0xae9da2272daaced3},
+      {1, 0, 0, 0}},
+     {{0x56fdc215f7f34ac5, 0xebcb4ff2da3877d3, 0x1eb96792aba6b832,
+       0x807ce6bea24741aa},
+      {0xff1c10109c721fb4, 0xd187d4bc796353a7, 0x7639ae749af2d303,
+       0xaff6d783d56c9286},
+      {1, 0, 0, 0}},
+     {{0x6002d51b6290dd01, 0xcba3ab0099a836a5, 0x71776611e00d2528,
+       0xfaf2cb8c87fce119},
+      {0xd445228bdf6882ae, 0xcbbfade17cbce919, 0x837b6335a2eb2453,
+       0x11ad7c4b8597f6b6},
+      {1, 0, 0, 0}},
+     {{0x48de8f368cf2e399, 0x7ae3d25630a74277, 0xdef1a9a6c505323f,
+       0xe55f203b4b8d9672},
+      {0xc58d8f0d9a1e6e97, 0xe160e6d4b2737a76, 0xd60bd087d47cbdd8,
+       0x687d41364d5fef53},
+      {1, 0, 0, 0}},
+     {{0x83f21bbe056bbf9b, 0x4c2a9d120b4ba5ab, 0xff383d1845b64e4f,
+       0x8f13cc8d06dd7867},
+      {0xf3a292d8424f0995, 0xfd2546eae7cbe44b, 0x67d14dee6c1e75a3,
+       0x53b49e6cc93fb5a8},
+      {1, 0, 0, 0}}}
+};
+
+/*
+ * select_point selects the |idx|th point from a precomputation table and
+ * copies it to out.
+ */
+static void select_point(const u64 idx, unsigned int size,
+                         const smallfelem pre_comp[16][3], smallfelem out[3])
+{
+    unsigned j;
+    u64 *outlimbs = &out[0][0];
+
+#ifdef SM2_NO_CONST_TIME
+    const u64 *inlimbs = (u64 *)&pre_comp[idx][0][0];
+    for (j = 0; j < NLIMBS * 3; j++) {
+        outlimbs[j] = inlimbs[j];
+    }
+#else
+    int i;
+    memset(out, 0, sizeof(*out) * 3);
+
+    for (i = 0; i < size; i++) {
+        const u64 *inlimbs = (u64 *)&pre_comp[i][0][0];
+        u64 mask = i ^ idx;
+        mask |= mask >> 4;
+        mask |= mask >> 2;
+        mask |= mask >> 1;
+        mask &= 1;
+        mask--;
+        for (j = 0; j < NLIMBS * 3; j++)
+            outlimbs[j] |= inlimbs[j] & mask;
+    }
+#endif
+}
+
+/* get_bit returns the |i|th bit in |in| */
+static char get_bit(const felem_bytearray in, int i)
+{
+    if ((i < 0) || (i >= 256))
+        return 0;
+    return (in[i >> 3] >> (i & 7)) & 1;
+}
+
+/*
+ * Interleaved point multiplication using precomputed point multiples: The
+ * small point multiples 0*P, 1*P, ..., 17*P are in pre_comp[], the scalars
+ * in scalars[]. If g_scalar is non-NULL, we also add this multiple of the
+ * generator, using certain (large) precomputed multiples in g_pre_comp.
+ * Output point (X, Y, Z) is stored in x_out, y_out, z_out
+ */
+static void batch_mul(felem x_out, felem y_out, felem z_out,
+                      const felem_bytearray scalars[],
+                      const unsigned num_points, const u8 *g_scalar,
+                      const int mixed, const smallfelem pre_comp[][17][3],
+                      const smallfelem g_pre_comp[2][16][3])
+{
+    int i, skip;
+    unsigned num, gen_mul = (g_scalar != NULL);
+    felem nq[3], ftmp;
+    smallfelem tmp[3];
+    u64 bits;
+    u8 sign, digit;
+
+    /* set nq to the point at infinity */
+    memset(nq, 0, sizeof(nq));
+
+
+    /*
+     * Loop over all scalars msb-to-lsb, interleaving additions of multiples
+     * of the generator (two in each of the last 32 rounds) and additions of
+     * other points multiples (every 5th round).
+     */
+    skip = 1;                   /* save two point operations in the first
+                                 * round */
+    for (i = (num_points ? 255 : 31); i >= 0; --i) {
+        /* double */
+        if (!skip)
+            point_double(nq[0], nq[1], nq[2], nq[0], nq[1], nq[2]);
+
+        /* add multiples of the generator */
+        if (gen_mul && (i <= 31)) {
+            /* first, look 32 bits upwards */
+            bits = get_bit(g_scalar, i + 224) << 3;
+            bits |= get_bit(g_scalar, i + 160) << 2;
+            bits |= get_bit(g_scalar, i + 96) << 1;
+            bits |= get_bit(g_scalar, i + 32);
+            /* select the point to add, in constant time */
+            select_point(bits, 16, g_pre_comp[1], tmp);
+
+            if (!skip) {
+                /* Arg 1 below is for "mixed" */
+                point_add(nq[0], nq[1], nq[2],
+                          nq[0], nq[1], nq[2], 1, tmp[0], tmp[1], tmp[2]);
+            } else {
+                smallfelem_expand(nq[0], tmp[0]);
+                smallfelem_expand(nq[1], tmp[1]);
+                smallfelem_expand(nq[2], tmp[2]);
+                skip = 0;
+            }
+
+            /* second, look at the current position */
+            bits = get_bit(g_scalar, i + 192) << 3;
+            bits |= get_bit(g_scalar, i + 128) << 2;
+            bits |= get_bit(g_scalar, i + 64) << 1;
+            bits |= get_bit(g_scalar, i);
+            /* select the point to add, in constant time */
+            select_point(bits, 16, g_pre_comp[0], tmp);
+            /* Arg 1 below is for "mixed" */
+            point_add(nq[0], nq[1], nq[2],
+                      nq[0], nq[1], nq[2], 1, tmp[0], tmp[1], tmp[2]);
+        }
+
+        /* do other additions every 5 doublings */
+        if (num_points && (i % 5 == 0)) {
+            /* loop over all scalars */
+            for (num = 0; num < num_points; ++num) {
+                bits = get_bit(scalars[num], i + 4) << 5;
+                bits |= get_bit(scalars[num], i + 3) << 4;
+                bits |= get_bit(scalars[num], i + 2) << 3;
+                bits |= get_bit(scalars[num], i + 1) << 2;
+                bits |= get_bit(scalars[num], i) << 1;
+                bits |= get_bit(scalars[num], i - 1);
+                ec_GFp_nistp_recode_scalar_bits(&sign, &digit, bits);
+                /*
+                 * select the point to add or subtract, in constant time
+                 */
+                select_point(digit, 17, pre_comp[num], tmp);
+                smallfelem_neg(ftmp, tmp[1]); /* (X, -Y, Z) is the negative
+                                               * point */
+                copy_small_conditional(ftmp, tmp[1], (((limb) sign) - 1));
+                felem_contract(tmp[1], ftmp);
+
+                if (!skip) {
+                    point_add(nq[0], nq[1], nq[2],
+                              nq[0], nq[1], nq[2],
+                              mixed, tmp[0], tmp[1], tmp[2]);
+                } else {
+                    smallfelem_expand(nq[0], tmp[0]);
+                    smallfelem_expand(nq[1], tmp[1]);
+                    smallfelem_expand(nq[2], tmp[2]);
+                    skip = 0;
+                }
+            }
+        }
+    }
+    felem_assign(x_out, nq[0]);
+    felem_assign(y_out, nq[1]);
+    felem_assign(z_out, nq[2]);
+}
+
+/* Precomputation for the group generator. */
+struct sm2p256_pre_comp_st {
+    smallfelem g_pre_comp[2][16][3];
+    int references;
+    CRYPTO_RWLOCK *lock;
+};
+
+const EC_METHOD *EC_GFp_sm2p256_method(void)
+{
+    static const EC_METHOD ret = {
+        EC_FLAGS_DEFAULT_OCT,
+        NID_X9_62_prime_field,
+        ec_GFp_sm2p256_group_init,
+        ec_GFp_simple_group_finish,
+        ec_GFp_simple_group_clear_finish,
+        ec_GFp_nist_group_copy,
+        ec_GFp_sm2p256_group_set_curve,
+        ec_GFp_simple_group_get_curve,
+        ec_GFp_simple_group_get_degree,
+        ec_group_simple_order_bits,
+        ec_GFp_simple_group_check_discriminant,
+        ec_GFp_simple_point_init,
+        ec_GFp_simple_point_finish,
+        ec_GFp_simple_point_clear_finish,
+        ec_GFp_simple_point_copy,
+        ec_GFp_simple_point_set_to_infinity,
+        ec_GFp_simple_set_Jprojective_coordinates_GFp,
+        ec_GFp_simple_get_Jprojective_coordinates_GFp,
+        ec_GFp_simple_point_set_affine_coordinates,
+        ec_GFp_sm2p256_point_get_affine_coordinates,
+        0 /* point_set_compressed_coordinates */ ,
+        0 /* point2oct */ ,
+        0 /* oct2point */ ,
+        ec_GFp_simple_add,
+        ec_GFp_simple_dbl,
+        ec_GFp_simple_invert,
+        ec_GFp_simple_is_at_infinity,
+        ec_GFp_simple_is_on_curve,
+        ec_GFp_simple_cmp,
+        ec_GFp_simple_make_affine,
+        ec_GFp_simple_points_make_affine,
+        ec_GFp_sm2p256_points_mul,
+        ec_GFp_sm2p256_precompute_mult,
+        ec_GFp_sm2p256_have_precompute_mult,
+        ec_GFp_nist_field_mul,
+        ec_GFp_nist_field_sqr,
+        0 /* field_div */ ,
+        0 /* field_encode */ ,
+        0 /* field_decode */ ,
+        0,                      /* field_set_to_one */
+        ec_key_simple_priv2oct,
+        ec_key_simple_oct2priv,
+        0, /* set private */
+        ec_key_simple_generate_key,
+        ec_key_simple_check_key,
+        ec_key_simple_generate_public_key,
+        0, /* keycopy */
+        0, /* keyfinish */
+        ecdh_simple_compute_key
+    };
+    return &ret;
+}
+
+/******************************************************************************/
+/*
+ * FUNCTIONS TO MANAGE PRECOMPUTATION
+ */
+
+static SM2P256_PRE_COMP *sm2p256_pre_comp_new()
+{
+    SM2P256_PRE_COMP *ret = OPENSSL_zalloc(sizeof(*ret));
+
+    if (ret == NULL) {
+        ECerr(EC_F_SM2P256_PRE_COMP_NEW, ERR_R_MALLOC_FAILURE);
+        return ret;
+    }
+
+    ret->references = 1;
+
+    ret->lock = CRYPTO_THREAD_lock_new();
+    if (ret->lock == NULL) {
+        ECerr(EC_F_SM2P256_PRE_COMP_NEW, ERR_R_MALLOC_FAILURE);
+        OPENSSL_free(ret);
+        return NULL;
+    }
+    return ret;
+}
+
+SM2P256_PRE_COMP *EC_sm2p256_pre_comp_dup(SM2P256_PRE_COMP *p)
+{
+    int i;
+    if (p != NULL)
+        CRYPTO_atomic_add(&p->references, 1, &i, p->lock);
+    return p;
+}
+
+void EC_sm2p256_pre_comp_free(SM2P256_PRE_COMP *pre)
+{
+    int i;
+
+    if (pre == NULL)
+        return;
+
+    CRYPTO_atomic_add(&pre->references, -1, &i, pre->lock);
+    REF_PRINT_COUNT("EC_sm2p256", x);
+    if (i > 0)
+        return;
+    REF_ASSERT_ISNT(i < 0);
+
+    CRYPTO_THREAD_lock_free(pre->lock);
+    OPENSSL_free(pre);
+}
+
+/******************************************************************************/
+/*
+ * OPENSSL EC_METHOD FUNCTIONS
+ */
+
+int ec_GFp_sm2p256_group_init(EC_GROUP *group)
+{
+    int ret;
+    ret = ec_GFp_simple_group_init(group);
+    group->a_is_minus3 = 1;
+    return ret;
+}
+
+int ec_GFp_sm2p256_group_set_curve(EC_GROUP *group, const BIGNUM *p,
+                                   const BIGNUM *a, const BIGNUM *b,
+                                   BN_CTX *ctx)
+{
+    int ret = 0;
+    BN_CTX *new_ctx = NULL;
+    BIGNUM *curve_p, *curve_a, *curve_b;
+
+    if (ctx == NULL)
+        if ((ctx = new_ctx = BN_CTX_new()) == NULL)
+            return 0;
+    BN_CTX_start(ctx);
+    if (((curve_p = BN_CTX_get(ctx)) == NULL) ||
+        ((curve_a = BN_CTX_get(ctx)) == NULL) ||
+        ((curve_b = BN_CTX_get(ctx)) == NULL))
+        goto err;
+    BN_bin2bn(sm2p256v1_curve_params[0], sizeof(felem_bytearray), curve_p);
+    BN_bin2bn(sm2p256v1_curve_params[1], sizeof(felem_bytearray), curve_a);
+    BN_bin2bn(sm2p256v1_curve_params[2], sizeof(felem_bytearray), curve_b);
+    if ((BN_cmp(curve_p, p)) || (BN_cmp(curve_a, a)) || (BN_cmp(curve_b, b))) {
+        ECerr(EC_F_EC_GFP_SM2P256_GROUP_SET_CURVE,
+              EC_R_WRONG_CURVE_PARAMETERS);
+        goto err;
+    }
+    group->field_mod_func = BN_sm2_mod_256;
+    ret = ec_GFp_simple_group_set_curve(group, p, a, b, ctx);
+ err:
+    BN_CTX_end(ctx);
+    BN_CTX_free(new_ctx);
+    return ret;
+}
+
+/*
+ * Takes the Jacobian coordinates (X, Y, Z) of a point and returns (X', Y') =
+ * (X/Z^2, Y/Z^3)
+ */
+int ec_GFp_sm2p256_point_get_affine_coordinates(const EC_GROUP *group,
+                                                const EC_POINT *point,
+                                                BIGNUM *x, BIGNUM *y,
+                                                BN_CTX *ctx)
+{
+    felem z1, z2, x_in, y_in;
+    smallfelem x_out, y_out;
+    longfelem tmp;
+
+    if (EC_POINT_is_at_infinity(group, point)) {
+        ECerr(EC_F_EC_GFP_SM2P256_POINT_GET_AFFINE_COORDINATES,
+              EC_R_POINT_AT_INFINITY);
+        return 0;
+    }
+    if ((!BN_to_felem(x_in, point->X)) || (!BN_to_felem(y_in, point->Y)) ||
+        (!BN_to_felem(z1, point->Z)))
+        return 0;
+
+#ifdef SM2_USE_INV_SQR
+    /* z2 = z^-2 */
+    felem_inv_sqr(z2, z1);
+    felem_mul(tmp, x_in, z2);
+#else
+    felem_inv(z2, z1);
+    felem_square(tmp, z2);
+    felem_reduce(z1, tmp);
+    felem_mul(tmp, x_in, z1);
+#endif
+
+    felem_reduce(x_in, tmp);
+    felem_contract(x_out, x_in);
+    if (x != NULL) {
+        if (!smallfelem_to_BN(x, x_out)) {
+            ECerr(EC_F_EC_GFP_SM2P256_POINT_GET_AFFINE_COORDINATES,
+                  ERR_R_BN_LIB);
+            return 0;
+        }
+    }
+    if (y != NULL) {
+#ifdef SM2_USE_INV_SQR
+        felem_square(tmp, z2);
+        felem_reduce(z2, tmp);
+        felem_mul(tmp, z1, z2);
+#else
+        felem_mul(tmp, z1, z2);
+#endif
+        felem_reduce(z1, tmp);
+        felem_mul(tmp, y_in, z1);
+        felem_reduce(y_in, tmp);
+        felem_contract(y_out, y_in);
+
+        if (!smallfelem_to_BN(y, y_out)) {
+            ECerr(EC_F_EC_GFP_SM2P256_POINT_GET_AFFINE_COORDINATES,
+                  ERR_R_BN_LIB);
+            return 0;
+        }
+    }
+    return 1;
+}
+
+/* points below is of size |num|, and tmp_smallfelems is of size |num+1| */
+static void make_points_affine(size_t num, smallfelem points[][3],
+                               smallfelem tmp_smallfelems[])
+{
+    /*
+     * Runs in constant time, unless an input is the point at infinity (which
+     * normally shouldn't happen).
+     */
+    ec_GFp_nistp_points_make_affine_internal(num,
+                                             points,
+                                             sizeof(smallfelem),
+                                             tmp_smallfelems,
+                                             (void (*)(void *))smallfelem_one,
+                                             (int (*)(const void *))
+                                             smallfelem_is_zero_int,
+                                             (void (*)(void *, const void *))
+                                             smallfelem_assign,
+                                             (void (*)(void *, const void *))
+                                             smallfelem_square_contract,
+                                             (void (*)
+                                              (void *, const void *,
+                                               const void *))
+                                             smallfelem_mul_contract,
+                                             (void (*)(void *, const void *))
+                                             smallfelem_inv_contract,
+                                             /* nothing to contract */
+                                             (void (*)(void *, const void *))
+                                             smallfelem_assign);
+}
+
+/*
+ * Computes scalar*generator + \sum scalars[i]*points[i], ignoring NULL
+ * values Result is stored in r (r can equal one of the inputs).
+ */
+int ec_GFp_sm2p256_points_mul(const EC_GROUP *group, EC_POINT *r,
+                              const BIGNUM *scalar, size_t num,
+                              const EC_POINT *points[],
+                              const BIGNUM *scalars[], BN_CTX *ctx)
+{
+    int ret = 0;
+    int j;
+    int mixed = 0;
+    BN_CTX *new_ctx = NULL;
+    BIGNUM *x, *y, *z, *tmp_scalar;
+    felem_bytearray g_secret;
+    felem_bytearray *secrets = NULL;
+    smallfelem (*pre_comp)[17][3] = NULL;
+    smallfelem *tmp_smallfelems = NULL;
+    felem_bytearray tmp;
+    unsigned i, num_bytes;
+    int have_pre_comp = 0;
+    size_t num_points = num;
+    smallfelem x_in, y_in, z_in;
+    felem x_out, y_out, z_out;
+    SM2P256_PRE_COMP *pre = NULL;
+    const smallfelem(*g_pre_comp)[16][3] = NULL;
+    EC_POINT *generator = NULL;
+    const EC_POINT *p = NULL;
+    const BIGNUM *p_scalar = NULL;
+
+    if (ctx == NULL)
+        if ((ctx = new_ctx = BN_CTX_new()) == NULL)
+            return 0;
+    BN_CTX_start(ctx);
+    if (((x = BN_CTX_get(ctx)) == NULL) ||
+        ((y = BN_CTX_get(ctx)) == NULL) ||
+        ((z = BN_CTX_get(ctx)) == NULL) ||
+        ((tmp_scalar = BN_CTX_get(ctx)) == NULL))
+        goto err;
+
+    if (scalar != NULL) {
+        pre = group->pre_comp.sm2p256;
+        if (pre)
+            /* we have precomputation, try to use it */
+            g_pre_comp = (const smallfelem(*)[16][3])pre->g_pre_comp;
+        else {
+            /* try to use the standard precomputation */
+            g_pre_comp = &gmul[0];
+        }
+        generator = EC_POINT_new(group);
+        if (generator == NULL)
+            goto err;
+        /* get the generator from precomputation */
+        if (!smallfelem_to_BN(x, g_pre_comp[0][1][0]) ||
+            !smallfelem_to_BN(y, g_pre_comp[0][1][1]) ||
+            !smallfelem_to_BN(z, g_pre_comp[0][1][2])) {
+            ECerr(EC_F_EC_GFP_SM2P256_POINTS_MUL, ERR_R_BN_LIB);
+            goto err;
+        }
+        if (!EC_POINT_set_Jprojective_coordinates_GFp(group,
+                                                      generator, x, y, z,
+                                                      ctx))
+            goto err;
+        if (0 == EC_POINT_cmp(group, generator, group->generator, ctx))
+            /* precomputation matches generator */
+            have_pre_comp = 1;
+        else
+            /*
+             * we don't have valid precomputation: treat the generator as a
+             * random point
+             */
+            num_points++;
+    }
+    if (num_points > 0) {
+        if (num_points >= 3) {
+            /*
+             * unless we precompute multiples for just one or two points,
+             * converting those into affine form is time well spent
+             */
+            mixed = 1;
+        }
+        secrets = OPENSSL_malloc(sizeof(*secrets) * num_points);
+        pre_comp = OPENSSL_malloc(sizeof(*pre_comp) * num_points);
+        if (mixed)
+            tmp_smallfelems =
+              OPENSSL_malloc(sizeof(*tmp_smallfelems) * (num_points * 17 + 1));
+        if ((secrets == NULL) || (pre_comp == NULL)
+            || (mixed && (tmp_smallfelems == NULL))) {
+            ECerr(EC_F_EC_GFP_SM2P256_POINTS_MUL, ERR_R_MALLOC_FAILURE);
+            goto err;
+        }
+
+        /*
+         * we treat NULL scalars as 0, and NULL points as points at infinity,
+         * i.e., they contribute nothing to the linear combination
+         */
+        memset(secrets, 0, sizeof(*secrets) * num_points);
+        memset(pre_comp, 0, sizeof(*pre_comp) * num_points);
+        for (i = 0; i < num_points; ++i) {
+            if (i == num)
+                /*
+                 * we didn't have a valid precomputation, so we pick the
+                 * generator
+                 */
+            {
+                p = EC_GROUP_get0_generator(group);
+                p_scalar = scalar;
+            } else
+                /* the i^th point */
+            {
+                p = points[i];
+                p_scalar = scalars[i];
+            }
+            if ((p_scalar != NULL) && (p != NULL)) {
+                /* reduce scalar to 0 <= scalar < 2^256 */
+                if ((BN_num_bits(p_scalar) > 256)
+                    || (BN_is_negative(p_scalar))) {
+                    /*
+                     * this is an unusual input, and we don't guarantee
+                     * constant-timeness
+                     */
+                    if (!BN_nnmod(tmp_scalar, p_scalar, group->order, ctx)) {
+                        ECerr(EC_F_EC_GFP_SM2P256_POINTS_MUL, ERR_R_BN_LIB);
+                        goto err;
+                    }
+                    num_bytes = BN_bn2bin(tmp_scalar, tmp);
+                } else
+                    num_bytes = BN_bn2bin(p_scalar, tmp);
+                flip_endian(secrets[i], tmp, num_bytes);
+                /* precompute multiples */
+                if ((!BN_to_felem(x_out, p->X)) ||
+                    (!BN_to_felem(y_out, p->Y)) ||
+                    (!BN_to_felem(z_out, p->Z)))
+                    goto err;
+                felem_shrink(pre_comp[i][1][0], x_out);
+                felem_shrink(pre_comp[i][1][1], y_out);
+                felem_shrink(pre_comp[i][1][2], z_out);
+                for (j = 2; j <= 16; ++j) {
+                    if (j & 1) {
+                        point_add_small(pre_comp[i][j][0], pre_comp[i][j][1],
+                                        pre_comp[i][j][2], pre_comp[i][1][0],
+                                        pre_comp[i][1][1], pre_comp[i][1][2],
+                                        pre_comp[i][j - 1][0],
+                                        pre_comp[i][j - 1][1],
+                                        pre_comp[i][j - 1][2]);
+                    } else {
+                        point_double_small(pre_comp[i][j][0],
+                                           pre_comp[i][j][1],
+                                           pre_comp[i][j][2],
+                                           pre_comp[i][j / 2][0],
+                                           pre_comp[i][j / 2][1],
+                                           pre_comp[i][j / 2][2]);
+                    }
+                }
+            }
+        }
+        if (mixed)
+            make_points_affine(num_points * 17, pre_comp[0], tmp_smallfelems);
+    }
+
+    /* the scalar for the generator */
+    if ((scalar != NULL) && (have_pre_comp)) {
+        memset(g_secret, 0, sizeof(g_secret));
+        /* reduce scalar to 0 <= scalar < 2^256 */
+        if ((BN_num_bits(scalar) > 256) || (BN_is_negative(scalar))) {
+            /*
+             * this is an unusual input, and we don't guarantee
+             * constant-timeness
+             */
+            if (!BN_nnmod(tmp_scalar, scalar, group->order, ctx)) {
+                ECerr(EC_F_EC_GFP_SM2P256_POINTS_MUL, ERR_R_BN_LIB);
+                goto err;
+            }
+            num_bytes = BN_bn2bin(tmp_scalar, tmp);
+        } else
+            num_bytes = BN_bn2bin(scalar, tmp);
+        flip_endian(g_secret, tmp, num_bytes);
+        /* do the multiplication with generator precomputation */
+        batch_mul(x_out, y_out, z_out,
+                  (const felem_bytearray(*))secrets, num_points,
+                  g_secret,
+                  mixed, (const smallfelem(*)[17][3])pre_comp, g_pre_comp);
+    } else
+        /* do the multiplication without generator precomputation */
+        batch_mul(x_out, y_out, z_out,
+                  (const felem_bytearray(*))secrets, num_points,
+                  NULL, mixed, (const smallfelem(*)[17][3])pre_comp, NULL);
+    /* reduce the output to its unique minimal representation */
+    felem_contract(x_in, x_out);
+    felem_contract(y_in, y_out);
+    felem_contract(z_in, z_out);
+    if ((!smallfelem_to_BN(x, x_in)) || (!smallfelem_to_BN(y, y_in)) ||
+        (!smallfelem_to_BN(z, z_in))) {
+        ECerr(EC_F_EC_GFP_SM2P256_POINTS_MUL, ERR_R_BN_LIB);
+        goto err;
+    }
+    ret = EC_POINT_set_Jprojective_coordinates_GFp(group, r, x, y, z, ctx);
+
+ err:
+    BN_CTX_end(ctx);
+    EC_POINT_free(generator);
+    BN_CTX_free(new_ctx);
+    OPENSSL_free(secrets);
+    OPENSSL_free(pre_comp);
+    OPENSSL_free(tmp_smallfelems);
+    return ret;
+}
+
+int ec_GFp_sm2p256_precompute_mult(EC_GROUP *group, BN_CTX *ctx)
+{
+    int ret = 0;
+    SM2P256_PRE_COMP *pre = NULL;
+    int i, j;
+    BN_CTX *new_ctx = NULL;
+    BIGNUM *x, *y;
+    EC_POINT *generator = NULL;
+    smallfelem tmp_smallfelems[32];
+    felem x_tmp, y_tmp, z_tmp;
+
+    /* throw away old precomputation */
+    EC_pre_comp_free(group);
+    if (ctx == NULL)
+        if ((ctx = new_ctx = BN_CTX_new()) == NULL)
+            return 0;
+    BN_CTX_start(ctx);
+    if (((x = BN_CTX_get(ctx)) == NULL) || ((y = BN_CTX_get(ctx)) == NULL))
+        goto err;
+    /* get the generator */
+    if (group->generator == NULL)
+        goto err;
+    generator = EC_POINT_new(group);
+    if (generator == NULL)
+        goto err;
+    BN_bin2bn(sm2p256v1_curve_params[3], sizeof(felem_bytearray), x);
+    BN_bin2bn(sm2p256v1_curve_params[4], sizeof(felem_bytearray), y);
+    if (!EC_POINT_set_affine_coordinates_GFp(group, generator, x, y, ctx))
+        goto err;
+    if ((pre = sm2p256_pre_comp_new()) == NULL)
+        goto err;
+    /*
+     * if the generator is the standard one, use built-in precomputation
+     */
+    if (0 == EC_POINT_cmp(group, generator, group->generator, ctx)) {
+        memcpy(pre->g_pre_comp, gmul, sizeof(pre->g_pre_comp));
+        goto done;
+    }
+    if ((!BN_to_felem(x_tmp, group->generator->X)) ||
+        (!BN_to_felem(y_tmp, group->generator->Y)) ||
+        (!BN_to_felem(z_tmp, group->generator->Z)))
+        goto err;
+    felem_shrink(pre->g_pre_comp[0][1][0], x_tmp);
+    felem_shrink(pre->g_pre_comp[0][1][1], y_tmp);
+    felem_shrink(pre->g_pre_comp[0][1][2], z_tmp);
+    /*
+     * compute 2^64*G, 2^128*G, 2^192*G for the first table, 2^32*G, 2^96*G,
+     * 2^160*G, 2^224*G for the second one
+     */
+    for (i = 1; i <= 8; i <<= 1) {
+        point_double_small(pre->g_pre_comp[1][i][0], pre->g_pre_comp[1][i][1],
+                           pre->g_pre_comp[1][i][2], pre->g_pre_comp[0][i][0],
+                           pre->g_pre_comp[0][i][1],
+                           pre->g_pre_comp[0][i][2]);
+        for (j = 0; j < 31; ++j) {
+            point_double_small(pre->g_pre_comp[1][i][0],
+                               pre->g_pre_comp[1][i][1],
+                               pre->g_pre_comp[1][i][2],
+                               pre->g_pre_comp[1][i][0],
+                               pre->g_pre_comp[1][i][1],
+                               pre->g_pre_comp[1][i][2]);
+        }
+        if (i == 8)
+            break;
+        point_double_small(pre->g_pre_comp[0][2 * i][0],
+                           pre->g_pre_comp[0][2 * i][1],
+                           pre->g_pre_comp[0][2 * i][2],
+                           pre->g_pre_comp[1][i][0], pre->g_pre_comp[1][i][1],
+                           pre->g_pre_comp[1][i][2]);
+        for (j = 0; j < 31; ++j) {
+            point_double_small(pre->g_pre_comp[0][2 * i][0],
+                               pre->g_pre_comp[0][2 * i][1],
+                               pre->g_pre_comp[0][2 * i][2],
+                               pre->g_pre_comp[0][2 * i][0],
+                               pre->g_pre_comp[0][2 * i][1],
+                               pre->g_pre_comp[0][2 * i][2]);
+        }
+    }
+    for (i = 0; i < 2; i++) {
+        /* g_pre_comp[i][0] is the point at infinity */
+        memset(pre->g_pre_comp[i][0], 0, sizeof(pre->g_pre_comp[i][0]));
+        /* the remaining multiples */
+        /* 2^64*G + 2^128*G resp. 2^96*G + 2^160*G */
+        point_add_small(pre->g_pre_comp[i][6][0], pre->g_pre_comp[i][6][1],
+                        pre->g_pre_comp[i][6][2], pre->g_pre_comp[i][4][0],
+                        pre->g_pre_comp[i][4][1], pre->g_pre_comp[i][4][2],
+                        pre->g_pre_comp[i][2][0], pre->g_pre_comp[i][2][1],
+                        pre->g_pre_comp[i][2][2]);
+        /* 2^64*G + 2^192*G resp. 2^96*G + 2^224*G */
+        point_add_small(pre->g_pre_comp[i][10][0], pre->g_pre_comp[i][10][1],
+                        pre->g_pre_comp[i][10][2], pre->g_pre_comp[i][8][0],
+                        pre->g_pre_comp[i][8][1], pre->g_pre_comp[i][8][2],
+                        pre->g_pre_comp[i][2][0], pre->g_pre_comp[i][2][1],
+                        pre->g_pre_comp[i][2][2]);
+        /* 2^128*G + 2^192*G resp. 2^160*G + 2^224*G */
+        point_add_small(pre->g_pre_comp[i][12][0], pre->g_pre_comp[i][12][1],
+                        pre->g_pre_comp[i][12][2], pre->g_pre_comp[i][8][0],
+                        pre->g_pre_comp[i][8][1], pre->g_pre_comp[i][8][2],
+                        pre->g_pre_comp[i][4][0], pre->g_pre_comp[i][4][1],
+                        pre->g_pre_comp[i][4][2]);
+        /*
+         * 2^64*G + 2^128*G + 2^192*G resp. 2^96*G + 2^160*G + 2^224*G
+         */
+        point_add_small(pre->g_pre_comp[i][14][0], pre->g_pre_comp[i][14][1],
+                        pre->g_pre_comp[i][14][2], pre->g_pre_comp[i][12][0],
+                        pre->g_pre_comp[i][12][1], pre->g_pre_comp[i][12][2],
+                        pre->g_pre_comp[i][2][0], pre->g_pre_comp[i][2][1],
+                        pre->g_pre_comp[i][2][2]);
+        for (j = 1; j < 8; ++j) {
+            /* odd multiples: add G resp. 2^32*G */
+            point_add_small(pre->g_pre_comp[i][2 * j + 1][0],
+                            pre->g_pre_comp[i][2 * j + 1][1],
+                            pre->g_pre_comp[i][2 * j + 1][2],
+                            pre->g_pre_comp[i][2 * j][0],
+                            pre->g_pre_comp[i][2 * j][1],
+                            pre->g_pre_comp[i][2 * j][2],
+                            pre->g_pre_comp[i][1][0],
+                            pre->g_pre_comp[i][1][1],
+                            pre->g_pre_comp[i][1][2]);
+        }
+    }
+    make_points_affine(31, &(pre->g_pre_comp[0][1]), tmp_smallfelems);
+
+ done:
+    SETPRECOMP(group, sm2p256, pre);
+    pre = NULL;
+    ret = 1;
+
+ err:
+    BN_CTX_end(ctx);
+    EC_POINT_free(generator);
+    BN_CTX_free(new_ctx);
+    EC_sm2p256_pre_comp_free(pre);
+    return ret;
+}
+
+int ec_GFp_sm2p256_have_precompute_mult(const EC_GROUP *group)
+{
+    return HAVEPRECOMP(group, sm2p256);
+}
+#endif
diff --git a/crypto/ec/ecp_sm2z256.c b/crypto/ec/ecp_sm2z256.c
new file mode 100644
index 000000000..0a448e105
--- /dev/null
+++ b/crypto/ec/ecp_sm2z256.c
@@ -0,0 +1,1617 @@
+/* ====================================================================
+ * Copyright (c) 2014 - 2018 The GmSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the GmSSL Project.
+ *    (http://gmssl.org/)"
+ *
+ * 4. The name "GmSSL Project" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    guanzhi1980@gmail.com.
+ *
+ * 5. Products derived from this software may not be called "GmSSL"
+ *    nor may "GmSSL" appear in their names without prior written
+ *    permission of the GmSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the GmSSL Project
+ *    (http://gmssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE GmSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE GmSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+/*
+ * Copyright 2014-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/******************************************************************************
+ *                                                                            *
+ * Copyright 2014 Intel Corporation                                           *
+ *                                                                            *
+ * Licensed under the Apache License, Version 2.0 (the "License");            *
+ * you may not use this file except in compliance with the License.           *
+ * You may obtain a copy of the License at                                    *
+ *                                                                            *
+ *    http://www.apache.org/licenses/LICENSE-2.0                              *
+ *                                                                            *
+ * Unless required by applicable law or agreed to in writing, software        *
+ * distributed under the License is distributed on an "AS IS" BASIS,          *
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   *
+ * See the License for the specific language governing permissions and        *
+ * limitations under the License.                                             *
+ *                                                                            *
+ ******************************************************************************
+ *                                                                            *
+ * Developers and authors:                                                    *
+ * Shay Gueron (1, 2), and Vlad Krasnov (1)                                   *
+ * (1) Intel Corporation, Israel Development Center                           *
+ * (2) University of Haifa                                                    *
+ * Reference:                                                                 *
+ * S.Gueron and V.Krasnov, "Fast Prime Field Elliptic Curve Cryptography with *
+ *                          256 Bit Primes"                                   *
+ *                                                                            *
+ ******************************************************************************/
+
+#include 
+
+#include "internal/cryptlib.h"
+#include "internal/bn_int.h"
+#include "ec_lcl.h"
+
+#if BN_BITS2 != 64
+# define TOBN(hi,lo)    lo,hi
+#else
+# define TOBN(hi,lo)    ((BN_ULONG)hi<<32|lo)
+#endif
+
+#if defined(__GNUC__)
+# define ALIGN32        __attribute((aligned(32)))
+#elif defined(_MSC_VER)
+# define ALIGN32        __declspec(align(32))
+#else
+# define ALIGN32
+#endif
+
+#define ALIGNPTR(p,N)   ((unsigned char *)p+N-(size_t)p%N)
+#define P256_LIMBS      (256/BN_BITS2)
+
+typedef unsigned short u16;
+
+typedef struct {
+    BN_ULONG X[P256_LIMBS];
+    BN_ULONG Y[P256_LIMBS];
+    BN_ULONG Z[P256_LIMBS];
+} P256_POINT;
+
+typedef struct {
+    BN_ULONG X[P256_LIMBS];
+    BN_ULONG Y[P256_LIMBS];
+} P256_POINT_AFFINE;
+
+typedef P256_POINT_AFFINE PRECOMP256_ROW[64];
+
+/* structure for precomputed multiples of the generator */
+struct sm2z256_pre_comp_st {
+    const EC_GROUP *group;      /* Parent EC_GROUP object */
+    size_t w;                   /* Window size */
+    /*
+     * Constant time access to the X and Y coordinates of the pre-computed,
+     * generator multiplies, in the Montgomery domain. Pre-calculated
+     * multiplies are stored in affine form.
+     */
+    PRECOMP256_ROW *precomp;
+    void *precomp_storage;
+    int references;
+    CRYPTO_RWLOCK *lock;
+};
+
+/* Functions implemented in assembly */
+/*
+ * Most of below mentioned functions *preserve* the property of inputs
+ * being fully reduced, i.e. being in [0, modulus) range. Simply put if
+ * inputs are fully reduced, then output is too. Note that reverse is
+ * not true, in sense that given partially reduced inputs output can be
+ * either, not unlikely reduced. And "most" in first sentence refers to
+ * the fact that given the calculations flow one can tolerate that
+ * addition, 1st function below, produces partially reduced result *if*
+ * multiplications by 2 and 3, which customarily use addition, fully
+ * reduce it. This effectively gives two options: a) addition produces
+ * fully reduced result [as long as inputs are, just like remaining
+ * functions]; b) addition is allowed to produce partially reduced
+ * result, but multiplications by 2 and 3 perform additional reduction
+ * step. Choice between the two can be platform-specific, but it was a)
+ * in all cases so far...
+ */
+/* Modular add: res = a+b mod P   */
+void ecp_sm2z256_add(BN_ULONG res[P256_LIMBS],
+                     const BN_ULONG a[P256_LIMBS],
+                     const BN_ULONG b[P256_LIMBS]);
+/* Modular mul by 2: res = 2*a mod P */
+void ecp_sm2z256_mul_by_2(BN_ULONG res[P256_LIMBS],
+                          const BN_ULONG a[P256_LIMBS]);
+/* Modular mul by 3: res = 3*a mod P */
+void ecp_sm2z256_mul_by_3(BN_ULONG res[P256_LIMBS],
+                          const BN_ULONG a[P256_LIMBS]);
+
+/* Modular div by 2: res = a/2 mod P */
+void ecp_sm2z256_div_by_2(BN_ULONG res[P256_LIMBS],
+                          const BN_ULONG a[P256_LIMBS]);
+/* Modular sub: res = a-b mod P   */
+void ecp_sm2z256_sub(BN_ULONG res[P256_LIMBS],
+                     const BN_ULONG a[P256_LIMBS],
+                     const BN_ULONG b[P256_LIMBS]);
+/* Modular neg: res = -a mod P    */
+void ecp_sm2z256_neg(BN_ULONG res[P256_LIMBS], const BN_ULONG a[P256_LIMBS]);
+/* Montgomery mul: res = a*b*2^-256 mod P */
+void ecp_sm2z256_mul_mont(BN_ULONG res[P256_LIMBS],
+                          const BN_ULONG a[P256_LIMBS],
+                          const BN_ULONG b[P256_LIMBS]);
+/* Montgomery sqr: res = a*a*2^-256 mod P */
+void ecp_sm2z256_sqr_mont(BN_ULONG res[P256_LIMBS],
+                          const BN_ULONG a[P256_LIMBS]);
+/* Convert a number from Montgomery domain, by multiplying with 1 */
+void ecp_sm2z256_from_mont(BN_ULONG res[P256_LIMBS],
+                           const BN_ULONG in[P256_LIMBS]);
+/* Convert a number to Montgomery domain, by multiplying with 2^512 mod P*/
+void ecp_sm2z256_to_mont(BN_ULONG res[P256_LIMBS],
+                         const BN_ULONG in[P256_LIMBS]);
+/* Functions that perform constant time access to the precomputed tables */
+void ecp_sm2z256_scatter_w5(P256_POINT *val,
+                            const P256_POINT *in_t, int idx);
+void ecp_sm2z256_gather_w5(P256_POINT *val,
+                           const P256_POINT *in_t, int idx);
+void ecp_sm2z256_scatter_w7(P256_POINT_AFFINE *val,
+                            const P256_POINT_AFFINE *in_t, int idx);
+void ecp_sm2z256_gather_w7(P256_POINT_AFFINE *val,
+                           const P256_POINT_AFFINE *in_t, int idx);
+
+/* One converted into the Montgomery domain */
+static const BN_ULONG ONE[P256_LIMBS] = {
+    TOBN(0x00000000, 0x00000001), TOBN(0x00000000, 0xffffffff),
+    TOBN(0x00000000, 0x00000000), TOBN(0x00000001, 0x00000000)
+};
+
+static SM2Z256_PRE_COMP *ecp_sm2z256_pre_comp_new(const EC_GROUP *group);
+
+/* Precomputed tables for the default generator */
+extern const PRECOMP256_ROW ecp_sm2z256_precomputed[37];
+
+/* Recode window to a signed digit, see ecp_nistputil.c for details */
+static unsigned int _booth_recode_w5(unsigned int in)
+{
+    unsigned int s, d;
+
+    s = ~((in >> 5) - 1);
+    d = (1 << 6) - in - 1;
+    d = (d & s) | (in & ~s);
+    d = (d >> 1) + (d & 1);
+
+    return (d << 1) + (s & 1);
+}
+
+static unsigned int _booth_recode_w7(unsigned int in)
+{
+    unsigned int s, d;
+
+    s = ~((in >> 7) - 1);
+    d = (1 << 8) - in - 1;
+    d = (d & s) | (in & ~s);
+    d = (d >> 1) + (d & 1);
+
+    return (d << 1) + (s & 1);
+}
+
+static void copy_conditional(BN_ULONG dst[P256_LIMBS],
+                             const BN_ULONG src[P256_LIMBS], BN_ULONG move)
+{
+    BN_ULONG mask1 = 0-move;
+    BN_ULONG mask2 = ~mask1;
+
+    dst[0] = (src[0] & mask1) ^ (dst[0] & mask2);
+    dst[1] = (src[1] & mask1) ^ (dst[1] & mask2);
+    dst[2] = (src[2] & mask1) ^ (dst[2] & mask2);
+    dst[3] = (src[3] & mask1) ^ (dst[3] & mask2);
+    if (P256_LIMBS == 8) {
+        dst[4] = (src[4] & mask1) ^ (dst[4] & mask2);
+        dst[5] = (src[5] & mask1) ^ (dst[5] & mask2);
+        dst[6] = (src[6] & mask1) ^ (dst[6] & mask2);
+        dst[7] = (src[7] & mask1) ^ (dst[7] & mask2);
+    }
+}
+
+static BN_ULONG is_zero(BN_ULONG in)
+{
+    in |= (0 - in);
+    in = ~in;
+    in >>= BN_BITS2 - 1;
+    return in;
+}
+
+static BN_ULONG is_equal(const BN_ULONG a[P256_LIMBS],
+                         const BN_ULONG b[P256_LIMBS])
+{
+    BN_ULONG res;
+
+    res = a[0] ^ b[0];
+    res |= a[1] ^ b[1];
+    res |= a[2] ^ b[2];
+    res |= a[3] ^ b[3];
+    if (P256_LIMBS == 8) {
+        res |= a[4] ^ b[4];
+        res |= a[5] ^ b[5];
+        res |= a[6] ^ b[6];
+        res |= a[7] ^ b[7];
+    }
+
+    return is_zero(res);
+}
+
+static BN_ULONG is_one(const BIGNUM *z)
+{
+    BN_ULONG res = 0;
+    BN_ULONG *a = bn_get_words(z);
+
+    if (bn_get_top(z) == (P256_LIMBS - P256_LIMBS / 8)) {
+        res = a[0] ^ ONE[0];
+        res |= a[1] ^ ONE[1];
+        res |= a[2] ^ ONE[2];
+        res |= a[3] ^ ONE[3];
+        if (P256_LIMBS == 8) {
+            res |= a[4] ^ ONE[4];
+            res |= a[5] ^ ONE[5];
+            res |= a[6] ^ ONE[6];
+            /*
+             * no check for a[7] (being zero) on 32-bit platforms,
+             * because value of "one" takes only 7 limbs.
+             */
+        }
+        res = is_zero(res);
+    }
+
+    return res;
+}
+
+#ifndef ECP_SM2Z256_REFERENCE_IMPLEMENTATION
+// the following functions are not correct in asm
+void ecp_sm2z256_point_double(P256_POINT *r, const P256_POINT *a);
+void ecp_sm2z256_point_add(P256_POINT *r,
+                           const P256_POINT *a, const P256_POINT *b);
+void ecp_sm2z256_point_add_affine(P256_POINT *r,
+                                  const P256_POINT *a,
+                                  const P256_POINT_AFFINE *b);
+#else
+/* Point double: r = 2*a */
+static void ecp_sm2z256_point_double(P256_POINT *r, const P256_POINT *a)
+{
+    BN_ULONG S[P256_LIMBS];
+    BN_ULONG M[P256_LIMBS];
+    BN_ULONG Zsqr[P256_LIMBS];
+    BN_ULONG tmp0[P256_LIMBS];
+
+    const BN_ULONG *in_x = a->X;
+    const BN_ULONG *in_y = a->Y;
+    const BN_ULONG *in_z = a->Z;
+
+    BN_ULONG *res_x = r->X;
+    BN_ULONG *res_y = r->Y;
+    BN_ULONG *res_z = r->Z;
+
+    ecp_sm2z256_mul_by_2(S, in_y);
+
+    ecp_sm2z256_sqr_mont(Zsqr, in_z);
+
+    ecp_sm2z256_sqr_mont(S, S);
+
+    ecp_sm2z256_mul_mont(res_z, in_z, in_y);
+    ecp_sm2z256_mul_by_2(res_z, res_z);
+
+    ecp_sm2z256_add(M, in_x, Zsqr);
+    ecp_sm2z256_sub(Zsqr, in_x, Zsqr);
+
+    ecp_sm2z256_sqr_mont(res_y, S);
+    ecp_sm2z256_div_by_2(res_y, res_y);
+
+    ecp_sm2z256_mul_mont(M, M, Zsqr);
+    ecp_sm2z256_mul_by_3(M, M);
+
+    ecp_sm2z256_mul_mont(S, S, in_x);
+    ecp_sm2z256_mul_by_2(tmp0, S);
+
+    ecp_sm2z256_sqr_mont(res_x, M);
+
+    ecp_sm2z256_sub(res_x, res_x, tmp0);
+    ecp_sm2z256_sub(S, S, res_x);
+
+    ecp_sm2z256_mul_mont(S, S, M);
+    ecp_sm2z256_sub(res_y, S, res_y);
+}
+
+/* Point addition: r = a+b */
+static void ecp_sm2z256_point_add(P256_POINT *r,
+                                  const P256_POINT *a, const P256_POINT *b)
+{
+    BN_ULONG U2[P256_LIMBS], S2[P256_LIMBS];
+    BN_ULONG U1[P256_LIMBS], S1[P256_LIMBS];
+    BN_ULONG Z1sqr[P256_LIMBS];
+    BN_ULONG Z2sqr[P256_LIMBS];
+    BN_ULONG H[P256_LIMBS], R[P256_LIMBS];
+    BN_ULONG Hsqr[P256_LIMBS];
+    BN_ULONG Rsqr[P256_LIMBS];
+    BN_ULONG Hcub[P256_LIMBS];
+
+    BN_ULONG res_x[P256_LIMBS];
+    BN_ULONG res_y[P256_LIMBS];
+    BN_ULONG res_z[P256_LIMBS];
+
+    BN_ULONG in1infty, in2infty;
+
+    const BN_ULONG *in1_x = a->X;
+    const BN_ULONG *in1_y = a->Y;
+    const BN_ULONG *in1_z = a->Z;
+
+    const BN_ULONG *in2_x = b->X;
+    const BN_ULONG *in2_y = b->Y;
+    const BN_ULONG *in2_z = b->Z;
+
+    /*
+     * Infinity in encoded as (,,0)
+     */
+    in1infty = (in1_z[0] | in1_z[1] | in1_z[2] | in1_z[3]);
+    if (P256_LIMBS == 8)
+        in1infty |= (in1_z[4] | in1_z[5] | in1_z[6] | in1_z[7]);
+
+    in2infty = (in2_z[0] | in2_z[1] | in2_z[2] | in2_z[3]);
+    if (P256_LIMBS == 8)
+        in2infty |= (in2_z[4] | in2_z[5] | in2_z[6] | in2_z[7]);
+
+    in1infty = is_zero(in1infty);
+    in2infty = is_zero(in2infty);
+
+    ecp_sm2z256_sqr_mont(Z2sqr, in2_z);        /* Z2^2 */
+    ecp_sm2z256_sqr_mont(Z1sqr, in1_z);        /* Z1^2 */
+
+    ecp_sm2z256_mul_mont(S1, Z2sqr, in2_z);    /* S1 = Z2^3 */
+    ecp_sm2z256_mul_mont(S2, Z1sqr, in1_z);    /* S2 = Z1^3 */
+
+    ecp_sm2z256_mul_mont(S1, S1, in1_y);       /* S1 = Y1*Z2^3 */
+    ecp_sm2z256_mul_mont(S2, S2, in2_y);       /* S2 = Y2*Z1^3 */
+    ecp_sm2z256_sub(R, S2, S1);                /* R = S2 - S1 */
+
+    ecp_sm2z256_mul_mont(U1, in1_x, Z2sqr);    /* U1 = X1*Z2^2 */
+    ecp_sm2z256_mul_mont(U2, in2_x, Z1sqr);    /* U2 = X2*Z1^2 */
+    ecp_sm2z256_sub(H, U2, U1);                /* H = U2 - U1 */
+
+    /*
+     * This should not happen during sign/ecdh, so no constant time violation
+     */
+    if (is_equal(U1, U2) && !in1infty && !in2infty) {
+        if (is_equal(S1, S2)) {
+            ecp_sm2z256_point_double(r, a);
+            return;
+        } else {
+            memset(r, 0, sizeof(*r));
+            return;
+        }
+    }
+
+    ecp_sm2z256_sqr_mont(Rsqr, R);             /* R^2 */
+    ecp_sm2z256_mul_mont(res_z, H, in1_z);     /* Z3 = H*Z1*Z2 */
+    ecp_sm2z256_sqr_mont(Hsqr, H);             /* H^2 */
+    ecp_sm2z256_mul_mont(res_z, res_z, in2_z); /* Z3 = H*Z1*Z2 */
+    ecp_sm2z256_mul_mont(Hcub, Hsqr, H);       /* H^3 */
+
+    ecp_sm2z256_mul_mont(U2, U1, Hsqr);        /* U1*H^2 */
+    ecp_sm2z256_mul_by_2(Hsqr, U2);            /* 2*U1*H^2 */
+
+    ecp_sm2z256_sub(res_x, Rsqr, Hsqr);
+    ecp_sm2z256_sub(res_x, res_x, Hcub);
+
+    ecp_sm2z256_sub(res_y, U2, res_x);
+
+    ecp_sm2z256_mul_mont(S2, S1, Hcub);
+    ecp_sm2z256_mul_mont(res_y, R, res_y);
+    ecp_sm2z256_sub(res_y, res_y, S2);
+
+    copy_conditional(res_x, in2_x, in1infty);
+    copy_conditional(res_y, in2_y, in1infty);
+    copy_conditional(res_z, in2_z, in1infty);
+
+    copy_conditional(res_x, in1_x, in2infty);
+    copy_conditional(res_y, in1_y, in2infty);
+    copy_conditional(res_z, in1_z, in2infty);
+
+    memcpy(r->X, res_x, sizeof(res_x));
+    memcpy(r->Y, res_y, sizeof(res_y));
+    memcpy(r->Z, res_z, sizeof(res_z));
+}
+
+/* Point addition when b is known to be affine: r = a+b */
+static void ecp_sm2z256_point_add_affine(P256_POINT *r,
+                                         const P256_POINT *a,
+                                         const P256_POINT_AFFINE *b)
+{
+    BN_ULONG U2[P256_LIMBS], S2[P256_LIMBS];
+    BN_ULONG Z1sqr[P256_LIMBS];
+    BN_ULONG H[P256_LIMBS], R[P256_LIMBS];
+    BN_ULONG Hsqr[P256_LIMBS];
+    BN_ULONG Rsqr[P256_LIMBS];
+    BN_ULONG Hcub[P256_LIMBS];
+
+    BN_ULONG res_x[P256_LIMBS];
+    BN_ULONG res_y[P256_LIMBS];
+    BN_ULONG res_z[P256_LIMBS];
+
+    BN_ULONG in1infty, in2infty;
+
+    const BN_ULONG *in1_x = a->X;
+    const BN_ULONG *in1_y = a->Y;
+    const BN_ULONG *in1_z = a->Z;
+
+    const BN_ULONG *in2_x = b->X;
+    const BN_ULONG *in2_y = b->Y;
+
+    /*
+     * Infinity in encoded as (,,0)
+     */
+    in1infty = (in1_z[0] | in1_z[1] | in1_z[2] | in1_z[3]);
+    if (P256_LIMBS == 8)
+        in1infty |= (in1_z[4] | in1_z[5] | in1_z[6] | in1_z[7]);
+
+    /*
+     * In affine representation we encode infinity as (0,0), which is
+     * not on the curve, so it is OK
+     */
+    in2infty = (in2_x[0] | in2_x[1] | in2_x[2] | in2_x[3] |
+                in2_y[0] | in2_y[1] | in2_y[2] | in2_y[3]);
+    if (P256_LIMBS == 8)
+        in2infty |= (in2_x[4] | in2_x[5] | in2_x[6] | in2_x[7] |
+                     in2_y[4] | in2_y[5] | in2_y[6] | in2_y[7]);
+
+    in1infty = is_zero(in1infty);
+    in2infty = is_zero(in2infty);
+
+    ecp_sm2z256_sqr_mont(Z1sqr, in1_z);        /* Z1^2 */
+
+    ecp_sm2z256_mul_mont(U2, in2_x, Z1sqr);    /* U2 = X2*Z1^2 */
+    ecp_sm2z256_sub(H, U2, in1_x);             /* H = U2 - U1 */
+
+    ecp_sm2z256_mul_mont(S2, Z1sqr, in1_z);    /* S2 = Z1^3 */
+
+    ecp_sm2z256_mul_mont(res_z, H, in1_z);     /* Z3 = H*Z1*Z2 */
+
+    ecp_sm2z256_mul_mont(S2, S2, in2_y);       /* S2 = Y2*Z1^3 */
+    ecp_sm2z256_sub(R, S2, in1_y);             /* R = S2 - S1 */
+
+    ecp_sm2z256_sqr_mont(Hsqr, H);             /* H^2 */
+    ecp_sm2z256_sqr_mont(Rsqr, R);             /* R^2 */
+    ecp_sm2z256_mul_mont(Hcub, Hsqr, H);       /* H^3 */
+
+    ecp_sm2z256_mul_mont(U2, in1_x, Hsqr);     /* U1*H^2 */
+    ecp_sm2z256_mul_by_2(Hsqr, U2);            /* 2*U1*H^2 */
+
+    ecp_sm2z256_sub(res_x, Rsqr, Hsqr);
+    ecp_sm2z256_sub(res_x, res_x, Hcub);
+    ecp_sm2z256_sub(H, U2, res_x);
+
+    ecp_sm2z256_mul_mont(S2, in1_y, Hcub);
+    ecp_sm2z256_mul_mont(H, H, R);
+    ecp_sm2z256_sub(res_y, H, S2);
+
+    copy_conditional(res_x, in2_x, in1infty);
+    copy_conditional(res_x, in1_x, in2infty);
+
+    copy_conditional(res_y, in2_y, in1infty);
+    copy_conditional(res_y, in1_y, in2infty);
+
+    copy_conditional(res_z, ONE, in1infty);
+    copy_conditional(res_z, in1_z, in2infty);
+
+    memcpy(r->X, res_x, sizeof(res_x));
+    memcpy(r->Y, res_y, sizeof(res_y));
+    memcpy(r->Z, res_z, sizeof(res_z));
+}
+#endif
+
+/* r = in^-1 mod p */
+static void ecp_sm2z256_mod_inverse(BN_ULONG r[P256_LIMBS],
+                                    const BN_ULONG in[P256_LIMBS])
+{
+    BN_ULONG a1[P256_LIMBS];
+    BN_ULONG a2[P256_LIMBS];
+    BN_ULONG a3[P256_LIMBS];
+    BN_ULONG a4[P256_LIMBS];
+    BN_ULONG a5[P256_LIMBS];
+    int i;
+
+    ecp_sm2z256_sqr_mont(a1, in);
+    ecp_sm2z256_mul_mont(a2, a1, in);
+    ecp_sm2z256_sqr_mont(a3, a2);
+    ecp_sm2z256_sqr_mont(a3, a3);
+    ecp_sm2z256_mul_mont(a3, a3, a2);
+    ecp_sm2z256_sqr_mont(a4, a3);
+    ecp_sm2z256_sqr_mont(a4, a4);
+    ecp_sm2z256_sqr_mont(a4, a4);
+    ecp_sm2z256_sqr_mont(a4, a4);
+    ecp_sm2z256_mul_mont(a4, a4, a3);
+    ecp_sm2z256_sqr_mont(a5, a4);
+    for (i = 0; i < 7; i++) {
+        ecp_sm2z256_sqr_mont(a5, a5);
+    }
+    ecp_sm2z256_mul_mont(a5, a5, a4);
+    for (i = 0; i < 8; i++) {
+        ecp_sm2z256_sqr_mont(a5, a5);
+    }
+    ecp_sm2z256_mul_mont(a5, a5, a4);
+    ecp_sm2z256_sqr_mont(a5, a5);
+    ecp_sm2z256_sqr_mont(a5, a5);
+    ecp_sm2z256_sqr_mont(a5, a5);
+    ecp_sm2z256_sqr_mont(a5, a5);
+    ecp_sm2z256_mul_mont(a5, a5, a3);
+    ecp_sm2z256_sqr_mont(a5, a5);
+    ecp_sm2z256_sqr_mont(a5, a5);
+    ecp_sm2z256_mul_mont(a5, a5, a2);
+    ecp_sm2z256_sqr_mont(a5, a5);
+    ecp_sm2z256_mul_mont(a5, a5, in);
+    ecp_sm2z256_sqr_mont(a4, a5);
+    ecp_sm2z256_mul_mont(a3, a4, a1);
+    ecp_sm2z256_sqr_mont(a5, a4);
+    for (i = 0; i < 30; i++) {
+        ecp_sm2z256_sqr_mont(a5, a5);
+    }
+    ecp_sm2z256_mul_mont(a4, a5, a4);
+    ecp_sm2z256_sqr_mont(a4, a4);
+    ecp_sm2z256_mul_mont(a4, a4, in);
+    ecp_sm2z256_mul_mont(a3, a4, a2);
+    for (i = 0; i < 33; i++) {
+        ecp_sm2z256_sqr_mont(a5, a5);
+    }
+    ecp_sm2z256_mul_mont(a2, a5, a3);
+    ecp_sm2z256_mul_mont(a3, a2, a3);
+    for (i = 0; i < 32; i++) {
+        ecp_sm2z256_sqr_mont(a5, a5);
+    }
+    ecp_sm2z256_mul_mont(a2, a5, a3);
+    ecp_sm2z256_mul_mont(a3, a2, a3);
+    ecp_sm2z256_mul_mont(a4, a2, a4);
+    for (i = 0; i < 32; i++) {
+        ecp_sm2z256_sqr_mont(a5, a5);
+    }
+    ecp_sm2z256_mul_mont(a2, a5, a3);
+    ecp_sm2z256_mul_mont(a3, a2, a3);
+    ecp_sm2z256_mul_mont(a4, a2, a4);
+    for (i = 0; i < 32; i++) {
+        ecp_sm2z256_sqr_mont(a5, a5);
+    }
+    ecp_sm2z256_mul_mont(a2, a5, a3);
+    ecp_sm2z256_mul_mont(a3, a2, a3);
+    ecp_sm2z256_mul_mont(a4, a2, a4);
+    for (i = 0; i < 32; i++) {
+        ecp_sm2z256_sqr_mont(a5, a5);
+    }
+    ecp_sm2z256_mul_mont(a2, a5, a3);
+    ecp_sm2z256_mul_mont(a3, a2, a3);
+    ecp_sm2z256_mul_mont(a4, a2, a4);
+    for (i = 0; i < 32; i++) {
+        ecp_sm2z256_sqr_mont(a5, a5);
+    }
+    ecp_sm2z256_mul_mont(r, a4, a5);
+}
+
+/*
+ * ecp_sm2z256_bignum_to_field_elem copies the contents of |in| to |out| and
+ * returns one if it fits. Otherwise it returns zero.
+ */
+__owur static int ecp_sm2z256_bignum_to_field_elem(BN_ULONG out[P256_LIMBS],
+                                                   const BIGNUM *in)
+{
+    return bn_copy_words(out, in, P256_LIMBS);
+}
+
+/* r = sum(scalar[i]*point[i]) */
+__owur static int ecp_sm2z256_windowed_mul(const EC_GROUP *group,
+                                           P256_POINT *r,
+                                           const BIGNUM **scalar,
+                                           const EC_POINT **point,
+                                           size_t num, BN_CTX *ctx)
+{
+    size_t i;
+    int j, ret = 0;
+    unsigned int idx;
+    unsigned char (*p_str)[33] = NULL;
+    const unsigned int window_size = 5;
+    const unsigned int mask = (1 << (window_size + 1)) - 1;
+    unsigned int wvalue;
+    P256_POINT *temp;           /* place for 5 temporary points */
+    const BIGNUM **scalars = NULL;
+    P256_POINT (*table)[16] = NULL;
+    void *table_storage = NULL;
+
+    if ((num * 16 + 6) > OPENSSL_MALLOC_MAX_NELEMS(P256_POINT)
+        || (table_storage =
+            OPENSSL_malloc((num * 16 + 5) * sizeof(P256_POINT) + 64)) == NULL
+        || (p_str =
+            OPENSSL_malloc(num * 33 * sizeof(unsigned char))) == NULL
+        || (scalars = OPENSSL_malloc(num * sizeof(BIGNUM *))) == NULL) {
+        ECerr(EC_F_ECP_SM2Z256_WINDOWED_MUL, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+
+    table = (void *)ALIGNPTR(table_storage, 64);
+    temp = (P256_POINT *)(table + num);
+
+    for (i = 0; i < num; i++) {
+        P256_POINT *row = table[i];
+
+        /* This is an unusual input, we don't guarantee constant-timeness. */
+        if ((BN_num_bits(scalar[i]) > 256) || BN_is_negative(scalar[i])) {
+            BIGNUM *mod;
+
+            if ((mod = BN_CTX_get(ctx)) == NULL)
+                goto err;
+            if (!BN_nnmod(mod, scalar[i], group->order, ctx)) {
+                ECerr(EC_F_ECP_SM2Z256_WINDOWED_MUL, ERR_R_BN_LIB);
+                goto err;
+            }
+            scalars[i] = mod;
+        } else
+            scalars[i] = scalar[i];
+
+        for (j = 0; j < bn_get_top(scalars[i]) * BN_BYTES; j += BN_BYTES) {
+            BN_ULONG d = bn_get_words(scalars[i])[j / BN_BYTES];
+
+            p_str[i][j + 0] = (unsigned char)d;
+            p_str[i][j + 1] = (unsigned char)(d >> 8);
+            p_str[i][j + 2] = (unsigned char)(d >> 16);
+            p_str[i][j + 3] = (unsigned char)(d >>= 24);
+            if (BN_BYTES == 8) {
+                d >>= 8;
+                p_str[i][j + 4] = (unsigned char)d;
+                p_str[i][j + 5] = (unsigned char)(d >> 8);
+                p_str[i][j + 6] = (unsigned char)(d >> 16);
+                p_str[i][j + 7] = (unsigned char)(d >> 24);
+            }
+        }
+        for (; j < 33; j++)
+            p_str[i][j] = 0;
+
+        if (!ecp_sm2z256_bignum_to_field_elem(temp[0].X, point[i]->X)
+            || !ecp_sm2z256_bignum_to_field_elem(temp[0].Y, point[i]->Y)
+            || !ecp_sm2z256_bignum_to_field_elem(temp[0].Z, point[i]->Z)) {
+            ECerr(EC_F_ECP_SM2Z256_WINDOWED_MUL,
+                  EC_R_COORDINATES_OUT_OF_RANGE);
+            goto err;
+        }
+
+        /*
+         * row[0] is implicitly (0,0,0) (the point at infinity), therefore it
+         * is not stored. All other values are actually stored with an offset
+         * of -1 in table.
+         */
+
+        ecp_sm2z256_scatter_w5  (row, &temp[0], 1);
+        ecp_sm2z256_point_double(&temp[1], &temp[0]);              /*1+1=2  */
+        ecp_sm2z256_scatter_w5  (row, &temp[1], 2);
+        ecp_sm2z256_point_add   (&temp[2], &temp[1], &temp[0]);    /*2+1=3  */
+        ecp_sm2z256_scatter_w5  (row, &temp[2], 3);
+        ecp_sm2z256_point_double(&temp[1], &temp[1]);              /*2*2=4  */
+        ecp_sm2z256_scatter_w5  (row, &temp[1], 4);
+        ecp_sm2z256_point_double(&temp[2], &temp[2]);              /*2*3=6  */
+        ecp_sm2z256_scatter_w5  (row, &temp[2], 6);
+        ecp_sm2z256_point_add   (&temp[3], &temp[1], &temp[0]);    /*4+1=5  */
+        ecp_sm2z256_scatter_w5  (row, &temp[3], 5);
+        ecp_sm2z256_point_add   (&temp[4], &temp[2], &temp[0]);    /*6+1=7  */
+        ecp_sm2z256_scatter_w5  (row, &temp[4], 7);
+        ecp_sm2z256_point_double(&temp[1], &temp[1]);              /*2*4=8  */
+        ecp_sm2z256_scatter_w5  (row, &temp[1], 8);
+        ecp_sm2z256_point_double(&temp[2], &temp[2]);              /*2*6=12 */
+        ecp_sm2z256_scatter_w5  (row, &temp[2], 12);
+        ecp_sm2z256_point_double(&temp[3], &temp[3]);              /*2*5=10 */
+        ecp_sm2z256_scatter_w5  (row, &temp[3], 10);
+        ecp_sm2z256_point_double(&temp[4], &temp[4]);              /*2*7=14 */
+        ecp_sm2z256_scatter_w5  (row, &temp[4], 14);
+        ecp_sm2z256_point_add   (&temp[2], &temp[2], &temp[0]);    /*12+1=13*/
+        ecp_sm2z256_scatter_w5  (row, &temp[2], 13);
+        ecp_sm2z256_point_add   (&temp[3], &temp[3], &temp[0]);    /*10+1=11*/
+        ecp_sm2z256_scatter_w5  (row, &temp[3], 11);
+        ecp_sm2z256_point_add   (&temp[4], &temp[4], &temp[0]);    /*14+1=15*/
+        ecp_sm2z256_scatter_w5  (row, &temp[4], 15);
+        ecp_sm2z256_point_add   (&temp[2], &temp[1], &temp[0]);    /*8+1=9  */
+        ecp_sm2z256_scatter_w5  (row, &temp[2], 9);
+        ecp_sm2z256_point_double(&temp[1], &temp[1]);              /*2*8=16 */
+        ecp_sm2z256_scatter_w5  (row, &temp[1], 16);
+    }
+
+    idx = 255;
+
+    wvalue = p_str[0][(idx - 1) / 8];
+    wvalue = (wvalue >> ((idx - 1) % 8)) & mask;
+
+    /*
+     * We gather to temp[0], because we know it's position relative
+     * to table
+     */
+    ecp_sm2z256_gather_w5(&temp[0], table[0], _booth_recode_w5(wvalue) >> 1);
+    memcpy(r, &temp[0], sizeof(temp[0]));
+
+    while (idx >= 5) {
+        for (i = (idx == 255 ? 1 : 0); i < num; i++) {
+            unsigned int off = (idx - 1) / 8;
+
+            wvalue = p_str[i][off] | p_str[i][off + 1] << 8;
+            wvalue = (wvalue >> ((idx - 1) % 8)) & mask;
+
+            wvalue = _booth_recode_w5(wvalue);
+
+            ecp_sm2z256_gather_w5(&temp[0], table[i], wvalue >> 1);
+
+            ecp_sm2z256_neg(temp[1].Y, temp[0].Y);
+            copy_conditional(temp[0].Y, temp[1].Y, (wvalue & 1));
+
+            ecp_sm2z256_point_add(r, r, &temp[0]);
+        }
+
+        idx -= window_size;
+
+        ecp_sm2z256_point_double(r, r);
+        ecp_sm2z256_point_double(r, r);
+        ecp_sm2z256_point_double(r, r);
+        ecp_sm2z256_point_double(r, r);
+        ecp_sm2z256_point_double(r, r);
+    }
+
+    /* Final window */
+    for (i = 0; i < num; i++) {
+        wvalue = p_str[i][0];
+        wvalue = (wvalue << 1) & mask;
+
+        wvalue = _booth_recode_w5(wvalue);
+
+        ecp_sm2z256_gather_w5(&temp[0], table[i], wvalue >> 1);
+
+        ecp_sm2z256_neg(temp[1].Y, temp[0].Y);
+        copy_conditional(temp[0].Y, temp[1].Y, wvalue & 1);
+
+        ecp_sm2z256_point_add(r, r, &temp[0]);
+    }
+
+    ret = 1;
+ err:
+    OPENSSL_free(table_storage);
+    OPENSSL_free(p_str);
+    OPENSSL_free(scalars);
+    return ret;
+}
+
+/* Coordinates of G, for which we have precomputed tables */
+const static BN_ULONG def_xG[P256_LIMBS] = {
+     TOBN(0x61328990, 0xf418029e), TOBN(0x3e7981ed, 0xdca6c050),
+     TOBN(0xd6a1ed99, 0xac24c3c3), TOBN(0x91167a5e, 0xe1c13b05)
+};
+
+const static BN_ULONG def_yG[P256_LIMBS] = {
+     TOBN(0xc1354e59, 0x3c2d0ddd), TOBN(0xc1f5e578, 0x8d3295fa),
+     TOBN(0x8d4cfb06, 0x6e2a48f8), TOBN(0x63cd65d4, 0x81d735bd)
+};
+
+/*
+ * ecp_sm2z256_is_affine_G returns one if |generator| is the standard, P-256
+ * generator.
+ */
+static int ecp_sm2z256_is_affine_G(const EC_POINT *generator)
+{
+    return (bn_get_top(generator->X) == P256_LIMBS) &&
+        (bn_get_top(generator->Y) == P256_LIMBS) &&
+        is_equal(bn_get_words(generator->X), def_xG) &&
+        is_equal(bn_get_words(generator->Y), def_yG) &&
+        is_one(generator->Z);
+}
+
+__owur static int ecp_sm2z256_mult_precompute(EC_GROUP *group, BN_CTX *ctx)
+{
+    /*
+     * We precompute a table for a Booth encoded exponent (wNAF) based
+     * computation. Each table holds 64 values for safe access, with an
+     * implicit value of infinity at index zero. We use window of size 7, and
+     * therefore require ceil(256/7) = 37 tables.
+     */
+    const BIGNUM *order;
+    EC_POINT *P = NULL, *T = NULL;
+    const EC_POINT *generator;
+    SM2Z256_PRE_COMP *pre_comp;
+    BN_CTX *new_ctx = NULL;
+    int i, j, k, ret = 0;
+    size_t w;
+
+    PRECOMP256_ROW *preComputedTable = NULL;
+    unsigned char *precomp_storage = NULL;
+
+    /* if there is an old SM2Z256_PRE_COMP object, throw it away */
+    EC_pre_comp_free(group);
+    generator = EC_GROUP_get0_generator(group);
+    if (generator == NULL) {
+        ECerr(EC_F_ECP_SM2Z256_MULT_PRECOMPUTE, EC_R_UNDEFINED_GENERATOR);
+        return 0;
+    }
+
+    if (ecp_sm2z256_is_affine_G(generator)) {
+        /*
+         * No need to calculate tables for the standard generator because we
+         * have them statically.
+         */
+        return 1;
+    }
+
+    if ((pre_comp = ecp_sm2z256_pre_comp_new(group)) == NULL)
+        return 0;
+
+    if (ctx == NULL) {
+        ctx = new_ctx = BN_CTX_new();
+        if (ctx == NULL)
+            goto err;
+    }
+
+    BN_CTX_start(ctx);
+
+    order = EC_GROUP_get0_order(group);
+    if (order == NULL)
+        goto err;
+
+    if (BN_is_zero(order)) {
+        ECerr(EC_F_ECP_SM2Z256_MULT_PRECOMPUTE, EC_R_UNKNOWN_ORDER);
+        goto err;
+    }
+
+    w = 7;
+
+    if ((precomp_storage =
+         OPENSSL_malloc(37 * 64 * sizeof(P256_POINT_AFFINE) + 64)) == NULL) {
+        ECerr(EC_F_ECP_SM2Z256_MULT_PRECOMPUTE, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+
+    preComputedTable = (void *)ALIGNPTR(precomp_storage, 64);
+
+    P = EC_POINT_new(group);
+    T = EC_POINT_new(group);
+    if (P == NULL || T == NULL)
+        goto err;
+
+    /*
+     * The zero entry is implicitly infinity, and we skip it, storing other
+     * values with -1 offset.
+     */
+    if (!EC_POINT_copy(T, generator))
+        goto err;
+
+    for (k = 0; k < 64; k++) {
+        if (!EC_POINT_copy(P, T))
+            goto err;
+        for (j = 0; j < 37; j++) {
+            P256_POINT_AFFINE temp;
+            /*
+             * It would be faster to use EC_POINTs_make_affine and
+             * make multiple points affine at the same time.
+             */
+            if (!EC_POINT_make_affine(group, P, ctx))
+                goto err;
+            if (!ecp_sm2z256_bignum_to_field_elem(temp.X, P->X) ||
+                !ecp_sm2z256_bignum_to_field_elem(temp.Y, P->Y)) {
+                ECerr(EC_F_ECP_SM2Z256_MULT_PRECOMPUTE,
+                      EC_R_COORDINATES_OUT_OF_RANGE);
+                goto err;
+            }
+            ecp_sm2z256_scatter_w7(preComputedTable[j], &temp, k);
+            for (i = 0; i < 7; i++) {
+                if (!EC_POINT_dbl(group, P, P, ctx))
+                    goto err;
+            }
+        }
+        if (!EC_POINT_add(group, T, T, generator, ctx))
+            goto err;
+    }
+
+    pre_comp->group = group;
+    pre_comp->w = w;
+    pre_comp->precomp = preComputedTable;
+    pre_comp->precomp_storage = precomp_storage;
+    precomp_storage = NULL;
+    SETPRECOMP(group, sm2z256, pre_comp);
+    pre_comp = NULL;
+    ret = 1;
+
+ err:
+    if (ctx != NULL)
+        BN_CTX_end(ctx);
+    BN_CTX_free(new_ctx);
+
+    EC_sm2z256_pre_comp_free(pre_comp);
+    OPENSSL_free(precomp_storage);
+    EC_POINT_free(P);
+    EC_POINT_free(T);
+    return ret;
+}
+
+/*
+ * Note that by default ECP_NISTZ256_AVX2 is undefined. While it's great
+ * code processing 4 points in parallel, corresponding serial operation
+ * is several times slower, because it uses 29x29=58-bit multiplication
+ * as opposite to 64x64=128-bit in integer-only scalar case. As result
+ * it doesn't provide *significant* performance improvement. Note that
+ * just defining ECP_NISTZ256_AVX2 is not sufficient to make it work,
+ * you'd need to compile even asm/ecp_nistz256-avx.pl module.
+ */
+#if defined(ECP_NISTZ256_AVX2)
+# if !(defined(__x86_64) || defined(__x86_64__) || \
+       defined(_M_AMD64) || defined(_MX64)) || \
+     !(defined(__GNUC__) || defined(_MSC_VER)) /* this is for ALIGN32 */
+#  undef ECP_NISTZ256_AVX2
+# else
+/* Constant time access, loading four values, from four consecutive tables */
+void ecp_sm2z256_avx2_multi_gather_w7(void *result, const void *in,
+                                      int index0, int index1, int index2,
+                                      int index3);
+void ecp_sm2z256_avx2_transpose_convert(void *RESULTx4, const void *in);
+void ecp_sm2z256_avx2_convert_transpose_back(void *result, const void *Ax4);
+void ecp_sm2z256_avx2_point_add_affine_x4(void *RESULTx4, const void *Ax4,
+                                          const void *Bx4);
+void ecp_sm2z256_avx2_point_add_affines_x4(void *RESULTx4, const void *Ax4,
+                                           const void *Bx4);
+void ecp_sm2z256_avx2_to_mont(void *RESULTx4, const void *Ax4);
+void ecp_sm2z256_avx2_from_mont(void *RESULTx4, const void *Ax4);
+void ecp_sm2z256_avx2_set1(void *RESULTx4);
+int ecp_sm2z_avx2_eligible(void);
+
+static void booth_recode_w7(unsigned char *sign,
+                            unsigned char *digit, unsigned char in)
+{
+    unsigned char s, d;
+
+    s = ~((in >> 7) - 1);
+    d = (1 << 8) - in - 1;
+    d = (d & s) | (in & ~s);
+    d = (d >> 1) + (d & 1);
+
+    *sign = s & 1;
+    *digit = d;
+}
+
+/*
+ * ecp_sm2z256_avx2_mul_g performs multiplication by G, using only the
+ * precomputed table. It does 4 affine point additions in parallel,
+ * significantly speeding up point multiplication for a fixed value.
+ */
+static void ecp_sm2z256_avx2_mul_g(P256_POINT *r,
+                                   unsigned char p_str[33],
+                                   const P256_POINT_AFFINE(*preComputedTable)[64])
+{
+    const unsigned int window_size = 7;
+    const unsigned int mask = (1 << (window_size + 1)) - 1;
+    unsigned int wvalue;
+    /* Using 4 windows at a time */
+    unsigned char sign0, digit0;
+    unsigned char sign1, digit1;
+    unsigned char sign2, digit2;
+    unsigned char sign3, digit3;
+    unsigned int idx = 0;
+    BN_ULONG tmp[P256_LIMBS];
+    int i;
+
+    ALIGN32 BN_ULONG aX4[4 * 9 * 3] = { 0 };
+    ALIGN32 BN_ULONG bX4[4 * 9 * 2] = { 0 };
+    ALIGN32 P256_POINT_AFFINE point_arr[4];
+    ALIGN32 P256_POINT res_point_arr[4];
+
+    /* Initial four windows */
+    wvalue = *((u16 *) & p_str[0]);
+    wvalue = (wvalue << 1) & mask;
+    idx += window_size;
+    booth_recode_w7(&sign0, &digit0, wvalue);
+    wvalue = *((u16 *) & p_str[(idx - 1) / 8]);
+    wvalue = (wvalue >> ((idx - 1) % 8)) & mask;
+    idx += window_size;
+    booth_recode_w7(&sign1, &digit1, wvalue);
+    wvalue = *((u16 *) & p_str[(idx - 1) / 8]);
+    wvalue = (wvalue >> ((idx - 1) % 8)) & mask;
+    idx += window_size;
+    booth_recode_w7(&sign2, &digit2, wvalue);
+    wvalue = *((u16 *) & p_str[(idx - 1) / 8]);
+    wvalue = (wvalue >> ((idx - 1) % 8)) & mask;
+    idx += window_size;
+    booth_recode_w7(&sign3, &digit3, wvalue);
+
+    ecp_sm2z256_avx2_multi_gather_w7(point_arr, preComputedTable[0],
+                                     digit0, digit1, digit2, digit3);
+
+    ecp_sm2z256_neg(tmp, point_arr[0].Y);
+    copy_conditional(point_arr[0].Y, tmp, sign0);
+    ecp_sm2z256_neg(tmp, point_arr[1].Y);
+    copy_conditional(point_arr[1].Y, tmp, sign1);
+    ecp_sm2z256_neg(tmp, point_arr[2].Y);
+    copy_conditional(point_arr[2].Y, tmp, sign2);
+    ecp_sm2z256_neg(tmp, point_arr[3].Y);
+    copy_conditional(point_arr[3].Y, tmp, sign3);
+
+    ecp_sm2z256_avx2_transpose_convert(aX4, point_arr);
+    ecp_sm2z256_avx2_to_mont(aX4, aX4);
+    ecp_sm2z256_avx2_to_mont(&aX4[4 * 9], &aX4[4 * 9]);
+    ecp_sm2z256_avx2_set1(&aX4[4 * 9 * 2]);
+
+    wvalue = *((u16 *) & p_str[(idx - 1) / 8]);
+    wvalue = (wvalue >> ((idx - 1) % 8)) & mask;
+    idx += window_size;
+    booth_recode_w7(&sign0, &digit0, wvalue);
+    wvalue = *((u16 *) & p_str[(idx - 1) / 8]);
+    wvalue = (wvalue >> ((idx - 1) % 8)) & mask;
+    idx += window_size;
+    booth_recode_w7(&sign1, &digit1, wvalue);
+    wvalue = *((u16 *) & p_str[(idx - 1) / 8]);
+    wvalue = (wvalue >> ((idx - 1) % 8)) & mask;
+    idx += window_size;
+    booth_recode_w7(&sign2, &digit2, wvalue);
+    wvalue = *((u16 *) & p_str[(idx - 1) / 8]);
+    wvalue = (wvalue >> ((idx - 1) % 8)) & mask;
+    idx += window_size;
+    booth_recode_w7(&sign3, &digit3, wvalue);
+
+    ecp_sm2z256_avx2_multi_gather_w7(point_arr, preComputedTable[4 * 1],
+                                     digit0, digit1, digit2, digit3);
+
+    ecp_sm2z256_neg(tmp, point_arr[0].Y);
+    copy_conditional(point_arr[0].Y, tmp, sign0);
+    ecp_sm2z256_neg(tmp, point_arr[1].Y);
+    copy_conditional(point_arr[1].Y, tmp, sign1);
+    ecp_sm2z256_neg(tmp, point_arr[2].Y);
+    copy_conditional(point_arr[2].Y, tmp, sign2);
+    ecp_sm2z256_neg(tmp, point_arr[3].Y);
+    copy_conditional(point_arr[3].Y, tmp, sign3);
+
+    ecp_sm2z256_avx2_transpose_convert(bX4, point_arr);
+    ecp_sm2z256_avx2_to_mont(bX4, bX4);
+    ecp_sm2z256_avx2_to_mont(&bX4[4 * 9], &bX4[4 * 9]);
+    /* Optimized when both inputs are affine */
+    ecp_sm2z256_avx2_point_add_affines_x4(aX4, aX4, bX4);
+
+    for (i = 2; i < 9; i++) {
+        wvalue = *((u16 *) & p_str[(idx - 1) / 8]);
+        wvalue = (wvalue >> ((idx - 1) % 8)) & mask;
+        idx += window_size;
+        booth_recode_w7(&sign0, &digit0, wvalue);
+        wvalue = *((u16 *) & p_str[(idx - 1) / 8]);
+        wvalue = (wvalue >> ((idx - 1) % 8)) & mask;
+        idx += window_size;
+        booth_recode_w7(&sign1, &digit1, wvalue);
+        wvalue = *((u16 *) & p_str[(idx - 1) / 8]);
+        wvalue = (wvalue >> ((idx - 1) % 8)) & mask;
+        idx += window_size;
+        booth_recode_w7(&sign2, &digit2, wvalue);
+        wvalue = *((u16 *) & p_str[(idx - 1) / 8]);
+        wvalue = (wvalue >> ((idx - 1) % 8)) & mask;
+        idx += window_size;
+        booth_recode_w7(&sign3, &digit3, wvalue);
+
+        ecp_sm2z256_avx2_multi_gather_w7(point_arr,
+                                         preComputedTable[4 * i],
+                                         digit0, digit1, digit2, digit3);
+
+        ecp_sm2z256_neg(tmp, point_arr[0].Y);
+        copy_conditional(point_arr[0].Y, tmp, sign0);
+        ecp_sm2z256_neg(tmp, point_arr[1].Y);
+        copy_conditional(point_arr[1].Y, tmp, sign1);
+        ecp_sm2z256_neg(tmp, point_arr[2].Y);
+        copy_conditional(point_arr[2].Y, tmp, sign2);
+        ecp_sm2z256_neg(tmp, point_arr[3].Y);
+        copy_conditional(point_arr[3].Y, tmp, sign3);
+
+        ecp_sm2z256_avx2_transpose_convert(bX4, point_arr);
+        ecp_sm2z256_avx2_to_mont(bX4, bX4);
+        ecp_sm2z256_avx2_to_mont(&bX4[4 * 9], &bX4[4 * 9]);
+
+        ecp_sm2z256_avx2_point_add_affine_x4(aX4, aX4, bX4);
+    }
+
+    ecp_sm2z256_avx2_from_mont(&aX4[4 * 9 * 0], &aX4[4 * 9 * 0]);
+    ecp_sm2z256_avx2_from_mont(&aX4[4 * 9 * 1], &aX4[4 * 9 * 1]);
+    ecp_sm2z256_avx2_from_mont(&aX4[4 * 9 * 2], &aX4[4 * 9 * 2]);
+
+    ecp_sm2z256_avx2_convert_transpose_back(res_point_arr, aX4);
+    /* Last window is performed serially */
+    wvalue = *((u16 *) & p_str[(idx - 1) / 8]);
+    wvalue = (wvalue >> ((idx - 1) % 8)) & mask;
+    booth_recode_w7(&sign0, &digit0, wvalue);
+    ecp_sm2z256_gather_w7((P256_POINT_AFFINE *)r,
+                          preComputedTable[36], digit0);
+    ecp_sm2z256_neg(tmp, r->Y);
+    copy_conditional(r->Y, tmp, sign0);
+    memcpy(r->Z, ONE, sizeof(ONE));
+    /* Sum the four windows */
+    ecp_sm2z256_point_add(r, r, &res_point_arr[0]);
+    ecp_sm2z256_point_add(r, r, &res_point_arr[1]);
+    ecp_sm2z256_point_add(r, r, &res_point_arr[2]);
+    ecp_sm2z256_point_add(r, r, &res_point_arr[3]);
+}
+# endif
+#endif
+
+__owur static int ecp_sm2z256_set_from_affine(EC_POINT *out, const EC_GROUP *group,
+                                              const P256_POINT_AFFINE *in,
+                                              BN_CTX *ctx)
+{
+    BIGNUM *x, *y;
+    BN_ULONG d_x[P256_LIMBS], d_y[P256_LIMBS];
+    int ret = 0;
+
+    x = BN_new();
+    if (x == NULL)
+        return 0;
+    y = BN_new();
+    if (y == NULL) {
+        BN_free(x);
+        return 0;
+    }
+    memcpy(d_x, in->X, sizeof(d_x));
+    bn_set_static_words(x, d_x, P256_LIMBS);
+
+    memcpy(d_y, in->Y, sizeof(d_y));
+    bn_set_static_words(y, d_y, P256_LIMBS);
+
+    ret = EC_POINT_set_affine_coordinates_GFp(group, out, x, y, ctx);
+
+    BN_free(x);
+    BN_free(y);
+
+    return ret;
+}
+
+/* r = scalar*G + sum(scalars[i]*points[i]) */
+__owur static int ecp_sm2z256_points_mul(const EC_GROUP *group,
+                                         EC_POINT *r,
+                                         const BIGNUM *scalar,
+                                         size_t num,
+                                         const EC_POINT *points[],
+                                         const BIGNUM *scalars[], BN_CTX *ctx)
+{
+    int i = 0, ret = 0, no_precomp_for_generator = 0, p_is_infinity = 0;
+    size_t j;
+    unsigned char p_str[33] = { 0 };
+    const PRECOMP256_ROW *preComputedTable = NULL;
+    const SM2Z256_PRE_COMP *pre_comp = NULL;
+    const EC_POINT *generator = NULL;
+    BN_CTX *new_ctx = NULL;
+    const BIGNUM **new_scalars = NULL;
+    const EC_POINT **new_points = NULL;
+    unsigned int idx = 0;
+    const unsigned int window_size = 7;
+    const unsigned int mask = (1 << (window_size + 1)) - 1;
+    unsigned int wvalue;
+    ALIGN32 union {
+        P256_POINT p;
+        P256_POINT_AFFINE a;
+    } t, p;
+    BIGNUM *tmp_scalar;
+
+    if ((num + 1) == 0 || (num + 1) > OPENSSL_MALLOC_MAX_NELEMS(void *)) {
+        ECerr(EC_F_ECP_SM2Z256_POINTS_MUL, ERR_R_MALLOC_FAILURE);
+        return 0;
+    }
+
+    if (group->meth != r->meth) {
+        ECerr(EC_F_ECP_SM2Z256_POINTS_MUL, EC_R_INCOMPATIBLE_OBJECTS);
+        return 0;
+    }
+
+    if ((scalar == NULL) && (num == 0))
+        return EC_POINT_set_to_infinity(group, r);
+
+    for (j = 0; j < num; j++) {
+        if (group->meth != points[j]->meth) {
+            ECerr(EC_F_ECP_SM2Z256_POINTS_MUL, EC_R_INCOMPATIBLE_OBJECTS);
+            return 0;
+        }
+    }
+
+    if (ctx == NULL) {
+        ctx = new_ctx = BN_CTX_new();
+        if (ctx == NULL)
+            goto err;
+    }
+
+    BN_CTX_start(ctx);
+
+    if (scalar) {
+        generator = EC_GROUP_get0_generator(group);
+        if (generator == NULL) {
+            ECerr(EC_F_ECP_SM2Z256_POINTS_MUL, EC_R_UNDEFINED_GENERATOR);
+            goto err;
+        }
+
+        /* look if we can use precomputed multiples of generator */
+        pre_comp = group->pre_comp.sm2z256;
+
+        if (pre_comp) {
+            /*
+             * If there is a precomputed table for the generator, check that
+             * it was generated with the same generator.
+             */
+            EC_POINT *pre_comp_generator = EC_POINT_new(group);
+            if (pre_comp_generator == NULL)
+                goto err;
+
+            if (!ecp_sm2z256_set_from_affine(pre_comp_generator,
+                                             group, pre_comp->precomp[0],
+                                             ctx)) {
+                EC_POINT_free(pre_comp_generator);
+                goto err;
+            }
+
+            if (0 == EC_POINT_cmp(group, generator, pre_comp_generator, ctx))
+                preComputedTable = (const PRECOMP256_ROW *)pre_comp->precomp;
+
+            EC_POINT_free(pre_comp_generator);
+        }
+
+        if (preComputedTable == NULL && ecp_sm2z256_is_affine_G(generator)) {
+            /*
+             * If there is no precomputed data, but the generator is the
+             * default, a hardcoded table of precomputed data is used. This
+             * is because applications, such as Apache, do not use
+             * EC_KEY_precompute_mult.
+             */
+            preComputedTable = ecp_sm2z256_precomputed;
+        }
+
+        if (preComputedTable) {
+            if ((BN_num_bits(scalar) > 256)
+                || BN_is_negative(scalar)) {
+                if ((tmp_scalar = BN_CTX_get(ctx)) == NULL)
+                    goto err;
+
+                if (!BN_nnmod(tmp_scalar, scalar, group->order, ctx)) {
+                    ECerr(EC_F_ECP_SM2Z256_POINTS_MUL, ERR_R_BN_LIB);
+                    goto err;
+                }
+                scalar = tmp_scalar;
+            }
+
+            for (i = 0; i < bn_get_top(scalar) * BN_BYTES; i += BN_BYTES) {
+                BN_ULONG d = bn_get_words(scalar)[i / BN_BYTES];
+
+                p_str[i + 0] = (unsigned char)d;
+                p_str[i + 1] = (unsigned char)(d >> 8);
+                p_str[i + 2] = (unsigned char)(d >> 16);
+                p_str[i + 3] = (unsigned char)(d >>= 24);
+                if (BN_BYTES == 8) {
+                    d >>= 8;
+                    p_str[i + 4] = (unsigned char)d;
+                    p_str[i + 5] = (unsigned char)(d >> 8);
+                    p_str[i + 6] = (unsigned char)(d >> 16);
+                    p_str[i + 7] = (unsigned char)(d >> 24);
+                }
+            }
+
+            for (; i < 33; i++)
+                p_str[i] = 0;
+
+#if defined(ECP_SM2Z256_AVX2)
+printf("%s %d: ECP_SM2Z256_AVX2 defined\n", __FILE__, __LINE__);
+            if (ecp_sm2z_avx2_eligible()) {
+                ecp_sm2z256_avx2_mul_g(&p.p, p_str, preComputedTable);
+            } else
+#endif
+            {
+                BN_ULONG infty;
+
+                /* First window */
+                wvalue = (p_str[0] << 1) & mask;
+                idx += window_size;
+
+                wvalue = _booth_recode_w7(wvalue);
+
+                ecp_sm2z256_gather_w7(&p.a, preComputedTable[0],
+                                      wvalue >> 1);
+
+                ecp_sm2z256_neg(p.p.Z, p.p.Y);
+                copy_conditional(p.p.Y, p.p.Z, wvalue & 1);
+
+                /*
+                 * Since affine infinity is encoded as (0,0) and
+                 * Jacobian ias (,,0), we need to harmonize them
+                 * by assigning "one" or zero to Z.
+                 */
+                infty = (p.p.X[0] | p.p.X[1] | p.p.X[2] | p.p.X[3] |
+                         p.p.Y[0] | p.p.Y[1] | p.p.Y[2] | p.p.Y[3]);
+                if (P256_LIMBS == 8)
+                    infty |= (p.p.X[4] | p.p.X[5] | p.p.X[6] | p.p.X[7] |
+                              p.p.Y[4] | p.p.Y[5] | p.p.Y[6] | p.p.Y[7]);
+
+                infty = 0 - is_zero(infty);
+                infty = ~infty;
+
+                p.p.Z[0] = ONE[0] & infty;
+                p.p.Z[1] = ONE[1] & infty;
+                p.p.Z[2] = ONE[2] & infty;
+                p.p.Z[3] = ONE[3] & infty;
+                if (P256_LIMBS == 8) {
+                    p.p.Z[4] = ONE[4] & infty;
+                    p.p.Z[5] = ONE[5] & infty;
+                    p.p.Z[6] = ONE[6] & infty;
+                    p.p.Z[7] = ONE[7] & infty;
+                }
+
+                for (i = 1; i < 37; i++) {
+                    unsigned int off = (idx - 1) / 8;
+                    wvalue = p_str[off] | p_str[off + 1] << 8;
+                    wvalue = (wvalue >> ((idx - 1) % 8)) & mask;
+                    idx += window_size;
+
+                    wvalue = _booth_recode_w7(wvalue);
+
+                    ecp_sm2z256_gather_w7(&t.a,
+                                          preComputedTable[i], wvalue >> 1);
+
+                    ecp_sm2z256_neg(t.p.Z, t.a.Y);
+                    copy_conditional(t.a.Y, t.p.Z, wvalue & 1);
+
+                    ecp_sm2z256_point_add_affine(&p.p, &p.p, &t.a);
+                }
+            }
+        } else {
+            p_is_infinity = 1;
+            no_precomp_for_generator = 1;
+        }
+    } else
+        p_is_infinity = 1;
+
+    if (no_precomp_for_generator) {
+        /*
+         * Without a precomputed table for the generator, it has to be
+         * handled like a normal point.
+         */
+        new_scalars = OPENSSL_malloc((num + 1) * sizeof(BIGNUM *));
+        if (new_scalars == NULL) {
+            ECerr(EC_F_ECP_SM2Z256_POINTS_MUL, ERR_R_MALLOC_FAILURE);
+            goto err;
+        }
+
+        new_points = OPENSSL_malloc((num + 1) * sizeof(EC_POINT *));
+        if (new_points == NULL) {
+            ECerr(EC_F_ECP_SM2Z256_POINTS_MUL, ERR_R_MALLOC_FAILURE);
+            goto err;
+        }
+
+        memcpy(new_scalars, scalars, num * sizeof(BIGNUM *));
+        new_scalars[num] = scalar;
+        memcpy(new_points, points, num * sizeof(EC_POINT *));
+        new_points[num] = generator;
+
+        scalars = new_scalars;
+        points = new_points;
+        num++;
+    }
+
+    if (num) {
+        P256_POINT *out = &t.p;
+        if (p_is_infinity)
+            out = &p.p;
+
+        if (!ecp_sm2z256_windowed_mul(group, out, scalars, points, num, ctx))
+            goto err;
+
+        if (!p_is_infinity)
+            ecp_sm2z256_point_add(&p.p, &p.p, out);
+    }
+
+    /* Not constant-time, but we're only operating on the public output. */
+    if (!bn_set_words(r->X, p.p.X, P256_LIMBS) ||
+        !bn_set_words(r->Y, p.p.Y, P256_LIMBS) ||
+        !bn_set_words(r->Z, p.p.Z, P256_LIMBS)) {
+        goto err;
+    }
+    r->Z_is_one = is_one(r->Z) & 1;
+
+    ret = 1;
+
+err:
+    if (ctx)
+        BN_CTX_end(ctx);
+    BN_CTX_free(new_ctx);
+    OPENSSL_free(new_points);
+    OPENSSL_free(new_scalars);
+    return ret;
+}
+
+__owur static int ecp_sm2z256_get_affine(const EC_GROUP *group,
+                                         const EC_POINT *point,
+                                         BIGNUM *x, BIGNUM *y, BN_CTX *ctx)
+{
+    BN_ULONG z_inv2[P256_LIMBS];
+    BN_ULONG z_inv3[P256_LIMBS];
+    BN_ULONG x_aff[P256_LIMBS];
+    BN_ULONG y_aff[P256_LIMBS];
+    BN_ULONG point_x[P256_LIMBS], point_y[P256_LIMBS], point_z[P256_LIMBS];
+    BN_ULONG x_ret[P256_LIMBS], y_ret[P256_LIMBS];
+
+    if (EC_POINT_is_at_infinity(group, point)) {
+        ECerr(EC_F_ECP_SM2Z256_GET_AFFINE, EC_R_POINT_AT_INFINITY);
+        return 0;
+    }
+
+    if (!ecp_sm2z256_bignum_to_field_elem(point_x, point->X) ||
+        !ecp_sm2z256_bignum_to_field_elem(point_y, point->Y) ||
+        !ecp_sm2z256_bignum_to_field_elem(point_z, point->Z)) {
+        ECerr(EC_F_ECP_SM2Z256_GET_AFFINE, EC_R_COORDINATES_OUT_OF_RANGE);
+        return 0;
+    }
+
+    ecp_sm2z256_mod_inverse(z_inv3, point_z);
+    ecp_sm2z256_sqr_mont(z_inv2, z_inv3);
+    ecp_sm2z256_mul_mont(x_aff, z_inv2, point_x);
+
+    if (x != NULL) {
+        ecp_sm2z256_from_mont(x_ret, x_aff);
+        if (!bn_set_words(x, x_ret, P256_LIMBS))
+            return 0;
+    }
+
+    if (y != NULL) {
+        ecp_sm2z256_mul_mont(z_inv3, z_inv3, z_inv2);
+        ecp_sm2z256_mul_mont(y_aff, z_inv3, point_y);
+        ecp_sm2z256_from_mont(y_ret, y_aff);
+        if (!bn_set_words(y, y_ret, P256_LIMBS))
+            return 0;
+    }
+
+    return 1;
+}
+
+static SM2Z256_PRE_COMP *ecp_sm2z256_pre_comp_new(const EC_GROUP *group)
+{
+    SM2Z256_PRE_COMP *ret = NULL;
+
+    if (!group)
+        return NULL;
+
+    ret = OPENSSL_zalloc(sizeof(*ret));
+
+    if (ret == NULL) {
+        ECerr(EC_F_ECP_SM2Z256_PRE_COMP_NEW, ERR_R_MALLOC_FAILURE);
+        return ret;
+    }
+
+    ret->group = group;
+    ret->w = 6;                 /* default */
+    ret->references = 1;
+
+    ret->lock = CRYPTO_THREAD_lock_new();
+    if (ret->lock == NULL) {
+        ECerr(EC_F_ECP_SM2Z256_PRE_COMP_NEW, ERR_R_MALLOC_FAILURE);
+        OPENSSL_free(ret);
+        return NULL;
+    }
+    return ret;
+}
+
+SM2Z256_PRE_COMP *EC_sm2z256_pre_comp_dup(SM2Z256_PRE_COMP *p)
+{
+    int i;
+    if (p != NULL)
+        CRYPTO_atomic_add(&p->references, 1, &i, p->lock);
+    return p;
+}
+
+void EC_sm2z256_pre_comp_free(SM2Z256_PRE_COMP *pre)
+{
+    int i;
+
+    if (pre == NULL)
+        return;
+
+    CRYPTO_atomic_add(&pre->references, -1, &i, pre->lock);
+    REF_PRINT_COUNT("EC_sm2z256", x);
+    if (i > 0)
+        return;
+    REF_ASSERT_ISNT(i < 0);
+
+    OPENSSL_free(pre->precomp_storage);
+    CRYPTO_THREAD_lock_free(pre->lock);
+    OPENSSL_free(pre);
+}
+
+
+static int ecp_sm2z256_window_have_precompute_mult(const EC_GROUP *group)
+{
+    /* There is a hard-coded table for the default generator. */
+    const EC_POINT *generator = EC_GROUP_get0_generator(group);
+
+    if (generator != NULL && ecp_sm2z256_is_affine_G(generator)) {
+        /* There is a hard-coded table for the default generator. */
+        return 1;
+    }
+
+    return HAVEPRECOMP(group, sm2z256);
+}
+
+const EC_METHOD *EC_GFp_sm2z256_method(void)
+{
+    static const EC_METHOD ret = {
+        EC_FLAGS_DEFAULT_OCT,
+        NID_X9_62_prime_field,
+        ec_GFp_mont_group_init,
+        ec_GFp_mont_group_finish,
+        ec_GFp_mont_group_clear_finish,
+        ec_GFp_mont_group_copy,
+        ec_GFp_mont_group_set_curve,
+        ec_GFp_simple_group_get_curve,
+        ec_GFp_simple_group_get_degree,
+        ec_group_simple_order_bits,
+        ec_GFp_simple_group_check_discriminant,
+        ec_GFp_simple_point_init,
+        ec_GFp_simple_point_finish,
+        ec_GFp_simple_point_clear_finish,
+        ec_GFp_simple_point_copy,
+        ec_GFp_simple_point_set_to_infinity,
+        ec_GFp_simple_set_Jprojective_coordinates_GFp,
+        ec_GFp_simple_get_Jprojective_coordinates_GFp,
+        ec_GFp_simple_point_set_affine_coordinates,
+        ecp_sm2z256_get_affine,
+        0, 0, 0,
+        ec_GFp_simple_add,
+        ec_GFp_simple_dbl,
+        ec_GFp_simple_invert,
+        ec_GFp_simple_is_at_infinity,
+        ec_GFp_simple_is_on_curve,
+        ec_GFp_simple_cmp,
+        ec_GFp_simple_make_affine,
+        ec_GFp_simple_points_make_affine,
+        ecp_sm2z256_points_mul,                    /* mul */
+        ecp_sm2z256_mult_precompute,               /* precompute_mult */
+        ecp_sm2z256_window_have_precompute_mult,   /* have_precompute_mult */
+        ec_GFp_mont_field_mul,
+        ec_GFp_mont_field_sqr,
+        0,                                          /* field_div */
+        ec_GFp_mont_field_encode,
+        ec_GFp_mont_field_decode,
+        ec_GFp_mont_field_set_to_one,
+        ec_key_simple_priv2oct,
+        ec_key_simple_oct2priv,
+        0, /* set private */
+        ec_key_simple_generate_key,
+        ec_key_simple_check_key,
+        ec_key_simple_generate_public_key,
+        0, /* keycopy */
+        0, /* keyfinish */
+        ecdh_simple_compute_key
+    };
+
+    return &ret;
+}
diff --git a/crypto/ec/ecp_sm2z256_table.c b/crypto/ec/ecp_sm2z256_table.c
new file mode 100644
index 000000000..0134fc855
--- /dev/null
+++ b/crypto/ec/ecp_sm2z256_table.c
@@ -0,0 +1,9628 @@
+/* ====================================================================
+ * Copyright (c) 2014 - 2018 The GmSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the GmSSL Project.
+ *    (http://gmssl.org/)"
+ *
+ * 4. The name "GmSSL Project" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    guanzhi1980@gmail.com.
+ *
+ * 5. Products derived from this software may not be called "GmSSL"
+ *    nor may "GmSSL" appear in their names without prior written
+ *    permission of the GmSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the GmSSL Project
+ *    (http://gmssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE GmSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE GmSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+/*
+ * Copyright 2014-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * This is the precomputed constant time access table for the code in
+ * ecp_montp256.c, for the default generator. The table consists of 37
+ * subtables, each subtable contains 64 affine points. The affine points are
+ * encoded as eight uint64's, four for the x coordinate and four for the y.
+ * Both values are in little-endian order. There are 37 tables because a
+ * signed, 6-bit wNAF form of the scalar is used and ceil(256/(6 + 1)) = 37.
+ * Within each table there are 64 values because the 6-bit wNAF value can
+ * take 64 values, ignoring the sign bit, which is implemented by performing
+ * a negation of the affine point when required. We would like to align it
+ * to 2MB in order to increase the chances of using a large page but that
+ * appears to lead to invalid ELF files being produced.
+ */
+
+#if defined(__GNUC__)
+__attribute((aligned(4096)))
+#elif defined(_MSC_VER)
+__declspec(align(4096))
+#elif defined(__SUNPRO_C)
+# pragma align 4096(ecp_sm2z256_precomputed)
+#endif
+static const BN_ULONG ecp_sm2z256_precomputed[37][64 *
+                                                  sizeof(P256_POINT_AFFINE) /
+                                                  sizeof(BN_ULONG)] = {
+    {
+     TOBN(0x61328990, 0xf418029e), TOBN(0x3e7981ed, 0xdca6c050),
+     TOBN(0xd6a1ed99, 0xac24c3c3), TOBN(0x91167a5e, 0xe1c13b05),
+     TOBN(0xc1354e59, 0x3c2d0ddd), TOBN(0xc1f5e578, 0x8d3295fa),
+     TOBN(0x8d4cfb06, 0x6e2a48f8), TOBN(0x63cd65d4, 0x81d735bd),
+     TOBN(0x0af037bf, 0xbc3be46a), TOBN(0x83bdc9ba, 0x2d8fa938),
+     TOBN(0x5349d94b, 0x5788cd24), TOBN(0x0d7e9c18, 0xcaa5736a),
+     TOBN(0x6a7e1a1d, 0x69db9ac1), TOBN(0xccbd8d37, 0xc4a8e82b),
+     TOBN(0xc7b14516, 0x9b7157ac), TOBN(0x947e7465, 0x6c21bdf5),
+     TOBN(0x1cda54fd, 0xab589e4a), TOBN(0x26765289, 0xdb4f0a0d),
+     TOBN(0x0a265a30, 0x8ceb4a0a), TOBN(0x3019fd6b, 0xfe887c64),
+     TOBN(0x0a10fbe9, 0x4b2fc190), TOBN(0xf40aa52b, 0x87cbce60),
+     TOBN(0xcc496bfa, 0x6dc13c97), TOBN(0x28ad3478, 0x5bb3fbb4),
+     TOBN(0x393f7c5a, 0x98615060), TOBN(0x487ea27f, 0xe9016209),
+     TOBN(0x8a86bcb4, 0xa09f9020), TOBN(0x50dc8e3a, 0xc899dbe1),
+     TOBN(0xfc099043, 0xfd619998), TOBN(0x1de135ea, 0x7c7383bd),
+     TOBN(0x4d0bd556, 0x32cf70ed), TOBN(0x6ffc31c5, 0x25bce9e3),
+     TOBN(0x9a575633, 0x6a9c8162), TOBN(0x15aa58f2, 0x21dfcc53),
+     TOBN(0x7ad354bf, 0x1ef5f4c5), TOBN(0x0f443ef3, 0x63f875b9),
+     TOBN(0x2e81d68f, 0xd3450133), TOBN(0xb30f4bbd, 0xe3607d18),
+     TOBN(0xb1826a4c, 0x362258ef), TOBN(0x7b415276, 0x142a6768),
+     TOBN(0x136a9c4c, 0x0acd72ba), TOBN(0xb1274a25, 0x5e7ec73c),
+     TOBN(0xf15a876e, 0x5de34db6), TOBN(0x85e74ca0, 0x8cba8047),
+     TOBN(0x08454cdd, 0xb469eb37), TOBN(0x8fbf6d1f, 0xc99754f8),
+     TOBN(0x1060e7f8, 0xec30e984), TOBN(0xb568bc97, 0x4b8c598a),
+     TOBN(0xaa3531c7, 0x81f06784), TOBN(0x0b894193, 0x07132520),
+     TOBN(0x84ee5b69, 0xacfe18c5), TOBN(0xbbf492e0, 0xd9fbec28),
+     TOBN(0x313a35c1, 0xe5f6186d), TOBN(0x0e449a2e, 0x757a01b8),
+     TOBN(0x96c9b992, 0x2bd99baf), TOBN(0x2ba05a8f, 0x3b84d777),
+     TOBN(0xde523a1c, 0x09122670), TOBN(0x90be6f2a, 0x22cc810c),
+     TOBN(0x086e6341, 0x4387df9e), TOBN(0x115c2fc0, 0xd9c44134),
+     TOBN(0x9334430d, 0x8799302a), TOBN(0x693b3500, 0xe27b7ea4),
+     TOBN(0xcbe1136f, 0x9a8f3382), TOBN(0xe77fd5f2, 0xb5778247),
+     TOBN(0x98e795c3, 0x30fbde86), TOBN(0x8e5e0495, 0xab21af8f),
+     TOBN(0x3925bf83, 0xb48669b4), TOBN(0x77d88740, 0x469522c8),
+     TOBN(0x8fbf8b5b, 0x987b04ce), TOBN(0x63c563a8, 0x3aff4428),
+     TOBN(0x5dc11165, 0x53a6e969), TOBN(0x822a6c24, 0x32697f4c),
+     TOBN(0x6774298a, 0x642cb143), TOBN(0xecdb60d8, 0x2d110e71),
+     TOBN(0xe810b11b, 0x1388728e), TOBN(0x2e8237d8, 0xd8603a8a),
+     TOBN(0x673968fc, 0x50aeeae1), TOBN(0x08c65d19, 0x6746a3f4),
+     TOBN(0x7a61a6b5, 0xd7dd7165), TOBN(0xe31bbfd9, 0xa9b6df3a),
+     TOBN(0x2b252ad0, 0x3421e115), TOBN(0x7557c8c7, 0xc6affc01),
+     TOBN(0xd90c19fd, 0x8a509267), TOBN(0x483da168, 0xe0d871c8),
+     TOBN(0x72d6f9b3, 0xc10729bf), TOBN(0x5dd84021, 0x15b7061e),
+     TOBN(0x9bfea2db, 0x9f2c587d), TOBN(0x528398a7, 0x98641ec2),
+     TOBN(0x18a65d8d, 0xf3afdd62), TOBN(0x89f38500, 0xd6d7e4e4),
+     TOBN(0x65708c6a, 0x9d8d4f07), TOBN(0xb90ea13c, 0xd0bdc7f4),
+     TOBN(0x58985855, 0x8c3e2b32), TOBN(0xfa48d5c5, 0xbcfad3a1),
+     TOBN(0x5c3544e7, 0x62385ffa), TOBN(0xb6bd39ed, 0x7e72aeb7),
+     TOBN(0x34e51c6a, 0x8000fe4e), TOBN(0x7da2bdfd, 0x89c46941),
+     TOBN(0x667ba91d, 0xe1bc2b2e), TOBN(0x3c80c9d0, 0x10a73e5c),
+     TOBN(0x4fadebbe, 0xc7f5c64d), TOBN(0xaef09eb4, 0x3ea35052),
+     TOBN(0x167ee11b, 0x26ec55f9), TOBN(0x45fa508a, 0x85189260),
+     TOBN(0xa0e9a439, 0x22542fc3), TOBN(0x3f194a6c, 0xddac78dc),
+     TOBN(0xa75ae72d, 0x6f74d053), TOBN(0x0f8babeb, 0x097c6617),
+     TOBN(0x1d12bc5c, 0x4303c247), TOBN(0xfe0c027a, 0xbd1e246c),
+     TOBN(0xe9ca1a99, 0xb69b55ad), TOBN(0xff6cd2b0, 0x117cd63a),
+     TOBN(0xf3489343, 0xdde97d4d), TOBN(0x9c14e38a, 0xbbb2ce1f),
+     TOBN(0x25866911, 0xcfddf221), TOBN(0x0df89411, 0x460efef1),
+     TOBN(0xf713f30e, 0x73ae8326), TOBN(0xd9be66a8, 0xcdd274a1),
+     TOBN(0xdf915ae2, 0x36885947), TOBN(0x2c5c1e9e, 0x7878b781),
+     TOBN(0xf71560c9, 0x39e8a120), TOBN(0x7121d6b8, 0x7273b59a),
+     TOBN(0x649535ce, 0x8ef4639d), TOBN(0xcd01076e, 0x14cc6d58),
+     TOBN(0x2705729a, 0x96e74f8a), TOBN(0xb07e3230, 0x5533037e),
+     TOBN(0x0846dcc1, 0x663c5c62), TOBN(0x6a4759c1, 0x10fc3ac1),
+     TOBN(0x3c126193, 0xcfbdfeff), TOBN(0x4a31dd20, 0x4996d845),
+     TOBN(0x48a76ba0, 0x19f2b658), TOBN(0xbe330142, 0x8890a8bc),
+     TOBN(0x287b34e1, 0x308aa041), TOBN(0xcbf5da24, 0x813adf29),
+     TOBN(0xcdfc5a58, 0xcdcdc439), TOBN(0xbda3bda2, 0x198a6075),
+     TOBN(0x639f92bc, 0x1497fe38), TOBN(0x8ed8eeac, 0xd58bd278),
+     TOBN(0xcf5d7ce6, 0xb417bfe4), TOBN(0xf617c54e, 0x44400c59),
+     TOBN(0xde635635, 0x7d8dc939), TOBN(0x2e6a3a75, 0x241baaff),
+     TOBN(0x02f324e5, 0xe07e8e97), TOBN(0xeb715487, 0x70f9fc9d),
+     TOBN(0xbefd3380, 0x86712116), TOBN(0x9b9e9707, 0x884efe46),
+     TOBN(0x611a1eec, 0x8c9e513f), TOBN(0xe2d8e3f5, 0x3b6dbcec),
+     TOBN(0x7cedab1c, 0x4f8964e4), TOBN(0xee12d062, 0xf4e139f8),
+     TOBN(0x8e63c9c0, 0x9a9af4f3), TOBN(0xe3246dbb, 0x8b907b23),
+     TOBN(0x70d5bda2, 0x71099001), TOBN(0x3d876d4a, 0x15fae7dd),
+     TOBN(0xaba0500f, 0x7b69c20e), TOBN(0xa8e3e094, 0x9834adf2),
+     TOBN(0x69db851b, 0x980b21b9), TOBN(0x274c1de2, 0x788c2a30),
+     TOBN(0x5caa5336, 0xd47d153d), TOBN(0xada69877, 0x57cef318),
+     TOBN(0x83879486, 0xa0551c80), TOBN(0x1611dea0, 0x658e61be),
+     TOBN(0x1fe95c82, 0x1b935068), TOBN(0x8f01e019, 0x5b229223),
+     TOBN(0x23017e05, 0x7e93c389), TOBN(0xce4ac99d, 0x9840dd64),
+     TOBN(0xddc9b900, 0x1de86399), TOBN(0x6abe5cc3, 0x88015785),
+     TOBN(0xc09545a9, 0xb3c50898), TOBN(0xbd443361, 0x6c05d902),
+     TOBN(0xed71f70c, 0x2c6bcc8c), TOBN(0x8dbc0b88, 0xbdf8e908),
+     TOBN(0x56eb5b98, 0x4fcbcd9a), TOBN(0xafb6fedc, 0x08114397),
+     TOBN(0x0500ce5b, 0xb35f7927), TOBN(0x7005bcf9, 0x95efe710),
+     TOBN(0x125cbed2, 0x2eba7f39), TOBN(0xc7c42e76, 0x6c488d44),
+     TOBN(0xdb8991f9, 0x676915c4), TOBN(0xdf6ae594, 0x9183839f),
+     TOBN(0x4f69c304, 0xc79f8bd1), TOBN(0x638cb070, 0xaa1662fa),
+     TOBN(0xc7f68c72, 0xba6f2599), TOBN(0x11bb84d9, 0x1f6edfa9),
+     TOBN(0x9ed156ec, 0xa215fda2), TOBN(0x19de7a91, 0x20c5ddb6),
+     TOBN(0xc1ed949d, 0x0668c65d), TOBN(0x96683044, 0xd0826f6a),
+     TOBN(0x1e6325e0, 0x1adaa8ff), TOBN(0xbc53bc24, 0x07ac392a),
+     TOBN(0x2c342db5, 0xd9f06e44), TOBN(0x3f529385, 0x30db8c1a),
+     TOBN(0xc5957d29, 0xe7492326), TOBN(0x3addc3df, 0x0663f829),
+     TOBN(0x8faa3169, 0x728cfdc1), TOBN(0xde53aa7c, 0x6b975134),
+     TOBN(0xf481759b, 0xefddc764), TOBN(0xd605474b, 0x09edaff3),
+     TOBN(0xc7df1eb9, 0x653d48c9), TOBN(0xa71e6854, 0xc5040212),
+     TOBN(0x136d8342, 0xafe945b5), TOBN(0x91707e7d, 0xe9d239c7),
+     TOBN(0xeda23dc5, 0xfb2e80de), TOBN(0x892bed73, 0xff614966),
+     TOBN(0x2ded2367, 0x838dc12d), TOBN(0x73fd298c, 0xb002bd9c),
+     TOBN(0xc548b426, 0x2c4629df), TOBN(0x93605d17, 0x8f7e03b7),
+     TOBN(0x32861816, 0xd37c24cc), TOBN(0x5bb54ee2, 0xe427975a),
+     TOBN(0x6da013d2, 0x32f943a9), TOBN(0x0746a77a, 0x9bc202e5),
+     TOBN(0x6db07a84, 0xcd1def5b), TOBN(0x9421fe7f, 0x861d9f9b),
+     TOBN(0x71767292, 0x692181fb), TOBN(0x0560e7e5, 0xc9d2441d),
+     TOBN(0xf1496afd, 0x4d7e922a), TOBN(0x67f42a3f, 0xe11fa533),
+     TOBN(0x9f903e5b, 0x977956cd), TOBN(0x37671e24, 0x1eb49608),
+     TOBN(0x967950a0, 0x21fb2047), TOBN(0x141f96fb, 0x35da3c6b),
+     TOBN(0xe07c3c40, 0xd27bba59), TOBN(0xbde5ed1d, 0x0e1af754),
+     TOBN(0xdc64c4b0, 0x54f1f257), TOBN(0xecb033c8, 0xb01196dc),
+     TOBN(0x54e65f4d, 0x8202d5bd), TOBN(0x63afcc93, 0x2b2fd451),
+     TOBN(0x1e929a39, 0x30640fb7), TOBN(0xdc91387e, 0x5b361718),
+     TOBN(0x10aadecb, 0xf8f0bbe8), TOBN(0x81d8f466, 0x0977e2bb),
+     TOBN(0xdcaa3790, 0xbd64cd96), TOBN(0xbc8ac152, 0xcee698d3),
+     TOBN(0xde7192f7, 0xa1143c45), TOBN(0xf7c9d826, 0xf5fb9ea0),
+     TOBN(0x54aea92e, 0xc9468f50), TOBN(0x340f4459, 0xcc427ed4),
+     TOBN(0x3fec5be9, 0x02ad5467), TOBN(0xec780d9c, 0x2cc6c8b5),
+     TOBN(0x7b179a8b, 0xb889c78a), TOBN(0x069a7ab9, 0x0aca32c5),
+     TOBN(0xe4e5215e, 0x591b9a36), TOBN(0x7802fb3e, 0x3bd54630),
+     TOBN(0x9a479313, 0x233c6eeb), TOBN(0x18c612ad, 0x4e1cbabc),
+     TOBN(0x28a29273, 0xc0e36f3b), TOBN(0xf4e2dfb1, 0x7d3deb26),
+     TOBN(0xa6c11369, 0xadbb3c8b), TOBN(0xd78af40b, 0x4c8ec378),
+     TOBN(0xffb3a80d, 0x03f0a982), TOBN(0x550e3e71, 0xa83be50a),
+     TOBN(0x845c0fb2, 0x418ee45b), TOBN(0x5297cf43, 0x0791b964),
+     TOBN(0x676b638c, 0xcc47e33b), TOBN(0xb1c52fac, 0xfecf85b2),
+     TOBN(0xf011b5e5, 0x3dba2c0e), TOBN(0xa6c68448, 0x026d4f11),
+     TOBN(0x11596db3, 0xc3f206fb), TOBN(0xc91c76dc, 0x29414a3c),
+     TOBN(0x1839b9d1, 0xb94ddc7c), TOBN(0xdfb20ce7, 0x56ae8610),
+     TOBN(0x3e2b1cd9, 0xd8734400), TOBN(0x59f9329a, 0xf01ea540),
+     TOBN(0x7d4c140c, 0x2351a2a9), TOBN(0x575c1e1b, 0xbf4c9823),
+     TOBN(0x8f11c2ca, 0x31068df9), TOBN(0xe3c17aa0, 0x05e6def0),
+     TOBN(0xe6281c70, 0x501c8630), TOBN(0xad240917, 0xc88a412e),
+     TOBN(0x6f21bfb7, 0x390492d7), TOBN(0x61ea1385, 0xc3a3ccb7),
+     TOBN(0x60494a83, 0x33733cbc), TOBN(0x8da622a0, 0x27ed8157),
+     TOBN(0x0022b154, 0x0471ad90), TOBN(0x3bd0a4c5, 0xd3568003),
+     TOBN(0xdc8e2d03, 0xd932df23), TOBN(0x859ed940, 0x7a1f5159),
+     TOBN(0xad670e63, 0x2a375b0f), TOBN(0x15922fae, 0x9520db97),
+     TOBN(0xfb73d16f, 0x59eb1a9b), TOBN(0x3ee8cc1f, 0x8511e541),
+     TOBN(0x20d72d59, 0x1590c321), TOBN(0x62eab566, 0x3bd075d4),
+     TOBN(0xac07a7c7, 0xfae123ab), TOBN(0x83b89abf, 0x1f10af6e),
+     TOBN(0x469962ec, 0x1da8ac5d), TOBN(0x09761c35, 0x8c58c3b3),
+     TOBN(0x2c086d5e, 0x7da90fc9), TOBN(0x458e5ffd, 0x5cc27782),
+     TOBN(0xc3f48611, 0xb9268939), TOBN(0x39fed873, 0xde4b9110),
+     TOBN(0x16ef8f78, 0xfda698cc), TOBN(0xb028dc21, 0xa973bb50),
+     TOBN(0x45eb849e, 0xe29b725b), TOBN(0xd41b5b6d, 0x14c6eae9),
+     TOBN(0x5e931b21, 0xc55d5720), TOBN(0xb628ccb2, 0xa0e40b19),
+     TOBN(0x42044ffe, 0x000651a5), TOBN(0x2130b4de, 0x076544e7),
+     TOBN(0x38428594, 0x3677c70f), TOBN(0xfdcdb038, 0xf8945d86),
+     TOBN(0xfb2e3d4c, 0x4169ae44), TOBN(0xd4695e9b, 0x0d13bce2),
+     TOBN(0x45191390, 0x039d646d), TOBN(0x983b7a2e, 0xb12ba339),
+     TOBN(0xdfd30d3e, 0x5923e7d6), TOBN(0xae3590f0, 0xba9d206a),
+     TOBN(0x7d58d334, 0xb6d5e62a), TOBN(0xb15b0544, 0x7e402b12),
+     TOBN(0xac57e113, 0x62ae8e01), TOBN(0x4d83804c, 0xf473edee),
+     TOBN(0x2faa7c4d, 0xc81bc828), TOBN(0xb16ed9d7, 0xfb62561f),
+     TOBN(0x4c9da270, 0x49c2fa93), TOBN(0x3b014c73, 0xb311d90d),
+     TOBN(0xd29c5d65, 0xf5443332), TOBN(0xb6457d54, 0xeebdb7c2),
+     TOBN(0xc6a0bf3a, 0x4cce9480), TOBN(0xd434a3b0, 0x85355854),
+     TOBN(0x178ca01b, 0x8b2c703c), TOBN(0x605bba53, 0x0ab71a51),
+     TOBN(0x2140948e, 0x3db948d5), TOBN(0xc45b2689, 0x5fb6b8c1),
+     TOBN(0x421f66de, 0xf17b47bd), TOBN(0x57627a5a, 0x2e9b3ee5),
+     TOBN(0xedf3920a, 0x66614339), TOBN(0x7ea61903, 0x4b638a46),
+     TOBN(0x7203d542, 0x3c030643), TOBN(0x7112bb3d, 0x5e631461),
+     TOBN(0x2604eac7, 0x2bc3da9c), TOBN(0x2e4964e7, 0x32d2541b),
+     TOBN(0x940faf46, 0xe8b6482a), TOBN(0x8f772fcb, 0x24d27c9e),
+     TOBN(0x125c34d7, 0xca7c5f88), TOBN(0x9903eadb, 0xd1f47795),
+     TOBN(0x11aaa417, 0xe2147129), TOBN(0x3ccef5c2, 0xf88a0a30),
+     TOBN(0x78d5207a, 0x90283f97), TOBN(0xba1261e9, 0xd25226b6),
+     TOBN(0xbfc79248, 0xd1e7a01c), TOBN(0x373f1cd5, 0x941ab2bd),
+     TOBN(0xf0881e21, 0x19a0668b), TOBN(0x7b793789, 0x1f77bf0a),
+     TOBN(0x49c2769b, 0x63d4523d), TOBN(0xf8df2cba, 0xf0399eaf),
+     TOBN(0x5ae94c69, 0x22a2a74d), TOBN(0xd08f8d45, 0xefd1e193),
+     TOBN(0x64341fc4, 0xc681f376), TOBN(0x3a8e25c8, 0xec918711),
+     TOBN(0xdf35304d, 0x0608f50f), TOBN(0x9b4c6967, 0x9a973742),
+     TOBN(0xb5c1f5d3, 0xbfba043b), TOBN(0xaff4f896, 0xe975f03b),
+     TOBN(0xea1f39bd, 0xae2cbb01), TOBN(0x4cc1c4cb, 0xa62915ff),
+     TOBN(0x5eb4afa3, 0x89e943b8), TOBN(0x8c4d27e5, 0x154e565a),
+     TOBN(0x4e2e5a7e, 0x7f2bced6), TOBN(0x7af408e2, 0x4487f6a3),
+     TOBN(0xe5dacbae, 0x97a60de7), TOBN(0x9774834c, 0x4401b0ad),
+     TOBN(0x7683bb00, 0x8a9113f9), TOBN(0xc6fe7e8b, 0x42b2ba67),
+     TOBN(0xc0c0564d, 0x54e760c8), TOBN(0xf7b05401, 0x118606c2),
+     TOBN(0x554a9b0f, 0xec3cd7b9), TOBN(0xce75ecfb, 0x27916a21),
+     TOBN(0xf6638997, 0x12118abd), TOBN(0x2ba6e754, 0x097da3a7),
+     TOBN(0x1df82085, 0x0fdf9985), TOBN(0xbf73502a, 0x546c864a),
+     TOBN(0xdfde9323, 0xc02d9ce0), TOBN(0x580491e2, 0xe4dd0e7d),
+     TOBN(0xe71522d2, 0xae43b9b4), TOBN(0x876e3627, 0x6a231a41),
+     TOBN(0xfa8ff511, 0xb36362ec), TOBN(0x11c5a9f6, 0x34085959),
+     TOBN(0x272b86f2, 0x9770c62b), TOBN(0xf0626225, 0x7c7e8827),
+     TOBN(0x929168bf, 0xea1e13eb), TOBN(0xdb892971, 0xce59b0f5),
+     TOBN(0x6769e31d, 0x4f826f34), TOBN(0xfa1dd934, 0x0a955cec),
+     TOBN(0x123d9ca2, 0xa294d7ea), TOBN(0x8699063b, 0x4492569b),
+     TOBN(0x6a50eae9, 0xa8dd86c3), TOBN(0x3d757d10, 0x12c06c38),
+     TOBN(0x5a92c2c0, 0x3e41e556), TOBN(0xa64595eb, 0x6330c21a),
+     TOBN(0x70d8141a, 0xe184d925), TOBN(0x8543f2ce, 0xa2f10304),
+     TOBN(0x4559b0a2, 0x9eaca504), TOBN(0xb9843a4b, 0x2617bc9b),
+     TOBN(0x5b28d4ee, 0x1b641003), TOBN(0x3e9af8e1, 0x4ced538a),
+     TOBN(0x3790fe89, 0x7bdf7dc2), TOBN(0xc7c74941, 0xc32549ee),
+     TOBN(0xdcc8295b, 0xabcd2f42), TOBN(0x48b29a4f, 0xead078b6),
+     TOBN(0x8e8b28e3, 0x2040178e), TOBN(0xceff8f3e, 0x971725fc),
+     TOBN(0x4a97b6fa, 0xfcee2cc1), TOBN(0x775df6a9, 0xbac85b56),
+     TOBN(0x32e5cbe6, 0xd28a21cc), TOBN(0xe8b86ada, 0xae2b82db),
+     TOBN(0x44dfbb50, 0x86e38e96), TOBN(0x45d3fe7d, 0x1afc2d4b),
+     TOBN(0x838b356e, 0xd23f620d), TOBN(0x2e8fa8ac, 0x4592fe4b),
+     TOBN(0x1396e1b3, 0x3af5b1d8), TOBN(0x9c0c2ef3, 0xcbf50fb0),
+     TOBN(0xd9efb6c9, 0x836e93e9), TOBN(0xe6eb5870, 0x0899163f),
+     TOBN(0x3a2f6d77, 0xdca00d1b), TOBN(0x36f55f89, 0xb40ba0d6),
+     TOBN(0xf3b1701f, 0x32866e57), TOBN(0xf0768473, 0x59de0f2e),
+     TOBN(0xe55d7aed, 0xab57962d), TOBN(0x45004985, 0x2b60cabb),
+     TOBN(0x8d539d6e, 0xd5498888), TOBN(0x176ce1a0, 0xa5e0ff6a),
+     TOBN(0xcb7c15ef, 0xdc088c50), TOBN(0x90393d7a, 0xc9a9ae2f),
+     TOBN(0xd9c1a140, 0xd396bdce), TOBN(0x4215b78b, 0x6fb2800f),
+     TOBN(0x8939109f, 0x2f76b0df), TOBN(0x0f250897, 0x2adb40a8),
+     TOBN(0x4db0007c, 0x3a86e009), TOBN(0x6ef0ad95, 0xf968a635),
+     TOBN(0x58a82d4b, 0x8eaefa78), TOBN(0xe8a181cb, 0x493604a4),
+     TOBN(0x36c84e34, 0x520d216d), TOBN(0x2b2ef6b5, 0xc666171c),
+     TOBN(0x9469b91f, 0x2ce29d37), TOBN(0x3ecd84e7, 0xc15f20aa),
+     TOBN(0xf1090635, 0x292edd2c), TOBN(0x6d439362, 0x7c3447f6),
+     TOBN(0x51b9a0a9, 0x3eea3fdf), TOBN(0x68e0d1f8, 0x9e57e450),
+     TOBN(0x25d249d5, 0x7380931e), TOBN(0x87f03fad, 0x2011a45b),
+     TOBN(0x89df0324, 0xefde1ca3), TOBN(0x52ae43cd, 0x9a9b4330),
+     TOBN(0xfe48bc64, 0xa1867c1b), TOBN(0xdd874f66, 0x9866920e),
+     TOBN(0x6942a7e4, 0xfcf50251), TOBN(0xf5c10048, 0x9c5f6298),
+     TOBN(0x305183eb, 0x00973d66), TOBN(0x1ce66760, 0x95baf07c),
+     TOBN(0x74c9d971, 0x74822e13), TOBN(0x2ccd7fbb, 0x76b5e6ef),
+     TOBN(0x51688b49, 0xa3e1ca18), TOBN(0x1beb5bbb, 0xa603f2f1),
+     TOBN(0x09a231d1, 0x962534b6), TOBN(0x70417ce1, 0xafa92f75),
+     TOBN(0xb86b6d82, 0xe154bc00), TOBN(0x5a0b19e8, 0x895483e5),
+     TOBN(0xb15f6c05, 0xa0ff1e44), TOBN(0x2938b88a, 0xfdd8615d),
+     TOBN(0x81800a05, 0x971615c3), TOBN(0x6be6d56b, 0xc03d2039),
+     TOBN(0xff3e57d2, 0xc476ce64), TOBN(0x5b509b7b, 0x6f583ee8),
+     TOBN(0x1d92c36c, 0x7c1f5d3b), TOBN(0x1e60b19b, 0xe11df757),
+     TOBN(0x20261501, 0xe37e36f6), TOBN(0xb68a9aaa, 0x29bc86e3),
+     TOBN(0xfba81eaa, 0xf61d23ca), TOBN(0x63440834, 0xd5adaa18),
+     TOBN(0xa80d76ed, 0xa5f93bb8), TOBN(0x3264283d, 0x5a728480),
+     TOBN(0xd6171111, 0xe4b8c48e), TOBN(0x3ee227a1, 0xde557cca),
+     TOBN(0x2bebc09a, 0x3cb59841), TOBN(0x2f8047fe, 0x99bf6205),
+     TOBN(0xb78b243e, 0x4c43845f), TOBN(0x484ac183, 0x46d3b5e0),
+     TOBN(0xa07be476, 0x0314524d), TOBN(0xc0a3aa35, 0x1ab4c447),
+     TOBN(0x2f302d58, 0x9c341f84), TOBN(0x264911a7, 0x84f130ba),
+     TOBN(0x30bed408, 0x3ee64343), TOBN(0xd7d6e92d, 0x5dc5868a),
+     TOBN(0x92074568, 0x80adb3fb), TOBN(0x005ab33c, 0xa133123e),
+     TOBN(0x105119fd, 0x42e1da50), TOBN(0x6987117d, 0xb7f6b1e8),
+     TOBN(0xa2315af3, 0xc2bccb7a), TOBN(0x95ddd3ee, 0x8672c98a),
+     TOBN(0xa9032645, 0x5f48f607), TOBN(0x76861e62, 0xc5273603),
+     TOBN(0x71aaa35f, 0x88817217), TOBN(0x57e95b6c, 0x2892afac),
+     TOBN(0xf65e909b, 0x9e84c791), TOBN(0x257bcc2d, 0xaa52f3b1),
+     TOBN(0xd5f6110a, 0x865c665a), TOBN(0xddc3afe1, 0x30c08b4c),
+     TOBN(0x4df3d04a, 0xefec26fc), TOBN(0xf229bddf, 0xb035af5d),
+     TOBN(0x364913cf, 0xd191b439), TOBN(0xf41b8f6d, 0x5a7fa8a4),
+     TOBN(0x677cc51b, 0x6f6c1219), TOBN(0x593afe4a, 0x148b7f64),
+     TOBN(0x80ffa5ae, 0x0d038ad4), TOBN(0xf44d3df3, 0x36256c8f),
+     TOBN(0x0a3077c8, 0xbc978dce), TOBN(0xf3d9b4b0, 0x745b8317),
+     TOBN(0x8bbf4484, 0xb6b1852c), TOBN(0x0cd02ed4, 0x0e78ff07),
+     TOBN(0x91cb827e, 0x49c24238), TOBN(0x58adaee5, 0xdaa3cb55),
+    },
+    {
+     TOBN(0x07e6ce4d, 0x033fc12a), TOBN(0xba4f98a1, 0x4886f316),
+     TOBN(0xb24b38f3, 0xe66f3f11), TOBN(0xe3f6205a, 0x5ea4bde3),
+     TOBN(0x00705387, 0xa77b998f), TOBN(0x2c9b4457, 0x9549f3b1),
+     TOBN(0xdef6625b, 0x533a61d6), TOBN(0x4eda7e2a, 0x7e4f781a),
+     TOBN(0xe9730aaa, 0xfd120134), TOBN(0xb22b9089, 0xc057309c),
+     TOBN(0x98e79565, 0x84726ce7), TOBN(0x0e1431a0, 0xd635a584),
+     TOBN(0xbd387023, 0xe834ffa6), TOBN(0x64198ddf, 0x036ab1ae),
+     TOBN(0x46e5ebb1, 0x9124b684), TOBN(0xa316fa44, 0x233b3c6d),
+     TOBN(0xec2a9325, 0x84782513), TOBN(0xd67c8ab7, 0x2903d20b),
+     TOBN(0x6b65b262, 0x157f9aee), TOBN(0x547be60c, 0x69f964a2),
+     TOBN(0x001bf327, 0xee0419db), TOBN(0x92fa0800, 0xf20c7005),
+     TOBN(0x1e11e745, 0xcdc1ccda), TOBN(0xa785ec10, 0xe471f822),
+     TOBN(0xbc970210, 0xa1371aa4), TOBN(0xaff481a0, 0x54b5424e),
+     TOBN(0xbcdf91fd, 0x0e64269b), TOBN(0x18bb37bb, 0xb02fc7cf),
+     TOBN(0xd99edd79, 0x6f69d439), TOBN(0x4e27a58f, 0x169514b2),
+     TOBN(0x80eca1ca, 0x66e19ae4), TOBN(0x0470e965, 0x0788696d),
+     TOBN(0xa6b1992f, 0x8c9d34f6), TOBN(0xaf062ffe, 0xa5ed969e),
+     TOBN(0xbca2580d, 0x3a6d7ae2), TOBN(0xf30cd9e6, 0xc8999158),
+     TOBN(0x93e57897, 0x49d1ab0d), TOBN(0xcfa3aa4d, 0x30214280),
+     TOBN(0x0ca8b4fd, 0x0a814831), TOBN(0xdad179db, 0x0b10097c),
+     TOBN(0x63778bfc, 0x3dfdc228), TOBN(0xc0bae0ad, 0xb9648a36),
+     TOBN(0xda8cb8ab, 0x015a99b5), TOBN(0xb045cccb, 0x8366b58a),
+     TOBN(0x74ef8ef4, 0x4164cebd), TOBN(0x41e71fc8, 0xc5e00e5f),
+     TOBN(0x753cf906, 0x4479468e), TOBN(0x78b5223f, 0x332ea72d),
+     TOBN(0x8fc3e370, 0xddebafa2), TOBN(0x15ffcce0, 0x351f9f04),
+     TOBN(0x3fbd5f5c, 0x45b0efdc), TOBN(0xb8216623, 0x0fe3b460),
+     TOBN(0xe8322fbd, 0x533c7db6), TOBN(0xf3866d15, 0x00a243ff),
+     TOBN(0xf1194ae2, 0xa0e5aaea), TOBN(0x3e93eb01, 0xb9287b3d),
+     TOBN(0x528a9e2f, 0x5876d6e8), TOBN(0x93c48f85, 0xd2b622d7),
+     TOBN(0x88d9eac8, 0x3e5411d7), TOBN(0xb7e4a6ba, 0x00a70e91),
+     TOBN(0xaf18e5ba, 0xf1c43b2e), TOBN(0x46578c7e, 0xa2f347de),
+     TOBN(0x19ca736d, 0xf009638f), TOBN(0xa6563f1e, 0xbd1acd29),
+     TOBN(0xdf9bcd3a, 0x2f4126e7), TOBN(0xecc22d13, 0xd62efebd),
+     TOBN(0xd9b29b4b, 0x10943242), TOBN(0x499ffa74, 0x670136f9),
+     TOBN(0xa2a9ad2c, 0x2b889952), TOBN(0x945f306e, 0xfd132dad),
+     TOBN(0xfd05b884, 0x15cebfd7), TOBN(0x653e70af, 0xc7a5627f),
+     TOBN(0xfefc54b5, 0x577dae35), TOBN(0x9d2f0546, 0xaac3a655),
+     TOBN(0xb96bd298, 0xfac31d00), TOBN(0x3328a51c, 0xee69563d),
+     TOBN(0x5e19098e, 0x43195f4e), TOBN(0x657f4ba5, 0xa998010b),
+     TOBN(0x45f29a84, 0x4047ccb6), TOBN(0x833a5443, 0x6e423da6),
+     TOBN(0x97e480c6, 0xca33f42b), TOBN(0x20a51033, 0x06e52a05),
+     TOBN(0x85e87255, 0x0a9be572), TOBN(0xe8bc857a, 0xb988b582),
+     TOBN(0x782495e8, 0xc183c500), TOBN(0xf33a87fd, 0xfee0ae2f),
+     TOBN(0xf069fe20, 0xc64d1dec), TOBN(0x0b6dd98a, 0xf4b08816),
+     TOBN(0x6e6cf298, 0x99229a90), TOBN(0xa6840bc8, 0x1d71d532),
+     TOBN(0x803e5407, 0x71e3a8b7), TOBN(0xd5611ee4, 0x6afd9a0e),
+     TOBN(0xd739ca0e, 0xbbbefa73), TOBN(0x6082dbab, 0xc5ec48b7),
+     TOBN(0xa0ab10df, 0xbbdea0ec), TOBN(0xb1b7ebe4, 0xf1633e03),
+     TOBN(0xfa752496, 0x7be26441), TOBN(0xf52cb1b6, 0x0ef683e6),
+     TOBN(0x1c96401f, 0x39dd611d), TOBN(0x09c5a35b, 0x7bb19083),
+     TOBN(0xa2f002b8, 0x00a5d5a1), TOBN(0x4e300ddd, 0xacf4e8ed),
+     TOBN(0x0d26b600, 0xb4cc58c6), TOBN(0x5a53863a, 0x50062651),
+     TOBN(0x62e64475, 0xad1cac22), TOBN(0x2008653e, 0xc7e11395),
+     TOBN(0xa875ad01, 0xd9479c4a), TOBN(0x3e6cf633, 0x804b30d1),
+     TOBN(0x58b3ef6e, 0xb6b06e46), TOBN(0x74c45dbe, 0xf7b8410b),
+     TOBN(0x02675759, 0xc278458d), TOBN(0xb2ef4956, 0xacd30bd1),
+     TOBN(0x1a4a5773, 0x339aae8d), TOBN(0xa775b952, 0x0c0fe175),
+     TOBN(0x7b39ac1b, 0x5d5d5ac1), TOBN(0x3f183d49, 0x11a511b6),
+     TOBN(0x9524e286, 0x045ac045), TOBN(0x0498d296, 0x4934c52f),
+     TOBN(0x1fec5474, 0x9b636528), TOBN(0xec6f7a37, 0xc3e9b84b),
+     TOBN(0x870b12dd, 0x12ee579d), TOBN(0x2a9a12ab, 0x06dd62d6),
+     TOBN(0xbcd52599, 0x071d7582), TOBN(0x7a36193a, 0xa869c457),
+     TOBN(0xd29e6592, 0xe976ae5b), TOBN(0xe82c8712, 0xadfecd58),
+     TOBN(0xbc83a440, 0xf714686d), TOBN(0xfe19344a, 0x0c21e3ba),
+     TOBN(0x2a32c989, 0xd7a191ae), TOBN(0x00a25163, 0x4e58caca),
+     TOBN(0x2c6501b8, 0xe4a11597), TOBN(0xb3e45d09, 0x7f1891e6),
+     TOBN(0xb7f532b1, 0x659fd516), TOBN(0x99cf64de, 0xa7002930),
+     TOBN(0x56357ed4, 0xf2cd2d4d), TOBN(0xa94cf5c5, 0x3447951f),
+     TOBN(0x26c7f244, 0x76a164be), TOBN(0xbd83e20b, 0xa72e974c),
+     TOBN(0x64e9c241, 0xda31de06), TOBN(0x022bc0f0, 0x1cdb203d),
+     TOBN(0x5eec4fcb, 0x55c0601f), TOBN(0xa1504f91, 0xb168a484),
+     TOBN(0xb9cf98b1, 0x1243026d), TOBN(0x6a009deb, 0xfb3e5a1c),
+     TOBN(0xf1df3752, 0x60657650), TOBN(0xa5bbd8f5, 0xcb1b8d9e),
+     TOBN(0x9e0d9447, 0x81b6af13), TOBN(0x8572cecf, 0x624cb828),
+     TOBN(0x28319d57, 0xd003617a), TOBN(0x175c4766, 0x996fde09),
+     TOBN(0x168514b2, 0x04878e13), TOBN(0x58a541d7, 0xec83a771),
+     TOBN(0xafdaad3b, 0x29fb000f), TOBN(0x1977a8de, 0xc20f56f5),
+     TOBN(0x450faf6f, 0xc5b7ba77), TOBN(0x93253964, 0xe5954518),
+     TOBN(0x11ee0f31, 0x644c3385), TOBN(0x6c24de9d, 0xa8a57bad),
+     TOBN(0xe8ff408c, 0x5533a7ba), TOBN(0x660a74d9, 0xeace56fa),
+     TOBN(0xb4b2543b, 0x8cc2a866), TOBN(0x69f23f18, 0xeffc0cbf),
+     TOBN(0x0db4682a, 0x5308b9b1), TOBN(0xce7fac53, 0x17037e08),
+     TOBN(0xf02098c4, 0x0a885b01), TOBN(0xd375c03d, 0xb2e4eb6e),
+     TOBN(0xb6d4f6c1, 0x70d4b81b), TOBN(0xa2b5e9cd, 0x7ce5f297),
+     TOBN(0x787229cc, 0xb8a233c3), TOBN(0x44ef5dd8, 0x3419867f),
+     TOBN(0x00316d22, 0x79d3d8dc), TOBN(0xdcf32003, 0x90bb1410),
+     TOBN(0x62ad0125, 0x835d2264), TOBN(0x768c8658, 0x0ed6605c),
+     TOBN(0xa31abf17, 0xfc44e760), TOBN(0xc91848ac, 0xbb22e570),
+     TOBN(0xad1882f5, 0xb16c805f), TOBN(0xb74cc0ed, 0x7ccf9e9a),
+     TOBN(0x9635af23, 0x7b122dd7), TOBN(0x48a20903, 0x5c3cd11b),
+     TOBN(0xa24820b6, 0x34c1eb54), TOBN(0x31a3c330, 0x5284dcb3),
+     TOBN(0xd966cf59, 0x069c2ee4), TOBN(0xa74eec6f, 0xb3ff9335),
+     TOBN(0xf44eeb99, 0x4620e739), TOBN(0x7663a596, 0xf4159a9a),
+     TOBN(0x79c54f42, 0xb4b745b1), TOBN(0xa8d34937, 0x59db9482),
+     TOBN(0x35fad92a, 0x579501df), TOBN(0x1d81bbe3, 0x289d7c2b),
+     TOBN(0x1d60a274, 0xddf3d371), TOBN(0xf08e23e5, 0x46df1233),
+     TOBN(0x4bc4c079, 0xf3a95f04), TOBN(0x0b43e660, 0xa8626015),
+     TOBN(0xedb31526, 0x246ae3ac), TOBN(0xa8536eb6, 0x41247209),
+     TOBN(0x6893a7df, 0xfdfacc62), TOBN(0xf3de226f, 0xc557777b),
+     TOBN(0xa68c8d8c, 0x0d7f4265), TOBN(0x55a628eb, 0x15c685e3),
+     TOBN(0x8cad8f87, 0x5ecec6ee), TOBN(0x4aefda2d, 0x2a06c242),
+     TOBN(0x46a21033, 0x57f00a7d), TOBN(0x91910c3a, 0x7ed125cf),
+     TOBN(0x0b7f0e4a, 0x541165d2), TOBN(0x15ed1b93, 0x553eeec1),
+     TOBN(0xadf5b4db, 0xd24e020b), TOBN(0xf05307a3, 0xa7493b8b),
+     TOBN(0x725548dc, 0x62070042), TOBN(0x74d71526, 0xc274916a),
+     TOBN(0x3269851e, 0x6f098d01), TOBN(0xb2e01cb7, 0xf9ec928c),
+     TOBN(0x96c2d922, 0x2b4368cd), TOBN(0x8eb84b03, 0xa0ec45d1),
+     TOBN(0x733ad068, 0x26e5b3ac), TOBN(0xced3679e, 0x93c5a962),
+     TOBN(0x23c6a22d, 0xdd6eb876), TOBN(0xbd98ad9a, 0xa343dc3b),
+     TOBN(0x61933d03, 0x56054515), TOBN(0x4a64b769, 0xe45cd744),
+     TOBN(0x617a63f3, 0x12586de6), TOBN(0x04984a9f, 0x7976e7d1),
+     TOBN(0xb00ba446, 0xcd2a0a6b), TOBN(0x5b64e7f5, 0x7d059d46),
+     TOBN(0x8801ce04, 0x6a4b08e6), TOBN(0x66f31460, 0xb13bbe9c),
+     TOBN(0xb174e887, 0x4d87114e), TOBN(0xb2fee192, 0xf348e94f),
+     TOBN(0xfede2283, 0x7c822d05), TOBN(0x8d50c49c, 0x8f82b14a),
+     TOBN(0x21ea4f6e, 0x0f5f1b5d), TOBN(0x68627cf0, 0xc1818095),
+     TOBN(0xc1c0650c, 0x8a7b2458), TOBN(0x82ab62bb, 0x8bbc6aff),
+     TOBN(0x7b3665d7, 0x6ce6989d), TOBN(0x2ad7991f, 0x7579e973),
+     TOBN(0x701287aa, 0x7e9e8510), TOBN(0xb296a038, 0x0a18da53),
+     TOBN(0xf8c3af86, 0x2bf00fdc), TOBN(0x55776951, 0xb220dc06),
+     TOBN(0x4e6e4b4f, 0x7d7dd541), TOBN(0x812feac7, 0xfe5c7431),
+     TOBN(0x6bdfa63e, 0x340297b1), TOBN(0xecc11e55, 0x98009910),
+     TOBN(0xee4c6165, 0xb25b98c0), TOBN(0x8a07b0d2, 0x02c5939c),
+     TOBN(0x9b36c176, 0x23147c40), TOBN(0x396054a2, 0xde2eab3a),
+     TOBN(0x1f41010b, 0x2c439171), TOBN(0x3ff85ee6, 0xe8139388),
+     TOBN(0x4ada4c7d, 0x8f077633), TOBN(0x9976011a, 0x824e6023),
+     TOBN(0xa2501197, 0xeaf49f63), TOBN(0xdff2122f, 0xd60b0c4c),
+     TOBN(0x1a6a3abb, 0xbab3df90), TOBN(0x854bbcc6, 0xb66ffd5f),
+     TOBN(0x525964a9, 0x728572c1), TOBN(0x8a4923a2, 0xfadbd14b),
+     TOBN(0x03830df9, 0xcd90b61b), TOBN(0xcdb00f4a, 0x79c2afe9),
+     TOBN(0xff2f84bb, 0xa6c3f13d), TOBN(0xdee45c30, 0x5c0de4dd),
+     TOBN(0x3e1dd748, 0xfba2e933), TOBN(0xe9dcc690, 0x7c51124c),
+     TOBN(0x725177af, 0x28e11f62), TOBN(0xc8e120a1, 0x8a64fdf5),
+     TOBN(0x82ab73df, 0xf24fb357), TOBN(0x2d5d1618, 0x44724879),
+     TOBN(0x09627e26, 0x96c66b86), TOBN(0x1d547cae, 0xc81d38c1),
+     TOBN(0xbe8991a4, 0xd0f76658), TOBN(0xf1508662, 0xcf11a976),
+     TOBN(0xa5dafebd, 0x3be3e582), TOBN(0xd9f545ba, 0x07399295),
+     TOBN(0xd9f564a4, 0x676f9598), TOBN(0xec00bddf, 0x9294431e),
+     TOBN(0xc1971113, 0xc1fdc758), TOBN(0xe32f572f, 0x69a001de),
+     TOBN(0x048d7776, 0xb907f044), TOBN(0x4a474e6e, 0x5ca10e67),
+     TOBN(0x6476dd40, 0x3039a4b7), TOBN(0x85de9baa, 0x018ee2b8),
+     TOBN(0x0c945aeb, 0xfd7365f2), TOBN(0x2b47dc0d, 0x96c7267e),
+     TOBN(0xb12b48a7, 0x0410de25), TOBN(0x3ba7a11a, 0x177242c1),
+     TOBN(0x44e6cee7, 0x6504ff87), TOBN(0xb2605ff6, 0x9d19f26c),
+     TOBN(0xa56bb589, 0x50fb1b6b), TOBN(0x98dc1180, 0x71d2fb53),
+     TOBN(0xa4fdc6f8, 0xa1b78e04), TOBN(0xbea745b0, 0x39d9349d),
+     TOBN(0xac474229, 0x62d7eb73), TOBN(0x7b765138, 0x8b808ac3),
+     TOBN(0x882370af, 0xd0ca219f), TOBN(0x28dcff7b, 0x9d1c23e8),
+     TOBN(0xc6dc70eb, 0x3872f0a9), TOBN(0xb2f21248, 0xdfb642b1),
+     TOBN(0x86838f0f, 0x65bbdfc9), TOBN(0x1d04a8b5, 0x40b28364),
+     TOBN(0xd4fa229d, 0x1e4d8d58), TOBN(0x74ee5e20, 0xfad0a9cd),
+     TOBN(0x25a59aae, 0x5a40ec4a), TOBN(0x73be91f3, 0x3727d6cd),
+     TOBN(0x9c31405e, 0xd63f64eb), TOBN(0x9943c34c, 0x91d2f1c1),
+     TOBN(0x70ad75d7, 0x4fcdbf34), TOBN(0xa6ce7145, 0xb239e00d),
+     TOBN(0x136bceed, 0xcd04b9e9), TOBN(0xb9ebeb8d, 0x44ed7f96),
+     TOBN(0x068b43a5, 0x5d136280), TOBN(0x2e1b6624, 0x4c559b6b),
+     TOBN(0xe3808e72, 0x5472d67b), TOBN(0x73450378, 0xce74546e),
+     TOBN(0xc1b1b66e, 0xea1d58f7), TOBN(0x2b576e4f, 0xe34c2a7d),
+     TOBN(0xc2b1bdf7, 0x2f732803), TOBN(0x37aea390, 0x9f8e48c3),
+     TOBN(0x8bbbb61e, 0x944f1cf3), TOBN(0x5cc7ccaa, 0x86c59643),
+     TOBN(0xaf4c18e3, 0x8d5b000a), TOBN(0x23b0edd0, 0x2b6d561c),
+     TOBN(0x11b67ef0, 0x0d6cbe27), TOBN(0x679d789b, 0xb1b50e9d),
+     TOBN(0xda198336, 0x372c4015), TOBN(0x5da50baf, 0x65781ea7),
+     TOBN(0x00b3a6d4, 0x550201ba), TOBN(0x988b89f7, 0xecfffc72),
+     TOBN(0xf2f08a09, 0x25948852), TOBN(0x4036bbb7, 0x406d1a34),
+     TOBN(0x1cd57f08, 0x23d2dd87), TOBN(0x11a4387e, 0x4704dac3),
+     TOBN(0xb8091a7a, 0xc5413b59), TOBN(0xe58940c6, 0x09b5fa71),
+     TOBN(0x70fd5154, 0x6a75397c), TOBN(0xea544375, 0x5c59ff75),
+     TOBN(0x15e5bed3, 0xac25bd3a), TOBN(0x1bed3c33, 0x6b17971e),
+     TOBN(0x046fc1cd, 0xbaa96968), TOBN(0xda1b010d, 0x7090256f),
+     TOBN(0xeec55752, 0xe6677d20), TOBN(0x8eac5d06, 0x24c9bb42),
+     TOBN(0xc2f6270e, 0x8f4120e0), TOBN(0xd9ae9fff, 0x07748faa),
+     TOBN(0x5a1b2634, 0xe8f7b7af), TOBN(0x1fcd743d, 0x81b1612d),
+     TOBN(0x6b065aa2, 0x3d420398), TOBN(0xe758b9c7, 0x41e06643),
+     TOBN(0xe1e52b53, 0x7f111b3b), TOBN(0xb9ee0a5d, 0x83498731),
+     TOBN(0x49c19631, 0xea8154f4), TOBN(0x8f5a3479, 0xe1c08746),
+     TOBN(0xe032d7c1, 0x65dd5561), TOBN(0x6c3420fe, 0x442bef09),
+     TOBN(0x1d390561, 0xa64eff47), TOBN(0x0d8fbf07, 0x902763bf),
+     TOBN(0x0262f26d, 0xa4bc6856), TOBN(0x7c1b59a7, 0x9f4f2101),
+     TOBN(0x663d9b38, 0x51240642), TOBN(0x39a0b4c2, 0x77ce53cc),
+     TOBN(0x1c896beb, 0x61f5e655), TOBN(0x75c4c049, 0x9f4bfd2d),
+     TOBN(0xb8799a15, 0x10111b02), TOBN(0xc76f8641, 0xa4c2fa0e),
+     TOBN(0xd77ff7fd, 0x185fc036), TOBN(0x53212bd6, 0xf5acbd16),
+     TOBN(0x4ef7431f, 0x0408cff8), TOBN(0x45aa9d99, 0xfb082c4b),
+     TOBN(0x22c1fa8e, 0xf0438565), TOBN(0x8e3a2ee3, 0x4cb43ab5),
+     TOBN(0x457df338, 0x232081d1), TOBN(0xd1293d9b, 0x482ff47b),
+     TOBN(0x802a300e, 0x68106365), TOBN(0xa8f27aa1, 0xe51978c9),
+     TOBN(0x6ca0edda, 0xa6a6a4d3), TOBN(0x4cab1223, 0x24c9526a),
+     TOBN(0x26234b2e, 0x56730245), TOBN(0x9a04c15d, 0xe1b54be4),
+     TOBN(0x153fb6cf, 0xee89282b), TOBN(0x5901ca12, 0xd79d81ad),
+     TOBN(0xbe6853d8, 0x7c3c5ffd), TOBN(0x16d3efb5, 0x35e1942a),
+     TOBN(0x3491f207, 0x3b56bece), TOBN(0x0d75e0c1, 0x5b818cfd),
+     TOBN(0x79a0e319, 0x40969df4), TOBN(0x75e4632c, 0x9ae34b31),
+     TOBN(0x4a47585c, 0x68e8df30), TOBN(0x4a4a40e4, 0x2a495467),
+     TOBN(0x92b8a6f5, 0x2762eae9), TOBN(0xa204cd80, 0xc9a3d133),
+     TOBN(0xa441ecfd, 0xd1ff23cf), TOBN(0xd06feb58, 0x4550ee57),
+     TOBN(0xe14ca6a1, 0xdc032002), TOBN(0x9a780e57, 0x05505a36),
+     TOBN(0xad93852e, 0x08cb2b29), TOBN(0xa54deaab, 0x008b00c4),
+     TOBN(0x8cd2c71a, 0xe1042350), TOBN(0x2014b85d, 0xa8915596),
+     TOBN(0x1228b3e4, 0x97ddd1dc), TOBN(0xa97282ce, 0x4a3b3ab7),
+     TOBN(0xd978cd73, 0x0f1559ad), TOBN(0x2e877fa2, 0x86b14d3c),
+     TOBN(0x01d3dc94, 0x3660f189), TOBN(0x90ad950d, 0x0d2b4ddd),
+     TOBN(0xa8d26760, 0x92245e3e), TOBN(0xfc1bf8d5, 0x4964245d),
+     TOBN(0x31206c72, 0xac3d97eb), TOBN(0x39dfd972, 0xa58c64cb),
+     TOBN(0xd631907f, 0x7efbbd16), TOBN(0x4fdc84e2, 0x174f1fd5),
+     TOBN(0xe81e89b3, 0x5c277996), TOBN(0xcb277b4e, 0x5f79f1de),
+     TOBN(0x2eff44b3, 0x2ed1962c), TOBN(0xbe36a640, 0x72883505),
+     TOBN(0x14a1fac0, 0x545e69a0), TOBN(0x76dbbcbd, 0xd6f658b9),
+     TOBN(0x0720c584, 0x78e2e86d), TOBN(0x52fccffb, 0xcaeead35),
+     TOBN(0x06f28c72, 0x587fd1b2), TOBN(0xec36a912, 0x9e48bf69),
+     TOBN(0x74874436, 0xdaa3cdbd), TOBN(0xb3f7409f, 0xcdc2f2a3),
+     TOBN(0x0e50d7fa, 0x1951c078), TOBN(0xd97ff34e, 0xee8949f0),
+     TOBN(0x00db635e, 0x742d7b1d), TOBN(0x5c0b280e, 0x29f0d0f9),
+     TOBN(0xafa7e616, 0xeabf9b35), TOBN(0x7341e2c7, 0x2c8a76e8),
+     TOBN(0x9679e34d, 0x2e96f198), TOBN(0x8c2661c0, 0x90ee26ca),
+     TOBN(0x9c6dab35, 0x67a6516e), TOBN(0x7c8edc4b, 0x46b4b34f),
+     TOBN(0xc502cf2f, 0x2afba4fe), TOBN(0x76847ae0, 0x6776dbf1),
+     TOBN(0xace02706, 0xa2c3c83e), TOBN(0x0012645f, 0x4601c550),
+     TOBN(0x1940e14a, 0xef6189bd), TOBN(0xba7f615f, 0x2cdf5e89),
+     TOBN(0x698101aa, 0x438a3781), TOBN(0xf568a45d, 0xa9e22357),
+     TOBN(0x83af640e, 0x1f913210), TOBN(0x529a29fd, 0x8d505edc),
+     TOBN(0xdf3d3090, 0xd6b0c85a), TOBN(0x46e23886, 0x6897ea43),
+     TOBN(0x97cca980, 0x416577ae), TOBN(0x1f5a96a8, 0x9aa08fc3),
+     TOBN(0xcb014b33, 0x56c05c30), TOBN(0x1944765a, 0x05ec9be4),
+     TOBN(0x2d6789cc, 0xddc4371d), TOBN(0xd768f5a6, 0xf3618fc2),
+     TOBN(0x77065e11, 0x3da93c1c), TOBN(0x4ea3fbc3, 0x0e27b3eb),
+     TOBN(0x7c1bfba0, 0x11ba30e9), TOBN(0xfc6fba67, 0x1036ebe6),
+     TOBN(0x0053a30c, 0xd3231aed), TOBN(0x7f0613d9, 0xee3ac524),
+     TOBN(0x95ec2fd9, 0x63093df6), TOBN(0xfbc63768, 0x7c0eea52),
+     TOBN(0xf767b286, 0x8b64ea48), TOBN(0x6959b0ec, 0xf75bc633),
+     TOBN(0x47e34c3b, 0xc9f63154), TOBN(0xd616b19f, 0xa524bc76),
+     TOBN(0xefc9bb54, 0x632ac100), TOBN(0xd9abba10, 0xb4d96a7d),
+     TOBN(0x3b7dd91a, 0xfe2ad7e8), TOBN(0x29134cd7, 0xb4ebf343),
+     TOBN(0x49d1c305, 0x152864fd), TOBN(0x3afd83d0, 0x80efc220),
+     TOBN(0x3552517e, 0x3f2f0d27), TOBN(0x0a2b5006, 0xfda48969),
+     TOBN(0x568863ed, 0x3c3e8ec9), TOBN(0xd99d5c62, 0x891edec9),
+     TOBN(0xb0ddc129, 0xd1c9d6ee), TOBN(0x373dad74, 0x57db23b4),
+     TOBN(0x7c178b0b, 0xb416c7df), TOBN(0x77431dac, 0x4f8a7153),
+     TOBN(0xf5288888, 0x41c1367e), TOBN(0xf1518939, 0xb838c91c),
+     TOBN(0x81e17838, 0x541f3281), TOBN(0x70030244, 0x65b2bde5),
+     TOBN(0xdc309424, 0x7350251c), TOBN(0xfac0c6ad, 0x7c811130),
+     TOBN(0x3817aa1a, 0x6a141269), TOBN(0x1aa5a92f, 0xe10b4a6d),
+     TOBN(0x996cca7f, 0x34648a96), TOBN(0x517a25b9, 0x4e2a4f52),
+     TOBN(0xff95ac42, 0x38b1547c), TOBN(0x01d981b6, 0xd9b9cd4f),
+     TOBN(0xcc34d15e, 0x88d60eba), TOBN(0x45851bf4, 0xa0ea1a51),
+     TOBN(0x5d5f9b30, 0x82854ee0), TOBN(0x914be21f, 0x176ea156),
+     TOBN(0xecac86d1, 0x2a05c368), TOBN(0x255cb9c0, 0x73a666a8),
+     TOBN(0x5e4799d9, 0x78c0eec5), TOBN(0x40ed8989, 0x8fc05a71),
+     TOBN(0x54888ac2, 0x8ae03edd), TOBN(0xef3e9865, 0xa83b554b),
+     TOBN(0x47b41822, 0xb7612fe4), TOBN(0xf6e16fd5, 0x8f76cd2e),
+     TOBN(0x091c7b12, 0xa977b5dd), TOBN(0x7051bf6b, 0x8f99d4aa),
+     TOBN(0x9f737902, 0xfed218fe), TOBN(0xd8112477, 0xb752c612),
+     TOBN(0xbb45c287, 0x18d13bd3), TOBN(0xbbf3a894, 0x23c6dd1a),
+     TOBN(0xc8171c5e, 0x13b9cf87), TOBN(0x2dfc7792, 0x34f5348d),
+     TOBN(0x9b9a662d, 0x985cabd4), TOBN(0x588a6ebc, 0x4d971de0),
+     TOBN(0xda9fd894, 0x574cba64), TOBN(0x7e0f0cca, 0x651e6e67),
+    },
+    {
+     TOBN(0x88ca276c, 0x4b8bceb8), TOBN(0x6d4ec101, 0x752d1106),
+     TOBN(0x2ad98063, 0xf834dcbf), TOBN(0x4da81d19, 0xdfff19d0),
+     TOBN(0x4ccc7cd2, 0x3a9828ff), TOBN(0xf1e389b0, 0x2e64b332),
+     TOBN(0xe2fb6a6c, 0x7308b81c), TOBN(0xc6df66b2, 0x5bcc0ac6),
+     TOBN(0x5ccb8c75, 0xe1c58c80), TOBN(0x2ba9de04, 0x83fcc95a),
+     TOBN(0xccdeb0ee, 0xdfccbcf9), TOBN(0x1d667d4f, 0x70f3d3ad),
+     TOBN(0xc6aa14a5, 0x36269820), TOBN(0x329a308b, 0x0fe87940),
+     TOBN(0x39869970, 0xede5cfb2), TOBN(0xc33c3068, 0xf601bb2c),
+     TOBN(0x3087444a, 0xa1a8781b), TOBN(0x6cb5b706, 0x5cff3cbf),
+     TOBN(0x7673a8e4, 0x83082714), TOBN(0xc4bce015, 0x0842a792),
+     TOBN(0xae71a033, 0x53e2a531), TOBN(0x147b28f8, 0x8b5315f9),
+     TOBN(0xcc460133, 0x6c5ab37a), TOBN(0xb1dd088b, 0x540dde16),
+     TOBN(0xec250455, 0x11c09289), TOBN(0x83042ba7, 0x164079c9),
+     TOBN(0x4881640c, 0x6e3879a2), TOBN(0x77c5babc, 0x802452ee),
+     TOBN(0x7a7759a6, 0x7088f360), TOBN(0x02da352c, 0xb74be7e9),
+     TOBN(0x15800cdb, 0xe0338289), TOBN(0xad69f7c9, 0x501688c6),
+     TOBN(0xb7d35063, 0x57ae1213), TOBN(0xd536753a, 0x97024ecd),
+     TOBN(0x9d680716, 0x24938196), TOBN(0xac1bee4c, 0x44ed6d4e),
+     TOBN(0x6dd9c9bf, 0x33e95503), TOBN(0x5ee9f1fd, 0x88fc1c3d),
+     TOBN(0x4a701ff4, 0x21654473), TOBN(0x9a316450, 0xbd2ffe36),
+     TOBN(0xe9130a63, 0x103b5fa2), TOBN(0xe97f7120, 0x8eee983b),
+     TOBN(0x54b7f85b, 0xe8749cba), TOBN(0x69976910, 0xbb1bca55),
+     TOBN(0x9ec4034f, 0xf4e621d3), TOBN(0xaad567ed, 0x695e17da),
+     TOBN(0x7647f054, 0xcedb2ea8), TOBN(0xf85f944c, 0x09fc7433),
+     TOBN(0x30af23b3, 0xb95eeddd), TOBN(0xfd1d565a, 0x89985f3d),
+     TOBN(0xfbb53173, 0x4c254738), TOBN(0xb07ba56a, 0x171170a4),
+     TOBN(0x5069882c, 0x294d55d1), TOBN(0xae0385c4, 0x792694c1),
+     TOBN(0x0a0c7927, 0x11225dc6), TOBN(0xadcc5f08, 0xe22867c9),
+     TOBN(0x164ac67f, 0xaee03999), TOBN(0x4de174d3, 0x79ff7f91),
+     TOBN(0x063e4943, 0x548da6ea), TOBN(0x5264880b, 0xdb7ccdf7),
+     TOBN(0x4a18f34b, 0x49b992cc), TOBN(0xe16b6f4d, 0x14065870),
+     TOBN(0xd32479ac, 0x4cdb0e21), TOBN(0xce8151f6, 0x162bc9f8),
+     TOBN(0x0f8d9a2f, 0xe8f78545), TOBN(0x091643db, 0x3145b086),
+     TOBN(0x5915a582, 0x23a1bcc9), TOBN(0x97348efd, 0x8a280fc7),
+     TOBN(0x3f9d6236, 0x65eccf5d), TOBN(0xd1a34937, 0x01ac8146),
+     TOBN(0x1b8e5128, 0x8ad0d5c1), TOBN(0x5cbcc9ef, 0xd581dd11),
+     TOBN(0x947ceaff, 0xed059f1d), TOBN(0xf5754d03, 0x7460a186),
+     TOBN(0x37698fa6, 0x0164ff7b), TOBN(0x630900d2, 0x35805339),
+     TOBN(0xe467a6be, 0xeddd6bbc), TOBN(0xc53bffec, 0x5e36b12e),
+     TOBN(0x06dfd3f9, 0xf831fc7d), TOBN(0xd995fcc4, 0xdaef86ac),
+     TOBN(0x7d148468, 0x32d5b2e3), TOBN(0x7796b94c, 0x6335f566),
+     TOBN(0x693983d6, 0x6769b8bd), TOBN(0xff0306aa, 0xed5244fa),
+     TOBN(0x2e90d41a, 0x89b8e801), TOBN(0x1af09d86, 0x39e732f3),
+     TOBN(0x96d14e1f, 0x320ccb1d), TOBN(0xbaf21c6f, 0xc05dcece),
+     TOBN(0x8ae82a1c, 0xc216cf37), TOBN(0xac437f45, 0x773828bf),
+     TOBN(0x8c12ff18, 0x9d51a85b), TOBN(0xfeb563be, 0x34c16578),
+     TOBN(0x9d9353b6, 0xc6706966), TOBN(0xcdc6eb5a, 0x0cda8733),
+     TOBN(0x033c186e, 0x3e4953db), TOBN(0x2ba46a66, 0xb2e37f7c),
+     TOBN(0xb32115e2, 0xb9f3ee06), TOBN(0x1bc12cec, 0xdd6346a9),
+     TOBN(0x6b9c2142, 0x321242fe), TOBN(0xcf9b9bb3, 0x5c68ea06),
+     TOBN(0x7fe554ac, 0x920d49bc), TOBN(0x90b3a9b4, 0x37aedebb),
+     TOBN(0xacb181e0, 0x7695af86), TOBN(0xd1c99c55, 0xfd567fea),
+     TOBN(0xb7c18083, 0xfccf76eb), TOBN(0xc693bdbb, 0xf93113a3),
+     TOBN(0x215ff05d, 0x66e03205), TOBN(0x4424aaea, 0xf76d2a12),
+     TOBN(0xb23f2782, 0xe7f30891), TOBN(0xad814d5e, 0x062db479),
+     TOBN(0x347ec1d0, 0x4aea78c3), TOBN(0x3d0f0a7e, 0x6a2332f2),
+     TOBN(0x02ecefa6, 0x8ad9e323), TOBN(0x16c81248, 0x0d45e0c9),
+     TOBN(0xd4b6253d, 0x2757306c), TOBN(0xe90203a3, 0x81e42d04),
+     TOBN(0xbcef10fb, 0xc13782f0), TOBN(0x823efe5d, 0x156267d4),
+     TOBN(0x18add11a, 0xfddb0092), TOBN(0x27068a29, 0xb104561a),
+     TOBN(0x7eb7f516, 0xda0abf3e), TOBN(0x3c92ac94, 0x61b3381f),
+     TOBN(0xbad7320e, 0xd3418870), TOBN(0xbab7a126, 0x07dbe066),
+     TOBN(0xe7ce59be, 0x2def303f), TOBN(0x0bf1f237, 0x2d1e0c9f),
+     TOBN(0x12c18d1e, 0x38f418dc), TOBN(0x7fcc5e3e, 0xb85bb676),
+     TOBN(0x0bcf25ad, 0x1b038ac6), TOBN(0x35388760, 0xddf1becb),
+     TOBN(0x5734bf37, 0x8a1ad137), TOBN(0x92f3a250, 0xb7645056),
+     TOBN(0x6ed926a4, 0x718a5ace), TOBN(0x8e63f0a2, 0xb967f1cf),
+     TOBN(0x6d9cccc9, 0xd835fe33), TOBN(0xb1b5efee, 0x31f82e18),
+     TOBN(0x997aa2a4, 0x24f2c6b1), TOBN(0xde87114f, 0x9e536a91),
+     TOBN(0x01938bd2, 0x0f819ec8), TOBN(0x012e9031, 0xef772a43),
+     TOBN(0x1578eb4c, 0x77aa9256), TOBN(0x052b4088, 0x61a0c8ed),
+     TOBN(0x1153a330, 0x6ab5a380), TOBN(0xa3e7f085, 0x132f5675),
+     TOBN(0x5e946e00, 0x909b8a41), TOBN(0x55f7d231, 0x16a3c156),
+     TOBN(0xcd952464, 0x8ac8f8e3), TOBN(0x7c5184d4, 0xcd8d67f7),
+     TOBN(0xb3468964, 0x39ef93a7), TOBN(0xf4aa0b7e, 0x4e9058c8),
+     TOBN(0xa409403e, 0x4b7c713f), TOBN(0x9d55e33c, 0x41a83e50),
+     TOBN(0x9efee704, 0x7e1754b8), TOBN(0x54085471, 0xb1c0027b),
+     TOBN(0xc5e7a6fa, 0x45af4e6d), TOBN(0xb4d3cd58, 0x30048569),
+     TOBN(0xd2c20014, 0xf3ae8e79), TOBN(0xd0b6af13, 0x849f3f23),
+     TOBN(0x3a1db915, 0x17982a8c), TOBN(0xea3c8099, 0xf9ffcf90),
+     TOBN(0x25d9eb82, 0x7bada472), TOBN(0xff84d98c, 0x09afd498),
+     TOBN(0x5e2c1ffe, 0x56ff21f4), TOBN(0xafd07201, 0x2f2f3a94),
+     TOBN(0xb0227fe6, 0xcdb673bc), TOBN(0x58fc0e7e, 0xfe8d7326),
+     TOBN(0xb988d3eb, 0x191bfd4d), TOBN(0x82499093, 0x2474d8b6),
+     TOBN(0xd1ef53cb, 0x68caff21), TOBN(0x3cff018c, 0x5074160a),
+     TOBN(0x609a4688, 0x98f982fc), TOBN(0xee5caaac, 0x562a099e),
+     TOBN(0xf650365b, 0xf8c6cfd7), TOBN(0x2652aa23, 0x9cbc10ee),
+     TOBN(0x904fd66e, 0x6ab86f4e), TOBN(0x6a25bbc3, 0x2d82f3d8),
+     TOBN(0xd3e6ecad, 0x19c7a275), TOBN(0x05ed0451, 0x3604b2dd),
+     TOBN(0xdd1d87e2, 0x00c71863), TOBN(0xd9fc8793, 0x8cd23356),
+     TOBN(0x3337f8ba, 0x0036b81f), TOBN(0x63b5a762, 0xb5300622),
+     TOBN(0x4cf696f1, 0xce8800e3), TOBN(0x12cb3261, 0x07e3cbc3),
+     TOBN(0x18eac953, 0x0fa12b5b), TOBN(0x45ccf073, 0x77d159b5),
+     TOBN(0xa7480444, 0x6e844a0d), TOBN(0x4404e6c6, 0xd77d1c18),
+     TOBN(0x003e43a6, 0xce1af18f), TOBN(0x8a828081, 0x17fdffcc),
+     TOBN(0x91b63c11, 0xcabf3d17), TOBN(0xa4dedc21, 0xad26f286),
+     TOBN(0x6bf62b69, 0x1a2b1579), TOBN(0x3b67b87b, 0xceeb29ff),
+     TOBN(0x451ffadb, 0x40d4b996), TOBN(0x10c6ae50, 0x080978f8),
+     TOBN(0x959d47e2, 0x2c242dc5), TOBN(0xced9e922, 0x5423e158),
+     TOBN(0x9a212d4c, 0x8d8a68f1), TOBN(0xeff3d644, 0x3708393f),
+     TOBN(0x43f51810, 0xfbaffded), TOBN(0x3886ccb4, 0x0f6fd7c3),
+     TOBN(0xb939247b, 0x13c31946), TOBN(0xbc1ee613, 0xaa1fd72a),
+     TOBN(0x6d40140a, 0x631fd790), TOBN(0x9382e3ba, 0xd26b3fd9),
+     TOBN(0xff414370, 0xb3af96c3), TOBN(0x38c813cf, 0xe0ea9ad6),
+     TOBN(0xf8844c3c, 0x157594da), TOBN(0x2a7b514f, 0xcac628bd),
+     TOBN(0xc023e4e2, 0xc08c5107), TOBN(0x6c164496, 0x3f2722fe),
+     TOBN(0x842e1d06, 0xc03a22ad), TOBN(0x5dbc2865, 0x37ddae0d),
+     TOBN(0x46dfc88d, 0x0342bc72), TOBN(0x873c805c, 0xa4a3c65c),
+     TOBN(0xd202853b, 0x60aa5c14), TOBN(0x1dc35d34, 0x3850cc05),
+     TOBN(0x8014357e, 0x0cabccfd), TOBN(0x1aa44ce9, 0xc5a5225a),
+     TOBN(0xa3cef920, 0x3a8444b4), TOBN(0xcf3f91b3, 0xc95384b1),
+     TOBN(0x1d625ba1, 0xc9e5da54), TOBN(0xbf1fba37, 0xb1d0f46a),
+     TOBN(0xdcef4fad, 0xfb3f4885), TOBN(0xa49debb2, 0x3267f912),
+     TOBN(0x6417d37a, 0x1e121cb8), TOBN(0xa6d871fc, 0x533e94c9),
+     TOBN(0x89f80208, 0x2e4834fb), TOBN(0x27e83f0f, 0xb353452f),
+     TOBN(0xaf009f3c, 0xe1f8f322), TOBN(0xa5b77a77, 0x89319fd8),
+     TOBN(0x0a89e741, 0xedf71900), TOBN(0xd679b841, 0xd514d93f),
+     TOBN(0x8878577f, 0xb0a03702), TOBN(0xc9607b78, 0x85a209ae),
+     TOBN(0xb7bd0616, 0x59432a28), TOBN(0x0da060a2, 0xed567145),
+     TOBN(0x44e35a7a, 0x1a449f52), TOBN(0x9c9a2c82, 0xbbaccc0f),
+     TOBN(0x83abd436, 0xd83701f4), TOBN(0x56e8bfe8, 0x4bb9cbe8),
+     TOBN(0x5b545cc8, 0xc631cd1e), TOBN(0x6d03426f, 0x955aca7d),
+     TOBN(0x049fc9fa, 0x2f8db817), TOBN(0xfcec1799, 0xdc59675f),
+     TOBN(0xa00ed392, 0x0455f095), TOBN(0x6d7cfa5f, 0xe5096b18),
+     TOBN(0x2cda5cae, 0xe30ae90b), TOBN(0x2cc34290, 0xcaabea0d),
+     TOBN(0x564afcd9, 0x41e67856), TOBN(0x210c7a09, 0xcf6ef8b7),
+     TOBN(0xc316d352, 0xf82a591d), TOBN(0x5fe8cc4d, 0xab43d2a1),
+     TOBN(0xd8ebce97, 0x8b4e9470), TOBN(0x26c78f44, 0xba321a07),
+     TOBN(0xa63f4b34, 0xd75e509c), TOBN(0x9122bbc5, 0xb9a6c63b),
+     TOBN(0x8bf792a3, 0x17942443), TOBN(0x95b05d68, 0x7f4f70f9),
+     TOBN(0x57d7dee5, 0x13b70dc8), TOBN(0xe84259ed, 0xfc376fdd),
+     TOBN(0xf8c4c4ff, 0xe3e313b4), TOBN(0xf8e2d3da, 0x13fa8ff1),
+     TOBN(0x9692c390, 0xf8e4eede), TOBN(0x3b514551, 0x0e95a902),
+     TOBN(0x45c1670c, 0x7360623e), TOBN(0xf7a74f55, 0x6abd2a82),
+     TOBN(0x99b16e7e, 0x24e8e721), TOBN(0xae52fa2a, 0x512f1401),
+     TOBN(0x46c60e80, 0x3f3a09d5), TOBN(0xf803d1b3, 0x0750e968),
+     TOBN(0x17840d2f, 0x1791644e), TOBN(0x3e32b3db, 0x3b7981e6),
+     TOBN(0x2d0830a5, 0xd3dfae10), TOBN(0x1b28d118, 0x6cc6dd0d),
+     TOBN(0x944a9889, 0x78368274), TOBN(0x310da94a, 0x55b1bf81),
+     TOBN(0x503061ec, 0x0d739056), TOBN(0x1947e940, 0xb4d73288),
+     TOBN(0x760ee846, 0x0228346f), TOBN(0x108765b3, 0xc5cff077),
+     TOBN(0x22092b39, 0xbeb12160), TOBN(0xa631d553, 0xb63001af),
+     TOBN(0x9340cac4, 0x0af3d43a), TOBN(0xe6cbfb54, 0x60d338a3),
+     TOBN(0x2280ff0c, 0x7ca3f604), TOBN(0xaf48f86b, 0x3ba738cb),
+     TOBN(0x7435dd78, 0x47d372ff), TOBN(0xbf9c7149, 0xf005c006),
+     TOBN(0x624084b9, 0x7a8d0e81), TOBN(0x50b578f3, 0x4840496c),
+     TOBN(0x414ca2c1, 0xb52a4266), TOBN(0xa3c30275, 0x5535ef0b),
+     TOBN(0xd4b808c1, 0xb50f7f47), TOBN(0xe6781ae2, 0x9a199920),
+     TOBN(0x50141234, 0x27a91ef8), TOBN(0x2f4f5937, 0x5b77d060),
+     TOBN(0x1be8269e, 0xc2dcb03d), TOBN(0xa293017c, 0x9f65043f),
+     TOBN(0x1678dfe0, 0x8caac401), TOBN(0x4942d8ce, 0x968b1716),
+     TOBN(0xa9b55fae, 0xae36e201), TOBN(0xcfe4bde3, 0xd5279632),
+     TOBN(0x6126d744, 0x79d637e6), TOBN(0x8491f1a8, 0xd63b4aad),
+     TOBN(0xdf97b736, 0x9816b82c), TOBN(0xafca2c36, 0x796408c1),
+     TOBN(0xc17f3f01, 0x7a8e8058), TOBN(0xb3335a24, 0xe74705e2),
+     TOBN(0xee200023, 0x46e3e3b0), TOBN(0x07bce061, 0x40630e08),
+     TOBN(0x46b42c00, 0xee8f9dfc), TOBN(0x3b8e8509, 0x9e9b7f58),
+     TOBN(0x83df4b18, 0xd36e8e89), TOBN(0x09631af5, 0x15d50555),
+     TOBN(0xb7906b77, 0xef1ee3f1), TOBN(0x8272dc83, 0x4bd1e17b),
+     TOBN(0xf160bfd9, 0x4903faac), TOBN(0x7fe9e999, 0x0dc71e59),
+     TOBN(0x6ee9b790, 0xe714187d), TOBN(0x7391ec2a, 0x9d5a656f),
+     TOBN(0xcbb55ec6, 0xe10b20f0), TOBN(0xbba3b57b, 0xec3645d6),
+     TOBN(0x9c3265bc, 0xe18322e8), TOBN(0xdb49b0f3, 0x93328c91),
+     TOBN(0xa911db72, 0x49c2bbec), TOBN(0xf71b4df3, 0x6e5bd229),
+     TOBN(0xdccede33, 0x7ba27baa), TOBN(0x1af4476a, 0x4b712a97),
+     TOBN(0xf0aaabec, 0x8a8683ad), TOBN(0x138cdac5, 0x6fa8e84c),
+     TOBN(0xd2d50b00, 0xdc78b1ad), TOBN(0x26fc0b72, 0x696442b9),
+     TOBN(0x12cd5d8b, 0x125bf11b), TOBN(0x2a2ce980, 0xc4f82ca6),
+     TOBN(0x9921c0a6, 0x52e00dd3), TOBN(0x98e8707a, 0xf1d7e1af),
+     TOBN(0xaa7aa8b8, 0xdf03b040), TOBN(0xb3ba8b23, 0xdff6bd74),
+     TOBN(0x2fd0faab, 0x31db8c0b), TOBN(0x4697e9bf, 0x2819b732),
+     TOBN(0x2dc3a5d0, 0x0425b866), TOBN(0x4b9e7899, 0xd97816f1),
+     TOBN(0x1355c412, 0x4c756c70), TOBN(0x2d4c4eee, 0x0fa089af),
+     TOBN(0x4d8425a8, 0x3b8a01b5), TOBN(0xcc26b8a9, 0xa3531d3a),
+     TOBN(0x6eebe11b, 0x7ebd9eea), TOBN(0xd511a797, 0x92c0f858),
+     TOBN(0xaa863f01, 0xec49a0c8), TOBN(0x7fb65625, 0xa8242995),
+     TOBN(0x9de9d3f4, 0x3dbc00c3), TOBN(0xb846152f, 0x3f7d61ab),
+     TOBN(0xc060fdbd, 0xd0d74549), TOBN(0xe722aab2, 0x7b273702),
+     TOBN(0x9e54f098, 0xd81b6f6e), TOBN(0x32dbaa5f, 0x9e2fde1f),
+     TOBN(0x14cc9995, 0x9ebbc796), TOBN(0x4ca6686c, 0x0eb83921),
+     TOBN(0x6e65d7c6, 0x10a0c0bd), TOBN(0x1f6930d7, 0xb3c0f6cd),
+     TOBN(0xe4e0a933, 0x4d783d6f), TOBN(0xc945ee7f, 0x70b20ad4),
+     TOBN(0x521bd135, 0x034b0265), TOBN(0xeb5d96e0, 0x0fa9be95),
+     TOBN(0x834c28c2, 0x357ef592), TOBN(0x08ab5b4c, 0xb81df99f),
+     TOBN(0x6be99d80, 0xf464825d), TOBN(0x1cc83719, 0x9a0c1293),
+     TOBN(0x76616803, 0xe7e43c6a), TOBN(0x6fa33715, 0x91cc47ac),
+     TOBN(0xc3fdb99b, 0xdbfc08b9), TOBN(0x66e1ef2d, 0x68e2b249),
+     TOBN(0xd3d8ef7f, 0x64a4a438), TOBN(0x775a70fc, 0xa6f25b00),
+     TOBN(0x2444c682, 0xa0cb5443), TOBN(0x264c2662, 0x4b743ee7),
+     TOBN(0xd7a1adc4, 0xa303eb20), TOBN(0x3f14821b, 0xf60a5b98),
+     TOBN(0xa439102d, 0x1a1d7661), TOBN(0x47c25a37, 0x8d8a5a1a),
+     TOBN(0xdf4a48db, 0xa34c66a9), TOBN(0xab467364, 0x4c828c73),
+     TOBN(0xd3caad73, 0x3459cc8b), TOBN(0x08eeb442, 0x181b16c2),
+     TOBN(0x3444abbb, 0x70600d33), TOBN(0xaa2a39c4, 0xcd0f8e70),
+     TOBN(0x5fc6ae8f, 0x24836d70), TOBN(0xc119be84, 0x47d32fd4),
+     TOBN(0x2b3f3771, 0x0d6000ce), TOBN(0x439893a8, 0xe602337a),
+     TOBN(0x4b75ff6e, 0xc1e8e564), TOBN(0x6185413c, 0xe451cf42),
+     TOBN(0x0276d3b6, 0x162c3150), TOBN(0x844539e0, 0x3aea9c55),
+     TOBN(0xfc629ee6, 0x42e9d70b), TOBN(0x4eb9b7e6, 0x0be610c9),
+     TOBN(0x8c53fda1, 0x39ca3d92), TOBN(0xd2e4cfa6, 0x14c2e9e2),
+     TOBN(0x3c1f6895, 0xf14b31b2), TOBN(0xad42d951, 0xeb951fad),
+     TOBN(0x5b20a169, 0xb8f10fc1), TOBN(0x284810bd, 0x586c61cd),
+     TOBN(0x0c4a89aa, 0xe863d781), TOBN(0x2eda4847, 0x9c235d5c),
+     TOBN(0x8e141950, 0xe6005150), TOBN(0x75716e1b, 0x52785efa),
+     TOBN(0x290ced58, 0x8305624e), TOBN(0x398956a8, 0x06650920),
+     TOBN(0xd057a47b, 0xdb5bd5b6), TOBN(0xf2d85299, 0xbe9e119c),
+     TOBN(0x4783095c, 0x7c5fc039), TOBN(0x72f7e7cd, 0x05363915),
+     TOBN(0xe46b90d1, 0xdf3e2968), TOBN(0xaadb3dae, 0xaaea2e53),
+     TOBN(0xf5d37496, 0x0cc4f426), TOBN(0xa59bffa8, 0x59d78369),
+     TOBN(0x7ad4cc11, 0xf0a46b04), TOBN(0xcbd63351, 0xb8e21b9e),
+     TOBN(0x60d255e6, 0x5653ebbf), TOBN(0x3eaa59af, 0x4d6b5843),
+     TOBN(0x90049d25, 0x9e1df2e2), TOBN(0x9a185a6d, 0xe56aa105),
+     TOBN(0xbd31c5cf, 0x80e3d909), TOBN(0x30caad3b, 0xa1f034d1),
+     TOBN(0xaca74fa1, 0xd9c7c342), TOBN(0xac722cfc, 0x9565cf8a),
+     TOBN(0x8b172ce6, 0x5b42e582), TOBN(0x9e99e4e5, 0x9b0607b2),
+     TOBN(0x284eb579, 0x9446ca45), TOBN(0x6c5464ba, 0xc57c9feb),
+     TOBN(0x1437fc95, 0xe511bc3b), TOBN(0x22d7bc16, 0x834d0889),
+     TOBN(0x62e545b2, 0xc5071c43), TOBN(0x4c644d48, 0x8cb4acd6),
+     TOBN(0xd9efbe50, 0x68246492), TOBN(0xc9d169e7, 0xcbd8ad0e),
+     TOBN(0xcb7365dc, 0x798ae01f), TOBN(0x5783f98f, 0x6d0dea3a),
+     TOBN(0x9b4a7e38, 0xec454423), TOBN(0x27405d08, 0x96ff4c8c),
+     TOBN(0x9769f097, 0x0c462f7c), TOBN(0xcbda5412, 0x7dc946aa),
+     TOBN(0xdacb510f, 0xe7dd5146), TOBN(0x9c9a0d39, 0x30507b37),
+     TOBN(0xa605730b, 0x05ded0ac), TOBN(0x7e683472, 0x6c6c7b5b),
+     TOBN(0xb378d92c, 0x7c952984), TOBN(0xec76370d, 0x72ae34d6),
+     TOBN(0x1fde0bde, 0xacda665b), TOBN(0xc8f648f4, 0xb931afc1),
+     TOBN(0x2b55adb2, 0xb960f6ce), TOBN(0x71b3bdd4, 0x7336a643),
+     TOBN(0xf66e77bf, 0x73cc39e7), TOBN(0xf582c5e8, 0x2fa3999a),
+     TOBN(0x30ecd0c7, 0xaf986d1d), TOBN(0xa2ae53ed, 0x4557dd65),
+     TOBN(0x97ebccfb, 0x7d618a1d), TOBN(0xcbf54149, 0x11eed889),
+     TOBN(0xdd0ff0e7, 0xd8f2bdd4), TOBN(0x6ac4a9fb, 0xfa769e74),
+     TOBN(0xdfdfc7e9, 0x93e5abab), TOBN(0x0c7151c5, 0xdffc6fcc),
+     TOBN(0x6d75e962, 0x5cbae56c), TOBN(0x77fae152, 0x96dccb89),
+     TOBN(0x275c4946, 0x6cc0e535), TOBN(0xc4a400a9, 0x81781318),
+     TOBN(0x8b9f872c, 0x77ba50e6), TOBN(0x971b6cb3, 0xa138eeb4),
+     TOBN(0xa7e7d1f9, 0x53f552a7), TOBN(0x360512ce, 0x8447c730),
+     TOBN(0xf0c73bbb, 0xc5454439), TOBN(0x7f1b9b18, 0xa3a24b5c),
+     TOBN(0xc5bb48dc, 0x51fa7d6b), TOBN(0xd264d6ec, 0x8b05a553),
+     TOBN(0x123caaf2, 0xe9371f83), TOBN(0xdf5da393, 0xb149f564),
+     TOBN(0x38e02eb6, 0x853b9bab), TOBN(0xc6aab96e, 0x95bf6647),
+     TOBN(0x4890be89, 0x3141219b), TOBN(0x7afe4c2f, 0x7883fe8e),
+     TOBN(0xc27bd13c, 0x59b86241), TOBN(0x1b9720f5, 0xaacebdc9),
+     TOBN(0xa054e203, 0xf6b2174c), TOBN(0xd4e7b952, 0x60f6de8e),
+     TOBN(0xcf7b1aea, 0xf4558633), TOBN(0x43fc1881, 0xbefa40a6),
+     TOBN(0x592164dd, 0xe23cef63), TOBN(0xfe57d6e8, 0xf7b4aaf2),
+     TOBN(0x38a5e2c9, 0xe8aef9bc), TOBN(0x576bd78c, 0x1ac2b10b),
+     TOBN(0x2357944c, 0x14309d10), TOBN(0x9933d7ed, 0xed0ed94a),
+     TOBN(0xb8792ea3, 0x0339f299), TOBN(0xcfb44322, 0x87fd9bd1),
+     TOBN(0x864f2fd5, 0x92966739), TOBN(0x7435ecc5, 0xd3cfd83e),
+     TOBN(0x8516d277, 0xec4249f2), TOBN(0xaa7e1a8a, 0xfc158b34),
+     TOBN(0xfc0fc22b, 0xfbe640a1), TOBN(0xf287767f, 0x91121fec),
+     TOBN(0x0ce48273, 0x3f590dcb), TOBN(0x5e994e2f, 0xf087c249),
+     TOBN(0x681a38c7, 0x65604726), TOBN(0x4f8c6ae3, 0x247a421e),
+     TOBN(0x1a51eaa0, 0x1294956e), TOBN(0x0984b1ef, 0x47c9b324),
+     TOBN(0x3749bd0d, 0x597b7696), TOBN(0x9d432b78, 0x08e57ee7),
+     TOBN(0x3092afe1, 0x2ba112d2), TOBN(0x89ccee49, 0x16c5a7f5),
+    },
+    {
+     TOBN(0x355e9d7b, 0x54089685), TOBN(0x9f0ec68f, 0x40818349),
+     TOBN(0x4cf4d8cd, 0x3861b80f), TOBN(0xcce669fd, 0xc1f5fa14),
+     TOBN(0xea212509, 0x1788f9da), TOBN(0x32953613, 0xf3ccf239),
+     TOBN(0x1048d092, 0x50027f3b), TOBN(0xe807b39d, 0x4270fbcb),
+     TOBN(0x5099dc55, 0x95e388c3), TOBN(0xd0670ff5, 0xea44e3ea),
+     TOBN(0xd212c993, 0x61b41f7b), TOBN(0x4f594af9, 0xfaf13305),
+     TOBN(0xbc508bf2, 0x05c01232), TOBN(0x76833536, 0x39ff08a5),
+     TOBN(0xa1cf70bd, 0xb837741a), TOBN(0xba8e6616, 0xaaf7bd2a),
+     TOBN(0xde04c343, 0xdef27938), TOBN(0x3f15ca91, 0x48cee32a),
+     TOBN(0xcb61573b, 0x9dd142da), TOBN(0xc094eefd, 0x126dd9bc),
+     TOBN(0x5d42f1a5, 0x136bb4da), TOBN(0x75693952, 0xdb2f3449),
+     TOBN(0x98017cd6, 0x5c16795e), TOBN(0x9e401530, 0x2afb67db),
+     TOBN(0x6376749f, 0x9b7c6c75), TOBN(0x680eacdc, 0xacbca35d),
+     TOBN(0xe87fd5b5, 0x5e145b32), TOBN(0xeb20d1ba, 0x36b886af),
+     TOBN(0xca499055, 0x779b12bb), TOBN(0x6f290ff2, 0x0be39fb7),
+     TOBN(0x33ad6fe0, 0xf4a128ce), TOBN(0xf09e2a40, 0x9b31da81),
+     TOBN(0xb2ed3d70, 0x12039372), TOBN(0xb87e02c4, 0x2ff46c13),
+     TOBN(0x164246c6, 0xfb27dce2), TOBN(0xe34ee8f6, 0xe6d95811),
+     TOBN(0x66cc601c, 0x3ec1fde9), TOBN(0x056b3194, 0x80ffdd56),
+     TOBN(0xff009868, 0x9626aa21), TOBN(0xc3e4982c, 0x2d931092),
+     TOBN(0xbc0da9c1, 0xc3d42729), TOBN(0x4905da24, 0x720df0a0),
+     TOBN(0x0e5e1fa0, 0x45f6eadf), TOBN(0xc02033f3, 0x2aab7523),
+     TOBN(0x45ba916f, 0xedde75e1), TOBN(0xf43919bd, 0x75c68e52),
+     TOBN(0x00e7c076, 0x84892e6a), TOBN(0x259f8488, 0x70dfeb08),
+     TOBN(0x3bfd5f2c, 0xd8a869a0), TOBN(0x1df48669, 0x574e7d67),
+     TOBN(0x16d6ed5a, 0xe14cfd3b), TOBN(0x583aac2c, 0xfcf78465),
+     TOBN(0x67210e6b, 0x67da2ae9), TOBN(0x0b024e70, 0xcfee511d),
+     TOBN(0xf27e122c, 0x13839a4f), TOBN(0xfa5356c9, 0xb79dfa97),
+     TOBN(0xf0c24783, 0xf357999b), TOBN(0x2c21474c, 0x26bfacb3),
+     TOBN(0xe3abed6a, 0xd3ddb945), TOBN(0xbb21b764, 0x6031a5ea),
+     TOBN(0x6db3b68b, 0x8afc2a09), TOBN(0x1aac2f08, 0x81306b71),
+     TOBN(0x882c3371, 0x852eb6f5), TOBN(0xadfe0c1a, 0xd98e9b6f),
+     TOBN(0x0247ee7b, 0x7edcb9e5), TOBN(0xe29ec013, 0x1f29918b),
+     TOBN(0x5d1629e6, 0x6099b6ce), TOBN(0x68587803, 0xcb534584),
+     TOBN(0x6ccfeddb, 0x8ce551d3), TOBN(0x7ef98b72, 0xf85123a8),
+     TOBN(0x19af4771, 0xf9711dcd), TOBN(0x8f67858b, 0xfd80e4dd),
+     TOBN(0xa4c8c016, 0x7d607ee3), TOBN(0x15db36d7, 0x4015a479),
+     TOBN(0x0cb58eee, 0x9d28ea30), TOBN(0xb3d469b0, 0xbecb7b4e),
+     TOBN(0x811081b9, 0x6f476e2c), TOBN(0x264da3aa, 0x59c78fab),
+     TOBN(0xd6e5813d, 0x3cd73147), TOBN(0xce9e34a4, 0xe905362c),
+     TOBN(0xe551ec2e, 0xcb3afa55), TOBN(0x2c9bef25, 0x4b05589c),
+     TOBN(0xd36ddeb7, 0xbcd083bc), TOBN(0x1c180b52, 0xddb54a24),
+     TOBN(0xb84220f3, 0xc0961f32), TOBN(0xa71103fb, 0xfe3ae670),
+     TOBN(0x6a14d319, 0x46902477), TOBN(0x516701d2, 0x778b8eee),
+     TOBN(0x1cdb1025, 0x4c3166d5), TOBN(0x3a0ba2c2, 0x3d6fcb6e),
+     TOBN(0xa218b4af, 0xb3820def), TOBN(0xda6de958, 0xbfe8a8f8),
+     TOBN(0xc2b3c755, 0x4ceabdfa), TOBN(0xd3534691, 0x8d73edcb),
+     TOBN(0x453b8e63, 0x0ce17182), TOBN(0x6507a5b0, 0x01654263),
+     TOBN(0xb2b8e424, 0xd5da0e59), TOBN(0x7e599c75, 0x61ac4c2e),
+     TOBN(0xc64cb4c3, 0x41aff49a), TOBN(0x0e231e63, 0xea3e378e),
+     TOBN(0x707cc0e3, 0xe08edace), TOBN(0x18918dd2, 0x5410779f),
+     TOBN(0xcdd57690, 0x2eef6bb3), TOBN(0x4c54d7d8, 0xff758569),
+     TOBN(0x49459204, 0x2c89683c), TOBN(0x93596a16, 0x7827e518),
+     TOBN(0x6198954b, 0x2b20c939), TOBN(0x6672c94d, 0x8044d3ba),
+     TOBN(0x55e95fd3, 0x199b16dd), TOBN(0xa8484135, 0x4185999a),
+     TOBN(0x5e8709c8, 0xfe36e449), TOBN(0x47470e2e, 0x91401957),
+     TOBN(0x0058bb09, 0x0874afce), TOBN(0x19fb1d56, 0x606c3e52),
+     TOBN(0xe1208b2a, 0x710903a0), TOBN(0xecabc372, 0xd47dfd1c),
+     TOBN(0xd9daa7f4, 0x5e94818f), TOBN(0x1302ac8f, 0x5dc99882),
+     TOBN(0x7b4c6b15, 0xc44d37be), TOBN(0x0bcf6d4c, 0x72d19e0d),
+     TOBN(0x1e0bf063, 0x3fd5a1de), TOBN(0x5d05e901, 0xa75b5b8c),
+     TOBN(0xbbbdb1ab, 0xcb3c617a), TOBN(0x44954a8c, 0x1aef4706),
+     TOBN(0xbc3ceea3, 0xff6a6e47), TOBN(0x6140f421, 0x0ded1275),
+     TOBN(0xbb4b4c04, 0x4dabe95f), TOBN(0xc55e87da, 0x7135e813),
+     TOBN(0x15ad105c, 0xd963dd6b), TOBN(0x33d18f73, 0x666941a3),
+     TOBN(0x860ccabe, 0x5d9253d6), TOBN(0x2af702fd, 0xd16e8b69),
+     TOBN(0x7e46aadd, 0x74e525c0), TOBN(0xd9958a44, 0xaf59f48f),
+     TOBN(0xd8ca872f, 0x8e7de482), TOBN(0xc2270c14, 0xcf7d007d),
+     TOBN(0x87c6204e, 0xa200e574), TOBN(0x0ee014cb, 0x7b69e79e),
+     TOBN(0x176ff378, 0x82b23226), TOBN(0x802d829d, 0x8dbbb2f3),
+     TOBN(0xb902924f, 0xe0a4dc31), TOBN(0x1f1a9ec7, 0x5fe522f2),
+     TOBN(0xbcd95d85, 0x4da7c04a), TOBN(0x3a3a2e63, 0xb1543c0c),
+     TOBN(0x9e70a3ff, 0xf3271bf8), TOBN(0xd2522d88, 0xd2cd68ad),
+     TOBN(0xb777851b, 0xa6b727b9), TOBN(0x58953d6f, 0x63ff5264),
+     TOBN(0x5e111c22, 0xb65c70d2), TOBN(0xaae73c5b, 0xd3a5143f),
+     TOBN(0x2daa2bfc, 0x85ef5dc0), TOBN(0x5e7258d2, 0xea13ded3),
+     TOBN(0x4161127c, 0x2e3ce423), TOBN(0x7e35a0a2, 0x6b1af415),
+     TOBN(0x004483a8, 0xeed24b7b), TOBN(0x2816180a, 0x9f9d44f1),
+     TOBN(0x214add93, 0x062829a1), TOBN(0x262a0bef, 0x225e847c),
+     TOBN(0x4bb1b1ce, 0x5d6c53c4), TOBN(0xd02f829a, 0x91d06e53),
+     TOBN(0xcdc8ba5c, 0x784da27c), TOBN(0x78a6c0d2, 0x161b5836),
+     TOBN(0x6bea92c4, 0x8373c6a4), TOBN(0x815f1a30, 0xa881f59a),
+     TOBN(0x699c8642, 0x227cb8e2), TOBN(0x515d1e2b, 0x25a2b9d0),
+     TOBN(0xcb5f1c6c, 0x1787b3e5), TOBN(0xc9a10260, 0x104dddc6),
+     TOBN(0x18be4f2a, 0x0f3811e5), TOBN(0x8c05d3fc, 0x71e727d3),
+     TOBN(0xecae3e5f, 0xfa707140), TOBN(0x4bb05b16, 0xd275b463),
+     TOBN(0x74bad373, 0xb02a5ac8), TOBN(0x7232875a, 0x520344ee),
+     TOBN(0x32cef98c, 0x65059d8f), TOBN(0x68e0fdb6, 0x54e1b11d),
+     TOBN(0x683424f3, 0x3f3db43f), TOBN(0xf5f0878f, 0xabf4a83f),
+     TOBN(0x681350d9, 0x4ac2c5c9), TOBN(0x825e9ecb, 0x47dd3652),
+     TOBN(0x420743f0, 0x20713db6), TOBN(0x95db7427, 0xd1b082e5),
+     TOBN(0xa0e1117f, 0x1affa57d), TOBN(0x62c87b5e, 0xf940f325),
+     TOBN(0x6a65fda8, 0x4e1d5d9a), TOBN(0x0c0fe385, 0x345ccdef),
+     TOBN(0x19ff360f, 0xd6d72c0a), TOBN(0x1be1e8d7, 0xfb016131),
+     TOBN(0xe2f27e91, 0x025b45e1), TOBN(0x25bec266, 0x05259bf1),
+     TOBN(0xd7b8b4e7, 0xe51cc67e), TOBN(0x3a839aa5, 0xab80a20e),
+     TOBN(0x04a9b695, 0x9f85320d), TOBN(0xb939cd83, 0x98d669f3),
+     TOBN(0x24464ced, 0xe6948957), TOBN(0x463de507, 0xa43928e8),
+     TOBN(0x4e1844e7, 0xf8755845), TOBN(0xc9c71091, 0x5447e61c),
+     TOBN(0x1798f394, 0x599d4bd7), TOBN(0x758f7630, 0x1e072c64),
+     TOBN(0x83c93728, 0x739b1925), TOBN(0x692017d7, 0xfa8eb048),
+     TOBN(0x4a3a2a59, 0x478d1ee3), TOBN(0xb8e62912, 0x022640cd),
+     TOBN(0x4689a4dd, 0x8572b8d7), TOBN(0x6281ddfe, 0x8f79da63),
+     TOBN(0x788bf9aa, 0x212a153c), TOBN(0xb67e18f5, 0xb3438da6),
+     TOBN(0x3fbafc51, 0x31cebdb8), TOBN(0x7f8ad590, 0xb042bd47),
+     TOBN(0xf5d26c88, 0xe3055004), TOBN(0x7f23a149, 0x3d7d6f5c),
+     TOBN(0x2fee5428, 0x8758ccc0), TOBN(0xb08c91b7, 0xe1b80dfa),
+     TOBN(0xf2bcc903, 0xea0c0a53), TOBN(0xcdf2eae0, 0x04e684ff),
+     TOBN(0x354b2c07, 0xe1d9a693), TOBN(0x93b1fa2d, 0x97a833a8),
+     TOBN(0x2dcd22c7, 0xe9e5f2b1), TOBN(0xf040a69c, 0x18aa3163),
+     TOBN(0x4f9a4b29, 0x76939874), TOBN(0x58e5947f, 0x15e24d44),
+     TOBN(0x9b47a945, 0xb0c2ef6f), TOBN(0xc4a15b7d, 0xf630e92c),
+     TOBN(0x8d7a33e7, 0x7b1d4141), TOBN(0x44dabde9, 0x966486bc),
+     TOBN(0x387a6016, 0xef31dc9d), TOBN(0x76744b23, 0x1462ff61),
+     TOBN(0x2ad63954, 0x20cdd726), TOBN(0x9cff7e86, 0x0e7803da),
+     TOBN(0xaf5b8b4a, 0xfd124ed3), TOBN(0x466dbbbd, 0x050c1149),
+     TOBN(0x68352636, 0x06b296a3), TOBN(0x0ab40080, 0x7f3fe1ef),
+     TOBN(0x1fc38951, 0x05bf08f8), TOBN(0x69b54ae4, 0x633c457f),
+     TOBN(0x2ad428c6, 0x1a206c53), TOBN(0xd6725687, 0x8b09b3f9),
+     TOBN(0x552d4d0e, 0x0bc619c9), TOBN(0x0e88b313, 0x3113c689),
+     TOBN(0xb2483b80, 0xe87a91b4), TOBN(0xb9f842d7, 0x0c75377b),
+     TOBN(0x50463f38, 0x5a78145e), TOBN(0xf2d3810d, 0x830817a9),
+     TOBN(0x1819261e, 0x39cc886a), TOBN(0x697de51d, 0x8415699b),
+     TOBN(0x688a874e, 0x5cab106e), TOBN(0xde48f3bb, 0xcb8692ec),
+     TOBN(0xffa1de97, 0x38f4194d), TOBN(0x33d2726a, 0x3b996b63),
+     TOBN(0x787c0ec3, 0x0d2053a7), TOBN(0x9447e9cb, 0xeecd5c0c),
+     TOBN(0x077f121c, 0x284773c0), TOBN(0x496427e4, 0x815829a1),
+     TOBN(0x4b119786, 0x94def08b), TOBN(0x9e7b29e6, 0x9c15a778),
+     TOBN(0xa4d6d2be, 0xfd4a8a47), TOBN(0x4f000a12, 0x4333baef),
+     TOBN(0xc9049d86, 0x642c570b), TOBN(0x9424e8f9, 0x25e6aa6a),
+     TOBN(0x84de7fe9, 0xe011cfec), TOBN(0xf273f956, 0x1e8c83b0),
+     TOBN(0x98960835, 0xa47a40a6), TOBN(0xd91a20f1, 0x0a13c27b),
+     TOBN(0xaf08b4ef, 0xed703e13), TOBN(0xefcbcf34, 0xc9994946),
+     TOBN(0x019e6f38, 0x2d53b069), TOBN(0x3d62c3c0, 0x9b160894),
+     TOBN(0xac7ad700, 0xadfc8f3b), TOBN(0x41cc0cc3, 0x0042fce6),
+     TOBN(0x0228ae75, 0x21cf742c), TOBN(0x56a1152a, 0xf4c9a1a9),
+     TOBN(0x5d8a3321, 0xfebd27dc), TOBN(0x89bce700, 0x7c525f7f),
+     TOBN(0xe8f815a9, 0x1c1039ee), TOBN(0x9f6db698, 0x62e86536),
+     TOBN(0x1ea6e7a6, 0x66fe804d), TOBN(0x652acc41, 0x261aea16),
+     TOBN(0xde28e5d8, 0xf9df596b), TOBN(0x18f453c1, 0x1553a545),
+     TOBN(0xa224f763, 0x84eeb5c8), TOBN(0x8ac452f5, 0x835ba87e),
+     TOBN(0x9b2b5939, 0xc5f4c054), TOBN(0xb2577943, 0x3ac1cdcc),
+     TOBN(0x1ba2cd0d, 0x772c60dc), TOBN(0x1fa52c43, 0xd7a9bd1c),
+     TOBN(0x2efd4f4a, 0x60444f34), TOBN(0x7d188c05, 0x2bdcfc9d),
+     TOBN(0x49ef6825, 0xe1913711), TOBN(0xbca95ded, 0x600d6c46),
+     TOBN(0x63916baa, 0xaf8d66d3), TOBN(0x04981202, 0x2dc837a8),
+     TOBN(0xb501e517, 0x0d3ae79d), TOBN(0x99ff7864, 0xb4edb859),
+     TOBN(0x5099edee, 0xaf4ec081), TOBN(0x89574889, 0x964f4052),
+     TOBN(0x1690fdb8, 0x52066d70), TOBN(0xb403207d, 0x671f4e7f),
+     TOBN(0x8ebc1d1b, 0xd7413111), TOBN(0x1432d7fe, 0xb4cfdf14),
+     TOBN(0x9277666a, 0x65ad5d0e), TOBN(0xbd5ae578, 0xa928e194),
+     TOBN(0x2f6c10d5, 0xb64962fb), TOBN(0xe3d756c0, 0x2e794187),
+     TOBN(0xf04fd82a, 0xd3e6349c), TOBN(0xde602dba, 0xcc7d39b6),
+     TOBN(0x0886e20a, 0x044e7deb), TOBN(0x6e30c75f, 0xe9ba917e),
+     TOBN(0x763961fc, 0x4a322ede), TOBN(0x6df4a3cb, 0x2324bb92),
+     TOBN(0x9fe82323, 0x8f2ac967), TOBN(0x3c372afe, 0x2345372a),
+     TOBN(0xbf7e9c55, 0x50b66fec), TOBN(0x5db7dd71, 0x0c065cfa),
+     TOBN(0x3525e310, 0x50d459ea), TOBN(0xad7abe5a, 0x8122941a),
+     TOBN(0xc7aeba80, 0x122d92fa), TOBN(0x066c3765, 0xefcc1c24),
+     TOBN(0xa6d767ca, 0x8ffd71b1), TOBN(0x4a75fab5, 0x9cc16dbc),
+     TOBN(0x9acf8b89, 0xbb58b63d), TOBN(0x226cdcd3, 0x6fc8c090),
+     TOBN(0x852965b7, 0xae7fbd0b), TOBN(0x4cadd176, 0xb8bfe65f),
+     TOBN(0x4ccc11d1, 0xcfa2ac11), TOBN(0x8abf7420, 0x800319ab),
+     TOBN(0x24ab82cb, 0x88bb3ef1), TOBN(0x4d3db003, 0x524c0ce1),
+     TOBN(0x38483641, 0x3a431e8c), TOBN(0xfc0c04a0, 0x792848ad),
+     TOBN(0x2fc52bb8, 0xa07701b0), TOBN(0xdfdced3d, 0xf29c72cb),
+     TOBN(0x677e3d84, 0x5280c2e0), TOBN(0x2dda1451, 0xe98cbec5),
+     TOBN(0xba28b181, 0xaec26be2), TOBN(0x16694717, 0x5ddea39b),
+     TOBN(0x911ec5f0, 0x4b9aa9b0), TOBN(0x24b9aaa0, 0x3594ae7d),
+     TOBN(0x0ccfa661, 0xc3c136a0), TOBN(0x5518964d, 0xb7474319),
+     TOBN(0xf0b0427b, 0x2175c3dc), TOBN(0x08db4cfc, 0x966b7bad),
+     TOBN(0x6f61428a, 0x5e888ad1), TOBN(0xfaa96176, 0x57b52d37),
+     TOBN(0xe834013b, 0x10aac879), TOBN(0x73397bb0, 0x95a62172),
+     TOBN(0x97806839, 0x33a244b2), TOBN(0x0ab3806c, 0xc3bec0d0),
+     TOBN(0x4fc7a859, 0x2a72512d), TOBN(0x96474939, 0x0a4228b9),
+     TOBN(0x8e5d79a8, 0x4de4b4a5), TOBN(0x5a60d1b0, 0x05d62667),
+     TOBN(0xd31be21d, 0x08d90c20), TOBN(0x3f7ed5f2, 0xcc14dbb1),
+     TOBN(0xdc8f58f9, 0xd7d431c4), TOBN(0x714f6dee, 0x82b5c63f),
+     TOBN(0x6b285466, 0x76d2de93), TOBN(0x3c2f5d8f, 0xc39dd98c),
+     TOBN(0x9bba0075, 0xea3760a2), TOBN(0x75e0389a, 0x2411742e),
+     TOBN(0x87d6715a, 0x7ffdb955), TOBN(0x702108fc, 0x9efb199d),
+     TOBN(0xf11db1f9, 0x6c010f8a), TOBN(0xf52b1e0f, 0x7eb6871b),
+     TOBN(0xc49c0dc7, 0x97c3ed9e), TOBN(0x18846f95, 0x77220a50),
+     TOBN(0xdb2273bc, 0x97afddcb), TOBN(0x5b9a16d6, 0xcc469f75),
+     TOBN(0xee364394, 0x3beedaf4), TOBN(0x825e01d6, 0x528a9239),
+     TOBN(0xb60ba965, 0xffd0f17c), TOBN(0xc00106b0, 0xb888384b),
+     TOBN(0x6e24322f, 0x31751f74), TOBN(0xfe4d074c, 0x1821d05a),
+     TOBN(0xf2493c73, 0xbf072932), TOBN(0xa797e208, 0x21089f21),
+     TOBN(0xf1b318af, 0x2988abcd), TOBN(0xf887558f, 0x8e7da518),
+     TOBN(0xb8b9939c, 0x97836b57), TOBN(0xf793e3b5, 0xc0a74cf3),
+     TOBN(0xe191008a, 0x37684170), TOBN(0x7708823b, 0x05cb453c),
+     TOBN(0xec221d40, 0x361beb2c), TOBN(0x0e5a6cce, 0xeb1b68f4),
+     TOBN(0x3dc535f0, 0x9644e937), TOBN(0xf506d720, 0xfda6c1b7),
+     TOBN(0xc78c0e0b, 0xf99437bd), TOBN(0xa920b4d3, 0xcc9e2b09),
+     TOBN(0x550965fe, 0xf089b0e0), TOBN(0xf9813492, 0x0109d910),
+     TOBN(0xd2496f20, 0x8c9d5d83), TOBN(0x751b6900, 0x3e3e661f),
+     TOBN(0x921edbde, 0x9e6ac190), TOBN(0x75891359, 0xf02d0e7a),
+     TOBN(0xdeb0f83b, 0x1c4da092), TOBN(0x7b427915, 0x4feb2375),
+     TOBN(0x24637c72, 0x7c3a85c3), TOBN(0xbbfabf86, 0x3f214ac3),
+     TOBN(0xe8765740, 0xae22fbfa), TOBN(0x3a09fab0, 0x5f14045a),
+     TOBN(0x546d574f, 0x8190dd41), TOBN(0xdfcf0b73, 0x48b5a39f),
+     TOBN(0xf26c69de, 0x74097b2d), TOBN(0x37aa27ff, 0x3a7e3e90),
+     TOBN(0x0942447b, 0x83bbe3df), TOBN(0xe779fe20, 0x9ab378aa),
+     TOBN(0xad18ad23, 0x91e2264f), TOBN(0xe1dad926, 0xaaabd6d1),
+     TOBN(0x9de0aa4f, 0x5db5e5c8), TOBN(0x45c3d73e, 0xdb67e44e),
+     TOBN(0x440862a1, 0x5cd83936), TOBN(0x9f2b9a88, 0xffce9a79),
+     TOBN(0x63299069, 0x76cc6265), TOBN(0xf596a67f, 0x7a8830f5),
+     TOBN(0x7051c842, 0x8d1d8284), TOBN(0xa00d05a8, 0x3e5561fc),
+     TOBN(0x15ce42d5, 0x7a34d5bc), TOBN(0x4d9b3f5f, 0xb0e37254),
+     TOBN(0x26e84094, 0x38841ab4), TOBN(0xa7afd35d, 0x9a8ede27),
+     TOBN(0x4e8bcdb8, 0x14835fa9), TOBN(0x85d04ddc, 0x79493e39),
+     TOBN(0xbfa8fa79, 0xdf8f65ae), TOBN(0xe31d759a, 0xda6c7c62),
+     TOBN(0x76f27e70, 0x7600aea7), TOBN(0xbec79f15, 0xd4d9acf5),
+     TOBN(0x0f10bd0f, 0x5eae2ff6), TOBN(0x96c9eef1, 0x7116a0c4),
+     TOBN(0x30add2cc, 0x0cb6f595), TOBN(0x0c70b548, 0x943efe90),
+     TOBN(0x2ce8026f, 0x0a05f4a8), TOBN(0xaa3da153, 0xb7c53c00),
+     TOBN(0xcc46bf67, 0x8e385a26), TOBN(0x64bcf06e, 0x99bae0f6),
+     TOBN(0x49480a36, 0x035dcb4c), TOBN(0x2cc1a299, 0xe3cbae58),
+     TOBN(0x849f8633, 0xb5480cb2), TOBN(0x1d8fa56d, 0x5607d83e),
+     TOBN(0xcc3f0eee, 0xcea9f22b), TOBN(0x7d5ece29, 0x1a23f3da),
+     TOBN(0xc6f0a006, 0x8ae66f00), TOBN(0x2620157e, 0x78d648f0),
+     TOBN(0xfc717762, 0x40d2880f), TOBN(0x2e0e293c, 0xbe105017),
+     TOBN(0xb320f214, 0x854116f4), TOBN(0x5e4fa700, 0x2d5cd4ec),
+     TOBN(0x83fa0a23, 0xdffc1c55), TOBN(0x18fcb8d2, 0xc9a9ca15),
+     TOBN(0x9e9baccd, 0xd0ac70fe), TOBN(0x8ba02fb7, 0x27fe06ce),
+     TOBN(0x2708804c, 0x3868fdd4), TOBN(0x355eaf0c, 0x9ba83df4),
+     TOBN(0x014089ba, 0xbe43993a), TOBN(0xc8b59eda, 0x469cccd6),
+     TOBN(0x77c94507, 0xb893a5a7), TOBN(0x0dffd39b, 0x8e517fd5),
+     TOBN(0x71b6edb7, 0x13dbeadf), TOBN(0x1617b77f, 0xfea2d0cb),
+     TOBN(0xf7454736, 0x48ff989f), TOBN(0x27357890, 0xb618bfa3),
+     TOBN(0xf08c70ac, 0xa7181331), TOBN(0x33b6cfe5, 0xb8bc036d),
+     TOBN(0x75ed10f9, 0x7163f883), TOBN(0x979875fc, 0x47d1cbbd),
+     TOBN(0x6644b234, 0x7ad23938), TOBN(0x0f09e7f1, 0xd82e2bc8),
+     TOBN(0x1e6c512b, 0x5588a39d), TOBN(0xb44e6694, 0xce8eae85),
+     TOBN(0x107336e2, 0xf392a4c7), TOBN(0x2619b284, 0xdbcd7b43),
+     TOBN(0x7b7ec516, 0xb7f476a5), TOBN(0x0de74ef3, 0x43081af2),
+     TOBN(0x93d08bc6, 0xd8d54faf), TOBN(0x88d343a7, 0xf2ae6c9f),
+     TOBN(0x7cdb9003, 0xbc147c27), TOBN(0xd740b19d, 0x69248562),
+     TOBN(0x7f3c48bb, 0x464b3b60), TOBN(0xfc4cd7e9, 0xc91d92c1),
+     TOBN(0x8172af80, 0xd7420ac9), TOBN(0x66907b77, 0xb9a50be9),
+     TOBN(0xed99fea1, 0x9ec8e974), TOBN(0x624a8c94, 0x54f39b1c),
+     TOBN(0x9c4d608a, 0xce9798d1), TOBN(0x81e1652e, 0xa4812277),
+     TOBN(0xa2cf7509, 0xf58b7db8), TOBN(0xef2cd193, 0x745e450e),
+     TOBN(0x48ee8431, 0x9d9da493), TOBN(0x7b471698, 0xb8ce96fd),
+     TOBN(0x14dbaff8, 0xe7553998), TOBN(0xb0b14e4a, 0x822de823),
+     TOBN(0x11032354, 0x429d7c51), TOBN(0xc1bb3327, 0xd572d20e),
+     TOBN(0xff473811, 0x6a9c189e), TOBN(0x7cf2354e, 0x9c7b3b83),
+     TOBN(0x29681ff6, 0x7662df92), TOBN(0x09296227, 0x51c297d1),
+     TOBN(0x1b800b34, 0x5e3da635), TOBN(0xb5fd32d2, 0x745116e4),
+     TOBN(0xdae17a1f, 0x2565abb0), TOBN(0x4f39d3d7, 0x1fec80c2),
+     TOBN(0xb4a19cc2, 0x290c2f4b), TOBN(0x1a1b049e, 0x0b6e5ae0),
+     TOBN(0x41be6e92, 0x6a823b6b), TOBN(0x35648873, 0x969649ce),
+     TOBN(0xe85f995e, 0x2a8ed3d7), TOBN(0x9dc712e8, 0x2f319e47),
+     TOBN(0xc4402eff, 0x536d98a2), TOBN(0xca61e310, 0x37521e35),
+     TOBN(0xfed39621, 0xc3196672), TOBN(0x29e7743f, 0xff17e8a7),
+     TOBN(0x47eca488, 0x412a7c49), TOBN(0xf0114513, 0x33a2a6da),
+    },
+    {
+     TOBN(0x5675a12a, 0xe6880b5f), TOBN(0x9ba1e92c, 0xe2606d25),
+     TOBN(0xb012facb, 0xeb3b2125), TOBN(0x3c50fdfb, 0xc37b0099),
+     TOBN(0xc9ce461c, 0x9ce223e9), TOBN(0xcb90bdd6, 0xeefbd8ac),
+     TOBN(0xf657e5a4, 0xc631ea8e), TOBN(0x6584520b, 0x38a83ff6),
+     TOBN(0xd959f317, 0x635abcf0), TOBN(0xa516a43f, 0x99e17618),
+     TOBN(0xed90ccf2, 0xce3bd99b), TOBN(0x2fc6d460, 0xa9fb3290),
+     TOBN(0xb61ebe09, 0x0cde4302), TOBN(0x5a3b061f, 0xf908003b),
+     TOBN(0xf51bb736, 0xf60f5787), TOBN(0x1717f6e9, 0x057efc2f),
+     TOBN(0x565acf93, 0x1ca260ef), TOBN(0x7d6e797d, 0xf1811d23),
+     TOBN(0xe63c6920, 0x783e42c8), TOBN(0xdc9dbce8, 0x8dcb5158),
+     TOBN(0x1426dc7a, 0xc8e39022), TOBN(0xf3037f34, 0x30ebfe47),
+     TOBN(0x75aa6845, 0xf87d6395), TOBN(0xbf792fd5, 0x61f53539),
+     TOBN(0xa8bf2172, 0x6ddc3d83), TOBN(0xf68deb6e, 0xd88207bb),
+     TOBN(0xa8eae2eb, 0xcd03bd7e), TOBN(0x64c7f57e, 0x951f59a4),
+     TOBN(0x8badb223, 0xa1786d57), TOBN(0x2e7fda60, 0x71182790),
+     TOBN(0x9dc90e36, 0x9a5a9457), TOBN(0x6eca838b, 0xf4b07e07),
+     TOBN(0xad2e235b, 0x03264871), TOBN(0xb4c56243, 0xb8b933de),
+     TOBN(0xd9c2bdda, 0x91354c8e), TOBN(0x97d743ff, 0x6a73fc76),
+     TOBN(0xbed4109d, 0xce88013e), TOBN(0xa2428275, 0xf3b3bf4f),
+     TOBN(0x900d3560, 0x011e761c), TOBN(0x34925d7d, 0xe24fd6c2),
+     TOBN(0x08b966ca, 0xa8198235), TOBN(0x355d098c, 0xed2d764a),
+     TOBN(0xfac27f7c, 0xa3d63f3a), TOBN(0x3e553f6c, 0xd3edc140),
+     TOBN(0x64d72c7f, 0x11ff4334), TOBN(0x48735aab, 0xbc62cb57),
+     TOBN(0xcf064294, 0xeba21082), TOBN(0xc1f9e456, 0xbb8d96fd),
+     TOBN(0x1d24bdbc, 0x293cd945), TOBN(0x76985bcb, 0xea254e36),
+     TOBN(0x3df2cb6a, 0x876fb485), TOBN(0x0176969f, 0xcd1f673d),
+     TOBN(0x8b41cacb, 0x642133a7), TOBN(0x31ea88f8, 0x373880e2),
+     TOBN(0xccf1ff85, 0xb3b1463f), TOBN(0x88fffa15, 0xaca74a27),
+     TOBN(0x9a4b9b92, 0x167cdd1f), TOBN(0xa9118fc0, 0xf879b894),
+     TOBN(0xf6e73387, 0xc55479f5), TOBN(0xfadf82ed, 0xc626d292),
+     TOBN(0xa03bb761, 0x56e80e6a), TOBN(0x59a783f9, 0xf27555d1),
+     TOBN(0x027d63b6, 0x3d087e43), TOBN(0x29f9ff32, 0x02fdeded),
+     TOBN(0x88a9173d, 0x371d0ec5), TOBN(0x04ac4d0d, 0x08c0227a),
+     TOBN(0x00213011, 0x9c7ec715), TOBN(0x0d2b7c76, 0xd9d6b472),
+     TOBN(0xe678d53a, 0x5050bdff), TOBN(0x8f929d57, 0x65a5fcd5),
+     TOBN(0x0793920b, 0x1dc3e712), TOBN(0x9a6a690f, 0x4b073699),
+     TOBN(0x329d9a81, 0x758bdc9d), TOBN(0xebbaadd9, 0x7d867b66),
+     TOBN(0x0d7e6b19, 0xe6025f68), TOBN(0x50184374, 0xc53dce26),
+     TOBN(0x298cb00f, 0x3ed13916), TOBN(0x835fe31e, 0xf5d45b26),
+     TOBN(0x373a9c49, 0xf5a7fb7a), TOBN(0x59ed7e23, 0x34d3d8a8),
+     TOBN(0x1a8dfe33, 0x3baf6fa3), TOBN(0x926ccce7, 0xda53714f),
+     TOBN(0xda4feaed, 0x18ef6fe2), TOBN(0xeddaf090, 0xc3ca5cdd),
+     TOBN(0xc39c2046, 0xbfe06d45), TOBN(0x1d9e889e, 0x0d7f549f),
+     TOBN(0x209ace73, 0x8d537d0a), TOBN(0x6f182c88, 0x0e31e1ce),
+     TOBN(0x865e0761, 0x1b8c82e2), TOBN(0xcf11bcb9, 0xa659f2ab),
+     TOBN(0x1804bbeb, 0x7c868143), TOBN(0x2fa89a0e, 0x453e36eb),
+     TOBN(0x42d69d8f, 0x2e17bad1), TOBN(0xe7fcea6f, 0xdc2ec741),
+     TOBN(0xe7f19b45, 0x379ceb37), TOBN(0x84f0bd89, 0x49bb35a0),
+     TOBN(0xa8a50678, 0x5264b33d), TOBN(0x8cfae763, 0xab1c9e26),
+     TOBN(0x1e837dc3, 0xff9b931a), TOBN(0x76164be8, 0x796ac029),
+     TOBN(0x26a8bb2b, 0x1266db27), TOBN(0xfba4ab83, 0x54822255),
+     TOBN(0x7a5adcfd, 0x38524458), TOBN(0xa056c882, 0x44ee212c),
+     TOBN(0xe8db6fee, 0x55018577), TOBN(0xf71256b6, 0x91955960),
+     TOBN(0xeb1c118e, 0x10abe8d8), TOBN(0x984efc9f, 0xd45a8426),
+     TOBN(0x4e1b323a, 0x00f2c6ed), TOBN(0x1759a7af, 0x331baae2),
+     TOBN(0xf1587189, 0x2e00ba6c), TOBN(0xbd8a877e, 0xbb385d39),
+     TOBN(0x440d1eae, 0x57d6c1ae), TOBN(0x092abdef, 0xa957dc67),
+     TOBN(0x1065cbc6, 0x74554b3f), TOBN(0x67062382, 0x710566c7),
+     TOBN(0xd327679d, 0x6d04ae2b), TOBN(0x11507b00, 0xb0340551),
+     TOBN(0x2e571583, 0xa2f52d80), TOBN(0x673628f4, 0xe8578507),
+     TOBN(0xecb8f92d, 0x0cf4efe5), TOBN(0x88c47214, 0x960e2d22),
+     TOBN(0xca9549ef, 0x6059f079), TOBN(0xd0a3774a, 0x7016da7c),
+     TOBN(0xd51c95f6, 0x1d001cab), TOBN(0x2d744def, 0xa3feeec1),
+     TOBN(0xb7c20cc2, 0x0afedf2b), TOBN(0xbf16c5f1, 0x71d144a5),
+     TOBN(0x00384727, 0x3dc0d12e), TOBN(0xaa95f450, 0xb01cc80f),
+     TOBN(0x19be3106, 0xa6f8e927), TOBN(0x6d6e10aa, 0x0417ba8b),
+     TOBN(0x149f120c, 0x870e3491), TOBN(0x27380b41, 0x026dde94),
+     TOBN(0x97d00840, 0xf29b04e6), TOBN(0x21d5d7e3, 0x4bf9eb19),
+     TOBN(0xea1daad9, 0xd5327f05), TOBN(0xf1f45d94, 0x9c88c17c),
+     TOBN(0xc5f3dee2, 0x3f8ee0ab), TOBN(0x706b777c, 0x75238a56),
+     TOBN(0xf7aee379, 0xf834c60b), TOBN(0x5c24dae6, 0x13cfe17b),
+     TOBN(0x354c82e5, 0x8091804b), TOBN(0x0dec2fdf, 0x102a577b),
+     TOBN(0xbf3b7030, 0x5253f8fc), TOBN(0xe516fa69, 0xd913c01c),
+     TOBN(0x053afef4, 0xa105ba64), TOBN(0x91a1f36c, 0xc89c1e76),
+     TOBN(0x3375865c, 0x7e724e18), TOBN(0x43132144, 0x29327b2b),
+     TOBN(0x9cb2fc3b, 0x6f7bb24e), TOBN(0x20a6a16d, 0x6319e789),
+     TOBN(0x20bfbd77, 0x642c467a), TOBN(0x3452bb12, 0x259d50c8),
+     TOBN(0x0d3ba9c7, 0xec7ffab2), TOBN(0xbbdb5454, 0x3560e541),
+     TOBN(0xab1d6e22, 0xd63ba04b), TOBN(0xdf6f11d3, 0x7d24f015),
+     TOBN(0x7c4d61d2, 0xf3df15fa), TOBN(0xd5269f79, 0x40b3288c),
+     TOBN(0xf8516b9e, 0x0e7c7b6c), TOBN(0x48750d82, 0xc203dac8),
+     TOBN(0x89845d36, 0xa13d3083), TOBN(0xb3db3cfa, 0x280a131a),
+     TOBN(0x40045401, 0xfbf752e6), TOBN(0x0289f97b, 0x1432e856),
+     TOBN(0x41a9f371, 0x5fc1aa11), TOBN(0xe5c1e5a5, 0x8d464042),
+     TOBN(0xfbee2ea2, 0x589b71a7), TOBN(0xdd6ee5bd, 0x5de7056c),
+     TOBN(0xcf8a4541, 0x8fd6b6de), TOBN(0xb47831dc, 0xb15e33b1),
+     TOBN(0x126a2169, 0x2064321e), TOBN(0xa21d2d22, 0x6e517edd),
+     TOBN(0x1f8072be, 0x5ba5a30b), TOBN(0x24cca576, 0xc6a24b7d),
+     TOBN(0x57eab82f, 0x5c282027), TOBN(0x1620f5e6, 0x557344b3),
+     TOBN(0x59e852e0, 0x460b3385), TOBN(0xc906e3db, 0xf050816c),
+     TOBN(0xc031f8cf, 0x3eb398e8), TOBN(0x9c25b69b, 0x507ac07f),
+     TOBN(0x652baf2b, 0x9cf7bdbe), TOBN(0x06fedc53, 0x5ad91107),
+     TOBN(0xa8ca0be2, 0x4c4b12c5), TOBN(0x633292b6, 0x28762d5d),
+     TOBN(0xc04983f2, 0x827c0d5e), TOBN(0xcb6b867d, 0xc707ef03),
+     TOBN(0xa7fc0d5b, 0xb9ac1124), TOBN(0xa5ce085b, 0xaab7dcaf),
+     TOBN(0xb85e8f1c, 0x1cfda998), TOBN(0x8208df42, 0x27822503),
+     TOBN(0xeaa82320, 0xa8dd6d76), TOBN(0x7b2fb4ae, 0xad36eb73),
+     TOBN(0x24d73191, 0x97a7b040), TOBN(0xc3ff64ae, 0x4001e02f),
+     TOBN(0xd5d87157, 0x88799d94), TOBN(0x559142d0, 0x93ceb95a),
+     TOBN(0x798a453c, 0x59c3009a), TOBN(0x546b6fab, 0x7d6c83a2),
+     TOBN(0xe263b23a, 0x5c76029a), TOBN(0x85630532, 0x4ac62973),
+     TOBN(0x14ee0643, 0xecb007ac), TOBN(0xf9e06297, 0x7ca60905),
+     TOBN(0x21b2fb23, 0x92f1f170), TOBN(0x31c40918, 0x46528ab2),
+     TOBN(0x43b53242, 0x3395cfd2), TOBN(0x4042138f, 0x6d14fb40),
+     TOBN(0x80899c8c, 0x4464f342), TOBN(0x0f54c993, 0x084be305),
+     TOBN(0xfacecac3, 0xfbf84810), TOBN(0xa6585936, 0x8ae5244f),
+     TOBN(0xb467c3c0, 0x9a9f8d4a), TOBN(0x3e5f219c, 0xfd394895),
+     TOBN(0x39f0767a, 0x9bf85fa8), TOBN(0xd97cc55d, 0xd8ee6022),
+     TOBN(0xc480938f, 0xc83f86c4), TOBN(0x6479b8ef, 0xe43bfcc6),
+     TOBN(0x8e6f2e22, 0x38cabad7), TOBN(0x48e57fdd, 0x31f8c6aa),
+     TOBN(0x66dd6a77, 0xcfbbdcac), TOBN(0xc7d9950b, 0x50ece329),
+     TOBN(0x2e31f205, 0x0747a937), TOBN(0xc0f8a7e2, 0xa07acb8a),
+     TOBN(0x578477bd, 0x15eaa686), TOBN(0xd72fb935, 0xf2f58b50),
+     TOBN(0xe9fdbc6f, 0xd3a64d22), TOBN(0xa3e42674, 0x492dc89f),
+     TOBN(0x42410ffd, 0xa8fb7d24), TOBN(0x08a37dfd, 0x52676ed7),
+     TOBN(0x4607c41b, 0xcb5d6125), TOBN(0x7db48af8, 0x4001fa42),
+     TOBN(0xe2264eb1, 0x50cd30f0), TOBN(0xbb6fe952, 0xe215f8d7),
+     TOBN(0xf3ce2411, 0x97e3fe73), TOBN(0xe52e2179, 0x37f19247),
+     TOBN(0x9c7fc8c0, 0x20c233c1), TOBN(0x91c7e721, 0xb383b101),
+     TOBN(0x1163c472, 0xa7ac883f), TOBN(0xbe1c3b3a, 0x9d3b0f1e),
+     TOBN(0x07be716f, 0xa3536baf), TOBN(0x764d9f4e, 0x62e9c19a),
+     TOBN(0x15af3499, 0x8eaf19f4), TOBN(0x987a7c47, 0x38ea0ace),
+     TOBN(0xb03740b8, 0x4a1f0117), TOBN(0x5cd1164f, 0xfe098a9f),
+     TOBN(0xaf952cef, 0xc9d6fee5), TOBN(0x4e86dcbb, 0x3c0ad28b),
+     TOBN(0x81125450, 0x677b7a8f), TOBN(0xba889fce, 0xe69273d2),
+     TOBN(0x4a40a859, 0x582c5990), TOBN(0x836638b3, 0xf48934c3),
+     TOBN(0xe964e189, 0xf3596ba6), TOBN(0x2f417c0e, 0xde8b0754),
+     TOBN(0xd883169f, 0xd5f93f1b), TOBN(0x0318fe4e, 0xd45bb389),
+     TOBN(0xe2c998a1, 0xdb03273e), TOBN(0xc34f544d, 0x33ec151b),
+     TOBN(0xae0456b1, 0xeb92d963), TOBN(0xaab61ec4, 0x9738857f),
+     TOBN(0x4fb6a34e, 0xf71d9c39), TOBN(0xaa9dbd8c, 0xd816ec44),
+     TOBN(0xf6532e37, 0x5efdf950), TOBN(0x7151dc44, 0x67109c55),
+     TOBN(0xb18b586a, 0x3f4e322c), TOBN(0x27b30066, 0x3553a18b),
+     TOBN(0xbd31ea24, 0x1ae4cd85), TOBN(0xe8f88f4a, 0xa64de69a),
+     TOBN(0x8c946a97, 0x609c13bb), TOBN(0xbf8cc55a, 0x0eebd9f0),
+     TOBN(0x446aa2e4, 0x7a8892b9), TOBN(0x660c0a55, 0x65b98c31),
+     TOBN(0x568c56fc, 0xd3463522), TOBN(0xfa6bf3a6, 0xeb130aa5),
+     TOBN(0x16c1568b, 0x008dc0da), TOBN(0x9c4132cc, 0xfed70a87),
+     TOBN(0x3e983d09, 0xd497fdff), TOBN(0xd7a0e542, 0xf0ebe6b0),
+     TOBN(0x193a07e0, 0x68b542ca), TOBN(0x4909776b, 0x07c6ab4f),
+     TOBN(0x55b77ef4, 0x418acd7b), TOBN(0x64ba62d3, 0x47a77d32),
+     TOBN(0xaec1aa93, 0x2d1f562e), TOBN(0x3468725b, 0x10dc5999),
+     TOBN(0x422851b1, 0x6ff0d478), TOBN(0x15da8429, 0x8e7dddcc),
+     TOBN(0x38567920, 0xb8ac5238), TOBN(0xfd29eb4a, 0x2e3344d8),
+     TOBN(0x7b2af70c, 0x4fc636b5), TOBN(0x242acfc8, 0x879e7640),
+     TOBN(0x88e89786, 0xb5e25c7b), TOBN(0x85576b1b, 0x16ec1bfd),
+     TOBN(0xb31c8253, 0x1891e595), TOBN(0x14315dfe, 0xca5608a5),
+     TOBN(0xb9d61b76, 0xb0c14fd9), TOBN(0x5d5ad8a3, 0x734b6cab),
+     TOBN(0xc2ea321d, 0x44aee005), TOBN(0xd68abd2c, 0x147ed658),
+     TOBN(0x31152d60, 0x893db877), TOBN(0x4807ac46, 0x281487b6),
+     TOBN(0x58ebd15e, 0x65da04b5), TOBN(0xf0f74fd4, 0xb2f9d1fd),
+     TOBN(0x3d04aa65, 0x393c7d91), TOBN(0xb46fb59a, 0x8e7e6a2c),
+     TOBN(0x9236fdf1, 0xae1eed5d), TOBN(0x71936f56, 0x7810e2be),
+     TOBN(0xa1ead7d5, 0x6d9ff147), TOBN(0x32670ed8, 0x149a9b6d),
+     TOBN(0x12772fdd, 0xcb58ea59), TOBN(0xfce260b3, 0x9df52ddb),
+     TOBN(0x3221f2fb, 0xccab1e97), TOBN(0xf8ff7e37, 0x57762484),
+     TOBN(0xb0a31a1c, 0x855512cf), TOBN(0x293a819e, 0xd71d4c4e),
+     TOBN(0xc1ebc896, 0xcd6a900f), TOBN(0xc727a646, 0x9b9e0a4b),
+     TOBN(0x06124fc0, 0x0018f29f), TOBN(0x67bd8fed, 0x41b7730c),
+     TOBN(0xeeebf0f0, 0xc77be72e), TOBN(0x427fe6fe, 0x474d747a),
+     TOBN(0xa7fb9a4f, 0x932ccbf0), TOBN(0xabb9c85e, 0x5f3d489f),
+     TOBN(0xe7e4f956, 0xbdf26442), TOBN(0xd014848e, 0x38d17422),
+     TOBN(0xae37d855, 0xd3e9bff6), TOBN(0x88fbae1d, 0xca5aeb09),
+     TOBN(0x1a8a740b, 0xf025feaa), TOBN(0xc1a67821, 0xb9475ebb),
+     TOBN(0xb6cb6acc, 0xde2bf8a2), TOBN(0x9b2ab1ca, 0x66a0f14e),
+     TOBN(0xcbfbc068, 0x83b2ba59), TOBN(0x336ab62c, 0x68447934),
+     TOBN(0xd3a016a9, 0xf19719b8), TOBN(0x819a31bb, 0x0b5b9d6e),
+     TOBN(0x7b24be2b, 0x3e1c6c0b), TOBN(0x10834b4a, 0x013f6821),
+     TOBN(0xe5e5de27, 0x86f21d2c), TOBN(0x56b46a2d, 0xe9e35ad5),
+     TOBN(0xfc4e861d, 0xe2111e59), TOBN(0x7472ce5e, 0x6e37ca63),
+     TOBN(0xafab9a71, 0x27d2210e), TOBN(0x1644a0a6, 0x9ff6245a),
+     TOBN(0xee498acb, 0x8dbef51f), TOBN(0xd4c70da1, 0x2e9604d3),
+     TOBN(0xde487364, 0x6fecb64c), TOBN(0xa8fda1fd, 0xd15fb62f),
+     TOBN(0x97e2febe, 0x088de028), TOBN(0x4a769019, 0xecdce095),
+     TOBN(0x4cb6a338, 0x50a58ddb), TOBN(0x08df59d8, 0x17028d36),
+     TOBN(0xfe3a80ff, 0xb51722b7), TOBN(0xa3cc2fe2, 0x963c2383),
+     TOBN(0x40b2df49, 0x53cc5341), TOBN(0xf3e90d4c, 0xa3c4bf2f),
+     TOBN(0x3f25c5ec, 0x20f02731), TOBN(0xd84f5b5a, 0x69065d9a),
+     TOBN(0x156d350e, 0x129921be), TOBN(0xe98787cc, 0x1b116922),
+     TOBN(0xba5f9b82, 0x39e77b13), TOBN(0xee4d79f5, 0x044449a5),
+     TOBN(0xb54b7388, 0xdd6d852d), TOBN(0xf7554c5c, 0xf9ca5fdf),
+     TOBN(0x864d1fbf, 0x51228a81), TOBN(0x721e1add, 0x9a80f90b),
+     TOBN(0x89d4e297, 0xad0efa62), TOBN(0x4e471a87, 0x6dba9404),
+     TOBN(0x9a38158b, 0x1c1008b0), TOBN(0x3dfe81a7, 0x95c47ec2),
+     TOBN(0xcb02ce9b, 0x28603026), TOBN(0xfd3207aa, 0x3bd357fc),
+     TOBN(0xb3807bdd, 0xf296f5f2), TOBN(0x7895918d, 0x23c2ea7e),
+     TOBN(0xdc0eb62f, 0x88feb3ba), TOBN(0x024dfd84, 0xbdd75674),
+     TOBN(0xe5bd3828, 0x0a1e0496), TOBN(0xb8b1cd86, 0x24c8f30c),
+     TOBN(0xb559e34d, 0x674d10cf), TOBN(0x6955bb69, 0x9f962ec5),
+     TOBN(0x8bf1ab6c, 0x542af42d), TOBN(0x3f2f33fa, 0xdfa61256),
+     TOBN(0x32140195, 0x73d1049e), TOBN(0xf5089278, 0xdfd7f39b),
+     TOBN(0xb42eb51c, 0xb4237be0), TOBN(0xdf747f44, 0x874d0e57),
+     TOBN(0xbe64bb22, 0x77b5d475), TOBN(0x2c3d5ecb, 0x28308634),
+     TOBN(0x936a2987, 0xcb999c46), TOBN(0x5a30ddfa, 0xe26489ea),
+     TOBN(0x8bfc782e, 0xc8eabf9c), TOBN(0xb9995bb0, 0x74c8c6e3),
+     TOBN(0x4f99c7ac, 0x391f5c5a), TOBN(0x67f4092b, 0x5270c4ad),
+     TOBN(0x6771a29d, 0xe6e8135e), TOBN(0x988dfb2b, 0x6c698cec),
+     TOBN(0x7818600f, 0x77812aa1), TOBN(0x04393c83, 0xfd98e1c1),
+     TOBN(0xe448232e, 0x864ef146), TOBN(0x9b70ecf4, 0xa465ab71),
+     TOBN(0x31df0531, 0xb13cc704), TOBN(0x401ae0b3, 0x16e48426),
+     TOBN(0xa8106630, 0x7fc514ed), TOBN(0xda798170, 0xde4b1614),
+     TOBN(0xde892efc, 0xc2c684fe), TOBN(0xd5205bc1, 0x05d64eff),
+     TOBN(0x84df4ead, 0xe1d59ba5), TOBN(0x65245ca1, 0x89bb2ea7),
+     TOBN(0x3de6ca34, 0x64edbf51), TOBN(0x115296e4, 0x56bcebf9),
+     TOBN(0x0851631f, 0x7fd52a3d), TOBN(0x9881db71, 0x949ad4be),
+     TOBN(0x4b2337dd, 0x88caf772), TOBN(0x02da59de, 0x33ec7979),
+     TOBN(0x2473c620, 0xafe840df), TOBN(0x2965ebff, 0xa92ef1d0),
+     TOBN(0x2452854f, 0x6fcd9651), TOBN(0x97092935, 0xbac2ed99),
+     TOBN(0xf0743ce7, 0x08242246), TOBN(0x76fdd82c, 0x6d1a8439),
+     TOBN(0x3627c890, 0x61079258), TOBN(0x312f98f1, 0x82b21983),
+     TOBN(0xd87dceec, 0xe9173891), TOBN(0xad16cfe0, 0xd7a30e32),
+     TOBN(0xc404a1a6, 0xc9c7efaf), TOBN(0x46e34671, 0xd6df357e),
+     TOBN(0x92fec7c4, 0x7a02aa32), TOBN(0x567fef7e, 0x5a6a7bb9),
+     TOBN(0x35fd570c, 0xa3f97b5d), TOBN(0x456bad8c, 0x4a4b0dfa),
+     TOBN(0x85a3f42c, 0xa677f090), TOBN(0x35060bb8, 0x22a68d53),
+     TOBN(0x1cea9d11, 0x53567530), TOBN(0xf2cbc8dd, 0x8169fbce),
+     TOBN(0xa3e1d52d, 0x86cde794), TOBN(0x72a258cb, 0xb3bdf344),
+     TOBN(0x2997cd59, 0x31b8614d), TOBN(0x31ce2ea4, 0x8164b632),
+     TOBN(0xe495e9b7, 0x0eba7545), TOBN(0xaad69130, 0xbc4403b5),
+     TOBN(0x37f6389b, 0x45760d9b), TOBN(0x00f4d58d, 0xb871b17d),
+     TOBN(0x91973d4d, 0x4aa359d7), TOBN(0x249f510c, 0xc8dd0582),
+     TOBN(0xef11ac87, 0x7608be27), TOBN(0xce116714, 0xd940b1c7),
+     TOBN(0xf34881f3, 0xfef20037), TOBN(0x26222472, 0x98203f4e),
+     TOBN(0x4c9e98ed, 0xe9363154), TOBN(0xa806b3a6, 0x03a8158d),
+     TOBN(0xdd974d66, 0x09d16ce3), TOBN(0xe1bcc513, 0x59ae977a),
+     TOBN(0x0e6201c7, 0x218464d6), TOBN(0x05789811, 0x9e35c7af),
+     TOBN(0xb1596f7a, 0x8b33a863), TOBN(0x8fa93aeb, 0x42bd8284),
+     TOBN(0xf197c202, 0x46e11559), TOBN(0x356b9c81, 0xadd27d86),
+     TOBN(0x3c4080fd, 0x1695cb70), TOBN(0xc10c28cc, 0x20f20318),
+     TOBN(0xe9d7ed93, 0xce1ffab9), TOBN(0xb23976b3, 0x4f9de9bd),
+     TOBN(0x9b1b81dd, 0x6d61a6f2), TOBN(0x7537d729, 0xf6318874),
+     TOBN(0xb75022f4, 0x20cee7ab), TOBN(0x425fddba, 0xaa430952),
+     TOBN(0x54c4306d, 0x1ccfb3fd), TOBN(0xf10a54f1, 0x46a30a37),
+     TOBN(0x2d332a29, 0x74fd4925), TOBN(0x8d2fa921, 0x1438feb2),
+     TOBN(0x46a9c6b5, 0xfbb41bd2), TOBN(0x87e98550, 0xd30c65fd),
+     TOBN(0xfbcb2ca6, 0x66cd9a20), TOBN(0xc176530e, 0x91719670),
+     TOBN(0xdd4a1a18, 0xcec38056), TOBN(0xe6ef1793, 0x75544998),
+     TOBN(0xf58f69cf, 0x30583fb6), TOBN(0x12197860, 0xaa76bf2b),
+     TOBN(0x717813e5, 0x3bb686fc), TOBN(0x9beeb1ae, 0x0f06c403),
+     TOBN(0xd83416ee, 0x2782dc86), TOBN(0x5fc89c01, 0xb5500ccc),
+     TOBN(0x063aee25, 0x8e110ed3), TOBN(0x1a87377c, 0x45963073),
+     TOBN(0x86944f59, 0x5110634f), TOBN(0x50659ae1, 0x0ba76459),
+     TOBN(0xa00e48ff, 0xde9eb40d), TOBN(0x49235afa, 0xfe5b118c),
+     TOBN(0x81705008, 0xc425ee38), TOBN(0x3c01abc8, 0x2d5f2f92),
+     TOBN(0x4a21bc95, 0x6fdf148b), TOBN(0xea5cc30e, 0x0b7e6871),
+     TOBN(0x90b4abb6, 0x11713844), TOBN(0x3b7d734f, 0xf2001af9),
+     TOBN(0xfc616b89, 0x782b2020), TOBN(0x68b3935c, 0xd8b0e02d),
+     TOBN(0x54cf5b8c, 0x1cbb2de4), TOBN(0x42b0432a, 0xa7c0f7ac),
+     TOBN(0xa04e06ef, 0xbdffae5e), TOBN(0x36cac28e, 0xd4b636ee),
+     TOBN(0x08a06b2f, 0xc3a98127), TOBN(0x1ef0b57b, 0x290c5385),
+     TOBN(0x14e184b8, 0x27154c46), TOBN(0xa5dd3444, 0x60910b3e),
+     TOBN(0xd0008ac4, 0x3c67a74b), TOBN(0x2649cba4, 0xefed9fd1),
+     TOBN(0x26bc537a, 0xf0c1bb4f), TOBN(0x37f376ff, 0xd06b90f5),
+     TOBN(0x4d48d994, 0xbe7c89cf), TOBN(0x511c2158, 0x8d572003),
+     TOBN(0xc26fbac1, 0x088dda1e), TOBN(0xc3d55189, 0x7ad4934c),
+     TOBN(0x5233c176, 0x85dcaf7c), TOBN(0xec3a8a29, 0xa88b473d),
+    },
+    {
+     TOBN(0xfd96667a, 0xb1f0c175), TOBN(0xa256a611, 0x2ab99e7d),
+     TOBN(0xff07c1ea, 0x05e43f9d), TOBN(0x305700bc, 0x7e1c9cd6),
+     TOBN(0x3f1e2546, 0x2b2887a3), TOBN(0xdd782f49, 0xc772fd14),
+     TOBN(0x9125f996, 0x38584057), TOBN(0x19fd0396, 0x16a02cf9),
+     TOBN(0xa8d62bd3, 0x4c58174d), TOBN(0x872251d3, 0xa900551f),
+     TOBN(0x06f5862d, 0xf12802c3), TOBN(0x5d93c48a, 0xdd925555),
+     TOBN(0xc39b67d5, 0xbd6006f8), TOBN(0xea6f756b, 0xf96ccc67),
+     TOBN(0x140e853e, 0x543014db), TOBN(0x2bdc5674, 0xe9de42c0),
+     TOBN(0x01dfda7b, 0xe01c073d), TOBN(0x07a6bb65, 0xff9e1234),
+     TOBN(0x2a4f7f18, 0x622cee4e), TOBN(0xdf4cead8, 0x50f0a3a7),
+     TOBN(0x152b3c8e, 0x1b8c2903), TOBN(0x9e82e999, 0x5f2a89b3),
+     TOBN(0x0e6cfa7e, 0x68ce7a3c), TOBN(0xebb34d90, 0x0ca0464c),
+     TOBN(0xfa1a58fa, 0xeda49f74), TOBN(0xddb89957, 0x0e4545a3),
+     TOBN(0xd3576489, 0xc74c07e4), TOBN(0x64e4b39e, 0xb59b1008),
+     TOBN(0x3b090340, 0xf66b546c), TOBN(0x0e0f4013, 0xcdeb912f),
+     TOBN(0xbb00b46c, 0x01e55cca), TOBN(0x55b61b34, 0x99ad0768),
+     TOBN(0xb06b71fc, 0xe8bbda5b), TOBN(0x8de64d84, 0xa24b0a63),
+     TOBN(0xb73dc262, 0xb5d4603f), TOBN(0x5d5fa964, 0x1965a916),
+     TOBN(0xb48a4053, 0x3bc98966), TOBN(0xaa871863, 0x6f564743),
+     TOBN(0x88b00822, 0xe76a6a3e), TOBN(0x58c9e92e, 0xb38d9e0d),
+     TOBN(0xc0d22337, 0xe989963e), TOBN(0x2c4831ce, 0xd3778d5a),
+     TOBN(0xd775c6a5, 0xee8c4178), TOBN(0xe2391654, 0x9d0c2894),
+     TOBN(0xf7d4fe86, 0x5d0eb314), TOBN(0x42801b8f, 0x8b2290d3),
+     TOBN(0x73e9b332, 0xcdcefa78), TOBN(0xc0d169d9, 0x3e877fea),
+     TOBN(0x29c8138b, 0xffee23fa), TOBN(0xbff98230, 0xfb92e3b8),
+     TOBN(0x14077ad5, 0x8fa75007), TOBN(0x4d3a6e10, 0x88e61b81),
+     TOBN(0x218a867d, 0x3bcf733d), TOBN(0x20ff6566, 0x665e37fc),
+     TOBN(0xe39c0581, 0xda5cbf67), TOBN(0x4a6e1d7c, 0x8add8c4c),
+     TOBN(0xcab02370, 0x734a1327), TOBN(0xa1df7afc, 0x1951afa8),
+     TOBN(0x581cfbaf, 0x42638b8a), TOBN(0x39db6d2b, 0x2130eaa6),
+     TOBN(0x4bbc805b, 0xda2f91a5), TOBN(0x3dcb0a7e, 0xe569add8),
+     TOBN(0x724ab65a, 0xd721fa7d), TOBN(0xa5152b95, 0xf88f8008),
+     TOBN(0x7fe7f1b9, 0x281615ba), TOBN(0x419d1a53, 0x41d5aa0c),
+     TOBN(0xafc556dc, 0x9fb0917e), TOBN(0xab2a69f3, 0x616ce893),
+     TOBN(0xfb9a6eb1, 0xc0861e05), TOBN(0x0b74ae11, 0x5eb02b8f),
+     TOBN(0xccff0ad5, 0x3b1e44fe), TOBN(0x86dfe0e6, 0x88824f53),
+     TOBN(0xedf38dc4, 0x41177a46), TOBN(0xd9a955bb, 0x7f039a7b),
+     TOBN(0x4f152581, 0x4d8ae7c2), TOBN(0x063c9f83, 0x4f848819),
+     TOBN(0x54ea4526, 0x841e8783), TOBN(0xe86a4119, 0xaa5f2b32),
+     TOBN(0xb7529a3b, 0x19846dcf), TOBN(0x91356a07, 0x35689d70),
+     TOBN(0xbe66f5db, 0x8f049ef8), TOBN(0x0f5fd99e, 0xc38dd5ed),
+     TOBN(0x1896d52b, 0x1b4ae7a7), TOBN(0xf27c45c6, 0x480b1ebb),
+     TOBN(0xd88cff4c, 0x3fede5c1), TOBN(0x57d902c9, 0xda27560b),
+     TOBN(0x84aa7f07, 0x52d57deb), TOBN(0x8da4c7c8, 0x08bb6028),
+     TOBN(0x658f4dea, 0x8910763e), TOBN(0x6e5fcb48, 0x076a0f80),
+     TOBN(0x6a5447a4, 0xab65f9b9), TOBN(0xd7d863d4, 0xa75bb0c5),
+     TOBN(0x806c34a7, 0xe87e7916), TOBN(0x05391559, 0xcd961e88),
+     TOBN(0x5def2d88, 0x74fe6aeb), TOBN(0x8ac350b2, 0xf9226ca1),
+     TOBN(0xffa8a649, 0x12401813), TOBN(0xd6182762, 0x5337c55d),
+     TOBN(0xfce9d7ff, 0x3be902e3), TOBN(0xb3b275d1, 0xea0dd7a5),
+     TOBN(0x342620f4, 0x2cb48ac9), TOBN(0xc0369384, 0xa8b38a74),
+     TOBN(0x04b0ee6a, 0xc0695d3a), TOBN(0x4d025585, 0x94c5394d),
+     TOBN(0xff9635d0, 0x81443d16), TOBN(0x2342cbfa, 0xa6cc364b),
+     TOBN(0x63b0a032, 0x25bf8438), TOBN(0x6ccd3ce5, 0xa078d298),
+     TOBN(0xf93bd108, 0x91292fd3), TOBN(0xc887a31b, 0x14073286),
+     TOBN(0xeb1275bf, 0x9f62cd16), TOBN(0x0335bae3, 0x61578b46),
+     TOBN(0x810d5efd, 0x53348e4e), TOBN(0xf9cd822a, 0x63c74225),
+     TOBN(0x93d2e810, 0xa426bf44), TOBN(0x95a47a97, 0x019d36b3),
+     TOBN(0x1da421b9, 0xd5d1f840), TOBN(0xe5b8a55f, 0xd6c46e3c),
+     TOBN(0x2dd3a5e7, 0xc9244881), TOBN(0xd50f9cde, 0x70c1fd2f),
+     TOBN(0xbee2aca7, 0x614d9ff3), TOBN(0xd1f13b2c, 0x358f245a),
+     TOBN(0x9e92d83f, 0xc46f62ab), TOBN(0xc1dd32dd, 0x827d7374),
+     TOBN(0x1636d593, 0xc3e566e7), TOBN(0x81c2f4e7, 0x04ccb02b),
+     TOBN(0xb57782c6, 0xcd35b652), TOBN(0xad88787e, 0x88210d42),
+     TOBN(0x3ad52d72, 0xfbd7d35a), TOBN(0x4117f502, 0x37a2a095),
+     TOBN(0xed03d415, 0xd356b3b6), TOBN(0x135d5a8c, 0x15ca6087),
+     TOBN(0xfbaba41f, 0xef5dca2a), TOBN(0x660e5cd0, 0xafb4787d),
+     TOBN(0xe0e66378, 0xa55e9ef0), TOBN(0xf24513cf, 0x69939f56),
+     TOBN(0x0f38f09c, 0xab4f6bd9), TOBN(0xec3037b4, 0x922dcac1),
+     TOBN(0x706b201a, 0x08a1a51e), TOBN(0x15911351, 0x8ffff040),
+     TOBN(0x239d7b6a, 0xccf63d87), TOBN(0xeca37dc8, 0x5187f595),
+     TOBN(0x04ea79e4, 0xad5a0ab3), TOBN(0xcdd81522, 0xe9520e8f),
+     TOBN(0x7fe6b6aa, 0xc35e1020), TOBN(0x57b63c9e, 0x140ac884),
+     TOBN(0xc45c23fc, 0x33f19077), TOBN(0x468d2c36, 0xb71273c5),
+     TOBN(0xeb6839d6, 0xfc305ac2), TOBN(0xf6e310ff, 0x0183793a),
+     TOBN(0xbca206e4, 0x32da639d), TOBN(0x8eb5cac1, 0x8518e27e),
+     TOBN(0xfeed0feb, 0x66ed96f9), TOBN(0x1632632e, 0xecc3a8dc),
+     TOBN(0x90449363, 0x1455c8ae), TOBN(0x8d7619d4, 0x0aeada65),
+     TOBN(0x2f2fa898, 0x9f630ee9), TOBN(0xd78caf0c, 0x370db87c),
+     TOBN(0x46fa0fc9, 0xc45898cf), TOBN(0xa509cc3e, 0x2d84244f),
+     TOBN(0xbdbea4b4, 0xa5b099aa), TOBN(0x8e8fe284, 0x7592587b),
+     TOBN(0x0226d387, 0x42000897), TOBN(0xb6780551, 0x36db5cd9),
+     TOBN(0xd8fe5eb1, 0xca64f047), TOBN(0x6f21474b, 0xb77cf8cb),
+     TOBN(0xab8fcae7, 0xee45ae34), TOBN(0x73eaf9eb, 0x1f19cd67),
+     TOBN(0x5bb96415, 0xee4df6ce), TOBN(0xd1e27bcf, 0xa3ae4cf3),
+     TOBN(0x9bf7ace3, 0xc7f1868e), TOBN(0xe821aa8b, 0x82091dca),
+     TOBN(0xf732e6bc, 0xd381b6c4), TOBN(0x5feda346, 0xdd01864f),
+     TOBN(0x0933b92c, 0xb6387846), TOBN(0xbf1f1e83, 0xa0028029),
+     TOBN(0x0848bf8c, 0xa3e38124), TOBN(0xfe295fdf, 0x208fda8f),
+     TOBN(0x73379239, 0x8913a1c4), TOBN(0x59354b24, 0x7e78564e),
+     TOBN(0x042b7529, 0x32dcafbc), TOBN(0x752173d3, 0xfa93c5c7),
+     TOBN(0x6ffd4590, 0x9737135a), TOBN(0x249712b0, 0x0f983005),
+     TOBN(0xdbba2874, 0x1f25da8b), TOBN(0x14027f11, 0x097ba4a9),
+     TOBN(0xe429b3c7, 0x34b8e4a2), TOBN(0xd66a43e3, 0x056b4afc),
+     TOBN(0x158644ad, 0x2ac351e6), TOBN(0xff4aecd9, 0x164bc6cc),
+     TOBN(0xbb5b0c87, 0xf6c615ee), TOBN(0xc497d8ee, 0xd7679b1b),
+     TOBN(0xf666c625, 0xf1c6e97a), TOBN(0xe89f84b2, 0xc73a277f),
+     TOBN(0x2403d513, 0x746af4c0), TOBN(0xe6858fdf, 0xb7101feb),
+     TOBN(0x1a42c51b, 0x84f1dcb7), TOBN(0xc57f12e0, 0x8202bc04),
+     TOBN(0xf8326a93, 0x754df5ae), TOBN(0x3d3daf04, 0x81a46aef),
+     TOBN(0x8bb8c276, 0x01232d03), TOBN(0xd446c82e, 0xfb371cf1),
+     TOBN(0xe5e8b639, 0xefa495f4), TOBN(0x51a7b34a, 0x477e6493),
+     TOBN(0xffba5466, 0x824f2b6e), TOBN(0xcc67ddad, 0xf0eaa6a9),
+     TOBN(0xcf0f8ce1, 0xfee19b24), TOBN(0x34309127, 0x83b3df41),
+     TOBN(0xc8b13e8c, 0x9719a6cd), TOBN(0xb408e505, 0x619d5c33),
+     TOBN(0x8c1b831b, 0xa3158864), TOBN(0x506b3c16, 0x0b3d02bb),
+     TOBN(0xf23846bc, 0xbf11ff8d), TOBN(0xf0f043e8, 0x16e0328a),
+     TOBN(0x30b7b9cd, 0x65986a7a), TOBN(0x0951b102, 0x21b660cd),
+     TOBN(0x72a26c5f, 0x52bf29a5), TOBN(0xb513d669, 0xb6534592),
+     TOBN(0xb8ac15ad, 0x578195ea), TOBN(0xd6ed33ea, 0xc0785f88),
+     TOBN(0x39e23dbf, 0xb9e33946), TOBN(0xeadb2453, 0xf43e88eb),
+     TOBN(0x6d82fefa, 0x2746c34b), TOBN(0xe9172aa0, 0xcc542b54),
+     TOBN(0x8af6b819, 0xecb50699), TOBN(0x4af76939, 0x1c1d0af9),
+     TOBN(0x5a7dbbbe, 0x99dddb1a), TOBN(0x97b0a3aa, 0x891ea41d),
+     TOBN(0x32b457e6, 0x6e35ea4f), TOBN(0xe2a21c2a, 0x9d77b900),
+     TOBN(0xb18718d6, 0x2ac991cf), TOBN(0xc4416237, 0x740743cd),
+     TOBN(0xcc3f76b6, 0x6a05ab55), TOBN(0x2ab4b29e, 0x98091425),
+     TOBN(0xbf373ad1, 0xb6478fc8), TOBN(0x8a1a9489, 0x178b5844),
+     TOBN(0xb5295edf, 0x09daf4be), TOBN(0x07fbb119, 0x4ed54766),
+     TOBN(0x6e44367b, 0x7d0b9d8f), TOBN(0x6dc4d8f6, 0xedb96a10),
+     TOBN(0x2ba69106, 0x37fc19a3), TOBN(0x522eba39, 0x0b138296),
+     TOBN(0x751544c7, 0xfda58cf3), TOBN(0xaba6fe16, 0x0ba33938),
+     TOBN(0x48e085be, 0x94dac7d6), TOBN(0x06c87014, 0x19f99faa),
+     TOBN(0x33b9a8d6, 0x1a587f89), TOBN(0xdae382ca, 0x3fd8d8fe),
+     TOBN(0xb5b383c6, 0x150b0fcd), TOBN(0xf948da80, 0xed9b0f4c),
+     TOBN(0xcf075225, 0xccd05413), TOBN(0x3f31b12c, 0x4f62be64),
+     TOBN(0x23b21fc8, 0x368c17f6), TOBN(0x423d5369, 0x400bc690),
+     TOBN(0x5335dd1e, 0xdeac140e), TOBN(0xe631c249, 0x9493ad61),
+     TOBN(0xc274c695, 0x32fe490a), TOBN(0x42bcb4e1, 0x6d8ebd70),
+     TOBN(0x69059e1e, 0x65d7a1d0), TOBN(0xf36dfe2f, 0x29fdd109),
+     TOBN(0xacfea1ec, 0x0c4e6370), TOBN(0x97e7f722, 0x7a935ff4),
+     TOBN(0x83e7c7c3, 0xf8006bbd), TOBN(0x87a8b84d, 0x78e6792c),
+     TOBN(0x5cbe4883, 0x94d3d60f), TOBN(0x6eba464d, 0x91cbc054),
+     TOBN(0xf9c880d0, 0x021c38fa), TOBN(0x6200faf1, 0x21af4942),
+     TOBN(0xd5b2b12d, 0x5f03e261), TOBN(0x1659a0ac, 0xf3ea3e07),
+     TOBN(0x8008f18d, 0x836757a8), TOBN(0xfb2f467b, 0x75a8f8e0),
+     TOBN(0x9a6183c7, 0x9c9b00cc), TOBN(0x82ca07e3, 0x3bf842b0),
+     TOBN(0xe7089191, 0xee1f83d9), TOBN(0xc41ecde4, 0x2d0cd2da),
+     TOBN(0x0ce421b0, 0x4d1feacd), TOBN(0xe80a1395, 0x431c53f1),
+     TOBN(0xae9b2018, 0xe6bfccf5), TOBN(0xdf9f86ad, 0x8b359c3b),
+     TOBN(0x9887e28f, 0xb6170a5f), TOBN(0xf5b85d21, 0xf3c0c30c),
+     TOBN(0x30861cf8, 0x632af7a4), TOBN(0x2fb670ad, 0xbb4ec123),
+     TOBN(0x0668b84c, 0x3c425976), TOBN(0x55c21b4e, 0x02883af7),
+     TOBN(0x0fad58b5, 0xf8698d29), TOBN(0xef210770, 0x68b671c5),
+     TOBN(0x534d510a, 0x23f232b8), TOBN(0xdb66fec1, 0x49c99708),
+     TOBN(0xf1a6f3e7, 0x6d54721b), TOBN(0x8d37ab64, 0x4480f858),
+     TOBN(0x7fcfca6c, 0xb0f7f354), TOBN(0x58c7ff5f, 0x95bfd318),
+     TOBN(0x3048e9af, 0x903f9d91), TOBN(0xe480bc0e, 0x75357af0),
+     TOBN(0x4f915e1c, 0xa5a1162e), TOBN(0xdd539c21, 0x37efa40c),
+     TOBN(0x61a45c53, 0x789201c2), TOBN(0x1bc2333d, 0xe7890746),
+     TOBN(0xeed38f50, 0xbbed8f77), TOBN(0xc1e93732, 0x178501a0),
+     TOBN(0xfed5b1d1, 0xa8fb8623), TOBN(0xa3be3e2c, 0xdc3e1148),
+     TOBN(0x62fc1633, 0xa71a390a), TOBN(0x4be2868e, 0x2891c4c3),
+     TOBN(0x6573fe49, 0x2a0c3c23), TOBN(0x182d0bd4, 0xde1589d0),
+     TOBN(0x17c6a780, 0x5a7aa63d), TOBN(0x9d84cfa8, 0x12543191),
+     TOBN(0xcdb22db7, 0x950c85c9), TOBN(0xd03589e0, 0x119010c4),
+     TOBN(0xbcd02e8a, 0x8220dee8), TOBN(0xbd4d1f2a, 0x705632fd),
+     TOBN(0x00119bfd, 0x22f8e30b), TOBN(0x06c6e73e, 0x6eb97c32),
+     TOBN(0xa26f0a6a, 0x35abff53), TOBN(0x7d79a89f, 0x8564c37d),
+     TOBN(0x0347bb17, 0x1b207495), TOBN(0x1baf90e9, 0xb5c8253a),
+     TOBN(0x01059b5f, 0x37affc96), TOBN(0xbe76c578, 0xffee0a60),
+     TOBN(0x45d7291b, 0x75d6b83c), TOBN(0x212ff131, 0xe0b58129),
+     TOBN(0x4acc5748, 0xaa5d46ed), TOBN(0x9fc557d9, 0x9193931b),
+     TOBN(0x17568fcf, 0xda4eba9b), TOBN(0x2cf3690c, 0xa0edc975),
+     TOBN(0x0e8b0e7e, 0x953df6fd), TOBN(0x38ea7cea, 0x62036a87),
+     TOBN(0x57e01428, 0x655c3685), TOBN(0xaedfee73, 0xc39d8a43),
+     TOBN(0xed7f6598, 0x5fb27e0a), TOBN(0x524c3653, 0x946888e0),
+     TOBN(0xd84a299b, 0xe949b72f), TOBN(0x76c1397a, 0xb0c61ea4),
+     TOBN(0xfd9f7ed0, 0x1afe465a), TOBN(0x832c69ad, 0xdbbaf852),
+     TOBN(0xcd888c22, 0x03713338), TOBN(0x4e1fe026, 0xe3306617),
+     TOBN(0xa87adf86, 0x23521b97), TOBN(0x673d3625, 0xf9fbb2a0),
+     TOBN(0xf29a1413, 0x5d8f5b80), TOBN(0x6e9be0c4, 0xd3526501),
+     TOBN(0x6129f861, 0xe8bfd84d), TOBN(0x1df491d6, 0x77e35a47),
+     TOBN(0xefe0e9a9, 0xa84a82cb), TOBN(0x972bc3bc, 0x6d949612),
+     TOBN(0x8d7795f5, 0x3a766eca), TOBN(0x6119383f, 0x12fcc6d4),
+     TOBN(0xa66d9836, 0xc95f0e21), TOBN(0x77a0aa0a, 0x684e434b),
+     TOBN(0x3d55d256, 0x7dd7b05a), TOBN(0xda616243, 0x0fed8362),
+     TOBN(0x24bd0fe8, 0x383e94fe), TOBN(0xbc2b7334, 0x6bfd0cd2),
+     TOBN(0xf9497232, 0x321f7a70), TOBN(0x37a4c2f6, 0x6a3df54f),
+     TOBN(0x7ba783bf, 0x4ddc49d6), TOBN(0x4d142317, 0x04408c76),
+     TOBN(0x7502146b, 0x38b99f23), TOBN(0x479ab73c, 0x21992e8f),
+     TOBN(0xf605370a, 0xd52c41d3), TOBN(0x358b746d, 0x3a60435f),
+     TOBN(0xb2cbab94, 0x5bc537b8), TOBN(0x1fd24431, 0xb99057d3),
+     TOBN(0xff2242a0, 0xb8510f3c), TOBN(0x74b4965d, 0x0733bc53),
+     TOBN(0x30a3a634, 0x86edc9b2), TOBN(0x99c9cf19, 0x49c07c7f),
+     TOBN(0x9d8a50c2, 0x5b0cd506), TOBN(0x0ed9da5a, 0xbbcb3d65),
+     TOBN(0x6de1fb5e, 0x013f88ec), TOBN(0xc9356bff, 0x09086f8c),
+     TOBN(0xa272e1ac, 0x2b8825d7), TOBN(0x3ad83acb, 0xf2c5ba33),
+     TOBN(0x721ca22c, 0x275bce43), TOBN(0xf058b8a7, 0xd24f78e8),
+     TOBN(0xd178eb57, 0xeed46b97), TOBN(0x4ad7d425, 0x259fdb5b),
+     TOBN(0x669ed853, 0x1b515fe5), TOBN(0x9f14b8e5, 0x76fa1b5e),
+     TOBN(0xfaba8d0c, 0x3da46b02), TOBN(0x759c2c95, 0x338f7652),
+     TOBN(0x9a369cb0, 0xb5c0ceb3), TOBN(0xc1d2d1ab, 0x28a2a633),
+     TOBN(0x676190e3, 0xfcb48cd3), TOBN(0x9af97ab3, 0xee09c3b1),
+     TOBN(0x39323719, 0xf7e918f5), TOBN(0xc657cb57, 0xfd3cd554),
+     TOBN(0x78a33d05, 0xa2a02d5c), TOBN(0xda3b79df, 0x64ada03f),
+     TOBN(0x7115ab5c, 0x61b3a72a), TOBN(0xdd19f34b, 0x337194fc),
+     TOBN(0x0f23bfec, 0x8f0a14c3), TOBN(0x1fe19eec, 0xa60485d3),
+     TOBN(0x1ca308c3, 0xa463dc9b), TOBN(0x83e18dd0, 0x5e1ae8be),
+     TOBN(0x849eabdf, 0xd928c0e7), TOBN(0x2d131ff5, 0x6bd3e7b3),
+     TOBN(0xc84cd284, 0x45be4c14), TOBN(0xdee94092, 0xf8f4c719),
+     TOBN(0xe8f223ef, 0x3cb73831), TOBN(0x24382f88, 0x18c2361e),
+     TOBN(0x205366d0, 0xbe91c8dd), TOBN(0x1e17b50c, 0x56024b95),
+     TOBN(0x3c3487da, 0x742cabd3), TOBN(0xbe451387, 0x8bad494c),
+     TOBN(0xfae6c0bf, 0x18ffaef0), TOBN(0x2e7b0ee3, 0x85ed1ede),
+     TOBN(0x3cebaa05, 0x125d1488), TOBN(0xcd0de0fe, 0x7c8b7fb8),
+     TOBN(0x59434d54, 0x464bc74a), TOBN(0x17472da2, 0xa03fd77b),
+     TOBN(0xab23d042, 0x2c1a9edc), TOBN(0x5390625e, 0xd9cf4b37),
+     TOBN(0x43b85844, 0x0531264e), TOBN(0x8d71805e, 0xee7aedca),
+     TOBN(0x4ace3068, 0xfbe643ad), TOBN(0xc98d1cd2, 0x5f7d46c1),
+     TOBN(0xd4888744, 0xf59b3acd), TOBN(0xcf662d61, 0x27288b99),
+     TOBN(0xf2704561, 0x5bce2649), TOBN(0x33a8f3f9, 0x206ae654),
+     TOBN(0xe834843f, 0x9bce2b39), TOBN(0x8de8e41d, 0xa90cfc7d),
+     TOBN(0x398800ed, 0xd81115b4), TOBN(0x4d33f7c5, 0xff2532da),
+     TOBN(0x5ae37fb2, 0xdcc59e2c), TOBN(0xca27b622, 0x24015306),
+     TOBN(0x51beca89, 0x11e8d6e6), TOBN(0x08c0b7e2, 0xa9693774),
+     TOBN(0x795e1a21, 0x72fa713e), TOBN(0x5ec1c123, 0x4be58266),
+     TOBN(0x5d8e87da, 0x1be14fc3), TOBN(0x82cefc1e, 0x80283ad5),
+     TOBN(0x820a385b, 0xdab7865e), TOBN(0x11e32d62, 0xf3daf96c),
+     TOBN(0xf022ade7, 0x5835a989), TOBN(0x2cbc2554, 0x00242233),
+     TOBN(0x653335a0, 0xe7ce649c), TOBN(0x8b30baef, 0x6857eff7),
+     TOBN(0x7ea7c856, 0xf3288377), TOBN(0x1387b347, 0xe8572f5d),
+     TOBN(0x8a6b0352, 0xbe10c0cf), TOBN(0x2a74e834, 0x037c97b9),
+     TOBN(0xfe10bf59, 0x197b122e), TOBN(0xd1ee174c, 0x1918aced),
+     TOBN(0x568e5fb9, 0x3958c20d), TOBN(0x1188cbe6, 0x0484a92f),
+     TOBN(0x00ec14f4, 0x4b0d29e3), TOBN(0x2b2e078e, 0x16a2796d),
+     TOBN(0x48b8cffa, 0x20440444), TOBN(0xd4b877a0, 0x661ab68d),
+     TOBN(0x1f352ab1, 0xc4b459fa), TOBN(0x33accbe6, 0xc53aa54c),
+     TOBN(0xce4ff566, 0x02bb383b), TOBN(0xcad561c6, 0xfd62813d),
+     TOBN(0x0927c348, 0x01dfc9a8), TOBN(0x0dde73fb, 0x00fb9a61),
+     TOBN(0xd859809f, 0xfce59f34), TOBN(0x225bd9b6, 0x81872a46),
+     TOBN(0x2642add2, 0x0314bb90), TOBN(0x82dc7958, 0x0ae61eb8),
+     TOBN(0x84c97478, 0x22d5b667), TOBN(0xb2fe94d1, 0x6214f46d),
+     TOBN(0x834740f2, 0x12cb20de), TOBN(0x336dc7a7, 0x8aa69c94),
+     TOBN(0x8ca085a4, 0x939a33e6), TOBN(0xd59c9ae9, 0x75a94543),
+     TOBN(0x83c97f98, 0x3c47dd07), TOBN(0x0985f73e, 0xe3177231),
+     TOBN(0xe556c3fc, 0xebbc623d), TOBN(0x30a3242f, 0xb1b968fa),
+     TOBN(0x842ce9b0, 0xbcd05a51), TOBN(0x241a35ed, 0x0ad576ce),
+     TOBN(0x49ccaf3c, 0xbb4a793e), TOBN(0x6e6c7a7b, 0x4492a828),
+     TOBN(0x72f4f5fc, 0xba53eb42), TOBN(0x0ca4ba53, 0x3ea74dab),
+     TOBN(0xe7b5fb06, 0xbbaf9d5f), TOBN(0xd49c2e17, 0xb02d3b20),
+     TOBN(0x4d31052a, 0x2d933cc8), TOBN(0x5346e0b4, 0x07299aec),
+     TOBN(0x952a6205, 0x79aa99ec), TOBN(0xaab9bc32, 0xecb34e97),
+     TOBN(0xd539d7e4, 0x58ffe9ae), TOBN(0x91599393, 0x9d994472),
+     TOBN(0x6b1d4868, 0xe8822711), TOBN(0x8857e282, 0x73d452b8),
+     TOBN(0xad59adfd, 0xf08ed046), TOBN(0xdb755d65, 0xc1c47abe),
+     TOBN(0x2df8520b, 0x63275d49), TOBN(0xc3c712ec, 0x7f8a3249),
+     TOBN(0x55f2a108, 0x5215ef57), TOBN(0x955e07a3, 0x3ee2f149),
+     TOBN(0x2194ff53, 0x33f344f4), TOBN(0xb455b9fe, 0xbad16820),
+     TOBN(0xfe69ea78, 0x610b4e4c), TOBN(0x2957be96, 0x8ab11fe8),
+     TOBN(0x3efdee3c, 0x2ce14366), TOBN(0x42043f9f, 0x01eddf9f),
+     TOBN(0xfb7998b1, 0x93524f6c), TOBN(0x95ea64c0, 0xdfecf763),
+     TOBN(0xb23d2620, 0x21afa86f), TOBN(0xea757f03, 0x86b11457),
+     TOBN(0x0bc4d2d1, 0xb0148d30), TOBN(0x119b5535, 0x88ce4170),
+     TOBN(0xaab5bb67, 0x0aa9c8f6), TOBN(0xdfc9693c, 0x88e05de2),
+     TOBN(0x6cae7e57, 0xe3f1e9c3), TOBN(0x2b1ceb54, 0x6f6c3b9c),
+    },
+    {
+     TOBN(0x12e335ca, 0x87636183), TOBN(0x1461a65a, 0x719d1ca3),
+     TOBN(0x8150080a, 0xb14161d8), TOBN(0x08da4ebf, 0xc612e112),
+     TOBN(0xc95dfb6b, 0xa8498a9a), TOBN(0x099cf91d, 0xba0f8dba),
+     TOBN(0x12d2ae14, 0x4fb4f497), TOBN(0xfa3a28b0, 0x33cb7306),
+     TOBN(0xc89fc5d0, 0x0f01c7ce), TOBN(0x6fc45ffd, 0x7283bdf0),
+     TOBN(0x71dece81, 0x81151923), TOBN(0xed1cb14c, 0xc433fcc9),
+     TOBN(0x4279612b, 0xd3959bcf), TOBN(0xe163880b, 0x35b5732f),
+     TOBN(0x35414ca7, 0x71d0a1ca), TOBN(0xe8b9e651, 0x2c1e47f3),
+     TOBN(0x4ff11b0c, 0xc8df0a74), TOBN(0x346ba520, 0xe095ea9a),
+     TOBN(0x81dd2268, 0xcc2bc6c0), TOBN(0x2fb2e99f, 0xc2701468),
+     TOBN(0x0d213361, 0x98053f0e), TOBN(0xe0b8280d, 0xf7ae879a),
+     TOBN(0xd92b7a75, 0x952560f7), TOBN(0x8d17dfad, 0x9723b62e),
+     TOBN(0x5ce8a78a, 0x08b21362), TOBN(0xf37f5e7f, 0xd9fe0b36),
+     TOBN(0xdca66c7f, 0x2c87837c), TOBN(0x92524b94, 0x0bf2e993),
+     TOBN(0xfc0f020c, 0x71745788), TOBN(0x6018de46, 0x3cbfbf4c),
+     TOBN(0xa8446691, 0xac3de1c8), TOBN(0xb194d419, 0x5de5ae41),
+     TOBN(0x1586cdff, 0x2ff27af2), TOBN(0xee628535, 0xde26b5ef),
+     TOBN(0x58480040, 0xc682923e), TOBN(0x4dd4596b, 0x5e37da30),
+     TOBN(0x247b9fd7, 0x2f64225f), TOBN(0xdcc6de5f, 0x51ca2121),
+     TOBN(0x99fb41ac, 0x86e7ab9a), TOBN(0x54c782a0, 0x952b413a),
+     TOBN(0x7641190e, 0x7298c7d9), TOBN(0x499c35ed, 0x716eda14),
+     TOBN(0x316134bf, 0xbb764e90), TOBN(0x4d23467e, 0x884fc34e),
+     TOBN(0xfd1208a9, 0xf1d13484), TOBN(0x089d9605, 0xcd298a74),
+     TOBN(0xb398c85a, 0x73c4346a), TOBN(0x50064076, 0xf37f13de),
+     TOBN(0xfe10d25a, 0xa6ebb83d), TOBN(0xc5e3edf8, 0xa834b30d),
+     TOBN(0x546b5d5c, 0x683e09ff), TOBN(0x02f96218, 0xc6dc44c6),
+     TOBN(0x64528c55, 0xc0edfc04), TOBN(0xb5a44a2c, 0xb0fc3058),
+     TOBN(0x9f09b116, 0xceeff21c), TOBN(0x077bcd67, 0x6b0fbbcd),
+     TOBN(0x29aaa4a8, 0x9ce76a94), TOBN(0x847cd081, 0xc0725c97),
+     TOBN(0x0c099e90, 0x97e16665), TOBN(0xe409ffc9, 0x8f7b1fc4),
+     TOBN(0xc0575b80, 0x690941ed), TOBN(0x8e25100a, 0x92c0ee9d),
+     TOBN(0x71662d27, 0x9b75837d), TOBN(0x6eeb9e97, 0xe56bb22b),
+     TOBN(0xf1d6333f, 0x85c6a60b), TOBN(0x982fee9d, 0x1d7ccfaa),
+     TOBN(0x1c5e28e7, 0xd4634535), TOBN(0xa76e1d27, 0x94fec469),
+     TOBN(0x1fe944d6, 0xafe377ec), TOBN(0xbd579a25, 0x2f68ae6b),
+     TOBN(0x10eabb93, 0xab6b515e), TOBN(0xa17b5f6c, 0x31b4e4b8),
+     TOBN(0x05e785fb, 0xaf72c239), TOBN(0x597e2016, 0x8af42e92),
+     TOBN(0x663f5a72, 0xb32ae6c9), TOBN(0x3040ff13, 0x45541cc6),
+     TOBN(0x6140081f, 0xdeca6b32), TOBN(0xcdaccaf7, 0xc735001b),
+     TOBN(0x62de5066, 0xdaef0e68), TOBN(0x056e9021, 0xd837df99),
+     TOBN(0xba399283, 0x16cd1be7), TOBN(0x2a486323, 0xcfacf7ad),
+     TOBN(0x00c15730, 0x277777ce), TOBN(0x5d2f200f, 0xd49a254c),
+     TOBN(0xf38a1f3b, 0xdb68078d), TOBN(0x595dea3f, 0x33604a36),
+     TOBN(0x14749d8c, 0x904b60b2), TOBN(0xe70c96d8, 0x246348ff),
+     TOBN(0x04340d52, 0x390e35da), TOBN(0xc098e3d3, 0x27a9947c),
+     TOBN(0xe6d78198, 0x9ecc7a3f), TOBN(0x2c39102e, 0x23aa6df6),
+     TOBN(0xb83fed0d, 0x300f3cb1), TOBN(0xc0b1e356, 0xdcfbe054),
+     TOBN(0x3da2224c, 0x20cf45a8), TOBN(0x5be55df7, 0x2f30deda),
+     TOBN(0x4d31c29d, 0x2faa9530), TOBN(0x1d5783ae, 0x49d42f79),
+     TOBN(0xe588c224, 0xf618b3f3), TOBN(0x7d8a6f90, 0xf8f5b65d),
+     TOBN(0xa802a3d2, 0x62d09174), TOBN(0x4f1a93d9, 0xbddd1cb7),
+     TOBN(0xe08e1d3c, 0x35a5c1dc), TOBN(0x856b2323, 0xf9d2958e),
+     TOBN(0xefd1e3ba, 0x96f00090), TOBN(0xd489943f, 0x3e0d25de),
+     TOBN(0x082c40ae, 0x30c8626f), TOBN(0xf6e5b5ef, 0xa4f428e0),
+     TOBN(0x660414a3, 0x38a7f623), TOBN(0xcd4e68de, 0x23eefed8),
+     TOBN(0x6dcadc62, 0xfc14e750), TOBN(0xcb78b3bc, 0xbeae89b6),
+     TOBN(0x445acc56, 0x1d5e580e), TOBN(0xbf6547ef, 0xc43abe19),
+     TOBN(0xd160a81b, 0xc922d50f), TOBN(0x3877c7f8, 0xf68eed4e),
+     TOBN(0x395745ea, 0xf8a9f64a), TOBN(0x9085b253, 0x603350f6),
+     TOBN(0x2a4c71f1, 0x8b1df366), TOBN(0x49b9e818, 0xabe332dc),
+     TOBN(0xb3e76e66, 0x528960b1), TOBN(0x445dc393, 0xd84aecb3),
+     TOBN(0x13618436, 0x1612ad64), TOBN(0x3ccbeccc, 0x8c831e37),
+     TOBN(0x0fb0bd41, 0x6121383c), TOBN(0x316164a3, 0x80d895a3),
+     TOBN(0xc3d34153, 0x233f2f1e), TOBN(0x2905906f, 0xe0d92225),
+     TOBN(0xe12d66e2, 0x95456622), TOBN(0x10469942, 0xff554b13),
+     TOBN(0xa894af86, 0xf7126c09), TOBN(0x448f3267, 0xf581d3f5),
+     TOBN(0xb5512926, 0xa2b5e758), TOBN(0x08f02988, 0x43fddd90),
+     TOBN(0x5f437035, 0x8ba319e6), TOBN(0xd254188e, 0x865b37e7),
+     TOBN(0x5b281b23, 0x8a5cb63a), TOBN(0xa15a2712, 0x6dd136c2),
+     TOBN(0x00fab229, 0x169beae4), TOBN(0x400d3f37, 0xde31b4a1),
+     TOBN(0x275877a4, 0xf8545cb0), TOBN(0xb396a513, 0x36df0277),
+     TOBN(0xf9896978, 0x838ced07), TOBN(0x86e68167, 0x715cea8d),
+     TOBN(0x0eb0f0de, 0x06a5a96d), TOBN(0x2c7a3672, 0x1fcf91ae),
+     TOBN(0x287bf614, 0x630eca3a), TOBN(0x65347473, 0xf60c9d2d),
+     TOBN(0xed15a229, 0x906efa7f), TOBN(0xe7be6381, 0xd549b5b3),
+     TOBN(0x23f32972, 0x2ce9141b), TOBN(0x9618d9a1, 0xfcf823f8),
+     TOBN(0x3d0ef0d3, 0xa3d89e15), TOBN(0x4d5a30c9, 0x0d07f5eb),
+     TOBN(0xc359e310, 0x73e4887a), TOBN(0x2f4c6b7e, 0xdbdec349),
+     TOBN(0xc5a1d3e9, 0xba142643), TOBN(0x8f4fd58e, 0x11c794b6),
+     TOBN(0xcad091d1, 0x1810c63d), TOBN(0x5b616239, 0xf0bfa76c),
+     TOBN(0xe3433562, 0xa838792a), TOBN(0x4aead02b, 0x54148e98),
+     TOBN(0x809f2baf, 0xdb66f216), TOBN(0x09cc90ff, 0xeabfe5da),
+     TOBN(0x69eb235a, 0x63e8edad), TOBN(0x64f7acb5, 0xa7f95997),
+     TOBN(0xe999ea18, 0xfae20f25), TOBN(0xcd7ff208, 0x3c4966b3),
+     TOBN(0x595e0cc0, 0x345c8929), TOBN(0xfe43c73c, 0xde5e2498),
+     TOBN(0x0cdefc98, 0x503f216d), TOBN(0x8e4e170d, 0xf98826fb),
+     TOBN(0x1492247d, 0xb6c79b1c), TOBN(0xf8e24b38, 0xef0532aa),
+     TOBN(0x9f349d51, 0x044bc458), TOBN(0x2ef04ead, 0x1002d315),
+     TOBN(0xaf322f23, 0xda60d581), TOBN(0x07deaa88, 0x0681173f),
+     TOBN(0x86b97444, 0xa78feca0), TOBN(0x64d336ea, 0xc633a54d),
+     TOBN(0x10dd4b1f, 0x2a426cbf), TOBN(0x08d97c15, 0x7af59869),
+     TOBN(0xb8cc814b, 0x2d7fe97e), TOBN(0x7eacd2e1, 0x3bfb60fe),
+     TOBN(0x967dafb7, 0xb790881c), TOBN(0x2002b8e4, 0x3663e76c),
+     TOBN(0x3bd28ede, 0xf8e82490), TOBN(0x44dd2e81, 0x4bb2a47a),
+     TOBN(0xde750dfe, 0xdbc3f2f8), TOBN(0xd9b6e912, 0x6e2eec70),
+     TOBN(0xe8400e2f, 0x1e4c4d2f), TOBN(0xd3325697, 0x23217be2),
+     TOBN(0x030b7e39, 0xd4231a1d), TOBN(0x1f72e8b1, 0x613d17d8),
+     TOBN(0xcd423512, 0x01857d37), TOBN(0x9ecd682c, 0x0b4b7926),
+     TOBN(0xfe4ac1c3, 0x8ec44636), TOBN(0x4a030cbf, 0x9aacc091),
+     TOBN(0x12bb252e, 0x0b133f54), TOBN(0xbf90ea5d, 0xf970d266),
+     TOBN(0xe00d25f7, 0xf5484410), TOBN(0xb4984eeb, 0x2a922894),
+     TOBN(0x498102fd, 0x8e1e09ce), TOBN(0x8f8c9fcb, 0xe1d731bf),
+     TOBN(0xdb897669, 0x0b4983b7), TOBN(0x7a7767f9, 0x7b2468f5),
+     TOBN(0x1a2f9fe8, 0x72f4d5f4), TOBN(0x10e95aa9, 0xa6169daa),
+     TOBN(0x9487e50f, 0x520166e9), TOBN(0x6f6b115b, 0xc4ee6a95),
+     TOBN(0xaf29926f, 0xcf7560f8), TOBN(0x20a32458, 0x1f989e46),
+     TOBN(0x165a2232, 0xd3bd2473), TOBN(0x93d841ff, 0xe9fecdf8),
+     TOBN(0x71d63fa7, 0xbf9978c0), TOBN(0x381bcf34, 0xe7de184b),
+     TOBN(0x317c8e40, 0x347dfaef), TOBN(0x795b0f7d, 0x64464bf3),
+     TOBN(0x15dc99d6, 0x1364ec20), TOBN(0xc07fce28, 0x91600d3f),
+     TOBN(0x9825338b, 0xc8bebbda), TOBN(0x5e5e89f6, 0xa8547c03),
+     TOBN(0x3c50032f, 0x1a040b84), TOBN(0xcea7544f, 0x2b3a533d),
+     TOBN(0xea26d87d, 0x43247e19), TOBN(0x7e753390, 0xfba8368e),
+     TOBN(0xb35e75cb, 0x3c7bcfc6), TOBN(0xf78cb5ce, 0x7e44aab3),
+     TOBN(0x4a3534e9, 0xa98d7104), TOBN(0x2b83ea6c, 0x6f5852ea),
+     TOBN(0x11337fff, 0x68dced7c), TOBN(0xcca0f2c6, 0xd1a2a294),
+     TOBN(0xb547c662, 0x426bf202), TOBN(0xec50423e, 0x66194a34),
+     TOBN(0x11d34865, 0x78161e84), TOBN(0x83508c06, 0x64f04dec),
+     TOBN(0xd1c72976, 0xf7732345), TOBN(0xd624bacd, 0x18e77e0a),
+     TOBN(0x71344b75, 0xba79bdd9), TOBN(0xe4bfe085, 0x8d6c1474),
+     TOBN(0x505e8fd9, 0xcc5eb43a), TOBN(0x612ab1d0, 0xdaaf0621),
+     TOBN(0xde170783, 0xe6672368), TOBN(0xfee7df44, 0x83788673),
+     TOBN(0x364d6885, 0xd119271d), TOBN(0xdd70bae8, 0xe1b0cea2),
+     TOBN(0xb4b873ad, 0x5832adba), TOBN(0xad3ecc18, 0x8c683749),
+     TOBN(0x963d8793, 0x4d217a2e), TOBN(0x099e8c56, 0x1fa4702d),
+     TOBN(0x6d91bc47, 0xe6431f1b), TOBN(0x3fd21287, 0xa5f61399),
+     TOBN(0x2fc90bae, 0x682fa08b), TOBN(0x51699c85, 0xc1ca371c),
+     TOBN(0x16f29d74, 0x831c428f), TOBN(0x0ecefb66, 0x9fa2b504),
+     TOBN(0xd04ac53f, 0xa75c5a91), TOBN(0xcbe62421, 0x3bf0524b),
+     TOBN(0x91dcb3ce, 0xb8792826), TOBN(0x28a6bf88, 0x7885092a),
+     TOBN(0x24798e59, 0x64c1e218), TOBN(0x18e848dc, 0x3fec97dc),
+     TOBN(0x935e0f50, 0x9da457b7), TOBN(0x46b67ab7, 0xb8f497a6),
+     TOBN(0x15a38140, 0x7651e4ff), TOBN(0x6ba6c617, 0x4890cd7f),
+     TOBN(0xa527b8d2, 0x5fe253ca), TOBN(0x945277b8, 0xff3d603b),
+     TOBN(0x10796155, 0x75392f01), TOBN(0xcac8f713, 0x2bd9619d),
+     TOBN(0x71a87eca, 0xdebb8e28), TOBN(0xe8e6179e, 0x52ab1792),
+     TOBN(0x4ce3998b, 0xe33705e7), TOBN(0xf9a0661a, 0x48ba56e4),
+     TOBN(0x47f06b30, 0xd9e4e184), TOBN(0xda465f75, 0xf9f8f6df),
+     TOBN(0xb05acbbe, 0xc0ad3e20), TOBN(0xec8776a4, 0x92bc2c13),
+     TOBN(0xbb3971b7, 0x240a908b), TOBN(0x80a14367, 0xbbd0cecc),
+     TOBN(0x40911e50, 0x086949bc), TOBN(0x39b3ab69, 0x4064a19d),
+     TOBN(0x538c6d96, 0x6b07eaa7), TOBN(0x38c05b47, 0xd3723bde),
+     TOBN(0x1e669308, 0x080d2a64), TOBN(0x6b44dbe5, 0x2a77601f),
+     TOBN(0x35579681, 0xe7c6ce9a), TOBN(0xd2950b0e, 0xa16afa75),
+     TOBN(0xd228a3ba, 0xeaf7fafc), TOBN(0x9324e71d, 0xee878f00),
+     TOBN(0xa853bfc1, 0xe413c1ce), TOBN(0xfe916368, 0xdcf17d1c),
+     TOBN(0x8611383a, 0x2546154b), TOBN(0xdbdf225d, 0xe715b375),
+     TOBN(0x874d70a6, 0x8dbb0651), TOBN(0x84e58895, 0x9ed56391),
+     TOBN(0xca83d8ad, 0x3776503c), TOBN(0x2cf38b4e, 0x46e82d65),
+     TOBN(0x65af46e6, 0xadf3a8d1), TOBN(0x4f09a4ab, 0x1d31056d),
+     TOBN(0xdba27b42, 0xcacc81d5), TOBN(0xb6caa0ca, 0x5d6e1bdd),
+     TOBN(0x1086e441, 0xf7198b26), TOBN(0x15dfe6cb, 0xac572f9b),
+     TOBN(0xd9444337, 0xd2051dd5), TOBN(0x6c34b236, 0x834cd72d),
+     TOBN(0x84783216, 0x58df3f28), TOBN(0x59b8808e, 0x2e921372),
+     TOBN(0x3b268249, 0x55835302), TOBN(0x3299cbe0, 0x9f4863f1),
+     TOBN(0x616e3cdd, 0x4c4e213a), TOBN(0xa3c84868, 0x8c824317),
+     TOBN(0x884be61c, 0x460ed764), TOBN(0x388df47b, 0xb6041177),
+     TOBN(0x27089763, 0x60b29b0b), TOBN(0xd66d7d53, 0xe502ba08),
+     TOBN(0xadec85ca, 0x5acbfaf4), TOBN(0xfbacf9b7, 0x470c9519),
+     TOBN(0x5d18b7f6, 0xdbcda5b2), TOBN(0x7615c036, 0x0f228ed5),
+     TOBN(0xdfcd8273, 0xd6000825), TOBN(0xdacfcf11, 0x9d440eb3),
+     TOBN(0xa8257834, 0x7738fa46), TOBN(0x7db548af, 0x76281df3),
+     TOBN(0x71dd19f6, 0x3e0b052c), TOBN(0x811feef2, 0xd876613f),
+     TOBN(0x7536e854, 0xf9c3155c), TOBN(0x3e594973, 0x4c8c1220),
+     TOBN(0xf8c5c72d, 0x069b4228), TOBN(0xc077d394, 0x1f2f6b02),
+     TOBN(0x0032dfb9, 0x76393a52), TOBN(0x5e52c880, 0x706c3572),
+     TOBN(0x4a9d6e9d, 0xe003750f), TOBN(0x3d54814d, 0x6147cee7),
+     TOBN(0x09ed7f77, 0x23b655fa), TOBN(0x14fff651, 0xf1988361),
+     TOBN(0x742f3abd, 0xfb263d48), TOBN(0xedb557dc, 0x53699a0c),
+     TOBN(0xc692a474, 0x7ecd0f61), TOBN(0xdc64f583, 0x058f0d17),
+     TOBN(0x68a9ce75, 0x3227d3ed), TOBN(0xfd0b0320, 0x4601d083),
+     TOBN(0x7167b330, 0x9c2cee38), TOBN(0xef658993, 0x710e350d),
+     TOBN(0x75a83be1, 0x16910648), TOBN(0x5b32e77d, 0x2e7d446c),
+     TOBN(0x8e0534e5, 0xa86ba2de), TOBN(0xc8a92eac, 0xb692aeee),
+     TOBN(0x3cf686eb, 0xf663150f), TOBN(0x840eaade, 0x01962baf),
+     TOBN(0x3e06154f, 0xa264d877), TOBN(0xbbd04137, 0x24630300),
+     TOBN(0x0b0151bd, 0x58631468), TOBN(0x570ef82c, 0x9f99bbe5),
+     TOBN(0x03565f47, 0xb30f7b96), TOBN(0x000628e0, 0x98c04b24),
+     TOBN(0xd34a90ae, 0xd6ccdb2b), TOBN(0x1a584858, 0xa99a761c),
+     TOBN(0xa640ddca, 0x65e29f1b), TOBN(0xffb672f9, 0x728d3208),
+     TOBN(0x550f6392, 0x5433abd8), TOBN(0x4f35e116, 0x13ff0107),
+     TOBN(0xbb2b0fab, 0xe731a37b), TOBN(0x1e8a5a08, 0xc83d6e74),
+     TOBN(0xf617e177, 0xc6565e23), TOBN(0x8e370e5a, 0x76da0795),
+     TOBN(0xa5631e02, 0x03936a55), TOBN(0xe576bee9, 0xd41293ad),
+     TOBN(0xfcfd9bc7, 0x5381bc98), TOBN(0x8a42ddfd, 0x000a98ab),
+     TOBN(0xd6091ca6, 0xb49463c4), TOBN(0xf37f6b9f, 0x9754ce07),
+     TOBN(0xe1543897, 0xfa399fda), TOBN(0x7b029ead, 0x8810063c),
+     TOBN(0xa98a46bd, 0xec5a5b52), TOBN(0xdd162811, 0xd50cc504),
+     TOBN(0x4d725c1d, 0x67a95e56), TOBN(0xc36d6e8b, 0x8e17af44),
+     TOBN(0x38ffb699, 0x313454c1), TOBN(0x22c3da8c, 0x991e4eaa),
+     TOBN(0xfa36ee15, 0x0bb72dc0), TOBN(0x356bbf74, 0x4fd01d32),
+     TOBN(0x9ff71a30, 0x3c7939a3), TOBN(0xa0ad2fb5, 0x691786e9),
+     TOBN(0x7d7f4770, 0x283c34c1), TOBN(0x0148a4f0, 0x2454a31c),
+     TOBN(0xdcbb138a, 0xeab3b646), TOBN(0x7834bdb9, 0xf101223e),
+     TOBN(0x49de6cc3, 0x965baa81), TOBN(0x5462f15e, 0x15471215),
+     TOBN(0xd77e7a56, 0x81d17760), TOBN(0xa08c5ad9, 0x53f00de3),
+     TOBN(0x2e6e6686, 0x397ed010), TOBN(0xe444a5a9, 0x2bef28cb),
+     TOBN(0x1ab9d616, 0x4073cbe4), TOBN(0x24c6b9e0, 0x18f0b7dd),
+     TOBN(0x456482b6, 0xc2a93055), TOBN(0x0f89129f, 0xefbe8715),
+     TOBN(0xb50818c3, 0x62e5f6f2), TOBN(0x1d74a1ca, 0x3d63c663),
+     TOBN(0x9a9124ee, 0xd84bfa55), TOBN(0x6cf81f41, 0x254b3f04),
+     TOBN(0x7c9b7c3e, 0xa1051109), TOBN(0x71c3d6d6, 0x640e8df2),
+     TOBN(0x56571157, 0x62f6af2f), TOBN(0x2ec61a3f, 0xe1bc9ae8),
+     TOBN(0x20caa2ff, 0x2fcc848b), TOBN(0x71e30dac, 0xdc5c297e),
+     TOBN(0x11901efc, 0xfbc0740a), TOBN(0x4994fc5f, 0xb6e35fca),
+     TOBN(0x4dc09eba, 0x177de7dd), TOBN(0xedfdd25c, 0x0494bebf),
+     TOBN(0xc4821ed9, 0x0cbaeb8b), TOBN(0xa9ef7a48, 0x66788fbd),
+     TOBN(0x5b7a7ca5, 0xd65efbc2), TOBN(0xe18feb42, 0xa9cb1fc6),
+     TOBN(0x2cc74b9c, 0x56b00ab5), TOBN(0xf559a140, 0xdb4bf3f4),
+     TOBN(0x283136d4, 0xb8a4b54e), TOBN(0xe969e4f8, 0x37032aa2),
+     TOBN(0x5635fb66, 0xd85beed3), TOBN(0x32bc4fdd, 0xa72a54bb),
+     TOBN(0xc1e5ee2b, 0x4c386a49), TOBN(0x979fd877, 0x795a0b08),
+     TOBN(0x5acef24d, 0x431f0b8e), TOBN(0x9f1c4a80, 0xd13cafef),
+     TOBN(0xf19ac70b, 0x4659f447), TOBN(0x82bab6b6, 0x10f561ae),
+     TOBN(0x1268e7f3, 0xbbc879a7), TOBN(0x7e7d7141, 0x79c37165),
+     TOBN(0x491f049d, 0x597e313f), TOBN(0x6ca7e73e, 0xecc98736),
+     TOBN(0xd7712aa4, 0x80a31eb9), TOBN(0xbf7376ca, 0x2d8b99d7),
+     TOBN(0xc1166cdc, 0x2b8e5f7b), TOBN(0x562bf290, 0xf1a48c9b),
+     TOBN(0xa6e72238, 0x31c38c75), TOBN(0x51a9a100, 0xb5f42def),
+     TOBN(0xa0931d81, 0xa100b75f), TOBN(0x7022479d, 0x967830be),
+     TOBN(0x53eaaa1f, 0xc192bc29), TOBN(0x09504e7a, 0x4123a9f2),
+     TOBN(0xe897397f, 0x90671997), TOBN(0xc56185d2, 0x4294fda2),
+     TOBN(0xb531f278, 0x9819b185), TOBN(0x390155ff, 0xe9dda4cc),
+     TOBN(0x1258a5d9, 0x14d26bf9), TOBN(0x47d8f5ae, 0x7e5f13a1),
+     TOBN(0xef9e05e3, 0xe9591945), TOBN(0x92d20e07, 0x846441be),
+     TOBN(0x28cc70ef, 0x1d897ad1), TOBN(0xee962e74, 0x0bac861f),
+     TOBN(0x9b7a4236, 0xbed368e4), TOBN(0xe65ac22a, 0xe49b9720),
+     TOBN(0x851f0032, 0x22c1bd82), TOBN(0x771573ec, 0x1e75ab15),
+     TOBN(0x2e0a4635, 0x702eb71a), TOBN(0x65167c74, 0xee924cd9),
+     TOBN(0xe16b3515, 0x10ccabb5), TOBN(0x63cf15c4, 0x10ea5907),
+     TOBN(0x59dacdc6, 0x616f5290), TOBN(0x19eb409b, 0x8e372a43),
+     TOBN(0x5c879337, 0xe3c36beb), TOBN(0x5841e7e2, 0x0555fa1e),
+     TOBN(0xce197347, 0xf346ec2d), TOBN(0xe14818a8, 0x221db43d),
+     TOBN(0x1bf37115, 0xc935c025), TOBN(0xb22bdb03, 0xfee14ce0),
+     TOBN(0x893c5efa, 0xf0e3dfd6), TOBN(0x8fe9d56c, 0xb1f95e1e),
+     TOBN(0x6ee580e5, 0x407e4db0), TOBN(0x0292bfc4, 0x9fb33911),
+     TOBN(0x598ce787, 0xb2869cac), TOBN(0xd2f77720, 0x4798690a),
+     TOBN(0x2689e0f3, 0x9cb6015a), TOBN(0x8831ddeb, 0x7127b0e8),
+     TOBN(0x44db087b, 0x5c4fd58c), TOBN(0x04a0c68e, 0xcda33285),
+     TOBN(0xe55f00d7, 0xe1a4c364), TOBN(0xb3a2ea9a, 0xb6393b21),
+     TOBN(0x99ef4da3, 0x5e9d8db9), TOBN(0xa01f11d9, 0x17b484bc),
+     TOBN(0xc2283fbf, 0x1767f6ca), TOBN(0xbb524479, 0x9e77f389),
+     TOBN(0xc4ea3c66, 0x10149726), TOBN(0x4b71d644, 0x82ec5f64),
+     TOBN(0x0fe49d52, 0x642944c7), TOBN(0x69fef689, 0x5a17a7bd),
+     TOBN(0x2f3588fc, 0x8c3dce23), TOBN(0x9d429231, 0x68e0c237),
+     TOBN(0x63756076, 0x86fa61d2), TOBN(0x1d89c6b8, 0x729bc192),
+     TOBN(0x85e098d2, 0x00d3ffd1), TOBN(0x5bf292c2, 0xde6f9109),
+     TOBN(0xb20dc994, 0x3e7b8f23), TOBN(0xcbe51bad, 0x87c6eb54),
+     TOBN(0x263fd862, 0x0517b914), TOBN(0x447624ad, 0x225499a7),
+     TOBN(0xfbb831bb, 0x71f807d9), TOBN(0x9514fe38, 0x2fe2e021),
+     TOBN(0x881e8763, 0x52418e9a), TOBN(0x268e4655, 0xf1d9b43b),
+     TOBN(0xf917044a, 0x1f780af9), TOBN(0x3727b2d9, 0x3d758ba5),
+     TOBN(0x8487eb90, 0x68755cf3), TOBN(0x1887394e, 0x7fe12541),
+     TOBN(0x2e4c65d4, 0x46af8ca8), TOBN(0x72aae645, 0xb9e119dc),
+     TOBN(0x958e0094, 0x1ec6ad73), TOBN(0x84a7eec4, 0x8ce4573e),
+     TOBN(0x3d6d00d4, 0xf9254b96), TOBN(0x4ef44f58, 0x8e421732),
+    },
+    {
+     TOBN(0xf59de0f8, 0x7d3ad2ac), TOBN(0xd2670cb1, 0xc0f92c5c),
+     TOBN(0x8f05944a, 0xc900b6a5), TOBN(0x11aeed23, 0x8d9668eb),
+     TOBN(0x21b038e4, 0x7c488ea6), TOBN(0x406ea3f7, 0x78083d75),
+     TOBN(0xd22197b4, 0x3bd31fe2), TOBN(0xdc8f8ccb, 0x28a6ef9a),
+     TOBN(0x679a6483, 0x02887281), TOBN(0x13f3d39b, 0x7f9de66f),
+     TOBN(0x289c3c50, 0xf1a7dee2), TOBN(0xa510a53c, 0x40b698b8),
+     TOBN(0xc566c3fb, 0x06f799ad), TOBN(0xcc95a879, 0xb5374650),
+     TOBN(0xbd7343c0, 0x61c947b2), TOBN(0xbbbff69d, 0x9543e51b),
+     TOBN(0xb80d38dc, 0xba75aba9), TOBN(0xe9b61ac6, 0xd150f881),
+     TOBN(0x9f56af52, 0xca7b47c5), TOBN(0x040300d9, 0x77fb3128),
+     TOBN(0x36877184, 0xc01fd0c1), TOBN(0x40112a04, 0x8b6e134b),
+     TOBN(0x56daed90, 0xccd71653), TOBN(0xec553aa6, 0xb74bd62e),
+     TOBN(0x0398381d, 0x11476dde), TOBN(0x4959204d, 0x1ea0923f),
+     TOBN(0xd67427ad, 0x017745bd), TOBN(0xef022a74, 0x6935e467),
+     TOBN(0x57e799f5, 0x24e0380a), TOBN(0x6ee2b64f, 0xb5f1a730),
+     TOBN(0x9aeaac48, 0x521771d8), TOBN(0x02c8521c, 0x0992a13c),
+     TOBN(0x25dd9f41, 0x48f6934a), TOBN(0x8d191988, 0x3e38e31f),
+     TOBN(0x35548844, 0x32899837), TOBN(0xf87c696b, 0xf56b07d6),
+     TOBN(0xbc66e7d7, 0x73e927b9), TOBN(0x04cdac77, 0xbb0bedc5),
+     TOBN(0x1e6f2903, 0x0bcd022a), TOBN(0xafa637be, 0x22c574b5),
+     TOBN(0xcdca4b1e, 0x55c1759b), TOBN(0x3d46ee3b, 0xa6819d39),
+     TOBN(0xf7497ade, 0xb4b0fce9), TOBN(0x54aef506, 0xdcb613c3),
+     TOBN(0xbc11d721, 0x522ff464), TOBN(0xf53f16f2, 0x37bd3c51),
+     TOBN(0x88f29955, 0x485695b9), TOBN(0x428ce742, 0xdac00fe3),
+     TOBN(0xd971fbd2, 0xa3520c27), TOBN(0x2204fe54, 0xe05e1b9d),
+     TOBN(0xb08be507, 0xf0c15c89), TOBN(0xfeeda919, 0x901a15c3),
+     TOBN(0x6576ad3b, 0x84b60eb5), TOBN(0x40d4b9a1, 0x59e951da),
+     TOBN(0xbe96e1b8, 0x76244608), TOBN(0x3af35ec9, 0x58ef9f37),
+     TOBN(0xbfe5c43b, 0x153adbf7), TOBN(0x07a66edf, 0x80351fec),
+     TOBN(0x3d804235, 0x3b109e60), TOBN(0x4dc97176, 0xa832c162),
+     TOBN(0x03fec75f, 0xb1db1e5c), TOBN(0x6aa02da6, 0xa15b9900),
+     TOBN(0x5f9e808f, 0x4faa1cff), TOBN(0x90aa28bd, 0xa6412a26),
+     TOBN(0x2fb2c15b, 0xe041d453), TOBN(0x2b847efa, 0x86c18ef0),
+     TOBN(0x84f5ee9d, 0x115b7318), TOBN(0xd568b080, 0x71699936),
+     TOBN(0x34658ae7, 0x6ea5c852), TOBN(0x99f918b3, 0x126d10ce),
+     TOBN(0x75e3d9cc, 0x09b9407f), TOBN(0x11c6a0bb, 0x7883f978),
+     TOBN(0x522a6925, 0x876db0fd), TOBN(0xc5401ca1, 0x7a9a4211),
+     TOBN(0x89163b57, 0x6789e755), TOBN(0xd2b2c99a, 0x0fd6f1b8),
+     TOBN(0x427eea22, 0xa7b452dc), TOBN(0xce8e6682, 0xef9db65c),
+     TOBN(0xfd835810, 0xda9c80f7), TOBN(0xdb91bfbb, 0xea916411),
+     TOBN(0x7a5aefad, 0x798b5051), TOBN(0xbd7ebc88, 0x42a0d4cd),
+     TOBN(0x958e327a, 0xbac28520), TOBN(0xfa8bf6d4, 0x7d010c77),
+     TOBN(0x8a7040aa, 0x579752f4), TOBN(0x47974d84, 0xe573d895),
+     TOBN(0xfd2a0cdc, 0xfe16f77b), TOBN(0x0f08f86a, 0xdbf71fdc),
+     TOBN(0xb19de6f1, 0x2983bd4d), TOBN(0xb990931e, 0x1e3a28b3),
+     TOBN(0x43b71b8b, 0x00cbc4f9), TOBN(0x35d1ddd0, 0xf4d75a0e),
+     TOBN(0xc211951d, 0xc653f111), TOBN(0xbbc46824, 0x88750928),
+     TOBN(0x0cf6e752, 0x174803e3), TOBN(0x81f5e0ac, 0x8960d80c),
+     TOBN(0xe03ca085, 0x0c52fcf9), TOBN(0xa795382e, 0x0865ced4),
+     TOBN(0x03bd561c, 0xe7117376), TOBN(0x8608dde1, 0x3fd7184a),
+     TOBN(0xfd48fd50, 0xa2a98acc), TOBN(0x902fa587, 0x11df74b0),
+     TOBN(0x683f101d, 0xfa73b8f1), TOBN(0xc805d31b, 0xe7c0efa6),
+     TOBN(0xe5effb4e, 0x5b11d6c0), TOBN(0xba30f747, 0x01c8374c),
+     TOBN(0x8733511b, 0x0c275aec), TOBN(0xf140b740, 0x97354e36),
+     TOBN(0xb01ded69, 0x0341268e), TOBN(0x17bc3176, 0x27eac17b),
+     TOBN(0x88097703, 0x8984992c), TOBN(0x37bfafab, 0x3e05061c),
+     TOBN(0x7eca9f09, 0x111d0eb8), TOBN(0xda7eb023, 0x8f243481),
+     TOBN(0xac3cb2d6, 0x59b5e491), TOBN(0x56e725b1, 0x4f794842),
+     TOBN(0x43245254, 0x45b2dff6), TOBN(0xeafe73b9, 0xef10ec78),
+     TOBN(0x0d3cb2bc, 0x78819dbf), TOBN(0xff1cd617, 0xe784eb22),
+     TOBN(0x0dbaf1c9, 0x9ce0fcd6), TOBN(0x732ea65e, 0x3232a847),
+     TOBN(0xdb2ce218, 0x6a75d822), TOBN(0x88ffd479, 0x3d2273ca),
+     TOBN(0x89092ad2, 0xf2f26b61), TOBN(0xfb7041bd, 0x686706ab),
+     TOBN(0xe3d5fa75, 0x5e23597c), TOBN(0xa2035bf8, 0x5995fc84),
+     TOBN(0x1feecd2c, 0x4514b8bd), TOBN(0x57cb78b4, 0x434b9233),
+     TOBN(0x59bd2ad7, 0x24215322), TOBN(0x41437de2, 0x1ce8daa0),
+     TOBN(0x401bbece, 0x7147ce80), TOBN(0x5e462137, 0x5abb61e8),
+     TOBN(0xbbf21033, 0x5a3790eb), TOBN(0x9a791c09, 0x5134dee3),
+     TOBN(0xc8ded766, 0xcedd2cc1), TOBN(0xa3e48e9d, 0x6447b925),
+     TOBN(0xc73282a3, 0x69efa728), TOBN(0x8cb72c30, 0x8d408bec),
+     TOBN(0xfb4f2797, 0x41cf92eb), TOBN(0xef3f42a0, 0x26f2412e),
+     TOBN(0xdbc0f972, 0xa941ab5a), TOBN(0xc7bd62dd, 0x98337613),
+     TOBN(0x31892744, 0x4e45dcbd), TOBN(0x3b2979cb, 0xb51b7f91),
+     TOBN(0x41e002f5, 0x29b27fec), TOBN(0x9007ee68, 0x4dd51b0f),
+     TOBN(0x82f417a3, 0x6e23d565), TOBN(0x3321f343, 0x77127820),
+     TOBN(0x8d09d965, 0x199b32be), TOBN(0x948429eb, 0x5bc2017b),
+     TOBN(0x22b639f9, 0x124eb248), TOBN(0xed097f74, 0x125f8c22),
+     TOBN(0xdbc02517, 0x5f8bed34), TOBN(0xb93f5b42, 0x51aa29c3),
+     TOBN(0x6fedd599, 0xc7368c44), TOBN(0x99a5a795, 0x2c772a9a),
+     TOBN(0x30b35ba7, 0x7a5f156e), TOBN(0x9dc50978, 0x191c45af),
+     TOBN(0xe8d241f5, 0xb5b4c4fe), TOBN(0xda89eac1, 0xb75f54f8),
+     TOBN(0xb399dba0, 0x9ef86ae5), TOBN(0x2337bb46, 0x51c1b8c6),
+     TOBN(0xfe60b0c5, 0x4d02f348), TOBN(0x709f1235, 0x0afc6cd0),
+     TOBN(0x8a0b458b, 0xb40fce18), TOBN(0xefe143aa, 0xe3929cfa),
+     TOBN(0xab3a4b0d, 0x0ca6cec0), TOBN(0xcb235374, 0x67246ec3),
+     TOBN(0xdf9b0e89, 0x1ec2538f), TOBN(0x3ec2ea13, 0x80c7b53d),
+     TOBN(0x920c55f2, 0xd0ae3146), TOBN(0xd3ac4e1e, 0x43946090),
+     TOBN(0xeba72583, 0x97ebe7a4), TOBN(0x5031644a, 0x393d401f),
+     TOBN(0x802c3409, 0x9714de1a), TOBN(0xc62d66d0, 0xde5bacba),
+     TOBN(0xb6c2abeb, 0x903b8148), TOBN(0x203531ef, 0x5bffe1c4),
+     TOBN(0x186266de, 0xe862ead7), TOBN(0x21e643d5, 0x1a23bebb),
+     TOBN(0x15c13d11, 0x6edda603), TOBN(0x39b4a3a3, 0xb1bebc77),
+     TOBN(0xdb456c1a, 0xb9ac4754), TOBN(0xf497e6e9, 0x4d3f305a),
+     TOBN(0x84d27e3a, 0x3fa62dc0), TOBN(0xc18c3569, 0x2524b94f),
+     TOBN(0x92198954, 0xe380f5cb), TOBN(0x81d8221c, 0x272ea458),
+     TOBN(0x6fa082f6, 0x5f328491), TOBN(0x810ca5af, 0x8e304ccf),
+     TOBN(0xda9f1c15, 0x0d76e6d5), TOBN(0x4bd38afc, 0xb7abad72),
+     TOBN(0x14b5cc26, 0x08aa20f5), TOBN(0x010a1af8, 0x81061318),
+     TOBN(0xaf9d7a73, 0x03c287c0), TOBN(0x9ba5105a, 0xbc4d40ab),
+     TOBN(0x99e4b824, 0xb07937a6), TOBN(0x026d294c, 0xc869f63c),
+     TOBN(0xaaebde75, 0xc910b521), TOBN(0xc803ded4, 0xa7d5dd9c),
+     TOBN(0xc8b713b0, 0x62764be2), TOBN(0x5ea9ea2b, 0x92540cf3),
+     TOBN(0xbaa999c6, 0x6930bd0e), TOBN(0x57052e53, 0x1f4b254c),
+     TOBN(0xfb9fd699, 0x2b0b27ee), TOBN(0x86b70932, 0x4cc83793),
+     TOBN(0x09ab4dd7, 0xfba59bbe), TOBN(0x83204fee, 0x04f4609e),
+     TOBN(0x251cb390, 0x93934977), TOBN(0x8add9e8b, 0x647c13e8),
+     TOBN(0x444815d3, 0xe7ea7006), TOBN(0x22333c0a, 0xbd032c32),
+     TOBN(0xe7728dc8, 0x4058b7cb), TOBN(0xde8eb503, 0xd1bc061f),
+     TOBN(0x5d3ece2e, 0x493d76c2), TOBN(0xa425f3ae, 0xd804954a),
+     TOBN(0x49100271, 0xeac95eb8), TOBN(0x94e4dfa0, 0x38b4be56),
+     TOBN(0xa855893f, 0x650f9930), TOBN(0x1fa0a07d, 0x50264765),
+     TOBN(0x37a3c169, 0x0d1d40be), TOBN(0xfedb51e4, 0x2eed2a0d),
+     TOBN(0xa6e0c2b2, 0x1b3348b4), TOBN(0x9e361f42, 0xc414464e),
+     TOBN(0x3e14e2ee, 0x176e109a), TOBN(0x5f1a6bbe, 0xf4af92fd),
+     TOBN(0xf15d4647, 0x84beb8e5), TOBN(0xac3f01c1, 0x97d36132),
+     TOBN(0x36e669bf, 0x84ca42ae), TOBN(0xf789bdbd, 0xd9433ca1),
+     TOBN(0x384f37f4, 0xf71e84d4), TOBN(0x57de9473, 0x59d6481a),
+     TOBN(0xa9a81f99, 0xf5e6fa70), TOBN(0x26f0a64f, 0x6cb57bf3),
+     TOBN(0xc07e1c13, 0x061d38fe), TOBN(0x6fae70e9, 0x4a475732),
+     TOBN(0x6cfb6b1d, 0x840e595c), TOBN(0xb23cf1f2, 0x62848351),
+     TOBN(0xef6094c7, 0x4fcf8743), TOBN(0x7dc42218, 0x05fab119),
+     TOBN(0x3207463f, 0x5c220d15), TOBN(0xdf51b3f0, 0x22c4bfb2),
+     TOBN(0x13db445b, 0x1572735b), TOBN(0xd7662537, 0x2f6db574),
+     TOBN(0x692f1e05, 0x7796f888), TOBN(0x9f3d7a5b, 0x33f45593),
+     TOBN(0xb5deb892, 0x313de667), TOBN(0x75c872d7, 0x66a478a8),
+     TOBN(0xb67b5513, 0xc4992428), TOBN(0xf97e010e, 0xf70fde09),
+     TOBN(0x49b0f053, 0x60ee268c), TOBN(0x981b5141, 0xf67cd321),
+     TOBN(0xb5a1ac8d, 0x4fbc187c), TOBN(0x162417e2, 0xc12e6da8),
+     TOBN(0x07bb6fff, 0x62914938), TOBN(0xd385285b, 0x19f44438),
+     TOBN(0x05a610a1, 0xa28904dc), TOBN(0xd80a7099, 0x5a29b9f8),
+     TOBN(0x72ccb553, 0xc177af4a), TOBN(0xac0bd91b, 0x5e3752f4),
+     TOBN(0x8e8ae668, 0x7ae838a0), TOBN(0xcaa5a46c, 0x1fdfe7c3),
+     TOBN(0x2cc2c1a5, 0x93d34156), TOBN(0x22beffb1, 0x61fe4572),
+     TOBN(0x66f9f3ce, 0xfcdc7418), TOBN(0xbaccda41, 0x6af66892),
+     TOBN(0x775c783d, 0x1af43f56), TOBN(0x1b23b187, 0x0ae04034),
+     TOBN(0x5a9325f4, 0xe99b486b), TOBN(0x36d5bfe9, 0x8367ab05),
+     TOBN(0x17d8d2fb, 0xa519d028), TOBN(0x27b6beb2, 0xbe00e7e0),
+     TOBN(0x8d51c36c, 0x15a3f03a), TOBN(0xbf90e78b, 0x5faac8dd),
+     TOBN(0x4e8c28e7, 0xb27ab69f), TOBN(0x37ecf0c7, 0x4a553da4),
+     TOBN(0x3a39682f, 0x210fe720), TOBN(0x60c62e80, 0x0b0cdd0c),
+     TOBN(0x893aa225, 0xa16647cd), TOBN(0xcffb728e, 0x64ce0455),
+     TOBN(0x81891d39, 0xc4f0fe79), TOBN(0x1abe3073, 0xf9c39f00),
+     TOBN(0x88336c27, 0xf961d05c), TOBN(0xc9033a88, 0xa5fc96df),
+     TOBN(0x0d084405, 0x864b39f8), TOBN(0x866aa904, 0x851e95c9),
+     TOBN(0x0c36da08, 0x98bae4a8), TOBN(0x9f88d799, 0xb5feb202),
+     TOBN(0xcd9aeb4a, 0x8054e4da), TOBN(0x005206bf, 0x1e9134cb),
+     TOBN(0xd5f32bf8, 0x17ee6649), TOBN(0x9431dcd8, 0x60847ad2),
+     TOBN(0xbe6d62c7, 0x8a3e4050), TOBN(0x3ae68f7a, 0xedf10d40),
+     TOBN(0xa95c9ea0, 0x4604d71f), TOBN(0x01aa3fea, 0x415f8028),
+     TOBN(0x3dd55ca5, 0x5a41970a), TOBN(0x05978ad4, 0x0b5776b4),
+     TOBN(0x7c9f5bdd, 0x787fe20c), TOBN(0x23b9fb7d, 0x75fdba0b),
+     TOBN(0xfb1a724d, 0x5fcf3a0f), TOBN(0xd63b3515, 0x87817071),
+     TOBN(0xecae282d, 0x44e40138), TOBN(0x8732df23, 0x87605748),
+     TOBN(0x0ef49da0, 0xd11188cb), TOBN(0xc0478138, 0x51146cc0),
+     TOBN(0x4ba42323, 0x46621921), TOBN(0x8836dd44, 0x47dfa4eb),
+     TOBN(0xdb6a0100, 0x8ec16442), TOBN(0xabdd9b81, 0x9cdd2e87),
+     TOBN(0x205ee262, 0x502e26d1), TOBN(0xb961ef9c, 0x3294e240),
+     TOBN(0x7178f1fb, 0x6da7733d), TOBN(0x989b69fb, 0x232ecf73),
+     TOBN(0xb7278a35, 0x9a9bccae), TOBN(0xb1c81a0b, 0x400a01f3),
+     TOBN(0x0781855a, 0xa6b213ba), TOBN(0x8acc1b78, 0x3429817e),
+     TOBN(0x527e3a9f, 0xfb4e1aae), TOBN(0xc18c1cfd, 0x4c0b0f4c),
+     TOBN(0x0676c365, 0x1fa7d9f0), TOBN(0x3314509f, 0x4454cc7c),
+     TOBN(0xb0f45371, 0xc7c48245), TOBN(0x913fe759, 0x695ef470),
+     TOBN(0xbb676070, 0xc8d3e0ad), TOBN(0x0db98fcc, 0x902e1638),
+     TOBN(0x42874e9c, 0xfc4dfaa8), TOBN(0xcbf89462, 0x7084b2cb),
+     TOBN(0xd6d46f77, 0x8a846ab8), TOBN(0x9e4506ca, 0x14c183b1),
+     TOBN(0xc2d0f9b7, 0xc53b7631), TOBN(0xe47c3d8f, 0x294d6c34),
+     TOBN(0x04e3c868, 0xc05d3f1c), TOBN(0xbacec4f3, 0xa5957fef),
+     TOBN(0x4f4530ba, 0x3b77893e), TOBN(0x4c234f54, 0x69a18bd9),
+     TOBN(0xb45aadd8, 0x5071f5e3), TOBN(0x73e4160a, 0xd1bd0b86),
+     TOBN(0x43fcb30d, 0x1c474f64), TOBN(0xedef0769, 0x617d1612),
+     TOBN(0x92076734, 0x0eec330e), TOBN(0xd7767770, 0x5b0a21b5),
+     TOBN(0x4b7dea31, 0x183e26f4), TOBN(0x59d6ff20, 0xc9fd2e70),
+     TOBN(0x7bdea00f, 0xd5d914f5), TOBN(0xc736dd0d, 0x56376640),
+     TOBN(0x593ae6ef, 0x38ae8300), TOBN(0xdafe49f1, 0xdf0355bf),
+     TOBN(0x094ccd86, 0x0db4491f), TOBN(0x32295701, 0xfe4271ab),
+     TOBN(0x2b7690e4, 0x5db7014e), TOBN(0x1bbc9c36, 0xd7766bfb),
+     TOBN(0xc52249f0, 0x7d986d0c), TOBN(0xc7eec37b, 0x324f20ae),
+     TOBN(0xd376afa3, 0x0e912329), TOBN(0xbc35e949, 0x04268fa3),
+     TOBN(0x617bf765, 0x9e91a4ac), TOBN(0xb1e932ed, 0x1d483ecc),
+     TOBN(0xd4e31672, 0xac493266), TOBN(0x1c779fe2, 0xecdafb85),
+     TOBN(0xed09eb4a, 0x06280499), TOBN(0x3dd8d965, 0xcd4e1f33),
+     TOBN(0x0fb4308d, 0xf34576dc), TOBN(0xa8ccbf5e, 0x85781a43),
+     TOBN(0x8dbf488a, 0xce623a24), TOBN(0xb0e71d30, 0x6118cfd4),
+     TOBN(0xfc68da68, 0x8cc9d957), TOBN(0x7e5e6b65, 0x83815670),
+     TOBN(0x2c16f5ef, 0x3f185dfe), TOBN(0x23a4098b, 0x98952b33),
+     TOBN(0x15a80298, 0xd515f551), TOBN(0x71a2e7fc, 0xa7f8f341),
+     TOBN(0xed42b1b6, 0x8cf4f7b6), TOBN(0x02743db2, 0x1504d390),
+     TOBN(0x2bded3a8, 0x3016e513), TOBN(0xa3c508af, 0xfb0f7bfb),
+     TOBN(0xa6a490de, 0xaa2be716), TOBN(0x5a04d9e5, 0xf4485b9f),
+     TOBN(0xd07b99d1, 0x6ad25b5d), TOBN(0xa1840109, 0x65a72cb4),
+     TOBN(0xc8e2b32d, 0x14c45a95), TOBN(0x0fae6e86, 0xe4f2ecff),
+     TOBN(0xd09f454b, 0xd94b6fe7), TOBN(0xa776a633, 0x23006b62),
+     TOBN(0x6c700a1c, 0xd332b4b9), TOBN(0x50c3fb34, 0xce016225),
+     TOBN(0x4b805bc3, 0x8af71463), TOBN(0x049143e2, 0x5f1fb3b7),
+     TOBN(0xbcaf4b61, 0x5a6d1dd3), TOBN(0x02093dd7, 0x4733abac),
+     TOBN(0x1a23c3f6, 0xdf59f061), TOBN(0x87a6c141, 0x80c4efb7),
+     TOBN(0x47635ae4, 0xd88e4363), TOBN(0x75e2089f, 0xbf8d2072),
+     TOBN(0xa2bc1b27, 0xac83803b), TOBN(0x8ae61522, 0xe2aafecf),
+     TOBN(0x4b459205, 0xd0010193), TOBN(0x900f6a31, 0x9205f876),
+     TOBN(0x49cddbc9, 0xf808f044), TOBN(0x94637692, 0x95094ead),
+     TOBN(0x3c9c7c0c, 0xb87c9bbf), TOBN(0x1699670a, 0x4e1844d1),
+     TOBN(0xd8a978f2, 0xcbcf85c3), TOBN(0x83e7b806, 0x6a36e1c9),
+     TOBN(0x6f28a73f, 0xfaff9c52), TOBN(0x51341222, 0xb71eaa80),
+     TOBN(0x195461da, 0x9328a676), TOBN(0xefcc93e5, 0x21766180),
+     TOBN(0xed82c930, 0x771a5485), TOBN(0x34f15ce0, 0x205a8bff),
+     TOBN(0x88ab72cb, 0xb8b3bfd8), TOBN(0xbb59a5be, 0x8110fe55),
+     TOBN(0x9ce8a082, 0xc7d61a31), TOBN(0xfe81d072, 0x5b1c63d2),
+     TOBN(0x9fae0be1, 0xe9ff8421), TOBN(0x4254f89d, 0x967e13a6),
+     TOBN(0x1c094620, 0x35da926f), TOBN(0x84eda272, 0x4a76583d),
+     TOBN(0xa4033064, 0xe0e0ffb8), TOBN(0x47951945, 0xabc72d0c),
+     TOBN(0x0af6bb4c, 0xb72c32e7), TOBN(0x6c73357b, 0xda797f9e),
+     TOBN(0xd7a726c9, 0x2ac2e99d), TOBN(0xf44b4731, 0xcd62e7cc),
+     TOBN(0xf89f8e29, 0xe6225822), TOBN(0xa44bb9b0, 0x8d713d92),
+     TOBN(0x3291e8d3, 0x9404f6c6), TOBN(0x50b7a4ff, 0x37bdb22d),
+     TOBN(0xe008662e, 0x216a0f13), TOBN(0x150fa2d6, 0xcf382547),
+     TOBN(0xe5e47c55, 0x3138acbc), TOBN(0x595cf1e2, 0x40d7f3db),
+     TOBN(0x2872392d, 0x2ee1949d), TOBN(0xdbd15bf8, 0x8a4fb721),
+     TOBN(0x30e78cdc, 0x183351dc), TOBN(0xa39b8efb, 0x6b294729),
+     TOBN(0x0df4d23e, 0xc7b553e8), TOBN(0x434f38fa, 0x659d3ffc),
+     TOBN(0x1764115e, 0x55a0c931), TOBN(0x34ea18b9, 0xa5c920a4),
+     TOBN(0x6a099ddc, 0xaf903710), TOBN(0x4b937dc1, 0xe49f2c7a),
+     TOBN(0xacfc4a1a, 0x430f0a7e), TOBN(0x8f106a58, 0x421dbe96),
+     TOBN(0x48ac7026, 0x1811d3fe), TOBN(0x5484226a, 0xb80f13c5),
+     TOBN(0xf692e17b, 0x8da7ca79), TOBN(0x4827aaa2, 0x718691b9),
+     TOBN(0x881f1c38, 0x5c5ea68c), TOBN(0x1620f5d6, 0x88bdf643),
+     TOBN(0xe5703cb2, 0x0b9a5add), TOBN(0x392e6ea5, 0xbe925061),
+     TOBN(0x2a66ce20, 0xb0bab0d5), TOBN(0x83a8e0e5, 0xf98e8dad),
+     TOBN(0x53532223, 0xdeec2329), TOBN(0x6a740238, 0x346eea96),
+     TOBN(0xa54afbdf, 0x1dde2a6a), TOBN(0x0e6ca8c1, 0xf2b5b190),
+     TOBN(0xcccaa3c6, 0xf3cd4e46), TOBN(0x168d66bd, 0x0eb7bb3c),
+     TOBN(0xf1275144, 0x08d4f4e9), TOBN(0x2ae8c946, 0x139811fc),
+     TOBN(0x4973c726, 0xc870713a), TOBN(0x298465ee, 0xba54b13f),
+     TOBN(0x9f901403, 0x940f224f), TOBN(0x5cd6a07b, 0xb9950a40),
+     TOBN(0x9d4095e6, 0x069a8484), TOBN(0xe6bf3181, 0xd4f8831f),
+     TOBN(0x37ceb29a, 0x39243da8), TOBN(0xb3887f31, 0x2693653c),
+     TOBN(0x685d2172, 0x42c98a56), TOBN(0x350fbab8, 0x3969dd9a),
+     TOBN(0x728edca9, 0xe8ac84ec), TOBN(0xf42beab3, 0x59bbb0c4),
+     TOBN(0x9793e746, 0x27d3c3fd), TOBN(0xbf6016de, 0xc732b37e),
+     TOBN(0x3688173a, 0xdf0f248f), TOBN(0x84fbd040, 0x7ed59dfa),
+     TOBN(0x2bad638f, 0xa6731b1b), TOBN(0x1c7b4b13, 0xb7355979),
+     TOBN(0xf21550e0, 0xb8e77093), TOBN(0x14d0bc9d, 0x53efc63c),
+     TOBN(0x119ae9fb, 0xd56e1f29), TOBN(0x3511309c, 0x4d60bc5a),
+     TOBN(0xec654f06, 0xe3574e43), TOBN(0x2d6695df, 0xbef6aea2),
+     TOBN(0x27ece611, 0x5d6abff7), TOBN(0xa706d42d, 0x640c9ab8),
+     TOBN(0x7a7252d9, 0x5a6f8fa6), TOBN(0x32be7195, 0x349aaf8c),
+     TOBN(0xffb48a3d, 0xff734e23), TOBN(0xa9b36c82, 0x7d27b99c),
+     TOBN(0x85b7a57e, 0x0ccaedbc), TOBN(0xb93b14fd, 0xc04f2394),
+     TOBN(0x3a3a78c5, 0x160700e0), TOBN(0xbd7ae60a, 0x961e4af8),
+     TOBN(0xe1deb736, 0xd9472cd7), TOBN(0x276b51b7, 0x3880bbbe),
+     TOBN(0xcf0c4b9a, 0x1aa99bfb), TOBN(0xaf949d5f, 0x689d7f58),
+     TOBN(0x00878488, 0x65f33328), TOBN(0x0f1a178c, 0xe7d7b484),
+     TOBN(0xd44550f8, 0x849e6d32), TOBN(0xe7bc29d4, 0xfe16485e),
+     TOBN(0x29bbfec6, 0x2f343924), TOBN(0xeeb802f2, 0x40f2b5ce),
+     TOBN(0x2b337542, 0xbbb64f33), TOBN(0x4c1d3a36, 0x9f9bdb3c),
+     TOBN(0x1067cf3b, 0xc7a1cb88), TOBN(0x3f12a31d, 0x4601fb6e),
+    },
+    {
+     TOBN(0xb720a78f, 0x1f8a4a91), TOBN(0x59e22211, 0x753dbe73),
+     TOBN(0x9f5ad99c, 0xadd0991a), TOBN(0x3a0db802, 0x7380726f),
+     TOBN(0x37f0761c, 0x7dfb4f1c), TOBN(0x68e7098a, 0x5ac819cd),
+     TOBN(0x9683d610, 0x37ffe348), TOBN(0x5bf205e5, 0x2b7b5140),
+     TOBN(0x9846b5f6, 0x61a97262), TOBN(0xedf2cacb, 0x974a82f7),
+     TOBN(0x3dfab85f, 0xaf439654), TOBN(0x43fb0ef9, 0xc724ee09),
+     TOBN(0xd0d5016f, 0x53b0119a), TOBN(0x68445363, 0x5bc8fc81),
+     TOBN(0x6d10b649, 0x1f65d298), TOBN(0x0f3c88c6, 0x21a4e64f),
+     TOBN(0x320372a1, 0x7f34c517), TOBN(0x5602bd16, 0x2378bc27),
+     TOBN(0x666a592d, 0x91aae024), TOBN(0x716886ab, 0x317bbdaa),
+     TOBN(0xce55fe68, 0xe3045103), TOBN(0xf2c4b0b2, 0x7de1d701),
+     TOBN(0x8da35885, 0x7d724cb6), TOBN(0x9aac623c, 0x9ec47314),
+     TOBN(0x824cff46, 0xb8529a01), TOBN(0x6e4d82a2, 0x4856b95c),
+     TOBN(0x58c6b833, 0xc65af7f7), TOBN(0x8a6c4125, 0xae110e53),
+     TOBN(0x38207c30, 0x4f083340), TOBN(0x71aa384b, 0x176cdb31),
+     TOBN(0x1ada2941, 0x42882de1), TOBN(0x38b1ad2e, 0xc16a2e4a),
+     TOBN(0xbdda2720, 0x142bcb30), TOBN(0x56175263, 0xfaf604d1),
+     TOBN(0x086189c1, 0xe6796314), TOBN(0xdab01c68, 0x5b04dd19),
+     TOBN(0xce54e4b0, 0xba8ed3c1), TOBN(0xf616513b, 0xe281acfb),
+     TOBN(0xaf179629, 0x5e0a6319), TOBN(0x85e79ac9, 0x328b587b),
+     TOBN(0x11d84588, 0xc9fd7da0), TOBN(0xa78682d0, 0x1238d0c4),
+     TOBN(0x333ddde0, 0x829d6475), TOBN(0x80c88440, 0x69de9e18),
+     TOBN(0x5d15f21a, 0xc6d8176f), TOBN(0xdaff9434, 0xa509d470),
+     TOBN(0x0191bb0a, 0x8bbbfcd5), TOBN(0xff7732b8, 0x08fc2688),
+     TOBN(0x02fe772d, 0x5ab3d89e), TOBN(0xf1580ec9, 0x9a786c91),
+     TOBN(0x8fd83417, 0x5a323866), TOBN(0x93711d49, 0xbadec96f),
+     TOBN(0x2020c34a, 0x6b9b4a30), TOBN(0xbf10e000, 0xb8b0de24),
+     TOBN(0x2a5f298d, 0x28de3ce5), TOBN(0x807a398e, 0xfe1a1c63),
+     TOBN(0x9fb640cd, 0x73f7c45c), TOBN(0xeb1f87ad, 0x0afe059c),
+     TOBN(0xa3c3979a, 0x52b168d4), TOBN(0x6eef460c, 0x7b1e403f),
+     TOBN(0x6d943e50, 0x2724bb3f), TOBN(0x53f3f1bb, 0xf9d922d1),
+     TOBN(0x547e7a03, 0xcd538b4a), TOBN(0x37631e20, 0xd2c4145f),
+     TOBN(0xe7e49922, 0xb1f810bf), TOBN(0xacafdb0f, 0xf2645825),
+     TOBN(0x0f22216a, 0x15f35bda), TOBN(0x6f2b4d95, 0xd85bd0b7),
+     TOBN(0x2f203db8, 0xbedc9ecd), TOBN(0x26639ff6, 0xb91e090d),
+     TOBN(0x94cd6596, 0x3486eb84), TOBN(0x32747db3, 0x42c05747),
+     TOBN(0xcd3e7a52, 0xcebfa9f1), TOBN(0x5e792d76, 0xfb2b3007),
+     TOBN(0x9669523d, 0xb9ecce81), TOBN(0x9263cc85, 0x04f191e1),
+     TOBN(0x192019c0, 0x69655fe1), TOBN(0x1c5cc5eb, 0x4d984e59),
+     TOBN(0x9ad10ed6, 0xdf33f336), TOBN(0x0ca48387, 0x41d94897),
+     TOBN(0xbd1ddf67, 0xf222476c), TOBN(0xb4ad7126, 0x12d6dc4d),
+     TOBN(0x5c327b18, 0x93ed702a), TOBN(0x7e3a27b1, 0xfa70cd9f),
+     TOBN(0xdca750bd, 0xc0c4f415), TOBN(0x98197c90, 0x213a5d61),
+     TOBN(0x9bbd014a, 0x6f10fcc7), TOBN(0xb06061e1, 0x2ceed4fb),
+     TOBN(0xaf6dbbe2, 0xa8ad25f9), TOBN(0xe70e9f40, 0x7ade697d),
+     TOBN(0xb829e016, 0x6eb872d7), TOBN(0xc330e15c, 0x1b04173f),
+     TOBN(0xd4868e29, 0x0d4763d8), TOBN(0x37867f72, 0x4c18c9fb),
+     TOBN(0x5fd2f47f, 0x28019486), TOBN(0xe6bfdf81, 0xb16e9bdd),
+     TOBN(0xace2a977, 0x783e43c5), TOBN(0xe1791288, 0x76eed46a),
+     TOBN(0x3884a5b2, 0xd1767739), TOBN(0x14eddddb, 0x427c50a3),
+     TOBN(0xbeeed5ac, 0x1c9b1fcc), TOBN(0x50b1cb44, 0x4ecdb47a),
+     TOBN(0xcbf69555, 0x0dcb78d5), TOBN(0xe60bf9c7, 0xf2b17a99),
+     TOBN(0x0edae6b0, 0x9e9ade95), TOBN(0xb5c6e13d, 0xcb78b1e1),
+     TOBN(0x32860fba, 0x1c257848), TOBN(0xfc9aa9f4, 0xef7f5080),
+     TOBN(0xccef8508, 0x32aac870), TOBN(0x4b237704, 0xfb5310a0),
+     TOBN(0x4c3cf970, 0xfeebb972), TOBN(0x5dd3c7a0, 0x763d5f67),
+     TOBN(0xa656797e, 0xccbf29c6), TOBN(0x6d77f211, 0x5a76a56b),
+     TOBN(0xc627156b, 0x0e3daff3), TOBN(0xa4bd37f5, 0x7646fb1c),
+     TOBN(0x5fd7e286, 0xa8cd3e5a), TOBN(0x3889951a, 0x2f5fed51),
+     TOBN(0xf8186fc5, 0xe48c49be), TOBN(0x0d3d308a, 0xc662ee38),
+     TOBN(0xb7c9bf06, 0x970e164d), TOBN(0xc27a88d8, 0xbd3d3087),
+     TOBN(0x8a37c9cd, 0xf4e7c899), TOBN(0x18494d5a, 0xab411371),
+     TOBN(0x06532375, 0xd9d8b29c), TOBN(0xb92dd45c, 0x915a2f74),
+     TOBN(0x8a23f6bf, 0x515acb02), TOBN(0x0e69248c, 0x435bfa89),
+     TOBN(0x8bf41ec3, 0x6866c5e4), TOBN(0xf059e652, 0x0999159d),
+     TOBN(0xf906838f, 0xd29d7cd8), TOBN(0xc30100f6, 0x3a269735),
+     TOBN(0xb7742bc8, 0x6280e70b), TOBN(0x0067d971, 0x867b54e1),
+     TOBN(0xafe9032b, 0xf544622a), TOBN(0x6b441e39, 0x118a2042),
+     TOBN(0x905c8655, 0xcdd66b70), TOBN(0xe88cce1b, 0xc1e2110d),
+     TOBN(0x8cc23c0c, 0xee674093), TOBN(0x55ded4d9, 0xb2ea3fc3),
+     TOBN(0xdd14502b, 0xb58dfbed), TOBN(0x523a4dd9, 0x49f698f8),
+     TOBN(0xf843a501, 0x01c83e5a), TOBN(0xf11fd4c1, 0xfe71ee1e),
+     TOBN(0xeedd7229, 0x162d7c0b), TOBN(0xd42d6a9e, 0x4ccad713),
+     TOBN(0xa082fffd, 0x2b0c7b93), TOBN(0xee3abd48, 0x2a5016b9),
+     TOBN(0x5079c95f, 0xc117e22b), TOBN(0x5d4b9169, 0x814b8666),
+     TOBN(0x9e0f5e87, 0x9bf90a6d), TOBN(0x4346bd29, 0x744bf7ab),
+     TOBN(0x4d85af0e, 0xbfb551b6), TOBN(0xb48e3da8, 0x31f7a958),
+     TOBN(0x3b474ce6, 0x6f5bc50d), TOBN(0x9fdb47bc, 0xe7c8dced),
+     TOBN(0x2064450e, 0x53003272), TOBN(0x6bb230f3, 0x839e69da),
+     TOBN(0xb6941512, 0x4d822be5), TOBN(0xb51bc6aa, 0xf11a9dc1),
+     TOBN(0x866447f8, 0xb23047dc), TOBN(0xe02dbd63, 0xe5f52c2d),
+     TOBN(0xe6ea43cb, 0x02770a76), TOBN(0x853f5fe3, 0x56fa6c25),
+     TOBN(0xfe9615f0, 0x960de6d5), TOBN(0x37c8b0c8, 0xf4b1b945),
+     TOBN(0xa6e83805, 0x4618629d), TOBN(0x38fb5264, 0x23a2ac61),
+     TOBN(0x5dfd7005, 0x01751c20), TOBN(0x7e100245, 0xce72773a),
+     TOBN(0xdf09f92a, 0x0776794a), TOBN(0xc4a8de81, 0x1b730fdc),
+     TOBN(0x72c302ab, 0xf0c7b031), TOBN(0xdddff68e, 0x1283913b),
+     TOBN(0x24889098, 0xe32517b5), TOBN(0x2483a0f5, 0x856a2934),
+     TOBN(0xdf6d7dcc, 0xa1c3d56d), TOBN(0x07f9c00b, 0x09afb797),
+     TOBN(0xe90da23d, 0x083d9557), TOBN(0x80ae6e53, 0xcbc03826),
+     TOBN(0x1fd6ff6d, 0x7c0e1b23), TOBN(0x1e90f3c8, 0xb1100226),
+     TOBN(0xf179e00e, 0x05a24e23), TOBN(0xe5361efe, 0x946f16bd),
+     TOBN(0x50f12e4a, 0x4c662091), TOBN(0xdad2c7a3, 0x28608585),
+     TOBN(0x55c66749, 0xf7429473), TOBN(0x440b77de, 0x045ea1b4),
+     TOBN(0x9f707b49, 0x91229927), TOBN(0x3501e29e, 0xc6725715),
+     TOBN(0x5626fabb, 0x1225a8e6), TOBN(0x270a9c2b, 0x9507e709),
+     TOBN(0xe0d629da, 0xbdcb9039), TOBN(0xb4d7cd22, 0x20255b7c),
+     TOBN(0x10c8614b, 0x5ed874a6), TOBN(0x36891e70, 0x4e67d406),
+     TOBN(0x020da834, 0x1dce66fe), TOBN(0xae69e1e7, 0xabd64dea),
+     TOBN(0x9cf153a1, 0xcc71b37b), TOBN(0xa6e9d024, 0x44771c7e),
+     TOBN(0xb15e31c7, 0x8840fc17), TOBN(0x57853112, 0x349124a4),
+     TOBN(0x78a9d807, 0xbac542ee), TOBN(0xe7b4d812, 0x38fe1188),
+     TOBN(0x874adc70, 0xb3a3b801), TOBN(0x80c0e02a, 0x4694cec2),
+     TOBN(0xd05c8c0e, 0xe97805e1), TOBN(0x8eaebceb, 0x89d8cd40),
+     TOBN(0x888c777b, 0x378d055f), TOBN(0x6956795e, 0xb104a132),
+     TOBN(0xe4bce719, 0xbe8472d7), TOBN(0x23c9f0bf, 0x5f51729e),
+     TOBN(0xfe7f7e19, 0x36a3bf3e), TOBN(0xf8f5d2ca, 0x20a32d37),
+     TOBN(0xf383b467, 0x93b8a344), TOBN(0x7eab76f5, 0x27a6e2c5),
+     TOBN(0x86c31b0e, 0x93b54bc1), TOBN(0xb9405ea9, 0xfc4ecab2),
+     TOBN(0x09485578, 0xa0f6d341), TOBN(0x88053bb8, 0x4b77e8e7),
+     TOBN(0xcde9b777, 0x29a07ddd), TOBN(0xec8ea63f, 0x97649102),
+     TOBN(0xf74d082a, 0xc516777a), TOBN(0xf4e26d89, 0xbacf0dd3),
+     TOBN(0x6a919da8, 0xd0b3b578), TOBN(0x0bcc3b29, 0xa0b5f7d8),
+     TOBN(0xbf4565e5, 0x9e55924b), TOBN(0x13b36187, 0x7889dbb6),
+     TOBN(0xad0e59c6, 0x533981bd), TOBN(0xea941b62, 0x0bd0cb7a),
+     TOBN(0xe5e35e9a, 0xa9e7aa7c), TOBN(0x27f61727, 0x088bfd7d),
+     TOBN(0xda2a5a20, 0x8b3c7fbc), TOBN(0x33cdd403, 0xba55cb48),
+     TOBN(0xb72b51cf, 0x90e7ff36), TOBN(0x8cc4b553, 0x6f215840),
+     TOBN(0xf7b80ad9, 0xd2671224), TOBN(0x560b4387, 0x6a45436b),
+     TOBN(0xdca90694, 0xff9e8fae), TOBN(0x2e7e9546, 0xf97aa84e),
+     TOBN(0x71e9ff45, 0xf37cd717), TOBN(0x6edf335e, 0x0d73e98f),
+     TOBN(0xf355690c, 0x9f715170), TOBN(0xf74df40b, 0x3f5a82bd),
+     TOBN(0x28b6d931, 0x95e5b105), TOBN(0x8827f47c, 0x2841a54c),
+     TOBN(0x159cb943, 0x62b4312d), TOBN(0x277943d7, 0x8db37edb),
+     TOBN(0x561454fd, 0x6113a9f8), TOBN(0x78ebe733, 0xe70e67e6),
+     TOBN(0x8764360b, 0x903f2feb), TOBN(0x2ba3b3d8, 0x97902f36),
+     TOBN(0x28808cef, 0x87490b8a), TOBN(0xb1175954, 0xf05f31b3),
+     TOBN(0xbd5d6005, 0x6c9b4f4d), TOBN(0x12b13fca, 0xdd254e60),
+     TOBN(0x38d4e812, 0x14959566), TOBN(0xe253b750, 0x36fe9a6c),
+     TOBN(0x24b2c81a, 0x809450c1), TOBN(0x0aa89966, 0x8fec36b1),
+     TOBN(0x9a99deb5, 0x053e97e7), TOBN(0x5e57321c, 0xe31d3a6e),
+     TOBN(0xcd7a4f33, 0x8dbe78a2), TOBN(0x9f809d4f, 0x3299e070),
+     TOBN(0xd6de8cfa, 0xa26a9eca), TOBN(0x33d5705b, 0xa158a735),
+     TOBN(0x08dd3fcc, 0xc2293743), TOBN(0x1f8d0a46, 0x68bbbaea),
+     TOBN(0x53ff76f9, 0x61bc4105), TOBN(0x6445e88d, 0x7c4a8fc9),
+     TOBN(0xfd9a8d04, 0xc285d0e6), TOBN(0xf08d0d6b, 0xfe62b449),
+     TOBN(0x08c27292, 0xc062810c), TOBN(0x955629f6, 0x6663fa28),
+     TOBN(0xbaf96c0e, 0x9d86fee8), TOBN(0x1dbc5406, 0x46bb9894),
+     TOBN(0x8d6b6207, 0x93dd45c7), TOBN(0xaf3baef6, 0x3ee989fc),
+     TOBN(0xf66cfdb1, 0x59b7b2f7), TOBN(0x287fc2bf, 0xda16c637),
+     TOBN(0xa44ca8fa, 0x2d71f863), TOBN(0xa1161962, 0x84d5dee5),
+     TOBN(0x5a5c8ce3, 0x3957b610), TOBN(0xdbb32253, 0x17f50b57),
+     TOBN(0xc6a4eb7d, 0x76056358), TOBN(0xff9eb424, 0xc359d90f),
+     TOBN(0xdf4afe23, 0xa88cb38c), TOBN(0x2ae727cb, 0xa696b75d),
+     TOBN(0x47cc63ef, 0xd20a58c8), TOBN(0xd319dc3a, 0xc492ab36),
+     TOBN(0x887a7d83, 0x36c7f76e), TOBN(0x65ed5e3e, 0xfcd4cf49),
+     TOBN(0x0e6f2f34, 0xda301d39), TOBN(0xf2f7c102, 0x38ad4533),
+     TOBN(0x8a3a003b, 0xae834622), TOBN(0x94084169, 0xa060a0d4),
+     TOBN(0xb673168b, 0x13c8a1eb), TOBN(0x80109609, 0x459f8da1),
+     TOBN(0x68003fa1, 0x5c82007b), TOBN(0x9f634159, 0x248e0430),
+     TOBN(0x188156ab, 0xfb9b6510), TOBN(0xc35be1cc, 0xe62844de),
+     TOBN(0x21e8f908, 0xb0c84d39), TOBN(0xa886d3eb, 0xdad3ae53),
+     TOBN(0x9e20cd56, 0x82b0f5fd), TOBN(0xc0c12f0b, 0xc465c721),
+     TOBN(0xfeeb1051, 0x6f913a6e), TOBN(0x9e7c76b9, 0xaa32d6fe),
+     TOBN(0x820b49a0, 0xb8637b5f), TOBN(0xe9ae172a, 0xf4abccf0),
+     TOBN(0xccc050b1, 0xfb270e67), TOBN(0x0b51d7e3, 0x2269d1de),
+     TOBN(0xca772ec1, 0x678c8d8b), TOBN(0x74eea3f8, 0x77ae7c7b),
+     TOBN(0x51550df1, 0x1e1bcbd3), TOBN(0xa931c17c, 0x3458b249),
+     TOBN(0x192c3a45, 0xf204aed5), TOBN(0x93abf63d, 0xc993c881),
+     TOBN(0xc60aa2cb, 0x83421891), TOBN(0x11ce6735, 0xf6b70284),
+     TOBN(0x53e8a3ee, 0x69e152e4), TOBN(0x6889ece0, 0x0033da23),
+     TOBN(0xada56904, 0x7d585418), TOBN(0xaf81a877, 0xf5e5abb9),
+     TOBN(0x36e0267d, 0xdf515727), TOBN(0xe04b532d, 0x3daad2a9),
+     TOBN(0x290e3ee7, 0x1a11ced6), TOBN(0x5be7c429, 0x65e7a651),
+     TOBN(0xc0662cd3, 0x8ef9b498), TOBN(0x0ec5fbf0, 0x6c4dcbf9),
+     TOBN(0x26694c70, 0xce4d7e3a), TOBN(0xc1699a93, 0xfa52de99),
+     TOBN(0x2e0d394b, 0x6dae3e97), TOBN(0xe3af28cf, 0x4c66e572),
+     TOBN(0x9caf7bf8, 0xba1e27e4), TOBN(0xd5c39337, 0xd5a4bdaa),
+     TOBN(0xbb5d9551, 0x9ec8ad6d), TOBN(0xfb3bc1f1, 0x609fc2e1),
+     TOBN(0x0d95ad2a, 0x95fe12b5), TOBN(0xf6fd6e89, 0x5341dc74),
+     TOBN(0x1532991e, 0x7537b803), TOBN(0x77772fd3, 0xeaf96f9c),
+     TOBN(0x4ed09840, 0xf832749a), TOBN(0x69a194ce, 0x95f19d25),
+     TOBN(0x5464471a, 0x041cc340), TOBN(0x26f7e550, 0x1c442289),
+     TOBN(0x38f2c20e, 0xb5ce9706), TOBN(0xcf73f8f2, 0x8a44efd3),
+     TOBN(0x5176eda5, 0x586e8f77), TOBN(0x47e33844, 0x63ece447),
+     TOBN(0x83826e8f, 0x86b00be2), TOBN(0x49cffcdb, 0x539807b7),
+     TOBN(0x543d1fad, 0x414d3fb1), TOBN(0xd56aac6a, 0x38b1ef44),
+     TOBN(0x9980bb64, 0x96c89050), TOBN(0xc300cb46, 0xb169b8a9),
+     TOBN(0x5ab01a6b, 0x83413df4), TOBN(0x179b8922, 0xf3c91eda),
+     TOBN(0x4060b943, 0x43cccc06), TOBN(0x4f6adeb5, 0x9458ec1e),
+     TOBN(0x0a4c6437, 0xe339e40e), TOBN(0x9cb6c532, 0x02aefe83),
+     TOBN(0xb072d02b, 0x23dce7ea), TOBN(0x2cd7b117, 0x59a9032f),
+     TOBN(0x01220cea, 0x81dbfaef), TOBN(0xffe0026c, 0x0905332d),
+     TOBN(0x95ec2cb2, 0x0197adff), TOBN(0x853bf6f5, 0x4c3d0e49),
+     TOBN(0x04ed54fb, 0x25d78f7c), TOBN(0x45aae3e1, 0xbb68cb9f),
+     TOBN(0xf4f1a2c6, 0xe32d7421), TOBN(0x646ade65, 0x45a05771),
+     TOBN(0xab241cfa, 0x91eab45e), TOBN(0xb1cf204c, 0x7b214af0),
+     TOBN(0x92dfb3e3, 0x851d311c), TOBN(0x56479ffb, 0x144ae0da),
+     TOBN(0xbf847444, 0x9a7a4ede), TOBN(0xb26b1f15, 0xf5cfd20f),
+     TOBN(0xf380ed7d, 0x83b33b64), TOBN(0xa21f9564, 0x3d1998c9),
+     TOBN(0xd985c7d3, 0xa720e347), TOBN(0x98078974, 0x8bdf09d5),
+     TOBN(0xa1f34ce2, 0xce947692), TOBN(0xf69e6144, 0xf419c385),
+     TOBN(0xe1926526, 0x8c3adcc6), TOBN(0x848974fb, 0x42746263),
+     TOBN(0xa731261f, 0x97791569), TOBN(0xfed39da2, 0x065b721b),
+     TOBN(0x8369b04c, 0x836a7e20), TOBN(0x5758a761, 0x53c19f62),
+     TOBN(0x45746383, 0x0ebea868), TOBN(0x20179927, 0x3b7d71a8),
+     TOBN(0xb466ed4f, 0x57632243), TOBN(0xc8d918cb, 0x120577c9),
+     TOBN(0xbab307e5, 0xeda40e9c), TOBN(0xe6dbc7d4, 0xd5f65d1b),
+     TOBN(0xcae0c649, 0x60619e10), TOBN(0xffddf6d1, 0x6b0df67c),
+     TOBN(0x60488755, 0xb32ee5d1), TOBN(0xcb278aaf, 0x47164a55),
+     TOBN(0x354c3392, 0x0bfb732d), TOBN(0xcd4fc821, 0x649bc125),
+     TOBN(0xa8e1253f, 0x770ffdb8), TOBN(0xf7eec595, 0x0ff0c37e),
+     TOBN(0xe5a65279, 0x7149b102), TOBN(0x1cbbb56b, 0xd0528224),
+     TOBN(0x40b1a8d9, 0xb51c5df4), TOBN(0xccb43d26, 0x39e1ca25),
+     TOBN(0x48f74dc2, 0xfdcfe8c5), TOBN(0x3ccb31b6, 0xfa5b8daf),
+     TOBN(0x6f8dc5bc, 0x7de6300f), TOBN(0x2a373fd3, 0xf247bc0b),
+     TOBN(0xefe13539, 0x17825306), TOBN(0xeb253484, 0xc50c47b4),
+     TOBN(0x4a7f2af3, 0x3c739f02), TOBN(0x3a3eb385, 0x9a3c6746),
+     TOBN(0xa90afa2a, 0x588978e2), TOBN(0x501fcebf, 0x8d80894f),
+     TOBN(0x1de1d06d, 0x6bf1a4cb), TOBN(0xb0f4a61d, 0x6cc42a07),
+     TOBN(0x975cb8de, 0x78d406f0), TOBN(0x560b0d7b, 0xe3d293e3),
+     TOBN(0x5746227c, 0x32e686ca), TOBN(0xd12854f5, 0x3fcb0205),
+     TOBN(0x8c0eaba8, 0x499512e3), TOBN(0x8d97c229, 0xade99108),
+     TOBN(0xd80da38e, 0xff2b5782), TOBN(0xf8c30ba1, 0xaef08107),
+     TOBN(0x9068d7d0, 0x076b97c3), TOBN(0x851d1cb9, 0xb1b7eba5),
+     TOBN(0x02bb728c, 0x318e4675), TOBN(0x0efe9707, 0x76ddc683),
+     TOBN(0x6985d358, 0x6a248b04), TOBN(0x75eb6019, 0xf8969ed1),
+     TOBN(0xecb66a20, 0x606a0c64), TOBN(0xd1252f64, 0xfe39b5e5),
+     TOBN(0x93d5d61c, 0x2aa222a9), TOBN(0x16c0d6f9, 0x1ffff8ec),
+     TOBN(0x0f1f962d, 0x5dfab0fe), TOBN(0x88776fe1, 0xcedcccb0),
+     TOBN(0x410333c6, 0xa32cbff1), TOBN(0xca13ce28, 0x093bcbda),
+     TOBN(0xd97b0684, 0x0e479259), TOBN(0x8b2b3ad8, 0xbf505c93),
+     TOBN(0x42092d64, 0x71761412), TOBN(0x9d0c842d, 0x918acf33),
+     TOBN(0x904d3add, 0xac9ade57), TOBN(0x025e4177, 0xe0d5ef6a),
+     TOBN(0xce406ec0, 0x0b33d4ed), TOBN(0xf73ac4da, 0x57b5c958),
+     TOBN(0x5f96cb8c, 0x6ef70849), TOBN(0x702ccc6f, 0x77b32d5d),
+     TOBN(0x75bda8d8, 0xcea6885c), TOBN(0xbfc3e62e, 0xc0c0432e),
+     TOBN(0x46db9cc6, 0x54631c9a), TOBN(0x1669075b, 0xba1d1550),
+     TOBN(0x5ccc4e34, 0x2d227656), TOBN(0x0724e41b, 0x02cb0644),
+     TOBN(0xc5e2077d, 0x435601fb), TOBN(0x356155c5, 0x68d6aee2),
+     TOBN(0x0ea00013, 0xfde58906), TOBN(0x79fa13c3, 0x37a9eda4),
+     TOBN(0x7d09479d, 0x8f51a6a6), TOBN(0x86e955b7, 0x1f979fed),
+     TOBN(0x9cb22960, 0xe39ab804), TOBN(0x6aeae783, 0x03535a39),
+     TOBN(0xeb4741de, 0xb9909be6), TOBN(0xb957c5da, 0x1a5f4139),
+     TOBN(0xafdb3e8b, 0xedc1819f), TOBN(0x33545722, 0xe7caa690),
+     TOBN(0x0ef33e28, 0x8bb66ed0), TOBN(0x59073742, 0x70e667b5),
+     TOBN(0x0390fb3c, 0x5c7773b8), TOBN(0xb80b4a2f, 0x286a809d),
+     TOBN(0xd17d6103, 0xfac46467), TOBN(0x9a09a0d6, 0x91a48972),
+     TOBN(0xa2124b62, 0x39e44585), TOBN(0x14c8a671, 0x174d241a),
+     TOBN(0x99abfa37, 0xada8ba26), TOBN(0x847f3040, 0xfbb457ae),
+     TOBN(0x0587aaa4, 0x7529a18c), TOBN(0x23b3f724, 0x9bb45ee6),
+     TOBN(0x4d7f5712, 0x2aa81155), TOBN(0xa9185804, 0xa4f16d09),
+     TOBN(0xab638141, 0x3fc992d1), TOBN(0xb6c326fa, 0x0cad0bb0),
+     TOBN(0xe21c3625, 0x60f2cb10), TOBN(0x6c7af09e, 0x2fac20a9),
+     TOBN(0x31e892fa, 0xdc6f72ab), TOBN(0x71d5c6a3, 0x21b81f7b),
+     TOBN(0xc3e2d70d, 0x298a0dd2), TOBN(0xbc0c37e2, 0x13ecdc80),
+     TOBN(0xd3191146, 0xe6496ba4), TOBN(0x15f81541, 0x35115466),
+     TOBN(0x162be77d, 0x07d1937f), TOBN(0x38b4d194, 0x7b176367),
+     TOBN(0x4485966d, 0xb8cafbc9), TOBN(0x7cfc0d67, 0xf44c2a81),
+     TOBN(0xe9e7ec4d, 0xe624cefe), TOBN(0x4db8bec3, 0x581d4e48),
+     TOBN(0xe76edf00, 0x7fc8615a), TOBN(0x1b62c4a5, 0x9a02cdb8),
+     TOBN(0x8b565749, 0x83938a6d), TOBN(0xd813864e, 0x50c86c19),
+     TOBN(0x7fc071ef, 0x16f55d40), TOBN(0x70195438, 0x9bb45ea5),
+     TOBN(0x83cf09f2, 0xa35543ca), TOBN(0x07e91a84, 0x20554c19),
+     TOBN(0x51ecd701, 0x62a9d06e), TOBN(0x00e14c62, 0x2044a663),
+     TOBN(0xb1317c13, 0x00423dd9), TOBN(0xf49431bc, 0xa46eab4c),
+    },
+    {
+     TOBN(0x35118434, 0xd0614aa1), TOBN(0x8bae9779, 0xd1418434),
+     TOBN(0xf5641d82, 0xb8c15b89), TOBN(0x2383af56, 0x416432eb),
+     TOBN(0xa552d3f0, 0x2c73f990), TOBN(0x8df82e9e, 0xa6bbdc7d),
+     TOBN(0x0f336aa8, 0xd75ec634), TOBN(0xc42e3b2d, 0x1603e53f),
+     TOBN(0x4b33e020, 0xbad830d2), TOBN(0x5c101f9e, 0x590dffb3),
+     TOBN(0xcd0e0498, 0xbc80ecb0), TOBN(0x302787f8, 0x52aa293e),
+     TOBN(0xbfd64ced, 0x220f8fc8), TOBN(0xcf5cebe0, 0xbe0ee377),
+     TOBN(0xdc03a038, 0x8913b128), TOBN(0x4b096971, 0xfde23279),
+     TOBN(0xb0f8c0de, 0xd2d638ad), TOBN(0x47fc8c77, 0x4f299d5f),
+     TOBN(0xd1720a92, 0x9b68d48e), TOBN(0xf944e708, 0xa1c6f103),
+     TOBN(0x36e34e04, 0xa146889b), TOBN(0xb0aad2d6, 0xe74a2a28),
+     TOBN(0xedbb034b, 0xca52f53c), TOBN(0xe987a8e1, 0x87fb2713),
+     TOBN(0x6c5389af, 0xf727ef3a), TOBN(0x95ffeb95, 0x33db88fb),
+     TOBN(0x27cb7042, 0x9dae0777), TOBN(0xd20afe81, 0x616dbf02),
+     TOBN(0x0fab8e18, 0x914bf706), TOBN(0x3b1e66f3, 0x0517cd09),
+     TOBN(0x24b46dce, 0x12e40644), TOBN(0x0ff10168, 0x08f2d8fa),
+     TOBN(0xe08a10df, 0xea2d8d84), TOBN(0xe31f05e7, 0xe97dda79),
+     TOBN(0xfe95f84a, 0x4e9ab132), TOBN(0xacd6f7fc, 0x927e216f),
+     TOBN(0x025e27bd, 0x83c5a3ea), TOBN(0xed010c0d, 0x50f120fc),
+     TOBN(0x443b3b8a, 0xb828101f), TOBN(0xd8384819, 0x8cfc0dea),
+     TOBN(0xe55f34c8, 0x83dc5447), TOBN(0xbe76243b, 0x04e4e9a0),
+     TOBN(0x78fb4cbc, 0x819166a2), TOBN(0x0bdfb703, 0xae37f80a),
+     TOBN(0xf869288e, 0xc217cda8), TOBN(0x2662bb71, 0x62af4156),
+     TOBN(0xce64f291, 0x50ae9d30), TOBN(0xee0d4440, 0xdc0353c9),
+     TOBN(0x3e61a9ea, 0xbd25609c), TOBN(0x4ccaea93, 0xb3839c8b),
+     TOBN(0x721cefa3, 0xe43736e2), TOBN(0x229cb244, 0xd0035908),
+     TOBN(0x936bc1dc, 0x7f10aebb), TOBN(0xc93a1002, 0xb67332e7),
+     TOBN(0xf4b53dd4, 0xf98d1132), TOBN(0x7b99a196, 0xd5a75030),
+     TOBN(0xb13caadd, 0xca9a9526), TOBN(0x701c63fa, 0x69a303e9),
+     TOBN(0xb97d667a, 0xb0a50f3c), TOBN(0x27c03d7c, 0x68e6557f),
+     TOBN(0xab24e712, 0xeb105607), TOBN(0x4936aedd, 0x8dd86ccb),
+     TOBN(0x32196f8a, 0x0a986d68), TOBN(0x0307b826, 0x248f5a65),
+     TOBN(0x20e14b4c, 0xfcadb2ad), TOBN(0x4cb4a092, 0x8c3b8c23),
+     TOBN(0x50fe3c1a, 0x1caa9db1), TOBN(0x23cc56e8, 0x81c0a4e9),
+     TOBN(0x5ab09199, 0x0867753f), TOBN(0x5a253d19, 0xf9d47c55),
+     TOBN(0x422b4e03, 0x1a9bcc88), TOBN(0x4e1ce22b, 0x671e4f36),
+     TOBN(0x588f58b5, 0xebbe949f), TOBN(0xb7762296, 0x6982215b),
+     TOBN(0x3cc83dd6, 0xcff863c0), TOBN(0x81ec094d, 0x01098f51),
+     TOBN(0x214d69aa, 0xbe0432d0), TOBN(0xe4e52a9c, 0x6455957d),
+     TOBN(0x94743ba8, 0xfadc1eab), TOBN(0x2c395d97, 0x8176316f),
+     TOBN(0xeab6400c, 0xe6bb4d34), TOBN(0x7364dc55, 0xc0d49bf2),
+     TOBN(0xd6fa6e40, 0xe6959c7e), TOBN(0x7960a997, 0x7eaae61c),
+     TOBN(0x918b3c63, 0x94ea77c2), TOBN(0x2cf4997f, 0x76866dd1),
+     TOBN(0xc4214abf, 0xbcbba8ca), TOBN(0x349a6133, 0x7aa4aab2),
+     TOBN(0xd64bab77, 0x99458b24), TOBN(0x6fe19e25, 0x2eba3064),
+     TOBN(0x9aabd83d, 0x74068f03), TOBN(0xaef81218, 0x6fdf8655),
+     TOBN(0xf506d27b, 0x65593fef), TOBN(0x0a1ad85d, 0xfaa457b2),
+     TOBN(0x266d0f06, 0xa303dff4), TOBN(0xe8114f4e, 0xabb416e3),
+     TOBN(0xe743f617, 0x6aa5a1b8), TOBN(0xaf84652d, 0x1b5b2bd6),
+     TOBN(0x8b1beab1, 0x092e2c46), TOBN(0x7e857549, 0xe2518383),
+     TOBN(0x6be2ece1, 0xa9383124), TOBN(0x8309442a, 0x7fc20081),
+     TOBN(0x1f00eb8b, 0xc3046cab), TOBN(0x959f3155, 0x26f39f8c),
+     TOBN(0xaacfe2d3, 0x8fc2ed93), TOBN(0x83446645, 0x78f0f858),
+     TOBN(0x58de6f09, 0xdda35ec4), TOBN(0x891e5ecd, 0xf78c69b1),
+     TOBN(0xff4a4ba9, 0x91c13d67), TOBN(0x6e78063d, 0x487d5575),
+     TOBN(0x226b621e, 0x8d303a7e), TOBN(0x5c9bc103, 0xc95987ed),
+     TOBN(0x28980108, 0x5e3be13e), TOBN(0x5e8c0ac5, 0x414af955),
+     TOBN(0x0f08e93b, 0xeaaa71a5), TOBN(0x1bc50407, 0xce4524f0),
+     TOBN(0x6a6a2e6a, 0x921be66b), TOBN(0x37113baa, 0xc27da9f2),
+     TOBN(0xc7b3c636, 0x52e90e29), TOBN(0xc075d178, 0xc8558307),
+     TOBN(0x605f581a, 0x88a45b65), TOBN(0xcb789200, 0x68e58c1c),
+     TOBN(0x14cbed65, 0xbc5bfe1c), TOBN(0xd1af7dc7, 0xf02b11d7),
+     TOBN(0xb8341bc0, 0xcd3a7cc8), TOBN(0x8e9aefe8, 0xa01a77b7),
+     TOBN(0x8eeafe87, 0x5ae2a402), TOBN(0x27a0698f, 0xc11f3859),
+     TOBN(0xc5e49f07, 0xf7af9756), TOBN(0xffd65bcc, 0x9e5b871f),
+     TOBN(0x62a95357, 0x423eed7b), TOBN(0x93cf64d5, 0xb2ec687c),
+     TOBN(0x04b87dd7, 0xbe5da479), TOBN(0xdcceabd7, 0x1a134c0b),
+     TOBN(0xa4875091, 0xc5c6925c), TOBN(0x3bf947df, 0x8e9c098d),
+     TOBN(0xb2617271, 0x11d1323b), TOBN(0x7769247c, 0xe4c6046d),
+     TOBN(0xf9c1aaab, 0xcfa6aac3), TOBN(0xf7f13317, 0x354492e2),
+     TOBN(0x4bd65afd, 0xe91befb6), TOBN(0x3e78cd8c, 0xf25b8f8d),
+     TOBN(0x2adf53ed, 0xe60ff4d9), TOBN(0x81ec3853, 0x3d288d4c),
+     TOBN(0xda852a71, 0xee3bf44a), TOBN(0x39b4ef6c, 0xd7b5c6da),
+     TOBN(0x472e6996, 0x44f4e720), TOBN(0xbbd19d38, 0x9191614b),
+     TOBN(0xa2bcc2ec, 0x30c0e99d), TOBN(0x29318d7b, 0x57ba0582),
+     TOBN(0x322faf40, 0xd315963a), TOBN(0x49ba5570, 0x0c0619d1),
+     TOBN(0xc28c1f81, 0xe5dcd066), TOBN(0x64d1268d, 0xff9e3493),
+     TOBN(0xab0db38e, 0xbdf8992c), TOBN(0xe3790c26, 0x320cce30),
+     TOBN(0x59b408a0, 0x26e3e4b0), TOBN(0xe9e5fe29, 0x6ab8504e),
+     TOBN(0x45c827bd, 0x83c9eaf3), TOBN(0xc298e236, 0x89518edb),
+     TOBN(0xb79a8b15, 0x8d3ab381), TOBN(0x6bb951e8, 0xdb0bb7c0),
+     TOBN(0x5ebd3854, 0xbe4b3353), TOBN(0x107ba27d, 0x2eb3b0fe),
+     TOBN(0x9d01654d, 0x46786cb4), TOBN(0xf46d8352, 0xcf3a1aa2),
+     TOBN(0xa8f669a0, 0xa1662f72), TOBN(0xc979209f, 0x68a1d3e1),
+     TOBN(0xc64975fa, 0x65471473), TOBN(0x1f8eec02, 0xff1f2aad),
+     TOBN(0x1b520fcc, 0x8d0dd401), TOBN(0xcd732092, 0x15e14346),
+     TOBN(0x616478d8, 0x8f878a76), TOBN(0x3579d49c, 0x7423e0f5),
+     TOBN(0x119f6d6e, 0x1b2af15f), TOBN(0xbbe33d81, 0xb08c2c8c),
+     TOBN(0x051d99c9, 0x8534a355), TOBN(0xe3f3ddd3, 0x458b764b),
+     TOBN(0xbd7e51aa, 0xbc8c03bd), TOBN(0xcd7abf4a, 0xe8d42e38),
+     TOBN(0xf0d97428, 0x3160e63f), TOBN(0x258bba07, 0x34d13871),
+     TOBN(0x4fedb647, 0x3dcb885e), TOBN(0x009fca27, 0x50f0a645),
+     TOBN(0x3f06c146, 0x99775c4e), TOBN(0xb10a4ed3, 0xf66e7d05),
+     TOBN(0x9300e3ca, 0x3a3ab903), TOBN(0x0a5610e0, 0xde3c3e1f),
+     TOBN(0xe2827312, 0x1af56fb7), TOBN(0x7e2a2365, 0xd75d9a9c),
+     TOBN(0x9c3bb05a, 0xf11f8963), TOBN(0xdf94cac7, 0x30c80488),
+     TOBN(0xaff1682f, 0x2d1143f5), TOBN(0x5837e83a, 0xb4d6ed7f),
+     TOBN(0xf3e179be, 0xb4bce662), TOBN(0xfa8d7862, 0x8caa5fbb),
+     TOBN(0xbdde016f, 0x59ea54c1), TOBN(0xc488c829, 0x3c1ac962),
+     TOBN(0xabe8b367, 0x14b46863), TOBN(0xbcfde363, 0x82897d1a),
+     TOBN(0x87ddf0ec, 0x8c152354), TOBN(0xdec85db7, 0x7a953398),
+     TOBN(0x927a8b10, 0x0b57108f), TOBN(0xb38b732f, 0x525f78f2),
+     TOBN(0x7e696084, 0xeb306d56), TOBN(0x9befefef, 0x50269227),
+     TOBN(0xfa86e376, 0xcaddfa11), TOBN(0xd50a08da, 0x404be227),
+     TOBN(0xb7408e33, 0x03bb523c), TOBN(0x6d21aa4a, 0xc093aaf1),
+     TOBN(0x52aae4c9, 0xa85d6fcf), TOBN(0xf5d057c9, 0xb726afa9),
+     TOBN(0x7979bb5c, 0xf92ca5b2), TOBN(0x4b1f7936, 0xc4e3e4f3),
+     TOBN(0x2c534200, 0x071ec517), TOBN(0x47b52ffe, 0x67d3f86a),
+     TOBN(0x4a0b581d, 0x84d1c5b2), TOBN(0xfc825a4a, 0x0dfa90cb),
+     TOBN(0x2df2ec98, 0x11c72996), TOBN(0x82077a6e, 0x7dde922e),
+     TOBN(0x89acda10, 0x9f28b584), TOBN(0x54578eb8, 0xe49fe66f),
+     TOBN(0x90a5f700, 0x4a1c29d7), TOBN(0x2de4719c, 0xb3b44748),
+     TOBN(0x6944fe14, 0x18d85e6b), TOBN(0x90bd8208, 0xde7b904f),
+     TOBN(0x5811f3b6, 0xa4597137), TOBN(0x7ea43767, 0xd4ab5433),
+     TOBN(0x7ec39109, 0xa204a36f), TOBN(0xa43a4a57, 0xa30fb76e),
+     TOBN(0x4fd514f8, 0xe090f2be), TOBN(0x3918138e, 0xda1c97f8),
+     TOBN(0x2b466ae2, 0x15145a20), TOBN(0x28ccb2ce, 0xfbac86b7),
+     TOBN(0xb891b707, 0x04106b98), TOBN(0xe40a2310, 0x29696a08),
+     TOBN(0x1210fed0, 0x636d9e11), TOBN(0xdaea218d, 0x2043caa1),
+     TOBN(0x10c2ed0f, 0x0aef7dcd), TOBN(0x926be98a, 0xffa5db7b),
+     TOBN(0xe762191c, 0x36abac30), TOBN(0xe21acfaa, 0x8b75b5cb),
+     TOBN(0x4f5e6b9f, 0xd180cc32), TOBN(0x01358309, 0x55deffdd),
+     TOBN(0x1b1ab943, 0x992a66f3), TOBN(0x1ebe0246, 0xceef1a9c),
+     TOBN(0xa24c9e25, 0x7a01dcb9), TOBN(0x3d45c4e3, 0x326505f5),
+     TOBN(0x9b805759, 0xc8544885), TOBN(0xbe9b99ca, 0x7bfcad78),
+     TOBN(0xd1db36e1, 0x2b8fe78e), TOBN(0x37255a2d, 0xd5387bcf),
+     TOBN(0x044b3a3e, 0xa150ad32), TOBN(0xc65bc2a3, 0x6671ae59),
+     TOBN(0x41ce078e, 0x1d52384b), TOBN(0x3115f1b1, 0x9e72c300),
+     TOBN(0x487ff9da, 0xd0a358a0), TOBN(0x4b20c369, 0x9c242aec),
+     TOBN(0x7813a44c, 0x1c7b145f), TOBN(0x87c6bede, 0xd6f2d3ee),
+     TOBN(0x34d2a89b, 0x47d393b1), TOBN(0x1e9f97c6, 0x73f78679),
+     TOBN(0xcb614fe0, 0x2edce91c), TOBN(0x62b96009, 0x7e9a5fa9),
+     TOBN(0x7eb2aeb5, 0x58c34b85), TOBN(0xa256a478, 0xcf5074fc),
+     TOBN(0x73f23a56, 0x98c1de9b), TOBN(0xeffd490e, 0x61ce6535),
+     TOBN(0x2569df2a, 0x4a6c15c8), TOBN(0x91e202a0, 0xfffc97a5),
+     TOBN(0xd83c428e, 0x28dc7a57), TOBN(0x03bc53c7, 0x9fc8dca8),
+     TOBN(0xed394cfa, 0x9b60487b), TOBN(0xa4259f91, 0xb483a686),
+     TOBN(0x11f51779, 0x179a5cca), TOBN(0x00b00ef0, 0x86c1d1c7),
+     TOBN(0x6e596d2a, 0xf1231aed), TOBN(0x6c1a702b, 0xd80eaa89),
+     TOBN(0xd28f8c15, 0xd23f1d64), TOBN(0x93e85bea, 0x6d01728f),
+     TOBN(0xd4288fb5, 0x1c704922), TOBN(0xaadd1968, 0x8c1363c5),
+     TOBN(0x9b5c42d7, 0x52f2cc4e), TOBN(0xf9e4bc96, 0xc298e794),
+     TOBN(0xd604f076, 0xaf0804ac), TOBN(0xa441140a, 0xb3bb2628),
+     TOBN(0x761eabca, 0xd37bf6bd), TOBN(0x7d192922, 0xbe1cf79c),
+     TOBN(0x2365739e, 0x3da7073d), TOBN(0xfb7423ea, 0x8e2c078f),
+     TOBN(0x08f5132e, 0x3adfb6f3), TOBN(0x470a4205, 0x710a26fe),
+     TOBN(0xbe0afeb4, 0x2b6c9b33), TOBN(0x94d9edc8, 0x3cd813bf),
+     TOBN(0xa2c7a7a0, 0x440a1699), TOBN(0xbdc4ea3b, 0x4eaf0c10),
+     TOBN(0x5a5455db, 0x52fdc8d3), TOBN(0x60485f39, 0xb2945868),
+     TOBN(0x54ce9567, 0x00af0abe), TOBN(0x17bff77b, 0xe8d15f54),
+     TOBN(0x0021c531, 0x0e14a7ec), TOBN(0x3efdb22d, 0xdc9c800a),
+     TOBN(0x9a9e2747, 0x4d947ada), TOBN(0x19417bc4, 0xb37fc947),
+     TOBN(0x71ca8da8, 0x8f02c551), TOBN(0x782d5da4, 0x074cebc0),
+     TOBN(0x99094980, 0xc1a43a2d), TOBN(0xe1b02ff0, 0x24890d9b),
+     TOBN(0x4eedaddb, 0x45d82f7c), TOBN(0x7ae170a5, 0x5061c943),
+     TOBN(0xaf8c7ea0, 0x4d47c578), TOBN(0xcad17044, 0xad3a6eae),
+     TOBN(0x51383e61, 0x4f4c9c8b), TOBN(0x78d17182, 0x9182fc81),
+     TOBN(0xbed6f0d4, 0x90d72cb4), TOBN(0x98761291, 0x7bea62f0),
+     TOBN(0x27594570, 0xef3cd3fc), TOBN(0xf4759534, 0x91a8c364),
+     TOBN(0xf5c607c5, 0x2744eb2d), TOBN(0x0d6264eb, 0xd8d8f337),
+     TOBN(0xb54867a6, 0xa8701750), TOBN(0x1387e940, 0x87691191),
+     TOBN(0xc451f178, 0xbd2f75dc), TOBN(0x31a099d3, 0xd1da6080),
+     TOBN(0x0d0fcf97, 0x49f87f03), TOBN(0x0b7585f8, 0x0af6273d),
+     TOBN(0x3619cf2c, 0x1142265d), TOBN(0xf84d3f96, 0x05c559a4),
+     TOBN(0xc3d3c80e, 0xb83f2cb9), TOBN(0xf4ef0b54, 0x8f602141),
+     TOBN(0x3afb343d, 0xb9612928), TOBN(0x7abe5620, 0x8db5c907),
+     TOBN(0xcd692215, 0xcf019b08), TOBN(0x98d70b38, 0x9ae12255),
+     TOBN(0xb459e256, 0x8dfda5f2), TOBN(0x066a445e, 0x8f3f403e),
+     TOBN(0x5663e123, 0x423fbbb6), TOBN(0xcc55ce36, 0x5424d48f),
+     TOBN(0x8bca99b9, 0x3b6d5467), TOBN(0x299ff0ea, 0x316fc956),
+     TOBN(0xd973a8d8, 0xa0ceb256), TOBN(0x443ecdb9, 0x6d9956b9),
+     TOBN(0x8c16a75d, 0x2f64f912), TOBN(0x89e490c2, 0xbbf7ab50),
+     TOBN(0x4bd00db0, 0xb8dbf031), TOBN(0x866e0bbe, 0x7d2cb92d),
+     TOBN(0xad36406e, 0x1dd3db2c), TOBN(0x969dc881, 0xe4e3f194),
+     TOBN(0xcb3ac9e4, 0x2a115bc8), TOBN(0xb45efd5d, 0xe0a5ab75),
+     TOBN(0x1709c293, 0x55377d5c), TOBN(0x06d11ba4, 0xde6bc25d),
+     TOBN(0x84a09347, 0xccf2d10b), TOBN(0x571cd4d9, 0x08ee5aef),
+     TOBN(0x1379ac02, 0xa450dd82), TOBN(0x5b7f02f5, 0xae404542),
+     TOBN(0x17366e7f, 0x2a7df4ce), TOBN(0x5bb3560c, 0x9830ebec),
+     TOBN(0x5c582580, 0x7c254726), TOBN(0xea13f8fd, 0x70ab7b3d),
+     TOBN(0x868c0f8d, 0x314e2a25), TOBN(0x4b3dad3a, 0x0be90b12),
+     TOBN(0x09970da4, 0x32aaffcf), TOBN(0xe711e9cf, 0x8a6d894d),
+     TOBN(0x511521af, 0x0a80d07a), TOBN(0xe3814716, 0x8a2a2851),
+     TOBN(0xde76d41b, 0x1de9183e), TOBN(0x8a9fc79a, 0xaac779e5),
+     TOBN(0xd7d1f235, 0x26879f8b), TOBN(0xcc849c85, 0xe37d5f9f),
+     TOBN(0x26b5488a, 0x6b9cd82f), TOBN(0x1b068e8d, 0x91099141),
+     TOBN(0x040dc00f, 0x35ee636f), TOBN(0xab40f94b, 0xd84a9cbb),
+     TOBN(0x2e4cf65c, 0xdb303776), TOBN(0x42eaa12e, 0x78e8affb),
+     TOBN(0x7835e4e9, 0x876f8f38), TOBN(0xcd421d77, 0x090ca6b6),
+     TOBN(0x71a1d12d, 0xad0604f7), TOBN(0x51c2d158, 0x1a22e872),
+     TOBN(0xfe7dfcc8, 0x429e45e9), TOBN(0x20028f5c, 0x48224b6f),
+     TOBN(0xf7afed37, 0x50abf907), TOBN(0x92183692, 0xc4ce1a69),
+     TOBN(0x0b93365c, 0x2d128add), TOBN(0x883f43c3, 0x13200221),
+     TOBN(0x9d3b5a53, 0x4d309b2d), TOBN(0x60f0db16, 0xcf91a023),
+     TOBN(0x20f0ebbd, 0x5b0e47be), TOBN(0xcc20dde8, 0x317d8b4b),
+     TOBN(0xab033b48, 0x5303dd7c), TOBN(0x6703eac7, 0x7a9c1974),
+     TOBN(0x92f0b738, 0x351c8f26), TOBN(0xadd39ac8, 0x08569159),
+     TOBN(0x80866e5e, 0x61098dd5), TOBN(0x7d0c1c6f, 0xcae578f6),
+     TOBN(0x13d89cee, 0x975f59e4), TOBN(0x86006ed4, 0x0092de2c),
+     TOBN(0xda825b0a, 0x819adda4), TOBN(0x74fefb46, 0xde710934),
+     TOBN(0x7c2ec289, 0xd3dc8683), TOBN(0x25898cd8, 0x690e5d08),
+     TOBN(0x9bed0f32, 0xbcc67531), TOBN(0x356ba80c, 0xac762453),
+     TOBN(0xd3224c57, 0x7680da5e), TOBN(0xaae2597b, 0x3399d679),
+     TOBN(0xb2a2a1e5, 0x68df6e74), TOBN(0x49d23e8c, 0x2301b373),
+     TOBN(0xcb89b484, 0x170dd677), TOBN(0x36b1d3d1, 0x6b3ee110),
+     TOBN(0xe50ada4f, 0x0d7b51b4), TOBN(0xa2f4fb57, 0xfd9afdbc),
+     TOBN(0xb1b9b81d, 0xaa6dd8e8), TOBN(0x616056a0, 0x0be328aa),
+     TOBN(0x8f6dd943, 0xe12b07c8), TOBN(0x4bb551c6, 0x325abaf6),
+     TOBN(0xa5460380, 0x68fbed5f), TOBN(0xa65d059f, 0x87ed0d37),
+     TOBN(0xff60beda, 0x208aa8cc), TOBN(0xc91ff11b, 0x33d2d65e),
+     TOBN(0x078c4e5e, 0xf65f65d2), TOBN(0xa92ed905, 0xf347dccf),
+     TOBN(0x261ad25d, 0xf59e3be9), TOBN(0x95903d91, 0x3b69facc),
+     TOBN(0xcf0a2f94, 0xe789d854), TOBN(0x9d39cd51, 0x10fbf531),
+     TOBN(0x980ed5d4, 0x6de44e3c), TOBN(0xaedbae37, 0x78425caa),
+     TOBN(0x35804bc1, 0x7bd278b8), TOBN(0xf4bee96a, 0x6a2d7bee),
+     TOBN(0xc6c553a6, 0xa605671c), TOBN(0x182c9238, 0x86f010d2),
+     TOBN(0x94343b7a, 0x9cd6f37a), TOBN(0xa71e3853, 0x237190a9),
+     TOBN(0xfcbebde7, 0xa8a28451), TOBN(0xfa928367, 0xd711d2be),
+     TOBN(0xba8fd2ea, 0xc3668951), TOBN(0x00fad1ed, 0x2d241329),
+     TOBN(0x61b82e19, 0x5dbdffd1), TOBN(0x0e5e5782, 0x5a181dfe),
+     TOBN(0x1c1bf593, 0xc60f1799), TOBN(0x388d6950, 0x64ef800f),
+     TOBN(0xf78ef00f, 0xce927a87), TOBN(0x2a010419, 0x6abfff9f),
+     TOBN(0x13a7b08e, 0xb0b7ffe2), TOBN(0x4619da3e, 0x6da4cc8f),
+     TOBN(0x8ac19190, 0x7937e0bd), TOBN(0xf97d3fcb, 0x1af4f84c),
+     TOBN(0xaea2abd0, 0x8ac425a1), TOBN(0xc619c17d, 0x4a02e136),
+     TOBN(0xf09a57d3, 0x1b2c4acb), TOBN(0xc6fce6fc, 0x87b4eb40),
+     TOBN(0xa161bb70, 0xb21b02f7), TOBN(0x075301fb, 0x95bcb925),
+     TOBN(0x1d408003, 0xe1b440ce), TOBN(0xb42a47af, 0x606b3142),
+     TOBN(0xd4ad09c7, 0x1c832c35), TOBN(0x5bebe913, 0x0e17fb8f),
+     TOBN(0xbf8efbcd, 0x8b45b250), TOBN(0xbef3cafe, 0xe5ca21e4),
+     TOBN(0x08a18be7, 0x688076f1), TOBN(0xabbb3fc5, 0x0c3a2648),
+     TOBN(0xa77086e8, 0xfb54720e), TOBN(0x84277757, 0x19c29e8e),
+     TOBN(0x551768ca, 0x5b95b36d), TOBN(0x8850a9b0, 0xc7df6d3f),
+     TOBN(0xe5a2737f, 0x5008c00a), TOBN(0x9a577c0d, 0xad076e3c),
+     TOBN(0xbe7c611c, 0x2afa6a8a), TOBN(0x5dd9142a, 0x04259dac),
+     TOBN(0xd14253bb, 0x422bf3d1), TOBN(0x8c9dc4c6, 0x6805c78b),
+     TOBN(0xb9837258, 0xd430488c), TOBN(0xf9fc178b, 0x7abc184b),
+     TOBN(0x035d3079, 0x0c5e6a11), TOBN(0x20cbe540, 0xfbc2182b),
+     TOBN(0x849994e2, 0x9d76812f), TOBN(0x166a9279, 0xf7a85553),
+     TOBN(0x15ff0643, 0x19d70aff), TOBN(0x3c58e0b0, 0x4bc6a829),
+     TOBN(0x3809904b, 0x84df75ff), TOBN(0x454c63fd, 0x67a7c214),
+     TOBN(0x79e0ffde, 0x2d873c09), TOBN(0x620a3293, 0xcef301bf),
+     TOBN(0x8f38c8e8, 0x237c2bdf), TOBN(0x61cf96de, 0x13203c2c),
+     TOBN(0xdff401d6, 0xd0bef848), TOBN(0x3c8ed7ce, 0xee4bcbb6),
+     TOBN(0x3e128e2d, 0x07ff8f9a), TOBN(0x0653c0b2, 0xad7e8d5e),
+     TOBN(0x7bb30bb5, 0xb1930408), TOBN(0x91d18705, 0x4c158386),
+     TOBN(0xc4cf843c, 0x80c21fb4), TOBN(0x97a72d75, 0x8a04133a),
+     TOBN(0x6b7c49f3, 0x4218755a), TOBN(0xc1a5a447, 0x68a40f68),
+     TOBN(0x0ab9650e, 0x15ca3037), TOBN(0x16b1fa71, 0xac06feb0),
+     TOBN(0x50179660, 0x0faa3dca), TOBN(0x368b2d89, 0x1c1aaeae),
+     TOBN(0xf6fa652a, 0xb46f0310), TOBN(0x86a4d627, 0x79fcbc59),
+     TOBN(0x78169b8e, 0x6106a132), TOBN(0x40a741eb, 0x9e387d16),
+     TOBN(0x14a45174, 0x80eed542), TOBN(0xadd64561, 0x3362ef7f),
+     TOBN(0x39228bfc, 0xc5dd0396), TOBN(0xe9fdf903, 0xea0c538b),
+     TOBN(0x6bfd91ec, 0x74d235de), TOBN(0x96ec2378, 0x24aa0a47),
+     TOBN(0xf5699241, 0xaf8d6168), TOBN(0x0a7b9be3, 0xc548a60b),
+    },
+    {
+     TOBN(0xe5255c30, 0x2ade9556), TOBN(0xe328af1b, 0x75ba2e9b),
+     TOBN(0x9d3391ef, 0x41ce9e47), TOBN(0xb74cd668, 0xfb0ffcc9),
+     TOBN(0xc67103e4, 0xe3226acf), TOBN(0xa65ad22c, 0xd2959e42),
+     TOBN(0x3aaa8406, 0x99d490fc), TOBN(0x3e26a1c2, 0x9ecc6356),
+     TOBN(0x71c975de, 0x4e92defc), TOBN(0x81aeb173, 0xd0089883),
+     TOBN(0x8a30ce4a, 0x2ec09190), TOBN(0x426e7838, 0x69a36d64),
+     TOBN(0x5899a0b6, 0x309bd2d7), TOBN(0x3b1c24af, 0x3cc1a4af),
+     TOBN(0xb2aa8142, 0x345163b3), TOBN(0xd2ad9a69, 0x2c78c86d),
+     TOBN(0xde59fe5d, 0x8e7a4174), TOBN(0xaedff2d2, 0xab3b0f3d),
+     TOBN(0x4688e9e0, 0x1f053470), TOBN(0x29ff3fb1, 0x97c95c7c),
+     TOBN(0xffb930cc, 0x85e6a8df), TOBN(0xaa4d3175, 0x623b6068),
+     TOBN(0x682101dd, 0xf9716623), TOBN(0xa3bc9f5f, 0x402db696),
+     TOBN(0xf67233c6, 0xba4e4651), TOBN(0x8cf95660, 0x0714d4ac),
+     TOBN(0xd70decc3, 0x71f1f8da), TOBN(0xb674732a, 0x7078370e),
+     TOBN(0x4270416d, 0x4ccc773b), TOBN(0xc848ff35, 0xde87951e),
+     TOBN(0xa61506a8, 0x493dfb56), TOBN(0xd8371ea9, 0x767eb110),
+     TOBN(0xb468c382, 0x9e6b1d70), TOBN(0x1a901978, 0x4cd025fb),
+     TOBN(0x4bf50c7e, 0x5e6879e8), TOBN(0x6b862c0f, 0x71cf7119),
+     TOBN(0x6a53ce89, 0x06240e95), TOBN(0x3ddfaa0a, 0x04107ff4),
+     TOBN(0x317093cc, 0x65532b51), TOBN(0xf1e0f859, 0x0e27b5fc),
+     TOBN(0x96a97a12, 0xfe4674b4), TOBN(0x2a132ae6, 0xf7c97b12),
+     TOBN(0x5afcd087, 0xa5f5ae91), TOBN(0xfd1d3a32, 0xd4805ddb),
+     TOBN(0x0a989dc0, 0xd7b5c8bd), TOBN(0x35d186e4, 0x4429af19),
+     TOBN(0x65623ad2, 0x42935fba), TOBN(0x4e274314, 0xe79b867d),
+     TOBN(0x47d92016, 0x08aaba2a), TOBN(0x12b62343, 0xf3f4c812),
+     TOBN(0xb35bf043, 0x464f4b4c), TOBN(0xdc9391c0, 0xd8e8ba16),
+     TOBN(0xcc0f8c4a, 0x5d460c0d), TOBN(0x04ce64bf, 0xe20fc6ad),
+     TOBN(0xd0289df5, 0xaa4b7db5), TOBN(0xe0ea15c5, 0xe5299815),
+     TOBN(0xc066ee2f, 0xda3adfe0), TOBN(0xce6a9bdc, 0x0c964e7d),
+     TOBN(0x04a0115b, 0x0c859476), TOBN(0xb5e02dc9, 0x9c95699c),
+     TOBN(0xf8301f62, 0x11377eb9), TOBN(0x57b245a2, 0x172bca2e),
+     TOBN(0xa7d9b470, 0x7b47cf1f), TOBN(0x1b469bab, 0x1774b1c1),
+     TOBN(0xbb9ec3e9, 0xda2dce70), TOBN(0x02d5353e, 0xd29bcdda),
+     TOBN(0xc193244a, 0xb215233c), TOBN(0xb8d5727f, 0xd27a4e2a),
+     TOBN(0x79e56194, 0xb6c5b114), TOBN(0xe2c20e71, 0x5ce727f0),
+     TOBN(0xc92f34a5, 0x236cbfea), TOBN(0xcc47dfd1, 0x56a02b8f),
+     TOBN(0x5cdbda39, 0xe983ba13), TOBN(0x20f3de57, 0x6e96c8b2),
+     TOBN(0x2ff05aa7, 0x66b76faa), TOBN(0xa876bc62, 0xd7f84b47),
+     TOBN(0x962ef8a9, 0x0d677d1f), TOBN(0xabc7bb1e, 0x801d3001),
+     TOBN(0xdb5f0b1a, 0x7d13a23f), TOBN(0x2664f3ab, 0x20b819e4),
+     TOBN(0x96be66c5, 0xdc45375a), TOBN(0x780ee062, 0x4a6c24e8),
+     TOBN(0xc6fbfd1a, 0x013a13ee), TOBN(0x6ce1496c, 0x21fc4f9c),
+     TOBN(0x03130c09, 0x81f272c5), TOBN(0x06e59457, 0xa26609cd),
+     TOBN(0xf4c5e564, 0xee5363b4), TOBN(0x1cd19a11, 0x7df0775d),
+     TOBN(0xcdfcfa67, 0xdfd6586e), TOBN(0x358953e5, 0x1ba23faa),
+     TOBN(0x0f467275, 0xaeec5d6f), TOBN(0xb815967a, 0x5b0e6b2a),
+     TOBN(0xb01bf133, 0x012b89b4), TOBN(0xdd924bbc, 0x6839cc04),
+     TOBN(0xa5cd2180, 0x120dfd73), TOBN(0x1abb11ef, 0x19bf8098),
+     TOBN(0xd56c11ce, 0x6a281d1d), TOBN(0xfb01f455, 0x70daeb19),
+     TOBN(0xbb442a0d, 0x8f29fcc1), TOBN(0x9aa60157, 0xe9b2f829),
+     TOBN(0x1f3f6e61, 0x90ae8113), TOBN(0xc701a185, 0x6c946c0d),
+     TOBN(0xb4b89268, 0x52ba7caa), TOBN(0xd657c679, 0xb0a5c77f),
+     TOBN(0x0f14eb11, 0x0dd26330), TOBN(0xff622296, 0x9b036325),
+     TOBN(0xaf833fb8, 0x186e735a), TOBN(0x7801b02f, 0xc7e710f5),
+     TOBN(0xa0bf821f, 0x5c948f43), TOBN(0x3be31aea, 0x86225c71),
+     TOBN(0xe98f4f7b, 0xe60b1c88), TOBN(0x6306588d, 0x73c5281b),
+     TOBN(0xd6178277, 0x83c9f829), TOBN(0x67b20e6c, 0xc06675f1),
+     TOBN(0x8a63fb89, 0xcb2d5b9d), TOBN(0xcb28128c, 0x038c00fe),
+     TOBN(0x070a5141, 0x8c7c6c6e), TOBN(0xc05e18c3, 0x8789c3b7),
+     TOBN(0x09fd03c2, 0xd5c67731), TOBN(0xc59e2abb, 0xf54010ec),
+     TOBN(0x03977889, 0xf1ef2232), TOBN(0xbe2c82f1, 0x9c7409a5),
+     TOBN(0x35ac44f9, 0x32004e03), TOBN(0x048bb359, 0x856823a3),
+     TOBN(0x2e108d6c, 0xec1cf253), TOBN(0xe98e74d7, 0x703eb1d2),
+     TOBN(0xcaf64f60, 0x570ac34d), TOBN(0xff814e7d, 0x4d7797fa),
+     TOBN(0x93b6abc3, 0x70818472), TOBN(0x0e984be6, 0x888357af),
+     TOBN(0x2a7ca1b0, 0x3fe0c135), TOBN(0x0c6c4a11, 0x94a82d67),
+     TOBN(0x0c90c359, 0xbb83ae74), TOBN(0x49b25e5e, 0x328b8af1),
+     TOBN(0x26a36032, 0x798ff0a6), TOBN(0xbbf89c99, 0x1fc28ca3),
+     TOBN(0x4ce174e2, 0xe679eb71), TOBN(0x17c76176, 0xd1c183bd),
+     TOBN(0x4bf67be8, 0x03a69f58), TOBN(0x937a391c, 0xc0ee530c),
+     TOBN(0x2daa9d90, 0x1f7daaff), TOBN(0xa47e99b2, 0xc54f14d0),
+     TOBN(0x6be357e7, 0xc57feeca), TOBN(0x3753fad2, 0xcfdfd5dd),
+     TOBN(0x74e1457a, 0x48f90174), TOBN(0xb8092642, 0x9b4734da),
+     TOBN(0x291e75ba, 0x5800ea72), TOBN(0x25a21b38, 0xc72c28f7),
+     TOBN(0x2193e0c9, 0x505aa4d2), TOBN(0x2f6829e3, 0xada9d3f8),
+     TOBN(0x66cd5a1d, 0x92900e29), TOBN(0x1360d287, 0x7414dc1d),
+     TOBN(0x5deeb2eb, 0x0d1b96aa), TOBN(0x25783ce3, 0x3e52ccf1),
+     TOBN(0xe4e251f4, 0x29d534ef), TOBN(0x9fe9693d, 0x55797dad),
+     TOBN(0x6a173d69, 0xc6ab9935), TOBN(0x23991318, 0x7721ca8e),
+     TOBN(0x38cbcd30, 0xc393eb26), TOBN(0xe3627ab9, 0x71d95a9e),
+     TOBN(0xdf1218be, 0x7f6fe2d4), TOBN(0x850c8598, 0xfabd8326),
+     TOBN(0x1214d3d7, 0xb0f7cf49), TOBN(0xeaf60d31, 0x1805345b),
+     TOBN(0xc5caf65b, 0xbfee2c5f), TOBN(0x70127979, 0x45e23043),
+     TOBN(0xda36e794, 0x500fbad2), TOBN(0x38fa60b0, 0x4156e3a6),
+     TOBN(0x45934bdd, 0x3cbab88a), TOBN(0x72821e74, 0x1b19dce4),
+     TOBN(0x532f706d, 0x8661e32d), TOBN(0x3dbfc225, 0x73a9930e),
+     TOBN(0x72d1cb2a, 0x3cbeb0f1), TOBN(0x795b0696, 0xe20f5613),
+     TOBN(0x6e3469e8, 0x9fc88717), TOBN(0xf4bf0531, 0x483864d2),
+     TOBN(0xc92e6a8c, 0xfa19ddd9), TOBN(0x7db7e2ee, 0x3528e628),
+     TOBN(0x997a00eb, 0xf321fc88), TOBN(0x7605a2c9, 0xacdf205f),
+     TOBN(0x9fca58cf, 0xea9c3ed0), TOBN(0x833078cb, 0x56ff0e98),
+     TOBN(0x75159b8f, 0x662a1695), TOBN(0x186560b7, 0x1919f51f),
+     TOBN(0xe9b60e57, 0x5ef764b4), TOBN(0x61ad29fc, 0xbe73c698),
+     TOBN(0x18489017, 0xdd2be1ee), TOBN(0xac95e3b6, 0x7febda71),
+     TOBN(0xa6985346, 0x5ac96092), TOBN(0x906265c3, 0xbfc83566),
+     TOBN(0x1692c812, 0x5972fa4a), TOBN(0x4e76f879, 0x00773540),
+     TOBN(0xba9a6268, 0x542b137e), TOBN(0x43a52b90, 0x4c7926e2),
+     TOBN(0x28785bf5, 0xfeae554e), TOBN(0xc023b688, 0x0ab61576),
+     TOBN(0xb3ec8181, 0x10933a55), TOBN(0x75634459, 0x6331678e),
+     TOBN(0xe0dfa141, 0x17c50b5d), TOBN(0x4cbe7fda, 0xe2151f25),
+     TOBN(0x3f3072ac, 0xce81fbaf), TOBN(0xa387bb20, 0x0ff56a32),
+     TOBN(0x2b08a811, 0x99865968), TOBN(0x7279f913, 0x084cb1f2),
+     TOBN(0x78cca6c9, 0xdad70f5a), TOBN(0x72469f6a, 0xff47647d),
+     TOBN(0x2505c7ff, 0xe358b597), TOBN(0x7c5268a8, 0x998ff0dc),
+     TOBN(0x32d70129, 0x99d5b1c1), TOBN(0x72727c15, 0x24a90c34),
+     TOBN(0x57dad21c, 0x715662b1), TOBN(0x76b4b6ec, 0x132f3294),
+     TOBN(0xd03b46b1, 0x267d246e), TOBN(0xc7c848ec, 0x29b47516),
+     TOBN(0x5eab3dbc, 0x1660af51), TOBN(0x818894c4, 0x04c66383),
+     TOBN(0x26a45c3e, 0xa7b82f5c), TOBN(0x494694de, 0xea98adfb),
+     TOBN(0x44a06ec3, 0x134b182c), TOBN(0x5570befa, 0x75b82b41),
+     TOBN(0x819f6808, 0x129f952e), TOBN(0xa6fad25f, 0x914f87c4),
+     TOBN(0x831e5668, 0x7c7be905), TOBN(0x93107f38, 0xe623a5c2),
+     TOBN(0xa9d88469, 0x5e40c5f4), TOBN(0x4314d233, 0xaa5993c5),
+     TOBN(0x5eab88d0, 0x9c19bbc5), TOBN(0x795e1c21, 0xb385d3cd),
+     TOBN(0x532a1871, 0xce163fbe), TOBN(0x727cb126, 0xb867aea4),
+     TOBN(0xfc7047eb, 0xf7f30476), TOBN(0x18031271, 0xfcc4fe35),
+     TOBN(0x4b84fa4a, 0x884a4c6d), TOBN(0x82cb9aee, 0x55c830ab),
+     TOBN(0xd4cfdf04, 0x0cc927cc), TOBN(0x787efdde, 0xa16bef30),
+     TOBN(0xd1fb2dd6, 0x32e3c763), TOBN(0x8739566f, 0x16737272),
+     TOBN(0xf9ae4f46, 0x03a1055a), TOBN(0x199970cd, 0xf9a7472b),
+     TOBN(0xf9893cfb, 0x1d33ac50), TOBN(0x74cf7dd4, 0x1e5ff4e5),
+     TOBN(0x72ec32e5, 0xf7165331), TOBN(0xa082c59a, 0xbb4679cf),
+     TOBN(0x3cd0a467, 0x5c75461a), TOBN(0xd2872d68, 0x40f06206),
+     TOBN(0x08271eef, 0xb5122795), TOBN(0x7006d350, 0x1475e22b),
+     TOBN(0xf7cd1bcc, 0x89e35108), TOBN(0x924efa43, 0x93f1cbaf),
+     TOBN(0xe3716559, 0xf35b13ac), TOBN(0xa0a88e87, 0x60370a1d),
+     TOBN(0x1203be0a, 0x8c286ea3), TOBN(0x97fc5ab6, 0x6ebd50c7),
+     TOBN(0x2b5b3602, 0x74284d08), TOBN(0x3055716f, 0x694a20e0),
+     TOBN(0x793c8a89, 0x193520c0), TOBN(0x356402d0, 0x655f5ff2),
+     TOBN(0x0cf889ee, 0x1af5ddd6), TOBN(0x3eb7eb35, 0xb3f149b2),
+     TOBN(0x5254b57c, 0x68e07e0e), TOBN(0xb1971de2, 0x9c5bbfa7),
+     TOBN(0xcc85a53a, 0x0ad81e7e), TOBN(0xbaaa4d2b, 0xed3cbb10),
+     TOBN(0xbdf9941c, 0x8f3a7eec), TOBN(0x6e1b7dab, 0xa1f26666),
+     TOBN(0xe7a0dfa4, 0x2d79a58f), TOBN(0x25e0ddb5, 0x1f2b9667),
+     TOBN(0x4b3b5105, 0x5fd96351), TOBN(0x12325832, 0x8874abd1),
+     TOBN(0x56e90310, 0x795d91a5), TOBN(0x376a79d2, 0x2c32eec8),
+     TOBN(0xd9dd8817, 0xa8a16445), TOBN(0xd61f6aec, 0x0e00fa92),
+     TOBN(0x594a620d, 0x17d95f07), TOBN(0xa1534fda, 0xf4b15001),
+     TOBN(0xe9402601, 0x0974f4a3), TOBN(0x4c3fc130, 0x8f671f13),
+     TOBN(0x8eaab35a, 0xc5f35bfb), TOBN(0x13b98472, 0x4626baca),
+     TOBN(0xf48703ad, 0xcdee6f8d), TOBN(0xf1ba0309, 0x12d39694),
+     TOBN(0xeb2d4d92, 0x9fcda52c), TOBN(0x984f5111, 0x5507b401),
+     TOBN(0xe3aa26ae, 0xf6bab9ec), TOBN(0x557b5e3f, 0x6b2e077d),
+     TOBN(0x7185ab4f, 0x2f9c5f35), TOBN(0x96f21a33, 0x1680bcbc),
+     TOBN(0x2e7f6e07, 0x2ddb1173), TOBN(0xa704416e, 0x816ffc8d),
+     TOBN(0x55acfaa3, 0x52e26265), TOBN(0x9c244253, 0x8b758c94),
+     TOBN(0x4012e0a6, 0x0479936d), TOBN(0x12749e93, 0x6d8541d8),
+     TOBN(0x374f420d, 0xce56a2a1), TOBN(0x6a8e3614, 0xc79da17f),
+     TOBN(0x3602ad09, 0x157cc9e1), TOBN(0xf3c4a54c, 0x13603196),
+     TOBN(0x354fc6ed, 0x8473ae27), TOBN(0xb4cf4251, 0x651b8003),
+     TOBN(0x456b1b9b, 0x3415b5f0), TOBN(0xe078a858, 0xc4c71507),
+     TOBN(0xf4210409, 0x9a0a11fb), TOBN(0x76de42a0, 0xf930ec45),
+     TOBN(0x82ecd0af, 0xcfa869a1), TOBN(0xa637938a, 0xdccf2e47),
+     TOBN(0x1d2858f2, 0xc041648a), TOBN(0xcf9abfe8, 0xc0dfacd2),
+     TOBN(0x3af77e19, 0xbdddebe1), TOBN(0x15f6b0bb, 0x180b535f),
+     TOBN(0x497de678, 0x549d34c1), TOBN(0x31495c9e, 0x3dba7d6f),
+     TOBN(0x47b9368b, 0x393ab61c), TOBN(0xdb8ee3a8, 0x27441f92),
+     TOBN(0x214a6a5f, 0xfb0b0117), TOBN(0x429e61ad, 0x43759430),
+     TOBN(0x78116e88, 0x77316c0e), TOBN(0x59c82616, 0xa6a29e98),
+     TOBN(0xbfed454f, 0xaaef4619), TOBN(0x673327c4, 0x35926335),
+     TOBN(0xaa66d8c5, 0x18c616a7), TOBN(0xa93946a6, 0x6d28fb98),
+     TOBN(0x4fc30de5, 0x133336dd), TOBN(0x7e000a34, 0x7f0c805e),
+     TOBN(0xa82dcf54, 0xcf7eab23), TOBN(0x679e67a8, 0x8dc24371),
+     TOBN(0x26b2dffc, 0x1af582d8), TOBN(0x4c1d692a, 0xd3fe2155),
+     TOBN(0x2475b110, 0x2d024923), TOBN(0x0cc9245d, 0xc303c1e7),
+     TOBN(0x290b7a77, 0x03667a7a), TOBN(0x2ab8eb6d, 0xd87dbd9c),
+     TOBN(0x7089e481, 0xc098719e), TOBN(0x17dd6d74, 0x12c022c8),
+     TOBN(0x90efa01f, 0x8b7aca14), TOBN(0x8b601fba, 0xf55fbe83),
+     TOBN(0xf800bd76, 0x415aa7a5), TOBN(0x015573d3, 0x3aa74349),
+     TOBN(0xd5143779, 0xaf5ec789), TOBN(0x32330b4b, 0xd76dd2dd),
+     TOBN(0xec6caa4d, 0x82331ef1), TOBN(0x92cc8865, 0x25ad1965),
+     TOBN(0xf8209a40, 0x134370b0), TOBN(0x320a37b9, 0xb883cf95),
+     TOBN(0x94237ba2, 0x5d39731d), TOBN(0x6359d195, 0x8532a968),
+     TOBN(0x8bca94c9, 0xf86b8b75), TOBN(0xdb5c6a9c, 0xde316226),
+     TOBN(0xdf300c59, 0xa2fa0c26), TOBN(0x6dbf6082, 0x48af4357),
+     TOBN(0x25066c6c, 0x06535fc9), TOBN(0xba2e774e, 0xa29b2477),
+     TOBN(0x5157e93d, 0x1931299b), TOBN(0xd6249c10, 0x3a8035a4),
+     TOBN(0xcb18fcf1, 0x81245740), TOBN(0xb4d84c9d, 0xad5ebe1f),
+     TOBN(0x95050bec, 0x8df0d59d), TOBN(0x190a4860, 0xac2a2e0c),
+     TOBN(0x29029e0f, 0xfd1bbb11), TOBN(0x341f73de, 0xb075b432),
+     TOBN(0xa825c3c5, 0x74836028), TOBN(0xec4fd74b, 0x8f55343a),
+     TOBN(0x009bcab5, 0x60a683b3), TOBN(0x29877303, 0xcd3adea6),
+     TOBN(0x9f264bf2, 0x684a33ac), TOBN(0xc8bf19e6, 0x84b3c379),
+     TOBN(0x8ac35fb8, 0xa1215470), TOBN(0x2919d9da, 0x405386d6),
+     TOBN(0xb4e4aa30, 0x19780b2a), TOBN(0x639b8fcb, 0x356ddd4e),
+     TOBN(0x6ed7b10c, 0x9322c245), TOBN(0x84ec0bc6, 0x57f39c76),
+     TOBN(0x6a1be66c, 0x879176fb), TOBN(0x4cab3151, 0xe10e0f77),
+     TOBN(0x01c6321f, 0xe2ae0777), TOBN(0x04d6a04c, 0x65e57ff1),
+     TOBN(0x8c1725ed, 0x142596dc), TOBN(0xd321d49a, 0xb2d413a6),
+     TOBN(0x19c25fc3, 0x2b5e8ae7), TOBN(0xfc32cbeb, 0xbd3c7dc6),
+     TOBN(0xf3ec98b8, 0x57b98ff5), TOBN(0x52e5f1ad, 0xf75b1a00),
+     TOBN(0x16812bb4, 0x8f8ad463), TOBN(0x9d67cb11, 0xa274f0c3),
+     TOBN(0xdec72055, 0x80064047), TOBN(0x3f828c01, 0x4319f87b),
+     TOBN(0xffcad5c3, 0xff4d6c4a), TOBN(0xee6659b2, 0x67a6e030),
+     TOBN(0x9cb5c729, 0x0478715f), TOBN(0xc63fc281, 0x5a1c926e),
+     TOBN(0x1b8788ca, 0xdeb11155), TOBN(0xbe2eebf1, 0x4f0c00b2),
+     TOBN(0x9b72ffd0, 0xa6af09d1), TOBN(0xcbac42bd, 0xa9a459f3),
+     TOBN(0x150806c0, 0xf560dc93), TOBN(0x71c636e4, 0xc57787ee),
+     TOBN(0xe4f3acb8, 0x2a951b0d), TOBN(0x510dc771, 0x3b18d294),
+     TOBN(0xfbb3fb53, 0xb060e782), TOBN(0x0659cadd, 0x0358210e),
+     TOBN(0x23638245, 0xecde1629), TOBN(0xee6e9a65, 0xcc09daa5),
+     TOBN(0xf440bb81, 0xee18f9cf), TOBN(0x99e4d6e8, 0x955200e0),
+     TOBN(0x34555b58, 0x93e69354), TOBN(0xa6080e13, 0xfb29b19b),
+     TOBN(0x3bfa4796, 0x5100ab06), TOBN(0xf5db4b13, 0x22eec8fc),
+     TOBN(0x2c1a229e, 0xe5aaa980), TOBN(0x446cd46b, 0xd29eb83e),
+     TOBN(0xe0c044da, 0x7f5aa015), TOBN(0xa55d5f23, 0xa18f23f7),
+     TOBN(0xd71e58c1, 0xb6b70f51), TOBN(0x77c72d10, 0xb5862c51),
+     TOBN(0x01db731a, 0xfce94ceb), TOBN(0x877703a8, 0x13a47553),
+     TOBN(0x4878b0b1, 0x3b75e6d9), TOBN(0xbe8421f0, 0xfe60f98a),
+     TOBN(0x6312821b, 0xc69d6820), TOBN(0x4c019374, 0x00d2db20),
+     TOBN(0xb1bd5175, 0x2a1d8b74), TOBN(0xa0a24ad2, 0xef7fdad6),
+     TOBN(0xf29fd27d, 0x929fc488), TOBN(0x8e28b4ed, 0x162a02de),
+     TOBN(0x434cbdb3, 0xc166afcf), TOBN(0x7b79e808, 0xbf663e65),
+     TOBN(0xd445f1b0, 0xa3c3b159), TOBN(0xdf9f92b7, 0xb35b2be9),
+     TOBN(0x815b57f3, 0x788a9bbc), TOBN(0x9e03e357, 0xabbba2e0),
+     TOBN(0x3fc574d5, 0x91a658d8), TOBN(0x83b35d8a, 0xadf4e250),
+     TOBN(0xa0e15175, 0xacd1e4f0), TOBN(0xeca899a4, 0x868b4e04),
+     TOBN(0x713b4e9e, 0x782b7ee7), TOBN(0xed177e1e, 0xb7d58c1a),
+     TOBN(0x4778df76, 0xac8364b2), TOBN(0x6898fb31, 0x2e8f7ef7),
+     TOBN(0xfccf4c53, 0xa03975b0), TOBN(0x0f908d14, 0x8597436f),
+     TOBN(0xbeaf1a16, 0x96671c53), TOBN(0x9be64329, 0x6bc4cbbf),
+     TOBN(0xc8f66f63, 0x80017bf3), TOBN(0x92d700f2, 0x8836ff35),
+     TOBN(0x9ddd7a81, 0x13a4daf1), TOBN(0xb3c42723, 0x9b72664b),
+     TOBN(0x3d96f79a, 0x81babf43), TOBN(0xa46c9c0c, 0x7ce5830d),
+     TOBN(0x54dfec97, 0xf5999a71), TOBN(0xdb5be461, 0xe586a578),
+     TOBN(0xf9bc3f04, 0xcfb4e7ad), TOBN(0x6e5448a9, 0xb11f07aa),
+     TOBN(0x29ef7791, 0x70132d5a), TOBN(0x4fe486c3, 0x28ba851a),
+     TOBN(0x6743fecd, 0xb62f7f8a), TOBN(0xeb264387, 0x44d24d01),
+     TOBN(0xf93c05bc, 0x72ebb976), TOBN(0xe65b30c0, 0xaaae099e),
+     TOBN(0x4194721a, 0xc8427104), TOBN(0x3af3487f, 0x3025853e),
+     TOBN(0xb33a2893, 0xdbf48435), TOBN(0x2c5ac639, 0x2d79334e),
+     TOBN(0x8fc199f8, 0xb16b05a6), TOBN(0xc145358e, 0x7661a77b),
+     TOBN(0x15b580b6, 0x1841f719), TOBN(0x24f5fadb, 0xd7045528),
+     TOBN(0xe443c257, 0x98df2c22), TOBN(0x48acf5a7, 0xd7eed58b),
+     TOBN(0xe24e6571, 0xedeb9e4b), TOBN(0xcd047b81, 0x562533fd),
+     TOBN(0x618ddd86, 0xd1566e36), TOBN(0x09a77b70, 0xdba1eced),
+     TOBN(0x0e9de410, 0x6968ddb9), TOBN(0x10f0f429, 0x12985b32),
+     TOBN(0xbe21b10f, 0x8ca7d2fa), TOBN(0x0844d8e8, 0xc610ae2b),
+     TOBN(0x58a08694, 0xd57107d5), TOBN(0x45f44bd5, 0xc34441f3),
+     TOBN(0xe8b3b3df, 0x79a54186), TOBN(0x6496d668, 0xb8b5f26b),
+     TOBN(0xd69cefb8, 0x192503be), TOBN(0xb692128d, 0x40f928fc),
+     TOBN(0x13b11dfd, 0xa7ed8c47), TOBN(0x90bd279f, 0x5a196756),
+     TOBN(0x78f2e8c6, 0x17ff33eb), TOBN(0xa7b233d4, 0xaaf6c182),
+     TOBN(0x63d62503, 0x76a31f46), TOBN(0x53143dc3, 0x1449dc97),
+     TOBN(0x5bb86802, 0x94922922), TOBN(0x2f45a7dc, 0x89181334),
+     TOBN(0xf7c466d5, 0x1ec5cce4), TOBN(0x52d15eed, 0xbb3bd5f3),
+     TOBN(0x150bd5f6, 0xe6eacf86), TOBN(0x7fecaf3a, 0x0ba66988),
+     TOBN(0xcdd7fadb, 0x11f6f528), TOBN(0x60f64c68, 0x55042faf),
+     TOBN(0x1615919d, 0x961ddf76), TOBN(0xdba095cb, 0x53f2f514),
+     TOBN(0xf04960ba, 0x1e6c076c), TOBN(0xe5276722, 0x4c9f17d5),
+     TOBN(0x93ff80f9, 0x61c186a9), TOBN(0xd17378b0, 0x3c58ab92),
+     TOBN(0xc67f9ae1, 0x769492e8), TOBN(0xaccfc868, 0x0c3d023b),
+     TOBN(0x7d67a63d, 0x5b99708d), TOBN(0xfb29bef7, 0x4b80189a),
+     TOBN(0x3cb7eeaa, 0x241c402e), TOBN(0x328cb6de, 0x2c5c2767),
+     TOBN(0x0d24a7b4, 0x9cec231d), TOBN(0x725955fc, 0x0e2e6a7f),
+     TOBN(0xa2040cfa, 0xb7f17b13), TOBN(0x215eff8d, 0xa25c71cf),
+    },
+    {
+     TOBN(0xe4d9cab1, 0xc0d41a94), TOBN(0xc38b202a, 0x9e60f7d4),
+     TOBN(0x2bbf6b17, 0x9336116c), TOBN(0x2f9aa877, 0x2e068f13),
+     TOBN(0xf8820627, 0xa4bac9fd), TOBN(0x2209cb9e, 0x8a593cb4),
+     TOBN(0xaa78ec63, 0xc7417931), TOBN(0x42d21251, 0x7cfccfbf),
+     TOBN(0x40cee5ae, 0x3e611940), TOBN(0x4e2d9ea9, 0x0aa090ec),
+     TOBN(0x73d167ef, 0x1b926e42), TOBN(0x7fff36df, 0x5d5112a3),
+     TOBN(0x25587745, 0xcaffa3fb), TOBN(0x224f7f4e, 0xc5a83504),
+     TOBN(0x5ceff018, 0x3b47bf2a), TOBN(0xed9bfa73, 0xecfab5c6),
+     TOBN(0xf3d570b8, 0xd9b429c9), TOBN(0x69460116, 0xc5ad81cb),
+     TOBN(0x30833a08, 0x2f6825bd), TOBN(0xa297122a, 0x7c99f36a),
+     TOBN(0x6fc9b848, 0x05c3abdf), TOBN(0xefe95298, 0x5f2e24b2),
+     TOBN(0xf045275a, 0x8915d922), TOBN(0x79146aab, 0x298a9726),
+     TOBN(0x0c7d5905, 0x4f831d0b), TOBN(0xfaaaa26c, 0x2d47d4fe),
+     TOBN(0x5ac28599, 0x85042e12), TOBN(0x7eda370b, 0x7796977d),
+     TOBN(0x9f0bd71d, 0x95c0be63), TOBN(0x7c4601bc, 0x8e821005),
+     TOBN(0xf1ecbc60, 0x4c2ffae9), TOBN(0x7e3efc57, 0x9b688173),
+     TOBN(0x868c672b, 0xf4ea7641), TOBN(0x4fa90a82, 0xd94243dc),
+     TOBN(0xbd82913e, 0xf5eab489), TOBN(0xceebc159, 0xe966b39d),
+     TOBN(0x31fe4f5f, 0x35c51e2b), TOBN(0x2fea6ab1, 0xc79c1725),
+     TOBN(0x5856cd85, 0x83afad40), TOBN(0x7f960988, 0x4ca89b88),
+     TOBN(0x9d237c2d, 0x1ed8fed0), TOBN(0x69b4ec80, 0x4e0c6f10),
+     TOBN(0x11f83283, 0xe4648395), TOBN(0x6f4636a7, 0x306e152a),
+     TOBN(0xf70fd23a, 0x804539b3), TOBN(0x4db0903a, 0xb3cdb981),
+     TOBN(0xe506ae6f, 0x6691eb18), TOBN(0xaa69c7aa, 0x1d8e9d9d),
+     TOBN(0x540af950, 0x3e4e2088), TOBN(0x8fab01d5, 0x95f04e57),
+     TOBN(0x51bf909a, 0xa8c51a67), TOBN(0x01299f5e, 0xfd19beb7),
+     TOBN(0xdf703400, 0xb8f15aeb), TOBN(0x19c70987, 0x2d890394),
+     TOBN(0xf5fcc675, 0x203d2d99), TOBN(0xabbf3f21, 0xc3d4ddea),
+     TOBN(0x8348ca15, 0x587feffa), TOBN(0x585d0740, 0x7d69e4ad),
+     TOBN(0x6fbe5619, 0x885a0745), TOBN(0x04ee9eba, 0xb10b24dd),
+     TOBN(0x5c27075c, 0x0f4c12d7), TOBN(0xacf4acdc, 0x3c51c605),
+     TOBN(0x782fa52b, 0xfce336d0), TOBN(0x6e1d078f, 0x483621d2),
+     TOBN(0xa2253bfb, 0xd4dc3277), TOBN(0x3a014307, 0x4691bc12),
+     TOBN(0x415aa5b2, 0xebdef616), TOBN(0x1008a44a, 0x16fe065b),
+     TOBN(0x4004a90a, 0x16dfa94d), TOBN(0x0e24f541, 0x8464785b),
+     TOBN(0xd2715c89, 0x88d30ea8), TOBN(0xaf81a9ff, 0x1f05a312),
+     TOBN(0x958da470, 0x3e8d5eef), TOBN(0x09561898, 0xd3c80414),
+     TOBN(0xba6b731c, 0x8bb74691), TOBN(0x6b7355cd, 0x577f2ef9),
+     TOBN(0xd1f416ed, 0xb8a98efa), TOBN(0xd4a1293a, 0x11590271),
+     TOBN(0x2c4d5934, 0x4658e9eb), TOBN(0x51803651, 0xd1f15d39),
+     TOBN(0x9b9f0c05, 0x3c95fffe), TOBN(0x8f021451, 0x31acd6ca),
+     TOBN(0x5fee2961, 0xf9dba549), TOBN(0x05217975, 0x17ea0456),
+     TOBN(0xc0591906, 0xd13a6a4a), TOBN(0xa7f5ed02, 0x90daf370),
+     TOBN(0x1f8b7158, 0xfc4c304d), TOBN(0x77016c29, 0x1b7f0246),
+     TOBN(0xc27d1847, 0x2ea265d2), TOBN(0xec0789c6, 0x2862781e),
+     TOBN(0x0a79ac1f, 0x5d86a60e), TOBN(0xe325b563, 0x130670a5),
+     TOBN(0xf4794460, 0x6d33bfee), TOBN(0x126e703e, 0xec25bb10),
+     TOBN(0xeae22fd3, 0xa7bf902f), TOBN(0x8b2fb282, 0x28eef62e),
+     TOBN(0xb68de35b, 0x059138b4), TOBN(0xfc44bf56, 0xd46e68e3),
+     TOBN(0x71567daa, 0xff11f76a), TOBN(0x9110e849, 0x6b17cd2a),
+     TOBN(0x7c4027e3, 0x69573b93), TOBN(0x84ee945a, 0x1eb9bf01),
+     TOBN(0xa3fadc6d, 0x28c26cdb), TOBN(0x7037a74d, 0x575dfc1b),
+     TOBN(0x58c96a91, 0x9b2223dd), TOBN(0x912fc795, 0x51461b94),
+     TOBN(0xc18ced63, 0x2df3329a), TOBN(0x79d6f75f, 0x88a002d0),
+     TOBN(0x73d7a089, 0xf44d3d84), TOBN(0x98c78aa7, 0x8c058073),
+     TOBN(0x0ab8b3c7, 0x333ae8ff), TOBN(0xf5a8f5ec, 0xebd4e536),
+     TOBN(0x2c7df9fd, 0x83a5f52b), TOBN(0x314fc7c3, 0xcc062097),
+     TOBN(0x6c3195f8, 0xc5a3113c), TOBN(0xf130cef9, 0x2c25a629),
+     TOBN(0x10c8cc5b, 0x70c8dd70), TOBN(0xecb7a35f, 0x01cd40d3),
+     TOBN(0xfbee538f, 0x6fe21c93), TOBN(0x57ec1959, 0x2ba12ee8),
+     TOBN(0x74387a1b, 0xb2b806a8), TOBN(0x14efa300, 0xbad5d0f4),
+     TOBN(0xee7e4421, 0x23a0e9e4), TOBN(0x504ae428, 0x3b6abdec),
+     TOBN(0xb8c06fcb, 0x927b1aac), TOBN(0x55e1c85c, 0x323b71d3),
+     TOBN(0xf47e180f, 0x48d6dae1), TOBN(0x6d80dd63, 0xa84cb0b8),
+     TOBN(0xf8e07d53, 0xd75d7785), TOBN(0x3edf19b7, 0x37614325),
+     TOBN(0xf03709b0, 0x357946ed), TOBN(0x567d8c0d, 0xd12105e7),
+     TOBN(0xecf6abc0, 0xa9383b49), TOBN(0xfe9c760b, 0xcab329a7),
+     TOBN(0x425e62fa, 0x43928852), TOBN(0x27945ae0, 0x6194b51f),
+     TOBN(0x756f0f54, 0x3ee4f7cd), TOBN(0x4337ac7b, 0x26e4c865),
+     TOBN(0xf94596c3, 0x35b9c3ae), TOBN(0x066fd3da, 0x4d6aa5d2),
+     TOBN(0xce1a5b7e, 0x43c8d94c), TOBN(0xaed496a8, 0x614c0fc2),
+     TOBN(0x355e16f5, 0x2a6d5481), TOBN(0x8f934639, 0xa09d3afe),
+     TOBN(0xd170daef, 0x2bf2a099), TOBN(0x3920d87a, 0xae6ee507),
+     TOBN(0xbdac1c8e, 0x248158e3), TOBN(0x99033a9a, 0x05c54e69),
+     TOBN(0x4513bdf0, 0x41872197), TOBN(0x15634020, 0xd3f0f889),
+     TOBN(0x76c1855a, 0x05d42aa8), TOBN(0x23079179, 0xe8ba47cc),
+     TOBN(0xf80b787b, 0x728429e6), TOBN(0x896b6ea5, 0x3dd8c4f8),
+     TOBN(0x711eef39, 0xc7d9fe06), TOBN(0xfff28d03, 0xebced829),
+     TOBN(0x5d7823b8, 0x4ad40c88), TOBN(0x40a5a166, 0x3b112bd4),
+     TOBN(0x84230bfa, 0x63bce926), TOBN(0x39d2e6bd, 0xbe17e7cd),
+     TOBN(0xa772e242, 0xef03ee6c), TOBN(0x888bc969, 0xfa009e67),
+     TOBN(0x0f06ee83, 0x4893e1f0), TOBN(0xf28f0d3c, 0x6b89e124),
+     TOBN(0xb3e70ef8, 0x71f5cbc5), TOBN(0xff0f7e62, 0x6cad191e),
+     TOBN(0x990697be, 0xf028d276), TOBN(0x144c101c, 0x4ad8f830),
+     TOBN(0xbcaafb45, 0x3556d74f), TOBN(0xbc216224, 0xeb4c7ea0),
+     TOBN(0x73ad1043, 0x234a62c8), TOBN(0xa644eb6a, 0x2d95ff46),
+     TOBN(0xd545b60a, 0x0a3373f8), TOBN(0xf7a0572c, 0xd4edaa10),
+     TOBN(0xa7177049, 0xa97a85b4), TOBN(0x529dbadd, 0x7d3ec769),
+     TOBN(0xc45610f6, 0x7822dd37), TOBN(0xfad07fab, 0x98258666),
+     TOBN(0xac27001f, 0x87259c1b), TOBN(0xa9bdc6a9, 0x1b849745),
+     TOBN(0xc7ee7216, 0x04c862b1), TOBN(0x32c1541e, 0x0012f591),
+     TOBN(0x8198aadd, 0x5a366d79), TOBN(0x03cd312e, 0x68866e1b),
+     TOBN(0xa0dbc381, 0x9ec64698), TOBN(0x770e4d11, 0x1ef36dd2),
+     TOBN(0x01d17915, 0x8d56bdfd), TOBN(0xb48153cd, 0x75eb9947),
+     TOBN(0xc1d17a54, 0xfde98390), TOBN(0x0817eaf7, 0x0fe2d6fc),
+     TOBN(0x44a63591, 0xa6a704f1), TOBN(0x9423465f, 0x7f554182),
+     TOBN(0xc7c23cbd, 0x13e0574e), TOBN(0x6e06e2cb, 0x439941b6),
+     TOBN(0xa8aebd2c, 0xafa39c79), TOBN(0x1b859e2b, 0xedede735),
+     TOBN(0x2f485781, 0x6b4f5465), TOBN(0xec3093f0, 0x624c81e8),
+     TOBN(0xc1f027c1, 0xc282644c), TOBN(0x2f6e990b, 0x2b74ab51),
+     TOBN(0x9a988d1c, 0xed2ea3df), TOBN(0xa3f50efd, 0xff39d3df),
+     TOBN(0x418a3627, 0xec1d7614), TOBN(0xafc1520c, 0x3d4fa3e8),
+     TOBN(0x741305af, 0x891a9c69), TOBN(0xe87064d4, 0x5d6f8296),
+     TOBN(0x47c9394c, 0x12307b05), TOBN(0x6b038acb, 0xc35f0f40),
+     TOBN(0xa6e77647, 0x1ccca008), TOBN(0x58e4cfb6, 0x9dd71746),
+     TOBN(0xdf649c98, 0xc1fe84ae), TOBN(0x2e308ddc, 0x90db4388),
+     TOBN(0xc2641332, 0xe9362400), TOBN(0x92dd9842, 0x42d265e5),
+     TOBN(0xe0e4ed9b, 0x31eb91bd), TOBN(0x62ec7dd1, 0x145535c5),
+     TOBN(0x1ff29a09, 0xf810812f), TOBN(0x56b64acb, 0x15e9b102),
+     TOBN(0xb5f6d4d4, 0x5b353184), TOBN(0xc3c9292c, 0x1c593774),
+     TOBN(0x16781036, 0x2c700292), TOBN(0xf0948fc9, 0x3ae2f0c6),
+     TOBN(0x40e353cc, 0x4da778ea), TOBN(0x07febf09, 0xa34df03a),
+     TOBN(0x349812ae, 0x9ec397ff), TOBN(0x7c78812f, 0x330f02d0),
+     TOBN(0xf956700b, 0x7d241ea2), TOBN(0x864b1809, 0xebed08be),
+     TOBN(0xe4cec3df, 0xb9eb1635), TOBN(0x7dd65ad6, 0xab49fb60),
+     TOBN(0x06551163, 0x86788a28), TOBN(0xda8792d5, 0x11fb4214),
+     TOBN(0x82140df3, 0xcec09c3e), TOBN(0xcd34ca30, 0x539db03f),
+     TOBN(0xf07cf030, 0xe7dd0e09), TOBN(0x7b08a242, 0x56ae3487),
+     TOBN(0x9c0fd607, 0xbf5a6549), TOBN(0x0b1fc745, 0xd189d68e),
+     TOBN(0x0d91be74, 0x9cf52022), TOBN(0x6013f31f, 0x43ff7fc3),
+     TOBN(0x3bf90bd5, 0xb5654233), TOBN(0xd0a17969, 0x202bf534),
+     TOBN(0xff373b8b, 0xc97e02ba), TOBN(0x4606de54, 0xd31dba07),
+     TOBN(0xb045c50a, 0x8114562a), TOBN(0xc123acac, 0x7b8d8915),
+     TOBN(0xa8f7cd87, 0xb60aa324), TOBN(0x077cab67, 0xabc48200),
+     TOBN(0x88a68643, 0x0d7fff59), TOBN(0x82b92193, 0x67bfe743),
+     TOBN(0x1a8b86cf, 0xc2ce06f9), TOBN(0xa38414a0, 0xf9ad6134),
+     TOBN(0x7f102610, 0x28e2c39f), TOBN(0x34805c20, 0x480856a0),
+     TOBN(0x1b3f9302, 0x18c3034d), TOBN(0x1713f457, 0x574c0c9d),
+     TOBN(0xd84fa26f, 0x690ce2a5), TOBN(0xd4cfa19f, 0xe01a4637),
+     TOBN(0x4807fb82, 0xcc6fad47), TOBN(0xc9d84b48, 0xf933226a),
+     TOBN(0x9b7c530e, 0x7cd2c9dd), TOBN(0x6436a001, 0xf44818e3),
+     TOBN(0xbae0ceed, 0xdfb00461), TOBN(0xed6a7c5f, 0x51c8c8a3),
+     TOBN(0xa6e7fa54, 0x0463ac73), TOBN(0xa0deed89, 0xc77b19e5),
+     TOBN(0x4e0a3396, 0xff218725), TOBN(0x7cfbbd57, 0x2edf2704),
+     TOBN(0x8114d0ca, 0x4e8608c5), TOBN(0xceae65b9, 0x38c935b7),
+     TOBN(0x052b1407, 0x330af8fd), TOBN(0x02e189a1, 0x723c422b),
+     TOBN(0xf1cd6216, 0x657560c8), TOBN(0x099eec2f, 0xe5068361),
+     TOBN(0x68ef58fb, 0x3de78037), TOBN(0x83e0d34e, 0xf3e399e9),
+     TOBN(0x3a2a14c8, 0xf9a17095), TOBN(0xc7a360be, 0xaaf9f08a),
+     TOBN(0x6420155f, 0x30e99527), TOBN(0x8f610960, 0x9f180405),
+     TOBN(0x871a832f, 0x02bc97fe), TOBN(0xa14b3326, 0x8dc7f1f2),
+     TOBN(0xc9bd8b41, 0x87f78ad1), TOBN(0xd378d02a, 0x0b59b9c5),
+     TOBN(0x418a32a5, 0x35c0dc14), TOBN(0x4c96979d, 0xf53d85af),
+     TOBN(0xb6f40e97, 0x08eb4b26), TOBN(0xa21349ca, 0xcaa6252f),
+     TOBN(0xb13d8062, 0x5de38e2d), TOBN(0x54ea3684, 0x9b43c5d6),
+     TOBN(0xc0ad58d7, 0xb1d6521d), TOBN(0x182f8823, 0x22085236),
+     TOBN(0x9d50cecc, 0x2a08449e), TOBN(0xeb85e785, 0x17ab0b68),
+     TOBN(0xb8a22ab7, 0x8d74e26b), TOBN(0x77515524, 0x77d03987),
+     TOBN(0x117a63f2, 0x77ad71de), TOBN(0x1cca30d0, 0xc94c8c82),
+     TOBN(0xe5fefba9, 0x2f05382d), TOBN(0xcc9e8916, 0x9b4b42f1),
+     TOBN(0xbe939e13, 0x9fe716c1), TOBN(0xbf2b9c80, 0x95e38cc2),
+     TOBN(0xf60c4491, 0x37adde62), TOBN(0x3eb3338a, 0xf4df75a3),
+     TOBN(0x16398af3, 0xfe4d84df), TOBN(0xed752cf8, 0xfaf3e5f2),
+     TOBN(0x746a4339, 0xb4cf0e1c), TOBN(0xb8bd479a, 0x39fb6018),
+     TOBN(0x3a9a045b, 0x57dffed3), TOBN(0x2b46ea98, 0xa5ae3c78),
+     TOBN(0x74b5163f, 0xde6b0399), TOBN(0x069628a0, 0x80e511c5),
+     TOBN(0x19cfc882, 0x1b96672b), TOBN(0x2759c76b, 0x379d1f57),
+     TOBN(0xa6cc7a98, 0x2269a514), TOBN(0x1bc2015b, 0x683b1413),
+     TOBN(0xc43b1178, 0x1bf4be62), TOBN(0xd2941975, 0x7bf2b0be),
+     TOBN(0x1eac3587, 0xc4591cfd), TOBN(0x283169e6, 0x0e66d495),
+     TOBN(0xd39bedb7, 0x052352e1), TOBN(0xb04af7f2, 0xd719cd43),
+     TOBN(0x702143d4, 0xe92956d7), TOBN(0x53498722, 0xa0e5b847),
+     TOBN(0xf0e8edc5, 0x574885fb), TOBN(0x4d9418ac, 0x8b5187c6),
+     TOBN(0x70e99cb3, 0xd2a00019), TOBN(0xf0da5be4, 0xe7f8a61b),
+     TOBN(0x52704cbe, 0x7dd34fde), TOBN(0x0fb7224a, 0x2926bb6a),
+     TOBN(0x0d58bddd, 0xf2b0af92), TOBN(0x2f986a07, 0x0e9cad36),
+     TOBN(0xc85549d4, 0x80e3a6f9), TOBN(0xa013e913, 0x322cb14c),
+     TOBN(0x8a19cf30, 0xf25ac201), TOBN(0x130e4ce0, 0xffb8f2e4),
+     TOBN(0x21ad2c8c, 0x0ce56c13), TOBN(0x13ed2106, 0xb15f6a2f),
+     TOBN(0xa217b5f6, 0x9453ce96), TOBN(0x93b1cdc7, 0x64e0bf9c),
+     TOBN(0x753d894d, 0xc4fe8e72), TOBN(0x46c6cea3, 0xf3a3916a),
+     TOBN(0xc1fb78e1, 0x383dd581), TOBN(0x1b7ba1a9, 0x17376a3e),
+     TOBN(0xa1411287, 0x5df66852), TOBN(0x4e9d333c, 0xa30445d3),
+     TOBN(0xb5a26c14, 0x917568a9), TOBN(0x885f1857, 0xe857a6ac),
+     TOBN(0x05fbd3ee, 0x84b1f8cf), TOBN(0x5c1f4097, 0x1e81e4e1),
+     TOBN(0x43999be4, 0x011f30e6), TOBN(0xa8aab3bd, 0xa890719d),
+     TOBN(0x49d598ce, 0xc7088eb2), TOBN(0x7a892468, 0xe341047c),
+     TOBN(0x8e69b5c4, 0x07cb6075), TOBN(0x83d066fd, 0x8c37dc04),
+     TOBN(0x4fcc6d02, 0x6ffff7ac), TOBN(0x1afaaf74, 0x7edfb968),
+     TOBN(0x2972b753, 0x70d529de), TOBN(0xf65bff0d, 0x08218b2e),
+     TOBN(0x119b3c4b, 0x4182b9fc), TOBN(0xcab66591, 0x27b5e101),
+     TOBN(0xfff2e939, 0x2ab87a02), TOBN(0x1c10c40d, 0xeec5949b),
+     TOBN(0x98366224, 0x30aa1242), TOBN(0x833e9dee, 0xf225a4e7),
+     TOBN(0x07f1cfec, 0x992e8475), TOBN(0x377a9d79, 0x1ef83a8a),
+     TOBN(0xaf1d0863, 0xc6715544), TOBN(0x34dd65c1, 0x1fd71505),
+     TOBN(0x74d55c22, 0x04fed293), TOBN(0x31b1e50e, 0x86d2f3be),
+     TOBN(0x876148b9, 0xc09594ac), TOBN(0x73aace3b, 0x8900b56e),
+     TOBN(0x4617258a, 0xa2cf4c37), TOBN(0x554e8f16, 0xc6f38a92),
+     TOBN(0xd8594800, 0xda0723bc), TOBN(0x524452df, 0xf3c8381d),
+     TOBN(0x846dfa02, 0x138ca980), TOBN(0xaa77a80c, 0xe2d32e56),
+     TOBN(0x27573fbc, 0x419c86b5), TOBN(0xe7486807, 0xb70216c3),
+     TOBN(0x8b7a685a, 0xc72036e6), TOBN(0xa1764627, 0x15fae3d8),
+     TOBN(0x0a1f2361, 0x815f379c), TOBN(0x9811607e, 0x01ab64d2),
+     TOBN(0x31841038, 0xff2c75cd), TOBN(0x8751674e, 0x474982aa),
+     TOBN(0x2f32b55b, 0x52a2523f), TOBN(0x6ff8d2a7, 0xe85f2025),
+     TOBN(0xd2ec31ee, 0x707b2dcb), TOBN(0xdac81e59, 0x6e277971),
+     TOBN(0x5445e3a2, 0x0e78191b), TOBN(0x134dba0b, 0x8c80db2f),
+     TOBN(0xe9925a87, 0x94002b55), TOBN(0xe56fa2be, 0x4293c71d),
+     TOBN(0x72aca4d2, 0xa9d009c2), TOBN(0x0c1219dd, 0x02fb0741),
+     TOBN(0x689fbc66, 0x208fd227), TOBN(0x8266f2f7, 0xe4bb09d8),
+     TOBN(0x1a791f9b, 0x2a61b8bb), TOBN(0xb29b31b7, 0x3eff4f21),
+     TOBN(0x2f97803a, 0xab7812db), TOBN(0xdbf27bae, 0x880ceb4c),
+     TOBN(0xecb84887, 0x45e9db5b), TOBN(0x3dfd84e1, 0x5cb7d0ec),
+     TOBN(0xc89f61c2, 0x77c0b1e0), TOBN(0x7ada1d37, 0xb7656544),
+     TOBN(0x0bca9585, 0x910a966c), TOBN(0x80385b47, 0x6f12c20c),
+     TOBN(0xf63a1605, 0xa4b30374), TOBN(0x2f91b24c, 0x104b4783),
+     TOBN(0x9210f5b9, 0xb3ab423f), TOBN(0xb9aa656d, 0x2fd424a6),
+     TOBN(0x63c615d5, 0xf7e8d640), TOBN(0xd567ff98, 0xbb59cfec),
+     TOBN(0x78121697, 0xf7692947), TOBN(0xb9166739, 0xbd9f5ed5),
+     TOBN(0x58d9a4f4, 0xb64b20e2), TOBN(0x291898d9, 0xc9fcc93c),
+     TOBN(0xbce6509e, 0xd6c6065a), TOBN(0x39af658f, 0xb84834a4),
+     TOBN(0x0f919d44, 0x94b49185), TOBN(0x3b80fc51, 0x5dbe7308),
+     TOBN(0xb9fd8ae4, 0xe321c228), TOBN(0x4a46bd2d, 0x360692ba),
+     TOBN(0x91d5396e, 0xd05b84b0), TOBN(0x266e89fd, 0xd6b058d0),
+     TOBN(0x6fb142d7, 0xb2c42e38), TOBN(0x93c9fe18, 0x994ebc2f),
+     TOBN(0x90e17885, 0x104b04a3), TOBN(0x6a5fa420, 0x654eb6ac),
+     TOBN(0x26c8a9b4, 0x3f349b26), TOBN(0x39387f7e, 0xb4e528ae),
+     TOBN(0xa74bea43, 0x5eb46726), TOBN(0x0b3e82dc, 0x9150b043),
+     TOBN(0xc69ffac9, 0xe2fc799f), TOBN(0xd0479697, 0x48921338),
+     TOBN(0x91a68264, 0x0a4e061b), TOBN(0x93a6c41e, 0x3f410bcc),
+     TOBN(0xaea8d055, 0x6b1fb104), TOBN(0x2ff339a4, 0x31fe146f),
+     TOBN(0x3d7ef85b, 0xcf63c413), TOBN(0x1f0f57c5, 0x289a097b),
+     TOBN(0x82f2f83b, 0x5bda1160), TOBN(0x433eea4d, 0x6fea66e8),
+     TOBN(0x1f3fff4f, 0xcae11651), TOBN(0xfa71c3fd, 0x6b1c243b),
+     TOBN(0x59f36add, 0x674832a4), TOBN(0x7b6d3802, 0x2891e4e6),
+     TOBN(0x47b313bc, 0x084fa3c6), TOBN(0x90003ac6, 0x6584c9c0),
+     TOBN(0x9718c2dd, 0xbc802849), TOBN(0x9a5a2698, 0x2870ca08),
+     TOBN(0xb5cfe625, 0xcf68f352), TOBN(0x90d0e2ed, 0x6e6b0caa),
+     TOBN(0xb30780c3, 0xba64d50b), TOBN(0x16328345, 0x7acb4fca),
+     TOBN(0xf64e01fd, 0x84b258de), TOBN(0x2a25873e, 0x35dcd2f1),
+     TOBN(0x36606813, 0xce4b39da), TOBN(0x5285c91e, 0xa69a93e3),
+     TOBN(0x4da13aaa, 0xdcb501d6), TOBN(0xb90d0a52, 0x52e3dc24),
+     TOBN(0x6882d15e, 0x60a57d0f), TOBN(0x52142caf, 0x167612fe),
+     TOBN(0x532ccfb1, 0x463d39cc), TOBN(0xcdecde85, 0xe5a969f3),
+     TOBN(0xa89c1d1d, 0xd1bc4480), TOBN(0x9373f362, 0x83f32199),
+     TOBN(0x42f3493d, 0x6d653c44), TOBN(0xa867e4db, 0x6c80e27e),
+     TOBN(0x954fbd83, 0x5cb7623d), TOBN(0xba8b3007, 0x0b83d55c),
+     TOBN(0x71946b92, 0xe2b23256), TOBN(0xe0a2a7bf, 0xfaf95492),
+     TOBN(0x32ed3d91, 0x4e0c81ef), TOBN(0xb8c8b14c, 0x46f058d6),
+     TOBN(0xc76c917f, 0x67221924), TOBN(0xd26c1d51, 0x2ddf3cd4),
+     TOBN(0x184e1395, 0x4fc9b14a), TOBN(0x651a0c29, 0xc1969b8b),
+     TOBN(0x05687179, 0xc9d5bf9c), TOBN(0xb2f18ed1, 0xebcd85b6),
+     TOBN(0x8b662764, 0xe446f1ef), TOBN(0x6c0e051e, 0x71699f5a),
+     TOBN(0xf94a1151, 0x27d93da8), TOBN(0x751235c6, 0xa05fe7a4),
+     TOBN(0x40aaf88f, 0x624e9ae2), TOBN(0x6499b3f5, 0xf5f6e5c5),
+     TOBN(0x01fb0b8e, 0x98157f61), TOBN(0x070438f3, 0x33827220),
+     TOBN(0x7409012f, 0x50ab0b43), TOBN(0xdbbba563, 0x63c50e65),
+     TOBN(0x6b572ca3, 0xc0d084ad), TOBN(0xf10f6684, 0x7b76cd6c),
+     TOBN(0x32bcca97, 0x0c34363b), TOBN(0x7a9cef10, 0xb40e8157),
+     TOBN(0x3d5ffc51, 0x6eaec234), TOBN(0x7d7b41a5, 0x5f23d481),
+     TOBN(0xe5276c22, 0xeecdfe73), TOBN(0xa9b2725b, 0x8ac8c30d),
+     TOBN(0xee449588, 0xed0c743b), TOBN(0x6d3b82a3, 0x48df73b7),
+     TOBN(0xcb52edc2, 0x023cb0df), TOBN(0x08773a4d, 0xd5a24591),
+     TOBN(0x0d9a6aaa, 0xe12a9072), TOBN(0x4261f56f, 0x5bf5586e),
+     TOBN(0x184b0402, 0x60a08106), TOBN(0x1b398053, 0xb09cfa61),
+     TOBN(0xdf7f55b1, 0xd5dae483), TOBN(0x9554210e, 0x86ef2cde),
+    },
+    {
+     TOBN(0x564d6e85, 0x9204db30), TOBN(0x139bb928, 0x2aa84cdf),
+     TOBN(0x9413d7ea, 0x88476456), TOBN(0x5c554483, 0x5a1ffa66),
+     TOBN(0x7b863089, 0x2ed18080), TOBN(0x589aaf20, 0xd14e5daf),
+     TOBN(0xeee4f96f, 0x7b5f81ca), TOBN(0x88d47007, 0x1bb0b415),
+     TOBN(0x1bb400d3, 0x55c9bd11), TOBN(0x8402465c, 0x06fc2851),
+     TOBN(0xa81ba22d, 0x65063b3e), TOBN(0xbab2dcbc, 0x6e1aa0c6),
+     TOBN(0xe5f43f1a, 0xbe645e25), TOBN(0x62320533, 0x4df84be1),
+     TOBN(0x14ac7080, 0x21a2eaf4), TOBN(0x3f946464, 0x58beb26f),
+     TOBN(0x5f2a3e9a, 0x7a82d20f), TOBN(0x399e015c, 0x191011f2),
+     TOBN(0xfbec312a, 0x886ac8e6), TOBN(0x0dd5140a, 0xeda47f96),
+     TOBN(0x0d4df313, 0x26b47318), TOBN(0xe2c9ec78, 0xe6685ec8),
+     TOBN(0x4df119ae, 0xcd8442cd), TOBN(0xdb1ca955, 0x7b32a1cf),
+     TOBN(0x7e2c5852, 0x126506cc), TOBN(0xba94aac7, 0x08b3567d),
+     TOBN(0x6905cdf4, 0xc05a3f24), TOBN(0xbf5f559b, 0x3547f8b3),
+     TOBN(0x9e4b4e62, 0xaade7a1d), TOBN(0x56b8b9d6, 0x1fda3088),
+     TOBN(0xea3eb4c6, 0x4c43d89f), TOBN(0xfb7e537c, 0x9c69e047),
+     TOBN(0xc23d9491, 0xdfe5f6ab), TOBN(0x42fc362d, 0xc1a9c0af),
+     TOBN(0x04170b01, 0x127d2b35), TOBN(0x4f0f17bc, 0x04116aeb),
+     TOBN(0x716c01df, 0xc9184cf6), TOBN(0x914dc877, 0x895ceae7),
+     TOBN(0x696b2ae8, 0x390bff2e), TOBN(0xf6ccd628, 0xf88af5db),
+     TOBN(0xdada9bb9, 0x0f88095a), TOBN(0x7155c28f, 0x919ce305),
+     TOBN(0x32a01e47, 0x6d78b266), TOBN(0x6da94459, 0xb652c4f8),
+     TOBN(0xa31783a6, 0x827ea8ef), TOBN(0x4d69b7c6, 0xbdb1af2b),
+     TOBN(0x2874eb38, 0xaf31dab9), TOBN(0xa0ed9910, 0xafd9bace),
+     TOBN(0x7d892e3a, 0x4037f17e), TOBN(0x81fa9841, 0x5f91a4fa),
+     TOBN(0x17c7292d, 0x961cf02f), TOBN(0x35af0c0e, 0x388bcc75),
+     TOBN(0x340bec90, 0x127a29b0), TOBN(0x955714a4, 0x3d087445),
+     TOBN(0xfd430880, 0xa587c273), TOBN(0x715ecd50, 0xd24dfda2),
+     TOBN(0x4ade066d, 0xaafd6cef), TOBN(0xce59c8de, 0xf8c1decc),
+     TOBN(0x3e12a24a, 0x77b96ece), TOBN(0xee7c32fc, 0x44cc710c),
+     TOBN(0x70700e4f, 0x240e9bb7), TOBN(0x837ada54, 0x6a63b06e),
+     TOBN(0xa58ce980, 0xd19644ee), TOBN(0xcaa5d14d, 0x27e7451c),
+     TOBN(0x8e78d2ed, 0x387272fc), TOBN(0x9163a377, 0xfd8a0f13),
+     TOBN(0x858436bd, 0x635c55f0), TOBN(0x0a414f9b, 0x5ba5b0eb),
+     TOBN(0x2b58373a, 0x7d7383b1), TOBN(0x5e7b9d36, 0x6030a376),
+     TOBN(0x9c69af86, 0x543514ef), TOBN(0x044698cc, 0x26080ff3),
+     TOBN(0x76f54954, 0xa2e23074), TOBN(0x90393264, 0x17526081),
+     TOBN(0x0d095055, 0xf3b78a50), TOBN(0x1f3a3776, 0x69d8b26d),
+     TOBN(0x0575e3bb, 0xf5e7c8fb), TOBN(0xee7dd406, 0xee40b0c5),
+     TOBN(0xe6522e5d, 0x55dab556), TOBN(0x2d1b5709, 0xb61cd918),
+     TOBN(0x0ea9278e, 0x01400b8d), TOBN(0x9552e745, 0x6464f584),
+     TOBN(0x67f5645b, 0x12fc094f), TOBN(0x77c40f3c, 0xde303128),
+     TOBN(0x16d7e9a5, 0x0e3f3381), TOBN(0x017795ab, 0x59947693),
+     TOBN(0xb69b5708, 0x9222eaf5), TOBN(0x61b213e0, 0x1b77f122),
+     TOBN(0xa7cc8bbf, 0xdc8db00e), TOBN(0x1c51f5e4, 0x3aa7fc1f),
+     TOBN(0xb85b782e, 0xb4ac2d0c), TOBN(0x32fde94b, 0x0468e5ea),
+     TOBN(0x8ad5b9a2, 0x7f7ff0a9), TOBN(0xcd26f418, 0x8fdbb3f9),
+     TOBN(0x853bc95d, 0x6ebf89db), TOBN(0x1da0a323, 0xa066b849),
+     TOBN(0xc4cc7aab, 0x4bce0fa7), TOBN(0xd4a05b69, 0x6bc940f1),
+     TOBN(0xc77300e6, 0x392dbd11), TOBN(0x0dc2eac6, 0x21f70aae),
+     TOBN(0x9d4b513b, 0x4b2ad7e0), TOBN(0x19822167, 0xa6daee1d),
+     TOBN(0x7d71d202, 0x69b98eee), TOBN(0xdfd435dc, 0x35f3f150),
+     TOBN(0x66d46ad3, 0xddfd45ed), TOBN(0xf0325189, 0xe50a2f01),
+     TOBN(0xe19b9500, 0x3ec5683d), TOBN(0xc46ab0a2, 0x91dd97e9),
+     TOBN(0x74c971d7, 0xed682c4a), TOBN(0xafedac2d, 0xa14da289),
+     TOBN(0xd17838fe, 0xe39ba740), TOBN(0xeb497bca, 0x053536bc),
+     TOBN(0x551ba4ca, 0xde6d4c38), TOBN(0xa67be247, 0x4f52298b),
+     TOBN(0x98413188, 0x9a5b40a8), TOBN(0x083a26aa, 0xbb0acfb5),
+     TOBN(0x4929ff5e, 0x11d16ebb), TOBN(0x91f08b63, 0xa942ae7e),
+     TOBN(0xaa428ef3, 0x876663ec), TOBN(0xfaabd309, 0x1e97cbb2),
+     TOBN(0xca0ed50c, 0xf1edd62f), TOBN(0xc3c7ae6f, 0xd29f48d9),
+     TOBN(0xff47bf28, 0x8a72ae88), TOBN(0x584ddfe5, 0x348c6666),
+     TOBN(0x271137e9, 0x36731fdf), TOBN(0x714bc7db, 0x88d98bc8),
+     TOBN(0xcea912c1, 0x0da6be30), TOBN(0x91cb844d, 0xbe62d6a5),
+     TOBN(0xe16ca42a, 0xec027bfa), TOBN(0x0c88f701, 0x17603e76),
+     TOBN(0x799418e3, 0x63d5a31a), TOBN(0x033bb53b, 0xebb063f6),
+     TOBN(0xbcd05461, 0x625d3909), TOBN(0x2d7b7868, 0x85f23129),
+     TOBN(0x23b07887, 0x95090997), TOBN(0x216c08ae, 0x18d2c218),
+     TOBN(0xe1ccb6c1, 0xeebdbcf9), TOBN(0x89ca4552, 0xe873842e),
+     TOBN(0x4837f137, 0x3c2fcdd5), TOBN(0x805874e8, 0x108a8c0a),
+     TOBN(0xe7e524f4, 0x3d442fa7), TOBN(0x580d82be, 0xf8131f8a),
+     TOBN(0x6dcb7d27, 0x93d3d50f), TOBN(0x51207d3e, 0xb5b39168),
+     TOBN(0x9a3ce117, 0x09110fe9), TOBN(0x8f3c6e4f, 0x48721d93),
+     TOBN(0x60a62b48, 0x87bdfa61), TOBN(0x086dac65, 0x7c01d84a),
+     TOBN(0x4af7878c, 0x53841493), TOBN(0x3b1a8935, 0xb3bd5aa1),
+     TOBN(0x65c8445b, 0x902e5686), TOBN(0xde16cfa5, 0x2e3b1822),
+     TOBN(0x19879e78, 0x0a3e3684), TOBN(0xec553912, 0xee249180),
+     TOBN(0x8eb73fae, 0xf8f4c1ee), TOBN(0xdee59877, 0xb81fd20d),
+     TOBN(0x2452e63f, 0x20b5ece3), TOBN(0x17be9422, 0xb632dddb),
+     TOBN(0x01f89220, 0x94311e6d), TOBN(0x8f0fe052, 0xa332f84f),
+     TOBN(0x59657aab, 0x1b9784d5), TOBN(0x6f2ce032, 0xd8a7f347),
+     TOBN(0x84247793, 0x6b95e6e9), TOBN(0x34301cf4, 0x4395b044),
+     TOBN(0x98ebfd98, 0xf7fb5401), TOBN(0x14fd494b, 0xfcdb31a4),
+     TOBN(0x042f89d8, 0xf90e0481), TOBN(0x6b90a008, 0x4134ab52),
+     TOBN(0x8fa22555, 0x7fe2ffec), TOBN(0xc6dc3d32, 0xa778448f),
+     TOBN(0x4886fedb, 0x85f45aad), TOBN(0x5bdef90e, 0x51704d0c),
+     TOBN(0x46ad596d, 0xe2d1fdaf), TOBN(0x914e0090, 0x04126f0d),
+     TOBN(0x71aaeb18, 0xaef960a6), TOBN(0x8f4601e5, 0xac77472c),
+     TOBN(0x42e5a186, 0xd8d9768c), TOBN(0x8cbf3a6c, 0x00f6004f),
+     TOBN(0x9d4bf5ac, 0xc1ddebdc), TOBN(0x13354792, 0xa9c066fb),
+     TOBN(0x72e0b81c, 0x923fe808), TOBN(0x1e73b868, 0xc526d6e4),
+     TOBN(0x3f7bedc6, 0xa81f1e24), TOBN(0xed1ff363, 0xe920ba24),
+     TOBN(0x58234c89, 0x659604c5), TOBN(0xa6a421ad, 0xce4b0872),
+     TOBN(0x5dc8848a, 0xcc19578f), TOBN(0xfcb418d0, 0x4f28bdfc),
+     TOBN(0xf2e74820, 0x8d6442f5), TOBN(0x0c481d85, 0x4dcf6378),
+     TOBN(0x4987d1a6, 0x4556438b), TOBN(0x76359363, 0x3157c6be),
+     TOBN(0x29bbf3b7, 0x1c1dceef), TOBN(0x0995c340, 0x576f1dbd),
+     TOBN(0x0405db3d, 0x8fa61304), TOBN(0x63438f3d, 0xcc7d345e),
+     TOBN(0x688174dd, 0x942120e5), TOBN(0xc7dd05bd, 0xcd70c93c),
+     TOBN(0xdc8a32dc, 0x5e871ae0), TOBN(0x1a7896b9, 0x6178647a),
+     TOBN(0x1fc3f7a2, 0x59c437e3), TOBN(0x737de2e3, 0x24235e5e),
+     TOBN(0x589a56e3, 0x7a5eaabd), TOBN(0x5a79da8e, 0xcca140f3),
+     TOBN(0x3d8b0d82, 0xa12463fa), TOBN(0x63fc83d8, 0x0875daf5),
+     TOBN(0x42a30803, 0xbd9211f7), TOBN(0x62f6167f, 0x32d3935f),
+     TOBN(0x70cd6467, 0x6f269922), TOBN(0xf694ca21, 0x96163b47),
+     TOBN(0xf3bafb2d, 0x5f5ba669), TOBN(0xcf7cf341, 0xb8ed8333),
+     TOBN(0x34b2022d, 0x9997edc2), TOBN(0x57e6f4b5, 0x309c6508),
+     TOBN(0xf6fbf864, 0x64841008), TOBN(0xbc9821f5, 0xed075d44),
+     TOBN(0x78c80f73, 0xf37cc6b7), TOBN(0x41d28626, 0x6ab88fc2),
+     TOBN(0x2126981c, 0x58ca26fc), TOBN(0x7a956c64, 0xbe3dbf87),
+     TOBN(0x2f41e27d, 0xce0ce9f3), TOBN(0x0cb49ae0, 0xf4c98e5b),
+     TOBN(0xba6224a6, 0xcace473e), TOBN(0x25dddbc0, 0x393e092f),
+     TOBN(0x747daf46, 0xa4fb974d), TOBN(0xfb775fe7, 0xc76dbe2e),
+     TOBN(0xb7b3ad6d, 0x9670c22e), TOBN(0xc6580b23, 0x10a380bc),
+     TOBN(0x4ea226f5, 0x92087c3d), TOBN(0xe67c379f, 0xb53aa3c7),
+     TOBN(0x4133f831, 0x991c3c9b), TOBN(0x80f9e5bd, 0x4fa0dd18),
+     TOBN(0x0094e7c6, 0xc6f80fb4), TOBN(0x16e99ebc, 0x351bebd3),
+     TOBN(0xc555ed44, 0xaae16a6f), TOBN(0xe9d2846f, 0x2f6367eb),
+     TOBN(0xb34c93d0, 0x83d46d0f), TOBN(0xc0cb137a, 0x894fadc6),
+     TOBN(0x21e289f8, 0xab31f937), TOBN(0xac5e0516, 0x1bc72a35),
+     TOBN(0x6221871b, 0xf3d4db0d), TOBN(0x72d1fdce, 0xa039826c),
+     TOBN(0x69d9cc8b, 0x668c8022), TOBN(0x0bf359ce, 0xfee064ff),
+     TOBN(0xb8e636b7, 0xe8d16f19), TOBN(0xde88f403, 0x443160ac),
+     TOBN(0x4228177a, 0x032836ee), TOBN(0xee8fac37, 0xe9801d86),
+     TOBN(0x496c9363, 0x4626e343), TOBN(0xf6999578, 0xf4e4c8fa),
+     TOBN(0xce7306f6, 0xb8648a06), TOBN(0xe2775c8c, 0xae7996e5),
+     TOBN(0x7b47e678, 0xbf09d221), TOBN(0xf5251e1e, 0x515c2ace),
+     TOBN(0x087f9121, 0x77b48b41), TOBN(0xc40e7725, 0xeb38d74b),
+     TOBN(0x1d559f4a, 0xce95134a), TOBN(0x1048a1bc, 0x320c8bc6),
+     TOBN(0xad2ddaf8, 0xe3085f1b), TOBN(0xf1cfc4cb, 0x0ad35636),
+     TOBN(0x2bd8d4fb, 0x57db1e96), TOBN(0xd1813026, 0xe1976ab7),
+     TOBN(0xa80e501c, 0x15867022), TOBN(0xecaf1497, 0x01f68017),
+     TOBN(0xd82c5e79, 0x48ab68b7), TOBN(0xa0f117e4, 0x204d2962),
+     TOBN(0x99b3bda1, 0x7dedbf05), TOBN(0xb872dbff, 0x52786ecd),
+     TOBN(0x56253c32, 0x57592d3c), TOBN(0x495fbb05, 0x4d570c07),
+     TOBN(0x073c49cb, 0xfaecad3e), TOBN(0xec8c1f57, 0xb46bad46),
+     TOBN(0x13800a76, 0xce3b07c7), TOBN(0x9bbf87d7, 0x0ffaec55),
+     TOBN(0xf69a9ee3, 0xaf2426c3), TOBN(0x2d0c201f, 0x2fd70c22),
+     TOBN(0x957e5be1, 0xc42bb661), TOBN(0x3e6ae19d, 0x1dc771df),
+     TOBN(0x60af970d, 0xe3cfafa7), TOBN(0x721ce869, 0x5ebd1883),
+     TOBN(0xab0a80a5, 0xb87d0ede), TOBN(0x33576f02, 0x2954a3e3),
+     TOBN(0xcc2fe8c0, 0xc413fc00), TOBN(0x5ae762bd, 0xeb86a18b),
+     TOBN(0xbc309dde, 0x3fe6c6dc), TOBN(0xb4f9d001, 0xbf0d1eb5),
+     TOBN(0xf3f3c5b9, 0xd4fa748c), TOBN(0x78e8867f, 0x2ca78fdd),
+     TOBN(0x8f85f872, 0xcdf1624b), TOBN(0xfdce003b, 0xa7db0248),
+     TOBN(0x0ad33ef7, 0x1ad9866b), TOBN(0x27d12937, 0x296248a4),
+     TOBN(0x23bf35eb, 0xc99c656a), TOBN(0xcfb64da2, 0x17753ace),
+     TOBN(0x8bc0e741, 0x6fbf7969), TOBN(0x131018ef, 0xe719cff9),
+     TOBN(0x98f4ef66, 0xd1c02b67), TOBN(0xe8aa6cdb, 0x1f81f578),
+     TOBN(0xa6f97fb3, 0x159469de), TOBN(0xf8e834cd, 0xe3906d9e),
+     TOBN(0x33ccda6d, 0x71bbd3d1), TOBN(0xeac76a4a, 0xf952c038),
+     TOBN(0x2891eaa0, 0xe5b53383), TOBN(0xd629dbdd, 0xedcf6de7),
+     TOBN(0x4af093cd, 0xa3fb0fa1), TOBN(0x130fd057, 0x0d1ea294),
+     TOBN(0xb553cb13, 0xb57747bf), TOBN(0x107c0f0e, 0x024e856b),
+     TOBN(0xfd63a2ff, 0xbd631fef), TOBN(0x8df62ec2, 0x12c01222),
+     TOBN(0xacbce197, 0xc0af11a9), TOBN(0x35fa3e80, 0x5c4922b5),
+     TOBN(0xbc257ccf, 0xc3de57ba), TOBN(0xb481ca1c, 0x293ad2df),
+     TOBN(0xb123f3bb, 0x2058e222), TOBN(0x219cde82, 0xefe46989),
+     TOBN(0x58ac87b8, 0xe9a316da), TOBN(0xa8294237, 0xd4d25c91),
+     TOBN(0xb54dad88, 0x62d14158), TOBN(0x9250885f, 0xb3da2a84),
+     TOBN(0xb4e3bedf, 0xd54776bd), TOBN(0x81a4c582, 0x78043ee5),
+     TOBN(0x279a0963, 0x4eb87133), TOBN(0x827d333c, 0xf2bfdb52),
+     TOBN(0x3601c6d1, 0xed71e119), TOBN(0x3d9b1772, 0x0d64df1d),
+     TOBN(0x2f5bcc09, 0x3fa3c40e), TOBN(0x74b7b30d, 0x8e26aef5),
+     TOBN(0x98fd949b, 0x3d3ac848), TOBN(0xd99e99d0, 0x92e259f1),
+     TOBN(0x34404265, 0x8d353c77), TOBN(0xffc05a7d, 0x4d8dfb1f),
+     TOBN(0xbaf2f471, 0x4e9d92c9), TOBN(0xf354f8b2, 0x5ea9cef3),
+     TOBN(0xf2be0fea, 0xb8b2c8a0), TOBN(0xa392d3e3, 0xfbce308f),
+     TOBN(0x58cd793d, 0x02619258), TOBN(0x16a8c9e7, 0xfea6eacc),
+     TOBN(0x3fcae1ed, 0xb90f9cb5), TOBN(0x1df76d07, 0xd59bc4ce),
+     TOBN(0x39248217, 0x8574a3ce), TOBN(0x9d0df2b7, 0x03b6e82e),
+     TOBN(0x64227c0f, 0x33206733), TOBN(0xb909614f, 0xb342da7d),
+     TOBN(0xe46e977f, 0xb8e15a20), TOBN(0xdf2aa89d, 0x744eaa18),
+     TOBN(0xa40b36b7, 0x7ff12f89), TOBN(0xbf7ed788, 0x86b0e7d4),
+     TOBN(0x35930c5c, 0x9e044a5b), TOBN(0x599cfa2b, 0x4ac6b8a0),
+     TOBN(0x68f5f40d, 0xa8f06839), TOBN(0xe838649b, 0xe8a1b9d5),
+     TOBN(0x2e3c91a9, 0xdd45feda), TOBN(0x5f73aa38, 0x58de0625),
+     TOBN(0xcc2b2397, 0x7535cddc), TOBN(0x60e69d0b, 0xca7825fa),
+     TOBN(0x8f1a95c4, 0x62424bd7), TOBN(0x5e175a13, 0xf6f21e23),
+     TOBN(0x594e5b82, 0x4fa48b20), TOBN(0x2bfed204, 0x9b14fed3),
+     TOBN(0x87c925fc, 0x74484bc3), TOBN(0x052b634f, 0x5639abc5),
+     TOBN(0x169549b6, 0x290426dc), TOBN(0xfe515a22, 0xdaaefd38),
+     TOBN(0x8a63a39c, 0xb4d87ccb), TOBN(0x3dec5f62, 0x4034acdc),
+     TOBN(0x59969d81, 0x61090db0), TOBN(0xb089b8f7, 0xf157248d),
+     TOBN(0x42b0ca54, 0x9d59a29f), TOBN(0x522b3e3e, 0x9be7ee82),
+     TOBN(0x894aade2, 0xac166a7e), TOBN(0x57aaf19a, 0x9184ec33),
+     TOBN(0x84406a11, 0x5e50711a), TOBN(0x0cafd148, 0x1614f8d3),
+     TOBN(0xc6174fdc, 0x3f7d19f8), TOBN(0xca5bed9a, 0xff4958be),
+     TOBN(0x8dc18aaa, 0xe4fdd396), TOBN(0xf6e8a9ee, 0xd371c3f4),
+     TOBN(0xc6b58042, 0xa5dfefde), TOBN(0xccc3bbb6, 0xfc4f3577),
+     TOBN(0x9f583e4a, 0xdedfdd55), TOBN(0x9ea45133, 0xb48c5fb2),
+     TOBN(0xca2b3229, 0x232d61e0), TOBN(0x642101a8, 0xb0b5cb38),
+     TOBN(0x0cfac5fc, 0xa9ebda1a), TOBN(0x02398bd6, 0xd2dc9c7c),
+     TOBN(0xd95511d9, 0x80591234), TOBN(0x0e5cc99c, 0xe8230901),
+     TOBN(0x943350f6, 0x140eaba1), TOBN(0x9fe19108, 0xe0623c93),
+     TOBN(0x052bf5d9, 0xd74e189b), TOBN(0x3e341bff, 0x40cd7173),
+     TOBN(0x89b5b355, 0xcb7d384d), TOBN(0xedee32da, 0x50b76f18),
+     TOBN(0x6a9cfb19, 0x5804d9df), TOBN(0xccf638f8, 0x376fc2d8),
+     TOBN(0xebdce7a5, 0xe14de014), TOBN(0x0135085f, 0x7f606fa5),
+     TOBN(0xf8a3de5f, 0x69b58c3b), TOBN(0xbaa80445, 0x59ca19d1),
+     TOBN(0x3252147d, 0x0ce7238d), TOBN(0xd446960b, 0xd57bc36f),
+     TOBN(0x9b1743ce, 0xb275f5ca), TOBN(0xda048c48, 0x27629de8),
+     TOBN(0x005354db, 0xd3bbac67), TOBN(0x62c392fb, 0x1ba1facc),
+     TOBN(0xb066bfae, 0xa18da892), TOBN(0xdb090711, 0x367a8320),
+     TOBN(0xbb7092e2, 0x6f90249a), TOBN(0x6de14155, 0xe22da86b),
+     TOBN(0xe16136d3, 0xb38d4ad8), TOBN(0x9deaa5c9, 0xd0fbb940),
+     TOBN(0x54a54ba3, 0xaacf50e3), TOBN(0x66e5645a, 0xb9ba4570),
+     TOBN(0x77e28d94, 0x48cb742a), TOBN(0xc795b138, 0xed98a2c9),
+     TOBN(0x899331f6, 0x1daa17ee), TOBN(0xac950653, 0x4a77734f),
+     TOBN(0xd7f6304f, 0x71f3e3b6), TOBN(0xe7256955, 0x65fc119c),
+     TOBN(0x3e60a04c, 0xbe527794), TOBN(0xdaf53be4, 0x7c578fb0),
+     TOBN(0xf785a4f8, 0xebc0754b), TOBN(0x8b21b116, 0xde1b78b4),
+     TOBN(0xfe47e04f, 0x62fb1c56), TOBN(0x8a92f9e6, 0x229f1017),
+     TOBN(0x2d73dd23, 0x68b7842c), TOBN(0x3b43f7dc, 0xa56dbc4f),
+     TOBN(0x9435defe, 0xd0f3f4ca), TOBN(0xdabfb1ba, 0x500594e3),
+     TOBN(0x70e929e8, 0x428f5ead), TOBN(0x44adf585, 0xbdc7716e),
+     TOBN(0x7b7ff077, 0x02204867), TOBN(0xf2f306be, 0x0c78476c),
+     TOBN(0x48849fd5, 0x7e783793), TOBN(0xc2dc3c7d, 0xaf77e3c7),
+     TOBN(0x5eb2b691, 0xa980cdf6), TOBN(0x7ca7b7a4, 0x204e25df),
+     TOBN(0x1e7c2f82, 0xc5070eab), TOBN(0x32ca4b36, 0x4eb7cd3b),
+     TOBN(0x38ffde8f, 0xf94ad1ab), TOBN(0xb4757ae1, 0x59921b25),
+     TOBN(0x856cd3f3, 0xb4d2f624), TOBN(0x90593929, 0x1eb40708),
+     TOBN(0xffc4b89a, 0x1193b3e4), TOBN(0x6afba7a8, 0xbd2f804f),
+     TOBN(0x72aabbaa, 0x69dc21ed), TOBN(0x5d1da32e, 0xe7fb6de1),
+     TOBN(0x56c0f440, 0x98d1e26b), TOBN(0x9456a6c3, 0xf7cc7d6c),
+     TOBN(0x9eb0aebb, 0x14f2f24d), TOBN(0x51d7c699, 0x7dd788a5),
+     TOBN(0x053b8098, 0x46a22e97), TOBN(0x27d8ea2a, 0x8c025be8),
+     TOBN(0xe0bd464a, 0x10d5afaa), TOBN(0x137c452d, 0xe7cf120c),
+     TOBN(0xd06bd227, 0xd091397b), TOBN(0x4b307bf3, 0x21bc796f),
+     TOBN(0x701eaf3a, 0x7f5a37b0), TOBN(0x8d5a0f61, 0xac7d4718),
+     TOBN(0x0cf9eea3, 0xed8b1a37), TOBN(0x10854f10, 0x2aa9061c),
+     TOBN(0x0aaf430c, 0xa30eb4e6), TOBN(0xb74342f5, 0x2a050dfb),
+     TOBN(0x2feee9d7, 0x20e1899f), TOBN(0x49464a8e, 0xf2a1dbfc),
+     TOBN(0x4d7cf25e, 0x5762d68e), TOBN(0xe7b6e759, 0x7bf43462),
+     TOBN(0x71fce284, 0x79daf6e0), TOBN(0x2d3ff71f, 0x03858705),
+     TOBN(0x07d8d288, 0xbc4af4e6), TOBN(0x6777d197, 0x18f1c7d4),
+     TOBN(0xb5770041, 0x0e85f036), TOBN(0xe1bb263e, 0x4c8d9768),
+     TOBN(0x4fcc1d44, 0xe3917798), TOBN(0x274d1d90, 0x07abcde4),
+     TOBN(0xc9b8ae9f, 0xb7a10472), TOBN(0x6632e3be, 0x8d10e8ec),
+     TOBN(0xb6876fb0, 0x50f3a172), TOBN(0x753692d4, 0xb4cf4867),
+     TOBN(0xfe3624e6, 0x58e598f7), TOBN(0x15f90418, 0x6d81fb40),
+     TOBN(0xae762f7b, 0x9bea3649), TOBN(0xc48d2262, 0x161e85cb),
+     TOBN(0x8e8726a1, 0xcf5a21f0), TOBN(0x536c441f, 0xa1f6653b),
+     TOBN(0x0716bad0, 0x67ec5b86), TOBN(0xa423a957, 0xb2147d1f),
+     TOBN(0x8eec96c8, 0xdca2e393), TOBN(0x3619e36d, 0x2843ef12),
+     TOBN(0xdc16fe2d, 0x2ef695e1), TOBN(0x04ed2cad, 0xffea8124),
+     TOBN(0x5018a0ce, 0x180ce636), TOBN(0xc34b0bbf, 0xdce7b2f8),
+     TOBN(0x645a02a9, 0x0c54fc30), TOBN(0x6ee6772b, 0xf3f819d9),
+     TOBN(0xe2bbbdcd, 0x7cecded6), TOBN(0x9ae4fd55, 0x3f038851),
+     TOBN(0xc30664ab, 0xa2f316c7), TOBN(0x3cccf4a1, 0x63ffb50a),
+     TOBN(0xc37ee6ca, 0xd00fb8f2), TOBN(0x593db6d5, 0xad906eb1),
+     TOBN(0x8f75b594, 0x4aa84505), TOBN(0xeff39d82, 0x9e5939f0),
+     TOBN(0x4b7fab3c, 0xc064f530), TOBN(0x731153ae, 0xde175892),
+     TOBN(0x335e6503, 0x3d4c4e60), TOBN(0xb0876a8a, 0x776ce13a),
+     TOBN(0xa8a566ee, 0x22241ecd), TOBN(0xb7456b3e, 0x011e861c),
+     TOBN(0xa9aff4eb, 0x177dd490), TOBN(0x189b1ed9, 0xc8f77c40),
+    },
+    {
+     TOBN(0x624de687, 0x2857a1fc), TOBN(0xbd0a0d9c, 0x2ff8f505),
+     TOBN(0xeecb4fad, 0xc381bc9a), TOBN(0x72386292, 0xfa94e41b),
+     TOBN(0x354d3f83, 0xe75fc753), TOBN(0x06afc753, 0xa7a5a6bf),
+     TOBN(0x1ce792ee, 0xb2f568dc), TOBN(0xc5faaee3, 0xbd2f9647),
+     TOBN(0x175fbeb0, 0xf912b74f), TOBN(0x45fbe8e1, 0x6e0ceedd),
+     TOBN(0xf0e1aa68, 0xd9233ee7), TOBN(0xe55fc1ce, 0x406a626e),
+     TOBN(0x20efa1b9, 0xe08712e7), TOBN(0x5fd108b5, 0xbcfd6360),
+     TOBN(0xea431df6, 0xeec1edac), TOBN(0xae1c0521, 0x940803f1),
+     TOBN(0x584a16d0, 0x15407ffe), TOBN(0xa977f702, 0x08a82a69),
+     TOBN(0x52eefecf, 0x67f8a198), TOBN(0xec213738, 0x19f7a7e0),
+     TOBN(0x6795cfef, 0x35987b9a), TOBN(0xb243403b, 0x97028480),
+     TOBN(0xac24b12b, 0x9c1b9124), TOBN(0x1f379501, 0xa90f8aeb),
+     TOBN(0xa8e97fb6, 0x64bc0f09), TOBN(0x0b913991, 0xc953cd08),
+     TOBN(0x8385a1b3, 0x7fc3bf00), TOBN(0xb6e74dec, 0xb09ccd8f),
+     TOBN(0x6e1df026, 0xec473ea7), TOBN(0xf2f7fbbe, 0x530766bd),
+     TOBN(0xf18cb47a, 0x3292052b), TOBN(0x7f8d4592, 0x9114866a),
+     TOBN(0xf0a1c565, 0x8bfa2c22), TOBN(0xc28518c3, 0x2b326c0e),
+     TOBN(0xabafc6f0, 0xec107d66), TOBN(0xbc7a6abf, 0x8478907a),
+     TOBN(0x8c1c8f6a, 0xa2920288), TOBN(0x6c87579d, 0x930c043e),
+     TOBN(0x25ee808d, 0xb309696d), TOBN(0x433bbbda, 0xb7a71041),
+     TOBN(0x48d6d957, 0xb3086691), TOBN(0x9946a29b, 0x26640916),
+     TOBN(0x932ca93c, 0x43db59a9), TOBN(0xaa61a0c5, 0xe4fe91ba),
+     TOBN(0x9e22e112, 0x815bf003), TOBN(0xa9ed1b18, 0xc86ba8d3),
+     TOBN(0x1b5d3c14, 0x1069f434), TOBN(0x3cd2ebd0, 0x1cc01754),
+     TOBN(0x5c06b244, 0x3350f670), TOBN(0x7557dc9d, 0xf6f9c751),
+     TOBN(0xa7ebd3b8, 0xde66fd97), TOBN(0xc126dbaa, 0x2befe6fe),
+     TOBN(0x312f4897, 0x396f434a), TOBN(0xe05cfcd6, 0x61a4124d),
+     TOBN(0xc83b8688, 0x1525c05e), TOBN(0x4646dbf2, 0x11899f64),
+     TOBN(0x2b7507cb, 0x8e419e08), TOBN(0x785328d7, 0xaf855eec),
+     TOBN(0x875db0c7, 0x7b8683a5), TOBN(0x3d1bc968, 0x90a597e9),
+     TOBN(0x7d4afa10, 0x47eeeab4), TOBN(0x2668dd43, 0xd680ca71),
+     TOBN(0xc3210d1f, 0x17365023), TOBN(0xd5bb2ee4, 0x17fb31cc),
+     TOBN(0xbefb6a4f, 0x08e9ba09), TOBN(0xc6beedb8, 0xb0c1b6e1),
+     TOBN(0x59daf057, 0x3510ef35), TOBN(0x604047cf, 0xdbbabc65),
+     TOBN(0xfabc80a8, 0xa06b7340), TOBN(0x7433dee7, 0xdf765977),
+     TOBN(0x149a2c4a, 0xfd807cfb), TOBN(0x14e8ad3b, 0x3480a086),
+     TOBN(0xb0c3156f, 0xb22c5f89), TOBN(0xd10ece4a, 0xbf78675a),
+     TOBN(0xe270e317, 0x80b8ad9f), TOBN(0xfe7a6210, 0xb0c2b420),
+     TOBN(0xf091d738, 0x125ef635), TOBN(0xf1f277d6, 0xc1a6f202),
+     TOBN(0xe2727e7b, 0x3587d9bb), TOBN(0x83b209a9, 0xb3e2b84b),
+     TOBN(0xc9eb445d, 0x7a13effa), TOBN(0x89b856f1, 0x0d697480),
+     TOBN(0x834bbae2, 0x25c03cb7), TOBN(0x0d8adb85, 0xe0b4a7b2),
+     TOBN(0x7b6884af, 0xc7fbc240), TOBN(0x6b485409, 0xaa4f9097),
+     TOBN(0x4d0a367f, 0x290c106f), TOBN(0xab87d218, 0x3f0efdfd),
+     TOBN(0x15b9bab7, 0x50f2b65b), TOBN(0xa7403d4b, 0x5e5d53e4),
+     TOBN(0x2e23e376, 0x28529212), TOBN(0x6fe903a2, 0x6e050767),
+     TOBN(0x4c5291a1, 0x6cf570fb), TOBN(0x4bfb8607, 0x7a30b326),
+     TOBN(0xec4905f8, 0x27c572a9), TOBN(0x72eeb8c9, 0x0f381c31),
+     TOBN(0x33346cec, 0x460adca0), TOBN(0xd4d5bba8, 0x7b34756a),
+     TOBN(0x02b2e2d4, 0xeac84add), TOBN(0xa129845b, 0xdc1053b5),
+     TOBN(0x53f067e0, 0xdca6f9ce), TOBN(0x6e9998ed, 0x3526aba6),
+     TOBN(0xa4aef9e2, 0x1c0982da), TOBN(0xfe5b606e, 0x93f5d96f),
+     TOBN(0x26b8502e, 0x9c14b699), TOBN(0xf1bcdca6, 0x0948a291),
+     TOBN(0x73e43a32, 0x2aefd415), TOBN(0x7f523357, 0xd1e2cfb5),
+     TOBN(0xa60151c0, 0x97d3fa94), TOBN(0x820c0d58, 0x72129630),
+     TOBN(0xb8f2e1ed, 0x5854acf5), TOBN(0x86d6646c, 0x3c656ac3),
+     TOBN(0x2284a612, 0xbef1d0da), TOBN(0x2e7c5f4e, 0xa8c8faba),
+     TOBN(0xfd441ae7, 0x70303ea3), TOBN(0x9613f329, 0x5161cf82),
+     TOBN(0x65a3cc65, 0x2e19531f), TOBN(0x177a2775, 0x34281f69),
+     TOBN(0x0cc692a4, 0x7c82e094), TOBN(0x9d62a55b, 0xb6f377f0),
+     TOBN(0xa24cf6ac, 0xf96ec2b8), TOBN(0xd06747c3, 0xa961cc16),
+     TOBN(0x57c7001c, 0xbd17f0a2), TOBN(0x5f298db0, 0x34afe2d6),
+     TOBN(0x51b01ef2, 0xdf12f671), TOBN(0xc01c5066, 0x5ce712fe),
+     TOBN(0xac0f4034, 0x92a74776), TOBN(0xa3e9934f, 0x08d696bd),
+     TOBN(0xafb6981a, 0xe7daaff8), TOBN(0x5f8998d9, 0x73bdcafc),
+     TOBN(0x23ec39e1, 0xbaf9906c), TOBN(0x5e248410, 0xc999c9c0),
+     TOBN(0xd14c7a89, 0x17dad895), TOBN(0xfde9d01a, 0xcbb3f6b9),
+     TOBN(0x1d6b26ef, 0x5f698f1b), TOBN(0xc6495cd1, 0xf0baff97),
+     TOBN(0x5a72dc07, 0x587674ec), TOBN(0x100f9ff0, 0xdb09cd65),
+     TOBN(0xec0fb71f, 0xb30cf6e6), TOBN(0xf54cb597, 0x81066143),
+     TOBN(0x0090e997, 0x633857c4), TOBN(0x7326ed15, 0xda92c5d2),
+     TOBN(0x794cd8af, 0x47c56e86), TOBN(0xb272112f, 0xf89214c9),
+     TOBN(0x37960861, 0x3445879d), TOBN(0xc5e496b0, 0xf2fcfc55),
+     TOBN(0xfe74e95f, 0x6559e153), TOBN(0x1e18b2b5, 0x54a772af),
+     TOBN(0xd146980c, 0x157c157c), TOBN(0x31ee3f25, 0xa11d77b5),
+     TOBN(0x7762a07d, 0x5707db6d), TOBN(0x00804bcb, 0xbd2022b8),
+     TOBN(0xdf3f4658, 0xd571c59e), TOBN(0xc49e7a34, 0xcf45c1ee),
+     TOBN(0xf401ba3d, 0x43972cff), TOBN(0x146d989c, 0xe509c2b6),
+     TOBN(0x7c68d6c8, 0xeb72392f), TOBN(0xdd048de5, 0x0658b8e6),
+     TOBN(0xc9dc61b7, 0x9a0aeb72), TOBN(0x98b080e0, 0xb741d644),
+     TOBN(0xa6ec0eed, 0xb1c5982a), TOBN(0x58d28317, 0x5ebbc26f),
+     TOBN(0xac8f1e1e, 0x33e5b7dc), TOBN(0x31e4f65e, 0x9d8f9fed),
+     TOBN(0x6c9af383, 0x904ad76d), TOBN(0xfc38c53c, 0x9bdb0517),
+     TOBN(0x9ae278ee, 0x0e69f85e), TOBN(0x18b362b7, 0xefd9d887),
+     TOBN(0x65a5f74b, 0x5bbbd3ac), TOBN(0x41eb4593, 0x077bfb4f),
+     TOBN(0xb642934b, 0x83b38100), TOBN(0x643ceed7, 0xac1a99bb),
+     TOBN(0x9c27e66d, 0xee7cd5f7), TOBN(0x2ccf87d5, 0x6ddbaa6b),
+     TOBN(0xd51ca739, 0x447b1192), TOBN(0x78471053, 0x95f5f142),
+     TOBN(0x915f50cd, 0x3a650829), TOBN(0xe032bdc5, 0x898a6a1c),
+     TOBN(0xde8fb4f1, 0x2d15959f), TOBN(0x1fc5fc73, 0xbad56140),
+     TOBN(0xdafbf206, 0x8e60c3c3), TOBN(0x4963dc95, 0xe428adb5),
+     TOBN(0x1538e081, 0xd49584fb), TOBN(0xb34add66, 0xbc0e5fa9),
+     TOBN(0x404ecf12, 0xa7f28b2f), TOBN(0x6ddc3ce1, 0x7fa9c435),
+     TOBN(0xda887e3f, 0x61ee755e), TOBN(0x4b5da661, 0x8f71280a),
+     TOBN(0xee5a86df, 0xdc79a4cd), TOBN(0xd8514b8a, 0x99be4d36),
+     TOBN(0x674793ea, 0xcc82c751), TOBN(0xf3a2123a, 0x437aedcd),
+     TOBN(0xf825ff37, 0xfcd6f027), TOBN(0x60a056d8, 0xa681a001),
+     TOBN(0x92a39248, 0xaa92c135), TOBN(0x61884e23, 0xdcd190a7),
+     TOBN(0xec0d1420, 0x24cc911c), TOBN(0xbdb0baae, 0x5aa16ad7),
+     TOBN(0xf12726b5, 0x8a1694d7), TOBN(0x8c7cf113, 0xc93673f9),
+     TOBN(0x02fb6c69, 0x7f2edc38), TOBN(0xcc4d4304, 0x2fbe8690),
+     TOBN(0x405b2491, 0xe89c80d5), TOBN(0xdef46c76, 0x3d938bc1),
+     TOBN(0xd92ec0fa, 0x2520e3b0), TOBN(0x2501cfa3, 0x1fe2dfda),
+     TOBN(0xe7c5753d, 0x1d5c8474), TOBN(0xc059abc0, 0xe6226dcf),
+     TOBN(0x2dceefe6, 0x55a9011d), TOBN(0x8799064a, 0xbbbbef00),
+     TOBN(0x7fe944c2, 0x0b49b5ef), TOBN(0x722bbef0, 0x225b21dc),
+     TOBN(0x84687cbb, 0xd2bb14af), TOBN(0xfc4ab4f0, 0x9b6f6caf),
+     TOBN(0xb7b7bb59, 0x2c146a52), TOBN(0xb90d67f2, 0x1dfea10b),
+     TOBN(0xca4ca8c8, 0x713e1d30), TOBN(0x50cbb994, 0xf8a13db8),
+     TOBN(0x2bee12b2, 0xa5b0e3e5), TOBN(0xa7c5d6d1, 0xe71e19fb),
+     TOBN(0x28442423, 0x9e0314cd), TOBN(0xc95c2746, 0x66cda5c0),
+     TOBN(0xfe52a79a, 0x1c5ffd19), TOBN(0xb93875cc, 0x38a0339f),
+     TOBN(0x6a94196c, 0xb49fb049), TOBN(0xbeb1eb4b, 0xcc12a38d),
+     TOBN(0xbc136771, 0xf5e8d167), TOBN(0xa91e1232, 0xd5f8ae87),
+     TOBN(0xb2e812c7, 0x95172460), TOBN(0xc699d376, 0xb8f81870),
+     TOBN(0x9e4a3b70, 0xa8900827), TOBN(0xe0d4b2f4, 0x506c0b29),
+     TOBN(0x13b4d1c7, 0x7246fd96), TOBN(0x84ea2158, 0x33965581),
+     TOBN(0x9b9f071b, 0x2e53c024), TOBN(0xcb005908, 0x864a1b78),
+     TOBN(0x03daddf5, 0x3f742c2f), TOBN(0xd29230e5, 0xdf595911),
+     TOBN(0x3f7d4e6b, 0xca0406a1), TOBN(0xeb646f66, 0xb1db7e47),
+     TOBN(0xb896003e, 0x590e3107), TOBN(0x7a0dc361, 0xf754ac01),
+     TOBN(0xe877a6f3, 0xe63ab0ac), TOBN(0xd43b54f3, 0xdf60d307),
+     TOBN(0x65ef91ba, 0x59cf0add), TOBN(0x35e99393, 0x18990eb4),
+     TOBN(0xc186ab16, 0x8e46fbf6), TOBN(0x4c0eb22f, 0x8c1eaa91),
+     TOBN(0x4599b894, 0x1abd31f0), TOBN(0xdb34198d, 0x9a1da7d3),
+     TOBN(0xa8b89523, 0xa0f0217d), TOBN(0x2014cc43, 0xe56b884e),
+     TOBN(0x6fb94f88, 0x49efd4ee), TOBN(0xf1b81710, 0x287f4ae0),
+     TOBN(0x89d38a9a, 0x99fd2deb), TOBN(0x8179277a, 0x72b67a53),
+     TOBN(0x0ef6ce56, 0x1a03755b), TOBN(0x8dc768f2, 0xfcdb3469),
+     TOBN(0x0be58a91, 0xa21d959d), TOBN(0xea44861a, 0x9b103cd0),
+     TOBN(0x332e86e7, 0x808b8a46), TOBN(0x9882015c, 0x8772c3f8),
+     TOBN(0xe6b272fe, 0x9f4b5d29), TOBN(0x0e183a28, 0xa29b023b),
+     TOBN(0xf2fab88f, 0x2286ebf3), TOBN(0xb7532ced, 0xfce83e6f),
+     TOBN(0x17999d7c, 0xe0cde4fc), TOBN(0x7230fd85, 0xc1b7668a),
+     TOBN(0x97a57d39, 0xef588309), TOBN(0x7e175f28, 0xf906f6e7),
+     TOBN(0x51f67413, 0x72b70bfe), TOBN(0x2132f595, 0x2f82218c),
+     TOBN(0x9cc0746e, 0x9d8727cb), TOBN(0xa2af77fb, 0xbba1ec8e),
+     TOBN(0xc75aee60, 0x31a67cc9), TOBN(0xaeab9e0f, 0x57408325),
+     TOBN(0xf24de697, 0xec34bb89), TOBN(0x06b90039, 0x5d958bdf),
+     TOBN(0x6f55222e, 0x0603d6cc), TOBN(0x496537b5, 0x2eb0b239),
+     TOBN(0x083e5889, 0x8be08323), TOBN(0xc573596e, 0xf8dc0a78),
+     TOBN(0xc3e988fa, 0xe8901eca), TOBN(0x7f7b48f6, 0x6e350257),
+     TOBN(0xed820567, 0xa216e329), TOBN(0x55f46737, 0x8ce989c1),
+     TOBN(0x7f48c5f1, 0xeeab9441), TOBN(0x1d3cac11, 0x86fe0831),
+     TOBN(0xe0364bae, 0x408a0306), TOBN(0xe8d8aba0, 0x7a4eb2cb),
+     TOBN(0xe548725e, 0x1fd7d5da), TOBN(0x8de04491, 0xed5ed958),
+     TOBN(0x3e75eba2, 0x61d73977), TOBN(0x4f580400, 0x55420386),
+     TOBN(0x54642fa4, 0xd859a690), TOBN(0x2c905f7e, 0x296e336e),
+     TOBN(0x4e287e66, 0x22e260bc), TOBN(0x71a2ec99, 0x4a28d5bd),
+     TOBN(0x5528da21, 0xa7c5c3e3), TOBN(0xae9f6856, 0xa49471e0),
+     TOBN(0xdcd8e66b, 0x587cd94f), TOBN(0x91afbd79, 0x6c7b7db8),
+     TOBN(0xdf2e6625, 0x067e3cdd), TOBN(0x15b5a329, 0xa6375f59),
+     TOBN(0x3b8b3b1d, 0xb6586c5f), TOBN(0xe4d50a77, 0xd34f10fb),
+     TOBN(0x26cb86f5, 0x7c3c01f7), TOBN(0x36e9d3cc, 0x8c57e6f7),
+     TOBN(0xaa8e7ce1, 0x62c6dbae), TOBN(0x7f6b7689, 0x60d7fae5),
+     TOBN(0x519a7659, 0xc797ee16), TOBN(0xa1c7b30e, 0xb36a6b1b),
+     TOBN(0x8da05ba6, 0x74dff201), TOBN(0xd2eac07f, 0x40d0a835),
+     TOBN(0x2701eb31, 0x610a7d6f), TOBN(0x5c17a91e, 0xbf893c4f),
+     TOBN(0x68b92e88, 0x6bc8b161), TOBN(0xa312fd5b, 0xf52e6ec0),
+     TOBN(0xf7daf460, 0x6b7952cf), TOBN(0x847f0cf3, 0x18aeb57a),
+     TOBN(0x27b178ed, 0xb0146708), TOBN(0x85a23554, 0x54ca2aa5),
+     TOBN(0x80dd123c, 0x395a7b16), TOBN(0x64a9337b, 0x0058bfce),
+     TOBN(0xf6ae9380, 0xf4addc4a), TOBN(0x0f84385a, 0x464536f1),
+     TOBN(0x41fc2270, 0x16534f6c), TOBN(0x13d8976f, 0xb8795ec3),
+     TOBN(0x2e90b3e4, 0x8e12c560), TOBN(0x242a86ec, 0x239b2c58),
+     TOBN(0x6fb42ecc, 0x0768275c), TOBN(0xee341cd0, 0xbd96de9e),
+     TOBN(0xfd1833ac, 0x84355d11), TOBN(0xf062392c, 0x5f55ec6c),
+     TOBN(0x6ee7b59b, 0xfee33fba), TOBN(0x8113f0ca, 0xabf86e0f),
+     TOBN(0x2285aaaf, 0xcc68033c), TOBN(0x850b732b, 0x78430646),
+     TOBN(0x50fa4b61, 0x2b3fa03d), TOBN(0x4d893ecc, 0x3caf050d),
+     TOBN(0x454764e6, 0x988df419), TOBN(0x055d8a4b, 0xfb61f1a4),
+     TOBN(0x3b7c5f4b, 0x8475e07a), TOBN(0xf93a198b, 0xa6a228e4),
+     TOBN(0xe0a8ce61, 0xec8d566a), TOBN(0xe41397d6, 0xc55f4bd6),
+     TOBN(0x4cc18d48, 0x654bdf55), TOBN(0xe1b49f9e, 0x9325ac25),
+     TOBN(0x79840752, 0x72c68871), TOBN(0x8930d8b5, 0x6d806fe8),
+     TOBN(0x11c8b5a8, 0x0bd5f65e), TOBN(0xe931c025, 0xbf37d7a8),
+     TOBN(0x25b17fd9, 0xae8af083), TOBN(0xd589fd8b, 0xde4215ed),
+     TOBN(0x56378f04, 0x4b3f61fd), TOBN(0xf0f39213, 0x6bfb4f9a),
+     TOBN(0x6b0f9596, 0xe906cc6a), TOBN(0x441f13da, 0x096f8296),
+     TOBN(0x08f933d4, 0x1e4940e0), TOBN(0x6c35391c, 0x5a53e7ee),
+     TOBN(0x5f41c4d3, 0x19c3d18e), TOBN(0xc0804e09, 0x1d389d95),
+     TOBN(0x7323a9ab, 0x18a5a3f2), TOBN(0x7b7c2475, 0x410a6381),
+     TOBN(0xd362eb9a, 0xb02cfe18), TOBN(0x79ef3d0a, 0x553b2970),
+     TOBN(0x371f7760, 0x3d2acdab), TOBN(0x6cd37890, 0x7f241dfd),
+     TOBN(0x592a242e, 0xdf4a28e4), TOBN(0x1e623cdc, 0x1bb45217),
+     TOBN(0x5a9633a6, 0x494074d3), TOBN(0x81b74307, 0xd52fbfd8),
+     TOBN(0x98292651, 0xdec4c5ff), TOBN(0xe1b7bc86, 0x3e0f6edf),
+     TOBN(0x3d5fd86a, 0x6bb8fb31), TOBN(0xa830e9a2, 0x1cf29f19),
+     TOBN(0xfffc5482, 0xcf69c747), TOBN(0x7748a0f4, 0xa83549fd),
+     TOBN(0xba1c8a0d, 0xe7ccf4a6), TOBN(0x6cd1399a, 0xa2ede6b7),
+     TOBN(0x8fb634e6, 0x87bb90d9), TOBN(0xfa8e659b, 0xc59a5304),
+     TOBN(0xcd6bfc75, 0xa9122d95), TOBN(0xdb107def, 0xdfa6d75a),
+     TOBN(0xb0ec4cfc, 0xcc27760a), TOBN(0xf24c1e22, 0xbed3a1a1),
+     TOBN(0x4f8522a1, 0x819bffc7), TOBN(0x263c7b5b, 0xa93d97e1),
+     TOBN(0xab1d31e0, 0xa4b4de49), TOBN(0x374e968b, 0xebbfe8f5),
+     TOBN(0xe82e9756, 0x51ca0d08), TOBN(0xc05715a2, 0x7df3f2df),
+     TOBN(0x941f02c5, 0x038004ad), TOBN(0xc136a2a5, 0xa0fd46d4),
+     TOBN(0x85db7d24, 0x3d31d26c), TOBN(0x05bba6af, 0xbfefeecc),
+     TOBN(0xf803b539, 0x5a60aebf), TOBN(0x9bb8a479, 0x813d0e6d),
+     TOBN(0xb689c813, 0x066abdfb), TOBN(0xd93b3f4b, 0x0072e530),
+     TOBN(0x242140a4, 0x987446ad), TOBN(0x40b3f709, 0x06a02f0c),
+     TOBN(0x33f9bf20, 0xa0fd6018), TOBN(0x58517c18, 0xf21abfdc),
+     TOBN(0xa33dc5db, 0xc1f80f3f), TOBN(0xbb7dfe27, 0x7ec91c80),
+     TOBN(0xd2cf9338, 0x8ca97dd8), TOBN(0x5761f871, 0x32e43d44),
+     TOBN(0x3c8ffb0e, 0xe513ea90), TOBN(0x91ecda36, 0x79bcdecc),
+     TOBN(0xdad3fdd5, 0x9b1a5514), TOBN(0x8fb630f9, 0x640d3af0),
+     TOBN(0x82949b09, 0xf9d2e0be), TOBN(0x079ca0ff, 0xeba23663),
+     TOBN(0x51e62c53, 0x135238d4), TOBN(0xf5fa0c61, 0xc446bd67),
+     TOBN(0x19dcdd2f, 0xe8da43d6), TOBN(0x46fbf7ea, 0x95f29b5b),
+     TOBN(0x7f3eaa05, 0x635e8388), TOBN(0x5ef817c3, 0x5369750b),
+     TOBN(0x06025893, 0xc860c4aa), TOBN(0xa2f6684d, 0x5551c9ef),
+     TOBN(0xd6af67dc, 0xfbc0c667), TOBN(0xfd8d7394, 0xcd2fe44b),
+     TOBN(0x011968ae, 0x302a17cc), TOBN(0x2206ff24, 0xc3e5a5cb),
+     TOBN(0x4c7f0df3, 0xa20dbfb7), TOBN(0x59566376, 0xa395dd6f),
+     TOBN(0x68ff3d9f, 0x373ea76b), TOBN(0x2394f93a, 0xf6cf8ada),
+     TOBN(0x3acc5dba, 0xe7514a94), TOBN(0x0340da7a, 0x5ddfa11b),
+     TOBN(0xc3f03022, 0x1a05155d), TOBN(0x6cbbdc6b, 0x4f7656c0),
+     TOBN(0x6e30dbdd, 0x0b0875f5), TOBN(0x5e7c2883, 0x3471b0d5),
+     TOBN(0x49cfd71c, 0x408b4bc8), TOBN(0xd29a184e, 0xf01c002b),
+     TOBN(0x308be85c, 0xff415b0f), TOBN(0x1b4176f0, 0x01a8fe7d),
+     TOBN(0xb850acc7, 0x0c33bed3), TOBN(0x76aac640, 0x23af7af0),
+     TOBN(0x049187ee, 0x21d5853f), TOBN(0x44fbf6e5, 0x6620195c),
+     TOBN(0xf0abf14b, 0x36158178), TOBN(0x9866ffca, 0x90e419c2),
+     TOBN(0x7522e277, 0x9e8523a8), TOBN(0x2f2590f3, 0x08e90f1d),
+     TOBN(0xde1c0c52, 0x66d3f75b), TOBN(0x47dc9ceb, 0x6c299b57),
+     TOBN(0x4ad12847, 0x51f7f2b5), TOBN(0xeedf9d8d, 0x452b07a5),
+     TOBN(0x207b0627, 0x3dad76c6), TOBN(0xccbb5201, 0x5d4c0280),
+     TOBN(0x0bdca05b, 0xb019ae8d), TOBN(0xb5f8d088, 0xf2da7eb4),
+     TOBN(0x4e79a0be, 0x4626c00c), TOBN(0xf6fdd64f, 0x5af82c0f),
+     TOBN(0x7a828224, 0x5f7cba0f), TOBN(0xc2214834, 0xb0765493),
+     TOBN(0x5b0d0d1a, 0xabd53ccf), TOBN(0x3b03a22d, 0xf4a1b517),
+     TOBN(0xb235c862, 0x6ece453e), TOBN(0xf43ac344, 0xf66471c7),
+     TOBN(0xeaff93dd, 0xa1552fa5), TOBN(0xef1b40dc, 0xcf3ae702),
+     TOBN(0x35ced3fd, 0x9ca613a4), TOBN(0x90e350ab, 0xa2f33a0d),
+     TOBN(0x47bb89aa, 0x002b5738), TOBN(0xafc01bba, 0x032b8b08),
+     TOBN(0x688ae119, 0x61588b4b), TOBN(0xdb7d820a, 0xcf66ef14),
+     TOBN(0x83b654db, 0x084910bf), TOBN(0xbb581f8b, 0x60ea75a1),
+     TOBN(0x6917f282, 0x1cdae7c4), TOBN(0xb719f931, 0xa40a07c3),
+     TOBN(0xf8efb8b9, 0x31297b3b), TOBN(0x74246ded, 0x4d6fc5c6),
+     TOBN(0x5a111754, 0xd2c61503), TOBN(0xf64d2b88, 0x71654764),
+     TOBN(0x4b452692, 0x6ad8f168), TOBN(0x907beb21, 0x0cc6fc91),
+     TOBN(0xe876d523, 0xbf13c18b), TOBN(0x4d28e457, 0x4cf37ca1),
+     TOBN(0x4c0dc22d, 0x6d3d1172), TOBN(0x7935a8d2, 0x5a753137),
+     TOBN(0x03484e3d, 0xda44d652), TOBN(0x05a3d80f, 0xc50025a9),
+     TOBN(0x6d43c503, 0xff477c6d), TOBN(0x35f4c4cf, 0x1ccd416a),
+     TOBN(0x7070f471, 0xd5088349), TOBN(0x678460ca, 0x281d30c8),
+     TOBN(0x8923cd9a, 0xc6defb33), TOBN(0x44930f56, 0xe2557cab),
+     TOBN(0x33b020bb, 0xad156c4a), TOBN(0xfdab31e3, 0xbcaf4805),
+     TOBN(0xffe79bec, 0x864b5564), TOBN(0x0510e352, 0x10c60d52),
+     TOBN(0x66203aaf, 0x328a652e), TOBN(0x9d5403bf, 0x54fea042),
+     TOBN(0xb3fe6743, 0x6e5e5c7c), TOBN(0x6deef667, 0xecc66e02),
+     TOBN(0x199ee152, 0x44eacacb), TOBN(0x9f49fcd4, 0xd8803fd9),
+     TOBN(0xdd5fee9e, 0x2d3a6e28), TOBN(0x8eed56d4, 0x686d8ca3),
+     TOBN(0x36889a27, 0x78083491), TOBN(0xea1a6555, 0xbef20457),
+     TOBN(0xe7e6b609, 0xa501e2a3), TOBN(0x1ea0ae29, 0xfb23de2f),
+     TOBN(0x5f537d07, 0x632c9a6b), TOBN(0x61770d1f, 0x9a3db961),
+    },
+    {
+     TOBN(0x325c60db, 0x7497e8a5), TOBN(0x05d8eab8, 0x8c6949a9),
+     TOBN(0x3169e466, 0xc7bd5898), TOBN(0xadc06264, 0x192d8e3f),
+     TOBN(0x1ff468f4, 0xd55959fe), TOBN(0x97b33ee0, 0x202dba19),
+     TOBN(0xaa0c3fe2, 0x21cf84bb), TOBN(0x48cdc0af, 0x04a8d176),
+     TOBN(0x53d8c448, 0x9c0d4009), TOBN(0xd3714617, 0x2e24dbaa),
+     TOBN(0xdd92e730, 0x9b62e5f1), TOBN(0x97b344d7, 0x9922cc8a),
+     TOBN(0x416b009b, 0x0bfe3e8f), TOBN(0x56873834, 0xf3c82269),
+     TOBN(0xf82a980f, 0xe6623555), TOBN(0xb027ecaa, 0x5ce68e54),
+     TOBN(0x005a4b24, 0xfe87680d), TOBN(0xd92532dc, 0x4cf6ee32),
+     TOBN(0xfcd27c8c, 0x4bd3e01f), TOBN(0xda7d9949, 0xe1b59ffa),
+     TOBN(0xe3d5f31f, 0x2735d373), TOBN(0x7e139ca5, 0x288e71fb),
+     TOBN(0xe474bc80, 0x93979cb4), TOBN(0x7f4f6017, 0xf6fcc665),
+     TOBN(0xe6982c86, 0xfbd613c3), TOBN(0xf4af69d5, 0xc366f17a),
+     TOBN(0x8683eed6, 0xb85c2343), TOBN(0xf5bb244a, 0x24bc2116),
+     TOBN(0x997a74bc, 0xc9fc77d4), TOBN(0xe202eb91, 0x6f44b54b),
+     TOBN(0x77886412, 0xa6997e76), TOBN(0x6996c8fb, 0x02c8837e),
+     TOBN(0x0986df8a, 0x2c61382c), TOBN(0x90607b92, 0x667c8ee9),
+     TOBN(0x051fcbf7, 0x084eacde), TOBN(0x84e3dba4, 0x6e685877),
+     TOBN(0x35861a82, 0xe458da50), TOBN(0xd036823f, 0xcf392b51),
+     TOBN(0x43181481, 0x3dd86e74), TOBN(0x8dcfe17d, 0x3741a385),
+     TOBN(0x8e1a77cf, 0x40f56786), TOBN(0xc5bca7f6, 0x6d4b7774),
+     TOBN(0x86b588a0, 0xc81ec077), TOBN(0x88952a01, 0x9206354f),
+     TOBN(0x5444a989, 0x43a8519a), TOBN(0xe29dd68c, 0x2857b210),
+     TOBN(0x36658903, 0x9a144624), TOBN(0x8c4dedb0, 0xe423e899),
+     TOBN(0x482040c5, 0xea886e5c), TOBN(0x42fe5a56, 0x1cdd50f7),
+     TOBN(0xf034f132, 0x453b6e7f), TOBN(0xba3fa97d, 0xb3c69762),
+     TOBN(0x34262560, 0xcadb598e), TOBN(0x7ed74b51, 0x07afe0a8),
+     TOBN(0x2261d849, 0xebe0e8bb), TOBN(0x23747e55, 0x608cea1c),
+     TOBN(0xb2e9371c, 0x902c343d), TOBN(0xf57b2de8, 0xda4fdba1),
+     TOBN(0x43f9afa4, 0xb67703a1), TOBN(0xeafafb41, 0xf79fe203),
+     TOBN(0xfec99dc9, 0xf649a494), TOBN(0x14799ef9, 0xfe378232),
+     TOBN(0xba3f8114, 0x7184e31e), TOBN(0x0abbb815, 0xdc0e987f),
+     TOBN(0x1dc7221f, 0x6fb5d02c), TOBN(0xa0dfb113, 0x40f1607c),
+     TOBN(0xd217f238, 0xdbe8db86), TOBN(0xc02547eb, 0xe91f2859),
+     TOBN(0x41df6bcb, 0xa98d0875), TOBN(0xf51a8077, 0x78f6be54),
+     TOBN(0x3ebdf28d, 0xba66bef5), TOBN(0xc65c7b38, 0x9175ec20),
+     TOBN(0x2302981d, 0x475589d5), TOBN(0xbd479708, 0x199d8ad4),
+     TOBN(0xcb508be9, 0x8db46795), TOBN(0xba1e066e, 0x6b224eb4),
+     TOBN(0xae909633, 0x1326ab8a), TOBN(0xac989728, 0xf5bbe5e6),
+     TOBN(0x1de663f5, 0xb1729b41), TOBN(0x2018f58c, 0x00c3d293),
+     TOBN(0xe647c0e6, 0x71efe25b), TOBN(0xaa30f871, 0x6104ebd6),
+     TOBN(0x1003eebe, 0x3c166de6), TOBN(0x0c308538, 0x1cbc42d3),
+     TOBN(0x98b6c312, 0x56cba120), TOBN(0x9065ae66, 0x860d2916),
+     TOBN(0x2162062b, 0xa7dabb6b), TOBN(0x1422386b, 0x8e011036),
+     TOBN(0xd2589ad6, 0xe069de1c), TOBN(0x3fe8e67a, 0x7ee09300),
+     TOBN(0x5b881860, 0x2f8ae49b), TOBN(0x186a1482, 0x263a6a90),
+     TOBN(0xc6079b39, 0x9b35b33a), TOBN(0x6c38d789, 0x53d3411d),
+     TOBN(0x743c4462, 0x720b2f99), TOBN(0x4d903dd7, 0x29e14d08),
+     TOBN(0x625dfce0, 0x7b963913), TOBN(0x60a83daf, 0xcc65e41f),
+     TOBN(0x9e88c26c, 0x93e185a2), TOBN(0x6d950e92, 0x13707ac3),
+     TOBN(0xbd7df2dc, 0xa0a23dd0), TOBN(0x5fad27f2, 0xc2116cc8),
+     TOBN(0x0703b868, 0xda4430be), TOBN(0x5ebf0e2f, 0x83cc41df),
+     TOBN(0xebb91900, 0xf9e83fc8), TOBN(0xf9539591, 0x6d60bb8a),
+     TOBN(0x3bdd7a8b, 0xb604935a), TOBN(0x2cae8c67, 0x64e5eec0),
+     TOBN(0x30cf58bb, 0x60aaf21d), TOBN(0x5e0f6f5d, 0x359f63cc),
+     TOBN(0xda245055, 0x0547e03a), TOBN(0xa83fd8bb, 0xb9e143ce),
+     TOBN(0xa564435a, 0x1b0215aa), TOBN(0xecffccec, 0x354ba769),
+     TOBN(0xd846149a, 0xbdbbd594), TOBN(0x9137df36, 0x65dd1597),
+     TOBN(0xc4f39f37, 0xa9f3ac33), TOBN(0xf594bb74, 0x961d7e8d),
+     TOBN(0x41fa4b58, 0x835befbc), TOBN(0xa983eae9, 0xed79139e),
+     TOBN(0xd4085efc, 0xb4e31aa4), TOBN(0xc760aec0, 0x18b26adf),
+     TOBN(0x14c1f78e, 0x76a7400d), TOBN(0x87b8aced, 0x317fe128),
+     TOBN(0x4433582e, 0xcbd85bb4), TOBN(0x58f01426, 0x86adc041),
+     TOBN(0x3596dd50, 0x8f0d5781), TOBN(0x2e7f3b80, 0x1a31a82f),
+     TOBN(0xcac7ccc8, 0x2d1ede3e), TOBN(0xc9b9a8f3, 0xe89573db),
+     TOBN(0xbf744f69, 0x54b40df9), TOBN(0x88eb2281, 0xa85ecb47),
+     TOBN(0x6b115026, 0x426ec49d), TOBN(0xebda4660, 0xc8c41110),
+     TOBN(0x0a4a32ac, 0xdf392aae), TOBN(0x2a28f9b3, 0x37cb7080),
+     TOBN(0xfed99d1a, 0x6bd9414d), TOBN(0x15f59c41, 0x5715620e),
+     TOBN(0x93edd9fc, 0xac431919), TOBN(0xed1d43ae, 0xe1ccc47f),
+     TOBN(0xafed2acd, 0x556d1ab5), TOBN(0x817b967d, 0x02e039c0),
+     TOBN(0x335d15da, 0xe02a68bb), TOBN(0x4fa75ea0, 0x67df767c),
+     TOBN(0x384704b3, 0x44833943), TOBN(0x7809ed3c, 0x4084ef35),
+     TOBN(0x2abab3c4, 0x79c7ff41), TOBN(0x2b7ef5b2, 0xa833fb73),
+     TOBN(0x12b0335b, 0x4901a4ff), TOBN(0x3eea607b, 0x44d58617),
+     TOBN(0x7161b669, 0xd7f57746), TOBN(0xee17e43f, 0xb1e93442),
+     TOBN(0x95c9bd80, 0xd6d7878c), TOBN(0xe1ef20ee, 0x34ff7c75),
+     TOBN(0x3fab197a, 0xd2ccd599), TOBN(0x9e480593, 0x952ef4f9),
+     TOBN(0x69777fd2, 0x06ea3410), TOBN(0xb0280454, 0x74fa7dd5),
+     TOBN(0x641b6860, 0xc43bb5fc), TOBN(0x9f359d5b, 0xecd7b8a8),
+     TOBN(0x4431d4ed, 0x70be68cd), TOBN(0x712117f4, 0x08b55f8f),
+     TOBN(0x449e1319, 0x23d0b6ca), TOBN(0x658323cc, 0xfdee5357),
+     TOBN(0xa1ef8114, 0x62879a95), TOBN(0xc21257e5, 0x69963eeb),
+     TOBN(0x1016ab74, 0xc5bbee13), TOBN(0x99bb310a, 0x43d81a86),
+     TOBN(0xdef03c44, 0x1d33a15a), TOBN(0x3e78cf18, 0x49127148),
+     TOBN(0xe8d93368, 0x30b0cc00), TOBN(0xb05458fb, 0xbd7ccd85),
+     TOBN(0x8c2896dd, 0xdbaa76b0), TOBN(0x0d826600, 0x79e4cacb),
+     TOBN(0x50a45b23, 0xff730ed0), TOBN(0x4a0e079c, 0xeba9030e),
+     TOBN(0x3ead3fdc, 0xe3129aa0), TOBN(0xa93b39f3, 0x48aac890),
+     TOBN(0x0fd73860, 0xf362465e), TOBN(0x69177f2c, 0xf8df2764),
+     TOBN(0x4cd58c50, 0x824ebddf), TOBN(0x1478981f, 0x2fcef01d),
+     TOBN(0x511bd380, 0x980524b3), TOBN(0xc95252b1, 0x4d23e8e9),
+     TOBN(0x7ff12c37, 0x9ce08452), TOBN(0x3dd8dd09, 0xa3a87024),
+     TOBN(0x61ff0d39, 0xb849dcb6), TOBN(0x3f5eab86, 0xfefad6de),
+     TOBN(0xb6146886, 0x251523f9), TOBN(0x45ac1d52, 0x5be2135b),
+     TOBN(0x63507995, 0x41d2c5d4), TOBN(0x7f19f799, 0xb3064e72),
+     TOBN(0x7280ad9b, 0xa2efb9be), TOBN(0xbc8fbb60, 0xba9659de),
+     TOBN(0xa4861c12, 0x875a8062), TOBN(0x78c920c8, 0x2f2387ca),
+     TOBN(0xfe0a6ea7, 0x03b7da99), TOBN(0x936a44b8, 0x48e3afa3),
+     TOBN(0x618a0ecf, 0x89fd127b), TOBN(0xa35614cd, 0x06f24bc5),
+     TOBN(0x21a1002e, 0x8d49c05f), TOBN(0xceeacfd1, 0x2fd7989d),
+     TOBN(0x8c058f4b, 0x8f5f4ea5), TOBN(0xf31bd38e, 0x563e214b),
+     TOBN(0xbe47bd98, 0x245c7585), TOBN(0x70d1a05c, 0x6bc2a404),
+     TOBN(0x830d7f30, 0xc68257f7), TOBN(0x1abbbfbb, 0x8136c932),
+     TOBN(0x6fbc43b7, 0x6e03597b), TOBN(0xc18fddb6, 0x5fca14a2),
+     TOBN(0xd3c4ca78, 0xe8f506e2), TOBN(0x6b8711dd, 0xa47e27de),
+     TOBN(0xc308c189, 0x16382b5b), TOBN(0xe9d3145b, 0xcf96bd7a),
+     TOBN(0x5c290ec1, 0xafb26629), TOBN(0xb4eb8130, 0x209438ff),
+     TOBN(0x7335044a, 0xe1118539), TOBN(0xed6d43fc, 0x5192a007),
+     TOBN(0x1a8bf622, 0xa43a2bd4), TOBN(0xefec3fb6, 0xefa9f3a5),
+     TOBN(0x6d224bbc, 0x6d834bde), TOBN(0xaaebfcb8, 0xb0fbc744),
+     TOBN(0x383b2bfc, 0xc4ea1652), TOBN(0x9cd26d90, 0x751ae816),
+     TOBN(0xae614386, 0xf47f0f9a), TOBN(0x99873787, 0x73c6ecd0),
+     TOBN(0x0b56c1ad, 0xe5414fc7), TOBN(0x9b85e6b5, 0xfd40286c),
+     TOBN(0x7117aacd, 0xd64687dc), TOBN(0x85d148e7, 0xad8a8c4d),
+     TOBN(0xf62f8eb5, 0x7962655c), TOBN(0x8386b37e, 0x7f0c6a2c),
+     TOBN(0x6b4715a3, 0x90c47d7f), TOBN(0x1fc4ced1, 0x458a54e4),
+     TOBN(0x01853943, 0x0ed97b0a), TOBN(0x58a280be, 0x5b370e0e),
+     TOBN(0x8d488cb6, 0x344f3960), TOBN(0x9050c599, 0x0741604b),
+     TOBN(0x0878fb1b, 0x07954771), TOBN(0xd927ea8c, 0xdbb3c82b),
+     TOBN(0x2fe71d59, 0x384f01fa), TOBN(0x66d2b790, 0x238bb66b),
+     TOBN(0xceaec11f, 0xb8fd803b), TOBN(0xbb919914, 0x6dd09c0c),
+     TOBN(0xab5992e6, 0x2ccb2f67), TOBN(0x2421878f, 0xcca50326),
+     TOBN(0x363d933d, 0x9ee6dc73), TOBN(0xa374ab0b, 0x084b1fa3),
+     TOBN(0x2d832a29, 0x161f6475), TOBN(0x435b8d78, 0xfc8797eb),
+     TOBN(0x66bc156d, 0xd71b609c), TOBN(0xb3dca798, 0xfe0c2004),
+     TOBN(0x445d47bf, 0x02fd92d7), TOBN(0x1d1c9798, 0xc8b03083),
+     TOBN(0xca46d98d, 0x079a7c51), TOBN(0xb93f286c, 0x1afeb89a),
+     TOBN(0x1c174510, 0xeeb6665d), TOBN(0x65874b6a, 0x7479a932),
+     TOBN(0x28d16a85, 0x2335e3b8), TOBN(0x5e22bd3b, 0xc747eae6),
+     TOBN(0xa770e0a7, 0x04be16b4), TOBN(0x9f5f9ca9, 0x40b3ff57),
+     TOBN(0x3f39e529, 0x845ec39f), TOBN(0x5d5f4d60, 0xebe697ce),
+     TOBN(0xea2a262f, 0xbd90d4f4), TOBN(0xa1ce74ac, 0xfe1b6494),
+     TOBN(0x4e419004, 0xfa0fc472), TOBN(0xdef0e440, 0x4e691060),
+     TOBN(0x57195a3a, 0xa9f4baf3), TOBN(0xf14e108b, 0x5e758c53),
+     TOBN(0x10a34f9d, 0x920895e0), TOBN(0xc3f18af9, 0xfeb57a63),
+     TOBN(0x4b1c988c, 0xda1bef0d), TOBN(0x8b328cd9, 0x3df6915b),
+     TOBN(0x5ddc5ecc, 0xf45586d5), TOBN(0x426468b9, 0x040322fc),
+     TOBN(0xf7f44765, 0xbe981558), TOBN(0x25093991, 0x1855504a),
+     TOBN(0x72c51f2e, 0xf7d6df43), TOBN(0x858637fb, 0x849c99e5),
+     TOBN(0x68b84dfd, 0x0ee9f78b), TOBN(0xff42fc9b, 0xf2ee390e),
+     TOBN(0xaca71e10, 0x531e1dcf), TOBN(0x391620e2, 0x7feaedfa),
+     TOBN(0x7b2d6a02, 0xacf3e5da), TOBN(0x261823d2, 0xd20a16d3),
+     TOBN(0xf9afa5d6, 0xbb00cd30), TOBN(0xba151f4a, 0x1084580d),
+     TOBN(0xb5f4b3a9, 0x26a3fcc3), TOBN(0x5d84a823, 0x6729f4da),
+     TOBN(0x51540c41, 0xfc35f732), TOBN(0x81b0cb58, 0xa6ae5bf7),
+     TOBN(0x91c7ae07, 0xbd81bd27), TOBN(0x0868980e, 0x1d56ff5d),
+     TOBN(0xaef85a31, 0x65224df6), TOBN(0x112eba3b, 0x17a69e35),
+     TOBN(0x07c34677, 0xc3a9d932), TOBN(0x3b6b7cce, 0x8ac45e37),
+     TOBN(0x5e0e2b6e, 0x31b6248a), TOBN(0x14ee5b66, 0x453d9685),
+     TOBN(0x4c5e2be7, 0xbd4d807a), TOBN(0xc03f37f8, 0xc121fea8),
+     TOBN(0xcf34911e, 0x8df7b5e7), TOBN(0x00e7f18e, 0x5f754191),
+     TOBN(0x89a8c9e1, 0x2dcea4aa), TOBN(0xcc1cc31a, 0x50f6db55),
+     TOBN(0x4a6f542c, 0x9046955f), TOBN(0x85fed580, 0xda2485d4),
+     TOBN(0xa70f62d1, 0x9ac53748), TOBN(0xc2fbb850, 0x655870a7),
+     TOBN(0xaeb2d438, 0x8c859aef), TOBN(0xe3cc5ae5, 0xcc9ff51e),
+     TOBN(0xf8d8c55d, 0x0a3ebbfc), TOBN(0xdcd838d5, 0xed48f61a),
+     TOBN(0x032f91ea, 0xd4cba7ab), TOBN(0xeb0ed88d, 0x2f70235e),
+     TOBN(0xd4498170, 0x000ef325), TOBN(0xfd34e07f, 0x4b923c4a),
+     TOBN(0xf71c07a9, 0xb19b84cb), TOBN(0x000a669c, 0xed9690a3),
+     TOBN(0xf45eb0ef, 0xb5705e16), TOBN(0x8cfd6a62, 0x7d9ce007),
+     TOBN(0x76ba9a5f, 0xd9e52885), TOBN(0x13f72881, 0x8aa9ffd6),
+     TOBN(0x0a11e8dd, 0x85528e4a), TOBN(0x58f1993d, 0xcee8d663),
+     TOBN(0xb49d750b, 0xa1c81fd3), TOBN(0xaae29861, 0xe7de2e6b),
+     TOBN(0x9a40644e, 0x5dd7de70), TOBN(0x67fbae1c, 0x937a5546),
+     TOBN(0xb3e02907, 0x956c2fa8), TOBN(0xaf785374, 0x21b4aede),
+     TOBN(0xf42a1e96, 0x9c0a8bfe), TOBN(0x3f6690e6, 0x78957181),
+     TOBN(0x1b1c9575, 0xa6c5e0a7), TOBN(0x6def8124, 0xf9cfb9bd),
+     TOBN(0xde552cf9, 0x72faa1b0), TOBN(0xfac2f4ad, 0x9b5ebbbc),
+     TOBN(0x4b60a5a5, 0x8ef89ba1), TOBN(0xb6d9be57, 0x8012f3b1),
+     TOBN(0x3992a6f7, 0x9b2b083d), TOBN(0xe79ec527, 0xac640f77),
+     TOBN(0xf6cca775, 0x3f1e632e), TOBN(0x5dae8413, 0x8fb80790),
+     TOBN(0xf0d4146c, 0xb572407c), TOBN(0x829cfb38, 0x3f84cc39),
+     TOBN(0xd7c9fed4, 0xe31f007e), TOBN(0x93b2a5bc, 0x09e68ce9),
+     TOBN(0x073fb24a, 0xd01482b9), TOBN(0xfe494244, 0xb8d44e62),
+     TOBN(0xe59a1649, 0x3dc49858), TOBN(0x071776f7, 0xf005b31f),
+     TOBN(0xaa368f59, 0x285439af), TOBN(0xb0821574, 0xe27e783d),
+     TOBN(0xe16903f6, 0x098746ba), TOBN(0x436a95ae, 0x69dc00d0),
+     TOBN(0x9ae5a91a, 0x877bfcec), TOBN(0x5d8758d9, 0x1416ad3a),
+     TOBN(0xa420ce40, 0x25fd9a9a), TOBN(0x99610fdf, 0x39b0fbfa),
+     TOBN(0x782af222, 0x41cc1e9b), TOBN(0x346a4a92, 0x86ff4cf6),
+     TOBN(0x9dc4c6cd, 0xabc900c1), TOBN(0x7ed2476c, 0xe7fade51),
+     TOBN(0x68a72185, 0x4f1dd24b), TOBN(0xcefcf0fd, 0xb08260cb),
+     TOBN(0x0199a147, 0x8a6f61b6), TOBN(0x176b6079, 0xcb5769c5),
+     TOBN(0x6dbcceb0, 0x347d5a63), TOBN(0x26433ebc, 0x9c4dc505),
+     TOBN(0x52570318, 0x05d5e74d), TOBN(0x692f1d81, 0x057ca464),
+     TOBN(0xa09554a0, 0x477f424e), TOBN(0xbd3f9bbd, 0x176a695d),
+     TOBN(0x8c7f52f3, 0x5972db27), TOBN(0xacee8234, 0xf28b2aa4),
+     TOBN(0xb853465a, 0x3a4a0217), TOBN(0x74a2534f, 0x66f3a4d5),
+     TOBN(0xae1a7ff3, 0xeff0423b), TOBN(0xd2a01a09, 0xbb126028),
+     TOBN(0xff84c6f0, 0x4963e855), TOBN(0x63db264c, 0x6bc18d50),
+     TOBN(0x39792dca, 0xcc6a5e25), TOBN(0xf20cdf3e, 0xedb37a25),
+     TOBN(0x6a460f3d, 0x8730f2c4), TOBN(0xe9b786c4, 0x6a0ab6bb),
+     TOBN(0xa9720a6b, 0x084015c2), TOBN(0x28add2e2, 0x0dbe6f0f),
+     TOBN(0x90fb0ba7, 0x26be7de7), TOBN(0xfae8b5d4, 0xe40f15fd),
+     TOBN(0x007363a1, 0xceb9c856), TOBN(0x6d8bfe14, 0x586b770e),
+     TOBN(0x63e7d78e, 0xb7bcc0e0), TOBN(0x56c569f8, 0x3ed4ab62),
+     TOBN(0x76c6a5bb, 0x9e103abb), TOBN(0xeb24afeb, 0x638fc44d),
+     TOBN(0x15e00239, 0xf0be16f1), TOBN(0x7db92f67, 0x8778f084),
+     TOBN(0x5198680e, 0x63de2bef), TOBN(0x69031d0e, 0xe0510421),
+     TOBN(0x8058f8aa, 0xb94753c1), TOBN(0x412d4c97, 0x454bf609),
+     TOBN(0xb8dbfe8a, 0x95f9fd19), TOBN(0x6cd3221a, 0x68b43233),
+     TOBN(0x384a9f15, 0xa5adaaff), TOBN(0x60c70f90, 0x42b2ef95),
+     TOBN(0x085b2f3b, 0x2c7da919), TOBN(0x1e5d23d1, 0xbc8407e1),
+     TOBN(0x9ea95bc9, 0xadb45b3e), TOBN(0xb5a28fec, 0xed06ec67),
+     TOBN(0xd678df46, 0x62a3c143), TOBN(0x80f0bc9d, 0x6793284a),
+     TOBN(0xeb7865a9, 0x07d4afc7), TOBN(0x0fc5eafe, 0xc1301d87),
+     TOBN(0x50a8e7f5, 0x4823349b), TOBN(0x97800fa2, 0x2d019e96),
+     TOBN(0xfeff2579, 0x1bdd1d9c), TOBN(0x4d938c5d, 0x23886156),
+     TOBN(0x25e3a806, 0x6979b9f6), TOBN(0xeeef8fa0, 0x37bb6199),
+     TOBN(0x4d917977, 0xd7d308b8), TOBN(0x60ca7ff9, 0x4ae672cc),
+     TOBN(0xb24ec154, 0x2a68db6e), TOBN(0x7b0604ec, 0x9e9942f5),
+     TOBN(0xfdf4794f, 0xca4fad9e), TOBN(0x086020f8, 0x3df5de22),
+     TOBN(0x653da6ca, 0xd601533c), TOBN(0xf97c1865, 0x735709f3),
+     TOBN(0x2457ffd0, 0x7cbd6ab6), TOBN(0xce05a482, 0xd003a502),
+     TOBN(0x5c0c0cba, 0x33ee2740), TOBN(0x8146ca00, 0xf37174aa),
+     TOBN(0xec118827, 0x587262bb), TOBN(0x8468c029, 0x7e2a402c),
+     TOBN(0xe1c3a9e3, 0xbd6eb2aa), TOBN(0x77d09b4d, 0xb496bde8),
+     TOBN(0x454e767a, 0x854693bf), TOBN(0x6bbcc22e, 0xa4de85bc),
+     TOBN(0x66452f10, 0xb180f206), TOBN(0x110c5a05, 0x0f5b1744),
+     TOBN(0xb7be75d8, 0xa6b3f6e4), TOBN(0xf64bb3fd, 0x2c0d2e1d),
+     TOBN(0xad17a039, 0x935ae640), TOBN(0x7304ad63, 0x8f243748),
+     TOBN(0x04316bb4, 0xd278caa9), TOBN(0x19b89c62, 0x1e84f91d),
+     TOBN(0xdf4a47e9, 0x6e7a2511), TOBN(0xdef32df9, 0x998b6bc0),
+     TOBN(0xf1253ce0, 0xcee95a1f), TOBN(0xbacf5206, 0x0ae96e31),
+     TOBN(0x4ba2e24a, 0x0b343e63), TOBN(0xca64d07f, 0x79929dc6),
+     TOBN(0xf2823ac8, 0x9424ce75), TOBN(0x2d4add37, 0x0207ee9f),
+     TOBN(0x44d9ecd0, 0x387cde5f), TOBN(0xa5095ccb, 0xe180a21f),
+     TOBN(0x901cec8a, 0x7c0cedf8), TOBN(0xf291dc78, 0x3b8c759c),
+     TOBN(0x98b8efdc, 0x49401234), TOBN(0x8f2b16e3, 0x058e9d9e),
+     TOBN(0x16ce8007, 0x27dba00f), TOBN(0x5bb8fca9, 0x6d66d2f1),
+     TOBN(0x092eda98, 0x7b85a96b), TOBN(0xec53f4bc, 0x973eae20),
+     TOBN(0xe6e0df59, 0x1b93a60f), TOBN(0x65e06ecf, 0x2f6b0abf),
+     TOBN(0xb8c2ec3e, 0x569a9e1d), TOBN(0x27f9fe72, 0xaa8c1cc3),
+     TOBN(0x9cf3908f, 0xccd4d5e2), TOBN(0x5a40e0a9, 0x725c8528),
+     TOBN(0x27b15a1e, 0xd470b0b0), TOBN(0x50a09ec1, 0x33d81bff),
+     TOBN(0xba976a58, 0xda99fcf5), TOBN(0x3881ef1e, 0xc3536b7c),
+     TOBN(0xec65a069, 0xfbc931b1), TOBN(0xab7f57b4, 0xfc929a0e),
+     TOBN(0xc7c63491, 0xbc61f452), TOBN(0x5c1aa935, 0xc1750dbc),
+     TOBN(0x35b8789b, 0x9ff0465c), TOBN(0x8ff8589b, 0x727647b7),
+     TOBN(0x2b56fe50, 0xa95022b6), TOBN(0x24220569, 0x2adbdbbd),
+     TOBN(0x2370d94f, 0xd2b80ea8), TOBN(0xe5d47b73, 0x71d9e6f7),
+     TOBN(0x2fe84b72, 0x8d858032), TOBN(0x0411034b, 0x4ffd7cfe),
+     TOBN(0x0819a9f3, 0x7c2c84e0), TOBN(0xf8248dfd, 0x30228f9e),
+     TOBN(0x75c77f0a, 0x4fdf6c79), TOBN(0x195b4b59, 0x15579cc7),
+     TOBN(0x274c7c81, 0xf8d3705c), TOBN(0x45a2209f, 0x70ee9be1),
+     TOBN(0x4a4fce69, 0x0b1e818b), TOBN(0x74d05e5f, 0xbb9926b1),
+     TOBN(0xb64639ce, 0x3f2a796b), TOBN(0x2d168d5b, 0xae957d59),
+     TOBN(0x067c2270, 0x16171846), TOBN(0x7bb71d15, 0x1a03f8d1),
+     TOBN(0x2badd196, 0x495a09a1), TOBN(0x024db163, 0x51b990c4),
+     TOBN(0xc19edc61, 0xe79dcaac), TOBN(0xf17f54bd, 0x60df81e9),
+     TOBN(0x4560262e, 0x9ae347e4), TOBN(0x1d2c2542, 0x59eb711d),
+     TOBN(0x40372d2d, 0x4919affe), TOBN(0x2d4a2ea3, 0xa009bd16),
+     TOBN(0x48f1e7f8, 0xc1a62eb1), TOBN(0xce083725, 0x587a1124),
+     TOBN(0xe874261b, 0xe7ebadd3), TOBN(0x7ca5c156, 0x057b93e9),
+     TOBN(0xe8b381e5, 0xfe39e6ff), TOBN(0x2d16b32f, 0xd30815c3),
+    },
+    {
+     TOBN(0xdfd30b28, 0xad2e996a), TOBN(0x64d4eeec, 0x9df0fc37),
+     TOBN(0x8e049e3d, 0xddc39763), TOBN(0x9f55e0ce, 0x37ea2e8e),
+     TOBN(0xf3fcba4e, 0x4bf01984), TOBN(0x764d5c3d, 0x9d55bc4b),
+     TOBN(0x98cb92a1, 0xcfda895f), TOBN(0x27dfe795, 0x5c7bca69),
+     TOBN(0x86dfdecf, 0x23a86e2c), TOBN(0x02ac466b, 0x3387f712),
+     TOBN(0xc30a1ac2, 0xd63509d1), TOBN(0xd3913422, 0x63aebbd1),
+     TOBN(0xdc7b789f, 0x068ae34f), TOBN(0x499f2d01, 0x487dcd10),
+     TOBN(0x68e2a3bc, 0xa3e8a4b4), TOBN(0xdf87ba71, 0x14d2a26c),
+     TOBN(0x9e3ab999, 0x34479e7b), TOBN(0x9d5f2dc3, 0xb026e780),
+     TOBN(0x131374fd, 0x4f1bd429), TOBN(0x92e2e738, 0x9be1379a),
+     TOBN(0x6cc32f80, 0xd13bc111), TOBN(0x6fbfc350, 0x86f81c92),
+     TOBN(0x12ca1b30, 0x9263913a), TOBN(0x6f7da1ff, 0xe06ab75c),
+     TOBN(0x4780f12a, 0x19301b16), TOBN(0x233bc231, 0xbc368a20),
+     TOBN(0xd9650892, 0xcbadb344), TOBN(0x38a0d964, 0xad9425a1),
+     TOBN(0x277abcf2, 0x4a8d4d7e), TOBN(0x4ccd16b1, 0xb806ce9e),
+     TOBN(0x82ff40f0, 0x7570d473), TOBN(0x57491414, 0xdf130780),
+     TOBN(0x9f1f39f2, 0xa96ef42b), TOBN(0x1fd967ed, 0x107f1321),
+     TOBN(0x93832497, 0x29d4767e), TOBN(0x7761a38b, 0x3fa9e796),
+     TOBN(0x3e408c59, 0x66318df2), TOBN(0x9283ca44, 0x41745f03),
+     TOBN(0xfedf8fa3, 0x2cce1a86), TOBN(0x8363860d, 0xb44600b4),
+     TOBN(0x3dbfde55, 0x45f45a89), TOBN(0x8800c860, 0x26ce399c),
+     TOBN(0xfb25e8da, 0xc25e9479), TOBN(0x6ff0d6cb, 0xf7d367a2),
+     TOBN(0x70b0ba36, 0xa93f9890), TOBN(0xc07ca403, 0x49bd5a80),
+     TOBN(0x5f4feda6, 0xed54d1aa), TOBN(0xfa1e2efd, 0x671ad0b4),
+     TOBN(0xda465467, 0x8c56e7aa), TOBN(0x39d04cdc, 0x25e45bc0),
+     TOBN(0x26661bd6, 0xaf21c637), TOBN(0xf757ff5c, 0xb55ddfa5),
+     TOBN(0x07318fd1, 0x4394eb20), TOBN(0xe010b19d, 0x2bcf3ad1),
+     TOBN(0x71e2031c, 0x8e5c7e7f), TOBN(0xee35f346, 0xedbfda69),
+     TOBN(0x8d8d4120, 0x5d8f6fab), TOBN(0x5e420839, 0xabed09c8),
+     TOBN(0x5120e079, 0x4aacbb1e), TOBN(0xf7e840f8, 0xec1bc996),
+     TOBN(0xd921d63b, 0x9707c3bb), TOBN(0x3ab4b8b8, 0xae891a71),
+     TOBN(0xbe465756, 0x73bb2278), TOBN(0x776ce040, 0x553ee91c),
+     TOBN(0x86f07c2e, 0x88222e0a), TOBN(0x3f3688f1, 0xdf97816e),
+     TOBN(0x20d2d944, 0xa6df9d3a), TOBN(0xff399057, 0xb2cb925a),
+     TOBN(0x010c747e, 0x27f1de19), TOBN(0xbe940669, 0x7fc1fbc3),
+     TOBN(0x3ab94600, 0x877ef709), TOBN(0x9af40a6b, 0x8f4c8a8e),
+     TOBN(0xf7c0f202, 0x713f023a), TOBN(0xbe0bf3db, 0xd0a8dc63),
+     TOBN(0x09789926, 0x64a41337), TOBN(0xbb4a8964, 0x2c2823cf),
+     TOBN(0x9279cb27, 0xb90e45c6), TOBN(0x80283fd3, 0x382564ac),
+     TOBN(0x3a746b01, 0xfa5f0bcd), TOBN(0x28667a8e, 0x0afaf10a),
+     TOBN(0xeccfd0ee, 0x015d5295), TOBN(0xbd6678a5, 0x73ce8e21),
+     TOBN(0x132a87f2, 0x336ecb65), TOBN(0x09de4efc, 0xbe7dc659),
+     TOBN(0xbedadd10, 0x6f69b3bd), TOBN(0x357c61e2, 0xe8303650),
+     TOBN(0x6b3c613a, 0xa45777e6), TOBN(0x51dd30ad, 0x08d41179),
+     TOBN(0xa7b53f3f, 0xf98feac3), TOBN(0xe8e00328, 0xf9497319),
+     TOBN(0x1f3b080d, 0x0ca20b40), TOBN(0x06dc5456, 0xe19b088e),
+     TOBN(0x1f3f18d7, 0x0dce02cb), TOBN(0x2d215544, 0x6fd1ade9),
+     TOBN(0x4c6de38c, 0x055550d0), TOBN(0x5d535e6f, 0xf33f4973),
+     TOBN(0xdcfd4163, 0x744c033d), TOBN(0x0706a490, 0x76fc420a),
+     TOBN(0xdc9db55c, 0x394f5b6d), TOBN(0x1a050a62, 0xe040a093),
+     TOBN(0x60367ebb, 0xd0ab26f3), TOBN(0xb9c07239, 0xc3d91820),
+     TOBN(0x14a8bed5, 0xa3e32db2), TOBN(0x23b19cd3, 0xd49f37dd),
+     TOBN(0xd048c64f, 0xea6c85ce), TOBN(0x8aefff19, 0xd5716cfe),
+     TOBN(0x199fddb1, 0xab85bdcf), TOBN(0x3f592e7e, 0xbaea49d0),
+     TOBN(0x8c8decb6, 0xf1d58ff6), TOBN(0x02598a99, 0x7fe8f425),
+     TOBN(0xe4c73ae2, 0x64a93fd6), TOBN(0x208a0f61, 0x878db06b),
+     TOBN(0xff0272fe, 0x27c4a266), TOBN(0xccfc96ae, 0x69e7c6a2),
+     TOBN(0xbd7e715f, 0x8e78d74a), TOBN(0xd04ae9a4, 0x32ed35bc),
+     TOBN(0x302af413, 0x05614c5f), TOBN(0x98176637, 0x33943b5a),
+     TOBN(0xa4d1c9b2, 0x8a58cfd8), TOBN(0xf0ebd5f4, 0xac2f3f79),
+     TOBN(0xebadb010, 0xfdc7945f), TOBN(0x62c9cff0, 0x03750a4b),
+     TOBN(0x75192383, 0xa0c85b9d), TOBN(0x2aba7b53, 0x16eb0bf9),
+     TOBN(0x93d4adaa, 0xa4c06f9a), TOBN(0x573a86a2, 0x5f8e482c),
+     TOBN(0x109c6fdc, 0xe791a79f), TOBN(0xd9ed02ce, 0xb37eeec2),
+     TOBN(0x7b1fb4b4, 0x7dd63c8b), TOBN(0xae6e2767, 0x22493b49),
+     TOBN(0x5ef6beb7, 0x6a411bc9), TOBN(0x4d88918d, 0xe1bf4242),
+     TOBN(0x7ba26f8c, 0x02a97fbc), TOBN(0xf45b2a50, 0x7f41c518),
+     TOBN(0x6c35fb69, 0x83249e23), TOBN(0xc4a95134, 0x7a5778cc),
+     TOBN(0x6173f860, 0x08287cf7), TOBN(0xdcfc71d9, 0xfac3a444),
+     TOBN(0x894f3b33, 0x079ce3c9), TOBN(0x842bf7da, 0x916b661c),
+     TOBN(0x94299d6f, 0xa758a243), TOBN(0x0e23f984, 0xb242389a),
+     TOBN(0x653050f0, 0xc03a7aa2), TOBN(0x2ec3041b, 0x9559ad9c),
+     TOBN(0xa61dd499, 0x97cf6e9b), TOBN(0xfd090f38, 0x448fa6c6),
+     TOBN(0x4f1b75ac, 0x39b126bf), TOBN(0xb48d0372, 0x1ef1a280),
+     TOBN(0xe40c310d, 0xf2b2941f), TOBN(0x5b9a7369, 0x8d9d5ace),
+     TOBN(0xbe0415c1, 0x7ad9ad95), TOBN(0xffd864b6, 0xa8510622),
+     TOBN(0x2aceddcd, 0x898f28e8), TOBN(0xa0cfc30a, 0xba89a51f),
+     TOBN(0xd87db983, 0xe105e448), TOBN(0x16ba795e, 0x5e5ea6fb),
+     TOBN(0x5f48e45a, 0x352ad094), TOBN(0x1971a10f, 0x55fd75e7),
+     TOBN(0xfb0f1767, 0xfd1c8d68), TOBN(0x904229d9, 0x86df0665),
+     TOBN(0xc2c88671, 0xe87ab22b), TOBN(0xcbe384a1, 0x33611a50),
+     TOBN(0x38eec58e, 0xad2eb708), TOBN(0xaa19b174, 0x86d7bdee),
+     TOBN(0xa9f416d7, 0x51832b61), TOBN(0x10b751ff, 0x355e7b6d),
+     TOBN(0x6dd52063, 0x4ff07a5a), TOBN(0x6a6090c1, 0x4e3505c1),
+     TOBN(0x53addd1c, 0xd4c80f29), TOBN(0xe76d668b, 0x0d3d793e),
+     TOBN(0xbdcdc4c9, 0x191eedd9), TOBN(0x80675320, 0x0f8e4877),
+     TOBN(0xc41426dd, 0x32f5661b), TOBN(0x9fcbe1ac, 0x06199185),
+     TOBN(0x62fa2198, 0x404a1207), TOBN(0xc742fdc2, 0x33c8190e),
+     TOBN(0x16ec1b96, 0x778ee765), TOBN(0xda656f58, 0xe29d992d),
+     TOBN(0x5393775b, 0xb4369e7f), TOBN(0x512f5c7b, 0x0674fc45),
+     TOBN(0x55b8bd38, 0x60efa8e0), TOBN(0x1ab6d2df, 0x155b57ab),
+     TOBN(0xed0aa777, 0xe26ad439), TOBN(0x5b16333e, 0xd9b04376),
+     TOBN(0x321119d3, 0x8fc7ea72), TOBN(0x390b4ef5, 0x7211ef45),
+     TOBN(0x289f3945, 0x1feda1a2), TOBN(0xcee9cb33, 0x605c8596),
+     TOBN(0x91109e96, 0x971b6897), TOBN(0xdf70c173, 0x19701ea7),
+     TOBN(0xa07d0ecd, 0xa92c6b2b), TOBN(0xf8eb97fe, 0xa9eab766),
+     TOBN(0xbb2cf63b, 0x0e3cf9e8), TOBN(0xffa6c503, 0xdda02b26),
+     TOBN(0x42c1ec1a, 0x9cb18efd), TOBN(0x13cdda9c, 0xc45d5b22),
+     TOBN(0x6b3baf33, 0xc820cdf5), TOBN(0xa337bc5e, 0x610c8bfc),
+     TOBN(0x88186818, 0x06a9ca6b), TOBN(0x382a34ee, 0xa0c455af),
+     TOBN(0x725006c9, 0xe8fc59df), TOBN(0x0d46b697, 0xf929c670),
+     TOBN(0x7bd6eceb, 0x893a9f6e), TOBN(0xf25aa637, 0x1cd04e5a),
+     TOBN(0xf1563d79, 0xf6612d4a), TOBN(0x8c9372cf, 0x5efc49d8),
+     TOBN(0x09cc0513, 0x96c5bdc5), TOBN(0x70f19d46, 0xd3cc34d8),
+     TOBN(0xe62f6891, 0xfdfbe16c), TOBN(0x8b7db2fd, 0xdc60110b),
+     TOBN(0x3557bff8, 0xf7868674), TOBN(0x2b414c8e, 0x95a638d6),
+     TOBN(0x3b6610ac, 0x6d19ac65), TOBN(0x20864204, 0xd641b0ea),
+     TOBN(0xee372a46, 0x892161fb), TOBN(0xc7c5bd87, 0x4125868a),
+     TOBN(0x5edc6afc, 0xa61ee957), TOBN(0xa4374ba1, 0xd37faed4),
+     TOBN(0xf0684f46, 0x1e52b0ab), TOBN(0x0812cbca, 0x2a007b66),
+     TOBN(0xf3442afd, 0xe68c4893), TOBN(0xb02ac6df, 0x2d9dd9a2),
+     TOBN(0xa4fe98dc, 0x068c08bb), TOBN(0xfcd5dc05, 0x7795e2e9),
+     TOBN(0x28983aee, 0xa0f55cd8), TOBN(0xb390daf4, 0xe96cf557),
+     TOBN(0xbfd2f5ab, 0x3f119098), TOBN(0xed114587, 0x6386c0ad),
+     TOBN(0x578995b9, 0x69539392), TOBN(0xc8a77948, 0x36b880ab),
+     TOBN(0x7e03cfb6, 0xe4c8b560), TOBN(0x018cd4db, 0x06cdcbe0),
+     TOBN(0xbd7f2e3a, 0xb006f8d5), TOBN(0xf25d1718, 0xd6d9f30e),
+     TOBN(0x9ef6e2ee, 0x1b22ae3d), TOBN(0x89a2695d, 0x416154ab),
+     TOBN(0x1975e0c8, 0xda261e39), TOBN(0x8fe66aed, 0xda2c2031),
+     TOBN(0x6f93f839, 0x97e1e00c), TOBN(0xcba9a773, 0xa742b328),
+     TOBN(0x9529fec1, 0x3417df8b), TOBN(0x37a27cd9, 0x54e314b1),
+     TOBN(0x93feb0f8, 0x65f94a63), TOBN(0x65978b84, 0xb5e029c1),
+     TOBN(0x576fd830, 0x81b705f1), TOBN(0x37d07a37, 0x688e8c52),
+     TOBN(0x3b91d360, 0x332838dc), TOBN(0xcca9cbf8, 0xb0b14a94),
+     TOBN(0x86f18c44, 0x8b63b669), TOBN(0x53c6eba6, 0x6972d2d1),
+     TOBN(0x2a522d8c, 0x8cff59bc), TOBN(0xbc181d15, 0x6ed25ce5),
+     TOBN(0x913f173d, 0x5feb0eca), TOBN(0x34273f8d, 0xa207bd71),
+     TOBN(0x41b7572e, 0xfa1715ae), TOBN(0x8a8ffea2, 0x7f16f4ae),
+     TOBN(0x2b852908, 0xf95bdf78), TOBN(0xa75adbb3, 0xa26328b9),
+     TOBN(0x992ac179, 0xdae21d25), TOBN(0x8c99927a, 0x78e14467),
+     TOBN(0x23fb2fee, 0x0c16e0c2), TOBN(0x58e797bb, 0xdbcb5f4e),
+     TOBN(0x33d6956e, 0xa07bd7de), TOBN(0xc921fdb3, 0x7172d36a),
+     TOBN(0x035f1744, 0x158267b5), TOBN(0xc7495f33, 0xa291374d),
+     TOBN(0xe07db2f5, 0x4a41a6a0), TOBN(0xfdb2987e, 0xd1238792),
+     TOBN(0x616df624, 0x49741ce6), TOBN(0x90ecd21b, 0x8a693880),
+     TOBN(0x447c729d, 0x341fe21b), TOBN(0x06ad3c90, 0xc012a8ab),
+     TOBN(0x13dc4fa9, 0xddfd6b5a), TOBN(0x238a1add, 0x64cfc0f3),
+     TOBN(0x874a3c2f, 0xc34a2b1e), TOBN(0x427b6e3c, 0x0deb1dd4),
+     TOBN(0x78a1ad1d, 0x876f2621), TOBN(0x34f9207c, 0x252f6837),
+     TOBN(0x1c812fbb, 0x047d667b), TOBN(0xc3512ea3, 0x3ee03ba7),
+     TOBN(0x762de5f0, 0x527a1b55), TOBN(0x7873d692, 0xae3221af),
+     TOBN(0xa8ac73c7, 0xb112d35f), TOBN(0x1d118c30, 0x815414f6),
+     TOBN(0xbc686118, 0x865ab6f6), TOBN(0x01e75348, 0xecf8c02d),
+     TOBN(0x9b131840, 0xe6220bf9), TOBN(0x3d72dac4, 0xa67512b5),
+     TOBN(0xd9e49ecc, 0xaf95e16e), TOBN(0x1297c560, 0x1e2aa105),
+     TOBN(0x925301ac, 0xa1a5e8c2), TOBN(0x3b0ea863, 0x990ab503),
+     TOBN(0x7860b660, 0x15f258c4), TOBN(0xa4497040, 0x397253e4),
+     TOBN(0x88562ed0, 0x14a4774d), TOBN(0x325d8b0d, 0x7adbd093),
+     TOBN(0xd4df8df2, 0x41e24010), TOBN(0xe7cb8663, 0x580031be),
+     TOBN(0xd653593a, 0xd7fc7e5f), TOBN(0x429a8863, 0xe9f1fe9d),
+     TOBN(0x18a0a709, 0x63e71ced), TOBN(0x39d9316d, 0xa498140e),
+     TOBN(0x44466cff, 0x40f40117), TOBN(0x58d27cd6, 0x64602832),
+     TOBN(0xf4a4c22b, 0x86018a70), TOBN(0x7a4d4102, 0x6d703f04),
+     TOBN(0x4f5a0037, 0xb424b0fb), TOBN(0xfb591cfd, 0x322b1876),
+     TOBN(0xb45798b0, 0x632e0f56), TOBN(0x83ef9feb, 0xfdcbcf20),
+     TOBN(0x0a23b09c, 0x115749ac), TOBN(0x3b950273, 0x946248f8),
+     TOBN(0x5ed75e68, 0x1e13eaee), TOBN(0xbebd4744, 0x09dafdb9),
+     TOBN(0x8b46621f, 0x69941fc4), TOBN(0x1fd3c13f, 0x91129bc0),
+     TOBN(0x371448d0, 0xb7b9da22), TOBN(0xd452ccea, 0xd87a4742),
+     TOBN(0xe2f04778, 0xf91c38b9), TOBN(0xfb443a5d, 0xbd516bfe),
+     TOBN(0xd7bd4056, 0x044d666b), TOBN(0xb4546ffd, 0x2a9b17c4),
+     TOBN(0xf66765ae, 0x818fe55a), TOBN(0xc375126c, 0x1b5dc7a0),
+     TOBN(0xe9a7ed20, 0xc1a81c63), TOBN(0xaef2e23d, 0xf8cf06b9),
+     TOBN(0xf4536865, 0x3e67b95b), TOBN(0x25cbb5a6, 0x24309537),
+     TOBN(0x8a230e68, 0x3256c020), TOBN(0x4a33e462, 0x215dca7b),
+     TOBN(0xefef4936, 0x2935b6d1), TOBN(0xb383df4e, 0x852c39f4),
+     TOBN(0x197ddd77, 0x57c21e90), TOBN(0x236f98dd, 0x2164127f),
+     TOBN(0x464b86ec, 0xbbd61847), TOBN(0x107a387c, 0xfb89d515),
+     TOBN(0xe01e50b7, 0x400d66d5), TOBN(0x4377af2b, 0x5f864426),
+     TOBN(0xde21c49a, 0xf8fe847a), TOBN(0xc133e58f, 0x887c0674),
+     TOBN(0xda5b4c3b, 0xd2fda17c), TOBN(0x24157f13, 0xfed8fe68),
+     TOBN(0x1c4483f3, 0x8b6bb6bf), TOBN(0x940fab9e, 0xcf1bed73),
+     TOBN(0xce3fca79, 0x3c15c7e5), TOBN(0xb83fce10, 0x066de109),
+     TOBN(0xbd42ed01, 0x0cd5a04a), TOBN(0xba5446b8, 0x407fcb03),
+     TOBN(0x4a8cb929, 0xe5d35bda), TOBN(0x6338fd7e, 0xbff8631f),
+     TOBN(0xc85d4ee4, 0x4656a8cd), TOBN(0x83b1f39a, 0x92c99820),
+     TOBN(0x153fa4d4, 0x7e90c823), TOBN(0xc15809ba, 0x15326196),
+     TOBN(0x320b8fe9, 0x6eb4b379), TOBN(0x27cc07c0, 0x58704d9e),
+     TOBN(0x301840b2, 0xf13d6ee7), TOBN(0xf1f6530f, 0xc6d8c930),
+     TOBN(0x3e9254ea, 0x96a22034), TOBN(0xf8c0ee68, 0xaf5b8a2e),
+     TOBN(0x88e9e44e, 0xb8207fde), TOBN(0xdea11cbd, 0x29bc1310),
+     TOBN(0xa20c2f17, 0x9c9d7df7), TOBN(0x2a954927, 0xbceac495),
+     TOBN(0x3f405f5c, 0x2a58d6ba), TOBN(0x64df5364, 0x2ac9aeaa),
+     TOBN(0xb618f6db, 0xe8aa74a5), TOBN(0x22dadc7f, 0x74ef61e3),
+     TOBN(0x306ee832, 0x9cfdc4cd), TOBN(0xaff53321, 0x40698a5b),
+     TOBN(0x98893443, 0x89567341), TOBN(0xdfefbdd4, 0xac7c68ec),
+     TOBN(0xd3da7094, 0x3261a582), TOBN(0xd23e3fa5, 0x62ce96e7),
+     TOBN(0x62c060c0, 0xd773337e), TOBN(0x5cb2beca, 0xa041f910),
+     TOBN(0xe21ab479, 0x7e8215d8), TOBN(0x84384686, 0x923b4b27),
+     TOBN(0xa93c08fe, 0x6a3d21ef), TOBN(0x7bd96248, 0x2fa6de1c),
+     TOBN(0xb858ecd7, 0xca6de3e0), TOBN(0x466a48c8, 0x47c9fced),
+     TOBN(0x23ca9b75, 0xc708239e), TOBN(0x860d553d, 0xb5bbe833),
+     TOBN(0x45804f1a, 0x9e76e71d), TOBN(0x9fdb8b8d, 0x51e59be2),
+     TOBN(0xa76db4b7, 0x3bbc2a19), TOBN(0xaebb47ee, 0x96f82cd0),
+     TOBN(0x7a9b95b5, 0x97b1dd04), TOBN(0xcc149a8d, 0x5f0b1d7c),
+     TOBN(0xbba40e4d, 0x47a50eec), TOBN(0x4e635d70, 0x56b72539),
+     TOBN(0x31c40e90, 0xb18659c4), TOBN(0x08026224, 0x6f694b35),
+     TOBN(0x8ed3d7b8, 0xe0cd6e15), TOBN(0x157e2a9d, 0x9293cb36),
+     TOBN(0x7b457bb1, 0x96e54583), TOBN(0x75647498, 0x2609c44d),
+     TOBN(0x54667671, 0x970a8cf2), TOBN(0x3b7da9c8, 0x3af72572),
+     TOBN(0x8fbba977, 0x4d63473a), TOBN(0x7af5af43, 0x23001221),
+     TOBN(0x99093197, 0xea29d97e), TOBN(0x4409f6a9, 0xfa564089),
+     TOBN(0x64fd1cda, 0x2f70e06f), TOBN(0x8b7c83a6, 0x2e9d55ea),
+     TOBN(0x0dffbe4b, 0x6385fcef), TOBN(0x4513f584, 0x7a6fe72d),
+     TOBN(0x6a64f166, 0xba1de4ae), TOBN(0x1f8550a9, 0x89069fbd),
+     TOBN(0x72b411fd, 0xda7ef7fc), TOBN(0xa185d2c3, 0x829ea712),
+     TOBN(0x82f5ffb8, 0xccc1868d), TOBN(0xb484473a, 0xff9fafa9),
+     TOBN(0xe1857f3c, 0x089132ed), TOBN(0xdad885a9, 0x08ef378f),
+     TOBN(0xbdbdfc0e, 0x7af9e2aa), TOBN(0x366c07bb, 0x95486202),
+     TOBN(0x37040d45, 0xfc9d979f), TOBN(0xf279ed10, 0xa0f80630),
+     TOBN(0x27855261, 0x8f31b39c), TOBN(0xea0c3b1c, 0xf26f91cb),
+     TOBN(0x90b4e8c2, 0xb38c050f), TOBN(0x7acb26b1, 0x1623ab47),
+     TOBN(0xb6cc3cd9, 0xa4a064d2), TOBN(0xa410880c, 0x97245482),
+     TOBN(0xfb470e11, 0x3a6e6a1f), TOBN(0xf19080b1, 0x93f347e4),
+     TOBN(0x23253dcf, 0xb75a53d9), TOBN(0x55047049, 0x9c95d567),
+     TOBN(0x8c75631e, 0x7b20202a), TOBN(0x58fccf32, 0x834b8380),
+     TOBN(0xaf6bdbd8, 0x243dddde), TOBN(0xa3ca3e2c, 0xcf38f705),
+     TOBN(0xa2357b4b, 0xca38c9a2), TOBN(0x8bf0d270, 0x6eba095b),
+     TOBN(0xe4a43b7c, 0x9d998070), TOBN(0xdf412faa, 0x8469214c),
+     TOBN(0xd2578cc4, 0x000f1802), TOBN(0x2feb563f, 0xf8515863),
+     TOBN(0xe66ce02a, 0x5022112c), TOBN(0x8415df81, 0x1461b1c6),
+     TOBN(0xc2546e6a, 0xad247c38), TOBN(0x4b9788e6, 0x9a9c74d6),
+     TOBN(0x9d0cb2e0, 0xa22be3e8), TOBN(0x15db086c, 0x295f76ad),
+     TOBN(0x9758f99b, 0xa2337670), TOBN(0x61ae09bb, 0x9ab57f54),
+     TOBN(0x7af4d4aa, 0x93926a37), TOBN(0xa895f386, 0xf25cadb4),
+     TOBN(0x90e13821, 0xc6f4f514), TOBN(0x46738d95, 0x4eef97ab),
+     TOBN(0x66f559f3, 0xf0b479a2), TOBN(0x9ea62dcd, 0x3262fb2b),
+     TOBN(0x91a39a5e, 0x6a857025), TOBN(0x11bdd19a, 0xbb3e6e11),
+     TOBN(0xfa411fd6, 0x9353cc19), TOBN(0x275d094c, 0x94cd2276),
+     TOBN(0xe2069b22, 0x25243d1b), TOBN(0x9f02f1e2, 0x630c8cbc),
+     TOBN(0x85c942fd, 0x379b6b63), TOBN(0x293dcf87, 0xbdcc9307),
+     TOBN(0x56856d8e, 0xdc212ca8), TOBN(0x1927e931, 0x23a0c56d),
+     TOBN(0xacfed288, 0x9c2f8b66), TOBN(0x20f6b94e, 0x386ad1e3),
+     TOBN(0x0e622304, 0xdcbeff77), TOBN(0x67e895fa, 0x5978f2f6),
+     TOBN(0x12a63bde, 0x20029bfe), TOBN(0x0ed75b6c, 0x8d968b8c),
+     TOBN(0x611739ee, 0x57cec33e), TOBN(0xeffaae72, 0x42b9fc44),
+     TOBN(0xa7de79ce, 0x971a90a9), TOBN(0x529bd8a2, 0x4fead0d5),
+     TOBN(0x78434c30, 0x9a1a43ef), TOBN(0x904d1db2, 0x4f3c848c),
+     TOBN(0xedb3c11e, 0x6d4234ad), TOBN(0x4e0c9ec4, 0x5975e9a0),
+     TOBN(0xc51236db, 0xff16ec74), TOBN(0xde652cff, 0x36d09231),
+     TOBN(0x0d7c18b7, 0xe60a0bc5), TOBN(0xaf041383, 0x9d7df012),
+     TOBN(0x9c0ff3f4, 0x47d4cfd0), TOBN(0x64c7e6c2, 0x3d97bac7),
+     TOBN(0x239d2500, 0x8cb44d50), TOBN(0x47189764, 0xbba96026),
+     TOBN(0x22449328, 0x31ddca37), TOBN(0xa53a1886, 0xfb7c29ef),
+     TOBN(0x2515b665, 0x97a3789b), TOBN(0x825c5031, 0x540ea258),
+     TOBN(0x76680656, 0x09a5b24b), TOBN(0x833c240f, 0x60fb8bcc),
+     TOBN(0x758e0b10, 0x01e55cc7), TOBN(0x8b799c48, 0x8d260572),
+     TOBN(0x0981a802, 0x6c5dd0cd), TOBN(0x6f6da55d, 0x1b9c6cc9),
+     TOBN(0x3457b685, 0x8c4d503a), TOBN(0xc537730f, 0x009a7a94),
+     TOBN(0x334d46b4, 0xd01dfdff), TOBN(0x3e5dc6a8, 0x15e20bc7),
+     TOBN(0x1833b0bf, 0x6ce8b8ab), TOBN(0xe21b99ae, 0xc57a4292),
+     TOBN(0x743cb595, 0x1713ba15), TOBN(0x550e41bc, 0xe0bb44a7),
+     TOBN(0xf356917a, 0x2f8ebcf5), TOBN(0x6f2c400b, 0x953f056c),
+     TOBN(0x09d9ac41, 0xdd84bb48), TOBN(0x50dc7a8e, 0xa61e98e3),
+     TOBN(0x1179a9d3, 0x3d3a6776), TOBN(0xdda312e7, 0x16de8b3e),
+     TOBN(0x62a8b7c3, 0xce6e2bea), TOBN(0x2b00036c, 0x645e4ca0),
+    },
+    {
+     TOBN(0x9ad2cbd7, 0xab6cf0b4), TOBN(0x7a1e67f4, 0xf13d1ddf),
+     TOBN(0xa58f0c73, 0x746003ba), TOBN(0x8263e888, 0xa64a8fcc),
+     TOBN(0x535cbe37, 0xbe2452f7), TOBN(0x93125766, 0x6ae81a76),
+     TOBN(0x7d2ed0ab, 0x3a553701), TOBN(0x93d7e7df, 0xb0717d78),
+     TOBN(0x61bc013c, 0xf9cf03ba), TOBN(0x36223b88, 0xfeee3a78),
+     TOBN(0x86efc923, 0x3d7e4c95), TOBN(0xaf3801be, 0x965625e4),
+     TOBN(0xa7bba141, 0x3f32fd9d), TOBN(0x70724dec, 0x4e564acb),
+     TOBN(0x52587f32, 0xb7edcac1), TOBN(0x0b1cd94c, 0xb3969985),
+     TOBN(0x9f08407a, 0x661fbdab), TOBN(0xc4d7c536, 0x58e52151),
+     TOBN(0xa3765bce, 0x63dfe954), TOBN(0xc6829bfb, 0xac2dddd1),
+     TOBN(0x97393f65, 0xdc6e4487), TOBN(0x9ba29422, 0xbf04c930),
+     TOBN(0x493c6914, 0x18838c0a), TOBN(0x41b137ff, 0x4b2f35cd),
+     TOBN(0xc9e4aa24, 0x4e1c5e60), TOBN(0x54bb5289, 0x13253d51),
+     TOBN(0xf4a86ab3, 0x9bbabe53), TOBN(0xd561feae, 0x6ac0aa64),
+     TOBN(0x27a896ab, 0x1911bad7), TOBN(0x9cb22b98, 0x64337303),
+     TOBN(0xf14262fb, 0x161f6928), TOBN(0x1661885c, 0x59ba6c9f),
+     TOBN(0x4666ebd3, 0xb82574db), TOBN(0xc5e86672, 0x8d8af3f6),
+     TOBN(0xcc645205, 0x209319bf), TOBN(0xc183c12e, 0x3834d1a8),
+     TOBN(0x533d73da, 0x49eb0f40), TOBN(0x3bcab0bc, 0x6aca649e),
+     TOBN(0xa02f4c41, 0xe39e8361), TOBN(0x2391e7ae, 0xa89bdc85),
+     TOBN(0x88067c5e, 0x608cbe2f), TOBN(0xcdec82f6, 0xf16c22f2),
+     TOBN(0x80aa719a, 0xf1faf9dc), TOBN(0x261fe951, 0x2340185c),
+     TOBN(0xcb411366, 0x9713e72e), TOBN(0xdb1e405e, 0x6d8a2a46),
+     TOBN(0xed174757, 0x11106ce0), TOBN(0x6d70cf6e, 0xf71c0e69),
+     TOBN(0xb5310322, 0xcf707c76), TOBN(0x3a1eb2ff, 0x40b4b7d7),
+     TOBN(0xb83259fb, 0xb1a2d26d), TOBN(0xa83ffb0f, 0x799720c0),
+     TOBN(0xeecb1928, 0x0bedb326), TOBN(0x4473e820, 0xe9271715),
+     TOBN(0x506e6d20, 0x2f2d71a9), TOBN(0xe7b253b1, 0x4319756d),
+     TOBN(0x27e19335, 0xf576cb3c), TOBN(0xe16e4573, 0xdfb3b78f),
+     TOBN(0xaf96d785, 0x63da0001), TOBN(0xb38deafd, 0xf7444c5e),
+     TOBN(0xaca6b38c, 0xc0eb0e28), TOBN(0xa6ca3541, 0x7fe41b98),
+     TOBN(0xfeb37b47, 0x18965b31), TOBN(0x739cc322, 0x597d5bc6),
+     TOBN(0x82776041, 0x4cb1fbc3), TOBN(0xcdaa873d, 0x8e2a3ad1),
+     TOBN(0xb5f87b34, 0xc01318bf), TOBN(0x0c692268, 0x229cb81e),
+     TOBN(0xb0724016, 0xa53089f5), TOBN(0xb2976a53, 0x05415313),
+     TOBN(0x436eab75, 0xcee8fdf2), TOBN(0x8794e1a6, 0xd82b13e5),
+     TOBN(0x0d51a072, 0xd5516e3d), TOBN(0x7bae61ce, 0x4b2fdb3c),
+     TOBN(0x0d987e89, 0x550728ee), TOBN(0xfd5a656e, 0xee6778db),
+     TOBN(0x942ee623, 0xbb4d9d7a), TOBN(0xfc06d64b, 0x2dc1baf8),
+     TOBN(0x5244fcd8, 0x47c3dc8e), TOBN(0x5e37e156, 0x8568653e),
+     TOBN(0xe5c2c6ff, 0x048c8355), TOBN(0x480872ec, 0xa0474046),
+     TOBN(0x67e3089d, 0x7ff92484), TOBN(0xdc071893, 0x29971b3e),
+     TOBN(0x3857db2b, 0x5a63e8ad), TOBN(0xf617d94e, 0x5f2e0a09),
+     TOBN(0x267e9818, 0x5844de79), TOBN(0xfdb103b2, 0x861f5f92),
+     TOBN(0xb969bd3c, 0x1570d6e9), TOBN(0x7350b9db, 0xe0fb517e),
+     TOBN(0x083b142e, 0x55aa543b), TOBN(0x424757be, 0xaa2bc581),
+     TOBN(0x4bd50f64, 0x69ea3302), TOBN(0x053dcf83, 0xed4c8288),
+     TOBN(0xac2b3074, 0xc118ac52), TOBN(0x57f066a8, 0xe76ca88b),
+     TOBN(0xb9ac28fd, 0x0a3207cb), TOBN(0x0ec2ad13, 0x205829b0),
+     TOBN(0x76216e56, 0x4f403b64), TOBN(0x7eaef662, 0x6621dd5c),
+     TOBN(0x9e0cc836, 0x4b06ac54), TOBN(0x16ac3c6c, 0x9648523c),
+     TOBN(0xe1208a1a, 0x08eec1d8), TOBN(0x1e90b3a2, 0xbe054a54),
+     TOBN(0xdfab7f92, 0x23a836cb), TOBN(0x624d6bd2, 0x6f7674c8),
+     TOBN(0xc34a914c, 0xea06210a), TOBN(0xba5314ec, 0xf26c4f23),
+     TOBN(0xd440b196, 0xa33a11cd), TOBN(0xf81ab63e, 0x75eb951f),
+     TOBN(0x05ebb91a, 0x39350f0c), TOBN(0x3f3c08ec, 0x92e9528f),
+     TOBN(0x54ff8844, 0x4fe6f4e6), TOBN(0x217c0e02, 0x79b7ba49),
+     TOBN(0x13c4372b, 0xbf3a4179), TOBN(0x6e5ad10b, 0xa1434264),
+     TOBN(0xc8426540, 0x62bd8ff2), TOBN(0x7c3dd284, 0x85fe6ef1),
+     TOBN(0x2c59b300, 0x05da0e84), TOBN(0xf303ed94, 0x17468e18),
+     TOBN(0xe19fac99, 0xa211ffd3), TOBN(0x408f9424, 0x3f1f6bca),
+     TOBN(0x1f5b76d1, 0xa5648244), TOBN(0xef3942e8, 0x95b2bd67),
+     TOBN(0x1b9dee7f, 0xb538f1d7), TOBN(0x1cb78620, 0x444b8f85),
+     TOBN(0x9f8ecd63, 0xcb8ea6a3), TOBN(0xca111b2e, 0xb9d3b71f),
+     TOBN(0xff83d71e, 0xbdc4e8e2), TOBN(0x43745ddb, 0x3f76a9d5),
+     TOBN(0x72db22a9, 0xa25856ee), TOBN(0xf34d5aa2, 0x5e9a9ff7),
+     TOBN(0x01f6b5f3, 0xbc529902), TOBN(0xadf5d31e, 0x086f4867),
+     TOBN(0xbd88674f, 0xca556b56), TOBN(0xfd00120d, 0xfdc81625),
+     TOBN(0x90fbaba0, 0xfdde77f6), TOBN(0x266d3bfe, 0x559ec6e7),
+     TOBN(0x372acf54, 0xc8094357), TOBN(0x772bd8e4, 0x6c61bb78),
+     TOBN(0xcb2ac592, 0x1af9aefc), TOBN(0xacc3dc9b, 0x5b178386),
+     TOBN(0x09963084, 0x23438463), TOBN(0xae84f973, 0x8359f1e6),
+     TOBN(0xc3b19aa0, 0xa4cee642), TOBN(0xcd5ca5c8, 0xb19a918f),
+     TOBN(0x46ac0d2e, 0xe67cb207), TOBN(0x2ae45e10, 0x73ffebf2),
+     TOBN(0xf84aad8e, 0x10ef065c), TOBN(0xa0af57fa, 0x32a7e903),
+     TOBN(0x42295904, 0x43d346dc), TOBN(0x8d6f711d, 0x7f170965),
+     TOBN(0x11aa7070, 0xb110cffe), TOBN(0x091a100d, 0x9755605d),
+     TOBN(0xc5a0c654, 0xd27d86a6), TOBN(0x1031a244, 0xdb30feaa),
+     TOBN(0x36804045, 0xc02228d9), TOBN(0x1877fc67, 0x8b746039),
+     TOBN(0xba554015, 0xe09bb238), TOBN(0xc50d8f92, 0x1de9b40d),
+     TOBN(0x29e40d88, 0x032f7a2c), TOBN(0x514b56dd, 0x1d315ec2),
+     TOBN(0x9052652e, 0x61778f04), TOBN(0x0d2bc606, 0xe1a1c195),
+     TOBN(0x375fd7ec, 0xb05043ae), TOBN(0x03b82577, 0x6eba4d1b),
+     TOBN(0x8e61b567, 0xc349b39a), TOBN(0xa670973a, 0xb35fc43b),
+     TOBN(0x80c05ca7, 0x5571b4a7), TOBN(0x95d14f49, 0x8a4af0ba),
+     TOBN(0x96767cdb, 0x67bf4290), TOBN(0xb293372f, 0x46870ef1),
+     TOBN(0xc6494405, 0x1afe1027), TOBN(0x9f027a1c, 0x9019c4c2),
+     TOBN(0xa392ac59, 0x188a593a), TOBN(0x68acca2f, 0xfcb6e1ca),
+     TOBN(0xd8f86cbe, 0x68ed921b), TOBN(0x24679ac2, 0x712d2c07),
+     TOBN(0x18fbdb21, 0x4e672cd9), TOBN(0x401bb718, 0x51d1f8e1),
+     TOBN(0x688792e1, 0xaa8da4a1), TOBN(0xedf9266f, 0x3ca93d06),
+     TOBN(0x5ddba14a, 0xaed484df), TOBN(0xa5dab102, 0xb0ea10a5),
+     TOBN(0xd397edcd, 0x833396ea), TOBN(0x78a75693, 0xed5e6747),
+     TOBN(0xf2c844ba, 0x1a5f8260), TOBN(0xbcafe59d, 0x5fb9fec5),
+     TOBN(0xa2413d5f, 0xd3147e7e), TOBN(0x130dd9e3, 0xafdf26cd),
+     TOBN(0x44be87ec, 0x9ad1abde), TOBN(0xe925c495, 0x6e77fbe8),
+     TOBN(0x07ce8d96, 0xf26ced16), TOBN(0x36c85643, 0x86ef7306),
+     TOBN(0x62545902, 0xc7e3d409), TOBN(0x1747bf4a, 0x2ed4d38e),
+     TOBN(0x6fe6fc3d, 0x55adc839), TOBN(0x20a3cc09, 0x8eaf64a8),
+     TOBN(0xc1e9b766, 0x622887b1), TOBN(0x7b9d2f96, 0xc41ac659),
+     TOBN(0xfdb897ce, 0xf2a65e45), TOBN(0x0c09c905, 0x97889eb8),
+     TOBN(0xa15df10f, 0xe4becf5b), TOBN(0x14a3d4fe, 0xccef7e40),
+     TOBN(0xedaa11f6, 0xa8fc67bd), TOBN(0x7bf6fe9b, 0x5d185b42),
+     TOBN(0x7bb9f1f5, 0x6f9cb5c9), TOBN(0x1b4ab74e, 0xf97ea9cd),
+     TOBN(0xe9ebf11d, 0x07638d62), TOBN(0x413a4a87, 0xa78cf538),
+     TOBN(0x93785f86, 0x570dd371), TOBN(0xba431a91, 0xfb48063c),
+     TOBN(0xf1f2ea5b, 0x4ed4e5fa), TOBN(0x91a72c47, 0x5308325a),
+     TOBN(0x4e6518e7, 0xc9ea6acb), TOBN(0xfeaf4c3c, 0x208f67e3),
+     TOBN(0x98c5d768, 0x2c16bb1a), TOBN(0xbf91b62d, 0xee31dc08),
+     TOBN(0xe9ad408d, 0x33a54959), TOBN(0x9d754a64, 0x38b7170b),
+     TOBN(0x106db7bc, 0xd9d6da2b), TOBN(0xf556cbb4, 0xadd533af),
+     TOBN(0x62db0de0, 0xf16d3b58), TOBN(0x78a1b0be, 0x1fa579ba),
+     TOBN(0xda96740b, 0x7b552058), TOBN(0x0c689cc6, 0x626c4d93),
+     TOBN(0xee3dd5c9, 0xaf68e53b), TOBN(0x78653a9f, 0x134d763b),
+     TOBN(0xec9c1b72, 0x3ca5aa67), TOBN(0x67471dac, 0x7d56992e),
+     TOBN(0x0a97dffe, 0xad1d8580), TOBN(0x11c7d93d, 0x0063c040),
+     TOBN(0xb79e355c, 0x6e90b157), TOBN(0x2c06edcb, 0xd9c894c4),
+     TOBN(0x9b931897, 0x71a75ed7), TOBN(0xd7f00247, 0x8e95ad91),
+     TOBN(0xfce1b489, 0xb85bf054), TOBN(0xa3ffb8fd, 0x503b38bf),
+     TOBN(0xe7ea3ad4, 0xe0fe2ec9), TOBN(0x0f32f520, 0x0049865d),
+     TOBN(0x33afa324, 0xcff21b51), TOBN(0x3d6847d7, 0x62a1cd24),
+     TOBN(0xf534e159, 0x0b06ce2f), TOBN(0x24d27b3d, 0xae7cdae0),
+     TOBN(0xb727db29, 0x4ad68ab5), TOBN(0x7e8e47e3, 0xb63c0bc9),
+     TOBN(0xe81f3122, 0x02389039), TOBN(0x0c281f52, 0x88e6a17c),
+     TOBN(0x3cc00e21, 0x091e1c4c), TOBN(0xd500db44, 0x867ccc75),
+     TOBN(0xa8e2e84b, 0xf5ebbbe4), TOBN(0xc3266881, 0xc4279ac0),
+     TOBN(0x2e8fb4de, 0x7a170658), TOBN(0x219c5ec1, 0x51da4a2e),
+     TOBN(0xda69a3fd, 0xeeacee19), TOBN(0x9d4c6fbd, 0x30462243),
+     TOBN(0x43673fe8, 0xa978c29e), TOBN(0x6e825c95, 0x5861bbc1),
+     TOBN(0xb41d1435, 0xdba8e7ba), TOBN(0x0f286f78, 0xb885d004),
+     TOBN(0xea42b7fd, 0xee57436f), TOBN(0xcdae14bc, 0xef7e29c7),
+     TOBN(0x50cff3f0, 0x24251056), TOBN(0xf60809fe, 0x6f6e8cb1),
+     TOBN(0xee9f1d15, 0x12932e53), TOBN(0xa6e55cd6, 0x167d5097),
+     TOBN(0x5df8816d, 0x9d926359), TOBN(0x108e2117, 0x797b7eca),
+     TOBN(0x7ba20319, 0x91921587), TOBN(0x304138e4, 0xad23652e),
+     TOBN(0x73d0ed57, 0x51ebc32f), TOBN(0xe0c10a38, 0xf01d0fc3),
+     TOBN(0x14c6b72f, 0x78c49b19), TOBN(0x4f952b7e, 0x3b7c7418),
+     TOBN(0x3fe75d21, 0xa2d019bf), TOBN(0x4837bfd2, 0x7ca33e87),
+     TOBN(0x4597729b, 0x3946e7ea), TOBN(0xbe22c14a, 0x4c37ea10),
+     TOBN(0x91106c7c, 0xd7909474), TOBN(0xfbf1e7db, 0xbf5551a1),
+     TOBN(0x8e06336c, 0x55ffea13), TOBN(0x0deaeda0, 0x0a1f99f5),
+     TOBN(0x9b738c4b, 0xfda98fc9), TOBN(0x061cc613, 0xa59c98ba),
+     TOBN(0x5ceb5b83, 0xb477692c), TOBN(0x5db77559, 0x1fcc473b),
+     TOBN(0x77214b62, 0x83df476c), TOBN(0x2ffac971, 0x427ea01d),
+     TOBN(0xf29f600b, 0x49fd0ba7), TOBN(0x1106f8b2, 0x7233ef79),
+     TOBN(0x706bc171, 0xe8a0ca35), TOBN(0x4da7a9e6, 0xacbff08b),
+     TOBN(0x17c2fa4e, 0x7725f97c), TOBN(0xab459314, 0xe84a5095),
+     TOBN(0x01556f14, 0x6b24d47e), TOBN(0x01399059, 0xb016dc1a),
+     TOBN(0x154b84c7, 0x28eca6c6), TOBN(0x88ed8612, 0xd9084e68),
+     TOBN(0x4dfd5080, 0x00bf9b5b), TOBN(0x853cd8ab, 0xba9a0cca),
+     TOBN(0x8cbf9bd8, 0x8af0e94b), TOBN(0x50782b73, 0x83035d16),
+     TOBN(0x694d3e65, 0x4f44533c), TOBN(0x155d4bf4, 0xa6e534eb),
+     TOBN(0x9028e2ab, 0xee908b6b), TOBN(0x36e5aac0, 0xa6743060),
+     TOBN(0xd26f5a51, 0x3c37d6f1), TOBN(0x8483703e, 0x33729b9e),
+     TOBN(0xf27a6603, 0x2e5f82a5), TOBN(0x33bf2bdc, 0xca55d187),
+     TOBN(0x894c415c, 0x7724a85f), TOBN(0x9255d416, 0xa2ea178d),
+     TOBN(0x35ecb4e2, 0x0a6dc5b9), TOBN(0x8b3fc2c8, 0x51c54ed2),
+     TOBN(0x059f86eb, 0x9eede13d), TOBN(0xa796c750, 0x791dd5eb),
+     TOBN(0xb2f33680, 0xea88555b), TOBN(0x92730950, 0x1245348c),
+     TOBN(0x1a5545f8, 0xd1e63bfb), TOBN(0xfebc9a14, 0xbebb7858),
+     TOBN(0x13cce767, 0x6bdf0c84), TOBN(0x1aa9dc07, 0xa1d341fa),
+     TOBN(0xd46e8ff6, 0x1ee6fa43), TOBN(0x4198e5d6, 0x4b1dda64),
+     TOBN(0xe82a8134, 0x2782abd1), TOBN(0xe6f7b1b4, 0xb6d64830),
+     TOBN(0xabe00274, 0x7f0fb374), TOBN(0xf1a8e2b7, 0x7494d7d3),
+     TOBN(0xd16b0d9e, 0xf274f296), TOBN(0x3c94a7ac, 0x65246ee9),
+     TOBN(0xd32c32da, 0x91ec2262), TOBN(0x04c7bb90, 0x83116ec1),
+     TOBN(0x70fa0406, 0x78524a06), TOBN(0x8d2d5176, 0x07df8064),
+     TOBN(0x13e589f2, 0xe2c8d48a), TOBN(0x3b110ac4, 0x122aed4e),
+     TOBN(0xe8e0eb52, 0x34e972cf), TOBN(0xc082944a, 0xfb3a77fe),
+     TOBN(0xcdaff7a3, 0x6a32c23b), TOBN(0x88cc568d, 0xc37b4a2c),
+     TOBN(0xc9979a9c, 0xe27b2552), TOBN(0x8612ae7d, 0xd6ef51f9),
+     TOBN(0x7bf0f937, 0xef4e8f85), TOBN(0x2f360a58, 0x3f12d45c),
+     TOBN(0x3ec9d0e3, 0x9b336663), TOBN(0x5ac2df38, 0xb1438d2b),
+     TOBN(0x7f2de910, 0xff93fde4), TOBN(0xbbc460da, 0xd92534ba),
+     TOBN(0x74de3159, 0x59a94ab9), TOBN(0xd51cfd32, 0xc45b80dc),
+     TOBN(0x9f1f349c, 0x6e5b2397), TOBN(0xbdbd16ed, 0x995f7271),
+     TOBN(0x4a7efc1c, 0xf59709a6), TOBN(0x74e2760d, 0x4b3db99a),
+     TOBN(0xa7238e9e, 0x7726a2e1), TOBN(0x47764208, 0x1a376013),
+     TOBN(0xbc96f396, 0x7e33ebc0), TOBN(0x31e159e6, 0xc9e4ec0d),
+     TOBN(0x26a5aef2, 0x6a2ab9f6), TOBN(0x23add07c, 0x320eeea7),
+     TOBN(0xa79a97c9, 0x833b45b6), TOBN(0xb08da907, 0xc51885e6),
+     TOBN(0x23f5e651, 0xae8d5659), TOBN(0x1eb0be48, 0x1faff2f2),
+     TOBN(0xe414ee3d, 0xa1e090da), TOBN(0x16e4f8fa, 0x7fcb664f),
+     TOBN(0x7a15a7e4, 0x98c36865), TOBN(0xea60e8fd, 0xaf89dacf),
+     TOBN(0x4e009f45, 0x86c1a4b4), TOBN(0x78c1bebf, 0x769644ad),
+     TOBN(0xa41b480f, 0x0b4b3501), TOBN(0x98be5037, 0x57f0a0e9),
+     TOBN(0x06934887, 0x3af24657), TOBN(0xe2503ddb, 0x2b6260f9),
+     TOBN(0x37c936c2, 0xd1d0caaa), TOBN(0xd371e991, 0x16431f50),
+     TOBN(0xd9621d16, 0x6087c5e5), TOBN(0xae49c2ce, 0xc53a8bc5),
+     TOBN(0xd7868882, 0xcad314cd), TOBN(0xfde10dc7, 0xaa57af18),
+     TOBN(0x3fa8a60d, 0x3800f397), TOBN(0xcec8ae7b, 0x388b333c),
+     TOBN(0xefd8d692, 0x85fa8129), TOBN(0x33d5685b, 0x90776c32),
+     TOBN(0x47ecec0a, 0x65971a6e), TOBN(0xe8a20bbe, 0xad7c5755),
+     TOBN(0xbeed0a4d, 0x87372016), TOBN(0xd0d499bb, 0x1d9b8dc0),
+     TOBN(0xf4ce27cd, 0x2800907d), TOBN(0x07a30b77, 0x8622bbb7),
+     TOBN(0x7532f435, 0x77e264db), TOBN(0xfdd1a9c3, 0xd64f0710),
+     TOBN(0x92ca210f, 0xa6543720), TOBN(0x2f19ed66, 0x5bb6985a),
+     TOBN(0x08a4ac61, 0xf9399b43), TOBN(0x0b7bac5d, 0x757f241d),
+     TOBN(0x93ef27cc, 0x3763c20d), TOBN(0xda3b206e, 0xa86b1f05),
+     TOBN(0x8f19c74a, 0xd62467c0), TOBN(0x3ec5ef6d, 0x6a3ad263),
+     TOBN(0x249aa636, 0x2bc8b9f0), TOBN(0x0fca7318, 0x7f70990c),
+     TOBN(0x6d4aabc5, 0x6f038497), TOBN(0x4647f72b, 0x5afaaaef),
+     TOBN(0xc7cbe03a, 0x7bf90444), TOBN(0x6beb69ac, 0xcac2efb0),
+     TOBN(0x58544eba, 0xbb3c6dc0), TOBN(0x569cdcd6, 0x96aefc2f),
+     TOBN(0x2e8a4c57, 0x5e4c950d), TOBN(0x6f5341d7, 0x4dd32002),
+     TOBN(0xd0345db6, 0x6efa5908), TOBN(0x4b043197, 0xf1d2bbe6),
+     TOBN(0xde8a4cb6, 0xe8730bae), TOBN(0x9a89231f, 0xa485cb62),
+     TOBN(0xb24ace89, 0xfcd9bcd4), TOBN(0x01892cc0, 0x3ed5801d),
+     TOBN(0x80ce2f30, 0x413408f9), TOBN(0xaf67343a, 0xf8773b6a),
+     TOBN(0x91acc6d2, 0xdd6ade17), TOBN(0x9d2ffeca, 0xe5eb3def),
+     TOBN(0x72f8af06, 0x50b029fd), TOBN(0x339c802c, 0x79c0aabd),
+     TOBN(0x46161fff, 0xafc0a6ad), TOBN(0x1046d9f8, 0xbac9a2d4),
+     TOBN(0x2f12eb06, 0xab920e51), TOBN(0xfc004900, 0x2892e552),
+     TOBN(0x9aadf93e, 0x13e61154), TOBN(0x4468da94, 0xabcfd127),
+     TOBN(0x6a5d3ffe, 0x152f220e), TOBN(0xe6260c23, 0x4737fe79),
+     TOBN(0x8b5dd1c9, 0x5e6b4251), TOBN(0x044f0656, 0x9af02b98),
+     TOBN(0x434d475c, 0xa97ff366), TOBN(0xbae8db8e, 0x2c4bcc46),
+     TOBN(0x2ba43a8b, 0xf9777251), TOBN(0x7ff430a5, 0xdd019f1a),
+     TOBN(0x65e9f290, 0x630064ba), TOBN(0xfc57a54e, 0x7e871c0e),
+     TOBN(0x54107bbb, 0x5791ae48), TOBN(0xdfce137f, 0x5c334de0),
+     TOBN(0xab65c8f6, 0xaed5be73), TOBN(0x838c3958, 0x174bf00b),
+     TOBN(0x27c57199, 0xf1c7e594), TOBN(0x62643d81, 0x0d02fae6),
+     TOBN(0xc1e70c12, 0x5f4431fa), TOBN(0xfac86124, 0xb2b41f7e),
+     TOBN(0x062ac35a, 0xf0108e3c), TOBN(0xd7d34dfc, 0xa43d3a28),
+     TOBN(0xc40fb44a, 0x3143de4d), TOBN(0x06eac4ed, 0xd2e0f9ae),
+     TOBN(0x998f3211, 0x95d9a69a), TOBN(0xb268f6a0, 0xe950bd2e),
+     TOBN(0xadfab210, 0x1e9d4e40), TOBN(0xc453a68c, 0x73ce9324),
+     TOBN(0x5e2f2544, 0x80881171), TOBN(0xe4f596db, 0xee7e543e),
+     TOBN(0x77f48e4e, 0x76b6698e), TOBN(0x47b5165f, 0x227365c6),
+     TOBN(0xf56ec8d4, 0x14ef39e6), TOBN(0x1638d647, 0x74ce46fa),
+     TOBN(0x244d0fac, 0x08aa8b9a), TOBN(0x98ccc4d0, 0x298326c9),
+     TOBN(0x492d7661, 0xce0d2983), TOBN(0x728b3e3f, 0x73158cda),
+     TOBN(0x7959ca67, 0xc4948011), TOBN(0x32044ae9, 0x08425a38),
+     TOBN(0x05a7a6a7, 0xb1070c2b), TOBN(0x34ed541f, 0xcc129ba5),
+     TOBN(0x4b6bf65c, 0xb2f1c3e2), TOBN(0x6f090ce6, 0xd0d8aec8),
+     TOBN(0x11ade23a, 0xd4fe39c1), TOBN(0x50510c08, 0xa5a35528),
+     TOBN(0xb7e2a5de, 0xad6fd7c6), TOBN(0x9d691939, 0x2745dca8),
+     TOBN(0xff669c38, 0xad053184), TOBN(0x394ca6b7, 0xecd0703e),
+     TOBN(0x59e32e80, 0x60b15e75), TOBN(0x82dde889, 0x13c14864),
+     TOBN(0x0fd1624c, 0x64d4f086), TOBN(0x7fb502a7, 0xc9893d7f),
+     TOBN(0x59b86bcf, 0x711479a1), TOBN(0xfd4bc4d8, 0xc40b44bc),
+     TOBN(0x2fae18f5, 0x988100c3), TOBN(0xe4af2f4f, 0x615867d7),
+     TOBN(0x7d45e1e8, 0xbe479e28), TOBN(0x547accbd, 0xa04798a5),
+     TOBN(0xe88a85b1, 0x1c458b5e), TOBN(0xe144f7f2, 0x6032f0cc),
+     TOBN(0xad5276d3, 0x3f807365), TOBN(0x5b6519e7, 0xb318a6ea),
+     TOBN(0x5889cbb5, 0x2d0fcf50), TOBN(0xdce91cab, 0x2bdab4e0),
+     TOBN(0x17b6249f, 0x41b78954), TOBN(0xc9320b65, 0x6f10449b),
+     TOBN(0xe38a7cc0, 0xf264ae8f), TOBN(0xaab8803e, 0x52b85829),
+     TOBN(0x63668621, 0xdd97973c), TOBN(0x5aaedce7, 0xd04138c7),
+     TOBN(0x8e8e6614, 0x1762874c), TOBN(0xd0cefcf4, 0x163fc862),
+     TOBN(0x0ebe0048, 0xffed1ace), TOBN(0x070c3348, 0x7a8c2673),
+     TOBN(0xb801d159, 0x9b0d3fd7), TOBN(0xf1d55911, 0x922d4842),
+     TOBN(0xf0acf768, 0x680dcbf9), TOBN(0x5072b825, 0x4f0a51df),
+     TOBN(0x3a74751c, 0xd88df9c5), TOBN(0x9d20f989, 0x1cc1a332),
+     TOBN(0x4e90042b, 0x6926c34a), TOBN(0x5c728b1e, 0x00766880),
+     TOBN(0xf2e3bfe8, 0xf76e9dcb), TOBN(0xd9822f0a, 0x15a125ae),
+    },
+    {
+     TOBN(0xbf84db58, 0xf51b14b0), TOBN(0xdf73ccf5, 0xa39a79f0),
+     TOBN(0x0ce1e584, 0x2b5a1f11), TOBN(0x841fa6a3, 0x185fc400),
+     TOBN(0x94b09c68, 0x2455c32a), TOBN(0x383c9bde, 0xbfa71cc3),
+     TOBN(0xb6381486, 0x1e797929), TOBN(0x33036faf, 0x623d0a5d),
+     TOBN(0x41b6cf7c, 0x90f17cba), TOBN(0x5d655ff4, 0x30c7c5f4),
+     TOBN(0xc64f29d5, 0x4ccc7f38), TOBN(0xf28e8531, 0x6124a79e),
+     TOBN(0x1efa8d51, 0x67bf1e98), TOBN(0x8610027f, 0x5d7a33b0),
+     TOBN(0x35fe2bb2, 0xcb9a40a4), TOBN(0xc5cc1bf1, 0x43d50a0b),
+     TOBN(0x84dbc605, 0x46e33870), TOBN(0x23d8d2e5, 0x843c4e1e),
+     TOBN(0x69964b5e, 0x4cf8b569), TOBN(0x2a5228e8, 0xe0c546a5),
+     TOBN(0x4c0467ed, 0x96d6e111), TOBN(0x25764cdf, 0xa12bd298),
+     TOBN(0x92a3e7fa, 0xfbaaad46), TOBN(0x08ac1d36, 0xd12fa469),
+     TOBN(0x60ae2bbf, 0xa32106c2), TOBN(0xef155b2a, 0x3e917750),
+     TOBN(0x5567c3c7, 0x13853a30), TOBN(0xa6be8290, 0xeddb305b),
+     TOBN(0x2db58c21, 0xade26eec), TOBN(0xfa3c895c, 0x003c17ed),
+     TOBN(0x96ab0de1, 0x6293f8a2), TOBN(0xbd2365ec, 0xac3710c6),
+     TOBN(0x93ea8553, 0x6aa24f73), TOBN(0xf75140d0, 0xe0410c40),
+     TOBN(0x760cfa2f, 0xaff0f7f2), TOBN(0xc6dfb3c7, 0x3e580d68),
+     TOBN(0x25fc2091, 0xc16d39e2), TOBN(0xa05b0c81, 0x19e1d5e2),
+     TOBN(0xd4d223d8, 0x62bbec7a), TOBN(0x11a93775, 0xf293f34a),
+     TOBN(0x9ab03e73, 0xe194c642), TOBN(0x607b7106, 0x789e3c85),
+     TOBN(0x952aab02, 0x4bdacd55), TOBN(0x31ca3ee2, 0x21cc6084),
+     TOBN(0xd3149b2b, 0x1c6b93f9), TOBN(0xcbc5ef3b, 0xead930f8),
+     TOBN(0xed04984f, 0x22872630), TOBN(0xef5d052d, 0x6c4b6fe2),
+     TOBN(0x808ae6c0, 0x6010ffa2), TOBN(0x88b6fcd8, 0x1143166a),
+     TOBN(0xa2780263, 0x5ab945ec), TOBN(0x4777b4aa, 0x36db5012),
+     TOBN(0x2156bf83, 0x059aa3c7), TOBN(0xcbef6fb7, 0x2a660260),
+     TOBN(0x189fa221, 0x8b366ce5), TOBN(0xd6f5bdaa, 0x08290914),
+     TOBN(0xd2028d05, 0x57028677), TOBN(0x90eebeeb, 0xce9aabdf),
+     TOBN(0xab977aee, 0x06d4e5d0), TOBN(0x7a98c527, 0xf9361873),
+     TOBN(0xe49b1251, 0xb7c2474d), TOBN(0xcdaf2a36, 0x5f3e7b02),
+     TOBN(0x638bcaf4, 0x6fe54af1), TOBN(0xfec42624, 0x1dac06b7),
+     TOBN(0x422be225, 0x3741a88b), TOBN(0x1f3b82c3, 0x5304f139),
+     TOBN(0x101eab8e, 0x181c2fc2), TOBN(0x8522738e, 0x5898163c),
+     TOBN(0x0372d05f, 0x2d2bac1b), TOBN(0xb65c786c, 0x1d1f3c42),
+     TOBN(0xbd59694b, 0x64e2f5b3), TOBN(0xf1c424bf, 0x24c5671b),
+     TOBN(0xda738cf5, 0x1eafe37b), TOBN(0x503eac24, 0x30dd7c2b),
+     TOBN(0xf9b7b7a5, 0x11dd77af), TOBN(0x0ade03af, 0xe9dcfe7c),
+     TOBN(0x489bd34a, 0xf479e3b5), TOBN(0x993ab403, 0x030a33f3),
+     TOBN(0xaef322bf, 0x9fb64068), TOBN(0xa47cc71b, 0x0e27f110),
+     TOBN(0x1c445554, 0xefab99c8), TOBN(0x7c3c51e7, 0xa7f10e58),
+     TOBN(0xaa8b43ee, 0x78a87474), TOBN(0x037d6397, 0x2418475a),
+     TOBN(0xc9c751fe, 0x10324911), TOBN(0x3d65d9e0, 0x3e0797d4),
+     TOBN(0x98b68d2b, 0x7dea2a63), TOBN(0xa211ed3b, 0xf4afca19),
+     TOBN(0xe19ff8f8, 0xc63b9e75), TOBN(0x43f6b4fc, 0x0d7d130a),
+     TOBN(0xdba3385d, 0x7c8490b7), TOBN(0x97889df7, 0x0252c14a),
+     TOBN(0xfccfca86, 0xb2f18b9f), TOBN(0xf092ff9e, 0xc3a87422),
+     TOBN(0xf96dd675, 0x67474838), TOBN(0x039e8287, 0x5bad2e9f),
+     TOBN(0x7ed85e70, 0x52e041f6), TOBN(0x3d6ef1e7, 0xcfdeb19f),
+     TOBN(0x9f9fe399, 0x0d9ac66e), TOBN(0x5825e7bf, 0x16cb8554),
+     TOBN(0xecffdf90, 0xd954a4d5), TOBN(0x8617ffdd, 0x20678fc5),
+     TOBN(0x3e974054, 0x666df77b), TOBN(0x748379d1, 0xb5d92788),
+     TOBN(0x46a60911, 0x2da32c0a), TOBN(0xb2e1ac32, 0xb2676ca3),
+     TOBN(0xfb74093f, 0x17614dc6), TOBN(0xf44939e4, 0x3f27f965),
+     TOBN(0x4656a402, 0xc922422b), TOBN(0xd60a55ba, 0x3ff5c56f),
+     TOBN(0x0d061b41, 0xab9aa62e), TOBN(0xc9ceacfe, 0xaca3acd2),
+     TOBN(0x056d5718, 0xd946003b), TOBN(0xf8530d6d, 0x2c7815f3),
+     TOBN(0xbae14342, 0x706536b8), TOBN(0x45c96dda, 0x2b901188),
+     TOBN(0x386d88b6, 0xc64ed946), TOBN(0xb7017022, 0x6c00f1c2),
+     TOBN(0x28519811, 0xec8988df), TOBN(0x3b011fe2, 0x5a05cffc),
+     TOBN(0x4f581d47, 0x515f954c), TOBN(0x145f925b, 0x7f470a40),
+     TOBN(0xfee6b6b0, 0x736feaaf), TOBN(0xf90744af, 0x2ea5743b),
+     TOBN(0x4d8e8cea, 0xa2f36f56), TOBN(0x4239a6ce, 0xe3ed4007),
+     TOBN(0x0906b5bd, 0xd515e6db), TOBN(0x53622990, 0x8ac973d1),
+     TOBN(0x472ceb94, 0xeb2fe229), TOBN(0x0775ed41, 0x6a121363),
+     TOBN(0xc0492e07, 0x761ddb38), TOBN(0x80c24d51, 0xaef9be2f),
+     TOBN(0xa2a3982b, 0xdcba73a1), TOBN(0xe0d83978, 0x4e26d062),
+     TOBN(0x794959a8, 0xcd41c930), TOBN(0x7d2a88d7, 0x70131161),
+     TOBN(0x48f93fc3, 0xf4f966da), TOBN(0xf92691a0, 0xed5b6487),
+     TOBN(0xc5a93e5d, 0xada2c1fc), TOBN(0x4a7aca52, 0x4b7d9243),
+     TOBN(0x810aba93, 0xd7c5598b), TOBN(0x98f9ead2, 0x25109638),
+     TOBN(0xe8c6e893, 0xa388481a), TOBN(0x56e96b9b, 0xe91ce74b),
+     TOBN(0xfa1e5dc3, 0xd935f591), TOBN(0x985bb06c, 0x555eb816),
+     TOBN(0x6478c518, 0xc4d14e69), TOBN(0x48afbdbc, 0xc7f47349),
+     TOBN(0xbde90933, 0x26fed96c), TOBN(0xf9b96f41, 0xcd468186),
+     TOBN(0x22de6a29, 0x730e8518), TOBN(0x7a3dc912, 0x915db842),
+     TOBN(0x8d13b665, 0xfc1f9449), TOBN(0x6e9932a9, 0xdd4bba75),
+     TOBN(0xa90ce8e5, 0x564083da), TOBN(0x8a7cf362, 0xbbf7989d),
+     TOBN(0x652eccb7, 0x1b07ee2f), TOBN(0x0c0dcf1a, 0x6857a141),
+     TOBN(0xa87ec410, 0xb7bfb43e), TOBN(0xaebdb7e7, 0x82b8d179),
+     TOBN(0xeb3bc468, 0x625a24dd), TOBN(0x7e45e47b, 0x463b1f89),
+     TOBN(0xc3013535, 0x00c75a48), TOBN(0xafea920d, 0x13778807),
+     TOBN(0x0d1e9277, 0x22dcef16), TOBN(0xa2a10f67, 0x86cecfd6),
+     TOBN(0xad40e29c, 0xd7160bf2), TOBN(0xe78e6589, 0xeac1265e),
+     TOBN(0xd3a24310, 0x0c62c041), TOBN(0x4d27344a, 0x6c03c747),
+     TOBN(0x0b19e4a6, 0x7d3ee9d1), TOBN(0x9cf2eccd, 0xcd90de33),
+     TOBN(0x673a9d1f, 0xfda636a9), TOBN(0xb7349981, 0xa86ee501),
+     TOBN(0x11ca1e49, 0xe43766ed), TOBN(0x0806af6f, 0xe3ff3b08),
+     TOBN(0x21304338, 0x8a01f119), TOBN(0x58a6d3be, 0xf3cb268f),
+     TOBN(0x40ceacca, 0xe37d7851), TOBN(0x18694595, 0xef5b81e8),
+     TOBN(0x35678ed7, 0x84bad32a), TOBN(0x4f280f92, 0xd1624256),
+     TOBN(0xdecb1f1e, 0xfb28709c), TOBN(0x2a7f3048, 0x164911d7),
+     TOBN(0x32551d31, 0x579d8a41), TOBN(0x754c7c24, 0x60a5ee33),
+     TOBN(0x2c53fbff, 0x6a88f85f), TOBN(0x6ad0bda7, 0x2c7a36a0),
+     TOBN(0x8b3674f8, 0x15724d6c), TOBN(0x614727ce, 0xb9b7b34a),
+     TOBN(0x384fba98, 0x82ca9cd7), TOBN(0x8ef4343c, 0x0c566025),
+     TOBN(0x5645fefb, 0x64886c98), TOBN(0x702befb3, 0x0f5c29e8),
+     TOBN(0x6d74a7e0, 0x46de1523), TOBN(0xcb2bcdb9, 0xb1302163),
+     TOBN(0xe65cff39, 0xab4ca69b), TOBN(0xeacb7103, 0xf2d4f6ec),
+     TOBN(0x15826c2d, 0x1770d3ef), TOBN(0x38b008f1, 0x3f710600),
+     TOBN(0xc68450cb, 0x4bc7dccb), TOBN(0xb5f11b26, 0x9e5f2400),
+     TOBN(0x2af58e9e, 0x9c3a833b), TOBN(0xb60e2672, 0xa058abaa),
+     TOBN(0xe891bf8c, 0x75b080c0), TOBN(0x5b09b276, 0x2434bf38),
+     TOBN(0x0d90a040, 0x700b8739), TOBN(0x995cb042, 0xe69f4a0b),
+     TOBN(0xe30df0a1, 0x44a56b84), TOBN(0xbaf92d16, 0x1ead5a62),
+     TOBN(0xe214a062, 0x6e0193a4), TOBN(0xd41de5bc, 0xe9758b9e),
+     TOBN(0xcf214213, 0x732d82d5), TOBN(0xaa1421f6, 0xf949f07b),
+     TOBN(0x5f38c91e, 0xf7fb101c), TOBN(0x47ce2ec2, 0x2a3e41e4),
+     TOBN(0x6bb34768, 0x240c7897), TOBN(0x80ff54ea, 0x7b45473e),
+     TOBN(0x16acd40f, 0x82fe5aac), TOBN(0xa3e76f52, 0x4350449f),
+     TOBN(0xf7a3579e, 0xacacbeb9), TOBN(0x9791e0e0, 0x7bc40732),
+     TOBN(0xb26da7b5, 0xbc58cb9d), TOBN(0x11d9fc80, 0x987e18f4),
+     TOBN(0xc3c189a8, 0x1d8e0d34), TOBN(0x3011097c, 0x2d42e0b5),
+     TOBN(0x4e385932, 0x94ab9371), TOBN(0x79e0c2ce, 0x0c237147),
+     TOBN(0xc9f17122, 0x7172e6ce), TOBN(0xf8d73b1d, 0x9b89a008),
+     TOBN(0x91690c6b, 0xa945995d), TOBN(0x089eb941, 0xc15306c6),
+     TOBN(0xee5f526d, 0x12ac8df5), TOBN(0xf1dd23f7, 0x3bf94c65),
+     TOBN(0x594ceaac, 0x81a3cb0e), TOBN(0x07d41d3b, 0x9757cc8b),
+     TOBN(0x9eb0081d, 0xfc5603d5), TOBN(0xfb5d3298, 0x57bd230c),
+     TOBN(0xf2c0402e, 0xcde3f845), TOBN(0xa2defd67, 0x41e8aba6),
+     TOBN(0xb300802a, 0x2dd9573d), TOBN(0x64e401a5, 0x60c1ded3),
+     TOBN(0x19d4a677, 0x8ab1d3d8), TOBN(0x3c2092f2, 0xcca04f74),
+     TOBN(0xf4827ba5, 0xac40056a), TOBN(0x49d4cf22, 0x9c09ddc2),
+     TOBN(0xb2b00f6b, 0xdbf20277), TOBN(0xc9ac48d4, 0x5b281e9b),
+     TOBN(0x648d6674, 0x32efbbce), TOBN(0x64a6c2b3, 0xe9639719),
+     TOBN(0x38c04657, 0x30662e7d), TOBN(0x15d1d7ca, 0x352c9184),
+     TOBN(0x70e8630c, 0xcc3020cc), TOBN(0xe4b56c9c, 0xb09f038f),
+     TOBN(0xdb9cb5ed, 0xfe76a744), TOBN(0x4c85f020, 0x6947b988),
+     TOBN(0x7e500126, 0x29d8add4), TOBN(0xdbcfd295, 0xbfaf6d7e),
+     TOBN(0xc1a1c228, 0x38df80be), TOBN(0xcfa6272a, 0xf606ce3d),
+     TOBN(0xbf2a5720, 0x8e0af540), TOBN(0xb9c544fd, 0x5b599ab0),
+     TOBN(0xd6dc994d, 0xd0a22c9a), TOBN(0xa8a12acf, 0xd23e4c0e),
+     TOBN(0x41f7ac85, 0xba588a5e), TOBN(0x5425fa00, 0xccdb9687),
+     TOBN(0x12fbce76, 0xec9398f0), TOBN(0x2ad69251, 0x4f550b9b),
+     TOBN(0x120ff0f2, 0xbb318636), TOBN(0x9378346c, 0x01ecd90b),
+     TOBN(0x1b98fe99, 0xd0ba149b), TOBN(0xd69d5462, 0xc9c481c8),
+     TOBN(0x11c79184, 0x959e428e), TOBN(0x9de61a8d, 0xcff227cc),
+     TOBN(0x144dfdcd, 0x1e09b860), TOBN(0x110c3a47, 0xf8ebe350),
+     TOBN(0x59e574dc, 0xfadf86b0), TOBN(0xe6ff6e12, 0xcf3b8d30),
+     TOBN(0xe2d512fc, 0x19c77143), TOBN(0x63461543, 0x60279af1),
+     TOBN(0xff65189c, 0x32b4d371), TOBN(0x022fecca, 0x0faf5ba7),
+     TOBN(0xd08fe9bf, 0x414707b4), TOBN(0x0ef8af2b, 0x722d5fd2),
+     TOBN(0xbef06063, 0x4e6fa14a), TOBN(0x1c41f752, 0xcca29177),
+     TOBN(0x17dc7e18, 0x65091fe1), TOBN(0x693d72d2, 0x23f13c18),
+     TOBN(0xce88eb02, 0xce8e2d30), TOBN(0x7071f98a, 0xe972faca),
+     TOBN(0xb7388d61, 0x549c38ee), TOBN(0x7cfccee2, 0x0b788b8c),
+     TOBN(0xdc470705, 0xcb93b5e8), TOBN(0xea053c18, 0xab96d485),
+     TOBN(0x70e96456, 0xd634c9b3), TOBN(0x2c58c20b, 0xd5999cf2),
+     TOBN(0xcd392b3c, 0xa77c1767), TOBN(0x14471fab, 0x7c4e6bd9),
+     TOBN(0x312e1547, 0x75c905ff), TOBN(0x45764654, 0xace47038),
+     TOBN(0xa34a0b0e, 0x8fc3d697), TOBN(0x5d9ad31a, 0xd309ed3a),
+     TOBN(0xbba340c0, 0x0918f957), TOBN(0x768e06e8, 0x31fd72a1),
+     TOBN(0x77e5dd92, 0x3e1a4a54), TOBN(0x0970719f, 0x3fdbc1e1),
+     TOBN(0xd4f1da6f, 0xb0371fe2), TOBN(0x3635f433, 0xfd7f895a),
+     TOBN(0x0e8e40e6, 0x411c8e6f), TOBN(0x31d126bd, 0xec18103c),
+     TOBN(0x415a0cc1, 0xc299d7cc), TOBN(0xdf528e7b, 0x3a8e97f1),
+     TOBN(0x4551a8c7, 0xeed95e91), TOBN(0x8de89888, 0x32bcfb03),
+     TOBN(0x25da4f5f, 0x2eac5c3a), TOBN(0x6d0b2e25, 0x5f88d63f),
+     TOBN(0x8d158d14, 0x575d6145), TOBN(0xe5601a6b, 0x345f62b0),
+     TOBN(0x6f951699, 0x113c6895), TOBN(0x79e29fd5, 0xb87e50ef),
+     TOBN(0xf1ab215c, 0xd5fa51ff), TOBN(0x4fc5c4ea, 0xaf2c3094),
+     TOBN(0x1baeda40, 0x2c006042), TOBN(0xcdfcc37c, 0x3e30e75f),
+     TOBN(0xdd64e5dd, 0x467f57eb), TOBN(0xa5b13731, 0x22902d21),
+     TOBN(0x856866dd, 0x1c52cb7b), TOBN(0x05cf0f7a, 0x16a08caa),
+     TOBN(0xa46e8a55, 0x533b4d09), TOBN(0xfc803998, 0x4e073af1),
+     TOBN(0x8e3825c8, 0xe0d589c3), TOBN(0x505e8e5d, 0x4c1daef3),
+     TOBN(0x9f8363b1, 0xc5f3db12), TOBN(0xe7d46700, 0x74f569e2),
+     TOBN(0x551fd2ed, 0x4d68461a), TOBN(0x26248da5, 0xa8bbe83d),
+     TOBN(0x8d90c47f, 0x65681dbd), TOBN(0xe726d25e, 0x2200ba6b),
+     TOBN(0xa2fe408f, 0x65a3bc9b), TOBN(0x94a80457, 0x9c443b57),
+     TOBN(0x95f7f024, 0x07364677), TOBN(0xe9d9bc87, 0xdaf0fb34),
+     TOBN(0xe9082548, 0x5588e979), TOBN(0xede1f94d, 0xa0e61ff2),
+     TOBN(0xcb89a1e8, 0x45e1c230), TOBN(0xee014c23, 0x50a15304),
+     TOBN(0xf25d8ffa, 0x2bab57e1), TOBN(0x8a920680, 0x26223c6e),
+     TOBN(0xc5abb7af, 0xaadf7e6a), TOBN(0xcb57c893, 0x9e7d8da5),
+     TOBN(0x839bcda0, 0x7d589a91), TOBN(0x1fa774c0, 0x77e82027),
+     TOBN(0xeca669cf, 0xba6504d7), TOBN(0x7bf09544, 0x6845e47d),
+     TOBN(0x5eb6c33e, 0x607b3641), TOBN(0xf445556e, 0x64bab450),
+     TOBN(0xed0b1c02, 0x86096fde), TOBN(0x2c5ba666, 0x8ea41693),
+     TOBN(0xe578b315, 0x37ec248d), TOBN(0x97ef44fe, 0xf64ed28f),
+     TOBN(0xfa5a6c46, 0xce419462), TOBN(0x29336dc9, 0x9cce80e9),
+     TOBN(0x9e9054b9, 0xeee7617f), TOBN(0xcea9a100, 0xf3d51cba),
+     TOBN(0xc3cce5e8, 0x13267ec6), TOBN(0x650c8383, 0xa4e807e7),
+     TOBN(0x1338e22e, 0x9b2726dc), TOBN(0x220c50b2, 0xbf79b47a),
+     TOBN(0xe160d496, 0xa0e0962a), TOBN(0xe1a26460, 0xe1ed5cdc),
+     TOBN(0x9a1ed8c3, 0x31427c62), TOBN(0x65ef5300, 0xe99a096a),
+     TOBN(0x38abea5f, 0x4e3ad558), TOBN(0x03bb15e9, 0x0880ba0c),
+     TOBN(0x1e6dda7e, 0x0141b036), TOBN(0xd31b08bf, 0x5bf37669),
+     TOBN(0x948e0366, 0x68da20d2), TOBN(0x36998a24, 0x4108fe36),
+     TOBN(0x7606e6ed, 0xf9d6563b), TOBN(0xcf7cbdd3, 0xe42172ba),
+     TOBN(0x2335a9a4, 0xa1265b99), TOBN(0x64776cdc, 0x30ac3f69),
+     TOBN(0x04040362, 0xa59b595e), TOBN(0x82df96b9, 0x2cbc03cd),
+     TOBN(0xe9d18c7f, 0x6cea2796), TOBN(0x3112c4f6, 0xe1ea7e35),
+     TOBN(0xf9cbc205, 0x5f8a786d), TOBN(0x36cc6d42, 0x2097da0d),
+     TOBN(0x54093350, 0x2153e665), TOBN(0xebe9db0f, 0xce937bb9),
+     TOBN(0x9d1a5972, 0xd95942f8), TOBN(0x81c1f94a, 0xd4bd5c74),
+     TOBN(0x61dc7318, 0xaa04152e), TOBN(0xdf636db1, 0x95e5ec9f),
+     TOBN(0x64a80d46, 0x48090850), TOBN(0x2b37679e, 0xce890a30),
+     TOBN(0x9f46d5b9, 0xff6025e3), TOBN(0x6eed5a44, 0xf24534dd),
+     TOBN(0xc56b5cb1, 0xf740a84b), TOBN(0xb4641c28, 0x228cc826),
+     TOBN(0x676289be, 0xaf62b943), TOBN(0xe3f3810c, 0x1eae5409),
+     TOBN(0x73613f32, 0x04b5be78), TOBN(0xe6359179, 0x398b556c),
+     TOBN(0x6a342b12, 0xc0263f77), TOBN(0x6b093bbd, 0xc10a6db5),
+     TOBN(0x8f3fc90d, 0x29832fb9), TOBN(0xb3f2d8fc, 0xff03b2ff),
+     TOBN(0x1de7bd1c, 0x64457331), TOBN(0x0a03a06b, 0x43bb1380),
+     TOBN(0x6720cc3d, 0x8bf17419), TOBN(0x2627c7da, 0x33938d5a),
+     TOBN(0x204da058, 0x8d98f34c), TOBN(0x80e29f46, 0x51cbd284),
+     TOBN(0x11b22dd4, 0xa46f93d5), TOBN(0xd7341655, 0xe971a61a),
+     TOBN(0x36a748b7, 0xee69f782), TOBN(0xa3740020, 0x94f08ac0),
+     TOBN(0x383fb245, 0xc36099f3), TOBN(0xa7cb0ef9, 0x00137fdc),
+     TOBN(0x5371052f, 0x6e1dd2e5), TOBN(0xed3ab7b5, 0x7976a1d3),
+     TOBN(0xb0119c0d, 0x9df822e6), TOBN(0xafd2a477, 0x358685d1),
+     TOBN(0x82879cb0, 0x4ae1103c), TOBN(0x61cd6ca8, 0x94385be6),
+     TOBN(0x7c551809, 0xd85d4a62), TOBN(0x9632ac5f, 0xb318d965),
+     TOBN(0x67baad2c, 0xe1340605), TOBN(0x39c2c8c7, 0xac6ed4f7),
+     TOBN(0x42c4a7b1, 0x71211c2f), TOBN(0x43c778bb, 0x9bf758f6),
+     TOBN(0x2dc8fc39, 0xf519acb2), TOBN(0xd3c30a6d, 0x08eff177),
+     TOBN(0xf889c021, 0x5144484b), TOBN(0x01b82327, 0xca376af3),
+     TOBN(0x168a0b2f, 0xd3e71253), TOBN(0x5e92c6ba, 0x3f9ff89d),
+     TOBN(0x8c74c132, 0x5b4c6844), TOBN(0x707c4a40, 0x33de6858),
+     TOBN(0xb13f6abd, 0x9c36dd9e), TOBN(0x4baaef52, 0x9b3aa9f5),
+     TOBN(0x0a4fa929, 0xcd156392), TOBN(0xde0f1956, 0x6506f42f),
+     TOBN(0xe43dd7f0, 0x150d4ee7), TOBN(0xf7afe7db, 0x7247f552),
+     TOBN(0x9abc8d1c, 0x9072b450), TOBN(0x5d0645d5, 0xc9a8e159),
+     TOBN(0x863d3e8f, 0x01c6f17a), TOBN(0x3a0575ac, 0xdf080690),
+     TOBN(0xcad62d87, 0x2b0fb150), TOBN(0xa1f54744, 0x625c35c6),
+     TOBN(0x7d3bcec3, 0x41fe59ec), TOBN(0x0fd3e40e, 0x169f1e04),
+     TOBN(0xbde8c827, 0x2ed9aa4b), TOBN(0x71562ee6, 0x13046c6e),
+     TOBN(0xaf049c5c, 0xe9acac7a), TOBN(0x7efec06c, 0x261dd422),
+     TOBN(0xa530fbfd, 0x095564c4), TOBN(0x000c0c82, 0x2a57af79),
+     TOBN(0x9f79868f, 0x2ce1315c), TOBN(0x0dd99453, 0x1b5d575e),
+     TOBN(0xf1a49419, 0x1e534cfd), TOBN(0xc7de8756, 0xed7e8b39),
+     TOBN(0xef61f5c8, 0x3ed2ccb2), TOBN(0x032ee766, 0x34af2a15),
+     TOBN(0xe0692ed5, 0x9f69ae9d), TOBN(0xd34fc2d5, 0xf64900df),
+     TOBN(0x1c51c950, 0xaca6d51b), TOBN(0x10ae0fb2, 0xa7717dfb),
+     TOBN(0x9fa305f7, 0xa7ec7ca8), TOBN(0xb215a8ab, 0xb5728214),
+     TOBN(0x62628fdf, 0x8819505b), TOBN(0x3cefd86c, 0x004ba54e),
+     TOBN(0xa17bed74, 0xc571da3d), TOBN(0x362dfef6, 0x93a5faa5),
+     TOBN(0x1bee6899, 0xf8aeea05), TOBN(0xd7bf7e31, 0x16f18b7a),
+     TOBN(0x3f3cf39d, 0x1cb7685c), TOBN(0x1df41f23, 0xe2e57c8e),
+     TOBN(0x8f62ecb8, 0xe2fd94f1), TOBN(0x652099c9, 0x4c30a178),
+     TOBN(0xaa2454e1, 0x4262e9e6), TOBN(0x7f0d440f, 0x2015d4a9),
+     TOBN(0xa2c76313, 0xbb5b39fa), TOBN(0x46e57ab2, 0x1ab47bb3),
+     TOBN(0xd181f444, 0x8697e682), TOBN(0x55db129e, 0x33273dfe),
+     TOBN(0xda188361, 0xe71d029f), TOBN(0x3e3e19da, 0xb5def631),
+     TOBN(0x7431f513, 0x087ad30b), TOBN(0x2537887e, 0x9f27c84e),
+     TOBN(0x0c228c62, 0xac9df89d), TOBN(0xdcd2c5e9, 0x10031289),
+     TOBN(0x5cc76782, 0x0321d1b6), TOBN(0x4e460bdf, 0x6cb3d600),
+     TOBN(0x6f356aab, 0x9a870166), TOBN(0x21aecb3b, 0x497d4ac0),
+     TOBN(0xd981a4b0, 0xf0495ef1), TOBN(0x615e8bff, 0x0fb7704b),
+     TOBN(0xc148e8ea, 0x8478bf12), TOBN(0x7011ec5b, 0x364eee52),
+     TOBN(0xd9075965, 0xf692bc12), TOBN(0x3019c824, 0xe622ad51),
+     TOBN(0x349e4873, 0xec83c953), TOBN(0xb4f59fb3, 0x3a21ef0a),
+     TOBN(0x3872d314, 0x40f7d93e), TOBN(0x479e1d02, 0xc2568c82),
+     TOBN(0xd7e4dc9a, 0x65d43d22), TOBN(0xcc068e81, 0xe775efa8),
+     TOBN(0xb78ccae9, 0x326defa6), TOBN(0x8f92b296, 0x2da64956),
+    },
+    {
+     TOBN(0xb721f8d5, 0xdea227ee), TOBN(0xf48c766c, 0x3dda8ba0),
+     TOBN(0x0583d94b, 0xe43e3520), TOBN(0xebda36c9, 0xe1d898b6),
+     TOBN(0x1808286a, 0x6627adaa), TOBN(0x19c4c620, 0x9938368e),
+     TOBN(0xe0dbd707, 0xf750949f), TOBN(0xcadf4bea, 0x0cf356d9),
+     TOBN(0xf5de2126, 0x2dc890a7), TOBN(0x76b7b675, 0x95aa75a3),
+     TOBN(0x475fc143, 0x2a070b32), TOBN(0x7429a646, 0x8e31d68f),
+     TOBN(0xec3a9aaa, 0x09be3dca), TOBN(0x07e119a9, 0xaf780ed7),
+     TOBN(0x62125625, 0x64fd96c4), TOBN(0xb571494f, 0xe8e80577),
+     TOBN(0x955ee349, 0x5228d735), TOBN(0xa04ef2bb, 0x8fc5d4b6),
+     TOBN(0x0c532891, 0x3600814f), TOBN(0x41f1f637, 0x59f85bd4),
+     TOBN(0x72f1d731, 0xe3dcdfb4), TOBN(0x28a4ddb9, 0x3aa5edb3),
+     TOBN(0x116a68e1, 0xf702dcdb), TOBN(0x1975bc42, 0x3bde657e),
+     TOBN(0x7b9f561a, 0x8a914b50), TOBN(0x2bf7130e, 0x9154d377),
+     TOBN(0x6800f696, 0x519b4c35), TOBN(0xc9e65040, 0x568b4c56),
+     TOBN(0x30706e00, 0x6d98a331), TOBN(0x781a12f6, 0xe211ce1e),
+     TOBN(0x1fff9e3d, 0x40562e5f), TOBN(0x6356cf46, 0x8c166747),
+     TOBN(0x80e87329, 0x429945a7), TOBN(0xc619fe17, 0xb7ab06ad),
+     TOBN(0x9116bc2e, 0x6fd86b17), TOBN(0x64a41877, 0xb9116aac),
+     TOBN(0xe3ed867e, 0x32ba4f3b), TOBN(0x013e263b, 0x68b4ebe6),
+     TOBN(0x305ebfe7, 0xe779e4ec), TOBN(0x5536d45d, 0x50178251),
+     TOBN(0x5abb939f, 0x8873a93d), TOBN(0x0263ba48, 0x8c4c9cb1),
+     TOBN(0x36764b8d, 0x6b78a4b5), TOBN(0x205bb45d, 0x28bebc1e),
+     TOBN(0x16df4bb0, 0xae89dcd5), TOBN(0x85994670, 0x316fadb7),
+     TOBN(0x71f75664, 0x3af3c724), TOBN(0x43e30313, 0xe8520c9c),
+     TOBN(0x3ab9ec54, 0x29e91921), TOBN(0xd931436e, 0xe3299f47),
+     TOBN(0xb56da7bf, 0xb89cd49f), TOBN(0x90623412, 0xcff7f637),
+     TOBN(0x751e7944, 0x714022de), TOBN(0x86bcc342, 0x2c338262),
+     TOBN(0x85f6a9bc, 0x314c23bb), TOBN(0xedbe8e74, 0x1f0a3991),
+     TOBN(0x7a748d63, 0x003b40dd), TOBN(0x8a682402, 0x3951b7ae),
+     TOBN(0x41e92dd9, 0x704a91b0), TOBN(0x2dfb3eb9, 0x858cd3ea),
+     TOBN(0xc3c2af35, 0xf5094667), TOBN(0xffa287dc, 0x7435aa2d),
+     TOBN(0xd03f3979, 0x7462714f), TOBN(0xdb550f67, 0x203e5b0e),
+     TOBN(0x6df7801b, 0xe241ed0c), TOBN(0xb52c0a3f, 0xb642fd3a),
+     TOBN(0xdd35e1cf, 0x1977a29d), TOBN(0x8e793d60, 0xa661404c),
+     TOBN(0x393e2b87, 0x6b9442ae), TOBN(0x123b893a, 0x2aa6b707),
+     TOBN(0xeec88682, 0xdb8d306a), TOBN(0x92c2d93d, 0xce847879),
+     TOBN(0x725f1e7d, 0x80ec63b4), TOBN(0xcb8f53d9, 0x74113de0),
+     TOBN(0x2132a072, 0xb819f730), TOBN(0xfabf3c47, 0xb4c61f06),
+     TOBN(0x79c1bc86, 0x2cb243d8), TOBN(0x442833c5, 0x757e3600),
+     TOBN(0xfa4f69ad, 0x4e918b8a), TOBN(0x5816f3f3, 0x73bc193e),
+     TOBN(0xc671c7a4, 0x30f40e93), TOBN(0x6041aa03, 0x5c51cfa4),
+     TOBN(0x3a713549, 0x2fac25d7), TOBN(0xf5053237, 0x24a7df01),
+     TOBN(0x99efb34a, 0xd29f4ec5), TOBN(0x74810523, 0x71d2cb1b),
+     TOBN(0xacefaf8f, 0xf3a029ab), TOBN(0xc82e4f5a, 0x069d9545),
+     TOBN(0xd759549d, 0xd3341d80), TOBN(0x079e9fa7, 0x31a2a0a4),
+     TOBN(0x75da56c7, 0x2a164f75), TOBN(0x9313ef5a, 0xbeefc182),
+     TOBN(0x0aa365b6, 0xbde130ad), TOBN(0x44265977, 0x98411180),
+     TOBN(0xa65373f7, 0xaa26466a), TOBN(0x1a43bee6, 0x2e2cf404),
+     TOBN(0xe029ed6d, 0xb37a9390), TOBN(0x5c2351ca, 0x34970065),
+     TOBN(0x7c4f3c30, 0x1c46d72c), TOBN(0x09ce770a, 0x7262ce20),
+     TOBN(0x0cfeefad, 0xdd58a9f8), TOBN(0x06797d79, 0x408addaa),
+     TOBN(0x76a87c06, 0x05aed325), TOBN(0xe002b672, 0x8a46d0c6),
+     TOBN(0xcf77ea31, 0x05b6e1a4), TOBN(0x3bf900bc, 0xa5d92b00),
+     TOBN(0x05996d8c, 0xdccfe144), TOBN(0x73d4dfd7, 0x951a602c),
+     TOBN(0x033f3959, 0x0ed8885d), TOBN(0x8332dc73, 0x36400817),
+     TOBN(0x96372295, 0x2d8ebda7), TOBN(0x3fb32cf6, 0xb5da0c67),
+     TOBN(0xcb521d65, 0x3e36defc), TOBN(0xc293d170, 0xa67f00f0),
+     TOBN(0x6a3a2fd4, 0xfb35bd06), TOBN(0x537937dd, 0x0bd490a5),
+     TOBN(0x898d94bc, 0xc274ee5a), TOBN(0xdc70f9bd, 0x7515b5e7),
+     TOBN(0xa94673db, 0x3749900f), TOBN(0x3e6e2af0, 0x49ad3b04),
+     TOBN(0xb9dae1b8, 0x207eecd9), TOBN(0xd3f50d63, 0xec07b47c),
+     TOBN(0x02b4d909, 0x364292da), TOBN(0x919a6df3, 0xfc35975b),
+     TOBN(0xb41ed4aa, 0xb616452e), TOBN(0xe58689cd, 0x5cfc6abb),
+     TOBN(0xeac325d9, 0xf389b025), TOBN(0x45ceb1e6, 0x8f255de5),
+     TOBN(0xda4a0715, 0x5e46cdff), TOBN(0x8a860a55, 0x0f6c761c),
+     TOBN(0xe1395274, 0x5fe1eef1), TOBN(0x256e296a, 0xf7bc535f),
+     TOBN(0xf3d4b06c, 0x2755dd27), TOBN(0x3ced6ee5, 0xbb530c26),
+     TOBN(0x73249ad7, 0x96ba599d), TOBN(0x5de8dab3, 0xe8a66027),
+     TOBN(0xa4892840, 0xc2f97e01), TOBN(0xbe0dbe49, 0x427945be),
+     TOBN(0x6fd86a7b, 0xa57d4e4f), TOBN(0x7f56c3e0, 0x04a2e778),
+     TOBN(0x734708cc, 0xffc13d49), TOBN(0x3c1d9413, 0x788d31fe),
+     TOBN(0xfe85545b, 0x8d3e4c36), TOBN(0xcca441fc, 0x8815129c),
+     TOBN(0x2e2095e2, 0x15e3d172), TOBN(0xc0c8d3c4, 0x64b43e81),
+     TOBN(0x084557ab, 0xc68e802e), TOBN(0xa6b73590, 0x30d239b9),
+     TOBN(0x61ec00a9, 0xb67b0548), TOBN(0x630059de, 0xb8ab138d),
+     TOBN(0x800abf01, 0x36ca9888), TOBN(0xe26d644a, 0x9517149e),
+     TOBN(0x775d5a98, 0x58bf21d9), TOBN(0x00eb6846, 0xdbeab706),
+     TOBN(0x9d714c9f, 0x8232d477), TOBN(0x7cde2c3e, 0xb70f91c2),
+     TOBN(0xe6d0a8ce, 0xe9871f0c), TOBN(0x902bc60b, 0x19e8444a),
+     TOBN(0x8651ed57, 0xff0cd43a), TOBN(0x4418cc07, 0xd480d222),
+     TOBN(0xb5e0c7e3, 0xf3cbe01d), TOBN(0xbf4a899f, 0xe43adcdf),
+     TOBN(0xb89b022c, 0x78f8f79d), TOBN(0x79cbbf97, 0xf42c797c),
+     TOBN(0x46d73cc5, 0x59d53cc1), TOBN(0x99f683e6, 0x4ffca67c),
+     TOBN(0x527c16ec, 0x98865e5b), TOBN(0xc844b70f, 0xf68f8ee0),
+     TOBN(0xcffcccc0, 0xc9854994), TOBN(0x4aafcc15, 0x74926d5d),
+     TOBN(0xeb084832, 0x835aea59), TOBN(0xcb317b5f, 0x20df21cf),
+     TOBN(0x3c45b084, 0xe43d1853), TOBN(0xd12c9411, 0xb93b9167),
+     TOBN(0xb0901982, 0x19316bdf), TOBN(0x76bfa2ac, 0xd11ab5e2),
+     TOBN(0x22bf23cb, 0x4e84d3e9), TOBN(0x96ec9f8e, 0xd1572d4a),
+     TOBN(0x31594ae4, 0x080ba39a), TOBN(0x105b5677, 0xadc6bae4),
+     TOBN(0x501e45dd, 0xa644e230), TOBN(0xeb571f27, 0x64573126),
+     TOBN(0x1fc3d478, 0xa36ac1ef), TOBN(0xbd5fcee8, 0x327c7da7),
+     TOBN(0x1b2b1885, 0x34a70bfe), TOBN(0xcfa421f7, 0xa36345c5),
+     TOBN(0x2f33f4cc, 0x6f322ae9), TOBN(0xdac0bb75, 0x4dabb7a0),
+     TOBN(0xfba35536, 0x923cea0a), TOBN(0xc16f73e5, 0x6d9cb50c),
+     TOBN(0x23216dc6, 0x25812c96), TOBN(0x82945e67, 0x3d7ab424),
+     TOBN(0x829577b2, 0x0796605d), TOBN(0x47fa0978, 0x5026907f),
+     TOBN(0x99701169, 0x2d0f68b2), TOBN(0xa0d24be4, 0xbc1e46db),
+     TOBN(0xcf409c2e, 0x2eb2ac98), TOBN(0x7b59c3c5, 0x97f3ff5c),
+     TOBN(0x2f4576bd, 0x81ed7f02), TOBN(0xe41339e5, 0x10399c22),
+     TOBN(0x562d7744, 0x2ecce0e6), TOBN(0x1afc3869, 0x9a1656c2),
+     TOBN(0x5714820e, 0x86200621), TOBN(0xee36f7b6, 0x566da805),
+     TOBN(0xe6694104, 0x6e5a2a06), TOBN(0xd4390b74, 0x8caabaab),
+     TOBN(0x9db20998, 0x93b0d142), TOBN(0xe1811b81, 0x7926baf3),
+     TOBN(0xd578f2ed, 0x08bc1965), TOBN(0x9a7e31e2, 0x35f00d5d),
+     TOBN(0x3725b65c, 0xc9007327), TOBN(0x157cfe95, 0x29c36f38),
+     TOBN(0xb1c3d0f1, 0x23a521d7), TOBN(0x3e65fb7c, 0xb8a9ae08),
+     TOBN(0xed48bcf9, 0x690b8f78), TOBN(0xe5f46b2c, 0x90d5dfde),
+     TOBN(0x14aebb35, 0x0b6da2b6), TOBN(0x91fef336, 0x7b65ee55),
+     TOBN(0xdb77b57b, 0x1a0a004d), TOBN(0x1c59b628, 0x23aef1f7),
+     TOBN(0xa79c8c89, 0x3ec88d18), TOBN(0x52cca38a, 0x4fde31f1),
+     TOBN(0xe2f64a94, 0xcf4e30b6), TOBN(0x2b4cdbd7, 0x37ff1cbb),
+     TOBN(0xcb542f68, 0x0b566632), TOBN(0xedab69a6, 0x676fae9f),
+     TOBN(0xc4531e0b, 0xc45cb6f0), TOBN(0xf967ec6e, 0xb88fe4a5),
+     TOBN(0x4ab4e645, 0x2919785d), TOBN(0x2dcaefca, 0x7a17b20f),
+     TOBN(0x65c89c05, 0xda7afaa0), TOBN(0x59ea00e9, 0x4dafc6a2),
+     TOBN(0xa6362bf8, 0x8eb43733), TOBN(0xae2dddc1, 0x12011803),
+     TOBN(0xbbf516b1, 0x0bb2aaf8), TOBN(0x9f2627e9, 0xd8de21a3),
+     TOBN(0xaf30439a, 0x43a20b74), TOBN(0xac7e07b0, 0x4ce86408),
+     TOBN(0xc54cdff2, 0x7c10566b), TOBN(0xe3ee0622, 0x6279af0a),
+     TOBN(0x57d09708, 0xf7770f95), TOBN(0x6f0ba223, 0x123e020b),
+     TOBN(0x6c123fb9, 0x6cd41559), TOBN(0xc54f5c65, 0x6fb30f58),
+     TOBN(0x5e168af3, 0xbbf7101c), TOBN(0xf6d6dbdb, 0xce974455),
+     TOBN(0xa001f3b9, 0x88313516), TOBN(0xe6e4a26d, 0xdfb4ac20),
+     TOBN(0x74e7b7fc, 0x506f7dcd), TOBN(0x985e8546, 0x5d037d69),
+     TOBN(0xff00a4da, 0x1ec8d374), TOBN(0x8c339ae3, 0x505b4180),
+     TOBN(0x78bcd4f2, 0x3a5f71c4), TOBN(0x2fb4d99f, 0x67ac3e9f),
+     TOBN(0x7dd25aa6, 0xee65dad1), TOBN(0x2fd63fc2, 0xb62c34dc),
+     TOBN(0xdee42663, 0xf7700192), TOBN(0x9925a206, 0x2c3248e9),
+     TOBN(0x4a55a55d, 0x2ea9f052), TOBN(0xe1d6efcd, 0x16ac67fe),
+     TOBN(0x7f82246d, 0x9bb02415), TOBN(0x2fadbb9b, 0x72cd7a6c),
+     TOBN(0xe977a037, 0x712004dc), TOBN(0xe8c449b2, 0xb3c9f4b9),
+     TOBN(0xa2cb838a, 0x861ea138), TOBN(0xfcbe219a, 0x356ae003),
+     TOBN(0x15c02496, 0x1838504f), TOBN(0x58cef52c, 0x0769d5dc),
+     TOBN(0x7e94ff7d, 0xb3fef999), TOBN(0xf55501e0, 0x04e4fc87),
+     TOBN(0xcdb5fd36, 0xc05430dc), TOBN(0x49872453, 0x778c5cd4),
+     TOBN(0x4c4855ff, 0x1b5e7ace), TOBN(0x89fc6309, 0xb159fe74),
+     TOBN(0xaca00404, 0x3c9ebbe2), TOBN(0x4c030591, 0x866bf867),
+     TOBN(0xa7e8f599, 0x9b18a535), TOBN(0x9203ebfc, 0x5c0a0a44),
+     TOBN(0xbf1b30cc, 0x463207c9), TOBN(0x90b59001, 0x9d135aeb),
+     TOBN(0xedc44d04, 0x794cb3ab), TOBN(0xb3baa475, 0x0ad7be70),
+     TOBN(0xb7d8c7c5, 0x6c09fc91), TOBN(0x2a362d71, 0xf45a5bd6),
+     TOBN(0x36e308c3, 0x8cf3e5a6), TOBN(0x4caf2cd1, 0x0a649c31),
+     TOBN(0xbae328f5, 0xb3c501c7), TOBN(0x2efeca03, 0x83a0eeb3),
+     TOBN(0xc3a27585, 0x7086093a), TOBN(0x78e86515, 0x6d686d83),
+     TOBN(0x18cf3ac1, 0xedf0def6), TOBN(0x2f6a56da, 0x5a1d6cf4),
+     TOBN(0x350c822e, 0x30084873), TOBN(0x82d48087, 0x65843610),
+     TOBN(0xa4e752c1, 0xf393ecd1), TOBN(0xe3034d6d, 0xeeb74f25),
+     TOBN(0x1793727c, 0xb8b0c5c7), TOBN(0xde561ca6, 0x7ec9ce37),
+     TOBN(0xd9eddc50, 0x6190f612), TOBN(0xb52dc77c, 0xca89a191),
+     TOBN(0x990010b2, 0x4bf1e87a), TOBN(0x073136b2, 0x15b91691),
+     TOBN(0x50111261, 0x15546011), TOBN(0x17d48864, 0x0196cb8d),
+     TOBN(0x7ec44104, 0xfd61d824), TOBN(0x213550ef, 0xf088d3db),
+     TOBN(0x5e8d321f, 0xacbbb608), TOBN(0xc317c1f8, 0x39312b64),
+     TOBN(0x7a4a1cd0, 0x27de4329), TOBN(0xbfb33f07, 0xf9b135e4),
+     TOBN(0xcf82b639, 0x59b94480), TOBN(0xca62d957, 0x70b118e6),
+     TOBN(0x95b2ff03, 0x2b1d45fb), TOBN(0x472dd56c, 0x2570686c),
+     TOBN(0x4fbae8a0, 0xd3d50e32), TOBN(0xa31c65dd, 0x65796a08),
+     TOBN(0xe261f6f8, 0x037ce5bb), TOBN(0x3b7816bf, 0xd042073b),
+     TOBN(0x6d0ebbee, 0xbfba45f8), TOBN(0xf2d99979, 0xc9522e36),
+     TOBN(0x707f2a18, 0x77cb5b0c), TOBN(0x954b5a91, 0xdfc02b82),
+     TOBN(0x246b9a55, 0xc20ae04b), TOBN(0xa1486775, 0x9dd79f93),
+     TOBN(0xd4092830, 0xc11f6d8e), TOBN(0x74ca482f, 0x267a4dab),
+     TOBN(0xe3c80bb6, 0x9c58706f), TOBN(0x245f04b7, 0x099154c1),
+     TOBN(0x3a4b25b5, 0xf149259f), TOBN(0xeac735f8, 0x65ccbe91),
+     TOBN(0x260e749f, 0x572788a4), TOBN(0x30b9c736, 0xe34d40cb),
+     TOBN(0x65981d50, 0xf524a17f), TOBN(0x6c462f5d, 0xcddbbefc),
+     TOBN(0x245bfa18, 0xa1e57312), TOBN(0x3b4b003c, 0x46dc8ae0),
+     TOBN(0xb1958797, 0x5d262a35), TOBN(0x83f6e604, 0xffafd8c5),
+     TOBN(0x60843f9c, 0xbc2e0801), TOBN(0x11d85ac1, 0xc783ad3d),
+     TOBN(0x1ce477dd, 0x2e016e43), TOBN(0x2b628f06, 0xfb4a0201),
+     TOBN(0x897b7f62, 0xbf4f77d7), TOBN(0x52e04f22, 0x10277d8a),
+     TOBN(0x17132351, 0x5f3f0d6a), TOBN(0x13c9e064, 0x59a96c4d),
+     TOBN(0xc73892b0, 0x86f05ae8), TOBN(0x94545c8a, 0x4212ad65),
+     TOBN(0x0591b091, 0x3dc4984c), TOBN(0x06845698, 0xf2ec1ca9),
+     TOBN(0xb0e1e1d0, 0xb3ac894b), TOBN(0x962ca1da, 0xa7c915cd),
+     TOBN(0xb0640de8, 0x95331bd5), TOBN(0x2544348a, 0x478c1b6d),
+     TOBN(0x3c3bd415, 0x5647a67e), TOBN(0xd7970ef8, 0x5b20e5fb),
+     TOBN(0xd6e6f6be, 0xe06b4fa6), TOBN(0x5ae29e5e, 0x871390ff),
+     TOBN(0xc7924188, 0x7256daa1), TOBN(0xfae5e501, 0x59f61750),
+     TOBN(0xfac83ece, 0xd1ef1d2b), TOBN(0xa567060c, 0x554736da),
+     TOBN(0x697571f4, 0x1dba8bc7), TOBN(0xd3fc5aeb, 0x553fbcfc),
+     TOBN(0xe665970a, 0x9755fab0), TOBN(0x30fbe8d9, 0xb5537da8),
+     TOBN(0x7a7d0013, 0x97c2b5f0), TOBN(0x9fea5c9c, 0x1b700a02),
+     TOBN(0xcfc0166e, 0xe9a377da), TOBN(0xcc78f3d8, 0xac502375),
+     TOBN(0x803fbbda, 0xba64c3b7), TOBN(0xe53c7d6b, 0x4d70cc42),
+     TOBN(0x6b927bba, 0x5189b7da), TOBN(0x2c86253b, 0x8b05322a),
+     TOBN(0x333e7491, 0xf3869873), TOBN(0x9308348a, 0x4b492879),
+     TOBN(0x39bfa2a8, 0xb9ab0a36), TOBN(0x560f80a6, 0x18f71ac7),
+     TOBN(0xca9b265a, 0x45e24412), TOBN(0x6796bece, 0x8e2ddac3),
+     TOBN(0x87f1eee5, 0x17bfcabb), TOBN(0x624db4d9, 0x195c9bb2),
+     TOBN(0xf7110fcf, 0x2b4db6d2), TOBN(0x41d3fb0d, 0xb432d19d),
+     TOBN(0x3344ea7d, 0x73554a3c), TOBN(0x4c968dad, 0x830a3881),
+     TOBN(0x5df71ad2, 0x687f71ec), TOBN(0x4c4df41f, 0x259cbc07),
+     TOBN(0x8d12d2e0, 0xeb541d72), TOBN(0x94c0dab6, 0xa20fb162),
+     TOBN(0x9bbc2524, 0x1eda0516), TOBN(0x696c924e, 0xdd7871ff),
+     TOBN(0x97efb495, 0x1db84dc1), TOBN(0x7d293ce5, 0x03cbfbf8),
+     TOBN(0x79e25d3e, 0xbc48d007), TOBN(0xc900a580, 0x8591a1ea),
+     TOBN(0xf0277a09, 0xd37508c3), TOBN(0xbf583aa4, 0xe84557bf),
+     TOBN(0x2e258d60, 0xd8228591), TOBN(0xb037e17c, 0x117da3a9),
+     TOBN(0x4b35355e, 0x243d588d), TOBN(0xbe6dfa36, 0xcce2539e),
+     TOBN(0xa57d5823, 0x4843c9da), TOBN(0xe3d91511, 0xf59348fa),
+     TOBN(0xb5d1395c, 0x2791c08f), TOBN(0x04129e5d, 0xf6fdcc93),
+     TOBN(0x635a63ba, 0x0f53087b), TOBN(0x66da6bec, 0xf237612e),
+     TOBN(0xc3d052e5, 0x22755420), TOBN(0xc37a9b47, 0xd7a1bd35),
+     TOBN(0xf19613f3, 0x9b347e02), TOBN(0xee84dbac, 0xbbda7ae0),
+     TOBN(0x603be21d, 0x3a85f2e5), TOBN(0x5f0927c2, 0xff679451),
+     TOBN(0x799013ad, 0x8674f8d7), TOBN(0x17b248d3, 0x00f465e5),
+     TOBN(0x2a29135f, 0x96ca19de), TOBN(0xc8e56e32, 0x957d1844),
+     TOBN(0x935e7eaf, 0xa11a4349), TOBN(0x717308e1, 0x741b73d3),
+     TOBN(0x40477acb, 0x7233a9df), TOBN(0x7a78dac2, 0xd2c83b72),
+     TOBN(0xfb882461, 0x2c5d79d2), TOBN(0x984505fb, 0x76f44fa0),
+     TOBN(0x5cdded16, 0xdfdc4a9d), TOBN(0x4cbea135, 0x3f0ff597),
+     TOBN(0x38daf27a, 0x8a79078e), TOBN(0xb4b0085d, 0xce1bbf0e),
+     TOBN(0xb6b0d8d7, 0x86f19fd0), TOBN(0xe0fdcdae, 0x1778ca6a),
+     TOBN(0x257c7df9, 0x0b26b9b5), TOBN(0x4b82422c, 0x141dcafc),
+     TOBN(0xcf8a2dad, 0x4d3cf148), TOBN(0xf1a4e292, 0x5f17e914),
+     TOBN(0xc40755bb, 0x60de8f64), TOBN(0x412449f8, 0x8718f49d),
+     TOBN(0xdabb9968, 0x8737b6cb), TOBN(0xdd94ae81, 0x6236ea05),
+     TOBN(0xb5223cd0, 0x05c5aca2), TOBN(0x6b81bd33, 0x762210ed),
+     TOBN(0x1f0921db, 0x5d4164df), TOBN(0xf6fdb08f, 0x8d4a35df),
+     TOBN(0x1efcf3c7, 0xc602d4d8), TOBN(0xa2ecd9e6, 0x057f3aa0),
+     TOBN(0x13a6c576, 0xeb4fcba2), TOBN(0x16425bd4, 0x13130559),
+     TOBN(0xa9eac848, 0x416b4968), TOBN(0x617c32a9, 0x2119600e),
+     TOBN(0x1a84eca5, 0x0bb49e40), TOBN(0x2ed98d25, 0xbc2310b3),
+     TOBN(0xad191f88, 0x5edbc719), TOBN(0xd8d667d5, 0x0376ae08),
+     TOBN(0xb855a8ee, 0xf0b4fe29), TOBN(0xc3fe79fb, 0xe75354f7),
+     TOBN(0x1ee9b9e6, 0x403b651e), TOBN(0x99ddbb3c, 0x2baa2c6e),
+     TOBN(0xc6a84c47, 0xeccce37d), TOBN(0x71a05a24, 0x038c9821),
+     TOBN(0x8d32194c, 0x9a6353d8), TOBN(0x14cd3ea6, 0xcf0a1462),
+     TOBN(0x40d70aa2, 0x7bdbe521), TOBN(0x200f0b21, 0x95c80cd8),
+     TOBN(0x4c79dab9, 0x3efdf656), TOBN(0xafa44e4c, 0xa981d8b5),
+     TOBN(0x811b9387, 0xa7111315), TOBN(0x0255a234, 0x7590c35d),
+     TOBN(0xb18e87c0, 0xf1af875c), TOBN(0x0a930b41, 0xced5cc1f),
+     TOBN(0x6ff4fca4, 0x96094a55), TOBN(0x74095b88, 0x6a9dc519),
+     TOBN(0x44492273, 0xafa4894a), TOBN(0x54f16f88, 0xa2e6f56e),
+     TOBN(0xd613fbb4, 0x34485e31), TOBN(0xc716c370, 0xd2464242),
+     TOBN(0x21535837, 0x1644f2e1), TOBN(0x7719474b, 0xbe417c3a),
+     TOBN(0x31bfb158, 0x2045d2be), TOBN(0x10855524, 0xf50e6828),
+     TOBN(0xdb9490ad, 0x98a67af1), TOBN(0x41a34aa6, 0x1c281ff3),
+     TOBN(0x87109ba8, 0xa8bf2580), TOBN(0x70c2e936, 0x2d7eb52d),
+     TOBN(0xefe9fe2c, 0xfb3fc109), TOBN(0xfd3f4d7b, 0x780526bf),
+     TOBN(0x6f9a48d8, 0x9ed0c3bc), TOBN(0x0aec850f, 0x5d8205b2),
+     TOBN(0xa378f8c6, 0x1c6a13ef), TOBN(0xac02f367, 0x9d10e11b),
+     TOBN(0x79c6b396, 0x3b9bbf54), TOBN(0xfb586d71, 0x42779c58),
+     TOBN(0x5d975728, 0x889eecb3), TOBN(0xda2ec867, 0x434537d8),
+     TOBN(0x15a3c9c3, 0x62f31813), TOBN(0xc4b357c8, 0x3c30433e),
+     TOBN(0xf26d281f, 0xc464e972), TOBN(0x99fa49e7, 0x4512ffcf),
+     TOBN(0x456db1b2, 0x725b9753), TOBN(0xec501760, 0xb42941c5),
+     TOBN(0xd822a9d5, 0x7d6d406f), TOBN(0x4bb7a820, 0x7bbcd4d6),
+     TOBN(0x079b1fe0, 0xcc96a5b7), TOBN(0xf83e5755, 0x24aa4901),
+     TOBN(0x317cdd1d, 0x20da7fcb), TOBN(0x487fd706, 0x93b04a81),
+     TOBN(0x43e0671f, 0xe43332ef), TOBN(0x71c5dd5b, 0x441c2218),
+     TOBN(0x4c1d2c1f, 0xe922ba18), TOBN(0x558e9c2f, 0xd619cb67),
+     TOBN(0xd04acde0, 0x1ec51255), TOBN(0x824b3740, 0xaf824507),
+     TOBN(0x62d1b9de, 0x744c6afe), TOBN(0xb99616db, 0xab0d52e3),
+    },
+    {
+     TOBN(0x5ec9c084, 0x7f6a1cda), TOBN(0x68839c14, 0x823d6350),
+     TOBN(0xcbbb678b, 0x03bad007), TOBN(0x6a727255, 0x4788854e),
+     TOBN(0xc747fea2, 0xef5c7294), TOBN(0x74852778, 0x4875e775),
+     TOBN(0xad7b8e8b, 0xaa61a893), TOBN(0x18ff3335, 0x40da98b1),
+     TOBN(0xa51e9f4f, 0x5529ec80), TOBN(0x0420274a, 0x6fd146d1),
+     TOBN(0xbbf1ab66, 0x8e300c2c), TOBN(0x2d0b3a9d, 0x41653fea),
+     TOBN(0x2be2180f, 0x23a495b9), TOBN(0x6ef3c374, 0x5415d73b),
+     TOBN(0x1d3e1ec8, 0xc67ae4fc), TOBN(0xa5839e9c, 0x98d31f5f),
+     TOBN(0xf54114d6, 0x37d77c01), TOBN(0xc2e18a4b, 0x41023c87),
+     TOBN(0x6fa6c3d3, 0x9e6e1221), TOBN(0x9a6cf4e2, 0x410e48f9),
+     TOBN(0xe0881140, 0xb181828f), TOBN(0x17c6df29, 0x78cb7833),
+     TOBN(0xc1eb8df1, 0xa7cd2367), TOBN(0xb78f1c8d, 0xca89f922),
+     TOBN(0xf25d4777, 0xd0d42887), TOBN(0x4b489218, 0x2b7a2707),
+     TOBN(0x1b4dbf9b, 0x2d3966fe), TOBN(0x4bac5f48, 0x41ae2bec),
+     TOBN(0x68db2733, 0x1733964e), TOBN(0xa10c5dff, 0x6a814a69),
+     TOBN(0x84ebdaf0, 0xa9898348), TOBN(0x60e46823, 0xa74da3f4),
+     TOBN(0x452b6b1d, 0x93420649), TOBN(0x9dd6452b, 0x6ed5d7f6),
+     TOBN(0x4a9b8fa1, 0xe687b577), TOBN(0x1e203166, 0x854c49d7),
+     TOBN(0xf523667e, 0xa45feba8), TOBN(0x9ecb4d44, 0x5f9f4a56),
+     TOBN(0xb8655a5f, 0x7fb1c641), TOBN(0x5516401a, 0x87c26201),
+     TOBN(0x24677754, 0x0d2face6), TOBN(0xd9f7da7f, 0xa8ade59c),
+     TOBN(0x27e3ad77, 0x7fa7df06), TOBN(0x35a4caf0, 0xf60395ad),
+     TOBN(0xfaef231c, 0xe4e701ac), TOBN(0x9e135976, 0x23755489),
+     TOBN(0x7caa73ab, 0x43554ad3), TOBN(0x9d8554d9, 0x94f0d878),
+     TOBN(0xe42040ce, 0xa85b81d5), TOBN(0x4d28aca7, 0x40fa9631),
+     TOBN(0x076fed3d, 0x7e04b755), TOBN(0xdde3d347, 0x1129ce4c),
+     TOBN(0x77f785d7, 0x1179af95), TOBN(0x4782f842, 0xf74e0672),
+     TOBN(0xbd068cc1, 0x0b4597cb), TOBN(0x3d6d4b2a, 0x8f4c65b7),
+     TOBN(0xe0642d18, 0xf9066d73), TOBN(0xbe1d2ec3, 0xa098b3bf),
+     TOBN(0xefee860c, 0x21b4954c), TOBN(0x4d7c4e6d, 0x27b629bb),
+     TOBN(0xcd8f1e03, 0x8e8b81b0), TOBN(0x4a80168e, 0x7fe77eb0),
+     TOBN(0x4d977591, 0xce247c73), TOBN(0x9b30c9f2, 0x857e0356),
+     TOBN(0xc02495ba, 0x2940e9de), TOBN(0x357299f5, 0xb6d2b72c),
+     TOBN(0x132b4c63, 0x06a9c2e4), TOBN(0xe90a90c5, 0x084d8c67),
+     TOBN(0x0f0c9e94, 0xace1b471), TOBN(0x769457e1, 0xf1e3d8f6),
+     TOBN(0xc4c30ce3, 0xd71118c6), TOBN(0xdb5fd8d6, 0x6b652a3d),
+     TOBN(0x090df107, 0x4def5978), TOBN(0x1abcfa32, 0x2d8a5f3a),
+     TOBN(0x2976b012, 0xa34b70db), TOBN(0x90f541d4, 0xfa5e75b9),
+     TOBN(0x50c991a9, 0x37a6e9a0), TOBN(0xf51e8693, 0x903bffda),
+     TOBN(0xa2697ab4, 0x8d344776), TOBN(0x77134fe8, 0xe34a7850),
+     TOBN(0x723e5d3d, 0xa72597ac), TOBN(0x4a49847a, 0x4269aff7),
+     TOBN(0x75ad9088, 0x443b8db6), TOBN(0x9b7d00d5, 0xa51d80a1),
+     TOBN(0xce1c7049, 0xe5e04ac2), TOBN(0xb8c2793c, 0x2a792bde),
+     TOBN(0xde9220a0, 0xe410e175), TOBN(0x4b3a9b85, 0x9401bc2a),
+     TOBN(0xc7eaf2c5, 0xf037d15f), TOBN(0x410b627e, 0xc7afbf8b),
+     TOBN(0x243cdb79, 0xd7bedf50), TOBN(0x04813b51, 0xbe6512d0),
+     TOBN(0x2fb77cab, 0x26beca2f), TOBN(0xbb601975, 0x7baa3099),
+     TOBN(0x8c327e59, 0x40bda4d0), TOBN(0x85b9c764, 0x13c23444),
+     TOBN(0x26960d9c, 0x08ed59d8), TOBN(0x9b76dced, 0x4a72854d),
+     TOBN(0xca2f579a, 0xfdc3b7f5), TOBN(0xac27028a, 0x6cae8b4f),
+     TOBN(0x48fd1a49, 0x42326aa5), TOBN(0xb95fdb4f, 0x5759c63f),
+     TOBN(0x27655358, 0xe0a96abf), TOBN(0x26d38b64, 0x36ed53b0),
+     TOBN(0x03cfdd49, 0xfc6d1f3e), TOBN(0x20af5886, 0x15adaba0),
+     TOBN(0x74c6c943, 0x754dd268), TOBN(0xe7d52cdf, 0x7977717e),
+     TOBN(0x9a81d440, 0x3b414dd2), TOBN(0x697c7b4a, 0xd790a4c7),
+     TOBN(0xb1b7735f, 0xedbce1f2), TOBN(0xbd90e63f, 0xbefa7210),
+     TOBN(0x2e2b0dad, 0x7ab207d1), TOBN(0x89abbd83, 0x9b373211),
+     TOBN(0x45d34ebc, 0x8e35e2bb), TOBN(0x67ba3ac5, 0x064856f6),
+     TOBN(0xb5527dbe, 0xa52c7676), TOBN(0x906fb217, 0x71294012),
+     TOBN(0x65fca552, 0xab305260), TOBN(0x89ac52a3, 0x14ee193b),
+     TOBN(0x673aead4, 0x88c06b1c), TOBN(0xea8af420, 0x49d9d4e8),
+     TOBN(0xa7b4409a, 0xcb9e86bf), TOBN(0x49f76f71, 0x5414aa56),
+     TOBN(0x6603c801, 0x8c13857a), TOBN(0x7c26f1c2, 0xce742384),
+     TOBN(0x042fb224, 0x2a665719), TOBN(0x2619f254, 0xe175b0c6),
+     TOBN(0x5b3b71ea, 0x7c092397), TOBN(0xd9087023, 0xf18c29ae),
+     TOBN(0x48dbecbd, 0x2008841d), TOBN(0x658c998e, 0x22622bba),
+     TOBN(0x38a2cc6d, 0x578e463f), TOBN(0x7002380f, 0xcbb88796),
+     TOBN(0xc545baff, 0x71695101), TOBN(0x064a0500, 0xce65b49c),
+     TOBN(0x3651d926, 0xb1ae0398), TOBN(0x33c9ea8f, 0x4ace0e86),
+     TOBN(0x481fab1b, 0x1a6debd7), TOBN(0x65b58a79, 0x4d365031),
+     TOBN(0xb73ec84b, 0x811e8f01), TOBN(0xb6aa3955, 0x51342ef2),
+     TOBN(0xdbce3d9f, 0x9efcdbcc), TOBN(0x5791b35f, 0xcfbf2a4f),
+     TOBN(0x67024158, 0x6eaad1f0), TOBN(0xe8dbaa88, 0x0063ae25),
+     TOBN(0x6d2051cc, 0x9fedc144), TOBN(0x136c2ab1, 0x18b5e86d),
+     TOBN(0x3b2d3d63, 0xc89241d4), TOBN(0x843cfa3d, 0x4a82dec6),
+     TOBN(0x64fa5860, 0xf0a5f163), TOBN(0x2d9b6095, 0x1ae3be83),
+     TOBN(0x75f97753, 0xb01a91e5), TOBN(0xd374dfa2, 0xcd0d8cac),
+     TOBN(0xe5dbffef, 0x8eb72ba0), TOBN(0x61049807, 0xd7b8a624),
+     TOBN(0x9c8b5e93, 0xa39277d3), TOBN(0x6e5ba593, 0x3b1cc635),
+     TOBN(0x8bd0a69e, 0x21cde059), TOBN(0xd0a19b53, 0x071ec0c8),
+     TOBN(0x8c87785a, 0xd1bb088d), TOBN(0xd801d5a6, 0x7e249c39),
+     TOBN(0x002ee598, 0x8688914f), TOBN(0x52b014fc, 0x6b68413d),
+     TOBN(0xaf1d7e88, 0x507946df), TOBN(0xa38e436f, 0x84ccebf1),
+     TOBN(0x37d9b946, 0xaa86a4b6), TOBN(0x55da0db6, 0xc506a394),
+     TOBN(0x856928c3, 0x02b900bd), TOBN(0x9eb926a3, 0x7bc6a67b),
+     TOBN(0x2f4d392d, 0xd0f39446), TOBN(0xb12f2761, 0x01c49daa),
+     TOBN(0x07b8d23f, 0x13874ac7), TOBN(0xa473ef4c, 0x1efaa157),
+     TOBN(0x550765f6, 0xdf8cf2ab), TOBN(0xeba88504, 0xd23d3750),
+     TOBN(0xf05791d4, 0x2434fa2e), TOBN(0x8c0899c3, 0x4e2a05ea),
+     TOBN(0x40a53bdd, 0x898bc9b0), TOBN(0x6c255f6f, 0x40c8bf7c),
+     TOBN(0x203db8c5, 0xe164b910), TOBN(0x070baaee, 0xc1c4de69),
+     TOBN(0x89660629, 0x5df5c0a7), TOBN(0x0b9c2f4b, 0xdb364b99),
+     TOBN(0x012c6994, 0x44bb5a79), TOBN(0xf5928e0c, 0x9bd1fdc0),
+     TOBN(0xd30b8a97, 0x3ce49191), TOBN(0x52792b85, 0xe3a05dd3),
+     TOBN(0x0da08916, 0x1d3d69c3), TOBN(0x931759e8, 0xed59a28d),
+     TOBN(0x412148d9, 0x6ca05485), TOBN(0xb1517aa0, 0x3d6e9964),
+     TOBN(0x15204ba9, 0xde75812d), TOBN(0x49e377e0, 0x5698b03f),
+     TOBN(0xe7790d41, 0x05c9072e), TOBN(0xf79adbed, 0xdba28e80),
+     TOBN(0x6aad9f96, 0x4644840d), TOBN(0xc3f3d032, 0x2e0a695b),
+     TOBN(0x3eb739d2, 0xaa4aa737), TOBN(0x45c6b665, 0x37d8d520),
+     TOBN(0xc3ba2408, 0x9917cb85), TOBN(0x1c729ffb, 0xd7bf6304),
+     TOBN(0x56b9935e, 0xcc160245), TOBN(0x42379567, 0xe03cb227),
+     TOBN(0x2dc20028, 0xb66bfc5d), TOBN(0xfaf7d224, 0x95de8ed3),
+     TOBN(0xa7541158, 0x3214024e), TOBN(0x2f7755d8, 0x50aabdb6),
+     TOBN(0xb74ac27b, 0x7ea9b93a), TOBN(0xc1c5a8fe, 0xa2e0516c),
+     TOBN(0xe9f4f222, 0x6b64f56f), TOBN(0xf3c0c7fb, 0x8fbc4a64),
+     TOBN(0x43ac0ac2, 0xa16edc23), TOBN(0x0e26e4ad, 0x6d086e9e),
+     TOBN(0x5b8ef949, 0x5bc0961f), TOBN(0xa0d16d39, 0xd2b77c29),
+     TOBN(0x50b43efa, 0x78845d09), TOBN(0x3899e1be, 0xcb3acdd9),
+     TOBN(0xa93a28e3, 0x18d4ec31), TOBN(0x18a4eeed, 0x0a66fe47),
+     TOBN(0xd7a7bf46, 0x87333831), TOBN(0xbbf5c1a8, 0xdbe14699),
+     TOBN(0xf2a3da73, 0x80b9c9d0), TOBN(0x133c138a, 0x82bceb4e),
+     TOBN(0xcfd4b885, 0x335a923a), TOBN(0xf9b69b3f, 0x8fc82f3b),
+     TOBN(0x08908b60, 0x8784c35c), TOBN(0x76bf1082, 0xd843b66e),
+     TOBN(0x1ba730bf, 0xbb57a641), TOBN(0x3bb4a8d7, 0x34e9f095),
+     TOBN(0x0342d32b, 0xc28d5414), TOBN(0x8fb13cbf, 0xcfd99e1a),
+     TOBN(0x3845e507, 0x1d02f47c), TOBN(0x4d77af89, 0x14ef0b26),
+     TOBN(0x93454480, 0x5ef578d9), TOBN(0x23138c57, 0xbdc408ec),
+     TOBN(0xdac833ed, 0x47cf528a), TOBN(0xd18e9865, 0x29d7cf20),
+     TOBN(0x93208743, 0xcdc8e55a), TOBN(0xbfe570c8, 0x724025a0),
+     TOBN(0xb75c3de0, 0x3aee838e), TOBN(0x29304886, 0xe0f21f23),
+     TOBN(0xe46792ab, 0x82791daf), TOBN(0x3d798d92, 0x3f124394),
+     TOBN(0x2446dc81, 0x29a6fb5e), TOBN(0x2446e5b3, 0xbd68c23a),
+     TOBN(0xe1b5c76d, 0x689b1176), TOBN(0x3fb66661, 0x9a852082),
+     TOBN(0x8d6fbcc7, 0xd9b45206), TOBN(0x00ab735d, 0xeabc4640),
+     TOBN(0x428c7017, 0x810e37d1), TOBN(0xa4365872, 0x27af5718),
+     TOBN(0x8f195823, 0x0a910146), TOBN(0xc13ccdd7, 0x0ff76704),
+     TOBN(0x59d34ad6, 0x44d6f1c8), TOBN(0xd3dfa6b2, 0x795b61b4),
+     TOBN(0x1ec08010, 0x12eea439), TOBN(0xafbbea32, 0x7b2cd52a),
+     TOBN(0x99428f9a, 0x68cfe98b), TOBN(0x4ff9a5bc, 0x95628fe7),
+     TOBN(0x212baeb7, 0x7ac41e9a), TOBN(0x595cf03f, 0x29206e86),
+     TOBN(0x4b62a429, 0x733f37c4), TOBN(0xa1fac4ae, 0x4d3cb6a6),
+     TOBN(0x2d6cb0e6, 0x1aed3c45), TOBN(0xf6703493, 0x4e6da48d),
+     TOBN(0xa0036fb4, 0x2d13f9c1), TOBN(0x7db5078a, 0x7fe3ea2e),
+     TOBN(0x152a1fc0, 0xd5992163), TOBN(0xd63270e9, 0x744b44ff),
+     TOBN(0x56730292, 0xf177c075), TOBN(0x470f5e72, 0x17c3e08c),
+     TOBN(0xbf53d223, 0xecb927f5), TOBN(0xc80fbc1b, 0x629e8aa1),
+     TOBN(0xed59f186, 0x24d72477), TOBN(0xc266f5a6, 0x38811583),
+     TOBN(0xc6f37bc1, 0x7c404560), TOBN(0xd58c10e5, 0x0c5b68e9),
+     TOBN(0x696de793, 0x916e8f3c), TOBN(0x7298af8e, 0x56a7781f),
+     TOBN(0xaf063553, 0xb16679d5), TOBN(0xa509f449, 0x4316ed7e),
+     TOBN(0xe3d6ec43, 0xb53cc0e2), TOBN(0x9e957ce0, 0x16ba34cd),
+     TOBN(0x2b0c7fbc, 0x7857d80d), TOBN(0xc2c671fe, 0x3daffbf3),
+     TOBN(0xebcbf012, 0x0d251d41), TOBN(0xedcfe7f7, 0xffef45f5),
+     TOBN(0xf5b66555, 0x334a1734), TOBN(0x4354ccfa, 0xe505f4bb),
+     TOBN(0x6ee0b5b9, 0x52a59260), TOBN(0xb7bb64c1, 0x5a699a93),
+     TOBN(0x85e34c0e, 0x6de84422), TOBN(0xca9bacfe, 0x8bbe0560),
+     TOBN(0xa08c780f, 0x952a52d2), TOBN(0x0e794b05, 0x3919176b),
+     TOBN(0x8a496598, 0x154d282d), TOBN(0xb2999dc4, 0xdc34508c),
+     TOBN(0xfc304fe3, 0x9db4410a), TOBN(0xbc09aee4, 0xe1bc07c8),
+     TOBN(0x1d2f0147, 0xef6d497d), TOBN(0x3b9e06e0, 0x96488fc1),
+     TOBN(0x37635d04, 0x34cb97a7), TOBN(0x9a294b89, 0x8757f955),
+     TOBN(0x38c568ac, 0x59508819), TOBN(0x854370fc, 0x46e15b82),
+     TOBN(0x9f676404, 0xee57f0b4), TOBN(0x268854cc, 0x8f45319c),
+     TOBN(0x4256d25c, 0x63746274), TOBN(0x0a553821, 0x0496cf9c),
+     TOBN(0xb6bf27de, 0x15e2fc17), TOBN(0x6848f83a, 0x99bd538a),
+     TOBN(0x00e15d0a, 0x1685e460), TOBN(0x6fae8b37, 0x155d00b6),
+     TOBN(0x277126d8, 0xdc561456), TOBN(0x331c02e5, 0x6bf70c63),
+     TOBN(0xc9b7da4e, 0x515f39b7), TOBN(0xb7e0d135, 0x966c2060),
+     TOBN(0x9a801457, 0xc401f926), TOBN(0xcc560825, 0xffb0137e),
+     TOBN(0xbcfac5f8, 0x5c7e38fc), TOBN(0xd542c1a4, 0x174e97ba),
+     TOBN(0xbea67b1e, 0x0bb507b8), TOBN(0xf008cc2c, 0x3b782fd8),
+     TOBN(0x865834da, 0x0aa329bc), TOBN(0x0fd746f2, 0x2b6db70a),
+     TOBN(0x8e72e5f7, 0x65fbe439), TOBN(0xac23881d, 0x005295ee),
+     TOBN(0xc2c45fef, 0xad9d013c), TOBN(0x0df74277, 0x71c311f9),
+     TOBN(0x69caf967, 0x6bb32b66), TOBN(0x9fbd32ff, 0xb8e4a3e5),
+     TOBN(0x39d94e31, 0x78c0c439), TOBN(0x7489a8f0, 0xffa4b625),
+     TOBN(0x59af0ec3, 0x8aac717c), TOBN(0xdd3b470e, 0xa12d996f),
+     TOBN(0x6d60cb97, 0x8da3fef0), TOBN(0x5164d722, 0x044d64fc),
+     TOBN(0xefe06ead, 0xfc21305b), TOBN(0x72b4c45e, 0xceed89c1),
+     TOBN(0x072cf1dc, 0x8cabf0df), TOBN(0x0a0d7c0c, 0xa5371d3e),
+     TOBN(0xb13ba707, 0x2ae831d5), TOBN(0x7702c3c5, 0x269f189e),
+     TOBN(0xfb8e903e, 0xc8239fe7), TOBN(0x5805c2ef, 0x524f213c),
+     TOBN(0xdf056e45, 0x70645f7f), TOBN(0xfe10ecfb, 0x454c4577),
+     TOBN(0x422126da, 0x990dc567), TOBN(0x95a5d753, 0xbf55cd81),
+     TOBN(0x2705a00c, 0x8c2688ed), TOBN(0xd079ecb4, 0x2f8f91af),
+     TOBN(0x8cd13fa0, 0x2b69a2c8), TOBN(0x7b0f310a, 0x36b657b8),
+     TOBN(0xa7247cfd, 0x251c595b), TOBN(0xda352dc8, 0x5a36e4b1),
+     TOBN(0x588d2e88, 0xf43312de), TOBN(0xef80a48f, 0xdb9f6dec),
+     TOBN(0x39583634, 0x3fb2d6e3), TOBN(0x0fbfa769, 0x5a46bc46),
+     TOBN(0x3570a3f2, 0xfe701598), TOBN(0xd1d0d091, 0xac815fbb),
+     TOBN(0x4d7bfadd, 0xd7f2b1b2), TOBN(0x509298d4, 0x66496326),
+     TOBN(0xb7accafc, 0xcad9fb50), TOBN(0xcdbcb762, 0x9c592dee),
+     TOBN(0xfe47a3b1, 0x6888482a), TOBN(0x312be210, 0xe8b8c133),
+     TOBN(0xc474b07f, 0x00167f93), TOBN(0x19457000, 0xa637f35e),
+     TOBN(0x3eafa14e, 0x5005d8a1), TOBN(0x2a84723a, 0xadf25f29),
+     TOBN(0x2c9d7ebb, 0xa741cf9e), TOBN(0x94024dc2, 0xc3913acf),
+     TOBN(0xac2db91d, 0x97b98f1f), TOBN(0xfb9a0502, 0x46a7bf92),
+     TOBN(0x8874ffb5, 0x6487a5d4), TOBN(0xc02a12b5, 0x2f53e25f),
+     TOBN(0x38654a57, 0x416ba8fc), TOBN(0x226356f2, 0x0c0b25d6),
+     TOBN(0x34f2eaa6, 0x6030f2ac), TOBN(0xb788baa1, 0x9cea9176),
+     TOBN(0x66fbe9f7, 0x4e912104), TOBN(0x982ef71d, 0x39a69e3d),
+     TOBN(0x9f361d17, 0xbbe5733a), TOBN(0xc79569a0, 0x1988f31e),
+     TOBN(0xf2b96ecb, 0x9e0f52fe), TOBN(0xc78e44dc, 0x80235136),
+     TOBN(0x96053ab5, 0x8462ef4f), TOBN(0xf83c1f6d, 0x81506701),
+     TOBN(0xc7313eb1, 0xa65c09e9), TOBN(0xf5dfaa4a, 0x4efcf558),
+     TOBN(0x8b4819e4, 0xe65ede91), TOBN(0x5a5824ba, 0x6dc0a533),
+     TOBN(0x89d18b20, 0xb4c930f8), TOBN(0xaad7a5d8, 0xfcefa378),
+     TOBN(0x2ef790c2, 0x298dba63), TOBN(0x3e4b31b6, 0xe90c322f),
+     TOBN(0xa257bb81, 0x52ce2ee4), TOBN(0xb8c2966e, 0xd39c36bb),
+     TOBN(0x13954df8, 0x487719c7), TOBN(0xcb0f7ae5, 0x791b00e7),
+     TOBN(0x367a1cad, 0xc8d21faf), TOBN(0x44dd204d, 0x3fbd8a7c),
+     TOBN(0x778fdb56, 0x5f67ec30), TOBN(0xfb288790, 0x5de5caeb),
+     TOBN(0x310b4d56, 0xca53300c), TOBN(0x37dbb7c4, 0x325c54b1),
+     TOBN(0xc80c83a4, 0xfe771ef7), TOBN(0xe212050f, 0x1c1c1b92),
+     TOBN(0x0f12bb88, 0xf09c666f), TOBN(0x8ec5f396, 0x10a2eca2),
+     TOBN(0xdaf96996, 0x90a22eb7), TOBN(0xeb77eee5, 0x450de941),
+     TOBN(0x13823c58, 0x58fb0165), TOBN(0x2157ba6e, 0x31272111),
+     TOBN(0x110ee33e, 0x2b4f9e7e), TOBN(0x7e1b550b, 0xf682d48f),
+     TOBN(0x8fd8c6c1, 0x3e17cb9b), TOBN(0x91cfbcf7, 0xe1843894),
+     TOBN(0x5fc64346, 0x2917b1c7), TOBN(0x06f56d0f, 0xba86d14a),
+     TOBN(0xb8874d88, 0xaf219f21), TOBN(0xf8803b37, 0x11ab8b0b),
+     TOBN(0x7e63cf63, 0xbe12841e), TOBN(0x9c9cc421, 0xbc90765a),
+     TOBN(0x0264a597, 0x1084fa84), TOBN(0xce260a60, 0x252a9bbe),
+     TOBN(0xfaff225c, 0x2fefa4f2), TOBN(0x02b900ad, 0x05bd09b0),
+     TOBN(0x631e5cfb, 0x11b1b81c), TOBN(0x4d93de46, 0x0a193140),
+     TOBN(0xd92a710a, 0xe3173750), TOBN(0xd712d3a1, 0x671a3833),
+     TOBN(0xbc9caad1, 0x4116e26b), TOBN(0xeb24f658, 0xa72fbd71),
+     TOBN(0x3986a207, 0x9055f802), TOBN(0x212446f8, 0xe2707793),
+     TOBN(0x602541d6, 0x1721b395), TOBN(0x4099a2e6, 0xb07160c2),
+     TOBN(0x765390f6, 0x2369ce91), TOBN(0x2dc37639, 0x5754d219),
+     TOBN(0xbc552369, 0x7c018afb), TOBN(0xca835077, 0x35bf6b66),
+     TOBN(0x61b83e43, 0x61d4b0a6), TOBN(0x8f87f597, 0x27cf66c5),
+     TOBN(0xace57840, 0x9357cbf2), TOBN(0x24834481, 0xabe47fb7),
+     TOBN(0xa434c950, 0xdb3c6e47), TOBN(0x1f479519, 0xaa1da775),
+     TOBN(0x338c9cd2, 0xf14f9d5e), TOBN(0x4666ce7e, 0x1e75f72e),
+     TOBN(0x4fce4d95, 0xe56564e5), TOBN(0x0db55ed5, 0x89e0ff6f),
+     TOBN(0x88796e85, 0x00190b73), TOBN(0xfdf6492a, 0x454e31d0),
+     TOBN(0x30cb3fbe, 0xb9ed6e3b), TOBN(0x5c796282, 0xde8f0544),
+     TOBN(0xe11b36bd, 0xb6af89bc), TOBN(0x0a91cf73, 0xec439d95),
+     TOBN(0xbbe74a5e, 0x0a93fe1c), TOBN(0xcf1f376f, 0xa5d75083),
+     TOBN(0x6718bce5, 0xf7725460), TOBN(0x6654d7b1, 0xa316d17f),
+     TOBN(0xdaa925e5, 0x0393aa3b), TOBN(0x81217e18, 0x9446cdbd),
+     TOBN(0x07708483, 0xa7afc408), TOBN(0xa4c76c4f, 0x44709dfe),
+     TOBN(0x72557d71, 0x3a1c412b), TOBN(0xeb4c2648, 0xb49b0e1c),
+     TOBN(0xcdd24b77, 0xe4d6c002), TOBN(0x3384ea5b, 0x77113e0d),
+     TOBN(0x906fb748, 0x6a10a9ea), TOBN(0x46cda42e, 0x0a3b0e89),
+     TOBN(0x10b9096d, 0x7ae4ad43), TOBN(0xe1f23996, 0x3bf2afea),
+     TOBN(0xcb50b941, 0x0dd82d19), TOBN(0x008e593c, 0x832d93a0),
+     TOBN(0x0b1fb0e6, 0xd86a71e0), TOBN(0x75f2aa6b, 0xb1730860),
+     TOBN(0xed5d4d7c, 0x7efc480a), TOBN(0x500b9d8c, 0xc76c64de),
+     TOBN(0x28904003, 0xec4fc026), TOBN(0xe41b3f23, 0xdec8b315),
+     TOBN(0xa9b5caff, 0x70c06860), TOBN(0x5cb9a4d1, 0x28343b2d),
+     TOBN(0xec157abd, 0x9986a0c3), TOBN(0xbcad3bc6, 0xb5fc67e9),
+     TOBN(0x6e64dd26, 0x13aa9c17), TOBN(0xa347c4a2, 0x271aef54),
+     TOBN(0x47b26cb9, 0x883d90bf), TOBN(0xe84d9c6a, 0xe1c412c9),
+     TOBN(0xd2eacc10, 0x1c67439f), TOBN(0xd7797bb3, 0xc61b2b5d),
+     TOBN(0x0deda652, 0x8ebdb4be), TOBN(0x9e04455d, 0xac3fc2f4),
+     TOBN(0xbbfc6e69, 0x27c86688), TOBN(0xf7cf2947, 0xa1715a33),
+     TOBN(0xe047a3e3, 0x47bc6409), TOBN(0xefeb573a, 0x6f2a5b28),
+     TOBN(0xbf3ea3af, 0xd105ba3b), TOBN(0x5f01b4c2, 0x426c6482),
+     TOBN(0x778a5240, 0x968390b1), TOBN(0xc9c71625, 0x72bcf6a6),
+     TOBN(0x698ec2c9, 0x6fd8b309), TOBN(0x512ea17a, 0xa055809f),
+     TOBN(0x28cb44e7, 0x8822943c), TOBN(0x434dc709, 0xdeb7d3e6),
+     TOBN(0xb8b324d1, 0x1be76434), TOBN(0x7cf24ed3, 0x382ff0b1),
+     TOBN(0xda8265fe, 0x2905e726), TOBN(0xd57b3915, 0xee6c3abc),
+    },
+    {
+     TOBN(0xf9010095, 0xd2a819b5), TOBN(0x5291aaf9, 0x48f2f653),
+     TOBN(0xfa533907, 0xf0afe366), TOBN(0x88a58ecf, 0x8e279e27),
+     TOBN(0x0f077127, 0xfae130bc), TOBN(0xee9ccf1a, 0xf8a54c75),
+     TOBN(0x38a6783e, 0xbed82b6a), TOBN(0x9a1acb3d, 0xed414524),
+     TOBN(0xe4e53cee, 0xd9c12e2a), TOBN(0x11983fc1, 0x7fc1308f),
+     TOBN(0x3eb4d84d, 0x892c2d0f), TOBN(0xa0bfc1ca, 0x74499723),
+     TOBN(0x708344d9, 0x0145176b), TOBN(0xbb2988e0, 0x6f12e75b),
+     TOBN(0xdf73cead, 0xada67545), TOBN(0xf37069d1, 0x2bb8f989),
+     TOBN(0xa24a35e6, 0x9cc17f65), TOBN(0xc49b3e9a, 0x89d9abe0),
+     TOBN(0x82f40303, 0x2fc09ae3), TOBN(0xbffe7d4d, 0x002cc587),
+     TOBN(0x5511f4e6, 0x424ef713), TOBN(0xb86bf654, 0xa658f660),
+     TOBN(0x623388d9, 0x1c8baea2), TOBN(0x60664a71, 0x33656759),
+     TOBN(0x18996198, 0xd8447e16), TOBN(0x17195d76, 0x662171dd),
+     TOBN(0x28cfe6a1, 0xf448b8e6), TOBN(0x8a3c2823, 0x0658c923),
+     TOBN(0x0c548d89, 0x9c35e852), TOBN(0xadf1cd2f, 0x2b378157),
+     TOBN(0x999e41af, 0xf30113b6), TOBN(0xf87515a5, 0x9cf4696f),
+     TOBN(0x6c332c55, 0x9778aa8e), TOBN(0x290ae3ea, 0xd1b8d8b2),
+     TOBN(0x3e2bfa0b, 0xbf533883), TOBN(0xe48e3747, 0x1a523ee2),
+     TOBN(0x4d40f1d5, 0x50fde3ed), TOBN(0xb57e695b, 0x48710433),
+     TOBN(0xa4101258, 0x1b241f3f), TOBN(0xa0cabf7b, 0x3042cabc),
+     TOBN(0x68eb19c7, 0xedfea522), TOBN(0x68e028b8, 0xa400db7b),
+     TOBN(0x6cd97bf7, 0xa8f03a08), TOBN(0x09f4d266, 0xf442fe36),
+     TOBN(0x1ac77f91, 0x5d713a1f), TOBN(0x356e3a35, 0x2f58ccb5),
+     TOBN(0x31d657f1, 0xf8ddc47d), TOBN(0xfea7aede, 0x90092115),
+     TOBN(0x2aeba1d2, 0x4ad49f66), TOBN(0x16ff2bad, 0x9d40861b),
+     TOBN(0x25464f37, 0x7da225f5), TOBN(0xa2fe66cc, 0x1ffc3f0b),
+     TOBN(0x74074d7f, 0xac757f41), TOBN(0x5c85d3d1, 0xcd0a2c20),
+     TOBN(0xccda2a05, 0xc974b266), TOBN(0x5c2e2511, 0xcc10a04f),
+     TOBN(0x01ea2535, 0x510d515f), TOBN(0xc861c54c, 0x489e7856),
+     TOBN(0x9bc8485b, 0x680d17bc), TOBN(0x71472c11, 0x819ccc86),
+     TOBN(0xa7ef9485, 0x0e9b5d8b), TOBN(0x698c9fe8, 0xd029720d),
+     TOBN(0x6ce987d1, 0x61f50161), TOBN(0x035f6f32, 0x9d240bf6),
+     TOBN(0xe7c03c9d, 0x44ec2bed), TOBN(0x0bc4f4a2, 0x76cf95c5),
+     TOBN(0x0722d57c, 0x88f014ee), TOBN(0xae406348, 0x76fa941b),
+     TOBN(0x046424df, 0x23ee068d), TOBN(0xd30b6530, 0xe8c130c5),
+     TOBN(0x17b69098, 0x554f149d), TOBN(0x887e04f7, 0x92f95b71),
+     TOBN(0x414e7371, 0x941c1244), TOBN(0x1d48fe53, 0x94f1da50),
+     TOBN(0xc18bcd89, 0x6519802a), TOBN(0xfae7c2d8, 0x48925019),
+     TOBN(0x0f311ddf, 0xf2ece2af), TOBN(0x7e8e0e08, 0x0a779f79),
+     TOBN(0x47daa5f9, 0xb6207944), TOBN(0xf29dc331, 0xefd08d6e),
+     TOBN(0x9c096e19, 0x23e48f60), TOBN(0xbcc6fe53, 0x8dd36f0c),
+     TOBN(0x452e60f9, 0xbb86a9ca), TOBN(0xad35f732, 0xed16cf06),
+     TOBN(0xcbdd01a2, 0x2bf445f7), TOBN(0xb7848e94, 0xf60ce893),
+     TOBN(0x5e65e8ca, 0x2939a977), TOBN(0x304ebedc, 0x63cfa5e4),
+     TOBN(0x79bae721, 0x252cc655), TOBN(0xa3b9a4e5, 0xc4743792),
+     TOBN(0xf32dcfeb, 0x36fdba1c), TOBN(0xadbd0c1f, 0x7ac19885),
+     TOBN(0xefb4fb68, 0xdc42a2cd), TOBN(0x78b1ca37, 0x2289a71f),
+     TOBN(0x7e1f70fe, 0x87fc6df4), TOBN(0x8d024301, 0x90a9faec),
+     TOBN(0x37c08672, 0x46cd4141), TOBN(0x3c0fed17, 0x1a60d8e5),
+     TOBN(0xab18bf06, 0x0f56fea1), TOBN(0x879ee748, 0x372e757b),
+     TOBN(0x84b19b80, 0x1d280206), TOBN(0xa40d7ce3, 0xd96ac240),
+     TOBN(0x5d493fb1, 0xfea42ebc), TOBN(0x9a5fdafd, 0x40d79bbd),
+     TOBN(0x790c0b30, 0x383b371c), TOBN(0x6dae5df9, 0x676f8168),
+     TOBN(0x101bb4fe, 0x4c584948), TOBN(0xe3d7e99f, 0x55faafeb),
+     TOBN(0xd2c9aefa, 0x134c2e3b), TOBN(0x0aa2a71f, 0x79e27788),
+     TOBN(0x4082f7a6, 0x7ed0a606), TOBN(0x843c12bb, 0x6a1be308),
+     TOBN(0xae72ee74, 0x56e9e473), TOBN(0xcecde6c1, 0x743e16ee),
+     TOBN(0x9a06f105, 0x7c48ca04), TOBN(0x79179cd2, 0x5f822a31),
+     TOBN(0x570d3eeb, 0xe3530605), TOBN(0xbacb30c3, 0x4c7b03b0),
+     TOBN(0x0a8fe254, 0x0eea0cb4), TOBN(0xa052a555, 0x2cdf203a),
+     TOBN(0xee031587, 0x9c34971b), TOBN(0x5829eb07, 0xe76545cf),
+     TOBN(0xb7a3a6ae, 0x33a81bb9), TOBN(0xff42daff, 0x49c9f710),
+     TOBN(0x894eae85, 0xbffb951b), TOBN(0x815fe3e2, 0xce70f324),
+     TOBN(0x636564cb, 0x428b1f12), TOBN(0x722e0050, 0xa029b0bd),
+     TOBN(0xf45cb816, 0xd373a65b), TOBN(0xf2210e00, 0x6078d95e),
+     TOBN(0xf767d7a6, 0x20d2924a), TOBN(0x06d6b552, 0x25b66987),
+     TOBN(0x5c4a3999, 0x790563a1), TOBN(0xcea00a91, 0x3c85510c),
+     TOBN(0x7e37da9c, 0xd2db6297), TOBN(0xfca4735f, 0xf67303e8),
+     TOBN(0x324ca06e, 0xaf76f475), TOBN(0x3367845e, 0x76391adb),
+     TOBN(0x222aa1ce, 0xa26fe169), TOBN(0xb15a8665, 0x7ede94c7),
+     TOBN(0x5b736342, 0x6b6a1f33), TOBN(0x25db61e1, 0x8562f392),
+     TOBN(0xfd4d720d, 0xf2066206), TOBN(0x26ef773f, 0x82c555c3),
+     TOBN(0xb6e35b3a, 0xfde6caa3), TOBN(0x34eb5e13, 0x87fabf4a),
+     TOBN(0x4ad68635, 0x86236a62), TOBN(0x2651d3e6, 0x28510f8f),
+     TOBN(0x88073e34, 0xe0873ba6), TOBN(0x3becce70, 0x22f63746),
+     TOBN(0xff8f9b61, 0x7c08dac6), TOBN(0xc8b45a9c, 0x8c28aa65),
+     TOBN(0xe09c063b, 0xd87b59dc), TOBN(0xf3e4b9ef, 0xcbbdd4ec),
+     TOBN(0x1b6b1793, 0x4855a43e), TOBN(0x594d5565, 0x4ada74ac),
+     TOBN(0x10ee400f, 0xb410c5ef), TOBN(0xfc118113, 0x35695fe9),
+     TOBN(0x766cfe48, 0x8f75d723), TOBN(0xc72023eb, 0xff63aa76),
+     TOBN(0xc503a858, 0x9df9a165), TOBN(0x9b1099ef, 0x851acc4b),
+     TOBN(0x9246c61a, 0x66202ca0), TOBN(0xaba97788, 0x81390ccd),
+     TOBN(0x3309fa65, 0xba9e2785), TOBN(0xbc0388be, 0x2220f385),
+     TOBN(0x94c01a9e, 0x00ddc8ba), TOBN(0xaa54aed9, 0xbccfdec8),
+     TOBN(0x1a959c58, 0x059fc0d6), TOBN(0xd0f34c38, 0xf518e1c1),
+     TOBN(0x38aa2b1d, 0xb53be8fe), TOBN(0xd95a2a19, 0xacdc872f),
+     TOBN(0x97bde382, 0xb4140bd6), TOBN(0x4084ba9d, 0x4cfd5718),
+     TOBN(0xed016bfa, 0xfd22450c), TOBN(0xf00cdccf, 0xa5d1f5bc),
+     TOBN(0x905114cc, 0x111696ea), TOBN(0x1f58a4d3, 0x3a46e782),
+     TOBN(0x899d1856, 0xa5e57fa0), TOBN(0x25186954, 0x68c45c2f),
+     TOBN(0x806deb4a, 0xfa6e3eec), TOBN(0x3c358d48, 0x65a063a6),
+     TOBN(0xce28ed1f, 0x3feacdcd), TOBN(0xef9ee31c, 0xaaa8e601),
+     TOBN(0xddd4fe3d, 0x87c1c66b), TOBN(0xfc2b063e, 0xb3dbfac5),
+     TOBN(0x52d37cd0, 0x20c81dc5), TOBN(0xb628f163, 0x083b5f53),
+     TOBN(0xd9295094, 0x7e42860b), TOBN(0xb877a744, 0x307316ab),
+     TOBN(0xadec0d2d, 0x6b8d99b6), TOBN(0xa75183bd, 0x190bc675),
+     TOBN(0x2b1e0215, 0x4ad6bd44), TOBN(0xda01ad52, 0x9e020c66),
+     TOBN(0x65afd73d, 0x0c2913d9), TOBN(0x67024b45, 0xf0035373),
+     TOBN(0xf501bb4c, 0x4d308206), TOBN(0xfa020c88, 0x77e2e936),
+     TOBN(0x662b72bd, 0x936476a3), TOBN(0x07f76845, 0xbae57d17),
+     TOBN(0x77a43055, 0xf34ca404), TOBN(0x8e403294, 0x4eebc853),
+     TOBN(0xe19ee69a, 0x402fde89), TOBN(0x9092acd0, 0xfe00df56),
+     TOBN(0x640c035c, 0xfb225f92), TOBN(0x92d94246, 0xdce3aa84),
+     TOBN(0x7fe8d3f9, 0x971e9886), TOBN(0xc569905e, 0x014b2a74),
+     TOBN(0xbafb8c4d, 0x7b7c299b), TOBN(0x3d289c60, 0xd534cd21),
+     TOBN(0x95e7032b, 0xd311dee4), TOBN(0xac0c46dd, 0x6e8892a4),
+     TOBN(0x9fedef00, 0xe5bd6486), TOBN(0x3f4d8daa, 0x99f703aa),
+     TOBN(0x78e47925, 0xf0c0ecd2), TOBN(0x8f143c2b, 0xfdac73de),
+     TOBN(0x1f88f5a9, 0xc14bd094), TOBN(0x6cc19e43, 0x99d9532b),
+     TOBN(0x6e474499, 0x639ba66c), TOBN(0x5d9a283b, 0xf5d06b03),
+     TOBN(0xc7e8164f, 0xaa25dbb5), TOBN(0x7ab42a48, 0xf03faec8),
+     TOBN(0x4135765b, 0x647a0d72), TOBN(0x9562a676, 0xe196b571),
+     TOBN(0x62cc4c05, 0xa720cc20), TOBN(0x9ed3f637, 0x13fa1ad2),
+     TOBN(0xe5816f51, 0x7f59bac9), TOBN(0x738e1544, 0xb6884359),
+     TOBN(0x83bb2666, 0x34d0fb02), TOBN(0x0e582c6b, 0x8014c57b),
+     TOBN(0xbb4069ae, 0x145e2bff), TOBN(0xd1965cdf, 0x4f5f1d7d),
+     TOBN(0xaf77f98b, 0x3cead86d), TOBN(0x0ba278bd, 0x0e51cbd8),
+     TOBN(0xf11f20f6, 0xac2ebb7c), TOBN(0xafd2333f, 0xc9992b55),
+     TOBN(0x425dd0e4, 0x322472b6), TOBN(0x0027a74a, 0x0958215e),
+     TOBN(0xddb301e7, 0x4cf7e0e4), TOBN(0xd0656ed5, 0xcbb70c2e),
+     TOBN(0xa40f629f, 0x0e7662e8), TOBN(0xdaa85755, 0xe399a5ca),
+     TOBN(0x4c119aba, 0x7297010c), TOBN(0x4a4a6a43, 0xe5df7140),
+     TOBN(0x6d90d303, 0x474f7873), TOBN(0xc5b0e19c, 0xd1f8f867),
+     TOBN(0x188bcae6, 0x4f6dc217), TOBN(0x51ce999a, 0x6777357f),
+     TOBN(0xdfc9578b, 0x41aeb39f), TOBN(0xeeda86fe, 0x7dd55c1f),
+     TOBN(0xd4b8fc54, 0xfb814075), TOBN(0x12e32a78, 0x33a1317c),
+     TOBN(0xeb79cd2b, 0x2fd217d1), TOBN(0x5f5f20c0, 0xdbd07638),
+     TOBN(0xfc57643a, 0x53dc7d8b), TOBN(0x65126014, 0xf08350e2),
+     TOBN(0x737ef5b4, 0x871b0d3d), TOBN(0x6b7e04ce, 0xae3143a5),
+     TOBN(0x0e5ab52e, 0xb7ae12b9), TOBN(0x1a956daa, 0xdb66ee0e),
+     TOBN(0x59657e47, 0xeaa7042e), TOBN(0xbbc35318, 0xbf84a2cf),
+     TOBN(0xef55429c, 0x78679b8b), TOBN(0xef92df9d, 0x60cb7678),
+     TOBN(0x17655580, 0x1dd267d6), TOBN(0x00a3ec71, 0xeb0bc1fa),
+     TOBN(0xafa0a256, 0x50514840), TOBN(0x68c28d0c, 0xf161c685),
+     TOBN(0x069f7862, 0xb1c766dc), TOBN(0x6868a463, 0xd5ad4568),
+     TOBN(0xf9c3d670, 0x70e46d7d), TOBN(0xd2432cc9, 0x6c875260),
+     TOBN(0x534c3425, 0x088cecd9), TOBN(0x3f1818e6, 0xb4e34c6c),
+     TOBN(0x3aedf0a8, 0x028f153b), TOBN(0xe0a1cb9d, 0x50d9433a),
+     TOBN(0x9b4e225f, 0xe523b764), TOBN(0xcba6cba9, 0xe5f8542c),
+     TOBN(0x59c307e4, 0xa8f6b81e), TOBN(0x36abf4b7, 0x01bb44fc),
+     TOBN(0xf7692c14, 0xdd62528c), TOBN(0x0d4a8341, 0xdf57773e),
+     TOBN(0xece6957d, 0xc9b4f968), TOBN(0x82eda200, 0x52779586),
+     TOBN(0xb902c488, 0x2f06ec6b), TOBN(0x127dd7ba, 0x91a876f0),
+     TOBN(0x06eb96d8, 0x33ad0c13), TOBN(0xd7394080, 0xfc5985ce),
+     TOBN(0x624c8f61, 0x661aaa4d), TOBN(0x6fe10a11, 0x6717a3e1),
+     TOBN(0x6c288c53, 0x53168ad0), TOBN(0x91b8779b, 0x8b52d037),
+     TOBN(0x89e664d4, 0x1b5b0ab9), TOBN(0x9f69b44f, 0xf30d47d3),
+     TOBN(0xfe67cad5, 0x03176019), TOBN(0xb83efd48, 0xb346a205),
+     TOBN(0x63fc4863, 0xaeea0c91), TOBN(0xbabf9042, 0xdb56004b),
+     TOBN(0xdb19f2ee, 0xa9917def), TOBN(0x1d12f2dc, 0x54c3fae1),
+     TOBN(0x7bb496af, 0x55e36d40), TOBN(0x1f6c11f8, 0x6be63b27),
+     TOBN(0x96d79804, 0xcaf9a5b9), TOBN(0x03a085c4, 0x0648051c),
+     TOBN(0x3b54c223, 0xb56baf4c), TOBN(0x04af8c4c, 0x559c1fc1),
+     TOBN(0x05d55266, 0xabd3cebb), TOBN(0xd2d3ae9b, 0xf865e262),
+     TOBN(0x3bd3ca3a, 0xedfedc54), TOBN(0x30a6ff1c, 0x922776c4),
+     TOBN(0xfecd8845, 0x1616a6f2), TOBN(0x4e7bc2e8, 0x94948d8c),
+     TOBN(0x16e0d824, 0xedca784b), TOBN(0x84584f98, 0x67ea1eea),
+     TOBN(0xeceb1418, 0x8625626b), TOBN(0xa487cf9f, 0xc34fc1f3),
+     TOBN(0x4ecfedd2, 0xa57cec36), TOBN(0x08624865, 0xd24a0709),
+     TOBN(0x47bb1909, 0x6a48f3ee), TOBN(0x54c5dd0c, 0xc69bc041),
+     TOBN(0x15a291e6, 0x7527166e), TOBN(0x8a92370d, 0x4a9a8315),
+     TOBN(0xe9fe705d, 0xda584bd6), TOBN(0xed441dc3, 0x3625a669),
+     TOBN(0xa57929ce, 0x3063f2de), TOBN(0x2809fe4b, 0x6348cc31),
+     TOBN(0x92041d54, 0x04cc19c0), TOBN(0xd7c227fc, 0xb62c1f94),
+     TOBN(0xecfeee53, 0xcd0d497c), TOBN(0x8d1ea9fd, 0x128818a8),
+     TOBN(0xb5cf2c28, 0x2ae4725d), TOBN(0x7de9f967, 0xc6abad3a),
+     TOBN(0xc64a11fc, 0xb14a183c), TOBN(0x7f14d0fe, 0xd5777d77),
+     TOBN(0xbe79846c, 0x12957444), TOBN(0x4cf23abf, 0x0e3257ca),
+     TOBN(0x8da0fd8d, 0x6e01b48e), TOBN(0x63a7ff16, 0x5ee87ca4),
+     TOBN(0x90dff4d3, 0x5cc96b94), TOBN(0xff1b41c3, 0x406fc905),
+     TOBN(0xdd932925, 0x3ac71c41), TOBN(0xec57f1b8, 0xcf65e59d),
+     TOBN(0xa3116d6f, 0x3ce0512b), TOBN(0x3b46fd3c, 0xa2e28316),
+     TOBN(0x5a6c0314, 0x60156a5e), TOBN(0xfab3afe3, 0x55d46fd0),
+     TOBN(0x4617926c, 0x9846f0db), TOBN(0xc2d5a447, 0x121ef237),
+     TOBN(0x789498d1, 0xf1cda3b1), TOBN(0xa195cf03, 0xccd64aac),
+     TOBN(0xe8d1a162, 0x9440be2b), TOBN(0x7399890a, 0x3ad5373e),
+     TOBN(0x65dcea2f, 0x4fbf1899), TOBN(0x44d9c8ab, 0x44ee1a5a),
+     TOBN(0x2e94f8c8, 0x406880fb), TOBN(0x70732bad, 0x51faab7d),
+     TOBN(0xd69309dd, 0xf1e92b52), TOBN(0x25f9a675, 0x3c7685d0),
+     TOBN(0x3604f30b, 0x1dbfbaa0), TOBN(0x5ac0001b, 0x2ff28c22),
+     TOBN(0x648ec4fa, 0x52762d8d), TOBN(0x4fc248c6, 0x0cef95f8),
+     TOBN(0xfc0f7030, 0xe5fbb57d), TOBN(0x2e644729, 0x5ff2db51),
+     TOBN(0xd85877ec, 0x3775471c), TOBN(0xe2580058, 0x6076a271),
+     TOBN(0x58a4a24f, 0x09cb3873), TOBN(0xb412928b, 0xb142da8c),
+     TOBN(0x1da7964b, 0x5997987a), TOBN(0x69765ff0, 0x1825d97b),
+     TOBN(0xde8ae407, 0x4c97095c), TOBN(0xb257a968, 0xbb59316c),
+     TOBN(0x80e5240d, 0xcf2dfbd2), TOBN(0x2b3b04b0, 0x1fccd0e7),
+     TOBN(0x949f1234, 0x8ff1093e), TOBN(0xa4df3290, 0x65f62273),
+     TOBN(0x305b7479, 0xd0058ffa), TOBN(0xe459ecff, 0x180b0de6),
+     TOBN(0xfbe00c08, 0x8ca0585f), TOBN(0xc169e23a, 0xc3dd4fa0),
+     TOBN(0x65d9009a, 0x44026f6e), TOBN(0xbbc258c3, 0x1d96fe58),
+     TOBN(0xd7ed379c, 0x328ed1e0), TOBN(0xe8b45744, 0x23970884),
+     TOBN(0x2f44c8ce, 0xed1095b5), TOBN(0x25725b0d, 0xc59404aa),
+     TOBN(0xea926278, 0xa273e09f), TOBN(0x102b120b, 0x529143d5),
+     TOBN(0xbd2c012d, 0x81174d10), TOBN(0x0bf5cf89, 0x4e8333ad),
+     TOBN(0x6b93e3b0, 0xb2f60645), TOBN(0x040298b8, 0xf38df3ce),
+     TOBN(0x6433b6fc, 0xb89e18fd), TOBN(0x48d6584a, 0x6bd6af88),
+     TOBN(0x46c8a061, 0x0e1747a5), TOBN(0xe225d3cd, 0x1ed79faa),
+     TOBN(0x6c579abb, 0x5c28a48b), TOBN(0xc7a5ff4d, 0xda3889d6),
+     TOBN(0x037ebc4b, 0x008febdf), TOBN(0x03b60593, 0xa7c5cbc9),
+     TOBN(0x0840804d, 0xdcaa3319), TOBN(0x38b0f084, 0xb7481f45),
+     TOBN(0xfdb059c9, 0x112e794e), TOBN(0xb62bce05, 0xe1afb614),
+     TOBN(0xc15035b6, 0x53be7c02), TOBN(0x66fc7106, 0x153ee8e5),
+     TOBN(0x27fd2ea4, 0x8258727f), TOBN(0x8e012416, 0xb470105e),
+     TOBN(0x3d24685b, 0xd7711aee), TOBN(0x66a83c3b, 0x021bab69),
+     TOBN(0x6e1112a7, 0x5501d69d), TOBN(0x2219fe0b, 0x068b0504),
+     TOBN(0xaaa553c1, 0xa926ab1b), TOBN(0x1c81af95, 0x56d6ca58),
+     TOBN(0x0a997380, 0x221ef447), TOBN(0x881a62fa, 0xf5f55487),
+     TOBN(0xf88fa0bd, 0x4f1b618a), TOBN(0xb20e161c, 0xcbac98e8),
+     TOBN(0x443352b5, 0x3bc6a7ad), TOBN(0xbc8e03ff, 0x0fd5748a),
+     TOBN(0x64006aff, 0x0ca427fc), TOBN(0x1a477593, 0x7cbbda99),
+     TOBN(0x21ef1afd, 0x1a347c47), TOBN(0xfe056287, 0xdee162b6),
+     TOBN(0x9d4eb7da, 0x797a0b14), TOBN(0xe4e01a46, 0x951c4bd0),
+     TOBN(0xaf8fa17f, 0x7fe354a6), TOBN(0xd71f160c, 0xc430b12b),
+     TOBN(0x5bb68437, 0x83d46be0), TOBN(0x99d10d82, 0x619bba86),
+     TOBN(0x95c2219d, 0xf4327042), TOBN(0xdace2322, 0x9c19ab57),
+     TOBN(0x88abbc67, 0xc8750fe7), TOBN(0x81ab300a, 0xd3abe7d2),
+     TOBN(0x62a6d8d5, 0x45aa8948), TOBN(0x76175bbd, 0x5d4ce8c3),
+     TOBN(0x095cb181, 0x8ea70976), TOBN(0x785de3fc, 0xf7e62a19),
+     TOBN(0xc478bce8, 0xed11a7fe), TOBN(0xb7e5993b, 0x1528aee2),
+     TOBN(0xb9ec58d7, 0x76c32e4b), TOBN(0xef815613, 0x2dbc9a61),
+     TOBN(0x372c3856, 0x5e07410d), TOBN(0xa1b16510, 0x033276d0),
+     TOBN(0xd8589581, 0x82640d26), TOBN(0x1cb98180, 0x9053fff0),
+     TOBN(0x41519ce7, 0xc1ff11f1), TOBN(0x2f21a48f, 0x666431e4),
+     TOBN(0x2c223ed1, 0xe83ee840), TOBN(0x833ae708, 0x1e7cf4dc),
+     TOBN(0xec8853d8, 0xacd13385), TOBN(0x559115ab, 0x6a7a8cb1),
+     TOBN(0xe2f4ab2a, 0xeb184e71), TOBN(0x679abbce, 0xc10194df),
+     TOBN(0x10199c84, 0x3aca0828), TOBN(0x7474e113, 0x978cc1d6),
+     TOBN(0xa4460ef1, 0x44e8eb9a), TOBN(0x4cde5260, 0x828aa4bf),
+     TOBN(0xd3d23790, 0x249bff50), TOBN(0x2e6660da, 0x6bc7fbbc),
+     TOBN(0x3e3cc146, 0x61494df0), TOBN(0x6e9a1571, 0x0bcda8ba),
+     TOBN(0x68ce233e, 0x096e00b7), TOBN(0x247a5c49, 0x5106c85b),
+     TOBN(0xe6f0cb5c, 0x55fc5757), TOBN(0x452682b9, 0xb7585799),
+     TOBN(0x84509dfe, 0x869e747e), TOBN(0x904366e5, 0x8d23be04),
+     TOBN(0x7324a14d, 0xb0f72c6d), TOBN(0x9fbe3116, 0x1913a9ff),
+     TOBN(0x2f9fa62a, 0x428a6b5d), TOBN(0x8039270f, 0xf8a34d9e),
+     TOBN(0x0e3ce7ae, 0x407aec78), TOBN(0x4d935d3d, 0x826458cd),
+     TOBN(0xf75db7db, 0xfc6f46d4), TOBN(0x3ab7ba68, 0x88586874),
+     TOBN(0xec92749f, 0x9a198b50), TOBN(0x0ffc7681, 0xefc51cdb),
+     TOBN(0x951406d5, 0xe17bc0e3), TOBN(0x39cd2d07, 0xc898a018),
+     TOBN(0x9dc3803c, 0xf2f79af6), TOBN(0x292f3164, 0x0a56cd68),
+     TOBN(0xdcac21f9, 0xf6fbdbdf), TOBN(0x6f9ce2a4, 0x23e9e959),
+     TOBN(0x2011d221, 0x970f6c34), TOBN(0xd2e63711, 0x9e2decfb),
+     TOBN(0x19c7a489, 0x118ff327), TOBN(0xe19d7e83, 0xbb6e534e),
+     TOBN(0xc685389a, 0xbd1a426b), TOBN(0x432ff7b0, 0x8c679952),
+     TOBN(0x516cbdfa, 0xc5e2687f), TOBN(0x8242405d, 0xba1eac8f),
+     TOBN(0x63af3152, 0x0b09854e), TOBN(0xcecd0faa, 0x231ec979),
+     TOBN(0x4746733f, 0x7273f0b2), TOBN(0x69b28d87, 0x8f001365),
+     TOBN(0x0d87d506, 0xc6f2623c), TOBN(0xd209a9c6, 0x86c40ed2),
+     TOBN(0xa5b7fde2, 0x0fa20f3b), TOBN(0x54550dc5, 0x4f5b2129),
+     TOBN(0x36884047, 0xfeddaa1b), TOBN(0x51398fa0, 0xd899a29f),
+     TOBN(0x14a416be, 0xcdf11867), TOBN(0x86351ac6, 0x3e466b62),
+     TOBN(0xd63e91e1, 0x39bb481a), TOBN(0xdcdc072a, 0x99984155),
+     TOBN(0x9fce6e38, 0xd2d8e622), TOBN(0xbf6978b6, 0x8e8c8034),
+     TOBN(0xaa1ae874, 0x8c37990a), TOBN(0xd1f0e068, 0x0e749b86),
+     TOBN(0x5aa303b1, 0xcbdc7c12), TOBN(0x9a78baaf, 0xc9130211),
+    },
+    {
+     TOBN(0x5eeba910, 0xb3e2087b), TOBN(0xbd016dd3, 0x44a61a33),
+     TOBN(0xffd1f081, 0x48cd5c0f), TOBN(0x041c6aa0, 0x2e6a8e94),
+     TOBN(0xe09c35c5, 0xc4ac3d91), TOBN(0x58298105, 0x634767a4),
+     TOBN(0x6120e7cb, 0x1040c2b5), TOBN(0x32a661ef, 0xa713589f),
+     TOBN(0x5a056a90, 0xbd74c70e), TOBN(0x44f7d00d, 0x8af672f3),
+     TOBN(0xdc25ab68, 0xef4e9a48), TOBN(0xadd15cc3, 0xfdfb1069),
+     TOBN(0xb1f4fd28, 0x9f3033bf), TOBN(0x088a49bb, 0xebb8b8a7),
+     TOBN(0xea485869, 0xa8d2861c), TOBN(0x46dbfdaf, 0x6b977fb5),
+     TOBN(0x04e6461e, 0xd88ae888), TOBN(0x112d204a, 0x9be2d7ad),
+     TOBN(0x952dc813, 0xdb558427), TOBN(0x39b01652, 0x27e35200),
+     TOBN(0x8b89bfce, 0xff02cdbb), TOBN(0x1381a99b, 0x3e854e4c),
+     TOBN(0x68185218, 0x999efd2b), TOBN(0xeb41e1bb, 0x86dc62e1),
+     TOBN(0xa264ef40, 0x02d0aaff), TOBN(0xa678c07d, 0x6e679fe2),
+     TOBN(0xcff13be7, 0xfd88bdce), TOBN(0x9a8efe8d, 0x617badb8),
+     TOBN(0x1388a815, 0xad5a22f4), TOBN(0x8f821400, 0xfec398b3),
+     TOBN(0x85a6a565, 0xff4fc2da), TOBN(0x681f0181, 0x858dd4f3),
+     TOBN(0xdc870745, 0x91ee75bc), TOBN(0xbadbf940, 0x64f20e50),
+     TOBN(0xf3ea437f, 0x49d3d417), TOBN(0x7bebd868, 0xc02109d0),
+     TOBN(0xe6c8d93c, 0xd16bb240), TOBN(0x2087141a, 0xfdab9bbd),
+     TOBN(0x8dba80ff, 0x20a3b470), TOBN(0x960a0c7b, 0x29d3a8d7),
+     TOBN(0xae61b637, 0xf8ec1151), TOBN(0x415dd36b, 0xaadc8545),
+     TOBN(0xed21d176, 0x69d0985a), TOBN(0xc4d062af, 0x97893f50),
+     TOBN(0x4d93ba1a, 0x337b81f9), TOBN(0xb995fe9e, 0xb7c163a2),
+     TOBN(0x447eff3b, 0x5416e4ed), TOBN(0xd7660300, 0x0bf4a8e7),
+     TOBN(0x9e14c6a2, 0x56d9e00f), TOBN(0xa228491c, 0xfa1f60e0),
+     TOBN(0xd540713e, 0x8782a9be), TOBN(0x5d3fcce8, 0xdcd55e21),
+     TOBN(0xa176c34e, 0x35c87b90), TOBN(0xc1d80aa1, 0xf9508f35),
+     TOBN(0x14f7e7fc, 0x92302d47), TOBN(0x459372ba, 0x2b076e72),
+     TOBN(0x44168fbc, 0x4e933b19), TOBN(0xaf2db74c, 0xe54ea969),
+     TOBN(0x36fade13, 0xaeacbb56), TOBN(0x29708665, 0x84e6cd1d),
+     TOBN(0x6f7ff1e1, 0xb692df97), TOBN(0x5a68c1a2, 0x6ae66307),
+     TOBN(0x85bc544c, 0xe7685f20), TOBN(0xb3f42e6d, 0x0f65eeec),
+     TOBN(0xef209f44, 0x5b91b644), TOBN(0x808b930a, 0x50cb02b6),
+     TOBN(0xc5da5e86, 0x099f684f), TOBN(0xd8f1dbcc, 0x4330c2d8),
+     TOBN(0x52e8cab5, 0xd8608776), TOBN(0x13c89771, 0x16e56f5d),
+     TOBN(0x7d7d802a, 0xb135282b), TOBN(0x69762c39, 0xe9be8a20),
+     TOBN(0x13f6bbad, 0x2a08a1dc), TOBN(0xa7131e4a, 0x7f2dba7a),
+     TOBN(0x751dce48, 0x7864f5a3), TOBN(0xc5af1b45, 0x08135109),
+     TOBN(0x3c4d75f7, 0x4f08636e), TOBN(0x9949b2f5, 0x2e251e48),
+     TOBN(0xd0497977, 0x9bd98853), TOBN(0x09d8e627, 0x909b0e84),
+     TOBN(0x505753ee, 0x4ceff1c9), TOBN(0x03ca4571, 0x460710ca),
+     TOBN(0x0cf72dee, 0x5480abc0), TOBN(0x55d5a30e, 0xc19c8ef3),
+     TOBN(0x9e47641b, 0x86541f6f), TOBN(0x89b2e48f, 0x10c9d6fb),
+     TOBN(0x9d16382b, 0x0860915c), TOBN(0x770ac417, 0xf54b337f),
+     TOBN(0x366d0781, 0x77ef7f67), TOBN(0xfefec947, 0x2b6340c9),
+     TOBN(0x7ce3a056, 0x097acf63), TOBN(0x26538c7c, 0xaf306dec),
+     TOBN(0x8a8bb55e, 0x287dc8d1), TOBN(0x9431095b, 0x448823ae),
+     TOBN(0x8358087a, 0x7970fc51), TOBN(0x95299959, 0x413509ac),
+     TOBN(0xb165f92a, 0x8b0911d4), TOBN(0xdde855ee, 0xbfb37459),
+     TOBN(0xe672eed4, 0x3d9ce36e), TOBN(0xf7da91e5, 0x74bae261),
+     TOBN(0x1f67323c, 0xb741c8e6), TOBN(0x8efd4661, 0xc92c91ce),
+     TOBN(0x556f0b1a, 0x78e73c42), TOBN(0x7d326b7f, 0x16101f96),
+     TOBN(0x8814ef0b, 0xfb48bd3a), TOBN(0x1bbbe13e, 0xc508309e),
+     TOBN(0x7ddaf061, 0x67709c10), TOBN(0x82b67847, 0x6436f655),
+     TOBN(0x2a5601c6, 0x03712e64), TOBN(0xac1f0362, 0x3e3f9b2e),
+     TOBN(0xcc7e6a09, 0x09184b5d), TOBN(0x1258b265, 0xb4625149),
+     TOBN(0xd9f21461, 0x384a6b54), TOBN(0xde483161, 0x6cfe9311),
+     TOBN(0x593dae45, 0x889f43cc), TOBN(0x8454335b, 0xedee221b),
+     TOBN(0x90f3fb43, 0x3a2cbced), TOBN(0x895ed692, 0xcc8dcb75),
+     TOBN(0x9857d719, 0x14233aa7), TOBN(0x91b1a2ab, 0x48166d5f),
+     TOBN(0xfbf7033a, 0x675b47a0), TOBN(0xcb3669c4, 0x6542378f),
+     TOBN(0x96abb0f7, 0x125ec248), TOBN(0x6d5d2047, 0x795fc346),
+     TOBN(0xa6c6c9e8, 0x8f5cffb6), TOBN(0xb968f2c7, 0xbea5ee09),
+     TOBN(0x2f2ce735, 0x844ffd6f), TOBN(0x7931b877, 0x27e40ac8),
+     TOBN(0xe1f62dcf, 0x2b63d538), TOBN(0x395681dd, 0xf44d7bf9),
+     TOBN(0xf02eedf7, 0x54aec359), TOBN(0xc64b6233, 0xa0ad5eb7),
+     TOBN(0xc65093c7, 0x346b086a), TOBN(0xfcf8ecc9, 0xe957b243),
+     TOBN(0xe1accffa, 0x1ca48020), TOBN(0xe1f29792, 0x4047bbeb),
+     TOBN(0xb1097d40, 0x9fc6be25), TOBN(0x02d33d19, 0x923eb7b4),
+     TOBN(0x9e25200c, 0x1f58f545), TOBN(0x2ffae306, 0xda51efcb),
+     TOBN(0x7e6d76c1, 0xc0b011f2), TOBN(0xedbd8398, 0xf680676b),
+     TOBN(0x38517fc2, 0x8d7fc8c2), TOBN(0x55a7fcf9, 0x5c3ab05c),
+     TOBN(0x047e2422, 0x8e6dd02d), TOBN(0x7b3bf0e6, 0x1f290d6a),
+     TOBN(0xbcf326fc, 0x6a99a6d0), TOBN(0x1e3f38fa, 0x2eef8232),
+     TOBN(0x9890780e, 0x15bac529), TOBN(0x94202e0e, 0x9f143ba0),
+     TOBN(0xbd574712, 0x885e4ed5), TOBN(0x2404c223, 0x396f938d),
+     TOBN(0xd54d401c, 0xacde8286), TOBN(0x80397794, 0xe7af01fd),
+     TOBN(0x94457d07, 0xf615a8eb), TOBN(0x34680480, 0xd22d9ef7),
+     TOBN(0x04d4b302, 0x2c489ccf), TOBN(0x11dea4bd, 0xc3673dae),
+     TOBN(0x8fbb4df5, 0x58cdfe41), TOBN(0x49425184, 0x0f10a70e),
+     TOBN(0x831b977a, 0x077a59ce), TOBN(0xee08fb0c, 0x894627f3),
+     TOBN(0x21360507, 0x2f8553f0), TOBN(0xca77ccd1, 0x0487171b),
+     TOBN(0xc17d20c2, 0x07e11059), TOBN(0xcf74be6b, 0xbe613256),
+     TOBN(0x06f185e6, 0xa5fe18c1), TOBN(0x8d2cf4f5, 0x2b57ce3e),
+     TOBN(0x7179273c, 0xc9c983e7), TOBN(0xc7d27357, 0x153f58d8),
+     TOBN(0xc0273069, 0x4f400bd4), TOBN(0x23309c7f, 0x26262553),
+     TOBN(0xf26b6e11, 0x712d0314), TOBN(0xb925cebf, 0xf96ee39a),
+     TOBN(0x6df57108, 0x73944251), TOBN(0x95419b24, 0x589d90aa),
+     TOBN(0x57a1bcc5, 0x796a8ee2), TOBN(0x22a22530, 0x2acee09d),
+     TOBN(0xa4c2cc03, 0x66fa2911), TOBN(0x9cc2b7fa, 0xd85f13dc),
+     TOBN(0xf2498b8a, 0xce152790), TOBN(0xd8406007, 0x1caf39d1),
+     TOBN(0x7ff50064, 0x84c0822f), TOBN(0xaf14ca4b, 0x155f1280),
+     TOBN(0x113f094b, 0x89b781c2), TOBN(0x996bf893, 0x013833a5),
+     TOBN(0x26bc6210, 0xc0b9cf6d), TOBN(0x18e2d3ac, 0x6a88f1cf),
+     TOBN(0xc0ff2b3c, 0xa21a2d35), TOBN(0x409c2598, 0xa79e918e),
+     TOBN(0xffcf65a0, 0xb6917e22), TOBN(0x8036897f, 0xbb4f8f3c),
+     TOBN(0xac660365, 0x9ec27fd7), TOBN(0x3c5ca1a9, 0x0c56cbb1),
+     TOBN(0x01c5dce1, 0xbe9e9ec7), TOBN(0xdc21b01a, 0x386edb4a),
+     TOBN(0x47e02a92, 0x4b1dde01), TOBN(0x0613b7ca, 0x44af3e0b),
+     TOBN(0x644ac708, 0x1c445b6f), TOBN(0xb5566f0f, 0x87243e2a),
+     TOBN(0x5b244172, 0xba9f354a), TOBN(0xaca4e9d3, 0xeb653a5e),
+     TOBN(0x6ff6904a, 0x514809f2), TOBN(0xf87a329b, 0x96595230),
+     TOBN(0x39ebe6eb, 0x8d4bd051), TOBN(0x66f05f5c, 0x07d17d59),
+     TOBN(0xfa1ee673, 0xe0f81731), TOBN(0xf41c1042, 0xd12804a9),
+     TOBN(0x1c4a655a, 0xacd14cf5), TOBN(0xdc72f5bc, 0xef47548f),
+     TOBN(0xab07ceff, 0x0b3ee6c7), TOBN(0xcfa88319, 0xbb501a28),
+     TOBN(0xcec9c2e2, 0xd8f03f7c), TOBN(0x3098d752, 0xe0c98d62),
+     TOBN(0xa41a0794, 0x0a8681b3), TOBN(0x0e588076, 0x23587932),
+     TOBN(0x4617dc66, 0x5ef86f7c), TOBN(0x51de8430, 0xcedb5377),
+     TOBN(0x0dda704a, 0x737381b6), TOBN(0x83a44653, 0x008f4671),
+     TOBN(0x71bbb73e, 0x38401c11), TOBN(0x879fe49c, 0x4894d378),
+     TOBN(0x8875eef9, 0xeab186a2), TOBN(0xedefe278, 0x12a382a9),
+     TOBN(0x95ef41b8, 0x13b897fd), TOBN(0xfefd495f, 0x2a98ddd9),
+     TOBN(0x09cbccfc, 0x680b12e8), TOBN(0xc1888a21, 0x167da5dc),
+     TOBN(0x2a204912, 0x3bb290b4), TOBN(0xdcac95fc, 0xd9613190),
+     TOBN(0x4df94f62, 0x709c76f7), TOBN(0xc8c3a8ab, 0xa5cea926),
+     TOBN(0x15c876b2, 0x37a2b813), TOBN(0x9b52803e, 0x9c3c821c),
+     TOBN(0x40f2268c, 0xcb3d6ff1), TOBN(0x689f1696, 0xa1573601),
+     TOBN(0x8d7566dd, 0x8e921f56), TOBN(0x5d8a990c, 0xd992335a),
+     TOBN(0x6339153a, 0x20dc4f4b), TOBN(0x0b07419c, 0xdc5d06ab),
+     TOBN(0xe9cc014d, 0x97c201f9), TOBN(0xec04a52e, 0xa635f472),
+     TOBN(0x6aac504a, 0xa538a84f), TOBN(0x4d0288e3, 0x5762fe7c),
+     TOBN(0xaa8539f0, 0x34cbd09a), TOBN(0x6f7e0e94, 0x2619bcf7),
+     TOBN(0x178303dd, 0x0dd338d0), TOBN(0x6b58c2b1, 0x8326f40e),
+     TOBN(0x98bb15ec, 0xfe73e699), TOBN(0x7533abdc, 0x47526489),
+     TOBN(0x4b269607, 0x491dcc6d), TOBN(0x325ec2a0, 0x77187363),
+     TOBN(0x766faa19, 0x7e9ab865), TOBN(0x1c105b4a, 0xc25a9448),
+     TOBN(0x0b6b8963, 0x0531b5ba), TOBN(0x32691f11, 0x2db1a579),
+     TOBN(0x24d90a57, 0x643e479c), TOBN(0x048b27cb, 0xb98578df),
+     TOBN(0x0600f93f, 0xe53bed53), TOBN(0x1fd57dfc, 0x0aac585f),
+     TOBN(0xc3d72121, 0x71d0e4e0), TOBN(0x5ff10dfb, 0xf612fc4e),
+     TOBN(0x9edf4b23, 0xb5a7ec79), TOBN(0x975165c7, 0xd87706ab),
+     TOBN(0x8b99db04, 0x7095c3c4), TOBN(0x65196441, 0x897faf50),
+     TOBN(0x5d23d7d9, 0xdd5b64cb), TOBN(0xec734b06, 0xe95fe601),
+     TOBN(0x03a5f53f, 0x0b5fcde9), TOBN(0x0186ad22, 0xebe35310),
+     TOBN(0xe9a65eef, 0x84846603), TOBN(0xe99e5188, 0xa7c6e5be),
+     TOBN(0xa917327d, 0xf0887da6), TOBN(0x49965f78, 0xe3f9fa70),
+     TOBN(0x02ed227d, 0x4f10b31d), TOBN(0x535b4386, 0xb6120be5),
+     TOBN(0xdff21a8a, 0xcc1bf98a), TOBN(0x5b52a09a, 0xeb1634bc),
+     TOBN(0x60f8690d, 0xa3f61fa2), TOBN(0x58a02566, 0xb863c790),
+     TOBN(0xf9b90a9e, 0x5c6b2929), TOBN(0xd552e84c, 0x22fca36e),
+     TOBN(0x6b23da4f, 0x9eabcb58), TOBN(0x01111d07, 0x5d4136dc),
+     TOBN(0xfaa80059, 0xb3642a09), TOBN(0x1de667f4, 0x5f49d533),
+     TOBN(0xb3268776, 0x17525176), TOBN(0x75b0b102, 0xda729fde),
+     TOBN(0x4ec7f678, 0x6e9fe6ed), TOBN(0x28d29578, 0x2717f8b0),
+     TOBN(0x6a713c37, 0xd4cc149f), TOBN(0x4804e04f, 0x7dfdf8c6),
+     TOBN(0xe7c6daab, 0x5c931aa6), TOBN(0x793e411d, 0xa0394f29),
+     TOBN(0xc0741c0d, 0x79ed9819), TOBN(0x3f2ba70b, 0x24d5d992),
+     TOBN(0xa61dc03f, 0xca9c987a), TOBN(0x78201cb8, 0xe64b50c1),
+     TOBN(0x45a23c25, 0x1861f4e4), TOBN(0x10f19f4f, 0xc4ee5d82),
+     TOBN(0xf1520547, 0xf3f055f4), TOBN(0x69ae26b3, 0x006ccf49),
+     TOBN(0xe96eec0b, 0x33d8d4ad), TOBN(0x00765f0c, 0x48a4fc2c),
+     TOBN(0xad47e14e, 0xa3976c07), TOBN(0x82b1f882, 0xd905b6b4),
+     TOBN(0x7a1b9d73, 0x91382bac), TOBN(0xcc84a820, 0x18178290),
+     TOBN(0x1123c6f6, 0xb4e845ab), TOBN(0x63216635, 0xb92e3b08),
+     TOBN(0x748be745, 0x183879fb), TOBN(0x7f20e1f0, 0xa73e9ada),
+     TOBN(0x05de3e11, 0x9224c152), TOBN(0x2fa9a474, 0xea8fda4e),
+     TOBN(0xf5c8df05, 0xf48055ec), TOBN(0x48bbf43a, 0x9e23a599),
+     TOBN(0xf593f034, 0x148086db), TOBN(0x0173a87a, 0xef0a2b62),
+     TOBN(0x90ef0132, 0x3fbabb6f), TOBN(0x56ced09a, 0x21ade107),
+     TOBN(0xcf1ce892, 0x73f1d3e0), TOBN(0x765236c6, 0x22424580),
+     TOBN(0x0d223937, 0xd939d063), TOBN(0x9a21beda, 0x7cb2fe2f),
+     TOBN(0xa559a571, 0x1ce3a7fc), TOBN(0x7fd6b255, 0x1b060dd5),
+     TOBN(0x4dfbd210, 0xc5afdf1a), TOBN(0xa74751ce, 0x1239368a),
+     TOBN(0x93acdd06, 0x6d9a3eec), TOBN(0x7d97f794, 0x9832dcfd),
+     TOBN(0xdafa9a44, 0x0cc645ca), TOBN(0x1da27ddf, 0xcfee0817),
+     TOBN(0x0c1e6319, 0x01b8dd49), TOBN(0x8267e508, 0xd91aeace),
+     TOBN(0x86a2cedc, 0x87f43f20), TOBN(0x7dd0e670, 0x07db2f24),
+     TOBN(0x8ea1e973, 0x9db25177), TOBN(0x659cccb8, 0xab8802df),
+     TOBN(0x2bd39c65, 0x004940ab), TOBN(0x57676876, 0xd9419db5),
+     TOBN(0xd52058a3, 0x6d6f237c), TOBN(0xd9812dcd, 0xfb4a9a7b),
+     TOBN(0x879890d1, 0x53bec56e), TOBN(0x17b7f151, 0x7ac5d9d9),
+     TOBN(0x2db3f5df, 0x1ec6db6c), TOBN(0xe9a3b18f, 0xafdf29b0),
+     TOBN(0xda633d62, 0xe63a9ae9), TOBN(0x0922b16d, 0x38d13c8c),
+     TOBN(0xaeb7e270, 0x7462c8e6), TOBN(0xa98c96d8, 0xa01b6984),
+     TOBN(0x5586e0d3, 0x297c242f), TOBN(0xbeddd1ab, 0xff587596),
+     TOBN(0x79ac33ce, 0xc02ea084), TOBN(0xe7d06753, 0x8e02ae2f),
+     TOBN(0x05fffd7d, 0x94d526b8), TOBN(0x4590d655, 0x5ebc46d0),
+     TOBN(0xfb79c066, 0x855f85e6), TOBN(0xbb3f0a6d, 0x7400ed08),
+     TOBN(0x46f4c3cd, 0x67fb3683), TOBN(0x62fc1931, 0xd19804cf),
+     TOBN(0x0480e772, 0xd1b6f356), TOBN(0xa5810f25, 0x56320242),
+     TOBN(0x6cf6c9c3, 0x64073c03), TOBN(0x7dfe137b, 0x46a6bfbc),
+     TOBN(0xa5633fa0, 0xba59baf8), TOBN(0xb703e5db, 0x5fd4929a),
+     TOBN(0x09eef835, 0xd7515518), TOBN(0x2e596aa8, 0xa0e3b067),
+     TOBN(0x793831fc, 0x8649bb99), TOBN(0x91cb0057, 0x5ba4c1b6),
+     TOBN(0x44e93dbd, 0x270ec9e8), TOBN(0xbf2ed01a, 0xd139d219),
+     TOBN(0x39697e05, 0xc9d68198), TOBN(0xf04439cf, 0xde2b6894),
+     TOBN(0x65b7a04a, 0x1e6b8e6d), TOBN(0xce3e9425, 0xce35ae6d),
+     TOBN(0x041e0aff, 0x9f102fb3), TOBN(0x91b3a87c, 0x106ae748),
+     TOBN(0xfd969804, 0xc426fa5d), TOBN(0xe624f1cd, 0x28f95b76),
+     TOBN(0x6fe28cce, 0x34f2ea56), TOBN(0xdea55947, 0xd230f37c),
+     TOBN(0xd5e336f2, 0xf92f2742), TOBN(0x86852e3c, 0x1899c751),
+     TOBN(0x5ef2a63b, 0xa5d1bd04), TOBN(0x5f4721a2, 0xb6ca2b79),
+     TOBN(0xbdb27b7c, 0x9f484f78), TOBN(0x2b07bf5b, 0xb085b4ed),
+     TOBN(0x96b8ae73, 0x501b62ab), TOBN(0x0b1e003a, 0x3ba64e23),
+     TOBN(0x43f9ec00, 0x93024347), TOBN(0x3c8c0c7e, 0xae180a03),
+     TOBN(0x58c72237, 0x8c0b21d4), TOBN(0x9d51a996, 0x2b15a1fa),
+     TOBN(0xf5201743, 0xec755eda), TOBN(0x0c845fa3, 0x933800fb),
+     TOBN(0xb6b66cdb, 0x0e82418e), TOBN(0x875258e5, 0x3ae3eeb7),
+     TOBN(0xf2c30b1e, 0x1a8f2b3e), TOBN(0xa10b3843, 0x250f3328),
+     TOBN(0x9f449967, 0xc47c53f8), TOBN(0x5dfe8c76, 0x8775e16e),
+     TOBN(0xb02813a1, 0x336f2194), TOBN(0x90ad3ad5, 0x5636498b),
+     TOBN(0x095acf96, 0xc7c647e0), TOBN(0xc90ef12b, 0x1f57c069),
+     TOBN(0x52f51878, 0x1fb85cc1), TOBN(0x582cfd67, 0x25a125ad),
+     TOBN(0x53b4bfc7, 0x0d43ffad), TOBN(0x143b0b48, 0x04dcf047),
+     TOBN(0x65d16216, 0xd4500bf4), TOBN(0x960c7910, 0x9ab1e4cd),
+     TOBN(0x38b7ef7e, 0xe1d08c70), TOBN(0x64ae69e1, 0x9806e01e),
+     TOBN(0x07468184, 0x6796b923), TOBN(0x6480887a, 0x70af1e64),
+     TOBN(0x4eb2d6fb, 0x02384b34), TOBN(0xb29337a8, 0x05be47f3),
+     TOBN(0xfec96fc0, 0x6b744f9d), TOBN(0xc3de2fb0, 0xc8c9afc3),
+     TOBN(0xe8ccc3eb, 0xcc6dd0a5), TOBN(0x0329a9b9, 0x71d7de7a),
+     TOBN(0x459fbc8c, 0xe357c4f9), TOBN(0x80287f50, 0x025fdc97),
+     TOBN(0xedf1b0aa, 0xa089583d), TOBN(0xb1ad1a57, 0xfb08add3),
+     TOBN(0xd6826d03, 0xe1ae76c1), TOBN(0x3070cd2e, 0x541462c8),
+     TOBN(0x7b03c859, 0x83e6f4da), TOBN(0x5b39a809, 0x24bdb487),
+     TOBN(0x70017570, 0x453bebb7), TOBN(0xfe4e6206, 0xb8ebbfc6),
+     TOBN(0xbb8a1899, 0x106defe3), TOBN(0x6f23dc7a, 0x8683287a),
+     TOBN(0x2cf01995, 0x65d96aed), TOBN(0x4e4cf7e9, 0xdda4ea18),
+     TOBN(0x72ad201f, 0xd2d0316b), TOBN(0xd6451150, 0x61de6cd4),
+     TOBN(0x12432dbf, 0xc84856be), TOBN(0xdd4dca98, 0xd2a8378a),
+     TOBN(0xe70af958, 0xbf881f9e), TOBN(0xd4cd35ad, 0xc4e58ec4),
+     TOBN(0x3889d3d9, 0x5a531924), TOBN(0xac657424, 0xb4ce15ec),
+     TOBN(0xdbe384ca, 0xf41e1344), TOBN(0x9a1aed23, 0x5ab8bb08),
+     TOBN(0x375a041f, 0x8561df1d), TOBN(0x19f7a238, 0xb7685c1c),
+     TOBN(0x8ba59933, 0xa4ba6317), TOBN(0x0c44b6df, 0x271f4aa0),
+     TOBN(0x51f4e88f, 0xbd64e922), TOBN(0x7279df94, 0x9095769f),
+     TOBN(0x098c17b6, 0xeaf8c8d3), TOBN(0xe602ff2c, 0x1aa841d1),
+     TOBN(0xbe4e4926, 0x8b63ce81), TOBN(0x85de277a, 0xfcc79573),
+     TOBN(0x38253d40, 0x5b8304db), TOBN(0x58c50c3b, 0xe422af76),
+     TOBN(0x7f7ec0d1, 0xbf95c27a), TOBN(0xcb7c3a8c, 0x6041df33),
+     TOBN(0xc55595c0, 0x35364c89), TOBN(0xd1a72aa7, 0x2a6eb1e6),
+     TOBN(0x1fa941de, 0xdeb98a3d), TOBN(0x1e9607ab, 0xeff46690),
+     TOBN(0x6633e398, 0xad46a05a), TOBN(0xb99e5784, 0xb585e241),
+     TOBN(0xd63106a4, 0xea558424), TOBN(0xf0a5f939, 0x5df0e501),
+     TOBN(0xba17aaef, 0x59dacce3), TOBN(0x03dc5a07, 0xe907c457),
+     TOBN(0xa59f6d63, 0xa9800bc3), TOBN(0x294a3827, 0x364e1ef7),
+     TOBN(0x741bbab9, 0xdd191356), TOBN(0xe8fe9161, 0xc43954a4),
+     TOBN(0x6a711fa9, 0x65341d90), TOBN(0x09bd0faa, 0xadef2d82),
+     TOBN(0x2112f27e, 0x21ffc303), TOBN(0xcd2214dd, 0x395b69e2),
+     TOBN(0xe4b503c9, 0x8670b06f), TOBN(0x219a678a, 0xc4e13ef4),
+     TOBN(0xc4020eff, 0x4a993816), TOBN(0x00a9f5de, 0x1bac14d5),
+     TOBN(0xd00fce1f, 0xeba7c3a4), TOBN(0x2c6d4993, 0x14b537ab),
+     TOBN(0xe9b2b540, 0x6b898739), TOBN(0xae53e6e3, 0x29dbf826),
+     TOBN(0x634606c7, 0xc8438b2c), TOBN(0x268a9ee4, 0xfabfd429),
+     TOBN(0xb0486aae, 0x173b5583), TOBN(0xf88a2f80, 0xbf222673),
+     TOBN(0x49c56f76, 0x0b3178c8), TOBN(0xeab47059, 0xd77d1406),
+     TOBN(0x95035846, 0x993b1a7a), TOBN(0xd6446e94, 0xa9b83efa),
+     TOBN(0x1d1a71ce, 0xc4424fa8), TOBN(0x8d814c4d, 0x3d08b8d2),
+     TOBN(0xbc3ed8d2, 0x7b9374ac), TOBN(0x8dd2d56d, 0x77a3c020),
+     TOBN(0x93ada735, 0x97efca8d), TOBN(0x072bb2d0, 0x37974cd3),
+     TOBN(0xa7c86e7e, 0x7bd74e40), TOBN(0x7bff5613, 0x5b52e0ed),
+     TOBN(0xc8d0bb30, 0x053af1f1), TOBN(0xc5bdb8f9, 0x840bcb7d),
+     TOBN(0xabdf5f73, 0x41690d1c), TOBN(0x0e857a78, 0xf0edac8c),
+     TOBN(0x59f40fcf, 0x8238cfb0), TOBN(0xdcb54f67, 0x511d41d2),
+     TOBN(0x3f036ac8, 0x0e645117), TOBN(0xdc4e833e, 0x7af5fdcc),
+     TOBN(0x67d859b2, 0x3d7bab2c), TOBN(0x92489b23, 0x5f8b32bd),
+    },
+    {
+     TOBN(0xe412aaf7, 0xb9e2f9f8), TOBN(0x0484c1aa, 0x0ff419ac),
+     TOBN(0x9d944989, 0x417bcb90), TOBN(0x2b73dbe3, 0xfe7318ca),
+     TOBN(0xb91b71e5, 0x52dd7b0a), TOBN(0xd61f8eea, 0x3954afeb),
+     TOBN(0xaaeab13c, 0xa07e3958), TOBN(0xde442032, 0x02a1ff49),
+     TOBN(0x8292d96d, 0x0b054a0f), TOBN(0xa978af88, 0x74b9077a),
+     TOBN(0x70bd185b, 0xfff1d49f), TOBN(0xbe6d0844, 0x0279eab1),
+     TOBN(0xa8fffe45, 0xb8ed07e9), TOBN(0x714824a1, 0xcb920e55),
+     TOBN(0xcd5c628a, 0xaf1bb143), TOBN(0xd151afcd, 0x7637dbb7),
+     TOBN(0x83fb0f37, 0x62d7ee7c), TOBN(0x58c2282f, 0x9a3bcb7e),
+     TOBN(0x79f77476, 0xeac2ca5a), TOBN(0x7e80c351, 0x579a262b),
+     TOBN(0x19e67272, 0xedb4f0fc), TOBN(0xe142bb31, 0x1fbbe9fe),
+     TOBN(0x5c7d7cce, 0x95ea6cc1), TOBN(0x6465a380, 0xabfdcf7f),
+     TOBN(0xa433bd2e, 0x5a26a1d4), TOBN(0x1148bb1d, 0xd1c2d78c),
+     TOBN(0x4aae419e, 0x64102515), TOBN(0xd03b9939, 0x66489384),
+     TOBN(0xe21d58b1, 0xd61a9919), TOBN(0x17618c36, 0x4a0ef3d4),
+     TOBN(0x2519020d, 0x6fe8c0dd), TOBN(0x48d837d6, 0x00b87a75),
+     TOBN(0xe6e067ab, 0x426c1aa6), TOBN(0x431579d2, 0xb11d1280),
+     TOBN(0xb926943f, 0x2ead6552), TOBN(0x8fd692bf, 0x057fed1f),
+     TOBN(0xed11c0ed, 0xe9a98fae), TOBN(0xe2bc967a, 0x9bcb2abd),
+     TOBN(0x1b388d66, 0x68729b8e), TOBN(0x2144d67c, 0x6f74563d),
+     TOBN(0xbe519757, 0x03fcd3bf), TOBN(0x3f9dbd8d, 0xc8c7b62f),
+     TOBN(0xce91fce6, 0xfc476e0e), TOBN(0x2f140c73, 0x2715393a),
+     TOBN(0x8a149a94, 0xf11da35b), TOBN(0xf6a2be5e, 0x5367030d),
+     TOBN(0xb68c0d82, 0x0269def3), TOBN(0x32d58819, 0x8eecb775),
+     TOBN(0xccea6f53, 0x32845ab0), TOBN(0x792bc041, 0x2541c834),
+     TOBN(0xd726425f, 0xb1336aa7), TOBN(0x85b1d21e, 0x3ddd6256),
+     TOBN(0xd575bfa8, 0xd9b1ba0b), TOBN(0xd23084e2, 0xb778b77a),
+     TOBN(0xd44e7399, 0x44bb1010), TOBN(0x3d665388, 0xa91623fc),
+     TOBN(0x5cfd3a69, 0x3f11fc00), TOBN(0x1f2b5d01, 0x8bc8eade),
+     TOBN(0x5160359b, 0xa6b9f7ae), TOBN(0x1e2601dc, 0xfa696463),
+     TOBN(0x7f5ac6d2, 0x915f6084), TOBN(0x6e387789, 0x679176d5),
+     TOBN(0x7fb99f4b, 0xae26abee), TOBN(0x4798a2fc, 0xaa409d22),
+     TOBN(0x582164f7, 0x5965615c), TOBN(0x2c9dfb60, 0x0472cbeb),
+     TOBN(0x36eacc3f, 0x2266724f), TOBN(0x253eb08c, 0x5fcb8868),
+     TOBN(0x749a0577, 0x760c15b3), TOBN(0x71e4ce1e, 0x5686b036),
+     TOBN(0x47893a8f, 0xb710196a), TOBN(0xe27dfbac, 0xdf51c4e8),
+     TOBN(0xc9536d6b, 0xffb3df08), TOBN(0xc95169ce, 0x6dde9e09),
+     TOBN(0xcb050de7, 0xcc085766), TOBN(0x92fce77e, 0x0df088dc),
+     TOBN(0x10c124ca, 0x88781592), TOBN(0x6429d30b, 0xc81030f3),
+     TOBN(0x2e37721d, 0x09e20c50), TOBN(0x43e7f9ca, 0xf3e3d604),
+     TOBN(0xa277a87e, 0x5b095e01), TOBN(0x968bc951, 0x83a51a95),
+     TOBN(0x3b375d45, 0x53aff355), TOBN(0xb79d7cce, 0xe1ebac06),
+     TOBN(0xd929e1a6, 0x022995b4), TOBN(0x228cf7f4, 0x28164ff7),
+     TOBN(0x7bd12900, 0x5d3e4608), TOBN(0xc57ac873, 0x2f97ebd8),
+     TOBN(0xc192342d, 0x86b383b5), TOBN(0xe85f303f, 0x706b01d5),
+     TOBN(0x19e19213, 0x88cdcb89), TOBN(0xe88f1943, 0x2ce0de2a),
+     TOBN(0xf6fcf8cf, 0xe453aecc), TOBN(0x0dcd10b8, 0x9a67b49f),
+     TOBN(0xb93d5b4d, 0xafece961), TOBN(0xe232f34a, 0xc39d0b53),
+     TOBN(0x1b8f6cc3, 0x30735384), TOBN(0xc35c5a82, 0xe4f26c08),
+     TOBN(0x9e0c933b, 0xba98f626), TOBN(0x49868100, 0x4c70aed7),
+     TOBN(0x711a3aad, 0xb7f26c66), TOBN(0x786ea28d, 0x7dac506b),
+     TOBN(0xd3a7ab1e, 0x43935657), TOBN(0xda7f5c1f, 0xd1b69e9e),
+     TOBN(0xc08c85e5, 0x0e6c8579), TOBN(0x29d04ad4, 0x8d991759),
+     TOBN(0xbae8f163, 0x3a8ccd69), TOBN(0xade66539, 0x1790a49c),
+     TOBN(0xf9f5bc8c, 0x45915cc1), TOBN(0x63461cf0, 0x4f2b18c3),
+     TOBN(0xceb75a9c, 0xd236e848), TOBN(0xac653e3b, 0x847ce6c2),
+     TOBN(0xb93b3032, 0xdb088764), TOBN(0x567fe1c3, 0xa78e5943),
+     TOBN(0xba7a7acf, 0xe359cb34), TOBN(0x38f4fbfd, 0xe2c3827e),
+     TOBN(0x761c36d4, 0xc90abad2), TOBN(0xac1af4e7, 0x75027c02),
+     TOBN(0x95e6d01c, 0xd4715572), TOBN(0x5b06cf39, 0xd621145d),
+     TOBN(0x799acd7c, 0x64ca2efc), TOBN(0x3397a15b, 0x4e0bcb6c),
+     TOBN(0xb9b10ced, 0x0358a26c), TOBN(0x0a30dbbe, 0x4b8ddfaa),
+     TOBN(0xa70e9712, 0xe20f6fac), TOBN(0x87c7f732, 0xd11451b0),
+     TOBN(0xf0c967b1, 0xd5eece8f), TOBN(0xbc62882a, 0xab370e2d),
+     TOBN(0x134fb08e, 0x59ddb7cb), TOBN(0xe937c663, 0x3ae8f816),
+     TOBN(0x083f73a7, 0x802ed184), TOBN(0xd4badd85, 0x8cd69f8d),
+     TOBN(0x2d8bfaf5, 0x987f389e), TOBN(0x5338c056, 0x4454b1f2),
+     TOBN(0xdce38439, 0x2f104468), TOBN(0xffd94d27, 0x83c5278b),
+     TOBN(0x8740af50, 0x5628ad08), TOBN(0x30a233db, 0x8b1284e8),
+     TOBN(0xb3982d73, 0x57acc8cd), TOBN(0x211d53d3, 0x37c5ff03),
+     TOBN(0xb6371f1b, 0xf6578d40), TOBN(0x7f749bea, 0xa80dec53),
+     TOBN(0xe6b3f730, 0xa9816ec0), TOBN(0xd26832fd, 0xf5423ec6),
+     TOBN(0x80127368, 0x63e27b64), TOBN(0x17b7a4b2, 0xd2d21879),
+     TOBN(0x7dcced37, 0x43cf40d4), TOBN(0x999bbb80, 0x97cf7c4c),
+     TOBN(0x191c84e5, 0x6bafa0b0), TOBN(0x1d08c049, 0x917f6b17),
+     TOBN(0x02e5fe53, 0xf4715c99), TOBN(0xa92c6085, 0x0658f1de),
+     TOBN(0xe9c0ba85, 0x16a010bc), TOBN(0x2fd90fba, 0xea4f3e8e),
+     TOBN(0x8af18371, 0x4570a1e5), TOBN(0xe869e8f7, 0x7cca9004),
+     TOBN(0xe2c8afb7, 0x2dd83019), TOBN(0xb877995d, 0xfd99b386),
+     TOBN(0x1e3efc16, 0xf5adab87), TOBN(0x93105fe4, 0xaa3b191a),
+     TOBN(0x21690dca, 0xae504c31), TOBN(0x2d51ead4, 0x698f629d),
+     TOBN(0x2af3eef1, 0x724c9cbf), TOBN(0xa6181e60, 0x81a0d4aa),
+     TOBN(0x580982c7, 0xa94f6b05), TOBN(0xe8bea903, 0x48653ad7),
+     TOBN(0x0270614c, 0xa608598d), TOBN(0xa7cae0f0, 0x3d0d5360),
+     TOBN(0x81407687, 0x96067f64), TOBN(0xab2c2706, 0x77a62d7d),
+     TOBN(0xbe9c1edf, 0xae19786b), TOBN(0xa313f2b2, 0x887814eb),
+     TOBN(0xe2bc4c1f, 0x08fd3c04), TOBN(0x25387129, 0xe5a9d032),
+     TOBN(0x7b3ced22, 0x8fbc5030), TOBN(0xc22bea3b, 0xadbf1bdc),
+     TOBN(0x4f6b6b6d, 0x7b1308da), TOBN(0x0f2faaaf, 0xd0e33069),
+     TOBN(0xb461990f, 0x0d3677c4), TOBN(0x55c9df43, 0x0e6a876b),
+     TOBN(0x5ce9aaa4, 0x316d252a), TOBN(0x7d98a813, 0x0e8e5097),
+     TOBN(0x047ecd13, 0x9aa3343e), TOBN(0x15cc7072, 0x939277e1),
+     TOBN(0x305165d1, 0x0a020be7), TOBN(0x48560411, 0xf66eaf8b),
+     TOBN(0x5ff898dd, 0xffd2380e), TOBN(0x7da35f08, 0x784b4b11),
+     TOBN(0x50f53e2c, 0x38fd05c7), TOBN(0x64b3ee82, 0x47ada3a5),
+     TOBN(0x672ae316, 0x678995de), TOBN(0x74707460, 0xdfe96605),
+     TOBN(0xb346dc71, 0x441e7150), TOBN(0xd9505e7a, 0x55fd483c),
+     TOBN(0xca96e59f, 0x94302331), TOBN(0xcfde701c, 0x801930cf),
+     TOBN(0x02fc9936, 0x73c31e5d), TOBN(0x4ef53a55, 0x8cda0b51),
+     TOBN(0xa934e268, 0xa269a1f3), TOBN(0x7ba4e5e0, 0x7cca8542),
+     TOBN(0x4c6408f9, 0xa2ae339c), TOBN(0xf9ea4cb2, 0x5a70ba33),
+     TOBN(0x3eaa9364, 0x5cac2af4), TOBN(0x62686d46, 0x95eaea09),
+     TOBN(0x5196e88f, 0x3e771722), TOBN(0x749518e8, 0x7108b198),
+     TOBN(0x394107c4, 0x29b25254), TOBN(0xf9945ac1, 0x3a315aad),
+     TOBN(0xce15c84d, 0xaab9dbe5), TOBN(0xebb54d52, 0x3940eb15),
+     TOBN(0x69b649c7, 0xa2fdd11d), TOBN(0x4e2d1782, 0x3f6ade80),
+     TOBN(0x0f53ac9c, 0x2327f7d8), TOBN(0xf6158d6e, 0xc79eb564),
+     TOBN(0x2903bfc0, 0x4536f5c1), TOBN(0x0a25518b, 0xfb9e9e07),
+     TOBN(0x70cbce8b, 0x62a0b0ed), TOBN(0x92f5dc33, 0x0abbc9be),
+     TOBN(0xbb92b7d3, 0xf369c2d6), TOBN(0x70dd90c8, 0x79ef83e1),
+     TOBN(0xe0b33153, 0x7937ab45), TOBN(0x3a8d1f74, 0xc054af6d),
+     TOBN(0x35cf7380, 0xb05ebfc4), TOBN(0xefb8dac2, 0x58c2cd0c),
+     TOBN(0xe7316f99, 0x7d665d26), TOBN(0x59a7ead9, 0x800fba6f),
+     TOBN(0xfa4d2a2a, 0x08a2cb88), TOBN(0x2e7d3bab, 0xb441995d),
+     TOBN(0x390988c9, 0x93046f2b), TOBN(0xfd95b86e, 0x08869cf5),
+     TOBN(0x0185b6be, 0x9a76537b), TOBN(0xa89563bd, 0xb6cd3d59),
+     TOBN(0xe79a4f63, 0xecb1ad25), TOBN(0x1857cec7, 0x6948504d),
+     TOBN(0x03b7b3ad, 0xa497922f), TOBN(0x9df2f2e4, 0x38930f36),
+     TOBN(0x355e4a7a, 0x4bb5927c), TOBN(0x5ad3fd47, 0x636ec349),
+     TOBN(0x5400730d, 0xc41b19ca), TOBN(0xbfeabac1, 0x555afa93),
+     TOBN(0xb6232083, 0x6cca58b7), TOBN(0x55faae6b, 0x76d0c53e),
+     TOBN(0x64ef60e2, 0x40a8eb5a), TOBN(0xc68bc678, 0xe8f22c94),
+     TOBN(0x5156dc1c, 0x10a0416e), TOBN(0xac779644, 0x5c2037e4),
+     TOBN(0xd2e30925, 0xc7162aaa), TOBN(0x7bb5275f, 0x2cf21e2f),
+     TOBN(0x7722cb40, 0x0c11e65a), TOBN(0xc94a7f52, 0x68ff2be5),
+     TOBN(0x420085cc, 0x8d9f9352), TOBN(0x4addb986, 0xca4b2544),
+     TOBN(0x3c6ceac0, 0x06264a47), TOBN(0xebc01a03, 0xe2b48ccc),
+     TOBN(0xc430e7ab, 0xea94fef2), TOBN(0x973bb6f0, 0xbd94aa8a),
+     TOBN(0xd60e5feb, 0x3225b585), TOBN(0x6cbab39c, 0x01b56ad1),
+     TOBN(0xcb55a9cc, 0x37d6d1b4), TOBN(0xd7288c1e, 0xfbce1d89),
+     TOBN(0xcb516843, 0x162d4b46), TOBN(0xf0aca3a6, 0x15edb910),
+     TOBN(0xdb998b55, 0x08a6685a), TOBN(0x16b442e6, 0x07811873),
+     TOBN(0xa9badd09, 0xa1a7e0c2), TOBN(0x0a9a339b, 0x9f813289),
+     TOBN(0xabf1793f, 0xd4cda45b), TOBN(0xa9830a12, 0xc7378a84),
+     TOBN(0x1ae11c32, 0xd28165b1), TOBN(0xbfd49ace, 0xf71bca14),
+     TOBN(0x9a3990df, 0xfc035476), TOBN(0x0fd2b153, 0x6c32b72a),
+     TOBN(0xceece835, 0x3541b5ae), TOBN(0x2f7429f5, 0x8256c750),
+     TOBN(0x456c3478, 0x88104f8c), TOBN(0x8a435588, 0x8b23da06),
+     TOBN(0x6b6c14f2, 0xd817ce6a), TOBN(0x83bf0acb, 0xf35ab86a),
+     TOBN(0xdadb89ba, 0x364b83fd), TOBN(0x2c8fcf90, 0x5cfecaf3),
+     TOBN(0xa90f77ca, 0x20d12c92), TOBN(0x2e278e0e, 0x69d1739c),
+     TOBN(0x29d24b44, 0x5c1f9e82), TOBN(0xbf4fb4cb, 0x647c59b1),
+     TOBN(0x9c8ea39d, 0x90ffd733), TOBN(0xe37a1352, 0xf14db3fc),
+     TOBN(0x3c9164a2, 0x8f3e1dca), TOBN(0x515c16f2, 0xaec86440),
+     TOBN(0x736fee4c, 0x5c483906), TOBN(0x2325cabb, 0xa3f651c7),
+     TOBN(0x582324df, 0x35b94e45), TOBN(0xeacedb3a, 0x45598c64),
+     TOBN(0x674e1740, 0xde9ea8cd), TOBN(0x30f2f423, 0x89d2b975),
+     TOBN(0x330bd76d, 0x9c8abe45), TOBN(0xb97e89f6, 0x5371c0c4),
+     TOBN(0xb1769248, 0xb7569543), TOBN(0xd29cc9d2, 0xd85f4d72),
+     TOBN(0x89e1fd0c, 0x10a5b6dd), TOBN(0x501be0ae, 0xa693a796),
+     TOBN(0xc70965b6, 0xe490e600), TOBN(0xf518f8af, 0x1bb6c5cd),
+     TOBN(0xf51d40bb, 0x76f6daa2), TOBN(0x83a83b67, 0x5ec7849c),
+     TOBN(0x0fe0d975, 0x6d8aa314), TOBN(0x9bf9aed5, 0xea664a8c),
+     TOBN(0xef8bb989, 0x96fad9aa), TOBN(0xd07dce35, 0x04a0e441),
+     TOBN(0x53bd2a16, 0xb3c5eb81), TOBN(0x49e29fe2, 0xaf178b66),
+     TOBN(0x62cf7a62, 0x24dced32), TOBN(0xcc111fba, 0x0f541e36),
+     TOBN(0xc93cd7c1, 0xda9dd111), TOBN(0x56b625ab, 0x28c9c1b4),
+     TOBN(0xeff436ae, 0x3769f3a2), TOBN(0xa0d8d46b, 0xcbd31a17),
+     TOBN(0x241693fa, 0xc80dc873), TOBN(0x56083f64, 0x3cd579ab),
+     TOBN(0x12ee753b, 0x33fbd431), TOBN(0x1bde60ad, 0xd66c283a),
+     TOBN(0x0db508dd, 0x0243cd83), TOBN(0x3b12c134, 0x1349307c),
+     TOBN(0x8296aa6d, 0x61d86bda), TOBN(0x1d5c8a4f, 0x630adc96),
+     TOBN(0x9d01dc28, 0xa30a8ae6), TOBN(0xc555a743, 0x1dab8168),
+     TOBN(0x61fe0d14, 0x7abe577a), TOBN(0xe26aa4d8, 0xc8c93bb7),
+     TOBN(0xfb4b03bf, 0xda2bab5b), TOBN(0xfbd49089, 0x79b4e6c0),
+     TOBN(0xda1a0108, 0x86806aa4), TOBN(0x281f76ae, 0xdc078112),
+     TOBN(0x9f662594, 0xe0fbd693), TOBN(0x1da897b0, 0x49ec4ee0),
+     TOBN(0x20d52a97, 0xfc7d1578), TOBN(0xdbf8d157, 0x6b1f4ab4),
+     TOBN(0xfc0a5936, 0x3b97d1e3), TOBN(0x00f0f283, 0x1aa091b6),
+     TOBN(0x505e183e, 0x13aadeb0), TOBN(0xe28041ad, 0xa55b3f8a),
+     TOBN(0x2e0f76da, 0x086c2d23), TOBN(0x815b147d, 0xf2c5eceb),
+     TOBN(0x02066c02, 0x673ba5f2), TOBN(0xb85d6a8a, 0xce043d4d),
+     TOBN(0xd5f023a3, 0x113890f6), TOBN(0xaa4f9058, 0xa9d2491b),
+     TOBN(0x6d82393e, 0x16d175a3), TOBN(0x1d1e00b2, 0x671e2aed),
+     TOBN(0xd47c4f28, 0x40018bae), TOBN(0xd08eac83, 0x7b30838f),
+     TOBN(0xa0fde631, 0x5dfe910d), TOBN(0xfc16adf7, 0x5c66d5c6),
+     TOBN(0x0ed2a8a2, 0x18d8c6b1), TOBN(0x67ee6037, 0x632b5b07),
+     TOBN(0x7eed42e5, 0x21a89b8d), TOBN(0xd99942cf, 0x33e6da02),
+     TOBN(0x759ec79e, 0x39971405), TOBN(0x669a92c7, 0x174dca4c),
+     TOBN(0x85935ed7, 0x9d1e7c55), TOBN(0x5f3f9e68, 0xa82055c0),
+     TOBN(0xab0507c8, 0x56aa5af3), TOBN(0x354cac5d, 0x1bd2726f),
+     TOBN(0x46e85e16, 0xb864816f), TOBN(0xef2548f6, 0xd1840add),
+     TOBN(0xe494ea07, 0xc3842f43), TOBN(0xa6169c4a, 0xedf6c13a),
+     TOBN(0x65d9cca3, 0xa460e30b), TOBN(0xa6848e4f, 0x31e7dfc3),
+     TOBN(0x4309f315, 0x5c8109dd), TOBN(0x7a4ec14e, 0xc5799833),
+     TOBN(0xcb768a63, 0xa8132b78), TOBN(0x229106d1, 0xb416c77c),
+     TOBN(0x1ca71df6, 0xded02f41), TOBN(0xb6365d3e, 0xc1a1fc66),
+     TOBN(0xf7c432a1, 0x1431d1fa), TOBN(0x30364500, 0xa5654387),
+     TOBN(0xc9ed0cf8, 0xd5b13b2e), TOBN(0xdbd541bb, 0xd18d5a28),
+     TOBN(0x6b78c887, 0x754de9d2), TOBN(0x7d32fedb, 0x54651568),
+     TOBN(0x7f319680, 0x0d37c339), TOBN(0x22304d1f, 0x37d70b76),
+     TOBN(0x01b2709e, 0x6fb5e555), TOBN(0x978b0d3e, 0xfd5d1708),
+     TOBN(0x83206b9d, 0x96bc118d), TOBN(0xb1a4d7bf, 0xec7bfc1c),
+     TOBN(0x753f98a6, 0xb6b41502), TOBN(0x41139110, 0x4c5187ce),
+     TOBN(0x56e9e218, 0x587a8213), TOBN(0x3b39955b, 0xad9aefd0),
+     TOBN(0x7428b03f, 0xb9947ceb), TOBN(0xbbe82668, 0xbe8bda29),
+     TOBN(0x5142e8ba, 0x5c4b4c63), TOBN(0x90c3e2e3, 0xe92ee291),
+     TOBN(0x6947a55a, 0x8f6a076d), TOBN(0x9acdeec1, 0x61964435),
+     TOBN(0x56bc8e4c, 0x181dac35), TOBN(0x4f4f2c0a, 0x7a824372),
+     TOBN(0xd1958b99, 0xc1033f6b), TOBN(0xeeaa6604, 0xc83ecf98),
+     TOBN(0xe43c0b44, 0xaca52cb3), TOBN(0x12446426, 0x75443f14),
+     TOBN(0x0d14e885, 0xddcc00b4), TOBN(0xb0f5f11d, 0x6cfe5734),
+     TOBN(0x0e160164, 0x1013afcb), TOBN(0x4f570ca9, 0xed9f4535),
+     TOBN(0xe5162a12, 0x73a307ad), TOBN(0x6a431695, 0x3321ae54),
+     TOBN(0xa6c7b0c5, 0x5ae301b4), TOBN(0x6f5d42b1, 0xbd2d3f1d),
+     TOBN(0x4eb12c09, 0x15c0c94b), TOBN(0xf1c40386, 0x28618c41),
+     TOBN(0x30302333, 0xc0f55c25), TOBN(0xa5e41426, 0xbd1c19f0),
+     TOBN(0xd5d4d4d7, 0xcfcc66f8), TOBN(0xcfdf3039, 0x449253c5),
+     TOBN(0x17b0eb72, 0xb30ec0ff), TOBN(0xbce593e2, 0x5e6424f9),
+     TOBN(0xa5d82937, 0x2a73184e), TOBN(0x23d2857a, 0xebe58773),
+     TOBN(0xe3f0f676, 0x067e1eac), TOBN(0x073ded2d, 0x50509d7f),
+     TOBN(0xc22af8f0, 0xca405a7e), TOBN(0x7a4ef592, 0x6df6a46c),
+     TOBN(0xf9cb0178, 0x97067006), TOBN(0x9ae132af, 0x489d2a39),
+     TOBN(0xc7c46b35, 0x6a2da1c1), TOBN(0x0993353b, 0xd95850c9),
+     TOBN(0x6c313a57, 0xa25d52ef), TOBN(0xa6bdb2b2, 0x93c852c3),
+     TOBN(0x27ed916b, 0x7e9e296d), TOBN(0x10b58337, 0xc7aeb09b),
+     TOBN(0x78800c35, 0xecebe36e), TOBN(0xd93e2423, 0x2234ce8a),
+     TOBN(0xe4cf5cee, 0xfa95019f), TOBN(0x21396d37, 0x71e13748),
+     TOBN(0xeb028350, 0x0c32fdad), TOBN(0x31645697, 0x61f1652b),
+     TOBN(0x9e1c6e0b, 0xf6677491), TOBN(0x4d18f2e5, 0x74176c12),
+     TOBN(0x78d559bf, 0x3832d713), TOBN(0x04f0b57b, 0xb6e00e15),
+     TOBN(0xd6c9cb16, 0xe80add3a), TOBN(0xeabfabc5, 0x5c7b1d70),
+     TOBN(0x40570866, 0x98a62cc3), TOBN(0x39ef8ff1, 0x4abb2b1a),
+     TOBN(0xadb40548, 0x0c19959c), TOBN(0xd61632d7, 0x388b1f7c),
+     TOBN(0xd1f9b736, 0xd73b7d50), TOBN(0x652ed78e, 0x560bf0aa),
+     TOBN(0x58e71e33, 0x50e3fc4f), TOBN(0xbfaf5f44, 0x55df1ad1),
+     TOBN(0xefe8893b, 0x9106744f), TOBN(0xabfbd51e, 0x356d1fe9),
+     TOBN(0xab03570b, 0x9eb1cbaf), TOBN(0x92cfe2e4, 0x3919012c),
+     TOBN(0x7671e5fb, 0xb6f7c64d), TOBN(0xf040c039, 0x6e0a44b7),
+     TOBN(0xf430f593, 0x62b36088), TOBN(0xa85b4bc9, 0x94c7c0ac),
+     TOBN(0x07d5c40c, 0x16b54fff), TOBN(0x47aa73ee, 0xc53a3788),
+     TOBN(0xa63c5b36, 0x7000d61e), TOBN(0x04e8f53d, 0x91b9f09f),
+     TOBN(0x7e48021d, 0x87dc6a3d), TOBN(0xa2b5516b, 0x28ae0f74),
+     TOBN(0x84412003, 0x705132e2), TOBN(0xc55f69cf, 0xe243d1fa),
+     TOBN(0x758c0f71, 0x6a8f80bd), TOBN(0x69ecf887, 0xd09b719d),
+     TOBN(0x51b100f0, 0xa9b45194), TOBN(0x1fb9ef66, 0x90ae0769),
+     TOBN(0xfee82fab, 0x30fcdfd2), TOBN(0xf36185be, 0x36a6990b),
+     TOBN(0x88f343f6, 0x3d33027b), TOBN(0xb775dcbb, 0x38ae16c6),
+     TOBN(0xa107b9f0, 0x85a82e45), TOBN(0xaff8b0ae, 0xde6b9806),
+     TOBN(0x3cd3980f, 0x0392fad0), TOBN(0xdd829fc6, 0xf3cf7650),
+     TOBN(0x177190cc, 0x0dc8d031), TOBN(0x3e21cd25, 0x7fc491eb),
+     TOBN(0xea0cc90e, 0x0d929039), TOBN(0x5f7e6292, 0x1dfc37b3),
+     TOBN(0x66dd6dde, 0xe23bdd04), TOBN(0x70e7a317, 0x64fa490a),
+     TOBN(0x59c90f81, 0x10a03dd8), TOBN(0x425ee6ce, 0x96d58314),
+     TOBN(0x868001eb, 0x5f896ed1), TOBN(0xc4c003f5, 0x91dad4fd),
+     TOBN(0xfb4782b2, 0xd9ef80b4), TOBN(0xb9edb975, 0x323e4fc5),
+     TOBN(0xa2ec9b6c, 0x53ef4ccc), TOBN(0x4af8b2ca, 0xa77922b6),
+     TOBN(0x73850e89, 0x6697874b), TOBN(0x76e0fd72, 0x3568523f),
+     TOBN(0x64799f46, 0xe9c400a6), TOBN(0x6c5176e7, 0xa9c245de),
+     TOBN(0xbd97c80c, 0x93503700), TOBN(0xa92d9ee5, 0xffbe539f),
+     TOBN(0x76003d14, 0x8376bb3b), TOBN(0x2e75cc77, 0xac564679),
+     TOBN(0x126af6c7, 0x3a333970), TOBN(0xdbfd0133, 0x6b6604bd),
+     TOBN(0x11cf4c2e, 0x24424a48), TOBN(0x843c73ee, 0x37d4471c),
+     TOBN(0xb3047fc5, 0x617a488b), TOBN(0xf2a91709, 0xe3cf861c),
+     TOBN(0x84444421, 0x1c3a60f7), TOBN(0x74787a36, 0x26679148),
+     TOBN(0x115fbd06, 0x53d9404b), TOBN(0x70fd3365, 0x6244cef0),
+    },
+    {
+     TOBN(0x76695c9b, 0x2b574b7f), TOBN(0xcca80405, 0xc369b6be),
+     TOBN(0x1f4bae99, 0xe3108ded), TOBN(0x9e715ce2, 0xea133fce),
+     TOBN(0x60d52055, 0x54c2ee1c), TOBN(0x56bab301, 0x1680742e),
+     TOBN(0xa409b5f6, 0x3fe438b9), TOBN(0xe3a8e4d0, 0x8036f7ce),
+     TOBN(0xe1d7ec0f, 0x247fdfdf), TOBN(0xfb9d90e7, 0x4a23d1dc),
+     TOBN(0x7012eb2c, 0x190fdc41), TOBN(0x5c2bbff6, 0xddced48c),
+     TOBN(0x8a93426a, 0x68cd7feb), TOBN(0xb5963962, 0x6b4854e1),
+     TOBN(0x8ac72b8e, 0xe772bbd8), TOBN(0xc10d24d2, 0xa6b3040a),
+     TOBN(0x8fdfef16, 0x94d5f347), TOBN(0xf3189490, 0x2b04af0a),
+     TOBN(0x30e3da7a, 0x6d2ca633), TOBN(0x8d002aea, 0x4803814a),
+     TOBN(0xc15e311f, 0x95a0bfe9), TOBN(0x2891ec7e, 0x4b4cc50c),
+     TOBN(0x0936fed8, 0x8834df25), TOBN(0x7e5d7dbf, 0x78e00289),
+     TOBN(0xb9a92d78, 0xfbfcf1b5), TOBN(0x17ce4fab, 0xe8427d74),
+     TOBN(0xbae98ffd, 0xac66e74e), TOBN(0x6d548304, 0x145bb5e5),
+     TOBN(0xbf3dc603, 0x0992abe1), TOBN(0x318cfbda, 0xbefdc5c5),
+     TOBN(0xbb5fa37d, 0x59f8efb8), TOBN(0x347874a0, 0x4ef5bef8),
+     TOBN(0xdf552b01, 0xbf68688b), TOBN(0x2fc542cb, 0x8f96a57a),
+     TOBN(0x5a731b61, 0x4edb340e), TOBN(0x5143d103, 0x181cf578),
+     TOBN(0x749ab511, 0x2cc936b6), TOBN(0xbc94c053, 0x0dd355c2),
+     TOBN(0xa825eff5, 0xa3900fa2), TOBN(0x60a909a3, 0xc1dc2b31),
+     TOBN(0x59b33c78, 0xaf5bcab5), TOBN(0x0053d789, 0x496fbcdf),
+     TOBN(0x5a5afe02, 0xd7883bc1), TOBN(0xec9afe78, 0xfa66951d),
+     TOBN(0x38f28b83, 0x728e56a6), TOBN(0x21d0b6ac, 0x78cafb9d),
+     TOBN(0xd43996bc, 0x7042e327), TOBN(0x60686637, 0x7c31c145),
+     TOBN(0xe1f8d2e6, 0x3d919304), TOBN(0x09cf437c, 0x456be82a),
+     TOBN(0x6a01dae8, 0xf0c21973), TOBN(0x8bffcda8, 0x246d9ef8),
+     TOBN(0x7e03a0d4, 0x5d853975), TOBN(0xc3800ca8, 0x32533ba3),
+     TOBN(0xd77152cc, 0xf02ce43c), TOBN(0xb8bc17a6, 0x6392089a),
+     TOBN(0x6f5fcb61, 0x4b4558fb), TOBN(0x9602597b, 0x1f2545aa),
+     TOBN(0xfd89ab3f, 0xabe5e469), TOBN(0xf1daeea2, 0xfb2e16bc),
+     TOBN(0xe699acd7, 0x3a12940f), TOBN(0x24980f6c, 0x4d7c7311),
+     TOBN(0x4a5cf975, 0x336c8ec6), TOBN(0x8e180e32, 0x8c27d3dc),
+     TOBN(0xafb66269, 0xd36cb503), TOBN(0xe98b07d2, 0x754fdd67),
+     TOBN(0x1e0b425b, 0x5a1fe9bf), TOBN(0xb4ac13e9, 0x24fc4a85),
+     TOBN(0xef693781, 0xc05a9c3f), TOBN(0x266c1216, 0x5c0124dc),
+     TOBN(0x7f3184c4, 0x64ee22e2), TOBN(0x3f985fb3, 0xcdb5f1a9),
+     TOBN(0xb258cd5f, 0xfc01efaa), TOBN(0x861688b1, 0x0775588e),
+     TOBN(0x72184b18, 0xfa46eae0), TOBN(0xd17c9dea, 0x5003404a),
+     TOBN(0xa8791966, 0x92e7bf9e), TOBN(0x049c63cb, 0x7891ac50),
+     TOBN(0x2ed32928, 0x5d46b33d), TOBN(0x49d1bfbf, 0x0623595a),
+     TOBN(0x9f871470, 0x36c8e3e9), TOBN(0xdec7eb98, 0xb20d610d),
+     TOBN(0x15b9326f, 0x7b151f4e), TOBN(0xa624c23e, 0x04005d02),
+     TOBN(0x89fc2a8e, 0xd9cacded), TOBN(0x9eb8defa, 0x9a2c3a00),
+     TOBN(0x7c5dc2d6, 0xe8d7eab7), TOBN(0x48fa5403, 0xeb0a77cf),
+     TOBN(0xcc4c31d0, 0xbf033733), TOBN(0xf37d0072, 0xef211c17),
+     TOBN(0x8967fe49, 0xae35b246), TOBN(0x8c4cbd66, 0x5cb1aa9b),
+     TOBN(0xab0097db, 0x04840da3), TOBN(0x3946faec, 0x5828733e),
+     TOBN(0x96c6531e, 0x87d64045), TOBN(0x893d3780, 0x83bc0d0e),
+     TOBN(0xf833e355, 0x53bec0dc), TOBN(0xc9ff7280, 0x2803a655),
+     TOBN(0x300ff7aa, 0x42b99b53), TOBN(0x3b48a8db, 0x6a7c3f2c),
+     TOBN(0xf78c21d9, 0xf617f8aa), TOBN(0x23684cb7, 0xcbe4d565),
+     TOBN(0xf64ae9c8, 0x7514e9a0), TOBN(0x4ff5483c, 0x8429d8ba),
+     TOBN(0xdedab351, 0x5cb18391), TOBN(0xd3126ffc, 0x769ae948),
+     TOBN(0x6c2f9ba8, 0xd3546ad9), TOBN(0x4567e48a, 0x69aabfb7),
+     TOBN(0x6fbe29b0, 0xaa284747), TOBN(0x3185f0db, 0x98af4f2f),
+     TOBN(0xf2a958a2, 0x5b4c14e3), TOBN(0x106150c5, 0x27d04855),
+     TOBN(0x60a3b4fb, 0x68a19ca9), TOBN(0x65c5719a, 0xfac47c70),
+     TOBN(0xe228e088, 0x973e4cfd), TOBN(0x122a2429, 0xcb63c89f),
+     TOBN(0x39fda97e, 0xbaea08f7), TOBN(0xe7da5324, 0x621c12cb),
+     TOBN(0x569c8a51, 0xff9b3c84), TOBN(0x5ab8bb6d, 0x4c3b8d54),
+     TOBN(0x4f02ece4, 0x00e25a95), TOBN(0xef947402, 0x7ac1732e),
+     TOBN(0xecdb65ac, 0x51149260), TOBN(0x6043aa29, 0xa9180d51),
+     TOBN(0x07fc92bd, 0x852deca0), TOBN(0xf3338297, 0x15237c8d),
+     TOBN(0xecfb0e76, 0xe84b3f38), TOBN(0x21f2f5c5, 0x6b89af17),
+     TOBN(0xf7aec268, 0x9659963f), TOBN(0x67fb5260, 0xa0cb213c),
+     TOBN(0x5daa0fef, 0x66d931b7), TOBN(0x95457a7e, 0x34d309ff),
+     TOBN(0xe7cf1a56, 0xc21285b6), TOBN(0xcbff9b08, 0x244e11b4),
+     TOBN(0xd79ee62d, 0xc0ecce3d), TOBN(0xe3f20739, 0x8267c254),
+     TOBN(0xee06dd39, 0x037ef2d3), TOBN(0x790d1b0f, 0xd522f762),
+     TOBN(0xf0659106, 0xf30c47d0), TOBN(0xcd83214b, 0xb5fdc6b5),
+     TOBN(0xc8621660, 0x6593b717), TOBN(0xb10a6d99, 0xfe3fa381),
+     TOBN(0xa5c3224c, 0xab254244), TOBN(0xd15287e6, 0x5854b18e),
+     TOBN(0x6bf9594c, 0x225806ae), TOBN(0x75a97e21, 0x57e554f2),
+     TOBN(0x0ea199f3, 0x82b00b16), TOBN(0xde81a726, 0x5389c90f),
+     TOBN(0x8503609e, 0x86922afe), TOBN(0x6778ad88, 0x254b75c3),
+     TOBN(0x6bc2ac1b, 0xf3e660ba), TOBN(0x7efc1550, 0x209c04a4),
+     TOBN(0x6e90b6a5, 0x2528ec51), TOBN(0x9196a7c9, 0x0548389e),
+     TOBN(0xf7e285c1, 0x7b5b5dde), TOBN(0x6335a624, 0x223d4837),
+     TOBN(0x8acef5af, 0x412d19c4), TOBN(0xb22808a5, 0x9783256b),
+     TOBN(0x6ea3daaa, 0xf53e4b62), TOBN(0x7ca4c51b, 0xfa7bada4),
+     TOBN(0x3e40461e, 0xe4d3115e), TOBN(0x24889b50, 0x3646fc40),
+     TOBN(0x39e0eb1e, 0xfa26ccf7), TOBN(0xfcad5d47, 0xa82af350),
+     TOBN(0x90037503, 0x4862b1fd), TOBN(0x88e937e8, 0x1a79283c),
+     TOBN(0x16dd07c0, 0x9a0127fb), TOBN(0xac62a168, 0x39fca31a),
+     TOBN(0x26542e2a, 0xa294dac9), TOBN(0xefab45af, 0x2a5dcfe8),
+     TOBN(0x6166857d, 0xe642bbe8), TOBN(0x3f3ad480, 0xff6290a8),
+     TOBN(0x435d4c2b, 0x5f50633f), TOBN(0x36da60a7, 0x84451c8b),
+     TOBN(0x00f5e2e4, 0x261612e4), TOBN(0xe4318273, 0x2d04786a),
+     TOBN(0x192bcda5, 0x2c175edb), TOBN(0x74681e0a, 0x59a6f637),
+     TOBN(0x696df08b, 0x2d244985), TOBN(0xde61a87c, 0xfcf577c6),
+     TOBN(0xcbd2ceab, 0xf2c9de81), TOBN(0x878f06ce, 0xd36162e8),
+     TOBN(0xc4f312e0, 0xb3d22955), TOBN(0x736ed43f, 0xe903efee),
+     TOBN(0x2c687134, 0xca9bf60f), TOBN(0x2473ea8f, 0xbc7da3a5),
+     TOBN(0xf54ef685, 0xb45fb57e), TOBN(0x594e8445, 0x3383cadb),
+     TOBN(0xe1edd3fb, 0x4a7df4bb), TOBN(0xa783d987, 0xc17c2c92),
+     TOBN(0x0d498637, 0xcf8fcba8), TOBN(0xdebd801d, 0x3acd6e4c),
+     TOBN(0x2ade8a7c, 0x34d3761e), TOBN(0xc591c889, 0xd825cd19),
+     TOBN(0x3ffd60ba, 0x39b42dec), TOBN(0x136d4902, 0xfd9674dc),
+     TOBN(0x373a70f8, 0xda4842c4), TOBN(0x3208c485, 0x3f078bfd),
+     TOBN(0x3587f871, 0xef608639), TOBN(0xf990ab0f, 0xf04e46ed),
+     TOBN(0x39d542ab, 0xa83a8450), TOBN(0x634b9198, 0xdacb7c65),
+     TOBN(0x680cef78, 0x82486a05), TOBN(0xab1d4d77, 0x16eaf88b),
+     TOBN(0x5e605279, 0x699c7aa5), TOBN(0x7e37906f, 0x3c40a07f),
+     TOBN(0x4ae84ad8, 0xfb6926e3), TOBN(0x236b5f07, 0xe2ebc73b),
+     TOBN(0xa94e50ab, 0x9e0939a5), TOBN(0xabeed810, 0x2d9e10e2),
+     TOBN(0xea8190fb, 0x4e6423d3), TOBN(0xc739d209, 0x17acb62c),
+     TOBN(0xae38106e, 0x6fdbe8dc), TOBN(0x1c6532d7, 0x63204138),
+     TOBN(0x03af879d, 0xbb7d0510), TOBN(0x1d76faf0, 0x8cd2b1a4),
+     TOBN(0x2fcdaf9b, 0xd77386cc), TOBN(0x30f9f5a4, 0xe32d2633),
+     TOBN(0xa4fc8189, 0x382e7298), TOBN(0x946923a1, 0x588af205),
+     TOBN(0x2c527a79, 0x114f2beb), TOBN(0xa2ca55d3, 0x077762eb),
+     TOBN(0xe4b2eb7c, 0xcc85e41e), TOBN(0x4b5938d2, 0x89346ada),
+     TOBN(0x8e94e741, 0x4c2084cf), TOBN(0x4ef32d29, 0xa839ecb4),
+     TOBN(0xc5371755, 0x802f0897), TOBN(0xb0274ff1, 0xc49ae8a1),
+     TOBN(0xf7716d1c, 0x417bff62), TOBN(0x6efb0748, 0x918f9555),
+     TOBN(0x7d3bb9c8, 0x7aeb1e8d), TOBN(0xee9bd5e1, 0x20d51e18),
+     TOBN(0xfaf0a1a5, 0xd52033b1), TOBN(0x7967d3f4, 0xb8626432),
+     TOBN(0xe837ca4b, 0x5574dc0e), TOBN(0xf7eae237, 0x2c11d8ff),
+     TOBN(0xc0f2f1fa, 0x87dc4007), TOBN(0xf5f1f153, 0x8dfb51f7),
+     TOBN(0xa64b10ae, 0x5bd9ac7f), TOBN(0xb3c2ba37, 0xa2198841),
+     TOBN(0x5a7ebac5, 0x66c1ee7b), TOBN(0x59e06f4c, 0xdba62ea8),
+     TOBN(0xa2ea165e, 0x30944ef3), TOBN(0xfd5c7dfa, 0x3e21385b),
+     TOBN(0x4a012c73, 0xe3bb110d), TOBN(0x16d85219, 0x4fb2fdf3),
+     TOBN(0x1aac7f11, 0x7cad0594), TOBN(0xea7f7dbf, 0x4b098d9f),
+     TOBN(0x88abaa5c, 0x7fd181e7), TOBN(0x136a0c9f, 0xca3ad1eb),
+     TOBN(0xe6e5e6c2, 0xf394aab5), TOBN(0x84d697d4, 0x9349e4a5),
+     TOBN(0x8215578b, 0xf76f4b3b), TOBN(0x81a1cec6, 0x12feeb5f),
+     TOBN(0x5d336eb7, 0x3e876bc3), TOBN(0xe8afdcb5, 0x071892ca),
+     TOBN(0x22f16f6b, 0x3da8d94c), TOBN(0x28b276c5, 0x2d150069),
+     TOBN(0x49d20441, 0x643d3e58), TOBN(0x3450c84a, 0x3da3a7fb),
+     TOBN(0x8f5bf388, 0x442ca3e3), TOBN(0xca31411c, 0x9e615382),
+     TOBN(0xbe63e34d, 0x7798675f), TOBN(0x551eb64d, 0xd1ea01e1),
+     TOBN(0x1738a83b, 0x34a00e27), TOBN(0xe7591d15, 0xbf58ce70),
+     TOBN(0xde2ace5a, 0x57d806d8), TOBN(0xe89e8110, 0xd0338020),
+     TOBN(0x935ed5de, 0x4e25756c), TOBN(0x07ef8c2f, 0x46d0f00b),
+     TOBN(0xa28e5fb4, 0xa659592a), TOBN(0xcb45c622, 0x7fa4986a),
+     TOBN(0x6b7df066, 0x74de493c), TOBN(0x4d6bdaef, 0x79aa5258),
+     TOBN(0xe9709c34, 0xe2b255ed), TOBN(0xdba2653a, 0x7d0e7443),
+     TOBN(0xeb8da5c8, 0xa00eb3e4), TOBN(0xe978228e, 0x7ab0e45c),
+     TOBN(0x3a31bafd, 0x9d551a51), TOBN(0x1de0e9cf, 0x403352f5),
+     TOBN(0xb94d5478, 0x23ddd51c), TOBN(0x7c215c91, 0x130e78e3),
+     TOBN(0x556b92e0, 0xed547bce), TOBN(0x0072da6b, 0x909f5c6f),
+     TOBN(0x4ec71b11, 0xf0dc846b), TOBN(0xd0f3b3b4, 0xbf7baaa1),
+     TOBN(0x896391c5, 0x47770705), TOBN(0x41fe5736, 0x66732587),
+     TOBN(0x02a7e3e3, 0x4acd3c51), TOBN(0x217df736, 0xd30407b3),
+     TOBN(0x503a31ae, 0xe47c33cb), TOBN(0xe3186392, 0x4912bbb0),
+     TOBN(0x2491a08a, 0x75a5df9a), TOBN(0x2882f937, 0xc09294ad),
+     TOBN(0xe2576b69, 0x979ad9f9), TOBN(0xf44ddc15, 0x26dc1ffc),
+     TOBN(0x7dad21d4, 0x968268ae), TOBN(0x07378e90, 0xbe9c6fc0),
+     TOBN(0x9406a872, 0x2b329579), TOBN(0xb27b5c51, 0x761f10ae),
+     TOBN(0xf5dad2f9, 0xd04cf60b), TOBN(0x3154dff5, 0xdf950997),
+     TOBN(0xaaec9d30, 0xd8534a9a), TOBN(0x4ac722f5, 0xac43f212),
+     TOBN(0x722882f4, 0x46464c70), TOBN(0x9b9b5226, 0x6c3c702e),
+     TOBN(0x4e3974bb, 0x8325964e), TOBN(0xd3ceff9d, 0xaa0c5227),
+     TOBN(0xd530c8f9, 0x9534dba5), TOBN(0xd26e547b, 0xbc751878),
+     TOBN(0x184a3527, 0xea79b19a), TOBN(0x8dab9214, 0x74f1cdc4),
+     TOBN(0x708abc8c, 0xc051e9f6), TOBN(0x75194e9f, 0x4be2d9ca),
+     TOBN(0x031d69c1, 0xd6ab5348), TOBN(0x1785990e, 0x78b0e490),
+     TOBN(0xd825f125, 0xf6c41f8e), TOBN(0x429924ea, 0x0fbf2fe6),
+     TOBN(0x53c044be, 0xfb87161e), TOBN(0xa3bbdf1b, 0x0651d153),
+     TOBN(0xda660697, 0xec6ecb9c), TOBN(0x51b4a5fd, 0xddb8c619),
+     TOBN(0x80b87520, 0x230fbffb), TOBN(0xa0587430, 0x8848da9d),
+     TOBN(0x98715939, 0x864c2502), TOBN(0x2b10cbfb, 0xaf973396),
+     TOBN(0x28675184, 0x09572b5f), TOBN(0x0a40cdef, 0x39adf777),
+     TOBN(0x2efa3bb4, 0x3ead6eef), TOBN(0xbd76b425, 0xd1b9fe65),
+     TOBN(0x95f006cd, 0x5e527201), TOBN(0x00890f3b, 0x38a7dc3f),
+     TOBN(0x84ffa014, 0x3a7ce6be), TOBN(0x3406aaa0, 0x89541c2e),
+     TOBN(0x430542b6, 0x9559d989), TOBN(0x9b427b08, 0xb53bddd8),
+     TOBN(0x2182bd91, 0x49639170), TOBN(0xb9fb2b42, 0x3299ae83),
+     TOBN(0xbc993d59, 0x423b7ea2), TOBN(0x03e416ac, 0xc110039e),
+     TOBN(0x90c2269a, 0x3ffe24aa), TOBN(0x421ea02d, 0x1c322c49),
+     TOBN(0x40677b1c, 0x0ef8fa01), TOBN(0xa1acd239, 0xc59407d4),
+     TOBN(0xb8cd4f40, 0x8f14decc), TOBN(0x95e90d87, 0x69e16a6b),
+     TOBN(0x85dcf163, 0xc3c38fd3), TOBN(0xf4fb87ba, 0x0c01f90a),
+     TOBN(0x8274f825, 0xdcd0f994), TOBN(0x4c685fa5, 0x2e6bf7d8),
+     TOBN(0xc87d8847, 0x3d928011), TOBN(0x9add0105, 0xf9efa96a),
+     TOBN(0xed39152b, 0x50db3113), TOBN(0x6b523496, 0xb794e6b4),
+     TOBN(0x6bb241b6, 0x84630b17), TOBN(0x6e9f8ae0, 0x1de3ae08),
+     TOBN(0x97bd7c09, 0xd94ce4fe), TOBN(0xe887b02c, 0x9e61057a),
+     TOBN(0x853e8feb, 0xc62c27fa), TOBN(0x3f9d951a, 0x01600ed6),
+     TOBN(0x3e957b36, 0xb57b9742), TOBN(0x92bfd61e, 0x82b72110),
+     TOBN(0x108b450b, 0xfdce7ec4), TOBN(0xd8af107a, 0xcc29c494),
+     TOBN(0x8d67ff70, 0x47688c92), TOBN(0x57f42933, 0x28b9b681),
+     TOBN(0xbbc98ef3, 0xaaf8a48d), TOBN(0x14113b1a, 0xe2d549b6),
+     TOBN(0x1172b259, 0x0b412b3c), TOBN(0xaf86ca6f, 0x1d42a63e),
+     TOBN(0x5f893135, 0x83660d24), TOBN(0xe7bfe9a8, 0x5a21a441),
+     TOBN(0xecd0aa5b, 0x4ee5122e), TOBN(0xbb68654c, 0x5e4df46e),
+     TOBN(0x0c3e820b, 0x5e243845), TOBN(0x042b1895, 0x5c46bfa5),
+     TOBN(0x791b2085, 0x894f7f16), TOBN(0x42eb80f2, 0xb5c353fb),
+     TOBN(0x377777f7, 0xdf8db0d4), TOBN(0x023c0963, 0x34c42ef2),
+     TOBN(0xba05eb5e, 0xa34cb6d0), TOBN(0xffb8b01e, 0x55cd1242),
+     TOBN(0xeab6ff7d, 0x87cd9f24), TOBN(0x175e94c9, 0xab3c09fc),
+     TOBN(0x6dc68140, 0x7075fd9d), TOBN(0x63851566, 0x4b203c44),
+     TOBN(0x3071e924, 0x871d1be7), TOBN(0xe6285b56, 0x85ee9cd9),
+     TOBN(0x738dd629, 0x4bcf8edc), TOBN(0xf3a36813, 0x4ace75f5),
+     TOBN(0x37a09e34, 0x3cf6feb4), TOBN(0x4c2eaef7, 0x2cd0c8af),
+     TOBN(0xd945a28b, 0x16205f2a), TOBN(0xfe9112a7, 0xabadde7a),
+     TOBN(0x7db6c5ee, 0x2bbf97c2), TOBN(0x3eb84a8f, 0xb5b54833),
+     TOBN(0x9732a49f, 0x273007d9), TOBN(0xe61431c0, 0xc6a2e3ef),
+     TOBN(0x88aa1a06, 0x10a101da), TOBN(0x64b94de3, 0xb972cc61),
+     TOBN(0xe79eb6aa, 0xf8402027), TOBN(0xbb1fa5e3, 0xea6e7157),
+     TOBN(0x457f33a2, 0x4ebdbe4b), TOBN(0xf4e955e0, 0x7a61b393),
+     TOBN(0x578e2e64, 0x698d37cf), TOBN(0xbb139e23, 0x82ecbb69),
+     TOBN(0x268d0291, 0xcfe8d05f), TOBN(0x7dcfef41, 0x625fa854),
+     TOBN(0xe21d5b8f, 0x9c4da5e3), TOBN(0xb5e22209, 0x10bf3df1),
+     TOBN(0xb04dd106, 0x437bf2c6), TOBN(0x807c5d04, 0x1d055404),
+     TOBN(0x6e983206, 0x2c06fd15), TOBN(0x773450af, 0xed63ea25),
+     TOBN(0xc2dae106, 0x95c8dca3), TOBN(0x5323f6ba, 0xd82229e8),
+     TOBN(0x647fabee, 0x57c062bb), TOBN(0xcd6adee7, 0xcd5210ac),
+     TOBN(0x11b4df3b, 0x181f674f), TOBN(0x4e23bf4e, 0xf2a92b48),
+     TOBN(0xeea34e2e, 0x84a83d6f), TOBN(0xeaa09d51, 0x9cb197e5),
+     TOBN(0x7f36a278, 0x845e5008), TOBN(0x41fa9b52, 0x1581c0ab),
+     TOBN(0x58917f67, 0x23d1206a), TOBN(0xc04601ce, 0x11062b8d),
+     TOBN(0xdcc60fb6, 0xf31f7326), TOBN(0xc5aeef46, 0x4b071708),
+     TOBN(0x5364069e, 0xdc6939eb), TOBN(0x44bd15a2, 0x034a1052),
+     TOBN(0x8177eeb1, 0x62a307fe), TOBN(0x451ae4e7, 0x1907ad16),
+     TOBN(0x80e49544, 0x27eb3193), TOBN(0xd788e57a, 0xaf88f4c9),
+     TOBN(0xf062c60f, 0xd944e00a), TOBN(0x504463e6, 0xeb4a609f),
+     TOBN(0x3593ad20, 0x74f13c8b), TOBN(0xdc7c5a35, 0xc50bce88),
+     TOBN(0xa6336115, 0xb657d1f9), TOBN(0x18d14e5d, 0x591425ef),
+     TOBN(0x73896725, 0x1454f76e), TOBN(0x52772de4, 0x425c87a9),
+     TOBN(0xe59e4516, 0xc6efb7d6), TOBN(0xdddb8bf3, 0xd76bbc11),
+     TOBN(0x1acbebd9, 0xc6fd2066), TOBN(0x88c3b525, 0x1d7082ea),
+     TOBN(0x6a3b3d62, 0x6d69cea3), TOBN(0xdbf73dfa, 0x8d065405),
+     TOBN(0xd659c8d6, 0x4a7bd06e), TOBN(0x67867520, 0x7bd10bb0),
+     TOBN(0x7c4e3be5, 0x97838647), TOBN(0x545c7144, 0xc5891864),
+     TOBN(0xf64e1031, 0xfa78d62c), TOBN(0x1f046593, 0xfa71692b),
+     TOBN(0xd35a9cb7, 0x71310c47), TOBN(0x10911b96, 0x0ea84922),
+     TOBN(0x5647310d, 0x93a9f5ac), TOBN(0xa6785861, 0x6c05eedb),
+     TOBN(0x2f5aa7c8, 0x43950b68), TOBN(0x57580907, 0xa9d03b3a),
+     TOBN(0xd581049b, 0x42e15fe3), TOBN(0x55dcf9d2, 0x916c4e88),
+     TOBN(0x87ebfd13, 0x27d1a183), TOBN(0x13aee909, 0xf5aaa51e),
+     TOBN(0xa651959d, 0x3b9fc03e), TOBN(0x05c28772, 0x98997a74),
+     TOBN(0x73e047f4, 0xae2e4a65), TOBN(0x359e6c45, 0x783aa072),
+     TOBN(0x1124e9f0, 0x7a04b710), TOBN(0xd35094de, 0x6d2053f2),
+     TOBN(0x0d57d976, 0x2575dab0), TOBN(0x822256fc, 0x69171229),
+     TOBN(0xbd46937a, 0x3d19de1c), TOBN(0x71feede4, 0x6f0be84d),
+     TOBN(0xca205366, 0x7c4dc4b3), TOBN(0xfbb97d0d, 0xe3e851cb),
+     TOBN(0x0270b5ea, 0x2066e9a4), TOBN(0xeade87ff, 0x42ae150b),
+     TOBN(0x9a7f9e81, 0x8eb1bafa), TOBN(0xcb374aaf, 0x0eb5f68e),
+     TOBN(0xa5841c9a, 0xd5525ab2), TOBN(0x3eed9ba8, 0x03e02cd0),
+     TOBN(0x29449bca, 0x279fca98), TOBN(0x4990ec0f, 0x3f450c92),
+     TOBN(0xa241a8e3, 0xbecbba58), TOBN(0xd0e2487c, 0x2eb47817),
+     TOBN(0x6db7d420, 0x8300837d), TOBN(0x78872895, 0x2d7f59ef),
+     TOBN(0x1b3d5033, 0x1314fc73), TOBN(0x2cf4cd42, 0xe710aded),
+     TOBN(0x9159bc5d, 0x6f4026b7), TOBN(0x403f947b, 0x2e62cc45),
+     TOBN(0x18d6ac70, 0x47d97843), TOBN(0x69d5faaa, 0x0694f7eb),
+     TOBN(0x7711535c, 0x6932e0f0), TOBN(0xc85c9616, 0x6ebd1488),
+     TOBN(0x558e3750, 0xd3542212), TOBN(0x21fe02d7, 0x02921066),
+     TOBN(0x1636a1a2, 0x46b90554), TOBN(0x8acf01ed, 0x0108cc04),
+     TOBN(0x57a2b16a, 0xb4d60d37), TOBN(0x3301a33b, 0x91f4fdb4),
+     TOBN(0x70dc3d3a, 0x8e09b548), TOBN(0x35ae7d07, 0x079c0c2f),
+     TOBN(0x95792f06, 0x978f92cc), TOBN(0xb11574d3, 0x23196752),
+     TOBN(0xc3249711, 0xb8cfcac1), TOBN(0x2061c767, 0xcf93af67),
+     TOBN(0xeff09a1b, 0x2f63dbe7), TOBN(0x527776b6, 0x48091edd),
+     TOBN(0xf0fa985e, 0x19bba5a9), TOBN(0xc54f89f3, 0x66ae3221),
+    },
+    {
+     TOBN(0xbc5a6284, 0x6a436476), TOBN(0x6fcc2313, 0x35dbb9cb),
+     TOBN(0xa77d2d9f, 0x5012ffbf), TOBN(0xcc25e9f4, 0x4ae4bd14),
+     TOBN(0xd17fcfc4, 0x1a5e40c6), TOBN(0x7d716a5f, 0xff085322),
+     TOBN(0x9dcbc50b, 0xee3077c4), TOBN(0xebfe953c, 0xdb4a6e61),
+     TOBN(0xe7e66f2f, 0xd3d777d7), TOBN(0x3519dc64, 0xcf1a6b09),
+     TOBN(0x0df07beb, 0xdbf88dcf), TOBN(0x17b09654, 0xacd4e105),
+     TOBN(0xcbd7acd0, 0x4e70c783), TOBN(0xda66e747, 0x96b9d577),
+     TOBN(0x6d0488a1, 0xe3e52f8a), TOBN(0x3ec0fd11, 0x6ff71c1b),
+     TOBN(0x75474cb6, 0xbe4f2782), TOBN(0x10ef5e6b, 0x41c2c0cd),
+     TOBN(0x592c6b06, 0x6a65e29c), TOBN(0x4d424662, 0xd12d0608),
+     TOBN(0xf5280949, 0xb1a714fe), TOBN(0x52697bcc, 0x1199f802),
+     TOBN(0xc68ba4f8, 0xe6a4ff3a), TOBN(0x25a5380f, 0x351849ce),
+     TOBN(0x33207f69, 0x573ec6f5), TOBN(0x7ecc4bbe, 0x67bd2e8b),
+     TOBN(0xa07acd34, 0x8ffe2420), TOBN(0x0a957eb8, 0xa13f9cdd),
+     TOBN(0x0bc7f95b, 0x9ec9c0c5), TOBN(0xd82147cc, 0x6a8578cd),
+     TOBN(0x07a2e7c5, 0x9e61923c), TOBN(0x591eb066, 0x32e83f25),
+     TOBN(0xaaa61588, 0x957c94fa), TOBN(0x6a2bc707, 0x364911fb),
+     TOBN(0x09771450, 0xc4907b19), TOBN(0x4cc48773, 0x9694ccc4),
+     TOBN(0x9db6216e, 0x50c878ac), TOBN(0x6e89210c, 0x6f3031f1),
+     TOBN(0xb711dcbf, 0xced0d41e), TOBN(0xe39bfe3e, 0x0fbf9751),
+     TOBN(0x18fd7a45, 0x764636b5), TOBN(0xe437ee86, 0xb75d48f3),
+     TOBN(0xe323bb18, 0x60a80177), TOBN(0xedc3c8f3, 0xbc94c0ea),
+     TOBN(0xd8351164, 0xec8cb0cf), TOBN(0xccdd8829, 0x2472936d),
+     TOBN(0xa8db1b85, 0x58059756), TOBN(0x4eda8cf8, 0xd55c184a),
+     TOBN(0xdfb5727d, 0x2923b8cb), TOBN(0x6e793e5c, 0xe6773d5e),
+     TOBN(0x8ecc901b, 0xa0641165), TOBN(0x6077ab26, 0xd6da5095),
+     TOBN(0x00669b0c, 0x6b127d9d), TOBN(0x8140e4e0, 0xd63e2e1f),
+     TOBN(0x1ad5b03c, 0x9641b6a2), TOBN(0x44299f88, 0x9baed7b0),
+     TOBN(0x1736296d, 0x1ea4a056), TOBN(0x6f74702c, 0xd77811ba),
+     TOBN(0x5c927548, 0x432dd74b), TOBN(0x9cc73271, 0xe7a194ab),
+     TOBN(0x0f035ede, 0xd6328dca), TOBN(0x5292aa39, 0x28db755e),
+     TOBN(0xb5488385, 0xa0192a4a), TOBN(0x6e7d2fa8, 0xdfc6895c),
+     TOBN(0xfa912a0a, 0x5d8bbec9), TOBN(0x7051140a, 0x0087edb3),
+     TOBN(0x5293672b, 0x64865e5b), TOBN(0x6e8448c9, 0xc82c48d5),
+     TOBN(0xeece41cb, 0xa2c437b3), TOBN(0x148967d2, 0x21ce1ef4),
+     TOBN(0xf14391fa, 0x6b05c2a5), TOBN(0x15ff5fc9, 0x8fed2f1f),
+     TOBN(0x18ae5e74, 0x4557b49f), TOBN(0xe33760c6, 0x3db266b2),
+     TOBN(0xd5d830c7, 0xb1b249b5), TOBN(0x24c665b9, 0xc5fff531),
+     TOBN(0x6b304406, 0xc57df7c0), TOBN(0x59706667, 0xc3958e89),
+     TOBN(0xbf590ff2, 0x790a5483), TOBN(0xbcaea5a5, 0x5ce77aaa),
+     TOBN(0x8578a002, 0x80ceb559), TOBN(0x3639aadf, 0xd8d61946),
+     TOBN(0x3fd52d94, 0xadd3bb00), TOBN(0x16c27846, 0xe09a8ce3),
+     TOBN(0x75cfd6c6, 0x294c7967), TOBN(0xfb9b7f37, 0x59195034),
+     TOBN(0xae687a99, 0xaa972a86), TOBN(0x04bdefdb, 0xebd2394e),
+     TOBN(0x8e245a19, 0x2f96144d), TOBN(0xc740d348, 0x3b61e5ab),
+     TOBN(0x8703710e, 0x293ddb25), TOBN(0xf4bb6ac0, 0x2bbf8f63),
+     TOBN(0x86396457, 0xde3b5805), TOBN(0x607022db, 0x65d29e63),
+     TOBN(0xad0a0cdc, 0xcc930fe3), TOBN(0xd9997ebb, 0x1626abf6),
+     TOBN(0x2d872d17, 0x2a510565), TOBN(0x3e682079, 0x0357ba07),
+     TOBN(0x49edd962, 0xebfaf203), TOBN(0x3a13edfb, 0xf81eda20),
+     TOBN(0x87b5b5e1, 0x7a75f2d5), TOBN(0xf04de2b8, 0xddfd9511),
+     TOBN(0xf29a1569, 0xcfc5c5ff), TOBN(0xa3995532, 0x07160ed3),
+     TOBN(0xb6247469, 0xcb2b061b), TOBN(0xe75c5351, 0x2f10fe1e),
+     TOBN(0xbaf44963, 0xd20e1bf7), TOBN(0x216cb6ab, 0x2d93babf),
+     TOBN(0x7e0b655c, 0xf5109e45), TOBN(0xdcc712fc, 0x6657450d),
+     TOBN(0xe06c408e, 0xd51fc733), TOBN(0x85b11f96, 0xed9c0912),
+     TOBN(0x954cb91c, 0x37365c9b), TOBN(0xe0eaa047, 0xb2f74fe7),
+     TOBN(0x9af74b86, 0x15716541), TOBN(0x4da06207, 0xf73dc7bd),
+     TOBN(0xdb0d089e, 0xe07890a1), TOBN(0x5bf09681, 0x73902f91),
+     TOBN(0x14e1710c, 0xa897f0fe), TOBN(0x191ec9a1, 0x3605b1c2),
+     TOBN(0x271b2e2a, 0x0133903b), TOBN(0x5b3686f2, 0xe495ee32),
+     TOBN(0x89bcc974, 0x0c991f28), TOBN(0xadd20cce, 0x34f93b8a),
+     TOBN(0x5f5a1768, 0x680b65b6), TOBN(0x0c453ab8, 0xaad41c40),
+     TOBN(0xd479630f, 0xa7fb4269), TOBN(0x60039d01, 0x52c4e929),
+     TOBN(0x0d8d112c, 0xff860883), TOBN(0xe1dce5c9, 0x723c6e29),
+     TOBN(0xc19eadae, 0x191ad70e), TOBN(0x4af8194d, 0x62ce0e64),
+     TOBN(0xf207bfb0, 0xcc81415c), TOBN(0x3ab92f3b, 0x008495c8),
+     TOBN(0xe7250e17, 0xfdb9534b), TOBN(0xba67e9b8, 0x6c0c1d1c),
+     TOBN(0x117ae3ff, 0x072c793f), TOBN(0x5243e6ea, 0x9fb3091e),
+     TOBN(0xf93ad514, 0x31a59e39), TOBN(0x8ce9cfb0, 0xc93c1891),
+     TOBN(0xbfcbf901, 0x1ed08b0e), TOBN(0x4d13cf2a, 0xb53d687d),
+     TOBN(0x25aa82db, 0x5d81e4ad), TOBN(0xd12f01f5, 0x63c3cb41),
+     TOBN(0x1e799084, 0xf8d1333a), TOBN(0x30c96c55, 0x653bcd0a),
+     TOBN(0x9cf130fd, 0x44b5195c), TOBN(0x4cffc531, 0x13c77763),
+     TOBN(0x082287f8, 0x9430619f), TOBN(0x78bb037d, 0xb08ce0d9),
+     TOBN(0x2e69d512, 0x3affe8e8), TOBN(0xe9dbb263, 0xba9ec693),
+     TOBN(0x67b66ad8, 0x62f132b5), TOBN(0x70318d2b, 0xbeb47184),
+     TOBN(0x46c429ea, 0xf50a0e98), TOBN(0xd7e32eba, 0xe2b3542c),
+     TOBN(0x625c1ce9, 0xe096b4b7), TOBN(0x09221351, 0x389fd4dd),
+     TOBN(0x08dc02d2, 0xfb0ee85a), TOBN(0x98c0ba7d, 0x853cd901),
+     TOBN(0x88a0cd6d, 0x0deb1d99), TOBN(0x989e4962, 0x79a6b90c),
+     TOBN(0xf5d19b95, 0x24dd89d5), TOBN(0x189e5230, 0xb37cf19e),
+     TOBN(0x84a607b8, 0xb0c5fefa), TOBN(0xe48450c9, 0xd8c7fbd1),
+     TOBN(0x178f9b56, 0x46479ad7), TOBN(0x7d6a36c6, 0xcbcd2ae5),
+     TOBN(0x95a4d51f, 0x71ae6516), TOBN(0x0363349f, 0x566e2171),
+     TOBN(0x4d4bb4b0, 0xed1f2fc7), TOBN(0xde435aaf, 0xf10fa10c),
+     TOBN(0x711258a9, 0xb76e3b6e), TOBN(0x9a640eeb, 0x2792e0b3),
+     TOBN(0x7953ead8, 0x5fab8617), TOBN(0xd4b6d248, 0xdd64702a),
+     TOBN(0x95bbe528, 0x2d672209), TOBN(0xfcc53cfc, 0xb6926b8a),
+     TOBN(0x05814190, 0x57659f87), TOBN(0x4836e93b, 0x08d25069),
+     TOBN(0xd1eb2006, 0x6a5ad81e), TOBN(0x4bee145a, 0xaf0d37f8),
+     TOBN(0xd44362ad, 0xd31ce6cb), TOBN(0xdc03e581, 0x936c1060),
+     TOBN(0x13cffce9, 0x16fcb889), TOBN(0xed7e6683, 0xac7e709a),
+     TOBN(0xb655d098, 0x5896e541), TOBN(0x07124356, 0xb92a6204),
+     TOBN(0xa2ae43c8, 0xa8f50043), TOBN(0xeb39255c, 0x68731891),
+     TOBN(0xe07be0ad, 0x3d9c408b), TOBN(0x0db7904f, 0x0b4f5c3a),
+     TOBN(0x7ddc0235, 0x4d70bb81), TOBN(0xe3b323c3, 0x5347797a),
+     TOBN(0x3536cd9d, 0x3536deee), TOBN(0x579b6894, 0x001bfd25),
+     TOBN(0x58ad5301, 0xebe2922e), TOBN(0xe0aa2cae, 0x92a88d43),
+     TOBN(0x24567a3b, 0x4409e205), TOBN(0x3cece61a, 0x2258f0cb),
+     TOBN(0x8da5cf46, 0x3babf4f6), TOBN(0xb37428d9, 0x81fff8e6),
+     TOBN(0xcda1ff77, 0x48495d23), TOBN(0x98f9208f, 0x34f392ad),
+     TOBN(0x931f5b37, 0x5bc88514), TOBN(0xd49971be, 0xcb375921),
+     TOBN(0x9dcd4986, 0xb5c01fab), TOBN(0xcc26ec02, 0xc1ab1c94),
+     TOBN(0x34e8087d, 0xb4b874d6), TOBN(0x224fc277, 0x9d0a3761),
+     TOBN(0xacc1f258, 0x3f7e5159), TOBN(0xc82d71ec, 0x8966d593),
+     TOBN(0x5b1f9f40, 0x7dcd691a), TOBN(0xd8fafdae, 0xba28f416),
+     TOBN(0xe8622ae6, 0x43b6d90f), TOBN(0xec13fce7, 0x9ec71d5b),
+     TOBN(0x07b6aeb8, 0xfd2e8214), TOBN(0x813e718e, 0x4cbc297a),
+     TOBN(0xfac0dfab, 0x81fd6931), TOBN(0xa1fe8821, 0x3c48ffd7),
+     TOBN(0xd2715c18, 0x85e03c08), TOBN(0xb6e4418a, 0x977c57f0),
+     TOBN(0xfaa79ea4, 0x73418cde), TOBN(0x6ab8c25b, 0x171e2a89),
+     TOBN(0x2800445c, 0x4ec7cf05), TOBN(0x8e74a7b0, 0xb66c6200),
+     TOBN(0x081b1177, 0x481db950), TOBN(0x526d051c, 0xb89f7c02),
+     TOBN(0x3c830942, 0x5c29c905), TOBN(0xbfbd9e3e, 0x44c15ce5),
+     TOBN(0x6055c949, 0xa29472e6), TOBN(0xab0010c7, 0xa37c4912),
+     TOBN(0xeb8492be, 0x5b7d3647), TOBN(0x0b4cfd7b, 0x1ee31caf),
+     TOBN(0x81cfcde2, 0x4b46304b), TOBN(0x968df75d, 0xc554a5bc),
+     TOBN(0x7ce78806, 0x8d0e043c), TOBN(0x1e896819, 0x345ea27c),
+     TOBN(0xe040c19c, 0x6e287603), TOBN(0xa581856f, 0x138e8ece),
+     TOBN(0xe49f6558, 0xc354a9d6), TOBN(0xc4ad763a, 0xc0cfb2d3),
+     TOBN(0x4be2143b, 0x1b76b8f3), TOBN(0xa8caae14, 0xd0ad0247),
+     TOBN(0xcfe96bd5, 0x928b0ae5), TOBN(0xcf5051f7, 0x7724f8e4),
+     TOBN(0x9128916f, 0xec4af64a), TOBN(0xc211ff4b, 0xcb437bfb),
+     TOBN(0xee6e8134, 0xbce59c0f), TOBN(0x3d068b4c, 0xd59f7f86),
+     TOBN(0xafa2753c, 0x96283457), TOBN(0x453fe33c, 0x1aedcbf0),
+     TOBN(0x781294c8, 0x483c0b1a), TOBN(0x9e6f5133, 0x5c2ad1ee),
+     TOBN(0x2a77b6ce, 0x69383e0b), TOBN(0xcb5a83ab, 0xfa9f0142),
+     TOBN(0x2318aa98, 0x3b0e027f), TOBN(0xdea716a3, 0xc2c68dd5),
+     TOBN(0x3f75c46d, 0x9f548eb3), TOBN(0x71642513, 0x96120de9),
+     TOBN(0xf733614c, 0xdbee488e), TOBN(0xdf940026, 0xaad077f4),
+     TOBN(0xeda9c098, 0x94a840cb), TOBN(0x5108bf0b, 0x393be3b9),
+     TOBN(0x137c08b0, 0x39980cee), TOBN(0x2e31bba0, 0x0839112b),
+     TOBN(0x9ec73de2, 0xba614ea3), TOBN(0xd0bca8d4, 0xd17822c0),
+     TOBN(0x5d9f7482, 0x50b7805d), TOBN(0x16035a80, 0x298becf6),
+     TOBN(0x46571500, 0xd7c318e7), TOBN(0x6bd30919, 0xd0ee6956),
+     TOBN(0x5c0ad747, 0xb2e13320), TOBN(0xe7f7f71e, 0xda47666d),
+     TOBN(0xce322037, 0x318a8e8e), TOBN(0xf15232ae, 0xe9f84dd6),
+     TOBN(0xc59709c5, 0x915a03b7), TOBN(0x2e2000f7, 0x9a3040b4),
+     TOBN(0x41955f77, 0x8398a5a9), TOBN(0xa8e6620e, 0x7086b69e),
+     TOBN(0x63acd70e, 0x8344224b), TOBN(0x966efefc, 0xc3145159),
+     TOBN(0x406619ec, 0xf5e0f955), TOBN(0xedd0efc9, 0xec6de618),
+     TOBN(0x6fe3e34e, 0xb2580ed4), TOBN(0x9d8875b5, 0x4139b95e),
+     TOBN(0x85baf0c1, 0x8e5be187), TOBN(0x549cefca, 0x09553886),
+     TOBN(0xc965b2a2, 0xae9ef2cc), TOBN(0xd43079fb, 0x15afee63),
+     TOBN(0x02b8794a, 0x076cdb05), TOBN(0xd0ae7321, 0xa0d1a953),
+     TOBN(0x5a8b5281, 0x2ac5fff0), TOBN(0x73437d67, 0xcdda362d),
+     TOBN(0x1866b2b9, 0x1a95ff87), TOBN(0x5ff11398, 0x0420b3e1),
+     TOBN(0x0d43b92c, 0x92284adf), TOBN(0x81425367, 0x4da4c4a7),
+     TOBN(0xc8093c56, 0xdf17641a), TOBN(0xc418f19d, 0xb5ccd14d),
+     TOBN(0xaad98608, 0x506762ed), TOBN(0xb6f45297, 0xddb2c829),
+     TOBN(0xd0e49176, 0xd395692a), TOBN(0xc05b4e27, 0x3b1073d3),
+     TOBN(0xe8ca133b, 0xe5808e53), TOBN(0x6105cd0e, 0x06a64b56),
+     TOBN(0x89a64669, 0x53cf6d7e), TOBN(0xe281ca2d, 0x1bebfea5),
+     TOBN(0x98ee67ac, 0x324b25d8), TOBN(0x2227631f, 0xdca154ec),
+     TOBN(0xa242c5a1, 0x4406e8ba), TOBN(0xced39f05, 0x49250026),
+     TOBN(0xd256dd83, 0xdd77d731), TOBN(0x2faa6a0e, 0x7414d0c0),
+     TOBN(0xa2e0f928, 0x3b90f004), TOBN(0x019bb3ef, 0x8719bfd4),
+     TOBN(0x3f4f6109, 0xe2d515c2), TOBN(0xb50a9907, 0xbf88d7a6),
+     TOBN(0x8e5fbc2d, 0x015ac4de), TOBN(0x96992421, 0xe78a2117),
+     TOBN(0x321e6086, 0x26e53df3), TOBN(0x07eb1d15, 0xf42b2508),
+     TOBN(0x7b552108, 0x0ef22bc2), TOBN(0x9eedb828, 0x00f3e571),
+     TOBN(0x556abbaf, 0x6f0e883c), TOBN(0x8025770b, 0x40473ead),
+     TOBN(0x2fdab965, 0x6ece1cc8), TOBN(0xba07cf89, 0x00ec1adc),
+     TOBN(0xefec4deb, 0x4be5ad18), TOBN(0x16625be8, 0xd59fa732),
+     TOBN(0xffee542e, 0x6808cdf7), TOBN(0x85c19ef3, 0xd7a9f29b),
+     TOBN(0xca4ac1f9, 0x82dc1ae3), TOBN(0xa6c726d1, 0xca5e8f58),
+     TOBN(0x0bcc3d58, 0x66960edd), TOBN(0x8e8445d0, 0x56f94ea8),
+     TOBN(0xd4d0177b, 0x938e64c9), TOBN(0x8d0199f1, 0xf9a0288f),
+     TOBN(0x9176d559, 0x14a226c0), TOBN(0x13b373ee, 0xa00aea02),
+     TOBN(0xc63b2d79, 0x6b629fea), TOBN(0x36df7c09, 0xa7e0cc42),
+     TOBN(0x4628ba4f, 0x40bdbc8e), TOBN(0x7f5b0228, 0x0de296f2),
+     TOBN(0xb0598130, 0x3c63d73f), TOBN(0x55e59f61, 0x0431550e),
+     TOBN(0x6f2e109d, 0x6693eb8c), TOBN(0x3602ba82, 0x470b10fe),
+     TOBN(0x3acd0af4, 0x5ec7f357), TOBN(0xfa7479f4, 0xb071c758),
+     TOBN(0xbf47caa0, 0xe13652c9), TOBN(0x6fa139bb, 0xf5f5eca9),
+     TOBN(0xfa149b84, 0x8c0e197e), TOBN(0xca31714c, 0x60ae7755),
+     TOBN(0x934ed1af, 0x8ccc4241), TOBN(0x39772769, 0x781a024e),
+     TOBN(0x9f07dfb1, 0xbe24eb34), TOBN(0xfa8a9c60, 0x0a3dac06),
+     TOBN(0x08fbbe21, 0x8e410ce7), TOBN(0xea55fb96, 0x396a9702),
+     TOBN(0x4422bc58, 0xf18882bb), TOBN(0x1ccb7b47, 0x0ddd0dd7),
+     TOBN(0x828580a8, 0xf40ea941), TOBN(0xf9ec9728, 0x0db78350),
+     TOBN(0x2823b4fd, 0x1612f28a), TOBN(0x96dc3e29, 0x82b26487),
+     TOBN(0x1740fdae, 0x2497420a), TOBN(0x3bb39dfa, 0x322f1c6f),
+     TOBN(0xf32a21e6, 0x4cb19878), TOBN(0xeac04097, 0x9277c80b),
+     TOBN(0x67178d8f, 0x13380801), TOBN(0xfe5e2694, 0x34bf8872),
+     TOBN(0x8278bad4, 0x327129d3), TOBN(0xb42a3f9b, 0x941c4e5c),
+     TOBN(0x04eefb7d, 0x39de36f0), TOBN(0xed2aab7f, 0x8d967703),
+     TOBN(0xa3283a2c, 0x72aa1c89), TOBN(0x1969613e, 0x2a4d513e),
+     TOBN(0x0d4c0347, 0xddd5ea18), TOBN(0xbbad9ce4, 0x43cee5fe),
+     TOBN(0xe8c050a8, 0x57313b49), TOBN(0x3b91c3cc, 0xff09bf31),
+     TOBN(0xe6e5ab6d, 0x610395cb), TOBN(0xfc36cde0, 0xdeb31bef),
+     TOBN(0x76f806f2, 0x5d43c8a7), TOBN(0x08a64cb2, 0x63b7c746),
+     TOBN(0xb6cdcdab, 0x45256a2a), TOBN(0x640f67ea, 0x9bebae90),
+     TOBN(0x682eeeb6, 0xcf016971), TOBN(0x4d16d566, 0x50f59261),
+     TOBN(0xdaca66bb, 0xf41db99d), TOBN(0xccdb3da0, 0xf8f04d96),
+     TOBN(0x7c228cae, 0xcf41b164), TOBN(0x40bef27f, 0xedbefa7c),
+     TOBN(0x4efdd6c2, 0xecb43685), TOBN(0x4d0fa367, 0xa834a50b),
+     TOBN(0x2ec9c445, 0xb87f7ec7), TOBN(0xc3663ced, 0x23170d0f),
+     TOBN(0x189872e4, 0xc5b47b29), TOBN(0xf8047387, 0x746d6a13),
+     TOBN(0x753837d3, 0xb75ac898), TOBN(0xaee88a60, 0x91959a78),
+     TOBN(0xf46b0f6e, 0xe6f59621), TOBN(0x0e92e271, 0x10d981c8),
+     TOBN(0x610d0f80, 0x8d578b6d), TOBN(0x962bd7bb, 0xb4d9b9de),
+     TOBN(0xbe26960d, 0x84a0c394), TOBN(0x142a0c75, 0x3b5bd996),
+     TOBN(0x442bb39a, 0x0be95497), TOBN(0xce5d2c60, 0x0f33c9de),
+     TOBN(0x1ce0d08c, 0x283dc751), TOBN(0x106ed588, 0x79b3c1a8),
+     TOBN(0x4b2e29c6, 0x7f8ee4d7), TOBN(0x7d61e3bb, 0x08bbd660),
+     TOBN(0x11504dc5, 0x1e964a3e), TOBN(0x31544a52, 0xc77a957a),
+     TOBN(0x1fc4161e, 0xcd7d0dac), TOBN(0x83733f27, 0x370c15c9),
+     TOBN(0x224976de, 0x853758cc), TOBN(0x1bbb7730, 0x47c1ab78),
+     TOBN(0x94a3b697, 0x19c89029), TOBN(0x031432f0, 0x37dfc44f),
+     TOBN(0xf84593ac, 0xd88090cb), TOBN(0x381b51bc, 0x65bcfee8),
+     TOBN(0x38dac75b, 0x10b412b7), TOBN(0x6df5c9a1, 0xc7e06d08),
+     TOBN(0x9c6d8068, 0x0e08c41c), TOBN(0x1544e3c5, 0xc3600f4f),
+     TOBN(0xf827a48d, 0x9c83e0a1), TOBN(0xd8539228, 0x06bcb3c4),
+     TOBN(0x149862b3, 0x6268cf12), TOBN(0x4829ee56, 0x6ec4e354),
+     TOBN(0x44b2c3bf, 0xb712a1f9), TOBN(0xe556b78a, 0xc90852af),
+     TOBN(0x50f6de2e, 0x906a13b6), TOBN(0x1744efd5, 0x568a1293),
+     TOBN(0x942ad99e, 0x2b5745a1), TOBN(0x0f100bd9, 0xca362913),
+     TOBN(0xd9b6ad51, 0x91e96cde), TOBN(0x4aa440bc, 0x5a2f88e9),
+     TOBN(0x53c4c956, 0x57a10070), TOBN(0x7d1be72e, 0xae6e4872),
+     TOBN(0xb704009c, 0xd427eda4), TOBN(0x3e0aa93f, 0x5f39b7d8),
+     TOBN(0xdea1ab48, 0x3153a757), TOBN(0x10a070e7, 0x9ee60ead),
+     TOBN(0xd6a6e92d, 0xe6c916bf), TOBN(0x02b1e0e6, 0xbd7bb216),
+     TOBN(0x6efb5f1b, 0xb49138a3), TOBN(0x11f7a9be, 0xe88d2db0),
+     TOBN(0x0b9a2b11, 0x3233df5b), TOBN(0x0688afda, 0x1824fcc5),
+     TOBN(0xcf1ea2a5, 0x5ff97f9a), TOBN(0xe8ad7b15, 0x4998e602),
+     TOBN(0xdb4ae67e, 0xa455aad1), TOBN(0x823ac090, 0x74a27ff3),
+     TOBN(0x5c431060, 0x2573443f), TOBN(0x92f9f9ab, 0x94258714),
+     TOBN(0x1548fe21, 0xb1283d2e), TOBN(0xf86fe50b, 0x5c5be5f9),
+     TOBN(0xd20dfc8a, 0x520c5fc6), TOBN(0x6e721dd9, 0x53b5e7c5),
+     TOBN(0x8ef7eee5, 0x8f2a8969), TOBN(0xe894859f, 0x62d07bdf),
+     TOBN(0xaf279176, 0x1cfc6627), TOBN(0x94b8cff4, 0x483755e9),
+     TOBN(0xa5916f70, 0x0fda4bcb), TOBN(0x9c5318d0, 0x47ba65f3),
+     TOBN(0x9e9c8e54, 0x636cd7e3), TOBN(0x5c64a261, 0x54c49da3),
+     TOBN(0x04d7ff61, 0x690e932c), TOBN(0x92a357b3, 0xc84b0b78),
+     TOBN(0x47f6144c, 0xc6f3bd8d), TOBN(0xdf7b1ee4, 0x71c19265),
+     TOBN(0xa7ea37f1, 0x3fd5c30f), TOBN(0xdc2d890b, 0x79fa08cf),
+     TOBN(0x9813bced, 0x2fd40236), TOBN(0xa8a1055f, 0x432dde17),
+     TOBN(0x70011f47, 0x7772c727), TOBN(0x965c130a, 0x2e2e802f),
+     TOBN(0x31a6aca7, 0xf5bd4ac5), TOBN(0x83995bde, 0xd825db6f),
+     TOBN(0xcbf20325, 0xfe521833), TOBN(0x8dcd25a1, 0x0278f4a0),
+     TOBN(0xf1e83d97, 0x5f2293ea), TOBN(0x1717876b, 0x52317ad3),
+     TOBN(0x0df62167, 0x14181928), TOBN(0x24adfd6e, 0x2fe203ce),
+     TOBN(0x1d264af0, 0x797f25ff), TOBN(0x2cb7cc17, 0xd22e3da1),
+     TOBN(0x10c4b51a, 0xe0016a19), TOBN(0x5956ce8f, 0xd82b2a86),
+     TOBN(0xdef0fefc, 0xa3d4780e), TOBN(0x97e693ab, 0x6e92b93a),
+     TOBN(0x8fa3f4fa, 0x20bcc98f), TOBN(0x4fc004f1, 0xf9182867),
+     TOBN(0x1a206da3, 0x93e73961), TOBN(0x37d75a90, 0x1e7db32c),
+     TOBN(0xa39f0db1, 0x0455b815), TOBN(0x841646e0, 0xb69ee770),
+     TOBN(0xadb0aaaa, 0x0939f065), TOBN(0x5608613b, 0x0b147d7a),
+     TOBN(0x84ce1a4c, 0x470f6875), TOBN(0x501de5fe, 0x7983050e),
+     TOBN(0x19915b26, 0xc3d8ed98), TOBN(0xf451e57a, 0x9a66a6e5),
+     TOBN(0x29843607, 0x30dab6a3), TOBN(0x1710267c, 0x3d1a1ebb),
+     TOBN(0xce4ecfd4, 0xe11d88c0), TOBN(0x12fc2787, 0x11ce026a),
+     TOBN(0x9801cecd, 0x691227de), TOBN(0x517a92f3, 0x76ce6dae),
+    },
+    {
+     TOBN(0x821b0fdf, 0x648c48e5), TOBN(0x689e6d56, 0x9f45a433),
+     TOBN(0xa5a9dca8, 0x2e572855), TOBN(0xb0f07eb7, 0x8adfb547),
+     TOBN(0x48ecb166, 0x552c8d55), TOBN(0xfe3fc268, 0xce854171),
+     TOBN(0x323af5eb, 0xeeee9bc0), TOBN(0x0666a2a3, 0x41ae1c80),
+     TOBN(0xa06d20bc, 0x9ff262fb), TOBN(0xcba032fd, 0xd075868b),
+     TOBN(0x70376026, 0x943fd973), TOBN(0x81c57cba, 0xe35c5e02),
+     TOBN(0x1964e700, 0xba871f1b), TOBN(0xf03a8c04, 0x6b265f57),
+     TOBN(0xc8ebc912, 0x0b950259), TOBN(0xd2b0ee30, 0xad32ca8b),
+     TOBN(0xe01bf0c2, 0x89c8e719), TOBN(0xbce1e20f, 0xb2f4cdb0),
+     TOBN(0x8c38eeaf, 0xa1053ca5), TOBN(0x8c2d85ef, 0x7cd633a5),
+     TOBN(0x75695364, 0x9b53cdb1), TOBN(0x5e999741, 0x447eb1a5),
+     TOBN(0x6d6b2d88, 0xdbd88626), TOBN(0x87eaf045, 0x21876357),
+     TOBN(0x2c88f1ff, 0xdeec441e), TOBN(0xab52096b, 0xd01b2157),
+     TOBN(0x37eee275, 0x6c45cf5c), TOBN(0xa070d24e, 0x0520ecaa),
+     TOBN(0x61d15bd1, 0x546b9fd3), TOBN(0x3276fb74, 0x2c96db1c),
+     TOBN(0xc5c1b041, 0xb95b29b7), TOBN(0xe18008db, 0xbd7d3254),
+     TOBN(0xd56ae44f, 0x98dfb69a), TOBN(0xd5f66b0b, 0x609d941c),
+     TOBN(0xca6b6d35, 0xb91b5245), TOBN(0x98e3a4e3, 0x7b3f98a6),
+     TOBN(0x0715dfa6, 0xf358c56a), TOBN(0x3b02ff21, 0x36a66c64),
+     TOBN(0x737b1401, 0xcb22cbd3), TOBN(0x9dd15f5b, 0x6b8e9624),
+     TOBN(0x25f5a71d, 0xd360d017), TOBN(0x4c0779b5, 0x29b0ed73),
+     TOBN(0xc662fedc, 0x9825a018), TOBN(0xeee89125, 0x61d4add0),
+     TOBN(0x1543814d, 0x92163d14), TOBN(0x79f2376f, 0x27370d3c),
+     TOBN(0xf80c6963, 0xcbe1af7a), TOBN(0xf2d521bc, 0xeb9e41f7),
+     TOBN(0xe241619f, 0xc1805864), TOBN(0x6f1d6166, 0xb2de204a),
+     TOBN(0x13c3f912, 0x50e68d0b), TOBN(0x32eb021d, 0xc4a24f5a),
+     TOBN(0x3f1452f5, 0x0e78c588), TOBN(0xa267bf19, 0xc9971e98),
+     TOBN(0x77a231a7, 0xe801c021), TOBN(0xf363c9b3, 0xc2666e80),
+     TOBN(0xb8eb0bf0, 0xae309a0a), TOBN(0xa9f52f58, 0x375b8fbc),
+     TOBN(0xb8e4f948, 0x1a4993b7), TOBN(0x50ce578e, 0x8f73c292),
+     TOBN(0x2437a4a6, 0x02e503d6), TOBN(0x20cdfc50, 0xe4c68ea3),
+     TOBN(0xfec5993b, 0x3616f348), TOBN(0x5d96b4c5, 0xc0c420df),
+     TOBN(0x6b71cdf0, 0xcca0c48a), TOBN(0x00e71b54, 0xc1aea033),
+     TOBN(0x247f5baa, 0x69f6d107), TOBN(0x4e3ec504, 0x050c3704),
+     TOBN(0xf2b2be8a, 0x7a438e93), TOBN(0x240b632d, 0xa331e01e),
+     TOBN(0x61e66557, 0x91150675), TOBN(0x32364134, 0x95a99a28),
+     TOBN(0x5e5de136, 0xd3399e1e), TOBN(0xe38bab00, 0xfe2f8b75),
+     TOBN(0x736126de, 0x3a77db29), TOBN(0x7b0d1865, 0xf2aa121e),
+     TOBN(0x5545e45e, 0xdecf9cde), TOBN(0x9608ebce, 0x2318be70),
+     TOBN(0xe6596006, 0xfa55b0e5), TOBN(0x0c8c2f41, 0xbc4b6ca0),
+     TOBN(0xda1c5c7a, 0x92025945), TOBN(0xb114ba22, 0x5d3b0775),
+     TOBN(0x11cc6888, 0xcedb69a0), TOBN(0x4365bea8, 0x0f83c772),
+     TOBN(0x006fe80b, 0xbda8dbe3), TOBN(0x334adcb6, 0xc2d3d266),
+     TOBN(0x8c92c084, 0x1521de1c), TOBN(0x57873ef9, 0x78d8f72c),
+     TOBN(0xcfb0a7d0, 0x3b64dcd7), TOBN(0xf4c2f1fc, 0x558c9d55),
+     TOBN(0x110c2db2, 0xa0fbc656), TOBN(0x3cad85ca, 0xef5b6bea),
+     TOBN(0x7099dd0e, 0x4e0b1230), TOBN(0xc769b937, 0x098a2fcd),
+     TOBN(0x9209f550, 0x1e1e7407), TOBN(0x1b47255d, 0x1ba7cb47),
+     TOBN(0xd8aed0cd, 0x2c01b596), TOBN(0x1a1a2e11, 0x30efcda3),
+     TOBN(0xf771f93b, 0x36b1a5b5), TOBN(0x2ea34e3d, 0x14fcd251),
+     TOBN(0x6895cb31, 0xfd893979), TOBN(0x10b1d2c9, 0x14f556b4),
+     TOBN(0x835fdf7e, 0x6430bfa8), TOBN(0x1f4bbef5, 0x24bf4ba5),
+     TOBN(0xbc805aa5, 0xd562b5f1), TOBN(0x7101b9da, 0x35dac658),
+     TOBN(0x5b7f211d, 0xddc28e5a), TOBN(0xea89f24c, 0xd3d1cd0a),
+     TOBN(0xbaaa9ef5, 0x7567c80d), TOBN(0xe0d1f26d, 0x9a60c5ee),
+     TOBN(0xc88a044c, 0xab36cd64), TOBN(0xb8e03d02, 0x1b125de6),
+     TOBN(0xda0c1047, 0x3a707a66), TOBN(0x76ddb98f, 0x0c692d44),
+     TOBN(0xeccae586, 0xb15b7254), TOBN(0xeadc1b51, 0xe7e82423),
+     TOBN(0xd6779ff2, 0x7c3cb698), TOBN(0x0e271cb4, 0xdf6e7da6),
+     TOBN(0xeacf34c3, 0x45900023), TOBN(0xafd017ad, 0x03da2ba5),
+     TOBN(0x49266998, 0x27c7e6eb), TOBN(0x84ffa372, 0x6625bc7f),
+     TOBN(0xedec9247, 0x05c9cb15), TOBN(0xcfad0b90, 0x8075b84e),
+     TOBN(0x94bed316, 0xbc0898d3), TOBN(0x02481eec, 0x11f92525),
+     TOBN(0x19896e1b, 0x0d7e59d7), TOBN(0xa06adb6c, 0xf2bb3129),
+     TOBN(0x1539228e, 0x62a0a690), TOBN(0x98ac50b9, 0x8ae02bda),
+     TOBN(0xaf233c85, 0xe5cf21b9), TOBN(0x943348d3, 0xd6a9f599),
+     TOBN(0xf5a2f2d1, 0xdb363eaa), TOBN(0xe917e2c5, 0x7a8ea96b),
+     TOBN(0xc80b56c8, 0xbf5c8293), TOBN(0xcfc1c24f, 0xcdbb5c4f),
+     TOBN(0x7812dce2, 0xfbddf07b), TOBN(0xd4af2f9b, 0x0186013a),
+     TOBN(0x1fadcd16, 0x6fe8d485), TOBN(0xc3c2cd95, 0xc968f0b7),
+     TOBN(0xdbdd2ef0, 0x778bff58), TOBN(0x67369204, 0x8706da34),
+     TOBN(0x31cf3a66, 0xb8e70e35), TOBN(0x0b9e5cc5, 0xd333801f),
+     TOBN(0x1212a811, 0xf7177c4a), TOBN(0x9e266ec3, 0x2d379e12),
+     TOBN(0xc7382848, 0x2e8bbbf7), TOBN(0x3f3f1dc1, 0xa973be5f),
+     TOBN(0x534d04de, 0x786e727e), TOBN(0xfd7a5fbb, 0x225497dc),
+     TOBN(0x3c03a7fd, 0xb63b6320), TOBN(0xe77f8985, 0x5dc76e05),
+     TOBN(0xe8d14f32, 0x265f8b8f), TOBN(0xfeaab021, 0xb90549c9),
+     TOBN(0x7cd36751, 0x081ccea6), TOBN(0x7a001441, 0x1f1e8f7a),
+     TOBN(0x2e87b8a2, 0x1fdfd622), TOBN(0xe76138ce, 0x8bb4f812),
+     TOBN(0x9a5e8722, 0x71e03be4), TOBN(0x795e60f3, 0x153e0a5f),
+     TOBN(0x11d28438, 0xd0eb7d4c), TOBN(0x147884e1, 0x4254a845),
+     TOBN(0x6795f20f, 0x2a8693fb), TOBN(0x5a10d535, 0xee01bd1a),
+     TOBN(0xe39982c9, 0x218c0513), TOBN(0x6c23e5be, 0x1d4e6ab5),
+     TOBN(0x20a8c27f, 0x0f424e7c), TOBN(0x74ae9998, 0x3bbb486f),
+     TOBN(0x3fae61be, 0xb90ce3a1), TOBN(0xf0f5a1e4, 0x571c968b),
+     TOBN(0x6b9dded8, 0x7780d91b), TOBN(0x10f60ce2, 0x7597e866),
+     TOBN(0xf268ed02, 0xf1eb7d1c), TOBN(0xa49b5a46, 0x6030bf9b),
+     TOBN(0xc939c4e7, 0x251f8676), TOBN(0xbdfe5036, 0xe2b9928f),
+     TOBN(0x5abfbcc2, 0xbccf7f43), TOBN(0xb22067b6, 0x28c560af),
+     TOBN(0xecf07771, 0x04c6a2da), TOBN(0xa2bf88db, 0x8c4ae7dc),
+     TOBN(0x172396f2, 0x616675e8), TOBN(0x9abbb19c, 0x8bfcfbc2),
+     TOBN(0x52e26c06, 0xe85edd21), TOBN(0xfca4c4e0, 0xa65de52f),
+     TOBN(0x255e2d10, 0x281d58be), TOBN(0x93ec2934, 0x3614ed6c),
+     TOBN(0x36d6cc15, 0x6dc71abe), TOBN(0xaa2ad1ef, 0xf592ae78),
+     TOBN(0x39a82740, 0xcc9291fb), TOBN(0x6812b80f, 0x102600d8),
+     TOBN(0x64f4843c, 0x50c511d5), TOBN(0x28f5795e, 0x03445793),
+     TOBN(0x2c566372, 0x29f20b0c), TOBN(0xb168ca7a, 0x9e24474c),
+     TOBN(0xfadd8f80, 0xabe37949), TOBN(0xafa1bea2, 0x4cd69856),
+     TOBN(0x5ce6ed80, 0x46713b88), TOBN(0xaf8b5fb3, 0x4b3bb84d),
+     TOBN(0x134e5120, 0x29d53349), TOBN(0x1214f4f0, 0xcdcedefa),
+     TOBN(0xc346821b, 0x4bb405b9), TOBN(0x753afa86, 0xddd624d6),
+     TOBN(0x15fe543c, 0xc7c014e3), TOBN(0x6b3c0c5d, 0x43d08964),
+     TOBN(0xc77c8fb0, 0x745221aa), TOBN(0x3e144fce, 0x152995c9),
+     TOBN(0x57089907, 0xa61b96bc), TOBN(0xd19a912c, 0x5e05c1ee),
+     TOBN(0x7bcdc697, 0xa6ddd20b), TOBN(0xcb07e229, 0x2d5090f3),
+     TOBN(0x76afc803, 0xf089607e), TOBN(0x9f7a88b9, 0x637dae27),
+     TOBN(0x1352d8bd, 0x3bd20d78), TOBN(0xede1a780, 0x5ea79d4c),
+     TOBN(0x59a8222b, 0xf389e31d), TOBN(0xed066aa6, 0x5c09f3d4),
+     TOBN(0xade16197, 0x684529d3), TOBN(0x97bed904, 0x96a2a159),
+     TOBN(0xdd3da765, 0x1b695d68), TOBN(0xb8fa37e8, 0x02fecb9e),
+     TOBN(0x1af4311c, 0xbc0f7b99), TOBN(0x600bdd46, 0x2a492a7e),
+     TOBN(0x6aa9cb30, 0x45dc9d16), TOBN(0xaa494815, 0xc0b93019),
+     TOBN(0x1211834b, 0xba052dd8), TOBN(0xcdc0208e, 0x86822bf1),
+     TOBN(0x515eebd4, 0x8c8362a0), TOBN(0x9ea7b9f5, 0x9b90cf96),
+     TOBN(0x8418fe34, 0x3a0a5a48), TOBN(0x654d3c32, 0x331a2db1),
+     TOBN(0x22362ddf, 0xafde743c), TOBN(0x617a89e8, 0x6f6ee3ba),
+     TOBN(0xed5f3d04, 0xb7deb988), TOBN(0x31c2c9e6, 0xbbc8a6b2),
+     TOBN(0x8faa80e1, 0x81a3f184), TOBN(0xa7183488, 0x51ecc548),
+     TOBN(0xe67512d0, 0xa3780d0a), TOBN(0x9f868036, 0x822db54d),
+     TOBN(0x6c74490a, 0xe555beab), TOBN(0xe747e666, 0xd989d6be),
+     TOBN(0xf8346dd6, 0xdf8cd308), TOBN(0xe7ca105f, 0x4745cd8e),
+     TOBN(0xee059c58, 0x31055db8), TOBN(0x90f4053a, 0x18b38aa0),
+     TOBN(0xbb2e7fc3, 0x41081a21), TOBN(0x3602525e, 0x45b33a71),
+     TOBN(0xff21f2aa, 0x2b411945), TOBN(0xbeaadbd3, 0x064ccb11),
+     TOBN(0xc35f6950, 0xfe94629d), TOBN(0x1cbaa935, 0x9f860b15),
+     TOBN(0x29b4bcd3, 0xf24f8f15), TOBN(0x0ae5b06a, 0xd29c8458),
+     TOBN(0xa645c31d, 0x1b6c2df1), TOBN(0x640b0990, 0xd804facc),
+     TOBN(0x7a4a7f59, 0x122b33e6), TOBN(0x94bb0b2b, 0x7479b828),
+     TOBN(0x0567272a, 0xc4cd4887), TOBN(0x676d6962, 0xfc8e4b0b),
+     TOBN(0xa712b020, 0x8661c0c2), TOBN(0x660e6aff, 0x279454a7),
+     TOBN(0xe1295106, 0x1cd25bfd), TOBN(0x7096885c, 0x077496a8),
+     TOBN(0xdbc47c92, 0x3006ab7b), TOBN(0x498761fa, 0x509205f3),
+     TOBN(0x5d1eaeca, 0xe85ecfee), TOBN(0x9fcddeed, 0x534f38f5),
+     TOBN(0x4d34ec80, 0x8af32f70), TOBN(0x476dffc9, 0x24b3b4e3),
+     TOBN(0xb45cd869, 0x8bbcda9f), TOBN(0x3b0079e7, 0xdf684c2d),
+     TOBN(0xcaf3eeb5, 0x765cd483), TOBN(0x0b9e25e6, 0x63945b62),
+     TOBN(0xfd73833e, 0x06492e0a), TOBN(0x4d2937e1, 0x9507ea57),
+     TOBN(0x3e532c2e, 0xcf562953), TOBN(0xe4baa2d4, 0x81ca80c3),
+     TOBN(0x4699e5c4, 0x28d22139), TOBN(0x69aab900, 0x6b1c665a),
+     TOBN(0xf6a37330, 0x641016ac), TOBN(0x335f14cb, 0x5f3b7c71),
+     TOBN(0x94a6c868, 0xfacd904f), TOBN(0xb1127cc4, 0x2ec2bf99),
+     TOBN(0x0ccfceb3, 0xa4b72d69), TOBN(0x16b786a3, 0x55172f5b),
+     TOBN(0x51ebe029, 0xe093a729), TOBN(0xf57f4a1e, 0xc40c4487),
+     TOBN(0x8aaf0dd6, 0xa8ed5a3d), TOBN(0x617c51f7, 0x811f35d6),
+     TOBN(0x18c7ac62, 0x11e98d29), TOBN(0x71c578c4, 0x2c904ea8),
+     TOBN(0x4626b0ad, 0x3c4ef8bd), TOBN(0x121eb676, 0xa390be8b),
+     TOBN(0xcb7249f5, 0x154e297d), TOBN(0x678ad966, 0xc2288ba0),
+     TOBN(0x3c2ab066, 0x57cc9cbc), TOBN(0xe32c1d45, 0x80c8fbda),
+     TOBN(0xd2f152cb, 0xf0b35526), TOBN(0xc7f75fd4, 0x13877dfb),
+     TOBN(0x8603feff, 0xe83ca4a2), TOBN(0x6be89bb3, 0xcd680589),
+     TOBN(0x5650549e, 0x45e1f141), TOBN(0x7dab03b8, 0xa55ffadc),
+     TOBN(0x342edda4, 0x2dc5d31f), TOBN(0xa97451ac, 0x9af8105a),
+     TOBN(0x796e1fe3, 0x705b8fd7), TOBN(0x6252a7ba, 0x02d131b8),
+     TOBN(0x3db2ab14, 0x086c3099), TOBN(0xeb763df5, 0x9db0ce72),
+     TOBN(0xe7b57bab, 0x52b62fa5), TOBN(0x6076d449, 0x88b820bd),
+     TOBN(0xc43e1f61, 0x1b660123), TOBN(0xc802d40b, 0x189eace5),
+     TOBN(0x1f2a2a91, 0x341309a1), TOBN(0x8680be67, 0x414db96b),
+     TOBN(0x65dd0396, 0xc846e288), TOBN(0x8a1d871e, 0xb0bbea85),
+     TOBN(0x623e2408, 0x8ff931c6), TOBN(0x4933ffda, 0xe14c5941),
+     TOBN(0x72688986, 0xb2cbff67), TOBN(0xe51504d8, 0x8cf79450),
+     TOBN(0x50cd0a3f, 0xfeba1168), TOBN(0x08d2e0fe, 0xcd833df8),
+     TOBN(0xdbd60827, 0x0a4370ed), TOBN(0x010cf800, 0x66f4f58d),
+     TOBN(0xffa29252, 0x144e9656), TOBN(0x90b896a2, 0x9d1e9d61),
+     TOBN(0x1802257c, 0x81f7b4d3), TOBN(0xd7758e8b, 0x595612a5),
+     TOBN(0x751882d8, 0x241b4dd2), TOBN(0x7dae3003, 0xfe177abe),
+     TOBN(0x8f4d5dc4, 0xee6fe1cd), TOBN(0x93a9cd5b, 0xb08f625d),
+     TOBN(0xa4d6ee1a, 0xf91cc442), TOBN(0xe05976cd, 0x594d172f),
+     TOBN(0xfb4064c6, 0x6e762b2e), TOBN(0xb2068ad9, 0x51a0156d),
+     TOBN(0x0d2d5b26, 0x24f06e82), TOBN(0xad70f276, 0x8c85a9a9),
+     TOBN(0x00ede5d5, 0x0ed413cb), TOBN(0x245be28b, 0x927064d2),
+     TOBN(0x06eb2825, 0x2af70d77), TOBN(0x472af630, 0x52b0592b),
+     TOBN(0x493afd98, 0xd881d50e), TOBN(0x56fa76a8, 0x1189c989),
+     TOBN(0x775665d2, 0xfaa974f7), TOBN(0xe395ccdb, 0xc3f54eff),
+     TOBN(0xf0a40e4e, 0x3fc83a7f), TOBN(0xc3b11d22, 0x4c00087a),
+     TOBN(0xef8d2f06, 0xddb50678), TOBN(0x6e41f315, 0x6cd5f681),
+     TOBN(0x7c9d7a3d, 0xa1b97891), TOBN(0x8b297d75, 0xa0a41260),
+     TOBN(0xca44b65c, 0x3806a30a), TOBN(0x125c5702, 0x61a6206e),
+     TOBN(0x311842a2, 0x87003e1e), TOBN(0xe049a7d0, 0x4513d726),
+     TOBN(0x8022c2d0, 0x7b123469), TOBN(0x76533934, 0x86294393),
+     TOBN(0x6a6e84e7, 0x892e7bc4), TOBN(0x7daf8b11, 0xdb2007fe),
+     TOBN(0x092d1914, 0x923e185c), TOBN(0x5ec11237, 0x3def87c2),
+     TOBN(0x38019e96, 0x18742a51), TOBN(0xe05ea79e, 0x4808ca10),
+     TOBN(0x1fc8ae26, 0x134cbf9b), TOBN(0x14054672, 0x01b4c1c4),
+     TOBN(0x32abf912, 0x64051972), TOBN(0x0edaa925, 0x1af62fca),
+     TOBN(0x58fa82e2, 0x3c47d01a), TOBN(0xdb12a452, 0x780762c6),
+     TOBN(0x16d5a733, 0xfdbf4683), TOBN(0x1d7e8507, 0x2f798deb),
+     TOBN(0x259aa9b9, 0x9eab12f7), TOBN(0xb13e6e41, 0x91261397),
+     TOBN(0x564706fa, 0x32602f2e), TOBN(0x50daef9c, 0x9c2283ef),
+     TOBN(0x9275f219, 0xf07a196e), TOBN(0xfc58ebea, 0xbb8fcd35),
+     TOBN(0x5d1025f1, 0xbad69c11), TOBN(0xcf364154, 0x1605c11b),
+     TOBN(0x427bd117, 0x2992276c), TOBN(0x5545bec5, 0x6a73cbb3),
+     TOBN(0x86855c2f, 0x133f3266), TOBN(0xb3d753d1, 0x67d9e5b2),
+     TOBN(0x2134b384, 0xc9fb343b), TOBN(0xb572f5d6, 0xb0e12b7a),
+     TOBN(0x7ee5852f, 0x392d24fe), TOBN(0x73559fae, 0xc4f285f9),
+     TOBN(0x269cb9e7, 0x7711c525), TOBN(0x4badfc52, 0xf00d5606),
+     TOBN(0xef66d726, 0xb465df15), TOBN(0x83eb59a3, 0xaa4a301b),
+     TOBN(0xdb406469, 0xed329b12), TOBN(0x6eb95cc9, 0xd933eb45),
+     TOBN(0xe2dabfa4, 0x6b638bdd), TOBN(0x7a5d0098, 0x031df114),
+     TOBN(0xe22d8f3e, 0x38dbfaaf), TOBN(0x2306fd54, 0xd79d1ce1),
+     TOBN(0xda324535, 0x7acb7cce), TOBN(0xde6fcc16, 0x88f61a1e),
+     TOBN(0xaf3e4894, 0xb730fe5c), TOBN(0x7a3e4a7d, 0x28adf897),
+     TOBN(0x352c0069, 0xb160ae0e), TOBN(0x225cfb67, 0xee52c58a),
+     TOBN(0x12391b71, 0x96b2505f), TOBN(0x8d811bee, 0x3758141d),
+     TOBN(0xc941524d, 0x8cd82e11), TOBN(0x0feb26a5, 0xbb83a251),
+     TOBN(0x60ad0665, 0x76da8411), TOBN(0xe3c033d9, 0x88d4a968),
+     TOBN(0x767b3c05, 0xde120078), TOBN(0xab7da95a, 0x9f31e1e8),
+     TOBN(0x7ad9b263, 0xb93e7cb7), TOBN(0x280f6bc3, 0xfd626ea3),
+     TOBN(0x746c3945, 0x62713cad), TOBN(0xfa2b4599, 0x3a4edce8),
+     TOBN(0x8792d0cb, 0x10ab7f93), TOBN(0xfa38d031, 0xc25a2a86),
+     TOBN(0x6914db0b, 0x08b028e8), TOBN(0x75a98aa0, 0x383cab40),
+     TOBN(0x462e6b6d, 0x6da884bf), TOBN(0x2b0f682b, 0xd3aa74b1),
+     TOBN(0xb3b7995e, 0x5cee0a83), TOBN(0xe99fca2b, 0x3cce609a),
+     TOBN(0x342c41c9, 0x45451744), TOBN(0xb00d3c24, 0xc81be29f),
+     TOBN(0xd1e64d86, 0x022e8d27), TOBN(0x404550be, 0xbcf67326),
+     TOBN(0xc7c510f0, 0xc8aab829), TOBN(0xb61ae647, 0xa90c605d),
+     TOBN(0x582ad9c9, 0x02db8602), TOBN(0x732b19ed, 0x71cb4397),
+     TOBN(0xea097c35, 0x265e5369), TOBN(0xea7c368f, 0x9d5ea687),
+     TOBN(0x7fc3b213, 0x8fcae7f1), TOBN(0x641daa3f, 0x49c54942),
+     TOBN(0x0696372b, 0x404c39a6), TOBN(0x56815716, 0x87b4b181),
+     TOBN(0xa6e156b3, 0xfca24eb8), TOBN(0xf278eeae, 0xd078a39c),
+     TOBN(0x046566d1, 0xac762dbe), TOBN(0x662ef0f6, 0x625ed2e8),
+     TOBN(0x15499e72, 0x650e4764), TOBN(0x361ccef0, 0x84edf50e),
+     TOBN(0x2441f6f4, 0x1f4a2200), TOBN(0xf36fff06, 0xdb730d58),
+     TOBN(0xcc18624d, 0x3c01edc7), TOBN(0x4889078f, 0x8a77e5bb),
+     TOBN(0x02294e3b, 0x75f8dd8a), TOBN(0x5f6f6057, 0xfc4113c5),
+     TOBN(0xb5300e0d, 0x6f699f18), TOBN(0x52cce358, 0x639dc977),
+     TOBN(0x5dbe59b8, 0x328fd50b), TOBN(0x81500be6, 0x39d73c2f),
+     TOBN(0x409ac4d7, 0x96ae313d), TOBN(0x15205b7b, 0x5b16c316),
+     TOBN(0xe272300c, 0xfc688c09), TOBN(0xb412cf39, 0xbdf71f2d),
+     TOBN(0xe3ab9c44, 0xf85b23d4), TOBN(0x5c14085c, 0x7b662694),
+     TOBN(0x24b0b385, 0x9956d07b), TOBN(0xfa8ea968, 0x30b2c82d),
+     TOBN(0xd443b2aa, 0x6d403b58), TOBN(0x6da53ecb, 0xe7fc8d57),
+     TOBN(0x29655314, 0xdfdf488f), TOBN(0xb418943d, 0xadc57e2b),
+     TOBN(0xbaf090f1, 0x6395a287), TOBN(0x8fdb4fc8, 0xd62f5b38),
+     TOBN(0x115653c0, 0x371c9db1), TOBN(0x6f5e1f39, 0x96463359),
+     TOBN(0x106aaf1e, 0x825e6273), TOBN(0xba22b7db, 0x398cbe1b),
+     TOBN(0x3b545300, 0x62b6bf7e), TOBN(0x495d7d27, 0x5bb6f993),
+     TOBN(0xf558fc5d, 0x3f00290b), TOBN(0xdddbeb3e, 0x2cfc2381),
+     TOBN(0xca402179, 0x65c79326), TOBN(0x376ce4ac, 0x33b1973c),
+     TOBN(0xd6e65ae4, 0x9b30749a), TOBN(0xdf68ee04, 0x5f54bf5a),
+     TOBN(0xa982948d, 0x4cfdb41a), TOBN(0xddd1d3ba, 0xf2a7b4b3),
+     TOBN(0xf52a6b33, 0x581aaba1), TOBN(0xc3258264, 0x894ebf68),
+     TOBN(0x26c32c27, 0x84665ac1), TOBN(0xda0190eb, 0x20119b0e),
+     TOBN(0x4a599db7, 0xb86745c1), TOBN(0xf9570f50, 0x58964b41),
+     TOBN(0xb34d039b, 0xe0648365), TOBN(0x2cd7fde0, 0x5c5f61e1),
+     TOBN(0x76f514a3, 0xbc6b08cc), TOBN(0xc957b50d, 0x18a3cabf),
+     TOBN(0x775fc56a, 0x2334cd1f), TOBN(0x7bfe3864, 0x67ec91c6),
+     TOBN(0x99037daa, 0x35ad3a9a), TOBN(0x17ffe391, 0xb7ca5657),
+     TOBN(0x19f6d369, 0xfef04aef), TOBN(0x8030b467, 0xd1876f8c),
+     TOBN(0xa014be02, 0x3cd7878f), TOBN(0x03c22a58, 0x3358c943),
+     TOBN(0x3c77f083, 0x2a257094), TOBN(0x47386957, 0xd962a04f),
+     TOBN(0x768da40c, 0x82da3329), TOBN(0x1507350d, 0x458219cf),
+     TOBN(0xf460aed3, 0x4397ee7c), TOBN(0xf249e4cc, 0x36fbc7fe),
+     TOBN(0xc42d9da8, 0xaab03dfe), TOBN(0xa3d73ce3, 0x435ab9f3),
+     TOBN(0x86dddbc3, 0x3813a3f3), TOBN(0x86d5779a, 0xb79c32a3),
+     TOBN(0x7c3d9aff, 0x028a2c3f), TOBN(0xc687e71b, 0xb1add2bf),
+    },
+    {
+     TOBN(0x4612a562, 0x09b3fed3), TOBN(0xf296c17a, 0x3579966a),
+     TOBN(0xa124a36f, 0x07960971), TOBN(0x6d03b214, 0x380c4a05),
+     TOBN(0xcb0746e2, 0x70f1f268), TOBN(0xcc9b47ff, 0x9341aea4),
+     TOBN(0x1b3662d5, 0x6d2f59cc), TOBN(0xa6c65b2d, 0xd4b1a15d),
+     TOBN(0xf96c113a, 0xcccb0a4b), TOBN(0x24c26bba, 0x3615f016),
+     TOBN(0x52fe115a, 0xeead2f5b), TOBN(0x85623d26, 0x0d7aaabb),
+     TOBN(0x50791fd0, 0x31a2564f), TOBN(0x3659974d, 0xcd0d59a4),
+     TOBN(0x2cffdb74, 0x7a8b7466), TOBN(0xcf6b36e5, 0x514787b0),
+     TOBN(0x8afccd36, 0x4ab1ccd2), TOBN(0x67314635, 0x1c03ab29),
+     TOBN(0x458f36bf, 0xd7ff3a32), TOBN(0x70e9e789, 0xfcf71648),
+     TOBN(0xf3764534, 0xa6e904cf), TOBN(0x2d6130b1, 0xf4bdd378),
+     TOBN(0xc61c98fb, 0x1ca5ce34), TOBN(0xda11f502, 0xa4a684f5),
+     TOBN(0x8d9daa41, 0xb6223f04), TOBN(0x803c9c0e, 0x841c3fab),
+     TOBN(0x60eee3f9, 0xc50b42cf), TOBN(0xaf4a7a5a, 0x77298193),
+     TOBN(0xd379c2e1, 0xbf809ad6), TOBN(0x903ab4b1, 0xf67c0ff2),
+     TOBN(0xc779d7ed, 0x90f8e637), TOBN(0x968b0cc0, 0x2cf3d363),
+     TOBN(0xaadfa857, 0xacf51940), TOBN(0x0c789d1e, 0x50156581),
+     TOBN(0x5e79cef7, 0x62cff8f4), TOBN(0x54cdaba9, 0x65eb0d49),
+     TOBN(0xdf7a5828, 0x3321c57e), TOBN(0x8571e6e2, 0xa21a51a6),
+     TOBN(0x0b9b482b, 0xc3726e69), TOBN(0x3bc201e3, 0x1d92b657),
+     TOBN(0x271c58bb, 0x8a3b4cf8), TOBN(0x269fc422, 0x717eb539),
+     TOBN(0xe82644e9, 0x5b381fe1), TOBN(0x27fb0822, 0xcb62982f),
+     TOBN(0xb0acd51c, 0x5b5ec726), TOBN(0xfd01053d, 0xea4eff73),
+     TOBN(0x465311dd, 0x00b11c80), TOBN(0xe50a8615, 0x2ed8460c),
+     TOBN(0x3eade5eb, 0x7b2243a0), TOBN(0xa59ec933, 0x77424d11),
+     TOBN(0x65a8e1aa, 0xf5c7c3b7), TOBN(0x008399fa, 0x0c1db536),
+     TOBN(0x80b20e97, 0xfb194a74), TOBN(0x2316fb9b, 0x43be90dc),
+     TOBN(0xb2773b23, 0x0da4d913), TOBN(0x945d0671, 0xce973d27),
+     TOBN(0x64ca871c, 0xb79f82af), TOBN(0x31304b02, 0x2dab52f6),
+     TOBN(0x1825ab54, 0x928239a7), TOBN(0x740413b2, 0x8e4ad736),
+     TOBN(0xc5c5d3fa, 0x44071d19), TOBN(0x83e438f1, 0x3f0b2da8),
+     TOBN(0xfd759448, 0xc70a1981), TOBN(0x13e0c7ee, 0x565ebae3),
+     TOBN(0x31b74b0a, 0x26bd7c0a), TOBN(0x66e0e8e8, 0xd280cb56),
+     TOBN(0x086795e6, 0x3d1c83d2), TOBN(0x59e678da, 0x396ecf25),
+     TOBN(0xab3c8d74, 0xf015a36e), TOBN(0x0d19aed3, 0xadc03171),
+     TOBN(0xc83b787f, 0x5a263686), TOBN(0x46b94ad0, 0x9057ed63),
+     TOBN(0xfbf783a7, 0x90979da2), TOBN(0xf04dd6a0, 0xa335c784),
+     TOBN(0x6e3c2554, 0x87d93c4c), TOBN(0xe3e6b289, 0x47994eb3),
+     TOBN(0x473c0684, 0x1b74ba16), TOBN(0x4e959eb4, 0xabe84e1c),
+     TOBN(0xdc3bfd51, 0x7c4a67b7), TOBN(0xb4e3cb85, 0x5095bd6e),
+     TOBN(0x96fc11f0, 0x3229fb05), TOBN(0x598227e4, 0x4b36c83a),
+     TOBN(0xdc69ad06, 0xd46fca66), TOBN(0x14cc98e5, 0x703ad6be),
+     TOBN(0xf0fdd142, 0x6b22cd50), TOBN(0x9b821fe2, 0xf89c1a5d),
+     TOBN(0xa3762dca, 0x829f9a74), TOBN(0xf65a584a, 0xf0c320cc),
+     TOBN(0x58f4eaba, 0x5568f242), TOBN(0x83b0c37b, 0x029afc1f),
+     TOBN(0x93de2d27, 0x994d7dfc), TOBN(0x0d9a6edb, 0xb1679532),
+     TOBN(0x3b834279, 0x95f085b0), TOBN(0x46ebac98, 0xa299355f),
+     TOBN(0x0212e489, 0x044427f8), TOBN(0xf9e4ce34, 0xa2f37d0e),
+     TOBN(0x0fa328d6, 0xfdc9e233), TOBN(0xd5c8afab, 0x51ae732f),
+     TOBN(0x85e59553, 0x83c00cee), TOBN(0x9fce31f9, 0x87505db4),
+     TOBN(0x33ea5eb6, 0x7069d066), TOBN(0x10e32a39, 0xf01c0ce9),
+     TOBN(0xf170233c, 0x0c4f1a2e), TOBN(0x8a907760, 0xbd4cb366),
+     TOBN(0xda593421, 0x79bf05e3), TOBN(0x7730907a, 0xce49a5bd),
+     TOBN(0x0dfb8a97, 0x09be5c7b), TOBN(0x6f50c692, 0x23eb936e),
+     TOBN(0xc6160551, 0xcb18ff1a), TOBN(0xfa1d23fd, 0x661cc384),
+     TOBN(0xfef12307, 0x7ddec262), TOBN(0xd1aca960, 0xb15af580),
+     TOBN(0x7eab8a59, 0x2ee50fbd), TOBN(0xe7f71845, 0xbe1e7a42),
+     TOBN(0x5f46a511, 0x6121e573), TOBN(0xa25dacbf, 0x38ff7eba),
+     TOBN(0xe8aefcc7, 0xf58f047c), TOBN(0xc343aaa7, 0xb538aace),
+     TOBN(0x3e58cdda, 0x3c340b1f), TOBN(0xb0e9867c, 0x1fb98ccb),
+     TOBN(0x034dd314, 0xef7750f0), TOBN(0x2ceaa705, 0x22da84d7),
+     TOBN(0x4561a254, 0xfc0d647b), TOBN(0x81cf0915, 0xbbe01157),
+     TOBN(0x547a3d4e, 0x34b798eb), TOBN(0xbb5dd625, 0x8b1c7544),
+     TOBN(0x94fee01b, 0xc8194168), TOBN(0xdb4c25ff, 0xfaeb010b),
+     TOBN(0x1ff217fa, 0xe4d4a176), TOBN(0x8b46e6c2, 0xaf87f4f5),
+     TOBN(0xcf658775, 0x89734273), TOBN(0x73c4390d, 0x52746de9),
+     TOBN(0xb5c84899, 0xb01c7a06), TOBN(0xfa5ffe4d, 0xedd9ef63),
+     TOBN(0x28a313c5, 0xa1a8b2d4), TOBN(0xadd45f47, 0xdaf5a33f),
+     TOBN(0xc2dc9d13, 0x1fdb8694), TOBN(0xaa5e026c, 0x9a90b4d1),
+     TOBN(0x5edffd39, 0x208cbfa5), TOBN(0xf095b3fb, 0x72a4d6cc),
+     TOBN(0xbfca4e10, 0x6645fcc1), TOBN(0x14b872ac, 0x92408c6a),
+     TOBN(0x3d9261e1, 0xd0b82d2d), TOBN(0x13e4ecb6, 0x0294e75b),
+     TOBN(0xabd4541c, 0x3ac2ab9d), TOBN(0x025355b2, 0x4d5d1531),
+     TOBN(0x3d85f67c, 0xfb726ab8), TOBN(0x56e26c82, 0x6d6fc6bc),
+     TOBN(0xb24608bb, 0x495e05a0), TOBN(0x840e0978, 0xe5afdc5d),
+     TOBN(0x2cc543b5, 0x248727e2), TOBN(0xe48146da, 0x3bc8c021),
+     TOBN(0xa1b36baf, 0x530c98b7), TOBN(0x04503d7b, 0x5acf023b),
+     TOBN(0x96bc4449, 0x21de1503), TOBN(0xbb8a122f, 0xd2a9c89f),
+     TOBN(0x66df99df, 0xd5d4b133), TOBN(0x1bb4a13b, 0xc97d3e52),
+     TOBN(0xdab370f3, 0x79b318d6), TOBN(0xfa6c823e, 0x9f18552e),
+     TOBN(0xe5b27e78, 0x6388a194), TOBN(0x13270523, 0xc88ba441),
+     TOBN(0x9f309fbf, 0x4fecfef5), TOBN(0x72cd374b, 0x10afee60),
+     TOBN(0x16bd0e2e, 0x93dfe3af), TOBN(0x7e92096a, 0x24bc7e8e),
+     TOBN(0x144fdf82, 0xfec7f0bb), TOBN(0x5d1d4598, 0xe1f765f7),
+     TOBN(0xb6b91efc, 0x72c67697), TOBN(0xc7a2ceaf, 0xb2487905),
+     TOBN(0x4a4c9e63, 0x7fb24d99), TOBN(0x7ed373ac, 0x4d742089),
+     TOBN(0x3f9e6ae1, 0x9149ac54), TOBN(0x64fd7fef, 0x0611efc8),
+     TOBN(0x1c38da32, 0x3d779af6), TOBN(0x6893691b, 0x0a1681f5),
+     TOBN(0xbac29978, 0x50a0fa72), TOBN(0x98d5c82e, 0xba55c665),
+     TOBN(0xf3e5b58e, 0x2d4b76bc), TOBN(0xfae27d9a, 0x90615b32),
+     TOBN(0xb93bc327, 0xd49b2500), TOBN(0x7d9d4bff, 0xbbc213cd),
+     TOBN(0xf985fe72, 0xd1ee81c4), TOBN(0x6e2a94d1, 0x381f9e48),
+     TOBN(0x1f09b225, 0x14fb9624), TOBN(0x2eba4ff8, 0xca4229d7),
+     TOBN(0x5b159dd1, 0x21dc8c19), TOBN(0x1e1f968f, 0xb1aa553e),
+     TOBN(0x6ea25976, 0xc7674d52), TOBN(0x98e73adc, 0x7b283501),
+     TOBN(0x7cfce0e1, 0xd39468c2), TOBN(0x7aad0af9, 0x08438a62),
+     TOBN(0x2291cdd0, 0xb2a3dde2), TOBN(0x3a625d50, 0xf77a0aa4),
+     TOBN(0x3be0fba2, 0x5fbc5a0a), TOBN(0x67b7598a, 0xe794bf46),
+     TOBN(0x3673d805, 0x531ad772), TOBN(0xf9a9b392, 0x03e8479f),
+     TOBN(0x142d264c, 0x2e16a126), TOBN(0xc20409ac, 0x5a2f6f2c),
+     TOBN(0xd9d84438, 0xcd43f273), TOBN(0xfecc561d, 0xbda7be72),
+     TOBN(0xc4b113c6, 0x241b7ec2), TOBN(0xfc5bc32b, 0x40dba9e3),
+     TOBN(0x70853d39, 0xd56bca47), TOBN(0x2b9a902d, 0xa5306217),
+     TOBN(0x2bb1836d, 0x2017bfd0), TOBN(0x829ce116, 0xcd1c2768),
+     TOBN(0x42d5fcf8, 0x697097f5), TOBN(0xc1fe7be6, 0x1e936db5),
+     TOBN(0xcbc5cdcc, 0xcb6a81d4), TOBN(0xab1e4ecb, 0xafef5ffa),
+     TOBN(0x3cbbdf76, 0xb933c216), TOBN(0xdb5808da, 0x503607e2),
+     TOBN(0x5bdaab7c, 0x6bc96246), TOBN(0x91e5d17c, 0x68274629),
+     TOBN(0xa3cd09f6, 0x2eb1db21), TOBN(0xbe370485, 0x92c3e3e1),
+     TOBN(0xeb51fa29, 0x6aa43da5), TOBN(0x2c7fa809, 0xd726625e),
+     TOBN(0x90c6786f, 0xf0ec0e99), TOBN(0xd315af33, 0x08135cbf),
+     TOBN(0xc1b60172, 0x1504751b), TOBN(0x88674e2d, 0x0e28781a),
+     TOBN(0x6aa74055, 0xed74e633), TOBN(0xc44e740f, 0x7d06ce02),
+     TOBN(0x8b40bc5e, 0xa33b8d5e), TOBN(0x42d3539f, 0x20f00f14),
+     TOBN(0xd9f1f5cd, 0x3307ef15), TOBN(0xa9fe4dfb, 0xc8599bcc),
+     TOBN(0x31cb6703, 0xefa80b8d), TOBN(0x4172b46d, 0x53bb73fe),
+     TOBN(0x85a70280, 0x20e4c321), TOBN(0x999a0d07, 0x5ac075f3),
+     TOBN(0x59a62b62, 0x7bdb478c), TOBN(0x9aeb710a, 0x573c403b),
+     TOBN(0x1c099614, 0x950bb8fc), TOBN(0xc1efafab, 0x5dc09741),
+     TOBN(0x0de58ca5, 0x7296a74b), TOBN(0x657116a4, 0xf5be2ec4),
+     TOBN(0x0ce52f0f, 0xcb199b77), TOBN(0xdcdc5cb9, 0xbcd11438),
+     TOBN(0x587a68ff, 0x4777327b), TOBN(0x55d9abb7, 0x1cc6fbb3),
+     TOBN(0xf1970b82, 0x9eeb28a9), TOBN(0xe1ab4e14, 0x4ceef00f),
+     TOBN(0x184d3fb6, 0xf7462893), TOBN(0x9942a008, 0xc8ea54fd),
+     TOBN(0x1fee0f77, 0x1e33b2a3), TOBN(0xd4bed81f, 0x9f789870),
+     TOBN(0x6396feea, 0x6ef05b7e), TOBN(0x9c5d6a01, 0x2640b62a),
+     TOBN(0x170cfec9, 0x6834bea4), TOBN(0x68d16728, 0xe131feca),
+     TOBN(0x4be9c5d6, 0x00affb4d), TOBN(0xe34a423c, 0x99a6f256),
+     TOBN(0x1a254e4a, 0x09b9ed61), TOBN(0x902bc066, 0x30b10207),
+     TOBN(0xd2d5ed01, 0x62121f53), TOBN(0x0ba86811, 0x30f1b518),
+     TOBN(0x7916c132, 0xabe139c9), TOBN(0xb3a30fe0, 0x62c4f337),
+     TOBN(0x85d0a769, 0xaa5693be), TOBN(0x2d414379, 0xe3c7687b),
+     TOBN(0x92b0cb3c, 0x94958719), TOBN(0xb78aa37b, 0x4ec6575d),
+     TOBN(0xd035aae1, 0x4f1bf26a), TOBN(0x1383992d, 0xd31d5108),
+     TOBN(0x53ecc535, 0x92bdd6f5), TOBN(0xa9925ff6, 0x08c622ca),
+     TOBN(0xcaa3146e, 0x916d890c), TOBN(0x8cd0f12e, 0xb9c10220),
+     TOBN(0xcb6ad82b, 0x7e12a730), TOBN(0x3f89047c, 0xac9485db),
+     TOBN(0x6f926117, 0xfea2d970), TOBN(0x87b0cd9d, 0x46a19ecb),
+     TOBN(0x98bb5b02, 0x01e45bf6), TOBN(0xfc814620, 0x2ed7716d),
+     TOBN(0x8d6808cf, 0x4f5caa95), TOBN(0x3b57df03, 0x082f799e),
+     TOBN(0x469e1854, 0x2df84ca2), TOBN(0x00dd62eb, 0x64aac069),
+     TOBN(0x7d3ee9ce, 0x88d9efff), TOBN(0x9faed6a2, 0xbb830ffc),
+     TOBN(0xd073aac1, 0xd2d74f58), TOBN(0xf69e96b4, 0x2d44199e),
+     TOBN(0x6cb3a3b1, 0x83ed62ca), TOBN(0x472489fd, 0xd799acf8),
+     TOBN(0x5f84382d, 0xb63a36cc), TOBN(0x6ba1de87, 0x92d5b175),
+     TOBN(0x25aab130, 0x516101b7), TOBN(0x6f69c3fc, 0x5f039793),
+     TOBN(0xd28439ee, 0x89e3da4f), TOBN(0x8cb40a0e, 0x5e6b2b61),
+     TOBN(0xdfa55805, 0xe3d6650d), TOBN(0x2651f6c7, 0x0be59fd2),
+     TOBN(0x290e0044, 0x140d01c8), TOBN(0x78afa0a4, 0x62ea488f),
+     TOBN(0xc4e39971, 0x91eaa932), TOBN(0x8a9ef3a2, 0xfe2e79dc),
+     TOBN(0xdcfae315, 0x50705b7e), TOBN(0x73324dca, 0xd4be3d75),
+     TOBN(0x900bdd43, 0x03a91415), TOBN(0xc3ed02ed, 0xedfdc74d),
+     TOBN(0x509bd1d6, 0xf22b4a66), TOBN(0xfd8ed371, 0xb78d264b),
+     TOBN(0x562b2d3a, 0xa419134f), TOBN(0x80a2c274, 0x7a57a51e),
+     TOBN(0xebba5317, 0x8c662472), TOBN(0xebafedf2, 0xa0be71fb),
+     TOBN(0x0c5b9c1c, 0xb77899c8), TOBN(0x82448008, 0xc4888cb5),
+     TOBN(0xb795ea00, 0x78401c3b), TOBN(0x86776990, 0xa85ab971),
+     TOBN(0xdd81393b, 0x185739da), TOBN(0x76d0031f, 0x58136c97),
+     TOBN(0x6aceaa56, 0x641d39d1), TOBN(0x918844c7, 0x39be7ca8),
+     TOBN(0xa63993f7, 0xe18efc54), TOBN(0xb5369150, 0x4af0f30a),
+     TOBN(0x9bc2068c, 0x3d04af4f), TOBN(0xf309dff9, 0xa7796ed2),
+     TOBN(0x46e9a59d, 0x4e15b6a2), TOBN(0x617aaeba, 0xc22ef488),
+     TOBN(0xd91a8f90, 0xa15cf0cb), TOBN(0xc6ce12a4, 0xc30fb779),
+     TOBN(0xf3b80254, 0xb9d0a7ff), TOBN(0x32a63bf9, 0x6e9b6fa1),
+     TOBN(0x3e1ac837, 0x546fe4a8), TOBN(0x91ed89a5, 0x1279c7ef),
+     TOBN(0x8eb7b88e, 0xc73e9dea), TOBN(0x96d07205, 0x18238af0),
+     TOBN(0x56ebf306, 0xe96abf80), TOBN(0x5088ce24, 0x52c4b10f),
+     TOBN(0x65293176, 0xc979137f), TOBN(0x824642fb, 0x228d300a),
+     TOBN(0x968963a8, 0x7836aea5), TOBN(0x2d4c556c, 0xfabbfac1),
+     TOBN(0xa4c389bb, 0xd3f9977a), TOBN(0x2e8b2818, 0x99b4ccb6),
+     TOBN(0xc1fd8565, 0x6cb67df6), TOBN(0x0ac57d2a, 0xa72d2be8),
+     TOBN(0xa51ce6b8, 0xb8224ead), TOBN(0x33f7b468, 0xf417d744),
+     TOBN(0xcf8c80af, 0xf9f0bdf4), TOBN(0x0728f880, 0xd3222dd6),
+     TOBN(0x436188a3, 0x653afc12), TOBN(0x0f8bf160, 0x3c7940bb),
+     TOBN(0xdc18c13f, 0x424dcd2a), TOBN(0x038c1842, 0x20d3cd1f),
+     TOBN(0xed7f86a5, 0x7b12fd42), TOBN(0xa75ab77b, 0x7aaf1881),
+     TOBN(0x5c3d7612, 0xdf0574e2), TOBN(0x2eeeeb6f, 0x719414ce),
+     TOBN(0x797c5771, 0x90349fc4), TOBN(0x0d850f73, 0x2232eb33),
+     TOBN(0x0a0744f3, 0x2638c051), TOBN(0x739e6278, 0xb6e7dbfa),
+     TOBN(0xa77f286d, 0x659fc5f5), TOBN(0xb18b7cf1, 0x9654b0eb),
+     TOBN(0x5a2089ac, 0x6062e78e), TOBN(0x152f1804, 0xdfa6fb44),
+     TOBN(0xe8a404b4, 0xb61e6faa), TOBN(0x4774d30f, 0x08d06ea8),
+     TOBN(0xd7119b91, 0x3c359648), TOBN(0x850b02bd, 0x09473ff7),
+     TOBN(0x4db6f9a0, 0x936b7868), TOBN(0x84064dd5, 0xae38c3c5),
+     TOBN(0x294d6831, 0xfe448461), TOBN(0xc3c6f446, 0x42cd2105),
+     TOBN(0xa4412eb0, 0x3a2fdcae), TOBN(0x394c3774, 0x3d5a9181),
+     TOBN(0x58f19024, 0x5ca87c4b), TOBN(0xba1879db, 0x89ad5685),
+     TOBN(0x43c55c6a, 0x803c2589), TOBN(0xae1fad20, 0xa8249c65),
+     TOBN(0x4929e89f, 0xe0aff809), TOBN(0x19755ec2, 0x1769a00a),
+     TOBN(0x3b6a207b, 0xc242f335), TOBN(0xeca054ef, 0x090edab0),
+     TOBN(0x217e9c8b, 0xcd9e1c26), TOBN(0x917c2ecd, 0x35d4ac57),
+     TOBN(0xdc869d5d, 0xad33911d), TOBN(0x22d9d860, 0x2e828bd7),
+     TOBN(0x89262252, 0xf38dfaa1), TOBN(0x155c96ce, 0xeb9cd8d7),
+     TOBN(0xb0082b5d, 0xed5ebcc4), TOBN(0x7b6f9203, 0x17182086),
+     TOBN(0xaefe28aa, 0xee92aa6d), TOBN(0xbe67090c, 0x9aaaa0eb),
+     TOBN(0x88c5fbf1, 0x2f8ef18d), TOBN(0xbdc8bef1, 0xdd1fd65f),
+     TOBN(0xfb7052f5, 0xa9c7b483), TOBN(0x49634258, 0xbd6c8a99),
+     TOBN(0x1410a747, 0xc9f424f8), TOBN(0xfda0a304, 0xe9805723),
+     TOBN(0x1a438bd3, 0x0879bd30), TOBN(0xed09a9d3, 0x7f6903cb),
+     TOBN(0x920878f8, 0x57e53497), TOBN(0x87a12968, 0xa7fca0ed),
+     TOBN(0x7c8207cb, 0x38590ca1), TOBN(0x4cf52db1, 0xfae885c2),
+     TOBN(0x6cf384c4, 0xe8dc711f), TOBN(0x6fea20ff, 0x221dc698),
+     TOBN(0x6af56700, 0xa07bb79f), TOBN(0xc7da3b52, 0x33ca79c6),
+     TOBN(0x3a214691, 0xd05eb519), TOBN(0xea94c4f1, 0x93d4f089),
+     TOBN(0x734039d0, 0xba51f002), TOBN(0xc45e2042, 0xce206406),
+     TOBN(0xc0e75dbb, 0x4b3c3d53), TOBN(0x3a701272, 0x55b1b97c),
+     TOBN(0xec856e95, 0xd6addb6c), TOBN(0xb63fe8c6, 0xf283aae1),
+     TOBN(0x148fb239, 0x405788d1), TOBN(0x42e7148b, 0xe0181912),
+     TOBN(0x00bddcdd, 0x7de07978), TOBN(0xac79b657, 0x3c2e0a27),
+     TOBN(0x94024ba6, 0xdf1dd3dd), TOBN(0xcddeb357, 0x0bac41ad),
+     TOBN(0x51ec3dd7, 0x500c4f4b), TOBN(0xf00d594f, 0xd31c8fbe),
+     TOBN(0x6b8c6f43, 0x373a3e93), TOBN(0x891ba3a5, 0xfc2b6be9),
+     TOBN(0x3928225a, 0xddd72e36), TOBN(0x1e6a63bf, 0xcee362c1),
+     TOBN(0x317b78f4, 0xc5eb404c), TOBN(0xb882736b, 0x67c5e6b3),
+     TOBN(0xb1da56ce, 0x1f2f07aa), TOBN(0xab3c4fbe, 0xff83b633),
+     TOBN(0x9cc32f1c, 0x0ceeab99), TOBN(0xf1dead0d, 0x1062070e),
+     TOBN(0x49ea0d9b, 0x8a3e79c4), TOBN(0x4e7abe3f, 0xec9f16d1),
+     TOBN(0x19bda1c6, 0x5549ade0), TOBN(0xaae756a5, 0xe5885734),
+     TOBN(0xb3cff8ce, 0xcc2a1aaf), TOBN(0x812eebff, 0xf896ca47),
+     TOBN(0x0951b2bb, 0x9b2e1123), TOBN(0x7f245699, 0xdef6d6a9),
+     TOBN(0xa1331e95, 0x1be7ef41), TOBN(0xd1f0c3c3, 0x9fa1be62),
+     TOBN(0xb1d8295e, 0x4383e451), TOBN(0x658d8a84, 0x9f08bc14),
+     TOBN(0xb0587aef, 0x3ba4b85b), TOBN(0xb519c587, 0x481cbb27),
+     TOBN(0x2b975db6, 0x040d8f06), TOBN(0x399f6417, 0x1691d809),
+     TOBN(0x207a0e46, 0x7c6204fb), TOBN(0xe30f1420, 0x62c3e9d7),
+     TOBN(0x6127b782, 0x792f8208), TOBN(0x38f806ab, 0xb0d3fca9),
+     TOBN(0x38248542, 0x2ff46c64), TOBN(0xc18ffe85, 0x926ec166),
+     TOBN(0xfd268866, 0xc0c133fa), TOBN(0xb7f63f5a, 0xb93770e6),
+     TOBN(0xd8f1db26, 0xb13afb71), TOBN(0x5c5627eb, 0x32a790de),
+     TOBN(0x7f41bc1d, 0xdf50b6f8), TOBN(0x49d4ef17, 0x92d4c803),
+     TOBN(0x577f909f, 0xe8530065), TOBN(0x482cdede, 0xe630ff2d),
+     TOBN(0x682c8c6a, 0x14f54de8), TOBN(0xe6b5a504, 0xb4079343),
+     TOBN(0x00d927fc, 0xe58bde6b), TOBN(0x65d85f03, 0xf34841f4),
+     TOBN(0x265aec02, 0x2ac001d8), TOBN(0x1b785666, 0x2dfe518d),
+     TOBN(0x76142488, 0xc01e6e47), TOBN(0x8e8b2044, 0xdd5648dc),
+     TOBN(0x2c422006, 0xb3a340b3), TOBN(0xa5392113, 0x3dd67b22),
+     TOBN(0xbd08d05b, 0xa1567aaa), TOBN(0x84a55e43, 0x02acbec6),
+     TOBN(0x744ffd21, 0x5d898af0), TOBN(0x38067622, 0x6682e78a),
+     TOBN(0xf3696ff2, 0xffd08522), TOBN(0x49dd0060, 0x2bf02466),
+     TOBN(0xc9e0d1a5, 0x59c3e65d), TOBN(0x29537f56, 0x0a37fc25),
+     TOBN(0x6f6cb9eb, 0xa5f6b17a), TOBN(0xc1854320, 0x9c55857e),
+     TOBN(0x959585c6, 0x45dacc6e), TOBN(0xf4e97c94, 0xe5f4e930),
+     TOBN(0x966deb5f, 0x57d2a492), TOBN(0x98256831, 0x55d2df12),
+     TOBN(0xfdd65534, 0xaa457eca), TOBN(0x76dbb021, 0x03551474),
+     TOBN(0x0aeefee9, 0x09d9b4aa), TOBN(0x30fea11a, 0x784ca675),
+     TOBN(0x56b4b509, 0xff1d381a), TOBN(0xd1b26fea, 0x9fce40da),
+     TOBN(0x4835b911, 0x48d22911), TOBN(0x6aaac57a, 0x8bbe57e8),
+     TOBN(0xc8882792, 0x19d02037), TOBN(0x301e0aa6, 0x3ee49afa),
+     TOBN(0x1641ce6b, 0x00e6b020), TOBN(0x846b97de, 0xeac7cad8),
+     TOBN(0x9b74bfd8, 0x61aa6886), TOBN(0xdd95e765, 0xb0fa37ac),
+     TOBN(0xda0cde52, 0xf848a83b), TOBN(0xd2cc831d, 0x355b3528),
+     TOBN(0xc7fd2e03, 0x5e22238f), TOBN(0x6d5373fa, 0xab9a6c34),
+     TOBN(0x5dfc2874, 0xd8247f13), TOBN(0xc211a7a1, 0xe3c11f56),
+     TOBN(0x7512563f, 0xa2503b97), TOBN(0x124cd984, 0x5c007c82),
+     TOBN(0x4f6eb682, 0x491cd249), TOBN(0xaf4f70a3, 0xa683359d),
+     TOBN(0x2f1dfe71, 0xcc302b62), TOBN(0x83c474bb, 0xe57fbf56),
+    },
+    {
+     TOBN(0x43af7ab7, 0x916a8016), TOBN(0xf93d487f, 0x532bfb9c),
+     TOBN(0xa5f9af3c, 0xe2174971), TOBN(0xd1b9cf1f, 0x2d59b4d4),
+     TOBN(0x4a779418, 0x44f4eb91), TOBN(0x6a131fac, 0xc226edc5),
+     TOBN(0x472ab897, 0x80d4bb33), TOBN(0xb69687a5, 0x2f6ca1fe),
+     TOBN(0xffa73ca2, 0xfabd066a), TOBN(0x494e03a8, 0xf9c78bfd),
+     TOBN(0xe585a878, 0xff55cfef), TOBN(0x00770b1f, 0xd7053784),
+     TOBN(0xdec4da4a, 0x056fe70b), TOBN(0xe37395d8, 0x57bd444f),
+     TOBN(0x666250d4, 0x685df668), TOBN(0x0549569e, 0xbe6cc583),
+     TOBN(0x87629830, 0xab11639e), TOBN(0x869dd3ba, 0xa4488d53),
+     TOBN(0x10fe1c0b, 0xbaf06eb6), TOBN(0x99034839, 0x1687ac37),
+     TOBN(0x38418377, 0x7f1ffe7b), TOBN(0x3334a74c, 0x25bd7c57),
+     TOBN(0xc57cb7ed, 0x7008ba67), TOBN(0x384c12d0, 0xc1e4e12d),
+     TOBN(0xf48cdca6, 0xdb4bdb35), TOBN(0x6bc23aec, 0x74d913a7),
+     TOBN(0x8f0ccd9d, 0x12ed94d5), TOBN(0xe4aabd12, 0x86db09e7),
+     TOBN(0x0cbff31a, 0x1e948326), TOBN(0xcf68c47c, 0x17a479a2),
+     TOBN(0x3cced8e2, 0xca7686f1), TOBN(0x15ed1e99, 0x4eb62669),
+     TOBN(0xc373ab4b, 0xbdb0c561), TOBN(0x15082022, 0x6a9066a7),
+     TOBN(0x330a60c3, 0x62d31801), TOBN(0x53d97f09, 0xe35bea57),
+     TOBN(0xf204e006, 0x9c5dbb92), TOBN(0xfb9a8219, 0xf831262a),
+     TOBN(0x3468ae41, 0x42136174), TOBN(0x0f8fb5bc, 0x0e03218e),
+     TOBN(0x90337499, 0x4ad8bba6), TOBN(0xdb71e1fb, 0xe3ecb618),
+     TOBN(0x6955e874, 0x3cf2a8ad), TOBN(0x594501f5, 0xed691fee),
+     TOBN(0x7e2baef3, 0xd29bd364), TOBN(0x5cbd91ac, 0x6f766759),
+     TOBN(0xaba54aaa, 0xb2201a96), TOBN(0x2cfea457, 0xcfa392ab),
+     TOBN(0xa4da4162, 0x86f8f7da), TOBN(0x88d70b86, 0xcbc0b934),
+     TOBN(0x9df02795, 0xacff4f7b), TOBN(0x0fc80219, 0xc65ef81b),
+     TOBN(0x32d457de, 0xa299ca0f), TOBN(0x97081b35, 0x0896427b),
+     TOBN(0x92d6c309, 0x41bab6b4), TOBN(0x5d5e56f3, 0x73b8d601),
+     TOBN(0xfb3992a4, 0x202bde39), TOBN(0x2549f564, 0x3d6bab98),
+     TOBN(0x0b564642, 0x87712512), TOBN(0xd52442b4, 0x7fde7e50),
+     TOBN(0xa6cefd08, 0xa3d3e16e), TOBN(0x5b194f0a, 0xc83b29bd),
+     TOBN(0x6db0edd8, 0x906dec8c), TOBN(0x7a090959, 0x02570c1e),
+     TOBN(0xf6f74fcc, 0x4c41eb53), TOBN(0xd07678a9, 0x5b944a6c),
+     TOBN(0xf53bf11d, 0xb80677ea), TOBN(0x569a5761, 0xbc5900f8),
+     TOBN(0x34e5bba8, 0xd3d4de91), TOBN(0xc5774804, 0x8361f73e),
+     TOBN(0xd637d3dd, 0x59abdbd5), TOBN(0x64a81bf9, 0x8772b615),
+     TOBN(0x78bb12ea, 0x7f3d83ab), TOBN(0xca22c31c, 0x573f9b99),
+     TOBN(0x4283c173, 0x2aed4c39), TOBN(0xda054c1d, 0x39f32bdb),
+     TOBN(0x2ead717e, 0x1da2cbd7), TOBN(0x747d67cd, 0x62390b41),
+     TOBN(0x43739d9c, 0x6b9666a6), TOBN(0xb84e2f22, 0x8c827b12),
+     TOBN(0x0e4ac2b1, 0xc0312773), TOBN(0x571cfc75, 0xe53f068e),
+     TOBN(0x6c44df85, 0x42bfe41e), TOBN(0xe7d2edb9, 0x627e30bb),
+     TOBN(0x9c2e4fd6, 0x0dd5cedc), TOBN(0xe2d885ef, 0x0f7d22d7),
+     TOBN(0x44b0b5db, 0x1329bcfd), TOBN(0x006e872f, 0xba1c96f6),
+     TOBN(0xdbadab5d, 0x7e952317), TOBN(0xab849ed4, 0xc2a5bcaa),
+     TOBN(0xe3acbb74, 0x1e72dbb1), TOBN(0xbf42c3d3, 0x5d4b7cb7),
+     TOBN(0xebe967b5, 0x3d748639), TOBN(0x1fe93db5, 0xc03af7a1),
+     TOBN(0x2ab14596, 0xa944ea06), TOBN(0xfb05a759, 0x76655c09),
+     TOBN(0x5117890c, 0x6f8a532b), TOBN(0x2f57781f, 0x59430c5b),
+     TOBN(0xe70968b3, 0x79e07b84), TOBN(0x05df2305, 0xe86d7223),
+     TOBN(0x57af0dc5, 0x31e32933), TOBN(0x5473e34a, 0x84afc419),
+     TOBN(0xa7337a42, 0x03d5feb4), TOBN(0xd85c8602, 0x1b1c6bd8),
+     TOBN(0x25ca1891, 0x753008e6), TOBN(0x4338ec98, 0x5f0ff93a),
+     TOBN(0xd2ba8557, 0xddd30a7c), TOBN(0xb4b65361, 0x09c51794),
+     TOBN(0xfbb51399, 0xd1cbc66e), TOBN(0x28853781, 0xe53bca50),
+     TOBN(0x5b797232, 0xfd5a9aaa), TOBN(0x6249afd7, 0x5b88c4f3),
+     TOBN(0xcc5ab6cb, 0xba6918a0), TOBN(0x9f824ec1, 0x8fb65c7d),
+     TOBN(0x4796d80b, 0x56b18754), TOBN(0x4c83d371, 0x67721520),
+     TOBN(0xd77c373c, 0x63b03348), TOBN(0x91930e5e, 0x54f27457),
+     TOBN(0x83f97370, 0xaf40c03f), TOBN(0x65b55872, 0x34eea661),
+     TOBN(0x310695d0, 0xeb10175e), TOBN(0x79aaa6ea, 0xcd236aa1),
+     TOBN(0xf78539ff, 0x3edfff40), TOBN(0x2369c517, 0x02cd6063),
+     TOBN(0x81e43ae5, 0x5c8631ff), TOBN(0x065e8212, 0x216a60bd),
+     TOBN(0x225cb473, 0xe761a5f9), TOBN(0x695ef860, 0xab6de6fa),
+     TOBN(0x03536a46, 0x7d7d98d4), TOBN(0xa17d3a69, 0x18413673),
+     TOBN(0xa6ddcd46, 0x295ae303), TOBN(0x86de0bbd, 0x61beae2b),
+     TOBN(0xdd73dfcc, 0x7699458e), TOBN(0x827deba5, 0xb53f88dd),
+     TOBN(0x213c376b, 0x42a9a11c), TOBN(0xc854fd72, 0x12c73975),
+     TOBN(0x1fa96547, 0x15ac27ff), TOBN(0xcb0dc17b, 0xf49b6c9a),
+     TOBN(0xa3e44853, 0x709dd202), TOBN(0xd3905c5f, 0xcfe2bbea),
+     TOBN(0xb01e5799, 0x6c35ce9c), TOBN(0x0063e7ac, 0x900ef600),
+     TOBN(0x8c70b87e, 0xfffa5cc0), TOBN(0xebd76d34, 0x74230b0c),
+     TOBN(0x914eec9e, 0xed5f8529), TOBN(0x7a65ffd3, 0xe8edf477),
+     TOBN(0xf0cb206d, 0x70c74bee), TOBN(0x03445ff1, 0xd1b02e01),
+     TOBN(0x664ca356, 0xe5dbf791), TOBN(0xd678d4ae, 0x254e69c4),
+     TOBN(0x370c9f0f, 0x8617386b), TOBN(0x42af7a0c, 0xfdcd985d),
+     TOBN(0x8c4b5009, 0x83c3da54), TOBN(0x086a7ec5, 0x4c8a87c8),
+     TOBN(0x9ba0b368, 0xaa166c4c), TOBN(0xa279670f, 0xa658ac1c),
+     TOBN(0xc49f49bd, 0x5d0544da), TOBN(0x28c22323, 0x15cb0b41),
+     TOBN(0x86293dfa, 0xa4834d71), TOBN(0x283e191d, 0xd1e1d63b),
+     TOBN(0x0cad6519, 0xca188555), TOBN(0x323ce5da, 0x0cbd0c5c),
+     TOBN(0x6b7d2be1, 0x38560254), TOBN(0xb05ed385, 0x1696b9b9),
+     TOBN(0x8ce4b5a7, 0x9ae59f92), TOBN(0xabe5ff33, 0x4f7e61a3),
+     TOBN(0xae15a3cc, 0xdbfeb302), TOBN(0x691b1129, 0x837fde82),
+     TOBN(0xb60b31f3, 0x2e6d116b), TOBN(0xd49e9d11, 0xecab5aa9),
+     TOBN(0x3e95f844, 0x6787f23d), TOBN(0x2ab8834f, 0xa12f4846),
+     TOBN(0xe70e2ab1, 0x5b6359cc), TOBN(0x7a6349e9, 0x9322a047),
+     TOBN(0xc01e424c, 0x6c1e483a), TOBN(0x424b2027, 0x92bd5d1b),
+     TOBN(0x8a6e6766, 0x254e49a3), TOBN(0xb8d85d42, 0x97e70d58),
+     TOBN(0xa859082f, 0xb51b3abc), TOBN(0x2850573b, 0xe7bb828a),
+     TOBN(0x47cc95b2, 0x7bfe8021), TOBN(0x7c28fe9e, 0x5853f12c),
+     TOBN(0xe5fb0558, 0x10c0f389), TOBN(0xb99a639f, 0xdaf0a7e7),
+     TOBN(0xa6b9e6c9, 0xf60ee3e5), TOBN(0xb397af7f, 0xa236377f),
+     TOBN(0xb7a318ac, 0x7da4af71), TOBN(0xae64b613, 0x0a9d39fb),
+     TOBN(0x66ce6c74, 0x902b3411), TOBN(0xea256a70, 0x5a199e53),
+     TOBN(0x8dcddd89, 0x550fb76f), TOBN(0x9443b477, 0x03e70f9c),
+     TOBN(0x1787b8a5, 0x142113a6), TOBN(0xa58b6c61, 0x180aec95),
+     TOBN(0xcc381420, 0x947ff26d), TOBN(0x22520e8f, 0x3d8b8c36),
+     TOBN(0x192ee945, 0xef2cc6ef), TOBN(0xea52afee, 0xe9ca0c7a),
+     TOBN(0x5970d794, 0xe6e30d93), TOBN(0x0a325e42, 0x57c786ac),
+     TOBN(0x5e2dddf8, 0x33ca1226), TOBN(0x18e624b9, 0x588cb1e3),
+     TOBN(0xf3ba597a, 0x21809265), TOBN(0x90247702, 0x5d575728),
+     TOBN(0x48a5bf7b, 0xc1f918db), TOBN(0x17d1efaf, 0xd6840541),
+     TOBN(0x13dfe6fe, 0x3e2e754d), TOBN(0xc471e16a, 0x707a531f),
+     TOBN(0x79085bbd, 0x97d34b48), TOBN(0xfa5ba99d, 0xc2e9bea9),
+     TOBN(0x70b9c9fc, 0x6c5a6dc2), TOBN(0x4e042213, 0x4e94c5db),
+     TOBN(0x4a37b41f, 0x25ebb95f), TOBN(0x24691010, 0x055d79fb),
+     TOBN(0xdaff9352, 0x3f572a8f), TOBN(0xe63d55b0, 0xf327ec2a),
+     TOBN(0xc5a86d3c, 0xdebd5116), TOBN(0xd547fe08, 0xa2ddef2a),
+     TOBN(0xbabb617f, 0x6a149f12), TOBN(0x14f69a1b, 0x8a766128),
+     TOBN(0xb83a1477, 0x48236f77), TOBN(0xd0d81be1, 0x35711279),
+     TOBN(0x706f9067, 0x5eab1c3a), TOBN(0x8c4823f1, 0x16a1ffaf),
+     TOBN(0xd845c68b, 0xaff5ea89), TOBN(0xa276eaeb, 0x6b75eadb),
+     TOBN(0x2d0fc819, 0xcc230ec1), TOBN(0xdfad96e8, 0xedaaf1f2),
+     TOBN(0x0f25dcbf, 0x40868254), TOBN(0x53bbe31e, 0x5babd7f9),
+     TOBN(0x7f8afc48, 0xcf804a8d), TOBN(0x7f4922ef, 0x5f9b9a0d),
+     TOBN(0x703cbf6d, 0xd7422804), TOBN(0xe5df61f3, 0x83349bdd),
+     TOBN(0x0fa3d8cd, 0x77d285ad), TOBN(0xe990f9e5, 0x2e88e15e),
+     TOBN(0x40ec61f7, 0x8561d8a6), TOBN(0x7fc498a6, 0x16650305),
+     TOBN(0xa3bf5cb4, 0x8e5beabf), TOBN(0xfaa52008, 0x76ae0350),
+     TOBN(0x99e24318, 0xe4fc3e72), TOBN(0x9241c8ab, 0x2079c616),
+     TOBN(0xefa5bf38, 0x9584a258), TOBN(0xd7b770b5, 0x1eebb098),
+     TOBN(0x28b714a3, 0xe1fc18a7), TOBN(0xf0426bd2, 0x5b83dd9a),
+     TOBN(0x956d8972, 0x291b28ee), TOBN(0x8bb8cbde, 0x6eb553ff),
+     TOBN(0x396cfe2d, 0x95298003), TOBN(0xcaa66550, 0xad8412fc),
+     TOBN(0xf41d0215, 0x83997dd3), TOBN(0x7066e356, 0x45534587),
+     TOBN(0x0d5b5c3e, 0x5b6de0d7), TOBN(0x8ead45d0, 0xcecd5f26),
+     TOBN(0xe2f24e2c, 0xd252ae50), TOBN(0xf71e5d4f, 0x815150bf),
+     TOBN(0x3872685d, 0x54527ce5), TOBN(0x59b343ae, 0x91fd99ee),
+     TOBN(0xd621d027, 0x3462cc0c), TOBN(0xfa42481f, 0x8dbfbcf4),
+     TOBN(0xda481a9e, 0xaf7ae918), TOBN(0xfd5fd37c, 0x7c909a18),
+     TOBN(0xa5ebb7bf, 0x805fb7b7), TOBN(0xeac65687, 0x165200b1),
+     TOBN(0x56302866, 0x7cef9b97), TOBN(0x8f662dd6, 0xae3ddb64),
+     TOBN(0x90cb4e87, 0x60c1aa98), TOBN(0x33f9fc60, 0x986fb3bc),
+     TOBN(0x76f41ecc, 0x974593cd), TOBN(0xb19501f9, 0x6e0f01e8),
+     TOBN(0x587d9035, 0x25760dd5), TOBN(0xa31c971c, 0x9391032e),
+     TOBN(0x7650e3b1, 0x95c9a84f), TOBN(0xbb83ea93, 0x78c66087),
+     TOBN(0xda08a04c, 0xdfcf1365), TOBN(0xd23daeba, 0xca0b84a4),
+     TOBN(0xf89d395d, 0x2ca3bd2b), TOBN(0x779e2aaf, 0x6e53fc93),
+     TOBN(0xc0fc7dc8, 0x34216082), TOBN(0x6cd8bdf6, 0x42a66691),
+     TOBN(0x836a2cf3, 0x0fe014cf), TOBN(0xdde5fc22, 0x0c08373d),
+     TOBN(0xc4fa2387, 0xcb3b2b54), TOBN(0x96253732, 0xe2aa434a),
+     TOBN(0x4c4f5979, 0x1d502ce8), TOBN(0xf046f5a9, 0xb6df40c4),
+     TOBN(0xc7d05765, 0xac6b75b5), TOBN(0xae1cd887, 0xb69f3c34),
+     TOBN(0xafed4be6, 0x49b86680), TOBN(0x17b75fa5, 0x14f70463),
+     TOBN(0xb12e8051, 0x90b7c07f), TOBN(0xe2c8cf2b, 0x39a8e99d),
+     TOBN(0x984744c4, 0xd5fdb65b), TOBN(0xd8b1c012, 0xa28942e4),
+     TOBN(0x295d0199, 0x46693fb2), TOBN(0x5ab3a305, 0xa0a3b4fa),
+     TOBN(0x3c191023, 0x26755b3f), TOBN(0x75f35df1, 0xb6c084de),
+     TOBN(0x30640e66, 0x63e6286b), TOBN(0x3b3720ec, 0xd2c6c059),
+     TOBN(0x2c821a0f, 0x6ea837be), TOBN(0x238c4d93, 0x84f23bd0),
+     TOBN(0xbdc40703, 0x390ea4f5), TOBN(0xcb207d0a, 0xae68a2db),
+     TOBN(0x487d18bd, 0xe25f098f), TOBN(0x39018023, 0x9ab84e10),
+     TOBN(0xaa19aa62, 0x8b7ab4a2), TOBN(0xcb9cdebe, 0x89f08fbd),
+     TOBN(0x26a4c9eb, 0x2ca57917), TOBN(0xaadfd472, 0xda92ce1b),
+     TOBN(0x32b592d8, 0xdaa907db), TOBN(0x9bbebacc, 0x7d6f995e),
+     TOBN(0xa27a4735, 0xe1d88c25), TOBN(0x339905e1, 0x9bd66b67),
+     TOBN(0xa9bfa0ed, 0x62af9942), TOBN(0xd94dd9e0, 0x2e2cb83c),
+     TOBN(0x279d8fda, 0xab28e268), TOBN(0xf28ab69b, 0x51a97518),
+     TOBN(0xce9bd2ea, 0x9691f33e), TOBN(0xb9e8b2fe, 0x74be3d64),
+     TOBN(0x35072fab, 0xabefa07d), TOBN(0x1c2ba05c, 0x7b51ba8e),
+     TOBN(0x3bb1ec56, 0xd32d6bf5), TOBN(0x326bdfdc, 0x5d7bd7dc),
+     TOBN(0x33f4f4f6, 0xd95bdcb1), TOBN(0x781bfd34, 0x453ef338),
+     TOBN(0x4d210cad, 0x1ef61a66), TOBN(0x6ae7bb14, 0x2799bcc7),
+     TOBN(0xb105e5ec, 0x194f4d6a), TOBN(0x204a5480, 0x52b77f08),
+     TOBN(0x13f4e022, 0xa328ab98), TOBN(0xa56863c4, 0xb7f9a240),
+     TOBN(0x2780c9a7, 0xce4cf7bd), TOBN(0xf98e3f58, 0xc497fdf3),
+     TOBN(0x635b8bc6, 0xf52738fc), TOBN(0xc5fd89b8, 0x58937500),
+     TOBN(0x57070428, 0x75e98a64), TOBN(0x66aabaae, 0x946f094b),
+     TOBN(0x7d2376e8, 0x06d37944), TOBN(0x9b316827, 0x09319f13),
+     TOBN(0xbbde59a8, 0xa77eb79b), TOBN(0xb7f35bbb, 0xf451fde0),
+     TOBN(0xb2f7914e, 0x64aa79fd), TOBN(0x4d2944b3, 0x9f68a486),
+     TOBN(0xbd8a92de, 0xc1a7e537), TOBN(0x76695e9a, 0x4fc930a3),
+     TOBN(0x1b75f9eb, 0xbcb4153b), TOBN(0xf5a34d2d, 0xf6485404),
+     TOBN(0xe09ee965, 0x26853a8e), TOBN(0x63de8595, 0x9dbb319a),
+     TOBN(0xbbbc1b07, 0xda079d6d), TOBN(0x5956bb3d, 0xdfa71b9d),
+     TOBN(0x69709306, 0x209cbcc3), TOBN(0xbe2a08d0, 0xe3360429),
+     TOBN(0xd377a9fe, 0x92a58752), TOBN(0x37e175ea, 0x997bc322),
+     TOBN(0xfe355d4d, 0x042ff2e4), TOBN(0x4332ef31, 0x4c4babd3),
+     TOBN(0x634429c2, 0x2314b1af), TOBN(0xae6e8275, 0x91a7d5e5),
+     TOBN(0x134a39c7, 0x250a1476), TOBN(0xec9bb642, 0x08994f0c),
+     TOBN(0x2a9e0ac0, 0xd38704cd), TOBN(0x16490507, 0x536a4ad0),
+     TOBN(0xc7f747d2, 0x7c8dbfeb), TOBN(0x91e67dd2, 0xc0bb24ac),
+     TOBN(0x2dfc6c8a, 0x959eca45), TOBN(0x78bafaf0, 0xc54fefe8),
+     TOBN(0xf3eb2d1b, 0x5da056f3), TOBN(0xda14b631, 0x3b89c967),
+     TOBN(0x80923b1c, 0xcb51f621), TOBN(0xc3d5fd1f, 0x6609791c),
+     TOBN(0x68ad7bef, 0x817b1063), TOBN(0x3775b686, 0xa1f0b00c),
+     TOBN(0xb207c9a5, 0x6c7f0dc1), TOBN(0xb7c30a7d, 0xa9b91fff),
+     TOBN(0x9274c090, 0x8b9f8e8c), TOBN(0xa3757610, 0x24e94ce1),
+     TOBN(0x8f2b1f2c, 0x4f0f3ec1), TOBN(0x02e53bb2, 0x3938d26f),
+     TOBN(0x90a3da2c, 0x701e5ae8), TOBN(0x60c2eaca, 0xa271dcca),
+     TOBN(0xc9c08e39, 0x31fb2372), TOBN(0xcaa3245e, 0xb838908a),
+     TOBN(0x2e168b0b, 0xa6a6a275), TOBN(0x986a30a3, 0x0030ef6b),
+     TOBN(0x79f768f9, 0x170ab683), TOBN(0x7296fd6f, 0xff70981e),
+     TOBN(0xbab6fedf, 0x13a323cd), TOBN(0xa86ec0dd, 0x186e9813),
+     TOBN(0xd9f8db04, 0xcd56e7d5), TOBN(0x47b20676, 0xaa8b1c96),
+     TOBN(0xdff4574e, 0xf1fb3b03), TOBN(0x41a1f765, 0x1051f9fc),
+     TOBN(0x35779aee, 0x7f289a4e), TOBN(0x93bd54c9, 0x11c96180),
+     TOBN(0x1485074a, 0x37b95c67), TOBN(0x0b01af95, 0x0865b2f0),
+     TOBN(0x43033ffe, 0x90ce0b41), TOBN(0xffd6e34c, 0x71504d6f),
+     TOBN(0xb380cd60, 0x1aa946c8), TOBN(0x7f7cc73b, 0x806b2e19),
+     TOBN(0xc17df7d8, 0x2db88e6d), TOBN(0x7b767ca2, 0x072e5416),
+     TOBN(0xbb42d3ed, 0x0ad6134b), TOBN(0x5444339f, 0x640df8af),
+     TOBN(0x7e7c7e7b, 0x5bc73112), TOBN(0xe8f139b4, 0xf899dba4),
+     TOBN(0xd13b436d, 0x43a06bf3), TOBN(0xe43f8567, 0x773e4863),
+     TOBN(0x35555cd5, 0x56b814d7), TOBN(0x54af8e53, 0xd429ccc8),
+     TOBN(0xc346718f, 0x82ae0477), TOBN(0x301fb382, 0xbe02c7a8),
+     TOBN(0xcd65b3b2, 0xd2a70595), TOBN(0xcfcff499, 0x5aad01d6),
+     TOBN(0xd0fcc076, 0x589feca8), TOBN(0x7b2b93c7, 0x7c603ed8),
+     TOBN(0x2dda7a8c, 0x6ddfc3b8), TOBN(0x678d66e9, 0x74723d99),
+     TOBN(0x0f7e4215, 0x6db60b07), TOBN(0x40666848, 0xc0bfa2f9),
+     TOBN(0x70b46b5c, 0x8e116caf), TOBN(0xbd753511, 0xfba46d90),
+     TOBN(0xe48374cd, 0x019d8279), TOBN(0x7d40e6e2, 0x309b0fc6),
+     TOBN(0x226435ee, 0x9dec7a42), TOBN(0x818e79cb, 0x4243e7d0),
+     TOBN(0x3d7376d7, 0x54658634), TOBN(0xa40cafeb, 0x9f8727ac),
+     TOBN(0xdc1d09f0, 0x81f556bc), TOBN(0x32ca7367, 0x63223573),
+     TOBN(0x92e10f91, 0x5810a27d), TOBN(0x6fb34bad, 0x1fdf969f),
+     TOBN(0xe5c2b2ff, 0x657a067e), TOBN(0x173c0900, 0x382ba37a),
+     TOBN(0xdd5113c8, 0x86d87c1e), TOBN(0x56a2ca9d, 0xcaf77294),
+     TOBN(0x9f956881, 0x666a9821), TOBN(0xc4bcafc7, 0xa3b18c0f),
+     TOBN(0xb100f338, 0x2b02578b), TOBN(0x4716339e, 0x64b2c607),
+     TOBN(0x92c923ae, 0x5b161179), TOBN(0xada2e4da, 0x0df442a0),
+     TOBN(0x4d4b90c5, 0x47f85474), TOBN(0xa378bf79, 0x824e3195),
+     TOBN(0x4469339d, 0x2478a0d4), TOBN(0x0972e788, 0x0c1e69e2),
+     TOBN(0x1aedd761, 0x72edc384), TOBN(0xcabcd976, 0x9898d672),
+     TOBN(0xd370aa7a, 0xba814ca2), TOBN(0x20fa58db, 0xe88eca9c),
+     TOBN(0x1540ada9, 0x45a7ab8d), TOBN(0x8dcf9860, 0xbdca94fc),
+     TOBN(0xf0187e2c, 0xaa9df4f4), TOBN(0x9a197dc3, 0x54a49576),
+     TOBN(0xb54f5cb2, 0xb4a1f341), TOBN(0x1439caf0, 0xfe32827b),
+     TOBN(0x3c1a356d, 0xd36783f5), TOBN(0x284e2f15, 0xc56a6e47),
+     TOBN(0xc6abad59, 0x4dcfaddf), TOBN(0xe82993f7, 0x082bb2b4),
+     TOBN(0x3cb46972, 0x23655955), TOBN(0x8ab06385, 0x992551e3),
+     TOBN(0xcbd6cb99, 0xdaa13ab3), TOBN(0x01375bbd, 0x2dc1333d),
+     TOBN(0x638a7f20, 0x972c4440), TOBN(0x150665c6, 0x24dcb1cc),
+     TOBN(0x4044e12f, 0x1ea989c6), TOBN(0x204c4eba, 0x61012ea3),
+     TOBN(0x78b8edaa, 0xac2719c1), TOBN(0x6772643d, 0x2ab50d99),
+     TOBN(0x94604146, 0x606d63de), TOBN(0xa876d9b0, 0x693aadc8),
+     TOBN(0xf7401ffb, 0x667044ff), TOBN(0xab98d73e, 0xb599ecb4),
+     TOBN(0xe2b2048f, 0xda5cbee3), TOBN(0x526e3aa1, 0xa2b3da50),
+     TOBN(0x4d0885e3, 0xb4ad2073), TOBN(0x916ce3d2, 0x644a1a19),
+     TOBN(0x952b5747, 0x96930e8d), TOBN(0x2a489fd6, 0xb0cf7f5f),
+     TOBN(0xbff4b59b, 0xa8b3b538), TOBN(0xba0e03ff, 0x6aff1cbe),
+     TOBN(0xfa614adc, 0xd56b2285), TOBN(0x2305edd4, 0x50d58e62),
+     TOBN(0xb349fdce, 0xe36877e9), TOBN(0x5f808fc2, 0x43a6103b),
+     TOBN(0x66d8404b, 0x86493abe), TOBN(0x18c92d3d, 0x9b08ff7d),
+     TOBN(0x6a60ab6b, 0x89754762), TOBN(0xec69fd4c, 0x8233dee7),
+     TOBN(0x32449242, 0x06beadfa), TOBN(0x421caf1e, 0xe0df7084),
+     TOBN(0x6f89693b, 0xd7969339), TOBN(0xb9a53713, 0xfa30a7a9),
+     TOBN(0xf89d9bf5, 0x11556d9a), TOBN(0xe4e9c5f0, 0xee8cf993),
+     TOBN(0xe5b2a323, 0x17ed9a7e), TOBN(0xd4db3920, 0x93e80c9e),
+     TOBN(0xae857864, 0x1fda3726), TOBN(0xe5cb36a3, 0xa3e88485),
+     TOBN(0xa6b85205, 0xf495b9a8), TOBN(0xc1be0108, 0x38f3b180),
+     TOBN(0x79d0585b, 0x36a1f3a8), TOBN(0xa3d8f17f, 0x913ba5f2),
+     TOBN(0x1eaee5d6, 0x225acf11), TOBN(0xd4dfd0a2, 0x0d32de79),
+     TOBN(0x0cec324b, 0x6b3ceff3), TOBN(0x3acc6dec, 0xab447870),
+     TOBN(0xabbf7e6d, 0xb9c759ac), TOBN(0x0d5c1f47, 0xa5196938),
+    },
+    {
+     TOBN(0x781a2157, 0x45e7ea91), TOBN(0x4da3f86d, 0xacadfc40),
+     TOBN(0xc81d6c7d, 0x162cd393), TOBN(0x2c38a2a1, 0xad6e60fc),
+     TOBN(0x575b25d6, 0xf753479d), TOBN(0xc914e08e, 0xbdec6025),
+     TOBN(0xf81cea34, 0x492d5547), TOBN(0x6bbb8bb1, 0xfb1b6969),
+     TOBN(0x1ee8082c, 0x1279504c), TOBN(0xa466abb2, 0x2c92ffb7),
+     TOBN(0x4118b26a, 0x3e81c7e2), TOBN(0x1a76cc50, 0xfc60e33a),
+     TOBN(0x34998bc2, 0x5736d7ae), TOBN(0x20b39558, 0xbd1ef993),
+     TOBN(0xd669e2ae, 0x5fbf2525), TOBN(0xbf956ec6, 0x01cc7626),
+     TOBN(0xce817029, 0xb0ccbaa5), TOBN(0x57ef5bd2, 0x279b78a6),
+     TOBN(0xc9283747, 0x4df45d89), TOBN(0xe86b91a8, 0x2ec4bfd3),
+     TOBN(0xe5ab4c6d, 0xfe565915), TOBN(0xe6574716, 0x7c58a042),
+     TOBN(0xe141deda, 0x6301c4bc), TOBN(0x2f95d561, 0x8084513a),
+     TOBN(0xdc424508, 0xeecede3d), TOBN(0x11889b35, 0x386440d0),
+     TOBN(0x7b229f93, 0x98de0d77), TOBN(0x73fced8a, 0x300a7447),
+     TOBN(0xf75e1c79, 0xe31c8f88), TOBN(0x8db20bdd, 0xbb277e4f),
+     TOBN(0x8ded0a70, 0x2b87c02c), TOBN(0x166281b5, 0x4d164c1a),
+     TOBN(0x887356cf, 0xeedd8e0c), TOBN(0x8afab37f, 0xe44c012b),
+     TOBN(0x0795935f, 0xe4aa3eb6), TOBN(0x9b9efc0c, 0xda6dfa57),
+     TOBN(0x0ff0f8aa, 0xa8ab0840), TOBN(0x0f3a4b63, 0xc8561605),
+     TOBN(0x2ca911ef, 0xd5db9315), TOBN(0xef70e5ba, 0xc8ded9f8),
+     TOBN(0x443d9209, 0xa6aae58b), TOBN(0x3d0798e8, 0x274edda3),
+     TOBN(0x5c2d462c, 0xc2be3c9a), TOBN(0xb5488239, 0x439882dc),
+     TOBN(0x6391bb41, 0x977d4de4), TOBN(0x7fd91040, 0x1e8245c4),
+     TOBN(0x1a6d3c71, 0x3b093dc2), TOBN(0x423a4e3a, 0x7b22fe12),
+     TOBN(0xe3156f40, 0x3a9a04a3), TOBN(0x9b32c4e5, 0x297d9aff),
+     TOBN(0x7e0b401e, 0x62a89850), TOBN(0xffbf542e, 0xa84ef082),
+     TOBN(0x377cc0e0, 0xf990caf5), TOBN(0x02704343, 0xec88ea9b),
+     TOBN(0x846fd46c, 0x63f96a51), TOBN(0x37f5cebf, 0xe9855c47),
+     TOBN(0xbd140bd8, 0xe6ad29dc), TOBN(0x7dca4b10, 0x6a04da28),
+     TOBN(0xa84feafc, 0xade05b33), TOBN(0x44d031f8, 0x7630aacf),
+     TOBN(0x18af2fa6, 0xcdee269c), TOBN(0x1e40571b, 0x8697a40b),
+     TOBN(0xf71d44ad, 0xf0e5f826), TOBN(0x2a47ddf9, 0xa434cfe6),
+     TOBN(0x22b97078, 0xad17cc54), TOBN(0x223732dc, 0xf2c105b2),
+     TOBN(0x25168336, 0xe284fae8), TOBN(0x41b1bb94, 0xb356407b),
+     TOBN(0x299e7d7a, 0x89933a11), TOBN(0x19e13d3c, 0xff7dd9f6),
+     TOBN(0x9517bd16, 0xf23d7ca7), TOBN(0x9e5e9e34, 0x1eb978a4),
+     TOBN(0x4c222dae, 0x5fa3f92f), TOBN(0xd5e38e84, 0xed489ca7),
+     TOBN(0x3d81aca4, 0x70ea613d), TOBN(0xc7bed301, 0xbe4e88f6),
+     TOBN(0x6fd5a7bf, 0x0757d8db), TOBN(0x1472441d, 0x7a9181b0),
+     TOBN(0x78b78753, 0x5a90b66f), TOBN(0xe3fd5e91, 0xabdae620),
+     TOBN(0xea6a77d8, 0x84535653), TOBN(0x8d241deb, 0x81d7e667),
+     TOBN(0x1af73798, 0xfaf4ef1b), TOBN(0x5e1ae728, 0x3e0dae27),
+     TOBN(0x6a67088c, 0x2f7450b5), TOBN(0x7bccbe06, 0xda9cb3c6),
+     TOBN(0x520fabab, 0x5b808e05), TOBN(0x84222f68, 0x702b247b),
+     TOBN(0x2471546a, 0xe0bd7ef2), TOBN(0x27d310dc, 0x656a62a3),
+     TOBN(0xb8014eca, 0xad35da30), TOBN(0xbdfdcd82, 0x7f35cd7a),
+     TOBN(0xf1e4d51f, 0x040ae645), TOBN(0x672ffadf, 0xf42a4d9b),
+     TOBN(0x9d874370, 0x2d0be1c0), TOBN(0xcc3671c6, 0xc6e55471),
+     TOBN(0x39aa705c, 0xbb9c9667), TOBN(0x8c3e584a, 0xc51f661d),
+     TOBN(0xe5645b1d, 0xe570769c), TOBN(0x81923fda, 0xbc97abf4),
+     TOBN(0x51d64f64, 0x0caac97c), TOBN(0x45c17651, 0xff847f4a),
+     TOBN(0xc7a6eaf9, 0x8cbfa2c7), TOBN(0x6c2ab9f7, 0xba8ab893),
+     TOBN(0xbdaa2c7b, 0xf435624e), TOBN(0xc113e971, 0x1d961058),
+     TOBN(0xb230f1b0, 0xa2021a1c), TOBN(0x6b34e1ff, 0x521a4816),
+     TOBN(0x159dc24d, 0x9b874f4d), TOBN(0xeaa0f951, 0xbeaab169),
+     TOBN(0x4f38733f, 0xb56f4916), TOBN(0x4ee689db, 0xdc9d3ac7),
+     TOBN(0x720254bb, 0x7bf8d03b), TOBN(0x78b0e6d6, 0xd31d7679),
+     TOBN(0x848fb878, 0xf130d7b0), TOBN(0xe8e478ec, 0xd3ba625a),
+     TOBN(0xb0ce9168, 0x100dfefb), TOBN(0xfe1463ab, 0xe5098aa8),
+     TOBN(0xf780ac38, 0xa11ec558), TOBN(0x92f15c52, 0x8e474b9f),
+     TOBN(0x3b3892d3, 0x46410cb1), TOBN(0x72097f22, 0x03a5a136),
+     TOBN(0xdb3a1b80, 0x98de068d), TOBN(0xfb7438e4, 0x4b1a3890),
+     TOBN(0x8a10d5ea, 0x3839d3d9), TOBN(0xd9ad034d, 0xf4bd8126),
+     TOBN(0x07d108ef, 0xd4800261), TOBN(0x978d98ba, 0x9c5d6c52),
+     TOBN(0x63ae69e1, 0xecb9ce1c), TOBN(0x70d2b437, 0x51b28f39),
+     TOBN(0xc15696b6, 0x77f848a2), TOBN(0x6b6e60f4, 0xd8ab4d76),
+     TOBN(0x33a581a4, 0x030bf112), TOBN(0x9cdb1a6e, 0xc5e74a76),
+     TOBN(0x6c6f6ec4, 0x7a950053), TOBN(0xd47dc472, 0xb04ebcff),
+     TOBN(0xe85ca76a, 0x5e0970df), TOBN(0x74448d98, 0x78988af2),
+     TOBN(0x5227649b, 0x81620019), TOBN(0x47e2ac62, 0xaabc2027),
+     TOBN(0xfea15155, 0xfbffedf2), TOBN(0xa565c484, 0x3b4cb501),
+     TOBN(0x4c523be5, 0xd830cece), TOBN(0x2e2de6bc, 0xc321a440),
+     TOBN(0xa7d62771, 0x8d69887d), TOBN(0xf9d8ac67, 0x4e138de4),
+     TOBN(0xad3fbc08, 0x9fcb0a09), TOBN(0xcaabb0b0, 0xbfc3bc9a),
+     TOBN(0x84646bc3, 0xb1c20604), TOBN(0xf1059ac4, 0xd1574260),
+     TOBN(0x5c15c6a2, 0xeefff298), TOBN(0x7975ede6, 0xf3b0a393),
+     TOBN(0x0ea9d355, 0x27c2343a), TOBN(0xe21c75e4, 0x4b32e339),
+     TOBN(0x1438785e, 0xa7fc353a), TOBN(0x0b8d64ba, 0xe9a1dd56),
+     TOBN(0xcacf9b64, 0x75347c02), TOBN(0xf788c83e, 0xcaad57aa),
+     TOBN(0x90df1ab8, 0x36ecf2e0), TOBN(0x4db604a3, 0xf45070ac),
+     TOBN(0xbc76e168, 0xb4c4ed93), TOBN(0x07177b5e, 0x85b65a6c),
+     TOBN(0x41e3c275, 0x00d21309), TOBN(0xcc9678e4, 0x76a48f42),
+     TOBN(0x3a04d197, 0xb1c6256f), TOBN(0x940920a9, 0xb2cc7330),
+     TOBN(0x990e4da8, 0x2523d52f), TOBN(0x34709b24, 0x5a59d733),
+     TOBN(0x2f0da81c, 0x8e745028), TOBN(0x32b5f384, 0xcd5668ab),
+     TOBN(0x82744a5a, 0xee538e7e), TOBN(0x1b019bab, 0xf3eb2516),
+     TOBN(0xccbd28fb, 0xd79d735f), TOBN(0x0bb54a6e, 0x85f90aa2),
+     TOBN(0xacf5552f, 0x9a69ecaf), TOBN(0xbc51ee85, 0xd1f1e30b),
+     TOBN(0x12bf8b0b, 0xfa25193d), TOBN(0x3f0f51b0, 0x5ba4b3c8),
+     TOBN(0xc1b65deb, 0x66181f23), TOBN(0xfeb037f9, 0xc0156b03),
+     TOBN(0xdd7a0a8c, 0xa9dc59ed), TOBN(0x20b5c8ea, 0x7b57e018),
+     TOBN(0x0c3ebc94, 0xefaadad5), TOBN(0x146d05b6, 0x18758eba),
+     TOBN(0xcb952e41, 0x09c7b43c), TOBN(0x7f7a0ae3, 0x1c1b8fb6),
+     TOBN(0xbca8a9cf, 0x331dfb05), TOBN(0x4a1db4a1, 0xe0db9d7d),
+     TOBN(0x988d36a3, 0xe5b9c892), TOBN(0x64640e55, 0x010ad00e),
+     TOBN(0x4c33c7e8, 0xc011bffd), TOBN(0x5d7cf370, 0xa0ad217b),
+     TOBN(0xbaf8b74a, 0x71f3df52), TOBN(0x300963bc, 0xa5995b20),
+     TOBN(0x695cf7ee, 0xd6c27636), TOBN(0x74d4d3a1, 0x03ac244e),
+     TOBN(0xddba3bd6, 0xc898e5bb), TOBN(0x27573a89, 0xfe3499f7),
+     TOBN(0x666b4415, 0x4b0a6c98), TOBN(0xf4f3e8c5, 0xaa4ccfaa),
+     TOBN(0x5f136875, 0x1a5b919b), TOBN(0xed8eb5db, 0x670d4b04),
+     TOBN(0x4cd83d19, 0x0d0d73bf), TOBN(0xd2a5c62a, 0xbdf22579),
+     TOBN(0x8c41be16, 0xc2d04c2b), TOBN(0x5aa33bc4, 0xbf9ad465),
+     TOBN(0x36e20646, 0x5e00d922), TOBN(0x9df21e7c, 0x00b70e17),
+     TOBN(0x0601e630, 0xd440af4d), TOBN(0x4aab0d33, 0x963e87dc),
+     TOBN(0x2712abdb, 0x36d39000), TOBN(0x856d7e3b, 0xf9147e2a),
+     TOBN(0xadc4a96a, 0xc8e5d2f4), TOBN(0xac3e5336, 0x2e70c206),
+     TOBN(0x1ee7d838, 0x6f6f3d0e), TOBN(0x4674ef20, 0xead72426),
+     TOBN(0x3a804dd8, 0x6af5f580), TOBN(0x724a756b, 0xd74ea5ce),
+     TOBN(0x0c2968d0, 0x0432d854), TOBN(0xe3be03f3, 0xa4f262fe),
+     TOBN(0xe446692a, 0xc84c22bb), TOBN(0x156b3168, 0x9647650d),
+     TOBN(0x4e06bc39, 0xb5d3d62a), TOBN(0xf99d4fec, 0x80eea174),
+     TOBN(0x3a2b7ae8, 0xc08f1144), TOBN(0x35e65bf9, 0x12dae8d6),
+     TOBN(0xfa0af1cf, 0xae3892b5), TOBN(0xa57062dc, 0xac408112),
+     TOBN(0xef6a9ec3, 0x24bf1af9), TOBN(0xdda3b476, 0x5decd8bc),
+     TOBN(0x9314a36c, 0x7bed3775), TOBN(0x9e254b0e, 0x60aa296e),
+     TOBN(0x8be2de80, 0x65b9cf2c), TOBN(0x1b110df6, 0xcb3b96cf),
+     TOBN(0x0f647a12, 0x18957e21), TOBN(0xa1e11238, 0x4f907766),
+     TOBN(0x751a0d82, 0xc5528977), TOBN(0x958d8738, 0x9a4b1260),
+     TOBN(0x99087543, 0x773658ee), TOBN(0x18148bbe, 0xf19f74cf),
+     TOBN(0x5f50ef19, 0x0a19a374), TOBN(0xc5bc4160, 0x6bdd3392),
+     TOBN(0x1bdf5e4b, 0xb80ad74c), TOBN(0xc40ec2f7, 0xed7e68c8),
+     TOBN(0xedd7dd6a, 0xdecef5b8), TOBN(0x3d29a1cb, 0x896c95a3),
+     TOBN(0xfa84c325, 0x70ad41d4), TOBN(0x6a577072, 0xc398c177),
+     TOBN(0x4f942d01, 0x7375f2de), TOBN(0x968a7608, 0x8aa1523a),
+     TOBN(0x55dc7da6, 0x377e5c4c), TOBN(0xb75fff53, 0x282b540e),
+     TOBN(0xfee35c15, 0xfd4b6951), TOBN(0x6d1d64f6, 0xf04ddfae),
+     TOBN(0x320f1769, 0xaf7c8714), TOBN(0x2b5f86a4, 0x482ba6fd),
+     TOBN(0xcf691cb7, 0x1ab9986b), TOBN(0x42913d71, 0x7377ba6b),
+     TOBN(0x120b4601, 0x1e47bf08), TOBN(0xfb514e52, 0x764b2661),
+     TOBN(0x371c0155, 0xa140ae04), TOBN(0x94e65b70, 0x2e186763),
+     TOBN(0x5e440f7b, 0xd5764306), TOBN(0x3411dadf, 0x7b8a5eeb),
+     TOBN(0x6c25e519, 0xf0e3e158), TOBN(0x46ee66d6, 0xe8898c80),
+     TOBN(0xa0e9d4b1, 0xec4f9b03), TOBN(0xba48d97c, 0x126c1f31),
+     TOBN(0xb9f96818, 0xbdbf0928), TOBN(0x293ce87d, 0x7f51cb48),
+     TOBN(0x077a7420, 0x76f09d38), TOBN(0xc71cb875, 0xedea4c81),
+     TOBN(0xfeda750d, 0x9ddd1485), TOBN(0x987876dc, 0x51c10504),
+     TOBN(0x4d0253f8, 0x75ec7922), TOBN(0xbcc15e39, 0xc676b336),
+     TOBN(0x33d533d8, 0xb9370740), TOBN(0xc5c482db, 0xcb8c88ab),
+     TOBN(0x1ff3f223, 0xc7c08a75), TOBN(0xccfaf564, 0x401b00fd),
+     TOBN(0x6ba93d3a, 0x6ac9757c), TOBN(0xff09b546, 0xec2c92a0),
+     TOBN(0x95d3436c, 0xc5960be8), TOBN(0x90b7e8cb, 0x69029082),
+     TOBN(0xbdd1e2b9, 0xdb6b32e5), TOBN(0xf4d2e43b, 0xfd47ad85),
+     TOBN(0x8923251e, 0xcb005dbe), TOBN(0xc21368a0, 0x662912e7),
+     TOBN(0xc7ce2963, 0x062d205c), TOBN(0x1e8f812f, 0x9542b831),
+     TOBN(0x4f8a7915, 0x818c322d), TOBN(0x50073cba, 0xfb678809),
+     TOBN(0xed7b5237, 0x0cb91b3e), TOBN(0x22d1fa41, 0x60d3fe6b),
+     TOBN(0x3de39063, 0x2d690f75), TOBN(0x12b2e39e, 0xf164ec1f),
+     TOBN(0xa28a0d83, 0x332f408f), TOBN(0xe6d9406c, 0x6a054db1),
+     TOBN(0x67369b76, 0x5ddd64e0), TOBN(0x6d671707, 0x02b21c2d),
+     TOBN(0xb9ad3368, 0xac42170f), TOBN(0x5e8f5277, 0xe5802ffa),
+     TOBN(0x1b4468fb, 0xd9b4a0a9), TOBN(0x0daf8269, 0x96c24331),
+     TOBN(0x976c2f23, 0xc09ad26d), TOBN(0xd47afe88, 0x19c68d38),
+     TOBN(0x0e96c03b, 0xd3d8d84f), TOBN(0xe05b5fd8, 0x0932b2fe),
+     TOBN(0x13931043, 0x347fbbbd), TOBN(0xe0fa842f, 0xb0ccc752),
+     TOBN(0x7295ee0f, 0xc75bf745), TOBN(0xebaae0dc, 0xb0aa9d61),
+     TOBN(0xb392d49b, 0x6355564c), TOBN(0x57e2f166, 0x887c5a18),
+     TOBN(0x88b3a014, 0x230a278a), TOBN(0x088e4908, 0x4c194449),
+     TOBN(0xc6cd309f, 0x43d6af69), TOBN(0x394445e3, 0x589a7f7e),
+     TOBN(0x0610077a, 0x031e7c08), TOBN(0xd05547cc, 0xa3519f78),
+     TOBN(0x0123b543, 0x926e26ed), TOBN(0xcd430b80, 0x62d06da6),
+     TOBN(0xddb182d0, 0x0dcd6db5), TOBN(0x724c9bce, 0x8eb6e010),
+     TOBN(0x985a2f0f, 0x50a4a597), TOBN(0x35f2427f, 0x900f2a49),
+     TOBN(0xce6136fe, 0x13cbf187), TOBN(0xc893bdee, 0x1086c2aa),
+     TOBN(0xe2410ccb, 0x07eca624), TOBN(0xeda92913, 0xddf9afb0),
+     TOBN(0x8fc0cfd0, 0x5bb66033), TOBN(0x0ab7d29b, 0x0509ffc8),
+     TOBN(0xc063b004, 0xb3d4f10a), TOBN(0xed94a955, 0xeb8cf642),
+     TOBN(0xacfb2f14, 0xa272ac4d), TOBN(0x10f2c91a, 0xc4ebbf0b),
+     TOBN(0x73f6e02e, 0x06ea04eb), TOBN(0xb969e8f8, 0x8b97ea93),
+     TOBN(0xa9b27472, 0x0cd48657), TOBN(0xe1a874ec, 0x99264937),
+     TOBN(0x34e677a4, 0xf794332d), TOBN(0x5e67865e, 0x5ee2daea),
+     TOBN(0x3fe02b91, 0xe6886879), TOBN(0xe113432f, 0x0f9776ad),
+     TOBN(0x37567306, 0x6a2c47d1), TOBN(0xf66df9b8, 0x62feb54a),
+     TOBN(0xf734ee37, 0x3e07ce56), TOBN(0x50c4982d, 0x659809fd),
+     TOBN(0xe2fa768f, 0x9daf8faa), TOBN(0x66088ddc, 0x8b9fd7c3),
+     TOBN(0xb8265661, 0x333683c6), TOBN(0xe7dacf81, 0xdff2a0a7),
+     TOBN(0x1e99d6bd, 0x5e3da06c), TOBN(0xbd50e15c, 0xbae05a25),
+     TOBN(0x47a0d997, 0x802d7b40), TOBN(0x0a25b51b, 0x193ef621),
+     TOBN(0x24d3d4f4, 0x148ee5a3), TOBN(0x7012618f, 0x022a9df0),
+     TOBN(0xb3777339, 0xf68e648f), TOBN(0xcdfb557f, 0xd7544352),
+     TOBN(0x4b0b2d46, 0x1f912c5f), TOBN(0xddaf929a, 0x957515d9),
+     TOBN(0x29e4bf1f, 0x0ae46856), TOBN(0x158b4c85, 0x44e32ab0),
+     TOBN(0x17935398, 0x7c48d2d2), TOBN(0xe4ab6300, 0x6f2430bc),
+     TOBN(0x71dd7284, 0x0d8b24d4), TOBN(0xd9303af1, 0xfc21d7e4),
+     TOBN(0x816c616f, 0x450f7f6d), TOBN(0x17875d8e, 0x3306df19),
+     TOBN(0x7ce8d4a5, 0x087e86e0), TOBN(0xa53970ac, 0x36251f01),
+     TOBN(0x2037f12c, 0xfc98edaf), TOBN(0xc359a382, 0xabf72b6f),
+     TOBN(0x06acf1a6, 0x85130fa6), TOBN(0x08f45064, 0xadfe4344),
+     TOBN(0xc01e1f3b, 0xdd857b31), TOBN(0x92c2263a, 0xc9513734),
+     TOBN(0x562652d5, 0x589327b8), TOBN(0xa8edd065, 0x96a1c164),
+     TOBN(0x2cbf8f98, 0x79f8df8d), TOBN(0x3d5cf771, 0x40847dde),
+     TOBN(0x69b08ee4, 0x597c0622), TOBN(0xfff18c4d, 0x8a868f2a),
+     TOBN(0x28bca3d2, 0xf5ad0686), TOBN(0xf7992f89, 0x2d4eef7b),
+     TOBN(0xab10b9cc, 0x3977e15d), TOBN(0x47912ca1, 0x2db8ef03),
+     TOBN(0x1f3e70e6, 0xdf27884b), TOBN(0xdd9bb81e, 0x0e9d8efa),
+     TOBN(0x97a83b6f, 0x279e27f6), TOBN(0x47e259fb, 0x24daf922),
+     TOBN(0x49eb72bc, 0x124e71be), TOBN(0x01dba001, 0x3a6778ba),
+     TOBN(0x8d02baec, 0x3be03050), TOBN(0xe3571b3c, 0x6dd6c8ef),
+     TOBN(0x6e1ffbac, 0x2cc11ffe), TOBN(0x6d725c75, 0xf4e2e6f0),
+     TOBN(0x96c31b45, 0xf2b53a58), TOBN(0xa0e38dd7, 0x97f1634b),
+     TOBN(0xe8507959, 0x143ce001), TOBN(0xad9a9f52, 0x8b49cc63),
+     TOBN(0x950fd33d, 0x8438b0fa), TOBN(0x2b294c00, 0xbe0cbdf6),
+     TOBN(0xb2076b47, 0x937b00ce), TOBN(0x026153a1, 0x78041498),
+     TOBN(0xe958f12d, 0xe9e53d27), TOBN(0xf49e1124, 0xe8f8fad9),
+     TOBN(0xb78a5b74, 0x2bca0ae1), TOBN(0x35180dec, 0xcccdc3d3),
+     TOBN(0x15e4fba5, 0xa97e519f), TOBN(0xe49dac9d, 0xf5b8340e),
+     TOBN(0xdbd8ed3a, 0xe5978024), TOBN(0xd181f26a, 0xeb105505),
+     TOBN(0x38364818, 0x29f57098), TOBN(0xd674fe1f, 0x3900171a),
+     TOBN(0x5a2ff729, 0xf1bd5803), TOBN(0x53de7261, 0xeda23387),
+     TOBN(0x7f1d84c8, 0xf0dc417c), TOBN(0xa65694a7, 0x5360fa80),
+     TOBN(0x356e4518, 0x96ed36e6), TOBN(0x127a52de, 0x406bfd36),
+     TOBN(0xb575a98e, 0xde925d04), TOBN(0x35fb44be, 0xc0627c4f),
+     TOBN(0xc85f2c69, 0x471e745a), TOBN(0x1c01e1ea, 0x6213d79e),
+     TOBN(0x95ea99a1, 0x2f5081f0), TOBN(0xdb38bd3e, 0xc3367864),
+     TOBN(0x0e8cafec, 0xd8880436), TOBN(0x1d59fd74, 0xf9c63d09),
+     TOBN(0xe57b0b4f, 0x7f875dbb), TOBN(0xe266c939, 0x77084bd7),
+     TOBN(0x0e289c5a, 0x2fc1f909), TOBN(0xece9d225, 0x86c4fc40),
+     TOBN(0xe0a56fbe, 0x5d79b4b3), TOBN(0x2b96fae7, 0xd4489041),
+     TOBN(0x0f66316b, 0xe23c85e7), TOBN(0x2d1a3c78, 0xadfef0c2),
+     TOBN(0x1aece4ad, 0x9fbce9cd), TOBN(0xccd0f334, 0x6c32d32d),
+     TOBN(0x958d7a5c, 0xfb9ba6dd), TOBN(0xa0052032, 0xe673275d),
+     TOBN(0x514ffd9d, 0x7f978d07), TOBN(0x544bbce9, 0x450b76e1),
+     TOBN(0xeaa25d74, 0x6b5201b6), TOBN(0x7528a4ea, 0x74d082a5),
+     TOBN(0xa08c8d31, 0x66609e27), TOBN(0x5150d1be, 0xda7c6fd9),
+     TOBN(0x864f5b4c, 0x39930618), TOBN(0xe71e7f1a, 0xcebb516e),
+     TOBN(0xaeee7fa5, 0xebf1f8ac), TOBN(0x6efcad4a, 0x0ea827c6),
+     TOBN(0x6e0f4ecb, 0x74e21dd8), TOBN(0xc5311600, 0xf33a7939),
+     TOBN(0xdf62f3c3, 0xa4d93fc4), TOBN(0xd3b62727, 0x9a18476d),
+     TOBN(0x0b54f5e6, 0xc0e1256b), TOBN(0xe8987efb, 0x97ba9afa),
+     TOBN(0x4b6ea064, 0x41d11c15), TOBN(0xfed7017e, 0x79b79f0f),
+     TOBN(0x5a6bcf9e, 0x5bd04e40), TOBN(0xf3090153, 0x8fd3b4bd),
+     TOBN(0xa23b5acb, 0x82240648), TOBN(0x61d9a8b1, 0xb16cf033),
+     TOBN(0x2feb1706, 0xc9fbee1e), TOBN(0xfaa4cd69, 0xd7e07918),
+     TOBN(0x28562c58, 0x447cba7a), TOBN(0x727926c4, 0xa61a1064),
+     TOBN(0x1b32db7f, 0x97ac7eff), TOBN(0xfd968b22, 0x452253ce),
+     TOBN(0x69d1842f, 0x5cdd3217), TOBN(0xba7689da, 0x26f4007d),
+     TOBN(0x16445a64, 0x141c8b35), TOBN(0xc11c3101, 0x73c61779),
+     TOBN(0xa5aa0d18, 0x485300ee), TOBN(0x531b6de1, 0x1cc02bf0),
+     TOBN(0xf8b94155, 0xc4efeb2c), TOBN(0x83632663, 0xd015a9c8),
+     TOBN(0xc369b3ce, 0xcba18b7f), TOBN(0xe11b3ef6, 0xc29e0f9b),
+     TOBN(0x1a5e9bf2, 0x903ca95b), TOBN(0x2d9aefc6, 0xa50cb332),
+     TOBN(0xb5670264, 0xb29ce693), TOBN(0x806d08ac, 0xab1d7b7e),
+     TOBN(0xcbdfdf28, 0xc9107eac), TOBN(0xa8086243, 0x6cdf12ac),
+     TOBN(0xe7d9c315, 0x903d5999), TOBN(0x4379820b, 0xc079d951),
+     TOBN(0xe789ecad, 0xbf1edf4c), TOBN(0xec086811, 0x47bc7752),
+     TOBN(0xeea2eeb8, 0xc2fc8798), TOBN(0x763183e0, 0xe031a96b),
+     TOBN(0xc7daf0b2, 0xf9a6bfaf), TOBN(0x1a2a7ffb, 0x4b957cf7),
+     TOBN(0xa483c7c8, 0xbf2d2e7d), TOBN(0xf96921fc, 0x58ff7f9c),
+     TOBN(0x41386185, 0x574ee010), TOBN(0x62e6a1d8, 0x2780c649),
+     TOBN(0xdec553af, 0x60f2516e), TOBN(0x5b091537, 0x7a04eb11),
+     TOBN(0x1b53e9dd, 0x67eb90c5), TOBN(0xc390a23a, 0xddfda333),
+     TOBN(0xdd4e7c6d, 0x480568aa), TOBN(0xd6c1e8a8, 0x59ccbe61),
+     TOBN(0x1c6fd7a9, 0x9107901b), TOBN(0x5dc4a41e, 0xa211d116),
+     TOBN(0x597e94e7, 0xaf1b78a8), TOBN(0xe72da34d, 0x53afcb6a),
+     TOBN(0xbc364db7, 0x74512c24), TOBN(0xc26a8fb9, 0xb2811e91),
+     TOBN(0xfdd39d7f, 0x290469b1), TOBN(0x84515392, 0x20612535),
+    },
+    {
+     TOBN(0x7fe996a0, 0x1539cf31), TOBN(0x4a3f729a, 0x0ded7c6e),
+     TOBN(0x86f1f299, 0x3016f614), TOBN(0xc3d44e18, 0x86cb9163),
+     TOBN(0x96984531, 0x558fa36c), TOBN(0x58e8bf05, 0x369c89d6),
+     TOBN(0x287da114, 0xf9ee923f), TOBN(0x2032e984, 0xec271fbc),
+     TOBN(0x91b8579d, 0xd39207ad), TOBN(0x6f62c725, 0x0b1fe916),
+     TOBN(0x0f1599ac, 0xd89e01bf), TOBN(0x8d9bb86d, 0x4d1e5843),
+     TOBN(0x348b90d4, 0x726e38d1), TOBN(0xb824a1ca, 0x52a8c6b9),
+     TOBN(0x984d9309, 0xb1d2f6f4), TOBN(0xefa485b7, 0x431ec12e),
+     TOBN(0x24cafa66, 0x9d616a5c), TOBN(0xc1c7445f, 0x4c9d0ea8),
+     TOBN(0xf733e085, 0x90bee7b6), TOBN(0xa2f3ece3, 0xd251d2ba),
+     TOBN(0x6e422a45, 0x66aeba6c), TOBN(0x35e99b16, 0x37c1337f),
+     TOBN(0x52d0fdf7, 0x6d4f8d03), TOBN(0xa043420c, 0x79c92672),
+     TOBN(0x99725607, 0x76ac1925), TOBN(0x086449db, 0x3442fc58),
+     TOBN(0x8dbab920, 0x2e311e74), TOBN(0x29dee69b, 0x7ea25561),
+     TOBN(0x5a62b6ee, 0x19a7cd6c), TOBN(0xba38cc4c, 0x0d0dd5a0),
+     TOBN(0x779279e5, 0x166d0ff1), TOBN(0x0eef53cc, 0xf48b3dae),
+     TOBN(0x0463dcaa, 0x0f82c6a6), TOBN(0x75dfc96d, 0x11d7d6d8),
+     TOBN(0x61f05e7b, 0x6c100d92), TOBN(0xa118e548, 0xe13eabb4),
+     TOBN(0xcc77e3c8, 0xdcdf06b5), TOBN(0x902d37d6, 0x6ac25960),
+     TOBN(0x967d9993, 0x347d7116), TOBN(0xd2828650, 0x9ae33561),
+     TOBN(0x955b7840, 0x8c7c6d89), TOBN(0xbfa78bc8, 0x42c2555f),
+     TOBN(0x8c56ae3d, 0xa69c3165), TOBN(0x72b20e72, 0xe33bb1bc),
+     TOBN(0x686870b4, 0xd1aa6416), TOBN(0xf000040b, 0x7db03cdd),
+     TOBN(0xd25b16a9, 0xb0a0fd40), TOBN(0xeb89e932, 0x36815f1f),
+     TOBN(0xb3e5e912, 0x349b549c), TOBN(0x801f788e, 0x086c4e74),
+     TOBN(0xafb9ea4f, 0x0082ae92), TOBN(0x4e8f27a4, 0x0d740026),
+     TOBN(0xc4f8285a, 0x05f4a6ac), TOBN(0xefea5297, 0x0adcd58c),
+     TOBN(0x9d5b6ae5, 0xc52f8c21), TOBN(0x92622a7a, 0xe97af4dd),
+     TOBN(0x39b43935, 0x79f34a8a), TOBN(0x61acf55f, 0x30046435),
+     TOBN(0xf0a23fe6, 0x3f05fdb1), TOBN(0x7d6baee1, 0x0c4fa7ff),
+     TOBN(0x253f62b6, 0xe2daf735), TOBN(0xe370ead8, 0x2948637b),
+     TOBN(0xda57c16a, 0xd84e6206), TOBN(0xf19ffe09, 0x0dd22ad3),
+     TOBN(0x701acab2, 0x95bf2003), TOBN(0x50e4e10a, 0x9dff6efc),
+     TOBN(0xe637bcf0, 0x43b95430), TOBN(0xac45cd3e, 0x85050cbc),
+     TOBN(0xc2ebff64, 0x80639e4d), TOBN(0xe5af1fb5, 0x3056f603),
+     TOBN(0x302791d5, 0x08b17132), TOBN(0x87775ac4, 0xed517904),
+     TOBN(0xfe64ed1e, 0x4263a566), TOBN(0x735bbee4, 0x1d5e8f55),
+     TOBN(0x9ac61915, 0x8294342c), TOBN(0x0f522e5a, 0xd4904935),
+     TOBN(0x2ee883b5, 0x7c1e11f4), TOBN(0x0a2ce30f, 0xf0c237f4),
+     TOBN(0xf4a7157b, 0x8d955086), TOBN(0x7ec0462e, 0x022dc2db),
+     TOBN(0x562fb75b, 0xca391b0b), TOBN(0x13030aac, 0x3bb1efd6),
+     TOBN(0x305d9831, 0x347f44fe), TOBN(0x9f70c1ad, 0x94b2615b),
+     TOBN(0xaaf935f4, 0x4162ff22), TOBN(0x2b20f047, 0xa68d160e),
+     TOBN(0x30d52a97, 0x39222d1b), TOBN(0x051223b1, 0x6551642f),
+     TOBN(0xae65a5c7, 0x39620daa), TOBN(0x8ef6f93f, 0x6f7c078f),
+     TOBN(0xb06d52bc, 0xb0177db8), TOBN(0x915cdd08, 0x68fdf535),
+     TOBN(0x0070d150, 0xc5183222), TOBN(0x2b6495cd, 0x7817a2ae),
+     TOBN(0x3ce47614, 0x0b194f0b), TOBN(0x2eec6acf, 0x513bfdfb),
+     TOBN(0x725dbede, 0xf3dbd34d), TOBN(0x01c4412a, 0x3621fc75),
+     TOBN(0x17bd68de, 0x3c07f048), TOBN(0x117df57e, 0x62e735eb),
+     TOBN(0xb1596c6d, 0xb249c407), TOBN(0xa878f56a, 0xd46c55c4),
+     TOBN(0x33385670, 0xb8aa0cb4), TOBN(0xc7faa80e, 0x800ec887),
+     TOBN(0x2cd2814f, 0xd7daf836), TOBN(0x877b72b7, 0x0d616922),
+     TOBN(0xea73ca1b, 0xdb066012), TOBN(0xbe336c7b, 0xb0d4159d),
+     TOBN(0xb993b07f, 0x0f8fcd76), TOBN(0x5fdceaba, 0x8a593562),
+     TOBN(0x716595fb, 0xf691ec19), TOBN(0x51a77f61, 0x8e68e3c0),
+     TOBN(0xe9e4cdfe, 0x7a7c18db), TOBN(0x967d3575, 0x7b4f69b7),
+     TOBN(0x6dd350a1, 0xa9a434c1), TOBN(0xb92cdef9, 0x00c79ba7),
+     TOBN(0x7a762493, 0xa6bb0f93), TOBN(0x6c18cdc2, 0x8158ad36),
+     TOBN(0xa0bd83e3, 0xc529ecfd), TOBN(0x98363c59, 0x3962f96d),
+     TOBN(0xd80f45a5, 0x1d63aa7f), TOBN(0x8d5eba75, 0xb3b32da2),
+     TOBN(0x0ef233df, 0xa4708858), TOBN(0x74c3f4f7, 0x52161c61),
+     TOBN(0xfa9bfe6b, 0xe6420de4), TOBN(0x96c0c501, 0x97dd86d5),
+     TOBN(0x28e6827b, 0xcfce233b), TOBN(0x035cc9a9, 0x58e74d63),
+     TOBN(0x9ba64bf4, 0x7948782d), TOBN(0x5e5b7c72, 0x80d9ce1a),
+     TOBN(0x7b9435db, 0xf51df862), TOBN(0xe74ab6e8, 0xb4dd2421),
+     TOBN(0xb0d704db, 0x60954c75), TOBN(0xd31c5145, 0x0b59ae5b),
+     TOBN(0xe0ff4660, 0xd99ba307), TOBN(0x1a3800fd, 0x986bd82b),
+     TOBN(0xe7e06ab7, 0x509a0a50), TOBN(0xbdf63778, 0xe4539885),
+     TOBN(0xf888320f, 0x16ddb433), TOBN(0x0f108304, 0x18e18998),
+     TOBN(0x27e7ffd6, 0xfa834b14), TOBN(0x16de9a71, 0xc68b9434),
+     TOBN(0x53a12e2c, 0x4d360436), TOBN(0x5ad2c986, 0x5e110b02),
+     TOBN(0x3439443c, 0x3cf53e0c), TOBN(0xfeae29b0, 0x1d65a7a3),
+     TOBN(0x1e7774f6, 0x78ad7d78), TOBN(0x0c79fb01, 0x6fee368c),
+     TOBN(0xbec71de1, 0xe4faf3ee), TOBN(0x1a88f3e5, 0x263868e8),
+     TOBN(0x975d8381, 0x90e91a8c), TOBN(0x69c5a65d, 0x0f999c60),
+     TOBN(0xbd3360d8, 0x8b884857), TOBN(0x04190413, 0x31b7c579),
+     TOBN(0x40dd9229, 0x142cc145), TOBN(0xb5faab94, 0xdad0b2df),
+     TOBN(0x3e7d7921, 0x52df4831), TOBN(0xcf5bd1ed, 0x228bf67d),
+     TOBN(0xd8669635, 0xc4980085), TOBN(0x094b8973, 0x1c71903d),
+     TOBN(0x493a7a3d, 0xc4abb028), TOBN(0x0e1a8fac, 0xb4ab8e35),
+     TOBN(0x26094ca2, 0x017aa5f2), TOBN(0x94fcb8b1, 0x021476cb),
+     TOBN(0x57f101f9, 0x4abf3bcd), TOBN(0x1ac2c125, 0x2d7f12a8),
+     TOBN(0x575259d9, 0x2e42789c), TOBN(0xa64a4a4b, 0x22471eb3),
+     TOBN(0xcc02304d, 0xe1c00144), TOBN(0x6269dfb9, 0x754734b2),
+     TOBN(0x72e7a183, 0xf14fbc81), TOBN(0xd92a5b1c, 0x2a05caa2),
+     TOBN(0xd593492e, 0x15efc2fb), TOBN(0x1ace7dca, 0xd8dd458b),
+     TOBN(0x576b4bc8, 0xaef2ae81), TOBN(0x6de6a2db, 0x351b397e),
+     TOBN(0x73f13b48, 0x656cf9ab), TOBN(0xc18df1c9, 0xaee7e01d),
+     TOBN(0x30fb5155, 0x560355e7), TOBN(0xd2c9a0ee, 0x9ad059d5),
+     TOBN(0xd9f89936, 0x5e5e0c7c), TOBN(0x5d0a2cbd, 0xf0a6c9d7),
+     TOBN(0x3c2c497d, 0x58fa9be9), TOBN(0xe6c6fcf2, 0x6ac61a2f),
+     TOBN(0xf7ec89e3, 0x35607bc4), TOBN(0x17ca00ca, 0x9946bf52),
+     TOBN(0xee46be5b, 0x180c8bd8), TOBN(0xd29d5eb0, 0xb2873893),
+     TOBN(0x348ac939, 0x97487b3c), TOBN(0xc18f0ceb, 0xfeef78ce),
+     TOBN(0xfc648dca, 0xf797cce5), TOBN(0xe2467e0c, 0x442148d7),
+     TOBN(0x8e201ee7, 0xda6dbaf6), TOBN(0xc1a93ee4, 0xabd0490c),
+     TOBN(0xfd0007bf, 0x4de7c210), TOBN(0x02eccb8b, 0x083ffce0),
+     TOBN(0xbba82bbb, 0x97844c8d), TOBN(0xb7ff954c, 0x2747a430),
+     TOBN(0xb05f0584, 0x18fb5f29), TOBN(0x35a29cf5, 0xee7c263f),
+     TOBN(0x5fd84efd, 0x25282da8), TOBN(0xc1fc84c1, 0x7682db7c),
+     TOBN(0x8422b56a, 0x4bb291e6), TOBN(0xce379feb, 0x66a56078),
+     TOBN(0x1c3c581e, 0xe7f045a0), TOBN(0x8f42985d, 0x6b3f89ac),
+     TOBN(0x112839e9, 0xa6b2ba59), TOBN(0x3f0c7269, 0xc2a7b29a),
+     TOBN(0xecdadb6f, 0x79bd3046), TOBN(0x669f559b, 0x9617ff6e),
+     TOBN(0x242bb14d, 0x68928ca9), TOBN(0x28e2b0cb, 0xc19cafcc),
+     TOBN(0xfb7d8954, 0x81330593), TOBN(0x5c3741fd, 0x9fbf8665),
+     TOBN(0xaf49e83a, 0xc3b93e86), TOBN(0xd60ecf7d, 0x32822548),
+     TOBN(0x4bf36759, 0x7cfb37cd), TOBN(0x94b0c7f0, 0xdb7af2ed),
+     TOBN(0x2f1bebf6, 0xebf8b461), TOBN(0x90ebc9c7, 0x073e5c18),
+     TOBN(0xe431c793, 0x3774e905), TOBN(0xb8a4bc2f, 0xe03265bb),
+     TOBN(0x0bee0825, 0xfabb629e), TOBN(0xbd1481dc, 0x84557170),
+     TOBN(0xa2257b58, 0xe1a010a0), TOBN(0x43f5451c, 0x4688bb89),
+     TOBN(0xb87a5ff0, 0x91b96371), TOBN(0x445dd02f, 0xde3c7b25),
+     TOBN(0x44c0c080, 0x51a0964c), TOBN(0xb0c3190e, 0x9afdcefa),
+     TOBN(0x14cc65ad, 0x0044d258), TOBN(0x8c500b3e, 0x374fdd44),
+     TOBN(0xed8d840f, 0x51b07812), TOBN(0xd414a4a2, 0x76530691),
+     TOBN(0x9db9d138, 0x1468ef8d), TOBN(0xfc6b7434, 0x292b3870),
+     TOBN(0x80b66797, 0xc9d7ad96), TOBN(0x81e74eb6, 0x2a9c1e99),
+     TOBN(0x48657d9a, 0x9e92f64b), TOBN(0xf5c60075, 0x4c851ddd),
+     TOBN(0x08fa89be, 0xd99d5cfe), TOBN(0x78b1f26e, 0x4db4addf),
+     TOBN(0x03237177, 0x3523ead9), TOBN(0x0147af5c, 0x6a281494),
+     TOBN(0x8db3952a, 0x916836b0), TOBN(0x0632b102, 0xfd365650),
+     TOBN(0x3854a8e9, 0xccb3f2f1), TOBN(0x5048486c, 0x586ad427),
+     TOBN(0x22de9979, 0x17a86e18), TOBN(0xe2ac2321, 0xbe029111),
+     TOBN(0xbfd34397, 0x35cc5a17), TOBN(0x7a93461f, 0x525e13cf),
+     TOBN(0xd433542c, 0x5122d6f1), TOBN(0x41d2d9de, 0x833982c7),
+     TOBN(0xe9f1f29a, 0x8ec24d27), TOBN(0x4ae251f3, 0xf3b99d58),
+     TOBN(0x7234dd24, 0x10adb458), TOBN(0x0e4b6567, 0x88379ef5),
+     TOBN(0x3007df15, 0x748dba5d), TOBN(0x1485ef01, 0x35103772),
+     TOBN(0xe21a9dc9, 0x29c2382f), TOBN(0xcf7e0c24, 0x6b6c1c8d),
+     TOBN(0xf8a71820, 0x30550c0a), TOBN(0xb30e5c0f, 0xb797de2e),
+     TOBN(0xbe136119, 0x03705145), TOBN(0xe6d1f720, 0xf94aadc7),
+     TOBN(0x38ce1872, 0x255f5297), TOBN(0xbbba4793, 0xc3143f58),
+     TOBN(0xda5345fe, 0x0984e265), TOBN(0xe93989d6, 0xd895e0d7),
+     TOBN(0xb7392b18, 0xcaab40a3), TOBN(0x4a586963, 0x65e754fc),
+     TOBN(0xb3e88445, 0xa3afd381), TOBN(0xa6cbab0a, 0x693ad961),
+     TOBN(0x64d51359, 0x257d56dc), TOBN(0xf9e70fcc, 0xbbde137a),
+     TOBN(0xa33872fa, 0xadd016b1), TOBN(0xd1d263d2, 0x7344f234),
+     TOBN(0xc2d51210, 0x24ba41b2), TOBN(0x8c9c830c, 0xe4ab65cb),
+     TOBN(0x3b47563c, 0x175b4039), TOBN(0x53521dfd, 0x116b2025),
+     TOBN(0xe4f3aa89, 0x1a9f1cca), TOBN(0xcc73485c, 0xe7cb1d2b),
+     TOBN(0xa6ca61ef, 0xbf58fe30), TOBN(0x5d50e15d, 0x531a2b6a),
+     TOBN(0x71cfdb41, 0x22611c31), TOBN(0x0dc15533, 0x61e3d46a),
+     TOBN(0xb363c60b, 0x479074ba), TOBN(0x2a4d8f4c, 0x24cb405d),
+     TOBN(0x3d3bee13, 0x646b7f0a), TOBN(0xdfa9194c, 0x5571af63),
+     TOBN(0x951a61a7, 0xee76521c), TOBN(0x67466ba5, 0x65eda1f1),
+     TOBN(0xe41d33b8, 0x69ebc7ea), TOBN(0x8b6c992d, 0xd4f4848f),
+     TOBN(0x3572faac, 0xa5003eaa), TOBN(0x01e36500, 0xabf54df1),
+     TOBN(0x6622f12f, 0xac6f3af7), TOBN(0xb5d7c17f, 0x0a8bb122),
+     TOBN(0xd1fc1b99, 0x240286b1), TOBN(0x519d52ce, 0xad968ede),
+     TOBN(0xcd1e7d0c, 0xece28bb4), TOBN(0x64ffc699, 0x93f0b097),
+     TOBN(0xb18d51f8, 0x25af7d81), TOBN(0x8d0bb08f, 0x19820fb2),
+     TOBN(0xe9f45919, 0xaa344723), TOBN(0x558f18ea, 0x5f9e0501),
+     TOBN(0x56eff076, 0x07cc79dc), TOBN(0xf171e880, 0xd5fa9783),
+     TOBN(0xd5fb41f3, 0x8be7f1fe), TOBN(0x19a34620, 0xd6fe9afc),
+     TOBN(0x74c2696b, 0x7d8a042a), TOBN(0xcf4908c3, 0x54230ded),
+     TOBN(0x98a870d8, 0xdb32943b), TOBN(0x181cbe5c, 0x52f33e07),
+     TOBN(0x4d9d1172, 0x93709d8b), TOBN(0xb800c292, 0x2b2b7939),
+     TOBN(0xd5a1fb7a, 0xa8920f60), TOBN(0x8d0a7725, 0xbf7df622),
+     TOBN(0x83a370cf, 0xe9415cf4), TOBN(0x9f24d1e1, 0x62a4ff4d),
+     TOBN(0xca33914b, 0x0a6ff7cb), TOBN(0x2576f2d8, 0xda1d1aaf),
+     TOBN(0xbb2668bd, 0xb4793101), TOBN(0xb576672c, 0xca990b4f),
+     TOBN(0xfa165b5f, 0xff1d3988), TOBN(0x273d5b18, 0xba8c9e2c),
+     TOBN(0x13594ae5, 0x720a5b3f), TOBN(0x38c4e04a, 0x2e65454c),
+     TOBN(0xc5b55d98, 0x55d0d903), TOBN(0xfabeb890, 0xf72db301),
+     TOBN(0xe471f1d5, 0x2f3deaa2), TOBN(0x3f8d39f5, 0xc5ade08d),
+     TOBN(0xe08486a4, 0x6baf9d1a), TOBN(0x77c6d30e, 0xe5140d3b),
+     TOBN(0x828db96a, 0xd23d4e09), TOBN(0x2de4856d, 0x84dcac15),
+     TOBN(0x313c7f8b, 0xa6ac856d), TOBN(0x7c9f671c, 0xfe7dea98),
+     TOBN(0x4d6c14a5, 0xfebe9e72), TOBN(0x185ac4e6, 0x6014be55),
+     TOBN(0x42809988, 0x5ed64e23), TOBN(0xdc9395a1, 0xbd6440fe),
+     TOBN(0x10169080, 0x24108055), TOBN(0xfe0d9cb0, 0x38961403),
+     TOBN(0xf88e6a48, 0xceb87d29), TOBN(0x0365ca2f, 0x69618944),
+     TOBN(0x1504647e, 0x9fb59ec9), TOBN(0xb6486b3b, 0xa4aadbb7),
+     TOBN(0xfe8701af, 0x22ef3214), TOBN(0x4c895bc1, 0x5f56d985),
+     TOBN(0x6fdc6cb6, 0x2e1e68d2), TOBN(0x0689b22b, 0x13f07350),
+     TOBN(0xba94416b, 0x6d62eb1f), TOBN(0x5a2fcbba, 0x98996d72),
+     TOBN(0x2ca2a2e9, 0x04b2afed), TOBN(0x5b62c764, 0x0bf61008),
+     TOBN(0x30074e57, 0x37f4d486), TOBN(0x4e02be2a, 0x31865287),
+     TOBN(0x401cfb89, 0x6842ab16), TOBN(0x440fb52d, 0x5b2eb304),
+     TOBN(0x3245fd38, 0xd22eaa61), TOBN(0x252120e8, 0x373f201e),
+     TOBN(0x4d253f5c, 0xb2e724c9), TOBN(0x9428d6be, 0x27e5b5e4),
+     TOBN(0x00d4c598, 0x6785ee9c), TOBN(0x0b7fc5f9, 0x56208d4b),
+     TOBN(0x4426665d, 0x92310137), TOBN(0x75b96cd3, 0xfee8da95),
+     TOBN(0xaaaac6c3, 0xb561c6d8), TOBN(0x0784a3c5, 0x3f09e1d9),
+     TOBN(0xac78c064, 0xdcac620b), TOBN(0x49dd5f02, 0x119b8d90),
+     TOBN(0xf1f5ebf2, 0x57e5caf4), TOBN(0xd8a9fa2d, 0x0eb825e2),
+     TOBN(0x3553633a, 0xbfb6a2fd), TOBN(0x06533515, 0xa0c9ce9a),
+     TOBN(0x6decd9e8, 0x04c71728), TOBN(0xcbc0df55, 0x980b29bd),
+     TOBN(0x17b1b559, 0x31826d15), TOBN(0xc96ed7d7, 0x1c5cae17),
+     TOBN(0x24f58740, 0x88cda83e), TOBN(0x9e2ee1bc, 0x0c69f40f),
+     TOBN(0x138ebf0f, 0x9e5604ed), TOBN(0x0577f4c2, 0xf229f097),
+     TOBN(0x0a44f975, 0x9762825f), TOBN(0x113b8639, 0xdd085e55),
+     TOBN(0x4be02fee, 0x73acc59e), TOBN(0x7829f288, 0xada7a17d),
+     TOBN(0x086bd736, 0x84fb30d4), TOBN(0xb2f120ee, 0xe5338eca),
+     TOBN(0x21701393, 0xfb778d2f), TOBN(0xd46bc61e, 0x6441fd75),
+     TOBN(0x466671de, 0x135b55bc), TOBN(0xee1d9cbb, 0x51c0f145),
+     TOBN(0x7a7bce67, 0x6d9ce27c), TOBN(0xa8c9b1e0, 0x26d82b1d),
+     TOBN(0x250bee03, 0x4c87bd6e), TOBN(0xd6b02f71, 0xd3829702),
+     TOBN(0xf14b3748, 0x0e555d98), TOBN(0xf795e62a, 0x6f775e78),
+     TOBN(0xe9a4e4ac, 0x8f46de18), TOBN(0x773bd32a, 0xc5ab76ef),
+     TOBN(0x4f2dcc68, 0x5264cae9), TOBN(0x453b627e, 0xc63a6419),
+     TOBN(0xc3aeddd1, 0xe441c6d9), TOBN(0x669500b7, 0x3027b8f3),
+     TOBN(0x06b93413, 0x88d601e5), TOBN(0x0e996e87, 0x76c4483c),
+     TOBN(0xe2ff3a6d, 0x00eb0c21), TOBN(0x86ec3a73, 0xf4690674),
+     TOBN(0x673d123e, 0xe9f82ca7), TOBN(0x952c2765, 0xbf611d0c),
+     TOBN(0x26ed9a68, 0x3676497d), TOBN(0x2c9c0049, 0x9d29cefc),
+     TOBN(0x0b8c12fe, 0x2ae73af6), TOBN(0x555b0ab3, 0x6455c8e1),
+     TOBN(0xd2f49f03, 0x4804b006), TOBN(0x408a22bc, 0x02e829a0),
+     TOBN(0xde2a59cf, 0xf2832c53), TOBN(0x01332439, 0x96a54082),
+     TOBN(0x11dc4ab6, 0xbbd38f9f), TOBN(0x0248fd93, 0xa24764b5),
+     TOBN(0xc7774c12, 0x1374972a), TOBN(0xc7355966, 0x92764b41),
+     TOBN(0x31c10ea5, 0x23143092), TOBN(0xe89d9f88, 0x9070137f),
+     TOBN(0x7d074406, 0xa504d91e), TOBN(0xc5210379, 0x70b7aa8c),
+     TOBN(0xa67904f8, 0xea799dd8), TOBN(0x81e6516b, 0xe7b02c04),
+     TOBN(0x18daf05f, 0xb08cc6fe), TOBN(0xfbbd3061, 0xf59d49f6),
+     TOBN(0x5429b117, 0x93a78581), TOBN(0x795a4465, 0x1906df65),
+     TOBN(0x643c37e3, 0xd51a7866), TOBN(0x69b8118d, 0x663a17b1),
+     TOBN(0x5e385989, 0x3e8a2c53), TOBN(0xbc18c2ea, 0x50f007d2),
+     TOBN(0x4adec20a, 0xb616aa15), TOBN(0x99f77e49, 0xea085548),
+     TOBN(0x9108c205, 0xc01b9a33), TOBN(0x298fbeb1, 0x6ef3bcef),
+     TOBN(0xdf1a8d2e, 0xefd8ba0e), TOBN(0xf0ec9492, 0xe9756e7b),
+     TOBN(0x4fd33389, 0x7ff5fbc3), TOBN(0x122a6bfb, 0x03ac8371),
+     TOBN(0x7d053c8c, 0x90d66732), TOBN(0x83f26571, 0xf9b2909f),
+     TOBN(0x350dd6d0, 0x66cba4b6), TOBN(0x8c71c17a, 0x40d0d47d),
+     TOBN(0x3bf85053, 0x4d0be34a), TOBN(0x91ae4f59, 0xe11bd49f),
+     TOBN(0xf8a38b41, 0xa22c648f), TOBN(0xcb96620e, 0x58abaaea),
+     TOBN(0xa55cee46, 0xa7fabcf5), TOBN(0xd16a8b92, 0x79c8fbce),
+     TOBN(0x26ad700b, 0xcbf048bf), TOBN(0x83b3ce11, 0x47bb5f1d),
+     TOBN(0x31a48f46, 0x6b310318), TOBN(0x13a7f781, 0x00612ef3),
+     TOBN(0xcd840f2a, 0xa18db234), TOBN(0x3be2a7a8, 0x30611c74),
+     TOBN(0xbdf37cb2, 0x2b1c0447), TOBN(0x7f572382, 0xfe71722d),
+     TOBN(0x085b3566, 0x25535e86), TOBN(0xb5b43063, 0x3f5b9cc2),
+     TOBN(0x7c7cff51, 0xdee66228), TOBN(0xe29236ae, 0xb676fd6e),
+     TOBN(0xf0c0105e, 0xab0cdb1a), TOBN(0x0adc9d6e, 0x06b52812),
+     TOBN(0xc9e6ca97, 0x483baf0f), TOBN(0x09b063bf, 0xf9bf5055),
+     TOBN(0x8c4c6b2a, 0xfc5a407f), TOBN(0xe29cb487, 0x99a6f540),
+     TOBN(0x18b72239, 0xcb9a2758), TOBN(0xa0ae9f10, 0x8a5ed308),
+     TOBN(0x2a2cb603, 0x6e402657), TOBN(0x9c7f52cf, 0xaf6f4d14),
+     TOBN(0x0ed032e7, 0x70ca9046), TOBN(0xe4b0b1d3, 0x59cac9e9),
+     TOBN(0xd12c87b0, 0x18462dfd), TOBN(0xa25a23ee, 0xbb8860dd),
+     TOBN(0x6c9a8676, 0x507fa3d9), TOBN(0xc6bb96c4, 0x218f325f),
+     TOBN(0xe141bbb8, 0x2386b7b2), TOBN(0xf86a72d0, 0xd4183c77),
+     TOBN(0x35537f86, 0xaece96b6), TOBN(0x83aa1df9, 0x63f7e1fa),
+     TOBN(0xa39ab4aa, 0x7ac4aaf2), TOBN(0xb8d0ffa6, 0x8a202375),
+     TOBN(0xd916df09, 0x86514cd8), TOBN(0x71f905b3, 0x11902747),
+     TOBN(0x6388c2ee, 0x8c744f32), TOBN(0x6282e1f5, 0xa5431096),
+     TOBN(0x14bfa765, 0x7c388413), TOBN(0x95dd04d9, 0x7b4437aa),
+     TOBN(0xdf6ca849, 0x3c39d7c3), TOBN(0x85cb1123, 0x0c2ddf38),
+     TOBN(0xf7766d86, 0xc401529c), TOBN(0xe33416a8, 0x99a4d031),
+     TOBN(0x5c507c3f, 0xb874ace4), TOBN(0x0e3a42b6, 0xdad6fcb0),
+     TOBN(0x402da460, 0x47920742), TOBN(0xb142d6ef, 0xb45f94cc),
+     TOBN(0xc2d613e8, 0x76608dd4), TOBN(0xa2c06cdd, 0x5d75d4b5),
+     TOBN(0xa1951bc5, 0x3c564ff4), TOBN(0xe60f126b, 0xad1d5ecd),
+     TOBN(0xa634e765, 0x702135ad), TOBN(0xa5a56a6e, 0x8df44695),
+    },
+    {
+     TOBN(0x234b8c7a, 0x9e2207b4), TOBN(0x1f724f30, 0xf7ee9f62),
+     TOBN(0xfa908ca2, 0xc58e21b6), TOBN(0x55587744, 0xa74296ae),
+     TOBN(0x7dbe9130, 0x02911ae1), TOBN(0xc2075433, 0x9d3af02e),
+     TOBN(0x505b724b, 0x0f3955a1), TOBN(0x480e1a92, 0xcaeced66),
+     TOBN(0xb20f6128, 0x446d9f66), TOBN(0xd6e06b14, 0xc354b5a1),
+     TOBN(0xa72d287d, 0x63558aac), TOBN(0x4819be29, 0xae68a8fd),
+     TOBN(0xb024c324, 0x205fbdf2), TOBN(0x2fca94e7, 0x210927f9),
+     TOBN(0x74798be7, 0xbe658f80), TOBN(0x618e07f1, 0xef07c046),
+     TOBN(0xfba715fc, 0xb35a8c3d), TOBN(0xc2548193, 0xed1beba8),
+     TOBN(0xb956c6dd, 0x2ceb663c), TOBN(0x13d4ddbe, 0xaacafe85),
+     TOBN(0x2f8275b5, 0x30a29cc3), TOBN(0x10432e15, 0xf51b39ef),
+     TOBN(0xd6c9277c, 0x2509b2d0), TOBN(0x4ee0d4c3, 0x849b946c),
+     TOBN(0x547ba946, 0x54b01bbc), TOBN(0x7c56c36d, 0x055d4821),
+     TOBN(0x8e933620, 0x05575f20), TOBN(0xaec65be9, 0x3a621cf4),
+     TOBN(0x820b96df, 0x46287937), TOBN(0x35cea883, 0x733c67e7),
+     TOBN(0x30366a3a, 0x58cf3e05), TOBN(0x2580d565, 0x2da39773),
+     TOBN(0x7717c42f, 0xba4417ed), TOBN(0xb2d66fc7, 0x654c1086),
+     TOBN(0x07fe918e, 0x57503cd8), TOBN(0xf9385159, 0x3cacf74f),
+     TOBN(0x157d9081, 0x63063029), TOBN(0x79c84c08, 0x659034cf),
+     TOBN(0x02976610, 0xa8048cb9), TOBN(0xef822006, 0x03e81417),
+     TOBN(0x5fb5dd4d, 0x22e489c6), TOBN(0x9a06d9c2, 0x81e167e9),
+     TOBN(0x83fc248f, 0x6b974c90), TOBN(0xb78cab72, 0x7110dca6),
+     TOBN(0x73f8f311, 0x370ff66a), TOBN(0x8c5049eb, 0x3b61d20f),
+     TOBN(0xaac47edb, 0xc8516e05), TOBN(0x2ceba50d, 0x53f0201b),
+     TOBN(0x6679dc5e, 0x0b93fbc7), TOBN(0xf4457919, 0xa560bd27),
+     TOBN(0x2561bfca, 0xb1acadc9), TOBN(0x338fbb6d, 0x46708164),
+     TOBN(0x9f407621, 0x8b9cfd27), TOBN(0xe806c1e6, 0xd3123732),
+     TOBN(0xaa1eafc4, 0x7f24a161), TOBN(0xbee3f4a1, 0x68e6650b),
+     TOBN(0x453b6181, 0x5832cd6c), TOBN(0xc002e337, 0x985e90ba),
+     TOBN(0x4b33afde, 0x6414f686), TOBN(0xf9ab29e9, 0x8511fd45),
+     TOBN(0x067f0972, 0x6fb9a688), TOBN(0x7db6e14c, 0x7202a1b3),
+     TOBN(0x0c15b6e9, 0x73a881ab), TOBN(0xc8c324e0, 0xfad10660),
+     TOBN(0xa997a6d2, 0x87d9f927), TOBN(0x62307f24, 0xacd2f107),
+     TOBN(0xed7b48a5, 0x9c80a742), TOBN(0xecd33ae5, 0xa7c81e7e),
+     TOBN(0xcf05c763, 0xefa00a94), TOBN(0x38844b0d, 0xd9ee5aa7),
+     TOBN(0x02e0b05d, 0x214b293f), TOBN(0x732e776b, 0x8a8a510e),
+     TOBN(0x784cd909, 0x6c929e53), TOBN(0xe436e294, 0x56a33da2),
+     TOBN(0x68eeb727, 0xce9e06d2), TOBN(0x637890b3, 0xfce7e2f0),
+     TOBN(0xc0681a1c, 0xc3fde38c), TOBN(0x9cb729d9, 0x76dda134),
+     TOBN(0xaa69eb97, 0x5c333ece), TOBN(0xe138a680, 0x48eed8a6),
+     TOBN(0xd53cbd01, 0x505dc701), TOBN(0x413de346, 0x6a64c3d1),
+     TOBN(0x91f6cde9, 0x3170a5bf), TOBN(0x58ffdfd9, 0x8489b5fa),
+     TOBN(0xcc0b89d7, 0x5c584a48), TOBN(0x74f8ceed, 0x167f28de),
+     TOBN(0x250fa9f7, 0x8c878c91), TOBN(0xeb960a79, 0x630adfdb),
+     TOBN(0xe43ed412, 0xc9489dcb), TOBN(0xcec053a5, 0x112d084b),
+     TOBN(0x0fd4fe42, 0x664c7cd2), TOBN(0x48ee06f4, 0x82a917f7),
+     TOBN(0xc5de1970, 0x83cd65f5), TOBN(0x3a176578, 0x5569c42f),
+     TOBN(0xf24508f3, 0x4f876110), TOBN(0xf350374b, 0x0a415bc4),
+     TOBN(0x9c2b11c2, 0xc63aa8ad), TOBN(0x6ac1ae12, 0x7a51c0cc),
+     TOBN(0x75acd0d7, 0xe1db428f), TOBN(0x9e391227, 0x19800684),
+     TOBN(0x4f89e9c6, 0xb1050426), TOBN(0x099d97cc, 0xdaf99eee),
+     TOBN(0x27a19ad0, 0x1ffce97f), TOBN(0x05fad057, 0x3c038d77),
+     TOBN(0x96d6c678, 0x8597d09c), TOBN(0x38f6336c, 0x1e1d8b57),
+     TOBN(0x1f945bef, 0x6330ace7), TOBN(0x9d627bbb, 0x613f9faf),
+     TOBN(0xc0e7f21b, 0x19176cb7), TOBN(0xf0e09be3, 0xbb9db710),
+     TOBN(0x16b06011, 0xc650133c), TOBN(0x8629b975, 0xf3673d7c),
+     TOBN(0x8c8230b2, 0x67115575), TOBN(0x9bfc55b1, 0x9c9f30c0),
+     TOBN(0x132d0e07, 0xce93fd71), TOBN(0x08e4736e, 0x511c5947),
+     TOBN(0xd54a098e, 0xfe881630), TOBN(0x8ec67a85, 0x98efa501),
+     TOBN(0x72975dc7, 0x2267db00), TOBN(0x3d6fc706, 0xa338290d),
+     TOBN(0x0c891082, 0x55ade88b), TOBN(0x0525b501, 0x4b212e85),
+     TOBN(0x9ede010b, 0xb61362fa), TOBN(0x52f3d088, 0x881eecac),
+     TOBN(0x49957b6e, 0xbc6f0ae4), TOBN(0x25fe7263, 0x1659701d),
+     TOBN(0x41e9b7f5, 0x07b76f45), TOBN(0x5f2ad664, 0xbda77d42),
+     TOBN(0x5bdcb490, 0xa9c418c4), TOBN(0xd0e2c38d, 0xe500a527),
+     TOBN(0x0af29f6b, 0xca83fada), TOBN(0x1f75b1f2, 0x62273db6),
+     TOBN(0x8454f751, 0x9e857e57), TOBN(0x3fb816d1, 0xb9e79612),
+     TOBN(0xbe3040ae, 0x6412b5f3), TOBN(0x99535136, 0x843ca984),
+     TOBN(0xb26ec8a8, 0x3257f19d), TOBN(0xd32dc622, 0xe54dd785),
+     TOBN(0x0c52e874, 0xf8698ab5), TOBN(0xf9a60af5, 0x79183316),
+     TOBN(0x38575d53, 0xf7f4496a), TOBN(0x33adfd1e, 0x3d5cd0de),
+     TOBN(0x2133f4a1, 0x7f502017), TOBN(0x46c09393, 0x3e8676f8),
+     TOBN(0xca8a5a58, 0x3fb4c7fe), TOBN(0x2ad58826, 0x328ff257),
+     TOBN(0xd9264875, 0x13b8d08d), TOBN(0x661ae2b2, 0xdc5a845a),
+     TOBN(0xd2dcaa06, 0x49a408d3), TOBN(0x9ef164f8, 0x85c21e84),
+     TOBN(0x55efaf85, 0xb7819b61), TOBN(0x9488bb1c, 0xf504c32a),
+     TOBN(0xb571518c, 0x9bb777fc), TOBN(0xf369c391, 0x82415187),
+     TOBN(0x2d7c5dd9, 0x2720284b), TOBN(0x6feab634, 0x4eec41cc),
+     TOBN(0x2522d5db, 0x24ecd0be), TOBN(0x1fca0d97, 0x00338736),
+     TOBN(0x74416105, 0x20145279), TOBN(0xf4492e1e, 0x0496e925),
+     TOBN(0xa62e09fc, 0xbc33b547), TOBN(0x9434475a, 0xae063835),
+     TOBN(0x51edd69f, 0xa139b769), TOBN(0x17bbe224, 0xe5166a9d),
+     TOBN(0x6ecb0a02, 0x1b4b6c84), TOBN(0x16439490, 0x97968c70),
+     TOBN(0x75af0456, 0xbc8aa671), TOBN(0xaef056ab, 0x3b4411ff),
+     TOBN(0x686b7714, 0x05cef121), TOBN(0x5ad6bdf3, 0x078f4500),
+     TOBN(0x56df858c, 0x072e70eb), TOBN(0xa0fc5e6f, 0x254c0917),
+     TOBN(0x1a99de09, 0xc15bf9cf), TOBN(0x8aeb587a, 0xd008aacb),
+     TOBN(0xba2d8c53, 0xb900d652), TOBN(0x60eb5d0c, 0xad0f69b6),
+     TOBN(0x27098ff8, 0x10b27762), TOBN(0x33329ca9, 0x13264ed3),
+     TOBN(0xffceaf40, 0x887e3f40), TOBN(0x854b8284, 0x930df9ef),
+     TOBN(0xdda913a8, 0x7d5627be), TOBN(0x8eb94d64, 0x35e9823b),
+     TOBN(0x94c527fd, 0x2eb9e9bd), TOBN(0x18335b1b, 0xf57b9f74),
+     TOBN(0x3c44dac0, 0xd193a526), TOBN(0xd0717099, 0xe2b2d54f),
+     TOBN(0x65624fb4, 0xc4c67d11), TOBN(0x04aa7033, 0xbccedad8),
+     TOBN(0x0c522fac, 0x31470c52), TOBN(0x08eb33ca, 0x33b05d54),
+     TOBN(0x940e0693, 0xb4565e57), TOBN(0x7e2fd553, 0x7be56370),
+     TOBN(0xf722793c, 0x12e206ff), TOBN(0xb57e1f23, 0x155d3d02),
+     TOBN(0xd68be193, 0x94fc6ce3), TOBN(0xb0f3606c, 0x22d4815b),
+     TOBN(0xdaf995a9, 0xb62d5741), TOBN(0xa7d19980, 0x3e8f5df5),
+     TOBN(0x7bcdb661, 0xe4631583), TOBN(0x013193e3, 0x5a97dc7e),
+     TOBN(0x8210be46, 0xfc73494b), TOBN(0x508817ee, 0x57e10efc),
+     TOBN(0x7b6b8da2, 0x126f2466), TOBN(0x2f3b0ec6, 0x177bee35),
+     TOBN(0x5ceb71e0, 0x48db1eef), TOBN(0xd989d9c3, 0xdc62b6bc),
+     TOBN(0x2cc38cb9, 0xf78fac92), TOBN(0xcd2a009f, 0x955ba5f4),
+     TOBN(0x65a74191, 0x453b668e), TOBN(0x40e9dc38, 0xc081b447),
+     TOBN(0x48eb63bf, 0x8c3fdf2c), TOBN(0x7845cf66, 0x5763071a),
+     TOBN(0x30d9b771, 0x787754ca), TOBN(0x10b3729f, 0x8783a05a),
+     TOBN(0xf8090b3b, 0x6ab570d9), TOBN(0xc1dfbde1, 0x502b3558),
+     TOBN(0xa568f5d0, 0xbe4d36ec), TOBN(0x1e9e5393, 0x28e952fe),
+     TOBN(0x768113f9, 0xeaad6b24), TOBN(0x2bc798fc, 0xa8fbede9),
+     TOBN(0x2c787f9b, 0xaaa9010d), TOBN(0x32cbc77a, 0x761834ca),
+     TOBN(0x449e55f7, 0x032c25d8), TOBN(0xe824a5bf, 0xca6216dd),
+     TOBN(0x3beaec52, 0x17ac1130), TOBN(0xcc28c64b, 0x5e9d1aa8),
+     TOBN(0x355d68bf, 0x3af8c635), TOBN(0xcd12e443, 0xd3d1adaa),
+     TOBN(0xa456daca, 0x6c2030d8), TOBN(0x0dfe5bbb, 0x62427eff),
+     TOBN(0xae45325d, 0xf69e2484), TOBN(0xfc2a90ab, 0x7245c979),
+     TOBN(0xc34f38e9, 0xa008b446), TOBN(0x5e86163d, 0xaac355e0),
+     TOBN(0x61432646, 0xd586a2fa), TOBN(0xc68c7c8e, 0x3d92e0c3),
+     TOBN(0xbfa8c268, 0x020c1dd6), TOBN(0x25788741, 0x8bbcc223),
+     TOBN(0xbaf8c9a3, 0xef62642c), TOBN(0x6d2f1ae5, 0xa8c496d2),
+     TOBN(0x92d1c805, 0x44e57ce4), TOBN(0x34cdf4a3, 0xaacd2100),
+     TOBN(0xd5b89e4d, 0x31e9c649), TOBN(0x558a6e26, 0x232cfc85),
+     TOBN(0xb40f3e4a, 0xcea31a31), TOBN(0x346c1e07, 0x35c5c924),
+     TOBN(0x8ffedd8e, 0x3fcede81), TOBN(0x35269ba3, 0x3b55d143),
+     TOBN(0x0366065a, 0x848bdc53), TOBN(0xba2af074, 0x078554dd),
+     TOBN(0x3c755fba, 0x19ff3b4d), TOBN(0x5ea93372, 0x35a22cbb),
+     TOBN(0x0e55fe02, 0x1eb3e23b), TOBN(0x2626ecca, 0x765dede4),
+     TOBN(0x187bf094, 0x81f445da), TOBN(0xba011017, 0x9df30578),
+     TOBN(0x81148037, 0xd72507f2), TOBN(0x3a5841fc, 0x5db072d0),
+     TOBN(0xfd631862, 0xbd910aa1), TOBN(0x17b22d68, 0x23881c60),
+     TOBN(0x6fa799cb, 0xfcc13359), TOBN(0x55c40219, 0x2d39fc5a),
+     TOBN(0xd50bfff6, 0x9f1f6515), TOBN(0x575090b5, 0x2e30fa1a),
+     TOBN(0x70343a0b, 0x9ba20c27), TOBN(0xef34db86, 0x749306a5),
+     TOBN(0xd7ad61d2, 0x4ba85f8d), TOBN(0xe4d24ad8, 0x7e091a33),
+     TOBN(0xbd6b49b9, 0xfc348493), TOBN(0x4f11b543, 0x299c325d),
+     TOBN(0x4574a254, 0x55036e93), TOBN(0x534a623e, 0x676b75d9),
+     TOBN(0x54aa3d8a, 0x7d05afbd), TOBN(0x13349009, 0x7f3f2c90),
+     TOBN(0xcd03de0f, 0xa94b18aa), TOBN(0x13123c18, 0x855c050d),
+     TOBN(0x747c3cda, 0xf598b5f8), TOBN(0x0f7ed9b6, 0x8644df20),
+     TOBN(0x45e010fd, 0x08d73585), TOBN(0x3b0ff430, 0xf8cec4af),
+     TOBN(0x3745a41f, 0xb028a832), TOBN(0xcd6d2468, 0xc4106172),
+     TOBN(0x56c5a9b0, 0x1eceac6f), TOBN(0x769c1285, 0xe1e6e980),
+     TOBN(0xbd163a36, 0xfa113196), TOBN(0x206ffc36, 0x5840c242),
+     TOBN(0x12de1147, 0x9c57ef67), TOBN(0x50260273, 0x03bea885),
+     TOBN(0x3099c21b, 0x9684d63f), TOBN(0x06adb196, 0xc7c66691),
+     TOBN(0x8464492c, 0x3d63b3be), TOBN(0x86024ef4, 0x0bd38c15),
+     TOBN(0x226022a2, 0x10565cbf), TOBN(0x2ae6b298, 0xc9899033),
+     TOBN(0x5564856b, 0x262ffa14), TOBN(0x9472d0e1, 0x7e038b55),
+     TOBN(0x1b17ea07, 0xb1dd268f), TOBN(0x8340b9d5, 0x4899352e),
+     TOBN(0x4f159400, 0x75242992), TOBN(0xe4392a31, 0xe6727e9d),
+     TOBN(0x4df1ef86, 0xaef59026), TOBN(0xe40671ff, 0x950cfee6),
+     TOBN(0x7b36d1cd, 0xde4dd990), TOBN(0x25df10a6, 0x3366ff4b),
+     TOBN(0x83fb7e59, 0x584ef631), TOBN(0xf12dd400, 0x91af7b6a),
+     TOBN(0x4a5ae41e, 0xe26f11c7), TOBN(0xeb86d5dd, 0x96d90445),
+     TOBN(0x028ae37e, 0xa355d0a3), TOBN(0x3c118ef4, 0x99260127),
+     TOBN(0xb8c7538c, 0x76f51bd7), TOBN(0x66b90aae, 0x5fbadc4d),
+     TOBN(0x078de9df, 0x17dfd0cf), TOBN(0x938df6da, 0xfe44b17c),
+     TOBN(0x4a09616b, 0xc40bc950), TOBN(0x0b507845, 0xbc969aac),
+     TOBN(0x23bae091, 0x35f7fb82), TOBN(0xebc04d37, 0x9ad29b83),
+     TOBN(0x9fa48a5b, 0x26a3c5fa), TOBN(0xf67c661c, 0xf08f3d8c),
+     TOBN(0x21825747, 0xb2e9c3a1), TOBN(0x3520a82e, 0x46c3eb43),
+     TOBN(0xe309ae9f, 0x9646183d), TOBN(0xa19c31be, 0x26dac288),
+     TOBN(0x3c37018d, 0xc76623de), TOBN(0x59b9a0fa, 0x64b51087),
+     TOBN(0xa32f8357, 0xf1954f4e), TOBN(0x24251df4, 0x1f6da143),
+     TOBN(0x825c61cf, 0x01fb07d0), TOBN(0x69ae93fd, 0xf2f0243c),
+     TOBN(0xd507c99e, 0x0943f7bd), TOBN(0x5e6dfb09, 0x463ee64a),
+     TOBN(0x10a3c32a, 0x29afd00b), TOBN(0x92179b5f, 0xbe8cbada),
+     TOBN(0x7bebad0a, 0x7d89f9a6), TOBN(0x7ba9bbf6, 0xd13b3073),
+     TOBN(0xfc23c215, 0x6481cc75), TOBN(0x6a654801, 0xfeca7532),
+     TOBN(0x20785ec0, 0x0fed841f), TOBN(0xcb612be9, 0x25269b21),
+     TOBN(0xe9a9c09b, 0x414a0fab), TOBN(0x5404c7a7, 0x6b4fa0dd),
+     TOBN(0xde62dae8, 0xcb418588), TOBN(0x2d80afd4, 0xc594334e),
+     TOBN(0xfe454df1, 0x95366a44), TOBN(0xda7626c3, 0x755cf8b2),
+     TOBN(0x4446f0ab, 0x41397051), TOBN(0xd1788064, 0x70eb8b23),
+     TOBN(0xbc7737f1, 0x977b040a), TOBN(0xbfb39418, 0x57590edb),
+     TOBN(0xb094b4a8, 0x343a7333), TOBN(0xb15912ce, 0xeb91372f),
+     TOBN(0x584e1d5e, 0x0b56002c), TOBN(0x1460ce24, 0xaa0cb90f),
+     TOBN(0x58f0c144, 0x8f7ffcb6), TOBN(0x56e39f33, 0xbe0d802e),
+     TOBN(0xb02a6edb, 0x529458d0), TOBN(0xa0fbae74, 0xa730f9d5),
+     TOBN(0xd98c0ac5, 0x1bf69928), TOBN(0x5c9f888f, 0x796f12e9),
+     TOBN(0x749207b0, 0x22e065c8), TOBN(0x6e0232a4, 0x88c61143),
+     TOBN(0x27f30808, 0x837adda6), TOBN(0x0b748853, 0xd0c215d5),
+     TOBN(0x97bc4008, 0xbf076ba7), TOBN(0xadae0275, 0xf157f4d2),
+     TOBN(0x394e5d7a, 0x8bcba88b), TOBN(0xf995ec14, 0x23ef50ad),
+     TOBN(0x6b207f9c, 0xaa9b60a9), TOBN(0xcd7509c1, 0x52f9979f),
+     TOBN(0xe3e8f6dc, 0x0834e0ad), TOBN(0x6e2a4165, 0xcd5b1314),
+     TOBN(0x073a2db3, 0xfd60d975), TOBN(0x5ad92ca4, 0x2c053b7a),
+     TOBN(0xceb10220, 0xfba97ae8), TOBN(0xab82f6a0, 0xd265e913),
+     TOBN(0xc7a755ad, 0xaa68a13d), TOBN(0x56c13360, 0x10dd277a),
+     TOBN(0xbbcf6411, 0xdef56183), TOBN(0xebffe360, 0xb863a4e1),
+     TOBN(0x67ff26e5, 0x814e8aec), TOBN(0x90553561, 0xa0804732),
+     TOBN(0x407396ac, 0xad5fe672), TOBN(0x053a068b, 0x11ad53af),
+     TOBN(0xb518dd04, 0xc652cb95), TOBN(0xc818563a, 0x57e2b99f),
+     TOBN(0x217cf87d, 0xb96432a7), TOBN(0x7fdc35be, 0x8cc2fdcd),
+     TOBN(0x8c2ef271, 0x4effaebf), TOBN(0x21879369, 0xf427c7c2),
+     TOBN(0xd80ebbea, 0x5197ba08), TOBN(0x1b00db45, 0xfc6f4c66),
+     TOBN(0x99b9d2be, 0x3828a5c0), TOBN(0xa8855350, 0xd24a69e8),
+     TOBN(0x2f919695, 0x3ceaa821), TOBN(0x89390c91, 0x04296439),
+     TOBN(0xc12b3852, 0xc5cf8a0c), TOBN(0xdcf234ea, 0x73afc431),
+     TOBN(0xdddf5568, 0xf4adf01b), TOBN(0x2d04fc76, 0x1b426097),
+     TOBN(0x02a21dd5, 0xcb27c5e0), TOBN(0x01b842c3, 0xa6b2f9b3),
+     TOBN(0xefbd483e, 0xd8bd7a07), TOBN(0x0220825e, 0xd13a781e),
+     TOBN(0x8aa029a0, 0xde802c17), TOBN(0xb62fcd6e, 0x3a3f0fbf),
+     TOBN(0x80558aff, 0xe9635f48), TOBN(0xbdc6851d, 0x65dbeb2d),
+     TOBN(0x99f48286, 0xb5412271), TOBN(0xa53ef798, 0xe242a8a3),
+     TOBN(0x41d18606, 0xc0371086), TOBN(0x5df3379c, 0xfcb6d1aa),
+     TOBN(0x53f2f5a1, 0x00a7a13e), TOBN(0x3565a6eb, 0x2bf51e2a),
+     TOBN(0xa2832b52, 0x930c5a71), TOBN(0x66071ec7, 0xee2abfcb),
+     TOBN(0x75ce0a56, 0x3496a532), TOBN(0xa316dfbb, 0xbe0d69b7),
+     TOBN(0x35438d6a, 0x353e94fc), TOBN(0xf53433c1, 0x1e0ce775),
+     TOBN(0x47ea3e8f, 0x22ff3a1d), TOBN(0x60ebfba8, 0xcd7ccdb6),
+     TOBN(0x47c6b6e2, 0x33c475d0), TOBN(0xd18637e7, 0xb7959fd5),
+     TOBN(0x8d7a35ce, 0xa1ae3404), TOBN(0xf15c71d6, 0x75b832bf),
+     TOBN(0x65047419, 0x98b9d24b), TOBN(0x28625a55, 0x0dcf73f4),
+     TOBN(0x5aa9dce8, 0xc7c99478), TOBN(0x752d1625, 0x0bde8d53),
+     TOBN(0x7255ecfa, 0x93e99ee4), TOBN(0x1c53bf71, 0x22706f48),
+     TOBN(0x28335451, 0x4d478014), TOBN(0xd64b05ff, 0x3f392c0a),
+     TOBN(0x1d9ac89d, 0x4fba1661), TOBN(0x281a0ffb, 0x34828cd8),
+     TOBN(0x07abacdd, 0x577ed419), TOBN(0xa66949f5, 0x3cfb2c56),
+     TOBN(0x38e69105, 0x847ebe65), TOBN(0x8fbbba5a, 0x44d6236d),
+     TOBN(0x0c85bd64, 0x725ef820), TOBN(0x4ef91524, 0x25a152a7),
+     TOBN(0x5237ef0e, 0xb019cebf), TOBN(0x48203f41, 0xc9a7724f),
+     TOBN(0x1f61885c, 0xc55fc0d4), TOBN(0x2c4dd07a, 0xbcb3daeb),
+     TOBN(0x9855d5e7, 0x4b7dafc5), TOBN(0xd76e6fdf, 0x5f3769af),
+     TOBN(0xb834df1a, 0xbb547be6), TOBN(0x43056b12, 0x3e7a9586),
+     TOBN(0x7459e0bb, 0x4375fc7e), TOBN(0x5f5f295a, 0x9c85fc6b),
+     TOBN(0x3f2d2bb7, 0xbb23b709), TOBN(0x0b53bd8c, 0x955983ce),
+     TOBN(0x5aee93df, 0xfaf68dcc), TOBN(0x5575770c, 0x509f09dc),
+     TOBN(0x2bf23c0c, 0x40b1b744), TOBN(0x524154bb, 0x9a5bb913),
+     TOBN(0xb0e93d76, 0x296bdb2e), TOBN(0xb3c71f5b, 0xda0b2925),
+     TOBN(0x8e77ae7a, 0x0c617939), TOBN(0x2bfea97e, 0x1aca9b0a),
+     TOBN(0x8e3317c9, 0x7897c5a8), TOBN(0x850ddefb, 0x4cee2716),
+     TOBN(0x684ceee9, 0x107d9186), TOBN(0x48708423, 0x0082c312),
+     TOBN(0x5300137c, 0x6c556897), TOBN(0x6d464401, 0x7e99bc76),
+     TOBN(0x6b11e1e3, 0x8808ca2b), TOBN(0xedd30eee, 0xcca6433e),
+     TOBN(0xa9099f60, 0xaa072ff0), TOBN(0x774662ec, 0x5830f69d),
+     TOBN(0x0770355c, 0xf94547d9), TOBN(0xb5041edb, 0x42967865),
+     TOBN(0x169a6274, 0xe585a93b), TOBN(0x06cebf5e, 0xd04d6a81),
+     TOBN(0x0a59450f, 0x2ebc1791), TOBN(0x69fd006e, 0x765ac18a),
+     TOBN(0x4711ec9c, 0xa54f7e7a), TOBN(0xd72c8d58, 0x819c6af9),
+     TOBN(0x89c97c6c, 0xb5418e15), TOBN(0xd520b03c, 0xa558a854),
+     TOBN(0xe3c24aca, 0x0d76773a), TOBN(0xc4deb5ce, 0x67e5110e),
+     TOBN(0x5bb40152, 0xcbb04ba4), TOBN(0x672563b6, 0xde1b628a),
+     TOBN(0xaec916aa, 0x2e8d9e54), TOBN(0xa4e8cb47, 0x3c60ac70),
+     TOBN(0x54a03e39, 0x452d5064), TOBN(0x1e405c2d, 0x1e7bb355),
+     TOBN(0x2ab2d5df, 0x3bbd3ab0), TOBN(0x0808410e, 0xdbc9fbd8),
+     TOBN(0x4180ceea, 0x27f23f6f), TOBN(0x2b965b35, 0xba1d6beb),
+     TOBN(0x14f1f010, 0xf66d6546), TOBN(0xefdca6a8, 0xf85cfb4a),
+     TOBN(0x69e6188e, 0x6614c1c0), TOBN(0x00bd1531, 0xe07cb7f8),
+     TOBN(0x1b90f515, 0x4bb7ee68), TOBN(0x8afdf466, 0x51abb1f3),
+     TOBN(0xf59a7327, 0xb5f34316), TOBN(0x43c3c19d, 0x64c7bf22),
+     TOBN(0xb275733f, 0xcdb00a2b), TOBN(0x0160df79, 0x602915ba),
+     TOBN(0x1ae4ee9a, 0x1baea574), TOBN(0x0d922f28, 0x03ae5168),
+     TOBN(0x07df28fd, 0xca691124), TOBN(0x5aad2f31, 0x8dd457c4),
+     TOBN(0xe0d4f443, 0x137384ca), TOBN(0xd93d424a, 0x6620ea8c),
+     TOBN(0x21d544d3, 0x5067797a), TOBN(0xc8a8cc99, 0x9d8a15bc),
+    },
+    {
+     TOBN(0x610f0e26, 0x941d80a3), TOBN(0x30927879, 0xad36514e),
+     TOBN(0xaa2dfd48, 0x98f22601), TOBN(0xbc5b31b7, 0x88c8b0f6),
+     TOBN(0xb1281f37, 0x6c841cc8), TOBN(0xdae16719, 0x5a412b84),
+     TOBN(0x9ec1f6c8, 0x828f210d), TOBN(0x1935d576, 0xe8d92901),
+     TOBN(0x47247921, 0xaf3840f2), TOBN(0x348325d2, 0xdf3fcdfc),
+     TOBN(0xef578508, 0xc43961bd), TOBN(0x7d5e8ccd, 0x1bd98c29),
+     TOBN(0x59cdba10, 0xf8a30164), TOBN(0x0757408f, 0x7cb8c653),
+     TOBN(0xcd7ed73f, 0xc3056ef4), TOBN(0xd28e7cc1, 0xfb99cd1b),
+     TOBN(0xa8a67494, 0x6bb62286), TOBN(0x8d6ef816, 0xe7d87467),
+     TOBN(0x3529f938, 0xf673b6d5), TOBN(0xf5c0ee76, 0x8bbf101b),
+     TOBN(0x3768ed61, 0x49fdc949), TOBN(0x53b6999e, 0xcf405ee0),
+     TOBN(0xbf0108a0, 0x1718e51a), TOBN(0x38e609ec, 0x5181ebd7),
+     TOBN(0x82051640, 0xd8595159), TOBN(0x30e1c706, 0x579a3876),
+     TOBN(0x091154c6, 0x0298a67c), TOBN(0x76d9a9c9, 0x51132d27),
+     TOBN(0xe41de8b7, 0x5c661a4d), TOBN(0xf24b5e96, 0x2a6dcbaf),
+     TOBN(0x1ed4cb0c, 0xd714e045), TOBN(0x605926a4, 0x0c187c70),
+     TOBN(0x97672559, 0xf34b1c93), TOBN(0xa256be98, 0xe0b43506),
+     TOBN(0x7fcdd412, 0xe1a98eb3), TOBN(0x7fcfcd84, 0x295a061b),
+     TOBN(0xddbac22b, 0xcc2386cf), TOBN(0x7741adb7, 0x928c2556),
+     TOBN(0x3a2bb869, 0x4e6e1288), TOBN(0x53ed11da, 0x025bb4a1),
+     TOBN(0xb114bd67, 0x4108c8e2), TOBN(0x85948c6b, 0x3deb8e23),
+     TOBN(0x6a9e05d9, 0xd0e9434c), TOBN(0x395060b7, 0xb9c4fd70),
+     TOBN(0xa0ccfd16, 0x18893751), TOBN(0xa941ff60, 0xbbf65477),
+     TOBN(0x34ada238, 0x59423e35), TOBN(0x4ba7a7d1, 0x7e570058),
+     TOBN(0x3d05d455, 0x869ae596), TOBN(0x92a15400, 0x93368325),
+     TOBN(0xbd64ae0a, 0x62eb7ffa), TOBN(0x3f263a7a, 0xd34b2c59),
+     TOBN(0xe95eece3, 0xa22244e1), TOBN(0x39ccef58, 0x706fc500),
+     TOBN(0x39a88f93, 0x22f1d2e6), TOBN(0x1ec796b3, 0x6d47c670),
+     TOBN(0x1558e0f2, 0xaa1ff4af), TOBN(0x61f43487, 0x390503a4),
+     TOBN(0x66164732, 0x3c4f76f1), TOBN(0x50d0706e, 0x9e13432e),
+     TOBN(0x5f1a87ca, 0xf5eba0fb), TOBN(0x28a95c0f, 0x80bda2c5),
+     TOBN(0x10d693e0, 0x12ae6462), TOBN(0x79871340, 0xf45e6ba6),
+     TOBN(0x8abf0cad, 0x887051c0), TOBN(0xd09f571d, 0xb3c6b540),
+     TOBN(0x3fb2e16b, 0xe30ab25d), TOBN(0x12e057a7, 0x539e8bc4),
+     TOBN(0x6efe71ce, 0x733c2597), TOBN(0x72fa0df5, 0xe71058ac),
+     TOBN(0x49f14d06, 0xccc037bf), TOBN(0x9a3ceb03, 0xceb3eb6c),
+     TOBN(0x9895e908, 0x4195370a), TOBN(0xa75007e5, 0x927345e7),
+     TOBN(0x00b4c212, 0xd3850711), TOBN(0x76e4060d, 0xdfee8b34),
+     TOBN(0x15801862, 0x184c1d07), TOBN(0x234e39c0, 0x302f5711),
+     TOBN(0x4c0bd387, 0x6aa74204), TOBN(0x0515eddc, 0x6f496836),
+     TOBN(0xa9fd0cb5, 0xc849afbe), TOBN(0x041df5ba, 0xcad5c0aa),
+     TOBN(0x9a54af37, 0xddff259e), TOBN(0xa3f156bf, 0x9b550a8e),
+     TOBN(0x4df2d33d, 0x7e3298d4), TOBN(0x0957a0a0, 0x65ff0e1a),
+     TOBN(0xff7fb43d, 0x1e2b3a45), TOBN(0xb86d386a, 0x1a73285a),
+     TOBN(0x6e283c47, 0x28b18e93), TOBN(0x5458b92f, 0x4b4132ed),
+     TOBN(0x7026345e, 0xba674332), TOBN(0xc8e38151, 0x5c9fc32d),
+     TOBN(0xd6aaf8e1, 0x58e7b4fe), TOBN(0x3e77a5c9, 0x4267253a),
+     TOBN(0x6441cba2, 0xa0949607), TOBN(0xfa205185, 0xdee20b2e),
+     TOBN(0x64f3d576, 0xf5b44600), TOBN(0xf12125db, 0xef5c8446),
+     TOBN(0x1467539b, 0x487a0116), TOBN(0x3aa0fa49, 0xf380229a),
+     TOBN(0xcc6586f1, 0xd79f36dc), TOBN(0xebcf653d, 0x1b9a0b42),
+     TOBN(0x68af006d, 0x9c1df729), TOBN(0x507e055a, 0xa7903ee6),
+     TOBN(0xd1b48ef8, 0xafd8ac9b), TOBN(0xd8920af7, 0x3b5f6b51),
+     TOBN(0x9629e789, 0x983339c8), TOBN(0xbfd2d787, 0xfa9248d3),
+     TOBN(0xb50ca302, 0x9a078882), TOBN(0x1c34f848, 0xf5cb0529),
+     TOBN(0xb9b01519, 0x1646a9f8), TOBN(0xc9e3679e, 0x80d53f9d),
+     TOBN(0x5a63e822, 0x1af3e5f2), TOBN(0x05480ad8, 0xff58e3d1),
+     TOBN(0x2d241643, 0xd6b3626b), TOBN(0x33b15660, 0xc1eda15f),
+     TOBN(0x3e74f855, 0x8528e5d6), TOBN(0xafb6dc9a, 0xf63188f4),
+     TOBN(0x0cac44cb, 0xaeeb1d32), TOBN(0x50661046, 0xa627eff8),
+     TOBN(0xadc4b012, 0x64b49667), TOBN(0xa4bdafa7, 0x1e05f760),
+     TOBN(0x171b28b3, 0xf185d27a), TOBN(0x987e5163, 0x33425747),
+     TOBN(0x7c42ac4e, 0xc3864a65), TOBN(0x2dae1bb8, 0xbf449c12),
+     TOBN(0x680d9743, 0x06965832), TOBN(0x6ac1ef01, 0x7e31d9f4),
+     TOBN(0xdef57433, 0x579d6ae4), TOBN(0xe055b087, 0xd5240bf9),
+     TOBN(0xe4dbbe60, 0x90a5e069), TOBN(0x2161d5fe, 0xddb3dc15),
+     TOBN(0x7d303012, 0xda297b71), TOBN(0x04481034, 0xd468046c),
+     TOBN(0xaa94d5bb, 0x0ac93c6c), TOBN(0x95bacd45, 0xd8d8f53a),
+     TOBN(0x790a5d6f, 0x3e03e895), TOBN(0x27efd502, 0x44fa5a81),
+     TOBN(0xd9d35230, 0xe5998b32), TOBN(0xb36a0c07, 0xf22ade19),
+     TOBN(0x46ec8691, 0xf979a2fe), TOBN(0xa67ba933, 0xced8cb94),
+     TOBN(0x00d07245, 0x2f856ab3), TOBN(0xadc9ff42, 0x3c925dae),
+     TOBN(0x0e4eaa25, 0x563038a5), TOBN(0xfef7e89c, 0x8a8f6483),
+     TOBN(0x50433633, 0xace61af8), TOBN(0x8a1589e0, 0x2e1a3515),
+     TOBN(0x99f21e29, 0x5fdcb1ac), TOBN(0x8fd2d411, 0xc9a466da),
+     TOBN(0x55323c6f, 0xf56b7f13), TOBN(0xa016d64a, 0x5cff438c),
+     TOBN(0x3e3dfcbc, 0xdc05b5cc), TOBN(0xc1956ca8, 0xfc3c70ec),
+     TOBN(0x7dbbd169, 0xe63f02df), TOBN(0x95206689, 0x240b87c8),
+     TOBN(0x7bacda5e, 0x1aa6d48a), TOBN(0x51dcf19f, 0x39280f78),
+     TOBN(0x1511ae04, 0x660abac2), TOBN(0x3a95adc9, 0xd905af53),
+     TOBN(0x0c8c4330, 0xea464737), TOBN(0x989c09c4, 0x34fc4b51),
+     TOBN(0x1852120d, 0xe2cf3376), TOBN(0x5a1cb8a8, 0x25c04de3),
+     TOBN(0x50486f98, 0x75fe7596), TOBN(0x8cd78d2e, 0x223030b1),
+     TOBN(0x524cb8f8, 0xcfa1ab11), TOBN(0xa716ea3f, 0x5a15b0b9),
+     TOBN(0x7618e95e, 0xb902d114), TOBN(0x0a1a4146, 0x084ebf5d),
+     TOBN(0xdfb909e9, 0xe3f87683), TOBN(0xa0b7eee1, 0x4107410c),
+     TOBN(0xa45a551c, 0xf02b0e12), TOBN(0xceabbfd2, 0x9efccb9f),
+     TOBN(0xb0d1b6bc, 0x740f4e3a), TOBN(0xfc737250, 0x4cbfd0de),
+     TOBN(0x3fad2d9e, 0x32452b0e), TOBN(0xb4e659fe, 0xf523527d),
+     TOBN(0xf0dcd701, 0x6c0ff353), TOBN(0x28f06e2a, 0xd67b6f98),
+     TOBN(0x2d0c36ce, 0x82a789b4), TOBN(0x20e577da, 0x49c0d54e),
+     TOBN(0x8d1d5c7f, 0xae38dd0e), TOBN(0x72e01399, 0x894d9143),
+     TOBN(0xf7806856, 0x3958e055), TOBN(0xac35ee40, 0x5df44aee),
+     TOBN(0x2b478913, 0x97c18b8d), TOBN(0x5396824e, 0xfa2586cd),
+     TOBN(0x22b37b25, 0x1b23f8c4), TOBN(0xf9ced36e, 0xcdecdefa),
+     TOBN(0x28c3bee5, 0xc2fc39c0), TOBN(0xa1731fae, 0x6d9db32b),
+     TOBN(0xa0445fa7, 0xbc3e2c91), TOBN(0xa1ab6955, 0x75a4aa72),
+     TOBN(0xf0cd61c6, 0xbbe0a1c7), TOBN(0x923c3b69, 0x0123bc52),
+     TOBN(0x818ad28c, 0xafd7c4bc), TOBN(0x7c229224, 0x28b15b05),
+     TOBN(0xecde7efb, 0x1f78a4f4), TOBN(0x550d68e7, 0x03ef3ab3),
+     TOBN(0x0371021d, 0xfc5f8c35), TOBN(0x4440aa1e, 0x0ed2b06e),
+     TOBN(0x70c8ede9, 0x9ba7247d), TOBN(0x0d2b6ed3, 0x84f23fde),
+     TOBN(0xd0119d95, 0x5ff4478c), TOBN(0x66152d27, 0xf79c69d5),
+     TOBN(0x56d8bea4, 0x02afd13b), TOBN(0x035efe5f, 0x15bb938a),
+     TOBN(0xc5ca7d08, 0x2ccaa425), TOBN(0xc8c69ea6, 0xeeee9376),
+     TOBN(0xb22cfe59, 0x493a2051), TOBN(0xcb50e618, 0xdc7b90fb),
+     TOBN(0x0f6fdf2b, 0xe05a8705), TOBN(0x081f3fe7, 0x4814df31),
+     TOBN(0x6fefe18a, 0xeb1e3e76), TOBN(0x81910050, 0x03e06a50),
+     TOBN(0x8a801df1, 0xdb45bfea), TOBN(0x8c7fe1fd, 0x7a828cf6),
+     TOBN(0x1c1868b5, 0x8d173cfd), TOBN(0xe18f0a36, 0x0dbde1c8),
+     TOBN(0x3b29ed64, 0x9ac345b6), TOBN(0xd56d5956, 0x9dcd07a5),
+     TOBN(0xf4191570, 0xc6813a88), TOBN(0x39033ebc, 0xeda3af42),
+     TOBN(0xdee5591b, 0xad5d215d), TOBN(0x9cfa11c6, 0xafbe5a28),
+     TOBN(0x73d0f1e2, 0x1823c28f), TOBN(0x75d49925, 0xafab1f67),
+     TOBN(0x61c81e2c, 0x7c521448), TOBN(0xc547be6f, 0x4a96edb5),
+     TOBN(0xccb9fc59, 0x4ca368b3), TOBN(0x175ebe48, 0x04fc3303),
+     TOBN(0x507620cf, 0xfce42989), TOBN(0xf236e043, 0x9abfadb2),
+     TOBN(0x381c50c3, 0xab36ab58), TOBN(0xed4cb73e, 0xae22c6a3),
+     TOBN(0xa68a2827, 0x2158dc4c), TOBN(0x1715ac43, 0xe9fa53ff),
+     TOBN(0xb02fdf73, 0xfa266797), TOBN(0x3079f3c7, 0x7eefb203),
+     TOBN(0x0a41fb94, 0x7f7545bd), TOBN(0x6b9dd022, 0xcb923ace),
+     TOBN(0x582c7ff5, 0x3bea2541), TOBN(0x992f2379, 0x5ecdbe2d),
+     TOBN(0x821f1670, 0xfe17bdca), TOBN(0x521c06f2, 0x2626bdde),
+     TOBN(0x6292748c, 0x1864ca0b), TOBN(0x554d4ece, 0x1bc74d8b),
+     TOBN(0x745d4f74, 0xea3d4446), TOBN(0xa439f178, 0x40ad1c7f),
+     TOBN(0xc95d9510, 0x51374e92), TOBN(0x75870e9f, 0x90229008),
+     TOBN(0x3fec98c2, 0xc54e7e81), TOBN(0xef537ee9, 0x94b3860b),
+     TOBN(0x139dd834, 0x40bfc8f6), TOBN(0x20b51364, 0x0f114403),
+     TOBN(0x4752a49f, 0x30b4b4db), TOBN(0xdfbb8b17, 0x8c3c90e0),
+     TOBN(0x60c8915b, 0x70f0b16a), TOBN(0x5e395000, 0x40528319),
+     TOBN(0x8a1624c7, 0xa641f2e3), TOBN(0x3c9925c6, 0xbb4ca0dc),
+     TOBN(0x2aae6edb, 0x2c3152b5), TOBN(0x8dbac580, 0x08b896ff),
+     TOBN(0xe0516205, 0xe5a36bc8), TOBN(0xd7714332, 0x3295707b),
+     TOBN(0x61db6804, 0x51c3513f), TOBN(0xf2ee6e20, 0xab552df8),
+     TOBN(0x5ddcfa99, 0x353c17f0), TOBN(0x65687a2f, 0x046d5fd4),
+     TOBN(0xef567e9f, 0xfd1ccad4), TOBN(0x7cd5f7dd, 0xa0238b70),
+     TOBN(0x96fba79e, 0x92c01197), TOBN(0x46a9f2de, 0x83b50e70),
+     TOBN(0x7efcbbb2, 0xfe287169), TOBN(0xe30d60cb, 0x4528d67d),
+     TOBN(0x88fed0cc, 0x6cb04d3a), TOBN(0x63eb9d0d, 0x0221ceb8),
+     TOBN(0xc6954e9f, 0x748b5813), TOBN(0xceef2bd8, 0x5c96e897),
+     TOBN(0x99503ae2, 0x85648f04), TOBN(0xeee51f99, 0x923e87d7),
+     TOBN(0x90908fca, 0xb6560ceb), TOBN(0xafad5926, 0x80e0f6b3),
+     TOBN(0xa50f31f3, 0xaea32cf9), TOBN(0x7ea17064, 0xa74ae92d),
+     TOBN(0x0675ccc1, 0xcda71d1a), TOBN(0xd1e3b630, 0x1e0a464a),
+     TOBN(0xa361f2b7, 0x2442872d), TOBN(0xb21bcd39, 0x46e52c97),
+     TOBN(0x1405f89c, 0x85574630), TOBN(0x0da7bfbd, 0x8e0a96ab),
+     TOBN(0x48af06c2, 0x4220f57b), TOBN(0x772a9b12, 0x6a333e4f),
+     TOBN(0x3afc661e, 0x6f712eb8), TOBN(0x29deff6c, 0x2eba8817),
+     TOBN(0xbab680de, 0xd8c69e5a), TOBN(0xf8615abb, 0xe93daf10),
+     TOBN(0x7455ea1d, 0xcef6fae6), TOBN(0xac0a30ea, 0x868455fd),
+     TOBN(0xae967b17, 0xe47d628a), TOBN(0xa6d703e2, 0x65f1f482),
+     TOBN(0x2723a965, 0x0bfcc371), TOBN(0x9b06cc14, 0x6db4a042),
+     TOBN(0xa973d738, 0xa77c8b21), TOBN(0x9a981f80, 0xc008f2ed),
+     TOBN(0xecc7bbcb, 0xaf27cdb3), TOBN(0x514db964, 0xb5cb693a),
+     TOBN(0x24125414, 0xe75c93d1), TOBN(0xd9308c0e, 0x1f00d53c),
+     TOBN(0xdb56d155, 0x831eba6d), TOBN(0x29eefc2c, 0x672b86f1),
+     TOBN(0x332f6ab6, 0xdd13b3c9), TOBN(0x70e052f6, 0xe371f873),
+     TOBN(0x05740742, 0x125712ab), TOBN(0x4239152d, 0xb3512100),
+     TOBN(0x98355eaa, 0x80b22915), TOBN(0xd0e263ec, 0xb896f6fa),
+     TOBN(0x9378a8a6, 0x442b4c8f), TOBN(0x40c2b546, 0xf65795bb),
+     TOBN(0x0cfa46ed, 0xd572ead8), TOBN(0xb9b4abdb, 0x78361300),
+     TOBN(0x5fe63ef1, 0x8c102020), TOBN(0x1805c84e, 0x785a4b54),
+     TOBN(0x147cf487, 0x805cb642), TOBN(0x87cf50aa, 0x487e581f),
+     TOBN(0xe942fa5b, 0x9eaebcd0), TOBN(0x06d4fa96, 0xd1af71f2),
+     TOBN(0x20c1a770, 0xc4fc3823), TOBN(0xcdffd09e, 0x85140885),
+     TOBN(0x27ce78ab, 0x6b3592e9), TOBN(0xb8e8c15e, 0x8ba82008),
+     TOBN(0x5fe8f3f0, 0xfef74187), TOBN(0x8e85a3a5, 0x77ce808d),
+     TOBN(0x8447dc69, 0xc7395f64), TOBN(0xae90769f, 0x1181b854),
+     TOBN(0x54adc101, 0x456114c8), TOBN(0xe7962b76, 0x9ca6a9c1),
+     TOBN(0x3f0e77fb, 0x909410a4), TOBN(0xe18151cd, 0x9e2e44f9),
+     TOBN(0x5e510a0a, 0x2cf6e29e), TOBN(0x136896ab, 0xb1836b07),
+     TOBN(0x3ad4fdec, 0x0fe11010), TOBN(0x35b36790, 0xdbddf038),
+     TOBN(0x7c4f5a68, 0x75903df9), TOBN(0x3e9cb056, 0x2f5b7193),
+     TOBN(0x745e9452, 0x591a4524), TOBN(0xc406ad44, 0x1a056e15),
+     TOBN(0x2e93edf2, 0xa69e11ef), TOBN(0xa28b82fd, 0x73a1cb88),
+     TOBN(0xdc1c9cda, 0x1225c3d5), TOBN(0x86e9a994, 0xa5569794),
+     TOBN(0xd698506e, 0x5b092dde), TOBN(0x076a4c82, 0xd1ca8b06),
+     TOBN(0x4516033b, 0x2ef2bc6f), TOBN(0x0574c792, 0xd78fa65f),
+     TOBN(0xa3b1c3d8, 0x735bb362), TOBN(0x22fca7a4, 0x0da54317),
+     TOBN(0x3e7ae709, 0x60aaebb6), TOBN(0x42417d54, 0x937638c1),
+     TOBN(0x32f00a5d, 0x1dfe8b0e), TOBN(0x8ea5e8e1, 0x8dcdbdbc),
+     TOBN(0x38df57cb, 0x6b30ea52), TOBN(0xd325aa1c, 0xe94c30ca),
+     TOBN(0x7aa04a9d, 0xdce4d256), TOBN(0x78e98cd3, 0x74c7db6b),
+     TOBN(0x631475a8, 0x443d5c9f), TOBN(0x34e5c73a, 0x7adfbceb),
+     TOBN(0x7fb69bab, 0x9f1e8828), TOBN(0xcadc78be, 0xc84149e3),
+     TOBN(0xe9424ecc, 0x1fe86af8), TOBN(0x13160cc8, 0xbc504ea8),
+     TOBN(0xcb380078, 0x4c96a680), TOBN(0x006fb9d8, 0x845faae5),
+     TOBN(0xc6a64277, 0x1e0e66d1), TOBN(0x13f77d6e, 0x428f526d),
+     TOBN(0x9f80fe8c, 0x28474530), TOBN(0x5649a173, 0xdb7fec00),
+     TOBN(0xdeed5bf4, 0xd9cb05ca), TOBN(0x14b1a3a9, 0xd7077c41),
+     TOBN(0x4c2ed239, 0x096883ec), TOBN(0xd550edfe, 0x44ae671d),
+     TOBN(0xb233e5dc, 0xf7b7362a), TOBN(0x32c15820, 0x4fd464f2),
+     TOBN(0x0ecb18f7, 0x68880bf9), TOBN(0x53468bed, 0xaf230a34),
+     TOBN(0xe3ba97b9, 0x370cd6ef), TOBN(0xf5cdabf4, 0x3516d77e),
+     TOBN(0x08d78a56, 0x11462032), TOBN(0x1393fa93, 0xd583ccc5),
+     TOBN(0x52af7f5d, 0x0c1b3514), TOBN(0xf48cac66, 0x188ca043),
+     TOBN(0x2524c8dd, 0x5461a1d1), TOBN(0x6eee8101, 0x91b6e707),
+     TOBN(0x209fece6, 0xca2fe87e), TOBN(0x50b35727, 0x9ac56706),
+     TOBN(0x651a6701, 0xec373bb2), TOBN(0x881de85b, 0x1a4c2e84),
+     TOBN(0x4892861d, 0xcfdb47d5), TOBN(0x5ae2e653, 0x5cdc4424),
+     TOBN(0xc58f4f59, 0xa1f90dd9), TOBN(0xa5584f85, 0xfcf158a4),
+     TOBN(0xbde86fb0, 0xab072a7a), TOBN(0x7c69e25a, 0x268bae62),
+     TOBN(0xee3478f3, 0x44fc7b3e), TOBN(0xec148394, 0x6b7d3647),
+     TOBN(0x2a542ebf, 0xe1c8c0ca), TOBN(0x63d1d635, 0x161dc0c1),
+     TOBN(0x769acdbe, 0x57ab9282), TOBN(0x9c338971, 0x2a119cb9),
+     TOBN(0x049e366f, 0x125e5b4c), TOBN(0x3aec68e0, 0xf0c8fde4),
+     TOBN(0x9d95b6e5, 0x324cefda), TOBN(0x844cce33, 0x704014b5),
+     TOBN(0x03920a61, 0x6a6bb216), TOBN(0xd69d17e3, 0xf379db8e),
+     TOBN(0x1924ac16, 0xc5e386e5), TOBN(0x62373a48, 0xd64953c3),
+     TOBN(0x5b1f7d64, 0x47f4e4a4), TOBN(0xc043b5b5, 0xffa115fd),
+     TOBN(0xb2a2656e, 0x87fb16b0), TOBN(0xcac56b9b, 0xd8cd79a6),
+     TOBN(0x544971f6, 0xcc19d3af), TOBN(0xf539244b, 0x0fd63db9),
+     TOBN(0x0f052d3c, 0xfbf4d232), TOBN(0x6b3c8366, 0x7a2a7280),
+     TOBN(0xaa6579db, 0x48079b9f), TOBN(0xc5beb93d, 0xa4d9edcf),
+     TOBN(0x8ad58825, 0x0f1599a3), TOBN(0x3f3a2634, 0x5f3f640b),
+     TOBN(0xda15393a, 0x9032fd7c), TOBN(0x97c10230, 0xac0e7136),
+     TOBN(0xfa32ef9f, 0x599785ee), TOBN(0xe1ed3b28, 0x6b4c7a65),
+     TOBN(0xcee1af27, 0x2da1dcdd), TOBN(0x4e480c11, 0x6861e2c2),
+     TOBN(0x35b5ec42, 0x9c8ad8c3), TOBN(0xfd07f6a4, 0x3fc55f23),
+     TOBN(0xab18ead2, 0xea76d444), TOBN(0xcb8bde14, 0x22ba099a),
+     TOBN(0x252e6a81, 0xc61ae967), TOBN(0xaf11042c, 0x72a2e1e6),
+     TOBN(0xb353902a, 0x1a211ef8), TOBN(0x644d16e0, 0xc99a25fc),
+     TOBN(0x637fd606, 0x5b67e48a), TOBN(0xfa570963, 0x51a0b665),
+     TOBN(0xaa661c73, 0x7ee072b8), TOBN(0xde1eb4fe, 0xf2e0a727),
+     TOBN(0x56096a0c, 0x22ed7ee6), TOBN(0x31aaf403, 0x5825908b),
+     TOBN(0xfd5f6ba7, 0xbfa02db6), TOBN(0x85f4f9a9, 0xff798900),
+     TOBN(0xa0997d56, 0x4a0cd878), TOBN(0xdd76909c, 0xb1b6982e),
+     TOBN(0x874fab15, 0xeccf338e), TOBN(0x5e072b3c, 0x4ce82bb1),
+     TOBN(0x5dbe883f, 0x6dd0d997), TOBN(0xa32117f2, 0x41765fb6),
+     TOBN(0x59ca4da3, 0x7d87fc5e), TOBN(0xc91002cd, 0xb95ec918),
+     TOBN(0xd53bc123, 0x6548248f), TOBN(0xef10a373, 0x6c6d1e0e),
+     TOBN(0xafb2d760, 0x99d9893f), TOBN(0xb77c1f1b, 0xce0ba0ca),
+     TOBN(0xabce362c, 0xcfb9f6b2), TOBN(0xe6e108d2, 0x35f9be91),
+     TOBN(0xb2331290, 0x7187fa9d), TOBN(0xdcd1f4fd, 0xfc7ddce6),
+     TOBN(0x3a129991, 0x9086eb29), TOBN(0xb0730520, 0x53a56d57),
+     TOBN(0x9fcdf4cf, 0xabd421bd), TOBN(0x96271270, 0x08f3e8e0),
+     TOBN(0x951ea7e2, 0x401e0217), TOBN(0xa4d1d708, 0x733f637b),
+     TOBN(0xc75170f4, 0x4f4cd676), TOBN(0x568279ba, 0x832f0b4d),
+     TOBN(0xda4c01f7, 0x25c17ab7), TOBN(0xfcc13028, 0xfa30e1b9),
+     TOBN(0x4d1d8f71, 0xacba57ec), TOBN(0x0c7971cf, 0xef6b3913),
+     TOBN(0xdf16e73d, 0xc014f166), TOBN(0xd5796183, 0xf96f2c30),
+     TOBN(0xd13ee9f7, 0x3f70dd7c), TOBN(0x3f9aa0dd, 0xdac738c5),
+     TOBN(0xa200c7e4, 0xad021e28), TOBN(0x982abae3, 0x08414fd0),
+     TOBN(0x76d16a8c, 0xc3779882), TOBN(0x41563d33, 0xe70a6ff5),
+     TOBN(0xdbb9656e, 0x4b553a17), TOBN(0x96af21a0, 0xd9c87aa1),
+     TOBN(0x2de13a03, 0x7bd9a625), TOBN(0x29f8c49b, 0xfeb1fec2),
+     TOBN(0x84e2df47, 0x1a4ce44a), TOBN(0x83bb2965, 0x548b39ee),
+     TOBN(0x38b91cce, 0x94d996eb), TOBN(0x41e0a3cd, 0x9441ae0b),
+     TOBN(0x720d30d8, 0xdaa92f34), TOBN(0xba587579, 0x06f30fbb),
+     TOBN(0x24f74676, 0x4c96ad59), TOBN(0xf40493f7, 0x0d33bd5f),
+     TOBN(0x9068c3e9, 0x126a7267), TOBN(0xa51099df, 0x18927490),
+     TOBN(0x27452423, 0xa9cfe02f), TOBN(0xcfd035be, 0xb8749653),
+     TOBN(0x0dd9bc2a, 0xfda6a4a9), TOBN(0xdba0178a, 0x0106ae0e),
+     TOBN(0x3820c9f5, 0x4969a4bb), TOBN(0x5031e9fd, 0x99fbc715),
+     TOBN(0x642a030a, 0xc193d942), TOBN(0xdc3d6ab7, 0x454cbb39),
+     TOBN(0x507c17b9, 0x1c8fa77c), TOBN(0x8465bcc8, 0xe3642a95),
+    },
+    {
+     TOBN(0xe74e265b, 0xc25dfad3), TOBN(0xd03630b9, 0x493f44b6),
+     TOBN(0xb3270892, 0xbfd6d473), TOBN(0x5b2d9543, 0x1c5ee992),
+     TOBN(0xeeb94537, 0xa36f7c5f), TOBN(0x9befc01d, 0x8ab0b81d),
+     TOBN(0x483cdb08, 0x188b45e5), TOBN(0x44c753b7, 0x01e4648b),
+     TOBN(0xee43bc87, 0xb2411618), TOBN(0x08754bd2, 0xf07924c4),
+     TOBN(0xef205033, 0x4ac92557), TOBN(0x6e7e4fe6, 0xee0387f4),
+     TOBN(0x51f3e2e2, 0x76961d0e), TOBN(0x2b69d417, 0x37eac10f),
+     TOBN(0x36d0f45f, 0x73757a88), TOBN(0x38b967e5, 0x2b0c7d35),
+     TOBN(0x94ba8fc4, 0xb31fa779), TOBN(0x8024dc85, 0x0f13036e),
+     TOBN(0xfda2af63, 0x82d754b7), TOBN(0x4a784242, 0xae9ea9ae),
+     TOBN(0x67dd14ab, 0xf9887947), TOBN(0x7f2ecfc4, 0xcd555a0a),
+     TOBN(0xb37c4244, 0xf63a46aa), TOBN(0xd032cfc1, 0xff71b4b5),
+     TOBN(0x0aef84c1, 0x6b8a6a97), TOBN(0xd2e7f3de, 0x0b2bca36),
+     TOBN(0x721c6c09, 0x5b174d43), TOBN(0x5719cf31, 0xd52ccc5b),
+     TOBN(0x6c7361f0, 0x3adf9517), TOBN(0x1e264169, 0xabe20ff5),
+     TOBN(0x01f9d997, 0x69eacc0e), TOBN(0x721eba63, 0xc2e635d2),
+     TOBN(0x4225e9c8, 0x25df8bb5), TOBN(0x931f721e, 0xb5752d7e),
+     TOBN(0x3c4ed475, 0x0a3b281d), TOBN(0xcf927682, 0x4a4668be),
+     TOBN(0x1b7f358e, 0x75b7e90c), TOBN(0x06e5c24d, 0xb7a29b9a),
+     TOBN(0x0058967a, 0xa167f2c8), TOBN(0x9f1a6fb9, 0xa4ee62d3),
+     TOBN(0xca899c4f, 0x278291f1), TOBN(0x69a90324, 0xf4e64c1d),
+     TOBN(0x46cc5d42, 0x8d62916e), TOBN(0x3c802e65, 0xec1007cc),
+     TOBN(0xdadcf2aa, 0x6219cfbb), TOBN(0x942870dc, 0xd10258b2),
+     TOBN(0x77264e68, 0xa5e142af), TOBN(0xf25675e2, 0x089cc7a3),
+     TOBN(0x177e8a3b, 0x7336aa16), TOBN(0x5a92cc2d, 0xbc5c622c),
+     TOBN(0x33a35a2c, 0x1789e029), TOBN(0x6f91306e, 0x4e4d5573),
+     TOBN(0xe5a2a581, 0xda0a46f5), TOBN(0xfb532bed, 0x42640bba),
+     TOBN(0x88ff0f11, 0x4a7b3ae4), TOBN(0x2223e7b6, 0xb8ff7a71),
+     TOBN(0x75933133, 0x5d21d261), TOBN(0xa336289a, 0xcabb1fbd),
+     TOBN(0x797db2f3, 0x631b3b61), TOBN(0xc2cedb25, 0xd7e6a511),
+     TOBN(0xb8806f34, 0x10355332), TOBN(0xe5f1fb4a, 0x5d0ae37f),
+     TOBN(0x57cf26a5, 0x5d17c5c7), TOBN(0x82e8df47, 0x68c43ec3),
+     TOBN(0x70fa23eb, 0xf86bd784), TOBN(0x711a9dbb, 0x51b0ce75),
+     TOBN(0x83bb4a90, 0x82170008), TOBN(0x8f096ee9, 0x630602dc),
+     TOBN(0x7d275fc9, 0x7f15e77a), TOBN(0x63516a6a, 0xfe727ec7),
+     TOBN(0x6b06827a, 0x1dce9d38), TOBN(0xa01a5382, 0x023b31c2),
+     TOBN(0x12537433, 0x886209b8), TOBN(0xb7875fa8, 0xc5a11b32),
+     TOBN(0xfa63cb99, 0xbd61176d), TOBN(0xebb204ea, 0x33378ebb),
+     TOBN(0xf29a29a0, 0x70c135f6), TOBN(0xf53941e9, 0xfa29d69f),
+     TOBN(0xab97b39a, 0x9917da42), TOBN(0x4677cfea, 0x45947ae4),
+     TOBN(0xd4668cff, 0x0f6dd908), TOBN(0x48bb09ed, 0x987e0769),
+     TOBN(0x794ed298, 0x8d64b6fd), TOBN(0xaf89d530, 0xfac845da),
+     TOBN(0x57445649, 0x2d06e70d), TOBN(0xe2a1a8c2, 0x079e70a7),
+     TOBN(0xd2ef1779, 0xf524fc03), TOBN(0xeaccaacc, 0xb137bb1b),
+     TOBN(0x34d8ed87, 0x5d279f42), TOBN(0x4dd5344c, 0x1cd68904),
+     TOBN(0xb558b71d, 0x24abd550), TOBN(0x3728e850, 0x40df135e),
+     TOBN(0x9329e2b2, 0xcfe86519), TOBN(0x48ad17fb, 0xac74cde2),
+     TOBN(0x2ad61b22, 0x30b388b5), TOBN(0xebcbc1ad, 0xfaea71e1),
+     TOBN(0x50d7b19e, 0x35990d9d), TOBN(0xb17138e5, 0x6eb70243),
+     TOBN(0xb61618f6, 0xaa8ae7e6), TOBN(0xedee15b0, 0xabce12c6),
+     TOBN(0xa99ce250, 0xcc7205fc), TOBN(0xe438efc9, 0x69e0d75c),
+     TOBN(0x1feb6a10, 0x5084b390), TOBN(0x7b348954, 0x9c80d42d),
+     TOBN(0x67ac89d5, 0xe4b68140), TOBN(0x34afd23b, 0xc9b092af),
+     TOBN(0xad65cae9, 0xfe1ad661), TOBN(0x4f402947, 0xe02d884c),
+     TOBN(0xd0a48fcc, 0x6b1c40c1), TOBN(0xf950c9f7, 0x8961487b),
+     TOBN(0xdb1cd811, 0x206d1752), TOBN(0x863b0ded, 0xe99fd891),
+     TOBN(0xd3aad8c2, 0xbb2a4515), TOBN(0xc93c8cb8, 0x797e223c),
+     TOBN(0x0f471e49, 0x12a77d46), TOBN(0xa2ac9434, 0x600872b6),
+     TOBN(0x6fb1c7ef, 0x915f730b), TOBN(0x9fb72401, 0xd254d363),
+     TOBN(0xf521e33a, 0x6b44796a), TOBN(0xb7ed2e8d, 0x97c9fafb),
+     TOBN(0x60d41128, 0xffb5e7ce), TOBN(0xdbd8b542, 0xaecb96c2),
+     TOBN(0x029ab3dd, 0x0b5ca788), TOBN(0x8b1148a2, 0x190eb38c),
+     TOBN(0x59048db8, 0x69fb1924), TOBN(0xcd2149f0, 0xb18391a8),
+     TOBN(0x6bece5b6, 0xfed311b9), TOBN(0x5edbe9b9, 0x9ffd29b9),
+     TOBN(0x53810556, 0x1156ded2), TOBN(0xf812ce5d, 0x721f3e68),
+     TOBN(0x50504d40, 0x7ccdc8cb), TOBN(0xb559ba08, 0xc60fa4fc),
+     TOBN(0x862a83d9, 0x1d6bd879), TOBN(0x2f8f653b, 0x836e26ba),
+     TOBN(0x8587e6df, 0xeb26ca11), TOBN(0x127bd905, 0x8c8aaf7b),
+     TOBN(0xe26e690d, 0xd67d9902), TOBN(0x1a6061f4, 0xb58e7e78),
+     TOBN(0x960ef741, 0x480dd4d1), TOBN(0x7fd09736, 0x75589610),
+     TOBN(0x5a20a1a2, 0x855a8b2b), TOBN(0x3ed68662, 0x355b4e0f),
+     TOBN(0xd3786f45, 0xe76595b4), TOBN(0x72a6999d, 0x0bdedcfb),
+     TOBN(0x4e48e843, 0x6a175646), TOBN(0xde53c427, 0xe05dc02d),
+     TOBN(0x9728a4c5, 0x97d31bc6), TOBN(0x01a07129, 0x5bb3bd37),
+     TOBN(0x83c08a98, 0xa74a0fcc), TOBN(0x233e400f, 0xbc345df8),
+     TOBN(0x9578c5f2, 0xcc3e0edb), TOBN(0x0fe89df2, 0xf144a31f),
+     TOBN(0x308098a0, 0x14c5a2cc), TOBN(0xeda5a59d, 0xba40c0bc),
+     TOBN(0x0b10f7e0, 0xb718a5ae), TOBN(0xdaf7da8c, 0x5b8ad9ba),
+     TOBN(0xddc71285, 0x87394cde), TOBN(0x9bdb27cd, 0xe43458d3),
+     TOBN(0xc698d972, 0x4bd7c11c), TOBN(0x2ee97fbc, 0x3540be14),
+     TOBN(0x2c704995, 0x72f98422), TOBN(0xfc71fee2, 0xef8661c5),
+     TOBN(0x6574e022, 0xce08043e), TOBN(0x3d17162e, 0x5143733c),
+     TOBN(0x3bf0b448, 0x730e5b80), TOBN(0x56de346a, 0x7cf94b5e),
+     TOBN(0xfa87a53e, 0x6c797797), TOBN(0xe8b9edfa, 0x6487d014),
+     TOBN(0x09e74387, 0x7be60b03), TOBN(0x2277ebc3, 0xec8750db),
+     TOBN(0xf1e9d594, 0x7aeaa545), TOBN(0x41564562, 0x44c03394),
+     TOBN(0x57943adc, 0x4de9f7ec), TOBN(0x09dd58f9, 0x2a220cd5),
+     TOBN(0xdf848ec8, 0x06973808), TOBN(0xf1d5def1, 0xd3950024),
+     TOBN(0xd089eba5, 0x5a8707e5), TOBN(0x914046cb, 0xb0b90ebe),
+     TOBN(0xb01180b2, 0x63fe6bc2), TOBN(0x1ffbc968, 0x7ede9d83),
+     TOBN(0xe16d336f, 0x3c52c09f), TOBN(0x32270ecb, 0xdf40338d),
+     TOBN(0xb55ff5c6, 0x7eec7039), TOBN(0xb5ffb314, 0x38a63fab),
+     TOBN(0x3e9f284f, 0xee18ffec), TOBN(0x702d97f5, 0x1d1b4e80),
+     TOBN(0x2005ee57, 0x214c4da1), TOBN(0x1c210413, 0x2f5ea2f4),
+     TOBN(0xd24a486c, 0xa4149949), TOBN(0x3869a339, 0x23c8e201),
+     TOBN(0x00f6e410, 0x0149992e), TOBN(0x54e97b46, 0xf0a367dd),
+     TOBN(0xd967726c, 0xe169de5c), TOBN(0xa3e81f93, 0x6a0fcd03),
+     TOBN(0x171faa9d, 0xdb1b1733), TOBN(0x0bbb5e91, 0x3828e41b),
+     TOBN(0x789a7b2e, 0xf0828387), TOBN(0x9465cc16, 0xfca60b9b),
+     TOBN(0xcb58e90a, 0xab630d23), TOBN(0xe7d30293, 0xa339d4b4),
+     TOBN(0x0bcac958, 0x18e75428), TOBN(0xd2f1554a, 0x9a95900a),
+     TOBN(0xc63c2afb, 0x03846833), TOBN(0x703d0220, 0x6d1e8753),
+     TOBN(0x47f5fe57, 0x04a357a2), TOBN(0xaafba53e, 0xcdc17255),
+     TOBN(0x8f94c8eb, 0x821af8d5), TOBN(0x4d9918bc, 0x35e37920),
+     TOBN(0xc029bd84, 0xe32dd067), TOBN(0x25982357, 0xf77f8962),
+     TOBN(0x7af256ca, 0x510b7cfb), TOBN(0xca397f37, 0x446925d7),
+     TOBN(0xb3dc7be5, 0xe0614e1e), TOBN(0x3b64cd27, 0xbbc4cc93),
+     TOBN(0xbd762df5, 0xfb99bbc9), TOBN(0xc1ef0d4d, 0x04d7177f),
+     TOBN(0x77b6d3d6, 0x65e75ed6), TOBN(0xbe59c8da, 0x53053b45),
+     TOBN(0x054d089f, 0x54fe92cc), TOBN(0x2f2327e0, 0x6fa4758f),
+     TOBN(0x948cf03f, 0x5d2b5a01), TOBN(0x47544c4c, 0x9c23b83e),
+     TOBN(0x338590fa, 0x6c128d69), TOBN(0x5724008d, 0x76842160),
+     TOBN(0x4cbeb18e, 0xc4f063e6), TOBN(0x507ba094, 0x9c2f826c),
+     TOBN(0x0e877a6e, 0x6f4e49f3), TOBN(0x050c2040, 0x34f56868),
+     TOBN(0x8fd667c4, 0x0f119e25), TOBN(0x881dd34d, 0x13b47d3f),
+     TOBN(0x2a6b636d, 0xca8e9a6a), TOBN(0x67b081fb, 0x107ea18c),
+     TOBN(0xd3a46367, 0x84af55d9), TOBN(0x0e709a00, 0xd7626b67),
+     TOBN(0x135725fa, 0x4c6dfc8e), TOBN(0xbf6328d9, 0x133a6e20),
+     TOBN(0xa4b743b4, 0xa87031f2), TOBN(0x62e90e67, 0x13825d07),
+     TOBN(0x7004028e, 0xb85f3c45), TOBN(0x0465c502, 0x11751be0),
+     TOBN(0x4d5d467f, 0x8a5ab076), TOBN(0x9aa3f414, 0xf4fb8a45),
+     TOBN(0x9fa0422e, 0x5dc1fa84), TOBN(0x3205c05f, 0xd02cfd2b),
+     TOBN(0x3eac28fa, 0x078836b6), TOBN(0x53bc0189, 0xfc3ff573),
+     TOBN(0x2c45ef09, 0x00b02100), TOBN(0x61bc02ae, 0x34360ef7),
+     TOBN(0xeb5593e7, 0x532e8d6a), TOBN(0x94092904, 0xf02a1ee4),
+     TOBN(0x379b32e8, 0x200496c0), TOBN(0x46fb6e9e, 0x360a27c8),
+     TOBN(0x8a3377ba, 0x62005158), TOBN(0x1a3266c1, 0x0de3f191),
+     TOBN(0xe60fad96, 0xc94d2127), TOBN(0x41553dd1, 0x646302f3),
+     TOBN(0x88bf0bfa, 0x377e0766), TOBN(0x870a24db, 0xe75bf57b),
+     TOBN(0xc133cb49, 0x79e77976), TOBN(0x2f14924d, 0xf43b6f18),
+     TOBN(0xe561dc90, 0xaa94cd73), TOBN(0x8c420eb2, 0xd6eb695d),
+     TOBN(0x99e41ba8, 0x2f04ef79), TOBN(0x7f427bdf, 0x71e6d054),
+     TOBN(0x7304bb25, 0x10bde8ce), TOBN(0x5dbc4325, 0xe48b16f8),
+     TOBN(0x47d17ab2, 0x8796db7b), TOBN(0x83426817, 0x94c77832),
+     TOBN(0x6781850e, 0x9878ace2), TOBN(0x7f747b90, 0x019e97aa),
+     TOBN(0xa0545c85, 0x949f9b08), TOBN(0xe0a0bbf8, 0x244bc083),
+     TOBN(0x8cb53666, 0xa1f38ea6), TOBN(0x9be29ff6, 0x4989a568),
+     TOBN(0xbc5a7f87, 0x083a7fcd), TOBN(0x90d0129c, 0x44ca10f6),
+     TOBN(0x1ad274bb, 0xd724b7e2), TOBN(0xa5290cbd, 0xcad5f069),
+     TOBN(0x886b1a7c, 0x86a4e0a9), TOBN(0xd2481b5a, 0x8d8fb13f),
+     TOBN(0x80075fb2, 0x4f606ac5), TOBN(0xf984b5a2, 0xbfc10e7f),
+     TOBN(0xd3d91aea, 0xf056142f), TOBN(0x770bee0b, 0x4afdc017),
+     TOBN(0x3c42ca88, 0x6c49c827), TOBN(0xb620c4e8, 0x0aaa3139),
+     TOBN(0xac6b512d, 0xad87890c), TOBN(0xaee62df7, 0x0eb61f92),
+     TOBN(0xcf0f37fc, 0x21dad9ec), TOBN(0xd780d315, 0xc52e24c1),
+     TOBN(0x0263bcab, 0x23a2b699), TOBN(0xdc8dcd2f, 0x9714b577),
+     TOBN(0xeb16eca8, 0x55622b11), TOBN(0x94b3d649, 0x01118edf),
+     TOBN(0x6bafea64, 0xec66879d), TOBN(0xc35739c0, 0xc4ab9f48),
+     TOBN(0x082ccf53, 0xf3232370), TOBN(0x01b55dd3, 0x71407825),
+     TOBN(0x86e0fe94, 0x4f7f4038), TOBN(0x1a623675, 0xb04159e9),
+     TOBN(0xf908ca59, 0xbc4df337), TOBN(0x1b4f1ffe, 0x816162ce),
+     TOBN(0xb5128952, 0x2d60e05b), TOBN(0xb47ca0eb, 0xd38cbdf7),
+     TOBN(0xdccba22f, 0x8ee38219), TOBN(0xc9436453, 0x9fbb36ff),
+     TOBN(0x83cecbf5, 0x8ac12c9d), TOBN(0x591191b5, 0xf4cb1ebf),
+     TOBN(0x693cf383, 0xf03c1632), TOBN(0xaebd3f9b, 0xcb6abaca),
+     TOBN(0x1427c154, 0x0fa26e7a), TOBN(0x4f0de894, 0x01bf37af),
+     TOBN(0x4e497acf, 0xd88da2a6), TOBN(0x8014a215, 0xe5c86482),
+     TOBN(0xa627d78f, 0xcf94ee40), TOBN(0x7647708d, 0x9ca36aa3),
+     TOBN(0x3d4e8bb1, 0x87e108c8), TOBN(0xacdc3223, 0x516f8b28),
+     TOBN(0x74e4d436, 0x1d955038), TOBN(0x7e4a198c, 0xedd68905),
+     TOBN(0x41dc4bdb, 0xc4bfbad1), TOBN(0xfd1121b1, 0x85964942),
+     TOBN(0xe462eb9c, 0x0c0d85ff), TOBN(0xade1ccb3, 0x2b012d88),
+     TOBN(0x2eec3503, 0x318f2aa3), TOBN(0x656b44da, 0xdec8a13e),
+     TOBN(0x90b7aac8, 0xcda13a8c), TOBN(0xe7f3a5ff, 0x214a530f),
+     TOBN(0xa0c8062a, 0x159060b9), TOBN(0xc19f2608, 0xd9305873),
+     TOBN(0x0d6213c4, 0xc9609521), TOBN(0xde2b9134, 0x9aec4539),
+     TOBN(0x4a2a6476, 0xaeddf0a6), TOBN(0x89222dff, 0x5cf2e85d),
+     TOBN(0xad92a1d3, 0x084a0245), TOBN(0x29644a60, 0x2138063f),
+     TOBN(0x5b57a05b, 0xb8164832), TOBN(0xecf6620f, 0x885ce4d1),
+     TOBN(0xde28ed2f, 0x045d3b88), TOBN(0x3eb11759, 0xb4502308),
+     TOBN(0xe97f1db2, 0x4d9f94b8), TOBN(0xfa248583, 0xeb83f016),
+     TOBN(0x63a273b4, 0xcda53ba0), TOBN(0x9692973a, 0xa228b7b9),
+     TOBN(0x5968cb12, 0xb6707cbd), TOBN(0x1895ccb4, 0x5c1a2b4d),
+     TOBN(0xff309157, 0x37f0b240), TOBN(0x374d983e, 0xb90875c2),
+     TOBN(0x22fc40c6, 0xc4e815e7), TOBN(0xf2db27be, 0x98d18506),
+     TOBN(0x2854a948, 0xaa9ae62e), TOBN(0xd614a722, 0x79e39df1),
+     TOBN(0xebeec551, 0xb3501c19), TOBN(0xe2b878eb, 0xd89cefca),
+     TOBN(0xa0a34757, 0x6b4cd6bc), TOBN(0x0159129c, 0x70bfdf88),
+     TOBN(0x26fa3e53, 0x489502ca), TOBN(0x7932084f, 0x285be050),
+     TOBN(0xfe4998f4, 0x71912b0c), TOBN(0x3dce0a87, 0xc60b88b7),
+     TOBN(0x5b93edb0, 0xc718ee5c), TOBN(0xb93a225f, 0xbb075836),
+     TOBN(0x87a08c94, 0x7aa0595c), TOBN(0x401d2620, 0xc31e3249),
+     TOBN(0xbe6928b4, 0xdae2cdb9), TOBN(0x4b68e106, 0x5a226630),
+     TOBN(0xdc38c2fc, 0xc9d32e4f), TOBN(0xc51a6245, 0x26542f89),
+     TOBN(0x5fb37c1b, 0x1757f3c4), TOBN(0xa27d6c02, 0x89128aa4),
+     TOBN(0x3b74f56f, 0x5e629309), TOBN(0x24b5ad84, 0x2f7aeef2),
+     TOBN(0x54a962cc, 0xbdc89596), TOBN(0x6e8bccf8, 0xcc2f3d5d),
+     TOBN(0x4c1df22c, 0x312e9241), TOBN(0x8ffe6b0d, 0xfc30f0dc),
+     TOBN(0x670431a2, 0xa6ec0fe4), TOBN(0x49da0b42, 0x35964572),
+     TOBN(0xbb12d1b0, 0x9dda5c39), TOBN(0x64170fe1, 0x72d3de51),
+     TOBN(0xea8b2b16, 0xa4a2f5d9), TOBN(0xde1bad64, 0xe590be92),
+     TOBN(0xb7f93581, 0xf0b9b0b5), TOBN(0xb007f4dd, 0xe115d67e),
+     TOBN(0xab9d7523, 0x415732d4), TOBN(0x2951149d, 0x905ec0fe),
+     TOBN(0x94bb2c63, 0x74350478), TOBN(0xe6b63bfd, 0xe9b1ada4),
+     TOBN(0xd09b4d4b, 0x13e8528f), TOBN(0x6bed3d25, 0x685bf239),
+     TOBN(0x83023ad9, 0x1a14b7ce), TOBN(0x4bffff63, 0xd0505d6b),
+     TOBN(0x2ccc180a, 0x8bb1cfa0), TOBN(0x70c18508, 0x3a09c912),
+     TOBN(0x318c41c2, 0x5878201c), TOBN(0xb9f207b1, 0x64c01149),
+     TOBN(0x89fdd9ee, 0xc58287d9), TOBN(0xdb6fa8db, 0x05c43da0),
+     TOBN(0xc31354f0, 0x311a34a1), TOBN(0xccfbaddb, 0xf1521976),
+     TOBN(0xf4175f75, 0x0c5e8388), TOBN(0x7e090ce8, 0xa81226cf),
+     TOBN(0x5209107e, 0xeda6a1ab), TOBN(0xf0a263cb, 0x7e5ccefd),
+     TOBN(0x9fe05a36, 0x10c704a1), TOBN(0x3784d7ca, 0xcf133aee),
+     TOBN(0x066c3116, 0x37af86c8), TOBN(0xbf32ca04, 0xd7ebeb8a),
+     TOBN(0x0447a950, 0xa9b5bab4), TOBN(0x3b2f98bd, 0x41bb1f1c),
+     TOBN(0xd89bbdd7, 0x59c62f08), TOBN(0x26bab370, 0x3ded069b),
+     TOBN(0xb0db4ca5, 0x69ea63ae), TOBN(0x57b32f32, 0x9133df68),
+     TOBN(0xc79a22d0, 0x5a17efbe), TOBN(0x576976a3, 0xf8ae3c2d),
+     TOBN(0x5754198b, 0x9d02d212), TOBN(0x9cc9e61e, 0x230d0213),
+     TOBN(0x76772179, 0x92889e33), TOBN(0xb1246608, 0xf5df6cba),
+     TOBN(0x821766bc, 0x8d491280), TOBN(0xe148f470, 0x96bd3df5),
+     TOBN(0xc1e9fc70, 0xed753b73), TOBN(0x840e40ed, 0xd6cecfc5),
+     TOBN(0x03874679, 0x93e2f3a0), TOBN(0x462e5abf, 0x5b646b64),
+     TOBN(0x6fb19eda, 0xd7ae0e67), TOBN(0x01e8a27f, 0xc3d2dddf),
+     TOBN(0xc9e69639, 0x4bacfe2d), TOBN(0xbc3a134e, 0x712e8afb),
+     TOBN(0x5d943a86, 0x8af6d30f), TOBN(0x65eb5f99, 0x443c942c),
+     TOBN(0xf5000308, 0x2339e348), TOBN(0xd69b7693, 0xeb0d80e6),
+     TOBN(0x7b00b43b, 0x5b9d220b), TOBN(0xde0dfc80, 0x497bbcf9),
+     TOBN(0xcfe2e3f3, 0x0c2e851f), TOBN(0xef7793d1, 0x7e91d378),
+     TOBN(0x9e43eeac, 0x9d266a5b), TOBN(0x9c81d68b, 0x1766c5c1),
+     TOBN(0x121db320, 0xf6a4d560), TOBN(0xcd0a4f03, 0x073582a7),
+     TOBN(0xbf469f9a, 0x6e841041), TOBN(0x4314f0f6, 0x5eb2d753),
+     TOBN(0x09021001, 0x8c498414), TOBN(0xf63d00ee, 0x859275b7),
+     TOBN(0x228fa809, 0xf1c0385a), TOBN(0x44259d51, 0x694c3318),
+     TOBN(0xb0a5d5fe, 0xa2ad4eac), TOBN(0xbb950010, 0xabdedf11),
+     TOBN(0x081024ce, 0x6264c601), TOBN(0x6cc7faf2, 0xaefb4115),
+     TOBN(0x90511289, 0x8692a7a4), TOBN(0x2bef4135, 0x60596012),
+     TOBN(0xfec5209a, 0x0f0becb6), TOBN(0xad0907a6, 0xd1ceb42e),
+     TOBN(0x6cde3f21, 0xf517372c), TOBN(0x9acd4f09, 0x26131459),
+     TOBN(0xf3629a43, 0x491f59d7), TOBN(0xe944561a, 0x41a037dd),
+     TOBN(0x07beeabe, 0x826d1b5c), TOBN(0x0728a907, 0x3a1476cd),
+     TOBN(0xa91f41a0, 0x7d4a1abf), TOBN(0xdf58ed06, 0xa7a119c4),
+     TOBN(0x19990669, 0xba701704), TOBN(0x8aa3f76b, 0x47b67175),
+     TOBN(0x8bccff3e, 0xdd0a6e9a), TOBN(0x4173fcda, 0x24f49029),
+     TOBN(0x2a688911, 0x61c18233), TOBN(0xdf54b239, 0x78b9fa8f),
+     TOBN(0x714cf627, 0x37596f40), TOBN(0x2c73ddba, 0x24e6a879),
+     TOBN(0x1538fd36, 0xf2547f19), TOBN(0xd85c4730, 0x0e7e84ee),
+     TOBN(0x00799e9f, 0x306f5fc0), TOBN(0xfccc6a37, 0x49ce114c),
+     TOBN(0xf9cff5e8, 0x3fe08120), TOBN(0xdf876a1f, 0xc2be9f27),
+     TOBN(0xe817c72e, 0x6939fdb9), TOBN(0x244a1800, 0xd34d0e43),
+     TOBN(0x41e83eef, 0x78fa7f11), TOBN(0xecaa250c, 0xba6367e5),
+     TOBN(0x9c420347, 0x8def6ae6), TOBN(0x99efb3b1, 0x250b9e58),
+     TOBN(0xdaf311ee, 0x79b2298c), TOBN(0xb49200cf, 0x69b6dff3),
+     TOBN(0x5c7f17bb, 0x559e51f5), TOBN(0x117d0cbe, 0x424be7e9),
+     TOBN(0x290a35c4, 0x36e3af54), TOBN(0xd2326cd8, 0xe3a643b1),
+     TOBN(0xc208b2b3, 0x3580f9ee), TOBN(0x2419c661, 0x4464a9e0),
+     TOBN(0x87123d3a, 0xbccb2759), TOBN(0x5d36fcf3, 0x1a77d469),
+     TOBN(0x5aafd58a, 0x49b07e5a), TOBN(0xf534595b, 0x6b71e237),
+     TOBN(0x0f0d3161, 0x6705039f), TOBN(0x7282b08c, 0xca701676),
+     TOBN(0xb05e8c3e, 0x13796941), TOBN(0x5250012e, 0xfca06e08),
+     TOBN(0x7eb2341a, 0x980c5ea3), TOBN(0x92f5aeb1, 0xa41defb2),
+     TOBN(0x203244e0, 0x0e62453f), TOBN(0x74341218, 0x96181756),
+     TOBN(0x3b0cd36f, 0xe12a94ab), TOBN(0xf364b3b9, 0xb5ad7c48),
+     TOBN(0x96a7a2a7, 0x8e768469), TOBN(0xccc31c7e, 0x1bbc7cc5),
+     TOBN(0xe70ad5d0, 0x080dbb92), TOBN(0xfb201e92, 0x56fb0f1f),
+     TOBN(0xdfce7a1e, 0x29d99f57), TOBN(0xc12a02b0, 0x06457da5),
+    },
+    {
+     TOBN(0xdea72ba6, 0x2a80f39c), TOBN(0xcb2174b1, 0x68cbe088),
+     TOBN(0x9c73ec69, 0xd6bd1cc1), TOBN(0x6a2dbe20, 0xf20dcce6),
+     TOBN(0x20a5150b, 0xeeaae9da), TOBN(0xc935e85d, 0x9df630da),
+     TOBN(0x2147144f, 0xa1634cd8), TOBN(0x5eccb56c, 0x44f3af02),
+     TOBN(0xf77a79cf, 0xc0e2b70a), TOBN(0x2569c8bc, 0xee8cbae7),
+     TOBN(0x392a5dbe, 0xfadb18fc), TOBN(0x59bc96b4, 0x3ce6a0ff),
+     TOBN(0x287f04f4, 0x8b551005), TOBN(0x7efe3aa5, 0xa44b2bd8),
+     TOBN(0x0e9cb8ed, 0x6ac447d7), TOBN(0x9b4eb10a, 0x7783bdd0),
+     TOBN(0x793c4880, 0xb981d96f), TOBN(0xf719d828, 0xd56fb2a6),
+     TOBN(0x9fcc236f, 0x8149057e), TOBN(0x318c63ec, 0xb4d65c2b),
+     TOBN(0x5269c1d7, 0x5f95c5de), TOBN(0x33b3745f, 0xd0efd6bc),
+     TOBN(0xace344d5, 0x4ae87c64), TOBN(0x238809d6, 0xdd30ba2c),
+     TOBN(0x3cc32acc, 0x71192348), TOBN(0x545401bf, 0x3f17ef60),
+     TOBN(0xe209a493, 0xcde25b0e), TOBN(0x5c11886b, 0x663abab9),
+     TOBN(0xe61a81b1, 0x28ec7c90), TOBN(0x18b125a6, 0x75b57f5c),
+     TOBN(0x86d1b45a, 0xfad91696), TOBN(0xb31a786d, 0xa4c3f7ff),
+     TOBN(0x2fd4cd72, 0xf45831d8), TOBN(0x85978fa6, 0x8be40d9f),
+     TOBN(0x38106329, 0xa9301111), TOBN(0x1527e462, 0x9e5979ee),
+     TOBN(0x97f71c7e, 0x76c5fc8a), TOBN(0x205fa473, 0xf1f33056),
+     TOBN(0x7bb9d24e, 0xa6546a05), TOBN(0x0e282a5c, 0xf84c4d35),
+     TOBN(0x59d21896, 0x59471f1f), TOBN(0x2e613dec, 0xf6303573),
+     TOBN(0xa7995794, 0x78bf5a4b), TOBN(0x20adf6b5, 0xbf19fbe5),
+     TOBN(0x3a48c95f, 0x1574d34d), TOBN(0x95488f09, 0x09323ceb),
+     TOBN(0x450aee7f, 0x552df9cf), TOBN(0xdf016f7a, 0x53557500),
+     TOBN(0xf2acedc6, 0x2da8a2a6), TOBN(0x03fc8cf8, 0x2f4a0632),
+     TOBN(0xe7ff136b, 0x5b82f03a), TOBN(0xd5841c4d, 0x9e88c421),
+     TOBN(0x75a4d66f, 0x7eef63f0), TOBN(0x92091ade, 0x2865c14b),
+     TOBN(0x7060474c, 0x64fe7ba3), TOBN(0x4056724c, 0xfe30cb3e),
+     TOBN(0x38cf4c6f, 0x8d9fceb6), TOBN(0x11e85f78, 0xab528f38),
+     TOBN(0xe2896d25, 0x52303b2b), TOBN(0xf929675a, 0xed68c605),
+     TOBN(0xfbd22374, 0x10c708a9), TOBN(0x4682ca17, 0x40d7e5a7),
+     TOBN(0x4242b5c5, 0x9041047f), TOBN(0xaf571053, 0x0f9c6840),
+     TOBN(0x713b2bbb, 0xa56af433), TOBN(0x45aaf2ce, 0x5e82f947),
+     TOBN(0x9882571a, 0x106283c7), TOBN(0x37de12ca, 0x9b9c3c3c),
+     TOBN(0xcb463af2, 0xbef10529), TOBN(0xe18d763d, 0xd771236c),
+     TOBN(0xb47a69ca, 0x62935de3), TOBN(0x4798e91f, 0x9a41e09a),
+     TOBN(0x89696697, 0x8e93edd5), TOBN(0x35cdb8e1, 0xb7ea4f45),
+     TOBN(0x36f8305d, 0xfed33d87), TOBN(0x57623440, 0x625642d6),
+     TOBN(0xdfd9b580, 0x945dd7d6), TOBN(0x965ffcb5, 0x731739bc),
+     TOBN(0x34588e1f, 0x637bf127), TOBN(0x936c0ba0, 0x539d21c7),
+     TOBN(0x70832099, 0x71640eed), TOBN(0x916b1952, 0x3ff407e3),
+     TOBN(0x4cd58881, 0x88440bc0), TOBN(0xd9fcb83d, 0xc280e977),
+     TOBN(0x0d3df9db, 0xdf6cda83), TOBN(0xc629414e, 0x3d55047e),
+     TOBN(0xe05738a8, 0xc16b84c9), TOBN(0xf4bdc724, 0xe8783570),
+     TOBN(0x7d876a59, 0x9a93a5c9), TOBN(0x026be75c, 0xa8d12f61),
+     TOBN(0xe49900ed, 0xe9b2aa43), TOBN(0x44d6dc80, 0xb3a68dad),
+     TOBN(0xf96b116b, 0x7d23e11b), TOBN(0x12791212, 0xb6814209),
+     TOBN(0x3e2807cf, 0x6cc65956), TOBN(0xcc606ca7, 0xf405ffae),
+     TOBN(0x5484b2c5, 0x5df47226), TOBN(0xfbaf9042, 0x8802da81),
+     TOBN(0x84146315, 0x087adadc), TOBN(0x6adbcbc1, 0x58d593b3),
+     TOBN(0xc1fb3896, 0x68b97533), TOBN(0xa6919aac, 0x954cc1b7),
+     TOBN(0xf301b2e4, 0x27a4ddd0), TOBN(0xa15c16eb, 0xdf1a07b1),
+     TOBN(0xb36c017d, 0xc145a6c7), TOBN(0xcca64382, 0x968798da),
+     TOBN(0xd13b6376, 0x8d0eff5d), TOBN(0x06e39e2d, 0x2206e681),
+     TOBN(0x1d9dffa4, 0x3add517a), TOBN(0xe670e6c8, 0x10d95fef),
+     TOBN(0x0ecb51ab, 0xf1c7c7a8), TOBN(0xf17dff51, 0x74945305),
+     TOBN(0xf71b09b1, 0xb00d9c0f), TOBN(0xc719cf62, 0x9c72c80a),
+     TOBN(0x310c5aeb, 0xe00a49a1), TOBN(0xd01285e5, 0x1b33c5e6),
+     TOBN(0x7b23e7c7, 0x05aa6eb7), TOBN(0xf84188b1, 0x6bc88677),
+     TOBN(0x7e034cb5, 0x64be321a), TOBN(0x270df734, 0xe884323f),
+     TOBN(0x218e68f9, 0xe5137d20), TOBN(0x79588cba, 0x0f7e70ad),
+     TOBN(0xb6d37f52, 0x58b86b0a), TOBN(0xcb281c98, 0x7cc591fe),
+     TOBN(0x30e03fed, 0x8fe40e06), TOBN(0x394ded95, 0xed9ca793),
+     TOBN(0xf1d22cdd, 0xbcd3a3da), TOBN(0xcb88cb27, 0x0c591130),
+     TOBN(0x67786ba3, 0x8ff0cbf4), TOBN(0x85738a44, 0x0565d337),
+     TOBN(0x9d3b35ec, 0xaf9a667b), TOBN(0x45a17512, 0x8f5f540a),
+     TOBN(0xf1ae5171, 0xade5a5ba), TOBN(0x720e2823, 0x39869be4),
+     TOBN(0x6893f14a, 0x5352d84b), TOBN(0x919a4f15, 0xc784dc20),
+     TOBN(0x36136612, 0xf7ae5a67), TOBN(0x11f43d1c, 0xeaff0924),
+     TOBN(0xcfee088c, 0x39449b96), TOBN(0x3dc48359, 0x70c42ff6),
+     TOBN(0x4072683a, 0xbf208b67), TOBN(0x35a7c2be, 0xbe15d18f),
+     TOBN(0xe61d2748, 0xe2c3391b), TOBN(0x0a4109b1, 0x39921d95),
+     TOBN(0xe2cd131b, 0x05545cfc), TOBN(0xa898da2c, 0x3ae20d7f),
+     TOBN(0x501cd849, 0x50dc4c61), TOBN(0x10e287d4, 0x3374e7f0),
+     TOBN(0x90d6326f, 0x38aea879), TOBN(0xc48d9af7, 0xef43fa08),
+     TOBN(0xf8f4886a, 0x6c20f168), TOBN(0xc5d34a86, 0x23ccac4b),
+     TOBN(0x72357752, 0xb3d7004e), TOBN(0x167db0ed, 0x817bd377),
+     TOBN(0x5d45b3da, 0xdfb64d05), TOBN(0xed4b7fc4, 0xf0b414ac),
+     TOBN(0xc178941b, 0x0bf1dd64), TOBN(0x43feac17, 0x8fe835a5),
+     TOBN(0xe1c23a17, 0x6a014609), TOBN(0x63255991, 0xd5e23bd5),
+     TOBN(0xefc76468, 0xd7dfec55), TOBN(0xb1bc3fee, 0xc0831696),
+     TOBN(0x0996811b, 0x5f52433b), TOBN(0x6b8b6daa, 0x799649fc),
+     TOBN(0x6e9f7cb6, 0xab518b64), TOBN(0x6a67a009, 0x38a3a2ab),
+     TOBN(0xe55de954, 0x928209e2), TOBN(0x98b6d0a7, 0x3da81142),
+     TOBN(0xdec30331, 0xe3f832e8), TOBN(0xa9b77f3b, 0xe50fa9e3),
+     TOBN(0x20febc21, 0x5167c6a6), TOBN(0x0ce07d1a, 0x76fb0f13),
+     TOBN(0x9745dead, 0xe796f8a3), TOBN(0x2cb4eb1f, 0xd95deba6),
+     TOBN(0x062e7cac, 0x4caf2afe), TOBN(0xf50ce065, 0x16ace879),
+     TOBN(0xdec8954b, 0x1d99d3e7), TOBN(0x5287e954, 0xa48262c7),
+     TOBN(0x1c6fbd17, 0xcc3530de), TOBN(0x6bcbea50, 0x53af4211),
+     TOBN(0xe3533bca, 0x4dce058d), TOBN(0x6fe62e64, 0xfc9cdf00),
+     TOBN(0xee29fdec, 0xe8ec4cf9), TOBN(0x7361797d, 0xc8d52f80),
+     TOBN(0xb1d858da, 0xf4e36023), TOBN(0x4a1282ce, 0x73e6dee1),
+     TOBN(0x6ba8f8ba, 0xce1d71cc), TOBN(0xf5b7d6b4, 0xcbbd8eb9),
+     TOBN(0x60f8bd50, 0x5aed382b), TOBN(0x47b40519, 0x3f3a46b1),
+     TOBN(0xaed13bb9, 0x8a997d93), TOBN(0x6cc2260e, 0x4dc6e35d),
+     TOBN(0x173bfcdd, 0xccf915d9), TOBN(0xad4525e1, 0xc2d46f6e),
+     TOBN(0xb7ecec0b, 0xcdd2382b), TOBN(0x01ae8291, 0xd2b76c84),
+     TOBN(0x2d1e2a91, 0xbec6b980), TOBN(0x1b0040be, 0x7008a00c),
+     TOBN(0x6ac708d7, 0x7d555120), TOBN(0xa6017568, 0x0d745eef),
+     TOBN(0x735e3511, 0x1ed38ef4), TOBN(0x7c97f284, 0xcebe5a8c),
+     TOBN(0xd4059313, 0x24fecbac), TOBN(0xf874ca4b, 0xde18c42c),
+     TOBN(0x9ab736a8, 0xdbb829b6), TOBN(0xe914bdde, 0x82ff128d),
+     TOBN(0x6e949bab, 0xfd0f362b), TOBN(0x275824cf, 0xffea2e79),
+     TOBN(0x81f57245, 0x8cc52417), TOBN(0xed0a9079, 0x2f274090),
+     TOBN(0x98c3372e, 0xfdd0ba2f), TOBN(0x49820f41, 0x3ae99478),
+     TOBN(0x1c47e09f, 0xce373d3e), TOBN(0x875d7920, 0x6dd12846),
+     TOBN(0x7a9e7973, 0x15d5bbb1), TOBN(0x48512656, 0x6d227962),
+     TOBN(0x2c167c88, 0x199241e3), TOBN(0x98c1df6a, 0x7209ca37),
+     TOBN(0x09a1583f, 0xde89e412), TOBN(0xc19ed5b9, 0xc792de48),
+     TOBN(0xb8dd1b1d, 0x74dc0834), TOBN(0x9d458529, 0xa04456e9),
+     TOBN(0x66ef5463, 0xad0ad39d), TOBN(0x8d7df4a1, 0xe641edc5),
+     TOBN(0x97815de2, 0x6bd322e4), TOBN(0x0bf6fc83, 0xc1f77fb3),
+     TOBN(0x49378167, 0x8b4f7152), TOBN(0xfdd476ef, 0xbf0a1712),
+     TOBN(0xe87e1977, 0xf2f9883a), TOBN(0xdbb2fcbf, 0x9ad2d034),
+     TOBN(0x5afdd161, 0x64e1a4c6), TOBN(0x0e43f911, 0x3e435191),
+     TOBN(0xde2d1ee6, 0x42253804), TOBN(0x6def6ceb, 0xdaf13e57),
+     TOBN(0xae09fd4f, 0x05423bab), TOBN(0x6f6c17b8, 0xad46e409),
+     TOBN(0x966fa09d, 0x6c345cd3), TOBN(0x6c8aa1e9, 0x47abc35d),
+     TOBN(0x02991686, 0xe015a566), TOBN(0x39b3aeee, 0xcd2f925d),
+     TOBN(0xf9cda92a, 0x9119c117), TOBN(0x7b21ce82, 0xf4f833e1),
+     TOBN(0x87517bf5, 0xe4f99193), TOBN(0x1b7ddec9, 0x4eb412f9),
+     TOBN(0x7a30dd57, 0x6b077498), TOBN(0xe060625f, 0x0ec44230),
+     TOBN(0x0f355dc4, 0xb0e5446b), TOBN(0xdf324d65, 0xbbd2df28),
+     TOBN(0x28c7eb34, 0x649966a5), TOBN(0x97587f4f, 0x26639e19),
+     TOBN(0x0724cc00, 0x0bce0f38), TOBN(0x63578add, 0x4ae6280a),
+     TOBN(0xf1beaa57, 0xc7fd6a1a), TOBN(0x83b1a533, 0x7b017e35),
+     TOBN(0x01c027e3, 0xefdf2ed1), TOBN(0xf373d4ea, 0xd2d31852),
+     TOBN(0xe568acb6, 0x65b8f5d7), TOBN(0xea8ce1b8, 0x1240a545),
+     TOBN(0xb95b0db2, 0x555fac44), TOBN(0x01d18170, 0x768333dc),
+     TOBN(0xf938b55e, 0xab1798ad), TOBN(0x73a0d9dd, 0x999a7e5b),
+     TOBN(0xd2359bb5, 0x57fd9b51), TOBN(0x20f1d4fd, 0x77fb4e5e),
+     TOBN(0xf5efd71f, 0x932dc388), TOBN(0x40f86819, 0x21a37385),
+     TOBN(0x05395fb2, 0xff935ef3), TOBN(0xc2ee43ac, 0x1b615e8e),
+     TOBN(0xa3bb6518, 0xe82d509a), TOBN(0x3a87d5a2, 0x30b93347),
+     TOBN(0xac0a5ad0, 0x5b130bcc), TOBN(0x91fe8fdd, 0x9154d73a),
+     TOBN(0x677d7d48, 0xdeb203a2), TOBN(0x4d4108fe, 0x8b0168e1),
+     TOBN(0x16be4ad1, 0xddc3d24c), TOBN(0x9b0ea387, 0x9865df69),
+     TOBN(0x16daf932, 0x4c50ec70), TOBN(0xa4799bda, 0x478c96a3),
+     TOBN(0x4ef24d3f, 0x7114d3bb), TOBN(0x30a31509, 0x46e6bbdc),
+     TOBN(0x60137187, 0x97f3cb4b), TOBN(0xf2b66d8f, 0x0a29d865),
+     TOBN(0x93a4a37a, 0x60064a5c), TOBN(0x7dee9bed, 0xe8c3cf47),
+     TOBN(0x748833ce, 0x0b7ee8b8), TOBN(0xc07f2f6d, 0x56f89483),
+     TOBN(0xd71a40d8, 0xd24b406f), TOBN(0xbe3b2e8f, 0xebbb7655),
+     TOBN(0xa23c2b05, 0x4a536998), TOBN(0xdcaf45b3, 0xa9be2e14),
+     TOBN(0x515ad15f, 0xfe346ad1), TOBN(0xb9c06a18, 0xb7847640),
+     TOBN(0x8552eb06, 0xf35bff4a), TOBN(0x4fb792e7, 0x2b7a29f5),
+     TOBN(0x1cce2af5, 0xa41a38b4), TOBN(0xde68bd0d, 0x02b42414),
+     TOBN(0x8124d6e2, 0x7cd66728), TOBN(0x5906d1b4, 0x55efbadd),
+     TOBN(0x7e17855a, 0x827f2741), TOBN(0xab525dfb, 0x12c6966c),
+     TOBN(0x065ae99a, 0x758e0cd3), TOBN(0x0dcb8f5d, 0x517318a9),
+     TOBN(0x48756645, 0x42441f5e), TOBN(0x03859154, 0xd79d535e),
+     TOBN(0x99bb28cd, 0x8217e4bf), TOBN(0xd6aed2e5, 0x8291e54d),
+     TOBN(0x8f9067e3, 0x1c92a65e), TOBN(0x120890ea, 0x1540b9b5),
+     TOBN(0x227d7c86, 0xec60a215), TOBN(0xb6609e85, 0x556d8c65),
+     TOBN(0xa6a26c37, 0x47f8c8a3), TOBN(0x4c850fe3, 0xf1204bdc),
+     TOBN(0x25f7e61a, 0x42db4eb8), TOBN(0xfdf05575, 0x3d62869d),
+     TOBN(0x8b36a744, 0x52b31c23), TOBN(0x83b83c89, 0x1a5e8d4c),
+     TOBN(0x72d38dd3, 0x5d9208bf), TOBN(0xbeb8873b, 0x8cf7b6f4),
+     TOBN(0xa3ec5c36, 0xcf90bcb6), TOBN(0x35adda6f, 0x9a6d5fe7),
+     TOBN(0x7312423d, 0xf61c68d9), TOBN(0xb1c4e10f, 0x20bcaf77),
+     TOBN(0x4df2850d, 0xf168ee57), TOBN(0xed07a4de, 0x180985e1),
+     TOBN(0xcb353d6b, 0x2fba1f23), TOBN(0x00ea9094, 0x778cc15e),
+     TOBN(0x4967faaa, 0x20857170), TOBN(0x9ff70dbe, 0xd7153bc4),
+     TOBN(0x49eb7994, 0x59f62fc6), TOBN(0x5f459faf, 0x3c91862d),
+     TOBN(0x1c10f621, 0x46d8f2e0), TOBN(0x7e669c9a, 0x252954e7),
+     TOBN(0x4ccf659a, 0xa83f6c57), TOBN(0xdc2b77eb, 0xec0a0a03),
+     TOBN(0xcf09ad07, 0x2cc6b8a2), TOBN(0x231960fc, 0xa97aa2d0),
+     TOBN(0xc0609130, 0xde227de8), TOBN(0x40d2691c, 0xf1d7ddf1),
+     TOBN(0x60349cf4, 0xf9a73890), TOBN(0x3f50005d, 0xf9968132),
+     TOBN(0xb4be853e, 0xf16f44b9), TOBN(0x48bf4804, 0x799caac5),
+     TOBN(0xe6a64876, 0x3c562849), TOBN(0x2f4d487f, 0x854f283f),
+     TOBN(0x64b77e39, 0x159484c4), TOBN(0xd419d4bd, 0x523e1529),
+     TOBN(0x1bf9510c, 0x143dcf7d), TOBN(0xa85bea71, 0xed5cb4e1),
+     TOBN(0x73a4cfd2, 0xec715818), TOBN(0x88b11d0e, 0x67f475f5),
+     TOBN(0xbfe170d8, 0x4d12361c), TOBN(0x9fc48e64, 0x00a0f979),
+     TOBN(0x6a8bb2dd, 0x65682105), TOBN(0xc1362a9c, 0x00bd952a),
+     TOBN(0xef5b3d89, 0xa6013753), TOBN(0xc87bbacb, 0x8fdfa22a),
+     TOBN(0x74fbdfc0, 0x31bb19e4), TOBN(0x7d058029, 0x32bfe260),
+     TOBN(0x54a4cce4, 0xe53da990), TOBN(0x01acdff6, 0x822da672),
+     TOBN(0xd2a2d484, 0x95597766), TOBN(0x5960ac1f, 0xd43dc7fd),
+     TOBN(0xcf095b6f, 0x8d6db685), TOBN(0x87232088, 0xa85618f3),
+     TOBN(0x91497a48, 0x34753c7c), TOBN(0xf682e372, 0xd6353024),
+     TOBN(0x7889ceda, 0x0c9b271c), TOBN(0x7126504e, 0x18340951),
+     TOBN(0xf786b821, 0x967c8a60), TOBN(0xfce01b37, 0xc17f3d99),
+     TOBN(0xe23c00a1, 0x1f2a8575), TOBN(0x7f56aa1b, 0xab6ff8a0),
+     TOBN(0xdb73869d, 0xd193dfcb), TOBN(0xbec02c94, 0xd644733e),
+     TOBN(0x283117bc, 0xf7b43261), TOBN(0x920acf5d, 0xb4108e39),
+     TOBN(0x33f1ef5e, 0xe49aebb8), TOBN(0x9ead51e4, 0x0fcea2c1),
+     TOBN(0x1f800a68, 0xf8503f28), TOBN(0x78818531, 0x34a75f67),
+     TOBN(0x1aeb3760, 0xb70ffb27), TOBN(0x1cca590a, 0xcb6309e9),
+     TOBN(0x8d09f360, 0x7170d241), TOBN(0xbc970b5b, 0xa0e0d0f8),
+     TOBN(0x2ec93eea, 0x31d038a3), TOBN(0x3482a0d7, 0x5153f8a2),
+     TOBN(0xedcbe914, 0x6641b5d8), TOBN(0xc086e61b, 0xe516e387),
+     TOBN(0x03814266, 0x9b875513), TOBN(0x6d37fee3, 0x37340a4f),
+     TOBN(0xcf78515e, 0xe5d17ab7), TOBN(0x0c7cd830, 0x4119a759),
+     TOBN(0xbd49062b, 0x54924618), TOBN(0x34c44f45, 0x41e7e7a3),
+     TOBN(0x0039f3d2, 0x706bd0af), TOBN(0x146cadc6, 0x0be9a628),
+     TOBN(0x6d5d5020, 0x57d48746), TOBN(0x0ea43f7b, 0x82caf4b0),
+     TOBN(0x11a08927, 0x8a064d34), TOBN(0x30c0ef40, 0x95638fa2),
+     TOBN(0x4b950c04, 0x602a871b), TOBN(0xf50cb1ef, 0x6a731537),
+     TOBN(0xb87a1cd3, 0xcbe5e4ef), TOBN(0xb1fc4894, 0x3dd1c601),
+     TOBN(0xdf402711, 0xa516617a), TOBN(0x5bdd1d67, 0xaaf63c65),
+     TOBN(0x020d1062, 0x6e559bd9), TOBN(0x4dec26d0, 0x81ec09fc),
+     TOBN(0x7b91fafd, 0xeeeeb2bc), TOBN(0x56290f98, 0x33aaf2c4),
+     TOBN(0x57abbd27, 0x79c7bf9e), TOBN(0x568bdee6, 0x2b1e1ecf),
+     TOBN(0x58f8c80c, 0x470f1d01), TOBN(0xeecfe398, 0x1b9cb76b),
+     TOBN(0xc0ffa4de, 0x311a0634), TOBN(0x425fcd13, 0x0ae99877),
+     TOBN(0x1964c681, 0xf7bd0748), TOBN(0xebcca16f, 0x9d920471),
+     TOBN(0xa72b40cb, 0xab4aa03e), TOBN(0x4397d6af, 0xa05624fc),
+     TOBN(0x372d522c, 0xa94fca0a), TOBN(0xe1010d60, 0x3035b9fc),
+     TOBN(0x9f1f00cc, 0x4f788d44), TOBN(0xfd00ec75, 0x6a88b672),
+     TOBN(0x53706702, 0x2983aef7), TOBN(0xa5f67b0b, 0x9b458edb),
+     TOBN(0x10789b90, 0x7db93ca8), TOBN(0x885346f0, 0xfd27cd55),
+     TOBN(0x3af5b0c8, 0x2ebb5f15), TOBN(0x282e4c4a, 0x2a36b2a7),
+     TOBN(0x2f9d5d8b, 0xa6d88bd4), TOBN(0x6f016bda, 0x9856b7aa),
+     TOBN(0x990ae53e, 0xa8198c1d), TOBN(0x295beceb, 0xa07e7ac5),
+     TOBN(0x576f790f, 0x48c2d246), TOBN(0xe99ab2ae, 0xe3ea9864),
+     TOBN(0xcf4959f2, 0x43e2d400), TOBN(0xdd1d8fad, 0x7a39dfea),
+     TOBN(0xdd6ff9c2, 0xfcd7fda0), TOBN(0x61c25b3e, 0xb6ace55e),
+     TOBN(0xf94742af, 0xb4dcddad), TOBN(0xc49cfa21, 0x44601959),
+     TOBN(0x07b3f1d1, 0x30c18470), TOBN(0x2736cb99, 0x6e6afc82),
+     TOBN(0x401fb234, 0xe24a8785), TOBN(0x9af8ba40, 0x074f51ea),
+     TOBN(0xe1acc646, 0xa9faed0c), TOBN(0xd5a5f789, 0xc9667008),
+     TOBN(0xc6436514, 0x68c3ab8f), TOBN(0x6fa0d734, 0xfe8d6b46),
+     TOBN(0xe5fccbfc, 0xaf7f49c7), TOBN(0x42c88c53, 0xbebcc58c),
+     TOBN(0x7d2e2fed, 0xe2a0f7f2), TOBN(0x694eb76c, 0x36a18b26),
+     TOBN(0xf0e6ae43, 0x6b0f657b), TOBN(0x8a0f6255, 0x48f1ece7),
+     TOBN(0xd594c168, 0x8674bfee), TOBN(0xe59ad38d, 0xac7d5ebd),
+     TOBN(0x080a6b97, 0x21645a1e), TOBN(0xb900f0e1, 0xf221b37b),
+     TOBN(0x562dabce, 0x04cab97d), TOBN(0x5c308741, 0x6f472462),
+     TOBN(0xa5d87e23, 0xc7c4cba8), TOBN(0x5237fe16, 0x9b061062),
+     TOBN(0xeddfbeb4, 0x222021c1), TOBN(0xa4fe57d0, 0x4e7a2a8e),
+     TOBN(0x0fbf6bdb, 0x2de56c47), TOBN(0x819588e7, 0x6fcebc6c),
+     TOBN(0x14196961, 0xdf041e3a), TOBN(0x76a31437, 0x40cd4f23),
+     TOBN(0x44acd14d, 0x8e1a877d), TOBN(0x227a35c6, 0x37d7b7de),
+     TOBN(0xe1934f1d, 0x842a9534), TOBN(0x7a2ed2c1, 0x53ed73e2),
+     TOBN(0xcffedd58, 0x3903924b), TOBN(0x7c9dbf55, 0xb306431d),
+     TOBN(0x61a72f10, 0x56e06ab5), TOBN(0xb46cf5cc, 0x616bc5cb),
+     TOBN(0xecf07e10, 0xf7c22216), TOBN(0xa4bddad9, 0xd9364b3a),
+     TOBN(0x548b95b2, 0xda8b1c93), TOBN(0xc09a9598, 0xa1e1c0cb),
+     TOBN(0xedd80ef1, 0x21d80851), TOBN(0x4684c439, 0xc283f047),
+     TOBN(0x07ca41f3, 0x87333da3), TOBN(0x173ec4de, 0xca79a8f4),
+     TOBN(0x89ce06f2, 0xb4aec6eb), TOBN(0xfe6b0e92, 0x15aaf7f0),
+     TOBN(0xdab8876d, 0x7c1b9ed3), TOBN(0x88aba90f, 0xa2606f83),
+     TOBN(0xcd21a408, 0xbebaf9f6), TOBN(0x09da6696, 0x0042a162),
+     TOBN(0x4a9b8b21, 0x2d66ccf6), TOBN(0x34c74904, 0x44d5a648),
+     TOBN(0xf3fe98e9, 0x3b0e9564), TOBN(0xe4a8a352, 0x221aa4a5),
+     TOBN(0x6278b4b5, 0x26c2b53e), TOBN(0x4ddf26ce, 0x1b1708ea),
+     TOBN(0x704207af, 0x6eb0d845), TOBN(0x60533de3, 0x0f5862ef),
+     TOBN(0x2b5945dd, 0xe54393c0), TOBN(0x55941df2, 0x145ea298),
+     TOBN(0xe2b500b6, 0xc240f654), TOBN(0x5a49d8f1, 0xcf9f6934),
+     TOBN(0xfe8d5468, 0x27502203), TOBN(0x985039d4, 0x58ade316),
+     TOBN(0xefd373f1, 0x0a687415), TOBN(0xefccb791, 0x43526774),
+     TOBN(0xeef8d46e, 0x0f4497d9), TOBN(0x4152df71, 0x1601ab9a),
+     TOBN(0x4250cd2f, 0xe47b2ad1), TOBN(0xa2b63fa5, 0xfb048180),
+    },
+    {
+     TOBN(0xd8a6cb6f, 0x787d1f1c), TOBN(0x427bac94, 0x3d219a66),
+     TOBN(0x51d7d49f, 0x383146b0), TOBN(0x8164b77f, 0x7863d781),
+     TOBN(0x1646b084, 0x2f9631b8), TOBN(0xef5b3aa8, 0x849388df),
+     TOBN(0x60536422, 0xe58cd383), TOBN(0xb079d911, 0xf43ea3a0),
+     TOBN(0x504ac041, 0xcb73887e), TOBN(0xf878b618, 0xc3ce3a33),
+     TOBN(0x57ef73d5, 0x56393e75), TOBN(0xe4372d2e, 0xd276c08c),
+     TOBN(0xfd9bc894, 0x0924cf58), TOBN(0xfa2a4deb, 0xaaa317e2),
+     TOBN(0xe51edccc, 0x79608da5), TOBN(0xadcc68fa, 0x8cd4b960),
+     TOBN(0xaa66c201, 0xf8e156c7), TOBN(0x7c7cf22e, 0x1ab2e3fe),
+     TOBN(0xe479c393, 0x0a677d85), TOBN(0xc0cd340f, 0xb87c412b),
+     TOBN(0x2b2bcef4, 0xf95ff321), TOBN(0x65da11c9, 0xb8409952),
+     TOBN(0x143a2218, 0xeb67eb9c), TOBN(0x8919ff25, 0xe53508e4),
+     TOBN(0x6f154f09, 0xa9e0eeae), TOBN(0x2246e6fe, 0xab05a657),
+     TOBN(0x4d7c1c81, 0x1045b85d), TOBN(0xde99ea37, 0xd3bb7432),
+     TOBN(0x058f8187, 0x63184ff4), TOBN(0x2a223421, 0xd134bfc3),
+     TOBN(0x1560dbed, 0x23120320), TOBN(0x37243c95, 0x76a3de9c),
+     TOBN(0xb8f3851a, 0xd36a81b1), TOBN(0xfbc62bfc, 0xbdad7ad9),
+     TOBN(0xf68215c7, 0x561e0f8c), TOBN(0x894131d1, 0x1bcf765b),
+     TOBN(0x8da01f9e, 0x45c5d736), TOBN(0x025de05c, 0x7484e0c1),
+     TOBN(0x62f4c66c, 0x6858b504), TOBN(0x754b85d6, 0xd6dc5f93),
+     TOBN(0x5b37cecc, 0x822a3de0), TOBN(0x422e49b1, 0xa98a37c2),
+     TOBN(0x3ef53d89, 0xbe41e927), TOBN(0x0994dd11, 0xf4d5bffa),
+     TOBN(0xa62ea556, 0xf7eacca3), TOBN(0x37b4e230, 0x7c746025),
+     TOBN(0xb4291e37, 0xa8e14253), TOBN(0x2bfc9eaa, 0x2a2b666c),
+     TOBN(0xf604604a, 0xc26e5588), TOBN(0xf75816ff, 0xa7ec3971),
+     TOBN(0x859e9ec7, 0x26a30a6d), TOBN(0x2ce57b66, 0xa1a5b815),
+     TOBN(0xc7aa4df4, 0xd65e8ec2), TOBN(0xbab6b3bb, 0xa5d82ede),
+     TOBN(0x7a11b25d, 0x7b088314), TOBN(0x501a3891, 0xc2c636ac),
+     TOBN(0x9f116c8f, 0xe256b02b), TOBN(0x71495693, 0xfa5946e0),
+     TOBN(0xeb9696ff, 0xc335452a), TOBN(0x01ca5929, 0x4971162e),
+     TOBN(0xee0a1f50, 0xc0f28e72), TOBN(0x2baac62c, 0x70d8df1a),
+     TOBN(0xcf65d297, 0xf49110f8), TOBN(0x041dbb01, 0x9a45e16a),
+     TOBN(0x8db69426, 0x5e1410c0), TOBN(0xb21f3c6a, 0xa70d0268),
+     TOBN(0x64a3c30e, 0xbac0ddac), TOBN(0xdcebdedc, 0x66a2d33a),
+     TOBN(0xc5dcd769, 0xa519de21), TOBN(0xa692b6a0, 0x19322c69),
+     TOBN(0x454add5b, 0x154fca13), TOBN(0xd2281cf0, 0x4935eba2),
+     TOBN(0xb5f44fe7, 0xf2602323), TOBN(0x772fb6a6, 0x5d68a3db),
+     TOBN(0xf519c5d4, 0x76eec37a), TOBN(0xbc8e9a15, 0xada6c3f4),
+     TOBN(0x9cd2a2f2, 0xf18effee), TOBN(0x9601c142, 0x1808ab42),
+     TOBN(0x05d110f7, 0x0480ad18), TOBN(0x5f7e0721, 0xef6a7f33),
+     TOBN(0x1afbeaec, 0xe6409e21), TOBN(0xf6714651, 0x317f7967),
+     TOBN(0x34cd21ff, 0x80124751), TOBN(0xf85c70ec, 0x931d9639),
+     TOBN(0x4e26bef6, 0x1ca19094), TOBN(0xc513f66b, 0x0b841b9a),
+     TOBN(0xe25507bd, 0xb9b41001), TOBN(0xd77fee94, 0x94f49f7c),
+     TOBN(0x20b19223, 0xd39e1ee4), TOBN(0xc8832a2c, 0x4e3e6c2c),
+     TOBN(0x64a8f43d, 0xa3a45c34), TOBN(0x52a05eef, 0x21fb291b),
+     TOBN(0x10d3e24a, 0xe4b68e38), TOBN(0x5289120a, 0xee2d8a40),
+     TOBN(0x33836b98, 0x425b7da8), TOBN(0x5bd418f3, 0xb00c64e1),
+     TOBN(0x10e92e5a, 0xd511c3f8), TOBN(0x17f1301d, 0x18b62b7d),
+     TOBN(0xf710b02d, 0x97f0fcae), TOBN(0x8b1030f6, 0xbd394477),
+     TOBN(0x49040009, 0xe5aab897), TOBN(0xfdb23ac1, 0xce75b4d3),
+     TOBN(0x7a43d904, 0xf2b70e1b), TOBN(0xdc09e995, 0xf94fa56f),
+     TOBN(0x9f314e85, 0xd075dd65), TOBN(0xc0d39ce0, 0xb9e26b8d),
+     TOBN(0xd3f6778e, 0xfdc3b678), TOBN(0xce6573e9, 0xfc8497df),
+     TOBN(0x1f4535f8, 0x67abaf7a), TOBN(0x80706dab, 0xa47dd948),
+     TOBN(0x670ae5bc, 0xc059242b), TOBN(0x3a29bc73, 0xcf5f9308),
+     TOBN(0xd2f8e297, 0x8af2bf74), TOBN(0x98dbb443, 0x6c48bbec),
+     TOBN(0x211a3a96, 0xeb448447), TOBN(0x88ffb240, 0x5af4a2c0),
+     TOBN(0x1959dd34, 0x9cdf9e20), TOBN(0xa4d0d839, 0xf34627e0),
+     TOBN(0xa5cecad3, 0xf00057cc), TOBN(0x22f32cce, 0xc5d97b18),
+     TOBN(0x31a02241, 0xcedc2c97), TOBN(0xf0f5489b, 0x2b632641),
+     TOBN(0xb09091dd, 0xcbfb588f), TOBN(0x5ffd0f38, 0x5d9478e7),
+     TOBN(0x13f141a1, 0xdae35eda), TOBN(0xd25563a6, 0x62f0b26c),
+     TOBN(0x80014b17, 0x1b9dde18), TOBN(0x9fcf8f81, 0x7da49b36),
+     TOBN(0x68aac84a, 0x93519f31), TOBN(0xe14c35c1, 0xc0b3660a),
+     TOBN(0x29f447dd, 0x08195bc5), TOBN(0xc61fbbe6, 0x10bba62f),
+     TOBN(0xc471624c, 0x4ed8621a), TOBN(0x8005f67f, 0x0950a4c6),
+     TOBN(0xdfc3b3e5, 0x93a2a33e), TOBN(0x9c3c815e, 0x3c1d0e42),
+     TOBN(0x1ed1aedb, 0x93904766), TOBN(0xcd5e0cf6, 0x376fd0bc),
+     TOBN(0xdd4c3378, 0x90d03033), TOBN(0xde39db01, 0xd85dca30),
+     TOBN(0x49d01dc2, 0xe6fab58b), TOBN(0xd16d7940, 0x6d449f51),
+     TOBN(0x3fb6cf8e, 0xd20a95e6), TOBN(0x0b10a596, 0xbbeeccb1),
+     TOBN(0x06ceaa62, 0x0005acd3), TOBN(0x09db6b28, 0x47555053),
+     TOBN(0x45d84a85, 0x7e7d18d7), TOBN(0x33c28b02, 0x229ad33e),
+     TOBN(0x1e5a6b52, 0x72e4f34c), TOBN(0x81eefbe6, 0xb64fa831),
+     TOBN(0x4983b84a, 0x2aa209aa), TOBN(0x20777198, 0x38d6a8d6),
+     TOBN(0xbe993805, 0x40096f25), TOBN(0x900d4bdd, 0xec820131),
+     TOBN(0x2170cfd3, 0x2a993f9c), TOBN(0xa0e3d894, 0x2dfe1007),
+     TOBN(0x600d0b5a, 0x0e7df109), TOBN(0xc904985a, 0x47fde3dd),
+     TOBN(0x15597a84, 0xcb89816a), TOBN(0x8ac8b027, 0xb9dfeb9e),
+     TOBN(0x5c9211bc, 0x6450a179), TOBN(0xd448a70a, 0xf6333f95),
+     TOBN(0xe9c9a964, 0x824e1d7f), TOBN(0xc47d3f3c, 0x15750ae4),
+     TOBN(0x959f776b, 0xadcf9482), TOBN(0x00557ffe, 0xe741ceb3),
+     TOBN(0x8b69d3f6, 0x353d7898), TOBN(0x6b4d80d3, 0x45cfa492),
+     TOBN(0xc33ead78, 0x30c313da), TOBN(0x86f96c3a, 0x67eee139),
+     TOBN(0x0c6675c7, 0x08611b15), TOBN(0xf9ee695d, 0x60620c27),
+     TOBN(0xb35d438c, 0xd70c9258), TOBN(0x1bc2b1e7, 0xa5e7a4b1),
+     TOBN(0x38d257f8, 0xef92f629), TOBN(0x090af73a, 0x79fd1eb0),
+     TOBN(0x96ebd1f0, 0xf59342e5), TOBN(0xd4869362, 0x4d053375),
+     TOBN(0x7db504e2, 0x5fab54aa), TOBN(0x17c0801e, 0x6e8e43fb),
+     TOBN(0xd3904d62, 0x136b1941), TOBN(0x5932b753, 0x28a43bd1),
+     TOBN(0x551d895e, 0xacb35572), TOBN(0x3f7a8a46, 0x1a6fdfbe),
+     TOBN(0x9e3ea4fd, 0xf7a2df83), TOBN(0x8b68b26b, 0x64524d44),
+     TOBN(0x74caeeab, 0x126aee21), TOBN(0x590a00a5, 0x915d9e1c),
+     TOBN(0x5ae2a6ab, 0x49b90eff), TOBN(0x74b4cb1e, 0x2df4fe51),
+     TOBN(0x0306ed11, 0x07fcb6ed), TOBN(0x564ebe2e, 0x502f5b30),
+     TOBN(0x5a09a32e, 0x0c89e9ba), TOBN(0x967f9dfb, 0x84f2466d),
+     TOBN(0x8b27416c, 0x26a1a8a0), TOBN(0x1c21ef95, 0xc3158a18),
+     TOBN(0xa7ee1ad8, 0x2be23ae9), TOBN(0x1f312d04, 0x4daa1fcf),
+     TOBN(0x6782f843, 0x44f9c7d7), TOBN(0xb12ea2bf, 0xe19e2681),
+     TOBN(0xd2e43cbf, 0xd20578af), TOBN(0x5566460a, 0xbb5819b4),
+     TOBN(0x86f6c860, 0xb658c03c), TOBN(0xc8d90309, 0x62d42d82),
+     TOBN(0x7975a5f3, 0xcb883ceb), TOBN(0xf6f5cf0f, 0xdcded5a1),
+     TOBN(0x25554fb1, 0xd3eb810b), TOBN(0x3df7536b, 0xa596c7c6),
+     TOBN(0x255ca136, 0x83de31cd), TOBN(0x7ac532ee, 0x7795eb96),
+     TOBN(0xfa9d83a9, 0xb12bc235), TOBN(0x7df5d231, 0x4b287a83),
+     TOBN(0xb2eaaaf6, 0xb4f19fce), TOBN(0x7caabfb0, 0x1a045f6a),
+     TOBN(0x6d1b7f40, 0xb1449e6a), TOBN(0x24ae41da, 0x12d22c82),
+     TOBN(0xb0f7a0c3, 0xc0a9d128), TOBN(0x2adc34d3, 0xaed0d3bd),
+     TOBN(0x4ebf5778, 0x13e778e6), TOBN(0xd3b89bd0, 0xbb8476ba),
+     TOBN(0xe09eb528, 0x37413953), TOBN(0x952b705c, 0xd8ba3471),
+     TOBN(0xcaa81ade, 0x86a79c09), TOBN(0xc08eed3d, 0x7e0e7b2a),
+     TOBN(0x313fb103, 0xc80b4196), TOBN(0x25449ece, 0x88c7ac81),
+     TOBN(0xa6cb9ad3, 0x24f16fa2), TOBN(0x4602c441, 0x728a0c4b),
+     TOBN(0xc3366f88, 0x5a000a9c), TOBN(0x146623e3, 0xef8778bd),
+     TOBN(0x184ba0b1, 0xf0773fdc), TOBN(0xe1d115ef, 0xaecd8d63),
+     TOBN(0x420d5473, 0xae165e6c), TOBN(0xefe137cd, 0x108d8575),
+     TOBN(0x15378c57, 0x6fcff7d9), TOBN(0x49c48099, 0xc1f5b601),
+     TOBN(0x1b0e3aee, 0xf68473f1), TOBN(0xa78329bb, 0xd320720d),
+     TOBN(0x385e521b, 0xdca54cad), TOBN(0x478ce06f, 0x5c5b8d60),
+     TOBN(0x215d7d33, 0xca7c4d4b), TOBN(0x773f3ab3, 0xa095366c),
+     TOBN(0x668e0d11, 0x7afeeaa1), TOBN(0x410cd205, 0x4878d8a3),
+     TOBN(0x2748fd98, 0xb2800646), TOBN(0xf1183786, 0x73a1dbef),
+     TOBN(0xecc31bd2, 0x7567ed3a), TOBN(0x775210df, 0x0ceb3873),
+     TOBN(0x2ea0c2bc, 0x9a8f42ff), TOBN(0x62974240, 0x4a1c029a),
+     TOBN(0x5ee5f5f6, 0x9e4dd41b), TOBN(0x5b1bba80, 0x2e110249),
+     TOBN(0x5ac4eadb, 0x78da8016), TOBN(0xc2978780, 0x1809f79f),
+     TOBN(0x39d2dbce, 0xe3f8c0de), TOBN(0x7fb4b5fc, 0x064d3ba9),
+     TOBN(0x038a736d, 0xcd481ab4), TOBN(0xc4b15c5c, 0x396888eb),
+     TOBN(0x459a896d, 0xd421f36f), TOBN(0x6058b90f, 0x47f54663),
+     TOBN(0x0a5f6771, 0xafefebe5), TOBN(0x45c97ab2, 0xa5b7c2ca),
+     TOBN(0x6d547af9, 0x85139ca4), TOBN(0x6db218de, 0xa29d71cb),
+     TOBN(0xca6e0e7b, 0xfb956184), TOBN(0x6682b6e3, 0x1f660ac6),
+     TOBN(0x3959e396, 0x8b21bceb), TOBN(0x0459fd46, 0x632cf9c8),
+     TOBN(0xc741250c, 0x74f296bb), TOBN(0x29b9cacf, 0x990dbefa),
+     TOBN(0x5065d818, 0xfc35bdf7), TOBN(0xeb8e9e1b, 0xa551dc04),
+     TOBN(0x4f7d6f7d, 0x11befe9e), TOBN(0xa88f1fce, 0x7478fdee),
+     TOBN(0x39b1e053, 0xafa688e3), TOBN(0x562a0d6e, 0xe16847e0),
+     TOBN(0xf6044e4b, 0x34c26d14), TOBN(0x5ebe8727, 0x7df61b90),
+     TOBN(0xa82a4de4, 0x6b5e5a39), TOBN(0xc916b0ba, 0xfb9d296c),
+     TOBN(0x029f1cb2, 0x2e1dc01e), TOBN(0x7699d92e, 0xfc429483),
+     TOBN(0xee0e425a, 0x154560f0), TOBN(0x3f5cdfe6, 0x787b6641),
+     TOBN(0x726d87bb, 0xe5f6794e), TOBN(0x97d73588, 0x23aecad2),
+     TOBN(0x47f4f5b9, 0x09ca351c), TOBN(0xd742ef4b, 0x57dc5e3b),
+     TOBN(0xccd2209d, 0x71411a86), TOBN(0x94d57663, 0x2223e6ce),
+     TOBN(0x228a7400, 0x66c7950c), TOBN(0x2d00ef6e, 0x54dd4e37),
+     TOBN(0x9ea5daf3, 0xd60f66be), TOBN(0x743c58a5, 0x8aca724f),
+     TOBN(0x1f638406, 0x44e38446), TOBN(0x06314bb0, 0x92ef6bb0),
+     TOBN(0xa7459e7f, 0xbb05589d), TOBN(0xc3a39592, 0xbfa913d7),
+     TOBN(0x27dbabee, 0xdf07b190), TOBN(0x1c8a2f33, 0xd2ee62ff),
+     TOBN(0x60c8bcb8, 0xe31e8d63), TOBN(0xea1604d1, 0xce410061),
+     TOBN(0x55e8cfee, 0x3d7f7a98), TOBN(0x49efc316, 0xebc64102),
+     TOBN(0x04c86d8e, 0x41934914), TOBN(0x26667c76, 0xab7facd4),
+     TOBN(0x319158db, 0xa71a8916), TOBN(0xb802942d, 0x114fff43),
+     TOBN(0x5efdef7b, 0x8ce544f5), TOBN(0xf531c718, 0x70e870c1),
+     TOBN(0x4b9a5f1b, 0x4d92071d), TOBN(0xbe767cf2, 0x60cc48b6),
+     TOBN(0xbf389d37, 0x717381ea), TOBN(0xefd9e984, 0x06bc5bcb),
+     TOBN(0xcc8bc960, 0x67ff1110), TOBN(0xd3414c0b, 0xb05612e4),
+     TOBN(0x084e5f05, 0x927fad1a), TOBN(0x999bd581, 0x438e241f),
+     TOBN(0x0c917602, 0xfaa4fab8), TOBN(0xda0520d2, 0x95080657),
+     TOBN(0x3160f928, 0xce2f1af2), TOBN(0x61186d84, 0x364f56e4),
+     TOBN(0xe36a5fc0, 0x25fa68f0), TOBN(0x9e6f66bd, 0x774c584b),
+     TOBN(0x2611bba4, 0x9ecb449a), TOBN(0xb1e0b341, 0xec5a0667),
+     TOBN(0x336de76d, 0x6cddb6c3), TOBN(0x9668b5b3, 0x65a18f95),
+     TOBN(0x1ff6c81f, 0x7c3ec388), TOBN(0x53545b05, 0x40a8e2d0),
+     TOBN(0x990a3cc5, 0x14ae31d6), TOBN(0x769b4c26, 0x063a2995),
+     TOBN(0xcea238f4, 0x039e279f), TOBN(0xbfc5cfb9, 0x732fb38e),
+     TOBN(0x99f5a33c, 0x82fa05d8), TOBN(0x274dc741, 0x69c42686),
+     TOBN(0x193338ee, 0x76af2af7), TOBN(0x0488c19f, 0x6914ae66),
+     TOBN(0x8d197f4e, 0x5fc58bf4), TOBN(0x23de54df, 0xf0e61d4b),
+     TOBN(0x547dd09f, 0x44a165e1), TOBN(0x99878065, 0x1c2d5471),
+     TOBN(0xb2cabfad, 0x39b311db), TOBN(0x0aed63d9, 0x4b61a7eb),
+     TOBN(0x03713ac5, 0xbe8110ef), TOBN(0xaab1917d, 0x50f989d3),
+     TOBN(0x0d80fe98, 0x358fe8b0), TOBN(0xf6e874c5, 0xa7a1f8e3),
+     TOBN(0x05650fd8, 0xdeb42398), TOBN(0xbad3e085, 0x1c44de73),
+     TOBN(0x5369135f, 0x1c27f3c2), TOBN(0x14bc04f8, 0xa7fc74ac),
+     TOBN(0x18cbf622, 0xb5dae291), TOBN(0xce290570, 0x9356b88c),
+     TOBN(0x61bbb446, 0x39eba4e6), TOBN(0xa79c291b, 0x980fee37),
+     TOBN(0xd9f18006, 0x19960cc6), TOBN(0xb0823f41, 0x0ce98896),
+     TOBN(0xf2bc612e, 0x1377df6f), TOBN(0x1c07bded, 0xc0b0e71c),
+     TOBN(0xffbf9841, 0x37211fd5), TOBN(0xbd704a6b, 0x04a81410),
+     TOBN(0x653cd2ee, 0x6abf1c67), TOBN(0x73ab8aa1, 0x40681621),
+     TOBN(0xc0bae4fd, 0x271ada5c), TOBN(0xf567cae8, 0xc46f189d),
+     TOBN(0xd183cb27, 0xa5535334), TOBN(0xcbf133f7, 0xe53c530c),
+     TOBN(0x32e53f78, 0xedd6a17e), TOBN(0x6ce6da9a, 0xa2194fae),
+     TOBN(0xa89b8054, 0x58cd3586), TOBN(0x0037febb, 0x43b520a5),
+     TOBN(0xbe67a2cf, 0x653e2c0b), TOBN(0xc07a1ed1, 0x50301f52),
+     TOBN(0xf98b2b60, 0xf5ea954f), TOBN(0xfa6da95d, 0x7af6c499),
+     TOBN(0x44892091, 0xe3889cb1), TOBN(0x123fc555, 0xd45ae338),
+     TOBN(0x2bc4a9ef, 0x02a63128), TOBN(0xb72012c9, 0xa1dbb436),
+     TOBN(0x8c75f7b3, 0x556a0b46), TOBN(0xe4c6f46c, 0x5b7608a4),
+     TOBN(0xb36abf68, 0x38fce20f), TOBN(0xb5a8e657, 0xbf6c21e1),
+     TOBN(0x9ceaeefe, 0xcecd5963), TOBN(0xe84f200d, 0x6105fc29),
+     TOBN(0xc28981a9, 0x8591e346), TOBN(0x0be4e931, 0x207001f1),
+     TOBN(0x31399d9d, 0x88616b18), TOBN(0x3dac9f55, 0x238c626e),
+     TOBN(0x0213fca7, 0x65574274), TOBN(0xa3560244, 0x827aa853),
+     TOBN(0x3ffbfeeb, 0x1ca99134), TOBN(0x0a4b56f6, 0xd07a2dac),
+     TOBN(0x01795eca, 0x75417a6b), TOBN(0xe2a6dd9c, 0x18a5fb22),
+     TOBN(0x13c97586, 0x8aca0cd8), TOBN(0x3c2bb26e, 0x7c323c52),
+     TOBN(0xa3688cae, 0xe38319bf), TOBN(0xe04b44b4, 0x4c88726a),
+     TOBN(0xfed864d0, 0xb0a88a4c), TOBN(0x3e6cf152, 0x6b1fa305),
+     TOBN(0x8416b5f0, 0x00e18e4a), TOBN(0x3a7603ca, 0xfa4cd8f2),
+     TOBN(0x8b04d5ff, 0x7ec750ef), TOBN(0xe1867f9b, 0x1c1ced05),
+     TOBN(0x87ffd0fb, 0xdac2f8c1), TOBN(0xc9ebe420, 0x08b3cdca),
+     TOBN(0x5028a4fd, 0x029c247e), TOBN(0xd6225a43, 0xa5ae3e76),
+     TOBN(0xfb3fa71c, 0xf970817e), TOBN(0x74216809, 0x9ab4aef1),
+     TOBN(0xca81ee99, 0xa39c2f13), TOBN(0xa8336e42, 0x86a97827),
+     TOBN(0xb75aff99, 0xb6489555), TOBN(0x005b2338, 0xe565435c),
+     TOBN(0xbaee27bb, 0x524bdb34), TOBN(0xbf22e1c9, 0x82e47e71),
+     TOBN(0x6ab1d712, 0x97491139), TOBN(0xf420ce06, 0x2cf4cbff),
+     TOBN(0x9f96a2fc, 0xb2b0c86a), TOBN(0x42765dd9, 0xabeb7335),
+     TOBN(0x7c223bb7, 0x45b7e044), TOBN(0xce3f9245, 0x1794e144),
+     TOBN(0xa0a15b27, 0xf3ee5c4e), TOBN(0x1721c5bf, 0x54622215),
+     TOBN(0x0003fd16, 0xada5a99c), TOBN(0x8e96dd56, 0xdbdccc7b),
+     TOBN(0xd1abdc0b, 0x43f83748), TOBN(0x71cac4a6, 0x0f5ce74b),
+     TOBN(0xb8539aff, 0xd46717ad), TOBN(0xeb65c589, 0xb99661d9),
+     TOBN(0x66b4df3b, 0x85e89e17), TOBN(0xc94fad66, 0x6fc30672),
+     TOBN(0xfd2aa80f, 0x81d90df8), TOBN(0xed73a163, 0xbd8234c1),
+     TOBN(0xe1a2c0b9, 0x72eb45dd), TOBN(0x417e355d, 0x902b5ca9),
+     TOBN(0xa8f55aaa, 0xd5128812), TOBN(0x4826b934, 0x3c14cb79),
+     TOBN(0xeae495e3, 0x394d7a92), TOBN(0xcba23153, 0xb90faec0),
+     TOBN(0xd687c821, 0xf6d9d80c), TOBN(0x951dada2, 0x8bff3082),
+     TOBN(0x4e74f1f0, 0x701708ad), TOBN(0xa6333cd1, 0xdd2134f2),
+     TOBN(0xeea276cf, 0x04665f7c), TOBN(0x527257fc, 0xae74c17a),
+     TOBN(0xeb3fd493, 0xe51b53bd), TOBN(0xae7807db, 0x69ec567e),
+     TOBN(0xa50124aa, 0x5de15fd0), TOBN(0x781bfe70, 0x1336f055),
+     TOBN(0xb5729a74, 0xd70a0dfc), TOBN(0x89da37f3, 0x9f50c1a3),
+     TOBN(0x6e063297, 0xcd8e6c1c), TOBN(0x17eb6ec1, 0x181d0271),
+     TOBN(0x36e7251a, 0xe4e52a8c), TOBN(0x3acfe89b, 0x94705324),
+     TOBN(0xaa94f06e, 0xbc130c3b), TOBN(0x01b5e44c, 0x309ae25a),
+     TOBN(0xb72160f2, 0x0f61b55b), TOBN(0xbef61953, 0xe7bbc3f2),
+     TOBN(0x96734d7a, 0x1bf138a1), TOBN(0xdaa6186c, 0x08c25135),
+     TOBN(0xa3b031b2, 0xf34534a2), TOBN(0x44136619, 0xde46f581),
+     TOBN(0x4d0ed04b, 0x6d379647), TOBN(0x4879d90d, 0xbb2b6735),
+     TOBN(0x8f7e031a, 0x590156e0), TOBN(0x28428354, 0xf42bbc53),
+     TOBN(0x1cbed33c, 0x5c5b791e), TOBN(0x17571645, 0x4cfc5562),
+     TOBN(0x8392350a, 0x7f76462f), TOBN(0x659ce7db, 0x0c216ccb),
+     TOBN(0xe87a78b7, 0x047e35d5), TOBN(0x307c4861, 0x6e0862d6),
+     TOBN(0xd444fb86, 0xe70741bd), TOBN(0x1138a886, 0xfea1abe2),
+     TOBN(0x4695397d, 0x62b79c4f), TOBN(0x11aaf588, 0x003130ee),
+     TOBN(0x53bdda6d, 0x3a11712b), TOBN(0x30c384bd, 0x40fba3d2),
+     TOBN(0x63039585, 0x50ea38be), TOBN(0x7f110eca, 0x3da9738a),
+     TOBN(0xbd701fc6, 0x5b68c01e), TOBN(0xd23f3e8f, 0xcc48f38d),
+     TOBN(0x6e2557eb, 0xf8b9bb65), TOBN(0x29ceb4b6, 0xa3dafc8f),
+     TOBN(0x24659686, 0x4b6b7705), TOBN(0x04143a8a, 0x4aca2b43),
+     TOBN(0x3baed256, 0x975e06d8), TOBN(0x846fb3c9, 0x3e834249),
+     TOBN(0x7890761e, 0x75f6770a), TOBN(0x1187920e, 0x203c78fd),
+     TOBN(0x9b06c3a9, 0x6b26281f), TOBN(0x3fe3dccd, 0xa98215e1),
+     TOBN(0x099d7d7a, 0x4f33655e), TOBN(0x1ba571e6, 0x662fb55a),
+     TOBN(0x1a0d0147, 0xcbc497f0), TOBN(0xa94218ae, 0x2862ff38),
+     TOBN(0x1b0f919b, 0x5ce08047), TOBN(0x9a3ac37a, 0x2baf46cd),
+     TOBN(0x76b7a03a, 0x8964cc68), TOBN(0x5aed8c6d, 0x4d3e1a40),
+     TOBN(0x6607179c, 0x7f034ff4), TOBN(0xefb8fcd9, 0x3781eac2),
+     TOBN(0xfa57f8a9, 0x7b022138), TOBN(0xc5bb9f1d, 0x56ab6039),
+     TOBN(0xf9027e24, 0xe4d2ab7f), TOBN(0x3d67ad71, 0x77a9e364),
+     TOBN(0xc579e70c, 0x1f7f487d), TOBN(0x7fefc894, 0x2a7e6bd0),
+    },
+    {
+     TOBN(0x2cb91662, 0xa45cfd31), TOBN(0x09dd56d3, 0x16f65cfe),
+     TOBN(0x983e005d, 0x14f3de51), TOBN(0xb9dc05b0, 0x210f64fc),
+     TOBN(0x22790afd, 0x885eafe5), TOBN(0xbd5213d3, 0x7444bdec),
+     TOBN(0x289dca92, 0x8987300a), TOBN(0x69fb2ac2, 0xb3960b76),
+     TOBN(0xe3274886, 0x9ae7540f), TOBN(0xd7386631, 0x6131e921),
+     TOBN(0x2e3d4fd8, 0xf2a360c8), TOBN(0xb20a59b6, 0x3d9d41e0),
+     TOBN(0x72b67eae, 0x99082a34), TOBN(0xfad6aa7d, 0x51819470),
+     TOBN(0x7c299b9a, 0xa2d1d007), TOBN(0xc1f841e0, 0x8100bed0),
+     TOBN(0x2c1f7d4c, 0x43e90ebd), TOBN(0x58b78107, 0x1fc72b07),
+     TOBN(0xda8503e1, 0xaf94f402), TOBN(0xfbb724b7, 0x59f244b0),
+     TOBN(0x2fcd2186, 0xfcd8755a), TOBN(0x7407cdee, 0x868482b7),
+     TOBN(0x4d62f578, 0x349be3d5), TOBN(0x4a012544, 0xdcc6202c),
+     TOBN(0xb8a414d2, 0x151ffc08), TOBN(0xaa79acf0, 0x740d6b55),
+     TOBN(0xeeab0104, 0xcdf472ab), TOBN(0x5014a8c1, 0xa3aa5f1d),
+     TOBN(0x8c743405, 0x33f13425), TOBN(0x2b776b49, 0x57eb54d4),
+     TOBN(0x3a0cc4ac, 0x548a723b), TOBN(0x65aae6f3, 0xc79fe63a),
+     TOBN(0xe8b388f2, 0xee5e579c), TOBN(0x31cc9373, 0x991c03d4),
+     TOBN(0x53eed518, 0x567bfa7c), TOBN(0x267e282d, 0x67f985ed),
+     TOBN(0xc435fd22, 0xb4763ea0), TOBN(0xead83837, 0xe39b7703),
+     TOBN(0x806daad5, 0x094ba5b4), TOBN(0xa738a847, 0x45842672),
+     TOBN(0x99421b42, 0x9984c4c2), TOBN(0x1a3bce27, 0xd35c7bbd),
+     TOBN(0xe51ae6f6, 0x3563b09e), TOBN(0x8e67853b, 0x8d9c9fbf),
+     TOBN(0xca8784da, 0x6b2100b5), TOBN(0xe89a24f7, 0x98879bba),
+     TOBN(0xe901b45c, 0xe286b039), TOBN(0x23dedbb8, 0xf50384bd),
+     TOBN(0x4728cbdb, 0x5cbf7df2), TOBN(0xed274fdf, 0x6764329e),
+     TOBN(0xc2af1a07, 0x642d199a), TOBN(0x5d665659, 0x17a50e7e),
+     TOBN(0x7babf4bc, 0xfaa5eb82), TOBN(0xd3bcfc67, 0x99fe4026),
+     TOBN(0xaa5d2648, 0x607d9f41), TOBN(0x7405c071, 0x967efac1),
+     TOBN(0x79447ef9, 0xdfa782a4), TOBN(0x6dadc8e1, 0x74cd9500),
+     TOBN(0x0574020e, 0xdc38f762), TOBN(0x17596d7e, 0xe2ee7a14),
+     TOBN(0x9ef75af7, 0x9e1f8adc), TOBN(0x5ac5f216, 0xa4791da0),
+     TOBN(0x1583226b, 0x7b7b5d80), TOBN(0x59f3f053, 0xa21c26cc),
+     TOBN(0xd80e7fdc, 0xf95e30d9), TOBN(0xecf5041c, 0x0a3a3616),
+     TOBN(0x50b93b8b, 0x03043fa6), TOBN(0xa31a2aa4, 0xae262ad6),
+     TOBN(0x1468b370, 0xd63cd98d), TOBN(0xfb89cc65, 0xdc07a367),
+     TOBN(0x6cf1df6b, 0x4d47b59f), TOBN(0xab451a99, 0x1b6350fe),
+     TOBN(0xeb74554d, 0x8c124dff), TOBN(0x781a8c4d, 0x21be0be0),
+     TOBN(0xfaacc154, 0xe3510068), TOBN(0x16655d65, 0xd6238265),
+     TOBN(0xba46d27b, 0x0466134a), TOBN(0x1a3f51b9, 0x3101e283),
+     TOBN(0xc08298a9, 0x096ec237), TOBN(0x46248627, 0xc69cfb5b),
+     TOBN(0xf9e7a5a4, 0x81a0500f), TOBN(0x92db27d5, 0xbd2e03e7),
+     TOBN(0x3dcce4f6, 0x82899e3c), TOBN(0x861f1797, 0xf39a39c7),
+     TOBN(0x175b2430, 0x69dc8961), TOBN(0x93d2a88e, 0xdc67953e),
+     TOBN(0xa40f3704, 0x92d794d6), TOBN(0x607019f0, 0x3526eeaf),
+     TOBN(0xf20e163b, 0x22f37d65), TOBN(0x70fd00c8, 0x32cf180a),
+     TOBN(0xff1a97d2, 0x0b17244e), TOBN(0x9a5a141b, 0xacedb33a),
+     TOBN(0xf03dd868, 0xcc16bbb4), TOBN(0x9b15372d, 0xa40e44e9),
+     TOBN(0xd5ba6436, 0x15ac397f), TOBN(0xb1a886d4, 0xc821f6b7),
+     TOBN(0xbe3aacda, 0x4b7b4e21), TOBN(0xad9829fe, 0x66b56729),
+     TOBN(0x78965cef, 0xd541cc1a), TOBN(0x414bfea7, 0x7d17631e),
+     TOBN(0xf55835d9, 0xc64dd621), TOBN(0xa0ebf68b, 0xef644d93),
+     TOBN(0x01d15340, 0xc8a40021), TOBN(0x00ae640d, 0x42b99aa0),
+     TOBN(0x92321ee2, 0x6881e64f), TOBN(0xaccab5c8, 0x5267bdd3),
+     TOBN(0x0c1f87ea, 0x5927647b), TOBN(0x0093d07e, 0x162c6d86),
+     TOBN(0x49868df4, 0x389c711d), TOBN(0xe68d69ae, 0xc11e1f37),
+     TOBN(0xa40e7aa8, 0xb4207246), TOBN(0x4ae8d126, 0xce832395),
+     TOBN(0x5f9b128a, 0x86450cc0), TOBN(0x88f76293, 0xc8ec07e6),
+     TOBN(0x0762f293, 0x179702b8), TOBN(0xb5696102, 0x4910006d),
+     TOBN(0x3951291b, 0x35fe0505), TOBN(0x70f75a5c, 0xce8d7056),
+     TOBN(0x4541beaf, 0x2eb13369), TOBN(0x7060a749, 0xa643316c),
+     TOBN(0xee16abd0, 0x49c879a5), TOBN(0x844874a7, 0xa47ac42e),
+     TOBN(0xee3f8a20, 0x3c9c2326), TOBN(0x99a12054, 0xdeaed33b),
+     TOBN(0x4814a15b, 0x63b333ae), TOBN(0xee9f28a5, 0x9d923fa0),
+     TOBN(0x5b0cd250, 0x33b1b1ef), TOBN(0x3ccc39b9, 0x8346d928),
+     TOBN(0xf5c1000e, 0x002bec95), TOBN(0x2ba2f18c, 0xf63528c2),
+     TOBN(0x8102f6c8, 0xcdcec15a), TOBN(0xab7effcd, 0xbb13d14a),
+     TOBN(0x183e0ba2, 0xfcd3787c), TOBN(0xae70635e, 0x2f4a7fc0),
+     TOBN(0x473ed37f, 0x760bbc96), TOBN(0xf0ea0acf, 0x8a8efb39),
+     TOBN(0x63cea36c, 0x29b526a9), TOBN(0xcdb31613, 0x9d03f3db),
+     TOBN(0xa3891096, 0xd57cca8e), TOBN(0x646418a9, 0xa14a8ffc),
+     TOBN(0x10f8906b, 0x8075291f), TOBN(0x8221d941, 0x2c618bf6),
+     TOBN(0x1dc1ae7a, 0x8a5da4df), TOBN(0xb66b96e3, 0x8a8cc8bc),
+     TOBN(0xe4da7e48, 0xfe3551b4), TOBN(0xe6891cc9, 0xad9b3827),
+     TOBN(0xb266733f, 0x6b37b99f), TOBN(0xfccce911, 0xfd5d1219),
+     TOBN(0xe5a47c4b, 0x7262b8cc), TOBN(0x5d349caf, 0xe656af49),
+     TOBN(0x7a3a4a28, 0x7e04e48e), TOBN(0x7c39a68e, 0x80ea7d03),
+     TOBN(0xf35d5e32, 0xbee0d434), TOBN(0x9651f3d9, 0x0b536161),
+     TOBN(0x42634cc9, 0x72cb370c), TOBN(0xa7764026, 0x406b3457),
+     TOBN(0xec7525bd, 0x65d53d80), TOBN(0xf44a1bca, 0xadcc8b05),
+     TOBN(0x12ef8427, 0xda112ddc), TOBN(0x796a65b3, 0x20a0f78f),
+     TOBN(0x12726e24, 0x6bd5b0ab), TOBN(0x9e441467, 0x8242fe07),
+     TOBN(0x4b52e276, 0xde2bea52), TOBN(0x3a76b6b4, 0x10422c2c),
+     TOBN(0x71f14945, 0xb4e496b9), TOBN(0xd20f04b0, 0xf36dce4f),
+     TOBN(0xa0e57d8d, 0x2b310e90), TOBN(0x59eb7737, 0x32ec8f38),
+     TOBN(0x20a19834, 0xaaf6619e), TOBN(0x633b94e8, 0x691a538e),
+     TOBN(0xea1a8985, 0x92cdf395), TOBN(0xa3a01c57, 0x4349b971),
+     TOBN(0x0d65192a, 0x30822c59), TOBN(0x93a5152d, 0xa522ae8c),
+     TOBN(0x5b09a7a3, 0x0e1aa4bc), TOBN(0xdd2588f3, 0x8d3b97a9),
+     TOBN(0xafa1f62a, 0x5b62a3a5), TOBN(0xbded10e6, 0xa9ace9c5),
+     TOBN(0x9d03e061, 0xbf6e7fb2), TOBN(0x60c564a8, 0x4b87177a),
+     TOBN(0x36be1308, 0xc22c14c1), TOBN(0xeeda57e8, 0x9425b6bb),
+     TOBN(0x5ddaae14, 0x36af38c2), TOBN(0x1695464b, 0xecdc2911),
+     TOBN(0x4b795e1d, 0x161e13e7), TOBN(0x0f9abc20, 0x907e7069),
+     TOBN(0xfb3be618, 0x54522fe7), TOBN(0x9e2d0f37, 0x1938134e),
+     TOBN(0xb8dc7c36, 0xd292c6b0), TOBN(0xbafbf59c, 0xc1e77895),
+     TOBN(0x7d0132cd, 0x1b6c55f3), TOBN(0xefa02ed9, 0xf609f087),
+     TOBN(0x4bfe6aeb, 0x03519f9f), TOBN(0x248e19a0, 0xdab4c075),
+     TOBN(0x83ee803d, 0x69429f29), TOBN(0xdbbe31e2, 0x8190ce56),
+     TOBN(0x3ba735d2, 0x6b787a5d), TOBN(0xfa021185, 0x1818070c),
+     TOBN(0x9b653000, 0xa3683cee), TOBN(0xfc3c7907, 0xe9517ba2),
+     TOBN(0x6521c92f, 0x88d94f69), TOBN(0x3950e9e8, 0x7b52da8d),
+     TOBN(0xadb81700, 0x8ee64a88), TOBN(0x8ccbfa3c, 0xf73994fe),
+     TOBN(0xb780ab12, 0xb048e71e), TOBN(0x52879e7b, 0xe2aeb607),
+     TOBN(0xef04b1ed, 0x3237554f), TOBN(0xaeba6a96, 0xe1d5a5ef),
+     TOBN(0xedb58542, 0x266f7e93), TOBN(0x9a1b8057, 0x5ea02852),
+     TOBN(0x1963c6f2, 0x5beb3fbd), TOBN(0xf4183355, 0x1ad52473),
+     TOBN(0xca772e9e, 0x6faed2f4), TOBN(0x937eddd0, 0x3cf8fd1f),
+     TOBN(0xb3255376, 0xc1d49dac), TOBN(0x549c2119, 0xe670d3cc),
+     TOBN(0x10edbf39, 0x3b6cd199), TOBN(0xe9479223, 0x75644d6a),
+     TOBN(0x36cfba92, 0xd6e8cc36), TOBN(0xa37b1d91, 0xfe00d80a),
+     TOBN(0x3aadf918, 0xdeb5ef4a), TOBN(0x5bb2ca4d, 0xd3893df2),
+     TOBN(0xa776000e, 0x6569ab8b), TOBN(0x4fb2308f, 0x1cf64905),
+     TOBN(0x04da4d09, 0x273731c2), TOBN(0x1d4a61fe, 0x23c05518),
+     TOBN(0x201098a3, 0x0d5206e5), TOBN(0xd9a7ad4e, 0x06430c82),
+     TOBN(0x56df0d06, 0x36f7f462), TOBN(0x2c067f3d, 0x44c81042),
+     TOBN(0x01193bc9, 0xc453d28e), TOBN(0xcdf5af5d, 0x45ce6e64),
+     TOBN(0x9992ce1a, 0x0f7d8d12), TOBN(0xa7c46a61, 0x0e5e4542),
+     TOBN(0x3fcc0647, 0x057802ba), TOBN(0xa78f73d8, 0xc7dccbe2),
+     TOBN(0x67f37b94, 0xf138dc6d), TOBN(0x89330885, 0x650a9002),
+     TOBN(0xf661caf2, 0x68aa24c7), TOBN(0x47732bcd, 0xbf73c435),
+     TOBN(0xb9ba5f91, 0x3b04622e), TOBN(0x24265f73, 0x477d420a),
+     TOBN(0x5da6ddb0, 0x0d44cb89), TOBN(0x9f8cb8b6, 0x151fc40b),
+     TOBN(0x81b6956b, 0x9b9f2613), TOBN(0x37419206, 0xebb917df),
+     TOBN(0xdb9cfc16, 0x2bb7a709), TOBN(0x7a800aa3, 0xbacd3fb7),
+     TOBN(0xf8ea9359, 0xd93f6e1a), TOBN(0x729005d4, 0x3d41c118),
+     TOBN(0x4c293410, 0x7cb641af), TOBN(0x6b2b4671, 0x895e8e78),
+     TOBN(0x2a1251d0, 0x5958fad3), TOBN(0xb69bc2be, 0x78619fe4),
+     TOBN(0xd74df34c, 0xd911d318), TOBN(0x5def8378, 0x15102704),
+     TOBN(0xb19ea17a, 0x08268482), TOBN(0x14591196, 0x1c37e5d1),
+     TOBN(0xe0e12d2e, 0x7640df9c), TOBN(0x8fd6bd4d, 0x8c941274),
+     TOBN(0xc3f9f120, 0xdcd142b1), TOBN(0x106c49ac, 0x78dfe6b0),
+     TOBN(0x243c8e93, 0xcfd9b542), TOBN(0x6758062d, 0x0a2c5fe6),
+     TOBN(0xee5a99e8, 0x15f2f638), TOBN(0xb95b008d, 0x13979ab6),
+     TOBN(0x7fd03105, 0xacfcca6a), TOBN(0x6af914a4, 0xe4ced1b3),
+     TOBN(0x8bef3d0f, 0xa25f8627), TOBN(0x21bae309, 0xf9b2f931),
+     TOBN(0xe85dee2b, 0x2195a839), TOBN(0x46ad0ad9, 0xa3baeb25),
+     TOBN(0x6d8e40f8, 0x022b62a9), TOBN(0x4a6bbabf, 0x90b5cd33),
+     TOBN(0x53481e6b, 0xffa89bb2), TOBN(0xd674b3b3, 0x22003cc2),
+     TOBN(0xc71a0a85, 0x004a2aa6), TOBN(0x86df9697, 0xb5404657),
+     TOBN(0x407727f4, 0xc74e80cc), TOBN(0x39c13926, 0x950a7b08),
+     TOBN(0x26bee75a, 0xd74472a4), TOBN(0xbf7c4ea0, 0x2eb6f0d6),
+     TOBN(0x689a5de5, 0x608bea48), TOBN(0x5b383892, 0x29d513f8),
+     TOBN(0x49fee2c2, 0xda457cf9), TOBN(0x7fc0aee7, 0x62d523d3),
+     TOBN(0x5bf447de, 0xb636a46e), TOBN(0xda3efd98, 0x8194766e),
+     TOBN(0xa77c3ad2, 0xd382756d), TOBN(0xc0eaa9de, 0x0fa55315),
+     TOBN(0xe68d0a51, 0xb1df90e3), TOBN(0x0994e8c7, 0x01d1d8a7),
+     TOBN(0x4f898bc3, 0xa91bfed0), TOBN(0x1c2a3e46, 0xab6025df),
+     TOBN(0x37bd5c37, 0x8b269271), TOBN(0x4e07f5ca, 0x8b97f0af),
+     TOBN(0xe346b5aa, 0x97923d14), TOBN(0xa8549f61, 0x9e0bd9c4),
+     TOBN(0x78e59d6b, 0x40113a60), TOBN(0xe3015fb2, 0xed8a0fc6),
+     TOBN(0xfc56a08f, 0x8b43288a), TOBN(0xcbdb8cae, 0xcae6c18a),
+     TOBN(0xcb147c44, 0x5f4423db), TOBN(0xa6aaa6c9, 0x10f361c1),
+     TOBN(0x6be86c0c, 0x7caf702a), TOBN(0x2188e83c, 0x736f6dac),
+     TOBN(0x40b5ed25, 0x59ba2af9), TOBN(0x76211894, 0xab8018c3),
+     TOBN(0x0c1c532f, 0xf5b0b048), TOBN(0x7503aca9, 0xe3200521),
+     TOBN(0xb9325d85, 0xdfa7eb2d), TOBN(0xe6c25a00, 0x2edbb68f),
+     TOBN(0xf9ff5867, 0x8c673c89), TOBN(0x4925a046, 0x099c7bae),
+     TOBN(0x0b3bf59a, 0xdbb1e1b6), TOBN(0xc7e9d9f2, 0x30ae084f),
+     TOBN(0x70982396, 0x0fa1776f), TOBN(0xb2e1b98f, 0x624c897b),
+     TOBN(0xa9a6407d, 0x6c3534d5), TOBN(0x5e22319b, 0xa4dc3f63),
+     TOBN(0xc2f0bf3f, 0x2431dc01), TOBN(0x478f797d, 0xc2cfb315),
+     TOBN(0x6559f59c, 0x3b3ae0c5), TOBN(0x7e95aa62, 0xe18e74a8),
+     TOBN(0xf2a94006, 0xd3fce912), TOBN(0x7f1b57a2, 0xe1bd96ce),
+     TOBN(0x55028ad0, 0xa3d7b828), TOBN(0xadae7e92, 0x4f09fe92),
+     TOBN(0x2174c736, 0x757b418f), TOBN(0xd904ba43, 0x3661d54d),
+     TOBN(0x0281f912, 0x63218ecb), TOBN(0x5fd03ba0, 0xc50c8eb6),
+     TOBN(0x29626906, 0x896a5aea), TOBN(0xab4d3f27, 0xe55ee73f),
+     TOBN(0x3db1c43d, 0xedfd1991), TOBN(0x498cc31a, 0xa3f9c340),
+     TOBN(0xa43bdec1, 0x4fe75d33), TOBN(0x5b067dfb, 0x66ae5d4f),
+     TOBN(0x84581814, 0x464c8239), TOBN(0x2f10557f, 0x503a52ea),
+     TOBN(0x21c4c180, 0xa10fbb90), TOBN(0x33b191ee, 0xf79d5e02),
+     TOBN(0x6dee3881, 0xb499478e), TOBN(0x27dfef0b, 0xbfbd56fa),
+     TOBN(0x671a3dd7, 0x28be2d62), TOBN(0x06f2f4c2, 0x050897ff),
+     TOBN(0xd92bdab6, 0xb7c4587d), TOBN(0xd2253a16, 0xfd8d5160),
+     TOBN(0x64f6e4ae, 0xf1c098b1), TOBN(0x005a3939, 0x11ea7255),
+     TOBN(0x2ed4eb92, 0xdab542e5), TOBN(0x26920bc1, 0x50c5e874),
+     TOBN(0x93e8f58a, 0x5d0bc87c), TOBN(0xaa4d313e, 0xb2b29b4b),
+     TOBN(0x3e175dec, 0x01b2096f), TOBN(0x6c560972, 0x1cf31783),
+     TOBN(0x9d41aca2, 0x73b76f6b), TOBN(0xa2454cf5, 0x5f1d4b12),
+     TOBN(0xa5615196, 0x65b35eea), TOBN(0xf241e516, 0x70af4fde),
+     TOBN(0x5255e91b, 0x65061472), TOBN(0x6ef98d2d, 0x5bdbb257),
+     TOBN(0x0d1d1ab1, 0xc74c7b2c), TOBN(0x9ffb9fdf, 0x2e9febde),
+     TOBN(0x853f3b9f, 0x6c50bf24), TOBN(0x3d369594, 0x6fbd22bd),
+     TOBN(0x4d281126, 0xbcdad9a9), TOBN(0x99eb62b6, 0xdc46ddc1),
+     TOBN(0x5aa8c8b2, 0x4b10c402), TOBN(0x2e79f595, 0x473af61d),
+     TOBN(0x96153360, 0xce360f37), TOBN(0x16dffe22, 0x66bc29dd),
+     TOBN(0x35536eb1, 0x1137f9c3), TOBN(0xd636ecad, 0xe2a6a47a),
+     TOBN(0x83cdf214, 0xb499f840), TOBN(0x3642c77c, 0xd247f18c),
+     TOBN(0x4d906a2e, 0x916ef527), TOBN(0xadeb94d0, 0x293dc085),
+     TOBN(0x03a07801, 0x1491da3e), TOBN(0x177dceae, 0x0b84d2eb),
+     TOBN(0x61e5a3c1, 0x7b691e0c), TOBN(0x47d40bd7, 0xd172cea3),
+     TOBN(0x7d0646ad, 0x8ca76bce), TOBN(0x90b030a9, 0xc64d635f),
+     TOBN(0x71eca8e7, 0x97118df2), TOBN(0x2cd48f70, 0x3ac9536b),
+     TOBN(0x9ffd991d, 0x89fb4d72), TOBN(0xd49006bc, 0xebf781fb),
+     TOBN(0x688af07f, 0xd3e60da1), TOBN(0x5f74aa46, 0x619063b7),
+     TOBN(0x44fcbeb3, 0xa40d313f), TOBN(0x0ed5908b, 0x326faaa4),
+     TOBN(0xe836d537, 0xf41ec05d), TOBN(0x01eaf207, 0x221b0c32),
+     TOBN(0x1d6a0bb6, 0x72f8c904), TOBN(0xa6ef58b2, 0xdfd74641),
+     TOBN(0xbb855ceb, 0x811bd6cb), TOBN(0x7b1c8b71, 0x05408eab),
+     TOBN(0xd24d709e, 0x4187fb7f), TOBN(0x283d647d, 0x8b30a9be),
+     TOBN(0x6d9d3793, 0xf9f0d6e6), TOBN(0x02fc3ddb, 0xb1c06b19),
+     TOBN(0x8ff86793, 0x94d9abec), TOBN(0x1f20bba2, 0x24705873),
+     TOBN(0x74eebc12, 0x0021b290), TOBN(0xd859521e, 0x35b6c157),
+     TOBN(0x2201aa41, 0x431aea79), TOBN(0x79c1caaf, 0x90da1a75),
+     TOBN(0xcd6abab7, 0x6e412a6a), TOBN(0x82399139, 0xb4c58b05),
+     TOBN(0xdf416966, 0xa3b55538), TOBN(0x2b2d546f, 0x679882d3),
+     TOBN(0x17425cbc, 0xf9145657), TOBN(0x3cc6735f, 0xe1b8247e),
+     TOBN(0x13e50c56, 0x57edd04c), TOBN(0xc8723137, 0x1b85b7cb),
+     TOBN(0x907b5b02, 0xdc0ab9d5), TOBN(0x5617fb7f, 0x4ab23b78),
+     TOBN(0x7ae8ff03, 0xe8f449cd), TOBN(0x86d3ff17, 0x174e0e22),
+     TOBN(0x22cb7f69, 0xbf1e9f8d), TOBN(0x12f0abbe, 0x0b0c62f0),
+     TOBN(0xc8315981, 0x537f658c), TOBN(0x43da2770, 0xc467f2b4),
+     TOBN(0x3ef9bb81, 0x5b9e88ef), TOBN(0xb8526318, 0x3a8e51f2),
+     TOBN(0x2e47cb7f, 0xf8d744ac), TOBN(0x63d6dc16, 0x510aaa7c),
+     TOBN(0x54da7cdb, 0xb40ccc41), TOBN(0xdecbe5fd, 0x402b2ad9),
+     TOBN(0x14c6f15c, 0x34c8f225), TOBN(0x6d8b2342, 0xc6559496),
+     TOBN(0xa4b72281, 0x66fea635), TOBN(0x55f5c27f, 0x22f248a8),
+     TOBN(0x3ced1483, 0x0959cd01), TOBN(0xcc6469db, 0xb53bdf42),
+     TOBN(0x2bb2346f, 0x1e460645), TOBN(0x4d8573c6, 0x9d7535e7),
+     TOBN(0x988cddd5, 0x49cd2d68), TOBN(0x785c4a70, 0xb9835538),
+     TOBN(0xb08337b3, 0x1f6e396c), TOBN(0x6166b21e, 0x49a782f3),
+     TOBN(0x1ac6198b, 0x8ec9b974), TOBN(0xee2e3446, 0x0bb44d3d),
+     TOBN(0xdb283740, 0x35039dd9), TOBN(0x7c708f95, 0x29f5c692),
+     TOBN(0x8914cce0, 0x98ddb466), TOBN(0x8bb1b9f1, 0xd446f3cf),
+     TOBN(0xa9dea222, 0xee0933a3), TOBN(0x2538bd43, 0x4b26049e),
+     TOBN(0x18741aca, 0xbdcafae2), TOBN(0xe0f830f7, 0x16b0f4bb),
+     TOBN(0x0479ec95, 0x902caefa), TOBN(0x1f858937, 0xdcda9e64),
+     TOBN(0xe75b4f7b, 0x515c4089), TOBN(0xb78afde4, 0x2eb91b51),
+     TOBN(0x1eebe3e9, 0x18949935), TOBN(0xde8deaa9, 0xba092037),
+     TOBN(0xd43cf4ef, 0x93609411), TOBN(0xe0fdb1e4, 0xc2d7b76e),
+     TOBN(0x1d3191a5, 0x4e34b4bd), TOBN(0x106d92f1, 0x9ccc4c26),
+     TOBN(0x1a404ef6, 0x29a2a6d1), TOBN(0x3338bc9c, 0xc598f481),
+     TOBN(0x3945e39d, 0xe3fcbf71), TOBN(0x123b082c, 0x9c89ab61),
+     TOBN(0xc7477f77, 0x0f9f3c37), TOBN(0x408c0c7a, 0x7dbcc077),
+     TOBN(0x6c4d99f5, 0x3654f98c), TOBN(0x276a007a, 0x05299a1a),
+     TOBN(0xabd4b8ea, 0x23e2d7d0), TOBN(0xe05a5f3a, 0x86017545),
+     TOBN(0xde3b885c, 0xa11b03cd), TOBN(0x46ef0755, 0x8df5d64e),
+     TOBN(0x112a49d6, 0xbf3f085d), TOBN(0xf6ebf441, 0x198ff32f),
+     TOBN(0x581c00d8, 0x7feae481), TOBN(0xf2b43827, 0xcfde5b2f),
+     TOBN(0x3ceb7f8f, 0x9b7358f2), TOBN(0x95761fbd, 0x55fe7117),
+     TOBN(0x305836fa, 0xdc04773c), TOBN(0x66324504, 0xb3c4873c),
+     TOBN(0x5d878c1f, 0x55b130de), TOBN(0x96e9b28c, 0x8ad49a9b),
+     TOBN(0xd1a707b8, 0x76d70429), TOBN(0xaff33f93, 0xaa402e90),
+     TOBN(0x733d6256, 0xedbfb28f), TOBN(0x9e421a7c, 0xa75d2342),
+     TOBN(0xdf86b254, 0xc02e49c1), TOBN(0x6bb53867, 0xb56d308a),
+     TOBN(0x771dde4b, 0x73f29561), TOBN(0x96eaf73e, 0x8bf28e5f),
+     TOBN(0x9b1ee6be, 0x06fbb550), TOBN(0xe09fec77, 0x97d4a4e8),
+     TOBN(0x93bdcd60, 0xd5aa84fd), TOBN(0x3fa8d3a0, 0xd457ab9c),
+     TOBN(0x315b32b1, 0xa0a2e52c), TOBN(0xe7e50b2d, 0x3bbcb61d),
+     TOBN(0x8a55cc0e, 0x5e5c6e11), TOBN(0xc2bfa998, 0x961295ef),
+     TOBN(0x4a5ab3bb, 0x66e996d1), TOBN(0x22c42e4f, 0x4886a421),
+     TOBN(0xa0cdd364, 0x4850e0a4), TOBN(0x7682d38d, 0xc64ed713),
+     TOBN(0xe31575c2, 0xa2c539e4), TOBN(0x0bac5dcd, 0xa756daf9),
+     TOBN(0xe917cecf, 0x91f55a12), TOBN(0x1e96433b, 0xe96f6299),
+     TOBN(0xeec7c71c, 0x3700d8fb), TOBN(0x9a1d2965, 0xdc9b4444),
+     TOBN(0x3d2c6970, 0xcf74f19c), TOBN(0x3b444c48, 0xac5e0d6b),
+    },
+    {
+     TOBN(0xe563cefd, 0x8ccb854c), TOBN(0xf5452cdb, 0x65b0c45a),
+     TOBN(0xb3c78769, 0x9c37f743), TOBN(0x34e9d192, 0x95d444ab),
+     TOBN(0x29347946, 0x52ff26b7), TOBN(0x70d6ecfa, 0x9b94d642),
+     TOBN(0x7d201858, 0xfdaffb8f), TOBN(0xc288719d, 0x45dcdc71),
+     TOBN(0xc695469d, 0x0728a2eb), TOBN(0x7b46244e, 0xc433d11c),
+     TOBN(0x4a8b99ba, 0xf106c08e), TOBN(0x7989794f, 0x63422083),
+     TOBN(0x82218867, 0xd4fc5696), TOBN(0x6b021f28, 0x3c79cdb8),
+     TOBN(0x5ff7bbea, 0xb26d5049), TOBN(0xb78611ca, 0xa7261628),
+     TOBN(0x5a75f961, 0x531313d7), TOBN(0x85a1f4db, 0x66dcdc9e),
+     TOBN(0xae3026b9, 0x6460e991), TOBN(0x7d467bef, 0x17ecf7cc),
+     TOBN(0x8a0dbf67, 0x05118708), TOBN(0x54bfa368, 0xf3b2f1c9),
+     TOBN(0xa9fc9d5c, 0xf2c0e4e0), TOBN(0xa8c2ad11, 0x5e93611b),
+     TOBN(0x3ef1faf0, 0xaa1256bd), TOBN(0x0f224545, 0x9e4631de),
+     TOBN(0x69cb9800, 0xde9c2676), TOBN(0x26019816, 0x95782b24),
+     TOBN(0x945c172c, 0xa66c0ccd), TOBN(0x6c25f635, 0xb440719a),
+     TOBN(0x917d5dba, 0xa49f681a), TOBN(0xc0cad047, 0xb2dc5df4),
+     TOBN(0xd45bcf4c, 0x5960ef1c), TOBN(0xbabcb16d, 0x8c6979d5),
+     TOBN(0x8e3be750, 0xae9090d6), TOBN(0x9481d261, 0xac0eb728),
+     TOBN(0x46b436cd, 0x0d6a7d46), TOBN(0x6eb1a6a3, 0x1f976501),
+     TOBN(0x5984ffa2, 0xdbe1064f), TOBN(0xe6575fb1, 0xf809dc20),
+     TOBN(0xf0426d80, 0x4d974a81), TOBN(0x61304f0f, 0x97a74be6),
+     TOBN(0x2346ff98, 0xa9486531), TOBN(0xa1242cca, 0xf53d781a),
+     TOBN(0x482f03df, 0x97355f15), TOBN(0xc607ed33, 0xbd6058cf),
+     TOBN(0x03bc8cd4, 0x68aefe28), TOBN(0xa6e7de5a, 0x851307e4),
+     TOBN(0x2c07df0f, 0xc6af7d44), TOBN(0x310b251f, 0xb15a9188),
+     TOBN(0xd42661ce, 0xd3e15c2f), TOBN(0x5198fd90, 0x1b4d8313),
+     TOBN(0x7a6062cd, 0xda8368a1), TOBN(0x1a905d11, 0x5e9c2542),
+     TOBN(0x1d752b70, 0xdae37cee), TOBN(0x3ed8c1a5, 0x16bf84ca),
+     TOBN(0x5190fb0f, 0xeecc2f22), TOBN(0x3df210f3, 0x698d8e60),
+     TOBN(0xcce57d3a, 0xf5f3ce72), TOBN(0xb2fb6223, 0x312b8fc6),
+     TOBN(0x79947005, 0x71867c84), TOBN(0x141cd92c, 0xbe139ebe),
+     TOBN(0x415efc9e, 0x5de7944e), TOBN(0xae9ee919, 0x45821058),
+     TOBN(0xd696e1d9, 0x5bf363dc), TOBN(0x6a1bcfc0, 0x8251449c),
+     TOBN(0xa1b82dff, 0xa5fa53e9), TOBN(0x6c56b5be, 0xeef05378),
+     TOBN(0xaf9efe4c, 0xc0e74dc3), TOBN(0x3d9a7ae9, 0xe5c1f1a0),
+     TOBN(0x34b38577, 0x2823c3e5), TOBN(0x69f297dc, 0x41fbabac),
+     TOBN(0xf01aff98, 0xd74c5a65), TOBN(0x97993104, 0x1951a915),
+     TOBN(0x8b211915, 0x723096a6), TOBN(0xf85910c4, 0xa769ef1f),
+     TOBN(0x30cefb9e, 0x8ddc0eb4), TOBN(0xd5957eef, 0xbb09607b),
+     TOBN(0x2e139c9c, 0x2355b499), TOBN(0x5749531d, 0xc1789013),
+     TOBN(0x1603ca64, 0x5475f2d2), TOBN(0x57190e0e, 0x0a336508),
+     TOBN(0x2203b703, 0xcea7d558), TOBN(0xf16eba4d, 0xfb5503e3),
+     TOBN(0x62e2ce3d, 0xb7344a98), TOBN(0xebf5b243, 0x9a4efa7a),
+     TOBN(0x96212455, 0x1c914064), TOBN(0xd2c5e31c, 0xbe5bbc07),
+     TOBN(0x2b5f2f77, 0x06c30b28), TOBN(0x0931841d, 0xbc9823d3),
+     TOBN(0xd16fb08b, 0xadfa6fdb), TOBN(0x8892bae2, 0xd6fd441e),
+     TOBN(0x3fc64630, 0x2e576983), TOBN(0x08c60fb6, 0x07b05529),
+     TOBN(0x32b283b1, 0x7afc1d4d), TOBN(0xc9c56965, 0xa2f0e37f),
+     TOBN(0x8e719178, 0x4644e173), TOBN(0x4c2a11ec, 0xf88b43ff),
+     TOBN(0xb13644e6, 0x7d3ddbb3), TOBN(0xd4746056, 0xc3d8703c),
+     TOBN(0x6611395f, 0x55dca667), TOBN(0x63596712, 0x27c91d73),
+     TOBN(0x4ca68a87, 0xea2ff489), TOBN(0x2864a816, 0x337adc1d),
+     TOBN(0x8aa830ae, 0x224d4f21), TOBN(0xda6c122e, 0x9f7845dc),
+     TOBN(0xb0c61ffc, 0xfb240793), TOBN(0xf4df6842, 0xce8580e9),
+     TOBN(0x94372aaa, 0x0a990dc7), TOBN(0x42968cd3, 0x5ce1aa24),
+     TOBN(0x177c5ff0, 0x4df363a5), TOBN(0xa8c3f737, 0x68c4546f),
+     TOBN(0xc69750d5, 0xbd21c524), TOBN(0xbf3b4857, 0x22a6c4ae),
+     TOBN(0xcefcbb98, 0xe2883a1d), TOBN(0x6ffef743, 0xae13f22b),
+     TOBN(0x6316ba60, 0x5defea01), TOBN(0x0a89e6a7, 0x4ba63810),
+     TOBN(0x7f9af1de, 0x15ab0e11), TOBN(0x6247ca15, 0x385911c9),
+     TOBN(0x6f7b1a6a, 0x32f9eaf5), TOBN(0x2c440f94, 0xacfc13dc),
+     TOBN(0x2cf39bc5, 0x66b18adf), TOBN(0xb9939fe8, 0x9f01533f),
+     TOBN(0x031c4553, 0x383a6450), TOBN(0x16d96ad3, 0xf0102087),
+     TOBN(0xcbd6fa95, 0x501f1360), TOBN(0x667d3ea0, 0x65f96c08),
+     TOBN(0xa5a7cbfa, 0x68a1a680), TOBN(0xf131d779, 0x42041db7),
+     TOBN(0xbefee3ac, 0xd85d377f), TOBN(0x6d0ed6b7, 0x3b62dfa2),
+     TOBN(0xef683f0f, 0x1baacfbd), TOBN(0xc586c4f2, 0xc976cebd),
+     TOBN(0x3a4120dc, 0x3b163339), TOBN(0x9ac9b950, 0xc79e5c1f),
+     TOBN(0xaf1ff35f, 0xe294926a), TOBN(0x2703bab8, 0xa2103168),
+     TOBN(0xc645560a, 0x658a52bf), TOBN(0x5ff3ccd9, 0xe466fd97),
+     TOBN(0xe62fdc01, 0x54867f14), TOBN(0x435ef950, 0x9cdba39e),
+     TOBN(0x2a7bbffd, 0x92828acc), TOBN(0xe7538fdb, 0xfe763981),
+     TOBN(0xedf45173, 0x8bfe9773), TOBN(0xd187fa01, 0x471b8b9c),
+     TOBN(0x34506c35, 0x78fa54df), TOBN(0x73cab9fd, 0xc2767589),
+     TOBN(0xf8f76c65, 0x6726f305), TOBN(0xea45012d, 0x8de332b2),
+     TOBN(0xb746f40d, 0x87970e03), TOBN(0xb2b2279a, 0x1ba8fbd6),
+     TOBN(0x79cdc610, 0x21147dbc), TOBN(0x738ef680, 0x9939a3cc),
+     TOBN(0xd66d6ce6, 0x8101bd8b), TOBN(0x65612acb, 0x09323caa),
+     TOBN(0x6874b372, 0x10310a29), TOBN(0x3cf30f0a, 0x5ee9ecfa),
+     TOBN(0x4e1026ad, 0x8cfe1df8), TOBN(0x75a153f7, 0xd5989af5),
+     TOBN(0xc362ccee, 0x8b8e0c49), TOBN(0x8adfc0d2, 0xb533f3dd),
+     TOBN(0xe02ab03b, 0xa109572e), TOBN(0x06efacdc, 0xfd3066ec),
+     TOBN(0xf136a8ba, 0x3fa28700), TOBN(0x48a1e987, 0x308cceb9),
+     TOBN(0xe8ee7c03, 0x68253387), TOBN(0x47febbe8, 0xc2b463c7),
+     TOBN(0x485195f2, 0x39a5c4d3), TOBN(0xf42e000e, 0xa26241ec),
+     TOBN(0x08c64f90, 0xcd05368d), TOBN(0x46fbd381, 0x857cdbdb),
+     TOBN(0xf711df8b, 0x4c7e16ae), TOBN(0x95637e46, 0xe4edea42),
+     TOBN(0x2df8b206, 0xad171465), TOBN(0xa31ea895, 0x4bccedce),
+     TOBN(0x28dbcb77, 0x50743bb6), TOBN(0x13d12f8e, 0xf9cf84b9),
+     TOBN(0x39e3d3af, 0xc8f7d408), TOBN(0x5824117f, 0xeba591d4),
+     TOBN(0xd8ef7c9a, 0x1bead2d6), TOBN(0x9003a559, 0xcaf53dd7),
+     TOBN(0x33b2365c, 0x174cb9a9), TOBN(0x1149d080, 0xadb33afb),
+     TOBN(0x55231d00, 0xaea9bd3c), TOBN(0x07e107c9, 0xfdf3f015),
+     TOBN(0xf535947d, 0xec9d8fce), TOBN(0x8b64ed8a, 0xbba349a7),
+     TOBN(0xdd5881fd, 0x049301df), TOBN(0xefac9c43, 0xe6490fd0),
+     TOBN(0xd9902852, 0x73740a78), TOBN(0x6eef3724, 0x942c326c),
+     TOBN(0x5671a6e9, 0x5cfb3c8c), TOBN(0x040aabd2, 0x0ea29721),
+     TOBN(0x24e92ca6, 0xeac8da18), TOBN(0xc34d3d79, 0xa31170c3),
+     TOBN(0xf81dd15f, 0xb061e416), TOBN(0xff7be70e, 0x85f80af0),
+     TOBN(0xa9faba4b, 0xade45cd4), TOBN(0x42a6ab05, 0x505fddd4),
+     TOBN(0x17d5821d, 0x0a793534), TOBN(0x9e094e54, 0xce0ade43),
+     TOBN(0xa127fb6d, 0xc42cb4d2), TOBN(0x43865428, 0xdb12dc99),
+     TOBN(0xb6b1b347, 0x59e3bfc1), TOBN(0x0b0076a9, 0x1ec5b810),
+     TOBN(0xbf2dd17a, 0xa6864982), TOBN(0x0c45947f, 0x9d523c87),
+     TOBN(0x9f53372f, 0x4c5dd59e), TOBN(0x3d0ceaea, 0xca5ce09f),
+     TOBN(0xf3ff88e8, 0x7c0337fb), TOBN(0xb4fa4593, 0xfaa022c7),
+     TOBN(0x575240a7, 0xd65ea54d), TOBN(0xa4ec0a39, 0xadb92fb0),
+     TOBN(0xc20e737c, 0x79429eb1), TOBN(0xcea931d1, 0x69addec4),
+     TOBN(0x7a29011f, 0x3e09f46a), TOBN(0x9c36865e, 0x0e578a5b),
+     TOBN(0x8746ea51, 0x71d805f4), TOBN(0xf024de85, 0xe12d3024),
+     TOBN(0xc397b46c, 0x15a7f6be), TOBN(0x612db6fb, 0x1b0580d7),
+     TOBN(0xe5342f76, 0xf736d087), TOBN(0x65276853, 0x8c1e844c),
+     TOBN(0xedf48adc, 0x113841a5), TOBN(0xc21b67e1, 0xe5c482f0),
+     TOBN(0xe43b0138, 0x684a540b), TOBN(0xc4f2782b, 0xa5d4b266),
+     TOBN(0x184e240c, 0x397f3664), TOBN(0x968e89e7, 0x0d8788f8),
+     TOBN(0xec3eba1a, 0x377e18bf), TOBN(0x4d03cbbc, 0x36002652),
+     TOBN(0x21eedee7, 0x1005a953), TOBN(0xc178ddf1, 0x75ba987e),
+     TOBN(0xd0d577f6, 0xc4ba43f6), TOBN(0x9486f46c, 0x4d6f24fd),
+     TOBN(0x3d33c574, 0xc5421895), TOBN(0x5be6cb4c, 0x842e52ab),
+     TOBN(0x3809690d, 0x9debc9ff), TOBN(0xe4b1c692, 0xa84a5b6f),
+     TOBN(0x58b966ad, 0xd7e18b57), TOBN(0x7ff0b61e, 0x77c94715),
+     TOBN(0x0e295883, 0xf06add82), TOBN(0x7c3c04fd, 0x65c7f5a4),
+     TOBN(0x4ea92660, 0x60223be5), TOBN(0x5d843a57, 0x89262bfd),
+     TOBN(0x35bf4aef, 0x36da11c0), TOBN(0xa6692f14, 0xaf859eb1),
+     TOBN(0xca1fc13b, 0xa12fdf41), TOBN(0xd798c04b, 0x8224f5d2),
+     TOBN(0x22f4594e, 0x1dd5872b), TOBN(0xdee12df5, 0x1bddfda8),
+     TOBN(0x96473ff0, 0xed83420a), TOBN(0xf41cf1c7, 0x8daa27f4),
+     TOBN(0x2772cd56, 0xaecefd8a), TOBN(0xd5ddaf18, 0x4902b47f),
+     TOBN(0xff77551f, 0xc0798101), TOBN(0x8baa01d6, 0x26946bda),
+     TOBN(0xd0087e47, 0x100525f2), TOBN(0x521d6254, 0x4c0de308),
+     TOBN(0x4a0f45eb, 0x9bbce049), TOBN(0x5ee33cbe, 0xa6c6b96e),
+     TOBN(0x9a6af4b7, 0xd6a22345), TOBN(0x0d0d35e7, 0x38b1b406),
+     TOBN(0x9e71252d, 0xbbedc29b), TOBN(0x3aa70bb6, 0xcad1455e),
+     TOBN(0xa406fb7a, 0x42a1778c), TOBN(0xd94f9646, 0xf0897613),
+     TOBN(0x5256370f, 0xf57f66c8), TOBN(0x95891e35, 0x4432f679),
+     TOBN(0x75d6423a, 0xbcb6b3d3), TOBN(0x79d9ea01, 0x2367483f),
+     TOBN(0x1e36ccc6, 0x9efb0473), TOBN(0x3e64b034, 0xdfdc0cec),
+     TOBN(0x13bfd326, 0x028bb238), TOBN(0x171e9d96, 0x209edd95),
+     TOBN(0xda258380, 0x07b22424), TOBN(0xe31e97f6, 0xd41b8023),
+     TOBN(0xdd4ed390, 0x7269cecd), TOBN(0x810fb3c8, 0x12d5cec6),
+     TOBN(0x2f956519, 0xbabeec88), TOBN(0xb0350c52, 0x455baf52),
+     TOBN(0xa7fb548a, 0x48d5abf1), TOBN(0xcb81bd0c, 0xca5e2d9f),
+     TOBN(0xda5ecd39, 0xa6d17b19), TOBN(0xd2588bab, 0x508e5149),
+     TOBN(0x1a30cff5, 0xc3e23cfd), TOBN(0x2dd398b4, 0xf89f8712),
+     TOBN(0x2a911800, 0x5b304005), TOBN(0xd091be7a, 0xd9dece69),
+     TOBN(0x147e93da, 0xf6cabc89), TOBN(0x7eac2018, 0x44935824),
+     TOBN(0xd4aaf2be, 0x32f5de9b), TOBN(0xe302bc41, 0xd9396cd1),
+     TOBN(0x3c2794cf, 0x2c069d1a), TOBN(0xf9197eaa, 0xa9d433ae),
+     TOBN(0x98f822ef, 0x4445e8c2), TOBN(0xc578360e, 0x1383ece8),
+     TOBN(0xa5372c12, 0x01869457), TOBN(0x1c6ed00d, 0x787d6644),
+     TOBN(0x77fb08cd, 0x86531814), TOBN(0xeff6ee26, 0x63a70db8),
+     TOBN(0x980be153, 0x80976119), TOBN(0x534a09bd, 0xd69d60c5),
+     TOBN(0x71a58b0c, 0x759dba20), TOBN(0x34d5f06c, 0x679c0b40),
+     TOBN(0xdc0e7e5f, 0xceed2f9f), TOBN(0xaaa5996e, 0x48808edb),
+     TOBN(0x8ca96ff0, 0xbcdd88e5), TOBN(0x91b02d67, 0xc37c2b46),
+     TOBN(0xbe4f3948, 0x95526319), TOBN(0x4315c7f2, 0x89be56d1),
+     TOBN(0xa312a3c0, 0xdc85bba1), TOBN(0x3328fa8e, 0x431ca797),
+     TOBN(0x5438bf1c, 0x68fd219a), TOBN(0x98812c6f, 0x85837d74),
+     TOBN(0xe88c4913, 0xf8c96d49), TOBN(0xcc62e79c, 0xc2442aca),
+     TOBN(0x4ef3c7d4, 0x046655f1), TOBN(0x04a362ed, 0xdadab1ea),
+     TOBN(0x975e2f3c, 0x30a199cf), TOBN(0x831e04a9, 0x014a165a),
+     TOBN(0x1e1d3c53, 0xaa126719), TOBN(0xc42661e0, 0x1bf707a0),
+     TOBN(0x295b0738, 0xaa2da264), TOBN(0xb45f5ed8, 0x65d4ba34),
+     TOBN(0x27fb5a12, 0x9f3938fa), TOBN(0x25fba614, 0xcb26f86c),
+     TOBN(0x6bd41981, 0xcf3c1c4d), TOBN(0xd6f9239c, 0xa0dedafd),
+     TOBN(0x46882526, 0xae55b97f), TOBN(0x8e6fa994, 0x81b628d4),
+     TOBN(0xbdb314dd, 0xdc0aa158), TOBN(0x35343678, 0x12ba2a17),
+     TOBN(0xac018e83, 0x32e2e431), TOBN(0x43a64e35, 0xe65cc63e),
+     TOBN(0x887f3a2a, 0x0b6603ea), TOBN(0xe015426c, 0x76b2673f),
+     TOBN(0x59dc5530, 0x27edfe8a), TOBN(0xea9eacf3, 0x68d9ebf3),
+     TOBN(0x40301c8e, 0xcc3e07ca), TOBN(0xd8cb9b5b, 0x0f57a2e6),
+     TOBN(0x542e6b52, 0x60ec5864), TOBN(0xb8791dd6, 0x17f6affe),
+     TOBN(0x6735bd1c, 0x798d9993), TOBN(0x006d8b25, 0xd5da393c),
+     TOBN(0x1d675bdb, 0x49e6d0d2), TOBN(0x331d9a10, 0x8607f99e),
+     TOBN(0x4ff8ab74, 0x9dc4cd07), TOBN(0xa87d4ae1, 0x64ea3192),
+     TOBN(0xdde0d92e, 0x41196b5b), TOBN(0xa15ad47b, 0xb2e010eb),
+     TOBN(0x23e6003f, 0xa5522a75), TOBN(0xc6ef3f1b, 0x84afa749),
+     TOBN(0x9a723f75, 0x146d10a3), TOBN(0x5fa99480, 0x119106b0),
+     TOBN(0x01d500db, 0xc0013dba), TOBN(0x10b30ada, 0x548edbe0),
+     TOBN(0xb2eb046e, 0xb04ffc6b), TOBN(0xa57088f3, 0x64f25ee2),
+     TOBN(0xc0c919c3, 0x83a068a3), TOBN(0x8139559d, 0xfbde282f),
+     TOBN(0x4e2b5d13, 0x9fec9a99), TOBN(0x53bad712, 0xfbefa7e6),
+     TOBN(0xa6befe0d, 0x2860bd4f), TOBN(0x6ea0ae15, 0x0011bd15),
+     TOBN(0xc1ef3463, 0x2bce3779), TOBN(0xc09ecb30, 0x5d742dbb),
+     TOBN(0x29526afd, 0xf73db19d), TOBN(0x7c02c905, 0x6a029a40),
+     TOBN(0xa778460f, 0xde5a48ba), TOBN(0xda05993e, 0x77c105f6),
+     TOBN(0xb6d599f9, 0xc9ddece9), TOBN(0x9f9df668, 0x0cfc33ca),
+     TOBN(0xdcd8ef4f, 0xa0aa67a8), TOBN(0x31277019, 0xc3f4d178),
+     TOBN(0x98e05abf, 0x53e86ae1), TOBN(0xc1dc4d90, 0x3850830d),
+     TOBN(0xbd7fd806, 0xe06bc33c), TOBN(0x1ac330d6, 0xacf1286f),
+     TOBN(0x28ce2303, 0xe1588c1e), TOBN(0xdc25e54b, 0x1b7e9c19),
+     TOBN(0x11e51e49, 0x4b7149f2), TOBN(0x551b8391, 0xb5c7fa25),
+     TOBN(0xa2fc251c, 0x1bf69873), TOBN(0x099b7b53, 0x2aec1574),
+     TOBN(0x9ff98156, 0x7c53296a), TOBN(0xaf3f8d08, 0xa2dc60de),
+     TOBN(0x18dd295c, 0x59b72d6c), TOBN(0x165c9063, 0xe75f44fc),
+     TOBN(0x9046ee7c, 0x427a0c55), TOBN(0x317ea24d, 0xc79ffdb3),
+     TOBN(0x6835a315, 0x0ef0447d), TOBN(0xb2b9c786, 0x8068e7c7),
+     TOBN(0xe6352714, 0x0e646af5), TOBN(0xc5554a91, 0x442baaa0),
+     TOBN(0x671febc5, 0x6d0ba1ea), TOBN(0x44f9ef7b, 0x0cf649ed),
+     TOBN(0x4aa0cd61, 0x0c1dac6b), TOBN(0x865f3c23, 0x6e393e68),
+     TOBN(0xf6886bcd, 0xa71dee29), TOBN(0x934b0455, 0xda44ffae),
+     TOBN(0xda7621c4, 0x016d6039), TOBN(0xf36c41bf, 0x3ad35493),
+     TOBN(0x9063135e, 0xe5f6ab8d), TOBN(0xb0e8eaba, 0x47bdc0a8),
+     TOBN(0x62530616, 0x4c737cf3), TOBN(0x8046423e, 0x64f6b6cb),
+     TOBN(0x11e50ad7, 0x7958e3dc), TOBN(0x4dab4e16, 0xb57234ab),
+     TOBN(0x6ccfe2c6, 0xe0916210), TOBN(0x4d5dbc3b, 0x80f49d40),
+     TOBN(0x2b8ff368, 0xef1b2b1b), TOBN(0xf2afb326, 0x752fea2a),
+     TOBN(0xffa48ea7, 0x0246e36b), TOBN(0x3a4bae9b, 0x589b7444),
+     TOBN(0x80ff984a, 0x6ff3efcf), TOBN(0x7af53f30, 0x56b77b47),
+     TOBN(0x1f1c33b0, 0x9320cae6), TOBN(0xce1f1c48, 0x26fc4ad4),
+     TOBN(0x9cac662b, 0xad350ee5), TOBN(0xf4c72fff, 0xe27a7dbd),
+     TOBN(0xd766f986, 0x703184e5), TOBN(0x36d3efd5, 0x7c5b241e),
+     TOBN(0xd4d6e358, 0xf7ff5804), TOBN(0xa832b302, 0x8f5e0bf6),
+     TOBN(0x4b3d73f7, 0x453d9a22), TOBN(0xb4dae072, 0xdf938705),
+     TOBN(0x6bff7b2e, 0x92401620), TOBN(0x96b8494e, 0x9bfa61cd),
+     TOBN(0x4bcda341, 0xb74dc1e5), TOBN(0x383fe3d2, 0xc19c393d),
+     TOBN(0xa375fb70, 0x077e8821), TOBN(0xea35e04b, 0xc17eb9bc),
+     TOBN(0x941d21ba, 0x7c4dd076), TOBN(0x916c0a59, 0x3d0c3d8a),
+     TOBN(0x2c1304e3, 0x15b2cf47), TOBN(0x9233ebf3, 0xd0c955c0),
+     TOBN(0x77acdd07, 0x2b2fc935), TOBN(0xd71b6a7a, 0xc04276bf),
+     TOBN(0x789ea49b, 0xd2ee8d68), TOBN(0x89552b46, 0x0a84a920),
+     TOBN(0xe629d5de, 0x1a4ea456), TOBN(0xddfefe87, 0x22ddd405),
+     TOBN(0x3d56f697, 0x1cdb9e7b), TOBN(0x95a861b0, 0xa8bf715b),
+     TOBN(0xb01248d6, 0x7896c993), TOBN(0x3c4e3d98, 0x01a3085c),
+     TOBN(0x674939e1, 0x9085b360), TOBN(0xae67dea9, 0xb589a287),
+     TOBN(0xc563856f, 0x2bfdcfc9), TOBN(0x62fa9a80, 0x313b685d),
+     TOBN(0x36ff33d9, 0x7ad501d9), TOBN(0xf8bab4dd, 0x730ab349),
+     TOBN(0x18fd59f3, 0xc46ba69d), TOBN(0x81e08665, 0xe65278e9),
+     TOBN(0x5a5e803f, 0xeb8a1e84), TOBN(0x5b4eef35, 0x47243604),
+     TOBN(0x0ee71ee0, 0x393d6cdf), TOBN(0xde4d9dea, 0xc3a9c6db),
+     TOBN(0x0c14c376, 0x64466b53), TOBN(0xc2ce9642, 0x89e3b45e),
+     TOBN(0x6aa8012f, 0x54a2de21), TOBN(0x519759c1, 0x29b6bc2c),
+     TOBN(0x17768527, 0xe4667322), TOBN(0x09fdfe4d, 0xac83b2ea),
+     TOBN(0xd422125d, 0x04a0d5f5), TOBN(0x02e8ff96, 0x2b86b310),
+     TOBN(0xf033628d, 0xd7ee97af), TOBN(0x778a846c, 0x7d72e0e6),
+     TOBN(0x06fde613, 0x882f63d6), TOBN(0x9e258b0d, 0x8d434f14),
+     TOBN(0x5cdda529, 0xccdcd600), TOBN(0x37038b38, 0x033c4535),
+     TOBN(0xd6a1d639, 0x391c1d7d), TOBN(0x4f6489e4, 0x31d4ce6b),
+     TOBN(0xd1b82f17, 0x5754e08c), TOBN(0x7df268ee, 0x75db7bd6),
+     TOBN(0x1e4a1202, 0xad14dcfa), TOBN(0x7ab92ce2, 0xccfb9b77),
+     TOBN(0x61388e03, 0x23aef997), TOBN(0x9981f5bf, 0x06440ce3),
+     TOBN(0x8d7631da, 0xc67d0edd), TOBN(0xc6ea593f, 0xc0a93516),
+     TOBN(0x064a06e0, 0xee841b38), TOBN(0x0d1d4f57, 0x521ce83f),
+     TOBN(0xf7a0e0c3, 0x70df2613), TOBN(0x1506cccb, 0x84c071ab),
+     TOBN(0x42a138ec, 0x328565e9), TOBN(0xe16b4578, 0xb8130d16),
+     TOBN(0x0628ff22, 0x45ba251a), TOBN(0x016a84ca, 0x210e22e8),
+     TOBN(0x8ba14bb4, 0x94592d43), TOBN(0xffee4308, 0x785274a5),
+     TOBN(0x01fc21ab, 0x01354b75), TOBN(0xc37ce45f, 0x7e424674),
+     TOBN(0x71e153af, 0xa7fe2735), TOBN(0x000fcee9, 0xc307721f),
+     TOBN(0x3b189004, 0x805b56e3), TOBN(0x2f1435aa, 0x7f504d9b),
+     TOBN(0xd9aa1eba, 0xa083bd72), TOBN(0xf1145036, 0x720ccf3d),
+     TOBN(0x95b29e27, 0x4084fa32), TOBN(0x8862d213, 0x01f94f0c),
+     TOBN(0x23fc5ddf, 0x1510a406), TOBN(0x475a78f4, 0xc9f0e98d),
+     TOBN(0xb6d681c4, 0xe72843a0), TOBN(0xa90af2a4, 0x4a00c5a6),
+     TOBN(0x95fc6d45, 0xa34f4412), TOBN(0x60f9c0e2, 0xe7f5d703),
+     TOBN(0x2bc0642b, 0xad110925), TOBN(0x79abfc10, 0xbe24a4d5),
+    },
+};
diff --git a/crypto/ec/ecp_smpl.c b/crypto/ec/ecp_smpl.c
new file mode 100644
index 000000000..76e0caf90
--- /dev/null
+++ b/crypto/ec/ecp_smpl.c
@@ -0,0 +1,1369 @@
+/*
+ * Copyright 2001-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/* ====================================================================
+ * Copyright 2002 Sun Microsystems, Inc. ALL RIGHTS RESERVED.
+ * Portions of this software developed by SUN MICROSYSTEMS, INC.,
+ * and contributed to the OpenSSL project.
+ */
+
+#include 
+#include 
+
+#include "ec_lcl.h"
+
+const EC_METHOD *EC_GFp_simple_method(void)
+{
+    static const EC_METHOD ret = {
+        EC_FLAGS_DEFAULT_OCT,
+        NID_X9_62_prime_field,
+        ec_GFp_simple_group_init,
+        ec_GFp_simple_group_finish,
+        ec_GFp_simple_group_clear_finish,
+        ec_GFp_simple_group_copy,
+        ec_GFp_simple_group_set_curve,
+        ec_GFp_simple_group_get_curve,
+        ec_GFp_simple_group_get_degree,
+        ec_group_simple_order_bits,
+        ec_GFp_simple_group_check_discriminant,
+        ec_GFp_simple_point_init,
+        ec_GFp_simple_point_finish,
+        ec_GFp_simple_point_clear_finish,
+        ec_GFp_simple_point_copy,
+        ec_GFp_simple_point_set_to_infinity,
+        ec_GFp_simple_set_Jprojective_coordinates_GFp,
+        ec_GFp_simple_get_Jprojective_coordinates_GFp,
+        ec_GFp_simple_point_set_affine_coordinates,
+        ec_GFp_simple_point_get_affine_coordinates,
+        0, 0, 0,
+        ec_GFp_simple_add,
+        ec_GFp_simple_dbl,
+        ec_GFp_simple_invert,
+        ec_GFp_simple_is_at_infinity,
+        ec_GFp_simple_is_on_curve,
+        ec_GFp_simple_cmp,
+        ec_GFp_simple_make_affine,
+        ec_GFp_simple_points_make_affine,
+        0 /* mul */ ,
+        0 /* precompute_mult */ ,
+        0 /* have_precompute_mult */ ,
+        ec_GFp_simple_field_mul,
+        ec_GFp_simple_field_sqr,
+        0 /* field_div */ ,
+        0 /* field_encode */ ,
+        0 /* field_decode */ ,
+        0,                      /* field_set_to_one */
+        ec_key_simple_priv2oct,
+        ec_key_simple_oct2priv,
+        0, /* set private */
+        ec_key_simple_generate_key,
+        ec_key_simple_check_key,
+        ec_key_simple_generate_public_key,
+        0, /* keycopy */
+        0, /* keyfinish */
+        ecdh_simple_compute_key
+    };
+
+    return &ret;
+}
+
+/*
+ * Most method functions in this file are designed to work with
+ * non-trivial representations of field elements if necessary
+ * (see ecp_mont.c): while standard modular addition and subtraction
+ * are used, the field_mul and field_sqr methods will be used for
+ * multiplication, and field_encode and field_decode (if defined)
+ * will be used for converting between representations.
+ *
+ * Functions ec_GFp_simple_points_make_affine() and
+ * ec_GFp_simple_point_get_affine_coordinates() specifically assume
+ * that if a non-trivial representation is used, it is a Montgomery
+ * representation (i.e. 'encoding' means multiplying by some factor R).
+ */
+
+int ec_GFp_simple_group_init(EC_GROUP *group)
+{
+    group->field = BN_new();
+    group->a = BN_new();
+    group->b = BN_new();
+    if (group->field == NULL || group->a == NULL || group->b == NULL) {
+        BN_free(group->field);
+        BN_free(group->a);
+        BN_free(group->b);
+        return 0;
+    }
+    group->a_is_minus3 = 0;
+    return 1;
+}
+
+void ec_GFp_simple_group_finish(EC_GROUP *group)
+{
+    BN_free(group->field);
+    BN_free(group->a);
+    BN_free(group->b);
+}
+
+void ec_GFp_simple_group_clear_finish(EC_GROUP *group)
+{
+    BN_clear_free(group->field);
+    BN_clear_free(group->a);
+    BN_clear_free(group->b);
+}
+
+int ec_GFp_simple_group_copy(EC_GROUP *dest, const EC_GROUP *src)
+{
+    if (!BN_copy(dest->field, src->field))
+        return 0;
+    if (!BN_copy(dest->a, src->a))
+        return 0;
+    if (!BN_copy(dest->b, src->b))
+        return 0;
+
+    dest->a_is_minus3 = src->a_is_minus3;
+
+    return 1;
+}
+
+int ec_GFp_simple_group_set_curve(EC_GROUP *group,
+                                  const BIGNUM *p, const BIGNUM *a,
+                                  const BIGNUM *b, BN_CTX *ctx)
+{
+    int ret = 0;
+    BN_CTX *new_ctx = NULL;
+    BIGNUM *tmp_a;
+
+    /* p must be a prime > 3 */
+    if (BN_num_bits(p) <= 2 || !BN_is_odd(p)) {
+        ECerr(EC_F_EC_GFP_SIMPLE_GROUP_SET_CURVE, EC_R_INVALID_FIELD);
+        return 0;
+    }
+
+    if (ctx == NULL) {
+        ctx = new_ctx = BN_CTX_new();
+        if (ctx == NULL)
+            return 0;
+    }
+
+    BN_CTX_start(ctx);
+    tmp_a = BN_CTX_get(ctx);
+    if (tmp_a == NULL)
+        goto err;
+
+    /* group->field */
+    if (!BN_copy(group->field, p))
+        goto err;
+    BN_set_negative(group->field, 0);
+
+    /* group->a */
+    if (!BN_nnmod(tmp_a, a, p, ctx))
+        goto err;
+    if (group->meth->field_encode) {
+        if (!group->meth->field_encode(group, group->a, tmp_a, ctx))
+            goto err;
+    } else if (!BN_copy(group->a, tmp_a))
+        goto err;
+
+    /* group->b */
+    if (!BN_nnmod(group->b, b, p, ctx))
+        goto err;
+    if (group->meth->field_encode)
+        if (!group->meth->field_encode(group, group->b, group->b, ctx))
+            goto err;
+
+    /* group->a_is_minus3 */
+    if (!BN_add_word(tmp_a, 3))
+        goto err;
+    group->a_is_minus3 = (0 == BN_cmp(tmp_a, group->field));
+
+    ret = 1;
+
+ err:
+    BN_CTX_end(ctx);
+    BN_CTX_free(new_ctx);
+    return ret;
+}
+
+int ec_GFp_simple_group_get_curve(const EC_GROUP *group, BIGNUM *p, BIGNUM *a,
+                                  BIGNUM *b, BN_CTX *ctx)
+{
+    int ret = 0;
+    BN_CTX *new_ctx = NULL;
+
+    if (p != NULL) {
+        if (!BN_copy(p, group->field))
+            return 0;
+    }
+
+    if (a != NULL || b != NULL) {
+        if (group->meth->field_decode) {
+            if (ctx == NULL) {
+                ctx = new_ctx = BN_CTX_new();
+                if (ctx == NULL)
+                    return 0;
+            }
+            if (a != NULL) {
+                if (!group->meth->field_decode(group, a, group->a, ctx))
+                    goto err;
+            }
+            if (b != NULL) {
+                if (!group->meth->field_decode(group, b, group->b, ctx))
+                    goto err;
+            }
+        } else {
+            if (a != NULL) {
+                if (!BN_copy(a, group->a))
+                    goto err;
+            }
+            if (b != NULL) {
+                if (!BN_copy(b, group->b))
+                    goto err;
+            }
+        }
+    }
+
+    ret = 1;
+
+ err:
+    BN_CTX_free(new_ctx);
+    return ret;
+}
+
+int ec_GFp_simple_group_get_degree(const EC_GROUP *group)
+{
+    return BN_num_bits(group->field);
+}
+
+int ec_GFp_simple_group_check_discriminant(const EC_GROUP *group, BN_CTX *ctx)
+{
+    int ret = 0;
+    BIGNUM *a, *b, *order, *tmp_1, *tmp_2;
+    const BIGNUM *p = group->field;
+    BN_CTX *new_ctx = NULL;
+
+    if (ctx == NULL) {
+        ctx = new_ctx = BN_CTX_new();
+        if (ctx == NULL) {
+            ECerr(EC_F_EC_GFP_SIMPLE_GROUP_CHECK_DISCRIMINANT,
+                  ERR_R_MALLOC_FAILURE);
+            goto err;
+        }
+    }
+    BN_CTX_start(ctx);
+    a = BN_CTX_get(ctx);
+    b = BN_CTX_get(ctx);
+    tmp_1 = BN_CTX_get(ctx);
+    tmp_2 = BN_CTX_get(ctx);
+    order = BN_CTX_get(ctx);
+    if (order == NULL)
+        goto err;
+
+    if (group->meth->field_decode) {
+        if (!group->meth->field_decode(group, a, group->a, ctx))
+            goto err;
+        if (!group->meth->field_decode(group, b, group->b, ctx))
+            goto err;
+    } else {
+        if (!BN_copy(a, group->a))
+            goto err;
+        if (!BN_copy(b, group->b))
+            goto err;
+    }
+
+    /*-
+     * check the discriminant:
+     * y^2 = x^3 + a*x + b is an elliptic curve <=> 4*a^3 + 27*b^2 != 0 (mod p)
+     * 0 =< a, b < p
+     */
+    if (BN_is_zero(a)) {
+        if (BN_is_zero(b))
+            goto err;
+    } else if (!BN_is_zero(b)) {
+        if (!BN_mod_sqr(tmp_1, a, p, ctx))
+            goto err;
+        if (!BN_mod_mul(tmp_2, tmp_1, a, p, ctx))
+            goto err;
+        if (!BN_lshift(tmp_1, tmp_2, 2))
+            goto err;
+        /* tmp_1 = 4*a^3 */
+
+        if (!BN_mod_sqr(tmp_2, b, p, ctx))
+            goto err;
+        if (!BN_mul_word(tmp_2, 27))
+            goto err;
+        /* tmp_2 = 27*b^2 */
+
+        if (!BN_mod_add(a, tmp_1, tmp_2, p, ctx))
+            goto err;
+        if (BN_is_zero(a))
+            goto err;
+    }
+    ret = 1;
+
+ err:
+    if (ctx != NULL)
+        BN_CTX_end(ctx);
+    BN_CTX_free(new_ctx);
+    return ret;
+}
+
+int ec_GFp_simple_point_init(EC_POINT *point)
+{
+    point->X = BN_new();
+    point->Y = BN_new();
+    point->Z = BN_new();
+    point->Z_is_one = 0;
+
+    if (point->X == NULL || point->Y == NULL || point->Z == NULL) {
+        BN_free(point->X);
+        BN_free(point->Y);
+        BN_free(point->Z);
+        return 0;
+    }
+    return 1;
+}
+
+void ec_GFp_simple_point_finish(EC_POINT *point)
+{
+    BN_free(point->X);
+    BN_free(point->Y);
+    BN_free(point->Z);
+}
+
+void ec_GFp_simple_point_clear_finish(EC_POINT *point)
+{
+    BN_clear_free(point->X);
+    BN_clear_free(point->Y);
+    BN_clear_free(point->Z);
+    point->Z_is_one = 0;
+}
+
+int ec_GFp_simple_point_copy(EC_POINT *dest, const EC_POINT *src)
+{
+    if (!BN_copy(dest->X, src->X))
+        return 0;
+    if (!BN_copy(dest->Y, src->Y))
+        return 0;
+    if (!BN_copy(dest->Z, src->Z))
+        return 0;
+    dest->Z_is_one = src->Z_is_one;
+
+    return 1;
+}
+
+int ec_GFp_simple_point_set_to_infinity(const EC_GROUP *group,
+                                        EC_POINT *point)
+{
+    point->Z_is_one = 0;
+    BN_zero(point->Z);
+    return 1;
+}
+
+int ec_GFp_simple_set_Jprojective_coordinates_GFp(const EC_GROUP *group,
+                                                  EC_POINT *point,
+                                                  const BIGNUM *x,
+                                                  const BIGNUM *y,
+                                                  const BIGNUM *z,
+                                                  BN_CTX *ctx)
+{
+    BN_CTX *new_ctx = NULL;
+    int ret = 0;
+
+    if (ctx == NULL) {
+        ctx = new_ctx = BN_CTX_new();
+        if (ctx == NULL)
+            return 0;
+    }
+
+    if (x != NULL) {
+        if (!BN_nnmod(point->X, x, group->field, ctx))
+            goto err;
+        if (group->meth->field_encode) {
+            if (!group->meth->field_encode(group, point->X, point->X, ctx))
+                goto err;
+        }
+    }
+
+    if (y != NULL) {
+        if (!BN_nnmod(point->Y, y, group->field, ctx))
+            goto err;
+        if (group->meth->field_encode) {
+            if (!group->meth->field_encode(group, point->Y, point->Y, ctx))
+                goto err;
+        }
+    }
+
+    if (z != NULL) {
+        int Z_is_one;
+
+        if (!BN_nnmod(point->Z, z, group->field, ctx))
+            goto err;
+        Z_is_one = BN_is_one(point->Z);
+        if (group->meth->field_encode) {
+            if (Z_is_one && (group->meth->field_set_to_one != 0)) {
+                if (!group->meth->field_set_to_one(group, point->Z, ctx))
+                    goto err;
+            } else {
+                if (!group->
+                    meth->field_encode(group, point->Z, point->Z, ctx))
+                    goto err;
+            }
+        }
+        point->Z_is_one = Z_is_one;
+    }
+
+    ret = 1;
+
+ err:
+    BN_CTX_free(new_ctx);
+    return ret;
+}
+
+int ec_GFp_simple_get_Jprojective_coordinates_GFp(const EC_GROUP *group,
+                                                  const EC_POINT *point,
+                                                  BIGNUM *x, BIGNUM *y,
+                                                  BIGNUM *z, BN_CTX *ctx)
+{
+    BN_CTX *new_ctx = NULL;
+    int ret = 0;
+
+    if (group->meth->field_decode != 0) {
+        if (ctx == NULL) {
+            ctx = new_ctx = BN_CTX_new();
+            if (ctx == NULL)
+                return 0;
+        }
+
+        if (x != NULL) {
+            if (!group->meth->field_decode(group, x, point->X, ctx))
+                goto err;
+        }
+        if (y != NULL) {
+            if (!group->meth->field_decode(group, y, point->Y, ctx))
+                goto err;
+        }
+        if (z != NULL) {
+            if (!group->meth->field_decode(group, z, point->Z, ctx))
+                goto err;
+        }
+    } else {
+        if (x != NULL) {
+            if (!BN_copy(x, point->X))
+                goto err;
+        }
+        if (y != NULL) {
+            if (!BN_copy(y, point->Y))
+                goto err;
+        }
+        if (z != NULL) {
+            if (!BN_copy(z, point->Z))
+                goto err;
+        }
+    }
+
+    ret = 1;
+
+ err:
+    BN_CTX_free(new_ctx);
+    return ret;
+}
+
+int ec_GFp_simple_point_set_affine_coordinates(const EC_GROUP *group,
+                                               EC_POINT *point,
+                                               const BIGNUM *x,
+                                               const BIGNUM *y, BN_CTX *ctx)
+{
+    if (x == NULL || y == NULL) {
+        /*
+         * unlike for projective coordinates, we do not tolerate this
+         */
+        ECerr(EC_F_EC_GFP_SIMPLE_POINT_SET_AFFINE_COORDINATES,
+              ERR_R_PASSED_NULL_PARAMETER);
+        return 0;
+    }
+
+    return EC_POINT_set_Jprojective_coordinates_GFp(group, point, x, y,
+                                                    BN_value_one(), ctx);
+}
+
+int ec_GFp_simple_point_get_affine_coordinates(const EC_GROUP *group,
+                                               const EC_POINT *point,
+                                               BIGNUM *x, BIGNUM *y,
+                                               BN_CTX *ctx)
+{
+    BN_CTX *new_ctx = NULL;
+    BIGNUM *Z, *Z_1, *Z_2, *Z_3;
+    const BIGNUM *Z_;
+    int ret = 0;
+
+    if (EC_POINT_is_at_infinity(group, point)) {
+        ECerr(EC_F_EC_GFP_SIMPLE_POINT_GET_AFFINE_COORDINATES,
+              EC_R_POINT_AT_INFINITY);
+        return 0;
+    }
+
+    if (ctx == NULL) {
+        ctx = new_ctx = BN_CTX_new();
+        if (ctx == NULL)
+            return 0;
+    }
+
+    BN_CTX_start(ctx);
+    Z = BN_CTX_get(ctx);
+    Z_1 = BN_CTX_get(ctx);
+    Z_2 = BN_CTX_get(ctx);
+    Z_3 = BN_CTX_get(ctx);
+    if (Z_3 == NULL)
+        goto err;
+
+    /* transform  (X, Y, Z)  into  (x, y) := (X/Z^2, Y/Z^3) */
+
+    if (group->meth->field_decode) {
+        if (!group->meth->field_decode(group, Z, point->Z, ctx))
+            goto err;
+        Z_ = Z;
+    } else {
+        Z_ = point->Z;
+    }
+
+    if (BN_is_one(Z_)) {
+        if (group->meth->field_decode) {
+            if (x != NULL) {
+                if (!group->meth->field_decode(group, x, point->X, ctx))
+                    goto err;
+            }
+            if (y != NULL) {
+                if (!group->meth->field_decode(group, y, point->Y, ctx))
+                    goto err;
+            }
+        } else {
+            if (x != NULL) {
+                if (!BN_copy(x, point->X))
+                    goto err;
+            }
+            if (y != NULL) {
+                if (!BN_copy(y, point->Y))
+                    goto err;
+            }
+        }
+    } else {
+        if (!BN_mod_inverse(Z_1, Z_, group->field, ctx)) {
+            ECerr(EC_F_EC_GFP_SIMPLE_POINT_GET_AFFINE_COORDINATES,
+                  ERR_R_BN_LIB);
+            goto err;
+        }
+
+        if (group->meth->field_encode == 0) {
+            /* field_sqr works on standard representation */
+            if (!group->meth->field_sqr(group, Z_2, Z_1, ctx))
+                goto err;
+        } else {
+            if (!BN_mod_sqr(Z_2, Z_1, group->field, ctx))
+                goto err;
+        }
+
+        if (x != NULL) {
+            /*
+             * in the Montgomery case, field_mul will cancel out Montgomery
+             * factor in X:
+             */
+            if (!group->meth->field_mul(group, x, point->X, Z_2, ctx))
+                goto err;
+        }
+
+        if (y != NULL) {
+            if (group->meth->field_encode == 0) {
+                /*
+                 * field_mul works on standard representation
+                 */
+                if (!group->meth->field_mul(group, Z_3, Z_2, Z_1, ctx))
+                    goto err;
+            } else {
+                if (!BN_mod_mul(Z_3, Z_2, Z_1, group->field, ctx))
+                    goto err;
+            }
+
+            /*
+             * in the Montgomery case, field_mul will cancel out Montgomery
+             * factor in Y:
+             */
+            if (!group->meth->field_mul(group, y, point->Y, Z_3, ctx))
+                goto err;
+        }
+    }
+
+    ret = 1;
+
+ err:
+    BN_CTX_end(ctx);
+    BN_CTX_free(new_ctx);
+    return ret;
+}
+
+int ec_GFp_simple_add(const EC_GROUP *group, EC_POINT *r, const EC_POINT *a,
+                      const EC_POINT *b, BN_CTX *ctx)
+{
+    int (*field_mul) (const EC_GROUP *, BIGNUM *, const BIGNUM *,
+                      const BIGNUM *, BN_CTX *);
+    int (*field_sqr) (const EC_GROUP *, BIGNUM *, const BIGNUM *, BN_CTX *);
+    const BIGNUM *p;
+    BN_CTX *new_ctx = NULL;
+    BIGNUM *n0, *n1, *n2, *n3, *n4, *n5, *n6;
+    int ret = 0;
+
+    if (a == b)
+        return EC_POINT_dbl(group, r, a, ctx);
+    if (EC_POINT_is_at_infinity(group, a))
+        return EC_POINT_copy(r, b);
+    if (EC_POINT_is_at_infinity(group, b))
+        return EC_POINT_copy(r, a);
+
+    field_mul = group->meth->field_mul;
+    field_sqr = group->meth->field_sqr;
+    p = group->field;
+
+    if (ctx == NULL) {
+        ctx = new_ctx = BN_CTX_new();
+        if (ctx == NULL)
+            return 0;
+    }
+
+    BN_CTX_start(ctx);
+    n0 = BN_CTX_get(ctx);
+    n1 = BN_CTX_get(ctx);
+    n2 = BN_CTX_get(ctx);
+    n3 = BN_CTX_get(ctx);
+    n4 = BN_CTX_get(ctx);
+    n5 = BN_CTX_get(ctx);
+    n6 = BN_CTX_get(ctx);
+    if (n6 == NULL)
+        goto end;
+
+    /*
+     * Note that in this function we must not read components of 'a' or 'b'
+     * once we have written the corresponding components of 'r'. ('r' might
+     * be one of 'a' or 'b'.)
+     */
+
+    /* n1, n2 */
+    if (b->Z_is_one) {
+        if (!BN_copy(n1, a->X))
+            goto end;
+        if (!BN_copy(n2, a->Y))
+            goto end;
+        /* n1 = X_a */
+        /* n2 = Y_a */
+    } else {
+        if (!field_sqr(group, n0, b->Z, ctx))
+            goto end;
+        if (!field_mul(group, n1, a->X, n0, ctx))
+            goto end;
+        /* n1 = X_a * Z_b^2 */
+
+        if (!field_mul(group, n0, n0, b->Z, ctx))
+            goto end;
+        if (!field_mul(group, n2, a->Y, n0, ctx))
+            goto end;
+        /* n2 = Y_a * Z_b^3 */
+    }
+
+    /* n3, n4 */
+    if (a->Z_is_one) {
+        if (!BN_copy(n3, b->X))
+            goto end;
+        if (!BN_copy(n4, b->Y))
+            goto end;
+        /* n3 = X_b */
+        /* n4 = Y_b */
+    } else {
+        if (!field_sqr(group, n0, a->Z, ctx))
+            goto end;
+        if (!field_mul(group, n3, b->X, n0, ctx))
+            goto end;
+        /* n3 = X_b * Z_a^2 */
+
+        if (!field_mul(group, n0, n0, a->Z, ctx))
+            goto end;
+        if (!field_mul(group, n4, b->Y, n0, ctx))
+            goto end;
+        /* n4 = Y_b * Z_a^3 */
+    }
+
+    /* n5, n6 */
+    if (!BN_mod_sub_quick(n5, n1, n3, p))
+        goto end;
+    if (!BN_mod_sub_quick(n6, n2, n4, p))
+        goto end;
+    /* n5 = n1 - n3 */
+    /* n6 = n2 - n4 */
+
+    if (BN_is_zero(n5)) {
+        if (BN_is_zero(n6)) {
+            /* a is the same point as b */
+            BN_CTX_end(ctx);
+            ret = EC_POINT_dbl(group, r, a, ctx);
+            ctx = NULL;
+            goto end;
+        } else {
+            /* a is the inverse of b */
+            BN_zero(r->Z);
+            r->Z_is_one = 0;
+            ret = 1;
+            goto end;
+        }
+    }
+
+    /* 'n7', 'n8' */
+    if (!BN_mod_add_quick(n1, n1, n3, p))
+        goto end;
+    if (!BN_mod_add_quick(n2, n2, n4, p))
+        goto end;
+    /* 'n7' = n1 + n3 */
+    /* 'n8' = n2 + n4 */
+
+    /* Z_r */
+    if (a->Z_is_one && b->Z_is_one) {
+        if (!BN_copy(r->Z, n5))
+            goto end;
+    } else {
+        if (a->Z_is_one) {
+            if (!BN_copy(n0, b->Z))
+                goto end;
+        } else if (b->Z_is_one) {
+            if (!BN_copy(n0, a->Z))
+                goto end;
+        } else {
+            if (!field_mul(group, n0, a->Z, b->Z, ctx))
+                goto end;
+        }
+        if (!field_mul(group, r->Z, n0, n5, ctx))
+            goto end;
+    }
+    r->Z_is_one = 0;
+    /* Z_r = Z_a * Z_b * n5 */
+
+    /* X_r */
+    if (!field_sqr(group, n0, n6, ctx))
+        goto end;
+    if (!field_sqr(group, n4, n5, ctx))
+        goto end;
+    if (!field_mul(group, n3, n1, n4, ctx))
+        goto end;
+    if (!BN_mod_sub_quick(r->X, n0, n3, p))
+        goto end;
+    /* X_r = n6^2 - n5^2 * 'n7' */
+
+    /* 'n9' */
+    if (!BN_mod_lshift1_quick(n0, r->X, p))
+        goto end;
+    if (!BN_mod_sub_quick(n0, n3, n0, p))
+        goto end;
+    /* n9 = n5^2 * 'n7' - 2 * X_r */
+
+    /* Y_r */
+    if (!field_mul(group, n0, n0, n6, ctx))
+        goto end;
+    if (!field_mul(group, n5, n4, n5, ctx))
+        goto end;               /* now n5 is n5^3 */
+    if (!field_mul(group, n1, n2, n5, ctx))
+        goto end;
+    if (!BN_mod_sub_quick(n0, n0, n1, p))
+        goto end;
+    if (BN_is_odd(n0))
+        if (!BN_add(n0, n0, p))
+            goto end;
+    /* now  0 <= n0 < 2*p,  and n0 is even */
+    if (!BN_rshift1(r->Y, n0))
+        goto end;
+    /* Y_r = (n6 * 'n9' - 'n8' * 'n5^3') / 2 */
+
+    ret = 1;
+
+ end:
+    if (ctx)                    /* otherwise we already called BN_CTX_end */
+        BN_CTX_end(ctx);
+    BN_CTX_free(new_ctx);
+    return ret;
+}
+
+int ec_GFp_simple_dbl(const EC_GROUP *group, EC_POINT *r, const EC_POINT *a,
+                      BN_CTX *ctx)
+{
+    int (*field_mul) (const EC_GROUP *, BIGNUM *, const BIGNUM *,
+                      const BIGNUM *, BN_CTX *);
+    int (*field_sqr) (const EC_GROUP *, BIGNUM *, const BIGNUM *, BN_CTX *);
+    const BIGNUM *p;
+    BN_CTX *new_ctx = NULL;
+    BIGNUM *n0, *n1, *n2, *n3;
+    int ret = 0;
+
+    if (EC_POINT_is_at_infinity(group, a)) {
+        BN_zero(r->Z);
+        r->Z_is_one = 0;
+        return 1;
+    }
+
+    field_mul = group->meth->field_mul;
+    field_sqr = group->meth->field_sqr;
+    p = group->field;
+
+    if (ctx == NULL) {
+        ctx = new_ctx = BN_CTX_new();
+        if (ctx == NULL)
+            return 0;
+    }
+
+    BN_CTX_start(ctx);
+    n0 = BN_CTX_get(ctx);
+    n1 = BN_CTX_get(ctx);
+    n2 = BN_CTX_get(ctx);
+    n3 = BN_CTX_get(ctx);
+    if (n3 == NULL)
+        goto err;
+
+    /*
+     * Note that in this function we must not read components of 'a' once we
+     * have written the corresponding components of 'r'. ('r' might the same
+     * as 'a'.)
+     */
+
+    /* n1 */
+    if (a->Z_is_one) {
+        if (!field_sqr(group, n0, a->X, ctx))
+            goto err;
+        if (!BN_mod_lshift1_quick(n1, n0, p))
+            goto err;
+        if (!BN_mod_add_quick(n0, n0, n1, p))
+            goto err;
+        if (!BN_mod_add_quick(n1, n0, group->a, p))
+            goto err;
+        /* n1 = 3 * X_a^2 + a_curve */
+    } else if (group->a_is_minus3) {
+        if (!field_sqr(group, n1, a->Z, ctx))
+            goto err;
+        if (!BN_mod_add_quick(n0, a->X, n1, p))
+            goto err;
+        if (!BN_mod_sub_quick(n2, a->X, n1, p))
+            goto err;
+        if (!field_mul(group, n1, n0, n2, ctx))
+            goto err;
+        if (!BN_mod_lshift1_quick(n0, n1, p))
+            goto err;
+        if (!BN_mod_add_quick(n1, n0, n1, p))
+            goto err;
+        /*-
+         * n1 = 3 * (X_a + Z_a^2) * (X_a - Z_a^2)
+         *    = 3 * X_a^2 - 3 * Z_a^4
+         */
+    } else {
+        if (!field_sqr(group, n0, a->X, ctx))
+            goto err;
+        if (!BN_mod_lshift1_quick(n1, n0, p))
+            goto err;
+        if (!BN_mod_add_quick(n0, n0, n1, p))
+            goto err;
+        if (!field_sqr(group, n1, a->Z, ctx))
+            goto err;
+        if (!field_sqr(group, n1, n1, ctx))
+            goto err;
+        if (!field_mul(group, n1, n1, group->a, ctx))
+            goto err;
+        if (!BN_mod_add_quick(n1, n1, n0, p))
+            goto err;
+        /* n1 = 3 * X_a^2 + a_curve * Z_a^4 */
+    }
+
+    /* Z_r */
+    if (a->Z_is_one) {
+        if (!BN_copy(n0, a->Y))
+            goto err;
+    } else {
+        if (!field_mul(group, n0, a->Y, a->Z, ctx))
+            goto err;
+    }
+    if (!BN_mod_lshift1_quick(r->Z, n0, p))
+        goto err;
+    r->Z_is_one = 0;
+    /* Z_r = 2 * Y_a * Z_a */
+
+    /* n2 */
+    if (!field_sqr(group, n3, a->Y, ctx))
+        goto err;
+    if (!field_mul(group, n2, a->X, n3, ctx))
+        goto err;
+    if (!BN_mod_lshift_quick(n2, n2, 2, p))
+        goto err;
+    /* n2 = 4 * X_a * Y_a^2 */
+
+    /* X_r */
+    if (!BN_mod_lshift1_quick(n0, n2, p))
+        goto err;
+    if (!field_sqr(group, r->X, n1, ctx))
+        goto err;
+    if (!BN_mod_sub_quick(r->X, r->X, n0, p))
+        goto err;
+    /* X_r = n1^2 - 2 * n2 */
+
+    /* n3 */
+    if (!field_sqr(group, n0, n3, ctx))
+        goto err;
+    if (!BN_mod_lshift_quick(n3, n0, 3, p))
+        goto err;
+    /* n3 = 8 * Y_a^4 */
+
+    /* Y_r */
+    if (!BN_mod_sub_quick(n0, n2, r->X, p))
+        goto err;
+    if (!field_mul(group, n0, n1, n0, ctx))
+        goto err;
+    if (!BN_mod_sub_quick(r->Y, n0, n3, p))
+        goto err;
+    /* Y_r = n1 * (n2 - X_r) - n3 */
+
+    ret = 1;
+
+ err:
+    BN_CTX_end(ctx);
+    BN_CTX_free(new_ctx);
+    return ret;
+}
+
+int ec_GFp_simple_invert(const EC_GROUP *group, EC_POINT *point, BN_CTX *ctx)
+{
+    if (EC_POINT_is_at_infinity(group, point) || BN_is_zero(point->Y))
+        /* point is its own inverse */
+        return 1;
+
+    return BN_usub(point->Y, group->field, point->Y);
+}
+
+int ec_GFp_simple_is_at_infinity(const EC_GROUP *group, const EC_POINT *point)
+{
+    return BN_is_zero(point->Z);
+}
+
+int ec_GFp_simple_is_on_curve(const EC_GROUP *group, const EC_POINT *point,
+                              BN_CTX *ctx)
+{
+    int (*field_mul) (const EC_GROUP *, BIGNUM *, const BIGNUM *,
+                      const BIGNUM *, BN_CTX *);
+    int (*field_sqr) (const EC_GROUP *, BIGNUM *, const BIGNUM *, BN_CTX *);
+    const BIGNUM *p;
+    BN_CTX *new_ctx = NULL;
+    BIGNUM *rh, *tmp, *Z4, *Z6;
+    int ret = -1;
+
+    if (EC_POINT_is_at_infinity(group, point))
+        return 1;
+
+    field_mul = group->meth->field_mul;
+    field_sqr = group->meth->field_sqr;
+    p = group->field;
+
+    if (ctx == NULL) {
+        ctx = new_ctx = BN_CTX_new();
+        if (ctx == NULL)
+            return -1;
+    }
+
+    BN_CTX_start(ctx);
+    rh = BN_CTX_get(ctx);
+    tmp = BN_CTX_get(ctx);
+    Z4 = BN_CTX_get(ctx);
+    Z6 = BN_CTX_get(ctx);
+    if (Z6 == NULL)
+        goto err;
+
+    /*-
+     * We have a curve defined by a Weierstrass equation
+     *      y^2 = x^3 + a*x + b.
+     * The point to consider is given in Jacobian projective coordinates
+     * where  (X, Y, Z)  represents  (x, y) = (X/Z^2, Y/Z^3).
+     * Substituting this and multiplying by  Z^6  transforms the above equation into
+     *      Y^2 = X^3 + a*X*Z^4 + b*Z^6.
+     * To test this, we add up the right-hand side in 'rh'.
+     */
+
+    /* rh := X^2 */
+    if (!field_sqr(group, rh, point->X, ctx))
+        goto err;
+
+    if (!point->Z_is_one) {
+        if (!field_sqr(group, tmp, point->Z, ctx))
+            goto err;
+        if (!field_sqr(group, Z4, tmp, ctx))
+            goto err;
+        if (!field_mul(group, Z6, Z4, tmp, ctx))
+            goto err;
+
+        /* rh := (rh + a*Z^4)*X */
+        if (group->a_is_minus3) {
+            if (!BN_mod_lshift1_quick(tmp, Z4, p))
+                goto err;
+            if (!BN_mod_add_quick(tmp, tmp, Z4, p))
+                goto err;
+            if (!BN_mod_sub_quick(rh, rh, tmp, p))
+                goto err;
+            if (!field_mul(group, rh, rh, point->X, ctx))
+                goto err;
+        } else {
+            if (!field_mul(group, tmp, Z4, group->a, ctx))
+                goto err;
+            if (!BN_mod_add_quick(rh, rh, tmp, p))
+                goto err;
+            if (!field_mul(group, rh, rh, point->X, ctx))
+                goto err;
+        }
+
+        /* rh := rh + b*Z^6 */
+        if (!field_mul(group, tmp, group->b, Z6, ctx))
+            goto err;
+        if (!BN_mod_add_quick(rh, rh, tmp, p))
+            goto err;
+    } else {
+        /* point->Z_is_one */
+
+        /* rh := (rh + a)*X */
+        if (!BN_mod_add_quick(rh, rh, group->a, p))
+            goto err;
+        if (!field_mul(group, rh, rh, point->X, ctx))
+            goto err;
+        /* rh := rh + b */
+        if (!BN_mod_add_quick(rh, rh, group->b, p))
+            goto err;
+    }
+
+    /* 'lh' := Y^2 */
+    if (!field_sqr(group, tmp, point->Y, ctx))
+        goto err;
+
+    ret = (0 == BN_ucmp(tmp, rh));
+
+ err:
+    BN_CTX_end(ctx);
+    BN_CTX_free(new_ctx);
+    return ret;
+}
+
+int ec_GFp_simple_cmp(const EC_GROUP *group, const EC_POINT *a,
+                      const EC_POINT *b, BN_CTX *ctx)
+{
+    /*-
+     * return values:
+     *  -1   error
+     *   0   equal (in affine coordinates)
+     *   1   not equal
+     */
+
+    int (*field_mul) (const EC_GROUP *, BIGNUM *, const BIGNUM *,
+                      const BIGNUM *, BN_CTX *);
+    int (*field_sqr) (const EC_GROUP *, BIGNUM *, const BIGNUM *, BN_CTX *);
+    BN_CTX *new_ctx = NULL;
+    BIGNUM *tmp1, *tmp2, *Za23, *Zb23;
+    const BIGNUM *tmp1_, *tmp2_;
+    int ret = -1;
+
+    if (EC_POINT_is_at_infinity(group, a)) {
+        return EC_POINT_is_at_infinity(group, b) ? 0 : 1;
+    }
+
+    if (EC_POINT_is_at_infinity(group, b))
+        return 1;
+
+    if (a->Z_is_one && b->Z_is_one) {
+        return ((BN_cmp(a->X, b->X) == 0) && BN_cmp(a->Y, b->Y) == 0) ? 0 : 1;
+    }
+
+    field_mul = group->meth->field_mul;
+    field_sqr = group->meth->field_sqr;
+
+    if (ctx == NULL) {
+        ctx = new_ctx = BN_CTX_new();
+        if (ctx == NULL)
+            return -1;
+    }
+
+    BN_CTX_start(ctx);
+    tmp1 = BN_CTX_get(ctx);
+    tmp2 = BN_CTX_get(ctx);
+    Za23 = BN_CTX_get(ctx);
+    Zb23 = BN_CTX_get(ctx);
+    if (Zb23 == NULL)
+        goto end;
+
+    /*-
+     * We have to decide whether
+     *     (X_a/Z_a^2, Y_a/Z_a^3) = (X_b/Z_b^2, Y_b/Z_b^3),
+     * or equivalently, whether
+     *     (X_a*Z_b^2, Y_a*Z_b^3) = (X_b*Z_a^2, Y_b*Z_a^3).
+     */
+
+    if (!b->Z_is_one) {
+        if (!field_sqr(group, Zb23, b->Z, ctx))
+            goto end;
+        if (!field_mul(group, tmp1, a->X, Zb23, ctx))
+            goto end;
+        tmp1_ = tmp1;
+    } else
+        tmp1_ = a->X;
+    if (!a->Z_is_one) {
+        if (!field_sqr(group, Za23, a->Z, ctx))
+            goto end;
+        if (!field_mul(group, tmp2, b->X, Za23, ctx))
+            goto end;
+        tmp2_ = tmp2;
+    } else
+        tmp2_ = b->X;
+
+    /* compare  X_a*Z_b^2  with  X_b*Z_a^2 */
+    if (BN_cmp(tmp1_, tmp2_) != 0) {
+        ret = 1;                /* points differ */
+        goto end;
+    }
+
+    if (!b->Z_is_one) {
+        if (!field_mul(group, Zb23, Zb23, b->Z, ctx))
+            goto end;
+        if (!field_mul(group, tmp1, a->Y, Zb23, ctx))
+            goto end;
+        /* tmp1_ = tmp1 */
+    } else
+        tmp1_ = a->Y;
+    if (!a->Z_is_one) {
+        if (!field_mul(group, Za23, Za23, a->Z, ctx))
+            goto end;
+        if (!field_mul(group, tmp2, b->Y, Za23, ctx))
+            goto end;
+        /* tmp2_ = tmp2 */
+    } else
+        tmp2_ = b->Y;
+
+    /* compare  Y_a*Z_b^3  with  Y_b*Z_a^3 */
+    if (BN_cmp(tmp1_, tmp2_) != 0) {
+        ret = 1;                /* points differ */
+        goto end;
+    }
+
+    /* points are equal */
+    ret = 0;
+
+ end:
+    BN_CTX_end(ctx);
+    BN_CTX_free(new_ctx);
+    return ret;
+}
+
+int ec_GFp_simple_make_affine(const EC_GROUP *group, EC_POINT *point,
+                              BN_CTX *ctx)
+{
+    BN_CTX *new_ctx = NULL;
+    BIGNUM *x, *y;
+    int ret = 0;
+
+    if (point->Z_is_one || EC_POINT_is_at_infinity(group, point))
+        return 1;
+
+    if (ctx == NULL) {
+        ctx = new_ctx = BN_CTX_new();
+        if (ctx == NULL)
+            return 0;
+    }
+
+    BN_CTX_start(ctx);
+    x = BN_CTX_get(ctx);
+    y = BN_CTX_get(ctx);
+    if (y == NULL)
+        goto err;
+
+    if (!EC_POINT_get_affine_coordinates_GFp(group, point, x, y, ctx))
+        goto err;
+    if (!EC_POINT_set_affine_coordinates_GFp(group, point, x, y, ctx))
+        goto err;
+    if (!point->Z_is_one) {
+        ECerr(EC_F_EC_GFP_SIMPLE_MAKE_AFFINE, ERR_R_INTERNAL_ERROR);
+        goto err;
+    }
+
+    ret = 1;
+
+ err:
+    BN_CTX_end(ctx);
+    BN_CTX_free(new_ctx);
+    return ret;
+}
+
+int ec_GFp_simple_points_make_affine(const EC_GROUP *group, size_t num,
+                                     EC_POINT *points[], BN_CTX *ctx)
+{
+    BN_CTX *new_ctx = NULL;
+    BIGNUM *tmp, *tmp_Z;
+    BIGNUM **prod_Z = NULL;
+    size_t i;
+    int ret = 0;
+
+    if (num == 0)
+        return 1;
+
+    if (ctx == NULL) {
+        ctx = new_ctx = BN_CTX_new();
+        if (ctx == NULL)
+            return 0;
+    }
+
+    BN_CTX_start(ctx);
+    tmp = BN_CTX_get(ctx);
+    tmp_Z = BN_CTX_get(ctx);
+    if (tmp == NULL || tmp_Z == NULL)
+        goto err;
+
+    prod_Z = OPENSSL_malloc(num * sizeof prod_Z[0]);
+    if (prod_Z == NULL)
+        goto err;
+    for (i = 0; i < num; i++) {
+        prod_Z[i] = BN_new();
+        if (prod_Z[i] == NULL)
+            goto err;
+    }
+
+    /*
+     * Set each prod_Z[i] to the product of points[0]->Z .. points[i]->Z,
+     * skipping any zero-valued inputs (pretend that they're 1).
+     */
+
+    if (!BN_is_zero(points[0]->Z)) {
+        if (!BN_copy(prod_Z[0], points[0]->Z))
+            goto err;
+    } else {
+        if (group->meth->field_set_to_one != 0) {
+            if (!group->meth->field_set_to_one(group, prod_Z[0], ctx))
+                goto err;
+        } else {
+            if (!BN_one(prod_Z[0]))
+                goto err;
+        }
+    }
+
+    for (i = 1; i < num; i++) {
+        if (!BN_is_zero(points[i]->Z)) {
+            if (!group->
+                meth->field_mul(group, prod_Z[i], prod_Z[i - 1], points[i]->Z,
+                                ctx))
+                goto err;
+        } else {
+            if (!BN_copy(prod_Z[i], prod_Z[i - 1]))
+                goto err;
+        }
+    }
+
+    /*
+     * Now use a single explicit inversion to replace every non-zero
+     * points[i]->Z by its inverse.
+     */
+
+    if (!BN_mod_inverse(tmp, prod_Z[num - 1], group->field, ctx)) {
+        ECerr(EC_F_EC_GFP_SIMPLE_POINTS_MAKE_AFFINE, ERR_R_BN_LIB);
+        goto err;
+    }
+    if (group->meth->field_encode != 0) {
+        /*
+         * In the Montgomery case, we just turned R*H (representing H) into
+         * 1/(R*H), but we need R*(1/H) (representing 1/H); i.e. we need to
+         * multiply by the Montgomery factor twice.
+         */
+        if (!group->meth->field_encode(group, tmp, tmp, ctx))
+            goto err;
+        if (!group->meth->field_encode(group, tmp, tmp, ctx))
+            goto err;
+    }
+
+    for (i = num - 1; i > 0; --i) {
+        /*
+         * Loop invariant: tmp is the product of the inverses of points[0]->Z
+         * .. points[i]->Z (zero-valued inputs skipped).
+         */
+        if (!BN_is_zero(points[i]->Z)) {
+            /*
+             * Set tmp_Z to the inverse of points[i]->Z (as product of Z
+             * inverses 0 .. i, Z values 0 .. i - 1).
+             */
+            if (!group->
+                meth->field_mul(group, tmp_Z, prod_Z[i - 1], tmp, ctx))
+                goto err;
+            /*
+             * Update tmp to satisfy the loop invariant for i - 1.
+             */
+            if (!group->meth->field_mul(group, tmp, tmp, points[i]->Z, ctx))
+                goto err;
+            /* Replace points[i]->Z by its inverse. */
+            if (!BN_copy(points[i]->Z, tmp_Z))
+                goto err;
+        }
+    }
+
+    if (!BN_is_zero(points[0]->Z)) {
+        /* Replace points[0]->Z by its inverse. */
+        if (!BN_copy(points[0]->Z, tmp))
+            goto err;
+    }
+
+    /* Finally, fix up the X and Y coordinates for all points. */
+
+    for (i = 0; i < num; i++) {
+        EC_POINT *p = points[i];
+
+        if (!BN_is_zero(p->Z)) {
+            /* turn  (X, Y, 1/Z)  into  (X/Z^2, Y/Z^3, 1) */
+
+            if (!group->meth->field_sqr(group, tmp, p->Z, ctx))
+                goto err;
+            if (!group->meth->field_mul(group, p->X, p->X, tmp, ctx))
+                goto err;
+
+            if (!group->meth->field_mul(group, tmp, tmp, p->Z, ctx))
+                goto err;
+            if (!group->meth->field_mul(group, p->Y, p->Y, tmp, ctx))
+                goto err;
+
+            if (group->meth->field_set_to_one != 0) {
+                if (!group->meth->field_set_to_one(group, p->Z, ctx))
+                    goto err;
+            } else {
+                if (!BN_one(p->Z))
+                    goto err;
+            }
+            p->Z_is_one = 1;
+        }
+    }
+
+    ret = 1;
+
+ err:
+    BN_CTX_end(ctx);
+    BN_CTX_free(new_ctx);
+    if (prod_Z != NULL) {
+        for (i = 0; i < num; i++) {
+            if (prod_Z[i] == NULL)
+                break;
+            BN_clear_free(prod_Z[i]);
+        }
+        OPENSSL_free(prod_Z);
+    }
+    return ret;
+}
+
+int ec_GFp_simple_field_mul(const EC_GROUP *group, BIGNUM *r, const BIGNUM *a,
+                            const BIGNUM *b, BN_CTX *ctx)
+{
+    return BN_mod_mul(r, a, b, group->field, ctx);
+}
+
+int ec_GFp_simple_field_sqr(const EC_GROUP *group, BIGNUM *r, const BIGNUM *a,
+                            BN_CTX *ctx)
+{
+    return BN_mod_sqr(r, a, group->field, ctx);
+}
diff --git a/crypto/ec/ecx_meth.c b/crypto/ec/ecx_meth.c
new file mode 100644
index 000000000..06e391134
--- /dev/null
+++ b/crypto/ec/ecx_meth.c
@@ -0,0 +1,373 @@
+/*
+ * Copyright 2006-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+#include 
+#include "internal/asn1_int.h"
+#include "internal/evp_int.h"
+#include "ec_lcl.h"
+
+#define X25519_KEYLEN        32
+#define X25519_BITS          253
+#define X25519_SECURITY_BITS 128
+
+typedef struct {
+    unsigned char pubkey[X25519_KEYLEN];
+    unsigned char *privkey;
+} X25519_KEY;
+
+typedef enum {
+    X25519_PUBLIC,
+    X25519_PRIVATE,
+    X25519_KEYGEN
+} ecx_key_op_t;
+
+/* Setup EVP_PKEY using public, private or generation */
+static int ecx_key_op(EVP_PKEY *pkey, const X509_ALGOR *palg,
+                      const unsigned char *p, int plen, ecx_key_op_t op)
+{
+    X25519_KEY *xkey;
+
+    if (op != X25519_KEYGEN) {
+        if (palg != NULL) {
+            int ptype;
+
+            /* Algorithm parameters must be absent */
+            X509_ALGOR_get0(NULL, &ptype, NULL, palg);
+            if (ptype != V_ASN1_UNDEF) {
+                ECerr(EC_F_ECX_KEY_OP, EC_R_INVALID_ENCODING);
+                return 0;
+            }
+        }
+
+        if (p == NULL || plen != X25519_KEYLEN) {
+            ECerr(EC_F_ECX_KEY_OP, EC_R_INVALID_ENCODING);
+            return 0;
+        }
+    }
+
+    xkey = OPENSSL_zalloc(sizeof(*xkey));
+    if (xkey == NULL) {
+        ECerr(EC_F_ECX_KEY_OP, ERR_R_MALLOC_FAILURE);
+        return 0;
+    }
+
+    if (op == X25519_PUBLIC) {
+        memcpy(xkey->pubkey, p, plen);
+    } else {
+        xkey->privkey = OPENSSL_secure_malloc(X25519_KEYLEN);
+        if (xkey->privkey == NULL) {
+            ECerr(EC_F_ECX_KEY_OP, ERR_R_MALLOC_FAILURE);
+            OPENSSL_free(xkey);
+            return 0;
+        }
+        if (op == X25519_KEYGEN) {
+            if (RAND_bytes(xkey->privkey, X25519_KEYLEN) <= 0) {
+                OPENSSL_secure_free(xkey->privkey);
+                OPENSSL_free(xkey);
+                return 0;
+            }
+            xkey->privkey[0] &= 248;
+            xkey->privkey[31] &= 127;
+            xkey->privkey[31] |= 64;
+        } else {
+            memcpy(xkey->privkey, p, X25519_KEYLEN);
+        }
+        X25519_public_from_private(xkey->pubkey, xkey->privkey);
+    }
+
+    EVP_PKEY_assign(pkey, NID_X25519, xkey);
+    return 1;
+}
+
+static int ecx_pub_encode(X509_PUBKEY *pk, const EVP_PKEY *pkey)
+{
+    const X25519_KEY *xkey = pkey->pkey.ptr;
+    unsigned char *penc;
+
+    if (xkey == NULL) {
+        ECerr(EC_F_ECX_PUB_ENCODE, EC_R_INVALID_KEY);
+        return 0;
+    }
+
+    penc = OPENSSL_memdup(xkey->pubkey, X25519_KEYLEN);
+    if (penc == NULL) {
+        ECerr(EC_F_ECX_PUB_ENCODE, ERR_R_MALLOC_FAILURE);
+        return 0;
+    }
+
+    if (!X509_PUBKEY_set0_param(pk, OBJ_nid2obj(NID_X25519), V_ASN1_UNDEF,
+                                NULL, penc, X25519_KEYLEN)) {
+        OPENSSL_free(penc);
+        ECerr(EC_F_ECX_PUB_ENCODE, ERR_R_MALLOC_FAILURE);
+        return 0;
+    }
+    return 1;
+}
+
+static int ecx_pub_decode(EVP_PKEY *pkey, X509_PUBKEY *pubkey)
+{
+    const unsigned char *p;
+    int pklen;
+    X509_ALGOR *palg;
+
+    if (!X509_PUBKEY_get0_param(NULL, &p, &pklen, &palg, pubkey))
+        return 0;
+    return ecx_key_op(pkey, palg, p, pklen, X25519_PUBLIC);
+}
+
+static int ecx_pub_cmp(const EVP_PKEY *a, const EVP_PKEY *b)
+{
+    const X25519_KEY *akey = a->pkey.ptr;
+    const X25519_KEY *bkey = b->pkey.ptr;
+
+    if (akey == NULL || bkey == NULL)
+        return -2;
+    return !CRYPTO_memcmp(akey->pubkey, bkey->pubkey, X25519_KEYLEN);
+}
+
+static int ecx_priv_decode(EVP_PKEY *pkey, const PKCS8_PRIV_KEY_INFO *p8)
+{
+    const unsigned char *p;
+    int plen;
+    ASN1_OCTET_STRING *oct = NULL;
+    const X509_ALGOR *palg;
+    int rv;
+
+    if (!PKCS8_pkey_get0(NULL, &p, &plen, &palg, p8))
+        return 0;
+
+    oct = d2i_ASN1_OCTET_STRING(NULL, &p, plen);
+    if (oct == NULL) {
+        p = NULL;
+        plen = 0;
+    } else {
+        p = ASN1_STRING_get0_data(oct);
+        plen = ASN1_STRING_length(oct);
+    }
+
+    rv = ecx_key_op(pkey, palg, p, plen, X25519_PRIVATE);
+    ASN1_OCTET_STRING_free(oct);
+    return rv;
+}
+
+static int ecx_priv_encode(PKCS8_PRIV_KEY_INFO *p8, const EVP_PKEY *pkey)
+{
+    const X25519_KEY *xkey = pkey->pkey.ptr;
+    ASN1_OCTET_STRING oct;
+    unsigned char *penc = NULL;
+    int penclen;
+
+    if (xkey == NULL || xkey->privkey == NULL) {
+        ECerr(EC_F_ECX_PRIV_ENCODE, EC_R_INVALID_PRIVATE_KEY);
+        return 0;
+    }
+
+    oct.data = xkey->privkey;
+    oct.length = X25519_KEYLEN;
+    oct.flags = 0;
+
+    penclen = i2d_ASN1_OCTET_STRING(&oct, &penc);
+    if (penclen < 0) {
+        ECerr(EC_F_ECX_PRIV_ENCODE, ERR_R_MALLOC_FAILURE);
+        return 0;
+    }
+
+    if (!PKCS8_pkey_set0(p8, OBJ_nid2obj(NID_X25519), 0,
+                         V_ASN1_UNDEF, NULL, penc, penclen)) {
+        OPENSSL_clear_free(penc, penclen);
+        ECerr(EC_F_ECX_PRIV_ENCODE, ERR_R_MALLOC_FAILURE);
+        return 0;
+    }
+
+    return 1;
+}
+
+static int ecx_size(const EVP_PKEY *pkey)
+{
+    return X25519_KEYLEN;
+}
+
+static int ecx_bits(const EVP_PKEY *pkey)
+{
+    return X25519_BITS;
+}
+
+static int ecx_security_bits(const EVP_PKEY *pkey)
+{
+    return X25519_SECURITY_BITS;
+}
+
+static void ecx_free(EVP_PKEY *pkey)
+{
+    X25519_KEY *xkey = pkey->pkey.ptr;
+
+    if (xkey)
+        OPENSSL_secure_free(xkey->privkey);
+    OPENSSL_free(xkey);
+}
+
+/* "parameters" are always equal */
+static int ecx_cmp_parameters(const EVP_PKEY *a, const EVP_PKEY *b)
+{
+    return 1;
+}
+
+static int ecx_key_print(BIO *bp, const EVP_PKEY *pkey, int indent,
+                         ASN1_PCTX *ctx, ecx_key_op_t op)
+{
+    const X25519_KEY *xkey = pkey->pkey.ptr;
+
+    if (op == X25519_PRIVATE) {
+        if (xkey == NULL || xkey->privkey == NULL) {
+            if (BIO_printf(bp, "%*s\n", indent, "") <= 0)
+                return 0;
+            return 1;
+        }
+        if (BIO_printf(bp, "%*sX25519 Private-Key:\n", indent, "") <= 0)
+            return 0;
+        if (BIO_printf(bp, "%*spriv:\n", indent, "") <= 0)
+            return 0;
+        if (ASN1_buf_print(bp, xkey->privkey, X25519_KEYLEN, indent + 4) == 0)
+            return 0;
+    } else {
+        if (xkey == NULL) {
+            if (BIO_printf(bp, "%*s\n", indent, "") <= 0)
+                return 0;
+            return 1;
+        }
+        if (BIO_printf(bp, "%*sX25519 Public-Key:\n", indent, "") <= 0)
+            return 0;
+    }
+    if (BIO_printf(bp, "%*spub:\n", indent, "") <= 0)
+        return 0;
+    if (ASN1_buf_print(bp, xkey->pubkey, X25519_KEYLEN, indent + 4) == 0)
+        return 0;
+    return 1;
+}
+
+static int ecx_priv_print(BIO *bp, const EVP_PKEY *pkey, int indent,
+                          ASN1_PCTX *ctx)
+{
+    return ecx_key_print(bp, pkey, indent, ctx, X25519_PRIVATE);
+}
+
+static int ecx_pub_print(BIO *bp, const EVP_PKEY *pkey, int indent,
+                         ASN1_PCTX *ctx)
+{
+    return ecx_key_print(bp, pkey, indent, ctx, X25519_PUBLIC);
+}
+
+static int ecx_ctrl(EVP_PKEY *pkey, int op, long arg1, void *arg2)
+{
+    switch (op) {
+
+    case ASN1_PKEY_CTRL_SET1_TLS_ENCPT:
+        return ecx_key_op(pkey, NULL, arg2, arg1, X25519_PUBLIC);
+
+    case ASN1_PKEY_CTRL_GET1_TLS_ENCPT:
+        if (pkey->pkey.ptr != NULL) {
+            const X25519_KEY *xkey = pkey->pkey.ptr;
+            unsigned char **ppt = arg2;
+            *ppt = OPENSSL_memdup(xkey->pubkey, X25519_KEYLEN);
+            if (*ppt != NULL)
+                return X25519_KEYLEN;
+        }
+        return 0;
+
+    case ASN1_PKEY_CTRL_DEFAULT_MD_NID:
+        *(int *)arg2 = NID_sha256;
+        return 2;
+
+    default:
+        return -2;
+
+    }
+}
+
+const EVP_PKEY_ASN1_METHOD ecx25519_asn1_meth = {
+    NID_X25519,
+    NID_X25519,
+    0,
+    "X25519",
+    "OpenSSL X25519 algorithm",
+
+    ecx_pub_decode,
+    ecx_pub_encode,
+    ecx_pub_cmp,
+    ecx_pub_print,
+
+    ecx_priv_decode,
+    ecx_priv_encode,
+    ecx_priv_print,
+
+    ecx_size,
+    ecx_bits,
+    ecx_security_bits,
+
+    0, 0, 0, 0,
+    ecx_cmp_parameters,
+    0, 0,
+
+    ecx_free,
+    ecx_ctrl,
+    NULL,
+    NULL
+};
+
+static int pkey_ecx_keygen(EVP_PKEY_CTX *ctx, EVP_PKEY *pkey)
+{
+    return ecx_key_op(pkey, NULL, NULL, 0, X25519_KEYGEN);
+}
+
+static int pkey_ecx_derive(EVP_PKEY_CTX *ctx, unsigned char *key,
+                           size_t *keylen)
+{
+    const X25519_KEY *pkey, *peerkey;
+
+    if (ctx->pkey == NULL || ctx->peerkey == NULL) {
+        ECerr(EC_F_PKEY_ECX_DERIVE, EC_R_KEYS_NOT_SET);
+        return 0;
+    }
+    pkey = ctx->pkey->pkey.ptr;
+    peerkey = ctx->peerkey->pkey.ptr;
+    if (pkey == NULL || pkey->privkey == NULL) {
+        ECerr(EC_F_PKEY_ECX_DERIVE, EC_R_INVALID_PRIVATE_KEY);
+        return 0;
+    }
+    if (peerkey == NULL) {
+        ECerr(EC_F_PKEY_ECX_DERIVE, EC_R_INVALID_PEER_KEY);
+        return 0;
+    }
+    *keylen = X25519_KEYLEN;
+    if (key != NULL && X25519(key, pkey->privkey, peerkey->pubkey) == 0)
+        return 0;
+    return 1;
+}
+
+static int pkey_ecx_ctrl(EVP_PKEY_CTX *ctx, int type, int p1, void *p2)
+{
+    /* Only need to handle peer key for derivation */
+    if (type == EVP_PKEY_CTRL_PEER_KEY)
+        return 1;
+    return -2;
+}
+
+const EVP_PKEY_METHOD ecx25519_pkey_meth = {
+    NID_X25519,
+    0, 0, 0, 0, 0, 0, 0,
+    pkey_ecx_keygen,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    pkey_ecx_derive,
+    pkey_ecx_ctrl,
+    0
+};
diff --git a/crypto/ecies/build.info b/crypto/ecies/build.info
new file mode 100644
index 000000000..6983895db
--- /dev/null
+++ b/crypto/ecies/build.info
@@ -0,0 +1,2 @@
+LIBS=../../libcrypto
+SOURCE[../../libcrypto]=ecies_asn1.c ecies_lib.c
diff --git a/crypto/ecies/ecies_asn1.c b/crypto/ecies/ecies_asn1.c
new file mode 100644
index 000000000..35af1e190
--- /dev/null
+++ b/crypto/ecies/ecies_asn1.c
@@ -0,0 +1,270 @@
+/* ====================================================================
+ * Copyright (c) 2007 - 2016 The GmSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the GmSSL Project.
+ *    (http://gmssl.org/)"
+ *
+ * 4. The name "GmSSL Project" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    guanzhi1980@gmail.com.
+ *
+ * 5. Products derived from this software may not be called "GmSSL"
+ *    nor may "GmSSL" appear in their names without prior written
+ *    permission of the GmSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the GmSSL Project
+ *    (http://gmssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE GmSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE GmSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include "ecies_lcl.h"
+
+/*
+ * From SEC 1, Version 1.9 Draft, 2008
+ *
+secg-scheme OBJECT IDENTIFIER ::= {
+	iso(1) identified-organization(3) certicom(132) schemes(1) }
+
+ECIESAlgorithmSet ALGORITHM ::= {
+	{ OID ecies-recommendedParameters} |
+	{ OID ecies-specifiedParameters PARMS ECIESParameters}
+}
+
+
+KeyDerivationFunction ::= AlgorithmIdentifier {{KDFSet}}
+KDFSet ALGORITHM ::= {
+	{ OID x9-63-kdf PARMS HashAlgorithm } |
+	{ OID nist-concatenation-kdf PARMS HashAlgorithm } |
+	{ OID tls-kdf PARMS HashAlgorithm } |
+	{ OID ikev2-kdf PARMS HashAlgorithm }
+}
+
+SymmetricEncryption ::= AlgorithmIdentifier {{SYMENCSet}}
+SYMENCSet ALGORITHM ::= {
+	{ OID xor-in-ecies } |
+	{ OID tdes-cbc-in-ecies } |  -- IV should all be set to ZEROs
+	{ OID aes128-cbc-in-ecies } |
+	{ OID aes192-cbc-in-ecies } |
+	{ OID aes256-cbc-in-ecies } |
+	{ OID aes128-ctr-in-ecies } |
+	{ OID aes192-ctr-in-ecies } |
+	{ OID aes256-ctr-in-ecies } ,
+}
+
+MessageAuthenticationCode ::= AlgorithmIdentifier {{MACSet}}
+MACSet ALGORITHM ::= {
+	{ OID hmac-full-ecies PARMS HashAlgorithm} |
+	{ OID hmac-half-ecies PARMS HashAlgorithm} |
+	{ OID cmac-aes128-ecies } |
+	{ OID cmac-aes192-ecies } |
+	{ OID cmac-aes256-ecies } ,
+}
+
+*/
+
+/*
+ECIESParameters ::= SEQUENCE {
+	kdf [0] KeyDerivationFunction OPTIONAL,
+	sym [1] SymmetricEncryption OPTIONAL,
+	mac [2] MessageAuthenticationCode OPTIONAL
+}
+*/
+
+typedef struct ecies_parameters_st {
+	X509_ALGOR *kdf;
+	X509_ALGOR *sym;
+	X509_ALGOR *mac;
+} ECIES_PARAMETERS;
+
+ASN1_SEQUENCE(ECIES_PARAMETERS) = {
+	ASN1_EXP_OPT(ECIES_PARAMETERS, kdf, X509_ALGOR, 0),
+	ASN1_EXP_OPT(ECIES_PARAMETERS, sym, X509_ALGOR, 1),
+	ASN1_EXP_OPT(ECIES_PARAMETERS, mac, X509_ALGOR, 2)
+} ASN1_SEQUENCE_END(ECIES_PARAMETERS)
+IMPLEMENT_ASN1_FUNCTIONS(ECIES_PARAMETERS)
+IMPLEMENT_ASN1_DUP_FUNCTION(ECIES_PARAMETERS)
+
+ASN1_SEQUENCE(ECIES_CIPHERTEXT_VALUE) = {
+	ASN1_SIMPLE(ECIES_CIPHERTEXT_VALUE, ephem_point, ASN1_OCTET_STRING),
+	ASN1_SIMPLE(ECIES_CIPHERTEXT_VALUE, ciphertext, ASN1_OCTET_STRING),
+	ASN1_SIMPLE(ECIES_CIPHERTEXT_VALUE, mactag, ASN1_OCTET_STRING)
+} ASN1_SEQUENCE_END(ECIES_CIPHERTEXT_VALUE)
+IMPLEMENT_ASN1_FUNCTIONS(ECIES_CIPHERTEXT_VALUE)
+IMPLEMENT_ASN1_DUP_FUNCTION(ECIES_CIPHERTEXT_VALUE)
+
+
+int i2d_ECIESParameters(const ECIES_PARAMS *param, unsigned char **out)
+{
+	int ret = 0;
+	ECIES_PARAMETERS *asn1 = NULL;
+
+	if (!(asn1 = ECIES_PARAMETERS_new())) {
+		ECerr(EC_F_I2D_ECIESPARAMETERS, ERR_R_MALLOC_FAILURE);
+		goto end;
+	}
+
+	OPENSSL_assert(asn1->kdf && asn1->sym && asn1->mac);
+
+	if (!X509_ALGOR_set0(asn1->kdf, OBJ_nid2obj(param->kdf_nid),
+		V_ASN1_OBJECT, OBJ_nid2obj(EVP_MD_nid(param->kdf_md)))) {
+		ECerr(EC_F_I2D_ECIESPARAMETERS, ERR_R_X509_LIB);
+		goto end;
+	}
+	if (!X509_ALGOR_set0(asn1->sym, OBJ_nid2obj(param->enc_nid),
+		V_ASN1_UNDEF, NULL)) {
+		ECerr(EC_F_I2D_ECIESPARAMETERS, ERR_R_X509_LIB);
+		goto end;
+	}
+	if (param->mac_nid == NID_hmac_full_ecies ||
+		param->mac_nid == NID_hmac_half_ecies) {
+		if (!X509_ALGOR_set0(asn1->mac, OBJ_nid2obj(param->mac_nid),
+			V_ASN1_OBJECT, OBJ_nid2obj(EVP_MD_nid(param->hmac_md)))) {
+			ECerr(EC_F_I2D_ECIESPARAMETERS, ERR_R_MALLOC_FAILURE);
+			goto end;
+		}
+	} else {
+		if (!X509_ALGOR_set0(asn1->mac, OBJ_nid2obj(param->mac_nid),
+			V_ASN1_UNDEF, NULL)) {
+			ECerr(EC_F_I2D_ECIESPARAMETERS, ERR_R_MALLOC_FAILURE);
+			goto end;
+		}
+	}
+
+	if ((ret = i2d_ECIES_PARAMETERS(asn1, out)) <= 0) {
+		ECerr(EC_F_I2D_ECIESPARAMETERS, ERR_R_ASN1_LIB);
+		goto end;
+	}
+
+end:
+	ECIES_PARAMETERS_free(asn1);
+	return ret;
+}
+
+ECIES_PARAMS *d2i_ECIESParameters(ECIES_PARAMS **param,
+	const unsigned char **in, long len)
+{
+	int e = 1;
+	ECIES_PARAMS *ret = NULL;
+	ECIES_PARAMETERS *asn1 = NULL;
+
+	if (!(ret = OPENSSL_zalloc(sizeof(ECIES_PARAMS)))) {
+		ECerr(EC_F_D2I_ECIESPARAMETERS, ERR_R_ASN1_LIB);
+		goto end;
+	}
+	if (!(asn1 = d2i_ECIES_PARAMETERS(NULL, in, len))) {
+		ECerr(EC_F_D2I_ECIESPARAMETERS, ERR_R_ASN1_LIB);
+		goto end;
+	}
+
+	/* kdf */
+	ret->kdf_nid = OBJ_obj2nid(asn1->kdf->algorithm);
+	if (ret->kdf_nid != NID_x9_63_kdf) {
+		ECerr(EC_F_D2I_ECIESPARAMETERS, EC_R_INVALID_ECIES_PARAMETERS);
+		goto end;
+	}
+	if (asn1->kdf->parameter->type != V_ASN1_OBJECT) {
+		ECerr(EC_F_D2I_ECIESPARAMETERS, EC_R_INVALID_ECIES_PARAMETERS);
+		goto end;
+	}
+	if (!(ret->kdf_md = EVP_get_digestbynid(
+		OBJ_obj2nid(asn1->kdf->parameter->value.object)))) {
+		ECerr(EC_F_D2I_ECIESPARAMETERS, EC_R_INVALID_ECIES_PARAMETERS);
+		goto end;
+	}
+
+	/* sym */
+	ret->enc_nid = OBJ_obj2nid(asn1->sym->algorithm);
+	switch (ret->enc_nid) {
+	case NID_xor_in_ecies:
+	case NID_tdes_cbc_in_ecies:
+	case NID_aes128_cbc_in_ecies:
+	case NID_aes192_cbc_in_ecies:
+	case NID_aes256_cbc_in_ecies:
+	case NID_aes128_ctr_in_ecies:
+	case NID_aes192_ctr_in_ecies:
+	case NID_aes256_ctr_in_ecies:
+		break;
+	default:
+		ECerr(EC_F_D2I_ECIESPARAMETERS, EC_R_INVALID_ECIES_PARAMETERS);
+		goto end;
+	}
+
+	/* mac */
+	ret->mac_nid = OBJ_obj2nid(asn1->mac->algorithm);
+	switch (ret->enc_nid) {
+	case NID_hmac_full_ecies:
+	case NID_hmac_half_ecies:
+		if (asn1->mac->parameter->type != V_ASN1_OBJECT) {
+			ECerr(EC_F_D2I_ECIESPARAMETERS, EC_R_INVALID_ECIES_PARAMETERS);
+			goto end;
+		}
+		if (!(ret->hmac_md = EVP_get_digestbynid(
+			OBJ_obj2nid(asn1->mac->parameter->value.object)))) {
+			ECerr(EC_F_D2I_ECIESPARAMETERS, EC_R_INVALID_ECIES_PARAMETERS);
+			goto end;
+		}
+		break;
+	case NID_cmac_aes128_ecies:
+	case NID_cmac_aes192_ecies:
+	case NID_cmac_aes256_ecies:
+		break;
+	default:
+		ECerr(EC_F_D2I_ECIESPARAMETERS, EC_R_INVALID_ECIES_PARAMETERS);
+		goto end;
+	}
+
+	if (param && *param)
+		OPENSSL_free(*param);
+	if (param)
+		*param = ret;
+
+	e = 0;
+end:
+	if (e && ret) {
+		OPENSSL_free(ret);
+		ret = NULL;
+	}
+	ECIES_PARAMETERS_free(asn1);
+	return ret;
+}
+
diff --git a/crypto/ecies/ecies_lcl.h b/crypto/ecies/ecies_lcl.h
new file mode 100644
index 000000000..67d1d511a
--- /dev/null
+++ b/crypto/ecies/ecies_lcl.h
@@ -0,0 +1,66 @@
+/* ====================================================================
+ * Copyright (c) 2007 - 2017 The GmSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the GmSSL Project.
+ *    (http://gmssl.org/)"
+ *
+ * 4. The name "GmSSL Project" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    guanzhi1980@gmail.com.
+ *
+ * 5. Products derived from this software may not be called "GmSSL"
+ *    nor may "GmSSL" appear in their names without prior written
+ *    permission of the GmSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the GmSSL Project
+ *    (http://gmssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE GmSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE GmSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+
+#include 
+#include 
+
+struct ecies_ciphertext_value_st {
+	ASN1_OCTET_STRING *ephem_point;
+	ASN1_OCTET_STRING *ciphertext;
+	ASN1_OCTET_STRING *mactag;
+};
+
+int gmssl_ecies_encrypt(int type, const unsigned char *in, size_t inlen,
+	unsigned char *out, size_t *outlen, EC_KEY *ec_key);
+int gmssl_ecies_decrypt(int type, const unsigned char *in, size_t inlen,
+	unsigned char *out, size_t *outlen, EC_KEY *ec_key);
+ECIES_CIPHERTEXT_VALUE *gmssl_ecies_do_encrypt(int type,
+	const unsigned char *in, size_t inlen, EC_KEY *ec_key);
+int gmssl_ecies_do_decrypt(int type, const ECIES_CIPHERTEXT_VALUE *in,
+	unsigned char *out, size_t *outlen, EC_KEY *ec_key);
diff --git a/crypto/ecies/ecies_lib.c b/crypto/ecies/ecies_lib.c
new file mode 100644
index 000000000..63be3fb83
--- /dev/null
+++ b/crypto/ecies/ecies_lib.c
@@ -0,0 +1,765 @@
+/* ====================================================================
+ * Copyright (c) 2007 - 2017 The GmSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the GmSSL Project.
+ *    (http://gmssl.org/)"
+ *
+ * 4. The name "GmSSL Project" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    guanzhi1980@gmail.com.
+ *
+ * 5. Products derived from this software may not be called "GmSSL"
+ *    nor may "GmSSL" appear in their names without prior written
+ *    permission of the GmSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the GmSSL Project
+ *    (http://gmssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE GmSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE GmSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ */
+
+
+#include 
+#include 
+#include 
+#include 
+
+#ifndef OPENSSL_NO_AES
+# include 
+#endif
+#include 
+#ifndef OPENSSL_NO_CMAC
+# include 
+#endif
+#include 
+#include 
+#include 
+#include 
+#include "internal/o_str.h"
+#include "ecies_lcl.h"
+
+#define ECIES_ENC_RANDOM_IV	1
+
+int ECIES_PARAMS_init_with_type(ECIES_PARAMS *params, int type)
+{
+	if (!params) {
+		ECerr(EC_F_ECIES_PARAMS_INIT_WITH_TYPE, ERR_R_PASSED_NULL_PARAMETER);
+		return 0;
+	}
+
+	switch (type) {
+#ifndef OPENSSL_NO_SHA
+	case NID_ecies_with_x9_63_sha1_xor_hmac:
+		params->kdf_nid = NID_x9_63_kdf;
+		params->kdf_md = EVP_sha1();
+		params->enc_nid = NID_xor_in_ecies;
+		params->mac_nid = NID_hmac_full_ecies;
+		params->hmac_md = EVP_sha1();
+		break;
+# ifndef OPENSSL_NO_SHA256
+	case NID_ecies_with_x9_63_sha256_xor_hmac:
+		params->kdf_nid = NID_x9_63_kdf;
+		params->kdf_md = EVP_sha256();
+		params->enc_nid = NID_xor_in_ecies;
+		params->mac_nid = NID_hmac_full_ecies;
+		params->hmac_md = EVP_sha256();
+		break;
+# endif
+#endif
+	default:
+		ECerr(EC_F_ECIES_PARAMS_INIT_WITH_TYPE, EC_R_INVALID_ECIES_PARAMS);
+		return 0;
+	}
+
+	return 1;
+}
+
+int ECIES_PARAMS_init_with_recommended(ECIES_PARAMS *param)
+{
+	if (!param) {
+		ECerr(EC_F_ECIES_PARAMS_INIT_WITH_RECOMMENDED,
+			ERR_R_PASSED_NULL_PARAMETER);
+		return 0;
+	}
+
+	memset(param, 0, sizeof(*param));
+
+#ifndef OPENSSL_NO_SHA256
+	param->kdf_nid = NID_x9_63_kdf;
+	param->kdf_md = EVP_sha256();
+	param->enc_nid = NID_xor_in_ecies;
+	param->mac_nid = NID_hmac_full_ecies;
+	param->hmac_md = EVP_sha256();
+	return 1;
+#else
+	ECerr(EC_F_ECIES_PARAMS_INIT_WITH_RECOMMENDED,
+		EC_R_INVALID_ECIES_PARAMS);
+	return 0;
+#endif
+}
+
+KDF_FUNC ECIES_PARAMS_get_kdf(const ECIES_PARAMS *param)
+{
+	if (!param || !param->kdf_md) {
+		ECerr(EC_F_ECIES_PARAMS_GET_KDF, ERR_R_PASSED_NULL_PARAMETER);
+		return NULL;
+	}
+
+	switch (param->kdf_nid) {
+	case NID_x9_63_kdf:
+		return KDF_get_x9_63(param->kdf_md);
+	case NID_nist_concatenation_kdf:
+	case NID_tls_kdf:
+	case NID_ikev2_kdf:
+		ECerr(EC_F_ECIES_PARAMS_GET_KDF, EC_R_NOT_IMPLEMENTED);
+		return NULL;
+	}
+
+	ECerr(EC_F_ECIES_PARAMS_GET_KDF, EC_R_INVALID_ECIES_PARAMETERS);
+	return NULL;
+}
+
+int ECIES_PARAMS_get_enc(const ECIES_PARAMS *param, size_t inlen,
+	const EVP_CIPHER **enc_cipher, size_t *enckeylen, size_t *ciphertextlen)
+{
+	const EVP_CIPHER *cipher = NULL;
+	size_t keylen;
+	size_t outlen;
+
+	if (!param || !enc_cipher || !enckeylen || !ciphertextlen) {
+		ECerr(EC_F_ECIES_PARAMS_GET_ENC, ERR_R_PASSED_NULL_PARAMETER);
+		return 0;
+	}
+
+	switch (param->enc_nid) {
+	case NID_xor_in_ecies:
+		cipher = NULL;
+		keylen = inlen;
+		break;
+#ifndef OPENSSL_NO_AES
+	case NID_aes128_cbc_in_ecies:
+		cipher = EVP_aes_128_cbc();
+		break;
+	case NID_aes192_cbc_in_ecies:
+		cipher = EVP_aes_192_cbc();
+		break;
+	case NID_aes256_cbc_in_ecies:
+		cipher = EVP_aes_256_cbc();
+		break;
+	case NID_aes128_ctr_in_ecies:
+		cipher = EVP_aes_128_ctr();
+		break;
+	case NID_aes192_ctr_in_ecies:
+		cipher = EVP_aes_192_ctr();
+		break;
+	case NID_aes256_ctr_in_ecies:
+		cipher = EVP_aes_256_ctr();
+		break;
+#endif
+	default:
+		ECerr(EC_F_ECIES_PARAMS_GET_ENC, EC_R_INVALID_ECIES_PARAMETERS);
+		return 0;
+	}
+
+	outlen = inlen;
+	if (cipher) {
+		int blocksize = EVP_CIPHER_block_size(cipher);
+		keylen = EVP_CIPHER_key_length(cipher);
+		if (ECIES_ENC_RANDOM_IV) {
+			outlen += blocksize;
+		}
+		if (EVP_CIPHER_mode(cipher) == EVP_CIPH_CBC_MODE) {
+			outlen += blocksize - inlen % blocksize;
+		}
+	}
+
+	*enc_cipher = cipher;
+	*enckeylen = keylen;
+	*ciphertextlen = outlen;
+
+	return 1;
+}
+
+int ECIES_PARAMS_get_mac(const ECIES_PARAMS *param,
+	const EVP_MD **hmac_md, const EVP_CIPHER **cmac_cipher,
+	unsigned int *mackeylen, unsigned int *maclen)
+{
+	const EVP_CIPHER *cipher = NULL;
+	const EVP_MD *md = NULL;
+	unsigned int keylen;
+	unsigned int outlen;
+
+	if (!param || !hmac_md || !cmac_cipher || !mackeylen || !maclen) {
+		ECerr(EC_F_ECIES_PARAMS_GET_MAC, ERR_R_PASSED_NULL_PARAMETER);
+		return 0;
+	}
+
+	switch (param->mac_nid) {
+	case NID_hmac_full_ecies:
+		if (!(md = param->hmac_md)) {
+			ECerr(EC_F_ECIES_PARAMS_GET_MAC,
+				EC_R_INVALID_ECIES_PARAMETERS);
+			return 0;
+		}
+		keylen = EVP_MD_size(md);
+		outlen = EVP_MD_size(md);
+		break;
+	case NID_hmac_half_ecies:
+		if (!(md = param->hmac_md)) {
+			ECerr(EC_F_ECIES_PARAMS_GET_MAC,
+				EC_R_INVALID_ECIES_PARAMETERS);
+			return 0;
+		}
+		keylen = EVP_MD_size(md);
+		outlen = EVP_MD_size(md)/2;
+		break;
+#ifndef OPENSSL_NO_AES
+	case NID_cmac_aes128_ecies:
+		cipher = EVP_aes_128_ecb();
+		break;
+	case NID_cmac_aes192_ecies:
+		cipher = EVP_aes_192_ecb();
+		break;
+	case NID_cmac_aes256_ecies:
+		cipher = EVP_aes_256_ecb();
+		break;
+#endif
+	default:
+		ECerr(EC_F_ECIES_PARAMS_GET_MAC,
+			EC_R_INVALID_ECIES_PARAMETERS);
+		return 0;
+	}
+
+	if (cipher) {
+		keylen = EVP_CIPHER_key_length(cipher);
+		outlen = EVP_CIPHER_block_size(cipher);
+	}
+
+	*hmac_md = md;
+	*cmac_cipher = cipher;
+	*mackeylen = keylen;
+	*maclen = outlen;
+
+	return 1;
+}
+
+int ECIES_CIPHERTEXT_VALUE_ciphertext_length(const ECIES_CIPHERTEXT_VALUE *a)
+{
+	return ASN1_STRING_length(a->ciphertext);
+}
+
+ECIES_CIPHERTEXT_VALUE *ECIES_do_encrypt(const ECIES_PARAMS *param,
+	const unsigned char *in, size_t inlen, EC_KEY *ec_key)
+{
+	int e = 1;
+	KDF_FUNC kdf_func;
+	const EVP_CIPHER *enc_cipher = NULL;
+	size_t enckeylen, ciphertextlen;
+	const EVP_MD *hmac_md = NULL;
+	const EVP_CIPHER *mac_cipher = NULL;
+	unsigned int mackeylen, maclen;
+	ECIES_CIPHERTEXT_VALUE *ret = NULL;
+	const EC_GROUP *group = EC_KEY_get0_group(ec_key);
+	EC_KEY *ephem_key = NULL;
+	int point_form = POINT_CONVERSION_COMPRESSED;
+	unsigned char *sharekey = NULL;
+	unsigned int sharekeylen;
+	unsigned char *enckey, *mackey;
+	unsigned char mac[EVP_MAX_MD_SIZE];
+	size_t len;
+
+	if (!param || !in || !inlen || !ec_key || !group) {
+		ECerr(EC_F_ECIES_DO_ENCRYPT, ERR_R_PASSED_NULL_PARAMETER);
+		return 0;
+	}
+
+	/* parse parameters */
+	if (!(kdf_func = ECIES_PARAMS_get_kdf(param))) {
+		ECerr(EC_F_ECIES_DO_ENCRYPT, EC_R_INVALID_ECIES_PARAMETERS);
+		goto end;
+	}
+	if (!ECIES_PARAMS_get_enc(param, inlen, &enc_cipher, &enckeylen, &ciphertextlen)) {
+		ECerr(EC_F_ECIES_DO_ENCRYPT, EC_R_INVALID_ECIES_PARAMETERS);
+		goto end;
+	}
+	if (!ECIES_PARAMS_get_mac(param, &hmac_md, &mac_cipher, &mackeylen, &maclen)) {
+		ECerr(EC_F_ECIES_DO_ENCRYPT, EC_R_INVALID_ECIES_PARAMETERS);
+		goto end;
+	}
+
+	/* malloc ciphertext value */
+	if (!(ret = ECIES_CIPHERTEXT_VALUE_new())) {
+		ECerr(EC_F_ECIES_DO_ENCRYPT, ERR_R_MALLOC_FAILURE);
+		return 0;
+	}
+
+	/* generate ephem keypair */
+	if (!(ephem_key = EC_KEY_new())) {
+		ECerr(EC_F_ECIES_DO_ENCRYPT, ERR_R_MALLOC_FAILURE);
+		goto end;
+	}
+	if (!EC_KEY_set_group(ephem_key, group)) {
+		ECerr(EC_F_ECIES_DO_ENCRYPT, ERR_R_EC_LIB);
+		goto end;
+	}
+	if (!EC_KEY_generate_key(ephem_key)) {
+		ECerr(EC_F_ECIES_DO_ENCRYPT, ERR_R_EC_LIB);
+		goto end;
+	}
+
+	/* output ephem_point */
+	len = EC_POINT_point2oct(group, EC_KEY_get0_public_key(ephem_key),
+		point_form, NULL, 0, NULL);
+	if (!ASN1_OCTET_STRING_set(ret->ephem_point, NULL, len)) {
+		ECerr(EC_F_ECIES_DO_ENCRYPT, ERR_R_ASN1_LIB);
+		goto end;
+	}
+	if (EC_POINT_point2oct(group, EC_KEY_get0_public_key(ephem_key),
+		point_form, ret->ephem_point->data, len, NULL) <= 0) {
+		ECerr(EC_F_ECIES_DO_ENCRYPT, ERR_R_EC_LIB);
+		goto end;
+	}
+
+	/* ecdh to generate enckey and mackey */
+	sharekeylen = enckeylen + mackeylen;
+	if (!(sharekey = OPENSSL_malloc(sharekeylen))) {
+		ECerr(EC_F_ECIES_DO_ENCRYPT, ERR_R_MALLOC_FAILURE);
+		goto end;
+	}
+ 	if (!ECDH_compute_key(sharekey, sharekeylen,
+		EC_KEY_get0_public_key(ec_key), ephem_key,
+		kdf_func)) {
+		ECerr(EC_F_ECIES_DO_ENCRYPT, EC_R_ECDH_FAILED);
+		goto end;
+	}
+	enckey = sharekey;
+	mackey = sharekey + enckeylen;
+
+	/* encrypt */
+	if (!ASN1_OCTET_STRING_set(ret->ciphertext, NULL, ciphertextlen)) {
+		ECerr(EC_F_ECIES_DO_ENCRYPT, ERR_R_MALLOC_FAILURE);
+		goto end;
+	}
+	if (enc_cipher) {
+		EVP_CIPHER_CTX *cipher_ctx = NULL;
+		unsigned char ivbuf[EVP_MAX_IV_LENGTH];
+		unsigned char *iv, *pout;
+		unsigned int ivlen, len;
+
+		ivlen = EVP_CIPHER_iv_length(enc_cipher);
+		if (ECIES_ENC_RANDOM_IV) {
+			iv = ret->ciphertext->data;
+			pout = ret->ciphertext->data + ivlen;
+			RAND_bytes(iv, ivlen);
+		} else {
+			iv = ivbuf;
+			pout = ret->ciphertext->data;
+			memset(iv, 0, ivlen);
+		}
+
+		if (!(cipher_ctx = EVP_CIPHER_CTX_new())) {
+			ECerr(EC_F_ECIES_DO_ENCRYPT, ERR_R_MALLOC_FAILURE);
+			goto end;
+		}
+		if (!EVP_EncryptInit(cipher_ctx, enc_cipher, enckey, iv)) {
+			ECerr(EC_F_ECIES_DO_ENCRYPT, EC_R_ENCRYPT_FAILED);
+			EVP_CIPHER_CTX_free(cipher_ctx);
+			goto end;
+		}
+		if (!EVP_EncryptUpdate(cipher_ctx, pout, (int *)&len, in, inlen)) {
+			ECerr(EC_F_ECIES_DO_ENCRYPT, EC_R_ENCRYPT_FAILED);
+			EVP_CIPHER_CTX_free(cipher_ctx);
+			goto end;
+		}
+		pout += len;
+		if (!EVP_EncryptFinal(cipher_ctx, pout, (int *)&len)) {
+			ECerr(EC_F_ECIES_DO_ENCRYPT, EC_R_ENCRYPT_FAILED);
+			goto end;
+		}
+		pout += len;
+
+		OPENSSL_assert(pout - ret->ciphertext->data == ciphertextlen);
+
+	} else {
+		int i;
+		for (i = 0; i < ret->ciphertext->length; i++) {
+			ret->ciphertext->data[i] = in[i] ^ enckey[i];
+		}
+	}
+
+	/* generate mac */
+	if (mac_cipher) {
+		CMAC_CTX *cmac_ctx;
+		if (!(cmac_ctx = CMAC_CTX_new())) {
+			ECerr(EC_F_ECIES_DO_ENCRYPT, ERR_R_MALLOC_FAILURE);
+			goto end;
+		}
+		if (!CMAC_Init(cmac_ctx, mackey, mackeylen, mac_cipher, NULL)) {
+			ECerr(EC_F_ECIES_DO_ENCRYPT, EC_R_CMAC_INIT_FAILURE);
+			CMAC_CTX_free(cmac_ctx);
+			goto end;
+		}
+		if (!CMAC_Update(cmac_ctx, ret->ciphertext->data, ret->ciphertext->length)) {
+			ECerr(EC_F_ECIES_DO_ENCRYPT, EC_R_CMAC_UPDATE_FAILURE);
+			CMAC_CTX_free(cmac_ctx);
+			goto end;
+		}
+		len = sizeof(mac);
+		if (!CMAC_Final(cmac_ctx, mac, &len)) {
+			ECerr(EC_F_ECIES_DO_ENCRYPT, EC_R_CMAC_FINAL_FAILURE);
+			CMAC_CTX_free(cmac_ctx);
+			goto end;
+		}
+		OPENSSL_assert(len == maclen);
+		CMAC_CTX_free(cmac_ctx);
+
+	} else {
+		len = sizeof(mac);
+		if (!HMAC(param->hmac_md, mackey, mackeylen,
+			ret->ciphertext->data, ret->ciphertext->length,
+			mac, &maclen)) {
+			ECerr(EC_F_ECIES_DO_ENCRYPT, EC_R_HMAC_FAILURE);
+			goto end;
+		}
+		OPENSSL_assert(len == maclen || len/2 == maclen);
+	}
+
+	if (!ASN1_OCTET_STRING_set(ret->mactag, mac, maclen)) {
+		ECerr(EC_F_ECIES_DO_ENCRYPT, ERR_R_MALLOC_FAILURE);
+		goto end;
+	}
+
+
+	e = 0;
+end:
+	EC_KEY_free(ephem_key);
+	OPENSSL_free(sharekey);
+	if (e && ret) {
+		ECIES_CIPHERTEXT_VALUE_free(ret);
+		ret = NULL;
+	}
+	return ret;
+}
+
+int ECIES_do_decrypt(const ECIES_PARAMS *param, const ECIES_CIPHERTEXT_VALUE *in,
+	unsigned char *out, size_t *outlen, EC_KEY *ec_key)
+{
+	int ret = 0;
+	KDF_FUNC kdf_func;
+	const EVP_CIPHER *enc_cipher = NULL;
+	size_t enckeylen, ciphertextlen;
+	const EVP_MD *hmac_md = NULL;
+	const EVP_CIPHER *mac_cipher = NULL;
+	unsigned int mackeylen, maclen;
+	const EC_GROUP *group = EC_KEY_get0_group(ec_key);
+	EC_POINT *ephem_point = NULL;
+	unsigned char *sharekey = NULL;
+	unsigned int sharekeylen;
+	unsigned char *enckey, *mackey;
+	unsigned char mac[EVP_MAX_MD_SIZE];
+	size_t len;
+
+	if (!param || !in || !outlen || !ec_key || !group) {
+		ECerr(EC_F_ECIES_DO_DECRYPT, ERR_R_PASSED_NULL_PARAMETER);
+		return 0;
+	}
+
+	if (!out) {
+		*outlen = in->ciphertext->length;
+		return 1;
+	}
+	if (*outlen < in->ciphertext->length) {
+		ECerr(EC_F_ECIES_DO_DECRYPT, EC_R_BUFFER_TOO_SMALL);
+		return 0;
+	}
+
+	/* parse parameters */
+	if (!(kdf_func = ECIES_PARAMS_get_kdf(param))) {
+		ECerr(EC_F_ECIES_DO_DECRYPT, EC_R_INVALID_ECIES_PARAMETERS);
+		goto end;
+	}
+	if (!ECIES_PARAMS_get_enc(param, in->ciphertext->length,
+		&enc_cipher, &enckeylen, &ciphertextlen)) {
+		ECerr(EC_F_ECIES_DO_DECRYPT, EC_R_INVALID_ECIES_PARAMETERS);
+		goto end;
+	}
+	if (!ECIES_PARAMS_get_mac(param, &hmac_md, &mac_cipher, &mackeylen, &maclen)) {
+		ECerr(EC_F_ECIES_DO_DECRYPT, EC_R_INVALID_ECIES_PARAMETERS);
+		goto end;
+	}
+
+	/* parse ephem_point */
+	if (!in->ephem_point || !in->ephem_point->data || in->ephem_point->length <= 0) {
+		ECerr(EC_F_ECIES_DO_DECRYPT, EC_R_INVALID_ECIES_CIPHERTEXT);
+		goto end;
+	}
+	if (!(ephem_point = EC_POINT_new(group))) {
+		ECerr(EC_F_ECIES_DO_DECRYPT, ERR_R_MALLOC_FAILURE);
+		goto end;
+	}
+	if (!EC_POINT_oct2point(group, ephem_point,
+		in->ephem_point->data, in->ephem_point->length, NULL)) {
+		ECerr(EC_F_ECIES_DO_DECRYPT, EC_R_INVALID_ECIES_CIPHERTEXT);
+		goto end;
+	}
+
+	/* compute ecdh, get enckey and mackey */
+	sharekeylen = enckeylen + mackeylen;
+	if (!(sharekey = OPENSSL_malloc(sharekeylen))) {
+		ECerr(EC_F_ECIES_DO_DECRYPT, ERR_R_MALLOC_FAILURE);
+		goto end;
+	}
+	if (!ECDH_compute_key(sharekey, sharekeylen,
+		ephem_point, ec_key, kdf_func)) {
+		ECerr(EC_F_ECIES_DO_DECRYPT, EC_R_ECDH_FAILURE);
+		goto end;
+	}
+	enckey = sharekey;
+	mackey = sharekey + enckeylen;
+
+	/* generate and verify mac */
+	if (!in->mactag || !in->mactag->data) {
+		ECerr(EC_F_ECIES_DO_DECRYPT, EC_R_INVALID_ECIES_CIPHERTEXT);
+		goto end;
+	}
+
+	if (mac_cipher) {
+		CMAC_CTX *cmac_ctx;
+		if (!(cmac_ctx = CMAC_CTX_new())) {
+			ECerr(EC_F_ECIES_DO_DECRYPT, ERR_R_MALLOC_FAILURE);
+			goto end;
+		}
+		if (!CMAC_Init(cmac_ctx, mackey, mackeylen, mac_cipher, NULL)) {
+			ECerr(EC_F_ECIES_DO_DECRYPT, EC_R_CMAC_INIT_FAILURE);
+			CMAC_CTX_free(cmac_ctx);
+			goto end;
+		}
+		if (!CMAC_Update(cmac_ctx, in->ciphertext->data, in->ciphertext->length)) {
+			ECerr(EC_F_ECIES_DO_DECRYPT, EC_R_CMAC_UPDATE_FAILURE);
+			CMAC_CTX_free(cmac_ctx);
+			goto end;
+		}
+		len = sizeof(mac);
+		if (!CMAC_Final(cmac_ctx, mac, &len)) {
+			ECerr(EC_F_ECIES_DO_DECRYPT, EC_R_CMAC_FINAL_FAILURE);
+			CMAC_CTX_free(cmac_ctx);
+			goto end;
+		}
+		OPENSSL_assert(len == maclen);
+		CMAC_CTX_free(cmac_ctx);
+
+	} else {
+		unsigned int ulen;
+		ulen = sizeof(mac);
+		if (!HMAC(param->hmac_md, mackey, mackeylen,
+			in->ciphertext->data, in->ciphertext->length,
+			mac, &ulen)) {
+			ECerr(EC_F_ECIES_DO_DECRYPT, EC_R_GEN_MAC_FAILED);
+			goto end;
+		}
+		len = (size_t)ulen;
+		OPENSSL_assert(len == maclen || len/2 == maclen);
+	}
+
+	if (maclen != in->mactag->length) {
+		ECerr(EC_F_ECIES_DO_DECRYPT, EC_R_ECIES_VERIFY_MAC_FAILURE);
+		goto end;
+	}
+	if (OPENSSL_memcmp(in->mactag->data, mac, maclen)) {
+		ECerr(EC_F_ECIES_DO_DECRYPT, EC_R_ECIES_VERIFY_MAC_FAILURE);
+		goto end;
+	}
+
+	/* decrypt */
+	if (enc_cipher) {
+		EVP_CIPHER_CTX *cipher_ctx = NULL;
+		unsigned char ivbuf[EVP_MAX_IV_LENGTH];
+		unsigned char *iv, *pin, *pout;
+		unsigned int ivlen, inlen;
+		int ilen;
+
+		/* prepare iv */
+		ivlen = EVP_CIPHER_iv_length(enc_cipher);
+		if (ECIES_ENC_RANDOM_IV) {
+			iv = in->ciphertext->data;
+			pin = in->ciphertext->data + ivlen;
+			if (in->ciphertext->length < ivlen) {
+				ECerr(EC_F_ECIES_DO_DECRYPT, EC_R_INVALID_ECIES_CIPHERTEXT);
+				goto end;
+			}
+			inlen = in->ciphertext->length - ivlen;
+		} else {
+			/* use fixed all-zero iv */
+			memset(ivbuf, 0, ivlen);
+			iv = ivbuf;
+			pin = in->ciphertext->data;
+			if (in->ciphertext->length <= 0) {
+				ECerr(EC_F_ECIES_DO_DECRYPT, EC_R_INVALID_ECIES_CIPHERTEXT);
+				goto end;
+			}
+			inlen = in->ciphertext->length;
+		}
+
+		/* decrypt */
+		if (!(cipher_ctx = EVP_CIPHER_CTX_new())) {
+			ECerr(EC_F_ECIES_DO_DECRYPT, ERR_R_MALLOC_FAILURE);
+			goto end;
+		}
+
+		if (!EVP_DecryptInit(cipher_ctx, enc_cipher, enckey, iv)) {
+			ECerr(EC_F_ECIES_DO_DECRYPT, EC_R_ECIES_DECRYPT_INIT_FAILURE);
+			EVP_CIPHER_CTX_free(cipher_ctx);
+			goto end;
+		}
+		pout = out;
+		ilen = (int)*outlen; //FIXME: do we need to check it?
+		if (!EVP_DecryptUpdate(cipher_ctx, pout, &ilen, pin, inlen)) {
+			ECerr(EC_F_ECIES_DO_DECRYPT, EC_R_DECRYPT_FAILED);
+			EVP_CIPHER_CTX_free(cipher_ctx);
+			goto end;
+		}
+		pout += ilen;
+		if (!EVP_DecryptFinal(cipher_ctx, pout, &ilen)) {
+			ECerr(EC_F_ECIES_DO_DECRYPT, EC_R_DECRYPT_FAILED);
+			EVP_CIPHER_CTX_free(cipher_ctx);
+			goto end;
+		}
+		pout += ilen;
+		EVP_CIPHER_CTX_free(cipher_ctx);
+		*outlen = pout - out;
+
+	} else {
+		unsigned int i;
+		for (i = 0; i < in->ciphertext->length; i++) {
+			out[i] = in->ciphertext->data[i] ^ enckey[i];
+		}
+		*outlen = in->ciphertext->length;
+	}
+
+	ret = 1;
+end:
+	OPENSSL_free(sharekey);
+	EC_POINT_free(ephem_point);
+	return ret;
+}
+
+int ECIES_encrypt(int type, const unsigned char *in, size_t inlen,
+	unsigned char *out, size_t *outlen, EC_KEY *ec_key)
+{
+	int ret = 0;
+	ECIES_PARAMS param;
+	ECIES_CIPHERTEXT_VALUE *cv = NULL;
+	unsigned char *p = out;
+	int len;
+
+	if (!ECIES_PARAMS_init_with_type(¶m, type)) {
+		ECerr(EC_F_ECIES_ENCRYPT, EC_R_INVALID_ENC_PARAM);
+		return 0;
+	}
+
+	RAND_seed(in, inlen);
+	if (!(cv = ECIES_do_encrypt(¶m, in, inlen, ec_key))) {
+		ECerr(EC_F_ECIES_ENCRYPT, EC_R_ENCRYPT_FAILED);
+		return 0;
+	}
+
+	if ((len = i2d_ECIES_CIPHERTEXT_VALUE(cv, NULL)) <= 0) {
+		ECerr(EC_F_ECIES_ENCRYPT, EC_R_ENCRYPT_FAILED);
+		goto end;
+	}
+
+	if (!out) {
+		*outlen = (size_t)len;
+		ret = 1;
+		goto end;
+	} else if (*outlen < len) {
+		ECerr(EC_F_ECIES_ENCRYPT, EC_R_BUFFER_TOO_SMALL);
+		*outlen = (size_t)len;
+		goto end;
+	}
+
+	if ((len = i2d_ECIES_CIPHERTEXT_VALUE(cv, &p)) <= 0) {
+		ECerr(EC_F_ECIES_ENCRYPT, EC_R_ENCRYPT_FAILED);
+		goto end;
+	}
+
+	*outlen = (size_t)len;
+	ret = 1;
+
+end:
+	ECIES_CIPHERTEXT_VALUE_free(cv);
+	return ret;
+}
+
+int ECIES_decrypt(int type, const unsigned char *in, size_t inlen,
+	unsigned char *out, size_t *outlen, EC_KEY *ec_key)
+{
+	int ret = 0;
+	ECIES_PARAMS param;
+	ECIES_CIPHERTEXT_VALUE *cv = NULL;
+
+	if (!in) {
+		ECerr(EC_F_ECIES_DECRYPT, ERR_R_PASSED_NULL_PARAMETER);
+		return 0;
+	}
+
+	if (inlen <= 0 || inlen > INT_MAX) {
+		ECerr(EC_F_ECIES_DECRYPT, EC_R_INVALID_INPUT_LENGTH);
+		return 0;
+	}
+
+	if (!ECIES_PARAMS_init_with_type(¶m, type)) {
+		ECerr(EC_F_ECIES_DECRYPT, EC_R_INVALID_ENC_PARAM);
+		return 0;
+	}
+
+	if (!(cv = d2i_ECIES_CIPHERTEXT_VALUE(NULL, &in, (long)inlen))) {
+		ECerr(EC_F_ECIES_DECRYPT, EC_R_INVALID_ECIES_CIPHERTEXT);
+		return 0;
+	}
+
+	if (inlen != i2d_ECIES_CIPHERTEXT_VALUE(cv, NULL)) {
+		ECerr(EC_F_ECIES_DECRYPT, EC_R_INVALID_ECIES_CIPHERTEXT);
+		goto end;
+	}
+
+	if (!ECIES_do_decrypt(¶m, cv, out, outlen, ec_key)) {
+		ECerr(EC_F_ECIES_DECRYPT, EC_R_ENCRYPT_FAILURE);
+		goto end;
+	}
+
+	ret = 1;
+end:
+	ECIES_CIPHERTEXT_VALUE_free(cv);
+	return ret;
+}
diff --git a/crypto/engine/README b/crypto/engine/README
new file mode 100644
index 000000000..41baa184c
--- /dev/null
+++ b/crypto/engine/README
@@ -0,0 +1,211 @@
+Notes: 2001-09-24
+-----------------
+
+This "description" (if one chooses to call it that) needed some major updating
+so here goes. This update addresses a change being made at the same time to
+OpenSSL, and it pretty much completely restructures the underlying mechanics of
+the "ENGINE" code. So it serves a double purpose of being a "ENGINE internals
+for masochists" document *and* a rather extensive commit log message. (I'd get
+lynched for sticking all this in CHANGES or the commit mails :-).
+
+ENGINE_TABLE underlies this restructuring, as described in the internal header
+"eng_int.h", implemented in eng_table.c, and used in each of the "class" files;
+tb_rsa.c, tb_dsa.c, etc.
+
+However, "EVP_CIPHER" underlies the motivation and design of ENGINE_TABLE so
+I'll mention a bit about that first. EVP_CIPHER (and most of this applies
+equally to EVP_MD for digests) is both a "method" and a algorithm/mode
+identifier that, in the current API, "lingers". These cipher description +
+implementation structures can be defined or obtained directly by applications,
+or can be loaded "en masse" into EVP storage so that they can be catalogued and
+searched in various ways, ie. two ways of encrypting with the "des_cbc"
+algorithm/mode pair are;
+
+(i) directly;
+     const EVP_CIPHER *cipher = EVP_des_cbc();
+     EVP_EncryptInit(&ctx, cipher, key, iv);
+     [ ... use EVP_EncryptUpdate() and EVP_EncryptFinal() ...]
+
+(ii) indirectly; 
+     OpenSSL_add_all_ciphers();
+     cipher = EVP_get_cipherbyname("des_cbc");
+     EVP_EncryptInit(&ctx, cipher, key, iv);
+     [ ... etc ... ]
+
+The latter is more generally used because it also allows ciphers/digests to be
+looked up based on other identifiers which can be useful for automatic cipher
+selection, eg. in SSL/TLS, or by user-controllable configuration.
+
+The important point about this is that EVP_CIPHER definitions and structures are
+passed around with impunity and there is no safe way, without requiring massive
+rewrites of many applications, to assume that EVP_CIPHERs can be reference
+counted. One an EVP_CIPHER is exposed to the caller, neither it nor anything it
+comes from can "safely" be destroyed. Unless of course the way of getting to
+such ciphers is via entirely distinct API calls that didn't exist before.
+However existing API usage cannot be made to understand when an EVP_CIPHER
+pointer, that has been passed to the caller, is no longer being used.
+
+The other problem with the existing API w.r.t. to hooking EVP_CIPHER support
+into ENGINE is storage - the OBJ_NAME-based storage used by EVP to register
+ciphers simultaneously registers cipher *types* and cipher *implementations* -
+they are effectively the same thing, an "EVP_CIPHER" pointer. The problem with
+hooking in ENGINEs is that multiple ENGINEs may implement the same ciphers. The
+solution is necessarily that ENGINE-provided ciphers simply are not registered,
+stored, or exposed to the caller in the same manner as existing ciphers. This is
+especially necessary considering the fact ENGINE uses reference counts to allow
+for cleanup, modularity, and DSO support - yet EVP_CIPHERs, as exposed to
+callers in the current API, support no such controls.
+
+Another sticking point for integrating cipher support into ENGINE is linkage.
+Already there is a problem with the way ENGINE supports RSA, DSA, etc whereby
+they are available *because* they're part of a giant ENGINE called "openssl".
+Ie. all implementations *have* to come from an ENGINE, but we get round that by
+having a giant ENGINE with all the software support encapsulated. This creates
+linker hassles if nothing else - linking a 1-line application that calls 2 basic
+RSA functions (eg. "RSA_free(RSA_new());") will result in large quantities of
+ENGINE code being linked in *and* because of that DSA, DH, and RAND also. If we
+continue with this approach for EVP_CIPHER support (even if it *was* possible)
+we would lose our ability to link selectively by selectively loading certain
+implementations of certain functionality. Touching any part of any kind of
+crypto would result in massive static linkage of everything else. So the
+solution is to change the way ENGINE feeds existing "classes", ie. how the
+hooking to ENGINE works from RSA, DSA, DH, RAND, as well as adding new hooking
+for EVP_CIPHER, and EVP_MD.
+
+The way this is now being done is by mostly reverting back to how things used to
+work prior to ENGINE :-). Ie. RSA now has a "RSA_METHOD" pointer again - this
+was previously replaced by an "ENGINE" pointer and all RSA code that required
+the RSA_METHOD would call ENGINE_get_RSA() each time on its ENGINE handle to
+temporarily get and use the ENGINE's RSA implementation. Apart from being more
+efficient, switching back to each RSA having an RSA_METHOD pointer also allows
+us to conceivably operate with *no* ENGINE. As we'll see, this removes any need
+for a fallback ENGINE that encapsulates default implementations - we can simply
+have our RSA structure pointing its RSA_METHOD pointer to the software
+implementation and have its ENGINE pointer set to NULL.
+
+A look at the EVP_CIPHER hooking is most explanatory, the RSA, DSA (etc) cases
+turn out to be degenerate forms of the same thing. The EVP storage of ciphers,
+and the existing EVP API functions that return "software" implementations and
+descriptions remain untouched. However, the storage takes more meaning in terms
+of "cipher description" and less meaning in terms of "implementation". When an
+EVP_CIPHER_CTX is actually initialised with an EVP_CIPHER method and is about to
+begin en/decryption, the hooking to ENGINE comes into play. What happens is that
+cipher-specific ENGINE code is asked for an ENGINE pointer (a functional
+reference) for any ENGINE that is registered to perform the algo/mode that the
+provided EVP_CIPHER structure represents. Under normal circumstances, that
+ENGINE code will return NULL because no ENGINEs will have had any cipher
+implementations *registered*. As such, a NULL ENGINE pointer is stored in the
+EVP_CIPHER_CTX context, and the EVP_CIPHER structure is left hooked into the
+context and so is used as the implementation. Pretty much how things work now
+except we'd have a redundant ENGINE pointer set to NULL and doing nothing.
+
+Conversely, if an ENGINE *has* been registered to perform the algorithm/mode
+combination represented by the provided EVP_CIPHER, then a functional reference
+to that ENGINE will be returned to the EVP_CIPHER_CTX during initialisation.
+That functional reference will be stored in the context (and released on
+cleanup) - and having that reference provides a *safe* way to use an EVP_CIPHER
+definition that is private to the ENGINE. Ie. the EVP_CIPHER provided by the
+application will actually be replaced by an EVP_CIPHER from the registered
+ENGINE - it will support the same algorithm/mode as the original but will be a
+completely different implementation. Because this EVP_CIPHER isn't stored in the
+EVP storage, nor is it returned to applications from traditional API functions,
+there is no associated problem with it not having reference counts. And of
+course, when one of these "private" cipher implementations is hooked into
+EVP_CIPHER_CTX, it is done whilst the EVP_CIPHER_CTX holds a functional
+reference to the ENGINE that owns it, thus the use of the ENGINE's EVP_CIPHER is
+safe.
+
+The "cipher-specific ENGINE code" I mentioned is implemented in tb_cipher.c but
+in essence it is simply an instantiation of "ENGINE_TABLE" code for use by
+EVP_CIPHER code. tb_digest.c is virtually identical but, of course, it is for
+use by EVP_MD code. Ditto for tb_rsa.c, tb_dsa.c, etc. These instantiations of
+ENGINE_TABLE essentially provide linker-separation of the classes so that even
+if ENGINEs implement *all* possible algorithms, an application using only
+EVP_CIPHER code will link at most code relating to EVP_CIPHER, tb_cipher.c, core
+ENGINE code that is independent of class, and of course the ENGINE
+implementation that the application loaded. It will *not* however link any
+class-specific ENGINE code for digests, RSA, etc nor will it bleed over into
+other APIs, such as the RSA/DSA/etc library code.
+
+ENGINE_TABLE is a little more complicated than may seem necessary but this is
+mostly to avoid a lot of "init()"-thrashing on ENGINEs (that may have to load
+DSOs, and other expensive setup that shouldn't be thrashed unnecessarily) *and*
+to duplicate "default" behaviour. Basically an ENGINE_TABLE instantiation, for
+example tb_cipher.c, implements a hash-table keyed by integer "nid" values.
+These nids provide the uniquenness of an algorithm/mode - and each nid will hash
+to a potentially NULL "ENGINE_PILE". An ENGINE_PILE is essentially a list of
+pointers to ENGINEs that implement that particular 'nid'. Each "pile" uses some
+caching tricks such that requests on that 'nid' will be cached and all future
+requests will return immediately (well, at least with minimal operation) unless
+a change is made to the pile, eg. perhaps an ENGINE was unloaded. The reason is
+that an application could have support for 10 ENGINEs statically linked
+in, and the machine in question may not have any of the hardware those 10
+ENGINEs support. If each of those ENGINEs has a "des_cbc" implementation, we
+want to avoid every EVP_CIPHER_CTX setup from trying (and failing) to initialise
+each of those 10 ENGINEs. Instead, the first such request will try to do that
+and will either return (and cache) a NULL ENGINE pointer or will return a
+functional reference to the first that successfully initialised. In the latter
+case it will also cache an extra functional reference to the ENGINE as a
+"default" for that 'nid'. The caching is acknowledged by a 'uptodate' variable
+that is unset only if un/registration takes place on that pile. Ie. if
+implementations of "des_cbc" are added or removed. This behaviour can be
+tweaked; the ENGINE_TABLE_FLAG_NOINIT value can be passed to
+ENGINE_set_table_flags(), in which case the only ENGINEs that tb_cipher.c will
+try to initialise from the "pile" will be those that are already initialised
+(ie. it's simply an increment of the functional reference count, and no real
+"initialisation" will take place).
+
+RSA, DSA, DH, and RAND all have their own ENGINE_TABLE code as well, and the
+difference is that they all use an implicit 'nid' of 1. Whereas EVP_CIPHERs are
+actually qualitatively different depending on 'nid' (the "des_cbc" EVP_CIPHER is
+not an interoperable implementation of "aes_256_cbc"), RSA_METHODs are
+necessarily interoperable and don't have different flavours, only different
+implementations. In other words, the ENGINE_TABLE for RSA will either be empty,
+or will have a single ENGING_PILE hashed to by the 'nid' 1 and that pile
+represents ENGINEs that implement the single "type" of RSA there is.
+
+Cleanup - the registration and unregistration may pose questions about how
+cleanup works with the ENGINE_PILE doing all this caching nonsense (ie. when the
+application or EVP_CIPHER code releases its last reference to an ENGINE, the
+ENGINE_PILE code may still have references and thus those ENGINEs will stay
+hooked in forever). The way this is handled is via "unregistration". With these
+new ENGINE changes, an abstract ENGINE can be loaded and initialised, but that
+is an algorithm-agnostic process. Even if initialised, it will not have
+registered any of its implementations (to do so would link all class "table"
+code despite the fact the application may use only ciphers, for example). This
+is deliberately a distinct step. Moreover, registration and unregistration has
+nothing to do with whether an ENGINE is *functional* or not (ie. you can even
+register an ENGINE and its implementations without it being operational, you may
+not even have the drivers to make it operate). What actually happens with
+respect to cleanup is managed inside eng_lib.c with the "engine_cleanup_***"
+functions. These functions are internal-only and each part of ENGINE code that
+could require cleanup will, upon performing its first allocation, register a
+callback with the "engine_cleanup" code. The other part of this that makes it
+tick is that the ENGINE_TABLE instantiations (tb_***.c) use NULL as their
+initialised state. So if RSA code asks for an ENGINE and no ENGINE has
+registered an implementation, the code will simply return NULL and the tb_rsa.c
+state will be unchanged. Thus, no cleanup is required unless registration takes
+place. ENGINE_cleanup() will simply iterate across a list of registered cleanup
+callbacks calling each in turn, and will then internally delete its own storage
+(a STACK). When a cleanup callback is next registered (eg. if the cleanup() is
+part of a gracefull restart and the application wants to cleanup all state then
+start again), the internal STACK storage will be freshly allocated. This is much
+the same as the situation in the ENGINE_TABLE instantiations ... NULL is the
+initialised state, so only modification operations (not queries) will cause that
+code to have to register a cleanup.
+
+What else? The bignum callbacks and associated ENGINE functions have been
+removed for two obvious reasons; (i) there was no way to generalise them to the
+mechanism now used by RSA/DSA/..., because there's no such thing as a BIGNUM
+method, and (ii) because of (i), there was no meaningful way for library or
+application code to automatically hook and use ENGINE supplied bignum functions
+anyway. Also, ENGINE_cpy() has been removed (although an internal-only version
+exists) - the idea of providing an ENGINE_cpy() function probably wasn't a good
+one and now certainly doesn't make sense in any generalised way. Some of the
+RSA, DSA, DH, and RAND functions that were fiddled during the original ENGINE
+changes have now, as a consequence, been reverted back. This is because the
+hooking of ENGINE is now automatic (and passive, it can interally use a NULL
+ENGINE pointer to simply ignore ENGINE from then on).
+
+Hell, that should be enough for now ... comments welcome: geoff@openssl.org
+
diff --git a/crypto/engine/build.info b/crypto/engine/build.info
new file mode 100644
index 000000000..161dad4d0
--- /dev/null
+++ b/crypto/engine/build.info
@@ -0,0 +1,8 @@
+LIBS=../../libcrypto
+SOURCE[../../libcrypto]=\
+        eng_err.c eng_lib.c eng_list.c eng_init.c eng_ctrl.c \
+        eng_table.c eng_pkey.c eng_fat.c eng_all.c \
+        tb_rsa.c tb_dsa.c tb_dh.c tb_rand.c \
+        tb_cipher.c tb_digest.c tb_pkmeth.c tb_asnmth.c tb_eckey.c \
+        eng_openssl.c eng_cnf.c eng_dyn.c eng_cryptodev.c \
+        eng_rdrand.c
diff --git a/crypto/engine/eng_all.c b/crypto/engine/eng_all.c
new file mode 100644
index 000000000..ebe027737
--- /dev/null
+++ b/crypto/engine/eng_all.c
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2001-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "internal/cryptlib.h"
+#include "eng_int.h"
+
+void ENGINE_load_builtin_engines(void)
+{
+    /* Some ENGINEs need this */
+    OPENSSL_cpuid_setup();
+
+    OPENSSL_init_crypto(OPENSSL_INIT_ENGINE_ALL_BUILTIN, NULL);
+}
+
+#if (defined(__OpenBSD__) || defined(__FreeBSD__) || defined(HAVE_CRYPTODEV)) && !defined(OPENSSL_NO_DEPRECATED)
+void ENGINE_setup_bsd_cryptodev(void)
+{
+    static int bsd_cryptodev_default_loaded = 0;
+    if (!bsd_cryptodev_default_loaded) {
+        OPENSSL_init_crypto(OPENSSL_INIT_ENGINE_CRYPTODEV, NULL);
+        ENGINE_register_all_complete();
+    }
+    bsd_cryptodev_default_loaded = 1;
+}
+#endif
diff --git a/crypto/engine/eng_cnf.c b/crypto/engine/eng_cnf.c
new file mode 100644
index 000000000..c17c7a45f
--- /dev/null
+++ b/crypto/engine/eng_cnf.c
@@ -0,0 +1,193 @@
+/*
+ * Copyright 2002-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "eng_int.h"
+#include 
+
+/* #define ENGINE_CONF_DEBUG */
+
+/* ENGINE config module */
+
+static const char *skip_dot(const char *name)
+{
+    const char *p = strchr(name, '.');
+
+    if (p != NULL)
+        return p + 1;
+    return name;
+}
+
+static STACK_OF(ENGINE) *initialized_engines = NULL;
+
+static int int_engine_init(ENGINE *e)
+{
+    if (!ENGINE_init(e))
+        return 0;
+    if (!initialized_engines)
+        initialized_engines = sk_ENGINE_new_null();
+    if (!initialized_engines || !sk_ENGINE_push(initialized_engines, e)) {
+        ENGINE_finish(e);
+        return 0;
+    }
+    return 1;
+}
+
+static int int_engine_configure(const char *name, const char *value, const CONF *cnf)
+{
+    int i;
+    int ret = 0;
+    long do_init = -1;
+    STACK_OF(CONF_VALUE) *ecmds;
+    CONF_VALUE *ecmd = NULL;
+    const char *ctrlname, *ctrlvalue;
+    ENGINE *e = NULL;
+    int soft = 0;
+
+    name = skip_dot(name);
+#ifdef ENGINE_CONF_DEBUG
+    fprintf(stderr, "Configuring engine %s\n", name);
+#endif
+    /* Value is a section containing ENGINE commands */
+    ecmds = NCONF_get_section(cnf, value);
+
+    if (!ecmds) {
+        ENGINEerr(ENGINE_F_INT_ENGINE_CONFIGURE,
+                  ENGINE_R_ENGINE_SECTION_ERROR);
+        return 0;
+    }
+
+    for (i = 0; i < sk_CONF_VALUE_num(ecmds); i++) {
+        ecmd = sk_CONF_VALUE_value(ecmds, i);
+        ctrlname = skip_dot(ecmd->name);
+        ctrlvalue = ecmd->value;
+#ifdef ENGINE_CONF_DEBUG
+        fprintf(stderr, "ENGINE conf: doing ctrl(%s,%s)\n", ctrlname,
+                ctrlvalue);
+#endif
+
+        /* First handle some special pseudo ctrls */
+
+        /* Override engine name to use */
+        if (strcmp(ctrlname, "engine_id") == 0)
+            name = ctrlvalue;
+        else if (strcmp(ctrlname, "soft_load") == 0)
+            soft = 1;
+        /* Load a dynamic ENGINE */
+        else if (strcmp(ctrlname, "dynamic_path") == 0) {
+            e = ENGINE_by_id("dynamic");
+            if (!e)
+                goto err;
+            if (!ENGINE_ctrl_cmd_string(e, "SO_PATH", ctrlvalue, 0))
+                goto err;
+            if (!ENGINE_ctrl_cmd_string(e, "LIST_ADD", "2", 0))
+                goto err;
+            if (!ENGINE_ctrl_cmd_string(e, "LOAD", NULL, 0))
+                goto err;
+        }
+        /* ... add other pseudos here ... */
+        else {
+            /*
+             * At this point we need an ENGINE structural reference if we
+             * don't already have one.
+             */
+            if (!e) {
+                e = ENGINE_by_id(name);
+                if (!e && soft) {
+                    ERR_clear_error();
+                    return 1;
+                }
+                if (!e)
+                    goto err;
+            }
+            /*
+             * Allow "EMPTY" to mean no value: this allows a valid "value" to
+             * be passed to ctrls of type NO_INPUT
+             */
+            if (strcmp(ctrlvalue, "EMPTY") == 0)
+                ctrlvalue = NULL;
+            if (strcmp(ctrlname, "init") == 0) {
+                if (!NCONF_get_number_e(cnf, value, "init", &do_init))
+                    goto err;
+                if (do_init == 1) {
+                    if (!int_engine_init(e))
+                        goto err;
+                } else if (do_init != 0) {
+                    ENGINEerr(ENGINE_F_INT_ENGINE_CONFIGURE,
+                              ENGINE_R_INVALID_INIT_VALUE);
+                    goto err;
+                }
+            } else if (strcmp(ctrlname, "default_algorithms") == 0) {
+                if (!ENGINE_set_default_string(e, ctrlvalue))
+                    goto err;
+            } else if (!ENGINE_ctrl_cmd_string(e, ctrlname, ctrlvalue, 0)) {
+                goto err;
+            }
+        }
+
+    }
+    if (e && (do_init == -1) && !int_engine_init(e)) {
+        ecmd = NULL;
+        goto err;
+    }
+    ret = 1;
+ err:
+    if (ret != 1) {
+        ENGINEerr(ENGINE_F_INT_ENGINE_CONFIGURE,
+                  ENGINE_R_ENGINE_CONFIGURATION_ERROR);
+        if (ecmd)
+            ERR_add_error_data(6, "section=", ecmd->section,
+                               ", name=", ecmd->name,
+                               ", value=", ecmd->value);
+    }
+    ENGINE_free(e);
+    return ret;
+}
+
+static int int_engine_module_init(CONF_IMODULE *md, const CONF *cnf)
+{
+    STACK_OF(CONF_VALUE) *elist;
+    CONF_VALUE *cval;
+    int i;
+#ifdef ENGINE_CONF_DEBUG
+    fprintf(stderr, "Called engine module: name %s, value %s\n",
+            CONF_imodule_get_name(md), CONF_imodule_get_value(md));
+#endif
+    /* Value is a section containing ENGINEs to configure */
+    elist = NCONF_get_section(cnf, CONF_imodule_get_value(md));
+
+    if (!elist) {
+        ENGINEerr(ENGINE_F_INT_ENGINE_MODULE_INIT,
+                  ENGINE_R_ENGINES_SECTION_ERROR);
+        return 0;
+    }
+
+    for (i = 0; i < sk_CONF_VALUE_num(elist); i++) {
+        cval = sk_CONF_VALUE_value(elist, i);
+        if (!int_engine_configure(cval->name, cval->value, cnf))
+            return 0;
+    }
+
+    return 1;
+}
+
+static void int_engine_module_finish(CONF_IMODULE *md)
+{
+    ENGINE *e;
+
+    while ((e = sk_ENGINE_pop(initialized_engines)))
+        ENGINE_finish(e);
+    sk_ENGINE_free(initialized_engines);
+    initialized_engines = NULL;
+}
+
+void ENGINE_add_conf_module(void)
+{
+    CONF_module_add("engines",
+                    int_engine_module_init, int_engine_module_finish);
+}
diff --git a/crypto/engine/eng_cryptodev.c b/crypto/engine/eng_cryptodev.c
new file mode 100644
index 000000000..d63c918c7
--- /dev/null
+++ b/crypto/engine/eng_cryptodev.c
@@ -0,0 +1,1754 @@
+/*
+ * Copyright 2002-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * Copyright (c) 2002 Bob Beck 
+ * Copyright (c) 2002 Theo de Raadt
+ * Copyright (c) 2002 Markus Friedl
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#if (defined(__unix__) || defined(unix)) && !defined(USG) && \
+        (defined(OpenBSD) || defined(__FreeBSD__))
+# include 
+# if (OpenBSD >= 200112) || ((__FreeBSD_version >= 470101 && __FreeBSD_version < 500000) || __FreeBSD_version >= 500041)
+#  define HAVE_CRYPTODEV
+# endif
+# if (OpenBSD >= 200110)
+#  define HAVE_SYSLOG_R
+# endif
+#endif
+
+#include 
+#ifdef HAVE_CRYPTODEV
+# include 
+# include 
+# include 
+# include 
+# include 
+# include 
+# include 
+# include 
+# include 
+# include 
+#endif
+#include 
+#include 
+#include 
+#include 
+
+#ifndef HAVE_CRYPTODEV
+
+void engine_load_cryptodev_int(void)
+{
+    /* This is a NOP on platforms without /dev/crypto */
+    return;
+}
+
+#else
+
+struct dev_crypto_state {
+    struct session_op d_sess;
+    int d_fd;
+# ifdef USE_CRYPTODEV_DIGESTS
+    char dummy_mac_key[HASH_MAX_LEN];
+    unsigned char digest_res[HASH_MAX_LEN];
+    char *mac_data;
+    int mac_len;
+# endif
+};
+
+static u_int32_t cryptodev_asymfeat = 0;
+
+static RSA_METHOD *cryptodev_rsa;
+#ifndef OPENSSL_NO_DSA
+static DSA_METHOD *cryptodev_dsa = NULL;
+#endif
+#ifndef OPENSSL_NO_DH
+static DH_METHOD *cryptodev_dh;
+#endif
+
+static int get_asym_dev_crypto(void);
+static int open_dev_crypto(void);
+static int get_dev_crypto(void);
+static int get_cryptodev_ciphers(const int **cnids);
+# ifdef USE_CRYPTODEV_DIGESTS
+static int get_cryptodev_digests(const int **cnids);
+# endif
+static int cryptodev_usable_ciphers(const int **nids);
+static int cryptodev_usable_digests(const int **nids);
+static int cryptodev_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
+                            const unsigned char *in, size_t inl);
+static int cryptodev_init_key(EVP_CIPHER_CTX *ctx, const unsigned char *key,
+                              const unsigned char *iv, int enc);
+static int cryptodev_cleanup(EVP_CIPHER_CTX *ctx);
+static int cryptodev_engine_ciphers(ENGINE *e, const EVP_CIPHER **cipher,
+                                    const int **nids, int nid);
+static int cryptodev_engine_digests(ENGINE *e, const EVP_MD **digest,
+                                    const int **nids, int nid);
+static int bn2crparam(const BIGNUM *a, struct crparam *crp);
+static int crparam2bn(struct crparam *crp, BIGNUM *a);
+static void zapparams(struct crypt_kop *kop);
+static int cryptodev_asym(struct crypt_kop *kop, int rlen, BIGNUM *r,
+                          int slen, BIGNUM *s);
+
+static int cryptodev_bn_mod_exp(BIGNUM *r, const BIGNUM *a,
+                                const BIGNUM *p, const BIGNUM *m, BN_CTX *ctx,
+                                BN_MONT_CTX *m_ctx);
+static int cryptodev_rsa_nocrt_mod_exp(BIGNUM *r0, const BIGNUM *I, RSA *rsa,
+                                       BN_CTX *ctx);
+static int cryptodev_rsa_mod_exp(BIGNUM *r0, const BIGNUM *I, RSA *rsa,
+                                 BN_CTX *ctx);
+#ifndef OPENSSL_NO_DSA
+static int cryptodev_dsa_bn_mod_exp(DSA *dsa, BIGNUM *r, const BIGNUM *a,
+                                    const BIGNUM *p, const BIGNUM *m,
+                                    BN_CTX *ctx, BN_MONT_CTX *m_ctx);
+static int cryptodev_dsa_dsa_mod_exp(DSA *dsa, BIGNUM *t1, const BIGNUM *g,
+                                     const BIGNUM *u1, const BIGNUM *pub_key,
+                                     const BIGNUM *u2, const BIGNUM *p,
+                                     BN_CTX *ctx, BN_MONT_CTX *mont);
+static DSA_SIG *cryptodev_dsa_do_sign(const unsigned char *dgst, int dlen,
+                                      DSA *dsa);
+static int cryptodev_dsa_verify(const unsigned char *dgst, int dgst_len,
+                                DSA_SIG *sig, DSA *dsa);
+#endif
+#ifndef OPENSSL_NO_DH
+static int cryptodev_mod_exp_dh(const DH *dh, BIGNUM *r, const BIGNUM *a,
+                                const BIGNUM *p, const BIGNUM *m, BN_CTX *ctx,
+                                BN_MONT_CTX *m_ctx);
+static int cryptodev_dh_compute_key(unsigned char *key, const BIGNUM *pub_key,
+                                    DH *dh);
+#endif
+static int cryptodev_ctrl(ENGINE *e, int cmd, long i, void *p,
+                          void (*f) (void));
+void engine_load_cryptodev_int(void);
+
+static const ENGINE_CMD_DEFN cryptodev_defns[] = {
+    {0, NULL, NULL, 0}
+};
+
+static struct {
+    int id;
+    int nid;
+    int ivmax;
+    int keylen;
+} ciphers[] = {
+    {
+        CRYPTO_ARC4, NID_rc4, 0, 16,
+    },
+    {
+        CRYPTO_DES_CBC, NID_des_cbc, 8, 8,
+    },
+    {
+        CRYPTO_3DES_CBC, NID_des_ede3_cbc, 8, 24,
+    },
+    {
+        CRYPTO_AES_CBC, NID_aes_128_cbc, 16, 16,
+    },
+    {
+        CRYPTO_AES_CBC, NID_aes_192_cbc, 16, 24,
+    },
+    {
+        CRYPTO_AES_CBC, NID_aes_256_cbc, 16, 32,
+    },
+# ifdef CRYPTO_AES_CTR
+    {
+        CRYPTO_AES_CTR, NID_aes_128_ctr, 14, 16,
+    },
+    {
+        CRYPTO_AES_CTR, NID_aes_192_ctr, 14, 24,
+    },
+    {
+        CRYPTO_AES_CTR, NID_aes_256_ctr, 14, 32,
+    },
+# endif
+    {
+        CRYPTO_BLF_CBC, NID_bf_cbc, 8, 16,
+    },
+    {
+        CRYPTO_CAST_CBC, NID_cast5_cbc, 8, 16,
+    },
+    {
+        CRYPTO_SKIPJACK_CBC, NID_undef, 0, 0,
+    },
+    {
+        0, NID_undef, 0, 0,
+    },
+};
+
+# ifdef USE_CRYPTODEV_DIGESTS
+static struct {
+    int id;
+    int nid;
+    int keylen;
+} digests[] = {
+    {
+        CRYPTO_MD5_HMAC, NID_hmacWithMD5, 16
+    },
+    {
+        CRYPTO_SHA1_HMAC, NID_hmacWithSHA1, 20
+    },
+    {
+        CRYPTO_RIPEMD160_HMAC, NID_ripemd160, 16
+        /* ? */
+    },
+    {
+        CRYPTO_MD5_KPDK, NID_undef, 0
+    },
+    {
+        CRYPTO_SHA1_KPDK, NID_undef, 0
+    },
+    {
+        CRYPTO_MD5, NID_md5, 16
+    },
+    {
+        CRYPTO_SHA1, NID_sha1, 20
+    },
+    {
+        0, NID_undef, 0
+    },
+};
+# endif
+
+/*
+ * Return a fd if /dev/crypto seems usable, 0 otherwise.
+ */
+static int open_dev_crypto(void)
+{
+    static int fd = -1;
+
+    if (fd == -1) {
+        if ((fd = open("/dev/crypto", O_RDWR, 0)) == -1)
+            return (-1);
+        /* close on exec */
+        if (fcntl(fd, F_SETFD, 1) == -1) {
+            close(fd);
+            fd = -1;
+            return (-1);
+        }
+    }
+    return (fd);
+}
+
+static int get_dev_crypto(void)
+{
+    int fd, retfd;
+
+    if ((fd = open_dev_crypto()) == -1)
+        return (-1);
+# ifndef CRIOGET_NOT_NEEDED
+    if (ioctl(fd, CRIOGET, &retfd) == -1)
+        return (-1);
+
+    /* close on exec */
+    if (fcntl(retfd, F_SETFD, 1) == -1) {
+        close(retfd);
+        return (-1);
+    }
+# else
+    retfd = fd;
+# endif
+    return (retfd);
+}
+
+static void put_dev_crypto(int fd)
+{
+# ifndef CRIOGET_NOT_NEEDED
+    close(fd);
+# endif
+}
+
+/* Caching version for asym operations */
+static int get_asym_dev_crypto(void)
+{
+    static int fd = -1;
+
+    if (fd == -1)
+        fd = get_dev_crypto();
+    return fd;
+}
+
+/*
+ * Find out what ciphers /dev/crypto will let us have a session for.
+ * XXX note, that some of these openssl doesn't deal with yet!
+ * returning them here is harmless, as long as we return NULL
+ * when asked for a handler in the cryptodev_engine_ciphers routine
+ */
+static int get_cryptodev_ciphers(const int **cnids)
+{
+    static int nids[CRYPTO_ALGORITHM_MAX];
+    struct session_op sess;
+    int fd, i, count = 0;
+
+    if ((fd = get_dev_crypto()) < 0) {
+        *cnids = NULL;
+        return (0);
+    }
+    memset(&sess, 0, sizeof(sess));
+    sess.key = (caddr_t) "123456789abcdefghijklmno";
+
+    for (i = 0; ciphers[i].id && count < CRYPTO_ALGORITHM_MAX; i++) {
+        if (ciphers[i].nid == NID_undef)
+            continue;
+        sess.cipher = ciphers[i].id;
+        sess.keylen = ciphers[i].keylen;
+        sess.mac = 0;
+        if (ioctl(fd, CIOCGSESSION, &sess) != -1 &&
+            ioctl(fd, CIOCFSESSION, &sess.ses) != -1)
+            nids[count++] = ciphers[i].nid;
+    }
+    put_dev_crypto(fd);
+
+    if (count > 0)
+        *cnids = nids;
+    else
+        *cnids = NULL;
+    return (count);
+}
+
+# ifdef USE_CRYPTODEV_DIGESTS
+/*
+ * Find out what digests /dev/crypto will let us have a session for.
+ * XXX note, that some of these openssl doesn't deal with yet!
+ * returning them here is harmless, as long as we return NULL
+ * when asked for a handler in the cryptodev_engine_digests routine
+ */
+static int get_cryptodev_digests(const int **cnids)
+{
+    static int nids[CRYPTO_ALGORITHM_MAX];
+    struct session_op sess;
+    int fd, i, count = 0;
+
+    if ((fd = get_dev_crypto()) < 0) {
+        *cnids = NULL;
+        return (0);
+    }
+    memset(&sess, 0, sizeof(sess));
+    sess.mackey = (caddr_t) "123456789abcdefghijklmno";
+    for (i = 0; digests[i].id && count < CRYPTO_ALGORITHM_MAX; i++) {
+        if (digests[i].nid == NID_undef)
+            continue;
+        sess.mac = digests[i].id;
+        sess.mackeylen = digests[i].keylen;
+        sess.cipher = 0;
+        if (ioctl(fd, CIOCGSESSION, &sess) != -1 &&
+            ioctl(fd, CIOCFSESSION, &sess.ses) != -1)
+            nids[count++] = digests[i].nid;
+    }
+    put_dev_crypto(fd);
+
+    if (count > 0)
+        *cnids = nids;
+    else
+        *cnids = NULL;
+    return (count);
+}
+# endif                         /* 0 */
+
+/*
+ * Find the useable ciphers|digests from dev/crypto - this is the first
+ * thing called by the engine init crud which determines what it
+ * can use for ciphers from this engine. We want to return
+ * only what we can do, anything else is handled by software.
+ *
+ * If we can't initialize the device to do anything useful for
+ * any reason, we want to return a NULL array, and 0 length,
+ * which forces everything to be done is software. By putting
+ * the initialization of the device in here, we ensure we can
+ * use this engine as the default, and if for whatever reason
+ * /dev/crypto won't do what we want it will just be done in
+ * software
+ *
+ * This can (should) be greatly expanded to perhaps take into
+ * account speed of the device, and what we want to do.
+ * (although the disabling of particular alg's could be controlled
+ * by the device driver with sysctl's.) - this is where we
+ * want most of the decisions made about what we actually want
+ * to use from /dev/crypto.
+ */
+static int cryptodev_usable_ciphers(const int **nids)
+{
+    return (get_cryptodev_ciphers(nids));
+}
+
+static int cryptodev_usable_digests(const int **nids)
+{
+# ifdef USE_CRYPTODEV_DIGESTS
+    return (get_cryptodev_digests(nids));
+# else
+    /*
+     * XXXX just disable all digests for now, because it sucks.
+     * we need a better way to decide this - i.e. I may not
+     * want digests on slow cards like hifn on fast machines,
+     * but might want them on slow or loaded machines, etc.
+     * will also want them when using crypto cards that don't
+     * suck moose gonads - would be nice to be able to decide something
+     * as reasonable default without having hackery that's card dependent.
+     * of course, the default should probably be just do everything,
+     * with perhaps a sysctl to turn algorithms off (or have them off
+     * by default) on cards that generally suck like the hifn.
+     */
+    *nids = NULL;
+    return (0);
+# endif
+}
+
+static int
+cryptodev_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
+                 const unsigned char *in, size_t inl)
+{
+    struct crypt_op cryp;
+    struct dev_crypto_state *state = EVP_CIPHER_CTX_get_cipher_data(ctx);
+    struct session_op *sess = &state->d_sess;
+    const void *iiv;
+    unsigned char save_iv[EVP_MAX_IV_LENGTH];
+
+    if (state->d_fd < 0)
+        return (0);
+    if (!inl)
+        return (1);
+    if ((inl % EVP_CIPHER_CTX_block_size(ctx)) != 0)
+        return (0);
+
+    memset(&cryp, 0, sizeof(cryp));
+
+    cryp.ses = sess->ses;
+    cryp.flags = 0;
+    cryp.len = inl;
+    cryp.src = (caddr_t) in;
+    cryp.dst = (caddr_t) out;
+    cryp.mac = 0;
+
+    cryp.op = EVP_CIPHER_CTX_encrypting(ctx) ? COP_ENCRYPT : COP_DECRYPT;
+
+    if (EVP_CIPHER_CTX_iv_length(ctx) > 0) {
+        cryp.iv = (caddr_t) EVP_CIPHER_CTX_iv(ctx);
+        if (!EVP_CIPHER_CTX_encrypting(ctx)) {
+            iiv = in + inl - EVP_CIPHER_CTX_iv_length(ctx);
+            memcpy(save_iv, iiv, EVP_CIPHER_CTX_iv_length(ctx));
+        }
+    } else
+        cryp.iv = NULL;
+
+    if (ioctl(state->d_fd, CIOCCRYPT, &cryp) == -1) {
+        /*
+         * XXX need better error handling this can fail for a number of
+         * different reasons.
+         */
+        return (0);
+    }
+
+    if (EVP_CIPHER_CTX_iv_length(ctx) > 0) {
+        if (EVP_CIPHER_CTX_encrypting(ctx))
+            iiv = out + inl - EVP_CIPHER_CTX_iv_length(ctx);
+        else
+            iiv = save_iv;
+        memcpy(EVP_CIPHER_CTX_iv_noconst(ctx), iiv,
+               EVP_CIPHER_CTX_iv_length(ctx));
+    }
+    return (1);
+}
+
+static int
+cryptodev_init_key(EVP_CIPHER_CTX *ctx, const unsigned char *key,
+                   const unsigned char *iv, int enc)
+{
+    struct dev_crypto_state *state = EVP_CIPHER_CTX_get_cipher_data(ctx);
+    struct session_op *sess = &state->d_sess;
+    int cipher = -1, i;
+
+    for (i = 0; ciphers[i].id; i++)
+        if (EVP_CIPHER_CTX_nid(ctx) == ciphers[i].nid &&
+            EVP_CIPHER_CTX_iv_length(ctx) <= ciphers[i].ivmax &&
+            EVP_CIPHER_CTX_key_length(ctx) == ciphers[i].keylen) {
+            cipher = ciphers[i].id;
+            break;
+        }
+
+    if (!ciphers[i].id) {
+        state->d_fd = -1;
+        return (0);
+    }
+
+    memset(sess, 0, sizeof(*sess));
+
+    if ((state->d_fd = get_dev_crypto()) < 0)
+        return (0);
+
+    sess->key = (caddr_t) key;
+    sess->keylen = EVP_CIPHER_CTX_key_length(ctx);
+    sess->cipher = cipher;
+
+    if (ioctl(state->d_fd, CIOCGSESSION, sess) == -1) {
+        put_dev_crypto(state->d_fd);
+        state->d_fd = -1;
+        return (0);
+    }
+    return (1);
+}
+
+/*
+ * free anything we allocated earlier when initing a
+ * session, and close the session.
+ */
+static int cryptodev_cleanup(EVP_CIPHER_CTX *ctx)
+{
+    int ret = 0;
+    struct dev_crypto_state *state = EVP_CIPHER_CTX_get_cipher_data(ctx);
+    struct session_op *sess = &state->d_sess;
+
+    if (state->d_fd < 0)
+        return (0);
+
+    /*
+     * XXX if this ioctl fails, something's wrong. the invoker may have called
+     * us with a bogus ctx, or we could have a device that for whatever
+     * reason just doesn't want to play ball - it's not clear what's right
+     * here - should this be an error? should it just increase a counter,
+     * hmm. For right now, we return 0 - I don't believe that to be "right".
+     * we could call the gorpy openssl lib error handlers that print messages
+     * to users of the library. hmm..
+     */
+
+    if (ioctl(state->d_fd, CIOCFSESSION, &sess->ses) == -1) {
+        ret = 0;
+    } else {
+        ret = 1;
+    }
+    put_dev_crypto(state->d_fd);
+    state->d_fd = -1;
+
+    return (ret);
+}
+
+/*
+ * libcrypto EVP stuff - this is how we get wired to EVP so the engine
+ * gets called when libcrypto requests a cipher NID.
+ */
+
+/* RC4 */
+static EVP_CIPHER *rc4_cipher = NULL;
+static const EVP_CIPHER *cryptodev_rc4(void)
+{
+    if (rc4_cipher == NULL) {
+        EVP_CIPHER *cipher;
+
+        if ((cipher = EVP_CIPHER_meth_new(NID_rc4, 1, 16)) == NULL
+            || !EVP_CIPHER_meth_set_iv_length(cipher, 0)
+            || !EVP_CIPHER_meth_set_flags(cipher, EVP_CIPH_VARIABLE_LENGTH)
+            || !EVP_CIPHER_meth_set_init(cipher, cryptodev_init_key)
+            || !EVP_CIPHER_meth_set_do_cipher(cipher, cryptodev_cipher)
+            || !EVP_CIPHER_meth_set_cleanup(cipher, cryptodev_cleanup)
+            || !EVP_CIPHER_meth_set_impl_ctx_size(cipher, sizeof(struct dev_crypto_state))) {
+            EVP_CIPHER_meth_free(cipher);
+            cipher = NULL;
+        }
+        rc4_cipher = cipher;
+    }
+    return rc4_cipher;
+}
+
+/* DES CBC EVP */
+static EVP_CIPHER *des_cbc_cipher = NULL;
+static const EVP_CIPHER *cryptodev_des_cbc(void)
+{
+    if (des_cbc_cipher == NULL) {
+        EVP_CIPHER *cipher;
+
+        if ((cipher = EVP_CIPHER_meth_new(NID_des_cbc, 8, 8)) == NULL
+            || !EVP_CIPHER_meth_set_iv_length(cipher, 8)
+            || !EVP_CIPHER_meth_set_flags(cipher, EVP_CIPH_CBC_MODE)
+            || !EVP_CIPHER_meth_set_init(cipher, cryptodev_init_key)
+            || !EVP_CIPHER_meth_set_do_cipher(cipher, cryptodev_cipher)
+            || !EVP_CIPHER_meth_set_cleanup(cipher, cryptodev_cleanup)
+            || !EVP_CIPHER_meth_set_impl_ctx_size(cipher, sizeof(struct dev_crypto_state))
+            || !EVP_CIPHER_meth_set_set_asn1_params(cipher, EVP_CIPHER_set_asn1_iv)
+            || !EVP_CIPHER_meth_set_get_asn1_params(cipher, EVP_CIPHER_get_asn1_iv)) {
+            EVP_CIPHER_meth_free(cipher);
+            cipher = NULL;
+        }
+        des_cbc_cipher = cipher;
+    }
+    return des_cbc_cipher;
+}
+
+/* 3DES CBC EVP */
+static EVP_CIPHER *des3_cbc_cipher = NULL;
+static const EVP_CIPHER *cryptodev_3des_cbc(void)
+{
+    if (des3_cbc_cipher == NULL) {
+        EVP_CIPHER *cipher;
+
+        if ((cipher = EVP_CIPHER_meth_new(NID_des_ede3_cbc, 8, 24)) == NULL
+            || !EVP_CIPHER_meth_set_iv_length(cipher, 8)
+            || !EVP_CIPHER_meth_set_flags(cipher, EVP_CIPH_CBC_MODE)
+            || !EVP_CIPHER_meth_set_init(cipher, cryptodev_init_key)
+            || !EVP_CIPHER_meth_set_do_cipher(cipher, cryptodev_cipher)
+            || !EVP_CIPHER_meth_set_cleanup(cipher, cryptodev_cleanup)
+            || !EVP_CIPHER_meth_set_impl_ctx_size(cipher, sizeof(struct dev_crypto_state))
+            || !EVP_CIPHER_meth_set_set_asn1_params(cipher, EVP_CIPHER_set_asn1_iv)
+            || !EVP_CIPHER_meth_set_get_asn1_params(cipher, EVP_CIPHER_get_asn1_iv)) {
+            EVP_CIPHER_meth_free(cipher);
+            cipher = NULL;
+        }
+        des3_cbc_cipher = cipher;
+    }
+    return des3_cbc_cipher;
+}
+
+static EVP_CIPHER *bf_cbc_cipher = NULL;
+static const EVP_CIPHER *cryptodev_bf_cbc(void)
+{
+    if (bf_cbc_cipher == NULL) {
+        EVP_CIPHER *cipher;
+
+        if ((cipher = EVP_CIPHER_meth_new(NID_bf_cbc, 8, 16)) == NULL
+            || !EVP_CIPHER_meth_set_iv_length(cipher, 8)
+            || !EVP_CIPHER_meth_set_flags(cipher, EVP_CIPH_CBC_MODE)
+            || !EVP_CIPHER_meth_set_init(cipher, cryptodev_init_key)
+            || !EVP_CIPHER_meth_set_do_cipher(cipher, cryptodev_cipher)
+            || !EVP_CIPHER_meth_set_cleanup(cipher, cryptodev_cleanup)
+            || !EVP_CIPHER_meth_set_impl_ctx_size(cipher, sizeof(struct dev_crypto_state))
+            || !EVP_CIPHER_meth_set_set_asn1_params(cipher, EVP_CIPHER_set_asn1_iv)
+            || !EVP_CIPHER_meth_set_get_asn1_params(cipher, EVP_CIPHER_get_asn1_iv)) {
+            EVP_CIPHER_meth_free(cipher);
+            cipher = NULL;
+        }
+        bf_cbc_cipher = cipher;
+    }
+    return bf_cbc_cipher;
+}
+
+static EVP_CIPHER *cast_cbc_cipher = NULL;
+static const EVP_CIPHER *cryptodev_cast_cbc(void)
+{
+    if (cast_cbc_cipher == NULL) {
+        EVP_CIPHER *cipher;
+
+        if ((cipher = EVP_CIPHER_meth_new(NID_cast5_cbc, 8, 16)) == NULL
+            || !EVP_CIPHER_meth_set_iv_length(cipher, 8)
+            || !EVP_CIPHER_meth_set_flags(cipher, EVP_CIPH_CBC_MODE)
+            || !EVP_CIPHER_meth_set_init(cipher, cryptodev_init_key)
+            || !EVP_CIPHER_meth_set_do_cipher(cipher, cryptodev_cipher)
+            || !EVP_CIPHER_meth_set_cleanup(cipher, cryptodev_cleanup)
+            || !EVP_CIPHER_meth_set_impl_ctx_size(cipher, sizeof(struct dev_crypto_state))
+            || !EVP_CIPHER_meth_set_set_asn1_params(cipher, EVP_CIPHER_set_asn1_iv)
+            || !EVP_CIPHER_meth_set_get_asn1_params(cipher, EVP_CIPHER_get_asn1_iv)) {
+            EVP_CIPHER_meth_free(cipher);
+            cipher = NULL;
+        }
+        cast_cbc_cipher = cipher;
+    }
+    return cast_cbc_cipher;
+}
+
+static EVP_CIPHER *aes_cbc_cipher = NULL;
+static const EVP_CIPHER *cryptodev_aes_cbc(void)
+{
+    if (aes_cbc_cipher == NULL) {
+        EVP_CIPHER *cipher;
+
+        if ((cipher = EVP_CIPHER_meth_new(NID_aes_128_cbc, 16, 16)) == NULL
+            || !EVP_CIPHER_meth_set_iv_length(cipher, 16)
+            || !EVP_CIPHER_meth_set_flags(cipher, EVP_CIPH_CBC_MODE)
+            || !EVP_CIPHER_meth_set_init(cipher, cryptodev_init_key)
+            || !EVP_CIPHER_meth_set_do_cipher(cipher, cryptodev_cipher)
+            || !EVP_CIPHER_meth_set_cleanup(cipher, cryptodev_cleanup)
+            || !EVP_CIPHER_meth_set_impl_ctx_size(cipher, sizeof(struct dev_crypto_state))
+            || !EVP_CIPHER_meth_set_set_asn1_params(cipher, EVP_CIPHER_set_asn1_iv)
+            || !EVP_CIPHER_meth_set_get_asn1_params(cipher, EVP_CIPHER_get_asn1_iv)) {
+            EVP_CIPHER_meth_free(cipher);
+            cipher = NULL;
+        }
+        aes_cbc_cipher = cipher;
+    }
+    return aes_cbc_cipher;
+}
+
+static EVP_CIPHER *aes_192_cbc_cipher = NULL;
+static const EVP_CIPHER *cryptodev_aes_192_cbc(void)
+{
+    if (aes_192_cbc_cipher == NULL) {
+        EVP_CIPHER *cipher;
+
+        if ((cipher = EVP_CIPHER_meth_new(NID_aes_192_cbc, 16, 24)) == NULL
+            || !EVP_CIPHER_meth_set_iv_length(cipher, 16)
+            || !EVP_CIPHER_meth_set_flags(cipher, EVP_CIPH_CBC_MODE)
+            || !EVP_CIPHER_meth_set_init(cipher, cryptodev_init_key)
+            || !EVP_CIPHER_meth_set_do_cipher(cipher, cryptodev_cipher)
+            || !EVP_CIPHER_meth_set_cleanup(cipher, cryptodev_cleanup)
+            || !EVP_CIPHER_meth_set_impl_ctx_size(cipher, sizeof(struct dev_crypto_state))
+            || !EVP_CIPHER_meth_set_set_asn1_params(cipher, EVP_CIPHER_set_asn1_iv)
+            || !EVP_CIPHER_meth_set_get_asn1_params(cipher, EVP_CIPHER_get_asn1_iv)) {
+            EVP_CIPHER_meth_free(cipher);
+            cipher = NULL;
+        }
+        aes_192_cbc_cipher = cipher;
+    }
+    return aes_192_cbc_cipher;
+}
+
+static EVP_CIPHER *aes_256_cbc_cipher = NULL;
+static const EVP_CIPHER *cryptodev_aes_256_cbc(void)
+{
+    if (aes_256_cbc_cipher == NULL) {
+        EVP_CIPHER *cipher;
+
+        if ((cipher = EVP_CIPHER_meth_new(NID_aes_256_cbc, 16, 32)) == NULL
+            || !EVP_CIPHER_meth_set_iv_length(cipher, 16)
+            || !EVP_CIPHER_meth_set_flags(cipher, EVP_CIPH_CBC_MODE)
+            || !EVP_CIPHER_meth_set_init(cipher, cryptodev_init_key)
+            || !EVP_CIPHER_meth_set_do_cipher(cipher, cryptodev_cipher)
+            || !EVP_CIPHER_meth_set_cleanup(cipher, cryptodev_cleanup)
+            || !EVP_CIPHER_meth_set_impl_ctx_size(cipher, sizeof(struct dev_crypto_state))
+            || !EVP_CIPHER_meth_set_set_asn1_params(cipher, EVP_CIPHER_set_asn1_iv)
+            || !EVP_CIPHER_meth_set_get_asn1_params(cipher, EVP_CIPHER_get_asn1_iv)) {
+            EVP_CIPHER_meth_free(cipher);
+            cipher = NULL;
+        }
+        aes_256_cbc_cipher = cipher;
+    }
+    return aes_256_cbc_cipher;
+}
+
+# ifdef CRYPTO_AES_CTR
+static EVP_CIPHER *aes_ctr_cipher = NULL;
+static const EVP_CIPHER *cryptodev_aes_ctr(void)
+{
+    if (aes_ctr_cipher == NULL) {
+        EVP_CIPHER *cipher;
+
+        if ((cipher = EVP_CIPHER_meth_new(NID_aes_128_ctr, 16, 16)) == NULL
+            || !EVP_CIPHER_meth_set_iv_length(cipher, 14)
+            || !EVP_CIPHER_meth_set_flags(cipher, EVP_CIPH_CTR_MODE)
+            || !EVP_CIPHER_meth_set_init(cipher, cryptodev_init_key)
+            || !EVP_CIPHER_meth_set_do_cipher(cipher, cryptodev_cipher)
+            || !EVP_CIPHER_meth_set_cleanup(cipher, cryptodev_cleanup)
+            || !EVP_CIPHER_meth_set_impl_ctx_size(cipher, sizeof(struct dev_crypto_state))
+            || !EVP_CIPHER_meth_set_set_asn1_params(cipher, EVP_CIPHER_set_asn1_iv)
+            || !EVP_CIPHER_meth_set_get_asn1_params(cipher, EVP_CIPHER_get_asn1_iv)) {
+            EVP_CIPHER_meth_free(cipher);
+            cipher = NULL;
+        }
+        aes_ctr_cipher = cipher;
+    }
+    return aes_ctr_cipher;
+}
+
+static EVP_CIPHER *aes_192_ctr_cipher = NULL;
+static const EVP_CIPHER *cryptodev_aes_192_ctr(void)
+{
+    if (aes_192_ctr_cipher == NULL) {
+        EVP_CIPHER *cipher;
+
+        if ((cipher = EVP_CIPHER_meth_new(NID_aes_192_ctr, 16, 24)) == NULL
+            || !EVP_CIPHER_meth_set_iv_length(cipher, 14)
+            || !EVP_CIPHER_meth_set_flags(cipher, EVP_CIPH_CTR_MODE)
+            || !EVP_CIPHER_meth_set_init(cipher, cryptodev_init_key)
+            || !EVP_CIPHER_meth_set_do_cipher(cipher, cryptodev_cipher)
+            || !EVP_CIPHER_meth_set_cleanup(cipher, cryptodev_cleanup)
+            || !EVP_CIPHER_meth_set_impl_ctx_size(cipher, sizeof(struct dev_crypto_state))
+            || !EVP_CIPHER_meth_set_set_asn1_params(cipher, EVP_CIPHER_set_asn1_iv)
+            || !EVP_CIPHER_meth_set_get_asn1_params(cipher, EVP_CIPHER_get_asn1_iv)) {
+            EVP_CIPHER_meth_free(cipher);
+            cipher = NULL;
+        }
+        aes_192_ctr_cipher = cipher;
+    }
+    return aes_192_ctr_cipher;
+}
+
+static EVP_CIPHER *aes_256_ctr_cipher = NULL;
+static const EVP_CIPHER *cryptodev_aes_256_ctr(void)
+{
+    if (aes_256_ctr_cipher == NULL) {
+        EVP_CIPHER *cipher;
+
+        if ((cipher = EVP_CIPHER_meth_new(NID_aes_256_ctr, 16, 32)) == NULL
+            || !EVP_CIPHER_meth_set_iv_length(cipher, 14)
+            || !EVP_CIPHER_meth_set_flags(cipher, EVP_CIPH_CTR_MODE)
+            || !EVP_CIPHER_meth_set_init(cipher, cryptodev_init_key)
+            || !EVP_CIPHER_meth_set_do_cipher(cipher, cryptodev_cipher)
+            || !EVP_CIPHER_meth_set_cleanup(cipher, cryptodev_cleanup)
+            || !EVP_CIPHER_meth_set_impl_ctx_size(cipher, sizeof(struct dev_crypto_state))
+            || !EVP_CIPHER_meth_set_set_asn1_params(cipher, EVP_CIPHER_set_asn1_iv)
+            || !EVP_CIPHER_meth_set_get_asn1_params(cipher, EVP_CIPHER_get_asn1_iv)) {
+            EVP_CIPHER_meth_free(cipher);
+            cipher = NULL;
+        }
+        aes_256_ctr_cipher = cipher;
+    }
+    return aes_256_ctr_cipher;
+}
+# endif
+/*
+ * Registered by the ENGINE when used to find out how to deal with
+ * a particular NID in the ENGINE. this says what we'll do at the
+ * top level - note, that list is restricted by what we answer with
+ */
+static int
+cryptodev_engine_ciphers(ENGINE *e, const EVP_CIPHER **cipher,
+                         const int **nids, int nid)
+{
+    if (!cipher)
+        return (cryptodev_usable_ciphers(nids));
+
+    switch (nid) {
+    case NID_rc4:
+        *cipher = cryptodev_rc4();
+        break;
+    case NID_des_ede3_cbc:
+        *cipher = cryptodev_3des_cbc();
+        break;
+    case NID_des_cbc:
+        *cipher = cryptodev_des_cbc();
+        break;
+    case NID_bf_cbc:
+        *cipher = cryptodev_bf_cbc();
+        break;
+    case NID_cast5_cbc:
+        *cipher = cryptodev_cast_cbc();
+        break;
+    case NID_aes_128_cbc:
+        *cipher = cryptodev_aes_cbc();
+        break;
+    case NID_aes_192_cbc:
+        *cipher = cryptodev_aes_192_cbc();
+        break;
+    case NID_aes_256_cbc:
+        *cipher = cryptodev_aes_256_cbc();
+        break;
+# ifdef CRYPTO_AES_CTR
+    case NID_aes_128_ctr:
+        *cipher = cryptodev_aes_ctr();
+        break;
+    case NID_aes_192_ctr:
+        *cipher = cryptodev_aes_192_ctr();
+        break;
+    case NID_aes_256_ctr:
+        *cipher = cryptodev_aes_256_ctr();
+        break;
+# endif
+    default:
+        *cipher = NULL;
+        break;
+    }
+    return (*cipher != NULL);
+}
+
+# ifdef USE_CRYPTODEV_DIGESTS
+
+/* convert digest type to cryptodev */
+static int digest_nid_to_cryptodev(int nid)
+{
+    int i;
+
+    for (i = 0; digests[i].id; i++)
+        if (digests[i].nid == nid)
+            return (digests[i].id);
+    return (0);
+}
+
+static int digest_key_length(int nid)
+{
+    int i;
+
+    for (i = 0; digests[i].id; i++)
+        if (digests[i].nid == nid)
+            return digests[i].keylen;
+    return (0);
+}
+
+static int cryptodev_digest_init(EVP_MD_CTX *ctx)
+{
+    struct dev_crypto_state *state = EVP_MD_CTX_md_data(ctx);
+    struct session_op *sess = &state->d_sess;
+    int digest;
+
+    if ((digest = digest_nid_to_cryptodev(EVP_MD_CTX_type(ctx))) == NID_undef) {
+        printf("cryptodev_digest_init: Can't get digest \n");
+        return (0);
+    }
+
+    memset(state, 0, sizeof(*state));
+
+    if ((state->d_fd = get_dev_crypto()) < 0) {
+        printf("cryptodev_digest_init: Can't get Dev \n");
+        return (0);
+    }
+
+    sess->mackey = state->dummy_mac_key;
+    sess->mackeylen = digest_key_length(EVP_MD_CTX_type(ctx));
+    sess->mac = digest;
+
+    if (ioctl(state->d_fd, CIOCGSESSION, sess) < 0) {
+        put_dev_crypto(state->d_fd);
+        state->d_fd = -1;
+        printf("cryptodev_digest_init: Open session failed\n");
+        return (0);
+    }
+
+    return (1);
+}
+
+static int cryptodev_digest_update(EVP_MD_CTX *ctx, const void *data,
+                                   size_t count)
+{
+    struct crypt_op cryp;
+    struct dev_crypto_state *state = EVP_MD_CTX_md_data(ctx);
+    struct session_op *sess = &state->d_sess;
+    char *new_mac_data;
+
+    if (!data || state->d_fd < 0) {
+        printf("cryptodev_digest_update: illegal inputs \n");
+        return (0);
+    }
+
+    if (!count) {
+        return (0);
+    }
+
+    if (!EVP_MD_CTX_test_flags(ctx, EVP_MD_CTX_FLAG_ONESHOT)) {
+        /* if application doesn't support one buffer */
+        new_mac_data =
+            OPENSSL_realloc(state->mac_data, state->mac_len + count);
+
+        if (!new_mac_data) {
+            printf("cryptodev_digest_update: realloc failed\n");
+            return (0);
+        }
+        state->mac_data = new_mac_data;
+
+        memcpy(state->mac_data + state->mac_len, data, count);
+        state->mac_len += count;
+
+        return (1);
+    }
+
+    memset(&cryp, 0, sizeof(cryp));
+
+    cryp.ses = sess->ses;
+    cryp.flags = 0;
+    cryp.len = count;
+    cryp.src = (caddr_t) data;
+    cryp.dst = NULL;
+    cryp.mac = (caddr_t) state->digest_res;
+    if (ioctl(state->d_fd, CIOCCRYPT, &cryp) < 0) {
+        printf("cryptodev_digest_update: digest failed\n");
+        return (0);
+    }
+    return (1);
+}
+
+static int cryptodev_digest_final(EVP_MD_CTX *ctx, unsigned char *md)
+{
+    struct crypt_op cryp;
+    struct dev_crypto_state *state = EVP_MD_CTX_md_data(ctx);
+    struct session_op *sess = &state->d_sess;
+
+    int ret = 1;
+
+    if (!md || state->d_fd < 0) {
+        printf("cryptodev_digest_final: illegal input\n");
+        return (0);
+    }
+
+    if (!EVP_MD_CTX_test_flags(ctx, EVP_MD_CTX_FLAG_ONESHOT)) {
+        /* if application doesn't support one buffer */
+        memset(&cryp, 0, sizeof(cryp));
+        cryp.ses = sess->ses;
+        cryp.flags = 0;
+        cryp.len = state->mac_len;
+        cryp.src = state->mac_data;
+        cryp.dst = NULL;
+        cryp.mac = (caddr_t) md;
+        if (ioctl(state->d_fd, CIOCCRYPT, &cryp) < 0) {
+            printf("cryptodev_digest_final: digest failed\n");
+            return (0);
+        }
+
+        return 1;
+    }
+
+    memcpy(md, state->digest_res, EVP_MD_CTX_size(ctx));
+
+    return (ret);
+}
+
+static int cryptodev_digest_cleanup(EVP_MD_CTX *ctx)
+{
+    int ret = 1;
+    struct dev_crypto_state *state = EVP_MD_CTX_md_data(ctx);
+    struct session_op *sess = &state->d_sess;
+
+    if (state == NULL)
+        return 0;
+
+    if (state->d_fd < 0) {
+        printf("cryptodev_digest_cleanup: illegal input\n");
+        return (0);
+    }
+
+    OPENSSL_free(state->mac_data);
+    state->mac_data = NULL;
+    state->mac_len = 0;
+
+    if (ioctl(state->d_fd, CIOCFSESSION, &sess->ses) < 0) {
+        printf("cryptodev_digest_cleanup: failed to close session\n");
+        ret = 0;
+    } else {
+        ret = 1;
+    }
+    put_dev_crypto(state->d_fd);
+    state->d_fd = -1;
+
+    return (ret);
+}
+
+static int cryptodev_digest_copy(EVP_MD_CTX *to, const EVP_MD_CTX *from)
+{
+    struct dev_crypto_state *fstate = EVP_MD_CTX_md_data(from);
+    struct dev_crypto_state *dstate = EVP_MD_CTX_md_data(to);
+    struct session_op *sess;
+    int digest;
+
+    if (dstate == NULL || fstate == NULL)
+        return 1;
+
+    memcpy(dstate, fstate, sizeof(struct dev_crypto_state));
+
+    sess = &dstate->d_sess;
+
+    digest = digest_nid_to_cryptodev(EVP_MD_CTX_type(to));
+
+    sess->mackey = dstate->dummy_mac_key;
+    sess->mackeylen = digest_key_length(EVP_MD_CTX_type(to));
+    sess->mac = digest;
+
+    dstate->d_fd = get_dev_crypto();
+
+    if (ioctl(dstate->d_fd, CIOCGSESSION, sess) < 0) {
+        put_dev_crypto(dstate->d_fd);
+        dstate->d_fd = -1;
+        printf("cryptodev_digest_copy: Open session failed\n");
+        return (0);
+    }
+
+    if (fstate->mac_len != 0) {
+        if (fstate->mac_data != NULL) {
+            dstate->mac_data = OPENSSL_malloc(fstate->mac_len);
+            if (dstate->mac_data == NULL) {
+                printf("cryptodev_digest_copy: mac_data allocation failed\n");
+                return (0);
+            }
+            memcpy(dstate->mac_data, fstate->mac_data, fstate->mac_len);
+            dstate->mac_len = fstate->mac_len;
+        }
+    }
+
+    return 1;
+}
+
+static EVP_MD *sha1_md = NULL;
+static const EVP_MD *cryptodev_sha1(void)
+{
+    if (sha1_md == NULL) {
+        EVP_MD *md;
+
+        if ((md = EVP_MD_meth_new(NID_sha1, NID_undef)) == NULL
+            || !EVP_MD_meth_set_result_size(md, SHA_DIGEST_LENGTH)
+            || !EVP_MD_meth_set_flags(md, EVP_MD_FLAG_ONESHOT)
+            || !EVP_MD_meth_set_input_blocksize(md, SHA_CBLOCK)
+            || !EVP_MD_meth_set_app_datasize(md,
+                                             sizeof(struct dev_crypto_state))
+            || !EVP_MD_meth_set_init(md, cryptodev_digest_init)
+            || !EVP_MD_meth_set_update(md, cryptodev_digest_update)
+            || !EVP_MD_meth_set_final(md, cryptodev_digest_final)
+            || !EVP_MD_meth_set_copy(md, cryptodev_digest_copy)
+            || !EVP_MD_meth_set_cleanup(md, cryptodev_digest_cleanup)) {
+            EVP_MD_meth_free(md);
+            md = NULL;
+        }
+        sha1_md = md;
+    }
+    return sha1_md;
+}
+
+static EVP_MD *md5_md = NULL;
+static const EVP_MD *cryptodev_md5(void)
+{
+    if (md5_md == NULL) {
+        EVP_MD *md;
+
+        if ((md = EVP_MD_meth_new(NID_md5, NID_undef)) == NULL
+            || !EVP_MD_meth_set_result_size(md, 16 /* MD5_DIGEST_LENGTH */)
+            || !EVP_MD_meth_set_flags(md, EVP_MD_FLAG_ONESHOT)
+            || !EVP_MD_meth_set_input_blocksize(md, 64 /* MD5_CBLOCK */)
+            || !EVP_MD_meth_set_app_datasize(md,
+                                             sizeof(struct dev_crypto_state))
+            || !EVP_MD_meth_set_init(md, cryptodev_digest_init)
+            || !EVP_MD_meth_set_update(md, cryptodev_digest_update)
+            || !EVP_MD_meth_set_final(md, cryptodev_digest_final)
+            || !EVP_MD_meth_set_copy(md, cryptodev_digest_copy)
+            || !EVP_MD_meth_set_cleanup(md, cryptodev_digest_cleanup)) {
+            EVP_MD_meth_free(md);
+            md = NULL;
+        }
+        md5_md = md;
+    }
+    return md5_md;
+}
+
+# endif                         /* USE_CRYPTODEV_DIGESTS */
+
+static int
+cryptodev_engine_digests(ENGINE *e, const EVP_MD **digest,
+                         const int **nids, int nid)
+{
+    if (!digest)
+        return (cryptodev_usable_digests(nids));
+
+    switch (nid) {
+# ifdef USE_CRYPTODEV_DIGESTS
+    case NID_md5:
+        *digest = cryptodev_md5();
+        break;
+    case NID_sha1:
+        *digest = cryptodev_sha1();
+        break;
+    default:
+# endif                         /* USE_CRYPTODEV_DIGESTS */
+        *digest = NULL;
+        break;
+    }
+    return (*digest != NULL);
+}
+
+static int cryptodev_engine_destroy(ENGINE *e)
+{
+    EVP_CIPHER_meth_free(rc4_cipher);
+    rc4_cipher = NULL;
+    EVP_CIPHER_meth_free(des_cbc_cipher);
+    des_cbc_cipher = NULL;
+    EVP_CIPHER_meth_free(des3_cbc_cipher);
+    des3_cbc_cipher = NULL;
+    EVP_CIPHER_meth_free(bf_cbc_cipher);
+    bf_cbc_cipher = NULL;
+    EVP_CIPHER_meth_free(cast_cbc_cipher);
+    cast_cbc_cipher = NULL;
+    EVP_CIPHER_meth_free(aes_cbc_cipher);
+    aes_cbc_cipher = NULL;
+    EVP_CIPHER_meth_free(aes_192_cbc_cipher);
+    aes_192_cbc_cipher = NULL;
+    EVP_CIPHER_meth_free(aes_256_cbc_cipher);
+    aes_256_cbc_cipher = NULL;
+# ifdef CRYPTO_AES_CTR
+    EVP_CIPHER_meth_free(aes_ctr_cipher);
+    aes_ctr_cipher = NULL;
+    EVP_CIPHER_meth_free(aes_192_ctr_cipher);
+    aes_192_ctr_cipher = NULL;
+    EVP_CIPHER_meth_free(aes_256_ctr_cipher);
+    aes_256_ctr_cipher = NULL;
+# endif
+# ifdef USE_CRYPTODEV_DIGESTS
+    EVP_MD_meth_free(sha1_md);
+    sha1_md = NULL;
+    EVP_MD_meth_free(md5_md);
+    md5_md = NULL;
+# endif
+    RSA_meth_free(cryptodev_rsa);
+    cryptodev_rsa = NULL;
+#ifndef OPENSSL_NO_DSA
+    DSA_meth_free(cryptodev_dsa);
+    cryptodev_dsa = NULL;
+#endif
+#ifndef OPENSSL_NO_DH
+    DH_meth_free(cryptodev_dh);
+    cryptodev_dh = NULL;
+#endif
+    return 1;
+}
+
+/*
+ * Convert a BIGNUM to the representation that /dev/crypto needs.
+ * Upon completion of use, the caller is responsible for freeing
+ * crp->crp_p.
+ */
+static int bn2crparam(const BIGNUM *a, struct crparam *crp)
+{
+    ssize_t bytes, bits;
+    u_char *b;
+
+    crp->crp_p = NULL;
+    crp->crp_nbits = 0;
+
+    bits = BN_num_bits(a);
+    bytes = BN_num_bytes(a);
+
+    b = OPENSSL_zalloc(bytes);
+    if (b == NULL)
+        return (1);
+
+    crp->crp_p = (caddr_t) b;
+    crp->crp_nbits = bits;
+
+    BN_bn2bin(a, b);
+    return (0);
+}
+
+/* Convert a /dev/crypto parameter to a BIGNUM */
+static int crparam2bn(struct crparam *crp, BIGNUM *a)
+{
+    u_int8_t *pd;
+    int i, bytes;
+
+    bytes = (crp->crp_nbits + 7) / 8;
+
+    if (bytes == 0)
+        return (-1);
+
+    if ((pd = OPENSSL_malloc(bytes)) == NULL)
+        return (-1);
+
+    for (i = 0; i < bytes; i++)
+        pd[i] = crp->crp_p[bytes - i - 1];
+
+    BN_bin2bn(pd, bytes, a);
+    free(pd);
+
+    return (0);
+}
+
+static void zapparams(struct crypt_kop *kop)
+{
+    int i;
+
+    for (i = 0; i < kop->crk_iparams + kop->crk_oparams; i++) {
+        OPENSSL_free(kop->crk_param[i].crp_p);
+        kop->crk_param[i].crp_p = NULL;
+        kop->crk_param[i].crp_nbits = 0;
+    }
+}
+
+static int
+cryptodev_asym(struct crypt_kop *kop, int rlen, BIGNUM *r, int slen,
+               BIGNUM *s)
+{
+    int fd, ret = -1;
+
+    if ((fd = get_asym_dev_crypto()) < 0)
+        return ret;
+
+    if (r) {
+        kop->crk_param[kop->crk_iparams].crp_p = OPENSSL_zalloc(rlen);
+        if (kop->crk_param[kop->crk_iparams].crp_p == NULL)
+            return ret;
+        kop->crk_param[kop->crk_iparams].crp_nbits = rlen * 8;
+        kop->crk_oparams++;
+    }
+    if (s) {
+        kop->crk_param[kop->crk_iparams + 1].crp_p =
+            OPENSSL_zalloc(slen);
+        /* No need to free the kop->crk_iparams parameter if it was allocated,
+         * callers of this routine have to free allocated parameters through
+         * zapparams both in case of success and failure
+         */
+        if (kop->crk_param[kop->crk_iparams+1].crp_p == NULL)
+            return ret;
+        kop->crk_param[kop->crk_iparams + 1].crp_nbits = slen * 8;
+        kop->crk_oparams++;
+    }
+
+    if (ioctl(fd, CIOCKEY, kop) == 0) {
+        if (r)
+            crparam2bn(&kop->crk_param[kop->crk_iparams], r);
+        if (s)
+            crparam2bn(&kop->crk_param[kop->crk_iparams + 1], s);
+        ret = 0;
+    }
+
+    return ret;
+}
+
+static int
+cryptodev_bn_mod_exp(BIGNUM *r, const BIGNUM *a, const BIGNUM *p,
+                     const BIGNUM *m, BN_CTX *ctx, BN_MONT_CTX *in_mont)
+{
+    struct crypt_kop kop;
+    int ret = 1;
+
+    /*
+     * Currently, we know we can do mod exp iff we can do any asymmetric
+     * operations at all.
+     */
+    if (cryptodev_asymfeat == 0) {
+        ret = BN_mod_exp(r, a, p, m, ctx);
+        return (ret);
+    }
+
+    memset(&kop, 0, sizeof(kop));
+    kop.crk_op = CRK_MOD_EXP;
+
+    /* inputs: a^p % m */
+    if (bn2crparam(a, &kop.crk_param[0]))
+        goto err;
+    if (bn2crparam(p, &kop.crk_param[1]))
+        goto err;
+    if (bn2crparam(m, &kop.crk_param[2]))
+        goto err;
+    kop.crk_iparams = 3;
+
+    if (cryptodev_asym(&kop, BN_num_bytes(m), r, 0, NULL)) {
+        const RSA_METHOD *meth = RSA_PKCS1_OpenSSL();
+        printf("OCF asym process failed, Running in software\n");
+        ret = RSA_meth_get_bn_mod_exp(meth)(r, a, p, m, ctx, in_mont);
+
+    } else if (ECANCELED == kop.crk_status) {
+        const RSA_METHOD *meth = RSA_PKCS1_OpenSSL();
+        printf("OCF hardware operation cancelled. Running in Software\n");
+        ret = RSA_meth_get_bn_mod_exp(meth)(r, a, p, m, ctx, in_mont);
+    }
+    /* else cryptodev operation worked ok ==> ret = 1 */
+
+ err:
+    zapparams(&kop);
+    return (ret);
+}
+
+static int
+cryptodev_rsa_nocrt_mod_exp(BIGNUM *r0, const BIGNUM *I, RSA *rsa,
+                            BN_CTX *ctx)
+{
+    int r;
+    const BIGNUM *n = NULL;
+    const BIGNUM *d = NULL;
+
+    ctx = BN_CTX_new();
+    RSA_get0_key(rsa, &n, NULL, &d);
+    r = cryptodev_bn_mod_exp(r0, I, d, n, ctx, NULL);
+    BN_CTX_free(ctx);
+    return (r);
+}
+
+static int
+cryptodev_rsa_mod_exp(BIGNUM *r0, const BIGNUM *I, RSA *rsa, BN_CTX *ctx)
+{
+    struct crypt_kop kop;
+    int ret = 1;
+    const BIGNUM *p = NULL;
+    const BIGNUM *q = NULL;
+    const BIGNUM *dmp1 = NULL;
+    const BIGNUM *dmq1 = NULL;
+    const BIGNUM *iqmp = NULL;
+    const BIGNUM *n = NULL;
+
+    RSA_get0_factors(rsa, &p, &q);
+    RSA_get0_crt_params(rsa, &dmp1, &dmq1, &iqmp);
+    RSA_get0_key(rsa, &n, NULL, NULL);
+
+    if (!p || !q || !dmp1 || !dmq1 || !iqmp) {
+        /* XXX 0 means failure?? */
+        return (0);
+    }
+
+    memset(&kop, 0, sizeof(kop));
+    kop.crk_op = CRK_MOD_EXP_CRT;
+    /* inputs: rsa->p rsa->q I rsa->dmp1 rsa->dmq1 rsa->iqmp */
+    if (bn2crparam(p, &kop.crk_param[0]))
+        goto err;
+    if (bn2crparam(q, &kop.crk_param[1]))
+        goto err;
+    if (bn2crparam(I, &kop.crk_param[2]))
+        goto err;
+    if (bn2crparam(dmp1, &kop.crk_param[3]))
+        goto err;
+    if (bn2crparam(dmq1, &kop.crk_param[4]))
+        goto err;
+    if (bn2crparam(iqmp, &kop.crk_param[5]))
+        goto err;
+    kop.crk_iparams = 6;
+
+    if (cryptodev_asym(&kop, BN_num_bytes(n), r0, 0, NULL)) {
+        const RSA_METHOD *meth = RSA_PKCS1_OpenSSL();
+        printf("OCF asym process failed, running in Software\n");
+        ret = RSA_meth_get_mod_exp(meth)(r0, I, rsa, ctx);
+
+    } else if (ECANCELED == kop.crk_status) {
+        const RSA_METHOD *meth = RSA_PKCS1_OpenSSL();
+        printf("OCF hardware operation cancelled. Running in Software\n");
+        ret = RSA_meth_get_mod_exp(meth)(r0, I, rsa, ctx);
+    }
+    /* else cryptodev operation worked ok ==> ret = 1 */
+
+ err:
+    zapparams(&kop);
+    return (ret);
+}
+
+#ifndef OPENSSL_NO_DSA
+static int
+cryptodev_dsa_bn_mod_exp(DSA *dsa, BIGNUM *r, const BIGNUM *a, const BIGNUM *p,
+                         const BIGNUM *m, BN_CTX *ctx, BN_MONT_CTX *m_ctx)
+{
+    return cryptodev_bn_mod_exp(r, a, p, m, ctx, m_ctx);
+}
+
+static int
+cryptodev_dsa_dsa_mod_exp(DSA *dsa, BIGNUM *t1, const BIGNUM *g,
+                          const BIGNUM *u1, const BIGNUM *pub_key,
+                          const BIGNUM *u2, const BIGNUM *p, BN_CTX *ctx,
+                          BN_MONT_CTX *mont)
+{
+    const BIGNUM *dsag, *dsap, *dsapub_key;
+    BIGNUM *t2;
+    int ret = 0;
+    const DSA_METHOD *meth;
+    int (*bn_mod_exp)(DSA *, BIGNUM *, const BIGNUM *, const BIGNUM *, const BIGNUM *,
+                      BN_CTX *, BN_MONT_CTX *);
+
+    t2 = BN_new();
+    if (t2 == NULL)
+        goto err;
+
+    /* v = ( g^u1 * y^u2 mod p ) mod q */
+    /* let t1 = g ^ u1 mod p */
+    ret = 0;
+
+    DSA_get0_pqg(dsa, &dsap, NULL, &dsag);
+    DSA_get0_key(dsa, &dsapub_key, NULL);
+
+    meth = DSA_get_method(dsa);
+    if (meth == NULL)
+        goto err;
+    bn_mod_exp = DSA_meth_get_bn_mod_exp(meth);
+    if (bn_mod_exp == NULL)
+        goto err;
+
+    if (!bn_mod_exp(dsa, t1, dsag, u1, dsap, ctx, mont))
+        goto err;
+
+    /* let t2 = y ^ u2 mod p */
+    if (!bn_mod_exp(dsa, t2, dsapub_key, u2, dsap, ctx, mont))
+        goto err;
+    /* let t1 = t1 * t2 mod p */
+    if (!BN_mod_mul(t1, t1, t2, dsap, ctx))
+        goto err;
+
+    ret = 1;
+ err:
+    BN_free(t2);
+    return (ret);
+}
+
+static DSA_SIG *cryptodev_dsa_do_sign(const unsigned char *dgst, int dlen,
+                                      DSA *dsa)
+{
+    struct crypt_kop kop;
+    BIGNUM *r, *s;
+    const BIGNUM *dsap = NULL, *dsaq = NULL, *dsag = NULL;
+    const BIGNUM *priv_key = NULL;
+    DSA_SIG *dsasig, *dsaret = NULL;
+
+    dsasig = DSA_SIG_new();
+    if (dsasig == NULL)
+        goto err;
+
+    memset(&kop, 0, sizeof(kop));
+    kop.crk_op = CRK_DSA_SIGN;
+
+    /* inputs: dgst dsa->p dsa->q dsa->g dsa->priv_key */
+    kop.crk_param[0].crp_p = (caddr_t) dgst;
+    kop.crk_param[0].crp_nbits = dlen * 8;
+    DSA_get0_pqg(dsa, &dsap, &dsaq, &dsag);
+    DSA_get0_key(dsa, NULL, &priv_key);
+    if (bn2crparam(dsap, &kop.crk_param[1]))
+        goto err;
+    if (bn2crparam(dsaq, &kop.crk_param[2]))
+        goto err;
+    if (bn2crparam(dsag, &kop.crk_param[3]))
+        goto err;
+    if (bn2crparam(priv_key, &kop.crk_param[4]))
+        goto err;
+    kop.crk_iparams = 5;
+
+    r = BN_new();
+    if (r == NULL)
+        goto err;
+    s = BN_new();
+    if (s == NULL)
+        goto err;
+    if (cryptodev_asym(&kop, BN_num_bytes(dsaq), r,
+                       BN_num_bytes(dsaq), s) == 0) {
+        DSA_SIG_set0(dsasig, r, s);
+        dsaret = dsasig;
+    } else {
+        dsaret = DSA_meth_get_sign(DSA_OpenSSL())(dgst, dlen, dsa);
+    }
+ err:
+    if (dsaret != dsasig)
+        DSA_SIG_free(dsasig);
+    kop.crk_param[0].crp_p = NULL;
+    zapparams(&kop);
+    return dsaret;
+}
+
+static int
+cryptodev_dsa_verify(const unsigned char *dgst, int dlen,
+                     DSA_SIG *sig, DSA *dsa)
+{
+    struct crypt_kop kop;
+    int dsaret = 1;
+    const BIGNUM *pr, *ps, *p = NULL, *q = NULL, *g = NULL, *pub_key = NULL;
+
+    memset(&kop, 0, sizeof(kop));
+    kop.crk_op = CRK_DSA_VERIFY;
+
+    /* inputs: dgst dsa->p dsa->q dsa->g dsa->pub_key sig->r sig->s */
+    kop.crk_param[0].crp_p = (caddr_t) dgst;
+    kop.crk_param[0].crp_nbits = dlen * 8;
+    DSA_get0_pqg(dsa, &p, &q, &g);
+    if (bn2crparam(p, &kop.crk_param[1]))
+        goto err;
+    if (bn2crparam(q, &kop.crk_param[2]))
+        goto err;
+    if (bn2crparam(g, &kop.crk_param[3]))
+        goto err;
+    DSA_get0_key(dsa, &pub_key, NULL);
+    if (bn2crparam(pub_key, &kop.crk_param[4]))
+        goto err;
+    DSA_SIG_get0(sig, &pr, &ps);
+    if (bn2crparam(pr, &kop.crk_param[5]))
+        goto err;
+    if (bn2crparam(ps, &kop.crk_param[6]))
+        goto err;
+    kop.crk_iparams = 7;
+
+    if (cryptodev_asym(&kop, 0, NULL, 0, NULL) == 0) {
+        /*
+         * OCF success value is 0, if not zero, change dsaret to fail
+         */
+        if (0 != kop.crk_status)
+            dsaret = 0;
+    } else {
+        dsaret = DSA_meth_get_verify(DSA_OpenSSL())(dgst, dlen, sig, dsa);
+    }
+ err:
+    kop.crk_param[0].crp_p = NULL;
+    zapparams(&kop);
+    return (dsaret);
+}
+#endif
+
+#ifndef OPENSSL_NO_DH
+static int
+cryptodev_mod_exp_dh(const DH *dh, BIGNUM *r, const BIGNUM *a,
+                     const BIGNUM *p, const BIGNUM *m, BN_CTX *ctx,
+                     BN_MONT_CTX *m_ctx)
+{
+    return (cryptodev_bn_mod_exp(r, a, p, m, ctx, m_ctx));
+}
+
+static int
+cryptodev_dh_compute_key(unsigned char *key, const BIGNUM *pub_key, DH *dh)
+{
+    struct crypt_kop kop;
+    int dhret = 1;
+    int fd, keylen;
+    const BIGNUM *p = NULL;
+    const BIGNUM *priv_key = NULL;
+
+    if ((fd = get_asym_dev_crypto()) < 0) {
+        const DH_METHOD *meth = DH_OpenSSL();
+
+        return DH_meth_get_compute_key(meth)(key, pub_key, dh);
+    }
+
+    DH_get0_pqg(dh, &p, NULL, NULL);
+    DH_get0_key(dh, NULL, &priv_key);
+
+    keylen = BN_num_bits(p);
+
+    memset(&kop, 0, sizeof(kop));
+    kop.crk_op = CRK_DH_COMPUTE_KEY;
+
+    /* inputs: dh->priv_key pub_key dh->p key */
+    if (bn2crparam(priv_key, &kop.crk_param[0]))
+        goto err;
+    if (bn2crparam(pub_key, &kop.crk_param[1]))
+        goto err;
+    if (bn2crparam(p, &kop.crk_param[2]))
+        goto err;
+    kop.crk_iparams = 3;
+
+    kop.crk_param[3].crp_p = (caddr_t) key;
+    kop.crk_param[3].crp_nbits = keylen * 8;
+    kop.crk_oparams = 1;
+
+    if (ioctl(fd, CIOCKEY, &kop) == -1) {
+        const DH_METHOD *meth = DH_OpenSSL();
+
+        dhret = DH_meth_get_compute_key(meth)(key, pub_key, dh);
+    }
+ err:
+    kop.crk_param[3].crp_p = NULL;
+    zapparams(&kop);
+    return (dhret);
+}
+
+#endif /* ndef OPENSSL_NO_DH */
+
+/*
+ * ctrl right now is just a wrapper that doesn't do much
+ * but I expect we'll want some options soon.
+ */
+static int
+cryptodev_ctrl(ENGINE *e, int cmd, long i, void *p, void (*f) (void))
+{
+# ifdef HAVE_SYSLOG_R
+    struct syslog_data sd = SYSLOG_DATA_INIT;
+# endif
+
+    switch (cmd) {
+    default:
+# ifdef HAVE_SYSLOG_R
+        syslog_r(LOG_ERR, &sd, "cryptodev_ctrl: unknown command %d", cmd);
+# else
+        syslog(LOG_ERR, "cryptodev_ctrl: unknown command %d", cmd);
+# endif
+        break;
+    }
+    return (1);
+}
+
+void engine_load_cryptodev_int(void)
+{
+    ENGINE *engine = ENGINE_new();
+    int fd;
+
+    if (engine == NULL)
+        return;
+    if ((fd = get_dev_crypto()) < 0) {
+        ENGINE_free(engine);
+        return;
+    }
+
+    /*
+     * find out what asymmetric crypto algorithms we support
+     */
+    if (ioctl(fd, CIOCASYMFEAT, &cryptodev_asymfeat) == -1) {
+        put_dev_crypto(fd);
+        ENGINE_free(engine);
+        return;
+    }
+    put_dev_crypto(fd);
+
+    if (!ENGINE_set_id(engine, "cryptodev") ||
+        !ENGINE_set_name(engine, "BSD cryptodev engine") ||
+        !ENGINE_set_destroy_function(engine, cryptodev_engine_destroy) ||
+        !ENGINE_set_ciphers(engine, cryptodev_engine_ciphers) ||
+        !ENGINE_set_digests(engine, cryptodev_engine_digests) ||
+        !ENGINE_set_ctrl_function(engine, cryptodev_ctrl) ||
+        !ENGINE_set_cmd_defns(engine, cryptodev_defns)) {
+        ENGINE_free(engine);
+        return;
+    }
+
+    cryptodev_rsa = RSA_meth_dup(RSA_PKCS1_OpenSSL());
+    if (cryptodev_rsa != NULL) {
+        RSA_meth_set1_name(cryptodev_rsa, "cryptodev RSA method");
+        RSA_meth_set_flags(cryptodev_rsa, 0);
+        if (ENGINE_set_RSA(engine, cryptodev_rsa)) {
+            if (cryptodev_asymfeat & CRF_MOD_EXP) {
+                RSA_meth_set_bn_mod_exp(cryptodev_rsa, cryptodev_bn_mod_exp);
+                if (cryptodev_asymfeat & CRF_MOD_EXP_CRT)
+                    RSA_meth_set_mod_exp(cryptodev_rsa, cryptodev_rsa_mod_exp);
+                else
+                    RSA_meth_set_mod_exp(cryptodev_rsa,
+                                         cryptodev_rsa_nocrt_mod_exp);
+            }
+        }
+    } else {
+        ENGINE_free(engine);
+        return;
+    }
+
+#ifndef OPENSSL_NO_DSA
+    cryptodev_dsa = DSA_meth_dup(DSA_OpenSSL());
+    if (cryptodev_dsa != NULL) {
+        DSA_meth_set1_name(cryptodev_dsa, "cryptodev DSA method");
+        DSA_meth_set_flags(cryptodev_dsa, 0);
+        if (ENGINE_set_DSA(engine, cryptodev_dsa)) {
+            if (cryptodev_asymfeat & CRF_DSA_SIGN)
+                DSA_meth_set_sign(cryptodev_dsa, cryptodev_dsa_do_sign);
+            if (cryptodev_asymfeat & CRF_MOD_EXP) {
+                DSA_meth_set_bn_mod_exp(cryptodev_dsa,
+                                        cryptodev_dsa_bn_mod_exp);
+                DSA_meth_set_mod_exp(cryptodev_dsa, cryptodev_dsa_dsa_mod_exp);
+            }
+            if (cryptodev_asymfeat & CRF_DSA_VERIFY)
+                DSA_meth_set_verify(cryptodev_dsa, cryptodev_dsa_verify);
+        }
+    } else {
+        ENGINE_free(engine);
+        return;
+    }
+#endif
+
+#ifndef OPENSSL_NO_DH
+    cryptodev_dh = DH_meth_dup(DH_OpenSSL());
+    if (cryptodev_dh != NULL) {
+        DH_meth_set1_name(cryptodev_dh, "cryptodev DH method");
+        DH_meth_set_flags(cryptodev_dh, 0);
+        if (ENGINE_set_DH(engine, cryptodev_dh)) {
+            if (cryptodev_asymfeat & CRF_MOD_EXP) {
+                DH_meth_set_bn_mod_exp(cryptodev_dh, cryptodev_mod_exp_dh);
+                if (cryptodev_asymfeat & CRF_DH_COMPUTE_KEY)
+                    DH_meth_set_compute_key(cryptodev_dh,
+                                            cryptodev_dh_compute_key);
+            }
+        }
+    } else {
+        ENGINE_free(engine);
+        return;
+    }
+#endif
+
+    ENGINE_add(engine);
+    ENGINE_free(engine);
+    ERR_clear_error();
+}
+
+#endif                          /* HAVE_CRYPTODEV */
diff --git a/crypto/engine/eng_ctrl.c b/crypto/engine/eng_ctrl.c
new file mode 100644
index 000000000..02ba1a885
--- /dev/null
+++ b/crypto/engine/eng_ctrl.c
@@ -0,0 +1,341 @@
+/*
+ * Copyright 2001-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "eng_int.h"
+
+/*
+ * When querying a ENGINE-specific control command's 'description', this
+ * string is used if the ENGINE_CMD_DEFN has cmd_desc set to NULL.
+ */
+static const char *int_no_description = "";
+
+/*
+ * These internal functions handle 'CMD'-related control commands when the
+ * ENGINE in question has asked us to take care of it (ie. the ENGINE did not
+ * set the ENGINE_FLAGS_MANUAL_CMD_CTRL flag.
+ */
+
+static int int_ctrl_cmd_is_null(const ENGINE_CMD_DEFN *defn)
+{
+    if ((defn->cmd_num == 0) || (defn->cmd_name == NULL))
+        return 1;
+    return 0;
+}
+
+static int int_ctrl_cmd_by_name(const ENGINE_CMD_DEFN *defn, const char *s)
+{
+    int idx = 0;
+    while (!int_ctrl_cmd_is_null(defn) && (strcmp(defn->cmd_name, s) != 0)) {
+        idx++;
+        defn++;
+    }
+    if (int_ctrl_cmd_is_null(defn))
+        /* The given name wasn't found */
+        return -1;
+    return idx;
+}
+
+static int int_ctrl_cmd_by_num(const ENGINE_CMD_DEFN *defn, unsigned int num)
+{
+    int idx = 0;
+    /*
+     * NB: It is stipulated that 'cmd_defn' lists are ordered by cmd_num. So
+     * our searches don't need to take any longer than necessary.
+     */
+    while (!int_ctrl_cmd_is_null(defn) && (defn->cmd_num < num)) {
+        idx++;
+        defn++;
+    }
+    if (defn->cmd_num == num)
+        return idx;
+    /* The given cmd_num wasn't found */
+    return -1;
+}
+
+static int int_ctrl_helper(ENGINE *e, int cmd, long i, void *p,
+                           void (*f) (void))
+{
+    int idx;
+    char *s = (char *)p;
+    /* Take care of the easy one first (eg. it requires no searches) */
+    if (cmd == ENGINE_CTRL_GET_FIRST_CMD_TYPE) {
+        if ((e->cmd_defns == NULL) || int_ctrl_cmd_is_null(e->cmd_defns))
+            return 0;
+        return e->cmd_defns->cmd_num;
+    }
+    /* One or two commands require that "p" be a valid string buffer */
+    if ((cmd == ENGINE_CTRL_GET_CMD_FROM_NAME) ||
+        (cmd == ENGINE_CTRL_GET_NAME_FROM_CMD) ||
+        (cmd == ENGINE_CTRL_GET_DESC_FROM_CMD)) {
+        if (s == NULL) {
+            ENGINEerr(ENGINE_F_INT_CTRL_HELPER, ERR_R_PASSED_NULL_PARAMETER);
+            return -1;
+        }
+    }
+    /* Now handle cmd_name -> cmd_num conversion */
+    if (cmd == ENGINE_CTRL_GET_CMD_FROM_NAME) {
+        if ((e->cmd_defns == NULL)
+            || ((idx = int_ctrl_cmd_by_name(e->cmd_defns, s)) < 0)) {
+            ENGINEerr(ENGINE_F_INT_CTRL_HELPER, ENGINE_R_INVALID_CMD_NAME);
+            return -1;
+        }
+        return e->cmd_defns[idx].cmd_num;
+    }
+    /*
+     * For the rest of the commands, the 'long' argument must specify a valid
+     * command number - so we need to conduct a search.
+     */
+    if ((e->cmd_defns == NULL) || ((idx = int_ctrl_cmd_by_num(e->cmd_defns,
+                                                              (unsigned int)
+                                                              i)) < 0)) {
+        ENGINEerr(ENGINE_F_INT_CTRL_HELPER, ENGINE_R_INVALID_CMD_NUMBER);
+        return -1;
+    }
+    /* Now the logic splits depending on command type */
+    switch (cmd) {
+    case ENGINE_CTRL_GET_NEXT_CMD_TYPE:
+        idx++;
+        if (int_ctrl_cmd_is_null(e->cmd_defns + idx))
+            /* end-of-list */
+            return 0;
+        else
+            return e->cmd_defns[idx].cmd_num;
+    case ENGINE_CTRL_GET_NAME_LEN_FROM_CMD:
+        return strlen(e->cmd_defns[idx].cmd_name);
+    case ENGINE_CTRL_GET_NAME_FROM_CMD:
+        return BIO_snprintf(s, strlen(e->cmd_defns[idx].cmd_name) + 1,
+                            "%s", e->cmd_defns[idx].cmd_name);
+    case ENGINE_CTRL_GET_DESC_LEN_FROM_CMD:
+        if (e->cmd_defns[idx].cmd_desc)
+            return strlen(e->cmd_defns[idx].cmd_desc);
+        return strlen(int_no_description);
+    case ENGINE_CTRL_GET_DESC_FROM_CMD:
+        if (e->cmd_defns[idx].cmd_desc)
+            return BIO_snprintf(s,
+                                strlen(e->cmd_defns[idx].cmd_desc) + 1,
+                                "%s", e->cmd_defns[idx].cmd_desc);
+        return BIO_snprintf(s, strlen(int_no_description) + 1, "%s",
+                            int_no_description);
+    case ENGINE_CTRL_GET_CMD_FLAGS:
+        return e->cmd_defns[idx].cmd_flags;
+    }
+    /* Shouldn't really be here ... */
+    ENGINEerr(ENGINE_F_INT_CTRL_HELPER, ENGINE_R_INTERNAL_LIST_ERROR);
+    return -1;
+}
+
+int ENGINE_ctrl(ENGINE *e, int cmd, long i, void *p, void (*f) (void))
+{
+    int ctrl_exists, ref_exists;
+    if (e == NULL) {
+        ENGINEerr(ENGINE_F_ENGINE_CTRL, ERR_R_PASSED_NULL_PARAMETER);
+        return 0;
+    }
+    CRYPTO_THREAD_write_lock(global_engine_lock);
+    ref_exists = ((e->struct_ref > 0) ? 1 : 0);
+    CRYPTO_THREAD_unlock(global_engine_lock);
+    ctrl_exists = ((e->ctrl == NULL) ? 0 : 1);
+    if (!ref_exists) {
+        ENGINEerr(ENGINE_F_ENGINE_CTRL, ENGINE_R_NO_REFERENCE);
+        return 0;
+    }
+    /*
+     * Intercept any "root-level" commands before trying to hand them on to
+     * ctrl() handlers.
+     */
+    switch (cmd) {
+    case ENGINE_CTRL_HAS_CTRL_FUNCTION:
+        return ctrl_exists;
+    case ENGINE_CTRL_GET_FIRST_CMD_TYPE:
+    case ENGINE_CTRL_GET_NEXT_CMD_TYPE:
+    case ENGINE_CTRL_GET_CMD_FROM_NAME:
+    case ENGINE_CTRL_GET_NAME_LEN_FROM_CMD:
+    case ENGINE_CTRL_GET_NAME_FROM_CMD:
+    case ENGINE_CTRL_GET_DESC_LEN_FROM_CMD:
+    case ENGINE_CTRL_GET_DESC_FROM_CMD:
+    case ENGINE_CTRL_GET_CMD_FLAGS:
+        if (ctrl_exists && !(e->flags & ENGINE_FLAGS_MANUAL_CMD_CTRL))
+            return int_ctrl_helper(e, cmd, i, p, f);
+        if (!ctrl_exists) {
+            ENGINEerr(ENGINE_F_ENGINE_CTRL, ENGINE_R_NO_CONTROL_FUNCTION);
+            /*
+             * For these cmd-related functions, failure is indicated by a -1
+             * return value (because 0 is used as a valid return in some
+             * places).
+             */
+            return -1;
+        }
+    default:
+        break;
+    }
+    /* Anything else requires a ctrl() handler to exist. */
+    if (!ctrl_exists) {
+        ENGINEerr(ENGINE_F_ENGINE_CTRL, ENGINE_R_NO_CONTROL_FUNCTION);
+        return 0;
+    }
+    return e->ctrl(e, cmd, i, p, f);
+}
+
+int ENGINE_cmd_is_executable(ENGINE *e, int cmd)
+{
+    int flags;
+    if ((flags =
+         ENGINE_ctrl(e, ENGINE_CTRL_GET_CMD_FLAGS, cmd, NULL, NULL)) < 0) {
+        ENGINEerr(ENGINE_F_ENGINE_CMD_IS_EXECUTABLE,
+                  ENGINE_R_INVALID_CMD_NUMBER);
+        return 0;
+    }
+    if (!(flags & ENGINE_CMD_FLAG_NO_INPUT) &&
+        !(flags & ENGINE_CMD_FLAG_NUMERIC) &&
+        !(flags & ENGINE_CMD_FLAG_STRING))
+        return 0;
+    return 1;
+}
+
+int ENGINE_ctrl_cmd(ENGINE *e, const char *cmd_name,
+                    long i, void *p, void (*f) (void), int cmd_optional)
+{
+    int num;
+
+    if (e == NULL || cmd_name == NULL) {
+        ENGINEerr(ENGINE_F_ENGINE_CTRL_CMD, ERR_R_PASSED_NULL_PARAMETER);
+        return 0;
+    }
+    if (e->ctrl == NULL
+        || (num = ENGINE_ctrl(e, ENGINE_CTRL_GET_CMD_FROM_NAME,
+                              0, (void *)cmd_name, NULL)) <= 0) {
+        /*
+         * If the command didn't *have* to be supported, we fake success.
+         * This allows certain settings to be specified for multiple ENGINEs
+         * and only require a change of ENGINE id (without having to
+         * selectively apply settings). Eg. changing from a hardware device
+         * back to the regular software ENGINE without editing the config
+         * file, etc.
+         */
+        if (cmd_optional) {
+            ERR_clear_error();
+            return 1;
+        }
+        ENGINEerr(ENGINE_F_ENGINE_CTRL_CMD, ENGINE_R_INVALID_CMD_NAME);
+        return 0;
+    }
+    /*
+     * Force the result of the control command to 0 or 1, for the reasons
+     * mentioned before.
+     */
+    if (ENGINE_ctrl(e, num, i, p, f) > 0)
+        return 1;
+    return 0;
+}
+
+int ENGINE_ctrl_cmd_string(ENGINE *e, const char *cmd_name, const char *arg,
+                           int cmd_optional)
+{
+    int num, flags;
+    long l;
+    char *ptr;
+
+    if (e == NULL || cmd_name == NULL) {
+        ENGINEerr(ENGINE_F_ENGINE_CTRL_CMD_STRING, ERR_R_PASSED_NULL_PARAMETER);
+        return 0;
+    }
+    if (arg && *arg == 0) {
+        arg = NULL;
+    }
+    if (e->ctrl == NULL
+        || (num = ENGINE_ctrl(e, ENGINE_CTRL_GET_CMD_FROM_NAME,
+                              0, (void *)cmd_name, NULL)) <= 0) {
+        /*
+         * If the command didn't *have* to be supported, we fake success.
+         * This allows certain settings to be specified for multiple ENGINEs
+         * and only require a change of ENGINE id (without having to
+         * selectively apply settings). Eg. changing from a hardware device
+         * back to the regular software ENGINE without editing the config
+         * file, etc.
+         */
+        if (cmd_optional) {
+            ERR_clear_error();
+            return 1;
+        }
+        ENGINEerr(ENGINE_F_ENGINE_CTRL_CMD_STRING, ENGINE_R_INVALID_CMD_NAME);
+        return 0;
+    }
+    if (!ENGINE_cmd_is_executable(e, num)) {
+        ENGINEerr(ENGINE_F_ENGINE_CTRL_CMD_STRING,
+                  ENGINE_R_CMD_NOT_EXECUTABLE);
+        return 0;
+    }
+
+    flags = ENGINE_ctrl(e, ENGINE_CTRL_GET_CMD_FLAGS, num, NULL, NULL);
+    if (flags < 0) {
+        /*
+         * Shouldn't happen, given that ENGINE_cmd_is_executable() returned
+         * success.
+         */
+        ENGINEerr(ENGINE_F_ENGINE_CTRL_CMD_STRING,
+                  ENGINE_R_INTERNAL_LIST_ERROR);
+        return 0;
+    }
+    /*
+     * If the command takes no input, there must be no input. And vice versa.
+     */
+    if (flags & ENGINE_CMD_FLAG_NO_INPUT) {
+        if (arg != NULL) {
+            ENGINEerr(ENGINE_F_ENGINE_CTRL_CMD_STRING,
+                      ENGINE_R_COMMAND_TAKES_NO_INPUT);
+            return 0;
+        }
+        /*
+         * We deliberately force the result of ENGINE_ctrl() to 0 or 1 rather
+         * than returning it as "return data". This is to ensure usage of
+         * these commands is consistent across applications and that certain
+         * applications don't understand it one way, and others another.
+         */
+        if (ENGINE_ctrl(e, num, 0, (void *)arg, NULL) > 0)
+            return 1;
+        return 0;
+    }
+    /* So, we require input */
+    if (arg == NULL) {
+        ENGINEerr(ENGINE_F_ENGINE_CTRL_CMD_STRING,
+                  ENGINE_R_COMMAND_TAKES_INPUT);
+        return 0;
+    }
+    /* If it takes string input, that's easy */
+    if (flags & ENGINE_CMD_FLAG_STRING) {
+        /* Same explanation as above */
+        if (ENGINE_ctrl(e, num, 0, (void *)arg, NULL) > 0)
+            return 1;
+        return 0;
+    }
+    /*
+     * If it doesn't take numeric either, then it is unsupported for use in a
+     * config-setting situation, which is what this function is for. This
+     * should never happen though, because ENGINE_cmd_is_executable() was
+     * used.
+     */
+    if (!(flags & ENGINE_CMD_FLAG_NUMERIC)) {
+        ENGINEerr(ENGINE_F_ENGINE_CTRL_CMD_STRING,
+                  ENGINE_R_INTERNAL_LIST_ERROR);
+        return 0;
+    }
+    l = strtol(arg, &ptr, 10);
+    if ((arg == ptr) || (*ptr != '\0')) {
+        ENGINEerr(ENGINE_F_ENGINE_CTRL_CMD_STRING,
+                  ENGINE_R_ARGUMENT_IS_NOT_A_NUMBER);
+        return 0;
+    }
+    /*
+     * Force the result of the control command to 0 or 1, for the reasons
+     * mentioned before.
+     */
+    if (ENGINE_ctrl(e, num, l, NULL, NULL) > 0)
+        return 1;
+    return 0;
+}
diff --git a/crypto/engine/eng_dyn.c b/crypto/engine/eng_dyn.c
new file mode 100644
index 000000000..843226c07
--- /dev/null
+++ b/crypto/engine/eng_dyn.c
@@ -0,0 +1,510 @@
+/*
+ * Copyright 2001-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "eng_int.h"
+#include "internal/dso.h"
+#include 
+
+/*
+ * Shared libraries implementing ENGINEs for use by the "dynamic" ENGINE
+ * loader should implement the hook-up functions with the following
+ * prototypes.
+ */
+
+/* Our ENGINE handlers */
+static int dynamic_init(ENGINE *e);
+static int dynamic_finish(ENGINE *e);
+static int dynamic_ctrl(ENGINE *e, int cmd, long i, void *p,
+                        void (*f) (void));
+/* Predeclare our context type */
+typedef struct st_dynamic_data_ctx dynamic_data_ctx;
+/* The implementation for the important control command */
+static int dynamic_load(ENGINE *e, dynamic_data_ctx *ctx);
+
+#define DYNAMIC_CMD_SO_PATH             ENGINE_CMD_BASE
+#define DYNAMIC_CMD_NO_VCHECK           (ENGINE_CMD_BASE + 1)
+#define DYNAMIC_CMD_ID                  (ENGINE_CMD_BASE + 2)
+#define DYNAMIC_CMD_LIST_ADD            (ENGINE_CMD_BASE + 3)
+#define DYNAMIC_CMD_DIR_LOAD            (ENGINE_CMD_BASE + 4)
+#define DYNAMIC_CMD_DIR_ADD             (ENGINE_CMD_BASE + 5)
+#define DYNAMIC_CMD_LOAD                (ENGINE_CMD_BASE + 6)
+
+/* The constants used when creating the ENGINE */
+static const char *engine_dynamic_id = "dynamic";
+static const char *engine_dynamic_name = "Dynamic engine loading support";
+static const ENGINE_CMD_DEFN dynamic_cmd_defns[] = {
+    {DYNAMIC_CMD_SO_PATH,
+     "SO_PATH",
+     "Specifies the path to the new ENGINE shared library",
+     ENGINE_CMD_FLAG_STRING},
+    {DYNAMIC_CMD_NO_VCHECK,
+     "NO_VCHECK",
+     "Specifies to continue even if version checking fails (boolean)",
+     ENGINE_CMD_FLAG_NUMERIC},
+    {DYNAMIC_CMD_ID,
+     "ID",
+     "Specifies an ENGINE id name for loading",
+     ENGINE_CMD_FLAG_STRING},
+    {DYNAMIC_CMD_LIST_ADD,
+     "LIST_ADD",
+     "Whether to add a loaded ENGINE to the internal list (0=no,1=yes,2=mandatory)",
+     ENGINE_CMD_FLAG_NUMERIC},
+    {DYNAMIC_CMD_DIR_LOAD,
+     "DIR_LOAD",
+     "Specifies whether to load from 'DIR_ADD' directories (0=no,1=yes,2=mandatory)",
+     ENGINE_CMD_FLAG_NUMERIC},
+    {DYNAMIC_CMD_DIR_ADD,
+     "DIR_ADD",
+     "Adds a directory from which ENGINEs can be loaded",
+     ENGINE_CMD_FLAG_STRING},
+    {DYNAMIC_CMD_LOAD,
+     "LOAD",
+     "Load up the ENGINE specified by other settings",
+     ENGINE_CMD_FLAG_NO_INPUT},
+    {0, NULL, NULL, 0}
+};
+
+/*
+ * Loading code stores state inside the ENGINE structure via the "ex_data"
+ * element. We load all our state into a single structure and use that as a
+ * single context in the "ex_data" stack.
+ */
+struct st_dynamic_data_ctx {
+    /* The DSO object we load that supplies the ENGINE code */
+    DSO *dynamic_dso;
+    /*
+     * The function pointer to the version checking shared library function
+     */
+    dynamic_v_check_fn v_check;
+    /*
+     * The function pointer to the engine-binding shared library function
+     */
+    dynamic_bind_engine bind_engine;
+    /* The default name/path for loading the shared library */
+    char *DYNAMIC_LIBNAME;
+    /* Whether to continue loading on a version check failure */
+    int no_vcheck;
+    /* If non-NULL, stipulates the 'id' of the ENGINE to be loaded */
+    char *engine_id;
+    /*
+     * If non-zero, a successfully loaded ENGINE should be added to the
+     * internal ENGINE list. If 2, the add must succeed or the entire load
+     * should fail.
+     */
+    int list_add_value;
+    /* The symbol name for the version checking function */
+    const char *DYNAMIC_F1;
+    /* The symbol name for the "initialise ENGINE structure" function */
+    const char *DYNAMIC_F2;
+    /*
+     * Whether to never use 'dirs', use 'dirs' as a fallback, or only use
+     * 'dirs' for loading. Default is to use 'dirs' as a fallback.
+     */
+    int dir_load;
+    /* A stack of directories from which ENGINEs could be loaded */
+    STACK_OF(OPENSSL_STRING) *dirs;
+};
+
+/*
+ * This is the "ex_data" index we obtain and reserve for use with our context
+ * structure.
+ */
+static int dynamic_ex_data_idx = -1;
+
+static void int_free_str(char *s)
+{
+    OPENSSL_free(s);
+}
+
+/*
+ * Because our ex_data element may or may not get allocated depending on
+ * whether a "first-use" occurs before the ENGINE is freed, we have a memory
+ * leak problem to solve. We can't declare a "new" handler for the ex_data as
+ * we don't want a dynamic_data_ctx in *all* ENGINE structures of all types
+ * (this is a bug in the design of CRYPTO_EX_DATA). As such, we just declare
+ * a "free" handler and that will get called if an ENGINE is being destroyed
+ * and there was an ex_data element corresponding to our context type.
+ */
+static void dynamic_data_ctx_free_func(void *parent, void *ptr,
+                                       CRYPTO_EX_DATA *ad, int idx, long argl,
+                                       void *argp)
+{
+    if (ptr) {
+        dynamic_data_ctx *ctx = (dynamic_data_ctx *)ptr;
+        DSO_free(ctx->dynamic_dso);
+        OPENSSL_free(ctx->DYNAMIC_LIBNAME);
+        OPENSSL_free(ctx->engine_id);
+        sk_OPENSSL_STRING_pop_free(ctx->dirs, int_free_str);
+        OPENSSL_free(ctx);
+    }
+}
+
+/*
+ * Construct the per-ENGINE context. We create it blindly and then use a lock
+ * to check for a race - if so, all but one of the threads "racing" will have
+ * wasted their time. The alternative involves creating everything inside the
+ * lock which is far worse.
+ */
+static int dynamic_set_data_ctx(ENGINE *e, dynamic_data_ctx **ctx)
+{
+    dynamic_data_ctx *c = OPENSSL_zalloc(sizeof(*c));
+    int ret = 1;
+
+    if (c == NULL) {
+        ENGINEerr(ENGINE_F_DYNAMIC_SET_DATA_CTX, ERR_R_MALLOC_FAILURE);
+        return 0;
+    }
+    c->dirs = sk_OPENSSL_STRING_new_null();
+    if (c->dirs == NULL) {
+        ENGINEerr(ENGINE_F_DYNAMIC_SET_DATA_CTX, ERR_R_MALLOC_FAILURE);
+        OPENSSL_free(c);
+        return 0;
+    }
+    c->DYNAMIC_F1 = "v_check";
+    c->DYNAMIC_F2 = "bind_engine";
+    c->dir_load = 1;
+    CRYPTO_THREAD_write_lock(global_engine_lock);
+    if ((*ctx = (dynamic_data_ctx *)ENGINE_get_ex_data(e,
+                                                       dynamic_ex_data_idx))
+        == NULL) {
+        /* Good, we're the first */
+        ret = ENGINE_set_ex_data(e, dynamic_ex_data_idx, c);
+        if (ret) {
+            *ctx = c;
+            c = NULL;
+        }
+    }
+    CRYPTO_THREAD_unlock(global_engine_lock);
+    /*
+     * If we lost the race to set the context, c is non-NULL and *ctx is the
+     * context of the thread that won.
+     */
+    if (c)
+        sk_OPENSSL_STRING_free(c->dirs);
+    OPENSSL_free(c);
+    return ret;
+}
+
+/*
+ * This function retrieves the context structure from an ENGINE's "ex_data",
+ * or if it doesn't exist yet, sets it up.
+ */
+static dynamic_data_ctx *dynamic_get_data_ctx(ENGINE *e)
+{
+    dynamic_data_ctx *ctx;
+    if (dynamic_ex_data_idx < 0) {
+        /*
+         * Create and register the ENGINE ex_data, and associate our "free"
+         * function with it to ensure any allocated contexts get freed when
+         * an ENGINE goes underground.
+         */
+        int new_idx = ENGINE_get_ex_new_index(0, NULL, NULL, NULL,
+                                              dynamic_data_ctx_free_func);
+        if (new_idx == -1) {
+            ENGINEerr(ENGINE_F_DYNAMIC_GET_DATA_CTX, ENGINE_R_NO_INDEX);
+            return NULL;
+        }
+        CRYPTO_THREAD_write_lock(global_engine_lock);
+        /* Avoid a race by checking again inside this lock */
+        if (dynamic_ex_data_idx < 0) {
+            /* Good, someone didn't beat us to it */
+            dynamic_ex_data_idx = new_idx;
+            new_idx = -1;
+        }
+        CRYPTO_THREAD_unlock(global_engine_lock);
+        /*
+         * In theory we could "give back" the index here if (new_idx>-1), but
+         * it's not possible and wouldn't gain us much if it were.
+         */
+    }
+    ctx = (dynamic_data_ctx *)ENGINE_get_ex_data(e, dynamic_ex_data_idx);
+    /* Check if the context needs to be created */
+    if ((ctx == NULL) && !dynamic_set_data_ctx(e, &ctx))
+        /* "set_data" will set errors if necessary */
+        return NULL;
+    return ctx;
+}
+
+static ENGINE *engine_dynamic(void)
+{
+    ENGINE *ret = ENGINE_new();
+    if (ret == NULL)
+        return NULL;
+    if (!ENGINE_set_id(ret, engine_dynamic_id) ||
+        !ENGINE_set_name(ret, engine_dynamic_name) ||
+        !ENGINE_set_init_function(ret, dynamic_init) ||
+        !ENGINE_set_finish_function(ret, dynamic_finish) ||
+        !ENGINE_set_ctrl_function(ret, dynamic_ctrl) ||
+        !ENGINE_set_flags(ret, ENGINE_FLAGS_BY_ID_COPY) ||
+        !ENGINE_set_cmd_defns(ret, dynamic_cmd_defns)) {
+        ENGINE_free(ret);
+        return NULL;
+    }
+    return ret;
+}
+
+void engine_load_dynamic_int(void)
+{
+    ENGINE *toadd = engine_dynamic();
+    if (!toadd)
+        return;
+    ENGINE_add(toadd);
+    /*
+     * If the "add" worked, it gets a structural reference. So either way, we
+     * release our just-created reference.
+     */
+    ENGINE_free(toadd);
+    /*
+     * If the "add" didn't work, it was probably a conflict because it was
+     * already added (eg. someone calling ENGINE_load_blah then calling
+     * ENGINE_load_builtin_engines() perhaps).
+     */
+    ERR_clear_error();
+}
+
+static int dynamic_init(ENGINE *e)
+{
+    /*
+     * We always return failure - the "dynamic" engine itself can't be used
+     * for anything.
+     */
+    return 0;
+}
+
+static int dynamic_finish(ENGINE *e)
+{
+    /*
+     * This should never be called on account of "dynamic_init" always
+     * failing.
+     */
+    return 0;
+}
+
+static int dynamic_ctrl(ENGINE *e, int cmd, long i, void *p, void (*f) (void))
+{
+    dynamic_data_ctx *ctx = dynamic_get_data_ctx(e);
+    int initialised;
+
+    if (!ctx) {
+        ENGINEerr(ENGINE_F_DYNAMIC_CTRL, ENGINE_R_NOT_LOADED);
+        return 0;
+    }
+    initialised = ((ctx->dynamic_dso == NULL) ? 0 : 1);
+    /* All our control commands require the ENGINE to be uninitialised */
+    if (initialised) {
+        ENGINEerr(ENGINE_F_DYNAMIC_CTRL, ENGINE_R_ALREADY_LOADED);
+        return 0;
+    }
+    switch (cmd) {
+    case DYNAMIC_CMD_SO_PATH:
+        /* a NULL 'p' or a string of zero-length is the same thing */
+        if (p && (strlen((const char *)p) < 1))
+            p = NULL;
+        OPENSSL_free(ctx->DYNAMIC_LIBNAME);
+        if (p)
+            ctx->DYNAMIC_LIBNAME = OPENSSL_strdup(p);
+        else
+            ctx->DYNAMIC_LIBNAME = NULL;
+        return (ctx->DYNAMIC_LIBNAME ? 1 : 0);
+    case DYNAMIC_CMD_NO_VCHECK:
+        ctx->no_vcheck = ((i == 0) ? 0 : 1);
+        return 1;
+    case DYNAMIC_CMD_ID:
+        /* a NULL 'p' or a string of zero-length is the same thing */
+        if (p && (strlen((const char *)p) < 1))
+            p = NULL;
+        OPENSSL_free(ctx->engine_id);
+        if (p)
+            ctx->engine_id = OPENSSL_strdup(p);
+        else
+            ctx->engine_id = NULL;
+        return (ctx->engine_id ? 1 : 0);
+    case DYNAMIC_CMD_LIST_ADD:
+        if ((i < 0) || (i > 2)) {
+            ENGINEerr(ENGINE_F_DYNAMIC_CTRL, ENGINE_R_INVALID_ARGUMENT);
+            return 0;
+        }
+        ctx->list_add_value = (int)i;
+        return 1;
+    case DYNAMIC_CMD_LOAD:
+        return dynamic_load(e, ctx);
+    case DYNAMIC_CMD_DIR_LOAD:
+        if ((i < 0) || (i > 2)) {
+            ENGINEerr(ENGINE_F_DYNAMIC_CTRL, ENGINE_R_INVALID_ARGUMENT);
+            return 0;
+        }
+        ctx->dir_load = (int)i;
+        return 1;
+    case DYNAMIC_CMD_DIR_ADD:
+        /* a NULL 'p' or a string of zero-length is the same thing */
+        if (!p || (strlen((const char *)p) < 1)) {
+            ENGINEerr(ENGINE_F_DYNAMIC_CTRL, ENGINE_R_INVALID_ARGUMENT);
+            return 0;
+        }
+        {
+            char *tmp_str = OPENSSL_strdup(p);
+            if (tmp_str == NULL) {
+                ENGINEerr(ENGINE_F_DYNAMIC_CTRL, ERR_R_MALLOC_FAILURE);
+                return 0;
+            }
+            if (!sk_OPENSSL_STRING_push(ctx->dirs, tmp_str)) {
+                OPENSSL_free(tmp_str);
+                ENGINEerr(ENGINE_F_DYNAMIC_CTRL, ERR_R_MALLOC_FAILURE);
+                return 0;
+            }
+        }
+        return 1;
+    default:
+        break;
+    }
+    ENGINEerr(ENGINE_F_DYNAMIC_CTRL, ENGINE_R_CTRL_COMMAND_NOT_IMPLEMENTED);
+    return 0;
+}
+
+static int int_load(dynamic_data_ctx *ctx)
+{
+    int num, loop;
+    /* Unless told not to, try a direct load */
+    if ((ctx->dir_load != 2) && (DSO_load(ctx->dynamic_dso,
+                                          ctx->DYNAMIC_LIBNAME, NULL,
+                                          0)) != NULL)
+        return 1;
+    /* If we're not allowed to use 'dirs' or we have none, fail */
+    if (!ctx->dir_load || (num = sk_OPENSSL_STRING_num(ctx->dirs)) < 1)
+        return 0;
+    for (loop = 0; loop < num; loop++) {
+        const char *s = sk_OPENSSL_STRING_value(ctx->dirs, loop);
+        char *merge = DSO_merge(ctx->dynamic_dso, ctx->DYNAMIC_LIBNAME, s);
+        if (!merge)
+            return 0;
+        if (DSO_load(ctx->dynamic_dso, merge, NULL, 0)) {
+            /* Found what we're looking for */
+            OPENSSL_free(merge);
+            return 1;
+        }
+        OPENSSL_free(merge);
+    }
+    return 0;
+}
+
+static int dynamic_load(ENGINE *e, dynamic_data_ctx *ctx)
+{
+    ENGINE cpy;
+    dynamic_fns fns;
+
+    if (ctx->dynamic_dso == NULL)
+        ctx->dynamic_dso = DSO_new();
+    if (ctx->dynamic_dso == NULL)
+        return 0;
+    if (!ctx->DYNAMIC_LIBNAME) {
+        if (!ctx->engine_id)
+            return 0;
+        DSO_ctrl(ctx->dynamic_dso, DSO_CTRL_SET_FLAGS,
+                 DSO_FLAG_NAME_TRANSLATION_EXT_ONLY, NULL);
+        ctx->DYNAMIC_LIBNAME =
+            DSO_convert_filename(ctx->dynamic_dso, ctx->engine_id);
+    }
+    if (!int_load(ctx)) {
+        ENGINEerr(ENGINE_F_DYNAMIC_LOAD, ENGINE_R_DSO_NOT_FOUND);
+        DSO_free(ctx->dynamic_dso);
+        ctx->dynamic_dso = NULL;
+        return 0;
+    }
+    /* We have to find a bind function otherwise it'll always end badly */
+    if (!
+        (ctx->bind_engine =
+         (dynamic_bind_engine) DSO_bind_func(ctx->dynamic_dso,
+                                             ctx->DYNAMIC_F2))) {
+        ctx->bind_engine = NULL;
+        DSO_free(ctx->dynamic_dso);
+        ctx->dynamic_dso = NULL;
+        ENGINEerr(ENGINE_F_DYNAMIC_LOAD, ENGINE_R_DSO_FAILURE);
+        return 0;
+    }
+    /* Do we perform version checking? */
+    if (!ctx->no_vcheck) {
+        unsigned long vcheck_res = 0;
+        /*
+         * Now we try to find a version checking function and decide how to
+         * cope with failure if/when it fails.
+         */
+        ctx->v_check =
+            (dynamic_v_check_fn) DSO_bind_func(ctx->dynamic_dso,
+                                               ctx->DYNAMIC_F1);
+        if (ctx->v_check)
+            vcheck_res = ctx->v_check(OSSL_DYNAMIC_VERSION);
+        /*
+         * We fail if the version checker veto'd the load *or* if it is
+         * deferring to us (by returning its version) and we think it is too
+         * old.
+         */
+        if (vcheck_res < OSSL_DYNAMIC_OLDEST) {
+            /* Fail */
+            ctx->bind_engine = NULL;
+            ctx->v_check = NULL;
+            DSO_free(ctx->dynamic_dso);
+            ctx->dynamic_dso = NULL;
+            ENGINEerr(ENGINE_F_DYNAMIC_LOAD,
+                      ENGINE_R_VERSION_INCOMPATIBILITY);
+            return 0;
+        }
+    }
+    /*
+     * First binary copy the ENGINE structure so that we can roll back if the
+     * hand-over fails
+     */
+    memcpy(&cpy, e, sizeof(ENGINE));
+    /*
+     * Provide the ERR, "ex_data", memory, and locking callbacks so the
+     * loaded library uses our state rather than its own. FIXME: As noted in
+     * engine.h, much of this would be simplified if each area of code
+     * provided its own "summary" structure of all related callbacks. It
+     * would also increase opaqueness.
+     */
+    fns.static_state = ENGINE_get_static_state();
+    CRYPTO_get_mem_functions(&fns.mem_fns.malloc_fn, &fns.mem_fns.realloc_fn,
+                             &fns.mem_fns.free_fn);
+    /*
+     * Now that we've loaded the dynamic engine, make sure no "dynamic"
+     * ENGINE elements will show through.
+     */
+    engine_set_all_null(e);
+
+    /* Try to bind the ENGINE onto our own ENGINE structure */
+    if (!ctx->bind_engine(e, ctx->engine_id, &fns)) {
+        ctx->bind_engine = NULL;
+        ctx->v_check = NULL;
+        DSO_free(ctx->dynamic_dso);
+        ctx->dynamic_dso = NULL;
+        ENGINEerr(ENGINE_F_DYNAMIC_LOAD, ENGINE_R_INIT_FAILED);
+        /* Copy the original ENGINE structure back */
+        memcpy(e, &cpy, sizeof(ENGINE));
+        return 0;
+    }
+    /* Do we try to add this ENGINE to the internal list too? */
+    if (ctx->list_add_value > 0) {
+        if (!ENGINE_add(e)) {
+            /* Do we tolerate this or fail? */
+            if (ctx->list_add_value > 1) {
+                /*
+                 * Fail - NB: By this time, it's too late to rollback, and
+                 * trying to do so allows the bind_engine() code to have
+                 * created leaks. We just have to fail where we are, after
+                 * the ENGINE has changed.
+                 */
+                ENGINEerr(ENGINE_F_DYNAMIC_LOAD,
+                          ENGINE_R_CONFLICTING_ENGINE_ID);
+                return 0;
+            }
+            /* Tolerate */
+            ERR_clear_error();
+        }
+    }
+    return 1;
+}
diff --git a/crypto/engine/eng_err.c b/crypto/engine/eng_err.c
new file mode 100644
index 000000000..5e9d16f3c
--- /dev/null
+++ b/crypto/engine/eng_err.c
@@ -0,0 +1,123 @@
+/*
+ * Generated by util/mkerr.pl DO NOT EDIT
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include 
+
+/* BEGIN ERROR CODES */
+#ifndef OPENSSL_NO_ERR
+
+# define ERR_FUNC(func) ERR_PACK(ERR_LIB_ENGINE,func,0)
+# define ERR_REASON(reason) ERR_PACK(ERR_LIB_ENGINE,0,reason)
+
+static ERR_STRING_DATA ENGINE_str_functs[] = {
+    {ERR_FUNC(ENGINE_F_DYNAMIC_CTRL), "dynamic_ctrl"},
+    {ERR_FUNC(ENGINE_F_DYNAMIC_GET_DATA_CTX), "dynamic_get_data_ctx"},
+    {ERR_FUNC(ENGINE_F_DYNAMIC_LOAD), "dynamic_load"},
+    {ERR_FUNC(ENGINE_F_DYNAMIC_SET_DATA_CTX), "dynamic_set_data_ctx"},
+    {ERR_FUNC(ENGINE_F_ENGINE_ADD), "ENGINE_add"},
+    {ERR_FUNC(ENGINE_F_ENGINE_BY_ID), "ENGINE_by_id"},
+    {ERR_FUNC(ENGINE_F_ENGINE_CMD_IS_EXECUTABLE), "ENGINE_cmd_is_executable"},
+    {ERR_FUNC(ENGINE_F_ENGINE_CTRL), "ENGINE_ctrl"},
+    {ERR_FUNC(ENGINE_F_ENGINE_CTRL_CMD), "ENGINE_ctrl_cmd"},
+    {ERR_FUNC(ENGINE_F_ENGINE_CTRL_CMD_STRING), "ENGINE_ctrl_cmd_string"},
+    {ERR_FUNC(ENGINE_F_ENGINE_FINISH), "ENGINE_finish"},
+    {ERR_FUNC(ENGINE_F_ENGINE_GET_CIPHER), "ENGINE_get_cipher"},
+    {ERR_FUNC(ENGINE_F_ENGINE_GET_DIGEST), "ENGINE_get_digest"},
+    {ERR_FUNC(ENGINE_F_ENGINE_GET_FIRST), "ENGINE_get_first"},
+    {ERR_FUNC(ENGINE_F_ENGINE_GET_LAST), "ENGINE_get_last"},
+    {ERR_FUNC(ENGINE_F_ENGINE_GET_NEXT), "ENGINE_get_next"},
+    {ERR_FUNC(ENGINE_F_ENGINE_GET_PKEY_ASN1_METH),
+     "ENGINE_get_pkey_asn1_meth"},
+    {ERR_FUNC(ENGINE_F_ENGINE_GET_PKEY_METH), "ENGINE_get_pkey_meth"},
+    {ERR_FUNC(ENGINE_F_ENGINE_GET_PREV), "ENGINE_get_prev"},
+    {ERR_FUNC(ENGINE_F_ENGINE_INIT), "ENGINE_init"},
+    {ERR_FUNC(ENGINE_F_ENGINE_LIST_ADD), "engine_list_add"},
+    {ERR_FUNC(ENGINE_F_ENGINE_LIST_REMOVE), "engine_list_remove"},
+    {ERR_FUNC(ENGINE_F_ENGINE_LOAD_PRIVATE_KEY), "ENGINE_load_private_key"},
+    {ERR_FUNC(ENGINE_F_ENGINE_LOAD_PUBLIC_KEY), "ENGINE_load_public_key"},
+    {ERR_FUNC(ENGINE_F_ENGINE_LOAD_SSL_CLIENT_CERT),
+     "ENGINE_load_ssl_client_cert"},
+    {ERR_FUNC(ENGINE_F_ENGINE_NEW), "ENGINE_new"},
+    {ERR_FUNC(ENGINE_F_ENGINE_PKEY_ASN1_FIND_STR),
+     "ENGINE_pkey_asn1_find_str"},
+    {ERR_FUNC(ENGINE_F_ENGINE_REMOVE), "ENGINE_remove"},
+    {ERR_FUNC(ENGINE_F_ENGINE_SET_DEFAULT_STRING),
+     "ENGINE_set_default_string"},
+    {ERR_FUNC(ENGINE_F_ENGINE_SET_ID), "ENGINE_set_id"},
+    {ERR_FUNC(ENGINE_F_ENGINE_SET_NAME), "ENGINE_set_name"},
+    {ERR_FUNC(ENGINE_F_ENGINE_TABLE_REGISTER), "engine_table_register"},
+    {ERR_FUNC(ENGINE_F_ENGINE_UNLOCKED_FINISH), "engine_unlocked_finish"},
+    {ERR_FUNC(ENGINE_F_ENGINE_UP_REF), "ENGINE_up_ref"},
+    {ERR_FUNC(ENGINE_F_INT_CTRL_HELPER), "int_ctrl_helper"},
+    {ERR_FUNC(ENGINE_F_INT_ENGINE_CONFIGURE), "int_engine_configure"},
+    {ERR_FUNC(ENGINE_F_INT_ENGINE_MODULE_INIT), "int_engine_module_init"},
+    {0, NULL}
+};
+
+static ERR_STRING_DATA ENGINE_str_reasons[] = {
+    {ERR_REASON(ENGINE_R_ALREADY_LOADED), "already loaded"},
+    {ERR_REASON(ENGINE_R_ARGUMENT_IS_NOT_A_NUMBER),
+     "argument is not a number"},
+    {ERR_REASON(ENGINE_R_CMD_NOT_EXECUTABLE), "cmd not executable"},
+    {ERR_REASON(ENGINE_R_COMMAND_TAKES_INPUT), "command takes input"},
+    {ERR_REASON(ENGINE_R_COMMAND_TAKES_NO_INPUT), "command takes no input"},
+    {ERR_REASON(ENGINE_R_CONFLICTING_ENGINE_ID), "conflicting engine id"},
+    {ERR_REASON(ENGINE_R_CTRL_COMMAND_NOT_IMPLEMENTED),
+     "ctrl command not implemented"},
+    {ERR_REASON(ENGINE_R_DSO_FAILURE), "DSO failure"},
+    {ERR_REASON(ENGINE_R_DSO_NOT_FOUND), "dso not found"},
+    {ERR_REASON(ENGINE_R_ENGINES_SECTION_ERROR), "engines section error"},
+    {ERR_REASON(ENGINE_R_ENGINE_CONFIGURATION_ERROR),
+     "engine configuration error"},
+    {ERR_REASON(ENGINE_R_ENGINE_IS_NOT_IN_LIST), "engine is not in the list"},
+    {ERR_REASON(ENGINE_R_ENGINE_SECTION_ERROR), "engine section error"},
+    {ERR_REASON(ENGINE_R_FAILED_LOADING_PRIVATE_KEY),
+     "failed loading private key"},
+    {ERR_REASON(ENGINE_R_FAILED_LOADING_PUBLIC_KEY),
+     "failed loading public key"},
+    {ERR_REASON(ENGINE_R_FINISH_FAILED), "finish failed"},
+    {ERR_REASON(ENGINE_R_ID_OR_NAME_MISSING), "'id' or 'name' missing"},
+    {ERR_REASON(ENGINE_R_INIT_FAILED), "init failed"},
+    {ERR_REASON(ENGINE_R_INTERNAL_LIST_ERROR), "internal list error"},
+    {ERR_REASON(ENGINE_R_INVALID_ARGUMENT), "invalid argument"},
+    {ERR_REASON(ENGINE_R_INVALID_CMD_NAME), "invalid cmd name"},
+    {ERR_REASON(ENGINE_R_INVALID_CMD_NUMBER), "invalid cmd number"},
+    {ERR_REASON(ENGINE_R_INVALID_INIT_VALUE), "invalid init value"},
+    {ERR_REASON(ENGINE_R_INVALID_STRING), "invalid string"},
+    {ERR_REASON(ENGINE_R_NOT_INITIALISED), "not initialised"},
+    {ERR_REASON(ENGINE_R_NOT_LOADED), "not loaded"},
+    {ERR_REASON(ENGINE_R_NO_CONTROL_FUNCTION), "no control function"},
+    {ERR_REASON(ENGINE_R_NO_INDEX), "no index"},
+    {ERR_REASON(ENGINE_R_NO_LOAD_FUNCTION), "no load function"},
+    {ERR_REASON(ENGINE_R_NO_REFERENCE), "no reference"},
+    {ERR_REASON(ENGINE_R_NO_SUCH_ENGINE), "no such engine"},
+    {ERR_REASON(ENGINE_R_UNIMPLEMENTED_CIPHER), "unimplemented cipher"},
+    {ERR_REASON(ENGINE_R_UNIMPLEMENTED_DIGEST), "unimplemented digest"},
+    {ERR_REASON(ENGINE_R_UNIMPLEMENTED_PUBLIC_KEY_METHOD),
+     "unimplemented public key method"},
+    {ERR_REASON(ENGINE_R_VERSION_INCOMPATIBILITY), "version incompatibility"},
+    {0, NULL}
+};
+
+#endif
+
+int ERR_load_ENGINE_strings(void)
+{
+#ifndef OPENSSL_NO_ERR
+
+    if (ERR_func_error_string(ENGINE_str_functs[0].error) == NULL) {
+        ERR_load_strings(0, ENGINE_str_functs);
+        ERR_load_strings(0, ENGINE_str_reasons);
+    }
+#endif
+    return 1;
+}
diff --git a/crypto/engine/eng_fat.c b/crypto/engine/eng_fat.c
new file mode 100644
index 000000000..631aa3961
--- /dev/null
+++ b/crypto/engine/eng_fat.c
@@ -0,0 +1,127 @@
+/*
+ * Copyright 2001-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/* ====================================================================
+ * Copyright 2002 Sun Microsystems, Inc. ALL RIGHTS RESERVED.
+ * ECDH support in OpenSSL originally developed by
+ * SUN MICROSYSTEMS, INC., and contributed to the OpenSSL project.
+ */
+
+#include "eng_int.h"
+#include 
+
+int ENGINE_set_default(ENGINE *e, unsigned int flags)
+{
+    if ((flags & ENGINE_METHOD_CIPHERS) && !ENGINE_set_default_ciphers(e))
+        return 0;
+    if ((flags & ENGINE_METHOD_DIGESTS) && !ENGINE_set_default_digests(e))
+        return 0;
+#ifndef OPENSSL_NO_RSA
+    if ((flags & ENGINE_METHOD_RSA) && !ENGINE_set_default_RSA(e))
+        return 0;
+#endif
+#ifndef OPENSSL_NO_DSA
+    if ((flags & ENGINE_METHOD_DSA) && !ENGINE_set_default_DSA(e))
+        return 0;
+#endif
+#ifndef OPENSSL_NO_DH
+    if ((flags & ENGINE_METHOD_DH) && !ENGINE_set_default_DH(e))
+        return 0;
+#endif
+#ifndef OPENSSL_NO_EC
+    if ((flags & ENGINE_METHOD_EC) && !ENGINE_set_default_EC(e))
+        return 0;
+#endif
+    if ((flags & ENGINE_METHOD_RAND) && !ENGINE_set_default_RAND(e))
+        return 0;
+    if ((flags & ENGINE_METHOD_PKEY_METHS)
+        && !ENGINE_set_default_pkey_meths(e))
+        return 0;
+    if ((flags & ENGINE_METHOD_PKEY_ASN1_METHS)
+        && !ENGINE_set_default_pkey_asn1_meths(e))
+        return 0;
+    return 1;
+}
+
+/* Set default algorithms using a string */
+
+static int int_def_cb(const char *alg, int len, void *arg)
+{
+    unsigned int *pflags = arg;
+    if (alg == NULL)
+        return 0;
+    if (strncmp(alg, "ALL", len) == 0)
+        *pflags |= ENGINE_METHOD_ALL;
+    else if (strncmp(alg, "RSA", len) == 0)
+        *pflags |= ENGINE_METHOD_RSA;
+    else if (strncmp(alg, "DSA", len) == 0)
+        *pflags |= ENGINE_METHOD_DSA;
+    else if (strncmp(alg, "DH", len) == 0)
+        *pflags |= ENGINE_METHOD_DH;
+    else if (strncmp(alg, "EC", len) == 0)
+        *pflags |= ENGINE_METHOD_EC;
+    else if (strncmp(alg, "RAND", len) == 0)
+        *pflags |= ENGINE_METHOD_RAND;
+    else if (strncmp(alg, "CIPHERS", len) == 0)
+        *pflags |= ENGINE_METHOD_CIPHERS;
+    else if (strncmp(alg, "DIGESTS", len) == 0)
+        *pflags |= ENGINE_METHOD_DIGESTS;
+    else if (strncmp(alg, "PKEY", len) == 0)
+        *pflags |= ENGINE_METHOD_PKEY_METHS | ENGINE_METHOD_PKEY_ASN1_METHS;
+    else if (strncmp(alg, "PKEY_CRYPTO", len) == 0)
+        *pflags |= ENGINE_METHOD_PKEY_METHS;
+    else if (strncmp(alg, "PKEY_ASN1", len) == 0)
+        *pflags |= ENGINE_METHOD_PKEY_ASN1_METHS;
+    else
+        return 0;
+    return 1;
+}
+
+int ENGINE_set_default_string(ENGINE *e, const char *def_list)
+{
+    unsigned int flags = 0;
+    if (!CONF_parse_list(def_list, ',', 1, int_def_cb, &flags)) {
+        ENGINEerr(ENGINE_F_ENGINE_SET_DEFAULT_STRING,
+                  ENGINE_R_INVALID_STRING);
+        ERR_add_error_data(2, "str=", def_list);
+        return 0;
+    }
+    return ENGINE_set_default(e, flags);
+}
+
+int ENGINE_register_complete(ENGINE *e)
+{
+    ENGINE_register_ciphers(e);
+    ENGINE_register_digests(e);
+#ifndef OPENSSL_NO_RSA
+    ENGINE_register_RSA(e);
+#endif
+#ifndef OPENSSL_NO_DSA
+    ENGINE_register_DSA(e);
+#endif
+#ifndef OPENSSL_NO_DH
+    ENGINE_register_DH(e);
+#endif
+#ifndef OPENSSL_NO_EC
+    ENGINE_register_EC(e);
+#endif
+    ENGINE_register_RAND(e);
+    ENGINE_register_pkey_meths(e);
+    return 1;
+}
+
+int ENGINE_register_all_complete(void)
+{
+    ENGINE *e;
+
+    for (e = ENGINE_get_first(); e; e = ENGINE_get_next(e))
+        if (!(e->flags & ENGINE_FLAGS_NO_REGISTER_ALL))
+            ENGINE_register_complete(e);
+    return 1;
+}
diff --git a/crypto/engine/eng_init.c b/crypto/engine/eng_init.c
new file mode 100644
index 000000000..8be7c6fc8
--- /dev/null
+++ b/crypto/engine/eng_init.c
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2001-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "eng_int.h"
+
+/*
+ * Initialise a engine type for use (or up its functional reference count if
+ * it's already in use). This version is only used internally.
+ */
+int engine_unlocked_init(ENGINE *e)
+{
+    int to_return = 1;
+
+    if ((e->funct_ref == 0) && e->init)
+        /*
+         * This is the first functional reference and the engine requires
+         * initialisation so we do it now.
+         */
+        to_return = e->init(e);
+    if (to_return) {
+        /*
+         * OK, we return a functional reference which is also a structural
+         * reference.
+         */
+        e->struct_ref++;
+        e->funct_ref++;
+        engine_ref_debug(e, 0, 1);
+        engine_ref_debug(e, 1, 1);
+    }
+    return to_return;
+}
+
+/*
+ * Free a functional reference to a engine type. This version is only used
+ * internally.
+ */
+int engine_unlocked_finish(ENGINE *e, int unlock_for_handlers)
+{
+    int to_return = 1;
+
+    /*
+     * Reduce the functional reference count here so if it's the terminating
+     * case, we can release the lock safely and call the finish() handler
+     * without risk of a race. We get a race if we leave the count until
+     * after and something else is calling "finish" at the same time -
+     * there's a chance that both threads will together take the count from 2
+     * to 0 without either calling finish().
+     */
+    e->funct_ref--;
+    engine_ref_debug(e, 1, -1);
+    if ((e->funct_ref == 0) && e->finish) {
+        if (unlock_for_handlers)
+            CRYPTO_THREAD_unlock(global_engine_lock);
+        to_return = e->finish(e);
+        if (unlock_for_handlers)
+            CRYPTO_THREAD_write_lock(global_engine_lock);
+        if (!to_return)
+            return 0;
+    }
+    REF_ASSERT_ISNT(e->funct_ref < 0);
+    /* Release the structural reference too */
+    if (!engine_free_util(e, 0)) {
+        ENGINEerr(ENGINE_F_ENGINE_UNLOCKED_FINISH, ENGINE_R_FINISH_FAILED);
+        return 0;
+    }
+    return to_return;
+}
+
+/* The API (locked) version of "init" */
+int ENGINE_init(ENGINE *e)
+{
+    int ret;
+    if (e == NULL) {
+        ENGINEerr(ENGINE_F_ENGINE_INIT, ERR_R_PASSED_NULL_PARAMETER);
+        return 0;
+    }
+    if (!RUN_ONCE(&engine_lock_init, do_engine_lock_init)) {
+        ENGINEerr(ENGINE_F_ENGINE_INIT, ERR_R_MALLOC_FAILURE);
+        return 0;
+    }
+    CRYPTO_THREAD_write_lock(global_engine_lock);
+    ret = engine_unlocked_init(e);
+    CRYPTO_THREAD_unlock(global_engine_lock);
+    return ret;
+}
+
+/* The API (locked) version of "finish" */
+int ENGINE_finish(ENGINE *e)
+{
+    int to_return = 1;
+
+    if (e == NULL)
+        return 1;
+    CRYPTO_THREAD_write_lock(global_engine_lock);
+    to_return = engine_unlocked_finish(e, 1);
+    CRYPTO_THREAD_unlock(global_engine_lock);
+    if (!to_return) {
+        ENGINEerr(ENGINE_F_ENGINE_FINISH, ENGINE_R_FINISH_FAILED);
+        return 0;
+    }
+    return to_return;
+}
diff --git a/crypto/engine/eng_int.h b/crypto/engine/eng_int.h
new file mode 100644
index 000000000..c604faddd
--- /dev/null
+++ b/crypto/engine/eng_int.h
@@ -0,0 +1,183 @@
+/*
+ * Copyright 2001-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/* ====================================================================
+ * Copyright 2002 Sun Microsystems, Inc. ALL RIGHTS RESERVED.
+ * ECDH support in OpenSSL originally developed by
+ * SUN MICROSYSTEMS, INC., and contributed to the OpenSSL project.
+ */
+
+#ifndef HEADER_ENGINE_INT_H
+# define HEADER_ENGINE_INT_H
+
+# include "internal/cryptlib.h"
+# include 
+# include 
+
+#ifdef  __cplusplus
+extern "C" {
+#endif
+
+extern CRYPTO_RWLOCK *global_engine_lock;
+
+/*
+ * If we compile with this symbol defined, then both reference counts in the
+ * ENGINE structure will be monitored with a line of output on stderr for
+ * each change. This prints the engine's pointer address (truncated to
+ * unsigned int), "struct" or "funct" to indicate the reference type, the
+ * before and after reference count, and the file:line-number pair. The
+ * "engine_ref_debug" statements must come *after* the change.
+ */
+# ifdef ENGINE_REF_COUNT_DEBUG
+
+#  define engine_ref_debug(e, isfunct, diff) \
+        fprintf(stderr, "engine: %08x %s from %d to %d (%s:%d)\n", \
+                (unsigned int)(e), (isfunct ? "funct" : "struct"), \
+                ((isfunct) ? ((e)->funct_ref - (diff)) : ((e)->struct_ref - (diff))), \
+                ((isfunct) ? (e)->funct_ref : (e)->struct_ref), \
+                (OPENSSL_FILE), (OPENSSL_LINE))
+
+# else
+
+#  define engine_ref_debug(e, isfunct, diff)
+
+# endif
+
+/*
+ * Any code that will need cleanup operations should use these functions to
+ * register callbacks. engine_cleanup_int() will call all registered
+ * callbacks in order. NB: both the "add" functions assume the engine lock to
+ * already be held (in "write" mode).
+ */
+typedef void (ENGINE_CLEANUP_CB) (void);
+typedef struct st_engine_cleanup_item {
+    ENGINE_CLEANUP_CB *cb;
+} ENGINE_CLEANUP_ITEM;
+DEFINE_STACK_OF(ENGINE_CLEANUP_ITEM)
+void engine_cleanup_add_first(ENGINE_CLEANUP_CB *cb);
+void engine_cleanup_add_last(ENGINE_CLEANUP_CB *cb);
+
+/* We need stacks of ENGINEs for use in eng_table.c */
+DEFINE_STACK_OF(ENGINE)
+
+/*
+ * If this symbol is defined then engine_table_select(), the function that is
+ * used by RSA, DSA (etc) code to select registered ENGINEs, cache defaults
+ * and functional references (etc), will display debugging summaries to
+ * stderr.
+ */
+/* #define ENGINE_TABLE_DEBUG */
+
+/*
+ * This represents an implementation table. Dependent code should instantiate
+ * it as a (ENGINE_TABLE *) pointer value set initially to NULL.
+ */
+typedef struct st_engine_table ENGINE_TABLE;
+int engine_table_register(ENGINE_TABLE **table, ENGINE_CLEANUP_CB *cleanup,
+                          ENGINE *e, const int *nids, int num_nids,
+                          int setdefault);
+void engine_table_unregister(ENGINE_TABLE **table, ENGINE *e);
+void engine_table_cleanup(ENGINE_TABLE **table);
+# ifndef ENGINE_TABLE_DEBUG
+ENGINE *engine_table_select(ENGINE_TABLE **table, int nid);
+# else
+ENGINE *engine_table_select_tmp(ENGINE_TABLE **table, int nid, const char *f,
+                                int l);
+#  define engine_table_select(t,n) engine_table_select_tmp(t,n,OPENSSL_FILE,OPENSSL_LINE)
+# endif
+typedef void (engine_table_doall_cb) (int nid, STACK_OF(ENGINE) *sk,
+                                      ENGINE *def, void *arg);
+void engine_table_doall(ENGINE_TABLE *table, engine_table_doall_cb *cb,
+                        void *arg);
+
+/*
+ * Internal versions of API functions that have control over locking. These
+ * are used between C files when functionality needs to be shared but the
+ * caller may already be controlling of the engine lock.
+ */
+int engine_unlocked_init(ENGINE *e);
+int engine_unlocked_finish(ENGINE *e, int unlock_for_handlers);
+int engine_free_util(ENGINE *e, int locked);
+
+/*
+ * This function will reset all "set"able values in an ENGINE to NULL. This
+ * won't touch reference counts or ex_data, but is equivalent to calling all
+ * the ENGINE_set_***() functions with a NULL value.
+ */
+void engine_set_all_null(ENGINE *e);
+
+/*
+ * NB: Bitwise OR-able values for the "flags" variable in ENGINE are now
+ * exposed in engine.h.
+ */
+
+/* Free up dynamically allocated public key methods associated with ENGINE */
+
+void engine_pkey_meths_free(ENGINE *e);
+void engine_pkey_asn1_meths_free(ENGINE *e);
+
+/* Once initialisation function */
+extern CRYPTO_ONCE engine_lock_init;
+DECLARE_RUN_ONCE(do_engine_lock_init)
+
+/*
+ * This is a structure for storing implementations of various crypto
+ * algorithms and functions.
+ */
+struct engine_st {
+    const char *id;
+    const char *name;
+    const RSA_METHOD *rsa_meth;
+    const DSA_METHOD *dsa_meth;
+    const DH_METHOD *dh_meth;
+    const EC_KEY_METHOD *ec_meth;
+    const RAND_METHOD *rand_meth;
+    /* Cipher handling is via this callback */
+    ENGINE_CIPHERS_PTR ciphers;
+    /* Digest handling is via this callback */
+    ENGINE_DIGESTS_PTR digests;
+    /* Public key handling via this callback */
+    ENGINE_PKEY_METHS_PTR pkey_meths;
+    /* ASN1 public key handling via this callback */
+    ENGINE_PKEY_ASN1_METHS_PTR pkey_asn1_meths;
+    ENGINE_GEN_INT_FUNC_PTR destroy;
+    ENGINE_GEN_INT_FUNC_PTR init;
+    ENGINE_GEN_INT_FUNC_PTR finish;
+    ENGINE_CTRL_FUNC_PTR ctrl;
+    ENGINE_LOAD_KEY_PTR load_privkey;
+    ENGINE_LOAD_KEY_PTR load_pubkey;
+    ENGINE_SSL_CLIENT_CERT_PTR load_ssl_client_cert;
+    const ENGINE_CMD_DEFN *cmd_defns;
+    int flags;
+    /* reference count on the structure itself */
+    int struct_ref;
+    /*
+     * reference count on usability of the engine type. NB: This controls the
+     * loading and initialisation of any functionality required by this
+     * engine, whereas the previous count is simply to cope with
+     * (de)allocation of this structure. Hence, running_ref <= struct_ref at
+     * all times.
+     */
+    int funct_ref;
+    /* A place to store per-ENGINE data */
+    CRYPTO_EX_DATA ex_data;
+    /* Used to maintain the linked-list of engines. */
+    struct engine_st *prev;
+    struct engine_st *next;
+};
+
+typedef struct st_engine_pile ENGINE_PILE;
+
+DEFINE_LHASH_OF(ENGINE_PILE);
+
+#ifdef  __cplusplus
+}
+#endif
+
+#endif                          /* HEADER_ENGINE_INT_H */
diff --git a/crypto/engine/eng_lib.c b/crypto/engine/eng_lib.c
new file mode 100644
index 000000000..28de21d73
--- /dev/null
+++ b/crypto/engine/eng_lib.c
@@ -0,0 +1,295 @@
+/*
+ * Copyright 2001-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "eng_int.h"
+#include 
+
+CRYPTO_RWLOCK *global_engine_lock;
+
+CRYPTO_ONCE engine_lock_init = CRYPTO_ONCE_STATIC_INIT;
+
+/* The "new"/"free" stuff first */
+
+DEFINE_RUN_ONCE(do_engine_lock_init)
+{
+    OPENSSL_init_crypto(0, NULL);
+    global_engine_lock = CRYPTO_THREAD_lock_new();
+    return global_engine_lock != NULL;
+}
+
+ENGINE *ENGINE_new(void)
+{
+    ENGINE *ret;
+
+    if (!RUN_ONCE(&engine_lock_init, do_engine_lock_init)
+        || (ret = OPENSSL_zalloc(sizeof(*ret))) == NULL) {
+        ENGINEerr(ENGINE_F_ENGINE_NEW, ERR_R_MALLOC_FAILURE);
+        return NULL;
+    }
+    ret->struct_ref = 1;
+    engine_ref_debug(ret, 0, 1);
+    if (!CRYPTO_new_ex_data(CRYPTO_EX_INDEX_ENGINE, ret, &ret->ex_data)) {
+        OPENSSL_free(ret);
+        return NULL;
+    }
+    return ret;
+}
+
+/*
+ * Placed here (close proximity to ENGINE_new) so that modifications to the
+ * elements of the ENGINE structure are more likely to be caught and changed
+ * here.
+ */
+void engine_set_all_null(ENGINE *e)
+{
+    e->id = NULL;
+    e->name = NULL;
+    e->rsa_meth = NULL;
+    e->dsa_meth = NULL;
+    e->dh_meth = NULL;
+    e->rand_meth = NULL;
+    e->ciphers = NULL;
+    e->digests = NULL;
+    e->destroy = NULL;
+    e->init = NULL;
+    e->finish = NULL;
+    e->ctrl = NULL;
+    e->load_privkey = NULL;
+    e->load_pubkey = NULL;
+    e->cmd_defns = NULL;
+    e->flags = 0;
+}
+
+int engine_free_util(ENGINE *e, int locked)
+{
+    int i;
+
+    if (e == NULL)
+        return 1;
+    if (locked)
+        CRYPTO_atomic_add(&e->struct_ref, -1, &i, global_engine_lock);
+    else
+        i = --e->struct_ref;
+    engine_ref_debug(e, 0, -1)
+    if (i > 0)
+        return 1;
+    REF_ASSERT_ISNT(i < 0);
+    /* Free up any dynamically allocated public key methods */
+    engine_pkey_meths_free(e);
+    engine_pkey_asn1_meths_free(e);
+    /*
+     * Give the ENGINE a chance to do any structural cleanup corresponding to
+     * allocation it did in its constructor (eg. unload error strings)
+     */
+    if (e->destroy)
+        e->destroy(e);
+    CRYPTO_free_ex_data(CRYPTO_EX_INDEX_ENGINE, e, &e->ex_data);
+    OPENSSL_free(e);
+    return 1;
+}
+
+int ENGINE_free(ENGINE *e)
+{
+    return engine_free_util(e, 1);
+}
+
+/* Cleanup stuff */
+
+/*
+ * engine_cleanup_int() is coded such that anything that does work that will
+ * need cleanup can register a "cleanup" callback here. That way we don't get
+ * linker bloat by referring to all *possible* cleanups, but any linker bloat
+ * into code "X" will cause X's cleanup function to end up here.
+ */
+static STACK_OF(ENGINE_CLEANUP_ITEM) *cleanup_stack = NULL;
+static int int_cleanup_check(int create)
+{
+    if (cleanup_stack)
+        return 1;
+    if (!create)
+        return 0;
+    cleanup_stack = sk_ENGINE_CLEANUP_ITEM_new_null();
+    return (cleanup_stack ? 1 : 0);
+}
+
+static ENGINE_CLEANUP_ITEM *int_cleanup_item(ENGINE_CLEANUP_CB *cb)
+{
+    ENGINE_CLEANUP_ITEM *item = OPENSSL_malloc(sizeof(*item));
+    if (item == NULL)
+        return NULL;
+    item->cb = cb;
+    return item;
+}
+
+void engine_cleanup_add_first(ENGINE_CLEANUP_CB *cb)
+{
+    ENGINE_CLEANUP_ITEM *item;
+    if (!int_cleanup_check(1))
+        return;
+    item = int_cleanup_item(cb);
+    if (item)
+        sk_ENGINE_CLEANUP_ITEM_insert(cleanup_stack, item, 0);
+}
+
+void engine_cleanup_add_last(ENGINE_CLEANUP_CB *cb)
+{
+    ENGINE_CLEANUP_ITEM *item;
+    if (!int_cleanup_check(1))
+        return;
+    item = int_cleanup_item(cb);
+    if (item)
+        sk_ENGINE_CLEANUP_ITEM_push(cleanup_stack, item);
+}
+
+/* The API function that performs all cleanup */
+static void engine_cleanup_cb_free(ENGINE_CLEANUP_ITEM *item)
+{
+    (*(item->cb)) ();
+    OPENSSL_free(item);
+}
+
+void engine_cleanup_int(void)
+{
+    if (int_cleanup_check(0)) {
+        sk_ENGINE_CLEANUP_ITEM_pop_free(cleanup_stack,
+                                        engine_cleanup_cb_free);
+        cleanup_stack = NULL;
+    }
+    /*
+     * FIXME: This should be handled (somehow) through RAND, eg. by it
+     * registering a cleanup callback.
+     */
+    RAND_set_rand_method(NULL);
+    CRYPTO_THREAD_lock_free(global_engine_lock);
+}
+
+/* Now the "ex_data" support */
+
+int ENGINE_set_ex_data(ENGINE *e, int idx, void *arg)
+{
+    return (CRYPTO_set_ex_data(&e->ex_data, idx, arg));
+}
+
+void *ENGINE_get_ex_data(const ENGINE *e, int idx)
+{
+    return (CRYPTO_get_ex_data(&e->ex_data, idx));
+}
+
+/*
+ * Functions to get/set an ENGINE's elements - mainly to avoid exposing the
+ * ENGINE structure itself.
+ */
+
+int ENGINE_set_id(ENGINE *e, const char *id)
+{
+    if (id == NULL) {
+        ENGINEerr(ENGINE_F_ENGINE_SET_ID, ERR_R_PASSED_NULL_PARAMETER);
+        return 0;
+    }
+    e->id = id;
+    return 1;
+}
+
+int ENGINE_set_name(ENGINE *e, const char *name)
+{
+    if (name == NULL) {
+        ENGINEerr(ENGINE_F_ENGINE_SET_NAME, ERR_R_PASSED_NULL_PARAMETER);
+        return 0;
+    }
+    e->name = name;
+    return 1;
+}
+
+int ENGINE_set_destroy_function(ENGINE *e, ENGINE_GEN_INT_FUNC_PTR destroy_f)
+{
+    e->destroy = destroy_f;
+    return 1;
+}
+
+int ENGINE_set_init_function(ENGINE *e, ENGINE_GEN_INT_FUNC_PTR init_f)
+{
+    e->init = init_f;
+    return 1;
+}
+
+int ENGINE_set_finish_function(ENGINE *e, ENGINE_GEN_INT_FUNC_PTR finish_f)
+{
+    e->finish = finish_f;
+    return 1;
+}
+
+int ENGINE_set_ctrl_function(ENGINE *e, ENGINE_CTRL_FUNC_PTR ctrl_f)
+{
+    e->ctrl = ctrl_f;
+    return 1;
+}
+
+int ENGINE_set_flags(ENGINE *e, int flags)
+{
+    e->flags = flags;
+    return 1;
+}
+
+int ENGINE_set_cmd_defns(ENGINE *e, const ENGINE_CMD_DEFN *defns)
+{
+    e->cmd_defns = defns;
+    return 1;
+}
+
+const char *ENGINE_get_id(const ENGINE *e)
+{
+    return e->id;
+}
+
+const char *ENGINE_get_name(const ENGINE *e)
+{
+    return e->name;
+}
+
+ENGINE_GEN_INT_FUNC_PTR ENGINE_get_destroy_function(const ENGINE *e)
+{
+    return e->destroy;
+}
+
+ENGINE_GEN_INT_FUNC_PTR ENGINE_get_init_function(const ENGINE *e)
+{
+    return e->init;
+}
+
+ENGINE_GEN_INT_FUNC_PTR ENGINE_get_finish_function(const ENGINE *e)
+{
+    return e->finish;
+}
+
+ENGINE_CTRL_FUNC_PTR ENGINE_get_ctrl_function(const ENGINE *e)
+{
+    return e->ctrl;
+}
+
+int ENGINE_get_flags(const ENGINE *e)
+{
+    return e->flags;
+}
+
+const ENGINE_CMD_DEFN *ENGINE_get_cmd_defns(const ENGINE *e)
+{
+    return e->cmd_defns;
+}
+
+/*
+ * eng_lib.o is pretty much linked into anything that touches ENGINE already,
+ * so put the "static_state" hack here.
+ */
+
+static int internal_static_hack = 0;
+
+void *ENGINE_get_static_state(void)
+{
+    return &internal_static_hack;
+}
diff --git a/crypto/engine/eng_list.c b/crypto/engine/eng_list.c
new file mode 100644
index 000000000..934389f74
--- /dev/null
+++ b/crypto/engine/eng_list.c
@@ -0,0 +1,354 @@
+/*
+ * Copyright 2001-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/* ====================================================================
+ * Copyright 2002 Sun Microsystems, Inc. ALL RIGHTS RESERVED.
+ * ECDH support in OpenSSL originally developed by
+ * SUN MICROSYSTEMS, INC., and contributed to the OpenSSL project.
+ */
+
+#include "eng_int.h"
+
+/*
+ * The linked-list of pointers to engine types. engine_list_head incorporates
+ * an implicit structural reference but engine_list_tail does not - the
+ * latter is a computational niceity and only points to something that is
+ * already pointed to by its predecessor in the list (or engine_list_head
+ * itself). In the same way, the use of the "prev" pointer in each ENGINE is
+ * to save excessive list iteration, it doesn't correspond to an extra
+ * structural reference. Hence, engine_list_head, and each non-null "next"
+ * pointer account for the list itself assuming exactly 1 structural
+ * reference on each list member.
+ */
+static ENGINE *engine_list_head = NULL;
+static ENGINE *engine_list_tail = NULL;
+
+/*
+ * This cleanup function is only needed internally. If it should be called,
+ * we register it with the "engine_cleanup_int()" stack to be called during
+ * cleanup.
+ */
+
+static void engine_list_cleanup(void)
+{
+    ENGINE *iterator = engine_list_head;
+
+    while (iterator != NULL) {
+        ENGINE_remove(iterator);
+        iterator = engine_list_head;
+    }
+    return;
+}
+
+/*
+ * These static functions starting with a lower case "engine_" always take
+ * place when global_engine_lock has been locked up.
+ */
+static int engine_list_add(ENGINE *e)
+{
+    int conflict = 0;
+    ENGINE *iterator = NULL;
+
+    if (e == NULL) {
+        ENGINEerr(ENGINE_F_ENGINE_LIST_ADD, ERR_R_PASSED_NULL_PARAMETER);
+        return 0;
+    }
+    iterator = engine_list_head;
+    while (iterator && !conflict) {
+        conflict = (strcmp(iterator->id, e->id) == 0);
+        iterator = iterator->next;
+    }
+    if (conflict) {
+        ENGINEerr(ENGINE_F_ENGINE_LIST_ADD, ENGINE_R_CONFLICTING_ENGINE_ID);
+        return 0;
+    }
+    if (engine_list_head == NULL) {
+        /* We are adding to an empty list. */
+        if (engine_list_tail) {
+            ENGINEerr(ENGINE_F_ENGINE_LIST_ADD, ENGINE_R_INTERNAL_LIST_ERROR);
+            return 0;
+        }
+        engine_list_head = e;
+        e->prev = NULL;
+        /*
+         * The first time the list allocates, we should register the cleanup.
+         */
+        engine_cleanup_add_last(engine_list_cleanup);
+    } else {
+        /* We are adding to the tail of an existing list. */
+        if ((engine_list_tail == NULL) || (engine_list_tail->next != NULL)) {
+            ENGINEerr(ENGINE_F_ENGINE_LIST_ADD, ENGINE_R_INTERNAL_LIST_ERROR);
+            return 0;
+        }
+        engine_list_tail->next = e;
+        e->prev = engine_list_tail;
+    }
+    /*
+     * Having the engine in the list assumes a structural reference.
+     */
+    e->struct_ref++;
+    engine_ref_debug(e, 0, 1);
+    /* However it came to be, e is the last item in the list. */
+    engine_list_tail = e;
+    e->next = NULL;
+    return 1;
+}
+
+static int engine_list_remove(ENGINE *e)
+{
+    ENGINE *iterator;
+
+    if (e == NULL) {
+        ENGINEerr(ENGINE_F_ENGINE_LIST_REMOVE, ERR_R_PASSED_NULL_PARAMETER);
+        return 0;
+    }
+    /* We need to check that e is in our linked list! */
+    iterator = engine_list_head;
+    while (iterator && (iterator != e))
+        iterator = iterator->next;
+    if (iterator == NULL) {
+        ENGINEerr(ENGINE_F_ENGINE_LIST_REMOVE,
+                  ENGINE_R_ENGINE_IS_NOT_IN_LIST);
+        return 0;
+    }
+    /* un-link e from the chain. */
+    if (e->next)
+        e->next->prev = e->prev;
+    if (e->prev)
+        e->prev->next = e->next;
+    /* Correct our head/tail if necessary. */
+    if (engine_list_head == e)
+        engine_list_head = e->next;
+    if (engine_list_tail == e)
+        engine_list_tail = e->prev;
+    engine_free_util(e, 0);
+    return 1;
+}
+
+/* Get the first/last "ENGINE" type available. */
+ENGINE *ENGINE_get_first(void)
+{
+    ENGINE *ret;
+
+    if (!RUN_ONCE(&engine_lock_init, do_engine_lock_init)) {
+        ENGINEerr(ENGINE_F_ENGINE_GET_FIRST, ERR_R_MALLOC_FAILURE);
+        return NULL;
+    }
+
+    CRYPTO_THREAD_write_lock(global_engine_lock);
+    ret = engine_list_head;
+    if (ret) {
+        ret->struct_ref++;
+        engine_ref_debug(ret, 0, 1);
+    }
+    CRYPTO_THREAD_unlock(global_engine_lock);
+    return ret;
+}
+
+ENGINE *ENGINE_get_last(void)
+{
+    ENGINE *ret;
+
+    if (!RUN_ONCE(&engine_lock_init, do_engine_lock_init)) {
+        ENGINEerr(ENGINE_F_ENGINE_GET_LAST, ERR_R_MALLOC_FAILURE);
+        return NULL;
+    }
+
+    CRYPTO_THREAD_write_lock(global_engine_lock);
+    ret = engine_list_tail;
+    if (ret) {
+        ret->struct_ref++;
+        engine_ref_debug(ret, 0, 1);
+    }
+    CRYPTO_THREAD_unlock(global_engine_lock);
+    return ret;
+}
+
+/* Iterate to the next/previous "ENGINE" type (NULL = end of the list). */
+ENGINE *ENGINE_get_next(ENGINE *e)
+{
+    ENGINE *ret = NULL;
+    if (e == NULL) {
+        ENGINEerr(ENGINE_F_ENGINE_GET_NEXT, ERR_R_PASSED_NULL_PARAMETER);
+        return 0;
+    }
+    CRYPTO_THREAD_write_lock(global_engine_lock);
+    ret = e->next;
+    if (ret) {
+        /* Return a valid structural reference to the next ENGINE */
+        ret->struct_ref++;
+        engine_ref_debug(ret, 0, 1);
+    }
+    CRYPTO_THREAD_unlock(global_engine_lock);
+    /* Release the structural reference to the previous ENGINE */
+    ENGINE_free(e);
+    return ret;
+}
+
+ENGINE *ENGINE_get_prev(ENGINE *e)
+{
+    ENGINE *ret = NULL;
+    if (e == NULL) {
+        ENGINEerr(ENGINE_F_ENGINE_GET_PREV, ERR_R_PASSED_NULL_PARAMETER);
+        return 0;
+    }
+    CRYPTO_THREAD_write_lock(global_engine_lock);
+    ret = e->prev;
+    if (ret) {
+        /* Return a valid structural reference to the next ENGINE */
+        ret->struct_ref++;
+        engine_ref_debug(ret, 0, 1);
+    }
+    CRYPTO_THREAD_unlock(global_engine_lock);
+    /* Release the structural reference to the previous ENGINE */
+    ENGINE_free(e);
+    return ret;
+}
+
+/* Add another "ENGINE" type into the list. */
+int ENGINE_add(ENGINE *e)
+{
+    int to_return = 1;
+    if (e == NULL) {
+        ENGINEerr(ENGINE_F_ENGINE_ADD, ERR_R_PASSED_NULL_PARAMETER);
+        return 0;
+    }
+    if ((e->id == NULL) || (e->name == NULL)) {
+        ENGINEerr(ENGINE_F_ENGINE_ADD, ENGINE_R_ID_OR_NAME_MISSING);
+        return 0;
+    }
+    CRYPTO_THREAD_write_lock(global_engine_lock);
+    if (!engine_list_add(e)) {
+        ENGINEerr(ENGINE_F_ENGINE_ADD, ENGINE_R_INTERNAL_LIST_ERROR);
+        to_return = 0;
+    }
+    CRYPTO_THREAD_unlock(global_engine_lock);
+    return to_return;
+}
+
+/* Remove an existing "ENGINE" type from the array. */
+int ENGINE_remove(ENGINE *e)
+{
+    int to_return = 1;
+    if (e == NULL) {
+        ENGINEerr(ENGINE_F_ENGINE_REMOVE, ERR_R_PASSED_NULL_PARAMETER);
+        return 0;
+    }
+    CRYPTO_THREAD_write_lock(global_engine_lock);
+    if (!engine_list_remove(e)) {
+        ENGINEerr(ENGINE_F_ENGINE_REMOVE, ENGINE_R_INTERNAL_LIST_ERROR);
+        to_return = 0;
+    }
+    CRYPTO_THREAD_unlock(global_engine_lock);
+    return to_return;
+}
+
+static void engine_cpy(ENGINE *dest, const ENGINE *src)
+{
+    dest->id = src->id;
+    dest->name = src->name;
+#ifndef OPENSSL_NO_RSA
+    dest->rsa_meth = src->rsa_meth;
+#endif
+#ifndef OPENSSL_NO_DSA
+    dest->dsa_meth = src->dsa_meth;
+#endif
+#ifndef OPENSSL_NO_DH
+    dest->dh_meth = src->dh_meth;
+#endif
+#ifndef OPENSSL_NO_EC
+    dest->ec_meth = src->ec_meth;
+#endif
+    dest->rand_meth = src->rand_meth;
+    dest->ciphers = src->ciphers;
+    dest->digests = src->digests;
+    dest->pkey_meths = src->pkey_meths;
+    dest->destroy = src->destroy;
+    dest->init = src->init;
+    dest->finish = src->finish;
+    dest->ctrl = src->ctrl;
+    dest->load_privkey = src->load_privkey;
+    dest->load_pubkey = src->load_pubkey;
+    dest->cmd_defns = src->cmd_defns;
+    dest->flags = src->flags;
+}
+
+ENGINE *ENGINE_by_id(const char *id)
+{
+    ENGINE *iterator;
+    char *load_dir = NULL;
+    if (id == NULL) {
+        ENGINEerr(ENGINE_F_ENGINE_BY_ID, ERR_R_PASSED_NULL_PARAMETER);
+        return NULL;
+    }
+    if (!RUN_ONCE(&engine_lock_init, do_engine_lock_init)) {
+        ENGINEerr(ENGINE_F_ENGINE_BY_ID, ERR_R_MALLOC_FAILURE);
+        return NULL;
+    }
+
+    CRYPTO_THREAD_write_lock(global_engine_lock);
+    iterator = engine_list_head;
+    while (iterator && (strcmp(id, iterator->id) != 0))
+        iterator = iterator->next;
+    if (iterator != NULL) {
+        /*
+         * We need to return a structural reference. If this is an ENGINE
+         * type that returns copies, make a duplicate - otherwise increment
+         * the existing ENGINE's reference count.
+         */
+        if (iterator->flags & ENGINE_FLAGS_BY_ID_COPY) {
+            ENGINE *cp = ENGINE_new();
+            if (cp == NULL)
+                iterator = NULL;
+            else {
+                engine_cpy(cp, iterator);
+                iterator = cp;
+            }
+        } else {
+            iterator->struct_ref++;
+            engine_ref_debug(iterator, 0, 1);
+        }
+    }
+    CRYPTO_THREAD_unlock(global_engine_lock);
+    if (iterator != NULL)
+        return iterator;
+    /*
+     * Prevent infinite recursion if we're looking for the dynamic engine.
+     */
+    if (strcmp(id, "dynamic")) {
+        if ((load_dir = getenv("OPENSSL_ENGINES")) == 0)
+            load_dir = ENGINESDIR;
+        iterator = ENGINE_by_id("dynamic");
+        if (!iterator || !ENGINE_ctrl_cmd_string(iterator, "ID", id, 0) ||
+            !ENGINE_ctrl_cmd_string(iterator, "DIR_LOAD", "2", 0) ||
+            !ENGINE_ctrl_cmd_string(iterator, "DIR_ADD",
+                                    load_dir, 0) ||
+            !ENGINE_ctrl_cmd_string(iterator, "LIST_ADD", "1", 0) ||
+            !ENGINE_ctrl_cmd_string(iterator, "LOAD", NULL, 0))
+            goto notfound;
+        return iterator;
+    }
+ notfound:
+    ENGINE_free(iterator);
+    ENGINEerr(ENGINE_F_ENGINE_BY_ID, ENGINE_R_NO_SUCH_ENGINE);
+    ERR_add_error_data(2, "id=", id);
+    return NULL;
+    /* EEK! Experimental code ends */
+}
+
+int ENGINE_up_ref(ENGINE *e)
+{
+    int i;
+    if (e == NULL) {
+        ENGINEerr(ENGINE_F_ENGINE_UP_REF, ERR_R_PASSED_NULL_PARAMETER);
+        return 0;
+    }
+    CRYPTO_atomic_add(&e->struct_ref, 1, &i, global_engine_lock);
+    return 1;
+}
diff --git a/crypto/engine/eng_openssl.c b/crypto/engine/eng_openssl.c
new file mode 100644
index 000000000..9208f7eaf
--- /dev/null
+++ b/crypto/engine/eng_openssl.c
@@ -0,0 +1,652 @@
+/*
+ * Copyright 2001-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/* ====================================================================
+ * Copyright 2002 Sun Microsystems, Inc. ALL RIGHTS RESERVED.
+ * ECDH support in OpenSSL originally developed by
+ * SUN MICROSYSTEMS, INC., and contributed to the OpenSSL project.
+ */
+
+#include 
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#include 
+#include 
+
+/*
+ * This testing gunk is implemented (and explained) lower down. It also
+ * assumes the application explicitly calls "ENGINE_load_openssl()" because
+ * this is no longer automatic in ENGINE_load_builtin_engines().
+ */
+#define TEST_ENG_OPENSSL_RC4
+#ifndef OPENSSL_NO_STDIO
+#define TEST_ENG_OPENSSL_PKEY
+#endif
+/* #define TEST_ENG_OPENSSL_HMAC */
+/* #define TEST_ENG_OPENSSL_HMAC_INIT */
+/* #define TEST_ENG_OPENSSL_RC4_OTHERS */
+#define TEST_ENG_OPENSSL_RC4_P_INIT
+/* #define TEST_ENG_OPENSSL_RC4_P_CIPHER */
+#define TEST_ENG_OPENSSL_SHA
+/* #define TEST_ENG_OPENSSL_SHA_OTHERS */
+/* #define TEST_ENG_OPENSSL_SHA_P_INIT */
+/* #define TEST_ENG_OPENSSL_SHA_P_UPDATE */
+/* #define TEST_ENG_OPENSSL_SHA_P_FINAL */
+
+/* Now check what of those algorithms are actually enabled */
+#ifdef OPENSSL_NO_RC4
+# undef TEST_ENG_OPENSSL_RC4
+# undef TEST_ENG_OPENSSL_RC4_OTHERS
+# undef TEST_ENG_OPENSSL_RC4_P_INIT
+# undef TEST_ENG_OPENSSL_RC4_P_CIPHER
+#endif
+
+static int openssl_destroy(ENGINE *e);
+
+#ifdef TEST_ENG_OPENSSL_RC4
+static int openssl_ciphers(ENGINE *e, const EVP_CIPHER **cipher,
+                           const int **nids, int nid);
+#endif
+#ifdef TEST_ENG_OPENSSL_SHA
+static int openssl_digests(ENGINE *e, const EVP_MD **digest,
+                           const int **nids, int nid);
+#endif
+
+#ifdef TEST_ENG_OPENSSL_PKEY
+static EVP_PKEY *openssl_load_privkey(ENGINE *eng, const char *key_id,
+                                      UI_METHOD *ui_method,
+                                      void *callback_data);
+#endif
+
+#ifdef TEST_ENG_OPENSSL_HMAC
+static int ossl_register_hmac_meth(void);
+static int ossl_pkey_meths(ENGINE *e, EVP_PKEY_METHOD **pmeth,
+                           const int **nids, int nid);
+#endif
+
+/* The constants used when creating the ENGINE */
+static const char *engine_openssl_id = "openssl";
+static const char *engine_openssl_name = "Software engine support";
+
+/*
+ * This internal function is used by ENGINE_openssl() and possibly by the
+ * "dynamic" ENGINE support too
+ */
+static int bind_helper(ENGINE *e)
+{
+    if (!ENGINE_set_id(e, engine_openssl_id)
+        || !ENGINE_set_name(e, engine_openssl_name)
+        || !ENGINE_set_destroy_function(e, openssl_destroy)
+#ifndef TEST_ENG_OPENSSL_NO_ALGORITHMS
+# ifndef OPENSSL_NO_RSA
+        || !ENGINE_set_RSA(e, RSA_get_default_method())
+# endif
+# ifndef OPENSSL_NO_DSA
+        || !ENGINE_set_DSA(e, DSA_get_default_method())
+# endif
+# ifndef OPENSSL_NO_EC
+        || !ENGINE_set_EC(e, EC_KEY_OpenSSL())
+# endif
+# ifndef OPENSSL_NO_DH
+        || !ENGINE_set_DH(e, DH_get_default_method())
+# endif
+        || !ENGINE_set_RAND(e, RAND_OpenSSL())
+# ifdef TEST_ENG_OPENSSL_RC4
+        || !ENGINE_set_ciphers(e, openssl_ciphers)
+# endif
+# ifdef TEST_ENG_OPENSSL_SHA
+        || !ENGINE_set_digests(e, openssl_digests)
+# endif
+#endif
+#ifdef TEST_ENG_OPENSSL_PKEY
+        || !ENGINE_set_load_privkey_function(e, openssl_load_privkey)
+#endif
+#ifdef TEST_ENG_OPENSSL_HMAC
+        || !ossl_register_hmac_meth()
+        || !ENGINE_set_pkey_meths(e, ossl_pkey_meths)
+#endif
+        )
+        return 0;
+    /*
+     * If we add errors to this ENGINE, ensure the error handling is setup
+     * here
+     */
+    /* openssl_load_error_strings(); */
+    return 1;
+}
+
+static ENGINE *engine_openssl(void)
+{
+    ENGINE *ret = ENGINE_new();
+    if (ret == NULL)
+        return NULL;
+    if (!bind_helper(ret)) {
+        ENGINE_free(ret);
+        return NULL;
+    }
+    return ret;
+}
+
+void engine_load_openssl_int(void)
+{
+    ENGINE *toadd = engine_openssl();
+    if (!toadd)
+        return;
+    ENGINE_add(toadd);
+    /*
+     * If the "add" worked, it gets a structural reference. So either way, we
+     * release our just-created reference.
+     */
+    ENGINE_free(toadd);
+    ERR_clear_error();
+}
+
+/*
+ * This stuff is needed if this ENGINE is being compiled into a
+ * self-contained shared-library.
+ */
+#ifdef ENGINE_DYNAMIC_SUPPORT
+static int bind_fn(ENGINE *e, const char *id)
+{
+    if (id && (strcmp(id, engine_openssl_id) != 0))
+        return 0;
+    if (!bind_helper(e))
+        return 0;
+    return 1;
+}
+
+IMPLEMENT_DYNAMIC_CHECK_FN()
+    IMPLEMENT_DYNAMIC_BIND_FN(bind_fn)
+#endif                          /* ENGINE_DYNAMIC_SUPPORT */
+#ifdef TEST_ENG_OPENSSL_RC4
+/*-
+ * This section of code compiles an "alternative implementation" of two modes of
+ * RC4 into this ENGINE. The result is that EVP_CIPHER operation for "rc4"
+ * should under normal circumstances go via this support rather than the default
+ * EVP support. There are other symbols to tweak the testing;
+ *    TEST_ENC_OPENSSL_RC4_OTHERS - print a one line message to stderr each time
+ *        we're asked for a cipher we don't support (should not happen).
+ *    TEST_ENG_OPENSSL_RC4_P_INIT - print a one line message to stderr each time
+ *        the "init_key" handler is called.
+ *    TEST_ENG_OPENSSL_RC4_P_CIPHER - ditto for the "cipher" handler.
+ */
+# include 
+# define TEST_RC4_KEY_SIZE               16
+typedef struct {
+    unsigned char key[TEST_RC4_KEY_SIZE];
+    RC4_KEY ks;
+} TEST_RC4_KEY;
+# define test(ctx) ((TEST_RC4_KEY *)EVP_CIPHER_CTX_get_cipher_data(ctx))
+static int test_rc4_init_key(EVP_CIPHER_CTX *ctx, const unsigned char *key,
+                             const unsigned char *iv, int enc)
+{
+# ifdef TEST_ENG_OPENSSL_RC4_P_INIT
+    fprintf(stderr, "(TEST_ENG_OPENSSL_RC4) test_init_key() called\n");
+# endif
+    memcpy(&test(ctx)->key[0], key, EVP_CIPHER_CTX_key_length(ctx));
+    RC4_set_key(&test(ctx)->ks, EVP_CIPHER_CTX_key_length(ctx),
+                test(ctx)->key);
+    return 1;
+}
+
+static int test_rc4_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
+                           const unsigned char *in, size_t inl)
+{
+# ifdef TEST_ENG_OPENSSL_RC4_P_CIPHER
+    fprintf(stderr, "(TEST_ENG_OPENSSL_RC4) test_cipher() called\n");
+# endif
+    RC4(&test(ctx)->ks, inl, in, out);
+    return 1;
+}
+
+static EVP_CIPHER *r4_cipher = NULL;
+static const EVP_CIPHER *test_r4_cipher(void)
+{
+    if (r4_cipher == NULL) {
+        EVP_CIPHER *cipher;
+
+        if ((cipher = EVP_CIPHER_meth_new(NID_rc4, 1, TEST_RC4_KEY_SIZE)) == NULL
+            || !EVP_CIPHER_meth_set_iv_length(cipher, 0)
+            || !EVP_CIPHER_meth_set_flags(cipher, EVP_CIPH_VARIABLE_LENGTH)
+            || !EVP_CIPHER_meth_set_init(cipher, test_rc4_init_key)
+            || !EVP_CIPHER_meth_set_do_cipher(cipher, test_rc4_cipher)
+            || !EVP_CIPHER_meth_set_impl_ctx_size(cipher, sizeof(TEST_RC4_KEY))) {
+            EVP_CIPHER_meth_free(cipher);
+            cipher = NULL;
+        }
+        r4_cipher = cipher;
+    }
+    return r4_cipher;
+}
+static void test_r4_cipher_destroy(void)
+{
+    EVP_CIPHER_meth_free(r4_cipher);
+    r4_cipher = NULL;
+}
+
+static EVP_CIPHER *r4_40_cipher = NULL;
+static const EVP_CIPHER *test_r4_40_cipher(void)
+{
+    if (r4_40_cipher == NULL) {
+        EVP_CIPHER *cipher;
+
+        if ((cipher = EVP_CIPHER_meth_new(NID_rc4, 1, 5 /* 40 bits */)) == NULL
+            || !EVP_CIPHER_meth_set_iv_length(cipher, 0)
+            || !EVP_CIPHER_meth_set_flags(cipher, EVP_CIPH_VARIABLE_LENGTH)
+            || !EVP_CIPHER_meth_set_init(cipher, test_rc4_init_key)
+            || !EVP_CIPHER_meth_set_do_cipher(cipher, test_rc4_cipher)
+            || !EVP_CIPHER_meth_set_impl_ctx_size(cipher, sizeof(TEST_RC4_KEY))) {
+            EVP_CIPHER_meth_free(cipher);
+            cipher = NULL;
+        }
+        r4_40_cipher = cipher;
+    }
+    return r4_40_cipher;
+}
+static void test_r4_40_cipher_destroy(void)
+{
+    EVP_CIPHER_meth_free(r4_40_cipher);
+    r4_40_cipher = NULL;
+}
+static int test_cipher_nids(const int **nids)
+{
+    static int cipher_nids[4] = { 0, 0, 0, 0 };
+    static int pos = 0;
+    static int init = 0;
+
+    if (!init) {
+        const EVP_CIPHER *cipher;
+        if ((cipher = test_r4_cipher()) != NULL)
+            cipher_nids[pos++] = EVP_CIPHER_nid(cipher);
+        if ((cipher = test_r4_40_cipher()) != NULL)
+            cipher_nids[pos++] = EVP_CIPHER_nid(cipher);
+        cipher_nids[pos] = 0;
+        init = 1;
+    }
+    *nids = cipher_nids;
+    return pos;
+}
+
+static int openssl_ciphers(ENGINE *e, const EVP_CIPHER **cipher,
+                           const int **nids, int nid)
+{
+    if (!cipher) {
+        /* We are returning a list of supported nids */
+        return test_cipher_nids(nids);
+    }
+    /* We are being asked for a specific cipher */
+    if (nid == NID_rc4)
+        *cipher = test_r4_cipher();
+    else if (nid == NID_rc4_40)
+        *cipher = test_r4_40_cipher();
+    else {
+# ifdef TEST_ENG_OPENSSL_RC4_OTHERS
+        fprintf(stderr, "(TEST_ENG_OPENSSL_RC4) returning NULL for "
+                "nid %d\n", nid);
+# endif
+        *cipher = NULL;
+        return 0;
+    }
+    return 1;
+}
+#endif
+
+#ifdef TEST_ENG_OPENSSL_SHA
+/* Much the same sort of comment as for TEST_ENG_OPENSSL_RC4 */
+# include 
+
+static int test_sha1_init(EVP_MD_CTX *ctx)
+{
+# ifdef TEST_ENG_OPENSSL_SHA_P_INIT
+    fprintf(stderr, "(TEST_ENG_OPENSSL_SHA) test_sha1_init() called\n");
+# endif
+    return SHA1_Init(EVP_MD_CTX_md_data(ctx));
+}
+
+static int test_sha1_update(EVP_MD_CTX *ctx, const void *data, size_t count)
+{
+# ifdef TEST_ENG_OPENSSL_SHA_P_UPDATE
+    fprintf(stderr, "(TEST_ENG_OPENSSL_SHA) test_sha1_update() called\n");
+# endif
+    return SHA1_Update(EVP_MD_CTX_md_data(ctx), data, count);
+}
+
+static int test_sha1_final(EVP_MD_CTX *ctx, unsigned char *md)
+{
+# ifdef TEST_ENG_OPENSSL_SHA_P_FINAL
+    fprintf(stderr, "(TEST_ENG_OPENSSL_SHA) test_sha1_final() called\n");
+# endif
+    return SHA1_Final(md, EVP_MD_CTX_md_data(ctx));
+}
+
+static EVP_MD *sha1_md = NULL;
+static const EVP_MD *test_sha_md(void)
+{
+    if (sha1_md == NULL) {
+        EVP_MD *md;
+
+        if ((md = EVP_MD_meth_new(NID_sha1, NID_sha1WithRSAEncryption)) == NULL
+            || !EVP_MD_meth_set_result_size(md, SHA_DIGEST_LENGTH)
+            || !EVP_MD_meth_set_input_blocksize(md, SHA_CBLOCK)
+            || !EVP_MD_meth_set_app_datasize(md,
+                                             sizeof(EVP_MD *) + sizeof(SHA_CTX))
+            || !EVP_MD_meth_set_flags(md, 0)
+            || !EVP_MD_meth_set_init(md, test_sha1_init)
+            || !EVP_MD_meth_set_update(md, test_sha1_update)
+            || !EVP_MD_meth_set_final(md, test_sha1_final)) {
+            EVP_MD_meth_free(md);
+            md = NULL;
+        }
+        sha1_md = md;
+    }
+    return sha1_md;
+}
+static void test_sha_md_destroy(void)
+{
+    EVP_MD_meth_free(sha1_md);
+    sha1_md = NULL;
+}
+static int test_digest_nids(const int **nids)
+{
+    static int digest_nids[2] = { 0, 0 };
+    static int pos = 0;
+    static int init = 0;
+
+    if (!init) {
+        const EVP_MD *md;
+        if ((md = test_sha_md()) != NULL)
+            digest_nids[pos++] = EVP_MD_type(md);
+        digest_nids[pos] = 0;
+        init = 1;
+    }
+    *nids = digest_nids;
+    return pos;
+}
+
+static int openssl_digests(ENGINE *e, const EVP_MD **digest,
+                           const int **nids, int nid)
+{
+    if (!digest) {
+        /* We are returning a list of supported nids */
+        return test_digest_nids(nids);
+    }
+    /* We are being asked for a specific digest */
+    if (nid == NID_sha1)
+        *digest = test_sha_md();
+    else {
+# ifdef TEST_ENG_OPENSSL_SHA_OTHERS
+        fprintf(stderr, "(TEST_ENG_OPENSSL_SHA) returning NULL for "
+                "nid %d\n", nid);
+# endif
+        *digest = NULL;
+        return 0;
+    }
+    return 1;
+}
+#endif
+
+#ifdef TEST_ENG_OPENSSL_PKEY
+static EVP_PKEY *openssl_load_privkey(ENGINE *eng, const char *key_id,
+                                      UI_METHOD *ui_method,
+                                      void *callback_data)
+{
+    BIO *in;
+    EVP_PKEY *key;
+    fprintf(stderr, "(TEST_ENG_OPENSSL_PKEY)Loading Private key %s\n",
+            key_id);
+    in = BIO_new_file(key_id, "r");
+    if (!in)
+        return NULL;
+    key = PEM_read_bio_PrivateKey(in, NULL, 0, NULL);
+    BIO_free(in);
+    return key;
+}
+#endif
+
+#ifdef TEST_ENG_OPENSSL_HMAC
+
+/*
+ * Experimental HMAC redirection implementation: mainly copied from
+ * hm_pmeth.c
+ */
+
+/* HMAC pkey context structure */
+
+typedef struct {
+    const EVP_MD *md;           /* MD for HMAC use */
+    ASN1_OCTET_STRING ktmp;     /* Temp storage for key */
+    HMAC_CTX *ctx;
+} OSSL_HMAC_PKEY_CTX;
+
+static int ossl_hmac_init(EVP_PKEY_CTX *ctx)
+{
+    OSSL_HMAC_PKEY_CTX *hctx;
+
+    hctx = OPENSSL_zalloc(sizeof(*hctx));
+    if (hctx == NULL)
+        return 0;
+    hctx->ktmp.type = V_ASN1_OCTET_STRING;
+    hctx->ctx = HMAC_CTX_new();
+    if (hctx->ctx == NULL) {
+        OPENSSL_free(hctx);
+        return 0;
+    }
+    EVP_PKEY_CTX_set_data(ctx, hctx);
+    EVP_PKEY_CTX_set0_keygen_info(ctx, NULL, 0);
+# ifdef TEST_ENG_OPENSSL_HMAC_INIT
+    fprintf(stderr, "(TEST_ENG_OPENSSL_HMAC) ossl_hmac_init() called\n");
+# endif
+    return 1;
+}
+
+static void ossl_hmac_cleanup(EVP_PKEY_CTX *ctx);
+
+static int ossl_hmac_copy(EVP_PKEY_CTX *dst, EVP_PKEY_CTX *src)
+{
+    OSSL_HMAC_PKEY_CTX *sctx, *dctx;
+
+    /* allocate memory for dst->data and a new HMAC_CTX in dst->data->ctx */
+    if (!ossl_hmac_init(dst))
+        return 0;
+    sctx = EVP_PKEY_CTX_get_data(src);
+    dctx = EVP_PKEY_CTX_get_data(dst);
+    dctx->md = sctx->md;
+    if (!HMAC_CTX_copy(dctx->ctx, sctx->ctx))
+        goto err;
+    if (sctx->ktmp.data) {
+        if (!ASN1_OCTET_STRING_set(&dctx->ktmp,
+                                   sctx->ktmp.data, sctx->ktmp.length))
+            goto err;
+    }
+    return 1;
+err:
+    /* release HMAC_CTX in dst->data->ctx and memory allocated for dst->data */
+    ossl_hmac_cleanup(dst);
+    return 0;
+}
+
+static void ossl_hmac_cleanup(EVP_PKEY_CTX *ctx)
+{
+    OSSL_HMAC_PKEY_CTX *hctx = EVP_PKEY_CTX_get_data(ctx);
+
+    if (hctx) {
+        HMAC_CTX_free(hctx->ctx);
+        OPENSSL_clear_free(hctx->ktmp.data, hctx->ktmp.length);
+        OPENSSL_free(hctx);
+        EVP_PKEY_CTX_set_data(ctx, NULL);
+    }
+}
+
+static int ossl_hmac_keygen(EVP_PKEY_CTX *ctx, EVP_PKEY *pkey)
+{
+    ASN1_OCTET_STRING *hkey = NULL;
+    OSSL_HMAC_PKEY_CTX *hctx = EVP_PKEY_CTX_get_data(ctx);
+    if (!hctx->ktmp.data)
+        return 0;
+    hkey = ASN1_OCTET_STRING_dup(&hctx->ktmp);
+    if (!hkey)
+        return 0;
+    EVP_PKEY_assign(pkey, EVP_PKEY_HMAC, hkey);
+
+    return 1;
+}
+
+static int ossl_int_update(EVP_MD_CTX *ctx, const void *data, size_t count)
+{
+    OSSL_HMAC_PKEY_CTX *hctx = EVP_PKEY_CTX_get_data(EVP_MD_CTX_pkey_ctx(ctx));
+    if (!HMAC_Update(hctx->ctx, data, count))
+        return 0;
+    return 1;
+}
+
+static int ossl_hmac_signctx_init(EVP_PKEY_CTX *ctx, EVP_MD_CTX *mctx)
+{
+    EVP_MD_CTX_set_flags(mctx, EVP_MD_CTX_FLAG_NO_INIT);
+    EVP_MD_CTX_set_update_fn(mctx, ossl_int_update);
+    return 1;
+}
+
+static int ossl_hmac_signctx(EVP_PKEY_CTX *ctx, unsigned char *sig,
+                             size_t *siglen, EVP_MD_CTX *mctx)
+{
+    unsigned int hlen;
+    OSSL_HMAC_PKEY_CTX *hctx = EVP_PKEY_CTX_get_data(ctx);
+    int l = EVP_MD_CTX_size(mctx);
+
+    if (l < 0)
+        return 0;
+    *siglen = l;
+    if (!sig)
+        return 1;
+
+    if (!HMAC_Final(hctx->ctx, sig, &hlen))
+        return 0;
+    *siglen = (size_t)hlen;
+    return 1;
+}
+
+static int ossl_hmac_ctrl(EVP_PKEY_CTX *ctx, int type, int p1, void *p2)
+{
+    OSSL_HMAC_PKEY_CTX *hctx = EVP_PKEY_CTX_get_data(ctx);
+    EVP_PKEY *pk;
+    ASN1_OCTET_STRING *key;
+    switch (type) {
+
+    case EVP_PKEY_CTRL_SET_MAC_KEY:
+        if ((!p2 && p1 > 0) || (p1 < -1))
+            return 0;
+        if (!ASN1_OCTET_STRING_set(&hctx->ktmp, p2, p1))
+            return 0;
+        break;
+
+    case EVP_PKEY_CTRL_MD:
+        hctx->md = p2;
+        break;
+
+    case EVP_PKEY_CTRL_DIGESTINIT:
+        pk = EVP_PKEY_CTX_get0_pkey(ctx);
+        key = EVP_PKEY_get0(pk);
+        if (!HMAC_Init_ex(hctx->ctx, key->data, key->length, hctx->md, NULL))
+            return 0;
+        break;
+
+    default:
+        return -2;
+
+    }
+    return 1;
+}
+
+static int ossl_hmac_ctrl_str(EVP_PKEY_CTX *ctx,
+                              const char *type, const char *value)
+{
+    if (!value) {
+        return 0;
+    }
+    if (strcmp(type, "key") == 0) {
+        void *p = (void *)value;
+        return ossl_hmac_ctrl(ctx, EVP_PKEY_CTRL_SET_MAC_KEY, -1, p);
+    }
+    if (strcmp(type, "hexkey") == 0) {
+        unsigned char *key;
+        int r;
+        long keylen;
+        key = OPENSSL_hexstr2buf(value, &keylen);
+        if (!key)
+            return 0;
+        r = ossl_hmac_ctrl(ctx, EVP_PKEY_CTRL_SET_MAC_KEY, keylen, key);
+        OPENSSL_free(key);
+        return r;
+    }
+    return -2;
+}
+
+static EVP_PKEY_METHOD *ossl_hmac_meth;
+
+static int ossl_register_hmac_meth(void)
+{
+    EVP_PKEY_METHOD *meth;
+    meth = EVP_PKEY_meth_new(EVP_PKEY_HMAC, 0);
+    if (meth == NULL)
+        return 0;
+    EVP_PKEY_meth_set_init(meth, ossl_hmac_init);
+    EVP_PKEY_meth_set_copy(meth, ossl_hmac_copy);
+    EVP_PKEY_meth_set_cleanup(meth, ossl_hmac_cleanup);
+
+    EVP_PKEY_meth_set_keygen(meth, 0, ossl_hmac_keygen);
+
+    EVP_PKEY_meth_set_signctx(meth, ossl_hmac_signctx_init,
+                              ossl_hmac_signctx);
+
+    EVP_PKEY_meth_set_ctrl(meth, ossl_hmac_ctrl, ossl_hmac_ctrl_str);
+    ossl_hmac_meth = meth;
+    return 1;
+}
+
+static int ossl_pkey_meths(ENGINE *e, EVP_PKEY_METHOD **pmeth,
+                           const int **nids, int nid)
+{
+    static int ossl_pkey_nids[] = {
+        EVP_PKEY_HMAC,
+        0
+    };
+    if (!pmeth) {
+        *nids = ossl_pkey_nids;
+        return 1;
+    }
+
+    if (nid == EVP_PKEY_HMAC) {
+        *pmeth = ossl_hmac_meth;
+        return 1;
+    }
+
+    *pmeth = NULL;
+    return 0;
+}
+
+#endif
+
+int openssl_destroy(ENGINE *e)
+{
+    test_sha_md_destroy();
+#ifdef TEST_ENG_OPENSSL_RC4
+    test_r4_cipher_destroy();
+    test_r4_40_cipher_destroy();
+#endif
+    return 1;
+}
+
diff --git a/crypto/engine/eng_pkey.c b/crypto/engine/eng_pkey.c
new file mode 100644
index 000000000..305a648fe
--- /dev/null
+++ b/crypto/engine/eng_pkey.c
@@ -0,0 +1,140 @@
+/*
+ * Copyright 2001-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "eng_int.h"
+
+/* Basic get/set stuff */
+
+int ENGINE_set_load_privkey_function(ENGINE *e,
+                                     ENGINE_LOAD_KEY_PTR loadpriv_f)
+{
+    e->load_privkey = loadpriv_f;
+    return 1;
+}
+
+int ENGINE_set_load_pubkey_function(ENGINE *e, ENGINE_LOAD_KEY_PTR loadpub_f)
+{
+    e->load_pubkey = loadpub_f;
+    return 1;
+}
+
+int ENGINE_set_load_ssl_client_cert_function(ENGINE *e,
+                                             ENGINE_SSL_CLIENT_CERT_PTR
+                                             loadssl_f)
+{
+    e->load_ssl_client_cert = loadssl_f;
+    return 1;
+}
+
+ENGINE_LOAD_KEY_PTR ENGINE_get_load_privkey_function(const ENGINE *e)
+{
+    return e->load_privkey;
+}
+
+ENGINE_LOAD_KEY_PTR ENGINE_get_load_pubkey_function(const ENGINE *e)
+{
+    return e->load_pubkey;
+}
+
+ENGINE_SSL_CLIENT_CERT_PTR ENGINE_get_ssl_client_cert_function(const ENGINE
+                                                               *e)
+{
+    return e->load_ssl_client_cert;
+}
+
+/* API functions to load public/private keys */
+
+EVP_PKEY *ENGINE_load_private_key(ENGINE *e, const char *key_id,
+                                  UI_METHOD *ui_method, void *callback_data)
+{
+    EVP_PKEY *pkey;
+
+    if (e == NULL) {
+        ENGINEerr(ENGINE_F_ENGINE_LOAD_PRIVATE_KEY,
+                  ERR_R_PASSED_NULL_PARAMETER);
+        return 0;
+    }
+    CRYPTO_THREAD_write_lock(global_engine_lock);
+    if (e->funct_ref == 0) {
+        CRYPTO_THREAD_unlock(global_engine_lock);
+        ENGINEerr(ENGINE_F_ENGINE_LOAD_PRIVATE_KEY, ENGINE_R_NOT_INITIALISED);
+        return 0;
+    }
+    CRYPTO_THREAD_unlock(global_engine_lock);
+    if (!e->load_privkey) {
+        ENGINEerr(ENGINE_F_ENGINE_LOAD_PRIVATE_KEY,
+                  ENGINE_R_NO_LOAD_FUNCTION);
+        return 0;
+    }
+    pkey = e->load_privkey(e, key_id, ui_method, callback_data);
+    if (!pkey) {
+        ENGINEerr(ENGINE_F_ENGINE_LOAD_PRIVATE_KEY,
+                  ENGINE_R_FAILED_LOADING_PRIVATE_KEY);
+        return 0;
+    }
+    return pkey;
+}
+
+EVP_PKEY *ENGINE_load_public_key(ENGINE *e, const char *key_id,
+                                 UI_METHOD *ui_method, void *callback_data)
+{
+    EVP_PKEY *pkey;
+
+    if (e == NULL) {
+        ENGINEerr(ENGINE_F_ENGINE_LOAD_PUBLIC_KEY,
+                  ERR_R_PASSED_NULL_PARAMETER);
+        return 0;
+    }
+    CRYPTO_THREAD_write_lock(global_engine_lock);
+    if (e->funct_ref == 0) {
+        CRYPTO_THREAD_unlock(global_engine_lock);
+        ENGINEerr(ENGINE_F_ENGINE_LOAD_PUBLIC_KEY, ENGINE_R_NOT_INITIALISED);
+        return 0;
+    }
+    CRYPTO_THREAD_unlock(global_engine_lock);
+    if (!e->load_pubkey) {
+        ENGINEerr(ENGINE_F_ENGINE_LOAD_PUBLIC_KEY, ENGINE_R_NO_LOAD_FUNCTION);
+        return 0;
+    }
+    pkey = e->load_pubkey(e, key_id, ui_method, callback_data);
+    if (!pkey) {
+        ENGINEerr(ENGINE_F_ENGINE_LOAD_PUBLIC_KEY,
+                  ENGINE_R_FAILED_LOADING_PUBLIC_KEY);
+        return 0;
+    }
+    return pkey;
+}
+
+int ENGINE_load_ssl_client_cert(ENGINE *e, SSL *s,
+                                STACK_OF(X509_NAME) *ca_dn, X509 **pcert,
+                                EVP_PKEY **ppkey, STACK_OF(X509) **pother,
+                                UI_METHOD *ui_method, void *callback_data)
+{
+
+    if (e == NULL) {
+        ENGINEerr(ENGINE_F_ENGINE_LOAD_SSL_CLIENT_CERT,
+                  ERR_R_PASSED_NULL_PARAMETER);
+        return 0;
+    }
+    CRYPTO_THREAD_write_lock(global_engine_lock);
+    if (e->funct_ref == 0) {
+        CRYPTO_THREAD_unlock(global_engine_lock);
+        ENGINEerr(ENGINE_F_ENGINE_LOAD_SSL_CLIENT_CERT,
+                  ENGINE_R_NOT_INITIALISED);
+        return 0;
+    }
+    CRYPTO_THREAD_unlock(global_engine_lock);
+    if (!e->load_ssl_client_cert) {
+        ENGINEerr(ENGINE_F_ENGINE_LOAD_SSL_CLIENT_CERT,
+                  ENGINE_R_NO_LOAD_FUNCTION);
+        return 0;
+    }
+    return e->load_ssl_client_cert(e, s, ca_dn, pcert, ppkey, pother,
+                                   ui_method, callback_data);
+}
diff --git a/crypto/engine/eng_rdrand.c b/crypto/engine/eng_rdrand.c
new file mode 100644
index 000000000..b3defcbe4
--- /dev/null
+++ b/crypto/engine/eng_rdrand.c
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2011-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#if (defined(__i386)   || defined(__i386__)   || defined(_M_IX86) || \
+     defined(__x86_64) || defined(__x86_64__) || \
+     defined(_M_AMD64) || defined (_M_X64)) && defined(OPENSSL_CPUID_OBJ)
+
+size_t OPENSSL_ia32_rdrand(void);
+
+static int get_random_bytes(unsigned char *buf, int num)
+{
+    size_t rnd;
+
+    while (num >= (int)sizeof(size_t)) {
+        if ((rnd = OPENSSL_ia32_rdrand()) == 0)
+            return 0;
+
+        *((size_t *)buf) = rnd;
+        buf += sizeof(size_t);
+        num -= sizeof(size_t);
+    }
+    if (num) {
+        if ((rnd = OPENSSL_ia32_rdrand()) == 0)
+            return 0;
+
+        memcpy(buf, &rnd, num);
+    }
+
+    return 1;
+}
+
+static int random_status(void)
+{
+    return 1;
+}
+
+static RAND_METHOD rdrand_meth = {
+    NULL,                       /* seed */
+    get_random_bytes,
+    NULL,                       /* cleanup */
+    NULL,                       /* add */
+    get_random_bytes,
+    random_status,
+};
+
+static int rdrand_init(ENGINE *e)
+{
+    return 1;
+}
+
+static const char *engine_e_rdrand_id = "rdrand";
+static const char *engine_e_rdrand_name = "Intel RDRAND engine";
+
+static int bind_helper(ENGINE *e)
+{
+    if (!ENGINE_set_id(e, engine_e_rdrand_id) ||
+        !ENGINE_set_name(e, engine_e_rdrand_name) ||
+        !ENGINE_set_flags(e, ENGINE_FLAGS_NO_REGISTER_ALL) ||
+        !ENGINE_set_init_function(e, rdrand_init) ||
+        !ENGINE_set_RAND(e, &rdrand_meth))
+        return 0;
+
+    return 1;
+}
+
+static ENGINE *ENGINE_rdrand(void)
+{
+    ENGINE *ret = ENGINE_new();
+    if (ret == NULL)
+        return NULL;
+    if (!bind_helper(ret)) {
+        ENGINE_free(ret);
+        return NULL;
+    }
+    return ret;
+}
+
+void engine_load_rdrand_int(void)
+{
+    extern unsigned int OPENSSL_ia32cap_P[];
+
+    if (OPENSSL_ia32cap_P[1] & (1 << (62 - 32))) {
+        ENGINE *toadd = ENGINE_rdrand();
+        if (!toadd)
+            return;
+        ENGINE_add(toadd);
+        ENGINE_free(toadd);
+        ERR_clear_error();
+    }
+}
+#else
+void engine_load_rdrand_int(void)
+{
+}
+#endif
diff --git a/crypto/engine/eng_table.c b/crypto/engine/eng_table.c
new file mode 100644
index 000000000..219253a38
--- /dev/null
+++ b/crypto/engine/eng_table.c
@@ -0,0 +1,303 @@
+/*
+ * Copyright 2001-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "internal/cryptlib.h"
+#include 
+#include 
+#include "eng_int.h"
+
+/* The type of the items in the table */
+struct st_engine_pile {
+    /* The 'nid' of this algorithm/mode */
+    int nid;
+    /* ENGINEs that implement this algorithm/mode. */
+    STACK_OF(ENGINE) *sk;
+    /* The default ENGINE to perform this algorithm/mode. */
+    ENGINE *funct;
+    /*
+     * Zero if 'sk' is newer than the cached 'funct', non-zero otherwise
+     */
+    int uptodate;
+};
+
+/* The type exposed in eng_int.h */
+struct st_engine_table {
+    LHASH_OF(ENGINE_PILE) piles;
+};                              /* ENGINE_TABLE */
+
+typedef struct st_engine_pile_doall {
+    engine_table_doall_cb *cb;
+    void *arg;
+} ENGINE_PILE_DOALL;
+
+/* Global flags (ENGINE_TABLE_FLAG_***). */
+static unsigned int table_flags = 0;
+
+/* API function manipulating 'table_flags' */
+unsigned int ENGINE_get_table_flags(void)
+{
+    return table_flags;
+}
+
+void ENGINE_set_table_flags(unsigned int flags)
+{
+    table_flags = flags;
+}
+
+/* Internal functions for the "piles" hash table */
+static unsigned long engine_pile_hash(const ENGINE_PILE *c)
+{
+    return c->nid;
+}
+
+static int engine_pile_cmp(const ENGINE_PILE *a, const ENGINE_PILE *b)
+{
+    return a->nid - b->nid;
+}
+
+static int int_table_check(ENGINE_TABLE **t, int create)
+{
+    LHASH_OF(ENGINE_PILE) *lh;
+
+    if (*t)
+        return 1;
+    if (!create)
+        return 0;
+    if ((lh = lh_ENGINE_PILE_new(engine_pile_hash, engine_pile_cmp)) == NULL)
+        return 0;
+    *t = (ENGINE_TABLE *)lh;
+    return 1;
+}
+
+/*
+ * Privately exposed (via eng_int.h) functions for adding and/or removing
+ * ENGINEs from the implementation table
+ */
+int engine_table_register(ENGINE_TABLE **table, ENGINE_CLEANUP_CB *cleanup,
+                          ENGINE *e, const int *nids, int num_nids,
+                          int setdefault)
+{
+    int ret = 0, added = 0;
+    ENGINE_PILE tmplate, *fnd;
+    CRYPTO_THREAD_write_lock(global_engine_lock);
+    if (!(*table))
+        added = 1;
+    if (!int_table_check(table, 1))
+        goto end;
+    if (added)
+        /* The cleanup callback needs to be added */
+        engine_cleanup_add_first(cleanup);
+    while (num_nids--) {
+        tmplate.nid = *nids;
+        fnd = lh_ENGINE_PILE_retrieve(&(*table)->piles, &tmplate);
+        if (!fnd) {
+            fnd = OPENSSL_malloc(sizeof(*fnd));
+            if (fnd == NULL)
+                goto end;
+            fnd->uptodate = 1;
+            fnd->nid = *nids;
+            fnd->sk = sk_ENGINE_new_null();
+            if (!fnd->sk) {
+                OPENSSL_free(fnd);
+                goto end;
+            }
+            fnd->funct = NULL;
+            (void)lh_ENGINE_PILE_insert(&(*table)->piles, fnd);
+        }
+        /* A registration shouldn't add duplicate entries */
+        (void)sk_ENGINE_delete_ptr(fnd->sk, e);
+        /*
+         * if 'setdefault', this ENGINE goes to the head of the list
+         */
+        if (!sk_ENGINE_push(fnd->sk, e))
+            goto end;
+        /* "touch" this ENGINE_PILE */
+        fnd->uptodate = 0;
+        if (setdefault) {
+            if (!engine_unlocked_init(e)) {
+                ENGINEerr(ENGINE_F_ENGINE_TABLE_REGISTER,
+                          ENGINE_R_INIT_FAILED);
+                goto end;
+            }
+            if (fnd->funct)
+                engine_unlocked_finish(fnd->funct, 0);
+            fnd->funct = e;
+            fnd->uptodate = 1;
+        }
+        nids++;
+    }
+    ret = 1;
+ end:
+    CRYPTO_THREAD_unlock(global_engine_lock);
+    return ret;
+}
+
+static void int_unregister_cb(ENGINE_PILE *pile, ENGINE *e)
+{
+    int n;
+    /* Iterate the 'c->sk' stack removing any occurrence of 'e' */
+    while ((n = sk_ENGINE_find(pile->sk, e)) >= 0) {
+        (void)sk_ENGINE_delete(pile->sk, n);
+        pile->uptodate = 0;
+    }
+    if (pile->funct == e) {
+        engine_unlocked_finish(e, 0);
+        pile->funct = NULL;
+    }
+}
+
+IMPLEMENT_LHASH_DOALL_ARG(ENGINE_PILE, ENGINE);
+
+void engine_table_unregister(ENGINE_TABLE **table, ENGINE *e)
+{
+    CRYPTO_THREAD_write_lock(global_engine_lock);
+    if (int_table_check(table, 0))
+        lh_ENGINE_PILE_doall_ENGINE(&(*table)->piles, int_unregister_cb, e);
+    CRYPTO_THREAD_unlock(global_engine_lock);
+}
+
+static void int_cleanup_cb_doall(ENGINE_PILE *p)
+{
+    if (!p)
+        return;
+    sk_ENGINE_free(p->sk);
+    if (p->funct)
+        engine_unlocked_finish(p->funct, 0);
+    OPENSSL_free(p);
+}
+
+void engine_table_cleanup(ENGINE_TABLE **table)
+{
+    CRYPTO_THREAD_write_lock(global_engine_lock);
+    if (*table) {
+        lh_ENGINE_PILE_doall(&(*table)->piles, int_cleanup_cb_doall);
+        lh_ENGINE_PILE_free(&(*table)->piles);
+        *table = NULL;
+    }
+    CRYPTO_THREAD_unlock(global_engine_lock);
+}
+
+/* return a functional reference for a given 'nid' */
+#ifndef ENGINE_TABLE_DEBUG
+ENGINE *engine_table_select(ENGINE_TABLE **table, int nid)
+#else
+ENGINE *engine_table_select_tmp(ENGINE_TABLE **table, int nid, const char *f,
+                                int l)
+#endif
+{
+    ENGINE *ret = NULL;
+    ENGINE_PILE tmplate, *fnd = NULL;
+    int initres, loop = 0;
+
+    if (!(*table)) {
+#ifdef ENGINE_TABLE_DEBUG
+        fprintf(stderr, "engine_table_dbg: %s:%d, nid=%d, nothing "
+                "registered!\n", f, l, nid);
+#endif
+        return NULL;
+    }
+    ERR_set_mark();
+    CRYPTO_THREAD_write_lock(global_engine_lock);
+    /*
+     * Check again inside the lock otherwise we could race against cleanup
+     * operations. But don't worry about a fprintf(stderr).
+     */
+    if (!int_table_check(table, 0))
+        goto end;
+    tmplate.nid = nid;
+    fnd = lh_ENGINE_PILE_retrieve(&(*table)->piles, &tmplate);
+    if (!fnd)
+        goto end;
+    if (fnd->funct && engine_unlocked_init(fnd->funct)) {
+#ifdef ENGINE_TABLE_DEBUG
+        fprintf(stderr, "engine_table_dbg: %s:%d, nid=%d, using "
+                "ENGINE '%s' cached\n", f, l, nid, fnd->funct->id);
+#endif
+        ret = fnd->funct;
+        goto end;
+    }
+    if (fnd->uptodate) {
+        ret = fnd->funct;
+        goto end;
+    }
+ trynext:
+    ret = sk_ENGINE_value(fnd->sk, loop++);
+    if (!ret) {
+#ifdef ENGINE_TABLE_DEBUG
+        fprintf(stderr, "engine_table_dbg: %s:%d, nid=%d, no "
+                "registered implementations would initialise\n", f, l, nid);
+#endif
+        goto end;
+    }
+    /* Try to initialise the ENGINE? */
+    if ((ret->funct_ref > 0) || !(table_flags & ENGINE_TABLE_FLAG_NOINIT))
+        initres = engine_unlocked_init(ret);
+    else
+        initres = 0;
+    if (initres) {
+        /* Update 'funct' */
+        if ((fnd->funct != ret) && engine_unlocked_init(ret)) {
+            /* If there was a previous default we release it. */
+            if (fnd->funct)
+                engine_unlocked_finish(fnd->funct, 0);
+            fnd->funct = ret;
+#ifdef ENGINE_TABLE_DEBUG
+            fprintf(stderr, "engine_table_dbg: %s:%d, nid=%d, "
+                    "setting default to '%s'\n", f, l, nid, ret->id);
+#endif
+        }
+#ifdef ENGINE_TABLE_DEBUG
+        fprintf(stderr, "engine_table_dbg: %s:%d, nid=%d, using "
+                "newly initialised '%s'\n", f, l, nid, ret->id);
+#endif
+        goto end;
+    }
+    goto trynext;
+ end:
+    /*
+     * If it failed, it is unlikely to succeed again until some future
+     * registrations have taken place. In all cases, we cache.
+     */
+    if (fnd)
+        fnd->uptodate = 1;
+#ifdef ENGINE_TABLE_DEBUG
+    if (ret)
+        fprintf(stderr, "engine_table_dbg: %s:%d, nid=%d, caching "
+                "ENGINE '%s'\n", f, l, nid, ret->id);
+    else
+        fprintf(stderr, "engine_table_dbg: %s:%d, nid=%d, caching "
+                "'no matching ENGINE'\n", f, l, nid);
+#endif
+    CRYPTO_THREAD_unlock(global_engine_lock);
+    /*
+     * Whatever happened, any failed init()s are not failures in this
+     * context, so clear our error state.
+     */
+    ERR_pop_to_mark();
+    return ret;
+}
+
+/* Table enumeration */
+
+static void int_dall(const ENGINE_PILE *pile, ENGINE_PILE_DOALL *dall)
+{
+    dall->cb(pile->nid, pile->sk, pile->funct, dall->arg);
+}
+
+IMPLEMENT_LHASH_DOALL_ARG_CONST(ENGINE_PILE, ENGINE_PILE_DOALL);
+
+void engine_table_doall(ENGINE_TABLE *table, engine_table_doall_cb *cb,
+                        void *arg)
+{
+    ENGINE_PILE_DOALL dall;
+    dall.cb = cb;
+    dall.arg = arg;
+    if (table)
+        lh_ENGINE_PILE_doall_ENGINE_PILE_DOALL(&table->piles, int_dall, &dall);
+}
diff --git a/crypto/engine/tb_asnmth.c b/crypto/engine/tb_asnmth.c
new file mode 100644
index 000000000..480267daa
--- /dev/null
+++ b/crypto/engine/tb_asnmth.c
@@ -0,0 +1,207 @@
+/*
+ * Copyright 2006-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "eng_int.h"
+#include 
+#include "internal/asn1_int.h"
+
+/*
+ * If this symbol is defined then ENGINE_get_pkey_asn1_meth_engine(), the
+ * function that is used by EVP to hook in pkey_asn1_meth code and cache
+ * defaults (etc), will display brief debugging summaries to stderr with the
+ * 'nid'.
+ */
+/* #define ENGINE_PKEY_ASN1_METH_DEBUG */
+
+static ENGINE_TABLE *pkey_asn1_meth_table = NULL;
+
+void ENGINE_unregister_pkey_asn1_meths(ENGINE *e)
+{
+    engine_table_unregister(&pkey_asn1_meth_table, e);
+}
+
+static void engine_unregister_all_pkey_asn1_meths(void)
+{
+    engine_table_cleanup(&pkey_asn1_meth_table);
+}
+
+int ENGINE_register_pkey_asn1_meths(ENGINE *e)
+{
+    if (e->pkey_asn1_meths) {
+        const int *nids;
+        int num_nids = e->pkey_asn1_meths(e, NULL, &nids, 0);
+        if (num_nids > 0)
+            return engine_table_register(&pkey_asn1_meth_table,
+                                         engine_unregister_all_pkey_asn1_meths,
+                                         e, nids, num_nids, 0);
+    }
+    return 1;
+}
+
+void ENGINE_register_all_pkey_asn1_meths(void)
+{
+    ENGINE *e;
+
+    for (e = ENGINE_get_first(); e; e = ENGINE_get_next(e))
+        ENGINE_register_pkey_asn1_meths(e);
+}
+
+int ENGINE_set_default_pkey_asn1_meths(ENGINE *e)
+{
+    if (e->pkey_asn1_meths) {
+        const int *nids;
+        int num_nids = e->pkey_asn1_meths(e, NULL, &nids, 0);
+        if (num_nids > 0)
+            return engine_table_register(&pkey_asn1_meth_table,
+                                         engine_unregister_all_pkey_asn1_meths,
+                                         e, nids, num_nids, 1);
+    }
+    return 1;
+}
+
+/*
+ * Exposed API function to get a functional reference from the implementation
+ * table (ie. try to get a functional reference from the tabled structural
+ * references) for a given pkey_asn1_meth 'nid'
+ */
+ENGINE *ENGINE_get_pkey_asn1_meth_engine(int nid)
+{
+    return engine_table_select(&pkey_asn1_meth_table, nid);
+}
+
+/*
+ * Obtains a pkey_asn1_meth implementation from an ENGINE functional
+ * reference
+ */
+const EVP_PKEY_ASN1_METHOD *ENGINE_get_pkey_asn1_meth(ENGINE *e, int nid)
+{
+    EVP_PKEY_ASN1_METHOD *ret;
+    ENGINE_PKEY_ASN1_METHS_PTR fn = ENGINE_get_pkey_asn1_meths(e);
+    if (!fn || !fn(e, &ret, NULL, nid)) {
+        ENGINEerr(ENGINE_F_ENGINE_GET_PKEY_ASN1_METH,
+                  ENGINE_R_UNIMPLEMENTED_PUBLIC_KEY_METHOD);
+        return NULL;
+    }
+    return ret;
+}
+
+/* Gets the pkey_asn1_meth callback from an ENGINE structure */
+ENGINE_PKEY_ASN1_METHS_PTR ENGINE_get_pkey_asn1_meths(const ENGINE *e)
+{
+    return e->pkey_asn1_meths;
+}
+
+/* Sets the pkey_asn1_meth callback in an ENGINE structure */
+int ENGINE_set_pkey_asn1_meths(ENGINE *e, ENGINE_PKEY_ASN1_METHS_PTR f)
+{
+    e->pkey_asn1_meths = f;
+    return 1;
+}
+
+/*
+ * Internal function to free up EVP_PKEY_ASN1_METHOD structures before an
+ * ENGINE is destroyed
+ */
+
+void engine_pkey_asn1_meths_free(ENGINE *e)
+{
+    int i;
+    EVP_PKEY_ASN1_METHOD *pkm;
+    if (e->pkey_asn1_meths) {
+        const int *pknids;
+        int npknids;
+        npknids = e->pkey_asn1_meths(e, NULL, &pknids, 0);
+        for (i = 0; i < npknids; i++) {
+            if (e->pkey_asn1_meths(e, &pkm, NULL, pknids[i])) {
+                EVP_PKEY_asn1_free(pkm);
+            }
+        }
+    }
+}
+
+/*
+ * Find a method based on a string. This does a linear search through all
+ * implemented algorithms. This is OK in practice because only a small number
+ * of algorithms are likely to be implemented in an engine and it is not used
+ * for speed critical operations.
+ */
+
+const EVP_PKEY_ASN1_METHOD *ENGINE_get_pkey_asn1_meth_str(ENGINE *e,
+                                                          const char *str,
+                                                          int len)
+{
+    int i, nidcount;
+    const int *nids;
+    EVP_PKEY_ASN1_METHOD *ameth;
+    if (!e->pkey_asn1_meths)
+        return NULL;
+    if (len == -1)
+        len = strlen(str);
+    nidcount = e->pkey_asn1_meths(e, NULL, &nids, 0);
+    for (i = 0; i < nidcount; i++) {
+        e->pkey_asn1_meths(e, &ameth, NULL, nids[i]);
+        if (((int)strlen(ameth->pem_str) == len)
+            && strncasecmp(ameth->pem_str, str, len) == 0)
+            return ameth;
+    }
+    return NULL;
+}
+
+typedef struct {
+    ENGINE *e;
+    const EVP_PKEY_ASN1_METHOD *ameth;
+    const char *str;
+    int len;
+} ENGINE_FIND_STR;
+
+static void look_str_cb(int nid, STACK_OF(ENGINE) *sk, ENGINE *def, void *arg)
+{
+    ENGINE_FIND_STR *lk = arg;
+    int i;
+    if (lk->ameth)
+        return;
+    for (i = 0; i < sk_ENGINE_num(sk); i++) {
+        ENGINE *e = sk_ENGINE_value(sk, i);
+        EVP_PKEY_ASN1_METHOD *ameth;
+        e->pkey_asn1_meths(e, &ameth, NULL, nid);
+        if (((int)strlen(ameth->pem_str) == lk->len)
+                && strncasecmp(ameth->pem_str, lk->str, lk->len) == 0) {
+            lk->e = e;
+            lk->ameth = ameth;
+            return;
+        }
+    }
+}
+
+const EVP_PKEY_ASN1_METHOD *ENGINE_pkey_asn1_find_str(ENGINE **pe,
+                                                      const char *str,
+                                                      int len)
+{
+    ENGINE_FIND_STR fstr;
+    fstr.e = NULL;
+    fstr.ameth = NULL;
+    fstr.str = str;
+    fstr.len = len;
+
+    if (!RUN_ONCE(&engine_lock_init, do_engine_lock_init)) {
+        ENGINEerr(ENGINE_F_ENGINE_PKEY_ASN1_FIND_STR, ERR_R_MALLOC_FAILURE);
+        return NULL;
+    }
+
+    CRYPTO_THREAD_write_lock(global_engine_lock);
+    engine_table_doall(pkey_asn1_meth_table, look_str_cb, &fstr);
+    /* If found obtain a structural reference to engine */
+    if (fstr.e) {
+        fstr.e->struct_ref++;
+        engine_ref_debug(fstr.e, 0, 1);
+    }
+    *pe = fstr.e;
+    CRYPTO_THREAD_unlock(global_engine_lock);
+    return fstr.ameth;
+}
diff --git a/crypto/engine/tb_cipher.c b/crypto/engine/tb_cipher.c
new file mode 100644
index 000000000..ac4914111
--- /dev/null
+++ b/crypto/engine/tb_cipher.c
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2001-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "eng_int.h"
+
+static ENGINE_TABLE *cipher_table = NULL;
+
+void ENGINE_unregister_ciphers(ENGINE *e)
+{
+    engine_table_unregister(&cipher_table, e);
+}
+
+static void engine_unregister_all_ciphers(void)
+{
+    engine_table_cleanup(&cipher_table);
+}
+
+int ENGINE_register_ciphers(ENGINE *e)
+{
+    if (e->ciphers) {
+        const int *nids;
+        int num_nids = e->ciphers(e, NULL, &nids, 0);
+        if (num_nids > 0)
+            return engine_table_register(&cipher_table,
+                                         engine_unregister_all_ciphers, e,
+                                         nids, num_nids, 0);
+    }
+    return 1;
+}
+
+void ENGINE_register_all_ciphers()
+{
+    ENGINE *e;
+
+    for (e = ENGINE_get_first(); e; e = ENGINE_get_next(e))
+        ENGINE_register_ciphers(e);
+}
+
+int ENGINE_set_default_ciphers(ENGINE *e)
+{
+    if (e->ciphers) {
+        const int *nids;
+        int num_nids = e->ciphers(e, NULL, &nids, 0);
+        if (num_nids > 0)
+            return engine_table_register(&cipher_table,
+                                         engine_unregister_all_ciphers, e,
+                                         nids, num_nids, 1);
+    }
+    return 1;
+}
+
+/*
+ * Exposed API function to get a functional reference from the implementation
+ * table (ie. try to get a functional reference from the tabled structural
+ * references) for a given cipher 'nid'
+ */
+ENGINE *ENGINE_get_cipher_engine(int nid)
+{
+    return engine_table_select(&cipher_table, nid);
+}
+
+/* Obtains a cipher implementation from an ENGINE functional reference */
+const EVP_CIPHER *ENGINE_get_cipher(ENGINE *e, int nid)
+{
+    const EVP_CIPHER *ret;
+    ENGINE_CIPHERS_PTR fn = ENGINE_get_ciphers(e);
+    if (!fn || !fn(e, &ret, NULL, nid)) {
+        ENGINEerr(ENGINE_F_ENGINE_GET_CIPHER, ENGINE_R_UNIMPLEMENTED_CIPHER);
+        return NULL;
+    }
+    return ret;
+}
+
+/* Gets the cipher callback from an ENGINE structure */
+ENGINE_CIPHERS_PTR ENGINE_get_ciphers(const ENGINE *e)
+{
+    return e->ciphers;
+}
+
+/* Sets the cipher callback in an ENGINE structure */
+int ENGINE_set_ciphers(ENGINE *e, ENGINE_CIPHERS_PTR f)
+{
+    e->ciphers = f;
+    return 1;
+}
diff --git a/crypto/engine/tb_dh.c b/crypto/engine/tb_dh.c
new file mode 100644
index 000000000..c6440df20
--- /dev/null
+++ b/crypto/engine/tb_dh.c
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2001-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "eng_int.h"
+
+static ENGINE_TABLE *dh_table = NULL;
+static const int dummy_nid = 1;
+
+void ENGINE_unregister_DH(ENGINE *e)
+{
+    engine_table_unregister(&dh_table, e);
+}
+
+static void engine_unregister_all_DH(void)
+{
+    engine_table_cleanup(&dh_table);
+}
+
+int ENGINE_register_DH(ENGINE *e)
+{
+    if (e->dh_meth)
+        return engine_table_register(&dh_table,
+                                     engine_unregister_all_DH, e, &dummy_nid,
+                                     1, 0);
+    return 1;
+}
+
+void ENGINE_register_all_DH()
+{
+    ENGINE *e;
+
+    for (e = ENGINE_get_first(); e; e = ENGINE_get_next(e))
+        ENGINE_register_DH(e);
+}
+
+int ENGINE_set_default_DH(ENGINE *e)
+{
+    if (e->dh_meth)
+        return engine_table_register(&dh_table,
+                                     engine_unregister_all_DH, e, &dummy_nid,
+                                     1, 1);
+    return 1;
+}
+
+/*
+ * Exposed API function to get a functional reference from the implementation
+ * table (ie. try to get a functional reference from the tabled structural
+ * references).
+ */
+ENGINE *ENGINE_get_default_DH(void)
+{
+    return engine_table_select(&dh_table, dummy_nid);
+}
+
+/* Obtains an DH implementation from an ENGINE functional reference */
+const DH_METHOD *ENGINE_get_DH(const ENGINE *e)
+{
+    return e->dh_meth;
+}
+
+/* Sets an DH implementation in an ENGINE structure */
+int ENGINE_set_DH(ENGINE *e, const DH_METHOD *dh_meth)
+{
+    e->dh_meth = dh_meth;
+    return 1;
+}
diff --git a/crypto/engine/tb_digest.c b/crypto/engine/tb_digest.c
new file mode 100644
index 000000000..194b9c7e8
--- /dev/null
+++ b/crypto/engine/tb_digest.c
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2001-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "eng_int.h"
+
+static ENGINE_TABLE *digest_table = NULL;
+
+void ENGINE_unregister_digests(ENGINE *e)
+{
+    engine_table_unregister(&digest_table, e);
+}
+
+static void engine_unregister_all_digests(void)
+{
+    engine_table_cleanup(&digest_table);
+}
+
+int ENGINE_register_digests(ENGINE *e)
+{
+    if (e->digests) {
+        const int *nids;
+        int num_nids = e->digests(e, NULL, &nids, 0);
+        if (num_nids > 0)
+            return engine_table_register(&digest_table,
+                                         engine_unregister_all_digests, e,
+                                         nids, num_nids, 0);
+    }
+    return 1;
+}
+
+void ENGINE_register_all_digests()
+{
+    ENGINE *e;
+
+    for (e = ENGINE_get_first(); e; e = ENGINE_get_next(e))
+        ENGINE_register_digests(e);
+}
+
+int ENGINE_set_default_digests(ENGINE *e)
+{
+    if (e->digests) {
+        const int *nids;
+        int num_nids = e->digests(e, NULL, &nids, 0);
+        if (num_nids > 0)
+            return engine_table_register(&digest_table,
+                                         engine_unregister_all_digests, e,
+                                         nids, num_nids, 1);
+    }
+    return 1;
+}
+
+/*
+ * Exposed API function to get a functional reference from the implementation
+ * table (ie. try to get a functional reference from the tabled structural
+ * references) for a given digest 'nid'
+ */
+ENGINE *ENGINE_get_digest_engine(int nid)
+{
+    return engine_table_select(&digest_table, nid);
+}
+
+/* Obtains a digest implementation from an ENGINE functional reference */
+const EVP_MD *ENGINE_get_digest(ENGINE *e, int nid)
+{
+    const EVP_MD *ret;
+    ENGINE_DIGESTS_PTR fn = ENGINE_get_digests(e);
+    if (!fn || !fn(e, &ret, NULL, nid)) {
+        ENGINEerr(ENGINE_F_ENGINE_GET_DIGEST, ENGINE_R_UNIMPLEMENTED_DIGEST);
+        return NULL;
+    }
+    return ret;
+}
+
+/* Gets the digest callback from an ENGINE structure */
+ENGINE_DIGESTS_PTR ENGINE_get_digests(const ENGINE *e)
+{
+    return e->digests;
+}
+
+/* Sets the digest callback in an ENGINE structure */
+int ENGINE_set_digests(ENGINE *e, ENGINE_DIGESTS_PTR f)
+{
+    e->digests = f;
+    return 1;
+}
diff --git a/crypto/engine/tb_dsa.c b/crypto/engine/tb_dsa.c
new file mode 100644
index 000000000..fdb80cd79
--- /dev/null
+++ b/crypto/engine/tb_dsa.c
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2001-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "eng_int.h"
+
+static ENGINE_TABLE *dsa_table = NULL;
+static const int dummy_nid = 1;
+
+void ENGINE_unregister_DSA(ENGINE *e)
+{
+    engine_table_unregister(&dsa_table, e);
+}
+
+static void engine_unregister_all_DSA(void)
+{
+    engine_table_cleanup(&dsa_table);
+}
+
+int ENGINE_register_DSA(ENGINE *e)
+{
+    if (e->dsa_meth)
+        return engine_table_register(&dsa_table,
+                                     engine_unregister_all_DSA, e, &dummy_nid,
+                                     1, 0);
+    return 1;
+}
+
+void ENGINE_register_all_DSA()
+{
+    ENGINE *e;
+
+    for (e = ENGINE_get_first(); e; e = ENGINE_get_next(e))
+        ENGINE_register_DSA(e);
+}
+
+int ENGINE_set_default_DSA(ENGINE *e)
+{
+    if (e->dsa_meth)
+        return engine_table_register(&dsa_table,
+                                     engine_unregister_all_DSA, e, &dummy_nid,
+                                     1, 1);
+    return 1;
+}
+
+/*
+ * Exposed API function to get a functional reference from the implementation
+ * table (ie. try to get a functional reference from the tabled structural
+ * references).
+ */
+ENGINE *ENGINE_get_default_DSA(void)
+{
+    return engine_table_select(&dsa_table, dummy_nid);
+}
+
+/* Obtains an DSA implementation from an ENGINE functional reference */
+const DSA_METHOD *ENGINE_get_DSA(const ENGINE *e)
+{
+    return e->dsa_meth;
+}
+
+/* Sets an DSA implementation in an ENGINE structure */
+int ENGINE_set_DSA(ENGINE *e, const DSA_METHOD *dsa_meth)
+{
+    e->dsa_meth = dsa_meth;
+    return 1;
+}
diff --git a/crypto/engine/tb_eckey.c b/crypto/engine/tb_eckey.c
new file mode 100644
index 000000000..75750b29f
--- /dev/null
+++ b/crypto/engine/tb_eckey.c
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2015-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "eng_int.h"
+
+static ENGINE_TABLE *dh_table = NULL;
+static const int dummy_nid = 1;
+
+void ENGINE_unregister_EC(ENGINE *e)
+{
+    engine_table_unregister(&dh_table, e);
+}
+
+static void engine_unregister_all_EC(void)
+{
+    engine_table_cleanup(&dh_table);
+}
+
+int ENGINE_register_EC(ENGINE *e)
+{
+    if (e->ec_meth != NULL)
+        return engine_table_register(&dh_table,
+                                     engine_unregister_all_EC, e, &dummy_nid,
+                                     1, 0);
+    return 1;
+}
+
+void ENGINE_register_all_EC()
+{
+    ENGINE *e;
+
+    for (e = ENGINE_get_first(); e; e = ENGINE_get_next(e))
+        ENGINE_register_EC(e);
+}
+
+int ENGINE_set_default_EC(ENGINE *e)
+{
+    if (e->ec_meth != NULL)
+        return engine_table_register(&dh_table,
+                                     engine_unregister_all_EC, e, &dummy_nid,
+                                     1, 1);
+    return 1;
+}
+
+/*
+ * Exposed API function to get a functional reference from the implementation
+ * table (ie. try to get a functional reference from the tabled structural
+ * references).
+ */
+ENGINE *ENGINE_get_default_EC(void)
+{
+    return engine_table_select(&dh_table, dummy_nid);
+}
+
+/* Obtains an EC_KEY implementation from an ENGINE functional reference */
+const EC_KEY_METHOD *ENGINE_get_EC(const ENGINE *e)
+{
+    return e->ec_meth;
+}
+
+/* Sets an EC_KEY implementation in an ENGINE structure */
+int ENGINE_set_EC(ENGINE *e, const EC_KEY_METHOD *ec_meth)
+{
+    e->ec_meth = ec_meth;
+    return 1;
+}
diff --git a/crypto/engine/tb_pkmeth.c b/crypto/engine/tb_pkmeth.c
new file mode 100644
index 000000000..2e82d8551
--- /dev/null
+++ b/crypto/engine/tb_pkmeth.c
@@ -0,0 +1,114 @@
+/*
+ * Copyright 2006-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "eng_int.h"
+#include 
+
+static ENGINE_TABLE *pkey_meth_table = NULL;
+
+void ENGINE_unregister_pkey_meths(ENGINE *e)
+{
+    engine_table_unregister(&pkey_meth_table, e);
+}
+
+static void engine_unregister_all_pkey_meths(void)
+{
+    engine_table_cleanup(&pkey_meth_table);
+}
+
+int ENGINE_register_pkey_meths(ENGINE *e)
+{
+    if (e->pkey_meths) {
+        const int *nids;
+        int num_nids = e->pkey_meths(e, NULL, &nids, 0);
+        if (num_nids > 0)
+            return engine_table_register(&pkey_meth_table,
+                                         engine_unregister_all_pkey_meths, e,
+                                         nids, num_nids, 0);
+    }
+    return 1;
+}
+
+void ENGINE_register_all_pkey_meths()
+{
+    ENGINE *e;
+
+    for (e = ENGINE_get_first(); e; e = ENGINE_get_next(e))
+        ENGINE_register_pkey_meths(e);
+}
+
+int ENGINE_set_default_pkey_meths(ENGINE *e)
+{
+    if (e->pkey_meths) {
+        const int *nids;
+        int num_nids = e->pkey_meths(e, NULL, &nids, 0);
+        if (num_nids > 0)
+            return engine_table_register(&pkey_meth_table,
+                                         engine_unregister_all_pkey_meths, e,
+                                         nids, num_nids, 1);
+    }
+    return 1;
+}
+
+/*
+ * Exposed API function to get a functional reference from the implementation
+ * table (ie. try to get a functional reference from the tabled structural
+ * references) for a given pkey_meth 'nid'
+ */
+ENGINE *ENGINE_get_pkey_meth_engine(int nid)
+{
+    return engine_table_select(&pkey_meth_table, nid);
+}
+
+/* Obtains a pkey_meth implementation from an ENGINE functional reference */
+const EVP_PKEY_METHOD *ENGINE_get_pkey_meth(ENGINE *e, int nid)
+{
+    EVP_PKEY_METHOD *ret;
+    ENGINE_PKEY_METHS_PTR fn = ENGINE_get_pkey_meths(e);
+    if (!fn || !fn(e, &ret, NULL, nid)) {
+        ENGINEerr(ENGINE_F_ENGINE_GET_PKEY_METH,
+                  ENGINE_R_UNIMPLEMENTED_PUBLIC_KEY_METHOD);
+        return NULL;
+    }
+    return ret;
+}
+
+/* Gets the pkey_meth callback from an ENGINE structure */
+ENGINE_PKEY_METHS_PTR ENGINE_get_pkey_meths(const ENGINE *e)
+{
+    return e->pkey_meths;
+}
+
+/* Sets the pkey_meth callback in an ENGINE structure */
+int ENGINE_set_pkey_meths(ENGINE *e, ENGINE_PKEY_METHS_PTR f)
+{
+    e->pkey_meths = f;
+    return 1;
+}
+
+/*
+ * Internal function to free up EVP_PKEY_METHOD structures before an ENGINE
+ * is destroyed
+ */
+
+void engine_pkey_meths_free(ENGINE *e)
+{
+    int i;
+    EVP_PKEY_METHOD *pkm;
+    if (e->pkey_meths) {
+        const int *pknids;
+        int npknids;
+        npknids = e->pkey_meths(e, NULL, &pknids, 0);
+        for (i = 0; i < npknids; i++) {
+            if (e->pkey_meths(e, &pkm, NULL, pknids[i])) {
+                EVP_PKEY_meth_free(pkm);
+            }
+        }
+    }
+}
diff --git a/crypto/engine/tb_rand.c b/crypto/engine/tb_rand.c
new file mode 100644
index 000000000..225e7c81d
--- /dev/null
+++ b/crypto/engine/tb_rand.c
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2001-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "eng_int.h"
+
+static ENGINE_TABLE *rand_table = NULL;
+static const int dummy_nid = 1;
+
+void ENGINE_unregister_RAND(ENGINE *e)
+{
+    engine_table_unregister(&rand_table, e);
+}
+
+static void engine_unregister_all_RAND(void)
+{
+    engine_table_cleanup(&rand_table);
+}
+
+int ENGINE_register_RAND(ENGINE *e)
+{
+    if (e->rand_meth)
+        return engine_table_register(&rand_table,
+                                     engine_unregister_all_RAND, e,
+                                     &dummy_nid, 1, 0);
+    return 1;
+}
+
+void ENGINE_register_all_RAND()
+{
+    ENGINE *e;
+
+    for (e = ENGINE_get_first(); e; e = ENGINE_get_next(e))
+        ENGINE_register_RAND(e);
+}
+
+int ENGINE_set_default_RAND(ENGINE *e)
+{
+    if (e->rand_meth)
+        return engine_table_register(&rand_table,
+                                     engine_unregister_all_RAND, e,
+                                     &dummy_nid, 1, 1);
+    return 1;
+}
+
+/*
+ * Exposed API function to get a functional reference from the implementation
+ * table (ie. try to get a functional reference from the tabled structural
+ * references).
+ */
+ENGINE *ENGINE_get_default_RAND(void)
+{
+    return engine_table_select(&rand_table, dummy_nid);
+}
+
+/* Obtains an RAND implementation from an ENGINE functional reference */
+const RAND_METHOD *ENGINE_get_RAND(const ENGINE *e)
+{
+    return e->rand_meth;
+}
+
+/* Sets an RAND implementation in an ENGINE structure */
+int ENGINE_set_RAND(ENGINE *e, const RAND_METHOD *rand_meth)
+{
+    e->rand_meth = rand_meth;
+    return 1;
+}
diff --git a/crypto/engine/tb_rsa.c b/crypto/engine/tb_rsa.c
new file mode 100644
index 000000000..e2cc680a9
--- /dev/null
+++ b/crypto/engine/tb_rsa.c
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2001-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "eng_int.h"
+
+static ENGINE_TABLE *rsa_table = NULL;
+static const int dummy_nid = 1;
+
+void ENGINE_unregister_RSA(ENGINE *e)
+{
+    engine_table_unregister(&rsa_table, e);
+}
+
+static void engine_unregister_all_RSA(void)
+{
+    engine_table_cleanup(&rsa_table);
+}
+
+int ENGINE_register_RSA(ENGINE *e)
+{
+    if (e->rsa_meth)
+        return engine_table_register(&rsa_table,
+                                     engine_unregister_all_RSA, e, &dummy_nid,
+                                     1, 0);
+    return 1;
+}
+
+void ENGINE_register_all_RSA()
+{
+    ENGINE *e;
+
+    for (e = ENGINE_get_first(); e; e = ENGINE_get_next(e))
+        ENGINE_register_RSA(e);
+}
+
+int ENGINE_set_default_RSA(ENGINE *e)
+{
+    if (e->rsa_meth)
+        return engine_table_register(&rsa_table,
+                                     engine_unregister_all_RSA, e, &dummy_nid,
+                                     1, 1);
+    return 1;
+}
+
+/*
+ * Exposed API function to get a functional reference from the implementation
+ * table (ie. try to get a functional reference from the tabled structural
+ * references).
+ */
+ENGINE *ENGINE_get_default_RSA(void)
+{
+    return engine_table_select(&rsa_table, dummy_nid);
+}
+
+/* Obtains an RSA implementation from an ENGINE functional reference */
+const RSA_METHOD *ENGINE_get_RSA(const ENGINE *e)
+{
+    return e->rsa_meth;
+}
+
+/* Sets an RSA implementation in an ENGINE structure */
+int ENGINE_set_RSA(ENGINE *e, const RSA_METHOD *rsa_meth)
+{
+    e->rsa_meth = rsa_meth;
+    return 1;
+}
diff --git a/crypto/err/README b/crypto/err/README
new file mode 100644
index 000000000..6d2ce0cd0
--- /dev/null
+++ b/crypto/err/README
@@ -0,0 +1,44 @@
+Adding new libraries
+--------------------
+
+When adding a new sub-library to OpenSSL, assign it a library number
+ERR_LIB_XXX, define a macro XXXerr() (both in err.h), add its
+name to ERR_str_libraries[] (in crypto/err/err.c), and add
+ERR_load_XXX_strings() to the ERR_load_crypto_strings() function
+(in crypto/err/err_all.c). Finally, add an entry:
+
+    L      XXX     xxx.h   xxx_err.c
+
+to crypto/err/openssl.ec, and add xxx_err.c to the Makefile.
+Running make errors will then generate a file xxx_err.c, and
+add all error codes used in the library to xxx.h.
+
+Additionally the library include file must have a certain form.
+Typically it will initially look like this:
+
+    #ifndef HEADER_XXX_H
+    #define HEADER_XXX_H
+
+    #ifdef __cplusplus
+    extern "C" {
+    #endif
+
+    /* Include files */
+
+    #include 
+    #include 
+
+    /* Macros, structures and function prototypes */
+
+
+    /* BEGIN ERROR CODES */
+
+The BEGIN ERROR CODES sequence is used by the error code
+generation script as the point to place new error codes, any text
+after this point will be overwritten when make errors is run.
+The closing #endif etc will be automatically added by the script.
+
+The generated C error code file xxx_err.c will load the header
+files stdio.h, openssl/err.h and openssl/xxx.h so the
+header file must load any additional header files containing any
+definitions it uses.
diff --git a/crypto/err/build.info b/crypto/err/build.info
new file mode 100644
index 000000000..6163d95b7
--- /dev/null
+++ b/crypto/err/build.info
@@ -0,0 +1,3 @@
+LIBS=../../libcrypto
+SOURCE[../../libcrypto]=\
+        err.c err_all.c err_prn.c
diff --git a/crypto/err/err.c b/crypto/err/err.c
new file mode 100644
index 000000000..2e22a33db
--- /dev/null
+++ b/crypto/err/err.c
@@ -0,0 +1,786 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+static void err_load_strings(int lib, ERR_STRING_DATA *str);
+
+static void ERR_STATE_free(ERR_STATE *s);
+#ifndef OPENSSL_NO_ERR
+static ERR_STRING_DATA ERR_str_libraries[] = {
+    {ERR_PACK(ERR_LIB_NONE, 0, 0), "unknown library"},
+    {ERR_PACK(ERR_LIB_SYS, 0, 0), "system library"},
+    {ERR_PACK(ERR_LIB_BN, 0, 0), "bignum routines"},
+    {ERR_PACK(ERR_LIB_RSA, 0, 0), "rsa routines"},
+    {ERR_PACK(ERR_LIB_DH, 0, 0), "Diffie-Hellman routines"},
+    {ERR_PACK(ERR_LIB_EVP, 0, 0), "digital envelope routines"},
+    {ERR_PACK(ERR_LIB_BUF, 0, 0), "memory buffer routines"},
+    {ERR_PACK(ERR_LIB_OBJ, 0, 0), "object identifier routines"},
+    {ERR_PACK(ERR_LIB_PEM, 0, 0), "PEM routines"},
+    {ERR_PACK(ERR_LIB_DSA, 0, 0), "dsa routines"},
+    {ERR_PACK(ERR_LIB_X509, 0, 0), "x509 certificate routines"},
+    {ERR_PACK(ERR_LIB_ASN1, 0, 0), "asn1 encoding routines"},
+    {ERR_PACK(ERR_LIB_CONF, 0, 0), "configuration file routines"},
+    {ERR_PACK(ERR_LIB_CRYPTO, 0, 0), "common libcrypto routines"},
+    {ERR_PACK(ERR_LIB_EC, 0, 0), "elliptic curve routines"},
+    {ERR_PACK(ERR_LIB_ECDSA, 0, 0), "ECDSA routines"},
+    {ERR_PACK(ERR_LIB_ECDH, 0, 0), "ECDH routines"},
+    {ERR_PACK(ERR_LIB_SSL, 0, 0), "SSL routines"},
+    {ERR_PACK(ERR_LIB_BIO, 0, 0), "BIO routines"},
+    {ERR_PACK(ERR_LIB_PKCS7, 0, 0), "PKCS7 routines"},
+    {ERR_PACK(ERR_LIB_X509V3, 0, 0), "X509 V3 routines"},
+    {ERR_PACK(ERR_LIB_PKCS12, 0, 0), "PKCS12 routines"},
+    {ERR_PACK(ERR_LIB_RAND, 0, 0), "random number generator"},
+    {ERR_PACK(ERR_LIB_DSO, 0, 0), "DSO support routines"},
+    {ERR_PACK(ERR_LIB_TS, 0, 0), "time stamp routines"},
+    {ERR_PACK(ERR_LIB_ENGINE, 0, 0), "engine routines"},
+    {ERR_PACK(ERR_LIB_OCSP, 0, 0), "OCSP routines"},
+    {ERR_PACK(ERR_LIB_UI, 0, 0), "UI routines"},
+    {ERR_PACK(ERR_LIB_FIPS, 0, 0), "FIPS routines"},
+    {ERR_PACK(ERR_LIB_CMS, 0, 0), "CMS routines"},
+    {ERR_PACK(ERR_LIB_HMAC, 0, 0), "HMAC routines"},
+    {ERR_PACK(ERR_LIB_CT, 0, 0), "CT routines"},
+    {ERR_PACK(ERR_LIB_ASYNC, 0, 0), "ASYNC routines"},
+    {ERR_PACK(ERR_LIB_KDF, 0, 0), "KDF routines"},
+    {ERR_PACK(ERR_LIB_KDF2, 0, 0), "KDF2 routines"},
+    {ERR_PACK(ERR_LIB_PAILLIER, 0, 0), "PAILLIER routines"},
+    {ERR_PACK(ERR_LIB_OTP, 0, 0), "OTP routines"},
+    {ERR_PACK(ERR_LIB_GMAPI, 0, 0), "GMAPI routines"},
+    {ERR_PACK(ERR_LIB_SM2, 0, 0), "SM2 routines"},
+    {ERR_PACK(ERR_LIB_SM9, 0, 0), "SM9 routines"},
+    {ERR_PACK(ERR_LIB_SDF, 0, 0), "SDF routines"},
+    {ERR_PACK(ERR_LIB_SKF, 0, 0), "SKF routines"},
+    {0, NULL},
+};
+
+static ERR_STRING_DATA ERR_str_functs[] = {
+    {ERR_PACK(0, SYS_F_FOPEN, 0), "fopen"},
+    {ERR_PACK(0, SYS_F_CONNECT, 0), "connect"},
+    {ERR_PACK(0, SYS_F_GETSERVBYNAME, 0), "getservbyname"},
+    {ERR_PACK(0, SYS_F_SOCKET, 0), "socket"},
+    {ERR_PACK(0, SYS_F_IOCTLSOCKET, 0), "ioctlsocket"},
+    {ERR_PACK(0, SYS_F_BIND, 0), "bind"},
+    {ERR_PACK(0, SYS_F_LISTEN, 0), "listen"},
+    {ERR_PACK(0, SYS_F_ACCEPT, 0), "accept"},
+# ifdef OPENSSL_SYS_WINDOWS
+    {ERR_PACK(0, SYS_F_WSASTARTUP, 0), "WSAstartup"},
+# endif
+    {ERR_PACK(0, SYS_F_OPENDIR, 0), "opendir"},
+    {ERR_PACK(0, SYS_F_FREAD, 0), "fread"},
+    {ERR_PACK(0, SYS_F_GETADDRINFO, 0), "getaddrinfo"},
+    {ERR_PACK(0, SYS_F_GETNAMEINFO, 0), "getnameinfo"},
+    {ERR_PACK(0, SYS_F_SETSOCKOPT, 0), "setsockopt"},
+    {ERR_PACK(0, SYS_F_GETSOCKOPT, 0), "getsockopt"},
+    {ERR_PACK(0, SYS_F_GETSOCKNAME, 0), "getsockname"},
+    {ERR_PACK(0, SYS_F_GETHOSTBYNAME, 0), "gethostbyname"},
+    {0, NULL},
+};
+
+static ERR_STRING_DATA ERR_str_reasons[] = {
+    {ERR_R_SYS_LIB, "system lib"},
+    {ERR_R_BN_LIB, "BN lib"},
+    {ERR_R_RSA_LIB, "RSA lib"},
+    {ERR_R_DH_LIB, "DH lib"},
+    {ERR_R_EVP_LIB, "EVP lib"},
+    {ERR_R_BUF_LIB, "BUF lib"},
+    {ERR_R_OBJ_LIB, "OBJ lib"},
+    {ERR_R_PEM_LIB, "PEM lib"},
+    {ERR_R_DSA_LIB, "DSA lib"},
+    {ERR_R_X509_LIB, "X509 lib"},
+    {ERR_R_ASN1_LIB, "ASN1 lib"},
+    {ERR_R_EC_LIB, "EC lib"},
+    {ERR_R_BIO_LIB, "BIO lib"},
+    {ERR_R_PKCS7_LIB, "PKCS7 lib"},
+    {ERR_R_X509V3_LIB, "X509V3 lib"},
+    {ERR_R_ENGINE_LIB, "ENGINE lib"},
+    {ERR_R_ECDSA_LIB, "ECDSA lib"},
+    {ERR_R_KDF2_LIB, "KDF2 lib"},
+    {ERR_R_PAILLIER_LIB, "PAILLIER lib"},
+    {ERR_R_OTP_LIB, "OTP lib"},
+    {ERR_R_GMAPI_LIB, "GMAPI lib"},
+    {ERR_R_SM2_LIB, "SM2 lib"},
+    {ERR_R_SM9_LIB, "SM9 lib"},
+    {ERR_R_SDF_LIB, "SDF lib"},
+    {ERR_R_SKF_LIB, "SKF lib"},
+
+    {ERR_R_NESTED_ASN1_ERROR, "nested asn1 error"},
+    {ERR_R_MISSING_ASN1_EOS, "missing asn1 eos"},
+
+    {ERR_R_FATAL, "fatal"},
+    {ERR_R_MALLOC_FAILURE, "malloc failure"},
+    {ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED,
+     "called a function you should not call"},
+    {ERR_R_PASSED_NULL_PARAMETER, "passed a null parameter"},
+    {ERR_R_INTERNAL_ERROR, "internal error"},
+    {ERR_R_DISABLED, "called a function that was disabled at compile-time"},
+    {ERR_R_INIT_FAIL, "init fail"},
+
+    {0, NULL},
+};
+#endif
+
+static CRYPTO_ONCE err_init = CRYPTO_ONCE_STATIC_INIT;
+static CRYPTO_THREAD_LOCAL err_thread_local;
+
+static CRYPTO_ONCE err_string_init = CRYPTO_ONCE_STATIC_INIT;
+static CRYPTO_RWLOCK *err_string_lock;
+
+static ERR_STRING_DATA *int_err_get_item(const ERR_STRING_DATA *);
+
+/*
+ * The internal state
+ */
+
+static LHASH_OF(ERR_STRING_DATA) *int_error_hash = NULL;
+static int int_err_library_number = ERR_LIB_USER;
+
+static unsigned long get_error_values(int inc, int top, const char **file,
+                                      int *line, const char **data,
+                                      int *flags);
+
+static unsigned long err_string_data_hash(const ERR_STRING_DATA *a)
+{
+    unsigned long ret, l;
+
+    l = a->error;
+    ret = l ^ ERR_GET_LIB(l) ^ ERR_GET_FUNC(l);
+    return (ret ^ ret % 19 * 13);
+}
+
+static int err_string_data_cmp(const ERR_STRING_DATA *a,
+                               const ERR_STRING_DATA *b)
+{
+    return (int)(a->error - b->error);
+}
+
+static ERR_STRING_DATA *int_err_get_item(const ERR_STRING_DATA *d)
+{
+    ERR_STRING_DATA *p = NULL;
+
+    CRYPTO_THREAD_read_lock(err_string_lock);
+    if (int_error_hash != NULL)
+        p = lh_ERR_STRING_DATA_retrieve(int_error_hash, d);
+    CRYPTO_THREAD_unlock(err_string_lock);
+
+    return p;
+}
+
+#ifndef OPENSSL_NO_ERR
+# define NUM_SYS_STR_REASONS 127
+# define LEN_SYS_STR_REASON 32
+
+static ERR_STRING_DATA SYS_str_reasons[NUM_SYS_STR_REASONS + 1];
+/*
+ * SYS_str_reasons is filled with copies of strerror() results at
+ * initialization. 'errno' values up to 127 should cover all usual errors,
+ * others will be displayed numerically by ERR_error_string. It is crucial
+ * that we have something for each reason code that occurs in
+ * ERR_str_reasons, or bogus reason strings will be returned for SYSerr(),
+ * which always gets an errno value and never one of those 'standard' reason
+ * codes.
+ */
+
+static void build_SYS_str_reasons(void)
+{
+    /* OPENSSL_malloc cannot be used here, use static storage instead */
+    static char strerror_tab[NUM_SYS_STR_REASONS][LEN_SYS_STR_REASON];
+    static int init = 1;
+    int i;
+
+    CRYPTO_THREAD_write_lock(err_string_lock);
+    if (!init) {
+        CRYPTO_THREAD_unlock(err_string_lock);
+        return;
+    }
+
+    for (i = 1; i <= NUM_SYS_STR_REASONS; i++) {
+        ERR_STRING_DATA *str = &SYS_str_reasons[i - 1];
+
+        str->error = (unsigned long)i;
+        if (str->string == NULL) {
+            char (*dest)[LEN_SYS_STR_REASON] = &(strerror_tab[i - 1]);
+            if (openssl_strerror_r(i, *dest, sizeof(*dest)))
+                str->string = *dest;
+        }
+        if (str->string == NULL)
+            str->string = "unknown";
+    }
+
+    /*
+     * Now we still have SYS_str_reasons[NUM_SYS_STR_REASONS] = {0, NULL}, as
+     * required by ERR_load_strings.
+     */
+
+    init = 0;
+
+    CRYPTO_THREAD_unlock(err_string_lock);
+}
+#endif
+
+#define err_clear_data(p,i) \
+        do { \
+        if ((p)->err_data_flags[i] & ERR_TXT_MALLOCED) \
+                {  \
+                OPENSSL_free((p)->err_data[i]); \
+                (p)->err_data[i]=NULL; \
+                } \
+        (p)->err_data_flags[i]=0; \
+        } while(0)
+
+#define err_clear(p,i) \
+        do { \
+        (p)->err_flags[i]=0; \
+        (p)->err_buffer[i]=0; \
+        err_clear_data(p,i); \
+        (p)->err_file[i]=NULL; \
+        (p)->err_line[i]= -1; \
+        } while(0)
+
+static void ERR_STATE_free(ERR_STATE *s)
+{
+    int i;
+
+    if (s == NULL)
+        return;
+
+    for (i = 0; i < ERR_NUM_ERRORS; i++) {
+        err_clear_data(s, i);
+    }
+    OPENSSL_free(s);
+}
+
+DEFINE_RUN_ONCE_STATIC(do_err_strings_init)
+{
+    OPENSSL_init_crypto(0, NULL);
+    err_string_lock = CRYPTO_THREAD_lock_new();
+    return err_string_lock != NULL;
+}
+
+void err_cleanup(void)
+{
+    CRYPTO_THREAD_lock_free(err_string_lock);
+    err_string_lock = NULL;
+}
+
+int ERR_load_ERR_strings(void)
+{
+#ifndef OPENSSL_NO_ERR
+    if (!RUN_ONCE(&err_string_init, do_err_strings_init))
+        return 0;
+
+    err_load_strings(0, ERR_str_libraries);
+    err_load_strings(0, ERR_str_reasons);
+    err_load_strings(ERR_LIB_SYS, ERR_str_functs);
+    build_SYS_str_reasons();
+    err_load_strings(ERR_LIB_SYS, SYS_str_reasons);
+#endif
+    return 1;
+}
+
+static void err_load_strings(int lib, ERR_STRING_DATA *str)
+{
+    CRYPTO_THREAD_write_lock(err_string_lock);
+    if (int_error_hash == NULL)
+        int_error_hash = lh_ERR_STRING_DATA_new(err_string_data_hash,
+                                                err_string_data_cmp);
+    if (int_error_hash != NULL) {
+        for (; str->error; str++) {
+            if (lib)
+                str->error |= ERR_PACK(lib, 0, 0);
+            (void)lh_ERR_STRING_DATA_insert(int_error_hash, str);
+        }
+    }
+    CRYPTO_THREAD_unlock(err_string_lock);
+}
+
+int ERR_load_strings(int lib, ERR_STRING_DATA *str)
+{
+    if (ERR_load_ERR_strings() == 0)
+        return 0;
+    err_load_strings(lib, str);
+    return 1;
+}
+
+int ERR_unload_strings(int lib, ERR_STRING_DATA *str)
+{
+    if (!RUN_ONCE(&err_string_init, do_err_strings_init))
+        return 0;
+
+    CRYPTO_THREAD_write_lock(err_string_lock);
+    if (int_error_hash != NULL) {
+        for (; str->error; str++) {
+            if (lib)
+                str->error |= ERR_PACK(lib, 0, 0);
+            (void)lh_ERR_STRING_DATA_delete(int_error_hash, str);
+        }
+    }
+    CRYPTO_THREAD_unlock(err_string_lock);
+
+    return 1;
+}
+
+void err_free_strings_int(void)
+{
+    if (!RUN_ONCE(&err_string_init, do_err_strings_init))
+        return;
+
+    CRYPTO_THREAD_write_lock(err_string_lock);
+    lh_ERR_STRING_DATA_free(int_error_hash);
+    int_error_hash = NULL;
+    CRYPTO_THREAD_unlock(err_string_lock);
+}
+
+/********************************************************/
+
+void ERR_put_error(int lib, int func, int reason, const char *file, int line)
+{
+    ERR_STATE *es;
+
+#ifdef _OSD_POSIX
+    /*
+     * In the BS2000-OSD POSIX subsystem, the compiler generates path names
+     * in the form "*POSIX(/etc/passwd)". This dirty hack strips them to
+     * something sensible. @@@ We shouldn't modify a const string, though.
+     */
+    if (strncmp(file, "*POSIX(", sizeof("*POSIX(") - 1) == 0) {
+        char *end;
+
+        /* Skip the "*POSIX(" prefix */
+        file += sizeof("*POSIX(") - 1;
+        end = &file[strlen(file) - 1];
+        if (*end == ')')
+            *end = '\0';
+        /* Optional: use the basename of the path only. */
+        if ((end = strrchr(file, '/')) != NULL)
+            file = &end[1];
+    }
+#endif
+    es = ERR_get_state();
+
+    es->top = (es->top + 1) % ERR_NUM_ERRORS;
+    if (es->top == es->bottom)
+        es->bottom = (es->bottom + 1) % ERR_NUM_ERRORS;
+    es->err_flags[es->top] = 0;
+    es->err_buffer[es->top] = ERR_PACK(lib, func, reason);
+    es->err_file[es->top] = file;
+    es->err_line[es->top] = line;
+    err_clear_data(es, es->top);
+}
+
+void ERR_clear_error(void)
+{
+    int i;
+    ERR_STATE *es;
+
+    es = ERR_get_state();
+
+    for (i = 0; i < ERR_NUM_ERRORS; i++) {
+        err_clear(es, i);
+    }
+    es->top = es->bottom = 0;
+}
+
+unsigned long ERR_get_error(void)
+{
+    return (get_error_values(1, 0, NULL, NULL, NULL, NULL));
+}
+
+unsigned long ERR_get_error_line(const char **file, int *line)
+{
+    return (get_error_values(1, 0, file, line, NULL, NULL));
+}
+
+unsigned long ERR_get_error_line_data(const char **file, int *line,
+                                      const char **data, int *flags)
+{
+    return (get_error_values(1, 0, file, line, data, flags));
+}
+
+unsigned long ERR_peek_error(void)
+{
+    return (get_error_values(0, 0, NULL, NULL, NULL, NULL));
+}
+
+unsigned long ERR_peek_error_line(const char **file, int *line)
+{
+    return (get_error_values(0, 0, file, line, NULL, NULL));
+}
+
+unsigned long ERR_peek_error_line_data(const char **file, int *line,
+                                       const char **data, int *flags)
+{
+    return (get_error_values(0, 0, file, line, data, flags));
+}
+
+unsigned long ERR_peek_last_error(void)
+{
+    return (get_error_values(0, 1, NULL, NULL, NULL, NULL));
+}
+
+unsigned long ERR_peek_last_error_line(const char **file, int *line)
+{
+    return (get_error_values(0, 1, file, line, NULL, NULL));
+}
+
+unsigned long ERR_peek_last_error_line_data(const char **file, int *line,
+                                            const char **data, int *flags)
+{
+    return (get_error_values(0, 1, file, line, data, flags));
+}
+
+static unsigned long get_error_values(int inc, int top, const char **file,
+                                      int *line, const char **data,
+                                      int *flags)
+{
+    int i = 0;
+    ERR_STATE *es;
+    unsigned long ret;
+
+    es = ERR_get_state();
+
+    if (inc && top) {
+        if (file)
+            *file = "";
+        if (line)
+            *line = 0;
+        if (data)
+            *data = "";
+        if (flags)
+            *flags = 0;
+
+        return ERR_R_INTERNAL_ERROR;
+    }
+
+    if (es->bottom == es->top)
+        return 0;
+    if (top)
+        i = es->top;            /* last error */
+    else
+        i = (es->bottom + 1) % ERR_NUM_ERRORS; /* first error */
+
+    ret = es->err_buffer[i];
+    if (inc) {
+        es->bottom = i;
+        es->err_buffer[i] = 0;
+    }
+
+    if ((file != NULL) && (line != NULL)) {
+        if (es->err_file[i] == NULL) {
+            *file = "NA";
+            if (line != NULL)
+                *line = 0;
+        } else {
+            *file = es->err_file[i];
+            if (line != NULL)
+                *line = es->err_line[i];
+        }
+    }
+
+    if (data == NULL) {
+        if (inc) {
+            err_clear_data(es, i);
+        }
+    } else {
+        if (es->err_data[i] == NULL) {
+            *data = "";
+            if (flags != NULL)
+                *flags = 0;
+        } else {
+            *data = es->err_data[i];
+            if (flags != NULL)
+                *flags = es->err_data_flags[i];
+        }
+    }
+    return ret;
+}
+
+void ERR_error_string_n(unsigned long e, char *buf, size_t len)
+{
+    char lsbuf[64], fsbuf[64], rsbuf[64];
+    const char *ls, *fs, *rs;
+    unsigned long l, f, r;
+
+    if (len == 0)
+        return;
+
+    l = ERR_GET_LIB(e);
+    f = ERR_GET_FUNC(e);
+    r = ERR_GET_REASON(e);
+
+    ls = ERR_lib_error_string(e);
+    fs = ERR_func_error_string(e);
+    rs = ERR_reason_error_string(e);
+
+    if (ls == NULL)
+        BIO_snprintf(lsbuf, sizeof(lsbuf), "lib(%lu)", l);
+    if (fs == NULL)
+        BIO_snprintf(fsbuf, sizeof(fsbuf), "func(%lu)", f);
+    if (rs == NULL)
+        BIO_snprintf(rsbuf, sizeof(rsbuf), "reason(%lu)", r);
+
+    BIO_snprintf(buf, len, "error:%08lX:%s:%s:%s", e, ls ? ls : lsbuf,
+                 fs ? fs : fsbuf, rs ? rs : rsbuf);
+    if (strlen(buf) == len - 1) {
+        /*
+         * output may be truncated; make sure we always have 5
+         * colon-separated fields, i.e. 4 colons ...
+         */
+#define NUM_COLONS 4
+        if (len > NUM_COLONS) { /* ... if possible */
+            int i;
+            char *s = buf;
+
+            for (i = 0; i < NUM_COLONS; i++) {
+                char *colon = strchr(s, ':');
+                if (colon == NULL || colon > &buf[len - 1] - NUM_COLONS + i) {
+                    /*
+                     * set colon no. i at last possible position (buf[len-1]
+                     * is the terminating 0)
+                     */
+                    colon = &buf[len - 1] - NUM_COLONS + i;
+                    *colon = ':';
+                }
+                s = colon + 1;
+            }
+        }
+    }
+}
+
+/*
+ * ERR_error_string_n should be used instead for ret != NULL as
+ * ERR_error_string cannot know how large the buffer is
+ */
+char *ERR_error_string(unsigned long e, char *ret)
+{
+    static char buf[256];
+
+    if (ret == NULL)
+        ret = buf;
+    ERR_error_string_n(e, ret, 256);
+
+    return ret;
+}
+
+const char *ERR_lib_error_string(unsigned long e)
+{
+    ERR_STRING_DATA d, *p;
+    unsigned long l;
+
+    if (!RUN_ONCE(&err_string_init, do_err_strings_init)) {
+        return NULL;
+    }
+
+    l = ERR_GET_LIB(e);
+    d.error = ERR_PACK(l, 0, 0);
+    p = int_err_get_item(&d);
+    return ((p == NULL) ? NULL : p->string);
+}
+
+const char *ERR_func_error_string(unsigned long e)
+{
+    ERR_STRING_DATA d, *p;
+    unsigned long l, f;
+
+    if (!RUN_ONCE(&err_string_init, do_err_strings_init)) {
+        return NULL;
+    }
+
+    l = ERR_GET_LIB(e);
+    f = ERR_GET_FUNC(e);
+    d.error = ERR_PACK(l, f, 0);
+    p = int_err_get_item(&d);
+    return ((p == NULL) ? NULL : p->string);
+}
+
+const char *ERR_reason_error_string(unsigned long e)
+{
+    ERR_STRING_DATA d, *p = NULL;
+    unsigned long l, r;
+
+    if (!RUN_ONCE(&err_string_init, do_err_strings_init)) {
+        return NULL;
+    }
+
+    l = ERR_GET_LIB(e);
+    r = ERR_GET_REASON(e);
+    d.error = ERR_PACK(l, 0, r);
+    p = int_err_get_item(&d);
+    if (!p) {
+        d.error = ERR_PACK(0, 0, r);
+        p = int_err_get_item(&d);
+    }
+    return ((p == NULL) ? NULL : p->string);
+}
+
+void err_delete_thread_state(void)
+{
+    ERR_STATE *state = ERR_get_state();
+    if (state == NULL)
+        return;
+
+    CRYPTO_THREAD_set_local(&err_thread_local, NULL);
+    ERR_STATE_free(state);
+}
+
+#if OPENSSL_API_COMPAT < 0x10100000L
+void ERR_remove_thread_state(void *dummy)
+{
+}
+#endif
+
+#if OPENSSL_API_COMPAT < 0x10000000L
+void ERR_remove_state(unsigned long pid)
+{
+}
+#endif
+
+DEFINE_RUN_ONCE_STATIC(err_do_init)
+{
+    return CRYPTO_THREAD_init_local(&err_thread_local, NULL);
+}
+
+ERR_STATE *ERR_get_state(void)
+{
+    ERR_STATE *state = NULL;
+
+    if (!RUN_ONCE(&err_init, err_do_init))
+        return NULL;
+
+    state = CRYPTO_THREAD_get_local(&err_thread_local);
+
+    if (state == NULL) {
+        state = OPENSSL_zalloc(sizeof(*state));
+        if (state == NULL)
+            return NULL;
+
+        if (!CRYPTO_THREAD_set_local(&err_thread_local, state)) {
+            ERR_STATE_free(state);
+            return NULL;
+        }
+
+        /* Ignore failures from these */
+        OPENSSL_init_crypto(OPENSSL_INIT_LOAD_CRYPTO_STRINGS, NULL);
+        ossl_init_thread_start(OPENSSL_INIT_THREAD_ERR_STATE);
+    }
+
+    return state;
+}
+
+int ERR_get_next_error_library(void)
+{
+    int ret;
+
+    if (!RUN_ONCE(&err_string_init, do_err_strings_init)) {
+        return 0;
+    }
+
+    CRYPTO_THREAD_write_lock(err_string_lock);
+    ret = int_err_library_number++;
+    CRYPTO_THREAD_unlock(err_string_lock);
+    return ret;
+}
+
+void ERR_set_error_data(char *data, int flags)
+{
+    ERR_STATE *es;
+    int i;
+
+    es = ERR_get_state();
+
+    i = es->top;
+    if (i == 0)
+        i = ERR_NUM_ERRORS - 1;
+
+    err_clear_data(es, i);
+    es->err_data[i] = data;
+    es->err_data_flags[i] = flags;
+}
+
+void ERR_add_error_data(int num, ...)
+{
+    va_list args;
+    va_start(args, num);
+    ERR_add_error_vdata(num, args);
+    va_end(args);
+}
+
+void ERR_add_error_vdata(int num, va_list args)
+{
+    int i, n, s;
+    char *str, *p, *a;
+
+    s = 80;
+    str = OPENSSL_malloc(s + 1);
+    if (str == NULL)
+        return;
+    str[0] = '\0';
+
+    n = 0;
+    for (i = 0; i < num; i++) {
+        a = va_arg(args, char *);
+        /* ignore NULLs, thanks to Bob Beck  */
+        if (a != NULL) {
+            n += strlen(a);
+            if (n > s) {
+                s = n + 20;
+                p = OPENSSL_realloc(str, s + 1);
+                if (p == NULL) {
+                    OPENSSL_free(str);
+                    return;
+                }
+                str = p;
+            }
+            OPENSSL_strlcat(str, a, (size_t)s + 1);
+        }
+    }
+    ERR_set_error_data(str, ERR_TXT_MALLOCED | ERR_TXT_STRING);
+}
+
+int ERR_set_mark(void)
+{
+    ERR_STATE *es;
+
+    es = ERR_get_state();
+
+    if (es->bottom == es->top)
+        return 0;
+    es->err_flags[es->top] |= ERR_FLAG_MARK;
+    return 1;
+}
+
+int ERR_pop_to_mark(void)
+{
+    ERR_STATE *es;
+
+    es = ERR_get_state();
+
+    while (es->bottom != es->top
+           && (es->err_flags[es->top] & ERR_FLAG_MARK) == 0) {
+        err_clear(es, es->top);
+        es->top -= 1;
+        if (es->top == -1)
+            es->top = ERR_NUM_ERRORS - 1;
+    }
+
+    if (es->bottom == es->top)
+        return 0;
+    es->err_flags[es->top] &= ~ERR_FLAG_MARK;
+    return 1;
+}
diff --git a/crypto/err/err_all.c b/crypto/err/err_all.c
new file mode 100644
index 000000000..7028f3b00
--- /dev/null
+++ b/crypto/err/err_all.c
@@ -0,0 +1,184 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+
+#include "internal/err_int.h"
+#include 
+#include 
+#ifndef OPENSSL_NO_EC
+# include 
+#endif
+#include 
+#include 
+#ifndef OPENSSL_NO_COMP
+# include 
+#endif
+#ifndef OPENSSL_NO_RSA
+# include 
+#endif
+#ifndef OPENSSL_NO_DH
+# include 
+#endif
+#ifndef OPENSSL_NO_DSA
+# include 
+#endif
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include "internal/dso.h"
+#ifndef OPENSSL_NO_ENGINE
+# include 
+#endif
+#ifndef OPENSSL_NO_UI
+# include 
+#endif
+#ifndef OPENSSL_NO_OCSP
+# include 
+#endif
+#include 
+#ifdef OPENSSL_FIPS
+# include 
+#endif
+#ifndef OPENSSL_NO_TS
+# include 
+#endif
+#ifndef OPENSSL_NO_CMS
+# include 
+#endif
+#ifndef OPENSSL_NO_CT
+# include 
+#endif
+#ifndef OPENSSL_NO_ASYNC
+# include 
+#endif
+#include 
+#include 
+#ifndef OPENSSL_NO_PAILLIER
+# include 
+#endif
+#ifndef OPENSSL_NO_OTP
+# include 
+#endif
+#ifndef OPENSSL_NO_GMAPI
+# include 
+#endif
+#ifndef OPENSSL_NO_SM2
+# include 
+#endif
+#ifndef OPENSSL_NO_SM9
+# include 
+#endif
+#ifndef OPENSSL_NO_SDF
+# include 
+#endif
+#ifndef OPENSSL_NO_SKF
+# include 
+#endif
+
+
+int err_load_crypto_strings_int(void)
+{
+    if (
+#ifdef OPENSSL_FIPS
+        FIPS_set_error_callbacks(ERR_put_error, ERR_add_error_vdata) == 0 ||
+#endif
+#ifndef OPENSSL_NO_ERR
+        ERR_load_ERR_strings() == 0 ||    /* include error strings for SYSerr */
+        ERR_load_BN_strings() == 0 ||
+# ifndef OPENSSL_NO_RSA
+        ERR_load_RSA_strings() == 0 ||
+# endif
+# ifndef OPENSSL_NO_DH
+        ERR_load_DH_strings() == 0 ||
+# endif
+        ERR_load_EVP_strings() == 0 ||
+        ERR_load_BUF_strings() == 0 ||
+        ERR_load_OBJ_strings() == 0 ||
+        ERR_load_PEM_strings() == 0 ||
+# ifndef OPENSSL_NO_DSA
+        ERR_load_DSA_strings() == 0 ||
+# endif
+        ERR_load_X509_strings() == 0 ||
+        ERR_load_ASN1_strings() == 0 ||
+        ERR_load_CONF_strings() == 0 ||
+        ERR_load_CRYPTO_strings() == 0 ||
+# ifndef OPENSSL_NO_COMP
+        ERR_load_COMP_strings() == 0 ||
+# endif
+# ifndef OPENSSL_NO_EC
+        ERR_load_EC_strings() == 0 ||
+# endif
+        /* skip ERR_load_SSL_strings() because it is not in this library */
+        ERR_load_BIO_strings() == 0 ||
+        ERR_load_PKCS7_strings() == 0 ||
+        ERR_load_X509V3_strings() == 0 ||
+# ifndef OPENSSL_NO_PKCS12
+        ERR_load_PKCS12_strings() == 0 ||
+# endif
+        ERR_load_RAND_strings() == 0 ||
+        ERR_load_DSO_strings() == 0 ||
+# ifndef OPENSSL_NO_TS
+        ERR_load_TS_strings() == 0 ||
+# endif
+# ifndef OPENSSL_NO_ENGINE
+        ERR_load_ENGINE_strings() == 0 ||
+# endif
+# ifndef OPENSSL_NO_OCSP
+        ERR_load_OCSP_strings() == 0 ||
+# endif
+#ifndef OPENSSL_NO_UI
+        ERR_load_UI_strings() == 0 ||
+#endif
+# ifdef OPENSSL_FIPS
+        ERR_load_FIPS_strings() == 0 ||
+# endif
+# ifndef OPENSSL_NO_CMS
+        ERR_load_CMS_strings() == 0 ||
+# endif
+# ifndef OPENSSL_NO_CT
+        ERR_load_CT_strings() == 0 ||
+# endif
+        ERR_load_ASYNC_strings() == 0 ||
+# ifndef OPENSSL_NO_KDF2
+        ERR_load_KDF2_strings() == 0 ||
+# endif
+# ifndef OPENSSL_NO_PAILLIER
+        ERR_load_PAILLIER_strings() == 0 ||
+# endif
+# ifndef OPENSSL_NO_OTP
+        ERR_load_OTP_strings() == 0 ||
+# endif
+# ifndef OPENSSL_NO_GMAPI
+        ERR_load_GMAPI_strings() == 0 ||
+# endif
+# ifndef OPENSSL_NO_SM2
+        ERR_load_SM2_strings() == 0 ||
+# endif
+# ifndef OPENSSL_NO_SM9
+        ERR_load_SM9_strings() == 0 ||
+# endif
+# ifndef OPENSSL_NO_SDF
+        ERR_load_SDF_strings() == 0 ||
+# endif
+# ifndef OPENSSL_NO_SKF
+        ERR_load_SKF_strings() == 0 ||
+# endif
+#endif
+        ERR_load_KDF_strings() == 0)
+        return 0;
+
+    return 1;
+}
diff --git a/crypto/err/err_prn.c b/crypto/err/err_prn.c
new file mode 100644
index 000000000..c7dc1d151
--- /dev/null
+++ b/crypto/err/err_prn.c
@@ -0,0 +1,66 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+#include 
+#include 
+
+void ERR_print_errors_cb(int (*cb) (const char *str, size_t len, void *u),
+                         void *u)
+{
+    unsigned long l;
+    char buf[256];
+    char buf2[4096];
+    const char *file, *data;
+    int line, flags;
+    /*
+     * We don't know what kind of thing CRYPTO_THREAD_ID is. Here is our best
+     * attempt to convert it into something we can print.
+     */
+    union {
+        CRYPTO_THREAD_ID tid;
+        unsigned long ltid;
+    } tid;
+
+    tid.ltid = 0;
+    tid.tid = CRYPTO_THREAD_get_current_id();
+
+    while ((l = ERR_get_error_line_data(&file, &line, &data, &flags)) != 0) {
+        ERR_error_string_n(l, buf, sizeof buf);
+        BIO_snprintf(buf2, sizeof(buf2), "%lu:%s:%s:%d:%s\n", tid.ltid, buf,
+                     file, line, (flags & ERR_TXT_STRING) ? data : "");
+        if (cb(buf2, strlen(buf2), u) <= 0)
+            break;              /* abort outputting the error report */
+    }
+}
+
+static int print_bio(const char *str, size_t len, void *bp)
+{
+    return BIO_write((BIO *)bp, str, len);
+}
+
+void ERR_print_errors(BIO *bp)
+{
+    ERR_print_errors_cb(print_bio, bp);
+}
+
+#ifndef OPENSSL_NO_STDIO
+void ERR_print_errors_fp(FILE *fp)
+{
+    BIO *bio = BIO_new_fp(fp, BIO_NOCLOSE);
+    if (bio == NULL)
+        return;
+
+    ERR_print_errors_cb(print_bio, bio);
+    BIO_free(bio);
+}
+#endif
diff --git a/crypto/err/openssl.ec b/crypto/err/openssl.ec
new file mode 100644
index 000000000..446136555
--- /dev/null
+++ b/crypto/err/openssl.ec
@@ -0,0 +1,108 @@
+# crypto/err/openssl.ec
+
+# configuration file for util/mkerr.pl
+
+# files that may have to be rewritten by util/mkerr.pl
+L ERR		NONE				NONE
+L BN		include/openssl/bn.h		crypto/bn/bn_err.c
+L RSA		include/openssl/rsa.h		crypto/rsa/rsa_err.c
+L DH		include/openssl/dh.h		crypto/dh/dh_err.c
+L EVP		include/openssl/evp.h		crypto/evp/evp_err.c
+L BUF		include/openssl/buffer.h	crypto/buffer/buf_err.c
+L OBJ		include/openssl/objects.h	crypto/objects/obj_err.c
+L PEM		include/openssl/pem.h		crypto/pem/pem_err.c
+L DSA		include/openssl/dsa.h		crypto/dsa/dsa_err.c
+L X509		include/openssl/x509.h		crypto/x509/x509_err.c
+L ASN1		include/openssl/asn1.h		crypto/asn1/asn1_err.c
+L CONF		include/openssl/conf.h		crypto/conf/conf_err.c
+L CRYPTO	include/openssl/crypto.h	crypto/cpt_err.c
+L EC		include/openssl/ec.h		crypto/ec/ec_err.c
+L SSL		include/openssl/ssl.h		ssl/ssl_err.c
+L BIO		include/openssl/bio.h		crypto/bio/bio_err.c
+L PKCS7		include/openssl/pkcs7.h		crypto/pkcs7/pkcs7err.c
+L X509V3	include/openssl/x509v3.h	crypto/x509v3/v3err.c
+L PKCS12	include/openssl/pkcs12.h	crypto/pkcs12/pk12err.c
+L RAND		include/openssl/rand.h		crypto/rand/rand_err.c
+L DSO		include/internal/dso.h		crypto/dso/dso_err.c
+L ENGINE	include/openssl/engine.h	crypto/engine/eng_err.c
+L OCSP		include/openssl/ocsp.h		crypto/ocsp/ocsp_err.c
+L UI		include/openssl/ui.h		crypto/ui/ui_err.c
+L COMP		include/openssl/comp.h		crypto/comp/comp_err.c
+L TS		include/openssl/ts.h		crypto/ts/ts_err.c
+#L HMAC		include/openssl/hmac.h		crypto/hmac/hmac_err.c
+L CMS		include/openssl/cms.h		crypto/cms/cms_err.c
+#L FIPS		include/openssl/fips.h		crypto/fips_err.h
+L CT		include/openssl/ct.h		crypto/ct/ct_err.c
+L ASYNC		include/openssl/async.h		crypto/async/async_err.c
+L KDF		include/openssl/kdf.h		crypto/kdf/kdf_err.c
+L KDF2		include/openssl/kdf2.h		crypto/kdf2/kdf2_err.c
+L PAILLIER	include/openssl/paillier.h	crypto/paillier/pai_err.c
+L OTP		include/openssl/otp.h		crypto/otp/otp_err.c
+L GMAPI		include/openssl/gmapi.h		crypto/gmapi/gmapi_err.c
+L SM2		include/openssl/sm2.h		crypto/sm2/sm2_err.c
+L SM9		include/openssl/sm9.h		crypto/sm9/sm9_err.c
+L SDF		include/openssl/gmsdf.h		crypto/sdf/sdf_err.c
+L SKF		include/openssl/gmskf.h		crypto/skf/skf_err.c
+
+# additional header files to be scanned for function names
+L NONE		crypto/x509/x509_vfy.h		NONE
+L NONE		crypto/ec/ec_lcl.h		NONE
+L NONE		crypto/asn1/asn_lcl.h		NONE
+L NONE		crypto/cms/cms_lcl.h		NONE
+L NONE		crypto/ct/ct_locl.h		NONE
+L NONE		fips/rand/fips_rand.h		NONE
+L NONE		ssl/ssl_locl.h			NONE
+
+F RSAREF_F_RSA_BN2BIN
+F RSAREF_F_RSA_PRIVATE_DECRYPT
+F RSAREF_F_RSA_PRIVATE_ENCRYPT
+F RSAREF_F_RSA_PUBLIC_DECRYPT
+F RSAREF_F_RSA_PUBLIC_ENCRYPT
+
+R SSL_R_SSLV3_ALERT_UNEXPECTED_MESSAGE         1010
+R SSL_R_SSLV3_ALERT_BAD_RECORD_MAC             1020
+R SSL_R_TLSV1_ALERT_DECRYPTION_FAILED          1021
+R SSL_R_TLSV1_ALERT_RECORD_OVERFLOW            1022
+R SSL_R_SSLV3_ALERT_DECOMPRESSION_FAILURE      1030
+R SSL_R_SSLV3_ALERT_HANDSHAKE_FAILURE          1040
+R SSL_R_SSLV3_ALERT_NO_CERTIFICATE             1041
+R SSL_R_SSLV3_ALERT_BAD_CERTIFICATE            1042
+R SSL_R_SSLV3_ALERT_UNSUPPORTED_CERTIFICATE    1043
+R SSL_R_SSLV3_ALERT_CERTIFICATE_REVOKED        1044
+R SSL_R_SSLV3_ALERT_CERTIFICATE_EXPIRED        1045
+R SSL_R_SSLV3_ALERT_CERTIFICATE_UNKNOWN        1046
+R SSL_R_SSLV3_ALERT_ILLEGAL_PARAMETER          1047
+R SSL_R_TLSV1_ALERT_UNKNOWN_CA                 1048
+R SSL_R_TLSV1_ALERT_ACCESS_DENIED              1049
+R SSL_R_TLSV1_ALERT_DECODE_ERROR               1050
+R SSL_R_TLSV1_ALERT_DECRYPT_ERROR              1051
+R SSL_R_TLSV1_ALERT_EXPORT_RESTRICTION         1060
+R SSL_R_TLSV1_ALERT_PROTOCOL_VERSION           1070
+R SSL_R_TLSV1_ALERT_INSUFFICIENT_SECURITY      1071
+R SSL_R_TLSV1_ALERT_INTERNAL_ERROR             1080
+R SSL_R_TLSV1_ALERT_INAPPROPRIATE_FALLBACK     1086
+R SSL_R_TLSV1_ALERT_USER_CANCELLED             1090
+R SSL_R_TLSV1_ALERT_NO_RENEGOTIATION           1100
+R SSL_R_TLSV1_UNSUPPORTED_EXTENSION            1110
+R SSL_R_TLSV1_CERTIFICATE_UNOBTAINABLE         1111
+R SSL_R_TLSV1_UNRECOGNIZED_NAME                1112
+R SSL_R_TLSV1_BAD_CERTIFICATE_STATUS_RESPONSE  1113
+R SSL_R_TLSV1_BAD_CERTIFICATE_HASH_VALUE       1114
+R TLS1_AD_UNKNOWN_PSK_IDENTITY                 1115
+R TLS1_AD_NO_APPLICATION_PROTOCOL              1120
+
+R RSAREF_R_CONTENT_ENCODING			0x0400
+R RSAREF_R_DATA					0x0401
+R RSAREF_R_DIGEST_ALGORITHM			0x0402
+R RSAREF_R_ENCODING				0x0403
+R RSAREF_R_KEY					0x0404
+R RSAREF_R_KEY_ENCODING				0x0405
+R RSAREF_R_LEN					0x0406
+R RSAREF_R_MODULUS_LEN				0x0407
+R RSAREF_R_NEED_RANDOM				0x0408
+R RSAREF_R_PRIVATE_KEY				0x0409
+R RSAREF_R_PUBLIC_KEY				0x040a
+R RSAREF_R_SIGNATURE				0x040b
+R RSAREF_R_SIGNATURE_ENCODING			0x040c
+R RSAREF_R_ENCRYPTION_ALGORITHM			0x040d
+
diff --git a/crypto/evp/bio_b64.c b/crypto/evp/bio_b64.c
new file mode 100644
index 000000000..32a884a71
--- /dev/null
+++ b/crypto/evp/bio_b64.c
@@ -0,0 +1,542 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+#include "internal/bio.h"
+
+static int b64_write(BIO *h, const char *buf, int num);
+static int b64_read(BIO *h, char *buf, int size);
+static int b64_puts(BIO *h, const char *str);
+/*
+ * static int b64_gets(BIO *h, char *str, int size);
+ */
+static long b64_ctrl(BIO *h, int cmd, long arg1, void *arg2);
+static int b64_new(BIO *h);
+static int b64_free(BIO *data);
+static long b64_callback_ctrl(BIO *h, int cmd, bio_info_cb *fp);
+#define B64_BLOCK_SIZE  1024
+#define B64_BLOCK_SIZE2 768
+#define B64_NONE        0
+#define B64_ENCODE      1
+#define B64_DECODE      2
+
+typedef struct b64_struct {
+    /*
+     * BIO *bio; moved to the BIO structure
+     */
+    int buf_len;
+    int buf_off;
+    int tmp_len;                /* used to find the start when decoding */
+    int tmp_nl;                 /* If true, scan until '\n' */
+    int encode;
+    int start;                  /* have we started decoding yet? */
+    int cont;                   /* <= 0 when finished */
+    EVP_ENCODE_CTX *base64;
+    char buf[EVP_ENCODE_LENGTH(B64_BLOCK_SIZE) + 10];
+    char tmp[B64_BLOCK_SIZE];
+} BIO_B64_CTX;
+
+static const BIO_METHOD methods_b64 = {
+    BIO_TYPE_BASE64, "base64 encoding",
+    b64_write,
+    b64_read,
+    b64_puts,
+    NULL,                       /* b64_gets, */
+    b64_ctrl,
+    b64_new,
+    b64_free,
+    b64_callback_ctrl,
+};
+
+
+const BIO_METHOD *BIO_f_base64(void)
+{
+    return &methods_b64;
+}
+
+static int b64_new(BIO *bi)
+{
+    BIO_B64_CTX *ctx;
+
+    ctx = OPENSSL_zalloc(sizeof(*ctx));
+    if (ctx == NULL)
+        return 0;
+
+    ctx->cont = 1;
+    ctx->start = 1;
+    ctx->base64 = EVP_ENCODE_CTX_new();
+    if (ctx->base64 == NULL) {
+        OPENSSL_free(ctx);
+        return 0;
+    }
+
+    BIO_set_data(bi, ctx);
+    BIO_set_init(bi, 1);
+
+    return 1;
+}
+
+static int b64_free(BIO *a)
+{
+    BIO_B64_CTX *ctx;
+    if (a == NULL)
+        return 0;
+
+    ctx = BIO_get_data(a);
+    if (ctx == NULL)
+        return 0;
+
+    EVP_ENCODE_CTX_free(ctx->base64);
+    OPENSSL_free(ctx);
+    BIO_set_data(a, NULL);
+    BIO_set_init(a, 0);
+
+    return 1;
+}
+
+static int b64_read(BIO *b, char *out, int outl)
+{
+    int ret = 0, i, ii, j, k, x, n, num, ret_code = 0;
+    BIO_B64_CTX *ctx;
+    unsigned char *p, *q;
+    BIO *next;
+
+    if (out == NULL)
+        return (0);
+    ctx = (BIO_B64_CTX *)BIO_get_data(b);
+
+    next = BIO_next(b);
+    if ((ctx == NULL) || (next == NULL))
+        return 0;
+
+    BIO_clear_retry_flags(b);
+
+    if (ctx->encode != B64_DECODE) {
+        ctx->encode = B64_DECODE;
+        ctx->buf_len = 0;
+        ctx->buf_off = 0;
+        ctx->tmp_len = 0;
+        EVP_DecodeInit(ctx->base64);
+    }
+
+    /* First check if there are bytes decoded/encoded */
+    if (ctx->buf_len > 0) {
+        OPENSSL_assert(ctx->buf_len >= ctx->buf_off);
+        i = ctx->buf_len - ctx->buf_off;
+        if (i > outl)
+            i = outl;
+        OPENSSL_assert(ctx->buf_off + i < (int)sizeof(ctx->buf));
+        memcpy(out, &(ctx->buf[ctx->buf_off]), i);
+        ret = i;
+        out += i;
+        outl -= i;
+        ctx->buf_off += i;
+        if (ctx->buf_len == ctx->buf_off) {
+            ctx->buf_len = 0;
+            ctx->buf_off = 0;
+        }
+    }
+
+    /*
+     * At this point, we have room of outl bytes and an empty buffer, so we
+     * should read in some more.
+     */
+
+    ret_code = 0;
+    while (outl > 0) {
+        if (ctx->cont <= 0)
+            break;
+
+        i = BIO_read(next, &(ctx->tmp[ctx->tmp_len]),
+                     B64_BLOCK_SIZE - ctx->tmp_len);
+
+        if (i <= 0) {
+            ret_code = i;
+
+            /* Should we continue next time we are called? */
+            if (!BIO_should_retry(next)) {
+                ctx->cont = i;
+                /* If buffer empty break */
+                if (ctx->tmp_len == 0)
+                    break;
+                /* Fall through and process what we have */
+                else
+                    i = 0;
+            }
+            /* else we retry and add more data to buffer */
+            else
+                break;
+        }
+        i += ctx->tmp_len;
+        ctx->tmp_len = i;
+
+        /*
+         * We need to scan, a line at a time until we have a valid line if we
+         * are starting.
+         */
+        if (ctx->start && (BIO_get_flags(b) & BIO_FLAGS_BASE64_NO_NL)) {
+            /* ctx->start=1; */
+            ctx->tmp_len = 0;
+        } else if (ctx->start) {
+            q = p = (unsigned char *)ctx->tmp;
+            num = 0;
+            for (j = 0; j < i; j++) {
+                if (*(q++) != '\n')
+                    continue;
+
+                /*
+                 * due to a previous very long line, we need to keep on
+                 * scanning for a '\n' before we even start looking for
+                 * base64 encoded stuff.
+                 */
+                if (ctx->tmp_nl) {
+                    p = q;
+                    ctx->tmp_nl = 0;
+                    continue;
+                }
+
+                k = EVP_DecodeUpdate(ctx->base64,
+                                     (unsigned char *)ctx->buf,
+                                     &num, p, q - p);
+                if ((k <= 0) && (num == 0) && (ctx->start))
+                    EVP_DecodeInit(ctx->base64);
+                else {
+                    if (p != (unsigned char *)
+                        &(ctx->tmp[0])) {
+                        i -= (p - (unsigned char *)
+                              &(ctx->tmp[0]));
+                        for (x = 0; x < i; x++)
+                            ctx->tmp[x] = p[x];
+                    }
+                    EVP_DecodeInit(ctx->base64);
+                    ctx->start = 0;
+                    break;
+                }
+                p = q;
+            }
+
+            /* we fell off the end without starting */
+            if ((j == i) && (num == 0)) {
+                /*
+                 * Is this is one long chunk?, if so, keep on reading until a
+                 * new line.
+                 */
+                if (p == (unsigned char *)&(ctx->tmp[0])) {
+                    /* Check buffer full */
+                    if (i == B64_BLOCK_SIZE) {
+                        ctx->tmp_nl = 1;
+                        ctx->tmp_len = 0;
+                    }
+                } else if (p != q) { /* finished on a '\n' */
+                    n = q - p;
+                    for (ii = 0; ii < n; ii++)
+                        ctx->tmp[ii] = p[ii];
+                    ctx->tmp_len = n;
+                }
+                /* else finished on a '\n' */
+                continue;
+            } else {
+                ctx->tmp_len = 0;
+            }
+        } else if ((i < B64_BLOCK_SIZE) && (ctx->cont > 0)) {
+            /*
+             * If buffer isn't full and we can retry then restart to read in
+             * more data.
+             */
+            continue;
+        }
+
+        if (BIO_get_flags(b) & BIO_FLAGS_BASE64_NO_NL) {
+            int z, jj;
+
+            jj = i & ~3;        /* process per 4 */
+            z = EVP_DecodeBlock((unsigned char *)ctx->buf,
+                                (unsigned char *)ctx->tmp, jj);
+            if (jj > 2) {
+                if (ctx->tmp[jj - 1] == '=') {
+                    z--;
+                    if (ctx->tmp[jj - 2] == '=')
+                        z--;
+                }
+            }
+            /*
+             * z is now number of output bytes and jj is the number consumed
+             */
+            if (jj != i) {
+                memmove(ctx->tmp, &ctx->tmp[jj], i - jj);
+                ctx->tmp_len = i - jj;
+            }
+            ctx->buf_len = 0;
+            if (z > 0) {
+                ctx->buf_len = z;
+            }
+            i = z;
+        } else {
+            i = EVP_DecodeUpdate(ctx->base64,
+                                 (unsigned char *)ctx->buf, &ctx->buf_len,
+                                 (unsigned char *)ctx->tmp, i);
+            ctx->tmp_len = 0;
+        }
+        ctx->buf_off = 0;
+        if (i < 0) {
+            ret_code = 0;
+            ctx->buf_len = 0;
+            break;
+        }
+
+        if (ctx->buf_len <= outl)
+            i = ctx->buf_len;
+        else
+            i = outl;
+
+        memcpy(out, ctx->buf, i);
+        ret += i;
+        ctx->buf_off = i;
+        if (ctx->buf_off == ctx->buf_len) {
+            ctx->buf_len = 0;
+            ctx->buf_off = 0;
+        }
+        outl -= i;
+        out += i;
+    }
+    /* BIO_clear_retry_flags(b); */
+    BIO_copy_next_retry(b);
+    return ((ret == 0) ? ret_code : ret);
+}
+
+static int b64_write(BIO *b, const char *in, int inl)
+{
+    int ret = 0;
+    int n;
+    int i;
+    BIO_B64_CTX *ctx;
+    BIO *next;
+
+    ctx = (BIO_B64_CTX *)BIO_get_data(b);
+    next = BIO_next(b);
+    if ((ctx == NULL) || (next == NULL))
+        return 0;
+
+    BIO_clear_retry_flags(b);
+
+    if (ctx->encode != B64_ENCODE) {
+        ctx->encode = B64_ENCODE;
+        ctx->buf_len = 0;
+        ctx->buf_off = 0;
+        ctx->tmp_len = 0;
+        EVP_EncodeInit(ctx->base64);
+    }
+
+    OPENSSL_assert(ctx->buf_off < (int)sizeof(ctx->buf));
+    OPENSSL_assert(ctx->buf_len <= (int)sizeof(ctx->buf));
+    OPENSSL_assert(ctx->buf_len >= ctx->buf_off);
+    n = ctx->buf_len - ctx->buf_off;
+    while (n > 0) {
+        i = BIO_write(next, &(ctx->buf[ctx->buf_off]), n);
+        if (i <= 0) {
+            BIO_copy_next_retry(b);
+            return (i);
+        }
+        OPENSSL_assert(i <= n);
+        ctx->buf_off += i;
+        OPENSSL_assert(ctx->buf_off <= (int)sizeof(ctx->buf));
+        OPENSSL_assert(ctx->buf_len >= ctx->buf_off);
+        n -= i;
+    }
+    /* at this point all pending data has been written */
+    ctx->buf_off = 0;
+    ctx->buf_len = 0;
+
+    if ((in == NULL) || (inl <= 0))
+        return (0);
+
+    while (inl > 0) {
+        n = (inl > B64_BLOCK_SIZE) ? B64_BLOCK_SIZE : inl;
+
+        if (BIO_get_flags(b) & BIO_FLAGS_BASE64_NO_NL) {
+            if (ctx->tmp_len > 0) {
+                OPENSSL_assert(ctx->tmp_len <= 3);
+                n = 3 - ctx->tmp_len;
+                /*
+                 * There's a theoretical possibility for this
+                 */
+                if (n > inl)
+                    n = inl;
+                memcpy(&(ctx->tmp[ctx->tmp_len]), in, n);
+                ctx->tmp_len += n;
+                ret += n;
+                if (ctx->tmp_len < 3)
+                    break;
+                ctx->buf_len =
+                    EVP_EncodeBlock((unsigned char *)ctx->buf,
+                                    (unsigned char *)ctx->tmp, ctx->tmp_len);
+                OPENSSL_assert(ctx->buf_len <= (int)sizeof(ctx->buf));
+                OPENSSL_assert(ctx->buf_len >= ctx->buf_off);
+                /*
+                 * Since we're now done using the temporary buffer, the
+                 * length should be 0'd
+                 */
+                ctx->tmp_len = 0;
+            } else {
+                if (n < 3) {
+                    memcpy(ctx->tmp, in, n);
+                    ctx->tmp_len = n;
+                    ret += n;
+                    break;
+                }
+                n -= n % 3;
+                ctx->buf_len =
+                    EVP_EncodeBlock((unsigned char *)ctx->buf,
+                                    (const unsigned char *)in, n);
+                OPENSSL_assert(ctx->buf_len <= (int)sizeof(ctx->buf));
+                OPENSSL_assert(ctx->buf_len >= ctx->buf_off);
+                ret += n;
+            }
+        } else {
+            if (!EVP_EncodeUpdate(ctx->base64,
+                                 (unsigned char *)ctx->buf, &ctx->buf_len,
+                                 (unsigned char *)in, n))
+                return ((ret == 0) ? -1 : ret);
+            OPENSSL_assert(ctx->buf_len <= (int)sizeof(ctx->buf));
+            OPENSSL_assert(ctx->buf_len >= ctx->buf_off);
+            ret += n;
+        }
+        inl -= n;
+        in += n;
+
+        ctx->buf_off = 0;
+        n = ctx->buf_len;
+        while (n > 0) {
+            i = BIO_write(next, &(ctx->buf[ctx->buf_off]), n);
+            if (i <= 0) {
+                BIO_copy_next_retry(b);
+                return ((ret == 0) ? i : ret);
+            }
+            OPENSSL_assert(i <= n);
+            n -= i;
+            ctx->buf_off += i;
+            OPENSSL_assert(ctx->buf_off <= (int)sizeof(ctx->buf));
+            OPENSSL_assert(ctx->buf_len >= ctx->buf_off);
+        }
+        ctx->buf_len = 0;
+        ctx->buf_off = 0;
+    }
+    return (ret);
+}
+
+static long b64_ctrl(BIO *b, int cmd, long num, void *ptr)
+{
+    BIO_B64_CTX *ctx;
+    long ret = 1;
+    int i;
+    BIO *next;
+
+    ctx = (BIO_B64_CTX *)BIO_get_data(b);
+    next = BIO_next(b);
+    if ((ctx == NULL) || (next == NULL))
+        return 0;
+
+    switch (cmd) {
+    case BIO_CTRL_RESET:
+        ctx->cont = 1;
+        ctx->start = 1;
+        ctx->encode = B64_NONE;
+        ret = BIO_ctrl(next, cmd, num, ptr);
+        break;
+    case BIO_CTRL_EOF:         /* More to read */
+        if (ctx->cont <= 0)
+            ret = 1;
+        else
+            ret = BIO_ctrl(next, cmd, num, ptr);
+        break;
+    case BIO_CTRL_WPENDING:    /* More to write in buffer */
+        OPENSSL_assert(ctx->buf_len >= ctx->buf_off);
+        ret = ctx->buf_len - ctx->buf_off;
+        if ((ret == 0) && (ctx->encode != B64_NONE)
+            && (EVP_ENCODE_CTX_num(ctx->base64) != 0))
+            ret = 1;
+        else if (ret <= 0)
+            ret = BIO_ctrl(next, cmd, num, ptr);
+        break;
+    case BIO_CTRL_PENDING:     /* More to read in buffer */
+        OPENSSL_assert(ctx->buf_len >= ctx->buf_off);
+        ret = ctx->buf_len - ctx->buf_off;
+        if (ret <= 0)
+            ret = BIO_ctrl(next, cmd, num, ptr);
+        break;
+    case BIO_CTRL_FLUSH:
+        /* do a final write */
+ again:
+        while (ctx->buf_len != ctx->buf_off) {
+            i = b64_write(b, NULL, 0);
+            if (i < 0)
+                return i;
+        }
+        if (BIO_get_flags(b) & BIO_FLAGS_BASE64_NO_NL) {
+            if (ctx->tmp_len != 0) {
+                ctx->buf_len = EVP_EncodeBlock((unsigned char *)ctx->buf,
+                                               (unsigned char *)ctx->tmp,
+                                               ctx->tmp_len);
+                ctx->buf_off = 0;
+                ctx->tmp_len = 0;
+                goto again;
+            }
+        } else if (ctx->encode != B64_NONE
+                   && EVP_ENCODE_CTX_num(ctx->base64) != 0) {
+            ctx->buf_off = 0;
+            EVP_EncodeFinal(ctx->base64,
+                            (unsigned char *)ctx->buf, &(ctx->buf_len));
+            /* push out the bytes */
+            goto again;
+        }
+        /* Finally flush the underlying BIO */
+        ret = BIO_ctrl(next, cmd, num, ptr);
+        break;
+
+    case BIO_C_DO_STATE_MACHINE:
+        BIO_clear_retry_flags(b);
+        ret = BIO_ctrl(next, cmd, num, ptr);
+        BIO_copy_next_retry(b);
+        break;
+
+    case BIO_CTRL_DUP:
+        break;
+    case BIO_CTRL_INFO:
+    case BIO_CTRL_GET:
+    case BIO_CTRL_SET:
+    default:
+        ret = BIO_ctrl(next, cmd, num, ptr);
+        break;
+    }
+    return ret;
+}
+
+static long b64_callback_ctrl(BIO *b, int cmd, bio_info_cb *fp)
+{
+    long ret = 1;
+    BIO *next = BIO_next(b);
+
+    if (next == NULL)
+        return 0;
+    switch (cmd) {
+    default:
+        ret = BIO_callback_ctrl(next, cmd, fp);
+        break;
+    }
+    return (ret);
+}
+
+static int b64_puts(BIO *b, const char *str)
+{
+    return b64_write(b, str, strlen(str));
+}
diff --git a/crypto/evp/bio_enc.c b/crypto/evp/bio_enc.c
new file mode 100644
index 000000000..5a3beef97
--- /dev/null
+++ b/crypto/evp/bio_enc.c
@@ -0,0 +1,449 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+#include "internal/bio.h"
+
+static int enc_write(BIO *h, const char *buf, int num);
+static int enc_read(BIO *h, char *buf, int size);
+/*
+ * static int enc_puts(BIO *h, const char *str);
+ */
+/*
+ * static int enc_gets(BIO *h, char *str, int size);
+ */
+static long enc_ctrl(BIO *h, int cmd, long arg1, void *arg2);
+static int enc_new(BIO *h);
+static int enc_free(BIO *data);
+static long enc_callback_ctrl(BIO *h, int cmd, bio_info_cb *fps);
+#define ENC_BLOCK_SIZE  (1024*4)
+#define ENC_MIN_CHUNK   (256)
+#define BUF_OFFSET      (ENC_MIN_CHUNK + EVP_MAX_BLOCK_LENGTH)
+
+typedef struct enc_struct {
+    int buf_len;
+    int buf_off;
+    int cont;                   /* <= 0 when finished */
+    int finished;
+    int ok;                     /* bad decrypt */
+    EVP_CIPHER_CTX *cipher;
+    unsigned char *read_start, *read_end;
+    /*
+     * buf is larger than ENC_BLOCK_SIZE because EVP_DecryptUpdate can return
+     * up to a block more data than is presented to it
+     */
+    unsigned char buf[BUF_OFFSET + ENC_BLOCK_SIZE];
+} BIO_ENC_CTX;
+
+static const BIO_METHOD methods_enc = {
+    BIO_TYPE_CIPHER, "cipher",
+    enc_write,
+    enc_read,
+    NULL,                       /* enc_puts, */
+    NULL,                       /* enc_gets, */
+    enc_ctrl,
+    enc_new,
+    enc_free,
+    enc_callback_ctrl,
+};
+
+const BIO_METHOD *BIO_f_cipher(void)
+{
+    return (&methods_enc);
+}
+
+static int enc_new(BIO *bi)
+{
+    BIO_ENC_CTX *ctx;
+
+    ctx = OPENSSL_zalloc(sizeof(*ctx));
+    if (ctx == NULL)
+        return 0;
+
+    ctx->cipher = EVP_CIPHER_CTX_new();
+    if (ctx->cipher == NULL) {
+        OPENSSL_free(ctx);
+        return 0;
+    }
+    ctx->cont = 1;
+    ctx->ok = 1;
+    ctx->read_end = ctx->read_start = &(ctx->buf[BUF_OFFSET]);
+    BIO_set_data(bi, ctx);
+    BIO_set_init(bi, 1);
+
+    return 1;
+}
+
+static int enc_free(BIO *a)
+{
+    BIO_ENC_CTX *b;
+
+    if (a == NULL)
+        return 0;
+
+    b = BIO_get_data(a);
+    if (b == NULL)
+        return 0;
+
+    EVP_CIPHER_CTX_free(b->cipher);
+    OPENSSL_clear_free(b, sizeof(BIO_ENC_CTX));
+    BIO_set_data(a, NULL);
+    BIO_set_init(a, 0);
+
+    return 1;
+}
+
+static int enc_read(BIO *b, char *out, int outl)
+{
+    int ret = 0, i, blocksize;
+    BIO_ENC_CTX *ctx;
+    BIO *next;
+
+    if (out == NULL)
+        return (0);
+    ctx = BIO_get_data(b);
+
+    next = BIO_next(b);
+    if ((ctx == NULL) || (next == NULL))
+        return 0;
+
+    /* First check if there are bytes decoded/encoded */
+    if (ctx->buf_len > 0) {
+        i = ctx->buf_len - ctx->buf_off;
+        if (i > outl)
+            i = outl;
+        memcpy(out, &(ctx->buf[ctx->buf_off]), i);
+        ret = i;
+        out += i;
+        outl -= i;
+        ctx->buf_off += i;
+        if (ctx->buf_len == ctx->buf_off) {
+            ctx->buf_len = 0;
+            ctx->buf_off = 0;
+        }
+    }
+
+    blocksize = EVP_CIPHER_CTX_block_size(ctx->cipher);
+    if (blocksize == 1)
+        blocksize = 0;
+
+    /*
+     * At this point, we have room of outl bytes and an empty buffer, so we
+     * should read in some more.
+     */
+
+    while (outl > 0) {
+        if (ctx->cont <= 0)
+            break;
+
+        if (ctx->read_start == ctx->read_end) { /* time to read more data */
+            ctx->read_end = ctx->read_start = &(ctx->buf[BUF_OFFSET]);
+            i = BIO_read(next, ctx->read_start, ENC_BLOCK_SIZE);
+            if (i > 0)
+                ctx->read_end += i;
+        } else {
+            i = ctx->read_end - ctx->read_start;
+        }
+
+        if (i <= 0) {
+            /* Should be continue next time we are called? */
+            if (!BIO_should_retry(next)) {
+                ctx->cont = i;
+                i = EVP_CipherFinal_ex(ctx->cipher,
+                                       ctx->buf, &(ctx->buf_len));
+                ctx->ok = i;
+                ctx->buf_off = 0;
+            } else {
+                ret = (ret == 0) ? i : ret;
+                break;
+            }
+        } else {
+            if (outl > ENC_MIN_CHUNK) {
+                /*
+                 * Depending on flags block cipher decrypt can write
+                 * one extra block and then back off, i.e. output buffer
+                 * has to accommodate extra block...
+                 */
+                int j = outl - blocksize, buf_len;
+
+                if (!EVP_CipherUpdate(ctx->cipher,
+                                      (unsigned char *)out, &buf_len,
+                                      ctx->read_start, i > j ? j : i)) {
+                    BIO_clear_retry_flags(b);
+                    return 0;
+                }
+                ret += buf_len;
+                out += buf_len;
+                outl -= buf_len;
+
+                if ((i -= j) <= 0) {
+                    ctx->read_start = ctx->read_end;
+                    continue;
+                }
+                ctx->read_start += j;
+            }
+            if (i > ENC_MIN_CHUNK)
+                i = ENC_MIN_CHUNK;
+            if (!EVP_CipherUpdate(ctx->cipher,
+                                  ctx->buf, &ctx->buf_len,
+                                  ctx->read_start, i)) {
+                BIO_clear_retry_flags(b);
+                ctx->ok = 0;
+                return 0;
+            }
+            ctx->read_start += i;
+            ctx->cont = 1;
+            /*
+             * Note: it is possible for EVP_CipherUpdate to decrypt zero
+             * bytes because this is or looks like the final block: if this
+             * happens we should retry and either read more data or decrypt
+             * the final block
+             */
+            if (ctx->buf_len == 0)
+                continue;
+        }
+
+        if (ctx->buf_len <= outl)
+            i = ctx->buf_len;
+        else
+            i = outl;
+        if (i <= 0)
+            break;
+        memcpy(out, ctx->buf, i);
+        ret += i;
+        ctx->buf_off = i;
+        outl -= i;
+        out += i;
+    }
+
+    BIO_clear_retry_flags(b);
+    BIO_copy_next_retry(b);
+    return ((ret == 0) ? ctx->cont : ret);
+}
+
+static int enc_write(BIO *b, const char *in, int inl)
+{
+    int ret = 0, n, i;
+    BIO_ENC_CTX *ctx;
+    BIO *next;
+
+    ctx = BIO_get_data(b);
+    next = BIO_next(b);
+    if ((ctx == NULL) || (next == NULL))
+        return 0;
+
+    ret = inl;
+
+    BIO_clear_retry_flags(b);
+    n = ctx->buf_len - ctx->buf_off;
+    while (n > 0) {
+        i = BIO_write(next, &(ctx->buf[ctx->buf_off]), n);
+        if (i <= 0) {
+            BIO_copy_next_retry(b);
+            return (i);
+        }
+        ctx->buf_off += i;
+        n -= i;
+    }
+    /* at this point all pending data has been written */
+
+    if ((in == NULL) || (inl <= 0))
+        return (0);
+
+    ctx->buf_off = 0;
+    while (inl > 0) {
+        n = (inl > ENC_BLOCK_SIZE) ? ENC_BLOCK_SIZE : inl;
+        if (!EVP_CipherUpdate(ctx->cipher,
+                              ctx->buf, &ctx->buf_len,
+                              (const unsigned char *)in, n)) {
+            BIO_clear_retry_flags(b);
+            ctx->ok = 0;
+            return 0;
+        }
+        inl -= n;
+        in += n;
+
+        ctx->buf_off = 0;
+        n = ctx->buf_len;
+        while (n > 0) {
+            i = BIO_write(next, &(ctx->buf[ctx->buf_off]), n);
+            if (i <= 0) {
+                BIO_copy_next_retry(b);
+                return (ret == inl) ? i : ret - inl;
+            }
+            n -= i;
+            ctx->buf_off += i;
+        }
+        ctx->buf_len = 0;
+        ctx->buf_off = 0;
+    }
+    BIO_copy_next_retry(b);
+    return (ret);
+}
+
+static long enc_ctrl(BIO *b, int cmd, long num, void *ptr)
+{
+    BIO *dbio;
+    BIO_ENC_CTX *ctx, *dctx;
+    long ret = 1;
+    int i;
+    EVP_CIPHER_CTX **c_ctx;
+    BIO *next;
+
+    ctx = BIO_get_data(b);
+    next = BIO_next(b);
+    if (ctx == NULL)
+        return 0;
+
+    switch (cmd) {
+    case BIO_CTRL_RESET:
+        ctx->ok = 1;
+        ctx->finished = 0;
+        if (!EVP_CipherInit_ex(ctx->cipher, NULL, NULL, NULL, NULL,
+                               EVP_CIPHER_CTX_encrypting(ctx->cipher)))
+            return 0;
+        ret = BIO_ctrl(next, cmd, num, ptr);
+        break;
+    case BIO_CTRL_EOF:         /* More to read */
+        if (ctx->cont <= 0)
+            ret = 1;
+        else
+            ret = BIO_ctrl(next, cmd, num, ptr);
+        break;
+    case BIO_CTRL_WPENDING:
+        ret = ctx->buf_len - ctx->buf_off;
+        if (ret <= 0)
+            ret = BIO_ctrl(next, cmd, num, ptr);
+        break;
+    case BIO_CTRL_PENDING:     /* More to read in buffer */
+        ret = ctx->buf_len - ctx->buf_off;
+        if (ret <= 0)
+            ret = BIO_ctrl(next, cmd, num, ptr);
+        break;
+    case BIO_CTRL_FLUSH:
+        /* do a final write */
+ again:
+        while (ctx->buf_len != ctx->buf_off) {
+            i = enc_write(b, NULL, 0);
+            if (i < 0)
+                return i;
+        }
+
+        if (!ctx->finished) {
+            ctx->finished = 1;
+            ctx->buf_off = 0;
+            ret = EVP_CipherFinal_ex(ctx->cipher,
+                                     (unsigned char *)ctx->buf,
+                                     &(ctx->buf_len));
+            ctx->ok = (int)ret;
+            if (ret <= 0)
+                break;
+
+            /* push out the bytes */
+            goto again;
+        }
+
+        /* Finally flush the underlying BIO */
+        ret = BIO_ctrl(next, cmd, num, ptr);
+        break;
+    case BIO_C_GET_CIPHER_STATUS:
+        ret = (long)ctx->ok;
+        break;
+    case BIO_C_DO_STATE_MACHINE:
+        BIO_clear_retry_flags(b);
+        ret = BIO_ctrl(next, cmd, num, ptr);
+        BIO_copy_next_retry(b);
+        break;
+    case BIO_C_GET_CIPHER_CTX:
+        c_ctx = (EVP_CIPHER_CTX **)ptr;
+        *c_ctx = ctx->cipher;
+        BIO_set_init(b, 1);
+        break;
+    case BIO_CTRL_DUP:
+        dbio = (BIO *)ptr;
+        dctx = BIO_get_data(dbio);
+        dctx->cipher = EVP_CIPHER_CTX_new();
+        if (dctx->cipher == NULL)
+            return 0;
+        ret = EVP_CIPHER_CTX_copy(dctx->cipher, ctx->cipher);
+        if (ret)
+            BIO_set_init(dbio, 1);
+        break;
+    default:
+        ret = BIO_ctrl(next, cmd, num, ptr);
+        break;
+    }
+    return (ret);
+}
+
+static long enc_callback_ctrl(BIO *b, int cmd, bio_info_cb *fp)
+{
+    long ret = 1;
+    BIO *next = BIO_next(b);
+
+    if (next == NULL)
+        return (0);
+    switch (cmd) {
+    default:
+        ret = BIO_callback_ctrl(next, cmd, fp);
+        break;
+    }
+    return (ret);
+}
+
+/*-
+void BIO_set_cipher_ctx(b,c)
+BIO *b;
+EVP_CIPHER_ctx *c;
+        {
+        if (b == NULL) return;
+
+        if ((b->callback != NULL) &&
+                (b->callback(b,BIO_CB_CTRL,(char *)c,BIO_CTRL_SET,e,0L) <= 0))
+                return;
+
+        b->init=1;
+        ctx=(BIO_ENC_CTX *)b->ptr;
+        memcpy(ctx->cipher,c,sizeof(EVP_CIPHER_CTX));
+
+        if (b->callback != NULL)
+                b->callback(b,BIO_CB_CTRL,(char *)c,BIO_CTRL_SET,e,1L);
+        }
+*/
+
+int BIO_set_cipher(BIO *b, const EVP_CIPHER *c, const unsigned char *k,
+                   const unsigned char *i, int e)
+{
+    BIO_ENC_CTX *ctx;
+    long (*callback) (struct bio_st *, int, const char *, int, long, long);
+
+    ctx = BIO_get_data(b);
+    if (ctx == NULL)
+        return 0;
+
+    callback = BIO_get_callback(b);
+
+    if ((callback != NULL) &&
+            (callback(b, BIO_CB_CTRL, (const char *)c, BIO_CTRL_SET, e,
+                      0L) <= 0))
+        return 0;
+
+    BIO_set_init(b, 1);
+
+    if (!EVP_CipherInit_ex(ctx->cipher, c, NULL, k, i, e))
+        return 0;
+
+    if (callback != NULL)
+        return callback(b, BIO_CB_CTRL, (const char *)c, BIO_CTRL_SET, e, 1L);
+    return 1;
+}
diff --git a/crypto/evp/bio_md.c b/crypto/evp/bio_md.c
new file mode 100644
index 000000000..cd968ec2d
--- /dev/null
+++ b/crypto/evp/bio_md.c
@@ -0,0 +1,231 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+#include "internal/evp_int.h"
+#include "evp_locl.h"
+#include "internal/bio.h"
+
+/*
+ * BIO_put and BIO_get both add to the digest, BIO_gets returns the digest
+ */
+
+static int md_write(BIO *h, char const *buf, int num);
+static int md_read(BIO *h, char *buf, int size);
+/*
+ * static int md_puts(BIO *h, const char *str);
+ */
+static int md_gets(BIO *h, char *str, int size);
+static long md_ctrl(BIO *h, int cmd, long arg1, void *arg2);
+static int md_new(BIO *h);
+static int md_free(BIO *data);
+static long md_callback_ctrl(BIO *h, int cmd, bio_info_cb *fp);
+
+static const BIO_METHOD methods_md = {
+    BIO_TYPE_MD, "message digest",
+    md_write,
+    md_read,
+    NULL,                       /* md_puts, */
+    md_gets,
+    md_ctrl,
+    md_new,
+    md_free,
+    md_callback_ctrl,
+};
+
+const BIO_METHOD *BIO_f_md(void)
+{
+    return (&methods_md);
+}
+
+static int md_new(BIO *bi)
+{
+    EVP_MD_CTX *ctx;
+
+    ctx = EVP_MD_CTX_new();
+    if (ctx == NULL)
+        return (0);
+
+    BIO_set_init(bi, 1);
+    BIO_set_data(bi, ctx);
+
+    return 1;
+}
+
+static int md_free(BIO *a)
+{
+    if (a == NULL)
+        return (0);
+    EVP_MD_CTX_free(BIO_get_data(a));
+    BIO_set_data(a, NULL);
+    BIO_set_init(a, 0);
+
+    return 1;
+}
+
+static int md_read(BIO *b, char *out, int outl)
+{
+    int ret = 0;
+    EVP_MD_CTX *ctx;
+    BIO *next;
+
+    if (out == NULL)
+        return (0);
+
+    ctx = BIO_get_data(b);
+    next = BIO_next(b);
+
+    if ((ctx == NULL) || (next == NULL))
+        return (0);
+
+    ret = BIO_read(next, out, outl);
+    if (BIO_get_init(b)) {
+        if (ret > 0) {
+            if (EVP_DigestUpdate(ctx, (unsigned char *)out,
+                                 (unsigned int)ret) <= 0)
+                return (-1);
+        }
+    }
+    BIO_clear_retry_flags(b);
+    BIO_copy_next_retry(b);
+    return (ret);
+}
+
+static int md_write(BIO *b, const char *in, int inl)
+{
+    int ret = 0;
+    EVP_MD_CTX *ctx;
+    BIO *next;
+
+    if ((in == NULL) || (inl <= 0))
+        return 0;
+
+    ctx = BIO_get_data(b);
+    next = BIO_next(b);
+    if ((ctx != NULL) && (next != NULL))
+        ret = BIO_write(next, in, inl);
+
+    if (BIO_get_init(b)) {
+        if (ret > 0) {
+            if (!EVP_DigestUpdate(ctx, (const unsigned char *)in,
+                                  (unsigned int)ret)) {
+                BIO_clear_retry_flags(b);
+                return 0;
+            }
+        }
+    }
+    if (next != NULL) {
+        BIO_clear_retry_flags(b);
+        BIO_copy_next_retry(b);
+    }
+    return ret;
+}
+
+static long md_ctrl(BIO *b, int cmd, long num, void *ptr)
+{
+    EVP_MD_CTX *ctx, *dctx, **pctx;
+    const EVP_MD **ppmd;
+    EVP_MD *md;
+    long ret = 1;
+    BIO *dbio, *next;
+
+
+    ctx = BIO_get_data(b);
+    next = BIO_next(b);
+
+    switch (cmd) {
+    case BIO_CTRL_RESET:
+        if (BIO_get_init(b))
+            ret = EVP_DigestInit_ex(ctx, ctx->digest, NULL);
+        else
+            ret = 0;
+        if (ret > 0)
+            ret = BIO_ctrl(next, cmd, num, ptr);
+        break;
+    case BIO_C_GET_MD:
+        if (BIO_get_init(b)) {
+            ppmd = ptr;
+            *ppmd = ctx->digest;
+        } else
+            ret = 0;
+        break;
+    case BIO_C_GET_MD_CTX:
+        pctx = ptr;
+        *pctx = ctx;
+        BIO_set_init(b, 1);
+        break;
+    case BIO_C_SET_MD_CTX:
+        if (BIO_get_init(b))
+            BIO_set_data(b, ptr);
+        else
+            ret = 0;
+        break;
+    case BIO_C_DO_STATE_MACHINE:
+        BIO_clear_retry_flags(b);
+        ret = BIO_ctrl(next, cmd, num, ptr);
+        BIO_copy_next_retry(b);
+        break;
+
+    case BIO_C_SET_MD:
+        md = ptr;
+        ret = EVP_DigestInit_ex(ctx, md, NULL);
+        if (ret > 0)
+            BIO_set_init(b, 1);
+        break;
+    case BIO_CTRL_DUP:
+        dbio = ptr;
+        dctx = BIO_get_data(dbio);
+        if (!EVP_MD_CTX_copy_ex(dctx, ctx))
+            return 0;
+        BIO_set_init(b, 1);
+        break;
+    default:
+        ret = BIO_ctrl(next, cmd, num, ptr);
+        break;
+    }
+    return (ret);
+}
+
+static long md_callback_ctrl(BIO *b, int cmd, bio_info_cb *fp)
+{
+    long ret = 1;
+    BIO *next;
+
+    next = BIO_next(b);
+
+    if (next == NULL)
+        return 0;
+
+    switch (cmd) {
+    default:
+        ret = BIO_callback_ctrl(next, cmd, fp);
+        break;
+    }
+    return (ret);
+}
+
+static int md_gets(BIO *bp, char *buf, int size)
+{
+    EVP_MD_CTX *ctx;
+    unsigned int ret;
+
+    ctx = BIO_get_data(bp);
+
+    if (size < ctx->digest->md_size)
+        return 0;
+
+    if (EVP_DigestFinal_ex(ctx, (unsigned char *)buf, &ret) <= 0)
+        return -1;
+
+    return ((int)ret);
+}
diff --git a/crypto/evp/bio_ok.c b/crypto/evp/bio_ok.c
new file mode 100644
index 000000000..7974b9631
--- /dev/null
+++ b/crypto/evp/bio_ok.c
@@ -0,0 +1,604 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*-
+        From: Arne Ansper 
+
+        Why BIO_f_reliable?
+
+        I wrote function which took BIO* as argument, read data from it
+        and processed it. Then I wanted to store the input file in
+        encrypted form. OK I pushed BIO_f_cipher to the BIO stack
+        and everything was OK. BUT if user types wrong password
+        BIO_f_cipher outputs only garbage and my function crashes. Yes
+        I can and I should fix my function, but BIO_f_cipher is
+        easy way to add encryption support to many existing applications
+        and it's hard to debug and fix them all.
+
+        So I wanted another BIO which would catch the incorrect passwords and
+        file damages which cause garbage on BIO_f_cipher's output.
+
+        The easy way is to push the BIO_f_md and save the checksum at
+        the end of the file. However there are several problems with this
+        approach:
+
+        1) you must somehow separate checksum from actual data.
+        2) you need lot's of memory when reading the file, because you
+        must read to the end of the file and verify the checksum before
+        letting the application to read the data.
+
+        BIO_f_reliable tries to solve both problems, so that you can
+        read and write arbitrary long streams using only fixed amount
+        of memory.
+
+        BIO_f_reliable splits data stream into blocks. Each block is prefixed
+        with it's length and suffixed with it's digest. So you need only
+        several Kbytes of memory to buffer single block before verifying
+        it's digest.
+
+        BIO_f_reliable goes further and adds several important capabilities:
+
+        1) the digest of the block is computed over the whole stream
+        -- so nobody can rearrange the blocks or remove or replace them.
+
+        2) to detect invalid passwords right at the start BIO_f_reliable
+        adds special prefix to the stream. In order to avoid known plain-text
+        attacks this prefix is generated as follows:
+
+                *) digest is initialized with random seed instead of
+                standardized one.
+                *) same seed is written to output
+                *) well-known text is then hashed and the output
+                of the digest is also written to output.
+
+        reader can now read the seed from stream, hash the same string
+        and then compare the digest output.
+
+        Bad things: BIO_f_reliable knows what's going on in EVP_Digest. I
+        initially wrote and tested this code on x86 machine and wrote the
+        digests out in machine-dependent order :( There are people using
+        this code and I cannot change this easily without making existing
+        data files unreadable.
+
+*/
+
+#include 
+#include 
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include "internal/bio.h"
+#include 
+#include 
+#include "internal/evp_int.h"
+
+static int ok_write(BIO *h, const char *buf, int num);
+static int ok_read(BIO *h, char *buf, int size);
+static long ok_ctrl(BIO *h, int cmd, long arg1, void *arg2);
+static int ok_new(BIO *h);
+static int ok_free(BIO *data);
+static long ok_callback_ctrl(BIO *h, int cmd, bio_info_cb *fp);
+
+static __owur int sig_out(BIO *b);
+static __owur int sig_in(BIO *b);
+static __owur int block_out(BIO *b);
+static __owur int block_in(BIO *b);
+#define OK_BLOCK_SIZE   (1024*4)
+#define OK_BLOCK_BLOCK  4
+#define IOBS            (OK_BLOCK_SIZE+ OK_BLOCK_BLOCK+ 3*EVP_MAX_MD_SIZE)
+#define WELLKNOWN "The quick brown fox jumped over the lazy dog's back."
+
+typedef struct ok_struct {
+    size_t buf_len;
+    size_t buf_off;
+    size_t buf_len_save;
+    size_t buf_off_save;
+    int cont;                   /* <= 0 when finished */
+    int finished;
+    EVP_MD_CTX *md;
+    int blockout;               /* output block is ready */
+    int sigio;                  /* must process signature */
+    unsigned char buf[IOBS];
+} BIO_OK_CTX;
+
+static const BIO_METHOD methods_ok = {
+    BIO_TYPE_CIPHER, "reliable",
+    ok_write,
+    ok_read,
+    NULL,                       /* ok_puts, */
+    NULL,                       /* ok_gets, */
+    ok_ctrl,
+    ok_new,
+    ok_free,
+    ok_callback_ctrl,
+};
+
+const BIO_METHOD *BIO_f_reliable(void)
+{
+    return (&methods_ok);
+}
+
+static int ok_new(BIO *bi)
+{
+    BIO_OK_CTX *ctx;
+
+    ctx = OPENSSL_zalloc(sizeof(*ctx));
+    if (ctx == NULL)
+        return 0;
+
+    ctx->cont = 1;
+    ctx->sigio = 1;
+    ctx->md = EVP_MD_CTX_new();
+    if (ctx->md == NULL) {
+        OPENSSL_free(ctx);
+        return 0;
+    }
+    BIO_set_init(bi, 0);
+    BIO_set_data(bi, ctx);
+
+    return 1;
+}
+
+static int ok_free(BIO *a)
+{
+    BIO_OK_CTX *ctx;
+
+    if (a == NULL)
+        return 0;
+
+    ctx = BIO_get_data(a);
+
+    EVP_MD_CTX_free(ctx->md);
+    OPENSSL_clear_free(ctx, sizeof(BIO_OK_CTX));
+    BIO_set_data(a, NULL);
+    BIO_set_init(a, 0);
+
+    return 1;
+}
+
+static int ok_read(BIO *b, char *out, int outl)
+{
+    int ret = 0, i, n;
+    BIO_OK_CTX *ctx;
+    BIO *next;
+
+    if (out == NULL)
+        return 0;
+
+    ctx = BIO_get_data(b);
+    next = BIO_next(b);
+
+    if ((ctx == NULL) || (next == NULL) || (BIO_get_init(b) == 0))
+        return 0;
+
+    while (outl > 0) {
+
+        /* copy clean bytes to output buffer */
+        if (ctx->blockout) {
+            i = ctx->buf_len - ctx->buf_off;
+            if (i > outl)
+                i = outl;
+            memcpy(out, &(ctx->buf[ctx->buf_off]), i);
+            ret += i;
+            out += i;
+            outl -= i;
+            ctx->buf_off += i;
+
+            /* all clean bytes are out */
+            if (ctx->buf_len == ctx->buf_off) {
+                ctx->buf_off = 0;
+
+                /*
+                 * copy start of the next block into proper place
+                 */
+                if (ctx->buf_len_save - ctx->buf_off_save > 0) {
+                    ctx->buf_len = ctx->buf_len_save - ctx->buf_off_save;
+                    memmove(ctx->buf, &(ctx->buf[ctx->buf_off_save]),
+                            ctx->buf_len);
+                } else {
+                    ctx->buf_len = 0;
+                }
+                ctx->blockout = 0;
+            }
+        }
+
+        /* output buffer full -- cancel */
+        if (outl == 0)
+            break;
+
+        /* no clean bytes in buffer -- fill it */
+        n = IOBS - ctx->buf_len;
+        i = BIO_read(next, &(ctx->buf[ctx->buf_len]), n);
+
+        if (i <= 0)
+            break;              /* nothing new */
+
+        ctx->buf_len += i;
+
+        /* no signature yet -- check if we got one */
+        if (ctx->sigio == 1) {
+            if (!sig_in(b)) {
+                BIO_clear_retry_flags(b);
+                return 0;
+            }
+        }
+
+        /* signature ok -- check if we got block */
+        if (ctx->sigio == 0) {
+            if (!block_in(b)) {
+                BIO_clear_retry_flags(b);
+                return 0;
+            }
+        }
+
+        /* invalid block -- cancel */
+        if (ctx->cont <= 0)
+            break;
+
+    }
+
+    BIO_clear_retry_flags(b);
+    BIO_copy_next_retry(b);
+    return ret;
+}
+
+static int ok_write(BIO *b, const char *in, int inl)
+{
+    int ret = 0, n, i;
+    BIO_OK_CTX *ctx;
+    BIO *next;
+
+    if (inl <= 0)
+        return inl;
+
+    ctx = BIO_get_data(b);
+    next = BIO_next(b);
+    ret = inl;
+
+    if ((ctx == NULL) || (next == NULL) || (BIO_get_init(b) == 0))
+        return (0);
+
+    if (ctx->sigio && !sig_out(b))
+        return 0;
+
+    do {
+        BIO_clear_retry_flags(b);
+        n = ctx->buf_len - ctx->buf_off;
+        while (ctx->blockout && n > 0) {
+            i = BIO_write(next, &(ctx->buf[ctx->buf_off]), n);
+            if (i <= 0) {
+                BIO_copy_next_retry(b);
+                if (!BIO_should_retry(b))
+                    ctx->cont = 0;
+                return (i);
+            }
+            ctx->buf_off += i;
+            n -= i;
+        }
+
+        /* at this point all pending data has been written */
+        ctx->blockout = 0;
+        if (ctx->buf_len == ctx->buf_off) {
+            ctx->buf_len = OK_BLOCK_BLOCK;
+            ctx->buf_off = 0;
+        }
+
+        if ((in == NULL) || (inl <= 0))
+            return (0);
+
+        n = (inl + ctx->buf_len > OK_BLOCK_SIZE + OK_BLOCK_BLOCK) ?
+            (int)(OK_BLOCK_SIZE + OK_BLOCK_BLOCK - ctx->buf_len) : inl;
+
+        memcpy(&ctx->buf[ctx->buf_len], in, n);
+        ctx->buf_len += n;
+        inl -= n;
+        in += n;
+
+        if (ctx->buf_len >= OK_BLOCK_SIZE + OK_BLOCK_BLOCK) {
+            if (!block_out(b)) {
+                BIO_clear_retry_flags(b);
+                return 0;
+            }
+        }
+    } while (inl > 0);
+
+    BIO_clear_retry_flags(b);
+    BIO_copy_next_retry(b);
+    return (ret);
+}
+
+static long ok_ctrl(BIO *b, int cmd, long num, void *ptr)
+{
+    BIO_OK_CTX *ctx;
+    EVP_MD *md;
+    const EVP_MD **ppmd;
+    long ret = 1;
+    int i;
+    BIO *next;
+
+    ctx = BIO_get_data(b);
+    next = BIO_next(b);
+
+    switch (cmd) {
+    case BIO_CTRL_RESET:
+        ctx->buf_len = 0;
+        ctx->buf_off = 0;
+        ctx->buf_len_save = 0;
+        ctx->buf_off_save = 0;
+        ctx->cont = 1;
+        ctx->finished = 0;
+        ctx->blockout = 0;
+        ctx->sigio = 1;
+        ret = BIO_ctrl(next, cmd, num, ptr);
+        break;
+    case BIO_CTRL_EOF:         /* More to read */
+        if (ctx->cont <= 0)
+            ret = 1;
+        else
+            ret = BIO_ctrl(next, cmd, num, ptr);
+        break;
+    case BIO_CTRL_PENDING:     /* More to read in buffer */
+    case BIO_CTRL_WPENDING:    /* More to read in buffer */
+        ret = ctx->blockout ? ctx->buf_len - ctx->buf_off : 0;
+        if (ret <= 0)
+            ret = BIO_ctrl(next, cmd, num, ptr);
+        break;
+    case BIO_CTRL_FLUSH:
+        /* do a final write */
+        if (ctx->blockout == 0)
+            if (!block_out(b))
+                return 0;
+
+        while (ctx->blockout) {
+            i = ok_write(b, NULL, 0);
+            if (i < 0) {
+                ret = i;
+                break;
+            }
+        }
+
+        ctx->finished = 1;
+        ctx->buf_off = ctx->buf_len = 0;
+        ctx->cont = (int)ret;
+
+        /* Finally flush the underlying BIO */
+        ret = BIO_ctrl(next, cmd, num, ptr);
+        break;
+    case BIO_C_DO_STATE_MACHINE:
+        BIO_clear_retry_flags(b);
+        ret = BIO_ctrl(next, cmd, num, ptr);
+        BIO_copy_next_retry(b);
+        break;
+    case BIO_CTRL_INFO:
+        ret = (long)ctx->cont;
+        break;
+    case BIO_C_SET_MD:
+        md = ptr;
+        if (!EVP_DigestInit_ex(ctx->md, md, NULL))
+            return 0;
+        BIO_set_init(b, 1);
+        break;
+    case BIO_C_GET_MD:
+        if (BIO_get_init(b)) {
+            ppmd = ptr;
+            *ppmd = EVP_MD_CTX_md(ctx->md);
+        } else
+            ret = 0;
+        break;
+    default:
+        ret = BIO_ctrl(next, cmd, num, ptr);
+        break;
+    }
+    return ret;
+}
+
+static long ok_callback_ctrl(BIO *b, int cmd, bio_info_cb *fp)
+{
+    long ret = 1;
+    BIO *next;
+
+    next = BIO_next(b);
+
+    if (next == NULL)
+        return 0;
+
+    switch (cmd) {
+    default:
+        ret = BIO_callback_ctrl(next, cmd, fp);
+        break;
+    }
+
+    return ret;
+}
+
+static void longswap(void *_ptr, size_t len)
+{
+    const union {
+        long one;
+        char little;
+    } is_endian = {
+        1
+    };
+
+    if (is_endian.little) {
+        size_t i;
+        unsigned char *p = _ptr, c;
+
+        for (i = 0; i < len; i += 4) {
+            c = p[0], p[0] = p[3], p[3] = c;
+            c = p[1], p[1] = p[2], p[2] = c;
+        }
+    }
+}
+
+static int sig_out(BIO *b)
+{
+    BIO_OK_CTX *ctx;
+    EVP_MD_CTX *md;
+    const EVP_MD *digest;
+    int md_size;
+    void *md_data;
+
+    ctx = BIO_get_data(b);
+    md = ctx->md;
+    digest = EVP_MD_CTX_md(md);
+    md_size = EVP_MD_size(digest);
+    md_data = EVP_MD_CTX_md_data(md);
+
+    if (ctx->buf_len + 2 * md_size > OK_BLOCK_SIZE)
+        return 1;
+
+    if (!EVP_DigestInit_ex(md, digest, NULL))
+        goto berr;
+    /*
+     * FIXME: there's absolutely no guarantee this makes any sense at all,
+     * particularly now EVP_MD_CTX has been restructured.
+     */
+    if (RAND_bytes(md_data, md_size) <= 0)
+        goto berr;
+    memcpy(&(ctx->buf[ctx->buf_len]), md_data, md_size);
+    longswap(&(ctx->buf[ctx->buf_len]), md_size);
+    ctx->buf_len += md_size;
+
+    if (!EVP_DigestUpdate(md, WELLKNOWN, strlen(WELLKNOWN)))
+        goto berr;
+    if (!EVP_DigestFinal_ex(md, &(ctx->buf[ctx->buf_len]), NULL))
+        goto berr;
+    ctx->buf_len += md_size;
+    ctx->blockout = 1;
+    ctx->sigio = 0;
+    return 1;
+ berr:
+    BIO_clear_retry_flags(b);
+    return 0;
+}
+
+static int sig_in(BIO *b)
+{
+    BIO_OK_CTX *ctx;
+    EVP_MD_CTX *md;
+    unsigned char tmp[EVP_MAX_MD_SIZE];
+    int ret = 0;
+    const EVP_MD *digest;
+    int md_size;
+    void *md_data;
+
+    ctx = BIO_get_data(b);
+    md = ctx->md;
+    digest = EVP_MD_CTX_md(md);
+    md_size = EVP_MD_size(digest);
+    md_data = EVP_MD_CTX_md_data(md);
+
+    if ((int)(ctx->buf_len - ctx->buf_off) < 2 * md_size)
+        return 1;
+
+    if (!EVP_DigestInit_ex(md, digest, NULL))
+        goto berr;
+    memcpy(md_data, &(ctx->buf[ctx->buf_off]), md_size);
+    longswap(md_data, md_size);
+    ctx->buf_off += md_size;
+
+    if (!EVP_DigestUpdate(md, WELLKNOWN, strlen(WELLKNOWN)))
+        goto berr;
+    if (!EVP_DigestFinal_ex(md, tmp, NULL))
+        goto berr;
+    ret = memcmp(&(ctx->buf[ctx->buf_off]), tmp, md_size) == 0;
+    ctx->buf_off += md_size;
+    if (ret == 1) {
+        ctx->sigio = 0;
+        if (ctx->buf_len != ctx->buf_off) {
+            memmove(ctx->buf, &(ctx->buf[ctx->buf_off]),
+                    ctx->buf_len - ctx->buf_off);
+        }
+        ctx->buf_len -= ctx->buf_off;
+        ctx->buf_off = 0;
+    } else {
+        ctx->cont = 0;
+    }
+    return 1;
+ berr:
+    BIO_clear_retry_flags(b);
+    return 0;
+}
+
+static int block_out(BIO *b)
+{
+    BIO_OK_CTX *ctx;
+    EVP_MD_CTX *md;
+    unsigned long tl;
+    const EVP_MD *digest;
+    int md_size;
+
+    ctx = BIO_get_data(b);
+    md = ctx->md;
+    digest = EVP_MD_CTX_md(md);
+    md_size = EVP_MD_size(digest);
+
+    tl = ctx->buf_len - OK_BLOCK_BLOCK;
+    ctx->buf[0] = (unsigned char)(tl >> 24);
+    ctx->buf[1] = (unsigned char)(tl >> 16);
+    ctx->buf[2] = (unsigned char)(tl >> 8);
+    ctx->buf[3] = (unsigned char)(tl);
+    if (!EVP_DigestUpdate(md,
+                          (unsigned char *)&(ctx->buf[OK_BLOCK_BLOCK]), tl))
+        goto berr;
+    if (!EVP_DigestFinal_ex(md, &(ctx->buf[ctx->buf_len]), NULL))
+        goto berr;
+    ctx->buf_len += md_size;
+    ctx->blockout = 1;
+    return 1;
+ berr:
+    BIO_clear_retry_flags(b);
+    return 0;
+}
+
+static int block_in(BIO *b)
+{
+    BIO_OK_CTX *ctx;
+    EVP_MD_CTX *md;
+    unsigned long tl = 0;
+    unsigned char tmp[EVP_MAX_MD_SIZE];
+    int md_size;
+
+    ctx = BIO_get_data(b);
+    md = ctx->md;
+    md_size = EVP_MD_size(EVP_MD_CTX_md(md));
+
+    assert(sizeof(tl) >= OK_BLOCK_BLOCK); /* always true */
+    tl = ctx->buf[0];
+    tl <<= 8;
+    tl |= ctx->buf[1];
+    tl <<= 8;
+    tl |= ctx->buf[2];
+    tl <<= 8;
+    tl |= ctx->buf[3];
+
+    if (ctx->buf_len < tl + OK_BLOCK_BLOCK + md_size)
+        return 1;
+
+    if (!EVP_DigestUpdate(md,
+                          (unsigned char *)&(ctx->buf[OK_BLOCK_BLOCK]), tl))
+        goto berr;
+    if (!EVP_DigestFinal_ex(md, tmp, NULL))
+        goto berr;
+    if (memcmp(&(ctx->buf[tl + OK_BLOCK_BLOCK]), tmp, md_size) == 0) {
+        /* there might be parts from next block lurking around ! */
+        ctx->buf_off_save = tl + OK_BLOCK_BLOCK + md_size;
+        ctx->buf_len_save = ctx->buf_len;
+        ctx->buf_off = OK_BLOCK_BLOCK;
+        ctx->buf_len = tl + OK_BLOCK_BLOCK;
+        ctx->blockout = 1;
+    } else {
+        ctx->cont = 0;
+    }
+    return 1;
+ berr:
+    BIO_clear_retry_flags(b);
+    return 0;
+}
diff --git a/crypto/evp/build.info b/crypto/evp/build.info
new file mode 100644
index 000000000..83aec673a
--- /dev/null
+++ b/crypto/evp/build.info
@@ -0,0 +1,32 @@
+LIBS=../../libcrypto
+SOURCE[../../libcrypto]=\
+        encode.c digest.c evp_enc.c evp_key.c evp_cnf.c \
+        e_des.c e_bf.c e_idea.c e_des3.c e_camellia.c\
+        e_rc4.c e_aes.c names.c e_seed.c \
+        e_xcbc_d.c e_rc2.c e_cast.c e_rc5.c \
+        m_null.c m_md2.c m_md4.c m_md5.c m_sha1.c m_wp.c \
+        m_md5_sha1.c m_mdc2.c m_ripemd.c \
+        p_open.c p_seal.c p_sign.c p_verify.c p_lib.c p_enc.c p_dec.c \
+        bio_md.c bio_b64.c bio_enc.c evp_err.c e_null.c \
+        c_allc.c c_alld.c evp_lib.c bio_ok.c \
+        evp_pkey.c evp_pbe.c p5_crpt.c p5_crpt2.c scrypt.c \
+        e_old.c pmeth_lib.c pmeth_fn.c pmeth_gn.c m_sigver.c \
+        e_aes_cbc_hmac_sha1.c e_aes_cbc_hmac_sha256.c e_rc4_hmac_md5.c \
+        e_chacha20_poly1305.c cmeth_lib.c \
+	m_sm3.c m_sm9hash2.c \
+	e_sms4.c e_sms4_ccm.c e_sms4_gcm.c e_sms4_ocb.c e_sms4_wrap.c e_sms4_xts.c \
+	e_zuc.c \
+	evp_ctxt.c names2.c
+
+INCLUDE[e_aes.o]=.. ../modes
+INCLUDE[e_aes_cbc_hmac_sha1.o]=../modes
+INCLUDE[e_aes_cbc_hmac_sha256.o]=../modes
+INCLUDE[e_camellia.o]=.. ../modes
+INCLUDE[e_des.o]=..
+INCLUDE[e_des3.o]=..
+INCLUDE[e_sms4.o]=.. ../modes ../sms4
+INCLUDE[e_sms4_ccm.o]=.. ../modes
+INCLUDE[e_sms4_gcm.o]=.. ../modes
+INCLUDE[e_sms4_ocb.o]=.. ../modes
+INCLUDE[e_sms4_xts.o]=.. ../modes
+INCLUDE[e_sms4_wrap.o]=.. ../modes
diff --git a/crypto/evp/c_allc.c b/crypto/evp/c_allc.c
new file mode 100644
index 000000000..bdcd9483c
--- /dev/null
+++ b/crypto/evp/c_allc.c
@@ -0,0 +1,298 @@
+/* ====================================================================
+ * Copyright (c) 2014 - 2018 The GmSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the GmSSL Project.
+ *    (http://gmssl.org/)"
+ *
+ * 4. The name "GmSSL Project" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    guanzhi1980@gmail.com.
+ *
+ * 5. Products derived from this software may not be called "GmSSL"
+ *    nor may "GmSSL" appear in their names without prior written
+ *    permission of the GmSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the GmSSL Project
+ *    (http://gmssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE GmSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE GmSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+#include 
+#include 
+
+void openssl_add_all_ciphers_int(void)
+{
+
+#ifndef OPENSSL_NO_DES
+    EVP_add_cipher(EVP_des_cfb());
+    EVP_add_cipher(EVP_des_cfb1());
+    EVP_add_cipher(EVP_des_cfb8());
+    EVP_add_cipher(EVP_des_ede_cfb());
+    EVP_add_cipher(EVP_des_ede3_cfb());
+    EVP_add_cipher(EVP_des_ede3_cfb1());
+    EVP_add_cipher(EVP_des_ede3_cfb8());
+
+    EVP_add_cipher(EVP_des_ofb());
+    EVP_add_cipher(EVP_des_ede_ofb());
+    EVP_add_cipher(EVP_des_ede3_ofb());
+
+    EVP_add_cipher(EVP_desx_cbc());
+    EVP_add_cipher_alias(SN_desx_cbc, "DESX");
+    EVP_add_cipher_alias(SN_desx_cbc, "desx");
+
+    EVP_add_cipher(EVP_des_cbc());
+    EVP_add_cipher_alias(SN_des_cbc, "DES");
+    EVP_add_cipher_alias(SN_des_cbc, "des");
+    EVP_add_cipher(EVP_des_ede_cbc());
+    EVP_add_cipher(EVP_des_ede3_cbc());
+    EVP_add_cipher_alias(SN_des_ede3_cbc, "DES3");
+    EVP_add_cipher_alias(SN_des_ede3_cbc, "des3");
+
+    EVP_add_cipher(EVP_des_ecb());
+    EVP_add_cipher(EVP_des_ede());
+    EVP_add_cipher_alias(SN_des_ede_ecb, "DES-EDE-ECB");
+    EVP_add_cipher_alias(SN_des_ede_ecb, "des-ede-ecb");
+    EVP_add_cipher(EVP_des_ede3());
+    EVP_add_cipher_alias(SN_des_ede3_ecb, "DES-EDE3-ECB");
+    EVP_add_cipher_alias(SN_des_ede3_ecb, "des-ede3-ecb");
+# ifndef OPENSSL_NO_SHA
+    EVP_add_cipher(EVP_des_ede3_wrap());
+    EVP_add_cipher_alias(SN_id_smime_alg_CMS3DESwrap, "des3-wrap");
+# endif
+#endif
+
+#ifndef OPENSSL_NO_RC4
+    EVP_add_cipher(EVP_rc4());
+    EVP_add_cipher(EVP_rc4_40());
+# ifndef OPENSSL_NO_MD5
+    EVP_add_cipher(EVP_rc4_hmac_md5());
+# endif
+#endif
+
+#ifndef OPENSSL_NO_IDEA
+    EVP_add_cipher(EVP_idea_ecb());
+    EVP_add_cipher(EVP_idea_cfb());
+    EVP_add_cipher(EVP_idea_ofb());
+    EVP_add_cipher(EVP_idea_cbc());
+    EVP_add_cipher_alias(SN_idea_cbc, "IDEA");
+    EVP_add_cipher_alias(SN_idea_cbc, "idea");
+#endif
+
+#ifndef OPENSSL_NO_SEED
+    EVP_add_cipher(EVP_seed_ecb());
+    EVP_add_cipher(EVP_seed_cfb());
+    EVP_add_cipher(EVP_seed_ofb());
+    EVP_add_cipher(EVP_seed_cbc());
+    EVP_add_cipher_alias(SN_seed_cbc, "SEED");
+    EVP_add_cipher_alias(SN_seed_cbc, "seed");
+#endif
+
+#ifndef OPENSSL_NO_RC2
+    EVP_add_cipher(EVP_rc2_ecb());
+    EVP_add_cipher(EVP_rc2_cfb());
+    EVP_add_cipher(EVP_rc2_ofb());
+    EVP_add_cipher(EVP_rc2_cbc());
+    EVP_add_cipher(EVP_rc2_40_cbc());
+    EVP_add_cipher(EVP_rc2_64_cbc());
+    EVP_add_cipher_alias(SN_rc2_cbc, "RC2");
+    EVP_add_cipher_alias(SN_rc2_cbc, "rc2");
+    EVP_add_cipher_alias(SN_rc2_cbc, "rc2-128");
+    EVP_add_cipher_alias(SN_rc2_64_cbc, "rc2-64");
+    EVP_add_cipher_alias(SN_rc2_40_cbc, "rc2-40");
+#endif
+
+#ifndef OPENSSL_NO_BF
+    EVP_add_cipher(EVP_bf_ecb());
+    EVP_add_cipher(EVP_bf_cfb());
+    EVP_add_cipher(EVP_bf_ofb());
+    EVP_add_cipher(EVP_bf_cbc());
+    EVP_add_cipher_alias(SN_bf_cbc, "BF");
+    EVP_add_cipher_alias(SN_bf_cbc, "bf");
+    EVP_add_cipher_alias(SN_bf_cbc, "blowfish");
+#endif
+
+#ifndef OPENSSL_NO_CAST
+    EVP_add_cipher(EVP_cast5_ecb());
+    EVP_add_cipher(EVP_cast5_cfb());
+    EVP_add_cipher(EVP_cast5_ofb());
+    EVP_add_cipher(EVP_cast5_cbc());
+    EVP_add_cipher_alias(SN_cast5_cbc, "CAST");
+    EVP_add_cipher_alias(SN_cast5_cbc, "cast");
+    EVP_add_cipher_alias(SN_cast5_cbc, "CAST-cbc");
+    EVP_add_cipher_alias(SN_cast5_cbc, "cast-cbc");
+#endif
+
+#ifndef OPENSSL_NO_RC5
+    EVP_add_cipher(EVP_rc5_32_12_16_ecb());
+    EVP_add_cipher(EVP_rc5_32_12_16_cfb());
+    EVP_add_cipher(EVP_rc5_32_12_16_ofb());
+    EVP_add_cipher(EVP_rc5_32_12_16_cbc());
+    EVP_add_cipher_alias(SN_rc5_cbc, "rc5");
+    EVP_add_cipher_alias(SN_rc5_cbc, "RC5");
+#endif
+
+#ifndef OPENSSL_NO_AES
+    EVP_add_cipher(EVP_aes_128_ecb());
+    EVP_add_cipher(EVP_aes_128_cbc());
+    EVP_add_cipher(EVP_aes_128_cfb());
+    EVP_add_cipher(EVP_aes_128_cfb1());
+    EVP_add_cipher(EVP_aes_128_cfb8());
+    EVP_add_cipher(EVP_aes_128_ofb());
+    EVP_add_cipher(EVP_aes_128_ctr());
+    EVP_add_cipher(EVP_aes_128_gcm());
+# ifndef OPENSSL_NO_OCB
+    EVP_add_cipher(EVP_aes_128_ocb());
+# endif
+    EVP_add_cipher(EVP_aes_128_xts());
+    EVP_add_cipher(EVP_aes_128_ccm());
+    EVP_add_cipher(EVP_aes_128_wrap());
+    EVP_add_cipher_alias(SN_id_aes128_wrap, "aes128-wrap");
+    EVP_add_cipher(EVP_aes_128_wrap_pad());
+    EVP_add_cipher_alias(SN_aes_128_cbc, "AES128");
+    EVP_add_cipher_alias(SN_aes_128_cbc, "aes128");
+    EVP_add_cipher(EVP_aes_192_ecb());
+    EVP_add_cipher(EVP_aes_192_cbc());
+    EVP_add_cipher(EVP_aes_192_cfb());
+    EVP_add_cipher(EVP_aes_192_cfb1());
+    EVP_add_cipher(EVP_aes_192_cfb8());
+    EVP_add_cipher(EVP_aes_192_ofb());
+    EVP_add_cipher(EVP_aes_192_ctr());
+    EVP_add_cipher(EVP_aes_192_gcm());
+# ifndef OPENSSL_NO_OCB
+    EVP_add_cipher(EVP_aes_192_ocb());
+# endif
+    EVP_add_cipher(EVP_aes_192_ccm());
+    EVP_add_cipher(EVP_aes_192_wrap());
+    EVP_add_cipher_alias(SN_id_aes192_wrap, "aes192-wrap");
+    EVP_add_cipher(EVP_aes_192_wrap_pad());
+    EVP_add_cipher_alias(SN_aes_192_cbc, "AES192");
+    EVP_add_cipher_alias(SN_aes_192_cbc, "aes192");
+    EVP_add_cipher(EVP_aes_256_ecb());
+    EVP_add_cipher(EVP_aes_256_cbc());
+    EVP_add_cipher(EVP_aes_256_cfb());
+    EVP_add_cipher(EVP_aes_256_cfb1());
+    EVP_add_cipher(EVP_aes_256_cfb8());
+    EVP_add_cipher(EVP_aes_256_ofb());
+    EVP_add_cipher(EVP_aes_256_ctr());
+    EVP_add_cipher(EVP_aes_256_gcm());
+# ifndef OPENSSL_NO_OCB
+    EVP_add_cipher(EVP_aes_256_ocb());
+# endif
+    EVP_add_cipher(EVP_aes_256_xts());
+    EVP_add_cipher(EVP_aes_256_ccm());
+    EVP_add_cipher(EVP_aes_256_wrap());
+    EVP_add_cipher_alias(SN_id_aes256_wrap, "aes256-wrap");
+    EVP_add_cipher(EVP_aes_256_wrap_pad());
+    EVP_add_cipher_alias(SN_aes_256_cbc, "AES256");
+    EVP_add_cipher_alias(SN_aes_256_cbc, "aes256");
+#endif
+#if !defined(OPENSSL_NO_AES) && !defined(OPENSSL_NO_SHA)
+    EVP_add_cipher(EVP_aes_128_cbc_hmac_sha1());
+    EVP_add_cipher(EVP_aes_256_cbc_hmac_sha1());
+    EVP_add_cipher(EVP_aes_128_cbc_hmac_sha256());
+    EVP_add_cipher(EVP_aes_256_cbc_hmac_sha256());
+#endif
+
+#ifndef OPENSSL_NO_CAMELLIA
+    EVP_add_cipher(EVP_camellia_128_ecb());
+    EVP_add_cipher(EVP_camellia_128_cbc());
+    EVP_add_cipher(EVP_camellia_128_cfb());
+    EVP_add_cipher(EVP_camellia_128_cfb1());
+    EVP_add_cipher(EVP_camellia_128_cfb8());
+    EVP_add_cipher(EVP_camellia_128_ofb());
+    EVP_add_cipher_alias(SN_camellia_128_cbc, "CAMELLIA128");
+    EVP_add_cipher_alias(SN_camellia_128_cbc, "camellia128");
+    EVP_add_cipher(EVP_camellia_192_ecb());
+    EVP_add_cipher(EVP_camellia_192_cbc());
+    EVP_add_cipher(EVP_camellia_192_cfb());
+    EVP_add_cipher(EVP_camellia_192_cfb1());
+    EVP_add_cipher(EVP_camellia_192_cfb8());
+    EVP_add_cipher(EVP_camellia_192_ofb());
+    EVP_add_cipher_alias(SN_camellia_192_cbc, "CAMELLIA192");
+    EVP_add_cipher_alias(SN_camellia_192_cbc, "camellia192");
+    EVP_add_cipher(EVP_camellia_256_ecb());
+    EVP_add_cipher(EVP_camellia_256_cbc());
+    EVP_add_cipher(EVP_camellia_256_cfb());
+    EVP_add_cipher(EVP_camellia_256_cfb1());
+    EVP_add_cipher(EVP_camellia_256_cfb8());
+    EVP_add_cipher(EVP_camellia_256_ofb());
+    EVP_add_cipher_alias(SN_camellia_256_cbc, "CAMELLIA256");
+    EVP_add_cipher_alias(SN_camellia_256_cbc, "camellia256");
+    EVP_add_cipher(EVP_camellia_128_ctr());
+    EVP_add_cipher(EVP_camellia_192_ctr());
+    EVP_add_cipher(EVP_camellia_256_ctr());
+#endif
+
+#ifndef OPENSSL_NO_CHACHA
+    EVP_add_cipher(EVP_chacha20());
+# ifndef OPENSSL_NO_POLY1305
+    EVP_add_cipher(EVP_chacha20_poly1305());
+# endif
+#endif
+
+#ifndef OPENSSL_NO_SMS4
+    EVP_add_cipher(EVP_sms4_ecb());
+    EVP_add_cipher(EVP_sms4_cbc());
+    EVP_add_cipher(EVP_sms4_cfb());
+    EVP_add_cipher(EVP_sms4_cfb1());
+    EVP_add_cipher(EVP_sms4_cfb8());
+    EVP_add_cipher(EVP_sms4_ofb());
+    EVP_add_cipher(EVP_sms4_ctr());
+    EVP_add_cipher(EVP_sms4_ccm());
+    EVP_add_cipher(EVP_sms4_gcm());
+# ifndef OPENSSL_NO_OCB
+    EVP_add_cipher(EVP_sms4_ocb());
+# endif
+    EVP_add_cipher(EVP_sms4_xts());
+    EVP_add_cipher(EVP_sms4_wrap());
+    EVP_add_cipher(EVP_sms4_wrap_pad());
+    EVP_add_cipher_alias(SN_sms4_cbc,"SMS4");
+    EVP_add_cipher_alias(SN_sms4_cbc,"sms4");
+#endif
+#ifndef OPENSSL_NO_ZUC
+    EVP_add_cipher(EVP_zuc());
+    EVP_add_cipher(EVP_zuc256());
+#endif
+}
diff --git a/crypto/evp/c_alld.c b/crypto/evp/c_alld.c
new file mode 100644
index 000000000..736f2a8d1
--- /dev/null
+++ b/crypto/evp/c_alld.c
@@ -0,0 +1,62 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+#include 
+#include 
+
+void openssl_add_all_digests_int(void)
+{
+#ifndef OPENSSL_NO_MD4
+    EVP_add_digest(EVP_md4());
+#endif
+#ifndef OPENSSL_NO_MD5
+    EVP_add_digest(EVP_md5());
+    EVP_add_digest_alias(SN_md5, "ssl3-md5");
+#endif
+#if !defined(OPENSSL_NO_MD5) && !defined(OPENSSL_NO_SHA)
+    EVP_add_digest(EVP_md5_sha1());
+#endif
+#ifndef OPENSSL_NO_SHA
+    EVP_add_digest(EVP_sha1());
+    EVP_add_digest_alias(SN_sha1, "ssl3-sha1");
+    EVP_add_digest_alias(SN_sha1WithRSAEncryption, SN_sha1WithRSA);
+#endif
+#if !defined(OPENSSL_NO_MDC2) && !defined(OPENSSL_NO_DES)
+    EVP_add_digest(EVP_mdc2());
+#endif
+#ifndef OPENSSL_NO_RMD160
+    EVP_add_digest(EVP_ripemd160());
+    EVP_add_digest_alias(SN_ripemd160, "ripemd");
+    EVP_add_digest_alias(SN_ripemd160, "rmd160");
+#endif
+#ifndef OPENSSL_NO_SHA
+# ifndef OPENSSL_NO_SHA256
+    EVP_add_digest(EVP_sha224());
+    EVP_add_digest(EVP_sha256());
+# endif
+# ifndef OPENSSL_NO_SHA512
+    EVP_add_digest(EVP_sha384());
+    EVP_add_digest(EVP_sha512());
+# endif
+#endif
+#ifndef OPENSSL_NO_WHIRLPOOL
+    EVP_add_digest(EVP_whirlpool());
+#endif
+#ifndef OPENSSL_NO_BLAKE2
+    EVP_add_digest(EVP_blake2b512());
+    EVP_add_digest(EVP_blake2s256());
+#endif
+#ifndef OPENSSL_NO_SM3
+    EVP_add_digest(EVP_sm3());
+#endif
+}
diff --git a/crypto/evp/cmeth_lib.c b/crypto/evp/cmeth_lib.c
new file mode 100644
index 000000000..e2295c4dc
--- /dev/null
+++ b/crypto/evp/cmeth_lib.c
@@ -0,0 +1,151 @@
+/*
+ * Copyright 2015-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+
+#include 
+#include "internal/evp_int.h"
+#include "evp_locl.h"
+
+EVP_CIPHER *EVP_CIPHER_meth_new(int cipher_type, int block_size, int key_len)
+{
+    EVP_CIPHER *cipher = OPENSSL_zalloc(sizeof(EVP_CIPHER));
+
+    if (cipher != NULL) {
+        cipher->nid = cipher_type;
+        cipher->block_size = block_size;
+        cipher->key_len = key_len;
+    }
+    return cipher;
+}
+
+EVP_CIPHER *EVP_CIPHER_meth_dup(const EVP_CIPHER *cipher)
+{
+    EVP_CIPHER *to = EVP_CIPHER_meth_new(cipher->nid, cipher->block_size,
+                                         cipher->key_len);
+
+    if (to != NULL)
+        memcpy(to, cipher, sizeof(*to));
+    return to;
+}
+
+void EVP_CIPHER_meth_free(EVP_CIPHER *cipher)
+{
+    OPENSSL_free(cipher);
+}
+
+int EVP_CIPHER_meth_set_iv_length(EVP_CIPHER *cipher, int iv_len)
+{
+    cipher->iv_len = iv_len;
+    return 1;
+}
+
+int EVP_CIPHER_meth_set_flags(EVP_CIPHER *cipher, unsigned long flags)
+{
+    cipher->flags = flags;
+    return 1;
+}
+
+int EVP_CIPHER_meth_set_impl_ctx_size(EVP_CIPHER *cipher, int ctx_size)
+{
+    cipher->ctx_size = ctx_size;
+    return 1;
+}
+
+int EVP_CIPHER_meth_set_init(EVP_CIPHER *cipher,
+                             int (*init) (EVP_CIPHER_CTX *ctx,
+                                          const unsigned char *key,
+                                          const unsigned char *iv,
+                                          int enc))
+{
+    cipher->init = init;
+    return 1;
+}
+
+int EVP_CIPHER_meth_set_do_cipher(EVP_CIPHER *cipher,
+                                  int (*do_cipher) (EVP_CIPHER_CTX *ctx,
+                                                    unsigned char *out,
+                                                    const unsigned char *in,
+                                                    size_t inl))
+{
+    cipher->do_cipher = do_cipher;
+    return 1;
+}
+
+int EVP_CIPHER_meth_set_cleanup(EVP_CIPHER *cipher,
+                                int (*cleanup) (EVP_CIPHER_CTX *))
+{
+    cipher->cleanup = cleanup;
+    return 1;
+}
+
+int EVP_CIPHER_meth_set_set_asn1_params(EVP_CIPHER *cipher,
+                                        int (*set_asn1_parameters) (EVP_CIPHER_CTX *,
+                                                                    ASN1_TYPE *))
+{
+    cipher->set_asn1_parameters = set_asn1_parameters;
+    return 1;
+}
+
+int EVP_CIPHER_meth_set_get_asn1_params(EVP_CIPHER *cipher,
+                                        int (*get_asn1_parameters) (EVP_CIPHER_CTX *,
+                                                                    ASN1_TYPE *))
+{
+    cipher->get_asn1_parameters = get_asn1_parameters;
+    return 1;
+}
+
+int EVP_CIPHER_meth_set_ctrl(EVP_CIPHER *cipher,
+                             int (*ctrl) (EVP_CIPHER_CTX *, int type,
+                                          int arg, void *ptr))
+{
+    cipher->ctrl = ctrl;
+    return 1;
+}
+
+
+int (*EVP_CIPHER_meth_get_init(const EVP_CIPHER *cipher))(EVP_CIPHER_CTX *ctx,
+                                                          const unsigned char *key,
+                                                          const unsigned char *iv,
+                                                          int enc)
+{
+    return cipher->init;
+}
+int (*EVP_CIPHER_meth_get_do_cipher(const EVP_CIPHER *cipher))(EVP_CIPHER_CTX *ctx,
+                                                               unsigned char *out,
+                                                               const unsigned char *in,
+                                                               size_t inl)
+{
+    return cipher->do_cipher;
+}
+
+int (*EVP_CIPHER_meth_get_cleanup(const EVP_CIPHER *cipher))(EVP_CIPHER_CTX *)
+{
+    return cipher->cleanup;
+}
+
+int (*EVP_CIPHER_meth_get_set_asn1_params(const EVP_CIPHER *cipher))(EVP_CIPHER_CTX *,
+                                                                     ASN1_TYPE *)
+{
+    return cipher->set_asn1_parameters;
+}
+
+int (*EVP_CIPHER_meth_get_get_asn1_params(const EVP_CIPHER *cipher))(EVP_CIPHER_CTX *,
+                                                               ASN1_TYPE *)
+{
+    return cipher->get_asn1_parameters;
+}
+
+int (*EVP_CIPHER_meth_get_ctrl(const EVP_CIPHER *cipher))(EVP_CIPHER_CTX *,
+                                                          int type, int arg,
+                                                          void *ptr)
+{
+    return cipher->ctrl;
+}
+
diff --git a/crypto/evp/digest.c b/crypto/evp/digest.c
new file mode 100644
index 000000000..8e64808b4
--- /dev/null
+++ b/crypto/evp/digest.c
@@ -0,0 +1,289 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+#ifndef OPENSSL_NO_ENGINE
+# include 
+#endif
+#include "internal/evp_int.h"
+#include "evp_locl.h"
+#ifndef OPENSSL_NO_SM2
+# include 
+#endif
+
+/* This call frees resources associated with the context */
+int EVP_MD_CTX_reset(EVP_MD_CTX *ctx)
+{
+    if (ctx == NULL)
+        return 1;
+
+    /*
+     * Don't assume ctx->md_data was cleaned in EVP_Digest_Final, because
+     * sometimes only copies of the context are ever finalised.
+     */
+    if (ctx->digest && ctx->digest->cleanup
+        && !EVP_MD_CTX_test_flags(ctx, EVP_MD_CTX_FLAG_CLEANED))
+        ctx->digest->cleanup(ctx);
+    if (ctx->digest && ctx->digest->ctx_size && ctx->md_data
+        && !EVP_MD_CTX_test_flags(ctx, EVP_MD_CTX_FLAG_REUSE)) {
+        OPENSSL_clear_free(ctx->md_data, ctx->digest->ctx_size);
+    }
+    EVP_PKEY_CTX_free(ctx->pctx);
+#ifndef OPENSSL_NO_ENGINE
+    ENGINE_finish(ctx->engine);
+#endif
+    OPENSSL_cleanse(ctx, sizeof(*ctx));
+
+    return 1;
+}
+
+EVP_MD_CTX *EVP_MD_CTX_new(void)
+{
+    return OPENSSL_zalloc(sizeof(EVP_MD_CTX));
+}
+
+void EVP_MD_CTX_free(EVP_MD_CTX *ctx)
+{
+    EVP_MD_CTX_reset(ctx);
+    OPENSSL_free(ctx);
+}
+
+int EVP_DigestInit(EVP_MD_CTX *ctx, const EVP_MD *type)
+{
+    EVP_MD_CTX_reset(ctx);
+    return EVP_DigestInit_ex(ctx, type, NULL);
+}
+
+int EVP_DigestInit_ex(EVP_MD_CTX *ctx, const EVP_MD *type, ENGINE *impl)
+{
+    EVP_MD_CTX_clear_flags(ctx, EVP_MD_CTX_FLAG_CLEANED);
+#ifndef OPENSSL_NO_ENGINE
+    /*
+     * Whether it's nice or not, "Inits" can be used on "Final"'d contexts so
+     * this context may already have an ENGINE! Try to avoid releasing the
+     * previous handle, re-querying for an ENGINE, and having a
+     * reinitialisation, when it may all be unnecessary.
+     */
+    if (ctx->engine && ctx->digest &&
+        (type == NULL || (type->type == ctx->digest->type)))
+        goto skip_to_init;
+    if (type) {
+        /*
+         * Ensure an ENGINE left lying around from last time is cleared (the
+         * previous check attempted to avoid this if the same ENGINE and
+         * EVP_MD could be used).
+         */
+        ENGINE_finish(ctx->engine);
+        if (impl != NULL) {
+            if (!ENGINE_init(impl)) {
+                EVPerr(EVP_F_EVP_DIGESTINIT_EX, EVP_R_INITIALIZATION_ERROR);
+                return 0;
+            }
+        } else {
+            /* Ask if an ENGINE is reserved for this job */
+            impl = ENGINE_get_digest_engine(type->type);
+        }
+        if (impl != NULL) {
+            /* There's an ENGINE for this job ... (apparently) */
+            const EVP_MD *d = ENGINE_get_digest(impl, type->type);
+
+            if (d == NULL) {
+                EVPerr(EVP_F_EVP_DIGESTINIT_EX, EVP_R_INITIALIZATION_ERROR);
+                ENGINE_finish(impl);
+                return 0;
+            }
+            /* We'll use the ENGINE's private digest definition */
+            type = d;
+            /*
+             * Store the ENGINE functional reference so we know 'type' came
+             * from an ENGINE and we need to release it when done.
+             */
+            ctx->engine = impl;
+        } else
+            ctx->engine = NULL;
+    } else {
+        if (!ctx->digest) {
+            EVPerr(EVP_F_EVP_DIGESTINIT_EX, EVP_R_NO_DIGEST_SET);
+            return 0;
+        }
+        type = ctx->digest;
+    }
+#endif
+    if (ctx->digest != type) {
+        if (ctx->digest && ctx->digest->ctx_size) {
+            OPENSSL_clear_free(ctx->md_data, ctx->digest->ctx_size);
+            ctx->md_data = NULL;
+        }
+        ctx->digest = type;
+        if (!(ctx->flags & EVP_MD_CTX_FLAG_NO_INIT) && type->ctx_size) {
+            ctx->update = type->update;
+            ctx->md_data = OPENSSL_zalloc(type->ctx_size);
+            if (ctx->md_data == NULL) {
+                EVPerr(EVP_F_EVP_DIGESTINIT_EX, ERR_R_MALLOC_FAILURE);
+                return 0;
+            }
+        }
+    }
+#ifndef OPENSSL_NO_ENGINE
+ skip_to_init:
+#endif
+    if (ctx->pctx) {
+        int r;
+        r = EVP_PKEY_CTX_ctrl(ctx->pctx, -1, EVP_PKEY_OP_TYPE_SIG,
+                              EVP_PKEY_CTRL_DIGESTINIT, 0, ctx);
+        if (r <= 0 && (r != -2))
+            return 0;
+    }
+    if (ctx->flags & EVP_MD_CTX_FLAG_NO_INIT)
+        return 1;
+    return ctx->digest->init(ctx);
+}
+
+int EVP_DigestUpdate(EVP_MD_CTX *ctx, const void *data, size_t count)
+{
+#ifndef OPENSSL_NO_SM2
+	if (ctx->pctx && !EVP_MD_CTX_test_flags(ctx, EVP_MD_CTX_FLAG_UPDATED)
+		&& EVP_PKEY_id(EVP_PKEY_CTX_get0_pkey(ctx->pctx)) == EVP_PKEY_EC) {
+		const unsigned char *zid;
+		if (1 == EVP_PKEY_CTX_get_signer_zid(ctx->pctx, &zid)) {
+			ctx->update(ctx, zid, 32);
+# ifdef SM2_DEBUG
+			fprintf(stderr, "[SM2_DEBUG] %s() first update with SM2 ZID\n",
+				__FUNCTION__);
+# endif
+		}
+		EVP_MD_CTX_set_flags(ctx, EVP_MD_CTX_FLAG_UPDATED);
+	}
+#endif
+	return ctx->update(ctx, data, count);
+}
+
+/* The caller can assume that this removes any secret data from the context */
+int EVP_DigestFinal(EVP_MD_CTX *ctx, unsigned char *md, unsigned int *size)
+{
+    int ret;
+    ret = EVP_DigestFinal_ex(ctx, md, size);
+    EVP_MD_CTX_reset(ctx);
+    return ret;
+}
+
+/* The caller can assume that this removes any secret data from the context */
+int EVP_DigestFinal_ex(EVP_MD_CTX *ctx, unsigned char *md, unsigned int *size)
+{
+    int ret;
+
+    OPENSSL_assert(ctx->digest->md_size <= EVP_MAX_MD_SIZE);
+    ret = ctx->digest->final(ctx, md);
+    if (size != NULL)
+        *size = ctx->digest->md_size;
+    if (ctx->digest->cleanup) {
+        ctx->digest->cleanup(ctx);
+        EVP_MD_CTX_set_flags(ctx, EVP_MD_CTX_FLAG_CLEANED);
+    }
+    if (ctx->md_data)
+        OPENSSL_cleanse(ctx->md_data, ctx->digest->ctx_size);
+    return ret;
+}
+
+int EVP_MD_CTX_copy(EVP_MD_CTX *out, const EVP_MD_CTX *in)
+{
+    EVP_MD_CTX_reset(out);
+    return EVP_MD_CTX_copy_ex(out, in);
+}
+
+int EVP_MD_CTX_copy_ex(EVP_MD_CTX *out, const EVP_MD_CTX *in)
+{
+    unsigned char *tmp_buf;
+    if ((in == NULL) || (in->digest == NULL)) {
+        EVPerr(EVP_F_EVP_MD_CTX_COPY_EX, EVP_R_INPUT_NOT_INITIALIZED);
+        return 0;
+    }
+#ifndef OPENSSL_NO_ENGINE
+    /* Make sure it's safe to copy a digest context using an ENGINE */
+    if (in->engine && !ENGINE_init(in->engine)) {
+        EVPerr(EVP_F_EVP_MD_CTX_COPY_EX, ERR_R_ENGINE_LIB);
+        return 0;
+    }
+#endif
+
+    if (out->digest == in->digest) {
+        tmp_buf = out->md_data;
+        EVP_MD_CTX_set_flags(out, EVP_MD_CTX_FLAG_REUSE);
+    } else
+        tmp_buf = NULL;
+    EVP_MD_CTX_reset(out);
+    memcpy(out, in, sizeof(*out));
+
+    /* Null these variables, since they are getting fixed up
+     * properly below.  Anything else may cause a memleak and/or
+     * double free if any of the memory allocations below fail
+     */
+    out->md_data = NULL;
+    out->pctx = NULL;
+
+    if (in->md_data && out->digest->ctx_size) {
+        if (tmp_buf)
+            out->md_data = tmp_buf;
+        else {
+            out->md_data = OPENSSL_malloc(out->digest->ctx_size);
+            if (out->md_data == NULL) {
+                EVPerr(EVP_F_EVP_MD_CTX_COPY_EX, ERR_R_MALLOC_FAILURE);
+                return 0;
+            }
+        }
+        memcpy(out->md_data, in->md_data, out->digest->ctx_size);
+    }
+
+    out->update = in->update;
+
+    if (in->pctx) {
+        out->pctx = EVP_PKEY_CTX_dup(in->pctx);
+        if (!out->pctx) {
+            EVP_MD_CTX_reset(out);
+            return 0;
+        }
+    }
+
+    if (out->digest->copy)
+        return out->digest->copy(out, in);
+
+    return 1;
+}
+
+int EVP_Digest(const void *data, size_t count,
+               unsigned char *md, unsigned int *size, const EVP_MD *type,
+               ENGINE *impl)
+{
+    EVP_MD_CTX *ctx = EVP_MD_CTX_new();
+    int ret;
+
+    if (ctx == NULL)
+        return 0;
+    EVP_MD_CTX_set_flags(ctx, EVP_MD_CTX_FLAG_ONESHOT);
+    ret = EVP_DigestInit_ex(ctx, type, impl)
+        && EVP_DigestUpdate(ctx, data, count)
+        && EVP_DigestFinal_ex(ctx, md, size);
+    EVP_MD_CTX_free(ctx);
+
+    return ret;
+}
+
+int EVP_MD_CTX_ctrl(EVP_MD_CTX *ctx, int cmd, int p1, void *p2)
+{
+    if (ctx->digest && ctx->digest->md_ctrl) {
+        int ret = ctx->digest->md_ctrl(ctx, cmd, p1, p2);
+        if (ret <= 0)
+            return 0;
+        return 1;
+    }
+    return 0;
+}
diff --git a/crypto/evp/e_aes.c b/crypto/evp/e_aes.c
new file mode 100644
index 000000000..482b26cb7
--- /dev/null
+++ b/crypto/evp/e_aes.c
@@ -0,0 +1,2704 @@
+/*
+ * Copyright 2001-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#ifdef OPENSSL_NO_AES
+NON_EMPTY_TRANSLATION_UNIT
+#else
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include "internal/evp_int.h"
+#include "modes_lcl.h"
+#include 
+#include "evp_locl.h"
+
+typedef struct {
+    union {
+        double align;
+        AES_KEY ks;
+    } ks;
+    block128_f block;
+    union {
+        cbc128_f cbc;
+        ctr128_f ctr;
+    } stream;
+} EVP_AES_KEY;
+
+typedef struct {
+    union {
+        double align;
+        AES_KEY ks;
+    } ks;                       /* AES key schedule to use */
+    int key_set;                /* Set if key initialised */
+    int iv_set;                 /* Set if an iv is set */
+    GCM128_CONTEXT gcm;
+    unsigned char *iv;          /* Temporary IV store */
+    int ivlen;                  /* IV length */
+    int taglen;
+    int iv_gen;                 /* It is OK to generate IVs */
+    int tls_aad_len;            /* TLS AAD length */
+    ctr128_f ctr;
+} EVP_AES_GCM_CTX;
+
+typedef struct {
+    union {
+        double align;
+        AES_KEY ks;
+    } ks1, ks2;                 /* AES key schedules to use */
+    XTS128_CONTEXT xts;
+    void (*stream) (const unsigned char *in,
+                    unsigned char *out, size_t length,
+                    const AES_KEY *key1, const AES_KEY *key2,
+                    const unsigned char iv[16]);
+} EVP_AES_XTS_CTX;
+
+typedef struct {
+    union {
+        double align;
+        AES_KEY ks;
+    } ks;                       /* AES key schedule to use */
+    int key_set;                /* Set if key initialised */
+    int iv_set;                 /* Set if an iv is set */
+    int tag_set;                /* Set if tag is valid */
+    int len_set;                /* Set if message length set */
+    int L, M;                   /* L and M parameters from RFC3610 */
+    int tls_aad_len;            /* TLS AAD length */
+    CCM128_CONTEXT ccm;
+    ccm128_f str;
+} EVP_AES_CCM_CTX;
+
+#ifndef OPENSSL_NO_OCB
+typedef struct {
+    union {
+        double align;
+        AES_KEY ks;
+    } ksenc;                    /* AES key schedule to use for encryption */
+    union {
+        double align;
+        AES_KEY ks;
+    } ksdec;                    /* AES key schedule to use for decryption */
+    int key_set;                /* Set if key initialised */
+    int iv_set;                 /* Set if an iv is set */
+    OCB128_CONTEXT ocb;
+    unsigned char *iv;          /* Temporary IV store */
+    unsigned char tag[16];
+    unsigned char data_buf[16]; /* Store partial data blocks */
+    unsigned char aad_buf[16];  /* Store partial AAD blocks */
+    int data_buf_len;
+    int aad_buf_len;
+    int ivlen;                  /* IV length */
+    int taglen;
+} EVP_AES_OCB_CTX;
+#endif
+
+#define MAXBITCHUNK     ((size_t)1<<(sizeof(size_t)*8-4))
+
+#ifdef VPAES_ASM
+int vpaes_set_encrypt_key(const unsigned char *userKey, int bits,
+                          AES_KEY *key);
+int vpaes_set_decrypt_key(const unsigned char *userKey, int bits,
+                          AES_KEY *key);
+
+void vpaes_encrypt(const unsigned char *in, unsigned char *out,
+                   const AES_KEY *key);
+void vpaes_decrypt(const unsigned char *in, unsigned char *out,
+                   const AES_KEY *key);
+
+void vpaes_cbc_encrypt(const unsigned char *in,
+                       unsigned char *out,
+                       size_t length,
+                       const AES_KEY *key, unsigned char *ivec, int enc);
+#endif
+#ifdef BSAES_ASM
+void bsaes_cbc_encrypt(const unsigned char *in, unsigned char *out,
+                       size_t length, const AES_KEY *key,
+                       unsigned char ivec[16], int enc);
+void bsaes_ctr32_encrypt_blocks(const unsigned char *in, unsigned char *out,
+                                size_t len, const AES_KEY *key,
+                                const unsigned char ivec[16]);
+void bsaes_xts_encrypt(const unsigned char *inp, unsigned char *out,
+                       size_t len, const AES_KEY *key1,
+                       const AES_KEY *key2, const unsigned char iv[16]);
+void bsaes_xts_decrypt(const unsigned char *inp, unsigned char *out,
+                       size_t len, const AES_KEY *key1,
+                       const AES_KEY *key2, const unsigned char iv[16]);
+#endif
+#ifdef AES_CTR_ASM
+void AES_ctr32_encrypt(const unsigned char *in, unsigned char *out,
+                       size_t blocks, const AES_KEY *key,
+                       const unsigned char ivec[AES_BLOCK_SIZE]);
+#endif
+#ifdef AES_XTS_ASM
+void AES_xts_encrypt(const char *inp, char *out, size_t len,
+                     const AES_KEY *key1, const AES_KEY *key2,
+                     const unsigned char iv[16]);
+void AES_xts_decrypt(const char *inp, char *out, size_t len,
+                     const AES_KEY *key1, const AES_KEY *key2,
+                     const unsigned char iv[16]);
+#endif
+
+#if defined(OPENSSL_CPUID_OBJ) && (defined(__powerpc__) || defined(__ppc__) || defined(_ARCH_PPC))
+# include "ppc_arch.h"
+# ifdef VPAES_ASM
+#  define VPAES_CAPABLE (OPENSSL_ppccap_P & PPC_ALTIVEC)
+# endif
+# define HWAES_CAPABLE  (OPENSSL_ppccap_P & PPC_CRYPTO207)
+# define HWAES_set_encrypt_key aes_p8_set_encrypt_key
+# define HWAES_set_decrypt_key aes_p8_set_decrypt_key
+# define HWAES_encrypt aes_p8_encrypt
+# define HWAES_decrypt aes_p8_decrypt
+# define HWAES_cbc_encrypt aes_p8_cbc_encrypt
+# define HWAES_ctr32_encrypt_blocks aes_p8_ctr32_encrypt_blocks
+# define HWAES_xts_encrypt aes_p8_xts_encrypt
+# define HWAES_xts_decrypt aes_p8_xts_decrypt
+#endif
+
+#if     defined(AES_ASM) && !defined(I386_ONLY) &&      (  \
+        ((defined(__i386)       || defined(__i386__)    || \
+          defined(_M_IX86)) && defined(OPENSSL_IA32_SSE2))|| \
+        defined(__x86_64)       || defined(__x86_64__)  || \
+        defined(_M_AMD64)       || defined(_M_X64)      )
+
+extern unsigned int OPENSSL_ia32cap_P[];
+
+# ifdef VPAES_ASM
+#  define VPAES_CAPABLE   (OPENSSL_ia32cap_P[1]&(1<<(41-32)))
+# endif
+# ifdef BSAES_ASM
+#  define BSAES_CAPABLE   (OPENSSL_ia32cap_P[1]&(1<<(41-32)))
+# endif
+/*
+ * AES-NI section
+ */
+# define AESNI_CAPABLE   (OPENSSL_ia32cap_P[1]&(1<<(57-32)))
+
+int aesni_set_encrypt_key(const unsigned char *userKey, int bits,
+                          AES_KEY *key);
+int aesni_set_decrypt_key(const unsigned char *userKey, int bits,
+                          AES_KEY *key);
+
+void aesni_encrypt(const unsigned char *in, unsigned char *out,
+                   const AES_KEY *key);
+void aesni_decrypt(const unsigned char *in, unsigned char *out,
+                   const AES_KEY *key);
+
+void aesni_ecb_encrypt(const unsigned char *in,
+                       unsigned char *out,
+                       size_t length, const AES_KEY *key, int enc);
+void aesni_cbc_encrypt(const unsigned char *in,
+                       unsigned char *out,
+                       size_t length,
+                       const AES_KEY *key, unsigned char *ivec, int enc);
+
+void aesni_ctr32_encrypt_blocks(const unsigned char *in,
+                                unsigned char *out,
+                                size_t blocks,
+                                const void *key, const unsigned char *ivec);
+
+void aesni_xts_encrypt(const unsigned char *in,
+                       unsigned char *out,
+                       size_t length,
+                       const AES_KEY *key1, const AES_KEY *key2,
+                       const unsigned char iv[16]);
+
+void aesni_xts_decrypt(const unsigned char *in,
+                       unsigned char *out,
+                       size_t length,
+                       const AES_KEY *key1, const AES_KEY *key2,
+                       const unsigned char iv[16]);
+
+void aesni_ccm64_encrypt_blocks(const unsigned char *in,
+                                unsigned char *out,
+                                size_t blocks,
+                                const void *key,
+                                const unsigned char ivec[16],
+                                unsigned char cmac[16]);
+
+void aesni_ccm64_decrypt_blocks(const unsigned char *in,
+                                unsigned char *out,
+                                size_t blocks,
+                                const void *key,
+                                const unsigned char ivec[16],
+                                unsigned char cmac[16]);
+
+# if defined(__x86_64) || defined(__x86_64__) || defined(_M_AMD64) || defined(_M_X64)
+size_t aesni_gcm_encrypt(const unsigned char *in,
+                         unsigned char *out,
+                         size_t len,
+                         const void *key, unsigned char ivec[16], u64 *Xi);
+#  define AES_gcm_encrypt aesni_gcm_encrypt
+size_t aesni_gcm_decrypt(const unsigned char *in,
+                         unsigned char *out,
+                         size_t len,
+                         const void *key, unsigned char ivec[16], u64 *Xi);
+#  define AES_gcm_decrypt aesni_gcm_decrypt
+void gcm_ghash_avx(u64 Xi[2], const u128 Htable[16], const u8 *in,
+                   size_t len);
+#  define AES_GCM_ASM(gctx)       (gctx->ctr==aesni_ctr32_encrypt_blocks && \
+                                 gctx->gcm.ghash==gcm_ghash_avx)
+#  define AES_GCM_ASM2(gctx)      (gctx->gcm.block==(block128_f)aesni_encrypt && \
+                                 gctx->gcm.ghash==gcm_ghash_avx)
+#  undef AES_GCM_ASM2          /* minor size optimization */
+# endif
+
+static int aesni_init_key(EVP_CIPHER_CTX *ctx, const unsigned char *key,
+                          const unsigned char *iv, int enc)
+{
+    int ret, mode;
+    EVP_AES_KEY *dat = EVP_C_DATA(EVP_AES_KEY,ctx);
+
+    mode = EVP_CIPHER_CTX_mode(ctx);
+    if ((mode == EVP_CIPH_ECB_MODE || mode == EVP_CIPH_CBC_MODE)
+        && !enc) {
+        ret = aesni_set_decrypt_key(key, EVP_CIPHER_CTX_key_length(ctx) * 8,
+                                    &dat->ks.ks);
+        dat->block = (block128_f) aesni_decrypt;
+        dat->stream.cbc = mode == EVP_CIPH_CBC_MODE ?
+            (cbc128_f) aesni_cbc_encrypt : NULL;
+    } else {
+        ret = aesni_set_encrypt_key(key, EVP_CIPHER_CTX_key_length(ctx) * 8,
+                                    &dat->ks.ks);
+        dat->block = (block128_f) aesni_encrypt;
+        if (mode == EVP_CIPH_CBC_MODE)
+            dat->stream.cbc = (cbc128_f) aesni_cbc_encrypt;
+        else if (mode == EVP_CIPH_CTR_MODE)
+            dat->stream.ctr = (ctr128_f) aesni_ctr32_encrypt_blocks;
+        else
+            dat->stream.cbc = NULL;
+    }
+
+    if (ret < 0) {
+        EVPerr(EVP_F_AESNI_INIT_KEY, EVP_R_AES_KEY_SETUP_FAILED);
+        return 0;
+    }
+
+    return 1;
+}
+
+static int aesni_cbc_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
+                            const unsigned char *in, size_t len)
+{
+    aesni_cbc_encrypt(in, out, len, &EVP_C_DATA(EVP_AES_KEY,ctx)->ks.ks,
+                      EVP_CIPHER_CTX_iv_noconst(ctx),
+                      EVP_CIPHER_CTX_encrypting(ctx));
+
+    return 1;
+}
+
+static int aesni_ecb_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
+                            const unsigned char *in, size_t len)
+{
+    size_t bl = EVP_CIPHER_CTX_block_size(ctx);
+
+    if (len < bl)
+        return 1;
+
+    aesni_ecb_encrypt(in, out, len, &EVP_C_DATA(EVP_AES_KEY,ctx)->ks.ks,
+                      EVP_CIPHER_CTX_encrypting(ctx));
+
+    return 1;
+}
+
+# define aesni_ofb_cipher aes_ofb_cipher
+static int aesni_ofb_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
+                            const unsigned char *in, size_t len);
+
+# define aesni_cfb_cipher aes_cfb_cipher
+static int aesni_cfb_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
+                            const unsigned char *in, size_t len);
+
+# define aesni_cfb8_cipher aes_cfb8_cipher
+static int aesni_cfb8_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
+                             const unsigned char *in, size_t len);
+
+# define aesni_cfb1_cipher aes_cfb1_cipher
+static int aesni_cfb1_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
+                             const unsigned char *in, size_t len);
+
+# define aesni_ctr_cipher aes_ctr_cipher
+static int aesni_ctr_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
+                            const unsigned char *in, size_t len);
+
+static int aesni_gcm_init_key(EVP_CIPHER_CTX *ctx, const unsigned char *key,
+                              const unsigned char *iv, int enc)
+{
+    EVP_AES_GCM_CTX *gctx = EVP_C_DATA(EVP_AES_GCM_CTX,ctx);
+    if (!iv && !key)
+        return 1;
+    if (key) {
+        aesni_set_encrypt_key(key, EVP_CIPHER_CTX_key_length(ctx) * 8,
+                              &gctx->ks.ks);
+        CRYPTO_gcm128_init(&gctx->gcm, &gctx->ks, (block128_f) aesni_encrypt);
+        gctx->ctr = (ctr128_f) aesni_ctr32_encrypt_blocks;
+        /*
+         * If we have an iv can set it directly, otherwise use saved IV.
+         */
+        if (iv == NULL && gctx->iv_set)
+            iv = gctx->iv;
+        if (iv) {
+            CRYPTO_gcm128_setiv(&gctx->gcm, iv, gctx->ivlen);
+            gctx->iv_set = 1;
+        }
+        gctx->key_set = 1;
+    } else {
+        /* If key set use IV, otherwise copy */
+        if (gctx->key_set)
+            CRYPTO_gcm128_setiv(&gctx->gcm, iv, gctx->ivlen);
+        else
+            memcpy(gctx->iv, iv, gctx->ivlen);
+        gctx->iv_set = 1;
+        gctx->iv_gen = 0;
+    }
+    return 1;
+}
+
+# define aesni_gcm_cipher aes_gcm_cipher
+static int aesni_gcm_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
+                            const unsigned char *in, size_t len);
+
+static int aesni_xts_init_key(EVP_CIPHER_CTX *ctx, const unsigned char *key,
+                              const unsigned char *iv, int enc)
+{
+    EVP_AES_XTS_CTX *xctx = EVP_C_DATA(EVP_AES_XTS_CTX,ctx);
+    if (!iv && !key)
+        return 1;
+
+    if (key) {
+        /* key_len is two AES keys */
+        if (enc) {
+            aesni_set_encrypt_key(key, EVP_CIPHER_CTX_key_length(ctx) * 4,
+                                  &xctx->ks1.ks);
+            xctx->xts.block1 = (block128_f) aesni_encrypt;
+            xctx->stream = aesni_xts_encrypt;
+        } else {
+            aesni_set_decrypt_key(key, EVP_CIPHER_CTX_key_length(ctx) * 4,
+                                  &xctx->ks1.ks);
+            xctx->xts.block1 = (block128_f) aesni_decrypt;
+            xctx->stream = aesni_xts_decrypt;
+        }
+
+        aesni_set_encrypt_key(key + EVP_CIPHER_CTX_key_length(ctx) / 2,
+                              EVP_CIPHER_CTX_key_length(ctx) * 4,
+                              &xctx->ks2.ks);
+        xctx->xts.block2 = (block128_f) aesni_encrypt;
+
+        xctx->xts.key1 = &xctx->ks1;
+    }
+
+    if (iv) {
+        xctx->xts.key2 = &xctx->ks2;
+        memcpy(EVP_CIPHER_CTX_iv_noconst(ctx), iv, 16);
+    }
+
+    return 1;
+}
+
+# define aesni_xts_cipher aes_xts_cipher
+static int aesni_xts_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
+                            const unsigned char *in, size_t len);
+
+static int aesni_ccm_init_key(EVP_CIPHER_CTX *ctx, const unsigned char *key,
+                              const unsigned char *iv, int enc)
+{
+    EVP_AES_CCM_CTX *cctx = EVP_C_DATA(EVP_AES_CCM_CTX,ctx);
+    if (!iv && !key)
+        return 1;
+    if (key) {
+        aesni_set_encrypt_key(key, EVP_CIPHER_CTX_key_length(ctx) * 8,
+                              &cctx->ks.ks);
+        CRYPTO_ccm128_init(&cctx->ccm, cctx->M, cctx->L,
+                           &cctx->ks, (block128_f) aesni_encrypt);
+        cctx->str = enc ? (ccm128_f) aesni_ccm64_encrypt_blocks :
+            (ccm128_f) aesni_ccm64_decrypt_blocks;
+        cctx->key_set = 1;
+    }
+    if (iv) {
+        memcpy(EVP_CIPHER_CTX_iv_noconst(ctx), iv, 15 - cctx->L);
+        cctx->iv_set = 1;
+    }
+    return 1;
+}
+
+# define aesni_ccm_cipher aes_ccm_cipher
+static int aesni_ccm_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
+                            const unsigned char *in, size_t len);
+
+# ifndef OPENSSL_NO_OCB
+void aesni_ocb_encrypt(const unsigned char *in, unsigned char *out,
+                       size_t blocks, const void *key,
+                       size_t start_block_num,
+                       unsigned char offset_i[16],
+                       const unsigned char L_[][16],
+                       unsigned char checksum[16]);
+void aesni_ocb_decrypt(const unsigned char *in, unsigned char *out,
+                       size_t blocks, const void *key,
+                       size_t start_block_num,
+                       unsigned char offset_i[16],
+                       const unsigned char L_[][16],
+                       unsigned char checksum[16]);
+
+static int aesni_ocb_init_key(EVP_CIPHER_CTX *ctx, const unsigned char *key,
+                              const unsigned char *iv, int enc)
+{
+    EVP_AES_OCB_CTX *octx = EVP_C_DATA(EVP_AES_OCB_CTX,ctx);
+    if (!iv && !key)
+        return 1;
+    if (key) {
+        do {
+            /*
+             * We set both the encrypt and decrypt key here because decrypt
+             * needs both. We could possibly optimise to remove setting the
+             * decrypt for an encryption operation.
+             */
+            aesni_set_encrypt_key(key, EVP_CIPHER_CTX_key_length(ctx) * 8,
+                                  &octx->ksenc.ks);
+            aesni_set_decrypt_key(key, EVP_CIPHER_CTX_key_length(ctx) * 8,
+                                  &octx->ksdec.ks);
+            if (!CRYPTO_ocb128_init(&octx->ocb,
+                                    &octx->ksenc.ks, &octx->ksdec.ks,
+                                    (block128_f) aesni_encrypt,
+                                    (block128_f) aesni_decrypt,
+                                    enc ? aesni_ocb_encrypt
+                                        : aesni_ocb_decrypt))
+                return 0;
+        }
+        while (0);
+
+        /*
+         * If we have an iv we can set it directly, otherwise use saved IV.
+         */
+        if (iv == NULL && octx->iv_set)
+            iv = octx->iv;
+        if (iv) {
+            if (CRYPTO_ocb128_setiv(&octx->ocb, iv, octx->ivlen, octx->taglen)
+                != 1)
+                return 0;
+            octx->iv_set = 1;
+        }
+        octx->key_set = 1;
+    } else {
+        /* If key set use IV, otherwise copy */
+        if (octx->key_set)
+            CRYPTO_ocb128_setiv(&octx->ocb, iv, octx->ivlen, octx->taglen);
+        else
+            memcpy(octx->iv, iv, octx->ivlen);
+        octx->iv_set = 1;
+    }
+    return 1;
+}
+
+#  define aesni_ocb_cipher aes_ocb_cipher
+static int aesni_ocb_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
+                            const unsigned char *in, size_t len);
+# endif                        /* OPENSSL_NO_OCB */
+
+# define BLOCK_CIPHER_generic(nid,keylen,blocksize,ivlen,nmode,mode,MODE,flags) \
+static const EVP_CIPHER aesni_##keylen##_##mode = { \
+        nid##_##keylen##_##nmode,blocksize,keylen/8,ivlen, \
+        flags|EVP_CIPH_##MODE##_MODE,   \
+        aesni_init_key,                 \
+        aesni_##mode##_cipher,          \
+        NULL,                           \
+        sizeof(EVP_AES_KEY),            \
+        NULL,NULL,NULL,NULL }; \
+static const EVP_CIPHER aes_##keylen##_##mode = { \
+        nid##_##keylen##_##nmode,blocksize,     \
+        keylen/8,ivlen, \
+        flags|EVP_CIPH_##MODE##_MODE,   \
+        aes_init_key,                   \
+        aes_##mode##_cipher,            \
+        NULL,                           \
+        sizeof(EVP_AES_KEY),            \
+        NULL,NULL,NULL,NULL }; \
+const EVP_CIPHER *EVP_aes_##keylen##_##mode(void) \
+{ return AESNI_CAPABLE?&aesni_##keylen##_##mode:&aes_##keylen##_##mode; }
+
+# define BLOCK_CIPHER_custom(nid,keylen,blocksize,ivlen,mode,MODE,flags) \
+static const EVP_CIPHER aesni_##keylen##_##mode = { \
+        nid##_##keylen##_##mode,blocksize, \
+        (EVP_CIPH_##MODE##_MODE==EVP_CIPH_XTS_MODE?2:1)*keylen/8, ivlen, \
+        flags|EVP_CIPH_##MODE##_MODE,   \
+        aesni_##mode##_init_key,        \
+        aesni_##mode##_cipher,          \
+        aes_##mode##_cleanup,           \
+        sizeof(EVP_AES_##MODE##_CTX),   \
+        NULL,NULL,aes_##mode##_ctrl,NULL }; \
+static const EVP_CIPHER aes_##keylen##_##mode = { \
+        nid##_##keylen##_##mode,blocksize, \
+        (EVP_CIPH_##MODE##_MODE==EVP_CIPH_XTS_MODE?2:1)*keylen/8, ivlen, \
+        flags|EVP_CIPH_##MODE##_MODE,   \
+        aes_##mode##_init_key,          \
+        aes_##mode##_cipher,            \
+        aes_##mode##_cleanup,           \
+        sizeof(EVP_AES_##MODE##_CTX),   \
+        NULL,NULL,aes_##mode##_ctrl,NULL }; \
+const EVP_CIPHER *EVP_aes_##keylen##_##mode(void) \
+{ return AESNI_CAPABLE?&aesni_##keylen##_##mode:&aes_##keylen##_##mode; }
+
+#elif   defined(AES_ASM) && (defined(__sparc) || defined(__sparc__))
+
+# include "sparc_arch.h"
+
+extern unsigned int OPENSSL_sparcv9cap_P[];
+
+/*
+ * Initial Fujitsu SPARC64 X support
+ */
+# define HWAES_CAPABLE           (OPENSSL_sparcv9cap_P[0] & SPARCV9_FJAESX)
+# define HWAES_set_encrypt_key aes_fx_set_encrypt_key
+# define HWAES_set_decrypt_key aes_fx_set_decrypt_key
+# define HWAES_encrypt aes_fx_encrypt
+# define HWAES_decrypt aes_fx_decrypt
+# define HWAES_cbc_encrypt aes_fx_cbc_encrypt
+# define HWAES_ctr32_encrypt_blocks aes_fx_ctr32_encrypt_blocks
+
+# define SPARC_AES_CAPABLE       (OPENSSL_sparcv9cap_P[1] & CFR_AES)
+
+void aes_t4_set_encrypt_key(const unsigned char *key, int bits, AES_KEY *ks);
+void aes_t4_set_decrypt_key(const unsigned char *key, int bits, AES_KEY *ks);
+void aes_t4_encrypt(const unsigned char *in, unsigned char *out,
+                    const AES_KEY *key);
+void aes_t4_decrypt(const unsigned char *in, unsigned char *out,
+                    const AES_KEY *key);
+/*
+ * Key-length specific subroutines were chosen for following reason.
+ * Each SPARC T4 core can execute up to 8 threads which share core's
+ * resources. Loading as much key material to registers allows to
+ * minimize references to shared memory interface, as well as amount
+ * of instructions in inner loops [much needed on T4]. But then having
+ * non-key-length specific routines would require conditional branches
+ * either in inner loops or on subroutines' entries. Former is hardly
+ * acceptable, while latter means code size increase to size occupied
+ * by multiple key-length specific subroutines, so why fight?
+ */
+void aes128_t4_cbc_encrypt(const unsigned char *in, unsigned char *out,
+                           size_t len, const AES_KEY *key,
+                           unsigned char *ivec);
+void aes128_t4_cbc_decrypt(const unsigned char *in, unsigned char *out,
+                           size_t len, const AES_KEY *key,
+                           unsigned char *ivec);
+void aes192_t4_cbc_encrypt(const unsigned char *in, unsigned char *out,
+                           size_t len, const AES_KEY *key,
+                           unsigned char *ivec);
+void aes192_t4_cbc_decrypt(const unsigned char *in, unsigned char *out,
+                           size_t len, const AES_KEY *key,
+                           unsigned char *ivec);
+void aes256_t4_cbc_encrypt(const unsigned char *in, unsigned char *out,
+                           size_t len, const AES_KEY *key,
+                           unsigned char *ivec);
+void aes256_t4_cbc_decrypt(const unsigned char *in, unsigned char *out,
+                           size_t len, const AES_KEY *key,
+                           unsigned char *ivec);
+void aes128_t4_ctr32_encrypt(const unsigned char *in, unsigned char *out,
+                             size_t blocks, const AES_KEY *key,
+                             unsigned char *ivec);
+void aes192_t4_ctr32_encrypt(const unsigned char *in, unsigned char *out,
+                             size_t blocks, const AES_KEY *key,
+                             unsigned char *ivec);
+void aes256_t4_ctr32_encrypt(const unsigned char *in, unsigned char *out,
+                             size_t blocks, const AES_KEY *key,
+                             unsigned char *ivec);
+void aes128_t4_xts_encrypt(const unsigned char *in, unsigned char *out,
+                           size_t blocks, const AES_KEY *key1,
+                           const AES_KEY *key2, const unsigned char *ivec);
+void aes128_t4_xts_decrypt(const unsigned char *in, unsigned char *out,
+                           size_t blocks, const AES_KEY *key1,
+                           const AES_KEY *key2, const unsigned char *ivec);
+void aes256_t4_xts_encrypt(const unsigned char *in, unsigned char *out,
+                           size_t blocks, const AES_KEY *key1,
+                           const AES_KEY *key2, const unsigned char *ivec);
+void aes256_t4_xts_decrypt(const unsigned char *in, unsigned char *out,
+                           size_t blocks, const AES_KEY *key1,
+                           const AES_KEY *key2, const unsigned char *ivec);
+
+static int aes_t4_init_key(EVP_CIPHER_CTX *ctx, const unsigned char *key,
+                           const unsigned char *iv, int enc)
+{
+    int ret, mode, bits;
+    EVP_AES_KEY *dat = EVP_C_DATA(EVP_AES_KEY,ctx);
+
+    mode = EVP_CIPHER_CTX_mode(ctx);
+    bits = EVP_CIPHER_CTX_key_length(ctx) * 8;
+    if ((mode == EVP_CIPH_ECB_MODE || mode == EVP_CIPH_CBC_MODE)
+        && !enc) {
+        ret = 0;
+        aes_t4_set_decrypt_key(key, bits, &dat->ks.ks);
+        dat->block = (block128_f) aes_t4_decrypt;
+        switch (bits) {
+        case 128:
+            dat->stream.cbc = mode == EVP_CIPH_CBC_MODE ?
+                (cbc128_f) aes128_t4_cbc_decrypt : NULL;
+            break;
+        case 192:
+            dat->stream.cbc = mode == EVP_CIPH_CBC_MODE ?
+                (cbc128_f) aes192_t4_cbc_decrypt : NULL;
+            break;
+        case 256:
+            dat->stream.cbc = mode == EVP_CIPH_CBC_MODE ?
+                (cbc128_f) aes256_t4_cbc_decrypt : NULL;
+            break;
+        default:
+            ret = -1;
+        }
+    } else {
+        ret = 0;
+        aes_t4_set_encrypt_key(key, bits, &dat->ks.ks);
+        dat->block = (block128_f) aes_t4_encrypt;
+        switch (bits) {
+        case 128:
+            if (mode == EVP_CIPH_CBC_MODE)
+                dat->stream.cbc = (cbc128_f) aes128_t4_cbc_encrypt;
+            else if (mode == EVP_CIPH_CTR_MODE)
+                dat->stream.ctr = (ctr128_f) aes128_t4_ctr32_encrypt;
+            else
+                dat->stream.cbc = NULL;
+            break;
+        case 192:
+            if (mode == EVP_CIPH_CBC_MODE)
+                dat->stream.cbc = (cbc128_f) aes192_t4_cbc_encrypt;
+            else if (mode == EVP_CIPH_CTR_MODE)
+                dat->stream.ctr = (ctr128_f) aes192_t4_ctr32_encrypt;
+            else
+                dat->stream.cbc = NULL;
+            break;
+        case 256:
+            if (mode == EVP_CIPH_CBC_MODE)
+                dat->stream.cbc = (cbc128_f) aes256_t4_cbc_encrypt;
+            else if (mode == EVP_CIPH_CTR_MODE)
+                dat->stream.ctr = (ctr128_f) aes256_t4_ctr32_encrypt;
+            else
+                dat->stream.cbc = NULL;
+            break;
+        default:
+            ret = -1;
+        }
+    }
+
+    if (ret < 0) {
+        EVPerr(EVP_F_AES_T4_INIT_KEY, EVP_R_AES_KEY_SETUP_FAILED);
+        return 0;
+    }
+
+    return 1;
+}
+
+# define aes_t4_cbc_cipher aes_cbc_cipher
+static int aes_t4_cbc_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
+                             const unsigned char *in, size_t len);
+
+# define aes_t4_ecb_cipher aes_ecb_cipher
+static int aes_t4_ecb_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
+                             const unsigned char *in, size_t len);
+
+# define aes_t4_ofb_cipher aes_ofb_cipher
+static int aes_t4_ofb_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
+                             const unsigned char *in, size_t len);
+
+# define aes_t4_cfb_cipher aes_cfb_cipher
+static int aes_t4_cfb_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
+                             const unsigned char *in, size_t len);
+
+# define aes_t4_cfb8_cipher aes_cfb8_cipher
+static int aes_t4_cfb8_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
+                              const unsigned char *in, size_t len);
+
+# define aes_t4_cfb1_cipher aes_cfb1_cipher
+static int aes_t4_cfb1_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
+                              const unsigned char *in, size_t len);
+
+# define aes_t4_ctr_cipher aes_ctr_cipher
+static int aes_t4_ctr_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
+                             const unsigned char *in, size_t len);
+
+static int aes_t4_gcm_init_key(EVP_CIPHER_CTX *ctx, const unsigned char *key,
+                               const unsigned char *iv, int enc)
+{
+    EVP_AES_GCM_CTX *gctx = EVP_C_DATA(EVP_AES_GCM_CTX,ctx);
+    if (!iv && !key)
+        return 1;
+    if (key) {
+        int bits = EVP_CIPHER_CTX_key_length(ctx) * 8;
+        aes_t4_set_encrypt_key(key, bits, &gctx->ks.ks);
+        CRYPTO_gcm128_init(&gctx->gcm, &gctx->ks,
+                           (block128_f) aes_t4_encrypt);
+        switch (bits) {
+        case 128:
+            gctx->ctr = (ctr128_f) aes128_t4_ctr32_encrypt;
+            break;
+        case 192:
+            gctx->ctr = (ctr128_f) aes192_t4_ctr32_encrypt;
+            break;
+        case 256:
+            gctx->ctr = (ctr128_f) aes256_t4_ctr32_encrypt;
+            break;
+        default:
+            return 0;
+        }
+        /*
+         * If we have an iv can set it directly, otherwise use saved IV.
+         */
+        if (iv == NULL && gctx->iv_set)
+            iv = gctx->iv;
+        if (iv) {
+            CRYPTO_gcm128_setiv(&gctx->gcm, iv, gctx->ivlen);
+            gctx->iv_set = 1;
+        }
+        gctx->key_set = 1;
+    } else {
+        /* If key set use IV, otherwise copy */
+        if (gctx->key_set)
+            CRYPTO_gcm128_setiv(&gctx->gcm, iv, gctx->ivlen);
+        else
+            memcpy(gctx->iv, iv, gctx->ivlen);
+        gctx->iv_set = 1;
+        gctx->iv_gen = 0;
+    }
+    return 1;
+}
+
+# define aes_t4_gcm_cipher aes_gcm_cipher
+static int aes_t4_gcm_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
+                             const unsigned char *in, size_t len);
+
+static int aes_t4_xts_init_key(EVP_CIPHER_CTX *ctx, const unsigned char *key,
+                               const unsigned char *iv, int enc)
+{
+    EVP_AES_XTS_CTX *xctx = EVP_C_DATA(EVP_AES_XTS_CTX,ctx);
+    if (!iv && !key)
+        return 1;
+
+    if (key) {
+        int bits = EVP_CIPHER_CTX_key_length(ctx) * 4;
+        xctx->stream = NULL;
+        /* key_len is two AES keys */
+        if (enc) {
+            aes_t4_set_encrypt_key(key, bits, &xctx->ks1.ks);
+            xctx->xts.block1 = (block128_f) aes_t4_encrypt;
+            switch (bits) {
+            case 128:
+                xctx->stream = aes128_t4_xts_encrypt;
+                break;
+            case 256:
+                xctx->stream = aes256_t4_xts_encrypt;
+                break;
+            default:
+                return 0;
+            }
+        } else {
+            aes_t4_set_decrypt_key(key, EVP_CIPHER_CTX_key_length(ctx) * 4,
+                                   &xctx->ks1.ks);
+            xctx->xts.block1 = (block128_f) aes_t4_decrypt;
+            switch (bits) {
+            case 128:
+                xctx->stream = aes128_t4_xts_decrypt;
+                break;
+            case 256:
+                xctx->stream = aes256_t4_xts_decrypt;
+                break;
+            default:
+                return 0;
+            }
+        }
+
+        aes_t4_set_encrypt_key(key + EVP_CIPHER_CTX_key_length(ctx) / 2,
+                               EVP_CIPHER_CTX_key_length(ctx) * 4,
+                               &xctx->ks2.ks);
+        xctx->xts.block2 = (block128_f) aes_t4_encrypt;
+
+        xctx->xts.key1 = &xctx->ks1;
+    }
+
+    if (iv) {
+        xctx->xts.key2 = &xctx->ks2;
+        memcpy(EVP_CIPHER_CTX_iv_noconst(ctx), iv, 16);
+    }
+
+    return 1;
+}
+
+# define aes_t4_xts_cipher aes_xts_cipher
+static int aes_t4_xts_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
+                             const unsigned char *in, size_t len);
+
+static int aes_t4_ccm_init_key(EVP_CIPHER_CTX *ctx, const unsigned char *key,
+                               const unsigned char *iv, int enc)
+{
+    EVP_AES_CCM_CTX *cctx = EVP_C_DATA(EVP_AES_CCM_CTX,ctx);
+    if (!iv && !key)
+        return 1;
+    if (key) {
+        int bits = EVP_CIPHER_CTX_key_length(ctx) * 8;
+        aes_t4_set_encrypt_key(key, bits, &cctx->ks.ks);
+        CRYPTO_ccm128_init(&cctx->ccm, cctx->M, cctx->L,
+                           &cctx->ks, (block128_f) aes_t4_encrypt);
+        cctx->str = NULL;
+        cctx->key_set = 1;
+    }
+    if (iv) {
+        memcpy(EVP_CIPHER_CTX_iv_noconst(ctx), iv, 15 - cctx->L);
+        cctx->iv_set = 1;
+    }
+    return 1;
+}
+
+# define aes_t4_ccm_cipher aes_ccm_cipher
+static int aes_t4_ccm_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
+                             const unsigned char *in, size_t len);
+
+# ifndef OPENSSL_NO_OCB
+static int aes_t4_ocb_init_key(EVP_CIPHER_CTX *ctx, const unsigned char *key,
+                               const unsigned char *iv, int enc)
+{
+    EVP_AES_OCB_CTX *octx = EVP_C_DATA(EVP_AES_OCB_CTX,ctx);
+    if (!iv && !key)
+        return 1;
+    if (key) {
+        do {
+            /*
+             * We set both the encrypt and decrypt key here because decrypt
+             * needs both. We could possibly optimise to remove setting the
+             * decrypt for an encryption operation.
+             */
+            aes_t4_set_encrypt_key(key, EVP_CIPHER_CTX_key_length(ctx) * 8,
+                                   &octx->ksenc.ks);
+            aes_t4_set_decrypt_key(key, EVP_CIPHER_CTX_key_length(ctx) * 8,
+                                   &octx->ksdec.ks);
+            if (!CRYPTO_ocb128_init(&octx->ocb,
+                                    &octx->ksenc.ks, &octx->ksdec.ks,
+                                    (block128_f) aes_t4_encrypt,
+                                    (block128_f) aes_t4_decrypt,
+                                    NULL))
+                return 0;
+        }
+        while (0);
+
+        /*
+         * If we have an iv we can set it directly, otherwise use saved IV.
+         */
+        if (iv == NULL && octx->iv_set)
+            iv = octx->iv;
+        if (iv) {
+            if (CRYPTO_ocb128_setiv(&octx->ocb, iv, octx->ivlen, octx->taglen)
+                != 1)
+                return 0;
+            octx->iv_set = 1;
+        }
+        octx->key_set = 1;
+    } else {
+        /* If key set use IV, otherwise copy */
+        if (octx->key_set)
+            CRYPTO_ocb128_setiv(&octx->ocb, iv, octx->ivlen, octx->taglen);
+        else
+            memcpy(octx->iv, iv, octx->ivlen);
+        octx->iv_set = 1;
+    }
+    return 1;
+}
+
+#  define aes_t4_ocb_cipher aes_ocb_cipher
+static int aes_t4_ocb_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
+                             const unsigned char *in, size_t len);
+# endif                        /* OPENSSL_NO_OCB */
+
+# define BLOCK_CIPHER_generic(nid,keylen,blocksize,ivlen,nmode,mode,MODE,flags) \
+static const EVP_CIPHER aes_t4_##keylen##_##mode = { \
+        nid##_##keylen##_##nmode,blocksize,keylen/8,ivlen, \
+        flags|EVP_CIPH_##MODE##_MODE,   \
+        aes_t4_init_key,                \
+        aes_t4_##mode##_cipher,         \
+        NULL,                           \
+        sizeof(EVP_AES_KEY),            \
+        NULL,NULL,NULL,NULL }; \
+static const EVP_CIPHER aes_##keylen##_##mode = { \
+        nid##_##keylen##_##nmode,blocksize,     \
+        keylen/8,ivlen, \
+        flags|EVP_CIPH_##MODE##_MODE,   \
+        aes_init_key,                   \
+        aes_##mode##_cipher,            \
+        NULL,                           \
+        sizeof(EVP_AES_KEY),            \
+        NULL,NULL,NULL,NULL }; \
+const EVP_CIPHER *EVP_aes_##keylen##_##mode(void) \
+{ return SPARC_AES_CAPABLE?&aes_t4_##keylen##_##mode:&aes_##keylen##_##mode; }
+
+# define BLOCK_CIPHER_custom(nid,keylen,blocksize,ivlen,mode,MODE,flags) \
+static const EVP_CIPHER aes_t4_##keylen##_##mode = { \
+        nid##_##keylen##_##mode,blocksize, \
+        (EVP_CIPH_##MODE##_MODE==EVP_CIPH_XTS_MODE?2:1)*keylen/8, ivlen, \
+        flags|EVP_CIPH_##MODE##_MODE,   \
+        aes_t4_##mode##_init_key,       \
+        aes_t4_##mode##_cipher,         \
+        aes_##mode##_cleanup,           \
+        sizeof(EVP_AES_##MODE##_CTX),   \
+        NULL,NULL,aes_##mode##_ctrl,NULL }; \
+static const EVP_CIPHER aes_##keylen##_##mode = { \
+        nid##_##keylen##_##mode,blocksize, \
+        (EVP_CIPH_##MODE##_MODE==EVP_CIPH_XTS_MODE?2:1)*keylen/8, ivlen, \
+        flags|EVP_CIPH_##MODE##_MODE,   \
+        aes_##mode##_init_key,          \
+        aes_##mode##_cipher,            \
+        aes_##mode##_cleanup,           \
+        sizeof(EVP_AES_##MODE##_CTX),   \
+        NULL,NULL,aes_##mode##_ctrl,NULL }; \
+const EVP_CIPHER *EVP_aes_##keylen##_##mode(void) \
+{ return SPARC_AES_CAPABLE?&aes_t4_##keylen##_##mode:&aes_##keylen##_##mode; }
+
+#else
+
+# define BLOCK_CIPHER_generic(nid,keylen,blocksize,ivlen,nmode,mode,MODE,flags) \
+static const EVP_CIPHER aes_##keylen##_##mode = { \
+        nid##_##keylen##_##nmode,blocksize,keylen/8,ivlen, \
+        flags|EVP_CIPH_##MODE##_MODE,   \
+        aes_init_key,                   \
+        aes_##mode##_cipher,            \
+        NULL,                           \
+        sizeof(EVP_AES_KEY),            \
+        NULL,NULL,NULL,NULL }; \
+const EVP_CIPHER *EVP_aes_##keylen##_##mode(void) \
+{ return &aes_##keylen##_##mode; }
+
+# define BLOCK_CIPHER_custom(nid,keylen,blocksize,ivlen,mode,MODE,flags) \
+static const EVP_CIPHER aes_##keylen##_##mode = { \
+        nid##_##keylen##_##mode,blocksize, \
+        (EVP_CIPH_##MODE##_MODE==EVP_CIPH_XTS_MODE?2:1)*keylen/8, ivlen, \
+        flags|EVP_CIPH_##MODE##_MODE,   \
+        aes_##mode##_init_key,          \
+        aes_##mode##_cipher,            \
+        aes_##mode##_cleanup,           \
+        sizeof(EVP_AES_##MODE##_CTX),   \
+        NULL,NULL,aes_##mode##_ctrl,NULL }; \
+const EVP_CIPHER *EVP_aes_##keylen##_##mode(void) \
+{ return &aes_##keylen##_##mode; }
+
+#endif
+
+#if defined(OPENSSL_CPUID_OBJ) && (defined(__arm__) || defined(__arm) || defined(__aarch64__))
+# include "arm_arch.h"
+# if __ARM_MAX_ARCH__>=7
+#  if defined(BSAES_ASM)
+#   define BSAES_CAPABLE (OPENSSL_armcap_P & ARMV7_NEON)
+#  endif
+#  if defined(VPAES_ASM)
+#   define VPAES_CAPABLE (OPENSSL_armcap_P & ARMV7_NEON)
+#  endif
+#  define HWAES_CAPABLE (OPENSSL_armcap_P & ARMV8_AES)
+#  define HWAES_set_encrypt_key aes_v8_set_encrypt_key
+#  define HWAES_set_decrypt_key aes_v8_set_decrypt_key
+#  define HWAES_encrypt aes_v8_encrypt
+#  define HWAES_decrypt aes_v8_decrypt
+#  define HWAES_cbc_encrypt aes_v8_cbc_encrypt
+#  define HWAES_ctr32_encrypt_blocks aes_v8_ctr32_encrypt_blocks
+# endif
+#endif
+
+#if defined(HWAES_CAPABLE)
+int HWAES_set_encrypt_key(const unsigned char *userKey, const int bits,
+                          AES_KEY *key);
+int HWAES_set_decrypt_key(const unsigned char *userKey, const int bits,
+                          AES_KEY *key);
+void HWAES_encrypt(const unsigned char *in, unsigned char *out,
+                   const AES_KEY *key);
+void HWAES_decrypt(const unsigned char *in, unsigned char *out,
+                   const AES_KEY *key);
+void HWAES_cbc_encrypt(const unsigned char *in, unsigned char *out,
+                       size_t length, const AES_KEY *key,
+                       unsigned char *ivec, const int enc);
+void HWAES_ctr32_encrypt_blocks(const unsigned char *in, unsigned char *out,
+                                size_t len, const AES_KEY *key,
+                                const unsigned char ivec[16]);
+void HWAES_xts_encrypt(const unsigned char *inp, unsigned char *out,
+                       size_t len, const AES_KEY *key1,
+                       const AES_KEY *key2, const unsigned char iv[16]);
+void HWAES_xts_decrypt(const unsigned char *inp, unsigned char *out,
+                       size_t len, const AES_KEY *key1,
+                       const AES_KEY *key2, const unsigned char iv[16]);
+#endif
+
+#define BLOCK_CIPHER_generic_pack(nid,keylen,flags)             \
+        BLOCK_CIPHER_generic(nid,keylen,16,16,cbc,cbc,CBC,flags|EVP_CIPH_FLAG_DEFAULT_ASN1)     \
+        BLOCK_CIPHER_generic(nid,keylen,16,0,ecb,ecb,ECB,flags|EVP_CIPH_FLAG_DEFAULT_ASN1)      \
+        BLOCK_CIPHER_generic(nid,keylen,1,16,ofb128,ofb,OFB,flags|EVP_CIPH_FLAG_DEFAULT_ASN1)   \
+        BLOCK_CIPHER_generic(nid,keylen,1,16,cfb128,cfb,CFB,flags|EVP_CIPH_FLAG_DEFAULT_ASN1)   \
+        BLOCK_CIPHER_generic(nid,keylen,1,16,cfb1,cfb1,CFB,flags)       \
+        BLOCK_CIPHER_generic(nid,keylen,1,16,cfb8,cfb8,CFB,flags)       \
+        BLOCK_CIPHER_generic(nid,keylen,1,16,ctr,ctr,CTR,flags)
+
+static int aes_init_key(EVP_CIPHER_CTX *ctx, const unsigned char *key,
+                        const unsigned char *iv, int enc)
+{
+    int ret, mode;
+    EVP_AES_KEY *dat = EVP_C_DATA(EVP_AES_KEY,ctx);
+
+    mode = EVP_CIPHER_CTX_mode(ctx);
+    if ((mode == EVP_CIPH_ECB_MODE || mode == EVP_CIPH_CBC_MODE)
+        && !enc) {
+#ifdef HWAES_CAPABLE
+        if (HWAES_CAPABLE) {
+            ret = HWAES_set_decrypt_key(key,
+                                        EVP_CIPHER_CTX_key_length(ctx) * 8,
+                                        &dat->ks.ks);
+            dat->block = (block128_f) HWAES_decrypt;
+            dat->stream.cbc = NULL;
+# ifdef HWAES_cbc_encrypt
+            if (mode == EVP_CIPH_CBC_MODE)
+                dat->stream.cbc = (cbc128_f) HWAES_cbc_encrypt;
+# endif
+        } else
+#endif
+#ifdef BSAES_CAPABLE
+        if (BSAES_CAPABLE && mode == EVP_CIPH_CBC_MODE) {
+            ret = AES_set_decrypt_key(key, EVP_CIPHER_CTX_key_length(ctx) * 8,
+                                      &dat->ks.ks);
+            dat->block = (block128_f) AES_decrypt;
+            dat->stream.cbc = (cbc128_f) bsaes_cbc_encrypt;
+        } else
+#endif
+#ifdef VPAES_CAPABLE
+        if (VPAES_CAPABLE) {
+            ret = vpaes_set_decrypt_key(key,
+                                        EVP_CIPHER_CTX_key_length(ctx) * 8,
+                                        &dat->ks.ks);
+            dat->block = (block128_f) vpaes_decrypt;
+            dat->stream.cbc = mode == EVP_CIPH_CBC_MODE ?
+                (cbc128_f) vpaes_cbc_encrypt : NULL;
+        } else
+#endif
+        {
+            ret = AES_set_decrypt_key(key,
+                                      EVP_CIPHER_CTX_key_length(ctx) * 8,
+                                      &dat->ks.ks);
+            dat->block = (block128_f) AES_decrypt;
+            dat->stream.cbc = mode == EVP_CIPH_CBC_MODE ?
+                (cbc128_f) AES_cbc_encrypt : NULL;
+        }
+    } else
+#ifdef HWAES_CAPABLE
+    if (HWAES_CAPABLE) {
+        ret = HWAES_set_encrypt_key(key, EVP_CIPHER_CTX_key_length(ctx) * 8,
+                                    &dat->ks.ks);
+        dat->block = (block128_f) HWAES_encrypt;
+        dat->stream.cbc = NULL;
+# ifdef HWAES_cbc_encrypt
+        if (mode == EVP_CIPH_CBC_MODE)
+            dat->stream.cbc = (cbc128_f) HWAES_cbc_encrypt;
+        else
+# endif
+# ifdef HWAES_ctr32_encrypt_blocks
+        if (mode == EVP_CIPH_CTR_MODE)
+            dat->stream.ctr = (ctr128_f) HWAES_ctr32_encrypt_blocks;
+        else
+# endif
+            (void)0;            /* terminate potentially open 'else' */
+    } else
+#endif
+#ifdef BSAES_CAPABLE
+    if (BSAES_CAPABLE && mode == EVP_CIPH_CTR_MODE) {
+        ret = AES_set_encrypt_key(key, EVP_CIPHER_CTX_key_length(ctx) * 8,
+                                  &dat->ks.ks);
+        dat->block = (block128_f) AES_encrypt;
+        dat->stream.ctr = (ctr128_f) bsaes_ctr32_encrypt_blocks;
+    } else
+#endif
+#ifdef VPAES_CAPABLE
+    if (VPAES_CAPABLE) {
+        ret = vpaes_set_encrypt_key(key, EVP_CIPHER_CTX_key_length(ctx) * 8,
+                                    &dat->ks.ks);
+        dat->block = (block128_f) vpaes_encrypt;
+        dat->stream.cbc = mode == EVP_CIPH_CBC_MODE ?
+            (cbc128_f) vpaes_cbc_encrypt : NULL;
+    } else
+#endif
+    {
+        ret = AES_set_encrypt_key(key, EVP_CIPHER_CTX_key_length(ctx) * 8,
+                                  &dat->ks.ks);
+        dat->block = (block128_f) AES_encrypt;
+        dat->stream.cbc = mode == EVP_CIPH_CBC_MODE ?
+            (cbc128_f) AES_cbc_encrypt : NULL;
+#ifdef AES_CTR_ASM
+        if (mode == EVP_CIPH_CTR_MODE)
+            dat->stream.ctr = (ctr128_f) AES_ctr32_encrypt;
+#endif
+    }
+
+    if (ret < 0) {
+        EVPerr(EVP_F_AES_INIT_KEY, EVP_R_AES_KEY_SETUP_FAILED);
+        return 0;
+    }
+
+    return 1;
+}
+
+static int aes_cbc_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
+                          const unsigned char *in, size_t len)
+{
+    EVP_AES_KEY *dat = EVP_C_DATA(EVP_AES_KEY,ctx);
+
+    if (dat->stream.cbc)
+        (*dat->stream.cbc) (in, out, len, &dat->ks,
+                            EVP_CIPHER_CTX_iv_noconst(ctx),
+                            EVP_CIPHER_CTX_encrypting(ctx));
+    else if (EVP_CIPHER_CTX_encrypting(ctx))
+        CRYPTO_cbc128_encrypt(in, out, len, &dat->ks,
+                              EVP_CIPHER_CTX_iv_noconst(ctx), dat->block);
+    else
+        CRYPTO_cbc128_decrypt(in, out, len, &dat->ks,
+                              EVP_CIPHER_CTX_iv_noconst(ctx), dat->block);
+
+    return 1;
+}
+
+static int aes_ecb_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
+                          const unsigned char *in, size_t len)
+{
+    size_t bl = EVP_CIPHER_CTX_block_size(ctx);
+    size_t i;
+    EVP_AES_KEY *dat = EVP_C_DATA(EVP_AES_KEY,ctx);
+
+    if (len < bl)
+        return 1;
+
+    for (i = 0, len -= bl; i <= len; i += bl)
+        (*dat->block) (in + i, out + i, &dat->ks);
+
+    return 1;
+}
+
+static int aes_ofb_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
+                          const unsigned char *in, size_t len)
+{
+    EVP_AES_KEY *dat = EVP_C_DATA(EVP_AES_KEY,ctx);
+
+    int num = EVP_CIPHER_CTX_num(ctx);
+    CRYPTO_ofb128_encrypt(in, out, len, &dat->ks,
+                          EVP_CIPHER_CTX_iv_noconst(ctx), &num, dat->block);
+    EVP_CIPHER_CTX_set_num(ctx, num);
+    return 1;
+}
+
+static int aes_cfb_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
+                          const unsigned char *in, size_t len)
+{
+    EVP_AES_KEY *dat = EVP_C_DATA(EVP_AES_KEY,ctx);
+
+    int num = EVP_CIPHER_CTX_num(ctx);
+    CRYPTO_cfb128_encrypt(in, out, len, &dat->ks,
+                          EVP_CIPHER_CTX_iv_noconst(ctx), &num,
+                          EVP_CIPHER_CTX_encrypting(ctx), dat->block);
+    EVP_CIPHER_CTX_set_num(ctx, num);
+    return 1;
+}
+
+static int aes_cfb8_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
+                           const unsigned char *in, size_t len)
+{
+    EVP_AES_KEY *dat = EVP_C_DATA(EVP_AES_KEY,ctx);
+
+    int num = EVP_CIPHER_CTX_num(ctx);
+    CRYPTO_cfb128_8_encrypt(in, out, len, &dat->ks,
+                            EVP_CIPHER_CTX_iv_noconst(ctx), &num,
+                            EVP_CIPHER_CTX_encrypting(ctx), dat->block);
+    EVP_CIPHER_CTX_set_num(ctx, num);
+    return 1;
+}
+
+static int aes_cfb1_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
+                           const unsigned char *in, size_t len)
+{
+    EVP_AES_KEY *dat = EVP_C_DATA(EVP_AES_KEY,ctx);
+
+    if (EVP_CIPHER_CTX_test_flags(ctx, EVP_CIPH_FLAG_LENGTH_BITS)) {
+        int num = EVP_CIPHER_CTX_num(ctx);
+        CRYPTO_cfb128_1_encrypt(in, out, len, &dat->ks,
+                                EVP_CIPHER_CTX_iv_noconst(ctx), &num,
+                                EVP_CIPHER_CTX_encrypting(ctx), dat->block);
+        EVP_CIPHER_CTX_set_num(ctx, num);
+        return 1;
+    }
+
+    while (len >= MAXBITCHUNK) {
+        int num = EVP_CIPHER_CTX_num(ctx);
+        CRYPTO_cfb128_1_encrypt(in, out, MAXBITCHUNK * 8, &dat->ks,
+                                EVP_CIPHER_CTX_iv_noconst(ctx), &num,
+                                EVP_CIPHER_CTX_encrypting(ctx), dat->block);
+        EVP_CIPHER_CTX_set_num(ctx, num);
+        len -= MAXBITCHUNK;
+    }
+    if (len) {
+        int num = EVP_CIPHER_CTX_num(ctx);
+        CRYPTO_cfb128_1_encrypt(in, out, len * 8, &dat->ks,
+                                EVP_CIPHER_CTX_iv_noconst(ctx), &num,
+                                EVP_CIPHER_CTX_encrypting(ctx), dat->block);
+        EVP_CIPHER_CTX_set_num(ctx, num);
+    }
+
+    return 1;
+}
+
+static int aes_ctr_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
+                          const unsigned char *in, size_t len)
+{
+    unsigned int num = EVP_CIPHER_CTX_num(ctx);
+    EVP_AES_KEY *dat = EVP_C_DATA(EVP_AES_KEY,ctx);
+
+    if (dat->stream.ctr)
+        CRYPTO_ctr128_encrypt_ctr32(in, out, len, &dat->ks,
+                                    EVP_CIPHER_CTX_iv_noconst(ctx),
+                                    EVP_CIPHER_CTX_buf_noconst(ctx),
+                                    &num, dat->stream.ctr);
+    else
+        CRYPTO_ctr128_encrypt(in, out, len, &dat->ks,
+                              EVP_CIPHER_CTX_iv_noconst(ctx),
+                              EVP_CIPHER_CTX_buf_noconst(ctx), &num,
+                              dat->block);
+    EVP_CIPHER_CTX_set_num(ctx, num);
+    return 1;
+}
+
+BLOCK_CIPHER_generic_pack(NID_aes, 128, 0)
+    BLOCK_CIPHER_generic_pack(NID_aes, 192, 0)
+    BLOCK_CIPHER_generic_pack(NID_aes, 256, 0)
+
+static int aes_gcm_cleanup(EVP_CIPHER_CTX *c)
+{
+    EVP_AES_GCM_CTX *gctx = EVP_C_DATA(EVP_AES_GCM_CTX,c);
+    OPENSSL_cleanse(&gctx->gcm, sizeof(gctx->gcm));
+    if (gctx->iv != EVP_CIPHER_CTX_iv_noconst(c))
+        OPENSSL_free(gctx->iv);
+    return 1;
+}
+
+/* increment counter (64-bit int) by 1 */
+static void ctr64_inc(unsigned char *counter)
+{
+    int n = 8;
+    unsigned char c;
+
+    do {
+        --n;
+        c = counter[n];
+        ++c;
+        counter[n] = c;
+        if (c)
+            return;
+    } while (n);
+}
+
+static int aes_gcm_ctrl(EVP_CIPHER_CTX *c, int type, int arg, void *ptr)
+{
+    EVP_AES_GCM_CTX *gctx = EVP_C_DATA(EVP_AES_GCM_CTX,c);
+    switch (type) {
+    case EVP_CTRL_INIT:
+        gctx->key_set = 0;
+        gctx->iv_set = 0;
+        gctx->ivlen = EVP_CIPHER_CTX_iv_length(c);
+        gctx->iv = EVP_CIPHER_CTX_iv_noconst(c);
+        gctx->taglen = -1;
+        gctx->iv_gen = 0;
+        gctx->tls_aad_len = -1;
+        return 1;
+
+    case EVP_CTRL_AEAD_SET_IVLEN:
+        if (arg <= 0)
+            return 0;
+        /* Allocate memory for IV if needed */
+        if ((arg > EVP_MAX_IV_LENGTH) && (arg > gctx->ivlen)) {
+            if (gctx->iv != EVP_CIPHER_CTX_iv_noconst(c))
+                OPENSSL_free(gctx->iv);
+            gctx->iv = OPENSSL_malloc(arg);
+            if (gctx->iv == NULL)
+                return 0;
+        }
+        gctx->ivlen = arg;
+        return 1;
+
+    case EVP_CTRL_AEAD_SET_TAG:
+        if (arg <= 0 || arg > 16 || EVP_CIPHER_CTX_encrypting(c))
+            return 0;
+        memcpy(EVP_CIPHER_CTX_buf_noconst(c), ptr, arg);
+        gctx->taglen = arg;
+        return 1;
+
+    case EVP_CTRL_AEAD_GET_TAG:
+        if (arg <= 0 || arg > 16 || !EVP_CIPHER_CTX_encrypting(c)
+            || gctx->taglen < 0)
+            return 0;
+        memcpy(ptr, EVP_CIPHER_CTX_buf_noconst(c), arg);
+        return 1;
+
+    case EVP_CTRL_GCM_SET_IV_FIXED:
+        /* Special case: -1 length restores whole IV */
+        if (arg == -1) {
+            memcpy(gctx->iv, ptr, gctx->ivlen);
+            gctx->iv_gen = 1;
+            return 1;
+        }
+        /*
+         * Fixed field must be at least 4 bytes and invocation field at least
+         * 8.
+         */
+        if ((arg < 4) || (gctx->ivlen - arg) < 8)
+            return 0;
+        if (arg)
+            memcpy(gctx->iv, ptr, arg);
+        if (EVP_CIPHER_CTX_encrypting(c)
+            && RAND_bytes(gctx->iv + arg, gctx->ivlen - arg) <= 0)
+            return 0;
+        gctx->iv_gen = 1;
+        return 1;
+
+    case EVP_CTRL_GCM_IV_GEN:
+        if (gctx->iv_gen == 0 || gctx->key_set == 0)
+            return 0;
+        CRYPTO_gcm128_setiv(&gctx->gcm, gctx->iv, gctx->ivlen);
+        if (arg <= 0 || arg > gctx->ivlen)
+            arg = gctx->ivlen;
+        memcpy(ptr, gctx->iv + gctx->ivlen - arg, arg);
+        /*
+         * Invocation field will be at least 8 bytes in size and so no need
+         * to check wrap around or increment more than last 8 bytes.
+         */
+        ctr64_inc(gctx->iv + gctx->ivlen - 8);
+        gctx->iv_set = 1;
+        return 1;
+
+    case EVP_CTRL_GCM_SET_IV_INV:
+        if (gctx->iv_gen == 0 || gctx->key_set == 0
+            || EVP_CIPHER_CTX_encrypting(c))
+            return 0;
+        memcpy(gctx->iv + gctx->ivlen - arg, ptr, arg);
+        CRYPTO_gcm128_setiv(&gctx->gcm, gctx->iv, gctx->ivlen);
+        gctx->iv_set = 1;
+        return 1;
+
+    case EVP_CTRL_AEAD_TLS1_AAD:
+        /* Save the AAD for later use */
+        if (arg != EVP_AEAD_TLS1_AAD_LEN)
+            return 0;
+        memcpy(EVP_CIPHER_CTX_buf_noconst(c), ptr, arg);
+        gctx->tls_aad_len = arg;
+        {
+            unsigned int len =
+                EVP_CIPHER_CTX_buf_noconst(c)[arg - 2] << 8
+                | EVP_CIPHER_CTX_buf_noconst(c)[arg - 1];
+            /* Correct length for explicit IV */
+            if (len < EVP_GCM_TLS_EXPLICIT_IV_LEN)
+                return 0;
+            len -= EVP_GCM_TLS_EXPLICIT_IV_LEN;
+            /* If decrypting correct for tag too */
+            if (!EVP_CIPHER_CTX_encrypting(c)) {
+                if (len < EVP_GCM_TLS_TAG_LEN)
+                    return 0;
+                len -= EVP_GCM_TLS_TAG_LEN;
+            }
+            EVP_CIPHER_CTX_buf_noconst(c)[arg - 2] = len >> 8;
+            EVP_CIPHER_CTX_buf_noconst(c)[arg - 1] = len & 0xff;
+        }
+        /* Extra padding: tag appended to record */
+        return EVP_GCM_TLS_TAG_LEN;
+
+    case EVP_CTRL_COPY:
+        {
+            EVP_CIPHER_CTX *out = ptr;
+            EVP_AES_GCM_CTX *gctx_out = EVP_C_DATA(EVP_AES_GCM_CTX,out);
+            if (gctx->gcm.key) {
+                if (gctx->gcm.key != &gctx->ks)
+                    return 0;
+                gctx_out->gcm.key = &gctx_out->ks;
+            }
+            if (gctx->iv == EVP_CIPHER_CTX_iv_noconst(c))
+                gctx_out->iv = EVP_CIPHER_CTX_iv_noconst(out);
+            else {
+                gctx_out->iv = OPENSSL_malloc(gctx->ivlen);
+                if (gctx_out->iv == NULL)
+                    return 0;
+                memcpy(gctx_out->iv, gctx->iv, gctx->ivlen);
+            }
+            return 1;
+        }
+
+    default:
+        return -1;
+
+    }
+}
+
+static int aes_gcm_init_key(EVP_CIPHER_CTX *ctx, const unsigned char *key,
+                            const unsigned char *iv, int enc)
+{
+    EVP_AES_GCM_CTX *gctx = EVP_C_DATA(EVP_AES_GCM_CTX,ctx);
+    if (!iv && !key)
+        return 1;
+    if (key) {
+        do {
+#ifdef HWAES_CAPABLE
+            if (HWAES_CAPABLE) {
+                HWAES_set_encrypt_key(key, EVP_CIPHER_CTX_key_length(ctx) * 8,
+                                      &gctx->ks.ks);
+                CRYPTO_gcm128_init(&gctx->gcm, &gctx->ks,
+                                   (block128_f) HWAES_encrypt);
+# ifdef HWAES_ctr32_encrypt_blocks
+                gctx->ctr = (ctr128_f) HWAES_ctr32_encrypt_blocks;
+# else
+                gctx->ctr = NULL;
+# endif
+                break;
+            } else
+#endif
+#ifdef BSAES_CAPABLE
+            if (BSAES_CAPABLE) {
+                AES_set_encrypt_key(key, EVP_CIPHER_CTX_key_length(ctx) * 8,
+                                    &gctx->ks.ks);
+                CRYPTO_gcm128_init(&gctx->gcm, &gctx->ks,
+                                   (block128_f) AES_encrypt);
+                gctx->ctr = (ctr128_f) bsaes_ctr32_encrypt_blocks;
+                break;
+            } else
+#endif
+#ifdef VPAES_CAPABLE
+            if (VPAES_CAPABLE) {
+                vpaes_set_encrypt_key(key, EVP_CIPHER_CTX_key_length(ctx) * 8,
+                                      &gctx->ks.ks);
+                CRYPTO_gcm128_init(&gctx->gcm, &gctx->ks,
+                                   (block128_f) vpaes_encrypt);
+                gctx->ctr = NULL;
+                break;
+            } else
+#endif
+                (void)0;        /* terminate potentially open 'else' */
+
+            AES_set_encrypt_key(key, EVP_CIPHER_CTX_key_length(ctx) * 8,
+                                &gctx->ks.ks);
+            CRYPTO_gcm128_init(&gctx->gcm, &gctx->ks,
+                               (block128_f) AES_encrypt);
+#ifdef AES_CTR_ASM
+            gctx->ctr = (ctr128_f) AES_ctr32_encrypt;
+#else
+            gctx->ctr = NULL;
+#endif
+        } while (0);
+
+        /*
+         * If we have an iv can set it directly, otherwise use saved IV.
+         */
+        if (iv == NULL && gctx->iv_set)
+            iv = gctx->iv;
+        if (iv) {
+            CRYPTO_gcm128_setiv(&gctx->gcm, iv, gctx->ivlen);
+            gctx->iv_set = 1;
+        }
+        gctx->key_set = 1;
+    } else {
+        /* If key set use IV, otherwise copy */
+        if (gctx->key_set)
+            CRYPTO_gcm128_setiv(&gctx->gcm, iv, gctx->ivlen);
+        else
+            memcpy(gctx->iv, iv, gctx->ivlen);
+        gctx->iv_set = 1;
+        gctx->iv_gen = 0;
+    }
+    return 1;
+}
+
+/*
+ * Handle TLS GCM packet format. This consists of the last portion of the IV
+ * followed by the payload and finally the tag. On encrypt generate IV,
+ * encrypt payload and write the tag. On verify retrieve IV, decrypt payload
+ * and verify tag.
+ */
+
+static int aes_gcm_tls_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
+                              const unsigned char *in, size_t len)
+{
+    EVP_AES_GCM_CTX *gctx = EVP_C_DATA(EVP_AES_GCM_CTX,ctx);
+    int rv = -1;
+    /* Encrypt/decrypt must be performed in place */
+    if (out != in
+        || len < (EVP_GCM_TLS_EXPLICIT_IV_LEN + EVP_GCM_TLS_TAG_LEN))
+        return -1;
+    /*
+     * Set IV from start of buffer or generate IV and write to start of
+     * buffer.
+     */
+    if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CIPHER_CTX_encrypting(ctx) ?
+                            EVP_CTRL_GCM_IV_GEN : EVP_CTRL_GCM_SET_IV_INV,
+                            EVP_GCM_TLS_EXPLICIT_IV_LEN, out) <= 0)
+        goto err;
+    /* Use saved AAD */
+    if (CRYPTO_gcm128_aad(&gctx->gcm, EVP_CIPHER_CTX_buf_noconst(ctx),
+                          gctx->tls_aad_len))
+        goto err;
+    /* Fix buffer and length to point to payload */
+    in += EVP_GCM_TLS_EXPLICIT_IV_LEN;
+    out += EVP_GCM_TLS_EXPLICIT_IV_LEN;
+    len -= EVP_GCM_TLS_EXPLICIT_IV_LEN + EVP_GCM_TLS_TAG_LEN;
+    if (EVP_CIPHER_CTX_encrypting(ctx)) {
+        /* Encrypt payload */
+        if (gctx->ctr) {
+            size_t bulk = 0;
+#if defined(AES_GCM_ASM)
+            if (len >= 32 && AES_GCM_ASM(gctx)) {
+                if (CRYPTO_gcm128_encrypt(&gctx->gcm, NULL, NULL, 0))
+                    return -1;
+
+                bulk = AES_gcm_encrypt(in, out, len,
+                                       gctx->gcm.key,
+                                       gctx->gcm.Yi.c, gctx->gcm.Xi.u);
+                gctx->gcm.len.u[1] += bulk;
+            }
+#endif
+            if (CRYPTO_gcm128_encrypt_ctr32(&gctx->gcm,
+                                            in + bulk,
+                                            out + bulk,
+                                            len - bulk, gctx->ctr))
+                goto err;
+        } else {
+            size_t bulk = 0;
+#if defined(AES_GCM_ASM2)
+            if (len >= 32 && AES_GCM_ASM2(gctx)) {
+                if (CRYPTO_gcm128_encrypt(&gctx->gcm, NULL, NULL, 0))
+                    return -1;
+
+                bulk = AES_gcm_encrypt(in, out, len,
+                                       gctx->gcm.key,
+                                       gctx->gcm.Yi.c, gctx->gcm.Xi.u);
+                gctx->gcm.len.u[1] += bulk;
+            }
+#endif
+            if (CRYPTO_gcm128_encrypt(&gctx->gcm,
+                                      in + bulk, out + bulk, len - bulk))
+                goto err;
+        }
+        out += len;
+        /* Finally write tag */
+        CRYPTO_gcm128_tag(&gctx->gcm, out, EVP_GCM_TLS_TAG_LEN);
+        rv = len + EVP_GCM_TLS_EXPLICIT_IV_LEN + EVP_GCM_TLS_TAG_LEN;
+    } else {
+        /* Decrypt */
+        if (gctx->ctr) {
+            size_t bulk = 0;
+#if defined(AES_GCM_ASM)
+            if (len >= 16 && AES_GCM_ASM(gctx)) {
+                if (CRYPTO_gcm128_decrypt(&gctx->gcm, NULL, NULL, 0))
+                    return -1;
+
+                bulk = AES_gcm_decrypt(in, out, len,
+                                       gctx->gcm.key,
+                                       gctx->gcm.Yi.c, gctx->gcm.Xi.u);
+                gctx->gcm.len.u[1] += bulk;
+            }
+#endif
+            if (CRYPTO_gcm128_decrypt_ctr32(&gctx->gcm,
+                                            in + bulk,
+                                            out + bulk,
+                                            len - bulk, gctx->ctr))
+                goto err;
+        } else {
+            size_t bulk = 0;
+#if defined(AES_GCM_ASM2)
+            if (len >= 16 && AES_GCM_ASM2(gctx)) {
+                if (CRYPTO_gcm128_decrypt(&gctx->gcm, NULL, NULL, 0))
+                    return -1;
+
+                bulk = AES_gcm_decrypt(in, out, len,
+                                       gctx->gcm.key,
+                                       gctx->gcm.Yi.c, gctx->gcm.Xi.u);
+                gctx->gcm.len.u[1] += bulk;
+            }
+#endif
+            if (CRYPTO_gcm128_decrypt(&gctx->gcm,
+                                      in + bulk, out + bulk, len - bulk))
+                goto err;
+        }
+        /* Retrieve tag */
+        CRYPTO_gcm128_tag(&gctx->gcm, EVP_CIPHER_CTX_buf_noconst(ctx),
+                          EVP_GCM_TLS_TAG_LEN);
+        /* If tag mismatch wipe buffer */
+        if (CRYPTO_memcmp(EVP_CIPHER_CTX_buf_noconst(ctx), in + len,
+                          EVP_GCM_TLS_TAG_LEN)) {
+            OPENSSL_cleanse(out, len);
+            goto err;
+        }
+        rv = len;
+    }
+
+ err:
+    gctx->iv_set = 0;
+    gctx->tls_aad_len = -1;
+    return rv;
+}
+
+static int aes_gcm_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
+                          const unsigned char *in, size_t len)
+{
+    EVP_AES_GCM_CTX *gctx = EVP_C_DATA(EVP_AES_GCM_CTX,ctx);
+    /* If not set up, return error */
+    if (!gctx->key_set)
+        return -1;
+
+    if (gctx->tls_aad_len >= 0)
+        return aes_gcm_tls_cipher(ctx, out, in, len);
+
+    if (!gctx->iv_set)
+        return -1;
+    if (in) {
+        if (out == NULL) {
+            if (CRYPTO_gcm128_aad(&gctx->gcm, in, len))
+                return -1;
+        } else if (EVP_CIPHER_CTX_encrypting(ctx)) {
+            if (gctx->ctr) {
+                size_t bulk = 0;
+#if defined(AES_GCM_ASM)
+                if (len >= 32 && AES_GCM_ASM(gctx)) {
+                    size_t res = (16 - gctx->gcm.mres) % 16;
+
+                    if (CRYPTO_gcm128_encrypt(&gctx->gcm, in, out, res))
+                        return -1;
+
+                    bulk = AES_gcm_encrypt(in + res,
+                                           out + res, len - res,
+                                           gctx->gcm.key, gctx->gcm.Yi.c,
+                                           gctx->gcm.Xi.u);
+                    gctx->gcm.len.u[1] += bulk;
+                    bulk += res;
+                }
+#endif
+                if (CRYPTO_gcm128_encrypt_ctr32(&gctx->gcm,
+                                                in + bulk,
+                                                out + bulk,
+                                                len - bulk, gctx->ctr))
+                    return -1;
+            } else {
+                size_t bulk = 0;
+#if defined(AES_GCM_ASM2)
+                if (len >= 32 && AES_GCM_ASM2(gctx)) {
+                    size_t res = (16 - gctx->gcm.mres) % 16;
+
+                    if (CRYPTO_gcm128_encrypt(&gctx->gcm, in, out, res))
+                        return -1;
+
+                    bulk = AES_gcm_encrypt(in + res,
+                                           out + res, len - res,
+                                           gctx->gcm.key, gctx->gcm.Yi.c,
+                                           gctx->gcm.Xi.u);
+                    gctx->gcm.len.u[1] += bulk;
+                    bulk += res;
+                }
+#endif
+                if (CRYPTO_gcm128_encrypt(&gctx->gcm,
+                                          in + bulk, out + bulk, len - bulk))
+                    return -1;
+            }
+        } else {
+            if (gctx->ctr) {
+                size_t bulk = 0;
+#if defined(AES_GCM_ASM)
+                if (len >= 16 && AES_GCM_ASM(gctx)) {
+                    size_t res = (16 - gctx->gcm.mres) % 16;
+
+                    if (CRYPTO_gcm128_decrypt(&gctx->gcm, in, out, res))
+                        return -1;
+
+                    bulk = AES_gcm_decrypt(in + res,
+                                           out + res, len - res,
+                                           gctx->gcm.key,
+                                           gctx->gcm.Yi.c, gctx->gcm.Xi.u);
+                    gctx->gcm.len.u[1] += bulk;
+                    bulk += res;
+                }
+#endif
+                if (CRYPTO_gcm128_decrypt_ctr32(&gctx->gcm,
+                                                in + bulk,
+                                                out + bulk,
+                                                len - bulk, gctx->ctr))
+                    return -1;
+            } else {
+                size_t bulk = 0;
+#if defined(AES_GCM_ASM2)
+                if (len >= 16 && AES_GCM_ASM2(gctx)) {
+                    size_t res = (16 - gctx->gcm.mres) % 16;
+
+                    if (CRYPTO_gcm128_decrypt(&gctx->gcm, in, out, res))
+                        return -1;
+
+                    bulk = AES_gcm_decrypt(in + res,
+                                           out + res, len - res,
+                                           gctx->gcm.key,
+                                           gctx->gcm.Yi.c, gctx->gcm.Xi.u);
+                    gctx->gcm.len.u[1] += bulk;
+                    bulk += res;
+                }
+#endif
+                if (CRYPTO_gcm128_decrypt(&gctx->gcm,
+                                          in + bulk, out + bulk, len - bulk))
+                    return -1;
+            }
+        }
+        return len;
+    } else {
+        if (!EVP_CIPHER_CTX_encrypting(ctx)) {
+            if (gctx->taglen < 0)
+                return -1;
+            if (CRYPTO_gcm128_finish(&gctx->gcm,
+                                     EVP_CIPHER_CTX_buf_noconst(ctx),
+                                     gctx->taglen) != 0)
+                return -1;
+            gctx->iv_set = 0;
+            return 0;
+        }
+        CRYPTO_gcm128_tag(&gctx->gcm, EVP_CIPHER_CTX_buf_noconst(ctx), 16);
+        gctx->taglen = 16;
+        /* Don't reuse the IV */
+        gctx->iv_set = 0;
+        return 0;
+    }
+
+}
+
+#define CUSTOM_FLAGS    (EVP_CIPH_FLAG_DEFAULT_ASN1 \
+                | EVP_CIPH_CUSTOM_IV | EVP_CIPH_FLAG_CUSTOM_CIPHER \
+                | EVP_CIPH_ALWAYS_CALL_INIT | EVP_CIPH_CTRL_INIT \
+                | EVP_CIPH_CUSTOM_COPY)
+
+BLOCK_CIPHER_custom(NID_aes, 128, 1, 12, gcm, GCM,
+                    EVP_CIPH_FLAG_AEAD_CIPHER | CUSTOM_FLAGS)
+    BLOCK_CIPHER_custom(NID_aes, 192, 1, 12, gcm, GCM,
+                    EVP_CIPH_FLAG_AEAD_CIPHER | CUSTOM_FLAGS)
+    BLOCK_CIPHER_custom(NID_aes, 256, 1, 12, gcm, GCM,
+                    EVP_CIPH_FLAG_AEAD_CIPHER | CUSTOM_FLAGS)
+
+static int aes_xts_ctrl(EVP_CIPHER_CTX *c, int type, int arg, void *ptr)
+{
+    EVP_AES_XTS_CTX *xctx = EVP_C_DATA(EVP_AES_XTS_CTX,c);
+    if (type == EVP_CTRL_COPY) {
+        EVP_CIPHER_CTX *out = ptr;
+        EVP_AES_XTS_CTX *xctx_out = EVP_C_DATA(EVP_AES_XTS_CTX,out);
+        if (xctx->xts.key1) {
+            if (xctx->xts.key1 != &xctx->ks1)
+                return 0;
+            xctx_out->xts.key1 = &xctx_out->ks1;
+        }
+        if (xctx->xts.key2) {
+            if (xctx->xts.key2 != &xctx->ks2)
+                return 0;
+            xctx_out->xts.key2 = &xctx_out->ks2;
+        }
+        return 1;
+    } else if (type != EVP_CTRL_INIT)
+        return -1;
+    /* key1 and key2 are used as an indicator both key and IV are set */
+    xctx->xts.key1 = NULL;
+    xctx->xts.key2 = NULL;
+    return 1;
+}
+
+static int aes_xts_init_key(EVP_CIPHER_CTX *ctx, const unsigned char *key,
+                            const unsigned char *iv, int enc)
+{
+    EVP_AES_XTS_CTX *xctx = EVP_C_DATA(EVP_AES_XTS_CTX,ctx);
+    if (!iv && !key)
+        return 1;
+
+    if (key)
+        do {
+#ifdef AES_XTS_ASM
+            xctx->stream = enc ? AES_xts_encrypt : AES_xts_decrypt;
+#else
+            xctx->stream = NULL;
+#endif
+            /* key_len is two AES keys */
+#ifdef HWAES_CAPABLE
+            if (HWAES_CAPABLE) {
+                if (enc) {
+                    HWAES_set_encrypt_key(key,
+                                          EVP_CIPHER_CTX_key_length(ctx) * 4,
+                                          &xctx->ks1.ks);
+                    xctx->xts.block1 = (block128_f) HWAES_encrypt;
+# ifdef HWAES_xts_encrypt
+                    xctx->stream = HWAES_xts_encrypt;
+# endif
+                } else {
+                    HWAES_set_decrypt_key(key,
+                                          EVP_CIPHER_CTX_key_length(ctx) * 4,
+                                          &xctx->ks1.ks);
+                    xctx->xts.block1 = (block128_f) HWAES_decrypt;
+# ifdef HWAES_xts_decrypt
+                    xctx->stream = HWAES_xts_decrypt;
+#endif
+                }
+
+                HWAES_set_encrypt_key(key + EVP_CIPHER_CTX_key_length(ctx) / 2,
+                                      EVP_CIPHER_CTX_key_length(ctx) * 4,
+                                      &xctx->ks2.ks);
+                xctx->xts.block2 = (block128_f) HWAES_encrypt;
+
+                xctx->xts.key1 = &xctx->ks1;
+                break;
+            } else
+#endif
+#ifdef BSAES_CAPABLE
+            if (BSAES_CAPABLE)
+                xctx->stream = enc ? bsaes_xts_encrypt : bsaes_xts_decrypt;
+            else
+#endif
+#ifdef VPAES_CAPABLE
+            if (VPAES_CAPABLE) {
+                if (enc) {
+                    vpaes_set_encrypt_key(key,
+                                          EVP_CIPHER_CTX_key_length(ctx) * 4,
+                                          &xctx->ks1.ks);
+                    xctx->xts.block1 = (block128_f) vpaes_encrypt;
+                } else {
+                    vpaes_set_decrypt_key(key,
+                                          EVP_CIPHER_CTX_key_length(ctx) * 4,
+                                          &xctx->ks1.ks);
+                    xctx->xts.block1 = (block128_f) vpaes_decrypt;
+                }
+
+                vpaes_set_encrypt_key(key + EVP_CIPHER_CTX_key_length(ctx) / 2,
+                                      EVP_CIPHER_CTX_key_length(ctx) * 4,
+                                      &xctx->ks2.ks);
+                xctx->xts.block2 = (block128_f) vpaes_encrypt;
+
+                xctx->xts.key1 = &xctx->ks1;
+                break;
+            } else
+#endif
+                (void)0;        /* terminate potentially open 'else' */
+
+            if (enc) {
+                AES_set_encrypt_key(key, EVP_CIPHER_CTX_key_length(ctx) * 4,
+                                    &xctx->ks1.ks);
+                xctx->xts.block1 = (block128_f) AES_encrypt;
+            } else {
+                AES_set_decrypt_key(key, EVP_CIPHER_CTX_key_length(ctx) * 4,
+                                    &xctx->ks1.ks);
+                xctx->xts.block1 = (block128_f) AES_decrypt;
+            }
+
+            AES_set_encrypt_key(key + EVP_CIPHER_CTX_key_length(ctx) / 2,
+                                EVP_CIPHER_CTX_key_length(ctx) * 4,
+                                &xctx->ks2.ks);
+            xctx->xts.block2 = (block128_f) AES_encrypt;
+
+            xctx->xts.key1 = &xctx->ks1;
+        } while (0);
+
+    if (iv) {
+        xctx->xts.key2 = &xctx->ks2;
+        memcpy(EVP_CIPHER_CTX_iv_noconst(ctx), iv, 16);
+    }
+
+    return 1;
+}
+
+static int aes_xts_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
+                          const unsigned char *in, size_t len)
+{
+    EVP_AES_XTS_CTX *xctx = EVP_C_DATA(EVP_AES_XTS_CTX,ctx);
+    if (!xctx->xts.key1 || !xctx->xts.key2)
+        return 0;
+    if (!out || !in || len < AES_BLOCK_SIZE)
+        return 0;
+    if (xctx->stream)
+        (*xctx->stream) (in, out, len,
+                         xctx->xts.key1, xctx->xts.key2,
+                         EVP_CIPHER_CTX_iv_noconst(ctx));
+    else if (CRYPTO_xts128_encrypt(&xctx->xts, EVP_CIPHER_CTX_iv_noconst(ctx),
+                                   in, out, len,
+                                   EVP_CIPHER_CTX_encrypting(ctx)))
+        return 0;
+    return 1;
+}
+
+#define aes_xts_cleanup NULL
+
+#define XTS_FLAGS       (EVP_CIPH_FLAG_DEFAULT_ASN1 | EVP_CIPH_CUSTOM_IV \
+                         | EVP_CIPH_ALWAYS_CALL_INIT | EVP_CIPH_CTRL_INIT \
+                         | EVP_CIPH_CUSTOM_COPY)
+
+BLOCK_CIPHER_custom(NID_aes, 128, 1, 16, xts, XTS, XTS_FLAGS)
+    BLOCK_CIPHER_custom(NID_aes, 256, 1, 16, xts, XTS, XTS_FLAGS)
+
+static int aes_ccm_ctrl(EVP_CIPHER_CTX *c, int type, int arg, void *ptr)
+{
+    EVP_AES_CCM_CTX *cctx = EVP_C_DATA(EVP_AES_CCM_CTX,c);
+    switch (type) {
+    case EVP_CTRL_INIT:
+        cctx->key_set = 0;
+        cctx->iv_set = 0;
+        cctx->L = 8;
+        cctx->M = 12;
+        cctx->tag_set = 0;
+        cctx->len_set = 0;
+        cctx->tls_aad_len = -1;
+        return 1;
+
+    case EVP_CTRL_AEAD_TLS1_AAD:
+        /* Save the AAD for later use */
+        if (arg != EVP_AEAD_TLS1_AAD_LEN)
+            return 0;
+        memcpy(EVP_CIPHER_CTX_buf_noconst(c), ptr, arg);
+        cctx->tls_aad_len = arg;
+        {
+            uint16_t len =
+                EVP_CIPHER_CTX_buf_noconst(c)[arg - 2] << 8
+                | EVP_CIPHER_CTX_buf_noconst(c)[arg - 1];
+            /* Correct length for explicit IV */
+            if (len < EVP_CCM_TLS_EXPLICIT_IV_LEN)
+                return 0;
+            len -= EVP_CCM_TLS_EXPLICIT_IV_LEN;
+            /* If decrypting correct for tag too */
+            if (!EVP_CIPHER_CTX_encrypting(c)) {
+                if (len < cctx->M)
+                    return 0;
+                len -= cctx->M;
+            }
+            EVP_CIPHER_CTX_buf_noconst(c)[arg - 2] = len >> 8;
+            EVP_CIPHER_CTX_buf_noconst(c)[arg - 1] = len & 0xff;
+        }
+        /* Extra padding: tag appended to record */
+        return cctx->M;
+
+    case EVP_CTRL_CCM_SET_IV_FIXED:
+        /* Sanity check length */
+        if (arg != EVP_CCM_TLS_FIXED_IV_LEN)
+            return 0;
+        /* Just copy to first part of IV */
+        memcpy(EVP_CIPHER_CTX_iv_noconst(c), ptr, arg);
+        return 1;
+
+    case EVP_CTRL_AEAD_SET_IVLEN:
+        arg = 15 - arg;
+    case EVP_CTRL_CCM_SET_L:
+        if (arg < 2 || arg > 8)
+            return 0;
+        cctx->L = arg;
+        return 1;
+
+    case EVP_CTRL_AEAD_SET_TAG:
+        if ((arg & 1) || arg < 4 || arg > 16)
+            return 0;
+        if (EVP_CIPHER_CTX_encrypting(c) && ptr)
+            return 0;
+        if (ptr) {
+            cctx->tag_set = 1;
+            memcpy(EVP_CIPHER_CTX_buf_noconst(c), ptr, arg);
+        }
+        cctx->M = arg;
+        return 1;
+
+    case EVP_CTRL_AEAD_GET_TAG:
+        if (!EVP_CIPHER_CTX_encrypting(c) || !cctx->tag_set)
+            return 0;
+        if (!CRYPTO_ccm128_tag(&cctx->ccm, ptr, (size_t)arg))
+            return 0;
+        cctx->tag_set = 0;
+        cctx->iv_set = 0;
+        cctx->len_set = 0;
+        return 1;
+
+    case EVP_CTRL_COPY:
+        {
+            EVP_CIPHER_CTX *out = ptr;
+            EVP_AES_CCM_CTX *cctx_out = EVP_C_DATA(EVP_AES_CCM_CTX,out);
+            if (cctx->ccm.key) {
+                if (cctx->ccm.key != &cctx->ks)
+                    return 0;
+                cctx_out->ccm.key = &cctx_out->ks;
+            }
+            return 1;
+        }
+
+    default:
+        return -1;
+
+    }
+}
+
+static int aes_ccm_init_key(EVP_CIPHER_CTX *ctx, const unsigned char *key,
+                            const unsigned char *iv, int enc)
+{
+    EVP_AES_CCM_CTX *cctx = EVP_C_DATA(EVP_AES_CCM_CTX,ctx);
+    if (!iv && !key)
+        return 1;
+    if (key)
+        do {
+#ifdef HWAES_CAPABLE
+            if (HWAES_CAPABLE) {
+                HWAES_set_encrypt_key(key, EVP_CIPHER_CTX_key_length(ctx) * 8,
+                                      &cctx->ks.ks);
+
+                CRYPTO_ccm128_init(&cctx->ccm, cctx->M, cctx->L,
+                                   &cctx->ks, (block128_f) HWAES_encrypt);
+                cctx->str = NULL;
+                cctx->key_set = 1;
+                break;
+            } else
+#endif
+#ifdef VPAES_CAPABLE
+            if (VPAES_CAPABLE) {
+                vpaes_set_encrypt_key(key, EVP_CIPHER_CTX_key_length(ctx) * 8,
+                                      &cctx->ks.ks);
+                CRYPTO_ccm128_init(&cctx->ccm, cctx->M, cctx->L,
+                                   &cctx->ks, (block128_f) vpaes_encrypt);
+                cctx->str = NULL;
+                cctx->key_set = 1;
+                break;
+            }
+#endif
+            AES_set_encrypt_key(key, EVP_CIPHER_CTX_key_length(ctx) * 8,
+                                &cctx->ks.ks);
+            CRYPTO_ccm128_init(&cctx->ccm, cctx->M, cctx->L,
+                               &cctx->ks, (block128_f) AES_encrypt);
+            cctx->str = NULL;
+            cctx->key_set = 1;
+        } while (0);
+    if (iv) {
+        memcpy(EVP_CIPHER_CTX_iv_noconst(ctx), iv, 15 - cctx->L);
+        cctx->iv_set = 1;
+    }
+    return 1;
+}
+
+static int aes_ccm_tls_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
+                              const unsigned char *in, size_t len)
+{
+    EVP_AES_CCM_CTX *cctx = EVP_C_DATA(EVP_AES_CCM_CTX,ctx);
+    CCM128_CONTEXT *ccm = &cctx->ccm;
+    /* Encrypt/decrypt must be performed in place */
+    if (out != in || len < (EVP_CCM_TLS_EXPLICIT_IV_LEN + (size_t)cctx->M))
+        return -1;
+    /* If encrypting set explicit IV from sequence number (start of AAD) */
+    if (EVP_CIPHER_CTX_encrypting(ctx))
+        memcpy(out, EVP_CIPHER_CTX_buf_noconst(ctx),
+               EVP_CCM_TLS_EXPLICIT_IV_LEN);
+    /* Get rest of IV from explicit IV */
+    memcpy(EVP_CIPHER_CTX_iv_noconst(ctx) + EVP_CCM_TLS_FIXED_IV_LEN, in,
+           EVP_CCM_TLS_EXPLICIT_IV_LEN);
+    /* Correct length value */
+    len -= EVP_CCM_TLS_EXPLICIT_IV_LEN + cctx->M;
+    if (CRYPTO_ccm128_setiv(ccm, EVP_CIPHER_CTX_iv_noconst(ctx), 15 - cctx->L,
+                            len))
+            return -1;
+    /* Use saved AAD */
+    CRYPTO_ccm128_aad(ccm, EVP_CIPHER_CTX_buf_noconst(ctx), cctx->tls_aad_len);
+    /* Fix buffer to point to payload */
+    in += EVP_CCM_TLS_EXPLICIT_IV_LEN;
+    out += EVP_CCM_TLS_EXPLICIT_IV_LEN;
+    if (EVP_CIPHER_CTX_encrypting(ctx)) {
+        if (cctx->str ? CRYPTO_ccm128_encrypt_ccm64(ccm, in, out, len,
+                                                    cctx->str) :
+            CRYPTO_ccm128_encrypt(ccm, in, out, len))
+            return -1;
+        if (!CRYPTO_ccm128_tag(ccm, out + len, cctx->M))
+            return -1;
+        return len + EVP_CCM_TLS_EXPLICIT_IV_LEN + cctx->M;
+    } else {
+        if (cctx->str ? !CRYPTO_ccm128_decrypt_ccm64(ccm, in, out, len,
+                                                     cctx->str) :
+            !CRYPTO_ccm128_decrypt(ccm, in, out, len)) {
+            unsigned char tag[16];
+            if (CRYPTO_ccm128_tag(ccm, tag, cctx->M)) {
+                if (!CRYPTO_memcmp(tag, in + len, cctx->M))
+                    return len;
+            }
+        }
+        OPENSSL_cleanse(out, len);
+        return -1;
+    }
+}
+
+static int aes_ccm_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
+                          const unsigned char *in, size_t len)
+{
+    EVP_AES_CCM_CTX *cctx = EVP_C_DATA(EVP_AES_CCM_CTX,ctx);
+    CCM128_CONTEXT *ccm = &cctx->ccm;
+    /* If not set up, return error */
+    if (!cctx->key_set)
+        return -1;
+
+    if (cctx->tls_aad_len >= 0)
+        return aes_ccm_tls_cipher(ctx, out, in, len);
+
+    if (!cctx->iv_set)
+        return -1;
+
+    if (!EVP_CIPHER_CTX_encrypting(ctx) && !cctx->tag_set)
+        return -1;
+    if (!out) {
+        if (!in) {
+            if (CRYPTO_ccm128_setiv(ccm, EVP_CIPHER_CTX_iv_noconst(ctx),
+                                    15 - cctx->L, len))
+                return -1;
+            cctx->len_set = 1;
+            return len;
+        }
+        /* If have AAD need message length */
+        if (!cctx->len_set && len)
+            return -1;
+        CRYPTO_ccm128_aad(ccm, in, len);
+        return len;
+    }
+    /* EVP_*Final() doesn't return any data */
+    if (!in)
+        return 0;
+    /* If not set length yet do it */
+    if (!cctx->len_set) {
+        if (CRYPTO_ccm128_setiv(ccm, EVP_CIPHER_CTX_iv_noconst(ctx),
+                                15 - cctx->L, len))
+            return -1;
+        cctx->len_set = 1;
+    }
+    if (EVP_CIPHER_CTX_encrypting(ctx)) {
+        if (cctx->str ? CRYPTO_ccm128_encrypt_ccm64(ccm, in, out, len,
+                                                    cctx->str) :
+            CRYPTO_ccm128_encrypt(ccm, in, out, len))
+            return -1;
+        cctx->tag_set = 1;
+        return len;
+    } else {
+        int rv = -1;
+        if (cctx->str ? !CRYPTO_ccm128_decrypt_ccm64(ccm, in, out, len,
+                                                     cctx->str) :
+            !CRYPTO_ccm128_decrypt(ccm, in, out, len)) {
+            unsigned char tag[16];
+            if (CRYPTO_ccm128_tag(ccm, tag, cctx->M)) {
+                if (!CRYPTO_memcmp(tag, EVP_CIPHER_CTX_buf_noconst(ctx),
+                                   cctx->M))
+                    rv = len;
+            }
+        }
+        if (rv == -1)
+            OPENSSL_cleanse(out, len);
+        cctx->iv_set = 0;
+        cctx->tag_set = 0;
+        cctx->len_set = 0;
+        return rv;
+    }
+}
+
+#define aes_ccm_cleanup NULL
+
+BLOCK_CIPHER_custom(NID_aes, 128, 1, 12, ccm, CCM,
+                    EVP_CIPH_FLAG_AEAD_CIPHER | CUSTOM_FLAGS)
+    BLOCK_CIPHER_custom(NID_aes, 192, 1, 12, ccm, CCM,
+                        EVP_CIPH_FLAG_AEAD_CIPHER | CUSTOM_FLAGS)
+    BLOCK_CIPHER_custom(NID_aes, 256, 1, 12, ccm, CCM,
+                        EVP_CIPH_FLAG_AEAD_CIPHER | CUSTOM_FLAGS)
+
+typedef struct {
+    union {
+        double align;
+        AES_KEY ks;
+    } ks;
+    /* Indicates if IV has been set */
+    unsigned char *iv;
+} EVP_AES_WRAP_CTX;
+
+static int aes_wrap_init_key(EVP_CIPHER_CTX *ctx, const unsigned char *key,
+                             const unsigned char *iv, int enc)
+{
+    EVP_AES_WRAP_CTX *wctx = EVP_C_DATA(EVP_AES_WRAP_CTX,ctx);
+    if (!iv && !key)
+        return 1;
+    if (key) {
+        if (EVP_CIPHER_CTX_encrypting(ctx))
+            AES_set_encrypt_key(key, EVP_CIPHER_CTX_key_length(ctx) * 8,
+                                &wctx->ks.ks);
+        else
+            AES_set_decrypt_key(key, EVP_CIPHER_CTX_key_length(ctx) * 8,
+                                &wctx->ks.ks);
+        if (!iv)
+            wctx->iv = NULL;
+    }
+    if (iv) {
+        memcpy(EVP_CIPHER_CTX_iv_noconst(ctx), iv, EVP_CIPHER_CTX_iv_length(ctx));
+        wctx->iv = EVP_CIPHER_CTX_iv_noconst(ctx);
+    }
+    return 1;
+}
+
+static int aes_wrap_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
+                           const unsigned char *in, size_t inlen)
+{
+    EVP_AES_WRAP_CTX *wctx = EVP_C_DATA(EVP_AES_WRAP_CTX,ctx);
+    size_t rv;
+    /* AES wrap with padding has IV length of 4, without padding 8 */
+    int pad = EVP_CIPHER_CTX_iv_length(ctx) == 4;
+    /* No final operation so always return zero length */
+    if (!in)
+        return 0;
+    /* Input length must always be non-zero */
+    if (!inlen)
+        return -1;
+    /* If decrypting need at least 16 bytes and multiple of 8 */
+    if (!EVP_CIPHER_CTX_encrypting(ctx) && (inlen < 16 || inlen & 0x7))
+        return -1;
+    /* If not padding input must be multiple of 8 */
+    if (!pad && inlen & 0x7)
+        return -1;
+    if (is_partially_overlapping(out, in, inlen)) {
+        EVPerr(EVP_F_AES_WRAP_CIPHER, EVP_R_PARTIALLY_OVERLAPPING);
+        return 0;
+    }
+    if (!out) {
+        if (EVP_CIPHER_CTX_encrypting(ctx)) {
+            /* If padding round up to multiple of 8 */
+            if (pad)
+                inlen = (inlen + 7) / 8 * 8;
+            /* 8 byte prefix */
+            return inlen + 8;
+        } else {
+            /*
+             * If not padding output will be exactly 8 bytes smaller than
+             * input. If padding it will be at least 8 bytes smaller but we
+             * don't know how much.
+             */
+            return inlen - 8;
+        }
+    }
+    if (pad) {
+        if (EVP_CIPHER_CTX_encrypting(ctx))
+            rv = CRYPTO_128_wrap_pad(&wctx->ks.ks, wctx->iv,
+                                     out, in, inlen,
+                                     (block128_f) AES_encrypt);
+        else
+            rv = CRYPTO_128_unwrap_pad(&wctx->ks.ks, wctx->iv,
+                                       out, in, inlen,
+                                       (block128_f) AES_decrypt);
+    } else {
+        if (EVP_CIPHER_CTX_encrypting(ctx))
+            rv = CRYPTO_128_wrap(&wctx->ks.ks, wctx->iv,
+                                 out, in, inlen, (block128_f) AES_encrypt);
+        else
+            rv = CRYPTO_128_unwrap(&wctx->ks.ks, wctx->iv,
+                                   out, in, inlen, (block128_f) AES_decrypt);
+    }
+    return rv ? (int)rv : -1;
+}
+
+#define WRAP_FLAGS      (EVP_CIPH_WRAP_MODE \
+                | EVP_CIPH_CUSTOM_IV | EVP_CIPH_FLAG_CUSTOM_CIPHER \
+                | EVP_CIPH_ALWAYS_CALL_INIT | EVP_CIPH_FLAG_DEFAULT_ASN1)
+
+static const EVP_CIPHER aes_128_wrap = {
+    NID_id_aes128_wrap,
+    8, 16, 8, WRAP_FLAGS,
+    aes_wrap_init_key, aes_wrap_cipher,
+    NULL,
+    sizeof(EVP_AES_WRAP_CTX),
+    NULL, NULL, NULL, NULL
+};
+
+const EVP_CIPHER *EVP_aes_128_wrap(void)
+{
+    return &aes_128_wrap;
+}
+
+static const EVP_CIPHER aes_192_wrap = {
+    NID_id_aes192_wrap,
+    8, 24, 8, WRAP_FLAGS,
+    aes_wrap_init_key, aes_wrap_cipher,
+    NULL,
+    sizeof(EVP_AES_WRAP_CTX),
+    NULL, NULL, NULL, NULL
+};
+
+const EVP_CIPHER *EVP_aes_192_wrap(void)
+{
+    return &aes_192_wrap;
+}
+
+static const EVP_CIPHER aes_256_wrap = {
+    NID_id_aes256_wrap,
+    8, 32, 8, WRAP_FLAGS,
+    aes_wrap_init_key, aes_wrap_cipher,
+    NULL,
+    sizeof(EVP_AES_WRAP_CTX),
+    NULL, NULL, NULL, NULL
+};
+
+const EVP_CIPHER *EVP_aes_256_wrap(void)
+{
+    return &aes_256_wrap;
+}
+
+static const EVP_CIPHER aes_128_wrap_pad = {
+    NID_id_aes128_wrap_pad,
+    8, 16, 4, WRAP_FLAGS,
+    aes_wrap_init_key, aes_wrap_cipher,
+    NULL,
+    sizeof(EVP_AES_WRAP_CTX),
+    NULL, NULL, NULL, NULL
+};
+
+const EVP_CIPHER *EVP_aes_128_wrap_pad(void)
+{
+    return &aes_128_wrap_pad;
+}
+
+static const EVP_CIPHER aes_192_wrap_pad = {
+    NID_id_aes192_wrap_pad,
+    8, 24, 4, WRAP_FLAGS,
+    aes_wrap_init_key, aes_wrap_cipher,
+    NULL,
+    sizeof(EVP_AES_WRAP_CTX),
+    NULL, NULL, NULL, NULL
+};
+
+const EVP_CIPHER *EVP_aes_192_wrap_pad(void)
+{
+    return &aes_192_wrap_pad;
+}
+
+static const EVP_CIPHER aes_256_wrap_pad = {
+    NID_id_aes256_wrap_pad,
+    8, 32, 4, WRAP_FLAGS,
+    aes_wrap_init_key, aes_wrap_cipher,
+    NULL,
+    sizeof(EVP_AES_WRAP_CTX),
+    NULL, NULL, NULL, NULL
+};
+
+const EVP_CIPHER *EVP_aes_256_wrap_pad(void)
+{
+    return &aes_256_wrap_pad;
+}
+
+#ifndef OPENSSL_NO_OCB
+static int aes_ocb_ctrl(EVP_CIPHER_CTX *c, int type, int arg, void *ptr)
+{
+    EVP_AES_OCB_CTX *octx = EVP_C_DATA(EVP_AES_OCB_CTX,c);
+    EVP_CIPHER_CTX *newc;
+    EVP_AES_OCB_CTX *new_octx;
+
+    switch (type) {
+    case EVP_CTRL_INIT:
+        octx->key_set = 0;
+        octx->iv_set = 0;
+        octx->ivlen = EVP_CIPHER_CTX_iv_length(c);
+        octx->iv = EVP_CIPHER_CTX_iv_noconst(c);
+        octx->taglen = 16;
+        octx->data_buf_len = 0;
+        octx->aad_buf_len = 0;
+        return 1;
+
+    case EVP_CTRL_AEAD_SET_IVLEN:
+        /* IV len must be 1 to 15 */
+        if (arg <= 0 || arg > 15)
+            return 0;
+
+        octx->ivlen = arg;
+        return 1;
+
+    case EVP_CTRL_AEAD_SET_TAG:
+        if (!ptr) {
+            /* Tag len must be 0 to 16 */
+            if (arg < 0 || arg > 16)
+                return 0;
+
+            octx->taglen = arg;
+            return 1;
+        }
+        if (arg != octx->taglen || EVP_CIPHER_CTX_encrypting(c))
+            return 0;
+        memcpy(octx->tag, ptr, arg);
+        return 1;
+
+    case EVP_CTRL_AEAD_GET_TAG:
+        if (arg != octx->taglen || !EVP_CIPHER_CTX_encrypting(c))
+            return 0;
+
+        memcpy(ptr, octx->tag, arg);
+        return 1;
+
+    case EVP_CTRL_COPY:
+        newc = (EVP_CIPHER_CTX *)ptr;
+        new_octx = EVP_C_DATA(EVP_AES_OCB_CTX,newc);
+        return CRYPTO_ocb128_copy_ctx(&new_octx->ocb, &octx->ocb,
+                                      &new_octx->ksenc.ks,
+                                      &new_octx->ksdec.ks);
+
+    default:
+        return -1;
+
+    }
+}
+
+# ifdef HWAES_CAPABLE
+#  ifdef HWAES_ocb_encrypt
+void HWAES_ocb_encrypt(const unsigned char *in, unsigned char *out,
+                       size_t blocks, const void *key,
+                       size_t start_block_num,
+                       unsigned char offset_i[16],
+                       const unsigned char L_[][16],
+                       unsigned char checksum[16]);
+#  else
+#    define HWAES_ocb_encrypt ((ocb128_f)NULL)
+#  endif
+#  ifdef HWAES_ocb_decrypt
+void HWAES_ocb_decrypt(const unsigned char *in, unsigned char *out,
+                       size_t blocks, const void *key,
+                       size_t start_block_num,
+                       unsigned char offset_i[16],
+                       const unsigned char L_[][16],
+                       unsigned char checksum[16]);
+#  else
+#    define HWAES_ocb_decrypt ((ocb128_f)NULL)
+#  endif
+# endif
+
+static int aes_ocb_init_key(EVP_CIPHER_CTX *ctx, const unsigned char *key,
+                            const unsigned char *iv, int enc)
+{
+    EVP_AES_OCB_CTX *octx = EVP_C_DATA(EVP_AES_OCB_CTX,ctx);
+    if (!iv && !key)
+        return 1;
+    if (key) {
+        do {
+            /*
+             * We set both the encrypt and decrypt key here because decrypt
+             * needs both. We could possibly optimise to remove setting the
+             * decrypt for an encryption operation.
+             */
+# ifdef HWAES_CAPABLE
+            if (HWAES_CAPABLE) {
+                HWAES_set_encrypt_key(key, EVP_CIPHER_CTX_key_length(ctx) * 8,
+                                      &octx->ksenc.ks);
+                HWAES_set_decrypt_key(key, EVP_CIPHER_CTX_key_length(ctx) * 8,
+                                      &octx->ksdec.ks);
+                if (!CRYPTO_ocb128_init(&octx->ocb,
+                                        &octx->ksenc.ks, &octx->ksdec.ks,
+                                        (block128_f) HWAES_encrypt,
+                                        (block128_f) HWAES_decrypt,
+                                        enc ? HWAES_ocb_encrypt
+                                            : HWAES_ocb_decrypt))
+                    return 0;
+                break;
+            }
+# endif
+# ifdef VPAES_CAPABLE
+            if (VPAES_CAPABLE) {
+                vpaes_set_encrypt_key(key, EVP_CIPHER_CTX_key_length(ctx) * 8,
+                                      &octx->ksenc.ks);
+                vpaes_set_decrypt_key(key, EVP_CIPHER_CTX_key_length(ctx) * 8,
+                                      &octx->ksdec.ks);
+                if (!CRYPTO_ocb128_init(&octx->ocb,
+                                        &octx->ksenc.ks, &octx->ksdec.ks,
+                                        (block128_f) vpaes_encrypt,
+                                        (block128_f) vpaes_decrypt,
+                                        NULL))
+                    return 0;
+                break;
+            }
+# endif
+            AES_set_encrypt_key(key, EVP_CIPHER_CTX_key_length(ctx) * 8,
+                                &octx->ksenc.ks);
+            AES_set_decrypt_key(key, EVP_CIPHER_CTX_key_length(ctx) * 8,
+                                &octx->ksdec.ks);
+            if (!CRYPTO_ocb128_init(&octx->ocb,
+                                    &octx->ksenc.ks, &octx->ksdec.ks,
+                                    (block128_f) AES_encrypt,
+                                    (block128_f) AES_decrypt,
+                                    NULL))
+                return 0;
+        }
+        while (0);
+
+        /*
+         * If we have an iv we can set it directly, otherwise use saved IV.
+         */
+        if (iv == NULL && octx->iv_set)
+            iv = octx->iv;
+        if (iv) {
+            if (CRYPTO_ocb128_setiv(&octx->ocb, iv, octx->ivlen, octx->taglen)
+                != 1)
+                return 0;
+            octx->iv_set = 1;
+        }
+        octx->key_set = 1;
+    } else {
+        /* If key set use IV, otherwise copy */
+        if (octx->key_set)
+            CRYPTO_ocb128_setiv(&octx->ocb, iv, octx->ivlen, octx->taglen);
+        else
+            memcpy(octx->iv, iv, octx->ivlen);
+        octx->iv_set = 1;
+    }
+    return 1;
+}
+
+static int aes_ocb_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
+                          const unsigned char *in, size_t len)
+{
+    unsigned char *buf;
+    int *buf_len;
+    int written_len = 0;
+    size_t trailing_len;
+    EVP_AES_OCB_CTX *octx = EVP_C_DATA(EVP_AES_OCB_CTX,ctx);
+
+    /* If IV or Key not set then return error */
+    if (!octx->iv_set)
+        return -1;
+
+    if (!octx->key_set)
+        return -1;
+
+    if (in != NULL) {
+        /*
+         * Need to ensure we are only passing full blocks to low level OCB
+         * routines. We do it here rather than in EVP_EncryptUpdate/
+         * EVP_DecryptUpdate because we need to pass full blocks of AAD too
+         * and those routines don't support that
+         */
+
+        /* Are we dealing with AAD or normal data here? */
+        if (out == NULL) {
+            buf = octx->aad_buf;
+            buf_len = &(octx->aad_buf_len);
+        } else {
+            buf = octx->data_buf;
+            buf_len = &(octx->data_buf_len);
+
+            if (is_partially_overlapping(out + *buf_len, in, len)) {
+                EVPerr(EVP_F_AES_OCB_CIPHER, EVP_R_PARTIALLY_OVERLAPPING);
+                return 0;
+            }
+        }
+
+        /*
+         * If we've got a partially filled buffer from a previous call then
+         * use that data first
+         */
+        if (*buf_len > 0) {
+            unsigned int remaining;
+
+            remaining = AES_BLOCK_SIZE - (*buf_len);
+            if (remaining > len) {
+                memcpy(buf + (*buf_len), in, len);
+                *(buf_len) += len;
+                return 0;
+            }
+            memcpy(buf + (*buf_len), in, remaining);
+
+            /*
+             * If we get here we've filled the buffer, so process it
+             */
+            len -= remaining;
+            in += remaining;
+            if (out == NULL) {
+                if (!CRYPTO_ocb128_aad(&octx->ocb, buf, AES_BLOCK_SIZE))
+                    return -1;
+            } else if (EVP_CIPHER_CTX_encrypting(ctx)) {
+                if (!CRYPTO_ocb128_encrypt(&octx->ocb, buf, out,
+                                           AES_BLOCK_SIZE))
+                    return -1;
+            } else {
+                if (!CRYPTO_ocb128_decrypt(&octx->ocb, buf, out,
+                                           AES_BLOCK_SIZE))
+                    return -1;
+            }
+            written_len = AES_BLOCK_SIZE;
+            *buf_len = 0;
+            if (out != NULL)
+                out += AES_BLOCK_SIZE;
+        }
+
+        /* Do we have a partial block to handle at the end? */
+        trailing_len = len % AES_BLOCK_SIZE;
+
+        /*
+         * If we've got some full blocks to handle, then process these first
+         */
+        if (len != trailing_len) {
+            if (out == NULL) {
+                if (!CRYPTO_ocb128_aad(&octx->ocb, in, len - trailing_len))
+                    return -1;
+            } else if (EVP_CIPHER_CTX_encrypting(ctx)) {
+                if (!CRYPTO_ocb128_encrypt
+                    (&octx->ocb, in, out, len - trailing_len))
+                    return -1;
+            } else {
+                if (!CRYPTO_ocb128_decrypt
+                    (&octx->ocb, in, out, len - trailing_len))
+                    return -1;
+            }
+            written_len += len - trailing_len;
+            in += len - trailing_len;
+        }
+
+        /* Handle any trailing partial block */
+        if (trailing_len > 0) {
+            memcpy(buf, in, trailing_len);
+            *buf_len = trailing_len;
+        }
+
+        return written_len;
+    } else {
+        /*
+         * First of all empty the buffer of any partial block that we might
+         * have been provided - both for data and AAD
+         */
+        if (octx->data_buf_len > 0) {
+            if (EVP_CIPHER_CTX_encrypting(ctx)) {
+                if (!CRYPTO_ocb128_encrypt(&octx->ocb, octx->data_buf, out,
+                                           octx->data_buf_len))
+                    return -1;
+            } else {
+                if (!CRYPTO_ocb128_decrypt(&octx->ocb, octx->data_buf, out,
+                                           octx->data_buf_len))
+                    return -1;
+            }
+            written_len = octx->data_buf_len;
+            octx->data_buf_len = 0;
+        }
+        if (octx->aad_buf_len > 0) {
+            if (!CRYPTO_ocb128_aad
+                (&octx->ocb, octx->aad_buf, octx->aad_buf_len))
+                return -1;
+            octx->aad_buf_len = 0;
+        }
+        /* If decrypting then verify */
+        if (!EVP_CIPHER_CTX_encrypting(ctx)) {
+            if (octx->taglen < 0)
+                return -1;
+            if (CRYPTO_ocb128_finish(&octx->ocb,
+                                     octx->tag, octx->taglen) != 0)
+                return -1;
+            octx->iv_set = 0;
+            return written_len;
+        }
+        /* If encrypting then just get the tag */
+        if (CRYPTO_ocb128_tag(&octx->ocb, octx->tag, 16) != 1)
+            return -1;
+        /* Don't reuse the IV */
+        octx->iv_set = 0;
+        return written_len;
+    }
+}
+
+static int aes_ocb_cleanup(EVP_CIPHER_CTX *c)
+{
+    EVP_AES_OCB_CTX *octx = EVP_C_DATA(EVP_AES_OCB_CTX,c);
+    CRYPTO_ocb128_cleanup(&octx->ocb);
+    return 1;
+}
+
+BLOCK_CIPHER_custom(NID_aes, 128, 16, 12, ocb, OCB,
+                    EVP_CIPH_FLAG_AEAD_CIPHER | CUSTOM_FLAGS)
+BLOCK_CIPHER_custom(NID_aes, 192, 16, 12, ocb, OCB,
+                    EVP_CIPH_FLAG_AEAD_CIPHER | CUSTOM_FLAGS)
+BLOCK_CIPHER_custom(NID_aes, 256, 16, 12, ocb, OCB,
+                    EVP_CIPH_FLAG_AEAD_CIPHER | CUSTOM_FLAGS)
+#endif                         /* OPENSSL_NO_OCB */
+#endif
diff --git a/crypto/evp/e_aes_cbc_hmac_sha1.c b/crypto/evp/e_aes_cbc_hmac_sha1.c
new file mode 100644
index 000000000..e9a49259e
--- /dev/null
+++ b/crypto/evp/e_aes_cbc_hmac_sha1.c
@@ -0,0 +1,961 @@
+/*
+ * Copyright 2011-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#if defined(OPENSSL_NO_AES) || defined(OPENSSL_NO_SHA)
+NON_EMPTY_TRANSLATION_UNIT
+#else
+
+#include 
+#include 
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include "modes_lcl.h"
+#include "internal/evp_int.h"
+#include "internal/constant_time_locl.h"
+
+typedef struct {
+    AES_KEY ks;
+    SHA_CTX head, tail, md;
+    size_t payload_length;      /* AAD length in decrypt case */
+    union {
+        unsigned int tls_ver;
+        unsigned char tls_aad[16]; /* 13 used */
+    } aux;
+} EVP_AES_HMAC_SHA1;
+
+#define NO_PAYLOAD_LENGTH       ((size_t)-1)
+
+#if     defined(AES_ASM) &&     ( \
+        defined(__x86_64)       || defined(__x86_64__)  || \
+        defined(_M_AMD64)       || defined(_M_X64)      )
+
+extern unsigned int OPENSSL_ia32cap_P[];
+# define AESNI_CAPABLE   (1<<(57-32))
+
+int aesni_set_encrypt_key(const unsigned char *userKey, int bits,
+                          AES_KEY *key);
+int aesni_set_decrypt_key(const unsigned char *userKey, int bits,
+                          AES_KEY *key);
+
+void aesni_cbc_encrypt(const unsigned char *in,
+                       unsigned char *out,
+                       size_t length,
+                       const AES_KEY *key, unsigned char *ivec, int enc);
+
+void aesni_cbc_sha1_enc(const void *inp, void *out, size_t blocks,
+                        const AES_KEY *key, unsigned char iv[16],
+                        SHA_CTX *ctx, const void *in0);
+
+void aesni256_cbc_sha1_dec(const void *inp, void *out, size_t blocks,
+                           const AES_KEY *key, unsigned char iv[16],
+                           SHA_CTX *ctx, const void *in0);
+
+# define data(ctx) ((EVP_AES_HMAC_SHA1 *)EVP_CIPHER_CTX_get_cipher_data(ctx))
+
+static int aesni_cbc_hmac_sha1_init_key(EVP_CIPHER_CTX *ctx,
+                                        const unsigned char *inkey,
+                                        const unsigned char *iv, int enc)
+{
+    EVP_AES_HMAC_SHA1 *key = data(ctx);
+    int ret;
+
+    if (enc)
+        ret = aesni_set_encrypt_key(inkey,
+                                    EVP_CIPHER_CTX_key_length(ctx) * 8,
+                                    &key->ks);
+    else
+        ret = aesni_set_decrypt_key(inkey,
+                                    EVP_CIPHER_CTX_key_length(ctx) * 8,
+                                    &key->ks);
+
+    SHA1_Init(&key->head);      /* handy when benchmarking */
+    key->tail = key->head;
+    key->md = key->head;
+
+    key->payload_length = NO_PAYLOAD_LENGTH;
+
+    return ret < 0 ? 0 : 1;
+}
+
+# define STITCHED_CALL
+# undef  STITCHED_DECRYPT_CALL
+
+# if !defined(STITCHED_CALL)
+#  define aes_off 0
+# endif
+
+void sha1_block_data_order(void *c, const void *p, size_t len);
+
+static void sha1_update(SHA_CTX *c, const void *data, size_t len)
+{
+    const unsigned char *ptr = data;
+    size_t res;
+
+    if ((res = c->num)) {
+        res = SHA_CBLOCK - res;
+        if (len < res)
+            res = len;
+        SHA1_Update(c, ptr, res);
+        ptr += res;
+        len -= res;
+    }
+
+    res = len % SHA_CBLOCK;
+    len -= res;
+
+    if (len) {
+        sha1_block_data_order(c, ptr, len / SHA_CBLOCK);
+
+        ptr += len;
+        c->Nh += len >> 29;
+        c->Nl += len <<= 3;
+        if (c->Nl < (unsigned int)len)
+            c->Nh++;
+    }
+
+    if (res)
+        SHA1_Update(c, ptr, res);
+}
+
+# ifdef SHA1_Update
+#  undef SHA1_Update
+# endif
+# define SHA1_Update sha1_update
+
+# if !defined(OPENSSL_NO_MULTIBLOCK)
+
+typedef struct {
+    unsigned int A[8], B[8], C[8], D[8], E[8];
+} SHA1_MB_CTX;
+typedef struct {
+    const unsigned char *ptr;
+    int blocks;
+} HASH_DESC;
+
+void sha1_multi_block(SHA1_MB_CTX *, const HASH_DESC *, int);
+
+typedef struct {
+    const unsigned char *inp;
+    unsigned char *out;
+    int blocks;
+    u64 iv[2];
+} CIPH_DESC;
+
+void aesni_multi_cbc_encrypt(CIPH_DESC *, void *, int);
+
+static size_t tls1_1_multi_block_encrypt(EVP_AES_HMAC_SHA1 *key,
+                                         unsigned char *out,
+                                         const unsigned char *inp,
+                                         size_t inp_len, int n4x)
+{                               /* n4x is 1 or 2 */
+    HASH_DESC hash_d[8], edges[8];
+    CIPH_DESC ciph_d[8];
+    unsigned char storage[sizeof(SHA1_MB_CTX) + 32];
+    union {
+        u64 q[16];
+        u32 d[32];
+        u8 c[128];
+    } blocks[8];
+    SHA1_MB_CTX *ctx;
+    unsigned int frag, last, packlen, i, x4 = 4 * n4x, minblocks, processed =
+        0;
+    size_t ret = 0;
+    u8 *IVs;
+#  if defined(BSWAP8)
+    u64 seqnum;
+#  endif
+
+    /* ask for IVs in bulk */
+    if (RAND_bytes((IVs = blocks[0].c), 16 * x4) <= 0)
+        return 0;
+
+    ctx = (SHA1_MB_CTX *) (storage + 32 - ((size_t)storage % 32)); /* align */
+
+    frag = (unsigned int)inp_len >> (1 + n4x);
+    last = (unsigned int)inp_len + frag - (frag << (1 + n4x));
+    if (last > frag && ((last + 13 + 9) % 64) < (x4 - 1)) {
+        frag++;
+        last -= x4 - 1;
+    }
+
+    packlen = 5 + 16 + ((frag + 20 + 16) & -16);
+
+    /* populate descriptors with pointers and IVs */
+    hash_d[0].ptr = inp;
+    ciph_d[0].inp = inp;
+    /* 5+16 is place for header and explicit IV */
+    ciph_d[0].out = out + 5 + 16;
+    memcpy(ciph_d[0].out - 16, IVs, 16);
+    memcpy(ciph_d[0].iv, IVs, 16);
+    IVs += 16;
+
+    for (i = 1; i < x4; i++) {
+        ciph_d[i].inp = hash_d[i].ptr = hash_d[i - 1].ptr + frag;
+        ciph_d[i].out = ciph_d[i - 1].out + packlen;
+        memcpy(ciph_d[i].out - 16, IVs, 16);
+        memcpy(ciph_d[i].iv, IVs, 16);
+        IVs += 16;
+    }
+
+#  if defined(BSWAP8)
+    memcpy(blocks[0].c, key->md.data, 8);
+    seqnum = BSWAP8(blocks[0].q[0]);
+#  endif
+    for (i = 0; i < x4; i++) {
+        unsigned int len = (i == (x4 - 1) ? last : frag);
+#  if !defined(BSWAP8)
+        unsigned int carry, j;
+#  endif
+
+        ctx->A[i] = key->md.h0;
+        ctx->B[i] = key->md.h1;
+        ctx->C[i] = key->md.h2;
+        ctx->D[i] = key->md.h3;
+        ctx->E[i] = key->md.h4;
+
+        /* fix seqnum */
+#  if defined(BSWAP8)
+        blocks[i].q[0] = BSWAP8(seqnum + i);
+#  else
+        for (carry = i, j = 8; j--;) {
+            blocks[i].c[j] = ((u8 *)key->md.data)[j] + carry;
+            carry = (blocks[i].c[j] - carry) >> (sizeof(carry) * 8 - 1);
+        }
+#  endif
+        blocks[i].c[8] = ((u8 *)key->md.data)[8];
+        blocks[i].c[9] = ((u8 *)key->md.data)[9];
+        blocks[i].c[10] = ((u8 *)key->md.data)[10];
+        /* fix length */
+        blocks[i].c[11] = (u8)(len >> 8);
+        blocks[i].c[12] = (u8)(len);
+
+        memcpy(blocks[i].c + 13, hash_d[i].ptr, 64 - 13);
+        hash_d[i].ptr += 64 - 13;
+        hash_d[i].blocks = (len - (64 - 13)) / 64;
+
+        edges[i].ptr = blocks[i].c;
+        edges[i].blocks = 1;
+    }
+
+    /* hash 13-byte headers and first 64-13 bytes of inputs */
+    sha1_multi_block(ctx, edges, n4x);
+    /* hash bulk inputs */
+#  define MAXCHUNKSIZE    2048
+#  if     MAXCHUNKSIZE%64
+#   error  "MAXCHUNKSIZE is not divisible by 64"
+#  elif   MAXCHUNKSIZE
+    /*
+     * goal is to minimize pressure on L1 cache by moving in shorter steps,
+     * so that hashed data is still in the cache by the time we encrypt it
+     */
+    minblocks = ((frag <= last ? frag : last) - (64 - 13)) / 64;
+    if (minblocks > MAXCHUNKSIZE / 64) {
+        for (i = 0; i < x4; i++) {
+            edges[i].ptr = hash_d[i].ptr;
+            edges[i].blocks = MAXCHUNKSIZE / 64;
+            ciph_d[i].blocks = MAXCHUNKSIZE / 16;
+        }
+        do {
+            sha1_multi_block(ctx, edges, n4x);
+            aesni_multi_cbc_encrypt(ciph_d, &key->ks, n4x);
+
+            for (i = 0; i < x4; i++) {
+                edges[i].ptr = hash_d[i].ptr += MAXCHUNKSIZE;
+                hash_d[i].blocks -= MAXCHUNKSIZE / 64;
+                edges[i].blocks = MAXCHUNKSIZE / 64;
+                ciph_d[i].inp += MAXCHUNKSIZE;
+                ciph_d[i].out += MAXCHUNKSIZE;
+                ciph_d[i].blocks = MAXCHUNKSIZE / 16;
+                memcpy(ciph_d[i].iv, ciph_d[i].out - 16, 16);
+            }
+            processed += MAXCHUNKSIZE;
+            minblocks -= MAXCHUNKSIZE / 64;
+        } while (minblocks > MAXCHUNKSIZE / 64);
+    }
+#  endif
+#  undef  MAXCHUNKSIZE
+    sha1_multi_block(ctx, hash_d, n4x);
+
+    memset(blocks, 0, sizeof(blocks));
+    for (i = 0; i < x4; i++) {
+        unsigned int len = (i == (x4 - 1) ? last : frag),
+            off = hash_d[i].blocks * 64;
+        const unsigned char *ptr = hash_d[i].ptr + off;
+
+        off = (len - processed) - (64 - 13) - off; /* remainder actually */
+        memcpy(blocks[i].c, ptr, off);
+        blocks[i].c[off] = 0x80;
+        len += 64 + 13;         /* 64 is HMAC header */
+        len *= 8;               /* convert to bits */
+        if (off < (64 - 8)) {
+#  ifdef BSWAP4
+            blocks[i].d[15] = BSWAP4(len);
+#  else
+            PUTU32(blocks[i].c + 60, len);
+#  endif
+            edges[i].blocks = 1;
+        } else {
+#  ifdef BSWAP4
+            blocks[i].d[31] = BSWAP4(len);
+#  else
+            PUTU32(blocks[i].c + 124, len);
+#  endif
+            edges[i].blocks = 2;
+        }
+        edges[i].ptr = blocks[i].c;
+    }
+
+    /* hash input tails and finalize */
+    sha1_multi_block(ctx, edges, n4x);
+
+    memset(blocks, 0, sizeof(blocks));
+    for (i = 0; i < x4; i++) {
+#  ifdef BSWAP4
+        blocks[i].d[0] = BSWAP4(ctx->A[i]);
+        ctx->A[i] = key->tail.h0;
+        blocks[i].d[1] = BSWAP4(ctx->B[i]);
+        ctx->B[i] = key->tail.h1;
+        blocks[i].d[2] = BSWAP4(ctx->C[i]);
+        ctx->C[i] = key->tail.h2;
+        blocks[i].d[3] = BSWAP4(ctx->D[i]);
+        ctx->D[i] = key->tail.h3;
+        blocks[i].d[4] = BSWAP4(ctx->E[i]);
+        ctx->E[i] = key->tail.h4;
+        blocks[i].c[20] = 0x80;
+        blocks[i].d[15] = BSWAP4((64 + 20) * 8);
+#  else
+        PUTU32(blocks[i].c + 0, ctx->A[i]);
+        ctx->A[i] = key->tail.h0;
+        PUTU32(blocks[i].c + 4, ctx->B[i]);
+        ctx->B[i] = key->tail.h1;
+        PUTU32(blocks[i].c + 8, ctx->C[i]);
+        ctx->C[i] = key->tail.h2;
+        PUTU32(blocks[i].c + 12, ctx->D[i]);
+        ctx->D[i] = key->tail.h3;
+        PUTU32(blocks[i].c + 16, ctx->E[i]);
+        ctx->E[i] = key->tail.h4;
+        blocks[i].c[20] = 0x80;
+        PUTU32(blocks[i].c + 60, (64 + 20) * 8);
+#  endif
+        edges[i].ptr = blocks[i].c;
+        edges[i].blocks = 1;
+    }
+
+    /* finalize MACs */
+    sha1_multi_block(ctx, edges, n4x);
+
+    for (i = 0; i < x4; i++) {
+        unsigned int len = (i == (x4 - 1) ? last : frag), pad, j;
+        unsigned char *out0 = out;
+
+        memcpy(ciph_d[i].out, ciph_d[i].inp, len - processed);
+        ciph_d[i].inp = ciph_d[i].out;
+
+        out += 5 + 16 + len;
+
+        /* write MAC */
+        PUTU32(out + 0, ctx->A[i]);
+        PUTU32(out + 4, ctx->B[i]);
+        PUTU32(out + 8, ctx->C[i]);
+        PUTU32(out + 12, ctx->D[i]);
+        PUTU32(out + 16, ctx->E[i]);
+        out += 20;
+        len += 20;
+
+        /* pad */
+        pad = 15 - len % 16;
+        for (j = 0; j <= pad; j++)
+            *(out++) = pad;
+        len += pad + 1;
+
+        ciph_d[i].blocks = (len - processed) / 16;
+        len += 16;              /* account for explicit iv */
+
+        /* arrange header */
+        out0[0] = ((u8 *)key->md.data)[8];
+        out0[1] = ((u8 *)key->md.data)[9];
+        out0[2] = ((u8 *)key->md.data)[10];
+        out0[3] = (u8)(len >> 8);
+        out0[4] = (u8)(len);
+
+        ret += len + 5;
+        inp += frag;
+    }
+
+    aesni_multi_cbc_encrypt(ciph_d, &key->ks, n4x);
+
+    OPENSSL_cleanse(blocks, sizeof(blocks));
+    OPENSSL_cleanse(ctx, sizeof(*ctx));
+
+    return ret;
+}
+# endif
+
+static int aesni_cbc_hmac_sha1_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
+                                      const unsigned char *in, size_t len)
+{
+    EVP_AES_HMAC_SHA1 *key = data(ctx);
+    unsigned int l;
+    size_t plen = key->payload_length, iv = 0, /* explicit IV in TLS 1.1 and
+                                                * later */
+        sha_off = 0;
+# if defined(STITCHED_CALL)
+    size_t aes_off = 0, blocks;
+
+    sha_off = SHA_CBLOCK - key->md.num;
+# endif
+
+    key->payload_length = NO_PAYLOAD_LENGTH;
+
+    if (len % AES_BLOCK_SIZE)
+        return 0;
+
+    if (EVP_CIPHER_CTX_encrypting(ctx)) {
+        if (plen == NO_PAYLOAD_LENGTH)
+            plen = len;
+        else if (len !=
+                 ((plen + SHA_DIGEST_LENGTH +
+                   AES_BLOCK_SIZE) & -AES_BLOCK_SIZE))
+            return 0;
+        else if (key->aux.tls_ver >= TLS1_1_VERSION)
+            iv = AES_BLOCK_SIZE;
+
+# if defined(STITCHED_CALL)
+        if (plen > (sha_off + iv)
+            && (blocks = (plen - (sha_off + iv)) / SHA_CBLOCK)) {
+            SHA1_Update(&key->md, in + iv, sha_off);
+
+            aesni_cbc_sha1_enc(in, out, blocks, &key->ks,
+                               EVP_CIPHER_CTX_iv_noconst(ctx),
+                               &key->md, in + iv + sha_off);
+            blocks *= SHA_CBLOCK;
+            aes_off += blocks;
+            sha_off += blocks;
+            key->md.Nh += blocks >> 29;
+            key->md.Nl += blocks <<= 3;
+            if (key->md.Nl < (unsigned int)blocks)
+                key->md.Nh++;
+        } else {
+            sha_off = 0;
+        }
+# endif
+        sha_off += iv;
+        SHA1_Update(&key->md, in + sha_off, plen - sha_off);
+
+        if (plen != len) {      /* "TLS" mode of operation */
+            if (in != out)
+                memcpy(out + aes_off, in + aes_off, plen - aes_off);
+
+            /* calculate HMAC and append it to payload */
+            SHA1_Final(out + plen, &key->md);
+            key->md = key->tail;
+            SHA1_Update(&key->md, out + plen, SHA_DIGEST_LENGTH);
+            SHA1_Final(out + plen, &key->md);
+
+            /* pad the payload|hmac */
+            plen += SHA_DIGEST_LENGTH;
+            for (l = len - plen - 1; plen < len; plen++)
+                out[plen] = l;
+            /* encrypt HMAC|padding at once */
+            aesni_cbc_encrypt(out + aes_off, out + aes_off, len - aes_off,
+                              &key->ks, EVP_CIPHER_CTX_iv_noconst(ctx), 1);
+        } else {
+            aesni_cbc_encrypt(in + aes_off, out + aes_off, len - aes_off,
+                              &key->ks, EVP_CIPHER_CTX_iv_noconst(ctx), 1);
+        }
+    } else {
+        union {
+            unsigned int u[SHA_DIGEST_LENGTH / sizeof(unsigned int)];
+            unsigned char c[32 + SHA_DIGEST_LENGTH];
+        } mac, *pmac;
+
+        /* arrange cache line alignment */
+        pmac = (void *)(((size_t)mac.c + 31) & ((size_t)0 - 32));
+
+        if (plen != NO_PAYLOAD_LENGTH) { /* "TLS" mode of operation */
+            size_t inp_len, mask, j, i;
+            unsigned int res, maxpad, pad, bitlen;
+            int ret = 1;
+            union {
+                unsigned int u[SHA_LBLOCK];
+                unsigned char c[SHA_CBLOCK];
+            } *data = (void *)key->md.data;
+# if defined(STITCHED_DECRYPT_CALL)
+            unsigned char tail_iv[AES_BLOCK_SIZE];
+            int stitch = 0;
+# endif
+
+            if ((key->aux.tls_aad[plen - 4] << 8 | key->aux.tls_aad[plen - 3])
+                >= TLS1_1_VERSION) {
+                if (len < (AES_BLOCK_SIZE + SHA_DIGEST_LENGTH + 1))
+                    return 0;
+
+                /* omit explicit iv */
+                memcpy(EVP_CIPHER_CTX_iv_noconst(ctx), in, AES_BLOCK_SIZE);
+
+                in += AES_BLOCK_SIZE;
+                out += AES_BLOCK_SIZE;
+                len -= AES_BLOCK_SIZE;
+            } else if (len < (SHA_DIGEST_LENGTH + 1))
+                return 0;
+
+# if defined(STITCHED_DECRYPT_CALL)
+            if (len >= 1024 && ctx->key_len == 32) {
+                /* decrypt last block */
+                memcpy(tail_iv, in + len - 2 * AES_BLOCK_SIZE,
+                       AES_BLOCK_SIZE);
+                aesni_cbc_encrypt(in + len - AES_BLOCK_SIZE,
+                                  out + len - AES_BLOCK_SIZE, AES_BLOCK_SIZE,
+                                  &key->ks, tail_iv, 0);
+                stitch = 1;
+            } else
+# endif
+                /* decrypt HMAC|padding at once */
+                aesni_cbc_encrypt(in, out, len, &key->ks,
+                                  EVP_CIPHER_CTX_iv_noconst(ctx), 0);
+
+            /* figure out payload length */
+            pad = out[len - 1];
+            maxpad = len - (SHA_DIGEST_LENGTH + 1);
+            maxpad |= (255 - maxpad) >> (sizeof(maxpad) * 8 - 8);
+            maxpad &= 255;
+
+            ret &= constant_time_ge(maxpad, pad);
+
+            inp_len = len - (SHA_DIGEST_LENGTH + pad + 1);
+            mask = (0 - ((inp_len - len) >> (sizeof(inp_len) * 8 - 1)));
+            inp_len &= mask;
+            ret &= (int)mask;
+
+            key->aux.tls_aad[plen - 2] = inp_len >> 8;
+            key->aux.tls_aad[plen - 1] = inp_len;
+
+            /* calculate HMAC */
+            key->md = key->head;
+            SHA1_Update(&key->md, key->aux.tls_aad, plen);
+
+# if defined(STITCHED_DECRYPT_CALL)
+            if (stitch) {
+                blocks = (len - (256 + 32 + SHA_CBLOCK)) / SHA_CBLOCK;
+                aes_off = len - AES_BLOCK_SIZE - blocks * SHA_CBLOCK;
+                sha_off = SHA_CBLOCK - plen;
+
+                aesni_cbc_encrypt(in, out, aes_off, &key->ks, ctx->iv, 0);
+
+                SHA1_Update(&key->md, out, sha_off);
+                aesni256_cbc_sha1_dec(in + aes_off,
+                                      out + aes_off, blocks, &key->ks,
+                                      ctx->iv, &key->md, out + sha_off);
+
+                sha_off += blocks *= SHA_CBLOCK;
+                out += sha_off;
+                len -= sha_off;
+                inp_len -= sha_off;
+
+                key->md.Nl += (blocks << 3); /* at most 18 bits */
+                memcpy(ctx->iv, tail_iv, AES_BLOCK_SIZE);
+            }
+# endif
+
+# if 1
+            len -= SHA_DIGEST_LENGTH; /* amend mac */
+            if (len >= (256 + SHA_CBLOCK)) {
+                j = (len - (256 + SHA_CBLOCK)) & (0 - SHA_CBLOCK);
+                j += SHA_CBLOCK - key->md.num;
+                SHA1_Update(&key->md, out, j);
+                out += j;
+                len -= j;
+                inp_len -= j;
+            }
+
+            /* but pretend as if we hashed padded payload */
+            bitlen = key->md.Nl + (inp_len << 3); /* at most 18 bits */
+#  ifdef BSWAP4
+            bitlen = BSWAP4(bitlen);
+#  else
+            mac.c[0] = 0;
+            mac.c[1] = (unsigned char)(bitlen >> 16);
+            mac.c[2] = (unsigned char)(bitlen >> 8);
+            mac.c[3] = (unsigned char)bitlen;
+            bitlen = mac.u[0];
+#  endif
+
+            pmac->u[0] = 0;
+            pmac->u[1] = 0;
+            pmac->u[2] = 0;
+            pmac->u[3] = 0;
+            pmac->u[4] = 0;
+
+            for (res = key->md.num, j = 0; j < len; j++) {
+                size_t c = out[j];
+                mask = (j - inp_len) >> (sizeof(j) * 8 - 8);
+                c &= mask;
+                c |= 0x80 & ~mask & ~((inp_len - j) >> (sizeof(j) * 8 - 8));
+                data->c[res++] = (unsigned char)c;
+
+                if (res != SHA_CBLOCK)
+                    continue;
+
+                /* j is not incremented yet */
+                mask = 0 - ((inp_len + 7 - j) >> (sizeof(j) * 8 - 1));
+                data->u[SHA_LBLOCK - 1] |= bitlen & mask;
+                sha1_block_data_order(&key->md, data, 1);
+                mask &= 0 - ((j - inp_len - 72) >> (sizeof(j) * 8 - 1));
+                pmac->u[0] |= key->md.h0 & mask;
+                pmac->u[1] |= key->md.h1 & mask;
+                pmac->u[2] |= key->md.h2 & mask;
+                pmac->u[3] |= key->md.h3 & mask;
+                pmac->u[4] |= key->md.h4 & mask;
+                res = 0;
+            }
+
+            for (i = res; i < SHA_CBLOCK; i++, j++)
+                data->c[i] = 0;
+
+            if (res > SHA_CBLOCK - 8) {
+                mask = 0 - ((inp_len + 8 - j) >> (sizeof(j) * 8 - 1));
+                data->u[SHA_LBLOCK - 1] |= bitlen & mask;
+                sha1_block_data_order(&key->md, data, 1);
+                mask &= 0 - ((j - inp_len - 73) >> (sizeof(j) * 8 - 1));
+                pmac->u[0] |= key->md.h0 & mask;
+                pmac->u[1] |= key->md.h1 & mask;
+                pmac->u[2] |= key->md.h2 & mask;
+                pmac->u[3] |= key->md.h3 & mask;
+                pmac->u[4] |= key->md.h4 & mask;
+
+                memset(data, 0, SHA_CBLOCK);
+                j += 64;
+            }
+            data->u[SHA_LBLOCK - 1] = bitlen;
+            sha1_block_data_order(&key->md, data, 1);
+            mask = 0 - ((j - inp_len - 73) >> (sizeof(j) * 8 - 1));
+            pmac->u[0] |= key->md.h0 & mask;
+            pmac->u[1] |= key->md.h1 & mask;
+            pmac->u[2] |= key->md.h2 & mask;
+            pmac->u[3] |= key->md.h3 & mask;
+            pmac->u[4] |= key->md.h4 & mask;
+
+#  ifdef BSWAP4
+            pmac->u[0] = BSWAP4(pmac->u[0]);
+            pmac->u[1] = BSWAP4(pmac->u[1]);
+            pmac->u[2] = BSWAP4(pmac->u[2]);
+            pmac->u[3] = BSWAP4(pmac->u[3]);
+            pmac->u[4] = BSWAP4(pmac->u[4]);
+#  else
+            for (i = 0; i < 5; i++) {
+                res = pmac->u[i];
+                pmac->c[4 * i + 0] = (unsigned char)(res >> 24);
+                pmac->c[4 * i + 1] = (unsigned char)(res >> 16);
+                pmac->c[4 * i + 2] = (unsigned char)(res >> 8);
+                pmac->c[4 * i + 3] = (unsigned char)res;
+            }
+#  endif
+            len += SHA_DIGEST_LENGTH;
+# else
+            SHA1_Update(&key->md, out, inp_len);
+            res = key->md.num;
+            SHA1_Final(pmac->c, &key->md);
+
+            {
+                unsigned int inp_blocks, pad_blocks;
+
+                /* but pretend as if we hashed padded payload */
+                inp_blocks =
+                    1 + ((SHA_CBLOCK - 9 - res) >> (sizeof(res) * 8 - 1));
+                res += (unsigned int)(len - inp_len);
+                pad_blocks = res / SHA_CBLOCK;
+                res %= SHA_CBLOCK;
+                pad_blocks +=
+                    1 + ((SHA_CBLOCK - 9 - res) >> (sizeof(res) * 8 - 1));
+                for (; inp_blocks < pad_blocks; inp_blocks++)
+                    sha1_block_data_order(&key->md, data, 1);
+            }
+# endif
+            key->md = key->tail;
+            SHA1_Update(&key->md, pmac->c, SHA_DIGEST_LENGTH);
+            SHA1_Final(pmac->c, &key->md);
+
+            /* verify HMAC */
+            out += inp_len;
+            len -= inp_len;
+# if 1
+            {
+                unsigned char *p = out + len - 1 - maxpad - SHA_DIGEST_LENGTH;
+                size_t off = out - p;
+                unsigned int c, cmask;
+
+                maxpad += SHA_DIGEST_LENGTH;
+                for (res = 0, i = 0, j = 0; j < maxpad; j++) {
+                    c = p[j];
+                    cmask =
+                        ((int)(j - off - SHA_DIGEST_LENGTH)) >> (sizeof(int) *
+                                                                 8 - 1);
+                    res |= (c ^ pad) & ~cmask; /* ... and padding */
+                    cmask &= ((int)(off - 1 - j)) >> (sizeof(int) * 8 - 1);
+                    res |= (c ^ pmac->c[i]) & cmask;
+                    i += 1 & cmask;
+                }
+                maxpad -= SHA_DIGEST_LENGTH;
+
+                res = 0 - ((0 - res) >> (sizeof(res) * 8 - 1));
+                ret &= (int)~res;
+            }
+# else
+            for (res = 0, i = 0; i < SHA_DIGEST_LENGTH; i++)
+                res |= out[i] ^ pmac->c[i];
+            res = 0 - ((0 - res) >> (sizeof(res) * 8 - 1));
+            ret &= (int)~res;
+
+            /* verify padding */
+            pad = (pad & ~res) | (maxpad & res);
+            out = out + len - 1 - pad;
+            for (res = 0, i = 0; i < pad; i++)
+                res |= out[i] ^ pad;
+
+            res = (0 - res) >> (sizeof(res) * 8 - 1);
+            ret &= (int)~res;
+# endif
+            return ret;
+        } else {
+# if defined(STITCHED_DECRYPT_CALL)
+            if (len >= 1024 && ctx->key_len == 32) {
+                if (sha_off %= SHA_CBLOCK)
+                    blocks = (len - 3 * SHA_CBLOCK) / SHA_CBLOCK;
+                else
+                    blocks = (len - 2 * SHA_CBLOCK) / SHA_CBLOCK;
+                aes_off = len - blocks * SHA_CBLOCK;
+
+                aesni_cbc_encrypt(in, out, aes_off, &key->ks, ctx->iv, 0);
+                SHA1_Update(&key->md, out, sha_off);
+                aesni256_cbc_sha1_dec(in + aes_off,
+                                      out + aes_off, blocks, &key->ks,
+                                      ctx->iv, &key->md, out + sha_off);
+
+                sha_off += blocks *= SHA_CBLOCK;
+                out += sha_off;
+                len -= sha_off;
+
+                key->md.Nh += blocks >> 29;
+                key->md.Nl += blocks <<= 3;
+                if (key->md.Nl < (unsigned int)blocks)
+                    key->md.Nh++;
+            } else
+# endif
+                /* decrypt HMAC|padding at once */
+                aesni_cbc_encrypt(in, out, len, &key->ks,
+                                  EVP_CIPHER_CTX_iv_noconst(ctx), 0);
+
+            SHA1_Update(&key->md, out, len);
+        }
+    }
+
+    return 1;
+}
+
+static int aesni_cbc_hmac_sha1_ctrl(EVP_CIPHER_CTX *ctx, int type, int arg,
+                                    void *ptr)
+{
+    EVP_AES_HMAC_SHA1 *key = data(ctx);
+
+    switch (type) {
+    case EVP_CTRL_AEAD_SET_MAC_KEY:
+        {
+            unsigned int i;
+            unsigned char hmac_key[64];
+
+            memset(hmac_key, 0, sizeof(hmac_key));
+
+            if (arg > (int)sizeof(hmac_key)) {
+                SHA1_Init(&key->head);
+                SHA1_Update(&key->head, ptr, arg);
+                SHA1_Final(hmac_key, &key->head);
+            } else {
+                memcpy(hmac_key, ptr, arg);
+            }
+
+            for (i = 0; i < sizeof(hmac_key); i++)
+                hmac_key[i] ^= 0x36; /* ipad */
+            SHA1_Init(&key->head);
+            SHA1_Update(&key->head, hmac_key, sizeof(hmac_key));
+
+            for (i = 0; i < sizeof(hmac_key); i++)
+                hmac_key[i] ^= 0x36 ^ 0x5c; /* opad */
+            SHA1_Init(&key->tail);
+            SHA1_Update(&key->tail, hmac_key, sizeof(hmac_key));
+
+            OPENSSL_cleanse(hmac_key, sizeof(hmac_key));
+
+            return 1;
+        }
+    case EVP_CTRL_AEAD_TLS1_AAD:
+        {
+            unsigned char *p = ptr;
+            unsigned int len;
+
+            if (arg != EVP_AEAD_TLS1_AAD_LEN)
+                return -1;
+
+            len = p[arg - 2] << 8 | p[arg - 1];
+
+            if (EVP_CIPHER_CTX_encrypting(ctx)) {
+                key->payload_length = len;
+                if ((key->aux.tls_ver =
+                     p[arg - 4] << 8 | p[arg - 3]) >= TLS1_1_VERSION) {
+                    len -= AES_BLOCK_SIZE;
+                    p[arg - 2] = len >> 8;
+                    p[arg - 1] = len;
+                }
+                key->md = key->head;
+                SHA1_Update(&key->md, p, arg);
+
+                return (int)(((len + SHA_DIGEST_LENGTH +
+                               AES_BLOCK_SIZE) & -AES_BLOCK_SIZE)
+                             - len);
+            } else {
+                memcpy(key->aux.tls_aad, ptr, arg);
+                key->payload_length = arg;
+
+                return SHA_DIGEST_LENGTH;
+            }
+        }
+# if !defined(OPENSSL_NO_MULTIBLOCK)
+    case EVP_CTRL_TLS1_1_MULTIBLOCK_MAX_BUFSIZE:
+        return (int)(5 + 16 + ((arg + 20 + 16) & -16));
+    case EVP_CTRL_TLS1_1_MULTIBLOCK_AAD:
+        {
+            EVP_CTRL_TLS1_1_MULTIBLOCK_PARAM *param =
+                (EVP_CTRL_TLS1_1_MULTIBLOCK_PARAM *) ptr;
+            unsigned int n4x = 1, x4;
+            unsigned int frag, last, packlen, inp_len;
+
+            if (arg < (int)sizeof(EVP_CTRL_TLS1_1_MULTIBLOCK_PARAM))
+                return -1;
+
+            inp_len = param->inp[11] << 8 | param->inp[12];
+
+            if (EVP_CIPHER_CTX_encrypting(ctx)) {
+                if ((param->inp[9] << 8 | param->inp[10]) < TLS1_1_VERSION)
+                    return -1;
+
+                if (inp_len) {
+                    if (inp_len < 4096)
+                        return 0; /* too short */
+
+                    if (inp_len >= 8192 && OPENSSL_ia32cap_P[2] & (1 << 5))
+                        n4x = 2; /* AVX2 */
+                } else if ((n4x = param->interleave / 4) && n4x <= 2)
+                    inp_len = param->len;
+                else
+                    return -1;
+
+                key->md = key->head;
+                SHA1_Update(&key->md, param->inp, 13);
+
+                x4 = 4 * n4x;
+                n4x += 1;
+
+                frag = inp_len >> n4x;
+                last = inp_len + frag - (frag << n4x);
+                if (last > frag && ((last + 13 + 9) % 64 < (x4 - 1))) {
+                    frag++;
+                    last -= x4 - 1;
+                }
+
+                packlen = 5 + 16 + ((frag + 20 + 16) & -16);
+                packlen = (packlen << n4x) - packlen;
+                packlen += 5 + 16 + ((last + 20 + 16) & -16);
+
+                param->interleave = x4;
+
+                return (int)packlen;
+            } else
+                return -1;      /* not yet */
+        }
+    case EVP_CTRL_TLS1_1_MULTIBLOCK_ENCRYPT:
+        {
+            EVP_CTRL_TLS1_1_MULTIBLOCK_PARAM *param =
+                (EVP_CTRL_TLS1_1_MULTIBLOCK_PARAM *) ptr;
+
+            return (int)tls1_1_multi_block_encrypt(key, param->out,
+                                                   param->inp, param->len,
+                                                   param->interleave / 4);
+        }
+    case EVP_CTRL_TLS1_1_MULTIBLOCK_DECRYPT:
+# endif
+    default:
+        return -1;
+    }
+}
+
+static EVP_CIPHER aesni_128_cbc_hmac_sha1_cipher = {
+# ifdef NID_aes_128_cbc_hmac_sha1
+    NID_aes_128_cbc_hmac_sha1,
+# else
+    NID_undef,
+# endif
+    AES_BLOCK_SIZE, 16, AES_BLOCK_SIZE,
+    EVP_CIPH_CBC_MODE | EVP_CIPH_FLAG_DEFAULT_ASN1 |
+        EVP_CIPH_FLAG_AEAD_CIPHER | EVP_CIPH_FLAG_TLS1_1_MULTIBLOCK,
+    aesni_cbc_hmac_sha1_init_key,
+    aesni_cbc_hmac_sha1_cipher,
+    NULL,
+    sizeof(EVP_AES_HMAC_SHA1),
+    EVP_CIPH_FLAG_DEFAULT_ASN1 ? NULL : EVP_CIPHER_set_asn1_iv,
+    EVP_CIPH_FLAG_DEFAULT_ASN1 ? NULL : EVP_CIPHER_get_asn1_iv,
+    aesni_cbc_hmac_sha1_ctrl,
+    NULL
+};
+
+static EVP_CIPHER aesni_256_cbc_hmac_sha1_cipher = {
+# ifdef NID_aes_256_cbc_hmac_sha1
+    NID_aes_256_cbc_hmac_sha1,
+# else
+    NID_undef,
+# endif
+    AES_BLOCK_SIZE, 32, AES_BLOCK_SIZE,
+    EVP_CIPH_CBC_MODE | EVP_CIPH_FLAG_DEFAULT_ASN1 |
+        EVP_CIPH_FLAG_AEAD_CIPHER | EVP_CIPH_FLAG_TLS1_1_MULTIBLOCK,
+    aesni_cbc_hmac_sha1_init_key,
+    aesni_cbc_hmac_sha1_cipher,
+    NULL,
+    sizeof(EVP_AES_HMAC_SHA1),
+    EVP_CIPH_FLAG_DEFAULT_ASN1 ? NULL : EVP_CIPHER_set_asn1_iv,
+    EVP_CIPH_FLAG_DEFAULT_ASN1 ? NULL : EVP_CIPHER_get_asn1_iv,
+    aesni_cbc_hmac_sha1_ctrl,
+    NULL
+};
+
+const EVP_CIPHER *EVP_aes_128_cbc_hmac_sha1(void)
+{
+    return (OPENSSL_ia32cap_P[1] & AESNI_CAPABLE ?
+            &aesni_128_cbc_hmac_sha1_cipher : NULL);
+}
+
+const EVP_CIPHER *EVP_aes_256_cbc_hmac_sha1(void)
+{
+    return (OPENSSL_ia32cap_P[1] & AESNI_CAPABLE ?
+            &aesni_256_cbc_hmac_sha1_cipher : NULL);
+}
+#else
+const EVP_CIPHER *EVP_aes_128_cbc_hmac_sha1(void)
+{
+    return NULL;
+}
+
+const EVP_CIPHER *EVP_aes_256_cbc_hmac_sha1(void)
+{
+    return NULL;
+}
+#endif
+#endif
diff --git a/crypto/evp/e_aes_cbc_hmac_sha256.c b/crypto/evp/e_aes_cbc_hmac_sha256.c
new file mode 100644
index 000000000..a1c3be546
--- /dev/null
+++ b/crypto/evp/e_aes_cbc_hmac_sha256.c
@@ -0,0 +1,943 @@
+/*
+ * Copyright 2013-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#if defined(OPENSSL_NO_AES) || defined(OPENSSL_NO_SHA)
+NON_EMPTY_TRANSLATION_UNIT
+#else
+
+#include 
+#include 
+
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include "modes_lcl.h"
+#include "internal/constant_time_locl.h"
+#include "internal/evp_int.h"
+
+typedef struct {
+    AES_KEY ks;
+    SHA256_CTX head, tail, md;
+    size_t payload_length;      /* AAD length in decrypt case */
+    union {
+        unsigned int tls_ver;
+        unsigned char tls_aad[16]; /* 13 used */
+    } aux;
+} EVP_AES_HMAC_SHA256;
+
+# define NO_PAYLOAD_LENGTH       ((size_t)-1)
+
+#if     defined(AES_ASM) &&     ( \
+        defined(__x86_64)       || defined(__x86_64__)  || \
+        defined(_M_AMD64)       || defined(_M_X64)      )
+
+extern unsigned int OPENSSL_ia32cap_P[];
+# define AESNI_CAPABLE   (1<<(57-32))
+
+int aesni_set_encrypt_key(const unsigned char *userKey, int bits,
+                          AES_KEY *key);
+int aesni_set_decrypt_key(const unsigned char *userKey, int bits,
+                          AES_KEY *key);
+
+void aesni_cbc_encrypt(const unsigned char *in,
+                       unsigned char *out,
+                       size_t length,
+                       const AES_KEY *key, unsigned char *ivec, int enc);
+
+int aesni_cbc_sha256_enc(const void *inp, void *out, size_t blocks,
+                         const AES_KEY *key, unsigned char iv[16],
+                         SHA256_CTX *ctx, const void *in0);
+
+# define data(ctx) ((EVP_AES_HMAC_SHA256 *)EVP_CIPHER_CTX_get_cipher_data(ctx))
+
+static int aesni_cbc_hmac_sha256_init_key(EVP_CIPHER_CTX *ctx,
+                                          const unsigned char *inkey,
+                                          const unsigned char *iv, int enc)
+{
+    EVP_AES_HMAC_SHA256 *key = data(ctx);
+    int ret;
+
+    if (enc)
+        ret = aesni_set_encrypt_key(inkey,
+                                    EVP_CIPHER_CTX_key_length(ctx) * 8,
+                                    &key->ks);
+    else
+        ret = aesni_set_decrypt_key(inkey,
+                                    EVP_CIPHER_CTX_key_length(ctx) * 8,
+                                    &key->ks);
+
+    SHA256_Init(&key->head);    /* handy when benchmarking */
+    key->tail = key->head;
+    key->md = key->head;
+
+    key->payload_length = NO_PAYLOAD_LENGTH;
+
+    return ret < 0 ? 0 : 1;
+}
+
+# define STITCHED_CALL
+
+# if !defined(STITCHED_CALL)
+#  define aes_off 0
+# endif
+
+void sha256_block_data_order(void *c, const void *p, size_t len);
+
+static void sha256_update(SHA256_CTX *c, const void *data, size_t len)
+{
+    const unsigned char *ptr = data;
+    size_t res;
+
+    if ((res = c->num)) {
+        res = SHA256_CBLOCK - res;
+        if (len < res)
+            res = len;
+        SHA256_Update(c, ptr, res);
+        ptr += res;
+        len -= res;
+    }
+
+    res = len % SHA256_CBLOCK;
+    len -= res;
+
+    if (len) {
+        sha256_block_data_order(c, ptr, len / SHA256_CBLOCK);
+
+        ptr += len;
+        c->Nh += len >> 29;
+        c->Nl += len <<= 3;
+        if (c->Nl < (unsigned int)len)
+            c->Nh++;
+    }
+
+    if (res)
+        SHA256_Update(c, ptr, res);
+}
+
+# ifdef SHA256_Update
+#  undef SHA256_Update
+# endif
+# define SHA256_Update sha256_update
+
+# if !defined(OPENSSL_NO_MULTIBLOCK)
+
+typedef struct {
+    unsigned int A[8], B[8], C[8], D[8], E[8], F[8], G[8], H[8];
+} SHA256_MB_CTX;
+typedef struct {
+    const unsigned char *ptr;
+    int blocks;
+} HASH_DESC;
+
+void sha256_multi_block(SHA256_MB_CTX *, const HASH_DESC *, int);
+
+typedef struct {
+    const unsigned char *inp;
+    unsigned char *out;
+    int blocks;
+    u64 iv[2];
+} CIPH_DESC;
+
+void aesni_multi_cbc_encrypt(CIPH_DESC *, void *, int);
+
+static size_t tls1_1_multi_block_encrypt(EVP_AES_HMAC_SHA256 *key,
+                                         unsigned char *out,
+                                         const unsigned char *inp,
+                                         size_t inp_len, int n4x)
+{                               /* n4x is 1 or 2 */
+    HASH_DESC hash_d[8], edges[8];
+    CIPH_DESC ciph_d[8];
+    unsigned char storage[sizeof(SHA256_MB_CTX) + 32];
+    union {
+        u64 q[16];
+        u32 d[32];
+        u8 c[128];
+    } blocks[8];
+    SHA256_MB_CTX *ctx;
+    unsigned int frag, last, packlen, i, x4 = 4 * n4x, minblocks, processed =
+        0;
+    size_t ret = 0;
+    u8 *IVs;
+#  if defined(BSWAP8)
+    u64 seqnum;
+#  endif
+
+    /* ask for IVs in bulk */
+    if (RAND_bytes((IVs = blocks[0].c), 16 * x4) <= 0)
+        return 0;
+
+    /* align */
+    ctx = (SHA256_MB_CTX *) (storage + 32 - ((size_t)storage % 32));
+
+    frag = (unsigned int)inp_len >> (1 + n4x);
+    last = (unsigned int)inp_len + frag - (frag << (1 + n4x));
+    if (last > frag && ((last + 13 + 9) % 64) < (x4 - 1)) {
+        frag++;
+        last -= x4 - 1;
+    }
+
+    packlen = 5 + 16 + ((frag + 32 + 16) & -16);
+
+    /* populate descriptors with pointers and IVs */
+    hash_d[0].ptr = inp;
+    ciph_d[0].inp = inp;
+    /* 5+16 is place for header and explicit IV */
+    ciph_d[0].out = out + 5 + 16;
+    memcpy(ciph_d[0].out - 16, IVs, 16);
+    memcpy(ciph_d[0].iv, IVs, 16);
+    IVs += 16;
+
+    for (i = 1; i < x4; i++) {
+        ciph_d[i].inp = hash_d[i].ptr = hash_d[i - 1].ptr + frag;
+        ciph_d[i].out = ciph_d[i - 1].out + packlen;
+        memcpy(ciph_d[i].out - 16, IVs, 16);
+        memcpy(ciph_d[i].iv, IVs, 16);
+        IVs += 16;
+    }
+
+#  if defined(BSWAP8)
+    memcpy(blocks[0].c, key->md.data, 8);
+    seqnum = BSWAP8(blocks[0].q[0]);
+#  endif
+    for (i = 0; i < x4; i++) {
+        unsigned int len = (i == (x4 - 1) ? last : frag);
+#  if !defined(BSWAP8)
+        unsigned int carry, j;
+#  endif
+
+        ctx->A[i] = key->md.h[0];
+        ctx->B[i] = key->md.h[1];
+        ctx->C[i] = key->md.h[2];
+        ctx->D[i] = key->md.h[3];
+        ctx->E[i] = key->md.h[4];
+        ctx->F[i] = key->md.h[5];
+        ctx->G[i] = key->md.h[6];
+        ctx->H[i] = key->md.h[7];
+
+        /* fix seqnum */
+#  if defined(BSWAP8)
+        blocks[i].q[0] = BSWAP8(seqnum + i);
+#  else
+        for (carry = i, j = 8; j--;) {
+            blocks[i].c[j] = ((u8 *)key->md.data)[j] + carry;
+            carry = (blocks[i].c[j] - carry) >> (sizeof(carry) * 8 - 1);
+        }
+#  endif
+        blocks[i].c[8] = ((u8 *)key->md.data)[8];
+        blocks[i].c[9] = ((u8 *)key->md.data)[9];
+        blocks[i].c[10] = ((u8 *)key->md.data)[10];
+        /* fix length */
+        blocks[i].c[11] = (u8)(len >> 8);
+        blocks[i].c[12] = (u8)(len);
+
+        memcpy(blocks[i].c + 13, hash_d[i].ptr, 64 - 13);
+        hash_d[i].ptr += 64 - 13;
+        hash_d[i].blocks = (len - (64 - 13)) / 64;
+
+        edges[i].ptr = blocks[i].c;
+        edges[i].blocks = 1;
+    }
+
+    /* hash 13-byte headers and first 64-13 bytes of inputs */
+    sha256_multi_block(ctx, edges, n4x);
+    /* hash bulk inputs */
+#  define MAXCHUNKSIZE    2048
+#  if     MAXCHUNKSIZE%64
+#   error  "MAXCHUNKSIZE is not divisible by 64"
+#  elif   MAXCHUNKSIZE
+    /*
+     * goal is to minimize pressure on L1 cache by moving in shorter steps,
+     * so that hashed data is still in the cache by the time we encrypt it
+     */
+    minblocks = ((frag <= last ? frag : last) - (64 - 13)) / 64;
+    if (minblocks > MAXCHUNKSIZE / 64) {
+        for (i = 0; i < x4; i++) {
+            edges[i].ptr = hash_d[i].ptr;
+            edges[i].blocks = MAXCHUNKSIZE / 64;
+            ciph_d[i].blocks = MAXCHUNKSIZE / 16;
+        }
+        do {
+            sha256_multi_block(ctx, edges, n4x);
+            aesni_multi_cbc_encrypt(ciph_d, &key->ks, n4x);
+
+            for (i = 0; i < x4; i++) {
+                edges[i].ptr = hash_d[i].ptr += MAXCHUNKSIZE;
+                hash_d[i].blocks -= MAXCHUNKSIZE / 64;
+                edges[i].blocks = MAXCHUNKSIZE / 64;
+                ciph_d[i].inp += MAXCHUNKSIZE;
+                ciph_d[i].out += MAXCHUNKSIZE;
+                ciph_d[i].blocks = MAXCHUNKSIZE / 16;
+                memcpy(ciph_d[i].iv, ciph_d[i].out - 16, 16);
+            }
+            processed += MAXCHUNKSIZE;
+            minblocks -= MAXCHUNKSIZE / 64;
+        } while (minblocks > MAXCHUNKSIZE / 64);
+    }
+#  endif
+#  undef  MAXCHUNKSIZE
+    sha256_multi_block(ctx, hash_d, n4x);
+
+    memset(blocks, 0, sizeof(blocks));
+    for (i = 0; i < x4; i++) {
+        unsigned int len = (i == (x4 - 1) ? last : frag),
+            off = hash_d[i].blocks * 64;
+        const unsigned char *ptr = hash_d[i].ptr + off;
+
+        off = (len - processed) - (64 - 13) - off; /* remainder actually */
+        memcpy(blocks[i].c, ptr, off);
+        blocks[i].c[off] = 0x80;
+        len += 64 + 13;         /* 64 is HMAC header */
+        len *= 8;               /* convert to bits */
+        if (off < (64 - 8)) {
+#  ifdef BSWAP4
+            blocks[i].d[15] = BSWAP4(len);
+#  else
+            PUTU32(blocks[i].c + 60, len);
+#  endif
+            edges[i].blocks = 1;
+        } else {
+#  ifdef BSWAP4
+            blocks[i].d[31] = BSWAP4(len);
+#  else
+            PUTU32(blocks[i].c + 124, len);
+#  endif
+            edges[i].blocks = 2;
+        }
+        edges[i].ptr = blocks[i].c;
+    }
+
+    /* hash input tails and finalize */
+    sha256_multi_block(ctx, edges, n4x);
+
+    memset(blocks, 0, sizeof(blocks));
+    for (i = 0; i < x4; i++) {
+#  ifdef BSWAP4
+        blocks[i].d[0] = BSWAP4(ctx->A[i]);
+        ctx->A[i] = key->tail.h[0];
+        blocks[i].d[1] = BSWAP4(ctx->B[i]);
+        ctx->B[i] = key->tail.h[1];
+        blocks[i].d[2] = BSWAP4(ctx->C[i]);
+        ctx->C[i] = key->tail.h[2];
+        blocks[i].d[3] = BSWAP4(ctx->D[i]);
+        ctx->D[i] = key->tail.h[3];
+        blocks[i].d[4] = BSWAP4(ctx->E[i]);
+        ctx->E[i] = key->tail.h[4];
+        blocks[i].d[5] = BSWAP4(ctx->F[i]);
+        ctx->F[i] = key->tail.h[5];
+        blocks[i].d[6] = BSWAP4(ctx->G[i]);
+        ctx->G[i] = key->tail.h[6];
+        blocks[i].d[7] = BSWAP4(ctx->H[i]);
+        ctx->H[i] = key->tail.h[7];
+        blocks[i].c[32] = 0x80;
+        blocks[i].d[15] = BSWAP4((64 + 32) * 8);
+#  else
+        PUTU32(blocks[i].c + 0, ctx->A[i]);
+        ctx->A[i] = key->tail.h[0];
+        PUTU32(blocks[i].c + 4, ctx->B[i]);
+        ctx->B[i] = key->tail.h[1];
+        PUTU32(blocks[i].c + 8, ctx->C[i]);
+        ctx->C[i] = key->tail.h[2];
+        PUTU32(blocks[i].c + 12, ctx->D[i]);
+        ctx->D[i] = key->tail.h[3];
+        PUTU32(blocks[i].c + 16, ctx->E[i]);
+        ctx->E[i] = key->tail.h[4];
+        PUTU32(blocks[i].c + 20, ctx->F[i]);
+        ctx->F[i] = key->tail.h[5];
+        PUTU32(blocks[i].c + 24, ctx->G[i]);
+        ctx->G[i] = key->tail.h[6];
+        PUTU32(blocks[i].c + 28, ctx->H[i]);
+        ctx->H[i] = key->tail.h[7];
+        blocks[i].c[32] = 0x80;
+        PUTU32(blocks[i].c + 60, (64 + 32) * 8);
+#  endif
+        edges[i].ptr = blocks[i].c;
+        edges[i].blocks = 1;
+    }
+
+    /* finalize MACs */
+    sha256_multi_block(ctx, edges, n4x);
+
+    for (i = 0; i < x4; i++) {
+        unsigned int len = (i == (x4 - 1) ? last : frag), pad, j;
+        unsigned char *out0 = out;
+
+        memcpy(ciph_d[i].out, ciph_d[i].inp, len - processed);
+        ciph_d[i].inp = ciph_d[i].out;
+
+        out += 5 + 16 + len;
+
+        /* write MAC */
+        PUTU32(out + 0, ctx->A[i]);
+        PUTU32(out + 4, ctx->B[i]);
+        PUTU32(out + 8, ctx->C[i]);
+        PUTU32(out + 12, ctx->D[i]);
+        PUTU32(out + 16, ctx->E[i]);
+        PUTU32(out + 20, ctx->F[i]);
+        PUTU32(out + 24, ctx->G[i]);
+        PUTU32(out + 28, ctx->H[i]);
+        out += 32;
+        len += 32;
+
+        /* pad */
+        pad = 15 - len % 16;
+        for (j = 0; j <= pad; j++)
+            *(out++) = pad;
+        len += pad + 1;
+
+        ciph_d[i].blocks = (len - processed) / 16;
+        len += 16;              /* account for explicit iv */
+
+        /* arrange header */
+        out0[0] = ((u8 *)key->md.data)[8];
+        out0[1] = ((u8 *)key->md.data)[9];
+        out0[2] = ((u8 *)key->md.data)[10];
+        out0[3] = (u8)(len >> 8);
+        out0[4] = (u8)(len);
+
+        ret += len + 5;
+        inp += frag;
+    }
+
+    aesni_multi_cbc_encrypt(ciph_d, &key->ks, n4x);
+
+    OPENSSL_cleanse(blocks, sizeof(blocks));
+    OPENSSL_cleanse(ctx, sizeof(*ctx));
+
+    return ret;
+}
+# endif
+
+static int aesni_cbc_hmac_sha256_cipher(EVP_CIPHER_CTX *ctx,
+                                        unsigned char *out,
+                                        const unsigned char *in, size_t len)
+{
+    EVP_AES_HMAC_SHA256 *key = data(ctx);
+    unsigned int l;
+    size_t plen = key->payload_length, iv = 0, /* explicit IV in TLS 1.1 and
+                                                * later */
+        sha_off = 0;
+# if defined(STITCHED_CALL)
+    size_t aes_off = 0, blocks;
+
+    sha_off = SHA256_CBLOCK - key->md.num;
+# endif
+
+    key->payload_length = NO_PAYLOAD_LENGTH;
+
+    if (len % AES_BLOCK_SIZE)
+        return 0;
+
+    if (EVP_CIPHER_CTX_encrypting(ctx)) {
+        if (plen == NO_PAYLOAD_LENGTH)
+            plen = len;
+        else if (len !=
+                 ((plen + SHA256_DIGEST_LENGTH +
+                   AES_BLOCK_SIZE) & -AES_BLOCK_SIZE))
+            return 0;
+        else if (key->aux.tls_ver >= TLS1_1_VERSION)
+            iv = AES_BLOCK_SIZE;
+
+# if defined(STITCHED_CALL)
+        /*
+         * Assembly stitch handles AVX-capable processors, but its
+         * performance is not optimal on AMD Jaguar, ~40% worse, for
+         * unknown reasons. Incidentally processor in question supports
+         * AVX, but not AMD-specific XOP extension, which can be used
+         * to identify it and avoid stitch invocation. So that after we
+         * establish that current CPU supports AVX, we even see if it's
+         * either even XOP-capable Bulldozer-based or GenuineIntel one.
+         */
+        if (OPENSSL_ia32cap_P[1] & (1 << (60 - 32)) && /* AVX? */
+            ((OPENSSL_ia32cap_P[1] & (1 << (43 - 32))) /* XOP? */
+             | (OPENSSL_ia32cap_P[0] & (1<<30))) &&    /* "Intel CPU"? */
+            plen > (sha_off + iv) &&
+            (blocks = (plen - (sha_off + iv)) / SHA256_CBLOCK)) {
+            SHA256_Update(&key->md, in + iv, sha_off);
+
+            (void)aesni_cbc_sha256_enc(in, out, blocks, &key->ks,
+                                       EVP_CIPHER_CTX_iv_noconst(ctx),
+                                       &key->md, in + iv + sha_off);
+            blocks *= SHA256_CBLOCK;
+            aes_off += blocks;
+            sha_off += blocks;
+            key->md.Nh += blocks >> 29;
+            key->md.Nl += blocks <<= 3;
+            if (key->md.Nl < (unsigned int)blocks)
+                key->md.Nh++;
+        } else {
+            sha_off = 0;
+        }
+# endif
+        sha_off += iv;
+        SHA256_Update(&key->md, in + sha_off, plen - sha_off);
+
+        if (plen != len) {      /* "TLS" mode of operation */
+            if (in != out)
+                memcpy(out + aes_off, in + aes_off, plen - aes_off);
+
+            /* calculate HMAC and append it to payload */
+            SHA256_Final(out + plen, &key->md);
+            key->md = key->tail;
+            SHA256_Update(&key->md, out + plen, SHA256_DIGEST_LENGTH);
+            SHA256_Final(out + plen, &key->md);
+
+            /* pad the payload|hmac */
+            plen += SHA256_DIGEST_LENGTH;
+            for (l = len - plen - 1; plen < len; plen++)
+                out[plen] = l;
+            /* encrypt HMAC|padding at once */
+            aesni_cbc_encrypt(out + aes_off, out + aes_off, len - aes_off,
+                              &key->ks, EVP_CIPHER_CTX_iv_noconst(ctx), 1);
+        } else {
+            aesni_cbc_encrypt(in + aes_off, out + aes_off, len - aes_off,
+                              &key->ks, EVP_CIPHER_CTX_iv_noconst(ctx), 1);
+        }
+    } else {
+        union {
+            unsigned int u[SHA256_DIGEST_LENGTH / sizeof(unsigned int)];
+            unsigned char c[64 + SHA256_DIGEST_LENGTH];
+        } mac, *pmac;
+
+        /* arrange cache line alignment */
+        pmac = (void *)(((size_t)mac.c + 63) & ((size_t)0 - 64));
+
+        /* decrypt HMAC|padding at once */
+        aesni_cbc_encrypt(in, out, len, &key->ks,
+                          EVP_CIPHER_CTX_iv_noconst(ctx), 0);
+
+        if (plen != NO_PAYLOAD_LENGTH) { /* "TLS" mode of operation */
+            size_t inp_len, mask, j, i;
+            unsigned int res, maxpad, pad, bitlen;
+            int ret = 1;
+            union {
+                unsigned int u[SHA_LBLOCK];
+                unsigned char c[SHA256_CBLOCK];
+            } *data = (void *)key->md.data;
+
+            if ((key->aux.tls_aad[plen - 4] << 8 | key->aux.tls_aad[plen - 3])
+                >= TLS1_1_VERSION)
+                iv = AES_BLOCK_SIZE;
+
+            if (len < (iv + SHA256_DIGEST_LENGTH + 1))
+                return 0;
+
+            /* omit explicit iv */
+            out += iv;
+            len -= iv;
+
+            /* figure out payload length */
+            pad = out[len - 1];
+            maxpad = len - (SHA256_DIGEST_LENGTH + 1);
+            maxpad |= (255 - maxpad) >> (sizeof(maxpad) * 8 - 8);
+            maxpad &= 255;
+
+            ret &= constant_time_ge(maxpad, pad);
+
+            inp_len = len - (SHA256_DIGEST_LENGTH + pad + 1);
+            mask = (0 - ((inp_len - len) >> (sizeof(inp_len) * 8 - 1)));
+            inp_len &= mask;
+            ret &= (int)mask;
+
+            key->aux.tls_aad[plen - 2] = inp_len >> 8;
+            key->aux.tls_aad[plen - 1] = inp_len;
+
+            /* calculate HMAC */
+            key->md = key->head;
+            SHA256_Update(&key->md, key->aux.tls_aad, plen);
+
+# if 1
+            len -= SHA256_DIGEST_LENGTH; /* amend mac */
+            if (len >= (256 + SHA256_CBLOCK)) {
+                j = (len - (256 + SHA256_CBLOCK)) & (0 - SHA256_CBLOCK);
+                j += SHA256_CBLOCK - key->md.num;
+                SHA256_Update(&key->md, out, j);
+                out += j;
+                len -= j;
+                inp_len -= j;
+            }
+
+            /* but pretend as if we hashed padded payload */
+            bitlen = key->md.Nl + (inp_len << 3); /* at most 18 bits */
+#  ifdef BSWAP4
+            bitlen = BSWAP4(bitlen);
+#  else
+            mac.c[0] = 0;
+            mac.c[1] = (unsigned char)(bitlen >> 16);
+            mac.c[2] = (unsigned char)(bitlen >> 8);
+            mac.c[3] = (unsigned char)bitlen;
+            bitlen = mac.u[0];
+#  endif
+
+            pmac->u[0] = 0;
+            pmac->u[1] = 0;
+            pmac->u[2] = 0;
+            pmac->u[3] = 0;
+            pmac->u[4] = 0;
+            pmac->u[5] = 0;
+            pmac->u[6] = 0;
+            pmac->u[7] = 0;
+
+            for (res = key->md.num, j = 0; j < len; j++) {
+                size_t c = out[j];
+                mask = (j - inp_len) >> (sizeof(j) * 8 - 8);
+                c &= mask;
+                c |= 0x80 & ~mask & ~((inp_len - j) >> (sizeof(j) * 8 - 8));
+                data->c[res++] = (unsigned char)c;
+
+                if (res != SHA256_CBLOCK)
+                    continue;
+
+                /* j is not incremented yet */
+                mask = 0 - ((inp_len + 7 - j) >> (sizeof(j) * 8 - 1));
+                data->u[SHA_LBLOCK - 1] |= bitlen & mask;
+                sha256_block_data_order(&key->md, data, 1);
+                mask &= 0 - ((j - inp_len - 72) >> (sizeof(j) * 8 - 1));
+                pmac->u[0] |= key->md.h[0] & mask;
+                pmac->u[1] |= key->md.h[1] & mask;
+                pmac->u[2] |= key->md.h[2] & mask;
+                pmac->u[3] |= key->md.h[3] & mask;
+                pmac->u[4] |= key->md.h[4] & mask;
+                pmac->u[5] |= key->md.h[5] & mask;
+                pmac->u[6] |= key->md.h[6] & mask;
+                pmac->u[7] |= key->md.h[7] & mask;
+                res = 0;
+            }
+
+            for (i = res; i < SHA256_CBLOCK; i++, j++)
+                data->c[i] = 0;
+
+            if (res > SHA256_CBLOCK - 8) {
+                mask = 0 - ((inp_len + 8 - j) >> (sizeof(j) * 8 - 1));
+                data->u[SHA_LBLOCK - 1] |= bitlen & mask;
+                sha256_block_data_order(&key->md, data, 1);
+                mask &= 0 - ((j - inp_len - 73) >> (sizeof(j) * 8 - 1));
+                pmac->u[0] |= key->md.h[0] & mask;
+                pmac->u[1] |= key->md.h[1] & mask;
+                pmac->u[2] |= key->md.h[2] & mask;
+                pmac->u[3] |= key->md.h[3] & mask;
+                pmac->u[4] |= key->md.h[4] & mask;
+                pmac->u[5] |= key->md.h[5] & mask;
+                pmac->u[6] |= key->md.h[6] & mask;
+                pmac->u[7] |= key->md.h[7] & mask;
+
+                memset(data, 0, SHA256_CBLOCK);
+                j += 64;
+            }
+            data->u[SHA_LBLOCK - 1] = bitlen;
+            sha256_block_data_order(&key->md, data, 1);
+            mask = 0 - ((j - inp_len - 73) >> (sizeof(j) * 8 - 1));
+            pmac->u[0] |= key->md.h[0] & mask;
+            pmac->u[1] |= key->md.h[1] & mask;
+            pmac->u[2] |= key->md.h[2] & mask;
+            pmac->u[3] |= key->md.h[3] & mask;
+            pmac->u[4] |= key->md.h[4] & mask;
+            pmac->u[5] |= key->md.h[5] & mask;
+            pmac->u[6] |= key->md.h[6] & mask;
+            pmac->u[7] |= key->md.h[7] & mask;
+
+#  ifdef BSWAP4
+            pmac->u[0] = BSWAP4(pmac->u[0]);
+            pmac->u[1] = BSWAP4(pmac->u[1]);
+            pmac->u[2] = BSWAP4(pmac->u[2]);
+            pmac->u[3] = BSWAP4(pmac->u[3]);
+            pmac->u[4] = BSWAP4(pmac->u[4]);
+            pmac->u[5] = BSWAP4(pmac->u[5]);
+            pmac->u[6] = BSWAP4(pmac->u[6]);
+            pmac->u[7] = BSWAP4(pmac->u[7]);
+#  else
+            for (i = 0; i < 8; i++) {
+                res = pmac->u[i];
+                pmac->c[4 * i + 0] = (unsigned char)(res >> 24);
+                pmac->c[4 * i + 1] = (unsigned char)(res >> 16);
+                pmac->c[4 * i + 2] = (unsigned char)(res >> 8);
+                pmac->c[4 * i + 3] = (unsigned char)res;
+            }
+#  endif
+            len += SHA256_DIGEST_LENGTH;
+# else
+            SHA256_Update(&key->md, out, inp_len);
+            res = key->md.num;
+            SHA256_Final(pmac->c, &key->md);
+
+            {
+                unsigned int inp_blocks, pad_blocks;
+
+                /* but pretend as if we hashed padded payload */
+                inp_blocks =
+                    1 + ((SHA256_CBLOCK - 9 - res) >> (sizeof(res) * 8 - 1));
+                res += (unsigned int)(len - inp_len);
+                pad_blocks = res / SHA256_CBLOCK;
+                res %= SHA256_CBLOCK;
+                pad_blocks +=
+                    1 + ((SHA256_CBLOCK - 9 - res) >> (sizeof(res) * 8 - 1));
+                for (; inp_blocks < pad_blocks; inp_blocks++)
+                    sha1_block_data_order(&key->md, data, 1);
+            }
+# endif
+            key->md = key->tail;
+            SHA256_Update(&key->md, pmac->c, SHA256_DIGEST_LENGTH);
+            SHA256_Final(pmac->c, &key->md);
+
+            /* verify HMAC */
+            out += inp_len;
+            len -= inp_len;
+# if 1
+            {
+                unsigned char *p =
+                    out + len - 1 - maxpad - SHA256_DIGEST_LENGTH;
+                size_t off = out - p;
+                unsigned int c, cmask;
+
+                maxpad += SHA256_DIGEST_LENGTH;
+                for (res = 0, i = 0, j = 0; j < maxpad; j++) {
+                    c = p[j];
+                    cmask =
+                        ((int)(j - off - SHA256_DIGEST_LENGTH)) >>
+                        (sizeof(int) * 8 - 1);
+                    res |= (c ^ pad) & ~cmask; /* ... and padding */
+                    cmask &= ((int)(off - 1 - j)) >> (sizeof(int) * 8 - 1);
+                    res |= (c ^ pmac->c[i]) & cmask;
+                    i += 1 & cmask;
+                }
+                maxpad -= SHA256_DIGEST_LENGTH;
+
+                res = 0 - ((0 - res) >> (sizeof(res) * 8 - 1));
+                ret &= (int)~res;
+            }
+# else
+            for (res = 0, i = 0; i < SHA256_DIGEST_LENGTH; i++)
+                res |= out[i] ^ pmac->c[i];
+            res = 0 - ((0 - res) >> (sizeof(res) * 8 - 1));
+            ret &= (int)~res;
+
+            /* verify padding */
+            pad = (pad & ~res) | (maxpad & res);
+            out = out + len - 1 - pad;
+            for (res = 0, i = 0; i < pad; i++)
+                res |= out[i] ^ pad;
+
+            res = (0 - res) >> (sizeof(res) * 8 - 1);
+            ret &= (int)~res;
+# endif
+            return ret;
+        } else {
+            SHA256_Update(&key->md, out, len);
+        }
+    }
+
+    return 1;
+}
+
+static int aesni_cbc_hmac_sha256_ctrl(EVP_CIPHER_CTX *ctx, int type, int arg,
+                                      void *ptr)
+{
+    EVP_AES_HMAC_SHA256 *key = data(ctx);
+    unsigned int u_arg = (unsigned int)arg;
+
+    switch (type) {
+    case EVP_CTRL_AEAD_SET_MAC_KEY:
+        {
+            unsigned int i;
+            unsigned char hmac_key[64];
+
+            memset(hmac_key, 0, sizeof(hmac_key));
+
+            if (arg < 0)
+                return -1;
+
+            if (u_arg > sizeof(hmac_key)) {
+                SHA256_Init(&key->head);
+                SHA256_Update(&key->head, ptr, arg);
+                SHA256_Final(hmac_key, &key->head);
+            } else {
+                memcpy(hmac_key, ptr, arg);
+            }
+
+            for (i = 0; i < sizeof(hmac_key); i++)
+                hmac_key[i] ^= 0x36; /* ipad */
+            SHA256_Init(&key->head);
+            SHA256_Update(&key->head, hmac_key, sizeof(hmac_key));
+
+            for (i = 0; i < sizeof(hmac_key); i++)
+                hmac_key[i] ^= 0x36 ^ 0x5c; /* opad */
+            SHA256_Init(&key->tail);
+            SHA256_Update(&key->tail, hmac_key, sizeof(hmac_key));
+
+            OPENSSL_cleanse(hmac_key, sizeof(hmac_key));
+
+            return 1;
+        }
+    case EVP_CTRL_AEAD_TLS1_AAD:
+        {
+            unsigned char *p = ptr;
+            unsigned int len = p[arg - 2] << 8 | p[arg - 1];
+
+            if (arg != EVP_AEAD_TLS1_AAD_LEN)
+                return -1;
+
+            if (EVP_CIPHER_CTX_encrypting(ctx)) {
+                key->payload_length = len;
+                if ((key->aux.tls_ver =
+                     p[arg - 4] << 8 | p[arg - 3]) >= TLS1_1_VERSION) {
+                    len -= AES_BLOCK_SIZE;
+                    p[arg - 2] = len >> 8;
+                    p[arg - 1] = len;
+                }
+                key->md = key->head;
+                SHA256_Update(&key->md, p, arg);
+
+                return (int)(((len + SHA256_DIGEST_LENGTH +
+                               AES_BLOCK_SIZE) & -AES_BLOCK_SIZE)
+                             - len);
+            } else {
+                memcpy(key->aux.tls_aad, ptr, arg);
+                key->payload_length = arg;
+
+                return SHA256_DIGEST_LENGTH;
+            }
+        }
+# if !defined(OPENSSL_NO_MULTIBLOCK)
+    case EVP_CTRL_TLS1_1_MULTIBLOCK_MAX_BUFSIZE:
+        return (int)(5 + 16 + ((arg + 32 + 16) & -16));
+    case EVP_CTRL_TLS1_1_MULTIBLOCK_AAD:
+        {
+            EVP_CTRL_TLS1_1_MULTIBLOCK_PARAM *param =
+                (EVP_CTRL_TLS1_1_MULTIBLOCK_PARAM *) ptr;
+            unsigned int n4x = 1, x4;
+            unsigned int frag, last, packlen, inp_len;
+
+            if (arg < 0)
+                return -1;
+
+            if (u_arg < sizeof(EVP_CTRL_TLS1_1_MULTIBLOCK_PARAM))
+                return -1;
+
+            inp_len = param->inp[11] << 8 | param->inp[12];
+
+            if (EVP_CIPHER_CTX_encrypting(ctx)) {
+                if ((param->inp[9] << 8 | param->inp[10]) < TLS1_1_VERSION)
+                    return -1;
+
+                if (inp_len) {
+                    if (inp_len < 4096)
+                        return 0; /* too short */
+
+                    if (inp_len >= 8192 && OPENSSL_ia32cap_P[2] & (1 << 5))
+                        n4x = 2; /* AVX2 */
+                } else if ((n4x = param->interleave / 4) && n4x <= 2)
+                    inp_len = param->len;
+                else
+                    return -1;
+
+                key->md = key->head;
+                SHA256_Update(&key->md, param->inp, 13);
+
+                x4 = 4 * n4x;
+                n4x += 1;
+
+                frag = inp_len >> n4x;
+                last = inp_len + frag - (frag << n4x);
+                if (last > frag && ((last + 13 + 9) % 64 < (x4 - 1))) {
+                    frag++;
+                    last -= x4 - 1;
+                }
+
+                packlen = 5 + 16 + ((frag + 32 + 16) & -16);
+                packlen = (packlen << n4x) - packlen;
+                packlen += 5 + 16 + ((last + 32 + 16) & -16);
+
+                param->interleave = x4;
+
+                return (int)packlen;
+            } else
+                return -1;      /* not yet */
+        }
+    case EVP_CTRL_TLS1_1_MULTIBLOCK_ENCRYPT:
+        {
+            EVP_CTRL_TLS1_1_MULTIBLOCK_PARAM *param =
+                (EVP_CTRL_TLS1_1_MULTIBLOCK_PARAM *) ptr;
+
+            return (int)tls1_1_multi_block_encrypt(key, param->out,
+                                                   param->inp, param->len,
+                                                   param->interleave / 4);
+        }
+    case EVP_CTRL_TLS1_1_MULTIBLOCK_DECRYPT:
+# endif
+    default:
+        return -1;
+    }
+}
+
+static EVP_CIPHER aesni_128_cbc_hmac_sha256_cipher = {
+# ifdef NID_aes_128_cbc_hmac_sha256
+    NID_aes_128_cbc_hmac_sha256,
+# else
+    NID_undef,
+# endif
+    AES_BLOCK_SIZE, 16, AES_BLOCK_SIZE,
+    EVP_CIPH_CBC_MODE | EVP_CIPH_FLAG_DEFAULT_ASN1 |
+        EVP_CIPH_FLAG_AEAD_CIPHER | EVP_CIPH_FLAG_TLS1_1_MULTIBLOCK,
+    aesni_cbc_hmac_sha256_init_key,
+    aesni_cbc_hmac_sha256_cipher,
+    NULL,
+    sizeof(EVP_AES_HMAC_SHA256),
+    EVP_CIPH_FLAG_DEFAULT_ASN1 ? NULL : EVP_CIPHER_set_asn1_iv,
+    EVP_CIPH_FLAG_DEFAULT_ASN1 ? NULL : EVP_CIPHER_get_asn1_iv,
+    aesni_cbc_hmac_sha256_ctrl,
+    NULL
+};
+
+static EVP_CIPHER aesni_256_cbc_hmac_sha256_cipher = {
+# ifdef NID_aes_256_cbc_hmac_sha256
+    NID_aes_256_cbc_hmac_sha256,
+# else
+    NID_undef,
+# endif
+    AES_BLOCK_SIZE, 32, AES_BLOCK_SIZE,
+    EVP_CIPH_CBC_MODE | EVP_CIPH_FLAG_DEFAULT_ASN1 |
+        EVP_CIPH_FLAG_AEAD_CIPHER | EVP_CIPH_FLAG_TLS1_1_MULTIBLOCK,
+    aesni_cbc_hmac_sha256_init_key,
+    aesni_cbc_hmac_sha256_cipher,
+    NULL,
+    sizeof(EVP_AES_HMAC_SHA256),
+    EVP_CIPH_FLAG_DEFAULT_ASN1 ? NULL : EVP_CIPHER_set_asn1_iv,
+    EVP_CIPH_FLAG_DEFAULT_ASN1 ? NULL : EVP_CIPHER_get_asn1_iv,
+    aesni_cbc_hmac_sha256_ctrl,
+    NULL
+};
+
+const EVP_CIPHER *EVP_aes_128_cbc_hmac_sha256(void)
+{
+    return ((OPENSSL_ia32cap_P[1] & AESNI_CAPABLE) &&
+            aesni_cbc_sha256_enc(NULL, NULL, 0, NULL, NULL, NULL, NULL) ?
+            &aesni_128_cbc_hmac_sha256_cipher : NULL);
+}
+
+const EVP_CIPHER *EVP_aes_256_cbc_hmac_sha256(void)
+{
+    return ((OPENSSL_ia32cap_P[1] & AESNI_CAPABLE) &&
+            aesni_cbc_sha256_enc(NULL, NULL, 0, NULL, NULL, NULL, NULL) ?
+            &aesni_256_cbc_hmac_sha256_cipher : NULL);
+}
+#else
+const EVP_CIPHER *EVP_aes_128_cbc_hmac_sha256(void)
+{
+    return NULL;
+}
+
+const EVP_CIPHER *EVP_aes_256_cbc_hmac_sha256(void)
+{
+    return NULL;
+}
+#endif
+#endif
diff --git a/crypto/evp/e_bf.c b/crypto/evp/e_bf.c
new file mode 100644
index 000000000..dc386905c
--- /dev/null
+++ b/crypto/evp/e_bf.c
@@ -0,0 +1,38 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#ifndef OPENSSL_NO_BF
+# include 
+# include "internal/evp_int.h"
+# include 
+# include 
+
+static int bf_init_key(EVP_CIPHER_CTX *ctx, const unsigned char *key,
+                       const unsigned char *iv, int enc);
+
+typedef struct {
+    BF_KEY ks;
+} EVP_BF_KEY;
+
+# define data(ctx)       EVP_C_DATA(EVP_BF_KEY,ctx)
+
+IMPLEMENT_BLOCK_CIPHER(bf, ks, BF, EVP_BF_KEY, NID_bf, 8, 16, 8, 64,
+                       EVP_CIPH_VARIABLE_LENGTH, bf_init_key, NULL,
+                       EVP_CIPHER_set_asn1_iv, EVP_CIPHER_get_asn1_iv, NULL)
+
+static int bf_init_key(EVP_CIPHER_CTX *ctx, const unsigned char *key,
+                       const unsigned char *iv, int enc)
+{
+    BF_set_key(&data(ctx)->ks, EVP_CIPHER_CTX_key_length(ctx), key);
+    return 1;
+}
+
+#endif
diff --git a/crypto/evp/e_camellia.c b/crypto/evp/e_camellia.c
new file mode 100644
index 000000000..b50fa0b96
--- /dev/null
+++ b/crypto/evp/e_camellia.c
@@ -0,0 +1,364 @@
+/*
+ * Copyright 2006-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#ifdef OPENSSL_NO_CAMELLIA
+NON_EMPTY_TRANSLATION_UNIT
+#else
+
+# include 
+# include 
+# include 
+# include 
+# include 
+# include "internal/evp_int.h"
+# include "modes_lcl.h"
+
+static int camellia_init_key(EVP_CIPHER_CTX *ctx, const unsigned char *key,
+                             const unsigned char *iv, int enc);
+
+/* Camellia subkey Structure */
+typedef struct {
+    CAMELLIA_KEY ks;
+    block128_f block;
+    union {
+        cbc128_f cbc;
+        ctr128_f ctr;
+    } stream;
+} EVP_CAMELLIA_KEY;
+
+# define MAXBITCHUNK     ((size_t)1<<(sizeof(size_t)*8-4))
+
+/* Attribute operation for Camellia */
+# define data(ctx)       EVP_C_DATA(EVP_CAMELLIA_KEY,ctx)
+
+# if defined(AES_ASM) && (defined(__sparc) || defined(__sparc__))
+/* ---------^^^ this is not a typo, just a way to detect that
+ * assembler support was in general requested... */
+#  include "sparc_arch.h"
+
+extern unsigned int OPENSSL_sparcv9cap_P[];
+
+#  define SPARC_CMLL_CAPABLE      (OPENSSL_sparcv9cap_P[1] & CFR_CAMELLIA)
+
+void cmll_t4_set_key(const unsigned char *key, int bits, CAMELLIA_KEY *ks);
+void cmll_t4_encrypt(const unsigned char *in, unsigned char *out,
+                     const CAMELLIA_KEY *key);
+void cmll_t4_decrypt(const unsigned char *in, unsigned char *out,
+                     const CAMELLIA_KEY *key);
+
+void cmll128_t4_cbc_encrypt(const unsigned char *in, unsigned char *out,
+                            size_t len, const CAMELLIA_KEY *key,
+                            unsigned char *ivec);
+void cmll128_t4_cbc_decrypt(const unsigned char *in, unsigned char *out,
+                            size_t len, const CAMELLIA_KEY *key,
+                            unsigned char *ivec);
+void cmll256_t4_cbc_encrypt(const unsigned char *in, unsigned char *out,
+                            size_t len, const CAMELLIA_KEY *key,
+                            unsigned char *ivec);
+void cmll256_t4_cbc_decrypt(const unsigned char *in, unsigned char *out,
+                            size_t len, const CAMELLIA_KEY *key,
+                            unsigned char *ivec);
+void cmll128_t4_ctr32_encrypt(const unsigned char *in, unsigned char *out,
+                              size_t blocks, const CAMELLIA_KEY *key,
+                              unsigned char *ivec);
+void cmll256_t4_ctr32_encrypt(const unsigned char *in, unsigned char *out,
+                              size_t blocks, const CAMELLIA_KEY *key,
+                              unsigned char *ivec);
+
+static int cmll_t4_init_key(EVP_CIPHER_CTX *ctx, const unsigned char *key,
+                            const unsigned char *iv, int enc)
+{
+    int ret, mode, bits;
+    EVP_CAMELLIA_KEY *dat =
+        (EVP_CAMELLIA_KEY *)EVP_CIPHER_CTX_get_cipher_data(ctx);
+
+    mode = EVP_CIPHER_CTX_mode(ctx);
+    bits = EVP_CIPHER_CTX_key_length(ctx) * 8;
+
+    cmll_t4_set_key(key, bits, &dat->ks);
+
+    if ((mode == EVP_CIPH_ECB_MODE || mode == EVP_CIPH_CBC_MODE)
+        && !enc) {
+        ret = 0;
+        dat->block = (block128_f) cmll_t4_decrypt;
+        switch (bits) {
+        case 128:
+            dat->stream.cbc = mode == EVP_CIPH_CBC_MODE ?
+                (cbc128_f) cmll128_t4_cbc_decrypt : NULL;
+            break;
+        case 192:
+        case 256:
+            dat->stream.cbc = mode == EVP_CIPH_CBC_MODE ?
+                (cbc128_f) cmll256_t4_cbc_decrypt : NULL;
+            break;
+        default:
+            ret = -1;
+        }
+    } else {
+        ret = 0;
+        dat->block = (block128_f) cmll_t4_encrypt;
+        switch (bits) {
+        case 128:
+            if (mode == EVP_CIPH_CBC_MODE)
+                dat->stream.cbc = (cbc128_f) cmll128_t4_cbc_encrypt;
+            else if (mode == EVP_CIPH_CTR_MODE)
+                dat->stream.ctr = (ctr128_f) cmll128_t4_ctr32_encrypt;
+            else
+                dat->stream.cbc = NULL;
+            break;
+        case 192:
+        case 256:
+            if (mode == EVP_CIPH_CBC_MODE)
+                dat->stream.cbc = (cbc128_f) cmll256_t4_cbc_encrypt;
+            else if (mode == EVP_CIPH_CTR_MODE)
+                dat->stream.ctr = (ctr128_f) cmll256_t4_ctr32_encrypt;
+            else
+                dat->stream.cbc = NULL;
+            break;
+        default:
+            ret = -1;
+        }
+    }
+
+    if (ret < 0) {
+        EVPerr(EVP_F_CMLL_T4_INIT_KEY, EVP_R_CAMELLIA_KEY_SETUP_FAILED);
+        return 0;
+    }
+
+    return 1;
+}
+
+#  define cmll_t4_cbc_cipher camellia_cbc_cipher
+static int cmll_t4_cbc_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
+                              const unsigned char *in, size_t len);
+
+#  define cmll_t4_ecb_cipher camellia_ecb_cipher
+static int cmll_t4_ecb_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
+                              const unsigned char *in, size_t len);
+
+#  define cmll_t4_ofb_cipher camellia_ofb_cipher
+static int cmll_t4_ofb_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
+                              const unsigned char *in, size_t len);
+
+#  define cmll_t4_cfb_cipher camellia_cfb_cipher
+static int cmll_t4_cfb_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
+                              const unsigned char *in, size_t len);
+
+#  define cmll_t4_cfb8_cipher camellia_cfb8_cipher
+static int cmll_t4_cfb8_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
+                               const unsigned char *in, size_t len);
+
+#  define cmll_t4_cfb1_cipher camellia_cfb1_cipher
+static int cmll_t4_cfb1_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
+                               const unsigned char *in, size_t len);
+
+#  define cmll_t4_ctr_cipher camellia_ctr_cipher
+static int cmll_t4_ctr_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
+                              const unsigned char *in, size_t len);
+
+#  define BLOCK_CIPHER_generic(nid,keylen,blocksize,ivlen,nmode,mode,MODE,flags) \
+static const EVP_CIPHER cmll_t4_##keylen##_##mode = { \
+        nid##_##keylen##_##nmode,blocksize,keylen/8,ivlen, \
+        flags|EVP_CIPH_##MODE##_MODE,   \
+        cmll_t4_init_key,               \
+        cmll_t4_##mode##_cipher,        \
+        NULL,                           \
+        sizeof(EVP_CAMELLIA_KEY),       \
+        NULL,NULL,NULL,NULL }; \
+static const EVP_CIPHER camellia_##keylen##_##mode = { \
+        nid##_##keylen##_##nmode,blocksize,     \
+        keylen/8,ivlen, \
+        flags|EVP_CIPH_##MODE##_MODE,   \
+        camellia_init_key,              \
+        camellia_##mode##_cipher,       \
+        NULL,                           \
+        sizeof(EVP_CAMELLIA_KEY),       \
+        NULL,NULL,NULL,NULL }; \
+const EVP_CIPHER *EVP_camellia_##keylen##_##mode(void) \
+{ return SPARC_CMLL_CAPABLE?&cmll_t4_##keylen##_##mode:&camellia_##keylen##_##mode; }
+
+# else
+
+#  define BLOCK_CIPHER_generic(nid,keylen,blocksize,ivlen,nmode,mode,MODE,flags) \
+static const EVP_CIPHER camellia_##keylen##_##mode = { \
+        nid##_##keylen##_##nmode,blocksize,keylen/8,ivlen, \
+        flags|EVP_CIPH_##MODE##_MODE,   \
+        camellia_init_key,              \
+        camellia_##mode##_cipher,       \
+        NULL,                           \
+        sizeof(EVP_CAMELLIA_KEY),       \
+        NULL,NULL,NULL,NULL }; \
+const EVP_CIPHER *EVP_camellia_##keylen##_##mode(void) \
+{ return &camellia_##keylen##_##mode; }
+
+# endif
+
+# define BLOCK_CIPHER_generic_pack(nid,keylen,flags)             \
+        BLOCK_CIPHER_generic(nid,keylen,16,16,cbc,cbc,CBC,flags|EVP_CIPH_FLAG_DEFAULT_ASN1)     \
+        BLOCK_CIPHER_generic(nid,keylen,16,0,ecb,ecb,ECB,flags|EVP_CIPH_FLAG_DEFAULT_ASN1)      \
+        BLOCK_CIPHER_generic(nid,keylen,1,16,ofb128,ofb,OFB,flags|EVP_CIPH_FLAG_DEFAULT_ASN1)   \
+        BLOCK_CIPHER_generic(nid,keylen,1,16,cfb128,cfb,CFB,flags|EVP_CIPH_FLAG_DEFAULT_ASN1)   \
+        BLOCK_CIPHER_generic(nid,keylen,1,16,cfb1,cfb1,CFB,flags)       \
+        BLOCK_CIPHER_generic(nid,keylen,1,16,cfb8,cfb8,CFB,flags)       \
+        BLOCK_CIPHER_generic(nid, keylen, 1, 16, ctr, ctr, CTR, flags)
+
+/* The subkey for Camellia is generated. */
+static int camellia_init_key(EVP_CIPHER_CTX *ctx, const unsigned char *key,
+                             const unsigned char *iv, int enc)
+{
+    int ret, mode;
+    EVP_CAMELLIA_KEY *dat = EVP_C_DATA(EVP_CAMELLIA_KEY,ctx);
+
+    ret = Camellia_set_key(key, EVP_CIPHER_CTX_key_length(ctx) * 8, &dat->ks);
+    if (ret < 0) {
+        EVPerr(EVP_F_CAMELLIA_INIT_KEY, EVP_R_CAMELLIA_KEY_SETUP_FAILED);
+        return 0;
+    }
+
+    mode = EVP_CIPHER_CTX_mode(ctx);
+    if ((mode == EVP_CIPH_ECB_MODE || mode == EVP_CIPH_CBC_MODE)
+        && !enc) {
+        dat->block = (block128_f) Camellia_decrypt;
+        dat->stream.cbc = mode == EVP_CIPH_CBC_MODE ?
+            (cbc128_f) Camellia_cbc_encrypt : NULL;
+    } else {
+        dat->block = (block128_f) Camellia_encrypt;
+        dat->stream.cbc = mode == EVP_CIPH_CBC_MODE ?
+            (cbc128_f) Camellia_cbc_encrypt : NULL;
+    }
+
+    return 1;
+}
+
+static int camellia_cbc_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
+                               const unsigned char *in, size_t len)
+{
+    EVP_CAMELLIA_KEY *dat = EVP_C_DATA(EVP_CAMELLIA_KEY,ctx);
+
+    if (dat->stream.cbc)
+        (*dat->stream.cbc) (in, out, len, &dat->ks,
+                            EVP_CIPHER_CTX_iv_noconst(ctx),
+                            EVP_CIPHER_CTX_encrypting(ctx));
+    else if (EVP_CIPHER_CTX_encrypting(ctx))
+        CRYPTO_cbc128_encrypt(in, out, len, &dat->ks,
+                              EVP_CIPHER_CTX_iv_noconst(ctx), dat->block);
+    else
+        CRYPTO_cbc128_decrypt(in, out, len, &dat->ks,
+                              EVP_CIPHER_CTX_iv_noconst(ctx), dat->block);
+
+    return 1;
+}
+
+static int camellia_ecb_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
+                               const unsigned char *in, size_t len)
+{
+    size_t bl = EVP_CIPHER_CTX_block_size(ctx);
+    size_t i;
+    EVP_CAMELLIA_KEY *dat = EVP_C_DATA(EVP_CAMELLIA_KEY,ctx);
+
+    if (len < bl)
+        return 1;
+
+    for (i = 0, len -= bl; i <= len; i += bl)
+        (*dat->block) (in + i, out + i, &dat->ks);
+
+    return 1;
+}
+
+static int camellia_ofb_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
+                               const unsigned char *in, size_t len)
+{
+    EVP_CAMELLIA_KEY *dat = EVP_C_DATA(EVP_CAMELLIA_KEY,ctx);
+
+    int num = EVP_CIPHER_CTX_num(ctx);
+    CRYPTO_ofb128_encrypt(in, out, len, &dat->ks,
+                          EVP_CIPHER_CTX_iv_noconst(ctx), &num, dat->block);
+    EVP_CIPHER_CTX_set_num(ctx, num);
+    return 1;
+}
+
+static int camellia_cfb_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
+                               const unsigned char *in, size_t len)
+{
+    EVP_CAMELLIA_KEY *dat = EVP_C_DATA(EVP_CAMELLIA_KEY,ctx);
+
+    int num = EVP_CIPHER_CTX_num(ctx);
+    CRYPTO_cfb128_encrypt(in, out, len, &dat->ks,
+                          EVP_CIPHER_CTX_iv_noconst(ctx), &num, EVP_CIPHER_CTX_encrypting(ctx), dat->block);
+    EVP_CIPHER_CTX_set_num(ctx, num);
+    return 1;
+}
+
+static int camellia_cfb8_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
+                                const unsigned char *in, size_t len)
+{
+    EVP_CAMELLIA_KEY *dat = EVP_C_DATA(EVP_CAMELLIA_KEY,ctx);
+
+    int num = EVP_CIPHER_CTX_num(ctx);
+    CRYPTO_cfb128_8_encrypt(in, out, len, &dat->ks,
+                            EVP_CIPHER_CTX_iv_noconst(ctx), &num, EVP_CIPHER_CTX_encrypting(ctx), dat->block);
+    EVP_CIPHER_CTX_set_num(ctx, num);
+    return 1;
+}
+
+static int camellia_cfb1_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
+                                const unsigned char *in, size_t len)
+{
+    EVP_CAMELLIA_KEY *dat = EVP_C_DATA(EVP_CAMELLIA_KEY,ctx);
+
+    if (EVP_CIPHER_CTX_test_flags(ctx, EVP_CIPH_FLAG_LENGTH_BITS)) {
+        int num = EVP_CIPHER_CTX_num(ctx);
+        CRYPTO_cfb128_1_encrypt(in, out, len, &dat->ks,
+                                EVP_CIPHER_CTX_iv_noconst(ctx), &num, EVP_CIPHER_CTX_encrypting(ctx), dat->block);
+        EVP_CIPHER_CTX_set_num(ctx, num);
+        return 1;
+    }
+
+    while (len >= MAXBITCHUNK) {
+        int num = EVP_CIPHER_CTX_num(ctx);
+        CRYPTO_cfb128_1_encrypt(in, out, MAXBITCHUNK * 8, &dat->ks,
+                                EVP_CIPHER_CTX_iv_noconst(ctx), &num, EVP_CIPHER_CTX_encrypting(ctx), dat->block);
+        len -= MAXBITCHUNK;
+        EVP_CIPHER_CTX_set_num(ctx, num);
+    }
+    if (len) {
+        int num = EVP_CIPHER_CTX_num(ctx);
+        CRYPTO_cfb128_1_encrypt(in, out, len * 8, &dat->ks,
+                                EVP_CIPHER_CTX_iv_noconst(ctx), &num, EVP_CIPHER_CTX_encrypting(ctx), dat->block);
+        EVP_CIPHER_CTX_set_num(ctx, num);
+    }
+
+    return 1;
+}
+
+static int camellia_ctr_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
+                               const unsigned char *in, size_t len)
+{
+    unsigned int num = EVP_CIPHER_CTX_num(ctx);
+    EVP_CAMELLIA_KEY *dat = EVP_C_DATA(EVP_CAMELLIA_KEY,ctx);
+
+    if (dat->stream.ctr)
+        CRYPTO_ctr128_encrypt_ctr32(in, out, len, &dat->ks,
+                                    EVP_CIPHER_CTX_iv_noconst(ctx),
+                                    EVP_CIPHER_CTX_buf_noconst(ctx), &num,
+                                    dat->stream.ctr);
+    else
+        CRYPTO_ctr128_encrypt(in, out, len, &dat->ks,
+                              EVP_CIPHER_CTX_iv_noconst(ctx),
+                              EVP_CIPHER_CTX_buf_noconst(ctx), &num,
+                              dat->block);
+    EVP_CIPHER_CTX_set_num(ctx, num);
+    return 1;
+}
+
+BLOCK_CIPHER_generic_pack(NID_camellia, 128, 0)
+    BLOCK_CIPHER_generic_pack(NID_camellia, 192, 0)
+    BLOCK_CIPHER_generic_pack(NID_camellia, 256, 0)
+#endif
diff --git a/crypto/evp/e_cast.c b/crypto/evp/e_cast.c
new file mode 100644
index 000000000..259d44059
--- /dev/null
+++ b/crypto/evp/e_cast.c
@@ -0,0 +1,40 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+
+#ifndef OPENSSL_NO_CAST
+# include 
+# include 
+# include "internal/evp_int.h"
+# include 
+
+static int cast_init_key(EVP_CIPHER_CTX *ctx, const unsigned char *key,
+                         const unsigned char *iv, int enc);
+
+typedef struct {
+    CAST_KEY ks;
+} EVP_CAST_KEY;
+
+# define data(ctx)       EVP_C_DATA(EVP_CAST_KEY,ctx)
+
+IMPLEMENT_BLOCK_CIPHER(cast5, ks, CAST, EVP_CAST_KEY,
+                       NID_cast5, 8, CAST_KEY_LENGTH, 8, 64,
+                       EVP_CIPH_VARIABLE_LENGTH, cast_init_key, NULL,
+                       EVP_CIPHER_set_asn1_iv, EVP_CIPHER_get_asn1_iv, NULL)
+
+static int cast_init_key(EVP_CIPHER_CTX *ctx, const unsigned char *key,
+                         const unsigned char *iv, int enc)
+{
+    CAST_set_key(&data(ctx)->ks, EVP_CIPHER_CTX_key_length(ctx), key);
+    return 1;
+}
+
+#endif
diff --git a/crypto/evp/e_chacha20_poly1305.c b/crypto/evp/e_chacha20_poly1305.c
new file mode 100644
index 000000000..46bc2cb44
--- /dev/null
+++ b/crypto/evp/e_chacha20_poly1305.c
@@ -0,0 +1,454 @@
+/*
+ * Copyright 2015-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+
+#ifndef OPENSSL_NO_CHACHA
+
+# include 
+# include 
+# include "evp_locl.h"
+# include "internal/evp_int.h"
+# include "internal/chacha.h"
+
+typedef struct {
+    union {
+        double align;   /* this ensures even sizeof(EVP_CHACHA_KEY)%8==0 */
+        unsigned int d[CHACHA_KEY_SIZE / 4];
+    } key;
+    unsigned int  counter[CHACHA_CTR_SIZE / 4];
+    unsigned char buf[CHACHA_BLK_SIZE];
+    unsigned int  partial_len;
+} EVP_CHACHA_KEY;
+
+#define data(ctx)   ((EVP_CHACHA_KEY *)(ctx)->cipher_data)
+
+static int chacha_init_key(EVP_CIPHER_CTX *ctx,
+                           const unsigned char user_key[CHACHA_KEY_SIZE],
+                           const unsigned char iv[CHACHA_CTR_SIZE], int enc)
+{
+    EVP_CHACHA_KEY *key = data(ctx);
+    unsigned int i;
+
+    if (user_key)
+        for (i = 0; i < CHACHA_KEY_SIZE; i+=4) {
+            key->key.d[i/4] = CHACHA_U8TOU32(user_key+i);
+        }
+
+    if (iv)
+        for (i = 0; i < CHACHA_CTR_SIZE; i+=4) {
+            key->counter[i/4] = CHACHA_U8TOU32(iv+i);
+        }
+
+    key->partial_len = 0;
+
+    return 1;
+}
+
+static int chacha_cipher(EVP_CIPHER_CTX * ctx, unsigned char *out,
+                         const unsigned char *inp, size_t len)
+{
+    EVP_CHACHA_KEY *key = data(ctx);
+    unsigned int n, rem, ctr32;
+
+    if ((n = key->partial_len)) {
+        while (len && n < CHACHA_BLK_SIZE) {
+            *out++ = *inp++ ^ key->buf[n++];
+            len--;
+        }
+        key->partial_len = n;
+
+        if (len == 0)
+            return 1;
+
+        if (n == CHACHA_BLK_SIZE) {
+            key->partial_len = 0;
+            key->counter[0]++;
+            if (key->counter[0] == 0)
+                key->counter[1]++;
+        }
+    }
+
+    rem = (unsigned int)(len % CHACHA_BLK_SIZE);
+    len -= rem;
+    ctr32 = key->counter[0];
+    while (len >= CHACHA_BLK_SIZE) {
+        size_t blocks = len / CHACHA_BLK_SIZE;
+        /*
+         * 1<<28 is just a not-so-small yet not-so-large number...
+         * Below condition is practically never met, but it has to
+         * be checked for code correctness.
+         */
+        if (sizeof(size_t)>sizeof(unsigned int) && blocks>(1U<<28))
+            blocks = (1U<<28);
+
+        /*
+         * As ChaCha20_ctr32 operates on 32-bit counter, caller
+         * has to handle overflow. 'if' below detects the
+         * overflow, which is then handled by limiting the
+         * amount of blocks to the exact overflow point...
+         */
+        ctr32 += (unsigned int)blocks;
+        if (ctr32 < blocks) {
+            blocks -= ctr32;
+            ctr32 = 0;
+        }
+        blocks *= CHACHA_BLK_SIZE;
+        ChaCha20_ctr32(out, inp, blocks, key->key.d, key->counter);
+        len -= blocks;
+        inp += blocks;
+        out += blocks;
+
+        key->counter[0] = ctr32;
+        if (ctr32 == 0) key->counter[1]++;
+    }
+
+    if (rem) {
+        memset(key->buf, 0, sizeof(key->buf));
+        ChaCha20_ctr32(key->buf, key->buf, CHACHA_BLK_SIZE,
+                       key->key.d, key->counter);
+        for (n = 0; n < rem; n++)
+            out[n] = inp[n] ^ key->buf[n];
+        key->partial_len = rem;
+    }
+
+    return 1;
+}
+
+static const EVP_CIPHER chacha20 = {
+    NID_chacha20,
+    1,                      /* block_size */
+    CHACHA_KEY_SIZE,        /* key_len */
+    CHACHA_CTR_SIZE,        /* iv_len, 128-bit counter in the context */
+    EVP_CIPH_CUSTOM_IV | EVP_CIPH_ALWAYS_CALL_INIT,
+    chacha_init_key,
+    chacha_cipher,
+    NULL,
+    sizeof(EVP_CHACHA_KEY),
+    NULL,
+    NULL,
+    NULL,
+    NULL
+};
+
+const EVP_CIPHER *EVP_chacha20(void)
+{
+    return (&chacha20);
+}
+
+# ifndef OPENSSL_NO_POLY1305
+#  include "internal/poly1305.h"
+
+typedef struct {
+    EVP_CHACHA_KEY key;
+    unsigned int nonce[12/4];
+    unsigned char tag[POLY1305_BLOCK_SIZE];
+    struct { uint64_t aad, text; } len;
+    int aad, mac_inited, tag_len, nonce_len;
+    size_t tls_payload_length;
+} EVP_CHACHA_AEAD_CTX;
+
+#  define NO_TLS_PAYLOAD_LENGTH ((size_t)-1)
+#  define aead_data(ctx)        ((EVP_CHACHA_AEAD_CTX *)(ctx)->cipher_data)
+#  define POLY1305_ctx(actx)    ((POLY1305 *)(actx + 1))
+
+static int chacha20_poly1305_init_key(EVP_CIPHER_CTX *ctx,
+                                      const unsigned char *inkey,
+                                      const unsigned char *iv, int enc)
+{
+    EVP_CHACHA_AEAD_CTX *actx = aead_data(ctx);
+
+    if (!inkey && !iv)
+        return 1;
+
+    actx->len.aad = 0;
+    actx->len.text = 0;
+    actx->aad = 0;
+    actx->mac_inited = 0;
+    actx->tls_payload_length = NO_TLS_PAYLOAD_LENGTH;
+
+    if (iv != NULL) {
+        unsigned char temp[CHACHA_CTR_SIZE] = { 0 };
+
+        /* pad on the left */
+        if (actx->nonce_len <= CHACHA_CTR_SIZE)
+            memcpy(temp + CHACHA_CTR_SIZE - actx->nonce_len, iv, actx->nonce_len);
+
+        chacha_init_key(ctx, inkey, temp, enc);
+
+        actx->nonce[0] = actx->key.counter[1];
+        actx->nonce[1] = actx->key.counter[2];
+        actx->nonce[2] = actx->key.counter[3];
+    } else {
+        chacha_init_key(ctx, inkey, NULL, enc);
+    }
+
+    return 1;
+}
+
+static int chacha20_poly1305_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
+                                    const unsigned char *in, size_t len)
+{
+    EVP_CHACHA_AEAD_CTX *actx = aead_data(ctx);
+    size_t rem, plen = actx->tls_payload_length;
+    static const unsigned char zero[POLY1305_BLOCK_SIZE] = { 0 };
+
+    if (!actx->mac_inited) {
+        actx->key.counter[0] = 0;
+        memset(actx->key.buf, 0, sizeof(actx->key.buf));
+        ChaCha20_ctr32(actx->key.buf, actx->key.buf, CHACHA_BLK_SIZE,
+                       actx->key.key.d, actx->key.counter);
+        Poly1305_Init(POLY1305_ctx(actx), actx->key.buf);
+        actx->key.counter[0] = 1;
+        actx->key.partial_len = 0;
+        actx->len.aad = actx->len.text = 0;
+        actx->mac_inited = 1;
+    }
+
+    if (in) {                                   /* aad or text */
+        if (out == NULL) {                      /* aad */
+            Poly1305_Update(POLY1305_ctx(actx), in, len);
+            actx->len.aad += len;
+            actx->aad = 1;
+            return len;
+        } else {                                /* plain- or ciphertext */
+            if (actx->aad) {                    /* wrap up aad */
+                if ((rem = (size_t)actx->len.aad % POLY1305_BLOCK_SIZE))
+                    Poly1305_Update(POLY1305_ctx(actx), zero,
+                                    POLY1305_BLOCK_SIZE - rem);
+                actx->aad = 0;
+            }
+
+            actx->tls_payload_length = NO_TLS_PAYLOAD_LENGTH;
+            if (plen == NO_TLS_PAYLOAD_LENGTH)
+                plen = len;
+            else if (len != plen + POLY1305_BLOCK_SIZE)
+                return -1;
+
+            if (ctx->encrypt) {                 /* plaintext */
+                chacha_cipher(ctx, out, in, plen);
+                Poly1305_Update(POLY1305_ctx(actx), out, plen);
+                in += plen;
+                out += plen;
+                actx->len.text += plen;
+            } else {                            /* ciphertext */
+                Poly1305_Update(POLY1305_ctx(actx), in, plen);
+                chacha_cipher(ctx, out, in, plen);
+                in += plen;
+                out += plen;
+                actx->len.text += plen;
+            }
+        }
+    }
+    if (in == NULL                              /* explicit final */
+        || plen != len) {                       /* or tls mode */
+        const union {
+            long one;
+            char little;
+        } is_endian = { 1 };
+        unsigned char temp[POLY1305_BLOCK_SIZE];
+
+        if (actx->aad) {                        /* wrap up aad */
+            if ((rem = (size_t)actx->len.aad % POLY1305_BLOCK_SIZE))
+                Poly1305_Update(POLY1305_ctx(actx), zero,
+                                POLY1305_BLOCK_SIZE - rem);
+            actx->aad = 0;
+        }
+
+        if ((rem = (size_t)actx->len.text % POLY1305_BLOCK_SIZE))
+            Poly1305_Update(POLY1305_ctx(actx), zero,
+                            POLY1305_BLOCK_SIZE - rem);
+
+        if (is_endian.little) {
+            Poly1305_Update(POLY1305_ctx(actx),
+                            (unsigned char *)&actx->len, POLY1305_BLOCK_SIZE);
+        } else {
+            temp[0]  = (unsigned char)(actx->len.aad);
+            temp[1]  = (unsigned char)(actx->len.aad>>8);
+            temp[2]  = (unsigned char)(actx->len.aad>>16);
+            temp[3]  = (unsigned char)(actx->len.aad>>24);
+            temp[4]  = (unsigned char)(actx->len.aad>>32);
+            temp[5]  = (unsigned char)(actx->len.aad>>40);
+            temp[6]  = (unsigned char)(actx->len.aad>>48);
+            temp[7]  = (unsigned char)(actx->len.aad>>56);
+
+            temp[8]  = (unsigned char)(actx->len.text);
+            temp[9]  = (unsigned char)(actx->len.text>>8);
+            temp[10] = (unsigned char)(actx->len.text>>16);
+            temp[11] = (unsigned char)(actx->len.text>>24);
+            temp[12] = (unsigned char)(actx->len.text>>32);
+            temp[13] = (unsigned char)(actx->len.text>>40);
+            temp[14] = (unsigned char)(actx->len.text>>48);
+            temp[15] = (unsigned char)(actx->len.text>>56);
+
+            Poly1305_Update(POLY1305_ctx(actx), temp, POLY1305_BLOCK_SIZE);
+        }
+        Poly1305_Final(POLY1305_ctx(actx), ctx->encrypt ? actx->tag
+                                                        : temp);
+        actx->mac_inited = 0;
+
+        if (in != NULL && len != plen) {        /* tls mode */
+            if (ctx->encrypt) {
+                memcpy(out, actx->tag, POLY1305_BLOCK_SIZE);
+            } else {
+                if (CRYPTO_memcmp(temp, in, POLY1305_BLOCK_SIZE)) {
+                    memset(out - plen, 0, plen);
+                    return -1;
+                }
+            }
+        }
+        else if (!ctx->encrypt) {
+            if (CRYPTO_memcmp(temp, actx->tag, actx->tag_len))
+                return -1;
+        }
+    }
+    return len;
+}
+
+static int chacha20_poly1305_cleanup(EVP_CIPHER_CTX *ctx)
+{
+    EVP_CHACHA_AEAD_CTX *actx = aead_data(ctx);
+    if (actx)
+        OPENSSL_cleanse(ctx->cipher_data, sizeof(*ctx) + Poly1305_ctx_size());
+    return 1;
+}
+
+static int chacha20_poly1305_ctrl(EVP_CIPHER_CTX *ctx, int type, int arg,
+                                  void *ptr)
+{
+    EVP_CHACHA_AEAD_CTX *actx = aead_data(ctx);
+
+    switch(type) {
+    case EVP_CTRL_INIT:
+        if (actx == NULL)
+            actx = ctx->cipher_data
+                 = OPENSSL_zalloc(sizeof(*actx) + Poly1305_ctx_size());
+        if (actx == NULL) {
+            EVPerr(EVP_F_CHACHA20_POLY1305_CTRL, EVP_R_INITIALIZATION_ERROR);
+            return 0;
+        }
+        actx->len.aad = 0;
+        actx->len.text = 0;
+        actx->aad = 0;
+        actx->mac_inited = 0;
+        actx->tag_len = 0;
+        actx->nonce_len = 12;
+        actx->tls_payload_length = NO_TLS_PAYLOAD_LENGTH;
+        return 1;
+
+    case EVP_CTRL_COPY:
+        if (actx) {
+            EVP_CIPHER_CTX *dst = (EVP_CIPHER_CTX *)ptr;
+
+            dst->cipher_data =
+                   OPENSSL_memdup(actx, sizeof(*actx) + Poly1305_ctx_size());
+            if (dst->cipher_data == NULL) {
+                EVPerr(EVP_F_CHACHA20_POLY1305_CTRL, EVP_R_COPY_ERROR);
+                return 0;
+            }
+        }
+        return 1;
+
+    case EVP_CTRL_AEAD_SET_IVLEN:
+        if (arg <= 0 || arg > CHACHA_CTR_SIZE)
+            return 0;
+        actx->nonce_len = arg;
+        return 1;
+
+    case EVP_CTRL_AEAD_SET_IV_FIXED:
+        if (arg != 12)
+            return 0;
+        actx->nonce[0] = actx->key.counter[1]
+                       = CHACHA_U8TOU32((unsigned char *)ptr);
+        actx->nonce[1] = actx->key.counter[2]
+                       = CHACHA_U8TOU32((unsigned char *)ptr+4);
+        actx->nonce[2] = actx->key.counter[3]
+                       = CHACHA_U8TOU32((unsigned char *)ptr+8);
+        return 1;
+
+    case EVP_CTRL_AEAD_SET_TAG:
+        if (arg <= 0 || arg > POLY1305_BLOCK_SIZE)
+            return 0;
+        if (ptr != NULL) {
+            memcpy(actx->tag, ptr, arg);
+            actx->tag_len = arg;
+        }
+        return 1;
+
+    case EVP_CTRL_AEAD_GET_TAG:
+        if (arg <= 0 || arg > POLY1305_BLOCK_SIZE || !ctx->encrypt)
+            return 0;
+        memcpy(ptr, actx->tag, arg);
+        return 1;
+
+    case EVP_CTRL_AEAD_TLS1_AAD:
+        if (arg != EVP_AEAD_TLS1_AAD_LEN)
+            return 0;
+        {
+            unsigned int len;
+            unsigned char *aad = ptr, temp[POLY1305_BLOCK_SIZE];
+
+            len = aad[EVP_AEAD_TLS1_AAD_LEN - 2] << 8 |
+                  aad[EVP_AEAD_TLS1_AAD_LEN - 1];
+            if (!ctx->encrypt) {
+                if (len < POLY1305_BLOCK_SIZE)
+                    return 0;
+                len -= POLY1305_BLOCK_SIZE;     /* discount attached tag */
+                memcpy(temp, aad, EVP_AEAD_TLS1_AAD_LEN - 2);
+                aad = temp;
+                temp[EVP_AEAD_TLS1_AAD_LEN - 2] = (unsigned char)(len >> 8);
+                temp[EVP_AEAD_TLS1_AAD_LEN - 1] = (unsigned char)len;
+            }
+            actx->tls_payload_length = len;
+
+            /*
+             * merge record sequence number as per RFC7905
+             */
+            actx->key.counter[1] = actx->nonce[0];
+            actx->key.counter[2] = actx->nonce[1] ^ CHACHA_U8TOU32(aad);
+            actx->key.counter[3] = actx->nonce[2] ^ CHACHA_U8TOU32(aad+4);
+            actx->mac_inited = 0;
+            chacha20_poly1305_cipher(ctx, NULL, aad, EVP_AEAD_TLS1_AAD_LEN);
+            return POLY1305_BLOCK_SIZE;         /* tag length */
+        }
+
+    case EVP_CTRL_AEAD_SET_MAC_KEY:
+        /* no-op */
+        return 1;
+
+    default:
+        return -1;
+    }
+}
+
+static EVP_CIPHER chacha20_poly1305 = {
+    NID_chacha20_poly1305,
+    1,                  /* block_size */
+    CHACHA_KEY_SIZE,    /* key_len */
+    12,                 /* iv_len, 96-bit nonce in the context */
+    EVP_CIPH_FLAG_AEAD_CIPHER | EVP_CIPH_CUSTOM_IV |
+    EVP_CIPH_ALWAYS_CALL_INIT | EVP_CIPH_CTRL_INIT |
+    EVP_CIPH_CUSTOM_COPY | EVP_CIPH_FLAG_CUSTOM_CIPHER,
+    chacha20_poly1305_init_key,
+    chacha20_poly1305_cipher,
+    chacha20_poly1305_cleanup,
+    0,          /* 0 moves context-specific structure allocation to ctrl */
+    NULL,       /* set_asn1_parameters */
+    NULL,       /* get_asn1_parameters */
+    chacha20_poly1305_ctrl,
+    NULL        /* app_data */
+};
+
+const EVP_CIPHER *EVP_chacha20_poly1305(void)
+{
+    return(&chacha20_poly1305);
+}
+# endif
+#endif
diff --git a/crypto/evp/e_des.c b/crypto/evp/e_des.c
new file mode 100644
index 000000000..9b2facfec
--- /dev/null
+++ b/crypto/evp/e_des.c
@@ -0,0 +1,242 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#ifndef OPENSSL_NO_DES
+# include 
+# include 
+# include "internal/evp_int.h"
+# include 
+# include 
+
+typedef struct {
+    union {
+        double align;
+        DES_key_schedule ks;
+    } ks;
+    union {
+        void (*cbc) (const void *, void *, size_t,
+                     const DES_key_schedule *, unsigned char *);
+    } stream;
+} EVP_DES_KEY;
+
+# if defined(AES_ASM) && (defined(__sparc) || defined(__sparc__))
+/* ----------^^^ this is not a typo, just a way to detect that
+ * assembler support was in general requested... */
+#  include "sparc_arch.h"
+
+extern unsigned int OPENSSL_sparcv9cap_P[];
+
+#  define SPARC_DES_CAPABLE       (OPENSSL_sparcv9cap_P[1] & CFR_DES)
+
+void des_t4_key_expand(const void *key, DES_key_schedule *ks);
+void des_t4_cbc_encrypt(const void *inp, void *out, size_t len,
+                        const DES_key_schedule *ks, unsigned char iv[8]);
+void des_t4_cbc_decrypt(const void *inp, void *out, size_t len,
+                        const DES_key_schedule *ks, unsigned char iv[8]);
+# endif
+
+static int des_init_key(EVP_CIPHER_CTX *ctx, const unsigned char *key,
+                        const unsigned char *iv, int enc);
+static int des_ctrl(EVP_CIPHER_CTX *c, int type, int arg, void *ptr);
+
+/*
+ * Because of various casts and different names can't use
+ * IMPLEMENT_BLOCK_CIPHER
+ */
+
+static int des_ecb_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
+                          const unsigned char *in, size_t inl)
+{
+    BLOCK_CIPHER_ecb_loop()
+        DES_ecb_encrypt((DES_cblock *)(in + i), (DES_cblock *)(out + i),
+                        EVP_CIPHER_CTX_get_cipher_data(ctx),
+                        EVP_CIPHER_CTX_encrypting(ctx));
+    return 1;
+}
+
+static int des_ofb_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
+                          const unsigned char *in, size_t inl)
+{
+    while (inl >= EVP_MAXCHUNK) {
+        int num = EVP_CIPHER_CTX_num(ctx);
+        DES_ofb64_encrypt(in, out, (long)EVP_MAXCHUNK,
+                          EVP_CIPHER_CTX_get_cipher_data(ctx),
+                          (DES_cblock *)EVP_CIPHER_CTX_iv_noconst(ctx), &num);
+        EVP_CIPHER_CTX_set_num(ctx, num);
+        inl -= EVP_MAXCHUNK;
+        in += EVP_MAXCHUNK;
+        out += EVP_MAXCHUNK;
+    }
+    if (inl) {
+        int num = EVP_CIPHER_CTX_num(ctx);
+        DES_ofb64_encrypt(in, out, (long)inl,
+                          EVP_CIPHER_CTX_get_cipher_data(ctx),
+                          (DES_cblock *)EVP_CIPHER_CTX_iv_noconst(ctx), &num);
+        EVP_CIPHER_CTX_set_num(ctx, num);
+    }
+    return 1;
+}
+
+static int des_cbc_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
+                          const unsigned char *in, size_t inl)
+{
+    EVP_DES_KEY *dat = (EVP_DES_KEY *) EVP_CIPHER_CTX_get_cipher_data(ctx);
+
+    if (dat->stream.cbc != NULL) {
+        (*dat->stream.cbc) (in, out, inl, &dat->ks.ks,
+                            EVP_CIPHER_CTX_iv_noconst(ctx));
+        return 1;
+    }
+    while (inl >= EVP_MAXCHUNK) {
+        DES_ncbc_encrypt(in, out, (long)EVP_MAXCHUNK,
+                         EVP_CIPHER_CTX_get_cipher_data(ctx),
+                         (DES_cblock *)EVP_CIPHER_CTX_iv_noconst(ctx),
+                         EVP_CIPHER_CTX_encrypting(ctx));
+        inl -= EVP_MAXCHUNK;
+        in += EVP_MAXCHUNK;
+        out += EVP_MAXCHUNK;
+    }
+    if (inl)
+        DES_ncbc_encrypt(in, out, (long)inl,
+                         EVP_CIPHER_CTX_get_cipher_data(ctx),
+                         (DES_cblock *)EVP_CIPHER_CTX_iv_noconst(ctx),
+                         EVP_CIPHER_CTX_encrypting(ctx));
+    return 1;
+}
+
+static int des_cfb64_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
+                            const unsigned char *in, size_t inl)
+{
+    while (inl >= EVP_MAXCHUNK) {
+        int num = EVP_CIPHER_CTX_num(ctx);
+        DES_cfb64_encrypt(in, out, (long)EVP_MAXCHUNK,
+                          EVP_CIPHER_CTX_get_cipher_data(ctx),
+                          (DES_cblock *)EVP_CIPHER_CTX_iv_noconst(ctx), &num,
+                          EVP_CIPHER_CTX_encrypting(ctx));
+        EVP_CIPHER_CTX_set_num(ctx, num);
+        inl -= EVP_MAXCHUNK;
+        in += EVP_MAXCHUNK;
+        out += EVP_MAXCHUNK;
+    }
+    if (inl) {
+        int num = EVP_CIPHER_CTX_num(ctx);
+        DES_cfb64_encrypt(in, out, (long)inl,
+                          EVP_CIPHER_CTX_get_cipher_data(ctx),
+                          (DES_cblock *)EVP_CIPHER_CTX_iv_noconst(ctx), &num,
+                          EVP_CIPHER_CTX_encrypting(ctx));
+        EVP_CIPHER_CTX_set_num(ctx, num);
+    }
+    return 1;
+}
+
+/*
+ * Although we have a CFB-r implementation for DES, it doesn't pack the right
+ * way, so wrap it here
+ */
+static int des_cfb1_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
+                           const unsigned char *in, size_t inl)
+{
+    size_t n, chunk = EVP_MAXCHUNK / 8;
+    unsigned char c[1], d[1];
+
+    if (inl < chunk)
+        chunk = inl;
+
+    while (inl && inl >= chunk) {
+        for (n = 0; n < chunk * 8; ++n) {
+            c[0] = (in[n / 8] & (1 << (7 - n % 8))) ? 0x80 : 0;
+            DES_cfb_encrypt(c, d, 1, 1, EVP_CIPHER_CTX_get_cipher_data(ctx),
+                            (DES_cblock *)EVP_CIPHER_CTX_iv_noconst(ctx),
+                            EVP_CIPHER_CTX_encrypting(ctx));
+            out[n / 8] =
+                (out[n / 8] & ~(0x80 >> (unsigned int)(n % 8))) |
+                ((d[0] & 0x80) >> (unsigned int)(n % 8));
+        }
+        inl -= chunk;
+        in += chunk;
+        out += chunk;
+        if (inl < chunk)
+            chunk = inl;
+    }
+
+    return 1;
+}
+
+static int des_cfb8_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
+                           const unsigned char *in, size_t inl)
+{
+    while (inl >= EVP_MAXCHUNK) {
+        DES_cfb_encrypt(in, out, 8, (long)EVP_MAXCHUNK,
+                        EVP_CIPHER_CTX_get_cipher_data(ctx),
+                        (DES_cblock *)EVP_CIPHER_CTX_iv_noconst(ctx),
+                        EVP_CIPHER_CTX_encrypting(ctx));
+        inl -= EVP_MAXCHUNK;
+        in += EVP_MAXCHUNK;
+        out += EVP_MAXCHUNK;
+    }
+    if (inl)
+        DES_cfb_encrypt(in, out, 8, (long)inl,
+                        EVP_CIPHER_CTX_get_cipher_data(ctx),
+                        (DES_cblock *)EVP_CIPHER_CTX_iv_noconst(ctx),
+                        EVP_CIPHER_CTX_encrypting(ctx));
+    return 1;
+}
+
+BLOCK_CIPHER_defs(des, EVP_DES_KEY, NID_des, 8, 8, 8, 64,
+                  EVP_CIPH_RAND_KEY, des_init_key, NULL,
+                  EVP_CIPHER_set_asn1_iv, EVP_CIPHER_get_asn1_iv, des_ctrl)
+
+    BLOCK_CIPHER_def_cfb(des, EVP_DES_KEY, NID_des, 8, 8, 1,
+                     EVP_CIPH_RAND_KEY, des_init_key, NULL,
+                     EVP_CIPHER_set_asn1_iv, EVP_CIPHER_get_asn1_iv, des_ctrl)
+
+    BLOCK_CIPHER_def_cfb(des, EVP_DES_KEY, NID_des, 8, 8, 8,
+                     EVP_CIPH_RAND_KEY, des_init_key, NULL,
+                     EVP_CIPHER_set_asn1_iv, EVP_CIPHER_get_asn1_iv, des_ctrl)
+
+static int des_init_key(EVP_CIPHER_CTX *ctx, const unsigned char *key,
+                        const unsigned char *iv, int enc)
+{
+    DES_cblock *deskey = (DES_cblock *)key;
+    EVP_DES_KEY *dat = (EVP_DES_KEY *) EVP_CIPHER_CTX_get_cipher_data(ctx);
+
+    dat->stream.cbc = NULL;
+# if defined(SPARC_DES_CAPABLE)
+    if (SPARC_DES_CAPABLE) {
+        int mode = EVP_CIPHER_CTX_mode(ctx);
+
+        if (mode == EVP_CIPH_CBC_MODE) {
+            des_t4_key_expand(key, &dat->ks.ks);
+            dat->stream.cbc = enc ? des_t4_cbc_encrypt : des_t4_cbc_decrypt;
+            return 1;
+        }
+    }
+# endif
+    DES_set_key_unchecked(deskey, EVP_CIPHER_CTX_get_cipher_data(ctx));
+    return 1;
+}
+
+static int des_ctrl(EVP_CIPHER_CTX *c, int type, int arg, void *ptr)
+{
+
+    switch (type) {
+    case EVP_CTRL_RAND_KEY:
+        if (RAND_bytes(ptr, 8) <= 0)
+            return 0;
+        DES_set_odd_parity((DES_cblock *)ptr);
+        return 1;
+
+    default:
+        return -1;
+    }
+}
+
+#endif
diff --git a/crypto/evp/e_des3.c b/crypto/evp/e_des3.c
new file mode 100644
index 000000000..499826b04
--- /dev/null
+++ b/crypto/evp/e_des3.c
@@ -0,0 +1,424 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#ifndef OPENSSL_NO_DES
+# include 
+# include 
+# include "internal/evp_int.h"
+# include 
+# include 
+# include "evp_locl.h"
+
+typedef struct {
+    union {
+        double align;
+        DES_key_schedule ks[3];
+    } ks;
+    union {
+        void (*cbc) (const void *, void *, size_t,
+                     const DES_key_schedule *, unsigned char *);
+    } stream;
+} DES_EDE_KEY;
+# define ks1 ks.ks[0]
+# define ks2 ks.ks[1]
+# define ks3 ks.ks[2]
+
+# if defined(AES_ASM) && (defined(__sparc) || defined(__sparc__))
+/* ---------^^^ this is not a typo, just a way to detect that
+ * assembler support was in general requested... */
+#  include "sparc_arch.h"
+
+extern unsigned int OPENSSL_sparcv9cap_P[];
+
+#  define SPARC_DES_CAPABLE       (OPENSSL_sparcv9cap_P[1] & CFR_DES)
+
+void des_t4_key_expand(const void *key, DES_key_schedule *ks);
+void des_t4_ede3_cbc_encrypt(const void *inp, void *out, size_t len,
+                             const DES_key_schedule ks[3], unsigned char iv[8]);
+void des_t4_ede3_cbc_decrypt(const void *inp, void *out, size_t len,
+                             const DES_key_schedule ks[3], unsigned char iv[8]);
+# endif
+
+static int des_ede_init_key(EVP_CIPHER_CTX *ctx, const unsigned char *key,
+                            const unsigned char *iv, int enc);
+
+static int des_ede3_init_key(EVP_CIPHER_CTX *ctx, const unsigned char *key,
+                             const unsigned char *iv, int enc);
+
+static int des3_ctrl(EVP_CIPHER_CTX *c, int type, int arg, void *ptr);
+
+# define data(ctx) EVP_C_DATA(DES_EDE_KEY,ctx)
+
+/*
+ * Because of various casts and different args can't use
+ * IMPLEMENT_BLOCK_CIPHER
+ */
+
+static int des_ede_ecb_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
+                              const unsigned char *in, size_t inl)
+{
+    BLOCK_CIPHER_ecb_loop()
+        DES_ecb3_encrypt((const_DES_cblock *)(in + i),
+                         (DES_cblock *)(out + i),
+                         &data(ctx)->ks1, &data(ctx)->ks2,
+                         &data(ctx)->ks3, EVP_CIPHER_CTX_encrypting(ctx));
+    return 1;
+}
+
+static int des_ede_ofb_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
+                              const unsigned char *in, size_t inl)
+{
+    while (inl >= EVP_MAXCHUNK) {
+        int num = EVP_CIPHER_CTX_num(ctx);
+        DES_ede3_ofb64_encrypt(in, out, (long)EVP_MAXCHUNK,
+                               &data(ctx)->ks1, &data(ctx)->ks2,
+                               &data(ctx)->ks3,
+                               (DES_cblock *)EVP_CIPHER_CTX_iv_noconst(ctx),
+                               &num);
+        EVP_CIPHER_CTX_set_num(ctx, num);
+        inl -= EVP_MAXCHUNK;
+        in += EVP_MAXCHUNK;
+        out += EVP_MAXCHUNK;
+    }
+    if (inl) {
+        int num = EVP_CIPHER_CTX_num(ctx);
+        DES_ede3_ofb64_encrypt(in, out, (long)inl,
+                               &data(ctx)->ks1, &data(ctx)->ks2,
+                               &data(ctx)->ks3,
+                               (DES_cblock *)EVP_CIPHER_CTX_iv_noconst(ctx),
+                               &num);
+        EVP_CIPHER_CTX_set_num(ctx, num);
+    }
+    return 1;
+}
+
+static int des_ede_cbc_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
+                              const unsigned char *in, size_t inl)
+{
+    DES_EDE_KEY *dat = data(ctx);
+
+    if (dat->stream.cbc != NULL) {
+        (*dat->stream.cbc) (in, out, inl, dat->ks.ks,
+                            EVP_CIPHER_CTX_iv_noconst(ctx));
+        return 1;
+    }
+
+    while (inl >= EVP_MAXCHUNK) {
+        DES_ede3_cbc_encrypt(in, out, (long)EVP_MAXCHUNK,
+                             &dat->ks1, &dat->ks2, &dat->ks3,
+                             (DES_cblock *)EVP_CIPHER_CTX_iv_noconst(ctx),
+                             EVP_CIPHER_CTX_encrypting(ctx));
+        inl -= EVP_MAXCHUNK;
+        in += EVP_MAXCHUNK;
+        out += EVP_MAXCHUNK;
+    }
+    if (inl)
+        DES_ede3_cbc_encrypt(in, out, (long)inl,
+                             &dat->ks1, &dat->ks2, &dat->ks3,
+                             (DES_cblock *)EVP_CIPHER_CTX_iv_noconst(ctx),
+                             EVP_CIPHER_CTX_encrypting(ctx));
+    return 1;
+}
+
+static int des_ede_cfb64_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
+                                const unsigned char *in, size_t inl)
+{
+    while (inl >= EVP_MAXCHUNK) {
+        int num = EVP_CIPHER_CTX_num(ctx);
+        DES_ede3_cfb64_encrypt(in, out, (long)EVP_MAXCHUNK,
+                               &data(ctx)->ks1, &data(ctx)->ks2,
+                               &data(ctx)->ks3,
+                               (DES_cblock *)EVP_CIPHER_CTX_iv_noconst(ctx),
+                               &num, EVP_CIPHER_CTX_encrypting(ctx));
+        EVP_CIPHER_CTX_set_num(ctx, num);
+        inl -= EVP_MAXCHUNK;
+        in += EVP_MAXCHUNK;
+        out += EVP_MAXCHUNK;
+    }
+    if (inl) {
+        int num = EVP_CIPHER_CTX_num(ctx);
+        DES_ede3_cfb64_encrypt(in, out, (long)inl,
+                               &data(ctx)->ks1, &data(ctx)->ks2,
+                               &data(ctx)->ks3,
+                               (DES_cblock *)EVP_CIPHER_CTX_iv_noconst(ctx),
+                               &num, EVP_CIPHER_CTX_encrypting(ctx));
+        EVP_CIPHER_CTX_set_num(ctx, num);
+    }
+    return 1;
+}
+
+/*
+ * Although we have a CFB-r implementation for 3-DES, it doesn't pack the
+ * right way, so wrap it here
+ */
+static int des_ede3_cfb1_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
+                                const unsigned char *in, size_t inl)
+{
+    size_t n;
+    unsigned char c[1], d[1];
+
+    if (!EVP_CIPHER_CTX_test_flags(ctx, EVP_CIPH_FLAG_LENGTH_BITS))
+            inl *= 8;
+    for (n = 0; n < inl; ++n) {
+        c[0] = (in[n / 8] & (1 << (7 - n % 8))) ? 0x80 : 0;
+        DES_ede3_cfb_encrypt(c, d, 1, 1,
+                             &data(ctx)->ks1, &data(ctx)->ks2,
+                             &data(ctx)->ks3,
+                             (DES_cblock *)EVP_CIPHER_CTX_iv_noconst(ctx),
+                             EVP_CIPHER_CTX_encrypting(ctx));
+        out[n / 8] = (out[n / 8] & ~(0x80 >> (unsigned int)(n % 8)))
+            | ((d[0] & 0x80) >> (unsigned int)(n % 8));
+    }
+
+    return 1;
+}
+
+static int des_ede3_cfb8_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
+                                const unsigned char *in, size_t inl)
+{
+    while (inl >= EVP_MAXCHUNK) {
+        DES_ede3_cfb_encrypt(in, out, 8, (long)EVP_MAXCHUNK,
+                             &data(ctx)->ks1, &data(ctx)->ks2,
+                             &data(ctx)->ks3,
+                             (DES_cblock *)EVP_CIPHER_CTX_iv_noconst(ctx),
+                             EVP_CIPHER_CTX_encrypting(ctx));
+        inl -= EVP_MAXCHUNK;
+        in += EVP_MAXCHUNK;
+        out += EVP_MAXCHUNK;
+    }
+    if (inl)
+        DES_ede3_cfb_encrypt(in, out, 8, (long)inl,
+                             &data(ctx)->ks1, &data(ctx)->ks2,
+                             &data(ctx)->ks3,
+                             (DES_cblock *)EVP_CIPHER_CTX_iv_noconst(ctx),
+                             EVP_CIPHER_CTX_encrypting(ctx));
+    return 1;
+}
+
+BLOCK_CIPHER_defs(des_ede, DES_EDE_KEY, NID_des_ede, 8, 16, 8, 64,
+                  EVP_CIPH_RAND_KEY | EVP_CIPH_FLAG_DEFAULT_ASN1,
+                  des_ede_init_key, NULL, NULL, NULL, des3_ctrl)
+# define des_ede3_cfb64_cipher des_ede_cfb64_cipher
+# define des_ede3_ofb_cipher des_ede_ofb_cipher
+# define des_ede3_cbc_cipher des_ede_cbc_cipher
+# define des_ede3_ecb_cipher des_ede_ecb_cipher
+    BLOCK_CIPHER_defs(des_ede3, DES_EDE_KEY, NID_des_ede3, 8, 24, 8, 64,
+                  EVP_CIPH_RAND_KEY | EVP_CIPH_FLAG_DEFAULT_ASN1,
+                  des_ede3_init_key, NULL, NULL, NULL, des3_ctrl)
+
+    BLOCK_CIPHER_def_cfb(des_ede3, DES_EDE_KEY, NID_des_ede3, 24, 8, 1,
+                     EVP_CIPH_RAND_KEY | EVP_CIPH_FLAG_DEFAULT_ASN1,
+                     des_ede3_init_key, NULL, NULL, NULL, des3_ctrl)
+
+    BLOCK_CIPHER_def_cfb(des_ede3, DES_EDE_KEY, NID_des_ede3, 24, 8, 8,
+                     EVP_CIPH_RAND_KEY | EVP_CIPH_FLAG_DEFAULT_ASN1,
+                     des_ede3_init_key, NULL, NULL, NULL, des3_ctrl)
+
+static int des_ede_init_key(EVP_CIPHER_CTX *ctx, const unsigned char *key,
+                            const unsigned char *iv, int enc)
+{
+    DES_cblock *deskey = (DES_cblock *)key;
+    DES_EDE_KEY *dat = data(ctx);
+
+    dat->stream.cbc = NULL;
+# if defined(SPARC_DES_CAPABLE)
+    if (SPARC_DES_CAPABLE) {
+        int mode = EVP_CIPHER_CTX_mode(ctx);
+
+        if (mode == EVP_CIPH_CBC_MODE) {
+            des_t4_key_expand(&deskey[0], &dat->ks1);
+            des_t4_key_expand(&deskey[1], &dat->ks2);
+            memcpy(&dat->ks3, &dat->ks1, sizeof(dat->ks1));
+            dat->stream.cbc = enc ? des_t4_ede3_cbc_encrypt :
+                des_t4_ede3_cbc_decrypt;
+            return 1;
+        }
+    }
+# endif
+    DES_set_key_unchecked(&deskey[0], &dat->ks1);
+    DES_set_key_unchecked(&deskey[1], &dat->ks2);
+    memcpy(&dat->ks3, &dat->ks1, sizeof(dat->ks1));
+    return 1;
+}
+
+static int des_ede3_init_key(EVP_CIPHER_CTX *ctx, const unsigned char *key,
+                             const unsigned char *iv, int enc)
+{
+    DES_cblock *deskey = (DES_cblock *)key;
+    DES_EDE_KEY *dat = data(ctx);
+
+    dat->stream.cbc = NULL;
+# if defined(SPARC_DES_CAPABLE)
+    if (SPARC_DES_CAPABLE) {
+        int mode = EVP_CIPHER_CTX_mode(ctx);
+
+        if (mode == EVP_CIPH_CBC_MODE) {
+            des_t4_key_expand(&deskey[0], &dat->ks1);
+            des_t4_key_expand(&deskey[1], &dat->ks2);
+            des_t4_key_expand(&deskey[2], &dat->ks3);
+            dat->stream.cbc = enc ? des_t4_ede3_cbc_encrypt :
+                des_t4_ede3_cbc_decrypt;
+            return 1;
+        }
+    }
+# endif
+    DES_set_key_unchecked(&deskey[0], &dat->ks1);
+    DES_set_key_unchecked(&deskey[1], &dat->ks2);
+    DES_set_key_unchecked(&deskey[2], &dat->ks3);
+    return 1;
+}
+
+static int des3_ctrl(EVP_CIPHER_CTX *ctx, int type, int arg, void *ptr)
+{
+
+    DES_cblock *deskey = ptr;
+
+    switch (type) {
+    case EVP_CTRL_RAND_KEY:
+        if (RAND_bytes(ptr, EVP_CIPHER_CTX_key_length(ctx)) <= 0)
+            return 0;
+        DES_set_odd_parity(deskey);
+        if (EVP_CIPHER_CTX_key_length(ctx) >= 16)
+            DES_set_odd_parity(deskey + 1);
+        if (EVP_CIPHER_CTX_key_length(ctx) >= 24)
+            DES_set_odd_parity(deskey + 2);
+        return 1;
+
+    default:
+        return -1;
+    }
+}
+
+const EVP_CIPHER *EVP_des_ede(void)
+{
+    return &des_ede_ecb;
+}
+
+const EVP_CIPHER *EVP_des_ede3(void)
+{
+    return &des_ede3_ecb;
+}
+
+# ifndef OPENSSL_NO_SHA
+#  include 
+
+static const unsigned char wrap_iv[8] =
+    { 0x4a, 0xdd, 0xa2, 0x2c, 0x79, 0xe8, 0x21, 0x05 };
+
+static int des_ede3_unwrap(EVP_CIPHER_CTX *ctx, unsigned char *out,
+                           const unsigned char *in, size_t inl)
+{
+    unsigned char icv[8], iv[8], sha1tmp[SHA_DIGEST_LENGTH];
+    int rv = -1;
+    if (inl < 24)
+        return -1;
+    if (out == NULL)
+        return inl - 16;
+    memcpy(EVP_CIPHER_CTX_iv_noconst(ctx), wrap_iv, 8);
+    /* Decrypt first block which will end up as icv */
+    des_ede_cbc_cipher(ctx, icv, in, 8);
+    /* Decrypt central blocks */
+    /*
+     * If decrypting in place move whole output along a block so the next
+     * des_ede_cbc_cipher is in place.
+     */
+    if (out == in) {
+        memmove(out, out + 8, inl - 8);
+        in -= 8;
+    }
+    des_ede_cbc_cipher(ctx, out, in + 8, inl - 16);
+    /* Decrypt final block which will be IV */
+    des_ede_cbc_cipher(ctx, iv, in + inl - 8, 8);
+    /* Reverse order of everything */
+    BUF_reverse(icv, NULL, 8);
+    BUF_reverse(out, NULL, inl - 16);
+    BUF_reverse(EVP_CIPHER_CTX_iv_noconst(ctx), iv, 8);
+    /* Decrypt again using new IV */
+    des_ede_cbc_cipher(ctx, out, out, inl - 16);
+    des_ede_cbc_cipher(ctx, icv, icv, 8);
+    /* Work out SHA1 hash of first portion */
+    SHA1(out, inl - 16, sha1tmp);
+
+    if (!CRYPTO_memcmp(sha1tmp, icv, 8))
+        rv = inl - 16;
+    OPENSSL_cleanse(icv, 8);
+    OPENSSL_cleanse(sha1tmp, SHA_DIGEST_LENGTH);
+    OPENSSL_cleanse(iv, 8);
+    OPENSSL_cleanse(EVP_CIPHER_CTX_iv_noconst(ctx), 8);
+    if (rv == -1)
+        OPENSSL_cleanse(out, inl - 16);
+
+    return rv;
+}
+
+static int des_ede3_wrap(EVP_CIPHER_CTX *ctx, unsigned char *out,
+                         const unsigned char *in, size_t inl)
+{
+    unsigned char sha1tmp[SHA_DIGEST_LENGTH];
+    if (out == NULL)
+        return inl + 16;
+    /* Copy input to output buffer + 8 so we have space for IV */
+    memmove(out + 8, in, inl);
+    /* Work out ICV */
+    SHA1(in, inl, sha1tmp);
+    memcpy(out + inl + 8, sha1tmp, 8);
+    OPENSSL_cleanse(sha1tmp, SHA_DIGEST_LENGTH);
+    /* Generate random IV */
+    if (RAND_bytes(EVP_CIPHER_CTX_iv_noconst(ctx), 8) <= 0)
+        return -1;
+    memcpy(out, EVP_CIPHER_CTX_iv_noconst(ctx), 8);
+    /* Encrypt everything after IV in place */
+    des_ede_cbc_cipher(ctx, out + 8, out + 8, inl + 8);
+    BUF_reverse(out, NULL, inl + 16);
+    memcpy(EVP_CIPHER_CTX_iv_noconst(ctx), wrap_iv, 8);
+    des_ede_cbc_cipher(ctx, out, out, inl + 16);
+    return inl + 16;
+}
+
+static int des_ede3_wrap_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
+                                const unsigned char *in, size_t inl)
+{
+    /*
+     * Sanity check input length: we typically only wrap keys so EVP_MAXCHUNK
+     * is more than will ever be needed. Also input length must be a multiple
+     * of 8 bits.
+     */
+    if (inl >= EVP_MAXCHUNK || inl % 8)
+        return -1;
+
+    if (is_partially_overlapping(out, in, inl)) {
+        EVPerr(EVP_F_DES_EDE3_WRAP_CIPHER, EVP_R_PARTIALLY_OVERLAPPING);
+        return 0;
+    }
+
+    if (EVP_CIPHER_CTX_encrypting(ctx))
+        return des_ede3_wrap(ctx, out, in, inl);
+    else
+        return des_ede3_unwrap(ctx, out, in, inl);
+}
+
+static const EVP_CIPHER des3_wrap = {
+    NID_id_smime_alg_CMS3DESwrap,
+    8, 24, 0,
+    EVP_CIPH_WRAP_MODE | EVP_CIPH_CUSTOM_IV | EVP_CIPH_FLAG_CUSTOM_CIPHER
+        | EVP_CIPH_FLAG_DEFAULT_ASN1,
+    des_ede3_init_key, des_ede3_wrap_cipher,
+    NULL,
+    sizeof(DES_EDE_KEY),
+    NULL, NULL, NULL, NULL
+};
+
+const EVP_CIPHER *EVP_des_ede3_wrap(void)
+{
+    return &des3_wrap;
+}
+# endif /* OPENSSL_NO_SHA */
+#endif
diff --git a/crypto/evp/e_idea.c b/crypto/evp/e_idea.c
new file mode 100644
index 000000000..93f6a4131
--- /dev/null
+++ b/crypto/evp/e_idea.c
@@ -0,0 +1,70 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+
+#ifndef OPENSSL_NO_IDEA
+# include 
+# include 
+# include "internal/evp_int.h"
+# include 
+
+/* Can't use IMPLEMENT_BLOCK_CIPHER because IDEA_ecb_encrypt is different */
+
+typedef struct {
+    IDEA_KEY_SCHEDULE ks;
+} EVP_IDEA_KEY;
+
+static int idea_init_key(EVP_CIPHER_CTX *ctx, const unsigned char *key,
+                         const unsigned char *iv, int enc);
+
+/*
+ * NB IDEA_ecb_encrypt doesn't take an 'encrypt' argument so we treat it as a
+ * special case
+ */
+
+static int idea_ecb_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
+                           const unsigned char *in, size_t inl)
+{
+    BLOCK_CIPHER_ecb_loop()
+        IDEA_ecb_encrypt(in + i, out + i, &EVP_C_DATA(EVP_IDEA_KEY,ctx)->ks);
+    return 1;
+}
+
+BLOCK_CIPHER_func_cbc(idea, IDEA, EVP_IDEA_KEY, ks)
+BLOCK_CIPHER_func_ofb(idea, IDEA, 64, EVP_IDEA_KEY, ks)
+BLOCK_CIPHER_func_cfb(idea, IDEA, 64, EVP_IDEA_KEY, ks)
+
+BLOCK_CIPHER_defs(idea, IDEA_KEY_SCHEDULE, NID_idea, 8, 16, 8, 64,
+                  0, idea_init_key, NULL,
+                  EVP_CIPHER_set_asn1_iv, EVP_CIPHER_get_asn1_iv, NULL)
+
+static int idea_init_key(EVP_CIPHER_CTX *ctx, const unsigned char *key,
+                         const unsigned char *iv, int enc)
+{
+    if (!enc) {
+        if (EVP_CIPHER_CTX_mode(ctx) == EVP_CIPH_OFB_MODE)
+            enc = 1;
+        else if (EVP_CIPHER_CTX_mode(ctx) == EVP_CIPH_CFB_MODE)
+            enc = 1;
+    }
+    if (enc)
+        IDEA_set_encrypt_key(key, &EVP_C_DATA(EVP_IDEA_KEY,ctx)->ks);
+    else {
+        IDEA_KEY_SCHEDULE tmp;
+
+        IDEA_set_encrypt_key(key, &tmp);
+        IDEA_set_decrypt_key(&tmp, &EVP_C_DATA(EVP_IDEA_KEY,ctx)->ks);
+        OPENSSL_cleanse((unsigned char *)&tmp, sizeof(IDEA_KEY_SCHEDULE));
+    }
+    return 1;
+}
+
+#endif
diff --git a/crypto/evp/e_null.c b/crypto/evp/e_null.c
new file mode 100644
index 000000000..0dfc48abf
--- /dev/null
+++ b/crypto/evp/e_null.c
@@ -0,0 +1,50 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+#include "internal/evp_int.h"
+
+static int null_init_key(EVP_CIPHER_CTX *ctx, const unsigned char *key,
+                         const unsigned char *iv, int enc);
+static int null_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
+                       const unsigned char *in, size_t inl);
+static const EVP_CIPHER n_cipher = {
+    NID_undef,
+    1, 0, 0, 0,
+    null_init_key,
+    null_cipher,
+    NULL,
+    0,
+    NULL,
+    NULL,
+    NULL,
+    NULL
+};
+
+const EVP_CIPHER *EVP_enc_null(void)
+{
+    return (&n_cipher);
+}
+
+static int null_init_key(EVP_CIPHER_CTX *ctx, const unsigned char *key,
+                         const unsigned char *iv, int enc)
+{
+    return 1;
+}
+
+static int null_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
+                       const unsigned char *in, size_t inl)
+{
+    if (in != out)
+        memcpy(out, in, inl);
+    return 1;
+}
diff --git a/crypto/evp/e_old.c b/crypto/evp/e_old.c
new file mode 100644
index 000000000..020b76eb6
--- /dev/null
+++ b/crypto/evp/e_old.c
@@ -0,0 +1,115 @@
+/*
+ * Copyright 2004-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#if OPENSSL_API_COMPAT >= 0x00908000L
+NON_EMPTY_TRANSLATION_UNIT
+#else
+
+# include 
+
+/*
+ * Define some deprecated functions, so older programs don't crash and burn
+ * too quickly.  On Windows and VMS, these will never be used, since
+ * functions and variables in shared libraries are selected by entry point
+ * location, not by name.
+ */
+
+# ifndef OPENSSL_NO_BF
+#  undef EVP_bf_cfb
+const EVP_CIPHER *EVP_bf_cfb(void);
+const EVP_CIPHER *EVP_bf_cfb(void)
+{
+    return EVP_bf_cfb64();
+}
+# endif
+
+# ifndef OPENSSL_NO_DES
+#  undef EVP_des_cfb
+const EVP_CIPHER *EVP_des_cfb(void);
+const EVP_CIPHER *EVP_des_cfb(void)
+{
+    return EVP_des_cfb64();
+}
+
+#  undef EVP_des_ede3_cfb
+const EVP_CIPHER *EVP_des_ede3_cfb(void);
+const EVP_CIPHER *EVP_des_ede3_cfb(void)
+{
+    return EVP_des_ede3_cfb64();
+}
+
+#  undef EVP_des_ede_cfb
+const EVP_CIPHER *EVP_des_ede_cfb(void);
+const EVP_CIPHER *EVP_des_ede_cfb(void)
+{
+    return EVP_des_ede_cfb64();
+}
+# endif
+
+# ifndef OPENSSL_NO_IDEA
+#  undef EVP_idea_cfb
+const EVP_CIPHER *EVP_idea_cfb(void);
+const EVP_CIPHER *EVP_idea_cfb(void)
+{
+    return EVP_idea_cfb64();
+}
+# endif
+
+# ifndef OPENSSL_NO_RC2
+#  undef EVP_rc2_cfb
+const EVP_CIPHER *EVP_rc2_cfb(void);
+const EVP_CIPHER *EVP_rc2_cfb(void)
+{
+    return EVP_rc2_cfb64();
+}
+# endif
+
+# ifndef OPENSSL_NO_CAST
+#  undef EVP_cast5_cfb
+const EVP_CIPHER *EVP_cast5_cfb(void);
+const EVP_CIPHER *EVP_cast5_cfb(void)
+{
+    return EVP_cast5_cfb64();
+}
+# endif
+
+# ifndef OPENSSL_NO_RC5
+#  undef EVP_rc5_32_12_16_cfb
+const EVP_CIPHER *EVP_rc5_32_12_16_cfb(void);
+const EVP_CIPHER *EVP_rc5_32_12_16_cfb(void)
+{
+    return EVP_rc5_32_12_16_cfb64();
+}
+# endif
+
+# ifndef OPENSSL_NO_AES
+#  undef EVP_aes_128_cfb
+const EVP_CIPHER *EVP_aes_128_cfb(void);
+const EVP_CIPHER *EVP_aes_128_cfb(void)
+{
+    return EVP_aes_128_cfb128();
+}
+
+#  undef EVP_aes_192_cfb
+const EVP_CIPHER *EVP_aes_192_cfb(void);
+const EVP_CIPHER *EVP_aes_192_cfb(void)
+{
+    return EVP_aes_192_cfb128();
+}
+
+#  undef EVP_aes_256_cfb
+const EVP_CIPHER *EVP_aes_256_cfb(void);
+const EVP_CIPHER *EVP_aes_256_cfb(void)
+{
+    return EVP_aes_256_cfb128();
+}
+# endif
+
+#endif
diff --git a/crypto/evp/e_rc2.c b/crypto/evp/e_rc2.c
new file mode 100644
index 000000000..ed10bb332
--- /dev/null
+++ b/crypto/evp/e_rc2.c
@@ -0,0 +1,189 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+
+#ifndef OPENSSL_NO_RC2
+
+# include 
+# include 
+# include "internal/evp_int.h"
+# include 
+
+static int rc2_init_key(EVP_CIPHER_CTX *ctx, const unsigned char *key,
+                        const unsigned char *iv, int enc);
+static int rc2_meth_to_magic(EVP_CIPHER_CTX *ctx);
+static int rc2_magic_to_meth(int i);
+static int rc2_set_asn1_type_and_iv(EVP_CIPHER_CTX *c, ASN1_TYPE *type);
+static int rc2_get_asn1_type_and_iv(EVP_CIPHER_CTX *c, ASN1_TYPE *type);
+static int rc2_ctrl(EVP_CIPHER_CTX *c, int type, int arg, void *ptr);
+
+typedef struct {
+    int key_bits;               /* effective key bits */
+    RC2_KEY ks;                 /* key schedule */
+} EVP_RC2_KEY;
+
+# define data(ctx)       EVP_C_DATA(EVP_RC2_KEY,ctx)
+
+IMPLEMENT_BLOCK_CIPHER(rc2, ks, RC2, EVP_RC2_KEY, NID_rc2,
+                       8,
+                       RC2_KEY_LENGTH, 8, 64,
+                       EVP_CIPH_VARIABLE_LENGTH | EVP_CIPH_CTRL_INIT,
+                       rc2_init_key, NULL,
+                       rc2_set_asn1_type_and_iv, rc2_get_asn1_type_and_iv,
+                       rc2_ctrl)
+# define RC2_40_MAGIC    0xa0
+# define RC2_64_MAGIC    0x78
+# define RC2_128_MAGIC   0x3a
+static const EVP_CIPHER r2_64_cbc_cipher = {
+    NID_rc2_64_cbc,
+    8, 8 /* 64 bit */ , 8,
+    EVP_CIPH_CBC_MODE | EVP_CIPH_VARIABLE_LENGTH | EVP_CIPH_CTRL_INIT,
+    rc2_init_key,
+    rc2_cbc_cipher,
+    NULL,
+    sizeof(EVP_RC2_KEY),
+    rc2_set_asn1_type_and_iv,
+    rc2_get_asn1_type_and_iv,
+    rc2_ctrl,
+    NULL
+};
+
+static const EVP_CIPHER r2_40_cbc_cipher = {
+    NID_rc2_40_cbc,
+    8, 5 /* 40 bit */ , 8,
+    EVP_CIPH_CBC_MODE | EVP_CIPH_VARIABLE_LENGTH | EVP_CIPH_CTRL_INIT,
+    rc2_init_key,
+    rc2_cbc_cipher,
+    NULL,
+    sizeof(EVP_RC2_KEY),
+    rc2_set_asn1_type_and_iv,
+    rc2_get_asn1_type_and_iv,
+    rc2_ctrl,
+    NULL
+};
+
+const EVP_CIPHER *EVP_rc2_64_cbc(void)
+{
+    return (&r2_64_cbc_cipher);
+}
+
+const EVP_CIPHER *EVP_rc2_40_cbc(void)
+{
+    return (&r2_40_cbc_cipher);
+}
+
+static int rc2_init_key(EVP_CIPHER_CTX *ctx, const unsigned char *key,
+                        const unsigned char *iv, int enc)
+{
+    RC2_set_key(&data(ctx)->ks, EVP_CIPHER_CTX_key_length(ctx),
+                key, data(ctx)->key_bits);
+    return 1;
+}
+
+static int rc2_meth_to_magic(EVP_CIPHER_CTX *e)
+{
+    int i;
+
+    EVP_CIPHER_CTX_ctrl(e, EVP_CTRL_GET_RC2_KEY_BITS, 0, &i);
+    if (i == 128)
+        return (RC2_128_MAGIC);
+    else if (i == 64)
+        return (RC2_64_MAGIC);
+    else if (i == 40)
+        return (RC2_40_MAGIC);
+    else
+        return (0);
+}
+
+static int rc2_magic_to_meth(int i)
+{
+    if (i == RC2_128_MAGIC)
+        return 128;
+    else if (i == RC2_64_MAGIC)
+        return 64;
+    else if (i == RC2_40_MAGIC)
+        return 40;
+    else {
+        EVPerr(EVP_F_RC2_MAGIC_TO_METH, EVP_R_UNSUPPORTED_KEY_SIZE);
+        return (0);
+    }
+}
+
+static int rc2_get_asn1_type_and_iv(EVP_CIPHER_CTX *c, ASN1_TYPE *type)
+{
+    long num = 0;
+    int i = 0;
+    int key_bits;
+    unsigned int l;
+    unsigned char iv[EVP_MAX_IV_LENGTH];
+
+    if (type != NULL) {
+        l = EVP_CIPHER_CTX_iv_length(c);
+        OPENSSL_assert(l <= sizeof(iv));
+        i = ASN1_TYPE_get_int_octetstring(type, &num, iv, l);
+        if (i != (int)l)
+            return -1;
+        key_bits = rc2_magic_to_meth((int)num);
+        if (!key_bits)
+            return -1;
+        if (i > 0 && !EVP_CipherInit_ex(c, NULL, NULL, NULL, iv, -1))
+            return -1;
+        EVP_CIPHER_CTX_ctrl(c, EVP_CTRL_SET_RC2_KEY_BITS, key_bits, NULL);
+        if (EVP_CIPHER_CTX_set_key_length(c, key_bits / 8) <= 0)
+            return -1;
+    }
+    return i;
+}
+
+static int rc2_set_asn1_type_and_iv(EVP_CIPHER_CTX *c, ASN1_TYPE *type)
+{
+    long num;
+    int i = 0, j;
+
+    if (type != NULL) {
+        num = rc2_meth_to_magic(c);
+        j = EVP_CIPHER_CTX_iv_length(c);
+        i = ASN1_TYPE_set_int_octetstring(type, num,
+                                          (unsigned char *)EVP_CIPHER_CTX_original_iv(c),
+                                          j);
+    }
+    return (i);
+}
+
+static int rc2_ctrl(EVP_CIPHER_CTX *c, int type, int arg, void *ptr)
+{
+    switch (type) {
+    case EVP_CTRL_INIT:
+        data(c)->key_bits = EVP_CIPHER_CTX_key_length(c) * 8;
+        return 1;
+
+    case EVP_CTRL_GET_RC2_KEY_BITS:
+        *(int *)ptr = data(c)->key_bits;
+        return 1;
+
+    case EVP_CTRL_SET_RC2_KEY_BITS:
+        if (arg > 0) {
+            data(c)->key_bits = arg;
+            return 1;
+        }
+        return 0;
+# ifdef PBE_PRF_TEST
+    case EVP_CTRL_PBE_PRF_NID:
+        *(int *)ptr = NID_hmacWithMD5;
+        return 1;
+# endif
+
+    default:
+        return -1;
+    }
+}
+
+#endif
diff --git a/crypto/evp/e_rc4.c b/crypto/evp/e_rc4.c
new file mode 100644
index 000000000..ea95deab8
--- /dev/null
+++ b/crypto/evp/e_rc4.c
@@ -0,0 +1,82 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+
+#ifndef OPENSSL_NO_RC4
+
+# include 
+# include 
+# include 
+
+# include "internal/evp_int.h"
+
+typedef struct {
+    RC4_KEY ks;                 /* working key */
+} EVP_RC4_KEY;
+
+# define data(ctx) ((EVP_RC4_KEY *)EVP_CIPHER_CTX_get_cipher_data(ctx))
+
+static int rc4_init_key(EVP_CIPHER_CTX *ctx, const unsigned char *key,
+                        const unsigned char *iv, int enc);
+static int rc4_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
+                      const unsigned char *in, size_t inl);
+static const EVP_CIPHER r4_cipher = {
+    NID_rc4,
+    1, EVP_RC4_KEY_SIZE, 0,
+    EVP_CIPH_VARIABLE_LENGTH,
+    rc4_init_key,
+    rc4_cipher,
+    NULL,
+    sizeof(EVP_RC4_KEY),
+    NULL,
+    NULL,
+    NULL,
+    NULL
+};
+
+static const EVP_CIPHER r4_40_cipher = {
+    NID_rc4_40,
+    1, 5 /* 40 bit */ , 0,
+    EVP_CIPH_VARIABLE_LENGTH,
+    rc4_init_key,
+    rc4_cipher,
+    NULL,
+    sizeof(EVP_RC4_KEY),
+    NULL,
+    NULL,
+    NULL,
+    NULL
+};
+
+const EVP_CIPHER *EVP_rc4(void)
+{
+    return (&r4_cipher);
+}
+
+const EVP_CIPHER *EVP_rc4_40(void)
+{
+    return (&r4_40_cipher);
+}
+
+static int rc4_init_key(EVP_CIPHER_CTX *ctx, const unsigned char *key,
+                        const unsigned char *iv, int enc)
+{
+    RC4_set_key(&data(ctx)->ks, EVP_CIPHER_CTX_key_length(ctx), key);
+    return 1;
+}
+
+static int rc4_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
+                      const unsigned char *in, size_t inl)
+{
+    RC4(&data(ctx)->ks, inl, in, out);
+    return 1;
+}
+#endif
diff --git a/crypto/evp/e_rc4_hmac_md5.c b/crypto/evp/e_rc4_hmac_md5.c
new file mode 100644
index 000000000..8ab18c141
--- /dev/null
+++ b/crypto/evp/e_rc4_hmac_md5.c
@@ -0,0 +1,262 @@
+/*
+ * Copyright 2011-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+
+#include 
+#include 
+
+#if !defined(OPENSSL_NO_RC4) && !defined(OPENSSL_NO_MD5)
+
+# include 
+# include 
+# include 
+# include 
+# include 
+# include "internal/evp_int.h"
+
+typedef struct {
+    RC4_KEY ks;
+    MD5_CTX head, tail, md;
+    size_t payload_length;
+} EVP_RC4_HMAC_MD5;
+
+# define NO_PAYLOAD_LENGTH       ((size_t)-1)
+
+void rc4_md5_enc(RC4_KEY *key, const void *in0, void *out,
+                 MD5_CTX *ctx, const void *inp, size_t blocks);
+
+# define data(ctx) ((EVP_RC4_HMAC_MD5 *)EVP_CIPHER_CTX_get_cipher_data(ctx))
+
+static int rc4_hmac_md5_init_key(EVP_CIPHER_CTX *ctx,
+                                 const unsigned char *inkey,
+                                 const unsigned char *iv, int enc)
+{
+    EVP_RC4_HMAC_MD5 *key = data(ctx);
+
+    RC4_set_key(&key->ks, EVP_CIPHER_CTX_key_length(ctx), inkey);
+
+    MD5_Init(&key->head);       /* handy when benchmarking */
+    key->tail = key->head;
+    key->md = key->head;
+
+    key->payload_length = NO_PAYLOAD_LENGTH;
+
+    return 1;
+}
+
+# if     defined(RC4_ASM) && defined(MD5_ASM) &&     (	   \
+        defined(__x86_64)       || defined(__x86_64__)  || \
+        defined(_M_AMD64)       || defined(_M_X64)      )
+#  define STITCHED_CALL
+# endif
+
+# if !defined(STITCHED_CALL)
+#  define rc4_off 0
+#  define md5_off 0
+# endif
+
+static int rc4_hmac_md5_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
+                               const unsigned char *in, size_t len)
+{
+    EVP_RC4_HMAC_MD5 *key = data(ctx);
+# if defined(STITCHED_CALL)
+    size_t rc4_off = 32 - 1 - (key->ks.x & (32 - 1)), /* 32 is $MOD from
+                                                       * rc4_md5-x86_64.pl */
+        md5_off = MD5_CBLOCK - key->md.num, blocks;
+    unsigned int l;
+    extern unsigned int OPENSSL_ia32cap_P[];
+# endif
+    size_t plen = key->payload_length;
+
+    if (plen != NO_PAYLOAD_LENGTH && len != (plen + MD5_DIGEST_LENGTH))
+        return 0;
+
+    if (EVP_CIPHER_CTX_encrypting(ctx)) {
+        if (plen == NO_PAYLOAD_LENGTH)
+            plen = len;
+# if defined(STITCHED_CALL)
+        /* cipher has to "fall behind" */
+        if (rc4_off > md5_off)
+            md5_off += MD5_CBLOCK;
+
+        if (plen > md5_off && (blocks = (plen - md5_off) / MD5_CBLOCK) &&
+            (OPENSSL_ia32cap_P[0] & (1 << 20)) == 0) {
+            MD5_Update(&key->md, in, md5_off);
+            RC4(&key->ks, rc4_off, in, out);
+
+            rc4_md5_enc(&key->ks, in + rc4_off, out + rc4_off,
+                        &key->md, in + md5_off, blocks);
+            blocks *= MD5_CBLOCK;
+            rc4_off += blocks;
+            md5_off += blocks;
+            key->md.Nh += blocks >> 29;
+            key->md.Nl += blocks <<= 3;
+            if (key->md.Nl < (unsigned int)blocks)
+                key->md.Nh++;
+        } else {
+            rc4_off = 0;
+            md5_off = 0;
+        }
+# endif
+        MD5_Update(&key->md, in + md5_off, plen - md5_off);
+
+        if (plen != len) {      /* "TLS" mode of operation */
+            if (in != out)
+                memcpy(out + rc4_off, in + rc4_off, plen - rc4_off);
+
+            /* calculate HMAC and append it to payload */
+            MD5_Final(out + plen, &key->md);
+            key->md = key->tail;
+            MD5_Update(&key->md, out + plen, MD5_DIGEST_LENGTH);
+            MD5_Final(out + plen, &key->md);
+            /* encrypt HMAC at once */
+            RC4(&key->ks, len - rc4_off, out + rc4_off, out + rc4_off);
+        } else {
+            RC4(&key->ks, len - rc4_off, in + rc4_off, out + rc4_off);
+        }
+    } else {
+        unsigned char mac[MD5_DIGEST_LENGTH];
+# if defined(STITCHED_CALL)
+        /* digest has to "fall behind" */
+        if (md5_off > rc4_off)
+            rc4_off += 2 * MD5_CBLOCK;
+        else
+            rc4_off += MD5_CBLOCK;
+
+        if (len > rc4_off && (blocks = (len - rc4_off) / MD5_CBLOCK) &&
+            (OPENSSL_ia32cap_P[0] & (1 << 20)) == 0) {
+            RC4(&key->ks, rc4_off, in, out);
+            MD5_Update(&key->md, out, md5_off);
+
+            rc4_md5_enc(&key->ks, in + rc4_off, out + rc4_off,
+                        &key->md, out + md5_off, blocks);
+            blocks *= MD5_CBLOCK;
+            rc4_off += blocks;
+            md5_off += blocks;
+            l = (key->md.Nl + (blocks << 3)) & 0xffffffffU;
+            if (l < key->md.Nl)
+                key->md.Nh++;
+            key->md.Nl = l;
+            key->md.Nh += blocks >> 29;
+        } else {
+            md5_off = 0;
+            rc4_off = 0;
+        }
+# endif
+        /* decrypt HMAC at once */
+        RC4(&key->ks, len - rc4_off, in + rc4_off, out + rc4_off);
+        if (plen != NO_PAYLOAD_LENGTH) { /* "TLS" mode of operation */
+            MD5_Update(&key->md, out + md5_off, plen - md5_off);
+
+            /* calculate HMAC and verify it */
+            MD5_Final(mac, &key->md);
+            key->md = key->tail;
+            MD5_Update(&key->md, mac, MD5_DIGEST_LENGTH);
+            MD5_Final(mac, &key->md);
+
+            if (CRYPTO_memcmp(out + plen, mac, MD5_DIGEST_LENGTH))
+                return 0;
+        } else {
+            MD5_Update(&key->md, out + md5_off, len - md5_off);
+        }
+    }
+
+    key->payload_length = NO_PAYLOAD_LENGTH;
+
+    return 1;
+}
+
+static int rc4_hmac_md5_ctrl(EVP_CIPHER_CTX *ctx, int type, int arg,
+                             void *ptr)
+{
+    EVP_RC4_HMAC_MD5 *key = data(ctx);
+
+    switch (type) {
+    case EVP_CTRL_AEAD_SET_MAC_KEY:
+        {
+            unsigned int i;
+            unsigned char hmac_key[64];
+
+            memset(hmac_key, 0, sizeof(hmac_key));
+
+            if (arg > (int)sizeof(hmac_key)) {
+                MD5_Init(&key->head);
+                MD5_Update(&key->head, ptr, arg);
+                MD5_Final(hmac_key, &key->head);
+            } else {
+                memcpy(hmac_key, ptr, arg);
+            }
+
+            for (i = 0; i < sizeof(hmac_key); i++)
+                hmac_key[i] ^= 0x36; /* ipad */
+            MD5_Init(&key->head);
+            MD5_Update(&key->head, hmac_key, sizeof(hmac_key));
+
+            for (i = 0; i < sizeof(hmac_key); i++)
+                hmac_key[i] ^= 0x36 ^ 0x5c; /* opad */
+            MD5_Init(&key->tail);
+            MD5_Update(&key->tail, hmac_key, sizeof(hmac_key));
+
+            OPENSSL_cleanse(hmac_key, sizeof(hmac_key));
+
+            return 1;
+        }
+    case EVP_CTRL_AEAD_TLS1_AAD:
+        {
+            unsigned char *p = ptr;
+            unsigned int len;
+
+            if (arg != EVP_AEAD_TLS1_AAD_LEN)
+                return -1;
+
+            len = p[arg - 2] << 8 | p[arg - 1];
+
+            if (!EVP_CIPHER_CTX_encrypting(ctx)) {
+                if (len < MD5_DIGEST_LENGTH)
+                    return -1;
+                len -= MD5_DIGEST_LENGTH;
+                p[arg - 2] = len >> 8;
+                p[arg - 1] = len;
+            }
+            key->payload_length = len;
+            key->md = key->head;
+            MD5_Update(&key->md, p, arg);
+
+            return MD5_DIGEST_LENGTH;
+        }
+    default:
+        return -1;
+    }
+}
+
+static EVP_CIPHER r4_hmac_md5_cipher = {
+# ifdef NID_rc4_hmac_md5
+    NID_rc4_hmac_md5,
+# else
+    NID_undef,
+# endif
+    1, EVP_RC4_KEY_SIZE, 0,
+    EVP_CIPH_STREAM_CIPHER | EVP_CIPH_VARIABLE_LENGTH |
+        EVP_CIPH_FLAG_AEAD_CIPHER,
+    rc4_hmac_md5_init_key,
+    rc4_hmac_md5_cipher,
+    NULL,
+    sizeof(EVP_RC4_HMAC_MD5),
+    NULL,
+    NULL,
+    rc4_hmac_md5_ctrl,
+    NULL
+};
+
+const EVP_CIPHER *EVP_rc4_hmac_md5(void)
+{
+    return (&r4_hmac_md5_cipher);
+}
+#endif
diff --git a/crypto/evp/e_rc5.c b/crypto/evp/e_rc5.c
new file mode 100644
index 000000000..f69ba5b2f
--- /dev/null
+++ b/crypto/evp/e_rc5.c
@@ -0,0 +1,74 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+
+#ifndef OPENSSL_NO_RC5
+
+# include 
+# include 
+# include 
+# include "evp_locl.h"
+# include 
+
+static int r_32_12_16_init_key(EVP_CIPHER_CTX *ctx, const unsigned char *key,
+                               const unsigned char *iv, int enc);
+static int rc5_ctrl(EVP_CIPHER_CTX *c, int type, int arg, void *ptr);
+
+typedef struct {
+    int rounds;                 /* number of rounds */
+    RC5_32_KEY ks;              /* key schedule */
+} EVP_RC5_KEY;
+
+# define data(ctx)       EVP_C_DATA(EVP_RC5_KEY,ctx)
+
+IMPLEMENT_BLOCK_CIPHER(rc5_32_12_16, ks, RC5_32, EVP_RC5_KEY, NID_rc5,
+                       8, RC5_32_KEY_LENGTH, 8, 64,
+                       EVP_CIPH_VARIABLE_LENGTH | EVP_CIPH_CTRL_INIT,
+                       r_32_12_16_init_key, NULL, NULL, NULL, rc5_ctrl)
+
+static int rc5_ctrl(EVP_CIPHER_CTX *c, int type, int arg, void *ptr)
+{
+    switch (type) {
+    case EVP_CTRL_INIT:
+        data(c)->rounds = RC5_12_ROUNDS;
+        return 1;
+
+    case EVP_CTRL_GET_RC5_ROUNDS:
+        *(int *)ptr = data(c)->rounds;
+        return 1;
+
+    case EVP_CTRL_SET_RC5_ROUNDS:
+        switch (arg) {
+        case RC5_8_ROUNDS:
+        case RC5_12_ROUNDS:
+        case RC5_16_ROUNDS:
+            data(c)->rounds = arg;
+            return 1;
+
+        default:
+            EVPerr(EVP_F_RC5_CTRL, EVP_R_UNSUPPORTED_NUMBER_OF_ROUNDS);
+            return 0;
+        }
+
+    default:
+        return -1;
+    }
+}
+
+static int r_32_12_16_init_key(EVP_CIPHER_CTX *ctx, const unsigned char *key,
+                               const unsigned char *iv, int enc)
+{
+    RC5_32_set_key(&data(ctx)->ks, EVP_CIPHER_CTX_key_length(ctx),
+                   key, data(ctx)->rounds);
+    return 1;
+}
+
+#endif
diff --git a/crypto/evp/e_seed.c b/crypto/evp/e_seed.c
new file mode 100644
index 000000000..40aec5fc6
--- /dev/null
+++ b/crypto/evp/e_seed.c
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2007-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#ifdef OPENSSL_NO_SEED
+NON_EMPTY_TRANSLATION_UNIT
+#else
+# include 
+# include 
+# include 
+# include 
+# include 
+# include "internal/evp_int.h"
+
+static int seed_init_key(EVP_CIPHER_CTX *ctx, const unsigned char *key,
+                         const unsigned char *iv, int enc);
+
+typedef struct {
+    SEED_KEY_SCHEDULE ks;
+} EVP_SEED_KEY;
+
+IMPLEMENT_BLOCK_CIPHER(seed, ks, SEED, EVP_SEED_KEY, NID_seed,
+                       16, 16, 16, 128, EVP_CIPH_FLAG_DEFAULT_ASN1,
+                       seed_init_key, 0, 0, 0, 0)
+
+static int seed_init_key(EVP_CIPHER_CTX *ctx, const unsigned char *key,
+                         const unsigned char *iv, int enc)
+{
+    SEED_set_key(key, &EVP_C_DATA(EVP_SEED_KEY,ctx)->ks);
+    return 1;
+}
+
+#endif
diff --git a/crypto/evp/e_sms4.c b/crypto/evp/e_sms4.c
new file mode 100644
index 000000000..7600b4ae1
--- /dev/null
+++ b/crypto/evp/e_sms4.c
@@ -0,0 +1,244 @@
+/* ====================================================================
+ * Copyright (c) 2014 - 2017 The GmSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the GmSSL Project.
+ *    (http://gmssl.org/)"
+ *
+ * 4. The name "GmSSL Project" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    guanzhi1980@gmail.com.
+ *
+ * 5. Products derived from this software may not be called "GmSSL"
+ *    nor may "GmSSL" appear in their names without prior written
+ *    permission of the GmSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the GmSSL Project
+ *    (http://gmssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE GmSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE GmSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+ /*
+ * Copyright 2001-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include "evp_locl.h"
+#include "internal/evp_int.h"
+#include "modes_lcl.h"
+
+#ifndef OPENSSL_NO_SMS4
+
+# include 
+# include "sms4_lcl.h"
+
+# ifdef SMS4_AVX2
+void sms4_avx2_ecb_encrypt_blocks(const unsigned char *in,
+	unsigned char *out, size_t blocks, const sms4_key_t *key);
+void sms4_avx2_ctr32_encrypt_blocks(const unsigned char *in,
+	unsigned char *out, size_t blocks, const sms4_key_t *key,
+	const unsigned char iv[16]);
+# endif
+
+typedef struct {
+	block128_f block;
+	union {
+		cbc128_f cbc;
+		ctr128_f ctr;
+	} stream;
+	union {
+		double align;
+		sms4_key_t ks;
+	} ks;
+} EVP_SMS4_KEY;
+
+
+static int sms4_init_key(EVP_CIPHER_CTX *ctx, const unsigned char *key,
+	const unsigned char *iv, int enc)
+{
+	int mode;
+	EVP_SMS4_KEY *dat = EVP_C_DATA(EVP_SMS4_KEY, ctx);
+	mode = EVP_CIPHER_CTX_mode(ctx);
+
+	if ((mode == EVP_CIPH_ECB_MODE || mode == EVP_CIPH_CBC_MODE) && !enc) {
+		sms4_set_decrypt_key(&dat->ks.ks, key);
+	} else {
+		sms4_set_encrypt_key(&dat->ks.ks, key);
+	}
+	dat->block = (block128_f)sms4_encrypt;
+
+	if (mode == EVP_CIPH_CTR_MODE) {
+# ifdef SMS4_AVX2
+		dat->stream.ctr = (ctr128_f) sms4_avx2_ctr32_encrypt_blocks;
+# else
+		dat->stream.ctr = (ctr128_f) sms4_ctr32_encrypt_blocks;
+# endif
+	}
+
+	return 1;
+}
+
+static int sms4_ctrl(EVP_CIPHER_CTX *c, int type, int arg, void *ptr)
+{
+	switch (type) {
+	case EVP_CTRL_PBE_PRF_NID:
+		*(int *)ptr = NID_hmac_sm3;
+		return 1;
+	default:
+		return -1;
+	}
+}
+
+IMPLEMENT_BLOCK_CIPHER(sms4, ks.ks, sms4, EVP_SMS4_KEY, NID_sms4,
+	SMS4_BLOCK_SIZE, SMS4_KEY_LENGTH, SMS4_IV_LENGTH, 128,
+	EVP_CIPH_FLAG_DEFAULT_ASN1, sms4_init_key, NULL, NULL, NULL, sms4_ctrl)
+
+# define MAXBITCHUNK     ((size_t)1<<(sizeof(size_t)*8-4))
+
+static int sms4_cfb1_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
+	const unsigned char *in, size_t len)
+{
+	EVP_SMS4_KEY *sms4_key = (EVP_SMS4_KEY *)ctx->cipher_data;
+
+	if (ctx->flags & EVP_CIPH_FLAG_LENGTH_BITS) {
+		CRYPTO_cfb128_1_encrypt(in, out, len, &sms4_key->ks.ks,
+			ctx->iv, &ctx->num, ctx->encrypt, (block128_f)sms4_encrypt);
+		return 1;
+	}
+
+	while (len >= MAXBITCHUNK) {
+		CRYPTO_cfb128_1_encrypt(in, out, MAXBITCHUNK * 8, &sms4_key->ks.ks,
+			ctx->iv, &ctx->num, ctx->encrypt, (block128_f)sms4_encrypt);
+		len -= MAXBITCHUNK;
+	}
+
+	if (len) {
+		CRYPTO_cfb128_1_encrypt(in, out, len * 8, &sms4_key->ks.ks,
+			ctx->iv, &ctx->num, ctx->encrypt, (block128_f)sms4_encrypt);
+	}
+
+	return 1;
+}
+
+const EVP_CIPHER sms4_cfb1 = {
+	NID_sms4_cfb1,
+	1,
+	SMS4_KEY_LENGTH,
+	SMS4_IV_LENGTH,
+	EVP_CIPH_CTR_MODE,
+	sms4_init_key,
+	sms4_cfb1_cipher,
+	NULL,
+	sizeof(EVP_SMS4_KEY),
+	NULL,NULL,sms4_ctrl,NULL,
+};
+
+const EVP_CIPHER *EVP_sms4_cfb1(void)
+{
+	return &sms4_cfb1;
+}
+
+static int sms4_cfb8_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
+	const unsigned char *in, size_t len)
+{
+	EVP_SMS4_KEY *sms4_key = (EVP_SMS4_KEY *)ctx->cipher_data;
+
+	CRYPTO_cfb128_8_encrypt(in, out, len, &sms4_key->ks.ks,
+		ctx->iv, &ctx->num, ctx->encrypt, (block128_f)sms4_encrypt);
+
+	return 1;
+}
+
+const EVP_CIPHER sms4_cfb8 = {
+	NID_sms4_cfb8,
+	1,
+	SMS4_KEY_LENGTH,
+	SMS4_IV_LENGTH,
+	EVP_CIPH_CTR_MODE,
+	sms4_init_key,
+	sms4_cfb8_cipher,
+	NULL,
+	sizeof(EVP_SMS4_KEY),
+	NULL,NULL,sms4_ctrl,NULL,
+};
+
+const EVP_CIPHER *EVP_sms4_cfb8(void)
+{
+	return &sms4_cfb8;
+}
+
+static int sms4_ctr_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
+	const unsigned char *in, size_t len)
+{
+	unsigned int num = EVP_CIPHER_CTX_num(ctx);
+	EVP_SMS4_KEY *sms4 = (EVP_SMS4_KEY *)ctx->cipher_data;
+
+	if (sms4->stream.ctr)
+		CRYPTO_ctr128_encrypt_ctr32(in, out, len, &sms4->ks.ks,
+			EVP_CIPHER_CTX_iv_noconst(ctx),
+			EVP_CIPHER_CTX_buf_noconst(ctx),
+			&num, sms4->stream.ctr);
+	else
+		CRYPTO_ctr128_encrypt(in, out, len, &sms4->ks.ks,
+			EVP_CIPHER_CTX_iv_noconst(ctx),
+			EVP_CIPHER_CTX_buf_noconst(ctx), &num,
+			sms4->block);
+
+	EVP_CIPHER_CTX_set_num(ctx, num);
+	return 1;
+}
+
+const EVP_CIPHER sms4_ctr = {
+	NID_sms4_ctr,
+	1,
+	SMS4_KEY_LENGTH,
+	SMS4_IV_LENGTH,
+	EVP_CIPH_CTR_MODE,
+	sms4_init_key,
+	sms4_ctr_cipher,
+	NULL, /* cleanup() */
+	sizeof(EVP_SMS4_KEY),
+	NULL,NULL,sms4_ctrl,NULL,
+};
+
+const EVP_CIPHER *EVP_sms4_ctr(void)
+{
+	return &sms4_ctr;
+}
+#endif /* OPENSSL_NO_SMS4 */
diff --git a/crypto/evp/e_sms4_ccm.c b/crypto/evp/e_sms4_ccm.c
new file mode 100644
index 000000000..4d0cfd6bf
--- /dev/null
+++ b/crypto/evp/e_sms4_ccm.c
@@ -0,0 +1,340 @@
+/* ====================================================================
+ * Copyright (c) 2014 - 2017 The GmSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the GmSSL Project.
+ *    (http://gmssl.org/)"
+ *
+ * 4. The name "GmSSL Project" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    guanzhi1980@gmail.com.
+ *
+ * 5. Products derived from this software may not be called "GmSSL"
+ *    nor may "GmSSL" appear in their names without prior written
+ *    permission of the GmSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the GmSSL Project
+ *    (http://gmssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE GmSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE GmSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+ /*
+ * Copyright 2001-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+
+#include 
+#include 
+#include 
+#include "evp_locl.h"
+#include "internal/evp_int.h"
+#include "modes_lcl.h"
+
+#ifndef OPENSSL_NO_SMS4
+
+# include 
+
+typedef struct {
+    union {
+        double align;
+        sms4_key_t ks;
+    } ks;                       /* SMS4 key schedule to use */
+    int key_set;                /* Set if key initialised */
+    int iv_set;                 /* Set if an iv is set */
+    int tag_set;                /* Set if tag is valid */
+    int len_set;                /* Set if message length set */
+    int L, M;                   /* L and M parameters from RFC3610 */
+    int tls_aad_len;            /* TLS AAD length */
+    CCM128_CONTEXT ccm;
+    ccm128_f str;
+} EVP_SMS4_CCM_CTX;
+
+static int sms4_ccm_ctrl(EVP_CIPHER_CTX *c, int type, int arg, void *ptr)
+{
+    EVP_SMS4_CCM_CTX *cctx = EVP_C_DATA(EVP_SMS4_CCM_CTX,c);
+    switch (type) {
+    case EVP_CTRL_INIT:
+        cctx->key_set = 0;
+        cctx->iv_set = 0;
+        cctx->L = 8;
+        cctx->M = 12;
+        cctx->tag_set = 0;
+        cctx->len_set = 0;
+        cctx->tls_aad_len = -1;
+        return 1;
+
+    case EVP_CTRL_AEAD_TLS1_AAD:
+        /* Save the AAD for later use */
+        if (arg != EVP_AEAD_TLS1_AAD_LEN)
+            return 0;
+        memcpy(EVP_CIPHER_CTX_buf_noconst(c), ptr, arg);
+        cctx->tls_aad_len = arg;
+        {
+            uint16_t len =
+                EVP_CIPHER_CTX_buf_noconst(c)[arg - 2] << 8
+                | EVP_CIPHER_CTX_buf_noconst(c)[arg - 1];
+            /* Correct length for explicit IV */
+            len -= EVP_CCM_TLS_EXPLICIT_IV_LEN;
+            /* If decrypting correct for tag too */
+            if (!EVP_CIPHER_CTX_encrypting(c))
+                len -= cctx->M;
+            EVP_CIPHER_CTX_buf_noconst(c)[arg - 2] = len >> 8;
+            EVP_CIPHER_CTX_buf_noconst(c)[arg - 1] = len & 0xff;
+        }
+        /* Extra padding: tag appended to record */
+        return cctx->M;
+
+    case EVP_CTRL_CCM_SET_IV_FIXED:
+        /* Sanity check length */
+        if (arg != EVP_CCM_TLS_FIXED_IV_LEN)
+            return 0;
+        /* Just copy to first part of IV */
+        memcpy(EVP_CIPHER_CTX_iv_noconst(c), ptr, arg);
+        return 1;
+
+    case EVP_CTRL_AEAD_SET_IVLEN:
+        arg = 15 - arg;
+    case EVP_CTRL_CCM_SET_L:
+        if (arg < 2 || arg > 8)
+            return 0;
+        cctx->L = arg;
+        return 1;
+
+    case EVP_CTRL_AEAD_SET_TAG:
+        if ((arg & 1) || arg < 4 || arg > 16)
+            return 0;
+        if (EVP_CIPHER_CTX_encrypting(c) && ptr)
+            return 0;
+        if (ptr) {
+            cctx->tag_set = 1;
+            memcpy(EVP_CIPHER_CTX_buf_noconst(c), ptr, arg);
+        }
+        cctx->M = arg;
+        return 1;
+
+    case EVP_CTRL_AEAD_GET_TAG:
+        if (!EVP_CIPHER_CTX_encrypting(c) || !cctx->tag_set)
+            return 0;
+        if (!CRYPTO_ccm128_tag(&cctx->ccm, ptr, (size_t)arg))
+            return 0;
+        cctx->tag_set = 0;
+        cctx->iv_set = 0;
+        cctx->len_set = 0;
+        return 1;
+
+    case EVP_CTRL_COPY:
+        {
+            EVP_CIPHER_CTX *out = ptr;
+            EVP_SMS4_CCM_CTX *cctx_out = EVP_C_DATA(EVP_SMS4_CCM_CTX,out);
+            if (cctx->ccm.key) {
+                if (cctx->ccm.key != &cctx->ks)
+                    return 0;
+                cctx_out->ccm.key = &cctx_out->ks;
+            }
+            return 1;
+        }
+
+    default:
+        return -1;
+
+    }
+}
+
+static int sms4_ccm_init_key(EVP_CIPHER_CTX *ctx, const unsigned char *key,
+                             const unsigned char *iv, int enc)
+{
+    EVP_SMS4_CCM_CTX *cctx = EVP_C_DATA(EVP_SMS4_CCM_CTX,ctx);
+    if (!iv && !key)
+        return 1;
+    if (key)
+        do {
+            sms4_set_encrypt_key(&cctx->ks.ks, key);
+            CRYPTO_ccm128_init(&cctx->ccm, cctx->M, cctx->L,
+                               &cctx->ks, (block128_f) sms4_encrypt);
+            cctx->str = NULL;
+            cctx->key_set = 1;
+        } while (0);
+    if (iv) {
+        memcpy(EVP_CIPHER_CTX_iv_noconst(ctx), iv, 15 - cctx->L);
+        cctx->iv_set = 1;
+    }
+    return 1;
+}
+
+static int sms4_ccm_tls_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
+                               const unsigned char *in, size_t len)
+{
+    EVP_SMS4_CCM_CTX *cctx = EVP_C_DATA(EVP_SMS4_CCM_CTX,ctx);
+    CCM128_CONTEXT *ccm = &cctx->ccm;
+    /* Encrypt/decrypt must be performed in place */
+    if (out != in || len < (EVP_CCM_TLS_EXPLICIT_IV_LEN + (size_t)cctx->M))
+        return -1;
+    /* If encrypting set explicit IV from sequence number (start of AAD) */
+    if (EVP_CIPHER_CTX_encrypting(ctx))
+        memcpy(out, EVP_CIPHER_CTX_buf_noconst(ctx),
+               EVP_CCM_TLS_EXPLICIT_IV_LEN);
+    /* Get rest of IV from explicit IV */
+    memcpy(EVP_CIPHER_CTX_iv_noconst(ctx) + EVP_CCM_TLS_FIXED_IV_LEN, in,
+           EVP_CCM_TLS_EXPLICIT_IV_LEN);
+    /* Correct length value */
+    len -= EVP_CCM_TLS_EXPLICIT_IV_LEN + cctx->M;
+    if (CRYPTO_ccm128_setiv(ccm, EVP_CIPHER_CTX_iv_noconst(ctx), 15 - cctx->L,
+                            len))
+            return -1;
+    /* Use saved AAD */
+    CRYPTO_ccm128_aad(ccm, EVP_CIPHER_CTX_buf_noconst(ctx), cctx->tls_aad_len);
+    /* Fix buffer to point to payload */
+    in += EVP_CCM_TLS_EXPLICIT_IV_LEN;
+    out += EVP_CCM_TLS_EXPLICIT_IV_LEN;
+    if (EVP_CIPHER_CTX_encrypting(ctx)) {
+        if (cctx->str ? CRYPTO_ccm128_encrypt_ccm64(ccm, in, out, len,
+                                                    cctx->str) :
+            CRYPTO_ccm128_encrypt(ccm, in, out, len))
+            return -1;
+        if (!CRYPTO_ccm128_tag(ccm, out + len, cctx->M))
+            return -1;
+        return len + EVP_CCM_TLS_EXPLICIT_IV_LEN + cctx->M;
+    } else {
+        if (cctx->str ? !CRYPTO_ccm128_decrypt_ccm64(ccm, in, out, len,
+                                                     cctx->str) :
+            !CRYPTO_ccm128_decrypt(ccm, in, out, len)) {
+            unsigned char tag[16];
+            if (CRYPTO_ccm128_tag(ccm, tag, cctx->M)) {
+                if (!CRYPTO_memcmp(tag, in + len, cctx->M))
+                    return len;
+            }
+        }
+        OPENSSL_cleanse(out, len);
+        return -1;
+    }
+}
+
+static int sms4_ccm_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
+                           const unsigned char *in, size_t len)
+{
+    EVP_SMS4_CCM_CTX *cctx = EVP_C_DATA(EVP_SMS4_CCM_CTX,ctx);
+    CCM128_CONTEXT *ccm = &cctx->ccm;
+    /* If not set up, return error */
+    if (!cctx->key_set)
+        return -1;
+
+    if (cctx->tls_aad_len >= 0)
+        return sms4_ccm_tls_cipher(ctx, out, in, len);
+
+    if (!cctx->iv_set)
+        return -1;
+
+    if (!EVP_CIPHER_CTX_encrypting(ctx) && !cctx->tag_set)
+        return -1;
+    if (!out) {
+        if (!in) {
+            if (CRYPTO_ccm128_setiv(ccm, EVP_CIPHER_CTX_iv_noconst(ctx),
+                                    15 - cctx->L, len))
+                return -1;
+            cctx->len_set = 1;
+            return len;
+        }
+        /* If have AAD need message length */
+        if (!cctx->len_set && len)
+            return -1;
+        CRYPTO_ccm128_aad(ccm, in, len);
+        return len;
+    }
+    /* EVP_*Final() doesn't return any data */
+    if (!in)
+        return 0;
+    /* If not set length yet do it */
+    if (!cctx->len_set) {
+        if (CRYPTO_ccm128_setiv(ccm, EVP_CIPHER_CTX_iv_noconst(ctx),
+                                15 - cctx->L, len))
+            return -1;
+        cctx->len_set = 1;
+    }
+    if (EVP_CIPHER_CTX_encrypting(ctx)) {
+        if (cctx->str ? CRYPTO_ccm128_encrypt_ccm64(ccm, in, out, len,
+                                                    cctx->str) :
+            CRYPTO_ccm128_encrypt(ccm, in, out, len))
+            return -1;
+        cctx->tag_set = 1;
+        return len;
+    } else {
+        int rv = -1;
+        if (cctx->str ? !CRYPTO_ccm128_decrypt_ccm64(ccm, in, out, len,
+                                                     cctx->str) :
+            !CRYPTO_ccm128_decrypt(ccm, in, out, len)) {
+            unsigned char tag[16];
+            if (CRYPTO_ccm128_tag(ccm, tag, cctx->M)) {
+                if (!CRYPTO_memcmp(tag, EVP_CIPHER_CTX_buf_noconst(ctx),
+                                   cctx->M))
+                    rv = len;
+            }
+        }
+        if (rv == -1)
+            OPENSSL_cleanse(out, len);
+        cctx->iv_set = 0;
+        cctx->tag_set = 0;
+        cctx->len_set = 0;
+        return rv;
+    }
+}
+
+# define SMS4_CCM_BLOCK_SIZE	1
+# define SMS4_CCM_IV_LENGTH	7
+
+# define SMS4_CCM_FLAGS (EVP_CIPH_FLAG_DEFAULT_ASN1 \
+                | EVP_CIPH_CUSTOM_IV | EVP_CIPH_FLAG_CUSTOM_CIPHER \
+                | EVP_CIPH_ALWAYS_CALL_INIT | EVP_CIPH_CTRL_INIT \
+                | EVP_CIPH_CUSTOM_COPY \
+		| EVP_CIPH_FLAG_AEAD_CIPHER)
+
+static const EVP_CIPHER sms4_ccm = {
+	NID_sms4_ccm,
+	SMS4_CCM_BLOCK_SIZE,
+	SMS4_KEY_LENGTH,
+	SMS4_CCM_IV_LENGTH,
+	SMS4_CCM_FLAGS,
+	sms4_ccm_init_key,
+	sms4_ccm_cipher,
+	NULL,
+	sizeof(EVP_SMS4_CCM_CTX),
+	NULL, NULL, sms4_ccm_ctrl, NULL
+};
+
+const EVP_CIPHER *EVP_sms4_ccm(void) {
+	return &sms4_ccm;
+}
+#endif /* OPENSSL_NO_SMS4 */
diff --git a/crypto/evp/e_sms4_gcm.c b/crypto/evp/e_sms4_gcm.c
new file mode 100644
index 000000000..5993a522d
--- /dev/null
+++ b/crypto/evp/e_sms4_gcm.c
@@ -0,0 +1,463 @@
+/* ====================================================================
+ * Copyright (c) 2014 - 2017 The GmSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the GmSSL Project.
+ *    (http://gmssl.org/)"
+ *
+ * 4. The name "GmSSL Project" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    guanzhi1980@gmail.com.
+ *
+ * 5. Products derived from this software may not be called "GmSSL"
+ *    nor may "GmSSL" appear in their names without prior written
+ *    permission of the GmSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the GmSSL Project
+ *    (http://gmssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE GmSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE GmSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+ /*
+ * Copyright 2001-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+
+#include 
+#include 
+#include 
+#include 
+#include "evp_locl.h"
+#include "internal/evp_int.h"
+#include "modes_lcl.h"
+
+#ifndef OPENSSL_NO_SMS4
+
+# include 
+
+typedef struct {
+    union {
+        double align;
+        sms4_key_t ks;
+    } ks;                       /* SMS4 key schedule to use */
+    int key_set;                /* Set if key initialised */
+    int iv_set;                 /* Set if an iv is set */
+    GCM128_CONTEXT gcm;
+    unsigned char *iv;          /* Temporary IV store */
+    int ivlen;                  /* IV length */
+    int taglen;
+    int iv_gen;                 /* It is OK to generate IVs */
+    int tls_aad_len;            /* TLS AAD length */
+    ctr128_f ctr;
+} EVP_SMS4_GCM_CTX;
+
+
+static int sms4_gcm_cleanup(EVP_CIPHER_CTX *c)
+{
+    EVP_SMS4_GCM_CTX *gctx = EVP_C_DATA(EVP_SMS4_GCM_CTX,c);
+    OPENSSL_cleanse(&gctx->gcm, sizeof(gctx->gcm));
+    if (gctx->iv != EVP_CIPHER_CTX_iv_noconst(c))
+        OPENSSL_free(gctx->iv);
+    return 1;
+}
+
+/* increment counter (64-bit int) by 1 */
+static void ctr64_inc(unsigned char *counter)
+{
+    int n = 8;
+    unsigned char c;
+
+    do {
+        --n;
+        c = counter[n];
+        ++c;
+        counter[n] = c;
+        if (c)
+            return;
+    } while (n);
+}
+
+static int sms4_gcm_ctrl(EVP_CIPHER_CTX *c, int type, int arg, void *ptr)
+{
+    EVP_SMS4_GCM_CTX *gctx = EVP_C_DATA(EVP_SMS4_GCM_CTX,c);
+    switch (type) {
+    case EVP_CTRL_INIT:
+        gctx->key_set = 0;
+        gctx->iv_set = 0;
+        gctx->ivlen = EVP_CIPHER_CTX_iv_length(c);
+        gctx->iv = EVP_CIPHER_CTX_iv_noconst(c);
+        gctx->taglen = -1;
+        gctx->iv_gen = 0;
+        gctx->tls_aad_len = -1;
+        return 1;
+
+    case EVP_CTRL_AEAD_SET_IVLEN:
+        if (arg <= 0)
+            return 0;
+        /* Allocate memory for IV if needed */
+        if ((arg > EVP_MAX_IV_LENGTH) && (arg > gctx->ivlen)) {
+            if (gctx->iv != EVP_CIPHER_CTX_iv_noconst(c))
+                OPENSSL_free(gctx->iv);
+            gctx->iv = OPENSSL_malloc(arg);
+            if (gctx->iv == NULL)
+                return 0;
+        }
+        gctx->ivlen = arg;
+        return 1;
+
+    case EVP_CTRL_AEAD_SET_TAG:
+        if (arg <= 0 || arg > 16 || EVP_CIPHER_CTX_encrypting(c))
+            return 0;
+        memcpy(EVP_CIPHER_CTX_buf_noconst(c), ptr, arg);
+        gctx->taglen = arg;
+        return 1;
+
+    case EVP_CTRL_AEAD_GET_TAG:
+        if (arg <= 0 || arg > 16 || !EVP_CIPHER_CTX_encrypting(c)
+            || gctx->taglen < 0)
+            return 0;
+        memcpy(ptr, EVP_CIPHER_CTX_buf_noconst(c), arg);
+        return 1;
+
+    case EVP_CTRL_GCM_SET_IV_FIXED:
+        /* Special case: -1 length restores whole IV */
+        if (arg == -1) {
+            memcpy(gctx->iv, ptr, gctx->ivlen);
+            gctx->iv_gen = 1;
+            return 1;
+        }
+        /*
+         * Fixed field must be at least 4 bytes and invocation field at least
+         * 8.
+         */
+        if ((arg < 4) || (gctx->ivlen - arg) < 8)
+            return 0;
+        if (arg)
+            memcpy(gctx->iv, ptr, arg);
+        if (EVP_CIPHER_CTX_encrypting(c)
+            && RAND_bytes(gctx->iv + arg, gctx->ivlen - arg) <= 0)
+            return 0;
+        gctx->iv_gen = 1;
+        return 1;
+
+    case EVP_CTRL_GCM_IV_GEN:
+        if (gctx->iv_gen == 0 || gctx->key_set == 0)
+            return 0;
+        CRYPTO_gcm128_setiv(&gctx->gcm, gctx->iv, gctx->ivlen);
+        if (arg <= 0 || arg > gctx->ivlen)
+            arg = gctx->ivlen;
+        memcpy(ptr, gctx->iv + gctx->ivlen - arg, arg);
+        /*
+         * Invocation field will be at least 8 bytes in size and so no need
+         * to check wrap around or increment more than last 8 bytes.
+         */
+        ctr64_inc(gctx->iv + gctx->ivlen - 8);
+        gctx->iv_set = 1;
+        return 1;
+
+    case EVP_CTRL_GCM_SET_IV_INV:
+        if (gctx->iv_gen == 0 || gctx->key_set == 0
+            || EVP_CIPHER_CTX_encrypting(c))
+            return 0;
+        memcpy(gctx->iv + gctx->ivlen - arg, ptr, arg);
+        CRYPTO_gcm128_setiv(&gctx->gcm, gctx->iv, gctx->ivlen);
+        gctx->iv_set = 1;
+        return 1;
+
+    case EVP_CTRL_AEAD_TLS1_AAD:
+        /* Save the AAD for later use */
+        if (arg != EVP_AEAD_TLS1_AAD_LEN)
+            return 0;
+        memcpy(EVP_CIPHER_CTX_buf_noconst(c), ptr, arg);
+        gctx->tls_aad_len = arg;
+        {
+            unsigned int len =
+                EVP_CIPHER_CTX_buf_noconst(c)[arg - 2] << 8
+                | EVP_CIPHER_CTX_buf_noconst(c)[arg - 1];
+            /* Correct length for explicit IV */
+            len -= EVP_GCM_TLS_EXPLICIT_IV_LEN;
+            /* If decrypting correct for tag too */
+            if (!EVP_CIPHER_CTX_encrypting(c))
+                len -= EVP_GCM_TLS_TAG_LEN;
+            EVP_CIPHER_CTX_buf_noconst(c)[arg - 2] = len >> 8;
+            EVP_CIPHER_CTX_buf_noconst(c)[arg - 1] = len & 0xff;
+        }
+        /* Extra padding: tag appended to record */
+        return EVP_GCM_TLS_TAG_LEN;
+
+    case EVP_CTRL_COPY:
+        {
+            EVP_CIPHER_CTX *out = ptr;
+            EVP_SMS4_GCM_CTX *gctx_out = EVP_C_DATA(EVP_SMS4_GCM_CTX,out);
+            if (gctx->gcm.key) {
+                if (gctx->gcm.key != &gctx->ks)
+                    return 0;
+                gctx_out->gcm.key = &gctx_out->ks;
+            }
+            if (gctx->iv == EVP_CIPHER_CTX_iv_noconst(c))
+                gctx_out->iv = EVP_CIPHER_CTX_iv_noconst(out);
+            else {
+                gctx_out->iv = OPENSSL_malloc(gctx->ivlen);
+                if (gctx_out->iv == NULL)
+                    return 0;
+                memcpy(gctx_out->iv, gctx->iv, gctx->ivlen);
+            }
+            return 1;
+        }
+
+    default:
+        return -1;
+
+    }
+}
+
+static int sms4_gcm_init_key(EVP_CIPHER_CTX *ctx, const unsigned char *key,
+                             const unsigned char *iv, int enc)
+{
+    EVP_SMS4_GCM_CTX *gctx = EVP_C_DATA(EVP_SMS4_GCM_CTX,ctx);
+    if (!iv && !key)
+        return 1;
+    if (key) {
+        do {
+            (void)0;        /* terminate potentially open 'else' */
+
+            sms4_set_encrypt_key(&gctx->ks.ks, key);
+            CRYPTO_gcm128_init(&gctx->gcm, &gctx->ks,
+                               (block128_f)sms4_encrypt);
+            gctx->ctr = NULL;
+        } while (0);
+
+        /*
+         * If we have an iv can set it directly, otherwise use saved IV.
+         */
+        if (iv == NULL && gctx->iv_set)
+            iv = gctx->iv;
+        if (iv) {
+            CRYPTO_gcm128_setiv(&gctx->gcm, iv, gctx->ivlen);
+            gctx->iv_set = 1;
+        }
+        gctx->key_set = 1;
+    } else {
+        /* If key set use IV, otherwise copy */
+        if (gctx->key_set)
+            CRYPTO_gcm128_setiv(&gctx->gcm, iv, gctx->ivlen);
+        else
+            memcpy(gctx->iv, iv, gctx->ivlen);
+        gctx->iv_set = 1;
+        gctx->iv_gen = 0;
+    }
+    return 1;
+}
+
+/*
+ * Handle TLS GCM packet format. This consists of the last portion of the IV
+ * followed by the payload and finally the tag. On encrypt generate IV,
+ * encrypt payload and write the tag. On verify retrieve IV, decrypt payload
+ * and verify tag.
+ */
+
+static int sms4_gcm_tls_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
+                               const unsigned char *in, size_t len)
+{
+    EVP_SMS4_GCM_CTX *gctx = EVP_C_DATA(EVP_SMS4_GCM_CTX,ctx);
+    int rv = -1;
+    /* Encrypt/decrypt must be performed in place */
+    if (out != in
+        || len < (EVP_GCM_TLS_EXPLICIT_IV_LEN + EVP_GCM_TLS_TAG_LEN))
+        return -1;
+    /*
+     * Set IV from start of buffer or generate IV and write to start of
+     * buffer.
+     */
+    if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CIPHER_CTX_encrypting(ctx) ?
+                            EVP_CTRL_GCM_IV_GEN : EVP_CTRL_GCM_SET_IV_INV,
+                            EVP_GCM_TLS_EXPLICIT_IV_LEN, out) <= 0)
+        goto err;
+    /* Use saved AAD */
+    if (CRYPTO_gcm128_aad(&gctx->gcm, EVP_CIPHER_CTX_buf_noconst(ctx),
+                          gctx->tls_aad_len))
+        goto err;
+    /* Fix buffer and length to point to payload */
+    in += EVP_GCM_TLS_EXPLICIT_IV_LEN;
+    out += EVP_GCM_TLS_EXPLICIT_IV_LEN;
+    len -= EVP_GCM_TLS_EXPLICIT_IV_LEN + EVP_GCM_TLS_TAG_LEN;
+    if (EVP_CIPHER_CTX_encrypting(ctx)) {
+        /* Encrypt payload */
+        if (gctx->ctr) {
+            size_t bulk = 0;
+            if (CRYPTO_gcm128_encrypt_ctr32(&gctx->gcm,
+                                            in + bulk,
+                                            out + bulk,
+                                            len - bulk, gctx->ctr))
+                goto err;
+        } else {
+            size_t bulk = 0;
+            if (CRYPTO_gcm128_encrypt(&gctx->gcm,
+                                      in + bulk, out + bulk, len - bulk))
+                goto err;
+        }
+        out += len;
+        /* Finally write tag */
+        CRYPTO_gcm128_tag(&gctx->gcm, out, EVP_GCM_TLS_TAG_LEN);
+        rv = len + EVP_GCM_TLS_EXPLICIT_IV_LEN + EVP_GCM_TLS_TAG_LEN;
+    } else {
+        /* Decrypt */
+        if (gctx->ctr) {
+            size_t bulk = 0;
+            if (CRYPTO_gcm128_decrypt_ctr32(&gctx->gcm,
+                                            in + bulk,
+                                            out + bulk,
+                                            len - bulk, gctx->ctr))
+                goto err;
+        } else {
+            size_t bulk = 0;
+            if (CRYPTO_gcm128_decrypt(&gctx->gcm,
+                                      in + bulk, out + bulk, len - bulk))
+                goto err;
+        }
+        /* Retrieve tag */
+        CRYPTO_gcm128_tag(&gctx->gcm, EVP_CIPHER_CTX_buf_noconst(ctx),
+                          EVP_GCM_TLS_TAG_LEN);
+        /* If tag mismatch wipe buffer */
+        if (CRYPTO_memcmp(EVP_CIPHER_CTX_buf_noconst(ctx), in + len,
+                          EVP_GCM_TLS_TAG_LEN)) {
+            OPENSSL_cleanse(out, len);
+            goto err;
+        }
+        rv = len;
+    }
+
+ err:
+    gctx->iv_set = 0;
+    gctx->tls_aad_len = -1;
+    return rv;
+}
+
+static int sms4_gcm_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
+                           const unsigned char *in, size_t len)
+{
+    EVP_SMS4_GCM_CTX *gctx = EVP_C_DATA(EVP_SMS4_GCM_CTX,ctx);
+    /* If not set up, return error */
+    if (!gctx->key_set)
+        return -1;
+
+    if (gctx->tls_aad_len >= 0)
+        return sms4_gcm_tls_cipher(ctx, out, in, len);
+
+    if (!gctx->iv_set)
+        return -1;
+    if (in) {
+        if (out == NULL) {
+            if (CRYPTO_gcm128_aad(&gctx->gcm, in, len))
+                return -1;
+        } else if (EVP_CIPHER_CTX_encrypting(ctx)) {
+            if (gctx->ctr) {
+                size_t bulk = 0;
+                if (CRYPTO_gcm128_encrypt_ctr32(&gctx->gcm,
+                                                in + bulk,
+                                                out + bulk,
+                                                len - bulk, gctx->ctr))
+                    return -1;
+            } else {
+                size_t bulk = 0;
+                if (CRYPTO_gcm128_encrypt(&gctx->gcm,
+                                          in + bulk, out + bulk, len - bulk))
+                    return -1;
+            }
+        } else {
+            if (gctx->ctr) {
+                size_t bulk = 0;
+                if (CRYPTO_gcm128_decrypt_ctr32(&gctx->gcm,
+                                                in + bulk,
+                                                out + bulk,
+                                                len - bulk, gctx->ctr))
+                    return -1;
+            } else {
+                size_t bulk = 0;
+                if (CRYPTO_gcm128_decrypt(&gctx->gcm,
+                                          in + bulk, out + bulk, len - bulk))
+                    return -1;
+            }
+        }
+        return len;
+    } else {
+        if (!EVP_CIPHER_CTX_encrypting(ctx)) {
+            if (gctx->taglen < 0)
+                return -1;
+            if (CRYPTO_gcm128_finish(&gctx->gcm,
+                                     EVP_CIPHER_CTX_buf_noconst(ctx),
+                                     gctx->taglen) != 0)
+                return -1;
+            gctx->iv_set = 0;
+            return 0;
+        }
+        CRYPTO_gcm128_tag(&gctx->gcm, EVP_CIPHER_CTX_buf_noconst(ctx), 16);
+        gctx->taglen = 16;
+        /* Don't reuse the IV */
+        gctx->iv_set = 0;
+        return 0;
+    }
+
+}
+
+# define SMS4_GCM_BLOCK_SIZE	1
+# define SMS4_GCM_IV_LENGTH	12
+
+# define SMS4_GCM_FLAGS (EVP_CIPH_FLAG_DEFAULT_ASN1 \
+                | EVP_CIPH_CUSTOM_IV | EVP_CIPH_FLAG_CUSTOM_CIPHER \
+                | EVP_CIPH_ALWAYS_CALL_INIT | EVP_CIPH_CTRL_INIT \
+                | EVP_CIPH_CUSTOM_COPY | EVP_CIPH_GCM_MODE \
+		| EVP_CIPH_FLAG_AEAD_CIPHER)
+
+static const EVP_CIPHER sms4_gcm = {
+	NID_sms4_gcm,
+	SMS4_GCM_BLOCK_SIZE,
+	SMS4_KEY_LENGTH,
+	SMS4_GCM_IV_LENGTH,
+	SMS4_GCM_FLAGS,
+	sms4_gcm_init_key,
+	sms4_gcm_cipher,
+	sms4_gcm_cleanup,
+	sizeof(EVP_SMS4_GCM_CTX),
+	NULL, NULL, sms4_gcm_ctrl, NULL
+};
+
+const EVP_CIPHER *EVP_sms4_gcm(void) {
+	return &sms4_gcm;
+}
+
+#endif /* OPENSSL_NO_SMS4 */
diff --git a/crypto/evp/e_sms4_ocb.c b/crypto/evp/e_sms4_ocb.c
new file mode 100644
index 000000000..be617d658
--- /dev/null
+++ b/crypto/evp/e_sms4_ocb.c
@@ -0,0 +1,373 @@
+/* ====================================================================
+ * Copyright (c) 2014 - 2017 The GmSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the GmSSL Project.
+ *    (http://gmssl.org/)"
+ *
+ * 4. The name "GmSSL Project" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    guanzhi1980@gmail.com.
+ *
+ * 5. Products derived from this software may not be called "GmSSL"
+ *    nor may "GmSSL" appear in their names without prior written
+ *    permission of the GmSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the GmSSL Project
+ *    (http://gmssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE GmSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE GmSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+ /*
+ * Copyright 2001-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+
+#include 
+#include 
+#include 
+#include "evp_locl.h"
+#include "internal/evp_int.h"
+#include "modes_lcl.h"
+
+#ifndef OPENSSL_NO_SMS4
+
+# include 
+
+# ifndef OPENSSL_NO_OCB
+
+typedef struct {
+    union {
+        double align;
+        sms4_key_t ks;
+    } ksenc;                    /* SMS4 key schedule to use for encryption */
+    union {
+        double align;
+        sms4_key_t ks;
+    } ksdec;                    /* SMS4 key schedule to use for decryption */
+    int key_set;                /* Set if key initialised */
+    int iv_set;                 /* Set if an iv is set */
+    OCB128_CONTEXT ocb;
+    unsigned char *iv;          /* Temporary IV store */
+    unsigned char tag[16];
+    unsigned char data_buf[16]; /* Store partial data blocks */
+    unsigned char aad_buf[16];  /* Store partial AAD blocks */
+    int data_buf_len;
+    int aad_buf_len;
+    int ivlen;                  /* IV length */
+    int taglen;
+} EVP_SMS4_OCB_CTX;
+
+static int sms4_ocb_ctrl(EVP_CIPHER_CTX *c, int type, int arg, void *ptr)
+{
+    EVP_SMS4_OCB_CTX *octx = EVP_C_DATA(EVP_SMS4_OCB_CTX,c);
+    EVP_CIPHER_CTX *newc;
+    EVP_SMS4_OCB_CTX *new_octx;
+
+    switch (type) {
+    case EVP_CTRL_INIT:
+        octx->key_set = 0;
+        octx->iv_set = 0;
+        octx->ivlen = EVP_CIPHER_CTX_iv_length(c);
+        octx->iv = EVP_CIPHER_CTX_iv_noconst(c);
+        octx->taglen = 16;
+        octx->data_buf_len = 0;
+        octx->aad_buf_len = 0;
+        return 1;
+
+    case EVP_CTRL_AEAD_SET_IVLEN:
+        /* IV len must be 1 to 15 */
+        if (arg <= 0 || arg > 15)
+            return 0;
+
+        octx->ivlen = arg;
+        return 1;
+
+    case EVP_CTRL_AEAD_SET_TAG:
+        if (!ptr) {
+            /* Tag len must be 0 to 16 */
+            if (arg < 0 || arg > 16)
+                return 0;
+
+            octx->taglen = arg;
+            return 1;
+        }
+        if (arg != octx->taglen || EVP_CIPHER_CTX_encrypting(c))
+            return 0;
+        memcpy(octx->tag, ptr, arg);
+        return 1;
+
+    case EVP_CTRL_AEAD_GET_TAG:
+        if (arg != octx->taglen || !EVP_CIPHER_CTX_encrypting(c))
+            return 0;
+
+        memcpy(ptr, octx->tag, arg);
+        return 1;
+
+    case EVP_CTRL_COPY:
+        newc = (EVP_CIPHER_CTX *)ptr;
+        new_octx = EVP_C_DATA(EVP_SMS4_OCB_CTX,newc);
+        return CRYPTO_ocb128_copy_ctx(&new_octx->ocb, &octx->ocb,
+                                      &new_octx->ksenc.ks,
+                                      &new_octx->ksdec.ks);
+
+    default:
+        return -1;
+
+    }
+}
+
+static int sms4_ocb_init_key(EVP_CIPHER_CTX *ctx, const unsigned char *key,
+                             const unsigned char *iv, int enc)
+{
+    EVP_SMS4_OCB_CTX *octx = EVP_C_DATA(EVP_SMS4_OCB_CTX,ctx);
+    if (!iv && !key)
+        return 1;
+    if (key) {
+        do {
+            /*
+             * We set both the encrypt and decrypt key here because decrypt
+             * needs both. We could possibly optimise to remove setting the
+             * decrypt for an encryption operation.
+             */
+            sms4_set_encrypt_key(&octx->ksenc.ks, key);
+            sms4_set_decrypt_key(&octx->ksdec.ks, key);
+            if (!CRYPTO_ocb128_init(&octx->ocb,
+                                    &octx->ksenc.ks, &octx->ksdec.ks,
+                                    (block128_f)sms4_encrypt,
+                                    (block128_f)sms4_encrypt,
+                                    NULL))
+                return 0;
+        }
+        while (0);
+
+        /*
+         * If we have an iv we can set it directly, otherwise use saved IV.
+         */
+        if (iv == NULL && octx->iv_set)
+            iv = octx->iv;
+        if (iv) {
+            if (CRYPTO_ocb128_setiv(&octx->ocb, iv, octx->ivlen, octx->taglen)
+                != 1)
+                return 0;
+            octx->iv_set = 1;
+        }
+        octx->key_set = 1;
+    } else {
+        /* If key set use IV, otherwise copy */
+        if (octx->key_set)
+            CRYPTO_ocb128_setiv(&octx->ocb, iv, octx->ivlen, octx->taglen);
+        else
+            memcpy(octx->iv, iv, octx->ivlen);
+        octx->iv_set = 1;
+    }
+    return 1;
+}
+
+static int sms4_ocb_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
+                           const unsigned char *in, size_t len)
+{
+    unsigned char *buf;
+    int *buf_len;
+    int written_len = 0;
+    size_t trailing_len;
+    EVP_SMS4_OCB_CTX *octx = EVP_C_DATA(EVP_SMS4_OCB_CTX,ctx);
+
+    /* If IV or Key not set then return error */
+    if (!octx->iv_set)
+        return -1;
+
+    if (!octx->key_set)
+        return -1;
+
+    if (in) {
+        /*
+         * Need to ensure we are only passing full blocks to low level OCB
+         * routines. We do it here rather than in EVP_EncryptUpdate/
+         * EVP_DecryptUpdate because we need to pass full blocks of AAD too
+         * and those routines don't support that
+         */
+
+        /* Are we dealing with AAD or normal data here? */
+        if (out == NULL) {
+            buf = octx->aad_buf;
+            buf_len = &(octx->aad_buf_len);
+        } else {
+            buf = octx->data_buf;
+            buf_len = &(octx->data_buf_len);
+        }
+
+        /*
+         * If we've got a partially filled buffer from a previous call then
+         * use that data first
+         */
+        if (*buf_len) {
+            unsigned int remaining;
+
+            remaining = 16 - (*buf_len);
+            if (remaining > len) {
+                memcpy(buf + (*buf_len), in, len);
+                *(buf_len) += len;
+                return 0;
+            }
+            memcpy(buf + (*buf_len), in, remaining);
+
+            /*
+             * If we get here we've filled the buffer, so process it
+             */
+            len -= remaining;
+            in += remaining;
+            if (out == NULL) {
+                if (!CRYPTO_ocb128_aad(&octx->ocb, buf, 16))
+                    return -1;
+            } else if (EVP_CIPHER_CTX_encrypting(ctx)) {
+                if (!CRYPTO_ocb128_encrypt(&octx->ocb, buf, out, 16))
+                    return -1;
+            } else {
+                if (!CRYPTO_ocb128_decrypt(&octx->ocb, buf, out, 16))
+                    return -1;
+            }
+            written_len = 16;
+            *buf_len = 0;
+        }
+
+        /* Do we have a partial block to handle at the end? */
+        trailing_len = len % 16;
+
+        /*
+         * If we've got some full blocks to handle, then process these first
+         */
+        if (len != trailing_len) {
+            if (out == NULL) {
+                if (!CRYPTO_ocb128_aad(&octx->ocb, in, len - trailing_len))
+                    return -1;
+            } else if (EVP_CIPHER_CTX_encrypting(ctx)) {
+                if (!CRYPTO_ocb128_encrypt
+                    (&octx->ocb, in, out, len - trailing_len))
+                    return -1;
+            } else {
+                if (!CRYPTO_ocb128_decrypt
+                    (&octx->ocb, in, out, len - trailing_len))
+                    return -1;
+            }
+            written_len += len - trailing_len;
+            in += len - trailing_len;
+        }
+
+        /* Handle any trailing partial block */
+        if (trailing_len) {
+            memcpy(buf, in, trailing_len);
+            *buf_len = trailing_len;
+        }
+
+        return written_len;
+    } else {
+        /*
+         * First of all empty the buffer of any partial block that we might
+         * have been provided - both for data and AAD
+         */
+        if (octx->data_buf_len) {
+            if (EVP_CIPHER_CTX_encrypting(ctx)) {
+                if (!CRYPTO_ocb128_encrypt(&octx->ocb, octx->data_buf, out,
+                                           octx->data_buf_len))
+                    return -1;
+            } else {
+                if (!CRYPTO_ocb128_decrypt(&octx->ocb, octx->data_buf, out,
+                                           octx->data_buf_len))
+                    return -1;
+            }
+            written_len = octx->data_buf_len;
+            octx->data_buf_len = 0;
+        }
+        if (octx->aad_buf_len) {
+            if (!CRYPTO_ocb128_aad
+                (&octx->ocb, octx->aad_buf, octx->aad_buf_len))
+                return -1;
+            octx->aad_buf_len = 0;
+        }
+        /* If decrypting then verify */
+        if (!EVP_CIPHER_CTX_encrypting(ctx)) {
+            if (octx->taglen < 0)
+                return -1;
+            if (CRYPTO_ocb128_finish(&octx->ocb,
+                                     octx->tag, octx->taglen) != 0)
+                return -1;
+            octx->iv_set = 0;
+            return written_len;
+        }
+        /* If encrypting then just get the tag */
+        if (CRYPTO_ocb128_tag(&octx->ocb, octx->tag, 16) != 1)
+            return -1;
+        /* Don't reuse the IV */
+        octx->iv_set = 0;
+        return written_len;
+    }
+}
+
+static int sms4_ocb_cleanup(EVP_CIPHER_CTX *c)
+{
+    EVP_SMS4_OCB_CTX *octx = EVP_C_DATA(EVP_SMS4_OCB_CTX,c);
+    CRYPTO_ocb128_cleanup(&octx->ocb);
+    return 1;
+}
+
+#  define SMS4_OCB_IV_LENGTH 12
+
+#  define SMS4_OCB_FLAGS (EVP_CIPH_FLAG_DEFAULT_ASN1 \
+                | EVP_CIPH_CUSTOM_IV | EVP_CIPH_FLAG_CUSTOM_CIPHER \
+                | EVP_CIPH_ALWAYS_CALL_INIT | EVP_CIPH_CTRL_INIT \
+                | EVP_CIPH_CUSTOM_COPY \
+		| EVP_CIPH_FLAG_AEAD_CIPHER)
+
+static const EVP_CIPHER sms4_ocb = {
+	NID_sms4_ocb,
+	SMS4_BLOCK_SIZE,
+	SMS4_KEY_LENGTH,
+	SMS4_OCB_IV_LENGTH,
+	SMS4_OCB_FLAGS,
+	sms4_ocb_init_key,
+	sms4_ocb_cipher,
+	sms4_ocb_cleanup,
+	sizeof(EVP_SMS4_OCB_CTX),
+	NULL, NULL, sms4_ocb_ctrl, NULL
+};
+
+const EVP_CIPHER *EVP_sms4_ocb(void) {
+	return &sms4_ocb;
+}
+# endif /* OPENSSL_NO_OCB */
+#endif /* OPENSSL_NO_SMS4 */
diff --git a/crypto/evp/e_sms4_wrap.c b/crypto/evp/e_sms4_wrap.c
new file mode 100644
index 000000000..de856598d
--- /dev/null
+++ b/crypto/evp/e_sms4_wrap.c
@@ -0,0 +1,188 @@
+/* ====================================================================
+ * Copyright (c) 2014 - 2017 The GmSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the GmSSL Project.
+ *    (http://gmssl.org/)"
+ *
+ * 4. The name "GmSSL Project" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    guanzhi1980@gmail.com.
+ *
+ * 5. Products derived from this software may not be called "GmSSL"
+ *    nor may "GmSSL" appear in their names without prior written
+ *    permission of the GmSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the GmSSL Project
+ *    (http://gmssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE GmSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE GmSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+ /*
+ * Copyright 2001-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+
+#include 
+#include 
+#include 
+#include "evp_locl.h"
+#include "internal/evp_int.h"
+#include "modes_lcl.h"
+
+#ifndef OPENSSL_NO_SMS4
+# include 
+
+typedef struct {
+    union {
+        double align;
+        sms4_key_t ks;
+    } ks;
+    /* Indicates if IV has been set */
+    unsigned char *iv;
+} EVP_SMS4_WRAP_CTX;
+
+static int sms4_wrap_init_key(EVP_CIPHER_CTX *ctx, const unsigned char *key,
+                              const unsigned char *iv, int enc)
+{
+    EVP_SMS4_WRAP_CTX *wctx = EVP_C_DATA(EVP_SMS4_WRAP_CTX,ctx);
+    if (!iv && !key)
+        return 1;
+    if (key) {
+        if (EVP_CIPHER_CTX_encrypting(ctx))
+            sms4_set_encrypt_key(&wctx->ks.ks, key);
+        else
+            sms4_set_decrypt_key(&wctx->ks.ks, key);
+        if (!iv)
+            wctx->iv = NULL;
+    }
+    if (iv) {
+        memcpy(EVP_CIPHER_CTX_iv_noconst(ctx), iv, EVP_CIPHER_CTX_iv_length(ctx));
+        wctx->iv = EVP_CIPHER_CTX_iv_noconst(ctx);
+    }
+    return 1;
+}
+
+static int sms4_wrap_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
+                            const unsigned char *in, size_t inlen)
+{
+    EVP_SMS4_WRAP_CTX *wctx = EVP_C_DATA(EVP_SMS4_WRAP_CTX,ctx);
+    size_t rv;
+    /* SMS4 wrap with padding has IV length of 4, without padding 8 */
+    int pad = EVP_CIPHER_CTX_iv_length(ctx) == 4;
+    /* No final operation so always return zero length */
+    if (!in)
+        return 0;
+    /* Input length must always be non-zero */
+    if (!inlen)
+        return -1;
+    /* If decrypting need at least 16 bytes and multiple of 8 */
+    if (!EVP_CIPHER_CTX_encrypting(ctx) && (inlen < 16 || inlen & 0x7))
+        return -1;
+    /* If not padding input must be multiple of 8 */
+    if (!pad && inlen & 0x7)
+        return -1;
+    if (!out) {
+        if (EVP_CIPHER_CTX_encrypting(ctx)) {
+            /* If padding round up to multiple of 8 */
+            if (pad)
+                inlen = (inlen + 7) / 8 * 8;
+            /* 8 byte prefix */
+            return inlen + 8;
+        } else {
+            /*
+             * If not padding output will be exactly 8 bytes smaller than
+             * input. If padding it will be at least 8 bytes smaller but we
+             * don't know how much.
+             */
+            return inlen - 8;
+        }
+    }
+    if (pad) {
+        if (EVP_CIPHER_CTX_encrypting(ctx))
+            rv = CRYPTO_128_wrap_pad(&wctx->ks.ks, wctx->iv,
+                                     out, in, inlen,
+                                     (block128_f)sms4_encrypt);
+        else
+            rv = CRYPTO_128_unwrap_pad(&wctx->ks.ks, wctx->iv,
+                                       out, in, inlen,
+                                       (block128_f)sms4_encrypt);
+    } else {
+        if (EVP_CIPHER_CTX_encrypting(ctx))
+            rv = CRYPTO_128_wrap(&wctx->ks.ks, wctx->iv,
+                                 out, in, inlen, (block128_f)sms4_encrypt);
+        else
+            rv = CRYPTO_128_unwrap(&wctx->ks.ks, wctx->iv,
+                                   out, in, inlen, (block128_f)sms4_encrypt);
+    }
+    return rv ? (int)rv : -1;
+}
+
+# define SMS4_WRAP_FLAGS	    (EVP_CIPH_WRAP_MODE \
+                | EVP_CIPH_CUSTOM_IV | EVP_CIPH_FLAG_CUSTOM_CIPHER \
+                | EVP_CIPH_ALWAYS_CALL_INIT | EVP_CIPH_FLAG_DEFAULT_ASN1)
+
+
+
+static const EVP_CIPHER sms4_wrap = {
+    NID_sms4_wrap,
+    8, 16, 8, SMS4_WRAP_FLAGS,
+    sms4_wrap_init_key, sms4_wrap_cipher,
+    NULL,
+    sizeof(EVP_SMS4_WRAP_CTX),
+    NULL, NULL, NULL, NULL
+};
+
+const EVP_CIPHER *EVP_sms4_wrap(void)
+{
+    return &sms4_wrap;
+}
+
+static const EVP_CIPHER sms4_wrap_pad = {
+    NID_sms4_wrap_pad,
+    8, 16, 4, SMS4_WRAP_FLAGS,
+    sms4_wrap_init_key, sms4_wrap_cipher,
+    NULL,
+    sizeof(EVP_SMS4_WRAP_CTX),
+    NULL, NULL, NULL, NULL
+};
+
+const EVP_CIPHER *EVP_sms4_wrap_pad(void)
+{
+    return &sms4_wrap_pad;
+}
+#endif /* OPENSSL_NO_SMS4 */
diff --git a/crypto/evp/e_sms4_xts.c b/crypto/evp/e_sms4_xts.c
new file mode 100644
index 000000000..300d9f037
--- /dev/null
+++ b/crypto/evp/e_sms4_xts.c
@@ -0,0 +1,176 @@
+/* ====================================================================
+ * Copyright (c) 2014 - 2017 The GmSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the GmSSL Project.
+ *    (http://gmssl.org/)"
+ *
+ * 4. The name "GmSSL Project" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    guanzhi1980@gmail.com.
+ *
+ * 5. Products derived from this software may not be called "GmSSL"
+ *    nor may "GmSSL" appear in their names without prior written
+ *    permission of the GmSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the GmSSL Project
+ *    (http://gmssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE GmSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE GmSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+ /*
+ * Copyright 2001-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+
+#include 
+#include 
+#include 
+#include "evp_locl.h"
+#include "internal/evp_int.h"
+#include "modes_lcl.h"
+
+#ifndef OPENSSL_NO_SMS4
+# include 
+
+typedef struct {
+    union {
+        double align;
+        sms4_key_t ks;
+    } ks1, ks2;                 /* SMS4 key schedules to use */
+    XTS128_CONTEXT xts;
+    void (*stream) (const unsigned char *in,
+                    unsigned char *out, size_t length,
+                    const sms4_key_t *key1, const sms4_key_t *key2,
+                    const unsigned char iv[16]);
+} EVP_SMS4_XTS_CTX;
+
+static int sms4_xts_ctrl(EVP_CIPHER_CTX *c, int type, int arg, void *ptr)
+{
+    EVP_SMS4_XTS_CTX *xctx = EVP_C_DATA(EVP_SMS4_XTS_CTX,c);
+    if (type == EVP_CTRL_COPY) {
+        EVP_CIPHER_CTX *out = ptr;
+        EVP_SMS4_XTS_CTX *xctx_out = EVP_C_DATA(EVP_SMS4_XTS_CTX,out);
+        if (xctx->xts.key1) {
+            if (xctx->xts.key1 != &xctx->ks1)
+                return 0;
+            xctx_out->xts.key1 = &xctx_out->ks1;
+        }
+        if (xctx->xts.key2) {
+            if (xctx->xts.key2 != &xctx->ks2)
+                return 0;
+            xctx_out->xts.key2 = &xctx_out->ks2;
+        }
+        return 1;
+    } else if (type != EVP_CTRL_INIT)
+        return -1;
+    /* key1 and key2 are used as an indicator both key and IV are set */
+    xctx->xts.key1 = NULL;
+    xctx->xts.key2 = NULL;
+    return 1;
+}
+
+static int sms4_xts_init_key(EVP_CIPHER_CTX *ctx, const unsigned char *key,
+                             const unsigned char *iv, int enc)
+{
+    EVP_SMS4_XTS_CTX *xctx = EVP_C_DATA(EVP_SMS4_XTS_CTX, ctx);
+    if (!iv && !key)
+        return 1;
+
+    if (key) {
+        xctx->stream = NULL;
+        if (enc) {
+            sms4_set_encrypt_key(&xctx->ks1.ks, key);
+        } else {
+            sms4_set_decrypt_key(&xctx->ks1.ks, key);
+        }
+        sms4_set_encrypt_key(&xctx->ks2.ks, key + SMS4_KEY_LENGTH);
+        xctx->xts.block1 = (block128_f)sms4_encrypt;
+        xctx->xts.block2 = (block128_f)sms4_encrypt;
+        xctx->xts.key1 = &xctx->ks1;
+    }
+
+    if (iv) {
+        xctx->xts.key2 = &xctx->ks2;
+        memcpy(EVP_CIPHER_CTX_iv_noconst(ctx), iv, 16);
+    }
+
+    return 1;
+}
+
+static int sms4_xts_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
+                           const unsigned char *in, size_t len)
+{
+    EVP_SMS4_XTS_CTX *xctx = EVP_C_DATA(EVP_SMS4_XTS_CTX,ctx);
+    if (!xctx->xts.key1 || !xctx->xts.key2)
+        return 0;
+    if (!out || !in || len < SMS4_BLOCK_SIZE)
+        return 0;
+    if (xctx->stream)
+        (*xctx->stream) (in, out, len,
+                         xctx->xts.key1, xctx->xts.key2,
+                         EVP_CIPHER_CTX_iv_noconst(ctx));
+    else if (CRYPTO_xts128_encrypt(&xctx->xts, EVP_CIPHER_CTX_iv_noconst(ctx),
+                                   in, out, len,
+                                   EVP_CIPHER_CTX_encrypting(ctx)))
+        return 0;
+    return 1;
+}
+
+# define SMS4_XTS_BLOCK_SIZE	1
+
+# define SMS4_XTS_FLAGS   (EVP_CIPH_FLAG_DEFAULT_ASN1 | EVP_CIPH_CUSTOM_IV \
+                         | EVP_CIPH_ALWAYS_CALL_INIT | EVP_CIPH_CTRL_INIT \
+                         | EVP_CIPH_CUSTOM_COPY)
+
+static const EVP_CIPHER sms4_xts = {
+	NID_sms4_xts,
+	SMS4_XTS_BLOCK_SIZE,
+	SMS4_KEY_LENGTH * 2,
+	SMS4_IV_LENGTH,
+	SMS4_XTS_FLAGS,
+	sms4_xts_init_key,
+	sms4_xts_cipher,
+	NULL,
+	sizeof(EVP_SMS4_XTS_CTX),
+	NULL, NULL, sms4_xts_ctrl, NULL
+};
+
+const EVP_CIPHER *EVP_sms4_xts(void) {
+	return &sms4_xts;
+}
+#endif /* OPENSSL_NO_SMS4 */
diff --git a/crypto/evp/e_xcbc_d.c b/crypto/evp/e_xcbc_d.c
new file mode 100644
index 000000000..effaf5cc6
--- /dev/null
+++ b/crypto/evp/e_xcbc_d.c
@@ -0,0 +1,83 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+
+#ifndef OPENSSL_NO_DES
+
+# include 
+# include 
+# include "internal/evp_int.h"
+# include 
+
+static int desx_cbc_init_key(EVP_CIPHER_CTX *ctx, const unsigned char *key,
+                             const unsigned char *iv, int enc);
+static int desx_cbc_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
+                           const unsigned char *in, size_t inl);
+
+typedef struct {
+    DES_key_schedule ks;        /* key schedule */
+    DES_cblock inw;
+    DES_cblock outw;
+} DESX_CBC_KEY;
+
+# define data(ctx) EVP_C_DATA(DESX_CBC_KEY,ctx)
+
+static const EVP_CIPHER d_xcbc_cipher = {
+    NID_desx_cbc,
+    8, 24, 8,
+    EVP_CIPH_CBC_MODE,
+    desx_cbc_init_key,
+    desx_cbc_cipher,
+    NULL,
+    sizeof(DESX_CBC_KEY),
+    EVP_CIPHER_set_asn1_iv,
+    EVP_CIPHER_get_asn1_iv,
+    NULL,
+    NULL
+};
+
+const EVP_CIPHER *EVP_desx_cbc(void)
+{
+    return (&d_xcbc_cipher);
+}
+
+static int desx_cbc_init_key(EVP_CIPHER_CTX *ctx, const unsigned char *key,
+                             const unsigned char *iv, int enc)
+{
+    DES_cblock *deskey = (DES_cblock *)key;
+
+    DES_set_key_unchecked(deskey, &data(ctx)->ks);
+    memcpy(&data(ctx)->inw[0], &key[8], 8);
+    memcpy(&data(ctx)->outw[0], &key[16], 8);
+
+    return 1;
+}
+
+static int desx_cbc_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
+                           const unsigned char *in, size_t inl)
+{
+    while (inl >= EVP_MAXCHUNK) {
+        DES_xcbc_encrypt(in, out, (long)EVP_MAXCHUNK, &data(ctx)->ks,
+                         (DES_cblock *)EVP_CIPHER_CTX_iv_noconst(ctx),
+                         &data(ctx)->inw, &data(ctx)->outw,
+                         EVP_CIPHER_CTX_encrypting(ctx));
+        inl -= EVP_MAXCHUNK;
+        in += EVP_MAXCHUNK;
+        out += EVP_MAXCHUNK;
+    }
+    if (inl)
+        DES_xcbc_encrypt(in, out, (long)inl, &data(ctx)->ks,
+                         (DES_cblock *)EVP_CIPHER_CTX_iv_noconst(ctx),
+                         &data(ctx)->inw, &data(ctx)->outw,
+                         EVP_CIPHER_CTX_encrypting(ctx));
+    return 1;
+}
+#endif
diff --git a/crypto/evp/e_zuc.c b/crypto/evp/e_zuc.c
new file mode 100644
index 000000000..b81d5faa8
--- /dev/null
+++ b/crypto/evp/e_zuc.c
@@ -0,0 +1,139 @@
+/* ====================================================================
+ * Copyright (c) 2014 - 2018 The GmSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the GmSSL Project.
+ *    (http://gmssl.org/)"
+ *
+ * 4. The name "GmSSL Project" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    guanzhi1980@gmail.com.
+ *
+ * 5. Products derived from this software may not be called "GmSSL"
+ *    nor may "GmSSL" appear in their names without prior written
+ *    permission of the GmSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the GmSSL Project
+ *    (http://gmssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE GmSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE GmSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include "evp_locl.h"
+# include "internal/evp_int.h"
+
+#ifndef OPENSSL_NO_ZUC
+
+# include 
+
+typedef struct {
+	ZUC_KEY ks;
+} EVP_ZUC_KEY;
+
+
+static int zuc_init_key(EVP_CIPHER_CTX *ctx, const unsigned char *key,
+	const unsigned char *iv, int enc)
+{
+	EVP_ZUC_KEY *dctx = EVP_C_DATA(EVP_ZUC_KEY, ctx);
+	ZUC_set_key(&dctx->ks, key, iv);
+	return 1;
+}
+
+static int zuc_do_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
+	const unsigned char *in, size_t len)
+{
+	EVP_ZUC_KEY *dctx = EVP_C_DATA(EVP_ZUC_KEY, ctx);
+	unsigned char *buf = EVP_CIPHER_CTX_buf_noconst(ctx);
+	unsigned int n = EVP_CIPHER_CTX_num(ctx);
+	size_t l = 0;
+
+	while (l < len) {
+		if (n == 0) {
+			ZUC_generate_keystream(&dctx->ks, 4, (uint32_t *)buf);
+		}
+		out[l] = in[l] ^ buf[n];
+		++l;
+		n = (n + 1) % 16;
+	}
+
+	EVP_CIPHER_CTX_set_num(ctx, n);
+	return 1;
+}
+
+const EVP_CIPHER zuc_cipher = {
+	NID_zuc,
+	1,
+	ZUC_KEY_LENGTH,
+	ZUC_IV_LENGTH,
+	0,
+	zuc_init_key,
+	zuc_do_cipher,
+	NULL,
+	sizeof(EVP_ZUC_KEY),
+	NULL,NULL,NULL,NULL,
+};
+
+const EVP_CIPHER *EVP_zuc(void)
+{
+	return &zuc_cipher;
+}
+
+static int zuc256_init_key(EVP_CIPHER_CTX *ctx, const unsigned char *key,
+	const unsigned char *iv, int enc)
+{
+	EVP_ZUC_KEY *dctx = EVP_C_DATA(EVP_ZUC_KEY, ctx);
+	ZUC256_set_key(&dctx->ks, key, iv);
+	return 1;
+}
+
+const EVP_CIPHER zuc256_cipher = {
+	NID_zuc256,
+	1,
+	ZUC256_KEY_LENGTH,
+	ZUC256_IV_LENGTH,
+	0,
+	zuc256_init_key,
+	zuc_do_cipher,
+	NULL,
+	sizeof(EVP_ZUC_KEY),
+	NULL,NULL,NULL,NULL,
+};
+
+const EVP_CIPHER *EVP_zuc256(void)
+{
+	return &zuc256_cipher;
+}
+
+#endif /* OPENSSL_NO_ZUC */
diff --git a/crypto/evp/encode.c b/crypto/evp/encode.c
new file mode 100644
index 000000000..abb104437
--- /dev/null
+++ b/crypto/evp/encode.c
@@ -0,0 +1,404 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include "evp_locl.h"
+
+static unsigned char conv_ascii2bin(unsigned char a);
+#ifndef CHARSET_EBCDIC
+# define conv_bin2ascii(a)       (data_bin2ascii[(a)&0x3f])
+#else
+/*
+ * We assume that PEM encoded files are EBCDIC files (i.e., printable text
+ * files). Convert them here while decoding. When encoding, output is EBCDIC
+ * (text) format again. (No need for conversion in the conv_bin2ascii macro,
+ * as the underlying textstring data_bin2ascii[] is already EBCDIC)
+ */
+# define conv_bin2ascii(a)       (data_bin2ascii[(a)&0x3f])
+#endif
+
+/*-
+ * 64 char lines
+ * pad input with 0
+ * left over chars are set to =
+ * 1 byte  => xx==
+ * 2 bytes => xxx=
+ * 3 bytes => xxxx
+ */
+#define BIN_PER_LINE    (64/4*3)
+#define CHUNKS_PER_LINE (64/4)
+#define CHAR_PER_LINE   (64+1)
+
+static const unsigned char data_bin2ascii[65] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ\
+abcdefghijklmnopqrstuvwxyz0123456789+/";
+
+/*-
+ * 0xF0 is a EOLN
+ * 0xF1 is ignore but next needs to be 0xF0 (for \r\n processing).
+ * 0xF2 is EOF
+ * 0xE0 is ignore at start of line.
+ * 0xFF is error
+ */
+
+#define B64_EOLN                0xF0
+#define B64_CR                  0xF1
+#define B64_EOF                 0xF2
+#define B64_WS                  0xE0
+#define B64_ERROR               0xFF
+#define B64_NOT_BASE64(a)       (((a)|0x13) == 0xF3)
+#define B64_BASE64(a)           (!B64_NOT_BASE64(a))
+
+static const unsigned char data_ascii2bin[128] = {
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xE0, 0xF0, 0xFF, 0xFF, 0xF1, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xE0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0x3E, 0xFF, 0xF2, 0xFF, 0x3F,
+    0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B,
+    0x3C, 0x3D, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF,
+    0xFF, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
+    0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E,
+    0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
+    0x17, 0x18, 0x19, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20,
+    0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
+    0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30,
+    0x31, 0x32, 0x33, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+};
+
+#ifndef CHARSET_EBCDIC
+static unsigned char conv_ascii2bin(unsigned char a)
+{
+    if (a & 0x80)
+        return B64_ERROR;
+    return data_ascii2bin[a];
+}
+#else
+static unsigned char conv_ascii2bin(unsigned char a)
+{
+    a = os_toascii[a];
+    if (a & 0x80)
+        return B64_ERROR;
+    return data_ascii2bin[a];
+}
+#endif
+
+EVP_ENCODE_CTX *EVP_ENCODE_CTX_new(void)
+{
+    return OPENSSL_zalloc(sizeof(EVP_ENCODE_CTX));
+}
+
+void EVP_ENCODE_CTX_free(EVP_ENCODE_CTX *ctx)
+{
+    OPENSSL_free(ctx);
+}
+
+int EVP_ENCODE_CTX_copy(EVP_ENCODE_CTX *dctx, EVP_ENCODE_CTX *sctx)
+{
+    memcpy(dctx, sctx, sizeof(EVP_ENCODE_CTX));
+
+    return 1;
+}
+
+int EVP_ENCODE_CTX_num(EVP_ENCODE_CTX *ctx)
+{
+    return ctx->num;
+}
+
+void EVP_EncodeInit(EVP_ENCODE_CTX *ctx)
+{
+    ctx->length = 48;
+    ctx->num = 0;
+    ctx->line_num = 0;
+}
+
+int EVP_EncodeUpdate(EVP_ENCODE_CTX *ctx, unsigned char *out, int *outl,
+                      const unsigned char *in, int inl)
+{
+    int i, j;
+    size_t total = 0;
+
+    *outl = 0;
+    if (inl <= 0)
+        return 0;
+    OPENSSL_assert(ctx->length <= (int)sizeof(ctx->enc_data));
+    if (ctx->length - ctx->num > inl) {
+        memcpy(&(ctx->enc_data[ctx->num]), in, inl);
+        ctx->num += inl;
+        return 1;
+    }
+    if (ctx->num != 0) {
+        i = ctx->length - ctx->num;
+        memcpy(&(ctx->enc_data[ctx->num]), in, i);
+        in += i;
+        inl -= i;
+        j = EVP_EncodeBlock(out, ctx->enc_data, ctx->length);
+        ctx->num = 0;
+        out += j;
+        *(out++) = '\n';
+        *out = '\0';
+        total = j + 1;
+    }
+    while (inl >= ctx->length && total <= INT_MAX) {
+        j = EVP_EncodeBlock(out, in, ctx->length);
+        in += ctx->length;
+        inl -= ctx->length;
+        out += j;
+        *(out++) = '\n';
+        *out = '\0';
+        total += j + 1;
+    }
+    if (total > INT_MAX) {
+        /* Too much output data! */
+        *outl = 0;
+        return 0;
+    }
+    if (inl != 0)
+        memcpy(&(ctx->enc_data[0]), in, inl);
+    ctx->num = inl;
+    *outl = total;
+
+    return 1;
+}
+
+void EVP_EncodeFinal(EVP_ENCODE_CTX *ctx, unsigned char *out, int *outl)
+{
+    unsigned int ret = 0;
+
+    if (ctx->num != 0) {
+        ret = EVP_EncodeBlock(out, ctx->enc_data, ctx->num);
+        out[ret++] = '\n';
+        out[ret] = '\0';
+        ctx->num = 0;
+    }
+    *outl = ret;
+}
+
+int EVP_EncodeBlock(unsigned char *t, const unsigned char *f, int dlen)
+{
+    int i, ret = 0;
+    unsigned long l;
+
+    for (i = dlen; i > 0; i -= 3) {
+        if (i >= 3) {
+            l = (((unsigned long)f[0]) << 16L) |
+                (((unsigned long)f[1]) << 8L) | f[2];
+            *(t++) = conv_bin2ascii(l >> 18L);
+            *(t++) = conv_bin2ascii(l >> 12L);
+            *(t++) = conv_bin2ascii(l >> 6L);
+            *(t++) = conv_bin2ascii(l);
+        } else {
+            l = ((unsigned long)f[0]) << 16L;
+            if (i == 2)
+                l |= ((unsigned long)f[1] << 8L);
+
+            *(t++) = conv_bin2ascii(l >> 18L);
+            *(t++) = conv_bin2ascii(l >> 12L);
+            *(t++) = (i == 1) ? '=' : conv_bin2ascii(l >> 6L);
+            *(t++) = '=';
+        }
+        ret += 4;
+        f += 3;
+    }
+
+    *t = '\0';
+    return (ret);
+}
+
+void EVP_DecodeInit(EVP_ENCODE_CTX *ctx)
+{
+    /* Only ctx->num is used during decoding. */
+    ctx->num = 0;
+    ctx->length = 0;
+    ctx->line_num = 0;
+    ctx->expect_nl = 0;
+}
+
+/*-
+ * -1 for error
+ *  0 for last line
+ *  1 for full line
+ *
+ * Note: even though EVP_DecodeUpdate attempts to detect and report end of
+ * content, the context doesn't currently remember it and will accept more data
+ * in the next call. Therefore, the caller is responsible for checking and
+ * rejecting a 0 return value in the middle of content.
+ *
+ * Note: even though EVP_DecodeUpdate has historically tried to detect end of
+ * content based on line length, this has never worked properly. Therefore,
+ * we now return 0 when one of the following is true:
+ *   - Padding or B64_EOF was detected and the last block is complete.
+ *   - Input has zero-length.
+ * -1 is returned if:
+ *   - Invalid characters are detected.
+ *   - There is extra trailing padding, or data after padding.
+ *   - B64_EOF is detected after an incomplete base64 block.
+ */
+int EVP_DecodeUpdate(EVP_ENCODE_CTX *ctx, unsigned char *out, int *outl,
+                     const unsigned char *in, int inl)
+{
+    int seof = 0, eof = 0, rv = -1, ret = 0, i, v, tmp, n, decoded_len;
+    unsigned char *d;
+
+    n = ctx->num;
+    d = ctx->enc_data;
+
+    if (n > 0 && d[n - 1] == '=') {
+        eof++;
+        if (n > 1 && d[n - 2] == '=')
+            eof++;
+    }
+
+     /* Legacy behaviour: an empty input chunk signals end of input. */
+    if (inl == 0) {
+        rv = 0;
+        goto end;
+    }
+
+    for (i = 0; i < inl; i++) {
+        tmp = *(in++);
+        v = conv_ascii2bin(tmp);
+        if (v == B64_ERROR) {
+            rv = -1;
+            goto end;
+        }
+
+        if (tmp == '=') {
+            eof++;
+        } else if (eof > 0 && B64_BASE64(v)) {
+            /* More data after padding. */
+            rv = -1;
+            goto end;
+        }
+
+        if (eof > 2) {
+            rv = -1;
+            goto end;
+        }
+
+        if (v == B64_EOF) {
+            seof = 1;
+            goto tail;
+        }
+
+        /* Only save valid base64 characters. */
+        if (B64_BASE64(v)) {
+            if (n >= 64) {
+                /*
+                 * We increment n once per loop, and empty the buffer as soon as
+                 * we reach 64 characters, so this can only happen if someone's
+                 * manually messed with the ctx. Refuse to write any more data.
+                 */
+                rv = -1;
+                goto end;
+            }
+            OPENSSL_assert(n < (int)sizeof(ctx->enc_data));
+            d[n++] = tmp;
+        }
+
+        if (n == 64) {
+            decoded_len = EVP_DecodeBlock(out, d, n);
+            n = 0;
+            if (decoded_len < 0 || eof > decoded_len) {
+                rv = -1;
+                goto end;
+            }
+            ret += decoded_len - eof;
+            out += decoded_len - eof;
+        }
+    }
+
+    /*
+     * Legacy behaviour: if the current line is a full base64-block (i.e., has
+     * 0 mod 4 base64 characters), it is processed immediately. We keep this
+     * behaviour as applications may not be calling EVP_DecodeFinal properly.
+     */
+tail:
+    if (n > 0) {
+        if ((n & 3) == 0) {
+            decoded_len = EVP_DecodeBlock(out, d, n);
+            n = 0;
+            if (decoded_len < 0 || eof > decoded_len) {
+                rv = -1;
+                goto end;
+            }
+            ret += (decoded_len - eof);
+        } else if (seof) {
+            /* EOF in the middle of a base64 block. */
+            rv = -1;
+            goto end;
+        }
+    }
+
+    rv = seof || (n == 0 && eof) ? 0 : 1;
+end:
+    /* Legacy behaviour. This should probably rather be zeroed on error. */
+    *outl = ret;
+    ctx->num = n;
+    return (rv);
+}
+
+int EVP_DecodeBlock(unsigned char *t, const unsigned char *f, int n)
+{
+    int i, ret = 0, a, b, c, d;
+    unsigned long l;
+
+    /* trim white space from the start of the line. */
+    while ((conv_ascii2bin(*f) == B64_WS) && (n > 0)) {
+        f++;
+        n--;
+    }
+
+    /*
+     * strip off stuff at the end of the line ascii2bin values B64_WS,
+     * B64_EOLN, B64_EOLN and B64_EOF
+     */
+    while ((n > 3) && (B64_NOT_BASE64(conv_ascii2bin(f[n - 1]))))
+        n--;
+
+    if (n % 4 != 0)
+        return (-1);
+
+    for (i = 0; i < n; i += 4) {
+        a = conv_ascii2bin(*(f++));
+        b = conv_ascii2bin(*(f++));
+        c = conv_ascii2bin(*(f++));
+        d = conv_ascii2bin(*(f++));
+        if ((a & 0x80) || (b & 0x80) || (c & 0x80) || (d & 0x80))
+            return (-1);
+        l = ((((unsigned long)a) << 18L) |
+             (((unsigned long)b) << 12L) |
+             (((unsigned long)c) << 6L) | (((unsigned long)d)));
+        *(t++) = (unsigned char)(l >> 16L) & 0xff;
+        *(t++) = (unsigned char)(l >> 8L) & 0xff;
+        *(t++) = (unsigned char)(l) & 0xff;
+        ret += 3;
+    }
+    return (ret);
+}
+
+int EVP_DecodeFinal(EVP_ENCODE_CTX *ctx, unsigned char *out, int *outl)
+{
+    int i;
+
+    *outl = 0;
+    if (ctx->num != 0) {
+        i = EVP_DecodeBlock(out, ctx->enc_data, ctx->num);
+        if (i < 0)
+            return (-1);
+        ctx->num = 0;
+        *outl = i;
+        return (1);
+    } else
+        return (1);
+}
diff --git a/crypto/evp/evp_cnf.c b/crypto/evp/evp_cnf.c
new file mode 100644
index 000000000..71d13b8df
--- /dev/null
+++ b/crypto/evp/evp_cnf.c
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2012-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+#include 
+
+/* Algorithm configuration module. */
+
+static int alg_module_init(CONF_IMODULE *md, const CONF *cnf)
+{
+    int i;
+    const char *oid_section;
+    STACK_OF(CONF_VALUE) *sktmp;
+    CONF_VALUE *oval;
+
+    oid_section = CONF_imodule_get_value(md);
+    if ((sktmp = NCONF_get_section(cnf, oid_section)) == NULL) {
+        EVPerr(EVP_F_ALG_MODULE_INIT, EVP_R_ERROR_LOADING_SECTION);
+        return 0;
+    }
+    for (i = 0; i < sk_CONF_VALUE_num(sktmp); i++) {
+        oval = sk_CONF_VALUE_value(sktmp, i);
+        if (strcmp(oval->name, "fips_mode") == 0) {
+            int m;
+            if (!X509V3_get_value_bool(oval, &m)) {
+                EVPerr(EVP_F_ALG_MODULE_INIT, EVP_R_INVALID_FIPS_MODE);
+                return 0;
+            }
+            if (m > 0) {
+#ifdef OPENSSL_FIPS
+                if (!FIPS_mode() && !FIPS_mode_set(1)) {
+                    EVPerr(EVP_F_ALG_MODULE_INIT,
+                           EVP_R_ERROR_SETTING_FIPS_MODE);
+                    return 0;
+                }
+#else
+                EVPerr(EVP_F_ALG_MODULE_INIT, EVP_R_FIPS_MODE_NOT_SUPPORTED);
+                return 0;
+#endif
+            }
+        } else {
+            EVPerr(EVP_F_ALG_MODULE_INIT, EVP_R_UNKNOWN_OPTION);
+            ERR_add_error_data(4, "name=", oval->name,
+                               ", value=", oval->value);
+        }
+
+    }
+    return 1;
+}
+
+void EVP_add_alg_module(void)
+{
+    CONF_module_add("alg_section", alg_module_init, 0);
+}
diff --git a/crypto/evp/evp_ctxt.c b/crypto/evp/evp_ctxt.c
new file mode 100644
index 000000000..8177ee7f2
--- /dev/null
+++ b/crypto/evp/evp_ctxt.c
@@ -0,0 +1,69 @@
+/* ====================================================================
+ * Copyright (c) 2016 The GmSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the GmSSL Project.
+ *    (http://gmssl.org/)"
+ *
+ * 4. The name "GmSSL Project" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    guanzhi1980@gmail.com.
+ *
+ * 5. Products derived from this software may not be called "GmSSL"
+ *    nor may "GmSSL" appear in their names without prior written
+ *    permission of the GmSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the GmSSL Project
+ *    (http://gmssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE GmSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE GmSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include "internal/cryptlib.h"
+#include "internal/evp_int.h"
+
+int EVP_PKEY_CTX_ciphertext_size(EVP_PKEY_CTX *ctx,
+	size_t inlen, size_t *outlen)
+{
+	if (inlen > 4096) {
+		EVPerr(EVP_F_EVP_PKEY_CTX_CIPHERTEXT_SIZE,
+			EVP_R_INVALID_INPUT_LENGTH);
+		return 0;
+	}
+	//FIXME: this function should call some func_ptr from ctx->pkey
+	*outlen = inlen + 4096;
+	return 1;
+}
+
diff --git a/crypto/evp/evp_enc.c b/crypto/evp/evp_enc.c
new file mode 100644
index 000000000..556838e54
--- /dev/null
+++ b/crypto/evp/evp_enc.c
@@ -0,0 +1,630 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+#include 
+#ifndef OPENSSL_NO_ENGINE
+# include 
+#endif
+#include "internal/evp_int.h"
+#include "evp_locl.h"
+
+int EVP_CIPHER_CTX_reset(EVP_CIPHER_CTX *c)
+{
+    if (c == NULL)
+        return 1;
+    if (c->cipher != NULL) {
+        if (c->cipher->cleanup && !c->cipher->cleanup(c))
+            return 0;
+        /* Cleanse cipher context data */
+        if (c->cipher_data && c->cipher->ctx_size)
+            OPENSSL_cleanse(c->cipher_data, c->cipher->ctx_size);
+    }
+    OPENSSL_free(c->cipher_data);
+#ifndef OPENSSL_NO_ENGINE
+    ENGINE_finish(c->engine);
+#endif
+    memset(c, 0, sizeof(*c));
+    return 1;
+}
+
+EVP_CIPHER_CTX *EVP_CIPHER_CTX_new(void)
+{
+    return OPENSSL_zalloc(sizeof(EVP_CIPHER_CTX));
+}
+
+void EVP_CIPHER_CTX_free(EVP_CIPHER_CTX *ctx)
+{
+    EVP_CIPHER_CTX_reset(ctx);
+    OPENSSL_free(ctx);
+}
+
+int EVP_CipherInit(EVP_CIPHER_CTX *ctx, const EVP_CIPHER *cipher,
+                   const unsigned char *key, const unsigned char *iv, int enc)
+{
+    EVP_CIPHER_CTX_reset(ctx);
+    return EVP_CipherInit_ex(ctx, cipher, NULL, key, iv, enc);
+}
+
+int EVP_CipherInit_ex(EVP_CIPHER_CTX *ctx, const EVP_CIPHER *cipher,
+                      ENGINE *impl, const unsigned char *key,
+                      const unsigned char *iv, int enc)
+{
+    if (enc == -1)
+        enc = ctx->encrypt;
+    else {
+        if (enc)
+            enc = 1;
+        ctx->encrypt = enc;
+    }
+#ifndef OPENSSL_NO_ENGINE
+    /*
+     * Whether it's nice or not, "Inits" can be used on "Final"'d contexts so
+     * this context may already have an ENGINE! Try to avoid releasing the
+     * previous handle, re-querying for an ENGINE, and having a
+     * reinitialisation, when it may all be unnecessary.
+     */
+    if (ctx->engine && ctx->cipher
+        && (cipher == NULL || cipher->nid == ctx->cipher->nid))
+        goto skip_to_init;
+#endif
+    if (cipher) {
+        /*
+         * Ensure a context left lying around from last time is cleared (the
+         * previous check attempted to avoid this if the same ENGINE and
+         * EVP_CIPHER could be used).
+         */
+        if (ctx->cipher) {
+            unsigned long flags = ctx->flags;
+            EVP_CIPHER_CTX_reset(ctx);
+            /* Restore encrypt and flags */
+            ctx->encrypt = enc;
+            ctx->flags = flags;
+        }
+#ifndef OPENSSL_NO_ENGINE
+        if (impl) {
+            if (!ENGINE_init(impl)) {
+                EVPerr(EVP_F_EVP_CIPHERINIT_EX, EVP_R_INITIALIZATION_ERROR);
+                return 0;
+            }
+        } else
+            /* Ask if an ENGINE is reserved for this job */
+            impl = ENGINE_get_cipher_engine(cipher->nid);
+        if (impl) {
+            /* There's an ENGINE for this job ... (apparently) */
+            const EVP_CIPHER *c = ENGINE_get_cipher(impl, cipher->nid);
+            if (!c) {
+                /*
+                 * One positive side-effect of US's export control history,
+                 * is that we should at least be able to avoid using US
+                 * misspellings of "initialisation"?
+                 */
+                EVPerr(EVP_F_EVP_CIPHERINIT_EX, EVP_R_INITIALIZATION_ERROR);
+                return 0;
+            }
+            /* We'll use the ENGINE's private cipher definition */
+            cipher = c;
+            /*
+             * Store the ENGINE functional reference so we know 'cipher' came
+             * from an ENGINE and we need to release it when done.
+             */
+            ctx->engine = impl;
+        } else
+            ctx->engine = NULL;
+#endif
+
+        ctx->cipher = cipher;
+        if (ctx->cipher->ctx_size) {
+            ctx->cipher_data = OPENSSL_zalloc(ctx->cipher->ctx_size);
+            if (ctx->cipher_data == NULL) {
+                EVPerr(EVP_F_EVP_CIPHERINIT_EX, ERR_R_MALLOC_FAILURE);
+                return 0;
+            }
+        } else {
+            ctx->cipher_data = NULL;
+        }
+        ctx->key_len = cipher->key_len;
+        /* Preserve wrap enable flag, zero everything else */
+        ctx->flags &= EVP_CIPHER_CTX_FLAG_WRAP_ALLOW;
+        if (ctx->cipher->flags & EVP_CIPH_CTRL_INIT) {
+            if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_INIT, 0, NULL)) {
+                EVPerr(EVP_F_EVP_CIPHERINIT_EX, EVP_R_INITIALIZATION_ERROR);
+                return 0;
+            }
+        }
+    } else if (!ctx->cipher) {
+        EVPerr(EVP_F_EVP_CIPHERINIT_EX, EVP_R_NO_CIPHER_SET);
+        return 0;
+    }
+#ifndef OPENSSL_NO_ENGINE
+ skip_to_init:
+#endif
+    /* we assume block size is a power of 2 in *cryptUpdate */
+    OPENSSL_assert(ctx->cipher->block_size == 1
+                   || ctx->cipher->block_size == 8
+                   || ctx->cipher->block_size == 16);
+
+    if (!(ctx->flags & EVP_CIPHER_CTX_FLAG_WRAP_ALLOW)
+        && EVP_CIPHER_CTX_mode(ctx) == EVP_CIPH_WRAP_MODE) {
+        EVPerr(EVP_F_EVP_CIPHERINIT_EX, EVP_R_WRAP_MODE_NOT_ALLOWED);
+        return 0;
+    }
+
+    if (!(EVP_CIPHER_flags(EVP_CIPHER_CTX_cipher(ctx)) & EVP_CIPH_CUSTOM_IV)) {
+        switch (EVP_CIPHER_CTX_mode(ctx)) {
+
+        case EVP_CIPH_STREAM_CIPHER:
+        case EVP_CIPH_ECB_MODE:
+            break;
+
+        case EVP_CIPH_CFB_MODE:
+        case EVP_CIPH_OFB_MODE:
+
+            ctx->num = 0;
+            /* fall-through */
+
+        case EVP_CIPH_CBC_MODE:
+
+            OPENSSL_assert(EVP_CIPHER_CTX_iv_length(ctx) <=
+                           (int)sizeof(ctx->iv));
+            if (iv)
+                memcpy(ctx->oiv, iv, EVP_CIPHER_CTX_iv_length(ctx));
+            memcpy(ctx->iv, ctx->oiv, EVP_CIPHER_CTX_iv_length(ctx));
+            break;
+
+        case EVP_CIPH_CTR_MODE:
+            ctx->num = 0;
+            /* Don't reuse IV for CTR mode */
+            if (iv)
+                memcpy(ctx->iv, iv, EVP_CIPHER_CTX_iv_length(ctx));
+            break;
+
+        default:
+            return 0;
+        }
+    }
+
+    if (key || (ctx->cipher->flags & EVP_CIPH_ALWAYS_CALL_INIT)) {
+        if (!ctx->cipher->init(ctx, key, iv, enc))
+            return 0;
+    }
+    ctx->buf_len = 0;
+    ctx->final_used = 0;
+    ctx->block_mask = ctx->cipher->block_size - 1;
+    return 1;
+}
+
+int EVP_CipherUpdate(EVP_CIPHER_CTX *ctx, unsigned char *out, int *outl,
+                     const unsigned char *in, int inl)
+{
+    if (ctx->encrypt)
+        return EVP_EncryptUpdate(ctx, out, outl, in, inl);
+    else
+        return EVP_DecryptUpdate(ctx, out, outl, in, inl);
+}
+
+int EVP_CipherFinal_ex(EVP_CIPHER_CTX *ctx, unsigned char *out, int *outl)
+{
+    if (ctx->encrypt)
+        return EVP_EncryptFinal_ex(ctx, out, outl);
+    else
+        return EVP_DecryptFinal_ex(ctx, out, outl);
+}
+
+int EVP_CipherFinal(EVP_CIPHER_CTX *ctx, unsigned char *out, int *outl)
+{
+    if (ctx->encrypt)
+        return EVP_EncryptFinal(ctx, out, outl);
+    else
+        return EVP_DecryptFinal(ctx, out, outl);
+}
+
+int EVP_EncryptInit(EVP_CIPHER_CTX *ctx, const EVP_CIPHER *cipher,
+                    const unsigned char *key, const unsigned char *iv)
+{
+    return EVP_CipherInit(ctx, cipher, key, iv, 1);
+}
+
+int EVP_EncryptInit_ex(EVP_CIPHER_CTX *ctx, const EVP_CIPHER *cipher,
+                       ENGINE *impl, const unsigned char *key,
+                       const unsigned char *iv)
+{
+    return EVP_CipherInit_ex(ctx, cipher, impl, key, iv, 1);
+}
+
+int EVP_DecryptInit(EVP_CIPHER_CTX *ctx, const EVP_CIPHER *cipher,
+                    const unsigned char *key, const unsigned char *iv)
+{
+    return EVP_CipherInit(ctx, cipher, key, iv, 0);
+}
+
+int EVP_DecryptInit_ex(EVP_CIPHER_CTX *ctx, const EVP_CIPHER *cipher,
+                       ENGINE *impl, const unsigned char *key,
+                       const unsigned char *iv)
+{
+    return EVP_CipherInit_ex(ctx, cipher, impl, key, iv, 0);
+}
+
+/*
+ * According to the letter of standard difference between pointers
+ * is specified to be valid only within same object. This makes
+ * it formally challenging to determine if input and output buffers
+ * are not partially overlapping with standard pointer arithmetic.
+ */
+#ifdef PTRDIFF_T
+# undef PTRDIFF_T
+#endif
+#if defined(OPENSSL_SYS_VMS) && __INITIAL_POINTER_SIZE==64
+/*
+ * Then we have VMS that distinguishes itself by adhering to
+ * sizeof(size_t)==4 even in 64-bit builds, which means that
+ * difference between two pointers might be truncated to 32 bits.
+ * In the context one can even wonder how comparison for
+ * equality is implemented. To be on the safe side we adhere to
+ * PTRDIFF_T even for comparison for equality.
+ */
+# define PTRDIFF_T uint64_t
+#else
+# define PTRDIFF_T size_t
+#endif
+
+int is_partially_overlapping(const void *ptr1, const void *ptr2, int len)
+{
+    PTRDIFF_T diff = (PTRDIFF_T)ptr1-(PTRDIFF_T)ptr2;
+    /*
+     * Check for partially overlapping buffers. [Binary logical
+     * operations are used instead of boolean to minimize number
+     * of conditional branches.]
+     */
+    int overlapped = (len > 0) & (diff != 0) & ((diff < (PTRDIFF_T)len) |
+                                                (diff > (0 - (PTRDIFF_T)len)));
+
+    return overlapped;
+}
+
+int EVP_EncryptUpdate(EVP_CIPHER_CTX *ctx, unsigned char *out, int *outl,
+                      const unsigned char *in, int inl)
+{
+    int i, j, bl;
+
+    bl = ctx->cipher->block_size;
+
+    if (ctx->cipher->flags & EVP_CIPH_FLAG_CUSTOM_CIPHER) {
+        /* If block size > 1 then the cipher will have to do this check */
+        if (bl == 1 && is_partially_overlapping(out, in, inl)) {
+            EVPerr(EVP_F_EVP_ENCRYPTUPDATE, EVP_R_PARTIALLY_OVERLAPPING);
+            return 0;
+        }
+
+        i = ctx->cipher->do_cipher(ctx, out, in, inl);
+        if (i < 0)
+            return 0;
+        else
+            *outl = i;
+        return 1;
+    }
+
+    if (inl <= 0) {
+        *outl = 0;
+        return inl == 0;
+    }
+    if (is_partially_overlapping(out + ctx->buf_len, in, inl)) {
+        EVPerr(EVP_F_EVP_ENCRYPTUPDATE, EVP_R_PARTIALLY_OVERLAPPING);
+        return 0;
+    }
+
+    if (ctx->buf_len == 0 && (inl & (ctx->block_mask)) == 0) {
+        if (ctx->cipher->do_cipher(ctx, out, in, inl)) {
+            *outl = inl;
+            return 1;
+        } else {
+            *outl = 0;
+            return 0;
+        }
+    }
+    i = ctx->buf_len;
+    OPENSSL_assert(bl <= (int)sizeof(ctx->buf));
+    if (i != 0) {
+        if (bl - i > inl) {
+            memcpy(&(ctx->buf[i]), in, inl);
+            ctx->buf_len += inl;
+            *outl = 0;
+            return 1;
+        } else {
+            j = bl - i;
+            memcpy(&(ctx->buf[i]), in, j);
+            inl -= j;
+            in += j;
+            if (!ctx->cipher->do_cipher(ctx, out, ctx->buf, bl))
+                return 0;
+            out += bl;
+            *outl = bl;
+        }
+    } else
+        *outl = 0;
+    i = inl & (bl - 1);
+    inl -= i;
+    if (inl > 0) {
+        if (!ctx->cipher->do_cipher(ctx, out, in, inl))
+            return 0;
+        *outl += inl;
+    }
+
+    if (i != 0)
+        memcpy(ctx->buf, &(in[inl]), i);
+    ctx->buf_len = i;
+    return 1;
+}
+
+int EVP_EncryptFinal(EVP_CIPHER_CTX *ctx, unsigned char *out, int *outl)
+{
+    int ret;
+    ret = EVP_EncryptFinal_ex(ctx, out, outl);
+    return ret;
+}
+
+int EVP_EncryptFinal_ex(EVP_CIPHER_CTX *ctx, unsigned char *out, int *outl)
+{
+    int n, ret;
+    unsigned int i, b, bl;
+
+    if (ctx->cipher->flags & EVP_CIPH_FLAG_CUSTOM_CIPHER) {
+        ret = ctx->cipher->do_cipher(ctx, out, NULL, 0);
+        if (ret < 0)
+            return 0;
+        else
+            *outl = ret;
+        return 1;
+    }
+
+    b = ctx->cipher->block_size;
+    OPENSSL_assert(b <= sizeof ctx->buf);
+    if (b == 1) {
+        *outl = 0;
+        return 1;
+    }
+    bl = ctx->buf_len;
+    if (ctx->flags & EVP_CIPH_NO_PADDING) {
+        if (bl) {
+            EVPerr(EVP_F_EVP_ENCRYPTFINAL_EX,
+                   EVP_R_DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH);
+            return 0;
+        }
+        *outl = 0;
+        return 1;
+    }
+
+    n = b - bl;
+    for (i = bl; i < b; i++)
+        ctx->buf[i] = n;
+    ret = ctx->cipher->do_cipher(ctx, out, ctx->buf, b);
+
+    if (ret)
+        *outl = b;
+
+    return ret;
+}
+
+int EVP_DecryptUpdate(EVP_CIPHER_CTX *ctx, unsigned char *out, int *outl,
+                      const unsigned char *in, int inl)
+{
+    int fix_len;
+    unsigned int b;
+
+    b = ctx->cipher->block_size;
+
+    if (ctx->cipher->flags & EVP_CIPH_FLAG_CUSTOM_CIPHER) {
+        if (b == 1 && is_partially_overlapping(out, in, inl)) {
+            EVPerr(EVP_F_EVP_DECRYPTUPDATE, EVP_R_PARTIALLY_OVERLAPPING);
+            return 0;
+        }
+
+        fix_len = ctx->cipher->do_cipher(ctx, out, in, inl);
+        if (fix_len < 0) {
+            *outl = 0;
+            return 0;
+        } else
+            *outl = fix_len;
+        return 1;
+    }
+
+    if (inl <= 0) {
+        *outl = 0;
+        return inl == 0;
+    }
+
+    if (ctx->flags & EVP_CIPH_NO_PADDING)
+        return EVP_EncryptUpdate(ctx, out, outl, in, inl);
+
+    OPENSSL_assert(b <= sizeof ctx->final);
+
+    if (ctx->final_used) {
+        /* see comment about PTRDIFF_T comparison above */
+        if (((PTRDIFF_T)out == (PTRDIFF_T)in)
+            || is_partially_overlapping(out, in, b)) {
+            EVPerr(EVP_F_EVP_DECRYPTUPDATE, EVP_R_PARTIALLY_OVERLAPPING);
+            return 0;
+        }
+        memcpy(out, ctx->final, b);
+        out += b;
+        fix_len = 1;
+    } else
+        fix_len = 0;
+
+    if (!EVP_EncryptUpdate(ctx, out, outl, in, inl))
+        return 0;
+
+    /*
+     * if we have 'decrypted' a multiple of block size, make sure we have a
+     * copy of this last block
+     */
+    if (b > 1 && !ctx->buf_len) {
+        *outl -= b;
+        ctx->final_used = 1;
+        memcpy(ctx->final, &out[*outl], b);
+    } else
+        ctx->final_used = 0;
+
+    if (fix_len)
+        *outl += b;
+
+    return 1;
+}
+
+int EVP_DecryptFinal(EVP_CIPHER_CTX *ctx, unsigned char *out, int *outl)
+{
+    int ret;
+    ret = EVP_DecryptFinal_ex(ctx, out, outl);
+    return ret;
+}
+
+int EVP_DecryptFinal_ex(EVP_CIPHER_CTX *ctx, unsigned char *out, int *outl)
+{
+    int i, n;
+    unsigned int b;
+    *outl = 0;
+
+    if (ctx->cipher->flags & EVP_CIPH_FLAG_CUSTOM_CIPHER) {
+        i = ctx->cipher->do_cipher(ctx, out, NULL, 0);
+        if (i < 0)
+            return 0;
+        else
+            *outl = i;
+        return 1;
+    }
+
+    b = ctx->cipher->block_size;
+    if (ctx->flags & EVP_CIPH_NO_PADDING) {
+        if (ctx->buf_len) {
+            EVPerr(EVP_F_EVP_DECRYPTFINAL_EX,
+                   EVP_R_DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH);
+            return 0;
+        }
+        *outl = 0;
+        return 1;
+    }
+    if (b > 1) {
+        if (ctx->buf_len || !ctx->final_used) {
+            EVPerr(EVP_F_EVP_DECRYPTFINAL_EX, EVP_R_WRONG_FINAL_BLOCK_LENGTH);
+            return (0);
+        }
+        OPENSSL_assert(b <= sizeof ctx->final);
+
+        /*
+         * The following assumes that the ciphertext has been authenticated.
+         * Otherwise it provides a padding oracle.
+         */
+        n = ctx->final[b - 1];
+        if (n == 0 || n > (int)b) {
+            EVPerr(EVP_F_EVP_DECRYPTFINAL_EX, EVP_R_BAD_DECRYPT);
+            return (0);
+        }
+        for (i = 0; i < n; i++) {
+            if (ctx->final[--b] != n) {
+                EVPerr(EVP_F_EVP_DECRYPTFINAL_EX, EVP_R_BAD_DECRYPT);
+                return (0);
+            }
+        }
+        n = ctx->cipher->block_size - n;
+        for (i = 0; i < n; i++)
+            out[i] = ctx->final[i];
+        *outl = n;
+    } else
+        *outl = 0;
+    return (1);
+}
+
+int EVP_CIPHER_CTX_set_key_length(EVP_CIPHER_CTX *c, int keylen)
+{
+    if (c->cipher->flags & EVP_CIPH_CUSTOM_KEY_LENGTH)
+        return EVP_CIPHER_CTX_ctrl(c, EVP_CTRL_SET_KEY_LENGTH, keylen, NULL);
+    if (c->key_len == keylen)
+        return 1;
+    if ((keylen > 0) && (c->cipher->flags & EVP_CIPH_VARIABLE_LENGTH)) {
+        c->key_len = keylen;
+        return 1;
+    }
+    EVPerr(EVP_F_EVP_CIPHER_CTX_SET_KEY_LENGTH, EVP_R_INVALID_KEY_LENGTH);
+    return 0;
+}
+
+int EVP_CIPHER_CTX_set_padding(EVP_CIPHER_CTX *ctx, int pad)
+{
+    if (pad)
+        ctx->flags &= ~EVP_CIPH_NO_PADDING;
+    else
+        ctx->flags |= EVP_CIPH_NO_PADDING;
+    return 1;
+}
+
+int EVP_CIPHER_CTX_ctrl(EVP_CIPHER_CTX *ctx, int type, int arg, void *ptr)
+{
+    int ret;
+    if (!ctx->cipher) {
+        EVPerr(EVP_F_EVP_CIPHER_CTX_CTRL, EVP_R_NO_CIPHER_SET);
+        return 0;
+    }
+
+    if (!ctx->cipher->ctrl) {
+        EVPerr(EVP_F_EVP_CIPHER_CTX_CTRL, EVP_R_CTRL_NOT_IMPLEMENTED);
+        return 0;
+    }
+
+    ret = ctx->cipher->ctrl(ctx, type, arg, ptr);
+    if (ret == -1) {
+        EVPerr(EVP_F_EVP_CIPHER_CTX_CTRL,
+               EVP_R_CTRL_OPERATION_NOT_IMPLEMENTED);
+        return 0;
+    }
+    return ret;
+}
+
+int EVP_CIPHER_CTX_rand_key(EVP_CIPHER_CTX *ctx, unsigned char *key)
+{
+    if (ctx->cipher->flags & EVP_CIPH_RAND_KEY)
+        return EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_RAND_KEY, 0, key);
+    if (RAND_bytes(key, ctx->key_len) <= 0)
+        return 0;
+    return 1;
+}
+
+int EVP_CIPHER_CTX_copy(EVP_CIPHER_CTX *out, const EVP_CIPHER_CTX *in)
+{
+    if ((in == NULL) || (in->cipher == NULL)) {
+        EVPerr(EVP_F_EVP_CIPHER_CTX_COPY, EVP_R_INPUT_NOT_INITIALIZED);
+        return 0;
+    }
+#ifndef OPENSSL_NO_ENGINE
+    /* Make sure it's safe to copy a cipher context using an ENGINE */
+    if (in->engine && !ENGINE_init(in->engine)) {
+        EVPerr(EVP_F_EVP_CIPHER_CTX_COPY, ERR_R_ENGINE_LIB);
+        return 0;
+    }
+#endif
+
+    EVP_CIPHER_CTX_reset(out);
+    memcpy(out, in, sizeof(*out));
+
+    if (in->cipher_data && in->cipher->ctx_size) {
+        out->cipher_data = OPENSSL_malloc(in->cipher->ctx_size);
+        if (out->cipher_data == NULL) {
+            EVPerr(EVP_F_EVP_CIPHER_CTX_COPY, ERR_R_MALLOC_FAILURE);
+            return 0;
+        }
+        memcpy(out->cipher_data, in->cipher_data, in->cipher->ctx_size);
+    }
+
+    if (in->cipher->flags & EVP_CIPH_CUSTOM_COPY)
+        return in->cipher->ctrl((EVP_CIPHER_CTX *)in, EVP_CTRL_COPY, 0, out);
+    return 1;
+}
diff --git a/crypto/evp/evp_err.c b/crypto/evp/evp_err.c
new file mode 100644
index 000000000..b8a13cbdd
--- /dev/null
+++ b/crypto/evp/evp_err.c
@@ -0,0 +1,200 @@
+/*
+ * Generated by util/mkerr.pl DO NOT EDIT
+ * Copyright 1995-2018 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include 
+
+/* BEGIN ERROR CODES */
+#ifndef OPENSSL_NO_ERR
+
+# define ERR_FUNC(func) ERR_PACK(ERR_LIB_EVP,func,0)
+# define ERR_REASON(reason) ERR_PACK(ERR_LIB_EVP,0,reason)
+
+static ERR_STRING_DATA EVP_str_functs[] = {
+    {ERR_FUNC(EVP_F_AESNI_INIT_KEY), "aesni_init_key"},
+    {ERR_FUNC(EVP_F_AES_INIT_KEY), "aes_init_key"},
+    {ERR_FUNC(EVP_F_AES_OCB_CIPHER), "aes_ocb_cipher"},
+    {ERR_FUNC(EVP_F_AES_T4_INIT_KEY), "aes_t4_init_key"},
+    {ERR_FUNC(EVP_F_AES_WRAP_CIPHER), "aes_wrap_cipher"},
+    {ERR_FUNC(EVP_F_ALG_MODULE_INIT), "alg_module_init"},
+    {ERR_FUNC(EVP_F_CAMELLIA_INIT_KEY), "camellia_init_key"},
+    {ERR_FUNC(EVP_F_CHACHA20_POLY1305_CTRL), "chacha20_poly1305_ctrl"},
+    {ERR_FUNC(EVP_F_CMLL_T4_INIT_KEY), "cmll_t4_init_key"},
+    {ERR_FUNC(EVP_F_DES_EDE3_WRAP_CIPHER), "des_ede3_wrap_cipher"},
+    {ERR_FUNC(EVP_F_DO_SIGVER_INIT), "do_sigver_init"},
+    {ERR_FUNC(EVP_F_EVP_CIPHERINIT_EX), "EVP_CipherInit_ex"},
+    {ERR_FUNC(EVP_F_EVP_CIPHER_CTX_COPY), "EVP_CIPHER_CTX_copy"},
+    {ERR_FUNC(EVP_F_EVP_CIPHER_CTX_CTRL), "EVP_CIPHER_CTX_ctrl"},
+    {ERR_FUNC(EVP_F_EVP_CIPHER_CTX_SET_KEY_LENGTH),
+     "EVP_CIPHER_CTX_set_key_length"},
+    {ERR_FUNC(EVP_F_EVP_DECRYPTFINAL_EX), "EVP_DecryptFinal_ex"},
+    {ERR_FUNC(EVP_F_EVP_DECRYPTUPDATE), "EVP_DecryptUpdate"},
+    {ERR_FUNC(EVP_F_EVP_DIGESTINIT_EX), "EVP_DigestInit_ex"},
+    {ERR_FUNC(EVP_F_EVP_ENCRYPTFINAL_EX), "EVP_EncryptFinal_ex"},
+    {ERR_FUNC(EVP_F_EVP_ENCRYPTUPDATE), "EVP_EncryptUpdate"},
+    {ERR_FUNC(EVP_F_EVP_MD_CTX_COPY_EX), "EVP_MD_CTX_copy_ex"},
+    {ERR_FUNC(EVP_F_EVP_MD_SIZE), "EVP_MD_size"},
+    {ERR_FUNC(EVP_F_EVP_OPENINIT), "EVP_OpenInit"},
+    {ERR_FUNC(EVP_F_EVP_PBE_ALG_ADD), "EVP_PBE_alg_add"},
+    {ERR_FUNC(EVP_F_EVP_PBE_ALG_ADD_TYPE), "EVP_PBE_alg_add_type"},
+    {ERR_FUNC(EVP_F_EVP_PBE_CIPHERINIT), "EVP_PBE_CipherInit"},
+    {ERR_FUNC(EVP_F_EVP_PBE_SCRYPT), "EVP_PBE_scrypt"},
+    {ERR_FUNC(EVP_F_EVP_PKCS82PKEY), "EVP_PKCS82PKEY"},
+    {ERR_FUNC(EVP_F_EVP_PKEY2PKCS8), "EVP_PKEY2PKCS8"},
+    {ERR_FUNC(EVP_F_EVP_PKEY_COPY_PARAMETERS), "EVP_PKEY_copy_parameters"},
+    {ERR_FUNC(EVP_F_EVP_PKEY_CTX_CIPHERTEXT_SIZE),
+     "EVP_PKEY_CTX_ciphertext_size"},
+    {ERR_FUNC(EVP_F_EVP_PKEY_CTX_CTRL), "EVP_PKEY_CTX_ctrl"},
+    {ERR_FUNC(EVP_F_EVP_PKEY_CTX_CTRL_STR), "EVP_PKEY_CTX_ctrl_str"},
+    {ERR_FUNC(EVP_F_EVP_PKEY_CTX_DUP), "EVP_PKEY_CTX_dup"},
+    {ERR_FUNC(EVP_F_EVP_PKEY_DECRYPT), "EVP_PKEY_decrypt"},
+    {ERR_FUNC(EVP_F_EVP_PKEY_DECRYPT_INIT), "EVP_PKEY_decrypt_init"},
+    {ERR_FUNC(EVP_F_EVP_PKEY_DECRYPT_OLD), "EVP_PKEY_decrypt_old"},
+    {ERR_FUNC(EVP_F_EVP_PKEY_DERIVE), "EVP_PKEY_derive"},
+    {ERR_FUNC(EVP_F_EVP_PKEY_DERIVE_INIT), "EVP_PKEY_derive_init"},
+    {ERR_FUNC(EVP_F_EVP_PKEY_DERIVE_SET_PEER), "EVP_PKEY_derive_set_peer"},
+    {ERR_FUNC(EVP_F_EVP_PKEY_ENCRYPT), "EVP_PKEY_encrypt"},
+    {ERR_FUNC(EVP_F_EVP_PKEY_ENCRYPT_INIT), "EVP_PKEY_encrypt_init"},
+    {ERR_FUNC(EVP_F_EVP_PKEY_ENCRYPT_OLD), "EVP_PKEY_encrypt_old"},
+    {ERR_FUNC(EVP_F_EVP_PKEY_GET0_DH), "EVP_PKEY_get0_DH"},
+    {ERR_FUNC(EVP_F_EVP_PKEY_GET0_DSA), "EVP_PKEY_get0_DSA"},
+    {ERR_FUNC(EVP_F_EVP_PKEY_GET0_EC_KEY), "EVP_PKEY_get0_EC_KEY"},
+    {ERR_FUNC(EVP_F_EVP_PKEY_GET0_HMAC), "EVP_PKEY_get0_hmac"},
+    {ERR_FUNC(EVP_F_EVP_PKEY_GET0_PAILLIER), "EVP_PKEY_get0_PAILLIER"},
+    {ERR_FUNC(EVP_F_EVP_PKEY_GET0_RSA), "EVP_PKEY_get0_RSA"},
+    {ERR_FUNC(EVP_F_EVP_PKEY_GET0_SM9), "EVP_PKEY_get0_SM9"},
+    {ERR_FUNC(EVP_F_EVP_PKEY_GET0_SM9MASTERSECRET),
+     "EVP_PKEY_get0_SM9MasterSecret"},
+    {ERR_FUNC(EVP_F_EVP_PKEY_GET0_SM9PUBLICPARAMETERS),
+     "EVP_PKEY_get0_SM9PublicParameters"},
+    {ERR_FUNC(EVP_F_EVP_PKEY_GET0_SM9_MASTER), "EVP_PKEY_get0_SM9_MASTER"},
+    {ERR_FUNC(EVP_F_EVP_PKEY_KEYGEN), "EVP_PKEY_keygen"},
+    {ERR_FUNC(EVP_F_EVP_PKEY_KEYGEN_INIT), "EVP_PKEY_keygen_init"},
+    {ERR_FUNC(EVP_F_EVP_PKEY_NEW), "EVP_PKEY_new"},
+    {ERR_FUNC(EVP_F_EVP_PKEY_PARAMGEN), "EVP_PKEY_paramgen"},
+    {ERR_FUNC(EVP_F_EVP_PKEY_PARAMGEN_INIT), "EVP_PKEY_paramgen_init"},
+    {ERR_FUNC(EVP_F_EVP_PKEY_SIGN), "EVP_PKEY_sign"},
+    {ERR_FUNC(EVP_F_EVP_PKEY_SIGN_INIT), "EVP_PKEY_sign_init"},
+    {ERR_FUNC(EVP_F_EVP_PKEY_VERIFY), "EVP_PKEY_verify"},
+    {ERR_FUNC(EVP_F_EVP_PKEY_VERIFY_INIT), "EVP_PKEY_verify_init"},
+    {ERR_FUNC(EVP_F_EVP_PKEY_VERIFY_RECOVER), "EVP_PKEY_verify_recover"},
+    {ERR_FUNC(EVP_F_EVP_PKEY_VERIFY_RECOVER_INIT),
+     "EVP_PKEY_verify_recover_init"},
+    {ERR_FUNC(EVP_F_EVP_SIGNFINAL), "EVP_SignFinal"},
+    {ERR_FUNC(EVP_F_EVP_VERIFYFINAL), "EVP_VerifyFinal"},
+    {ERR_FUNC(EVP_F_INT_CTX_NEW), "int_ctx_new"},
+    {ERR_FUNC(EVP_F_PKCS5_PBE_KEYIVGEN), "PKCS5_PBE_keyivgen"},
+    {ERR_FUNC(EVP_F_PKCS5_V2_PBE_KEYIVGEN), "PKCS5_v2_PBE_keyivgen"},
+    {ERR_FUNC(EVP_F_PKCS5_V2_PBKDF2_KEYIVGEN), "PKCS5_v2_PBKDF2_keyivgen"},
+    {ERR_FUNC(EVP_F_PKCS5_V2_SCRYPT_KEYIVGEN), "PKCS5_v2_scrypt_keyivgen"},
+    {ERR_FUNC(EVP_F_PKEY_SET_TYPE), "pkey_set_type"},
+    {ERR_FUNC(EVP_F_RC2_MAGIC_TO_METH), "rc2_magic_to_meth"},
+    {ERR_FUNC(EVP_F_RC5_CTRL), "rc5_ctrl"},
+    {0, NULL}
+};
+
+static ERR_STRING_DATA EVP_str_reasons[] = {
+    {ERR_REASON(EVP_R_AES_KEY_SETUP_FAILED), "aes key setup failed"},
+    {ERR_REASON(EVP_R_BAD_DECRYPT), "bad decrypt"},
+    {ERR_REASON(EVP_R_BUFFER_TOO_SMALL), "buffer too small"},
+    {ERR_REASON(EVP_R_CAMELLIA_KEY_SETUP_FAILED),
+     "camellia key setup failed"},
+    {ERR_REASON(EVP_R_CIPHER_PARAMETER_ERROR), "cipher parameter error"},
+    {ERR_REASON(EVP_R_COMMAND_NOT_SUPPORTED), "command not supported"},
+    {ERR_REASON(EVP_R_COPY_ERROR), "copy error"},
+    {ERR_REASON(EVP_R_CTRL_NOT_IMPLEMENTED), "ctrl not implemented"},
+    {ERR_REASON(EVP_R_CTRL_OPERATION_NOT_IMPLEMENTED),
+     "ctrl operation not implemented"},
+    {ERR_REASON(EVP_R_DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH),
+     "data not multiple of block length"},
+    {ERR_REASON(EVP_R_DECODE_ERROR), "decode error"},
+    {ERR_REASON(EVP_R_DIFFERENT_KEY_TYPES), "different key types"},
+    {ERR_REASON(EVP_R_DIFFERENT_PARAMETERS), "different parameters"},
+    {ERR_REASON(EVP_R_ERROR_LOADING_SECTION), "error loading section"},
+    {ERR_REASON(EVP_R_ERROR_SETTING_FIPS_MODE), "error setting fips mode"},
+    {ERR_REASON(EVP_R_EXPECTING_AN_HMAC_KEY), "expecting an hmac key"},
+    {ERR_REASON(EVP_R_EXPECTING_AN_RSA_KEY), "expecting an rsa key"},
+    {ERR_REASON(EVP_R_EXPECTING_A_DH_KEY), "expecting a dh key"},
+    {ERR_REASON(EVP_R_EXPECTING_A_DSA_KEY), "expecting a dsa key"},
+    {ERR_REASON(EVP_R_EXPECTING_A_EC_KEY), "expecting a ec key"},
+    {ERR_REASON(EVP_R_EXPECTING_A_PAILLIER), "expecting a paillier"},
+    {ERR_REASON(EVP_R_EXPECTING_A_SM9_KEY), "expecting a sm9 key"},
+    {ERR_REASON(EVP_R_EXPECTING_A_SM9_MASTER_KEY),
+     "expecting a sm9 master key"},
+    {ERR_REASON(EVP_R_EXPECTING_A_SM9_MASTER_SECRET),
+     "expecting a sm9 master secret"},
+    {ERR_REASON(EVP_R_FIPS_MODE_NOT_SUPPORTED), "fips mode not supported"},
+    {ERR_REASON(EVP_R_ILLEGAL_SCRYPT_PARAMETERS),
+     "illegal scrypt parameters"},
+    {ERR_REASON(EVP_R_INITIALIZATION_ERROR), "initialization error"},
+    {ERR_REASON(EVP_R_INPUT_NOT_INITIALIZED), "input not initialized"},
+    {ERR_REASON(EVP_R_INVALID_DIGEST), "invalid digest"},
+    {ERR_REASON(EVP_R_INVALID_FIPS_MODE), "invalid fips mode"},
+    {ERR_REASON(EVP_R_INVALID_INPUT_LENGTH), "invalid input length"},
+    {ERR_REASON(EVP_R_INVALID_KEY), "invalid key"},
+    {ERR_REASON(EVP_R_INVALID_KEY_LENGTH), "invalid key length"},
+    {ERR_REASON(EVP_R_INVALID_OPERATION), "invalid operation"},
+    {ERR_REASON(EVP_R_KEYGEN_FAILURE), "keygen failure"},
+    {ERR_REASON(EVP_R_MEMORY_LIMIT_EXCEEDED), "memory limit exceeded"},
+    {ERR_REASON(EVP_R_MESSAGE_DIGEST_IS_NULL), "message digest is null"},
+    {ERR_REASON(EVP_R_METHOD_NOT_SUPPORTED), "method not supported"},
+    {ERR_REASON(EVP_R_MISSING_PARAMETERS), "missing parameters"},
+    {ERR_REASON(EVP_R_NO_AVAIABLE_DIGEST), "no avaiable digest"},
+    {ERR_REASON(EVP_R_NO_CIPHER_SET), "no cipher set"},
+    {ERR_REASON(EVP_R_NO_DEFAULT_DIGEST), "no default digest"},
+    {ERR_REASON(EVP_R_NO_DIGEST_SET), "no digest set"},
+    {ERR_REASON(EVP_R_NO_KEY_SET), "no key set"},
+    {ERR_REASON(EVP_R_NO_OPERATION_SET), "no operation set"},
+    {ERR_REASON(EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE),
+     "operation not supported for this keytype"},
+    {ERR_REASON(EVP_R_OPERATON_NOT_INITIALIZED), "operaton not initialized"},
+    {ERR_REASON(EVP_R_PARTIALLY_OVERLAPPING),
+     "partially overlapping buffers"},
+    {ERR_REASON(EVP_R_PRIVATE_KEY_DECODE_ERROR), "private key decode error"},
+    {ERR_REASON(EVP_R_PRIVATE_KEY_ENCODE_ERROR), "private key encode error"},
+    {ERR_REASON(EVP_R_PUBLIC_KEY_NOT_RSA), "public key not rsa"},
+    {ERR_REASON(EVP_R_PUBLIC_KEY_NOT_RSA_OR_EC), "public key not rsa or ec"},
+    {ERR_REASON(EVP_R_RSA_PUBLIC_ENCRYPT_FAILED),
+     "rsa public encrypt failed"},
+    {ERR_REASON(EVP_R_UNKNOWN_CIPHER), "unknown cipher"},
+    {ERR_REASON(EVP_R_UNKNOWN_DIGEST), "unknown digest"},
+    {ERR_REASON(EVP_R_UNKNOWN_OPTION), "unknown option"},
+    {ERR_REASON(EVP_R_UNKNOWN_PBE_ALGORITHM), "unknown pbe algorithm"},
+    {ERR_REASON(EVP_R_UNSUPPORTED_ALGORITHM), "unsupported algorithm"},
+    {ERR_REASON(EVP_R_UNSUPPORTED_CIPHER), "unsupported cipher"},
+    {ERR_REASON(EVP_R_UNSUPPORTED_KEYLENGTH), "unsupported keylength"},
+    {ERR_REASON(EVP_R_UNSUPPORTED_KEY_DERIVATION_FUNCTION),
+     "unsupported key derivation function"},
+    {ERR_REASON(EVP_R_UNSUPPORTED_KEY_SIZE), "unsupported key size"},
+    {ERR_REASON(EVP_R_UNSUPPORTED_NUMBER_OF_ROUNDS),
+     "unsupported number of rounds"},
+    {ERR_REASON(EVP_R_UNSUPPORTED_PRF), "unsupported prf"},
+    {ERR_REASON(EVP_R_UNSUPPORTED_PRIVATE_KEY_ALGORITHM),
+     "unsupported private key algorithm"},
+    {ERR_REASON(EVP_R_UNSUPPORTED_SALT_TYPE), "unsupported salt type"},
+    {ERR_REASON(EVP_R_WRAP_MODE_NOT_ALLOWED), "wrap mode not allowed"},
+    {ERR_REASON(EVP_R_WRONG_FINAL_BLOCK_LENGTH), "wrong final block length"},
+    {0, NULL}
+};
+
+#endif
+
+int ERR_load_EVP_strings(void)
+{
+#ifndef OPENSSL_NO_ERR
+
+    if (ERR_func_error_string(EVP_str_functs[0].error) == NULL) {
+        ERR_load_strings(0, EVP_str_functs);
+        ERR_load_strings(0, EVP_str_reasons);
+    }
+#endif
+    return 1;
+}
diff --git a/crypto/evp/evp_key.c b/crypto/evp/evp_key.c
new file mode 100644
index 000000000..8a4297cf6
--- /dev/null
+++ b/crypto/evp/evp_key.c
@@ -0,0 +1,150 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+#include 
+#include 
+
+#ifndef OPENSSL_NO_UI
+/* should be init to zeros. */
+static char prompt_string[80];
+
+void EVP_set_pw_prompt(const char *prompt)
+{
+    if (prompt == NULL)
+        prompt_string[0] = '\0';
+    else {
+        strncpy(prompt_string, prompt, 79);
+        prompt_string[79] = '\0';
+    }
+}
+
+char *EVP_get_pw_prompt(void)
+{
+    if (prompt_string[0] == '\0')
+        return (NULL);
+    else
+        return (prompt_string);
+}
+
+/*
+ * For historical reasons, the standard function for reading passwords is in
+ * the DES library -- if someone ever wants to disable DES, this function
+ * will fail
+ */
+int EVP_read_pw_string(char *buf, int len, const char *prompt, int verify)
+{
+    return EVP_read_pw_string_min(buf, 0, len, prompt, verify);
+}
+
+int EVP_read_pw_string_min(char *buf, int min, int len, const char *prompt,
+                           int verify)
+{
+    int ret;
+    char buff[BUFSIZ];
+    UI *ui;
+
+    if ((prompt == NULL) && (prompt_string[0] != '\0'))
+        prompt = prompt_string;
+    ui = UI_new();
+    if (ui == NULL)
+        return -1;
+    UI_add_input_string(ui, prompt, 0, buf, min,
+                        (len >= BUFSIZ) ? BUFSIZ - 1 : len);
+    if (verify)
+        UI_add_verify_string(ui, prompt, 0,
+                             buff, min, (len >= BUFSIZ) ? BUFSIZ - 1 : len,
+                             buf);
+    ret = UI_process(ui);
+    UI_free(ui);
+    OPENSSL_cleanse(buff, BUFSIZ);
+    return ret;
+}
+#endif /* OPENSSL_NO_UI */
+
+int EVP_BytesToKey(const EVP_CIPHER *type, const EVP_MD *md,
+                   const unsigned char *salt, const unsigned char *data,
+                   int datal, int count, unsigned char *key,
+                   unsigned char *iv)
+{
+    EVP_MD_CTX *c;
+    unsigned char md_buf[EVP_MAX_MD_SIZE];
+    int niv, nkey, addmd = 0;
+    unsigned int mds = 0, i;
+    int rv = 0;
+    nkey = EVP_CIPHER_key_length(type);
+    niv = EVP_CIPHER_iv_length(type);
+    OPENSSL_assert(nkey <= EVP_MAX_KEY_LENGTH);
+    OPENSSL_assert(niv <= EVP_MAX_IV_LENGTH);
+
+    if (data == NULL)
+        return (nkey);
+
+    c = EVP_MD_CTX_new();
+    if (c == NULL)
+        goto err;
+    for (;;) {
+        if (!EVP_DigestInit_ex(c, md, NULL))
+            goto err;
+        if (addmd++)
+            if (!EVP_DigestUpdate(c, &(md_buf[0]), mds))
+                goto err;
+        if (!EVP_DigestUpdate(c, data, datal))
+            goto err;
+        if (salt != NULL)
+            if (!EVP_DigestUpdate(c, salt, PKCS5_SALT_LEN))
+                goto err;
+        if (!EVP_DigestFinal_ex(c, &(md_buf[0]), &mds))
+            goto err;
+
+        for (i = 1; i < (unsigned int)count; i++) {
+            if (!EVP_DigestInit_ex(c, md, NULL))
+                goto err;
+            if (!EVP_DigestUpdate(c, &(md_buf[0]), mds))
+                goto err;
+            if (!EVP_DigestFinal_ex(c, &(md_buf[0]), &mds))
+                goto err;
+        }
+        i = 0;
+        if (nkey) {
+            for (;;) {
+                if (nkey == 0)
+                    break;
+                if (i == mds)
+                    break;
+                if (key != NULL)
+                    *(key++) = md_buf[i];
+                nkey--;
+                i++;
+            }
+        }
+        if (niv && (i != mds)) {
+            for (;;) {
+                if (niv == 0)
+                    break;
+                if (i == mds)
+                    break;
+                if (iv != NULL)
+                    *(iv++) = md_buf[i];
+                niv--;
+                i++;
+            }
+        }
+        if ((nkey == 0) && (niv == 0))
+            break;
+    }
+    rv = EVP_CIPHER_key_length(type);
+ err:
+    EVP_MD_CTX_free(c);
+    OPENSSL_cleanse(md_buf, sizeof(md_buf));
+    return rv;
+}
diff --git a/crypto/evp/evp_lib.c b/crypto/evp/evp_lib.c
new file mode 100644
index 000000000..f13d10e65
--- /dev/null
+++ b/crypto/evp/evp_lib.c
@@ -0,0 +1,579 @@
+/* ====================================================================
+ * Copyright (c) 2016 The GmSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the GmSSL Project.
+ *    (http://gmssl.org/)"
+ *
+ * 4. The name "GmSSL Project" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    guanzhi1980@gmail.com.
+ *
+ * 5. Products derived from this software may not be called "GmSSL"
+ *    nor may "GmSSL" appear in their names without prior written
+ *    permission of the GmSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the GmSSL Project
+ *    (http://gmssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE GmSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE GmSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+#include "internal/evp_int.h"
+#include "evp_locl.h"
+
+int EVP_CIPHER_param_to_asn1(EVP_CIPHER_CTX *c, ASN1_TYPE *type)
+{
+    int ret;
+
+    if (c->cipher->set_asn1_parameters != NULL)
+        ret = c->cipher->set_asn1_parameters(c, type);
+    else if (c->cipher->flags & EVP_CIPH_FLAG_DEFAULT_ASN1) {
+        switch (EVP_CIPHER_CTX_mode(c)) {
+        case EVP_CIPH_WRAP_MODE:
+            if (EVP_CIPHER_CTX_nid(c) == NID_id_smime_alg_CMS3DESwrap)
+                ASN1_TYPE_set(type, V_ASN1_NULL, NULL);
+            ret = 1;
+            break;
+
+        case EVP_CIPH_GCM_MODE:
+        case EVP_CIPH_CCM_MODE:
+        case EVP_CIPH_XTS_MODE:
+        case EVP_CIPH_OCB_MODE:
+            ret = -1;
+            break;
+
+        default:
+            ret = EVP_CIPHER_set_asn1_iv(c, type);
+        }
+    } else
+        ret = -1;
+    return (ret);
+}
+
+int EVP_CIPHER_asn1_to_param(EVP_CIPHER_CTX *c, ASN1_TYPE *type)
+{
+    int ret;
+
+    if (c->cipher->get_asn1_parameters != NULL)
+        ret = c->cipher->get_asn1_parameters(c, type);
+    else if (c->cipher->flags & EVP_CIPH_FLAG_DEFAULT_ASN1) {
+        switch (EVP_CIPHER_CTX_mode(c)) {
+
+        case EVP_CIPH_WRAP_MODE:
+            ret = 1;
+            break;
+
+        case EVP_CIPH_GCM_MODE:
+        case EVP_CIPH_CCM_MODE:
+        case EVP_CIPH_XTS_MODE:
+        case EVP_CIPH_OCB_MODE:
+            ret = -1;
+            break;
+
+        default:
+            ret = EVP_CIPHER_get_asn1_iv(c, type);
+            break;
+        }
+    } else
+        ret = -1;
+    return (ret);
+}
+
+int EVP_CIPHER_get_asn1_iv(EVP_CIPHER_CTX *c, ASN1_TYPE *type)
+{
+    int i = 0;
+    unsigned int l;
+
+    if (type != NULL) {
+        l = EVP_CIPHER_CTX_iv_length(c);
+        OPENSSL_assert(l <= sizeof(c->iv));
+        i = ASN1_TYPE_get_octetstring(type, c->oiv, l);
+        if (i != (int)l)
+            return (-1);
+        else if (i > 0)
+            memcpy(c->iv, c->oiv, l);
+    }
+    return (i);
+}
+
+int EVP_CIPHER_set_asn1_iv(EVP_CIPHER_CTX *c, ASN1_TYPE *type)
+{
+    int i = 0;
+    unsigned int j;
+
+    if (type != NULL) {
+        j = EVP_CIPHER_CTX_iv_length(c);
+        OPENSSL_assert(j <= sizeof(c->iv));
+        i = ASN1_TYPE_set_octetstring(type, c->oiv, j);
+    }
+    return (i);
+}
+
+/* Convert the various cipher NIDs and dummies to a proper OID NID */
+int EVP_CIPHER_type(const EVP_CIPHER *ctx)
+{
+    int nid;
+    ASN1_OBJECT *otmp;
+    nid = EVP_CIPHER_nid(ctx);
+
+    switch (nid) {
+
+    case NID_rc2_cbc:
+    case NID_rc2_64_cbc:
+    case NID_rc2_40_cbc:
+
+        return NID_rc2_cbc;
+
+    case NID_rc4:
+    case NID_rc4_40:
+
+        return NID_rc4;
+
+    case NID_aes_128_cfb128:
+    case NID_aes_128_cfb8:
+    case NID_aes_128_cfb1:
+
+        return NID_aes_128_cfb128;
+
+    case NID_aes_192_cfb128:
+    case NID_aes_192_cfb8:
+    case NID_aes_192_cfb1:
+
+        return NID_aes_192_cfb128;
+
+    case NID_aes_256_cfb128:
+    case NID_aes_256_cfb8:
+    case NID_aes_256_cfb1:
+
+        return NID_aes_256_cfb128;
+
+    case NID_des_cfb64:
+    case NID_des_cfb8:
+    case NID_des_cfb1:
+
+        return NID_des_cfb64;
+
+    case NID_des_ede3_cfb64:
+    case NID_des_ede3_cfb8:
+    case NID_des_ede3_cfb1:
+
+        return NID_des_cfb64;
+
+#ifndef OPENSSL_NO_SMS4
+    case NID_sms4_cfb128:
+    case NID_sms4_cfb8:
+    case NID_sms4_cfb1:
+
+        return NID_sms4_cfb128;
+#endif
+
+#ifndef OPENSSL_NO_SM1
+    case NID_sm1_cfb128:
+    case NID_sm1_cfb8:
+    case NID_sm1_cfb1:
+
+        return NID_sm1_cfb128;
+#endif
+
+#ifndef OPENSSL_NO_SSF33
+    case NID_ssf33_cfb128:
+    case NID_ssf33_cfb8:
+    case NID_ssf33_cfb1:
+
+        return NID_ssf33_cfb128;
+#endif
+
+    default:
+        /* Check it has an OID and it is valid */
+        otmp = OBJ_nid2obj(nid);
+        if (OBJ_get0_data(otmp) == NULL)
+            nid = NID_undef;
+        ASN1_OBJECT_free(otmp);
+        return nid;
+    }
+}
+
+int EVP_CIPHER_block_size(const EVP_CIPHER *e)
+{
+    return e->block_size;
+}
+
+int EVP_CIPHER_CTX_block_size(const EVP_CIPHER_CTX *ctx)
+{
+    return ctx->cipher->block_size;
+}
+
+int EVP_CIPHER_impl_ctx_size(const EVP_CIPHER *e)
+{
+    return e->ctx_size;
+}
+
+int EVP_Cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
+               const unsigned char *in, unsigned int inl)
+{
+    return ctx->cipher->do_cipher(ctx, out, in, inl);
+}
+
+const EVP_CIPHER *EVP_CIPHER_CTX_cipher(const EVP_CIPHER_CTX *ctx)
+{
+    return ctx->cipher;
+}
+
+int EVP_CIPHER_CTX_encrypting(const EVP_CIPHER_CTX *ctx)
+{
+    return ctx->encrypt;
+}
+
+unsigned long EVP_CIPHER_flags(const EVP_CIPHER *cipher)
+{
+    return cipher->flags;
+}
+
+void *EVP_CIPHER_CTX_get_app_data(const EVP_CIPHER_CTX *ctx)
+{
+    return ctx->app_data;
+}
+
+void EVP_CIPHER_CTX_set_app_data(EVP_CIPHER_CTX *ctx, void *data)
+{
+    ctx->app_data = data;
+}
+
+void *EVP_CIPHER_CTX_get_cipher_data(const EVP_CIPHER_CTX *ctx)
+{
+    return ctx->cipher_data;
+}
+
+void *EVP_CIPHER_CTX_set_cipher_data(EVP_CIPHER_CTX *ctx, void *cipher_data)
+{
+    void *old_cipher_data;
+
+    old_cipher_data = ctx->cipher_data;
+    ctx->cipher_data = cipher_data;
+
+    return old_cipher_data;
+}
+
+int EVP_CIPHER_iv_length(const EVP_CIPHER *cipher)
+{
+    return cipher->iv_len;
+}
+
+int EVP_CIPHER_CTX_iv_length(const EVP_CIPHER_CTX *ctx)
+{
+    return ctx->cipher->iv_len;
+}
+
+const unsigned char *EVP_CIPHER_CTX_original_iv(const EVP_CIPHER_CTX *ctx)
+{
+    return ctx->oiv;
+}
+
+const unsigned char *EVP_CIPHER_CTX_iv(const EVP_CIPHER_CTX *ctx)
+{
+    return ctx->iv;
+}
+
+unsigned char *EVP_CIPHER_CTX_iv_noconst(EVP_CIPHER_CTX *ctx)
+{
+    return ctx->iv;
+}
+
+unsigned char *EVP_CIPHER_CTX_buf_noconst(EVP_CIPHER_CTX *ctx)
+{
+    return ctx->buf;
+}
+
+int EVP_CIPHER_CTX_num(const EVP_CIPHER_CTX *ctx)
+{
+    return ctx->num;
+}
+
+void EVP_CIPHER_CTX_set_num(EVP_CIPHER_CTX *ctx, int num)
+{
+    ctx->num = num;
+}
+
+int EVP_CIPHER_key_length(const EVP_CIPHER *cipher)
+{
+    return cipher->key_len;
+}
+
+int EVP_CIPHER_CTX_key_length(const EVP_CIPHER_CTX *ctx)
+{
+    return ctx->key_len;
+}
+
+int EVP_CIPHER_nid(const EVP_CIPHER *cipher)
+{
+    return cipher->nid;
+}
+
+int EVP_CIPHER_CTX_nid(const EVP_CIPHER_CTX *ctx)
+{
+    return ctx->cipher->nid;
+}
+
+int EVP_MD_block_size(const EVP_MD *md)
+{
+    return md->block_size;
+}
+
+int EVP_MD_type(const EVP_MD *md)
+{
+    return md->type;
+}
+
+int EVP_MD_pkey_type(const EVP_MD *md)
+{
+    return md->pkey_type;
+}
+
+int EVP_MD_size(const EVP_MD *md)
+{
+    if (!md) {
+        EVPerr(EVP_F_EVP_MD_SIZE, EVP_R_MESSAGE_DIGEST_IS_NULL);
+        return -1;
+    }
+    return md->md_size;
+}
+
+unsigned long EVP_MD_flags(const EVP_MD *md)
+{
+    return md->flags;
+}
+
+EVP_MD *EVP_MD_meth_new(int md_type, int pkey_type)
+{
+    EVP_MD *md = OPENSSL_zalloc(sizeof(*md));
+
+    if (md != NULL) {
+        md->type = md_type;
+        md->pkey_type = pkey_type;
+    }
+    return md;
+}
+EVP_MD *EVP_MD_meth_dup(const EVP_MD *md)
+{
+    EVP_MD *to = EVP_MD_meth_new(md->type, md->pkey_type);
+
+    if (to != NULL)
+        memcpy(to, md, sizeof(*to));
+    return to;
+}
+void EVP_MD_meth_free(EVP_MD *md)
+{
+    OPENSSL_free(md);
+}
+int EVP_MD_meth_set_input_blocksize(EVP_MD *md, int blocksize)
+{
+    md->block_size = blocksize;
+    return 1;
+}
+int EVP_MD_meth_set_result_size(EVP_MD *md, int resultsize)
+{
+    md->md_size = resultsize;
+    return 1;
+}
+int EVP_MD_meth_set_app_datasize(EVP_MD *md, int datasize)
+{
+    md->ctx_size = datasize;
+    return 1;
+}
+int EVP_MD_meth_set_flags(EVP_MD *md, unsigned long flags)
+{
+    md->flags = flags;
+    return 1;
+}
+int EVP_MD_meth_set_init(EVP_MD *md, int (*init)(EVP_MD_CTX *ctx))
+{
+    md->init = init;
+    return 1;
+}
+int EVP_MD_meth_set_update(EVP_MD *md, int (*update)(EVP_MD_CTX *ctx,
+                                                     const void *data,
+                                                     size_t count))
+{
+    md->update = update;
+    return 1;
+}
+int EVP_MD_meth_set_final(EVP_MD *md, int (*final)(EVP_MD_CTX *ctx,
+                                                   unsigned char *md))
+{
+    md->final = final;
+    return 1;
+}
+int EVP_MD_meth_set_copy(EVP_MD *md, int (*copy)(EVP_MD_CTX *to,
+                                                 const EVP_MD_CTX *from))
+{
+    md->copy = copy;
+    return 1;
+}
+int EVP_MD_meth_set_cleanup(EVP_MD *md, int (*cleanup)(EVP_MD_CTX *ctx))
+{
+    md->cleanup = cleanup;
+    return 1;
+}
+int EVP_MD_meth_set_ctrl(EVP_MD *md, int (*ctrl)(EVP_MD_CTX *ctx, int cmd,
+                                                 int p1, void *p2))
+{
+    md->md_ctrl = ctrl;
+    return 1;
+}
+
+int EVP_MD_meth_get_input_blocksize(const EVP_MD *md)
+{
+    return md->block_size;
+}
+int EVP_MD_meth_get_result_size(const EVP_MD *md)
+{
+    return md->md_size;
+}
+int EVP_MD_meth_get_app_datasize(const EVP_MD *md)
+{
+    return md->ctx_size;
+}
+unsigned long EVP_MD_meth_get_flags(const EVP_MD *md)
+{
+    return md->flags;
+}
+int (*EVP_MD_meth_get_init(const EVP_MD *md))(EVP_MD_CTX *ctx)
+{
+    return md->init;
+}
+int (*EVP_MD_meth_get_update(const EVP_MD *md))(EVP_MD_CTX *ctx,
+                                                const void *data,
+                                                size_t count)
+{
+    return md->update;
+}
+int (*EVP_MD_meth_get_final(const EVP_MD *md))(EVP_MD_CTX *ctx,
+                                               unsigned char *md)
+{
+    return md->final;
+}
+int (*EVP_MD_meth_get_copy(const EVP_MD *md))(EVP_MD_CTX *to,
+                                              const EVP_MD_CTX *from)
+{
+    return md->copy;
+}
+int (*EVP_MD_meth_get_cleanup(const EVP_MD *md))(EVP_MD_CTX *ctx)
+{
+    return md->cleanup;
+}
+int (*EVP_MD_meth_get_ctrl(const EVP_MD *md))(EVP_MD_CTX *ctx, int cmd,
+                                              int p1, void *p2)
+{
+    return md->md_ctrl;
+}
+
+const EVP_MD *EVP_MD_CTX_md(const EVP_MD_CTX *ctx)
+{
+    if (!ctx)
+        return NULL;
+    return ctx->digest;
+}
+
+EVP_PKEY_CTX *EVP_MD_CTX_pkey_ctx(const EVP_MD_CTX *ctx)
+{
+    return ctx->pctx;
+}
+
+void *EVP_MD_CTX_md_data(const EVP_MD_CTX *ctx)
+{
+    return ctx->md_data;
+}
+
+void *EVP_MD_CTX_set_md_data(EVP_MD_CTX *ctx, void *md_data)
+{
+    void *old_md_data;
+
+    old_md_data = ctx->md_data;
+    ctx->md_data = md_data;
+
+    return old_md_data;
+}
+
+int (*EVP_MD_CTX_update_fn(EVP_MD_CTX *ctx))(EVP_MD_CTX *ctx,
+                                             const void *data, size_t count)
+{
+    return ctx->update;
+}
+
+void EVP_MD_CTX_set_update_fn(EVP_MD_CTX *ctx,
+                              int (*update) (EVP_MD_CTX *ctx,
+                                             const void *data, size_t count))
+{
+    ctx->update = update;
+}
+
+void EVP_MD_CTX_set_flags(EVP_MD_CTX *ctx, int flags)
+{
+    ctx->flags |= flags;
+}
+
+void EVP_MD_CTX_clear_flags(EVP_MD_CTX *ctx, int flags)
+{
+    ctx->flags &= ~flags;
+}
+
+int EVP_MD_CTX_test_flags(const EVP_MD_CTX *ctx, int flags)
+{
+    return (ctx->flags & flags);
+}
+
+void EVP_CIPHER_CTX_set_flags(EVP_CIPHER_CTX *ctx, int flags)
+{
+    ctx->flags |= flags;
+}
+
+void EVP_CIPHER_CTX_clear_flags(EVP_CIPHER_CTX *ctx, int flags)
+{
+    ctx->flags &= ~flags;
+}
+
+int EVP_CIPHER_CTX_test_flags(const EVP_CIPHER_CTX *ctx, int flags)
+{
+    return (ctx->flags & flags);
+}
diff --git a/crypto/evp/evp_locl.h b/crypto/evp/evp_locl.h
new file mode 100644
index 000000000..d5de73a76
--- /dev/null
+++ b/crypto/evp/evp_locl.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2000-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/* EVP_MD_CTX related stuff */
+
+#ifndef OPENSSL_NO_SM2
+# define EVP_MD_CTX_FLAG_UPDATED         0x0400
+#endif
+
+struct evp_md_ctx_st {
+    const EVP_MD *digest;
+    ENGINE *engine;             /* functional reference if 'digest' is
+                                 * ENGINE-provided */
+    unsigned long flags;
+    void *md_data;
+    /* Public key context for sign/verify */
+    EVP_PKEY_CTX *pctx;
+    /* Update function: usually copied from EVP_MD */
+    int (*update) (EVP_MD_CTX *ctx, const void *data, size_t count);
+} /* EVP_MD_CTX */ ;
+
+struct evp_cipher_ctx_st {
+    const EVP_CIPHER *cipher;
+    ENGINE *engine;             /* functional reference if 'cipher' is
+                                 * ENGINE-provided */
+    int encrypt;                /* encrypt or decrypt */
+    int buf_len;                /* number we have left */
+    unsigned char oiv[EVP_MAX_IV_LENGTH]; /* original iv */
+    unsigned char iv[EVP_MAX_IV_LENGTH]; /* working iv */
+    unsigned char buf[EVP_MAX_BLOCK_LENGTH]; /* saved partial block */
+    int num;                    /* used by cfb/ofb/ctr mode */
+    /* FIXME: Should this even exist? It appears unused */
+    void *app_data;             /* application stuff */
+    int key_len;                /* May change for variable length cipher */
+    unsigned long flags;        /* Various flags */
+    void *cipher_data;          /* per EVP data */
+    int final_used;
+    int block_mask;
+    unsigned char final[EVP_MAX_BLOCK_LENGTH]; /* possible final block */
+} /* EVP_CIPHER_CTX */ ;
+
+int PKCS5_v2_PBKDF2_keyivgen(EVP_CIPHER_CTX *ctx, const char *pass,
+                             int passlen, ASN1_TYPE *param,
+                             const EVP_CIPHER *c, const EVP_MD *md,
+                             int en_de);
+
+struct evp_Encode_Ctx_st {
+    /* number saved in a partial encode/decode */
+    int num;
+    /*
+     * The length is either the output line length (in input bytes) or the
+     * shortest input line length that is ok.  Once decoding begins, the
+     * length is adjusted up each time a longer line is decoded
+     */
+    int length;
+    /* data to encode */
+    unsigned char enc_data[80];
+    /* number read on current line */
+    int line_num;
+    int expect_nl;
+};
+
+typedef struct evp_pbe_st EVP_PBE_CTL;
+DEFINE_STACK_OF(EVP_PBE_CTL)
+
+int is_partially_overlapping(const void *ptr1, const void *ptr2, int len);
diff --git a/crypto/evp/evp_pbe.c b/crypto/evp/evp_pbe.c
new file mode 100644
index 000000000..660d2bfc5
--- /dev/null
+++ b/crypto/evp/evp_pbe.c
@@ -0,0 +1,264 @@
+/*
+ * Copyright 1999-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+#include 
+#include "evp_locl.h"
+
+/* Password based encryption (PBE) functions */
+
+/* Setup a cipher context from a PBE algorithm */
+
+struct evp_pbe_st {
+    int pbe_type;
+    int pbe_nid;
+    int cipher_nid;
+    int md_nid;
+    EVP_PBE_KEYGEN *keygen;
+};
+
+static STACK_OF(EVP_PBE_CTL) *pbe_algs;
+
+static const EVP_PBE_CTL builtin_pbe[] = {
+    {EVP_PBE_TYPE_OUTER, NID_pbeWithMD2AndDES_CBC,
+     NID_des_cbc, NID_md2, PKCS5_PBE_keyivgen},
+    {EVP_PBE_TYPE_OUTER, NID_pbeWithMD5AndDES_CBC,
+     NID_des_cbc, NID_md5, PKCS5_PBE_keyivgen},
+    {EVP_PBE_TYPE_OUTER, NID_pbeWithSHA1AndRC2_CBC,
+     NID_rc2_64_cbc, NID_sha1, PKCS5_PBE_keyivgen},
+
+    {EVP_PBE_TYPE_OUTER, NID_id_pbkdf2, -1, -1, PKCS5_v2_PBKDF2_keyivgen},
+
+#ifndef OPENSSL_NO_PKCS12
+    {EVP_PBE_TYPE_OUTER, NID_pbe_WithSHA1And128BitRC4,
+     NID_rc4, NID_sha1, PKCS12_PBE_keyivgen},
+    {EVP_PBE_TYPE_OUTER, NID_pbe_WithSHA1And40BitRC4,
+     NID_rc4_40, NID_sha1, PKCS12_PBE_keyivgen},
+    {EVP_PBE_TYPE_OUTER, NID_pbe_WithSHA1And3_Key_TripleDES_CBC,
+     NID_des_ede3_cbc, NID_sha1, PKCS12_PBE_keyivgen},
+    {EVP_PBE_TYPE_OUTER, NID_pbe_WithSHA1And2_Key_TripleDES_CBC,
+     NID_des_ede_cbc, NID_sha1, PKCS12_PBE_keyivgen},
+    {EVP_PBE_TYPE_OUTER, NID_pbe_WithSHA1And128BitRC2_CBC,
+     NID_rc2_cbc, NID_sha1, PKCS12_PBE_keyivgen},
+    {EVP_PBE_TYPE_OUTER, NID_pbe_WithSHA1And40BitRC2_CBC,
+     NID_rc2_40_cbc, NID_sha1, PKCS12_PBE_keyivgen},
+#endif
+
+    {EVP_PBE_TYPE_OUTER, NID_pbes2, -1, -1, PKCS5_v2_PBE_keyivgen},
+
+    {EVP_PBE_TYPE_OUTER, NID_pbeWithMD2AndRC2_CBC,
+     NID_rc2_64_cbc, NID_md2, PKCS5_PBE_keyivgen},
+    {EVP_PBE_TYPE_OUTER, NID_pbeWithMD5AndRC2_CBC,
+     NID_rc2_64_cbc, NID_md5, PKCS5_PBE_keyivgen},
+    {EVP_PBE_TYPE_OUTER, NID_pbeWithSHA1AndDES_CBC,
+     NID_des_cbc, NID_sha1, PKCS5_PBE_keyivgen},
+
+    {EVP_PBE_TYPE_PRF, NID_hmacWithSHA1, -1, NID_sha1, 0},
+    {EVP_PBE_TYPE_PRF, NID_hmacWithMD5, -1, NID_md5, 0},
+    {EVP_PBE_TYPE_PRF, NID_hmacWithSHA224, -1, NID_sha224, 0},
+    {EVP_PBE_TYPE_PRF, NID_hmacWithSHA256, -1, NID_sha256, 0},
+    {EVP_PBE_TYPE_PRF, NID_hmacWithSHA384, -1, NID_sha384, 0},
+    {EVP_PBE_TYPE_PRF, NID_hmacWithSHA512, -1, NID_sha512, 0},
+    {EVP_PBE_TYPE_PRF, NID_id_HMACGostR3411_94, -1, NID_id_GostR3411_94, 0},
+    {EVP_PBE_TYPE_PRF, NID_id_tc26_hmac_gost_3411_2012_256, -1,
+     NID_id_GostR3411_2012_256, 0},
+    {EVP_PBE_TYPE_PRF, NID_id_tc26_hmac_gost_3411_2012_512, -1,
+     NID_id_GostR3411_2012_512, 0},
+#ifndef OPENSSL_NO_SM3
+    {EVP_PBE_TYPE_PRF, NID_hmac_sm3, -1, NID_sm3, 0},
+#endif
+    {EVP_PBE_TYPE_KDF, NID_id_pbkdf2, -1, -1, PKCS5_v2_PBKDF2_keyivgen},
+#ifndef OPENSSL_NO_SCRYPT
+    {EVP_PBE_TYPE_KDF, NID_id_scrypt, -1, -1, PKCS5_v2_scrypt_keyivgen}
+#endif
+};
+
+int EVP_PBE_CipherInit(ASN1_OBJECT *pbe_obj, const char *pass, int passlen,
+                       ASN1_TYPE *param, EVP_CIPHER_CTX *ctx, int en_de)
+{
+    const EVP_CIPHER *cipher;
+    const EVP_MD *md;
+    int cipher_nid, md_nid;
+    EVP_PBE_KEYGEN *keygen;
+
+    if (!EVP_PBE_find(EVP_PBE_TYPE_OUTER, OBJ_obj2nid(pbe_obj),
+                      &cipher_nid, &md_nid, &keygen)) {
+        char obj_tmp[80];
+        EVPerr(EVP_F_EVP_PBE_CIPHERINIT, EVP_R_UNKNOWN_PBE_ALGORITHM);
+        if (!pbe_obj)
+            OPENSSL_strlcpy(obj_tmp, "NULL", sizeof obj_tmp);
+        else
+            i2t_ASN1_OBJECT(obj_tmp, sizeof obj_tmp, pbe_obj);
+        ERR_add_error_data(2, "TYPE=", obj_tmp);
+        return 0;
+    }
+
+    if (!pass)
+        passlen = 0;
+    else if (passlen == -1)
+        passlen = strlen(pass);
+
+    if (cipher_nid == -1)
+        cipher = NULL;
+    else {
+        cipher = EVP_get_cipherbynid(cipher_nid);
+        if (!cipher) {
+            EVPerr(EVP_F_EVP_PBE_CIPHERINIT, EVP_R_UNKNOWN_CIPHER);
+            return 0;
+        }
+    }
+
+    if (md_nid == -1)
+        md = NULL;
+    else {
+        md = EVP_get_digestbynid(md_nid);
+        if (!md) {
+            EVPerr(EVP_F_EVP_PBE_CIPHERINIT, EVP_R_UNKNOWN_DIGEST);
+            return 0;
+        }
+    }
+
+    if (!keygen(ctx, pass, passlen, param, cipher, md, en_de)) {
+        EVPerr(EVP_F_EVP_PBE_CIPHERINIT, EVP_R_KEYGEN_FAILURE);
+        return 0;
+    }
+    return 1;
+}
+
+DECLARE_OBJ_BSEARCH_CMP_FN(EVP_PBE_CTL, EVP_PBE_CTL, pbe2);
+
+static int pbe2_cmp(const EVP_PBE_CTL *pbe1, const EVP_PBE_CTL *pbe2)
+{
+    int ret = pbe1->pbe_type - pbe2->pbe_type;
+    if (ret)
+        return ret;
+    else
+        return pbe1->pbe_nid - pbe2->pbe_nid;
+}
+
+IMPLEMENT_OBJ_BSEARCH_CMP_FN(EVP_PBE_CTL, EVP_PBE_CTL, pbe2);
+
+static int pbe_cmp(const EVP_PBE_CTL *const *a, const EVP_PBE_CTL *const *b)
+{
+    int ret = (*a)->pbe_type - (*b)->pbe_type;
+    if (ret)
+        return ret;
+    else
+        return (*a)->pbe_nid - (*b)->pbe_nid;
+}
+
+/* Add a PBE algorithm */
+
+int EVP_PBE_alg_add_type(int pbe_type, int pbe_nid, int cipher_nid,
+                         int md_nid, EVP_PBE_KEYGEN *keygen)
+{
+    EVP_PBE_CTL *pbe_tmp;
+
+    if (pbe_algs == NULL) {
+        pbe_algs = sk_EVP_PBE_CTL_new(pbe_cmp);
+        if (pbe_algs == NULL)
+            goto err;
+    }
+
+    if ((pbe_tmp = OPENSSL_malloc(sizeof(*pbe_tmp))) == NULL)
+        goto err;
+
+    pbe_tmp->pbe_type = pbe_type;
+    pbe_tmp->pbe_nid = pbe_nid;
+    pbe_tmp->cipher_nid = cipher_nid;
+    pbe_tmp->md_nid = md_nid;
+    pbe_tmp->keygen = keygen;
+
+    if (!sk_EVP_PBE_CTL_push(pbe_algs, pbe_tmp)) {
+        OPENSSL_free(pbe_tmp);
+        goto err;
+    }
+    return 1;
+
+ err:
+    EVPerr(EVP_F_EVP_PBE_ALG_ADD_TYPE, ERR_R_MALLOC_FAILURE);
+    return 0;
+}
+
+int EVP_PBE_alg_add(int nid, const EVP_CIPHER *cipher, const EVP_MD *md,
+                    EVP_PBE_KEYGEN *keygen)
+{
+    int cipher_nid, md_nid;
+
+    if (cipher)
+        cipher_nid = EVP_CIPHER_nid(cipher);
+    else
+        cipher_nid = -1;
+    if (md)
+        md_nid = EVP_MD_type(md);
+    else
+        md_nid = -1;
+
+    return EVP_PBE_alg_add_type(EVP_PBE_TYPE_OUTER, nid,
+                                cipher_nid, md_nid, keygen);
+}
+
+int EVP_PBE_find(int type, int pbe_nid,
+                 int *pcnid, int *pmnid, EVP_PBE_KEYGEN **pkeygen)
+{
+    EVP_PBE_CTL *pbetmp = NULL, pbelu;
+    int i;
+    if (pbe_nid == NID_undef)
+        return 0;
+
+    pbelu.pbe_type = type;
+    pbelu.pbe_nid = pbe_nid;
+
+    if (pbe_algs) {
+        i = sk_EVP_PBE_CTL_find(pbe_algs, &pbelu);
+        if (i != -1)
+            pbetmp = sk_EVP_PBE_CTL_value(pbe_algs, i);
+    }
+    if (pbetmp == NULL) {
+        pbetmp = OBJ_bsearch_pbe2(&pbelu, builtin_pbe, OSSL_NELEM(builtin_pbe));
+    }
+    if (pbetmp == NULL)
+        return 0;
+    if (pcnid)
+        *pcnid = pbetmp->cipher_nid;
+    if (pmnid)
+        *pmnid = pbetmp->md_nid;
+    if (pkeygen)
+        *pkeygen = pbetmp->keygen;
+    return 1;
+}
+
+static void free_evp_pbe_ctl(EVP_PBE_CTL *pbe)
+{
+    OPENSSL_free(pbe);
+}
+
+void EVP_PBE_cleanup(void)
+{
+    sk_EVP_PBE_CTL_pop_free(pbe_algs, free_evp_pbe_ctl);
+    pbe_algs = NULL;
+}
+
+int EVP_PBE_get(int *ptype, int *ppbe_nid, size_t num)
+{
+    const EVP_PBE_CTL *tpbe;
+
+    if (num >= OSSL_NELEM(builtin_pbe))
+        return 0;
+
+    tpbe = builtin_pbe + num;
+    if (ptype)
+        *ptype = tpbe->pbe_type;
+    if (ppbe_nid)
+        *ppbe_nid = tpbe->pbe_nid;
+    return 1;
+}
diff --git a/crypto/evp/evp_pkey.c b/crypto/evp/evp_pkey.c
new file mode 100644
index 000000000..81bffa6d9
--- /dev/null
+++ b/crypto/evp/evp_pkey.c
@@ -0,0 +1,150 @@
+/*
+ * Copyright 1999-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+#include "internal/asn1_int.h"
+#include "internal/evp_int.h"
+#include "internal/x509_int.h"
+
+/* Extract a private key from a PKCS8 structure */
+
+EVP_PKEY *EVP_PKCS82PKEY(const PKCS8_PRIV_KEY_INFO *p8)
+{
+    EVP_PKEY *pkey = NULL;
+    const ASN1_OBJECT *algoid;
+    char obj_tmp[80];
+
+    if (!PKCS8_pkey_get0(&algoid, NULL, NULL, NULL, p8))
+        return NULL;
+
+    if ((pkey = EVP_PKEY_new()) == NULL) {
+        EVPerr(EVP_F_EVP_PKCS82PKEY, ERR_R_MALLOC_FAILURE);
+        return NULL;
+    }
+
+    if (!EVP_PKEY_set_type(pkey, OBJ_obj2nid(algoid))) {
+        EVPerr(EVP_F_EVP_PKCS82PKEY, EVP_R_UNSUPPORTED_PRIVATE_KEY_ALGORITHM);
+        i2t_ASN1_OBJECT(obj_tmp, 80, algoid);
+        ERR_add_error_data(2, "TYPE=", obj_tmp);
+        goto error;
+    }
+
+    if (pkey->ameth->priv_decode) {
+        if (!pkey->ameth->priv_decode(pkey, p8)) {
+            EVPerr(EVP_F_EVP_PKCS82PKEY, EVP_R_PRIVATE_KEY_DECODE_ERROR);
+            goto error;
+        }
+    } else {
+        EVPerr(EVP_F_EVP_PKCS82PKEY, EVP_R_METHOD_NOT_SUPPORTED);
+        goto error;
+    }
+
+    return pkey;
+
+ error:
+    EVP_PKEY_free(pkey);
+    return NULL;
+}
+
+/* Turn a private key into a PKCS8 structure */
+
+PKCS8_PRIV_KEY_INFO *EVP_PKEY2PKCS8(EVP_PKEY *pkey)
+{
+    PKCS8_PRIV_KEY_INFO *p8 = PKCS8_PRIV_KEY_INFO_new();
+    if (p8  == NULL) {
+        EVPerr(EVP_F_EVP_PKEY2PKCS8, ERR_R_MALLOC_FAILURE);
+        return NULL;
+    }
+
+    if (pkey->ameth) {
+        if (pkey->ameth->priv_encode) {
+            if (!pkey->ameth->priv_encode(p8, pkey)) {
+                EVPerr(EVP_F_EVP_PKEY2PKCS8, EVP_R_PRIVATE_KEY_ENCODE_ERROR);
+                goto error;
+            }
+        } else {
+            EVPerr(EVP_F_EVP_PKEY2PKCS8, EVP_R_METHOD_NOT_SUPPORTED);
+            goto error;
+        }
+    } else {
+        EVPerr(EVP_F_EVP_PKEY2PKCS8, EVP_R_UNSUPPORTED_PRIVATE_KEY_ALGORITHM);
+        goto error;
+    }
+    RAND_add(p8->pkey->data, p8->pkey->length, 0.0);
+    return p8;
+ error:
+    PKCS8_PRIV_KEY_INFO_free(p8);
+    return NULL;
+}
+
+/* EVP_PKEY attribute functions */
+
+int EVP_PKEY_get_attr_count(const EVP_PKEY *key)
+{
+    return X509at_get_attr_count(key->attributes);
+}
+
+int EVP_PKEY_get_attr_by_NID(const EVP_PKEY *key, int nid, int lastpos)
+{
+    return X509at_get_attr_by_NID(key->attributes, nid, lastpos);
+}
+
+int EVP_PKEY_get_attr_by_OBJ(const EVP_PKEY *key, const ASN1_OBJECT *obj,
+                             int lastpos)
+{
+    return X509at_get_attr_by_OBJ(key->attributes, obj, lastpos);
+}
+
+X509_ATTRIBUTE *EVP_PKEY_get_attr(const EVP_PKEY *key, int loc)
+{
+    return X509at_get_attr(key->attributes, loc);
+}
+
+X509_ATTRIBUTE *EVP_PKEY_delete_attr(EVP_PKEY *key, int loc)
+{
+    return X509at_delete_attr(key->attributes, loc);
+}
+
+int EVP_PKEY_add1_attr(EVP_PKEY *key, X509_ATTRIBUTE *attr)
+{
+    if (X509at_add1_attr(&key->attributes, attr))
+        return 1;
+    return 0;
+}
+
+int EVP_PKEY_add1_attr_by_OBJ(EVP_PKEY *key,
+                              const ASN1_OBJECT *obj, int type,
+                              const unsigned char *bytes, int len)
+{
+    if (X509at_add1_attr_by_OBJ(&key->attributes, obj, type, bytes, len))
+        return 1;
+    return 0;
+}
+
+int EVP_PKEY_add1_attr_by_NID(EVP_PKEY *key,
+                              int nid, int type,
+                              const unsigned char *bytes, int len)
+{
+    if (X509at_add1_attr_by_NID(&key->attributes, nid, type, bytes, len))
+        return 1;
+    return 0;
+}
+
+int EVP_PKEY_add1_attr_by_txt(EVP_PKEY *key,
+                              const char *attrname, int type,
+                              const unsigned char *bytes, int len)
+{
+    if (X509at_add1_attr_by_txt(&key->attributes, attrname, type, bytes, len))
+        return 1;
+    return 0;
+}
diff --git a/crypto/evp/m_md2.c b/crypto/evp/m_md2.c
new file mode 100644
index 000000000..c4e28ae17
--- /dev/null
+++ b/crypto/evp/m_md2.c
@@ -0,0 +1,56 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+
+#ifndef OPENSSL_NO_MD2
+
+# include 
+# include 
+# include 
+# include 
+# include 
+
+#include "internal/evp_int.h"
+
+static int init(EVP_MD_CTX *ctx)
+{
+    return MD2_Init(EVP_MD_CTX_md_data(ctx));
+}
+
+static int update(EVP_MD_CTX *ctx, const void *data, size_t count)
+{
+    return MD2_Update(EVP_MD_CTX_md_data(ctx), data, count);
+}
+
+static int final(EVP_MD_CTX *ctx, unsigned char *md)
+{
+    return MD2_Final(md, EVP_MD_CTX_md_data(ctx));
+}
+
+static const EVP_MD md2_md = {
+    NID_md2,
+    NID_md2WithRSAEncryption,
+    MD2_DIGEST_LENGTH,
+    0,
+    init,
+    update,
+    final,
+    NULL,
+    NULL,
+    MD2_BLOCK,
+    sizeof(EVP_MD *) + sizeof(MD2_CTX),
+};
+
+const EVP_MD *EVP_md2(void)
+{
+    return &md2_md;
+}
+#endif
diff --git a/crypto/evp/m_md4.c b/crypto/evp/m_md4.c
new file mode 100644
index 000000000..932c16b48
--- /dev/null
+++ b/crypto/evp/m_md4.c
@@ -0,0 +1,54 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+
+#ifndef OPENSSL_NO_MD4
+
+# include 
+# include 
+# include 
+# include 
+# include "internal/evp_int.h"
+
+static int init(EVP_MD_CTX *ctx)
+{
+    return MD4_Init(EVP_MD_CTX_md_data(ctx));
+}
+
+static int update(EVP_MD_CTX *ctx, const void *data, size_t count)
+{
+    return MD4_Update(EVP_MD_CTX_md_data(ctx), data, count);
+}
+
+static int final(EVP_MD_CTX *ctx, unsigned char *md)
+{
+    return MD4_Final(md, EVP_MD_CTX_md_data(ctx));
+}
+
+static const EVP_MD md4_md = {
+    NID_md4,
+    NID_md4WithRSAEncryption,
+    MD4_DIGEST_LENGTH,
+    0,
+    init,
+    update,
+    final,
+    NULL,
+    NULL,
+    MD4_CBLOCK,
+    sizeof(EVP_MD *) + sizeof(MD4_CTX),
+};
+
+const EVP_MD *EVP_md4(void)
+{
+    return (&md4_md);
+}
+#endif
diff --git a/crypto/evp/m_md5.c b/crypto/evp/m_md5.c
new file mode 100644
index 000000000..e431cdfd6
--- /dev/null
+++ b/crypto/evp/m_md5.c
@@ -0,0 +1,57 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+
+#ifndef OPENSSL_NO_MD5
+
+# include 
+# include 
+# include 
+# include 
+# ifndef OPENSSL_NO_RSA
+#  include 
+# endif
+# include "internal/evp_int.h"
+
+static int init(EVP_MD_CTX *ctx)
+{
+    return MD5_Init(EVP_MD_CTX_md_data(ctx));
+}
+
+static int update(EVP_MD_CTX *ctx, const void *data, size_t count)
+{
+    return MD5_Update(EVP_MD_CTX_md_data(ctx), data, count);
+}
+
+static int final(EVP_MD_CTX *ctx, unsigned char *md)
+{
+    return MD5_Final(md, EVP_MD_CTX_md_data(ctx));
+}
+
+static const EVP_MD md5_md = {
+    NID_md5,
+    NID_md5WithRSAEncryption,
+    MD5_DIGEST_LENGTH,
+    0,
+    init,
+    update,
+    final,
+    NULL,
+    NULL,
+    MD5_CBLOCK,
+    sizeof(EVP_MD *) + sizeof(MD5_CTX),
+};
+
+const EVP_MD *EVP_md5(void)
+{
+    return (&md5_md);
+}
+#endif
diff --git a/crypto/evp/m_md5_sha1.c b/crypto/evp/m_md5_sha1.c
new file mode 100644
index 000000000..8d791edb0
--- /dev/null
+++ b/crypto/evp/m_md5_sha1.c
@@ -0,0 +1,145 @@
+/*
+ * Copyright 2015-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+
+#if !defined(OPENSSL_NO_MD5) && !defined(OPENSSL_NO_SHA)
+
+# include 
+# include 
+# include 
+# include 
+# include 
+# include "internal/cryptlib.h"
+# include "internal/evp_int.h"
+//# include 
+// try no rsa if md5_sha1 ok?			
+
+struct md5_sha1_ctx {
+    MD5_CTX md5;
+    SHA_CTX sha1;
+};
+
+static int init(EVP_MD_CTX *ctx)
+{
+    struct md5_sha1_ctx *mctx = EVP_MD_CTX_md_data(ctx);
+    if (!MD5_Init(&mctx->md5))
+        return 0;
+    return SHA1_Init(&mctx->sha1);
+}
+
+static int update(EVP_MD_CTX *ctx, const void *data, size_t count)
+{
+    struct md5_sha1_ctx *mctx = EVP_MD_CTX_md_data(ctx);
+    if (!MD5_Update(&mctx->md5, data, count))
+        return 0;
+    return SHA1_Update(&mctx->sha1, data, count);
+}
+
+static int final(EVP_MD_CTX *ctx, unsigned char *md)
+{
+    struct md5_sha1_ctx *mctx = EVP_MD_CTX_md_data(ctx);
+    if (!MD5_Final(md, &mctx->md5))
+        return 0;
+    return SHA1_Final(md + MD5_DIGEST_LENGTH, &mctx->sha1);
+}
+
+static int ctrl(EVP_MD_CTX *ctx, int cmd, int mslen, void *ms)
+{
+    unsigned char padtmp[48];
+    unsigned char md5tmp[MD5_DIGEST_LENGTH];
+    unsigned char sha1tmp[SHA_DIGEST_LENGTH];
+    struct md5_sha1_ctx *mctx;
+
+    if (cmd != EVP_CTRL_SSL3_MASTER_SECRET)
+        return -2;
+
+    if (ctx == NULL)
+        return 0;
+
+    mctx = EVP_MD_CTX_md_data(ctx);
+
+    /* SSLv3 client auth handling: see RFC-6101 5.6.8 */
+    if (mslen != 48)
+        return 0;
+
+    /* At this point hash contains all handshake messages, update
+     * with master secret and pad_1.
+     */
+
+    if (update(ctx, ms, mslen) <= 0)
+        return 0;
+
+    /* Set padtmp to pad_1 value */
+    memset(padtmp, 0x36, sizeof(padtmp));
+
+    if (!MD5_Update(&mctx->md5, padtmp, sizeof(padtmp)))
+        return 0;
+
+    if (!MD5_Final(md5tmp, &mctx->md5))
+        return 0;
+
+    if (!SHA1_Update(&mctx->sha1, padtmp, 40))
+        return 0;
+
+    if (!SHA1_Final(sha1tmp, &mctx->sha1))
+        return 0;
+
+    /* Reinitialise context */
+
+    if (!init(ctx))
+        return 0;
+
+    if (update(ctx, ms, mslen) <= 0)
+        return 0;
+
+    /* Set padtmp to pad_2 value */
+    memset(padtmp, 0x5c, sizeof(padtmp));
+
+    if (!MD5_Update(&mctx->md5, padtmp, sizeof(padtmp)))
+        return 0;
+
+    if (!MD5_Update(&mctx->md5, md5tmp, sizeof(md5tmp)))
+        return 0;
+
+    if (!SHA1_Update(&mctx->sha1, padtmp, 40))
+        return 0;
+
+    if (!SHA1_Update(&mctx->sha1, sha1tmp, sizeof(sha1tmp)))
+        return 0;
+
+    /* Now when ctx is finalised it will return the SSL v3 hash value */
+
+    OPENSSL_cleanse(md5tmp, sizeof(md5tmp));
+    OPENSSL_cleanse(sha1tmp, sizeof(sha1tmp));
+
+    return 1;
+
+}
+
+static const EVP_MD md5_sha1_md = {
+    NID_md5_sha1,
+    NID_md5_sha1,
+    MD5_DIGEST_LENGTH + SHA_DIGEST_LENGTH,
+    0,
+    init,
+    update,
+    final,
+    NULL,
+    NULL,
+    MD5_CBLOCK,
+    sizeof(EVP_MD *) + sizeof(struct md5_sha1_ctx),
+    ctrl
+};
+
+const EVP_MD *EVP_md5_sha1(void)
+{
+    return &md5_sha1_md;
+}
+#endif
diff --git a/crypto/evp/m_mdc2.c b/crypto/evp/m_mdc2.c
new file mode 100644
index 000000000..b7f0fd8c1
--- /dev/null
+++ b/crypto/evp/m_mdc2.c
@@ -0,0 +1,55 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+
+#ifndef OPENSSL_NO_MDC2
+
+# include 
+# include 
+# include 
+# include 
+# include 
+# include "internal/evp_int.h"
+
+static int init(EVP_MD_CTX *ctx)
+{
+    return MDC2_Init(EVP_MD_CTX_md_data(ctx));
+}
+
+static int update(EVP_MD_CTX *ctx, const void *data, size_t count)
+{
+    return MDC2_Update(EVP_MD_CTX_md_data(ctx), data, count);
+}
+
+static int final(EVP_MD_CTX *ctx, unsigned char *md)
+{
+    return MDC2_Final(md, EVP_MD_CTX_md_data(ctx));
+}
+
+static const EVP_MD mdc2_md = {
+    NID_mdc2,
+    NID_mdc2WithRSA,
+    MDC2_DIGEST_LENGTH,
+    0,
+    init,
+    update,
+    final,
+    NULL,
+    NULL,
+    MDC2_BLOCK,
+    sizeof(EVP_MD *) + sizeof(MDC2_CTX),
+};
+
+const EVP_MD *EVP_mdc2(void)
+{
+    return (&mdc2_md);
+}
+#endif
diff --git a/crypto/evp/m_null.c b/crypto/evp/m_null.c
new file mode 100644
index 000000000..6c4daf56b
--- /dev/null
+++ b/crypto/evp/m_null.c
@@ -0,0 +1,49 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+#include 
+#include "internal/evp_int.h"
+
+static int init(EVP_MD_CTX *ctx)
+{
+    return 1;
+}
+
+static int update(EVP_MD_CTX *ctx, const void *data, size_t count)
+{
+    return 1;
+}
+
+static int final(EVP_MD_CTX *ctx, unsigned char *md)
+{
+    return 1;
+}
+
+static const EVP_MD null_md = {
+    NID_undef,
+    NID_undef,
+    0,
+    0,
+    init,
+    update,
+    final,
+    NULL,
+    NULL,
+    0,
+    sizeof(EVP_MD *),
+};
+
+const EVP_MD *EVP_md_null(void)
+{
+    return (&null_md);
+}
diff --git a/crypto/evp/m_ripemd.c b/crypto/evp/m_ripemd.c
new file mode 100644
index 000000000..dcefaaf58
--- /dev/null
+++ b/crypto/evp/m_ripemd.c
@@ -0,0 +1,54 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+
+#ifndef OPENSSL_NO_RMD160
+
+# include 
+# include 
+# include 
+# include 
+# include "internal/evp_int.h"
+
+static int init(EVP_MD_CTX *ctx)
+{
+    return RIPEMD160_Init(EVP_MD_CTX_md_data(ctx));
+}
+
+static int update(EVP_MD_CTX *ctx, const void *data, size_t count)
+{
+    return RIPEMD160_Update(EVP_MD_CTX_md_data(ctx), data, count);
+}
+
+static int final(EVP_MD_CTX *ctx, unsigned char *md)
+{
+    return RIPEMD160_Final(md, EVP_MD_CTX_md_data(ctx));
+}
+
+static const EVP_MD ripemd160_md = {
+    NID_ripemd160,
+    NID_ripemd160WithRSA,
+    RIPEMD160_DIGEST_LENGTH,
+    0,
+    init,
+    update,
+    final,
+    NULL,
+    NULL,
+    RIPEMD160_CBLOCK,
+    sizeof(EVP_MD *) + sizeof(RIPEMD160_CTX),
+};
+
+const EVP_MD *EVP_ripemd160(void)
+{
+    return (&ripemd160_md);
+}
+#endif
diff --git a/crypto/evp/m_sha1.c b/crypto/evp/m_sha1.c
new file mode 100644
index 000000000..e5afac231
--- /dev/null
+++ b/crypto/evp/m_sha1.c
@@ -0,0 +1,242 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+
+#ifndef OPENSSL_NO_SHA
+
+# include 
+# include 
+# include 
+# ifndef OPENSSL_NO_RSA
+#  include 
+# endif
+# include "internal/evp_int.h"
+
+static int init(EVP_MD_CTX *ctx)
+{
+    return SHA1_Init(EVP_MD_CTX_md_data(ctx));
+}
+
+static int update(EVP_MD_CTX *ctx, const void *data, size_t count)
+{
+    return SHA1_Update(EVP_MD_CTX_md_data(ctx), data, count);
+}
+
+static int final(EVP_MD_CTX *ctx, unsigned char *md)
+{
+    return SHA1_Final(md, EVP_MD_CTX_md_data(ctx));
+}
+
+static int ctrl(EVP_MD_CTX *ctx, int cmd, int mslen, void *ms)
+{
+    unsigned char padtmp[40];
+    unsigned char sha1tmp[SHA_DIGEST_LENGTH];
+
+    SHA_CTX *sha1;
+
+    if (cmd != EVP_CTRL_SSL3_MASTER_SECRET)
+        return -2;
+
+    if (ctx == NULL)
+        return 0;
+
+    sha1 = EVP_MD_CTX_md_data(ctx);
+
+    /* SSLv3 client auth handling: see RFC-6101 5.6.8 */
+    if (mslen != 48)
+        return 0;
+
+    /* At this point hash contains all handshake messages, update
+     * with master secret and pad_1.
+     */
+
+    if (SHA1_Update(sha1, ms, mslen) <= 0)
+        return 0;
+
+    /* Set padtmp to pad_1 value */
+    memset(padtmp, 0x36, sizeof(padtmp));
+
+    if (!SHA1_Update(sha1, padtmp, sizeof(padtmp)))
+        return 0;
+
+    if (!SHA1_Final(sha1tmp, sha1))
+        return 0;
+
+    /* Reinitialise context */
+
+    if (!SHA1_Init(sha1))
+        return 0;
+
+    if (SHA1_Update(sha1, ms, mslen) <= 0)
+        return 0;
+
+    /* Set padtmp to pad_2 value */
+    memset(padtmp, 0x5c, sizeof(padtmp));
+
+    if (!SHA1_Update(sha1, padtmp, sizeof(padtmp)))
+        return 0;
+
+    if (!SHA1_Update(sha1, sha1tmp, sizeof(sha1tmp)))
+        return 0;
+
+    /* Now when ctx is finalised it will return the SSL v3 hash value */
+    OPENSSL_cleanse(sha1tmp, sizeof(sha1tmp));
+
+    return 1;
+
+}
+
+static const EVP_MD sha1_md = {
+    NID_sha1,
+    NID_sha1WithRSAEncryption,
+    SHA_DIGEST_LENGTH,
+    EVP_MD_FLAG_DIGALGID_ABSENT,
+    init,
+    update,
+    final,
+    NULL,
+    NULL,
+    SHA_CBLOCK,
+    sizeof(EVP_MD *) + sizeof(SHA_CTX),
+    ctrl
+};
+
+const EVP_MD *EVP_sha1(void)
+{
+    return (&sha1_md);
+}
+
+# ifndef OPENSSL_NO_SHA256
+static int init224(EVP_MD_CTX *ctx)
+{
+    return SHA224_Init(EVP_MD_CTX_md_data(ctx));
+}
+
+static int init256(EVP_MD_CTX *ctx)
+{
+    return SHA256_Init(EVP_MD_CTX_md_data(ctx));
+}
+
+/*
+ * Even though there're separate SHA224_[Update|Final], we call
+ * SHA256 functions even in SHA224 context. This is what happens
+ * there anyway, so we can spare few CPU cycles:-)
+ */
+static int update256(EVP_MD_CTX *ctx, const void *data, size_t count)
+{
+    return SHA256_Update(EVP_MD_CTX_md_data(ctx), data, count);
+}
+
+static int final256(EVP_MD_CTX *ctx, unsigned char *md)
+{
+    return SHA256_Final(md, EVP_MD_CTX_md_data(ctx));
+}
+
+static const EVP_MD sha224_md = {
+    NID_sha224,
+    NID_sha224WithRSAEncryption,
+    SHA224_DIGEST_LENGTH,
+    EVP_MD_FLAG_DIGALGID_ABSENT,
+    init224,
+    update256,
+    final256,
+    NULL,
+    NULL,
+    SHA256_CBLOCK,
+    sizeof(EVP_MD *) + sizeof(SHA256_CTX),
+};
+
+const EVP_MD *EVP_sha224(void)
+{
+    return (&sha224_md);
+}
+
+static const EVP_MD sha256_md = {
+    NID_sha256,
+    NID_sha256WithRSAEncryption,
+    SHA256_DIGEST_LENGTH,
+    EVP_MD_FLAG_DIGALGID_ABSENT,
+    init256,
+    update256,
+    final256,
+    NULL,
+    NULL,
+    SHA256_CBLOCK,
+    sizeof(EVP_MD *) + sizeof(SHA256_CTX),
+};
+
+const EVP_MD *EVP_sha256(void)
+{
+    return (&sha256_md);
+}
+# endif
+
+# ifndef OPENSSL_NO_SHA512
+static int init384(EVP_MD_CTX *ctx)
+{
+    return SHA384_Init(EVP_MD_CTX_md_data(ctx));
+}
+
+static int init512(EVP_MD_CTX *ctx)
+{
+    return SHA512_Init(EVP_MD_CTX_md_data(ctx));
+}
+
+/* See comment in SHA224/256 section */
+static int update512(EVP_MD_CTX *ctx, const void *data, size_t count)
+{
+    return SHA512_Update(EVP_MD_CTX_md_data(ctx), data, count);
+}
+
+static int final512(EVP_MD_CTX *ctx, unsigned char *md)
+{
+    return SHA512_Final(md, EVP_MD_CTX_md_data(ctx));
+}
+
+static const EVP_MD sha384_md = {
+    NID_sha384,
+    NID_sha384WithRSAEncryption,
+    SHA384_DIGEST_LENGTH,
+    EVP_MD_FLAG_DIGALGID_ABSENT,
+    init384,
+    update512,
+    final512,
+    NULL,
+    NULL,
+    SHA512_CBLOCK,
+    sizeof(EVP_MD *) + sizeof(SHA512_CTX),
+};
+
+const EVP_MD *EVP_sha384(void)
+{
+    return (&sha384_md);
+}
+
+static const EVP_MD sha512_md = {
+    NID_sha512,
+    NID_sha512WithRSAEncryption,
+    SHA512_DIGEST_LENGTH,
+    EVP_MD_FLAG_DIGALGID_ABSENT,
+    init512,
+    update512,
+    final512,
+    NULL,
+    NULL,
+    SHA512_CBLOCK,
+    sizeof(EVP_MD *) + sizeof(SHA512_CTX),
+};
+
+const EVP_MD *EVP_sha512(void)
+{
+    return (&sha512_md);
+}
+# endif
+#endif
diff --git a/crypto/evp/m_sigver.c b/crypto/evp/m_sigver.c
new file mode 100644
index 000000000..8ef292f7e
--- /dev/null
+++ b/crypto/evp/m_sigver.c
@@ -0,0 +1,173 @@
+/*
+ * Copyright 2006-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+#include 
+#include "internal/evp_int.h"
+#include "evp_locl.h"
+
+static int do_sigver_init(EVP_MD_CTX *ctx, EVP_PKEY_CTX **pctx,
+                          const EVP_MD *type, ENGINE *e, EVP_PKEY *pkey,
+                          int ver)
+{
+    if (ctx->pctx == NULL)
+        ctx->pctx = EVP_PKEY_CTX_new(pkey, e);
+    if (ctx->pctx == NULL)
+        return 0;
+
+    if (!(ctx->pctx->pmeth->flags & EVP_PKEY_FLAG_SIGCTX_CUSTOM)) {
+
+        if (type == NULL) {
+            int def_nid;
+            if (EVP_PKEY_get_default_digest_nid(pkey, &def_nid) > 0)
+                type = EVP_get_digestbynid(def_nid);
+        }
+
+        if (type == NULL) {
+            EVPerr(EVP_F_DO_SIGVER_INIT, EVP_R_NO_DEFAULT_DIGEST);
+            return 0;
+        }
+    }
+
+    if (ver) {
+        if (ctx->pctx->pmeth->verifyctx_init) {
+            if (ctx->pctx->pmeth->verifyctx_init(ctx->pctx, ctx) <= 0)
+                return 0;
+            ctx->pctx->operation = EVP_PKEY_OP_VERIFYCTX;
+        } else if (EVP_PKEY_verify_init(ctx->pctx) <= 0)
+            return 0;
+    } else {
+        if (ctx->pctx->pmeth->signctx_init) {
+            if (ctx->pctx->pmeth->signctx_init(ctx->pctx, ctx) <= 0)
+                return 0;
+            ctx->pctx->operation = EVP_PKEY_OP_SIGNCTX;
+        } else if (EVP_PKEY_sign_init(ctx->pctx) <= 0)
+            return 0;
+    }
+    if (EVP_PKEY_CTX_set_signature_md(ctx->pctx, type) <= 0)
+        return 0;
+    if (pctx)
+        *pctx = ctx->pctx;
+    if (ctx->pctx->pmeth->flags & EVP_PKEY_FLAG_SIGCTX_CUSTOM)
+        return 1;
+    if (!EVP_DigestInit_ex(ctx, type, e))
+        return 0;
+    return 1;
+}
+
+int EVP_DigestSignInit(EVP_MD_CTX *ctx, EVP_PKEY_CTX **pctx,
+                       const EVP_MD *type, ENGINE *e, EVP_PKEY *pkey)
+{
+    return do_sigver_init(ctx, pctx, type, e, pkey, 0);
+}
+
+int EVP_DigestVerifyInit(EVP_MD_CTX *ctx, EVP_PKEY_CTX **pctx,
+                         const EVP_MD *type, ENGINE *e, EVP_PKEY *pkey)
+{
+    return do_sigver_init(ctx, pctx, type, e, pkey, 1);
+}
+
+int EVP_DigestSignFinal(EVP_MD_CTX *ctx, unsigned char *sigret,
+                        size_t *siglen)
+{
+    int sctx = 0, r = 0;
+    EVP_PKEY_CTX *pctx = ctx->pctx;
+    if (pctx->pmeth->flags & EVP_PKEY_FLAG_SIGCTX_CUSTOM) {
+        if (!sigret)
+            return pctx->pmeth->signctx(pctx, sigret, siglen, ctx);
+        if (ctx->flags & EVP_MD_CTX_FLAG_FINALISE)
+            r = pctx->pmeth->signctx(pctx, sigret, siglen, ctx);
+        else {
+            EVP_PKEY_CTX *dctx = EVP_PKEY_CTX_dup(ctx->pctx);
+            if (!dctx)
+                return 0;
+            r = dctx->pmeth->signctx(dctx, sigret, siglen, ctx);
+            EVP_PKEY_CTX_free(dctx);
+        }
+        return r;
+    }
+    if (pctx->pmeth->signctx)
+        sctx = 1;
+    else
+        sctx = 0;
+    if (sigret) {
+        unsigned char md[EVP_MAX_MD_SIZE];
+        unsigned int mdlen = 0;
+        if (ctx->flags & EVP_MD_CTX_FLAG_FINALISE) {
+            if (sctx)
+                r = ctx->pctx->pmeth->signctx(ctx->pctx, sigret, siglen, ctx);
+            else
+                r = EVP_DigestFinal_ex(ctx, md, &mdlen);
+        } else {
+            EVP_MD_CTX *tmp_ctx = EVP_MD_CTX_new();
+            if (tmp_ctx == NULL || !EVP_MD_CTX_copy_ex(tmp_ctx, ctx))
+                return 0;
+            if (sctx)
+                r = tmp_ctx->pctx->pmeth->signctx(tmp_ctx->pctx,
+                                                  sigret, siglen, tmp_ctx);
+            else
+                r = EVP_DigestFinal_ex(tmp_ctx, md, &mdlen);
+            EVP_MD_CTX_free(tmp_ctx);
+        }
+        if (sctx || !r)
+            return r;
+        if (EVP_PKEY_sign(ctx->pctx, sigret, siglen, md, mdlen) <= 0)
+            return 0;
+    } else {
+        if (sctx) {
+            if (pctx->pmeth->signctx(pctx, sigret, siglen, ctx) <= 0)
+                return 0;
+        } else {
+            int s = EVP_MD_size(ctx->digest);
+            if (s < 0 || EVP_PKEY_sign(pctx, sigret, siglen, NULL, s) <= 0)
+                return 0;
+        }
+    }
+    return 1;
+}
+
+int EVP_DigestVerifyFinal(EVP_MD_CTX *ctx, const unsigned char *sig,
+                          size_t siglen)
+{
+    unsigned char md[EVP_MAX_MD_SIZE];
+    int r = 0;
+    unsigned int mdlen = 0;
+    int vctx = 0;
+
+    if (ctx->pctx->pmeth->verifyctx)
+        vctx = 1;
+    else
+        vctx = 0;
+    if (ctx->flags & EVP_MD_CTX_FLAG_FINALISE) {
+        if (vctx) {
+            r = ctx->pctx->pmeth->verifyctx(ctx->pctx, sig, siglen, ctx);
+        } else {
+            r = EVP_DigestFinal_ex(ctx, md, &mdlen);
+        }
+    } else {
+        EVP_MD_CTX *tmp_ctx = EVP_MD_CTX_new();
+        if (tmp_ctx == NULL || !EVP_MD_CTX_copy_ex(tmp_ctx, ctx)) {
+            return -1;
+        }
+        if (vctx) {
+            r = tmp_ctx->pctx->pmeth->verifyctx(tmp_ctx->pctx,
+                                                sig, siglen, tmp_ctx);
+        } else {
+            r = EVP_DigestFinal_ex(tmp_ctx, md, &mdlen);
+        }
+        EVP_MD_CTX_free(tmp_ctx);
+    }
+    if (vctx || !r) {
+        return r;
+    }
+    return EVP_PKEY_verify(ctx->pctx, sig, siglen, md, mdlen);
+}
diff --git a/crypto/evp/m_sm3.c b/crypto/evp/m_sm3.c
new file mode 100644
index 000000000..d08e945fe
--- /dev/null
+++ b/crypto/evp/m_sm3.c
@@ -0,0 +1,106 @@
+/* ====================================================================
+ * Copyright (c) 2014 - 2016 The GmSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the GmSSL Project.
+ *    (http://gmssl.org/)"
+ *
+ * 4. The name "GmSSL Project" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    guanzhi1980@gmail.com.
+ *
+ * 5. Products derived from this software may not be called "GmSSL"
+ *    nor may "GmSSL" appear in their names without prior written
+ *    permission of the GmSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the GmSSL Project
+ *    (http://gmssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE GmSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE GmSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+
+#include 
+#include "internal/cryptlib.h"
+
+#ifndef OPENSSL_NO_SM3
+# include 
+# include 
+# include 
+# include 
+# include "internal/evp_int.h"
+
+static int init(EVP_MD_CTX *ctx)
+{
+	if (!ctx || !EVP_MD_CTX_md_data(ctx)) {
+		return 0;
+	}
+	sm3_init(EVP_MD_CTX_md_data(ctx));
+	return 1;
+}
+
+static int update(EVP_MD_CTX *ctx, const void *in, size_t inlen)
+{
+	if (!ctx || !EVP_MD_CTX_md_data(ctx) || (!in && inlen != 0)) {
+		return 0;
+	}
+	sm3_update(EVP_MD_CTX_md_data(ctx), in, inlen);
+	return 1;
+}
+
+static int final(EVP_MD_CTX *ctx, unsigned char *md)
+{
+	if (!ctx || !EVP_MD_CTX_md_data(ctx) || !md) {
+		return 0;
+	}
+	sm3_final(EVP_MD_CTX_md_data(ctx), md);
+	return 1;
+}
+
+static const EVP_MD sm3_md = {
+	NID_sm3,		/* type */
+	NID_sm2sign_with_sm3,	/* pkey_type */
+	SM3_DIGEST_LENGTH,	/* md_size */
+	0,			/* flags */
+	init,			/* init */
+	update,			/* update */
+	final,			/* final */
+	NULL,			/* copy */
+	NULL,			/* cleanup */
+	SM3_BLOCK_SIZE,		/* block_size */
+	sizeof(EVP_MD *) + sizeof(sm3_ctx_t), /* ctx_size */
+	NULL,			/* md_ctrl */
+};
+
+const EVP_MD *EVP_sm3(void)
+{
+        return &sm3_md;
+}
+#endif /* OPENSSL_NO_SM3 */
diff --git a/crypto/evp/m_sm9hash2.c b/crypto/evp/m_sm9hash2.c
new file mode 100644
index 000000000..bf3a41e89
--- /dev/null
+++ b/crypto/evp/m_sm9hash2.c
@@ -0,0 +1,165 @@
+/* ====================================================================
+ * Copyright (c) 2014 - 2018 The GmSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the GmSSL Project.
+ *    (http://gmssl.org/)"
+ *
+ * 4. The name "GmSSL Project" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    guanzhi1980@gmail.com.
+ *
+ * 5. Products derived from this software may not be called "GmSSL"
+ *    nor may "GmSSL" appear in their names without prior written
+ *    permission of the GmSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the GmSSL Project
+ *    (http://gmssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE GmSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE GmSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+
+#include 
+#include "internal/cryptlib.h"
+
+#ifndef OPENSSL_NO_SM9
+# include 
+# include 
+# include 
+# include 
+# include "internal/evp_int.h"
+
+# ifndef OPENSSL_NO_SM3
+#  include 
+
+static int sm9hash2_sm3_init(EVP_MD_CTX *ctx)
+{
+	if (!ctx || !EVP_MD_CTX_md_data(ctx)) {
+		return 0;
+	}
+	sm3_init(EVP_MD_CTX_md_data(ctx));
+	return 1;
+}
+
+static int sm9hash2_sm3_update(EVP_MD_CTX *ctx, const void *in, size_t inlen)
+{
+	if (!ctx || !EVP_MD_CTX_md_data(ctx) || (!in && inlen != 0)) {
+		return 0;
+	}
+	sm3_update(EVP_MD_CTX_md_data(ctx), in, inlen);
+	return 1;
+}
+
+static int sm9hash2_sm3_final(EVP_MD_CTX *ctx, unsigned char *md)
+{
+	if (!ctx || !EVP_MD_CTX_md_data(ctx) || !md) {
+		return 0;
+	}
+	sm3_final(EVP_MD_CTX_md_data(ctx), md);
+	return 1;
+}
+
+int sm9hash2_sm3_ctrl(EVP_MD_CTX *ctx, int cmd, int p1, void *p2)
+{
+	return 0;
+}
+
+# define SM9HASH2_SM3_CTX_SIZE (sizeof(EVP_MD *) + sizeof(sm3_ctx_t))
+
+static const EVP_MD sm9hash2_sm3 = {
+	NID_sm9hash2_with_sm3,	/* type */
+	NID_sm9sign_with_sm3,	/* pkey_type */
+	SM3_DIGEST_LENGTH,	/* md_size */
+	0,			/* flags */
+	sm9hash2_sm3_init,	/* init */
+	sm9hash2_sm3_update,	/* update */
+	sm9hash2_sm3_final,	/* final */
+	NULL,			/* copy */
+	NULL,			/* cleanup */
+	SM3_BLOCK_SIZE,		/* block_size */
+	SM9HASH2_SM3_CTX_SIZE,	/* ctx_size */
+	sm9hash2_sm3_ctrl,	/* md_ctrl */
+};
+
+const EVP_MD *EVP_sm9hash2_sm3(void)
+{
+        return &sm9hash2_sm3;
+}
+
+# endif /* OPENSSL_NO_SM3 */
+
+# ifndef OPENSSL_NO_SHA256
+#  include 
+
+static int sm9hash2_sha256_init(EVP_MD_CTX *ctx)
+{
+	return 0;
+}
+
+static int sm9hash2_sha256_update(EVP_MD_CTX *ctx, const void *in, size_t inlen)
+{
+	return 0;
+}
+
+static int sm9hash2_sha256_final(EVP_MD_CTX *ctx, unsigned char *md)
+{
+	return 0;
+}
+
+int sm9hash2_sha256_ctrl(EVP_MD_CTX *ctx, int cmd, int p1, void *p2)
+{
+	return 0;
+}
+
+#define SM9HASH2_SHA256_CTX_SIZE (sizeof(EVP_MD *) + sizeof(SHA256_CTX))
+
+static const EVP_MD sm9hash2_sha256 = {
+	NID_sm9hash2_with_sha256,	/* type */
+	NID_sm9sign_with_sha256,	/* pkey_type */
+	SHA256_DIGEST_LENGTH,		/* md_size */
+	0,				/* flags */
+	sm9hash2_sha256_init,		/* init */
+	sm9hash2_sha256_update,		/* update */
+	sm9hash2_sha256_final,		/* final */
+	NULL,				/* copy */
+	NULL,				/* cleanup */
+	SHA256_CBLOCK,			/* block_size */
+	SM9HASH2_SHA256_CTX_SIZE,	/* ctx_size */
+	sm9hash2_sha256_ctrl,		/* md_ctrl */
+};
+
+const EVP_MD *EVP_sm9hash2_sha256(void)
+{
+        return &sm9hash2_sha256;
+}
+# endif /* OPENSSL_NO_SHA256 */
+
+#endif /* OPENSSL_NO_SM9 */
diff --git a/crypto/evp/m_wp.c b/crypto/evp/m_wp.c
new file mode 100644
index 000000000..94fac226b
--- /dev/null
+++ b/crypto/evp/m_wp.c
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2005-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+
+#ifndef OPENSSL_NO_WHIRLPOOL
+
+# include 
+# include 
+# include 
+# include 
+# include "internal/evp_int.h"
+
+static int init(EVP_MD_CTX *ctx)
+{
+    return WHIRLPOOL_Init(EVP_MD_CTX_md_data(ctx));
+}
+
+static int update(EVP_MD_CTX *ctx, const void *data, size_t count)
+{
+    return WHIRLPOOL_Update(EVP_MD_CTX_md_data(ctx), data, count);
+}
+
+static int final(EVP_MD_CTX *ctx, unsigned char *md)
+{
+    return WHIRLPOOL_Final(md, EVP_MD_CTX_md_data(ctx));
+}
+
+static const EVP_MD whirlpool_md = {
+    NID_whirlpool,
+    0,
+    WHIRLPOOL_DIGEST_LENGTH,
+    0,
+    init,
+    update,
+    final,
+    NULL,
+    NULL,
+    WHIRLPOOL_BBLOCK / 8,
+    sizeof(EVP_MD *) + sizeof(WHIRLPOOL_CTX),
+};
+
+const EVP_MD *EVP_whirlpool(void)
+{
+    return (&whirlpool_md);
+}
+#endif
diff --git a/crypto/evp/names.c b/crypto/evp/names.c
new file mode 100644
index 000000000..a92be1fed
--- /dev/null
+++ b/crypto/evp/names.c
@@ -0,0 +1,178 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+#include 
+#include "internal/evp_int.h"
+
+int EVP_add_cipher(const EVP_CIPHER *c)
+{
+    int r;
+
+    if (c == NULL)
+        return 0;
+
+    r = OBJ_NAME_add(OBJ_nid2sn(c->nid), OBJ_NAME_TYPE_CIPHER_METH,
+                     (const char *)c);
+    if (r == 0)
+        return (0);
+    r = OBJ_NAME_add(OBJ_nid2ln(c->nid), OBJ_NAME_TYPE_CIPHER_METH,
+                     (const char *)c);
+    return (r);
+}
+
+int EVP_add_digest(const EVP_MD *md)
+{
+    int r;
+    const char *name;
+
+    name = OBJ_nid2sn(md->type);
+    r = OBJ_NAME_add(name, OBJ_NAME_TYPE_MD_METH, (const char *)md);
+    if (r == 0)
+        return (0);
+    r = OBJ_NAME_add(OBJ_nid2ln(md->type), OBJ_NAME_TYPE_MD_METH,
+                     (const char *)md);
+    if (r == 0)
+        return (0);
+
+    if (md->pkey_type && md->type != md->pkey_type) {
+        r = OBJ_NAME_add(OBJ_nid2sn(md->pkey_type),
+                         OBJ_NAME_TYPE_MD_METH | OBJ_NAME_ALIAS, name);
+        if (r == 0)
+            return (0);
+        r = OBJ_NAME_add(OBJ_nid2ln(md->pkey_type),
+                         OBJ_NAME_TYPE_MD_METH | OBJ_NAME_ALIAS, name);
+    }
+    return (r);
+}
+
+const EVP_CIPHER *EVP_get_cipherbyname(const char *name)
+{
+    const EVP_CIPHER *cp;
+
+    if (!OPENSSL_init_crypto(OPENSSL_INIT_ADD_ALL_CIPHERS, NULL))
+        return NULL;
+
+    cp = (const EVP_CIPHER *)OBJ_NAME_get(name, OBJ_NAME_TYPE_CIPHER_METH);
+    return (cp);
+}
+
+const EVP_MD *EVP_get_digestbyname(const char *name)
+{
+    const EVP_MD *cp;
+
+    if (!OPENSSL_init_crypto(OPENSSL_INIT_ADD_ALL_DIGESTS, NULL))
+        return NULL;
+
+    cp = (const EVP_MD *)OBJ_NAME_get(name, OBJ_NAME_TYPE_MD_METH);
+    return (cp);
+}
+
+void evp_cleanup_int(void)
+{
+    OBJ_NAME_cleanup(OBJ_NAME_TYPE_CIPHER_METH);
+    OBJ_NAME_cleanup(OBJ_NAME_TYPE_MD_METH);
+    /*
+     * The above calls will only clean out the contents of the name hash
+     * table, but not the hash table itself.  The following line does that
+     * part.  -- Richard Levitte
+     */
+    OBJ_NAME_cleanup(-1);
+
+    EVP_PBE_cleanup();
+    OBJ_sigid_free();
+}
+
+struct doall_cipher {
+    void *arg;
+    void (*fn) (const EVP_CIPHER *ciph,
+                const char *from, const char *to, void *arg);
+};
+
+static void do_all_cipher_fn(const OBJ_NAME *nm, void *arg)
+{
+    struct doall_cipher *dc = arg;
+    if (nm->alias)
+        dc->fn(NULL, nm->name, nm->data, dc->arg);
+    else
+        dc->fn((const EVP_CIPHER *)nm->data, nm->name, NULL, dc->arg);
+}
+
+void EVP_CIPHER_do_all(void (*fn) (const EVP_CIPHER *ciph,
+                                   const char *from, const char *to, void *x),
+                       void *arg)
+{
+    struct doall_cipher dc;
+
+    /* Ignore errors */
+    OPENSSL_init_crypto(OPENSSL_INIT_ADD_ALL_CIPHERS, NULL);
+
+    dc.fn = fn;
+    dc.arg = arg;
+    OBJ_NAME_do_all(OBJ_NAME_TYPE_CIPHER_METH, do_all_cipher_fn, &dc);
+}
+
+void EVP_CIPHER_do_all_sorted(void (*fn) (const EVP_CIPHER *ciph,
+                                          const char *from, const char *to,
+                                          void *x), void *arg)
+{
+    struct doall_cipher dc;
+
+    /* Ignore errors */
+    OPENSSL_init_crypto(OPENSSL_INIT_ADD_ALL_CIPHERS, NULL);
+
+    dc.fn = fn;
+    dc.arg = arg;
+    OBJ_NAME_do_all_sorted(OBJ_NAME_TYPE_CIPHER_METH, do_all_cipher_fn, &dc);
+}
+
+struct doall_md {
+    void *arg;
+    void (*fn) (const EVP_MD *ciph,
+                const char *from, const char *to, void *arg);
+};
+
+static void do_all_md_fn(const OBJ_NAME *nm, void *arg)
+{
+    struct doall_md *dc = arg;
+    if (nm->alias)
+        dc->fn(NULL, nm->name, nm->data, dc->arg);
+    else
+        dc->fn((const EVP_MD *)nm->data, nm->name, NULL, dc->arg);
+}
+
+void EVP_MD_do_all(void (*fn) (const EVP_MD *md,
+                               const char *from, const char *to, void *x),
+                   void *arg)
+{
+    struct doall_md dc;
+
+    /* Ignore errors */
+    OPENSSL_init_crypto(OPENSSL_INIT_ADD_ALL_DIGESTS, NULL);
+
+    dc.fn = fn;
+    dc.arg = arg;
+    OBJ_NAME_do_all(OBJ_NAME_TYPE_MD_METH, do_all_md_fn, &dc);
+}
+
+void EVP_MD_do_all_sorted(void (*fn) (const EVP_MD *md,
+                                      const char *from, const char *to,
+                                      void *x), void *arg)
+{
+    struct doall_md dc;
+
+    OPENSSL_init_crypto(OPENSSL_INIT_ADD_ALL_DIGESTS, NULL);
+
+    dc.fn = fn;
+    dc.arg = arg;
+    OBJ_NAME_do_all_sorted(OBJ_NAME_TYPE_MD_METH, do_all_md_fn, &dc);
+}
diff --git a/crypto/evp/names2.c b/crypto/evp/names2.c
new file mode 100644
index 000000000..d0a3fbfdf
--- /dev/null
+++ b/crypto/evp/names2.c
@@ -0,0 +1,37 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+#include 
+#include "internal/evp_int.h"
+
+/*
+ * use MD5 as default:
+ *	X509_REQ_to_X509		x509_r2x.c
+ *	X509_issuer_and_serial_hash	x509_cmp.c
+ *	X509_NAME_hash_old		x509_cmp.c
+ *	PEM_ASN1_write_bio		pem_lib.c
+ */
+const EVP_MD *EVP_get_default_digest(void)
+{
+#if !defined(OPENSSL_NO_MD5)
+	return EVP_md5();
+#elif !defined(OPENSSL_NO_SHA)
+	return EVP_sha1();
+#elif !defined(OPENSSL_NO_SM3)
+	return EVP_sm3();
+#elif !defined(OPENSSL_NO_RIPEMD)
+	return EVP_rmd160();
+#else
+	return NULL;
+#endif
+}
diff --git a/crypto/evp/p5_crpt.c b/crypto/evp/p5_crpt.c
new file mode 100644
index 000000000..7e55d0bfb
--- /dev/null
+++ b/crypto/evp/p5_crpt.c
@@ -0,0 +1,103 @@
+/*
+ * Copyright 1999-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+
+/*
+ * Doesn't do anything now: Builtin PBE algorithms in static table.
+ */
+
+void PKCS5_PBE_add(void)
+{
+}
+
+int PKCS5_PBE_keyivgen(EVP_CIPHER_CTX *cctx, const char *pass, int passlen,
+                       ASN1_TYPE *param, const EVP_CIPHER *cipher,
+                       const EVP_MD *md, int en_de)
+{
+    EVP_MD_CTX *ctx;
+    unsigned char md_tmp[EVP_MAX_MD_SIZE];
+    unsigned char key[EVP_MAX_KEY_LENGTH], iv[EVP_MAX_IV_LENGTH];
+    int i;
+    PBEPARAM *pbe;
+    int saltlen, iter;
+    unsigned char *salt;
+    int mdsize;
+    int rv = 0;
+
+    /* Extract useful info from parameter */
+    if (param == NULL || param->type != V_ASN1_SEQUENCE ||
+        param->value.sequence == NULL) {
+        EVPerr(EVP_F_PKCS5_PBE_KEYIVGEN, EVP_R_DECODE_ERROR);
+        return 0;
+    }
+
+    pbe = ASN1_TYPE_unpack_sequence(ASN1_ITEM_rptr(PBEPARAM), param);
+    if (pbe == NULL) {
+        EVPerr(EVP_F_PKCS5_PBE_KEYIVGEN, EVP_R_DECODE_ERROR);
+        return 0;
+    }
+
+    if (!pbe->iter)
+        iter = 1;
+    else
+        iter = ASN1_INTEGER_get(pbe->iter);
+    salt = pbe->salt->data;
+    saltlen = pbe->salt->length;
+
+    if (!pass)
+        passlen = 0;
+    else if (passlen == -1)
+        passlen = strlen(pass);
+
+    ctx = EVP_MD_CTX_new();
+    if (ctx == NULL) {
+        EVPerr(EVP_F_PKCS5_PBE_KEYIVGEN, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+
+    if (!EVP_DigestInit_ex(ctx, md, NULL))
+        goto err;
+    if (!EVP_DigestUpdate(ctx, pass, passlen))
+        goto err;
+    if (!EVP_DigestUpdate(ctx, salt, saltlen))
+        goto err;
+    PBEPARAM_free(pbe);
+    if (!EVP_DigestFinal_ex(ctx, md_tmp, NULL))
+        goto err;
+    mdsize = EVP_MD_size(md);
+    if (mdsize < 0)
+        return 0;
+    for (i = 1; i < iter; i++) {
+        if (!EVP_DigestInit_ex(ctx, md, NULL))
+            goto err;
+        if (!EVP_DigestUpdate(ctx, md_tmp, mdsize))
+            goto err;
+        if (!EVP_DigestFinal_ex(ctx, md_tmp, NULL))
+            goto err;
+    }
+    OPENSSL_assert(EVP_CIPHER_key_length(cipher) <= (int)sizeof(md_tmp));
+    memcpy(key, md_tmp, EVP_CIPHER_key_length(cipher));
+    OPENSSL_assert(EVP_CIPHER_iv_length(cipher) <= 16);
+    memcpy(iv, md_tmp + (16 - EVP_CIPHER_iv_length(cipher)),
+           EVP_CIPHER_iv_length(cipher));
+    if (!EVP_CipherInit_ex(cctx, cipher, NULL, key, iv, en_de))
+        goto err;
+    OPENSSL_cleanse(md_tmp, EVP_MAX_MD_SIZE);
+    OPENSSL_cleanse(key, EVP_MAX_KEY_LENGTH);
+    OPENSSL_cleanse(iv, EVP_MAX_IV_LENGTH);
+    rv = 1;
+ err:
+    EVP_MD_CTX_free(ctx);
+    return rv;
+}
diff --git a/crypto/evp/p5_crpt2.c b/crypto/evp/p5_crpt2.c
new file mode 100644
index 000000000..ac74002a6
--- /dev/null
+++ b/crypto/evp/p5_crpt2.c
@@ -0,0 +1,283 @@
+/*
+ * Copyright 1999-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include "internal/cryptlib.h"
+# include 
+# include 
+# include 
+# include "evp_locl.h"
+
+/* set this to print out info about the keygen algorithm */
+/* #define OPENSSL_DEBUG_PKCS5V2 */
+
+# ifdef OPENSSL_DEBUG_PKCS5V2
+static void h__dump(const unsigned char *p, int len);
+# endif
+
+/*
+ * This is an implementation of PKCS#5 v2.0 password based encryption key
+ * derivation function PBKDF2. SHA1 version verified against test vectors
+ * posted by Peter Gutmann  to the PKCS-TNG
+ *  mailing list.
+ */
+
+int PKCS5_PBKDF2_HMAC(const char *pass, int passlen,
+                      const unsigned char *salt, int saltlen, int iter,
+                      const EVP_MD *digest, int keylen, unsigned char *out)
+{
+    unsigned char digtmp[EVP_MAX_MD_SIZE], *p, itmp[4];
+    int cplen, j, k, tkeylen, mdlen;
+    unsigned long i = 1;
+    HMAC_CTX *hctx_tpl = NULL, *hctx = NULL;
+
+    mdlen = EVP_MD_size(digest);
+    if (mdlen < 0)
+        return 0;
+
+    hctx_tpl = HMAC_CTX_new();
+    if (hctx_tpl == NULL)
+        return 0;
+    p = out;
+    tkeylen = keylen;
+    if (!pass)
+        passlen = 0;
+    else if (passlen == -1)
+        passlen = strlen(pass);
+    if (!HMAC_Init_ex(hctx_tpl, pass, passlen, digest, NULL)) {
+        HMAC_CTX_free(hctx_tpl);
+        return 0;
+    }
+    hctx = HMAC_CTX_new();
+    if (hctx == NULL) {
+        HMAC_CTX_free(hctx_tpl);
+        return 0;
+    }
+    while (tkeylen) {
+        if (tkeylen > mdlen)
+            cplen = mdlen;
+        else
+            cplen = tkeylen;
+        /*
+         * We are unlikely to ever use more than 256 blocks (5120 bits!) but
+         * just in case...
+         */
+        itmp[0] = (unsigned char)((i >> 24) & 0xff);
+        itmp[1] = (unsigned char)((i >> 16) & 0xff);
+        itmp[2] = (unsigned char)((i >> 8) & 0xff);
+        itmp[3] = (unsigned char)(i & 0xff);
+        if (!HMAC_CTX_copy(hctx, hctx_tpl)) {
+            HMAC_CTX_free(hctx);
+            HMAC_CTX_free(hctx_tpl);
+            return 0;
+        }
+        if (!HMAC_Update(hctx, salt, saltlen)
+            || !HMAC_Update(hctx, itmp, 4)
+            || !HMAC_Final(hctx, digtmp, NULL)) {
+            HMAC_CTX_free(hctx);
+            HMAC_CTX_free(hctx_tpl);
+            return 0;
+        }
+        HMAC_CTX_reset(hctx);
+        memcpy(p, digtmp, cplen);
+        for (j = 1; j < iter; j++) {
+            if (!HMAC_CTX_copy(hctx, hctx_tpl)) {
+                HMAC_CTX_free(hctx);
+                HMAC_CTX_free(hctx_tpl);
+                return 0;
+            }
+            if (!HMAC_Update(hctx, digtmp, mdlen)
+                || !HMAC_Final(hctx, digtmp, NULL)) {
+                HMAC_CTX_free(hctx);
+                HMAC_CTX_free(hctx_tpl);
+                return 0;
+            }
+            HMAC_CTX_reset(hctx);
+            for (k = 0; k < cplen; k++)
+                p[k] ^= digtmp[k];
+        }
+        tkeylen -= cplen;
+        i++;
+        p += cplen;
+    }
+    HMAC_CTX_free(hctx);
+    HMAC_CTX_free(hctx_tpl);
+# ifdef OPENSSL_DEBUG_PKCS5V2
+    fprintf(stderr, "Password:\n");
+    h__dump(pass, passlen);
+    fprintf(stderr, "Salt:\n");
+    h__dump(salt, saltlen);
+    fprintf(stderr, "Iteration count %d\n", iter);
+    fprintf(stderr, "Key:\n");
+    h__dump(out, keylen);
+# endif
+    return 1;
+}
+
+# ifndef OPENSSL_NO_SHA
+int PKCS5_PBKDF2_HMAC_SHA1(const char *pass, int passlen,
+                           const unsigned char *salt, int saltlen, int iter,
+                           int keylen, unsigned char *out)
+{
+    return PKCS5_PBKDF2_HMAC(pass, passlen, salt, saltlen, iter, EVP_sha1(),
+                             keylen, out);
+}
+# endif
+
+# ifdef DO_TEST
+main()
+{
+    unsigned char out[4];
+    unsigned char salt[] = { 0x12, 0x34, 0x56, 0x78 };
+#  ifndef OPENSSL_NO_SHA
+    PKCS5_PBKDF2_HMAC_SHA1("password", -1, salt, 4, 5, 4, out);
+    fprintf(stderr, "Out %02X %02X %02X %02X\n",
+            out[0], out[1], out[2], out[3]);
+#  else
+    fprintf(stderr, "PKCS5_PBKDF2_HMAC_SHA1() disabled\n");
+#  endif
+}
+
+# endif
+
+/*
+ * Now the key derivation function itself. This is a bit evil because it has
+ * to check the ASN1 parameters are valid: and there are quite a few of
+ * them...
+ */
+
+int PKCS5_v2_PBE_keyivgen(EVP_CIPHER_CTX *ctx, const char *pass, int passlen,
+                          ASN1_TYPE *param, const EVP_CIPHER *c,
+                          const EVP_MD *md, int en_de)
+{
+    PBE2PARAM *pbe2 = NULL;
+    const EVP_CIPHER *cipher;
+    EVP_PBE_KEYGEN *kdf;
+
+    int rv = 0;
+
+    pbe2 = ASN1_TYPE_unpack_sequence(ASN1_ITEM_rptr(PBE2PARAM), param);
+    if (pbe2 == NULL) {
+        EVPerr(EVP_F_PKCS5_V2_PBE_KEYIVGEN, EVP_R_DECODE_ERROR);
+        goto err;
+    }
+
+    /* See if we recognise the key derivation function */
+    if (!EVP_PBE_find(EVP_PBE_TYPE_KDF, OBJ_obj2nid(pbe2->keyfunc->algorithm),
+                        NULL, NULL, &kdf)) {
+        EVPerr(EVP_F_PKCS5_V2_PBE_KEYIVGEN,
+               EVP_R_UNSUPPORTED_KEY_DERIVATION_FUNCTION);
+        goto err;
+    }
+
+    /*
+     * lets see if we recognise the encryption algorithm.
+     */
+
+    cipher = EVP_get_cipherbyobj(pbe2->encryption->algorithm);
+
+    if (!cipher) {
+        EVPerr(EVP_F_PKCS5_V2_PBE_KEYIVGEN, EVP_R_UNSUPPORTED_CIPHER);
+        goto err;
+    }
+
+    /* Fixup cipher based on AlgorithmIdentifier */
+    if (!EVP_CipherInit_ex(ctx, cipher, NULL, NULL, NULL, en_de))
+        goto err;
+    if (EVP_CIPHER_asn1_to_param(ctx, pbe2->encryption->parameter) < 0) {
+        EVPerr(EVP_F_PKCS5_V2_PBE_KEYIVGEN, EVP_R_CIPHER_PARAMETER_ERROR);
+        goto err;
+    }
+    rv = kdf(ctx, pass, passlen, pbe2->keyfunc->parameter, NULL, NULL, en_de);
+ err:
+    PBE2PARAM_free(pbe2);
+    return rv;
+}
+
+int PKCS5_v2_PBKDF2_keyivgen(EVP_CIPHER_CTX *ctx, const char *pass,
+                             int passlen, ASN1_TYPE *param,
+                             const EVP_CIPHER *c, const EVP_MD *md, int en_de)
+{
+    unsigned char *salt, key[EVP_MAX_KEY_LENGTH];
+    int saltlen, iter;
+    int rv = 0;
+    unsigned int keylen = 0;
+    int prf_nid, hmac_md_nid;
+    PBKDF2PARAM *kdf = NULL;
+    const EVP_MD *prfmd;
+
+    if (EVP_CIPHER_CTX_cipher(ctx) == NULL) {
+        EVPerr(EVP_F_PKCS5_V2_PBKDF2_KEYIVGEN, EVP_R_NO_CIPHER_SET);
+        goto err;
+    }
+    keylen = EVP_CIPHER_CTX_key_length(ctx);
+    OPENSSL_assert(keylen <= sizeof key);
+
+    /* Decode parameter */
+
+    kdf = ASN1_TYPE_unpack_sequence(ASN1_ITEM_rptr(PBKDF2PARAM), param);
+
+    if (kdf == NULL) {
+        EVPerr(EVP_F_PKCS5_V2_PBKDF2_KEYIVGEN, EVP_R_DECODE_ERROR);
+        goto err;
+    }
+
+    keylen = EVP_CIPHER_CTX_key_length(ctx);
+
+    /* Now check the parameters of the kdf */
+
+    if (kdf->keylength && (ASN1_INTEGER_get(kdf->keylength) != (int)keylen)) {
+        EVPerr(EVP_F_PKCS5_V2_PBKDF2_KEYIVGEN, EVP_R_UNSUPPORTED_KEYLENGTH);
+        goto err;
+    }
+
+    if (kdf->prf)
+        prf_nid = OBJ_obj2nid(kdf->prf->algorithm);
+    else
+        prf_nid = NID_hmacWithSHA1;
+
+    if (!EVP_PBE_find(EVP_PBE_TYPE_PRF, prf_nid, NULL, &hmac_md_nid, 0)) {
+        EVPerr(EVP_F_PKCS5_V2_PBKDF2_KEYIVGEN, EVP_R_UNSUPPORTED_PRF);
+        goto err;
+    }
+
+    prfmd = EVP_get_digestbynid(hmac_md_nid);
+    if (prfmd == NULL) {
+        EVPerr(EVP_F_PKCS5_V2_PBKDF2_KEYIVGEN, EVP_R_UNSUPPORTED_PRF);
+        goto err;
+    }
+
+    if (kdf->salt->type != V_ASN1_OCTET_STRING) {
+        EVPerr(EVP_F_PKCS5_V2_PBKDF2_KEYIVGEN, EVP_R_UNSUPPORTED_SALT_TYPE);
+        goto err;
+    }
+
+    /* it seems that its all OK */
+    salt = kdf->salt->value.octet_string->data;
+    saltlen = kdf->salt->value.octet_string->length;
+    iter = ASN1_INTEGER_get(kdf->iter);
+    if (!PKCS5_PBKDF2_HMAC(pass, passlen, salt, saltlen, iter, prfmd,
+                           keylen, key))
+        goto err;
+    rv = EVP_CipherInit_ex(ctx, NULL, NULL, key, NULL, en_de);
+ err:
+    OPENSSL_cleanse(key, keylen);
+    PBKDF2PARAM_free(kdf);
+    return rv;
+}
+
+# ifdef OPENSSL_DEBUG_PKCS5V2
+static void h__dump(const unsigned char *p, int len)
+{
+    for (; len--; p++)
+        fprintf(stderr, "%02X ", *p);
+    fprintf(stderr, "\n");
+}
+# endif
diff --git a/crypto/evp/p_dec.c b/crypto/evp/p_dec.c
new file mode 100644
index 000000000..30d756d78
--- /dev/null
+++ b/crypto/evp/p_dec.c
@@ -0,0 +1,116 @@
+/* ====================================================================
+ * Copyright (c) 2014 - 2017 The GmSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the GmSSL Project.
+ *    (http://gmssl.org/)"
+ *
+ * 4. The name "GmSSL Project" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    guanzhi1980@gmail.com.
+ *
+ * 5. Products derived from this software may not be called "GmSSL"
+ *    nor may "GmSSL" appear in their names without prior written
+ *    permission of the GmSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the GmSSL Project
+ *    (http://gmssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE GmSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE GmSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#ifndef OPENSSL_NO_RSA
+# include 
+#endif
+#include 
+#include 
+#include 
+#ifndef OPENSSL_NO_SM2
+# include 
+#endif
+
+int EVP_PKEY_decrypt_old(unsigned char *key, const unsigned char *ek, int ekl,
+                         EVP_PKEY *priv)
+{
+	int ret = 0;
+	EVP_PKEY_CTX *ctx = NULL;
+	size_t siz;
+
+#ifndef OPENSSL_NO_RSA
+	if (EVP_PKEY_id(priv) == EVP_PKEY_RSA) {
+		if ((ret = RSA_private_decrypt(ekl, ek, key, EVP_PKEY_get0_RSA(priv), RSA_PKCS1_PADDING)) < 0) {
+			EVPerr(EVP_F_EVP_PKEY_DECRYPT_OLD, ERR_R_RSA_LIB);
+			return 0;
+		}
+	}
+#endif
+
+#ifndef OPENSSL_NO_SM2
+	siz = ekl;
+	if (!(ctx = EVP_PKEY_CTX_new(priv, NULL))
+		|| !EVP_PKEY_decrypt_init(ctx)) {
+		EVPerr(EVP_F_EVP_PKEY_DECRYPT_OLD, ERR_R_EVP_LIB);
+		goto end;
+	}
+
+	if (EVP_PKEY_id(priv) == EVP_PKEY_EC && EC_GROUP_get_curve_name(
+		EC_KEY_get0_group(EVP_PKEY_get0_EC_KEY(priv))) == NID_sm2p256v1) {
+#  ifdef SM2_DEBUG
+		fprintf(stderr, "[SM2_DEBUG] %s->EVP_PKEY_CTX_set_ec_scheme\n", __FUNCTION__);
+#  endif
+		if (EVP_PKEY_CTX_set_ec_scheme(ctx, NID_sm_scheme) <= 0
+			|| EVP_PKEY_CTX_set_ec_encrypt_param(ctx, NID_sm3) <= 0) {
+			goto end;
+		}
+	}
+
+	if (!EVP_PKEY_decrypt(ctx, key, &siz, ek, ekl)) {
+		EVPerr(EVP_F_EVP_PKEY_DECRYPT_OLD, ERR_R_EVP_LIB);
+		goto end;
+	}
+
+	ret = (int)siz;
+#endif
+
+end:
+	EVP_PKEY_CTX_free(ctx);
+	return (ret);
+}
diff --git a/crypto/evp/p_enc.c b/crypto/evp/p_enc.c
new file mode 100644
index 000000000..8fbe542ad
--- /dev/null
+++ b/crypto/evp/p_enc.c
@@ -0,0 +1,120 @@
+ /* ====================================================================
+ * Copyright (c) 2014 - 2016 The GmSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the GmSSL Project.
+ *    (http://gmssl.org/)"
+ *
+ * 4. The name "GmSSL Project" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    guanzhi1980@gmail.com.
+ *
+ * 5. Products derived from this software may not be called "GmSSL"
+ *    nor may "GmSSL" appear in their names without prior written
+ *    permission of the GmSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the GmSSL Project
+ *    (http://gmssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE GmSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE GmSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#ifndef OPENSSL_NO_RSA
+# include 
+#endif
+#include 
+#include 
+#include 
+#ifndef OPENSSL_NO_SM2
+# include 
+#endif
+
+int EVP_PKEY_encrypt_old(unsigned char *out, const unsigned char *in,
+	int inlen, EVP_PKEY *pkey)
+{
+	int ret = 0;
+	EVP_PKEY_CTX *ctx = NULL;
+	size_t size;
+
+# ifndef OPENSSL_NO_RSA
+	if (EVP_PKEY_id(pkey) == EVP_PKEY_RSA) {
+		if ((ret = RSA_public_encrypt(inlen, in, out,
+			EVP_PKEY_get0_RSA(pkey), RSA_PKCS1_PADDING)) < 0) {
+			EVPerr(EVP_F_EVP_PKEY_ENCRYPT_OLD,
+				EVP_R_RSA_PUBLIC_ENCRYPT_FAILED);
+			return 0;
+		}
+	}
+# endif
+
+# ifndef OPENSSL_NO_SM2
+	/* hope the caller has prepared enough buffer for `out`
+	 * because this function has no idea of the out length */
+	if (!(ctx = EVP_PKEY_CTX_new(pkey, NULL))
+		|| !EVP_PKEY_encrypt_init(ctx)) {
+		EVPerr(EVP_F_EVP_PKEY_ENCRYPT_OLD, ERR_R_EVP_LIB);
+		goto end;
+	}
+
+	if (EVP_PKEY_id(pkey) == EVP_PKEY_EC && EC_GROUP_get_curve_name(
+		EC_KEY_get0_group(EVP_PKEY_get0_EC_KEY(pkey))) == NID_sm2p256v1) {
+#  ifdef SM2_DEBUG
+		fprintf(stderr, "[SM2_DEBUG] %s->EVP_PKEY_CTX_set_ec_scheme\n", __FUNCTION__);
+#  endif
+		if (EVP_PKEY_CTX_set_ec_scheme(ctx, NID_sm_scheme) <= 0
+			|| EVP_PKEY_CTX_set_ec_encrypt_param(ctx, NID_sm3) <= 0) {
+			goto end;
+		}
+	}
+
+	if (!EVP_PKEY_encrypt(ctx, NULL, &size, in, inlen)
+		|| !EVP_PKEY_encrypt(ctx, out, &size, in, inlen)) {
+		EVPerr(EVP_F_EVP_PKEY_ENCRYPT_OLD, ERR_R_EVP_LIB);
+		goto end;
+	}
+
+	ret = (int)size;
+#endif
+
+end:
+	EVP_PKEY_CTX_free(ctx);
+	return ret;
+}
diff --git a/crypto/evp/p_lib.c b/crypto/evp/p_lib.c
new file mode 100644
index 000000000..533e1fa41
--- /dev/null
+++ b/crypto/evp/p_lib.c
@@ -0,0 +1,577 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+#include 
+#include 
+#include 
+#ifndef OPENSSL_NO_RSA
+# include 
+#endif
+#ifndef OPENSSL_NO_DSA
+# include 
+#endif
+#ifndef OPENSSL_NO_DH
+# include 
+#endif
+#ifndef OPENSSL_NO_PAILLIER
+# include 
+#endif
+#ifndef OPENSSL_NO_ENGINE
+# include 
+#endif
+
+#include "internal/asn1_int.h"
+#include "internal/evp_int.h"
+
+static void EVP_PKEY_free_it(EVP_PKEY *x);
+
+int EVP_PKEY_bits(const EVP_PKEY *pkey)
+{
+    if (pkey && pkey->ameth && pkey->ameth->pkey_bits)
+        return pkey->ameth->pkey_bits(pkey);
+    return 0;
+}
+
+int EVP_PKEY_security_bits(const EVP_PKEY *pkey)
+{
+    if (pkey == NULL)
+        return 0;
+    if (!pkey->ameth || !pkey->ameth->pkey_security_bits)
+        return -2;
+    return pkey->ameth->pkey_security_bits(pkey);
+}
+
+int EVP_PKEY_size(EVP_PKEY *pkey)
+{
+    if (pkey && pkey->ameth && pkey->ameth->pkey_size)
+        return pkey->ameth->pkey_size(pkey);
+    return 0;
+}
+
+int EVP_PKEY_save_parameters(EVP_PKEY *pkey, int mode)
+{
+#ifndef OPENSSL_NO_DSA
+    if (pkey->type == EVP_PKEY_DSA) {
+        int ret = pkey->save_parameters;
+
+        if (mode >= 0)
+            pkey->save_parameters = mode;
+        return (ret);
+    }
+#endif
+#ifndef OPENSSL_NO_EC
+    if (pkey->type == EVP_PKEY_EC) {
+        int ret = pkey->save_parameters;
+
+        if (mode >= 0)
+            pkey->save_parameters = mode;
+        return (ret);
+    }
+#endif
+    return (0);
+}
+
+int EVP_PKEY_copy_parameters(EVP_PKEY *to, const EVP_PKEY *from)
+{
+    if (to->type == EVP_PKEY_NONE) {
+        if (EVP_PKEY_set_type(to, from->type) == 0)
+            return 0;
+    } else if (to->type != from->type) {
+        EVPerr(EVP_F_EVP_PKEY_COPY_PARAMETERS, EVP_R_DIFFERENT_KEY_TYPES);
+        goto err;
+    }
+
+    if (EVP_PKEY_missing_parameters(from)) {
+        EVPerr(EVP_F_EVP_PKEY_COPY_PARAMETERS, EVP_R_MISSING_PARAMETERS);
+        goto err;
+    }
+
+    if (!EVP_PKEY_missing_parameters(to)) {
+        if (EVP_PKEY_cmp_parameters(to, from) == 1)
+            return 1;
+        EVPerr(EVP_F_EVP_PKEY_COPY_PARAMETERS, EVP_R_DIFFERENT_PARAMETERS);
+        return 0;
+    }
+
+    if (from->ameth && from->ameth->param_copy)
+        return from->ameth->param_copy(to, from);
+ err:
+    return 0;
+}
+
+int EVP_PKEY_missing_parameters(const EVP_PKEY *pkey)
+{
+    if (pkey->ameth && pkey->ameth->param_missing)
+        return pkey->ameth->param_missing(pkey);
+    return 0;
+}
+
+int EVP_PKEY_cmp_parameters(const EVP_PKEY *a, const EVP_PKEY *b)
+{
+    if (a->type != b->type)
+        return -1;
+    if (a->ameth && a->ameth->param_cmp)
+        return a->ameth->param_cmp(a, b);
+    return -2;
+}
+
+int EVP_PKEY_cmp(const EVP_PKEY *a, const EVP_PKEY *b)
+{
+    if (a->type != b->type)
+        return -1;
+
+    if (a->ameth) {
+        int ret;
+        /* Compare parameters if the algorithm has them */
+        if (a->ameth->param_cmp) {
+            ret = a->ameth->param_cmp(a, b);
+            if (ret <= 0)
+                return ret;
+        }
+
+        if (a->ameth->pub_cmp)
+            return a->ameth->pub_cmp(a, b);
+    }
+
+    return -2;
+}
+
+EVP_PKEY *EVP_PKEY_new(void)
+{
+    EVP_PKEY *ret = OPENSSL_zalloc(sizeof(*ret));
+
+    if (ret == NULL) {
+        EVPerr(EVP_F_EVP_PKEY_NEW, ERR_R_MALLOC_FAILURE);
+        return NULL;
+    }
+    ret->type = EVP_PKEY_NONE;
+    ret->save_type = EVP_PKEY_NONE;
+    ret->references = 1;
+    ret->save_parameters = 1;
+    ret->lock = CRYPTO_THREAD_lock_new();
+    if (ret->lock == NULL) {
+        EVPerr(EVP_F_EVP_PKEY_NEW, ERR_R_MALLOC_FAILURE);
+        OPENSSL_free(ret);
+        return NULL;
+    }
+    return ret;
+}
+
+int EVP_PKEY_up_ref(EVP_PKEY *pkey)
+{
+    int i;
+
+    if (CRYPTO_atomic_add(&pkey->references, 1, &i, pkey->lock) <= 0)
+        return 0;
+
+    REF_PRINT_COUNT("EVP_PKEY", pkey);
+    REF_ASSERT_ISNT(i < 2);
+    return ((i > 1) ? 1 : 0);
+}
+
+/*
+ * Setup a public key ASN1 method and ENGINE from a NID or a string. If pkey
+ * is NULL just return 1 or 0 if the algorithm exists.
+ */
+
+static int pkey_set_type(EVP_PKEY *pkey, int type, const char *str, int len)
+{
+    const EVP_PKEY_ASN1_METHOD *ameth;
+    ENGINE *e = NULL;
+    if (pkey) {
+        if (pkey->pkey.ptr)
+            EVP_PKEY_free_it(pkey);
+        /*
+         * If key type matches and a method exists then this lookup has
+         * succeeded once so just indicate success.
+         */
+        if ((type == pkey->save_type) && pkey->ameth)
+            return 1;
+#ifndef OPENSSL_NO_ENGINE
+        /* If we have an ENGINE release it */
+        ENGINE_finish(pkey->engine);
+        pkey->engine = NULL;
+#endif
+    }
+    if (str)
+        ameth = EVP_PKEY_asn1_find_str(&e, str, len);
+    else
+        ameth = EVP_PKEY_asn1_find(&e, type);
+#ifndef OPENSSL_NO_ENGINE
+    if (pkey == NULL)
+        ENGINE_finish(e);
+#endif
+    if (ameth == NULL) {
+        EVPerr(EVP_F_PKEY_SET_TYPE, EVP_R_UNSUPPORTED_ALGORITHM);
+        return 0;
+    }
+    if (pkey) {
+        pkey->ameth = ameth;
+        pkey->engine = e;
+
+        pkey->type = pkey->ameth->pkey_id;
+        pkey->save_type = type;
+    }
+    return 1;
+}
+
+int EVP_PKEY_set_type(EVP_PKEY *pkey, int type)
+{
+    return pkey_set_type(pkey, type, NULL, -1);
+}
+
+int EVP_PKEY_set_type_str(EVP_PKEY *pkey, const char *str, int len)
+{
+    return pkey_set_type(pkey, EVP_PKEY_NONE, str, len);
+}
+
+int EVP_PKEY_assign(EVP_PKEY *pkey, int type, void *key)
+{
+    if (pkey == NULL || !EVP_PKEY_set_type(pkey, type))
+        return 0;
+    pkey->pkey.ptr = key;
+    return (key != NULL);
+}
+
+void *EVP_PKEY_get0(const EVP_PKEY *pkey)
+{
+    return pkey->pkey.ptr;
+}
+
+const unsigned char *EVP_PKEY_get0_hmac(const EVP_PKEY *pkey, size_t *len)
+{
+    ASN1_OCTET_STRING *os = NULL;
+    if (pkey->type != EVP_PKEY_HMAC) {
+        EVPerr(EVP_F_EVP_PKEY_GET0_HMAC, EVP_R_EXPECTING_AN_HMAC_KEY);
+        return NULL;
+    }
+    os = EVP_PKEY_get0(pkey);
+    *len = os->length;
+    return os->data;
+}
+
+#ifndef OPENSSL_NO_RSA
+int EVP_PKEY_set1_RSA(EVP_PKEY *pkey, RSA *key)
+{
+    int ret = EVP_PKEY_assign_RSA(pkey, key);
+    if (ret)
+        RSA_up_ref(key);
+    return ret;
+}
+
+RSA *EVP_PKEY_get0_RSA(EVP_PKEY *pkey)
+{
+    if (pkey->type != EVP_PKEY_RSA) {
+        EVPerr(EVP_F_EVP_PKEY_GET0_RSA, EVP_R_EXPECTING_AN_RSA_KEY);
+        return NULL;
+    }
+    return pkey->pkey.rsa;
+}
+
+RSA *EVP_PKEY_get1_RSA(EVP_PKEY *pkey)
+{
+    RSA *ret = EVP_PKEY_get0_RSA(pkey);
+    if (ret != NULL)
+        RSA_up_ref(ret);
+    return ret;
+}
+#endif
+
+#ifndef OPENSSL_NO_DSA
+int EVP_PKEY_set1_DSA(EVP_PKEY *pkey, DSA *key)
+{
+    int ret = EVP_PKEY_assign_DSA(pkey, key);
+    if (ret)
+        DSA_up_ref(key);
+    return ret;
+}
+
+DSA *EVP_PKEY_get0_DSA(EVP_PKEY *pkey)
+{
+    if (pkey->type != EVP_PKEY_DSA) {
+        EVPerr(EVP_F_EVP_PKEY_GET0_DSA, EVP_R_EXPECTING_A_DSA_KEY);
+        return NULL;
+    }
+    return pkey->pkey.dsa;
+}
+
+DSA *EVP_PKEY_get1_DSA(EVP_PKEY *pkey)
+{
+    DSA *ret = EVP_PKEY_get0_DSA(pkey);
+    if (ret != NULL)
+        DSA_up_ref(ret);
+    return ret;
+}
+#endif
+
+#ifndef OPENSSL_NO_EC
+
+int EVP_PKEY_set1_EC_KEY(EVP_PKEY *pkey, EC_KEY *key)
+{
+    int ret = EVP_PKEY_assign_EC_KEY(pkey, key);
+    if (ret)
+        EC_KEY_up_ref(key);
+    return ret;
+}
+
+EC_KEY *EVP_PKEY_get0_EC_KEY(EVP_PKEY *pkey)
+{
+    if (pkey->type != EVP_PKEY_EC) {
+        EVPerr(EVP_F_EVP_PKEY_GET0_EC_KEY, EVP_R_EXPECTING_A_EC_KEY);
+        return NULL;
+    }
+    return pkey->pkey.ec;
+}
+
+EC_KEY *EVP_PKEY_get1_EC_KEY(EVP_PKEY *pkey)
+{
+    EC_KEY *ret = EVP_PKEY_get0_EC_KEY(pkey);
+    if (ret != NULL)
+        EC_KEY_up_ref(ret);
+    return ret;
+}
+#endif
+
+#ifndef OPENSSL_NO_PAILLIER
+
+int EVP_PKEY_set1_PAILLIER(EVP_PKEY *pkey, PAILLIER *key)
+{
+    int ret = EVP_PKEY_assign_PAILLIER(pkey, key);
+    if (ret)
+        PAILLIER_up_ref(key);
+    return ret;
+}
+
+PAILLIER *EVP_PKEY_get0_PAILLIER(EVP_PKEY *pkey)
+{
+    if (pkey->type != EVP_PKEY_PAILLIER) {
+        EVPerr(EVP_F_EVP_PKEY_GET0_PAILLIER, EVP_R_EXPECTING_A_PAILLIER);
+        return NULL;
+    }
+    return pkey->pkey.paillier;
+}
+
+PAILLIER *EVP_PKEY_get1_PAILLIER(EVP_PKEY *pkey)
+{
+    PAILLIER *ret = EVP_PKEY_get0_PAILLIER(pkey);
+    if (ret != NULL)
+        PAILLIER_up_ref(ret);
+    return ret;
+}
+#endif
+
+#ifndef OPENSSL_NO_SM9
+
+int EVP_PKEY_set1_SM9_MASTER(EVP_PKEY *pkey, SM9_MASTER_KEY *key)
+{
+    int ret = EVP_PKEY_assign_SM9_MASTER(pkey, key);
+    if (ret)
+        SM9_MASTER_KEY_up_ref(key);
+    return ret;
+}
+
+SM9_MASTER_KEY *EVP_PKEY_get0_SM9_MASTER(EVP_PKEY *pkey)
+{
+    if (pkey->type != EVP_PKEY_SM9_MASTER) {
+        EVPerr(EVP_F_EVP_PKEY_GET0_SM9_MASTER, EVP_R_EXPECTING_A_SM9_MASTER_KEY);
+        return NULL;
+    }
+    return pkey->pkey.sm9_master;
+}
+
+SM9_MASTER_KEY *EVP_PKEY_get1_SM9_MASTER(EVP_PKEY *pkey)
+{
+    SM9_MASTER_KEY *ret = EVP_PKEY_get0_SM9_MASTER(pkey);
+    if (ret != NULL)
+        SM9_MASTER_KEY_up_ref(ret);
+    return ret;
+}
+
+int EVP_PKEY_set1_SM9(EVP_PKEY *pkey, SM9_KEY *key)
+{
+	int ret = EVP_PKEY_assign_SM9(pkey, key);
+	if (ret)
+		SM9_KEY_up_ref(key);
+	return ret;
+}
+
+SM9_KEY *EVP_PKEY_get0_SM9(EVP_PKEY *pkey)
+{
+	if (pkey->type != EVP_PKEY_SM9) {
+		EVPerr(EVP_F_EVP_PKEY_GET0_SM9, EVP_R_EXPECTING_A_SM9_KEY);
+		return NULL;
+	}
+	return pkey->pkey.sm9;
+}
+
+SM9_KEY *EVP_PKEY_get1_SM9(EVP_PKEY *pkey)
+{
+	SM9_KEY *ret = EVP_PKEY_get0_SM9(pkey);
+	if (ret != NULL)
+		SM9_KEY_up_ref(ret);
+	return ret;
+}
+
+#endif
+
+#ifndef OPENSSL_NO_DH
+
+int EVP_PKEY_set1_DH(EVP_PKEY *pkey, DH *key)
+{
+    int ret = EVP_PKEY_assign_DH(pkey, key);
+    if (ret)
+        DH_up_ref(key);
+    return ret;
+}
+
+DH *EVP_PKEY_get0_DH(EVP_PKEY *pkey)
+{
+    if (pkey->type != EVP_PKEY_DH && pkey->type != EVP_PKEY_DHX) {
+        EVPerr(EVP_F_EVP_PKEY_GET0_DH, EVP_R_EXPECTING_A_DH_KEY);
+        return NULL;
+    }
+    return pkey->pkey.dh;
+}
+
+DH *EVP_PKEY_get1_DH(EVP_PKEY *pkey)
+{
+    DH *ret = EVP_PKEY_get0_DH(pkey);
+    if (ret != NULL)
+        DH_up_ref(ret);
+    return ret;
+}
+#endif
+
+int EVP_PKEY_type(int type)
+{
+    int ret;
+    const EVP_PKEY_ASN1_METHOD *ameth;
+    ENGINE *e;
+    ameth = EVP_PKEY_asn1_find(&e, type);
+    if (ameth)
+        ret = ameth->pkey_id;
+    else
+        ret = NID_undef;
+#ifndef OPENSSL_NO_ENGINE
+    ENGINE_finish(e);
+#endif
+    return ret;
+}
+
+int EVP_PKEY_id(const EVP_PKEY *pkey)
+{
+    return pkey->type;
+}
+
+int EVP_PKEY_base_id(const EVP_PKEY *pkey)
+{
+    return EVP_PKEY_type(pkey->type);
+}
+
+void EVP_PKEY_free(EVP_PKEY *x)
+{
+    int i;
+
+    if (x == NULL)
+        return;
+
+    CRYPTO_atomic_add(&x->references, -1, &i, x->lock);
+    REF_PRINT_COUNT("EVP_PKEY", x);
+    if (i > 0)
+        return;
+    REF_ASSERT_ISNT(i < 0);
+    EVP_PKEY_free_it(x);
+    CRYPTO_THREAD_lock_free(x->lock);
+    sk_X509_ATTRIBUTE_pop_free(x->attributes, X509_ATTRIBUTE_free);
+    OPENSSL_free(x);
+}
+
+static void EVP_PKEY_free_it(EVP_PKEY *x)
+{
+    /* internal function; x is never NULL */
+    if (x->ameth && x->ameth->pkey_free) {
+        x->ameth->pkey_free(x);
+        x->pkey.ptr = NULL;
+    }
+#ifndef OPENSSL_NO_ENGINE
+    ENGINE_finish(x->engine);
+    x->engine = NULL;
+#endif
+}
+
+static int unsup_alg(BIO *out, const EVP_PKEY *pkey, int indent,
+                     const char *kstr)
+{
+    BIO_indent(out, indent, 128);
+    BIO_printf(out, "%s algorithm \"%s\" unsupported\n",
+               kstr, OBJ_nid2ln(pkey->type));
+    return 1;
+}
+
+int EVP_PKEY_print_public(BIO *out, const EVP_PKEY *pkey,
+                          int indent, ASN1_PCTX *pctx)
+{
+    if (pkey->ameth && pkey->ameth->pub_print)
+        return pkey->ameth->pub_print(out, pkey, indent, pctx);
+
+    return unsup_alg(out, pkey, indent, "Public Key");
+}
+
+int EVP_PKEY_print_private(BIO *out, const EVP_PKEY *pkey,
+                           int indent, ASN1_PCTX *pctx)
+{
+    if (pkey->ameth && pkey->ameth->priv_print)
+        return pkey->ameth->priv_print(out, pkey, indent, pctx);
+
+    return unsup_alg(out, pkey, indent, "Private Key");
+}
+
+int EVP_PKEY_print_params(BIO *out, const EVP_PKEY *pkey,
+                          int indent, ASN1_PCTX *pctx)
+{
+    if (pkey->ameth && pkey->ameth->param_print)
+        return pkey->ameth->param_print(out, pkey, indent, pctx);
+    return unsup_alg(out, pkey, indent, "Parameters");
+}
+
+static int evp_pkey_asn1_ctrl(EVP_PKEY *pkey, int op, int arg1, void *arg2)
+{
+    if (pkey->ameth == NULL || pkey->ameth->pkey_ctrl == NULL)
+        return -2;
+    return pkey->ameth->pkey_ctrl(pkey, op, arg1, arg2);
+}
+
+int EVP_PKEY_get_default_digest_nid(EVP_PKEY *pkey, int *pnid)
+{
+    return evp_pkey_asn1_ctrl(pkey, ASN1_PKEY_CTRL_DEFAULT_MD_NID, 0, pnid);
+}
+
+int EVP_PKEY_set1_tls_encodedpoint(EVP_PKEY *pkey,
+                               const unsigned char *pt, size_t ptlen)
+{
+    if (ptlen > INT_MAX)
+        return 0;
+    if (evp_pkey_asn1_ctrl(pkey, ASN1_PKEY_CTRL_SET1_TLS_ENCPT, ptlen,
+                           (void *)pt) <= 0)
+        return 0;
+    return 1;
+}
+
+size_t EVP_PKEY_get1_tls_encodedpoint(EVP_PKEY *pkey, unsigned char **ppt)
+{
+    int rv;
+    rv = evp_pkey_asn1_ctrl(pkey, ASN1_PKEY_CTRL_GET1_TLS_ENCPT, 0, ppt);
+    if (rv <= 0)
+        return 0;
+    return rv;
+}
diff --git a/crypto/evp/p_open.c b/crypto/evp/p_open.c
new file mode 100644
index 000000000..d3fd2c719
--- /dev/null
+++ b/crypto/evp/p_open.c
@@ -0,0 +1,73 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "internal/cryptlib.h"
+#ifdef OPENSSL_NO_RSA
+NON_EMPTY_TRANSLATION_UNIT
+#else
+
+# include 
+# include 
+# include 
+# include 
+# include 
+
+int EVP_OpenInit(EVP_CIPHER_CTX *ctx, const EVP_CIPHER *type,
+                 const unsigned char *ek, int ekl, const unsigned char *iv,
+                 EVP_PKEY *priv)
+{
+    unsigned char *key = NULL;
+    int i, size = 0, ret = 0;
+
+    if (type) {
+        EVP_CIPHER_CTX_reset(ctx);
+        if (!EVP_DecryptInit_ex(ctx, type, NULL, NULL, NULL))
+            return 0;
+    }
+
+    if (!priv)
+        return 1;
+
+    if (EVP_PKEY_id(priv) != EVP_PKEY_RSA && EVP_PKEY_id(priv) != EVP_PKEY_EC) {
+        EVPerr(EVP_F_EVP_OPENINIT, EVP_R_PUBLIC_KEY_NOT_RSA_OR_EC);
+        goto err;
+    }
+
+    size = EVP_PKEY_size(priv) >= ekl ? EVP_PKEY_size(priv) : ekl;
+    key = OPENSSL_malloc(size + 2);
+    if (key == NULL) {
+        /* ERROR */
+        EVPerr(EVP_F_EVP_OPENINIT, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+
+    i = EVP_PKEY_decrypt_old(key, ek, ekl, priv);
+    if ((i <= 0) || !EVP_CIPHER_CTX_set_key_length(ctx, i)) {
+        /* ERROR */
+        goto err;
+    }
+    if (!EVP_DecryptInit_ex(ctx, NULL, NULL, key, iv))
+        goto err;
+
+    ret = 1;
+ err:
+    OPENSSL_clear_free(key, size);
+    return (ret);
+}
+
+int EVP_OpenFinal(EVP_CIPHER_CTX *ctx, unsigned char *out, int *outl)
+{
+    int i;
+
+    i = EVP_DecryptFinal_ex(ctx, out, outl);
+    if (i)
+        i = EVP_DecryptInit_ex(ctx, NULL, NULL, NULL, NULL);
+    return (i);
+}
+#endif
diff --git a/crypto/evp/p_seal.c b/crypto/evp/p_seal.c
new file mode 100644
index 000000000..ad1cac8a5
--- /dev/null
+++ b/crypto/evp/p_seal.c
@@ -0,0 +1,69 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+#include 
+#include 
+
+int EVP_SealInit(EVP_CIPHER_CTX *ctx, const EVP_CIPHER *type,
+                 unsigned char **ek, int *ekl, unsigned char *iv,
+                 EVP_PKEY **pubk, int npubk)
+{
+    unsigned char key[EVP_MAX_KEY_LENGTH];
+    int i;
+
+    if (type) {
+        EVP_CIPHER_CTX_reset(ctx);
+        if (!EVP_EncryptInit_ex(ctx, type, NULL, NULL, NULL))
+            return 0;
+    }
+    if ((npubk <= 0) || !pubk)
+        return 1;
+    if (EVP_CIPHER_CTX_rand_key(ctx, key) <= 0)
+        return 0;
+    if (EVP_CIPHER_CTX_iv_length(ctx)
+        && RAND_bytes(iv, EVP_CIPHER_CTX_iv_length(ctx)) <= 0)
+        return 0;
+
+    if (!EVP_EncryptInit_ex(ctx, NULL, NULL, key, iv))
+        return 0;
+
+    for (i = 0; i < npubk; i++) {
+        ekl[i] =
+            EVP_PKEY_encrypt_old(ek[i], key, EVP_CIPHER_CTX_key_length(ctx),
+                                 pubk[i]);
+        if (ekl[i] <= 0)
+            return (-1);
+    }
+    return (npubk);
+}
+
+/*- MACRO
+void EVP_SealUpdate(ctx,out,outl,in,inl)
+EVP_CIPHER_CTX *ctx;
+unsigned char *out;
+int *outl;
+unsigned char *in;
+int inl;
+        {
+        EVP_EncryptUpdate(ctx,out,outl,in,inl);
+        }
+*/
+
+int EVP_SealFinal(EVP_CIPHER_CTX *ctx, unsigned char *out, int *outl)
+{
+    int i;
+    i = EVP_EncryptFinal_ex(ctx, out, outl);
+    if (i)
+        i = EVP_EncryptInit_ex(ctx, NULL, NULL, NULL, NULL);
+    return i;
+}
diff --git a/crypto/evp/p_sign.c b/crypto/evp/p_sign.c
new file mode 100644
index 000000000..b5f761fc3
--- /dev/null
+++ b/crypto/evp/p_sign.c
@@ -0,0 +1,75 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+#include 
+#ifndef OPENSSL_NO_SM2
+# include 
+#endif
+#include "internal/evp_int.h"
+
+int EVP_SignFinal(EVP_MD_CTX *ctx, unsigned char *sigret,
+                  unsigned int *siglen, EVP_PKEY *pkey)
+{
+    unsigned char m[EVP_MAX_MD_SIZE];
+    unsigned int m_len = 0;
+    int i = 0;
+    size_t sltmp;
+    EVP_PKEY_CTX *pkctx = NULL;
+
+    *siglen = 0;
+    if (EVP_MD_CTX_test_flags(ctx, EVP_MD_CTX_FLAG_FINALISE)) {
+        if (!EVP_DigestFinal_ex(ctx, m, &m_len))
+            goto err;
+    } else {
+        int rv = 0;
+        EVP_MD_CTX *tmp_ctx = EVP_MD_CTX_new();
+        if (tmp_ctx == NULL) {
+            EVPerr(EVP_F_EVP_SIGNFINAL, ERR_R_MALLOC_FAILURE);
+            return 0;
+        }
+        rv = EVP_MD_CTX_copy_ex(tmp_ctx, ctx);
+        if (rv)
+            rv = EVP_DigestFinal_ex(tmp_ctx, m, &m_len);
+        EVP_MD_CTX_free(tmp_ctx);
+        if (!rv)
+            return 0;
+    }
+
+    sltmp = (size_t)EVP_PKEY_size(pkey);
+    i = 0;
+    pkctx = EVP_PKEY_CTX_new(pkey, NULL);
+    if (pkctx == NULL)
+        goto err;
+    if (EVP_PKEY_sign_init(pkctx) <= 0)
+        goto err;
+    if (EVP_PKEY_CTX_set_signature_md(pkctx, EVP_MD_CTX_md(ctx)) <= 0)
+        goto err;
+#ifndef OPENSSL_NO_SM2
+    if (EVP_PKEY_id(pkey) == EVP_PKEY_EC && EC_GROUP_get_curve_name(
+        EC_KEY_get0_group(EVP_PKEY_get0_EC_KEY(pkey))) == NID_sm2p256v1) {
+# ifdef SM2_DEBUG
+        fprintf(stderr, "[SM2_DEBUG] %s->EVP_PKEY_CTX_set_ec_scheme\n", __FUNCTION__);
+# endif
+        if (EVP_PKEY_CTX_set_ec_scheme(pkctx, NID_sm_scheme) <= 0) {
+            goto err;
+        }
+    }
+#endif
+    if (EVP_PKEY_sign(pkctx, sigret, &sltmp, m, m_len) <= 0)
+        goto err;
+    *siglen = sltmp;
+    i = 1;
+ err:
+    EVP_PKEY_CTX_free(pkctx);
+    return i;
+}
diff --git a/crypto/evp/p_verify.c b/crypto/evp/p_verify.c
new file mode 100644
index 000000000..ce33e2173
--- /dev/null
+++ b/crypto/evp/p_verify.c
@@ -0,0 +1,69 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+#include 
+#ifndef OPENSSL_NO_SM2
+# include 
+#endif
+#include "internal/evp_int.h"
+
+int EVP_VerifyFinal(EVP_MD_CTX *ctx, const unsigned char *sigbuf,
+                    unsigned int siglen, EVP_PKEY *pkey)
+{
+    unsigned char m[EVP_MAX_MD_SIZE];
+    unsigned int m_len = 0;
+    int i = 0;
+    EVP_PKEY_CTX *pkctx = NULL;
+
+    if (EVP_MD_CTX_test_flags(ctx, EVP_MD_CTX_FLAG_FINALISE)) {
+        if (!EVP_DigestFinal_ex(ctx, m, &m_len))
+            goto err;
+    } else {
+        int rv = 0;
+        EVP_MD_CTX *tmp_ctx = EVP_MD_CTX_new();
+        if (tmp_ctx == NULL) {
+            EVPerr(EVP_F_EVP_VERIFYFINAL, ERR_R_MALLOC_FAILURE);
+            return 0;
+        }
+        rv = EVP_MD_CTX_copy_ex(tmp_ctx, ctx);
+        if (rv)
+            rv = EVP_DigestFinal_ex(tmp_ctx, m, &m_len);
+        EVP_MD_CTX_free(tmp_ctx);
+        if (!rv)
+            return 0;
+    }
+
+    i = -1;
+    pkctx = EVP_PKEY_CTX_new(pkey, NULL);
+    if (pkctx == NULL)
+        goto err;
+    if (EVP_PKEY_verify_init(pkctx) <= 0)
+        goto err;
+    if (EVP_PKEY_CTX_set_signature_md(pkctx, EVP_MD_CTX_md(ctx)) <= 0)
+        goto err;
+#ifndef OPENSSL_NO_SM2
+    if (EVP_PKEY_id(pkey) == EVP_PKEY_EC && EC_GROUP_get_curve_name(
+        EC_KEY_get0_group(EVP_PKEY_get0_EC_KEY(pkey))) == NID_sm2p256v1) {
+# ifdef SM2_DEBUG
+        fprintf(stderr, "[SM2_DEBUG] %s->EVP_PKEY_CTX_set_ec_scheme\n", __FUNCTION__);
+# endif
+        if (EVP_PKEY_CTX_set_ec_scheme(pkctx, NID_sm_scheme) <= 0) {
+            goto err;
+        }
+    }
+#endif
+    i = EVP_PKEY_verify(pkctx, sigbuf, siglen, m, m_len);
+ err:
+    EVP_PKEY_CTX_free(pkctx);
+    return i;
+}
diff --git a/crypto/evp/pmeth_fn.c b/crypto/evp/pmeth_fn.c
new file mode 100644
index 000000000..eb638019c
--- /dev/null
+++ b/crypto/evp/pmeth_fn.c
@@ -0,0 +1,297 @@
+/*
+ * Copyright 2006-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+#include "internal/evp_int.h"
+
+#define M_check_autoarg(ctx, arg, arglen, err) \
+    if (ctx->pmeth->flags & EVP_PKEY_FLAG_AUTOARGLEN) {           \
+        size_t pksize = (size_t)EVP_PKEY_size(ctx->pkey);         \
+                                                                  \
+        if (pksize == 0) {                                        \
+            EVPerr(err, EVP_R_INVALID_KEY); /*ckerr_ignore*/      \
+            return 0;                                             \
+        }                                                         \
+        if (!arg) {                                               \
+            *arglen = pksize;                                     \
+            return 1;                                             \
+        }                                                         \
+        if (*arglen < pksize) {                                   \
+            EVPerr(err, EVP_R_BUFFER_TOO_SMALL); /*ckerr_ignore*/ \
+            return 0;                                             \
+        }                                                         \
+    }
+
+int EVP_PKEY_sign_init(EVP_PKEY_CTX *ctx)
+{
+    int ret;
+    if (!ctx || !ctx->pmeth || !ctx->pmeth->sign) {
+        EVPerr(EVP_F_EVP_PKEY_SIGN_INIT,
+               EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE);
+        return -2;
+    }
+    ctx->operation = EVP_PKEY_OP_SIGN;
+    if (!ctx->pmeth->sign_init)
+        return 1;
+    ret = ctx->pmeth->sign_init(ctx);
+    if (ret <= 0)
+        ctx->operation = EVP_PKEY_OP_UNDEFINED;
+    return ret;
+}
+
+int EVP_PKEY_sign(EVP_PKEY_CTX *ctx,
+                  unsigned char *sig, size_t *siglen,
+                  const unsigned char *tbs, size_t tbslen)
+{
+    if (!ctx || !ctx->pmeth || !ctx->pmeth->sign) {
+        EVPerr(EVP_F_EVP_PKEY_SIGN,
+               EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE);
+        return -2;
+    }
+    if (ctx->operation != EVP_PKEY_OP_SIGN) {
+        EVPerr(EVP_F_EVP_PKEY_SIGN, EVP_R_OPERATON_NOT_INITIALIZED);
+        return -1;
+    }
+    M_check_autoarg(ctx, sig, siglen, EVP_F_EVP_PKEY_SIGN)
+        return ctx->pmeth->sign(ctx, sig, siglen, tbs, tbslen);
+}
+
+int EVP_PKEY_verify_init(EVP_PKEY_CTX *ctx)
+{
+    int ret;
+    if (!ctx || !ctx->pmeth || !ctx->pmeth->verify) {
+        EVPerr(EVP_F_EVP_PKEY_VERIFY_INIT,
+               EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE);
+        return -2;
+    }
+    ctx->operation = EVP_PKEY_OP_VERIFY;
+    if (!ctx->pmeth->verify_init)
+        return 1;
+    ret = ctx->pmeth->verify_init(ctx);
+    if (ret <= 0)
+        ctx->operation = EVP_PKEY_OP_UNDEFINED;
+    return ret;
+}
+
+int EVP_PKEY_verify(EVP_PKEY_CTX *ctx,
+                    const unsigned char *sig, size_t siglen,
+                    const unsigned char *tbs, size_t tbslen)
+{
+    if (!ctx || !ctx->pmeth || !ctx->pmeth->verify) {
+        EVPerr(EVP_F_EVP_PKEY_VERIFY,
+               EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE);
+        return -2;
+    }
+    if (ctx->operation != EVP_PKEY_OP_VERIFY) {
+        EVPerr(EVP_F_EVP_PKEY_VERIFY, EVP_R_OPERATON_NOT_INITIALIZED);
+        return -1;
+    }
+    return ctx->pmeth->verify(ctx, sig, siglen, tbs, tbslen);
+}
+
+int EVP_PKEY_verify_recover_init(EVP_PKEY_CTX *ctx)
+{
+    int ret;
+    if (!ctx || !ctx->pmeth || !ctx->pmeth->verify_recover) {
+        EVPerr(EVP_F_EVP_PKEY_VERIFY_RECOVER_INIT,
+               EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE);
+        return -2;
+    }
+    ctx->operation = EVP_PKEY_OP_VERIFYRECOVER;
+    if (!ctx->pmeth->verify_recover_init)
+        return 1;
+    ret = ctx->pmeth->verify_recover_init(ctx);
+    if (ret <= 0)
+        ctx->operation = EVP_PKEY_OP_UNDEFINED;
+    return ret;
+}
+
+int EVP_PKEY_verify_recover(EVP_PKEY_CTX *ctx,
+                            unsigned char *rout, size_t *routlen,
+                            const unsigned char *sig, size_t siglen)
+{
+    if (!ctx || !ctx->pmeth || !ctx->pmeth->verify_recover) {
+        EVPerr(EVP_F_EVP_PKEY_VERIFY_RECOVER,
+               EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE);
+        return -2;
+    }
+    if (ctx->operation != EVP_PKEY_OP_VERIFYRECOVER) {
+        EVPerr(EVP_F_EVP_PKEY_VERIFY_RECOVER, EVP_R_OPERATON_NOT_INITIALIZED);
+        return -1;
+    }
+    M_check_autoarg(ctx, rout, routlen, EVP_F_EVP_PKEY_VERIFY_RECOVER)
+        return ctx->pmeth->verify_recover(ctx, rout, routlen, sig, siglen);
+}
+
+int EVP_PKEY_encrypt_init(EVP_PKEY_CTX *ctx)
+{
+    int ret;
+    if (!ctx || !ctx->pmeth || !ctx->pmeth->encrypt) {
+        EVPerr(EVP_F_EVP_PKEY_ENCRYPT_INIT,
+               EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE);
+        return -2;
+    }
+    ctx->operation = EVP_PKEY_OP_ENCRYPT;
+    if (!ctx->pmeth->encrypt_init)
+        return 1;
+    ret = ctx->pmeth->encrypt_init(ctx);
+    if (ret <= 0)
+        ctx->operation = EVP_PKEY_OP_UNDEFINED;
+    return ret;
+}
+
+int EVP_PKEY_encrypt(EVP_PKEY_CTX *ctx,
+                     unsigned char *out, size_t *outlen,
+                     const unsigned char *in, size_t inlen)
+{
+    if (!ctx || !ctx->pmeth || !ctx->pmeth->encrypt) {
+        EVPerr(EVP_F_EVP_PKEY_ENCRYPT,
+               EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE);
+        return -2;
+    }
+    if (ctx->operation != EVP_PKEY_OP_ENCRYPT) {
+        EVPerr(EVP_F_EVP_PKEY_ENCRYPT, EVP_R_OPERATON_NOT_INITIALIZED);
+        return -1;
+    }
+    M_check_autoarg(ctx, out, outlen, EVP_F_EVP_PKEY_ENCRYPT)
+        return ctx->pmeth->encrypt(ctx, out, outlen, in, inlen);
+}
+
+int EVP_PKEY_decrypt_init(EVP_PKEY_CTX *ctx)
+{
+    int ret;
+    if (!ctx || !ctx->pmeth || !ctx->pmeth->decrypt) {
+        EVPerr(EVP_F_EVP_PKEY_DECRYPT_INIT,
+               EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE);
+        return -2;
+    }
+    ctx->operation = EVP_PKEY_OP_DECRYPT;
+    if (!ctx->pmeth->decrypt_init)
+        return 1;
+    ret = ctx->pmeth->decrypt_init(ctx);
+    if (ret <= 0)
+        ctx->operation = EVP_PKEY_OP_UNDEFINED;
+    return ret;
+}
+
+int EVP_PKEY_decrypt(EVP_PKEY_CTX *ctx,
+                     unsigned char *out, size_t *outlen,
+                     const unsigned char *in, size_t inlen)
+{
+    if (!ctx || !ctx->pmeth || !ctx->pmeth->decrypt) {
+        EVPerr(EVP_F_EVP_PKEY_DECRYPT,
+               EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE);
+        return -2;
+    }
+    if (ctx->operation != EVP_PKEY_OP_DECRYPT) {
+        EVPerr(EVP_F_EVP_PKEY_DECRYPT, EVP_R_OPERATON_NOT_INITIALIZED);
+        return -1;
+    }
+    M_check_autoarg(ctx, out, outlen, EVP_F_EVP_PKEY_DECRYPT)
+        return ctx->pmeth->decrypt(ctx, out, outlen, in, inlen);
+}
+
+int EVP_PKEY_derive_init(EVP_PKEY_CTX *ctx)
+{
+    int ret;
+    if (!ctx || !ctx->pmeth || !ctx->pmeth->derive) {
+        EVPerr(EVP_F_EVP_PKEY_DERIVE_INIT,
+               EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE);
+        return -2;
+    }
+    ctx->operation = EVP_PKEY_OP_DERIVE;
+    if (!ctx->pmeth->derive_init)
+        return 1;
+    ret = ctx->pmeth->derive_init(ctx);
+    if (ret <= 0)
+        ctx->operation = EVP_PKEY_OP_UNDEFINED;
+    return ret;
+}
+
+int EVP_PKEY_derive_set_peer(EVP_PKEY_CTX *ctx, EVP_PKEY *peer)
+{
+    int ret;
+    if (!ctx || !ctx->pmeth
+        || !(ctx->pmeth->derive || ctx->pmeth->encrypt || ctx->pmeth->decrypt)
+        || !ctx->pmeth->ctrl) {
+        EVPerr(EVP_F_EVP_PKEY_DERIVE_SET_PEER,
+               EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE);
+        return -2;
+    }
+    if (ctx->operation != EVP_PKEY_OP_DERIVE
+        && ctx->operation != EVP_PKEY_OP_ENCRYPT
+        && ctx->operation != EVP_PKEY_OP_DECRYPT) {
+        EVPerr(EVP_F_EVP_PKEY_DERIVE_SET_PEER,
+               EVP_R_OPERATON_NOT_INITIALIZED);
+        return -1;
+    }
+
+    ret = ctx->pmeth->ctrl(ctx, EVP_PKEY_CTRL_PEER_KEY, 0, peer);
+
+    if (ret <= 0)
+        return ret;
+
+    if (ret == 2)
+        return 1;
+
+    if (!ctx->pkey) {
+        EVPerr(EVP_F_EVP_PKEY_DERIVE_SET_PEER, EVP_R_NO_KEY_SET);
+        return -1;
+    }
+
+    if (ctx->pkey->type != peer->type) {
+        EVPerr(EVP_F_EVP_PKEY_DERIVE_SET_PEER, EVP_R_DIFFERENT_KEY_TYPES);
+        return -1;
+    }
+
+    /*
+     * ran@cryptocom.ru: For clarity.  The error is if parameters in peer are
+     * present (!missing) but don't match.  EVP_PKEY_cmp_parameters may return
+     * 1 (match), 0 (don't match) and -2 (comparison is not defined).  -1
+     * (different key types) is impossible here because it is checked earlier.
+     * -2 is OK for us here, as well as 1, so we can check for 0 only.
+     */
+    if (!EVP_PKEY_missing_parameters(peer) &&
+        !EVP_PKEY_cmp_parameters(ctx->pkey, peer)) {
+        EVPerr(EVP_F_EVP_PKEY_DERIVE_SET_PEER, EVP_R_DIFFERENT_PARAMETERS);
+        return -1;
+    }
+
+    EVP_PKEY_free(ctx->peerkey);
+    ctx->peerkey = peer;
+
+    ret = ctx->pmeth->ctrl(ctx, EVP_PKEY_CTRL_PEER_KEY, 1, peer);
+
+    if (ret <= 0) {
+        ctx->peerkey = NULL;
+        return ret;
+    }
+
+    EVP_PKEY_up_ref(peer);
+    return 1;
+}
+
+int EVP_PKEY_derive(EVP_PKEY_CTX *ctx, unsigned char *key, size_t *pkeylen)
+{
+    if (!ctx || !ctx->pmeth || !ctx->pmeth->derive) {
+        EVPerr(EVP_F_EVP_PKEY_DERIVE,
+               EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE);
+        return -2;
+    }
+    if (ctx->operation != EVP_PKEY_OP_DERIVE) {
+        EVPerr(EVP_F_EVP_PKEY_DERIVE, EVP_R_OPERATON_NOT_INITIALIZED);
+        return -1;
+    }
+    M_check_autoarg(ctx, key, pkeylen, EVP_F_EVP_PKEY_DERIVE)
+        return ctx->pmeth->derive(ctx, key, pkeylen);
+}
diff --git a/crypto/evp/pmeth_gn.c b/crypto/evp/pmeth_gn.c
new file mode 100644
index 000000000..6adc3a9c1
--- /dev/null
+++ b/crypto/evp/pmeth_gn.c
@@ -0,0 +1,169 @@
+/*
+ * Copyright 2006-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+#include "internal/bn_int.h"
+#include "internal/evp_int.h"
+
+int EVP_PKEY_paramgen_init(EVP_PKEY_CTX *ctx)
+{
+    int ret;
+    if (!ctx || !ctx->pmeth || !ctx->pmeth->paramgen) {
+        EVPerr(EVP_F_EVP_PKEY_PARAMGEN_INIT,
+               EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE);
+        return -2;
+    }
+    ctx->operation = EVP_PKEY_OP_PARAMGEN;
+    if (!ctx->pmeth->paramgen_init)
+        return 1;
+    ret = ctx->pmeth->paramgen_init(ctx);
+    if (ret <= 0)
+        ctx->operation = EVP_PKEY_OP_UNDEFINED;
+    return ret;
+}
+
+int EVP_PKEY_paramgen(EVP_PKEY_CTX *ctx, EVP_PKEY **ppkey)
+{
+    int ret;
+    if (!ctx || !ctx->pmeth || !ctx->pmeth->paramgen) {
+        EVPerr(EVP_F_EVP_PKEY_PARAMGEN,
+               EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE);
+        return -2;
+    }
+
+    if (ctx->operation != EVP_PKEY_OP_PARAMGEN) {
+        EVPerr(EVP_F_EVP_PKEY_PARAMGEN, EVP_R_OPERATON_NOT_INITIALIZED);
+        return -1;
+    }
+
+    if (ppkey == NULL)
+        return -1;
+
+    if (*ppkey == NULL)
+        *ppkey = EVP_PKEY_new();
+
+    if (*ppkey == NULL) {
+        EVPerr(EVP_F_EVP_PKEY_PARAMGEN, ERR_R_MALLOC_FAILURE);
+        return -1;
+    }
+
+    ret = ctx->pmeth->paramgen(ctx, *ppkey);
+    if (ret <= 0) {
+        EVP_PKEY_free(*ppkey);
+        *ppkey = NULL;
+    }
+    return ret;
+}
+
+int EVP_PKEY_keygen_init(EVP_PKEY_CTX *ctx)
+{
+    int ret;
+    if (!ctx || !ctx->pmeth || !ctx->pmeth->keygen) {
+        EVPerr(EVP_F_EVP_PKEY_KEYGEN_INIT,
+               EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE);
+        return -2;
+    }
+    ctx->operation = EVP_PKEY_OP_KEYGEN;
+    if (!ctx->pmeth->keygen_init)
+        return 1;
+    ret = ctx->pmeth->keygen_init(ctx);
+    if (ret <= 0)
+        ctx->operation = EVP_PKEY_OP_UNDEFINED;
+    return ret;
+}
+
+int EVP_PKEY_keygen(EVP_PKEY_CTX *ctx, EVP_PKEY **ppkey)
+{
+    int ret;
+
+    if (!ctx || !ctx->pmeth || !ctx->pmeth->keygen) {
+        EVPerr(EVP_F_EVP_PKEY_KEYGEN,
+               EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE);
+        return -2;
+    }
+    if (ctx->operation != EVP_PKEY_OP_KEYGEN) {
+        EVPerr(EVP_F_EVP_PKEY_KEYGEN, EVP_R_OPERATON_NOT_INITIALIZED);
+        return -1;
+    }
+
+    if (ppkey == NULL)
+        return -1;
+
+    if (*ppkey == NULL)
+        *ppkey = EVP_PKEY_new();
+    if (*ppkey == NULL)
+        return -1;
+
+    ret = ctx->pmeth->keygen(ctx, *ppkey);
+    if (ret <= 0) {
+        EVP_PKEY_free(*ppkey);
+        *ppkey = NULL;
+    }
+    return ret;
+}
+
+void EVP_PKEY_CTX_set_cb(EVP_PKEY_CTX *ctx, EVP_PKEY_gen_cb *cb)
+{
+    ctx->pkey_gencb = cb;
+}
+
+EVP_PKEY_gen_cb *EVP_PKEY_CTX_get_cb(EVP_PKEY_CTX *ctx)
+{
+    return ctx->pkey_gencb;
+}
+
+/*
+ * "translation callback" to call EVP_PKEY_CTX callbacks using BN_GENCB style
+ * callbacks.
+ */
+
+static int trans_cb(int a, int b, BN_GENCB *gcb)
+{
+    EVP_PKEY_CTX *ctx = BN_GENCB_get_arg(gcb);
+    ctx->keygen_info[0] = a;
+    ctx->keygen_info[1] = b;
+    return ctx->pkey_gencb(ctx);
+}
+
+void evp_pkey_set_cb_translate(BN_GENCB *cb, EVP_PKEY_CTX *ctx)
+{
+    BN_GENCB_set(cb, trans_cb, ctx);
+}
+
+int EVP_PKEY_CTX_get_keygen_info(EVP_PKEY_CTX *ctx, int idx)
+{
+    if (idx == -1)
+        return ctx->keygen_info_count;
+    if (idx < 0 || idx > ctx->keygen_info_count)
+        return 0;
+    return ctx->keygen_info[idx];
+}
+
+EVP_PKEY *EVP_PKEY_new_mac_key(int type, ENGINE *e,
+                               const unsigned char *key, int keylen)
+{
+    EVP_PKEY_CTX *mac_ctx = NULL;
+    EVP_PKEY *mac_key = NULL;
+    mac_ctx = EVP_PKEY_CTX_new_id(type, e);
+    if (!mac_ctx)
+        return NULL;
+    if (EVP_PKEY_keygen_init(mac_ctx) <= 0)
+        goto merr;
+    if (EVP_PKEY_CTX_set_mac_key(mac_ctx, key, keylen) <= 0)
+        goto merr;
+    if (EVP_PKEY_keygen(mac_ctx, &mac_key) <= 0)
+        goto merr;
+ merr:
+    EVP_PKEY_CTX_free(mac_ctx);
+    return mac_key;
+}
diff --git a/crypto/evp/pmeth_lib.c b/crypto/evp/pmeth_lib.c
new file mode 100644
index 000000000..f774feae2
--- /dev/null
+++ b/crypto/evp/pmeth_lib.c
@@ -0,0 +1,728 @@
+/*
+ * Copyright 2006-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include "internal/cryptlib.h"
+#ifndef OPENSSL_NO_ENGINE
+# include 
+#endif
+#include 
+#include 
+#include "internal/asn1_int.h"
+#include "internal/evp_int.h"
+#include "internal/numbers.h"
+
+typedef int sk_cmp_fn_type(const char *const *a, const char *const *b);
+
+static STACK_OF(EVP_PKEY_METHOD) *app_pkey_methods = NULL;
+
+static const EVP_PKEY_METHOD *standard_methods[] = {
+#ifndef OPENSSL_NO_RSA
+    &rsa_pkey_meth,
+#endif
+#ifndef OPENSSL_NO_DH
+    &dh_pkey_meth,
+#endif
+#ifndef OPENSSL_NO_DSA
+    &dsa_pkey_meth,
+#endif
+#ifndef OPENSSL_NO_EC
+    &ec_pkey_meth,
+#endif
+    &hmac_pkey_meth,
+#ifndef OPENSSL_NO_CMAC
+    &cmac_pkey_meth,
+#endif
+#ifndef OPENSSL_NO_DH
+    &dhx_pkey_meth,
+#endif
+    &tls1_prf_pkey_meth,
+#ifndef OPENSSL_NO_EC
+    &ecx25519_pkey_meth,
+#endif
+    &hkdf_pkey_meth,
+#ifndef OPENSSL_NO_SM9
+    &sm9_pkey_meth,
+    &sm9_master_pkey_meth,
+#endif
+#ifndef OPENSSL_NO_PAILLIER
+    &paillier_pkey_meth,
+#endif
+};
+
+DECLARE_OBJ_BSEARCH_CMP_FN(const EVP_PKEY_METHOD *, const EVP_PKEY_METHOD *,
+                           pmeth);
+
+static int pmeth_cmp(const EVP_PKEY_METHOD *const *a,
+                     const EVP_PKEY_METHOD *const *b)
+{
+    return ((*a)->pkey_id - (*b)->pkey_id);
+}
+
+IMPLEMENT_OBJ_BSEARCH_CMP_FN(const EVP_PKEY_METHOD *, const EVP_PKEY_METHOD *,
+                             pmeth);
+
+const EVP_PKEY_METHOD *EVP_PKEY_meth_find(int type)
+{
+    EVP_PKEY_METHOD tmp;
+    const EVP_PKEY_METHOD *t = &tmp, **ret;
+    tmp.pkey_id = type;
+    if (app_pkey_methods) {
+        int idx;
+        idx = sk_EVP_PKEY_METHOD_find(app_pkey_methods, &tmp);
+        if (idx >= 0)
+            return sk_EVP_PKEY_METHOD_value(app_pkey_methods, idx);
+    }
+    ret = OBJ_bsearch_pmeth(&t, standard_methods,
+                            sizeof(standard_methods) /
+                            sizeof(EVP_PKEY_METHOD *));
+    if (!ret || !*ret)
+        return NULL;
+    return *ret;
+}
+
+static EVP_PKEY_CTX *int_ctx_new(EVP_PKEY *pkey, ENGINE *e, int id)
+{
+    EVP_PKEY_CTX *ret;
+    const EVP_PKEY_METHOD *pmeth;
+    if (id == -1) {
+        if (!pkey || !pkey->ameth)
+            return NULL;
+        id = pkey->ameth->pkey_id;
+    }
+#ifndef OPENSSL_NO_ENGINE
+    if (pkey && pkey->engine)
+        e = pkey->engine;
+    /* Try to find an ENGINE which implements this method */
+    if (e) {
+        if (!ENGINE_init(e)) {
+            EVPerr(EVP_F_INT_CTX_NEW, ERR_R_ENGINE_LIB);
+            return NULL;
+        }
+    } else
+        e = ENGINE_get_pkey_meth_engine(id);
+
+    /*
+     * If an ENGINE handled this method look it up. Otherwise use internal
+     * tables.
+     */
+
+    if (e)
+        pmeth = ENGINE_get_pkey_meth(e, id);
+    else
+#endif
+        pmeth = EVP_PKEY_meth_find(id);
+
+    if (pmeth == NULL) {
+        EVPerr(EVP_F_INT_CTX_NEW, EVP_R_UNSUPPORTED_ALGORITHM);
+        return NULL;
+    }
+
+    ret = OPENSSL_zalloc(sizeof(*ret));
+    if (ret == NULL) {
+#ifndef OPENSSL_NO_ENGINE
+        ENGINE_finish(e);
+#endif
+        EVPerr(EVP_F_INT_CTX_NEW, ERR_R_MALLOC_FAILURE);
+        return NULL;
+    }
+    ret->engine = e;
+    ret->pmeth = pmeth;
+    ret->operation = EVP_PKEY_OP_UNDEFINED;
+    ret->pkey = pkey;
+    if (pkey)
+        EVP_PKEY_up_ref(pkey);
+
+    if (pmeth->init) {
+        if (pmeth->init(ret) <= 0) {
+            EVP_PKEY_CTX_free(ret);
+            return NULL;
+        }
+    }
+
+    return ret;
+}
+
+EVP_PKEY_METHOD *EVP_PKEY_meth_new(int id, int flags)
+{
+    EVP_PKEY_METHOD *pmeth;
+
+    pmeth = OPENSSL_zalloc(sizeof(*pmeth));
+    if (pmeth == NULL)
+        return NULL;
+
+    pmeth->pkey_id = id;
+    pmeth->flags = flags | EVP_PKEY_FLAG_DYNAMIC;
+    return pmeth;
+}
+
+void EVP_PKEY_meth_get0_info(int *ppkey_id, int *pflags,
+                             const EVP_PKEY_METHOD *meth)
+{
+    if (ppkey_id)
+        *ppkey_id = meth->pkey_id;
+    if (pflags)
+        *pflags = meth->flags;
+}
+
+void EVP_PKEY_meth_copy(EVP_PKEY_METHOD *dst, const EVP_PKEY_METHOD *src)
+{
+
+    dst->init = src->init;
+    dst->copy = src->copy;
+    dst->cleanup = src->cleanup;
+
+    dst->paramgen_init = src->paramgen_init;
+    dst->paramgen = src->paramgen;
+
+    dst->keygen_init = src->keygen_init;
+    dst->keygen = src->keygen;
+
+    dst->sign_init = src->sign_init;
+    dst->sign = src->sign;
+
+    dst->verify_init = src->verify_init;
+    dst->verify = src->verify;
+
+    dst->verify_recover_init = src->verify_recover_init;
+    dst->verify_recover = src->verify_recover;
+
+    dst->signctx_init = src->signctx_init;
+    dst->signctx = src->signctx;
+
+    dst->verifyctx_init = src->verifyctx_init;
+    dst->verifyctx = src->verifyctx;
+
+    dst->encrypt_init = src->encrypt_init;
+    dst->encrypt = src->encrypt;
+
+    dst->decrypt_init = src->decrypt_init;
+    dst->decrypt = src->decrypt;
+
+    dst->derive_init = src->derive_init;
+    dst->derive = src->derive;
+
+    dst->ctrl = src->ctrl;
+    dst->ctrl_str = src->ctrl_str;
+}
+
+void EVP_PKEY_meth_free(EVP_PKEY_METHOD *pmeth)
+{
+    if (pmeth && (pmeth->flags & EVP_PKEY_FLAG_DYNAMIC))
+        OPENSSL_free(pmeth);
+}
+
+EVP_PKEY_CTX *EVP_PKEY_CTX_new(EVP_PKEY *pkey, ENGINE *e)
+{
+    return int_ctx_new(pkey, e, -1);
+}
+
+EVP_PKEY_CTX *EVP_PKEY_CTX_new_id(int id, ENGINE *e)
+{
+    return int_ctx_new(NULL, e, id);
+}
+
+EVP_PKEY_CTX *EVP_PKEY_CTX_dup(EVP_PKEY_CTX *pctx)
+{
+    EVP_PKEY_CTX *rctx;
+    if (!pctx->pmeth || !pctx->pmeth->copy)
+        return NULL;
+#ifndef OPENSSL_NO_ENGINE
+    /* Make sure it's safe to copy a pkey context using an ENGINE */
+    if (pctx->engine && !ENGINE_init(pctx->engine)) {
+        EVPerr(EVP_F_EVP_PKEY_CTX_DUP, ERR_R_ENGINE_LIB);
+        return 0;
+    }
+#endif
+    rctx = OPENSSL_malloc(sizeof(*rctx));
+    if (rctx == NULL)
+        return NULL;
+
+    rctx->pmeth = pctx->pmeth;
+#ifndef OPENSSL_NO_ENGINE
+    rctx->engine = pctx->engine;
+#endif
+
+    if (pctx->pkey)
+        EVP_PKEY_up_ref(pctx->pkey);
+
+    rctx->pkey = pctx->pkey;
+
+    if (pctx->peerkey)
+        EVP_PKEY_up_ref(pctx->peerkey);
+
+    rctx->peerkey = pctx->peerkey;
+
+    rctx->data = NULL;
+    rctx->app_data = NULL;
+    rctx->operation = pctx->operation;
+
+    if (pctx->pmeth->copy(rctx, pctx) > 0)
+        return rctx;
+
+    EVP_PKEY_CTX_free(rctx);
+    return NULL;
+
+}
+
+int EVP_PKEY_meth_add0(const EVP_PKEY_METHOD *pmeth)
+{
+    if (app_pkey_methods == NULL) {
+        app_pkey_methods = sk_EVP_PKEY_METHOD_new(pmeth_cmp);
+        if (app_pkey_methods == NULL)
+            return 0;
+    }
+    if (!sk_EVP_PKEY_METHOD_push(app_pkey_methods, pmeth))
+        return 0;
+    sk_EVP_PKEY_METHOD_sort(app_pkey_methods);
+    return 1;
+}
+
+void EVP_PKEY_CTX_free(EVP_PKEY_CTX *ctx)
+{
+    if (ctx == NULL)
+        return;
+    if (ctx->pmeth && ctx->pmeth->cleanup)
+        ctx->pmeth->cleanup(ctx);
+    EVP_PKEY_free(ctx->pkey);
+    EVP_PKEY_free(ctx->peerkey);
+#ifndef OPENSSL_NO_ENGINE
+    ENGINE_finish(ctx->engine);
+#endif
+    OPENSSL_free(ctx);
+}
+
+int EVP_PKEY_CTX_ctrl(EVP_PKEY_CTX *ctx, int keytype, int optype,
+                      int cmd, int p1, void *p2)
+{
+    int ret;
+    if (!ctx || !ctx->pmeth || !ctx->pmeth->ctrl) {
+        EVPerr(EVP_F_EVP_PKEY_CTX_CTRL, EVP_R_COMMAND_NOT_SUPPORTED);
+        return -2;
+    }
+    if ((keytype != -1) && (ctx->pmeth->pkey_id != keytype))
+        return -1;
+
+    if (ctx->operation == EVP_PKEY_OP_UNDEFINED) {
+        EVPerr(EVP_F_EVP_PKEY_CTX_CTRL, EVP_R_NO_OPERATION_SET);
+        return -1;
+    }
+
+    if ((optype != -1) && !(ctx->operation & optype)) {
+        EVPerr(EVP_F_EVP_PKEY_CTX_CTRL, EVP_R_INVALID_OPERATION);
+        return -1;
+    }
+
+    ret = ctx->pmeth->ctrl(ctx, cmd, p1, p2);
+
+    if (ret == -2)
+        EVPerr(EVP_F_EVP_PKEY_CTX_CTRL, EVP_R_COMMAND_NOT_SUPPORTED);
+
+    return ret;
+
+}
+
+int EVP_PKEY_CTX_ctrl_str(EVP_PKEY_CTX *ctx,
+                          const char *name, const char *value)
+{
+    if (!ctx || !ctx->pmeth || !ctx->pmeth->ctrl_str) {
+        EVPerr(EVP_F_EVP_PKEY_CTX_CTRL_STR, EVP_R_COMMAND_NOT_SUPPORTED);
+        return -2;
+    }
+    if (strcmp(name, "digest") == 0) {
+        const EVP_MD *md;
+        if (value == NULL || (md = EVP_get_digestbyname(value)) == NULL) {
+            EVPerr(EVP_F_EVP_PKEY_CTX_CTRL_STR, EVP_R_INVALID_DIGEST);
+            return 0;
+        }
+        return EVP_PKEY_CTX_set_signature_md(ctx, md);
+    }
+    return ctx->pmeth->ctrl_str(ctx, name, value);
+}
+
+/* Utility functions to send a string of hex string to a ctrl */
+
+int EVP_PKEY_CTX_str2ctrl(EVP_PKEY_CTX *ctx, int cmd, const char *str)
+{
+    size_t len;
+
+    len = strlen(str);
+    if (len > INT_MAX)
+        return -1;
+    return ctx->pmeth->ctrl(ctx, cmd, len, (void *)str);
+}
+
+int EVP_PKEY_CTX_hex2ctrl(EVP_PKEY_CTX *ctx, int cmd, const char *hex)
+{
+    unsigned char *bin;
+    long binlen;
+    int rv = -1;
+
+    bin = OPENSSL_hexstr2buf(hex, &binlen);
+    if (bin == NULL)
+        return 0;
+    if (binlen <= INT_MAX)
+        rv = ctx->pmeth->ctrl(ctx, cmd, binlen, bin);
+    OPENSSL_free(bin);
+    return rv;
+}
+
+int EVP_PKEY_CTX_get_operation(EVP_PKEY_CTX *ctx)
+{
+    return ctx->operation;
+}
+
+void EVP_PKEY_CTX_set0_keygen_info(EVP_PKEY_CTX *ctx, int *dat, int datlen)
+{
+    ctx->keygen_info = dat;
+    ctx->keygen_info_count = datlen;
+}
+
+void EVP_PKEY_CTX_set_data(EVP_PKEY_CTX *ctx, void *data)
+{
+    ctx->data = data;
+}
+
+void *EVP_PKEY_CTX_get_data(EVP_PKEY_CTX *ctx)
+{
+    return ctx->data;
+}
+
+EVP_PKEY *EVP_PKEY_CTX_get0_pkey(EVP_PKEY_CTX *ctx)
+{
+    return ctx->pkey;
+}
+
+EVP_PKEY *EVP_PKEY_CTX_get0_peerkey(EVP_PKEY_CTX *ctx)
+{
+    return ctx->peerkey;
+}
+
+void EVP_PKEY_CTX_set_app_data(EVP_PKEY_CTX *ctx, void *data)
+{
+    ctx->app_data = data;
+}
+
+void *EVP_PKEY_CTX_get_app_data(EVP_PKEY_CTX *ctx)
+{
+    return ctx->app_data;
+}
+
+void EVP_PKEY_meth_set_init(EVP_PKEY_METHOD *pmeth,
+                            int (*init) (EVP_PKEY_CTX *ctx))
+{
+    pmeth->init = init;
+}
+
+void EVP_PKEY_meth_set_copy(EVP_PKEY_METHOD *pmeth,
+                            int (*copy) (EVP_PKEY_CTX *dst,
+                                         EVP_PKEY_CTX *src))
+{
+    pmeth->copy = copy;
+}
+
+void EVP_PKEY_meth_set_cleanup(EVP_PKEY_METHOD *pmeth,
+                               void (*cleanup) (EVP_PKEY_CTX *ctx))
+{
+    pmeth->cleanup = cleanup;
+}
+
+void EVP_PKEY_meth_set_paramgen(EVP_PKEY_METHOD *pmeth,
+                                int (*paramgen_init) (EVP_PKEY_CTX *ctx),
+                                int (*paramgen) (EVP_PKEY_CTX *ctx,
+                                                 EVP_PKEY *pkey))
+{
+    pmeth->paramgen_init = paramgen_init;
+    pmeth->paramgen = paramgen;
+}
+
+void EVP_PKEY_meth_set_keygen(EVP_PKEY_METHOD *pmeth,
+                              int (*keygen_init) (EVP_PKEY_CTX *ctx),
+                              int (*keygen) (EVP_PKEY_CTX *ctx,
+                                             EVP_PKEY *pkey))
+{
+    pmeth->keygen_init = keygen_init;
+    pmeth->keygen = keygen;
+}
+
+void EVP_PKEY_meth_set_sign(EVP_PKEY_METHOD *pmeth,
+                            int (*sign_init) (EVP_PKEY_CTX *ctx),
+                            int (*sign) (EVP_PKEY_CTX *ctx,
+                                         unsigned char *sig, size_t *siglen,
+                                         const unsigned char *tbs,
+                                         size_t tbslen))
+{
+    pmeth->sign_init = sign_init;
+    pmeth->sign = sign;
+}
+
+void EVP_PKEY_meth_set_verify(EVP_PKEY_METHOD *pmeth,
+                              int (*verify_init) (EVP_PKEY_CTX *ctx),
+                              int (*verify) (EVP_PKEY_CTX *ctx,
+                                             const unsigned char *sig,
+                                             size_t siglen,
+                                             const unsigned char *tbs,
+                                             size_t tbslen))
+{
+    pmeth->verify_init = verify_init;
+    pmeth->verify = verify;
+}
+
+void EVP_PKEY_meth_set_verify_recover(EVP_PKEY_METHOD *pmeth,
+                                      int (*verify_recover_init) (EVP_PKEY_CTX
+                                                                  *ctx),
+                                      int (*verify_recover) (EVP_PKEY_CTX
+                                                             *ctx,
+                                                             unsigned char
+                                                             *sig,
+                                                             size_t *siglen,
+                                                             const unsigned
+                                                             char *tbs,
+                                                             size_t tbslen))
+{
+    pmeth->verify_recover_init = verify_recover_init;
+    pmeth->verify_recover = verify_recover;
+}
+
+void EVP_PKEY_meth_set_signctx(EVP_PKEY_METHOD *pmeth,
+                               int (*signctx_init) (EVP_PKEY_CTX *ctx,
+                                                    EVP_MD_CTX *mctx),
+                               int (*signctx) (EVP_PKEY_CTX *ctx,
+                                               unsigned char *sig,
+                                               size_t *siglen,
+                                               EVP_MD_CTX *mctx))
+{
+    pmeth->signctx_init = signctx_init;
+    pmeth->signctx = signctx;
+}
+
+void EVP_PKEY_meth_set_verifyctx(EVP_PKEY_METHOD *pmeth,
+                                 int (*verifyctx_init) (EVP_PKEY_CTX *ctx,
+                                                        EVP_MD_CTX *mctx),
+                                 int (*verifyctx) (EVP_PKEY_CTX *ctx,
+                                                   const unsigned char *sig,
+                                                   int siglen,
+                                                   EVP_MD_CTX *mctx))
+{
+    pmeth->verifyctx_init = verifyctx_init;
+    pmeth->verifyctx = verifyctx;
+}
+
+void EVP_PKEY_meth_set_encrypt(EVP_PKEY_METHOD *pmeth,
+                               int (*encrypt_init) (EVP_PKEY_CTX *ctx),
+                               int (*encryptfn) (EVP_PKEY_CTX *ctx,
+                                                 unsigned char *out,
+                                                 size_t *outlen,
+                                                 const unsigned char *in,
+                                                 size_t inlen))
+{
+    pmeth->encrypt_init = encrypt_init;
+    pmeth->encrypt = encryptfn;
+}
+
+void EVP_PKEY_meth_set_decrypt(EVP_PKEY_METHOD *pmeth,
+                               int (*decrypt_init) (EVP_PKEY_CTX *ctx),
+                               int (*decrypt) (EVP_PKEY_CTX *ctx,
+                                               unsigned char *out,
+                                               size_t *outlen,
+                                               const unsigned char *in,
+                                               size_t inlen))
+{
+    pmeth->decrypt_init = decrypt_init;
+    pmeth->decrypt = decrypt;
+}
+
+void EVP_PKEY_meth_set_derive(EVP_PKEY_METHOD *pmeth,
+                              int (*derive_init) (EVP_PKEY_CTX *ctx),
+                              int (*derive) (EVP_PKEY_CTX *ctx,
+                                             unsigned char *key,
+                                             size_t *keylen))
+{
+    pmeth->derive_init = derive_init;
+    pmeth->derive = derive;
+}
+
+void EVP_PKEY_meth_set_ctrl(EVP_PKEY_METHOD *pmeth,
+                            int (*ctrl) (EVP_PKEY_CTX *ctx, int type, int p1,
+                                         void *p2),
+                            int (*ctrl_str) (EVP_PKEY_CTX *ctx,
+                                             const char *type,
+                                             const char *value))
+{
+    pmeth->ctrl = ctrl;
+    pmeth->ctrl_str = ctrl_str;
+}
+
+void EVP_PKEY_meth_get_init(EVP_PKEY_METHOD *pmeth,
+                            int (**pinit) (EVP_PKEY_CTX *ctx))
+{
+    *pinit = pmeth->init;
+}
+
+void EVP_PKEY_meth_get_copy(EVP_PKEY_METHOD *pmeth,
+                            int (**pcopy) (EVP_PKEY_CTX *dst,
+                                           EVP_PKEY_CTX *src))
+{
+    *pcopy = pmeth->copy;
+}
+
+void EVP_PKEY_meth_get_cleanup(EVP_PKEY_METHOD *pmeth,
+                               void (**pcleanup) (EVP_PKEY_CTX *ctx))
+{
+    *pcleanup = pmeth->cleanup;
+}
+
+void EVP_PKEY_meth_get_paramgen(EVP_PKEY_METHOD *pmeth,
+                                int (**pparamgen_init) (EVP_PKEY_CTX *ctx),
+                                int (**pparamgen) (EVP_PKEY_CTX *ctx,
+                                                   EVP_PKEY *pkey))
+{
+    if (pparamgen_init)
+        *pparamgen_init = pmeth->paramgen_init;
+    if (pparamgen)
+        *pparamgen = pmeth->paramgen;
+}
+
+void EVP_PKEY_meth_get_keygen(EVP_PKEY_METHOD *pmeth,
+                              int (**pkeygen_init) (EVP_PKEY_CTX *ctx),
+                              int (**pkeygen) (EVP_PKEY_CTX *ctx,
+                                               EVP_PKEY *pkey))
+{
+    if (pkeygen_init)
+        *pkeygen_init = pmeth->keygen_init;
+    if (pkeygen)
+        *pkeygen = pmeth->keygen;
+}
+
+void EVP_PKEY_meth_get_sign(EVP_PKEY_METHOD *pmeth,
+                            int (**psign_init) (EVP_PKEY_CTX *ctx),
+                            int (**psign) (EVP_PKEY_CTX *ctx,
+                                           unsigned char *sig, size_t *siglen,
+                                           const unsigned char *tbs,
+                                           size_t tbslen))
+{
+    if (psign_init)
+        *psign_init = pmeth->sign_init;
+    if (psign)
+        *psign = pmeth->sign;
+}
+
+void EVP_PKEY_meth_get_verify(EVP_PKEY_METHOD *pmeth,
+                              int (**pverify_init) (EVP_PKEY_CTX *ctx),
+                              int (**pverify) (EVP_PKEY_CTX *ctx,
+                                               const unsigned char *sig,
+                                               size_t siglen,
+                                               const unsigned char *tbs,
+                                               size_t tbslen))
+{
+    if (pverify_init)
+        *pverify_init = pmeth->verify_init;
+    if (pverify)
+        *pverify = pmeth->verify;
+}
+
+void EVP_PKEY_meth_get_verify_recover(EVP_PKEY_METHOD *pmeth,
+                                      int (**pverify_recover_init) (EVP_PKEY_CTX
+                                                                    *ctx),
+                                      int (**pverify_recover) (EVP_PKEY_CTX
+                                                               *ctx,
+                                                               unsigned char
+                                                               *sig,
+                                                               size_t *siglen,
+                                                               const unsigned
+                                                               char *tbs,
+                                                               size_t tbslen))
+{
+    if (pverify_recover_init)
+        *pverify_recover_init = pmeth->verify_recover_init;
+    if (pverify_recover)
+        *pverify_recover = pmeth->verify_recover;
+}
+
+void EVP_PKEY_meth_get_signctx(EVP_PKEY_METHOD *pmeth,
+                               int (**psignctx_init) (EVP_PKEY_CTX *ctx,
+                                                      EVP_MD_CTX *mctx),
+                               int (**psignctx) (EVP_PKEY_CTX *ctx,
+                                                 unsigned char *sig,
+                                                 size_t *siglen,
+                                                 EVP_MD_CTX *mctx))
+{
+    if (psignctx_init)
+        *psignctx_init = pmeth->signctx_init;
+    if (psignctx)
+        *psignctx = pmeth->signctx;
+}
+
+void EVP_PKEY_meth_get_verifyctx(EVP_PKEY_METHOD *pmeth,
+                                 int (**pverifyctx_init) (EVP_PKEY_CTX *ctx,
+                                                          EVP_MD_CTX *mctx),
+                                 int (**pverifyctx) (EVP_PKEY_CTX *ctx,
+                                                     const unsigned char *sig,
+                                                     int siglen,
+                                                     EVP_MD_CTX *mctx))
+{
+    if (pverifyctx_init)
+        *pverifyctx_init = pmeth->verifyctx_init;
+    if (pverifyctx)
+        *pverifyctx = pmeth->verifyctx;
+}
+
+void EVP_PKEY_meth_get_encrypt(EVP_PKEY_METHOD *pmeth,
+                               int (**pencrypt_init) (EVP_PKEY_CTX *ctx),
+                               int (**pencryptfn) (EVP_PKEY_CTX *ctx,
+                                                   unsigned char *out,
+                                                   size_t *outlen,
+                                                   const unsigned char *in,
+                                                   size_t inlen))
+{
+    if (pencrypt_init)
+        *pencrypt_init = pmeth->encrypt_init;
+    if (pencryptfn)
+        *pencryptfn = pmeth->encrypt;
+}
+
+void EVP_PKEY_meth_get_decrypt(EVP_PKEY_METHOD *pmeth,
+                               int (**pdecrypt_init) (EVP_PKEY_CTX *ctx),
+                               int (**pdecrypt) (EVP_PKEY_CTX *ctx,
+                                                 unsigned char *out,
+                                                 size_t *outlen,
+                                                 const unsigned char *in,
+                                                 size_t inlen))
+{
+    if (pdecrypt_init)
+        *pdecrypt_init = pmeth->decrypt_init;
+    if (pdecrypt)
+        *pdecrypt = pmeth->decrypt;
+}
+
+void EVP_PKEY_meth_get_derive(EVP_PKEY_METHOD *pmeth,
+                              int (**pderive_init) (EVP_PKEY_CTX *ctx),
+                              int (**pderive) (EVP_PKEY_CTX *ctx,
+                                               unsigned char *key,
+                                               size_t *keylen))
+{
+    if (pderive_init)
+        *pderive_init = pmeth->derive_init;
+    if (pderive)
+        *pderive = pmeth->derive;
+}
+
+void EVP_PKEY_meth_get_ctrl(EVP_PKEY_METHOD *pmeth,
+                            int (**pctrl) (EVP_PKEY_CTX *ctx, int type, int p1,
+                                           void *p2),
+                            int (**pctrl_str) (EVP_PKEY_CTX *ctx,
+                                               const char *type,
+                                               const char *value))
+{
+    if (pctrl)
+        *pctrl = pmeth->ctrl;
+    if (pctrl_str)
+        *pctrl_str = pmeth->ctrl_str;
+}
diff --git a/crypto/evp/scrypt.c b/crypto/evp/scrypt.c
new file mode 100644
index 000000000..200219c0f
--- /dev/null
+++ b/crypto/evp/scrypt.c
@@ -0,0 +1,258 @@
+/*
+ * Copyright 2015-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#ifndef OPENSSL_NO_SCRYPT
+
+#define R(a,b) (((a) << (b)) | ((a) >> (32 - (b))))
+static void salsa208_word_specification(uint32_t inout[16])
+{
+    int i;
+    uint32_t x[16];
+    memcpy(x, inout, sizeof(x));
+    for (i = 8; i > 0; i -= 2) {
+        x[4] ^= R(x[0] + x[12], 7);
+        x[8] ^= R(x[4] + x[0], 9);
+        x[12] ^= R(x[8] + x[4], 13);
+        x[0] ^= R(x[12] + x[8], 18);
+        x[9] ^= R(x[5] + x[1], 7);
+        x[13] ^= R(x[9] + x[5], 9);
+        x[1] ^= R(x[13] + x[9], 13);
+        x[5] ^= R(x[1] + x[13], 18);
+        x[14] ^= R(x[10] + x[6], 7);
+        x[2] ^= R(x[14] + x[10], 9);
+        x[6] ^= R(x[2] + x[14], 13);
+        x[10] ^= R(x[6] + x[2], 18);
+        x[3] ^= R(x[15] + x[11], 7);
+        x[7] ^= R(x[3] + x[15], 9);
+        x[11] ^= R(x[7] + x[3], 13);
+        x[15] ^= R(x[11] + x[7], 18);
+        x[1] ^= R(x[0] + x[3], 7);
+        x[2] ^= R(x[1] + x[0], 9);
+        x[3] ^= R(x[2] + x[1], 13);
+        x[0] ^= R(x[3] + x[2], 18);
+        x[6] ^= R(x[5] + x[4], 7);
+        x[7] ^= R(x[6] + x[5], 9);
+        x[4] ^= R(x[7] + x[6], 13);
+        x[5] ^= R(x[4] + x[7], 18);
+        x[11] ^= R(x[10] + x[9], 7);
+        x[8] ^= R(x[11] + x[10], 9);
+        x[9] ^= R(x[8] + x[11], 13);
+        x[10] ^= R(x[9] + x[8], 18);
+        x[12] ^= R(x[15] + x[14], 7);
+        x[13] ^= R(x[12] + x[15], 9);
+        x[14] ^= R(x[13] + x[12], 13);
+        x[15] ^= R(x[14] + x[13], 18);
+    }
+    for (i = 0; i < 16; ++i)
+        inout[i] += x[i];
+    OPENSSL_cleanse(x, sizeof(x));
+}
+
+static void scryptBlockMix(uint32_t *B_, uint32_t *B, uint64_t r)
+{
+    uint64_t i, j;
+    uint32_t X[16], *pB;
+
+    memcpy(X, B + (r * 2 - 1) * 16, sizeof(X));
+    pB = B;
+    for (i = 0; i < r * 2; i++) {
+        for (j = 0; j < 16; j++)
+            X[j] ^= *pB++;
+        salsa208_word_specification(X);
+        memcpy(B_ + (i / 2 + (i & 1) * r) * 16, X, sizeof(X));
+    }
+    OPENSSL_cleanse(X, sizeof(X));
+}
+
+static void scryptROMix(unsigned char *B, uint64_t r, uint64_t N,
+                        uint32_t *X, uint32_t *T, uint32_t *V)
+{
+    unsigned char *pB;
+    uint32_t *pV;
+    uint64_t i, k;
+
+    /* Convert from little endian input */
+    for (pV = V, i = 0, pB = B; i < 32 * r; i++, pV++) {
+        *pV = *pB++;
+        *pV |= *pB++ << 8;
+        *pV |= *pB++ << 16;
+        *pV |= (uint32_t)*pB++ << 24;
+    }
+
+    for (i = 1; i < N; i++, pV += 32 * r)
+        scryptBlockMix(pV, pV - 32 * r, r);
+
+    scryptBlockMix(X, V + (N - 1) * 32 * r, r);
+
+    for (i = 0; i < N; i++) {
+        uint32_t j;
+        j = X[16 * (2 * r - 1)] % N;
+        pV = V + 32 * r * j;
+        for (k = 0; k < 32 * r; k++)
+            T[k] = X[k] ^ *pV++;
+        scryptBlockMix(X, T, r);
+    }
+    /* Convert output to little endian */
+    for (i = 0, pB = B; i < 32 * r; i++) {
+        uint32_t xtmp = X[i];
+        *pB++ = xtmp & 0xff;
+        *pB++ = (xtmp >> 8) & 0xff;
+        *pB++ = (xtmp >> 16) & 0xff;
+        *pB++ = (xtmp >> 24) & 0xff;
+    }
+}
+
+#ifndef SIZE_MAX
+# define SIZE_MAX    ((size_t)-1)
+#endif
+
+/*
+ * Maximum power of two that will fit in uint64_t: this should work on
+ * most (all?) platforms.
+ */
+
+#define LOG2_UINT64_MAX         (sizeof(uint64_t) * 8 - 1)
+
+/*
+ * Maximum value of p * r:
+ * p <= ((2^32-1) * hLen) / MFLen =>
+ * p <= ((2^32-1) * 32) / (128 * r) =>
+ * p * r <= (2^30-1)
+ *
+ */
+
+#define SCRYPT_PR_MAX   ((1 << 30) - 1)
+
+/*
+ * Maximum permitted memory allow this to be overridden with Configuration
+ * option: e.g. -DSCRYPT_MAX_MEM=0 for maximum possible.
+ */
+
+#ifdef SCRYPT_MAX_MEM
+# if SCRYPT_MAX_MEM == 0
+#  undef SCRYPT_MAX_MEM
+/*
+ * Although we could theoretically allocate SIZE_MAX memory that would leave
+ * no memory available for anything else so set limit as half that.
+ */
+#  define SCRYPT_MAX_MEM (SIZE_MAX/2)
+# endif
+#else
+/* Default memory limit: 32 MB */
+# define SCRYPT_MAX_MEM  (1024 * 1024 * 32)
+#endif
+
+int EVP_PBE_scrypt(const char *pass, size_t passlen,
+                   const unsigned char *salt, size_t saltlen,
+                   uint64_t N, uint64_t r, uint64_t p, uint64_t maxmem,
+                   unsigned char *key, size_t keylen)
+{
+    int rv = 0;
+    unsigned char *B;
+    uint32_t *X, *V, *T;
+    uint64_t i, Blen, Vlen;
+    size_t allocsize;
+    const EVP_MD *md;
+
+#ifndef OPENSSL_NO_SHA
+    md = EVP_sha256();
+#elif !defined(OPENSSL_NO_SM3)
+    md = EVP_sm3();
+#else
+    EVPerr(EVP_F_EVP_PBE_SCRYPT, EVP_R_NO_AVAIABLE_DIGEST);
+    return 0;
+#endif
+
+    /* Sanity check parameters */
+    /* initial check, r,p must be non zero, N >= 2 and a power of 2 */
+    if (r == 0 || p == 0 || N < 2 || (N & (N - 1)))
+        return 0;
+    /* Check p * r < SCRYPT_PR_MAX avoiding overflow */
+    if (p > SCRYPT_PR_MAX / r)
+        return 0;
+
+    /*
+     * Need to check N: if 2^(128 * r / 8) overflows limit this is
+     * automatically satisfied since N <= UINT64_MAX.
+     */
+
+    if (16 * r <= LOG2_UINT64_MAX) {
+        if (N >= (((uint64_t)1) << (16 * r)))
+            return 0;
+    }
+
+    /* Memory checks: check total allocated buffer size fits in uint64_t */
+
+    /*
+     * B size in section 5 step 1.S
+     * Note: we know p * 128 * r < UINT64_MAX because we already checked
+     * p * r < SCRYPT_PR_MAX
+     */
+    Blen = p * 128 * r;
+
+    /*
+     * Check 32 * r * (N + 2) * sizeof(uint32_t) fits in
+     * uint64_t and also size_t (their sizes are unrelated).
+     * This is combined size V, X and T (section 4)
+     */
+    i = UINT64_MAX / (32 * sizeof(uint32_t));
+    if (N + 2 > i / r)
+        return 0;
+    Vlen = 32 * r * (N + 2) * sizeof(uint32_t);
+
+    /* check total allocated size fits in uint64_t */
+    if (Blen > UINT64_MAX - Vlen)
+        return 0;
+    /* check total allocated size fits in size_t */
+    if (Blen > SIZE_MAX - Vlen)
+        return 0;
+
+    allocsize = (size_t)(Blen + Vlen);
+
+    if (maxmem == 0)
+        maxmem = SCRYPT_MAX_MEM;
+
+    if (allocsize > maxmem) {
+        EVPerr(EVP_F_EVP_PBE_SCRYPT, EVP_R_MEMORY_LIMIT_EXCEEDED);
+        return 0;
+    }
+
+    /* If no key return to indicate parameters are OK */
+    if (key == NULL)
+        return 1;
+
+    B = OPENSSL_malloc(allocsize);
+    if (B == NULL)
+        return 0;
+    X = (uint32_t *)(B + Blen);
+    T = X + 32 * r;
+    V = T + 32 * r;
+    if (PKCS5_PBKDF2_HMAC(pass, passlen, salt, saltlen, 1, md,
+                          Blen, B) == 0)
+        goto err;
+
+    for (i = 0; i < p; i++)
+        scryptROMix(B + 128 * r * i, r, N, X, T, V);
+
+    if (PKCS5_PBKDF2_HMAC(pass, passlen, B, Blen, 1, md,
+                          keylen, key) == 0)
+        goto err;
+    rv = 1;
+ err:
+    OPENSSL_clear_free(B, allocsize);
+    return rv;
+}
+#endif
diff --git a/crypto/ex_data.c b/crypto/ex_data.c
new file mode 100644
index 000000000..bb1af0b3b
--- /dev/null
+++ b/crypto/ex_data.c
@@ -0,0 +1,380 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "internal/cryptlib_int.h"
+#include "internal/thread_once.h"
+#include 
+
+/*
+ * Each structure type (sometimes called a class), that supports
+ * exdata has a stack of callbacks for each instance.
+ */
+struct ex_callback_st {
+    long argl;                  /* Arbitrary long */
+    void *argp;                 /* Arbitrary void * */
+    CRYPTO_EX_new *new_func;
+    CRYPTO_EX_free *free_func;
+    CRYPTO_EX_dup *dup_func;
+};
+
+/*
+ * The state for each class.  This could just be a typedef, but
+ * a structure allows future changes.
+ */
+typedef struct ex_callbacks_st {
+    STACK_OF(EX_CALLBACK) *meth;
+} EX_CALLBACKS;
+
+static EX_CALLBACKS ex_data[CRYPTO_EX_INDEX__COUNT];
+
+static CRYPTO_RWLOCK *ex_data_lock = NULL;
+static CRYPTO_ONCE ex_data_init = CRYPTO_ONCE_STATIC_INIT;
+
+DEFINE_RUN_ONCE_STATIC(do_ex_data_init)
+{
+    OPENSSL_init_crypto(0, NULL);
+    ex_data_lock = CRYPTO_THREAD_lock_new();
+    return ex_data_lock != NULL;
+}
+
+/*
+ * Return the EX_CALLBACKS from the |ex_data| array that corresponds to
+ * a given class.  On success, *holds the lock.*
+ */
+static EX_CALLBACKS *get_and_lock(int class_index)
+{
+    EX_CALLBACKS *ip;
+
+    if (class_index < 0 || class_index >= CRYPTO_EX_INDEX__COUNT) {
+        CRYPTOerr(CRYPTO_F_GET_AND_LOCK, ERR_R_PASSED_INVALID_ARGUMENT);
+        return NULL;
+    }
+
+    if (!RUN_ONCE(&ex_data_init, do_ex_data_init)) {
+        CRYPTOerr(CRYPTO_F_GET_AND_LOCK, ERR_R_MALLOC_FAILURE);
+        return NULL;
+    }
+
+    if (ex_data_lock == NULL) {
+        /*
+         * This can happen in normal operation when using CRYPTO_mem_leaks().
+         * The CRYPTO_mem_leaks() function calls OPENSSL_cleanup() which cleans
+         * up the locks. Subsequently the BIO that CRYPTO_mem_leaks() uses gets
+         * freed, which also attempts to free the ex_data. However
+         * CRYPTO_mem_leaks() ensures that the ex_data is freed early (i.e.
+         * before OPENSSL_cleanup() is called), so if we get here we can safely
+         * ignore this operation. We just treat it as an error.
+         */
+         return NULL;
+    }
+
+    ip = &ex_data[class_index];
+    CRYPTO_THREAD_write_lock(ex_data_lock);
+    return ip;
+}
+
+static void cleanup_cb(EX_CALLBACK *funcs)
+{
+    OPENSSL_free(funcs);
+}
+
+/*
+ * Release all "ex_data" state to prevent memory leaks. This can't be made
+ * thread-safe without overhauling a lot of stuff, and shouldn't really be
+ * called under potential race-conditions anyway (it's for program shutdown
+ * after all).
+ */
+void crypto_cleanup_all_ex_data_int(void)
+{
+    int i;
+
+    for (i = 0; i < CRYPTO_EX_INDEX__COUNT; ++i) {
+        EX_CALLBACKS *ip = &ex_data[i];
+
+        sk_EX_CALLBACK_pop_free(ip->meth, cleanup_cb);
+        ip->meth = NULL;
+    }
+
+    CRYPTO_THREAD_lock_free(ex_data_lock);
+    ex_data_lock = NULL;
+}
+
+
+/*
+ * Unregister a new index by replacing the callbacks with no-ops.
+ * Any in-use instances are leaked.
+ */
+static void dummy_new(void *parent, void *ptr, CRYPTO_EX_DATA *ad, int idx,
+                     long argl, void *argp)
+{
+}
+
+static void dummy_free(void *parent, void *ptr, CRYPTO_EX_DATA *ad, int idx,
+                       long argl, void *argp)
+{
+}
+
+static int dummy_dup(CRYPTO_EX_DATA *to, const CRYPTO_EX_DATA *from,
+                     void *from_d, int idx,
+                     long argl, void *argp)
+{
+    return 0;
+}
+
+int CRYPTO_free_ex_index(int class_index, int idx)
+{
+    EX_CALLBACKS *ip = get_and_lock(class_index);
+    EX_CALLBACK *a;
+    int toret = 0;
+
+    if (ip == NULL)
+        return 0;
+    if (idx < 0 || idx >= sk_EX_CALLBACK_num(ip->meth))
+        goto err;
+    a = sk_EX_CALLBACK_value(ip->meth, idx);
+    if (a == NULL)
+        goto err;
+    a->new_func = dummy_new;
+    a->dup_func = dummy_dup;
+    a->free_func = dummy_free;
+    toret = 1;
+err:
+    CRYPTO_THREAD_unlock(ex_data_lock);
+    return toret;
+}
+
+/*
+ * Register a new index.
+ */
+int CRYPTO_get_ex_new_index(int class_index, long argl, void *argp,
+                            CRYPTO_EX_new *new_func, CRYPTO_EX_dup *dup_func,
+                            CRYPTO_EX_free *free_func)
+{
+    int toret = -1;
+    EX_CALLBACK *a;
+    EX_CALLBACKS *ip = get_and_lock(class_index);
+
+    if (ip == NULL)
+        return -1;
+
+    if (ip->meth == NULL) {
+        ip->meth = sk_EX_CALLBACK_new_null();
+        /* We push an initial value on the stack because the SSL
+         * "app_data" routines use ex_data index zero.  See RT 3710. */
+        if (ip->meth == NULL
+            || !sk_EX_CALLBACK_push(ip->meth, NULL)) {
+            CRYPTOerr(CRYPTO_F_CRYPTO_GET_EX_NEW_INDEX, ERR_R_MALLOC_FAILURE);
+            goto err;
+        }
+    }
+
+    a = (EX_CALLBACK *)OPENSSL_malloc(sizeof(*a));
+    if (a == NULL) {
+        CRYPTOerr(CRYPTO_F_CRYPTO_GET_EX_NEW_INDEX, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+    a->argl = argl;
+    a->argp = argp;
+    a->new_func = new_func;
+    a->dup_func = dup_func;
+    a->free_func = free_func;
+
+    if (!sk_EX_CALLBACK_push(ip->meth, NULL)) {
+        CRYPTOerr(CRYPTO_F_CRYPTO_GET_EX_NEW_INDEX, ERR_R_MALLOC_FAILURE);
+        OPENSSL_free(a);
+        goto err;
+    }
+    toret = sk_EX_CALLBACK_num(ip->meth) - 1;
+    (void)sk_EX_CALLBACK_set(ip->meth, toret, a);
+
+ err:
+    CRYPTO_THREAD_unlock(ex_data_lock);
+    return toret;
+}
+
+/*
+ * Initialise a new CRYPTO_EX_DATA for use in a particular class - including
+ * calling new() callbacks for each index in the class used by this variable
+ * Thread-safe by copying a class's array of "EX_CALLBACK" entries
+ * in the lock, then using them outside the lock. Note this only applies
+ * to the global "ex_data" state (ie. class definitions), not 'ad' itself.
+ */
+int CRYPTO_new_ex_data(int class_index, void *obj, CRYPTO_EX_DATA *ad)
+{
+    int mx, i;
+    void *ptr;
+    EX_CALLBACK **storage = NULL;
+    EX_CALLBACK *stack[10];
+    EX_CALLBACKS *ip = get_and_lock(class_index);
+
+    if (ip == NULL)
+        return 0;
+
+    ad->sk = NULL;
+
+    mx = sk_EX_CALLBACK_num(ip->meth);
+    if (mx > 0) {
+        if (mx < (int)OSSL_NELEM(stack))
+            storage = stack;
+        else
+            storage = OPENSSL_malloc(sizeof(*storage) * mx);
+        if (storage != NULL)
+            for (i = 0; i < mx; i++)
+                storage[i] = sk_EX_CALLBACK_value(ip->meth, i);
+    }
+    CRYPTO_THREAD_unlock(ex_data_lock);
+
+    if (mx > 0 && storage == NULL) {
+        CRYPTOerr(CRYPTO_F_CRYPTO_NEW_EX_DATA, ERR_R_MALLOC_FAILURE);
+        return 0;
+    }
+    for (i = 0; i < mx; i++) {
+        if (storage[i] && storage[i]->new_func) {
+            ptr = CRYPTO_get_ex_data(ad, i);
+            storage[i]->new_func(obj, ptr, ad, i,
+                                 storage[i]->argl, storage[i]->argp);
+        }
+    }
+    if (storage != stack)
+        OPENSSL_free(storage);
+    return 1;
+}
+
+/*
+ * Duplicate a CRYPTO_EX_DATA variable - including calling dup() callbacks
+ * for each index in the class used by this variable
+ */
+int CRYPTO_dup_ex_data(int class_index, CRYPTO_EX_DATA *to,
+                       const CRYPTO_EX_DATA *from)
+{
+    int mx, j, i;
+    char *ptr;
+    EX_CALLBACK *stack[10];
+    EX_CALLBACK **storage = NULL;
+    EX_CALLBACKS *ip;
+
+    if (from->sk == NULL)
+        /* Nothing to copy over */
+        return 1;
+    if ((ip = get_and_lock(class_index)) == NULL)
+        return 0;
+
+    mx = sk_EX_CALLBACK_num(ip->meth);
+    j = sk_void_num(from->sk);
+    if (j < mx)
+        mx = j;
+    if (mx > 0) {
+        if (mx < (int)OSSL_NELEM(stack))
+            storage = stack;
+        else
+            storage = OPENSSL_malloc(sizeof(*storage) * mx);
+        if (storage != NULL)
+            for (i = 0; i < mx; i++)
+                storage[i] = sk_EX_CALLBACK_value(ip->meth, i);
+    }
+    CRYPTO_THREAD_unlock(ex_data_lock);
+
+    if (mx > 0 && storage == NULL) {
+        CRYPTOerr(CRYPTO_F_CRYPTO_DUP_EX_DATA, ERR_R_MALLOC_FAILURE);
+        return 0;
+    }
+
+    for (i = 0; i < mx; i++) {
+        ptr = CRYPTO_get_ex_data(from, i);
+        if (storage[i] && storage[i]->dup_func)
+            storage[i]->dup_func(to, from, &ptr, i,
+                                 storage[i]->argl, storage[i]->argp);
+        CRYPTO_set_ex_data(to, i, ptr);
+    }
+    if (storage != stack)
+        OPENSSL_free(storage);
+    return 1;
+}
+
+
+/*
+ * Cleanup a CRYPTO_EX_DATA variable - including calling free() callbacks for
+ * each index in the class used by this variable
+ */
+void CRYPTO_free_ex_data(int class_index, void *obj, CRYPTO_EX_DATA *ad)
+{
+    int mx, i;
+    EX_CALLBACKS *ip;
+    void *ptr;
+    EX_CALLBACK *stack[10];
+    EX_CALLBACK **storage = NULL;
+
+    if ((ip = get_and_lock(class_index)) == NULL)
+        return;
+
+    mx = sk_EX_CALLBACK_num(ip->meth);
+    if (mx > 0) {
+        if (mx < (int)OSSL_NELEM(stack))
+            storage = stack;
+        else
+            storage = OPENSSL_malloc(sizeof(*storage) * mx);
+        if (storage != NULL)
+            for (i = 0; i < mx; i++)
+                storage[i] = sk_EX_CALLBACK_value(ip->meth, i);
+    }
+    CRYPTO_THREAD_unlock(ex_data_lock);
+
+    if (mx > 0 && storage == NULL) {
+        CRYPTOerr(CRYPTO_F_CRYPTO_FREE_EX_DATA, ERR_R_MALLOC_FAILURE);
+        return;
+    }
+    for (i = 0; i < mx; i++) {
+        if (storage[i] && storage[i]->free_func) {
+            ptr = CRYPTO_get_ex_data(ad, i);
+            storage[i]->free_func(obj, ptr, ad, i,
+                                  storage[i]->argl, storage[i]->argp);
+        }
+    }
+
+    if (storage != stack)
+        OPENSSL_free(storage);
+    sk_void_free(ad->sk);
+    ad->sk = NULL;
+}
+
+/*
+ * For a given CRYPTO_EX_DATA variable, set the value corresponding to a
+ * particular index in the class used by this variable
+ */
+int CRYPTO_set_ex_data(CRYPTO_EX_DATA *ad, int idx, void *val)
+{
+    int i;
+
+    if (ad->sk == NULL) {
+        if ((ad->sk = sk_void_new_null()) == NULL) {
+            CRYPTOerr(CRYPTO_F_CRYPTO_SET_EX_DATA, ERR_R_MALLOC_FAILURE);
+            return 0;
+        }
+    }
+
+    for (i = sk_void_num(ad->sk); i <= idx; ++i) {
+        if (!sk_void_push(ad->sk, NULL)) {
+            CRYPTOerr(CRYPTO_F_CRYPTO_SET_EX_DATA, ERR_R_MALLOC_FAILURE);
+            return 0;
+        }
+    }
+    sk_void_set(ad->sk, idx, val);
+    return 1;
+}
+
+/*
+ * For a given CRYPTO_EX_DATA_ variable, get the value corresponding to a
+ * particular index in the class used by this variable
+ */
+void *CRYPTO_get_ex_data(const CRYPTO_EX_DATA *ad, int idx)
+{
+    if (ad->sk == NULL || idx >= sk_void_num(ad->sk))
+        return NULL;
+    return sk_void_value(ad->sk, idx);
+}
diff --git a/crypto/gmapi/build.info b/crypto/gmapi/build.info
new file mode 100644
index 000000000..ef7a7b7f3
--- /dev/null
+++ b/crypto/gmapi/build.info
@@ -0,0 +1,3 @@
+LIBS=../../libcrypto
+SOURCE[../../libcrypto]=gmapi_err.c gmapi_sgd.c \
+	gmapi_sdf_ec.c gmapi_sdf_rsa.c gmapi_skf_ec.c gmapi_skf_rsa.c
diff --git a/crypto/gmapi/gmapi_err.c b/crypto/gmapi/gmapi_err.c
new file mode 100644
index 000000000..2fae03f2b
--- /dev/null
+++ b/crypto/gmapi/gmapi_err.c
@@ -0,0 +1,181 @@
+/*
+ * Generated by util/mkerr.pl DO NOT EDIT
+ * Copyright 1995-2017 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include 
+
+/* BEGIN ERROR CODES */
+#ifndef OPENSSL_NO_ERR
+
+# define ERR_FUNC(func) ERR_PACK(ERR_LIB_GMAPI,func,0)
+# define ERR_REASON(reason) ERR_PACK(ERR_LIB_GMAPI,0,reason)
+
+static ERR_STRING_DATA GMAPI_str_functs[] = {
+    {ERR_FUNC(GMAPI_F_D2I_ECCCIPHER), "d2i_ECCCipher"},
+    {ERR_FUNC(GMAPI_F_D2I_ECCCIPHERBLOB), "d2i_ECCCIPHERBLOB"},
+    {ERR_FUNC(GMAPI_F_D2I_ECCCIPHERBLOB_BIO), "d2i_ECCCIPHERBLOB_bio"},
+    {ERR_FUNC(GMAPI_F_D2I_ECCCIPHERBLOB_FP), "d2i_ECCCIPHERBLOB_fp"},
+    {ERR_FUNC(GMAPI_F_D2I_ECCSIGNATURE), "d2i_ECCSignature"},
+    {ERR_FUNC(GMAPI_F_D2I_ECCSIGNATUREBLOB), "d2i_ECCSIGNATUREBLOB"},
+    {ERR_FUNC(GMAPI_F_D2I_ECCSIGNATUREBLOB_BIO), "d2i_ECCSIGNATUREBLOB_bio"},
+    {ERR_FUNC(GMAPI_F_D2I_ECCSIGNATUREBLOB_FP), "d2i_ECCSIGNATUREBLOB_fp"},
+    {ERR_FUNC(GMAPI_F_ECCPRIVATEKEYBLOB_SET_PRIVATE_KEY),
+     "ECCPRIVATEKEYBLOB_set_private_key"},
+    {ERR_FUNC(GMAPI_F_ECDSA_SIG_GET_ECCSIGNATURE),
+     "ECDSA_SIG_get_ECCSignature"},
+    {ERR_FUNC(GMAPI_F_ECDSA_SIG_GET_ECCSIGNATUREBLOB),
+     "ECDSA_SIG_get_ECCSIGNATUREBLOB"},
+    {ERR_FUNC(GMAPI_F_ECDSA_SIG_NEW_FROM_ECCSIGNATURE),
+     "ECDSA_SIG_new_from_ECCSignature"},
+    {ERR_FUNC(GMAPI_F_ECDSA_SIG_NEW_FROM_ECCSIGNATUREBLOB),
+     "ECDSA_SIG_new_from_ECCSIGNATUREBLOB"},
+    {ERR_FUNC(GMAPI_F_ECDSA_SIG_SET_ECCSIGNATURE),
+     "ECDSA_SIG_set_ECCSignature"},
+    {ERR_FUNC(GMAPI_F_ECDSA_SIG_SET_ECCSIGNATUREBLOB),
+     "ECDSA_SIG_set_ECCSIGNATUREBLOB"},
+    {ERR_FUNC(GMAPI_F_ECIES_CIPHERTEXT_VALUE_GET_ECCCIPHER),
+     "ECIES_CIPHERTEXT_VALUE_get_ECCCipher"},
+    {ERR_FUNC(GMAPI_F_ECIES_CIPHERTEXT_VALUE_GET_ECCCIPHERBLOB),
+     "ECIES_CIPHERTEXT_VALUE_get_ECCCIPHERBLOB"},
+    {ERR_FUNC(GMAPI_F_ECIES_CIPHERTEXT_VALUE_NEW_FROM_ECCCIPHER),
+     "ECIES_CIPHERTEXT_VALUE_new_from_ECCCipher"},
+    {ERR_FUNC(GMAPI_F_ECIES_CIPHERTEXT_VALUE_NEW_FROM_ECCCIPHERBLOB),
+     "ECIES_CIPHERTEXT_VALUE_new_from_ECCCIPHERBLOB"},
+    {ERR_FUNC(GMAPI_F_ECIES_CIPHERTEXT_VALUE_SET_ECCCIPHER),
+     "ECIES_CIPHERTEXT_VALUE_set_ECCCipher"},
+    {ERR_FUNC(GMAPI_F_ECIES_CIPHERTEXT_VALUE_SET_ECCCIPHERBLOB),
+     "ECIES_CIPHERTEXT_VALUE_set_ECCCIPHERBLOB"},
+    {ERR_FUNC(GMAPI_F_EC_KEY_GET_ECCPRIVATEKEYBLOB),
+     "EC_KEY_get_ECCPRIVATEKEYBLOB"},
+    {ERR_FUNC(GMAPI_F_EC_KEY_GET_ECCPUBLICKEYBLOB),
+     "EC_KEY_get_ECCPUBLICKEYBLOB"},
+    {ERR_FUNC(GMAPI_F_EC_KEY_GET_ECCREFPRIVATEKEY),
+     "EC_KEY_get_ECCrefPrivateKey"},
+    {ERR_FUNC(GMAPI_F_EC_KEY_GET_ECCREFPUBLICKEY),
+     "EC_KEY_get_ECCrefPublicKey"},
+    {ERR_FUNC(GMAPI_F_EC_KEY_NEW_FROM_ECCPRIVATEKEYBLOB),
+     "EC_KEY_new_from_ECCPRIVATEKEYBLOB"},
+    {ERR_FUNC(GMAPI_F_EC_KEY_NEW_FROM_ECCPUBLICKEYBLOB),
+     "EC_KEY_new_from_ECCPUBLICKEYBLOB"},
+    {ERR_FUNC(GMAPI_F_EC_KEY_NEW_FROM_ECCREFPRIVATEKEY),
+     "EC_KEY_new_from_ECCrefPrivateKey"},
+    {ERR_FUNC(GMAPI_F_EC_KEY_NEW_FROM_ECCREFPUBLICKEY),
+     "EC_KEY_new_from_ECCrefPublicKey"},
+    {ERR_FUNC(GMAPI_F_EC_KEY_SET_ECCPRIVATEKEYBLOB),
+     "EC_KEY_set_ECCPRIVATEKEYBLOB"},
+    {ERR_FUNC(GMAPI_F_EC_KEY_SET_ECCPUBLICKEYBLOB),
+     "EC_KEY_set_ECCPUBLICKEYBLOB"},
+    {ERR_FUNC(GMAPI_F_EC_KEY_SET_ECCREFPRIVATEKEY),
+     "EC_KEY_set_ECCrefPrivateKey"},
+    {ERR_FUNC(GMAPI_F_EC_KEY_SET_ECCREFPUBLICKEY),
+     "EC_KEY_set_ECCrefPublicKey"},
+    {ERR_FUNC(GMAPI_F_EVP_CIPHER_CTX_GET_SGD), "EVP_CIPHER_CTX_get_sgd"},
+    {ERR_FUNC(GMAPI_F_EVP_CIPHER_GET_SGD), "EVP_CIPHER_get_sgd"},
+    {ERR_FUNC(GMAPI_F_EVP_MD_GET_SGD), "EVP_MD_get_sgd"},
+    {ERR_FUNC(GMAPI_F_EVP_PKEY_GET_SGD), "EVP_PKEY_get_sgd"},
+    {ERR_FUNC(GMAPI_F_I2D_ECCCIPHER), "i2d_ECCCipher"},
+    {ERR_FUNC(GMAPI_F_I2D_ECCCIPHERBLOB), "i2d_ECCCIPHERBLOB"},
+    {ERR_FUNC(GMAPI_F_I2D_ECCCIPHERBLOB_BIO), "i2d_ECCCIPHERBLOB_bio"},
+    {ERR_FUNC(GMAPI_F_I2D_ECCCIPHERBLOB_FP), "i2d_ECCCIPHERBLOB_fp"},
+    {ERR_FUNC(GMAPI_F_I2D_ECCSIGNATURE), "i2d_ECCSignature"},
+    {ERR_FUNC(GMAPI_F_I2D_ECCSIGNATUREBLOB), "i2d_ECCSIGNATUREBLOB"},
+    {ERR_FUNC(GMAPI_F_I2D_ECCSIGNATUREBLOB_BIO), "i2d_ECCSIGNATUREBLOB_bio"},
+    {ERR_FUNC(GMAPI_F_I2D_ECCSIGNATUREBLOB_FP), "i2d_ECCSIGNATUREBLOB_fp"},
+    {ERR_FUNC(GMAPI_F_RSA_GET_RSAPRIVATEKEYBLOB),
+     "RSA_get_RSAPRIVATEKEYBLOB"},
+    {ERR_FUNC(GMAPI_F_RSA_GET_RSAPUBLICKEYBLOB), "RSA_get_RSAPUBLICKEYBLOB"},
+    {ERR_FUNC(GMAPI_F_RSA_GET_RSAREFPRIVATEKEY), "RSA_get_RSArefPrivateKey"},
+    {ERR_FUNC(GMAPI_F_RSA_GET_RSAREFPUBLICKEY), "RSA_get_RSArefPublicKey"},
+    {ERR_FUNC(GMAPI_F_RSA_NEW_FROM_RSAPRIVATEKEYBLOB),
+     "RSA_new_from_RSAPRIVATEKEYBLOB"},
+    {ERR_FUNC(GMAPI_F_RSA_NEW_FROM_RSAPUBLICKEYBLOB),
+     "RSA_new_from_RSAPUBLICKEYBLOB"},
+    {ERR_FUNC(GMAPI_F_RSA_NEW_FROM_RSAREFPRIVATEKEY),
+     "RSA_new_from_RSArefPrivateKey"},
+    {ERR_FUNC(GMAPI_F_RSA_NEW_FROM_RSAREFPUBLICKEY),
+     "RSA_new_from_RSArefPublicKey"},
+    {ERR_FUNC(GMAPI_F_RSA_SET_RSAPRIVATEKEYBLOB),
+     "RSA_set_RSAPRIVATEKEYBLOB"},
+    {ERR_FUNC(GMAPI_F_RSA_SET_RSAPUBLICKEYBLOB), "RSA_set_RSAPUBLICKEYBLOB"},
+    {ERR_FUNC(GMAPI_F_RSA_SET_RSAREFPRIVATEKEY), "RSA_set_RSArefPrivateKey"},
+    {ERR_FUNC(GMAPI_F_RSA_SET_RSAREFPUBLICKEY), "RSA_set_RSArefPublicKey"},
+    {ERR_FUNC(GMAPI_F_SM2CIPHERTEXTVALUE_GET_ECCCIPHER),
+     "SM2CiphertextValue_get_ECCCipher"},
+    {ERR_FUNC(GMAPI_F_SM2CIPHERTEXTVALUE_GET_ECCCIPHERBLOB),
+     "SM2CiphertextValue_get_ECCCIPHERBLOB"},
+    {ERR_FUNC(GMAPI_F_SM2CIPHERTEXTVALUE_NEW_FROM_ECCCIPHER),
+     "SM2CiphertextValue_new_from_ECCCipher"},
+    {ERR_FUNC(GMAPI_F_SM2CIPHERTEXTVALUE_NEW_FROM_ECCCIPHERBLOB),
+     "SM2CiphertextValue_new_from_ECCCIPHERBLOB"},
+    {ERR_FUNC(GMAPI_F_SM2CIPHERTEXTVALUE_SET_ECCCIPHER),
+     "SM2CiphertextValue_set_ECCCipher"},
+    {ERR_FUNC(GMAPI_F_SM2CIPHERTEXTVALUE_SET_ECCCIPHERBLOB),
+     "SM2CiphertextValue_set_ECCCIPHERBLOB"},
+    {0, NULL}
+};
+
+static ERR_STRING_DATA GMAPI_str_reasons[] = {
+    {ERR_REASON(GMAPI_R_BUFFER_TOO_SMALL), "buffer too small"},
+    {ERR_REASON(GMAPI_R_DECODE_EC_PRIVATE_KEY_FAILED),
+     "decode ec private key failed"},
+    {ERR_REASON(GMAPI_R_DECODE_EC_PUBLIC_KEY_FAILED),
+     "decode ec public key failed"},
+    {ERR_REASON(GMAPI_R_ENCODE_RSA_PUBLIC_KEY_FAILED),
+     "encode rsa public key failed"},
+    {ERR_REASON(GMAPI_R_INVALID_ALGOR), "invalid algor"},
+    {ERR_REASON(GMAPI_R_INVALID_BIGNUM_LENGTH), "invalid bignum length"},
+    {ERR_REASON(GMAPI_R_INVALID_CIPHERTEXT_LENGTH),
+     "invalid ciphertext length"},
+    {ERR_REASON(GMAPI_R_INVALID_CIPHERTEXT_MAC), "invalid ciphertext mac"},
+    {ERR_REASON(GMAPI_R_INVALID_CIPHERTEXT_POINT),
+     "invalid ciphertext point"},
+    {ERR_REASON(GMAPI_R_INVALID_CIPHETEXT_LENGTH),
+     "invalid ciphetext length"},
+    {ERR_REASON(GMAPI_R_INVALID_EC_KEY), "invalid ec key"},
+    {ERR_REASON(GMAPI_R_INVALID_EC_PRIVATE_KEY), "invalid ec private key"},
+    {ERR_REASON(GMAPI_R_INVALID_EC_PUBLIC_KEY), "invalid ec public key"},
+    {ERR_REASON(GMAPI_R_INVALID_KEY_LENGTH), "invalid key length"},
+    {ERR_REASON(GMAPI_R_INVALID_PRIVATE_KEY), "invalid private key"},
+    {ERR_REASON(GMAPI_R_INVALID_PUBLIC_KEY), "invalid public key"},
+    {ERR_REASON(GMAPI_R_INVALID_RSA_KEY_LENGTH), "invalid rsa key length"},
+    {ERR_REASON(GMAPI_R_INVALID_RSA_PRIVATE_KEY), "invalid rsa private key"},
+    {ERR_REASON(GMAPI_R_INVALID_RSA_PUBLIC_KEY), "invalid rsa public key"},
+    {ERR_REASON(GMAPI_R_INVALID_SIGNATURE), "invalid signature"},
+    {ERR_REASON(GMAPI_R_INVALID_SKF_CIPHERTEXT), "invalid skf ciphertext"},
+    {ERR_REASON(GMAPI_R_INVALID_SKF_EC_CIPHERTEXT),
+     "invalid skf ec ciphertext"},
+    {ERR_REASON(GMAPI_R_INVALID_SM2_CIPHERTEXT), "invalid sm2 ciphertext"},
+    {ERR_REASON(GMAPI_R_INVALID_SM2_CIPHERTEXT_MAC_LENGTH),
+     "invalid sm2 ciphertext mac length"},
+    {ERR_REASON(GMAPI_R_INVALID_SM2_PRIVATE_KEY), "invalid sm2 private key"},
+    {ERR_REASON(GMAPI_R_INVALID_SM2_SIGNATURE), "invalid sm2 signature"},
+    {ERR_REASON(GMAPI_R_MALLOC_FAILED), "malloc failed"},
+    {ERR_REASON(GMAPI_R_NOT_CONVERTABLE), "not convertable"},
+    {ERR_REASON(GMAPI_R_NOT_IMPLEMENTED), "not implemented"},
+    {ERR_REASON(GMAPI_R_NOT_SUPPORTED_GMAPI_CIPHER),
+     "not supported gmapi cipher"},
+    {ERR_REASON(GMAPI_R_NOT_SUPPORTED_PKEY), "not supported pkey"},
+    {0, NULL}
+};
+
+#endif
+
+int ERR_load_GMAPI_strings(void)
+{
+#ifndef OPENSSL_NO_ERR
+
+    if (ERR_func_error_string(GMAPI_str_functs[0].error) == NULL) {
+        ERR_load_strings(0, GMAPI_str_functs);
+        ERR_load_strings(0, GMAPI_str_reasons);
+    }
+#endif
+    return 1;
+}
diff --git a/crypto/gmapi/gmapi_sdf_ec.c b/crypto/gmapi/gmapi_sdf_ec.c
new file mode 100644
index 000000000..908eca754
--- /dev/null
+++ b/crypto/gmapi/gmapi_sdf_ec.c
@@ -0,0 +1,857 @@
+/* ====================================================================
+ * Copyright (c) 2016 The GmSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the GmSSL Project.
+ *    (http://gmssl.org/)"
+ *
+ * 4. The name "GmSSL Project" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    guanzhi1980@gmail.com.
+ *
+ * 5. Products derived from this software may not be called "GmSSL"
+ *    nor may "GmSSL" appear in their names without prior written
+ *    permission of the GmSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the GmSSL Project
+ *    (http://gmssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE GmSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE GmSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+
+/* put these functions under name `gmapi`, even if in the future the SDF
+ * implementation is organized into a standalone sub-library, we still need
+ * some convert functions between native types and the SDF types. So we can
+ * mix this with SDF engine without relying on the `sdf` sub-library
+ *
+ * Attension: as the functions in this file can be seen as the native
+ * functions of GmSSL/OpenSSL, so the return values of these functions will
+ * follow the convention of OpenSSL, return 1 for success and 0 for error.
+ * This is different from SAF/SDF/SKF where return 0 means success.
+ */
+
+#include 
+#include "internal/cryptlib.h"
+
+#if !defined(OPENSSL_NO_SDF) && !defined(OPENSSL_NO_EC)
+# include 
+# include 
+# include 
+# include 
+# include 
+# include 
+# include "../sm2/sm2_lcl.h"
+# include "../ecies/ecies_lcl.h"
+
+
+EC_KEY *EC_KEY_new_from_ECCrefPublicKey(const ECCrefPublicKey *ref)
+{
+	EC_KEY *ret;
+
+	if (!ref) {
+		GMAPIerr(GMAPI_F_EC_KEY_NEW_FROM_ECCREFPUBLICKEY,
+			ERR_R_PASSED_NULL_PARAMETER);
+		return NULL;
+	}
+
+	if (!(ret = EC_KEY_new_by_curve_name(NID_sm2p256v1))) {
+		GMAPIerr(GMAPI_F_EC_KEY_NEW_FROM_ECCREFPUBLICKEY, ERR_R_EC_LIB);
+		return NULL;
+	}
+
+	if (!EC_KEY_set_ECCrefPublicKey(ret, ref)) {
+		GMAPIerr(GMAPI_F_EC_KEY_NEW_FROM_ECCREFPUBLICKEY,
+			GMAPI_R_DECODE_EC_PUBLIC_KEY_FAILED);
+		EC_KEY_free(ret);
+		return NULL;
+	}
+
+	return ret;
+}
+
+int EC_KEY_set_ECCrefPublicKey(EC_KEY *ec_key, const ECCrefPublicKey *ref)
+{
+	int ret = 0;
+	BIGNUM *x = NULL;
+	BIGNUM *y = NULL;
+	int nbytes;
+
+	/* check arguments */
+	if (!ec_key || !ref) {
+		GMAPIerr(GMAPI_F_EC_KEY_SET_ECCREFPUBLICKEY,
+			ERR_R_PASSED_NULL_PARAMETER);
+		return 0;
+	}
+	if ((int)ref->bits != EC_GROUP_get_degree(EC_KEY_get0_group(ec_key))) {
+		GMAPIerr(GMAPI_F_EC_KEY_SET_ECCREFPUBLICKEY,
+			GMAPI_R_INVALID_KEY_LENGTH);
+		return 0;
+	}
+
+	/* ECCrefPublicKey ==> EC_KEY */
+	nbytes = (ref->bits + 7)/8;
+
+	if (!(x = BN_bin2bn(ref->x + ECCref_MAX_LEN - nbytes, nbytes, NULL))) {
+		GMAPIerr(GMAPI_F_EC_KEY_SET_ECCREFPUBLICKEY, ERR_R_BN_LIB);
+		goto end;
+	}
+	if (!(y = BN_bin2bn(ref->y + ECCref_MAX_LEN - nbytes, nbytes, NULL))) {
+		GMAPIerr(GMAPI_F_EC_KEY_SET_ECCREFPUBLICKEY, ERR_R_BN_LIB);
+		goto end;
+	}
+	if (!EC_KEY_set_public_key_affine_coordinates(ec_key, x, y)) {
+		GMAPIerr(GMAPI_F_EC_KEY_SET_ECCREFPUBLICKEY,
+			GMAPI_R_INVALID_PUBLIC_KEY);
+		goto end;
+	}
+
+	ret = 1;
+end:
+	BN_free(x);
+	BN_free(y);
+	return ret;
+}
+
+int EC_KEY_get_ECCrefPublicKey(EC_KEY *ec_key, ECCrefPublicKey *ref)
+{
+	int ret = 0;
+	BN_CTX *bn_ctx = NULL;
+	const EC_GROUP *group = EC_KEY_get0_group(ec_key);
+	const EC_POINT *point = EC_KEY_get0_public_key(ec_key);
+	BIGNUM *x;
+	BIGNUM *y;
+
+	/* check arguments */
+	if (!ec_key || !ref) {
+		GMAPIerr(GMAPI_F_EC_KEY_GET_ECCREFPUBLICKEY,
+			ERR_R_PASSED_NULL_PARAMETER);
+		return 0;
+	}
+
+	/* prepare */
+	if (!(bn_ctx = BN_CTX_new())) {
+		GMAPIerr(GMAPI_F_EC_KEY_GET_ECCREFPUBLICKEY, ERR_R_BN_LIB);
+		goto end;
+	}
+	BN_CTX_start(bn_ctx);
+	x = BN_CTX_get(bn_ctx);
+	y = BN_CTX_get(bn_ctx);
+	if (!x || !y) {
+		GMAPIerr(GMAPI_F_EC_KEY_GET_ECCREFPUBLICKEY, ERR_R_BN_LIB);
+		goto end;
+	}
+
+	if (EC_GROUP_get_degree(group) > ECCref_MAX_BITS) {
+		GMAPIerr(GMAPI_F_EC_KEY_GET_ECCREFPUBLICKEY,
+			GMAPI_R_INVALID_KEY_LENGTH);
+		goto end;
+	}
+
+	if (EC_METHOD_get_field_type(EC_GROUP_method_of(group)) == NID_X9_62_prime_field) {
+		if (!EC_POINT_get_affine_coordinates_GFp(group, point, x, y, bn_ctx)) {
+			GMAPIerr(GMAPI_F_EC_KEY_GET_ECCREFPUBLICKEY, ERR_R_EC_LIB);
+			goto end;
+		}
+	} else  {
+		if (!EC_POINT_get_affine_coordinates_GF2m(group, point, x, y, bn_ctx)) {
+			GMAPIerr(GMAPI_F_EC_KEY_GET_ECCREFPUBLICKEY, ERR_R_EC_LIB);
+			goto end;
+		}
+	}
+
+	/* EC_KEY ==> ECCrefPublicKey */
+	memset(ref, 0, sizeof(*ref));
+	ref->bits = EC_GROUP_get_degree(group);
+	if (!BN_bn2bin(x, ref->x + ECCref_MAX_LEN - BN_num_bytes(x))) {
+		GMAPIerr(GMAPI_F_EC_KEY_GET_ECCREFPUBLICKEY, ERR_R_BN_LIB);
+		goto end;
+	}
+	if (!BN_bn2bin(y, ref->y + ECCref_MAX_LEN - BN_num_bytes(y))) {
+		GMAPIerr(GMAPI_F_EC_KEY_GET_ECCREFPUBLICKEY, ERR_R_BN_LIB);
+		goto end;
+	}
+
+	ret = 1;
+end:
+	if (bn_ctx) {
+		BN_CTX_end(bn_ctx);
+	}
+	BN_CTX_free(bn_ctx);
+	return ret;
+}
+
+EC_KEY *EC_KEY_new_from_ECCrefPrivateKey(const ECCrefPrivateKey *ref)
+{
+	EC_KEY *ret;
+
+	if (!(ret = EC_KEY_new_by_curve_name(NID_sm2p256v1))) {
+		GMAPIerr(GMAPI_F_EC_KEY_NEW_FROM_ECCREFPRIVATEKEY, ERR_R_EC_LIB);
+		return NULL;
+	}
+
+	if (!EC_KEY_set_ECCrefPrivateKey(ret, ref)) {
+		GMAPIerr(GMAPI_F_EC_KEY_NEW_FROM_ECCREFPRIVATEKEY,
+			GMAPI_R_DECODE_EC_PRIVATE_KEY_FAILED);
+		EC_KEY_free(ret);
+		return NULL;
+	}
+
+	return ret;
+}
+
+int EC_KEY_set_ECCrefPrivateKey(EC_KEY *ec_key, const ECCrefPrivateKey *ref)
+{
+	int ret = 0;
+	BIGNUM *d = NULL;
+
+	if (!ec_key || !ref) {
+		GMAPIerr(GMAPI_F_EC_KEY_SET_ECCREFPRIVATEKEY,
+			ERR_R_PASSED_NULL_PARAMETER);
+		return 0;
+	}
+
+	if ((int)ref->bits != EC_GROUP_get_degree(EC_KEY_get0_group(ec_key))) {
+		GMAPIerr(GMAPI_F_EC_KEY_SET_ECCREFPRIVATEKEY,
+			GMAPI_R_INVALID_KEY_LENGTH);
+		goto end;
+	}
+
+	if (!(d = BN_bin2bn(ref->K, ECCref_MAX_LEN, NULL))) {
+		GMAPIerr(GMAPI_F_EC_KEY_SET_ECCREFPRIVATEKEY, ERR_R_BN_LIB);
+		goto end;
+	}
+	if (!EC_KEY_set_private_key(ec_key, d)) {
+		GMAPIerr(GMAPI_F_EC_KEY_SET_ECCREFPRIVATEKEY,
+			GMAPI_R_INVALID_PRIVATE_KEY);
+		goto end;
+	}
+
+	ret = 1;
+end:
+	BN_clear_free(d);
+	return ret;
+}
+
+int EC_KEY_get_ECCrefPrivateKey(EC_KEY *ec_key, ECCrefPrivateKey *ref)
+{
+	const EC_GROUP *group;
+	const BIGNUM *sk;
+
+	/* check arguments */
+	if (!ec_key || !ref) {
+		GMAPIerr(GMAPI_F_EC_KEY_GET_ECCREFPRIVATEKEY,
+			ERR_R_PASSED_NULL_PARAMETER);
+		return 0;
+	}
+
+	group = EC_KEY_get0_group(ec_key);
+	sk = EC_KEY_get0_private_key(ec_key);
+
+	if (!group || !sk) {
+		GMAPIerr(GMAPI_F_EC_KEY_GET_ECCREFPRIVATEKEY,
+			GMAPI_R_INVALID_EC_PRIVATE_KEY);
+		return 0;
+	}
+
+	if (EC_GROUP_get_degree(group) > ECCref_MAX_BITS) {
+		GMAPIerr(GMAPI_F_EC_KEY_GET_ECCREFPRIVATEKEY,
+			GMAPI_R_INVALID_KEY_LENGTH);
+		return 0;
+	}
+
+	/* EC_KEY ==> ECCrefPrivateKey */
+	memset(ref, 0, sizeof(*ref));
+
+	ref->bits = EC_GROUP_get_degree(group);
+
+	if (!BN_bn2bin(sk, ref->K + sizeof(ref->K) - BN_num_bytes(sk))) {
+		GMAPIerr(GMAPI_F_EC_KEY_GET_ECCREFPRIVATEKEY, ERR_R_BN_LIB);
+		return 0;
+	}
+
+	return 1;
+}
+
+# ifndef OPENSSL_NO_SM2
+SM2CiphertextValue *SM2CiphertextValue_new_from_ECCCipher(const ECCCipher *ref)
+{
+	SM2CiphertextValue *ret = NULL;
+	SM2CiphertextValue *cv = NULL;
+
+	/* check arguments */
+	if (!ref) {
+		GMAPIerr(GMAPI_F_SM2CIPHERTEXTVALUE_NEW_FROM_ECCCIPHER,
+			ERR_R_PASSED_NULL_PARAMETER);
+		return NULL;
+	}
+	if (ref->L > INT_MAX) {
+		GMAPIerr(GMAPI_F_SM2CIPHERTEXTVALUE_NEW_FROM_ECCCIPHER,
+			GMAPI_R_INVALID_CIPHETEXT_LENGTH);
+		return NULL;
+	}
+
+	/* ECCCipher => SM2CiphertextValue */
+	if (!(cv = SM2CiphertextValue_new())) {
+		GMAPIerr(GMAPI_F_SM2CIPHERTEXTVALUE_NEW_FROM_ECCCIPHER,
+			GMAPI_R_MALLOC_FAILED);
+		goto end;
+	}
+
+	if (!SM2CiphertextValue_set_ECCCipher(cv, ref)) {
+		GMAPIerr(GMAPI_F_SM2CIPHERTEXTVALUE_NEW_FROM_ECCCIPHER,
+			GMAPI_R_INVALID_SM2_CIPHERTEXT);
+		goto end;
+	}
+
+	ret = cv;
+	cv = NULL;
+
+end:
+	SM2CiphertextValue_free(cv);
+	return ret;
+}
+
+int SM2CiphertextValue_set_ECCCipher(SM2CiphertextValue *cv,
+	const ECCCipher *ref)
+{
+	int ret = 0;
+
+	/* check arguments */
+	if (!cv || !ref) {
+		GMAPIerr(GMAPI_F_SM2CIPHERTEXTVALUE_SET_ECCCIPHER,
+			ERR_R_PASSED_NULL_PARAMETER);
+		return 0;
+	}
+
+	/* ECCCipher ==> SM2CiphertextValue */
+	if (!BN_bin2bn(ref->x, ECCref_MAX_LEN, cv->xCoordinate)) {
+		GMAPIerr(GMAPI_F_SM2CIPHERTEXTVALUE_SET_ECCCIPHER,
+			ERR_R_BN_LIB);
+		goto end;
+	}
+	if (!BN_bin2bn(ref->y, ECCref_MAX_LEN, cv->yCoordinate)) {
+		GMAPIerr(GMAPI_F_SM2CIPHERTEXTVALUE_SET_ECCCIPHER,
+			ERR_R_BN_LIB);
+		goto end;
+	}
+
+	if (!ASN1_OCTET_STRING_set(cv->hash, ref->M, 32)) {
+		goto end;
+	}
+
+	if (ref->L <= 0 || ref->L > INT_MAX) {
+		GMAPIerr(GMAPI_F_SM2CIPHERTEXTVALUE_SET_ECCCIPHER,
+			GMAPI_R_INVALID_CIPHERTEXT_LENGTH);
+		goto end;
+	}
+	if (!ASN1_OCTET_STRING_set(cv->ciphertext, ref->C, ref->L)) {
+		goto end;
+	}
+
+
+	/* set return value */
+	ret = 1;
+
+end:
+	return ret;
+}
+
+int SM2CiphertextValue_get_ECCCipher(const SM2CiphertextValue *cv,
+	ECCCipher *ref)
+{
+	int ret = 0;
+
+	/* check arguments */
+	if (!cv || !ref) {
+		GMAPIerr(GMAPI_F_SM2CIPHERTEXTVALUE_GET_ECCCIPHER,
+			ERR_R_PASSED_NULL_PARAMETER);
+		return 0;
+	}
+
+	/* as the `ECCCipher->C[1]` default size is too small, we have to
+	 * check `ECCCipher->L` to make sure caller has initialized this
+	 * structure and prepared enough buffer to hold variable length
+	 * ciphertext
+	 */
+	if (ref->L < (unsigned int)ASN1_STRING_length(cv->ciphertext)) {
+		GMAPIerr(GMAPI_F_SM2CIPHERTEXTVALUE_GET_ECCCIPHER,
+			GMAPI_R_BUFFER_TOO_SMALL);
+		return 0;
+	}
+
+	/*
+	 * check compatible of SM2CiphertextValue with EC_GROUP
+	 * In gmapi we only do simple checks, i.e. length of coordinates.
+	 * We assume that more checks, such as x, y in the range of [1, p]
+	 * and other semantic checks should be done by the `sm2` module.
+	 */
+	if (BN_num_bytes(cv->xCoordinate) > ECCref_MAX_LEN
+		|| BN_num_bytes(cv->yCoordinate) > ECCref_MAX_LEN) {
+		GMAPIerr(GMAPI_F_SM2CIPHERTEXTVALUE_GET_ECCCIPHER,
+			GMAPI_R_INVALID_CIPHERTEXT_POINT);
+		goto end;
+	}
+
+	/* SM2CiphertextValue ==> ECCCipher */
+	memset(ref, 0, sizeof(*ref));
+
+	if (!BN_bn2bin(cv->xCoordinate,
+		ref->x + ECCref_MAX_LEN - BN_num_bytes(cv->xCoordinate))) {
+		GMAPIerr(GMAPI_F_SM2CIPHERTEXTVALUE_GET_ECCCIPHER,
+			ERR_R_BN_LIB);
+		goto end;
+	}
+
+	if (!BN_bn2bin(cv->yCoordinate,
+		ref->y + ECCref_MAX_LEN - BN_num_bytes(cv->yCoordinate))) {
+		GMAPIerr(GMAPI_F_SM2CIPHERTEXTVALUE_GET_ECCCIPHER,
+			ERR_R_BN_LIB);
+		goto end;
+	}
+
+	/* encode mac `ECCCipher->M[32]` */
+	if (ASN1_STRING_length(cv->hash) != 32) {
+		GMAPIerr(GMAPI_F_SM2CIPHERTEXTVALUE_GET_ECCCIPHER,
+			GMAPI_R_INVALID_CIPHERTEXT_MAC);
+		goto end;
+	}
+	memcpy(ref->M, ASN1_STRING_get0_data(cv->hash),
+		ASN1_STRING_length(cv->hash));
+
+	/* encode ciphertext `ECCCipher->L`, `ECCCipher->C[]` */
+
+	if (ASN1_STRING_length(cv->ciphertext) <= 0
+		|| ASN1_STRING_length(cv->ciphertext) > INT_MAX) {
+		GMAPIerr(GMAPI_F_SM2CIPHERTEXTVALUE_GET_ECCCIPHER,
+			GMAPI_R_INVALID_CIPHERTEXT_LENGTH);
+		goto end;
+	}
+	ref->L = ASN1_STRING_length(cv->ciphertext);
+	memcpy(ref->C, ASN1_STRING_get0_data(cv->ciphertext),
+		ASN1_STRING_length(cv->ciphertext));
+
+	/* set return value */
+	ret = 1;
+end:
+	return ret;
+}
+# endif
+
+ECDSA_SIG *ECDSA_SIG_new_from_ECCSignature(const ECCSignature *ref)
+{
+	ECDSA_SIG *ret = NULL;
+	ECDSA_SIG *sig = NULL;
+
+	/* check arguments */
+	if (!ref) {
+		GMAPIerr(GMAPI_F_ECDSA_SIG_NEW_FROM_ECCSIGNATURE,
+			ERR_R_PASSED_NULL_PARAMETER);
+		return NULL;
+	}
+
+	/* generate and convert */
+	if (!(sig = ECDSA_SIG_new())) {
+		GMAPIerr(GMAPI_F_ECDSA_SIG_NEW_FROM_ECCSIGNATURE,
+			GMAPI_R_MALLOC_FAILED);
+		goto end;
+	}
+	if (!ECDSA_SIG_set_ECCSignature(sig, ref)) {
+		GMAPIerr(GMAPI_F_ECDSA_SIG_NEW_FROM_ECCSIGNATURE,
+			GMAPI_R_INVALID_SIGNATURE);
+		goto end;
+	}
+
+	/* set return value */
+	ret = sig;
+	sig = NULL;
+
+end:
+	ECDSA_SIG_free(sig);
+	return ret;
+}
+
+int ECDSA_SIG_set_ECCSignature(ECDSA_SIG *sig, const ECCSignature *ref)
+{
+	int ret = 0;
+	BIGNUM *r = NULL;
+	BIGNUM *s = NULL;
+
+	/* check arguments */
+	if (!sig || !ref) {
+		GMAPIerr(GMAPI_F_ECDSA_SIG_SET_ECCSIGNATURE,
+			ERR_R_PASSED_NULL_PARAMETER);
+		return 0;
+	}
+
+	/* ECCSignature ==> ECDSA_SIG */
+	if (!(r = BN_bin2bn(ref->r, ECCref_MAX_LEN, NULL))) {
+		GMAPIerr(GMAPI_F_ECDSA_SIG_SET_ECCSIGNATURE, ERR_R_BN_LIB);
+		goto end;
+	}
+	if (!(s = BN_bin2bn(ref->s, ECCref_MAX_LEN, NULL))) {
+		GMAPIerr(GMAPI_F_ECDSA_SIG_SET_ECCSIGNATURE, ERR_R_BN_LIB);
+		goto end;
+	}
+	/* when using `sm2p256v1`, we need to check (s, r) length correct */
+	if (BN_num_bytes(r) != 256/8 || BN_num_bytes(s) != 256/8) {
+		GMAPIerr(GMAPI_F_ECDSA_SIG_SET_ECCSIGNATURE,
+			GMAPI_R_INVALID_SM2_SIGNATURE);
+		goto end;
+	}
+
+	/* set return value
+	 * `ECDSA_SIG_set0` should make sure that if failed, do not accept
+	 * the value of (r, s), or there will be double-free
+	 */
+	if (!ECDSA_SIG_set0(sig, r, s)) {
+		GMAPIerr(GMAPI_F_ECDSA_SIG_SET_ECCSIGNATURE,
+			ERR_R_EC_LIB);
+		goto end;
+	}
+
+	r = NULL;
+	s = NULL;
+	ret = 1;
+
+end:
+	BN_free(r);
+	BN_free(s);
+	return ret;
+}
+
+int ECDSA_SIG_get_ECCSignature(const ECDSA_SIG *sig, ECCSignature *ref)
+{
+	/* (r, s) are pointed to (sig->r, sig->s), so dont free (r, s) */
+	const BIGNUM *r = NULL;
+	const BIGNUM *s = NULL;
+
+	/* check arguments */
+	if (!sig || !ref) {
+		GMAPIerr(GMAPI_F_ECDSA_SIG_GET_ECCSIGNATURE,
+			ERR_R_PASSED_NULL_PARAMETER);
+		return 0;
+	}
+
+	/* check ECDSA_SIG
+	 * `ECDSA_SIG_get0() return void
+	 */
+	ECDSA_SIG_get0(sig, &r, &s);
+
+	if (BN_num_bytes(r) > ECCref_MAX_LEN ||
+		BN_num_bytes(s) > ECCref_MAX_LEN) {
+		GMAPIerr(GMAPI_F_ECDSA_SIG_GET_ECCSIGNATURE,
+			GMAPI_R_NOT_CONVERTABLE);
+		return 0;
+	}
+
+	/* ECDSA_SIG ==> ECCSignature */
+	memset(ref, 0, sizeof(*ref));
+
+	if (!BN_bn2bin(r, ref->r + ECCref_MAX_LEN - BN_num_bytes(r))) {
+		GMAPIerr(GMAPI_F_ECDSA_SIG_GET_ECCSIGNATURE, ERR_R_BN_LIB);
+		return 0;
+	}
+	if (!BN_bn2bin(s, ref->s + ECCref_MAX_LEN - BN_num_bytes(s))) {
+		GMAPIerr(GMAPI_F_ECDSA_SIG_GET_ECCSIGNATURE, ERR_R_BN_LIB);
+		return 0;
+	}
+
+	return 1;
+}
+
+ECCCipher *d2i_ECCCipher(ECCCipher **a, const unsigned char **pp, long length)
+{
+	ECCCipher *ret = NULL;
+	ECCCipher *sdf = NULL;
+	SM2CiphertextValue *cv = NULL;
+
+	if (!(cv = d2i_SM2CiphertextValue(NULL, pp, length))) {
+		GMAPIerr(GMAPI_F_D2I_ECCCIPHER, ERR_R_SM2_LIB);
+		goto end;
+	}
+
+						
+	if (a && *a) {
+		if (!SM2CiphertextValue_get_ECCCipher(cv, *a)) {
+			GMAPIerr(GMAPI_F_D2I_ECCCIPHER, ERR_R_GMAPI_LIB);
+			goto end;
+		}
+		ret = *a;
+	} else {
+		if (SDF_NewECCCipher(&sdf, ASN1_STRING_length(cv->ciphertext)) != SDR_OK) {
+			GMAPIerr(GMAPI_F_D2I_ECCCIPHER, ERR_R_SDF_LIB);
+			goto end;
+		}
+		sdf->L = ASN1_STRING_length(cv->ciphertext);
+		if (!SM2CiphertextValue_get_ECCCipher(cv, sdf)) {
+			GMAPIerr(GMAPI_F_D2I_ECCCIPHER, ERR_R_GMAPI_LIB);
+			goto end;
+		}
+		ret = sdf;
+		sdf = NULL;
+	}
+
+end:
+	OPENSSL_free(sdf);
+	SM2CiphertextValue_free(cv);
+	return ret;
+}
+
+int i2d_ECCCipher(ECCCipher *a, unsigned char **pp)
+{
+	int ret;
+	SM2CiphertextValue *cv = NULL;
+
+	if (!(cv = SM2CiphertextValue_new_from_ECCCipher(a))) {
+		GMAPIerr(GMAPI_F_I2D_ECCCIPHER, ERR_R_SM2_LIB);
+		return 0;
+	}
+
+	ret = i2d_SM2CiphertextValue(cv, pp);
+	SM2CiphertextValue_free(cv);
+	return ret;
+}
+
+ECCSignature *d2i_ECCSignature(ECCSignature **a, const unsigned char **pp, long length)
+{
+	ECCSignature *ret = NULL;
+	ECCSignature *sdf_sig = NULL;
+	ECDSA_SIG *sig = NULL;
+
+				
+	/* FIXME: `a` not set */
+	(void)a;
+				
+
+
+	if (!(sig = d2i_ECDSA_SIG(NULL, pp, length))) {
+		GMAPIerr(GMAPI_F_D2I_ECCSIGNATURE, ERR_R_EC_LIB);
+		goto end;
+	}
+
+	if (!(sdf_sig = OPENSSL_malloc(sizeof(ECCSignature)))) {
+		GMAPIerr(GMAPI_F_D2I_ECCSIGNATURE, ERR_R_MALLOC_FAILURE);
+		goto end;
+	}
+
+	if (!ECDSA_SIG_get_ECCSignature(sig, sdf_sig)) {
+		GMAPIerr(GMAPI_F_D2I_ECCSIGNATURE, ERR_R_GMAPI_LIB);
+		goto end;
+	}
+
+	ret = sdf_sig;
+	sdf_sig = NULL;
+
+end:
+	OPENSSL_free(sdf_sig);
+	ECDSA_SIG_free(sig);
+	return ret;
+}
+
+int i2d_ECCSignature(ECCSignature *a, unsigned char **pp)
+{
+	int ret;
+	ECDSA_SIG *sig = NULL;
+
+	if (!(sig = ECDSA_SIG_new_from_ECCSignature(a))) {
+		GMAPIerr(GMAPI_F_I2D_ECCSIGNATURE, ERR_R_GMAPI_LIB);
+		return 0;
+	}
+
+	ret = i2d_ECDSA_SIG(sig, pp);
+	ECDSA_SIG_free(sig);
+	return ret;
+}
+
+# ifndef OPENSSL_NO_ECIES
+ECIES_CIPHERTEXT_VALUE *ECIES_CIPHERTEXT_VALUE_new_from_ECCCipher(
+	const ECCCipher *ref)
+{
+	ECIES_CIPHERTEXT_VALUE *ret = NULL;
+	ECIES_CIPHERTEXT_VALUE *cv = NULL;
+
+
+	if (!(cv = ECIES_CIPHERTEXT_VALUE_new())) {
+		GMAPIerr(GMAPI_F_ECIES_CIPHERTEXT_VALUE_NEW_FROM_ECCCIPHER,
+			ERR_R_MALLOC_FAILURE);
+		goto end;
+	}
+
+	if (!ECIES_CIPHERTEXT_VALUE_set_ECCCipher(cv, ref)) {
+		GMAPIerr(GMAPI_F_ECIES_CIPHERTEXT_VALUE_NEW_FROM_ECCCIPHER,
+			ERR_R_GMAPI_LIB);
+		goto end;
+	}
+
+	ret = cv;
+	cv = NULL;
+
+end:
+	ECIES_CIPHERTEXT_VALUE_free(cv);
+	return ret;
+}
+
+int ECIES_CIPHERTEXT_VALUE_set_ECCCipher(ECIES_CIPHERTEXT_VALUE *cv, const ECCCipher *ref)
+{
+	int ret = 0;
+	int point_form = POINT_CONVERSION_COMPRESSED;
+	EC_GROUP *group = NULL;
+	EC_POINT *point = NULL;
+	BIGNUM *x = NULL;
+	BIGNUM *y = NULL;
+	BN_CTX *bn_ctx = NULL;
+	int len;
+
+	if (!(group = EC_GROUP_new_by_curve_name(NID_sm2p256v1))
+		|| !(point = EC_POINT_new(group))
+		|| !(x = BN_new())
+		|| !(y = BN_new())
+		|| !(bn_ctx = BN_CTX_new())) {
+		GMAPIerr(GMAPI_F_ECIES_CIPHERTEXT_VALUE_SET_ECCCIPHER,
+			ERR_R_MALLOC_FAILURE);
+		goto end;
+	}
+
+	if (!BN_bin2bn(ref->x, sizeof(ref->x), x)
+		|| !BN_bin2bn(ref->y, sizeof(ref->y), y)) {
+		GMAPIerr(GMAPI_F_ECIES_CIPHERTEXT_VALUE_SET_ECCCIPHER,
+			ERR_R_BN_LIB);
+		goto end;
+	}
+
+	if (!EC_POINT_set_affine_coordinates_GFp(group, point, x, y, bn_ctx)) {
+		GMAPIerr(GMAPI_F_ECIES_CIPHERTEXT_VALUE_SET_ECCCIPHER,
+			ERR_R_EC_LIB);
+		goto end;
+	}
+
+	len = EC_POINT_point2oct(group, point, point_form, NULL, 0, NULL);
+	if (!ASN1_OCTET_STRING_set(cv->ephem_point, NULL, len)) {
+		GMAPIerr(GMAPI_F_ECIES_CIPHERTEXT_VALUE_SET_ECCCIPHER,
+			ERR_R_MALLOC_FAILURE);
+		goto end;
+	}
+	if (EC_POINT_point2oct(group, point, point_form,
+		cv->ephem_point->data, len, NULL) <= 0) {
+		GMAPIerr(GMAPI_F_ECIES_CIPHERTEXT_VALUE_SET_ECCCIPHER,
+			ERR_R_EC_LIB);
+		goto end;
+	}
+
+	if (!ASN1_OCTET_STRING_set(cv->ciphertext, ref->C, ref->L)) {
+		GMAPIerr(GMAPI_F_ECIES_CIPHERTEXT_VALUE_SET_ECCCIPHER,
+			ERR_R_ASN1_LIB);
+		goto end;
+	}
+
+	if (!ASN1_OCTET_STRING_set(cv->mactag, ref->M, sizeof(ref->M))) {
+		GMAPIerr(GMAPI_F_ECIES_CIPHERTEXT_VALUE_SET_ECCCIPHER,
+			ERR_R_ASN1_LIB);
+		goto end;
+	}
+
+	ret = 1;
+
+end:
+	EC_GROUP_free(group);
+	EC_POINT_free(point);
+	BN_free(x);
+	BN_free(y);
+	BN_CTX_free(bn_ctx);
+	return ret;
+}
+
+int ECIES_CIPHERTEXT_VALUE_get_ECCCipher(const ECIES_CIPHERTEXT_VALUE *cv, ECCCipher *ref)
+{
+	int ret = 0;
+	EC_GROUP *group = NULL;
+	EC_POINT *point = NULL;
+	BIGNUM *x = NULL;
+	BIGNUM *y = NULL;
+	BN_CTX *bn_ctx = NULL;
+	int len;
+
+	if (ASN1_STRING_length(cv->mactag) != sizeof(ref->M)) {
+		GMAPIerr(GMAPI_F_ECIES_CIPHERTEXT_VALUE_GET_ECCCIPHER,
+			GMAPI_R_INVALID_SM2_CIPHERTEXT_MAC_LENGTH);
+		return 0;
+	}
+
+	len = sizeof(ECCCipher) - 1 + ASN1_STRING_length(cv->ciphertext);
+
+	if (!ref) {
+		return len;
+	}
+
+	if (!(group = EC_GROUP_new_by_curve_name(NID_sm2p256v1))
+		|| !(point = EC_POINT_new(group))
+		|| !(x = BN_new())
+		|| !(y = BN_new())
+		|| !(bn_ctx = BN_CTX_new())) {
+		GMAPIerr(GMAPI_F_ECIES_CIPHERTEXT_VALUE_GET_ECCCIPHER,
+			ERR_R_MALLOC_FAILURE);
+		goto end;
+	}
+
+	if (!EC_POINT_oct2point(group, point,
+		ASN1_STRING_get0_data(cv->ephem_point),
+		ASN1_STRING_length(cv->ephem_point), bn_ctx)) {
+		GMAPIerr(GMAPI_F_ECIES_CIPHERTEXT_VALUE_GET_ECCCIPHER,
+			ERR_R_EC_LIB);
+		goto end;
+	}
+
+	if (!EC_POINT_get_affine_coordinates_GFp(group, point, x, y, bn_ctx)) {
+		GMAPIerr(GMAPI_F_ECIES_CIPHERTEXT_VALUE_GET_ECCCIPHER,
+			ERR_R_EC_LIB);
+		goto end;
+	}
+
+	memset(ref, 0, len);
+	BN_bn2bin(x, ref->x + sizeof(ref->x) - BN_num_bytes(x));
+	BN_bn2bin(y, ref->y + sizeof(ref->y) - BN_num_bytes(y));
+	memcpy(ref->C, ASN1_STRING_get0_data(cv->ciphertext),
+		ASN1_STRING_length(cv->ciphertext));
+	memcpy(ref->M, ASN1_STRING_get0_data(cv->mactag),
+		ASN1_STRING_length(cv->mactag));
+
+	ret = len;
+
+end:
+	EC_GROUP_free(group);
+	EC_POINT_free(point);
+	BN_free(x);
+	BN_free(y);
+	BN_CTX_free(bn_ctx);
+	return ret;
+}
+# endif /* OPENSSL_NO_ECIES */
+#endif
diff --git a/crypto/gmapi/gmapi_sdf_rsa.c b/crypto/gmapi/gmapi_sdf_rsa.c
new file mode 100644
index 000000000..7ca93cf1f
--- /dev/null
+++ b/crypto/gmapi/gmapi_sdf_rsa.c
@@ -0,0 +1,385 @@
+/* ====================================================================
+ * Copyright (c) 2015-2016 The GmSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the GmSSL Project.
+ *    (http://gmssl.org/)"
+ *
+ * 4. The name "GmSSL Project" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    guanzhi1980@gmail.com.
+ *
+ * 5. Products derived from this software may not be called "GmSSL"
+ *    nor may "GmSSL" appear in their names without prior written
+ *    permission of the GmSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the GmSSL Project
+ *    (http://gmssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE GmSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE GmSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+
+#include 
+#include "internal/cryptlib.h"
+
+#if !defined(OPENSSL_NO_SDF) && !defined(OPENSSL_NO_RSA)
+# include 
+# include 
+# include 
+# include 
+
+RSA *RSA_new_from_RSArefPublicKey(const RSArefPublicKey *ref)
+{
+	RSA *ret = NULL;
+	RSA *rsa = NULL;
+
+	if (!ref) {
+		GMAPIerr(GMAPI_F_RSA_NEW_FROM_RSAREFPUBLICKEY,
+			ERR_R_PASSED_NULL_PARAMETER);
+		return NULL;
+	}
+
+	if (!(rsa = RSA_new())) {
+		GMAPIerr(GMAPI_F_RSA_NEW_FROM_RSAREFPUBLICKEY,
+			ERR_R_MALLOC_FAILURE);
+		return NULL;
+	}
+
+	if (!RSA_set_RSArefPublicKey(rsa, ref)) {
+		GMAPIerr(GMAPI_F_RSA_NEW_FROM_RSAREFPUBLICKEY,
+			GMAPI_R_INVALID_RSA_PUBLIC_KEY);
+		goto end;
+	}
+
+	ret = rsa;
+	rsa = NULL;
+
+end:
+	RSA_free(rsa);
+	return ret;
+}
+
+int RSA_set_RSArefPublicKey(RSA *rsa, const RSArefPublicKey *ref)
+{
+	int ret = 0;
+	BIGNUM *n = NULL;
+	BIGNUM *e = NULL;
+
+	if (!rsa || !ref) {
+		GMAPIerr(GMAPI_F_RSA_SET_RSAREFPUBLICKEY,
+			ERR_R_PASSED_NULL_PARAMETER);
+		return 0;
+	}
+
+	if ((ref->bits < OPENSSL_RSA_FIPS_MIN_MODULUS_BITS) ||
+		(ref->bits > sizeof(ref->m) * 8) || (ref->bits % 8 != 0)) {
+		GMAPIerr(GMAPI_F_RSA_SET_RSAREFPUBLICKEY,
+			GMAPI_R_INVALID_RSA_KEY_LENGTH);
+		return 0;
+	}
+
+	if (!(n = BN_bin2bn(ref->m, sizeof(ref->m), NULL))) {
+		GMAPIerr(GMAPI_F_RSA_SET_RSAREFPUBLICKEY, ERR_R_BN_LIB);
+		goto end;
+	}
+
+	if (!(e = BN_bin2bn(ref->e, sizeof(ref->e), NULL))) {
+		GMAPIerr(GMAPI_F_RSA_SET_RSAREFPUBLICKEY, ERR_R_BN_LIB);
+		goto end;
+	}
+
+	if (!RSA_set0_key(rsa, n, e, NULL)) {
+		GMAPIerr(GMAPI_F_RSA_SET_RSAREFPUBLICKEY, ERR_R_RSA_LIB);
+		goto end;
+	}
+	n = NULL;
+	e = NULL;
+
+	if (1 != RSA_check_key(rsa)) {
+		GMAPIerr(GMAPI_F_RSA_SET_RSAREFPUBLICKEY,
+			GMAPI_R_INVALID_RSA_PUBLIC_KEY);
+		goto end;
+	}
+
+	ret = 1;
+
+end:
+	BN_free(n);
+	BN_free(e);
+	return ret;
+}
+
+int RSA_get_RSArefPublicKey(RSA *rsa, RSArefPublicKey *ref)
+{
+	const BIGNUM *n;
+	const BIGNUM *e;
+
+	if (!rsa || !ref) {
+		GMAPIerr(GMAPI_F_RSA_GET_RSAREFPUBLICKEY,
+			ERR_R_PASSED_NULL_PARAMETER);
+		return 0;
+	}
+
+	RSA_get0_key(rsa, &n, &e, NULL);
+
+	if (!n || !e) {
+		GMAPIerr(GMAPI_F_RSA_GET_RSAREFPUBLICKEY,
+			GMAPI_R_INVALID_RSA_PUBLIC_KEY);
+		return 0;
+	}
+	if (RSA_bits(rsa) % 8 != 0 || RSA_bits(rsa) > sizeof(ref->m) * 8) {
+		GMAPIerr(GMAPI_F_RSA_GET_RSAREFPUBLICKEY,
+			GMAPI_R_INVALID_RSA_PUBLIC_KEY);
+		return 0;
+	}
+
+	memset(ref, 0, sizeof(RSArefPublicKey));
+	ref->bits = RSA_bits(rsa);
+
+	if (BN_bn2bin(n, ref->m + sizeof(ref->m) - BN_num_bytes(n)) <= 0) {
+		GMAPIerr(GMAPI_F_RSA_GET_RSAREFPUBLICKEY,
+			GMAPI_R_ENCODE_RSA_PUBLIC_KEY_FAILED);
+		return 0;
+	}
+
+	if (BN_bn2bin(e, ref->e + sizeof(ref->e) - BN_num_bytes(e)) <= 0) {
+		GMAPIerr(GMAPI_F_RSA_GET_RSAREFPUBLICKEY,
+			GMAPI_R_ENCODE_RSA_PUBLIC_KEY_FAILED);
+		return 0;
+	}
+
+	return 1;
+}
+
+RSA *RSA_new_from_RSArefPrivateKey(const RSArefPrivateKey *ref)
+{
+	RSA *ret = NULL;
+	RSA *rsa = NULL;
+
+	if (!ref) {
+		GMAPIerr(GMAPI_F_RSA_NEW_FROM_RSAREFPRIVATEKEY,
+			ERR_R_PASSED_NULL_PARAMETER);
+		return NULL;
+	}
+
+	if (!(rsa = RSA_new())) {
+		GMAPIerr(GMAPI_F_RSA_NEW_FROM_RSAREFPRIVATEKEY,
+			ERR_R_MALLOC_FAILURE);
+		return NULL;
+	}
+
+	if (!RSA_set_RSArefPrivateKey(rsa, ref)) {
+		GMAPIerr(GMAPI_F_RSA_NEW_FROM_RSAREFPRIVATEKEY,
+			GMAPI_R_INVALID_RSA_PRIVATE_KEY);
+		goto end;
+	}
+
+	ret = rsa;
+	rsa = NULL;
+
+end:
+	RSA_free(rsa);
+	return ret;
+}
+
+
+int RSA_set_RSArefPrivateKey(RSA *rsa, const RSArefPrivateKey *ref)
+{
+	int ret = 0;
+	BIGNUM *n = NULL;
+	BIGNUM *e = NULL;
+	BIGNUM *d = NULL;
+	BIGNUM *p = NULL;
+	BIGNUM *q = NULL;
+	BIGNUM *dmp1 = NULL;
+	BIGNUM *dmq1 = NULL;
+	BIGNUM *iqmp = NULL;
+
+	if (!rsa || !ref) {
+		GMAPIerr(GMAPI_F_RSA_SET_RSAREFPRIVATEKEY,
+			ERR_R_PASSED_NULL_PARAMETER);
+		return 0;
+	}
+
+	if (ref->bits < OPENSSL_RSA_FIPS_MIN_MODULUS_BITS
+		|| ref->bits > sizeof(ref->m) *8
+		|| ref->bits % 8 != 0
+		|| ref->bits % 16 != 0) {
+		GMAPIerr(GMAPI_F_RSA_SET_RSAREFPRIVATEKEY,
+			GMAPI_R_INVALID_KEY_LENGTH);
+		return 0;
+	}
+
+	if (!(n = BN_bin2bn(ref->m, sizeof(ref->m), NULL))
+		|| !(e = BN_bin2bn(ref->e, sizeof(ref->e), NULL))
+		|| !(d = BN_bin2bn(ref->d, sizeof(ref->d), NULL))
+		|| !(p = BN_bin2bn(ref->prime[0], sizeof(ref->prime[0]), NULL))
+		|| !(q = BN_bin2bn(ref->prime[1], sizeof(ref->prime[1]), NULL))
+		|| !(dmp1 = BN_bin2bn(ref->pexp[0], sizeof(ref->pexp[0]), NULL))
+		|| !(dmq1 = BN_bin2bn(ref->pexp[1], sizeof(ref->pexp[1]), NULL))
+		|| !(iqmp = BN_bin2bn(ref->coef, sizeof(ref->coef), NULL))) {
+		GMAPIerr(GMAPI_F_RSA_SET_RSAREFPRIVATEKEY, ERR_R_BN_LIB);
+		goto end;
+	}
+
+	if (!RSA_set0_key(rsa, n, e, d)) {
+		GMAPIerr(GMAPI_F_RSA_SET_RSAREFPRIVATEKEY,
+			GMAPI_R_INVALID_RSA_PRIVATE_KEY);
+		goto end;
+	}
+	n = NULL;
+	e = NULL;
+	d = NULL;
+
+	if (!RSA_set0_factors(rsa, p, q)) {
+		GMAPIerr(GMAPI_F_RSA_SET_RSAREFPRIVATEKEY,
+			GMAPI_R_INVALID_RSA_PRIVATE_KEY);
+		goto end;
+	}
+	p = NULL;
+	q = NULL;
+
+	if (!RSA_set0_crt_params(rsa, dmp1, dmq1, iqmp)) {
+		GMAPIerr(GMAPI_F_RSA_SET_RSAREFPRIVATEKEY,
+			GMAPI_R_INVALID_RSA_PRIVATE_KEY);
+		goto end;
+	}
+	dmp1 = NULL;
+	dmq1 = NULL;
+	iqmp = NULL;
+
+	ret = 1;
+
+end:
+	BN_free(n);
+	BN_free(e);
+	BN_free(d);
+	BN_free(p);
+	BN_free(q);
+	BN_free(dmp1);
+	BN_free(dmq1);
+	BN_free(iqmp);
+	return ret;
+}
+
+int RSA_get_RSArefPrivateKey(RSA *rsa, RSArefPrivateKey *ref)
+{
+	const BIGNUM *n;
+	const BIGNUM *e;
+	const BIGNUM *d;
+	const BIGNUM *p;
+	const BIGNUM *q;
+	const BIGNUM *dmp1;
+	const BIGNUM *dmq1;
+	const BIGNUM *iqmp;
+
+	if (!rsa || !ref) {
+		GMAPIerr(GMAPI_F_RSA_GET_RSAREFPRIVATEKEY,
+			ERR_R_PASSED_NULL_PARAMETER);
+		return 0;
+	}
+
+	if (RSA_bits(rsa) % 8 != 0 || RSA_bits(rsa) > sizeof(ref->m) * 8) {
+		GMAPIerr(GMAPI_F_RSA_GET_RSAREFPRIVATEKEY,
+			GMAPI_R_INVALID_RSA_PRIVATE_KEY);
+		return 0;
+	}
+
+	RSA_get0_key(rsa, &n, &e, &d);
+	RSA_get0_factors(rsa, &p, &q);
+	RSA_get0_crt_params(rsa, &dmp1, &dmq1, &iqmp);
+
+	if (!n || !e || !d) {
+		GMAPIerr(GMAPI_F_RSA_GET_RSAREFPRIVATEKEY,
+			GMAPI_R_INVALID_RSA_PRIVATE_KEY);
+		return 0;
+	}
+
+	memset(ref, 0, sizeof(RSArefPrivateKey));
+
+	ref->bits = RSA_bits(rsa);
+
+	if (BN_bn2bin(n, ref->m + sizeof(ref->m) - BN_num_bytes(n)) <= 0) {
+		GMAPIerr(GMAPI_F_RSA_GET_RSAREFPRIVATEKEY,
+			GMAPI_R_INVALID_RSA_PRIVATE_KEY);
+		return 0;
+	}
+
+	if (BN_bn2bin(e, ref->e + sizeof(ref->e) - BN_num_bytes(e)) <= 0) {
+		GMAPIerr(GMAPI_F_RSA_GET_RSAREFPRIVATEKEY,
+			GMAPI_R_INVALID_RSA_PRIVATE_KEY);
+		return 0;
+	}
+
+	if (BN_bn2bin(d, ref->d + sizeof(ref->d) - BN_num_bytes(d)) <= 0) {
+		GMAPIerr(GMAPI_F_RSA_GET_RSAREFPRIVATEKEY,
+			GMAPI_R_INVALID_RSA_PRIVATE_KEY);
+		return 0;
+	}
+
+	if (p && BN_bn2bin(p, ref->prime[0] + sizeof(ref->prime[0]) -
+		BN_num_bytes(p)) < 0) {
+		GMAPIerr(GMAPI_F_RSA_GET_RSAREFPRIVATEKEY,
+			GMAPI_R_INVALID_RSA_PRIVATE_KEY);
+		return 0;
+	}
+
+	if (q && BN_bn2bin(q, ref->prime[1] + sizeof(ref->prime[1]) -
+		BN_num_bytes(q)) < 0) {
+		GMAPIerr(GMAPI_F_RSA_GET_RSAREFPRIVATEKEY,
+			GMAPI_R_INVALID_RSA_PRIVATE_KEY);
+		return 0;
+	}
+
+	if (dmp1 && BN_bn2bin(dmp1, ref->pexp[0] + sizeof(ref->pexp[0]) -
+		BN_num_bytes(dmp1)) < 0) {
+		GMAPIerr(GMAPI_F_RSA_GET_RSAREFPRIVATEKEY,
+			GMAPI_R_INVALID_RSA_PRIVATE_KEY);
+		return 0;
+	}
+
+	if (dmq1 && BN_bn2bin(dmq1, ref->pexp[1] + sizeof(ref->pexp[1]) -
+		BN_num_bytes(dmq1)) < 0) {
+		GMAPIerr(GMAPI_F_RSA_GET_RSAREFPRIVATEKEY,
+			GMAPI_R_INVALID_RSA_PRIVATE_KEY);
+		return 0;
+	}
+
+	if (iqmp && BN_bn2bin(iqmp, ref->coef + sizeof(ref->coef) -
+		BN_num_bytes(iqmp)) < 0) {
+		GMAPIerr(GMAPI_F_RSA_GET_RSAREFPRIVATEKEY,
+			GMAPI_R_INVALID_RSA_PRIVATE_KEY);
+		return 0;
+	}
+
+	return 1;
+}
+#endif
diff --git a/crypto/gmapi/gmapi_sgd.c b/crypto/gmapi/gmapi_sgd.c
new file mode 100644
index 000000000..91f1436a5
--- /dev/null
+++ b/crypto/gmapi/gmapi_sgd.c
@@ -0,0 +1,216 @@
+/* ====================================================================
+ * Copyright (c) 2016 The GmSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the GmSSL Project.
+ *    (http://gmssl.org/)"
+ *
+ * 4. The name "GmSSL Project" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    guanzhi1980@gmail.com.
+ *
+ * 5. Products derived from this software may not be called "GmSSL"
+ *    nor may "GmSSL" appear in their names without prior written
+ *    permission of the GmSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the GmSSL Project
+ *    (http://gmssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE GmSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE GmSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include "../../e_os.h"
+
+typedef struct {
+	int nid;
+	ULONG ulAlgId;
+	ULONG ulFeedBitLen;
+} GMAPI_CIPHER_ITEM;
+
+static GMAPI_CIPHER_ITEM gmapi_ciphers[] = {
+	{NID_sm1_ecb, SGD_SM1_ECB, 0},
+	{NID_sm1_cbc, SGD_SM1_CBC, 0},
+	{NID_sm1_cfb1, SGD_SM1_CFB, 1},
+	{NID_sm1_cfb8, SGD_SM1_CFB, 8},
+	{NID_sm1_cfb128, SGD_SM1_CFB, 128},
+	{NID_sm1_ofb128, SGD_SM1_OFB, 128},
+	{NID_sms4_ecb, SGD_SM4_ECB, 0},
+	{NID_sms4_cbc, SGD_SM4_CBC, 0},
+	{NID_sms4_cfb1, SGD_SM4_CFB, 1},
+	{NID_sms4_cfb8, SGD_SM4_CFB, 8},
+	{NID_sms4_cfb128, SGD_SM4_CFB, 128},
+	{NID_sms4_ofb128, SGD_SM4_OFB, 128},
+	{NID_ssf33_ecb, SGD_SSF33_ECB, 0},
+	{NID_ssf33_cbc, SGD_SSF33_CBC, 0},
+	{NID_ssf33_cfb1, SGD_SSF33_CFB, 1},
+	{NID_ssf33_cfb8, SGD_SSF33_CFB, 8},
+	{NID_ssf33_cfb128, SGD_SSF33_CFB, 128},
+	{NID_ssf33_ofb128, SGD_SSF33_OFB, 128},
+	{NID_zuc_128eea3, SGD_ZUC_EEA3, 0},
+};
+
+const EVP_CIPHER *EVP_get_cipherbysgd(ULONG ulAlgId, ULONG ulFeedBitLen)
+{
+	size_t i;
+
+	for (i = 0; i < OSSL_NELEM(gmapi_ciphers); i++) {
+		if (gmapi_ciphers[i].ulAlgId == ulAlgId
+			&& gmapi_ciphers[i].ulFeedBitLen == ulFeedBitLen) {
+			return EVP_get_cipherbynid(gmapi_ciphers[i].nid);
+		}
+	}
+
+	return NULL;	
+}
+
+int EVP_CIPHER_get_sgd(const EVP_CIPHER *cipher, ULONG *pulAlgId, ULONG *pulFeedBits)
+{
+	size_t i;
+
+	if (!cipher || !pulAlgId || !pulFeedBits) {
+		GMAPIerr(GMAPI_F_EVP_CIPHER_GET_SGD, ERR_R_PASSED_NULL_PARAMETER);
+		return 0;
+	}
+
+	for (i = 0; i < OSSL_NELEM(gmapi_ciphers); i++) {
+		if (EVP_CIPHER_nid(cipher) == gmapi_ciphers[i].nid) {
+			*pulAlgId = gmapi_ciphers[i].ulAlgId;
+			*pulFeedBits = gmapi_ciphers[i].ulFeedBitLen;
+			return 1;
+		}
+	}
+
+	/* caller can clear this error */
+	GMAPIerr(GMAPI_F_EVP_CIPHER_GET_SGD, GMAPI_R_NOT_SUPPORTED_GMAPI_CIPHER);
+	*pulAlgId = 0;
+	*pulFeedBits = 0;
+	return 0;
+}
+
+int EVP_CIPHER_CTX_get_sgd(const EVP_CIPHER_CTX *ctx,
+	ULONG *pulAlgId, ULONG *pulFeedBits)
+{
+	if (!ctx || !pulAlgId || !pulFeedBits) {
+		GMAPIerr(GMAPI_F_EVP_CIPHER_CTX_GET_SGD,
+			ERR_R_PASSED_NULL_PARAMETER);
+		return 0;
+	}
+	return EVP_CIPHER_get_sgd(EVP_CIPHER_CTX_cipher(ctx), pulAlgId, pulFeedBits);
+}
+
+typedef struct {
+	int nid;
+	ULONG ulAlgId;
+} GMAPI_ALGOR_ITEM;
+
+
+static GMAPI_ALGOR_ITEM gmapi_digests[] = {
+	{NID_sm3, SGD_SM3},
+	{NID_sha1, SGD_SHA1},
+	{NID_sha256, SGD_SHA256},
+};
+
+const EVP_MD *EVP_get_digestbysgd(ULONG ulAlgId)
+{
+	size_t i;
+
+	for (i = 0; i < OSSL_NELEM(gmapi_digests); i++) {
+		if (gmapi_digests[i].ulAlgId == ulAlgId) {
+			return EVP_get_digestbynid(gmapi_digests[i].nid);
+		}
+	}
+
+	return NULL;
+}
+
+int EVP_MD_get_sgd(const EVP_MD *md, ULONG *ulAlgId)
+{
+	size_t i;
+
+	if (!md || !ulAlgId) {
+		GMAPIerr(GMAPI_F_EVP_MD_GET_SGD, ERR_R_PASSED_NULL_PARAMETER);
+		return 0;
+	}
+
+	for (i = 0; i < OSSL_NELEM(gmapi_digests); i++) {
+		if (gmapi_digests[i].nid == EVP_MD_nid(md)) {
+			*ulAlgId = gmapi_digests[i].ulAlgId;
+			return 1;
+		}
+	}
+
+	*ulAlgId = 0;
+	return 0;
+}
+
+int EVP_MD_CTX_get_sgd(const EVP_MD_CTX *ctx, ULONG *ulAlgId)
+{
+	return EVP_MD_get_sgd(EVP_MD_CTX_md(ctx), ulAlgId);
+}
+
+static GMAPI_ALGOR_ITEM gmapi_pkeys[] = {
+	{NID_rsa, SGD_RSA_SIGN},
+	{NID_rsaEncryption, SGD_RSA_ENC},
+	{NID_sm2sign, SGD_SM2_1},
+	{NID_sm2exchange, SGD_SM2_2},
+	{NID_sm2encrypt, SGD_SM2_3}
+};
+
+int EVP_PKEY_get_sgd(const EVP_PKEY *pkey, ULONG *ulAlgId)
+{
+	size_t i;
+
+	if (!pkey || !ulAlgId) {
+		GMAPIerr(GMAPI_F_EVP_PKEY_GET_SGD, ERR_R_PASSED_NULL_PARAMETER);
+		return 0;
+	}
+
+	for (i = 0; i < OSSL_NELEM(gmapi_pkeys); i++) {
+		if (gmapi_pkeys[i].nid == EVP_PKEY_base_id(pkey)) {
+			*ulAlgId = gmapi_pkeys[i].ulAlgId;
+			return 1;
+		}
+	}
+
+	GMAPIerr(GMAPI_F_EVP_PKEY_GET_SGD, GMAPI_R_NOT_SUPPORTED_PKEY);
+	return 0;
+}
+
+int EVP_PKEY_CTX_get_sgd(const EVP_PKEY_CTX *ctx, ULONG *ulAlgId)
+{
+	return EVP_PKEY_get_sgd(EVP_PKEY_CTX_get0_pkey((EVP_PKEY_CTX *)ctx), ulAlgId);
+}
diff --git a/crypto/gmapi/gmapi_skf_ec.c b/crypto/gmapi/gmapi_skf_ec.c
new file mode 100644
index 000000000..8bff0b28c
--- /dev/null
+++ b/crypto/gmapi/gmapi_skf_ec.c
@@ -0,0 +1,682 @@
+/* ====================================================================
+ * Copyright (c) 2016 The GmSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the GmSSL Project.
+ *    (http://gmssl.org/)"
+ *
+ * 4. The name "GmSSL Project" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    guanzhi1980@gmail.com.
+ *
+ * 5. Products derived from this software may not be called "GmSSL"
+ *    nor may "GmSSL" appear in their names without prior written
+ *    permission of the GmSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the GmSSL Project
+ *    (http://gmssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE GmSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE GmSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+
+#include 
+#include "internal/cryptlib.h"
+
+#if !defined(OPENSSL_NO_SKF) && !defined(OPENSSL_NO_EC)
+# include 
+# include 
+# include 
+# include 
+# include "../ec/ec_lcl.h"
+# include "../sm2/sm2_lcl.h"
+# include "../ecies/ecies_lcl.h"
+
+EC_KEY *EC_KEY_new_from_ECCPUBLICKEYBLOB(const ECCPUBLICKEYBLOB *blob)
+{
+	EC_KEY *ret;
+
+	if (!(ret = EC_KEY_new_by_curve_name(NID_sm2p256v1))) {
+		GMAPIerr(GMAPI_F_EC_KEY_NEW_FROM_ECCPUBLICKEYBLOB, ERR_R_EC_LIB);
+		return NULL;
+	}
+
+	if (!EC_KEY_set_ECCPUBLICKEYBLOB(ret, blob)) {
+		GMAPIerr(GMAPI_F_EC_KEY_NEW_FROM_ECCPUBLICKEYBLOB,
+			GMAPI_R_DECODE_EC_PUBLIC_KEY_FAILED);
+		EC_KEY_free(ret);
+		return NULL;
+	}
+
+	return ret;
+}
+
+int EC_KEY_set_ECCPUBLICKEYBLOB(EC_KEY *ec_key, const ECCPUBLICKEYBLOB *blob)
+{
+	int ret = 0;
+	BIGNUM *x = NULL;
+	BIGNUM *y = NULL;
+
+	if ((int)blob->BitLen != EC_GROUP_get_degree(EC_KEY_get0_group(ec_key))) {
+		GMAPIerr(GMAPI_F_EC_KEY_SET_ECCPUBLICKEYBLOB, GMAPI_R_INVALID_KEY_LENGTH);
+		return 0;
+	}
+
+	if (!(x = BN_bin2bn(blob->XCoordinate, sizeof(blob->XCoordinate), NULL))) {
+		GMAPIerr(GMAPI_F_EC_KEY_SET_ECCPUBLICKEYBLOB, ERR_R_BN_LIB);
+		goto end;
+	}
+	if (!(y = BN_bin2bn(blob->YCoordinate, sizeof(blob->YCoordinate), NULL))) {
+		GMAPIerr(GMAPI_F_EC_KEY_SET_ECCPUBLICKEYBLOB, ERR_R_BN_LIB);
+		goto end;
+	}
+	if (!EC_KEY_set_public_key_affine_coordinates(ec_key, x, y)) {
+		GMAPIerr(GMAPI_F_EC_KEY_SET_ECCPUBLICKEYBLOB, GMAPI_R_INVALID_PUBLIC_KEY);
+		goto end;
+	}
+
+	ret = 1;
+end:
+	BN_free(x);
+	BN_free(y);
+	return ret;
+}
+
+int EC_KEY_get_ECCPUBLICKEYBLOB(EC_KEY *ec_key, ECCPUBLICKEYBLOB *blob)
+{
+	int ret = 0;
+	BIGNUM *x = NULL;
+	BIGNUM *y = NULL;
+	BN_CTX *bn_ctx = NULL;
+	const EC_GROUP *group = EC_KEY_get0_group(ec_key);
+	const EC_POINT *point = EC_KEY_get0_public_key(ec_key);
+
+	if (EC_GROUP_get_degree(group) > ECC_MAX_MODULUS_BITS_LEN) {
+		GMAPIerr(GMAPI_F_EC_KEY_GET_ECCPUBLICKEYBLOB, GMAPI_R_INVALID_KEY_LENGTH);
+		goto end;
+	}
+
+	x = BN_new();
+	y = BN_new();
+	bn_ctx = BN_CTX_new();
+	if (!x || !y || !bn_ctx) {
+		GMAPIerr(GMAPI_F_EC_KEY_GET_ECCPUBLICKEYBLOB, ERR_R_BN_LIB);
+		goto end;
+	}
+
+	if (EC_METHOD_get_field_type(EC_GROUP_method_of(group)) == NID_X9_62_prime_field) {
+		if (!EC_POINT_get_affine_coordinates_GFp(group, point, x, y, bn_ctx)) {
+			GMAPIerr(GMAPI_F_EC_KEY_GET_ECCPUBLICKEYBLOB, ERR_R_EC_LIB);
+			goto end;
+		}
+	} else  {
+		if (!EC_POINT_get_affine_coordinates_GF2m(group, point, x, y, bn_ctx)) {
+			GMAPIerr(GMAPI_F_EC_KEY_GET_ECCPUBLICKEYBLOB, ERR_R_EC_LIB);
+			goto end;
+		}
+	}
+
+	memset(blob, 0, sizeof(*blob));
+	blob->BitLen = EC_GROUP_get_degree(group);
+	if (!BN_bn2bin(x, blob->XCoordinate + sizeof(blob->XCoordinate) - BN_num_bytes(x))) {
+		GMAPIerr(GMAPI_F_EC_KEY_GET_ECCPUBLICKEYBLOB, ERR_R_BN_LIB);
+		goto end;
+	}
+	if (!BN_bn2bin(y, blob->YCoordinate + sizeof(blob->YCoordinate) - BN_num_bytes(y))) {
+		GMAPIerr(GMAPI_F_EC_KEY_GET_ECCPUBLICKEYBLOB, ERR_R_BN_LIB);
+		goto end;
+	}
+
+	ret = 1;
+end:
+	BN_free(x);
+	BN_free(y);
+	BN_CTX_free(bn_ctx);
+	return ret;
+}
+
+EC_KEY *EC_KEY_new_from_ECCPRIVATEKEYBLOB(const ECCPRIVATEKEYBLOB *blob)
+{
+	EC_KEY *ret;
+
+	if (!(ret = EC_KEY_new_by_curve_name(NID_sm2p256v1))) {
+		GMAPIerr(GMAPI_F_EC_KEY_NEW_FROM_ECCPRIVATEKEYBLOB, ERR_R_EC_LIB);
+		return NULL;
+	}
+
+	if (!EC_KEY_set_ECCPRIVATEKEYBLOB(ret, blob)) {
+		GMAPIerr(GMAPI_F_EC_KEY_NEW_FROM_ECCPRIVATEKEYBLOB,
+			GMAPI_R_DECODE_EC_PRIVATE_KEY_FAILED);
+		EC_KEY_free(ret);
+		return NULL;
+	}
+
+	return ret;
+}
+
+int EC_KEY_set_ECCPRIVATEKEYBLOB(EC_KEY *ec_key, const ECCPRIVATEKEYBLOB *blob)
+{
+	int ret = 0;
+	BIGNUM *d = NULL;
+
+	if ((int)blob->BitLen != EC_GROUP_get_degree(EC_KEY_get0_group(ec_key))) {
+		GMAPIerr(GMAPI_F_EC_KEY_SET_ECCPRIVATEKEYBLOB, GMAPI_R_INVALID_KEY_LENGTH);
+		goto end;
+	}
+
+	if (!(d = BN_bin2bn(blob->PrivateKey, sizeof(blob->PrivateKey), NULL))) {
+		GMAPIerr(GMAPI_F_EC_KEY_SET_ECCPRIVATEKEYBLOB, ERR_R_BN_LIB);
+		goto end;
+	}
+
+	if (!EC_KEY_set_private_key(ec_key, d)) {
+		GMAPIerr(GMAPI_F_EC_KEY_SET_ECCPRIVATEKEYBLOB, GMAPI_R_INVALID_PRIVATE_KEY);
+		goto end;
+	}
+
+	ret = 1;
+end:
+	BN_clear_free(d);
+	return ret;
+}
+
+int EC_KEY_get_ECCPRIVATEKEYBLOB(EC_KEY *ec_key, ECCPRIVATEKEYBLOB *blob)
+{
+	const BIGNUM *d;
+
+	if (EC_GROUP_get_degree(EC_KEY_get0_group(ec_key)) > ECC_MAX_MODULUS_BITS_LEN) {
+		GMAPIerr(GMAPI_F_EC_KEY_GET_ECCPRIVATEKEYBLOB, GMAPI_R_INVALID_KEY_LENGTH);
+		return 0;
+	}
+
+	if (!(d = EC_KEY_get0_private_key(ec_key))) {
+		GMAPIerr(GMAPI_F_EC_KEY_GET_ECCPRIVATEKEYBLOB, GMAPI_R_INVALID_EC_KEY);
+		return 0;
+	}
+
+	memset(blob, 0, sizeof(*blob));
+
+	blob->BitLen = EC_GROUP_get_degree(EC_KEY_get0_group(ec_key));
+
+	if (!BN_bn2bin(d, blob->PrivateKey + sizeof(blob->PrivateKey) - BN_num_bytes(d))) {
+		GMAPIerr(GMAPI_F_EC_KEY_GET_ECCPRIVATEKEYBLOB, ERR_R_BN_LIB);
+		return 0;
+	}
+
+	return 1;
+}
+
+# ifndef OPENSSL_NO_SM2
+SM2CiphertextValue *SM2CiphertextValue_new_from_ECCCIPHERBLOB(
+	const ECCCIPHERBLOB *blob)
+{
+	SM2CiphertextValue *ret = NULL;
+
+	if (!(ret = SM2CiphertextValue_new())) {
+		GMAPIerr(GMAPI_F_SM2CIPHERTEXTVALUE_NEW_FROM_ECCCIPHERBLOB,
+			GMAPI_R_MALLOC_FAILED);
+		return NULL;
+	}
+
+	if (!SM2CiphertextValue_set_ECCCIPHERBLOB(ret, blob)) {
+		GMAPIerr(GMAPI_F_SM2CIPHERTEXTVALUE_NEW_FROM_ECCCIPHERBLOB,
+			GMAPI_R_INVALID_EC_PUBLIC_KEY);
+		SM2CiphertextValue_free(ret);
+		return NULL;
+	}
+
+	return ret;
+}
+
+int SM2CiphertextValue_set_ECCCIPHERBLOB(SM2CiphertextValue *cv,
+	const ECCCIPHERBLOB *blob)
+{
+	int ret = 0;
+
+	if (!cv || !blob) {
+		return 0;
+	}
+
+	if (!BN_bin2bn(blob->XCoordinate, sizeof(blob->XCoordinate),
+		cv->xCoordinate)) {
+		GMAPIerr(GMAPI_F_SM2CIPHERTEXTVALUE_SET_ECCCIPHERBLOB, ERR_R_BN_LIB);
+		goto end;
+	}
+
+	if (!BN_bin2bn(blob->YCoordinate, sizeof(blob->YCoordinate),
+		cv->yCoordinate)) {
+		GMAPIerr(GMAPI_F_SM2CIPHERTEXTVALUE_SET_ECCCIPHERBLOB, ERR_R_BN_LIB);
+		goto end;
+	}
+
+	if (!ASN1_OCTET_STRING_set(cv->hash, blob->HASH, sizeof(blob->HASH))) {
+		GMAPIerr(GMAPI_F_SM2CIPHERTEXTVALUE_SET_ECCCIPHERBLOB, ERR_R_ASN1_LIB);
+		goto end;
+	}
+
+	if (!ASN1_OCTET_STRING_set(cv->ciphertext, blob->Cipher,
+		blob->CipherLen)) {
+		GMAPIerr(GMAPI_F_SM2CIPHERTEXTVALUE_SET_ECCCIPHERBLOB,
+			GMAPI_R_INVALID_CIPHERTEXT_LENGTH);
+		goto end;
+	}
+
+	ret = 1;
+end:
+	return ret;
+}
+
+int SM2CiphertextValue_get_ECCCIPHERBLOB(const SM2CiphertextValue *cv,
+	ECCCIPHERBLOB *blob)
+{
+	if (BN_num_bits(cv->xCoordinate) > ECC_MAX_XCOORDINATE_BITS_LEN ||
+		BN_num_bits(cv->yCoordinate) > ECC_MAX_YCOORDINATE_BITS_LEN) {
+		GMAPIerr(GMAPI_F_SM2CIPHERTEXTVALUE_GET_ECCCIPHERBLOB,
+			GMAPI_R_INVALID_CIPHERTEXT_POINT);
+		return 0;
+	}
+
+	if (ASN1_STRING_length(cv->hash) != sizeof(blob->HASH)) {
+		GMAPIerr(GMAPI_F_SM2CIPHERTEXTVALUE_GET_ECCCIPHERBLOB,
+			GMAPI_R_INVALID_CIPHERTEXT_LENGTH);
+		return 0;
+	}
+
+	if (blob->CipherLen < (unsigned int)ASN1_STRING_length(cv->ciphertext)) {
+		GMAPIerr(GMAPI_F_SM2CIPHERTEXTVALUE_GET_ECCCIPHERBLOB,
+			GMAPI_R_BUFFER_TOO_SMALL);
+		return 0;
+		return 0;
+	}
+
+	if (!BN_bn2bin(cv->xCoordinate, blob->XCoordinate +
+		sizeof(blob->XCoordinate) - BN_num_bytes(cv->xCoordinate))) {
+		GMAPIerr(GMAPI_F_SM2CIPHERTEXTVALUE_GET_ECCCIPHERBLOB, ERR_R_BN_LIB);
+		return 0;
+	}
+	if (!BN_bn2bin(cv->yCoordinate, blob->YCoordinate +
+		sizeof(blob->YCoordinate) - BN_num_bytes(cv->yCoordinate))) {
+		GMAPIerr(GMAPI_F_SM2CIPHERTEXTVALUE_GET_ECCCIPHERBLOB, ERR_R_BN_LIB);
+		return 0;
+	}
+
+	memcpy(blob->HASH, ASN1_STRING_get0_data(cv->hash),
+		ASN1_STRING_length(cv->hash));
+
+	blob->CipherLen = ASN1_STRING_length(cv->ciphertext);
+	memcpy(blob->Cipher, ASN1_STRING_get0_data(cv->ciphertext),
+		ASN1_STRING_length(cv->ciphertext));
+
+	return 1;
+}
+# endif /* OPENSSL_NO_SM2 */
+
+ECDSA_SIG *ECDSA_SIG_new_from_ECCSIGNATUREBLOB(const ECCSIGNATUREBLOB *blob)
+{
+	ECDSA_SIG *ret = NULL;
+
+	if (!(ret = ECDSA_SIG_new())) {
+		GMAPIerr(GMAPI_F_ECDSA_SIG_NEW_FROM_ECCSIGNATUREBLOB,
+			GMAPI_R_MALLOC_FAILED);
+		return NULL;
+	}
+
+	if (!ECDSA_SIG_set_ECCSIGNATUREBLOB(ret, blob)) {
+		GMAPIerr(GMAPI_F_ECDSA_SIG_NEW_FROM_ECCSIGNATUREBLOB,
+			GMAPI_R_INVALID_SIGNATURE);
+		ECDSA_SIG_free(ret);
+		return NULL;
+	}
+
+	return ret;
+}
+
+int ECDSA_SIG_set_ECCSIGNATUREBLOB(ECDSA_SIG *sig, const ECCSIGNATUREBLOB *blob)
+{
+//	OPENSSL_assert(sig->r);
+//	OPENSSL_assert(sig->s);
+
+	if (!(sig->r = BN_bin2bn(blob->r, sizeof(blob->r), sig->r))) {
+		GMAPIerr(GMAPI_F_ECDSA_SIG_SET_ECCSIGNATUREBLOB, ERR_R_BN_LIB);
+		return 0;
+	}
+
+	if (!(sig->s = BN_bin2bn(blob->s, sizeof(blob->s), sig->s))) {
+		GMAPIerr(GMAPI_F_ECDSA_SIG_SET_ECCSIGNATUREBLOB, ERR_R_BN_LIB);
+		return 0;
+	}
+
+	return 1;
+}
+
+int ECDSA_SIG_get_ECCSIGNATUREBLOB(const ECDSA_SIG *sig, ECCSIGNATUREBLOB *blob)
+{
+	if (((size_t)BN_num_bytes(sig->r) > sizeof(blob->r))
+		|| ((size_t)BN_num_bytes(sig->s) > sizeof(blob->s))) {
+		GMAPIerr(GMAPI_F_ECDSA_SIG_GET_ECCSIGNATUREBLOB, GMAPI_R_INVALID_BIGNUM_LENGTH);
+		return 0;
+	}
+
+	if (!BN_bn2bin(sig->r, blob->r + sizeof(blob->r) - BN_num_bytes(sig->r))) {
+		GMAPIerr(GMAPI_F_ECDSA_SIG_GET_ECCSIGNATUREBLOB, ERR_R_BN_LIB);
+		return 0;
+	}
+
+	if (!BN_bn2bin(sig->s, blob->s + sizeof(blob->s) - BN_num_bytes(sig->s))) {
+		GMAPIerr(GMAPI_F_ECDSA_SIG_GET_ECCSIGNATUREBLOB, ERR_R_BN_LIB);
+		return 0;
+	}
+
+	return 1;
+}
+
+int ECCPRIVATEKEYBLOB_set_private_key(ECCPRIVATEKEYBLOB *blob,
+	const BIGNUM *priv_key)
+{
+	if (BN_num_bytes(priv_key) > 32) {
+		GMAPIerr(GMAPI_F_ECCPRIVATEKEYBLOB_SET_PRIVATE_KEY,
+			GMAPI_R_INVALID_SM2_PRIVATE_KEY);
+		return 0;
+	}
+
+	blob->BitLen = 256;
+	memset(blob->PrivateKey, 0, sizeof(blob->PrivateKey));
+	BN_bn2bin(priv_key, blob->PrivateKey + sizeof(blob->PrivateKey)
+		- BN_num_bytes(priv_key));
+	return 1;
+}
+
+# ifndef OPENSSL_NO_ECIES
+ECIES_CIPHERTEXT_VALUE *ECIES_CIPHERTEXT_VALUE_new_from_ECCCIPHERBLOB(const ECCCIPHERBLOB *blob)
+{
+	ECIES_CIPHERTEXT_VALUE *ret = NULL;
+
+	if (!(ret = ECIES_CIPHERTEXT_VALUE_new())) {
+		GMAPIerr(GMAPI_F_ECIES_CIPHERTEXT_VALUE_NEW_FROM_ECCCIPHERBLOB,
+			ERR_R_MALLOC_FAILURE);
+		return NULL;
+	}
+
+	if (!ECIES_CIPHERTEXT_VALUE_set_ECCCIPHERBLOB(ret, blob)) {
+		GMAPIerr(GMAPI_F_ECIES_CIPHERTEXT_VALUE_NEW_FROM_ECCCIPHERBLOB,
+			ERR_R_GMAPI_LIB);
+		ECIES_CIPHERTEXT_VALUE_free(ret);
+		return NULL;
+	}
+
+	return ret;
+}
+
+int ECIES_CIPHERTEXT_VALUE_set_ECCCIPHERBLOB(ECIES_CIPHERTEXT_VALUE *cv, const ECCCIPHERBLOB *blob)
+{
+	int ret = 0;
+	BIGNUM *x = NULL;
+	BIGNUM *y = NULL;
+	EC_GROUP *group = NULL;
+	EC_POINT *point = NULL;
+	BN_CTX *bn_ctx = NULL;
+	size_t len;
+
+	if (!cv || !blob) {
+		GMAPIerr(GMAPI_F_ECIES_CIPHERTEXT_VALUE_SET_ECCCIPHERBLOB,
+			ERR_R_PASSED_NULL_PARAMETER);
+		return 0;
+	}
+
+	if (!(x = BN_bin2bn(blob->XCoordinate, sizeof(blob->XCoordinate), NULL))) {
+		GMAPIerr(GMAPI_F_ECIES_CIPHERTEXT_VALUE_SET_ECCCIPHERBLOB, ERR_R_BN_LIB);
+		goto end;
+	}
+	if (!(y = BN_bin2bn(blob->YCoordinate, sizeof(blob->YCoordinate), NULL))) {
+		GMAPIerr(GMAPI_F_ECIES_CIPHERTEXT_VALUE_SET_ECCCIPHERBLOB, ERR_R_BN_LIB);
+		goto end;
+	}
+	if (!(group = EC_GROUP_new_by_curve_name(NID_sm2p256v1))) {
+		GMAPIerr(GMAPI_F_ECIES_CIPHERTEXT_VALUE_SET_ECCCIPHERBLOB, ERR_R_EC_LIB);
+		goto end;
+	}
+	if (!(point = EC_POINT_new(group))) {
+		GMAPIerr(GMAPI_F_ECIES_CIPHERTEXT_VALUE_SET_ECCCIPHERBLOB, ERR_R_MALLOC_FAILURE);
+		goto end;
+	}
+	if (!(bn_ctx = BN_CTX_new())) {
+		GMAPIerr(GMAPI_F_ECIES_CIPHERTEXT_VALUE_SET_ECCCIPHERBLOB, ERR_R_MALLOC_FAILURE);
+		goto end;
+	}
+	if (!EC_POINT_set_affine_coordinates_GFp(group, point, x, y, bn_ctx)) {
+		GMAPIerr(GMAPI_F_ECIES_CIPHERTEXT_VALUE_SET_ECCCIPHERBLOB, ERR_R_EC_LIB);
+		goto end;
+	}
+
+	if (!(len = EC_POINT_point2oct(group, point, POINT_CONVERSION_UNCOMPRESSED, NULL, 0, bn_ctx))) {
+		GMAPIerr(GMAPI_F_ECIES_CIPHERTEXT_VALUE_SET_ECCCIPHERBLOB, ERR_R_EC_LIB);
+		goto end;
+	}
+	if (!ASN1_OCTET_STRING_set(cv->ephem_point, NULL, (int)len)) {
+		GMAPIerr(GMAPI_F_ECIES_CIPHERTEXT_VALUE_SET_ECCCIPHERBLOB, ERR_R_MALLOC_FAILURE);
+		goto end;
+	}
+	if (EC_POINT_point2oct(group, point, POINT_CONVERSION_UNCOMPRESSED, cv->ephem_point->data, len, bn_ctx) != len) {
+		GMAPIerr(GMAPI_F_ECIES_CIPHERTEXT_VALUE_SET_ECCCIPHERBLOB, ERR_R_EC_LIB);
+		goto end;
+	}
+	if (!ASN1_OCTET_STRING_set(cv->ciphertext, blob->Cipher, blob->CipherLen)) {
+		GMAPIerr(GMAPI_F_ECIES_CIPHERTEXT_VALUE_SET_ECCCIPHERBLOB, ERR_R_MALLOC_FAILURE);
+		goto end;
+	}
+	if (!ASN1_OCTET_STRING_set(cv->mactag, blob->HASH, sizeof(blob->HASH))) {
+		GMAPIerr(GMAPI_F_ECIES_CIPHERTEXT_VALUE_SET_ECCCIPHERBLOB, ERR_R_MALLOC_FAILURE);
+		goto end;
+	}
+
+	ret = 1;
+
+end:
+	BN_free(x);
+	BN_free(y);
+	EC_GROUP_free(group);
+	EC_POINT_free(point);
+	BN_CTX_free(bn_ctx);
+	return ret;
+}
+
+int ECIES_CIPHERTEXT_VALUE_get_ECCCIPHERBLOB(const ECIES_CIPHERTEXT_VALUE *cv, ECCCIPHERBLOB *blob)
+{
+	int ret = 0;
+	EC_GROUP *group = NULL;
+	EC_POINT *point = NULL;
+	BIGNUM *x = NULL;
+	BIGNUM *y = NULL;
+	BN_CTX *bn_ctx = NULL;
+
+	if (!(group = EC_GROUP_new_by_curve_name(NID_sm2p256v1))) {
+		GMAPIerr(GMAPI_F_ECIES_CIPHERTEXT_VALUE_GET_ECCCIPHERBLOB, ERR_R_EC_LIB);
+		goto end;
+	}
+	if (!(point = EC_POINT_new(group))) {
+		GMAPIerr(GMAPI_F_ECIES_CIPHERTEXT_VALUE_GET_ECCCIPHERBLOB, ERR_R_EC_LIB);
+		goto end;
+	}
+	if (!(x = BN_new())) {
+		GMAPIerr(GMAPI_F_ECIES_CIPHERTEXT_VALUE_GET_ECCCIPHERBLOB, ERR_R_MALLOC_FAILURE);
+		goto end;
+	}
+	if (!(y = BN_new())) {
+		GMAPIerr(GMAPI_F_ECIES_CIPHERTEXT_VALUE_GET_ECCCIPHERBLOB, ERR_R_MALLOC_FAILURE);
+		goto end;
+	}
+	if (!(bn_ctx = BN_CTX_new())) {
+		GMAPIerr(GMAPI_F_ECIES_CIPHERTEXT_VALUE_GET_ECCCIPHERBLOB, ERR_R_MALLOC_FAILURE);
+		goto end;
+	}
+
+	if (!EC_POINT_oct2point(group, point, ASN1_STRING_get0_data(cv->ephem_point), ASN1_STRING_length(cv->ephem_point), bn_ctx)) {
+		GMAPIerr(GMAPI_F_ECIES_CIPHERTEXT_VALUE_GET_ECCCIPHERBLOB, ERR_R_EC_LIB);
+		goto end;
+	}
+	if (!EC_POINT_get_affine_coordinates_GFp(group, point, x, y, bn_ctx)) {
+		GMAPIerr(GMAPI_F_ECIES_CIPHERTEXT_VALUE_GET_ECCCIPHERBLOB, ERR_R_EC_LIB);
+		goto end;
+	}
+
+	if ((size_t)BN_num_bytes(x) > sizeof(blob->XCoordinate)) {
+		GMAPIerr(GMAPI_F_ECIES_CIPHERTEXT_VALUE_GET_ECCCIPHERBLOB, GMAPI_R_INVALID_SKF_EC_CIPHERTEXT);
+		goto end;
+	}
+	BN_bn2bin(x, blob->XCoordinate + sizeof(blob->XCoordinate) - BN_num_bytes(x));
+
+	if ((size_t)BN_num_bytes(y) > sizeof(blob->YCoordinate)) {
+		GMAPIerr(GMAPI_F_ECIES_CIPHERTEXT_VALUE_GET_ECCCIPHERBLOB, GMAPI_R_INVALID_SKF_EC_CIPHERTEXT);
+		goto end;
+	}
+	BN_bn2bin(y, blob->YCoordinate + sizeof(blob->YCoordinate) - BN_num_bytes(y));
+
+	if (ASN1_STRING_length(cv->mactag) != sizeof(blob->HASH)) {
+		GMAPIerr(GMAPI_F_ECIES_CIPHERTEXT_VALUE_GET_ECCCIPHERBLOB, GMAPI_R_INVALID_SKF_EC_CIPHERTEXT);
+		goto end;
+	}
+	memcpy(blob->HASH, ASN1_STRING_get0_data(cv->mactag), ASN1_STRING_length(cv->mactag));
+
+	//FIXME: check input ?
+	blob->CipherLen = ASN1_STRING_length(cv->ciphertext);
+	memcpy(blob->Cipher, ASN1_STRING_get0_data(cv->ciphertext),
+		ASN1_STRING_length(cv->ciphertext));
+
+
+end:
+	EC_GROUP_free(group);
+	EC_POINT_free(point);
+	BN_free(x);
+	BN_free(y);
+	BN_CTX_free(bn_ctx);
+	return ret;
+}
+# endif /* OPENSSL_NO_ECIES */
+
+ECCCIPHERBLOB *d2i_ECCCIPHERBLOB(ECCCIPHERBLOB **a, const unsigned char **pp, long length)
+{
+	ECCCIPHERBLOB *ret = NULL;
+	ECCCIPHERBLOB *blob = NULL;
+	SM2CiphertextValue *cv = NULL;
+
+	/* FIXME: set `a` */
+	(void)a;
+
+	if (!(cv = d2i_SM2CiphertextValue(NULL, pp, length))) {
+		GMAPIerr(GMAPI_F_D2I_ECCCIPHERBLOB, ERR_R_SM2_LIB);
+		goto end;
+	}
+
+	if (!(blob = OPENSSL_malloc(sizeof(ECCCIPHERBLOB) - 1 + ASN1_STRING_length(cv->ciphertext)))) {
+		GMAPIerr(GMAPI_F_D2I_ECCCIPHERBLOB, ERR_R_MALLOC_FAILURE);
+		goto end;
+	}
+	blob->CipherLen = ASN1_STRING_length(cv->ciphertext);
+
+	if (!SM2CiphertextValue_get_ECCCIPHERBLOB(cv, blob)) {
+		GMAPIerr(GMAPI_F_D2I_ECCCIPHERBLOB, ERR_R_GMAPI_LIB);
+		goto end;
+	}
+
+			
+	ret = blob;
+	blob = NULL;
+
+end:
+	OPENSSL_free(blob);
+	SM2CiphertextValue_free(cv);
+	return ret;
+}
+
+int i2d_ECCCIPHERBLOB(ECCCIPHERBLOB *a, unsigned char **pp)
+{
+	int ret;
+	SM2CiphertextValue *cv = NULL;
+
+	if (!(cv = SM2CiphertextValue_new_from_ECCCIPHERBLOB(a))) {
+		GMAPIerr(GMAPI_F_I2D_ECCCIPHERBLOB, GMAPI_R_INVALID_SKF_CIPHERTEXT);
+		return 0;
+	}
+
+	ret = i2d_SM2CiphertextValue(cv, pp);
+	SM2CiphertextValue_free(cv);
+	return ret;
+}
+
+ECCSIGNATUREBLOB *d2i_ECCSIGNATUREBLOB(ECCSIGNATUREBLOB **a, const unsigned char **pp, long length)
+{
+	ECCSIGNATUREBLOB *ret = NULL;
+	ECCSIGNATUREBLOB *blob = NULL;
+	ECDSA_SIG *sig = NULL;
+
+	/* FIXME: set `a` */
+	(void)a;
+
+	if (!(sig = d2i_ECDSA_SIG(NULL, pp, length))) {
+		GMAPIerr(GMAPI_F_D2I_ECCSIGNATUREBLOB, ERR_R_EC_LIB);
+		goto end;
+	}
+
+	if (!(blob = OPENSSL_malloc(sizeof(ECCSIGNATUREBLOB)))) {
+		GMAPIerr(GMAPI_F_D2I_ECCSIGNATUREBLOB, ERR_R_MALLOC_FAILURE);
+		goto end;
+	}
+
+	if (!ECDSA_SIG_get_ECCSIGNATUREBLOB(sig, blob)) {
+		GMAPIerr(GMAPI_F_D2I_ECCSIGNATUREBLOB, ERR_R_GMAPI_LIB);
+		goto end;
+	}
+
+	// a not used 					
+
+	ret = blob;
+	blob = NULL;
+
+end:
+	OPENSSL_free(blob);
+	ECDSA_SIG_free(sig);
+	return ret;
+}
+
+int i2d_ECCSIGNATUREBLOB(ECCSIGNATUREBLOB *a, unsigned char **pp)
+{
+	int ret;
+	ECDSA_SIG *sig = NULL;
+
+	if (!(sig = ECDSA_SIG_new_from_ECCSIGNATUREBLOB(a))) {
+		GMAPIerr(GMAPI_F_I2D_ECCSIGNATUREBLOB, ERR_R_GMAPI_LIB);
+		return 0;
+	}
+
+	ret = i2d_ECDSA_SIG(sig, pp);
+	ECDSA_SIG_free(sig);
+	return ret;
+}
+#endif
diff --git a/crypto/gmapi/gmapi_skf_rsa.c b/crypto/gmapi/gmapi_skf_rsa.c
new file mode 100644
index 000000000..2532663d0
--- /dev/null
+++ b/crypto/gmapi/gmapi_skf_rsa.c
@@ -0,0 +1,402 @@
+/* ====================================================================
+ * Copyright (c) 2015-2016 The GmSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the GmSSL Project.
+ *    (http://gmssl.org/)"
+ *
+ * 4. The name "GmSSL Project" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    guanzhi1980@gmail.com.
+ *
+ * 5. Products derived from this software may not be called "GmSSL"
+ *    nor may "GmSSL" appear in their names without prior written
+ *    permission of the GmSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the GmSSL Project
+ *    (http://gmssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE GmSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE GmSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ */
+
+#include 
+#include "internal/cryptlib.h"
+
+#if !defined(OPENSSL_NO_SKF) && !defined(OPENSSL_NO_RSA)
+# include 
+# include 
+# include 
+# include 
+
+/* Wrapper functions */
+
+RSA *RSA_new_from_RSAPUBLICKEYBLOB(const RSAPUBLICKEYBLOB *blob)
+{
+	RSA *ret = NULL;
+	RSA *rsa = NULL;
+
+	if (!blob) {
+		GMAPIerr(GMAPI_F_RSA_NEW_FROM_RSAPUBLICKEYBLOB,
+			ERR_R_PASSED_NULL_PARAMETER);
+		return NULL;
+	}
+
+	if (!(rsa = RSA_new())) {
+		GMAPIerr(GMAPI_F_RSA_NEW_FROM_RSAPUBLICKEYBLOB,
+			ERR_R_MALLOC_FAILURE);
+		return NULL;
+	}
+
+	if (!RSA_set_RSAPUBLICKEYBLOB(rsa, blob)) {
+		GMAPIerr(GMAPI_F_RSA_NEW_FROM_RSAPUBLICKEYBLOB,
+			GMAPI_R_INVALID_RSA_PUBLIC_KEY);
+		goto end;
+	}
+
+	ret = rsa;
+	rsa = NULL;
+
+end:
+	RSA_free(rsa);
+	return ret;
+}
+
+int RSA_set_RSAPUBLICKEYBLOB(RSA *rsa, const RSAPUBLICKEYBLOB *blob)
+{
+	int ret = 0;
+	BIGNUM *n = NULL;
+	BIGNUM *e = NULL;
+
+	if (!rsa || !blob) {
+		GMAPIerr(GMAPI_F_RSA_SET_RSAPUBLICKEYBLOB,
+			ERR_R_PASSED_NULL_PARAMETER);
+		return 0;
+	}
+
+	if ((blob->BitLen < OPENSSL_RSA_FIPS_MIN_MODULUS_BITS)
+		|| (blob->BitLen > sizeof(blob->Modulus) * 8)
+		|| (blob->BitLen % 8 != 0)) {
+		GMAPIerr(GMAPI_F_RSA_SET_RSAPUBLICKEYBLOB,
+			GMAPI_R_INVALID_RSA_KEY_LENGTH);
+		return 0;
+	}
+
+	if (!(n = BN_bin2bn(blob->Modulus, sizeof(blob->Modulus), NULL))) {
+		GMAPIerr(GMAPI_F_RSA_SET_RSAPUBLICKEYBLOB,
+			GMAPI_R_INVALID_RSA_PUBLIC_KEY);
+		goto end;
+	}
+
+	if (!(e = BN_bin2bn(blob->PublicExponent,
+		sizeof(blob->PublicExponent), NULL))) {
+		GMAPIerr(GMAPI_F_RSA_SET_RSAPUBLICKEYBLOB,
+			GMAPI_R_INVALID_RSA_PUBLIC_KEY);
+		goto end;
+	}
+
+	if (!RSA_set0_key(rsa, n, e, NULL)) {
+		GMAPIerr(GMAPI_F_RSA_SET_RSAPUBLICKEYBLOB,
+			GMAPI_R_INVALID_RSA_PUBLIC_KEY);
+		goto end;
+	}
+	n = NULL;
+	e = NULL;
+
+	ret = 1;
+
+end:
+	BN_free(n);
+	BN_free(e);
+	return ret;
+}
+
+int RSA_get_RSAPUBLICKEYBLOB(RSA *rsa, RSAPUBLICKEYBLOB *blob)
+{
+	const BIGNUM *n;
+	const BIGNUM *e;
+
+	if (!rsa || !blob) {
+		GMAPIerr(GMAPI_F_RSA_GET_RSAPUBLICKEYBLOB,
+			ERR_R_PASSED_NULL_PARAMETER);
+		return 0;
+	}
+
+	RSA_get0_key(rsa, &n, &e, NULL);
+
+	if (!n || !e) {
+		GMAPIerr(GMAPI_F_RSA_GET_RSAPUBLICKEYBLOB,
+			GMAPI_R_INVALID_RSA_PUBLIC_KEY);
+		return 0;
+	}
+
+	if (RSA_bits(rsa) > sizeof(blob->Modulus) * 8
+		|| RSA_bits(rsa) % 8 != 0) {
+		GMAPIerr(GMAPI_F_RSA_GET_RSAPUBLICKEYBLOB,
+			GMAPI_R_INVALID_RSA_PUBLIC_KEY);
+		return 0;
+	}
+
+	memset(blob, 0, sizeof(RSAPUBLICKEYBLOB));
+	blob->AlgID = SGD_RSA;
+	blob->BitLen = RSA_bits(rsa);
+
+	if (BN_bn2bin(n, blob->Modulus +
+		sizeof(blob->Modulus) - BN_num_bytes(n)) <= 0) {
+		GMAPIerr(GMAPI_F_RSA_GET_RSAPUBLICKEYBLOB,
+			GMAPI_R_ENCODE_RSA_PUBLIC_KEY_FAILED);
+		return 0;
+	}
+
+	if (BN_bn2bin(e, blob->PublicExponent +
+		sizeof(blob->PublicExponent) - BN_num_bytes(e)) <= 0) {
+		GMAPIerr(GMAPI_F_RSA_GET_RSAPUBLICKEYBLOB,
+			GMAPI_R_ENCODE_RSA_PUBLIC_KEY_FAILED);
+		return 0;
+	}
+
+	return 1;
+}
+
+RSA *RSA_new_from_RSAPRIVATEKEYBLOB(const RSAPRIVATEKEYBLOB *blob)
+{
+	RSA *ret = NULL;
+	RSA *rsa = NULL;
+
+	if (!blob) {
+		GMAPIerr(GMAPI_F_RSA_NEW_FROM_RSAPRIVATEKEYBLOB,
+			ERR_R_PASSED_NULL_PARAMETER);
+		return NULL;
+	}
+
+	if (!(rsa = RSA_new())) {
+		GMAPIerr(GMAPI_F_RSA_NEW_FROM_RSAPRIVATEKEYBLOB,
+			ERR_R_MALLOC_FAILURE);
+		return NULL;
+	}
+
+	if (!RSA_set_RSAPRIVATEKEYBLOB(rsa, blob)) {
+		GMAPIerr(GMAPI_F_RSA_NEW_FROM_RSAPRIVATEKEYBLOB,
+			GMAPI_R_INVALID_RSA_PRIVATE_KEY);
+		goto end;
+	}
+
+	ret = rsa;
+	rsa = NULL;
+
+end:
+	RSA_free(rsa);
+	return ret;
+}
+
+int RSA_set_RSAPRIVATEKEYBLOB(RSA *rsa, const RSAPRIVATEKEYBLOB *blob)
+{
+	int ret = 0;
+	BIGNUM *n = NULL;
+	BIGNUM *e = NULL;
+	BIGNUM *d = NULL;
+	BIGNUM *p = NULL;
+	BIGNUM *q = NULL;
+	BIGNUM *dmp1 = NULL;
+	BIGNUM *dmq1 = NULL;
+	BIGNUM *iqmp = NULL;
+
+	if (!rsa || !blob) {
+		GMAPIerr(GMAPI_F_RSA_SET_RSAPRIVATEKEYBLOB,
+			ERR_R_PASSED_NULL_PARAMETER);
+		return 0;
+	}
+
+	if (blob->AlgID != SGD_RSA) {
+		GMAPIerr(GMAPI_F_RSA_SET_RSAPRIVATEKEYBLOB,
+			GMAPI_R_INVALID_ALGOR);
+		return 0;
+	}
+
+	if (blob->BitLen < OPENSSL_RSA_FIPS_MIN_MODULUS_BITS
+		|| blob->BitLen > sizeof(blob->Modulus) * 8
+		|| blob->BitLen % 8 != 0
+		|| blob->BitLen % 16 != 0) {
+		GMAPIerr(GMAPI_F_RSA_SET_RSAPRIVATEKEYBLOB,
+			ERR_R_PASSED_NULL_PARAMETER);
+		return 0;
+	}
+
+	if (!(n = BN_bin2bn(blob->Modulus, sizeof(blob->Modulus), NULL))
+		|| !(e = BN_bin2bn(blob->PublicExponent, sizeof(blob->PublicExponent), NULL))
+		|| !(d = BN_bin2bn(blob->PrivateExponent, sizeof(blob->PrivateExponent), NULL))
+		|| !(p = BN_bin2bn(blob->Prime1, sizeof(blob->Prime1), NULL))
+		|| !(q = BN_bin2bn(blob->Prime2, sizeof(blob->Prime2), NULL))
+		|| !(dmp1 = BN_bin2bn(blob->Prime1Exponent, sizeof(blob->Prime1Exponent), NULL))
+		|| !(dmq1 = BN_bin2bn(blob->Prime2Exponent, sizeof(blob->Prime2Exponent), NULL))
+		|| !(iqmp = BN_bin2bn(blob->Coefficient, sizeof(blob->Coefficient), NULL))) {
+		GMAPIerr(GMAPI_F_RSA_SET_RSAPRIVATEKEYBLOB, ERR_R_BN_LIB);
+		goto end;
+	}
+
+	if (!RSA_set0_key(rsa, n, e, d)) {
+		GMAPIerr(GMAPI_F_RSA_SET_RSAPRIVATEKEYBLOB,
+			GMAPI_R_INVALID_RSA_PRIVATE_KEY);
+		goto end;
+	}
+	n = NULL;
+	e = NULL;
+	d = NULL;
+
+	if (!RSA_set0_factors(rsa, p, q)) {
+		GMAPIerr(GMAPI_F_RSA_SET_RSAPRIVATEKEYBLOB,
+			GMAPI_R_INVALID_RSA_PRIVATE_KEY);
+		goto end;
+	}
+	p = NULL;
+	q = NULL;
+
+	if (!RSA_set0_crt_params(rsa, dmp1, dmq1, iqmp)) {
+		GMAPIerr(GMAPI_F_RSA_SET_RSAPRIVATEKEYBLOB,
+			GMAPI_R_INVALID_RSA_PRIVATE_KEY);
+		goto end;
+	}
+	dmp1 = NULL;
+	dmq1 = NULL;
+	iqmp = NULL;
+
+	ret = 1;
+
+end:
+	BN_free(n);
+	BN_free(e);
+	BN_free(d);
+	BN_free(p);
+	BN_free(q);
+	BN_free(dmp1);
+	BN_free(dmq1);
+	BN_free(iqmp);
+	return ret;
+}
+
+int RSA_get_RSAPRIVATEKEYBLOB(RSA *rsa, RSAPRIVATEKEYBLOB *blob)
+{
+	const BIGNUM *n;
+	const BIGNUM *e;
+	const BIGNUM *d;
+	const BIGNUM *p;
+	const BIGNUM *q;
+	const BIGNUM *dmp1;
+	const BIGNUM *dmq1;
+	const BIGNUM *iqmp;
+
+	if (!rsa || !blob) {
+		GMAPIerr(GMAPI_F_RSA_GET_RSAPRIVATEKEYBLOB,
+			ERR_R_PASSED_NULL_PARAMETER);
+		return 0;
+	}
+
+	if (RSA_bits(rsa) > sizeof(blob->Modulus) * 8
+		|| RSA_bits(rsa) % 8 != 0) {
+		GMAPIerr(GMAPI_F_RSA_GET_RSAPRIVATEKEYBLOB,
+			ERR_R_PASSED_NULL_PARAMETER);
+		return 0;
+	}
+
+	RSA_get0_key(rsa, &n, &e, &d);
+	RSA_get0_factors(rsa, &p, &q);
+	RSA_get0_crt_params(rsa, &dmp1, &dmq1, &iqmp);
+
+	if (!n || !e || !d) {
+		GMAPIerr(GMAPI_F_RSA_GET_RSAPRIVATEKEYBLOB,
+			GMAPI_R_INVALID_RSA_PRIVATE_KEY);
+		return 0;
+	}
+
+	memset(blob, 0, sizeof(RSAPRIVATEKEYBLOB));
+
+	blob->AlgID = SGD_RSA;
+	blob->BitLen = RSA_bits(rsa);
+
+	if (BN_bn2bin(n, blob->Modulus +
+		sizeof(blob->Modulus) - BN_num_bytes(n)) <= 0) {
+		GMAPIerr(GMAPI_F_RSA_GET_RSAPRIVATEKEYBLOB,
+			GMAPI_R_INVALID_RSA_PRIVATE_KEY);
+		return 0;
+	}
+
+	if (BN_bn2bin(e, blob->PublicExponent +
+		sizeof(blob->PublicExponent) - BN_num_bytes(e)) <= 0) {
+		GMAPIerr(GMAPI_F_RSA_GET_RSAPRIVATEKEYBLOB,
+			GMAPI_R_INVALID_RSA_PRIVATE_KEY);
+		return 0;
+	}
+
+	if (BN_bn2bin(d, blob->PrivateExponent +
+		sizeof(blob->PrivateExponent) - BN_num_bytes(d)) <= 0) {
+		GMAPIerr(GMAPI_F_RSA_GET_RSAPRIVATEKEYBLOB,
+			GMAPI_R_INVALID_RSA_PRIVATE_KEY);
+		return 0;
+	}
+
+	if (p && BN_bn2bin(p, blob->Prime1 +
+		sizeof(blob->Prime1) - BN_num_bytes(p)) < 0) {
+		GMAPIerr(GMAPI_F_RSA_GET_RSAPRIVATEKEYBLOB,
+			GMAPI_R_INVALID_RSA_PRIVATE_KEY);
+		return 0;
+	}
+
+	if (q && BN_bn2bin(q, blob->Prime2 +
+		sizeof(blob->Prime2) - BN_num_bytes(q)) < 0) {
+		GMAPIerr(GMAPI_F_RSA_GET_RSAPRIVATEKEYBLOB,
+			GMAPI_R_INVALID_RSA_PRIVATE_KEY);
+		return 0;
+	}
+
+	if (dmp1 && BN_bn2bin(dmp1, blob->Prime1Exponent +
+		sizeof(blob->Prime1Exponent) - BN_num_bytes(dmp1)) < 0) {
+		GMAPIerr(GMAPI_F_RSA_GET_RSAPRIVATEKEYBLOB,
+			GMAPI_R_INVALID_RSA_PRIVATE_KEY);
+		return 0;
+	}
+
+	if (dmq1 && BN_bn2bin(dmq1, blob->Prime2Exponent +
+		sizeof(blob->Prime2Exponent) - BN_num_bytes(dmq1)) < 0) {
+		GMAPIerr(GMAPI_F_RSA_GET_RSAPRIVATEKEYBLOB,
+			GMAPI_R_INVALID_RSA_PRIVATE_KEY);
+		return 0;
+	}
+
+	if (iqmp && BN_bn2bin(iqmp, blob->Coefficient +
+		sizeof(blob->Coefficient) - BN_num_bytes(iqmp)) < 0) {
+		GMAPIerr(GMAPI_F_RSA_GET_RSAPRIVATEKEYBLOB,
+			GMAPI_R_INVALID_RSA_PRIVATE_KEY);
+		return 0;
+	}
+
+	return 1;
+}
+#endif
diff --git a/crypto/hmac/build.info b/crypto/hmac/build.info
new file mode 100644
index 000000000..09f67c2a0
--- /dev/null
+++ b/crypto/hmac/build.info
@@ -0,0 +1,3 @@
+LIBS=../../libcrypto
+SOURCE[../../libcrypto]=\
+        hmac.c hm_ameth.c hm_pmeth.c
diff --git a/crypto/hmac/hm_ameth.c b/crypto/hmac/hm_ameth.c
new file mode 100644
index 000000000..78ae0ea63
--- /dev/null
+++ b/crypto/hmac/hm_ameth.c
@@ -0,0 +1,125 @@
+/*
+ * Copyright 2007-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include "internal/asn1_int.h"
+
+#define HMAC_TEST_PRIVATE_KEY_FORMAT
+
+/*
+ * HMAC "ASN1" method. This is just here to indicate the maximum HMAC output
+ * length and to free up an HMAC key.
+ */
+
+static int hmac_size(const EVP_PKEY *pkey)
+{
+    return EVP_MAX_MD_SIZE;
+}
+
+static void hmac_key_free(EVP_PKEY *pkey)
+{
+    ASN1_OCTET_STRING *os = EVP_PKEY_get0(pkey);
+    if (os) {
+        if (os->data)
+            OPENSSL_cleanse(os->data, os->length);
+        ASN1_OCTET_STRING_free(os);
+    }
+}
+
+static int hmac_pkey_ctrl(EVP_PKEY *pkey, int op, long arg1, void *arg2)
+{
+    switch (op) {
+    case ASN1_PKEY_CTRL_DEFAULT_MD_NID:
+        *(int *)arg2 = NID_sha256;
+        return 1;
+
+    default:
+        return -2;
+    }
+}
+
+static int hmac_pkey_public_cmp(const EVP_PKEY *a, const EVP_PKEY *b)
+{
+    return ASN1_OCTET_STRING_cmp(EVP_PKEY_get0(a), EVP_PKEY_get0(b));
+}
+
+#ifdef HMAC_TEST_PRIVATE_KEY_FORMAT
+/*
+ * A bogus private key format for test purposes. This is simply the HMAC key
+ * with "HMAC PRIVATE KEY" in the headers. When enabled the genpkey utility
+ * can be used to "generate" HMAC keys.
+ */
+
+static int old_hmac_decode(EVP_PKEY *pkey,
+                           const unsigned char **pder, int derlen)
+{
+    ASN1_OCTET_STRING *os;
+    os = ASN1_OCTET_STRING_new();
+    if (os == NULL || !ASN1_OCTET_STRING_set(os, *pder, derlen))
+        goto err;
+    if (!EVP_PKEY_assign(pkey, EVP_PKEY_HMAC, os))
+        goto err;
+    return 1;
+
+ err:
+    ASN1_OCTET_STRING_free(os);
+    return 0;
+}
+
+static int old_hmac_encode(const EVP_PKEY *pkey, unsigned char **pder)
+{
+    int inc;
+    ASN1_OCTET_STRING *os = EVP_PKEY_get0(pkey);
+    if (pder) {
+        if (!*pder) {
+            *pder = OPENSSL_malloc(os->length);
+            if (*pder == NULL)
+                return -1;
+            inc = 0;
+        } else
+            inc = 1;
+
+        memcpy(*pder, os->data, os->length);
+
+        if (inc)
+            *pder += os->length;
+    }
+
+    return os->length;
+}
+
+#endif
+
+const EVP_PKEY_ASN1_METHOD hmac_asn1_meth = {
+    EVP_PKEY_HMAC,
+    EVP_PKEY_HMAC,
+    0,
+
+    "HMAC",
+    "OpenSSL HMAC method",
+
+    0, 0, hmac_pkey_public_cmp, 0,
+
+    0, 0, 0,
+
+    hmac_size,
+    0, 0,
+    0, 0, 0, 0, 0, 0, 0,
+
+    hmac_key_free,
+    hmac_pkey_ctrl,
+#ifdef HMAC_TEST_PRIVATE_KEY_FORMAT
+    old_hmac_decode,
+    old_hmac_encode
+#else
+    0, 0
+#endif
+};
diff --git a/crypto/hmac/hm_pmeth.c b/crypto/hmac/hm_pmeth.c
new file mode 100644
index 000000000..5b98477f9
--- /dev/null
+++ b/crypto/hmac/hm_pmeth.c
@@ -0,0 +1,210 @@
+/*
+ * Copyright 2007-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+#include 
+#include 
+#include "internal/evp_int.h"
+
+/* HMAC pkey context structure */
+
+typedef struct {
+    const EVP_MD *md;           /* MD for HMAC use */
+    ASN1_OCTET_STRING ktmp;     /* Temp storage for key */
+    HMAC_CTX *ctx;
+} HMAC_PKEY_CTX;
+
+static int pkey_hmac_init(EVP_PKEY_CTX *ctx)
+{
+    HMAC_PKEY_CTX *hctx;
+
+    hctx = OPENSSL_zalloc(sizeof(*hctx));
+    if (hctx == NULL)
+        return 0;
+    hctx->ktmp.type = V_ASN1_OCTET_STRING;
+    hctx->ctx = HMAC_CTX_new();
+    if (hctx->ctx == NULL) {
+        OPENSSL_free(hctx);
+        return 0;
+    }
+
+    ctx->data = hctx;
+    ctx->keygen_info_count = 0;
+
+    return 1;
+}
+
+static void pkey_hmac_cleanup(EVP_PKEY_CTX *ctx);
+
+static int pkey_hmac_copy(EVP_PKEY_CTX *dst, EVP_PKEY_CTX *src)
+{
+    HMAC_PKEY_CTX *sctx, *dctx;
+
+    /* allocate memory for dst->data and a new HMAC_CTX in dst->data->ctx */
+    if (!pkey_hmac_init(dst))
+        return 0;
+    sctx = EVP_PKEY_CTX_get_data(src);
+    dctx = EVP_PKEY_CTX_get_data(dst);
+    dctx->md = sctx->md;
+    if (!HMAC_CTX_copy(dctx->ctx, sctx->ctx))
+        goto err;
+    if (sctx->ktmp.data) {
+        if (!ASN1_OCTET_STRING_set(&dctx->ktmp,
+                                   sctx->ktmp.data, sctx->ktmp.length))
+            goto err;
+    }
+    return 1;
+err:
+    /* release HMAC_CTX in dst->data->ctx and memory allocated for dst->data */
+    pkey_hmac_cleanup (dst);
+    return 0;
+}
+
+static void pkey_hmac_cleanup(EVP_PKEY_CTX *ctx)
+{
+    HMAC_PKEY_CTX *hctx = EVP_PKEY_CTX_get_data(ctx);
+
+    if (hctx != NULL) {
+        HMAC_CTX_free(hctx->ctx);
+        OPENSSL_clear_free(hctx->ktmp.data, hctx->ktmp.length);
+        OPENSSL_free(hctx);
+        EVP_PKEY_CTX_set_data(ctx, NULL);
+    }
+}
+
+static int pkey_hmac_keygen(EVP_PKEY_CTX *ctx, EVP_PKEY *pkey)
+{
+    ASN1_OCTET_STRING *hkey = NULL;
+    HMAC_PKEY_CTX *hctx = ctx->data;
+    if (!hctx->ktmp.data)
+        return 0;
+    hkey = ASN1_OCTET_STRING_dup(&hctx->ktmp);
+    if (!hkey)
+        return 0;
+    EVP_PKEY_assign(pkey, EVP_PKEY_HMAC, hkey);
+
+    return 1;
+}
+
+static int int_update(EVP_MD_CTX *ctx, const void *data, size_t count)
+{
+    HMAC_PKEY_CTX *hctx = EVP_MD_CTX_pkey_ctx(ctx)->data;
+    if (!HMAC_Update(hctx->ctx, data, count))
+        return 0;
+    return 1;
+}
+
+static int hmac_signctx_init(EVP_PKEY_CTX *ctx, EVP_MD_CTX *mctx)
+{
+    HMAC_PKEY_CTX *hctx = ctx->data;
+    HMAC_CTX_set_flags(hctx->ctx,
+                       EVP_MD_CTX_test_flags(mctx, ~EVP_MD_CTX_FLAG_NO_INIT));
+    EVP_MD_CTX_set_flags(mctx, EVP_MD_CTX_FLAG_NO_INIT);
+    EVP_MD_CTX_set_update_fn(mctx, int_update);
+    return 1;
+}
+
+static int hmac_signctx(EVP_PKEY_CTX *ctx, unsigned char *sig, size_t *siglen,
+                        EVP_MD_CTX *mctx)
+{
+    unsigned int hlen;
+    HMAC_PKEY_CTX *hctx = ctx->data;
+    int l = EVP_MD_CTX_size(mctx);
+
+    if (l < 0)
+        return 0;
+    *siglen = l;
+    if (!sig)
+        return 1;
+
+    if (!HMAC_Final(hctx->ctx, sig, &hlen))
+        return 0;
+    *siglen = (size_t)hlen;
+    return 1;
+}
+
+static int pkey_hmac_ctrl(EVP_PKEY_CTX *ctx, int type, int p1, void *p2)
+{
+    HMAC_PKEY_CTX *hctx = ctx->data;
+    ASN1_OCTET_STRING *key;
+    switch (type) {
+
+    case EVP_PKEY_CTRL_SET_MAC_KEY:
+        if ((!p2 && p1 > 0) || (p1 < -1))
+            return 0;
+        if (!ASN1_OCTET_STRING_set(&hctx->ktmp, p2, p1))
+            return 0;
+        break;
+
+    case EVP_PKEY_CTRL_MD:
+        hctx->md = p2;
+        break;
+
+    case EVP_PKEY_CTRL_DIGESTINIT:
+        key = (ASN1_OCTET_STRING *)ctx->pkey->pkey.ptr;
+        if (!HMAC_Init_ex(hctx->ctx, key->data, key->length, hctx->md,
+                          ctx->engine))
+            return 0;
+        break;
+
+    default:
+        return -2;
+
+    }
+    return 1;
+}
+
+static int pkey_hmac_ctrl_str(EVP_PKEY_CTX *ctx,
+                              const char *type, const char *value)
+{
+    if (!value) {
+        return 0;
+    }
+    if (strcmp(type, "key") == 0)
+        return EVP_PKEY_CTX_str2ctrl(ctx, EVP_PKEY_CTRL_SET_MAC_KEY, value);
+    if (strcmp(type, "hexkey") == 0)
+        return EVP_PKEY_CTX_hex2ctrl(ctx, EVP_PKEY_CTRL_SET_MAC_KEY, value);
+    return -2;
+}
+
+const EVP_PKEY_METHOD hmac_pkey_meth = {
+    EVP_PKEY_HMAC,
+    0,
+    pkey_hmac_init,
+    pkey_hmac_copy,
+    pkey_hmac_cleanup,
+
+    0, 0,
+
+    0,
+    pkey_hmac_keygen,
+
+    0, 0,
+
+    0, 0,
+
+    0, 0,
+
+    hmac_signctx_init,
+    hmac_signctx,
+
+    0, 0,
+
+    0, 0,
+
+    0, 0,
+
+    0, 0,
+
+    pkey_hmac_ctrl,
+    pkey_hmac_ctrl_str
+};
diff --git a/crypto/hmac/hmac.c b/crypto/hmac/hmac.c
new file mode 100644
index 000000000..3374105cb
--- /dev/null
+++ b/crypto/hmac/hmac.c
@@ -0,0 +1,240 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+#include "hmac_lcl.h"
+
+int HMAC_Init_ex(HMAC_CTX *ctx, const void *key, int len,
+                 const EVP_MD *md, ENGINE *impl)
+{
+    int i, j, reset = 0;
+    unsigned char pad[HMAC_MAX_MD_CBLOCK];
+
+    /* If we are changing MD then we must have a key */
+    if (md != NULL && md != ctx->md && (key == NULL || len < 0))
+        return 0;
+
+    if (md != NULL) {
+        reset = 1;
+        ctx->md = md;
+    } else if (ctx->md) {
+        md = ctx->md;
+    } else {
+        return 0;
+    }
+
+    if (key != NULL) {
+        reset = 1;
+        j = EVP_MD_block_size(md);
+        OPENSSL_assert(j <= (int)sizeof(ctx->key));
+        if (j < len) {
+            if (!EVP_DigestInit_ex(ctx->md_ctx, md, impl))
+                goto err;
+            if (!EVP_DigestUpdate(ctx->md_ctx, key, len))
+                goto err;
+            if (!EVP_DigestFinal_ex(ctx->md_ctx, ctx->key,
+                                    &ctx->key_length))
+                goto err;
+        } else {
+            if (len < 0 || len > (int)sizeof(ctx->key))
+                return 0;
+            memcpy(ctx->key, key, len);
+            ctx->key_length = len;
+        }
+        if (ctx->key_length != HMAC_MAX_MD_CBLOCK)
+            memset(&ctx->key[ctx->key_length], 0,
+                   HMAC_MAX_MD_CBLOCK - ctx->key_length);
+    }
+
+    if (reset) {
+        for (i = 0; i < HMAC_MAX_MD_CBLOCK; i++)
+            pad[i] = 0x36 ^ ctx->key[i];
+        if (!EVP_DigestInit_ex(ctx->i_ctx, md, impl))
+            goto err;
+        if (!EVP_DigestUpdate(ctx->i_ctx, pad, EVP_MD_block_size(md)))
+            goto err;
+
+        for (i = 0; i < HMAC_MAX_MD_CBLOCK; i++)
+            pad[i] = 0x5c ^ ctx->key[i];
+        if (!EVP_DigestInit_ex(ctx->o_ctx, md, impl))
+            goto err;
+        if (!EVP_DigestUpdate(ctx->o_ctx, pad, EVP_MD_block_size(md)))
+            goto err;
+    }
+    if (!EVP_MD_CTX_copy_ex(ctx->md_ctx, ctx->i_ctx))
+        goto err;
+    return 1;
+ err:
+    return 0;
+}
+
+#if OPENSSL_API_COMPAT < 0x10100000L
+int HMAC_Init(HMAC_CTX *ctx, const void *key, int len, const EVP_MD *md)
+{
+    if (key && md)
+        HMAC_CTX_reset(ctx);
+    return HMAC_Init_ex(ctx, key, len, md, NULL);
+}
+#endif
+
+int HMAC_Update(HMAC_CTX *ctx, const unsigned char *data, size_t len)
+{
+    if (!ctx->md)
+        return 0;
+    return EVP_DigestUpdate(ctx->md_ctx, data, len);
+}
+
+int HMAC_Final(HMAC_CTX *ctx, unsigned char *md, unsigned int *len)
+{
+    unsigned int i;
+    unsigned char buf[EVP_MAX_MD_SIZE];
+
+    if (!ctx->md)
+        goto err;
+
+    if (!EVP_DigestFinal_ex(ctx->md_ctx, buf, &i))
+        goto err;
+    if (!EVP_MD_CTX_copy_ex(ctx->md_ctx, ctx->o_ctx))
+        goto err;
+    if (!EVP_DigestUpdate(ctx->md_ctx, buf, i))
+        goto err;
+    if (!EVP_DigestFinal_ex(ctx->md_ctx, md, len))
+        goto err;
+    return 1;
+ err:
+    return 0;
+}
+
+size_t HMAC_size(const HMAC_CTX *ctx)
+{
+    return EVP_MD_size((ctx)->md);
+}
+
+HMAC_CTX *HMAC_CTX_new(void)
+{
+    HMAC_CTX *ctx = OPENSSL_zalloc(sizeof(HMAC_CTX));
+
+    if (ctx != NULL) {
+        if (!HMAC_CTX_reset(ctx)) {
+            HMAC_CTX_free(ctx);
+            return NULL;
+        }
+    }
+    return ctx;
+}
+
+static void hmac_ctx_cleanup(HMAC_CTX *ctx)
+{
+    EVP_MD_CTX_reset(ctx->i_ctx);
+    EVP_MD_CTX_reset(ctx->o_ctx);
+    EVP_MD_CTX_reset(ctx->md_ctx);
+    ctx->md = NULL;
+    ctx->key_length = 0;
+    OPENSSL_cleanse(ctx->key, sizeof(ctx->key));
+}
+
+void HMAC_CTX_free(HMAC_CTX *ctx)
+{
+    if (ctx != NULL) {
+        hmac_ctx_cleanup(ctx);
+        EVP_MD_CTX_free(ctx->i_ctx);
+        EVP_MD_CTX_free(ctx->o_ctx);
+        EVP_MD_CTX_free(ctx->md_ctx);
+        OPENSSL_free(ctx);
+    }
+}
+
+int HMAC_CTX_reset(HMAC_CTX *ctx)
+{
+    hmac_ctx_cleanup(ctx);
+    if (ctx->i_ctx == NULL)
+        ctx->i_ctx = EVP_MD_CTX_new();
+    if (ctx->i_ctx == NULL)
+        goto err;
+    if (ctx->o_ctx == NULL)
+        ctx->o_ctx = EVP_MD_CTX_new();
+    if (ctx->o_ctx == NULL)
+        goto err;
+    if (ctx->md_ctx == NULL)
+        ctx->md_ctx = EVP_MD_CTX_new();
+    if (ctx->md_ctx == NULL)
+        goto err;
+    ctx->md = NULL;
+    return 1;
+ err:
+    hmac_ctx_cleanup(ctx);
+    return 0;
+}
+
+int HMAC_CTX_copy(HMAC_CTX *dctx, HMAC_CTX *sctx)
+{
+    if (!HMAC_CTX_reset(dctx))
+        goto err;
+    if (!EVP_MD_CTX_copy_ex(dctx->i_ctx, sctx->i_ctx))
+        goto err;
+    if (!EVP_MD_CTX_copy_ex(dctx->o_ctx, sctx->o_ctx))
+        goto err;
+    if (!EVP_MD_CTX_copy_ex(dctx->md_ctx, sctx->md_ctx))
+        goto err;
+    memcpy(dctx->key, sctx->key, HMAC_MAX_MD_CBLOCK);
+    dctx->key_length = sctx->key_length;
+    dctx->md = sctx->md;
+    return 1;
+ err:
+    hmac_ctx_cleanup(dctx);
+    return 0;
+}
+
+unsigned char *HMAC(const EVP_MD *evp_md, const void *key, int key_len,
+                    const unsigned char *d, size_t n, unsigned char *md,
+                    unsigned int *md_len)
+{
+    HMAC_CTX *c = NULL;
+    static unsigned char m[EVP_MAX_MD_SIZE];
+    static const unsigned char dummy_key[1] = {'\0'};
+
+    if (md == NULL)
+        md = m;
+    if ((c = HMAC_CTX_new()) == NULL)
+        goto err;
+
+    /* For HMAC_Init_ex, NULL key signals reuse. */
+    if (key == NULL && key_len == 0) {
+        key = dummy_key;
+    }
+
+    if (!HMAC_Init_ex(c, key, key_len, evp_md, NULL))
+        goto err;
+    if (!HMAC_Update(c, d, n))
+        goto err;
+    if (!HMAC_Final(c, md, md_len))
+        goto err;
+    HMAC_CTX_free(c);
+    return md;
+ err:
+    HMAC_CTX_free(c);
+    return NULL;
+}
+
+void HMAC_CTX_set_flags(HMAC_CTX *ctx, unsigned long flags)
+{
+    EVP_MD_CTX_set_flags(ctx->i_ctx, flags);
+    EVP_MD_CTX_set_flags(ctx->o_ctx, flags);
+    EVP_MD_CTX_set_flags(ctx->md_ctx, flags);
+}
+
+const EVP_MD *HMAC_CTX_get_md(const HMAC_CTX *ctx)
+{
+    return ctx->md;
+}
diff --git a/crypto/hmac/hmac_lcl.h b/crypto/hmac/hmac_lcl.h
new file mode 100644
index 000000000..4c156dc12
--- /dev/null
+++ b/crypto/hmac/hmac_lcl.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#ifndef HEADER_HMAC_LCL_H
+# define HEADER_HMAC_LCL_H
+
+#ifdef  __cplusplus
+extern "C" {
+#endif
+#if 0                            /* emacs indentation fix */
+}
+#endif
+
+struct hmac_ctx_st {
+    const EVP_MD *md;
+    EVP_MD_CTX *md_ctx;
+    EVP_MD_CTX *i_ctx;
+    EVP_MD_CTX *o_ctx;
+    unsigned int key_length;
+    unsigned char key[HMAC_MAX_MD_CBLOCK];
+};
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif
diff --git a/crypto/ia64cpuid.S b/crypto/ia64cpuid.S
new file mode 100644
index 000000000..ffd6d6ca6
--- /dev/null
+++ b/crypto/ia64cpuid.S
@@ -0,0 +1,297 @@
+// Copyright 2004-2016 The OpenSSL Project Authors. All Rights Reserved.
+//
+// Licensed under the OpenSSL license (the "License").  You may not use
+// this file except in compliance with the License.  You can obtain a copy
+// in the file LICENSE in the source distribution or at
+// https://www.openssl.org/source/license.html
+// Works on all IA-64 platforms: Linux, HP-UX, Win64i...
+// On Win64i compile with ias.exe.
+.text
+
+#if defined(_HPUX_SOURCE) && !defined(_LP64)
+#define	ADDP	addp4
+#else
+#define	ADDP	add
+#endif
+
+.global	OPENSSL_cpuid_setup#
+.proc	OPENSSL_cpuid_setup#
+OPENSSL_cpuid_setup:
+{ .mib;	br.ret.sptk.many	b0		};;
+.endp	OPENSSL_cpuid_setup#
+
+.global	OPENSSL_rdtsc#
+.proc	OPENSSL_rdtsc#
+OPENSSL_rdtsc:
+{ .mib;	mov			r8=ar.itc
+	br.ret.sptk.many	b0		};;
+.endp   OPENSSL_rdtsc#
+
+.global	OPENSSL_atomic_add#
+.proc	OPENSSL_atomic_add#
+.align	32
+OPENSSL_atomic_add:
+{ .mii;	ld4		r2=[r32]
+	nop.i		0
+	nop.i		0		};;
+.Lspin:
+{ .mii;	mov		ar.ccv=r2
+	add		r8=r2,r33
+	mov		r3=r2		};;
+{ .mmi;	mf;;
+	cmpxchg4.acq	r2=[r32],r8,ar.ccv
+	nop.i		0		};;
+{ .mib;	cmp.ne		p6,p0=r2,r3
+	nop.i		0
+(p6)	br.dpnt		.Lspin		};;
+{ .mib;	nop.m		0
+	sxt4		r8=r8
+	br.ret.sptk.many	b0	};;
+.endp	OPENSSL_atomic_add#
+
+// Returns a structure comprising pointer to the top of stack of
+// the caller and pointer beyond backing storage for the current
+// register frame. The latter is required, because it might be
+// insufficient to wipe backing storage for the current frame
+// (as this procedure does), one might have to go further, toward
+// higher addresses to reach for whole "retroactively" saved
+// context...
+.global	OPENSSL_wipe_cpu#
+.proc	OPENSSL_wipe_cpu#
+.align	32
+OPENSSL_wipe_cpu:
+	.prologue
+	.fframe	0
+	.save	ar.pfs,r2
+	.save	ar.lc,r3
+{ .mib;	alloc		r2=ar.pfs,0,96,0,96
+	mov		r3=ar.lc
+	brp.loop.imp	.L_wipe_top,.L_wipe_end-16
+					};;
+{ .mii;	mov		r9=ar.bsp
+	mov		r8=pr
+	mov		ar.lc=96	};;
+	.body
+{ .mii;	add		r9=96*8-8,r9
+	mov		ar.ec=1		};;
+
+// One can sweep double as fast, but then we can't quarantee
+// that backing storage is wiped...
+.L_wipe_top:
+{ .mfi;	st8		[r9]=r0,-8
+	mov		f127=f0
+	mov		r127=r0		}
+{ .mfb;	nop.m		0
+	nop.f		0
+	br.ctop.sptk	.L_wipe_top	};;
+.L_wipe_end:
+
+{ .mfi;	mov		r11=r0
+	mov		f6=f0
+	mov		r14=r0		}
+{ .mfi;	mov		r15=r0
+	mov		f7=f0
+	mov		r16=r0		}
+{ .mfi;	mov		r17=r0
+	mov		f8=f0
+	mov		r18=r0		}
+{ .mfi;	mov		r19=r0
+	mov		f9=f0
+	mov		r20=r0		}
+{ .mfi;	mov		r21=r0
+	mov		f10=f0
+	mov		r22=r0		}
+{ .mfi;	mov		r23=r0
+	mov		f11=f0
+	mov		r24=r0		}
+{ .mfi;	mov		r25=r0
+	mov		f12=f0
+	mov		r26=r0		}
+{ .mfi;	mov		r27=r0
+	mov		f13=f0
+	mov		r28=r0		}
+{ .mfi;	mov		r29=r0
+	mov		f14=f0
+	mov		r30=r0		}
+{ .mfi;	mov		r31=r0
+	mov		f15=f0
+	nop.i		0		}
+{ .mfi;	mov		f16=f0		}
+{ .mfi;	mov		f17=f0		}
+{ .mfi;	mov		f18=f0		}
+{ .mfi;	mov		f19=f0		}
+{ .mfi;	mov		f20=f0		}
+{ .mfi;	mov		f21=f0		}
+{ .mfi;	mov		f22=f0		}
+{ .mfi;	mov		f23=f0		}
+{ .mfi;	mov		f24=f0		}
+{ .mfi;	mov		f25=f0		}
+{ .mfi;	mov		f26=f0		}
+{ .mfi;	mov		f27=f0		}
+{ .mfi;	mov		f28=f0		}
+{ .mfi;	mov		f29=f0		}
+{ .mfi;	mov		f30=f0		}
+{ .mfi;	add		r9=96*8+8,r9
+	mov		f31=f0
+	mov		pr=r8,0x1ffff	}
+{ .mib;	mov		r8=sp
+	mov		ar.lc=r3
+	br.ret.sptk	b0		};;
+.endp	OPENSSL_wipe_cpu#
+
+.global	OPENSSL_cleanse#
+.proc	OPENSSL_cleanse#
+OPENSSL_cleanse:
+{ .mib;	cmp.eq		p6,p0=0,r33	    // len==0
+	ADDP		r32=0,r32
+(p6)	br.ret.spnt	b0		};;
+{ .mib;	and		r2=7,r32
+	cmp.leu		p6,p0=15,r33	    // len>=15
+(p6)	br.cond.dptk	.Lot		};;
+
+.Little:
+{ .mib;	st1		[r32]=r0,1
+	cmp.ltu		p6,p7=1,r33	}  // len>1
+{ .mbb;	add		r33=-1,r33	   // len--
+(p6)	br.cond.dptk	.Little
+(p7)	br.ret.sptk.many	b0	};;
+
+.Lot:
+{ .mib;	cmp.eq		p6,p0=0,r2
+(p6)	br.cond.dptk	.Laligned	};;
+{ .mmi;	st1		[r32]=r0,1;;
+	and		r2=7,r32	}
+{ .mib;	add		r33=-1,r33
+	br		.Lot		};;
+
+.Laligned:
+{ .mmi;	st8		[r32]=r0,8
+	and		r2=-8,r33	    // len&~7
+	add		r33=-8,r33	};; // len-=8
+{ .mib;	cmp.ltu		p6,p0=8,r2	    // ((len+8)&~7)>8
+(p6)	br.cond.dptk	.Laligned	};;
+
+{ .mbb;	cmp.eq		p6,p7=r0,r33
+(p7)	br.cond.dpnt	.Little
+(p6)	br.ret.sptk.many	b0	};;
+.endp	OPENSSL_cleanse#
+
+.global	CRYPTO_memcmp#
+.proc	CRYPTO_memcmp#
+.align	32
+.skip	16
+CRYPTO_memcmp:
+	.prologue
+{ .mib;	mov		r8=0
+	cmp.eq		p6,p0=0,r34	    // len==0?
+(p6)	br.ret.spnt	b0		};;
+	.save		ar.pfs,r2
+{ .mib;	alloc		r2=ar.pfs,3,5,0,8
+	.save		ar.lc,r3
+	mov		r3=ar.lc
+	brp.loop.imp	.Loop_cmp_ctop,.Loop_cmp_cend-16
+					}
+{ .mib;	sub		r10=r34,r0,1
+	.save		pr,r9
+	mov		r9=pr		};;
+{ .mii;	ADDP		r16=0,r32
+	mov		ar.lc=r10
+	mov		ar.ec=4		}
+{ .mib;	ADDP		r17=0,r33
+	mov		pr.rot=1<<16	};;
+
+.Loop_cmp_ctop:
+{ .mib;	(p16)	ld1	r32=[r16],1
+	(p18)	xor	r34=r34,r38	}
+{ .mib;	(p16)	ld1	r36=[r17],1
+	(p19)	or	r8=r8,r35
+	br.ctop.sptk	.Loop_cmp_ctop	};;
+.Loop_cmp_cend:
+
+{ .mib;	cmp.ne		p6,p0=0,r8
+	mov		ar.lc=r3	};;
+{ .mib;
+(p6)	mov		r8=1
+	mov		pr=r9,0x1ffff
+	br.ret.sptk.many	b0	};;
+.endp	CRYPTO_memcmp#
+
+.global	OPENSSL_instrument_bus#
+.proc	OPENSSL_instrument_bus#
+OPENSSL_instrument_bus:
+{ .mmi;	mov		r2=r33
+	ADDP		r32=0,r32	}
+{ .mmi;	mov		r8=ar.itc;;
+	mov		r10=r0
+	mov		r9=r8		};;
+
+{ .mmi;	fc		r32;;
+	ld4		r8=[r32]	};;
+{ .mmi;	mf
+	mov		ar.ccv=r8
+	add		r8=r8,r10	};;
+{ .mmi;	cmpxchg4.acq	r3=[r32],r8,ar.ccv
+					};;
+.Loop:
+{ .mmi;	mov		r8=ar.itc;;
+	sub		r10=r8,r9		// diff=tick-lasttick
+	mov		r9=r8		};;	// lasttick=tick
+{ .mmi;	fc		r32;;
+	ld4		r8=[r32]	};;
+{ .mmi;	mf
+	mov		ar.ccv=r8
+	add		r8=r8,r10	};;
+{ .mmi;	cmpxchg4.acq	r3=[r32],r8,ar.ccv
+	add		r33=-1,r33
+	add		r32=4,r32	};;
+{ .mib;	cmp4.ne		p6,p0=0,r33
+(p6)	br.cond.dptk	.Loop		};;
+
+{ .mib;	sub		r8=r2,r33
+	br.ret.sptk.many	b0	};;
+.endp	OPENSSL_instrument_bus#
+
+.global	OPENSSL_instrument_bus2#
+.proc	OPENSSL_instrument_bus2#
+OPENSSL_instrument_bus2:
+{ .mmi;	mov		r2=r33			// put aside cnt
+	ADDP		r32=0,r32	}
+{ .mmi;	mov		r8=ar.itc;;
+	mov		r10=r0
+	mov		r9=r8		};;
+
+{ .mmi;	fc		r32;;
+	ld4		r8=[r32]	};;
+{ .mmi;	mf
+	mov		ar.ccv=r8
+	add		r8=r8,r10	};;
+{ .mmi;	cmpxchg4.acq	r3=[r32],r8,ar.ccv
+					};;
+
+{ .mmi;	mov		r8=ar.itc;;
+	sub		r10=r8,r9
+	mov		r9=r8		};;
+.Loop2:
+{ .mmi;	mov		r11=r10			// lastdiff=diff
+	add		r34=-1,r34	};;	// --max
+{ .mmi;	fc		r32;;
+	ld4		r8=[r32]
+	cmp4.eq		p6,p0=0,r34	};;
+{ .mmi;	mf
+	mov		ar.ccv=r8
+	add		r8=r8,r10	};;
+{ .mmb;	cmpxchg4.acq	r3=[r32],r8,ar.ccv
+(p6)	br.cond.spnt	.Ldone2		};;
+
+{ .mmi;	mov		r8=ar.itc;;
+	sub		r10=r8,r9		// diff=tick-lasttick
+	mov		r9=r8		};;	// lasttick=tick
+{ .mmi;	cmp.ne		p6,p0=r10,r11;;		// diff!=lastdiff
+(p6)	add		r33=-1,r33	};;	// conditional --cnt
+{ .mib;	cmp4.ne		p7,p0=0,r33
+(p6)	add		r32=4,r32		// conditional ++out
+(p7)	br.cond.dptk	.Loop2		};;
+.Ldone2:
+{ .mib;	sub		r8=r2,r33
+	br.ret.sptk.many	b0	};;
+.endp	OPENSSL_instrument_bus2#
diff --git a/crypto/idea/build.info b/crypto/idea/build.info
new file mode 100644
index 000000000..232612379
--- /dev/null
+++ b/crypto/idea/build.info
@@ -0,0 +1,3 @@
+LIBS=../../libcrypto
+SOURCE[../../libcrypto]=\
+        i_cbc.c i_cfb64.c i_ofb64.c i_ecb.c i_skey.c
diff --git a/crypto/idea/i_cbc.c b/crypto/idea/i_cbc.c
new file mode 100644
index 000000000..a70a8682a
--- /dev/null
+++ b/crypto/idea/i_cbc.c
@@ -0,0 +1,122 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "idea_lcl.h"
+
+void IDEA_cbc_encrypt(const unsigned char *in, unsigned char *out,
+                      long length, IDEA_KEY_SCHEDULE *ks, unsigned char *iv,
+                      int encrypt)
+{
+    register unsigned long tin0, tin1;
+    register unsigned long tout0, tout1, xor0, xor1;
+    register long l = length;
+    unsigned long tin[2];
+
+    if (encrypt) {
+        n2l(iv, tout0);
+        n2l(iv, tout1);
+        iv -= 8;
+        for (l -= 8; l >= 0; l -= 8) {
+            n2l(in, tin0);
+            n2l(in, tin1);
+            tin0 ^= tout0;
+            tin1 ^= tout1;
+            tin[0] = tin0;
+            tin[1] = tin1;
+            IDEA_encrypt(tin, ks);
+            tout0 = tin[0];
+            l2n(tout0, out);
+            tout1 = tin[1];
+            l2n(tout1, out);
+        }
+        if (l != -8) {
+            n2ln(in, tin0, tin1, l + 8);
+            tin0 ^= tout0;
+            tin1 ^= tout1;
+            tin[0] = tin0;
+            tin[1] = tin1;
+            IDEA_encrypt(tin, ks);
+            tout0 = tin[0];
+            l2n(tout0, out);
+            tout1 = tin[1];
+            l2n(tout1, out);
+        }
+        l2n(tout0, iv);
+        l2n(tout1, iv);
+    } else {
+        n2l(iv, xor0);
+        n2l(iv, xor1);
+        iv -= 8;
+        for (l -= 8; l >= 0; l -= 8) {
+            n2l(in, tin0);
+            tin[0] = tin0;
+            n2l(in, tin1);
+            tin[1] = tin1;
+            IDEA_encrypt(tin, ks);
+            tout0 = tin[0] ^ xor0;
+            tout1 = tin[1] ^ xor1;
+            l2n(tout0, out);
+            l2n(tout1, out);
+            xor0 = tin0;
+            xor1 = tin1;
+        }
+        if (l != -8) {
+            n2l(in, tin0);
+            tin[0] = tin0;
+            n2l(in, tin1);
+            tin[1] = tin1;
+            IDEA_encrypt(tin, ks);
+            tout0 = tin[0] ^ xor0;
+            tout1 = tin[1] ^ xor1;
+            l2nn(tout0, tout1, out, l + 8);
+            xor0 = tin0;
+            xor1 = tin1;
+        }
+        l2n(xor0, iv);
+        l2n(xor1, iv);
+    }
+    tin0 = tin1 = tout0 = tout1 = xor0 = xor1 = 0;
+    tin[0] = tin[1] = 0;
+}
+
+void IDEA_encrypt(unsigned long *d, IDEA_KEY_SCHEDULE *key)
+{
+    register IDEA_INT *p;
+    register unsigned long x1, x2, x3, x4, t0, t1, ul;
+
+    x2 = d[0];
+    x1 = (x2 >> 16);
+    x4 = d[1];
+    x3 = (x4 >> 16);
+
+    p = &(key->data[0][0]);
+
+    E_IDEA(0);
+    E_IDEA(1);
+    E_IDEA(2);
+    E_IDEA(3);
+    E_IDEA(4);
+    E_IDEA(5);
+    E_IDEA(6);
+    E_IDEA(7);
+
+    x1 &= 0xffff;
+    idea_mul(x1, x1, *p, ul);
+    p++;
+
+    t0 = x3 + *(p++);
+    t1 = x2 + *(p++);
+
+    x4 &= 0xffff;
+    idea_mul(x4, x4, *p, ul);
+
+    d[0] = (t0 & 0xffff) | ((x1 & 0xffff) << 16);
+    d[1] = (x4 & 0xffff) | ((t1 & 0xffff) << 16);
+}
diff --git a/crypto/idea/i_cfb64.c b/crypto/idea/i_cfb64.c
new file mode 100644
index 000000000..daf467eb5
--- /dev/null
+++ b/crypto/idea/i_cfb64.c
@@ -0,0 +1,74 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "idea_lcl.h"
+
+/*
+ * The input and output encrypted as though 64bit cfb mode is being used.
+ * The extra state information to record how much of the 64bit block we have
+ * used is contained in *num;
+ */
+
+void IDEA_cfb64_encrypt(const unsigned char *in, unsigned char *out,
+                        long length, IDEA_KEY_SCHEDULE *schedule,
+                        unsigned char *ivec, int *num, int encrypt)
+{
+    register unsigned long v0, v1, t;
+    register int n = *num;
+    register long l = length;
+    unsigned long ti[2];
+    unsigned char *iv, c, cc;
+
+    iv = (unsigned char *)ivec;
+    if (encrypt) {
+        while (l--) {
+            if (n == 0) {
+                n2l(iv, v0);
+                ti[0] = v0;
+                n2l(iv, v1);
+                ti[1] = v1;
+                IDEA_encrypt((unsigned long *)ti, schedule);
+                iv = (unsigned char *)ivec;
+                t = ti[0];
+                l2n(t, iv);
+                t = ti[1];
+                l2n(t, iv);
+                iv = (unsigned char *)ivec;
+            }
+            c = *(in++) ^ iv[n];
+            *(out++) = c;
+            iv[n] = c;
+            n = (n + 1) & 0x07;
+        }
+    } else {
+        while (l--) {
+            if (n == 0) {
+                n2l(iv, v0);
+                ti[0] = v0;
+                n2l(iv, v1);
+                ti[1] = v1;
+                IDEA_encrypt((unsigned long *)ti, schedule);
+                iv = (unsigned char *)ivec;
+                t = ti[0];
+                l2n(t, iv);
+                t = ti[1];
+                l2n(t, iv);
+                iv = (unsigned char *)ivec;
+            }
+            cc = *(in++);
+            c = iv[n];
+            iv[n] = cc;
+            *(out++) = c ^ cc;
+            n = (n + 1) & 0x07;
+        }
+    }
+    v0 = v1 = ti[0] = ti[1] = t = c = cc = 0;
+    *num = n;
+}
diff --git a/crypto/idea/i_ecb.c b/crypto/idea/i_ecb.c
new file mode 100644
index 000000000..2208287e3
--- /dev/null
+++ b/crypto/idea/i_ecb.c
@@ -0,0 +1,34 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "idea_lcl.h"
+#include 
+
+const char *IDEA_options(void)
+{
+    return ("idea(int)");
+}
+
+void IDEA_ecb_encrypt(const unsigned char *in, unsigned char *out,
+                      IDEA_KEY_SCHEDULE *ks)
+{
+    unsigned long l0, l1, d[2];
+
+    n2l(in, l0);
+    d[0] = l0;
+    n2l(in, l1);
+    d[1] = l1;
+    IDEA_encrypt(d, ks);
+    l0 = d[0];
+    l2n(l0, out);
+    l1 = d[1];
+    l2n(l1, out);
+    l0 = l1 = d[0] = d[1] = 0;
+}
diff --git a/crypto/idea/i_ofb64.c b/crypto/idea/i_ofb64.c
new file mode 100644
index 000000000..997a7b88e
--- /dev/null
+++ b/crypto/idea/i_ofb64.c
@@ -0,0 +1,61 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "idea_lcl.h"
+
+/*
+ * The input and output encrypted as though 64bit ofb mode is being used.
+ * The extra state information to record how much of the 64bit block we have
+ * used is contained in *num;
+ */
+void IDEA_ofb64_encrypt(const unsigned char *in, unsigned char *out,
+                        long length, IDEA_KEY_SCHEDULE *schedule,
+                        unsigned char *ivec, int *num)
+{
+    register unsigned long v0, v1, t;
+    register int n = *num;
+    register long l = length;
+    unsigned char d[8];
+    register char *dp;
+    unsigned long ti[2];
+    unsigned char *iv;
+    int save = 0;
+
+    iv = (unsigned char *)ivec;
+    n2l(iv, v0);
+    n2l(iv, v1);
+    ti[0] = v0;
+    ti[1] = v1;
+    dp = (char *)d;
+    l2n(v0, dp);
+    l2n(v1, dp);
+    while (l--) {
+        if (n == 0) {
+            IDEA_encrypt((unsigned long *)ti, schedule);
+            dp = (char *)d;
+            t = ti[0];
+            l2n(t, dp);
+            t = ti[1];
+            l2n(t, dp);
+            save++;
+        }
+        *(out++) = *(in++) ^ d[n];
+        n = (n + 1) & 0x07;
+    }
+    if (save) {
+        v0 = ti[0];
+        v1 = ti[1];
+        iv = (unsigned char *)ivec;
+        l2n(v0, iv);
+        l2n(v1, iv);
+    }
+    t = v0 = v1 = ti[0] = ti[1] = 0;
+    *num = n;
+}
diff --git a/crypto/idea/i_skey.c b/crypto/idea/i_skey.c
new file mode 100644
index 000000000..02853246d
--- /dev/null
+++ b/crypto/idea/i_skey.c
@@ -0,0 +1,112 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "idea_lcl.h"
+
+static IDEA_INT inverse(unsigned int xin);
+void IDEA_set_encrypt_key(const unsigned char *key, IDEA_KEY_SCHEDULE *ks)
+{
+    int i;
+    register IDEA_INT *kt, *kf, r0, r1, r2;
+
+    kt = &(ks->data[0][0]);
+    n2s(key, kt[0]);
+    n2s(key, kt[1]);
+    n2s(key, kt[2]);
+    n2s(key, kt[3]);
+    n2s(key, kt[4]);
+    n2s(key, kt[5]);
+    n2s(key, kt[6]);
+    n2s(key, kt[7]);
+
+    kf = kt;
+    kt += 8;
+    for (i = 0; i < 6; i++) {
+        r2 = kf[1];
+        r1 = kf[2];
+        *(kt++) = ((r2 << 9) | (r1 >> 7)) & 0xffff;
+        r0 = kf[3];
+        *(kt++) = ((r1 << 9) | (r0 >> 7)) & 0xffff;
+        r1 = kf[4];
+        *(kt++) = ((r0 << 9) | (r1 >> 7)) & 0xffff;
+        r0 = kf[5];
+        *(kt++) = ((r1 << 9) | (r0 >> 7)) & 0xffff;
+        r1 = kf[6];
+        *(kt++) = ((r0 << 9) | (r1 >> 7)) & 0xffff;
+        r0 = kf[7];
+        *(kt++) = ((r1 << 9) | (r0 >> 7)) & 0xffff;
+        r1 = kf[0];
+        if (i >= 5)
+            break;
+        *(kt++) = ((r0 << 9) | (r1 >> 7)) & 0xffff;
+        *(kt++) = ((r1 << 9) | (r2 >> 7)) & 0xffff;
+        kf += 8;
+    }
+}
+
+void IDEA_set_decrypt_key(IDEA_KEY_SCHEDULE *ek, IDEA_KEY_SCHEDULE *dk)
+{
+    int r;
+    register IDEA_INT *fp, *tp, t;
+
+    tp = &(dk->data[0][0]);
+    fp = &(ek->data[8][0]);
+    for (r = 0; r < 9; r++) {
+        *(tp++) = inverse(fp[0]);
+        *(tp++) = ((int)(0x10000L - fp[2]) & 0xffff);
+        *(tp++) = ((int)(0x10000L - fp[1]) & 0xffff);
+        *(tp++) = inverse(fp[3]);
+        if (r == 8)
+            break;
+        fp -= 6;
+        *(tp++) = fp[4];
+        *(tp++) = fp[5];
+    }
+
+    tp = &(dk->data[0][0]);
+    t = tp[1];
+    tp[1] = tp[2];
+    tp[2] = t;
+
+    t = tp[49];
+    tp[49] = tp[50];
+    tp[50] = t;
+}
+
+/* taken directly from the 'paper' I'll have a look at it later */
+static IDEA_INT inverse(unsigned int xin)
+{
+    long n1, n2, q, r, b1, b2, t;
+
+    if (xin == 0)
+        b2 = 0;
+    else {
+        n1 = 0x10001;
+        n2 = xin;
+        b2 = 1;
+        b1 = 0;
+
+        do {
+            r = (n1 % n2);
+            q = (n1 - r) / n2;
+            if (r == 0) {
+                if (b2 < 0)
+                    b2 = 0x10001 + b2;
+            } else {
+                n1 = n2;
+                n2 = r;
+                t = b2;
+                b2 = b1 - q * b2;
+                b1 = t;
+            }
+        } while (r != 0);
+    }
+    return ((IDEA_INT) b2);
+}
diff --git a/crypto/idea/idea_lcl.h b/crypto/idea/idea_lcl.h
new file mode 100644
index 000000000..f227d0db3
--- /dev/null
+++ b/crypto/idea/idea_lcl.h
@@ -0,0 +1,103 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * The new form of this macro (check if the a*b == 0) was suggested by Colin
+ * Plumb 
+ */
+/* Removal of the inner if from from Wei Dai 24/4/96 */
+#define idea_mul(r,a,b,ul) \
+ul=(unsigned long)a*b; \
+if (ul != 0) \
+        { \
+        r=(ul&0xffff)-(ul>>16); \
+        r-=((r)>>16); \
+        } \
+else \
+        r=(-(int)a-b+1);        /* assuming a or b is 0 and in range */
+
+/*
+ * 7/12/95 - Many thanks to Rhys Weatherley  for
+ * pointing out that I was assuming little endian byte order for all
+ * quantities what idea actually used bigendian.  No where in the spec does
+ * it mention this, it is all in terms of 16 bit numbers and even the example
+ * does not use byte streams for the input example :-(. If you byte swap each
+ * pair of input, keys and iv, the functions would produce the output as the
+ * old version :-(.
+ */
+
+/* NOTE - c is not incremented as per n2l */
+#define n2ln(c,l1,l2,n) { \
+                        c+=n; \
+                        l1=l2=0; \
+                        switch (n) { \
+                        case 8: l2 =((unsigned long)(*(--(c))))    ; \
+                        case 7: l2|=((unsigned long)(*(--(c))))<< 8; \
+                        case 6: l2|=((unsigned long)(*(--(c))))<<16; \
+                        case 5: l2|=((unsigned long)(*(--(c))))<<24; \
+                        case 4: l1 =((unsigned long)(*(--(c))))    ; \
+                        case 3: l1|=((unsigned long)(*(--(c))))<< 8; \
+                        case 2: l1|=((unsigned long)(*(--(c))))<<16; \
+                        case 1: l1|=((unsigned long)(*(--(c))))<<24; \
+                                } \
+                        }
+
+/* NOTE - c is not incremented as per l2n */
+#define l2nn(l1,l2,c,n) { \
+                        c+=n; \
+                        switch (n) { \
+                        case 8: *(--(c))=(unsigned char)(((l2)    )&0xff); \
+                        case 7: *(--(c))=(unsigned char)(((l2)>> 8)&0xff); \
+                        case 6: *(--(c))=(unsigned char)(((l2)>>16)&0xff); \
+                        case 5: *(--(c))=(unsigned char)(((l2)>>24)&0xff); \
+                        case 4: *(--(c))=(unsigned char)(((l1)    )&0xff); \
+                        case 3: *(--(c))=(unsigned char)(((l1)>> 8)&0xff); \
+                        case 2: *(--(c))=(unsigned char)(((l1)>>16)&0xff); \
+                        case 1: *(--(c))=(unsigned char)(((l1)>>24)&0xff); \
+                                } \
+                        }
+
+#undef n2l
+#define n2l(c,l)        (l =((unsigned long)(*((c)++)))<<24L, \
+                         l|=((unsigned long)(*((c)++)))<<16L, \
+                         l|=((unsigned long)(*((c)++)))<< 8L, \
+                         l|=((unsigned long)(*((c)++))))
+
+#undef l2n
+#define l2n(l,c)        (*((c)++)=(unsigned char)(((l)>>24L)&0xff), \
+                         *((c)++)=(unsigned char)(((l)>>16L)&0xff), \
+                         *((c)++)=(unsigned char)(((l)>> 8L)&0xff), \
+                         *((c)++)=(unsigned char)(((l)     )&0xff))
+
+#undef s2n
+#define s2n(l,c)        (*((c)++)=(unsigned char)(((l)     )&0xff), \
+                         *((c)++)=(unsigned char)(((l)>> 8L)&0xff))
+
+#undef n2s
+#define n2s(c,l)        (l =((IDEA_INT)(*((c)++)))<< 8L, \
+                         l|=((IDEA_INT)(*((c)++)))      )
+
+
+#define E_IDEA(num) \
+        x1&=0xffff; \
+        idea_mul(x1,x1,*p,ul); p++; \
+        x2+= *(p++); \
+        x3+= *(p++); \
+        x4&=0xffff; \
+        idea_mul(x4,x4,*p,ul); p++; \
+        t0=(x1^x3)&0xffff; \
+        idea_mul(t0,t0,*p,ul); p++; \
+        t1=(t0+(x2^x4))&0xffff; \
+        idea_mul(t1,t1,*p,ul); p++; \
+        t0+=t1; \
+        x1^=t1; \
+        x4^=t0; \
+        ul=x2^t0; /* do the swap to x3 */ \
+        x2=x3^t1; \
+        x3=ul;
diff --git a/crypto/include/internal/asn1_int.h b/crypto/include/internal/asn1_int.h
new file mode 100644
index 000000000..63c1bb3e9
--- /dev/null
+++ b/crypto/include/internal/asn1_int.h
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2015-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/* Internal ASN1 structures and functions: not for application use */
+
+/* ASN1 public key method structure */
+
+struct evp_pkey_asn1_method_st {
+    int pkey_id;
+    int pkey_base_id;
+    unsigned long pkey_flags;
+    char *pem_str;
+    char *info;
+    int (*pub_decode) (EVP_PKEY *pk, X509_PUBKEY *pub);
+    int (*pub_encode) (X509_PUBKEY *pub, const EVP_PKEY *pk);
+    int (*pub_cmp) (const EVP_PKEY *a, const EVP_PKEY *b);
+    int (*pub_print) (BIO *out, const EVP_PKEY *pkey, int indent,
+                      ASN1_PCTX *pctx);
+    int (*priv_decode) (EVP_PKEY *pk, const PKCS8_PRIV_KEY_INFO *p8inf);
+    int (*priv_encode) (PKCS8_PRIV_KEY_INFO *p8, const EVP_PKEY *pk);
+    int (*priv_print) (BIO *out, const EVP_PKEY *pkey, int indent,
+                       ASN1_PCTX *pctx);
+    int (*pkey_size) (const EVP_PKEY *pk);
+    int (*pkey_bits) (const EVP_PKEY *pk);
+    int (*pkey_security_bits) (const EVP_PKEY *pk);
+    int (*param_decode) (EVP_PKEY *pkey,
+                         const unsigned char **pder, int derlen);
+    int (*param_encode) (const EVP_PKEY *pkey, unsigned char **pder);
+    int (*param_missing) (const EVP_PKEY *pk);
+    int (*param_copy) (EVP_PKEY *to, const EVP_PKEY *from);
+    int (*param_cmp) (const EVP_PKEY *a, const EVP_PKEY *b);
+    int (*param_print) (BIO *out, const EVP_PKEY *pkey, int indent,
+                        ASN1_PCTX *pctx);
+    int (*sig_print) (BIO *out,
+                      const X509_ALGOR *sigalg, const ASN1_STRING *sig,
+                      int indent, ASN1_PCTX *pctx);
+    void (*pkey_free) (EVP_PKEY *pkey);
+    int (*pkey_ctrl) (EVP_PKEY *pkey, int op, long arg1, void *arg2);
+    /* Legacy functions for old PEM */
+    int (*old_priv_decode) (EVP_PKEY *pkey,
+                            const unsigned char **pder, int derlen);
+    int (*old_priv_encode) (const EVP_PKEY *pkey, unsigned char **pder);
+    /* Custom ASN1 signature verification */
+    int (*item_verify) (EVP_MD_CTX *ctx, const ASN1_ITEM *it, void *asn,
+                        X509_ALGOR *a, ASN1_BIT_STRING *sig, EVP_PKEY *pkey);
+    int (*item_sign) (EVP_MD_CTX *ctx, const ASN1_ITEM *it, void *asn,
+                      X509_ALGOR *alg1, X509_ALGOR *alg2,
+                      ASN1_BIT_STRING *sig);
+} /* EVP_PKEY_ASN1_METHOD */ ;
+
+DEFINE_STACK_OF_CONST(EVP_PKEY_ASN1_METHOD)
+
+extern const EVP_PKEY_ASN1_METHOD cmac_asn1_meth;
+extern const EVP_PKEY_ASN1_METHOD dh_asn1_meth;
+extern const EVP_PKEY_ASN1_METHOD dhx_asn1_meth;
+extern const EVP_PKEY_ASN1_METHOD dsa_asn1_meths[5];
+extern const EVP_PKEY_ASN1_METHOD eckey_asn1_meth;
+extern const EVP_PKEY_ASN1_METHOD ecx25519_asn1_meth;
+extern const EVP_PKEY_ASN1_METHOD hmac_asn1_meth;
+extern const EVP_PKEY_ASN1_METHOD rsa_asn1_meths[2];
+#ifndef OPENSSL_NO_PAILLIER
+extern const EVP_PKEY_ASN1_METHOD paillier_asn1_meth;
+#endif
+#ifndef OPENSSL_NO_SM9
+extern const EVP_PKEY_ASN1_METHOD sm9_master_asn1_meth;
+extern const EVP_PKEY_ASN1_METHOD sm9_asn1_meth;
+#endif
+
+/*
+ * These are used internally in the ASN1_OBJECT to keep track of whether the
+ * names and data need to be free()ed
+ */
+# define ASN1_OBJECT_FLAG_DYNAMIC         0x01/* internal use */
+# define ASN1_OBJECT_FLAG_CRITICAL        0x02/* critical x509v3 object id */
+# define ASN1_OBJECT_FLAG_DYNAMIC_STRINGS 0x04/* internal use */
+# define ASN1_OBJECT_FLAG_DYNAMIC_DATA    0x08/* internal use */
+struct asn1_object_st {
+    const char *sn, *ln;
+    int nid;
+    int length;
+    const unsigned char *data;  /* data remains const after init */
+    int flags;                  /* Should we free this one */
+};
+
+/* ASN1 print context structure */
+
+struct asn1_pctx_st {
+    unsigned long flags;
+    unsigned long nm_flags;
+    unsigned long cert_flags;
+    unsigned long oid_flags;
+    unsigned long str_flags;
+} /* ASN1_PCTX */ ;
+
+int asn1_valid_host(const ASN1_STRING *host);
diff --git a/crypto/include/internal/async.h b/crypto/include/internal/async.h
new file mode 100644
index 000000000..6b10428a8
--- /dev/null
+++ b/crypto/include/internal/async.h
@@ -0,0 +1,18 @@
+/*
+ * Copyright 2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+//FIXME:
+
+#ifndef OPENSSL_NO_ASYNC
+#include 
+#endif
+
+int async_init(void);
+void async_deinit(void);
+
diff --git a/crypto/include/internal/bn_conf.h.in b/crypto/include/internal/bn_conf.h.in
new file mode 100644
index 000000000..ec6e4f6c1
--- /dev/null
+++ b/crypto/include/internal/bn_conf.h.in
@@ -0,0 +1,27 @@
+{- join("\n",map { "/* $_ */" } @autowarntext) -}
+/*
+ * Copyright 2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#ifndef HEADER_BN_CONF_H
+# define HEADER_BN_CONF_H
+
+/*
+ * The contents of this file are not used in the UEFI build, as
+ * both 32-bit and 64-bit builds are supported from a single run
+ * of the Configure script.
+ */
+
+/* Should we define BN_DIV2W here? */
+
+/* Only one for the following should be defined */
+{- $config{b64l} ? "#define" : "#undef" -} SIXTY_FOUR_BIT_LONG
+{- $config{b64}  ? "#define" : "#undef" -} SIXTY_FOUR_BIT
+{- $config{b32}  ? "#define" : "#undef" -} THIRTY_TWO_BIT
+
+#endif
diff --git a/crypto/include/internal/bn_dh.h b/crypto/include/internal/bn_dh.h
new file mode 100644
index 000000000..b4bca40c5
--- /dev/null
+++ b/crypto/include/internal/bn_dh.h
@@ -0,0 +1,17 @@
+/*
+ * Copyright 2015-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#define declare_dh_bn(x) \
+    const extern BIGNUM _bignum_dh##x##_p;              \
+    const extern BIGNUM _bignum_dh##x##_g;              \
+    const extern BIGNUM _bignum_dh##x##_q;
+
+declare_dh_bn(1024_160)
+declare_dh_bn(2048_224)
+declare_dh_bn(2048_256)
diff --git a/crypto/include/internal/bn_int.h b/crypto/include/internal/bn_int.h
new file mode 100644
index 000000000..9c984ba78
--- /dev/null
+++ b/crypto/include/internal/bn_int.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2014-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#ifndef HEADER_BN_INT_H
+# define HEADER_BN_INT_H
+
+# include 
+# include 
+
+#ifdef  __cplusplus
+extern "C" {
+#endif
+
+BIGNUM *bn_wexpand(BIGNUM *a, int words);
+BIGNUM *bn_expand2(BIGNUM *a, int words);
+
+void bn_correct_top(BIGNUM *a);
+
+/*
+ * Determine the modified width-(w+1) Non-Adjacent Form (wNAF) of 'scalar'.
+ * This is an array r[] of values that are either zero or odd with an
+ * absolute value less than 2^w satisfying scalar = \sum_j r[j]*2^j where at
+ * most one of any w+1 consecutive digits is non-zero with the exception that
+ * the most significant digit may be only w-1 zeros away from that next
+ * non-zero digit.
+ */
+signed char *bn_compute_wNAF(const BIGNUM *scalar, int w, size_t *ret_len);
+
+int bn_get_top(const BIGNUM *a);
+
+void bn_set_top(BIGNUM *a, int top);
+
+int bn_get_dmax(const BIGNUM *a);
+
+/* Set all words to zero */
+void bn_set_all_zero(BIGNUM *a);
+
+/*
+ * Copy the internal BIGNUM words into out which holds size elements (and size
+ * must be bigger than top)
+ */
+int bn_copy_words(BN_ULONG *out, const BIGNUM *in, int size);
+
+BN_ULONG *bn_get_words(const BIGNUM *a);
+
+/*
+ * Set the internal data words in a to point to words which contains size
+ * elements. The BN_FLG_STATIC_DATA flag is set
+ */
+void bn_set_static_words(BIGNUM *a, BN_ULONG *words, int size);
+
+/*
+ * Copy words into the BIGNUM |a|, reallocating space as necessary.
+ * The negative flag of |a| is not modified.
+ * Returns 1 on success and 0 on failure.
+ */
+/*
+ * |num_words| is int because bn_expand2 takes an int. This is an internal
+ * function so we simply trust callers not to pass negative values.
+ */
+int bn_set_words(BIGNUM *a, BN_ULONG *words, int num_words);
+
+size_t bn_sizeof_BIGNUM(void);
+
+/*
+ * Return element el from an array of BIGNUMs starting at base (required
+ * because callers do not know the size of BIGNUM at compilation time)
+ */
+BIGNUM *bn_array_el(BIGNUM *base, int el);
+
+
+#ifdef  __cplusplus
+}
+#endif
+
+#endif
diff --git a/crypto/include/internal/bn_srp.h b/crypto/include/internal/bn_srp.h
new file mode 100644
index 000000000..d4b282a6b
--- /dev/null
+++ b/crypto/include/internal/bn_srp.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2014-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#ifndef OPENSSL_NO_SRP
+
+extern const BIGNUM bn_group_1024;
+
+extern const BIGNUM bn_group_1536;
+
+extern const BIGNUM bn_group_2048;
+
+extern const BIGNUM bn_group_3072;
+
+extern const BIGNUM bn_group_4096;
+
+extern const BIGNUM bn_group_6144;
+
+extern const BIGNUM bn_group_8192;
+
+extern const BIGNUM bn_generator_19;
+
+extern const BIGNUM bn_generator_5;
+
+extern const BIGNUM bn_generator_2;
+
+#endif
diff --git a/crypto/include/internal/chacha.h b/crypto/include/internal/chacha.h
new file mode 100644
index 000000000..7d4366ea2
--- /dev/null
+++ b/crypto/include/internal/chacha.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2015-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#ifndef HEADER_CHACHA_H
+#define HEADER_CHACHA_H
+
+#include 
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * ChaCha20_ctr32 encrypts |len| bytes from |inp| with the given key and
+ * nonce and writes the result to |out|, which may be equal to |inp|.
+ * The |key| is not 32 bytes of verbatim key material though, but the
+ * said material collected into 8 32-bit elements array in host byte
+ * order. Same approach applies to nonce: the |counter| argument is
+ * pointer to concatenated nonce and counter values collected into 4
+ * 32-bit elements. This, passing crypto material collected into 32-bit
+ * elements as opposite to passing verbatim byte vectors, is chosen for
+ * efficiency in multi-call scenarios.
+ */
+void ChaCha20_ctr32(unsigned char *out, const unsigned char *inp,
+                    size_t len, const unsigned int key[8],
+                    const unsigned int counter[4]);
+/*
+ * You can notice that there is no key setup procedure. Because it's
+ * as trivial as collecting bytes into 32-bit elements, it's reckoned
+ * that below macro is sufficient.
+ */
+#define CHACHA_U8TOU32(p)  ( \
+                ((unsigned int)(p)[0])     | ((unsigned int)(p)[1]<<8) | \
+                ((unsigned int)(p)[2]<<16) | ((unsigned int)(p)[3]<<24)  )
+
+#define CHACHA_KEY_SIZE		32
+#define CHACHA_CTR_SIZE		16
+#define CHACHA_BLK_SIZE		64
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/crypto/include/internal/cryptlib.h b/crypto/include/internal/cryptlib.h
new file mode 100644
index 000000000..f3ec9b67b
--- /dev/null
+++ b/crypto/include/internal/cryptlib.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#ifndef HEADER_CRYPTLIB_H
+# define HEADER_CRYPTLIB_H
+
+# include 
+# include 
+
+# include "e_os.h"
+
+# ifdef OPENSSL_USE_APPLINK
+#  undef BIO_FLAGS_UPLINK
+#  define BIO_FLAGS_UPLINK 0x8000
+#  include "ms/uplink.h"
+# endif
+
+# include 
+# include 
+# include 
+# include 
+
+#ifdef  __cplusplus
+extern "C" {
+#endif
+
+typedef struct ex_callback_st EX_CALLBACK;
+
+DEFINE_STACK_OF(EX_CALLBACK)
+
+typedef struct app_mem_info_st APP_INFO;
+
+typedef struct mem_st MEM;
+DEFINE_LHASH_OF(MEM);
+
+# ifndef OPENSSL_SYS_VMS
+#  define X509_CERT_AREA          OPENSSLDIR
+#  define X509_CERT_DIR           OPENSSLDIR "/certs"
+#  define X509_CERT_FILE          OPENSSLDIR "/cert.pem"
+#  define X509_PRIVATE_DIR        OPENSSLDIR "/private"
+#  define CTLOG_FILE              OPENSSLDIR "/ct_log_list.cnf"
+# else
+#  define X509_CERT_AREA          "OSSL$DATAROOT:[000000]"
+#  define X509_CERT_DIR           "OSSL$DATAROOT:[CERTS]"
+#  define X509_CERT_FILE          "OSSL$DATAROOT:[000000]cert.pem"
+#  define X509_PRIVATE_DIR        "OSSL$DATAROOT:[PRIVATE]"
+#  define CTLOG_FILE              "OSSL$DATAROOT:[000000]ct_log_list.cnf"
+# endif
+
+# define X509_CERT_DIR_EVP        "SSL_CERT_DIR"
+# define X509_CERT_FILE_EVP       "SSL_CERT_FILE"
+# define CTLOG_FILE_EVP           "CTLOG_FILE"
+
+/* size of string representations */
+# define DECIMAL_SIZE(type)      ((sizeof(type)*8+2)/3+1)
+# define HEX_SIZE(type)          (sizeof(type)*2)
+
+void OPENSSL_cpuid_setup(void);
+extern unsigned int OPENSSL_ia32cap_P[];
+void OPENSSL_showfatal(const char *fmta, ...);
+extern int OPENSSL_NONPIC_relocated;
+void crypto_cleanup_all_ex_data_int(void);
+
+int openssl_strerror_r(int errnum, char *buf, size_t buflen);
+# if !defined(OPENSSL_NO_STDIO)
+FILE *openssl_fopen(const char *filename, const char *mode);
+# else
+void *openssl_fopen(const char *filename, const char *mode);
+# endif
+
+#ifdef  __cplusplus
+}
+#endif
+
+#endif
diff --git a/crypto/include/internal/cryptlib_int.h b/crypto/include/internal/cryptlib_int.h
new file mode 100644
index 000000000..8e2a7199a
--- /dev/null
+++ b/crypto/include/internal/cryptlib_int.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+
+/* This file is not scanned by mkdef.pl, whereas cryptlib.h is */
+
+struct thread_local_inits_st {
+    int async;
+    int err_state;
+};
+
+int ossl_init_thread_start(uint64_t opts);
+
+/*
+ * OPENSSL_INIT flags. The primary list of these is in crypto.h. Flags below
+ * are those omitted from crypto.h because they are "reserved for internal
+ * use".
+ */
+# define OPENSSL_INIT_ZLIB                   0x00010000L
+
+/* OPENSSL_INIT_THREAD flags */
+# define OPENSSL_INIT_THREAD_ASYNC           0x01
+# define OPENSSL_INIT_THREAD_ERR_STATE       0x02
+
diff --git a/crypto/include/internal/dso_conf.h.in b/crypto/include/internal/dso_conf.h.in
new file mode 100644
index 000000000..daa5e247a
--- /dev/null
+++ b/crypto/include/internal/dso_conf.h.in
@@ -0,0 +1,15 @@
+{- join("\n",map { "/* $_ */" } @autowarntext) -}
+/*
+ * Copyright 2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#ifndef HEADER_DSO_CONF_H
+# define HEADER_DSO_CONF_H
+
+# define DSO_EXTENSION "{- $target{dso_extension} -}"
+#endif
diff --git a/crypto/include/internal/engine.h b/crypto/include/internal/engine.h
new file mode 100644
index 000000000..469cc5d6d
--- /dev/null
+++ b/crypto/include/internal/engine.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#ifndef OPENSSL_NO_ENGINE
+#include 
+#endif
+
+void engine_load_openssl_int(void);
+void engine_load_cryptodev_int(void);
+void engine_load_rdrand_int(void);
+void engine_load_dynamic_int(void);
+void engine_load_padlock_int(void);
+void engine_load_capi_int(void);
+void engine_load_skf_int(void);
+void engine_load_dasync_int(void);
+void engine_load_afalg_int(void);
+void engine_cleanup_int(void);
diff --git a/crypto/include/internal/err_int.h b/crypto/include/internal/err_int.h
new file mode 100644
index 000000000..7fec3ed76
--- /dev/null
+++ b/crypto/include/internal/err_int.h
@@ -0,0 +1,17 @@
+/*
+ * Copyright 2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#ifndef INTERNAL_ERR_INT_H
+# define INTERNAL_ERR_INT_H
+
+int err_load_crypto_strings_int(void);
+void err_cleanup(void);
+void err_delete_thread_state(void);
+
+#endif
diff --git a/crypto/include/internal/evp_int.h b/crypto/include/internal/evp_int.h
new file mode 100644
index 000000000..94c4697ac
--- /dev/null
+++ b/crypto/include/internal/evp_int.h
@@ -0,0 +1,403 @@
+/*
+ * Copyright 2015-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+struct evp_pkey_ctx_st {
+    /* Method associated with this operation */
+    const EVP_PKEY_METHOD *pmeth;
+    /* Engine that implements this method or NULL if builtin */
+    ENGINE *engine;
+    /* Key: may be NULL */
+    EVP_PKEY *pkey;
+    /* Peer key for key agreement, may be NULL */
+    EVP_PKEY *peerkey;
+    /* Actual operation */
+    int operation;
+    /* Algorithm specific data */
+    void *data;
+    /* Application specific data */
+    void *app_data;
+    /* Keygen callback */
+    EVP_PKEY_gen_cb *pkey_gencb;
+    /* implementation specific keygen data */
+    int *keygen_info;
+    int keygen_info_count;
+} /* EVP_PKEY_CTX */ ;
+
+#define EVP_PKEY_FLAG_DYNAMIC   1
+
+struct evp_pkey_method_st {
+    int pkey_id;
+    int flags;
+    int (*init) (EVP_PKEY_CTX *ctx);
+    int (*copy) (EVP_PKEY_CTX *dst, EVP_PKEY_CTX *src);
+    void (*cleanup) (EVP_PKEY_CTX *ctx);
+    int (*paramgen_init) (EVP_PKEY_CTX *ctx);
+    int (*paramgen) (EVP_PKEY_CTX *ctx, EVP_PKEY *pkey);
+    int (*keygen_init) (EVP_PKEY_CTX *ctx);
+    int (*keygen) (EVP_PKEY_CTX *ctx, EVP_PKEY *pkey);
+    int (*sign_init) (EVP_PKEY_CTX *ctx);
+    int (*sign) (EVP_PKEY_CTX *ctx, unsigned char *sig, size_t *siglen,
+                 const unsigned char *tbs, size_t tbslen);
+    int (*verify_init) (EVP_PKEY_CTX *ctx);
+    int (*verify) (EVP_PKEY_CTX *ctx,
+                   const unsigned char *sig, size_t siglen,
+                   const unsigned char *tbs, size_t tbslen);
+    int (*verify_recover_init) (EVP_PKEY_CTX *ctx);
+    int (*verify_recover) (EVP_PKEY_CTX *ctx,
+                           unsigned char *rout, size_t *routlen,
+                           const unsigned char *sig, size_t siglen);
+    int (*signctx_init) (EVP_PKEY_CTX *ctx, EVP_MD_CTX *mctx);
+    int (*signctx) (EVP_PKEY_CTX *ctx, unsigned char *sig, size_t *siglen,
+                    EVP_MD_CTX *mctx);
+    int (*verifyctx_init) (EVP_PKEY_CTX *ctx, EVP_MD_CTX *mctx);
+    int (*verifyctx) (EVP_PKEY_CTX *ctx, const unsigned char *sig, int siglen,
+                      EVP_MD_CTX *mctx);
+    int (*encrypt_init) (EVP_PKEY_CTX *ctx);
+    int (*encrypt) (EVP_PKEY_CTX *ctx, unsigned char *out, size_t *outlen,
+                    const unsigned char *in, size_t inlen);
+    int (*decrypt_init) (EVP_PKEY_CTX *ctx);
+    int (*decrypt) (EVP_PKEY_CTX *ctx, unsigned char *out, size_t *outlen,
+                    const unsigned char *in, size_t inlen);
+    int (*derive_init) (EVP_PKEY_CTX *ctx);
+    int (*derive) (EVP_PKEY_CTX *ctx, unsigned char *key, size_t *keylen);
+    int (*ctrl) (EVP_PKEY_CTX *ctx, int type, int p1, void *p2);
+    int (*ctrl_str) (EVP_PKEY_CTX *ctx, const char *type, const char *value);
+} /* EVP_PKEY_METHOD */ ;
+
+DEFINE_STACK_OF_CONST(EVP_PKEY_METHOD)
+
+void evp_pkey_set_cb_translate(BN_GENCB *cb, EVP_PKEY_CTX *ctx);
+
+extern const EVP_PKEY_METHOD cmac_pkey_meth;
+extern const EVP_PKEY_METHOD dh_pkey_meth;
+extern const EVP_PKEY_METHOD dhx_pkey_meth;
+extern const EVP_PKEY_METHOD dsa_pkey_meth;
+extern const EVP_PKEY_METHOD ec_pkey_meth;
+extern const EVP_PKEY_METHOD ecx25519_pkey_meth;
+extern const EVP_PKEY_METHOD hmac_pkey_meth;
+extern const EVP_PKEY_METHOD rsa_pkey_meth;
+extern const EVP_PKEY_METHOD tls1_prf_pkey_meth;
+extern const EVP_PKEY_METHOD hkdf_pkey_meth;
+#ifndef OPENSSL_NO_SM9
+extern const EVP_PKEY_METHOD sm9_pkey_meth;
+extern const EVP_PKEY_METHOD sm9_master_pkey_meth;
+#endif
+#ifndef OPENSSL_NO_PAILLIER
+extern const EVP_PKEY_METHOD paillier_pkey_meth;
+#endif
+
+struct evp_md_st {
+    int type;
+    int pkey_type;
+    int md_size;
+    unsigned long flags;
+    int (*init) (EVP_MD_CTX *ctx);
+    int (*update) (EVP_MD_CTX *ctx, const void *data, size_t count);
+    int (*final) (EVP_MD_CTX *ctx, unsigned char *md);
+    int (*copy) (EVP_MD_CTX *to, const EVP_MD_CTX *from);
+    int (*cleanup) (EVP_MD_CTX *ctx);
+    int block_size;
+    int ctx_size;               /* how big does the ctx->md_data need to be */
+    /* control function */
+    int (*md_ctrl) (EVP_MD_CTX *ctx, int cmd, int p1, void *p2);
+} /* EVP_MD */ ;
+
+struct evp_cipher_st {
+    int nid;
+    int block_size;
+    /* Default value for variable length ciphers */
+    int key_len;
+    int iv_len;
+    /* Various flags */
+    unsigned long flags;
+    /* init key */
+    int (*init) (EVP_CIPHER_CTX *ctx, const unsigned char *key,
+                 const unsigned char *iv, int enc);
+    /* encrypt/decrypt data */
+    int (*do_cipher) (EVP_CIPHER_CTX *ctx, unsigned char *out,
+                      const unsigned char *in, size_t inl);
+    /* cleanup ctx */
+    int (*cleanup) (EVP_CIPHER_CTX *);
+    /* how big ctx->cipher_data needs to be */
+    int ctx_size;
+    /* Populate a ASN1_TYPE with parameters */
+    int (*set_asn1_parameters) (EVP_CIPHER_CTX *, ASN1_TYPE *);
+    /* Get parameters from a ASN1_TYPE */
+    int (*get_asn1_parameters) (EVP_CIPHER_CTX *, ASN1_TYPE *);
+    /* Miscellaneous operations */
+    int (*ctrl) (EVP_CIPHER_CTX *, int type, int arg, void *ptr);
+    /* Application data */
+    void *app_data;
+} /* EVP_CIPHER */ ;
+
+/* Macros to code block cipher wrappers */
+
+/* Wrapper functions for each cipher mode */
+
+#define EVP_C_DATA(kstruct, ctx) \
+        ((kstruct *)EVP_CIPHER_CTX_get_cipher_data(ctx))
+
+#define BLOCK_CIPHER_ecb_loop() \
+        size_t i, bl; \
+        bl = EVP_CIPHER_CTX_cipher(ctx)->block_size;    \
+        if (inl < bl) return 1;\
+        inl -= bl; \
+        for (i=0; i <= inl; i+=bl)
+
+#define BLOCK_CIPHER_func_ecb(cname, cprefix, kstruct, ksched) \
+static int cname##_ecb_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out, const unsigned char *in, size_t inl) \
+{\
+        BLOCK_CIPHER_ecb_loop() \
+            cprefix##_ecb_encrypt(in + i, out + i, &EVP_C_DATA(kstruct,ctx)->ksched, EVP_CIPHER_CTX_encrypting(ctx)); \
+        return 1;\
+}
+
+#define EVP_MAXCHUNK ((size_t)1<<(sizeof(long)*8-2))
+
+#define BLOCK_CIPHER_func_ofb(cname, cprefix, cbits, kstruct, ksched) \
+    static int cname##_ofb_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out, const unsigned char *in, size_t inl) \
+{\
+        while(inl>=EVP_MAXCHUNK) {\
+            int num = EVP_CIPHER_CTX_num(ctx);\
+            cprefix##_ofb##cbits##_encrypt(in, out, (long)EVP_MAXCHUNK, &EVP_C_DATA(kstruct,ctx)->ksched, EVP_CIPHER_CTX_iv_noconst(ctx), &num); \
+            EVP_CIPHER_CTX_set_num(ctx, num);\
+            inl-=EVP_MAXCHUNK;\
+            in +=EVP_MAXCHUNK;\
+            out+=EVP_MAXCHUNK;\
+        }\
+        if (inl) {\
+            int num = EVP_CIPHER_CTX_num(ctx);\
+            cprefix##_ofb##cbits##_encrypt(in, out, (long)inl, &EVP_C_DATA(kstruct,ctx)->ksched, EVP_CIPHER_CTX_iv_noconst(ctx), &num); \
+            EVP_CIPHER_CTX_set_num(ctx, num);\
+        }\
+        return 1;\
+}
+
+#define BLOCK_CIPHER_func_cbc(cname, cprefix, kstruct, ksched) \
+static int cname##_cbc_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out, const unsigned char *in, size_t inl) \
+{\
+        while(inl>=EVP_MAXCHUNK) \
+            {\
+            cprefix##_cbc_encrypt(in, out, (long)EVP_MAXCHUNK, &EVP_C_DATA(kstruct,ctx)->ksched, EVP_CIPHER_CTX_iv_noconst(ctx), EVP_CIPHER_CTX_encrypting(ctx));\
+            inl-=EVP_MAXCHUNK;\
+            in +=EVP_MAXCHUNK;\
+            out+=EVP_MAXCHUNK;\
+            }\
+        if (inl)\
+            cprefix##_cbc_encrypt(in, out, (long)inl, &EVP_C_DATA(kstruct,ctx)->ksched, EVP_CIPHER_CTX_iv_noconst(ctx), EVP_CIPHER_CTX_encrypting(ctx));\
+        return 1;\
+}
+
+#define BLOCK_CIPHER_func_cfb(cname, cprefix, cbits, kstruct, ksched)  \
+static int cname##_cfb##cbits##_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out, const unsigned char *in, size_t inl) \
+{\
+    size_t chunk = EVP_MAXCHUNK;\
+    if (cbits == 1)  chunk >>= 3;\
+    if (inl < chunk) chunk = inl;\
+    while (inl && inl >= chunk)\
+    {\
+        int num = EVP_CIPHER_CTX_num(ctx);\
+        cprefix##_cfb##cbits##_encrypt(in, out, (long) \
+            ((cbits == 1) \
+                && !EVP_CIPHER_CTX_test_flags(ctx, EVP_CIPH_FLAG_LENGTH_BITS) \
+                ? inl*8 : inl), \
+            &EVP_C_DATA(kstruct, ctx)->ksched, EVP_CIPHER_CTX_iv_noconst(ctx),\
+            &num, EVP_CIPHER_CTX_encrypting(ctx));\
+        EVP_CIPHER_CTX_set_num(ctx, num);\
+        inl -= chunk;\
+        in += chunk;\
+        out += chunk;\
+        if (inl < chunk) chunk = inl;\
+    }\
+    return 1;\
+}
+
+#define BLOCK_CIPHER_all_funcs(cname, cprefix, cbits, kstruct, ksched) \
+        BLOCK_CIPHER_func_cbc(cname, cprefix, kstruct, ksched) \
+        BLOCK_CIPHER_func_cfb(cname, cprefix, cbits, kstruct, ksched) \
+        BLOCK_CIPHER_func_ecb(cname, cprefix, kstruct, ksched) \
+        BLOCK_CIPHER_func_ofb(cname, cprefix, cbits, kstruct, ksched)
+
+#define BLOCK_CIPHER_def1(cname, nmode, mode, MODE, kstruct, nid, block_size, \
+                          key_len, iv_len, flags, init_key, cleanup, \
+                          set_asn1, get_asn1, ctrl) \
+static const EVP_CIPHER cname##_##mode = { \
+        nid##_##nmode, block_size, key_len, iv_len, \
+        flags | EVP_CIPH_##MODE##_MODE, \
+        init_key, \
+        cname##_##mode##_cipher, \
+        cleanup, \
+        sizeof(kstruct), \
+        set_asn1, get_asn1,\
+        ctrl, \
+        NULL \
+}; \
+const EVP_CIPHER *EVP_##cname##_##mode(void) { return &cname##_##mode; }
+
+#define BLOCK_CIPHER_def_cbc(cname, kstruct, nid, block_size, key_len, \
+                             iv_len, flags, init_key, cleanup, set_asn1, \
+                             get_asn1, ctrl) \
+BLOCK_CIPHER_def1(cname, cbc, cbc, CBC, kstruct, nid, block_size, key_len, \
+                  iv_len, flags, init_key, cleanup, set_asn1, get_asn1, ctrl)
+
+#define BLOCK_CIPHER_def_cfb(cname, kstruct, nid, key_len, \
+                             iv_len, cbits, flags, init_key, cleanup, \
+                             set_asn1, get_asn1, ctrl) \
+BLOCK_CIPHER_def1(cname, cfb##cbits, cfb##cbits, CFB, kstruct, nid, 1, \
+                  key_len, iv_len, flags, init_key, cleanup, set_asn1, \
+                  get_asn1, ctrl)
+
+#define BLOCK_CIPHER_def_ofb(cname, kstruct, nid, key_len, \
+                             iv_len, cbits, flags, init_key, cleanup, \
+                             set_asn1, get_asn1, ctrl) \
+BLOCK_CIPHER_def1(cname, ofb##cbits, ofb, OFB, kstruct, nid, 1, \
+                  key_len, iv_len, flags, init_key, cleanup, set_asn1, \
+                  get_asn1, ctrl)
+
+#define BLOCK_CIPHER_def_ecb(cname, kstruct, nid, block_size, key_len, \
+                             flags, init_key, cleanup, set_asn1, \
+                             get_asn1, ctrl) \
+BLOCK_CIPHER_def1(cname, ecb, ecb, ECB, kstruct, nid, block_size, key_len, \
+                  0, flags, init_key, cleanup, set_asn1, get_asn1, ctrl)
+
+#define BLOCK_CIPHER_defs(cname, kstruct, \
+                          nid, block_size, key_len, iv_len, cbits, flags, \
+                          init_key, cleanup, set_asn1, get_asn1, ctrl) \
+BLOCK_CIPHER_def_cbc(cname, kstruct, nid, block_size, key_len, iv_len, flags, \
+                     init_key, cleanup, set_asn1, get_asn1, ctrl) \
+BLOCK_CIPHER_def_cfb(cname, kstruct, nid, key_len, iv_len, cbits, \
+                     flags, init_key, cleanup, set_asn1, get_asn1, ctrl) \
+BLOCK_CIPHER_def_ofb(cname, kstruct, nid, key_len, iv_len, cbits, \
+                     flags, init_key, cleanup, set_asn1, get_asn1, ctrl) \
+BLOCK_CIPHER_def_ecb(cname, kstruct, nid, block_size, key_len, flags, \
+                     init_key, cleanup, set_asn1, get_asn1, ctrl)
+
+/*-
+#define BLOCK_CIPHER_defs(cname, kstruct, \
+                                nid, block_size, key_len, iv_len, flags,\
+                                 init_key, cleanup, set_asn1, get_asn1, ctrl)\
+static const EVP_CIPHER cname##_cbc = {\
+        nid##_cbc, block_size, key_len, iv_len, \
+        flags | EVP_CIPH_CBC_MODE,\
+        init_key,\
+        cname##_cbc_cipher,\
+        cleanup,\
+        sizeof(EVP_CIPHER_CTX)-sizeof((((EVP_CIPHER_CTX *)NULL)->c))+\
+                sizeof((((EVP_CIPHER_CTX *)NULL)->c.kstruct)),\
+        set_asn1, get_asn1,\
+        ctrl, \
+        NULL \
+};\
+const EVP_CIPHER *EVP_##cname##_cbc(void) { return &cname##_cbc; }\
+static const EVP_CIPHER cname##_cfb = {\
+        nid##_cfb64, 1, key_len, iv_len, \
+        flags | EVP_CIPH_CFB_MODE,\
+        init_key,\
+        cname##_cfb_cipher,\
+        cleanup,\
+        sizeof(EVP_CIPHER_CTX)-sizeof((((EVP_CIPHER_CTX *)NULL)->c))+\
+                sizeof((((EVP_CIPHER_CTX *)NULL)->c.kstruct)),\
+        set_asn1, get_asn1,\
+        ctrl,\
+        NULL \
+};\
+const EVP_CIPHER *EVP_##cname##_cfb(void) { return &cname##_cfb; }\
+static const EVP_CIPHER cname##_ofb = {\
+        nid##_ofb64, 1, key_len, iv_len, \
+        flags | EVP_CIPH_OFB_MODE,\
+        init_key,\
+        cname##_ofb_cipher,\
+        cleanup,\
+        sizeof(EVP_CIPHER_CTX)-sizeof((((EVP_CIPHER_CTX *)NULL)->c))+\
+                sizeof((((EVP_CIPHER_CTX *)NULL)->c.kstruct)),\
+        set_asn1, get_asn1,\
+        ctrl,\
+        NULL \
+};\
+const EVP_CIPHER *EVP_##cname##_ofb(void) { return &cname##_ofb; }\
+static const EVP_CIPHER cname##_ecb = {\
+        nid##_ecb, block_size, key_len, iv_len, \
+        flags | EVP_CIPH_ECB_MODE,\
+        init_key,\
+        cname##_ecb_cipher,\
+        cleanup,\
+        sizeof(EVP_CIPHER_CTX)-sizeof((((EVP_CIPHER_CTX *)NULL)->c))+\
+                sizeof((((EVP_CIPHER_CTX *)NULL)->c.kstruct)),\
+        set_asn1, get_asn1,\
+        ctrl,\
+        NULL \
+};\
+const EVP_CIPHER *EVP_##cname##_ecb(void) { return &cname##_ecb; }
+*/
+
+#define IMPLEMENT_BLOCK_CIPHER(cname, ksched, cprefix, kstruct, nid, \
+                               block_size, key_len, iv_len, cbits, \
+                               flags, init_key, \
+                               cleanup, set_asn1, get_asn1, ctrl) \
+        BLOCK_CIPHER_all_funcs(cname, cprefix, cbits, kstruct, ksched) \
+        BLOCK_CIPHER_defs(cname, kstruct, nid, block_size, key_len, iv_len, \
+                          cbits, flags, init_key, cleanup, set_asn1, \
+                          get_asn1, ctrl)
+
+#define IMPLEMENT_CFBR(cipher,cprefix,kstruct,ksched,keysize,cbits,iv_len,fl) \
+        BLOCK_CIPHER_func_cfb(cipher##_##keysize,cprefix,cbits,kstruct,ksched) \
+        BLOCK_CIPHER_def_cfb(cipher##_##keysize,kstruct, \
+                             NID_##cipher##_##keysize, keysize/8, iv_len, cbits, \
+                             (fl)|EVP_CIPH_FLAG_DEFAULT_ASN1, \
+                             cipher##_init_key, NULL, NULL, NULL, NULL)
+
+
+/*
+ * Type needs to be a bit field Sub-type needs to be for variations on the
+ * method, as in, can it do arbitrary encryption....
+ */
+struct evp_pkey_st {
+    int type;
+    int save_type;
+    int references;
+    const EVP_PKEY_ASN1_METHOD *ameth;
+    ENGINE *engine;
+    union {
+        void *ptr;
+# ifndef OPENSSL_NO_RSA
+        struct rsa_st *rsa;     /* RSA */
+# endif
+# ifndef OPENSSL_NO_DSA
+        struct dsa_st *dsa;     /* DSA */
+# endif
+# ifndef OPENSSL_NO_DH
+        struct dh_st *dh;       /* DH */
+# endif
+# ifndef OPENSSL_NO_EC
+        struct ec_key_st *ec;   /* ECC */
+# endif
+# ifndef OPENSSL_NO_PAILLIER	/* PAILLIER */
+        struct paillier_st *paillier;
+# endif
+# ifndef OPENSSL_NO_SM9         /* SM9 */
+        struct SM9_MASTER_KEY_st *sm9_master;
+        struct SM9_KEY_st *sm9;
+# endif
+    } pkey;
+    int save_parameters;
+    STACK_OF(X509_ATTRIBUTE) *attributes; /* [ 0 ] */
+    CRYPTO_RWLOCK *lock;
+} /* EVP_PKEY */ ;
+
+
+void openssl_add_all_ciphers_int(void);
+void openssl_add_all_digests_int(void);
+void evp_cleanup_int(void);
+
+/* Pulling defines out of C soure files */
+
+#define EVP_RC4_KEY_SIZE 16
+#ifndef TLS1_1_VERSION
+# define TLS1_1_VERSION   0x0302
+#endif
diff --git a/crypto/include/internal/md32_common.h b/crypto/include/internal/md32_common.h
new file mode 100644
index 000000000..6e4ce14e9
--- /dev/null
+++ b/crypto/include/internal/md32_common.h
@@ -0,0 +1,383 @@
+/*
+ * Copyright 1999-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*-
+ * This is a generic 32 bit "collector" for message digest algorithms.
+ * Whenever needed it collects input character stream into chunks of
+ * 32 bit values and invokes a block function that performs actual hash
+ * calculations.
+ *
+ * Porting guide.
+ *
+ * Obligatory macros:
+ *
+ * DATA_ORDER_IS_BIG_ENDIAN or DATA_ORDER_IS_LITTLE_ENDIAN
+ *      this macro defines byte order of input stream.
+ * HASH_CBLOCK
+ *      size of a unit chunk HASH_BLOCK operates on.
+ * HASH_LONG
+ *      has to be at lest 32 bit wide.
+ * HASH_CTX
+ *      context structure that at least contains following
+ *      members:
+ *              typedef struct {
+ *                      ...
+ *                      HASH_LONG       Nl,Nh;
+ *                      either {
+ *                      HASH_LONG       data[HASH_LBLOCK];
+ *                      unsigned char   data[HASH_CBLOCK];
+ *                      };
+ *                      unsigned int    num;
+ *                      ...
+ *                      } HASH_CTX;
+ *      data[] vector is expected to be zeroed upon first call to
+ *      HASH_UPDATE.
+ * HASH_UPDATE
+ *      name of "Update" function, implemented here.
+ * HASH_TRANSFORM
+ *      name of "Transform" function, implemented here.
+ * HASH_FINAL
+ *      name of "Final" function, implemented here.
+ * HASH_BLOCK_DATA_ORDER
+ *      name of "block" function capable of treating *unaligned* input
+ *      message in original (data) byte order, implemented externally.
+ * HASH_MAKE_STRING
+ *      macro convering context variables to an ASCII hash string.
+ *
+ * MD5 example:
+ *
+ *      #define DATA_ORDER_IS_LITTLE_ENDIAN
+ *
+ *      #define HASH_LONG               MD5_LONG
+ *      #define HASH_CTX                MD5_CTX
+ *      #define HASH_CBLOCK             MD5_CBLOCK
+ *      #define HASH_UPDATE             MD5_Update
+ *      #define HASH_TRANSFORM          MD5_Transform
+ *      #define HASH_FINAL              MD5_Final
+ *      #define HASH_BLOCK_DATA_ORDER   md5_block_data_order
+ *
+ *                                      
+ */
+
+#include 
+
+#if !defined(DATA_ORDER_IS_BIG_ENDIAN) && !defined(DATA_ORDER_IS_LITTLE_ENDIAN)
+# error "DATA_ORDER must be defined!"
+#endif
+
+#ifndef HASH_CBLOCK
+# error "HASH_CBLOCK must be defined!"
+#endif
+#ifndef HASH_LONG
+# error "HASH_LONG must be defined!"
+#endif
+#ifndef HASH_CTX
+# error "HASH_CTX must be defined!"
+#endif
+
+#ifndef HASH_UPDATE
+# error "HASH_UPDATE must be defined!"
+#endif
+#ifndef HASH_TRANSFORM
+# error "HASH_TRANSFORM must be defined!"
+#endif
+#ifndef HASH_FINAL
+# error "HASH_FINAL must be defined!"
+#endif
+
+#ifndef HASH_BLOCK_DATA_ORDER
+# error "HASH_BLOCK_DATA_ORDER must be defined!"
+#endif
+
+/*
+ * Engage compiler specific rotate intrinsic function if available.
+ */
+#undef ROTATE
+#ifndef PEDANTIC
+# if defined(_MSC_VER)
+#  define ROTATE(a,n)   _lrotl(a,n)
+# elif defined(__ICC)
+#  define ROTATE(a,n)   _rotl(a,n)
+# elif defined(__GNUC__) && __GNUC__>=2 && !defined(OPENSSL_NO_ASM) && !defined(OPENSSL_NO_INLINE_ASM)
+  /*
+   * Some GNU C inline assembler templates. Note that these are
+   * rotates by *constant* number of bits! But that's exactly
+   * what we need here...
+   *                                    
+   */
+#  if defined(__i386) || defined(__i386__) || defined(__x86_64) || defined(__x86_64__)
+#   define ROTATE(a,n)  ({ register unsigned int ret;   \
+                                asm (                   \
+                                "roll %1,%0"            \
+                                : "=r"(ret)             \
+                                : "I"(n), "0"((unsigned int)(a))        \
+                                : "cc");                \
+                           ret;                         \
+                        })
+#  elif defined(_ARCH_PPC) || defined(_ARCH_PPC64) || \
+        defined(__powerpc) || defined(__ppc__) || defined(__powerpc64__)
+#   define ROTATE(a,n)  ({ register unsigned int ret;   \
+                                asm (                   \
+                                "rlwinm %0,%1,%2,0,31"  \
+                                : "=r"(ret)             \
+                                : "r"(a), "I"(n));      \
+                           ret;                         \
+                        })
+#  elif defined(__s390x__)
+#   define ROTATE(a,n) ({ register unsigned int ret;    \
+                                asm ("rll %0,%1,%2"     \
+                                : "=r"(ret)             \
+                                : "r"(a), "I"(n));      \
+                          ret;                          \
+                        })
+#  endif
+# endif
+#endif                          /* PEDANTIC */
+
+#ifndef ROTATE
+# define ROTATE(a,n)     (((a)<<(n))|(((a)&0xffffffff)>>(32-(n))))
+#endif
+
+#if defined(DATA_ORDER_IS_BIG_ENDIAN)
+
+# ifndef PEDANTIC
+#  if defined(__GNUC__) && __GNUC__>=2 && !defined(OPENSSL_NO_ASM) && !defined(OPENSSL_NO_INLINE_ASM)
+#   if ((defined(__i386) || defined(__i386__)) && !defined(I386_ONLY)) || \
+      (defined(__x86_64) || defined(__x86_64__))
+#    if !defined(B_ENDIAN)
+    /*
+     * This gives ~30-40% performance improvement in SHA-256 compiled
+     * with gcc [on P4]. Well, first macro to be frank. We can pull
+     * this trick on x86* platforms only, because these CPUs can fetch
+     * unaligned data without raising an exception.
+     */
+#     define HOST_c2l(c,l)        ({ unsigned int r=*((const unsigned int *)(c)); \
+                                   asm ("bswapl %0":"=r"(r):"0"(r));    \
+                                   (c)+=4; (l)=r;                       })
+#     define HOST_l2c(l,c)        ({ unsigned int r=(l);                  \
+                                   asm ("bswapl %0":"=r"(r):"0"(r));    \
+                                   *((unsigned int *)(c))=r; (c)+=4; r; })
+#    endif
+#   elif defined(__aarch64__)
+#    if defined(__BYTE_ORDER__)
+#     if defined(__ORDER_LITTLE_ENDIAN__) && __BYTE_ORDER__==__ORDER_LITTLE_ENDIAN__
+#      define HOST_c2l(c,l)      ({ unsigned int r;              \
+                                   asm ("rev    %w0,%w1"        \
+                                        :"=r"(r)                \
+                                        :"r"(*((const unsigned int *)(c))));\
+                                   (c)+=4; (l)=r;               })
+#      define HOST_l2c(l,c)      ({ unsigned int r;              \
+                                   asm ("rev    %w0,%w1"        \
+                                        :"=r"(r)                \
+                                        :"r"((unsigned int)(l)));\
+                                   *((unsigned int *)(c))=r; (c)+=4; r; })
+#     elif defined(__ORDER_BIG_ENDIAN__) && __BYTE_ORDER__==__ORDER_BIG_ENDIAN__
+#      define HOST_c2l(c,l)      ((l)=*((const unsigned int *)(c)), (c)+=4, (l))
+#      define HOST_l2c(l,c)      (*((unsigned int *)(c))=(l), (c)+=4, (l))
+#     endif
+#    endif
+#   endif
+#  endif
+#  if defined(__s390__) || defined(__s390x__)
+#   define HOST_c2l(c,l) ((l)=*((const unsigned int *)(c)), (c)+=4, (l))
+#   define HOST_l2c(l,c) (*((unsigned int *)(c))=(l), (c)+=4, (l))
+#  endif
+# endif
+
+# ifndef HOST_c2l
+#  define HOST_c2l(c,l)   (l =(((unsigned long)(*((c)++)))<<24),          \
+                         l|=(((unsigned long)(*((c)++)))<<16),          \
+                         l|=(((unsigned long)(*((c)++)))<< 8),          \
+                         l|=(((unsigned long)(*((c)++)))    )           )
+# endif
+# ifndef HOST_l2c
+#  define HOST_l2c(l,c)   (*((c)++)=(unsigned char)(((l)>>24)&0xff),      \
+                         *((c)++)=(unsigned char)(((l)>>16)&0xff),      \
+                         *((c)++)=(unsigned char)(((l)>> 8)&0xff),      \
+                         *((c)++)=(unsigned char)(((l)    )&0xff),      \
+                         l)
+# endif
+
+#elif defined(DATA_ORDER_IS_LITTLE_ENDIAN)
+
+# ifndef PEDANTIC
+#  if defined(__GNUC__) && __GNUC__>=2 && !defined(OPENSSL_NO_ASM) && !defined(OPENSSL_NO_INLINE_ASM)
+#   if defined(__s390x__)
+#    define HOST_c2l(c,l)        ({ asm ("lrv    %0,%1"                  \
+                                   :"=d"(l) :"m"(*(const unsigned int *)(c)));\
+                                   (c)+=4; (l);                         })
+#    define HOST_l2c(l,c)        ({ asm ("strv   %1,%0"                  \
+                                   :"=m"(*(unsigned int *)(c)) :"d"(l));\
+                                   (c)+=4; (l);                         })
+#   endif
+#  endif
+#  if defined(__i386) || defined(__i386__) || defined(__x86_64) || defined(__x86_64__)
+#   ifndef B_ENDIAN
+    /* See comment in DATA_ORDER_IS_BIG_ENDIAN section. */
+#    define HOST_c2l(c,l)        ((l)=*((const unsigned int *)(c)), (c)+=4, l)
+#    define HOST_l2c(l,c)        (*((unsigned int *)(c))=(l), (c)+=4, l)
+#   endif
+#  endif
+# endif
+
+# ifndef HOST_c2l
+#  define HOST_c2l(c,l)   (l =(((unsigned long)(*((c)++)))    ),          \
+                         l|=(((unsigned long)(*((c)++)))<< 8),          \
+                         l|=(((unsigned long)(*((c)++)))<<16),          \
+                         l|=(((unsigned long)(*((c)++)))<<24)           )
+# endif
+# ifndef HOST_l2c
+#  define HOST_l2c(l,c)   (*((c)++)=(unsigned char)(((l)    )&0xff),      \
+                         *((c)++)=(unsigned char)(((l)>> 8)&0xff),      \
+                         *((c)++)=(unsigned char)(((l)>>16)&0xff),      \
+                         *((c)++)=(unsigned char)(((l)>>24)&0xff),      \
+                         l)
+# endif
+
+#endif
+
+/*
+ * Time for some action:-)
+ */
+
+int HASH_UPDATE(HASH_CTX *c, const void *data_, size_t len)
+{
+    const unsigned char *data = data_;
+    unsigned char *p;
+    HASH_LONG l;
+    size_t n;
+
+    if (len == 0)
+        return 1;
+
+    l = (c->Nl + (((HASH_LONG) len) << 3)) & 0xffffffffUL;
+    /*
+     * 95-05-24 eay Fixed a bug with the overflow handling, thanks to Wei Dai
+     *  for pointing it out.
+     */
+    if (l < c->Nl)              /* overflow */
+        c->Nh++;
+    c->Nh += (HASH_LONG) (len >> 29); /* might cause compiler warning on
+                                       * 16-bit */
+    c->Nl = l;
+
+    n = c->num;
+    if (n != 0) {
+        p = (unsigned char *)c->data;
+
+        if (len >= HASH_CBLOCK || len + n >= HASH_CBLOCK) {
+            memcpy(p + n, data, HASH_CBLOCK - n);
+            HASH_BLOCK_DATA_ORDER(c, p, 1);
+            n = HASH_CBLOCK - n;
+            data += n;
+            len -= n;
+            c->num = 0;
+            /*
+             * We use memset rather than OPENSSL_cleanse() here deliberately.
+             * Using OPENSSL_cleanse() here could be a performance issue. It
+             * will get properly cleansed on finalisation so this isn't a
+             * security problem.
+             */
+            memset(p, 0, HASH_CBLOCK); /* keep it zeroed */
+        } else {
+            memcpy(p + n, data, len);
+            c->num += (unsigned int)len;
+            return 1;
+        }
+    }
+
+    n = len / HASH_CBLOCK;
+    if (n > 0) {
+        HASH_BLOCK_DATA_ORDER(c, data, n);
+        n *= HASH_CBLOCK;
+        data += n;
+        len -= n;
+    }
+
+    if (len != 0) {
+        p = (unsigned char *)c->data;
+        c->num = (unsigned int)len;
+        memcpy(p, data, len);
+    }
+    return 1;
+}
+
+void HASH_TRANSFORM(HASH_CTX *c, const unsigned char *data)
+{
+    HASH_BLOCK_DATA_ORDER(c, data, 1);
+}
+
+int HASH_FINAL(unsigned char *md, HASH_CTX *c)
+{
+    unsigned char *p = (unsigned char *)c->data;
+    size_t n = c->num;
+
+    p[n] = 0x80;                /* there is always room for one */
+    n++;
+
+    if (n > (HASH_CBLOCK - 8)) {
+        memset(p + n, 0, HASH_CBLOCK - n);
+        n = 0;
+        HASH_BLOCK_DATA_ORDER(c, p, 1);
+    }
+    memset(p + n, 0, HASH_CBLOCK - 8 - n);
+
+    p += HASH_CBLOCK - 8;
+#if   defined(DATA_ORDER_IS_BIG_ENDIAN)
+    (void)HOST_l2c(c->Nh, p);
+    (void)HOST_l2c(c->Nl, p);
+#elif defined(DATA_ORDER_IS_LITTLE_ENDIAN)
+    (void)HOST_l2c(c->Nl, p);
+    (void)HOST_l2c(c->Nh, p);
+#endif
+    p -= HASH_CBLOCK;
+    HASH_BLOCK_DATA_ORDER(c, p, 1);
+    c->num = 0;
+    OPENSSL_cleanse(p, HASH_CBLOCK);
+
+#ifndef HASH_MAKE_STRING
+# error "HASH_MAKE_STRING must be defined!"
+#else
+    HASH_MAKE_STRING(c, md);
+#endif
+
+    return 1;
+}
+
+#ifndef MD32_REG_T
+# if defined(__alpha) || defined(__sparcv9) || defined(__mips)
+#  define MD32_REG_T long
+/*
+ * This comment was originally written for MD5, which is why it
+ * discusses A-D. But it basically applies to all 32-bit digests,
+ * which is why it was moved to common header file.
+ *
+ * In case you wonder why A-D are declared as long and not
+ * as MD5_LONG. Doing so results in slight performance
+ * boost on LP64 architectures. The catch is we don't
+ * really care if 32 MSBs of a 64-bit register get polluted
+ * with eventual overflows as we *save* only 32 LSBs in
+ * *either* case. Now declaring 'em long excuses the compiler
+ * from keeping 32 MSBs zeroed resulting in 13% performance
+ * improvement under SPARC Solaris7/64 and 5% under AlphaLinux.
+ * Well, to be honest it should say that this *prevents*
+ * performance degradation.
+ *                              
+ */
+# else
+/*
+ * Above is not absolute and there are LP64 compilers that
+ * generate better code if MD32_REG_T is defined int. The above
+ * pre-processor condition reflects the circumstances under which
+ * the conclusion was made and is subject to further extension.
+ *                              
+ */
+#  define MD32_REG_T int
+# endif
+#endif
diff --git a/crypto/include/internal/objects.h b/crypto/include/internal/objects.h
new file mode 100644
index 000000000..76e1b4d98
--- /dev/null
+++ b/crypto/include/internal/objects.h
@@ -0,0 +1,12 @@
+/*
+ * Copyright 2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+
+void obj_cleanup_int(void);
diff --git a/crypto/include/internal/poly1305.h b/crypto/include/internal/poly1305.h
new file mode 100644
index 000000000..1bc8716fc
--- /dev/null
+++ b/crypto/include/internal/poly1305.h
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2015-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+
+#define POLY1305_BLOCK_SIZE 16
+
+typedef struct poly1305_context POLY1305;
+
+size_t Poly1305_ctx_size(void);
+void Poly1305_Init(POLY1305 *ctx, const unsigned char key[32]);
+void Poly1305_Update(POLY1305 *ctx, const unsigned char *inp, size_t len);
+void Poly1305_Final(POLY1305 *ctx, unsigned char mac[16]);
diff --git a/crypto/include/internal/rand.h b/crypto/include/internal/rand.h
new file mode 100644
index 000000000..30887c4a7
--- /dev/null
+++ b/crypto/include/internal/rand.h
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * Licensed under the OpenSSL licenses, (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * https://www.openssl.org/source/license.html
+ * or in the file LICENSE in the source distribution.
+ */
+
+#include 
+
+void rand_cleanup_int(void);
diff --git a/crypto/include/internal/rotate.h b/crypto/include/internal/rotate.h
new file mode 100644
index 000000000..2d9a085f9
--- /dev/null
+++ b/crypto/include/internal/rotate.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright 1999-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+
+/*
+ * Engage compiler specific rotate intrinsic function if available.
+ */
+#undef ROL32
+#ifndef PEDANTIC
+# if defined(_MSC_VER)
+#  define ROL32(a,n)   _lrotl(a,n)
+# elif defined(__ICC)
+#  define ROL32(a,n)   _rotl(a,n)
+# elif defined(__GNUC__) && __GNUC__>=2 && !defined(OPENSSL_NO_ASM) && !defined(OPENSSL_NO_INLINE_ASM)
+  /*
+   * Some GNU C inline assembler templates. Note that these are
+   * rotates by *constant* number of bits! But that's exactly
+   * what we need here...
+   *                                    
+   */
+#  if defined(__i386) || defined(__i386__) || defined(__x86_64) || defined(__x86_64__)
+#   define ROL32(a,n)  ({ register unsigned int ret;   \
+                                asm (                   \
+                                "roll %1,%0"            \
+                                : "=r"(ret)             \
+                                : "I"(n), "0"((unsigned int)(a))        \
+                                : "cc");                \
+                           ret;                         \
+                        })
+#  elif defined(_ARCH_PPC) || defined(_ARCH_PPC64) || \
+        defined(__powerpc) || defined(__ppc__) || defined(__powerpc64__)
+#   define ROL32(a,n)  ({ register unsigned int ret;   \
+                                asm (                   \
+                                "rlwinm %0,%1,%2,0,31"  \
+                                : "=r"(ret)             \
+                                : "r"(a), "I"(n));      \
+                           ret;                         \
+                        })
+#  elif defined(__s390x__)
+#   define ROL32(a,n) ({ register unsigned int ret;    \
+                                asm ("rll %0,%1,%2"     \
+                                : "=r"(ret)             \
+                                : "r"(a), "I"(n));      \
+                          ret;                          \
+                        })
+#  endif
+# endif
+#endif                          /* PEDANTIC */
+
+#ifndef ROL32
+# define ROL32(a,n)     (((a)<<(n))|(((a)&0xffffffff)>>(32-(n))))
+#endif
diff --git a/crypto/include/internal/x509_int.h b/crypto/include/internal/x509_int.h
new file mode 100644
index 000000000..e54cca05b
--- /dev/null
+++ b/crypto/include/internal/x509_int.h
@@ -0,0 +1,267 @@
+/*
+ * Copyright 2015-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/* Internal X509 structures and functions: not for application use */
+
+/* Note: unless otherwise stated a field pointer is mandatory and should
+ * never be set to NULL: the ASN.1 code and accessors rely on mandatory
+ * fields never being NULL.
+ */
+
+/*
+ * name entry structure, equivalent to AttributeTypeAndValue defined
+ * in RFC5280 et al.
+ */
+struct X509_name_entry_st {
+    ASN1_OBJECT *object;        /* AttributeType */
+    ASN1_STRING *value;         /* AttributeValue */
+    int set;                    /* index of RDNSequence for this entry */
+    int size;                   /* temp variable */
+};
+
+/* Name from RFC 5280. */
+struct X509_name_st {
+    STACK_OF(X509_NAME_ENTRY) *entries; /* DN components */
+    int modified;               /* true if 'bytes' needs to be built */
+    BUF_MEM *bytes;             /* cached encoding: cannot be NULL */
+    /* canonical encoding used for rapid Name comparison */
+    unsigned char *canon_enc;
+    int canon_enclen;
+} /* X509_NAME */ ;
+
+/* PKCS#10 certificate request */
+
+struct X509_req_info_st {
+    ASN1_ENCODING enc;          /* cached encoding of signed part */
+    ASN1_INTEGER *version;      /* version, defaults to v1(0) so can be NULL */
+    X509_NAME *subject;         /* certificate request DN */
+    X509_PUBKEY *pubkey;        /* public key of request */
+    /*
+     * Zero or more attributes.
+     * NB: although attributes is a mandatory field some broken
+     * encodings omit it so this may be NULL in that case.
+     */
+    STACK_OF(X509_ATTRIBUTE) *attributes;
+};
+
+struct X509_req_st {
+    X509_REQ_INFO req_info;     /* signed certificate request data */
+    X509_ALGOR sig_alg;         /* signature algorithm */
+    ASN1_BIT_STRING *signature; /* signature */
+    int references;
+    CRYPTO_RWLOCK *lock;
+};
+
+struct X509_crl_info_st {
+    ASN1_INTEGER *version;      /* version: defaults to v1(0) so may be NULL */
+    X509_ALGOR sig_alg;         /* signature algorithm */
+    X509_NAME *issuer;          /* CRL issuer name */
+    ASN1_TIME *lastUpdate;      /* lastUpdate field */
+    ASN1_TIME *nextUpdate;      /* nextUpdate field: optional */
+    STACK_OF(X509_REVOKED) *revoked;        /* revoked entries: optional */
+    STACK_OF(X509_EXTENSION) *extensions;   /* extensions: optional */
+    ASN1_ENCODING enc;                      /* encoding of signed portion of CRL */
+};
+
+struct X509_crl_st {
+    X509_CRL_INFO crl;          /* signed CRL data */
+    X509_ALGOR sig_alg;         /* CRL signature algorithm */
+    ASN1_BIT_STRING signature;  /* CRL signature */
+    int references;
+    int flags;
+    /*
+     * Cached copies of decoded extension values, since extensions
+     * are optional any of these can be NULL.
+     */
+    AUTHORITY_KEYID *akid;
+    ISSUING_DIST_POINT *idp;
+    /* Convenient breakdown of IDP */
+    int idp_flags;
+    int idp_reasons;
+    /* CRL and base CRL numbers for delta processing */
+    ASN1_INTEGER *crl_number;
+    ASN1_INTEGER *base_crl_number;
+    STACK_OF(GENERAL_NAMES) *issuers;
+    /* hash of CRL */
+    unsigned char sha1_hash[32 /*SHA_DIGEST_LENGTH*/];
+    /* alternative method to handle this CRL */
+    const X509_CRL_METHOD *meth;
+    void *meth_data;
+    CRYPTO_RWLOCK *lock;
+};
+
+struct x509_revoked_st {
+    ASN1_INTEGER serialNumber; /* revoked entry serial number */
+    ASN1_TIME *revocationDate;  /* revocation date */
+    STACK_OF(X509_EXTENSION) *extensions;   /* CRL entry extensions: optional */
+    /* decoded value of CRLissuer extension: set if indirect CRL */
+    STACK_OF(GENERAL_NAME) *issuer;
+    /* revocation reason: set to CRL_REASON_NONE if reason extension absent */
+    int reason;
+    /*
+     * CRL entries are reordered for faster lookup of serial numbers. This
+     * field contains the original load sequence for this entry.
+     */
+    int sequence;
+};
+
+/*
+ * This stuff is certificate "auxiliary info": it contains details which are
+ * useful in certificate stores and databases. When used this is tagged onto
+ * the end of the certificate itself. OpenSSL specific structure not defined
+ * in any RFC.
+ */
+
+struct x509_cert_aux_st {
+    STACK_OF(ASN1_OBJECT) *trust; /* trusted uses */
+    STACK_OF(ASN1_OBJECT) *reject; /* rejected uses */
+    ASN1_UTF8STRING *alias;     /* "friendly name" */
+    ASN1_OCTET_STRING *keyid;   /* key id of private key */
+    STACK_OF(X509_ALGOR) *other; /* other unspecified info */
+};
+
+struct x509_cinf_st {
+    ASN1_INTEGER *version;      /* [ 0 ] default of v1 */
+    ASN1_INTEGER serialNumber;
+    X509_ALGOR signature;
+    X509_NAME *issuer;
+    X509_VAL validity;
+    X509_NAME *subject;
+    X509_PUBKEY *key;
+    ASN1_BIT_STRING *issuerUID; /* [ 1 ] optional in v2 */
+    ASN1_BIT_STRING *subjectUID; /* [ 2 ] optional in v2 */
+    STACK_OF(X509_EXTENSION) *extensions; /* [ 3 ] optional in v3 */
+    ASN1_ENCODING enc;
+};
+
+struct x509_st {
+    X509_CINF cert_info;
+    X509_ALGOR sig_alg;
+    ASN1_BIT_STRING signature;
+    int references;
+    CRYPTO_EX_DATA ex_data;
+    /* These contain copies of various extension values */
+    long ex_pathlen;
+    long ex_pcpathlen;
+    uint32_t ex_flags;
+    uint32_t ex_kusage;
+    uint32_t ex_xkusage;
+    uint32_t ex_nscert;
+    ASN1_OCTET_STRING *skid;
+    AUTHORITY_KEYID *akid;
+    X509_POLICY_CACHE *policy_cache;
+    STACK_OF(DIST_POINT) *crldp;
+    STACK_OF(GENERAL_NAME) *altname;
+    NAME_CONSTRAINTS *nc;
+#ifndef OPENSSL_NO_RFC3779
+    STACK_OF(IPAddressFamily) *rfc3779_addr;
+    struct ASIdentifiers_st *rfc3779_asid;
+# endif
+    unsigned char sha1_hash[32 /*SHA_DIGEST_LENGTH*/];
+    X509_CERT_AUX *aux;
+    CRYPTO_RWLOCK *lock;
+} /* X509 */ ;
+
+/*
+ * This is a used when verifying cert chains.  Since the gathering of the
+ * cert chain can take some time (and have to be 'retried', this needs to be
+ * kept and passed around.
+ */
+struct x509_store_ctx_st {      /* X509_STORE_CTX */
+    X509_STORE *ctx;
+    /* The following are set by the caller */
+    /* The cert to check */
+    X509 *cert;
+    /* chain of X509s - untrusted - passed in */
+    STACK_OF(X509) *untrusted;
+    /* set of CRLs passed in */
+    STACK_OF(X509_CRL) *crls;
+    X509_VERIFY_PARAM *param;
+    /* Other info for use with get_issuer() */
+    void *other_ctx;
+    /* Callbacks for various operations */
+    /* called to verify a certificate */
+    int (*verify) (X509_STORE_CTX *ctx);
+    /* error callback */
+    int (*verify_cb) (int ok, X509_STORE_CTX *ctx);
+    /* get issuers cert from ctx */
+    int (*get_issuer) (X509 **issuer, X509_STORE_CTX *ctx, X509 *x);
+    /* check issued */
+    int (*check_issued) (X509_STORE_CTX *ctx, X509 *x, X509 *issuer);
+    /* Check revocation status of chain */
+    int (*check_revocation) (X509_STORE_CTX *ctx);
+    /* retrieve CRL */
+    int (*get_crl) (X509_STORE_CTX *ctx, X509_CRL **crl, X509 *x);
+    /* Check CRL validity */
+    int (*check_crl) (X509_STORE_CTX *ctx, X509_CRL *crl);
+    /* Check certificate against CRL */
+    int (*cert_crl) (X509_STORE_CTX *ctx, X509_CRL *crl, X509 *x);
+    /* Check policy status of the chain */
+    int (*check_policy) (X509_STORE_CTX *ctx);
+    STACK_OF(X509) *(*lookup_certs) (X509_STORE_CTX *ctx, X509_NAME *nm);
+    STACK_OF(X509_CRL) *(*lookup_crls) (X509_STORE_CTX *ctx, X509_NAME *nm);
+    int (*cleanup) (X509_STORE_CTX *ctx);
+    /* The following is built up */
+    /* if 0, rebuild chain */
+    int valid;
+    /* number of untrusted certs */
+    int num_untrusted;
+    /* chain of X509s - built up and trusted */
+    STACK_OF(X509) *chain;
+    /* Valid policy tree */
+    X509_POLICY_TREE *tree;
+    /* Require explicit policy value */
+    int explicit_policy;
+    /* When something goes wrong, this is why */
+    int error_depth;
+    int error;
+    X509 *current_cert;
+    /* cert currently being tested as valid issuer */
+    X509 *current_issuer;
+    /* current CRL */
+    X509_CRL *current_crl;
+    /* score of current CRL */
+    int current_crl_score;
+    /* Reason mask */
+    unsigned int current_reasons;
+    /* For CRL path validation: parent context */
+    X509_STORE_CTX *parent;
+    CRYPTO_EX_DATA ex_data;
+    SSL_DANE *dane;
+    /* signed via bare TA public key, rather than CA certificate */
+    int bare_ta_signed;
+};
+
+/* PKCS#8 private key info structure */
+
+struct pkcs8_priv_key_info_st {
+    ASN1_INTEGER *version;
+    X509_ALGOR *pkeyalg;
+    ASN1_OCTET_STRING *pkey;
+    STACK_OF(X509_ATTRIBUTE) *attributes;
+};
+
+struct X509_sig_st {
+    X509_ALGOR *algor;
+    ASN1_OCTET_STRING *digest;
+};
+
+struct x509_object_st {
+    /* one of the above types */
+    X509_LOOKUP_TYPE type;
+    union {
+        char *ptr;
+        X509 *x509;
+        X509_CRL *crl;
+        EVP_PKEY *pkey;
+    } data;
+};
+
+int a2i_ipadd(unsigned char *ipout, const char *ipasc);
+int x509_set1_time(ASN1_TIME **ptm, const ASN1_TIME *tm);
diff --git a/crypto/init.c b/crypto/init.c
new file mode 100644
index 000000000..a5dc8379e
--- /dev/null
+++ b/crypto/init.c
@@ -0,0 +1,698 @@
+/*
+ * Copyright 2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+static int stopped = 0;
+
+static void ossl_init_thread_stop(struct thread_local_inits_st *locals);
+
+static CRYPTO_THREAD_LOCAL threadstopkey;
+
+static void ossl_init_thread_stop_wrap(void *local)
+{
+    ossl_init_thread_stop((struct thread_local_inits_st *)local);
+}
+
+static struct thread_local_inits_st *ossl_init_get_thread_local(int alloc)
+{
+    struct thread_local_inits_st *local =
+        CRYPTO_THREAD_get_local(&threadstopkey);
+
+    if (local == NULL && alloc) {
+        local = OPENSSL_zalloc(sizeof *local);
+        CRYPTO_THREAD_set_local(&threadstopkey, local);
+    }
+    if (!alloc) {
+        CRYPTO_THREAD_set_local(&threadstopkey, NULL);
+    }
+
+    return local;
+}
+
+typedef struct ossl_init_stop_st OPENSSL_INIT_STOP;
+struct ossl_init_stop_st {
+    void (*handler)(void);
+    OPENSSL_INIT_STOP *next;
+};
+
+static OPENSSL_INIT_STOP *stop_handlers = NULL;
+static CRYPTO_RWLOCK *init_lock = NULL;
+
+static CRYPTO_ONCE base = CRYPTO_ONCE_STATIC_INIT;
+static int base_inited = 0;
+DEFINE_RUN_ONCE_STATIC(ossl_init_base)
+{
+#ifdef OPENSSL_INIT_DEBUG
+    fprintf(stderr, "OPENSSL_INIT: ossl_init_base: Setting up stop handlers\n");
+#endif
+    /*
+     * We use a dummy thread local key here. We use the destructor to detect
+     * when the thread is going to stop (where that feature is available)
+     */
+    CRYPTO_THREAD_init_local(&threadstopkey, ossl_init_thread_stop_wrap);
+#ifndef OPENSSL_SYS_UEFI
+    atexit(OPENSSL_cleanup);
+#endif
+    if ((init_lock = CRYPTO_THREAD_lock_new()) == NULL)
+        return 0;
+    OPENSSL_cpuid_setup();
+
+    /*
+     * BIG FAT WARNING!
+     * Everything needed to be initialized in this function before threads
+     * come along MUST happen before base_inited is set to 1, or we will
+     * see race conditions.
+     */
+    base_inited = 1;
+
+#if !defined(OPENSSL_NO_DSO) && !defined(OPENSSL_USE_NODELETE)
+# ifdef DSO_WIN32
+    {
+        HMODULE handle = NULL;
+        BOOL ret;
+
+        /* We don't use the DSO route for WIN32 because there is a better way */
+        ret = GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS
+                                | GET_MODULE_HANDLE_EX_FLAG_PIN,
+                                (void *)&base_inited, &handle);
+
+        return (ret == TRUE) ? 1 : 0;
+    }
+# else
+    /*
+     * Deliberately leak a reference to ourselves. This will force the library
+     * to remain loaded until the atexit() handler is run a process exit.
+     */
+    {
+        DSO *dso = NULL;
+
+        dso = DSO_dsobyaddr(&base_inited, DSO_FLAG_NO_UNLOAD_ON_FREE);
+        DSO_free(dso);
+    }
+# endif
+#endif
+
+    return 1;
+}
+
+static CRYPTO_ONCE load_crypto_strings = CRYPTO_ONCE_STATIC_INIT;
+static int load_crypto_strings_inited = 0;
+DEFINE_RUN_ONCE_STATIC(ossl_init_no_load_crypto_strings)
+{
+    /* Do nothing in this case */
+    return 1;
+}
+
+DEFINE_RUN_ONCE_STATIC(ossl_init_load_crypto_strings)
+{
+    int ret = 1;
+    /*
+     * OPENSSL_NO_AUTOERRINIT is provided here to prevent at compile time
+     * pulling in all the error strings during static linking
+     */
+#if !defined(OPENSSL_NO_ERR) && !defined(OPENSSL_NO_AUTOERRINIT)
+# ifdef OPENSSL_INIT_DEBUG
+    fprintf(stderr, "OPENSSL_INIT: ossl_init_load_crypto_strings: "
+                    "err_load_crypto_strings_int()\n");
+# endif
+    ret = err_load_crypto_strings_int();
+    load_crypto_strings_inited = 1;
+#endif    
+    return ret;
+}
+
+static CRYPTO_ONCE add_all_ciphers = CRYPTO_ONCE_STATIC_INIT;
+DEFINE_RUN_ONCE_STATIC(ossl_init_add_all_ciphers)
+{
+    /*
+     * OPENSSL_NO_AUTOALGINIT is provided here to prevent at compile time
+     * pulling in all the ciphers during static linking
+     */
+#ifndef OPENSSL_NO_AUTOALGINIT
+# ifdef OPENSSL_INIT_DEBUG
+    fprintf(stderr, "OPENSSL_INIT: ossl_init_add_all_ciphers: "
+                    "openssl_add_all_ciphers_int()\n");
+# endif
+    openssl_add_all_ciphers_int();
+#endif
+    return 1;
+}
+
+static CRYPTO_ONCE add_all_digests = CRYPTO_ONCE_STATIC_INIT;
+DEFINE_RUN_ONCE_STATIC(ossl_init_add_all_digests)
+{
+    /*
+     * OPENSSL_NO_AUTOALGINIT is provided here to prevent at compile time
+     * pulling in all the ciphers during static linking
+     */
+#ifndef OPENSSL_NO_AUTOALGINIT
+# ifdef OPENSSL_INIT_DEBUG
+    fprintf(stderr, "OPENSSL_INIT: ossl_init_add_all_digests: "
+                    "openssl_add_all_digests()\n");
+# endif
+    openssl_add_all_digests_int();
+#endif
+    return 1;
+}
+
+DEFINE_RUN_ONCE_STATIC(ossl_init_no_add_algs)
+{
+    /* Do nothing */
+    return 1;
+}
+
+static CRYPTO_ONCE config = CRYPTO_ONCE_STATIC_INIT;
+static int config_inited = 0;
+static const char *appname;
+DEFINE_RUN_ONCE_STATIC(ossl_init_config)
+{
+#ifdef OPENSSL_INIT_DEBUG
+    fprintf(stderr,
+            "OPENSSL_INIT: ossl_init_config: openssl_config(%s)\n",
+            appname == NULL ? "NULL" : appname);
+#endif
+    openssl_config_int(appname);
+    config_inited = 1;
+    return 1;
+}
+DEFINE_RUN_ONCE_STATIC(ossl_init_no_config)
+{
+#ifdef OPENSSL_INIT_DEBUG
+    fprintf(stderr,
+            "OPENSSL_INIT: ossl_init_config: openssl_no_config_int()\n");
+#endif
+    openssl_no_config_int();
+    config_inited = 1;
+    return 1;
+}
+
+static CRYPTO_ONCE async = CRYPTO_ONCE_STATIC_INIT;
+static int async_inited = 0;
+DEFINE_RUN_ONCE_STATIC(ossl_init_async)
+{
+#ifdef OPENSSL_INIT_DEBUG
+    fprintf(stderr, "OPENSSL_INIT: ossl_init_async: async_init()\n");
+#endif
+    if (!async_init())
+        return 0;
+    async_inited = 1;
+    return 1;
+}
+
+#ifndef OPENSSL_NO_ENGINE
+static CRYPTO_ONCE engine_openssl = CRYPTO_ONCE_STATIC_INIT;
+DEFINE_RUN_ONCE_STATIC(ossl_init_engine_openssl)
+{
+# ifdef OPENSSL_INIT_DEBUG
+    fprintf(stderr, "OPENSSL_INIT: ossl_init_engine_openssl: "
+                    "engine_load_openssl_int()\n");
+# endif
+    engine_load_openssl_int();
+    return 1;
+}
+# if !defined(OPENSSL_NO_HW) && \
+    (defined(__OpenBSD__) || defined(__FreeBSD__) || defined(HAVE_CRYPTODEV))
+static CRYPTO_ONCE engine_cryptodev = CRYPTO_ONCE_STATIC_INIT;
+DEFINE_RUN_ONCE_STATIC(ossl_init_engine_cryptodev)
+{
+#  ifdef OPENSSL_INIT_DEBUG
+    fprintf(stderr, "OPENSSL_INIT: ossl_init_engine_cryptodev: "
+                    "engine_load_cryptodev_int()\n");
+#  endif
+    engine_load_cryptodev_int();
+    return 1;
+}
+# endif
+
+# ifndef OPENSSL_NO_RDRAND
+static CRYPTO_ONCE engine_rdrand = CRYPTO_ONCE_STATIC_INIT;
+DEFINE_RUN_ONCE_STATIC(ossl_init_engine_rdrand)
+{
+#  ifdef OPENSSL_INIT_DEBUG
+    fprintf(stderr, "OPENSSL_INIT: ossl_init_engine_rdrand: "
+                    "engine_load_rdrand_int()\n");
+#  endif
+    engine_load_rdrand_int();
+    return 1;
+}
+# endif
+static CRYPTO_ONCE engine_dynamic = CRYPTO_ONCE_STATIC_INIT;
+DEFINE_RUN_ONCE_STATIC(ossl_init_engine_dynamic)
+{
+# ifdef OPENSSL_INIT_DEBUG
+    fprintf(stderr, "OPENSSL_INIT: ossl_init_engine_dynamic: "
+                    "engine_load_dynamic_int()\n");
+# endif
+    engine_load_dynamic_int();
+    return 1;
+}
+# ifndef OPENSSL_NO_STATIC_ENGINE
+#  if !defined(OPENSSL_NO_HW) && !defined(OPENSSL_NO_HW_PADLOCK)
+static CRYPTO_ONCE engine_padlock = CRYPTO_ONCE_STATIC_INIT;
+DEFINE_RUN_ONCE_STATIC(ossl_init_engine_padlock)
+{
+#   ifdef OPENSSL_INIT_DEBUG
+    fprintf(stderr, "OPENSSL_INIT: ossl_init_engine_padlock: "
+                    "engine_load_padlock_int()\n");
+#   endif
+    engine_load_padlock_int();
+    return 1;
+}
+#  endif
+#  if defined(OPENSSL_SYS_WIN32) && !defined(OPENSSL_NO_CAPIENG)
+static CRYPTO_ONCE engine_capi = CRYPTO_ONCE_STATIC_INIT;
+DEFINE_RUN_ONCE_STATIC(ossl_init_engine_capi)
+{
+#   ifdef OPENSSL_INIT_DEBUG
+    fprintf(stderr, "OPENSSL_INIT: ossl_init_engine_capi: "
+                    "engine_load_capi_int()\n");
+#   endif
+    engine_load_capi_int();
+    return 1;
+}
+#  endif
+#  if !defined(OPENSSL_NO_SKF) && !defined(OPENSSL_NO_SKFENG)
+static CRYPTO_ONCE engine_skf = CRYPTO_ONCE_STATIC_INIT;
+DEFINE_RUN_ONCE_STATIC(ossl_init_engine_skf)
+{
+#   ifdef OPENSSL_INIT_DEBUG
+    fprintf(stderr, "OPENSSL_INIT: ossl_init_engine_skf: "
+                    "engine_load_skf_int()\n");
+#   endif
+    engine_load_skf_int();
+    return 1;
+}
+#  endif
+#  if !defined(OPENSSL_NO_SDF) && !defined(OPENSSL_NO_SDFENG)
+static CRYPTO_ONCE engine_sdf = CRYPTO_ONCE_STATIC_INIT;
+DEFINE_RUN_ONCE_STATIC(ossl_init_engine_sdf)
+{
+#   ifdef OPENSSL_INIT_DEBUG
+    fprintf(stderr, "OPENSSL_INIT: ossl_init_engine_sdf: "
+                    "engine_load_sdf_int()\n");
+#   endif
+    engine_load_sdf_int();
+    return 1;
+}
+#  endif
+#  if !defined(OPENSSL_NO_AFALGENG)
+static CRYPTO_ONCE engine_afalg = CRYPTO_ONCE_STATIC_INIT;
+DEFINE_RUN_ONCE_STATIC(ossl_init_engine_afalg)
+{
+#   ifdef OPENSSL_INIT_DEBUG
+    fprintf(stderr, "OPENSSL_INIT: ossl_init_engine_afalg: "
+                    "engine_load_afalg_int()\n");
+#   endif
+    engine_load_afalg_int();
+    return 1;
+}
+#  endif
+# endif
+#endif
+
+#ifndef OPENSSL_NO_COMP
+static CRYPTO_ONCE zlib = CRYPTO_ONCE_STATIC_INIT;
+
+static int zlib_inited = 0;
+DEFINE_RUN_ONCE_STATIC(ossl_init_zlib)
+{
+    /* Do nothing - we need to know about this for the later cleanup */
+    zlib_inited = 1;
+    return 1;
+}
+#endif
+
+static void ossl_init_thread_stop(struct thread_local_inits_st *locals)
+{
+    /* Can't do much about this */
+    if (locals == NULL)
+        return;
+
+    if (locals->async) {
+#ifdef OPENSSL_INIT_DEBUG
+        fprintf(stderr, "OPENSSL_INIT: ossl_init_thread_stop: "
+                        "ASYNC_cleanup_thread()\n");
+#endif
+        ASYNC_cleanup_thread();
+    }
+
+    if (locals->err_state) {
+#ifdef OPENSSL_INIT_DEBUG
+        fprintf(stderr, "OPENSSL_INIT: ossl_init_thread_stop: "
+                        "err_delete_thread_state()\n");
+#endif
+        err_delete_thread_state();
+    }
+
+    OPENSSL_free(locals);
+}
+
+void OPENSSL_thread_stop(void)
+{
+    ossl_init_thread_stop(
+        (struct thread_local_inits_st *)ossl_init_get_thread_local(0));
+}
+
+int ossl_init_thread_start(uint64_t opts)
+{
+    struct thread_local_inits_st *locals = ossl_init_get_thread_local(1);
+
+    if (locals == NULL)
+        return 0;
+
+    if (opts & OPENSSL_INIT_THREAD_ASYNC) {
+#ifdef OPENSSL_INIT_DEBUG
+        fprintf(stderr, "OPENSSL_INIT: ossl_init_thread_start: "
+                        "marking thread for async\n");
+#endif
+        locals->async = 1;
+    }
+
+    if (opts & OPENSSL_INIT_THREAD_ERR_STATE) {
+#ifdef OPENSSL_INIT_DEBUG
+        fprintf(stderr, "OPENSSL_INIT: ossl_init_thread_start: "
+                        "marking thread for err_state\n");
+#endif
+        locals->err_state = 1;
+    }
+
+    return 1;
+}
+
+void OPENSSL_cleanup(void)
+{
+    OPENSSL_INIT_STOP *currhandler, *lasthandler;
+
+    /* If we've not been inited then no need to deinit */
+    if (!base_inited)
+        return;
+
+    /* Might be explicitly called and also by atexit */
+    if (stopped)
+        return;
+    stopped = 1;
+
+    /*
+     * Thread stop may not get automatically called by the thread library for
+     * the very last thread in some situations, so call it directly.
+     */
+    ossl_init_thread_stop(ossl_init_get_thread_local(0));
+
+    currhandler = stop_handlers;
+    while (currhandler != NULL) {
+        currhandler->handler();
+        lasthandler = currhandler;
+        currhandler = currhandler->next;
+        OPENSSL_free(lasthandler);
+    }
+    stop_handlers = NULL;
+
+    CRYPTO_THREAD_lock_free(init_lock);
+
+    /*
+     * We assume we are single-threaded for this function, i.e. no race
+     * conditions for the various "*_inited" vars below.
+     */
+
+#ifndef OPENSSL_NO_COMP
+    if (zlib_inited) {
+#ifdef OPENSSL_INIT_DEBUG
+        fprintf(stderr, "OPENSSL_INIT: OPENSSL_cleanup: "
+                        "comp_zlib_cleanup_int()\n");
+#endif
+        comp_zlib_cleanup_int();
+    }
+#endif
+
+    if (async_inited) {
+# ifdef OPENSSL_INIT_DEBUG
+        fprintf(stderr, "OPENSSL_INIT: OPENSSL_cleanup: "
+                        "async_deinit()\n");
+# endif
+        async_deinit();
+    }
+
+    if (load_crypto_strings_inited) {
+#ifdef OPENSSL_INIT_DEBUG
+        fprintf(stderr, "OPENSSL_INIT: OPENSSL_cleanup: "
+                        "err_free_strings_int()\n");
+#endif
+        err_free_strings_int();
+    }
+
+    CRYPTO_THREAD_cleanup_local(&threadstopkey);
+
+#ifdef OPENSSL_INIT_DEBUG
+    fprintf(stderr, "OPENSSL_INIT: OPENSSL_cleanup: "
+                    "rand_cleanup_int()\n");
+    fprintf(stderr, "OPENSSL_INIT: OPENSSL_cleanup: "
+                    "conf_modules_free_int()\n");
+#ifndef OPENSSL_NO_ENGINE
+    fprintf(stderr, "OPENSSL_INIT: OPENSSL_cleanup: "
+                    "engine_cleanup_int()\n");
+#endif
+    fprintf(stderr, "OPENSSL_INIT: OPENSSL_cleanup: "
+                    "crypto_cleanup_all_ex_data_int()\n");
+    fprintf(stderr, "OPENSSL_INIT: OPENSSL_cleanup: "
+                    "bio_sock_cleanup_int()\n");
+    fprintf(stderr, "OPENSSL_INIT: OPENSSL_cleanup: "
+                    "bio_cleanup()\n");
+    fprintf(stderr, "OPENSSL_INIT: OPENSSL_cleanup: "
+                    "evp_cleanup_int()\n");
+    fprintf(stderr, "OPENSSL_INIT: OPENSSL_cleanup: "
+                    "obj_cleanup_int()\n");
+    fprintf(stderr, "OPENSSL_INIT: OPENSSL_cleanup: "
+                    "err_cleanup()\n");
+#endif
+    /*
+     * Note that cleanup order is important:
+     * - rand_cleanup_int could call an ENGINE's RAND cleanup function so
+     * must be called before engine_cleanup_int()
+     * - ENGINEs use CRYPTO_EX_DATA and therefore, must be cleaned up
+     * before the ex data handlers are wiped in CRYPTO_cleanup_all_ex_data().
+     * - conf_modules_free_int() can end up in ENGINE code so must be called
+     * before engine_cleanup_int()
+     * - ENGINEs and additional EVP algorithms might use added OIDs names so
+     * obj_cleanup_int() must be called last
+     */
+    rand_cleanup_int();
+    conf_modules_free_int();
+#ifndef OPENSSL_NO_ENGINE
+    engine_cleanup_int();
+#endif
+    crypto_cleanup_all_ex_data_int();
+    bio_cleanup();
+    evp_cleanup_int();
+    obj_cleanup_int();
+    err_cleanup();
+
+    base_inited = 0;
+}
+
+/*
+ * If this function is called with a non NULL settings value then it must be
+ * called prior to any threads making calls to any OpenSSL functions,
+ * i.e. passing a non-null settings value is assumed to be single-threaded.
+ */
+int OPENSSL_init_crypto(uint64_t opts, const OPENSSL_INIT_SETTINGS *settings)
+{
+    static int stoperrset = 0;
+
+    if (stopped) {
+        if (!stoperrset) {
+            /*
+             * We only ever set this once to avoid getting into an infinite
+             * loop where the error system keeps trying to init and fails so
+             * sets an error etc
+             */
+            stoperrset = 1;
+            CRYPTOerr(CRYPTO_F_OPENSSL_INIT_CRYPTO, ERR_R_INIT_FAIL);
+        }
+        return 0;
+    }
+
+    if (!base_inited && !RUN_ONCE(&base, ossl_init_base))
+        return 0;
+
+    if ((opts & OPENSSL_INIT_NO_LOAD_CRYPTO_STRINGS)
+            && !RUN_ONCE(&load_crypto_strings,
+                         ossl_init_no_load_crypto_strings))
+        return 0;
+
+    if ((opts & OPENSSL_INIT_LOAD_CRYPTO_STRINGS)
+            && !RUN_ONCE(&load_crypto_strings, ossl_init_load_crypto_strings))
+        return 0;
+
+    if ((opts & OPENSSL_INIT_NO_ADD_ALL_CIPHERS)
+            && !RUN_ONCE(&add_all_ciphers, ossl_init_no_add_algs))
+        return 0;
+
+    if ((opts & OPENSSL_INIT_ADD_ALL_CIPHERS)
+            && !RUN_ONCE(&add_all_ciphers, ossl_init_add_all_ciphers))
+        return 0;
+
+    if ((opts & OPENSSL_INIT_NO_ADD_ALL_DIGESTS)
+            && !RUN_ONCE(&add_all_digests, ossl_init_no_add_algs))
+        return 0;
+
+    if ((opts & OPENSSL_INIT_ADD_ALL_DIGESTS)
+            && !RUN_ONCE(&add_all_digests, ossl_init_add_all_digests))
+        return 0;
+
+    if ((opts & OPENSSL_INIT_NO_LOAD_CONFIG)
+            && !RUN_ONCE(&config, ossl_init_no_config))
+        return 0;
+
+    if (opts & OPENSSL_INIT_LOAD_CONFIG) {
+        int ret;
+        CRYPTO_THREAD_write_lock(init_lock);
+        appname = (settings == NULL) ? NULL : settings->appname;
+        ret = RUN_ONCE(&config, ossl_init_config);
+        CRYPTO_THREAD_unlock(init_lock);
+        if (!ret)
+            return 0;
+    }
+
+    if ((opts & OPENSSL_INIT_ASYNC)
+            && !RUN_ONCE(&async, ossl_init_async))
+        return 0;
+
+#ifndef OPENSSL_NO_ENGINE
+    if ((opts & OPENSSL_INIT_ENGINE_OPENSSL)
+            && !RUN_ONCE(&engine_openssl, ossl_init_engine_openssl))
+        return 0;
+# if !defined(OPENSSL_NO_HW) && \
+    (defined(__OpenBSD__) || defined(__FreeBSD__) || defined(HAVE_CRYPTODEV))
+    if ((opts & OPENSSL_INIT_ENGINE_CRYPTODEV)
+            && !RUN_ONCE(&engine_cryptodev, ossl_init_engine_cryptodev))
+        return 0;
+# endif
+# ifndef OPENSSL_NO_RDRAND
+    if ((opts & OPENSSL_INIT_ENGINE_RDRAND)
+            && !RUN_ONCE(&engine_rdrand, ossl_init_engine_rdrand))
+        return 0;
+# endif
+    if ((opts & OPENSSL_INIT_ENGINE_DYNAMIC)
+            && !RUN_ONCE(&engine_dynamic, ossl_init_engine_dynamic))
+        return 0;
+# ifndef OPENSSL_NO_STATIC_ENGINE
+#  if !defined(OPENSSL_NO_HW) && !defined(OPENSSL_NO_HW_PADLOCK)
+    if ((opts & OPENSSL_INIT_ENGINE_PADLOCK)
+            && !RUN_ONCE(&engine_padlock, ossl_init_engine_padlock))
+        return 0;
+#  endif
+#  if defined(OPENSSL_SYS_WIN32) && !defined(OPENSSL_NO_CAPIENG)
+    if ((opts & OPENSSL_INIT_ENGINE_CAPI)
+            && !RUN_ONCE(&engine_capi, ossl_init_engine_capi))
+        return 0;
+#  endif
+#  if !defined(OPENSSL_NO_SKF) && !defined(OPENSSL_NO_SKFENG)
+    if ((opts & OPENSSL_INIT_ENGINE_SKF)
+            && !RUN_ONCE(&engine_skf, ossl_init_engine_skf))
+        return 0;
+#  endif
+#  if !defined(OPENSSL_NO_SDF) && !defined(OPENSSL_NO_SDFENG)
+    if ((opts & OPENSSL_INIT_ENGINE_SDF)
+            && !RUN_ONCE(&engine_sdf, ossl_init_engine_sdf))
+        return 0;
+#  endif
+#  if !defined(OPENSSL_NO_AFALGENG)
+    if ((opts & OPENSSL_INIT_ENGINE_AFALG)
+            && !RUN_ONCE(&engine_afalg, ossl_init_engine_afalg))
+        return 0;
+#  endif
+# endif
+    if (opts & (OPENSSL_INIT_ENGINE_ALL_BUILTIN
+                | OPENSSL_INIT_ENGINE_OPENSSL
+                | OPENSSL_INIT_ENGINE_AFALG)) {
+        ENGINE_register_all_complete();
+    }
+#endif
+
+#ifndef OPENSSL_NO_COMP
+    if ((opts & OPENSSL_INIT_ZLIB)
+            && !RUN_ONCE(&zlib, ossl_init_zlib))
+        return 0;
+#endif
+
+    return 1;
+}
+
+int OPENSSL_atexit(void (*handler)(void))
+{
+    OPENSSL_INIT_STOP *newhand;
+
+#if !defined(OPENSSL_NO_DSO) && !defined(OPENSSL_USE_NODELETE)
+    {
+        union {
+            void *sym;
+            void (*func)(void);
+        } handlersym;
+
+        handlersym.func = handler;
+# ifdef DSO_WIN32
+        {
+            HMODULE handle = NULL;
+            BOOL ret;
+
+            /*
+             * We don't use the DSO route for WIN32 because there is a better
+             * way
+             */
+            ret = GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS
+                                    | GET_MODULE_HANDLE_EX_FLAG_PIN,
+                                    handlersym.sym, &handle);
+
+            if (!ret)
+                return 0;
+        }
+# else
+        /*
+         * Deliberately leak a reference to the handler. This will force the
+         * library/code containing the handler to remain loaded until we run the
+         * atexit handler. If -znodelete has been used then this is
+         * unneccessary.
+         */
+        {
+            DSO *dso = NULL;
+
+            dso = DSO_dsobyaddr(handlersym.sym, DSO_FLAG_NO_UNLOAD_ON_FREE);
+            DSO_free(dso);
+        }
+# endif
+    }
+#endif
+
+    newhand = OPENSSL_malloc(sizeof(*newhand));
+    if (newhand == NULL)
+        return 0;
+
+    newhand->handler = handler;
+    newhand->next = stop_handlers;
+    stop_handlers = newhand;
+
+    return 1;
+}
diff --git a/crypto/kdf/build.info b/crypto/kdf/build.info
new file mode 100644
index 000000000..cbe2080ed
--- /dev/null
+++ b/crypto/kdf/build.info
@@ -0,0 +1,3 @@
+LIBS=../../libcrypto
+SOURCE[../../libcrypto]=\
+        tls1_prf.c kdf_err.c hkdf.c
diff --git a/crypto/kdf/hkdf.c b/crypto/kdf/hkdf.c
new file mode 100644
index 000000000..f851da7be
--- /dev/null
+++ b/crypto/kdf/hkdf.c
@@ -0,0 +1,293 @@
+/*
+ * Copyright 2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include "internal/cryptlib.h"
+#include "internal/evp_int.h"
+
+#define HKDF_MAXBUF 1024
+
+/*static*/ unsigned char *HKDF(const EVP_MD *evp_md,
+                           const unsigned char *salt, size_t salt_len,
+                           const unsigned char *key, size_t key_len,
+                           const unsigned char *info, size_t info_len,
+                           unsigned char *okm, size_t okm_len);
+
+/*static*/ unsigned char *HKDF_Extract(const EVP_MD *evp_md,
+                                   const unsigned char *salt, size_t salt_len,
+                                   const unsigned char *key, size_t key_len,
+                                   unsigned char *prk, size_t *prk_len);
+
+/*static*/ unsigned char *HKDF_Expand(const EVP_MD *evp_md,
+                                  const unsigned char *prk, size_t prk_len,
+                                  const unsigned char *info, size_t info_len,
+                                  unsigned char *okm, size_t okm_len);
+
+typedef struct {
+    const EVP_MD *md;
+    unsigned char *salt;
+    size_t salt_len;
+    unsigned char *key;
+    size_t key_len;
+    unsigned char info[HKDF_MAXBUF];
+    size_t info_len;
+} HKDF_PKEY_CTX;
+
+static int pkey_hkdf_init(EVP_PKEY_CTX *ctx)
+{
+    HKDF_PKEY_CTX *kctx;
+
+    kctx = OPENSSL_zalloc(sizeof(*kctx));
+    if (kctx == NULL)
+        return 0;
+
+    ctx->data = kctx;
+
+    return 1;
+}
+
+static void pkey_hkdf_cleanup(EVP_PKEY_CTX *ctx)
+{
+    HKDF_PKEY_CTX *kctx = ctx->data;
+    OPENSSL_clear_free(kctx->salt, kctx->salt_len);
+    OPENSSL_clear_free(kctx->key, kctx->key_len);
+    OPENSSL_cleanse(kctx->info, kctx->info_len);
+    OPENSSL_free(kctx);
+}
+
+static int pkey_hkdf_ctrl(EVP_PKEY_CTX *ctx, int type, int p1, void *p2)
+{
+    HKDF_PKEY_CTX *kctx = ctx->data;
+
+    switch (type) {
+    case EVP_PKEY_CTRL_HKDF_MD:
+        if (p2 == NULL)
+            return 0;
+
+        kctx->md = p2;
+        return 1;
+
+    case EVP_PKEY_CTRL_HKDF_SALT:
+        if (p1 == 0 || p2 == NULL)
+            return 1;
+
+        if (p1 < 0)
+            return 0;
+
+        if (kctx->salt != NULL)
+            OPENSSL_clear_free(kctx->salt, kctx->salt_len);
+
+        kctx->salt = OPENSSL_memdup(p2, p1);
+        if (kctx->salt == NULL)
+            return 0;
+
+        kctx->salt_len = p1;
+        return 1;
+
+    case EVP_PKEY_CTRL_HKDF_KEY:
+        if (p1 < 0)
+            return 0;
+
+        if (kctx->key != NULL)
+            OPENSSL_clear_free(kctx->key, kctx->key_len);
+
+        kctx->key = OPENSSL_memdup(p2, p1);
+        if (kctx->key == NULL)
+            return 0;
+
+        kctx->key_len  = p1;
+        return 1;
+
+    case EVP_PKEY_CTRL_HKDF_INFO:
+        if (p1 == 0 || p2 == NULL)
+            return 1;
+
+        if (p1 < 0 || p1 > (int)(HKDF_MAXBUF - kctx->info_len))
+            return 0;
+
+        memcpy(kctx->info + kctx->info_len, p2, p1);
+        kctx->info_len += p1;
+        return 1;
+
+    default:
+        return -2;
+
+    }
+}
+
+static int pkey_hkdf_ctrl_str(EVP_PKEY_CTX *ctx, const char *type,
+                              const char *value)
+{
+    if (strcmp(type, "md") == 0)
+        return EVP_PKEY_CTX_set_hkdf_md(ctx, EVP_get_digestbyname(value));
+
+    if (strcmp(type, "salt") == 0)
+        return EVP_PKEY_CTX_str2ctrl(ctx, EVP_PKEY_CTRL_HKDF_SALT, value);
+
+    if (strcmp(type, "hexsalt") == 0)
+        return EVP_PKEY_CTX_hex2ctrl(ctx, EVP_PKEY_CTRL_HKDF_SALT, value);
+
+    if (strcmp(type, "key") == 0)
+        return EVP_PKEY_CTX_str2ctrl(ctx, EVP_PKEY_CTRL_HKDF_KEY, value);
+
+    if (strcmp(type, "hexkey") == 0)
+        return EVP_PKEY_CTX_hex2ctrl(ctx, EVP_PKEY_CTRL_HKDF_KEY, value);
+
+    if (strcmp(type, "info") == 0)
+        return EVP_PKEY_CTX_str2ctrl(ctx, EVP_PKEY_CTRL_HKDF_INFO, value);
+
+    if (strcmp(type, "hexinfo") == 0)
+        return EVP_PKEY_CTX_hex2ctrl(ctx, EVP_PKEY_CTRL_HKDF_INFO, value);
+
+    return -2;
+}
+
+static int pkey_hkdf_derive(EVP_PKEY_CTX *ctx, unsigned char *key,
+                            size_t *keylen)
+{
+    HKDF_PKEY_CTX *kctx = ctx->data;
+
+    if (kctx->md == NULL || kctx->key == NULL)
+        return 0;
+
+    if (HKDF(kctx->md, kctx->salt, kctx->salt_len, kctx->key, kctx->key_len,
+             kctx->info, kctx->info_len, key, *keylen) == NULL)
+    {
+        return 0;
+    }
+
+    return 1;
+}
+
+const EVP_PKEY_METHOD hkdf_pkey_meth = {
+    EVP_PKEY_HKDF,
+    0,
+    pkey_hkdf_init,
+    0,
+    pkey_hkdf_cleanup,
+
+    0, 0,
+    0, 0,
+
+    0,
+    0,
+
+    0,
+    0,
+
+    0, 0,
+
+    0, 0, 0, 0,
+
+    0, 0,
+
+    0, 0,
+
+    0,
+    pkey_hkdf_derive,
+    pkey_hkdf_ctrl,
+    pkey_hkdf_ctrl_str
+};
+
+/*static*/ unsigned char *HKDF(const EVP_MD *evp_md,
+                           const unsigned char *salt, size_t salt_len,
+                           const unsigned char *key, size_t key_len,
+                           const unsigned char *info, size_t info_len,
+                           unsigned char *okm, size_t okm_len)
+{
+    unsigned char prk[EVP_MAX_MD_SIZE];
+    size_t prk_len;
+
+    if (!HKDF_Extract(evp_md, salt, salt_len, key, key_len, prk, &prk_len))
+        return NULL;
+
+    return HKDF_Expand(evp_md, prk, prk_len, info, info_len, okm, okm_len);
+}
+
+/*static*/ unsigned char *HKDF_Extract(const EVP_MD *evp_md,
+                                   const unsigned char *salt, size_t salt_len,
+                                   const unsigned char *key, size_t key_len,
+                                   unsigned char *prk, size_t *prk_len)
+{
+    unsigned int tmp_len;
+
+    if (!HMAC(evp_md, salt, salt_len, key, key_len, prk, &tmp_len))
+        return NULL;
+
+    *prk_len = tmp_len;
+    return prk;
+}
+
+/*static*/ unsigned char *HKDF_Expand(const EVP_MD *evp_md,
+                                  const unsigned char *prk, size_t prk_len,
+                                  const unsigned char *info, size_t info_len,
+                                  unsigned char *okm, size_t okm_len)
+{
+    HMAC_CTX *hmac;
+
+    unsigned int i;
+
+    unsigned char prev[EVP_MAX_MD_SIZE];
+
+    size_t done_len = 0, dig_len = EVP_MD_size(evp_md);
+
+    size_t n = okm_len / dig_len;
+    if (okm_len % dig_len)
+        n++;
+
+    if (n > 255)
+        return NULL;
+
+    if ((hmac = HMAC_CTX_new()) == NULL)
+        return NULL;
+
+    if (!HMAC_Init_ex(hmac, prk, prk_len, evp_md, NULL))
+        goto err;
+
+    for (i = 1; i <= n; i++) {
+        size_t copy_len;
+        const unsigned char ctr = i;
+
+        if (i > 1) {
+            if (!HMAC_Init_ex(hmac, NULL, 0, NULL, NULL))
+                goto err;
+
+            if (!HMAC_Update(hmac, prev, dig_len))
+                goto err;
+        }
+
+        if (!HMAC_Update(hmac, info, info_len))
+            goto err;
+
+        if (!HMAC_Update(hmac, &ctr, 1))
+            goto err;
+
+        if (!HMAC_Final(hmac, prev, NULL))
+            goto err;
+
+        copy_len = (done_len + dig_len > okm_len) ?
+                       okm_len - done_len :
+                       dig_len;
+
+        memcpy(okm + done_len, prev, copy_len);
+
+        done_len += copy_len;
+    }
+
+    HMAC_CTX_free(hmac);
+    return okm;
+
+ err:
+    HMAC_CTX_free(hmac);
+    return NULL;
+}
diff --git a/crypto/kdf/kdf_err.c b/crypto/kdf/kdf_err.c
new file mode 100644
index 000000000..d7d71b35e
--- /dev/null
+++ b/crypto/kdf/kdf_err.c
@@ -0,0 +1,46 @@
+/*
+ * Generated by util/mkerr.pl DO NOT EDIT
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include 
+
+/* BEGIN ERROR CODES */
+#ifndef OPENSSL_NO_ERR
+
+# define ERR_FUNC(func) ERR_PACK(ERR_LIB_KDF,func,0)
+# define ERR_REASON(reason) ERR_PACK(ERR_LIB_KDF,0,reason)
+
+static ERR_STRING_DATA KDF_str_functs[] = {
+    {ERR_FUNC(KDF_F_PKEY_TLS1_PRF_CTRL_STR), "pkey_tls1_prf_ctrl_str"},
+    {ERR_FUNC(KDF_F_PKEY_TLS1_PRF_DERIVE), "pkey_tls1_prf_derive"},
+    {0, NULL}
+};
+
+static ERR_STRING_DATA KDF_str_reasons[] = {
+    {ERR_REASON(KDF_R_INVALID_DIGEST), "invalid digest"},
+    {ERR_REASON(KDF_R_MISSING_PARAMETER), "missing parameter"},
+    {ERR_REASON(KDF_R_VALUE_MISSING), "value missing"},
+    {0, NULL}
+};
+
+#endif
+
+int ERR_load_KDF_strings(void)
+{
+#ifndef OPENSSL_NO_ERR
+
+    if (ERR_func_error_string(KDF_str_functs[0].error) == NULL) {
+        ERR_load_strings(0, KDF_str_functs);
+        ERR_load_strings(0, KDF_str_reasons);
+    }
+#endif
+    return 1;
+}
diff --git a/crypto/kdf/tls1_prf.c b/crypto/kdf/tls1_prf.c
new file mode 100644
index 000000000..1266a66da
--- /dev/null
+++ b/crypto/kdf/tls1_prf.c
@@ -0,0 +1,266 @@
+/*
+ * Copyright 2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+#include "internal/evp_int.h"
+
+static int tls1_prf_alg(const EVP_MD *md,
+                        const unsigned char *sec, size_t slen,
+                        const unsigned char *seed, size_t seed_len,
+                        unsigned char *out, size_t olen);
+
+#define TLS1_PRF_MAXBUF 1024
+
+/* TLS KDF pkey context structure */
+
+typedef struct {
+    /* Digest to use for PRF */
+    const EVP_MD *md;
+    /* Secret value to use for PRF */
+    unsigned char *sec;
+    size_t seclen;
+    /* Buffer of concatenated seed data */
+    unsigned char seed[TLS1_PRF_MAXBUF];
+    size_t seedlen;
+} TLS1_PRF_PKEY_CTX;
+
+static int pkey_tls1_prf_init(EVP_PKEY_CTX *ctx)
+{
+    TLS1_PRF_PKEY_CTX *kctx;
+
+    kctx = OPENSSL_zalloc(sizeof(*kctx));
+    if (kctx == NULL)
+        return 0;
+    ctx->data = kctx;
+
+    return 1;
+}
+
+static void pkey_tls1_prf_cleanup(EVP_PKEY_CTX *ctx)
+{
+    TLS1_PRF_PKEY_CTX *kctx = ctx->data;
+    OPENSSL_clear_free(kctx->sec, kctx->seclen);
+    OPENSSL_cleanse(kctx->seed, kctx->seedlen);
+    OPENSSL_free(kctx);
+}
+
+static int pkey_tls1_prf_ctrl(EVP_PKEY_CTX *ctx, int type, int p1, void *p2)
+{
+    TLS1_PRF_PKEY_CTX *kctx = ctx->data;
+    switch (type) {
+    case EVP_PKEY_CTRL_TLS_MD:
+        kctx->md = p2;
+        return 1;
+
+    case EVP_PKEY_CTRL_TLS_SECRET:
+        if (p1 < 0)
+            return 0;
+        if (kctx->sec != NULL)
+            OPENSSL_clear_free(kctx->sec, kctx->seclen);
+        OPENSSL_cleanse(kctx->seed, kctx->seedlen);
+        kctx->seedlen = 0;
+        kctx->sec = OPENSSL_memdup(p2, p1);
+        if (kctx->sec == NULL)
+            return 0;
+        kctx->seclen  = p1;
+        return 1;
+
+    case EVP_PKEY_CTRL_TLS_SEED:
+        if (p1 == 0 || p2 == NULL)
+            return 1;
+        if (p1 < 0 || p1 > (int)(TLS1_PRF_MAXBUF - kctx->seedlen))
+            return 0;
+        memcpy(kctx->seed + kctx->seedlen, p2, p1);
+        kctx->seedlen += p1;
+        return 1;
+
+    default:
+        return -2;
+
+    }
+}
+
+static int pkey_tls1_prf_ctrl_str(EVP_PKEY_CTX *ctx,
+                                  const char *type, const char *value)
+{
+    if (value == NULL) {
+        KDFerr(KDF_F_PKEY_TLS1_PRF_CTRL_STR, KDF_R_VALUE_MISSING);
+        return 0;
+    }
+    if (strcmp(type, "md") == 0) {
+        TLS1_PRF_PKEY_CTX *kctx = ctx->data;
+
+        const EVP_MD *md = EVP_get_digestbyname(value);
+        if (md == NULL) {
+            KDFerr(KDF_F_PKEY_TLS1_PRF_CTRL_STR, KDF_R_INVALID_DIGEST);
+            return 0;
+        }
+        kctx->md = md;
+        return 1;
+    }
+    if (strcmp(type, "secret") == 0)
+        return EVP_PKEY_CTX_str2ctrl(ctx, EVP_PKEY_CTRL_TLS_SECRET, value);
+    if (strcmp(type, "hexsecret") == 0)
+        return EVP_PKEY_CTX_hex2ctrl(ctx, EVP_PKEY_CTRL_TLS_SECRET, value);
+    if (strcmp(type, "seed") == 0)
+        return EVP_PKEY_CTX_str2ctrl(ctx, EVP_PKEY_CTRL_TLS_SEED, value);
+    if (strcmp(type, "hexseed") == 0)
+        return EVP_PKEY_CTX_hex2ctrl(ctx, EVP_PKEY_CTRL_TLS_SEED, value);
+    return -2;
+}
+
+static int pkey_tls1_prf_derive(EVP_PKEY_CTX *ctx, unsigned char *key,
+                                size_t *keylen)
+{
+    TLS1_PRF_PKEY_CTX *kctx = ctx->data;
+    if (kctx->md == NULL || kctx->sec == NULL || kctx->seedlen == 0) {
+        KDFerr(KDF_F_PKEY_TLS1_PRF_DERIVE, KDF_R_MISSING_PARAMETER);
+        return 0;
+    }
+    return tls1_prf_alg(kctx->md, kctx->sec, kctx->seclen,
+                        kctx->seed, kctx->seedlen,
+                        key, *keylen);
+}
+
+const EVP_PKEY_METHOD tls1_prf_pkey_meth = {
+    EVP_PKEY_TLS1_PRF,
+    0,
+    pkey_tls1_prf_init,
+    0,
+    pkey_tls1_prf_cleanup,
+
+    0, 0,
+    0, 0,
+
+    0,
+    0,
+
+    0,
+    0,
+
+    0, 0,
+
+    0, 0, 0, 0,
+
+    0, 0,
+
+    0, 0,
+
+    0,
+    pkey_tls1_prf_derive,
+    pkey_tls1_prf_ctrl,
+    pkey_tls1_prf_ctrl_str
+};
+
+static int tls1_prf_P_hash(const EVP_MD *md,
+                           const unsigned char *sec, size_t sec_len,
+                           const unsigned char *seed, size_t seed_len,
+                           unsigned char *out, size_t olen)
+{
+    int chunk;
+    EVP_MD_CTX *ctx = NULL, *ctx_tmp = NULL, *ctx_init = NULL;
+    EVP_PKEY *mac_key = NULL;
+    unsigned char A1[EVP_MAX_MD_SIZE];
+    size_t A1_len;
+    int ret = 0;
+
+    chunk = EVP_MD_size(md);
+    OPENSSL_assert(chunk >= 0);
+
+    ctx = EVP_MD_CTX_new();
+    ctx_tmp = EVP_MD_CTX_new();
+    ctx_init = EVP_MD_CTX_new();
+    if (ctx == NULL || ctx_tmp == NULL || ctx_init == NULL)
+        goto err;
+    EVP_MD_CTX_set_flags(ctx_init, EVP_MD_CTX_FLAG_NON_FIPS_ALLOW);
+    mac_key = EVP_PKEY_new_mac_key(EVP_PKEY_HMAC, NULL, sec, sec_len);
+    if (mac_key == NULL)
+        goto err;
+    if (!EVP_DigestSignInit(ctx_init, NULL, md, NULL, mac_key))
+        goto err;
+    if (!EVP_MD_CTX_copy_ex(ctx, ctx_init))
+        goto err;
+    if (seed != NULL && !EVP_DigestSignUpdate(ctx, seed, seed_len))
+        goto err;
+    if (!EVP_DigestSignFinal(ctx, A1, &A1_len))
+        goto err;
+
+    for (;;) {
+        /* Reinit mac contexts */
+        if (!EVP_MD_CTX_copy_ex(ctx, ctx_init))
+            goto err;
+        if (!EVP_DigestSignUpdate(ctx, A1, A1_len))
+            goto err;
+        if (olen > (size_t)chunk && !EVP_MD_CTX_copy_ex(ctx_tmp, ctx))
+            goto err;
+        if (seed && !EVP_DigestSignUpdate(ctx, seed, seed_len))
+            goto err;
+
+        if (olen > (size_t)chunk) {
+            size_t mac_len;
+            if (!EVP_DigestSignFinal(ctx, out, &mac_len))
+                goto err;
+            out += mac_len;
+            olen -= mac_len;
+            /* calc the next A1 value */
+            if (!EVP_DigestSignFinal(ctx_tmp, A1, &A1_len))
+                goto err;
+        } else {                /* last one */
+
+            if (!EVP_DigestSignFinal(ctx, A1, &A1_len))
+                goto err;
+            memcpy(out, A1, olen);
+            break;
+        }
+    }
+    ret = 1;
+ err:
+    EVP_PKEY_free(mac_key);
+    EVP_MD_CTX_free(ctx);
+    EVP_MD_CTX_free(ctx_tmp);
+    EVP_MD_CTX_free(ctx_init);
+    OPENSSL_cleanse(A1, sizeof(A1));
+    return ret;
+}
+
+static int tls1_prf_alg(const EVP_MD *md,
+                        const unsigned char *sec, size_t slen,
+                        const unsigned char *seed, size_t seed_len,
+                        unsigned char *out, size_t olen)
+{
+#if !defined(OPENSSL_NO_MD5) && !defined(OPENSSL_NO_SHA)
+    if (EVP_MD_type(md) == NID_md5_sha1) {
+        size_t i;
+        unsigned char *tmp;
+        if (!tls1_prf_P_hash(EVP_md5(), sec, slen/2 + (slen & 1),
+                         seed, seed_len, out, olen))
+            return 0;
+
+        tmp = OPENSSL_malloc(olen);
+        if (tmp == NULL)
+            return 0;
+        if (!tls1_prf_P_hash(EVP_sha1(), sec + slen/2, slen/2 + (slen & 1),
+                         seed, seed_len, tmp, olen)) {
+            OPENSSL_clear_free(tmp, olen);
+            return 0;
+        }
+        for (i = 0; i < olen; i++)
+            out[i] ^= tmp[i];
+        OPENSSL_clear_free(tmp, olen);
+        return 1;
+    }
+#endif
+    if (!tls1_prf_P_hash(md, sec, slen, seed, seed_len, out, olen))
+        return 0;
+
+    return 1;
+}
diff --git a/crypto/kdf2/build.info b/crypto/kdf2/build.info
new file mode 100644
index 000000000..5799c2576
--- /dev/null
+++ b/crypto/kdf2/build.info
@@ -0,0 +1,2 @@
+LIBS=../../libcrypto
+SOURCE[../../libcrypto]=kdf2_err.c kdf_x9_63.c
diff --git a/crypto/kdf2/kdf2_err.c b/crypto/kdf2/kdf2_err.c
new file mode 100644
index 000000000..2a3684768
--- /dev/null
+++ b/crypto/kdf2/kdf2_err.c
@@ -0,0 +1,44 @@
+/*
+ * Generated by util/mkerr.pl DO NOT EDIT
+ * Copyright 1995-2017 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include 
+
+/* BEGIN ERROR CODES */
+#ifndef OPENSSL_NO_ERR
+
+# define ERR_FUNC(func) ERR_PACK(ERR_LIB_KDF2,func,0)
+# define ERR_REASON(reason) ERR_PACK(ERR_LIB_KDF2,0,reason)
+
+static ERR_STRING_DATA KDF2_str_functs[] = {
+    {ERR_FUNC(KDF2_F_IBCS_KDF), "ibcs_kdf"},
+    {ERR_FUNC(KDF2_F_X963_KDF), "x963_kdf"},
+    {0, NULL}
+};
+
+static ERR_STRING_DATA KDF2_str_reasons[] = {
+    {ERR_REASON(KDF2_R_DIGEST_FAILURE), "digest failure"},
+    {0, NULL}
+};
+
+#endif
+
+int ERR_load_KDF2_strings(void)
+{
+#ifndef OPENSSL_NO_ERR
+
+    if (ERR_func_error_string(KDF2_str_functs[0].error) == NULL) {
+        ERR_load_strings(0, KDF2_str_functs);
+        ERR_load_strings(0, KDF2_str_reasons);
+    }
+#endif
+    return 1;
+}
diff --git a/crypto/kdf2/kdf_x9_63.c b/crypto/kdf2/kdf_x9_63.c
new file mode 100644
index 000000000..2d92ffb6c
--- /dev/null
+++ b/crypto/kdf2/kdf_x9_63.c
@@ -0,0 +1,190 @@
+/* ====================================================================
+ * Copyright (c) 2007 - 2017 The GmSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the GmSSL Project.
+ *    (http://gmssl.org/)"
+ *
+ * 4. The name "GmSSL Project" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    guanzhi1980@gmail.com.
+ *
+ * 5. Products derived from this software may not be called "GmSSL"
+ *    nor may "GmSSL" appear in their names without prior written
+ *    permission of the GmSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the GmSSL Project
+ *    (http://gmssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE GmSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE GmSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include "internal/byteorder.h"
+
+
+static void *x963_kdf(const EVP_MD *md, const void *in, size_t inlen,
+	void *out, size_t *outlen)
+{
+	void *ret = NULL;
+	EVP_MD_CTX *ctx = NULL;
+	uint32_t counter = 1;
+	uint32_t counter_be;
+	unsigned char dgst[EVP_MAX_MD_SIZE];
+	unsigned int dgstlen;
+	unsigned char *pout = out;
+	size_t rlen = *outlen;
+	size_t len;
+
+	if (!(ctx = EVP_MD_CTX_new())) {
+		KDF2err(KDF2_F_X963_KDF, ERR_R_MALLOC_FAILURE);
+		goto end;
+	}
+
+	while (rlen > 0) {
+		counter_be = cpu_to_be32(counter);
+		counter++;
+
+		if (!EVP_DigestInit(ctx, md)) {
+			KDF2err(KDF2_F_X963_KDF, KDF2_R_DIGEST_FAILURE);
+			goto end;
+		}
+		if (!EVP_DigestUpdate(ctx, in, inlen)) {
+			KDF2err(KDF2_F_X963_KDF, KDF2_R_DIGEST_FAILURE);
+			goto end;
+		}
+		if (!EVP_DigestUpdate(ctx, &counter_be, sizeof(counter_be))) {
+			KDF2err(KDF2_F_X963_KDF, KDF2_R_DIGEST_FAILURE);
+			goto end;
+		}
+		if (!EVP_DigestFinal(ctx, dgst, &dgstlen)) {
+			KDF2err(KDF2_F_X963_KDF, KDF2_R_DIGEST_FAILURE);
+			goto end;
+		}
+
+		len = dgstlen <= rlen ? dgstlen : rlen;
+		memcpy(pout, dgst, len);
+		rlen -= len;
+		pout += len;
+	}
+
+	ret = out;
+end:
+	EVP_MD_CTX_free(ctx);
+	return ret;
+}
+
+#define IMPLEMENT_X963_KDF(md) \
+static void *x963_##md##kdf(const void *in, size_t inlen, void *out, size_t *outlen) { \
+	return x963_kdf(EVP_##md(), in, inlen, out, outlen); \
+}
+
+IMPLEMENT_X963_KDF(sm3)
+#ifndef OPENSSL_NO_MD5
+IMPLEMENT_X963_KDF(md5)
+#endif
+#ifndef OPENSSL_NO_BLAKE2
+IMPLEMENT_X963_KDF(blake2b512)
+IMPLEMENT_X963_KDF(blake2s256)
+#endif
+#ifndef OPENSSL_NO_SHA
+IMPLEMENT_X963_KDF(sha1)
+# ifndef OPENSSL_NO_SHA256
+IMPLEMENT_X963_KDF(sha224)
+IMPLEMENT_X963_KDF(sha256)
+# endif
+# ifndef OPENSSL_NO_SHA512
+IMPLEMENT_X963_KDF(sha384)
+IMPLEMENT_X963_KDF(sha512)
+# endif
+#endif
+#ifndef OPENSSL_NO_MDC2
+IMPLEMENT_X963_KDF(mdc2)
+#endif
+#ifndef OPENSSL_NO_RMD160
+IMPLEMENT_X963_KDF(ripemd160)
+#endif
+#ifndef OPENSSL_NO_WHIRLPOOL
+IMPLEMENT_X963_KDF(whirlpool)
+#endif
+
+KDF_FUNC KDF_get_x9_63(const EVP_MD *md)
+{
+	switch (EVP_MD_type(md)) {
+	case NID_sm3:
+		return x963_sm3kdf;
+#ifndef OPENSSL_NO_MD5
+	case NID_md5:
+		return x963_md5kdf;
+#endif
+#ifndef OPENSSL_NO_BLAKE2
+	case NID_blake2b512:
+		return x963_blake2b512kdf;
+	case NID_blake2s256:
+		return x963_blake2s256kdf;
+#endif
+#ifndef OPENSSL_NO_SHA
+	case NID_sha1:
+		return x963_sha1kdf;
+# ifndef OPENSSL_NO_SHA256
+	case NID_sha224:
+		return x963_sha224kdf;
+	case NID_sha256:
+		return x963_sha256kdf;
+# endif
+# ifndef OPENSSL_NO_SHA512
+	case NID_sha384:
+		return x963_sha384kdf;
+	case NID_sha512:
+		return x963_sha512kdf;
+# endif
+#endif
+#ifndef OPENSSL_NO_MDC2
+	case NID_mdc2:
+		return x963_mdc2kdf;
+#endif
+#ifndef OPENSSL_NO_RMD160
+	case NID_ripemd160:
+		return x963_ripemd160kdf;
+#endif
+#ifndef OPENSSL_NO_WHIRLPOOL
+	case NID_whirlpool:
+		return x963_whirlpoolkdf;
+#endif
+	}
+
+	return NULL;
+}
diff --git a/crypto/lhash/build.info b/crypto/lhash/build.info
new file mode 100644
index 000000000..30797f2ca
--- /dev/null
+++ b/crypto/lhash/build.info
@@ -0,0 +1,3 @@
+LIBS=../../libcrypto
+SOURCE[../../libcrypto]=\
+        lhash.c lh_stats.c
diff --git a/crypto/lhash/lh_stats.c b/crypto/lhash/lh_stats.c
new file mode 100644
index 000000000..733783242
--- /dev/null
+++ b/crypto/lhash/lh_stats.c
@@ -0,0 +1,118 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include 
+/*
+ * If you wish to build this outside of OpenSSL, remove the following lines
+ * and things should work as expected
+ */
+#include "internal/cryptlib.h"
+
+#include 
+#include 
+#include "lhash_lcl.h"
+
+# ifndef OPENSSL_NO_STDIO
+void OPENSSL_LH_stats(const OPENSSL_LHASH *lh, FILE *fp)
+{
+    BIO *bp;
+
+    bp = BIO_new(BIO_s_file());
+    if (bp == NULL)
+        return;
+    BIO_set_fp(bp, fp, BIO_NOCLOSE);
+    OPENSSL_LH_stats_bio(lh, bp);
+    BIO_free(bp);
+}
+
+void OPENSSL_LH_node_stats(const OPENSSL_LHASH *lh, FILE *fp)
+{
+    BIO *bp;
+
+    bp = BIO_new(BIO_s_file());
+    if (bp == NULL)
+        return;
+    BIO_set_fp(bp, fp, BIO_NOCLOSE);
+    OPENSSL_LH_node_stats_bio(lh, bp);
+    BIO_free(bp);
+}
+
+void OPENSSL_LH_node_usage_stats(const OPENSSL_LHASH *lh, FILE *fp)
+{
+    BIO *bp;
+
+    bp = BIO_new(BIO_s_file());
+    if (bp == NULL)
+        return;
+    BIO_set_fp(bp, fp, BIO_NOCLOSE);
+    OPENSSL_LH_node_usage_stats_bio(lh, bp);
+    BIO_free(bp);
+}
+
+# endif
+
+void OPENSSL_LH_stats_bio(const OPENSSL_LHASH *lh, BIO *out)
+{
+    BIO_printf(out, "num_items             = %lu\n", lh->num_items);
+    BIO_printf(out, "num_nodes             = %u\n", lh->num_nodes);
+    BIO_printf(out, "num_alloc_nodes       = %u\n", lh->num_alloc_nodes);
+    BIO_printf(out, "num_expands           = %lu\n", lh->num_expands);
+    BIO_printf(out, "num_expand_reallocs   = %lu\n", lh->num_expand_reallocs);
+    BIO_printf(out, "num_contracts         = %lu\n", lh->num_contracts);
+    BIO_printf(out, "num_contract_reallocs = %lu\n",
+               lh->num_contract_reallocs);
+    BIO_printf(out, "num_hash_calls        = %lu\n", lh->num_hash_calls);
+    BIO_printf(out, "num_comp_calls        = %lu\n", lh->num_comp_calls);
+    BIO_printf(out, "num_insert            = %lu\n", lh->num_insert);
+    BIO_printf(out, "num_replace           = %lu\n", lh->num_replace);
+    BIO_printf(out, "num_delete            = %lu\n", lh->num_delete);
+    BIO_printf(out, "num_no_delete         = %lu\n", lh->num_no_delete);
+    BIO_printf(out, "num_retrieve          = %lu\n", lh->num_retrieve);
+    BIO_printf(out, "num_retrieve_miss     = %lu\n", lh->num_retrieve_miss);
+    BIO_printf(out, "num_hash_comps        = %lu\n", lh->num_hash_comps);
+}
+
+void OPENSSL_LH_node_stats_bio(const OPENSSL_LHASH *lh, BIO *out)
+{
+    OPENSSL_LH_NODE *n;
+    unsigned int i, num;
+
+    for (i = 0; i < lh->num_nodes; i++) {
+        for (n = lh->b[i], num = 0; n != NULL; n = n->next)
+            num++;
+        BIO_printf(out, "node %6u -> %3u\n", i, num);
+    }
+}
+
+void OPENSSL_LH_node_usage_stats_bio(const OPENSSL_LHASH *lh, BIO *out)
+{
+    OPENSSL_LH_NODE *n;
+    unsigned long num;
+    unsigned int i;
+    unsigned long total = 0, n_used = 0;
+
+    for (i = 0; i < lh->num_nodes; i++) {
+        for (n = lh->b[i], num = 0; n != NULL; n = n->next)
+            num++;
+        if (num != 0) {
+            n_used++;
+            total += num;
+        }
+    }
+    BIO_printf(out, "%lu nodes used out of %u\n", n_used, lh->num_nodes);
+    BIO_printf(out, "%lu items\n", total);
+    if (n_used == 0)
+        return;
+    BIO_printf(out, "load %d.%02d  actual load %d.%02d\n",
+               (int)(total / lh->num_nodes),
+               (int)((total % lh->num_nodes) * 100 / lh->num_nodes),
+               (int)(total / n_used), (int)((total % n_used) * 100 / n_used));
+}
diff --git a/crypto/lhash/lhash.c b/crypto/lhash/lhash.c
new file mode 100644
index 000000000..adde832cc
--- /dev/null
+++ b/crypto/lhash/lhash.c
@@ -0,0 +1,349 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include "lhash_lcl.h"
+
+
+#undef MIN_NODES
+#define MIN_NODES       16
+#define UP_LOAD         (2*LH_LOAD_MULT) /* load times 256 (default 2) */
+#define DOWN_LOAD       (LH_LOAD_MULT) /* load times 256 (default 1) */
+
+static int expand(OPENSSL_LHASH *lh);
+static void contract(OPENSSL_LHASH *lh);
+static OPENSSL_LH_NODE **getrn(OPENSSL_LHASH *lh, const void *data, unsigned long *rhash);
+
+OPENSSL_LHASH *OPENSSL_LH_new(OPENSSL_LH_HASHFUNC h, OPENSSL_LH_COMPFUNC c)
+{
+    OPENSSL_LHASH *ret;
+
+    if ((ret = OPENSSL_zalloc(sizeof(*ret))) == NULL)
+        goto err0;
+    if ((ret->b = OPENSSL_zalloc(sizeof(*ret->b) * MIN_NODES)) == NULL)
+        goto err1;
+    ret->comp = ((c == NULL) ? (OPENSSL_LH_COMPFUNC)strcmp : c);
+    ret->hash = ((h == NULL) ? (OPENSSL_LH_HASHFUNC)OPENSSL_LH_strhash : h);
+    ret->num_nodes = MIN_NODES / 2;
+    ret->num_alloc_nodes = MIN_NODES;
+    ret->pmax = MIN_NODES / 2;
+    ret->up_load = UP_LOAD;
+    ret->down_load = DOWN_LOAD;
+    return (ret);
+
+ err1:
+    OPENSSL_free(ret);
+ err0:
+    return (NULL);
+}
+
+void OPENSSL_LH_free(OPENSSL_LHASH *lh)
+{
+    unsigned int i;
+    OPENSSL_LH_NODE *n, *nn;
+
+    if (lh == NULL)
+        return;
+
+    for (i = 0; i < lh->num_nodes; i++) {
+        n = lh->b[i];
+        while (n != NULL) {
+            nn = n->next;
+            OPENSSL_free(n);
+            n = nn;
+        }
+    }
+    OPENSSL_free(lh->b);
+    OPENSSL_free(lh);
+}
+
+void *OPENSSL_LH_insert(OPENSSL_LHASH *lh, void *data)
+{
+    unsigned long hash;
+    OPENSSL_LH_NODE *nn, **rn;
+    void *ret;
+
+    lh->error = 0;
+    if ((lh->up_load <= (lh->num_items * LH_LOAD_MULT / lh->num_nodes)) && !expand(lh))
+        return NULL;        /* 'lh->error++' already done in 'expand' */
+
+    rn = getrn(lh, data, &hash);
+
+    if (*rn == NULL) {
+        if ((nn = OPENSSL_malloc(sizeof(*nn))) == NULL) {
+            lh->error++;
+            return (NULL);
+        }
+        nn->data = data;
+        nn->next = NULL;
+        nn->hash = hash;
+        *rn = nn;
+        ret = NULL;
+        lh->num_insert++;
+        lh->num_items++;
+    } else {                    /* replace same key */
+
+        ret = (*rn)->data;
+        (*rn)->data = data;
+        lh->num_replace++;
+    }
+    return (ret);
+}
+
+void *OPENSSL_LH_delete(OPENSSL_LHASH *lh, const void *data)
+{
+    unsigned long hash;
+    OPENSSL_LH_NODE *nn, **rn;
+    void *ret;
+
+    lh->error = 0;
+    rn = getrn(lh, data, &hash);
+
+    if (*rn == NULL) {
+        lh->num_no_delete++;
+        return (NULL);
+    } else {
+        nn = *rn;
+        *rn = nn->next;
+        ret = nn->data;
+        OPENSSL_free(nn);
+        lh->num_delete++;
+    }
+
+    lh->num_items--;
+    if ((lh->num_nodes > MIN_NODES) &&
+        (lh->down_load >= (lh->num_items * LH_LOAD_MULT / lh->num_nodes)))
+        contract(lh);
+
+    return (ret);
+}
+
+void *OPENSSL_LH_retrieve(OPENSSL_LHASH *lh, const void *data)
+{
+    unsigned long hash;
+    OPENSSL_LH_NODE **rn;
+    void *ret;
+
+    lh->error = 0;
+    rn = getrn(lh, data, &hash);
+
+    if (*rn == NULL) {
+        lh->num_retrieve_miss++;
+        return (NULL);
+    } else {
+        ret = (*rn)->data;
+        lh->num_retrieve++;
+    }
+    return (ret);
+}
+
+static void doall_util_fn(OPENSSL_LHASH *lh, int use_arg,
+                          OPENSSL_LH_DOALL_FUNC func,
+                          OPENSSL_LH_DOALL_FUNCARG func_arg, void *arg)
+{
+    int i;
+    OPENSSL_LH_NODE *a, *n;
+
+    if (lh == NULL)
+        return;
+
+    /*
+     * reverse the order so we search from 'top to bottom' We were having
+     * memory leaks otherwise
+     */
+    for (i = lh->num_nodes - 1; i >= 0; i--) {
+        a = lh->b[i];
+        while (a != NULL) {
+            n = a->next;
+            if (use_arg)
+                func_arg(a->data, arg);
+            else
+                func(a->data);
+            a = n;
+        }
+    }
+}
+
+void OPENSSL_LH_doall(OPENSSL_LHASH *lh, OPENSSL_LH_DOALL_FUNC func)
+{
+    doall_util_fn(lh, 0, func, (OPENSSL_LH_DOALL_FUNCARG)0, NULL);
+}
+
+void OPENSSL_LH_doall_arg(OPENSSL_LHASH *lh, OPENSSL_LH_DOALL_FUNCARG func, void *arg)
+{
+    doall_util_fn(lh, 1, (OPENSSL_LH_DOALL_FUNC)0, func, arg);
+}
+
+static int expand(OPENSSL_LHASH *lh)
+{
+    OPENSSL_LH_NODE **n, **n1, **n2, *np;
+    unsigned int p, i, j;
+    unsigned long hash, nni;
+
+    lh->num_nodes++;
+    lh->num_expands++;
+    p = (int)lh->p++;
+    n1 = &(lh->b[p]);
+    n2 = &(lh->b[p + (int)lh->pmax]);
+    *n2 = NULL;
+    nni = lh->num_alloc_nodes;
+
+    for (np = *n1; np != NULL;) {
+        hash = np->hash;
+        if ((hash % nni) != p) { /* move it */
+            *n1 = (*n1)->next;
+            np->next = *n2;
+            *n2 = np;
+        } else
+            n1 = &((*n1)->next);
+        np = *n1;
+    }
+
+    if ((lh->p) >= lh->pmax) {
+        j = (int)lh->num_alloc_nodes * 2;
+        n = OPENSSL_realloc(lh->b, (int)(sizeof(OPENSSL_LH_NODE *) * j));
+        if (n == NULL) {
+            lh->error++;
+            lh->num_nodes--;
+            lh->p = 0;
+            return 0;
+        }
+        for (i = (int)lh->num_alloc_nodes; i < j; i++) /* 26/02/92 eay */
+            n[i] = NULL;        /* 02/03/92 eay */
+        lh->pmax = lh->num_alloc_nodes;
+        lh->num_alloc_nodes = j;
+        lh->num_expand_reallocs++;
+        lh->p = 0;
+        lh->b = n;
+    }
+    return 1;
+}
+
+static void contract(OPENSSL_LHASH *lh)
+{
+    OPENSSL_LH_NODE **n, *n1, *np;
+
+    np = lh->b[lh->p + lh->pmax - 1];
+    lh->b[lh->p + lh->pmax - 1] = NULL; /* 24/07-92 - eay - weird but :-( */
+    if (lh->p == 0) {
+        n = OPENSSL_realloc(lh->b,
+                            (unsigned int)(sizeof(OPENSSL_LH_NODE *) * lh->pmax));
+        if (n == NULL) {
+            /* fputs("realloc error in lhash",stderr); */
+            lh->error++;
+            return;
+        }
+        lh->num_contract_reallocs++;
+        lh->num_alloc_nodes /= 2;
+        lh->pmax /= 2;
+        lh->p = lh->pmax - 1;
+        lh->b = n;
+    } else
+        lh->p--;
+
+    lh->num_nodes--;
+    lh->num_contracts++;
+
+    n1 = lh->b[(int)lh->p];
+    if (n1 == NULL)
+        lh->b[(int)lh->p] = np;
+    else {
+        while (n1->next != NULL)
+            n1 = n1->next;
+        n1->next = np;
+    }
+}
+
+static OPENSSL_LH_NODE **getrn(OPENSSL_LHASH *lh,
+                               const void *data, unsigned long *rhash)
+{
+    OPENSSL_LH_NODE **ret, *n1;
+    unsigned long hash, nn;
+    OPENSSL_LH_COMPFUNC cf;
+
+    hash = (*(lh->hash)) (data);
+    lh->num_hash_calls++;
+    *rhash = hash;
+
+    nn = hash % lh->pmax;
+    if (nn < lh->p)
+        nn = hash % lh->num_alloc_nodes;
+
+    cf = lh->comp;
+    ret = &(lh->b[(int)nn]);
+    for (n1 = *ret; n1 != NULL; n1 = n1->next) {
+        lh->num_hash_comps++;
+        if (n1->hash != hash) {
+            ret = &(n1->next);
+            continue;
+        }
+        lh->num_comp_calls++;
+        if (cf(n1->data, data) == 0)
+            break;
+        ret = &(n1->next);
+    }
+    return (ret);
+}
+
+/*
+ * The following hash seems to work very well on normal text strings no
+ * collisions on /usr/dict/words and it distributes on %2^n quite well, not
+ * as good as MD5, but still good.
+ */
+unsigned long OPENSSL_LH_strhash(const char *c)
+{
+    unsigned long ret = 0;
+    long n;
+    unsigned long v;
+    int r;
+
+    if ((c == NULL) || (*c == '\0'))
+        return (ret);
+/*-
+    unsigned char b[16];
+    MD5(c,strlen(c),b);
+    return(b[0]|(b[1]<<8)|(b[2]<<16)|(b[3]<<24));
+*/
+
+    n = 0x100;
+    while (*c) {
+        v = n | (*c);
+        n += 0x100;
+        r = (int)((v >> 2) ^ v) & 0x0f;
+        ret = (ret << r) | (ret >> (32 - r));
+        ret &= 0xFFFFFFFFL;
+        ret ^= v * v;
+        c++;
+    }
+    return ((ret >> 16) ^ ret);
+}
+
+unsigned long OPENSSL_LH_num_items(const OPENSSL_LHASH *lh)
+{
+    return lh ? lh->num_items : 0;
+}
+
+unsigned long OPENSSL_LH_get_down_load(const OPENSSL_LHASH *lh)
+{
+    return lh->down_load;
+}
+
+void OPENSSL_LH_set_down_load(OPENSSL_LHASH *lh, unsigned long down_load)
+{
+    lh->down_load = down_load;
+}
+
+int OPENSSL_LH_error(OPENSSL_LHASH *lh)
+{
+    return lh->error;
+}
diff --git a/crypto/lhash/lhash_lcl.h b/crypto/lhash/lhash_lcl.h
new file mode 100644
index 000000000..eb4a1a3f6
--- /dev/null
+++ b/crypto/lhash/lhash_lcl.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+
+struct lhash_node_st {
+    void *data;
+    struct lhash_node_st *next;
+    unsigned long hash;
+};
+
+struct lhash_st {
+    OPENSSL_LH_NODE **b;
+    OPENSSL_LH_COMPFUNC comp;
+    OPENSSL_LH_HASHFUNC hash;
+    unsigned int num_nodes;
+    unsigned int num_alloc_nodes;
+    unsigned int p;
+    unsigned int pmax;
+    unsigned long up_load;      /* load times 256 */
+    unsigned long down_load;    /* load times 256 */
+    unsigned long num_items;
+    unsigned long num_expands;
+    unsigned long num_expand_reallocs;
+    unsigned long num_contracts;
+    unsigned long num_contract_reallocs;
+    unsigned long num_hash_calls;
+    unsigned long num_comp_calls;
+    unsigned long num_insert;
+    unsigned long num_replace;
+    unsigned long num_delete;
+    unsigned long num_no_delete;
+    unsigned long num_retrieve;
+    unsigned long num_retrieve_miss;
+    unsigned long num_hash_comps;
+    int error;
+};
diff --git a/crypto/lhash/num.pl b/crypto/lhash/num.pl
new file mode 100644
index 000000000..8a8c42c8a
--- /dev/null
+++ b/crypto/lhash/num.pl
@@ -0,0 +1,23 @@
+#! /usr/bin/env perl
+# Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+#node     10 ->   4
+
+while (<>)
+	{
+	next unless /^node/;
+	s|\R$||;                # Better chomp
+	@a=split;
+	$num{$a[3]}++;
+	}
+
+@a=sort {$a <=> $b } keys %num;
+foreach (0 .. $a[$#a])
+	{
+	printf "%4d:%4d\n",$_,$num{$_};
+	}
diff --git a/crypto/md2/build.info b/crypto/md2/build.info
new file mode 100644
index 000000000..e31948c23
--- /dev/null
+++ b/crypto/md2/build.info
@@ -0,0 +1,3 @@
+LIBS=../../libcrypto
+SOURCE[../../libcrypto]=\
+        md2_dgst.c md2_one.c
diff --git a/crypto/md2/md2_dgst.c b/crypto/md2/md2_dgst.c
new file mode 100644
index 000000000..ff062fd47
--- /dev/null
+++ b/crypto/md2/md2_dgst.c
@@ -0,0 +1,173 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+/*
+ * Implemented from RFC1319 The MD2 Message-Digest Algorithm
+ */
+
+#define UCHAR   unsigned char
+
+static void md2_block(MD2_CTX *c, const unsigned char *d);
+/*
+ * The magic S table - I have converted it to hex since it is basically just
+ * a random byte string.
+ */
+static const MD2_INT S[256] = {
+    0x29, 0x2E, 0x43, 0xC9, 0xA2, 0xD8, 0x7C, 0x01,
+    0x3D, 0x36, 0x54, 0xA1, 0xEC, 0xF0, 0x06, 0x13,
+    0x62, 0xA7, 0x05, 0xF3, 0xC0, 0xC7, 0x73, 0x8C,
+    0x98, 0x93, 0x2B, 0xD9, 0xBC, 0x4C, 0x82, 0xCA,
+    0x1E, 0x9B, 0x57, 0x3C, 0xFD, 0xD4, 0xE0, 0x16,
+    0x67, 0x42, 0x6F, 0x18, 0x8A, 0x17, 0xE5, 0x12,
+    0xBE, 0x4E, 0xC4, 0xD6, 0xDA, 0x9E, 0xDE, 0x49,
+    0xA0, 0xFB, 0xF5, 0x8E, 0xBB, 0x2F, 0xEE, 0x7A,
+    0xA9, 0x68, 0x79, 0x91, 0x15, 0xB2, 0x07, 0x3F,
+    0x94, 0xC2, 0x10, 0x89, 0x0B, 0x22, 0x5F, 0x21,
+    0x80, 0x7F, 0x5D, 0x9A, 0x5A, 0x90, 0x32, 0x27,
+    0x35, 0x3E, 0xCC, 0xE7, 0xBF, 0xF7, 0x97, 0x03,
+    0xFF, 0x19, 0x30, 0xB3, 0x48, 0xA5, 0xB5, 0xD1,
+    0xD7, 0x5E, 0x92, 0x2A, 0xAC, 0x56, 0xAA, 0xC6,
+    0x4F, 0xB8, 0x38, 0xD2, 0x96, 0xA4, 0x7D, 0xB6,
+    0x76, 0xFC, 0x6B, 0xE2, 0x9C, 0x74, 0x04, 0xF1,
+    0x45, 0x9D, 0x70, 0x59, 0x64, 0x71, 0x87, 0x20,
+    0x86, 0x5B, 0xCF, 0x65, 0xE6, 0x2D, 0xA8, 0x02,
+    0x1B, 0x60, 0x25, 0xAD, 0xAE, 0xB0, 0xB9, 0xF6,
+    0x1C, 0x46, 0x61, 0x69, 0x34, 0x40, 0x7E, 0x0F,
+    0x55, 0x47, 0xA3, 0x23, 0xDD, 0x51, 0xAF, 0x3A,
+    0xC3, 0x5C, 0xF9, 0xCE, 0xBA, 0xC5, 0xEA, 0x26,
+    0x2C, 0x53, 0x0D, 0x6E, 0x85, 0x28, 0x84, 0x09,
+    0xD3, 0xDF, 0xCD, 0xF4, 0x41, 0x81, 0x4D, 0x52,
+    0x6A, 0xDC, 0x37, 0xC8, 0x6C, 0xC1, 0xAB, 0xFA,
+    0x24, 0xE1, 0x7B, 0x08, 0x0C, 0xBD, 0xB1, 0x4A,
+    0x78, 0x88, 0x95, 0x8B, 0xE3, 0x63, 0xE8, 0x6D,
+    0xE9, 0xCB, 0xD5, 0xFE, 0x3B, 0x00, 0x1D, 0x39,
+    0xF2, 0xEF, 0xB7, 0x0E, 0x66, 0x58, 0xD0, 0xE4,
+    0xA6, 0x77, 0x72, 0xF8, 0xEB, 0x75, 0x4B, 0x0A,
+    0x31, 0x44, 0x50, 0xB4, 0x8F, 0xED, 0x1F, 0x1A,
+    0xDB, 0x99, 0x8D, 0x33, 0x9F, 0x11, 0x83, 0x14,
+};
+
+const char *MD2_options(void)
+{
+    if (sizeof(MD2_INT) == 1)
+        return ("md2(char)");
+    else
+        return ("md2(int)");
+}
+
+int MD2_Init(MD2_CTX *c)
+{
+    c->num = 0;
+    memset(c->state, 0, sizeof(c->state));
+    memset(c->cksm, 0, sizeof(c->cksm));
+    memset(c->data, 0, sizeof(c->data));
+    return 1;
+}
+
+int MD2_Update(MD2_CTX *c, const unsigned char *data, size_t len)
+{
+    register UCHAR *p;
+
+    if (len == 0)
+        return 1;
+
+    p = c->data;
+    if (c->num != 0) {
+        if ((c->num + len) >= MD2_BLOCK) {
+            memcpy(&(p[c->num]), data, MD2_BLOCK - c->num);
+            md2_block(c, c->data);
+            data += (MD2_BLOCK - c->num);
+            len -= (MD2_BLOCK - c->num);
+            c->num = 0;
+            /* drop through and do the rest */
+        } else {
+            memcpy(&(p[c->num]), data, len);
+            /* data+=len; */
+            c->num += (int)len;
+            return 1;
+        }
+    }
+    /*
+     * we now can process the input data in blocks of MD2_BLOCK chars and
+     * save the leftovers to c->data.
+     */
+    while (len >= MD2_BLOCK) {
+        md2_block(c, data);
+        data += MD2_BLOCK;
+        len -= MD2_BLOCK;
+    }
+    memcpy(p, data, len);
+    c->num = (int)len;
+    return 1;
+}
+
+static void md2_block(MD2_CTX *c, const unsigned char *d)
+{
+    register MD2_INT t, *sp1, *sp2;
+    register int i, j;
+    MD2_INT state[48];
+
+    sp1 = c->state;
+    sp2 = c->cksm;
+    j = sp2[MD2_BLOCK - 1];
+    for (i = 0; i < 16; i++) {
+        state[i] = sp1[i];
+        state[i + 16] = t = d[i];
+        state[i + 32] = (t ^ sp1[i]);
+        j = sp2[i] ^= S[t ^ j];
+    }
+    t = 0;
+    for (i = 0; i < 18; i++) {
+        for (j = 0; j < 48; j += 8) {
+            t = state[j + 0] ^= S[t];
+            t = state[j + 1] ^= S[t];
+            t = state[j + 2] ^= S[t];
+            t = state[j + 3] ^= S[t];
+            t = state[j + 4] ^= S[t];
+            t = state[j + 5] ^= S[t];
+            t = state[j + 6] ^= S[t];
+            t = state[j + 7] ^= S[t];
+        }
+        t = (t + i) & 0xff;
+    }
+    memcpy(sp1, state, 16 * sizeof(MD2_INT));
+    OPENSSL_cleanse(state, 48 * sizeof(MD2_INT));
+}
+
+int MD2_Final(unsigned char *md, MD2_CTX *c)
+{
+    int i, v;
+    register UCHAR *cp;
+    register MD2_INT *p1, *p2;
+
+    cp = c->data;
+    p1 = c->state;
+    p2 = c->cksm;
+    v = MD2_BLOCK - c->num;
+    for (i = c->num; i < MD2_BLOCK; i++)
+        cp[i] = (UCHAR) v;
+
+    md2_block(c, cp);
+
+    for (i = 0; i < MD2_BLOCK; i++)
+        cp[i] = (UCHAR) p2[i];
+    md2_block(c, cp);
+
+    for (i = 0; i < 16; i++)
+        md[i] = (UCHAR) (p1[i] & 0xff);
+    OPENSSL_cleanse(c, sizeof(*c));
+    return 1;
+}
diff --git a/crypto/md2/md2_one.c b/crypto/md2/md2_one.c
new file mode 100644
index 000000000..460f96e47
--- /dev/null
+++ b/crypto/md2/md2_one.c
@@ -0,0 +1,47 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+
+/*
+ * This is a separate file so that #defines in cryptlib.h can map my MD
+ * functions to different names
+ */
+
+unsigned char *MD2(const unsigned char *d, size_t n, unsigned char *md)
+{
+    MD2_CTX c;
+    static unsigned char m[MD2_DIGEST_LENGTH];
+
+    if (md == NULL)
+        md = m;
+    if (!MD2_Init(&c))
+        return NULL;
+#ifndef CHARSET_EBCDIC
+    MD2_Update(&c, d, n);
+#else
+    {
+        char temp[1024];
+        unsigned long chunk;
+
+        while (n > 0) {
+            chunk = (n > sizeof(temp)) ? sizeof(temp) : n;
+            ebcdic2ascii(temp, d, chunk);
+            MD2_Update(&c, temp, chunk);
+            n -= chunk;
+            d += chunk;
+        }
+    }
+#endif
+    MD2_Final(md, &c);
+    OPENSSL_cleanse(&c, sizeof(c)); /* Security consideration */
+    return (md);
+}
diff --git a/crypto/md4/build.info b/crypto/md4/build.info
new file mode 100644
index 000000000..20846e0dc
--- /dev/null
+++ b/crypto/md4/build.info
@@ -0,0 +1,3 @@
+LIBS=../../libcrypto
+SOURCE[../../libcrypto]=\
+        md4_dgst.c md4_one.c
diff --git a/crypto/md4/md4_dgst.c b/crypto/md4/md4_dgst.c
new file mode 100644
index 000000000..531961861
--- /dev/null
+++ b/crypto/md4/md4_dgst.c
@@ -0,0 +1,147 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include "md4_locl.h"
+
+/*
+ * Implemented from RFC1186 The MD4 Message-Digest Algorithm
+ */
+
+#define INIT_DATA_A (unsigned long)0x67452301L
+#define INIT_DATA_B (unsigned long)0xefcdab89L
+#define INIT_DATA_C (unsigned long)0x98badcfeL
+#define INIT_DATA_D (unsigned long)0x10325476L
+
+int MD4_Init(MD4_CTX *c)
+{
+    memset(c, 0, sizeof(*c));
+    c->A = INIT_DATA_A;
+    c->B = INIT_DATA_B;
+    c->C = INIT_DATA_C;
+    c->D = INIT_DATA_D;
+    return 1;
+}
+
+#ifndef md4_block_data_order
+# ifdef X
+#  undef X
+# endif
+void md4_block_data_order(MD4_CTX *c, const void *data_, size_t num)
+{
+    const unsigned char *data = data_;
+    register unsigned MD32_REG_T A, B, C, D, l;
+# ifndef MD32_XARRAY
+    /* See comment in crypto/sha/sha_locl.h for details. */
+    unsigned MD32_REG_T XX0, XX1, XX2, XX3, XX4, XX5, XX6, XX7,
+        XX8, XX9, XX10, XX11, XX12, XX13, XX14, XX15;
+#  define X(i)   XX##i
+# else
+    MD4_LONG XX[MD4_LBLOCK];
+#  define X(i)   XX[i]
+# endif
+
+    A = c->A;
+    B = c->B;
+    C = c->C;
+    D = c->D;
+
+    for (; num--;) {
+        (void)HOST_c2l(data, l);
+        X(0) = l;
+        (void)HOST_c2l(data, l);
+        X(1) = l;
+        /* Round 0 */
+        R0(A, B, C, D, X(0), 3, 0);
+        (void)HOST_c2l(data, l);
+        X(2) = l;
+        R0(D, A, B, C, X(1), 7, 0);
+        (void)HOST_c2l(data, l);
+        X(3) = l;
+        R0(C, D, A, B, X(2), 11, 0);
+        (void)HOST_c2l(data, l);
+        X(4) = l;
+        R0(B, C, D, A, X(3), 19, 0);
+        (void)HOST_c2l(data, l);
+        X(5) = l;
+        R0(A, B, C, D, X(4), 3, 0);
+        (void)HOST_c2l(data, l);
+        X(6) = l;
+        R0(D, A, B, C, X(5), 7, 0);
+        (void)HOST_c2l(data, l);
+        X(7) = l;
+        R0(C, D, A, B, X(6), 11, 0);
+        (void)HOST_c2l(data, l);
+        X(8) = l;
+        R0(B, C, D, A, X(7), 19, 0);
+        (void)HOST_c2l(data, l);
+        X(9) = l;
+        R0(A, B, C, D, X(8), 3, 0);
+        (void)HOST_c2l(data, l);
+        X(10) = l;
+        R0(D, A, B, C, X(9), 7, 0);
+        (void)HOST_c2l(data, l);
+        X(11) = l;
+        R0(C, D, A, B, X(10), 11, 0);
+        (void)HOST_c2l(data, l);
+        X(12) = l;
+        R0(B, C, D, A, X(11), 19, 0);
+        (void)HOST_c2l(data, l);
+        X(13) = l;
+        R0(A, B, C, D, X(12), 3, 0);
+        (void)HOST_c2l(data, l);
+        X(14) = l;
+        R0(D, A, B, C, X(13), 7, 0);
+        (void)HOST_c2l(data, l);
+        X(15) = l;
+        R0(C, D, A, B, X(14), 11, 0);
+        R0(B, C, D, A, X(15), 19, 0);
+        /* Round 1 */
+        R1(A, B, C, D, X(0), 3, 0x5A827999L);
+        R1(D, A, B, C, X(4), 5, 0x5A827999L);
+        R1(C, D, A, B, X(8), 9, 0x5A827999L);
+        R1(B, C, D, A, X(12), 13, 0x5A827999L);
+        R1(A, B, C, D, X(1), 3, 0x5A827999L);
+        R1(D, A, B, C, X(5), 5, 0x5A827999L);
+        R1(C, D, A, B, X(9), 9, 0x5A827999L);
+        R1(B, C, D, A, X(13), 13, 0x5A827999L);
+        R1(A, B, C, D, X(2), 3, 0x5A827999L);
+        R1(D, A, B, C, X(6), 5, 0x5A827999L);
+        R1(C, D, A, B, X(10), 9, 0x5A827999L);
+        R1(B, C, D, A, X(14), 13, 0x5A827999L);
+        R1(A, B, C, D, X(3), 3, 0x5A827999L);
+        R1(D, A, B, C, X(7), 5, 0x5A827999L);
+        R1(C, D, A, B, X(11), 9, 0x5A827999L);
+        R1(B, C, D, A, X(15), 13, 0x5A827999L);
+        /* Round 2 */
+        R2(A, B, C, D, X(0), 3, 0x6ED9EBA1L);
+        R2(D, A, B, C, X(8), 9, 0x6ED9EBA1L);
+        R2(C, D, A, B, X(4), 11, 0x6ED9EBA1L);
+        R2(B, C, D, A, X(12), 15, 0x6ED9EBA1L);
+        R2(A, B, C, D, X(2), 3, 0x6ED9EBA1L);
+        R2(D, A, B, C, X(10), 9, 0x6ED9EBA1L);
+        R2(C, D, A, B, X(6), 11, 0x6ED9EBA1L);
+        R2(B, C, D, A, X(14), 15, 0x6ED9EBA1L);
+        R2(A, B, C, D, X(1), 3, 0x6ED9EBA1L);
+        R2(D, A, B, C, X(9), 9, 0x6ED9EBA1L);
+        R2(C, D, A, B, X(5), 11, 0x6ED9EBA1L);
+        R2(B, C, D, A, X(13), 15, 0x6ED9EBA1L);
+        R2(A, B, C, D, X(3), 3, 0x6ED9EBA1L);
+        R2(D, A, B, C, X(11), 9, 0x6ED9EBA1L);
+        R2(C, D, A, B, X(7), 11, 0x6ED9EBA1L);
+        R2(B, C, D, A, X(15), 15, 0x6ED9EBA1L);
+
+        A = c->A += A;
+        B = c->B += B;
+        C = c->C += C;
+        D = c->D += D;
+    }
+}
+#endif
diff --git a/crypto/md4/md4_locl.h b/crypto/md4/md4_locl.h
new file mode 100644
index 000000000..6aec55626
--- /dev/null
+++ b/crypto/md4/md4_locl.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include 
+#include 
+
+void md4_block_data_order(MD4_CTX *c, const void *p, size_t num);
+
+#define DATA_ORDER_IS_LITTLE_ENDIAN
+
+#define HASH_LONG               MD4_LONG
+#define HASH_CTX                MD4_CTX
+#define HASH_CBLOCK             MD4_CBLOCK
+#define HASH_UPDATE             MD4_Update
+#define HASH_TRANSFORM          MD4_Transform
+#define HASH_FINAL              MD4_Final
+#define HASH_MAKE_STRING(c,s)   do {    \
+        unsigned long ll;               \
+        ll=(c)->A; (void)HOST_l2c(ll,(s));      \
+        ll=(c)->B; (void)HOST_l2c(ll,(s));      \
+        ll=(c)->C; (void)HOST_l2c(ll,(s));      \
+        ll=(c)->D; (void)HOST_l2c(ll,(s));      \
+        } while (0)
+#define HASH_BLOCK_DATA_ORDER   md4_block_data_order
+
+#include "internal/md32_common.h"
+
+/*-
+#define F(x,y,z)        (((x) & (y))  |  ((~(x)) & (z)))
+#define G(x,y,z)        (((x) & (y))  |  ((x) & ((z))) | ((y) & ((z))))
+*/
+
+/*
+ * As pointed out by Wei Dai , the above can be simplified
+ * to the code below.  Wei attributes these optimizations to Peter Gutmann's
+ * SHS code, and he attributes it to Rich Schroeppel.
+ */
+#define F(b,c,d)        ((((c) ^ (d)) & (b)) ^ (d))
+#define G(b,c,d)        (((b) & (c)) | ((b) & (d)) | ((c) & (d)))
+#define H(b,c,d)        ((b) ^ (c) ^ (d))
+
+#define R0(a,b,c,d,k,s,t) { \
+        a+=((k)+(t)+F((b),(c),(d))); \
+        a=ROTATE(a,s); };
+
+#define R1(a,b,c,d,k,s,t) { \
+        a+=((k)+(t)+G((b),(c),(d))); \
+        a=ROTATE(a,s); };\
+
+#define R2(a,b,c,d,k,s,t) { \
+        a+=((k)+(t)+H((b),(c),(d))); \
+        a=ROTATE(a,s); };
diff --git a/crypto/md4/md4_one.c b/crypto/md4/md4_one.c
new file mode 100644
index 000000000..9f0989fad
--- /dev/null
+++ b/crypto/md4/md4_one.c
@@ -0,0 +1,47 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include 
+#include 
+
+#ifdef CHARSET_EBCDIC
+# include 
+#endif
+
+unsigned char *MD4(const unsigned char *d, size_t n, unsigned char *md)
+{
+    MD4_CTX c;
+    static unsigned char m[MD4_DIGEST_LENGTH];
+
+    if (md == NULL)
+        md = m;
+    if (!MD4_Init(&c))
+        return NULL;
+#ifndef CHARSET_EBCDIC
+    MD4_Update(&c, d, n);
+#else
+    {
+        char temp[1024];
+        unsigned long chunk;
+
+        while (n > 0) {
+            chunk = (n > sizeof(temp)) ? sizeof(temp) : n;
+            ebcdic2ascii(temp, d, chunk);
+            MD4_Update(&c, temp, chunk);
+            n -= chunk;
+            d += chunk;
+        }
+    }
+#endif
+    MD4_Final(md, &c);
+    OPENSSL_cleanse(&c, sizeof(c)); /* security consideration */
+    return (md);
+}
diff --git a/crypto/md5/asm/md5-586.pl b/crypto/md5/asm/md5-586.pl
new file mode 100644
index 000000000..24f68af54
--- /dev/null
+++ b/crypto/md5/asm/md5-586.pl
@@ -0,0 +1,318 @@
+#! /usr/bin/env perl
+# Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+
+# Normal is the
+# md5_block_x86(MD5_CTX *c, ULONG *X);
+# version, non-normal is the
+# md5_block_x86(MD5_CTX *c, ULONG *X,int blocks);
+
+$normal=0;
+
+$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+push(@INC,"${dir}","${dir}../../perlasm");
+require "x86asm.pl";
+
+$output=pop;
+open STDOUT,">$output";
+
+&asm_init($ARGV[0],$0);
+
+$A="eax";
+$B="ebx";
+$C="ecx";
+$D="edx";
+$tmp1="edi";
+$tmp2="ebp";
+$X="esi";
+
+# What we need to load into $tmp for the next round
+%Ltmp1=("R0",&Np($C), "R1",&Np($C), "R2",&Np($C), "R3",&Np($D));
+@xo=(
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,	# R0
+ 1, 6, 11, 0, 5, 10, 15, 4, 9, 14, 3, 8, 13, 2, 7, 12,	# R1
+ 5, 8, 11, 14, 1, 4, 7, 10, 13, 0, 3, 6, 9, 12, 15, 2,	# R2
+ 0, 7, 14, 5, 12, 3, 10, 1, 8, 15, 6, 13, 4, 11, 2, 9,	# R3
+ );
+
+&md5_block("md5_block_asm_data_order");
+&asm_finish();
+
+close STDOUT;
+
+sub Np
+	{
+	local($p)=@_;
+	local(%n)=($A,$D,$B,$A,$C,$B,$D,$C);
+	return($n{$p});
+	}
+
+sub R0
+	{
+	local($pos,$a,$b,$c,$d,$K,$ki,$s,$t)=@_;
+
+	&mov($tmp1,$C)  if $pos < 0;
+	&mov($tmp2,&DWP($xo[$ki]*4,$K,"",0)) if $pos < 0; # very first one 
+
+	# body proper
+
+	&comment("R0 $ki");
+	&xor($tmp1,$d); # F function - part 2
+
+	&and($tmp1,$b); # F function - part 3
+	&lea($a,&DWP($t,$a,$tmp2,1));
+
+	&xor($tmp1,$d); # F function - part 4
+	&mov($tmp2,&DWP($xo[$ki+1]*4,$K,"",0)) if ($pos != 2);
+
+	&add($a,$tmp1);
+
+	&rotl($a,$s);
+
+	&mov($tmp1,&Np($c)) if $pos < 1;	# next tmp1 for R0
+	&mov($tmp1,&Np($c)) if $pos == 1;	# next tmp1 for R1
+
+	&add($a,$b);
+	}
+
+sub R1
+	{
+	local($pos,$a,$b,$c,$d,$K,$ki,$s,$t)=@_;
+
+	&comment("R1 $ki");
+
+	&xor($tmp1,$b); # G function - part 2
+	&and($tmp1,$d); # G function - part 3
+	&lea($a,&DWP($t,$a,$tmp2,1));
+
+	&xor($tmp1,$c);			# G function - part 4
+	&mov($tmp2,&DWP($xo[$ki+1]*4,$K,"",0)) if ($pos != 2);
+
+	&add($a,$tmp1);
+	&mov($tmp1,&Np($c)) if $pos < 1;	# G function - part 1
+	&mov($tmp1,&Np($c)) if $pos == 1;	# G function - part 1
+
+	&rotl($a,$s);
+
+	&add($a,$b);
+	}
+
+sub R2
+	{
+	local($n,$pos,$a,$b,$c,$d,$K,$ki,$s,$t)=@_;
+	# This one is different, only 3 logical operations
+
+if (($n & 1) == 0)
+	{
+	&comment("R2 $ki");
+	# make sure to do 'D' first, not 'B', else we clash with
+	# the last add from the previous round.
+
+	&xor($tmp1,$d); # H function - part 2
+
+	&xor($tmp1,$b); # H function - part 3
+	&lea($a,&DWP($t,$a,$tmp2,1));
+
+	&add($a,$tmp1);
+	&mov($tmp2,&DWP($xo[$ki+1]*4,$K,"",0));
+
+	&rotl($a,$s);
+
+	&mov($tmp1,&Np($c));
+	}
+else
+	{
+	&comment("R2 $ki");
+	# make sure to do 'D' first, not 'B', else we clash with
+	# the last add from the previous round.
+
+	&add($b,$c);			# MOVED FORWARD
+	&xor($tmp1,$d); # H function - part 2
+
+	&lea($a,&DWP($t,$a,$tmp2,1));
+
+	&xor($tmp1,$b); # H function - part 3
+	&mov($tmp2,&DWP($xo[$ki+1]*4,$K,"",0)) if ($pos != 2);
+
+	&add($a,$tmp1);
+	&mov($tmp1,&Np($c)) if $pos < 1;	# H function - part 1
+	&mov($tmp1,-1) if $pos == 1;		# I function - part 1
+
+	&rotl($a,$s);
+
+	&add($a,$b);
+	}
+	}
+
+sub R3
+	{
+	local($pos,$a,$b,$c,$d,$K,$ki,$s,$t)=@_;
+
+	&comment("R3 $ki");
+
+	# ¬($tmp1)
+	&xor($tmp1,$d) if $pos < 0; 	# I function - part 2
+
+	&or($tmp1,$b);				# I function - part 3
+	&lea($a,&DWP($t,$a,$tmp2,1));
+
+	&xor($tmp1,$c); 			# I function - part 4
+	&mov($tmp2,&DWP($xo[$ki+1]*4,$K,"",0))	if $pos != 2; # load X/k value
+	&mov($tmp2,&wparam(0)) if $pos == 2;
+
+	&add($a,$tmp1);
+	&mov($tmp1,-1) if $pos < 1;	# H function - part 1
+	&add($K,64) if $pos >=1 && !$normal;
+
+	&rotl($a,$s);
+
+	&xor($tmp1,&Np($d)) if $pos <= 0; 	# I function - part = first time
+	&mov($tmp1,&DWP( 0,$tmp2,"",0)) if $pos > 0;
+	&add($a,$b);
+	}
+
+
+sub md5_block
+	{
+	local($name)=@_;
+
+	&function_begin_B($name,"",3);
+
+	# parameter 1 is the MD5_CTX structure.
+	# A	0
+	# B	4
+	# C	8
+	# D 	12
+
+	&push("esi");
+	 &push("edi");
+	&mov($tmp1,	&wparam(0)); # edi
+	 &mov($X,	&wparam(1)); # esi
+	&mov($C,	&wparam(2));
+	 &push("ebp");
+	&shl($C,	6);
+	&push("ebx");
+	 &add($C,	$X); # offset we end at
+	&sub($C,	64);
+	 &mov($A,	&DWP( 0,$tmp1,"",0));
+	&push($C);	# Put on the TOS
+	 &mov($B,	&DWP( 4,$tmp1,"",0));
+	&mov($C,	&DWP( 8,$tmp1,"",0));
+	 &mov($D,	&DWP(12,$tmp1,"",0));
+
+	&set_label("start") unless $normal;
+	&comment("");
+	&comment("R0 section");
+
+	&R0(-2,$A,$B,$C,$D,$X, 0, 7,0xd76aa478);
+	&R0( 0,$D,$A,$B,$C,$X, 1,12,0xe8c7b756);
+	&R0( 0,$C,$D,$A,$B,$X, 2,17,0x242070db);
+	&R0( 0,$B,$C,$D,$A,$X, 3,22,0xc1bdceee);
+	&R0( 0,$A,$B,$C,$D,$X, 4, 7,0xf57c0faf);
+	&R0( 0,$D,$A,$B,$C,$X, 5,12,0x4787c62a);
+	&R0( 0,$C,$D,$A,$B,$X, 6,17,0xa8304613);
+	&R0( 0,$B,$C,$D,$A,$X, 7,22,0xfd469501);
+	&R0( 0,$A,$B,$C,$D,$X, 8, 7,0x698098d8);
+	&R0( 0,$D,$A,$B,$C,$X, 9,12,0x8b44f7af);
+	&R0( 0,$C,$D,$A,$B,$X,10,17,0xffff5bb1);
+	&R0( 0,$B,$C,$D,$A,$X,11,22,0x895cd7be);
+	&R0( 0,$A,$B,$C,$D,$X,12, 7,0x6b901122);
+	&R0( 0,$D,$A,$B,$C,$X,13,12,0xfd987193);
+	&R0( 0,$C,$D,$A,$B,$X,14,17,0xa679438e);
+	&R0( 1,$B,$C,$D,$A,$X,15,22,0x49b40821);
+
+	&comment("");
+	&comment("R1 section");
+	&R1(-1,$A,$B,$C,$D,$X,16, 5,0xf61e2562);
+	&R1( 0,$D,$A,$B,$C,$X,17, 9,0xc040b340);
+	&R1( 0,$C,$D,$A,$B,$X,18,14,0x265e5a51);
+	&R1( 0,$B,$C,$D,$A,$X,19,20,0xe9b6c7aa);
+	&R1( 0,$A,$B,$C,$D,$X,20, 5,0xd62f105d);
+	&R1( 0,$D,$A,$B,$C,$X,21, 9,0x02441453);
+	&R1( 0,$C,$D,$A,$B,$X,22,14,0xd8a1e681);
+	&R1( 0,$B,$C,$D,$A,$X,23,20,0xe7d3fbc8);
+	&R1( 0,$A,$B,$C,$D,$X,24, 5,0x21e1cde6);
+	&R1( 0,$D,$A,$B,$C,$X,25, 9,0xc33707d6);
+	&R1( 0,$C,$D,$A,$B,$X,26,14,0xf4d50d87);
+	&R1( 0,$B,$C,$D,$A,$X,27,20,0x455a14ed);
+	&R1( 0,$A,$B,$C,$D,$X,28, 5,0xa9e3e905);
+	&R1( 0,$D,$A,$B,$C,$X,29, 9,0xfcefa3f8);
+	&R1( 0,$C,$D,$A,$B,$X,30,14,0x676f02d9);
+	&R1( 1,$B,$C,$D,$A,$X,31,20,0x8d2a4c8a);
+
+	&comment("");
+	&comment("R2 section");
+	&R2( 0,-1,$A,$B,$C,$D,$X,32, 4,0xfffa3942);
+	&R2( 1, 0,$D,$A,$B,$C,$X,33,11,0x8771f681);
+	&R2( 2, 0,$C,$D,$A,$B,$X,34,16,0x6d9d6122);
+	&R2( 3, 0,$B,$C,$D,$A,$X,35,23,0xfde5380c);
+	&R2( 4, 0,$A,$B,$C,$D,$X,36, 4,0xa4beea44);
+	&R2( 5, 0,$D,$A,$B,$C,$X,37,11,0x4bdecfa9);
+	&R2( 6, 0,$C,$D,$A,$B,$X,38,16,0xf6bb4b60);
+	&R2( 7, 0,$B,$C,$D,$A,$X,39,23,0xbebfbc70);
+	&R2( 8, 0,$A,$B,$C,$D,$X,40, 4,0x289b7ec6);
+	&R2( 9, 0,$D,$A,$B,$C,$X,41,11,0xeaa127fa);
+	&R2(10, 0,$C,$D,$A,$B,$X,42,16,0xd4ef3085);
+	&R2(11, 0,$B,$C,$D,$A,$X,43,23,0x04881d05);
+	&R2(12, 0,$A,$B,$C,$D,$X,44, 4,0xd9d4d039);
+	&R2(13, 0,$D,$A,$B,$C,$X,45,11,0xe6db99e5);
+	&R2(14, 0,$C,$D,$A,$B,$X,46,16,0x1fa27cf8);
+	&R2(15, 1,$B,$C,$D,$A,$X,47,23,0xc4ac5665);
+
+	&comment("");
+	&comment("R3 section");
+	&R3(-1,$A,$B,$C,$D,$X,48, 6,0xf4292244);
+	&R3( 0,$D,$A,$B,$C,$X,49,10,0x432aff97);
+	&R3( 0,$C,$D,$A,$B,$X,50,15,0xab9423a7);
+	&R3( 0,$B,$C,$D,$A,$X,51,21,0xfc93a039);
+	&R3( 0,$A,$B,$C,$D,$X,52, 6,0x655b59c3);
+	&R3( 0,$D,$A,$B,$C,$X,53,10,0x8f0ccc92);
+	&R3( 0,$C,$D,$A,$B,$X,54,15,0xffeff47d);
+	&R3( 0,$B,$C,$D,$A,$X,55,21,0x85845dd1);
+	&R3( 0,$A,$B,$C,$D,$X,56, 6,0x6fa87e4f);
+	&R3( 0,$D,$A,$B,$C,$X,57,10,0xfe2ce6e0);
+	&R3( 0,$C,$D,$A,$B,$X,58,15,0xa3014314);
+	&R3( 0,$B,$C,$D,$A,$X,59,21,0x4e0811a1);
+	&R3( 0,$A,$B,$C,$D,$X,60, 6,0xf7537e82);
+	&R3( 0,$D,$A,$B,$C,$X,61,10,0xbd3af235);
+	&R3( 0,$C,$D,$A,$B,$X,62,15,0x2ad7d2bb);
+	&R3( 2,$B,$C,$D,$A,$X,63,21,0xeb86d391);
+
+	# &mov($tmp2,&wparam(0));	# done in the last R3
+	# &mov($tmp1,	&DWP( 0,$tmp2,"",0)); # done is the last R3
+
+	&add($A,$tmp1);
+	 &mov($tmp1,	&DWP( 4,$tmp2,"",0));
+
+	&add($B,$tmp1);
+	&mov($tmp1,	&DWP( 8,$tmp2,"",0));
+
+	&add($C,$tmp1);
+	&mov($tmp1,	&DWP(12,$tmp2,"",0));
+
+	&add($D,$tmp1);
+	&mov(&DWP( 0,$tmp2,"",0),$A);
+
+	&mov(&DWP( 4,$tmp2,"",0),$B);
+	&mov($tmp1,&swtmp(0)) unless $normal;
+
+	&mov(&DWP( 8,$tmp2,"",0),$C);
+	 &mov(&DWP(12,$tmp2,"",0),$D);
+
+	&cmp($tmp1,$X) unless $normal;			# check count
+	 &jae(&label("start")) unless $normal;
+
+	&pop("eax"); # pop the temp variable off the stack
+	 &pop("ebx");
+	&pop("ebp");
+	 &pop("edi");
+	&pop("esi");
+	 &ret();
+	&function_end_B($name);
+	}
+
diff --git a/crypto/md5/asm/md5-ia64.S b/crypto/md5/asm/md5-ia64.S
new file mode 100644
index 000000000..c20467b47
--- /dev/null
+++ b/crypto/md5/asm/md5-ia64.S
@@ -0,0 +1,1002 @@
+/*
+ *
+ * Copyright 2005-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/* Copyright (c) 2005 Hewlett-Packard Development Company, L.P.
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.  */
+
+//	Common registers are assigned as follows:
+//
+//	COMMON
+//
+//	t0		Const Tbl Ptr	TPtr
+//	t1		Round Constant	TRound
+//	t4		Block residual	LenResid
+//	t5		Residual Data	DTmp
+//
+//	{in,out}0	Block 0 Cycle	RotateM0
+//	{in,out}1	Block Value 12	M12
+//	{in,out}2	Block Value 8	M8
+//	{in,out}3	Block Value 4	M4
+//	{in,out}4	Block Value 0	M0
+//	{in,out}5	Block 1 Cycle	RotateM1
+//	{in,out}6	Block Value 13	M13
+//	{in,out}7	Block Value 9	M9
+//	{in,out}8	Block Value 5	M5
+//	{in,out}9	Block Value 1	M1
+//	{in,out}10	Block 2 Cycle	RotateM2
+//	{in,out}11	Block Value 14	M14
+//	{in,out}12	Block Value 10	M10
+//	{in,out}13	Block Value 6	M6
+//	{in,out}14	Block Value 2	M2
+//	{in,out}15	Block 3 Cycle	RotateM3
+//	{in,out}16	Block Value 15	M15
+//	{in,out}17	Block Value 11	M11
+//	{in,out}18	Block Value 7	M7
+//	{in,out}19	Block Value 3	M3
+//	{in,out}20	Scratch			Z
+//	{in,out}21	Scratch			Y
+//	{in,out}22	Scratch			X
+//	{in,out}23	Scratch			W
+//	{in,out}24	Digest A		A
+//	{in,out}25	Digest B		B
+//	{in,out}26	Digest C		C
+//	{in,out}27	Digest D		D
+//	{in,out}28	Active Data Ptr	DPtr
+//	in28		Dummy Value		-
+//	out28		Dummy Value		-
+//	bt0			Coroutine Link	QUICK_RTN
+//
+///	These predicates are used for computing the padding block(s) and
+///	are shared between the driver and digest co-routines
+//
+//	pt0			Extra Pad Block	pExtra
+//	pt1			Load next word	pLoad
+//	pt2			Skip next word	pSkip
+//	pt3			Search for Pad	pNoPad
+//	pt4			Pad Word 0		pPad0
+//	pt5			Pad Word 1		pPad1
+//	pt6			Pad Word 2		pPad2
+//	pt7			Pad Word 3		pPad3
+
+#define	DTmp		r19
+#define	LenResid	r18
+#define	QUICK_RTN	b6
+#define	TPtr		r14
+#define	TRound		r15
+#define	pExtra		p6
+#define	pLoad		p7
+#define	pNoPad		p9
+#define	pPad0		p10
+#define	pPad1		p11
+#define	pPad2		p12
+#define	pPad3		p13
+#define	pSkip		p8
+
+#define	A_		out24
+#define	B_		out25
+#define	C_		out26
+#define	D_		out27
+#define	DPtr_		out28
+#define	M0_		out4
+#define	M1_		out9
+#define	M10_		out12
+#define	M11_		out17
+#define	M12_		out1
+#define	M13_		out6
+#define	M14_		out11
+#define	M15_		out16
+#define	M2_		out14
+#define	M3_		out19
+#define	M4_		out3
+#define	M5_		out8
+#define	M6_		out13
+#define	M7_		out18
+#define	M8_		out2
+#define	M9_		out7
+#define	RotateM0_	out0
+#define	RotateM1_	out5
+#define	RotateM2_	out10
+#define	RotateM3_	out15
+#define	W_		out23
+#define	X_		out22
+#define	Y_		out21
+#define	Z_		out20
+
+#define	A		in24
+#define	B		in25
+#define	C		in26
+#define	D		in27
+#define	DPtr		in28
+#define	M0		in4
+#define	M1		in9
+#define	M10		in12
+#define	M11		in17
+#define	M12		in1
+#define	M13		in6
+#define	M14		in11
+#define	M15		in16
+#define	M2		in14
+#define	M3		in19
+#define	M4		in3
+#define	M5		in8
+#define	M6		in13
+#define	M7		in18
+#define	M8		in2
+#define	M9		in7
+#define	RotateM0	in0
+#define	RotateM1	in5
+#define	RotateM2	in10
+#define	RotateM3	in15
+#define	W		in23
+#define	X		in22
+#define	Y		in21
+#define	Z		in20
+
+/* register stack configuration for md5_block_asm_data_order(): */
+#define	MD5_NINP	3
+#define	MD5_NLOC	0
+#define MD5_NOUT	29
+#define MD5_NROT	0
+
+/* register stack configuration for helpers: */
+#define	_NINPUTS	MD5_NOUT
+#define	_NLOCALS	0
+#define _NOUTPUT	0
+#define	_NROTATE	24	/* this must be <= _NINPUTS */
+
+#if defined(_HPUX_SOURCE) && !defined(_LP64)
+#define	ADDP	addp4
+#else
+#define	ADDP	add
+#endif
+
+#if defined(_HPUX_SOURCE) || defined(B_ENDIAN)
+#define HOST_IS_BIG_ENDIAN
+#endif
+
+//	Macros for getting the left and right portions of little-endian words
+
+#define	GETLW(dst, src, align)	dep.z dst = src, 32 - 8 * align, 8 * align
+#define	GETRW(dst, src, align)	extr.u dst = src, 8 * align, 32 - 8 * align
+
+//	MD5 driver
+//
+//		Reads an input block, then calls the digest block
+//		subroutine and adds the results to the accumulated
+//		digest.  It allocates 32 outs which the subroutine
+//		uses as it's inputs and rotating
+//		registers. Initializes the round constant pointer and
+//		takes care of saving/restoring ar.lc
+//
+///	INPUT
+//
+//	in0		Context Ptr		CtxPtr0
+//	in1		Input Data Ptr		DPtrIn
+//	in2		Integral Blocks		BlockCount
+//	rp		Return Address		-
+//
+///	CODE
+//
+//	v2		Input Align		InAlign
+//	t0		Shared w/digest		-
+//	t1		Shared w/digest		-
+//	t2		Shared w/digest		-
+//	t3		Shared w/digest		-
+//	t4		Shared w/digest		-
+//	t5		Shared w/digest		-
+//	t6		PFS Save		PFSSave
+//	t7		ar.lc Save		LCSave
+//	t8		Saved PR		PRSave
+//	t9		2nd CtxPtr		CtxPtr1
+//	t10		Table Base		CTable
+//	t11		Table[0]		CTable0
+//	t13		Accumulator A		AccumA
+//	t14		Accumulator B		AccumB
+//	t15		Accumulator C		AccumC
+//	t16		Accumulator D		AccumD
+//	pt0		Shared w/digest		-
+//	pt1		Shared w/digest		-
+//	pt2		Shared w/digest		-
+//	pt3		Shared w/digest		-
+//	pt4		Shared w/digest		-
+//	pt5		Shared w/digest		-
+//	pt6		Shared w/digest		-
+//	pt7		Shared w/digest		-
+//	pt8		Not Aligned		pOff
+//	pt8		Blocks Left		pAgain
+
+#define	AccumA		r27
+#define	AccumB		r28
+#define	AccumC		r29
+#define	AccumD		r30
+#define	CTable		r24
+#define	CTable0		r25
+#define	CtxPtr0		in0
+#define	CtxPtr1		r23
+#define	DPtrIn		in1
+#define	BlockCount	in2
+#define	InAlign		r10
+#define	LCSave		r21
+#define	PFSSave		r20
+#define	PRSave		r22
+#define	pAgain		p63
+#define	pOff		p63
+
+	.text
+
+/* md5_block_asm_data_order(MD5_CTX *c, const void *data, size_t num)
+
+     where:
+      c: a pointer to a structure of this type:
+
+	   typedef struct MD5state_st
+	     {
+	       MD5_LONG A,B,C,D;
+	       MD5_LONG Nl,Nh;
+	       MD5_LONG data[MD5_LBLOCK];
+	       unsigned int num;
+	     }
+	   MD5_CTX;
+
+      data: a pointer to the input data (may be misaligned)
+      num:  the number of 16-byte blocks to hash (i.e., the length
+            of DATA is 16*NUM.
+
+   */
+
+	.type	md5_block_asm_data_order, @function
+	.global	md5_block_asm_data_order
+	.align	32
+	.proc	md5_block_asm_data_order
+md5_block_asm_data_order:
+.md5_block:
+	.prologue
+{	.mmi
+	.save	ar.pfs, PFSSave
+	alloc	PFSSave = ar.pfs, MD5_NINP, MD5_NLOC, MD5_NOUT, MD5_NROT
+	ADDP	CtxPtr1 = 8, CtxPtr0
+	mov	CTable = ip
+}
+{	.mmi
+	ADDP	DPtrIn = 0, DPtrIn
+	ADDP	CtxPtr0 = 0, CtxPtr0
+	.save	ar.lc, LCSave
+	mov	LCSave = ar.lc
+}
+;;
+{	.mmi
+	add	CTable = .md5_tbl_data_order#-.md5_block#, CTable
+	and	InAlign = 0x3, DPtrIn
+}
+
+{	.mmi
+	ld4	AccumA = [CtxPtr0], 4
+	ld4	AccumC = [CtxPtr1], 4
+	.save pr, PRSave
+	mov	PRSave = pr
+	.body
+}
+;;
+{	.mmi
+	ld4	AccumB = [CtxPtr0]
+	ld4	AccumD = [CtxPtr1]
+	dep	DPtr_ = 0, DPtrIn, 0, 2
+} ;;
+#ifdef HOST_IS_BIG_ENDIAN
+	rum	psr.be;;	// switch to little-endian
+#endif
+{	.mmb
+	ld4	CTable0 = [CTable], 4
+	cmp.ne	pOff, p0 = 0, InAlign
+(pOff)	br.cond.spnt.many .md5_unaligned
+} ;;
+
+//	The FF load/compute loop rotates values three times, so that
+//	loading into M12 here produces the M0 value, M13 -> M1, etc.
+
+.md5_block_loop0:
+{	.mmi
+	ld4	M12_ = [DPtr_], 4
+	mov	TPtr = CTable
+	mov	TRound = CTable0
+} ;;
+{	.mmi
+	ld4	M13_ = [DPtr_], 4
+	mov	A_ = AccumA
+	mov	B_ = AccumB
+} ;;
+{	.mmi
+	ld4	M14_ = [DPtr_], 4
+	mov	C_ = AccumC
+	mov	D_ = AccumD
+} ;;
+{	.mmb
+	ld4	M15_ = [DPtr_], 4
+	add	BlockCount = -1, BlockCount
+	br.call.sptk.many QUICK_RTN = md5_digest_block0
+} ;;
+
+//	Now, we add the new digest values and do some clean-up
+//	before checking if there's another full block to process
+
+{	.mmi
+	add	AccumA = AccumA, A_
+	add	AccumB = AccumB, B_
+	cmp.ne	pAgain, p0 = 0, BlockCount
+}
+{	.mib
+	add	AccumC = AccumC, C_
+	add	AccumD = AccumD, D_
+(pAgain) br.cond.dptk.many .md5_block_loop0
+} ;;
+
+.md5_exit:
+#ifdef HOST_IS_BIG_ENDIAN
+	sum	psr.be;;	// switch back to big-endian mode
+#endif
+{	.mmi
+	st4	[CtxPtr0] = AccumB, -4
+	st4	[CtxPtr1] = AccumD, -4
+	mov	pr = PRSave, 0x1ffff ;;
+}
+{	.mmi
+	st4	[CtxPtr0] = AccumA
+	st4	[CtxPtr1] = AccumC
+	mov	ar.lc = LCSave
+} ;;
+{	.mib
+	mov	ar.pfs = PFSSave
+	br.ret.sptk.few	rp
+} ;;
+
+#define	MD5UNALIGNED(offset)						\
+.md5_process##offset:							\
+{	.mib ;								\
+	nop	0x0	;						\
+	GETRW(DTmp, DTmp, offset) ;					\
+} ;;									\
+.md5_block_loop##offset:						\
+{	.mmi ;								\
+	ld4	Y_ = [DPtr_], 4 ;					\
+	mov	TPtr = CTable ;						\
+	mov	TRound = CTable0 ;					\
+} ;;									\
+{	.mmi ;								\
+	ld4	M13_ = [DPtr_], 4 ;					\
+	mov	A_ = AccumA ;						\
+	mov	B_ = AccumB ;						\
+} ;;									\
+{	.mii ;								\
+	ld4	M14_ = [DPtr_], 4 ;					\
+	GETLW(W_, Y_, offset) ;						\
+	mov	C_ = AccumC ;						\
+}									\
+{	.mmi ;								\
+	mov	D_ = AccumD ;;						\
+	or	M12_ = W_, DTmp ;					\
+	GETRW(DTmp, Y_, offset) ;					\
+}									\
+{	.mib ;								\
+	ld4	M15_ = [DPtr_], 4 ;					\
+	add	BlockCount = -1, BlockCount ;				\
+	br.call.sptk.many QUICK_RTN = md5_digest_block##offset;		\
+} ;;									\
+{	.mmi ;								\
+	add	AccumA = AccumA, A_ ;					\
+	add	AccumB = AccumB, B_ ;					\
+	cmp.ne	pAgain, p0 = 0, BlockCount ;				\
+}									\
+{	.mib ;								\
+	add	AccumC = AccumC, C_ ;					\
+	add	AccumD = AccumD, D_ ;					\
+(pAgain) br.cond.dptk.many .md5_block_loop##offset ;			\
+} ;;									\
+{	.mib ;								\
+	nop	0x0 ;							\
+	nop	0x0 ;							\
+	br.cond.sptk.many .md5_exit ;					\
+} ;;
+
+	.align	32
+.md5_unaligned:
+//
+//	Because variable shifts are expensive, we special case each of
+//	the four alignements. In practice, this won't hurt too much
+//	since only one working set of code will be loaded.
+//
+{	.mib
+	ld4	DTmp = [DPtr_], 4
+	cmp.eq	pOff, p0 = 1, InAlign
+(pOff)	br.cond.dpnt.many .md5_process1
+} ;;
+{	.mib
+	cmp.eq	pOff, p0 = 2, InAlign
+	nop	0x0
+(pOff)	br.cond.dpnt.many .md5_process2
+} ;;
+	MD5UNALIGNED(3)
+	MD5UNALIGNED(1)
+	MD5UNALIGNED(2)
+
+	.endp md5_block_asm_data_order
+
+
+// MD5 Perform the F function and load
+//
+// Passed the first 4 words (M0 - M3) and initial (A, B, C, D) values,
+// computes the FF() round of functions, then branches to the common
+// digest code to finish up with GG(), HH, and II().
+//
+// INPUT
+//
+// rp Return Address -
+//
+// CODE
+//
+// v0 PFS bit bucket PFS
+// v1 Loop Trip Count LTrip
+// pt0 Load next word pMore
+
+/* For F round: */
+#define LTrip	r9
+#define PFS	r8
+#define pMore	p6
+
+/* For GHI rounds: */
+#define T	r9
+#define U	r10
+#define V	r11
+
+#define COMPUTE(a, b, s, M, R)			\
+{						\
+	.mii ;					\
+	ld4 TRound = [TPtr], 4 ;		\
+	dep.z Y = Z, 32, 32 ;;			\
+	shrp Z = Z, Y, 64 - s ;			\
+} ;;						\
+{						\
+	.mmi ;					\
+	add a = Z, b ;				\
+	mov R = M ;				\
+	nop 0x0 ;				\
+} ;;
+
+#define LOOP(a, b, s, M, R, label)		\
+{	.mii ;					\
+	ld4 TRound = [TPtr], 4 ;		\
+	dep.z Y = Z, 32, 32 ;;			\
+	shrp Z = Z, Y, 64 - s ;			\
+} ;;						\
+{	.mib ;					\
+	add a = Z, b ;				\
+	mov R = M ;				\
+	br.ctop.sptk.many label ;		\
+} ;;
+
+// G(B, C, D) = (B & D) | (C & ~D)
+
+#define G(a, b, c, d, M)			\
+{	.mmi ;					\
+	add Z = M, TRound ;			\
+	and Y = b, d ;				\
+	andcm X = c, d ;			\
+} ;;						\
+{	.mii ;					\
+	add Z = Z, a ;				\
+	or Y = Y, X ;;				\
+	add Z = Z, Y ;				\
+} ;;
+
+// H(B, C, D) = B ^ C ^ D
+
+#define H(a, b, c, d, M)			\
+{	.mmi ;					\
+	add Z = M, TRound ;			\
+	xor Y = b, c ;				\
+	nop 0x0 ;				\
+} ;;						\
+{	.mii ;					\
+	add Z = Z, a ;				\
+	xor Y = Y, d ;;				\
+	add Z = Z, Y ;				\
+} ;;
+
+// I(B, C, D) = C ^ (B | ~D)
+//
+// However, since we have an andcm operator, we use the fact that
+//
+// Y ^ Z == ~Y ^ ~Z
+//
+// to rewrite the expression as
+//
+// I(B, C, D) = ~C ^ (~B & D)
+
+#define I(a, b, c, d, M)			\
+{	.mmi ;					\
+	add Z = M, TRound ;			\
+	andcm Y = d, b ;			\
+	andcm X = -1, c ;			\
+} ;;						\
+{	.mii ;					\
+	add Z = Z, a ;				\
+	xor Y = Y, X ;;				\
+	add Z = Z, Y ;				\
+} ;;
+
+#define GG4(label)				\
+	G(A, B, C, D, M0)			\
+	COMPUTE(A, B, 5, M0, RotateM0)		\
+	G(D, A, B, C, M1)			\
+	COMPUTE(D, A, 9, M1, RotateM1)		\
+	G(C, D, A, B, M2)			\
+	COMPUTE(C, D, 14, M2, RotateM2)		\
+	G(B, C, D, A, M3)			\
+	LOOP(B, C, 20, M3, RotateM3, label)
+
+#define HH4(label)				\
+	H(A, B, C, D, M0)			\
+	COMPUTE(A, B, 4, M0, RotateM0)		\
+	H(D, A, B, C, M1)			\
+	COMPUTE(D, A, 11, M1, RotateM1)		\
+	H(C, D, A, B, M2)			\
+	COMPUTE(C, D, 16, M2, RotateM2)		\
+	H(B, C, D, A, M3)			\
+	LOOP(B, C, 23, M3, RotateM3, label)
+
+#define II4(label)				\
+	I(A, B, C, D, M0)			\
+	COMPUTE(A, B, 6, M0, RotateM0)		\
+	I(D, A, B, C, M1)			\
+	COMPUTE(D, A, 10, M1, RotateM1)		\
+	I(C, D, A, B, M2)			\
+	COMPUTE(C, D, 15, M2, RotateM2)		\
+	I(B, C, D, A, M3)			\
+	LOOP(B, C, 21, M3, RotateM3, label)
+
+#define FFLOAD(a, b, c, d, M, N, s)		\
+{	.mii ;					\
+(pMore) ld4 N = [DPtr], 4 ;			\
+	add Z = M, TRound ;			\
+	and Y = c, b ;				\
+}						\
+{	.mmi ;					\
+	andcm X = d, b ;;			\
+	add Z = Z, a ;				\
+	or Y = Y, X ;				\
+} ;;						\
+{	.mii ;					\
+	ld4 TRound = [TPtr], 4 ;		\
+	add Z = Z, Y ;;				\
+	dep.z Y = Z, 32, 32 ;			\
+} ;;						\
+{	.mii ;					\
+	nop 0x0 ;				\
+	shrp Z = Z, Y, 64 - s ;;		\
+	add a = Z, b ;				\
+} ;;
+
+#define FFLOOP(a, b, c, d, M, N, s, dest)	\
+{	.mii ;					\
+(pMore)	ld4 N = [DPtr], 4 ;			\
+	add Z = M, TRound ;			\
+	and Y = c, b ;				\
+}						\
+{	.mmi ;					\
+	andcm X = d, b ;;			\
+	add Z = Z, a ;				\
+	or Y = Y, X ;				\
+} ;;						\
+{	.mii ;					\
+	ld4 TRound = [TPtr], 4 ;		\
+	add Z = Z, Y ;;				\
+	dep.z Y = Z, 32, 32 ;			\
+} ;;						\
+{	.mii ;					\
+	nop 0x0 ;				\
+	shrp Z = Z, Y, 64 - s ;;		\
+	add a = Z, b ;				\
+}						\
+{	.mib ;					\
+	cmp.ne pMore, p0 = 0, LTrip ;		\
+	add LTrip = -1, LTrip ;			\
+	br.ctop.dptk.many dest ;		\
+} ;;
+
+	.type md5_digest_block0, @function
+	.align 32
+
+	.proc md5_digest_block0
+	.prologue
+md5_digest_block0:
+	.altrp QUICK_RTN
+	.body
+{	.mmi
+	alloc PFS = ar.pfs, _NINPUTS, _NLOCALS, _NOUTPUT, _NROTATE
+	mov LTrip = 2
+	mov ar.lc = 3
+} ;;
+{	.mii
+	cmp.eq pMore, p0 = r0, r0
+	mov ar.ec = 0
+	nop 0x0
+} ;;
+
+.md5_FF_round0:
+	FFLOAD(A, B, C, D, M12, RotateM0, 7)
+	FFLOAD(D, A, B, C, M13, RotateM1, 12)
+	FFLOAD(C, D, A, B, M14, RotateM2, 17)
+	FFLOOP(B, C, D, A, M15, RotateM3, 22, .md5_FF_round0)
+	//
+	// !!! Fall through to md5_digest_GHI
+	//
+	.endp md5_digest_block0
+
+	.type md5_digest_GHI, @function
+	.align 32
+
+	.proc md5_digest_GHI
+	.prologue
+	.regstk _NINPUTS, _NLOCALS, _NOUTPUT, _NROTATE
+md5_digest_GHI:
+	.altrp QUICK_RTN
+	.body
+//
+// The following sequence shuffles the block counstants round for the
+// next round:
+//
+// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
+// 1 6 11 0 5 10 14 4 9 14 3 8 13 2 7 12
+//
+{	.mmi
+	mov Z = M0
+	mov Y = M15
+	mov ar.lc = 3
+}
+{	.mmi
+	mov X = M2
+	mov W = M9
+	mov V = M4
+} ;;
+
+{	.mmi
+	mov M0 = M1
+	mov M15 = M12
+	mov ar.ec = 1
+}
+{	.mmi
+	mov M2 = M11
+	mov M9 = M14
+	mov M4 = M5
+} ;;
+
+{	.mmi
+	mov M1 = M6
+	mov M12 = M13
+	mov U = M3
+}
+{	.mmi
+	mov M11 = M8
+	mov M14 = M7
+	mov M5 = M10
+} ;;
+
+{	.mmi
+	mov M6 = Y
+	mov M13 = X
+	mov M3 = Z
+}
+{	.mmi
+	mov M8 = W
+	mov M7 = V
+	mov M10 = U
+} ;;
+
+.md5_GG_round:
+	GG4(.md5_GG_round)
+
+// The following sequence shuffles the block constants round for the
+// next round:
+//
+// 1 6 11 0 5 10 14 4 9 14 3 8 13 2 7 12
+// 5 8 11 14 1 4 7 10 13 0 3 6 9 12 15 2
+
+{	.mmi
+	mov Z = M0
+	mov Y = M1
+	mov ar.lc = 3
+}
+{	.mmi
+	mov X = M3
+	mov W = M5
+	mov V = M6
+} ;;
+
+{	.mmi
+	mov M0 = M4
+	mov M1 = M11
+	mov ar.ec = 1
+}
+{	.mmi
+	mov M3 = M9
+	mov U = M8
+	mov T = M13
+} ;;
+
+{	.mmi
+	mov M4 = Z
+	mov M11 = Y
+	mov M5 = M7
+}
+{	.mmi
+	mov M6 = M14
+	mov M8 = M12
+	mov M13 = M15
+} ;;
+
+{	.mmi
+	mov M7 = W
+	mov M14 = V
+	nop 0x0
+}
+{	.mmi
+	mov M9 = X
+	mov M12 = U
+	mov M15 = T
+} ;;
+
+.md5_HH_round:
+	HH4(.md5_HH_round)
+
+// The following sequence shuffles the block constants round for the
+// next round:
+//
+// 5 8 11 14 1 4 7 10 13 0 3 6 9 12 15 2
+// 0 7 14 5 12 3 10 1 8 15 6 13 4 11 2 9
+
+{	.mmi
+	mov Z = M0
+	mov Y = M15
+	mov ar.lc = 3
+}
+{	.mmi
+	mov X = M10
+	mov W = M1
+	mov V = M4
+} ;;
+
+{	.mmi
+	mov M0 = M9
+	mov M15 = M12
+	mov ar.ec = 1
+}
+{	.mmi
+	mov M10 = M11
+	mov M1 = M6
+	mov M4 = M13
+} ;;
+
+{	.mmi
+	mov M9 = M14
+	mov M12 = M5
+	mov U = M3
+}
+{	.mmi
+	mov M11 = M8
+	mov M6 = M7
+	mov M13 = M2
+} ;;
+
+{	.mmi
+	mov M14 = Y
+	mov M5 = X
+	mov M3 = Z
+}
+{	.mmi
+	mov M8 = W
+	mov M7 = V
+	mov M2 = U
+} ;;
+
+.md5_II_round:
+	II4(.md5_II_round)
+
+{	.mib
+	nop 0x0
+	nop 0x0
+	br.ret.sptk.many QUICK_RTN
+} ;;
+
+	.endp md5_digest_GHI
+
+#define FFLOADU(a, b, c, d, M, P, N, s, offset)	\
+{	.mii ;					\
+(pMore) ld4 N = [DPtr], 4 ;			\
+	add Z = M, TRound ;			\
+	and Y = c, b ;				\
+}						\
+{	.mmi ;					\
+	andcm X = d, b ;;			\
+	add Z = Z, a ;				\
+	or Y = Y, X ;				\
+} ;;						\
+{	.mii ;					\
+	ld4 TRound = [TPtr], 4 ;		\
+	GETLW(W, P, offset) ;			\
+	add Z = Z, Y ;				\
+} ;;						\
+{	.mii ;					\
+	or W = W, DTmp ;			\
+	dep.z Y = Z, 32, 32 ;;			\
+	shrp Z = Z, Y, 64 - s ;			\
+} ;;						\
+{	.mii ;					\
+	add a = Z, b ;				\
+	GETRW(DTmp, P, offset) ;		\
+	mov P = W ;				\
+} ;;
+
+#define FFLOOPU(a, b, c, d, M, P, N, s, offset)		\
+{	.mii ;						\
+(pMore) ld4 N = [DPtr], 4 ;				\
+	add Z = M, TRound ;				\
+	and Y = c, b ;					\
+}							\
+{	.mmi ;						\
+	andcm X = d, b ;;				\
+	add Z = Z, a ;					\
+	or Y = Y, X ;					\
+} ;;							\
+{	.mii ;						\
+	ld4 TRound = [TPtr], 4 ;			\
+(pMore) GETLW(W, P, offset) 	;			\
+	add Z = Z, Y ;					\
+} ;;							\
+{	.mii ;						\
+(pMore) or W = W, DTmp ;				\
+	dep.z Y = Z, 32, 32 ;;				\
+	shrp Z = Z, Y, 64 - s ;				\
+} ;;							\
+{	.mii ;						\
+	add a = Z, b ;					\
+(pMore) GETRW(DTmp, P, offset) 	;			\
+(pMore) mov P = W ;					\
+}							\
+{	.mib ;						\
+	cmp.ne pMore, p0 = 0, LTrip ;			\
+	add LTrip = -1, LTrip ;				\
+	br.ctop.sptk.many .md5_FF_round##offset ;	\
+} ;;
+
+#define MD5FBLOCK(offset)						\
+	.type md5_digest_block##offset, @function ;			\
+									\
+	.align 32 ;							\
+	.proc md5_digest_block##offset ;				\
+	.prologue ;							\
+	.altrp QUICK_RTN ;						\
+	.body ;								\
+md5_digest_block##offset:						\
+{	.mmi ;								\
+	alloc PFS = ar.pfs, _NINPUTS, _NLOCALS, _NOUTPUT, _NROTATE ;	\
+	mov LTrip = 2 ;							\
+	mov ar.lc = 3 ;							\
+} ;;									\
+{	.mii ;								\
+	cmp.eq pMore, p0 = r0, r0 ;					\
+	mov ar.ec = 0 ;							\
+	nop 0x0 ;							\
+} ;;									\
+									\
+	.pred.rel "mutex", pLoad, pSkip ;				\
+.md5_FF_round##offset:							\
+	FFLOADU(A, B, C, D, M12, M13, RotateM0, 7, offset)		\
+	FFLOADU(D, A, B, C, M13, M14, RotateM1, 12, offset)		\
+	FFLOADU(C, D, A, B, M14, M15, RotateM2, 17, offset)		\
+	FFLOOPU(B, C, D, A, M15, RotateM0, RotateM3, 22, offset)	\
+									\
+{	.mib ;								\
+	nop 0x0 ;							\
+	nop 0x0 ;							\
+	br.cond.sptk.many md5_digest_GHI ;				\
+} ;;									\
+	.endp md5_digest_block##offset
+
+MD5FBLOCK(1)
+MD5FBLOCK(2)
+MD5FBLOCK(3)
+
+	.align 64
+	.type md5_constants, @object
+md5_constants:
+.md5_tbl_data_order:			// To ensure little-endian data
+					// order, code as bytes.
+	data1 0x78, 0xa4, 0x6a, 0xd7	//     0
+	data1 0x56, 0xb7, 0xc7, 0xe8	//     1
+	data1 0xdb, 0x70, 0x20, 0x24	//     2
+	data1 0xee, 0xce, 0xbd, 0xc1	//     3
+	data1 0xaf, 0x0f, 0x7c, 0xf5	//     4
+	data1 0x2a, 0xc6, 0x87, 0x47	//     5
+	data1 0x13, 0x46, 0x30, 0xa8	//     6
+	data1 0x01, 0x95, 0x46, 0xfd	//     7
+	data1 0xd8, 0x98, 0x80, 0x69	//     8
+	data1 0xaf, 0xf7, 0x44, 0x8b	//     9
+	data1 0xb1, 0x5b, 0xff, 0xff	//    10
+	data1 0xbe, 0xd7, 0x5c, 0x89	//    11
+	data1 0x22, 0x11, 0x90, 0x6b	//    12
+	data1 0x93, 0x71, 0x98, 0xfd	//    13
+	data1 0x8e, 0x43, 0x79, 0xa6	//    14
+	data1 0x21, 0x08, 0xb4, 0x49	//    15
+	data1 0x62, 0x25, 0x1e, 0xf6	//    16
+	data1 0x40, 0xb3, 0x40, 0xc0	//    17
+	data1 0x51, 0x5a, 0x5e, 0x26	//    18
+	data1 0xaa, 0xc7, 0xb6, 0xe9	//    19
+	data1 0x5d, 0x10, 0x2f, 0xd6	//    20
+	data1 0x53, 0x14, 0x44, 0x02	//    21
+	data1 0x81, 0xe6, 0xa1, 0xd8	//    22
+	data1 0xc8, 0xfb, 0xd3, 0xe7	//    23
+	data1 0xe6, 0xcd, 0xe1, 0x21	//    24
+	data1 0xd6, 0x07, 0x37, 0xc3	//    25
+	data1 0x87, 0x0d, 0xd5, 0xf4	//    26
+	data1 0xed, 0x14, 0x5a, 0x45	//    27
+	data1 0x05, 0xe9, 0xe3, 0xa9	//    28
+	data1 0xf8, 0xa3, 0xef, 0xfc	//    29
+	data1 0xd9, 0x02, 0x6f, 0x67	//    30
+	data1 0x8a, 0x4c, 0x2a, 0x8d	//    31
+	data1 0x42, 0x39, 0xfa, 0xff	//    32
+	data1 0x81, 0xf6, 0x71, 0x87	//    33
+	data1 0x22, 0x61, 0x9d, 0x6d	//    34
+	data1 0x0c, 0x38, 0xe5, 0xfd	//    35
+	data1 0x44, 0xea, 0xbe, 0xa4	//    36
+	data1 0xa9, 0xcf, 0xde, 0x4b	//    37
+	data1 0x60, 0x4b, 0xbb, 0xf6	//    38
+	data1 0x70, 0xbc, 0xbf, 0xbe	//    39
+	data1 0xc6, 0x7e, 0x9b, 0x28	//    40
+	data1 0xfa, 0x27, 0xa1, 0xea	//    41
+	data1 0x85, 0x30, 0xef, 0xd4	//    42
+	data1 0x05, 0x1d, 0x88, 0x04	//    43
+	data1 0x39, 0xd0, 0xd4, 0xd9	//    44
+	data1 0xe5, 0x99, 0xdb, 0xe6	//    45
+	data1 0xf8, 0x7c, 0xa2, 0x1f	//    46
+	data1 0x65, 0x56, 0xac, 0xc4	//    47
+	data1 0x44, 0x22, 0x29, 0xf4	//    48
+	data1 0x97, 0xff, 0x2a, 0x43	//    49
+	data1 0xa7, 0x23, 0x94, 0xab	//    50
+	data1 0x39, 0xa0, 0x93, 0xfc	//    51
+	data1 0xc3, 0x59, 0x5b, 0x65	//    52
+	data1 0x92, 0xcc, 0x0c, 0x8f	//    53
+	data1 0x7d, 0xf4, 0xef, 0xff	//    54
+	data1 0xd1, 0x5d, 0x84, 0x85	//    55
+	data1 0x4f, 0x7e, 0xa8, 0x6f	//    56
+	data1 0xe0, 0xe6, 0x2c, 0xfe	//    57
+	data1 0x14, 0x43, 0x01, 0xa3	//    58
+	data1 0xa1, 0x11, 0x08, 0x4e	//    59
+	data1 0x82, 0x7e, 0x53, 0xf7	//    60
+	data1 0x35, 0xf2, 0x3a, 0xbd	//    61
+	data1 0xbb, 0xd2, 0xd7, 0x2a	//    62
+	data1 0x91, 0xd3, 0x86, 0xeb	//    63
+.size	md5_constants#,64*4
diff --git a/crypto/md5/asm/md5-sparcv9.pl b/crypto/md5/asm/md5-sparcv9.pl
new file mode 100644
index 000000000..09e6d7139
--- /dev/null
+++ b/crypto/md5/asm/md5-sparcv9.pl
@@ -0,0 +1,437 @@
+#! /usr/bin/env perl
+# Copyright 2012-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+
+# ====================================================================
+# Written by Andy Polyakov  for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+#
+# Hardware SPARC T4 support by David S. Miller .
+# ====================================================================
+
+# MD5 for SPARCv9, 6.9 cycles per byte on UltraSPARC, >40% faster than
+# code generated by Sun C 5.2.
+
+# SPARC T4 MD5 hardware achieves 3.20 cycles per byte, which is 2.1x
+# faster than software. Multi-process benchmark saturates at 12x
+# single-process result on 8-core processor, or ~11GBps per 2.85GHz
+# socket.
+
+$output=pop;
+open STDOUT,">$output";
+
+use integer;
+
+($ctx,$inp,$len)=("%i0","%i1","%i2");	# input arguments
+
+# 64-bit values
+@X=("%o0","%o1","%o2","%o3","%o4","%o5","%o7","%g1","%g2");
+$tx="%g3";
+($AB,$CD)=("%g4","%g5");
+
+# 32-bit values
+@V=($A,$B,$C,$D)=map("%l$_",(0..3));
+($t1,$t2,$t3,$saved_asi)=map("%l$_",(4..7));
+($shr,$shl1,$shl2)=("%i3","%i4","%i5");
+
+my @K=(	0xd76aa478,0xe8c7b756,0x242070db,0xc1bdceee,
+	0xf57c0faf,0x4787c62a,0xa8304613,0xfd469501,
+	0x698098d8,0x8b44f7af,0xffff5bb1,0x895cd7be,
+	0x6b901122,0xfd987193,0xa679438e,0x49b40821,
+
+	0xf61e2562,0xc040b340,0x265e5a51,0xe9b6c7aa,
+	0xd62f105d,0x02441453,0xd8a1e681,0xe7d3fbc8,
+	0x21e1cde6,0xc33707d6,0xf4d50d87,0x455a14ed,
+	0xa9e3e905,0xfcefa3f8,0x676f02d9,0x8d2a4c8a,
+
+	0xfffa3942,0x8771f681,0x6d9d6122,0xfde5380c,
+	0xa4beea44,0x4bdecfa9,0xf6bb4b60,0xbebfbc70,
+	0x289b7ec6,0xeaa127fa,0xd4ef3085,0x04881d05,
+	0xd9d4d039,0xe6db99e5,0x1fa27cf8,0xc4ac5665,
+
+	0xf4292244,0x432aff97,0xab9423a7,0xfc93a039,
+	0x655b59c3,0x8f0ccc92,0xffeff47d,0x85845dd1,
+	0x6fa87e4f,0xfe2ce6e0,0xa3014314,0x4e0811a1,
+	0xf7537e82,0xbd3af235,0x2ad7d2bb,0xeb86d391, 0	);
+
+sub R0 {
+  my ($i,$a,$b,$c,$d) = @_;
+  my $rot = (7,12,17,22)[$i%4];
+  my $j   = ($i+1)/2;
+
+  if ($i&1) {
+    $code.=<<___;
+	 srlx	@X[$j],$shr,@X[$j]	! align X[`$i+1`]
+	and	$b,$t1,$t1		! round $i
+	 sllx	@X[$j+1],$shl1,$tx
+	add	$t2,$a,$a
+	 sllx	$tx,$shl2,$tx
+	xor	$d,$t1,$t1
+	 or	$tx,@X[$j],@X[$j]
+	 sethi	%hi(@K[$i+1]),$t2
+	add	$t1,$a,$a
+	 or	$t2,%lo(@K[$i+1]),$t2
+	sll	$a,$rot,$t3
+	 add	@X[$j],$t2,$t2		! X[`$i+1`]+K[`$i+1`]
+	srl	$a,32-$rot,$a
+	add	$b,$t3,$t3
+	 xor	 $b,$c,$t1
+	add	$t3,$a,$a
+___
+  } else {
+    $code.=<<___;
+	 srlx	@X[$j],32,$tx		! extract X[`2*$j+1`]
+	and	$b,$t1,$t1		! round $i
+	add	$t2,$a,$a
+	xor	$d,$t1,$t1
+	 sethi	%hi(@K[$i+1]),$t2
+	add	$t1,$a,$a
+	 or	$t2,%lo(@K[$i+1]),$t2
+	sll	$a,$rot,$t3
+	 add	$tx,$t2,$t2		! X[`2*$j+1`]+K[`$i+1`]
+	srl	$a,32-$rot,$a
+	add	$b,$t3,$t3
+	 xor	 $b,$c,$t1
+	add	$t3,$a,$a
+___
+  }
+}
+
+sub R0_1 {
+  my ($i,$a,$b,$c,$d) = @_;
+  my $rot = (7,12,17,22)[$i%4];
+
+$code.=<<___;
+	 srlx	@X[0],32,$tx		! extract X[1]
+	and	$b,$t1,$t1		! round $i
+	add	$t2,$a,$a
+	xor	$d,$t1,$t1
+	 sethi	%hi(@K[$i+1]),$t2
+	add	$t1,$a,$a
+	 or	$t2,%lo(@K[$i+1]),$t2
+	sll	$a,$rot,$t3
+	 add	$tx,$t2,$t2		! X[1]+K[`$i+1`]
+	srl	$a,32-$rot,$a
+	add	$b,$t3,$t3
+	 andn	 $b,$c,$t1
+	add	$t3,$a,$a
+___
+}
+
+sub R1 {
+  my ($i,$a,$b,$c,$d) = @_;
+  my $rot = (5,9,14,20)[$i%4];
+  my $j   = $i<31 ? (1+5*($i+1))%16 : (5+3*($i+1))%16;
+  my $xi  = @X[$j/2];
+
+$code.=<<___ if ($j&1 && ($xi=$tx));
+	 srlx	@X[$j/2],32,$xi		! extract X[$j]
+___
+$code.=<<___;
+	and	$b,$d,$t3		! round $i
+	add	$t2,$a,$a
+	or	$t3,$t1,$t1
+	 sethi	%hi(@K[$i+1]),$t2
+	add	$t1,$a,$a
+	 or	$t2,%lo(@K[$i+1]),$t2
+	sll	$a,$rot,$t3
+	 add	$xi,$t2,$t2		! X[$j]+K[`$i+1`]
+	srl	$a,32-$rot,$a
+	add	$b,$t3,$t3
+	 `$i<31?"andn":"xor"`	 $b,$c,$t1
+	add	$t3,$a,$a
+___
+}
+
+sub R2 {
+  my ($i,$a,$b,$c,$d) = @_;
+  my $rot = (4,11,16,23)[$i%4];
+  my $j   = $i<47 ? (5+3*($i+1))%16 : (0+7*($i+1))%16;
+  my $xi  = @X[$j/2];
+
+$code.=<<___ if ($j&1 && ($xi=$tx));
+	 srlx	@X[$j/2],32,$xi		! extract X[$j]
+___
+$code.=<<___;
+	add	$t2,$a,$a		! round $i
+	xor	$b,$t1,$t1
+	 sethi	%hi(@K[$i+1]),$t2
+	add	$t1,$a,$a
+	 or	$t2,%lo(@K[$i+1]),$t2
+	sll	$a,$rot,$t3
+	 add	$xi,$t2,$t2		! X[$j]+K[`$i+1`]
+	srl	$a,32-$rot,$a
+	add	$b,$t3,$t3
+	 xor	 $b,$c,$t1
+	add	$t3,$a,$a
+___
+}
+
+sub R3 {
+  my ($i,$a,$b,$c,$d) = @_;
+  my $rot = (6,10,15,21)[$i%4];
+  my $j   = (0+7*($i+1))%16;
+  my $xi  = @X[$j/2];
+
+$code.=<<___;
+	add	$t2,$a,$a		! round $i
+___
+$code.=<<___ if ($j&1 && ($xi=$tx));
+	 srlx	@X[$j/2],32,$xi		! extract X[$j]
+___
+$code.=<<___;
+	orn	$b,$d,$t1
+	 sethi	%hi(@K[$i+1]),$t2
+	xor	$c,$t1,$t1
+	 or	$t2,%lo(@K[$i+1]),$t2
+	add	$t1,$a,$a
+	sll	$a,$rot,$t3
+	 add	$xi,$t2,$t2		! X[$j]+K[`$i+1`]
+	srl	$a,32-$rot,$a
+	add	$b,$t3,$t3
+	add	$t3,$a,$a
+___
+}
+
+$code.=<<___;
+#include "sparc_arch.h"
+
+#ifdef __arch64__
+.register	%g2,#scratch
+.register	%g3,#scratch
+#endif
+
+.section	".text",#alloc,#execinstr
+
+#ifdef __PIC__
+SPARC_PIC_THUNK(%g1)
+#endif
+
+.globl	md5_block_asm_data_order
+.align	32
+md5_block_asm_data_order:
+	SPARC_LOAD_ADDRESS_LEAF(OPENSSL_sparcv9cap_P,%g1,%g5)
+	ld	[%g1+4],%g1		! OPENSSL_sparcv9cap_P[1]
+
+	andcc	%g1, CFR_MD5, %g0
+	be	.Lsoftware
+	nop
+
+	mov	4, %g1
+	andcc	%o1, 0x7, %g0
+	lda	[%o0 + %g0]0x88, %f0		! load context
+	lda	[%o0 + %g1]0x88, %f1
+	add	%o0, 8, %o0
+	lda	[%o0 + %g0]0x88, %f2
+	lda	[%o0 + %g1]0x88, %f3
+	bne,pn	%icc, .Lhwunaligned
+	sub	%o0, 8, %o0
+
+.Lhw_loop:
+	ldd	[%o1 + 0x00], %f8
+	ldd	[%o1 + 0x08], %f10
+	ldd	[%o1 + 0x10], %f12
+	ldd	[%o1 + 0x18], %f14
+	ldd	[%o1 + 0x20], %f16
+	ldd	[%o1 + 0x28], %f18
+	ldd	[%o1 + 0x30], %f20
+	subcc	%o2, 1, %o2		! done yet? 
+	ldd	[%o1 + 0x38], %f22
+	add	%o1, 0x40, %o1
+	prefetch [%o1 + 63], 20
+
+	.word	0x81b02800		! MD5
+
+	bne,pt	SIZE_T_CC, .Lhw_loop
+	nop
+
+.Lhwfinish:
+	sta	%f0, [%o0 + %g0]0x88	! store context
+	sta	%f1, [%o0 + %g1]0x88
+	add	%o0, 8, %o0
+	sta	%f2, [%o0 + %g0]0x88
+	sta	%f3, [%o0 + %g1]0x88
+	retl
+	nop
+
+.align	8
+.Lhwunaligned:
+	alignaddr %o1, %g0, %o1
+
+	ldd	[%o1 + 0x00], %f10
+.Lhwunaligned_loop:
+	ldd	[%o1 + 0x08], %f12
+	ldd	[%o1 + 0x10], %f14
+	ldd	[%o1 + 0x18], %f16
+	ldd	[%o1 + 0x20], %f18
+	ldd	[%o1 + 0x28], %f20
+	ldd	[%o1 + 0x30], %f22
+	ldd	[%o1 + 0x38], %f24
+	subcc	%o2, 1, %o2		! done yet?
+	ldd	[%o1 + 0x40], %f26
+	add	%o1, 0x40, %o1
+	prefetch [%o1 + 63], 20
+
+	faligndata %f10, %f12, %f8
+	faligndata %f12, %f14, %f10
+	faligndata %f14, %f16, %f12
+	faligndata %f16, %f18, %f14
+	faligndata %f18, %f20, %f16
+	faligndata %f20, %f22, %f18
+	faligndata %f22, %f24, %f20
+	faligndata %f24, %f26, %f22
+
+	.word	0x81b02800		! MD5
+
+	bne,pt	SIZE_T_CC, .Lhwunaligned_loop
+	for	%f26, %f26, %f10	! %f10=%f26
+
+	ba	.Lhwfinish
+	nop
+
+.align	16
+.Lsoftware:
+	save	%sp,-STACK_FRAME,%sp
+
+	rd	%asi,$saved_asi
+	wr	%g0,0x88,%asi		! ASI_PRIMARY_LITTLE
+	and	$inp,7,$shr
+	andn	$inp,7,$inp
+
+	sll	$shr,3,$shr		! *=8
+	mov	56,$shl2
+	ld	[$ctx+0],$A
+	sub	$shl2,$shr,$shl2
+	ld	[$ctx+4],$B
+	and	$shl2,32,$shl1
+	add	$shl2,8,$shl2
+	ld	[$ctx+8],$C
+	sub	$shl2,$shl1,$shl2	! shr+shl1+shl2==64
+	ld	[$ctx+12],$D
+	nop
+
+.Loop:
+	 cmp	$shr,0			! was inp aligned?
+	ldxa	[$inp+0]%asi,@X[0]	! load little-endian input
+	ldxa	[$inp+8]%asi,@X[1]
+	ldxa	[$inp+16]%asi,@X[2]
+	ldxa	[$inp+24]%asi,@X[3]
+	ldxa	[$inp+32]%asi,@X[4]
+	 sllx	$A,32,$AB		! pack A,B
+	ldxa	[$inp+40]%asi,@X[5]
+	 sllx	$C,32,$CD		! pack C,D
+	ldxa	[$inp+48]%asi,@X[6]
+	 or	$B,$AB,$AB
+	ldxa	[$inp+56]%asi,@X[7]
+	 or	$D,$CD,$CD
+	bnz,a,pn	%icc,.+8
+	ldxa	[$inp+64]%asi,@X[8]
+
+	srlx	@X[0],$shr,@X[0]	! align X[0]
+	sllx	@X[1],$shl1,$tx
+	 sethi	%hi(@K[0]),$t2
+	sllx	$tx,$shl2,$tx
+	 or	$t2,%lo(@K[0]),$t2
+	or	$tx,@X[0],@X[0]
+	 xor	$C,$D,$t1
+	 add	@X[0],$t2,$t2		! X[0]+K[0]
+___
+	for ($i=0;$i<15;$i++)	{ &R0($i,@V);	unshift(@V,pop(@V)); }
+	for (;$i<16;$i++)	{ &R0_1($i,@V);	unshift(@V,pop(@V)); }
+	for (;$i<32;$i++)	{ &R1($i,@V);	unshift(@V,pop(@V)); }
+	for (;$i<48;$i++)	{ &R2($i,@V);	unshift(@V,pop(@V)); }
+	for (;$i<64;$i++)	{ &R3($i,@V);	unshift(@V,pop(@V)); }
+$code.=<<___;
+	srlx	$AB,32,$t1		! unpack A,B,C,D and accumulate
+	add	$inp,64,$inp		! advance inp
+	srlx	$CD,32,$t2
+	add	$t1,$A,$A
+	subcc	$len,1,$len		! done yet?
+	add	$AB,$B,$B
+	add	$t2,$C,$C
+	add	$CD,$D,$D
+	srl	$B,0,$B			! clruw	$B
+	bne	SIZE_T_CC,.Loop
+	srl	$D,0,$D			! clruw	$D
+
+	st	$A,[$ctx+0]		! write out ctx
+	st	$B,[$ctx+4]
+	st	$C,[$ctx+8]
+	st	$D,[$ctx+12]
+
+	wr	%g0,$saved_asi,%asi
+	ret
+	restore
+.type	md5_block_asm_data_order,#function
+.size	md5_block_asm_data_order,(.-md5_block_asm_data_order)
+
+.asciz	"MD5 block transform for SPARCv9, CRYPTOGAMS by "
+.align	4
+___
+
+# Purpose of these subroutines is to explicitly encode VIS instructions,
+# so that one can compile the module without having to specify VIS
+# extensions on compiler command line, e.g. -xarch=v9 vs. -xarch=v9a.
+# Idea is to reserve for option to produce "universal" binary and let
+# programmer detect if current CPU is VIS capable at run-time.
+sub unvis {
+my ($mnemonic,$rs1,$rs2,$rd)=@_;
+my $ref,$opf;
+my %visopf = (	"faligndata"	=> 0x048,
+		"for"		=> 0x07c	);
+
+    $ref = "$mnemonic\t$rs1,$rs2,$rd";
+
+    if ($opf=$visopf{$mnemonic}) {
+	foreach ($rs1,$rs2,$rd) {
+	    return $ref if (!/%f([0-9]{1,2})/);
+	    $_=$1;
+	    if ($1>=32) {
+		return $ref if ($1&1);
+		# re-encode for upper double register addressing
+		$_=($1|$1>>5)&31;
+	    }
+	}
+
+	return	sprintf ".word\t0x%08x !%s",
+			0x81b00000|$rd<<25|$rs1<<14|$opf<<5|$rs2,
+			$ref;
+    } else {
+	return $ref;
+    }
+}
+sub unalignaddr {
+my ($mnemonic,$rs1,$rs2,$rd)=@_;
+my %bias = ( "g" => 0, "o" => 8, "l" => 16, "i" => 24 );
+my $ref="$mnemonic\t$rs1,$rs2,$rd";
+
+    foreach ($rs1,$rs2,$rd) {
+	if (/%([goli])([0-7])/)	{ $_=$bias{$1}+$2; }
+	else			{ return $ref; }
+    }
+    return  sprintf ".word\t0x%08x !%s",
+		    0x81b00300|$rd<<25|$rs1<<14|$rs2,
+		    $ref;
+}
+
+foreach (split("\n",$code)) {
+	s/\`([^\`]*)\`/eval $1/ge;
+
+	s/\b(f[^\s]*)\s+(%f[0-9]{1,2}),\s*(%f[0-9]{1,2}),\s*(%f[0-9]{1,2})/
+		&unvis($1,$2,$3,$4)
+	 /ge;
+	s/\b(alignaddr)\s+(%[goli][0-7]),\s*(%[goli][0-7]),\s*(%[goli][0-7])/
+		&unalignaddr($1,$2,$3,$4)
+	 /ge;
+
+	print $_,"\n";
+}
+
+close STDOUT;
diff --git a/crypto/md5/asm/md5-x86_64.pl b/crypto/md5/asm/md5-x86_64.pl
new file mode 100755
index 000000000..3f656dc0b
--- /dev/null
+++ b/crypto/md5/asm/md5-x86_64.pl
@@ -0,0 +1,380 @@
+#! /usr/bin/env perl
+# Author: Marc Bevand 
+# Copyright 2005-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+# MD5 optimized for AMD64.
+
+use strict;
+
+my $code;
+
+# round1_step() does:
+#   dst = x + ((dst + F(x,y,z) + X[k] + T_i) <<< s)
+#   %r10d = X[k_next]
+#   %r11d = z' (copy of z for the next step)
+# Each round1_step() takes about 5.3 clocks (9 instructions, 1.7 IPC)
+sub round1_step
+{
+    my ($pos, $dst, $x, $y, $z, $k_next, $T_i, $s) = @_;
+    $code .= " mov	0*4(%rsi),	%r10d		/* (NEXT STEP) X[0] */\n" if ($pos == -1);
+    $code .= " mov	%edx,		%r11d		/* (NEXT STEP) z' = %edx */\n" if ($pos == -1);
+    $code .= <A
+	mov	1*4(%rbp),	%ebx	# ebx = ctx->B
+	mov	2*4(%rbp),	%ecx	# ecx = ctx->C
+	mov	3*4(%rbp),	%edx	# edx = ctx->D
+	# end is 'rdi'
+	# ptr is 'rsi'
+	# A is 'eax'
+	# B is 'ebx'
+	# C is 'ecx'
+	# D is 'edx'
+
+	cmp	%rdi,		%rsi		# cmp end with ptr
+	je	.Lend				# jmp if ptr == end
+
+	# BEGIN of loop over 16-word blocks
+.Lloop:	# save old values of A, B, C, D
+	mov	%eax,		%r8d
+	mov	%ebx,		%r9d
+	mov	%ecx,		%r14d
+	mov	%edx,		%r15d
+EOF
+round1_step(-1,'%eax','%ebx','%ecx','%edx', '1','0xd76aa478', '7');
+round1_step( 0,'%edx','%eax','%ebx','%ecx', '2','0xe8c7b756','12');
+round1_step( 0,'%ecx','%edx','%eax','%ebx', '3','0x242070db','17');
+round1_step( 0,'%ebx','%ecx','%edx','%eax', '4','0xc1bdceee','22');
+round1_step( 0,'%eax','%ebx','%ecx','%edx', '5','0xf57c0faf', '7');
+round1_step( 0,'%edx','%eax','%ebx','%ecx', '6','0x4787c62a','12');
+round1_step( 0,'%ecx','%edx','%eax','%ebx', '7','0xa8304613','17');
+round1_step( 0,'%ebx','%ecx','%edx','%eax', '8','0xfd469501','22');
+round1_step( 0,'%eax','%ebx','%ecx','%edx', '9','0x698098d8', '7');
+round1_step( 0,'%edx','%eax','%ebx','%ecx','10','0x8b44f7af','12');
+round1_step( 0,'%ecx','%edx','%eax','%ebx','11','0xffff5bb1','17');
+round1_step( 0,'%ebx','%ecx','%edx','%eax','12','0x895cd7be','22');
+round1_step( 0,'%eax','%ebx','%ecx','%edx','13','0x6b901122', '7');
+round1_step( 0,'%edx','%eax','%ebx','%ecx','14','0xfd987193','12');
+round1_step( 0,'%ecx','%edx','%eax','%ebx','15','0xa679438e','17');
+round1_step( 1,'%ebx','%ecx','%edx','%eax', '1','0x49b40821','22');
+
+round2_step(-1,'%eax','%ebx','%ecx','%edx', '6','0xf61e2562', '5');
+round2_step( 0,'%edx','%eax','%ebx','%ecx','11','0xc040b340', '9');
+round2_step( 0,'%ecx','%edx','%eax','%ebx', '0','0x265e5a51','14');
+round2_step( 0,'%ebx','%ecx','%edx','%eax', '5','0xe9b6c7aa','20');
+round2_step( 0,'%eax','%ebx','%ecx','%edx','10','0xd62f105d', '5');
+round2_step( 0,'%edx','%eax','%ebx','%ecx','15', '0x2441453', '9');
+round2_step( 0,'%ecx','%edx','%eax','%ebx', '4','0xd8a1e681','14');
+round2_step( 0,'%ebx','%ecx','%edx','%eax', '9','0xe7d3fbc8','20');
+round2_step( 0,'%eax','%ebx','%ecx','%edx','14','0x21e1cde6', '5');
+round2_step( 0,'%edx','%eax','%ebx','%ecx', '3','0xc33707d6', '9');
+round2_step( 0,'%ecx','%edx','%eax','%ebx', '8','0xf4d50d87','14');
+round2_step( 0,'%ebx','%ecx','%edx','%eax','13','0x455a14ed','20');
+round2_step( 0,'%eax','%ebx','%ecx','%edx', '2','0xa9e3e905', '5');
+round2_step( 0,'%edx','%eax','%ebx','%ecx', '7','0xfcefa3f8', '9');
+round2_step( 0,'%ecx','%edx','%eax','%ebx','12','0x676f02d9','14');
+round2_step( 1,'%ebx','%ecx','%edx','%eax', '5','0x8d2a4c8a','20');
+
+round3_step(-1,'%eax','%ebx','%ecx','%edx', '8','0xfffa3942', '4');
+round3_step( 0,'%edx','%eax','%ebx','%ecx','11','0x8771f681','11');
+round3_step( 0,'%ecx','%edx','%eax','%ebx','14','0x6d9d6122','16');
+round3_step( 0,'%ebx','%ecx','%edx','%eax', '1','0xfde5380c','23');
+round3_step( 0,'%eax','%ebx','%ecx','%edx', '4','0xa4beea44', '4');
+round3_step( 0,'%edx','%eax','%ebx','%ecx', '7','0x4bdecfa9','11');
+round3_step( 0,'%ecx','%edx','%eax','%ebx','10','0xf6bb4b60','16');
+round3_step( 0,'%ebx','%ecx','%edx','%eax','13','0xbebfbc70','23');
+round3_step( 0,'%eax','%ebx','%ecx','%edx', '0','0x289b7ec6', '4');
+round3_step( 0,'%edx','%eax','%ebx','%ecx', '3','0xeaa127fa','11');
+round3_step( 0,'%ecx','%edx','%eax','%ebx', '6','0xd4ef3085','16');
+round3_step( 0,'%ebx','%ecx','%edx','%eax', '9', '0x4881d05','23');
+round3_step( 0,'%eax','%ebx','%ecx','%edx','12','0xd9d4d039', '4');
+round3_step( 0,'%edx','%eax','%ebx','%ecx','15','0xe6db99e5','11');
+round3_step( 0,'%ecx','%edx','%eax','%ebx', '2','0x1fa27cf8','16');
+round3_step( 1,'%ebx','%ecx','%edx','%eax', '0','0xc4ac5665','23');
+
+round4_step(-1,'%eax','%ebx','%ecx','%edx', '7','0xf4292244', '6');
+round4_step( 0,'%edx','%eax','%ebx','%ecx','14','0x432aff97','10');
+round4_step( 0,'%ecx','%edx','%eax','%ebx', '5','0xab9423a7','15');
+round4_step( 0,'%ebx','%ecx','%edx','%eax','12','0xfc93a039','21');
+round4_step( 0,'%eax','%ebx','%ecx','%edx', '3','0x655b59c3', '6');
+round4_step( 0,'%edx','%eax','%ebx','%ecx','10','0x8f0ccc92','10');
+round4_step( 0,'%ecx','%edx','%eax','%ebx', '1','0xffeff47d','15');
+round4_step( 0,'%ebx','%ecx','%edx','%eax', '8','0x85845dd1','21');
+round4_step( 0,'%eax','%ebx','%ecx','%edx','15','0x6fa87e4f', '6');
+round4_step( 0,'%edx','%eax','%ebx','%ecx', '6','0xfe2ce6e0','10');
+round4_step( 0,'%ecx','%edx','%eax','%ebx','13','0xa3014314','15');
+round4_step( 0,'%ebx','%ecx','%edx','%eax', '4','0x4e0811a1','21');
+round4_step( 0,'%eax','%ebx','%ecx','%edx','11','0xf7537e82', '6');
+round4_step( 0,'%edx','%eax','%ebx','%ecx', '2','0xbd3af235','10');
+round4_step( 0,'%ecx','%edx','%eax','%ebx', '9','0x2ad7d2bb','15');
+round4_step( 1,'%ebx','%ecx','%edx','%eax', '0','0xeb86d391','21');
+$code .= <A = A
+	mov	%ebx,		1*4(%rbp)	# ctx->B = B
+	mov	%ecx,		2*4(%rbp)	# ctx->C = C
+	mov	%edx,		3*4(%rbp)	# ctx->D = D
+
+	mov	(%rsp),%r15
+	mov	8(%rsp),%r14
+	mov	16(%rsp),%r12
+	mov	24(%rsp),%rbx
+	mov	32(%rsp),%rbp
+	add	\$40,%rsp
+.Lepilogue:
+	ret
+.size md5_block_asm_data_order,.-md5_block_asm_data_order
+EOF
+
+# EXCEPTION_DISPOSITION handler (EXCEPTION_RECORD *rec,ULONG64 frame,
+#		CONTEXT *context,DISPATCHER_CONTEXT *disp)
+if ($win64) {
+my $rec="%rcx";
+my $frame="%rdx";
+my $context="%r8";
+my $disp="%r9";
+
+$code.=<<___;
+.extern	__imp_RtlVirtualUnwind
+.type	se_handler,\@abi-omnipotent
+.align	16
+se_handler:
+	push	%rsi
+	push	%rdi
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+	pushfq
+	sub	\$64,%rsp
+
+	mov	120($context),%rax	# pull context->Rax
+	mov	248($context),%rbx	# pull context->Rip
+
+	lea	.Lprologue(%rip),%r10
+	cmp	%r10,%rbx		# context->Rip<.Lprologue
+	jb	.Lin_prologue
+
+	mov	152($context),%rax	# pull context->Rsp
+
+	lea	.Lepilogue(%rip),%r10
+	cmp	%r10,%rbx		# context->Rip>=.Lepilogue
+	jae	.Lin_prologue
+
+	lea	40(%rax),%rax
+
+	mov	-8(%rax),%rbp
+	mov	-16(%rax),%rbx
+	mov	-24(%rax),%r12
+	mov	-32(%rax),%r14
+	mov	-40(%rax),%r15
+	mov	%rbx,144($context)	# restore context->Rbx
+	mov	%rbp,160($context)	# restore context->Rbp
+	mov	%r12,216($context)	# restore context->R12
+	mov	%r14,232($context)	# restore context->R14
+	mov	%r15,240($context)	# restore context->R15
+
+.Lin_prologue:
+	mov	8(%rax),%rdi
+	mov	16(%rax),%rsi
+	mov	%rax,152($context)	# restore context->Rsp
+	mov	%rsi,168($context)	# restore context->Rsi
+	mov	%rdi,176($context)	# restore context->Rdi
+
+	mov	40($disp),%rdi		# disp->ContextRecord
+	mov	$context,%rsi		# context
+	mov	\$154,%ecx		# sizeof(CONTEXT)
+	.long	0xa548f3fc		# cld; rep movsq
+
+	mov	$disp,%rsi
+	xor	%rcx,%rcx		# arg1, UNW_FLAG_NHANDLER
+	mov	8(%rsi),%rdx		# arg2, disp->ImageBase
+	mov	0(%rsi),%r8		# arg3, disp->ControlPc
+	mov	16(%rsi),%r9		# arg4, disp->FunctionEntry
+	mov	40(%rsi),%r10		# disp->ContextRecord
+	lea	56(%rsi),%r11		# &disp->HandlerData
+	lea	24(%rsi),%r12		# &disp->EstablisherFrame
+	mov	%r10,32(%rsp)		# arg5
+	mov	%r11,40(%rsp)		# arg6
+	mov	%r12,48(%rsp)		# arg7
+	mov	%rcx,56(%rsp)		# arg8, (NULL)
+	call	*__imp_RtlVirtualUnwind(%rip)
+
+	mov	\$1,%eax		# ExceptionContinueSearch
+	add	\$64,%rsp
+	popfq
+	pop	%r15
+	pop	%r14
+	pop	%r13
+	pop	%r12
+	pop	%rbp
+	pop	%rbx
+	pop	%rdi
+	pop	%rsi
+	ret
+.size	se_handler,.-se_handler
+
+.section	.pdata
+.align	4
+	.rva	.LSEH_begin_md5_block_asm_data_order
+	.rva	.LSEH_end_md5_block_asm_data_order
+	.rva	.LSEH_info_md5_block_asm_data_order
+
+.section	.xdata
+.align	8
+.LSEH_info_md5_block_asm_data_order:
+	.byte	9,0,0,0
+	.rva	se_handler
+___
+}
+
+print $code;
+
+close STDOUT;
diff --git a/crypto/md5/build.info b/crypto/md5/build.info
new file mode 100644
index 000000000..38323a3fc
--- /dev/null
+++ b/crypto/md5/build.info
@@ -0,0 +1,22 @@
+LIBS=../../libcrypto
+SOURCE[../../libcrypto]=\
+        md5_dgst.c md5_one.c {- $target{md5_asm_src} -}
+
+GENERATE[md5-586.s]=asm/md5-586.pl $(PERLASM_SCHEME) $(CFLAGS) $(LIB_CFLAGS)
+
+GENERATE[md5-x86_64.s]=asm/md5-x86_64.pl $(PERLASM_SCHEME)
+
+GENERATE[md5-sparcv9.S]=asm/md5-sparcv9.pl $(PERLASM_SCHEME)
+INCLUDE[md5-sparcv9.o]=..
+
+BEGINRAW[makefile(windows)]
+{- $builddir -}\md5-ia64.asm: {- $sourcedir -}\asm\md5-ia64.S
+	$(CC) $(CFLAGS) -EP {- $sourcedir -}\asm\md5-ia64.S > $@.i && move /Y $@.i $@
+ENDRAW[makefile(windows)]
+
+BEGINRAW[Makefile]
+{- $builddir -}/md5-ia64.s: {- $sourcedir -}/asm/md5-ia64.S
+	$(CC) $(CFLAGS) -E {- $sourcedir -}/asm/md5-ia64.S | \
+	$(PERL) -ne 's/;\s+/;\n/g; print;' > $@
+
+ENDRAW[Makefile]
diff --git a/crypto/md5/md5_dgst.c b/crypto/md5/md5_dgst.c
new file mode 100644
index 000000000..fbede6742
--- /dev/null
+++ b/crypto/md5/md5_dgst.c
@@ -0,0 +1,164 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "md5_locl.h"
+#include 
+
+/*
+ * Implemented from RFC1321 The MD5 Message-Digest Algorithm
+ */
+
+#define INIT_DATA_A (unsigned long)0x67452301L
+#define INIT_DATA_B (unsigned long)0xefcdab89L
+#define INIT_DATA_C (unsigned long)0x98badcfeL
+#define INIT_DATA_D (unsigned long)0x10325476L
+
+int MD5_Init(MD5_CTX *c)
+{
+    memset(c, 0, sizeof(*c));
+    c->A = INIT_DATA_A;
+    c->B = INIT_DATA_B;
+    c->C = INIT_DATA_C;
+    c->D = INIT_DATA_D;
+    return 1;
+}
+
+#ifndef md5_block_data_order
+# ifdef X
+#  undef X
+# endif
+void md5_block_data_order(MD5_CTX *c, const void *data_, size_t num)
+{
+    const unsigned char *data = data_;
+    register unsigned MD32_REG_T A, B, C, D, l;
+# ifndef MD32_XARRAY
+    /* See comment in crypto/sha/sha_locl.h for details. */
+    unsigned MD32_REG_T XX0, XX1, XX2, XX3, XX4, XX5, XX6, XX7,
+        XX8, XX9, XX10, XX11, XX12, XX13, XX14, XX15;
+#  define X(i)   XX##i
+# else
+    MD5_LONG XX[MD5_LBLOCK];
+#  define X(i)   XX[i]
+# endif
+
+    A = c->A;
+    B = c->B;
+    C = c->C;
+    D = c->D;
+
+    for (; num--;) {
+        (void)HOST_c2l(data, l);
+        X(0) = l;
+        (void)HOST_c2l(data, l);
+        X(1) = l;
+        /* Round 0 */
+        R0(A, B, C, D, X(0), 7, 0xd76aa478L);
+        (void)HOST_c2l(data, l);
+        X(2) = l;
+        R0(D, A, B, C, X(1), 12, 0xe8c7b756L);
+        (void)HOST_c2l(data, l);
+        X(3) = l;
+        R0(C, D, A, B, X(2), 17, 0x242070dbL);
+        (void)HOST_c2l(data, l);
+        X(4) = l;
+        R0(B, C, D, A, X(3), 22, 0xc1bdceeeL);
+        (void)HOST_c2l(data, l);
+        X(5) = l;
+        R0(A, B, C, D, X(4), 7, 0xf57c0fafL);
+        (void)HOST_c2l(data, l);
+        X(6) = l;
+        R0(D, A, B, C, X(5), 12, 0x4787c62aL);
+        (void)HOST_c2l(data, l);
+        X(7) = l;
+        R0(C, D, A, B, X(6), 17, 0xa8304613L);
+        (void)HOST_c2l(data, l);
+        X(8) = l;
+        R0(B, C, D, A, X(7), 22, 0xfd469501L);
+        (void)HOST_c2l(data, l);
+        X(9) = l;
+        R0(A, B, C, D, X(8), 7, 0x698098d8L);
+        (void)HOST_c2l(data, l);
+        X(10) = l;
+        R0(D, A, B, C, X(9), 12, 0x8b44f7afL);
+        (void)HOST_c2l(data, l);
+        X(11) = l;
+        R0(C, D, A, B, X(10), 17, 0xffff5bb1L);
+        (void)HOST_c2l(data, l);
+        X(12) = l;
+        R0(B, C, D, A, X(11), 22, 0x895cd7beL);
+        (void)HOST_c2l(data, l);
+        X(13) = l;
+        R0(A, B, C, D, X(12), 7, 0x6b901122L);
+        (void)HOST_c2l(data, l);
+        X(14) = l;
+        R0(D, A, B, C, X(13), 12, 0xfd987193L);
+        (void)HOST_c2l(data, l);
+        X(15) = l;
+        R0(C, D, A, B, X(14), 17, 0xa679438eL);
+        R0(B, C, D, A, X(15), 22, 0x49b40821L);
+        /* Round 1 */
+        R1(A, B, C, D, X(1), 5, 0xf61e2562L);
+        R1(D, A, B, C, X(6), 9, 0xc040b340L);
+        R1(C, D, A, B, X(11), 14, 0x265e5a51L);
+        R1(B, C, D, A, X(0), 20, 0xe9b6c7aaL);
+        R1(A, B, C, D, X(5), 5, 0xd62f105dL);
+        R1(D, A, B, C, X(10), 9, 0x02441453L);
+        R1(C, D, A, B, X(15), 14, 0xd8a1e681L);
+        R1(B, C, D, A, X(4), 20, 0xe7d3fbc8L);
+        R1(A, B, C, D, X(9), 5, 0x21e1cde6L);
+        R1(D, A, B, C, X(14), 9, 0xc33707d6L);
+        R1(C, D, A, B, X(3), 14, 0xf4d50d87L);
+        R1(B, C, D, A, X(8), 20, 0x455a14edL);
+        R1(A, B, C, D, X(13), 5, 0xa9e3e905L);
+        R1(D, A, B, C, X(2), 9, 0xfcefa3f8L);
+        R1(C, D, A, B, X(7), 14, 0x676f02d9L);
+        R1(B, C, D, A, X(12), 20, 0x8d2a4c8aL);
+        /* Round 2 */
+        R2(A, B, C, D, X(5), 4, 0xfffa3942L);
+        R2(D, A, B, C, X(8), 11, 0x8771f681L);
+        R2(C, D, A, B, X(11), 16, 0x6d9d6122L);
+        R2(B, C, D, A, X(14), 23, 0xfde5380cL);
+        R2(A, B, C, D, X(1), 4, 0xa4beea44L);
+        R2(D, A, B, C, X(4), 11, 0x4bdecfa9L);
+        R2(C, D, A, B, X(7), 16, 0xf6bb4b60L);
+        R2(B, C, D, A, X(10), 23, 0xbebfbc70L);
+        R2(A, B, C, D, X(13), 4, 0x289b7ec6L);
+        R2(D, A, B, C, X(0), 11, 0xeaa127faL);
+        R2(C, D, A, B, X(3), 16, 0xd4ef3085L);
+        R2(B, C, D, A, X(6), 23, 0x04881d05L);
+        R2(A, B, C, D, X(9), 4, 0xd9d4d039L);
+        R2(D, A, B, C, X(12), 11, 0xe6db99e5L);
+        R2(C, D, A, B, X(15), 16, 0x1fa27cf8L);
+        R2(B, C, D, A, X(2), 23, 0xc4ac5665L);
+        /* Round 3 */
+        R3(A, B, C, D, X(0), 6, 0xf4292244L);
+        R3(D, A, B, C, X(7), 10, 0x432aff97L);
+        R3(C, D, A, B, X(14), 15, 0xab9423a7L);
+        R3(B, C, D, A, X(5), 21, 0xfc93a039L);
+        R3(A, B, C, D, X(12), 6, 0x655b59c3L);
+        R3(D, A, B, C, X(3), 10, 0x8f0ccc92L);
+        R3(C, D, A, B, X(10), 15, 0xffeff47dL);
+        R3(B, C, D, A, X(1), 21, 0x85845dd1L);
+        R3(A, B, C, D, X(8), 6, 0x6fa87e4fL);
+        R3(D, A, B, C, X(15), 10, 0xfe2ce6e0L);
+        R3(C, D, A, B, X(6), 15, 0xa3014314L);
+        R3(B, C, D, A, X(13), 21, 0x4e0811a1L);
+        R3(A, B, C, D, X(4), 6, 0xf7537e82L);
+        R3(D, A, B, C, X(11), 10, 0xbd3af235L);
+        R3(C, D, A, B, X(2), 15, 0x2ad7d2bbL);
+        R3(B, C, D, A, X(9), 21, 0xeb86d391L);
+
+        A = c->A += A;
+        B = c->B += B;
+        C = c->C += C;
+        D = c->D += D;
+    }
+}
+#endif
diff --git a/crypto/md5/md5_locl.h b/crypto/md5/md5_locl.h
new file mode 100644
index 000000000..9c7aade84
--- /dev/null
+++ b/crypto/md5/md5_locl.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include 
+#include 
+
+#ifdef MD5_ASM
+# if defined(__i386) || defined(__i386__) || defined(_M_IX86) || \
+     defined(__x86_64) || defined(__x86_64__) || defined(_M_AMD64) || defined(_M_X64)
+#  define md5_block_data_order md5_block_asm_data_order
+# elif defined(__ia64) || defined(__ia64__) || defined(_M_IA64)
+#  define md5_block_data_order md5_block_asm_data_order
+# elif defined(__sparc) || defined(__sparc__)
+#  define md5_block_data_order md5_block_asm_data_order
+# endif
+#endif
+
+void md5_block_data_order(MD5_CTX *c, const void *p, size_t num);
+
+#define DATA_ORDER_IS_LITTLE_ENDIAN
+
+#define HASH_LONG               MD5_LONG
+#define HASH_CTX                MD5_CTX
+#define HASH_CBLOCK             MD5_CBLOCK
+#define HASH_UPDATE             MD5_Update
+#define HASH_TRANSFORM          MD5_Transform
+#define HASH_FINAL              MD5_Final
+#define HASH_MAKE_STRING(c,s)   do {    \
+        unsigned long ll;               \
+        ll=(c)->A; (void)HOST_l2c(ll,(s));      \
+        ll=(c)->B; (void)HOST_l2c(ll,(s));      \
+        ll=(c)->C; (void)HOST_l2c(ll,(s));      \
+        ll=(c)->D; (void)HOST_l2c(ll,(s));      \
+        } while (0)
+#define HASH_BLOCK_DATA_ORDER   md5_block_data_order
+
+#include "internal/md32_common.h"
+
+/*-
+#define F(x,y,z)        (((x) & (y))  |  ((~(x)) & (z)))
+#define G(x,y,z)        (((x) & (z))  |  ((y) & (~(z))))
+*/
+
+/*
+ * As pointed out by Wei Dai , the above can be simplified
+ * to the code below.  Wei attributes these optimizations to Peter Gutmann's
+ * SHS code, and he attributes it to Rich Schroeppel.
+ */
+#define F(b,c,d)        ((((c) ^ (d)) & (b)) ^ (d))
+#define G(b,c,d)        ((((b) ^ (c)) & (d)) ^ (c))
+#define H(b,c,d)        ((b) ^ (c) ^ (d))
+#define I(b,c,d)        (((~(d)) | (b)) ^ (c))
+
+#define R0(a,b,c,d,k,s,t) { \
+        a+=((k)+(t)+F((b),(c),(d))); \
+        a=ROTATE(a,s); \
+        a+=b; };\
+
+#define R1(a,b,c,d,k,s,t) { \
+        a+=((k)+(t)+G((b),(c),(d))); \
+        a=ROTATE(a,s); \
+        a+=b; };
+
+#define R2(a,b,c,d,k,s,t) { \
+        a+=((k)+(t)+H((b),(c),(d))); \
+        a=ROTATE(a,s); \
+        a+=b; };
+
+#define R3(a,b,c,d,k,s,t) { \
+        a+=((k)+(t)+I((b),(c),(d))); \
+        a=ROTATE(a,s); \
+        a+=b; };
diff --git a/crypto/md5/md5_one.c b/crypto/md5/md5_one.c
new file mode 100644
index 000000000..becd87e4d
--- /dev/null
+++ b/crypto/md5/md5_one.c
@@ -0,0 +1,47 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include 
+#include 
+
+#ifdef CHARSET_EBCDIC
+# include 
+#endif
+
+unsigned char *MD5(const unsigned char *d, size_t n, unsigned char *md)
+{
+    MD5_CTX c;
+    static unsigned char m[MD5_DIGEST_LENGTH];
+
+    if (md == NULL)
+        md = m;
+    if (!MD5_Init(&c))
+        return NULL;
+#ifndef CHARSET_EBCDIC
+    MD5_Update(&c, d, n);
+#else
+    {
+        char temp[1024];
+        unsigned long chunk;
+
+        while (n > 0) {
+            chunk = (n > sizeof(temp)) ? sizeof(temp) : n;
+            ebcdic2ascii(temp, d, chunk);
+            MD5_Update(&c, temp, chunk);
+            n -= chunk;
+            d += chunk;
+        }
+    }
+#endif
+    MD5_Final(md, &c);
+    OPENSSL_cleanse(&c, sizeof(c)); /* security consideration */
+    return (md);
+}
diff --git a/crypto/mdc2/build.info b/crypto/mdc2/build.info
new file mode 100644
index 000000000..8fe6878d6
--- /dev/null
+++ b/crypto/mdc2/build.info
@@ -0,0 +1,3 @@
+LIBS=../../libcrypto
+SOURCE[../../libcrypto]=\
+        mdc2dgst.c mdc2_one.c
diff --git a/crypto/mdc2/mdc2_one.c b/crypto/mdc2/mdc2_one.c
new file mode 100644
index 000000000..472a5ec2e
--- /dev/null
+++ b/crypto/mdc2/mdc2_one.c
@@ -0,0 +1,27 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+
+unsigned char *MDC2(const unsigned char *d, size_t n, unsigned char *md)
+{
+    MDC2_CTX c;
+    static unsigned char m[MDC2_DIGEST_LENGTH];
+
+    if (md == NULL)
+        md = m;
+    if (!MDC2_Init(&c))
+        return NULL;
+    MDC2_Update(&c, d, n);
+    MDC2_Final(md, &c);
+    OPENSSL_cleanse(&c, sizeof(c)); /* security consideration */
+    return (md);
+}
diff --git a/crypto/mdc2/mdc2dgst.c b/crypto/mdc2/mdc2dgst.c
new file mode 100644
index 000000000..37d99f48a
--- /dev/null
+++ b/crypto/mdc2/mdc2dgst.c
@@ -0,0 +1,147 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#undef c2l
+#define c2l(c,l)        (l =((DES_LONG)(*((c)++)))    , \
+                         l|=((DES_LONG)(*((c)++)))<< 8L, \
+                         l|=((DES_LONG)(*((c)++)))<<16L, \
+                         l|=((DES_LONG)(*((c)++)))<<24L)
+
+#undef l2c
+#define l2c(l,c)        (*((c)++)=(unsigned char)(((l)     )&0xff), \
+                        *((c)++)=(unsigned char)(((l)>> 8L)&0xff), \
+                        *((c)++)=(unsigned char)(((l)>>16L)&0xff), \
+                        *((c)++)=(unsigned char)(((l)>>24L)&0xff))
+
+static void mdc2_body(MDC2_CTX *c, const unsigned char *in, size_t len);
+int MDC2_Init(MDC2_CTX *c)
+{
+    c->num = 0;
+    c->pad_type = 1;
+    memset(&(c->h[0]), 0x52, MDC2_BLOCK);
+    memset(&(c->hh[0]), 0x25, MDC2_BLOCK);
+    return 1;
+}
+
+int MDC2_Update(MDC2_CTX *c, const unsigned char *in, size_t len)
+{
+    size_t i, j;
+
+    i = c->num;
+    if (i != 0) {
+        if (len < MDC2_BLOCK - i) {
+            /* partial block */
+            memcpy(&(c->data[i]), in, len);
+            c->num += (int)len;
+            return 1;
+        } else {
+            /* filled one */
+            j = MDC2_BLOCK - i;
+            memcpy(&(c->data[i]), in, j);
+            len -= j;
+            in += j;
+            c->num = 0;
+            mdc2_body(c, &(c->data[0]), MDC2_BLOCK);
+        }
+    }
+    i = len & ~((size_t)MDC2_BLOCK - 1);
+    if (i > 0)
+        mdc2_body(c, in, i);
+    j = len - i;
+    if (j > 0) {
+        memcpy(&(c->data[0]), &(in[i]), j);
+        c->num = (int)j;
+    }
+    return 1;
+}
+
+static void mdc2_body(MDC2_CTX *c, const unsigned char *in, size_t len)
+{
+    register DES_LONG tin0, tin1;
+    register DES_LONG ttin0, ttin1;
+    DES_LONG d[2], dd[2];
+    DES_key_schedule k;
+    unsigned char *p;
+    size_t i;
+
+    for (i = 0; i < len; i += 8) {
+        c2l(in, tin0);
+        d[0] = dd[0] = tin0;
+        c2l(in, tin1);
+        d[1] = dd[1] = tin1;
+        c->h[0] = (c->h[0] & 0x9f) | 0x40;
+        c->hh[0] = (c->hh[0] & 0x9f) | 0x20;
+
+        DES_set_odd_parity(&c->h);
+        DES_set_key_unchecked(&c->h, &k);
+        DES_encrypt1(d, &k, 1);
+
+        DES_set_odd_parity(&c->hh);
+        DES_set_key_unchecked(&c->hh, &k);
+        DES_encrypt1(dd, &k, 1);
+
+        ttin0 = tin0 ^ dd[0];
+        ttin1 = tin1 ^ dd[1];
+        tin0 ^= d[0];
+        tin1 ^= d[1];
+
+        p = c->h;
+        l2c(tin0, p);
+        l2c(ttin1, p);
+        p = c->hh;
+        l2c(ttin0, p);
+        l2c(tin1, p);
+    }
+}
+
+int MDC2_Final(unsigned char *md, MDC2_CTX *c)
+{
+    unsigned int i;
+    int j;
+
+    i = c->num;
+    j = c->pad_type;
+    if ((i > 0) || (j == 2)) {
+        if (j == 2)
+            c->data[i++] = 0x80;
+        memset(&(c->data[i]), 0, MDC2_BLOCK - i);
+        mdc2_body(c, c->data, MDC2_BLOCK);
+    }
+    memcpy(md, (char *)c->h, MDC2_BLOCK);
+    memcpy(&(md[MDC2_BLOCK]), (char *)c->hh, MDC2_BLOCK);
+    return 1;
+}
+
+#undef TEST
+
+#ifdef TEST
+main()
+{
+    unsigned char md[MDC2_DIGEST_LENGTH];
+    int i;
+    MDC2_CTX c;
+    static char *text = "Now is the time for all ";
+
+    MDC2_Init(&c);
+    MDC2_Update(&c, text, strlen(text));
+    MDC2_Final(&(md[0]), &c);
+
+    for (i = 0; i < MDC2_DIGEST_LENGTH; i++)
+        printf("%02X", md[i]);
+    printf("\n");
+}
+
+#endif
diff --git a/crypto/mem.c b/crypto/mem.c
new file mode 100644
index 000000000..02aa43a7e
--- /dev/null
+++ b/crypto/mem.c
@@ -0,0 +1,190 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include "internal/cryptlib.h"
+
+/*
+ * the following pointers may be changed as long as 'allow_customize' is set
+ */
+static int allow_customize = 1;
+
+static void *(*malloc_impl)(size_t, const char *, int)
+    = CRYPTO_malloc;
+static void *(*realloc_impl)(void *, size_t, const char *, int)
+    = CRYPTO_realloc;
+static void (*free_impl)(void *, const char *, int)
+    = CRYPTO_free;
+
+#ifndef OPENSSL_NO_CRYPTO_MDEBUG
+static int call_malloc_debug = 1;
+#else
+static int call_malloc_debug = 0;
+#endif
+
+int CRYPTO_set_mem_functions(
+        void *(*m)(size_t, const char *, int),
+        void *(*r)(void *, size_t, const char *, int),
+        void (*f)(void *, const char *, int))
+{
+    if (!allow_customize)
+        return 0;
+    if (m)
+        malloc_impl = m;
+    if (r)
+        realloc_impl = r;
+    if (f)
+        free_impl = f;
+    return 1;
+}
+
+int CRYPTO_set_mem_debug(int flag)
+{
+    if (!allow_customize)
+        return 0;
+    call_malloc_debug = flag;
+    return 1;
+}
+
+void CRYPTO_get_mem_functions(
+        void *(**m)(size_t, const char *, int),
+        void *(**r)(void *, size_t, const char *, int),
+        void (**f)(void *, const char *, int))
+{
+    if (m != NULL)
+        *m = malloc_impl;
+    if (r != NULL)
+        *r = realloc_impl;
+    if (f != NULL)
+        *f = free_impl;
+}
+
+void *CRYPTO_malloc(size_t num, const char *file, int line)
+{
+    void *ret = NULL;
+
+    if (malloc_impl != NULL && malloc_impl != CRYPTO_malloc)
+        return malloc_impl(num, file, line);
+
+    if (num <= 0)
+        return NULL;
+
+    allow_customize = 0;
+#ifndef OPENSSL_NO_CRYPTO_MDEBUG
+    if (call_malloc_debug) {
+        CRYPTO_mem_debug_malloc(NULL, num, 0, file, line);
+        ret = malloc(num);
+        CRYPTO_mem_debug_malloc(ret, num, 1, file, line);
+    } else {
+        ret = malloc(num);
+    }
+#else
+    osslargused(file); osslargused(line);
+    ret = malloc(num);
+#endif
+
+    return ret;
+}
+
+void *CRYPTO_zalloc(size_t num, const char *file, int line)
+{
+    void *ret = CRYPTO_malloc(num, file, line);
+
+    if (ret != NULL)
+        memset(ret, 0, num);
+    return ret;
+}
+
+void *CRYPTO_realloc(void *str, size_t num, const char *file, int line)
+{
+    if (realloc_impl != NULL && realloc_impl != &CRYPTO_realloc)
+        return realloc_impl(str, num, file, line);
+
+    if (str == NULL)
+        return CRYPTO_malloc(num, file, line);
+
+    if (num == 0) {
+        CRYPTO_free(str, file, line);
+        return NULL;
+    }
+
+    allow_customize = 0;
+#ifndef OPENSSL_NO_CRYPTO_MDEBUG
+    if (call_malloc_debug) {
+        void *ret;
+        CRYPTO_mem_debug_realloc(str, NULL, num, 0, file, line);
+        ret = realloc(str, num);
+        CRYPTO_mem_debug_realloc(str, ret, num, 1, file, line);
+        return ret;
+    }
+#else
+    osslargused(file); osslargused(line);
+#endif
+    return realloc(str, num);
+
+}
+
+void *CRYPTO_clear_realloc(void *str, size_t old_len, size_t num,
+                           const char *file, int line)
+{
+    void *ret = NULL;
+
+    if (str == NULL)
+        return CRYPTO_malloc(num, file, line);
+
+    if (num == 0) {
+        CRYPTO_clear_free(str, old_len, file, line);
+        return NULL;
+    }
+
+    /* Can't shrink the buffer since memcpy below copies |old_len| bytes. */
+    if (num < old_len) {
+        OPENSSL_cleanse((char*)str + num, old_len - num);
+        return str;
+    }
+
+    ret = CRYPTO_malloc(num, file, line);
+    if (ret != NULL) {
+        memcpy(ret, str, old_len);
+        CRYPTO_clear_free(str, old_len, file, line);
+    }
+    return ret;
+}
+
+void CRYPTO_free(void *str, const char *file, int line)
+{
+    if (free_impl != NULL && free_impl != &CRYPTO_free) {
+        free_impl(str, file, line);
+        return;
+    }
+
+#ifndef OPENSSL_NO_CRYPTO_MDEBUG
+    if (call_malloc_debug) {
+        CRYPTO_mem_debug_free(str, 0, file, line);
+        free(str);
+        CRYPTO_mem_debug_free(str, 1, file, line);
+    } else {
+        free(str);
+    }
+#else
+    free(str);
+#endif
+}
+
+void CRYPTO_clear_free(void *str, size_t num, const char *file, int line)
+{
+    if (str == NULL)
+        return;
+    if (num)
+        OPENSSL_cleanse(str, num);
+    CRYPTO_free(str, file, line);
+}
diff --git a/crypto/mem_clr.c b/crypto/mem_clr.c
new file mode 100644
index 000000000..35bfb74ea
--- /dev/null
+++ b/crypto/mem_clr.c
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2002-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+
+/*
+ * Pointer to memset is volatile so that compiler must de-reference
+ * the pointer and can't assume that it points to any function in
+ * particular (such as memset, which it then might further "optimize")
+ */
+typedef void *(*memset_t)(void *, int, size_t);
+
+static volatile memset_t memset_func = memset;
+
+void OPENSSL_cleanse(void *ptr, size_t len)
+{
+    memset_func(ptr, 0, len);
+}
diff --git a/crypto/mem_dbg.c b/crypto/mem_dbg.c
new file mode 100644
index 000000000..dc3f8ff57
--- /dev/null
+++ b/crypto/mem_dbg.c
@@ -0,0 +1,629 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include 
+#include "internal/cryptlib.h"
+#include "internal/thread_once.h"
+#include 
+#include 
+#include "internal/bio.h"
+#include 
+
+#ifndef OPENSSL_NO_CRYPTO_MDEBUG_BACKTRACE
+# include 
+#endif
+
+/*
+ * The state changes to CRYPTO_MEM_CHECK_ON | CRYPTO_MEM_CHECK_ENABLE when
+ * the application asks for it (usually after library initialisation for
+ * which no book-keeping is desired). State CRYPTO_MEM_CHECK_ON exists only
+ * temporarily when the library thinks that certain allocations should not be
+ * checked (e.g. the data structures used for memory checking).  It is not
+ * suitable as an initial state: the library will unexpectedly enable memory
+ * checking when it executes one of those sections that want to disable
+ * checking temporarily. State CRYPTO_MEM_CHECK_ENABLE without ..._ON makes
+ * no sense whatsoever.
+ */
+#ifndef OPENSSL_NO_CRYPTO_MDEBUG
+static int mh_mode = CRYPTO_MEM_CHECK_OFF;
+#endif
+
+#ifndef OPENSSL_NO_CRYPTO_MDEBUG
+static unsigned long order = 0; /* number of memory requests */
+
+/*-
+ * For application-defined information (static C-string `info')
+ * to be displayed in memory leak list.
+ * Each thread has its own stack.  For applications, there is
+ *   OPENSSL_mem_debug_push("...")     to push an entry,
+ *   OPENSSL_mem_debug_pop()     to pop an entry,
+ */
+struct app_mem_info_st {
+    CRYPTO_THREAD_ID threadid;
+    const char *file;
+    int line;
+    const char *info;
+    struct app_mem_info_st *next; /* tail of thread's stack */
+    int references;
+};
+
+static CRYPTO_ONCE memdbg_init = CRYPTO_ONCE_STATIC_INIT;
+static CRYPTO_RWLOCK *malloc_lock = NULL;
+static CRYPTO_RWLOCK *long_malloc_lock = NULL;
+static CRYPTO_THREAD_LOCAL appinfokey;
+
+/* memory-block description */
+struct mem_st {
+    void *addr;
+    int num;
+    const char *file;
+    int line;
+    CRYPTO_THREAD_ID threadid;
+    unsigned long order;
+    time_t time;
+    APP_INFO *app_info;
+#ifndef OPENSSL_NO_CRYPTO_MDEBUG_BACKTRACE
+    void *array[30];
+    size_t array_siz;
+#endif
+};
+
+static LHASH_OF(MEM) *mh = NULL; /* hash-table of memory requests (address as
+                                  * key); access requires MALLOC2 lock */
+
+/* num_disable > 0 iff mh_mode == CRYPTO_MEM_CHECK_ON (w/o ..._ENABLE) */
+static unsigned int num_disable = 0;
+
+/*
+ * Valid iff num_disable > 0.  long_malloc_lock is locked exactly in this
+ * case (by the thread named in disabling_thread).
+ */
+static CRYPTO_THREAD_ID disabling_threadid;
+
+DEFINE_RUN_ONCE_STATIC(do_memdbg_init)
+{
+    malloc_lock = CRYPTO_THREAD_lock_new();
+    long_malloc_lock = CRYPTO_THREAD_lock_new();
+    if (malloc_lock == NULL || long_malloc_lock == NULL
+        || !CRYPTO_THREAD_init_local(&appinfokey, NULL)) {
+        CRYPTO_THREAD_lock_free(malloc_lock);
+        malloc_lock = NULL;
+        CRYPTO_THREAD_lock_free(long_malloc_lock);
+        long_malloc_lock = NULL;
+        return 0;
+    }
+    return 1;
+}
+
+static void app_info_free(APP_INFO *inf)
+{
+    if (!inf)
+        return;
+    if (--(inf->references) <= 0) {
+        app_info_free(inf->next);
+        OPENSSL_free(inf);
+    }
+}
+#endif
+
+int CRYPTO_mem_ctrl(int mode)
+{
+#ifdef OPENSSL_NO_CRYPTO_MDEBUG
+    return mode - mode;
+#else
+    int ret = mh_mode;
+
+    if (!RUN_ONCE(&memdbg_init, do_memdbg_init))
+        return -1;
+
+    CRYPTO_THREAD_write_lock(malloc_lock);
+    switch (mode) {
+    default:
+        break;
+
+    case CRYPTO_MEM_CHECK_ON:
+        mh_mode = CRYPTO_MEM_CHECK_ON | CRYPTO_MEM_CHECK_ENABLE;
+        num_disable = 0;
+        break;
+
+    case CRYPTO_MEM_CHECK_OFF:
+        mh_mode = 0;
+        num_disable = 0;
+        break;
+
+    /* switch off temporarily (for library-internal use): */
+    case CRYPTO_MEM_CHECK_DISABLE:
+        if (mh_mode & CRYPTO_MEM_CHECK_ON) {
+            CRYPTO_THREAD_ID cur = CRYPTO_THREAD_get_current_id();
+            /* see if we don't have long_malloc_lock already */
+            if (!num_disable
+                || !CRYPTO_THREAD_compare_id(disabling_threadid, cur)) {
+                /*
+                 * Long-time lock long_malloc_lock must not be claimed
+                 * while we're holding malloc_lock, or we'll deadlock
+                 * if somebody else holds long_malloc_lock (and cannot
+                 * release it because we block entry to this function). Give
+                 * them a chance, first, and then claim the locks in
+                 * appropriate order (long-time lock first).
+                 */
+                CRYPTO_THREAD_unlock(malloc_lock);
+                /*
+                 * Note that after we have waited for long_malloc_lock and
+                 * malloc_lock, we'll still be in the right "case" and
+                 * "if" branch because MemCheck_start and MemCheck_stop may
+                 * never be used while there are multiple OpenSSL threads.
+                 */
+                CRYPTO_THREAD_write_lock(long_malloc_lock);
+                CRYPTO_THREAD_write_lock(malloc_lock);
+                mh_mode &= ~CRYPTO_MEM_CHECK_ENABLE;
+                disabling_threadid = cur;
+            }
+            num_disable++;
+        }
+        break;
+
+    case CRYPTO_MEM_CHECK_ENABLE:
+        if (mh_mode & CRYPTO_MEM_CHECK_ON) {
+            if (num_disable) {  /* always true, or something is going wrong */
+                num_disable--;
+                if (num_disable == 0) {
+                    mh_mode |= CRYPTO_MEM_CHECK_ENABLE;
+                    CRYPTO_THREAD_unlock(long_malloc_lock);
+                }
+            }
+        }
+        break;
+    }
+    CRYPTO_THREAD_unlock(malloc_lock);
+    return (ret);
+#endif
+}
+
+#ifndef OPENSSL_NO_CRYPTO_MDEBUG
+
+static int mem_check_on(void)
+{
+    int ret = 0;
+    CRYPTO_THREAD_ID cur;
+
+    if (mh_mode & CRYPTO_MEM_CHECK_ON) {
+        if (!RUN_ONCE(&memdbg_init, do_memdbg_init))
+            return 0;
+
+        cur = CRYPTO_THREAD_get_current_id();
+        CRYPTO_THREAD_read_lock(malloc_lock);
+
+        ret = (mh_mode & CRYPTO_MEM_CHECK_ENABLE)
+            || !CRYPTO_THREAD_compare_id(disabling_threadid, cur);
+
+        CRYPTO_THREAD_unlock(malloc_lock);
+    }
+    return (ret);
+}
+
+static int mem_cmp(const MEM *a, const MEM *b)
+{
+#ifdef _WIN64
+    const char *ap = (const char *)a->addr, *bp = (const char *)b->addr;
+    if (ap == bp)
+        return 0;
+    else if (ap > bp)
+        return 1;
+    else
+        return -1;
+#else
+    return (const char *)a->addr - (const char *)b->addr;
+#endif
+}
+
+static unsigned long mem_hash(const MEM *a)
+{
+    size_t ret;
+
+    ret = (size_t)a->addr;
+
+    ret = ret * 17851 + (ret >> 14) * 7 + (ret >> 4) * 251;
+    return (ret);
+}
+
+/* returns 1 if there was an info to pop, 0 if the stack was empty. */
+static int pop_info(void)
+{
+    APP_INFO *current = NULL;
+
+    if (!RUN_ONCE(&memdbg_init, do_memdbg_init))
+        return 0;
+
+    current = (APP_INFO *)CRYPTO_THREAD_get_local(&appinfokey);
+    if (current != NULL) {
+        APP_INFO *next = current->next;
+
+        if (next != NULL) {
+            next->references++;
+            CRYPTO_THREAD_set_local(&appinfokey, next);
+        } else {
+            CRYPTO_THREAD_set_local(&appinfokey, NULL);
+        }
+        if (--(current->references) <= 0) {
+            current->next = NULL;
+            if (next != NULL)
+                next->references--;
+            OPENSSL_free(current);
+        }
+        return 1;
+    }
+    return 0;
+}
+
+int CRYPTO_mem_debug_push(const char *info, const char *file, int line)
+{
+    APP_INFO *ami, *amim;
+    int ret = 0;
+
+    if (mem_check_on()) {
+        CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_DISABLE);
+
+        if (!RUN_ONCE(&memdbg_init, do_memdbg_init)
+            || (ami = OPENSSL_malloc(sizeof(*ami))) == NULL)
+            goto err;
+
+        ami->threadid = CRYPTO_THREAD_get_current_id();
+        ami->file = file;
+        ami->line = line;
+        ami->info = info;
+        ami->references = 1;
+        ami->next = NULL;
+
+        amim = (APP_INFO *)CRYPTO_THREAD_get_local(&appinfokey);
+        CRYPTO_THREAD_set_local(&appinfokey, ami);
+
+        if (amim != NULL)
+            ami->next = amim;
+        ret = 1;
+ err:
+        CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_ENABLE);
+    }
+
+    return (ret);
+}
+
+int CRYPTO_mem_debug_pop(void)
+{
+    int ret = 0;
+
+    if (mem_check_on()) {
+        CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_DISABLE);
+        ret = pop_info();
+        CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_ENABLE);
+    }
+    return (ret);
+}
+
+static unsigned long break_order_num = 0;
+
+void CRYPTO_mem_debug_malloc(void *addr, size_t num, int before_p,
+                             const char *file, int line)
+{
+    MEM *m, *mm;
+    APP_INFO *amim;
+
+    switch (before_p & 127) {
+    case 0:
+        break;
+    case 1:
+        if (addr == NULL)
+            break;
+
+        if (mem_check_on()) {
+            CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_DISABLE);
+
+            if (!RUN_ONCE(&memdbg_init, do_memdbg_init)
+                || (m = OPENSSL_malloc(sizeof(*m))) == NULL) {
+                OPENSSL_free(addr);
+                CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_ENABLE);
+                return;
+            }
+            if (mh == NULL) {
+                if ((mh = lh_MEM_new(mem_hash, mem_cmp)) == NULL) {
+                    OPENSSL_free(addr);
+                    OPENSSL_free(m);
+                    addr = NULL;
+                    goto err;
+                }
+            }
+
+            m->addr = addr;
+            m->file = file;
+            m->line = line;
+            m->num = num;
+            m->threadid = CRYPTO_THREAD_get_current_id();
+
+            if (order == break_order_num) {
+                /* BREAK HERE */
+                m->order = order;
+            }
+            m->order = order++;
+# ifndef OPENSSL_NO_CRYPTO_MDEBUG_BACKTRACE
+            m->array_siz = backtrace(m->array, OSSL_NELEM(m->array));
+# endif
+            m->time = time(NULL);
+
+            amim = (APP_INFO *)CRYPTO_THREAD_get_local(&appinfokey);
+            m->app_info = amim;
+            if (amim != NULL)
+                amim->references++;
+
+            if ((mm = lh_MEM_insert(mh, m)) != NULL) {
+                /* Not good, but don't sweat it */
+                if (mm->app_info != NULL) {
+                    mm->app_info->references--;
+                }
+                OPENSSL_free(mm);
+            }
+ err:
+            CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_ENABLE);
+        }
+        break;
+    }
+    return;
+}
+
+void CRYPTO_mem_debug_free(void *addr, int before_p,
+        const char *file, int line)
+{
+    MEM m, *mp;
+
+    switch (before_p) {
+    case 0:
+        if (addr == NULL)
+            break;
+
+        if (mem_check_on() && (mh != NULL)) {
+            CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_DISABLE);
+
+            m.addr = addr;
+            mp = lh_MEM_delete(mh, &m);
+            if (mp != NULL) {
+                app_info_free(mp->app_info);
+                OPENSSL_free(mp);
+            }
+
+            CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_ENABLE);
+        }
+        break;
+    case 1:
+        break;
+    }
+}
+
+void CRYPTO_mem_debug_realloc(void *addr1, void *addr2, size_t num,
+                              int before_p, const char *file, int line)
+{
+    MEM m, *mp;
+
+    switch (before_p) {
+    case 0:
+        break;
+    case 1:
+        if (addr2 == NULL)
+            break;
+
+        if (addr1 == NULL) {
+            CRYPTO_mem_debug_malloc(addr2, num, 128 | before_p, file, line);
+            break;
+        }
+
+        if (mem_check_on()) {
+            CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_DISABLE);
+
+            m.addr = addr1;
+            mp = lh_MEM_delete(mh, &m);
+            if (mp != NULL) {
+                mp->addr = addr2;
+                mp->num = num;
+#ifndef OPENSSL_NO_CRYPTO_MDEBUG_BACKTRACE
+                mp->array_siz = backtrace(mp->array, OSSL_NELEM(mp->array));
+#endif
+                (void)lh_MEM_insert(mh, mp);
+            }
+
+            CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_ENABLE);
+        }
+        break;
+    }
+    return;
+}
+
+typedef struct mem_leak_st {
+    BIO *bio;
+    int chunks;
+    long bytes;
+} MEM_LEAK;
+
+static void print_leak(const MEM *m, MEM_LEAK *l)
+{
+    char buf[1024];
+    char *bufp = buf;
+    APP_INFO *amip;
+    int ami_cnt;
+    struct tm *lcl = NULL;
+    /*
+     * Convert between CRYPTO_THREAD_ID (which could be anything at all) and
+     * a long. This may not be meaningful depending on what CRYPTO_THREAD_ID is
+     * but hopefully should give something sensible on most platforms
+     */
+    union {
+        CRYPTO_THREAD_ID tid;
+        unsigned long ltid;
+    } tid;
+    CRYPTO_THREAD_ID ti;
+
+#define BUF_REMAIN (sizeof buf - (size_t)(bufp - buf))
+
+    lcl = localtime(&m->time);
+    BIO_snprintf(bufp, BUF_REMAIN, "[%02d:%02d:%02d] ",
+                 lcl->tm_hour, lcl->tm_min, lcl->tm_sec);
+    bufp += strlen(bufp);
+
+    BIO_snprintf(bufp, BUF_REMAIN, "%5lu file=%s, line=%d, ",
+                 m->order, m->file, m->line);
+    bufp += strlen(bufp);
+
+    tid.ltid = 0;
+    tid.tid = m->threadid;
+    BIO_snprintf(bufp, BUF_REMAIN, "thread=%lu, ", tid.ltid);
+    bufp += strlen(bufp);
+
+    BIO_snprintf(bufp, BUF_REMAIN, "number=%d, address=%p\n",
+                 m->num, m->addr);
+    bufp += strlen(bufp);
+
+    BIO_puts(l->bio, buf);
+
+    l->chunks++;
+    l->bytes += m->num;
+
+    amip = m->app_info;
+    ami_cnt = 0;
+
+    if (amip) {
+        ti = amip->threadid;
+
+        do {
+            int buf_len;
+            int info_len;
+
+            ami_cnt++;
+            memset(buf, '>', ami_cnt);
+            tid.ltid = 0;
+            tid.tid = amip->threadid;
+            BIO_snprintf(buf + ami_cnt, sizeof buf - ami_cnt,
+                         " thread=%lu, file=%s, line=%d, info=\"",
+                         tid.ltid, amip->file,
+                         amip->line);
+            buf_len = strlen(buf);
+            info_len = strlen(amip->info);
+            if (128 - buf_len - 3 < info_len) {
+                memcpy(buf + buf_len, amip->info, 128 - buf_len - 3);
+                buf_len = 128 - 3;
+            } else {
+                OPENSSL_strlcpy(buf + buf_len, amip->info, sizeof buf - buf_len);
+                buf_len = strlen(buf);
+            }
+            BIO_snprintf(buf + buf_len, sizeof buf - buf_len, "\"\n");
+
+            BIO_puts(l->bio, buf);
+
+            amip = amip->next;
+        }
+        while (amip && CRYPTO_THREAD_compare_id(amip->threadid, ti));
+    }
+
+#ifndef OPENSSL_NO_CRYPTO_MDEBUG_BACKTRACE
+    {
+        size_t i;
+        char **strings = backtrace_symbols(m->array, m->array_siz);
+
+        for (i = 0; i < m->array_siz; i++)
+            fprintf(stderr, "##> %s\n", strings[i]);
+        free(strings);
+    }
+#endif
+}
+
+IMPLEMENT_LHASH_DOALL_ARG_CONST(MEM, MEM_LEAK);
+
+int CRYPTO_mem_leaks(BIO *b)
+{
+    MEM_LEAK ml;
+
+    /*
+     * OPENSSL_cleanup() will free the ex_data locks so we can't have any
+     * ex_data hanging around
+     */
+    bio_free_ex_data(b);
+
+    /* Ensure all resources are released */
+    OPENSSL_cleanup();
+
+    if (!RUN_ONCE(&memdbg_init, do_memdbg_init))
+        return -1;
+
+    CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_DISABLE);
+
+    ml.bio = b;
+    ml.bytes = 0;
+    ml.chunks = 0;
+    if (mh != NULL)
+        lh_MEM_doall_MEM_LEAK(mh, print_leak, &ml);
+
+    if (ml.chunks != 0) {
+        BIO_printf(b, "%ld bytes leaked in %d chunks\n", ml.bytes, ml.chunks);
+    } else {
+        /*
+         * Make sure that, if we found no leaks, memory-leak debugging itself
+         * does not introduce memory leaks (which might irritate external
+         * debugging tools). (When someone enables leak checking, but does not
+         * call this function, we declare it to be their fault.)
+         */
+        int old_mh_mode;
+
+        CRYPTO_THREAD_write_lock(malloc_lock);
+
+        /*
+         * avoid deadlock when lh_free() uses CRYPTO_mem_debug_free(), which uses
+         * mem_check_on
+         */
+        old_mh_mode = mh_mode;
+        mh_mode = CRYPTO_MEM_CHECK_OFF;
+
+        lh_MEM_free(mh);
+        mh = NULL;
+
+        mh_mode = old_mh_mode;
+        CRYPTO_THREAD_unlock(malloc_lock);
+    }
+    CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_OFF);
+
+    /* Clean up locks etc */
+    CRYPTO_THREAD_cleanup_local(&appinfokey);
+    CRYPTO_THREAD_lock_free(malloc_lock);
+    CRYPTO_THREAD_lock_free(long_malloc_lock);
+    malloc_lock = NULL;
+    long_malloc_lock = NULL;
+
+    return ml.chunks == 0 ? 1 : 0;
+}
+
+# ifndef OPENSSL_NO_STDIO
+int CRYPTO_mem_leaks_fp(FILE *fp)
+{
+    BIO *b;
+    int ret;
+
+    /*
+     * Need to turn off memory checking when allocated BIOs ... especially as
+     * we're creating them at a time when we're trying to check we've not
+     * left anything un-free()'d!!
+     */
+    CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_DISABLE);
+    b = BIO_new(BIO_s_file());
+    CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_ENABLE);
+    if (b == NULL)
+        return -1;
+    BIO_set_fp(b, fp, BIO_NOCLOSE);
+    ret = CRYPTO_mem_leaks(b);
+    BIO_free(b);
+    return ret;
+}
+# endif
+
+#endif
diff --git a/crypto/mem_sec.c b/crypto/mem_sec.c
new file mode 100644
index 000000000..4ccff34e5
--- /dev/null
+++ b/crypto/mem_sec.c
@@ -0,0 +1,581 @@
+/*
+ * Copyright 2015-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * Copyright 2004-2014, Akamai Technologies. All Rights Reserved.
+ * This file is distributed under the terms of the OpenSSL license.
+ */
+
+/*
+ * This file is in two halves. The first half implements the public API
+ * to be used by external consumers, and to be used by OpenSSL to store
+ * data in a "secure arena." The second half implements the secure arena.
+ * For details on that implementation, see below (look for uppercase
+ * "SECURE HEAP IMPLEMENTATION").
+ */
+#include 
+#include 
+
+#include 
+
+#if defined(OPENSSL_SYS_LINUX) || defined(OPENSSL_SYS_UNIX)
+# define IMPLEMENTED
+# include 
+# include 
+# include 
+# include 
+# include 
+# include 
+# include 
+# include 
+#endif
+
+#define CLEAR(p, s) OPENSSL_cleanse(p, s)
+#ifndef PAGE_SIZE
+# define PAGE_SIZE    4096
+#endif
+
+#ifdef IMPLEMENTED
+static size_t secure_mem_used;
+
+static int secure_mem_initialized;
+
+static CRYPTO_RWLOCK *sec_malloc_lock = NULL;
+
+/*
+ * These are the functions that must be implemented by a secure heap (sh).
+ */
+static int sh_init(size_t size, int minsize);
+static char *sh_malloc(size_t size);
+static void sh_free(char *ptr);
+static void sh_done(void);
+static size_t sh_actual_size(char *ptr);
+static int sh_allocated(const char *ptr);
+#endif
+
+int CRYPTO_secure_malloc_init(size_t size, int minsize)
+{
+#ifdef IMPLEMENTED
+    int ret = 0;
+
+    if (!secure_mem_initialized) {
+        sec_malloc_lock = CRYPTO_THREAD_lock_new();
+        if (sec_malloc_lock == NULL)
+            return 0;
+        ret = sh_init(size, minsize);
+        secure_mem_initialized = 1;
+    }
+
+    return ret;
+#else
+    return 0;
+#endif /* IMPLEMENTED */
+}
+
+int CRYPTO_secure_malloc_done()
+{
+#ifdef IMPLEMENTED
+    if (secure_mem_used == 0) {
+        sh_done();
+        secure_mem_initialized = 0;
+        CRYPTO_THREAD_lock_free(sec_malloc_lock);
+        return 1;
+    }
+#endif /* IMPLEMENTED */
+    return 0;
+}
+
+int CRYPTO_secure_malloc_initialized()
+{
+#ifdef IMPLEMENTED
+    return secure_mem_initialized;
+#else
+    return 0;
+#endif /* IMPLEMENTED */
+}
+
+void *CRYPTO_secure_malloc(size_t num, const char *file, int line)
+{
+#ifdef IMPLEMENTED
+    void *ret;
+    size_t actual_size;
+
+    if (!secure_mem_initialized) {
+        return CRYPTO_malloc(num, file, line);
+    }
+    CRYPTO_THREAD_write_lock(sec_malloc_lock);
+    ret = sh_malloc(num);
+    actual_size = ret ? sh_actual_size(ret) : 0;
+    secure_mem_used += actual_size;
+    CRYPTO_THREAD_unlock(sec_malloc_lock);
+    return ret;
+#else
+    return CRYPTO_malloc(num, file, line);
+#endif /* IMPLEMENTED */
+}
+
+void *CRYPTO_secure_zalloc(size_t num, const char *file, int line)
+{
+    void *ret = CRYPTO_secure_malloc(num, file, line);
+
+    if (ret != NULL)
+        memset(ret, 0, num);
+    return ret;
+}
+
+void CRYPTO_secure_free(void *ptr, const char *file, int line)
+{
+#ifdef IMPLEMENTED
+    size_t actual_size;
+
+    if (ptr == NULL)
+        return;
+    if (!CRYPTO_secure_allocated(ptr)) {
+        CRYPTO_free(ptr, file, line);
+        return;
+    }
+    CRYPTO_THREAD_write_lock(sec_malloc_lock);
+    actual_size = sh_actual_size(ptr);
+    CLEAR(ptr, actual_size);
+    secure_mem_used -= actual_size;
+    sh_free(ptr);
+    CRYPTO_THREAD_unlock(sec_malloc_lock);
+#else
+    CRYPTO_free(ptr, file, line);
+#endif /* IMPLEMENTED */
+}
+
+int CRYPTO_secure_allocated(const void *ptr)
+{
+#ifdef IMPLEMENTED
+    int ret;
+
+    if (!secure_mem_initialized)
+        return 0;
+    CRYPTO_THREAD_write_lock(sec_malloc_lock);
+    ret = sh_allocated(ptr);
+    CRYPTO_THREAD_unlock(sec_malloc_lock);
+    return ret;
+#else
+    return 0;
+#endif /* IMPLEMENTED */
+}
+
+size_t CRYPTO_secure_used()
+{
+#ifdef IMPLEMENTED
+    return secure_mem_used;
+#else
+    return 0;
+#endif /* IMPLEMENTED */
+}
+
+size_t CRYPTO_secure_actual_size(void *ptr)
+{
+#ifdef IMPLEMENTED
+    size_t actual_size;
+
+    CRYPTO_THREAD_write_lock(sec_malloc_lock);
+    actual_size = sh_actual_size(ptr);
+    CRYPTO_THREAD_unlock(sec_malloc_lock);
+    return actual_size;
+#else
+    return 0;
+#endif
+}
+/* END OF PAGE ...
+
+   ... START OF PAGE */
+
+/*
+ * SECURE HEAP IMPLEMENTATION
+ */
+#ifdef IMPLEMENTED
+
+
+/*
+ * The implementation provided here uses a fixed-sized mmap() heap,
+ * which is locked into memory, not written to core files, and protected
+ * on either side by an unmapped page, which will catch pointer overruns
+ * (or underruns) and an attempt to read data out of the secure heap.
+ * Free'd memory is zero'd or otherwise cleansed.
+ *
+ * This is a pretty standard buddy allocator.  We keep areas in a multiple
+ * of "sh.minsize" units.  The freelist and bitmaps are kept separately,
+ * so all (and only) data is kept in the mmap'd heap.
+ *
+ * This code assumes eight-bit bytes.  The numbers 3 and 7 are all over the
+ * place.
+ */
+
+#define ONE ((size_t)1)
+
+# define TESTBIT(t, b)  (t[(b) >> 3] &  (ONE << ((b) & 7)))
+# define SETBIT(t, b)   (t[(b) >> 3] |= (ONE << ((b) & 7)))
+# define CLEARBIT(t, b) (t[(b) >> 3] &= (0xFF & ~(ONE << ((b) & 7))))
+
+#define WITHIN_ARENA(p) \
+    ((char*)(p) >= sh.arena && (char*)(p) < &sh.arena[sh.arena_size])
+#define WITHIN_FREELIST(p) \
+    ((char*)(p) >= (char*)sh.freelist && (char*)(p) < (char*)&sh.freelist[sh.freelist_size])
+
+
+typedef struct sh_list_st
+{
+    struct sh_list_st *next;
+    struct sh_list_st **p_next;
+} SH_LIST;
+
+typedef struct sh_st
+{
+    char* map_result;
+    size_t map_size;
+    char *arena;
+    size_t arena_size;
+    char **freelist;
+    ossl_ssize_t freelist_size;
+    size_t minsize;
+    unsigned char *bittable;
+    unsigned char *bitmalloc;
+    size_t bittable_size; /* size in bits */
+} SH;
+
+static SH sh;
+
+static size_t sh_getlist(char *ptr)
+{
+    ossl_ssize_t list = sh.freelist_size - 1;
+    size_t bit = (sh.arena_size + ptr - sh.arena) / sh.minsize;
+
+    for (; bit; bit >>= 1, list--) {
+        if (TESTBIT(sh.bittable, bit))
+            break;
+        OPENSSL_assert((bit & 1) == 0);
+    }
+
+    return list;
+}
+
+
+static int sh_testbit(char *ptr, int list, unsigned char *table)
+{
+    size_t bit;
+
+    OPENSSL_assert(list >= 0 && list < sh.freelist_size);
+    OPENSSL_assert(((ptr - sh.arena) & ((sh.arena_size >> list) - 1)) == 0);
+    bit = (ONE << list) + ((ptr - sh.arena) / (sh.arena_size >> list));
+    OPENSSL_assert(bit > 0 && bit < sh.bittable_size);
+    return TESTBIT(table, bit);
+}
+
+static void sh_clearbit(char *ptr, int list, unsigned char *table)
+{
+    size_t bit;
+
+    OPENSSL_assert(list >= 0 && list < sh.freelist_size);
+    OPENSSL_assert(((ptr - sh.arena) & ((sh.arena_size >> list) - 1)) == 0);
+    bit = (ONE << list) + ((ptr - sh.arena) / (sh.arena_size >> list));
+    OPENSSL_assert(bit > 0 && bit < sh.bittable_size);
+    OPENSSL_assert(TESTBIT(table, bit));
+    CLEARBIT(table, bit);
+}
+
+static void sh_setbit(char *ptr, int list, unsigned char *table)
+{
+    size_t bit;
+
+    OPENSSL_assert(list >= 0 && list < sh.freelist_size);
+    OPENSSL_assert(((ptr - sh.arena) & ((sh.arena_size >> list) - 1)) == 0);
+    bit = (ONE << list) + ((ptr - sh.arena) / (sh.arena_size >> list));
+    OPENSSL_assert(bit > 0 && bit < sh.bittable_size);
+    OPENSSL_assert(!TESTBIT(table, bit));
+    SETBIT(table, bit);
+}
+
+static void sh_add_to_list(char **list, char *ptr)
+{
+    SH_LIST *temp;
+
+    OPENSSL_assert(WITHIN_FREELIST(list));
+    OPENSSL_assert(WITHIN_ARENA(ptr));
+
+    temp = (SH_LIST *)ptr;
+    temp->next = *(SH_LIST **)list;
+    OPENSSL_assert(temp->next == NULL || WITHIN_ARENA(temp->next));
+    temp->p_next = (SH_LIST **)list;
+
+    if (temp->next != NULL) {
+        OPENSSL_assert((char **)temp->next->p_next == list);
+        temp->next->p_next = &(temp->next);
+    }
+
+    *list = ptr;
+}
+
+static void sh_remove_from_list(char *ptr)
+{
+    SH_LIST *temp, *temp2;
+
+    temp = (SH_LIST *)ptr;
+    if (temp->next != NULL)
+        temp->next->p_next = temp->p_next;
+    *temp->p_next = temp->next;
+    if (temp->next == NULL)
+        return;
+
+    temp2 = temp->next;
+    OPENSSL_assert(WITHIN_FREELIST(temp2->p_next) || WITHIN_ARENA(temp2->p_next));
+}
+
+
+static int sh_init(size_t size, int minsize)
+{
+    int i, ret;
+    size_t pgsize;
+    size_t aligned;
+
+    memset(&sh, 0, sizeof sh);
+
+    /* make sure size and minsize are powers of 2 */
+    OPENSSL_assert(size > 0);
+    OPENSSL_assert((size & (size - 1)) == 0);
+    OPENSSL_assert(minsize > 0);
+    OPENSSL_assert((minsize & (minsize - 1)) == 0);
+    if (size <= 0 || (size & (size - 1)) != 0)
+        goto err;
+    if (minsize <= 0 || (minsize & (minsize - 1)) != 0)
+        goto err;
+
+    sh.arena_size = size;
+    sh.minsize = minsize;
+    sh.bittable_size = (sh.arena_size / sh.minsize) * 2;
+
+    sh.freelist_size = -1;
+    for (i = sh.bittable_size; i; i >>= 1)
+        sh.freelist_size++;
+
+    sh.freelist = OPENSSL_zalloc(sh.freelist_size * sizeof (char *));
+    OPENSSL_assert(sh.freelist != NULL);
+    if (sh.freelist == NULL)
+        goto err;
+
+    sh.bittable = OPENSSL_zalloc(sh.bittable_size >> 3);
+    OPENSSL_assert(sh.bittable != NULL);
+    if (sh.bittable == NULL)
+        goto err;
+
+    sh.bitmalloc = OPENSSL_zalloc(sh.bittable_size >> 3);
+    OPENSSL_assert(sh.bitmalloc != NULL);
+    if (sh.bitmalloc == NULL)
+        goto err;
+
+    /* Allocate space for heap, and two extra pages as guards */
+#if defined(_SC_PAGE_SIZE) || defined (_SC_PAGESIZE)
+    {
+# if defined(_SC_PAGE_SIZE)
+        long tmppgsize = sysconf(_SC_PAGE_SIZE);
+# else
+        long tmppgsize = sysconf(_SC_PAGESIZE);
+# endif
+        if (tmppgsize < 1)
+            pgsize = PAGE_SIZE;
+        else
+            pgsize = (size_t)tmppgsize;
+    }
+#else
+    pgsize = PAGE_SIZE;
+#endif
+    sh.map_size = pgsize + sh.arena_size + pgsize;
+    if (1) {
+#ifdef MAP_ANON
+        sh.map_result = mmap(NULL, sh.map_size,
+                             PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, -1, 0);
+    } else {
+#endif
+        int fd;
+
+        sh.map_result = MAP_FAILED;
+        if ((fd = open("/dev/zero", O_RDWR)) >= 0) {
+            sh.map_result = mmap(NULL, sh.map_size,
+                                 PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0);
+            close(fd);
+        }
+    }
+    OPENSSL_assert(sh.map_result != MAP_FAILED);
+    if (sh.map_result == MAP_FAILED)
+        goto err;
+    sh.arena = (char *)(sh.map_result + pgsize);
+    sh_setbit(sh.arena, 0, sh.bittable);
+    sh_add_to_list(&sh.freelist[0], sh.arena);
+
+    /* Now try to add guard pages and lock into memory. */
+    ret = 1;
+
+    /* Starting guard is already aligned from mmap. */
+    if (mprotect(sh.map_result, pgsize, PROT_NONE) < 0)
+        ret = 2;
+
+    /* Ending guard page - need to round up to page boundary */
+    aligned = (pgsize + sh.arena_size + (pgsize - 1)) & ~(pgsize - 1);
+    if (mprotect(sh.map_result + aligned, pgsize, PROT_NONE) < 0)
+        ret = 2;
+
+    if (mlock(sh.arena, sh.arena_size) < 0)
+        ret = 2;
+#ifdef MADV_DONTDUMP
+    if (madvise(sh.arena, sh.arena_size, MADV_DONTDUMP) < 0)
+        ret = 2;
+#endif
+
+    return ret;
+
+ err:
+    sh_done();
+    return 0;
+}
+
+static void sh_done()
+{
+    OPENSSL_free(sh.freelist);
+    OPENSSL_free(sh.bittable);
+    OPENSSL_free(sh.bitmalloc);
+    if (sh.map_result != NULL && sh.map_size)
+        munmap(sh.map_result, sh.map_size);
+    memset(&sh, 0, sizeof sh);
+}
+
+static int sh_allocated(const char *ptr)
+{
+    return WITHIN_ARENA(ptr) ? 1 : 0;
+}
+
+static char *sh_find_my_buddy(char *ptr, int list)
+{
+    size_t bit;
+    char *chunk = NULL;
+
+    bit = (ONE << list) + (ptr - sh.arena) / (sh.arena_size >> list);
+    bit ^= 1;
+
+    if (TESTBIT(sh.bittable, bit) && !TESTBIT(sh.bitmalloc, bit))
+        chunk = sh.arena + ((bit & ((ONE << list) - 1)) * (sh.arena_size >> list));
+
+    return chunk;
+}
+
+static char *sh_malloc(size_t size)
+{
+    ossl_ssize_t list, slist;
+    size_t i;
+    char *chunk;
+
+    list = sh.freelist_size - 1;
+    for (i = sh.minsize; i < size; i <<= 1)
+        list--;
+    if (list < 0)
+        return NULL;
+
+    /* try to find a larger entry to split */
+    for (slist = list; slist >= 0; slist--)
+        if (sh.freelist[slist] != NULL)
+            break;
+    if (slist < 0)
+        return NULL;
+
+    /* split larger entry */
+    while (slist != list) {
+        char *temp = sh.freelist[slist];
+
+        /* remove from bigger list */
+        OPENSSL_assert(!sh_testbit(temp, slist, sh.bitmalloc));
+        sh_clearbit(temp, slist, sh.bittable);
+        sh_remove_from_list(temp);
+        OPENSSL_assert(temp != sh.freelist[slist]);
+
+        /* done with bigger list */
+        slist++;
+
+        /* add to smaller list */
+        OPENSSL_assert(!sh_testbit(temp, slist, sh.bitmalloc));
+        sh_setbit(temp, slist, sh.bittable);
+        sh_add_to_list(&sh.freelist[slist], temp);
+        OPENSSL_assert(sh.freelist[slist] == temp);
+
+        /* split in 2 */
+        temp += sh.arena_size >> slist;
+        OPENSSL_assert(!sh_testbit(temp, slist, sh.bitmalloc));
+        sh_setbit(temp, slist, sh.bittable);
+        sh_add_to_list(&sh.freelist[slist], temp);
+        OPENSSL_assert(sh.freelist[slist] == temp);
+
+        OPENSSL_assert(temp-(sh.arena_size >> slist) == sh_find_my_buddy(temp, slist));
+    }
+
+    /* peel off memory to hand back */
+    chunk = sh.freelist[list];
+    OPENSSL_assert(sh_testbit(chunk, list, sh.bittable));
+    sh_setbit(chunk, list, sh.bitmalloc);
+    sh_remove_from_list(chunk);
+
+    OPENSSL_assert(WITHIN_ARENA(chunk));
+
+    return chunk;
+}
+
+static void sh_free(char *ptr)
+{
+    size_t list;
+    char *buddy;
+
+    if (ptr == NULL)
+        return;
+    OPENSSL_assert(WITHIN_ARENA(ptr));
+    if (!WITHIN_ARENA(ptr))
+        return;
+
+    list = sh_getlist(ptr);
+    OPENSSL_assert(sh_testbit(ptr, list, sh.bittable));
+    sh_clearbit(ptr, list, sh.bitmalloc);
+    sh_add_to_list(&sh.freelist[list], ptr);
+
+    /* Try to coalesce two adjacent free areas. */
+    while ((buddy = sh_find_my_buddy(ptr, list)) != NULL) {
+        OPENSSL_assert(ptr == sh_find_my_buddy(buddy, list));
+        OPENSSL_assert(ptr != NULL);
+        OPENSSL_assert(!sh_testbit(ptr, list, sh.bitmalloc));
+        sh_clearbit(ptr, list, sh.bittable);
+        sh_remove_from_list(ptr);
+        OPENSSL_assert(!sh_testbit(ptr, list, sh.bitmalloc));
+        sh_clearbit(buddy, list, sh.bittable);
+        sh_remove_from_list(buddy);
+
+        list--;
+
+        if (ptr > buddy)
+            ptr = buddy;
+
+        OPENSSL_assert(!sh_testbit(ptr, list, sh.bitmalloc));
+        sh_setbit(ptr, list, sh.bittable);
+        sh_add_to_list(&sh.freelist[list], ptr);
+        OPENSSL_assert(sh.freelist[list] == ptr);
+    }
+}
+
+static size_t sh_actual_size(char *ptr)
+{
+    int list;
+
+    OPENSSL_assert(WITHIN_ARENA(ptr));
+    if (!WITHIN_ARENA(ptr))
+        return 0;
+    list = sh_getlist(ptr);
+    OPENSSL_assert(sh_testbit(ptr, list, sh.bittable));
+    return sh.arena_size / (ONE << list);
+}
+#endif /* IMPLEMENTED */
diff --git a/crypto/modes/asm/aesni-gcm-x86_64.pl b/crypto/modes/asm/aesni-gcm-x86_64.pl
new file mode 100644
index 000000000..5ad62b397
--- /dev/null
+++ b/crypto/modes/asm/aesni-gcm-x86_64.pl
@@ -0,0 +1,1106 @@
+#! /usr/bin/env perl
+# Copyright 2013-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+#
+# ====================================================================
+# Written by Andy Polyakov  for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+#
+#
+# AES-NI-CTR+GHASH stitch.
+#
+# February 2013
+#
+# OpenSSL GCM implementation is organized in such way that its
+# performance is rather close to the sum of its streamed components,
+# in the context parallelized AES-NI CTR and modulo-scheduled
+# PCLMULQDQ-enabled GHASH. Unfortunately, as no stitch implementation
+# was observed to perform significantly better than the sum of the
+# components on contemporary CPUs, the effort was deemed impossible to
+# justify. This module is based on combination of Intel submissions,
+# [1] and [2], with MOVBE twist suggested by Ilya Albrekht and Max
+# Locktyukhin of Intel Corp. who verified that it reduces shuffles
+# pressure with notable relative improvement, achieving 1.0 cycle per
+# byte processed with 128-bit key on Haswell processor, 0.74 - on
+# Broadwell, 0.63 - on Skylake... [Mentioned results are raw profiled
+# measurements for favourable packet size, one divisible by 96.
+# Applications using the EVP interface will observe a few percent
+# worse performance.]
+#
+# [1] http://rt.openssl.org/Ticket/Display.html?id=2900&user=guest&pass=guest
+# [2] http://www.intel.com/content/dam/www/public/us/en/documents/software-support/enabling-high-performance-gcm.pdf
+
+$flavour = shift;
+$output  = shift;
+if ($flavour =~ /\./) { $output = $flavour; undef $flavour; }
+
+$win64=0; $win64=1 if ($flavour =~ /[nm]asm|mingw64/ || $output =~ /\.asm$/);
+
+$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+( $xlate="${dir}x86_64-xlate.pl" and -f $xlate ) or
+( $xlate="${dir}../../perlasm/x86_64-xlate.pl" and -f $xlate) or
+die "can't locate x86_64-xlate.pl";
+
+if (`$ENV{CC} -Wa,-v -c -o /dev/null -x assembler /dev/null 2>&1`
+		=~ /GNU assembler version ([2-9]\.[0-9]+)/) {
+	$avx = ($1>=2.20) + ($1>=2.22);
+}
+
+if (!$avx && $win64 && ($flavour =~ /nasm/ || $ENV{ASM} =~ /nasm/) &&
+	    `nasm -v 2>&1` =~ /NASM version ([2-9]\.[0-9]+)/) {
+	$avx = ($1>=2.09) + ($1>=2.10);
+}
+
+if (!$avx && $win64 && ($flavour =~ /masm/ || $ENV{ASM} =~ /ml64/) &&
+	    `ml64 2>&1` =~ /Version ([0-9]+)\./) {
+	$avx = ($1>=10) + ($1>=11);
+}
+
+if (!$avx && `$ENV{CC} -v 2>&1` =~ /((?:^clang|LLVM) version|.*based on LLVM) ([3-9]\.[0-9]+)/) {
+	$avx = ($2>=3.0) + ($2>3.0);
+}
+
+open OUT,"| \"$^X\" \"$xlate\" $flavour \"$output\"";
+*STDOUT=*OUT;
+
+if ($avx>1) {{{
+
+($inp,$out,$len,$key,$ivp,$Xip)=("%rdi","%rsi","%rdx","%rcx","%r8","%r9");
+
+($Ii,$T1,$T2,$Hkey,
+ $Z0,$Z1,$Z2,$Z3,$Xi) = map("%xmm$_",(0..8));
+
+($inout0,$inout1,$inout2,$inout3,$inout4,$inout5,$rndkey) = map("%xmm$_",(9..15));
+
+($counter,$rounds,$ret,$const,$in0,$end0)=("%ebx","%ebp","%r10","%r11","%r14","%r15");
+
+$code=<<___;
+.text
+
+.type	_aesni_ctr32_ghash_6x,\@abi-omnipotent
+.align	32
+_aesni_ctr32_ghash_6x:
+	vmovdqu		0x20($const),$T2	# borrow $T2, .Lone_msb
+	sub		\$6,$len
+	vpxor		$Z0,$Z0,$Z0		# $Z0   = 0
+	vmovdqu		0x00-0x80($key),$rndkey
+	vpaddb		$T2,$T1,$inout1
+	vpaddb		$T2,$inout1,$inout2
+	vpaddb		$T2,$inout2,$inout3
+	vpaddb		$T2,$inout3,$inout4
+	vpaddb		$T2,$inout4,$inout5
+	vpxor		$rndkey,$T1,$inout0
+	vmovdqu		$Z0,16+8(%rsp)		# "$Z3" = 0
+	jmp		.Loop6x
+
+.align	32
+.Loop6x:
+	add		\$`6<<24`,$counter
+	jc		.Lhandle_ctr32		# discard $inout[1-5]?
+	vmovdqu		0x00-0x20($Xip),$Hkey	# $Hkey^1
+	  vpaddb	$T2,$inout5,$T1		# next counter value
+	  vpxor		$rndkey,$inout1,$inout1
+	  vpxor		$rndkey,$inout2,$inout2
+
+.Lresume_ctr32:
+	vmovdqu		$T1,($ivp)		# save next counter value
+	vpclmulqdq	\$0x10,$Hkey,$Z3,$Z1
+	  vpxor		$rndkey,$inout3,$inout3
+	  vmovups	0x10-0x80($key),$T2	# borrow $T2 for $rndkey
+	vpclmulqdq	\$0x01,$Hkey,$Z3,$Z2
+
+	# At this point, the current block of 96 (0x60) bytes has already been
+	# loaded into registers. Concurrently with processing it, we want to
+	# load the next 96 bytes of input for the next round. Obviously, we can
+	# only do this if there are at least 96 more bytes of input beyond the
+	# input we're currently processing, or else we'd read past the end of
+	# the input buffer. Here, we set |%r12| to 96 if there are at least 96
+	# bytes of input beyond the 96 bytes we're already processing, and we
+	# set |%r12| to 0 otherwise. In the case where we set |%r12| to 96,
+	# we'll read in the next block so that it is in registers for the next
+	# loop iteration. In the case where we set |%r12| to 0, we'll re-read
+	# the current block and then ignore what we re-read.
+	#
+	# At this point, |$in0| points to the current (already read into
+	# registers) block, and |$end0| points to 2*96 bytes before the end of
+	# the input. Thus, |$in0| > |$end0| means that we do not have the next
+	# 96-byte block to read in, and |$in0| <= |$end0| means we do.
+	xor		%r12,%r12
+	cmp		$in0,$end0
+
+	  vaesenc	$T2,$inout0,$inout0
+	vmovdqu		0x30+8(%rsp),$Ii	# I[4]
+	  vpxor		$rndkey,$inout4,$inout4
+	vpclmulqdq	\$0x00,$Hkey,$Z3,$T1
+	  vaesenc	$T2,$inout1,$inout1
+	  vpxor		$rndkey,$inout5,$inout5
+	setnc		%r12b
+	vpclmulqdq	\$0x11,$Hkey,$Z3,$Z3
+	  vaesenc	$T2,$inout2,$inout2
+	vmovdqu		0x10-0x20($Xip),$Hkey	# $Hkey^2
+	neg		%r12
+	  vaesenc	$T2,$inout3,$inout3
+	 vpxor		$Z1,$Z2,$Z2
+	vpclmulqdq	\$0x00,$Hkey,$Ii,$Z1
+	 vpxor		$Z0,$Xi,$Xi		# modulo-scheduled
+	  vaesenc	$T2,$inout4,$inout4
+	 vpxor		$Z1,$T1,$Z0
+	and		\$0x60,%r12
+	  vmovups	0x20-0x80($key),$rndkey
+	vpclmulqdq	\$0x10,$Hkey,$Ii,$T1
+	  vaesenc	$T2,$inout5,$inout5
+
+	vpclmulqdq	\$0x01,$Hkey,$Ii,$T2
+	lea		($in0,%r12),$in0
+	  vaesenc	$rndkey,$inout0,$inout0
+	 vpxor		16+8(%rsp),$Xi,$Xi	# modulo-scheduled [vpxor $Z3,$Xi,$Xi]
+	vpclmulqdq	\$0x11,$Hkey,$Ii,$Hkey
+	 vmovdqu	0x40+8(%rsp),$Ii	# I[3]
+	  vaesenc	$rndkey,$inout1,$inout1
+	movbe		0x58($in0),%r13
+	  vaesenc	$rndkey,$inout2,$inout2
+	movbe		0x50($in0),%r12
+	  vaesenc	$rndkey,$inout3,$inout3
+	mov		%r13,0x20+8(%rsp)
+	  vaesenc	$rndkey,$inout4,$inout4
+	mov		%r12,0x28+8(%rsp)
+	vmovdqu		0x30-0x20($Xip),$Z1	# borrow $Z1 for $Hkey^3
+	  vaesenc	$rndkey,$inout5,$inout5
+
+	  vmovups	0x30-0x80($key),$rndkey
+	 vpxor		$T1,$Z2,$Z2
+	vpclmulqdq	\$0x00,$Z1,$Ii,$T1
+	  vaesenc	$rndkey,$inout0,$inout0
+	 vpxor		$T2,$Z2,$Z2
+	vpclmulqdq	\$0x10,$Z1,$Ii,$T2
+	  vaesenc	$rndkey,$inout1,$inout1
+	 vpxor		$Hkey,$Z3,$Z3
+	vpclmulqdq	\$0x01,$Z1,$Ii,$Hkey
+	  vaesenc	$rndkey,$inout2,$inout2
+	vpclmulqdq	\$0x11,$Z1,$Ii,$Z1
+	 vmovdqu	0x50+8(%rsp),$Ii	# I[2]
+	  vaesenc	$rndkey,$inout3,$inout3
+	  vaesenc	$rndkey,$inout4,$inout4
+	 vpxor		$T1,$Z0,$Z0
+	vmovdqu		0x40-0x20($Xip),$T1	# borrow $T1 for $Hkey^4
+	  vaesenc	$rndkey,$inout5,$inout5
+
+	  vmovups	0x40-0x80($key),$rndkey
+	 vpxor		$T2,$Z2,$Z2
+	vpclmulqdq	\$0x00,$T1,$Ii,$T2
+	  vaesenc	$rndkey,$inout0,$inout0
+	 vpxor		$Hkey,$Z2,$Z2
+	vpclmulqdq	\$0x10,$T1,$Ii,$Hkey
+	  vaesenc	$rndkey,$inout1,$inout1
+	movbe		0x48($in0),%r13
+	 vpxor		$Z1,$Z3,$Z3
+	vpclmulqdq	\$0x01,$T1,$Ii,$Z1
+	  vaesenc	$rndkey,$inout2,$inout2
+	movbe		0x40($in0),%r12
+	vpclmulqdq	\$0x11,$T1,$Ii,$T1
+	 vmovdqu	0x60+8(%rsp),$Ii	# I[1]
+	  vaesenc	$rndkey,$inout3,$inout3
+	mov		%r13,0x30+8(%rsp)
+	  vaesenc	$rndkey,$inout4,$inout4
+	mov		%r12,0x38+8(%rsp)
+	 vpxor		$T2,$Z0,$Z0
+	vmovdqu		0x60-0x20($Xip),$T2	# borrow $T2 for $Hkey^5
+	  vaesenc	$rndkey,$inout5,$inout5
+
+	  vmovups	0x50-0x80($key),$rndkey
+	 vpxor		$Hkey,$Z2,$Z2
+	vpclmulqdq	\$0x00,$T2,$Ii,$Hkey
+	  vaesenc	$rndkey,$inout0,$inout0
+	 vpxor		$Z1,$Z2,$Z2
+	vpclmulqdq	\$0x10,$T2,$Ii,$Z1
+	  vaesenc	$rndkey,$inout1,$inout1
+	movbe		0x38($in0),%r13
+	 vpxor		$T1,$Z3,$Z3
+	vpclmulqdq	\$0x01,$T2,$Ii,$T1
+	 vpxor		0x70+8(%rsp),$Xi,$Xi	# accumulate I[0]
+	  vaesenc	$rndkey,$inout2,$inout2
+	movbe		0x30($in0),%r12
+	vpclmulqdq	\$0x11,$T2,$Ii,$T2
+	  vaesenc	$rndkey,$inout3,$inout3
+	mov		%r13,0x40+8(%rsp)
+	  vaesenc	$rndkey,$inout4,$inout4
+	mov		%r12,0x48+8(%rsp)
+	 vpxor		$Hkey,$Z0,$Z0
+	 vmovdqu	0x70-0x20($Xip),$Hkey	# $Hkey^6
+	  vaesenc	$rndkey,$inout5,$inout5
+
+	  vmovups	0x60-0x80($key),$rndkey
+	 vpxor		$Z1,$Z2,$Z2
+	vpclmulqdq	\$0x10,$Hkey,$Xi,$Z1
+	  vaesenc	$rndkey,$inout0,$inout0
+	 vpxor		$T1,$Z2,$Z2
+	vpclmulqdq	\$0x01,$Hkey,$Xi,$T1
+	  vaesenc	$rndkey,$inout1,$inout1
+	movbe		0x28($in0),%r13
+	 vpxor		$T2,$Z3,$Z3
+	vpclmulqdq	\$0x00,$Hkey,$Xi,$T2
+	  vaesenc	$rndkey,$inout2,$inout2
+	movbe		0x20($in0),%r12
+	vpclmulqdq	\$0x11,$Hkey,$Xi,$Xi
+	  vaesenc	$rndkey,$inout3,$inout3
+	mov		%r13,0x50+8(%rsp)
+	  vaesenc	$rndkey,$inout4,$inout4
+	mov		%r12,0x58+8(%rsp)
+	vpxor		$Z1,$Z2,$Z2
+	  vaesenc	$rndkey,$inout5,$inout5
+	vpxor		$T1,$Z2,$Z2
+
+	  vmovups	0x70-0x80($key),$rndkey
+	vpslldq		\$8,$Z2,$Z1
+	vpxor		$T2,$Z0,$Z0
+	vmovdqu		0x10($const),$Hkey	# .Lpoly
+
+	  vaesenc	$rndkey,$inout0,$inout0
+	vpxor		$Xi,$Z3,$Z3
+	  vaesenc	$rndkey,$inout1,$inout1
+	vpxor		$Z1,$Z0,$Z0
+	movbe		0x18($in0),%r13
+	  vaesenc	$rndkey,$inout2,$inout2
+	movbe		0x10($in0),%r12
+	vpalignr	\$8,$Z0,$Z0,$Ii		# 1st phase
+	vpclmulqdq	\$0x10,$Hkey,$Z0,$Z0
+	mov		%r13,0x60+8(%rsp)
+	  vaesenc	$rndkey,$inout3,$inout3
+	mov		%r12,0x68+8(%rsp)
+	  vaesenc	$rndkey,$inout4,$inout4
+	  vmovups	0x80-0x80($key),$T1	# borrow $T1 for $rndkey
+	  vaesenc	$rndkey,$inout5,$inout5
+
+	  vaesenc	$T1,$inout0,$inout0
+	  vmovups	0x90-0x80($key),$rndkey
+	  vaesenc	$T1,$inout1,$inout1
+	vpsrldq		\$8,$Z2,$Z2
+	  vaesenc	$T1,$inout2,$inout2
+	vpxor		$Z2,$Z3,$Z3
+	  vaesenc	$T1,$inout3,$inout3
+	vpxor		$Ii,$Z0,$Z0
+	movbe		0x08($in0),%r13
+	  vaesenc	$T1,$inout4,$inout4
+	movbe		0x00($in0),%r12
+	  vaesenc	$T1,$inout5,$inout5
+	  vmovups	0xa0-0x80($key),$T1
+	  cmp		\$11,$rounds
+	  jb		.Lenc_tail		# 128-bit key
+
+	  vaesenc	$rndkey,$inout0,$inout0
+	  vaesenc	$rndkey,$inout1,$inout1
+	  vaesenc	$rndkey,$inout2,$inout2
+	  vaesenc	$rndkey,$inout3,$inout3
+	  vaesenc	$rndkey,$inout4,$inout4
+	  vaesenc	$rndkey,$inout5,$inout5
+
+	  vaesenc	$T1,$inout0,$inout0
+	  vaesenc	$T1,$inout1,$inout1
+	  vaesenc	$T1,$inout2,$inout2
+	  vaesenc	$T1,$inout3,$inout3
+	  vaesenc	$T1,$inout4,$inout4
+	  vmovups	0xb0-0x80($key),$rndkey
+	  vaesenc	$T1,$inout5,$inout5
+	  vmovups	0xc0-0x80($key),$T1
+	  je		.Lenc_tail		# 192-bit key
+
+	  vaesenc	$rndkey,$inout0,$inout0
+	  vaesenc	$rndkey,$inout1,$inout1
+	  vaesenc	$rndkey,$inout2,$inout2
+	  vaesenc	$rndkey,$inout3,$inout3
+	  vaesenc	$rndkey,$inout4,$inout4
+	  vaesenc	$rndkey,$inout5,$inout5
+
+	  vaesenc	$T1,$inout0,$inout0
+	  vaesenc	$T1,$inout1,$inout1
+	  vaesenc	$T1,$inout2,$inout2
+	  vaesenc	$T1,$inout3,$inout3
+	  vaesenc	$T1,$inout4,$inout4
+	  vmovups	0xd0-0x80($key),$rndkey
+	  vaesenc	$T1,$inout5,$inout5
+	  vmovups	0xe0-0x80($key),$T1
+	  jmp		.Lenc_tail		# 256-bit key
+
+.align	32
+.Lhandle_ctr32:
+	vmovdqu		($const),$Ii		# borrow $Ii for .Lbswap_mask
+	  vpshufb	$Ii,$T1,$Z2		# byte-swap counter
+	  vmovdqu	0x30($const),$Z1	# borrow $Z1, .Ltwo_lsb
+	  vpaddd	0x40($const),$Z2,$inout1	# .Lone_lsb
+	  vpaddd	$Z1,$Z2,$inout2
+	vmovdqu		0x00-0x20($Xip),$Hkey	# $Hkey^1
+	  vpaddd	$Z1,$inout1,$inout3
+	  vpshufb	$Ii,$inout1,$inout1
+	  vpaddd	$Z1,$inout2,$inout4
+	  vpshufb	$Ii,$inout2,$inout2
+	  vpxor		$rndkey,$inout1,$inout1
+	  vpaddd	$Z1,$inout3,$inout5
+	  vpshufb	$Ii,$inout3,$inout3
+	  vpxor		$rndkey,$inout2,$inout2
+	  vpaddd	$Z1,$inout4,$T1		# byte-swapped next counter value
+	  vpshufb	$Ii,$inout4,$inout4
+	  vpshufb	$Ii,$inout5,$inout5
+	  vpshufb	$Ii,$T1,$T1		# next counter value
+	jmp		.Lresume_ctr32
+
+.align	32
+.Lenc_tail:
+	  vaesenc	$rndkey,$inout0,$inout0
+	vmovdqu		$Z3,16+8(%rsp)		# postpone vpxor $Z3,$Xi,$Xi
+	vpalignr	\$8,$Z0,$Z0,$Xi		# 2nd phase
+	  vaesenc	$rndkey,$inout1,$inout1
+	vpclmulqdq	\$0x10,$Hkey,$Z0,$Z0
+	  vpxor		0x00($inp),$T1,$T2
+	  vaesenc	$rndkey,$inout2,$inout2
+	  vpxor		0x10($inp),$T1,$Ii
+	  vaesenc	$rndkey,$inout3,$inout3
+	  vpxor		0x20($inp),$T1,$Z1
+	  vaesenc	$rndkey,$inout4,$inout4
+	  vpxor		0x30($inp),$T1,$Z2
+	  vaesenc	$rndkey,$inout5,$inout5
+	  vpxor		0x40($inp),$T1,$Z3
+	  vpxor		0x50($inp),$T1,$Hkey
+	  vmovdqu	($ivp),$T1		# load next counter value
+
+	  vaesenclast	$T2,$inout0,$inout0
+	  vmovdqu	0x20($const),$T2	# borrow $T2, .Lone_msb
+	  vaesenclast	$Ii,$inout1,$inout1
+	 vpaddb		$T2,$T1,$Ii
+	mov		%r13,0x70+8(%rsp)
+	lea		0x60($inp),$inp
+	  vaesenclast	$Z1,$inout2,$inout2
+	 vpaddb		$T2,$Ii,$Z1
+	mov		%r12,0x78+8(%rsp)
+	lea		0x60($out),$out
+	  vmovdqu	0x00-0x80($key),$rndkey
+	  vaesenclast	$Z2,$inout3,$inout3
+	 vpaddb		$T2,$Z1,$Z2
+	  vaesenclast	$Z3, $inout4,$inout4
+	 vpaddb		$T2,$Z2,$Z3
+	  vaesenclast	$Hkey,$inout5,$inout5
+	 vpaddb		$T2,$Z3,$Hkey
+
+	add		\$0x60,$ret
+	sub		\$0x6,$len
+	jc		.L6x_done
+
+	  vmovups	$inout0,-0x60($out)	# save output
+	 vpxor		$rndkey,$T1,$inout0
+	  vmovups	$inout1,-0x50($out)
+	 vmovdqa	$Ii,$inout1		# 0 latency
+	  vmovups	$inout2,-0x40($out)
+	 vmovdqa	$Z1,$inout2		# 0 latency
+	  vmovups	$inout3,-0x30($out)
+	 vmovdqa	$Z2,$inout3		# 0 latency
+	  vmovups	$inout4,-0x20($out)
+	 vmovdqa	$Z3,$inout4		# 0 latency
+	  vmovups	$inout5,-0x10($out)
+	 vmovdqa	$Hkey,$inout5		# 0 latency
+	vmovdqu		0x20+8(%rsp),$Z3	# I[5]
+	jmp		.Loop6x
+
+.L6x_done:
+	vpxor		16+8(%rsp),$Xi,$Xi	# modulo-scheduled
+	vpxor		$Z0,$Xi,$Xi		# modulo-scheduled
+
+	ret
+.size	_aesni_ctr32_ghash_6x,.-_aesni_ctr32_ghash_6x
+___
+######################################################################
+#
+# size_t aesni_gcm_[en|de]crypt(const void *inp, void *out, size_t len,
+#		const AES_KEY *key, unsigned char iv[16],
+#		struct { u128 Xi,H,Htbl[9]; } *Xip);
+$code.=<<___;
+.globl	aesni_gcm_decrypt
+.type	aesni_gcm_decrypt,\@function,6
+.align	32
+aesni_gcm_decrypt:
+	xor	$ret,$ret
+
+	# We call |_aesni_ctr32_ghash_6x|, which requires at least 96 (0x60)
+	# bytes of input.
+	cmp	\$0x60,$len			# minimal accepted length
+	jb	.Lgcm_dec_abort
+
+	lea	(%rsp),%rax			# save stack pointer
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+___
+$code.=<<___ if ($win64);
+	lea	-0xa8(%rsp),%rsp
+	movaps	%xmm6,-0xd8(%rax)
+	movaps	%xmm7,-0xc8(%rax)
+	movaps	%xmm8,-0xb8(%rax)
+	movaps	%xmm9,-0xa8(%rax)
+	movaps	%xmm10,-0x98(%rax)
+	movaps	%xmm11,-0x88(%rax)
+	movaps	%xmm12,-0x78(%rax)
+	movaps	%xmm13,-0x68(%rax)
+	movaps	%xmm14,-0x58(%rax)
+	movaps	%xmm15,-0x48(%rax)
+.Lgcm_dec_body:
+___
+$code.=<<___;
+	vzeroupper
+
+	vmovdqu		($ivp),$T1		# input counter value
+	add		\$-128,%rsp
+	mov		12($ivp),$counter
+	lea		.Lbswap_mask(%rip),$const
+	lea		-0x80($key),$in0	# borrow $in0
+	mov		\$0xf80,$end0		# borrow $end0
+	vmovdqu		($Xip),$Xi		# load Xi
+	and		\$-128,%rsp		# ensure stack alignment
+	vmovdqu		($const),$Ii		# borrow $Ii for .Lbswap_mask
+	lea		0x80($key),$key		# size optimization
+	lea		0x20+0x20($Xip),$Xip	# size optimization
+	mov		0xf0-0x80($key),$rounds
+	vpshufb		$Ii,$Xi,$Xi
+
+	and		$end0,$in0
+	and		%rsp,$end0
+	sub		$in0,$end0
+	jc		.Ldec_no_key_aliasing
+	cmp		\$768,$end0
+	jnc		.Ldec_no_key_aliasing
+	sub		$end0,%rsp		# avoid aliasing with key
+.Ldec_no_key_aliasing:
+
+	vmovdqu		0x50($inp),$Z3		# I[5]
+	lea		($inp),$in0
+	vmovdqu		0x40($inp),$Z0
+
+	# |_aesni_ctr32_ghash_6x| requires |$end0| to point to 2*96 (0xc0)
+	# bytes before the end of the input. Note, in particular, that this is
+	# correct even if |$len| is not an even multiple of 96 or 16. XXX: This
+	# seems to require that |$inp| + |$len| >= 2*96 (0xc0); i.e. |$inp| must
+	# not be near the very beginning of the address space when |$len| < 2*96
+	# (0xc0).
+	lea		-0xc0($inp,$len),$end0
+
+	vmovdqu		0x30($inp),$Z1
+	shr		\$4,$len
+	xor		$ret,$ret
+	vmovdqu		0x20($inp),$Z2
+	 vpshufb	$Ii,$Z3,$Z3		# passed to _aesni_ctr32_ghash_6x
+	vmovdqu		0x10($inp),$T2
+	 vpshufb	$Ii,$Z0,$Z0
+	vmovdqu		($inp),$Hkey
+	 vpshufb	$Ii,$Z1,$Z1
+	vmovdqu		$Z0,0x30(%rsp)
+	 vpshufb	$Ii,$Z2,$Z2
+	vmovdqu		$Z1,0x40(%rsp)
+	 vpshufb	$Ii,$T2,$T2
+	vmovdqu		$Z2,0x50(%rsp)
+	 vpshufb	$Ii,$Hkey,$Hkey
+	vmovdqu		$T2,0x60(%rsp)
+	vmovdqu		$Hkey,0x70(%rsp)
+
+	call		_aesni_ctr32_ghash_6x
+
+	vmovups		$inout0,-0x60($out)	# save output
+	vmovups		$inout1,-0x50($out)
+	vmovups		$inout2,-0x40($out)
+	vmovups		$inout3,-0x30($out)
+	vmovups		$inout4,-0x20($out)
+	vmovups		$inout5,-0x10($out)
+
+	vpshufb		($const),$Xi,$Xi	# .Lbswap_mask
+	vmovdqu		$Xi,-0x40($Xip)		# output Xi
+
+	vzeroupper
+___
+$code.=<<___ if ($win64);
+	movaps	-0xd8(%rax),%xmm6
+	movaps	-0xc8(%rax),%xmm7
+	movaps	-0xb8(%rax),%xmm8
+	movaps	-0xa8(%rax),%xmm9
+	movaps	-0x98(%rax),%xmm10
+	movaps	-0x88(%rax),%xmm11
+	movaps	-0x78(%rax),%xmm12
+	movaps	-0x68(%rax),%xmm13
+	movaps	-0x58(%rax),%xmm14
+	movaps	-0x48(%rax),%xmm15
+___
+$code.=<<___;
+	mov	-48(%rax),%r15
+	mov	-40(%rax),%r14
+	mov	-32(%rax),%r13
+	mov	-24(%rax),%r12
+	mov	-16(%rax),%rbp
+	mov	-8(%rax),%rbx
+	lea	(%rax),%rsp		# restore %rsp
+.Lgcm_dec_abort:
+	mov	$ret,%rax		# return value
+	ret
+.size	aesni_gcm_decrypt,.-aesni_gcm_decrypt
+___
+
+$code.=<<___;
+.type	_aesni_ctr32_6x,\@abi-omnipotent
+.align	32
+_aesni_ctr32_6x:
+	vmovdqu		0x00-0x80($key),$Z0	# borrow $Z0 for $rndkey
+	vmovdqu		0x20($const),$T2	# borrow $T2, .Lone_msb
+	lea		-1($rounds),%r13
+	vmovups		0x10-0x80($key),$rndkey
+	lea		0x20-0x80($key),%r12
+	vpxor		$Z0,$T1,$inout0
+	add		\$`6<<24`,$counter
+	jc		.Lhandle_ctr32_2
+	vpaddb		$T2,$T1,$inout1
+	vpaddb		$T2,$inout1,$inout2
+	vpxor		$Z0,$inout1,$inout1
+	vpaddb		$T2,$inout2,$inout3
+	vpxor		$Z0,$inout2,$inout2
+	vpaddb		$T2,$inout3,$inout4
+	vpxor		$Z0,$inout3,$inout3
+	vpaddb		$T2,$inout4,$inout5
+	vpxor		$Z0,$inout4,$inout4
+	vpaddb		$T2,$inout5,$T1
+	vpxor		$Z0,$inout5,$inout5
+	jmp		.Loop_ctr32
+
+.align	16
+.Loop_ctr32:
+	vaesenc		$rndkey,$inout0,$inout0
+	vaesenc		$rndkey,$inout1,$inout1
+	vaesenc		$rndkey,$inout2,$inout2
+	vaesenc		$rndkey,$inout3,$inout3
+	vaesenc		$rndkey,$inout4,$inout4
+	vaesenc		$rndkey,$inout5,$inout5
+	vmovups		(%r12),$rndkey
+	lea		0x10(%r12),%r12
+	dec		%r13d
+	jnz		.Loop_ctr32
+
+	vmovdqu		(%r12),$Hkey		# last round key
+	vaesenc		$rndkey,$inout0,$inout0
+	vpxor		0x00($inp),$Hkey,$Z0
+	vaesenc		$rndkey,$inout1,$inout1
+	vpxor		0x10($inp),$Hkey,$Z1
+	vaesenc		$rndkey,$inout2,$inout2
+	vpxor		0x20($inp),$Hkey,$Z2
+	vaesenc		$rndkey,$inout3,$inout3
+	vpxor		0x30($inp),$Hkey,$Xi
+	vaesenc		$rndkey,$inout4,$inout4
+	vpxor		0x40($inp),$Hkey,$T2
+	vaesenc		$rndkey,$inout5,$inout5
+	vpxor		0x50($inp),$Hkey,$Hkey
+	lea		0x60($inp),$inp
+
+	vaesenclast	$Z0,$inout0,$inout0
+	vaesenclast	$Z1,$inout1,$inout1
+	vaesenclast	$Z2,$inout2,$inout2
+	vaesenclast	$Xi,$inout3,$inout3
+	vaesenclast	$T2,$inout4,$inout4
+	vaesenclast	$Hkey,$inout5,$inout5
+	vmovups		$inout0,0x00($out)
+	vmovups		$inout1,0x10($out)
+	vmovups		$inout2,0x20($out)
+	vmovups		$inout3,0x30($out)
+	vmovups		$inout4,0x40($out)
+	vmovups		$inout5,0x50($out)
+	lea		0x60($out),$out
+
+	ret
+.align	32
+.Lhandle_ctr32_2:
+	vpshufb		$Ii,$T1,$Z2		# byte-swap counter
+	vmovdqu		0x30($const),$Z1	# borrow $Z1, .Ltwo_lsb
+	vpaddd		0x40($const),$Z2,$inout1	# .Lone_lsb
+	vpaddd		$Z1,$Z2,$inout2
+	vpaddd		$Z1,$inout1,$inout3
+	vpshufb		$Ii,$inout1,$inout1
+	vpaddd		$Z1,$inout2,$inout4
+	vpshufb		$Ii,$inout2,$inout2
+	vpxor		$Z0,$inout1,$inout1
+	vpaddd		$Z1,$inout3,$inout5
+	vpshufb		$Ii,$inout3,$inout3
+	vpxor		$Z0,$inout2,$inout2
+	vpaddd		$Z1,$inout4,$T1		# byte-swapped next counter value
+	vpshufb		$Ii,$inout4,$inout4
+	vpxor		$Z0,$inout3,$inout3
+	vpshufb		$Ii,$inout5,$inout5
+	vpxor		$Z0,$inout4,$inout4
+	vpshufb		$Ii,$T1,$T1		# next counter value
+	vpxor		$Z0,$inout5,$inout5
+	jmp	.Loop_ctr32
+.size	_aesni_ctr32_6x,.-_aesni_ctr32_6x
+
+.globl	aesni_gcm_encrypt
+.type	aesni_gcm_encrypt,\@function,6
+.align	32
+aesni_gcm_encrypt:
+	xor	$ret,$ret
+
+	# We call |_aesni_ctr32_6x| twice, each call consuming 96 bytes of
+	# input. Then we call |_aesni_ctr32_ghash_6x|, which requires at
+	# least 96 more bytes of input.
+	cmp	\$0x60*3,$len			# minimal accepted length
+	jb	.Lgcm_enc_abort
+
+	lea	(%rsp),%rax			# save stack pointer
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+___
+$code.=<<___ if ($win64);
+	lea	-0xa8(%rsp),%rsp
+	movaps	%xmm6,-0xd8(%rax)
+	movaps	%xmm7,-0xc8(%rax)
+	movaps	%xmm8,-0xb8(%rax)
+	movaps	%xmm9,-0xa8(%rax)
+	movaps	%xmm10,-0x98(%rax)
+	movaps	%xmm11,-0x88(%rax)
+	movaps	%xmm12,-0x78(%rax)
+	movaps	%xmm13,-0x68(%rax)
+	movaps	%xmm14,-0x58(%rax)
+	movaps	%xmm15,-0x48(%rax)
+.Lgcm_enc_body:
+___
+$code.=<<___;
+	vzeroupper
+
+	vmovdqu		($ivp),$T1		# input counter value
+	add		\$-128,%rsp
+	mov		12($ivp),$counter
+	lea		.Lbswap_mask(%rip),$const
+	lea		-0x80($key),$in0	# borrow $in0
+	mov		\$0xf80,$end0		# borrow $end0
+	lea		0x80($key),$key		# size optimization
+	vmovdqu		($const),$Ii		# borrow $Ii for .Lbswap_mask
+	and		\$-128,%rsp		# ensure stack alignment
+	mov		0xf0-0x80($key),$rounds
+
+	and		$end0,$in0
+	and		%rsp,$end0
+	sub		$in0,$end0
+	jc		.Lenc_no_key_aliasing
+	cmp		\$768,$end0
+	jnc		.Lenc_no_key_aliasing
+	sub		$end0,%rsp		# avoid aliasing with key
+.Lenc_no_key_aliasing:
+
+	lea		($out),$in0
+
+	# |_aesni_ctr32_ghash_6x| requires |$end0| to point to 2*96 (0xc0)
+	# bytes before the end of the input. Note, in particular, that this is
+	# correct even if |$len| is not an even multiple of 96 or 16. Unlike in
+	# the decryption case, there's no caveat that |$out| must not be near
+	# the very beginning of the address space, because we know that
+	# |$len| >= 3*96 from the check above, and so we know
+	# |$out| + |$len| >= 2*96 (0xc0).
+	lea		-0xc0($out,$len),$end0
+
+	shr		\$4,$len
+
+	call		_aesni_ctr32_6x
+	vpshufb		$Ii,$inout0,$Xi		# save bswapped output on stack
+	vpshufb		$Ii,$inout1,$T2
+	vmovdqu		$Xi,0x70(%rsp)
+	vpshufb		$Ii,$inout2,$Z0
+	vmovdqu		$T2,0x60(%rsp)
+	vpshufb		$Ii,$inout3,$Z1
+	vmovdqu		$Z0,0x50(%rsp)
+	vpshufb		$Ii,$inout4,$Z2
+	vmovdqu		$Z1,0x40(%rsp)
+	vpshufb		$Ii,$inout5,$Z3		# passed to _aesni_ctr32_ghash_6x
+	vmovdqu		$Z2,0x30(%rsp)
+
+	call		_aesni_ctr32_6x
+
+	vmovdqu		($Xip),$Xi		# load Xi
+	lea		0x20+0x20($Xip),$Xip	# size optimization
+	sub		\$12,$len
+	mov		\$0x60*2,$ret
+	vpshufb		$Ii,$Xi,$Xi
+
+	call		_aesni_ctr32_ghash_6x
+	vmovdqu		0x20(%rsp),$Z3		# I[5]
+	 vmovdqu	($const),$Ii		# borrow $Ii for .Lbswap_mask
+	vmovdqu		0x00-0x20($Xip),$Hkey	# $Hkey^1
+	vpunpckhqdq	$Z3,$Z3,$T1
+	vmovdqu		0x20-0x20($Xip),$rndkey	# borrow $rndkey for $HK
+	 vmovups	$inout0,-0x60($out)	# save output
+	 vpshufb	$Ii,$inout0,$inout0	# but keep bswapped copy
+	vpxor		$Z3,$T1,$T1
+	 vmovups	$inout1,-0x50($out)
+	 vpshufb	$Ii,$inout1,$inout1
+	 vmovups	$inout2,-0x40($out)
+	 vpshufb	$Ii,$inout2,$inout2
+	 vmovups	$inout3,-0x30($out)
+	 vpshufb	$Ii,$inout3,$inout3
+	 vmovups	$inout4,-0x20($out)
+	 vpshufb	$Ii,$inout4,$inout4
+	 vmovups	$inout5,-0x10($out)
+	 vpshufb	$Ii,$inout5,$inout5
+	 vmovdqu	$inout0,0x10(%rsp)	# free $inout0
+___
+{ my ($HK,$T3)=($rndkey,$inout0);
+
+$code.=<<___;
+	 vmovdqu	0x30(%rsp),$Z2		# I[4]
+	 vmovdqu	0x10-0x20($Xip),$Ii	# borrow $Ii for $Hkey^2
+	 vpunpckhqdq	$Z2,$Z2,$T2
+	vpclmulqdq	\$0x00,$Hkey,$Z3,$Z1
+	 vpxor		$Z2,$T2,$T2
+	vpclmulqdq	\$0x11,$Hkey,$Z3,$Z3
+	vpclmulqdq	\$0x00,$HK,$T1,$T1
+
+	 vmovdqu	0x40(%rsp),$T3		# I[3]
+	vpclmulqdq	\$0x00,$Ii,$Z2,$Z0
+	 vmovdqu	0x30-0x20($Xip),$Hkey	# $Hkey^3
+	vpxor		$Z1,$Z0,$Z0
+	 vpunpckhqdq	$T3,$T3,$Z1
+	vpclmulqdq	\$0x11,$Ii,$Z2,$Z2
+	 vpxor		$T3,$Z1,$Z1
+	vpxor		$Z3,$Z2,$Z2
+	vpclmulqdq	\$0x10,$HK,$T2,$T2
+	 vmovdqu	0x50-0x20($Xip),$HK
+	vpxor		$T1,$T2,$T2
+
+	 vmovdqu	0x50(%rsp),$T1		# I[2]
+	vpclmulqdq	\$0x00,$Hkey,$T3,$Z3
+	 vmovdqu	0x40-0x20($Xip),$Ii	# borrow $Ii for $Hkey^4
+	vpxor		$Z0,$Z3,$Z3
+	 vpunpckhqdq	$T1,$T1,$Z0
+	vpclmulqdq	\$0x11,$Hkey,$T3,$T3
+	 vpxor		$T1,$Z0,$Z0
+	vpxor		$Z2,$T3,$T3
+	vpclmulqdq	\$0x00,$HK,$Z1,$Z1
+	vpxor		$T2,$Z1,$Z1
+
+	 vmovdqu	0x60(%rsp),$T2		# I[1]
+	vpclmulqdq	\$0x00,$Ii,$T1,$Z2
+	 vmovdqu	0x60-0x20($Xip),$Hkey	# $Hkey^5
+	vpxor		$Z3,$Z2,$Z2
+	 vpunpckhqdq	$T2,$T2,$Z3
+	vpclmulqdq	\$0x11,$Ii,$T1,$T1
+	 vpxor		$T2,$Z3,$Z3
+	vpxor		$T3,$T1,$T1
+	vpclmulqdq	\$0x10,$HK,$Z0,$Z0
+	 vmovdqu	0x80-0x20($Xip),$HK
+	vpxor		$Z1,$Z0,$Z0
+
+	 vpxor		0x70(%rsp),$Xi,$Xi	# accumulate I[0]
+	vpclmulqdq	\$0x00,$Hkey,$T2,$Z1
+	 vmovdqu	0x70-0x20($Xip),$Ii	# borrow $Ii for $Hkey^6
+	 vpunpckhqdq	$Xi,$Xi,$T3
+	vpxor		$Z2,$Z1,$Z1
+	vpclmulqdq	\$0x11,$Hkey,$T2,$T2
+	 vpxor		$Xi,$T3,$T3
+	vpxor		$T1,$T2,$T2
+	vpclmulqdq	\$0x00,$HK,$Z3,$Z3
+	vpxor		$Z0,$Z3,$Z0
+
+	vpclmulqdq	\$0x00,$Ii,$Xi,$Z2
+	 vmovdqu	0x00-0x20($Xip),$Hkey	# $Hkey^1
+	 vpunpckhqdq	$inout5,$inout5,$T1
+	vpclmulqdq	\$0x11,$Ii,$Xi,$Xi
+	 vpxor		$inout5,$T1,$T1
+	vpxor		$Z1,$Z2,$Z1
+	vpclmulqdq	\$0x10,$HK,$T3,$T3
+	 vmovdqu	0x20-0x20($Xip),$HK
+	vpxor		$T2,$Xi,$Z3
+	vpxor		$Z0,$T3,$Z2
+
+	 vmovdqu	0x10-0x20($Xip),$Ii	# borrow $Ii for $Hkey^2
+	  vpxor		$Z1,$Z3,$T3		# aggregated Karatsuba post-processing
+	vpclmulqdq	\$0x00,$Hkey,$inout5,$Z0
+	  vpxor		$T3,$Z2,$Z2
+	 vpunpckhqdq	$inout4,$inout4,$T2
+	vpclmulqdq	\$0x11,$Hkey,$inout5,$inout5
+	 vpxor		$inout4,$T2,$T2
+	  vpslldq	\$8,$Z2,$T3
+	vpclmulqdq	\$0x00,$HK,$T1,$T1
+	  vpxor		$T3,$Z1,$Xi
+	  vpsrldq	\$8,$Z2,$Z2
+	  vpxor		$Z2,$Z3,$Z3
+
+	vpclmulqdq	\$0x00,$Ii,$inout4,$Z1
+	 vmovdqu	0x30-0x20($Xip),$Hkey	# $Hkey^3
+	vpxor		$Z0,$Z1,$Z1
+	 vpunpckhqdq	$inout3,$inout3,$T3
+	vpclmulqdq	\$0x11,$Ii,$inout4,$inout4
+	 vpxor		$inout3,$T3,$T3
+	vpxor		$inout5,$inout4,$inout4
+	  vpalignr	\$8,$Xi,$Xi,$inout5	# 1st phase
+	vpclmulqdq	\$0x10,$HK,$T2,$T2
+	 vmovdqu	0x50-0x20($Xip),$HK
+	vpxor		$T1,$T2,$T2
+
+	vpclmulqdq	\$0x00,$Hkey,$inout3,$Z0
+	 vmovdqu	0x40-0x20($Xip),$Ii	# borrow $Ii for $Hkey^4
+	vpxor		$Z1,$Z0,$Z0
+	 vpunpckhqdq	$inout2,$inout2,$T1
+	vpclmulqdq	\$0x11,$Hkey,$inout3,$inout3
+	 vpxor		$inout2,$T1,$T1
+	vpxor		$inout4,$inout3,$inout3
+	  vxorps	0x10(%rsp),$Z3,$Z3	# accumulate $inout0
+	vpclmulqdq	\$0x00,$HK,$T3,$T3
+	vpxor		$T2,$T3,$T3
+
+	  vpclmulqdq	\$0x10,0x10($const),$Xi,$Xi
+	  vxorps	$inout5,$Xi,$Xi
+
+	vpclmulqdq	\$0x00,$Ii,$inout2,$Z1
+	 vmovdqu	0x60-0x20($Xip),$Hkey	# $Hkey^5
+	vpxor		$Z0,$Z1,$Z1
+	 vpunpckhqdq	$inout1,$inout1,$T2
+	vpclmulqdq	\$0x11,$Ii,$inout2,$inout2
+	 vpxor		$inout1,$T2,$T2
+	  vpalignr	\$8,$Xi,$Xi,$inout5	# 2nd phase
+	vpxor		$inout3,$inout2,$inout2
+	vpclmulqdq	\$0x10,$HK,$T1,$T1
+	 vmovdqu	0x80-0x20($Xip),$HK
+	vpxor		$T3,$T1,$T1
+
+	  vxorps	$Z3,$inout5,$inout5
+	  vpclmulqdq	\$0x10,0x10($const),$Xi,$Xi
+	  vxorps	$inout5,$Xi,$Xi
+
+	vpclmulqdq	\$0x00,$Hkey,$inout1,$Z0
+	 vmovdqu	0x70-0x20($Xip),$Ii	# borrow $Ii for $Hkey^6
+	vpxor		$Z1,$Z0,$Z0
+	 vpunpckhqdq	$Xi,$Xi,$T3
+	vpclmulqdq	\$0x11,$Hkey,$inout1,$inout1
+	 vpxor		$Xi,$T3,$T3
+	vpxor		$inout2,$inout1,$inout1
+	vpclmulqdq	\$0x00,$HK,$T2,$T2
+	vpxor		$T1,$T2,$T2
+
+	vpclmulqdq	\$0x00,$Ii,$Xi,$Z1
+	vpclmulqdq	\$0x11,$Ii,$Xi,$Z3
+	vpxor		$Z0,$Z1,$Z1
+	vpclmulqdq	\$0x10,$HK,$T3,$Z2
+	vpxor		$inout1,$Z3,$Z3
+	vpxor		$T2,$Z2,$Z2
+
+	vpxor		$Z1,$Z3,$Z0		# aggregated Karatsuba post-processing
+	vpxor		$Z0,$Z2,$Z2
+	vpslldq		\$8,$Z2,$T1
+	vmovdqu		0x10($const),$Hkey	# .Lpoly
+	vpsrldq		\$8,$Z2,$Z2
+	vpxor		$T1,$Z1,$Xi
+	vpxor		$Z2,$Z3,$Z3
+
+	vpalignr	\$8,$Xi,$Xi,$T2		# 1st phase
+	vpclmulqdq	\$0x10,$Hkey,$Xi,$Xi
+	vpxor		$T2,$Xi,$Xi
+
+	vpalignr	\$8,$Xi,$Xi,$T2		# 2nd phase
+	vpclmulqdq	\$0x10,$Hkey,$Xi,$Xi
+	vpxor		$Z3,$T2,$T2
+	vpxor		$T2,$Xi,$Xi
+___
+}
+$code.=<<___;
+	vpshufb		($const),$Xi,$Xi	# .Lbswap_mask
+	vmovdqu		$Xi,-0x40($Xip)		# output Xi
+
+	vzeroupper
+___
+$code.=<<___ if ($win64);
+	movaps	-0xd8(%rax),%xmm6
+	movaps	-0xc8(%rax),%xmm7
+	movaps	-0xb8(%rax),%xmm8
+	movaps	-0xa8(%rax),%xmm9
+	movaps	-0x98(%rax),%xmm10
+	movaps	-0x88(%rax),%xmm11
+	movaps	-0x78(%rax),%xmm12
+	movaps	-0x68(%rax),%xmm13
+	movaps	-0x58(%rax),%xmm14
+	movaps	-0x48(%rax),%xmm15
+___
+$code.=<<___;
+	mov	-48(%rax),%r15
+	mov	-40(%rax),%r14
+	mov	-32(%rax),%r13
+	mov	-24(%rax),%r12
+	mov	-16(%rax),%rbp
+	mov	-8(%rax),%rbx
+	lea	(%rax),%rsp		# restore %rsp
+.Lgcm_enc_abort:
+	mov	$ret,%rax		# return value
+	ret
+.size	aesni_gcm_encrypt,.-aesni_gcm_encrypt
+___
+
+$code.=<<___;
+.align	64
+.Lbswap_mask:
+	.byte	15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0
+.Lpoly:
+	.byte	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0xc2
+.Lone_msb:
+	.byte	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1
+.Ltwo_lsb:
+	.byte	2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+.Lone_lsb:
+	.byte	1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+.asciz	"AES-NI GCM module for x86_64, CRYPTOGAMS by "
+.align	64
+___
+if ($win64) {
+$rec="%rcx";
+$frame="%rdx";
+$context="%r8";
+$disp="%r9";
+
+$code.=<<___
+.extern	__imp_RtlVirtualUnwind
+.type	gcm_se_handler,\@abi-omnipotent
+.align	16
+gcm_se_handler:
+	push	%rsi
+	push	%rdi
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+	pushfq
+	sub	\$64,%rsp
+
+	mov	120($context),%rax	# pull context->Rax
+	mov	248($context),%rbx	# pull context->Rip
+
+	mov	8($disp),%rsi		# disp->ImageBase
+	mov	56($disp),%r11		# disp->HandlerData
+
+	mov	0(%r11),%r10d		# HandlerData[0]
+	lea	(%rsi,%r10),%r10	# prologue label
+	cmp	%r10,%rbx		# context->RipRsp
+
+	mov	4(%r11),%r10d		# HandlerData[1]
+	lea	(%rsi,%r10),%r10	# epilogue label
+	cmp	%r10,%rbx		# context->Rip>=epilogue label
+	jae	.Lcommon_seh_tail
+
+	mov	120($context),%rax	# pull context->Rax
+
+	mov	-48(%rax),%r15
+	mov	-40(%rax),%r14
+	mov	-32(%rax),%r13
+	mov	-24(%rax),%r12
+	mov	-16(%rax),%rbp
+	mov	-8(%rax),%rbx
+	mov	%r15,240($context)
+	mov	%r14,232($context)
+	mov	%r13,224($context)
+	mov	%r12,216($context)
+	mov	%rbp,160($context)
+	mov	%rbx,144($context)
+
+	lea	-0xd8(%rax),%rsi	# %xmm save area
+	lea	512($context),%rdi	# & context.Xmm6
+	mov	\$20,%ecx		# 10*sizeof(%xmm0)/sizeof(%rax)
+	.long	0xa548f3fc		# cld; rep movsq
+
+.Lcommon_seh_tail:
+	mov	8(%rax),%rdi
+	mov	16(%rax),%rsi
+	mov	%rax,152($context)	# restore context->Rsp
+	mov	%rsi,168($context)	# restore context->Rsi
+	mov	%rdi,176($context)	# restore context->Rdi
+
+	mov	40($disp),%rdi		# disp->ContextRecord
+	mov	$context,%rsi		# context
+	mov	\$154,%ecx		# sizeof(CONTEXT)
+	.long	0xa548f3fc		# cld; rep movsq
+
+	mov	$disp,%rsi
+	xor	%rcx,%rcx		# arg1, UNW_FLAG_NHANDLER
+	mov	8(%rsi),%rdx		# arg2, disp->ImageBase
+	mov	0(%rsi),%r8		# arg3, disp->ControlPc
+	mov	16(%rsi),%r9		# arg4, disp->FunctionEntry
+	mov	40(%rsi),%r10		# disp->ContextRecord
+	lea	56(%rsi),%r11		# &disp->HandlerData
+	lea	24(%rsi),%r12		# &disp->EstablisherFrame
+	mov	%r10,32(%rsp)		# arg5
+	mov	%r11,40(%rsp)		# arg6
+	mov	%r12,48(%rsp)		# arg7
+	mov	%rcx,56(%rsp)		# arg8, (NULL)
+	call	*__imp_RtlVirtualUnwind(%rip)
+
+	mov	\$1,%eax		# ExceptionContinueSearch
+	add	\$64,%rsp
+	popfq
+	pop	%r15
+	pop	%r14
+	pop	%r13
+	pop	%r12
+	pop	%rbp
+	pop	%rbx
+	pop	%rdi
+	pop	%rsi
+	ret
+.size	gcm_se_handler,.-gcm_se_handler
+
+.section	.pdata
+.align	4
+	.rva	.LSEH_begin_aesni_gcm_decrypt
+	.rva	.LSEH_end_aesni_gcm_decrypt
+	.rva	.LSEH_gcm_dec_info
+
+	.rva	.LSEH_begin_aesni_gcm_encrypt
+	.rva	.LSEH_end_aesni_gcm_encrypt
+	.rva	.LSEH_gcm_enc_info
+.section	.xdata
+.align	8
+.LSEH_gcm_dec_info:
+	.byte	9,0,0,0
+	.rva	gcm_se_handler
+	.rva	.Lgcm_dec_body,.Lgcm_dec_abort
+.LSEH_gcm_enc_info:
+	.byte	9,0,0,0
+	.rva	gcm_se_handler
+	.rva	.Lgcm_enc_body,.Lgcm_enc_abort
+___
+}
+}}} else {{{
+$code=<<___;	# assembler is too old
+.text
+
+.globl	aesni_gcm_encrypt
+.type	aesni_gcm_encrypt,\@abi-omnipotent
+aesni_gcm_encrypt:
+	xor	%eax,%eax
+	ret
+.size	aesni_gcm_encrypt,.-aesni_gcm_encrypt
+
+.globl	aesni_gcm_decrypt
+.type	aesni_gcm_decrypt,\@abi-omnipotent
+aesni_gcm_decrypt:
+	xor	%eax,%eax
+	ret
+.size	aesni_gcm_decrypt,.-aesni_gcm_decrypt
+___
+}}}
+
+$code =~ s/\`([^\`]*)\`/eval($1)/gem;
+
+print $code;
+
+close STDOUT;
diff --git a/crypto/modes/asm/ghash-alpha.pl b/crypto/modes/asm/ghash-alpha.pl
new file mode 100644
index 000000000..ccf6b2bd6
--- /dev/null
+++ b/crypto/modes/asm/ghash-alpha.pl
@@ -0,0 +1,467 @@
+#! /usr/bin/env perl
+# Copyright 2010-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+#
+# ====================================================================
+# Written by Andy Polyakov  for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+#
+# March 2010
+#
+# The module implements "4-bit" GCM GHASH function and underlying
+# single multiplication operation in GF(2^128). "4-bit" means that it
+# uses 256 bytes per-key table [+128 bytes shared table]. Even though
+# loops are aggressively modulo-scheduled in respect to references to
+# Htbl and Z.hi updates for 8 cycles per byte, measured performance is
+# ~12 cycles per processed byte on 21264 CPU. It seems to be a dynamic
+# scheduling "glitch," because uprofile(1) indicates uniform sample
+# distribution, as if all instruction bundles execute in 1.5 cycles.
+# Meaning that it could have been even faster, yet 12 cycles is ~60%
+# better than gcc-generated code and ~80% than code generated by vendor
+# compiler.
+
+$cnt="v0";	# $0
+$t0="t0";
+$t1="t1";
+$t2="t2";
+$Thi0="t3";	# $4
+$Tlo0="t4";
+$Thi1="t5";
+$Tlo1="t6";
+$rem="t7";	# $8
+#################
+$Xi="a0";	# $16, input argument block
+$Htbl="a1";
+$inp="a2";
+$len="a3";
+$nlo="a4";	# $20
+$nhi="a5";
+$Zhi="t8";
+$Zlo="t9";
+$Xhi="t10";	# $24
+$Xlo="t11";
+$remp="t12";
+$rem_4bit="AT";	# $28
+
+{ my $N;
+  sub loop() {
+
+	$N++;
+$code.=<<___;
+.align	4
+	extbl	$Xlo,7,$nlo
+	and	$nlo,0xf0,$nhi
+	sll	$nlo,4,$nlo
+	and	$nlo,0xf0,$nlo
+
+	addq	$nlo,$Htbl,$nlo
+	ldq	$Zlo,8($nlo)
+	addq	$nhi,$Htbl,$nhi
+	ldq	$Zhi,0($nlo)
+
+	and	$Zlo,0x0f,$remp
+	sll	$Zhi,60,$t0
+	lda	$cnt,6(zero)
+	extbl	$Xlo,6,$nlo
+
+	ldq	$Tlo1,8($nhi)
+	s8addq	$remp,$rem_4bit,$remp
+	ldq	$Thi1,0($nhi)
+	srl	$Zlo,4,$Zlo
+
+	ldq	$rem,0($remp)
+	srl	$Zhi,4,$Zhi
+	xor	$t0,$Zlo,$Zlo
+	and	$nlo,0xf0,$nhi
+
+	xor	$Tlo1,$Zlo,$Zlo
+	sll	$nlo,4,$nlo
+	xor	$Thi1,$Zhi,$Zhi
+	and	$nlo,0xf0,$nlo
+
+	addq	$nlo,$Htbl,$nlo
+	ldq	$Tlo0,8($nlo)
+	addq	$nhi,$Htbl,$nhi
+	ldq	$Thi0,0($nlo)
+
+.Looplo$N:
+	and	$Zlo,0x0f,$remp
+	sll	$Zhi,60,$t0
+	subq	$cnt,1,$cnt
+	srl	$Zlo,4,$Zlo
+
+	ldq	$Tlo1,8($nhi)
+	xor	$rem,$Zhi,$Zhi
+	ldq	$Thi1,0($nhi)
+	s8addq	$remp,$rem_4bit,$remp
+
+	ldq	$rem,0($remp)
+	srl	$Zhi,4,$Zhi
+	xor	$t0,$Zlo,$Zlo
+	extbl	$Xlo,$cnt,$nlo
+
+	and	$nlo,0xf0,$nhi
+	xor	$Thi0,$Zhi,$Zhi
+	xor	$Tlo0,$Zlo,$Zlo
+	sll	$nlo,4,$nlo
+
+
+	and	$Zlo,0x0f,$remp
+	sll	$Zhi,60,$t0
+	and	$nlo,0xf0,$nlo
+	srl	$Zlo,4,$Zlo
+
+	s8addq	$remp,$rem_4bit,$remp
+	xor	$rem,$Zhi,$Zhi
+	addq	$nlo,$Htbl,$nlo
+	addq	$nhi,$Htbl,$nhi
+
+	ldq	$rem,0($remp)
+	srl	$Zhi,4,$Zhi
+	ldq	$Tlo0,8($nlo)
+	xor	$t0,$Zlo,$Zlo
+
+	xor	$Tlo1,$Zlo,$Zlo
+	xor	$Thi1,$Zhi,$Zhi
+	ldq	$Thi0,0($nlo)
+	bne	$cnt,.Looplo$N
+
+
+	and	$Zlo,0x0f,$remp
+	sll	$Zhi,60,$t0
+	lda	$cnt,7(zero)
+	srl	$Zlo,4,$Zlo
+
+	ldq	$Tlo1,8($nhi)
+	xor	$rem,$Zhi,$Zhi
+	ldq	$Thi1,0($nhi)
+	s8addq	$remp,$rem_4bit,$remp
+
+	ldq	$rem,0($remp)
+	srl	$Zhi,4,$Zhi
+	xor	$t0,$Zlo,$Zlo
+	extbl	$Xhi,$cnt,$nlo
+
+	and	$nlo,0xf0,$nhi
+	xor	$Thi0,$Zhi,$Zhi
+	xor	$Tlo0,$Zlo,$Zlo
+	sll	$nlo,4,$nlo
+
+	and	$Zlo,0x0f,$remp
+	sll	$Zhi,60,$t0
+	and	$nlo,0xf0,$nlo
+	srl	$Zlo,4,$Zlo
+
+	s8addq	$remp,$rem_4bit,$remp
+	xor	$rem,$Zhi,$Zhi
+	addq	$nlo,$Htbl,$nlo
+	addq	$nhi,$Htbl,$nhi
+
+	ldq	$rem,0($remp)
+	srl	$Zhi,4,$Zhi
+	ldq	$Tlo0,8($nlo)
+	xor	$t0,$Zlo,$Zlo
+
+	xor	$Tlo1,$Zlo,$Zlo
+	xor	$Thi1,$Zhi,$Zhi
+	ldq	$Thi0,0($nlo)
+	unop
+
+
+.Loophi$N:
+	and	$Zlo,0x0f,$remp
+	sll	$Zhi,60,$t0
+	subq	$cnt,1,$cnt
+	srl	$Zlo,4,$Zlo
+
+	ldq	$Tlo1,8($nhi)
+	xor	$rem,$Zhi,$Zhi
+	ldq	$Thi1,0($nhi)
+	s8addq	$remp,$rem_4bit,$remp
+
+	ldq	$rem,0($remp)
+	srl	$Zhi,4,$Zhi
+	xor	$t0,$Zlo,$Zlo
+	extbl	$Xhi,$cnt,$nlo
+
+	and	$nlo,0xf0,$nhi
+	xor	$Thi0,$Zhi,$Zhi
+	xor	$Tlo0,$Zlo,$Zlo
+	sll	$nlo,4,$nlo
+
+
+	and	$Zlo,0x0f,$remp
+	sll	$Zhi,60,$t0
+	and	$nlo,0xf0,$nlo
+	srl	$Zlo,4,$Zlo
+
+	s8addq	$remp,$rem_4bit,$remp
+	xor	$rem,$Zhi,$Zhi
+	addq	$nlo,$Htbl,$nlo
+	addq	$nhi,$Htbl,$nhi
+
+	ldq	$rem,0($remp)
+	srl	$Zhi,4,$Zhi
+	ldq	$Tlo0,8($nlo)
+	xor	$t0,$Zlo,$Zlo
+
+	xor	$Tlo1,$Zlo,$Zlo
+	xor	$Thi1,$Zhi,$Zhi
+	ldq	$Thi0,0($nlo)
+	bne	$cnt,.Loophi$N
+
+
+	and	$Zlo,0x0f,$remp
+	sll	$Zhi,60,$t0
+	srl	$Zlo,4,$Zlo
+
+	ldq	$Tlo1,8($nhi)
+	xor	$rem,$Zhi,$Zhi
+	ldq	$Thi1,0($nhi)
+	s8addq	$remp,$rem_4bit,$remp
+
+	ldq	$rem,0($remp)
+	srl	$Zhi,4,$Zhi
+	xor	$t0,$Zlo,$Zlo
+
+	xor	$Tlo0,$Zlo,$Zlo
+	xor	$Thi0,$Zhi,$Zhi
+
+	and	$Zlo,0x0f,$remp
+	sll	$Zhi,60,$t0
+	srl	$Zlo,4,$Zlo
+
+	s8addq	$remp,$rem_4bit,$remp
+	xor	$rem,$Zhi,$Zhi
+
+	ldq	$rem,0($remp)
+	srl	$Zhi,4,$Zhi
+	xor	$Tlo1,$Zlo,$Zlo
+	xor	$Thi1,$Zhi,$Zhi
+	xor	$t0,$Zlo,$Zlo
+	xor	$rem,$Zhi,$Zhi
+___
+}}
+
+$code=<<___;
+#ifdef __linux__
+#include 
+#else
+#include 
+#include 
+#endif
+
+.text
+
+.set	noat
+.set	noreorder
+.globl	gcm_gmult_4bit
+.align	4
+.ent	gcm_gmult_4bit
+gcm_gmult_4bit:
+	.frame	sp,0,ra
+	.prologue 0
+
+	ldq	$Xlo,8($Xi)
+	ldq	$Xhi,0($Xi)
+
+	bsr	$t0,picmeup
+	nop
+___
+
+	&loop();
+
+$code.=<<___;
+	srl	$Zlo,24,$t0	# byte swap
+	srl	$Zlo,8,$t1
+
+	sll	$Zlo,8,$t2
+	sll	$Zlo,24,$Zlo
+	zapnot	$t0,0x11,$t0
+	zapnot	$t1,0x22,$t1
+
+	zapnot	$Zlo,0x88,$Zlo
+	or	$t0,$t1,$t0
+	zapnot	$t2,0x44,$t2
+
+	or	$Zlo,$t0,$Zlo
+	srl	$Zhi,24,$t0
+	srl	$Zhi,8,$t1
+
+	or	$Zlo,$t2,$Zlo
+	sll	$Zhi,8,$t2
+	sll	$Zhi,24,$Zhi
+
+	srl	$Zlo,32,$Xlo
+	sll	$Zlo,32,$Zlo
+
+	zapnot	$t0,0x11,$t0
+	zapnot	$t1,0x22,$t1
+	or	$Zlo,$Xlo,$Xlo
+
+	zapnot	$Zhi,0x88,$Zhi
+	or	$t0,$t1,$t0
+	zapnot	$t2,0x44,$t2
+
+	or	$Zhi,$t0,$Zhi
+	or	$Zhi,$t2,$Zhi
+
+	srl	$Zhi,32,$Xhi
+	sll	$Zhi,32,$Zhi
+
+	or	$Zhi,$Xhi,$Xhi
+	stq	$Xlo,8($Xi)
+	stq	$Xhi,0($Xi)
+
+	ret	(ra)
+.end	gcm_gmult_4bit
+___
+
+$inhi="s0";
+$inlo="s1";
+
+$code.=<<___;
+.globl	gcm_ghash_4bit
+.align	4
+.ent	gcm_ghash_4bit
+gcm_ghash_4bit:
+	lda	sp,-32(sp)
+	stq	ra,0(sp)
+	stq	s0,8(sp)
+	stq	s1,16(sp)
+	.mask	0x04000600,-32
+	.frame	sp,32,ra
+	.prologue 0
+
+	ldq_u	$inhi,0($inp)
+	ldq_u	$Thi0,7($inp)
+	ldq_u	$inlo,8($inp)
+	ldq_u	$Tlo0,15($inp)
+	ldq	$Xhi,0($Xi)
+	ldq	$Xlo,8($Xi)
+
+	bsr	$t0,picmeup
+	nop
+
+.Louter:
+	extql	$inhi,$inp,$inhi
+	extqh	$Thi0,$inp,$Thi0
+	or	$inhi,$Thi0,$inhi
+	lda	$inp,16($inp)
+
+	extql	$inlo,$inp,$inlo
+	extqh	$Tlo0,$inp,$Tlo0
+	or	$inlo,$Tlo0,$inlo
+	subq	$len,16,$len
+
+	xor	$Xlo,$inlo,$Xlo
+	xor	$Xhi,$inhi,$Xhi
+___
+
+	&loop();
+
+$code.=<<___;
+	srl	$Zlo,24,$t0	# byte swap
+	srl	$Zlo,8,$t1
+
+	sll	$Zlo,8,$t2
+	sll	$Zlo,24,$Zlo
+	zapnot	$t0,0x11,$t0
+	zapnot	$t1,0x22,$t1
+
+	zapnot	$Zlo,0x88,$Zlo
+	or	$t0,$t1,$t0
+	zapnot	$t2,0x44,$t2
+
+	or	$Zlo,$t0,$Zlo
+	srl	$Zhi,24,$t0
+	srl	$Zhi,8,$t1
+
+	or	$Zlo,$t2,$Zlo
+	sll	$Zhi,8,$t2
+	sll	$Zhi,24,$Zhi
+
+	srl	$Zlo,32,$Xlo
+	sll	$Zlo,32,$Zlo
+	beq	$len,.Ldone
+
+	zapnot	$t0,0x11,$t0
+	zapnot	$t1,0x22,$t1
+	or	$Zlo,$Xlo,$Xlo
+	ldq_u	$inhi,0($inp)
+
+	zapnot	$Zhi,0x88,$Zhi
+	or	$t0,$t1,$t0
+	zapnot	$t2,0x44,$t2
+	ldq_u	$Thi0,7($inp)
+
+	or	$Zhi,$t0,$Zhi
+	or	$Zhi,$t2,$Zhi
+	ldq_u	$inlo,8($inp)
+	ldq_u	$Tlo0,15($inp)
+
+	srl	$Zhi,32,$Xhi
+	sll	$Zhi,32,$Zhi
+
+	or	$Zhi,$Xhi,$Xhi
+	br	zero,.Louter
+
+.Ldone:
+	zapnot	$t0,0x11,$t0
+	zapnot	$t1,0x22,$t1
+	or	$Zlo,$Xlo,$Xlo
+
+	zapnot	$Zhi,0x88,$Zhi
+	or	$t0,$t1,$t0
+	zapnot	$t2,0x44,$t2
+
+	or	$Zhi,$t0,$Zhi
+	or	$Zhi,$t2,$Zhi
+
+	srl	$Zhi,32,$Xhi
+	sll	$Zhi,32,$Zhi
+
+	or	$Zhi,$Xhi,$Xhi
+
+	stq	$Xlo,8($Xi)
+	stq	$Xhi,0($Xi)
+
+	.set	noreorder
+	/*ldq	ra,0(sp)*/
+	ldq	s0,8(sp)
+	ldq	s1,16(sp)
+	lda	sp,32(sp)
+	ret	(ra)
+.end	gcm_ghash_4bit
+
+.align	4
+.ent	picmeup
+picmeup:
+	.frame	sp,0,$t0
+	.prologue 0
+	br	$rem_4bit,.Lpic
+.Lpic:	lda	$rem_4bit,12($rem_4bit)
+	ret	($t0)
+.end	picmeup
+	nop
+rem_4bit:
+	.long	0,0x0000<<16, 0,0x1C20<<16, 0,0x3840<<16, 0,0x2460<<16
+	.long	0,0x7080<<16, 0,0x6CA0<<16, 0,0x48C0<<16, 0,0x54E0<<16
+	.long	0,0xE100<<16, 0,0xFD20<<16, 0,0xD940<<16, 0,0xC560<<16
+	.long	0,0x9180<<16, 0,0x8DA0<<16, 0,0xA9C0<<16, 0,0xB5E0<<16
+.ascii	"GHASH for Alpha, CRYPTOGAMS by "
+.align	4
+
+___
+$output=pop and open STDOUT,">$output";
+print $code;
+close STDOUT;
+
diff --git a/crypto/modes/asm/ghash-armv4.pl b/crypto/modes/asm/ghash-armv4.pl
new file mode 100644
index 000000000..7d880c94a
--- /dev/null
+++ b/crypto/modes/asm/ghash-armv4.pl
@@ -0,0 +1,554 @@
+#! /usr/bin/env perl
+# Copyright 2010-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+#
+# ====================================================================
+# Written by Andy Polyakov  for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+#
+# April 2010
+#
+# The module implements "4-bit" GCM GHASH function and underlying
+# single multiplication operation in GF(2^128). "4-bit" means that it
+# uses 256 bytes per-key table [+32 bytes shared table]. There is no
+# experimental performance data available yet. The only approximation
+# that can be made at this point is based on code size. Inner loop is
+# 32 instructions long and on single-issue core should execute in <40
+# cycles. Having verified that gcc 3.4 didn't unroll corresponding
+# loop, this assembler loop body was found to be ~3x smaller than
+# compiler-generated one...
+#
+# July 2010
+#
+# Rescheduling for dual-issue pipeline resulted in 8.5% improvement on
+# Cortex A8 core and ~25 cycles per processed byte (which was observed
+# to be ~3 times faster than gcc-generated code:-)
+#
+# February 2011
+#
+# Profiler-assisted and platform-specific optimization resulted in 7%
+# improvement on Cortex A8 core and ~23.5 cycles per byte.
+#
+# March 2011
+#
+# Add NEON implementation featuring polynomial multiplication, i.e. no
+# lookup tables involved. On Cortex A8 it was measured to process one
+# byte in 15 cycles or 55% faster than integer-only code.
+#
+# April 2014
+#
+# Switch to multiplication algorithm suggested in paper referred
+# below and combine it with reduction algorithm from x86 module.
+# Performance improvement over previous version varies from 65% on
+# Snapdragon S4 to 110% on Cortex A9. In absolute terms Cortex A8
+# processes one byte in 8.45 cycles, A9 - in 10.2, A15 - in 7.63,
+# Snapdragon S4 - in 9.33.
+#
+# Câmara, D.; Gouvêa, C. P. L.; López, J. & Dahab, R.: Fast Software
+# Polynomial Multiplication on ARM Processors using the NEON Engine.
+# 
+# http://conradoplg.cryptoland.net/files/2010/12/mocrysen13.pdf
+
+# ====================================================================
+# Note about "528B" variant. In ARM case it makes lesser sense to
+# implement it for following reasons:
+#
+# - performance improvement won't be anywhere near 50%, because 128-
+#   bit shift operation is neatly fused with 128-bit xor here, and
+#   "538B" variant would eliminate only 4-5 instructions out of 32
+#   in the inner loop (meaning that estimated improvement is ~15%);
+# - ARM-based systems are often embedded ones and extra memory
+#   consumption might be unappreciated (for so little improvement);
+#
+# Byte order [in]dependence. =========================================
+#
+# Caller is expected to maintain specific *dword* order in Htable,
+# namely with *least* significant dword of 128-bit value at *lower*
+# address. This differs completely from C code and has everything to
+# do with ldm instruction and order in which dwords are "consumed" by
+# algorithm. *Byte* order within these dwords in turn is whatever
+# *native* byte order on current platform. See gcm128.c for working
+# example...
+
+$flavour = shift;
+if ($flavour=~/\w[\w\-]*\.\w+$/) { $output=$flavour; undef $flavour; }
+else { while (($output=shift) && ($output!~/\w[\w\-]*\.\w+$/)) {} }
+
+if ($flavour && $flavour ne "void") {
+    $0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+    ( $xlate="${dir}arm-xlate.pl" and -f $xlate ) or
+    ( $xlate="${dir}../../perlasm/arm-xlate.pl" and -f $xlate) or
+    die "can't locate arm-xlate.pl";
+
+    open STDOUT,"| \"$^X\" $xlate $flavour $output";
+} else {
+    open STDOUT,">$output";
+}
+
+$Xi="r0";	# argument block
+$Htbl="r1";
+$inp="r2";
+$len="r3";
+
+$Zll="r4";	# variables
+$Zlh="r5";
+$Zhl="r6";
+$Zhh="r7";
+$Tll="r8";
+$Tlh="r9";
+$Thl="r10";
+$Thh="r11";
+$nlo="r12";
+################# r13 is stack pointer
+$nhi="r14";
+################# r15 is program counter
+
+$rem_4bit=$inp;	# used in gcm_gmult_4bit
+$cnt=$len;
+
+sub Zsmash() {
+  my $i=12;
+  my @args=@_;
+  for ($Zll,$Zlh,$Zhl,$Zhh) {
+    $code.=<<___;
+#if __ARM_ARCH__>=7 && defined(__ARMEL__)
+	rev	$_,$_
+	str	$_,[$Xi,#$i]
+#elif defined(__ARMEB__)
+	str	$_,[$Xi,#$i]
+#else
+	mov	$Tlh,$_,lsr#8
+	strb	$_,[$Xi,#$i+3]
+	mov	$Thl,$_,lsr#16
+	strb	$Tlh,[$Xi,#$i+2]
+	mov	$Thh,$_,lsr#24
+	strb	$Thl,[$Xi,#$i+1]
+	strb	$Thh,[$Xi,#$i]
+#endif
+___
+    $code.="\t".shift(@args)."\n";
+    $i-=4;
+  }
+}
+
+$code=<<___;
+#include "arm_arch.h"
+
+.text
+#if defined(__thumb2__) || defined(__clang__)
+.syntax	unified
+#endif
+#if defined(__thumb2__)
+.thumb
+#else
+.code	32
+#endif
+
+#ifdef  __clang__
+#define ldrplb  ldrbpl
+#define ldrneb  ldrbne
+#endif
+
+.type	rem_4bit,%object
+.align	5
+rem_4bit:
+.short	0x0000,0x1C20,0x3840,0x2460
+.short	0x7080,0x6CA0,0x48C0,0x54E0
+.short	0xE100,0xFD20,0xD940,0xC560
+.short	0x9180,0x8DA0,0xA9C0,0xB5E0
+.size	rem_4bit,.-rem_4bit
+
+.type	rem_4bit_get,%function
+rem_4bit_get:
+#if defined(__thumb2__)
+	adr	$rem_4bit,rem_4bit
+#else
+	sub	$rem_4bit,pc,#8+32	@ &rem_4bit
+#endif
+	b	.Lrem_4bit_got
+	nop
+	nop
+.size	rem_4bit_get,.-rem_4bit_get
+
+.global	gcm_ghash_4bit
+.type	gcm_ghash_4bit,%function
+.align	4
+gcm_ghash_4bit:
+#if defined(__thumb2__)
+	adr	r12,rem_4bit
+#else
+	sub	r12,pc,#8+48		@ &rem_4bit
+#endif
+	add	$len,$inp,$len		@ $len to point at the end
+	stmdb	sp!,{r3-r11,lr}		@ save $len/end too
+
+	ldmia	r12,{r4-r11}		@ copy rem_4bit ...
+	stmdb	sp!,{r4-r11}		@ ... to stack
+
+	ldrb	$nlo,[$inp,#15]
+	ldrb	$nhi,[$Xi,#15]
+.Louter:
+	eor	$nlo,$nlo,$nhi
+	and	$nhi,$nlo,#0xf0
+	and	$nlo,$nlo,#0x0f
+	mov	$cnt,#14
+
+	add	$Zhh,$Htbl,$nlo,lsl#4
+	ldmia	$Zhh,{$Zll-$Zhh}	@ load Htbl[nlo]
+	add	$Thh,$Htbl,$nhi
+	ldrb	$nlo,[$inp,#14]
+
+	and	$nhi,$Zll,#0xf		@ rem
+	ldmia	$Thh,{$Tll-$Thh}	@ load Htbl[nhi]
+	add	$nhi,$nhi,$nhi
+	eor	$Zll,$Tll,$Zll,lsr#4
+	ldrh	$Tll,[sp,$nhi]		@ rem_4bit[rem]
+	eor	$Zll,$Zll,$Zlh,lsl#28
+	ldrb	$nhi,[$Xi,#14]
+	eor	$Zlh,$Tlh,$Zlh,lsr#4
+	eor	$Zlh,$Zlh,$Zhl,lsl#28
+	eor	$Zhl,$Thl,$Zhl,lsr#4
+	eor	$Zhl,$Zhl,$Zhh,lsl#28
+	eor	$Zhh,$Thh,$Zhh,lsr#4
+	eor	$nlo,$nlo,$nhi
+	and	$nhi,$nlo,#0xf0
+	and	$nlo,$nlo,#0x0f
+	eor	$Zhh,$Zhh,$Tll,lsl#16
+
+.Linner:
+	add	$Thh,$Htbl,$nlo,lsl#4
+	and	$nlo,$Zll,#0xf		@ rem
+	subs	$cnt,$cnt,#1
+	add	$nlo,$nlo,$nlo
+	ldmia	$Thh,{$Tll-$Thh}	@ load Htbl[nlo]
+	eor	$Zll,$Tll,$Zll,lsr#4
+	eor	$Zll,$Zll,$Zlh,lsl#28
+	eor	$Zlh,$Tlh,$Zlh,lsr#4
+	eor	$Zlh,$Zlh,$Zhl,lsl#28
+	ldrh	$Tll,[sp,$nlo]		@ rem_4bit[rem]
+	eor	$Zhl,$Thl,$Zhl,lsr#4
+#ifdef	__thumb2__
+	it	pl
+#endif
+	ldrplb	$nlo,[$inp,$cnt]
+	eor	$Zhl,$Zhl,$Zhh,lsl#28
+	eor	$Zhh,$Thh,$Zhh,lsr#4
+
+	add	$Thh,$Htbl,$nhi
+	and	$nhi,$Zll,#0xf		@ rem
+	eor	$Zhh,$Zhh,$Tll,lsl#16	@ ^= rem_4bit[rem]
+	add	$nhi,$nhi,$nhi
+	ldmia	$Thh,{$Tll-$Thh}	@ load Htbl[nhi]
+	eor	$Zll,$Tll,$Zll,lsr#4
+#ifdef	__thumb2__
+	it	pl
+#endif
+	ldrplb	$Tll,[$Xi,$cnt]
+	eor	$Zll,$Zll,$Zlh,lsl#28
+	eor	$Zlh,$Tlh,$Zlh,lsr#4
+	ldrh	$Tlh,[sp,$nhi]
+	eor	$Zlh,$Zlh,$Zhl,lsl#28
+	eor	$Zhl,$Thl,$Zhl,lsr#4
+	eor	$Zhl,$Zhl,$Zhh,lsl#28
+#ifdef	__thumb2__
+	it	pl
+#endif
+	eorpl	$nlo,$nlo,$Tll
+	eor	$Zhh,$Thh,$Zhh,lsr#4
+#ifdef	__thumb2__
+	itt	pl
+#endif
+	andpl	$nhi,$nlo,#0xf0
+	andpl	$nlo,$nlo,#0x0f
+	eor	$Zhh,$Zhh,$Tlh,lsl#16	@ ^= rem_4bit[rem]
+	bpl	.Linner
+
+	ldr	$len,[sp,#32]		@ re-load $len/end
+	add	$inp,$inp,#16
+	mov	$nhi,$Zll
+___
+	&Zsmash("cmp\t$inp,$len","\n".
+				 "#ifdef __thumb2__\n".
+				 "	it	ne\n".
+				 "#endif\n".
+				 "	ldrneb	$nlo,[$inp,#15]");
+$code.=<<___;
+	bne	.Louter
+
+	add	sp,sp,#36
+#if __ARM_ARCH__>=5
+	ldmia	sp!,{r4-r11,pc}
+#else
+	ldmia	sp!,{r4-r11,lr}
+	tst	lr,#1
+	moveq	pc,lr			@ be binary compatible with V4, yet
+	bx	lr			@ interoperable with Thumb ISA:-)
+#endif
+.size	gcm_ghash_4bit,.-gcm_ghash_4bit
+
+.global	gcm_gmult_4bit
+.type	gcm_gmult_4bit,%function
+gcm_gmult_4bit:
+	stmdb	sp!,{r4-r11,lr}
+	ldrb	$nlo,[$Xi,#15]
+	b	rem_4bit_get
+.Lrem_4bit_got:
+	and	$nhi,$nlo,#0xf0
+	and	$nlo,$nlo,#0x0f
+	mov	$cnt,#14
+
+	add	$Zhh,$Htbl,$nlo,lsl#4
+	ldmia	$Zhh,{$Zll-$Zhh}	@ load Htbl[nlo]
+	ldrb	$nlo,[$Xi,#14]
+
+	add	$Thh,$Htbl,$nhi
+	and	$nhi,$Zll,#0xf		@ rem
+	ldmia	$Thh,{$Tll-$Thh}	@ load Htbl[nhi]
+	add	$nhi,$nhi,$nhi
+	eor	$Zll,$Tll,$Zll,lsr#4
+	ldrh	$Tll,[$rem_4bit,$nhi]	@ rem_4bit[rem]
+	eor	$Zll,$Zll,$Zlh,lsl#28
+	eor	$Zlh,$Tlh,$Zlh,lsr#4
+	eor	$Zlh,$Zlh,$Zhl,lsl#28
+	eor	$Zhl,$Thl,$Zhl,lsr#4
+	eor	$Zhl,$Zhl,$Zhh,lsl#28
+	eor	$Zhh,$Thh,$Zhh,lsr#4
+	and	$nhi,$nlo,#0xf0
+	eor	$Zhh,$Zhh,$Tll,lsl#16
+	and	$nlo,$nlo,#0x0f
+
+.Loop:
+	add	$Thh,$Htbl,$nlo,lsl#4
+	and	$nlo,$Zll,#0xf		@ rem
+	subs	$cnt,$cnt,#1
+	add	$nlo,$nlo,$nlo
+	ldmia	$Thh,{$Tll-$Thh}	@ load Htbl[nlo]
+	eor	$Zll,$Tll,$Zll,lsr#4
+	eor	$Zll,$Zll,$Zlh,lsl#28
+	eor	$Zlh,$Tlh,$Zlh,lsr#4
+	eor	$Zlh,$Zlh,$Zhl,lsl#28
+	ldrh	$Tll,[$rem_4bit,$nlo]	@ rem_4bit[rem]
+	eor	$Zhl,$Thl,$Zhl,lsr#4
+#ifdef	__thumb2__
+	it	pl
+#endif
+	ldrplb	$nlo,[$Xi,$cnt]
+	eor	$Zhl,$Zhl,$Zhh,lsl#28
+	eor	$Zhh,$Thh,$Zhh,lsr#4
+
+	add	$Thh,$Htbl,$nhi
+	and	$nhi,$Zll,#0xf		@ rem
+	eor	$Zhh,$Zhh,$Tll,lsl#16	@ ^= rem_4bit[rem]
+	add	$nhi,$nhi,$nhi
+	ldmia	$Thh,{$Tll-$Thh}	@ load Htbl[nhi]
+	eor	$Zll,$Tll,$Zll,lsr#4
+	eor	$Zll,$Zll,$Zlh,lsl#28
+	eor	$Zlh,$Tlh,$Zlh,lsr#4
+	ldrh	$Tll,[$rem_4bit,$nhi]	@ rem_4bit[rem]
+	eor	$Zlh,$Zlh,$Zhl,lsl#28
+	eor	$Zhl,$Thl,$Zhl,lsr#4
+	eor	$Zhl,$Zhl,$Zhh,lsl#28
+	eor	$Zhh,$Thh,$Zhh,lsr#4
+#ifdef	__thumb2__
+	itt	pl
+#endif
+	andpl	$nhi,$nlo,#0xf0
+	andpl	$nlo,$nlo,#0x0f
+	eor	$Zhh,$Zhh,$Tll,lsl#16	@ ^= rem_4bit[rem]
+	bpl	.Loop
+___
+	&Zsmash();
+$code.=<<___;
+#if __ARM_ARCH__>=5
+	ldmia	sp!,{r4-r11,pc}
+#else
+	ldmia	sp!,{r4-r11,lr}
+	tst	lr,#1
+	moveq	pc,lr			@ be binary compatible with V4, yet
+	bx	lr			@ interoperable with Thumb ISA:-)
+#endif
+.size	gcm_gmult_4bit,.-gcm_gmult_4bit
+___
+{
+my ($Xl,$Xm,$Xh,$IN)=map("q$_",(0..3));
+my ($t0,$t1,$t2,$t3)=map("q$_",(8..12));
+my ($Hlo,$Hhi,$Hhl,$k48,$k32,$k16)=map("d$_",(26..31));
+
+sub clmul64x64 {
+my ($r,$a,$b)=@_;
+$code.=<<___;
+	vext.8		$t0#lo, $a, $a, #1	@ A1
+	vmull.p8	$t0, $t0#lo, $b		@ F = A1*B
+	vext.8		$r#lo, $b, $b, #1	@ B1
+	vmull.p8	$r, $a, $r#lo		@ E = A*B1
+	vext.8		$t1#lo, $a, $a, #2	@ A2
+	vmull.p8	$t1, $t1#lo, $b		@ H = A2*B
+	vext.8		$t3#lo, $b, $b, #2	@ B2
+	vmull.p8	$t3, $a, $t3#lo		@ G = A*B2
+	vext.8		$t2#lo, $a, $a, #3	@ A3
+	veor		$t0, $t0, $r		@ L = E + F
+	vmull.p8	$t2, $t2#lo, $b		@ J = A3*B
+	vext.8		$r#lo, $b, $b, #3	@ B3
+	veor		$t1, $t1, $t3		@ M = G + H
+	vmull.p8	$r, $a, $r#lo		@ I = A*B3
+	veor		$t0#lo, $t0#lo, $t0#hi	@ t0 = (L) (P0 + P1) << 8
+	vand		$t0#hi, $t0#hi, $k48
+	vext.8		$t3#lo, $b, $b, #4	@ B4
+	veor		$t1#lo, $t1#lo, $t1#hi	@ t1 = (M) (P2 + P3) << 16
+	vand		$t1#hi, $t1#hi, $k32
+	vmull.p8	$t3, $a, $t3#lo		@ K = A*B4
+	veor		$t2, $t2, $r		@ N = I + J
+	veor		$t0#lo, $t0#lo, $t0#hi
+	veor		$t1#lo, $t1#lo, $t1#hi
+	veor		$t2#lo, $t2#lo, $t2#hi	@ t2 = (N) (P4 + P5) << 24
+	vand		$t2#hi, $t2#hi, $k16
+	vext.8		$t0, $t0, $t0, #15
+	veor		$t3#lo, $t3#lo, $t3#hi	@ t3 = (K) (P6 + P7) << 32
+	vmov.i64	$t3#hi, #0
+	vext.8		$t1, $t1, $t1, #14
+	veor		$t2#lo, $t2#lo, $t2#hi
+	vmull.p8	$r, $a, $b		@ D = A*B
+	vext.8		$t3, $t3, $t3, #12
+	vext.8		$t2, $t2, $t2, #13
+	veor		$t0, $t0, $t1
+	veor		$t2, $t2, $t3
+	veor		$r, $r, $t0
+	veor		$r, $r, $t2
+___
+}
+
+$code.=<<___;
+#if __ARM_MAX_ARCH__>=7
+.arch	armv7-a
+.fpu	neon
+
+.global	gcm_init_neon
+.type	gcm_init_neon,%function
+.align	4
+gcm_init_neon:
+	vld1.64		$IN#hi,[r1]!		@ load H
+	vmov.i8		$t0,#0xe1
+	vld1.64		$IN#lo,[r1]
+	vshl.i64	$t0#hi,#57
+	vshr.u64	$t0#lo,#63		@ t0=0xc2....01
+	vdup.8		$t1,$IN#hi[7]
+	vshr.u64	$Hlo,$IN#lo,#63
+	vshr.s8		$t1,#7			@ broadcast carry bit
+	vshl.i64	$IN,$IN,#1
+	vand		$t0,$t0,$t1
+	vorr		$IN#hi,$Hlo		@ H<<<=1
+	veor		$IN,$IN,$t0		@ twisted H
+	vstmia		r0,{$IN}
+
+	ret					@ bx lr
+.size	gcm_init_neon,.-gcm_init_neon
+
+.global	gcm_gmult_neon
+.type	gcm_gmult_neon,%function
+.align	4
+gcm_gmult_neon:
+	vld1.64		$IN#hi,[$Xi]!		@ load Xi
+	vld1.64		$IN#lo,[$Xi]!
+	vmov.i64	$k48,#0x0000ffffffffffff
+	vldmia		$Htbl,{$Hlo-$Hhi}	@ load twisted H
+	vmov.i64	$k32,#0x00000000ffffffff
+#ifdef __ARMEL__
+	vrev64.8	$IN,$IN
+#endif
+	vmov.i64	$k16,#0x000000000000ffff
+	veor		$Hhl,$Hlo,$Hhi		@ Karatsuba pre-processing
+	mov		$len,#16
+	b		.Lgmult_neon
+.size	gcm_gmult_neon,.-gcm_gmult_neon
+
+.global	gcm_ghash_neon
+.type	gcm_ghash_neon,%function
+.align	4
+gcm_ghash_neon:
+	vld1.64		$Xl#hi,[$Xi]!		@ load Xi
+	vld1.64		$Xl#lo,[$Xi]!
+	vmov.i64	$k48,#0x0000ffffffffffff
+	vldmia		$Htbl,{$Hlo-$Hhi}	@ load twisted H
+	vmov.i64	$k32,#0x00000000ffffffff
+#ifdef __ARMEL__
+	vrev64.8	$Xl,$Xl
+#endif
+	vmov.i64	$k16,#0x000000000000ffff
+	veor		$Hhl,$Hlo,$Hhi		@ Karatsuba pre-processing
+
+.Loop_neon:
+	vld1.64		$IN#hi,[$inp]!		@ load inp
+	vld1.64		$IN#lo,[$inp]!
+#ifdef __ARMEL__
+	vrev64.8	$IN,$IN
+#endif
+	veor		$IN,$Xl			@ inp^=Xi
+.Lgmult_neon:
+___
+	&clmul64x64	($Xl,$Hlo,"$IN#lo");	# H.lo·Xi.lo
+$code.=<<___;
+	veor		$IN#lo,$IN#lo,$IN#hi	@ Karatsuba pre-processing
+___
+	&clmul64x64	($Xm,$Hhl,"$IN#lo");	# (H.lo+H.hi)·(Xi.lo+Xi.hi)
+	&clmul64x64	($Xh,$Hhi,"$IN#hi");	# H.hi·Xi.hi
+$code.=<<___;
+	veor		$Xm,$Xm,$Xl		@ Karatsuba post-processing
+	veor		$Xm,$Xm,$Xh
+	veor		$Xl#hi,$Xl#hi,$Xm#lo
+	veor		$Xh#lo,$Xh#lo,$Xm#hi	@ Xh|Xl - 256-bit result
+
+	@ equivalent of reduction_avx from ghash-x86_64.pl
+	vshl.i64	$t1,$Xl,#57		@ 1st phase
+	vshl.i64	$t2,$Xl,#62
+	veor		$t2,$t2,$t1		@
+	vshl.i64	$t1,$Xl,#63
+	veor		$t2, $t2, $t1		@
+ 	veor		$Xl#hi,$Xl#hi,$t2#lo	@
+	veor		$Xh#lo,$Xh#lo,$t2#hi
+
+	vshr.u64	$t2,$Xl,#1		@ 2nd phase
+	veor		$Xh,$Xh,$Xl
+	veor		$Xl,$Xl,$t2		@
+	vshr.u64	$t2,$t2,#6
+	vshr.u64	$Xl,$Xl,#1		@
+	veor		$Xl,$Xl,$Xh		@
+	veor		$Xl,$Xl,$t2		@
+
+	subs		$len,#16
+	bne		.Loop_neon
+
+#ifdef __ARMEL__
+	vrev64.8	$Xl,$Xl
+#endif
+	sub		$Xi,#16	
+	vst1.64		$Xl#hi,[$Xi]!		@ write out Xi
+	vst1.64		$Xl#lo,[$Xi]
+
+	ret					@ bx lr
+.size	gcm_ghash_neon,.-gcm_ghash_neon
+#endif
+___
+}
+$code.=<<___;
+.asciz  "GHASH for ARMv4/NEON, CRYPTOGAMS by "
+.align  2
+___
+
+foreach (split("\n",$code)) {
+	s/\`([^\`]*)\`/eval $1/geo;
+
+	s/\bq([0-9]+)#(lo|hi)/sprintf "d%d",2*$1+($2 eq "hi")/geo	or
+	s/\bret\b/bx	lr/go		or
+	s/\bbx\s+lr\b/.word\t0xe12fff1e/go;    # make it possible to compile with -march=armv4
+
+	print $_,"\n";
+}
+close STDOUT; # enforce flush
diff --git a/crypto/modes/asm/ghash-c64xplus.pl b/crypto/modes/asm/ghash-c64xplus.pl
new file mode 100644
index 000000000..3cadda399
--- /dev/null
+++ b/crypto/modes/asm/ghash-c64xplus.pl
@@ -0,0 +1,247 @@
+#! /usr/bin/env perl
+# Copyright 2012-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+#
+# ====================================================================
+# Written by Andy Polyakov  for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+#
+# December 2011
+#
+# The module implements GCM GHASH function and underlying single
+# multiplication operation in GF(2^128). Even though subroutines
+# have _4bit suffix, they are not using any tables, but rely on
+# hardware Galois Field Multiply support. Streamed GHASH processes
+# byte in ~7 cycles, which is >6x faster than "4-bit" table-driven
+# code compiled with TI's cl6x 6.0 with -mv6400+ -o2 flags. We are
+# comparing apples vs. oranges, but compiler surely could have done
+# better, because theoretical [though not necessarily achievable]
+# estimate for "4-bit" table-driven implementation is ~12 cycles.
+
+while (($output=shift) && ($output!~/\w[\w\-]*\.\w+$/)) {}
+open STDOUT,">$output";
+
+($Xip,$Htable,$inp,$len)=("A4","B4","A6","B6");	# arguments
+
+($Z0,$Z1,$Z2,$Z3,	$H0, $H1, $H2, $H3,
+			$H0x,$H1x,$H2x,$H3x)=map("A$_",(16..27));
+($H01u,$H01y,$H2u,$H3u,	$H0y,$H1y,$H2y,$H3y,
+			$H0z,$H1z,$H2z,$H3z)=map("B$_",(16..27));
+($FF000000,$E10000)=("B30","B31");
+($xip,$x0,$x1,$xib)=map("B$_",(6..9));	# $xip zaps $len
+ $xia="A9";
+($rem,$res)=("B4","B5");		# $rem zaps $Htable
+
+$code.=<<___;
+	.text
+
+	.if	.ASSEMBLER_VERSION<7000000
+	.asg	0,__TI_EABI__
+	.endif
+	.if	__TI_EABI__
+	.asg	gcm_gmult_1bit,_gcm_gmult_1bit
+	.asg	gcm_gmult_4bit,_gcm_gmult_4bit
+	.asg	gcm_ghash_4bit,_gcm_ghash_4bit
+	.endif
+
+	.asg	B3,RA
+
+	.if	0
+	.global	_gcm_gmult_1bit
+_gcm_gmult_1bit:
+	ADDAD	$Htable,2,$Htable
+	.endif
+	.global	_gcm_gmult_4bit
+_gcm_gmult_4bit:
+	.asmfunc
+	LDDW	*${Htable}[-1],$H1:$H0	; H.lo
+	LDDW	*${Htable}[-2],$H3:$H2	; H.hi
+||	MV	$Xip,${xip}		; reassign Xi
+||	MVK	15,B1			; SPLOOPD constant
+
+	MVK	0xE1,$E10000
+||	LDBU	*++${xip}[15],$x1	; Xi[15]
+	MVK	0xFF,$FF000000
+||	LDBU	*--${xip},$x0		; Xi[14]
+	SHL	$E10000,16,$E10000	; [pre-shifted] reduction polynomial
+	SHL	$FF000000,24,$FF000000	; upper byte mask
+||	BNOP	ghash_loop?
+||	MVK	1,B0			; take a single spin
+
+	PACKH2	$H0,$H1,$xia		; pack H0' and H1's upper bytes
+	AND	$H2,$FF000000,$H2u	; H2's upper byte
+	AND	$H3,$FF000000,$H3u	; H3's upper byte
+||	SHRU	$H2u,8,$H2u
+	SHRU	$H3u,8,$H3u
+||	ZERO	$Z1:$Z0
+	SHRU2	$xia,8,$H01u
+||	ZERO	$Z3:$Z2
+	.endasmfunc
+
+	.global	_gcm_ghash_4bit
+_gcm_ghash_4bit:
+	.asmfunc
+	LDDW	*${Htable}[-1],$H1:$H0	; H.lo
+||	SHRU	$len,4,B0		; reassign len
+	LDDW	*${Htable}[-2],$H3:$H2	; H.hi
+||	MV	$Xip,${xip}		; reassign Xi
+||	MVK	15,B1			; SPLOOPD constant
+
+	MVK	0xE1,$E10000
+|| [B0]	LDNDW	*${inp}[1],$H1x:$H0x
+	MVK	0xFF,$FF000000
+|| [B0]	LDNDW	*${inp}++[2],$H3x:$H2x
+	SHL	$E10000,16,$E10000	; [pre-shifted] reduction polynomial
+||	LDDW	*${xip}[1],$Z1:$Z0
+	SHL	$FF000000,24,$FF000000	; upper byte mask
+||	LDDW	*${xip}[0],$Z3:$Z2
+
+	PACKH2	$H0,$H1,$xia		; pack H0' and H1's upper bytes
+	AND	$H2,$FF000000,$H2u	; H2's upper byte
+	AND	$H3,$FF000000,$H3u	; H3's upper byte
+||	SHRU	$H2u,8,$H2u
+	SHRU	$H3u,8,$H3u
+	SHRU2	$xia,8,$H01u
+
+|| [B0]	XOR	$H0x,$Z0,$Z0		; Xi^=inp
+|| [B0]	XOR	$H1x,$Z1,$Z1
+	.if	.LITTLE_ENDIAN
+   [B0]	XOR	$H2x,$Z2,$Z2
+|| [B0]	XOR	$H3x,$Z3,$Z3
+|| [B0]	SHRU	$Z1,24,$xia		; Xi[15], avoid cross-path stall
+	STDW	$Z1:$Z0,*${xip}[1]
+|| [B0]	SHRU	$Z1,16,$x0		; Xi[14]
+|| [B0]	ZERO	$Z1:$Z0
+	.else
+   [B0]	XOR	$H2x,$Z2,$Z2
+|| [B0]	XOR	$H3x,$Z3,$Z3
+|| [B0]	MV	$Z0,$xia		; Xi[15], avoid cross-path stall
+	STDW	$Z1:$Z0,*${xip}[1]
+|| [B0] SHRU	$Z0,8,$x0		; Xi[14]
+|| [B0]	ZERO	$Z1:$Z0
+	.endif
+	STDW	$Z3:$Z2,*${xip}[0]
+|| [B0]	ZERO	$Z3:$Z2
+|| [B0]	MV	$xia,$x1
+   [B0]	ADDK	14,${xip}
+
+ghash_loop?:
+	SPLOOPD	6			; 6*16+7
+||	MVC	B1,ILC
+|| [B0]	SUB	B0,1,B0
+||	ZERO	A0
+||	ADD	$x1,$x1,$xib		; SHL	$x1,1,$xib
+||	SHL	$x1,1,$xia
+___
+
+########____________________________
+#  0    D2.     M1          M2      |
+#  1            M1                  |
+#  2            M1          M2      |
+#  3        D1. M1          M2      |
+#  4        S1. L1                  |
+#  5    S2  S1x L1          D2  L2  |____________________________
+#  6/0          L1  S1      L2  S2x |D2.     M1          M2      |
+#  7/1          L1  S1  D1x S2  M2  |        M1                  |
+#  8/2              S1  L1x S2      |        M1          M2      |
+#  9/3              S1  L1x         |    D1. M1          M2      |
+# 10/4                  D1x         |    S1. L1                  |
+# 11/5                              |S2  S1x L1          D2  L2  |____________
+# 12/6/0                D1x       __|        L1  S1      L2  S2x |D2.     ....
+#    7/1                                     L1  S1  D1x S2  M2  |        ....
+#    8/2                                         S1  L1x S2      |        ....
+#####...                                         ................|............
+$code.=<<___;
+	XORMPY	$H0,$xia,$H0x		; 0	; H·(Xi[i]<<1)
+||	XORMPY	$H01u,$xib,$H01y
+|| [A0]	LDBU	*--${xip},$x0
+	XORMPY	$H1,$xia,$H1x		; 1
+	XORMPY	$H2,$xia,$H2x		; 2
+||	XORMPY	$H2u,$xib,$H2y
+	XORMPY	$H3,$xia,$H3x		; 3
+||	XORMPY	$H3u,$xib,$H3y
+||[!A0]	MVK.D	15,A0				; *--${xip} counter
+	XOR.L	$H0x,$Z0,$Z0		; 4	; Z^=H·(Xi[i]<<1)
+|| [A0]	SUB.S	A0,1,A0
+	XOR.L	$H1x,$Z1,$Z1		; 5
+||	AND.D	$H01y,$FF000000,$H0z
+||	SWAP2.L	$H01y,$H1y		;	; SHL	$H01y,16,$H1y
+||	SHL	$x0,1,$xib
+||	SHL	$x0,1,$xia
+
+	XOR.L	$H2x,$Z2,$Z2		; 6/0	; [0,0] in epilogue
+||	SHL	$Z0,1,$rem		;	; rem=Z<<1
+||	SHRMB.S	$Z1,$Z0,$Z0		;	; Z>>=8
+||	AND.L	$H1y,$FF000000,$H1z
+	XOR.L	$H3x,$Z3,$Z3		; 7/1
+||	SHRMB.S	$Z2,$Z1,$Z1
+||	XOR.D	$H0z,$Z0,$Z0			; merge upper byte products
+||	AND.S	$H2y,$FF000000,$H2z
+||	XORMPY	$E10000,$rem,$res	;	; implicit rem&0x1FE
+	XOR.L	$H1z,$Z1,$Z1		; 8/2
+||	SHRMB.S	$Z3,$Z2,$Z2
+||	AND.S	$H3y,$FF000000,$H3z
+	XOR.L	$H2z,$Z2,$Z2		; 9/3
+||	SHRU	$Z3,8,$Z3
+	XOR.D	$H3z,$Z3,$Z3		; 10/4
+	NOP				; 11/5
+
+	SPKERNEL 0,2
+||	XOR.D	$res,$Z3,$Z3		; 12/6/0; Z^=res
+
+	; input pre-fetch is possible where D1 slot is available...
+   [B0]	LDNDW	*${inp}[1],$H1x:$H0x	; 8/-
+   [B0]	LDNDW	*${inp}++[2],$H3x:$H2x	; 9/-
+	NOP				; 10/-
+	.if	.LITTLE_ENDIAN
+	SWAP2	$Z0,$Z1			; 11/-
+||	SWAP4	$Z1,$Z0
+	SWAP4	$Z1,$Z1			; 12/-
+||	SWAP2	$Z0,$Z0
+	SWAP2	$Z2,$Z3
+||	SWAP4	$Z3,$Z2
+||[!B0]	BNOP	RA
+	SWAP4	$Z3,$Z3
+||	SWAP2	$Z2,$Z2
+|| [B0]	BNOP	ghash_loop?
+   [B0]	XOR	$H0x,$Z0,$Z0		; Xi^=inp
+|| [B0]	XOR	$H1x,$Z1,$Z1
+   [B0]	XOR	$H2x,$Z2,$Z2
+|| [B0]	XOR	$H3x,$Z3,$Z3
+|| [B0]	SHRU	$Z1,24,$xia		; Xi[15], avoid cross-path stall
+	STDW	$Z1:$Z0,*${xip}[1]
+|| [B0]	SHRU	$Z1,16,$x0		; Xi[14]
+|| [B0]	ZERO	$Z1:$Z0
+	.else
+  [!B0]	BNOP	RA			; 11/-
+   [B0]	BNOP	ghash_loop?		; 12/-
+   [B0]	XOR	$H0x,$Z0,$Z0		; Xi^=inp
+|| [B0]	XOR	$H1x,$Z1,$Z1
+   [B0]	XOR	$H2x,$Z2,$Z2
+|| [B0]	XOR	$H3x,$Z3,$Z3
+|| [B0]	MV	$Z0,$xia		; Xi[15], avoid cross-path stall
+	STDW	$Z1:$Z0,*${xip}[1]
+|| [B0] SHRU	$Z0,8,$x0		; Xi[14]
+|| [B0]	ZERO	$Z1:$Z0
+	.endif
+	STDW	$Z3:$Z2,*${xip}[0]
+|| [B0]	ZERO	$Z3:$Z2
+|| [B0]	MV	$xia,$x1
+   [B0]	ADDK	14,${xip}
+	.endasmfunc
+
+	.sect	.const
+	.cstring "GHASH for C64x+, CRYPTOGAMS by "
+	.align	4
+___
+
+print $code;
+close STDOUT;
diff --git a/crypto/modes/asm/ghash-ia64.pl b/crypto/modes/asm/ghash-ia64.pl
new file mode 100755
index 000000000..81e75f71a
--- /dev/null
+++ b/crypto/modes/asm/ghash-ia64.pl
@@ -0,0 +1,470 @@
+#! /usr/bin/env perl
+# Copyright 2010-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+
+# ====================================================================
+# Written by Andy Polyakov  for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+#
+# March 2010
+#
+# The module implements "4-bit" GCM GHASH function and underlying
+# single multiplication operation in GF(2^128). "4-bit" means that it
+# uses 256 bytes per-key table [+128 bytes shared table]. Streamed
+# GHASH performance was measured to be 6.67 cycles per processed byte
+# on Itanium 2, which is >90% better than Microsoft compiler generated
+# code. To anchor to something else sha1-ia64.pl module processes one
+# byte in 5.7 cycles. On Itanium GHASH should run at ~8.5 cycles per
+# byte.
+
+# September 2010
+#
+# It was originally thought that it makes lesser sense to implement
+# "528B" variant on Itanium 2 for following reason. Because number of
+# functional units is naturally limited, it appeared impossible to
+# implement "528B" loop in 4 cycles, only in 5. This would mean that
+# theoretically performance improvement couldn't be more than 20%.
+# But occasionally you prove yourself wrong:-) I figured out a way to
+# fold couple of instructions and having freed yet another instruction
+# slot by unrolling the loop... Resulting performance is 4.45 cycles
+# per processed byte and 50% better than "256B" version. On original
+# Itanium performance should remain the same as the "256B" version,
+# i.e. ~8.5 cycles.
+
+$output=pop and (open STDOUT,">$output" or die "can't open $output: $!");
+
+if ($^O eq "hpux") {
+    $ADDP="addp4";
+    for (@ARGV) { $ADDP="add" if (/[\+DD|\-mlp]64/); }
+} else { $ADDP="add"; }
+for (@ARGV)  {  $big_endian=1 if (/\-DB_ENDIAN/);
+                $big_endian=0 if (/\-DL_ENDIAN/);  }
+if (!defined($big_endian))
+             {  $big_endian=(unpack('L',pack('N',1))==1);  }
+
+sub loop() {
+my $label=shift;
+my ($p16,$p17)=(shift)?("p63","p63"):("p16","p17"); # mask references to inp
+
+# Loop is scheduled for 6 ticks on Itanium 2 and 8 on Itanium, i.e.
+# in scalable manner;-) Naturally assuming data in L1 cache...
+# Special note about 'dep' instruction, which is used to construct
+# &rem_4bit[Zlo&0xf]. It works, because rem_4bit is aligned at 128
+# bytes boundary and lower 7 bits of its address are guaranteed to
+# be zero.
+$code.=<<___;
+$label:
+{ .mfi;	(p18)	ld8	Hlo=[Hi[1]],-8
+	(p19)	dep	rem=Zlo,rem_4bitp,3,4	}
+{ .mfi;	(p19)	xor	Zhi=Zhi,Hhi
+	($p17)	xor	xi[1]=xi[1],in[1]	};;
+{ .mfi;	(p18)	ld8	Hhi=[Hi[1]]
+	(p19)	shrp	Zlo=Zhi,Zlo,4		}
+{ .mfi;	(p19)	ld8	rem=[rem]
+	(p18)	and	Hi[1]=mask0xf0,xi[2]	};;
+{ .mmi;	($p16)	ld1	in[0]=[inp],-1
+	(p18)	xor	Zlo=Zlo,Hlo
+	(p19)	shr.u	Zhi=Zhi,4		}
+{ .mib;	(p19)	xor	Hhi=Hhi,rem
+	(p18)	add	Hi[1]=Htbl,Hi[1]	};;
+
+{ .mfi;	(p18)	ld8	Hlo=[Hi[1]],-8
+	(p18)	dep	rem=Zlo,rem_4bitp,3,4	}
+{ .mfi;	(p17)	shladd	Hi[0]=xi[1],4,r0
+	(p18)	xor	Zhi=Zhi,Hhi		};;
+{ .mfi;	(p18)	ld8	Hhi=[Hi[1]]
+	(p18)	shrp	Zlo=Zhi,Zlo,4		}
+{ .mfi;	(p18)	ld8	rem=[rem]
+	(p17)	and	Hi[0]=mask0xf0,Hi[0]	};;
+{ .mmi;	(p16)	ld1	xi[0]=[Xi],-1
+	(p18)	xor	Zlo=Zlo,Hlo
+	(p18)	shr.u	Zhi=Zhi,4		}
+{ .mib;	(p18)	xor	Hhi=Hhi,rem
+	(p17)	add	Hi[0]=Htbl,Hi[0]
+	br.ctop.sptk	$label			};;
+___
+}
+
+$code=<<___;
+.explicit
+.text
+
+prevfs=r2;	prevlc=r3;	prevpr=r8;
+mask0xf0=r21;
+rem=r22;	rem_4bitp=r23;
+Xi=r24;		Htbl=r25;
+inp=r26;	end=r27;
+Hhi=r28;	Hlo=r29;
+Zhi=r30;	Zlo=r31;
+
+.align	128
+.skip	16					// aligns loop body
+.global	gcm_gmult_4bit#
+.proc	gcm_gmult_4bit#
+gcm_gmult_4bit:
+	.prologue
+{ .mmi;	.save	ar.pfs,prevfs
+	alloc	prevfs=ar.pfs,2,6,0,8
+	$ADDP	Xi=15,in0			// &Xi[15]
+	mov	rem_4bitp=ip		}
+{ .mii;	$ADDP	Htbl=8,in1			// &Htbl[0].lo
+	.save	ar.lc,prevlc
+	mov	prevlc=ar.lc
+	.save	pr,prevpr
+	mov	prevpr=pr		};;
+
+	.body
+	.rotr	in[3],xi[3],Hi[2]
+
+{ .mib;	ld1	xi[2]=[Xi],-1			// Xi[15]
+	mov	mask0xf0=0xf0
+	brp.loop.imp	.Loop1,.Lend1-16};;
+{ .mmi;	ld1	xi[1]=[Xi],-1			// Xi[14]
+					};;
+{ .mii;	shladd	Hi[1]=xi[2],4,r0
+	mov	pr.rot=0x7<<16
+	mov	ar.lc=13		};;
+{ .mii;	and	Hi[1]=mask0xf0,Hi[1]
+	mov	ar.ec=3
+	xor	Zlo=Zlo,Zlo		};;
+{ .mii;	add	Hi[1]=Htbl,Hi[1]		// &Htbl[nlo].lo
+	add	rem_4bitp=rem_4bit#-gcm_gmult_4bit#,rem_4bitp
+	xor	Zhi=Zhi,Zhi		};;
+___
+	&loop	(".Loop1",1);
+$code.=<<___;
+.Lend1:
+{ .mib;	xor	Zhi=Zhi,Hhi		};;	// modulo-scheduling artefact
+{ .mib;	mux1	Zlo=Zlo,\@rev		};;
+{ .mib;	mux1	Zhi=Zhi,\@rev		};;
+{ .mmi;	add	Hlo=9,Xi;;			// ;; is here to prevent
+	add	Hhi=1,Xi		};;	// pipeline flush on Itanium
+{ .mib;	st8	[Hlo]=Zlo
+	mov	pr=prevpr,0x1ffff	};;
+{ .mib;	st8	[Hhi]=Zhi
+	mov	ar.lc=prevlc
+	br.ret.sptk.many	b0	};;
+.endp	gcm_gmult_4bit#
+___
+
+######################################################################
+# "528B" (well, "512B" actualy) streamed GHASH
+#
+$Xip="in0";
+$Htbl="in1";
+$inp="in2";
+$len="in3";
+$rem_8bit="loc0";
+$mask0xff="loc1";
+($sum,$rum) = $big_endian ? ("nop.m","nop.m") : ("sum","rum");
+
+sub load_htable() {
+    for (my $i=0;$i<8;$i++) {
+	$code.=<<___;
+{ .mmi;	ld8	r`16+2*$i+1`=[r8],16		// Htable[$i].hi
+	ld8	r`16+2*$i`=[r9],16	}	// Htable[$i].lo
+{ .mmi;	ldf8	f`32+2*$i+1`=[r10],16		// Htable[`8+$i`].hi
+	ldf8	f`32+2*$i`=[r11],16		// Htable[`8+$i`].lo
+___
+	$code.=shift	if (($i+$#_)==7);
+	$code.="\t};;\n"
+    }
+}
+
+$code.=<<___;
+prevsp=r3;
+
+.align	32
+.skip	16					// aligns loop body
+.global	gcm_ghash_4bit#
+.proc	gcm_ghash_4bit#
+gcm_ghash_4bit:
+	.prologue
+{ .mmi;	.save	ar.pfs,prevfs
+	alloc	prevfs=ar.pfs,4,2,0,0
+	.vframe	prevsp
+	mov	prevsp=sp
+	mov	$rem_8bit=ip		};;
+	.body
+{ .mfi;	$ADDP	r8=0+0,$Htbl
+	$ADDP	r9=0+8,$Htbl		}
+{ .mfi;	$ADDP	r10=128+0,$Htbl
+	$ADDP	r11=128+8,$Htbl		};;
+___
+	&load_htable(
+	"	$ADDP	$Xip=15,$Xip",		# &Xi[15]
+	"	$ADDP	$len=$len,$inp",	# &inp[len]
+	"	$ADDP	$inp=15,$inp",		# &inp[15]
+	"	mov	$mask0xff=0xff",
+	"	add	sp=-512,sp",
+	"	andcm	sp=sp,$mask0xff",	# align stack frame
+	"	add	r14=0,sp",
+	"	add	r15=8,sp");
+$code.=<<___;
+{ .mmi;	$sum	1<<1				// go big-endian
+	add	r8=256+0,sp
+	add	r9=256+8,sp		}
+{ .mmi;	add	r10=256+128+0,sp
+	add	r11=256+128+8,sp
+	add	$len=-17,$len		};;
+___
+for($i=0;$i<8;$i++) {	# generate first half of Hshr4[]
+my ($rlo,$rhi)=("r".eval(16+2*$i),"r".eval(16+2*$i+1));
+$code.=<<___;
+{ .mmi;	st8	[r8]=$rlo,16			// Htable[$i].lo
+	st8	[r9]=$rhi,16			// Htable[$i].hi
+	shrp	$rlo=$rhi,$rlo,4	}//;;
+{ .mmi;	stf8	[r10]=f`32+2*$i`,16		// Htable[`8+$i`].lo
+	stf8	[r11]=f`32+2*$i+1`,16		// Htable[`8+$i`].hi
+	shr.u	$rhi=$rhi,4		};;
+{ .mmi;	st8	[r14]=$rlo,16			// Htable[$i].lo>>4
+	st8	[r15]=$rhi,16		}//;;	// Htable[$i].hi>>4
+___
+}
+$code.=<<___;
+{ .mmi;	ld8	r16=[r8],16			// Htable[8].lo
+	ld8	r17=[r9],16		};;	// Htable[8].hi
+{ .mmi;	ld8	r18=[r8],16			// Htable[9].lo
+	ld8	r19=[r9],16		}	// Htable[9].hi
+{ .mmi;	rum	1<<5				// clear um.mfh
+	shrp	r16=r17,r16,4		};;
+___
+for($i=0;$i<6;$i++) {	# generate second half of Hshr4[]
+$code.=<<___;
+{ .mmi;	ld8	r`20+2*$i`=[r8],16		// Htable[`10+$i`].lo
+	ld8	r`20+2*$i+1`=[r9],16		// Htable[`10+$i`].hi
+	shr.u	r`16+2*$i+1`=r`16+2*$i+1`,4	};;
+{ .mmi;	st8	[r14]=r`16+2*$i`,16		// Htable[`8+$i`].lo>>4
+	st8	[r15]=r`16+2*$i+1`,16		// Htable[`8+$i`].hi>>4
+	shrp	r`18+2*$i`=r`18+2*$i+1`,r`18+2*$i`,4	}
+___
+}
+$code.=<<___;
+{ .mmi;	shr.u	r`16+2*$i+1`=r`16+2*$i+1`,4	};;
+{ .mmi;	st8	[r14]=r`16+2*$i`,16		// Htable[`8+$i`].lo>>4
+	st8	[r15]=r`16+2*$i+1`,16		// Htable[`8+$i`].hi>>4
+	shrp	r`18+2*$i`=r`18+2*$i+1`,r`18+2*$i`,4	}
+{ .mmi;	add	$Htbl=256,sp			// &Htable[0]
+	add	$rem_8bit=rem_8bit#-gcm_ghash_4bit#,$rem_8bit
+	shr.u	r`18+2*$i+1`=r`18+2*$i+1`,4	};;
+{ .mmi;	st8	[r14]=r`18+2*$i`		// Htable[`8+$i`].lo>>4
+	st8	[r15]=r`18+2*$i+1`	}	// Htable[`8+$i`].hi>>4
+___
+
+$in="r15";
+@xi=("r16","r17");
+@rem=("r18","r19");
+($Alo,$Ahi,$Blo,$Bhi,$Zlo,$Zhi)=("r20","r21","r22","r23","r24","r25");
+($Atbl,$Btbl)=("r26","r27");
+
+$code.=<<___;	# (p16)
+{ .mmi;	ld1	$in=[$inp],-1			//(p16) *inp--
+	ld1	$xi[0]=[$Xip],-1		//(p16) *Xi--
+	cmp.eq	p0,p6=r0,r0		};;	//	clear p6
+___
+push (@xi,shift(@xi)); push (@rem,shift(@rem));	# "rotate" registers
+
+$code.=<<___;	# (p16),(p17)
+{ .mmi;	ld1	$xi[0]=[$Xip],-1		//(p16) *Xi--
+	xor	$xi[1]=$xi[1],$in	};;	//(p17) xi=$xi[i]^inp[i]
+{ .mii;	ld1	$in=[$inp],-1			//(p16) *inp--
+	dep	$Atbl=$xi[1],$Htbl,4,4		//(p17) &Htable[nlo].lo
+	and	$xi[1]=-16,$xi[1]	};;	//(p17) nhi=xi&0xf0
+.align	32
+.LOOP:
+{ .mmi;
+(p6)	st8	[$Xip]=$Zhi,13
+	xor	$Zlo=$Zlo,$Zlo
+	add	$Btbl=$xi[1],$Htbl	};;	//(p17) &Htable[nhi].lo
+___
+push (@xi,shift(@xi)); push (@rem,shift(@rem));	# "rotate" registers
+
+$code.=<<___;	# (p16),(p17),(p18)
+{ .mmi;	ld8	$Alo=[$Atbl],8			//(p18) Htable[nlo].lo,&Htable[nlo].hi
+	ld8	$rem[0]=[$Btbl],-256		//(p18) Htable[nhi].lo,&Hshr4[nhi].lo
+	xor	$xi[1]=$xi[1],$in	};;	//(p17) xi=$xi[i]^inp[i]
+{ .mfi;	ld8	$Ahi=[$Atbl]			//(p18) Htable[nlo].hi
+	dep	$Atbl=$xi[1],$Htbl,4,4	}	//(p17) &Htable[nlo].lo
+{ .mfi;	shladd	$rem[0]=$rem[0],4,r0		//(p18) Htable[nhi].lo<<4
+	xor	$Zlo=$Zlo,$Alo		};;	//(p18) Z.lo^=Htable[nlo].lo
+{ .mmi;	ld8	$Blo=[$Btbl],8			//(p18) Hshr4[nhi].lo,&Hshr4[nhi].hi
+	ld1	$in=[$inp],-1		}	//(p16) *inp--
+{ .mmi;	xor	$rem[0]=$rem[0],$Zlo		//(p18) Z.lo^(Htable[nhi].lo<<4)
+	mov	$Zhi=$Ahi			//(p18) Z.hi^=Htable[nlo].hi
+	and	$xi[1]=-16,$xi[1]	};;	//(p17) nhi=xi&0xf0
+{ .mmi;	ld8	$Bhi=[$Btbl]			//(p18) Hshr4[nhi].hi
+	ld1	$xi[0]=[$Xip],-1		//(p16) *Xi--
+	shrp	$Zlo=$Zhi,$Zlo,8	}	//(p18) Z.lo=(Z.hi<<56)|(Z.lo>>8)
+{ .mmi;	and	$rem[0]=$rem[0],$mask0xff	//(p18) rem=($Zlo^(Htable[nhi].lo<<4))&0xff
+	add	$Btbl=$xi[1],$Htbl	};;	//(p17) &Htable[nhi]
+___
+push (@xi,shift(@xi)); push (@rem,shift(@rem));	# "rotate" registers
+
+for ($i=1;$i<14;$i++) {
+# Above and below fragments are derived from this one by removing
+# unsuitable (p??) instructions.
+$code.=<<___;	# (p16),(p17),(p18),(p19)
+{ .mmi;	ld8	$Alo=[$Atbl],8			//(p18) Htable[nlo].lo,&Htable[nlo].hi
+	ld8	$rem[0]=[$Btbl],-256		//(p18) Htable[nhi].lo,&Hshr4[nhi].lo
+	shr.u	$Zhi=$Zhi,8		}	//(p19) Z.hi>>=8
+{ .mmi;	shladd	$rem[1]=$rem[1],1,$rem_8bit	//(p19) &rem_8bit[rem]
+	xor	$Zlo=$Zlo,$Blo			//(p19) Z.lo^=Hshr4[nhi].lo
+	xor	$xi[1]=$xi[1],$in	};;	//(p17) xi=$xi[i]^inp[i]
+{ .mmi;	ld8	$Ahi=[$Atbl]			//(p18) Htable[nlo].hi
+	ld2	$rem[1]=[$rem[1]]		//(p19) rem_8bit[rem]
+	dep	$Atbl=$xi[1],$Htbl,4,4	}	//(p17) &Htable[nlo].lo
+{ .mmi;	shladd	$rem[0]=$rem[0],4,r0		//(p18) Htable[nhi].lo<<4
+	xor	$Zlo=$Zlo,$Alo			//(p18) Z.lo^=Htable[nlo].lo
+	xor	$Zhi=$Zhi,$Bhi		};;	//(p19) Z.hi^=Hshr4[nhi].hi
+{ .mmi;	ld8	$Blo=[$Btbl],8			//(p18) Hshr4[nhi].lo,&Hshr4[nhi].hi
+	ld1	$in=[$inp],-1			//(p16) *inp--
+	shl	$rem[1]=$rem[1],48	}	//(p19) rem_8bit[rem]<<48
+{ .mmi;	xor	$rem[0]=$rem[0],$Zlo		//(p18) Z.lo^(Htable[nhi].lo<<4)
+	xor	$Zhi=$Zhi,$Ahi			//(p18) Z.hi^=Htable[nlo].hi
+	and	$xi[1]=-16,$xi[1]	};;	//(p17) nhi=xi&0xf0
+{ .mmi;	ld8	$Bhi=[$Btbl]			//(p18) Hshr4[nhi].hi
+	ld1	$xi[0]=[$Xip],-1		//(p16) *Xi--
+	shrp	$Zlo=$Zhi,$Zlo,8	}	//(p18) Z.lo=(Z.hi<<56)|(Z.lo>>8)
+{ .mmi;	and	$rem[0]=$rem[0],$mask0xff	//(p18) rem=($Zlo^(Htable[nhi].lo<<4))&0xff
+	xor	$Zhi=$Zhi,$rem[1]		//(p19) Z.hi^=rem_8bit[rem]<<48
+	add	$Btbl=$xi[1],$Htbl	};;	//(p17) &Htable[nhi]
+___
+push (@xi,shift(@xi)); push (@rem,shift(@rem));	# "rotate" registers
+}
+
+$code.=<<___;	# (p17),(p18),(p19)
+{ .mmi;	ld8	$Alo=[$Atbl],8			//(p18) Htable[nlo].lo,&Htable[nlo].hi
+	ld8	$rem[0]=[$Btbl],-256		//(p18) Htable[nhi].lo,&Hshr4[nhi].lo
+	shr.u	$Zhi=$Zhi,8		}	//(p19) Z.hi>>=8
+{ .mmi;	shladd	$rem[1]=$rem[1],1,$rem_8bit	//(p19) &rem_8bit[rem]
+	xor	$Zlo=$Zlo,$Blo			//(p19) Z.lo^=Hshr4[nhi].lo
+	xor	$xi[1]=$xi[1],$in	};;	//(p17) xi=$xi[i]^inp[i]
+{ .mmi;	ld8	$Ahi=[$Atbl]			//(p18) Htable[nlo].hi
+	ld2	$rem[1]=[$rem[1]]		//(p19) rem_8bit[rem]
+	dep	$Atbl=$xi[1],$Htbl,4,4	};;	//(p17) &Htable[nlo].lo
+{ .mmi;	shladd	$rem[0]=$rem[0],4,r0		//(p18) Htable[nhi].lo<<4
+	xor	$Zlo=$Zlo,$Alo			//(p18) Z.lo^=Htable[nlo].lo
+	xor	$Zhi=$Zhi,$Bhi		};;	//(p19) Z.hi^=Hshr4[nhi].hi
+{ .mmi;	ld8	$Blo=[$Btbl],8			//(p18) Hshr4[nhi].lo,&Hshr4[nhi].hi
+	shl	$rem[1]=$rem[1],48	}	//(p19) rem_8bit[rem]<<48
+{ .mmi;	xor	$rem[0]=$rem[0],$Zlo		//(p18) Z.lo^(Htable[nhi].lo<<4)
+	xor	$Zhi=$Zhi,$Ahi			//(p18) Z.hi^=Htable[nlo].hi
+	and	$xi[1]=-16,$xi[1]	};;	//(p17) nhi=xi&0xf0
+{ .mmi;	ld8	$Bhi=[$Btbl]			//(p18) Hshr4[nhi].hi
+	shrp	$Zlo=$Zhi,$Zlo,8	}	//(p18) Z.lo=(Z.hi<<56)|(Z.lo>>8)
+{ .mmi;	and	$rem[0]=$rem[0],$mask0xff	//(p18) rem=($Zlo^(Htable[nhi].lo<<4))&0xff
+	xor	$Zhi=$Zhi,$rem[1]		//(p19) Z.hi^=rem_8bit[rem]<<48
+	add	$Btbl=$xi[1],$Htbl	};;	//(p17) &Htable[nhi]
+___
+push (@xi,shift(@xi)); push (@rem,shift(@rem));	# "rotate" registers
+
+$code.=<<___;	# (p18),(p19)
+{ .mfi;	ld8	$Alo=[$Atbl],8			//(p18) Htable[nlo].lo,&Htable[nlo].hi
+	shr.u	$Zhi=$Zhi,8		}	//(p19) Z.hi>>=8
+{ .mfi;	shladd	$rem[1]=$rem[1],1,$rem_8bit	//(p19) &rem_8bit[rem]
+	xor	$Zlo=$Zlo,$Blo		};;	//(p19) Z.lo^=Hshr4[nhi].lo
+{ .mfi;	ld8	$Ahi=[$Atbl]			//(p18) Htable[nlo].hi
+	xor	$Zlo=$Zlo,$Alo		}	//(p18) Z.lo^=Htable[nlo].lo
+{ .mfi;	ld2	$rem[1]=[$rem[1]]		//(p19) rem_8bit[rem]
+	xor	$Zhi=$Zhi,$Bhi		};;	//(p19) Z.hi^=Hshr4[nhi].hi
+{ .mfi;	ld8	$Blo=[$Btbl],8			//(p18) Htable[nhi].lo,&Htable[nhi].hi
+	shl	$rem[1]=$rem[1],48	}	//(p19) rem_8bit[rem]<<48
+{ .mfi;	shladd	$rem[0]=$Zlo,4,r0		//(p18) Z.lo<<4
+	xor	$Zhi=$Zhi,$Ahi		};;	//(p18) Z.hi^=Htable[nlo].hi
+{ .mfi;	ld8	$Bhi=[$Btbl]			//(p18) Htable[nhi].hi
+	shrp	$Zlo=$Zhi,$Zlo,4	}	//(p18) Z.lo=(Z.hi<<60)|(Z.lo>>4)
+{ .mfi;	and	$rem[0]=$rem[0],$mask0xff	//(p18) rem=($Zlo^(Htable[nhi].lo<<4))&0xff
+	xor	$Zhi=$Zhi,$rem[1]	};;	//(p19) Z.hi^=rem_8bit[rem]<<48
+___
+push (@xi,shift(@xi)); push (@rem,shift(@rem));	# "rotate" registers
+
+$code.=<<___;	# (p19)
+{ .mmi;	cmp.ltu	p6,p0=$inp,$len
+	add	$inp=32,$inp
+	shr.u	$Zhi=$Zhi,4		}	//(p19) Z.hi>>=4
+{ .mmi;	shladd	$rem[1]=$rem[1],1,$rem_8bit	//(p19) &rem_8bit[rem]
+	xor	$Zlo=$Zlo,$Blo			//(p19) Z.lo^=Hshr4[nhi].lo
+	add	$Xip=9,$Xip		};;	//	&Xi.lo
+{ .mmi;	ld2	$rem[1]=[$rem[1]]		//(p19) rem_8bit[rem]
+(p6)	ld1	$in=[$inp],-1			//[p16] *inp--
+(p6)	extr.u	$xi[1]=$Zlo,8,8		}	//[p17] Xi[14]
+{ .mmi;	xor	$Zhi=$Zhi,$Bhi			//(p19) Z.hi^=Hshr4[nhi].hi
+(p6)	and	$xi[0]=$Zlo,$mask0xff	};;	//[p16] Xi[15]
+{ .mmi;	st8	[$Xip]=$Zlo,-8
+(p6)	xor	$xi[0]=$xi[0],$in		//[p17] xi=$xi[i]^inp[i]
+	shl	$rem[1]=$rem[1],48	};;	//(p19) rem_8bit[rem]<<48
+{ .mmi;
+(p6)	ld1	$in=[$inp],-1			//[p16] *inp--
+	xor	$Zhi=$Zhi,$rem[1]		//(p19) Z.hi^=rem_8bit[rem]<<48
+(p6)	dep	$Atbl=$xi[0],$Htbl,4,4	}	//[p17] &Htable[nlo].lo
+{ .mib;
+(p6)	and	$xi[0]=-16,$xi[0]		//[p17] nhi=xi&0xf0
+(p6)	br.cond.dptk.many	.LOOP	};;
+
+{ .mib;	st8	[$Xip]=$Zhi		};;
+{ .mib;	$rum	1<<1				// return to little-endian
+	.restore	sp
+	mov	sp=prevsp
+	br.ret.sptk.many	b0	};;
+.endp	gcm_ghash_4bit#
+___
+$code.=<<___;
+.align	128
+.type	rem_4bit#,\@object
+rem_4bit:
+        data8	0x0000<<48, 0x1C20<<48, 0x3840<<48, 0x2460<<48
+        data8	0x7080<<48, 0x6CA0<<48, 0x48C0<<48, 0x54E0<<48
+        data8	0xE100<<48, 0xFD20<<48, 0xD940<<48, 0xC560<<48
+        data8	0x9180<<48, 0x8DA0<<48, 0xA9C0<<48, 0xB5E0<<48
+.size	rem_4bit#,128
+.type	rem_8bit#,\@object
+rem_8bit:
+	data1	0x00,0x00, 0x01,0xC2, 0x03,0x84, 0x02,0x46, 0x07,0x08, 0x06,0xCA, 0x04,0x8C, 0x05,0x4E
+	data1	0x0E,0x10, 0x0F,0xD2, 0x0D,0x94, 0x0C,0x56, 0x09,0x18, 0x08,0xDA, 0x0A,0x9C, 0x0B,0x5E
+	data1	0x1C,0x20, 0x1D,0xE2, 0x1F,0xA4, 0x1E,0x66, 0x1B,0x28, 0x1A,0xEA, 0x18,0xAC, 0x19,0x6E
+	data1	0x12,0x30, 0x13,0xF2, 0x11,0xB4, 0x10,0x76, 0x15,0x38, 0x14,0xFA, 0x16,0xBC, 0x17,0x7E
+	data1	0x38,0x40, 0x39,0x82, 0x3B,0xC4, 0x3A,0x06, 0x3F,0x48, 0x3E,0x8A, 0x3C,0xCC, 0x3D,0x0E
+	data1	0x36,0x50, 0x37,0x92, 0x35,0xD4, 0x34,0x16, 0x31,0x58, 0x30,0x9A, 0x32,0xDC, 0x33,0x1E
+	data1	0x24,0x60, 0x25,0xA2, 0x27,0xE4, 0x26,0x26, 0x23,0x68, 0x22,0xAA, 0x20,0xEC, 0x21,0x2E
+	data1	0x2A,0x70, 0x2B,0xB2, 0x29,0xF4, 0x28,0x36, 0x2D,0x78, 0x2C,0xBA, 0x2E,0xFC, 0x2F,0x3E
+	data1	0x70,0x80, 0x71,0x42, 0x73,0x04, 0x72,0xC6, 0x77,0x88, 0x76,0x4A, 0x74,0x0C, 0x75,0xCE
+	data1	0x7E,0x90, 0x7F,0x52, 0x7D,0x14, 0x7C,0xD6, 0x79,0x98, 0x78,0x5A, 0x7A,0x1C, 0x7B,0xDE
+	data1	0x6C,0xA0, 0x6D,0x62, 0x6F,0x24, 0x6E,0xE6, 0x6B,0xA8, 0x6A,0x6A, 0x68,0x2C, 0x69,0xEE
+	data1	0x62,0xB0, 0x63,0x72, 0x61,0x34, 0x60,0xF6, 0x65,0xB8, 0x64,0x7A, 0x66,0x3C, 0x67,0xFE
+	data1	0x48,0xC0, 0x49,0x02, 0x4B,0x44, 0x4A,0x86, 0x4F,0xC8, 0x4E,0x0A, 0x4C,0x4C, 0x4D,0x8E
+	data1	0x46,0xD0, 0x47,0x12, 0x45,0x54, 0x44,0x96, 0x41,0xD8, 0x40,0x1A, 0x42,0x5C, 0x43,0x9E
+	data1	0x54,0xE0, 0x55,0x22, 0x57,0x64, 0x56,0xA6, 0x53,0xE8, 0x52,0x2A, 0x50,0x6C, 0x51,0xAE
+	data1	0x5A,0xF0, 0x5B,0x32, 0x59,0x74, 0x58,0xB6, 0x5D,0xF8, 0x5C,0x3A, 0x5E,0x7C, 0x5F,0xBE
+	data1	0xE1,0x00, 0xE0,0xC2, 0xE2,0x84, 0xE3,0x46, 0xE6,0x08, 0xE7,0xCA, 0xE5,0x8C, 0xE4,0x4E
+	data1	0xEF,0x10, 0xEE,0xD2, 0xEC,0x94, 0xED,0x56, 0xE8,0x18, 0xE9,0xDA, 0xEB,0x9C, 0xEA,0x5E
+	data1	0xFD,0x20, 0xFC,0xE2, 0xFE,0xA4, 0xFF,0x66, 0xFA,0x28, 0xFB,0xEA, 0xF9,0xAC, 0xF8,0x6E
+	data1	0xF3,0x30, 0xF2,0xF2, 0xF0,0xB4, 0xF1,0x76, 0xF4,0x38, 0xF5,0xFA, 0xF7,0xBC, 0xF6,0x7E
+	data1	0xD9,0x40, 0xD8,0x82, 0xDA,0xC4, 0xDB,0x06, 0xDE,0x48, 0xDF,0x8A, 0xDD,0xCC, 0xDC,0x0E
+	data1	0xD7,0x50, 0xD6,0x92, 0xD4,0xD4, 0xD5,0x16, 0xD0,0x58, 0xD1,0x9A, 0xD3,0xDC, 0xD2,0x1E
+	data1	0xC5,0x60, 0xC4,0xA2, 0xC6,0xE4, 0xC7,0x26, 0xC2,0x68, 0xC3,0xAA, 0xC1,0xEC, 0xC0,0x2E
+	data1	0xCB,0x70, 0xCA,0xB2, 0xC8,0xF4, 0xC9,0x36, 0xCC,0x78, 0xCD,0xBA, 0xCF,0xFC, 0xCE,0x3E
+	data1	0x91,0x80, 0x90,0x42, 0x92,0x04, 0x93,0xC6, 0x96,0x88, 0x97,0x4A, 0x95,0x0C, 0x94,0xCE
+	data1	0x9F,0x90, 0x9E,0x52, 0x9C,0x14, 0x9D,0xD6, 0x98,0x98, 0x99,0x5A, 0x9B,0x1C, 0x9A,0xDE
+	data1	0x8D,0xA0, 0x8C,0x62, 0x8E,0x24, 0x8F,0xE6, 0x8A,0xA8, 0x8B,0x6A, 0x89,0x2C, 0x88,0xEE
+	data1	0x83,0xB0, 0x82,0x72, 0x80,0x34, 0x81,0xF6, 0x84,0xB8, 0x85,0x7A, 0x87,0x3C, 0x86,0xFE
+	data1	0xA9,0xC0, 0xA8,0x02, 0xAA,0x44, 0xAB,0x86, 0xAE,0xC8, 0xAF,0x0A, 0xAD,0x4C, 0xAC,0x8E
+	data1	0xA7,0xD0, 0xA6,0x12, 0xA4,0x54, 0xA5,0x96, 0xA0,0xD8, 0xA1,0x1A, 0xA3,0x5C, 0xA2,0x9E
+	data1	0xB5,0xE0, 0xB4,0x22, 0xB6,0x64, 0xB7,0xA6, 0xB2,0xE8, 0xB3,0x2A, 0xB1,0x6C, 0xB0,0xAE
+	data1	0xBB,0xF0, 0xBA,0x32, 0xB8,0x74, 0xB9,0xB6, 0xBC,0xF8, 0xBD,0x3A, 0xBF,0x7C, 0xBE,0xBE
+.size	rem_8bit#,512
+stringz	"GHASH for IA64, CRYPTOGAMS by "
+___
+
+$code =~ s/mux1(\s+)\S+\@rev/nop.i$1 0x0/gm      if ($big_endian);
+$code =~ s/\`([^\`]*)\`/eval $1/gem;
+
+print $code;
+close STDOUT;
diff --git a/crypto/modes/asm/ghash-parisc.pl b/crypto/modes/asm/ghash-parisc.pl
new file mode 100644
index 000000000..1d6254543
--- /dev/null
+++ b/crypto/modes/asm/ghash-parisc.pl
@@ -0,0 +1,738 @@
+#! /usr/bin/env perl
+# Copyright 2010-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+#
+# ====================================================================
+# Written by Andy Polyakov  for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+#
+# April 2010
+#
+# The module implements "4-bit" GCM GHASH function and underlying
+# single multiplication operation in GF(2^128). "4-bit" means that it
+# uses 256 bytes per-key table [+128 bytes shared table]. On PA-7100LC
+# it processes one byte in 19.6 cycles, which is more than twice as
+# fast as code generated by gcc 3.2. PA-RISC 2.0 loop is scheduled for
+# 8 cycles, but measured performance on PA-8600 system is ~9 cycles per
+# processed byte. This is ~2.2x faster than 64-bit code generated by
+# vendor compiler (which used to be very hard to beat:-).
+#
+# Special thanks to polarhome.com for providing HP-UX account.
+
+$flavour = shift;
+$output = shift;
+open STDOUT,">$output";
+
+if ($flavour =~ /64/) {
+	$LEVEL		="2.0W";
+	$SIZE_T		=8;
+	$FRAME_MARKER	=80;
+	$SAVED_RP	=16;
+	$PUSH		="std";
+	$PUSHMA		="std,ma";
+	$POP		="ldd";
+	$POPMB		="ldd,mb";
+	$NREGS		=6;
+} else {
+	$LEVEL		="1.0";	#"\n\t.ALLOW\t2.0";
+	$SIZE_T		=4;
+	$FRAME_MARKER	=48;
+	$SAVED_RP	=20;
+	$PUSH		="stw";
+	$PUSHMA		="stwm";
+	$POP		="ldw";
+	$POPMB		="ldwm";
+	$NREGS		=11;
+}
+
+$FRAME=10*$SIZE_T+$FRAME_MARKER;# NREGS saved regs + frame marker
+				#                 [+ argument transfer]
+
+################# volatile registers
+$Xi="%r26";	# argument block
+$Htbl="%r25";
+$inp="%r24";
+$len="%r23";
+$Hhh=$Htbl;	# variables
+$Hll="%r22";
+$Zhh="%r21";
+$Zll="%r20";
+$cnt="%r19";
+$rem_4bit="%r28";
+$rem="%r29";
+$mask0xf0="%r31";
+
+################# preserved registers
+$Thh="%r1";
+$Tll="%r2";
+$nlo="%r3";
+$nhi="%r4";
+$byte="%r5";
+if ($SIZE_T==4) {
+	$Zhl="%r6";
+	$Zlh="%r7";
+	$Hhl="%r8";
+	$Hlh="%r9";
+	$Thl="%r10";
+	$Tlh="%r11";
+}
+$rem2="%r6";	# used in PA-RISC 2.0 code
+
+$code.=<<___;
+	.LEVEL	$LEVEL
+	.SPACE	\$TEXT\$
+	.SUBSPA	\$CODE\$,QUAD=0,ALIGN=8,ACCESS=0x2C,CODE_ONLY
+
+	.EXPORT	gcm_gmult_4bit,ENTRY,ARGW0=GR,ARGW1=GR
+	.ALIGN	64
+gcm_gmult_4bit
+	.PROC
+	.CALLINFO	FRAME=`$FRAME-10*$SIZE_T`,NO_CALLS,SAVE_RP,ENTRY_GR=$NREGS
+	.ENTRY
+	$PUSH	%r2,-$SAVED_RP(%sp)	; standard prologue
+	$PUSHMA	%r3,$FRAME(%sp)
+	$PUSH	%r4,`-$FRAME+1*$SIZE_T`(%sp)
+	$PUSH	%r5,`-$FRAME+2*$SIZE_T`(%sp)
+	$PUSH	%r6,`-$FRAME+3*$SIZE_T`(%sp)
+___
+$code.=<<___ if ($SIZE_T==4);
+	$PUSH	%r7,`-$FRAME+4*$SIZE_T`(%sp)
+	$PUSH	%r8,`-$FRAME+5*$SIZE_T`(%sp)
+	$PUSH	%r9,`-$FRAME+6*$SIZE_T`(%sp)
+	$PUSH	%r10,`-$FRAME+7*$SIZE_T`(%sp)
+	$PUSH	%r11,`-$FRAME+8*$SIZE_T`(%sp)
+___
+$code.=<<___;
+	blr	%r0,$rem_4bit
+	ldi	3,$rem
+L\$pic_gmult
+	andcm	$rem_4bit,$rem,$rem_4bit
+	addl	$inp,$len,$len
+	ldo	L\$rem_4bit-L\$pic_gmult($rem_4bit),$rem_4bit
+	ldi	0xf0,$mask0xf0
+___
+$code.=<<___ if ($SIZE_T==4);
+	ldi	31,$rem
+	mtctl	$rem,%cr11
+	extrd,u,*= $rem,%sar,1,$rem	; executes on PA-RISC 1.0
+	b	L\$parisc1_gmult
+	nop
+___
+
+$code.=<<___;
+	ldb	15($Xi),$nlo
+	ldo	8($Htbl),$Hll
+
+	and	$mask0xf0,$nlo,$nhi
+	depd,z	$nlo,59,4,$nlo
+
+	ldd	$nlo($Hll),$Zll
+	ldd	$nlo($Hhh),$Zhh
+
+	depd,z	$Zll,60,4,$rem
+	shrpd	$Zhh,$Zll,4,$Zll
+	extrd,u	$Zhh,59,60,$Zhh
+	ldb	14($Xi),$nlo
+
+	ldd	$nhi($Hll),$Tll
+	ldd	$nhi($Hhh),$Thh
+	and	$mask0xf0,$nlo,$nhi
+	depd,z	$nlo,59,4,$nlo
+
+	xor	$Tll,$Zll,$Zll
+	xor	$Thh,$Zhh,$Zhh
+	ldd	$rem($rem_4bit),$rem
+	b	L\$oop_gmult_pa2
+	ldi	13,$cnt
+
+	.ALIGN	8
+L\$oop_gmult_pa2
+	xor	$rem,$Zhh,$Zhh		; moved here to work around gas bug
+	depd,z	$Zll,60,4,$rem
+
+	shrpd	$Zhh,$Zll,4,$Zll
+	extrd,u	$Zhh,59,60,$Zhh
+	ldd	$nlo($Hll),$Tll
+	ldd	$nlo($Hhh),$Thh
+
+	xor	$Tll,$Zll,$Zll
+	xor	$Thh,$Zhh,$Zhh
+	ldd	$rem($rem_4bit),$rem
+
+	xor	$rem,$Zhh,$Zhh
+	depd,z	$Zll,60,4,$rem
+	ldbx	$cnt($Xi),$nlo
+
+	shrpd	$Zhh,$Zll,4,$Zll
+	extrd,u	$Zhh,59,60,$Zhh
+	ldd	$nhi($Hll),$Tll
+	ldd	$nhi($Hhh),$Thh
+
+	and	$mask0xf0,$nlo,$nhi
+	depd,z	$nlo,59,4,$nlo
+	ldd	$rem($rem_4bit),$rem
+
+	xor	$Tll,$Zll,$Zll
+	addib,uv -1,$cnt,L\$oop_gmult_pa2
+	xor	$Thh,$Zhh,$Zhh
+
+	xor	$rem,$Zhh,$Zhh
+	depd,z	$Zll,60,4,$rem
+
+	shrpd	$Zhh,$Zll,4,$Zll
+	extrd,u	$Zhh,59,60,$Zhh
+	ldd	$nlo($Hll),$Tll
+	ldd	$nlo($Hhh),$Thh
+
+	xor	$Tll,$Zll,$Zll
+	xor	$Thh,$Zhh,$Zhh
+	ldd	$rem($rem_4bit),$rem
+
+	xor	$rem,$Zhh,$Zhh
+	depd,z	$Zll,60,4,$rem
+
+	shrpd	$Zhh,$Zll,4,$Zll
+	extrd,u	$Zhh,59,60,$Zhh
+	ldd	$nhi($Hll),$Tll
+	ldd	$nhi($Hhh),$Thh
+
+	xor	$Tll,$Zll,$Zll
+	xor	$Thh,$Zhh,$Zhh
+	ldd	$rem($rem_4bit),$rem
+
+	xor	$rem,$Zhh,$Zhh
+	std	$Zll,8($Xi)
+	std	$Zhh,0($Xi)
+___
+
+$code.=<<___ if ($SIZE_T==4);
+	b	L\$done_gmult
+	nop
+
+L\$parisc1_gmult
+	ldb	15($Xi),$nlo
+	ldo	12($Htbl),$Hll
+	ldo	8($Htbl),$Hlh
+	ldo	4($Htbl),$Hhl
+
+	and	$mask0xf0,$nlo,$nhi
+	zdep	$nlo,27,4,$nlo
+
+	ldwx	$nlo($Hll),$Zll
+	ldwx	$nlo($Hlh),$Zlh
+	ldwx	$nlo($Hhl),$Zhl
+	ldwx	$nlo($Hhh),$Zhh
+	zdep	$Zll,28,4,$rem
+	ldb	14($Xi),$nlo
+	ldwx	$rem($rem_4bit),$rem
+	shrpw	$Zlh,$Zll,4,$Zll
+	ldwx	$nhi($Hll),$Tll
+	shrpw	$Zhl,$Zlh,4,$Zlh
+	ldwx	$nhi($Hlh),$Tlh
+	shrpw	$Zhh,$Zhl,4,$Zhl
+	ldwx	$nhi($Hhl),$Thl
+	extru	$Zhh,27,28,$Zhh
+	ldwx	$nhi($Hhh),$Thh
+	xor	$rem,$Zhh,$Zhh
+	and	$mask0xf0,$nlo,$nhi
+	zdep	$nlo,27,4,$nlo
+
+	xor	$Tll,$Zll,$Zll
+	ldwx	$nlo($Hll),$Tll
+	xor	$Tlh,$Zlh,$Zlh
+	ldwx	$nlo($Hlh),$Tlh
+	xor	$Thl,$Zhl,$Zhl
+	b	L\$oop_gmult_pa1
+	ldi	13,$cnt
+
+	.ALIGN	8
+L\$oop_gmult_pa1
+	zdep	$Zll,28,4,$rem
+	ldwx	$nlo($Hhl),$Thl
+	xor	$Thh,$Zhh,$Zhh
+	ldwx	$rem($rem_4bit),$rem
+	shrpw	$Zlh,$Zll,4,$Zll
+	ldwx	$nlo($Hhh),$Thh
+	shrpw	$Zhl,$Zlh,4,$Zlh
+	ldbx	$cnt($Xi),$nlo
+	xor	$Tll,$Zll,$Zll
+	ldwx	$nhi($Hll),$Tll
+	shrpw	$Zhh,$Zhl,4,$Zhl
+	xor	$Tlh,$Zlh,$Zlh
+	ldwx	$nhi($Hlh),$Tlh
+	extru	$Zhh,27,28,$Zhh
+	xor	$Thl,$Zhl,$Zhl
+	ldwx	$nhi($Hhl),$Thl
+	xor	$rem,$Zhh,$Zhh
+	zdep	$Zll,28,4,$rem
+	xor	$Thh,$Zhh,$Zhh
+	ldwx	$nhi($Hhh),$Thh
+	shrpw	$Zlh,$Zll,4,$Zll
+	ldwx	$rem($rem_4bit),$rem
+	shrpw	$Zhl,$Zlh,4,$Zlh
+	shrpw	$Zhh,$Zhl,4,$Zhl
+	and	$mask0xf0,$nlo,$nhi
+	extru	$Zhh,27,28,$Zhh
+	zdep	$nlo,27,4,$nlo
+	xor	$Tll,$Zll,$Zll
+	ldwx	$nlo($Hll),$Tll
+	xor	$Tlh,$Zlh,$Zlh
+	ldwx	$nlo($Hlh),$Tlh
+	xor	$rem,$Zhh,$Zhh
+	addib,uv -1,$cnt,L\$oop_gmult_pa1
+	xor	$Thl,$Zhl,$Zhl
+
+	zdep	$Zll,28,4,$rem
+	ldwx	$nlo($Hhl),$Thl
+	xor	$Thh,$Zhh,$Zhh
+	ldwx	$rem($rem_4bit),$rem
+	shrpw	$Zlh,$Zll,4,$Zll
+	ldwx	$nlo($Hhh),$Thh
+	shrpw	$Zhl,$Zlh,4,$Zlh
+	xor	$Tll,$Zll,$Zll
+	ldwx	$nhi($Hll),$Tll
+	shrpw	$Zhh,$Zhl,4,$Zhl
+	xor	$Tlh,$Zlh,$Zlh
+	ldwx	$nhi($Hlh),$Tlh
+	extru	$Zhh,27,28,$Zhh
+	xor	$rem,$Zhh,$Zhh
+	xor	$Thl,$Zhl,$Zhl
+	ldwx	$nhi($Hhl),$Thl
+	xor	$Thh,$Zhh,$Zhh
+	ldwx	$nhi($Hhh),$Thh
+	zdep	$Zll,28,4,$rem
+	ldwx	$rem($rem_4bit),$rem
+	shrpw	$Zlh,$Zll,4,$Zll
+	shrpw	$Zhl,$Zlh,4,$Zlh
+	shrpw	$Zhh,$Zhl,4,$Zhl
+	extru	$Zhh,27,28,$Zhh
+	xor	$Tll,$Zll,$Zll
+	xor	$Tlh,$Zlh,$Zlh
+	xor	$rem,$Zhh,$Zhh
+	stw	$Zll,12($Xi)
+	xor	$Thl,$Zhl,$Zhl
+	stw	$Zlh,8($Xi)
+	xor	$Thh,$Zhh,$Zhh
+	stw	$Zhl,4($Xi)
+	stw	$Zhh,0($Xi)
+___
+$code.=<<___;
+L\$done_gmult
+	$POP	`-$FRAME-$SAVED_RP`(%sp),%r2		; standard epilogue
+	$POP	`-$FRAME+1*$SIZE_T`(%sp),%r4
+	$POP	`-$FRAME+2*$SIZE_T`(%sp),%r5
+	$POP	`-$FRAME+3*$SIZE_T`(%sp),%r6
+___
+$code.=<<___ if ($SIZE_T==4);
+	$POP	`-$FRAME+4*$SIZE_T`(%sp),%r7
+	$POP	`-$FRAME+5*$SIZE_T`(%sp),%r8
+	$POP	`-$FRAME+6*$SIZE_T`(%sp),%r9
+	$POP	`-$FRAME+7*$SIZE_T`(%sp),%r10
+	$POP	`-$FRAME+8*$SIZE_T`(%sp),%r11
+___
+$code.=<<___;
+	bv	(%r2)
+	.EXIT
+	$POPMB	-$FRAME(%sp),%r3
+	.PROCEND
+
+	.EXPORT	gcm_ghash_4bit,ENTRY,ARGW0=GR,ARGW1=GR,ARGW2=GR,ARGW3=GR
+	.ALIGN	64
+gcm_ghash_4bit
+	.PROC
+	.CALLINFO	FRAME=`$FRAME-10*$SIZE_T`,NO_CALLS,SAVE_RP,ENTRY_GR=11
+	.ENTRY
+	$PUSH	%r2,-$SAVED_RP(%sp)	; standard prologue
+	$PUSHMA	%r3,$FRAME(%sp)
+	$PUSH	%r4,`-$FRAME+1*$SIZE_T`(%sp)
+	$PUSH	%r5,`-$FRAME+2*$SIZE_T`(%sp)
+	$PUSH	%r6,`-$FRAME+3*$SIZE_T`(%sp)
+___
+$code.=<<___ if ($SIZE_T==4);
+	$PUSH	%r7,`-$FRAME+4*$SIZE_T`(%sp)
+	$PUSH	%r8,`-$FRAME+5*$SIZE_T`(%sp)
+	$PUSH	%r9,`-$FRAME+6*$SIZE_T`(%sp)
+	$PUSH	%r10,`-$FRAME+7*$SIZE_T`(%sp)
+	$PUSH	%r11,`-$FRAME+8*$SIZE_T`(%sp)
+___
+$code.=<<___;
+	blr	%r0,$rem_4bit
+	ldi	3,$rem
+L\$pic_ghash
+	andcm	$rem_4bit,$rem,$rem_4bit
+	addl	$inp,$len,$len
+	ldo	L\$rem_4bit-L\$pic_ghash($rem_4bit),$rem_4bit
+	ldi	0xf0,$mask0xf0
+___
+$code.=<<___ if ($SIZE_T==4);
+	ldi	31,$rem
+	mtctl	$rem,%cr11
+	extrd,u,*= $rem,%sar,1,$rem	; executes on PA-RISC 1.0
+	b	L\$parisc1_ghash
+	nop
+___
+
+$code.=<<___;
+	ldb	15($Xi),$nlo
+	ldo	8($Htbl),$Hll
+
+L\$outer_ghash_pa2
+	ldb	15($inp),$nhi
+	xor	$nhi,$nlo,$nlo
+	and	$mask0xf0,$nlo,$nhi
+	depd,z	$nlo,59,4,$nlo
+
+	ldd	$nlo($Hll),$Zll
+	ldd	$nlo($Hhh),$Zhh
+
+	depd,z	$Zll,60,4,$rem
+	shrpd	$Zhh,$Zll,4,$Zll
+	extrd,u	$Zhh,59,60,$Zhh
+	ldb	14($Xi),$nlo
+	ldb	14($inp),$byte
+
+	ldd	$nhi($Hll),$Tll
+	ldd	$nhi($Hhh),$Thh
+	xor	$byte,$nlo,$nlo
+	and	$mask0xf0,$nlo,$nhi
+	depd,z	$nlo,59,4,$nlo
+
+	xor	$Tll,$Zll,$Zll
+	xor	$Thh,$Zhh,$Zhh
+	ldd	$rem($rem_4bit),$rem
+	b	L\$oop_ghash_pa2
+	ldi	13,$cnt
+
+	.ALIGN	8
+L\$oop_ghash_pa2
+	xor	$rem,$Zhh,$Zhh		; moved here to work around gas bug
+	depd,z	$Zll,60,4,$rem2
+
+	shrpd	$Zhh,$Zll,4,$Zll
+	extrd,u	$Zhh,59,60,$Zhh
+	ldd	$nlo($Hll),$Tll
+	ldd	$nlo($Hhh),$Thh
+
+	xor	$Tll,$Zll,$Zll
+	xor	$Thh,$Zhh,$Zhh
+	ldbx	$cnt($Xi),$nlo
+	ldbx	$cnt($inp),$byte
+
+	depd,z	$Zll,60,4,$rem
+	shrpd	$Zhh,$Zll,4,$Zll
+	ldd	$rem2($rem_4bit),$rem2
+
+	xor	$rem2,$Zhh,$Zhh
+	xor	$byte,$nlo,$nlo
+	ldd	$nhi($Hll),$Tll
+	ldd	$nhi($Hhh),$Thh
+
+	and	$mask0xf0,$nlo,$nhi
+	depd,z	$nlo,59,4,$nlo
+
+	extrd,u	$Zhh,59,60,$Zhh
+	xor	$Tll,$Zll,$Zll
+
+	ldd	$rem($rem_4bit),$rem
+	addib,uv -1,$cnt,L\$oop_ghash_pa2
+	xor	$Thh,$Zhh,$Zhh
+
+	xor	$rem,$Zhh,$Zhh
+	depd,z	$Zll,60,4,$rem2
+
+	shrpd	$Zhh,$Zll,4,$Zll
+	extrd,u	$Zhh,59,60,$Zhh
+	ldd	$nlo($Hll),$Tll
+	ldd	$nlo($Hhh),$Thh
+
+	xor	$Tll,$Zll,$Zll
+	xor	$Thh,$Zhh,$Zhh
+
+	depd,z	$Zll,60,4,$rem
+	shrpd	$Zhh,$Zll,4,$Zll
+	ldd	$rem2($rem_4bit),$rem2
+
+	xor	$rem2,$Zhh,$Zhh
+	ldd	$nhi($Hll),$Tll
+	ldd	$nhi($Hhh),$Thh
+
+	extrd,u	$Zhh,59,60,$Zhh
+	xor	$Tll,$Zll,$Zll
+	xor	$Thh,$Zhh,$Zhh
+	ldd	$rem($rem_4bit),$rem
+
+	xor	$rem,$Zhh,$Zhh
+	std	$Zll,8($Xi)
+	ldo	16($inp),$inp
+	std	$Zhh,0($Xi)
+	cmpb,*<> $inp,$len,L\$outer_ghash_pa2
+	copy	$Zll,$nlo
+___
+
+$code.=<<___ if ($SIZE_T==4);
+	b	L\$done_ghash
+	nop
+
+L\$parisc1_ghash
+	ldb	15($Xi),$nlo
+	ldo	12($Htbl),$Hll
+	ldo	8($Htbl),$Hlh
+	ldo	4($Htbl),$Hhl
+
+L\$outer_ghash_pa1
+	ldb	15($inp),$byte
+	xor	$byte,$nlo,$nlo
+	and	$mask0xf0,$nlo,$nhi
+	zdep	$nlo,27,4,$nlo
+
+	ldwx	$nlo($Hll),$Zll
+	ldwx	$nlo($Hlh),$Zlh
+	ldwx	$nlo($Hhl),$Zhl
+	ldwx	$nlo($Hhh),$Zhh
+	zdep	$Zll,28,4,$rem
+	ldb	14($Xi),$nlo
+	ldb	14($inp),$byte
+	ldwx	$rem($rem_4bit),$rem
+	shrpw	$Zlh,$Zll,4,$Zll
+	ldwx	$nhi($Hll),$Tll
+	shrpw	$Zhl,$Zlh,4,$Zlh
+	ldwx	$nhi($Hlh),$Tlh
+	shrpw	$Zhh,$Zhl,4,$Zhl
+	ldwx	$nhi($Hhl),$Thl
+	extru	$Zhh,27,28,$Zhh
+	ldwx	$nhi($Hhh),$Thh
+	xor	$byte,$nlo,$nlo
+	xor	$rem,$Zhh,$Zhh
+	and	$mask0xf0,$nlo,$nhi
+	zdep	$nlo,27,4,$nlo
+
+	xor	$Tll,$Zll,$Zll
+	ldwx	$nlo($Hll),$Tll
+	xor	$Tlh,$Zlh,$Zlh
+	ldwx	$nlo($Hlh),$Tlh
+	xor	$Thl,$Zhl,$Zhl
+	b	L\$oop_ghash_pa1
+	ldi	13,$cnt
+
+	.ALIGN	8
+L\$oop_ghash_pa1
+	zdep	$Zll,28,4,$rem
+	ldwx	$nlo($Hhl),$Thl
+	xor	$Thh,$Zhh,$Zhh
+	ldwx	$rem($rem_4bit),$rem
+	shrpw	$Zlh,$Zll,4,$Zll
+	ldwx	$nlo($Hhh),$Thh
+	shrpw	$Zhl,$Zlh,4,$Zlh
+	ldbx	$cnt($Xi),$nlo
+	xor	$Tll,$Zll,$Zll
+	ldwx	$nhi($Hll),$Tll
+	shrpw	$Zhh,$Zhl,4,$Zhl
+	ldbx	$cnt($inp),$byte
+	xor	$Tlh,$Zlh,$Zlh
+	ldwx	$nhi($Hlh),$Tlh
+	extru	$Zhh,27,28,$Zhh
+	xor	$Thl,$Zhl,$Zhl
+	ldwx	$nhi($Hhl),$Thl
+	xor	$rem,$Zhh,$Zhh
+	zdep	$Zll,28,4,$rem
+	xor	$Thh,$Zhh,$Zhh
+	ldwx	$nhi($Hhh),$Thh
+	shrpw	$Zlh,$Zll,4,$Zll
+	ldwx	$rem($rem_4bit),$rem
+	shrpw	$Zhl,$Zlh,4,$Zlh
+	xor	$byte,$nlo,$nlo
+	shrpw	$Zhh,$Zhl,4,$Zhl
+	and	$mask0xf0,$nlo,$nhi
+	extru	$Zhh,27,28,$Zhh
+	zdep	$nlo,27,4,$nlo
+	xor	$Tll,$Zll,$Zll
+	ldwx	$nlo($Hll),$Tll
+	xor	$Tlh,$Zlh,$Zlh
+	ldwx	$nlo($Hlh),$Tlh
+	xor	$rem,$Zhh,$Zhh
+	addib,uv -1,$cnt,L\$oop_ghash_pa1
+	xor	$Thl,$Zhl,$Zhl
+
+	zdep	$Zll,28,4,$rem
+	ldwx	$nlo($Hhl),$Thl
+	xor	$Thh,$Zhh,$Zhh
+	ldwx	$rem($rem_4bit),$rem
+	shrpw	$Zlh,$Zll,4,$Zll
+	ldwx	$nlo($Hhh),$Thh
+	shrpw	$Zhl,$Zlh,4,$Zlh
+	xor	$Tll,$Zll,$Zll
+	ldwx	$nhi($Hll),$Tll
+	shrpw	$Zhh,$Zhl,4,$Zhl
+	xor	$Tlh,$Zlh,$Zlh
+	ldwx	$nhi($Hlh),$Tlh
+	extru	$Zhh,27,28,$Zhh
+	xor	$rem,$Zhh,$Zhh
+	xor	$Thl,$Zhl,$Zhl
+	ldwx	$nhi($Hhl),$Thl
+	xor	$Thh,$Zhh,$Zhh
+	ldwx	$nhi($Hhh),$Thh
+	zdep	$Zll,28,4,$rem
+	ldwx	$rem($rem_4bit),$rem
+	shrpw	$Zlh,$Zll,4,$Zll
+	shrpw	$Zhl,$Zlh,4,$Zlh
+	shrpw	$Zhh,$Zhl,4,$Zhl
+	extru	$Zhh,27,28,$Zhh
+	xor	$Tll,$Zll,$Zll
+	xor	$Tlh,$Zlh,$Zlh
+	xor	$rem,$Zhh,$Zhh
+	stw	$Zll,12($Xi)
+	xor	$Thl,$Zhl,$Zhl
+	stw	$Zlh,8($Xi)
+	xor	$Thh,$Zhh,$Zhh
+	stw	$Zhl,4($Xi)
+	ldo	16($inp),$inp
+	stw	$Zhh,0($Xi)
+	comb,<>	$inp,$len,L\$outer_ghash_pa1
+	copy	$Zll,$nlo
+___
+$code.=<<___;
+L\$done_ghash
+	$POP	`-$FRAME-$SAVED_RP`(%sp),%r2		; standard epilogue
+	$POP	`-$FRAME+1*$SIZE_T`(%sp),%r4
+	$POP	`-$FRAME+2*$SIZE_T`(%sp),%r5
+	$POP	`-$FRAME+3*$SIZE_T`(%sp),%r6
+___
+$code.=<<___ if ($SIZE_T==4);
+	$POP	`-$FRAME+4*$SIZE_T`(%sp),%r7
+	$POP	`-$FRAME+5*$SIZE_T`(%sp),%r8
+	$POP	`-$FRAME+6*$SIZE_T`(%sp),%r9
+	$POP	`-$FRAME+7*$SIZE_T`(%sp),%r10
+	$POP	`-$FRAME+8*$SIZE_T`(%sp),%r11
+___
+$code.=<<___;
+	bv	(%r2)
+	.EXIT
+	$POPMB	-$FRAME(%sp),%r3
+	.PROCEND
+
+	.ALIGN	64
+L\$rem_4bit
+	.WORD	`0x0000<<16`,0,`0x1C20<<16`,0,`0x3840<<16`,0,`0x2460<<16`,0
+	.WORD	`0x7080<<16`,0,`0x6CA0<<16`,0,`0x48C0<<16`,0,`0x54E0<<16`,0
+	.WORD	`0xE100<<16`,0,`0xFD20<<16`,0,`0xD940<<16`,0,`0xC560<<16`,0
+	.WORD	`0x9180<<16`,0,`0x8DA0<<16`,0,`0xA9C0<<16`,0,`0xB5E0<<16`,0
+	.STRINGZ "GHASH for PA-RISC, GRYPTOGAMS by "
+	.ALIGN	64
+___
+
+# Explicitly encode PA-RISC 2.0 instructions used in this module, so
+# that it can be compiled with .LEVEL 1.0. It should be noted that I
+# wouldn't have to do this, if GNU assembler understood .ALLOW 2.0
+# directive...
+
+my $ldd = sub {
+  my ($mod,$args) = @_;
+  my $orig = "ldd$mod\t$args";
+
+    if ($args =~ /%r([0-9]+)\(%r([0-9]+)\),%r([0-9]+)/)		# format 4
+    {	my $opcode=(0x03<<26)|($2<<21)|($1<<16)|(3<<6)|$3;
+	sprintf "\t.WORD\t0x%08x\t; %s",$opcode,$orig;
+    }
+    elsif ($args =~ /(\-?[0-9]+)\(%r([0-9]+)\),%r([0-9]+)/)	# format 5
+    {	my $opcode=(0x03<<26)|($2<<21)|(1<<12)|(3<<6)|$3;
+	$opcode|=(($1&0xF)<<17)|(($1&0x10)<<12);		# encode offset
+	$opcode|=(1<<5)  if ($mod =~ /^,m/);
+	$opcode|=(1<<13) if ($mod =~ /^,mb/);
+	sprintf "\t.WORD\t0x%08x\t; %s",$opcode,$orig;
+    }
+    else { "\t".$orig; }
+};
+
+my $std = sub {
+  my ($mod,$args) = @_;
+  my $orig = "std$mod\t$args";
+
+    if ($args =~ /%r([0-9]+),(\-?[0-9]+)\(%r([0-9]+)\)/) # format 3 suffices
+    {	my $opcode=(0x1c<<26)|($3<<21)|($1<<16)|(($2&0x1FF8)<<1)|(($2>>13)&1);
+	sprintf "\t.WORD\t0x%08x\t; %s",$opcode,$orig;
+    }
+    else { "\t".$orig; }
+};
+
+my $extrd = sub {
+  my ($mod,$args) = @_;
+  my $orig = "extrd$mod\t$args";
+
+    # I only have ",u" completer, it's implicitly encoded...
+    if ($args =~ /%r([0-9]+),([0-9]+),([0-9]+),%r([0-9]+)/)	# format 15
+    {	my $opcode=(0x36<<26)|($1<<21)|($4<<16);
+	my $len=32-$3;
+	$opcode |= (($2&0x20)<<6)|(($2&0x1f)<<5);		# encode pos
+	$opcode |= (($len&0x20)<<7)|($len&0x1f);		# encode len
+	sprintf "\t.WORD\t0x%08x\t; %s",$opcode,$orig;
+    }
+    elsif ($args =~ /%r([0-9]+),%sar,([0-9]+),%r([0-9]+)/)	# format 12
+    {	my $opcode=(0x34<<26)|($1<<21)|($3<<16)|(2<<11)|(1<<9);
+	my $len=32-$2;
+	$opcode |= (($len&0x20)<<3)|($len&0x1f);		# encode len
+	$opcode |= (1<<13) if ($mod =~ /,\**=/);
+	sprintf "\t.WORD\t0x%08x\t; %s",$opcode,$orig;
+    }
+    else { "\t".$orig; }
+};
+
+my $shrpd = sub {
+  my ($mod,$args) = @_;
+  my $orig = "shrpd$mod\t$args";
+
+    if ($args =~ /%r([0-9]+),%r([0-9]+),([0-9]+),%r([0-9]+)/)	# format 14
+    {	my $opcode=(0x34<<26)|($2<<21)|($1<<16)|(1<<10)|$4;
+	my $cpos=63-$3;
+	$opcode |= (($cpos&0x20)<<6)|(($cpos&0x1f)<<5);		# encode sa
+	sprintf "\t.WORD\t0x%08x\t; %s",$opcode,$orig;
+    }
+    elsif ($args =~ /%r([0-9]+),%r([0-9]+),%sar,%r([0-9]+)/)	# format 11
+    {	sprintf "\t.WORD\t0x%08x\t; %s",
+		(0x34<<26)|($2<<21)|($1<<16)|(1<<9)|$3,$orig;
+    }
+    else { "\t".$orig; }
+};
+
+my $depd = sub {
+  my ($mod,$args) = @_;
+  my $orig = "depd$mod\t$args";
+
+    # I only have ",z" completer, it's impicitly encoded...
+    if ($args =~ /%r([0-9]+),([0-9]+),([0-9]+),%r([0-9]+)/)	# format 16
+    {	my $opcode=(0x3c<<26)|($4<<21)|($1<<16);
+    	my $cpos=63-$2;
+	my $len=32-$3;
+	$opcode |= (($cpos&0x20)<<6)|(($cpos&0x1f)<<5);		# encode pos
+	$opcode |= (($len&0x20)<<7)|($len&0x1f);		# encode len
+	sprintf "\t.WORD\t0x%08x\t; %s",$opcode,$orig;
+    }
+    else { "\t".$orig; }
+};
+
+sub assemble {
+  my ($mnemonic,$mod,$args)=@_;
+  my $opcode = eval("\$$mnemonic");
+
+    ref($opcode) eq 'CODE' ? &$opcode($mod,$args) : "\t$mnemonic$mod\t$args";
+}
+
+foreach (split("\n",$code)) {
+	s/\`([^\`]*)\`/eval $1/ge;
+	if ($SIZE_T==4) {
+		s/^\s+([a-z]+)([\S]*)\s+([\S]*)/&assemble($1,$2,$3)/e;
+		s/cmpb,\*/comb,/;
+		s/,\*/,/;
+	}
+	s/\bbv\b/bve/	if ($SIZE_T==8);
+	print $_,"\n";
+}
+
+close STDOUT;
diff --git a/crypto/modes/asm/ghash-s390x.pl b/crypto/modes/asm/ghash-s390x.pl
new file mode 100644
index 000000000..65ffaf98b
--- /dev/null
+++ b/crypto/modes/asm/ghash-s390x.pl
@@ -0,0 +1,267 @@
+#! /usr/bin/env perl
+# Copyright 2010-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+
+# ====================================================================
+# Written by Andy Polyakov  for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+
+# September 2010.
+#
+# The module implements "4-bit" GCM GHASH function and underlying
+# single multiplication operation in GF(2^128). "4-bit" means that it
+# uses 256 bytes per-key table [+128 bytes shared table]. Performance
+# was measured to be ~18 cycles per processed byte on z10, which is
+# almost 40% better than gcc-generated code. It should be noted that
+# 18 cycles is worse result than expected: loop is scheduled for 12
+# and the result should be close to 12. In the lack of instruction-
+# level profiling data it's impossible to tell why...
+
+# November 2010.
+#
+# Adapt for -m31 build. If kernel supports what's called "highgprs"
+# feature on Linux [see /proc/cpuinfo], it's possible to use 64-bit
+# instructions and achieve "64-bit" performance even in 31-bit legacy
+# application context. The feature is not specific to any particular
+# processor, as long as it's "z-CPU". Latter implies that the code
+# remains z/Architecture specific. On z990 it was measured to perform
+# 2.8x better than 32-bit code generated by gcc 4.3.
+
+# March 2011.
+#
+# Support for hardware KIMD-GHASH is verified to produce correct
+# result and therefore is engaged. On z196 it was measured to process
+# 8KB buffer ~7 faster than software implementation. It's not as
+# impressive for smaller buffer sizes and for smallest 16-bytes buffer
+# it's actually almost 2 times slower. Which is the reason why
+# KIMD-GHASH is not used in gcm_gmult_4bit.
+
+$flavour = shift;
+
+if ($flavour =~ /3[12]/) {
+	$SIZE_T=4;
+	$g="";
+} else {
+	$SIZE_T=8;
+	$g="g";
+}
+
+while (($output=shift) && ($output!~/\w[\w\-]*\.\w+$/)) {}
+open STDOUT,">$output";
+
+$softonly=0;
+
+$Zhi="%r0";
+$Zlo="%r1";
+
+$Xi="%r2";	# argument block
+$Htbl="%r3";
+$inp="%r4";
+$len="%r5";
+
+$rem0="%r6";	# variables
+$rem1="%r7";
+$nlo="%r8";
+$nhi="%r9";
+$xi="%r10";
+$cnt="%r11";
+$tmp="%r12";
+$x78="%r13";
+$rem_4bit="%r14";
+
+$sp="%r15";
+
+$code.=<<___;
+.text
+
+.globl	gcm_gmult_4bit
+.align	32
+gcm_gmult_4bit:
+___
+$code.=<<___ if(!$softonly && 0);	# hardware is slow for single block...
+	larl	%r1,OPENSSL_s390xcap_P
+	lg	%r0,0(%r1)
+	tmhl	%r0,0x4000	# check for message-security-assist
+	jz	.Lsoft_gmult
+	lghi	%r0,0
+	lg	%r1,24(%r1)	# load second word of kimd capabilities vector
+	tmhh	%r1,0x4000	# check for function 65
+	jz	.Lsoft_gmult
+	stg	%r0,16($sp)	# arrange 16 bytes of zero input
+	stg	%r0,24($sp)
+	lghi	%r0,65		# function 65
+	la	%r1,0($Xi)	# H lies right after Xi in gcm128_context
+	la	$inp,16($sp)
+	lghi	$len,16
+	.long	0xb93e0004	# kimd %r0,$inp
+	brc	1,.-4		# pay attention to "partial completion"
+	br	%r14
+.align	32
+.Lsoft_gmult:
+___
+$code.=<<___;
+	stm${g}	%r6,%r14,6*$SIZE_T($sp)
+
+	aghi	$Xi,-1
+	lghi	$len,1
+	lghi	$x78,`0xf<<3`
+	larl	$rem_4bit,rem_4bit
+
+	lg	$Zlo,8+1($Xi)		# Xi
+	j	.Lgmult_shortcut
+.type	gcm_gmult_4bit,\@function
+.size	gcm_gmult_4bit,(.-gcm_gmult_4bit)
+
+.globl	gcm_ghash_4bit
+.align	32
+gcm_ghash_4bit:
+___
+$code.=<<___ if(!$softonly);
+	larl	%r1,OPENSSL_s390xcap_P
+	lg	%r0,0(%r1)
+	tmhl	%r0,0x4000	# check for message-security-assist
+	jz	.Lsoft_ghash
+	lghi	%r0,0
+	la	%r1,16($sp)
+	.long	0xb93e0004	# kimd %r0,%r4
+	lg	%r1,24($sp)
+	tmhh	%r1,0x4000	# check for function 65
+	jz	.Lsoft_ghash
+	lghi	%r0,65		# function 65
+	la	%r1,0($Xi)	# H lies right after Xi in gcm128_context
+	.long	0xb93e0004	# kimd %r0,$inp
+	brc	1,.-4		# pay attention to "partial completion"
+	br	%r14
+.align	32
+.Lsoft_ghash:
+___
+$code.=<<___ if ($flavour =~ /3[12]/);
+	llgfr	$len,$len
+___
+$code.=<<___;
+	stm${g}	%r6,%r14,6*$SIZE_T($sp)
+
+	aghi	$Xi,-1
+	srlg	$len,$len,4
+	lghi	$x78,`0xf<<3`
+	larl	$rem_4bit,rem_4bit
+
+	lg	$Zlo,8+1($Xi)		# Xi
+	lg	$Zhi,0+1($Xi)
+	lghi	$tmp,0
+.Louter:
+	xg	$Zhi,0($inp)		# Xi ^= inp 
+	xg	$Zlo,8($inp)
+	xgr	$Zhi,$tmp
+	stg	$Zlo,8+1($Xi)
+	stg	$Zhi,0+1($Xi)
+
+.Lgmult_shortcut:
+	lghi	$tmp,0xf0
+	sllg	$nlo,$Zlo,4
+	srlg	$xi,$Zlo,8		# extract second byte
+	ngr	$nlo,$tmp
+	lgr	$nhi,$Zlo
+	lghi	$cnt,14
+	ngr	$nhi,$tmp
+
+	lg	$Zlo,8($nlo,$Htbl)
+	lg	$Zhi,0($nlo,$Htbl)
+
+	sllg	$nlo,$xi,4
+	sllg	$rem0,$Zlo,3
+	ngr	$nlo,$tmp
+	ngr	$rem0,$x78
+	ngr	$xi,$tmp
+
+	sllg	$tmp,$Zhi,60
+	srlg	$Zlo,$Zlo,4
+	srlg	$Zhi,$Zhi,4
+	xg	$Zlo,8($nhi,$Htbl)
+	xg	$Zhi,0($nhi,$Htbl)
+	lgr	$nhi,$xi
+	sllg	$rem1,$Zlo,3
+	xgr	$Zlo,$tmp
+	ngr	$rem1,$x78
+	sllg	$tmp,$Zhi,60
+	j	.Lghash_inner
+.align	16
+.Lghash_inner:
+	srlg	$Zlo,$Zlo,4
+	srlg	$Zhi,$Zhi,4
+	xg	$Zlo,8($nlo,$Htbl)
+	llgc	$xi,0($cnt,$Xi)
+	xg	$Zhi,0($nlo,$Htbl)
+	sllg	$nlo,$xi,4
+	xg	$Zhi,0($rem0,$rem_4bit)
+	nill	$nlo,0xf0
+	sllg	$rem0,$Zlo,3
+	xgr	$Zlo,$tmp
+	ngr	$rem0,$x78
+	nill	$xi,0xf0
+
+	sllg	$tmp,$Zhi,60
+	srlg	$Zlo,$Zlo,4
+	srlg	$Zhi,$Zhi,4
+	xg	$Zlo,8($nhi,$Htbl)
+	xg	$Zhi,0($nhi,$Htbl)
+	lgr	$nhi,$xi
+	xg	$Zhi,0($rem1,$rem_4bit)
+	sllg	$rem1,$Zlo,3
+	xgr	$Zlo,$tmp
+	ngr	$rem1,$x78
+	sllg	$tmp,$Zhi,60
+	brct	$cnt,.Lghash_inner
+
+	srlg	$Zlo,$Zlo,4
+	srlg	$Zhi,$Zhi,4
+	xg	$Zlo,8($nlo,$Htbl)
+	xg	$Zhi,0($nlo,$Htbl)
+	sllg	$xi,$Zlo,3
+	xg	$Zhi,0($rem0,$rem_4bit)
+	xgr	$Zlo,$tmp
+	ngr	$xi,$x78
+
+	sllg	$tmp,$Zhi,60
+	srlg	$Zlo,$Zlo,4
+	srlg	$Zhi,$Zhi,4
+	xg	$Zlo,8($nhi,$Htbl)
+	xg	$Zhi,0($nhi,$Htbl)
+	xgr	$Zlo,$tmp
+	xg	$Zhi,0($rem1,$rem_4bit)
+
+	lg	$tmp,0($xi,$rem_4bit)
+	la	$inp,16($inp)
+	sllg	$tmp,$tmp,4		# correct last rem_4bit[rem]
+	brctg	$len,.Louter
+
+	xgr	$Zhi,$tmp
+	stg	$Zlo,8+1($Xi)
+	stg	$Zhi,0+1($Xi)
+	lm${g}	%r6,%r14,6*$SIZE_T($sp)
+	br	%r14
+.type	gcm_ghash_4bit,\@function
+.size	gcm_ghash_4bit,(.-gcm_ghash_4bit)
+
+.align	64
+rem_4bit:
+	.long	`0x0000<<12`,0,`0x1C20<<12`,0,`0x3840<<12`,0,`0x2460<<12`,0
+	.long	`0x7080<<12`,0,`0x6CA0<<12`,0,`0x48C0<<12`,0,`0x54E0<<12`,0
+	.long	`0xE100<<12`,0,`0xFD20<<12`,0,`0xD940<<12`,0,`0xC560<<12`,0
+	.long	`0x9180<<12`,0,`0x8DA0<<12`,0,`0xA9C0<<12`,0,`0xB5E0<<12`,0
+.type	rem_4bit,\@object
+.size	rem_4bit,(.-rem_4bit)
+.string	"GHASH for s390x, CRYPTOGAMS by "
+___
+
+$code =~ s/\`([^\`]*)\`/eval $1/gem;
+print $code;
+close STDOUT;
diff --git a/crypto/modes/asm/ghash-sparcv9.pl b/crypto/modes/asm/ghash-sparcv9.pl
new file mode 100644
index 000000000..c4eb3b1f0
--- /dev/null
+++ b/crypto/modes/asm/ghash-sparcv9.pl
@@ -0,0 +1,581 @@
+#! /usr/bin/env perl
+# Copyright 2010-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+
+# ====================================================================
+# Written by Andy Polyakov  for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+
+# March 2010
+#
+# The module implements "4-bit" GCM GHASH function and underlying
+# single multiplication operation in GF(2^128). "4-bit" means that it
+# uses 256 bytes per-key table [+128 bytes shared table]. Performance
+# results are for streamed GHASH subroutine on UltraSPARC pre-Tx CPU
+# and are expressed in cycles per processed byte, less is better:
+#
+#		gcc 3.3.x	cc 5.2		this assembler
+#
+# 32-bit build	81.4		43.3		12.6	(+546%/+244%)
+# 64-bit build	20.2		21.2		12.6	(+60%/+68%)
+#
+# Here is data collected on UltraSPARC T1 system running Linux:
+#
+#		gcc 4.4.1			this assembler
+#
+# 32-bit build	566				50	(+1000%)
+# 64-bit build	56				50	(+12%)
+#
+# I don't quite understand why difference between 32-bit and 64-bit
+# compiler-generated code is so big. Compilers *were* instructed to
+# generate code for UltraSPARC and should have used 64-bit registers
+# for Z vector (see C code) even in 32-bit build... Oh well, it only
+# means more impressive improvement coefficients for this assembler
+# module;-) Loops are aggressively modulo-scheduled in respect to
+# references to input data and Z.hi updates to achieve 12 cycles
+# timing. To anchor to something else, sha1-sparcv9.pl spends 11.6
+# cycles to process one byte on UltraSPARC pre-Tx CPU and ~24 on T1.
+#
+# October 2012
+#
+# Add VIS3 lookup-table-free implementation using polynomial
+# multiplication xmulx[hi] and extended addition addxc[cc]
+# instructions. 4.52/7.63x improvement on T3/T4 or in absolute
+# terms 7.90/2.14 cycles per byte. On T4 multi-process benchmark
+# saturates at ~15.5x single-process result on 8-core processor,
+# or ~20.5GBps per 2.85GHz socket.
+
+$output=pop;
+open STDOUT,">$output";
+
+$frame="STACK_FRAME";
+$bias="STACK_BIAS";
+
+$Zhi="%o0";	# 64-bit values
+$Zlo="%o1";
+$Thi="%o2";
+$Tlo="%o3";
+$rem="%o4";
+$tmp="%o5";
+
+$nhi="%l0";	# small values and pointers
+$nlo="%l1";
+$xi0="%l2";
+$xi1="%l3";
+$rem_4bit="%l4";
+$remi="%l5";
+$Htblo="%l6";
+$cnt="%l7";
+
+$Xi="%i0";	# input argument block
+$Htbl="%i1";
+$inp="%i2";
+$len="%i3";
+
+$code.=<<___;
+#include "sparc_arch.h"
+
+#ifdef  __arch64__
+.register	%g2,#scratch
+.register	%g3,#scratch
+#endif
+
+.section	".text",#alloc,#execinstr
+
+.align	64
+rem_4bit:
+	.long	`0x0000<<16`,0,`0x1C20<<16`,0,`0x3840<<16`,0,`0x2460<<16`,0
+	.long	`0x7080<<16`,0,`0x6CA0<<16`,0,`0x48C0<<16`,0,`0x54E0<<16`,0
+	.long	`0xE100<<16`,0,`0xFD20<<16`,0,`0xD940<<16`,0,`0xC560<<16`,0
+	.long	`0x9180<<16`,0,`0x8DA0<<16`,0,`0xA9C0<<16`,0,`0xB5E0<<16`,0
+.type	rem_4bit,#object
+.size	rem_4bit,(.-rem_4bit)
+
+.globl	gcm_ghash_4bit
+.align	32
+gcm_ghash_4bit:
+	save	%sp,-$frame,%sp
+	ldub	[$inp+15],$nlo
+	ldub	[$Xi+15],$xi0
+	ldub	[$Xi+14],$xi1
+	add	$len,$inp,$len
+	add	$Htbl,8,$Htblo
+
+1:	call	.+8
+	add	%o7,rem_4bit-1b,$rem_4bit
+
+.Louter:
+	xor	$xi0,$nlo,$nlo
+	and	$nlo,0xf0,$nhi
+	and	$nlo,0x0f,$nlo
+	sll	$nlo,4,$nlo
+	ldx	[$Htblo+$nlo],$Zlo
+	ldx	[$Htbl+$nlo],$Zhi
+
+	ldub	[$inp+14],$nlo
+
+	ldx	[$Htblo+$nhi],$Tlo
+	and	$Zlo,0xf,$remi
+	ldx	[$Htbl+$nhi],$Thi
+	sll	$remi,3,$remi
+	ldx	[$rem_4bit+$remi],$rem
+	srlx	$Zlo,4,$Zlo
+	mov	13,$cnt
+	sllx	$Zhi,60,$tmp
+	xor	$Tlo,$Zlo,$Zlo
+	srlx	$Zhi,4,$Zhi
+	xor	$Zlo,$tmp,$Zlo
+
+	xor	$xi1,$nlo,$nlo
+	and	$Zlo,0xf,$remi
+	and	$nlo,0xf0,$nhi
+	and	$nlo,0x0f,$nlo
+	ba	.Lghash_inner
+	sll	$nlo,4,$nlo
+.align	32
+.Lghash_inner:
+	ldx	[$Htblo+$nlo],$Tlo
+	sll	$remi,3,$remi
+	xor	$Thi,$Zhi,$Zhi
+	ldx	[$Htbl+$nlo],$Thi
+	srlx	$Zlo,4,$Zlo
+	xor	$rem,$Zhi,$Zhi
+	ldx	[$rem_4bit+$remi],$rem
+	sllx	$Zhi,60,$tmp
+	xor	$Tlo,$Zlo,$Zlo
+	ldub	[$inp+$cnt],$nlo
+	srlx	$Zhi,4,$Zhi
+	xor	$Zlo,$tmp,$Zlo
+	ldub	[$Xi+$cnt],$xi1
+	xor	$Thi,$Zhi,$Zhi
+	and	$Zlo,0xf,$remi
+
+	ldx	[$Htblo+$nhi],$Tlo
+	sll	$remi,3,$remi
+	xor	$rem,$Zhi,$Zhi
+	ldx	[$Htbl+$nhi],$Thi
+	srlx	$Zlo,4,$Zlo
+	ldx	[$rem_4bit+$remi],$rem
+	sllx	$Zhi,60,$tmp
+	xor	$xi1,$nlo,$nlo
+	srlx	$Zhi,4,$Zhi
+	and	$nlo,0xf0,$nhi
+	addcc	$cnt,-1,$cnt
+	xor	$Zlo,$tmp,$Zlo
+	and	$nlo,0x0f,$nlo
+	xor	$Tlo,$Zlo,$Zlo
+	sll	$nlo,4,$nlo
+	blu	.Lghash_inner
+	and	$Zlo,0xf,$remi
+
+	ldx	[$Htblo+$nlo],$Tlo
+	sll	$remi,3,$remi
+	xor	$Thi,$Zhi,$Zhi
+	ldx	[$Htbl+$nlo],$Thi
+	srlx	$Zlo,4,$Zlo
+	xor	$rem,$Zhi,$Zhi
+	ldx	[$rem_4bit+$remi],$rem
+	sllx	$Zhi,60,$tmp
+	xor	$Tlo,$Zlo,$Zlo
+	srlx	$Zhi,4,$Zhi
+	xor	$Zlo,$tmp,$Zlo
+	xor	$Thi,$Zhi,$Zhi
+
+	add	$inp,16,$inp
+	cmp	$inp,$len
+	be,pn	SIZE_T_CC,.Ldone
+	and	$Zlo,0xf,$remi
+
+	ldx	[$Htblo+$nhi],$Tlo
+	sll	$remi,3,$remi
+	xor	$rem,$Zhi,$Zhi
+	ldx	[$Htbl+$nhi],$Thi
+	srlx	$Zlo,4,$Zlo
+	ldx	[$rem_4bit+$remi],$rem
+	sllx	$Zhi,60,$tmp
+	xor	$Tlo,$Zlo,$Zlo
+	ldub	[$inp+15],$nlo
+	srlx	$Zhi,4,$Zhi
+	xor	$Zlo,$tmp,$Zlo
+	xor	$Thi,$Zhi,$Zhi
+	stx	$Zlo,[$Xi+8]
+	xor	$rem,$Zhi,$Zhi
+	stx	$Zhi,[$Xi]
+	srl	$Zlo,8,$xi1
+	and	$Zlo,0xff,$xi0
+	ba	.Louter
+	and	$xi1,0xff,$xi1
+.align	32
+.Ldone:
+	ldx	[$Htblo+$nhi],$Tlo
+	sll	$remi,3,$remi
+	xor	$rem,$Zhi,$Zhi
+	ldx	[$Htbl+$nhi],$Thi
+	srlx	$Zlo,4,$Zlo
+	ldx	[$rem_4bit+$remi],$rem
+	sllx	$Zhi,60,$tmp
+	xor	$Tlo,$Zlo,$Zlo
+	srlx	$Zhi,4,$Zhi
+	xor	$Zlo,$tmp,$Zlo
+	xor	$Thi,$Zhi,$Zhi
+	stx	$Zlo,[$Xi+8]
+	xor	$rem,$Zhi,$Zhi
+	stx	$Zhi,[$Xi]
+
+	ret
+	restore
+.type	gcm_ghash_4bit,#function
+.size	gcm_ghash_4bit,(.-gcm_ghash_4bit)
+___
+
+undef $inp;
+undef $len;
+
+$code.=<<___;
+.globl	gcm_gmult_4bit
+.align	32
+gcm_gmult_4bit:
+	save	%sp,-$frame,%sp
+	ldub	[$Xi+15],$nlo
+	add	$Htbl,8,$Htblo
+
+1:	call	.+8
+	add	%o7,rem_4bit-1b,$rem_4bit
+
+	and	$nlo,0xf0,$nhi
+	and	$nlo,0x0f,$nlo
+	sll	$nlo,4,$nlo
+	ldx	[$Htblo+$nlo],$Zlo
+	ldx	[$Htbl+$nlo],$Zhi
+
+	ldub	[$Xi+14],$nlo
+
+	ldx	[$Htblo+$nhi],$Tlo
+	and	$Zlo,0xf,$remi
+	ldx	[$Htbl+$nhi],$Thi
+	sll	$remi,3,$remi
+	ldx	[$rem_4bit+$remi],$rem
+	srlx	$Zlo,4,$Zlo
+	mov	13,$cnt
+	sllx	$Zhi,60,$tmp
+	xor	$Tlo,$Zlo,$Zlo
+	srlx	$Zhi,4,$Zhi
+	xor	$Zlo,$tmp,$Zlo
+
+	and	$Zlo,0xf,$remi
+	and	$nlo,0xf0,$nhi
+	and	$nlo,0x0f,$nlo
+	ba	.Lgmult_inner
+	sll	$nlo,4,$nlo
+.align	32
+.Lgmult_inner:
+	ldx	[$Htblo+$nlo],$Tlo
+	sll	$remi,3,$remi
+	xor	$Thi,$Zhi,$Zhi
+	ldx	[$Htbl+$nlo],$Thi
+	srlx	$Zlo,4,$Zlo
+	xor	$rem,$Zhi,$Zhi
+	ldx	[$rem_4bit+$remi],$rem
+	sllx	$Zhi,60,$tmp
+	xor	$Tlo,$Zlo,$Zlo
+	ldub	[$Xi+$cnt],$nlo
+	srlx	$Zhi,4,$Zhi
+	xor	$Zlo,$tmp,$Zlo
+	xor	$Thi,$Zhi,$Zhi
+	and	$Zlo,0xf,$remi
+
+	ldx	[$Htblo+$nhi],$Tlo
+	sll	$remi,3,$remi
+	xor	$rem,$Zhi,$Zhi
+	ldx	[$Htbl+$nhi],$Thi
+	srlx	$Zlo,4,$Zlo
+	ldx	[$rem_4bit+$remi],$rem
+	sllx	$Zhi,60,$tmp
+	srlx	$Zhi,4,$Zhi
+	and	$nlo,0xf0,$nhi
+	addcc	$cnt,-1,$cnt
+	xor	$Zlo,$tmp,$Zlo
+	and	$nlo,0x0f,$nlo
+	xor	$Tlo,$Zlo,$Zlo
+	sll	$nlo,4,$nlo
+	blu	.Lgmult_inner
+	and	$Zlo,0xf,$remi
+
+	ldx	[$Htblo+$nlo],$Tlo
+	sll	$remi,3,$remi
+	xor	$Thi,$Zhi,$Zhi
+	ldx	[$Htbl+$nlo],$Thi
+	srlx	$Zlo,4,$Zlo
+	xor	$rem,$Zhi,$Zhi
+	ldx	[$rem_4bit+$remi],$rem
+	sllx	$Zhi,60,$tmp
+	xor	$Tlo,$Zlo,$Zlo
+	srlx	$Zhi,4,$Zhi
+	xor	$Zlo,$tmp,$Zlo
+	xor	$Thi,$Zhi,$Zhi
+	and	$Zlo,0xf,$remi
+
+	ldx	[$Htblo+$nhi],$Tlo
+	sll	$remi,3,$remi
+	xor	$rem,$Zhi,$Zhi
+	ldx	[$Htbl+$nhi],$Thi
+	srlx	$Zlo,4,$Zlo
+	ldx	[$rem_4bit+$remi],$rem
+	sllx	$Zhi,60,$tmp
+	xor	$Tlo,$Zlo,$Zlo
+	srlx	$Zhi,4,$Zhi
+	xor	$Zlo,$tmp,$Zlo
+	xor	$Thi,$Zhi,$Zhi
+	stx	$Zlo,[$Xi+8]
+	xor	$rem,$Zhi,$Zhi
+	stx	$Zhi,[$Xi]
+
+	ret
+	restore
+.type	gcm_gmult_4bit,#function
+.size	gcm_gmult_4bit,(.-gcm_gmult_4bit)
+___
+
+{{{
+# Straightforward 128x128-bit multiplication using Karatsuba algorithm
+# followed by pair of 64-bit reductions [with a shortcut in first one,
+# which allowed to break dependency between reductions and remove one
+# multiplication from critical path]. While it might be suboptimal
+# with regard to sheer number of multiplications, other methods [such
+# as aggregate reduction] would require more 64-bit registers, which
+# we don't have in 32-bit application context.
+
+($Xip,$Htable,$inp,$len)=map("%i$_",(0..3));
+
+($Hhl,$Hlo,$Hhi,$Xlo,$Xhi,$xE1,$sqr, $C0,$C1,$C2,$C3,$V)=
+	(map("%o$_",(0..5,7)),map("%g$_",(1..5)));
+
+($shl,$shr)=map("%l$_",(0..7));
+
+# For details regarding "twisted H" see ghash-x86.pl.
+$code.=<<___;
+.globl	gcm_init_vis3
+.align	32
+gcm_init_vis3:
+	save	%sp,-$frame,%sp
+
+	ldx	[%i1+0],$Hhi
+	ldx	[%i1+8],$Hlo
+	mov	0xE1,$Xhi
+	mov	1,$Xlo
+	sllx	$Xhi,57,$Xhi
+	srax	$Hhi,63,$C0		! broadcast carry
+	addcc	$Hlo,$Hlo,$Hlo		! H<<=1
+	addxc	$Hhi,$Hhi,$Hhi
+	and	$C0,$Xlo,$Xlo
+	and	$C0,$Xhi,$Xhi
+	xor	$Xlo,$Hlo,$Hlo
+	xor	$Xhi,$Hhi,$Hhi
+	stx	$Hlo,[%i0+8]		! save twisted H
+	stx	$Hhi,[%i0+0]
+
+	sethi	%hi(0xA0406080),$V
+	sethi	%hi(0x20C0E000),%l0
+	or	$V,%lo(0xA0406080),$V
+	or	%l0,%lo(0x20C0E000),%l0
+	sllx	$V,32,$V
+	or	%l0,$V,$V		! (0xE0·i)&0xff=0xA040608020C0E000
+	stx	$V,[%i0+16]
+
+	ret
+	restore
+.type	gcm_init_vis3,#function
+.size	gcm_init_vis3,.-gcm_init_vis3
+
+.globl	gcm_gmult_vis3
+.align	32
+gcm_gmult_vis3:
+	save	%sp,-$frame,%sp
+
+	ldx	[$Xip+8],$Xlo		! load Xi
+	ldx	[$Xip+0],$Xhi
+	ldx	[$Htable+8],$Hlo	! load twisted H
+	ldx	[$Htable+0],$Hhi
+
+	mov	0xE1,%l7
+	sllx	%l7,57,$xE1		! 57 is not a typo
+	ldx	[$Htable+16],$V		! (0xE0·i)&0xff=0xA040608020C0E000
+
+	xor	$Hhi,$Hlo,$Hhl		! Karatsuba pre-processing
+	xmulx	$Xlo,$Hlo,$C0
+	xor	$Xlo,$Xhi,$C2		! Karatsuba pre-processing
+	xmulx	$C2,$Hhl,$C1
+	xmulxhi	$Xlo,$Hlo,$Xlo
+	xmulxhi	$C2,$Hhl,$C2
+	xmulxhi	$Xhi,$Hhi,$C3
+	xmulx	$Xhi,$Hhi,$Xhi
+
+	sll	$C0,3,$sqr
+	srlx	$V,$sqr,$sqr		! ·0xE0 [implicit &(7<<3)]
+	xor	$C0,$sqr,$sqr
+	sllx	$sqr,57,$sqr		! ($C0·0xE1)<<1<<56 [implicit &0x7f]
+
+	xor	$C0,$C1,$C1		! Karatsuba post-processing
+	xor	$Xlo,$C2,$C2
+	 xor	$sqr,$Xlo,$Xlo		! real destination is $C1
+	xor	$C3,$C2,$C2
+	xor	$Xlo,$C1,$C1
+	xor	$Xhi,$C2,$C2
+	xor	$Xhi,$C1,$C1
+
+	xmulxhi	$C0,$xE1,$Xlo		! ·0xE1<<1<<56
+	 xor	$C0,$C2,$C2
+	xmulx	$C1,$xE1,$C0
+	 xor	$C1,$C3,$C3
+	xmulxhi	$C1,$xE1,$C1
+
+	xor	$Xlo,$C2,$C2
+	xor	$C0,$C2,$C2
+	xor	$C1,$C3,$C3
+
+	stx	$C2,[$Xip+8]		! save Xi
+	stx	$C3,[$Xip+0]
+
+	ret
+	restore
+.type	gcm_gmult_vis3,#function
+.size	gcm_gmult_vis3,.-gcm_gmult_vis3
+
+.globl	gcm_ghash_vis3
+.align	32
+gcm_ghash_vis3:
+	save	%sp,-$frame,%sp
+	nop
+	srln	$len,0,$len		! needed on v8+, "nop" on v9
+
+	ldx	[$Xip+8],$C2		! load Xi
+	ldx	[$Xip+0],$C3
+	ldx	[$Htable+8],$Hlo	! load twisted H
+	ldx	[$Htable+0],$Hhi
+
+	mov	0xE1,%l7
+	sllx	%l7,57,$xE1		! 57 is not a typo
+	ldx	[$Htable+16],$V		! (0xE0·i)&0xff=0xA040608020C0E000
+
+	and	$inp,7,$shl
+	andn	$inp,7,$inp
+	sll	$shl,3,$shl
+	prefetch [$inp+63], 20
+	sub	%g0,$shl,$shr
+
+	xor	$Hhi,$Hlo,$Hhl		! Karatsuba pre-processing
+.Loop:
+	ldx	[$inp+8],$Xlo
+	brz,pt	$shl,1f
+	ldx	[$inp+0],$Xhi
+
+	ldx	[$inp+16],$C1		! align data
+	srlx	$Xlo,$shr,$C0
+	sllx	$Xlo,$shl,$Xlo
+	sllx	$Xhi,$shl,$Xhi
+	srlx	$C1,$shr,$C1
+	or	$C0,$Xhi,$Xhi
+	or	$C1,$Xlo,$Xlo
+1:
+	add	$inp,16,$inp
+	sub	$len,16,$len
+	xor	$C2,$Xlo,$Xlo
+	xor	$C3,$Xhi,$Xhi
+	prefetch [$inp+63], 20
+
+	xmulx	$Xlo,$Hlo,$C0
+	xor	$Xlo,$Xhi,$C2		! Karatsuba pre-processing
+	xmulx	$C2,$Hhl,$C1
+	xmulxhi	$Xlo,$Hlo,$Xlo
+	xmulxhi	$C2,$Hhl,$C2
+	xmulxhi	$Xhi,$Hhi,$C3
+	xmulx	$Xhi,$Hhi,$Xhi
+
+	sll	$C0,3,$sqr
+	srlx	$V,$sqr,$sqr		! ·0xE0 [implicit &(7<<3)]
+	xor	$C0,$sqr,$sqr
+	sllx	$sqr,57,$sqr		! ($C0·0xE1)<<1<<56 [implicit &0x7f]
+
+	xor	$C0,$C1,$C1		! Karatsuba post-processing
+	xor	$Xlo,$C2,$C2
+	 xor	$sqr,$Xlo,$Xlo		! real destination is $C1
+	xor	$C3,$C2,$C2
+	xor	$Xlo,$C1,$C1
+	xor	$Xhi,$C2,$C2
+	xor	$Xhi,$C1,$C1
+
+	xmulxhi	$C0,$xE1,$Xlo		! ·0xE1<<1<<56
+	 xor	$C0,$C2,$C2
+	xmulx	$C1,$xE1,$C0
+	 xor	$C1,$C3,$C3
+	xmulxhi	$C1,$xE1,$C1
+
+	xor	$Xlo,$C2,$C2
+	xor	$C0,$C2,$C2
+	brnz,pt	$len,.Loop
+	xor	$C1,$C3,$C3
+
+	stx	$C2,[$Xip+8]		! save Xi
+	stx	$C3,[$Xip+0]
+
+	ret
+	restore
+.type	gcm_ghash_vis3,#function
+.size	gcm_ghash_vis3,.-gcm_ghash_vis3
+___
+}}}
+$code.=<<___;
+.asciz	"GHASH for SPARCv9/VIS3, CRYPTOGAMS by "
+.align	4
+___
+
+
+# Purpose of these subroutines is to explicitly encode VIS instructions,
+# so that one can compile the module without having to specify VIS
+# extensions on compiler command line, e.g. -xarch=v9 vs. -xarch=v9a.
+# Idea is to reserve for option to produce "universal" binary and let
+# programmer detect if current CPU is VIS capable at run-time.
+sub unvis3 {
+my ($mnemonic,$rs1,$rs2,$rd)=@_;
+my %bias = ( "g" => 0, "o" => 8, "l" => 16, "i" => 24 );
+my ($ref,$opf);
+my %visopf = (	"addxc"		=> 0x011,
+		"addxccc"	=> 0x013,
+		"xmulx"		=> 0x115,
+		"xmulxhi"	=> 0x116	);
+
+    $ref = "$mnemonic\t$rs1,$rs2,$rd";
+
+    if ($opf=$visopf{$mnemonic}) {
+	foreach ($rs1,$rs2,$rd) {
+	    return $ref if (!/%([goli])([0-9])/);
+	    $_=$bias{$1}+$2;
+	}
+
+	return	sprintf ".word\t0x%08x !%s",
+			0x81b00000|$rd<<25|$rs1<<14|$opf<<5|$rs2,
+			$ref;
+    } else {
+	return $ref;
+    }
+}
+
+foreach (split("\n",$code)) {
+	s/\`([^\`]*)\`/eval $1/ge;
+
+	s/\b(xmulx[hi]*|addxc[c]{0,2})\s+(%[goli][0-7]),\s*(%[goli][0-7]),\s*(%[goli][0-7])/
+		&unvis3($1,$2,$3,$4)
+	 /ge;
+
+	print $_,"\n";
+}
+
+close STDOUT;
diff --git a/crypto/modes/asm/ghash-x86.pl b/crypto/modes/asm/ghash-x86.pl
new file mode 100644
index 000000000..cd8458256
--- /dev/null
+++ b/crypto/modes/asm/ghash-x86.pl
@@ -0,0 +1,1405 @@
+#! /usr/bin/env perl
+# Copyright 2010-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+#
+# ====================================================================
+# Written by Andy Polyakov  for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+#
+# March, May, June 2010
+#
+# The module implements "4-bit" GCM GHASH function and underlying
+# single multiplication operation in GF(2^128). "4-bit" means that it
+# uses 256 bytes per-key table [+64/128 bytes fixed table]. It has two
+# code paths: vanilla x86 and vanilla SSE. Former will be executed on
+# 486 and Pentium, latter on all others. SSE GHASH features so called
+# "528B" variant of "4-bit" method utilizing additional 256+16 bytes
+# of per-key storage [+512 bytes shared table]. Performance results
+# are for streamed GHASH subroutine and are expressed in cycles per
+# processed byte, less is better:
+#
+#		gcc 2.95.3(*)	SSE assembler	x86 assembler
+#
+# Pentium	105/111(**)	-		50
+# PIII		68 /75		12.2		24
+# P4		125/125		17.8		84(***)
+# Opteron	66 /70		10.1		30
+# Core2		54 /67		8.4		18
+# Atom		105/105		16.8		53
+# VIA Nano	69 /71		13.0		27
+#
+# (*)	gcc 3.4.x was observed to generate few percent slower code,
+#	which is one of reasons why 2.95.3 results were chosen,
+#	another reason is lack of 3.4.x results for older CPUs;
+#	comparison with SSE results is not completely fair, because C
+#	results are for vanilla "256B" implementation, while
+#	assembler results are for "528B";-)
+# (**)	second number is result for code compiled with -fPIC flag,
+#	which is actually more relevant, because assembler code is
+#	position-independent;
+# (***)	see comment in non-MMX routine for further details;
+#
+# To summarize, it's >2-5 times faster than gcc-generated code. To
+# anchor it to something else SHA1 assembler processes one byte in
+# ~7 cycles on contemporary x86 cores. As for choice of MMX/SSE
+# in particular, see comment at the end of the file...
+
+# May 2010
+#
+# Add PCLMULQDQ version performing at 2.10 cycles per processed byte.
+# The question is how close is it to theoretical limit? The pclmulqdq
+# instruction latency appears to be 14 cycles and there can't be more
+# than 2 of them executing at any given time. This means that single
+# Karatsuba multiplication would take 28 cycles *plus* few cycles for
+# pre- and post-processing. Then multiplication has to be followed by
+# modulo-reduction. Given that aggregated reduction method [see
+# "Carry-less Multiplication and Its Usage for Computing the GCM Mode"
+# white paper by Intel] allows you to perform reduction only once in
+# a while we can assume that asymptotic performance can be estimated
+# as (28+Tmod/Naggr)/16, where Tmod is time to perform reduction
+# and Naggr is the aggregation factor.
+#
+# Before we proceed to this implementation let's have closer look at
+# the best-performing code suggested by Intel in their white paper.
+# By tracing inter-register dependencies Tmod is estimated as ~19
+# cycles and Naggr chosen by Intel is 4, resulting in 2.05 cycles per
+# processed byte. As implied, this is quite optimistic estimate,
+# because it does not account for Karatsuba pre- and post-processing,
+# which for a single multiplication is ~5 cycles. Unfortunately Intel
+# does not provide performance data for GHASH alone. But benchmarking
+# AES_GCM_encrypt ripped out of Fig. 15 of the white paper with aadt
+# alone resulted in 2.46 cycles per byte of out 16KB buffer. Note that
+# the result accounts even for pre-computing of degrees of the hash
+# key H, but its portion is negligible at 16KB buffer size.
+#
+# Moving on to the implementation in question. Tmod is estimated as
+# ~13 cycles and Naggr is 2, giving asymptotic performance of ...
+# 2.16. How is it possible that measured performance is better than
+# optimistic theoretical estimate? There is one thing Intel failed
+# to recognize. By serializing GHASH with CTR in same subroutine
+# former's performance is really limited to above (Tmul + Tmod/Naggr)
+# equation. But if GHASH procedure is detached, the modulo-reduction
+# can be interleaved with Naggr-1 multiplications at instruction level
+# and under ideal conditions even disappear from the equation. So that
+# optimistic theoretical estimate for this implementation is ...
+# 28/16=1.75, and not 2.16. Well, it's probably way too optimistic,
+# at least for such small Naggr. I'd argue that (28+Tproc/Naggr),
+# where Tproc is time required for Karatsuba pre- and post-processing,
+# is more realistic estimate. In this case it gives ... 1.91 cycles.
+# Or in other words, depending on how well we can interleave reduction
+# and one of the two multiplications the performance should be between
+# 1.91 and 2.16. As already mentioned, this implementation processes
+# one byte out of 8KB buffer in 2.10 cycles, while x86_64 counterpart
+# - in 2.02. x86_64 performance is better, because larger register
+# bank allows to interleave reduction and multiplication better.
+#
+# Does it make sense to increase Naggr? To start with it's virtually
+# impossible in 32-bit mode, because of limited register bank
+# capacity. Otherwise improvement has to be weighed agiainst slower
+# setup, as well as code size and complexity increase. As even
+# optimistic estimate doesn't promise 30% performance improvement,
+# there are currently no plans to increase Naggr.
+#
+# Special thanks to David Woodhouse  for
+# providing access to a Westmere-based system on behalf of Intel
+# Open Source Technology Centre.
+
+# January 2010
+#
+# Tweaked to optimize transitions between integer and FP operations
+# on same XMM register, PCLMULQDQ subroutine was measured to process
+# one byte in 2.07 cycles on Sandy Bridge, and in 2.12 - on Westmere.
+# The minor regression on Westmere is outweighed by ~15% improvement
+# on Sandy Bridge. Strangely enough attempt to modify 64-bit code in
+# similar manner resulted in almost 20% degradation on Sandy Bridge,
+# where original 64-bit code processes one byte in 1.95 cycles.
+
+#####################################################################
+# For reference, AMD Bulldozer processes one byte in 1.98 cycles in
+# 32-bit mode and 1.89 in 64-bit.
+
+# February 2013
+#
+# Overhaul: aggregate Karatsuba post-processing, improve ILP in
+# reduction_alg9. Resulting performance is 1.96 cycles per byte on
+# Westmere, 1.95 - on Sandy/Ivy Bridge, 1.76 - on Bulldozer.
+
+$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+push(@INC,"${dir}","${dir}../../perlasm");
+require "x86asm.pl";
+
+$output=pop;
+open STDOUT,">$output";
+
+&asm_init($ARGV[0],"ghash-x86.pl",$x86only = $ARGV[$#ARGV] eq "386");
+
+$sse2=0;
+for (@ARGV) { $sse2=1 if (/-DOPENSSL_IA32_SSE2/); }
+
+($Zhh,$Zhl,$Zlh,$Zll) = ("ebp","edx","ecx","ebx");
+$inp  = "edi";
+$Htbl = "esi";
+
+$unroll = 0;	# Affects x86 loop. Folded loop performs ~7% worse
+		# than unrolled, which has to be weighted against
+		# 2.5x x86-specific code size reduction.
+
+sub x86_loop {
+    my $off = shift;
+    my $rem = "eax";
+
+	&mov	($Zhh,&DWP(4,$Htbl,$Zll));
+	&mov	($Zhl,&DWP(0,$Htbl,$Zll));
+	&mov	($Zlh,&DWP(12,$Htbl,$Zll));
+	&mov	($Zll,&DWP(8,$Htbl,$Zll));
+	&xor	($rem,$rem);	# avoid partial register stalls on PIII
+
+	# shrd practically kills P4, 2.5x deterioration, but P4 has
+	# MMX code-path to execute. shrd runs tad faster [than twice
+	# the shifts, move's and or's] on pre-MMX Pentium (as well as
+	# PIII and Core2), *but* minimizes code size, spares register
+	# and thus allows to fold the loop...
+	if (!$unroll) {
+	my $cnt = $inp;
+	&mov	($cnt,15);
+	&jmp	(&label("x86_loop"));
+	&set_label("x86_loop",16);
+	    for($i=1;$i<=2;$i++) {
+		&mov	(&LB($rem),&LB($Zll));
+		&shrd	($Zll,$Zlh,4);
+		&and	(&LB($rem),0xf);
+		&shrd	($Zlh,$Zhl,4);
+		&shrd	($Zhl,$Zhh,4);
+		&shr	($Zhh,4);
+		&xor	($Zhh,&DWP($off+16,"esp",$rem,4));
+
+		&mov	(&LB($rem),&BP($off,"esp",$cnt));
+		if ($i&1) {
+			&and	(&LB($rem),0xf0);
+		} else {
+			&shl	(&LB($rem),4);
+		}
+
+		&xor	($Zll,&DWP(8,$Htbl,$rem));
+		&xor	($Zlh,&DWP(12,$Htbl,$rem));
+		&xor	($Zhl,&DWP(0,$Htbl,$rem));
+		&xor	($Zhh,&DWP(4,$Htbl,$rem));
+
+		if ($i&1) {
+			&dec	($cnt);
+			&js	(&label("x86_break"));
+		} else {
+			&jmp	(&label("x86_loop"));
+		}
+	    }
+	&set_label("x86_break",16);
+	} else {
+	    for($i=1;$i<32;$i++) {
+		&comment($i);
+		&mov	(&LB($rem),&LB($Zll));
+		&shrd	($Zll,$Zlh,4);
+		&and	(&LB($rem),0xf);
+		&shrd	($Zlh,$Zhl,4);
+		&shrd	($Zhl,$Zhh,4);
+		&shr	($Zhh,4);
+		&xor	($Zhh,&DWP($off+16,"esp",$rem,4));
+
+		if ($i&1) {
+			&mov	(&LB($rem),&BP($off+15-($i>>1),"esp"));
+			&and	(&LB($rem),0xf0);
+		} else {
+			&mov	(&LB($rem),&BP($off+15-($i>>1),"esp"));
+			&shl	(&LB($rem),4);
+		}
+
+		&xor	($Zll,&DWP(8,$Htbl,$rem));
+		&xor	($Zlh,&DWP(12,$Htbl,$rem));
+		&xor	($Zhl,&DWP(0,$Htbl,$rem));
+		&xor	($Zhh,&DWP(4,$Htbl,$rem));
+	    }
+	}
+	&bswap	($Zll);
+	&bswap	($Zlh);
+	&bswap	($Zhl);
+	if (!$x86only) {
+		&bswap	($Zhh);
+	} else {
+		&mov	("eax",$Zhh);
+		&bswap	("eax");
+		&mov	($Zhh,"eax");
+	}
+}
+
+if ($unroll) {
+    &function_begin_B("_x86_gmult_4bit_inner");
+	&x86_loop(4);
+	&ret	();
+    &function_end_B("_x86_gmult_4bit_inner");
+}
+
+sub deposit_rem_4bit {
+    my $bias = shift;
+
+	&mov	(&DWP($bias+0, "esp"),0x0000<<16);
+	&mov	(&DWP($bias+4, "esp"),0x1C20<<16);
+	&mov	(&DWP($bias+8, "esp"),0x3840<<16);
+	&mov	(&DWP($bias+12,"esp"),0x2460<<16);
+	&mov	(&DWP($bias+16,"esp"),0x7080<<16);
+	&mov	(&DWP($bias+20,"esp"),0x6CA0<<16);
+	&mov	(&DWP($bias+24,"esp"),0x48C0<<16);
+	&mov	(&DWP($bias+28,"esp"),0x54E0<<16);
+	&mov	(&DWP($bias+32,"esp"),0xE100<<16);
+	&mov	(&DWP($bias+36,"esp"),0xFD20<<16);
+	&mov	(&DWP($bias+40,"esp"),0xD940<<16);
+	&mov	(&DWP($bias+44,"esp"),0xC560<<16);
+	&mov	(&DWP($bias+48,"esp"),0x9180<<16);
+	&mov	(&DWP($bias+52,"esp"),0x8DA0<<16);
+	&mov	(&DWP($bias+56,"esp"),0xA9C0<<16);
+	&mov	(&DWP($bias+60,"esp"),0xB5E0<<16);
+}
+
+$suffix = $x86only ? "" : "_x86";
+
+&function_begin("gcm_gmult_4bit".$suffix);
+	&stack_push(16+4+1);			# +1 for stack alignment
+	&mov	($inp,&wparam(0));		# load Xi
+	&mov	($Htbl,&wparam(1));		# load Htable
+
+	&mov	($Zhh,&DWP(0,$inp));		# load Xi[16]
+	&mov	($Zhl,&DWP(4,$inp));
+	&mov	($Zlh,&DWP(8,$inp));
+	&mov	($Zll,&DWP(12,$inp));
+
+	&deposit_rem_4bit(16);
+
+	&mov	(&DWP(0,"esp"),$Zhh);		# copy Xi[16] on stack
+	&mov	(&DWP(4,"esp"),$Zhl);
+	&mov	(&DWP(8,"esp"),$Zlh);
+	&mov	(&DWP(12,"esp"),$Zll);
+	&shr	($Zll,20);
+	&and	($Zll,0xf0);
+
+	if ($unroll) {
+		&call	("_x86_gmult_4bit_inner");
+	} else {
+		&x86_loop(0);
+		&mov	($inp,&wparam(0));
+	}
+
+	&mov	(&DWP(12,$inp),$Zll);
+	&mov	(&DWP(8,$inp),$Zlh);
+	&mov	(&DWP(4,$inp),$Zhl);
+	&mov	(&DWP(0,$inp),$Zhh);
+	&stack_pop(16+4+1);
+&function_end("gcm_gmult_4bit".$suffix);
+
+&function_begin("gcm_ghash_4bit".$suffix);
+	&stack_push(16+4+1);			# +1 for 64-bit alignment
+	&mov	($Zll,&wparam(0));		# load Xi
+	&mov	($Htbl,&wparam(1));		# load Htable
+	&mov	($inp,&wparam(2));		# load in
+	&mov	("ecx",&wparam(3));		# load len
+	&add	("ecx",$inp);
+	&mov	(&wparam(3),"ecx");
+
+	&mov	($Zhh,&DWP(0,$Zll));		# load Xi[16]
+	&mov	($Zhl,&DWP(4,$Zll));
+	&mov	($Zlh,&DWP(8,$Zll));
+	&mov	($Zll,&DWP(12,$Zll));
+
+	&deposit_rem_4bit(16);
+
+    &set_label("x86_outer_loop",16);
+	&xor	($Zll,&DWP(12,$inp));		# xor with input
+	&xor	($Zlh,&DWP(8,$inp));
+	&xor	($Zhl,&DWP(4,$inp));
+	&xor	($Zhh,&DWP(0,$inp));
+	&mov	(&DWP(12,"esp"),$Zll);		# dump it on stack
+	&mov	(&DWP(8,"esp"),$Zlh);
+	&mov	(&DWP(4,"esp"),$Zhl);
+	&mov	(&DWP(0,"esp"),$Zhh);
+
+	&shr	($Zll,20);
+	&and	($Zll,0xf0);
+
+	if ($unroll) {
+		&call	("_x86_gmult_4bit_inner");
+	} else {
+		&x86_loop(0);
+		&mov	($inp,&wparam(2));
+	}
+	&lea	($inp,&DWP(16,$inp));
+	&cmp	($inp,&wparam(3));
+	&mov	(&wparam(2),$inp)	if (!$unroll);
+	&jb	(&label("x86_outer_loop"));
+
+	&mov	($inp,&wparam(0));	# load Xi
+	&mov	(&DWP(12,$inp),$Zll);
+	&mov	(&DWP(8,$inp),$Zlh);
+	&mov	(&DWP(4,$inp),$Zhl);
+	&mov	(&DWP(0,$inp),$Zhh);
+	&stack_pop(16+4+1);
+&function_end("gcm_ghash_4bit".$suffix);
+
+if (!$x86only) {{{
+
+&static_label("rem_4bit");
+
+if (!$sse2) {{	# pure-MMX "May" version...
+
+$S=12;		# shift factor for rem_4bit
+
+&function_begin_B("_mmx_gmult_4bit_inner");
+# MMX version performs 3.5 times better on P4 (see comment in non-MMX
+# routine for further details), 100% better on Opteron, ~70% better
+# on Core2 and PIII... In other words effort is considered to be well
+# spent... Since initial release the loop was unrolled in order to
+# "liberate" register previously used as loop counter. Instead it's
+# used to optimize critical path in 'Z.hi ^= rem_4bit[Z.lo&0xf]'.
+# The path involves move of Z.lo from MMX to integer register,
+# effective address calculation and finally merge of value to Z.hi.
+# Reference to rem_4bit is scheduled so late that I had to >>4
+# rem_4bit elements. This resulted in 20-45% procent improvement
+# on contemporary µ-archs.
+{
+    my $cnt;
+    my $rem_4bit = "eax";
+    my @rem = ($Zhh,$Zll);
+    my $nhi = $Zhl;
+    my $nlo = $Zlh;
+
+    my ($Zlo,$Zhi) = ("mm0","mm1");
+    my $tmp = "mm2";
+
+	&xor	($nlo,$nlo);	# avoid partial register stalls on PIII
+	&mov	($nhi,$Zll);
+	&mov	(&LB($nlo),&LB($nhi));
+	&shl	(&LB($nlo),4);
+	&and	($nhi,0xf0);
+	&movq	($Zlo,&QWP(8,$Htbl,$nlo));
+	&movq	($Zhi,&QWP(0,$Htbl,$nlo));
+	&movd	($rem[0],$Zlo);
+
+	for ($cnt=28;$cnt>=-2;$cnt--) {
+	    my $odd = $cnt&1;
+	    my $nix = $odd ? $nlo : $nhi;
+
+		&shl	(&LB($nlo),4)			if ($odd);
+		&psrlq	($Zlo,4);
+		&movq	($tmp,$Zhi);
+		&psrlq	($Zhi,4);
+		&pxor	($Zlo,&QWP(8,$Htbl,$nix));
+		&mov	(&LB($nlo),&BP($cnt/2,$inp))	if (!$odd && $cnt>=0);
+		&psllq	($tmp,60);
+		&and	($nhi,0xf0)			if ($odd);
+		&pxor	($Zhi,&QWP(0,$rem_4bit,$rem[1],8)) if ($cnt<28);
+		&and	($rem[0],0xf);
+		&pxor	($Zhi,&QWP(0,$Htbl,$nix));
+		&mov	($nhi,$nlo)			if (!$odd && $cnt>=0);
+		&movd	($rem[1],$Zlo);
+		&pxor	($Zlo,$tmp);
+
+		push	(@rem,shift(@rem));		# "rotate" registers
+	}
+
+	&mov	($inp,&DWP(4,$rem_4bit,$rem[1],8));	# last rem_4bit[rem]
+
+	&psrlq	($Zlo,32);	# lower part of Zlo is already there
+	&movd	($Zhl,$Zhi);
+	&psrlq	($Zhi,32);
+	&movd	($Zlh,$Zlo);
+	&movd	($Zhh,$Zhi);
+	&shl	($inp,4);	# compensate for rem_4bit[i] being >>4
+
+	&bswap	($Zll);
+	&bswap	($Zhl);
+	&bswap	($Zlh);
+	&xor	($Zhh,$inp);
+	&bswap	($Zhh);
+
+	&ret	();
+}
+&function_end_B("_mmx_gmult_4bit_inner");
+
+&function_begin("gcm_gmult_4bit_mmx");
+	&mov	($inp,&wparam(0));	# load Xi
+	&mov	($Htbl,&wparam(1));	# load Htable
+
+	&call	(&label("pic_point"));
+	&set_label("pic_point");
+	&blindpop("eax");
+	&lea	("eax",&DWP(&label("rem_4bit")."-".&label("pic_point"),"eax"));
+
+	&movz	($Zll,&BP(15,$inp));
+
+	&call	("_mmx_gmult_4bit_inner");
+
+	&mov	($inp,&wparam(0));	# load Xi
+	&emms	();
+	&mov	(&DWP(12,$inp),$Zll);
+	&mov	(&DWP(4,$inp),$Zhl);
+	&mov	(&DWP(8,$inp),$Zlh);
+	&mov	(&DWP(0,$inp),$Zhh);
+&function_end("gcm_gmult_4bit_mmx");
+
+# Streamed version performs 20% better on P4, 7% on Opteron,
+# 10% on Core2 and PIII...
+&function_begin("gcm_ghash_4bit_mmx");
+	&mov	($Zhh,&wparam(0));	# load Xi
+	&mov	($Htbl,&wparam(1));	# load Htable
+	&mov	($inp,&wparam(2));	# load in
+	&mov	($Zlh,&wparam(3));	# load len
+
+	&call	(&label("pic_point"));
+	&set_label("pic_point");
+	&blindpop("eax");
+	&lea	("eax",&DWP(&label("rem_4bit")."-".&label("pic_point"),"eax"));
+
+	&add	($Zlh,$inp);
+	&mov	(&wparam(3),$Zlh);	# len to point at the end of input
+	&stack_push(4+1);		# +1 for stack alignment
+
+	&mov	($Zll,&DWP(12,$Zhh));	# load Xi[16]
+	&mov	($Zhl,&DWP(4,$Zhh));
+	&mov	($Zlh,&DWP(8,$Zhh));
+	&mov	($Zhh,&DWP(0,$Zhh));
+	&jmp	(&label("mmx_outer_loop"));
+
+    &set_label("mmx_outer_loop",16);
+	&xor	($Zll,&DWP(12,$inp));
+	&xor	($Zhl,&DWP(4,$inp));
+	&xor	($Zlh,&DWP(8,$inp));
+	&xor	($Zhh,&DWP(0,$inp));
+	&mov	(&wparam(2),$inp);
+	&mov	(&DWP(12,"esp"),$Zll);
+	&mov	(&DWP(4,"esp"),$Zhl);
+	&mov	(&DWP(8,"esp"),$Zlh);
+	&mov	(&DWP(0,"esp"),$Zhh);
+
+	&mov	($inp,"esp");
+	&shr	($Zll,24);
+
+	&call	("_mmx_gmult_4bit_inner");
+
+	&mov	($inp,&wparam(2));
+	&lea	($inp,&DWP(16,$inp));
+	&cmp	($inp,&wparam(3));
+	&jb	(&label("mmx_outer_loop"));
+
+	&mov	($inp,&wparam(0));	# load Xi
+	&emms	();
+	&mov	(&DWP(12,$inp),$Zll);
+	&mov	(&DWP(4,$inp),$Zhl);
+	&mov	(&DWP(8,$inp),$Zlh);
+	&mov	(&DWP(0,$inp),$Zhh);
+
+	&stack_pop(4+1);
+&function_end("gcm_ghash_4bit_mmx");
+
+}} else {{	# "June" MMX version...
+		# ... has slower "April" gcm_gmult_4bit_mmx with folded
+		# loop. This is done to conserve code size...
+$S=16;		# shift factor for rem_4bit
+
+sub mmx_loop() {
+# MMX version performs 2.8 times better on P4 (see comment in non-MMX
+# routine for further details), 40% better on Opteron and Core2, 50%
+# better on PIII... In other words effort is considered to be well
+# spent...
+    my $inp = shift;
+    my $rem_4bit = shift;
+    my $cnt = $Zhh;
+    my $nhi = $Zhl;
+    my $nlo = $Zlh;
+    my $rem = $Zll;
+
+    my ($Zlo,$Zhi) = ("mm0","mm1");
+    my $tmp = "mm2";
+
+	&xor	($nlo,$nlo);	# avoid partial register stalls on PIII
+	&mov	($nhi,$Zll);
+	&mov	(&LB($nlo),&LB($nhi));
+	&mov	($cnt,14);
+	&shl	(&LB($nlo),4);
+	&and	($nhi,0xf0);
+	&movq	($Zlo,&QWP(8,$Htbl,$nlo));
+	&movq	($Zhi,&QWP(0,$Htbl,$nlo));
+	&movd	($rem,$Zlo);
+	&jmp	(&label("mmx_loop"));
+
+    &set_label("mmx_loop",16);
+	&psrlq	($Zlo,4);
+	&and	($rem,0xf);
+	&movq	($tmp,$Zhi);
+	&psrlq	($Zhi,4);
+	&pxor	($Zlo,&QWP(8,$Htbl,$nhi));
+	&mov	(&LB($nlo),&BP(0,$inp,$cnt));
+	&psllq	($tmp,60);
+	&pxor	($Zhi,&QWP(0,$rem_4bit,$rem,8));
+	&dec	($cnt);
+	&movd	($rem,$Zlo);
+	&pxor	($Zhi,&QWP(0,$Htbl,$nhi));
+	&mov	($nhi,$nlo);
+	&pxor	($Zlo,$tmp);
+	&js	(&label("mmx_break"));
+
+	&shl	(&LB($nlo),4);
+	&and	($rem,0xf);
+	&psrlq	($Zlo,4);
+	&and	($nhi,0xf0);
+	&movq	($tmp,$Zhi);
+	&psrlq	($Zhi,4);
+	&pxor	($Zlo,&QWP(8,$Htbl,$nlo));
+	&psllq	($tmp,60);
+	&pxor	($Zhi,&QWP(0,$rem_4bit,$rem,8));
+	&movd	($rem,$Zlo);
+	&pxor	($Zhi,&QWP(0,$Htbl,$nlo));
+	&pxor	($Zlo,$tmp);
+	&jmp	(&label("mmx_loop"));
+
+    &set_label("mmx_break",16);
+	&shl	(&LB($nlo),4);
+	&and	($rem,0xf);
+	&psrlq	($Zlo,4);
+	&and	($nhi,0xf0);
+	&movq	($tmp,$Zhi);
+	&psrlq	($Zhi,4);
+	&pxor	($Zlo,&QWP(8,$Htbl,$nlo));
+	&psllq	($tmp,60);
+	&pxor	($Zhi,&QWP(0,$rem_4bit,$rem,8));
+	&movd	($rem,$Zlo);
+	&pxor	($Zhi,&QWP(0,$Htbl,$nlo));
+	&pxor	($Zlo,$tmp);
+
+	&psrlq	($Zlo,4);
+	&and	($rem,0xf);
+	&movq	($tmp,$Zhi);
+	&psrlq	($Zhi,4);
+	&pxor	($Zlo,&QWP(8,$Htbl,$nhi));
+	&psllq	($tmp,60);
+	&pxor	($Zhi,&QWP(0,$rem_4bit,$rem,8));
+	&movd	($rem,$Zlo);
+	&pxor	($Zhi,&QWP(0,$Htbl,$nhi));
+	&pxor	($Zlo,$tmp);
+
+	&psrlq	($Zlo,32);	# lower part of Zlo is already there
+	&movd	($Zhl,$Zhi);
+	&psrlq	($Zhi,32);
+	&movd	($Zlh,$Zlo);
+	&movd	($Zhh,$Zhi);
+
+	&bswap	($Zll);
+	&bswap	($Zhl);
+	&bswap	($Zlh);
+	&bswap	($Zhh);
+}
+
+&function_begin("gcm_gmult_4bit_mmx");
+	&mov	($inp,&wparam(0));	# load Xi
+	&mov	($Htbl,&wparam(1));	# load Htable
+
+	&call	(&label("pic_point"));
+	&set_label("pic_point");
+	&blindpop("eax");
+	&lea	("eax",&DWP(&label("rem_4bit")."-".&label("pic_point"),"eax"));
+
+	&movz	($Zll,&BP(15,$inp));
+
+	&mmx_loop($inp,"eax");
+
+	&emms	();
+	&mov	(&DWP(12,$inp),$Zll);
+	&mov	(&DWP(4,$inp),$Zhl);
+	&mov	(&DWP(8,$inp),$Zlh);
+	&mov	(&DWP(0,$inp),$Zhh);
+&function_end("gcm_gmult_4bit_mmx");
+
+######################################################################
+# Below subroutine is "528B" variant of "4-bit" GCM GHASH function
+# (see gcm128.c for details). It provides further 20-40% performance
+# improvement over above mentioned "May" version.
+
+&static_label("rem_8bit");
+
+&function_begin("gcm_ghash_4bit_mmx");
+{ my ($Zlo,$Zhi) = ("mm7","mm6");
+  my $rem_8bit = "esi";
+  my $Htbl = "ebx";
+
+    # parameter block
+    &mov	("eax",&wparam(0));		# Xi
+    &mov	("ebx",&wparam(1));		# Htable
+    &mov	("ecx",&wparam(2));		# inp
+    &mov	("edx",&wparam(3));		# len
+    &mov	("ebp","esp");			# original %esp
+    &call	(&label("pic_point"));
+    &set_label	("pic_point");
+    &blindpop	($rem_8bit);
+    &lea	($rem_8bit,&DWP(&label("rem_8bit")."-".&label("pic_point"),$rem_8bit));
+
+    &sub	("esp",512+16+16);		# allocate stack frame...
+    &and	("esp",-64);			# ...and align it
+    &sub	("esp",16);			# place for (u8)(H[]<<4)
+
+    &add	("edx","ecx");			# pointer to the end of input
+    &mov	(&DWP(528+16+0,"esp"),"eax");	# save Xi
+    &mov	(&DWP(528+16+8,"esp"),"edx");	# save inp+len
+    &mov	(&DWP(528+16+12,"esp"),"ebp");	# save original %esp
+
+    { my @lo  = ("mm0","mm1","mm2");
+      my @hi  = ("mm3","mm4","mm5");
+      my @tmp = ("mm6","mm7");
+      my ($off1,$off2,$i) = (0,0,);
+
+      &add	($Htbl,128);			# optimize for size
+      &lea	("edi",&DWP(16+128,"esp"));
+      &lea	("ebp",&DWP(16+256+128,"esp"));
+
+      # decompose Htable (low and high parts are kept separately),
+      # generate Htable[]>>4, (u8)(Htable[]<<4), save to stack...
+      for ($i=0;$i<18;$i++) {
+
+	&mov	("edx",&DWP(16*$i+8-128,$Htbl))		if ($i<16);
+	&movq	($lo[0],&QWP(16*$i+8-128,$Htbl))	if ($i<16);
+	&psllq	($tmp[1],60)				if ($i>1);
+	&movq	($hi[0],&QWP(16*$i+0-128,$Htbl))	if ($i<16);
+	&por	($lo[2],$tmp[1])			if ($i>1);
+	&movq	(&QWP($off1-128,"edi"),$lo[1])		if ($i>0 && $i<17);
+	&psrlq	($lo[1],4)				if ($i>0 && $i<17);
+	&movq	(&QWP($off1,"edi"),$hi[1])		if ($i>0 && $i<17);
+	&movq	($tmp[0],$hi[1])			if ($i>0 && $i<17);
+	&movq	(&QWP($off2-128,"ebp"),$lo[2])		if ($i>1);
+	&psrlq	($hi[1],4)				if ($i>0 && $i<17);
+	&movq	(&QWP($off2,"ebp"),$hi[2])		if ($i>1);
+	&shl	("edx",4)				if ($i<16);
+	&mov	(&BP($i,"esp"),&LB("edx"))		if ($i<16);
+
+	unshift	(@lo,pop(@lo));			# "rotate" registers
+	unshift	(@hi,pop(@hi));
+	unshift	(@tmp,pop(@tmp));
+	$off1 += 8	if ($i>0);
+	$off2 += 8	if ($i>1);
+      }
+    }
+
+    &movq	($Zhi,&QWP(0,"eax"));
+    &mov	("ebx",&DWP(8,"eax"));
+    &mov	("edx",&DWP(12,"eax"));		# load Xi
+
+&set_label("outer",16);
+  { my $nlo = "eax";
+    my $dat = "edx";
+    my @nhi = ("edi","ebp");
+    my @rem = ("ebx","ecx");
+    my @red = ("mm0","mm1","mm2");
+    my $tmp = "mm3";
+
+    &xor	($dat,&DWP(12,"ecx"));		# merge input data
+    &xor	("ebx",&DWP(8,"ecx"));
+    &pxor	($Zhi,&QWP(0,"ecx"));
+    &lea	("ecx",&DWP(16,"ecx"));		# inp+=16
+    #&mov	(&DWP(528+12,"esp"),$dat);	# save inp^Xi
+    &mov	(&DWP(528+8,"esp"),"ebx");
+    &movq	(&QWP(528+0,"esp"),$Zhi);
+    &mov	(&DWP(528+16+4,"esp"),"ecx");	# save inp
+
+    &xor	($nlo,$nlo);
+    &rol	($dat,8);
+    &mov	(&LB($nlo),&LB($dat));
+    &mov	($nhi[1],$nlo);
+    &and	(&LB($nlo),0x0f);
+    &shr	($nhi[1],4);
+    &pxor	($red[0],$red[0]);
+    &rol	($dat,8);			# next byte
+    &pxor	($red[1],$red[1]);
+    &pxor	($red[2],$red[2]);
+
+    # Just like in "May" version modulo-schedule for critical path in
+    # 'Z.hi ^= rem_8bit[Z.lo&0xff^((u8)H[nhi]<<4)]<<48'. Final 'pxor'
+    # is scheduled so late that rem_8bit[] has to be shifted *right*
+    # by 16, which is why last argument to pinsrw is 2, which
+    # corresponds to <<32=<<48>>16...
+    for ($j=11,$i=0;$i<15;$i++) {
+
+      if ($i>0) {
+	&pxor	($Zlo,&QWP(16,"esp",$nlo,8));		# Z^=H[nlo]
+	&rol	($dat,8);				# next byte
+	&pxor	($Zhi,&QWP(16+128,"esp",$nlo,8));
+
+	&pxor	($Zlo,$tmp);
+	&pxor	($Zhi,&QWP(16+256+128,"esp",$nhi[0],8));
+	&xor	(&LB($rem[1]),&BP(0,"esp",$nhi[0]));	# rem^(H[nhi]<<4)
+      } else {
+	&movq	($Zlo,&QWP(16,"esp",$nlo,8));
+	&movq	($Zhi,&QWP(16+128,"esp",$nlo,8));
+      }
+
+	&mov	(&LB($nlo),&LB($dat));
+	&mov	($dat,&DWP(528+$j,"esp"))		if (--$j%4==0);
+
+	&movd	($rem[0],$Zlo);
+	&movz	($rem[1],&LB($rem[1]))			if ($i>0);
+	&psrlq	($Zlo,8);				# Z>>=8
+
+	&movq	($tmp,$Zhi);
+	&mov	($nhi[0],$nlo);
+	&psrlq	($Zhi,8);
+
+	&pxor	($Zlo,&QWP(16+256+0,"esp",$nhi[1],8));	# Z^=H[nhi]>>4
+	&and	(&LB($nlo),0x0f);
+	&psllq	($tmp,56);
+
+	&pxor	($Zhi,$red[1])				if ($i>1);
+	&shr	($nhi[0],4);
+	&pinsrw	($red[0],&WP(0,$rem_8bit,$rem[1],2),2)	if ($i>0);
+
+	unshift	(@red,pop(@red));			# "rotate" registers
+	unshift	(@rem,pop(@rem));
+	unshift	(@nhi,pop(@nhi));
+    }
+
+    &pxor	($Zlo,&QWP(16,"esp",$nlo,8));		# Z^=H[nlo]
+    &pxor	($Zhi,&QWP(16+128,"esp",$nlo,8));
+    &xor	(&LB($rem[1]),&BP(0,"esp",$nhi[0]));	# rem^(H[nhi]<<4)
+
+    &pxor	($Zlo,$tmp);
+    &pxor	($Zhi,&QWP(16+256+128,"esp",$nhi[0],8));
+    &movz	($rem[1],&LB($rem[1]));
+
+    &pxor	($red[2],$red[2]);			# clear 2nd word
+    &psllq	($red[1],4);
+
+    &movd	($rem[0],$Zlo);
+    &psrlq	($Zlo,4);				# Z>>=4
+
+    &movq	($tmp,$Zhi);
+    &psrlq	($Zhi,4);
+    &shl	($rem[0],4);				# rem<<4
+
+    &pxor	($Zlo,&QWP(16,"esp",$nhi[1],8));	# Z^=H[nhi]
+    &psllq	($tmp,60);
+    &movz	($rem[0],&LB($rem[0]));
+
+    &pxor	($Zlo,$tmp);
+    &pxor	($Zhi,&QWP(16+128,"esp",$nhi[1],8));
+
+    &pinsrw	($red[0],&WP(0,$rem_8bit,$rem[1],2),2);
+    &pxor	($Zhi,$red[1]);
+
+    &movd	($dat,$Zlo);
+    &pinsrw	($red[2],&WP(0,$rem_8bit,$rem[0],2),3);	# last is <<48
+
+    &psllq	($red[0],12);				# correct by <<16>>4
+    &pxor	($Zhi,$red[0]);
+    &psrlq	($Zlo,32);
+    &pxor	($Zhi,$red[2]);
+
+    &mov	("ecx",&DWP(528+16+4,"esp"));	# restore inp
+    &movd	("ebx",$Zlo);
+    &movq	($tmp,$Zhi);			# 01234567
+    &psllw	($Zhi,8);			# 1.3.5.7.
+    &psrlw	($tmp,8);			# .0.2.4.6
+    &por	($Zhi,$tmp);			# 10325476
+    &bswap	($dat);
+    &pshufw	($Zhi,$Zhi,0b00011011);		# 76543210
+    &bswap	("ebx");
+    
+    &cmp	("ecx",&DWP(528+16+8,"esp"));	# are we done?
+    &jne	(&label("outer"));
+  }
+
+    &mov	("eax",&DWP(528+16+0,"esp"));	# restore Xi
+    &mov	(&DWP(12,"eax"),"edx");
+    &mov	(&DWP(8,"eax"),"ebx");
+    &movq	(&QWP(0,"eax"),$Zhi);
+
+    &mov	("esp",&DWP(528+16+12,"esp"));	# restore original %esp
+    &emms	();
+}
+&function_end("gcm_ghash_4bit_mmx");
+}}
+
+if ($sse2) {{
+######################################################################
+# PCLMULQDQ version.
+
+$Xip="eax";
+$Htbl="edx";
+$const="ecx";
+$inp="esi";
+$len="ebx";
+
+($Xi,$Xhi)=("xmm0","xmm1");	$Hkey="xmm2";
+($T1,$T2,$T3)=("xmm3","xmm4","xmm5");
+($Xn,$Xhn)=("xmm6","xmm7");
+
+&static_label("bswap");
+
+sub clmul64x64_T2 {	# minimal "register" pressure
+my ($Xhi,$Xi,$Hkey,$HK)=@_;
+
+	&movdqa		($Xhi,$Xi);		#
+	&pshufd		($T1,$Xi,0b01001110);
+	&pshufd		($T2,$Hkey,0b01001110)	if (!defined($HK));
+	&pxor		($T1,$Xi);		#
+	&pxor		($T2,$Hkey)		if (!defined($HK));
+			$HK=$T2			if (!defined($HK));
+
+	&pclmulqdq	($Xi,$Hkey,0x00);	#######
+	&pclmulqdq	($Xhi,$Hkey,0x11);	#######
+	&pclmulqdq	($T1,$HK,0x00);		#######
+	&xorps		($T1,$Xi);		#
+	&xorps		($T1,$Xhi);		#
+
+	&movdqa		($T2,$T1);		#
+	&psrldq		($T1,8);
+	&pslldq		($T2,8);		#
+	&pxor		($Xhi,$T1);
+	&pxor		($Xi,$T2);		#
+}
+
+sub clmul64x64_T3 {
+# Even though this subroutine offers visually better ILP, it
+# was empirically found to be a tad slower than above version.
+# At least in gcm_ghash_clmul context. But it's just as well,
+# because loop modulo-scheduling is possible only thanks to
+# minimized "register" pressure...
+my ($Xhi,$Xi,$Hkey)=@_;
+
+	&movdqa		($T1,$Xi);		#
+	&movdqa		($Xhi,$Xi);
+	&pclmulqdq	($Xi,$Hkey,0x00);	#######
+	&pclmulqdq	($Xhi,$Hkey,0x11);	#######
+	&pshufd		($T2,$T1,0b01001110);	#
+	&pshufd		($T3,$Hkey,0b01001110);
+	&pxor		($T2,$T1);		#
+	&pxor		($T3,$Hkey);
+	&pclmulqdq	($T2,$T3,0x00);		#######
+	&pxor		($T2,$Xi);		#
+	&pxor		($T2,$Xhi);		#
+
+	&movdqa		($T3,$T2);		#
+	&psrldq		($T2,8);
+	&pslldq		($T3,8);		#
+	&pxor		($Xhi,$T2);
+	&pxor		($Xi,$T3);		#
+}
+
+if (1) {		# Algorithm 9 with <<1 twist.
+			# Reduction is shorter and uses only two
+			# temporary registers, which makes it better
+			# candidate for interleaving with 64x64
+			# multiplication. Pre-modulo-scheduled loop
+			# was found to be ~20% faster than Algorithm 5
+			# below. Algorithm 9 was therefore chosen for
+			# further optimization...
+
+sub reduction_alg9 {	# 17/11 times faster than Intel version
+my ($Xhi,$Xi) = @_;
+
+	# 1st phase
+	&movdqa		($T2,$Xi);		#
+	&movdqa		($T1,$Xi);
+	&psllq		($Xi,5);
+	&pxor		($T1,$Xi);		#
+	&psllq		($Xi,1);
+	&pxor		($Xi,$T1);		#
+	&psllq		($Xi,57);		#
+	&movdqa		($T1,$Xi);		#
+	&pslldq		($Xi,8);
+	&psrldq		($T1,8);		#	
+	&pxor		($Xi,$T2);
+	&pxor		($Xhi,$T1);		#
+
+	# 2nd phase
+	&movdqa		($T2,$Xi);
+	&psrlq		($Xi,1);
+	&pxor		($Xhi,$T2);		#
+	&pxor		($T2,$Xi);
+	&psrlq		($Xi,5);
+	&pxor		($Xi,$T2);		#
+	&psrlq		($Xi,1);		#
+	&pxor		($Xi,$Xhi)		#
+}
+
+&function_begin_B("gcm_init_clmul");
+	&mov		($Htbl,&wparam(0));
+	&mov		($Xip,&wparam(1));
+
+	&call		(&label("pic"));
+&set_label("pic");
+	&blindpop	($const);
+	&lea		($const,&DWP(&label("bswap")."-".&label("pic"),$const));
+
+	&movdqu		($Hkey,&QWP(0,$Xip));
+	&pshufd		($Hkey,$Hkey,0b01001110);# dword swap
+
+	# <<1 twist
+	&pshufd		($T2,$Hkey,0b11111111);	# broadcast uppermost dword
+	&movdqa		($T1,$Hkey);
+	&psllq		($Hkey,1);
+	&pxor		($T3,$T3);		#
+	&psrlq		($T1,63);
+	&pcmpgtd	($T3,$T2);		# broadcast carry bit
+	&pslldq		($T1,8);
+	&por		($Hkey,$T1);		# H<<=1
+
+	# magic reduction
+	&pand		($T3,&QWP(16,$const));	# 0x1c2_polynomial
+	&pxor		($Hkey,$T3);		# if(carry) H^=0x1c2_polynomial
+
+	# calculate H^2
+	&movdqa		($Xi,$Hkey);
+	&clmul64x64_T2	($Xhi,$Xi,$Hkey);
+	&reduction_alg9	($Xhi,$Xi);
+
+	&pshufd		($T1,$Hkey,0b01001110);
+	&pshufd		($T2,$Xi,0b01001110);
+	&pxor		($T1,$Hkey);		# Karatsuba pre-processing
+	&movdqu		(&QWP(0,$Htbl),$Hkey);	# save H
+	&pxor		($T2,$Xi);		# Karatsuba pre-processing
+	&movdqu		(&QWP(16,$Htbl),$Xi);	# save H^2
+	&palignr	($T2,$T1,8);		# low part is H.lo^H.hi
+	&movdqu		(&QWP(32,$Htbl),$T2);	# save Karatsuba "salt"
+
+	&ret		();
+&function_end_B("gcm_init_clmul");
+
+&function_begin_B("gcm_gmult_clmul");
+	&mov		($Xip,&wparam(0));
+	&mov		($Htbl,&wparam(1));
+
+	&call		(&label("pic"));
+&set_label("pic");
+	&blindpop	($const);
+	&lea		($const,&DWP(&label("bswap")."-".&label("pic"),$const));
+
+	&movdqu		($Xi,&QWP(0,$Xip));
+	&movdqa		($T3,&QWP(0,$const));
+	&movups		($Hkey,&QWP(0,$Htbl));
+	&pshufb		($Xi,$T3);
+	&movups		($T2,&QWP(32,$Htbl));
+
+	&clmul64x64_T2	($Xhi,$Xi,$Hkey,$T2);
+	&reduction_alg9	($Xhi,$Xi);
+
+	&pshufb		($Xi,$T3);
+	&movdqu		(&QWP(0,$Xip),$Xi);
+
+	&ret	();
+&function_end_B("gcm_gmult_clmul");
+
+&function_begin("gcm_ghash_clmul");
+	&mov		($Xip,&wparam(0));
+	&mov		($Htbl,&wparam(1));
+	&mov		($inp,&wparam(2));
+	&mov		($len,&wparam(3));
+
+	&call		(&label("pic"));
+&set_label("pic");
+	&blindpop	($const);
+	&lea		($const,&DWP(&label("bswap")."-".&label("pic"),$const));
+
+	&movdqu		($Xi,&QWP(0,$Xip));
+	&movdqa		($T3,&QWP(0,$const));
+	&movdqu		($Hkey,&QWP(0,$Htbl));
+	&pshufb		($Xi,$T3);
+
+	&sub		($len,0x10);
+	&jz		(&label("odd_tail"));
+
+	#######
+	# Xi+2 =[H*(Ii+1 + Xi+1)] mod P =
+	#	[(H*Ii+1) + (H*Xi+1)] mod P =
+	#	[(H*Ii+1) + H^2*(Ii+Xi)] mod P
+	#
+	&movdqu		($T1,&QWP(0,$inp));	# Ii
+	&movdqu		($Xn,&QWP(16,$inp));	# Ii+1
+	&pshufb		($T1,$T3);
+	&pshufb		($Xn,$T3);
+	&movdqu		($T3,&QWP(32,$Htbl));
+	&pxor		($Xi,$T1);		# Ii+Xi
+
+	&pshufd		($T1,$Xn,0b01001110);	# H*Ii+1
+	&movdqa		($Xhn,$Xn);
+	&pxor		($T1,$Xn);		#
+	&lea		($inp,&DWP(32,$inp));	# i+=2
+
+	&pclmulqdq	($Xn,$Hkey,0x00);	#######
+	&pclmulqdq	($Xhn,$Hkey,0x11);	#######
+	&pclmulqdq	($T1,$T3,0x00);		#######
+	&movups		($Hkey,&QWP(16,$Htbl));	# load H^2
+	&nop		();
+
+	&sub		($len,0x20);
+	&jbe		(&label("even_tail"));
+	&jmp		(&label("mod_loop"));
+
+&set_label("mod_loop",32);
+	&pshufd		($T2,$Xi,0b01001110);	# H^2*(Ii+Xi)
+	&movdqa		($Xhi,$Xi);
+	&pxor		($T2,$Xi);		#
+	&nop		();
+
+	&pclmulqdq	($Xi,$Hkey,0x00);	#######
+	&pclmulqdq	($Xhi,$Hkey,0x11);	#######
+	&pclmulqdq	($T2,$T3,0x10);		#######
+	&movups		($Hkey,&QWP(0,$Htbl));	# load H
+
+	&xorps		($Xi,$Xn);		# (H*Ii+1) + H^2*(Ii+Xi)
+	&movdqa		($T3,&QWP(0,$const));
+	&xorps		($Xhi,$Xhn);
+	 &movdqu	($Xhn,&QWP(0,$inp));	# Ii
+	&pxor		($T1,$Xi);		# aggregated Karatsuba post-processing
+	 &movdqu	($Xn,&QWP(16,$inp));	# Ii+1
+	&pxor		($T1,$Xhi);		#
+
+	 &pshufb	($Xhn,$T3);
+	&pxor		($T2,$T1);		#
+
+	&movdqa		($T1,$T2);		#
+	&psrldq		($T2,8);
+	&pslldq		($T1,8);		#
+	&pxor		($Xhi,$T2);
+	&pxor		($Xi,$T1);		#
+	 &pshufb	($Xn,$T3);
+	 &pxor		($Xhi,$Xhn);		# "Ii+Xi", consume early
+
+	&movdqa		($Xhn,$Xn);		#&clmul64x64_TX	($Xhn,$Xn,$Hkey); H*Ii+1
+	  &movdqa	($T2,$Xi);		#&reduction_alg9($Xhi,$Xi); 1st phase
+	  &movdqa	($T1,$Xi);
+	  &psllq	($Xi,5);
+	  &pxor		($T1,$Xi);		#
+	  &psllq	($Xi,1);
+	  &pxor		($Xi,$T1);		#
+	&pclmulqdq	($Xn,$Hkey,0x00);	#######
+	&movups		($T3,&QWP(32,$Htbl));
+	  &psllq	($Xi,57);		#
+	  &movdqa	($T1,$Xi);		#
+	  &pslldq	($Xi,8);
+	  &psrldq	($T1,8);		#	
+	  &pxor		($Xi,$T2);
+	  &pxor		($Xhi,$T1);		#
+	&pshufd		($T1,$Xhn,0b01001110);
+	  &movdqa	($T2,$Xi);		# 2nd phase
+	  &psrlq	($Xi,1);
+	&pxor		($T1,$Xhn);
+	  &pxor		($Xhi,$T2);		#
+	&pclmulqdq	($Xhn,$Hkey,0x11);	#######
+	&movups		($Hkey,&QWP(16,$Htbl));	# load H^2
+	  &pxor		($T2,$Xi);
+	  &psrlq	($Xi,5);
+	  &pxor		($Xi,$T2);		#
+	  &psrlq	($Xi,1);		#
+	  &pxor		($Xi,$Xhi)		#
+	&pclmulqdq	($T1,$T3,0x00);		#######
+
+	&lea		($inp,&DWP(32,$inp));
+	&sub		($len,0x20);
+	&ja		(&label("mod_loop"));
+
+&set_label("even_tail");
+	&pshufd		($T2,$Xi,0b01001110);	# H^2*(Ii+Xi)
+	&movdqa		($Xhi,$Xi);
+	&pxor		($T2,$Xi);		#
+
+	&pclmulqdq	($Xi,$Hkey,0x00);	#######
+	&pclmulqdq	($Xhi,$Hkey,0x11);	#######
+	&pclmulqdq	($T2,$T3,0x10);		#######
+	&movdqa		($T3,&QWP(0,$const));
+
+	&xorps		($Xi,$Xn);		# (H*Ii+1) + H^2*(Ii+Xi)
+	&xorps		($Xhi,$Xhn);
+	&pxor		($T1,$Xi);		# aggregated Karatsuba post-processing
+	&pxor		($T1,$Xhi);		#
+
+	&pxor		($T2,$T1);		#
+
+	&movdqa		($T1,$T2);		#
+	&psrldq		($T2,8);
+	&pslldq		($T1,8);		#
+	&pxor		($Xhi,$T2);
+	&pxor		($Xi,$T1);		#
+
+	&reduction_alg9	($Xhi,$Xi);
+
+	&test		($len,$len);
+	&jnz		(&label("done"));
+
+	&movups		($Hkey,&QWP(0,$Htbl));	# load H
+&set_label("odd_tail");
+	&movdqu		($T1,&QWP(0,$inp));	# Ii
+	&pshufb		($T1,$T3);
+	&pxor		($Xi,$T1);		# Ii+Xi
+
+	&clmul64x64_T2	($Xhi,$Xi,$Hkey);	# H*(Ii+Xi)
+	&reduction_alg9	($Xhi,$Xi);
+
+&set_label("done");
+	&pshufb		($Xi,$T3);
+	&movdqu		(&QWP(0,$Xip),$Xi);
+&function_end("gcm_ghash_clmul");
+
+} else {		# Algorithm 5. Kept for reference purposes.
+
+sub reduction_alg5 {	# 19/16 times faster than Intel version
+my ($Xhi,$Xi)=@_;
+
+	# <<1
+	&movdqa		($T1,$Xi);		#
+	&movdqa		($T2,$Xhi);
+	&pslld		($Xi,1);
+	&pslld		($Xhi,1);		#
+	&psrld		($T1,31);
+	&psrld		($T2,31);		#
+	&movdqa		($T3,$T1);
+	&pslldq		($T1,4);
+	&psrldq		($T3,12);		#
+	&pslldq		($T2,4);
+	&por		($Xhi,$T3);		#
+	&por		($Xi,$T1);
+	&por		($Xhi,$T2);		#
+
+	# 1st phase
+	&movdqa		($T1,$Xi);
+	&movdqa		($T2,$Xi);
+	&movdqa		($T3,$Xi);		#
+	&pslld		($T1,31);
+	&pslld		($T2,30);
+	&pslld		($Xi,25);		#
+	&pxor		($T1,$T2);
+	&pxor		($T1,$Xi);		#
+	&movdqa		($T2,$T1);		#
+	&pslldq		($T1,12);
+	&psrldq		($T2,4);		#
+	&pxor		($T3,$T1);
+
+	# 2nd phase
+	&pxor		($Xhi,$T3);		#
+	&movdqa		($Xi,$T3);
+	&movdqa		($T1,$T3);
+	&psrld		($Xi,1);		#
+	&psrld		($T1,2);
+	&psrld		($T3,7);		#
+	&pxor		($Xi,$T1);
+	&pxor		($Xhi,$T2);
+	&pxor		($Xi,$T3);		#
+	&pxor		($Xi,$Xhi);		#
+}
+
+&function_begin_B("gcm_init_clmul");
+	&mov		($Htbl,&wparam(0));
+	&mov		($Xip,&wparam(1));
+
+	&call		(&label("pic"));
+&set_label("pic");
+	&blindpop	($const);
+	&lea		($const,&DWP(&label("bswap")."-".&label("pic"),$const));
+
+	&movdqu		($Hkey,&QWP(0,$Xip));
+	&pshufd		($Hkey,$Hkey,0b01001110);# dword swap
+
+	# calculate H^2
+	&movdqa		($Xi,$Hkey);
+	&clmul64x64_T3	($Xhi,$Xi,$Hkey);
+	&reduction_alg5	($Xhi,$Xi);
+
+	&movdqu		(&QWP(0,$Htbl),$Hkey);	# save H
+	&movdqu		(&QWP(16,$Htbl),$Xi);	# save H^2
+
+	&ret		();
+&function_end_B("gcm_init_clmul");
+
+&function_begin_B("gcm_gmult_clmul");
+	&mov		($Xip,&wparam(0));
+	&mov		($Htbl,&wparam(1));
+
+	&call		(&label("pic"));
+&set_label("pic");
+	&blindpop	($const);
+	&lea		($const,&DWP(&label("bswap")."-".&label("pic"),$const));
+
+	&movdqu		($Xi,&QWP(0,$Xip));
+	&movdqa		($Xn,&QWP(0,$const));
+	&movdqu		($Hkey,&QWP(0,$Htbl));
+	&pshufb		($Xi,$Xn);
+
+	&clmul64x64_T3	($Xhi,$Xi,$Hkey);
+	&reduction_alg5	($Xhi,$Xi);
+
+	&pshufb		($Xi,$Xn);
+	&movdqu		(&QWP(0,$Xip),$Xi);
+
+	&ret	();
+&function_end_B("gcm_gmult_clmul");
+
+&function_begin("gcm_ghash_clmul");
+	&mov		($Xip,&wparam(0));
+	&mov		($Htbl,&wparam(1));
+	&mov		($inp,&wparam(2));
+	&mov		($len,&wparam(3));
+
+	&call		(&label("pic"));
+&set_label("pic");
+	&blindpop	($const);
+	&lea		($const,&DWP(&label("bswap")."-".&label("pic"),$const));
+
+	&movdqu		($Xi,&QWP(0,$Xip));
+	&movdqa		($T3,&QWP(0,$const));
+	&movdqu		($Hkey,&QWP(0,$Htbl));
+	&pshufb		($Xi,$T3);
+
+	&sub		($len,0x10);
+	&jz		(&label("odd_tail"));
+
+	#######
+	# Xi+2 =[H*(Ii+1 + Xi+1)] mod P =
+	#	[(H*Ii+1) + (H*Xi+1)] mod P =
+	#	[(H*Ii+1) + H^2*(Ii+Xi)] mod P
+	#
+	&movdqu		($T1,&QWP(0,$inp));	# Ii
+	&movdqu		($Xn,&QWP(16,$inp));	# Ii+1
+	&pshufb		($T1,$T3);
+	&pshufb		($Xn,$T3);
+	&pxor		($Xi,$T1);		# Ii+Xi
+
+	&clmul64x64_T3	($Xhn,$Xn,$Hkey);	# H*Ii+1
+	&movdqu		($Hkey,&QWP(16,$Htbl));	# load H^2
+
+	&sub		($len,0x20);
+	&lea		($inp,&DWP(32,$inp));	# i+=2
+	&jbe		(&label("even_tail"));
+
+&set_label("mod_loop");
+	&clmul64x64_T3	($Xhi,$Xi,$Hkey);	# H^2*(Ii+Xi)
+	&movdqu		($Hkey,&QWP(0,$Htbl));	# load H
+
+	&pxor		($Xi,$Xn);		# (H*Ii+1) + H^2*(Ii+Xi)
+	&pxor		($Xhi,$Xhn);
+
+	&reduction_alg5	($Xhi,$Xi);
+
+	#######
+	&movdqa		($T3,&QWP(0,$const));
+	&movdqu		($T1,&QWP(0,$inp));	# Ii
+	&movdqu		($Xn,&QWP(16,$inp));	# Ii+1
+	&pshufb		($T1,$T3);
+	&pshufb		($Xn,$T3);
+	&pxor		($Xi,$T1);		# Ii+Xi
+
+	&clmul64x64_T3	($Xhn,$Xn,$Hkey);	# H*Ii+1
+	&movdqu		($Hkey,&QWP(16,$Htbl));	# load H^2
+
+	&sub		($len,0x20);
+	&lea		($inp,&DWP(32,$inp));
+	&ja		(&label("mod_loop"));
+
+&set_label("even_tail");
+	&clmul64x64_T3	($Xhi,$Xi,$Hkey);	# H^2*(Ii+Xi)
+
+	&pxor		($Xi,$Xn);		# (H*Ii+1) + H^2*(Ii+Xi)
+	&pxor		($Xhi,$Xhn);
+
+	&reduction_alg5	($Xhi,$Xi);
+
+	&movdqa		($T3,&QWP(0,$const));
+	&test		($len,$len);
+	&jnz		(&label("done"));
+
+	&movdqu		($Hkey,&QWP(0,$Htbl));	# load H
+&set_label("odd_tail");
+	&movdqu		($T1,&QWP(0,$inp));	# Ii
+	&pshufb		($T1,$T3);
+	&pxor		($Xi,$T1);		# Ii+Xi
+
+	&clmul64x64_T3	($Xhi,$Xi,$Hkey);	# H*(Ii+Xi)
+	&reduction_alg5	($Xhi,$Xi);
+
+	&movdqa		($T3,&QWP(0,$const));
+&set_label("done");
+	&pshufb		($Xi,$T3);
+	&movdqu		(&QWP(0,$Xip),$Xi);
+&function_end("gcm_ghash_clmul");
+
+}
+
+&set_label("bswap",64);
+	&data_byte(15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0);
+	&data_byte(1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0xc2);	# 0x1c2_polynomial
+&set_label("rem_8bit",64);
+	&data_short(0x0000,0x01C2,0x0384,0x0246,0x0708,0x06CA,0x048C,0x054E);
+	&data_short(0x0E10,0x0FD2,0x0D94,0x0C56,0x0918,0x08DA,0x0A9C,0x0B5E);
+	&data_short(0x1C20,0x1DE2,0x1FA4,0x1E66,0x1B28,0x1AEA,0x18AC,0x196E);
+	&data_short(0x1230,0x13F2,0x11B4,0x1076,0x1538,0x14FA,0x16BC,0x177E);
+	&data_short(0x3840,0x3982,0x3BC4,0x3A06,0x3F48,0x3E8A,0x3CCC,0x3D0E);
+	&data_short(0x3650,0x3792,0x35D4,0x3416,0x3158,0x309A,0x32DC,0x331E);
+	&data_short(0x2460,0x25A2,0x27E4,0x2626,0x2368,0x22AA,0x20EC,0x212E);
+	&data_short(0x2A70,0x2BB2,0x29F4,0x2836,0x2D78,0x2CBA,0x2EFC,0x2F3E);
+	&data_short(0x7080,0x7142,0x7304,0x72C6,0x7788,0x764A,0x740C,0x75CE);
+	&data_short(0x7E90,0x7F52,0x7D14,0x7CD6,0x7998,0x785A,0x7A1C,0x7BDE);
+	&data_short(0x6CA0,0x6D62,0x6F24,0x6EE6,0x6BA8,0x6A6A,0x682C,0x69EE);
+	&data_short(0x62B0,0x6372,0x6134,0x60F6,0x65B8,0x647A,0x663C,0x67FE);
+	&data_short(0x48C0,0x4902,0x4B44,0x4A86,0x4FC8,0x4E0A,0x4C4C,0x4D8E);
+	&data_short(0x46D0,0x4712,0x4554,0x4496,0x41D8,0x401A,0x425C,0x439E);
+	&data_short(0x54E0,0x5522,0x5764,0x56A6,0x53E8,0x522A,0x506C,0x51AE);
+	&data_short(0x5AF0,0x5B32,0x5974,0x58B6,0x5DF8,0x5C3A,0x5E7C,0x5FBE);
+	&data_short(0xE100,0xE0C2,0xE284,0xE346,0xE608,0xE7CA,0xE58C,0xE44E);
+	&data_short(0xEF10,0xEED2,0xEC94,0xED56,0xE818,0xE9DA,0xEB9C,0xEA5E);
+	&data_short(0xFD20,0xFCE2,0xFEA4,0xFF66,0xFA28,0xFBEA,0xF9AC,0xF86E);
+	&data_short(0xF330,0xF2F2,0xF0B4,0xF176,0xF438,0xF5FA,0xF7BC,0xF67E);
+	&data_short(0xD940,0xD882,0xDAC4,0xDB06,0xDE48,0xDF8A,0xDDCC,0xDC0E);
+	&data_short(0xD750,0xD692,0xD4D4,0xD516,0xD058,0xD19A,0xD3DC,0xD21E);
+	&data_short(0xC560,0xC4A2,0xC6E4,0xC726,0xC268,0xC3AA,0xC1EC,0xC02E);
+	&data_short(0xCB70,0xCAB2,0xC8F4,0xC936,0xCC78,0xCDBA,0xCFFC,0xCE3E);
+	&data_short(0x9180,0x9042,0x9204,0x93C6,0x9688,0x974A,0x950C,0x94CE);
+	&data_short(0x9F90,0x9E52,0x9C14,0x9DD6,0x9898,0x995A,0x9B1C,0x9ADE);
+	&data_short(0x8DA0,0x8C62,0x8E24,0x8FE6,0x8AA8,0x8B6A,0x892C,0x88EE);
+	&data_short(0x83B0,0x8272,0x8034,0x81F6,0x84B8,0x857A,0x873C,0x86FE);
+	&data_short(0xA9C0,0xA802,0xAA44,0xAB86,0xAEC8,0xAF0A,0xAD4C,0xAC8E);
+	&data_short(0xA7D0,0xA612,0xA454,0xA596,0xA0D8,0xA11A,0xA35C,0xA29E);
+	&data_short(0xB5E0,0xB422,0xB664,0xB7A6,0xB2E8,0xB32A,0xB16C,0xB0AE);
+	&data_short(0xBBF0,0xBA32,0xB874,0xB9B6,0xBCF8,0xBD3A,0xBF7C,0xBEBE);
+}}	# $sse2
+
+&set_label("rem_4bit",64);
+	&data_word(0,0x0000<<$S,0,0x1C20<<$S,0,0x3840<<$S,0,0x2460<<$S);
+	&data_word(0,0x7080<<$S,0,0x6CA0<<$S,0,0x48C0<<$S,0,0x54E0<<$S);
+	&data_word(0,0xE100<<$S,0,0xFD20<<$S,0,0xD940<<$S,0,0xC560<<$S);
+	&data_word(0,0x9180<<$S,0,0x8DA0<<$S,0,0xA9C0<<$S,0,0xB5E0<<$S);
+}}}	# !$x86only
+
+&asciz("GHASH for x86, CRYPTOGAMS by ");
+&asm_finish();
+
+close STDOUT;
+
+# A question was risen about choice of vanilla MMX. Or rather why wasn't
+# SSE2 chosen instead? In addition to the fact that MMX runs on legacy
+# CPUs such as PIII, "4-bit" MMX version was observed to provide better
+# performance than *corresponding* SSE2 one even on contemporary CPUs.
+# SSE2 results were provided by Peter-Michael Hager. He maintains SSE2
+# implementation featuring full range of lookup-table sizes, but with
+# per-invocation lookup table setup. Latter means that table size is
+# chosen depending on how much data is to be hashed in every given call,
+# more data - larger table. Best reported result for Core2 is ~4 cycles
+# per processed byte out of 64KB block. This number accounts even for
+# 64KB table setup overhead. As discussed in gcm128.c we choose to be
+# more conservative in respect to lookup table sizes, but how do the
+# results compare? Minimalistic "256B" MMX version delivers ~11 cycles
+# on same platform. As also discussed in gcm128.c, next in line "8-bit
+# Shoup's" or "4KB" method should deliver twice the performance of
+# "256B" one, in other words not worse than ~6 cycles per byte. It
+# should be also be noted that in SSE2 case improvement can be "super-
+# linear," i.e. more than twice, mostly because >>8 maps to single
+# instruction on SSE2 register. This is unlike "4-bit" case when >>4
+# maps to same amount of instructions in both MMX and SSE2 cases.
+# Bottom line is that switch to SSE2 is considered to be justifiable
+# only in case we choose to implement "8-bit" method...
diff --git a/crypto/modes/asm/ghash-x86_64.pl b/crypto/modes/asm/ghash-x86_64.pl
new file mode 100644
index 000000000..387e3f854
--- /dev/null
+++ b/crypto/modes/asm/ghash-x86_64.pl
@@ -0,0 +1,1762 @@
+#! /usr/bin/env perl
+# Copyright 2010-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+#
+# ====================================================================
+# Written by Andy Polyakov  for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+#
+# March, June 2010
+#
+# The module implements "4-bit" GCM GHASH function and underlying
+# single multiplication operation in GF(2^128). "4-bit" means that
+# it uses 256 bytes per-key table [+128 bytes shared table]. GHASH
+# function features so called "528B" variant utilizing additional
+# 256+16 bytes of per-key storage [+512 bytes shared table].
+# Performance results are for this streamed GHASH subroutine and are
+# expressed in cycles per processed byte, less is better:
+#
+#		gcc 3.4.x(*)	assembler
+#
+# P4		28.6		14.0		+100%
+# Opteron	19.3		7.7		+150%
+# Core2		17.8		8.1(**)		+120%
+# Atom		31.6		16.8		+88%
+# VIA Nano	21.8		10.1		+115%
+#
+# (*)	comparison is not completely fair, because C results are
+#	for vanilla "256B" implementation, while assembler results
+#	are for "528B";-)
+# (**)	it's mystery [to me] why Core2 result is not same as for
+#	Opteron;
+
+# May 2010
+#
+# Add PCLMULQDQ version performing at 2.02 cycles per processed byte.
+# See ghash-x86.pl for background information and details about coding
+# techniques.
+#
+# Special thanks to David Woodhouse  for
+# providing access to a Westmere-based system on behalf of Intel
+# Open Source Technology Centre.
+
+# December 2012
+#
+# Overhaul: aggregate Karatsuba post-processing, improve ILP in
+# reduction_alg9, increase reduction aggregate factor to 4x. As for
+# the latter. ghash-x86.pl discusses that it makes lesser sense to
+# increase aggregate factor. Then why increase here? Critical path
+# consists of 3 independent pclmulqdq instructions, Karatsuba post-
+# processing and reduction. "On top" of this we lay down aggregated
+# multiplication operations, triplets of independent pclmulqdq's. As
+# issue rate for pclmulqdq is limited, it makes lesser sense to
+# aggregate more multiplications than it takes to perform remaining
+# non-multiplication operations. 2x is near-optimal coefficient for
+# contemporary Intel CPUs (therefore modest improvement coefficient),
+# but not for Bulldozer. Latter is because logical SIMD operations
+# are twice as slow in comparison to Intel, so that critical path is
+# longer. A CPU with higher pclmulqdq issue rate would also benefit
+# from higher aggregate factor...
+#
+# Westmere	1.78(+13%)
+# Sandy Bridge	1.80(+8%)
+# Ivy Bridge	1.80(+7%)
+# Haswell	0.55(+93%) (if system doesn't support AVX)
+# Broadwell	0.45(+110%)(if system doesn't support AVX)
+# Skylake	0.44(+110%)(if system doesn't support AVX)
+# Bulldozer	1.49(+27%)
+# Silvermont	2.88(+13%)
+# Goldmont	1.08(+24%)
+
+# March 2013
+#
+# ... 8x aggregate factor AVX code path is using reduction algorithm
+# suggested by Shay Gueron[1]. Even though contemporary AVX-capable
+# CPUs such as Sandy and Ivy Bridge can execute it, the code performs
+# sub-optimally in comparison to above mentioned version. But thanks
+# to Ilya Albrekht and Max Locktyukhin of Intel Corp. we knew that
+# it performs in 0.41 cycles per byte on Haswell processor, in
+# 0.29 on Broadwell, and in 0.36 on Skylake.
+#
+# [1] http://rt.openssl.org/Ticket/Display.html?id=2900&user=guest&pass=guest
+
+$flavour = shift;
+$output  = shift;
+if ($flavour =~ /\./) { $output = $flavour; undef $flavour; }
+
+$win64=0; $win64=1 if ($flavour =~ /[nm]asm|mingw64/ || $output =~ /\.asm$/);
+
+$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+( $xlate="${dir}x86_64-xlate.pl" and -f $xlate ) or
+( $xlate="${dir}../../perlasm/x86_64-xlate.pl" and -f $xlate) or
+die "can't locate x86_64-xlate.pl";
+
+if (`$ENV{CC} -Wa,-v -c -o /dev/null -x assembler /dev/null 2>&1`
+		=~ /GNU assembler version ([2-9]\.[0-9]+)/) {
+	$avx = ($1>=2.20) + ($1>=2.22);
+}
+
+if (!$avx && $win64 && ($flavour =~ /nasm/ || $ENV{ASM} =~ /nasm/) &&
+	    `nasm -v 2>&1` =~ /NASM version ([2-9]\.[0-9]+)/) {
+	$avx = ($1>=2.09) + ($1>=2.10);
+}
+
+if (!$avx && $win64 && ($flavour =~ /masm/ || $ENV{ASM} =~ /ml64/) &&
+	    `ml64 2>&1` =~ /Version ([0-9]+)\./) {
+	$avx = ($1>=10) + ($1>=11);
+}
+
+if (!$avx && `$ENV{CC} -v 2>&1` =~ /((?:^clang|LLVM) version|.*based on LLVM) ([3-9]\.[0-9]+)/) {
+	$avx = ($2>=3.0) + ($2>3.0);
+}
+
+open OUT,"| \"$^X\" \"$xlate\" $flavour \"$output\"";
+*STDOUT=*OUT;
+
+$do4xaggr=1;
+
+# common register layout
+$nlo="%rax";
+$nhi="%rbx";
+$Zlo="%r8";
+$Zhi="%r9";
+$tmp="%r10";
+$rem_4bit = "%r11";
+
+$Xi="%rdi";
+$Htbl="%rsi";
+
+# per-function register layout
+$cnt="%rcx";
+$rem="%rdx";
+
+sub LB() { my $r=shift; $r =~ s/%[er]([a-d])x/%\1l/	or
+			$r =~ s/%[er]([sd]i)/%\1l/	or
+			$r =~ s/%[er](bp)/%\1l/		or
+			$r =~ s/%(r[0-9]+)[d]?/%\1b/;   $r; }
+
+sub AUTOLOAD()		# thunk [simplified] 32-bit style perlasm
+{ my $opcode = $AUTOLOAD; $opcode =~ s/.*:://;
+  my $arg = pop;
+    $arg = "\$$arg" if ($arg*1 eq $arg);
+    $code .= "\t$opcode\t".join(',',$arg,reverse @_)."\n";
+}
+
+{ my $N;
+  sub loop() {
+  my $inp = shift;
+
+	$N++;
+$code.=<<___;
+	xor	$nlo,$nlo
+	xor	$nhi,$nhi
+	mov	`&LB("$Zlo")`,`&LB("$nlo")`
+	mov	`&LB("$Zlo")`,`&LB("$nhi")`
+	shl	\$4,`&LB("$nlo")`
+	mov	\$14,$cnt
+	mov	8($Htbl,$nlo),$Zlo
+	mov	($Htbl,$nlo),$Zhi
+	and	\$0xf0,`&LB("$nhi")`
+	mov	$Zlo,$rem
+	jmp	.Loop$N
+
+.align	16
+.Loop$N:
+	shr	\$4,$Zlo
+	and	\$0xf,$rem
+	mov	$Zhi,$tmp
+	mov	($inp,$cnt),`&LB("$nlo")`
+	shr	\$4,$Zhi
+	xor	8($Htbl,$nhi),$Zlo
+	shl	\$60,$tmp
+	xor	($Htbl,$nhi),$Zhi
+	mov	`&LB("$nlo")`,`&LB("$nhi")`
+	xor	($rem_4bit,$rem,8),$Zhi
+	mov	$Zlo,$rem
+	shl	\$4,`&LB("$nlo")`
+	xor	$tmp,$Zlo
+	dec	$cnt
+	js	.Lbreak$N
+
+	shr	\$4,$Zlo
+	and	\$0xf,$rem
+	mov	$Zhi,$tmp
+	shr	\$4,$Zhi
+	xor	8($Htbl,$nlo),$Zlo
+	shl	\$60,$tmp
+	xor	($Htbl,$nlo),$Zhi
+	and	\$0xf0,`&LB("$nhi")`
+	xor	($rem_4bit,$rem,8),$Zhi
+	mov	$Zlo,$rem
+	xor	$tmp,$Zlo
+	jmp	.Loop$N
+
+.align	16
+.Lbreak$N:
+	shr	\$4,$Zlo
+	and	\$0xf,$rem
+	mov	$Zhi,$tmp
+	shr	\$4,$Zhi
+	xor	8($Htbl,$nlo),$Zlo
+	shl	\$60,$tmp
+	xor	($Htbl,$nlo),$Zhi
+	and	\$0xf0,`&LB("$nhi")`
+	xor	($rem_4bit,$rem,8),$Zhi
+	mov	$Zlo,$rem
+	xor	$tmp,$Zlo
+
+	shr	\$4,$Zlo
+	and	\$0xf,$rem
+	mov	$Zhi,$tmp
+	shr	\$4,$Zhi
+	xor	8($Htbl,$nhi),$Zlo
+	shl	\$60,$tmp
+	xor	($Htbl,$nhi),$Zhi
+	xor	$tmp,$Zlo
+	xor	($rem_4bit,$rem,8),$Zhi
+
+	bswap	$Zlo
+	bswap	$Zhi
+___
+}}
+
+$code=<<___;
+.text
+.extern	OPENSSL_ia32cap_P
+
+.globl	gcm_gmult_4bit
+.type	gcm_gmult_4bit,\@function,2
+.align	16
+gcm_gmult_4bit:
+	push	%rbx
+	push	%rbp		# %rbp and %r12 are pushed exclusively in
+	push	%r12		# order to reuse Win64 exception handler...
+.Lgmult_prologue:
+
+	movzb	15($Xi),$Zlo
+	lea	.Lrem_4bit(%rip),$rem_4bit
+___
+	&loop	($Xi);
+$code.=<<___;
+	mov	$Zlo,8($Xi)
+	mov	$Zhi,($Xi)
+
+	mov	16(%rsp),%rbx
+	lea	24(%rsp),%rsp
+.Lgmult_epilogue:
+	ret
+.size	gcm_gmult_4bit,.-gcm_gmult_4bit
+___
+
+# per-function register layout
+$inp="%rdx";
+$len="%rcx";
+$rem_8bit=$rem_4bit;
+
+$code.=<<___;
+.globl	gcm_ghash_4bit
+.type	gcm_ghash_4bit,\@function,4
+.align	16
+gcm_ghash_4bit:
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+	sub	\$280,%rsp
+.Lghash_prologue:
+	mov	$inp,%r14		# reassign couple of args
+	mov	$len,%r15
+___
+{ my $inp="%r14";
+  my $dat="%edx";
+  my $len="%r15";
+  my @nhi=("%ebx","%ecx");
+  my @rem=("%r12","%r13");
+  my $Hshr4="%rbp";
+
+	&sub	($Htbl,-128);		# size optimization
+	&lea	($Hshr4,"16+128(%rsp)");
+	{ my @lo =($nlo,$nhi);
+          my @hi =($Zlo,$Zhi);
+
+	  &xor	($dat,$dat);
+	  for ($i=0,$j=-2;$i<18;$i++,$j++) {
+	    &mov	("$j(%rsp)",&LB($dat))		if ($i>1);
+	    &or		($lo[0],$tmp)			if ($i>1);
+	    &mov	(&LB($dat),&LB($lo[1]))		if ($i>0 && $i<17);
+	    &shr	($lo[1],4)			if ($i>0 && $i<17);
+	    &mov	($tmp,$hi[1])			if ($i>0 && $i<17);
+	    &shr	($hi[1],4)			if ($i>0 && $i<17);
+	    &mov	("8*$j($Hshr4)",$hi[0])		if ($i>1);
+	    &mov	($hi[0],"16*$i+0-128($Htbl)")	if ($i<16);
+	    &shl	(&LB($dat),4)			if ($i>0 && $i<17);
+	    &mov	("8*$j-128($Hshr4)",$lo[0])	if ($i>1);
+	    &mov	($lo[0],"16*$i+8-128($Htbl)")	if ($i<16);
+	    &shl	($tmp,60)			if ($i>0 && $i<17);
+
+	    push	(@lo,shift(@lo));
+	    push	(@hi,shift(@hi));
+	  }
+	}
+	&add	($Htbl,-128);
+	&mov	($Zlo,"8($Xi)");
+	&mov	($Zhi,"0($Xi)");
+	&add	($len,$inp);		# pointer to the end of data
+	&lea	($rem_8bit,".Lrem_8bit(%rip)");
+	&jmp	(".Louter_loop");
+
+$code.=".align	16\n.Louter_loop:\n";
+	&xor	($Zhi,"($inp)");
+	&mov	("%rdx","8($inp)");
+	&lea	($inp,"16($inp)");
+	&xor	("%rdx",$Zlo);
+	&mov	("($Xi)",$Zhi);
+	&mov	("8($Xi)","%rdx");
+	&shr	("%rdx",32);
+
+	&xor	($nlo,$nlo);
+	&rol	($dat,8);
+	&mov	(&LB($nlo),&LB($dat));
+	&movz	($nhi[0],&LB($dat));
+	&shl	(&LB($nlo),4);
+	&shr	($nhi[0],4);
+
+	for ($j=11,$i=0;$i<15;$i++) {
+	    &rol	($dat,8);
+	    &xor	($Zlo,"8($Htbl,$nlo)")			if ($i>0);
+	    &xor	($Zhi,"($Htbl,$nlo)")			if ($i>0);
+	    &mov	($Zlo,"8($Htbl,$nlo)")			if ($i==0);
+	    &mov	($Zhi,"($Htbl,$nlo)")			if ($i==0);
+
+	    &mov	(&LB($nlo),&LB($dat));
+	    &xor	($Zlo,$tmp)				if ($i>0);
+	    &movzw	($rem[1],"($rem_8bit,$rem[1],2)")	if ($i>0);
+
+	    &movz	($nhi[1],&LB($dat));
+	    &shl	(&LB($nlo),4);
+	    &movzb	($rem[0],"(%rsp,$nhi[0])");
+
+	    &shr	($nhi[1],4)				if ($i<14);
+	    &and	($nhi[1],0xf0)				if ($i==14);
+	    &shl	($rem[1],48)				if ($i>0);
+	    &xor	($rem[0],$Zlo);
+
+	    &mov	($tmp,$Zhi);
+	    &xor	($Zhi,$rem[1])				if ($i>0);
+	    &shr	($Zlo,8);
+
+	    &movz	($rem[0],&LB($rem[0]));
+	    &mov	($dat,"$j($Xi)")			if (--$j%4==0);
+	    &shr	($Zhi,8);
+
+	    &xor	($Zlo,"-128($Hshr4,$nhi[0],8)");
+	    &shl	($tmp,56);
+	    &xor	($Zhi,"($Hshr4,$nhi[0],8)");
+
+	    unshift	(@nhi,pop(@nhi));		# "rotate" registers
+	    unshift	(@rem,pop(@rem));
+	}
+	&movzw	($rem[1],"($rem_8bit,$rem[1],2)");
+	&xor	($Zlo,"8($Htbl,$nlo)");
+	&xor	($Zhi,"($Htbl,$nlo)");
+
+	&shl	($rem[1],48);
+	&xor	($Zlo,$tmp);
+
+	&xor	($Zhi,$rem[1]);
+	&movz	($rem[0],&LB($Zlo));
+	&shr	($Zlo,4);
+
+	&mov	($tmp,$Zhi);
+	&shl	(&LB($rem[0]),4);
+	&shr	($Zhi,4);
+
+	&xor	($Zlo,"8($Htbl,$nhi[0])");
+	&movzw	($rem[0],"($rem_8bit,$rem[0],2)");
+	&shl	($tmp,60);
+
+	&xor	($Zhi,"($Htbl,$nhi[0])");
+	&xor	($Zlo,$tmp);
+	&shl	($rem[0],48);
+
+	&bswap	($Zlo);
+	&xor	($Zhi,$rem[0]);
+
+	&bswap	($Zhi);
+	&cmp	($inp,$len);
+	&jb	(".Louter_loop");
+}
+$code.=<<___;
+	mov	$Zlo,8($Xi)
+	mov	$Zhi,($Xi)
+
+	lea	280(%rsp),%rsi
+	mov	0(%rsi),%r15
+	mov	8(%rsi),%r14
+	mov	16(%rsi),%r13
+	mov	24(%rsi),%r12
+	mov	32(%rsi),%rbp
+	mov	40(%rsi),%rbx
+	lea	48(%rsi),%rsp
+.Lghash_epilogue:
+	ret
+.size	gcm_ghash_4bit,.-gcm_ghash_4bit
+___
+
+######################################################################
+# PCLMULQDQ version.
+
+@_4args=$win64?	("%rcx","%rdx","%r8", "%r9") :	# Win64 order
+		("%rdi","%rsi","%rdx","%rcx");	# Unix order
+
+($Xi,$Xhi)=("%xmm0","%xmm1");	$Hkey="%xmm2";
+($T1,$T2,$T3)=("%xmm3","%xmm4","%xmm5");
+
+sub clmul64x64_T2 {	# minimal register pressure
+my ($Xhi,$Xi,$Hkey,$HK)=@_;
+
+if (!defined($HK)) {	$HK = $T2;
+$code.=<<___;
+	movdqa		$Xi,$Xhi		#
+	pshufd		\$0b01001110,$Xi,$T1
+	pshufd		\$0b01001110,$Hkey,$T2
+	pxor		$Xi,$T1			#
+	pxor		$Hkey,$T2
+___
+} else {
+$code.=<<___;
+	movdqa		$Xi,$Xhi		#
+	pshufd		\$0b01001110,$Xi,$T1
+	pxor		$Xi,$T1			#
+___
+}
+$code.=<<___;
+	pclmulqdq	\$0x00,$Hkey,$Xi	#######
+	pclmulqdq	\$0x11,$Hkey,$Xhi	#######
+	pclmulqdq	\$0x00,$HK,$T1		#######
+	pxor		$Xi,$T1			#
+	pxor		$Xhi,$T1		#
+
+	movdqa		$T1,$T2			#
+	psrldq		\$8,$T1
+	pslldq		\$8,$T2			#
+	pxor		$T1,$Xhi
+	pxor		$T2,$Xi			#
+___
+}
+
+sub reduction_alg9 {	# 17/11 times faster than Intel version
+my ($Xhi,$Xi) = @_;
+
+$code.=<<___;
+	# 1st phase
+	movdqa		$Xi,$T2			#
+	movdqa		$Xi,$T1
+	psllq		\$5,$Xi
+	pxor		$Xi,$T1			#
+	psllq		\$1,$Xi
+	pxor		$T1,$Xi			#
+	psllq		\$57,$Xi		#
+	movdqa		$Xi,$T1			#
+	pslldq		\$8,$Xi
+	psrldq		\$8,$T1			#	
+	pxor		$T2,$Xi
+	pxor		$T1,$Xhi		#
+
+	# 2nd phase
+	movdqa		$Xi,$T2
+	psrlq		\$1,$Xi
+	pxor		$T2,$Xhi		#
+	pxor		$Xi,$T2
+	psrlq		\$5,$Xi
+	pxor		$T2,$Xi			#
+	psrlq		\$1,$Xi			#
+	pxor		$Xhi,$Xi		#
+___
+}
+
+{ my ($Htbl,$Xip)=@_4args;
+  my $HK="%xmm6";
+
+$code.=<<___;
+.globl	gcm_init_clmul
+.type	gcm_init_clmul,\@abi-omnipotent
+.align	16
+gcm_init_clmul:
+.L_init_clmul:
+___
+$code.=<<___ if ($win64);
+.LSEH_begin_gcm_init_clmul:
+	# I can't trust assembler to use specific encoding:-(
+	.byte	0x48,0x83,0xec,0x18		#sub	$0x18,%rsp
+	.byte	0x0f,0x29,0x34,0x24		#movaps	%xmm6,(%rsp)
+___
+$code.=<<___;
+	movdqu		($Xip),$Hkey
+	pshufd		\$0b01001110,$Hkey,$Hkey	# dword swap
+
+	# <<1 twist
+	pshufd		\$0b11111111,$Hkey,$T2	# broadcast uppermost dword
+	movdqa		$Hkey,$T1
+	psllq		\$1,$Hkey
+	pxor		$T3,$T3			#
+	psrlq		\$63,$T1
+	pcmpgtd		$T2,$T3			# broadcast carry bit
+	pslldq		\$8,$T1
+	por		$T1,$Hkey		# H<<=1
+
+	# magic reduction
+	pand		.L0x1c2_polynomial(%rip),$T3
+	pxor		$T3,$Hkey		# if(carry) H^=0x1c2_polynomial
+
+	# calculate H^2
+	pshufd		\$0b01001110,$Hkey,$HK
+	movdqa		$Hkey,$Xi
+	pxor		$Hkey,$HK
+___
+	&clmul64x64_T2	($Xhi,$Xi,$Hkey,$HK);
+	&reduction_alg9	($Xhi,$Xi);
+$code.=<<___;
+	pshufd		\$0b01001110,$Hkey,$T1
+	pshufd		\$0b01001110,$Xi,$T2
+	pxor		$Hkey,$T1		# Karatsuba pre-processing
+	movdqu		$Hkey,0x00($Htbl)	# save H
+	pxor		$Xi,$T2			# Karatsuba pre-processing
+	movdqu		$Xi,0x10($Htbl)		# save H^2
+	palignr		\$8,$T1,$T2		# low part is H.lo^H.hi...
+	movdqu		$T2,0x20($Htbl)		# save Karatsuba "salt"
+___
+if ($do4xaggr) {
+	&clmul64x64_T2	($Xhi,$Xi,$Hkey,$HK);	# H^3
+	&reduction_alg9	($Xhi,$Xi);
+$code.=<<___;
+	movdqa		$Xi,$T3
+___
+	&clmul64x64_T2	($Xhi,$Xi,$Hkey,$HK);	# H^4
+	&reduction_alg9	($Xhi,$Xi);
+$code.=<<___;
+	pshufd		\$0b01001110,$T3,$T1
+	pshufd		\$0b01001110,$Xi,$T2
+	pxor		$T3,$T1			# Karatsuba pre-processing
+	movdqu		$T3,0x30($Htbl)		# save H^3
+	pxor		$Xi,$T2			# Karatsuba pre-processing
+	movdqu		$Xi,0x40($Htbl)		# save H^4
+	palignr		\$8,$T1,$T2		# low part is H^3.lo^H^3.hi...
+	movdqu		$T2,0x50($Htbl)		# save Karatsuba "salt"
+___
+}
+$code.=<<___ if ($win64);
+	movaps	(%rsp),%xmm6
+	lea	0x18(%rsp),%rsp
+.LSEH_end_gcm_init_clmul:
+___
+$code.=<<___;
+	ret
+.size	gcm_init_clmul,.-gcm_init_clmul
+___
+}
+
+{ my ($Xip,$Htbl)=@_4args;
+
+$code.=<<___;
+.globl	gcm_gmult_clmul
+.type	gcm_gmult_clmul,\@abi-omnipotent
+.align	16
+gcm_gmult_clmul:
+.L_gmult_clmul:
+	movdqu		($Xip),$Xi
+	movdqa		.Lbswap_mask(%rip),$T3
+	movdqu		($Htbl),$Hkey
+	movdqu		0x20($Htbl),$T2
+	pshufb		$T3,$Xi
+___
+	&clmul64x64_T2	($Xhi,$Xi,$Hkey,$T2);
+$code.=<<___ if (0 || (&reduction_alg9($Xhi,$Xi)&&0));
+	# experimental alternative. special thing about is that there
+	# no dependency between the two multiplications... 
+	mov		\$`0xE1<<1`,%eax
+	mov		\$0xA040608020C0E000,%r10	# ((7..0)·0xE0)&0xff
+	mov		\$0x07,%r11d
+	movq		%rax,$T1
+	movq		%r10,$T2
+	movq		%r11,$T3		# borrow $T3
+	pand		$Xi,$T3
+	pshufb		$T3,$T2			# ($Xi&7)·0xE0
+	movq		%rax,$T3
+	pclmulqdq	\$0x00,$Xi,$T1		# ·(0xE1<<1)
+	pxor		$Xi,$T2
+	pslldq		\$15,$T2
+	paddd		$T2,$T2			# <<(64+56+1)
+	pxor		$T2,$Xi
+	pclmulqdq	\$0x01,$T3,$Xi
+	movdqa		.Lbswap_mask(%rip),$T3	# reload $T3
+	psrldq		\$1,$T1
+	pxor		$T1,$Xhi
+	pslldq		\$7,$Xi
+	pxor		$Xhi,$Xi
+___
+$code.=<<___;
+	pshufb		$T3,$Xi
+	movdqu		$Xi,($Xip)
+	ret
+.size	gcm_gmult_clmul,.-gcm_gmult_clmul
+___
+}
+
+{ my ($Xip,$Htbl,$inp,$len)=@_4args;
+  my ($Xln,$Xmn,$Xhn,$Hkey2,$HK) = map("%xmm$_",(3..7));
+  my ($T1,$T2,$T3)=map("%xmm$_",(8..10));
+
+$code.=<<___;
+.globl	gcm_ghash_clmul
+.type	gcm_ghash_clmul,\@abi-omnipotent
+.align	32
+gcm_ghash_clmul:
+.L_ghash_clmul:
+___
+$code.=<<___ if ($win64);
+	lea	-0x88(%rsp),%rax
+.LSEH_begin_gcm_ghash_clmul:
+	# I can't trust assembler to use specific encoding:-(
+	.byte	0x48,0x8d,0x60,0xe0		#lea	-0x20(%rax),%rsp
+	.byte	0x0f,0x29,0x70,0xe0		#movaps	%xmm6,-0x20(%rax)
+	.byte	0x0f,0x29,0x78,0xf0		#movaps	%xmm7,-0x10(%rax)
+	.byte	0x44,0x0f,0x29,0x00		#movaps	%xmm8,0(%rax)
+	.byte	0x44,0x0f,0x29,0x48,0x10	#movaps	%xmm9,0x10(%rax)
+	.byte	0x44,0x0f,0x29,0x50,0x20	#movaps	%xmm10,0x20(%rax)
+	.byte	0x44,0x0f,0x29,0x58,0x30	#movaps	%xmm11,0x30(%rax)
+	.byte	0x44,0x0f,0x29,0x60,0x40	#movaps	%xmm12,0x40(%rax)
+	.byte	0x44,0x0f,0x29,0x68,0x50	#movaps	%xmm13,0x50(%rax)
+	.byte	0x44,0x0f,0x29,0x70,0x60	#movaps	%xmm14,0x60(%rax)
+	.byte	0x44,0x0f,0x29,0x78,0x70	#movaps	%xmm15,0x70(%rax)
+___
+$code.=<<___;
+	movdqa		.Lbswap_mask(%rip),$T3
+
+	movdqu		($Xip),$Xi
+	movdqu		($Htbl),$Hkey
+	movdqu		0x20($Htbl),$HK
+	pshufb		$T3,$Xi
+
+	sub		\$0x10,$len
+	jz		.Lodd_tail
+
+	movdqu		0x10($Htbl),$Hkey2
+___
+if ($do4xaggr) {
+my ($Xl,$Xm,$Xh,$Hkey3,$Hkey4)=map("%xmm$_",(11..15));
+
+$code.=<<___;
+	mov		OPENSSL_ia32cap_P+4(%rip),%eax
+	cmp		\$0x30,$len
+	jb		.Lskip4x
+
+	and		\$`1<<26|1<<22`,%eax	# isolate MOVBE+XSAVE
+	cmp		\$`1<<22`,%eax		# check for MOVBE without XSAVE
+	je		.Lskip4x
+
+	sub		\$0x30,$len
+	mov		\$0xA040608020C0E000,%rax	# ((7..0)·0xE0)&0xff
+	movdqu		0x30($Htbl),$Hkey3
+	movdqu		0x40($Htbl),$Hkey4
+
+	#######
+	# Xi+4 =[(H*Ii+3) + (H^2*Ii+2) + (H^3*Ii+1) + H^4*(Ii+Xi)] mod P
+	#
+	movdqu		0x30($inp),$Xln
+	 movdqu		0x20($inp),$Xl
+	pshufb		$T3,$Xln
+	 pshufb		$T3,$Xl
+	movdqa		$Xln,$Xhn
+	pshufd		\$0b01001110,$Xln,$Xmn
+	pxor		$Xln,$Xmn
+	pclmulqdq	\$0x00,$Hkey,$Xln
+	pclmulqdq	\$0x11,$Hkey,$Xhn
+	pclmulqdq	\$0x00,$HK,$Xmn
+
+	movdqa		$Xl,$Xh
+	pshufd		\$0b01001110,$Xl,$Xm
+	pxor		$Xl,$Xm
+	pclmulqdq	\$0x00,$Hkey2,$Xl
+	pclmulqdq	\$0x11,$Hkey2,$Xh
+	pclmulqdq	\$0x10,$HK,$Xm
+	xorps		$Xl,$Xln
+	xorps		$Xh,$Xhn
+	movups		0x50($Htbl),$HK
+	xorps		$Xm,$Xmn
+
+	movdqu		0x10($inp),$Xl
+	 movdqu		0($inp),$T1
+	pshufb		$T3,$Xl
+	 pshufb		$T3,$T1
+	movdqa		$Xl,$Xh
+	pshufd		\$0b01001110,$Xl,$Xm
+	 pxor		$T1,$Xi
+	pxor		$Xl,$Xm
+	pclmulqdq	\$0x00,$Hkey3,$Xl
+	 movdqa		$Xi,$Xhi
+	 pshufd		\$0b01001110,$Xi,$T1
+	 pxor		$Xi,$T1
+	pclmulqdq	\$0x11,$Hkey3,$Xh
+	pclmulqdq	\$0x00,$HK,$Xm
+	xorps		$Xl,$Xln
+	xorps		$Xh,$Xhn
+
+	lea	0x40($inp),$inp
+	sub	\$0x40,$len
+	jc	.Ltail4x
+
+	jmp	.Lmod4_loop
+.align	32
+.Lmod4_loop:
+	pclmulqdq	\$0x00,$Hkey4,$Xi
+	xorps		$Xm,$Xmn
+	 movdqu		0x30($inp),$Xl
+	 pshufb		$T3,$Xl
+	pclmulqdq	\$0x11,$Hkey4,$Xhi
+	xorps		$Xln,$Xi
+	 movdqu		0x20($inp),$Xln
+	 movdqa		$Xl,$Xh
+	pclmulqdq	\$0x10,$HK,$T1
+	 pshufd		\$0b01001110,$Xl,$Xm
+	xorps		$Xhn,$Xhi
+	 pxor		$Xl,$Xm
+	 pshufb		$T3,$Xln
+	movups		0x20($Htbl),$HK
+	xorps		$Xmn,$T1
+	 pclmulqdq	\$0x00,$Hkey,$Xl
+	 pshufd		\$0b01001110,$Xln,$Xmn
+
+	pxor		$Xi,$T1			# aggregated Karatsuba post-processing
+	 movdqa		$Xln,$Xhn
+	pxor		$Xhi,$T1		#
+	 pxor		$Xln,$Xmn
+	movdqa		$T1,$T2			#
+	 pclmulqdq	\$0x11,$Hkey,$Xh
+	pslldq		\$8,$T1
+	psrldq		\$8,$T2			#
+	pxor		$T1,$Xi
+	movdqa		.L7_mask(%rip),$T1
+	pxor		$T2,$Xhi		#
+	movq		%rax,$T2
+
+	pand		$Xi,$T1			# 1st phase
+	pshufb		$T1,$T2			#
+	pxor		$Xi,$T2			#
+	 pclmulqdq	\$0x00,$HK,$Xm
+	psllq		\$57,$T2		#
+	movdqa		$T2,$T1			#
+	pslldq		\$8,$T2
+	 pclmulqdq	\$0x00,$Hkey2,$Xln
+	psrldq		\$8,$T1			#	
+	pxor		$T2,$Xi
+	pxor		$T1,$Xhi		#
+	movdqu		0($inp),$T1
+
+	movdqa		$Xi,$T2			# 2nd phase
+	psrlq		\$1,$Xi
+	 pclmulqdq	\$0x11,$Hkey2,$Xhn
+	 xorps		$Xl,$Xln
+	 movdqu		0x10($inp),$Xl
+	 pshufb		$T3,$Xl
+	 pclmulqdq	\$0x10,$HK,$Xmn
+	 xorps		$Xh,$Xhn
+	 movups		0x50($Htbl),$HK
+	pshufb		$T3,$T1
+	pxor		$T2,$Xhi		#
+	pxor		$Xi,$T2
+	psrlq		\$5,$Xi
+
+	 movdqa		$Xl,$Xh
+	 pxor		$Xm,$Xmn
+	 pshufd		\$0b01001110,$Xl,$Xm
+	pxor		$T2,$Xi			#
+	pxor		$T1,$Xhi
+	 pxor		$Xl,$Xm
+	 pclmulqdq	\$0x00,$Hkey3,$Xl
+	psrlq		\$1,$Xi			#
+	pxor		$Xhi,$Xi		#
+	movdqa		$Xi,$Xhi
+	 pclmulqdq	\$0x11,$Hkey3,$Xh
+	 xorps		$Xl,$Xln
+	pshufd		\$0b01001110,$Xi,$T1
+	pxor		$Xi,$T1
+
+	 pclmulqdq	\$0x00,$HK,$Xm
+	 xorps		$Xh,$Xhn
+
+	lea	0x40($inp),$inp
+	sub	\$0x40,$len
+	jnc	.Lmod4_loop
+
+.Ltail4x:
+	pclmulqdq	\$0x00,$Hkey4,$Xi
+	pclmulqdq	\$0x11,$Hkey4,$Xhi
+	pclmulqdq	\$0x10,$HK,$T1
+	xorps		$Xm,$Xmn
+	xorps		$Xln,$Xi
+	xorps		$Xhn,$Xhi
+	pxor		$Xi,$Xhi		# aggregated Karatsuba post-processing
+	pxor		$Xmn,$T1
+
+	pxor		$Xhi,$T1		#
+	pxor		$Xi,$Xhi
+
+	movdqa		$T1,$T2			#
+	psrldq		\$8,$T1
+	pslldq		\$8,$T2			#
+	pxor		$T1,$Xhi
+	pxor		$T2,$Xi			#
+___
+	&reduction_alg9($Xhi,$Xi);
+$code.=<<___;
+	add	\$0x40,$len
+	jz	.Ldone
+	movdqu	0x20($Htbl),$HK
+	sub	\$0x10,$len
+	jz	.Lodd_tail
+.Lskip4x:
+___
+}
+$code.=<<___;
+	#######
+	# Xi+2 =[H*(Ii+1 + Xi+1)] mod P =
+	#	[(H*Ii+1) + (H*Xi+1)] mod P =
+	#	[(H*Ii+1) + H^2*(Ii+Xi)] mod P
+	#
+	movdqu		($inp),$T1		# Ii
+	movdqu		16($inp),$Xln		# Ii+1
+	pshufb		$T3,$T1
+	pshufb		$T3,$Xln
+	pxor		$T1,$Xi			# Ii+Xi
+
+	movdqa		$Xln,$Xhn
+	pshufd		\$0b01001110,$Xln,$Xmn
+	pxor		$Xln,$Xmn
+	pclmulqdq	\$0x00,$Hkey,$Xln
+	pclmulqdq	\$0x11,$Hkey,$Xhn
+	pclmulqdq	\$0x00,$HK,$Xmn
+
+	lea		32($inp),$inp		# i+=2
+	nop
+	sub		\$0x20,$len
+	jbe		.Leven_tail
+	nop
+	jmp		.Lmod_loop
+
+.align	32
+.Lmod_loop:
+	movdqa		$Xi,$Xhi
+	movdqa		$Xmn,$T1
+	pshufd		\$0b01001110,$Xi,$Xmn	#
+	pxor		$Xi,$Xmn		#
+
+	pclmulqdq	\$0x00,$Hkey2,$Xi
+	pclmulqdq	\$0x11,$Hkey2,$Xhi
+	pclmulqdq	\$0x10,$HK,$Xmn
+
+	pxor		$Xln,$Xi		# (H*Ii+1) + H^2*(Ii+Xi)
+	pxor		$Xhn,$Xhi
+	  movdqu	($inp),$T2		# Ii
+	pxor		$Xi,$T1			# aggregated Karatsuba post-processing
+	  pshufb	$T3,$T2
+	  movdqu	16($inp),$Xln		# Ii+1
+
+	pxor		$Xhi,$T1
+	  pxor		$T2,$Xhi		# "Ii+Xi", consume early
+	pxor		$T1,$Xmn
+	 pshufb		$T3,$Xln
+	movdqa		$Xmn,$T1		#
+	psrldq		\$8,$T1
+	pslldq		\$8,$Xmn		#
+	pxor		$T1,$Xhi
+	pxor		$Xmn,$Xi		#
+
+	movdqa		$Xln,$Xhn		#
+
+	  movdqa	$Xi,$T2			# 1st phase
+	  movdqa	$Xi,$T1
+	  psllq		\$5,$Xi
+	  pxor		$Xi,$T1			#
+	pclmulqdq	\$0x00,$Hkey,$Xln	#######
+	  psllq		\$1,$Xi
+	  pxor		$T1,$Xi			#
+	  psllq		\$57,$Xi		#
+	  movdqa	$Xi,$T1			#
+	  pslldq	\$8,$Xi
+	  psrldq	\$8,$T1			#	
+	  pxor		$T2,$Xi
+	pshufd		\$0b01001110,$Xhn,$Xmn
+	  pxor		$T1,$Xhi		#
+	pxor		$Xhn,$Xmn		#
+
+	  movdqa	$Xi,$T2			# 2nd phase
+	  psrlq		\$1,$Xi
+	pclmulqdq	\$0x11,$Hkey,$Xhn	#######
+	  pxor		$T2,$Xhi		#
+	  pxor		$Xi,$T2
+	  psrlq		\$5,$Xi
+	  pxor		$T2,$Xi			#
+	lea		32($inp),$inp
+	  psrlq		\$1,$Xi			#
+	pclmulqdq	\$0x00,$HK,$Xmn		#######
+	  pxor		$Xhi,$Xi		#
+
+	sub		\$0x20,$len
+	ja		.Lmod_loop
+
+.Leven_tail:
+	 movdqa		$Xi,$Xhi
+	 movdqa		$Xmn,$T1
+	 pshufd		\$0b01001110,$Xi,$Xmn	#
+	 pxor		$Xi,$Xmn		#
+
+	pclmulqdq	\$0x00,$Hkey2,$Xi
+	pclmulqdq	\$0x11,$Hkey2,$Xhi
+	pclmulqdq	\$0x10,$HK,$Xmn
+
+	pxor		$Xln,$Xi		# (H*Ii+1) + H^2*(Ii+Xi)
+	pxor		$Xhn,$Xhi
+	pxor		$Xi,$T1
+	pxor		$Xhi,$T1
+	pxor		$T1,$Xmn
+	movdqa		$Xmn,$T1		#
+	psrldq		\$8,$T1
+	pslldq		\$8,$Xmn		#
+	pxor		$T1,$Xhi
+	pxor		$Xmn,$Xi		#
+___
+	&reduction_alg9	($Xhi,$Xi);
+$code.=<<___;
+	test		$len,$len
+	jnz		.Ldone
+
+.Lodd_tail:
+	movdqu		($inp),$T1		# Ii
+	pshufb		$T3,$T1
+	pxor		$T1,$Xi			# Ii+Xi
+___
+	&clmul64x64_T2	($Xhi,$Xi,$Hkey,$HK);	# H*(Ii+Xi)
+	&reduction_alg9	($Xhi,$Xi);
+$code.=<<___;
+.Ldone:
+	pshufb		$T3,$Xi
+	movdqu		$Xi,($Xip)
+___
+$code.=<<___ if ($win64);
+	movaps	(%rsp),%xmm6
+	movaps	0x10(%rsp),%xmm7
+	movaps	0x20(%rsp),%xmm8
+	movaps	0x30(%rsp),%xmm9
+	movaps	0x40(%rsp),%xmm10
+	movaps	0x50(%rsp),%xmm11
+	movaps	0x60(%rsp),%xmm12
+	movaps	0x70(%rsp),%xmm13
+	movaps	0x80(%rsp),%xmm14
+	movaps	0x90(%rsp),%xmm15
+	lea	0xa8(%rsp),%rsp
+.LSEH_end_gcm_ghash_clmul:
+___
+$code.=<<___;
+	ret
+.size	gcm_ghash_clmul,.-gcm_ghash_clmul
+___
+}
+
+$code.=<<___;
+.globl	gcm_init_avx
+.type	gcm_init_avx,\@abi-omnipotent
+.align	32
+gcm_init_avx:
+___
+if ($avx) {
+my ($Htbl,$Xip)=@_4args;
+my $HK="%xmm6";
+
+$code.=<<___ if ($win64);
+.LSEH_begin_gcm_init_avx:
+	# I can't trust assembler to use specific encoding:-(
+	.byte	0x48,0x83,0xec,0x18		#sub	$0x18,%rsp
+	.byte	0x0f,0x29,0x34,0x24		#movaps	%xmm6,(%rsp)
+___
+$code.=<<___;
+	vzeroupper
+
+	vmovdqu		($Xip),$Hkey
+	vpshufd		\$0b01001110,$Hkey,$Hkey	# dword swap
+
+	# <<1 twist
+	vpshufd		\$0b11111111,$Hkey,$T2	# broadcast uppermost dword
+	vpsrlq		\$63,$Hkey,$T1
+	vpsllq		\$1,$Hkey,$Hkey
+	vpxor		$T3,$T3,$T3		#
+	vpcmpgtd	$T2,$T3,$T3		# broadcast carry bit
+	vpslldq		\$8,$T1,$T1
+	vpor		$T1,$Hkey,$Hkey		# H<<=1
+
+	# magic reduction
+	vpand		.L0x1c2_polynomial(%rip),$T3,$T3
+	vpxor		$T3,$Hkey,$Hkey		# if(carry) H^=0x1c2_polynomial
+
+	vpunpckhqdq	$Hkey,$Hkey,$HK
+	vmovdqa		$Hkey,$Xi
+	vpxor		$Hkey,$HK,$HK
+	mov		\$4,%r10		# up to H^8
+	jmp		.Linit_start_avx
+___
+
+sub clmul64x64_avx {
+my ($Xhi,$Xi,$Hkey,$HK)=@_;
+
+if (!defined($HK)) {	$HK = $T2;
+$code.=<<___;
+	vpunpckhqdq	$Xi,$Xi,$T1
+	vpunpckhqdq	$Hkey,$Hkey,$T2
+	vpxor		$Xi,$T1,$T1		#
+	vpxor		$Hkey,$T2,$T2
+___
+} else {
+$code.=<<___;
+	vpunpckhqdq	$Xi,$Xi,$T1
+	vpxor		$Xi,$T1,$T1		#
+___
+}
+$code.=<<___;
+	vpclmulqdq	\$0x11,$Hkey,$Xi,$Xhi	#######
+	vpclmulqdq	\$0x00,$Hkey,$Xi,$Xi	#######
+	vpclmulqdq	\$0x00,$HK,$T1,$T1	#######
+	vpxor		$Xi,$Xhi,$T2		#
+	vpxor		$T2,$T1,$T1		#
+
+	vpslldq		\$8,$T1,$T2		#
+	vpsrldq		\$8,$T1,$T1
+	vpxor		$T2,$Xi,$Xi		#
+	vpxor		$T1,$Xhi,$Xhi
+___
+}
+
+sub reduction_avx {
+my ($Xhi,$Xi) = @_;
+
+$code.=<<___;
+	vpsllq		\$57,$Xi,$T1		# 1st phase
+	vpsllq		\$62,$Xi,$T2
+	vpxor		$T1,$T2,$T2		#
+	vpsllq		\$63,$Xi,$T1
+	vpxor		$T1,$T2,$T2		#
+	vpslldq		\$8,$T2,$T1		#
+	vpsrldq		\$8,$T2,$T2
+	vpxor		$T1,$Xi,$Xi		#
+	vpxor		$T2,$Xhi,$Xhi
+
+	vpsrlq		\$1,$Xi,$T2		# 2nd phase
+	vpxor		$Xi,$Xhi,$Xhi
+	vpxor		$T2,$Xi,$Xi		#
+	vpsrlq		\$5,$T2,$T2
+	vpxor		$T2,$Xi,$Xi		#
+	vpsrlq		\$1,$Xi,$Xi		#
+	vpxor		$Xhi,$Xi,$Xi		#
+___
+}
+
+$code.=<<___;
+.align	32
+.Linit_loop_avx:
+	vpalignr	\$8,$T1,$T2,$T3		# low part is H.lo^H.hi...
+	vmovdqu		$T3,-0x10($Htbl)	# save Karatsuba "salt"
+___
+	&clmul64x64_avx	($Xhi,$Xi,$Hkey,$HK);	# calculate H^3,5,7
+	&reduction_avx	($Xhi,$Xi);
+$code.=<<___;
+.Linit_start_avx:
+	vmovdqa		$Xi,$T3
+___
+	&clmul64x64_avx	($Xhi,$Xi,$Hkey,$HK);	# calculate H^2,4,6,8
+	&reduction_avx	($Xhi,$Xi);
+$code.=<<___;
+	vpshufd		\$0b01001110,$T3,$T1
+	vpshufd		\$0b01001110,$Xi,$T2
+	vpxor		$T3,$T1,$T1		# Karatsuba pre-processing
+	vmovdqu		$T3,0x00($Htbl)		# save H^1,3,5,7
+	vpxor		$Xi,$T2,$T2		# Karatsuba pre-processing
+	vmovdqu		$Xi,0x10($Htbl)		# save H^2,4,6,8
+	lea		0x30($Htbl),$Htbl
+	sub		\$1,%r10
+	jnz		.Linit_loop_avx
+
+	vpalignr	\$8,$T2,$T1,$T3		# last "salt" is flipped
+	vmovdqu		$T3,-0x10($Htbl)
+
+	vzeroupper
+___
+$code.=<<___ if ($win64);
+	movaps	(%rsp),%xmm6
+	lea	0x18(%rsp),%rsp
+.LSEH_end_gcm_init_avx:
+___
+$code.=<<___;
+	ret
+.size	gcm_init_avx,.-gcm_init_avx
+___
+} else {
+$code.=<<___;
+	jmp	.L_init_clmul
+.size	gcm_init_avx,.-gcm_init_avx
+___
+}
+
+$code.=<<___;
+.globl	gcm_gmult_avx
+.type	gcm_gmult_avx,\@abi-omnipotent
+.align	32
+gcm_gmult_avx:
+	jmp	.L_gmult_clmul
+.size	gcm_gmult_avx,.-gcm_gmult_avx
+___
+
+$code.=<<___;
+.globl	gcm_ghash_avx
+.type	gcm_ghash_avx,\@abi-omnipotent
+.align	32
+gcm_ghash_avx:
+___
+if ($avx) {
+my ($Xip,$Htbl,$inp,$len)=@_4args;
+my ($Xlo,$Xhi,$Xmi,
+    $Zlo,$Zhi,$Zmi,
+    $Hkey,$HK,$T1,$T2,
+    $Xi,$Xo,$Tred,$bswap,$Ii,$Ij) = map("%xmm$_",(0..15));
+
+$code.=<<___ if ($win64);
+	lea	-0x88(%rsp),%rax
+.LSEH_begin_gcm_ghash_avx:
+	# I can't trust assembler to use specific encoding:-(
+	.byte	0x48,0x8d,0x60,0xe0		#lea	-0x20(%rax),%rsp
+	.byte	0x0f,0x29,0x70,0xe0		#movaps	%xmm6,-0x20(%rax)
+	.byte	0x0f,0x29,0x78,0xf0		#movaps	%xmm7,-0x10(%rax)
+	.byte	0x44,0x0f,0x29,0x00		#movaps	%xmm8,0(%rax)
+	.byte	0x44,0x0f,0x29,0x48,0x10	#movaps	%xmm9,0x10(%rax)
+	.byte	0x44,0x0f,0x29,0x50,0x20	#movaps	%xmm10,0x20(%rax)
+	.byte	0x44,0x0f,0x29,0x58,0x30	#movaps	%xmm11,0x30(%rax)
+	.byte	0x44,0x0f,0x29,0x60,0x40	#movaps	%xmm12,0x40(%rax)
+	.byte	0x44,0x0f,0x29,0x68,0x50	#movaps	%xmm13,0x50(%rax)
+	.byte	0x44,0x0f,0x29,0x70,0x60	#movaps	%xmm14,0x60(%rax)
+	.byte	0x44,0x0f,0x29,0x78,0x70	#movaps	%xmm15,0x70(%rax)
+___
+$code.=<<___;
+	vzeroupper
+
+	vmovdqu		($Xip),$Xi		# load $Xi
+	lea		.L0x1c2_polynomial(%rip),%r10
+	lea		0x40($Htbl),$Htbl	# size optimization
+	vmovdqu		.Lbswap_mask(%rip),$bswap
+	vpshufb		$bswap,$Xi,$Xi
+	cmp		\$0x80,$len
+	jb		.Lshort_avx
+	sub		\$0x80,$len
+
+	vmovdqu		0x70($inp),$Ii		# I[7]
+	vmovdqu		0x00-0x40($Htbl),$Hkey	# $Hkey^1
+	vpshufb		$bswap,$Ii,$Ii
+	vmovdqu		0x20-0x40($Htbl),$HK
+
+	vpunpckhqdq	$Ii,$Ii,$T2
+	 vmovdqu	0x60($inp),$Ij		# I[6]
+	vpclmulqdq	\$0x00,$Hkey,$Ii,$Xlo
+	vpxor		$Ii,$T2,$T2
+	 vpshufb	$bswap,$Ij,$Ij
+	vpclmulqdq	\$0x11,$Hkey,$Ii,$Xhi
+	 vmovdqu	0x10-0x40($Htbl),$Hkey	# $Hkey^2
+	 vpunpckhqdq	$Ij,$Ij,$T1
+	 vmovdqu	0x50($inp),$Ii		# I[5]
+	vpclmulqdq	\$0x00,$HK,$T2,$Xmi
+	 vpxor		$Ij,$T1,$T1
+
+	 vpshufb	$bswap,$Ii,$Ii
+	vpclmulqdq	\$0x00,$Hkey,$Ij,$Zlo
+	 vpunpckhqdq	$Ii,$Ii,$T2
+	vpclmulqdq	\$0x11,$Hkey,$Ij,$Zhi
+	 vmovdqu	0x30-0x40($Htbl),$Hkey	# $Hkey^3
+	 vpxor		$Ii,$T2,$T2
+	 vmovdqu	0x40($inp),$Ij		# I[4]
+	vpclmulqdq	\$0x10,$HK,$T1,$Zmi
+	 vmovdqu	0x50-0x40($Htbl),$HK
+
+	 vpshufb	$bswap,$Ij,$Ij
+	vpxor		$Xlo,$Zlo,$Zlo
+	vpclmulqdq	\$0x00,$Hkey,$Ii,$Xlo
+	vpxor		$Xhi,$Zhi,$Zhi
+	 vpunpckhqdq	$Ij,$Ij,$T1
+	vpclmulqdq	\$0x11,$Hkey,$Ii,$Xhi
+	 vmovdqu	0x40-0x40($Htbl),$Hkey	# $Hkey^4
+	vpxor		$Xmi,$Zmi,$Zmi
+	vpclmulqdq	\$0x00,$HK,$T2,$Xmi
+	 vpxor		$Ij,$T1,$T1
+
+	 vmovdqu	0x30($inp),$Ii		# I[3]
+	vpxor		$Zlo,$Xlo,$Xlo
+	vpclmulqdq	\$0x00,$Hkey,$Ij,$Zlo
+	vpxor		$Zhi,$Xhi,$Xhi
+	 vpshufb	$bswap,$Ii,$Ii
+	vpclmulqdq	\$0x11,$Hkey,$Ij,$Zhi
+	 vmovdqu	0x60-0x40($Htbl),$Hkey	# $Hkey^5
+	vpxor		$Zmi,$Xmi,$Xmi
+	 vpunpckhqdq	$Ii,$Ii,$T2
+	vpclmulqdq	\$0x10,$HK,$T1,$Zmi
+	 vmovdqu	0x80-0x40($Htbl),$HK
+	 vpxor		$Ii,$T2,$T2
+
+	 vmovdqu	0x20($inp),$Ij		# I[2]
+	vpxor		$Xlo,$Zlo,$Zlo
+	vpclmulqdq	\$0x00,$Hkey,$Ii,$Xlo
+	vpxor		$Xhi,$Zhi,$Zhi
+	 vpshufb	$bswap,$Ij,$Ij
+	vpclmulqdq	\$0x11,$Hkey,$Ii,$Xhi
+	 vmovdqu	0x70-0x40($Htbl),$Hkey	# $Hkey^6
+	vpxor		$Xmi,$Zmi,$Zmi
+	 vpunpckhqdq	$Ij,$Ij,$T1
+	vpclmulqdq	\$0x00,$HK,$T2,$Xmi
+	 vpxor		$Ij,$T1,$T1
+
+	 vmovdqu	0x10($inp),$Ii		# I[1]
+	vpxor		$Zlo,$Xlo,$Xlo
+	vpclmulqdq	\$0x00,$Hkey,$Ij,$Zlo
+	vpxor		$Zhi,$Xhi,$Xhi
+	 vpshufb	$bswap,$Ii,$Ii
+	vpclmulqdq	\$0x11,$Hkey,$Ij,$Zhi
+	 vmovdqu	0x90-0x40($Htbl),$Hkey	# $Hkey^7
+	vpxor		$Zmi,$Xmi,$Xmi
+	 vpunpckhqdq	$Ii,$Ii,$T2
+	vpclmulqdq	\$0x10,$HK,$T1,$Zmi
+	 vmovdqu	0xb0-0x40($Htbl),$HK
+	 vpxor		$Ii,$T2,$T2
+
+	 vmovdqu	($inp),$Ij		# I[0]
+	vpxor		$Xlo,$Zlo,$Zlo
+	vpclmulqdq	\$0x00,$Hkey,$Ii,$Xlo
+	vpxor		$Xhi,$Zhi,$Zhi
+	 vpshufb	$bswap,$Ij,$Ij
+	vpclmulqdq	\$0x11,$Hkey,$Ii,$Xhi
+	 vmovdqu	0xa0-0x40($Htbl),$Hkey	# $Hkey^8
+	vpxor		$Xmi,$Zmi,$Zmi
+	vpclmulqdq	\$0x10,$HK,$T2,$Xmi
+
+	lea		0x80($inp),$inp
+	cmp		\$0x80,$len
+	jb		.Ltail_avx
+
+	vpxor		$Xi,$Ij,$Ij		# accumulate $Xi
+	sub		\$0x80,$len
+	jmp		.Loop8x_avx
+
+.align	32
+.Loop8x_avx:
+	vpunpckhqdq	$Ij,$Ij,$T1
+	 vmovdqu	0x70($inp),$Ii		# I[7]
+	vpxor		$Xlo,$Zlo,$Zlo
+	vpxor		$Ij,$T1,$T1
+	vpclmulqdq	\$0x00,$Hkey,$Ij,$Xi
+	 vpshufb	$bswap,$Ii,$Ii
+	vpxor		$Xhi,$Zhi,$Zhi
+	vpclmulqdq	\$0x11,$Hkey,$Ij,$Xo
+	 vmovdqu	0x00-0x40($Htbl),$Hkey	# $Hkey^1
+	 vpunpckhqdq	$Ii,$Ii,$T2
+	vpxor		$Xmi,$Zmi,$Zmi
+	vpclmulqdq	\$0x00,$HK,$T1,$Tred
+	 vmovdqu	0x20-0x40($Htbl),$HK
+	 vpxor		$Ii,$T2,$T2
+
+	  vmovdqu	0x60($inp),$Ij		# I[6]
+	 vpclmulqdq	\$0x00,$Hkey,$Ii,$Xlo
+	vpxor		$Zlo,$Xi,$Xi		# collect result
+	  vpshufb	$bswap,$Ij,$Ij
+	 vpclmulqdq	\$0x11,$Hkey,$Ii,$Xhi
+	vxorps		$Zhi,$Xo,$Xo
+	  vmovdqu	0x10-0x40($Htbl),$Hkey	# $Hkey^2
+	 vpunpckhqdq	$Ij,$Ij,$T1
+	 vpclmulqdq	\$0x00,$HK,  $T2,$Xmi
+	vpxor		$Zmi,$Tred,$Tred
+	 vxorps		$Ij,$T1,$T1
+
+	  vmovdqu	0x50($inp),$Ii		# I[5]
+	vpxor		$Xi,$Tred,$Tred		# aggregated Karatsuba post-processing
+	 vpclmulqdq	\$0x00,$Hkey,$Ij,$Zlo
+	vpxor		$Xo,$Tred,$Tred
+	vpslldq		\$8,$Tred,$T2
+	 vpxor		$Xlo,$Zlo,$Zlo
+	 vpclmulqdq	\$0x11,$Hkey,$Ij,$Zhi
+	vpsrldq		\$8,$Tred,$Tred
+	vpxor		$T2, $Xi, $Xi
+	  vmovdqu	0x30-0x40($Htbl),$Hkey	# $Hkey^3
+	  vpshufb	$bswap,$Ii,$Ii
+	vxorps		$Tred,$Xo, $Xo
+	 vpxor		$Xhi,$Zhi,$Zhi
+	 vpunpckhqdq	$Ii,$Ii,$T2
+	 vpclmulqdq	\$0x10,$HK,  $T1,$Zmi
+	  vmovdqu	0x50-0x40($Htbl),$HK
+	 vpxor		$Ii,$T2,$T2
+	 vpxor		$Xmi,$Zmi,$Zmi
+
+	  vmovdqu	0x40($inp),$Ij		# I[4]
+	vpalignr	\$8,$Xi,$Xi,$Tred	# 1st phase
+	 vpclmulqdq	\$0x00,$Hkey,$Ii,$Xlo
+	  vpshufb	$bswap,$Ij,$Ij
+	 vpxor		$Zlo,$Xlo,$Xlo
+	 vpclmulqdq	\$0x11,$Hkey,$Ii,$Xhi
+	  vmovdqu	0x40-0x40($Htbl),$Hkey	# $Hkey^4
+	 vpunpckhqdq	$Ij,$Ij,$T1
+	 vpxor		$Zhi,$Xhi,$Xhi
+	 vpclmulqdq	\$0x00,$HK,  $T2,$Xmi
+	 vxorps		$Ij,$T1,$T1
+	 vpxor		$Zmi,$Xmi,$Xmi
+
+	  vmovdqu	0x30($inp),$Ii		# I[3]
+	vpclmulqdq	\$0x10,(%r10),$Xi,$Xi
+	 vpclmulqdq	\$0x00,$Hkey,$Ij,$Zlo
+	  vpshufb	$bswap,$Ii,$Ii
+	 vpxor		$Xlo,$Zlo,$Zlo
+	 vpclmulqdq	\$0x11,$Hkey,$Ij,$Zhi
+	  vmovdqu	0x60-0x40($Htbl),$Hkey	# $Hkey^5
+	 vpunpckhqdq	$Ii,$Ii,$T2
+	 vpxor		$Xhi,$Zhi,$Zhi
+	 vpclmulqdq	\$0x10,$HK,  $T1,$Zmi
+	  vmovdqu	0x80-0x40($Htbl),$HK
+	 vpxor		$Ii,$T2,$T2
+	 vpxor		$Xmi,$Zmi,$Zmi
+
+	  vmovdqu	0x20($inp),$Ij		# I[2]
+	 vpclmulqdq	\$0x00,$Hkey,$Ii,$Xlo
+	  vpshufb	$bswap,$Ij,$Ij
+	 vpxor		$Zlo,$Xlo,$Xlo
+	 vpclmulqdq	\$0x11,$Hkey,$Ii,$Xhi
+	  vmovdqu	0x70-0x40($Htbl),$Hkey	# $Hkey^6
+	 vpunpckhqdq	$Ij,$Ij,$T1
+	 vpxor		$Zhi,$Xhi,$Xhi
+	 vpclmulqdq	\$0x00,$HK,  $T2,$Xmi
+	 vpxor		$Ij,$T1,$T1
+	 vpxor		$Zmi,$Xmi,$Xmi
+	vxorps		$Tred,$Xi,$Xi
+
+	  vmovdqu	0x10($inp),$Ii		# I[1]
+	vpalignr	\$8,$Xi,$Xi,$Tred	# 2nd phase
+	 vpclmulqdq	\$0x00,$Hkey,$Ij,$Zlo
+	  vpshufb	$bswap,$Ii,$Ii
+	 vpxor		$Xlo,$Zlo,$Zlo
+	 vpclmulqdq	\$0x11,$Hkey,$Ij,$Zhi
+	  vmovdqu	0x90-0x40($Htbl),$Hkey	# $Hkey^7
+	vpclmulqdq	\$0x10,(%r10),$Xi,$Xi
+	vxorps		$Xo,$Tred,$Tred
+	 vpunpckhqdq	$Ii,$Ii,$T2
+	 vpxor		$Xhi,$Zhi,$Zhi
+	 vpclmulqdq	\$0x10,$HK,  $T1,$Zmi
+	  vmovdqu	0xb0-0x40($Htbl),$HK
+	 vpxor		$Ii,$T2,$T2
+	 vpxor		$Xmi,$Zmi,$Zmi
+
+	  vmovdqu	($inp),$Ij		# I[0]
+	 vpclmulqdq	\$0x00,$Hkey,$Ii,$Xlo
+	  vpshufb	$bswap,$Ij,$Ij
+	 vpclmulqdq	\$0x11,$Hkey,$Ii,$Xhi
+	  vmovdqu	0xa0-0x40($Htbl),$Hkey	# $Hkey^8
+	vpxor		$Tred,$Ij,$Ij
+	 vpclmulqdq	\$0x10,$HK,  $T2,$Xmi
+	vpxor		$Xi,$Ij,$Ij		# accumulate $Xi
+
+	lea		0x80($inp),$inp
+	sub		\$0x80,$len
+	jnc		.Loop8x_avx
+
+	add		\$0x80,$len
+	jmp		.Ltail_no_xor_avx
+
+.align	32
+.Lshort_avx:
+	vmovdqu		-0x10($inp,$len),$Ii	# very last word
+	lea		($inp,$len),$inp
+	vmovdqu		0x00-0x40($Htbl),$Hkey	# $Hkey^1
+	vmovdqu		0x20-0x40($Htbl),$HK
+	vpshufb		$bswap,$Ii,$Ij
+
+	vmovdqa		$Xlo,$Zlo		# subtle way to zero $Zlo,
+	vmovdqa		$Xhi,$Zhi		# $Zhi and
+	vmovdqa		$Xmi,$Zmi		# $Zmi
+	sub		\$0x10,$len
+	jz		.Ltail_avx
+
+	vpunpckhqdq	$Ij,$Ij,$T1
+	vpxor		$Xlo,$Zlo,$Zlo
+	vpclmulqdq	\$0x00,$Hkey,$Ij,$Xlo
+	vpxor		$Ij,$T1,$T1
+	 vmovdqu	-0x20($inp),$Ii
+	vpxor		$Xhi,$Zhi,$Zhi
+	vpclmulqdq	\$0x11,$Hkey,$Ij,$Xhi
+	vmovdqu		0x10-0x40($Htbl),$Hkey	# $Hkey^2
+	 vpshufb	$bswap,$Ii,$Ij
+	vpxor		$Xmi,$Zmi,$Zmi
+	vpclmulqdq	\$0x00,$HK,$T1,$Xmi
+	vpsrldq		\$8,$HK,$HK
+	sub		\$0x10,$len
+	jz		.Ltail_avx
+
+	vpunpckhqdq	$Ij,$Ij,$T1
+	vpxor		$Xlo,$Zlo,$Zlo
+	vpclmulqdq	\$0x00,$Hkey,$Ij,$Xlo
+	vpxor		$Ij,$T1,$T1
+	 vmovdqu	-0x30($inp),$Ii
+	vpxor		$Xhi,$Zhi,$Zhi
+	vpclmulqdq	\$0x11,$Hkey,$Ij,$Xhi
+	vmovdqu		0x30-0x40($Htbl),$Hkey	# $Hkey^3
+	 vpshufb	$bswap,$Ii,$Ij
+	vpxor		$Xmi,$Zmi,$Zmi
+	vpclmulqdq	\$0x00,$HK,$T1,$Xmi
+	vmovdqu		0x50-0x40($Htbl),$HK
+	sub		\$0x10,$len
+	jz		.Ltail_avx
+
+	vpunpckhqdq	$Ij,$Ij,$T1
+	vpxor		$Xlo,$Zlo,$Zlo
+	vpclmulqdq	\$0x00,$Hkey,$Ij,$Xlo
+	vpxor		$Ij,$T1,$T1
+	 vmovdqu	-0x40($inp),$Ii
+	vpxor		$Xhi,$Zhi,$Zhi
+	vpclmulqdq	\$0x11,$Hkey,$Ij,$Xhi
+	vmovdqu		0x40-0x40($Htbl),$Hkey	# $Hkey^4
+	 vpshufb	$bswap,$Ii,$Ij
+	vpxor		$Xmi,$Zmi,$Zmi
+	vpclmulqdq	\$0x00,$HK,$T1,$Xmi
+	vpsrldq		\$8,$HK,$HK
+	sub		\$0x10,$len
+	jz		.Ltail_avx
+
+	vpunpckhqdq	$Ij,$Ij,$T1
+	vpxor		$Xlo,$Zlo,$Zlo
+	vpclmulqdq	\$0x00,$Hkey,$Ij,$Xlo
+	vpxor		$Ij,$T1,$T1
+	 vmovdqu	-0x50($inp),$Ii
+	vpxor		$Xhi,$Zhi,$Zhi
+	vpclmulqdq	\$0x11,$Hkey,$Ij,$Xhi
+	vmovdqu		0x60-0x40($Htbl),$Hkey	# $Hkey^5
+	 vpshufb	$bswap,$Ii,$Ij
+	vpxor		$Xmi,$Zmi,$Zmi
+	vpclmulqdq	\$0x00,$HK,$T1,$Xmi
+	vmovdqu		0x80-0x40($Htbl),$HK
+	sub		\$0x10,$len
+	jz		.Ltail_avx
+
+	vpunpckhqdq	$Ij,$Ij,$T1
+	vpxor		$Xlo,$Zlo,$Zlo
+	vpclmulqdq	\$0x00,$Hkey,$Ij,$Xlo
+	vpxor		$Ij,$T1,$T1
+	 vmovdqu	-0x60($inp),$Ii
+	vpxor		$Xhi,$Zhi,$Zhi
+	vpclmulqdq	\$0x11,$Hkey,$Ij,$Xhi
+	vmovdqu		0x70-0x40($Htbl),$Hkey	# $Hkey^6
+	 vpshufb	$bswap,$Ii,$Ij
+	vpxor		$Xmi,$Zmi,$Zmi
+	vpclmulqdq	\$0x00,$HK,$T1,$Xmi
+	vpsrldq		\$8,$HK,$HK
+	sub		\$0x10,$len
+	jz		.Ltail_avx
+
+	vpunpckhqdq	$Ij,$Ij,$T1
+	vpxor		$Xlo,$Zlo,$Zlo
+	vpclmulqdq	\$0x00,$Hkey,$Ij,$Xlo
+	vpxor		$Ij,$T1,$T1
+	 vmovdqu	-0x70($inp),$Ii
+	vpxor		$Xhi,$Zhi,$Zhi
+	vpclmulqdq	\$0x11,$Hkey,$Ij,$Xhi
+	vmovdqu		0x90-0x40($Htbl),$Hkey	# $Hkey^7
+	 vpshufb	$bswap,$Ii,$Ij
+	vpxor		$Xmi,$Zmi,$Zmi
+	vpclmulqdq	\$0x00,$HK,$T1,$Xmi
+	vmovq		0xb8-0x40($Htbl),$HK
+	sub		\$0x10,$len
+	jmp		.Ltail_avx
+
+.align	32
+.Ltail_avx:
+	vpxor		$Xi,$Ij,$Ij		# accumulate $Xi
+.Ltail_no_xor_avx:
+	vpunpckhqdq	$Ij,$Ij,$T1
+	vpxor		$Xlo,$Zlo,$Zlo
+	vpclmulqdq	\$0x00,$Hkey,$Ij,$Xlo
+	vpxor		$Ij,$T1,$T1
+	vpxor		$Xhi,$Zhi,$Zhi
+	vpclmulqdq	\$0x11,$Hkey,$Ij,$Xhi
+	vpxor		$Xmi,$Zmi,$Zmi
+	vpclmulqdq	\$0x00,$HK,$T1,$Xmi
+
+	vmovdqu		(%r10),$Tred
+
+	vpxor		$Xlo,$Zlo,$Xi
+	vpxor		$Xhi,$Zhi,$Xo
+	vpxor		$Xmi,$Zmi,$Zmi
+
+	vpxor		$Xi, $Zmi,$Zmi		# aggregated Karatsuba post-processing
+	vpxor		$Xo, $Zmi,$Zmi
+	vpslldq		\$8, $Zmi,$T2
+	vpsrldq		\$8, $Zmi,$Zmi
+	vpxor		$T2, $Xi, $Xi
+	vpxor		$Zmi,$Xo, $Xo
+
+	vpclmulqdq	\$0x10,$Tred,$Xi,$T2	# 1st phase
+	vpalignr	\$8,$Xi,$Xi,$Xi
+	vpxor		$T2,$Xi,$Xi
+
+	vpclmulqdq	\$0x10,$Tred,$Xi,$T2	# 2nd phase
+	vpalignr	\$8,$Xi,$Xi,$Xi
+	vpxor		$Xo,$Xi,$Xi
+	vpxor		$T2,$Xi,$Xi
+
+	cmp		\$0,$len
+	jne		.Lshort_avx
+
+	vpshufb		$bswap,$Xi,$Xi
+	vmovdqu		$Xi,($Xip)
+	vzeroupper
+___
+$code.=<<___ if ($win64);
+	movaps	(%rsp),%xmm6
+	movaps	0x10(%rsp),%xmm7
+	movaps	0x20(%rsp),%xmm8
+	movaps	0x30(%rsp),%xmm9
+	movaps	0x40(%rsp),%xmm10
+	movaps	0x50(%rsp),%xmm11
+	movaps	0x60(%rsp),%xmm12
+	movaps	0x70(%rsp),%xmm13
+	movaps	0x80(%rsp),%xmm14
+	movaps	0x90(%rsp),%xmm15
+	lea	0xa8(%rsp),%rsp
+.LSEH_end_gcm_ghash_avx:
+___
+$code.=<<___;
+	ret
+.size	gcm_ghash_avx,.-gcm_ghash_avx
+___
+} else {
+$code.=<<___;
+	jmp	.L_ghash_clmul
+.size	gcm_ghash_avx,.-gcm_ghash_avx
+___
+}
+
+$code.=<<___;
+.align	64
+.Lbswap_mask:
+	.byte	15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0
+.L0x1c2_polynomial:
+	.byte	1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0xc2
+.L7_mask:
+	.long	7,0,7,0
+.L7_mask_poly:
+	.long	7,0,`0xE1<<1`,0
+.align	64
+.type	.Lrem_4bit,\@object
+.Lrem_4bit:
+	.long	0,`0x0000<<16`,0,`0x1C20<<16`,0,`0x3840<<16`,0,`0x2460<<16`
+	.long	0,`0x7080<<16`,0,`0x6CA0<<16`,0,`0x48C0<<16`,0,`0x54E0<<16`
+	.long	0,`0xE100<<16`,0,`0xFD20<<16`,0,`0xD940<<16`,0,`0xC560<<16`
+	.long	0,`0x9180<<16`,0,`0x8DA0<<16`,0,`0xA9C0<<16`,0,`0xB5E0<<16`
+.type	.Lrem_8bit,\@object
+.Lrem_8bit:
+	.value	0x0000,0x01C2,0x0384,0x0246,0x0708,0x06CA,0x048C,0x054E
+	.value	0x0E10,0x0FD2,0x0D94,0x0C56,0x0918,0x08DA,0x0A9C,0x0B5E
+	.value	0x1C20,0x1DE2,0x1FA4,0x1E66,0x1B28,0x1AEA,0x18AC,0x196E
+	.value	0x1230,0x13F2,0x11B4,0x1076,0x1538,0x14FA,0x16BC,0x177E
+	.value	0x3840,0x3982,0x3BC4,0x3A06,0x3F48,0x3E8A,0x3CCC,0x3D0E
+	.value	0x3650,0x3792,0x35D4,0x3416,0x3158,0x309A,0x32DC,0x331E
+	.value	0x2460,0x25A2,0x27E4,0x2626,0x2368,0x22AA,0x20EC,0x212E
+	.value	0x2A70,0x2BB2,0x29F4,0x2836,0x2D78,0x2CBA,0x2EFC,0x2F3E
+	.value	0x7080,0x7142,0x7304,0x72C6,0x7788,0x764A,0x740C,0x75CE
+	.value	0x7E90,0x7F52,0x7D14,0x7CD6,0x7998,0x785A,0x7A1C,0x7BDE
+	.value	0x6CA0,0x6D62,0x6F24,0x6EE6,0x6BA8,0x6A6A,0x682C,0x69EE
+	.value	0x62B0,0x6372,0x6134,0x60F6,0x65B8,0x647A,0x663C,0x67FE
+	.value	0x48C0,0x4902,0x4B44,0x4A86,0x4FC8,0x4E0A,0x4C4C,0x4D8E
+	.value	0x46D0,0x4712,0x4554,0x4496,0x41D8,0x401A,0x425C,0x439E
+	.value	0x54E0,0x5522,0x5764,0x56A6,0x53E8,0x522A,0x506C,0x51AE
+	.value	0x5AF0,0x5B32,0x5974,0x58B6,0x5DF8,0x5C3A,0x5E7C,0x5FBE
+	.value	0xE100,0xE0C2,0xE284,0xE346,0xE608,0xE7CA,0xE58C,0xE44E
+	.value	0xEF10,0xEED2,0xEC94,0xED56,0xE818,0xE9DA,0xEB9C,0xEA5E
+	.value	0xFD20,0xFCE2,0xFEA4,0xFF66,0xFA28,0xFBEA,0xF9AC,0xF86E
+	.value	0xF330,0xF2F2,0xF0B4,0xF176,0xF438,0xF5FA,0xF7BC,0xF67E
+	.value	0xD940,0xD882,0xDAC4,0xDB06,0xDE48,0xDF8A,0xDDCC,0xDC0E
+	.value	0xD750,0xD692,0xD4D4,0xD516,0xD058,0xD19A,0xD3DC,0xD21E
+	.value	0xC560,0xC4A2,0xC6E4,0xC726,0xC268,0xC3AA,0xC1EC,0xC02E
+	.value	0xCB70,0xCAB2,0xC8F4,0xC936,0xCC78,0xCDBA,0xCFFC,0xCE3E
+	.value	0x9180,0x9042,0x9204,0x93C6,0x9688,0x974A,0x950C,0x94CE
+	.value	0x9F90,0x9E52,0x9C14,0x9DD6,0x9898,0x995A,0x9B1C,0x9ADE
+	.value	0x8DA0,0x8C62,0x8E24,0x8FE6,0x8AA8,0x8B6A,0x892C,0x88EE
+	.value	0x83B0,0x8272,0x8034,0x81F6,0x84B8,0x857A,0x873C,0x86FE
+	.value	0xA9C0,0xA802,0xAA44,0xAB86,0xAEC8,0xAF0A,0xAD4C,0xAC8E
+	.value	0xA7D0,0xA612,0xA454,0xA596,0xA0D8,0xA11A,0xA35C,0xA29E
+	.value	0xB5E0,0xB422,0xB664,0xB7A6,0xB2E8,0xB32A,0xB16C,0xB0AE
+	.value	0xBBF0,0xBA32,0xB874,0xB9B6,0xBCF8,0xBD3A,0xBF7C,0xBEBE
+
+.asciz	"GHASH for x86_64, CRYPTOGAMS by "
+.align	64
+___
+
+# EXCEPTION_DISPOSITION handler (EXCEPTION_RECORD *rec,ULONG64 frame,
+#		CONTEXT *context,DISPATCHER_CONTEXT *disp)
+if ($win64) {
+$rec="%rcx";
+$frame="%rdx";
+$context="%r8";
+$disp="%r9";
+
+$code.=<<___;
+.extern	__imp_RtlVirtualUnwind
+.type	se_handler,\@abi-omnipotent
+.align	16
+se_handler:
+	push	%rsi
+	push	%rdi
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+	pushfq
+	sub	\$64,%rsp
+
+	mov	120($context),%rax	# pull context->Rax
+	mov	248($context),%rbx	# pull context->Rip
+
+	mov	8($disp),%rsi		# disp->ImageBase
+	mov	56($disp),%r11		# disp->HandlerData
+
+	mov	0(%r11),%r10d		# HandlerData[0]
+	lea	(%rsi,%r10),%r10	# prologue label
+	cmp	%r10,%rbx		# context->RipRsp
+
+	mov	4(%r11),%r10d		# HandlerData[1]
+	lea	(%rsi,%r10),%r10	# epilogue label
+	cmp	%r10,%rbx		# context->Rip>=epilogue label
+	jae	.Lin_prologue
+
+	lea	24(%rax),%rax		# adjust "rsp"
+
+	mov	-8(%rax),%rbx
+	mov	-16(%rax),%rbp
+	mov	-24(%rax),%r12
+	mov	%rbx,144($context)	# restore context->Rbx
+	mov	%rbp,160($context)	# restore context->Rbp
+	mov	%r12,216($context)	# restore context->R12
+
+.Lin_prologue:
+	mov	8(%rax),%rdi
+	mov	16(%rax),%rsi
+	mov	%rax,152($context)	# restore context->Rsp
+	mov	%rsi,168($context)	# restore context->Rsi
+	mov	%rdi,176($context)	# restore context->Rdi
+
+	mov	40($disp),%rdi		# disp->ContextRecord
+	mov	$context,%rsi		# context
+	mov	\$`1232/8`,%ecx		# sizeof(CONTEXT)
+	.long	0xa548f3fc		# cld; rep movsq
+
+	mov	$disp,%rsi
+	xor	%rcx,%rcx		# arg1, UNW_FLAG_NHANDLER
+	mov	8(%rsi),%rdx		# arg2, disp->ImageBase
+	mov	0(%rsi),%r8		# arg3, disp->ControlPc
+	mov	16(%rsi),%r9		# arg4, disp->FunctionEntry
+	mov	40(%rsi),%r10		# disp->ContextRecord
+	lea	56(%rsi),%r11		# &disp->HandlerData
+	lea	24(%rsi),%r12		# &disp->EstablisherFrame
+	mov	%r10,32(%rsp)		# arg5
+	mov	%r11,40(%rsp)		# arg6
+	mov	%r12,48(%rsp)		# arg7
+	mov	%rcx,56(%rsp)		# arg8, (NULL)
+	call	*__imp_RtlVirtualUnwind(%rip)
+
+	mov	\$1,%eax		# ExceptionContinueSearch
+	add	\$64,%rsp
+	popfq
+	pop	%r15
+	pop	%r14
+	pop	%r13
+	pop	%r12
+	pop	%rbp
+	pop	%rbx
+	pop	%rdi
+	pop	%rsi
+	ret
+.size	se_handler,.-se_handler
+
+.section	.pdata
+.align	4
+	.rva	.LSEH_begin_gcm_gmult_4bit
+	.rva	.LSEH_end_gcm_gmult_4bit
+	.rva	.LSEH_info_gcm_gmult_4bit
+
+	.rva	.LSEH_begin_gcm_ghash_4bit
+	.rva	.LSEH_end_gcm_ghash_4bit
+	.rva	.LSEH_info_gcm_ghash_4bit
+
+	.rva	.LSEH_begin_gcm_init_clmul
+	.rva	.LSEH_end_gcm_init_clmul
+	.rva	.LSEH_info_gcm_init_clmul
+
+	.rva	.LSEH_begin_gcm_ghash_clmul
+	.rva	.LSEH_end_gcm_ghash_clmul
+	.rva	.LSEH_info_gcm_ghash_clmul
+___
+$code.=<<___	if ($avx);
+	.rva	.LSEH_begin_gcm_init_avx
+	.rva	.LSEH_end_gcm_init_avx
+	.rva	.LSEH_info_gcm_init_clmul
+
+	.rva	.LSEH_begin_gcm_ghash_avx
+	.rva	.LSEH_end_gcm_ghash_avx
+	.rva	.LSEH_info_gcm_ghash_clmul
+___
+$code.=<<___;
+.section	.xdata
+.align	8
+.LSEH_info_gcm_gmult_4bit:
+	.byte	9,0,0,0
+	.rva	se_handler
+	.rva	.Lgmult_prologue,.Lgmult_epilogue	# HandlerData
+.LSEH_info_gcm_ghash_4bit:
+	.byte	9,0,0,0
+	.rva	se_handler
+	.rva	.Lghash_prologue,.Lghash_epilogue	# HandlerData
+.LSEH_info_gcm_init_clmul:
+	.byte	0x01,0x08,0x03,0x00
+	.byte	0x08,0x68,0x00,0x00	#movaps	0x00(rsp),xmm6
+	.byte	0x04,0x22,0x00,0x00	#sub	rsp,0x18
+.LSEH_info_gcm_ghash_clmul:
+	.byte	0x01,0x33,0x16,0x00
+	.byte	0x33,0xf8,0x09,0x00	#movaps 0x90(rsp),xmm15
+	.byte	0x2e,0xe8,0x08,0x00	#movaps 0x80(rsp),xmm14
+	.byte	0x29,0xd8,0x07,0x00	#movaps 0x70(rsp),xmm13
+	.byte	0x24,0xc8,0x06,0x00	#movaps 0x60(rsp),xmm12
+	.byte	0x1f,0xb8,0x05,0x00	#movaps 0x50(rsp),xmm11
+	.byte	0x1a,0xa8,0x04,0x00	#movaps 0x40(rsp),xmm10
+	.byte	0x15,0x98,0x03,0x00	#movaps 0x30(rsp),xmm9
+	.byte	0x10,0x88,0x02,0x00	#movaps 0x20(rsp),xmm8
+	.byte	0x0c,0x78,0x01,0x00	#movaps 0x10(rsp),xmm7
+	.byte	0x08,0x68,0x00,0x00	#movaps 0x00(rsp),xmm6
+	.byte	0x04,0x01,0x15,0x00	#sub	rsp,0xa8
+___
+}
+
+$code =~ s/\`([^\`]*)\`/eval($1)/gem;
+
+print $code;
+
+close STDOUT;
diff --git a/crypto/modes/asm/ghashp8-ppc.pl b/crypto/modes/asm/ghashp8-ppc.pl
new file mode 100755
index 000000000..f0598cb28
--- /dev/null
+++ b/crypto/modes/asm/ghashp8-ppc.pl
@@ -0,0 +1,670 @@
+#! /usr/bin/env perl
+# Copyright 2014-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+#
+# ====================================================================
+# Written by Andy Polyakov  for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+#
+# GHASH for for PowerISA v2.07.
+#
+# July 2014
+#
+# Accurate performance measurements are problematic, because it's
+# always virtualized setup with possibly throttled processor.
+# Relative comparison is therefore more informative. This initial
+# version is ~2.1x slower than hardware-assisted AES-128-CTR, ~12x
+# faster than "4-bit" integer-only compiler-generated 64-bit code.
+# "Initial version" means that there is room for futher improvement.
+
+# May 2016
+#
+# 2x aggregated reduction improves performance by 50% (resulting
+# performance on POWER8 is 1 cycle per processed byte), and 4x
+# aggregated reduction - by 170% or 2.7x (resulting in 0.55 cpb).
+
+$flavour=shift;
+$output =shift;
+
+if ($flavour =~ /64/) {
+	$SIZE_T=8;
+	$LRSAVE=2*$SIZE_T;
+	$STU="stdu";
+	$POP="ld";
+	$PUSH="std";
+	$UCMP="cmpld";
+	$SHRI="srdi";
+} elsif ($flavour =~ /32/) {
+	$SIZE_T=4;
+	$LRSAVE=$SIZE_T;
+	$STU="stwu";
+	$POP="lwz";
+	$PUSH="stw";
+	$UCMP="cmplw";
+	$SHRI="srwi";
+} else { die "nonsense $flavour"; }
+
+$sp="r1";
+$FRAME=6*$SIZE_T+13*16;	# 13*16 is for v20-v31 offload
+
+$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+( $xlate="${dir}ppc-xlate.pl" and -f $xlate ) or
+( $xlate="${dir}../../perlasm/ppc-xlate.pl" and -f $xlate) or
+die "can't locate ppc-xlate.pl";
+
+open STDOUT,"| $^X $xlate $flavour $output" || die "can't call $xlate: $!";
+
+my ($Xip,$Htbl,$inp,$len)=map("r$_",(3..6));	# argument block
+
+my ($Xl,$Xm,$Xh,$IN)=map("v$_",(0..3));
+my ($zero,$t0,$t1,$t2,$xC2,$H,$Hh,$Hl,$lemask)=map("v$_",(4..12));
+my ($Xl1,$Xm1,$Xh1,$IN1,$H2,$H2h,$H2l)=map("v$_",(13..19));
+my $vrsave="r12";
+
+$code=<<___;
+.machine	"any"
+
+.text
+
+.globl	.gcm_init_p8
+.align	5
+.gcm_init_p8:
+	li		r0,-4096
+	li		r8,0x10
+	mfspr		$vrsave,256
+	li		r9,0x20
+	mtspr		256,r0
+	li		r10,0x30
+	lvx_u		$H,0,r4			# load H
+
+	vspltisb	$xC2,-16		# 0xf0
+	vspltisb	$t0,1			# one
+	vaddubm		$xC2,$xC2,$xC2		# 0xe0
+	vxor		$zero,$zero,$zero
+	vor		$xC2,$xC2,$t0		# 0xe1
+	vsldoi		$xC2,$xC2,$zero,15	# 0xe1...
+	vsldoi		$t1,$zero,$t0,1		# ...1
+	vaddubm		$xC2,$xC2,$xC2		# 0xc2...
+	vspltisb	$t2,7
+	vor		$xC2,$xC2,$t1		# 0xc2....01
+	vspltb		$t1,$H,0		# most significant byte
+	vsl		$H,$H,$t0		# H<<=1
+	vsrab		$t1,$t1,$t2		# broadcast carry bit
+	vand		$t1,$t1,$xC2
+	vxor		$IN,$H,$t1		# twisted H
+
+	vsldoi		$H,$IN,$IN,8		# twist even more ...
+	vsldoi		$xC2,$zero,$xC2,8	# 0xc2.0
+	vsldoi		$Hl,$zero,$H,8		# ... and split
+	vsldoi		$Hh,$H,$zero,8
+
+	stvx_u		$xC2,0,r3		# save pre-computed table
+	stvx_u		$Hl,r8,r3
+	li		r8,0x40
+	stvx_u		$H, r9,r3
+	li		r9,0x50
+	stvx_u		$Hh,r10,r3
+	li		r10,0x60
+
+	vpmsumd		$Xl,$IN,$Hl		# H.lo·H.lo
+	vpmsumd		$Xm,$IN,$H		# H.hi·H.lo+H.lo·H.hi
+	vpmsumd		$Xh,$IN,$Hh		# H.hi·H.hi
+
+	vpmsumd		$t2,$Xl,$xC2		# 1st reduction phase
+
+	vsldoi		$t0,$Xm,$zero,8
+	vsldoi		$t1,$zero,$Xm,8
+	vxor		$Xl,$Xl,$t0
+	vxor		$Xh,$Xh,$t1
+
+	vsldoi		$Xl,$Xl,$Xl,8
+	vxor		$Xl,$Xl,$t2
+
+	vsldoi		$t1,$Xl,$Xl,8		# 2nd reduction phase
+	vpmsumd		$Xl,$Xl,$xC2
+	vxor		$t1,$t1,$Xh
+	vxor		$IN1,$Xl,$t1
+
+	vsldoi		$H2,$IN1,$IN1,8
+	vsldoi		$H2l,$zero,$H2,8
+	vsldoi		$H2h,$H2,$zero,8
+
+	stvx_u		$H2l,r8,r3		# save H^2
+	li		r8,0x70
+	stvx_u		$H2,r9,r3
+	li		r9,0x80
+	stvx_u		$H2h,r10,r3
+	li		r10,0x90
+___
+{
+my ($t4,$t5,$t6) = ($Hl,$H,$Hh);
+$code.=<<___;
+	vpmsumd		$Xl,$IN,$H2l		# H.lo·H^2.lo
+	 vpmsumd	$Xl1,$IN1,$H2l		# H^2.lo·H^2.lo
+	vpmsumd		$Xm,$IN,$H2		# H.hi·H^2.lo+H.lo·H^2.hi
+	 vpmsumd	$Xm1,$IN1,$H2		# H^2.hi·H^2.lo+H^2.lo·H^2.hi
+	vpmsumd		$Xh,$IN,$H2h		# H.hi·H^2.hi
+	 vpmsumd	$Xh1,$IN1,$H2h		# H^2.hi·H^2.hi
+
+	vpmsumd		$t2,$Xl,$xC2		# 1st reduction phase
+	 vpmsumd	$t6,$Xl1,$xC2		# 1st reduction phase
+
+	vsldoi		$t0,$Xm,$zero,8
+	vsldoi		$t1,$zero,$Xm,8
+	 vsldoi		$t4,$Xm1,$zero,8
+	 vsldoi		$t5,$zero,$Xm1,8
+	vxor		$Xl,$Xl,$t0
+	vxor		$Xh,$Xh,$t1
+	 vxor		$Xl1,$Xl1,$t4
+	 vxor		$Xh1,$Xh1,$t5
+
+	vsldoi		$Xl,$Xl,$Xl,8
+	 vsldoi		$Xl1,$Xl1,$Xl1,8
+	vxor		$Xl,$Xl,$t2
+	 vxor		$Xl1,$Xl1,$t6
+
+	vsldoi		$t1,$Xl,$Xl,8		# 2nd reduction phase
+	 vsldoi		$t5,$Xl1,$Xl1,8		# 2nd reduction phase
+	vpmsumd		$Xl,$Xl,$xC2
+	 vpmsumd	$Xl1,$Xl1,$xC2
+	vxor		$t1,$t1,$Xh
+	 vxor		$t5,$t5,$Xh1
+	vxor		$Xl,$Xl,$t1
+	 vxor		$Xl1,$Xl1,$t5
+
+	vsldoi		$H,$Xl,$Xl,8
+	 vsldoi		$H2,$Xl1,$Xl1,8
+	vsldoi		$Hl,$zero,$H,8
+	vsldoi		$Hh,$H,$zero,8
+	 vsldoi		$H2l,$zero,$H2,8
+	 vsldoi		$H2h,$H2,$zero,8
+
+	stvx_u		$Hl,r8,r3		# save H^3
+	li		r8,0xa0
+	stvx_u		$H,r9,r3
+	li		r9,0xb0
+	stvx_u		$Hh,r10,r3
+	li		r10,0xc0
+	 stvx_u		$H2l,r8,r3		# save H^4
+	 stvx_u		$H2,r9,r3
+	 stvx_u		$H2h,r10,r3
+
+	mtspr		256,$vrsave
+	blr
+	.long		0
+	.byte		0,12,0x14,0,0,0,2,0
+	.long		0
+.size	.gcm_init_p8,.-.gcm_init_p8
+___
+}
+$code.=<<___;
+.globl	.gcm_gmult_p8
+.align	5
+.gcm_gmult_p8:
+	lis		r0,0xfff8
+	li		r8,0x10
+	mfspr		$vrsave,256
+	li		r9,0x20
+	mtspr		256,r0
+	li		r10,0x30
+	lvx_u		$IN,0,$Xip		# load Xi
+
+	lvx_u		$Hl,r8,$Htbl		# load pre-computed table
+	 le?lvsl	$lemask,r0,r0
+	lvx_u		$H, r9,$Htbl
+	 le?vspltisb	$t0,0x07
+	lvx_u		$Hh,r10,$Htbl
+	 le?vxor	$lemask,$lemask,$t0
+	lvx_u		$xC2,0,$Htbl
+	 le?vperm	$IN,$IN,$IN,$lemask
+	vxor		$zero,$zero,$zero
+
+	vpmsumd		$Xl,$IN,$Hl		# H.lo·Xi.lo
+	vpmsumd		$Xm,$IN,$H		# H.hi·Xi.lo+H.lo·Xi.hi
+	vpmsumd		$Xh,$IN,$Hh		# H.hi·Xi.hi
+
+	vpmsumd		$t2,$Xl,$xC2		# 1st reduction phase
+
+	vsldoi		$t0,$Xm,$zero,8
+	vsldoi		$t1,$zero,$Xm,8
+	vxor		$Xl,$Xl,$t0
+	vxor		$Xh,$Xh,$t1
+
+	vsldoi		$Xl,$Xl,$Xl,8
+	vxor		$Xl,$Xl,$t2
+
+	vsldoi		$t1,$Xl,$Xl,8		# 2nd reduction phase
+	vpmsumd		$Xl,$Xl,$xC2
+	vxor		$t1,$t1,$Xh
+	vxor		$Xl,$Xl,$t1
+
+	le?vperm	$Xl,$Xl,$Xl,$lemask
+	stvx_u		$Xl,0,$Xip		# write out Xi
+
+	mtspr		256,$vrsave
+	blr
+	.long		0
+	.byte		0,12,0x14,0,0,0,2,0
+	.long		0
+.size	.gcm_gmult_p8,.-.gcm_gmult_p8
+
+.globl	.gcm_ghash_p8
+.align	5
+.gcm_ghash_p8:
+	li		r0,-4096
+	li		r8,0x10
+	mfspr		$vrsave,256
+	li		r9,0x20
+	mtspr		256,r0
+	li		r10,0x30
+	lvx_u		$Xl,0,$Xip		# load Xi
+
+	lvx_u		$Hl,r8,$Htbl		# load pre-computed table
+	li		r8,0x40
+	 le?lvsl	$lemask,r0,r0
+	lvx_u		$H, r9,$Htbl
+	li		r9,0x50
+	 le?vspltisb	$t0,0x07
+	lvx_u		$Hh,r10,$Htbl
+	li		r10,0x60
+	 le?vxor	$lemask,$lemask,$t0
+	lvx_u		$xC2,0,$Htbl
+	 le?vperm	$Xl,$Xl,$Xl,$lemask
+	vxor		$zero,$zero,$zero
+
+	${UCMP}i	$len,64
+	bge		Lgcm_ghash_p8_4x
+
+	lvx_u		$IN,0,$inp
+	addi		$inp,$inp,16
+	subic.		$len,$len,16
+	 le?vperm	$IN,$IN,$IN,$lemask
+	vxor		$IN,$IN,$Xl
+	beq		Lshort
+
+	lvx_u		$H2l,r8,$Htbl		# load H^2
+	li		r8,16
+	lvx_u		$H2, r9,$Htbl
+	add		r9,$inp,$len		# end of input
+	lvx_u		$H2h,r10,$Htbl
+	be?b		Loop_2x
+
+.align	5
+Loop_2x:
+	lvx_u		$IN1,0,$inp
+	le?vperm	$IN1,$IN1,$IN1,$lemask
+
+	 subic		$len,$len,32
+	vpmsumd		$Xl,$IN,$H2l		# H^2.lo·Xi.lo
+	 vpmsumd	$Xl1,$IN1,$Hl		# H.lo·Xi+1.lo
+	 subfe		r0,r0,r0		# borrow?-1:0
+	vpmsumd		$Xm,$IN,$H2		# H^2.hi·Xi.lo+H^2.lo·Xi.hi
+	 vpmsumd	$Xm1,$IN1,$H		# H.hi·Xi+1.lo+H.lo·Xi+1.hi
+	 and		r0,r0,$len
+	vpmsumd		$Xh,$IN,$H2h		# H^2.hi·Xi.hi
+	 vpmsumd	$Xh1,$IN1,$Hh		# H.hi·Xi+1.hi
+	 add		$inp,$inp,r0
+
+	vxor		$Xl,$Xl,$Xl1
+	vxor		$Xm,$Xm,$Xm1
+
+	vpmsumd		$t2,$Xl,$xC2		# 1st reduction phase
+
+	vsldoi		$t0,$Xm,$zero,8
+	vsldoi		$t1,$zero,$Xm,8
+	 vxor		$Xh,$Xh,$Xh1
+	vxor		$Xl,$Xl,$t0
+	vxor		$Xh,$Xh,$t1
+
+	vsldoi		$Xl,$Xl,$Xl,8
+	vxor		$Xl,$Xl,$t2
+	 lvx_u		$IN,r8,$inp
+	 addi		$inp,$inp,32
+
+	vsldoi		$t1,$Xl,$Xl,8		# 2nd reduction phase
+	vpmsumd		$Xl,$Xl,$xC2
+	 le?vperm	$IN,$IN,$IN,$lemask
+	vxor		$t1,$t1,$Xh
+	vxor		$IN,$IN,$t1
+	vxor		$IN,$IN,$Xl
+	$UCMP		r9,$inp
+	bgt		Loop_2x			# done yet?
+
+	cmplwi		$len,0
+	bne		Leven
+
+Lshort:
+	vpmsumd		$Xl,$IN,$Hl		# H.lo·Xi.lo
+	vpmsumd		$Xm,$IN,$H		# H.hi·Xi.lo+H.lo·Xi.hi
+	vpmsumd		$Xh,$IN,$Hh		# H.hi·Xi.hi
+
+	vpmsumd		$t2,$Xl,$xC2		# 1st reduction phase
+
+	vsldoi		$t0,$Xm,$zero,8
+	vsldoi		$t1,$zero,$Xm,8
+	vxor		$Xl,$Xl,$t0
+	vxor		$Xh,$Xh,$t1
+
+	vsldoi		$Xl,$Xl,$Xl,8
+	vxor		$Xl,$Xl,$t2
+
+	vsldoi		$t1,$Xl,$Xl,8		# 2nd reduction phase
+	vpmsumd		$Xl,$Xl,$xC2
+	vxor		$t1,$t1,$Xh
+
+Leven:
+	vxor		$Xl,$Xl,$t1
+	le?vperm	$Xl,$Xl,$Xl,$lemask
+	stvx_u		$Xl,0,$Xip		# write out Xi
+
+	mtspr		256,$vrsave
+	blr
+	.long		0
+	.byte		0,12,0x14,0,0,0,4,0
+	.long		0
+___
+{
+my ($Xl3,$Xm2,$IN2,$H3l,$H3,$H3h,
+    $Xh3,$Xm3,$IN3,$H4l,$H4,$H4h) = map("v$_",(20..31));
+my $IN0=$IN;
+my ($H21l,$H21h,$loperm,$hiperm) = ($Hl,$Hh,$H2l,$H2h);
+
+$code.=<<___;
+.align	5
+.gcm_ghash_p8_4x:
+Lgcm_ghash_p8_4x:
+	$STU		$sp,-$FRAME($sp)
+	li		r10,`15+6*$SIZE_T`
+	li		r11,`31+6*$SIZE_T`
+	stvx		v20,r10,$sp
+	addi		r10,r10,32
+	stvx		v21,r11,$sp
+	addi		r11,r11,32
+	stvx		v22,r10,$sp
+	addi		r10,r10,32
+	stvx		v23,r11,$sp
+	addi		r11,r11,32
+	stvx		v24,r10,$sp
+	addi		r10,r10,32
+	stvx		v25,r11,$sp
+	addi		r11,r11,32
+	stvx		v26,r10,$sp
+	addi		r10,r10,32
+	stvx		v27,r11,$sp
+	addi		r11,r11,32
+	stvx		v28,r10,$sp
+	addi		r10,r10,32
+	stvx		v29,r11,$sp
+	addi		r11,r11,32
+	stvx		v30,r10,$sp
+	li		r10,0x60
+	stvx		v31,r11,$sp
+	li		r0,-1
+	stw		$vrsave,`$FRAME-4`($sp)	# save vrsave
+	mtspr		256,r0			# preserve all AltiVec registers
+
+	lvsl		$t0,0,r8		# 0x0001..0e0f
+	#lvx_u		$H2l,r8,$Htbl		# load H^2
+	li		r8,0x70
+	lvx_u		$H2, r9,$Htbl
+	li		r9,0x80
+	vspltisb	$t1,8			# 0x0808..0808
+	#lvx_u		$H2h,r10,$Htbl
+	li		r10,0x90
+	lvx_u		$H3l,r8,$Htbl		# load H^3
+	li		r8,0xa0
+	lvx_u		$H3, r9,$Htbl
+	li		r9,0xb0
+	lvx_u		$H3h,r10,$Htbl
+	li		r10,0xc0
+	lvx_u		$H4l,r8,$Htbl		# load H^4
+	li		r8,0x10
+	lvx_u		$H4, r9,$Htbl
+	li		r9,0x20
+	lvx_u		$H4h,r10,$Htbl
+	li		r10,0x30
+
+	vsldoi		$t2,$zero,$t1,8		# 0x0000..0808
+	vaddubm		$hiperm,$t0,$t2		# 0x0001..1617
+	vaddubm		$loperm,$t1,$hiperm	# 0x0809..1e1f
+
+	$SHRI		$len,$len,4		# this allows to use sign bit
+						# as carry
+	lvx_u		$IN0,0,$inp		# load input
+	lvx_u		$IN1,r8,$inp
+	subic.		$len,$len,8
+	lvx_u		$IN2,r9,$inp
+	lvx_u		$IN3,r10,$inp
+	addi		$inp,$inp,0x40
+	le?vperm	$IN0,$IN0,$IN0,$lemask
+	le?vperm	$IN1,$IN1,$IN1,$lemask
+	le?vperm	$IN2,$IN2,$IN2,$lemask
+	le?vperm	$IN3,$IN3,$IN3,$lemask
+
+	vxor		$Xh,$IN0,$Xl
+
+	 vpmsumd	$Xl1,$IN1,$H3l
+	 vpmsumd	$Xm1,$IN1,$H3
+	 vpmsumd	$Xh1,$IN1,$H3h
+
+	 vperm		$H21l,$H2,$H,$hiperm
+	 vperm		$t0,$IN2,$IN3,$loperm
+	 vperm		$H21h,$H2,$H,$loperm
+	 vperm		$t1,$IN2,$IN3,$hiperm
+	 vpmsumd	$Xm2,$IN2,$H2		# H^2.lo·Xi+2.hi+H^2.hi·Xi+2.lo
+	 vpmsumd	$Xl3,$t0,$H21l		# H^2.lo·Xi+2.lo+H.lo·Xi+3.lo
+	 vpmsumd	$Xm3,$IN3,$H		# H.hi·Xi+3.lo  +H.lo·Xi+3.hi
+	 vpmsumd	$Xh3,$t1,$H21h		# H^2.hi·Xi+2.hi+H.hi·Xi+3.hi
+
+	 vxor		$Xm2,$Xm2,$Xm1
+	 vxor		$Xl3,$Xl3,$Xl1
+	 vxor		$Xm3,$Xm3,$Xm2
+	 vxor		$Xh3,$Xh3,$Xh1
+
+	blt		Ltail_4x
+
+Loop_4x:
+	lvx_u		$IN0,0,$inp
+	lvx_u		$IN1,r8,$inp
+	subic.		$len,$len,4
+	lvx_u		$IN2,r9,$inp
+	lvx_u		$IN3,r10,$inp
+	addi		$inp,$inp,0x40
+	le?vperm	$IN1,$IN1,$IN1,$lemask
+	le?vperm	$IN2,$IN2,$IN2,$lemask
+	le?vperm	$IN3,$IN3,$IN3,$lemask
+	le?vperm	$IN0,$IN0,$IN0,$lemask
+
+	vpmsumd		$Xl,$Xh,$H4l		# H^4.lo·Xi.lo
+	vpmsumd		$Xm,$Xh,$H4		# H^4.hi·Xi.lo+H^4.lo·Xi.hi
+	vpmsumd		$Xh,$Xh,$H4h		# H^4.hi·Xi.hi
+	 vpmsumd	$Xl1,$IN1,$H3l
+	 vpmsumd	$Xm1,$IN1,$H3
+	 vpmsumd	$Xh1,$IN1,$H3h
+
+	vxor		$Xl,$Xl,$Xl3
+	vxor		$Xm,$Xm,$Xm3
+	vxor		$Xh,$Xh,$Xh3
+	 vperm		$t0,$IN2,$IN3,$loperm
+	 vperm		$t1,$IN2,$IN3,$hiperm
+
+	vpmsumd		$t2,$Xl,$xC2		# 1st reduction phase
+	 vpmsumd	$Xl3,$t0,$H21l		# H.lo·Xi+3.lo  +H^2.lo·Xi+2.lo
+	 vpmsumd	$Xh3,$t1,$H21h		# H.hi·Xi+3.hi  +H^2.hi·Xi+2.hi
+
+	vsldoi		$t0,$Xm,$zero,8
+	vsldoi		$t1,$zero,$Xm,8
+	vxor		$Xl,$Xl,$t0
+	vxor		$Xh,$Xh,$t1
+
+	vsldoi		$Xl,$Xl,$Xl,8
+	vxor		$Xl,$Xl,$t2
+
+	vsldoi		$t1,$Xl,$Xl,8		# 2nd reduction phase
+	 vpmsumd	$Xm2,$IN2,$H2		# H^2.hi·Xi+2.lo+H^2.lo·Xi+2.hi
+	 vpmsumd	$Xm3,$IN3,$H		# H.hi·Xi+3.lo  +H.lo·Xi+3.hi
+	vpmsumd		$Xl,$Xl,$xC2
+
+	 vxor		$Xl3,$Xl3,$Xl1
+	 vxor		$Xh3,$Xh3,$Xh1
+	vxor		$Xh,$Xh,$IN0
+	 vxor		$Xm2,$Xm2,$Xm1
+	vxor		$Xh,$Xh,$t1
+	 vxor		$Xm3,$Xm3,$Xm2
+	vxor		$Xh,$Xh,$Xl
+	bge		Loop_4x
+
+Ltail_4x:
+	vpmsumd		$Xl,$Xh,$H4l		# H^4.lo·Xi.lo
+	vpmsumd		$Xm,$Xh,$H4		# H^4.hi·Xi.lo+H^4.lo·Xi.hi
+	vpmsumd		$Xh,$Xh,$H4h		# H^4.hi·Xi.hi
+
+	vxor		$Xl,$Xl,$Xl3
+	vxor		$Xm,$Xm,$Xm3
+
+	vpmsumd		$t2,$Xl,$xC2		# 1st reduction phase
+
+	vsldoi		$t0,$Xm,$zero,8
+	vsldoi		$t1,$zero,$Xm,8
+	 vxor		$Xh,$Xh,$Xh3
+	vxor		$Xl,$Xl,$t0
+	vxor		$Xh,$Xh,$t1
+
+	vsldoi		$Xl,$Xl,$Xl,8
+	vxor		$Xl,$Xl,$t2
+
+	vsldoi		$t1,$Xl,$Xl,8		# 2nd reduction phase
+	vpmsumd		$Xl,$Xl,$xC2
+	vxor		$t1,$t1,$Xh
+	vxor		$Xl,$Xl,$t1
+
+	addic.		$len,$len,4
+	beq		Ldone_4x
+
+	lvx_u		$IN0,0,$inp
+	${UCMP}i	$len,2
+	li		$len,-4
+	blt		Lone
+	lvx_u		$IN1,r8,$inp
+	beq		Ltwo
+
+Lthree:
+	lvx_u		$IN2,r9,$inp
+	le?vperm	$IN0,$IN0,$IN0,$lemask
+	le?vperm	$IN1,$IN1,$IN1,$lemask
+	le?vperm	$IN2,$IN2,$IN2,$lemask
+
+	vxor		$Xh,$IN0,$Xl
+	vmr		$H4l,$H3l
+	vmr		$H4, $H3
+	vmr		$H4h,$H3h
+
+	vperm		$t0,$IN1,$IN2,$loperm
+	vperm		$t1,$IN1,$IN2,$hiperm
+	vpmsumd		$Xm2,$IN1,$H2		# H^2.lo·Xi+1.hi+H^2.hi·Xi+1.lo
+	vpmsumd		$Xm3,$IN2,$H		# H.hi·Xi+2.lo  +H.lo·Xi+2.hi
+	vpmsumd		$Xl3,$t0,$H21l		# H^2.lo·Xi+1.lo+H.lo·Xi+2.lo
+	vpmsumd		$Xh3,$t1,$H21h		# H^2.hi·Xi+1.hi+H.hi·Xi+2.hi
+
+	vxor		$Xm3,$Xm3,$Xm2
+	b		Ltail_4x
+
+.align	4
+Ltwo:
+	le?vperm	$IN0,$IN0,$IN0,$lemask
+	le?vperm	$IN1,$IN1,$IN1,$lemask
+
+	vxor		$Xh,$IN0,$Xl
+	vperm		$t0,$zero,$IN1,$loperm
+	vperm		$t1,$zero,$IN1,$hiperm
+
+	vsldoi		$H4l,$zero,$H2,8
+	vmr		$H4, $H2
+	vsldoi		$H4h,$H2,$zero,8
+
+	vpmsumd		$Xl3,$t0, $H21l		# H.lo·Xi+1.lo
+	vpmsumd		$Xm3,$IN1,$H		# H.hi·Xi+1.lo+H.lo·Xi+2.hi
+	vpmsumd		$Xh3,$t1, $H21h		# H.hi·Xi+1.hi
+
+	b		Ltail_4x
+
+.align	4
+Lone:
+	le?vperm	$IN0,$IN0,$IN0,$lemask
+
+	vsldoi		$H4l,$zero,$H,8
+	vmr		$H4, $H
+	vsldoi		$H4h,$H,$zero,8
+
+	vxor		$Xh,$IN0,$Xl
+	vxor		$Xl3,$Xl3,$Xl3
+	vxor		$Xm3,$Xm3,$Xm3
+	vxor		$Xh3,$Xh3,$Xh3
+
+	b		Ltail_4x
+
+Ldone_4x:
+	le?vperm	$Xl,$Xl,$Xl,$lemask
+	stvx_u		$Xl,0,$Xip		# write out Xi
+
+	li		r10,`15+6*$SIZE_T`
+	li		r11,`31+6*$SIZE_T`
+	mtspr		256,$vrsave
+	lvx		v20,r10,$sp
+	addi		r10,r10,32
+	lvx		v21,r11,$sp
+	addi		r11,r11,32
+	lvx		v22,r10,$sp
+	addi		r10,r10,32
+	lvx		v23,r11,$sp
+	addi		r11,r11,32
+	lvx		v24,r10,$sp
+	addi		r10,r10,32
+	lvx		v25,r11,$sp
+	addi		r11,r11,32
+	lvx		v26,r10,$sp
+	addi		r10,r10,32
+	lvx		v27,r11,$sp
+	addi		r11,r11,32
+	lvx		v28,r10,$sp
+	addi		r10,r10,32
+	lvx		v29,r11,$sp
+	addi		r11,r11,32
+	lvx		v30,r10,$sp
+	lvx		v31,r11,$sp
+	addi		$sp,$sp,$FRAME
+	blr
+	.long		0
+	.byte		0,12,0x04,0,0x80,0,4,0
+	.long		0
+___
+}
+$code.=<<___;
+.size	.gcm_ghash_p8,.-.gcm_ghash_p8
+
+.asciz  "GHASH for PowerISA 2.07, CRYPTOGAMS by "
+.align  2
+___
+
+foreach (split("\n",$code)) {
+	s/\`([^\`]*)\`/eval $1/geo;
+
+	if ($flavour =~ /le$/o) {	# little-endian
+	    s/le\?//o		or
+	    s/be\?/#be#/o;
+	} else {
+	    s/le\?/#le#/o	or
+	    s/be\?//o;
+	}
+	print $_,"\n";
+}
+
+close STDOUT; # enforce flush
diff --git a/crypto/modes/asm/ghashv8-armx.pl b/crypto/modes/asm/ghashv8-armx.pl
new file mode 100644
index 000000000..cb4537b22
--- /dev/null
+++ b/crypto/modes/asm/ghashv8-armx.pl
@@ -0,0 +1,426 @@
+#! /usr/bin/env perl
+# Copyright 2014-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+#
+# ====================================================================
+# Written by Andy Polyakov  for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+#
+# GHASH for ARMv8 Crypto Extension, 64-bit polynomial multiplication.
+#
+# June 2014
+#
+# Initial version was developed in tight cooperation with Ard
+# Biesheuvel  from bits-n-pieces from
+# other assembly modules. Just like aesv8-armx.pl this module
+# supports both AArch32 and AArch64 execution modes.
+#
+# July 2014
+#
+# Implement 2x aggregated reduction [see ghash-x86.pl for background
+# information].
+#
+# Current performance in cycles per processed byte:
+#
+#		PMULL[2]	32-bit NEON(*)
+# Apple A7	0.92		5.62
+# Cortex-A53	1.01		8.39
+# Cortex-A57	1.17		7.61
+# Denver	0.71		6.02
+# Mongoose	1.10		8.06
+#
+# (*)	presented for reference/comparison purposes;
+
+$flavour = shift;
+$output  = shift;
+
+$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+( $xlate="${dir}arm-xlate.pl" and -f $xlate ) or
+( $xlate="${dir}../../perlasm/arm-xlate.pl" and -f $xlate) or
+die "can't locate arm-xlate.pl";
+
+open OUT,"| \"$^X\" $xlate $flavour $output";
+*STDOUT=*OUT;
+
+$Xi="x0";	# argument block
+$Htbl="x1";
+$inp="x2";
+$len="x3";
+
+$inc="x12";
+
+{
+my ($Xl,$Xm,$Xh,$IN)=map("q$_",(0..3));
+my ($t0,$t1,$t2,$xC2,$H,$Hhl,$H2)=map("q$_",(8..14));
+
+$code=<<___;
+#include "arm_arch.h"
+
+.text
+___
+$code.=".arch	armv8-a+crypto\n"	if ($flavour =~ /64/);
+$code.=".fpu	neon\n.code	32\n"	if ($flavour !~ /64/);
+
+################################################################################
+# void gcm_init_v8(u128 Htable[16],const u64 H[2]);
+#
+# input:	128-bit H - secret parameter E(K,0^128)
+# output:	precomputed table filled with degrees of twisted H;
+#		H is twisted to handle reverse bitness of GHASH;
+#		only few of 16 slots of Htable[16] are used;
+#		data is opaque to outside world (which allows to
+#		optimize the code independently);
+#
+$code.=<<___;
+.global	gcm_init_v8
+.type	gcm_init_v8,%function
+.align	4
+gcm_init_v8:
+	vld1.64		{$t1},[x1]		@ load input H
+	vmov.i8		$xC2,#0xe1
+	vshl.i64	$xC2,$xC2,#57		@ 0xc2.0
+	vext.8		$IN,$t1,$t1,#8
+	vshr.u64	$t2,$xC2,#63
+	vdup.32		$t1,${t1}[1]
+	vext.8		$t0,$t2,$xC2,#8		@ t0=0xc2....01
+	vshr.u64	$t2,$IN,#63
+	vshr.s32	$t1,$t1,#31		@ broadcast carry bit
+	vand		$t2,$t2,$t0
+	vshl.i64	$IN,$IN,#1
+	vext.8		$t2,$t2,$t2,#8
+	vand		$t0,$t0,$t1
+	vorr		$IN,$IN,$t2		@ H<<<=1
+	veor		$H,$IN,$t0		@ twisted H
+	vst1.64		{$H},[x0],#16		@ store Htable[0]
+
+	@ calculate H^2
+	vext.8		$t0,$H,$H,#8		@ Karatsuba pre-processing
+	vpmull.p64	$Xl,$H,$H
+	veor		$t0,$t0,$H
+	vpmull2.p64	$Xh,$H,$H
+	vpmull.p64	$Xm,$t0,$t0
+
+	vext.8		$t1,$Xl,$Xh,#8		@ Karatsuba post-processing
+	veor		$t2,$Xl,$Xh
+	veor		$Xm,$Xm,$t1
+	veor		$Xm,$Xm,$t2
+	vpmull.p64	$t2,$Xl,$xC2		@ 1st phase
+
+	vmov		$Xh#lo,$Xm#hi		@ Xh|Xm - 256-bit result
+	vmov		$Xm#hi,$Xl#lo		@ Xm is rotated Xl
+	veor		$Xl,$Xm,$t2
+
+	vext.8		$t2,$Xl,$Xl,#8		@ 2nd phase
+	vpmull.p64	$Xl,$Xl,$xC2
+	veor		$t2,$t2,$Xh
+	veor		$H2,$Xl,$t2
+
+	vext.8		$t1,$H2,$H2,#8		@ Karatsuba pre-processing
+	veor		$t1,$t1,$H2
+	vext.8		$Hhl,$t0,$t1,#8		@ pack Karatsuba pre-processed
+	vst1.64		{$Hhl-$H2},[x0]		@ store Htable[1..2]
+
+	ret
+.size	gcm_init_v8,.-gcm_init_v8
+___
+################################################################################
+# void gcm_gmult_v8(u64 Xi[2],const u128 Htable[16]);
+#
+# input:	Xi - current hash value;
+#		Htable - table precomputed in gcm_init_v8;
+# output:	Xi - next hash value Xi;
+#
+$code.=<<___;
+.global	gcm_gmult_v8
+.type	gcm_gmult_v8,%function
+.align	4
+gcm_gmult_v8:
+	vld1.64		{$t1},[$Xi]		@ load Xi
+	vmov.i8		$xC2,#0xe1
+	vld1.64		{$H-$Hhl},[$Htbl]	@ load twisted H, ...
+	vshl.u64	$xC2,$xC2,#57
+#ifndef __ARMEB__
+	vrev64.8	$t1,$t1
+#endif
+	vext.8		$IN,$t1,$t1,#8
+
+	vpmull.p64	$Xl,$H,$IN		@ H.lo·Xi.lo
+	veor		$t1,$t1,$IN		@ Karatsuba pre-processing
+	vpmull2.p64	$Xh,$H,$IN		@ H.hi·Xi.hi
+	vpmull.p64	$Xm,$Hhl,$t1		@ (H.lo+H.hi)·(Xi.lo+Xi.hi)
+
+	vext.8		$t1,$Xl,$Xh,#8		@ Karatsuba post-processing
+	veor		$t2,$Xl,$Xh
+	veor		$Xm,$Xm,$t1
+	veor		$Xm,$Xm,$t2
+	vpmull.p64	$t2,$Xl,$xC2		@ 1st phase of reduction
+
+	vmov		$Xh#lo,$Xm#hi		@ Xh|Xm - 256-bit result
+	vmov		$Xm#hi,$Xl#lo		@ Xm is rotated Xl
+	veor		$Xl,$Xm,$t2
+
+	vext.8		$t2,$Xl,$Xl,#8		@ 2nd phase of reduction
+	vpmull.p64	$Xl,$Xl,$xC2
+	veor		$t2,$t2,$Xh
+	veor		$Xl,$Xl,$t2
+
+#ifndef __ARMEB__
+	vrev64.8	$Xl,$Xl
+#endif
+	vext.8		$Xl,$Xl,$Xl,#8
+	vst1.64		{$Xl},[$Xi]		@ write out Xi
+
+	ret
+.size	gcm_gmult_v8,.-gcm_gmult_v8
+___
+################################################################################
+# void gcm_ghash_v8(u64 Xi[2],const u128 Htable[16],const u8 *inp,size_t len);
+#
+# input:	table precomputed in gcm_init_v8;
+#		current hash value Xi;
+#		pointer to input data;
+#		length of input data in bytes, but divisible by block size;
+# output:	next hash value Xi;
+#
+$code.=<<___;
+.global	gcm_ghash_v8
+.type	gcm_ghash_v8,%function
+.align	4
+gcm_ghash_v8:
+___
+$code.=<<___		if ($flavour !~ /64/);
+	vstmdb		sp!,{d8-d15}		@ 32-bit ABI says so
+___
+$code.=<<___;
+	vld1.64		{$Xl},[$Xi]		@ load [rotated] Xi
+						@ "[rotated]" means that
+						@ loaded value would have
+						@ to be rotated in order to
+						@ make it appear as in
+						@ alorithm specification
+	subs		$len,$len,#32		@ see if $len is 32 or larger
+	mov		$inc,#16		@ $inc is used as post-
+						@ increment for input pointer;
+						@ as loop is modulo-scheduled
+						@ $inc is zeroed just in time
+						@ to preclude oversteping
+						@ inp[len], which means that
+						@ last block[s] are actually
+						@ loaded twice, but last
+						@ copy is not processed
+	vld1.64		{$H-$Hhl},[$Htbl],#32	@ load twisted H, ..., H^2
+	vmov.i8		$xC2,#0xe1
+	vld1.64		{$H2},[$Htbl]
+	cclr		$inc,eq			@ is it time to zero $inc?
+	vext.8		$Xl,$Xl,$Xl,#8		@ rotate Xi
+	vld1.64		{$t0},[$inp],#16	@ load [rotated] I[0]
+	vshl.u64	$xC2,$xC2,#57		@ compose 0xc2.0 constant
+#ifndef __ARMEB__
+	vrev64.8	$t0,$t0
+	vrev64.8	$Xl,$Xl
+#endif
+	vext.8		$IN,$t0,$t0,#8		@ rotate I[0]
+	b.lo		.Lodd_tail_v8		@ $len was less than 32
+___
+{ my ($Xln,$Xmn,$Xhn,$In) = map("q$_",(4..7));
+	#######
+	# Xi+2 =[H*(Ii+1 + Xi+1)] mod P =
+	#	[(H*Ii+1) + (H*Xi+1)] mod P =
+	#	[(H*Ii+1) + H^2*(Ii+Xi)] mod P
+	#
+$code.=<<___;
+	vld1.64		{$t1},[$inp],$inc	@ load [rotated] I[1]
+#ifndef __ARMEB__
+	vrev64.8	$t1,$t1
+#endif
+	vext.8		$In,$t1,$t1,#8
+	veor		$IN,$IN,$Xl		@ I[i]^=Xi
+	vpmull.p64	$Xln,$H,$In		@ H·Ii+1
+	veor		$t1,$t1,$In		@ Karatsuba pre-processing
+	vpmull2.p64	$Xhn,$H,$In
+	b		.Loop_mod2x_v8
+
+.align	4
+.Loop_mod2x_v8:
+	vext.8		$t2,$IN,$IN,#8
+	subs		$len,$len,#32		@ is there more data?
+	vpmull.p64	$Xl,$H2,$IN		@ H^2.lo·Xi.lo
+	cclr		$inc,lo			@ is it time to zero $inc?
+
+	 vpmull.p64	$Xmn,$Hhl,$t1
+	veor		$t2,$t2,$IN		@ Karatsuba pre-processing
+	vpmull2.p64	$Xh,$H2,$IN		@ H^2.hi·Xi.hi
+	veor		$Xl,$Xl,$Xln		@ accumulate
+	vpmull2.p64	$Xm,$Hhl,$t2		@ (H^2.lo+H^2.hi)·(Xi.lo+Xi.hi)
+	 vld1.64	{$t0},[$inp],$inc	@ load [rotated] I[i+2]
+
+	veor		$Xh,$Xh,$Xhn
+	 cclr		$inc,eq			@ is it time to zero $inc?
+	veor		$Xm,$Xm,$Xmn
+
+	vext.8		$t1,$Xl,$Xh,#8		@ Karatsuba post-processing
+	veor		$t2,$Xl,$Xh
+	veor		$Xm,$Xm,$t1
+	 vld1.64	{$t1},[$inp],$inc	@ load [rotated] I[i+3]
+#ifndef __ARMEB__
+	 vrev64.8	$t0,$t0
+#endif
+	veor		$Xm,$Xm,$t2
+	vpmull.p64	$t2,$Xl,$xC2		@ 1st phase of reduction
+
+#ifndef __ARMEB__
+	 vrev64.8	$t1,$t1
+#endif
+	vmov		$Xh#lo,$Xm#hi		@ Xh|Xm - 256-bit result
+	vmov		$Xm#hi,$Xl#lo		@ Xm is rotated Xl
+	 vext.8		$In,$t1,$t1,#8
+	 vext.8		$IN,$t0,$t0,#8
+	veor		$Xl,$Xm,$t2
+	 vpmull.p64	$Xln,$H,$In		@ H·Ii+1
+	veor		$IN,$IN,$Xh		@ accumulate $IN early
+
+	vext.8		$t2,$Xl,$Xl,#8		@ 2nd phase of reduction
+	vpmull.p64	$Xl,$Xl,$xC2
+	veor		$IN,$IN,$t2
+	 veor		$t1,$t1,$In		@ Karatsuba pre-processing
+	veor		$IN,$IN,$Xl
+	 vpmull2.p64	$Xhn,$H,$In
+	b.hs		.Loop_mod2x_v8		@ there was at least 32 more bytes
+
+	veor		$Xh,$Xh,$t2
+	vext.8		$IN,$t0,$t0,#8		@ re-construct $IN
+	adds		$len,$len,#32		@ re-construct $len
+	veor		$Xl,$Xl,$Xh		@ re-construct $Xl
+	b.eq		.Ldone_v8		@ is $len zero?
+___
+}
+$code.=<<___;
+.Lodd_tail_v8:
+	vext.8		$t2,$Xl,$Xl,#8
+	veor		$IN,$IN,$Xl		@ inp^=Xi
+	veor		$t1,$t0,$t2		@ $t1 is rotated inp^Xi
+
+	vpmull.p64	$Xl,$H,$IN		@ H.lo·Xi.lo
+	veor		$t1,$t1,$IN		@ Karatsuba pre-processing
+	vpmull2.p64	$Xh,$H,$IN		@ H.hi·Xi.hi
+	vpmull.p64	$Xm,$Hhl,$t1		@ (H.lo+H.hi)·(Xi.lo+Xi.hi)
+
+	vext.8		$t1,$Xl,$Xh,#8		@ Karatsuba post-processing
+	veor		$t2,$Xl,$Xh
+	veor		$Xm,$Xm,$t1
+	veor		$Xm,$Xm,$t2
+	vpmull.p64	$t2,$Xl,$xC2		@ 1st phase of reduction
+
+	vmov		$Xh#lo,$Xm#hi		@ Xh|Xm - 256-bit result
+	vmov		$Xm#hi,$Xl#lo		@ Xm is rotated Xl
+	veor		$Xl,$Xm,$t2
+
+	vext.8		$t2,$Xl,$Xl,#8		@ 2nd phase of reduction
+	vpmull.p64	$Xl,$Xl,$xC2
+	veor		$t2,$t2,$Xh
+	veor		$Xl,$Xl,$t2
+
+.Ldone_v8:
+#ifndef __ARMEB__
+	vrev64.8	$Xl,$Xl
+#endif
+	vext.8		$Xl,$Xl,$Xl,#8
+	vst1.64		{$Xl},[$Xi]		@ write out Xi
+
+___
+$code.=<<___		if ($flavour !~ /64/);
+	vldmia		sp!,{d8-d15}		@ 32-bit ABI says so
+___
+$code.=<<___;
+	ret
+.size	gcm_ghash_v8,.-gcm_ghash_v8
+___
+}
+$code.=<<___;
+.asciz  "GHASH for ARMv8, CRYPTOGAMS by "
+.align  2
+___
+
+if ($flavour =~ /64/) {			######## 64-bit code
+    sub unvmov {
+	my $arg=shift;
+
+	$arg =~ m/q([0-9]+)#(lo|hi),\s*q([0-9]+)#(lo|hi)/o &&
+	sprintf	"ins	v%d.d[%d],v%d.d[%d]",$1,($2 eq "lo")?0:1,$3,($4 eq "lo")?0:1;
+    }
+    foreach(split("\n",$code)) {
+	s/cclr\s+([wx])([^,]+),\s*([a-z]+)/csel	$1$2,$1zr,$1$2,$3/o	or
+	s/vmov\.i8/movi/o		or	# fix up legacy mnemonics
+	s/vmov\s+(.*)/unvmov($1)/geo	or
+	s/vext\.8/ext/o			or
+	s/vshr\.s/sshr\.s/o		or
+	s/vshr/ushr/o			or
+	s/^(\s+)v/$1/o			or	# strip off v prefix
+	s/\bbx\s+lr\b/ret/o;
+
+	s/\bq([0-9]+)\b/"v".($1<8?$1:$1+8).".16b"/geo;	# old->new registers
+	s/@\s/\/\//o;				# old->new style commentary
+
+	# fix up remainig legacy suffixes
+	s/\.[ui]?8(\s)/$1/o;
+	s/\.[uis]?32//o and s/\.16b/\.4s/go;
+	m/\.p64/o and s/\.16b/\.1q/o;		# 1st pmull argument
+	m/l\.p64/o and s/\.16b/\.1d/go;		# 2nd and 3rd pmull arguments
+	s/\.[uisp]?64//o and s/\.16b/\.2d/go;
+	s/\.[42]([sd])\[([0-3])\]/\.$1\[$2\]/o;
+
+	print $_,"\n";
+    }
+} else {				######## 32-bit code
+    sub unvdup32 {
+	my $arg=shift;
+
+	$arg =~ m/q([0-9]+),\s*q([0-9]+)\[([0-3])\]/o &&
+	sprintf	"vdup.32	q%d,d%d[%d]",$1,2*$2+($3>>1),$3&1;
+    }
+    sub unvpmullp64 {
+	my ($mnemonic,$arg)=@_;
+
+	if ($arg =~ m/q([0-9]+),\s*q([0-9]+),\s*q([0-9]+)/o) {
+	    my $word = 0xf2a00e00|(($1&7)<<13)|(($1&8)<<19)
+				 |(($2&7)<<17)|(($2&8)<<4)
+				 |(($3&7)<<1) |(($3&8)<<2);
+	    $word |= 0x00010001	 if ($mnemonic =~ "2");
+	    # since ARMv7 instructions are always encoded little-endian.
+	    # correct solution is to use .inst directive, but older
+	    # assemblers don't implement it:-(
+	    sprintf ".byte\t0x%02x,0x%02x,0x%02x,0x%02x\t@ %s %s",
+			$word&0xff,($word>>8)&0xff,
+			($word>>16)&0xff,($word>>24)&0xff,
+			$mnemonic,$arg;
+	}
+    }
+
+    foreach(split("\n",$code)) {
+	s/\b[wx]([0-9]+)\b/r$1/go;		# new->old registers
+	s/\bv([0-9])\.[12468]+[bsd]\b/q$1/go;	# new->old registers
+	s/\/\/\s?/@ /o;				# new->old style commentary
+
+	# fix up remainig new-style suffixes
+	s/\],#[0-9]+/]!/o;
+
+	s/cclr\s+([^,]+),\s*([a-z]+)/mov$2	$1,#0/o			or
+	s/vdup\.32\s+(.*)/unvdup32($1)/geo				or
+	s/v?(pmull2?)\.p64\s+(.*)/unvpmullp64($1,$2)/geo		or
+	s/\bq([0-9]+)#(lo|hi)/sprintf "d%d",2*$1+($2 eq "hi")/geo	or
+	s/^(\s+)b\./$1b/o						or
+	s/^(\s+)ret/$1bx\tlr/o;
+
+	print $_,"\n";
+    }
+}
+
+close STDOUT; # enforce flush
diff --git a/crypto/modes/build.info b/crypto/modes/build.info
new file mode 100644
index 000000000..38195c44a
--- /dev/null
+++ b/crypto/modes/build.info
@@ -0,0 +1,27 @@
+LIBS=../../libcrypto
+SOURCE[../../libcrypto]=\
+        cbc128.c ctr128.c cts128.c cfb128.c ofb128.c gcm128.c \
+        ccm128.c xts128.c wrap128.c ocb128.c \
+        {- $target{modes_asm_src} -}
+
+INCLUDE[gcm128.o]=..
+
+GENERATE[ghash-ia64.s]=asm/ghash-ia64.pl $(CFLAGS) $(LIB_CFLAGS)
+GENERATE[ghash-x86.s]=asm/ghash-x86.pl $(PERLASM_SCHEME) $(CFLAGS) $(LIB_CFLAGS) $(PROCESSOR)
+GENERATE[ghash-x86_64.s]=asm/ghash-x86_64.pl $(PERLASM_SCHEME)
+GENERATE[aesni-gcm-x86_64.s]=asm/aesni-gcm-x86_64.pl $(PERLASM_SCHEME)
+GENERATE[ghash-sparcv9.S]=asm/ghash-sparcv9.pl $(PERLASM_SCHEME)
+INCLUDE[ghash-sparcv9.o]=..
+GENERATE[ghash-alpha.S]=asm/ghash-alpha.pl $(PERLASM_SCHEME)
+GENERATE[ghash-parisc.s]=asm/ghash-parisc.pl $(PERLASM_SCHEME)
+GENERATE[ghashp8-ppc.s]=asm/ghashp8-ppc.pl $(PERLASM_SCHEME)
+GENERATE[ghash-armv4.S]=asm/ghash-armv4.pl $(PERLASM_SCHEME)
+INCLUDE[ghash-armv4.o]=..
+GENERATE[ghashv8-armx.S]=asm/ghashv8-armx.pl $(PERLASM_SCHEME)
+INCLUDE[ghashv8-armx.o]=..
+
+BEGINRAW[Makefile]
+# GNU make "catch all"
+{- $builddir -}/ghash-%.S:	{- $sourcedir -}/asm/ghash-%.pl
+	CC="$(CC)" $(PERL) $< $(PERLASM_SCHEME) $@
+ENDRAW[Makefile]
diff --git a/crypto/modes/cbc128.c b/crypto/modes/cbc128.c
new file mode 100644
index 000000000..4c9bc85ea
--- /dev/null
+++ b/crypto/modes/cbc128.c
@@ -0,0 +1,155 @@
+/*
+ * Copyright 2008-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "modes_lcl.h"
+#include 
+
+#if !defined(STRICT_ALIGNMENT) && !defined(PEDANTIC)
+# define STRICT_ALIGNMENT 0
+#endif
+
+void CRYPTO_cbc128_encrypt(const unsigned char *in, unsigned char *out,
+                           size_t len, const void *key,
+                           unsigned char ivec[16], block128_f block)
+{
+    size_t n;
+    const unsigned char *iv = ivec;
+
+#if !defined(OPENSSL_SMALL_FOOTPRINT)
+    if (STRICT_ALIGNMENT &&
+        ((size_t)in | (size_t)out | (size_t)ivec) % sizeof(size_t) != 0) {
+        while (len >= 16) {
+            for (n = 0; n < 16; ++n)
+                out[n] = in[n] ^ iv[n];
+            (*block) (out, out, key);
+            iv = out;
+            len -= 16;
+            in += 16;
+            out += 16;
+        }
+    } else {
+        while (len >= 16) {
+            for (n = 0; n < 16; n += sizeof(size_t))
+                *(size_t *)(out + n) =
+                    *(size_t *)(in + n) ^ *(size_t *)(iv + n);
+            (*block) (out, out, key);
+            iv = out;
+            len -= 16;
+            in += 16;
+            out += 16;
+        }
+    }
+#endif
+    while (len) {
+        for (n = 0; n < 16 && n < len; ++n)
+            out[n] = in[n] ^ iv[n];
+        for (; n < 16; ++n)
+            out[n] = iv[n];
+        (*block) (out, out, key);
+        iv = out;
+        if (len <= 16)
+            break;
+        len -= 16;
+        in += 16;
+        out += 16;
+    }
+    memcpy(ivec, iv, 16);
+}
+
+void CRYPTO_cbc128_decrypt(const unsigned char *in, unsigned char *out,
+                           size_t len, const void *key,
+                           unsigned char ivec[16], block128_f block)
+{
+    size_t n;
+    union {
+        size_t t[16 / sizeof(size_t)];
+        unsigned char c[16];
+    } tmp;
+
+#if !defined(OPENSSL_SMALL_FOOTPRINT)
+    if (in != out) {
+        const unsigned char *iv = ivec;
+
+        if (STRICT_ALIGNMENT &&
+            ((size_t)in | (size_t)out | (size_t)ivec) % sizeof(size_t) != 0) {
+            while (len >= 16) {
+                (*block) (in, out, key);
+                for (n = 0; n < 16; ++n)
+                    out[n] ^= iv[n];
+                iv = in;
+                len -= 16;
+                in += 16;
+                out += 16;
+            }
+        } else if (16 % sizeof(size_t) == 0) { /* always true */
+            while (len >= 16) {
+                size_t *out_t = (size_t *)out, *iv_t = (size_t *)iv;
+
+                (*block) (in, out, key);
+                for (n = 0; n < 16 / sizeof(size_t); n++)
+                    out_t[n] ^= iv_t[n];
+                iv = in;
+                len -= 16;
+                in += 16;
+                out += 16;
+            }
+        }
+        memcpy(ivec, iv, 16);
+    } else {
+        if (STRICT_ALIGNMENT &&
+            ((size_t)in | (size_t)out | (size_t)ivec) % sizeof(size_t) != 0) {
+            unsigned char c;
+            while (len >= 16) {
+                (*block) (in, tmp.c, key);
+                for (n = 0; n < 16; ++n) {
+                    c = in[n];
+                    out[n] = tmp.c[n] ^ ivec[n];
+                    ivec[n] = c;
+                }
+                len -= 16;
+                in += 16;
+                out += 16;
+            }
+        } else if (16 % sizeof(size_t) == 0) { /* always true */
+            while (len >= 16) {
+                size_t c, *out_t = (size_t *)out, *ivec_t = (size_t *)ivec;
+                const size_t *in_t = (const size_t *)in;
+
+                (*block) (in, tmp.c, key);
+                for (n = 0; n < 16 / sizeof(size_t); n++) {
+                    c = in_t[n];
+                    out_t[n] = tmp.t[n] ^ ivec_t[n];
+                    ivec_t[n] = c;
+                }
+                len -= 16;
+                in += 16;
+                out += 16;
+            }
+        }
+    }
+#endif
+    while (len) {
+        unsigned char c;
+        (*block) (in, tmp.c, key);
+        for (n = 0; n < 16 && n < len; ++n) {
+            c = in[n];
+            out[n] = tmp.c[n] ^ ivec[n];
+            ivec[n] = c;
+        }
+        if (len <= 16) {
+            for (; n < 16; ++n)
+                ivec[n] = in[n];
+            break;
+        }
+        len -= 16;
+        in += 16;
+        out += 16;
+    }
+}
diff --git a/crypto/modes/ccm128.c b/crypto/modes/ccm128.c
new file mode 100644
index 000000000..85ce84f10
--- /dev/null
+++ b/crypto/modes/ccm128.c
@@ -0,0 +1,432 @@
+/*
+ * Copyright 2011-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "modes_lcl.h"
+#include 
+
+/*
+ * First you setup M and L parameters and pass the key schedule. This is
+ * called once per session setup...
+ */
+void CRYPTO_ccm128_init(CCM128_CONTEXT *ctx,
+                        unsigned int M, unsigned int L, void *key,
+                        block128_f block)
+{
+    memset(ctx->nonce.c, 0, sizeof(ctx->nonce.c));
+    ctx->nonce.c[0] = ((u8)(L - 1) & 7) | (u8)(((M - 2) / 2) & 7) << 3;
+    ctx->blocks = 0;
+    ctx->block = block;
+    ctx->key = key;
+}
+
+/* !!! Following interfaces are to be called *once* per packet !!! */
+
+/* Then you setup per-message nonce and pass the length of the message */
+int CRYPTO_ccm128_setiv(CCM128_CONTEXT *ctx,
+                        const unsigned char *nonce, size_t nlen, size_t mlen)
+{
+    unsigned int L = ctx->nonce.c[0] & 7; /* the L parameter */
+
+    if (nlen < (14 - L))
+        return -1;              /* nonce is too short */
+
+    if (sizeof(mlen) == 8 && L >= 3) {
+        ctx->nonce.c[8] = (u8)(mlen >> (56 % (sizeof(mlen) * 8)));
+        ctx->nonce.c[9] = (u8)(mlen >> (48 % (sizeof(mlen) * 8)));
+        ctx->nonce.c[10] = (u8)(mlen >> (40 % (sizeof(mlen) * 8)));
+        ctx->nonce.c[11] = (u8)(mlen >> (32 % (sizeof(mlen) * 8)));
+    } else
+        ctx->nonce.u[1] = 0;
+
+    ctx->nonce.c[12] = (u8)(mlen >> 24);
+    ctx->nonce.c[13] = (u8)(mlen >> 16);
+    ctx->nonce.c[14] = (u8)(mlen >> 8);
+    ctx->nonce.c[15] = (u8)mlen;
+
+    ctx->nonce.c[0] &= ~0x40;   /* clear Adata flag */
+    memcpy(&ctx->nonce.c[1], nonce, 14 - L);
+
+    return 0;
+}
+
+/* Then you pass additional authentication data, this is optional */
+void CRYPTO_ccm128_aad(CCM128_CONTEXT *ctx,
+                       const unsigned char *aad, size_t alen)
+{
+    unsigned int i;
+    block128_f block = ctx->block;
+
+    if (alen == 0)
+        return;
+
+    ctx->nonce.c[0] |= 0x40;    /* set Adata flag */
+    (*block) (ctx->nonce.c, ctx->cmac.c, ctx->key), ctx->blocks++;
+
+    if (alen < (0x10000 - 0x100)) {
+        ctx->cmac.c[0] ^= (u8)(alen >> 8);
+        ctx->cmac.c[1] ^= (u8)alen;
+        i = 2;
+    } else if (sizeof(alen) == 8
+               && alen >= (size_t)1 << (32 % (sizeof(alen) * 8))) {
+        ctx->cmac.c[0] ^= 0xFF;
+        ctx->cmac.c[1] ^= 0xFF;
+        ctx->cmac.c[2] ^= (u8)(alen >> (56 % (sizeof(alen) * 8)));
+        ctx->cmac.c[3] ^= (u8)(alen >> (48 % (sizeof(alen) * 8)));
+        ctx->cmac.c[4] ^= (u8)(alen >> (40 % (sizeof(alen) * 8)));
+        ctx->cmac.c[5] ^= (u8)(alen >> (32 % (sizeof(alen) * 8)));
+        ctx->cmac.c[6] ^= (u8)(alen >> 24);
+        ctx->cmac.c[7] ^= (u8)(alen >> 16);
+        ctx->cmac.c[8] ^= (u8)(alen >> 8);
+        ctx->cmac.c[9] ^= (u8)alen;
+        i = 10;
+    } else {
+        ctx->cmac.c[0] ^= 0xFF;
+        ctx->cmac.c[1] ^= 0xFE;
+        ctx->cmac.c[2] ^= (u8)(alen >> 24);
+        ctx->cmac.c[3] ^= (u8)(alen >> 16);
+        ctx->cmac.c[4] ^= (u8)(alen >> 8);
+        ctx->cmac.c[5] ^= (u8)alen;
+        i = 6;
+    }
+
+    do {
+        for (; i < 16 && alen; ++i, ++aad, --alen)
+            ctx->cmac.c[i] ^= *aad;
+        (*block) (ctx->cmac.c, ctx->cmac.c, ctx->key), ctx->blocks++;
+        i = 0;
+    } while (alen);
+}
+
+/* Finally you encrypt or decrypt the message */
+
+/*
+ * counter part of nonce may not be larger than L*8 bits, L is not larger
+ * than 8, therefore 64-bit counter...
+ */
+static void ctr64_inc(unsigned char *counter)
+{
+    unsigned int n = 8;
+    u8 c;
+
+    counter += 8;
+    do {
+        --n;
+        c = counter[n];
+        ++c;
+        counter[n] = c;
+        if (c)
+            return;
+    } while (n);
+}
+
+int CRYPTO_ccm128_encrypt(CCM128_CONTEXT *ctx,
+                          const unsigned char *inp, unsigned char *out,
+                          size_t len)
+{
+    size_t n;
+    unsigned int i, L;
+    unsigned char flags0 = ctx->nonce.c[0];
+    block128_f block = ctx->block;
+    void *key = ctx->key;
+    union {
+        u64 u[2];
+        u8 c[16];
+    } scratch;
+
+    if (!(flags0 & 0x40))
+        (*block) (ctx->nonce.c, ctx->cmac.c, key), ctx->blocks++;
+
+    ctx->nonce.c[0] = L = flags0 & 7;
+    for (n = 0, i = 15 - L; i < 15; ++i) {
+        n |= ctx->nonce.c[i];
+        ctx->nonce.c[i] = 0;
+        n <<= 8;
+    }
+    n |= ctx->nonce.c[15];      /* reconstructed length */
+    ctx->nonce.c[15] = 1;
+
+    if (n != len)
+        return -1;              /* length mismatch */
+
+    ctx->blocks += ((len + 15) >> 3) | 1;
+    if (ctx->blocks > (U64(1) << 61))
+        return -2;              /* too much data */
+
+    while (len >= 16) {
+#if defined(STRICT_ALIGNMENT)
+        union {
+            u64 u[2];
+            u8 c[16];
+        } temp;
+
+        memcpy(temp.c, inp, 16);
+        ctx->cmac.u[0] ^= temp.u[0];
+        ctx->cmac.u[1] ^= temp.u[1];
+#else
+        ctx->cmac.u[0] ^= ((u64 *)inp)[0];
+        ctx->cmac.u[1] ^= ((u64 *)inp)[1];
+#endif
+        (*block) (ctx->cmac.c, ctx->cmac.c, key);
+        (*block) (ctx->nonce.c, scratch.c, key);
+        ctr64_inc(ctx->nonce.c);
+#if defined(STRICT_ALIGNMENT)
+        temp.u[0] ^= scratch.u[0];
+        temp.u[1] ^= scratch.u[1];
+        memcpy(out, temp.c, 16);
+#else
+        ((u64 *)out)[0] = scratch.u[0] ^ ((u64 *)inp)[0];
+        ((u64 *)out)[1] = scratch.u[1] ^ ((u64 *)inp)[1];
+#endif
+        inp += 16;
+        out += 16;
+        len -= 16;
+    }
+
+    if (len) {
+        for (i = 0; i < len; ++i)
+            ctx->cmac.c[i] ^= inp[i];
+        (*block) (ctx->cmac.c, ctx->cmac.c, key);
+        (*block) (ctx->nonce.c, scratch.c, key);
+        for (i = 0; i < len; ++i)
+            out[i] = scratch.c[i] ^ inp[i];
+    }
+
+    for (i = 15 - L; i < 16; ++i)
+        ctx->nonce.c[i] = 0;
+
+    (*block) (ctx->nonce.c, scratch.c, key);
+    ctx->cmac.u[0] ^= scratch.u[0];
+    ctx->cmac.u[1] ^= scratch.u[1];
+
+    ctx->nonce.c[0] = flags0;
+
+    return 0;
+}
+
+int CRYPTO_ccm128_decrypt(CCM128_CONTEXT *ctx,
+                          const unsigned char *inp, unsigned char *out,
+                          size_t len)
+{
+    size_t n;
+    unsigned int i, L;
+    unsigned char flags0 = ctx->nonce.c[0];
+    block128_f block = ctx->block;
+    void *key = ctx->key;
+    union {
+        u64 u[2];
+        u8 c[16];
+    } scratch;
+
+    if (!(flags0 & 0x40))
+        (*block) (ctx->nonce.c, ctx->cmac.c, key);
+
+    ctx->nonce.c[0] = L = flags0 & 7;
+    for (n = 0, i = 15 - L; i < 15; ++i) {
+        n |= ctx->nonce.c[i];
+        ctx->nonce.c[i] = 0;
+        n <<= 8;
+    }
+    n |= ctx->nonce.c[15];      /* reconstructed length */
+    ctx->nonce.c[15] = 1;
+
+    if (n != len)
+        return -1;
+
+    while (len >= 16) {
+#if defined(STRICT_ALIGNMENT)
+        union {
+            u64 u[2];
+            u8 c[16];
+        } temp;
+#endif
+        (*block) (ctx->nonce.c, scratch.c, key);
+        ctr64_inc(ctx->nonce.c);
+#if defined(STRICT_ALIGNMENT)
+        memcpy(temp.c, inp, 16);
+        ctx->cmac.u[0] ^= (scratch.u[0] ^= temp.u[0]);
+        ctx->cmac.u[1] ^= (scratch.u[1] ^= temp.u[1]);
+        memcpy(out, scratch.c, 16);
+#else
+        ctx->cmac.u[0] ^= (((u64 *)out)[0] = scratch.u[0] ^ ((u64 *)inp)[0]);
+        ctx->cmac.u[1] ^= (((u64 *)out)[1] = scratch.u[1] ^ ((u64 *)inp)[1]);
+#endif
+        (*block) (ctx->cmac.c, ctx->cmac.c, key);
+
+        inp += 16;
+        out += 16;
+        len -= 16;
+    }
+
+    if (len) {
+        (*block) (ctx->nonce.c, scratch.c, key);
+        for (i = 0; i < len; ++i)
+            ctx->cmac.c[i] ^= (out[i] = scratch.c[i] ^ inp[i]);
+        (*block) (ctx->cmac.c, ctx->cmac.c, key);
+    }
+
+    for (i = 15 - L; i < 16; ++i)
+        ctx->nonce.c[i] = 0;
+
+    (*block) (ctx->nonce.c, scratch.c, key);
+    ctx->cmac.u[0] ^= scratch.u[0];
+    ctx->cmac.u[1] ^= scratch.u[1];
+
+    ctx->nonce.c[0] = flags0;
+
+    return 0;
+}
+
+static void ctr64_add(unsigned char *counter, size_t inc)
+{
+    size_t n = 8, val = 0;
+
+    counter += 8;
+    do {
+        --n;
+        val += counter[n] + (inc & 0xff);
+        counter[n] = (unsigned char)val;
+        val >>= 8;              /* carry bit */
+        inc >>= 8;
+    } while (n && (inc || val));
+}
+
+int CRYPTO_ccm128_encrypt_ccm64(CCM128_CONTEXT *ctx,
+                                const unsigned char *inp, unsigned char *out,
+                                size_t len, ccm128_f stream)
+{
+    size_t n;
+    unsigned int i, L;
+    unsigned char flags0 = ctx->nonce.c[0];
+    block128_f block = ctx->block;
+    void *key = ctx->key;
+    union {
+        u64 u[2];
+        u8 c[16];
+    } scratch;
+
+    if (!(flags0 & 0x40))
+        (*block) (ctx->nonce.c, ctx->cmac.c, key), ctx->blocks++;
+
+    ctx->nonce.c[0] = L = flags0 & 7;
+    for (n = 0, i = 15 - L; i < 15; ++i) {
+        n |= ctx->nonce.c[i];
+        ctx->nonce.c[i] = 0;
+        n <<= 8;
+    }
+    n |= ctx->nonce.c[15];      /* reconstructed length */
+    ctx->nonce.c[15] = 1;
+
+    if (n != len)
+        return -1;              /* length mismatch */
+
+    ctx->blocks += ((len + 15) >> 3) | 1;
+    if (ctx->blocks > (U64(1) << 61))
+        return -2;              /* too much data */
+
+    if ((n = len / 16)) {
+        (*stream) (inp, out, n, key, ctx->nonce.c, ctx->cmac.c);
+        n *= 16;
+        inp += n;
+        out += n;
+        len -= n;
+        if (len)
+            ctr64_add(ctx->nonce.c, n / 16);
+    }
+
+    if (len) {
+        for (i = 0; i < len; ++i)
+            ctx->cmac.c[i] ^= inp[i];
+        (*block) (ctx->cmac.c, ctx->cmac.c, key);
+        (*block) (ctx->nonce.c, scratch.c, key);
+        for (i = 0; i < len; ++i)
+            out[i] = scratch.c[i] ^ inp[i];
+    }
+
+    for (i = 15 - L; i < 16; ++i)
+        ctx->nonce.c[i] = 0;
+
+    (*block) (ctx->nonce.c, scratch.c, key);
+    ctx->cmac.u[0] ^= scratch.u[0];
+    ctx->cmac.u[1] ^= scratch.u[1];
+
+    ctx->nonce.c[0] = flags0;
+
+    return 0;
+}
+
+int CRYPTO_ccm128_decrypt_ccm64(CCM128_CONTEXT *ctx,
+                                const unsigned char *inp, unsigned char *out,
+                                size_t len, ccm128_f stream)
+{
+    size_t n;
+    unsigned int i, L;
+    unsigned char flags0 = ctx->nonce.c[0];
+    block128_f block = ctx->block;
+    void *key = ctx->key;
+    union {
+        u64 u[2];
+        u8 c[16];
+    } scratch;
+
+    if (!(flags0 & 0x40))
+        (*block) (ctx->nonce.c, ctx->cmac.c, key);
+
+    ctx->nonce.c[0] = L = flags0 & 7;
+    for (n = 0, i = 15 - L; i < 15; ++i) {
+        n |= ctx->nonce.c[i];
+        ctx->nonce.c[i] = 0;
+        n <<= 8;
+    }
+    n |= ctx->nonce.c[15];      /* reconstructed length */
+    ctx->nonce.c[15] = 1;
+
+    if (n != len)
+        return -1;
+
+    if ((n = len / 16)) {
+        (*stream) (inp, out, n, key, ctx->nonce.c, ctx->cmac.c);
+        n *= 16;
+        inp += n;
+        out += n;
+        len -= n;
+        if (len)
+            ctr64_add(ctx->nonce.c, n / 16);
+    }
+
+    if (len) {
+        (*block) (ctx->nonce.c, scratch.c, key);
+        for (i = 0; i < len; ++i)
+            ctx->cmac.c[i] ^= (out[i] = scratch.c[i] ^ inp[i]);
+        (*block) (ctx->cmac.c, ctx->cmac.c, key);
+    }
+
+    for (i = 15 - L; i < 16; ++i)
+        ctx->nonce.c[i] = 0;
+
+    (*block) (ctx->nonce.c, scratch.c, key);
+    ctx->cmac.u[0] ^= scratch.u[0];
+    ctx->cmac.u[1] ^= scratch.u[1];
+
+    ctx->nonce.c[0] = flags0;
+
+    return 0;
+}
+
+size_t CRYPTO_ccm128_tag(CCM128_CONTEXT *ctx, unsigned char *tag, size_t len)
+{
+    unsigned int M = (ctx->nonce.c[0] >> 3) & 7; /* the M parameter */
+
+    M *= 2;
+    M += 2;
+    if (len < M)
+        return 0;
+    memcpy(tag, ctx->cmac.c, M);
+    return M;
+}
diff --git a/crypto/modes/cfb128.c b/crypto/modes/cfb128.c
new file mode 100644
index 000000000..e439567fe
--- /dev/null
+++ b/crypto/modes/cfb128.c
@@ -0,0 +1,198 @@
+/*
+ * Copyright 2008-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "modes_lcl.h"
+#include 
+
+/*
+ * The input and output encrypted as though 128bit cfb mode is being used.
+ * The extra state information to record how much of the 128bit block we have
+ * used is contained in *num;
+ */
+void CRYPTO_cfb128_encrypt(const unsigned char *in, unsigned char *out,
+                           size_t len, const void *key,
+                           unsigned char ivec[16], int *num,
+                           int enc, block128_f block)
+{
+    unsigned int n;
+    size_t l = 0;
+
+    n = *num;
+
+    if (enc) {
+#if !defined(OPENSSL_SMALL_FOOTPRINT)
+        if (16 % sizeof(size_t) == 0) { /* always true actually */
+            do {
+                while (n && len) {
+                    *(out++) = ivec[n] ^= *(in++);
+                    --len;
+                    n = (n + 1) % 16;
+                }
+# if defined(STRICT_ALIGNMENT)
+                if (((size_t)in | (size_t)out | (size_t)ivec) %
+                    sizeof(size_t) != 0)
+                    break;
+# endif
+                while (len >= 16) {
+                    (*block) (ivec, ivec, key);
+                    for (; n < 16; n += sizeof(size_t)) {
+                        *(size_t *)(out + n) =
+                            *(size_t *)(ivec + n) ^= *(size_t *)(in + n);
+                    }
+                    len -= 16;
+                    out += 16;
+                    in += 16;
+                    n = 0;
+                }
+                if (len) {
+                    (*block) (ivec, ivec, key);
+                    while (len--) {
+                        out[n] = ivec[n] ^= in[n];
+                        ++n;
+                    }
+                }
+                *num = n;
+                return;
+            } while (0);
+        }
+        /* the rest would be commonly eliminated by x86* compiler */
+#endif
+        while (l < len) {
+            if (n == 0) {
+                (*block) (ivec, ivec, key);
+            }
+            out[l] = ivec[n] ^= in[l];
+            ++l;
+            n = (n + 1) % 16;
+        }
+        *num = n;
+    } else {
+#if !defined(OPENSSL_SMALL_FOOTPRINT)
+        if (16 % sizeof(size_t) == 0) { /* always true actually */
+            do {
+                while (n && len) {
+                    unsigned char c;
+                    *(out++) = ivec[n] ^ (c = *(in++));
+                    ivec[n] = c;
+                    --len;
+                    n = (n + 1) % 16;
+                }
+# if defined(STRICT_ALIGNMENT)
+                if (((size_t)in | (size_t)out | (size_t)ivec) %
+                    sizeof(size_t) != 0)
+                    break;
+# endif
+                while (len >= 16) {
+                    (*block) (ivec, ivec, key);
+                    for (; n < 16; n += sizeof(size_t)) {
+                        size_t t = *(size_t *)(in + n);
+                        *(size_t *)(out + n) = *(size_t *)(ivec + n) ^ t;
+                        *(size_t *)(ivec + n) = t;
+                    }
+                    len -= 16;
+                    out += 16;
+                    in += 16;
+                    n = 0;
+                }
+                if (len) {
+                    (*block) (ivec, ivec, key);
+                    while (len--) {
+                        unsigned char c;
+                        out[n] = ivec[n] ^ (c = in[n]);
+                        ivec[n] = c;
+                        ++n;
+                    }
+                }
+                *num = n;
+                return;
+            } while (0);
+        }
+        /* the rest would be commonly eliminated by x86* compiler */
+#endif
+        while (l < len) {
+            unsigned char c;
+            if (n == 0) {
+                (*block) (ivec, ivec, key);
+            }
+            out[l] = ivec[n] ^ (c = in[l]);
+            ivec[n] = c;
+            ++l;
+            n = (n + 1) % 16;
+        }
+        *num = n;
+    }
+}
+
+/*
+ * This expects a single block of size nbits for both in and out. Note that
+ * it corrupts any extra bits in the last byte of out
+ */
+static void cfbr_encrypt_block(const unsigned char *in, unsigned char *out,
+                               int nbits, const void *key,
+                               unsigned char ivec[16], int enc,
+                               block128_f block)
+{
+    int n, rem, num;
+    unsigned char ovec[16 * 2 + 1]; /* +1 because we dereference (but don't
+                                     * use) one byte off the end */
+
+    if (nbits <= 0 || nbits > 128)
+        return;
+
+    /* fill in the first half of the new IV with the current IV */
+    memcpy(ovec, ivec, 16);
+    /* construct the new IV */
+    (*block) (ivec, ivec, key);
+    num = (nbits + 7) / 8;
+    if (enc)                    /* encrypt the input */
+        for (n = 0; n < num; ++n)
+            out[n] = (ovec[16 + n] = in[n] ^ ivec[n]);
+    else                        /* decrypt the input */
+        for (n = 0; n < num; ++n)
+            out[n] = (ovec[16 + n] = in[n]) ^ ivec[n];
+    /* shift ovec left... */
+    rem = nbits % 8;
+    num = nbits / 8;
+    if (rem == 0)
+        memcpy(ivec, ovec + num, 16);
+    else
+        for (n = 0; n < 16; ++n)
+            ivec[n] = ovec[n + num] << rem | ovec[n + num + 1] >> (8 - rem);
+
+    /* it is not necessary to cleanse ovec, since the IV is not secret */
+}
+
+/* N.B. This expects the input to be packed, MS bit first */
+void CRYPTO_cfb128_1_encrypt(const unsigned char *in, unsigned char *out,
+                             size_t bits, const void *key,
+                             unsigned char ivec[16], int *num,
+                             int enc, block128_f block)
+{
+    size_t n;
+    unsigned char c[1], d[1];
+
+    for (n = 0; n < bits; ++n) {
+        c[0] = (in[n / 8] & (1 << (7 - n % 8))) ? 0x80 : 0;
+        cfbr_encrypt_block(c, d, 1, key, ivec, enc, block);
+        out[n / 8] = (out[n / 8] & ~(1 << (unsigned int)(7 - n % 8))) |
+            ((d[0] & 0x80) >> (unsigned int)(n % 8));
+    }
+}
+
+void CRYPTO_cfb128_8_encrypt(const unsigned char *in, unsigned char *out,
+                             size_t length, const void *key,
+                             unsigned char ivec[16], int *num,
+                             int enc, block128_f block)
+{
+    size_t n;
+
+    for (n = 0; n < length; ++n)
+        cfbr_encrypt_block(&in[n], &out[n], 8, key, ivec, enc, block);
+}
diff --git a/crypto/modes/ctr128.c b/crypto/modes/ctr128.c
new file mode 100644
index 000000000..03920b447
--- /dev/null
+++ b/crypto/modes/ctr128.c
@@ -0,0 +1,209 @@
+/*
+ * Copyright 2008-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "modes_lcl.h"
+#include 
+
+/*
+ * NOTE: the IV/counter CTR mode is big-endian.  The code itself is
+ * endian-neutral.
+ */
+
+/* increment counter (128-bit int) by 1 */
+static void ctr128_inc(unsigned char *counter)
+{
+    u32 n = 16, c = 1;
+
+    do {
+        --n;
+        c += counter[n];
+        counter[n] = (u8)c;
+        c >>= 8;
+    } while (n);
+}
+
+#if !defined(OPENSSL_SMALL_FOOTPRINT)
+static void ctr128_inc_aligned(unsigned char *counter)
+{
+    size_t *data, c, d, n;
+    const union {
+        long one;
+        char little;
+    } is_endian = {
+        1
+    };
+
+    if (is_endian.little || ((size_t)counter % sizeof(size_t)) != 0) {
+        ctr128_inc(counter);
+        return;
+    }
+
+    data = (size_t *)counter;
+    c = 1;
+    n = 16 / sizeof(size_t);
+    do {
+        --n;
+        d = data[n] += c;
+        /* did addition carry? */
+        c = ((d - c) & ~d) >> (sizeof(size_t) * 8 - 1);
+    } while (n);
+}
+#endif
+
+/*
+ * The input encrypted as though 128bit counter mode is being used.  The
+ * extra state information to record how much of the 128bit block we have
+ * used is contained in *num, and the encrypted counter is kept in
+ * ecount_buf.  Both *num and ecount_buf must be initialised with zeros
+ * before the first call to CRYPTO_ctr128_encrypt(). This algorithm assumes
+ * that the counter is in the x lower bits of the IV (ivec), and that the
+ * application has full control over overflow and the rest of the IV.  This
+ * implementation takes NO responsibility for checking that the counter
+ * doesn't overflow into the rest of the IV when incremented.
+ */
+void CRYPTO_ctr128_encrypt(const unsigned char *in, unsigned char *out,
+                           size_t len, const void *key,
+                           unsigned char ivec[16],
+                           unsigned char ecount_buf[16], unsigned int *num,
+                           block128_f block)
+{
+    unsigned int n;
+    size_t l = 0;
+
+    n = *num;
+
+#if !defined(OPENSSL_SMALL_FOOTPRINT)
+    if (16 % sizeof(size_t) == 0) { /* always true actually */
+        do {
+            while (n && len) {
+                *(out++) = *(in++) ^ ecount_buf[n];
+                --len;
+                n = (n + 1) % 16;
+            }
+
+# if defined(STRICT_ALIGNMENT)
+            if (((size_t)in | (size_t)out | (size_t)ecount_buf)
+                % sizeof(size_t) != 0)
+                break;
+# endif
+            while (len >= 16) {
+                (*block) (ivec, ecount_buf, key);
+                ctr128_inc_aligned(ivec);
+                for (n = 0; n < 16; n += sizeof(size_t))
+                    *(size_t *)(out + n) =
+                        *(size_t *)(in + n) ^ *(size_t *)(ecount_buf + n);
+                len -= 16;
+                out += 16;
+                in += 16;
+                n = 0;
+            }
+            if (len) {
+                (*block) (ivec, ecount_buf, key);
+                ctr128_inc_aligned(ivec);
+                while (len--) {
+                    out[n] = in[n] ^ ecount_buf[n];
+                    ++n;
+                }
+            }
+            *num = n;
+            return;
+        } while (0);
+    }
+    /* the rest would be commonly eliminated by x86* compiler */
+#endif
+    while (l < len) {
+        if (n == 0) {
+            (*block) (ivec, ecount_buf, key);
+            ctr128_inc(ivec);
+        }
+        out[l] = in[l] ^ ecount_buf[n];
+        ++l;
+        n = (n + 1) % 16;
+    }
+
+    *num = n;
+}
+
+/* increment upper 96 bits of 128-bit counter by 1 */
+static void ctr96_inc(unsigned char *counter)
+{
+    u32 n = 12, c = 1;
+
+    do {
+        --n;
+        c += counter[n];
+        counter[n] = (u8)c;
+        c >>= 8;
+    } while (n);
+}
+
+void CRYPTO_ctr128_encrypt_ctr32(const unsigned char *in, unsigned char *out,
+                                 size_t len, const void *key,
+                                 unsigned char ivec[16],
+                                 unsigned char ecount_buf[16],
+                                 unsigned int *num, ctr128_f func)
+{
+    unsigned int n, ctr32;
+
+    n = *num;
+
+    while (n && len) {
+        *(out++) = *(in++) ^ ecount_buf[n];
+        --len;
+        n = (n + 1) % 16;
+    }
+
+    ctr32 = GETU32(ivec + 12);
+    while (len >= 16) {
+        size_t blocks = len / 16;
+        /*
+         * 1<<28 is just a not-so-small yet not-so-large number...
+         * Below condition is practically never met, but it has to
+         * be checked for code correctness.
+         */
+        if (sizeof(size_t) > sizeof(unsigned int) && blocks > (1U << 28))
+            blocks = (1U << 28);
+        /*
+         * As (*func) operates on 32-bit counter, caller
+         * has to handle overflow. 'if' below detects the
+         * overflow, which is then handled by limiting the
+         * amount of blocks to the exact overflow point...
+         */
+        ctr32 += (u32)blocks;
+        if (ctr32 < blocks) {
+            blocks -= ctr32;
+            ctr32 = 0;
+        }
+        (*func) (in, out, blocks, key, ivec);
+        /* (*ctr) does not update ivec, caller does: */
+        PUTU32(ivec + 12, ctr32);
+        /* ... overflow was detected, propagate carry. */
+        if (ctr32 == 0)
+            ctr96_inc(ivec);
+        blocks *= 16;
+        len -= blocks;
+        out += blocks;
+        in += blocks;
+    }
+    if (len) {
+        memset(ecount_buf, 0, 16);
+        (*func) (ecount_buf, ecount_buf, 1, key, ivec);
+        ++ctr32;
+        PUTU32(ivec + 12, ctr32);
+        if (ctr32 == 0)
+            ctr96_inc(ivec);
+        while (len--) {
+            out[n] = in[n] ^ ecount_buf[n];
+            ++n;
+        }
+    }
+
+    *num = n;
+}
diff --git a/crypto/modes/cts128.c b/crypto/modes/cts128.c
new file mode 100644
index 000000000..77ec994b4
--- /dev/null
+++ b/crypto/modes/cts128.c
@@ -0,0 +1,523 @@
+/*
+ * Copyright 2008-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "modes_lcl.h"
+#include 
+
+/*
+ * Trouble with Ciphertext Stealing, CTS, mode is that there is no
+ * common official specification, but couple of cipher/application
+ * specific ones: RFC2040 and RFC3962. Then there is 'Proposal to
+ * Extend CBC Mode By "Ciphertext Stealing"' at NIST site, which
+ * deviates from mentioned RFCs. Most notably it allows input to be
+ * of block length and it doesn't flip the order of the last two
+ * blocks. CTS is being discussed even in ECB context, but it's not
+ * adopted for any known application. This implementation provides
+ * two interfaces: one compliant with above mentioned RFCs and one
+ * compliant with the NIST proposal, both extending CBC mode.
+ */
+
+size_t CRYPTO_cts128_encrypt_block(const unsigned char *in,
+                                   unsigned char *out, size_t len,
+                                   const void *key, unsigned char ivec[16],
+                                   block128_f block)
+{
+    size_t residue, n;
+
+    if (len <= 16)
+        return 0;
+
+    if ((residue = len % 16) == 0)
+        residue = 16;
+
+    len -= residue;
+
+    CRYPTO_cbc128_encrypt(in, out, len, key, ivec, block);
+
+    in += len;
+    out += len;
+
+    for (n = 0; n < residue; ++n)
+        ivec[n] ^= in[n];
+    (*block) (ivec, ivec, key);
+    memcpy(out, out - 16, residue);
+    memcpy(out - 16, ivec, 16);
+
+    return len + residue;
+}
+
+size_t CRYPTO_nistcts128_encrypt_block(const unsigned char *in,
+                                       unsigned char *out, size_t len,
+                                       const void *key,
+                                       unsigned char ivec[16],
+                                       block128_f block)
+{
+    size_t residue, n;
+
+    if (len < 16)
+        return 0;
+
+    residue = len % 16;
+
+    len -= residue;
+
+    CRYPTO_cbc128_encrypt(in, out, len, key, ivec, block);
+
+    if (residue == 0)
+        return len;
+
+    in += len;
+    out += len;
+
+    for (n = 0; n < residue; ++n)
+        ivec[n] ^= in[n];
+    (*block) (ivec, ivec, key);
+    memcpy(out - 16 + residue, ivec, 16);
+
+    return len + residue;
+}
+
+size_t CRYPTO_cts128_encrypt(const unsigned char *in, unsigned char *out,
+                             size_t len, const void *key,
+                             unsigned char ivec[16], cbc128_f cbc)
+{
+    size_t residue;
+    union {
+        size_t align;
+        unsigned char c[16];
+    } tmp;
+
+    if (len <= 16)
+        return 0;
+
+    if ((residue = len % 16) == 0)
+        residue = 16;
+
+    len -= residue;
+
+    (*cbc) (in, out, len, key, ivec, 1);
+
+    in += len;
+    out += len;
+
+#if defined(CBC_HANDLES_TRUNCATED_IO)
+    memcpy(tmp.c, out - 16, 16);
+    (*cbc) (in, out - 16, residue, key, ivec, 1);
+    memcpy(out, tmp.c, residue);
+#else
+    memset(tmp.c, 0, sizeof(tmp));
+    memcpy(tmp.c, in, residue);
+    memcpy(out, out - 16, residue);
+    (*cbc) (tmp.c, out - 16, 16, key, ivec, 1);
+#endif
+    return len + residue;
+}
+
+size_t CRYPTO_nistcts128_encrypt(const unsigned char *in, unsigned char *out,
+                                 size_t len, const void *key,
+                                 unsigned char ivec[16], cbc128_f cbc)
+{
+    size_t residue;
+    union {
+        size_t align;
+        unsigned char c[16];
+    } tmp;
+
+    if (len < 16)
+        return 0;
+
+    residue = len % 16;
+
+    len -= residue;
+
+    (*cbc) (in, out, len, key, ivec, 1);
+
+    if (residue == 0)
+        return len;
+
+    in += len;
+    out += len;
+
+#if defined(CBC_HANDLES_TRUNCATED_IO)
+    (*cbc) (in, out - 16 + residue, residue, key, ivec, 1);
+#else
+    memset(tmp.c, 0, sizeof(tmp));
+    memcpy(tmp.c, in, residue);
+    (*cbc) (tmp.c, out - 16 + residue, 16, key, ivec, 1);
+#endif
+    return len + residue;
+}
+
+size_t CRYPTO_cts128_decrypt_block(const unsigned char *in,
+                                   unsigned char *out, size_t len,
+                                   const void *key, unsigned char ivec[16],
+                                   block128_f block)
+{
+    size_t residue, n;
+    union {
+        size_t align;
+        unsigned char c[32];
+    } tmp;
+
+    if (len <= 16)
+        return 0;
+
+    if ((residue = len % 16) == 0)
+        residue = 16;
+
+    len -= 16 + residue;
+
+    if (len) {
+        CRYPTO_cbc128_decrypt(in, out, len, key, ivec, block);
+        in += len;
+        out += len;
+    }
+
+    (*block) (in, tmp.c + 16, key);
+
+    memcpy(tmp.c, tmp.c + 16, 16);
+    memcpy(tmp.c, in + 16, residue);
+    (*block) (tmp.c, tmp.c, key);
+
+    for (n = 0; n < 16; ++n) {
+        unsigned char c = in[n];
+        out[n] = tmp.c[n] ^ ivec[n];
+        ivec[n] = c;
+    }
+    for (residue += 16; n < residue; ++n)
+        out[n] = tmp.c[n] ^ in[n];
+
+    return 16 + len + residue;
+}
+
+size_t CRYPTO_nistcts128_decrypt_block(const unsigned char *in,
+                                       unsigned char *out, size_t len,
+                                       const void *key,
+                                       unsigned char ivec[16],
+                                       block128_f block)
+{
+    size_t residue, n;
+    union {
+        size_t align;
+        unsigned char c[32];
+    } tmp;
+
+    if (len < 16)
+        return 0;
+
+    residue = len % 16;
+
+    if (residue == 0) {
+        CRYPTO_cbc128_decrypt(in, out, len, key, ivec, block);
+        return len;
+    }
+
+    len -= 16 + residue;
+
+    if (len) {
+        CRYPTO_cbc128_decrypt(in, out, len, key, ivec, block);
+        in += len;
+        out += len;
+    }
+
+    (*block) (in + residue, tmp.c + 16, key);
+
+    memcpy(tmp.c, tmp.c + 16, 16);
+    memcpy(tmp.c, in, residue);
+    (*block) (tmp.c, tmp.c, key);
+
+    for (n = 0; n < 16; ++n) {
+        unsigned char c = in[n];
+        out[n] = tmp.c[n] ^ ivec[n];
+        ivec[n] = in[n + residue];
+        tmp.c[n] = c;
+    }
+    for (residue += 16; n < residue; ++n)
+        out[n] = tmp.c[n] ^ tmp.c[n - 16];
+
+    return 16 + len + residue;
+}
+
+size_t CRYPTO_cts128_decrypt(const unsigned char *in, unsigned char *out,
+                             size_t len, const void *key,
+                             unsigned char ivec[16], cbc128_f cbc)
+{
+    size_t residue;
+    union {
+        size_t align;
+        unsigned char c[32];
+    } tmp;
+
+    if (len <= 16)
+        return 0;
+
+    if ((residue = len % 16) == 0)
+        residue = 16;
+
+    len -= 16 + residue;
+
+    if (len) {
+        (*cbc) (in, out, len, key, ivec, 0);
+        in += len;
+        out += len;
+    }
+
+    memset(tmp.c, 0, sizeof(tmp));
+    /*
+     * this places in[16] at &tmp.c[16] and decrypted block at &tmp.c[0]
+     */
+    (*cbc) (in, tmp.c, 16, key, tmp.c + 16, 0);
+
+    memcpy(tmp.c, in + 16, residue);
+#if defined(CBC_HANDLES_TRUNCATED_IO)
+    (*cbc) (tmp.c, out, 16 + residue, key, ivec, 0);
+#else
+    (*cbc) (tmp.c, tmp.c, 32, key, ivec, 0);
+    memcpy(out, tmp.c, 16 + residue);
+#endif
+    return 16 + len + residue;
+}
+
+size_t CRYPTO_nistcts128_decrypt(const unsigned char *in, unsigned char *out,
+                                 size_t len, const void *key,
+                                 unsigned char ivec[16], cbc128_f cbc)
+{
+    size_t residue;
+    union {
+        size_t align;
+        unsigned char c[32];
+    } tmp;
+
+    if (len < 16)
+        return 0;
+
+    residue = len % 16;
+
+    if (residue == 0) {
+        (*cbc) (in, out, len, key, ivec, 0);
+        return len;
+    }
+
+    len -= 16 + residue;
+
+    if (len) {
+        (*cbc) (in, out, len, key, ivec, 0);
+        in += len;
+        out += len;
+    }
+
+    memset(tmp.c, 0, sizeof(tmp));
+    /*
+     * this places in[16] at &tmp.c[16] and decrypted block at &tmp.c[0]
+     */
+    (*cbc) (in + residue, tmp.c, 16, key, tmp.c + 16, 0);
+
+    memcpy(tmp.c, in, residue);
+#if defined(CBC_HANDLES_TRUNCATED_IO)
+    (*cbc) (tmp.c, out, 16 + residue, key, ivec, 0);
+#else
+    (*cbc) (tmp.c, tmp.c, 32, key, ivec, 0);
+    memcpy(out, tmp.c, 16 + residue);
+#endif
+    return 16 + len + residue;
+}
+
+#if defined(SELFTEST)
+# include 
+# include 
+
+/* test vectors from RFC 3962 */
+static const unsigned char test_key[16] = "chicken teriyaki";
+static const unsigned char test_input[64] =
+    "I would like the" " General Gau's C"
+    "hicken, please, " "and wonton soup.";
+static const unsigned char test_iv[16] =
+    { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+
+static const unsigned char vector_17[17] = {
+    0xc6, 0x35, 0x35, 0x68, 0xf2, 0xbf, 0x8c, 0xb4,
+    0xd8, 0xa5, 0x80, 0x36, 0x2d, 0xa7, 0xff, 0x7f,
+    0x97
+};
+
+static const unsigned char vector_31[31] = {
+    0xfc, 0x00, 0x78, 0x3e, 0x0e, 0xfd, 0xb2, 0xc1,
+    0xd4, 0x45, 0xd4, 0xc8, 0xef, 0xf7, 0xed, 0x22,
+    0x97, 0x68, 0x72, 0x68, 0xd6, 0xec, 0xcc, 0xc0,
+    0xc0, 0x7b, 0x25, 0xe2, 0x5e, 0xcf, 0xe5
+};
+
+static const unsigned char vector_32[32] = {
+    0x39, 0x31, 0x25, 0x23, 0xa7, 0x86, 0x62, 0xd5,
+    0xbe, 0x7f, 0xcb, 0xcc, 0x98, 0xeb, 0xf5, 0xa8,
+    0x97, 0x68, 0x72, 0x68, 0xd6, 0xec, 0xcc, 0xc0,
+    0xc0, 0x7b, 0x25, 0xe2, 0x5e, 0xcf, 0xe5, 0x84
+};
+
+static const unsigned char vector_47[47] = {
+    0x97, 0x68, 0x72, 0x68, 0xd6, 0xec, 0xcc, 0xc0,
+    0xc0, 0x7b, 0x25, 0xe2, 0x5e, 0xcf, 0xe5, 0x84,
+    0xb3, 0xff, 0xfd, 0x94, 0x0c, 0x16, 0xa1, 0x8c,
+    0x1b, 0x55, 0x49, 0xd2, 0xf8, 0x38, 0x02, 0x9e,
+    0x39, 0x31, 0x25, 0x23, 0xa7, 0x86, 0x62, 0xd5,
+    0xbe, 0x7f, 0xcb, 0xcc, 0x98, 0xeb, 0xf5
+};
+
+static const unsigned char vector_48[48] = {
+    0x97, 0x68, 0x72, 0x68, 0xd6, 0xec, 0xcc, 0xc0,
+    0xc0, 0x7b, 0x25, 0xe2, 0x5e, 0xcf, 0xe5, 0x84,
+    0x9d, 0xad, 0x8b, 0xbb, 0x96, 0xc4, 0xcd, 0xc0,
+    0x3b, 0xc1, 0x03, 0xe1, 0xa1, 0x94, 0xbb, 0xd8,
+    0x39, 0x31, 0x25, 0x23, 0xa7, 0x86, 0x62, 0xd5,
+    0xbe, 0x7f, 0xcb, 0xcc, 0x98, 0xeb, 0xf5, 0xa8
+};
+
+static const unsigned char vector_64[64] = {
+    0x97, 0x68, 0x72, 0x68, 0xd6, 0xec, 0xcc, 0xc0,
+    0xc0, 0x7b, 0x25, 0xe2, 0x5e, 0xcf, 0xe5, 0x84,
+    0x39, 0x31, 0x25, 0x23, 0xa7, 0x86, 0x62, 0xd5,
+    0xbe, 0x7f, 0xcb, 0xcc, 0x98, 0xeb, 0xf5, 0xa8,
+    0x48, 0x07, 0xef, 0xe8, 0x36, 0xee, 0x89, 0xa5,
+    0x26, 0x73, 0x0d, 0xbc, 0x2f, 0x7b, 0xc8, 0x40,
+    0x9d, 0xad, 0x8b, 0xbb, 0x96, 0xc4, 0xcd, 0xc0,
+    0x3b, 0xc1, 0x03, 0xe1, 0xa1, 0x94, 0xbb, 0xd8
+};
+
+static AES_KEY encks, decks;
+
+void test_vector(const unsigned char *vector, size_t len)
+{
+    unsigned char iv[sizeof(test_iv)];
+    unsigned char cleartext[64], ciphertext[64];
+    size_t tail;
+
+    printf("vector_%d\n", len);
+    fflush(stdout);
+
+    if ((tail = len % 16) == 0)
+        tail = 16;
+    tail += 16;
+
+    /* test block-based encryption */
+    memcpy(iv, test_iv, sizeof(test_iv));
+    CRYPTO_cts128_encrypt_block(test_input, ciphertext, len, &encks, iv,
+                                (block128_f) AES_encrypt);
+    if (memcmp(ciphertext, vector, len))
+        fprintf(stderr, "output_%d mismatch\n", len), exit(1);
+    if (memcmp(iv, vector + len - tail, sizeof(iv)))
+        fprintf(stderr, "iv_%d mismatch\n", len), exit(1);
+
+    /* test block-based decryption */
+    memcpy(iv, test_iv, sizeof(test_iv));
+    CRYPTO_cts128_decrypt_block(ciphertext, cleartext, len, &decks, iv,
+                                (block128_f) AES_decrypt);
+    if (memcmp(cleartext, test_input, len))
+        fprintf(stderr, "input_%d mismatch\n", len), exit(2);
+    if (memcmp(iv, vector + len - tail, sizeof(iv)))
+        fprintf(stderr, "iv_%d mismatch\n", len), exit(2);
+
+    /* test streamed encryption */
+    memcpy(iv, test_iv, sizeof(test_iv));
+    CRYPTO_cts128_encrypt(test_input, ciphertext, len, &encks, iv,
+                          (cbc128_f) AES_cbc_encrypt);
+    if (memcmp(ciphertext, vector, len))
+        fprintf(stderr, "output_%d mismatch\n", len), exit(3);
+    if (memcmp(iv, vector + len - tail, sizeof(iv)))
+        fprintf(stderr, "iv_%d mismatch\n", len), exit(3);
+
+    /* test streamed decryption */
+    memcpy(iv, test_iv, sizeof(test_iv));
+    CRYPTO_cts128_decrypt(ciphertext, cleartext, len, &decks, iv,
+                          (cbc128_f) AES_cbc_encrypt);
+    if (memcmp(cleartext, test_input, len))
+        fprintf(stderr, "input_%d mismatch\n", len), exit(4);
+    if (memcmp(iv, vector + len - tail, sizeof(iv)))
+        fprintf(stderr, "iv_%d mismatch\n", len), exit(4);
+}
+
+void test_nistvector(const unsigned char *vector, size_t len)
+{
+    unsigned char iv[sizeof(test_iv)];
+    unsigned char cleartext[64], ciphertext[64], nistvector[64];
+    size_t tail;
+
+    printf("nistvector_%d\n", len);
+    fflush(stdout);
+
+    if ((tail = len % 16) == 0)
+        tail = 16;
+
+    len -= 16 + tail;
+    memcpy(nistvector, vector, len);
+    /* flip two last blocks */
+    memcpy(nistvector + len, vector + len + 16, tail);
+    memcpy(nistvector + len + tail, vector + len, 16);
+    len += 16 + tail;
+    tail = 16;
+
+    /* test block-based encryption */
+    memcpy(iv, test_iv, sizeof(test_iv));
+    CRYPTO_nistcts128_encrypt_block(test_input, ciphertext, len, &encks, iv,
+                                    (block128_f) AES_encrypt);
+    if (memcmp(ciphertext, nistvector, len))
+        fprintf(stderr, "output_%d mismatch\n", len), exit(1);
+    if (memcmp(iv, nistvector + len - tail, sizeof(iv)))
+        fprintf(stderr, "iv_%d mismatch\n", len), exit(1);
+
+    /* test block-based decryption */
+    memcpy(iv, test_iv, sizeof(test_iv));
+    CRYPTO_nistcts128_decrypt_block(ciphertext, cleartext, len, &decks, iv,
+                                    (block128_f) AES_decrypt);
+    if (memcmp(cleartext, test_input, len))
+        fprintf(stderr, "input_%d mismatch\n", len), exit(2);
+    if (memcmp(iv, nistvector + len - tail, sizeof(iv)))
+        fprintf(stderr, "iv_%d mismatch\n", len), exit(2);
+
+    /* test streamed encryption */
+    memcpy(iv, test_iv, sizeof(test_iv));
+    CRYPTO_nistcts128_encrypt(test_input, ciphertext, len, &encks, iv,
+                              (cbc128_f) AES_cbc_encrypt);
+    if (memcmp(ciphertext, nistvector, len))
+        fprintf(stderr, "output_%d mismatch\n", len), exit(3);
+    if (memcmp(iv, nistvector + len - tail, sizeof(iv)))
+        fprintf(stderr, "iv_%d mismatch\n", len), exit(3);
+
+    /* test streamed decryption */
+    memcpy(iv, test_iv, sizeof(test_iv));
+    CRYPTO_nistcts128_decrypt(ciphertext, cleartext, len, &decks, iv,
+                              (cbc128_f) AES_cbc_encrypt);
+    if (memcmp(cleartext, test_input, len))
+        fprintf(stderr, "input_%d mismatch\n", len), exit(4);
+    if (memcmp(iv, nistvector + len - tail, sizeof(iv)))
+        fprintf(stderr, "iv_%d mismatch\n", len), exit(4);
+}
+
+int main()
+{
+    AES_set_encrypt_key(test_key, 128, &encks);
+    AES_set_decrypt_key(test_key, 128, &decks);
+
+    test_vector(vector_17, sizeof(vector_17));
+    test_vector(vector_31, sizeof(vector_31));
+    test_vector(vector_32, sizeof(vector_32));
+    test_vector(vector_47, sizeof(vector_47));
+    test_vector(vector_48, sizeof(vector_48));
+    test_vector(vector_64, sizeof(vector_64));
+
+    test_nistvector(vector_17, sizeof(vector_17));
+    test_nistvector(vector_31, sizeof(vector_31));
+    test_nistvector(vector_32, sizeof(vector_32));
+    test_nistvector(vector_47, sizeof(vector_47));
+    test_nistvector(vector_48, sizeof(vector_48));
+    test_nistvector(vector_64, sizeof(vector_64));
+
+    return 0;
+}
+#endif
diff --git a/crypto/modes/gcm128.c b/crypto/modes/gcm128.c
new file mode 100644
index 000000000..df9f65476
--- /dev/null
+++ b/crypto/modes/gcm128.c
@@ -0,0 +1,2302 @@
+/*
+ * Copyright 2010-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "modes_lcl.h"
+#include 
+
+#if defined(BSWAP4) && defined(STRICT_ALIGNMENT)
+/* redefine, because alignment is ensured */
+# undef  GETU32
+# define GETU32(p)       BSWAP4(*(const u32 *)(p))
+# undef  PUTU32
+# define PUTU32(p,v)     *(u32 *)(p) = BSWAP4(v)
+#endif
+
+#define PACK(s)         ((size_t)(s)<<(sizeof(size_t)*8-16))
+#define REDUCE1BIT(V)   do { \
+        if (sizeof(size_t)==8) { \
+                u64 T = U64(0xe100000000000000) & (0-(V.lo&1)); \
+                V.lo  = (V.hi<<63)|(V.lo>>1); \
+                V.hi  = (V.hi>>1 )^T; \
+        } \
+        else { \
+                u32 T = 0xe1000000U & (0-(u32)(V.lo&1)); \
+                V.lo  = (V.hi<<63)|(V.lo>>1); \
+                V.hi  = (V.hi>>1 )^((u64)T<<32); \
+        } \
+} while(0)
+
+/*-
+ * Even though permitted values for TABLE_BITS are 8, 4 and 1, it should
+ * never be set to 8. 8 is effectively reserved for testing purposes.
+ * TABLE_BITS>1 are lookup-table-driven implementations referred to as
+ * "Shoup's" in GCM specification. In other words OpenSSL does not cover
+ * whole spectrum of possible table driven implementations. Why? In
+ * non-"Shoup's" case memory access pattern is segmented in such manner,
+ * that it's trivial to see that cache timing information can reveal
+ * fair portion of intermediate hash value. Given that ciphertext is
+ * always available to attacker, it's possible for him to attempt to
+ * deduce secret parameter H and if successful, tamper with messages
+ * [which is nothing but trivial in CTR mode]. In "Shoup's" case it's
+ * not as trivial, but there is no reason to believe that it's resistant
+ * to cache-timing attack. And the thing about "8-bit" implementation is
+ * that it consumes 16 (sixteen) times more memory, 4KB per individual
+ * key + 1KB shared. Well, on pros side it should be twice as fast as
+ * "4-bit" version. And for gcc-generated x86[_64] code, "8-bit" version
+ * was observed to run ~75% faster, closer to 100% for commercial
+ * compilers... Yet "4-bit" procedure is preferred, because it's
+ * believed to provide better security-performance balance and adequate
+ * all-round performance. "All-round" refers to things like:
+ *
+ * - shorter setup time effectively improves overall timing for
+ *   handling short messages;
+ * - larger table allocation can become unbearable because of VM
+ *   subsystem penalties (for example on Windows large enough free
+ *   results in VM working set trimming, meaning that consequent
+ *   malloc would immediately incur working set expansion);
+ * - larger table has larger cache footprint, which can affect
+ *   performance of other code paths (not necessarily even from same
+ *   thread in Hyper-Threading world);
+ *
+ * Value of 1 is not appropriate for performance reasons.
+ */
+#if     TABLE_BITS==8
+
+static void gcm_init_8bit(u128 Htable[256], u64 H[2])
+{
+    int i, j;
+    u128 V;
+
+    Htable[0].hi = 0;
+    Htable[0].lo = 0;
+    V.hi = H[0];
+    V.lo = H[1];
+
+    for (Htable[128] = V, i = 64; i > 0; i >>= 1) {
+        REDUCE1BIT(V);
+        Htable[i] = V;
+    }
+
+    for (i = 2; i < 256; i <<= 1) {
+        u128 *Hi = Htable + i, H0 = *Hi;
+        for (j = 1; j < i; ++j) {
+            Hi[j].hi = H0.hi ^ Htable[j].hi;
+            Hi[j].lo = H0.lo ^ Htable[j].lo;
+        }
+    }
+}
+
+static void gcm_gmult_8bit(u64 Xi[2], const u128 Htable[256])
+{
+    u128 Z = { 0, 0 };
+    const u8 *xi = (const u8 *)Xi + 15;
+    size_t rem, n = *xi;
+    const union {
+        long one;
+        char little;
+    } is_endian = { 1 };
+    static const size_t rem_8bit[256] = {
+        PACK(0x0000), PACK(0x01C2), PACK(0x0384), PACK(0x0246),
+        PACK(0x0708), PACK(0x06CA), PACK(0x048C), PACK(0x054E),
+        PACK(0x0E10), PACK(0x0FD2), PACK(0x0D94), PACK(0x0C56),
+        PACK(0x0918), PACK(0x08DA), PACK(0x0A9C), PACK(0x0B5E),
+        PACK(0x1C20), PACK(0x1DE2), PACK(0x1FA4), PACK(0x1E66),
+        PACK(0x1B28), PACK(0x1AEA), PACK(0x18AC), PACK(0x196E),
+        PACK(0x1230), PACK(0x13F2), PACK(0x11B4), PACK(0x1076),
+        PACK(0x1538), PACK(0x14FA), PACK(0x16BC), PACK(0x177E),
+        PACK(0x3840), PACK(0x3982), PACK(0x3BC4), PACK(0x3A06),
+        PACK(0x3F48), PACK(0x3E8A), PACK(0x3CCC), PACK(0x3D0E),
+        PACK(0x3650), PACK(0x3792), PACK(0x35D4), PACK(0x3416),
+        PACK(0x3158), PACK(0x309A), PACK(0x32DC), PACK(0x331E),
+        PACK(0x2460), PACK(0x25A2), PACK(0x27E4), PACK(0x2626),
+        PACK(0x2368), PACK(0x22AA), PACK(0x20EC), PACK(0x212E),
+        PACK(0x2A70), PACK(0x2BB2), PACK(0x29F4), PACK(0x2836),
+        PACK(0x2D78), PACK(0x2CBA), PACK(0x2EFC), PACK(0x2F3E),
+        PACK(0x7080), PACK(0x7142), PACK(0x7304), PACK(0x72C6),
+        PACK(0x7788), PACK(0x764A), PACK(0x740C), PACK(0x75CE),
+        PACK(0x7E90), PACK(0x7F52), PACK(0x7D14), PACK(0x7CD6),
+        PACK(0x7998), PACK(0x785A), PACK(0x7A1C), PACK(0x7BDE),
+        PACK(0x6CA0), PACK(0x6D62), PACK(0x6F24), PACK(0x6EE6),
+        PACK(0x6BA8), PACK(0x6A6A), PACK(0x682C), PACK(0x69EE),
+        PACK(0x62B0), PACK(0x6372), PACK(0x6134), PACK(0x60F6),
+        PACK(0x65B8), PACK(0x647A), PACK(0x663C), PACK(0x67FE),
+        PACK(0x48C0), PACK(0x4902), PACK(0x4B44), PACK(0x4A86),
+        PACK(0x4FC8), PACK(0x4E0A), PACK(0x4C4C), PACK(0x4D8E),
+        PACK(0x46D0), PACK(0x4712), PACK(0x4554), PACK(0x4496),
+        PACK(0x41D8), PACK(0x401A), PACK(0x425C), PACK(0x439E),
+        PACK(0x54E0), PACK(0x5522), PACK(0x5764), PACK(0x56A6),
+        PACK(0x53E8), PACK(0x522A), PACK(0x506C), PACK(0x51AE),
+        PACK(0x5AF0), PACK(0x5B32), PACK(0x5974), PACK(0x58B6),
+        PACK(0x5DF8), PACK(0x5C3A), PACK(0x5E7C), PACK(0x5FBE),
+        PACK(0xE100), PACK(0xE0C2), PACK(0xE284), PACK(0xE346),
+        PACK(0xE608), PACK(0xE7CA), PACK(0xE58C), PACK(0xE44E),
+        PACK(0xEF10), PACK(0xEED2), PACK(0xEC94), PACK(0xED56),
+        PACK(0xE818), PACK(0xE9DA), PACK(0xEB9C), PACK(0xEA5E),
+        PACK(0xFD20), PACK(0xFCE2), PACK(0xFEA4), PACK(0xFF66),
+        PACK(0xFA28), PACK(0xFBEA), PACK(0xF9AC), PACK(0xF86E),
+        PACK(0xF330), PACK(0xF2F2), PACK(0xF0B4), PACK(0xF176),
+        PACK(0xF438), PACK(0xF5FA), PACK(0xF7BC), PACK(0xF67E),
+        PACK(0xD940), PACK(0xD882), PACK(0xDAC4), PACK(0xDB06),
+        PACK(0xDE48), PACK(0xDF8A), PACK(0xDDCC), PACK(0xDC0E),
+        PACK(0xD750), PACK(0xD692), PACK(0xD4D4), PACK(0xD516),
+        PACK(0xD058), PACK(0xD19A), PACK(0xD3DC), PACK(0xD21E),
+        PACK(0xC560), PACK(0xC4A2), PACK(0xC6E4), PACK(0xC726),
+        PACK(0xC268), PACK(0xC3AA), PACK(0xC1EC), PACK(0xC02E),
+        PACK(0xCB70), PACK(0xCAB2), PACK(0xC8F4), PACK(0xC936),
+        PACK(0xCC78), PACK(0xCDBA), PACK(0xCFFC), PACK(0xCE3E),
+        PACK(0x9180), PACK(0x9042), PACK(0x9204), PACK(0x93C6),
+        PACK(0x9688), PACK(0x974A), PACK(0x950C), PACK(0x94CE),
+        PACK(0x9F90), PACK(0x9E52), PACK(0x9C14), PACK(0x9DD6),
+        PACK(0x9898), PACK(0x995A), PACK(0x9B1C), PACK(0x9ADE),
+        PACK(0x8DA0), PACK(0x8C62), PACK(0x8E24), PACK(0x8FE6),
+        PACK(0x8AA8), PACK(0x8B6A), PACK(0x892C), PACK(0x88EE),
+        PACK(0x83B0), PACK(0x8272), PACK(0x8034), PACK(0x81F6),
+        PACK(0x84B8), PACK(0x857A), PACK(0x873C), PACK(0x86FE),
+        PACK(0xA9C0), PACK(0xA802), PACK(0xAA44), PACK(0xAB86),
+        PACK(0xAEC8), PACK(0xAF0A), PACK(0xAD4C), PACK(0xAC8E),
+        PACK(0xA7D0), PACK(0xA612), PACK(0xA454), PACK(0xA596),
+        PACK(0xA0D8), PACK(0xA11A), PACK(0xA35C), PACK(0xA29E),
+        PACK(0xB5E0), PACK(0xB422), PACK(0xB664), PACK(0xB7A6),
+        PACK(0xB2E8), PACK(0xB32A), PACK(0xB16C), PACK(0xB0AE),
+        PACK(0xBBF0), PACK(0xBA32), PACK(0xB874), PACK(0xB9B6),
+        PACK(0xBCF8), PACK(0xBD3A), PACK(0xBF7C), PACK(0xBEBE)
+    };
+
+    while (1) {
+        Z.hi ^= Htable[n].hi;
+        Z.lo ^= Htable[n].lo;
+
+        if ((u8 *)Xi == xi)
+            break;
+
+        n = *(--xi);
+
+        rem = (size_t)Z.lo & 0xff;
+        Z.lo = (Z.hi << 56) | (Z.lo >> 8);
+        Z.hi = (Z.hi >> 8);
+        if (sizeof(size_t) == 8)
+            Z.hi ^= rem_8bit[rem];
+        else
+            Z.hi ^= (u64)rem_8bit[rem] << 32;
+    }
+
+    if (is_endian.little) {
+# ifdef BSWAP8
+        Xi[0] = BSWAP8(Z.hi);
+        Xi[1] = BSWAP8(Z.lo);
+# else
+        u8 *p = (u8 *)Xi;
+        u32 v;
+        v = (u32)(Z.hi >> 32);
+        PUTU32(p, v);
+        v = (u32)(Z.hi);
+        PUTU32(p + 4, v);
+        v = (u32)(Z.lo >> 32);
+        PUTU32(p + 8, v);
+        v = (u32)(Z.lo);
+        PUTU32(p + 12, v);
+# endif
+    } else {
+        Xi[0] = Z.hi;
+        Xi[1] = Z.lo;
+    }
+}
+
+# define GCM_MUL(ctx,Xi)   gcm_gmult_8bit(ctx->Xi.u,ctx->Htable)
+
+#elif   TABLE_BITS==4
+
+static void gcm_init_4bit(u128 Htable[16], u64 H[2])
+{
+    u128 V;
+# if defined(OPENSSL_SMALL_FOOTPRINT)
+    int i;
+# endif
+
+    Htable[0].hi = 0;
+    Htable[0].lo = 0;
+    V.hi = H[0];
+    V.lo = H[1];
+
+# if defined(OPENSSL_SMALL_FOOTPRINT)
+    for (Htable[8] = V, i = 4; i > 0; i >>= 1) {
+        REDUCE1BIT(V);
+        Htable[i] = V;
+    }
+
+    for (i = 2; i < 16; i <<= 1) {
+        u128 *Hi = Htable + i;
+        int j;
+        for (V = *Hi, j = 1; j < i; ++j) {
+            Hi[j].hi = V.hi ^ Htable[j].hi;
+            Hi[j].lo = V.lo ^ Htable[j].lo;
+        }
+    }
+# else
+    Htable[8] = V;
+    REDUCE1BIT(V);
+    Htable[4] = V;
+    REDUCE1BIT(V);
+    Htable[2] = V;
+    REDUCE1BIT(V);
+    Htable[1] = V;
+    Htable[3].hi = V.hi ^ Htable[2].hi, Htable[3].lo = V.lo ^ Htable[2].lo;
+    V = Htable[4];
+    Htable[5].hi = V.hi ^ Htable[1].hi, Htable[5].lo = V.lo ^ Htable[1].lo;
+    Htable[6].hi = V.hi ^ Htable[2].hi, Htable[6].lo = V.lo ^ Htable[2].lo;
+    Htable[7].hi = V.hi ^ Htable[3].hi, Htable[7].lo = V.lo ^ Htable[3].lo;
+    V = Htable[8];
+    Htable[9].hi = V.hi ^ Htable[1].hi, Htable[9].lo = V.lo ^ Htable[1].lo;
+    Htable[10].hi = V.hi ^ Htable[2].hi, Htable[10].lo = V.lo ^ Htable[2].lo;
+    Htable[11].hi = V.hi ^ Htable[3].hi, Htable[11].lo = V.lo ^ Htable[3].lo;
+    Htable[12].hi = V.hi ^ Htable[4].hi, Htable[12].lo = V.lo ^ Htable[4].lo;
+    Htable[13].hi = V.hi ^ Htable[5].hi, Htable[13].lo = V.lo ^ Htable[5].lo;
+    Htable[14].hi = V.hi ^ Htable[6].hi, Htable[14].lo = V.lo ^ Htable[6].lo;
+    Htable[15].hi = V.hi ^ Htable[7].hi, Htable[15].lo = V.lo ^ Htable[7].lo;
+# endif
+# if defined(GHASH_ASM) && (defined(__arm__) || defined(__arm))
+    /*
+     * ARM assembler expects specific dword order in Htable.
+     */
+    {
+        int j;
+        const union {
+            long one;
+            char little;
+        } is_endian = { 1 };
+
+        if (is_endian.little)
+            for (j = 0; j < 16; ++j) {
+                V = Htable[j];
+                Htable[j].hi = V.lo;
+                Htable[j].lo = V.hi;
+        } else
+            for (j = 0; j < 16; ++j) {
+                V = Htable[j];
+                Htable[j].hi = V.lo << 32 | V.lo >> 32;
+                Htable[j].lo = V.hi << 32 | V.hi >> 32;
+            }
+    }
+# endif
+}
+
+# ifndef GHASH_ASM
+static const size_t rem_4bit[16] = {
+    PACK(0x0000), PACK(0x1C20), PACK(0x3840), PACK(0x2460),
+    PACK(0x7080), PACK(0x6CA0), PACK(0x48C0), PACK(0x54E0),
+    PACK(0xE100), PACK(0xFD20), PACK(0xD940), PACK(0xC560),
+    PACK(0x9180), PACK(0x8DA0), PACK(0xA9C0), PACK(0xB5E0)
+};
+
+static void gcm_gmult_4bit(u64 Xi[2], const u128 Htable[16])
+{
+    u128 Z;
+    int cnt = 15;
+    size_t rem, nlo, nhi;
+    const union {
+        long one;
+        char little;
+    } is_endian = { 1 };
+
+    nlo = ((const u8 *)Xi)[15];
+    nhi = nlo >> 4;
+    nlo &= 0xf;
+
+    Z.hi = Htable[nlo].hi;
+    Z.lo = Htable[nlo].lo;
+
+    while (1) {
+        rem = (size_t)Z.lo & 0xf;
+        Z.lo = (Z.hi << 60) | (Z.lo >> 4);
+        Z.hi = (Z.hi >> 4);
+        if (sizeof(size_t) == 8)
+            Z.hi ^= rem_4bit[rem];
+        else
+            Z.hi ^= (u64)rem_4bit[rem] << 32;
+
+        Z.hi ^= Htable[nhi].hi;
+        Z.lo ^= Htable[nhi].lo;
+
+        if (--cnt < 0)
+            break;
+
+        nlo = ((const u8 *)Xi)[cnt];
+        nhi = nlo >> 4;
+        nlo &= 0xf;
+
+        rem = (size_t)Z.lo & 0xf;
+        Z.lo = (Z.hi << 60) | (Z.lo >> 4);
+        Z.hi = (Z.hi >> 4);
+        if (sizeof(size_t) == 8)
+            Z.hi ^= rem_4bit[rem];
+        else
+            Z.hi ^= (u64)rem_4bit[rem] << 32;
+
+        Z.hi ^= Htable[nlo].hi;
+        Z.lo ^= Htable[nlo].lo;
+    }
+
+    if (is_endian.little) {
+#  ifdef BSWAP8
+        Xi[0] = BSWAP8(Z.hi);
+        Xi[1] = BSWAP8(Z.lo);
+#  else
+        u8 *p = (u8 *)Xi;
+        u32 v;
+        v = (u32)(Z.hi >> 32);
+        PUTU32(p, v);
+        v = (u32)(Z.hi);
+        PUTU32(p + 4, v);
+        v = (u32)(Z.lo >> 32);
+        PUTU32(p + 8, v);
+        v = (u32)(Z.lo);
+        PUTU32(p + 12, v);
+#  endif
+    } else {
+        Xi[0] = Z.hi;
+        Xi[1] = Z.lo;
+    }
+}
+
+#  if !defined(OPENSSL_SMALL_FOOTPRINT)
+/*
+ * Streamed gcm_mult_4bit, see CRYPTO_gcm128_[en|de]crypt for
+ * details... Compiler-generated code doesn't seem to give any
+ * performance improvement, at least not on x86[_64]. It's here
+ * mostly as reference and a placeholder for possible future
+ * non-trivial optimization[s]...
+ */
+static void gcm_ghash_4bit(u64 Xi[2], const u128 Htable[16],
+                           const u8 *inp, size_t len)
+{
+    u128 Z;
+    int cnt;
+    size_t rem, nlo, nhi;
+    const union {
+        long one;
+        char little;
+    } is_endian = { 1 };
+
+#   if 1
+    do {
+        cnt = 15;
+        nlo = ((const u8 *)Xi)[15];
+        nlo ^= inp[15];
+        nhi = nlo >> 4;
+        nlo &= 0xf;
+
+        Z.hi = Htable[nlo].hi;
+        Z.lo = Htable[nlo].lo;
+
+        while (1) {
+            rem = (size_t)Z.lo & 0xf;
+            Z.lo = (Z.hi << 60) | (Z.lo >> 4);
+            Z.hi = (Z.hi >> 4);
+            if (sizeof(size_t) == 8)
+                Z.hi ^= rem_4bit[rem];
+            else
+                Z.hi ^= (u64)rem_4bit[rem] << 32;
+
+            Z.hi ^= Htable[nhi].hi;
+            Z.lo ^= Htable[nhi].lo;
+
+            if (--cnt < 0)
+                break;
+
+            nlo = ((const u8 *)Xi)[cnt];
+            nlo ^= inp[cnt];
+            nhi = nlo >> 4;
+            nlo &= 0xf;
+
+            rem = (size_t)Z.lo & 0xf;
+            Z.lo = (Z.hi << 60) | (Z.lo >> 4);
+            Z.hi = (Z.hi >> 4);
+            if (sizeof(size_t) == 8)
+                Z.hi ^= rem_4bit[rem];
+            else
+                Z.hi ^= (u64)rem_4bit[rem] << 32;
+
+            Z.hi ^= Htable[nlo].hi;
+            Z.lo ^= Htable[nlo].lo;
+        }
+#   else
+    /*
+     * Extra 256+16 bytes per-key plus 512 bytes shared tables
+     * [should] give ~50% improvement... One could have PACK()-ed
+     * the rem_8bit even here, but the priority is to minimize
+     * cache footprint...
+     */
+    u128 Hshr4[16];             /* Htable shifted right by 4 bits */
+    u8 Hshl4[16];               /* Htable shifted left by 4 bits */
+    static const unsigned short rem_8bit[256] = {
+        0x0000, 0x01C2, 0x0384, 0x0246, 0x0708, 0x06CA, 0x048C, 0x054E,
+        0x0E10, 0x0FD2, 0x0D94, 0x0C56, 0x0918, 0x08DA, 0x0A9C, 0x0B5E,
+        0x1C20, 0x1DE2, 0x1FA4, 0x1E66, 0x1B28, 0x1AEA, 0x18AC, 0x196E,
+        0x1230, 0x13F2, 0x11B4, 0x1076, 0x1538, 0x14FA, 0x16BC, 0x177E,
+        0x3840, 0x3982, 0x3BC4, 0x3A06, 0x3F48, 0x3E8A, 0x3CCC, 0x3D0E,
+        0x3650, 0x3792, 0x35D4, 0x3416, 0x3158, 0x309A, 0x32DC, 0x331E,
+        0x2460, 0x25A2, 0x27E4, 0x2626, 0x2368, 0x22AA, 0x20EC, 0x212E,
+        0x2A70, 0x2BB2, 0x29F4, 0x2836, 0x2D78, 0x2CBA, 0x2EFC, 0x2F3E,
+        0x7080, 0x7142, 0x7304, 0x72C6, 0x7788, 0x764A, 0x740C, 0x75CE,
+        0x7E90, 0x7F52, 0x7D14, 0x7CD6, 0x7998, 0x785A, 0x7A1C, 0x7BDE,
+        0x6CA0, 0x6D62, 0x6F24, 0x6EE6, 0x6BA8, 0x6A6A, 0x682C, 0x69EE,
+        0x62B0, 0x6372, 0x6134, 0x60F6, 0x65B8, 0x647A, 0x663C, 0x67FE,
+        0x48C0, 0x4902, 0x4B44, 0x4A86, 0x4FC8, 0x4E0A, 0x4C4C, 0x4D8E,
+        0x46D0, 0x4712, 0x4554, 0x4496, 0x41D8, 0x401A, 0x425C, 0x439E,
+        0x54E0, 0x5522, 0x5764, 0x56A6, 0x53E8, 0x522A, 0x506C, 0x51AE,
+        0x5AF0, 0x5B32, 0x5974, 0x58B6, 0x5DF8, 0x5C3A, 0x5E7C, 0x5FBE,
+        0xE100, 0xE0C2, 0xE284, 0xE346, 0xE608, 0xE7CA, 0xE58C, 0xE44E,
+        0xEF10, 0xEED2, 0xEC94, 0xED56, 0xE818, 0xE9DA, 0xEB9C, 0xEA5E,
+        0xFD20, 0xFCE2, 0xFEA4, 0xFF66, 0xFA28, 0xFBEA, 0xF9AC, 0xF86E,
+        0xF330, 0xF2F2, 0xF0B4, 0xF176, 0xF438, 0xF5FA, 0xF7BC, 0xF67E,
+        0xD940, 0xD882, 0xDAC4, 0xDB06, 0xDE48, 0xDF8A, 0xDDCC, 0xDC0E,
+        0xD750, 0xD692, 0xD4D4, 0xD516, 0xD058, 0xD19A, 0xD3DC, 0xD21E,
+        0xC560, 0xC4A2, 0xC6E4, 0xC726, 0xC268, 0xC3AA, 0xC1EC, 0xC02E,
+        0xCB70, 0xCAB2, 0xC8F4, 0xC936, 0xCC78, 0xCDBA, 0xCFFC, 0xCE3E,
+        0x9180, 0x9042, 0x9204, 0x93C6, 0x9688, 0x974A, 0x950C, 0x94CE,
+        0x9F90, 0x9E52, 0x9C14, 0x9DD6, 0x9898, 0x995A, 0x9B1C, 0x9ADE,
+        0x8DA0, 0x8C62, 0x8E24, 0x8FE6, 0x8AA8, 0x8B6A, 0x892C, 0x88EE,
+        0x83B0, 0x8272, 0x8034, 0x81F6, 0x84B8, 0x857A, 0x873C, 0x86FE,
+        0xA9C0, 0xA802, 0xAA44, 0xAB86, 0xAEC8, 0xAF0A, 0xAD4C, 0xAC8E,
+        0xA7D0, 0xA612, 0xA454, 0xA596, 0xA0D8, 0xA11A, 0xA35C, 0xA29E,
+        0xB5E0, 0xB422, 0xB664, 0xB7A6, 0xB2E8, 0xB32A, 0xB16C, 0xB0AE,
+        0xBBF0, 0xBA32, 0xB874, 0xB9B6, 0xBCF8, 0xBD3A, 0xBF7C, 0xBEBE
+    };
+    /*
+     * This pre-processing phase slows down procedure by approximately
+     * same time as it makes each loop spin faster. In other words
+     * single block performance is approximately same as straightforward
+     * "4-bit" implementation, and then it goes only faster...
+     */
+    for (cnt = 0; cnt < 16; ++cnt) {
+        Z.hi = Htable[cnt].hi;
+        Z.lo = Htable[cnt].lo;
+        Hshr4[cnt].lo = (Z.hi << 60) | (Z.lo >> 4);
+        Hshr4[cnt].hi = (Z.hi >> 4);
+        Hshl4[cnt] = (u8)(Z.lo << 4);
+    }
+
+    do {
+        for (Z.lo = 0, Z.hi = 0, cnt = 15; cnt; --cnt) {
+            nlo = ((const u8 *)Xi)[cnt];
+            nlo ^= inp[cnt];
+            nhi = nlo >> 4;
+            nlo &= 0xf;
+
+            Z.hi ^= Htable[nlo].hi;
+            Z.lo ^= Htable[nlo].lo;
+
+            rem = (size_t)Z.lo & 0xff;
+
+            Z.lo = (Z.hi << 56) | (Z.lo >> 8);
+            Z.hi = (Z.hi >> 8);
+
+            Z.hi ^= Hshr4[nhi].hi;
+            Z.lo ^= Hshr4[nhi].lo;
+            Z.hi ^= (u64)rem_8bit[rem ^ Hshl4[nhi]] << 48;
+        }
+
+        nlo = ((const u8 *)Xi)[0];
+        nlo ^= inp[0];
+        nhi = nlo >> 4;
+        nlo &= 0xf;
+
+        Z.hi ^= Htable[nlo].hi;
+        Z.lo ^= Htable[nlo].lo;
+
+        rem = (size_t)Z.lo & 0xf;
+
+        Z.lo = (Z.hi << 60) | (Z.lo >> 4);
+        Z.hi = (Z.hi >> 4);
+
+        Z.hi ^= Htable[nhi].hi;
+        Z.lo ^= Htable[nhi].lo;
+        Z.hi ^= ((u64)rem_8bit[rem << 4]) << 48;
+#   endif
+
+        if (is_endian.little) {
+#   ifdef BSWAP8
+            Xi[0] = BSWAP8(Z.hi);
+            Xi[1] = BSWAP8(Z.lo);
+#   else
+            u8 *p = (u8 *)Xi;
+            u32 v;
+            v = (u32)(Z.hi >> 32);
+            PUTU32(p, v);
+            v = (u32)(Z.hi);
+            PUTU32(p + 4, v);
+            v = (u32)(Z.lo >> 32);
+            PUTU32(p + 8, v);
+            v = (u32)(Z.lo);
+            PUTU32(p + 12, v);
+#   endif
+        } else {
+            Xi[0] = Z.hi;
+            Xi[1] = Z.lo;
+        }
+    } while (inp += 16, len -= 16);
+}
+#  endif
+# else
+void gcm_gmult_4bit(u64 Xi[2], const u128 Htable[16]);
+void gcm_ghash_4bit(u64 Xi[2], const u128 Htable[16], const u8 *inp,
+                    size_t len);
+# endif
+
+# define GCM_MUL(ctx,Xi)   gcm_gmult_4bit(ctx->Xi.u,ctx->Htable)
+# if defined(GHASH_ASM) || !defined(OPENSSL_SMALL_FOOTPRINT)
+#  define GHASH(ctx,in,len) gcm_ghash_4bit((ctx)->Xi.u,(ctx)->Htable,in,len)
+/*
+ * GHASH_CHUNK is "stride parameter" missioned to mitigate cache trashing
+ * effect. In other words idea is to hash data while it's still in L1 cache
+ * after encryption pass...
+ */
+#  define GHASH_CHUNK       (3*1024)
+# endif
+
+#else                           /* TABLE_BITS */
+
+static void gcm_gmult_1bit(u64 Xi[2], const u64 H[2])
+{
+    u128 V, Z = { 0, 0 };
+    long X;
+    int i, j;
+    const long *xi = (const long *)Xi;
+    const union {
+        long one;
+        char little;
+    } is_endian = { 1 };
+
+    V.hi = H[0];                /* H is in host byte order, no byte swapping */
+    V.lo = H[1];
+
+    for (j = 0; j < 16 / sizeof(long); ++j) {
+        if (is_endian.little) {
+            if (sizeof(long) == 8) {
+# ifdef BSWAP8
+                X = (long)(BSWAP8(xi[j]));
+# else
+                const u8 *p = (const u8 *)(xi + j);
+                X = (long)((u64)GETU32(p) << 32 | GETU32(p + 4));
+# endif
+            } else {
+                const u8 *p = (const u8 *)(xi + j);
+                X = (long)GETU32(p);
+            }
+        } else
+            X = xi[j];
+
+        for (i = 0; i < 8 * sizeof(long); ++i, X <<= 1) {
+            u64 M = (u64)(X >> (8 * sizeof(long) - 1));
+            Z.hi ^= V.hi & M;
+            Z.lo ^= V.lo & M;
+
+            REDUCE1BIT(V);
+        }
+    }
+
+    if (is_endian.little) {
+# ifdef BSWAP8
+        Xi[0] = BSWAP8(Z.hi);
+        Xi[1] = BSWAP8(Z.lo);
+# else
+        u8 *p = (u8 *)Xi;
+        u32 v;
+        v = (u32)(Z.hi >> 32);
+        PUTU32(p, v);
+        v = (u32)(Z.hi);
+        PUTU32(p + 4, v);
+        v = (u32)(Z.lo >> 32);
+        PUTU32(p + 8, v);
+        v = (u32)(Z.lo);
+        PUTU32(p + 12, v);
+# endif
+    } else {
+        Xi[0] = Z.hi;
+        Xi[1] = Z.lo;
+    }
+}
+
+# define GCM_MUL(ctx,Xi)   gcm_gmult_1bit(ctx->Xi.u,ctx->H.u)
+
+#endif
+
+#if     TABLE_BITS==4 && (defined(GHASH_ASM) || defined(OPENSSL_CPUID_OBJ))
+# if    !defined(I386_ONLY) && \
+        (defined(__i386)        || defined(__i386__)    || \
+         defined(__x86_64)      || defined(__x86_64__)  || \
+         defined(_M_IX86)       || defined(_M_AMD64)    || defined(_M_X64))
+#  define GHASH_ASM_X86_OR_64
+#  define GCM_FUNCREF_4BIT
+extern unsigned int OPENSSL_ia32cap_P[];
+
+void gcm_init_clmul(u128 Htable[16], const u64 Xi[2]);
+void gcm_gmult_clmul(u64 Xi[2], const u128 Htable[16]);
+void gcm_ghash_clmul(u64 Xi[2], const u128 Htable[16], const u8 *inp,
+                     size_t len);
+
+#  if defined(__i386) || defined(__i386__) || defined(_M_IX86)
+#   define gcm_init_avx   gcm_init_clmul
+#   define gcm_gmult_avx  gcm_gmult_clmul
+#   define gcm_ghash_avx  gcm_ghash_clmul
+#  else
+void gcm_init_avx(u128 Htable[16], const u64 Xi[2]);
+void gcm_gmult_avx(u64 Xi[2], const u128 Htable[16]);
+void gcm_ghash_avx(u64 Xi[2], const u128 Htable[16], const u8 *inp,
+                   size_t len);
+#  endif
+
+#  if   defined(__i386) || defined(__i386__) || defined(_M_IX86)
+#   define GHASH_ASM_X86
+void gcm_gmult_4bit_mmx(u64 Xi[2], const u128 Htable[16]);
+void gcm_ghash_4bit_mmx(u64 Xi[2], const u128 Htable[16], const u8 *inp,
+                        size_t len);
+
+void gcm_gmult_4bit_x86(u64 Xi[2], const u128 Htable[16]);
+void gcm_ghash_4bit_x86(u64 Xi[2], const u128 Htable[16], const u8 *inp,
+                        size_t len);
+#  endif
+# elif defined(__arm__) || defined(__arm) || defined(__aarch64__)
+#  include "arm_arch.h"
+#  if __ARM_MAX_ARCH__>=7
+#   define GHASH_ASM_ARM
+#   define GCM_FUNCREF_4BIT
+#   define PMULL_CAPABLE        (OPENSSL_armcap_P & ARMV8_PMULL)
+#   if defined(__arm__) || defined(__arm)
+#    define NEON_CAPABLE        (OPENSSL_armcap_P & ARMV7_NEON)
+#   endif
+void gcm_init_neon(u128 Htable[16], const u64 Xi[2]);
+void gcm_gmult_neon(u64 Xi[2], const u128 Htable[16]);
+void gcm_ghash_neon(u64 Xi[2], const u128 Htable[16], const u8 *inp,
+                    size_t len);
+void gcm_init_v8(u128 Htable[16], const u64 Xi[2]);
+void gcm_gmult_v8(u64 Xi[2], const u128 Htable[16]);
+void gcm_ghash_v8(u64 Xi[2], const u128 Htable[16], const u8 *inp,
+                  size_t len);
+#  endif
+# elif defined(__sparc__) || defined(__sparc)
+#  include "sparc_arch.h"
+#  define GHASH_ASM_SPARC
+#  define GCM_FUNCREF_4BIT
+extern unsigned int OPENSSL_sparcv9cap_P[];
+void gcm_init_vis3(u128 Htable[16], const u64 Xi[2]);
+void gcm_gmult_vis3(u64 Xi[2], const u128 Htable[16]);
+void gcm_ghash_vis3(u64 Xi[2], const u128 Htable[16], const u8 *inp,
+                    size_t len);
+# elif defined(OPENSSL_CPUID_OBJ) && (defined(__powerpc__) || defined(__ppc__) || defined(_ARCH_PPC))
+#  include "ppc_arch.h"
+#  define GHASH_ASM_PPC
+#  define GCM_FUNCREF_4BIT
+void gcm_init_p8(u128 Htable[16], const u64 Xi[2]);
+void gcm_gmult_p8(u64 Xi[2], const u128 Htable[16]);
+void gcm_ghash_p8(u64 Xi[2], const u128 Htable[16], const u8 *inp,
+                  size_t len);
+# endif
+#endif
+
+#ifdef GCM_FUNCREF_4BIT
+# undef  GCM_MUL
+# define GCM_MUL(ctx,Xi)        (*gcm_gmult_p)(ctx->Xi.u,ctx->Htable)
+# ifdef GHASH
+#  undef  GHASH
+#  define GHASH(ctx,in,len)     (*gcm_ghash_p)(ctx->Xi.u,ctx->Htable,in,len)
+# endif
+#endif
+
+void CRYPTO_gcm128_init(GCM128_CONTEXT *ctx, void *key, block128_f block)
+{
+    const union {
+        long one;
+        char little;
+    } is_endian = { 1 };
+
+    memset(ctx, 0, sizeof(*ctx));
+    ctx->block = block;
+    ctx->key = key;
+
+    (*block) (ctx->H.c, ctx->H.c, key);
+
+    if (is_endian.little) {
+        /* H is stored in host byte order */
+#ifdef BSWAP8
+        ctx->H.u[0] = BSWAP8(ctx->H.u[0]);
+        ctx->H.u[1] = BSWAP8(ctx->H.u[1]);
+#else
+        u8 *p = ctx->H.c;
+        u64 hi, lo;
+        hi = (u64)GETU32(p) << 32 | GETU32(p + 4);
+        lo = (u64)GETU32(p + 8) << 32 | GETU32(p + 12);
+        ctx->H.u[0] = hi;
+        ctx->H.u[1] = lo;
+#endif
+    }
+#if     TABLE_BITS==8
+    gcm_init_8bit(ctx->Htable, ctx->H.u);
+#elif   TABLE_BITS==4
+# if    defined(GHASH)
+#  define CTX__GHASH(f) (ctx->ghash = (f))
+# else
+#  define CTX__GHASH(f) (ctx->ghash = NULL)
+# endif
+# if    defined(GHASH_ASM_X86_OR_64)
+#  if   !defined(GHASH_ASM_X86) || defined(OPENSSL_IA32_SSE2)
+    if (OPENSSL_ia32cap_P[0] & (1 << 24) && /* check FXSR bit */
+        OPENSSL_ia32cap_P[1] & (1 << 1)) { /* check PCLMULQDQ bit */
+        if (((OPENSSL_ia32cap_P[1] >> 22) & 0x41) == 0x41) { /* AVX+MOVBE */
+            gcm_init_avx(ctx->Htable, ctx->H.u);
+            ctx->gmult = gcm_gmult_avx;
+            CTX__GHASH(gcm_ghash_avx);
+        } else {
+            gcm_init_clmul(ctx->Htable, ctx->H.u);
+            ctx->gmult = gcm_gmult_clmul;
+            CTX__GHASH(gcm_ghash_clmul);
+        }
+        return;
+    }
+#  endif
+    gcm_init_4bit(ctx->Htable, ctx->H.u);
+#  if   defined(GHASH_ASM_X86)  /* x86 only */
+#   if  defined(OPENSSL_IA32_SSE2)
+    if (OPENSSL_ia32cap_P[0] & (1 << 25)) { /* check SSE bit */
+#   else
+    if (OPENSSL_ia32cap_P[0] & (1 << 23)) { /* check MMX bit */
+#   endif
+        ctx->gmult = gcm_gmult_4bit_mmx;
+        CTX__GHASH(gcm_ghash_4bit_mmx);
+    } else {
+        ctx->gmult = gcm_gmult_4bit_x86;
+        CTX__GHASH(gcm_ghash_4bit_x86);
+    }
+#  else
+    ctx->gmult = gcm_gmult_4bit;
+    CTX__GHASH(gcm_ghash_4bit);
+#  endif
+# elif  defined(GHASH_ASM_ARM)
+#  ifdef PMULL_CAPABLE
+    if (PMULL_CAPABLE) {
+        gcm_init_v8(ctx->Htable, ctx->H.u);
+        ctx->gmult = gcm_gmult_v8;
+        CTX__GHASH(gcm_ghash_v8);
+    } else
+#  endif
+#  ifdef NEON_CAPABLE
+    if (NEON_CAPABLE) {
+        gcm_init_neon(ctx->Htable, ctx->H.u);
+        ctx->gmult = gcm_gmult_neon;
+        CTX__GHASH(gcm_ghash_neon);
+    } else
+#  endif
+    {
+        gcm_init_4bit(ctx->Htable, ctx->H.u);
+        ctx->gmult = gcm_gmult_4bit;
+        CTX__GHASH(gcm_ghash_4bit);
+    }
+# elif  defined(GHASH_ASM_SPARC)
+    if (OPENSSL_sparcv9cap_P[0] & SPARCV9_VIS3) {
+        gcm_init_vis3(ctx->Htable, ctx->H.u);
+        ctx->gmult = gcm_gmult_vis3;
+        CTX__GHASH(gcm_ghash_vis3);
+    } else {
+        gcm_init_4bit(ctx->Htable, ctx->H.u);
+        ctx->gmult = gcm_gmult_4bit;
+        CTX__GHASH(gcm_ghash_4bit);
+    }
+# elif  defined(GHASH_ASM_PPC)
+    if (OPENSSL_ppccap_P & PPC_CRYPTO207) {
+        gcm_init_p8(ctx->Htable, ctx->H.u);
+        ctx->gmult = gcm_gmult_p8;
+        CTX__GHASH(gcm_ghash_p8);
+    } else {
+        gcm_init_4bit(ctx->Htable, ctx->H.u);
+        ctx->gmult = gcm_gmult_4bit;
+        CTX__GHASH(gcm_ghash_4bit);
+    }
+# else
+    gcm_init_4bit(ctx->Htable, ctx->H.u);
+# endif
+# undef CTX__GHASH
+#endif
+}
+
+void CRYPTO_gcm128_setiv(GCM128_CONTEXT *ctx, const unsigned char *iv,
+                         size_t len)
+{
+    const union {
+        long one;
+        char little;
+    } is_endian = { 1 };
+    unsigned int ctr;
+#ifdef GCM_FUNCREF_4BIT
+    void (*gcm_gmult_p) (u64 Xi[2], const u128 Htable[16]) = ctx->gmult;
+#endif
+
+    ctx->Yi.u[0] = 0;
+    ctx->Yi.u[1] = 0;
+    ctx->Xi.u[0] = 0;
+    ctx->Xi.u[1] = 0;
+    ctx->len.u[0] = 0;          /* AAD length */
+    ctx->len.u[1] = 0;          /* message length */
+    ctx->ares = 0;
+    ctx->mres = 0;
+
+    if (len == 12) {
+        memcpy(ctx->Yi.c, iv, 12);
+        ctx->Yi.c[15] = 1;
+        ctr = 1;
+    } else {
+        size_t i;
+        u64 len0 = len;
+
+        while (len >= 16) {
+            for (i = 0; i < 16; ++i)
+                ctx->Yi.c[i] ^= iv[i];
+            GCM_MUL(ctx, Yi);
+            iv += 16;
+            len -= 16;
+        }
+        if (len) {
+            for (i = 0; i < len; ++i)
+                ctx->Yi.c[i] ^= iv[i];
+            GCM_MUL(ctx, Yi);
+        }
+        len0 <<= 3;
+        if (is_endian.little) {
+#ifdef BSWAP8
+            ctx->Yi.u[1] ^= BSWAP8(len0);
+#else
+            ctx->Yi.c[8] ^= (u8)(len0 >> 56);
+            ctx->Yi.c[9] ^= (u8)(len0 >> 48);
+            ctx->Yi.c[10] ^= (u8)(len0 >> 40);
+            ctx->Yi.c[11] ^= (u8)(len0 >> 32);
+            ctx->Yi.c[12] ^= (u8)(len0 >> 24);
+            ctx->Yi.c[13] ^= (u8)(len0 >> 16);
+            ctx->Yi.c[14] ^= (u8)(len0 >> 8);
+            ctx->Yi.c[15] ^= (u8)(len0);
+#endif
+        } else
+            ctx->Yi.u[1] ^= len0;
+
+        GCM_MUL(ctx, Yi);
+
+        if (is_endian.little)
+#ifdef BSWAP4
+            ctr = BSWAP4(ctx->Yi.d[3]);
+#else
+            ctr = GETU32(ctx->Yi.c + 12);
+#endif
+        else
+            ctr = ctx->Yi.d[3];
+    }
+
+    (*ctx->block) (ctx->Yi.c, ctx->EK0.c, ctx->key);
+    ++ctr;
+    if (is_endian.little)
+#ifdef BSWAP4
+        ctx->Yi.d[3] = BSWAP4(ctr);
+#else
+        PUTU32(ctx->Yi.c + 12, ctr);
+#endif
+    else
+        ctx->Yi.d[3] = ctr;
+}
+
+int CRYPTO_gcm128_aad(GCM128_CONTEXT *ctx, const unsigned char *aad,
+                      size_t len)
+{
+    size_t i;
+    unsigned int n;
+    u64 alen = ctx->len.u[0];
+#ifdef GCM_FUNCREF_4BIT
+    void (*gcm_gmult_p) (u64 Xi[2], const u128 Htable[16]) = ctx->gmult;
+# ifdef GHASH
+    void (*gcm_ghash_p) (u64 Xi[2], const u128 Htable[16],
+                         const u8 *inp, size_t len) = ctx->ghash;
+# endif
+#endif
+
+    if (ctx->len.u[1])
+        return -2;
+
+    alen += len;
+    if (alen > (U64(1) << 61) || (sizeof(len) == 8 && alen < len))
+        return -1;
+    ctx->len.u[0] = alen;
+
+    n = ctx->ares;
+    if (n) {
+        while (n && len) {
+            ctx->Xi.c[n] ^= *(aad++);
+            --len;
+            n = (n + 1) % 16;
+        }
+        if (n == 0)
+            GCM_MUL(ctx, Xi);
+        else {
+            ctx->ares = n;
+            return 0;
+        }
+    }
+#ifdef GHASH
+    if ((i = (len & (size_t)-16))) {
+        GHASH(ctx, aad, i);
+        aad += i;
+        len -= i;
+    }
+#else
+    while (len >= 16) {
+        for (i = 0; i < 16; ++i)
+            ctx->Xi.c[i] ^= aad[i];
+        GCM_MUL(ctx, Xi);
+        aad += 16;
+        len -= 16;
+    }
+#endif
+    if (len) {
+        n = (unsigned int)len;
+        for (i = 0; i < len; ++i)
+            ctx->Xi.c[i] ^= aad[i];
+    }
+
+    ctx->ares = n;
+    return 0;
+}
+
+int CRYPTO_gcm128_encrypt(GCM128_CONTEXT *ctx,
+                          const unsigned char *in, unsigned char *out,
+                          size_t len)
+{
+    const union {
+        long one;
+        char little;
+    } is_endian = { 1 };
+    unsigned int n, ctr;
+    size_t i;
+    u64 mlen = ctx->len.u[1];
+    block128_f block = ctx->block;
+    void *key = ctx->key;
+#ifdef GCM_FUNCREF_4BIT
+    void (*gcm_gmult_p) (u64 Xi[2], const u128 Htable[16]) = ctx->gmult;
+# if defined(GHASH) && !defined(OPENSSL_SMALL_FOOTPRINT)
+    void (*gcm_ghash_p) (u64 Xi[2], const u128 Htable[16],
+                         const u8 *inp, size_t len) = ctx->ghash;
+# endif
+#endif
+
+    mlen += len;
+    if (mlen > ((U64(1) << 36) - 32) || (sizeof(len) == 8 && mlen < len))
+        return -1;
+    ctx->len.u[1] = mlen;
+
+    if (ctx->ares) {
+        /* First call to encrypt finalizes GHASH(AAD) */
+        GCM_MUL(ctx, Xi);
+        ctx->ares = 0;
+    }
+
+    if (is_endian.little)
+#ifdef BSWAP4
+        ctr = BSWAP4(ctx->Yi.d[3]);
+#else
+        ctr = GETU32(ctx->Yi.c + 12);
+#endif
+    else
+        ctr = ctx->Yi.d[3];
+
+    n = ctx->mres;
+#if !defined(OPENSSL_SMALL_FOOTPRINT)
+    if (16 % sizeof(size_t) == 0) { /* always true actually */
+        do {
+            if (n) {
+                while (n && len) {
+                    ctx->Xi.c[n] ^= *(out++) = *(in++) ^ ctx->EKi.c[n];
+                    --len;
+                    n = (n + 1) % 16;
+                }
+                if (n == 0)
+                    GCM_MUL(ctx, Xi);
+                else {
+                    ctx->mres = n;
+                    return 0;
+                }
+            }
+# if defined(STRICT_ALIGNMENT)
+            if (((size_t)in | (size_t)out) % sizeof(size_t) != 0)
+                break;
+# endif
+# if defined(GHASH)
+#  if defined(GHASH_CHUNK)
+            while (len >= GHASH_CHUNK) {
+                size_t j = GHASH_CHUNK;
+
+                while (j) {
+                    size_t *out_t = (size_t *)out;
+                    const size_t *in_t = (const size_t *)in;
+
+                    (*block) (ctx->Yi.c, ctx->EKi.c, key);
+                    ++ctr;
+                    if (is_endian.little)
+#   ifdef BSWAP4
+                        ctx->Yi.d[3] = BSWAP4(ctr);
+#   else
+                        PUTU32(ctx->Yi.c + 12, ctr);
+#   endif
+                    else
+                        ctx->Yi.d[3] = ctr;
+                    for (i = 0; i < 16 / sizeof(size_t); ++i)
+                        out_t[i] = in_t[i] ^ ctx->EKi.t[i];
+                    out += 16;
+                    in += 16;
+                    j -= 16;
+                }
+                GHASH(ctx, out - GHASH_CHUNK, GHASH_CHUNK);
+                len -= GHASH_CHUNK;
+            }
+#  endif
+            if ((i = (len & (size_t)-16))) {
+                size_t j = i;
+
+                while (len >= 16) {
+                    size_t *out_t = (size_t *)out;
+                    const size_t *in_t = (const size_t *)in;
+
+                    (*block) (ctx->Yi.c, ctx->EKi.c, key);
+                    ++ctr;
+                    if (is_endian.little)
+#  ifdef BSWAP4
+                        ctx->Yi.d[3] = BSWAP4(ctr);
+#  else
+                        PUTU32(ctx->Yi.c + 12, ctr);
+#  endif
+                    else
+                        ctx->Yi.d[3] = ctr;
+                    for (i = 0; i < 16 / sizeof(size_t); ++i)
+                        out_t[i] = in_t[i] ^ ctx->EKi.t[i];
+                    out += 16;
+                    in += 16;
+                    len -= 16;
+                }
+                GHASH(ctx, out - j, j);
+            }
+# else
+            while (len >= 16) {
+                size_t *out_t = (size_t *)out;
+                const size_t *in_t = (const size_t *)in;
+
+                (*block) (ctx->Yi.c, ctx->EKi.c, key);
+                ++ctr;
+                if (is_endian.little)
+#  ifdef BSWAP4
+                    ctx->Yi.d[3] = BSWAP4(ctr);
+#  else
+                    PUTU32(ctx->Yi.c + 12, ctr);
+#  endif
+                else
+                    ctx->Yi.d[3] = ctr;
+                for (i = 0; i < 16 / sizeof(size_t); ++i)
+                    ctx->Xi.t[i] ^= out_t[i] = in_t[i] ^ ctx->EKi.t[i];
+                GCM_MUL(ctx, Xi);
+                out += 16;
+                in += 16;
+                len -= 16;
+            }
+# endif
+            if (len) {
+                (*block) (ctx->Yi.c, ctx->EKi.c, key);
+                ++ctr;
+                if (is_endian.little)
+# ifdef BSWAP4
+                    ctx->Yi.d[3] = BSWAP4(ctr);
+# else
+                    PUTU32(ctx->Yi.c + 12, ctr);
+# endif
+                else
+                    ctx->Yi.d[3] = ctr;
+                while (len--) {
+                    ctx->Xi.c[n] ^= out[n] = in[n] ^ ctx->EKi.c[n];
+                    ++n;
+                }
+            }
+
+            ctx->mres = n;
+            return 0;
+        } while (0);
+    }
+#endif
+    for (i = 0; i < len; ++i) {
+        if (n == 0) {
+            (*block) (ctx->Yi.c, ctx->EKi.c, key);
+            ++ctr;
+            if (is_endian.little)
+#ifdef BSWAP4
+                ctx->Yi.d[3] = BSWAP4(ctr);
+#else
+                PUTU32(ctx->Yi.c + 12, ctr);
+#endif
+            else
+                ctx->Yi.d[3] = ctr;
+        }
+        ctx->Xi.c[n] ^= out[i] = in[i] ^ ctx->EKi.c[n];
+        n = (n + 1) % 16;
+        if (n == 0)
+            GCM_MUL(ctx, Xi);
+    }
+
+    ctx->mres = n;
+    return 0;
+}
+
+int CRYPTO_gcm128_decrypt(GCM128_CONTEXT *ctx,
+                          const unsigned char *in, unsigned char *out,
+                          size_t len)
+{
+    const union {
+        long one;
+        char little;
+    } is_endian = { 1 };
+    unsigned int n, ctr;
+    size_t i;
+    u64 mlen = ctx->len.u[1];
+    block128_f block = ctx->block;
+    void *key = ctx->key;
+#ifdef GCM_FUNCREF_4BIT
+    void (*gcm_gmult_p) (u64 Xi[2], const u128 Htable[16]) = ctx->gmult;
+# if defined(GHASH) && !defined(OPENSSL_SMALL_FOOTPRINT)
+    void (*gcm_ghash_p) (u64 Xi[2], const u128 Htable[16],
+                         const u8 *inp, size_t len) = ctx->ghash;
+# endif
+#endif
+
+    mlen += len;
+    if (mlen > ((U64(1) << 36) - 32) || (sizeof(len) == 8 && mlen < len))
+        return -1;
+    ctx->len.u[1] = mlen;
+
+    if (ctx->ares) {
+        /* First call to decrypt finalizes GHASH(AAD) */
+        GCM_MUL(ctx, Xi);
+        ctx->ares = 0;
+    }
+
+    if (is_endian.little)
+#ifdef BSWAP4
+        ctr = BSWAP4(ctx->Yi.d[3]);
+#else
+        ctr = GETU32(ctx->Yi.c + 12);
+#endif
+    else
+        ctr = ctx->Yi.d[3];
+
+    n = ctx->mres;
+#if !defined(OPENSSL_SMALL_FOOTPRINT)
+    if (16 % sizeof(size_t) == 0) { /* always true actually */
+        do {
+            if (n) {
+                while (n && len) {
+                    u8 c = *(in++);
+                    *(out++) = c ^ ctx->EKi.c[n];
+                    ctx->Xi.c[n] ^= c;
+                    --len;
+                    n = (n + 1) % 16;
+                }
+                if (n == 0)
+                    GCM_MUL(ctx, Xi);
+                else {
+                    ctx->mres = n;
+                    return 0;
+                }
+            }
+# if defined(STRICT_ALIGNMENT)
+            if (((size_t)in | (size_t)out) % sizeof(size_t) != 0)
+                break;
+# endif
+# if defined(GHASH)
+#  if defined(GHASH_CHUNK)
+            while (len >= GHASH_CHUNK) {
+                size_t j = GHASH_CHUNK;
+
+                GHASH(ctx, in, GHASH_CHUNK);
+                while (j) {
+                    size_t *out_t = (size_t *)out;
+                    const size_t *in_t = (const size_t *)in;
+
+                    (*block) (ctx->Yi.c, ctx->EKi.c, key);
+                    ++ctr;
+                    if (is_endian.little)
+#   ifdef BSWAP4
+                        ctx->Yi.d[3] = BSWAP4(ctr);
+#   else
+                        PUTU32(ctx->Yi.c + 12, ctr);
+#   endif
+                    else
+                        ctx->Yi.d[3] = ctr;
+                    for (i = 0; i < 16 / sizeof(size_t); ++i)
+                        out_t[i] = in_t[i] ^ ctx->EKi.t[i];
+                    out += 16;
+                    in += 16;
+                    j -= 16;
+                }
+                len -= GHASH_CHUNK;
+            }
+#  endif
+            if ((i = (len & (size_t)-16))) {
+                GHASH(ctx, in, i);
+                while (len >= 16) {
+                    size_t *out_t = (size_t *)out;
+                    const size_t *in_t = (const size_t *)in;
+
+                    (*block) (ctx->Yi.c, ctx->EKi.c, key);
+                    ++ctr;
+                    if (is_endian.little)
+#  ifdef BSWAP4
+                        ctx->Yi.d[3] = BSWAP4(ctr);
+#  else
+                        PUTU32(ctx->Yi.c + 12, ctr);
+#  endif
+                    else
+                        ctx->Yi.d[3] = ctr;
+                    for (i = 0; i < 16 / sizeof(size_t); ++i)
+                        out_t[i] = in_t[i] ^ ctx->EKi.t[i];
+                    out += 16;
+                    in += 16;
+                    len -= 16;
+                }
+            }
+# else
+            while (len >= 16) {
+                size_t *out_t = (size_t *)out;
+                const size_t *in_t = (const size_t *)in;
+
+                (*block) (ctx->Yi.c, ctx->EKi.c, key);
+                ++ctr;
+                if (is_endian.little)
+#  ifdef BSWAP4
+                    ctx->Yi.d[3] = BSWAP4(ctr);
+#  else
+                    PUTU32(ctx->Yi.c + 12, ctr);
+#  endif
+                else
+                    ctx->Yi.d[3] = ctr;
+                for (i = 0; i < 16 / sizeof(size_t); ++i) {
+                    size_t c = in[i];
+                    out[i] = c ^ ctx->EKi.t[i];
+                    ctx->Xi.t[i] ^= c;
+                }
+                GCM_MUL(ctx, Xi);
+                out += 16;
+                in += 16;
+                len -= 16;
+            }
+# endif
+            if (len) {
+                (*block) (ctx->Yi.c, ctx->EKi.c, key);
+                ++ctr;
+                if (is_endian.little)
+# ifdef BSWAP4
+                    ctx->Yi.d[3] = BSWAP4(ctr);
+# else
+                    PUTU32(ctx->Yi.c + 12, ctr);
+# endif
+                else
+                    ctx->Yi.d[3] = ctr;
+                while (len--) {
+                    u8 c = in[n];
+                    ctx->Xi.c[n] ^= c;
+                    out[n] = c ^ ctx->EKi.c[n];
+                    ++n;
+                }
+            }
+
+            ctx->mres = n;
+            return 0;
+        } while (0);
+    }
+#endif
+    for (i = 0; i < len; ++i) {
+        u8 c;
+        if (n == 0) {
+            (*block) (ctx->Yi.c, ctx->EKi.c, key);
+            ++ctr;
+            if (is_endian.little)
+#ifdef BSWAP4
+                ctx->Yi.d[3] = BSWAP4(ctr);
+#else
+                PUTU32(ctx->Yi.c + 12, ctr);
+#endif
+            else
+                ctx->Yi.d[3] = ctr;
+        }
+        c = in[i];
+        out[i] = c ^ ctx->EKi.c[n];
+        ctx->Xi.c[n] ^= c;
+        n = (n + 1) % 16;
+        if (n == 0)
+            GCM_MUL(ctx, Xi);
+    }
+
+    ctx->mres = n;
+    return 0;
+}
+
+int CRYPTO_gcm128_encrypt_ctr32(GCM128_CONTEXT *ctx,
+                                const unsigned char *in, unsigned char *out,
+                                size_t len, ctr128_f stream)
+{
+#if defined(OPENSSL_SMALL_FOOTPRINT)
+    return CRYPTO_gcm128_encrypt(ctx, in, out, len);
+#else
+    const union {
+        long one;
+        char little;
+    } is_endian = { 1 };
+    unsigned int n, ctr;
+    size_t i;
+    u64 mlen = ctx->len.u[1];
+    void *key = ctx->key;
+# ifdef GCM_FUNCREF_4BIT
+    void (*gcm_gmult_p) (u64 Xi[2], const u128 Htable[16]) = ctx->gmult;
+#  ifdef GHASH
+    void (*gcm_ghash_p) (u64 Xi[2], const u128 Htable[16],
+                         const u8 *inp, size_t len) = ctx->ghash;
+#  endif
+# endif
+
+    mlen += len;
+    if (mlen > ((U64(1) << 36) - 32) || (sizeof(len) == 8 && mlen < len))
+        return -1;
+    ctx->len.u[1] = mlen;
+
+    if (ctx->ares) {
+        /* First call to encrypt finalizes GHASH(AAD) */
+        GCM_MUL(ctx, Xi);
+        ctx->ares = 0;
+    }
+
+    if (is_endian.little)
+# ifdef BSWAP4
+        ctr = BSWAP4(ctx->Yi.d[3]);
+# else
+        ctr = GETU32(ctx->Yi.c + 12);
+# endif
+    else
+        ctr = ctx->Yi.d[3];
+
+    n = ctx->mres;
+    if (n) {
+        while (n && len) {
+            ctx->Xi.c[n] ^= *(out++) = *(in++) ^ ctx->EKi.c[n];
+            --len;
+            n = (n + 1) % 16;
+        }
+        if (n == 0)
+            GCM_MUL(ctx, Xi);
+        else {
+            ctx->mres = n;
+            return 0;
+        }
+    }
+# if defined(GHASH) && defined(GHASH_CHUNK)
+    while (len >= GHASH_CHUNK) {
+        (*stream) (in, out, GHASH_CHUNK / 16, key, ctx->Yi.c);
+        ctr += GHASH_CHUNK / 16;
+        if (is_endian.little)
+#  ifdef BSWAP4
+            ctx->Yi.d[3] = BSWAP4(ctr);
+#  else
+            PUTU32(ctx->Yi.c + 12, ctr);
+#  endif
+        else
+            ctx->Yi.d[3] = ctr;
+        GHASH(ctx, out, GHASH_CHUNK);
+        out += GHASH_CHUNK;
+        in += GHASH_CHUNK;
+        len -= GHASH_CHUNK;
+    }
+# endif
+    if ((i = (len & (size_t)-16))) {
+        size_t j = i / 16;
+
+        (*stream) (in, out, j, key, ctx->Yi.c);
+        ctr += (unsigned int)j;
+        if (is_endian.little)
+# ifdef BSWAP4
+            ctx->Yi.d[3] = BSWAP4(ctr);
+# else
+            PUTU32(ctx->Yi.c + 12, ctr);
+# endif
+        else
+            ctx->Yi.d[3] = ctr;
+        in += i;
+        len -= i;
+# if defined(GHASH)
+        GHASH(ctx, out, i);
+        out += i;
+# else
+        while (j--) {
+            for (i = 0; i < 16; ++i)
+                ctx->Xi.c[i] ^= out[i];
+            GCM_MUL(ctx, Xi);
+            out += 16;
+        }
+# endif
+    }
+    if (len) {
+        (*ctx->block) (ctx->Yi.c, ctx->EKi.c, key);
+        ++ctr;
+        if (is_endian.little)
+# ifdef BSWAP4
+            ctx->Yi.d[3] = BSWAP4(ctr);
+# else
+            PUTU32(ctx->Yi.c + 12, ctr);
+# endif
+        else
+            ctx->Yi.d[3] = ctr;
+        while (len--) {
+            ctx->Xi.c[n] ^= out[n] = in[n] ^ ctx->EKi.c[n];
+            ++n;
+        }
+    }
+
+    ctx->mres = n;
+    return 0;
+#endif
+}
+
+int CRYPTO_gcm128_decrypt_ctr32(GCM128_CONTEXT *ctx,
+                                const unsigned char *in, unsigned char *out,
+                                size_t len, ctr128_f stream)
+{
+#if defined(OPENSSL_SMALL_FOOTPRINT)
+    return CRYPTO_gcm128_decrypt(ctx, in, out, len);
+#else
+    const union {
+        long one;
+        char little;
+    } is_endian = { 1 };
+    unsigned int n, ctr;
+    size_t i;
+    u64 mlen = ctx->len.u[1];
+    void *key = ctx->key;
+# ifdef GCM_FUNCREF_4BIT
+    void (*gcm_gmult_p) (u64 Xi[2], const u128 Htable[16]) = ctx->gmult;
+#  ifdef GHASH
+    void (*gcm_ghash_p) (u64 Xi[2], const u128 Htable[16],
+                         const u8 *inp, size_t len) = ctx->ghash;
+#  endif
+# endif
+
+    mlen += len;
+    if (mlen > ((U64(1) << 36) - 32) || (sizeof(len) == 8 && mlen < len))
+        return -1;
+    ctx->len.u[1] = mlen;
+
+    if (ctx->ares) {
+        /* First call to decrypt finalizes GHASH(AAD) */
+        GCM_MUL(ctx, Xi);
+        ctx->ares = 0;
+    }
+
+    if (is_endian.little)
+# ifdef BSWAP4
+        ctr = BSWAP4(ctx->Yi.d[3]);
+# else
+        ctr = GETU32(ctx->Yi.c + 12);
+# endif
+    else
+        ctr = ctx->Yi.d[3];
+
+    n = ctx->mres;
+    if (n) {
+        while (n && len) {
+            u8 c = *(in++);
+            *(out++) = c ^ ctx->EKi.c[n];
+            ctx->Xi.c[n] ^= c;
+            --len;
+            n = (n + 1) % 16;
+        }
+        if (n == 0)
+            GCM_MUL(ctx, Xi);
+        else {
+            ctx->mres = n;
+            return 0;
+        }
+    }
+# if defined(GHASH) && defined(GHASH_CHUNK)
+    while (len >= GHASH_CHUNK) {
+        GHASH(ctx, in, GHASH_CHUNK);
+        (*stream) (in, out, GHASH_CHUNK / 16, key, ctx->Yi.c);
+        ctr += GHASH_CHUNK / 16;
+        if (is_endian.little)
+#  ifdef BSWAP4
+            ctx->Yi.d[3] = BSWAP4(ctr);
+#  else
+            PUTU32(ctx->Yi.c + 12, ctr);
+#  endif
+        else
+            ctx->Yi.d[3] = ctr;
+        out += GHASH_CHUNK;
+        in += GHASH_CHUNK;
+        len -= GHASH_CHUNK;
+    }
+# endif
+    if ((i = (len & (size_t)-16))) {
+        size_t j = i / 16;
+
+# if defined(GHASH)
+        GHASH(ctx, in, i);
+# else
+        while (j--) {
+            size_t k;
+            for (k = 0; k < 16; ++k)
+                ctx->Xi.c[k] ^= in[k];
+            GCM_MUL(ctx, Xi);
+            in += 16;
+        }
+        j = i / 16;
+        in -= i;
+# endif
+        (*stream) (in, out, j, key, ctx->Yi.c);
+        ctr += (unsigned int)j;
+        if (is_endian.little)
+# ifdef BSWAP4
+            ctx->Yi.d[3] = BSWAP4(ctr);
+# else
+            PUTU32(ctx->Yi.c + 12, ctr);
+# endif
+        else
+            ctx->Yi.d[3] = ctr;
+        out += i;
+        in += i;
+        len -= i;
+    }
+    if (len) {
+        (*ctx->block) (ctx->Yi.c, ctx->EKi.c, key);
+        ++ctr;
+        if (is_endian.little)
+# ifdef BSWAP4
+            ctx->Yi.d[3] = BSWAP4(ctr);
+# else
+            PUTU32(ctx->Yi.c + 12, ctr);
+# endif
+        else
+            ctx->Yi.d[3] = ctr;
+        while (len--) {
+            u8 c = in[n];
+            ctx->Xi.c[n] ^= c;
+            out[n] = c ^ ctx->EKi.c[n];
+            ++n;
+        }
+    }
+
+    ctx->mres = n;
+    return 0;
+#endif
+}
+
+int CRYPTO_gcm128_finish(GCM128_CONTEXT *ctx, const unsigned char *tag,
+                         size_t len)
+{
+    const union {
+        long one;
+        char little;
+    } is_endian = { 1 };
+    u64 alen = ctx->len.u[0] << 3;
+    u64 clen = ctx->len.u[1] << 3;
+#ifdef GCM_FUNCREF_4BIT
+    void (*gcm_gmult_p) (u64 Xi[2], const u128 Htable[16]) = ctx->gmult;
+#endif
+
+    if (ctx->mres || ctx->ares)
+        GCM_MUL(ctx, Xi);
+
+    if (is_endian.little) {
+#ifdef BSWAP8
+        alen = BSWAP8(alen);
+        clen = BSWAP8(clen);
+#else
+        u8 *p = ctx->len.c;
+
+        ctx->len.u[0] = alen;
+        ctx->len.u[1] = clen;
+
+        alen = (u64)GETU32(p) << 32 | GETU32(p + 4);
+        clen = (u64)GETU32(p + 8) << 32 | GETU32(p + 12);
+#endif
+    }
+
+    ctx->Xi.u[0] ^= alen;
+    ctx->Xi.u[1] ^= clen;
+    GCM_MUL(ctx, Xi);
+
+    ctx->Xi.u[0] ^= ctx->EK0.u[0];
+    ctx->Xi.u[1] ^= ctx->EK0.u[1];
+
+    if (tag && len <= sizeof(ctx->Xi))
+        return CRYPTO_memcmp(ctx->Xi.c, tag, len);
+    else
+        return -1;
+}
+
+void CRYPTO_gcm128_tag(GCM128_CONTEXT *ctx, unsigned char *tag, size_t len)
+{
+    CRYPTO_gcm128_finish(ctx, NULL, 0);
+    memcpy(tag, ctx->Xi.c,
+           len <= sizeof(ctx->Xi.c) ? len : sizeof(ctx->Xi.c));
+}
+
+GCM128_CONTEXT *CRYPTO_gcm128_new(void *key, block128_f block)
+{
+    GCM128_CONTEXT *ret;
+
+    if ((ret = OPENSSL_malloc(sizeof(*ret))) != NULL)
+        CRYPTO_gcm128_init(ret, key, block);
+
+    return ret;
+}
+
+void CRYPTO_gcm128_release(GCM128_CONTEXT *ctx)
+{
+    OPENSSL_clear_free(ctx, sizeof(*ctx));
+}
+
+#if defined(SELFTEST)
+# include 
+# include 
+
+/* Test Case 1 */
+static const u8 K1[16], *P1 = NULL, *A1 = NULL, IV1[12], *C1 = NULL;
+static const u8 T1[] = {
+    0x58, 0xe2, 0xfc, 0xce, 0xfa, 0x7e, 0x30, 0x61,
+    0x36, 0x7f, 0x1d, 0x57, 0xa4, 0xe7, 0x45, 0x5a
+};
+
+/* Test Case 2 */
+# define K2 K1
+# define A2 A1
+# define IV2 IV1
+static const u8 P2[16];
+static const u8 C2[] = {
+    0x03, 0x88, 0xda, 0xce, 0x60, 0xb6, 0xa3, 0x92,
+    0xf3, 0x28, 0xc2, 0xb9, 0x71, 0xb2, 0xfe, 0x78
+};
+
+static const u8 T2[] = {
+    0xab, 0x6e, 0x47, 0xd4, 0x2c, 0xec, 0x13, 0xbd,
+    0xf5, 0x3a, 0x67, 0xb2, 0x12, 0x57, 0xbd, 0xdf
+};
+
+/* Test Case 3 */
+# define A3 A2
+static const u8 K3[] = {
+    0xfe, 0xff, 0xe9, 0x92, 0x86, 0x65, 0x73, 0x1c,
+    0x6d, 0x6a, 0x8f, 0x94, 0x67, 0x30, 0x83, 0x08
+};
+
+static const u8 P3[] = {
+    0xd9, 0x31, 0x32, 0x25, 0xf8, 0x84, 0x06, 0xe5,
+    0xa5, 0x59, 0x09, 0xc5, 0xaf, 0xf5, 0x26, 0x9a,
+    0x86, 0xa7, 0xa9, 0x53, 0x15, 0x34, 0xf7, 0xda,
+    0x2e, 0x4c, 0x30, 0x3d, 0x8a, 0x31, 0x8a, 0x72,
+    0x1c, 0x3c, 0x0c, 0x95, 0x95, 0x68, 0x09, 0x53,
+    0x2f, 0xcf, 0x0e, 0x24, 0x49, 0xa6, 0xb5, 0x25,
+    0xb1, 0x6a, 0xed, 0xf5, 0xaa, 0x0d, 0xe6, 0x57,
+    0xba, 0x63, 0x7b, 0x39, 0x1a, 0xaf, 0xd2, 0x55
+};
+
+static const u8 IV3[] = {
+    0xca, 0xfe, 0xba, 0xbe, 0xfa, 0xce, 0xdb, 0xad,
+    0xde, 0xca, 0xf8, 0x88
+};
+
+static const u8 C3[] = {
+    0x42, 0x83, 0x1e, 0xc2, 0x21, 0x77, 0x74, 0x24,
+    0x4b, 0x72, 0x21, 0xb7, 0x84, 0xd0, 0xd4, 0x9c,
+    0xe3, 0xaa, 0x21, 0x2f, 0x2c, 0x02, 0xa4, 0xe0,
+    0x35, 0xc1, 0x7e, 0x23, 0x29, 0xac, 0xa1, 0x2e,
+    0x21, 0xd5, 0x14, 0xb2, 0x54, 0x66, 0x93, 0x1c,
+    0x7d, 0x8f, 0x6a, 0x5a, 0xac, 0x84, 0xaa, 0x05,
+    0x1b, 0xa3, 0x0b, 0x39, 0x6a, 0x0a, 0xac, 0x97,
+    0x3d, 0x58, 0xe0, 0x91, 0x47, 0x3f, 0x59, 0x85
+};
+
+static const u8 T3[] = {
+    0x4d, 0x5c, 0x2a, 0xf3, 0x27, 0xcd, 0x64, 0xa6,
+    0x2c, 0xf3, 0x5a, 0xbd, 0x2b, 0xa6, 0xfa, 0xb4
+};
+
+/* Test Case 4 */
+# define K4 K3
+# define IV4 IV3
+static const u8 P4[] = {
+    0xd9, 0x31, 0x32, 0x25, 0xf8, 0x84, 0x06, 0xe5,
+    0xa5, 0x59, 0x09, 0xc5, 0xaf, 0xf5, 0x26, 0x9a,
+    0x86, 0xa7, 0xa9, 0x53, 0x15, 0x34, 0xf7, 0xda,
+    0x2e, 0x4c, 0x30, 0x3d, 0x8a, 0x31, 0x8a, 0x72,
+    0x1c, 0x3c, 0x0c, 0x95, 0x95, 0x68, 0x09, 0x53,
+    0x2f, 0xcf, 0x0e, 0x24, 0x49, 0xa6, 0xb5, 0x25,
+    0xb1, 0x6a, 0xed, 0xf5, 0xaa, 0x0d, 0xe6, 0x57,
+    0xba, 0x63, 0x7b, 0x39
+};
+
+static const u8 A4[] = {
+    0xfe, 0xed, 0xfa, 0xce, 0xde, 0xad, 0xbe, 0xef,
+    0xfe, 0xed, 0xfa, 0xce, 0xde, 0xad, 0xbe, 0xef,
+    0xab, 0xad, 0xda, 0xd2
+};
+
+static const u8 C4[] = {
+    0x42, 0x83, 0x1e, 0xc2, 0x21, 0x77, 0x74, 0x24,
+    0x4b, 0x72, 0x21, 0xb7, 0x84, 0xd0, 0xd4, 0x9c,
+    0xe3, 0xaa, 0x21, 0x2f, 0x2c, 0x02, 0xa4, 0xe0,
+    0x35, 0xc1, 0x7e, 0x23, 0x29, 0xac, 0xa1, 0x2e,
+    0x21, 0xd5, 0x14, 0xb2, 0x54, 0x66, 0x93, 0x1c,
+    0x7d, 0x8f, 0x6a, 0x5a, 0xac, 0x84, 0xaa, 0x05,
+    0x1b, 0xa3, 0x0b, 0x39, 0x6a, 0x0a, 0xac, 0x97,
+    0x3d, 0x58, 0xe0, 0x91
+};
+
+static const u8 T4[] = {
+    0x5b, 0xc9, 0x4f, 0xbc, 0x32, 0x21, 0xa5, 0xdb,
+    0x94, 0xfa, 0xe9, 0x5a, 0xe7, 0x12, 0x1a, 0x47
+};
+
+/* Test Case 5 */
+# define K5 K4
+# define P5 P4
+# define A5 A4
+static const u8 IV5[] = {
+    0xca, 0xfe, 0xba, 0xbe, 0xfa, 0xce, 0xdb, 0xad
+};
+
+static const u8 C5[] = {
+    0x61, 0x35, 0x3b, 0x4c, 0x28, 0x06, 0x93, 0x4a,
+    0x77, 0x7f, 0xf5, 0x1f, 0xa2, 0x2a, 0x47, 0x55,
+    0x69, 0x9b, 0x2a, 0x71, 0x4f, 0xcd, 0xc6, 0xf8,
+    0x37, 0x66, 0xe5, 0xf9, 0x7b, 0x6c, 0x74, 0x23,
+    0x73, 0x80, 0x69, 0x00, 0xe4, 0x9f, 0x24, 0xb2,
+    0x2b, 0x09, 0x75, 0x44, 0xd4, 0x89, 0x6b, 0x42,
+    0x49, 0x89, 0xb5, 0xe1, 0xeb, 0xac, 0x0f, 0x07,
+    0xc2, 0x3f, 0x45, 0x98
+};
+
+static const u8 T5[] = {
+    0x36, 0x12, 0xd2, 0xe7, 0x9e, 0x3b, 0x07, 0x85,
+    0x56, 0x1b, 0xe1, 0x4a, 0xac, 0xa2, 0xfc, 0xcb
+};
+
+/* Test Case 6 */
+# define K6 K5
+# define P6 P5
+# define A6 A5
+static const u8 IV6[] = {
+    0x93, 0x13, 0x22, 0x5d, 0xf8, 0x84, 0x06, 0xe5,
+    0x55, 0x90, 0x9c, 0x5a, 0xff, 0x52, 0x69, 0xaa,
+    0x6a, 0x7a, 0x95, 0x38, 0x53, 0x4f, 0x7d, 0xa1,
+    0xe4, 0xc3, 0x03, 0xd2, 0xa3, 0x18, 0xa7, 0x28,
+    0xc3, 0xc0, 0xc9, 0x51, 0x56, 0x80, 0x95, 0x39,
+    0xfc, 0xf0, 0xe2, 0x42, 0x9a, 0x6b, 0x52, 0x54,
+    0x16, 0xae, 0xdb, 0xf5, 0xa0, 0xde, 0x6a, 0x57,
+    0xa6, 0x37, 0xb3, 0x9b
+};
+
+static const u8 C6[] = {
+    0x8c, 0xe2, 0x49, 0x98, 0x62, 0x56, 0x15, 0xb6,
+    0x03, 0xa0, 0x33, 0xac, 0xa1, 0x3f, 0xb8, 0x94,
+    0xbe, 0x91, 0x12, 0xa5, 0xc3, 0xa2, 0x11, 0xa8,
+    0xba, 0x26, 0x2a, 0x3c, 0xca, 0x7e, 0x2c, 0xa7,
+    0x01, 0xe4, 0xa9, 0xa4, 0xfb, 0xa4, 0x3c, 0x90,
+    0xcc, 0xdc, 0xb2, 0x81, 0xd4, 0x8c, 0x7c, 0x6f,
+    0xd6, 0x28, 0x75, 0xd2, 0xac, 0xa4, 0x17, 0x03,
+    0x4c, 0x34, 0xae, 0xe5
+};
+
+static const u8 T6[] = {
+    0x61, 0x9c, 0xc5, 0xae, 0xff, 0xfe, 0x0b, 0xfa,
+    0x46, 0x2a, 0xf4, 0x3c, 0x16, 0x99, 0xd0, 0x50
+};
+
+/* Test Case 7 */
+static const u8 K7[24], *P7 = NULL, *A7 = NULL, IV7[12], *C7 = NULL;
+static const u8 T7[] = {
+    0xcd, 0x33, 0xb2, 0x8a, 0xc7, 0x73, 0xf7, 0x4b,
+    0xa0, 0x0e, 0xd1, 0xf3, 0x12, 0x57, 0x24, 0x35
+};
+
+/* Test Case 8 */
+# define K8 K7
+# define IV8 IV7
+# define A8 A7
+static const u8 P8[16];
+static const u8 C8[] = {
+    0x98, 0xe7, 0x24, 0x7c, 0x07, 0xf0, 0xfe, 0x41,
+    0x1c, 0x26, 0x7e, 0x43, 0x84, 0xb0, 0xf6, 0x00
+};
+
+static const u8 T8[] = {
+    0x2f, 0xf5, 0x8d, 0x80, 0x03, 0x39, 0x27, 0xab,
+    0x8e, 0xf4, 0xd4, 0x58, 0x75, 0x14, 0xf0, 0xfb
+};
+
+/* Test Case 9 */
+# define A9 A8
+static const u8 K9[] = {
+    0xfe, 0xff, 0xe9, 0x92, 0x86, 0x65, 0x73, 0x1c,
+    0x6d, 0x6a, 0x8f, 0x94, 0x67, 0x30, 0x83, 0x08,
+    0xfe, 0xff, 0xe9, 0x92, 0x86, 0x65, 0x73, 0x1c
+};
+
+static const u8 P9[] = {
+    0xd9, 0x31, 0x32, 0x25, 0xf8, 0x84, 0x06, 0xe5,
+    0xa5, 0x59, 0x09, 0xc5, 0xaf, 0xf5, 0x26, 0x9a,
+    0x86, 0xa7, 0xa9, 0x53, 0x15, 0x34, 0xf7, 0xda,
+    0x2e, 0x4c, 0x30, 0x3d, 0x8a, 0x31, 0x8a, 0x72,
+    0x1c, 0x3c, 0x0c, 0x95, 0x95, 0x68, 0x09, 0x53,
+    0x2f, 0xcf, 0x0e, 0x24, 0x49, 0xa6, 0xb5, 0x25,
+    0xb1, 0x6a, 0xed, 0xf5, 0xaa, 0x0d, 0xe6, 0x57,
+    0xba, 0x63, 0x7b, 0x39, 0x1a, 0xaf, 0xd2, 0x55
+};
+
+static const u8 IV9[] = {
+    0xca, 0xfe, 0xba, 0xbe, 0xfa, 0xce, 0xdb, 0xad,
+    0xde, 0xca, 0xf8, 0x88
+};
+
+static const u8 C9[] = {
+    0x39, 0x80, 0xca, 0x0b, 0x3c, 0x00, 0xe8, 0x41,
+    0xeb, 0x06, 0xfa, 0xc4, 0x87, 0x2a, 0x27, 0x57,
+    0x85, 0x9e, 0x1c, 0xea, 0xa6, 0xef, 0xd9, 0x84,
+    0x62, 0x85, 0x93, 0xb4, 0x0c, 0xa1, 0xe1, 0x9c,
+    0x7d, 0x77, 0x3d, 0x00, 0xc1, 0x44, 0xc5, 0x25,
+    0xac, 0x61, 0x9d, 0x18, 0xc8, 0x4a, 0x3f, 0x47,
+    0x18, 0xe2, 0x44, 0x8b, 0x2f, 0xe3, 0x24, 0xd9,
+    0xcc, 0xda, 0x27, 0x10, 0xac, 0xad, 0xe2, 0x56
+};
+
+static const u8 T9[] = {
+    0x99, 0x24, 0xa7, 0xc8, 0x58, 0x73, 0x36, 0xbf,
+    0xb1, 0x18, 0x02, 0x4d, 0xb8, 0x67, 0x4a, 0x14
+};
+
+/* Test Case 10 */
+# define K10 K9
+# define IV10 IV9
+static const u8 P10[] = {
+    0xd9, 0x31, 0x32, 0x25, 0xf8, 0x84, 0x06, 0xe5,
+    0xa5, 0x59, 0x09, 0xc5, 0xaf, 0xf5, 0x26, 0x9a,
+    0x86, 0xa7, 0xa9, 0x53, 0x15, 0x34, 0xf7, 0xda,
+    0x2e, 0x4c, 0x30, 0x3d, 0x8a, 0x31, 0x8a, 0x72,
+    0x1c, 0x3c, 0x0c, 0x95, 0x95, 0x68, 0x09, 0x53,
+    0x2f, 0xcf, 0x0e, 0x24, 0x49, 0xa6, 0xb5, 0x25,
+    0xb1, 0x6a, 0xed, 0xf5, 0xaa, 0x0d, 0xe6, 0x57,
+    0xba, 0x63, 0x7b, 0x39
+};
+
+static const u8 A10[] = {
+    0xfe, 0xed, 0xfa, 0xce, 0xde, 0xad, 0xbe, 0xef,
+    0xfe, 0xed, 0xfa, 0xce, 0xde, 0xad, 0xbe, 0xef,
+    0xab, 0xad, 0xda, 0xd2
+};
+
+static const u8 C10[] = {
+    0x39, 0x80, 0xca, 0x0b, 0x3c, 0x00, 0xe8, 0x41,
+    0xeb, 0x06, 0xfa, 0xc4, 0x87, 0x2a, 0x27, 0x57,
+    0x85, 0x9e, 0x1c, 0xea, 0xa6, 0xef, 0xd9, 0x84,
+    0x62, 0x85, 0x93, 0xb4, 0x0c, 0xa1, 0xe1, 0x9c,
+    0x7d, 0x77, 0x3d, 0x00, 0xc1, 0x44, 0xc5, 0x25,
+    0xac, 0x61, 0x9d, 0x18, 0xc8, 0x4a, 0x3f, 0x47,
+    0x18, 0xe2, 0x44, 0x8b, 0x2f, 0xe3, 0x24, 0xd9,
+    0xcc, 0xda, 0x27, 0x10
+};
+
+static const u8 T10[] = {
+    0x25, 0x19, 0x49, 0x8e, 0x80, 0xf1, 0x47, 0x8f,
+    0x37, 0xba, 0x55, 0xbd, 0x6d, 0x27, 0x61, 0x8c
+};
+
+/* Test Case 11 */
+# define K11 K10
+# define P11 P10
+# define A11 A10
+static const u8 IV11[] = { 0xca, 0xfe, 0xba, 0xbe, 0xfa, 0xce, 0xdb, 0xad };
+
+static const u8 C11[] = {
+    0x0f, 0x10, 0xf5, 0x99, 0xae, 0x14, 0xa1, 0x54,
+    0xed, 0x24, 0xb3, 0x6e, 0x25, 0x32, 0x4d, 0xb8,
+    0xc5, 0x66, 0x63, 0x2e, 0xf2, 0xbb, 0xb3, 0x4f,
+    0x83, 0x47, 0x28, 0x0f, 0xc4, 0x50, 0x70, 0x57,
+    0xfd, 0xdc, 0x29, 0xdf, 0x9a, 0x47, 0x1f, 0x75,
+    0xc6, 0x65, 0x41, 0xd4, 0xd4, 0xda, 0xd1, 0xc9,
+    0xe9, 0x3a, 0x19, 0xa5, 0x8e, 0x8b, 0x47, 0x3f,
+    0xa0, 0xf0, 0x62, 0xf7
+};
+
+static const u8 T11[] = {
+    0x65, 0xdc, 0xc5, 0x7f, 0xcf, 0x62, 0x3a, 0x24,
+    0x09, 0x4f, 0xcc, 0xa4, 0x0d, 0x35, 0x33, 0xf8
+};
+
+/* Test Case 12 */
+# define K12 K11
+# define P12 P11
+# define A12 A11
+static const u8 IV12[] = {
+    0x93, 0x13, 0x22, 0x5d, 0xf8, 0x84, 0x06, 0xe5,
+    0x55, 0x90, 0x9c, 0x5a, 0xff, 0x52, 0x69, 0xaa,
+    0x6a, 0x7a, 0x95, 0x38, 0x53, 0x4f, 0x7d, 0xa1,
+    0xe4, 0xc3, 0x03, 0xd2, 0xa3, 0x18, 0xa7, 0x28,
+    0xc3, 0xc0, 0xc9, 0x51, 0x56, 0x80, 0x95, 0x39,
+    0xfc, 0xf0, 0xe2, 0x42, 0x9a, 0x6b, 0x52, 0x54,
+    0x16, 0xae, 0xdb, 0xf5, 0xa0, 0xde, 0x6a, 0x57,
+    0xa6, 0x37, 0xb3, 0x9b
+};
+
+static const u8 C12[] = {
+    0xd2, 0x7e, 0x88, 0x68, 0x1c, 0xe3, 0x24, 0x3c,
+    0x48, 0x30, 0x16, 0x5a, 0x8f, 0xdc, 0xf9, 0xff,
+    0x1d, 0xe9, 0xa1, 0xd8, 0xe6, 0xb4, 0x47, 0xef,
+    0x6e, 0xf7, 0xb7, 0x98, 0x28, 0x66, 0x6e, 0x45,
+    0x81, 0xe7, 0x90, 0x12, 0xaf, 0x34, 0xdd, 0xd9,
+    0xe2, 0xf0, 0x37, 0x58, 0x9b, 0x29, 0x2d, 0xb3,
+    0xe6, 0x7c, 0x03, 0x67, 0x45, 0xfa, 0x22, 0xe7,
+    0xe9, 0xb7, 0x37, 0x3b
+};
+
+static const u8 T12[] = {
+    0xdc, 0xf5, 0x66, 0xff, 0x29, 0x1c, 0x25, 0xbb,
+    0xb8, 0x56, 0x8f, 0xc3, 0xd3, 0x76, 0xa6, 0xd9
+};
+
+/* Test Case 13 */
+static const u8 K13[32], *P13 = NULL, *A13 = NULL, IV13[12], *C13 = NULL;
+static const u8 T13[] = {
+    0x53, 0x0f, 0x8a, 0xfb, 0xc7, 0x45, 0x36, 0xb9,
+    0xa9, 0x63, 0xb4, 0xf1, 0xc4, 0xcb, 0x73, 0x8b
+};
+
+/* Test Case 14 */
+# define K14 K13
+# define A14 A13
+static const u8 P14[16], IV14[12];
+static const u8 C14[] = {
+    0xce, 0xa7, 0x40, 0x3d, 0x4d, 0x60, 0x6b, 0x6e,
+    0x07, 0x4e, 0xc5, 0xd3, 0xba, 0xf3, 0x9d, 0x18
+};
+
+static const u8 T14[] = {
+    0xd0, 0xd1, 0xc8, 0xa7, 0x99, 0x99, 0x6b, 0xf0,
+    0x26, 0x5b, 0x98, 0xb5, 0xd4, 0x8a, 0xb9, 0x19
+};
+
+/* Test Case 15 */
+# define A15 A14
+static const u8 K15[] = {
+    0xfe, 0xff, 0xe9, 0x92, 0x86, 0x65, 0x73, 0x1c,
+    0x6d, 0x6a, 0x8f, 0x94, 0x67, 0x30, 0x83, 0x08,
+    0xfe, 0xff, 0xe9, 0x92, 0x86, 0x65, 0x73, 0x1c,
+    0x6d, 0x6a, 0x8f, 0x94, 0x67, 0x30, 0x83, 0x08
+};
+
+static const u8 P15[] = {
+    0xd9, 0x31, 0x32, 0x25, 0xf8, 0x84, 0x06, 0xe5,
+    0xa5, 0x59, 0x09, 0xc5, 0xaf, 0xf5, 0x26, 0x9a,
+    0x86, 0xa7, 0xa9, 0x53, 0x15, 0x34, 0xf7, 0xda,
+    0x2e, 0x4c, 0x30, 0x3d, 0x8a, 0x31, 0x8a, 0x72,
+    0x1c, 0x3c, 0x0c, 0x95, 0x95, 0x68, 0x09, 0x53,
+    0x2f, 0xcf, 0x0e, 0x24, 0x49, 0xa6, 0xb5, 0x25,
+    0xb1, 0x6a, 0xed, 0xf5, 0xaa, 0x0d, 0xe6, 0x57,
+    0xba, 0x63, 0x7b, 0x39, 0x1a, 0xaf, 0xd2, 0x55
+};
+
+static const u8 IV15[] = {
+    0xca, 0xfe, 0xba, 0xbe, 0xfa, 0xce, 0xdb, 0xad,
+    0xde, 0xca, 0xf8, 0x88
+};
+
+static const u8 C15[] = {
+    0x52, 0x2d, 0xc1, 0xf0, 0x99, 0x56, 0x7d, 0x07,
+    0xf4, 0x7f, 0x37, 0xa3, 0x2a, 0x84, 0x42, 0x7d,
+    0x64, 0x3a, 0x8c, 0xdc, 0xbf, 0xe5, 0xc0, 0xc9,
+    0x75, 0x98, 0xa2, 0xbd, 0x25, 0x55, 0xd1, 0xaa,
+    0x8c, 0xb0, 0x8e, 0x48, 0x59, 0x0d, 0xbb, 0x3d,
+    0xa7, 0xb0, 0x8b, 0x10, 0x56, 0x82, 0x88, 0x38,
+    0xc5, 0xf6, 0x1e, 0x63, 0x93, 0xba, 0x7a, 0x0a,
+    0xbc, 0xc9, 0xf6, 0x62, 0x89, 0x80, 0x15, 0xad
+};
+
+static const u8 T15[] = {
+    0xb0, 0x94, 0xda, 0xc5, 0xd9, 0x34, 0x71, 0xbd,
+    0xec, 0x1a, 0x50, 0x22, 0x70, 0xe3, 0xcc, 0x6c
+};
+
+/* Test Case 16 */
+# define K16 K15
+# define IV16 IV15
+static const u8 P16[] = {
+    0xd9, 0x31, 0x32, 0x25, 0xf8, 0x84, 0x06, 0xe5,
+    0xa5, 0x59, 0x09, 0xc5, 0xaf, 0xf5, 0x26, 0x9a,
+    0x86, 0xa7, 0xa9, 0x53, 0x15, 0x34, 0xf7, 0xda,
+    0x2e, 0x4c, 0x30, 0x3d, 0x8a, 0x31, 0x8a, 0x72,
+    0x1c, 0x3c, 0x0c, 0x95, 0x95, 0x68, 0x09, 0x53,
+    0x2f, 0xcf, 0x0e, 0x24, 0x49, 0xa6, 0xb5, 0x25,
+    0xb1, 0x6a, 0xed, 0xf5, 0xaa, 0x0d, 0xe6, 0x57,
+    0xba, 0x63, 0x7b, 0x39
+};
+
+static const u8 A16[] = {
+    0xfe, 0xed, 0xfa, 0xce, 0xde, 0xad, 0xbe, 0xef,
+    0xfe, 0xed, 0xfa, 0xce, 0xde, 0xad, 0xbe, 0xef,
+    0xab, 0xad, 0xda, 0xd2
+};
+
+static const u8 C16[] = {
+    0x52, 0x2d, 0xc1, 0xf0, 0x99, 0x56, 0x7d, 0x07,
+    0xf4, 0x7f, 0x37, 0xa3, 0x2a, 0x84, 0x42, 0x7d,
+    0x64, 0x3a, 0x8c, 0xdc, 0xbf, 0xe5, 0xc0, 0xc9,
+    0x75, 0x98, 0xa2, 0xbd, 0x25, 0x55, 0xd1, 0xaa,
+    0x8c, 0xb0, 0x8e, 0x48, 0x59, 0x0d, 0xbb, 0x3d,
+    0xa7, 0xb0, 0x8b, 0x10, 0x56, 0x82, 0x88, 0x38,
+    0xc5, 0xf6, 0x1e, 0x63, 0x93, 0xba, 0x7a, 0x0a,
+    0xbc, 0xc9, 0xf6, 0x62
+};
+
+static const u8 T16[] = {
+    0x76, 0xfc, 0x6e, 0xce, 0x0f, 0x4e, 0x17, 0x68,
+    0xcd, 0xdf, 0x88, 0x53, 0xbb, 0x2d, 0x55, 0x1b
+};
+
+/* Test Case 17 */
+# define K17 K16
+# define P17 P16
+# define A17 A16
+static const u8 IV17[] = { 0xca, 0xfe, 0xba, 0xbe, 0xfa, 0xce, 0xdb, 0xad };
+
+static const u8 C17[] = {
+    0xc3, 0x76, 0x2d, 0xf1, 0xca, 0x78, 0x7d, 0x32,
+    0xae, 0x47, 0xc1, 0x3b, 0xf1, 0x98, 0x44, 0xcb,
+    0xaf, 0x1a, 0xe1, 0x4d, 0x0b, 0x97, 0x6a, 0xfa,
+    0xc5, 0x2f, 0xf7, 0xd7, 0x9b, 0xba, 0x9d, 0xe0,
+    0xfe, 0xb5, 0x82, 0xd3, 0x39, 0x34, 0xa4, 0xf0,
+    0x95, 0x4c, 0xc2, 0x36, 0x3b, 0xc7, 0x3f, 0x78,
+    0x62, 0xac, 0x43, 0x0e, 0x64, 0xab, 0xe4, 0x99,
+    0xf4, 0x7c, 0x9b, 0x1f
+};
+
+static const u8 T17[] = {
+    0x3a, 0x33, 0x7d, 0xbf, 0x46, 0xa7, 0x92, 0xc4,
+    0x5e, 0x45, 0x49, 0x13, 0xfe, 0x2e, 0xa8, 0xf2
+};
+
+/* Test Case 18 */
+# define K18 K17
+# define P18 P17
+# define A18 A17
+static const u8 IV18[] = {
+    0x93, 0x13, 0x22, 0x5d, 0xf8, 0x84, 0x06, 0xe5,
+    0x55, 0x90, 0x9c, 0x5a, 0xff, 0x52, 0x69, 0xaa,
+    0x6a, 0x7a, 0x95, 0x38, 0x53, 0x4f, 0x7d, 0xa1,
+    0xe4, 0xc3, 0x03, 0xd2, 0xa3, 0x18, 0xa7, 0x28,
+    0xc3, 0xc0, 0xc9, 0x51, 0x56, 0x80, 0x95, 0x39,
+    0xfc, 0xf0, 0xe2, 0x42, 0x9a, 0x6b, 0x52, 0x54,
+    0x16, 0xae, 0xdb, 0xf5, 0xa0, 0xde, 0x6a, 0x57,
+    0xa6, 0x37, 0xb3, 0x9b
+};
+
+static const u8 C18[] = {
+    0x5a, 0x8d, 0xef, 0x2f, 0x0c, 0x9e, 0x53, 0xf1,
+    0xf7, 0x5d, 0x78, 0x53, 0x65, 0x9e, 0x2a, 0x20,
+    0xee, 0xb2, 0xb2, 0x2a, 0xaf, 0xde, 0x64, 0x19,
+    0xa0, 0x58, 0xab, 0x4f, 0x6f, 0x74, 0x6b, 0xf4,
+    0x0f, 0xc0, 0xc3, 0xb7, 0x80, 0xf2, 0x44, 0x45,
+    0x2d, 0xa3, 0xeb, 0xf1, 0xc5, 0xd8, 0x2c, 0xde,
+    0xa2, 0x41, 0x89, 0x97, 0x20, 0x0e, 0xf8, 0x2e,
+    0x44, 0xae, 0x7e, 0x3f
+};
+
+static const u8 T18[] = {
+    0xa4, 0x4a, 0x82, 0x66, 0xee, 0x1c, 0x8e, 0xb0,
+    0xc8, 0xb5, 0xd4, 0xcf, 0x5a, 0xe9, 0xf1, 0x9a
+};
+
+/* Test Case 19 */
+# define K19 K1
+# define P19 P1
+# define IV19 IV1
+# define C19 C1
+static const u8 A19[] = {
+    0xd9, 0x31, 0x32, 0x25, 0xf8, 0x84, 0x06, 0xe5,
+    0xa5, 0x59, 0x09, 0xc5, 0xaf, 0xf5, 0x26, 0x9a,
+    0x86, 0xa7, 0xa9, 0x53, 0x15, 0x34, 0xf7, 0xda,
+    0x2e, 0x4c, 0x30, 0x3d, 0x8a, 0x31, 0x8a, 0x72,
+    0x1c, 0x3c, 0x0c, 0x95, 0x95, 0x68, 0x09, 0x53,
+    0x2f, 0xcf, 0x0e, 0x24, 0x49, 0xa6, 0xb5, 0x25,
+    0xb1, 0x6a, 0xed, 0xf5, 0xaa, 0x0d, 0xe6, 0x57,
+    0xba, 0x63, 0x7b, 0x39, 0x1a, 0xaf, 0xd2, 0x55,
+    0x52, 0x2d, 0xc1, 0xf0, 0x99, 0x56, 0x7d, 0x07,
+    0xf4, 0x7f, 0x37, 0xa3, 0x2a, 0x84, 0x42, 0x7d,
+    0x64, 0x3a, 0x8c, 0xdc, 0xbf, 0xe5, 0xc0, 0xc9,
+    0x75, 0x98, 0xa2, 0xbd, 0x25, 0x55, 0xd1, 0xaa,
+    0x8c, 0xb0, 0x8e, 0x48, 0x59, 0x0d, 0xbb, 0x3d,
+    0xa7, 0xb0, 0x8b, 0x10, 0x56, 0x82, 0x88, 0x38,
+    0xc5, 0xf6, 0x1e, 0x63, 0x93, 0xba, 0x7a, 0x0a,
+    0xbc, 0xc9, 0xf6, 0x62, 0x89, 0x80, 0x15, 0xad
+};
+
+static const u8 T19[] = {
+    0x5f, 0xea, 0x79, 0x3a, 0x2d, 0x6f, 0x97, 0x4d,
+    0x37, 0xe6, 0x8e, 0x0c, 0xb8, 0xff, 0x94, 0x92
+};
+
+/* Test Case 20 */
+# define K20 K1
+# define A20 A1
+/* this results in 0xff in counter LSB */
+static const u8 IV20[64] = { 0xff, 0xff, 0xff, 0xff };
+
+static const u8 P20[288];
+static const u8 C20[] = {
+    0x56, 0xb3, 0x37, 0x3c, 0xa9, 0xef, 0x6e, 0x4a,
+    0x2b, 0x64, 0xfe, 0x1e, 0x9a, 0x17, 0xb6, 0x14,
+    0x25, 0xf1, 0x0d, 0x47, 0xa7, 0x5a, 0x5f, 0xce,
+    0x13, 0xef, 0xc6, 0xbc, 0x78, 0x4a, 0xf2, 0x4f,
+    0x41, 0x41, 0xbd, 0xd4, 0x8c, 0xf7, 0xc7, 0x70,
+    0x88, 0x7a, 0xfd, 0x57, 0x3c, 0xca, 0x54, 0x18,
+    0xa9, 0xae, 0xff, 0xcd, 0x7c, 0x5c, 0xed, 0xdf,
+    0xc6, 0xa7, 0x83, 0x97, 0xb9, 0xa8, 0x5b, 0x49,
+    0x9d, 0xa5, 0x58, 0x25, 0x72, 0x67, 0xca, 0xab,
+    0x2a, 0xd0, 0xb2, 0x3c, 0xa4, 0x76, 0xa5, 0x3c,
+    0xb1, 0x7f, 0xb4, 0x1c, 0x4b, 0x8b, 0x47, 0x5c,
+    0xb4, 0xf3, 0xf7, 0x16, 0x50, 0x94, 0xc2, 0x29,
+    0xc9, 0xe8, 0xc4, 0xdc, 0x0a, 0x2a, 0x5f, 0xf1,
+    0x90, 0x3e, 0x50, 0x15, 0x11, 0x22, 0x13, 0x76,
+    0xa1, 0xcd, 0xb8, 0x36, 0x4c, 0x50, 0x61, 0xa2,
+    0x0c, 0xae, 0x74, 0xbc, 0x4a, 0xcd, 0x76, 0xce,
+    0xb0, 0xab, 0xc9, 0xfd, 0x32, 0x17, 0xef, 0x9f,
+    0x8c, 0x90, 0xbe, 0x40, 0x2d, 0xdf, 0x6d, 0x86,
+    0x97, 0xf4, 0xf8, 0x80, 0xdf, 0xf1, 0x5b, 0xfb,
+    0x7a, 0x6b, 0x28, 0x24, 0x1e, 0xc8, 0xfe, 0x18,
+    0x3c, 0x2d, 0x59, 0xe3, 0xf9, 0xdf, 0xff, 0x65,
+    0x3c, 0x71, 0x26, 0xf0, 0xac, 0xb9, 0xe6, 0x42,
+    0x11, 0xf4, 0x2b, 0xae, 0x12, 0xaf, 0x46, 0x2b,
+    0x10, 0x70, 0xbe, 0xf1, 0xab, 0x5e, 0x36, 0x06,
+    0x87, 0x2c, 0xa1, 0x0d, 0xee, 0x15, 0xb3, 0x24,
+    0x9b, 0x1a, 0x1b, 0x95, 0x8f, 0x23, 0x13, 0x4c,
+    0x4b, 0xcc, 0xb7, 0xd0, 0x32, 0x00, 0xbc, 0xe4,
+    0x20, 0xa2, 0xf8, 0xeb, 0x66, 0xdc, 0xf3, 0x64,
+    0x4d, 0x14, 0x23, 0xc1, 0xb5, 0x69, 0x90, 0x03,
+    0xc1, 0x3e, 0xce, 0xf4, 0xbf, 0x38, 0xa3, 0xb6,
+    0x0e, 0xed, 0xc3, 0x40, 0x33, 0xba, 0xc1, 0x90,
+    0x27, 0x83, 0xdc, 0x6d, 0x89, 0xe2, 0xe7, 0x74,
+    0x18, 0x8a, 0x43, 0x9c, 0x7e, 0xbc, 0xc0, 0x67,
+    0x2d, 0xbd, 0xa4, 0xdd, 0xcf, 0xb2, 0x79, 0x46,
+    0x13, 0xb0, 0xbe, 0x41, 0x31, 0x5e, 0xf7, 0x78,
+    0x70, 0x8a, 0x70, 0xee, 0x7d, 0x75, 0x16, 0x5c
+};
+
+static const u8 T20[] = {
+    0x8b, 0x30, 0x7f, 0x6b, 0x33, 0x28, 0x6d, 0x0a,
+    0xb0, 0x26, 0xa9, 0xed, 0x3f, 0xe1, 0xe8, 0x5f
+};
+
+# define TEST_CASE(n)    do {                                    \
+        u8 out[sizeof(P##n)];                                   \
+        AES_set_encrypt_key(K##n,sizeof(K##n)*8,&key);          \
+        CRYPTO_gcm128_init(&ctx,&key,(block128_f)AES_encrypt);  \
+        CRYPTO_gcm128_setiv(&ctx,IV##n,sizeof(IV##n));          \
+        memset(out,0,sizeof(out));                              \
+        if (A##n) CRYPTO_gcm128_aad(&ctx,A##n,sizeof(A##n));    \
+        if (P##n) CRYPTO_gcm128_encrypt(&ctx,P##n,out,sizeof(out));     \
+        if (CRYPTO_gcm128_finish(&ctx,T##n,16) ||               \
+            (C##n && memcmp(out,C##n,sizeof(out))))             \
+                ret++, printf ("encrypt test#%d failed.\n",n);  \
+        CRYPTO_gcm128_setiv(&ctx,IV##n,sizeof(IV##n));          \
+        memset(out,0,sizeof(out));                              \
+        if (A##n) CRYPTO_gcm128_aad(&ctx,A##n,sizeof(A##n));    \
+        if (C##n) CRYPTO_gcm128_decrypt(&ctx,C##n,out,sizeof(out));     \
+        if (CRYPTO_gcm128_finish(&ctx,T##n,16) ||               \
+            (P##n && memcmp(out,P##n,sizeof(out))))             \
+                ret++, printf ("decrypt test#%d failed.\n",n);  \
+        } while(0)
+
+int main()
+{
+    GCM128_CONTEXT ctx;
+    AES_KEY key;
+    int ret = 0;
+
+    TEST_CASE(1);
+    TEST_CASE(2);
+    TEST_CASE(3);
+    TEST_CASE(4);
+    TEST_CASE(5);
+    TEST_CASE(6);
+    TEST_CASE(7);
+    TEST_CASE(8);
+    TEST_CASE(9);
+    TEST_CASE(10);
+    TEST_CASE(11);
+    TEST_CASE(12);
+    TEST_CASE(13);
+    TEST_CASE(14);
+    TEST_CASE(15);
+    TEST_CASE(16);
+    TEST_CASE(17);
+    TEST_CASE(18);
+    TEST_CASE(19);
+    TEST_CASE(20);
+
+# ifdef OPENSSL_CPUID_OBJ
+    {
+        size_t start, stop, gcm_t, ctr_t, OPENSSL_rdtsc();
+        union {
+            u64 u;
+            u8 c[1024];
+        } buf;
+        int i;
+
+        AES_set_encrypt_key(K1, sizeof(K1) * 8, &key);
+        CRYPTO_gcm128_init(&ctx, &key, (block128_f) AES_encrypt);
+        CRYPTO_gcm128_setiv(&ctx, IV1, sizeof(IV1));
+
+        CRYPTO_gcm128_encrypt(&ctx, buf.c, buf.c, sizeof(buf));
+        start = OPENSSL_rdtsc();
+        CRYPTO_gcm128_encrypt(&ctx, buf.c, buf.c, sizeof(buf));
+        gcm_t = OPENSSL_rdtsc() - start;
+
+        CRYPTO_ctr128_encrypt(buf.c, buf.c, sizeof(buf),
+                              &key, ctx.Yi.c, ctx.EKi.c, &ctx.mres,
+                              (block128_f) AES_encrypt);
+        start = OPENSSL_rdtsc();
+        CRYPTO_ctr128_encrypt(buf.c, buf.c, sizeof(buf),
+                              &key, ctx.Yi.c, ctx.EKi.c, &ctx.mres,
+                              (block128_f) AES_encrypt);
+        ctr_t = OPENSSL_rdtsc() - start;
+
+        printf("%.2f-%.2f=%.2f\n",
+               gcm_t / (double)sizeof(buf),
+               ctr_t / (double)sizeof(buf),
+               (gcm_t - ctr_t) / (double)sizeof(buf));
+#  ifdef GHASH
+        {
+            void (*gcm_ghash_p) (u64 Xi[2], const u128 Htable[16],
+                                 const u8 *inp, size_t len) = ctx.ghash;
+
+            GHASH((&ctx), buf.c, sizeof(buf));
+            start = OPENSSL_rdtsc();
+            for (i = 0; i < 100; ++i)
+                GHASH((&ctx), buf.c, sizeof(buf));
+            gcm_t = OPENSSL_rdtsc() - start;
+            printf("%.2f\n", gcm_t / (double)sizeof(buf) / (double)i);
+        }
+#  endif
+    }
+# endif
+
+    return ret;
+}
+#endif
diff --git a/crypto/modes/modes_lcl.h b/crypto/modes/modes_lcl.h
new file mode 100644
index 000000000..31ab06414
--- /dev/null
+++ b/crypto/modes/modes_lcl.h
@@ -0,0 +1,189 @@
+/*
+ * Copyright 2010-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+#ifndef HEADER_MODES_LCL_H
+#define HEADER_MODES_LCL_H
+
+#include 
+
+#if (defined(_WIN32) || defined(_WIN64)) && !defined(__MINGW32__)
+typedef __int64 i64;
+typedef unsigned __int64 u64;
+# define U64(C) C##UI64
+#elif defined(__arch64__)
+typedef long i64;
+typedef unsigned long u64;
+# define U64(C) C##UL
+#else
+typedef long long i64;
+typedef unsigned long long u64;
+# define U64(C) C##ULL
+#endif
+
+typedef unsigned int u32;
+typedef unsigned char u8;
+
+#define STRICT_ALIGNMENT 1
+#ifndef PEDANTIC
+# if defined(__i386)    || defined(__i386__)    || \
+     defined(__x86_64)  || defined(__x86_64__)  || \
+     defined(_M_IX86)   || defined(_M_AMD64)    || defined(_M_X64) || \
+     defined(__aarch64__)                       || \
+     defined(__s390__)  || defined(__s390x__)
+#  undef STRICT_ALIGNMENT
+# endif
+#endif
+
+#if !defined(PEDANTIC) && !defined(OPENSSL_NO_ASM) && !defined(OPENSSL_NO_INLINE_ASM)
+# if defined(__GNUC__) && __GNUC__>=2
+#  if defined(__x86_64) || defined(__x86_64__)
+#   define BSWAP8(x) ({ u64 ret_=(x);                   \
+                        asm ("bswapq %0"                \
+                        : "+r"(ret_));   ret_;          })
+#   define BSWAP4(x) ({ u32 ret_=(x);                   \
+                        asm ("bswapl %0"                \
+                        : "+r"(ret_));   ret_;          })
+#  elif (defined(__i386) || defined(__i386__)) && !defined(I386_ONLY)
+#   define BSWAP8(x) ({ u32 lo_=(u64)(x)>>32,hi_=(x);   \
+                        asm ("bswapl %0; bswapl %1"     \
+                        : "+r"(hi_),"+r"(lo_));         \
+                        (u64)hi_<<32|lo_;               })
+#   define BSWAP4(x) ({ u32 ret_=(x);                   \
+                        asm ("bswapl %0"                \
+                        : "+r"(ret_));   ret_;          })
+#  elif defined(__aarch64__)
+#   define BSWAP8(x) ({ u64 ret_;                       \
+                        asm ("rev %0,%1"                \
+                        : "=r"(ret_) : "r"(x)); ret_;   })
+#   define BSWAP4(x) ({ u32 ret_;                       \
+                        asm ("rev %w0,%w1"              \
+                        : "=r"(ret_) : "r"(x)); ret_;   })
+#  elif (defined(__arm__) || defined(__arm)) && !defined(STRICT_ALIGNMENT)
+#   define BSWAP8(x) ({ u32 lo_=(u64)(x)>>32,hi_=(x);   \
+                        asm ("rev %0,%0; rev %1,%1"     \
+                        : "+r"(hi_),"+r"(lo_));         \
+                        (u64)hi_<<32|lo_;               })
+#   define BSWAP4(x) ({ u32 ret_;                       \
+                        asm ("rev %0,%1"                \
+                        : "=r"(ret_) : "r"((u32)(x)));  \
+                        ret_;                           })
+#  endif
+# elif defined(_MSC_VER)
+#  if _MSC_VER>=1300
+#   pragma intrinsic(_byteswap_uint64,_byteswap_ulong)
+#   define BSWAP8(x)    _byteswap_uint64((u64)(x))
+#   define BSWAP4(x)    _byteswap_ulong((u32)(x))
+#  elif defined(_M_IX86)
+__inline u32 _bswap4(u32 val)
+{
+_asm mov eax, val _asm bswap eax}
+#   define BSWAP4(x)    _bswap4(x)
+#  endif
+# endif
+#endif
+#if defined(BSWAP4) && !defined(STRICT_ALIGNMENT)
+# define GETU32(p)       BSWAP4(*(const u32 *)(p))
+# define PUTU32(p,v)     *(u32 *)(p) = BSWAP4(v)
+#else
+# define GETU32(p)       ((u32)(p)[0]<<24|(u32)(p)[1]<<16|(u32)(p)[2]<<8|(u32)(p)[3])
+# define PUTU32(p,v)     ((p)[0]=(u8)((v)>>24),(p)[1]=(u8)((v)>>16),(p)[2]=(u8)((v)>>8),(p)[3]=(u8)(v))
+#endif
+/*- GCM definitions */ typedef struct {
+    u64 hi, lo;
+} u128;
+
+#ifdef  TABLE_BITS
+# undef  TABLE_BITS
+#endif
+/*
+ * Even though permitted values for TABLE_BITS are 8, 4 and 1, it should
+ * never be set to 8 [or 1]. For further information see gcm128.c.
+ */
+#define TABLE_BITS 4
+
+struct gcm128_context {
+    /* Following 6 names follow names in GCM specification */
+    union {
+        u64 u[2];
+        u32 d[4];
+        u8 c[16];
+        size_t t[16 / sizeof(size_t)];
+    } Yi, EKi, EK0, len, Xi, H;
+    /*
+     * Relative position of Xi, H and pre-computed Htable is used in some
+     * assembler modules, i.e. don't change the order!
+     */
+#if TABLE_BITS==8
+    u128 Htable[256];
+#else
+    u128 Htable[16];
+    void (*gmult) (u64 Xi[2], const u128 Htable[16]);
+    void (*ghash) (u64 Xi[2], const u128 Htable[16], const u8 *inp,
+                   size_t len);
+#endif
+    unsigned int mres, ares;
+    block128_f block;
+    void *key;
+};
+
+struct xts128_context {
+    void *key1, *key2;
+    block128_f block1, block2;
+};
+
+struct ccm128_context {
+    union {
+        u64 u[2];
+        u8 c[16];
+    } nonce, cmac;
+    u64 blocks;
+    block128_f block;
+    void *key;
+};
+
+#ifndef OPENSSL_NO_OCB
+
+typedef union {
+    u64 a[2];
+    unsigned char c[16];
+} OCB_BLOCK;
+# define ocb_block16_xor(in1,in2,out) \
+    ( (out)->a[0]=(in1)->a[0]^(in2)->a[0], \
+      (out)->a[1]=(in1)->a[1]^(in2)->a[1] )
+# if STRICT_ALIGNMENT
+#  define ocb_block16_xor_misaligned(in1,in2,out) \
+    ocb_block_xor((in1)->c,(in2)->c,16,(out)->c)
+# else
+#  define ocb_block16_xor_misaligned ocb_block16_xor
+# endif
+
+struct ocb128_context {
+    /* Need both encrypt and decrypt key schedules for decryption */
+    block128_f encrypt;
+    block128_f decrypt;
+    void *keyenc;
+    void *keydec;
+    ocb128_f stream;    /* direction dependent */
+    /* Key dependent variables. Can be reused if key remains the same */
+    size_t l_index;
+    size_t max_l_index;
+    OCB_BLOCK l_star;
+    OCB_BLOCK l_dollar;
+    OCB_BLOCK *l;
+    /* Must be reset for each session */
+    u64 blocks_hashed;
+    u64 blocks_processed;
+    OCB_BLOCK tag;
+    OCB_BLOCK offset_aad;
+    OCB_BLOCK sum;
+    OCB_BLOCK offset;
+    OCB_BLOCK checksum;
+};
+#endif                          /* OPENSSL_NO_OCB */
+#endif
+
diff --git a/crypto/modes/ocb128.c b/crypto/modes/ocb128.c
new file mode 100644
index 000000000..c3bd13bbe
--- /dev/null
+++ b/crypto/modes/ocb128.c
@@ -0,0 +1,568 @@
+/*
+ * Copyright 2014-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include "modes_lcl.h"
+
+#ifndef OPENSSL_NO_OCB
+
+/*
+ * Calculate the number of binary trailing zero's in any given number
+ */
+static u32 ocb_ntz(u64 n)
+{
+    u32 cnt = 0;
+
+    /*
+     * We do a right-to-left simple sequential search. This is surprisingly
+     * efficient as the distribution of trailing zeros is not uniform,
+     * e.g. the number of possible inputs with no trailing zeros is equal to
+     * the number with 1 or more; the number with exactly 1 is equal to the
+     * number with 2 or more, etc. Checking the last two bits covers 75% of
+     * all numbers. Checking the last three covers 87.5%
+     */
+    while (!(n & 1)) {
+        n >>= 1;
+        cnt++;
+    }
+    return cnt;
+}
+
+/*
+ * Shift a block of 16 bytes left by shift bits
+ */
+static void ocb_block_lshift(const unsigned char *in, size_t shift,
+                             unsigned char *out)
+{
+    unsigned char shift_mask;
+    int i;
+    unsigned char mask[15];
+
+    shift_mask = 0xff;
+    shift_mask <<= (8 - shift);
+    for (i = 15; i >= 0; i--) {
+        if (i > 0) {
+            mask[i - 1] = in[i] & shift_mask;
+            mask[i - 1] >>= 8 - shift;
+        }
+        out[i] = in[i] << shift;
+
+        if (i != 15) {
+            out[i] ^= mask[i];
+        }
+    }
+}
+
+/*
+ * Perform a "double" operation as per OCB spec
+ */
+static void ocb_double(OCB_BLOCK *in, OCB_BLOCK *out)
+{
+    unsigned char mask;
+
+    /*
+     * Calculate the mask based on the most significant bit. There are more
+     * efficient ways to do this - but this way is constant time
+     */
+    mask = in->c[0] & 0x80;
+    mask >>= 7;
+    mask *= 135;
+
+    ocb_block_lshift(in->c, 1, out->c);
+
+    out->c[15] ^= mask;
+}
+
+/*
+ * Perform an xor on in1 and in2 - each of len bytes. Store result in out
+ */
+static void ocb_block_xor(const unsigned char *in1,
+                          const unsigned char *in2, size_t len,
+                          unsigned char *out)
+{
+    size_t i;
+    for (i = 0; i < len; i++) {
+        out[i] = in1[i] ^ in2[i];
+    }
+}
+
+/*
+ * Lookup L_index in our lookup table. If we haven't already got it we need to
+ * calculate it
+ */
+static OCB_BLOCK *ocb_lookup_l(OCB128_CONTEXT *ctx, size_t idx)
+{
+    size_t l_index = ctx->l_index;
+
+    if (idx <= l_index) {
+        return ctx->l + idx;
+    }
+
+    /* We don't have it - so calculate it */
+    if (idx >= ctx->max_l_index) {
+        void *tmp_ptr;
+        /*
+         * Each additional entry allows to process almost double as
+         * much data, so that in linear world the table will need to
+         * be expanded with smaller and smaller increments. Originally
+         * it was doubling in size, which was a waste. Growing it
+         * linearly is not formally optimal, but is simpler to implement.
+         * We grow table by minimally required 4*n that would accommodate
+         * the index.
+         */
+        ctx->max_l_index += (idx - ctx->max_l_index + 4) & ~3;
+        tmp_ptr =
+            OPENSSL_realloc(ctx->l, ctx->max_l_index * sizeof(OCB_BLOCK));
+        if (tmp_ptr == NULL) /* prevent ctx->l from being clobbered */
+            return NULL;
+        ctx->l = tmp_ptr;
+    }
+    while (l_index < idx) {
+        ocb_double(ctx->l + l_index, ctx->l + l_index + 1);
+        l_index++;
+    }
+    ctx->l_index = l_index;
+
+    return ctx->l + idx;
+}
+
+/*
+ * Create a new OCB128_CONTEXT
+ */
+OCB128_CONTEXT *CRYPTO_ocb128_new(void *keyenc, void *keydec,
+                                  block128_f encrypt, block128_f decrypt,
+                                  ocb128_f stream)
+{
+    OCB128_CONTEXT *octx;
+    int ret;
+
+    if ((octx = OPENSSL_malloc(sizeof(*octx))) != NULL) {
+        ret = CRYPTO_ocb128_init(octx, keyenc, keydec, encrypt, decrypt,
+                                 stream);
+        if (ret)
+            return octx;
+        OPENSSL_free(octx);
+    }
+
+    return NULL;
+}
+
+/*
+ * Initialise an existing OCB128_CONTEXT
+ */
+int CRYPTO_ocb128_init(OCB128_CONTEXT *ctx, void *keyenc, void *keydec,
+                       block128_f encrypt, block128_f decrypt,
+                       ocb128_f stream)
+{
+    memset(ctx, 0, sizeof(*ctx));
+    ctx->l_index = 0;
+    ctx->max_l_index = 5;
+    ctx->l = OPENSSL_malloc(ctx->max_l_index * 16);
+    if (ctx->l == NULL)
+        return 0;
+
+    /*
+     * We set both the encryption and decryption key schedules - decryption
+     * needs both. Don't really need decryption schedule if only doing
+     * encryption - but it simplifies things to take it anyway
+     */
+    ctx->encrypt = encrypt;
+    ctx->decrypt = decrypt;
+    ctx->stream = stream;
+    ctx->keyenc = keyenc;
+    ctx->keydec = keydec;
+
+    /* L_* = ENCIPHER(K, zeros(128)) */
+    ctx->encrypt(ctx->l_star.c, ctx->l_star.c, ctx->keyenc);
+
+    /* L_$ = double(L_*) */
+    ocb_double(&ctx->l_star, &ctx->l_dollar);
+
+    /* L_0 = double(L_$) */
+    ocb_double(&ctx->l_dollar, ctx->l);
+
+    /* L_{i} = double(L_{i-1}) */
+    ocb_double(ctx->l, ctx->l+1);
+    ocb_double(ctx->l+1, ctx->l+2);
+    ocb_double(ctx->l+2, ctx->l+3);
+    ocb_double(ctx->l+3, ctx->l+4);
+    ctx->l_index = 4;   /* enough to process up to 496 bytes */
+
+    return 1;
+}
+
+/*
+ * Copy an OCB128_CONTEXT object
+ */
+int CRYPTO_ocb128_copy_ctx(OCB128_CONTEXT *dest, OCB128_CONTEXT *src,
+                           void *keyenc, void *keydec)
+{
+    memcpy(dest, src, sizeof(OCB128_CONTEXT));
+    if (keyenc)
+        dest->keyenc = keyenc;
+    if (keydec)
+        dest->keydec = keydec;
+    if (src->l) {
+        dest->l = OPENSSL_malloc(src->max_l_index * 16);
+        if (dest->l == NULL)
+            return 0;
+        memcpy(dest->l, src->l, (src->l_index + 1) * 16);
+    }
+    return 1;
+}
+
+/*
+ * Set the IV to be used for this operation. Must be 1 - 15 bytes.
+ */
+int CRYPTO_ocb128_setiv(OCB128_CONTEXT *ctx, const unsigned char *iv,
+                        size_t len, size_t taglen)
+{
+    unsigned char ktop[16], tmp[16], mask;
+    unsigned char stretch[24], nonce[16];
+    size_t bottom, shift;
+
+    /*
+     * Spec says IV is 120 bits or fewer - it allows non byte aligned lengths.
+     * We don't support this at this stage
+     */
+    if ((len > 15) || (len < 1) || (taglen > 16) || (taglen < 1)) {
+        return -1;
+    }
+
+    /* Nonce = num2str(TAGLEN mod 128,7) || zeros(120-bitlen(N)) || 1 || N */
+    nonce[0] = ((taglen * 8) % 128) << 1;
+    memset(nonce + 1, 0, 15);
+    memcpy(nonce + 16 - len, iv, len);
+    nonce[15 - len] |= 1;
+
+    /* Ktop = ENCIPHER(K, Nonce[1..122] || zeros(6)) */
+    memcpy(tmp, nonce, 16);
+    tmp[15] &= 0xc0;
+    ctx->encrypt(tmp, ktop, ctx->keyenc);
+
+    /* Stretch = Ktop || (Ktop[1..64] xor Ktop[9..72]) */
+    memcpy(stretch, ktop, 16);
+    ocb_block_xor(ktop, ktop + 1, 8, stretch + 16);
+
+    /* bottom = str2num(Nonce[123..128]) */
+    bottom = nonce[15] & 0x3f;
+
+    /* Offset_0 = Stretch[1+bottom..128+bottom] */
+    shift = bottom % 8;
+    ocb_block_lshift(stretch + (bottom / 8), shift, ctx->offset.c);
+    mask = 0xff;
+    mask <<= 8 - shift;
+    ctx->offset.c[15] |=
+        (*(stretch + (bottom / 8) + 16) & mask) >> (8 - shift);
+
+    return 1;
+}
+
+/*
+ * Provide any AAD. This can be called multiple times. Only the final time can
+ * have a partial block
+ */
+int CRYPTO_ocb128_aad(OCB128_CONTEXT *ctx, const unsigned char *aad,
+                      size_t len)
+{
+    u64 i, all_num_blocks;
+    size_t num_blocks, last_len;
+    OCB_BLOCK tmp1;
+    OCB_BLOCK tmp2;
+
+    /* Calculate the number of blocks of AAD provided now, and so far */
+    num_blocks = len / 16;
+    all_num_blocks = num_blocks + ctx->blocks_hashed;
+
+    /* Loop through all full blocks of AAD */
+    for (i = ctx->blocks_hashed + 1; i <= all_num_blocks; i++) {
+        OCB_BLOCK *lookup;
+        OCB_BLOCK *aad_block;
+
+        /* Offset_i = Offset_{i-1} xor L_{ntz(i)} */
+        lookup = ocb_lookup_l(ctx, ocb_ntz(i));
+        if (lookup == NULL)
+            return 0;
+        ocb_block16_xor(&ctx->offset_aad, lookup, &ctx->offset_aad);
+
+        /* Sum_i = Sum_{i-1} xor ENCIPHER(K, A_i xor Offset_i) */
+        aad_block = (OCB_BLOCK *)(aad + ((i - ctx->blocks_hashed - 1) * 16));
+        ocb_block16_xor(&ctx->offset_aad, aad_block, &tmp1);
+        ctx->encrypt(tmp1.c, tmp2.c, ctx->keyenc);
+        ocb_block16_xor(&ctx->sum, &tmp2, &ctx->sum);
+    }
+
+    /*
+     * Check if we have any partial blocks left over. This is only valid in the
+     * last call to this function
+     */
+    last_len = len % 16;
+
+    if (last_len > 0) {
+        /* Offset_* = Offset_m xor L_* */
+        ocb_block16_xor(&ctx->offset_aad, &ctx->l_star, &ctx->offset_aad);
+
+        /* CipherInput = (A_* || 1 || zeros(127-bitlen(A_*))) xor Offset_* */
+        memset(&tmp1, 0, 16);
+        memcpy(&tmp1, aad + (num_blocks * 16), last_len);
+        ((unsigned char *)&tmp1)[last_len] = 0x80;
+        ocb_block16_xor(&ctx->offset_aad, &tmp1, &tmp2);
+
+        /* Sum = Sum_m xor ENCIPHER(K, CipherInput) */
+        ctx->encrypt(tmp2.c, tmp1.c, ctx->keyenc);
+        ocb_block16_xor(&ctx->sum, &tmp1, &ctx->sum);
+    }
+
+    ctx->blocks_hashed = all_num_blocks;
+
+    return 1;
+}
+
+/*
+ * Provide any data to be encrypted. This can be called multiple times. Only
+ * the final time can have a partial block
+ */
+int CRYPTO_ocb128_encrypt(OCB128_CONTEXT *ctx,
+                          const unsigned char *in, unsigned char *out,
+                          size_t len)
+{
+    u64 i, all_num_blocks;
+    size_t num_blocks, last_len;
+    OCB_BLOCK tmp1;
+    OCB_BLOCK tmp2;
+    OCB_BLOCK pad;
+
+    /*
+     * Calculate the number of blocks of data to be encrypted provided now, and
+     * so far
+     */
+    num_blocks = len / 16;
+    all_num_blocks = num_blocks + ctx->blocks_processed;
+
+    if (num_blocks && all_num_blocks == (size_t)all_num_blocks
+        && ctx->stream != NULL) {
+        size_t max_idx = 0, top = (size_t)all_num_blocks;
+
+        /*
+         * See how many L_{i} entries we need to process data at hand
+         * and pre-compute missing entries in the table [if any]...
+         */
+        while (top >>= 1)
+            max_idx++;
+        if (ocb_lookup_l(ctx, max_idx) == NULL)
+            return 0;
+
+        ctx->stream(in, out, num_blocks, ctx->keyenc,
+                    (size_t)ctx->blocks_processed + 1, ctx->offset.c,
+                    (const unsigned char (*)[16])ctx->l, ctx->checksum.c);
+    } else {
+        /* Loop through all full blocks to be encrypted */
+        for (i = ctx->blocks_processed + 1; i <= all_num_blocks; i++) {
+            OCB_BLOCK *lookup;
+            OCB_BLOCK *inblock;
+            OCB_BLOCK *outblock;
+
+            /* Offset_i = Offset_{i-1} xor L_{ntz(i)} */
+            lookup = ocb_lookup_l(ctx, ocb_ntz(i));
+            if (lookup == NULL)
+                return 0;
+            ocb_block16_xor(&ctx->offset, lookup, &ctx->offset);
+
+            /* C_i = Offset_i xor ENCIPHER(K, P_i xor Offset_i) */
+            inblock =
+                (OCB_BLOCK *)(in + ((i - ctx->blocks_processed - 1) * 16));
+            ocb_block16_xor_misaligned(&ctx->offset, inblock, &tmp1);
+            /* Checksum_i = Checksum_{i-1} xor P_i */
+            ocb_block16_xor_misaligned(&ctx->checksum, inblock, &ctx->checksum);
+            ctx->encrypt(tmp1.c, tmp2.c, ctx->keyenc);
+            outblock =
+                (OCB_BLOCK *)(out + ((i - ctx->blocks_processed - 1) * 16));
+            ocb_block16_xor_misaligned(&ctx->offset, &tmp2, outblock);
+        }
+    }
+
+    /*
+     * Check if we have any partial blocks left over. This is only valid in the
+     * last call to this function
+     */
+    last_len = len % 16;
+
+    if (last_len > 0) {
+        /* Offset_* = Offset_m xor L_* */
+        ocb_block16_xor(&ctx->offset, &ctx->l_star, &ctx->offset);
+
+        /* Pad = ENCIPHER(K, Offset_*) */
+        ctx->encrypt(ctx->offset.c, pad.c, ctx->keyenc);
+
+        /* C_* = P_* xor Pad[1..bitlen(P_*)] */
+        ocb_block_xor(in + (len / 16) * 16, (unsigned char *)&pad, last_len,
+                      out + (num_blocks * 16));
+
+        /* Checksum_* = Checksum_m xor (P_* || 1 || zeros(127-bitlen(P_*))) */
+        memset(&tmp1, 0, 16);
+        memcpy(&tmp1, in + (len / 16) * 16, last_len);
+        ((unsigned char *)(&tmp1))[last_len] = 0x80;
+        ocb_block16_xor(&ctx->checksum, &tmp1, &ctx->checksum);
+    }
+
+    ctx->blocks_processed = all_num_blocks;
+
+    return 1;
+}
+
+/*
+ * Provide any data to be decrypted. This can be called multiple times. Only
+ * the final time can have a partial block
+ */
+int CRYPTO_ocb128_decrypt(OCB128_CONTEXT *ctx,
+                          const unsigned char *in, unsigned char *out,
+                          size_t len)
+{
+    u64 i, all_num_blocks;
+    size_t num_blocks, last_len;
+    OCB_BLOCK tmp1;
+    OCB_BLOCK tmp2;
+    OCB_BLOCK pad;
+
+    /*
+     * Calculate the number of blocks of data to be decrypted provided now, and
+     * so far
+     */
+    num_blocks = len / 16;
+    all_num_blocks = num_blocks + ctx->blocks_processed;
+
+    if (num_blocks && all_num_blocks == (size_t)all_num_blocks
+        && ctx->stream != NULL) {
+        size_t max_idx = 0, top = (size_t)all_num_blocks;
+
+        /*
+         * See how many L_{i} entries we need to process data at hand
+         * and pre-compute missing entries in the table [if any]...
+         */
+        while (top >>= 1)
+            max_idx++;
+        if (ocb_lookup_l(ctx, max_idx) == NULL)
+            return 0;
+
+        ctx->stream(in, out, num_blocks, ctx->keydec,
+                    (size_t)ctx->blocks_processed + 1, ctx->offset.c,
+                    (const unsigned char (*)[16])ctx->l, ctx->checksum.c);
+    } else {
+        /* Loop through all full blocks to be decrypted */
+        for (i = ctx->blocks_processed + 1; i <= all_num_blocks; i++) {
+            OCB_BLOCK *inblock;
+            OCB_BLOCK *outblock;
+
+            /* Offset_i = Offset_{i-1} xor L_{ntz(i)} */
+            OCB_BLOCK *lookup = ocb_lookup_l(ctx, ocb_ntz(i));
+            if (lookup == NULL)
+                return 0;
+            ocb_block16_xor(&ctx->offset, lookup, &ctx->offset);
+
+            /* P_i = Offset_i xor DECIPHER(K, C_i xor Offset_i) */
+            inblock =
+                (OCB_BLOCK *)(in + ((i - ctx->blocks_processed - 1) * 16));
+            ocb_block16_xor_misaligned(&ctx->offset, inblock, &tmp1);
+            ctx->decrypt(tmp1.c, tmp2.c, ctx->keydec);
+            outblock =
+                (OCB_BLOCK *)(out + ((i - ctx->blocks_processed - 1) * 16));
+            ocb_block16_xor_misaligned(&ctx->offset, &tmp2, outblock);
+
+            /* Checksum_i = Checksum_{i-1} xor P_i */
+            ocb_block16_xor_misaligned(&ctx->checksum, outblock, &ctx->checksum);
+        }
+    }
+
+    /*
+     * Check if we have any partial blocks left over. This is only valid in the
+     * last call to this function
+     */
+    last_len = len % 16;
+
+    if (last_len > 0) {
+        /* Offset_* = Offset_m xor L_* */
+        ocb_block16_xor(&ctx->offset, &ctx->l_star, &ctx->offset);
+
+        /* Pad = ENCIPHER(K, Offset_*) */
+        ctx->encrypt(ctx->offset.c, pad.c, ctx->keyenc);
+
+        /* P_* = C_* xor Pad[1..bitlen(C_*)] */
+        ocb_block_xor(in + (len / 16) * 16, (unsigned char *)&pad, last_len,
+                      out + (num_blocks * 16));
+
+        /* Checksum_* = Checksum_m xor (P_* || 1 || zeros(127-bitlen(P_*))) */
+        memset(&tmp1, 0, 16);
+        memcpy(&tmp1, out + (len / 16) * 16, last_len);
+        ((unsigned char *)(&tmp1))[last_len] = 0x80;
+        ocb_block16_xor(&ctx->checksum, &tmp1, &ctx->checksum);
+    }
+
+    ctx->blocks_processed = all_num_blocks;
+
+    return 1;
+}
+
+/*
+ * Calculate the tag and verify it against the supplied tag
+ */
+int CRYPTO_ocb128_finish(OCB128_CONTEXT *ctx, const unsigned char *tag,
+                         size_t len)
+{
+    OCB_BLOCK tmp1, tmp2;
+
+    /*
+     * Tag = ENCIPHER(K, Checksum_* xor Offset_* xor L_$) xor HASH(K,A)
+     */
+    ocb_block16_xor(&ctx->checksum, &ctx->offset, &tmp1);
+    ocb_block16_xor(&tmp1, &ctx->l_dollar, &tmp2);
+    ctx->encrypt(tmp2.c, tmp1.c, ctx->keyenc);
+    ocb_block16_xor(&tmp1, &ctx->sum, &ctx->tag);
+
+    if (len > 16 || len < 1) {
+        return -1;
+    }
+
+    /* Compare the tag if we've been given one */
+    if (tag)
+        return CRYPTO_memcmp(&ctx->tag, tag, len);
+    else
+        return -1;
+}
+
+/*
+ * Retrieve the calculated tag
+ */
+int CRYPTO_ocb128_tag(OCB128_CONTEXT *ctx, unsigned char *tag, size_t len)
+{
+    if (len > 16 || len < 1) {
+        return -1;
+    }
+
+    /* Calculate the tag */
+    CRYPTO_ocb128_finish(ctx, NULL, 0);
+
+    /* Copy the tag into the supplied buffer */
+    memcpy(tag, &ctx->tag, len);
+
+    return 1;
+}
+
+/*
+ * Release all resources
+ */
+void CRYPTO_ocb128_cleanup(OCB128_CONTEXT *ctx)
+{
+    if (ctx) {
+        OPENSSL_clear_free(ctx->l, ctx->max_l_index * 16);
+        OPENSSL_cleanse(ctx, sizeof(*ctx));
+    }
+}
+
+#endif                          /* OPENSSL_NO_OCB */
diff --git a/crypto/modes/ofb128.c b/crypto/modes/ofb128.c
new file mode 100644
index 000000000..83092564c
--- /dev/null
+++ b/crypto/modes/ofb128.c
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2008-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "modes_lcl.h"
+#include 
+
+/*
+ * The input and output encrypted as though 128bit ofb mode is being used.
+ * The extra state information to record how much of the 128bit block we have
+ * used is contained in *num;
+ */
+void CRYPTO_ofb128_encrypt(const unsigned char *in, unsigned char *out,
+                           size_t len, const void *key,
+                           unsigned char ivec[16], int *num, block128_f block)
+{
+    unsigned int n;
+    size_t l = 0;
+
+    n = *num;
+
+#if !defined(OPENSSL_SMALL_FOOTPRINT)
+    if (16 % sizeof(size_t) == 0) { /* always true actually */
+        do {
+            while (n && len) {
+                *(out++) = *(in++) ^ ivec[n];
+                --len;
+                n = (n + 1) % 16;
+            }
+# if defined(STRICT_ALIGNMENT)
+            if (((size_t)in | (size_t)out | (size_t)ivec) % sizeof(size_t) !=
+                0)
+                break;
+# endif
+            while (len >= 16) {
+                (*block) (ivec, ivec, key);
+                for (; n < 16; n += sizeof(size_t))
+                    *(size_t *)(out + n) =
+                        *(size_t *)(in + n) ^ *(size_t *)(ivec + n);
+                len -= 16;
+                out += 16;
+                in += 16;
+                n = 0;
+            }
+            if (len) {
+                (*block) (ivec, ivec, key);
+                while (len--) {
+                    out[n] = in[n] ^ ivec[n];
+                    ++n;
+                }
+            }
+            *num = n;
+            return;
+        } while (0);
+    }
+    /* the rest would be commonly eliminated by x86* compiler */
+#endif
+    while (l < len) {
+        if (n == 0) {
+            (*block) (ivec, ivec, key);
+        }
+        out[l] = in[l] ^ ivec[n];
+        ++l;
+        n = (n + 1) % 16;
+    }
+
+    *num = n;
+}
diff --git a/crypto/modes/wrap128.c b/crypto/modes/wrap128.c
new file mode 100644
index 000000000..46809a0e7
--- /dev/null
+++ b/crypto/modes/wrap128.c
@@ -0,0 +1,329 @@
+/*
+ * Copyright 2013-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/**  Beware!
+ *
+ *  Following wrapping modes were designed for AES but this implementation
+ *  allows you to use them for any 128 bit block cipher.
+ */
+
+#include "internal/cryptlib.h"
+#include 
+
+/** RFC 3394 section 2.2.3.1 Default Initial Value */
+static const unsigned char default_iv[] = {
+    0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6,
+};
+
+/** RFC 5649 section 3 Alternative Initial Value 32-bit constant */
+static const unsigned char default_aiv[] = {
+    0xA6, 0x59, 0x59, 0xA6
+};
+
+/** Input size limit: lower than maximum of standards but far larger than
+ *  anything that will be used in practice.
+ */
+#define CRYPTO128_WRAP_MAX (1UL << 31)
+
+/** Wrapping according to RFC 3394 section 2.2.1.
+ *
+ *  @param[in]  key    Key value.
+ *  @param[in]  iv     IV value. Length = 8 bytes. NULL = use default_iv.
+ *  @param[in]  in     Plaintext as n 64-bit blocks, n >= 2.
+ *  @param[in]  inlen  Length of in.
+ *  @param[out] out    Ciphertext. Minimal buffer length = (inlen + 8) bytes.
+ *                     Input and output buffers can overlap if block function
+ *                     supports that.
+ *  @param[in]  block  Block processing function.
+ *  @return            0 if inlen does not consist of n 64-bit blocks, n >= 2.
+ *                     or if inlen > CRYPTO128_WRAP_MAX.
+ *                     Output length if wrapping succeeded.
+ */
+size_t CRYPTO_128_wrap(void *key, const unsigned char *iv,
+                       unsigned char *out,
+                       const unsigned char *in, size_t inlen,
+                       block128_f block)
+{
+    unsigned char *A, B[16], *R;
+    size_t i, j, t;
+    if ((inlen & 0x7) || (inlen < 16) || (inlen > CRYPTO128_WRAP_MAX))
+        return 0;
+    A = B;
+    t = 1;
+    memmove(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);
+            block(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;
+}
+
+/** Unwrapping according to RFC 3394 section 2.2.2 steps 1-2.
+ *  The IV check (step 3) is responsibility of the caller.
+ *
+ *  @param[in]  key    Key value.
+ *  @param[out] iv     Unchecked IV value. Minimal buffer length = 8 bytes.
+ *  @param[out] out    Plaintext without IV.
+ *                     Minimal buffer length = (inlen - 8) bytes.
+ *                     Input and output buffers can overlap if block function
+ *                     supports that.
+ *  @param[in]  in     Ciphertext as n 64-bit blocks.
+ *  @param[in]  inlen  Length of in.
+ *  @param[in]  block  Block processing function.
+ *  @return            0 if inlen is out of range [24, CRYPTO128_WRAP_MAX]
+ *                     or if inlen is not a multiple of 8.
+ *                     Output length otherwise.
+ */
+static size_t crypto_128_unwrap_raw(void *key, unsigned char *iv,
+                                    unsigned char *out,
+                                    const unsigned char *in, size_t inlen,
+                                    block128_f block)
+{
+    unsigned char *A, B[16], *R;
+    size_t i, j, t;
+    inlen -= 8;
+    if ((inlen & 0x7) || (inlen < 16) || (inlen > CRYPTO128_WRAP_MAX))
+        return 0;
+    A = B;
+    t = 6 * (inlen >> 3);
+    memcpy(A, in, 8);
+    memmove(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);
+            block(B, B, key);
+            memcpy(R, B + 8, 8);
+        }
+    }
+    memcpy(iv, A, 8);
+    return inlen;
+}
+
+/** Unwrapping according to RFC 3394 section 2.2.2, including the IV check.
+ *  The first block of plaintext has to match the supplied IV, otherwise an
+ *  error is returned.
+ *
+ *  @param[in]  key    Key value.
+ *  @param[out] iv     IV value to match against. Length = 8 bytes.
+ *                     NULL = use default_iv.
+ *  @param[out] out    Plaintext without IV.
+ *                     Minimal buffer length = (inlen - 8) bytes.
+ *                     Input and output buffers can overlap if block function
+ *                     supports that.
+ *  @param[in]  in     Ciphertext as n 64-bit blocks.
+ *  @param[in]  inlen  Length of in.
+ *  @param[in]  block  Block processing function.
+ *  @return            0 if inlen is out of range [24, CRYPTO128_WRAP_MAX]
+ *                     or if inlen is not a multiple of 8
+ *                     or if IV doesn't match expected value.
+ *                     Output length otherwise.
+ */
+size_t CRYPTO_128_unwrap(void *key, const unsigned char *iv,
+                         unsigned char *out, const unsigned char *in,
+                         size_t inlen, block128_f block)
+{
+    size_t ret;
+    unsigned char got_iv[8];
+
+    ret = crypto_128_unwrap_raw(key, got_iv, out, in, inlen, block);
+    if (ret == 0)
+        return 0;
+
+    if (!iv)
+        iv = default_iv;
+    if (CRYPTO_memcmp(got_iv, iv, 8)) {
+        OPENSSL_cleanse(out, ret);
+        return 0;
+    }
+    return ret;
+}
+
+/** Wrapping according to RFC 5649 section 4.1.
+ *
+ *  @param[in]  key    Key value.
+ *  @param[in]  icv    (Non-standard) IV, 4 bytes. NULL = use default_aiv.
+ *  @param[out] out    Ciphertext. Minimal buffer length = (inlen + 15) bytes.
+ *                     Input and output buffers can overlap if block function
+ *                     supports that.
+ *  @param[in]  in     Plaintext as n 64-bit blocks, n >= 2.
+ *  @param[in]  inlen  Length of in.
+ *  @param[in]  block  Block processing function.
+ *  @return            0 if inlen is out of range [1, CRYPTO128_WRAP_MAX].
+ *                     Output length if wrapping succeeded.
+ */
+size_t CRYPTO_128_wrap_pad(void *key, const unsigned char *icv,
+                           unsigned char *out,
+                           const unsigned char *in, size_t inlen,
+                           block128_f block)
+{
+    /* n: number of 64-bit blocks in the padded key data
+     *
+     * If length of plain text is not a multiple of 8, pad the plain text octet
+     * string on the right with octets of zeros, where final length is the
+     * smallest multiple of 8 that is greater than length of plain text.
+     * If length of plain text is a multiple of 8, then there is no padding. */
+    const size_t blocks_padded = (inlen + 7) / 8; /* CEILING(m/8) */
+    const size_t padded_len = blocks_padded * 8;
+    const size_t padding_len = padded_len - inlen;
+    /* RFC 5649 section 3: Alternative Initial Value */
+    unsigned char aiv[8];
+    int ret;
+
+    /* Section 1: use 32-bit fixed field for plaintext octet length */
+    if (inlen == 0 || inlen >= CRYPTO128_WRAP_MAX)
+        return 0;
+
+    /* Section 3: Alternative Initial Value */
+    if (!icv)
+        memcpy(aiv, default_aiv, 4);
+    else
+        memcpy(aiv, icv, 4);    /* Standard doesn't mention this. */
+
+    aiv[4] = (inlen >> 24) & 0xFF;
+    aiv[5] = (inlen >> 16) & 0xFF;
+    aiv[6] = (inlen >> 8) & 0xFF;
+    aiv[7] = inlen & 0xFF;
+
+    if (padded_len == 8) {
+        /*
+         * Section 4.1 - special case in step 2: If the padded plaintext
+         * contains exactly eight octets, then prepend the AIV and encrypt
+         * the resulting 128-bit block using AES in ECB mode.
+         */
+        memmove(out + 8, in, inlen);
+        memcpy(out, aiv, 8);
+        memset(out + 8 + inlen, 0, padding_len);
+        block(out, out, key);
+        ret = 16;               /* AIV + padded input */
+    } else {
+        memmove(out, in, inlen);
+        memset(out + inlen, 0, padding_len); /* Section 4.1 step 1 */
+        ret = CRYPTO_128_wrap(key, aiv, out, out, padded_len, block);
+    }
+
+    return ret;
+}
+
+/** Unwrapping according to RFC 5649 section 4.2.
+ *
+ *  @param[in]  key    Key value.
+ *  @param[in]  icv    (Non-standard) IV, 4 bytes. NULL = use default_aiv.
+ *  @param[out] out    Plaintext. Minimal buffer length = inlen bytes.
+ *                     Input and output buffers can overlap if block function
+ *                     supports that.
+ *  @param[in]  in     Ciphertext as n 64-bit blocks.
+ *  @param[in]  inlen  Length of in.
+ *  @param[in]  block  Block processing function.
+ *  @return            0 if inlen is out of range [16, CRYPTO128_WRAP_MAX],
+ *                     or if inlen is not a multiple of 8
+ *                     or if IV and message length indicator doesn't match.
+ *                     Output length if unwrapping succeeded and IV matches.
+ */
+size_t CRYPTO_128_unwrap_pad(void *key, const unsigned char *icv,
+                             unsigned char *out,
+                             const unsigned char *in, size_t inlen,
+                             block128_f block)
+{
+    /* n: number of 64-bit blocks in the padded key data */
+    size_t n = inlen / 8 - 1;
+    size_t padded_len;
+    size_t padding_len;
+    size_t ptext_len;
+    /* RFC 5649 section 3: Alternative Initial Value */
+    unsigned char aiv[8];
+    static unsigned char zeros[8] = { 0x0 };
+    size_t ret;
+
+    /* Section 4.2: Ciphertext length has to be (n+1) 64-bit blocks. */
+    if ((inlen & 0x7) != 0 || inlen < 16 || inlen >= CRYPTO128_WRAP_MAX)
+        return 0;
+
+    memmove(out, in, inlen);
+    if (inlen == 16) {
+        /*
+         * Section 4.2 - special case in step 1: When n=1, the ciphertext
+         * contains exactly two 64-bit blocks and they are decrypted as a
+         * single AES block using AES in ECB mode: AIV | P[1] = DEC(K, C[0] |
+         * C[1])
+         */
+        block(out, out, key);
+        memcpy(aiv, out, 8);
+        /* Remove AIV */
+        memmove(out, out + 8, 8);
+        padded_len = 8;
+    } else {
+        padded_len = inlen - 8;
+        ret = crypto_128_unwrap_raw(key, aiv, out, out, inlen, block);
+        if (padded_len != ret) {
+            OPENSSL_cleanse(out, inlen);
+            return 0;
+        }
+    }
+
+    /*
+     * Section 3: AIV checks: Check that MSB(32,A) = A65959A6. Optionally a
+     * user-supplied value can be used (even if standard doesn't mention
+     * this).
+     */
+    if ((!icv && CRYPTO_memcmp(aiv, default_aiv, 4))
+        || (icv && CRYPTO_memcmp(aiv, icv, 4))) {
+        OPENSSL_cleanse(out, inlen);
+        return 0;
+    }
+
+    /*
+     * Check that 8*(n-1) < LSB(32,AIV) <= 8*n. If so, let ptext_len =
+     * LSB(32,AIV).
+     */
+
+    ptext_len =   ((unsigned int)aiv[4] << 24)
+                | ((unsigned int)aiv[5] << 16)
+                | ((unsigned int)aiv[6] <<  8)
+                |  (unsigned int)aiv[7];
+    if (8 * (n - 1) >= ptext_len || ptext_len > 8 * n) {
+        OPENSSL_cleanse(out, inlen);
+        return 0;
+    }
+
+    /*
+     * Check that the rightmost padding_len octets of the output data are
+     * zero.
+     */
+    padding_len = padded_len - ptext_len;
+    if (CRYPTO_memcmp(out + ptext_len, zeros, padding_len) != 0) {
+        OPENSSL_cleanse(out, inlen);
+        return 0;
+    }
+
+    /* Section 4.2 step 3: Remove padding */
+    return ptext_len;
+}
diff --git a/crypto/modes/xts128.c b/crypto/modes/xts128.c
new file mode 100644
index 000000000..81b1eacd5
--- /dev/null
+++ b/crypto/modes/xts128.c
@@ -0,0 +1,157 @@
+/*
+ * Copyright 2011-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "modes_lcl.h"
+#include 
+
+int CRYPTO_xts128_encrypt(const XTS128_CONTEXT *ctx,
+                          const unsigned char iv[16],
+                          const unsigned char *inp, unsigned char *out,
+                          size_t len, int enc)
+{
+    const union {
+        long one;
+        char little;
+    } is_endian = {
+        1
+    };
+    union {
+        u64 u[2];
+        u32 d[4];
+        u8 c[16];
+    } tweak, scratch;
+    unsigned int i;
+
+    if (len < 16)
+        return -1;
+
+    memcpy(tweak.c, iv, 16);
+
+    (*ctx->block2) (tweak.c, tweak.c, ctx->key2);
+
+    if (!enc && (len % 16))
+        len -= 16;
+
+    while (len >= 16) {
+#if defined(STRICT_ALIGNMENT)
+        memcpy(scratch.c, inp, 16);
+        scratch.u[0] ^= tweak.u[0];
+        scratch.u[1] ^= tweak.u[1];
+#else
+        scratch.u[0] = ((u64 *)inp)[0] ^ tweak.u[0];
+        scratch.u[1] = ((u64 *)inp)[1] ^ tweak.u[1];
+#endif
+        (*ctx->block1) (scratch.c, scratch.c, ctx->key1);
+#if defined(STRICT_ALIGNMENT)
+        scratch.u[0] ^= tweak.u[0];
+        scratch.u[1] ^= tweak.u[1];
+        memcpy(out, scratch.c, 16);
+#else
+        ((u64 *)out)[0] = scratch.u[0] ^= tweak.u[0];
+        ((u64 *)out)[1] = scratch.u[1] ^= tweak.u[1];
+#endif
+        inp += 16;
+        out += 16;
+        len -= 16;
+
+        if (len == 0)
+            return 0;
+
+        if (is_endian.little) {
+            unsigned int carry, res;
+
+            res = 0x87 & (((int)tweak.d[3]) >> 31);
+            carry = (unsigned int)(tweak.u[0] >> 63);
+            tweak.u[0] = (tweak.u[0] << 1) ^ res;
+            tweak.u[1] = (tweak.u[1] << 1) | carry;
+        } else {
+            size_t c;
+
+            for (c = 0, i = 0; i < 16; ++i) {
+                /*
+                 * + substitutes for |, because c is 1 bit
+                 */
+                c += ((size_t)tweak.c[i]) << 1;
+                tweak.c[i] = (u8)c;
+                c = c >> 8;
+            }
+            tweak.c[0] ^= (u8)(0x87 & (0 - c));
+        }
+    }
+    if (enc) {
+        for (i = 0; i < len; ++i) {
+            u8 c = inp[i];
+            out[i] = scratch.c[i];
+            scratch.c[i] = c;
+        }
+        scratch.u[0] ^= tweak.u[0];
+        scratch.u[1] ^= tweak.u[1];
+        (*ctx->block1) (scratch.c, scratch.c, ctx->key1);
+        scratch.u[0] ^= tweak.u[0];
+        scratch.u[1] ^= tweak.u[1];
+        memcpy(out - 16, scratch.c, 16);
+    } else {
+        union {
+            u64 u[2];
+            u8 c[16];
+        } tweak1;
+
+        if (is_endian.little) {
+            unsigned int carry, res;
+
+            res = 0x87 & (((int)tweak.d[3]) >> 31);
+            carry = (unsigned int)(tweak.u[0] >> 63);
+            tweak1.u[0] = (tweak.u[0] << 1) ^ res;
+            tweak1.u[1] = (tweak.u[1] << 1) | carry;
+        } else {
+            size_t c;
+
+            for (c = 0, i = 0; i < 16; ++i) {
+                /*
+                 * + substitutes for |, because c is 1 bit
+                 */
+                c += ((size_t)tweak.c[i]) << 1;
+                tweak1.c[i] = (u8)c;
+                c = c >> 8;
+            }
+            tweak1.c[0] ^= (u8)(0x87 & (0 - c));
+        }
+#if defined(STRICT_ALIGNMENT)
+        memcpy(scratch.c, inp, 16);
+        scratch.u[0] ^= tweak1.u[0];
+        scratch.u[1] ^= tweak1.u[1];
+#else
+        scratch.u[0] = ((u64 *)inp)[0] ^ tweak1.u[0];
+        scratch.u[1] = ((u64 *)inp)[1] ^ tweak1.u[1];
+#endif
+        (*ctx->block1) (scratch.c, scratch.c, ctx->key1);
+        scratch.u[0] ^= tweak1.u[0];
+        scratch.u[1] ^= tweak1.u[1];
+
+        for (i = 0; i < len; ++i) {
+            u8 c = inp[16 + i];
+            out[16 + i] = scratch.c[i];
+            scratch.c[i] = c;
+        }
+        scratch.u[0] ^= tweak.u[0];
+        scratch.u[1] ^= tweak.u[1];
+        (*ctx->block1) (scratch.c, scratch.c, ctx->key1);
+#if defined(STRICT_ALIGNMENT)
+        scratch.u[0] ^= tweak.u[0];
+        scratch.u[1] ^= tweak.u[1];
+        memcpy(out, scratch.c, 16);
+#else
+        ((u64 *)out)[0] = scratch.u[0] ^ tweak.u[0];
+        ((u64 *)out)[1] = scratch.u[1] ^ tweak.u[1];
+#endif
+    }
+
+    return 0;
+}
diff --git a/crypto/o_dir.c b/crypto/o_dir.c
new file mode 100644
index 000000000..89c8c5c56
--- /dev/null
+++ b/crypto/o_dir.c
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2004-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+
+/*
+ * The routines really come from the Levitte Programming, so to make life
+ * simple, let's just use the raw files and hack the symbols to fit our
+ * namespace.
+ */
+#define LP_DIR_CTX OPENSSL_DIR_CTX
+#define LP_dir_context_st OPENSSL_dir_context_st
+#define LP_find_file OPENSSL_DIR_read
+#define LP_find_file_end OPENSSL_DIR_end
+
+#include "internal/o_dir.h"
+
+#define LPDIR_H
+#if defined OPENSSL_SYS_UNIX || defined DJGPP
+# include "LPdir_unix.c"
+#elif defined OPENSSL_SYS_VMS
+# include "LPdir_vms.c"
+#elif defined OPENSSL_SYS_WIN32
+# include "LPdir_win32.c"
+#elif defined OPENSSL_SYS_WINCE
+# include "LPdir_wince.c"
+#else
+# include "LPdir_nyi.c"
+#endif
diff --git a/crypto/o_fips.c b/crypto/o_fips.c
new file mode 100644
index 000000000..bf6db65fe
--- /dev/null
+++ b/crypto/o_fips.c
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2011-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "internal/cryptlib.h"
+#ifdef OPENSSL_FIPS
+# include 
+#endif
+
+int FIPS_mode(void)
+{
+#ifdef OPENSSL_FIPS
+    return FIPS_module_mode();
+#else
+    return 0;
+#endif
+}
+
+int FIPS_mode_set(int r)
+{
+#ifdef OPENSSL_FIPS
+    return FIPS_module_mode_set(r);
+#else
+    if (r == 0)
+        return 1;
+    CRYPTOerr(CRYPTO_F_FIPS_MODE_SET, CRYPTO_R_FIPS_MODE_NOT_SUPPORTED);
+    return 0;
+#endif
+}
diff --git a/crypto/o_fopen.c b/crypto/o_fopen.c
new file mode 100644
index 000000000..a3a006574
--- /dev/null
+++ b/crypto/o_fopen.c
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "internal/cryptlib.h"
+
+#if !defined(OPENSSL_NO_STDIO)
+
+# include 
+
+FILE *openssl_fopen(const char *filename, const char *mode)
+{
+    FILE *file = NULL;
+# if defined(_WIN32) && defined(CP_UTF8)
+    int sz, len_0 = (int)strlen(filename) + 1;
+    DWORD flags;
+
+    /*
+     * Basically there are three cases to cover: a) filename is
+     * pure ASCII string; b) actual UTF-8 encoded string and
+     * c) locale-ized string, i.e. one containing 8-bit
+     * characters that are meaningful in current system locale.
+     * If filename is pure ASCII or real UTF-8 encoded string,
+     * MultiByteToWideChar succeeds and _wfopen works. If
+     * filename is locale-ized string, chances are that
+     * MultiByteToWideChar fails reporting
+     * ERROR_NO_UNICODE_TRANSLATION, in which case we fall
+     * back to fopen...
+     */
+    if ((sz = MultiByteToWideChar(CP_UTF8, (flags = MB_ERR_INVALID_CHARS),
+                                  filename, len_0, NULL, 0)) > 0 ||
+        (GetLastError() == ERROR_INVALID_FLAGS &&
+         (sz = MultiByteToWideChar(CP_UTF8, (flags = 0),
+                                   filename, len_0, NULL, 0)) > 0)
+        ) {
+        WCHAR wmode[8];
+        WCHAR *wfilename = _alloca(sz * sizeof(WCHAR));
+
+        if (MultiByteToWideChar(CP_UTF8, flags,
+                                filename, len_0, wfilename, sz) &&
+            MultiByteToWideChar(CP_UTF8, 0, mode, strlen(mode) + 1,
+                                wmode, OSSL_NELEM(wmode)) &&
+            (file = _wfopen(wfilename, wmode)) == NULL &&
+            (errno == ENOENT || errno == EBADF)
+            ) {
+            /*
+             * UTF-8 decode succeeded, but no file, filename
+             * could still have been locale-ized...
+             */
+            file = fopen(filename, mode);
+        }
+    } else if (GetLastError() == ERROR_NO_UNICODE_TRANSLATION) {
+        file = fopen(filename, mode);
+    }
+# elif defined(__DJGPP__)
+    {
+        char *newname = NULL;
+
+        if (!HAS_LFN_SUPPORT(filename)) {
+            char *iterator;
+            char lastchar;
+
+            newname = OPENSSL_malloc(strlen(filename) + 1);
+            if (newname == NULL)
+                return NULL;
+
+            for (iterator = newname, lastchar = '\0';
+                *filename; filename++, iterator++) {
+                if (lastchar == '/' && filename[0] == '.'
+                    && filename[1] != '.' && filename[1] != '/') {
+                    /* Leading dots are not permitted in plain DOS. */
+                    *iterator = '_';
+                } else {
+                    *iterator = *filename;
+                }
+                lastchar = *filename;
+            }
+            *iterator = '\0';
+            filename = newname;
+        }
+        file = fopen(filename, mode);
+
+        OPENSSL_free(newname);
+    }
+# else
+    file = fopen(filename, mode);
+# endif
+    return file;
+}
+
+#else
+
+void *openssl_fopen(const char *filename, const char *mode)
+{
+    return NULL;
+}
+
+#endif
diff --git a/crypto/o_init.c b/crypto/o_init.c
new file mode 100644
index 000000000..2e0c12609
--- /dev/null
+++ b/crypto/o_init.c
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2011-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#ifdef OPENSSL_FIPS
+# include 
+# include 
+#endif
+
+/*
+ * Perform any essential OpenSSL initialization operations. Currently only
+ * sets FIPS callbacks
+ */
+
+void OPENSSL_init(void)
+{
+    static int done = 0;
+    if (done)
+        return;
+    done = 1;
+#ifdef OPENSSL_FIPS
+    FIPS_set_locking_callbacks(CRYPTO_lock, CRYPTO_add_lock);
+    FIPS_set_error_callbacks(ERR_put_error, ERR_add_error_vdata);
+    FIPS_set_malloc_callbacks(CRYPTO_malloc, CRYPTO_free);
+    RAND_init_fips();
+#endif
+}
diff --git a/crypto/o_str.c b/crypto/o_str.c
new file mode 100644
index 000000000..beabec0dd
--- /dev/null
+++ b/crypto/o_str.c
@@ -0,0 +1,250 @@
+/*
+ * Copyright 2003-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include "internal/cryptlib.h"
+#include "internal/o_str.h"
+
+int OPENSSL_memcmp(const void *v1, const void *v2, size_t n)
+{
+    const unsigned char *c1 = v1, *c2 = v2;
+    int ret = 0;
+
+    while (n && (ret = *c1 - *c2) == 0)
+        n--, c1++, c2++;
+
+    return ret;
+}
+
+char *CRYPTO_strdup(const char *str, const char* file, int line)
+{
+    char *ret;
+    size_t size;
+
+    if (str == NULL)
+        return NULL;
+    size = strlen(str) + 1;
+    ret = CRYPTO_malloc(size, file, line);
+    if (ret != NULL)
+        memcpy(ret, str, size);
+    return ret;
+}
+
+char *CRYPTO_strndup(const char *str, size_t s, const char* file, int line)
+{
+    size_t maxlen;
+    char *ret;
+
+    if (str == NULL)
+        return NULL;
+
+    maxlen = OPENSSL_strnlen(str, s);
+
+    ret = CRYPTO_malloc(maxlen + 1, file, line);
+    if (ret) {
+        memcpy(ret, str, maxlen);
+        ret[maxlen] = '\0';
+    }
+    return ret;
+}
+
+void *CRYPTO_memdup(const void *data, size_t siz, const char* file, int line)
+{
+    void *ret;
+
+    if (data == NULL || siz >= INT_MAX)
+        return NULL;
+
+    ret = CRYPTO_malloc(siz, file, line);
+    if (ret == NULL) {
+        CRYPTOerr(CRYPTO_F_CRYPTO_MEMDUP, ERR_R_MALLOC_FAILURE);
+        return NULL;
+    }
+    return memcpy(ret, data, siz);
+}
+
+size_t OPENSSL_strnlen(const char *str, size_t maxlen)
+{
+    const char *p;
+
+    for (p = str; maxlen-- != 0 && *p != '\0'; ++p) ;
+
+    return p - str;
+}
+
+size_t OPENSSL_strlcpy(char *dst, const char *src, size_t size)
+{
+    size_t l = 0;
+    for (; size > 1 && *src; size--) {
+        *dst++ = *src++;
+        l++;
+    }
+    if (size)
+        *dst = '\0';
+    return l + strlen(src);
+}
+
+size_t OPENSSL_strlcat(char *dst, const char *src, size_t size)
+{
+    size_t l = 0;
+    for (; size > 0 && *dst; size--, dst++)
+        l++;
+    return l + OPENSSL_strlcpy(dst, src, size);
+}
+
+int OPENSSL_hexchar2int(unsigned char c)
+{
+#ifdef CHARSET_EBCDIC
+    c = os_toebcdic[c];
+#endif
+
+    switch (c) {
+    case '0':
+        return 0;
+    case '1':
+        return 1;
+    case '2':
+        return 2;
+    case '3':
+        return 3;
+    case '4':
+          return 4;
+    case '5':
+          return 5;
+    case '6':
+          return 6;
+    case '7':
+          return 7;
+    case '8':
+          return 8;
+    case '9':
+          return 9;
+    case 'a': case 'A':
+          return 0x0A;
+    case 'b': case 'B':
+          return 0x0B;
+    case 'c': case 'C':
+          return 0x0C;
+    case 'd': case 'D':
+          return 0x0D;
+    case 'e': case 'E':
+          return 0x0E;
+    case 'f': case 'F':
+          return 0x0F;
+    }
+    return -1;
+}
+
+/*
+ * Give a string of hex digits convert to a buffer
+ */
+unsigned char *OPENSSL_hexstr2buf(const char *str, long *len)
+{
+    unsigned char *hexbuf, *q;
+    unsigned char ch, cl;
+    int chi, cli;
+    const unsigned char *p;
+    size_t s;
+
+    s = strlen(str);
+    if ((hexbuf = OPENSSL_malloc(s >> 1)) == NULL) {
+        CRYPTOerr(CRYPTO_F_OPENSSL_HEXSTR2BUF, ERR_R_MALLOC_FAILURE);
+        return NULL;
+    }
+    for (p = (const unsigned char *)str, q = hexbuf; *p; ) {
+        ch = *p++;
+        if (ch == ':')
+            continue;
+        cl = *p++;
+        if (!cl) {
+            CRYPTOerr(CRYPTO_F_OPENSSL_HEXSTR2BUF,
+                      CRYPTO_R_ODD_NUMBER_OF_DIGITS);
+            OPENSSL_free(hexbuf);
+            return NULL;
+        }
+        cli = OPENSSL_hexchar2int(cl);
+        chi = OPENSSL_hexchar2int(ch);
+        if (cli < 0 || chi < 0) {
+            OPENSSL_free(hexbuf);
+            CRYPTOerr(CRYPTO_F_OPENSSL_HEXSTR2BUF, CRYPTO_R_ILLEGAL_HEX_DIGIT);
+            return NULL;
+        }
+        *q++ = (unsigned char)((chi << 4) | cli);
+    }
+
+    if (len)
+        *len = q - hexbuf;
+    return hexbuf;
+}
+
+/*
+ * Given a buffer of length 'len' return a OPENSSL_malloc'ed string with its
+ * hex representation @@@ (Contents of buffer are always kept in ASCII, also
+ * on EBCDIC machines)
+ */
+char *OPENSSL_buf2hexstr(const unsigned char *buffer, long len)
+{
+    const static char hexdig[] = "0123456789ABCDEF";
+    char *tmp, *q;
+    const unsigned char *p;
+    int i;
+
+    if (len == 0)
+    {
+        return OPENSSL_zalloc(1);
+    }
+
+    if ((tmp = OPENSSL_malloc(len * 3)) == NULL) {
+        CRYPTOerr(CRYPTO_F_OPENSSL_BUF2HEXSTR, ERR_R_MALLOC_FAILURE);
+        return NULL;
+    }
+    q = tmp;
+    for (i = 0, p = buffer; i < len; i++, p++) {
+        *q++ = hexdig[(*p >> 4) & 0xf];
+        *q++ = hexdig[*p & 0xf];
+        *q++ = ':';
+    }
+    q[-1] = 0;
+#ifdef CHARSET_EBCDIC
+    ebcdic2ascii(tmp, tmp, q - tmp - 1);
+#endif
+
+    return tmp;
+}
+
+int openssl_strerror_r(int errnum, char *buf, size_t buflen)
+{
+#if defined(_MSC_VER) && _MSC_VER>=1400
+    return !strerror_s(buf, buflen, errnum);
+#elif defined(_GNU_SOURCE)
+    return strerror_r(errnum, buf, buflen) != NULL;
+#elif (_POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600)
+    /*
+     * We can use "real" strerror_r. The OpenSSL version differs in that it
+     * gives 1 on success and 0 on failure for consistency with other OpenSSL
+     * functions. Real strerror_r does it the other way around
+     */
+    return !strerror_r(errnum, buf, buflen);
+#else
+    char *err;
+    /* Fall back to non-thread safe strerror()...its all we can do */
+    if (buflen < 2)
+        return 0;
+    err = strerror(errnum);
+    /* Can this ever happen? */
+    if (err == NULL)
+        return 0;
+    strncpy(buf, err, buflen - 1);
+    buf[buflen - 1] = '\0';
+    return 1;
+#endif
+}
diff --git a/crypto/o_time.c b/crypto/o_time.c
new file mode 100755
index 000000000..e785525b3
--- /dev/null
+++ b/crypto/o_time.c
@@ -0,0 +1,363 @@
+/*
+ * Copyright 2001-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include 
+
+#ifdef OPENSSL_SYS_VMS
+# if __CRTL_VER >= 70000000 && \
+     (defined _POSIX_C_SOURCE || !defined _ANSI_C_SOURCE)
+#  define VMS_GMTIME_OK
+# endif
+# ifndef VMS_GMTIME_OK
+#  include 
+#  include 
+#  include 
+#  include 
+#  include 
+#  include 
+# endif                         /* ndef VMS_GMTIME_OK */
+
+
+/*
+ * Needed to pick up the correct definitions and declarations in some of the
+ * DEC C Header Files (*.H).
+ */
+# define __NEW_STARLET 1
+
+# if (defined(__alpha) || defined(__ia64))
+#  include 
+# else
+
+/* VAX */
+typedef struct _ile3 {          /* Copied from ILEDEF.H for Alpha   */
+#  pragma __nomember_alignment
+    unsigned short int ile3$w_length;        /* Length of buffer in bytes */
+    unsigned short int ile3$w_code;          /* Item code value */
+    void *ile3$ps_bufaddr;                   /* Buffer address */
+    unsigned short int *ile3$ps_retlen_addr; /* Address of word for returned length */
+} ILE3;
+# endif   /* alpha || ia64    */
+#endif    /* OPENSSL_SYS_VMS  */
+
+struct tm *OPENSSL_gmtime(const time_t *timer, struct tm *result)
+{
+    struct tm *ts = NULL;
+
+#if defined(OPENSSL_THREADS) && !defined(OPENSSL_SYS_WIN32) && (!defined(OPENSSL_SYS_VMS) || defined(gmtime_r)) && !defined(OPENSSL_SYS_MACOSX)
+    /*
+     * should return &data, but doesn't on some systems, so we don't even
+     * look at the return value
+     */
+    gmtime_r(timer, result);
+    ts = result;
+#elif !defined(OPENSSL_SYS_VMS) || defined(VMS_GMTIME_OK)
+    ts = gmtime(timer);
+    if (ts == NULL)
+        return NULL;
+
+    memcpy(result, ts, sizeof(struct tm));
+    ts = result;
+#endif
+#if defined( OPENSSL_SYS_VMS) && !defined( VMS_GMTIME_OK)
+    if (ts == NULL) {
+        static $DESCRIPTOR(tabnam, "LNM$DCL_LOGICAL");
+        static $DESCRIPTOR(lognam, "SYS$TIMEZONE_DIFFERENTIAL");
+        char logvalue[256];
+        unsigned int reslen = 0;
+# if __INITIAL_POINTER_SIZE == 64
+        ILEB_64 itemlist[2], *pitem;
+# else
+        ILE3 itemlist[2], *pitem;
+# endif
+        int status;
+        time_t t;
+
+
+        /*
+         * Setup an itemlist for the call to $TRNLNM - Translate Logical Name.
+         */
+        pitem = itemlist;
+
+# if __INITIAL_POINTER_SIZE == 64
+        pitem->ileb_64$w_mbo = 1;
+        pitem->ileb_64$w_code = LNM$_STRING;
+        pitem->ileb_64$l_mbmo = -1;
+        pitem->ileb_64$q_length = sizeof (logvalue);
+        pitem->ileb_64$pq_bufaddr = logvalue;
+        pitem->ileb_64$pq_retlen_addr = (unsigned __int64 *) &reslen;
+        pitem++;
+        /* Last item of the item list is null terminated */
+        pitem->ileb_64$q_length = pitem->ileb_64$w_code = 0;
+# else
+        pitem->ile3$w_length = sizeof (logvalue);
+        pitem->ile3$w_code = LNM$_STRING;
+        pitem->ile3$ps_bufaddr = logvalue;
+        pitem->ile3$ps_retlen_addr = (unsigned short int *) &reslen;
+        pitem++;
+        /* Last item of the item list is null terminated */
+        pitem->ile3$w_length = pitem->ile3$w_code = 0;
+# endif
+
+
+        /* Get the value for SYS$TIMEZONE_DIFFERENTIAL */
+        status = sys$trnlnm(0, &tabnam, &lognam, 0, itemlist);
+        if (!(status & 1))
+            return NULL;
+        logvalue[reslen] = '\0';
+
+        t = *timer;
+
+        /* The following is extracted from the DEC C header time.h */
+        /*
+         **  Beginning in OpenVMS Version 7.0 mktime, time, ctime, strftime
+         **  have two implementations.  One implementation is provided
+         **  for compatibility and deals with time in terms of local time,
+         **  the other __utc_* deals with time in terms of UTC.
+         */
+        /*
+         * We use the same conditions as in said time.h to check if we should
+         * assume that t contains local time (and should therefore be
+         * adjusted) or UTC (and should therefore be left untouched).
+         */
+# if __CRTL_VER < 70000000 || defined _VMS_V6_SOURCE
+        /* Get the numerical value of the equivalence string */
+        status = atoi(logvalue);
+
+        /* and use it to move time to GMT */
+        t -= status;
+# endif
+
+        /* then convert the result to the time structure */
+
+        /*
+         * Since there was no gmtime_r() to do this stuff for us, we have to
+         * do it the hard way.
+         */
+        {
+            /*-
+             * The VMS epoch is the astronomical Smithsonian date,
+               if I remember correctly, which is November 17, 1858.
+               Furthermore, time is measure in tenths of microseconds
+               and stored in quadwords (64 bit integers).  unix_epoch
+               below is January 1st 1970 expressed as a VMS time.  The
+               following code was used to get this number:
+
+               #include 
+               #include 
+               #include 
+               #include 
+
+               main()
+               {
+                 unsigned long systime[2];
+                 unsigned short epoch_values[7] =
+                   { 1970, 1, 1, 0, 0, 0, 0 };
+
+                 lib$cvt_vectim(epoch_values, systime);
+
+                 printf("%u %u", systime[0], systime[1]);
+               }
+            */
+            unsigned long unix_epoch[2] = { 1273708544, 8164711 };
+            unsigned long deltatime[2];
+            unsigned long systime[2];
+            struct vms_vectime {
+                short year, month, day, hour, minute, second, centi_second;
+            } time_values;
+            long operation;
+
+            /*
+             * Turn the number of seconds since January 1st 1970 to an
+             * internal delta time. Note that lib$cvt_to_internal_time() will
+             * assume that t is signed, and will therefore break on 32-bit
+             * systems some time in 2038.
+             */
+            operation = LIB$K_DELTA_SECONDS;
+            status = lib$cvt_to_internal_time(&operation, &t, deltatime);
+
+            /*
+             * Add the delta time with the Unix epoch and we have the current
+             * UTC time in internal format
+             */
+            status = lib$add_times(unix_epoch, deltatime, systime);
+
+            /* Turn the internal time into a time vector */
+            status = sys$numtim(&time_values, systime);
+
+            /* Fill in the struct tm with the result */
+            result->tm_sec = time_values.second;
+            result->tm_min = time_values.minute;
+            result->tm_hour = time_values.hour;
+            result->tm_mday = time_values.day;
+            result->tm_mon = time_values.month - 1;
+            result->tm_year = time_values.year - 1900;
+
+            operation = LIB$K_DAY_OF_WEEK;
+            status = lib$cvt_from_internal_time(&operation,
+                                                &result->tm_wday, systime);
+            result->tm_wday %= 7;
+
+            operation = LIB$K_DAY_OF_YEAR;
+            status = lib$cvt_from_internal_time(&operation,
+                                                &result->tm_yday, systime);
+            result->tm_yday--;
+
+            result->tm_isdst = 0; /* There's no way to know... */
+
+            ts = result;
+        }
+    }
+#endif
+    return ts;
+}
+
+/*
+ * Take a tm structure and add an offset to it. This avoids any OS issues
+ * with restricted date types and overflows which cause the year 2038
+ * problem.
+ */
+
+#define SECS_PER_DAY (24 * 60 * 60)
+
+static long date_to_julian(int y, int m, int d);
+static void julian_to_date(long jd, int *y, int *m, int *d);
+static int julian_adj(const struct tm *tm, int off_day, long offset_sec,
+                      long *pday, int *psec);
+
+int OPENSSL_gmtime_adj(struct tm *tm, int off_day, long offset_sec)
+{
+    int time_sec, time_year, time_month, time_day;
+    long time_jd;
+
+    /* Convert time and offset into Julian day and seconds */
+    if (!julian_adj(tm, off_day, offset_sec, &time_jd, &time_sec))
+        return 0;
+
+    /* Convert Julian day back to date */
+
+    julian_to_date(time_jd, &time_year, &time_month, &time_day);
+
+    if (time_year < 1900 || time_year > 9999)
+        return 0;
+
+    /* Update tm structure */
+
+    tm->tm_year = time_year - 1900;
+    tm->tm_mon = time_month - 1;
+    tm->tm_mday = time_day;
+
+    tm->tm_hour = time_sec / 3600;
+    tm->tm_min = (time_sec / 60) % 60;
+    tm->tm_sec = time_sec % 60;
+
+    return 1;
+
+}
+
+int OPENSSL_gmtime_diff(int *pday, int *psec,
+                        const struct tm *from, const struct tm *to)
+{
+    int from_sec, to_sec, diff_sec;
+    long from_jd, to_jd, diff_day;
+    if (!julian_adj(from, 0, 0, &from_jd, &from_sec))
+        return 0;
+    if (!julian_adj(to, 0, 0, &to_jd, &to_sec))
+        return 0;
+    diff_day = to_jd - from_jd;
+    diff_sec = to_sec - from_sec;
+    /* Adjust differences so both positive or both negative */
+    if (diff_day > 0 && diff_sec < 0) {
+        diff_day--;
+        diff_sec += SECS_PER_DAY;
+    }
+    if (diff_day < 0 && diff_sec > 0) {
+        diff_day++;
+        diff_sec -= SECS_PER_DAY;
+    }
+
+    if (pday)
+        *pday = (int)diff_day;
+    if (psec)
+        *psec = diff_sec;
+
+    return 1;
+
+}
+
+/* Convert tm structure and offset into julian day and seconds */
+static int julian_adj(const struct tm *tm, int off_day, long offset_sec,
+                      long *pday, int *psec)
+{
+    int offset_hms, offset_day;
+    long time_jd;
+    int time_year, time_month, time_day;
+    /* split offset into days and day seconds */
+    offset_day = offset_sec / SECS_PER_DAY;
+    /* Avoid sign issues with % operator */
+    offset_hms = offset_sec - (offset_day * SECS_PER_DAY);
+    offset_day += off_day;
+    /* Add current time seconds to offset */
+    offset_hms += tm->tm_hour * 3600 + tm->tm_min * 60 + tm->tm_sec;
+    /* Adjust day seconds if overflow */
+    if (offset_hms >= SECS_PER_DAY) {
+        offset_day++;
+        offset_hms -= SECS_PER_DAY;
+    } else if (offset_hms < 0) {
+        offset_day--;
+        offset_hms += SECS_PER_DAY;
+    }
+
+    /*
+     * Convert date of time structure into a Julian day number.
+     */
+
+    time_year = tm->tm_year + 1900;
+    time_month = tm->tm_mon + 1;
+    time_day = tm->tm_mday;
+
+    time_jd = date_to_julian(time_year, time_month, time_day);
+
+    /* Work out Julian day of new date */
+    time_jd += offset_day;
+
+    if (time_jd < 0)
+        return 0;
+
+    *pday = time_jd;
+    *psec = offset_hms;
+    return 1;
+}
+
+/*
+ * Convert date to and from julian day Uses Fliegel & Van Flandern algorithm
+ */
+static long date_to_julian(int y, int m, int d)
+{
+    return (1461 * (y + 4800 + (m - 14) / 12)) / 4 +
+        (367 * (m - 2 - 12 * ((m - 14) / 12))) / 12 -
+        (3 * ((y + 4900 + (m - 14) / 12) / 100)) / 4 + d - 32075;
+}
+
+static void julian_to_date(long jd, int *y, int *m, int *d)
+{
+    long L = jd + 68569;
+    long n = (4 * L) / 146097;
+    long i, j;
+
+    L = L - (146097 * n + 3) / 4;
+    i = (4000 * (L + 1)) / 1461001;
+    L = L - (1461 * i) / 4 + 31;
+    j = (80 * L) / 2447;
+    *d = L - (2447 * j) / 80;
+    L = j / 11;
+    *m = j + 2 - (12 * L);
+    *y = 100 * (n - 49) + i + L;
+}
diff --git a/crypto/objects/README b/crypto/objects/README
new file mode 100644
index 000000000..cb1d216ce
--- /dev/null
+++ b/crypto/objects/README
@@ -0,0 +1,44 @@
+objects.txt syntax
+------------------
+
+To cover all the naming hacks that were previously in objects.h needed some
+kind of hacks in objects.txt.
+
+The basic syntax for adding an object is as follows:
+
+	1 2 3 4		: shortName	: Long Name
+
+		If Long Name contains only word characters and hyphen-minus
+		(0x2D) or full stop (0x2E) then Long Name is used as basis
+		for the base name in C. Otherwise, the shortName is used.
+
+		The base name (let's call it 'base') will then be used to
+		create the C macros SN_base, LN_base, NID_base and OBJ_base.
+
+		Note that if the base name contains spaces, dashes or periods,
+		those will be converte to underscore.
+
+Then there are some extra commands:
+
+	!Alias foo 1 2 3 4
+
+		This just makes a name foo for an OID.  The C macro
+		OBJ_foo will be created as a result.
+
+	!Cname foo
+
+		This makes sure that the name foo will be used as base name
+		in C.
+
+	!module foo
+	1 2 3 4		: shortName	: Long Name
+	!global
+
+		The !module command was meant to define a kind of modularity.
+		What it does is to make sure the module name is prepended
+		to the base name.  !global turns this off.  This construction
+		is not recursive.
+
+Lines starting with # are treated as comments, as well as any line starting
+with ! and not matching the commands above.
+
diff --git a/crypto/objects/build.info b/crypto/objects/build.info
new file mode 100644
index 000000000..38e290756
--- /dev/null
+++ b/crypto/objects/build.info
@@ -0,0 +1,3 @@
+LIBS=../../libcrypto
+SOURCE[../../libcrypto]=\
+        o_names.c obj_dat.c obj_lib.c obj_err.c obj_xref.c
diff --git a/crypto/objects/o_names.c b/crypto/objects/o_names.c
new file mode 100644
index 000000000..ed98df8c2
--- /dev/null
+++ b/crypto/objects/o_names.c
@@ -0,0 +1,370 @@
+/*
+ * Copyright 1998-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include 
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include "obj_lcl.h"
+
+/*
+ * We define this wrapper for two reasons. Firstly, later versions of
+ * DEC C add linkage information to certain functions, which makes it
+ * tricky to use them as values to regular function pointers.
+ * Secondly, in the EDK2 build environment, the strcmp function is
+ * actually an external function (AsciiStrCmp) with the Microsoft ABI,
+ * so we can't transparently assign function pointers to it.
+ * Arguably the latter is a stupidity of the UEFI environment, but
+ * since the wrapper solves the DEC C issue too, let's just use the
+ * same solution.
+ */
+#if defined(OPENSSL_SYS_VMS_DECC) || defined(OPENSSL_SYS_UEFI)
+static int obj_strcmp(const char *a, const char *b)
+{
+    return strcmp(a, b);
+}
+#else
+#define obj_strcmp strcmp
+#endif
+
+/*
+ * I use the ex_data stuff to manage the identifiers for the obj_name_types
+ * that applications may define.  I only really use the free function field.
+ */
+static LHASH_OF(OBJ_NAME) *names_lh = NULL;
+static int names_type_num = OBJ_NAME_TYPE_NUM;
+
+struct name_funcs_st {
+    unsigned long (*hash_func) (const char *name);
+    int (*cmp_func) (const char *a, const char *b);
+    void (*free_func) (const char *, int, const char *);
+};
+
+static STACK_OF(NAME_FUNCS) *name_funcs_stack;
+
+/*
+ * The LHASH callbacks now use the raw "void *" prototypes and do
+ * per-variable casting in the functions. This prevents function pointer
+ * casting without the need for macro-generated wrapper functions.
+ */
+
+static unsigned long obj_name_hash(const OBJ_NAME *a);
+static int obj_name_cmp(const OBJ_NAME *a, const OBJ_NAME *b);
+
+int OBJ_NAME_init(void)
+{
+    if (names_lh != NULL)
+        return (1);
+    CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_DISABLE);
+    names_lh = lh_OBJ_NAME_new(obj_name_hash, obj_name_cmp);
+    CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_ENABLE);
+    return (names_lh != NULL);
+}
+
+int OBJ_NAME_new_index(unsigned long (*hash_func) (const char *),
+                       int (*cmp_func) (const char *, const char *),
+                       void (*free_func) (const char *, int, const char *))
+{
+    int ret, i, push;
+    NAME_FUNCS *name_funcs;
+
+    if (name_funcs_stack == NULL) {
+        CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_DISABLE);
+        name_funcs_stack = sk_NAME_FUNCS_new_null();
+        CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_ENABLE);
+    }
+    if (name_funcs_stack == NULL) {
+        /* ERROR */
+        return (0);
+    }
+    ret = names_type_num;
+    names_type_num++;
+    for (i = sk_NAME_FUNCS_num(name_funcs_stack); i < names_type_num; i++) {
+        CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_DISABLE);
+        name_funcs = OPENSSL_zalloc(sizeof(*name_funcs));
+        CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_ENABLE);
+        if (name_funcs == NULL) {
+            OBJerr(OBJ_F_OBJ_NAME_NEW_INDEX, ERR_R_MALLOC_FAILURE);
+            return (0);
+        }
+        name_funcs->hash_func = OPENSSL_LH_strhash;
+        name_funcs->cmp_func = obj_strcmp;
+        CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_DISABLE);
+
+        push = sk_NAME_FUNCS_push(name_funcs_stack, name_funcs);
+        CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_ENABLE);
+
+        if (!push) {
+            OBJerr(OBJ_F_OBJ_NAME_NEW_INDEX, ERR_R_MALLOC_FAILURE);
+            OPENSSL_free(name_funcs);
+            return 0;
+        }
+    }
+    name_funcs = sk_NAME_FUNCS_value(name_funcs_stack, ret);
+    if (hash_func != NULL)
+        name_funcs->hash_func = hash_func;
+    if (cmp_func != NULL)
+        name_funcs->cmp_func = cmp_func;
+    if (free_func != NULL)
+        name_funcs->free_func = free_func;
+    return (ret);
+}
+
+static int obj_name_cmp(const OBJ_NAME *a, const OBJ_NAME *b)
+{
+    int ret;
+
+    ret = a->type - b->type;
+    if (ret == 0) {
+        if ((name_funcs_stack != NULL)
+            && (sk_NAME_FUNCS_num(name_funcs_stack) > a->type)) {
+            ret = sk_NAME_FUNCS_value(name_funcs_stack,
+                                      a->type)->cmp_func(a->name, b->name);
+        } else
+            ret = strcmp(a->name, b->name);
+    }
+    return (ret);
+}
+
+static unsigned long obj_name_hash(const OBJ_NAME *a)
+{
+    unsigned long ret;
+
+    if ((name_funcs_stack != NULL)
+        && (sk_NAME_FUNCS_num(name_funcs_stack) > a->type)) {
+        ret =
+            sk_NAME_FUNCS_value(name_funcs_stack,
+                                a->type)->hash_func(a->name);
+    } else {
+        ret = OPENSSL_LH_strhash(a->name);
+    }
+    ret ^= a->type;
+    return (ret);
+}
+
+const char *OBJ_NAME_get(const char *name, int type)
+{
+    OBJ_NAME on, *ret;
+    int num = 0, alias;
+
+    if (name == NULL)
+        return (NULL);
+    if ((names_lh == NULL) && !OBJ_NAME_init())
+        return (NULL);
+
+    alias = type & OBJ_NAME_ALIAS;
+    type &= ~OBJ_NAME_ALIAS;
+
+    on.name = name;
+    on.type = type;
+
+    for (;;) {
+        ret = lh_OBJ_NAME_retrieve(names_lh, &on);
+        if (ret == NULL)
+            return (NULL);
+        if ((ret->alias) && !alias) {
+            if (++num > 10)
+                return (NULL);
+            on.name = ret->data;
+        } else {
+            return (ret->data);
+        }
+    }
+}
+
+int OBJ_NAME_add(const char *name, int type, const char *data)
+{
+    OBJ_NAME *onp, *ret;
+    int alias;
+
+    if ((names_lh == NULL) && !OBJ_NAME_init())
+        return (0);
+
+    alias = type & OBJ_NAME_ALIAS;
+    type &= ~OBJ_NAME_ALIAS;
+
+    onp = OPENSSL_malloc(sizeof(*onp));
+    if (onp == NULL) {
+        /* ERROR */
+        return 0;
+    }
+
+    onp->name = name;
+    onp->alias = alias;
+    onp->type = type;
+    onp->data = data;
+
+    ret = lh_OBJ_NAME_insert(names_lh, onp);
+    if (ret != NULL) {
+        /* free things */
+        if ((name_funcs_stack != NULL)
+            && (sk_NAME_FUNCS_num(name_funcs_stack) > ret->type)) {
+            /*
+             * XXX: I'm not sure I understand why the free function should
+             * get three arguments... -- Richard Levitte
+             */
+            sk_NAME_FUNCS_value(name_funcs_stack,
+                                ret->type)->free_func(ret->name, ret->type,
+                                                      ret->data);
+        }
+        OPENSSL_free(ret);
+    } else {
+        if (lh_OBJ_NAME_error(names_lh)) {
+            /* ERROR */
+            OPENSSL_free(onp);
+            return 0;
+        }
+    }
+    return 1;
+}
+
+int OBJ_NAME_remove(const char *name, int type)
+{
+    OBJ_NAME on, *ret;
+
+    if (names_lh == NULL)
+        return (0);
+
+    type &= ~OBJ_NAME_ALIAS;
+    on.name = name;
+    on.type = type;
+    ret = lh_OBJ_NAME_delete(names_lh, &on);
+    if (ret != NULL) {
+        /* free things */
+        if ((name_funcs_stack != NULL)
+            && (sk_NAME_FUNCS_num(name_funcs_stack) > ret->type)) {
+            /*
+             * XXX: I'm not sure I understand why the free function should
+             * get three arguments... -- Richard Levitte
+             */
+            sk_NAME_FUNCS_value(name_funcs_stack,
+                                ret->type)->free_func(ret->name, ret->type,
+                                                      ret->data);
+        }
+        OPENSSL_free(ret);
+        return (1);
+    } else
+        return (0);
+}
+
+typedef struct {
+    int type;
+    void (*fn) (const OBJ_NAME *, void *arg);
+    void *arg;
+} OBJ_DOALL;
+
+static void do_all_fn(const OBJ_NAME *name, OBJ_DOALL *d)
+{
+    if (name->type == d->type)
+        d->fn(name, d->arg);
+}
+
+IMPLEMENT_LHASH_DOALL_ARG_CONST(OBJ_NAME, OBJ_DOALL);
+
+void OBJ_NAME_do_all(int type, void (*fn) (const OBJ_NAME *, void *arg),
+                     void *arg)
+{
+    OBJ_DOALL d;
+
+    d.type = type;
+    d.fn = fn;
+    d.arg = arg;
+
+    lh_OBJ_NAME_doall_OBJ_DOALL(names_lh, do_all_fn, &d);
+}
+
+struct doall_sorted {
+    int type;
+    int n;
+    const OBJ_NAME **names;
+};
+
+static void do_all_sorted_fn(const OBJ_NAME *name, void *d_)
+{
+    struct doall_sorted *d = d_;
+
+    if (name->type != d->type)
+        return;
+
+    d->names[d->n++] = name;
+}
+
+static int do_all_sorted_cmp(const void *n1_, const void *n2_)
+{
+    const OBJ_NAME *const *n1 = n1_;
+    const OBJ_NAME *const *n2 = n2_;
+
+    return strcmp((*n1)->name, (*n2)->name);
+}
+
+void OBJ_NAME_do_all_sorted(int type,
+                            void (*fn) (const OBJ_NAME *, void *arg),
+                            void *arg)
+{
+    struct doall_sorted d;
+    int n;
+
+    d.type = type;
+    d.names =
+        OPENSSL_malloc(sizeof(*d.names) * lh_OBJ_NAME_num_items(names_lh));
+    /* Really should return an error if !d.names...but its a void function! */
+    if (d.names != NULL) {
+        d.n = 0;
+        OBJ_NAME_do_all(type, do_all_sorted_fn, &d);
+
+        qsort((void *)d.names, d.n, sizeof(*d.names), do_all_sorted_cmp);
+
+        for (n = 0; n < d.n; ++n)
+            fn(d.names[n], arg);
+
+        OPENSSL_free((void *)d.names);
+    }
+}
+
+static int free_type;
+
+static void names_lh_free_doall(OBJ_NAME *onp)
+{
+    if (onp == NULL)
+        return;
+
+    if (free_type < 0 || free_type == onp->type)
+        OBJ_NAME_remove(onp->name, onp->type);
+}
+
+static void name_funcs_free(NAME_FUNCS *ptr)
+{
+    OPENSSL_free(ptr);
+}
+
+void OBJ_NAME_cleanup(int type)
+{
+    unsigned long down_load;
+
+    if (names_lh == NULL)
+        return;
+
+    free_type = type;
+    down_load = lh_OBJ_NAME_get_down_load(names_lh);
+    lh_OBJ_NAME_set_down_load(names_lh, 0);
+
+    lh_OBJ_NAME_doall(names_lh, names_lh_free_doall);
+    if (type < 0) {
+        lh_OBJ_NAME_free(names_lh);
+        sk_NAME_FUNCS_pop_free(name_funcs_stack, name_funcs_free);
+        names_lh = NULL;
+        name_funcs_stack = NULL;
+    } else
+        lh_OBJ_NAME_set_down_load(names_lh, down_load);
+}
diff --git a/crypto/objects/obj_dat.c b/crypto/objects/obj_dat.c
new file mode 100644
index 000000000..259851bc0
--- /dev/null
+++ b/crypto/objects/obj_dat.c
@@ -0,0 +1,728 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+#include "internal/objects.h"
+#include 
+#include "internal/asn1_int.h"
+#include "obj_lcl.h"
+
+/* obj_dat.h is generated from objects.h by obj_dat.pl */
+#include "obj_dat.h"
+
+DECLARE_OBJ_BSEARCH_CMP_FN(const ASN1_OBJECT *, unsigned int, sn);
+DECLARE_OBJ_BSEARCH_CMP_FN(const ASN1_OBJECT *, unsigned int, ln);
+DECLARE_OBJ_BSEARCH_CMP_FN(const ASN1_OBJECT *, unsigned int, obj);
+
+#define ADDED_DATA      0
+#define ADDED_SNAME     1
+#define ADDED_LNAME     2
+#define ADDED_NID       3
+
+struct added_obj_st {
+    int type;
+    ASN1_OBJECT *obj;
+};
+
+static int new_nid = NUM_NID;
+static LHASH_OF(ADDED_OBJ) *added = NULL;
+
+static int sn_cmp(const ASN1_OBJECT *const *a, const unsigned int *b)
+{
+    return (strcmp((*a)->sn, nid_objs[*b].sn));
+}
+
+IMPLEMENT_OBJ_BSEARCH_CMP_FN(const ASN1_OBJECT *, unsigned int, sn);
+
+static int ln_cmp(const ASN1_OBJECT *const *a, const unsigned int *b)
+{
+    return (strcmp((*a)->ln, nid_objs[*b].ln));
+}
+
+IMPLEMENT_OBJ_BSEARCH_CMP_FN(const ASN1_OBJECT *, unsigned int, ln);
+
+static unsigned long added_obj_hash(const ADDED_OBJ *ca)
+{
+    const ASN1_OBJECT *a;
+    int i;
+    unsigned long ret = 0;
+    unsigned char *p;
+
+    a = ca->obj;
+    switch (ca->type) {
+    case ADDED_DATA:
+        ret = a->length << 20L;
+        p = (unsigned char *)a->data;
+        for (i = 0; i < a->length; i++)
+            ret ^= p[i] << ((i * 3) % 24);
+        break;
+    case ADDED_SNAME:
+        ret = OPENSSL_LH_strhash(a->sn);
+        break;
+    case ADDED_LNAME:
+        ret = OPENSSL_LH_strhash(a->ln);
+        break;
+    case ADDED_NID:
+        ret = a->nid;
+        break;
+    default:
+        /* abort(); */
+        return 0;
+    }
+    ret &= 0x3fffffffL;
+    ret |= ((unsigned long)ca->type) << 30L;
+    return (ret);
+}
+
+static int added_obj_cmp(const ADDED_OBJ *ca, const ADDED_OBJ *cb)
+{
+    ASN1_OBJECT *a, *b;
+    int i;
+
+    i = ca->type - cb->type;
+    if (i)
+        return (i);
+    a = ca->obj;
+    b = cb->obj;
+    switch (ca->type) {
+    case ADDED_DATA:
+        i = (a->length - b->length);
+        if (i)
+            return (i);
+        return (memcmp(a->data, b->data, (size_t)a->length));
+    case ADDED_SNAME:
+        if (a->sn == NULL)
+            return (-1);
+        else if (b->sn == NULL)
+            return (1);
+        else
+            return (strcmp(a->sn, b->sn));
+    case ADDED_LNAME:
+        if (a->ln == NULL)
+            return (-1);
+        else if (b->ln == NULL)
+            return (1);
+        else
+            return (strcmp(a->ln, b->ln));
+    case ADDED_NID:
+        return (a->nid - b->nid);
+    default:
+        /* abort(); */
+        return 0;
+    }
+}
+
+static int init_added(void)
+{
+    if (added != NULL)
+        return (1);
+    added = lh_ADDED_OBJ_new(added_obj_hash, added_obj_cmp);
+    return (added != NULL);
+}
+
+static void cleanup1_doall(ADDED_OBJ *a)
+{
+    a->obj->nid = 0;
+    a->obj->flags |= ASN1_OBJECT_FLAG_DYNAMIC |
+        ASN1_OBJECT_FLAG_DYNAMIC_STRINGS | ASN1_OBJECT_FLAG_DYNAMIC_DATA;
+}
+
+static void cleanup2_doall(ADDED_OBJ *a)
+{
+    a->obj->nid++;
+}
+
+static void cleanup3_doall(ADDED_OBJ *a)
+{
+    if (--a->obj->nid == 0)
+        ASN1_OBJECT_free(a->obj);
+    OPENSSL_free(a);
+}
+
+void obj_cleanup_int(void)
+{
+    if (added == NULL)
+        return;
+    lh_ADDED_OBJ_set_down_load(added, 0);
+    lh_ADDED_OBJ_doall(added, cleanup1_doall); /* zero counters */
+    lh_ADDED_OBJ_doall(added, cleanup2_doall); /* set counters */
+    lh_ADDED_OBJ_doall(added, cleanup3_doall); /* free objects */
+    lh_ADDED_OBJ_free(added);
+    added = NULL;
+}
+
+int OBJ_new_nid(int num)
+{
+    int i;
+
+    i = new_nid;
+    new_nid += num;
+    return (i);
+}
+
+int OBJ_add_object(const ASN1_OBJECT *obj)
+{
+    ASN1_OBJECT *o;
+    ADDED_OBJ *ao[4] = { NULL, NULL, NULL, NULL }, *aop;
+    int i;
+
+    if (added == NULL)
+        if (!init_added())
+            return (0);
+    if ((o = OBJ_dup(obj)) == NULL)
+        goto err;
+    if ((ao[ADDED_NID] = OPENSSL_malloc(sizeof(*ao[0]))) == NULL)
+        goto err2;
+    if ((o->length != 0) && (obj->data != NULL))
+        if ((ao[ADDED_DATA] = OPENSSL_malloc(sizeof(*ao[0]))) == NULL)
+            goto err2;
+    if (o->sn != NULL)
+        if ((ao[ADDED_SNAME] = OPENSSL_malloc(sizeof(*ao[0]))) == NULL)
+            goto err2;
+    if (o->ln != NULL)
+        if ((ao[ADDED_LNAME] = OPENSSL_malloc(sizeof(*ao[0]))) == NULL)
+            goto err2;
+
+    for (i = ADDED_DATA; i <= ADDED_NID; i++) {
+        if (ao[i] != NULL) {
+            ao[i]->type = i;
+            ao[i]->obj = o;
+            aop = lh_ADDED_OBJ_insert(added, ao[i]);
+            /* memory leak, but should not normally matter */
+            OPENSSL_free(aop);
+        }
+    }
+    o->flags &=
+        ~(ASN1_OBJECT_FLAG_DYNAMIC | ASN1_OBJECT_FLAG_DYNAMIC_STRINGS |
+          ASN1_OBJECT_FLAG_DYNAMIC_DATA);
+
+    return (o->nid);
+ err2:
+    OBJerr(OBJ_F_OBJ_ADD_OBJECT, ERR_R_MALLOC_FAILURE);
+ err:
+    for (i = ADDED_DATA; i <= ADDED_NID; i++)
+        OPENSSL_free(ao[i]);
+    OPENSSL_free(o);
+    return (NID_undef);
+}
+
+ASN1_OBJECT *OBJ_nid2obj(int n)
+{
+    ADDED_OBJ ad, *adp;
+    ASN1_OBJECT ob;
+
+    if ((n >= 0) && (n < NUM_NID)) {
+        if ((n != NID_undef) && (nid_objs[n].nid == NID_undef)) {
+            OBJerr(OBJ_F_OBJ_NID2OBJ, OBJ_R_UNKNOWN_NID);
+            return (NULL);
+        }
+        return ((ASN1_OBJECT *)&(nid_objs[n]));
+    } else if (added == NULL)
+        return (NULL);
+    else {
+        ad.type = ADDED_NID;
+        ad.obj = &ob;
+        ob.nid = n;
+        adp = lh_ADDED_OBJ_retrieve(added, &ad);
+        if (adp != NULL)
+            return (adp->obj);
+        else {
+            OBJerr(OBJ_F_OBJ_NID2OBJ, OBJ_R_UNKNOWN_NID);
+            return (NULL);
+        }
+    }
+}
+
+const char *OBJ_nid2sn(int n)
+{
+    ADDED_OBJ ad, *adp;
+    ASN1_OBJECT ob;
+
+    if ((n >= 0) && (n < NUM_NID)) {
+        if ((n != NID_undef) && (nid_objs[n].nid == NID_undef)) {
+            OBJerr(OBJ_F_OBJ_NID2SN, OBJ_R_UNKNOWN_NID);
+            return (NULL);
+        }
+        return (nid_objs[n].sn);
+    } else if (added == NULL)
+        return (NULL);
+    else {
+        ad.type = ADDED_NID;
+        ad.obj = &ob;
+        ob.nid = n;
+        adp = lh_ADDED_OBJ_retrieve(added, &ad);
+        if (adp != NULL)
+            return (adp->obj->sn);
+        else {
+            OBJerr(OBJ_F_OBJ_NID2SN, OBJ_R_UNKNOWN_NID);
+            return (NULL);
+        }
+    }
+}
+
+const char *OBJ_nid2ln(int n)
+{
+    ADDED_OBJ ad, *adp;
+    ASN1_OBJECT ob;
+
+    if ((n >= 0) && (n < NUM_NID)) {
+        if ((n != NID_undef) && (nid_objs[n].nid == NID_undef)) {
+            OBJerr(OBJ_F_OBJ_NID2LN, OBJ_R_UNKNOWN_NID);
+            return (NULL);
+        }
+        return (nid_objs[n].ln);
+    } else if (added == NULL)
+        return (NULL);
+    else {
+        ad.type = ADDED_NID;
+        ad.obj = &ob;
+        ob.nid = n;
+        adp = lh_ADDED_OBJ_retrieve(added, &ad);
+        if (adp != NULL)
+            return (adp->obj->ln);
+        else {
+            OBJerr(OBJ_F_OBJ_NID2LN, OBJ_R_UNKNOWN_NID);
+            return (NULL);
+        }
+    }
+}
+
+static int obj_cmp(const ASN1_OBJECT *const *ap, const unsigned int *bp)
+{
+    int j;
+    const ASN1_OBJECT *a = *ap;
+    const ASN1_OBJECT *b = &nid_objs[*bp];
+
+    j = (a->length - b->length);
+    if (j)
+        return (j);
+    if (a->length == 0)
+        return 0;
+    return (memcmp(a->data, b->data, a->length));
+}
+
+IMPLEMENT_OBJ_BSEARCH_CMP_FN(const ASN1_OBJECT *, unsigned int, obj);
+
+int OBJ_obj2nid(const ASN1_OBJECT *a)
+{
+    const unsigned int *op;
+    ADDED_OBJ ad, *adp;
+
+    if (a == NULL)
+        return (NID_undef);
+    if (a->nid != 0)
+        return (a->nid);
+
+    if (a->length == 0)
+        return NID_undef;
+
+    if (added != NULL) {
+        ad.type = ADDED_DATA;
+        ad.obj = (ASN1_OBJECT *)a; /* XXX: ugly but harmless */
+        adp = lh_ADDED_OBJ_retrieve(added, &ad);
+        if (adp != NULL)
+            return (adp->obj->nid);
+    }
+    op = OBJ_bsearch_obj(&a, obj_objs, NUM_OBJ);
+    if (op == NULL)
+        return (NID_undef);
+    return (nid_objs[*op].nid);
+}
+
+/*
+ * Convert an object name into an ASN1_OBJECT if "noname" is not set then
+ * search for short and long names first. This will convert the "dotted" form
+ * into an object: unlike OBJ_txt2nid it can be used with any objects, not
+ * just registered ones.
+ */
+
+ASN1_OBJECT *OBJ_txt2obj(const char *s, int no_name)
+{
+    int nid = NID_undef;
+    ASN1_OBJECT *op = NULL;
+    unsigned char *buf;
+    unsigned char *p;
+    const unsigned char *cp;
+    int i, j;
+
+    if (!no_name) {
+        if (((nid = OBJ_sn2nid(s)) != NID_undef) ||
+            ((nid = OBJ_ln2nid(s)) != NID_undef))
+            return OBJ_nid2obj(nid);
+    }
+
+    /* Work out size of content octets */
+    i = a2d_ASN1_OBJECT(NULL, 0, s, -1);
+    if (i <= 0) {
+        /* Don't clear the error */
+        /*
+         * ERR_clear_error();
+         */
+        return NULL;
+    }
+    /* Work out total size */
+    j = ASN1_object_size(0, i, V_ASN1_OBJECT);
+    if (j < 0)
+        return NULL;
+
+    if ((buf = OPENSSL_malloc(j)) == NULL)
+        return NULL;
+
+    p = buf;
+    /* Write out tag+length */
+    ASN1_put_object(&p, 0, i, V_ASN1_OBJECT, V_ASN1_UNIVERSAL);
+    /* Write out contents */
+    a2d_ASN1_OBJECT(p, i, s, -1);
+
+    cp = buf;
+    op = d2i_ASN1_OBJECT(NULL, &cp, j);
+    OPENSSL_free(buf);
+    return op;
+}
+
+int OBJ_obj2txt(char *buf, int buf_len, const ASN1_OBJECT *a, int no_name)
+{
+    int i, n = 0, len, nid, first, use_bn;
+    BIGNUM *bl;
+    unsigned long l;
+    const unsigned char *p;
+    char tbuf[DECIMAL_SIZE(i) + DECIMAL_SIZE(l) + 2];
+
+    /* Ensure that, at every state, |buf| is NUL-terminated. */
+    if (buf && buf_len > 0)
+        buf[0] = '\0';
+
+    if ((a == NULL) || (a->data == NULL))
+        return (0);
+
+    if (!no_name && (nid = OBJ_obj2nid(a)) != NID_undef) {
+        const char *s;
+        s = OBJ_nid2ln(nid);
+        if (s == NULL)
+            s = OBJ_nid2sn(nid);
+        if (s) {
+            if (buf)
+                OPENSSL_strlcpy(buf, s, buf_len);
+            n = strlen(s);
+            return n;
+        }
+    }
+
+    len = a->length;
+    p = a->data;
+
+    first = 1;
+    bl = NULL;
+
+    while (len > 0) {
+        l = 0;
+        use_bn = 0;
+        for (;;) {
+            unsigned char c = *p++;
+            len--;
+            if ((len == 0) && (c & 0x80))
+                goto err;
+            if (use_bn) {
+                if (!BN_add_word(bl, c & 0x7f))
+                    goto err;
+            } else
+                l |= c & 0x7f;
+            if (!(c & 0x80))
+                break;
+            if (!use_bn && (l > (ULONG_MAX >> 7L))) {
+                if (bl == NULL && (bl = BN_new()) == NULL)
+                    goto err;
+                if (!BN_set_word(bl, l))
+                    goto err;
+                use_bn = 1;
+            }
+            if (use_bn) {
+                if (!BN_lshift(bl, bl, 7))
+                    goto err;
+            } else
+                l <<= 7L;
+        }
+
+        if (first) {
+            first = 0;
+            if (l >= 80) {
+                i = 2;
+                if (use_bn) {
+                    if (!BN_sub_word(bl, 80))
+                        goto err;
+                } else
+                    l -= 80;
+            } else {
+                i = (int)(l / 40);
+                l -= (long)(i * 40);
+            }
+            if (buf && (buf_len > 1)) {
+                *buf++ = i + '0';
+                *buf = '\0';
+                buf_len--;
+            }
+            n++;
+        }
+
+        if (use_bn) {
+            char *bndec;
+            bndec = BN_bn2dec(bl);
+            if (!bndec)
+                goto err;
+            i = strlen(bndec);
+            if (buf) {
+                if (buf_len > 1) {
+                    *buf++ = '.';
+                    *buf = '\0';
+                    buf_len--;
+                }
+                OPENSSL_strlcpy(buf, bndec, buf_len);
+                if (i > buf_len) {
+                    buf += buf_len;
+                    buf_len = 0;
+                } else {
+                    buf += i;
+                    buf_len -= i;
+                }
+            }
+            n++;
+            n += i;
+            OPENSSL_free(bndec);
+        } else {
+            BIO_snprintf(tbuf, sizeof tbuf, ".%lu", l);
+            i = strlen(tbuf);
+            if (buf && (buf_len > 0)) {
+                OPENSSL_strlcpy(buf, tbuf, buf_len);
+                if (i > buf_len) {
+                    buf += buf_len;
+                    buf_len = 0;
+                } else {
+                    buf += i;
+                    buf_len -= i;
+                }
+            }
+            n += i;
+            l = 0;
+        }
+    }
+
+    BN_free(bl);
+    return n;
+
+ err:
+    BN_free(bl);
+    return -1;
+}
+
+int OBJ_txt2nid(const char *s)
+{
+    ASN1_OBJECT *obj;
+    int nid;
+    obj = OBJ_txt2obj(s, 0);
+    nid = OBJ_obj2nid(obj);
+    ASN1_OBJECT_free(obj);
+    return nid;
+}
+
+int OBJ_ln2nid(const char *s)
+{
+    ASN1_OBJECT o;
+    const ASN1_OBJECT *oo = &o;
+    ADDED_OBJ ad, *adp;
+    const unsigned int *op;
+
+    o.ln = s;
+    if (added != NULL) {
+        ad.type = ADDED_LNAME;
+        ad.obj = &o;
+        adp = lh_ADDED_OBJ_retrieve(added, &ad);
+        if (adp != NULL)
+            return (adp->obj->nid);
+    }
+    op = OBJ_bsearch_ln(&oo, ln_objs, NUM_LN);
+    if (op == NULL)
+        return (NID_undef);
+    return (nid_objs[*op].nid);
+}
+
+int OBJ_sn2nid(const char *s)
+{
+    ASN1_OBJECT o;
+    const ASN1_OBJECT *oo = &o;
+    ADDED_OBJ ad, *adp;
+    const unsigned int *op;
+
+    o.sn = s;
+    if (added != NULL) {
+        ad.type = ADDED_SNAME;
+        ad.obj = &o;
+        adp = lh_ADDED_OBJ_retrieve(added, &ad);
+        if (adp != NULL)
+            return (adp->obj->nid);
+    }
+    op = OBJ_bsearch_sn(&oo, sn_objs, NUM_SN);
+    if (op == NULL)
+        return (NID_undef);
+    return (nid_objs[*op].nid);
+}
+
+const void *OBJ_bsearch_(const void *key, const void *base, int num, int size,
+                         int (*cmp) (const void *, const void *))
+{
+    return OBJ_bsearch_ex_(key, base, num, size, cmp, 0);
+}
+
+const void *OBJ_bsearch_ex_(const void *key, const void *base_, int num,
+                            int size,
+                            int (*cmp) (const void *, const void *),
+                            int flags)
+{
+    const char *base = base_;
+    int l, h, i = 0, c = 0;
+    const char *p = NULL;
+
+    if (num == 0)
+        return (NULL);
+    l = 0;
+    h = num;
+    while (l < h) {
+        i = (l + h) / 2;
+        p = &(base[i * size]);
+        c = (*cmp) (key, p);
+        if (c < 0)
+            h = i;
+        else if (c > 0)
+            l = i + 1;
+        else
+            break;
+    }
+#ifdef CHARSET_EBCDIC
+    /*
+     * THIS IS A KLUDGE - Because the *_obj is sorted in ASCII order, and I
+     * don't have perl (yet), we revert to a *LINEAR* search when the object
+     * wasn't found in the binary search.
+     */
+    if (c != 0) {
+        for (i = 0; i < num; ++i) {
+            p = &(base[i * size]);
+            c = (*cmp) (key, p);
+            if (c == 0 || (c < 0 && (flags & OBJ_BSEARCH_VALUE_ON_NOMATCH)))
+                return p;
+        }
+    }
+#endif
+    if (c != 0 && !(flags & OBJ_BSEARCH_VALUE_ON_NOMATCH))
+        p = NULL;
+    else if (c == 0 && (flags & OBJ_BSEARCH_FIRST_VALUE_ON_MATCH)) {
+        while (i > 0 && (*cmp) (key, &(base[(i - 1) * size])) == 0)
+            i--;
+        p = &(base[i * size]);
+    }
+    return (p);
+}
+
+int OBJ_create_objects(BIO *in)
+{
+    char buf[512];
+    int i, num = 0;
+    char *o, *s, *l = NULL;
+
+    for (;;) {
+        s = o = NULL;
+        i = BIO_gets(in, buf, 512);
+        if (i <= 0)
+            return (num);
+        buf[i - 1] = '\0';
+        if (!isalnum((unsigned char)buf[0]))
+            return (num);
+        o = s = buf;
+        while (isdigit((unsigned char)*s) || (*s == '.'))
+            s++;
+        if (*s != '\0') {
+            *(s++) = '\0';
+            while (isspace((unsigned char)*s))
+                s++;
+            if (*s == '\0')
+                s = NULL;
+            else {
+                l = s;
+                while ((*l != '\0') && !isspace((unsigned char)*l))
+                    l++;
+                if (*l != '\0') {
+                    *(l++) = '\0';
+                    while (isspace((unsigned char)*l))
+                        l++;
+                    if (*l == '\0')
+                        l = NULL;
+                } else
+                    l = NULL;
+            }
+        } else
+            s = NULL;
+        if ((o == NULL) || (*o == '\0'))
+            return (num);
+        if (!OBJ_create(o, s, l))
+            return (num);
+        num++;
+    }
+    /* return(num); */
+}
+
+int OBJ_create(const char *oid, const char *sn, const char *ln)
+{
+    ASN1_OBJECT *tmpoid = NULL;
+    int ok = 0;
+
+    /* Check to see if short or long name already present */
+    if (OBJ_sn2nid(sn) != NID_undef || OBJ_ln2nid(ln) != NID_undef) {
+        OBJerr(OBJ_F_OBJ_CREATE, OBJ_R_OID_EXISTS);
+        return 0;
+    }
+
+    /* Convert numerical OID string to an ASN1_OBJECT structure */
+    tmpoid = OBJ_txt2obj(oid, 1);
+
+    /* If NID is not NID_undef then object already exists */
+    if (OBJ_obj2nid(tmpoid) != NID_undef) {
+        OBJerr(OBJ_F_OBJ_CREATE, OBJ_R_OID_EXISTS);
+        goto err;
+    }
+
+    tmpoid->nid = OBJ_new_nid(1);
+    tmpoid->sn = (char *)sn;
+    tmpoid->ln = (char *)ln;
+
+    ok = OBJ_add_object(tmpoid);
+
+    tmpoid->sn = NULL;
+    tmpoid->ln = NULL;
+
+ err:
+    ASN1_OBJECT_free(tmpoid);
+    return ok;
+}
+
+size_t OBJ_length(const ASN1_OBJECT *obj)
+{
+    if (obj == NULL)
+        return 0;
+    return obj->length;
+}
+
+const unsigned char *OBJ_get0_data(const ASN1_OBJECT *obj)
+{
+    if (obj == NULL)
+        return NULL;
+    return obj->data;
+}
diff --git a/crypto/objects/obj_dat.h b/crypto/objects/obj_dat.h
new file mode 100644
index 000000000..292dfaf06
--- /dev/null
+++ b/crypto/objects/obj_dat.h
@@ -0,0 +1,5868 @@
+/*
+ * WARNING: do not edit!
+ * Generated by crypto/objects/obj_dat.pl
+ *
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/* Serialized OID's */
+static const unsigned char so[7972] = {
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,                 /* [    0] OBJ_rsadsi */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,            /* [    6] OBJ_pkcs */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x02,0x02,       /* [   13] OBJ_md2 */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x02,0x05,       /* [   21] OBJ_md5 */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x03,0x04,       /* [   29] OBJ_rc4 */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x01,  /* [   37] OBJ_rsaEncryption */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x02,  /* [   46] OBJ_md2WithRSAEncryption */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x04,  /* [   55] OBJ_md5WithRSAEncryption */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x05,0x01,  /* [   64] OBJ_pbeWithMD2AndDES_CBC */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x05,0x03,  /* [   73] OBJ_pbeWithMD5AndDES_CBC */
+    0x55,                                          /* [   82] OBJ_X500 */
+    0x55,0x04,                                     /* [   83] OBJ_X509 */
+    0x55,0x04,0x03,                                /* [   85] OBJ_commonName */
+    0x55,0x04,0x06,                                /* [   88] OBJ_countryName */
+    0x55,0x04,0x07,                                /* [   91] OBJ_localityName */
+    0x55,0x04,0x08,                                /* [   94] OBJ_stateOrProvinceName */
+    0x55,0x04,0x0A,                                /* [   97] OBJ_organizationName */
+    0x55,0x04,0x0B,                                /* [  100] OBJ_organizationalUnitName */
+    0x55,0x08,0x01,0x01,                           /* [  103] OBJ_rsa */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x07,       /* [  107] OBJ_pkcs7 */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x07,0x01,  /* [  115] OBJ_pkcs7_data */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x07,0x02,  /* [  124] OBJ_pkcs7_signed */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x07,0x03,  /* [  133] OBJ_pkcs7_enveloped */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x07,0x04,  /* [  142] OBJ_pkcs7_signedAndEnveloped */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x07,0x05,  /* [  151] OBJ_pkcs7_digest */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x07,0x06,  /* [  160] OBJ_pkcs7_encrypted */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x03,       /* [  169] OBJ_pkcs3 */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x03,0x01,  /* [  177] OBJ_dhKeyAgreement */
+    0x2B,0x0E,0x03,0x02,0x06,                      /* [  186] OBJ_des_ecb */
+    0x2B,0x0E,0x03,0x02,0x09,                      /* [  191] OBJ_des_cfb64 */
+    0x2B,0x0E,0x03,0x02,0x07,                      /* [  196] OBJ_des_cbc */
+    0x2B,0x0E,0x03,0x02,0x11,                      /* [  201] OBJ_des_ede_ecb */
+    0x2B,0x06,0x01,0x04,0x01,0x81,0x3C,0x07,0x01,0x01,0x02,  /* [  206] OBJ_idea_cbc */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x03,0x02,       /* [  217] OBJ_rc2_cbc */
+    0x2B,0x0E,0x03,0x02,0x12,                      /* [  225] OBJ_sha */
+    0x2B,0x0E,0x03,0x02,0x0F,                      /* [  230] OBJ_shaWithRSAEncryption */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x03,0x07,       /* [  235] OBJ_des_ede3_cbc */
+    0x2B,0x0E,0x03,0x02,0x08,                      /* [  243] OBJ_des_ofb64 */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,       /* [  248] OBJ_pkcs9 */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x01,  /* [  256] OBJ_pkcs9_emailAddress */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x02,  /* [  265] OBJ_pkcs9_unstructuredName */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x03,  /* [  274] OBJ_pkcs9_contentType */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x04,  /* [  283] OBJ_pkcs9_messageDigest */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x05,  /* [  292] OBJ_pkcs9_signingTime */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x06,  /* [  301] OBJ_pkcs9_countersignature */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x07,  /* [  310] OBJ_pkcs9_challengePassword */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x08,  /* [  319] OBJ_pkcs9_unstructuredAddress */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x09,  /* [  328] OBJ_pkcs9_extCertAttributes */
+    0x60,0x86,0x48,0x01,0x86,0xF8,0x42,            /* [  337] OBJ_netscape */
+    0x60,0x86,0x48,0x01,0x86,0xF8,0x42,0x01,       /* [  344] OBJ_netscape_cert_extension */
+    0x60,0x86,0x48,0x01,0x86,0xF8,0x42,0x02,       /* [  352] OBJ_netscape_data_type */
+    0x2B,0x0E,0x03,0x02,0x1A,                      /* [  360] OBJ_sha1 */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x05,  /* [  365] OBJ_sha1WithRSAEncryption */
+    0x2B,0x0E,0x03,0x02,0x0D,                      /* [  374] OBJ_dsaWithSHA */
+    0x2B,0x0E,0x03,0x02,0x0C,                      /* [  379] OBJ_dsa_2 */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x05,0x0B,  /* [  384] OBJ_pbeWithSHA1AndRC2_CBC */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x05,0x0C,  /* [  393] OBJ_id_pbkdf2 */
+    0x2B,0x0E,0x03,0x02,0x1B,                      /* [  402] OBJ_dsaWithSHA1_2 */
+    0x60,0x86,0x48,0x01,0x86,0xF8,0x42,0x01,0x01,  /* [  407] OBJ_netscape_cert_type */
+    0x60,0x86,0x48,0x01,0x86,0xF8,0x42,0x01,0x02,  /* [  416] OBJ_netscape_base_url */
+    0x60,0x86,0x48,0x01,0x86,0xF8,0x42,0x01,0x03,  /* [  425] OBJ_netscape_revocation_url */
+    0x60,0x86,0x48,0x01,0x86,0xF8,0x42,0x01,0x04,  /* [  434] OBJ_netscape_ca_revocation_url */
+    0x60,0x86,0x48,0x01,0x86,0xF8,0x42,0x01,0x07,  /* [  443] OBJ_netscape_renewal_url */
+    0x60,0x86,0x48,0x01,0x86,0xF8,0x42,0x01,0x08,  /* [  452] OBJ_netscape_ca_policy_url */
+    0x60,0x86,0x48,0x01,0x86,0xF8,0x42,0x01,0x0C,  /* [  461] OBJ_netscape_ssl_server_name */
+    0x60,0x86,0x48,0x01,0x86,0xF8,0x42,0x01,0x0D,  /* [  470] OBJ_netscape_comment */
+    0x60,0x86,0x48,0x01,0x86,0xF8,0x42,0x02,0x05,  /* [  479] OBJ_netscape_cert_sequence */
+    0x55,0x1D,                                     /* [  488] OBJ_id_ce */
+    0x55,0x1D,0x0E,                                /* [  490] OBJ_subject_key_identifier */
+    0x55,0x1D,0x0F,                                /* [  493] OBJ_key_usage */
+    0x55,0x1D,0x10,                                /* [  496] OBJ_private_key_usage_period */
+    0x55,0x1D,0x11,                                /* [  499] OBJ_subject_alt_name */
+    0x55,0x1D,0x12,                                /* [  502] OBJ_issuer_alt_name */
+    0x55,0x1D,0x13,                                /* [  505] OBJ_basic_constraints */
+    0x55,0x1D,0x14,                                /* [  508] OBJ_crl_number */
+    0x55,0x1D,0x20,                                /* [  511] OBJ_certificate_policies */
+    0x55,0x1D,0x23,                                /* [  514] OBJ_authority_key_identifier */
+    0x2B,0x06,0x01,0x04,0x01,0x97,0x55,0x01,0x02,  /* [  517] OBJ_bf_cbc */
+    0x55,0x08,0x03,0x65,                           /* [  526] OBJ_mdc2 */
+    0x55,0x08,0x03,0x64,                           /* [  530] OBJ_mdc2WithRSA */
+    0x55,0x04,0x2A,                                /* [  534] OBJ_givenName */
+    0x55,0x04,0x04,                                /* [  537] OBJ_surname */
+    0x55,0x04,0x2B,                                /* [  540] OBJ_initials */
+    0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x01,0x2C,  /* [  543] OBJ_uniqueIdentifier */
+    0x55,0x1D,0x1F,                                /* [  553] OBJ_crl_distribution_points */
+    0x2B,0x0E,0x03,0x02,0x03,                      /* [  556] OBJ_md5WithRSA */
+    0x55,0x04,0x05,                                /* [  561] OBJ_serialNumber */
+    0x55,0x04,0x0C,                                /* [  564] OBJ_title */
+    0x55,0x04,0x0D,                                /* [  567] OBJ_description */
+    0x2A,0x86,0x48,0x86,0xF6,0x7D,0x07,0x42,0x0A,  /* [  570] OBJ_cast5_cbc */
+    0x2A,0x86,0x48,0x86,0xF6,0x7D,0x07,0x42,0x0C,  /* [  579] OBJ_pbeWithMD5AndCast5_CBC */
+    0x2A,0x86,0x48,0xCE,0x38,0x04,0x03,            /* [  588] OBJ_dsaWithSHA1 */
+    0x2B,0x0E,0x03,0x02,0x1D,                      /* [  595] OBJ_sha1WithRSA */
+    0x2A,0x86,0x48,0xCE,0x38,0x04,0x01,            /* [  600] OBJ_dsa */
+    0x2B,0x24,0x03,0x02,0x01,                      /* [  607] OBJ_ripemd160 */
+    0x2B,0x24,0x03,0x03,0x01,0x02,                 /* [  612] OBJ_ripemd160WithRSA */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x03,0x08,       /* [  618] OBJ_rc5_cbc */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x03,0x08,  /* [  626] OBJ_zlib_compression */
+    0x55,0x1D,0x25,                                /* [  637] OBJ_ext_key_usage */
+    0x2B,0x06,0x01,0x05,0x05,0x07,                 /* [  640] OBJ_id_pkix */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x03,            /* [  646] OBJ_id_kp */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x03,0x01,       /* [  653] OBJ_server_auth */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x03,0x02,       /* [  661] OBJ_client_auth */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x03,0x03,       /* [  669] OBJ_code_sign */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x03,0x04,       /* [  677] OBJ_email_protect */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x03,0x08,       /* [  685] OBJ_time_stamp */
+    0x2B,0x06,0x01,0x04,0x01,0x82,0x37,0x02,0x01,0x15,  /* [  693] OBJ_ms_code_ind */
+    0x2B,0x06,0x01,0x04,0x01,0x82,0x37,0x02,0x01,0x16,  /* [  703] OBJ_ms_code_com */
+    0x2B,0x06,0x01,0x04,0x01,0x82,0x37,0x0A,0x03,0x01,  /* [  713] OBJ_ms_ctl_sign */
+    0x2B,0x06,0x01,0x04,0x01,0x82,0x37,0x0A,0x03,0x03,  /* [  723] OBJ_ms_sgc */
+    0x2B,0x06,0x01,0x04,0x01,0x82,0x37,0x0A,0x03,0x04,  /* [  733] OBJ_ms_efs */
+    0x60,0x86,0x48,0x01,0x86,0xF8,0x42,0x04,0x01,  /* [  743] OBJ_ns_sgc */
+    0x55,0x1D,0x1B,                                /* [  752] OBJ_delta_crl */
+    0x55,0x1D,0x15,                                /* [  755] OBJ_crl_reason */
+    0x55,0x1D,0x18,                                /* [  758] OBJ_invalidity_date */
+    0x2B,0x65,0x01,0x04,0x01,                      /* [  761] OBJ_sxnet */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x0C,0x01,0x01,  /* [  766] OBJ_pbe_WithSHA1And128BitRC4 */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x0C,0x01,0x02,  /* [  776] OBJ_pbe_WithSHA1And40BitRC4 */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x0C,0x01,0x03,  /* [  786] OBJ_pbe_WithSHA1And3_Key_TripleDES_CBC */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x0C,0x01,0x04,  /* [  796] OBJ_pbe_WithSHA1And2_Key_TripleDES_CBC */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x0C,0x01,0x05,  /* [  806] OBJ_pbe_WithSHA1And128BitRC2_CBC */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x0C,0x01,0x06,  /* [  816] OBJ_pbe_WithSHA1And40BitRC2_CBC */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x0C,0x0A,0x01,0x01,  /* [  826] OBJ_keyBag */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x0C,0x0A,0x01,0x02,  /* [  837] OBJ_pkcs8ShroudedKeyBag */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x0C,0x0A,0x01,0x03,  /* [  848] OBJ_certBag */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x0C,0x0A,0x01,0x04,  /* [  859] OBJ_crlBag */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x0C,0x0A,0x01,0x05,  /* [  870] OBJ_secretBag */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x0C,0x0A,0x01,0x06,  /* [  881] OBJ_safeContentsBag */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x14,  /* [  892] OBJ_friendlyName */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x15,  /* [  901] OBJ_localKeyID */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x16,0x01,  /* [  910] OBJ_x509Certificate */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x16,0x02,  /* [  920] OBJ_sdsiCertificate */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x17,0x01,  /* [  930] OBJ_x509Crl */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x05,0x0D,  /* [  940] OBJ_pbes2 */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x05,0x0E,  /* [  949] OBJ_pbmac1 */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x02,0x07,       /* [  958] OBJ_hmacWithSHA1 */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x02,0x01,       /* [  966] OBJ_id_qt_cps */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x02,0x02,       /* [  974] OBJ_id_qt_unotice */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x0F,  /* [  982] OBJ_SMIMECapabilities */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x05,0x04,  /* [  991] OBJ_pbeWithMD2AndRC2_CBC */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x05,0x06,  /* [ 1000] OBJ_pbeWithMD5AndRC2_CBC */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x05,0x0A,  /* [ 1009] OBJ_pbeWithSHA1AndDES_CBC */
+    0x2B,0x06,0x01,0x04,0x01,0x82,0x37,0x02,0x01,0x0E,  /* [ 1018] OBJ_ms_ext_req */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x0E,  /* [ 1028] OBJ_ext_req */
+    0x55,0x04,0x29,                                /* [ 1037] OBJ_name */
+    0x55,0x04,0x2E,                                /* [ 1040] OBJ_dnQualifier */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x01,            /* [ 1043] OBJ_id_pe */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x30,            /* [ 1050] OBJ_id_ad */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x01,0x01,       /* [ 1057] OBJ_info_access */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x30,0x01,       /* [ 1065] OBJ_ad_OCSP */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x30,0x02,       /* [ 1073] OBJ_ad_ca_issuers */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x03,0x09,       /* [ 1081] OBJ_OCSP_sign */
+    0x2A,                                          /* [ 1089] OBJ_member_body */
+    0x2A,0x86,0x48,                                /* [ 1090] OBJ_ISO_US */
+    0x2A,0x86,0x48,0xCE,0x38,                      /* [ 1093] OBJ_X9_57 */
+    0x2A,0x86,0x48,0xCE,0x38,0x04,                 /* [ 1098] OBJ_X9cm */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,       /* [ 1104] OBJ_pkcs1 */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x05,       /* [ 1112] OBJ_pkcs5 */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,  /* [ 1120] OBJ_SMIME */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x00,  /* [ 1129] OBJ_id_smime_mod */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x01,  /* [ 1139] OBJ_id_smime_ct */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x02,  /* [ 1149] OBJ_id_smime_aa */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x03,  /* [ 1159] OBJ_id_smime_alg */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x04,  /* [ 1169] OBJ_id_smime_cd */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x05,  /* [ 1179] OBJ_id_smime_spq */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x06,  /* [ 1189] OBJ_id_smime_cti */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x00,0x01,  /* [ 1199] OBJ_id_smime_mod_cms */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x00,0x02,  /* [ 1210] OBJ_id_smime_mod_ess */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x00,0x03,  /* [ 1221] OBJ_id_smime_mod_oid */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x00,0x04,  /* [ 1232] OBJ_id_smime_mod_msg_v3 */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x00,0x05,  /* [ 1243] OBJ_id_smime_mod_ets_eSignature_88 */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x00,0x06,  /* [ 1254] OBJ_id_smime_mod_ets_eSignature_97 */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x00,0x07,  /* [ 1265] OBJ_id_smime_mod_ets_eSigPolicy_88 */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x00,0x08,  /* [ 1276] OBJ_id_smime_mod_ets_eSigPolicy_97 */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x01,0x01,  /* [ 1287] OBJ_id_smime_ct_receipt */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x01,0x02,  /* [ 1298] OBJ_id_smime_ct_authData */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x01,0x03,  /* [ 1309] OBJ_id_smime_ct_publishCert */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x01,0x04,  /* [ 1320] OBJ_id_smime_ct_TSTInfo */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x01,0x05,  /* [ 1331] OBJ_id_smime_ct_TDTInfo */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x01,0x06,  /* [ 1342] OBJ_id_smime_ct_contentInfo */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x01,0x07,  /* [ 1353] OBJ_id_smime_ct_DVCSRequestData */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x01,0x08,  /* [ 1364] OBJ_id_smime_ct_DVCSResponseData */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x02,0x01,  /* [ 1375] OBJ_id_smime_aa_receiptRequest */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x02,0x02,  /* [ 1386] OBJ_id_smime_aa_securityLabel */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x02,0x03,  /* [ 1397] OBJ_id_smime_aa_mlExpandHistory */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x02,0x04,  /* [ 1408] OBJ_id_smime_aa_contentHint */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x02,0x05,  /* [ 1419] OBJ_id_smime_aa_msgSigDigest */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x02,0x06,  /* [ 1430] OBJ_id_smime_aa_encapContentType */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x02,0x07,  /* [ 1441] OBJ_id_smime_aa_contentIdentifier */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x02,0x08,  /* [ 1452] OBJ_id_smime_aa_macValue */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x02,0x09,  /* [ 1463] OBJ_id_smime_aa_equivalentLabels */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x02,0x0A,  /* [ 1474] OBJ_id_smime_aa_contentReference */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x02,0x0B,  /* [ 1485] OBJ_id_smime_aa_encrypKeyPref */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x02,0x0C,  /* [ 1496] OBJ_id_smime_aa_signingCertificate */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x02,0x0D,  /* [ 1507] OBJ_id_smime_aa_smimeEncryptCerts */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x02,0x0E,  /* [ 1518] OBJ_id_smime_aa_timeStampToken */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x02,0x0F,  /* [ 1529] OBJ_id_smime_aa_ets_sigPolicyId */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x02,0x10,  /* [ 1540] OBJ_id_smime_aa_ets_commitmentType */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x02,0x11,  /* [ 1551] OBJ_id_smime_aa_ets_signerLocation */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x02,0x12,  /* [ 1562] OBJ_id_smime_aa_ets_signerAttr */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x02,0x13,  /* [ 1573] OBJ_id_smime_aa_ets_otherSigCert */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x02,0x14,  /* [ 1584] OBJ_id_smime_aa_ets_contentTimestamp */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x02,0x15,  /* [ 1595] OBJ_id_smime_aa_ets_CertificateRefs */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x02,0x16,  /* [ 1606] OBJ_id_smime_aa_ets_RevocationRefs */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x02,0x17,  /* [ 1617] OBJ_id_smime_aa_ets_certValues */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x02,0x18,  /* [ 1628] OBJ_id_smime_aa_ets_revocationValues */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x02,0x19,  /* [ 1639] OBJ_id_smime_aa_ets_escTimeStamp */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x02,0x1A,  /* [ 1650] OBJ_id_smime_aa_ets_certCRLTimestamp */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x02,0x1B,  /* [ 1661] OBJ_id_smime_aa_ets_archiveTimeStamp */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x02,0x1C,  /* [ 1672] OBJ_id_smime_aa_signatureType */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x02,0x1D,  /* [ 1683] OBJ_id_smime_aa_dvcs_dvc */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x03,0x01,  /* [ 1694] OBJ_id_smime_alg_ESDHwith3DES */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x03,0x02,  /* [ 1705] OBJ_id_smime_alg_ESDHwithRC2 */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x03,0x03,  /* [ 1716] OBJ_id_smime_alg_3DESwrap */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x03,0x04,  /* [ 1727] OBJ_id_smime_alg_RC2wrap */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x03,0x05,  /* [ 1738] OBJ_id_smime_alg_ESDH */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x03,0x06,  /* [ 1749] OBJ_id_smime_alg_CMS3DESwrap */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x03,0x07,  /* [ 1760] OBJ_id_smime_alg_CMSRC2wrap */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x04,0x01,  /* [ 1771] OBJ_id_smime_cd_ldap */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x05,0x01,  /* [ 1782] OBJ_id_smime_spq_ets_sqt_uri */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x05,0x02,  /* [ 1793] OBJ_id_smime_spq_ets_sqt_unotice */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x06,0x01,  /* [ 1804] OBJ_id_smime_cti_ets_proofOfOrigin */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x06,0x02,  /* [ 1815] OBJ_id_smime_cti_ets_proofOfReceipt */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x06,0x03,  /* [ 1826] OBJ_id_smime_cti_ets_proofOfDelivery */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x06,0x04,  /* [ 1837] OBJ_id_smime_cti_ets_proofOfSender */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x06,0x05,  /* [ 1848] OBJ_id_smime_cti_ets_proofOfApproval */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x06,0x06,  /* [ 1859] OBJ_id_smime_cti_ets_proofOfCreation */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x02,0x04,       /* [ 1870] OBJ_md4 */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x00,            /* [ 1878] OBJ_id_pkix_mod */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x02,            /* [ 1885] OBJ_id_qt */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x04,            /* [ 1892] OBJ_id_it */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x05,            /* [ 1899] OBJ_id_pkip */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x06,            /* [ 1906] OBJ_id_alg */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x07,            /* [ 1913] OBJ_id_cmc */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x08,            /* [ 1920] OBJ_id_on */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x09,            /* [ 1927] OBJ_id_pda */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x0A,            /* [ 1934] OBJ_id_aca */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x0B,            /* [ 1941] OBJ_id_qcs */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x0C,            /* [ 1948] OBJ_id_cct */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x00,0x01,       /* [ 1955] OBJ_id_pkix1_explicit_88 */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x00,0x02,       /* [ 1963] OBJ_id_pkix1_implicit_88 */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x00,0x03,       /* [ 1971] OBJ_id_pkix1_explicit_93 */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x00,0x04,       /* [ 1979] OBJ_id_pkix1_implicit_93 */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x00,0x05,       /* [ 1987] OBJ_id_mod_crmf */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x00,0x06,       /* [ 1995] OBJ_id_mod_cmc */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x00,0x07,       /* [ 2003] OBJ_id_mod_kea_profile_88 */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x00,0x08,       /* [ 2011] OBJ_id_mod_kea_profile_93 */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x00,0x09,       /* [ 2019] OBJ_id_mod_cmp */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x00,0x0A,       /* [ 2027] OBJ_id_mod_qualified_cert_88 */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x00,0x0B,       /* [ 2035] OBJ_id_mod_qualified_cert_93 */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x00,0x0C,       /* [ 2043] OBJ_id_mod_attribute_cert */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x00,0x0D,       /* [ 2051] OBJ_id_mod_timestamp_protocol */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x00,0x0E,       /* [ 2059] OBJ_id_mod_ocsp */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x00,0x0F,       /* [ 2067] OBJ_id_mod_dvcs */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x00,0x10,       /* [ 2075] OBJ_id_mod_cmp2000 */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x01,0x02,       /* [ 2083] OBJ_biometricInfo */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x01,0x03,       /* [ 2091] OBJ_qcStatements */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x01,0x04,       /* [ 2099] OBJ_ac_auditEntity */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x01,0x05,       /* [ 2107] OBJ_ac_targeting */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x01,0x06,       /* [ 2115] OBJ_aaControls */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x01,0x07,       /* [ 2123] OBJ_sbgp_ipAddrBlock */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x01,0x08,       /* [ 2131] OBJ_sbgp_autonomousSysNum */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x01,0x09,       /* [ 2139] OBJ_sbgp_routerIdentifier */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x02,0x03,       /* [ 2147] OBJ_textNotice */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x03,0x05,       /* [ 2155] OBJ_ipsecEndSystem */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x03,0x06,       /* [ 2163] OBJ_ipsecTunnel */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x03,0x07,       /* [ 2171] OBJ_ipsecUser */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x03,0x0A,       /* [ 2179] OBJ_dvcs */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x04,0x01,       /* [ 2187] OBJ_id_it_caProtEncCert */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x04,0x02,       /* [ 2195] OBJ_id_it_signKeyPairTypes */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x04,0x03,       /* [ 2203] OBJ_id_it_encKeyPairTypes */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x04,0x04,       /* [ 2211] OBJ_id_it_preferredSymmAlg */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x04,0x05,       /* [ 2219] OBJ_id_it_caKeyUpdateInfo */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x04,0x06,       /* [ 2227] OBJ_id_it_currentCRL */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x04,0x07,       /* [ 2235] OBJ_id_it_unsupportedOIDs */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x04,0x08,       /* [ 2243] OBJ_id_it_subscriptionRequest */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x04,0x09,       /* [ 2251] OBJ_id_it_subscriptionResponse */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x04,0x0A,       /* [ 2259] OBJ_id_it_keyPairParamReq */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x04,0x0B,       /* [ 2267] OBJ_id_it_keyPairParamRep */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x04,0x0C,       /* [ 2275] OBJ_id_it_revPassphrase */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x04,0x0D,       /* [ 2283] OBJ_id_it_implicitConfirm */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x04,0x0E,       /* [ 2291] OBJ_id_it_confirmWaitTime */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x04,0x0F,       /* [ 2299] OBJ_id_it_origPKIMessage */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x05,0x01,       /* [ 2307] OBJ_id_regCtrl */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x05,0x02,       /* [ 2315] OBJ_id_regInfo */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x05,0x01,0x01,  /* [ 2323] OBJ_id_regCtrl_regToken */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x05,0x01,0x02,  /* [ 2332] OBJ_id_regCtrl_authenticator */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x05,0x01,0x03,  /* [ 2341] OBJ_id_regCtrl_pkiPublicationInfo */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x05,0x01,0x04,  /* [ 2350] OBJ_id_regCtrl_pkiArchiveOptions */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x05,0x01,0x05,  /* [ 2359] OBJ_id_regCtrl_oldCertID */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x05,0x01,0x06,  /* [ 2368] OBJ_id_regCtrl_protocolEncrKey */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x05,0x02,0x01,  /* [ 2377] OBJ_id_regInfo_utf8Pairs */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x05,0x02,0x02,  /* [ 2386] OBJ_id_regInfo_certReq */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x06,0x01,       /* [ 2395] OBJ_id_alg_des40 */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x06,0x02,       /* [ 2403] OBJ_id_alg_noSignature */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x06,0x03,       /* [ 2411] OBJ_id_alg_dh_sig_hmac_sha1 */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x06,0x04,       /* [ 2419] OBJ_id_alg_dh_pop */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x07,0x01,       /* [ 2427] OBJ_id_cmc_statusInfo */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x07,0x02,       /* [ 2435] OBJ_id_cmc_identification */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x07,0x03,       /* [ 2443] OBJ_id_cmc_identityProof */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x07,0x04,       /* [ 2451] OBJ_id_cmc_dataReturn */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x07,0x05,       /* [ 2459] OBJ_id_cmc_transactionId */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x07,0x06,       /* [ 2467] OBJ_id_cmc_senderNonce */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x07,0x07,       /* [ 2475] OBJ_id_cmc_recipientNonce */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x07,0x08,       /* [ 2483] OBJ_id_cmc_addExtensions */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x07,0x09,       /* [ 2491] OBJ_id_cmc_encryptedPOP */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x07,0x0A,       /* [ 2499] OBJ_id_cmc_decryptedPOP */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x07,0x0B,       /* [ 2507] OBJ_id_cmc_lraPOPWitness */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x07,0x0F,       /* [ 2515] OBJ_id_cmc_getCert */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x07,0x10,       /* [ 2523] OBJ_id_cmc_getCRL */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x07,0x11,       /* [ 2531] OBJ_id_cmc_revokeRequest */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x07,0x12,       /* [ 2539] OBJ_id_cmc_regInfo */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x07,0x13,       /* [ 2547] OBJ_id_cmc_responseInfo */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x07,0x15,       /* [ 2555] OBJ_id_cmc_queryPending */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x07,0x16,       /* [ 2563] OBJ_id_cmc_popLinkRandom */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x07,0x17,       /* [ 2571] OBJ_id_cmc_popLinkWitness */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x07,0x18,       /* [ 2579] OBJ_id_cmc_confirmCertAcceptance */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x08,0x01,       /* [ 2587] OBJ_id_on_personalData */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x09,0x01,       /* [ 2595] OBJ_id_pda_dateOfBirth */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x09,0x02,       /* [ 2603] OBJ_id_pda_placeOfBirth */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x09,0x03,       /* [ 2611] OBJ_id_pda_gender */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x09,0x04,       /* [ 2619] OBJ_id_pda_countryOfCitizenship */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x09,0x05,       /* [ 2627] OBJ_id_pda_countryOfResidence */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x0A,0x01,       /* [ 2635] OBJ_id_aca_authenticationInfo */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x0A,0x02,       /* [ 2643] OBJ_id_aca_accessIdentity */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x0A,0x03,       /* [ 2651] OBJ_id_aca_chargingIdentity */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x0A,0x04,       /* [ 2659] OBJ_id_aca_group */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x0A,0x05,       /* [ 2667] OBJ_id_aca_role */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x0B,0x01,       /* [ 2675] OBJ_id_qcs_pkixQCSyntax_v1 */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x0C,0x01,       /* [ 2683] OBJ_id_cct_crs */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x0C,0x02,       /* [ 2691] OBJ_id_cct_PKIData */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x0C,0x03,       /* [ 2699] OBJ_id_cct_PKIResponse */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x30,0x03,       /* [ 2707] OBJ_ad_timeStamping */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x30,0x04,       /* [ 2715] OBJ_ad_dvcs */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x30,0x01,0x01,  /* [ 2723] OBJ_id_pkix_OCSP_basic */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x30,0x01,0x02,  /* [ 2732] OBJ_id_pkix_OCSP_Nonce */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x30,0x01,0x03,  /* [ 2741] OBJ_id_pkix_OCSP_CrlID */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x30,0x01,0x04,  /* [ 2750] OBJ_id_pkix_OCSP_acceptableResponses */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x30,0x01,0x05,  /* [ 2759] OBJ_id_pkix_OCSP_noCheck */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x30,0x01,0x06,  /* [ 2768] OBJ_id_pkix_OCSP_archiveCutoff */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x30,0x01,0x07,  /* [ 2777] OBJ_id_pkix_OCSP_serviceLocator */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x30,0x01,0x08,  /* [ 2786] OBJ_id_pkix_OCSP_extendedStatus */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x30,0x01,0x09,  /* [ 2795] OBJ_id_pkix_OCSP_valid */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x30,0x01,0x0A,  /* [ 2804] OBJ_id_pkix_OCSP_path */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x30,0x01,0x0B,  /* [ 2813] OBJ_id_pkix_OCSP_trustRoot */
+    0x2B,0x0E,0x03,0x02,                           /* [ 2822] OBJ_algorithm */
+    0x2B,0x0E,0x03,0x02,0x0B,                      /* [ 2826] OBJ_rsaSignature */
+    0x55,0x08,                                     /* [ 2831] OBJ_X500algorithms */
+    0x2B,                                          /* [ 2833] OBJ_org */
+    0x2B,0x06,                                     /* [ 2834] OBJ_dod */
+    0x2B,0x06,0x01,                                /* [ 2836] OBJ_iana */
+    0x2B,0x06,0x01,0x01,                           /* [ 2839] OBJ_Directory */
+    0x2B,0x06,0x01,0x02,                           /* [ 2843] OBJ_Management */
+    0x2B,0x06,0x01,0x03,                           /* [ 2847] OBJ_Experimental */
+    0x2B,0x06,0x01,0x04,                           /* [ 2851] OBJ_Private */
+    0x2B,0x06,0x01,0x05,                           /* [ 2855] OBJ_Security */
+    0x2B,0x06,0x01,0x06,                           /* [ 2859] OBJ_SNMPv2 */
+    0x2B,0x06,0x01,0x07,                           /* [ 2863] OBJ_Mail */
+    0x2B,0x06,0x01,0x04,0x01,                      /* [ 2867] OBJ_Enterprises */
+    0x2B,0x06,0x01,0x04,0x01,0x8B,0x3A,0x82,0x58,  /* [ 2872] OBJ_dcObject */
+    0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x01,0x19,  /* [ 2881] OBJ_domainComponent */
+    0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x04,0x0D,  /* [ 2891] OBJ_Domain */
+    0x55,0x01,0x05,                                /* [ 2901] OBJ_selected_attribute_types */
+    0x55,0x01,0x05,0x37,                           /* [ 2904] OBJ_clearance */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x03,  /* [ 2908] OBJ_md4WithRSAEncryption */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x01,0x0A,       /* [ 2917] OBJ_ac_proxying */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x01,0x0B,       /* [ 2925] OBJ_sinfo_access */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x0A,0x06,       /* [ 2933] OBJ_id_aca_encAttrs */
+    0x55,0x04,0x48,                                /* [ 2941] OBJ_role */
+    0x55,0x1D,0x24,                                /* [ 2944] OBJ_policy_constraints */
+    0x55,0x1D,0x37,                                /* [ 2947] OBJ_target_information */
+    0x55,0x1D,0x38,                                /* [ 2950] OBJ_no_rev_avail */
+    0x2A,0x86,0x48,0xCE,0x3D,                      /* [ 2953] OBJ_ansi_X9_62 */
+    0x2A,0x86,0x48,0xCE,0x3D,0x01,0x01,            /* [ 2958] OBJ_X9_62_prime_field */
+    0x2A,0x86,0x48,0xCE,0x3D,0x01,0x02,            /* [ 2965] OBJ_X9_62_characteristic_two_field */
+    0x2A,0x86,0x48,0xCE,0x3D,0x02,0x01,            /* [ 2972] OBJ_X9_62_id_ecPublicKey */
+    0x2A,0x86,0x48,0xCE,0x3D,0x03,0x01,0x01,       /* [ 2979] OBJ_X9_62_prime192v1 */
+    0x2A,0x86,0x48,0xCE,0x3D,0x03,0x01,0x02,       /* [ 2987] OBJ_X9_62_prime192v2 */
+    0x2A,0x86,0x48,0xCE,0x3D,0x03,0x01,0x03,       /* [ 2995] OBJ_X9_62_prime192v3 */
+    0x2A,0x86,0x48,0xCE,0x3D,0x03,0x01,0x04,       /* [ 3003] OBJ_X9_62_prime239v1 */
+    0x2A,0x86,0x48,0xCE,0x3D,0x03,0x01,0x05,       /* [ 3011] OBJ_X9_62_prime239v2 */
+    0x2A,0x86,0x48,0xCE,0x3D,0x03,0x01,0x06,       /* [ 3019] OBJ_X9_62_prime239v3 */
+    0x2A,0x86,0x48,0xCE,0x3D,0x03,0x01,0x07,       /* [ 3027] OBJ_X9_62_prime256v1 */
+    0x2A,0x86,0x48,0xCE,0x3D,0x04,0x01,            /* [ 3035] OBJ_ecdsa_with_SHA1 */
+    0x2B,0x06,0x01,0x04,0x01,0x82,0x37,0x11,0x01,  /* [ 3042] OBJ_ms_csp_name */
+    0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x01,0x01,  /* [ 3051] OBJ_aes_128_ecb */
+    0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x01,0x02,  /* [ 3060] OBJ_aes_128_cbc */
+    0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x01,0x03,  /* [ 3069] OBJ_aes_128_ofb128 */
+    0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x01,0x04,  /* [ 3078] OBJ_aes_128_cfb128 */
+    0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x01,0x15,  /* [ 3087] OBJ_aes_192_ecb */
+    0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x01,0x16,  /* [ 3096] OBJ_aes_192_cbc */
+    0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x01,0x17,  /* [ 3105] OBJ_aes_192_ofb128 */
+    0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x01,0x18,  /* [ 3114] OBJ_aes_192_cfb128 */
+    0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x01,0x29,  /* [ 3123] OBJ_aes_256_ecb */
+    0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x01,0x2A,  /* [ 3132] OBJ_aes_256_cbc */
+    0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x01,0x2B,  /* [ 3141] OBJ_aes_256_ofb128 */
+    0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x01,0x2C,  /* [ 3150] OBJ_aes_256_cfb128 */
+    0x55,0x1D,0x17,                                /* [ 3159] OBJ_hold_instruction_code */
+    0x2A,0x86,0x48,0xCE,0x38,0x02,0x01,            /* [ 3162] OBJ_hold_instruction_none */
+    0x2A,0x86,0x48,0xCE,0x38,0x02,0x02,            /* [ 3169] OBJ_hold_instruction_call_issuer */
+    0x2A,0x86,0x48,0xCE,0x38,0x02,0x03,            /* [ 3176] OBJ_hold_instruction_reject */
+    0x09,                                          /* [ 3183] OBJ_data */
+    0x09,0x92,0x26,                                /* [ 3184] OBJ_pss */
+    0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,            /* [ 3187] OBJ_ucl */
+    0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,       /* [ 3194] OBJ_pilot */
+    0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x01,  /* [ 3202] OBJ_pilotAttributeType */
+    0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x03,  /* [ 3211] OBJ_pilotAttributeSyntax */
+    0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x04,  /* [ 3220] OBJ_pilotObjectClass */
+    0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x0A,  /* [ 3229] OBJ_pilotGroups */
+    0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x03,0x04,  /* [ 3238] OBJ_iA5StringSyntax */
+    0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x03,0x05,  /* [ 3248] OBJ_caseIgnoreIA5StringSyntax */
+    0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x04,0x03,  /* [ 3258] OBJ_pilotObject */
+    0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x04,0x04,  /* [ 3268] OBJ_pilotPerson */
+    0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x04,0x05,  /* [ 3278] OBJ_account */
+    0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x04,0x06,  /* [ 3288] OBJ_document */
+    0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x04,0x07,  /* [ 3298] OBJ_room */
+    0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x04,0x09,  /* [ 3308] OBJ_documentSeries */
+    0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x04,0x0E,  /* [ 3318] OBJ_rFC822localPart */
+    0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x04,0x0F,  /* [ 3328] OBJ_dNSDomain */
+    0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x04,0x11,  /* [ 3338] OBJ_domainRelatedObject */
+    0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x04,0x12,  /* [ 3348] OBJ_friendlyCountry */
+    0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x04,0x13,  /* [ 3358] OBJ_simpleSecurityObject */
+    0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x04,0x14,  /* [ 3368] OBJ_pilotOrganization */
+    0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x04,0x15,  /* [ 3378] OBJ_pilotDSA */
+    0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x04,0x16,  /* [ 3388] OBJ_qualityLabelledData */
+    0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x01,0x01,  /* [ 3398] OBJ_userId */
+    0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x01,0x02,  /* [ 3408] OBJ_textEncodedORAddress */
+    0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x01,0x03,  /* [ 3418] OBJ_rfc822Mailbox */
+    0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x01,0x04,  /* [ 3428] OBJ_info */
+    0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x01,0x05,  /* [ 3438] OBJ_favouriteDrink */
+    0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x01,0x06,  /* [ 3448] OBJ_roomNumber */
+    0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x01,0x07,  /* [ 3458] OBJ_photo */
+    0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x01,0x08,  /* [ 3468] OBJ_userClass */
+    0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x01,0x09,  /* [ 3478] OBJ_host */
+    0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x01,0x0A,  /* [ 3488] OBJ_manager */
+    0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x01,0x0B,  /* [ 3498] OBJ_documentIdentifier */
+    0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x01,0x0C,  /* [ 3508] OBJ_documentTitle */
+    0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x01,0x0D,  /* [ 3518] OBJ_documentVersion */
+    0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x01,0x0E,  /* [ 3528] OBJ_documentAuthor */
+    0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x01,0x0F,  /* [ 3538] OBJ_documentLocation */
+    0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x01,0x14,  /* [ 3548] OBJ_homeTelephoneNumber */
+    0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x01,0x15,  /* [ 3558] OBJ_secretary */
+    0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x01,0x16,  /* [ 3568] OBJ_otherMailbox */
+    0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x01,0x17,  /* [ 3578] OBJ_lastModifiedTime */
+    0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x01,0x18,  /* [ 3588] OBJ_lastModifiedBy */
+    0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x01,0x1A,  /* [ 3598] OBJ_aRecord */
+    0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x01,0x1B,  /* [ 3608] OBJ_pilotAttributeType27 */
+    0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x01,0x1C,  /* [ 3618] OBJ_mXRecord */
+    0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x01,0x1D,  /* [ 3628] OBJ_nSRecord */
+    0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x01,0x1E,  /* [ 3638] OBJ_sOARecord */
+    0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x01,0x1F,  /* [ 3648] OBJ_cNAMERecord */
+    0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x01,0x25,  /* [ 3658] OBJ_associatedDomain */
+    0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x01,0x26,  /* [ 3668] OBJ_associatedName */
+    0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x01,0x27,  /* [ 3678] OBJ_homePostalAddress */
+    0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x01,0x28,  /* [ 3688] OBJ_personalTitle */
+    0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x01,0x29,  /* [ 3698] OBJ_mobileTelephoneNumber */
+    0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x01,0x2A,  /* [ 3708] OBJ_pagerTelephoneNumber */
+    0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x01,0x2B,  /* [ 3718] OBJ_friendlyCountryName */
+    0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x01,0x2D,  /* [ 3728] OBJ_organizationalStatus */
+    0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x01,0x2E,  /* [ 3738] OBJ_janetMailbox */
+    0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x01,0x2F,  /* [ 3748] OBJ_mailPreferenceOption */
+    0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x01,0x30,  /* [ 3758] OBJ_buildingName */
+    0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x01,0x31,  /* [ 3768] OBJ_dSAQuality */
+    0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x01,0x32,  /* [ 3778] OBJ_singleLevelQuality */
+    0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x01,0x33,  /* [ 3788] OBJ_subtreeMinimumQuality */
+    0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x01,0x34,  /* [ 3798] OBJ_subtreeMaximumQuality */
+    0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x01,0x35,  /* [ 3808] OBJ_personalSignature */
+    0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x01,0x36,  /* [ 3818] OBJ_dITRedirect */
+    0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x01,0x37,  /* [ 3828] OBJ_audio */
+    0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x01,0x38,  /* [ 3838] OBJ_documentPublisher */
+    0x55,0x04,0x2D,                                /* [ 3848] OBJ_x500UniqueIdentifier */
+    0x2B,0x06,0x01,0x07,0x01,                      /* [ 3851] OBJ_mime_mhs */
+    0x2B,0x06,0x01,0x07,0x01,0x01,                 /* [ 3856] OBJ_mime_mhs_headings */
+    0x2B,0x06,0x01,0x07,0x01,0x02,                 /* [ 3862] OBJ_mime_mhs_bodies */
+    0x2B,0x06,0x01,0x07,0x01,0x01,0x01,            /* [ 3868] OBJ_id_hex_partial_message */
+    0x2B,0x06,0x01,0x07,0x01,0x01,0x02,            /* [ 3875] OBJ_id_hex_multipart_message */
+    0x55,0x04,0x2C,                                /* [ 3882] OBJ_generationQualifier */
+    0x55,0x04,0x41,                                /* [ 3885] OBJ_pseudonym */
+    0x67,0x2A,                                     /* [ 3888] OBJ_id_set */
+    0x67,0x2A,0x00,                                /* [ 3890] OBJ_set_ctype */
+    0x67,0x2A,0x01,                                /* [ 3893] OBJ_set_msgExt */
+    0x67,0x2A,0x03,                                /* [ 3896] OBJ_set_attr */
+    0x67,0x2A,0x05,                                /* [ 3899] OBJ_set_policy */
+    0x67,0x2A,0x07,                                /* [ 3902] OBJ_set_certExt */
+    0x67,0x2A,0x08,                                /* [ 3905] OBJ_set_brand */
+    0x67,0x2A,0x00,0x00,                           /* [ 3908] OBJ_setct_PANData */
+    0x67,0x2A,0x00,0x01,                           /* [ 3912] OBJ_setct_PANToken */
+    0x67,0x2A,0x00,0x02,                           /* [ 3916] OBJ_setct_PANOnly */
+    0x67,0x2A,0x00,0x03,                           /* [ 3920] OBJ_setct_OIData */
+    0x67,0x2A,0x00,0x04,                           /* [ 3924] OBJ_setct_PI */
+    0x67,0x2A,0x00,0x05,                           /* [ 3928] OBJ_setct_PIData */
+    0x67,0x2A,0x00,0x06,                           /* [ 3932] OBJ_setct_PIDataUnsigned */
+    0x67,0x2A,0x00,0x07,                           /* [ 3936] OBJ_setct_HODInput */
+    0x67,0x2A,0x00,0x08,                           /* [ 3940] OBJ_setct_AuthResBaggage */
+    0x67,0x2A,0x00,0x09,                           /* [ 3944] OBJ_setct_AuthRevReqBaggage */
+    0x67,0x2A,0x00,0x0A,                           /* [ 3948] OBJ_setct_AuthRevResBaggage */
+    0x67,0x2A,0x00,0x0B,                           /* [ 3952] OBJ_setct_CapTokenSeq */
+    0x67,0x2A,0x00,0x0C,                           /* [ 3956] OBJ_setct_PInitResData */
+    0x67,0x2A,0x00,0x0D,                           /* [ 3960] OBJ_setct_PI_TBS */
+    0x67,0x2A,0x00,0x0E,                           /* [ 3964] OBJ_setct_PResData */
+    0x67,0x2A,0x00,0x10,                           /* [ 3968] OBJ_setct_AuthReqTBS */
+    0x67,0x2A,0x00,0x11,                           /* [ 3972] OBJ_setct_AuthResTBS */
+    0x67,0x2A,0x00,0x12,                           /* [ 3976] OBJ_setct_AuthResTBSX */
+    0x67,0x2A,0x00,0x13,                           /* [ 3980] OBJ_setct_AuthTokenTBS */
+    0x67,0x2A,0x00,0x14,                           /* [ 3984] OBJ_setct_CapTokenData */
+    0x67,0x2A,0x00,0x15,                           /* [ 3988] OBJ_setct_CapTokenTBS */
+    0x67,0x2A,0x00,0x16,                           /* [ 3992] OBJ_setct_AcqCardCodeMsg */
+    0x67,0x2A,0x00,0x17,                           /* [ 3996] OBJ_setct_AuthRevReqTBS */
+    0x67,0x2A,0x00,0x18,                           /* [ 4000] OBJ_setct_AuthRevResData */
+    0x67,0x2A,0x00,0x19,                           /* [ 4004] OBJ_setct_AuthRevResTBS */
+    0x67,0x2A,0x00,0x1A,                           /* [ 4008] OBJ_setct_CapReqTBS */
+    0x67,0x2A,0x00,0x1B,                           /* [ 4012] OBJ_setct_CapReqTBSX */
+    0x67,0x2A,0x00,0x1C,                           /* [ 4016] OBJ_setct_CapResData */
+    0x67,0x2A,0x00,0x1D,                           /* [ 4020] OBJ_setct_CapRevReqTBS */
+    0x67,0x2A,0x00,0x1E,                           /* [ 4024] OBJ_setct_CapRevReqTBSX */
+    0x67,0x2A,0x00,0x1F,                           /* [ 4028] OBJ_setct_CapRevResData */
+    0x67,0x2A,0x00,0x20,                           /* [ 4032] OBJ_setct_CredReqTBS */
+    0x67,0x2A,0x00,0x21,                           /* [ 4036] OBJ_setct_CredReqTBSX */
+    0x67,0x2A,0x00,0x22,                           /* [ 4040] OBJ_setct_CredResData */
+    0x67,0x2A,0x00,0x23,                           /* [ 4044] OBJ_setct_CredRevReqTBS */
+    0x67,0x2A,0x00,0x24,                           /* [ 4048] OBJ_setct_CredRevReqTBSX */
+    0x67,0x2A,0x00,0x25,                           /* [ 4052] OBJ_setct_CredRevResData */
+    0x67,0x2A,0x00,0x26,                           /* [ 4056] OBJ_setct_PCertReqData */
+    0x67,0x2A,0x00,0x27,                           /* [ 4060] OBJ_setct_PCertResTBS */
+    0x67,0x2A,0x00,0x28,                           /* [ 4064] OBJ_setct_BatchAdminReqData */
+    0x67,0x2A,0x00,0x29,                           /* [ 4068] OBJ_setct_BatchAdminResData */
+    0x67,0x2A,0x00,0x2A,                           /* [ 4072] OBJ_setct_CardCInitResTBS */
+    0x67,0x2A,0x00,0x2B,                           /* [ 4076] OBJ_setct_MeAqCInitResTBS */
+    0x67,0x2A,0x00,0x2C,                           /* [ 4080] OBJ_setct_RegFormResTBS */
+    0x67,0x2A,0x00,0x2D,                           /* [ 4084] OBJ_setct_CertReqData */
+    0x67,0x2A,0x00,0x2E,                           /* [ 4088] OBJ_setct_CertReqTBS */
+    0x67,0x2A,0x00,0x2F,                           /* [ 4092] OBJ_setct_CertResData */
+    0x67,0x2A,0x00,0x30,                           /* [ 4096] OBJ_setct_CertInqReqTBS */
+    0x67,0x2A,0x00,0x31,                           /* [ 4100] OBJ_setct_ErrorTBS */
+    0x67,0x2A,0x00,0x32,                           /* [ 4104] OBJ_setct_PIDualSignedTBE */
+    0x67,0x2A,0x00,0x33,                           /* [ 4108] OBJ_setct_PIUnsignedTBE */
+    0x67,0x2A,0x00,0x34,                           /* [ 4112] OBJ_setct_AuthReqTBE */
+    0x67,0x2A,0x00,0x35,                           /* [ 4116] OBJ_setct_AuthResTBE */
+    0x67,0x2A,0x00,0x36,                           /* [ 4120] OBJ_setct_AuthResTBEX */
+    0x67,0x2A,0x00,0x37,                           /* [ 4124] OBJ_setct_AuthTokenTBE */
+    0x67,0x2A,0x00,0x38,                           /* [ 4128] OBJ_setct_CapTokenTBE */
+    0x67,0x2A,0x00,0x39,                           /* [ 4132] OBJ_setct_CapTokenTBEX */
+    0x67,0x2A,0x00,0x3A,                           /* [ 4136] OBJ_setct_AcqCardCodeMsgTBE */
+    0x67,0x2A,0x00,0x3B,                           /* [ 4140] OBJ_setct_AuthRevReqTBE */
+    0x67,0x2A,0x00,0x3C,                           /* [ 4144] OBJ_setct_AuthRevResTBE */
+    0x67,0x2A,0x00,0x3D,                           /* [ 4148] OBJ_setct_AuthRevResTBEB */
+    0x67,0x2A,0x00,0x3E,                           /* [ 4152] OBJ_setct_CapReqTBE */
+    0x67,0x2A,0x00,0x3F,                           /* [ 4156] OBJ_setct_CapReqTBEX */
+    0x67,0x2A,0x00,0x40,                           /* [ 4160] OBJ_setct_CapResTBE */
+    0x67,0x2A,0x00,0x41,                           /* [ 4164] OBJ_setct_CapRevReqTBE */
+    0x67,0x2A,0x00,0x42,                           /* [ 4168] OBJ_setct_CapRevReqTBEX */
+    0x67,0x2A,0x00,0x43,                           /* [ 4172] OBJ_setct_CapRevResTBE */
+    0x67,0x2A,0x00,0x44,                           /* [ 4176] OBJ_setct_CredReqTBE */
+    0x67,0x2A,0x00,0x45,                           /* [ 4180] OBJ_setct_CredReqTBEX */
+    0x67,0x2A,0x00,0x46,                           /* [ 4184] OBJ_setct_CredResTBE */
+    0x67,0x2A,0x00,0x47,                           /* [ 4188] OBJ_setct_CredRevReqTBE */
+    0x67,0x2A,0x00,0x48,                           /* [ 4192] OBJ_setct_CredRevReqTBEX */
+    0x67,0x2A,0x00,0x49,                           /* [ 4196] OBJ_setct_CredRevResTBE */
+    0x67,0x2A,0x00,0x4A,                           /* [ 4200] OBJ_setct_BatchAdminReqTBE */
+    0x67,0x2A,0x00,0x4B,                           /* [ 4204] OBJ_setct_BatchAdminResTBE */
+    0x67,0x2A,0x00,0x4C,                           /* [ 4208] OBJ_setct_RegFormReqTBE */
+    0x67,0x2A,0x00,0x4D,                           /* [ 4212] OBJ_setct_CertReqTBE */
+    0x67,0x2A,0x00,0x4E,                           /* [ 4216] OBJ_setct_CertReqTBEX */
+    0x67,0x2A,0x00,0x4F,                           /* [ 4220] OBJ_setct_CertResTBE */
+    0x67,0x2A,0x00,0x50,                           /* [ 4224] OBJ_setct_CRLNotificationTBS */
+    0x67,0x2A,0x00,0x51,                           /* [ 4228] OBJ_setct_CRLNotificationResTBS */
+    0x67,0x2A,0x00,0x52,                           /* [ 4232] OBJ_setct_BCIDistributionTBS */
+    0x67,0x2A,0x01,0x01,                           /* [ 4236] OBJ_setext_genCrypt */
+    0x67,0x2A,0x01,0x03,                           /* [ 4240] OBJ_setext_miAuth */
+    0x67,0x2A,0x01,0x04,                           /* [ 4244] OBJ_setext_pinSecure */
+    0x67,0x2A,0x01,0x05,                           /* [ 4248] OBJ_setext_pinAny */
+    0x67,0x2A,0x01,0x07,                           /* [ 4252] OBJ_setext_track2 */
+    0x67,0x2A,0x01,0x08,                           /* [ 4256] OBJ_setext_cv */
+    0x67,0x2A,0x05,0x00,                           /* [ 4260] OBJ_set_policy_root */
+    0x67,0x2A,0x07,0x00,                           /* [ 4264] OBJ_setCext_hashedRoot */
+    0x67,0x2A,0x07,0x01,                           /* [ 4268] OBJ_setCext_certType */
+    0x67,0x2A,0x07,0x02,                           /* [ 4272] OBJ_setCext_merchData */
+    0x67,0x2A,0x07,0x03,                           /* [ 4276] OBJ_setCext_cCertRequired */
+    0x67,0x2A,0x07,0x04,                           /* [ 4280] OBJ_setCext_tunneling */
+    0x67,0x2A,0x07,0x05,                           /* [ 4284] OBJ_setCext_setExt */
+    0x67,0x2A,0x07,0x06,                           /* [ 4288] OBJ_setCext_setQualf */
+    0x67,0x2A,0x07,0x07,                           /* [ 4292] OBJ_setCext_PGWYcapabilities */
+    0x67,0x2A,0x07,0x08,                           /* [ 4296] OBJ_setCext_TokenIdentifier */
+    0x67,0x2A,0x07,0x09,                           /* [ 4300] OBJ_setCext_Track2Data */
+    0x67,0x2A,0x07,0x0A,                           /* [ 4304] OBJ_setCext_TokenType */
+    0x67,0x2A,0x07,0x0B,                           /* [ 4308] OBJ_setCext_IssuerCapabilities */
+    0x67,0x2A,0x03,0x00,                           /* [ 4312] OBJ_setAttr_Cert */
+    0x67,0x2A,0x03,0x01,                           /* [ 4316] OBJ_setAttr_PGWYcap */
+    0x67,0x2A,0x03,0x02,                           /* [ 4320] OBJ_setAttr_TokenType */
+    0x67,0x2A,0x03,0x03,                           /* [ 4324] OBJ_setAttr_IssCap */
+    0x67,0x2A,0x03,0x00,0x00,                      /* [ 4328] OBJ_set_rootKeyThumb */
+    0x67,0x2A,0x03,0x00,0x01,                      /* [ 4333] OBJ_set_addPolicy */
+    0x67,0x2A,0x03,0x02,0x01,                      /* [ 4338] OBJ_setAttr_Token_EMV */
+    0x67,0x2A,0x03,0x02,0x02,                      /* [ 4343] OBJ_setAttr_Token_B0Prime */
+    0x67,0x2A,0x03,0x03,0x03,                      /* [ 4348] OBJ_setAttr_IssCap_CVM */
+    0x67,0x2A,0x03,0x03,0x04,                      /* [ 4353] OBJ_setAttr_IssCap_T2 */
+    0x67,0x2A,0x03,0x03,0x05,                      /* [ 4358] OBJ_setAttr_IssCap_Sig */
+    0x67,0x2A,0x03,0x03,0x03,0x01,                 /* [ 4363] OBJ_setAttr_GenCryptgrm */
+    0x67,0x2A,0x03,0x03,0x04,0x01,                 /* [ 4369] OBJ_setAttr_T2Enc */
+    0x67,0x2A,0x03,0x03,0x04,0x02,                 /* [ 4375] OBJ_setAttr_T2cleartxt */
+    0x67,0x2A,0x03,0x03,0x05,0x01,                 /* [ 4381] OBJ_setAttr_TokICCsig */
+    0x67,0x2A,0x03,0x03,0x05,0x02,                 /* [ 4387] OBJ_setAttr_SecDevSig */
+    0x67,0x2A,0x08,0x01,                           /* [ 4393] OBJ_set_brand_IATA_ATA */
+    0x67,0x2A,0x08,0x1E,                           /* [ 4397] OBJ_set_brand_Diners */
+    0x67,0x2A,0x08,0x22,                           /* [ 4401] OBJ_set_brand_AmericanExpress */
+    0x67,0x2A,0x08,0x23,                           /* [ 4405] OBJ_set_brand_JCB */
+    0x67,0x2A,0x08,0x04,                           /* [ 4409] OBJ_set_brand_Visa */
+    0x67,0x2A,0x08,0x05,                           /* [ 4413] OBJ_set_brand_MasterCard */
+    0x67,0x2A,0x08,0xAE,0x7B,                      /* [ 4417] OBJ_set_brand_Novus */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x03,0x0A,       /* [ 4422] OBJ_des_cdmf */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x06,  /* [ 4430] OBJ_rsaOAEPEncryptionSET */
+    0x67,                                          /* [ 4439] OBJ_international_organizations */
+    0x2B,0x06,0x01,0x04,0x01,0x82,0x37,0x14,0x02,0x02,  /* [ 4440] OBJ_ms_smartcard_login */
+    0x2B,0x06,0x01,0x04,0x01,0x82,0x37,0x14,0x02,0x03,  /* [ 4450] OBJ_ms_upn */
+    0x55,0x04,0x09,                                /* [ 4460] OBJ_streetAddress */
+    0x55,0x04,0x11,                                /* [ 4463] OBJ_postalCode */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x15,            /* [ 4466] OBJ_id_ppl */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x01,0x0E,       /* [ 4473] OBJ_proxyCertInfo */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x15,0x00,       /* [ 4481] OBJ_id_ppl_anyLanguage */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x15,0x01,       /* [ 4489] OBJ_id_ppl_inheritAll */
+    0x55,0x1D,0x1E,                                /* [ 4497] OBJ_name_constraints */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x15,0x02,       /* [ 4500] OBJ_Independent */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x0B,  /* [ 4508] OBJ_sha256WithRSAEncryption */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x0C,  /* [ 4517] OBJ_sha384WithRSAEncryption */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x0D,  /* [ 4526] OBJ_sha512WithRSAEncryption */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x0E,  /* [ 4535] OBJ_sha224WithRSAEncryption */
+    0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x02,0x01,  /* [ 4544] OBJ_sha256 */
+    0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x02,0x02,  /* [ 4553] OBJ_sha384 */
+    0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x02,0x03,  /* [ 4562] OBJ_sha512 */
+    0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x02,0x04,  /* [ 4571] OBJ_sha224 */
+    0x2B,                                          /* [ 4580] OBJ_identified_organization */
+    0x2B,0x81,0x04,                                /* [ 4581] OBJ_certicom_arc */
+    0x67,0x2B,                                     /* [ 4584] OBJ_wap */
+    0x67,0x2B,0x01,                                /* [ 4586] OBJ_wap_wsg */
+    0x2A,0x86,0x48,0xCE,0x3D,0x01,0x02,0x03,       /* [ 4589] OBJ_X9_62_id_characteristic_two_basis */
+    0x2A,0x86,0x48,0xCE,0x3D,0x01,0x02,0x03,0x01,  /* [ 4597] OBJ_X9_62_onBasis */
+    0x2A,0x86,0x48,0xCE,0x3D,0x01,0x02,0x03,0x02,  /* [ 4606] OBJ_X9_62_tpBasis */
+    0x2A,0x86,0x48,0xCE,0x3D,0x01,0x02,0x03,0x03,  /* [ 4615] OBJ_X9_62_ppBasis */
+    0x2A,0x86,0x48,0xCE,0x3D,0x03,0x00,0x01,       /* [ 4624] OBJ_X9_62_c2pnb163v1 */
+    0x2A,0x86,0x48,0xCE,0x3D,0x03,0x00,0x02,       /* [ 4632] OBJ_X9_62_c2pnb163v2 */
+    0x2A,0x86,0x48,0xCE,0x3D,0x03,0x00,0x03,       /* [ 4640] OBJ_X9_62_c2pnb163v3 */
+    0x2A,0x86,0x48,0xCE,0x3D,0x03,0x00,0x04,       /* [ 4648] OBJ_X9_62_c2pnb176v1 */
+    0x2A,0x86,0x48,0xCE,0x3D,0x03,0x00,0x05,       /* [ 4656] OBJ_X9_62_c2tnb191v1 */
+    0x2A,0x86,0x48,0xCE,0x3D,0x03,0x00,0x06,       /* [ 4664] OBJ_X9_62_c2tnb191v2 */
+    0x2A,0x86,0x48,0xCE,0x3D,0x03,0x00,0x07,       /* [ 4672] OBJ_X9_62_c2tnb191v3 */
+    0x2A,0x86,0x48,0xCE,0x3D,0x03,0x00,0x08,       /* [ 4680] OBJ_X9_62_c2onb191v4 */
+    0x2A,0x86,0x48,0xCE,0x3D,0x03,0x00,0x09,       /* [ 4688] OBJ_X9_62_c2onb191v5 */
+    0x2A,0x86,0x48,0xCE,0x3D,0x03,0x00,0x0A,       /* [ 4696] OBJ_X9_62_c2pnb208w1 */
+    0x2A,0x86,0x48,0xCE,0x3D,0x03,0x00,0x0B,       /* [ 4704] OBJ_X9_62_c2tnb239v1 */
+    0x2A,0x86,0x48,0xCE,0x3D,0x03,0x00,0x0C,       /* [ 4712] OBJ_X9_62_c2tnb239v2 */
+    0x2A,0x86,0x48,0xCE,0x3D,0x03,0x00,0x0D,       /* [ 4720] OBJ_X9_62_c2tnb239v3 */
+    0x2A,0x86,0x48,0xCE,0x3D,0x03,0x00,0x0E,       /* [ 4728] OBJ_X9_62_c2onb239v4 */
+    0x2A,0x86,0x48,0xCE,0x3D,0x03,0x00,0x0F,       /* [ 4736] OBJ_X9_62_c2onb239v5 */
+    0x2A,0x86,0x48,0xCE,0x3D,0x03,0x00,0x10,       /* [ 4744] OBJ_X9_62_c2pnb272w1 */
+    0x2A,0x86,0x48,0xCE,0x3D,0x03,0x00,0x11,       /* [ 4752] OBJ_X9_62_c2pnb304w1 */
+    0x2A,0x86,0x48,0xCE,0x3D,0x03,0x00,0x12,       /* [ 4760] OBJ_X9_62_c2tnb359v1 */
+    0x2A,0x86,0x48,0xCE,0x3D,0x03,0x00,0x13,       /* [ 4768] OBJ_X9_62_c2pnb368w1 */
+    0x2A,0x86,0x48,0xCE,0x3D,0x03,0x00,0x14,       /* [ 4776] OBJ_X9_62_c2tnb431r1 */
+    0x2B,0x81,0x04,0x00,0x06,                      /* [ 4784] OBJ_secp112r1 */
+    0x2B,0x81,0x04,0x00,0x07,                      /* [ 4789] OBJ_secp112r2 */
+    0x2B,0x81,0x04,0x00,0x1C,                      /* [ 4794] OBJ_secp128r1 */
+    0x2B,0x81,0x04,0x00,0x1D,                      /* [ 4799] OBJ_secp128r2 */
+    0x2B,0x81,0x04,0x00,0x09,                      /* [ 4804] OBJ_secp160k1 */
+    0x2B,0x81,0x04,0x00,0x08,                      /* [ 4809] OBJ_secp160r1 */
+    0x2B,0x81,0x04,0x00,0x1E,                      /* [ 4814] OBJ_secp160r2 */
+    0x2B,0x81,0x04,0x00,0x1F,                      /* [ 4819] OBJ_secp192k1 */
+    0x2B,0x81,0x04,0x00,0x20,                      /* [ 4824] OBJ_secp224k1 */
+    0x2B,0x81,0x04,0x00,0x21,                      /* [ 4829] OBJ_secp224r1 */
+    0x2B,0x81,0x04,0x00,0x0A,                      /* [ 4834] OBJ_secp256k1 */
+    0x2B,0x81,0x04,0x00,0x22,                      /* [ 4839] OBJ_secp384r1 */
+    0x2B,0x81,0x04,0x00,0x23,                      /* [ 4844] OBJ_secp521r1 */
+    0x2B,0x81,0x04,0x00,0x04,                      /* [ 4849] OBJ_sect113r1 */
+    0x2B,0x81,0x04,0x00,0x05,                      /* [ 4854] OBJ_sect113r2 */
+    0x2B,0x81,0x04,0x00,0x16,                      /* [ 4859] OBJ_sect131r1 */
+    0x2B,0x81,0x04,0x00,0x17,                      /* [ 4864] OBJ_sect131r2 */
+    0x2B,0x81,0x04,0x00,0x01,                      /* [ 4869] OBJ_sect163k1 */
+    0x2B,0x81,0x04,0x00,0x02,                      /* [ 4874] OBJ_sect163r1 */
+    0x2B,0x81,0x04,0x00,0x0F,                      /* [ 4879] OBJ_sect163r2 */
+    0x2B,0x81,0x04,0x00,0x18,                      /* [ 4884] OBJ_sect193r1 */
+    0x2B,0x81,0x04,0x00,0x19,                      /* [ 4889] OBJ_sect193r2 */
+    0x2B,0x81,0x04,0x00,0x1A,                      /* [ 4894] OBJ_sect233k1 */
+    0x2B,0x81,0x04,0x00,0x1B,                      /* [ 4899] OBJ_sect233r1 */
+    0x2B,0x81,0x04,0x00,0x03,                      /* [ 4904] OBJ_sect239k1 */
+    0x2B,0x81,0x04,0x00,0x10,                      /* [ 4909] OBJ_sect283k1 */
+    0x2B,0x81,0x04,0x00,0x11,                      /* [ 4914] OBJ_sect283r1 */
+    0x2B,0x81,0x04,0x00,0x24,                      /* [ 4919] OBJ_sect409k1 */
+    0x2B,0x81,0x04,0x00,0x25,                      /* [ 4924] OBJ_sect409r1 */
+    0x2B,0x81,0x04,0x00,0x26,                      /* [ 4929] OBJ_sect571k1 */
+    0x2B,0x81,0x04,0x00,0x27,                      /* [ 4934] OBJ_sect571r1 */
+    0x67,0x2B,0x01,0x04,0x01,                      /* [ 4939] OBJ_wap_wsg_idm_ecid_wtls1 */
+    0x67,0x2B,0x01,0x04,0x03,                      /* [ 4944] OBJ_wap_wsg_idm_ecid_wtls3 */
+    0x67,0x2B,0x01,0x04,0x04,                      /* [ 4949] OBJ_wap_wsg_idm_ecid_wtls4 */
+    0x67,0x2B,0x01,0x04,0x05,                      /* [ 4954] OBJ_wap_wsg_idm_ecid_wtls5 */
+    0x67,0x2B,0x01,0x04,0x06,                      /* [ 4959] OBJ_wap_wsg_idm_ecid_wtls6 */
+    0x67,0x2B,0x01,0x04,0x07,                      /* [ 4964] OBJ_wap_wsg_idm_ecid_wtls7 */
+    0x67,0x2B,0x01,0x04,0x08,                      /* [ 4969] OBJ_wap_wsg_idm_ecid_wtls8 */
+    0x67,0x2B,0x01,0x04,0x09,                      /* [ 4974] OBJ_wap_wsg_idm_ecid_wtls9 */
+    0x67,0x2B,0x01,0x04,0x0A,                      /* [ 4979] OBJ_wap_wsg_idm_ecid_wtls10 */
+    0x67,0x2B,0x01,0x04,0x0B,                      /* [ 4984] OBJ_wap_wsg_idm_ecid_wtls11 */
+    0x67,0x2B,0x01,0x04,0x0C,                      /* [ 4989] OBJ_wap_wsg_idm_ecid_wtls12 */
+    0x55,0x1D,0x20,0x00,                           /* [ 4994] OBJ_any_policy */
+    0x55,0x1D,0x21,                                /* [ 4998] OBJ_policy_mappings */
+    0x55,0x1D,0x36,                                /* [ 5001] OBJ_inhibit_any_policy */
+    0x2A,0x83,0x08,0x8C,0x9A,0x4B,0x3D,0x01,0x01,0x01,0x02,  /* [ 5004] OBJ_camellia_128_cbc */
+    0x2A,0x83,0x08,0x8C,0x9A,0x4B,0x3D,0x01,0x01,0x01,0x03,  /* [ 5015] OBJ_camellia_192_cbc */
+    0x2A,0x83,0x08,0x8C,0x9A,0x4B,0x3D,0x01,0x01,0x01,0x04,  /* [ 5026] OBJ_camellia_256_cbc */
+    0x03,0xA2,0x31,0x05,0x03,0x01,0x09,0x01,       /* [ 5037] OBJ_camellia_128_ecb */
+    0x03,0xA2,0x31,0x05,0x03,0x01,0x09,0x15,       /* [ 5045] OBJ_camellia_192_ecb */
+    0x03,0xA2,0x31,0x05,0x03,0x01,0x09,0x29,       /* [ 5053] OBJ_camellia_256_ecb */
+    0x03,0xA2,0x31,0x05,0x03,0x01,0x09,0x04,       /* [ 5061] OBJ_camellia_128_cfb128 */
+    0x03,0xA2,0x31,0x05,0x03,0x01,0x09,0x18,       /* [ 5069] OBJ_camellia_192_cfb128 */
+    0x03,0xA2,0x31,0x05,0x03,0x01,0x09,0x2C,       /* [ 5077] OBJ_camellia_256_cfb128 */
+    0x03,0xA2,0x31,0x05,0x03,0x01,0x09,0x03,       /* [ 5085] OBJ_camellia_128_ofb128 */
+    0x03,0xA2,0x31,0x05,0x03,0x01,0x09,0x17,       /* [ 5093] OBJ_camellia_192_ofb128 */
+    0x03,0xA2,0x31,0x05,0x03,0x01,0x09,0x2B,       /* [ 5101] OBJ_camellia_256_ofb128 */
+    0x55,0x1D,0x09,                                /* [ 5109] OBJ_subject_directory_attributes */
+    0x55,0x1D,0x1C,                                /* [ 5112] OBJ_issuing_distribution_point */
+    0x55,0x1D,0x1D,                                /* [ 5115] OBJ_certificate_issuer */
+    0x2A,0x83,0x1A,0x8C,0x9A,0x44,                 /* [ 5118] OBJ_kisa */
+    0x2A,0x83,0x1A,0x8C,0x9A,0x44,0x01,0x03,       /* [ 5124] OBJ_seed_ecb */
+    0x2A,0x83,0x1A,0x8C,0x9A,0x44,0x01,0x04,       /* [ 5132] OBJ_seed_cbc */
+    0x2A,0x83,0x1A,0x8C,0x9A,0x44,0x01,0x06,       /* [ 5140] OBJ_seed_ofb128 */
+    0x2A,0x83,0x1A,0x8C,0x9A,0x44,0x01,0x05,       /* [ 5148] OBJ_seed_cfb128 */
+    0x2B,0x06,0x01,0x05,0x05,0x08,0x01,0x01,       /* [ 5156] OBJ_hmac_md5 */
+    0x2B,0x06,0x01,0x05,0x05,0x08,0x01,0x02,       /* [ 5164] OBJ_hmac_sha1 */
+    0x2A,0x86,0x48,0x86,0xF6,0x7D,0x07,0x42,0x0D,  /* [ 5172] OBJ_id_PasswordBasedMAC */
+    0x2A,0x86,0x48,0x86,0xF6,0x7D,0x07,0x42,0x1E,  /* [ 5181] OBJ_id_DHBasedMac */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x04,0x10,       /* [ 5190] OBJ_id_it_suppLangTags */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x30,0x05,       /* [ 5198] OBJ_caRepository */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x01,0x09,  /* [ 5206] OBJ_id_smime_ct_compressedData */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x01,0x1B,  /* [ 5217] OBJ_id_ct_asciiTextWithCRLF */
+    0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x01,0x05,  /* [ 5228] OBJ_id_aes128_wrap */
+    0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x01,0x19,  /* [ 5237] OBJ_id_aes192_wrap */
+    0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x01,0x2D,  /* [ 5246] OBJ_id_aes256_wrap */
+    0x2A,0x86,0x48,0xCE,0x3D,0x04,0x02,            /* [ 5255] OBJ_ecdsa_with_Recommended */
+    0x2A,0x86,0x48,0xCE,0x3D,0x04,0x03,            /* [ 5262] OBJ_ecdsa_with_Specified */
+    0x2A,0x86,0x48,0xCE,0x3D,0x04,0x03,0x01,       /* [ 5269] OBJ_ecdsa_with_SHA224 */
+    0x2A,0x86,0x48,0xCE,0x3D,0x04,0x03,0x02,       /* [ 5277] OBJ_ecdsa_with_SHA256 */
+    0x2A,0x86,0x48,0xCE,0x3D,0x04,0x03,0x03,       /* [ 5285] OBJ_ecdsa_with_SHA384 */
+    0x2A,0x86,0x48,0xCE,0x3D,0x04,0x03,0x04,       /* [ 5293] OBJ_ecdsa_with_SHA512 */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x02,0x06,       /* [ 5301] OBJ_hmacWithMD5 */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x02,0x08,       /* [ 5309] OBJ_hmacWithSHA224 */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x02,0x09,       /* [ 5317] OBJ_hmacWithSHA256 */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x02,0x0A,       /* [ 5325] OBJ_hmacWithSHA384 */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x02,0x0B,       /* [ 5333] OBJ_hmacWithSHA512 */
+    0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x03,0x01,  /* [ 5341] OBJ_dsa_with_SHA224 */
+    0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x03,0x02,  /* [ 5350] OBJ_dsa_with_SHA256 */
+    0x28,0xCF,0x06,0x03,0x00,0x37,                 /* [ 5359] OBJ_whirlpool */
+    0x2A,0x85,0x03,0x02,0x02,                      /* [ 5365] OBJ_cryptopro */
+    0x2A,0x85,0x03,0x02,0x09,                      /* [ 5370] OBJ_cryptocom */
+    0x2A,0x85,0x03,0x02,0x02,0x03,                 /* [ 5375] OBJ_id_GostR3411_94_with_GostR3410_2001 */
+    0x2A,0x85,0x03,0x02,0x02,0x04,                 /* [ 5381] OBJ_id_GostR3411_94_with_GostR3410_94 */
+    0x2A,0x85,0x03,0x02,0x02,0x09,                 /* [ 5387] OBJ_id_GostR3411_94 */
+    0x2A,0x85,0x03,0x02,0x02,0x0A,                 /* [ 5393] OBJ_id_HMACGostR3411_94 */
+    0x2A,0x85,0x03,0x02,0x02,0x13,                 /* [ 5399] OBJ_id_GostR3410_2001 */
+    0x2A,0x85,0x03,0x02,0x02,0x14,                 /* [ 5405] OBJ_id_GostR3410_94 */
+    0x2A,0x85,0x03,0x02,0x02,0x15,                 /* [ 5411] OBJ_id_Gost28147_89 */
+    0x2A,0x85,0x03,0x02,0x02,0x16,                 /* [ 5417] OBJ_id_Gost28147_89_MAC */
+    0x2A,0x85,0x03,0x02,0x02,0x17,                 /* [ 5423] OBJ_id_GostR3411_94_prf */
+    0x2A,0x85,0x03,0x02,0x02,0x62,                 /* [ 5429] OBJ_id_GostR3410_2001DH */
+    0x2A,0x85,0x03,0x02,0x02,0x63,                 /* [ 5435] OBJ_id_GostR3410_94DH */
+    0x2A,0x85,0x03,0x02,0x02,0x0E,0x01,            /* [ 5441] OBJ_id_Gost28147_89_CryptoPro_KeyMeshing */
+    0x2A,0x85,0x03,0x02,0x02,0x0E,0x00,            /* [ 5448] OBJ_id_Gost28147_89_None_KeyMeshing */
+    0x2A,0x85,0x03,0x02,0x02,0x1E,0x00,            /* [ 5455] OBJ_id_GostR3411_94_TestParamSet */
+    0x2A,0x85,0x03,0x02,0x02,0x1E,0x01,            /* [ 5462] OBJ_id_GostR3411_94_CryptoProParamSet */
+    0x2A,0x85,0x03,0x02,0x02,0x1F,0x00,            /* [ 5469] OBJ_id_Gost28147_89_TestParamSet */
+    0x2A,0x85,0x03,0x02,0x02,0x1F,0x01,            /* [ 5476] OBJ_id_Gost28147_89_CryptoPro_A_ParamSet */
+    0x2A,0x85,0x03,0x02,0x02,0x1F,0x02,            /* [ 5483] OBJ_id_Gost28147_89_CryptoPro_B_ParamSet */
+    0x2A,0x85,0x03,0x02,0x02,0x1F,0x03,            /* [ 5490] OBJ_id_Gost28147_89_CryptoPro_C_ParamSet */
+    0x2A,0x85,0x03,0x02,0x02,0x1F,0x04,            /* [ 5497] OBJ_id_Gost28147_89_CryptoPro_D_ParamSet */
+    0x2A,0x85,0x03,0x02,0x02,0x1F,0x05,            /* [ 5504] OBJ_id_Gost28147_89_CryptoPro_Oscar_1_1_ParamSet */
+    0x2A,0x85,0x03,0x02,0x02,0x1F,0x06,            /* [ 5511] OBJ_id_Gost28147_89_CryptoPro_Oscar_1_0_ParamSet */
+    0x2A,0x85,0x03,0x02,0x02,0x1F,0x07,            /* [ 5518] OBJ_id_Gost28147_89_CryptoPro_RIC_1_ParamSet */
+    0x2A,0x85,0x03,0x02,0x02,0x20,0x00,            /* [ 5525] OBJ_id_GostR3410_94_TestParamSet */
+    0x2A,0x85,0x03,0x02,0x02,0x20,0x02,            /* [ 5532] OBJ_id_GostR3410_94_CryptoPro_A_ParamSet */
+    0x2A,0x85,0x03,0x02,0x02,0x20,0x03,            /* [ 5539] OBJ_id_GostR3410_94_CryptoPro_B_ParamSet */
+    0x2A,0x85,0x03,0x02,0x02,0x20,0x04,            /* [ 5546] OBJ_id_GostR3410_94_CryptoPro_C_ParamSet */
+    0x2A,0x85,0x03,0x02,0x02,0x20,0x05,            /* [ 5553] OBJ_id_GostR3410_94_CryptoPro_D_ParamSet */
+    0x2A,0x85,0x03,0x02,0x02,0x21,0x01,            /* [ 5560] OBJ_id_GostR3410_94_CryptoPro_XchA_ParamSet */
+    0x2A,0x85,0x03,0x02,0x02,0x21,0x02,            /* [ 5567] OBJ_id_GostR3410_94_CryptoPro_XchB_ParamSet */
+    0x2A,0x85,0x03,0x02,0x02,0x21,0x03,            /* [ 5574] OBJ_id_GostR3410_94_CryptoPro_XchC_ParamSet */
+    0x2A,0x85,0x03,0x02,0x02,0x23,0x00,            /* [ 5581] OBJ_id_GostR3410_2001_TestParamSet */
+    0x2A,0x85,0x03,0x02,0x02,0x23,0x01,            /* [ 5588] OBJ_id_GostR3410_2001_CryptoPro_A_ParamSet */
+    0x2A,0x85,0x03,0x02,0x02,0x23,0x02,            /* [ 5595] OBJ_id_GostR3410_2001_CryptoPro_B_ParamSet */
+    0x2A,0x85,0x03,0x02,0x02,0x23,0x03,            /* [ 5602] OBJ_id_GostR3410_2001_CryptoPro_C_ParamSet */
+    0x2A,0x85,0x03,0x02,0x02,0x24,0x00,            /* [ 5609] OBJ_id_GostR3410_2001_CryptoPro_XchA_ParamSet */
+    0x2A,0x85,0x03,0x02,0x02,0x24,0x01,            /* [ 5616] OBJ_id_GostR3410_2001_CryptoPro_XchB_ParamSet */
+    0x2A,0x85,0x03,0x02,0x02,0x14,0x01,            /* [ 5623] OBJ_id_GostR3410_94_a */
+    0x2A,0x85,0x03,0x02,0x02,0x14,0x02,            /* [ 5630] OBJ_id_GostR3410_94_aBis */
+    0x2A,0x85,0x03,0x02,0x02,0x14,0x03,            /* [ 5637] OBJ_id_GostR3410_94_b */
+    0x2A,0x85,0x03,0x02,0x02,0x14,0x04,            /* [ 5644] OBJ_id_GostR3410_94_bBis */
+    0x2A,0x85,0x03,0x02,0x09,0x01,0x06,0x01,       /* [ 5651] OBJ_id_Gost28147_89_cc */
+    0x2A,0x85,0x03,0x02,0x09,0x01,0x05,0x03,       /* [ 5659] OBJ_id_GostR3410_94_cc */
+    0x2A,0x85,0x03,0x02,0x09,0x01,0x05,0x04,       /* [ 5667] OBJ_id_GostR3410_2001_cc */
+    0x2A,0x85,0x03,0x02,0x09,0x01,0x03,0x03,       /* [ 5675] OBJ_id_GostR3411_94_with_GostR3410_94_cc */
+    0x2A,0x85,0x03,0x02,0x09,0x01,0x03,0x04,       /* [ 5683] OBJ_id_GostR3411_94_with_GostR3410_2001_cc */
+    0x2A,0x85,0x03,0x02,0x09,0x01,0x08,0x01,       /* [ 5691] OBJ_id_GostR3410_2001_ParamSet_cc */
+    0x2B,0x06,0x01,0x04,0x01,0x82,0x37,0x11,0x02,  /* [ 5699] OBJ_LocalKeySet */
+    0x55,0x1D,0x2E,                                /* [ 5708] OBJ_freshest_crl */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x08,0x03,       /* [ 5711] OBJ_id_on_permanentIdentifier */
+    0x55,0x04,0x0E,                                /* [ 5719] OBJ_searchGuide */
+    0x55,0x04,0x0F,                                /* [ 5722] OBJ_businessCategory */
+    0x55,0x04,0x10,                                /* [ 5725] OBJ_postalAddress */
+    0x55,0x04,0x12,                                /* [ 5728] OBJ_postOfficeBox */
+    0x55,0x04,0x13,                                /* [ 5731] OBJ_physicalDeliveryOfficeName */
+    0x55,0x04,0x14,                                /* [ 5734] OBJ_telephoneNumber */
+    0x55,0x04,0x15,                                /* [ 5737] OBJ_telexNumber */
+    0x55,0x04,0x16,                                /* [ 5740] OBJ_teletexTerminalIdentifier */
+    0x55,0x04,0x17,                                /* [ 5743] OBJ_facsimileTelephoneNumber */
+    0x55,0x04,0x18,                                /* [ 5746] OBJ_x121Address */
+    0x55,0x04,0x19,                                /* [ 5749] OBJ_internationaliSDNNumber */
+    0x55,0x04,0x1A,                                /* [ 5752] OBJ_registeredAddress */
+    0x55,0x04,0x1B,                                /* [ 5755] OBJ_destinationIndicator */
+    0x55,0x04,0x1C,                                /* [ 5758] OBJ_preferredDeliveryMethod */
+    0x55,0x04,0x1D,                                /* [ 5761] OBJ_presentationAddress */
+    0x55,0x04,0x1E,                                /* [ 5764] OBJ_supportedApplicationContext */
+    0x55,0x04,0x1F,                                /* [ 5767] OBJ_member */
+    0x55,0x04,0x20,                                /* [ 5770] OBJ_owner */
+    0x55,0x04,0x21,                                /* [ 5773] OBJ_roleOccupant */
+    0x55,0x04,0x22,                                /* [ 5776] OBJ_seeAlso */
+    0x55,0x04,0x23,                                /* [ 5779] OBJ_userPassword */
+    0x55,0x04,0x24,                                /* [ 5782] OBJ_userCertificate */
+    0x55,0x04,0x25,                                /* [ 5785] OBJ_cACertificate */
+    0x55,0x04,0x26,                                /* [ 5788] OBJ_authorityRevocationList */
+    0x55,0x04,0x27,                                /* [ 5791] OBJ_certificateRevocationList */
+    0x55,0x04,0x28,                                /* [ 5794] OBJ_crossCertificatePair */
+    0x55,0x04,0x2F,                                /* [ 5797] OBJ_enhancedSearchGuide */
+    0x55,0x04,0x30,                                /* [ 5800] OBJ_protocolInformation */
+    0x55,0x04,0x31,                                /* [ 5803] OBJ_distinguishedName */
+    0x55,0x04,0x32,                                /* [ 5806] OBJ_uniqueMember */
+    0x55,0x04,0x33,                                /* [ 5809] OBJ_houseIdentifier */
+    0x55,0x04,0x34,                                /* [ 5812] OBJ_supportedAlgorithms */
+    0x55,0x04,0x35,                                /* [ 5815] OBJ_deltaRevocationList */
+    0x55,0x04,0x36,                                /* [ 5818] OBJ_dmdName */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x03,0x09,  /* [ 5821] OBJ_id_alg_PWRI_KEK */
+    0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x01,0x06,  /* [ 5832] OBJ_aes_128_gcm */
+    0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x01,0x07,  /* [ 5841] OBJ_aes_128_ccm */
+    0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x01,0x08,  /* [ 5850] OBJ_id_aes128_wrap_pad */
+    0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x01,0x1A,  /* [ 5859] OBJ_aes_192_gcm */
+    0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x01,0x1B,  /* [ 5868] OBJ_aes_192_ccm */
+    0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x01,0x1C,  /* [ 5877] OBJ_id_aes192_wrap_pad */
+    0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x01,0x2E,  /* [ 5886] OBJ_aes_256_gcm */
+    0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x01,0x2F,  /* [ 5895] OBJ_aes_256_ccm */
+    0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x01,0x30,  /* [ 5904] OBJ_id_aes256_wrap_pad */
+    0x2A,0x83,0x08,0x8C,0x9A,0x4B,0x3D,0x01,0x01,0x03,0x02,  /* [ 5913] OBJ_id_camellia128_wrap */
+    0x2A,0x83,0x08,0x8C,0x9A,0x4B,0x3D,0x01,0x01,0x03,0x03,  /* [ 5924] OBJ_id_camellia192_wrap */
+    0x2A,0x83,0x08,0x8C,0x9A,0x4B,0x3D,0x01,0x01,0x03,0x04,  /* [ 5935] OBJ_id_camellia256_wrap */
+    0x55,0x1D,0x25,0x00,                           /* [ 5946] OBJ_anyExtendedKeyUsage */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x08,  /* [ 5950] OBJ_mgf1 */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x0A,  /* [ 5959] OBJ_rsassaPss */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x07,  /* [ 5968] OBJ_rsaesOaep */
+    0x2A,0x86,0x48,0xCE,0x3E,0x02,0x01,            /* [ 5977] OBJ_dhpublicnumber */
+    0x2B,0x24,0x03,0x03,0x02,0x08,0x01,0x01,0x01,  /* [ 5984] OBJ_brainpoolP160r1 */
+    0x2B,0x24,0x03,0x03,0x02,0x08,0x01,0x01,0x02,  /* [ 5993] OBJ_brainpoolP160t1 */
+    0x2B,0x24,0x03,0x03,0x02,0x08,0x01,0x01,0x03,  /* [ 6002] OBJ_brainpoolP192r1 */
+    0x2B,0x24,0x03,0x03,0x02,0x08,0x01,0x01,0x04,  /* [ 6011] OBJ_brainpoolP192t1 */
+    0x2B,0x24,0x03,0x03,0x02,0x08,0x01,0x01,0x05,  /* [ 6020] OBJ_brainpoolP224r1 */
+    0x2B,0x24,0x03,0x03,0x02,0x08,0x01,0x01,0x06,  /* [ 6029] OBJ_brainpoolP224t1 */
+    0x2B,0x24,0x03,0x03,0x02,0x08,0x01,0x01,0x07,  /* [ 6038] OBJ_brainpoolP256r1 */
+    0x2B,0x24,0x03,0x03,0x02,0x08,0x01,0x01,0x08,  /* [ 6047] OBJ_brainpoolP256t1 */
+    0x2B,0x24,0x03,0x03,0x02,0x08,0x01,0x01,0x09,  /* [ 6056] OBJ_brainpoolP320r1 */
+    0x2B,0x24,0x03,0x03,0x02,0x08,0x01,0x01,0x0A,  /* [ 6065] OBJ_brainpoolP320t1 */
+    0x2B,0x24,0x03,0x03,0x02,0x08,0x01,0x01,0x0B,  /* [ 6074] OBJ_brainpoolP384r1 */
+    0x2B,0x24,0x03,0x03,0x02,0x08,0x01,0x01,0x0C,  /* [ 6083] OBJ_brainpoolP384t1 */
+    0x2B,0x24,0x03,0x03,0x02,0x08,0x01,0x01,0x0D,  /* [ 6092] OBJ_brainpoolP512r1 */
+    0x2B,0x24,0x03,0x03,0x02,0x08,0x01,0x01,0x0E,  /* [ 6101] OBJ_brainpoolP512t1 */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x09,  /* [ 6110] OBJ_pSpecified */
+    0x2B,0x81,0x05,0x10,0x86,0x48,0x3F,0x00,0x02,  /* [ 6119] OBJ_dhSinglePass_stdDH_sha1kdf_scheme */
+    0x2B,0x81,0x04,0x01,0x0B,0x00,                 /* [ 6128] OBJ_dhSinglePass_stdDH_sha224kdf_scheme */
+    0x2B,0x81,0x04,0x01,0x0B,0x01,                 /* [ 6134] OBJ_dhSinglePass_stdDH_sha256kdf_scheme */
+    0x2B,0x81,0x04,0x01,0x0B,0x02,                 /* [ 6140] OBJ_dhSinglePass_stdDH_sha384kdf_scheme */
+    0x2B,0x81,0x04,0x01,0x0B,0x03,                 /* [ 6146] OBJ_dhSinglePass_stdDH_sha512kdf_scheme */
+    0x2B,0x81,0x05,0x10,0x86,0x48,0x3F,0x00,0x03,  /* [ 6152] OBJ_dhSinglePass_cofactorDH_sha1kdf_scheme */
+    0x2B,0x81,0x04,0x01,0x0E,0x00,                 /* [ 6161] OBJ_dhSinglePass_cofactorDH_sha224kdf_scheme */
+    0x2B,0x81,0x04,0x01,0x0E,0x01,                 /* [ 6167] OBJ_dhSinglePass_cofactorDH_sha256kdf_scheme */
+    0x2B,0x81,0x04,0x01,0x0E,0x02,                 /* [ 6173] OBJ_dhSinglePass_cofactorDH_sha384kdf_scheme */
+    0x2B,0x81,0x04,0x01,0x0E,0x03,                 /* [ 6179] OBJ_dhSinglePass_cofactorDH_sha512kdf_scheme */
+    0x2B,0x06,0x01,0x04,0x01,0xD6,0x79,0x02,0x04,0x02,  /* [ 6185] OBJ_ct_precert_scts */
+    0x2B,0x06,0x01,0x04,0x01,0xD6,0x79,0x02,0x04,0x03,  /* [ 6195] OBJ_ct_precert_poison */
+    0x2B,0x06,0x01,0x04,0x01,0xD6,0x79,0x02,0x04,0x04,  /* [ 6205] OBJ_ct_precert_signer */
+    0x2B,0x06,0x01,0x04,0x01,0xD6,0x79,0x02,0x04,0x05,  /* [ 6215] OBJ_ct_cert_scts */
+    0x2B,0x06,0x01,0x04,0x01,0x82,0x37,0x3C,0x02,0x01,0x01,  /* [ 6225] OBJ_jurisdictionLocalityName */
+    0x2B,0x06,0x01,0x04,0x01,0x82,0x37,0x3C,0x02,0x01,0x02,  /* [ 6236] OBJ_jurisdictionStateOrProvinceName */
+    0x2B,0x06,0x01,0x04,0x01,0x82,0x37,0x3C,0x02,0x01,0x03,  /* [ 6247] OBJ_jurisdictionCountryName */
+    0x03,0xA2,0x31,0x05,0x03,0x01,0x09,0x06,       /* [ 6258] OBJ_camellia_128_gcm */
+    0x03,0xA2,0x31,0x05,0x03,0x01,0x09,0x07,       /* [ 6266] OBJ_camellia_128_ccm */
+    0x03,0xA2,0x31,0x05,0x03,0x01,0x09,0x09,       /* [ 6274] OBJ_camellia_128_ctr */
+    0x03,0xA2,0x31,0x05,0x03,0x01,0x09,0x0A,       /* [ 6282] OBJ_camellia_128_cmac */
+    0x03,0xA2,0x31,0x05,0x03,0x01,0x09,0x1A,       /* [ 6290] OBJ_camellia_192_gcm */
+    0x03,0xA2,0x31,0x05,0x03,0x01,0x09,0x1B,       /* [ 6298] OBJ_camellia_192_ccm */
+    0x03,0xA2,0x31,0x05,0x03,0x01,0x09,0x1D,       /* [ 6306] OBJ_camellia_192_ctr */
+    0x03,0xA2,0x31,0x05,0x03,0x01,0x09,0x1E,       /* [ 6314] OBJ_camellia_192_cmac */
+    0x03,0xA2,0x31,0x05,0x03,0x01,0x09,0x2E,       /* [ 6322] OBJ_camellia_256_gcm */
+    0x03,0xA2,0x31,0x05,0x03,0x01,0x09,0x2F,       /* [ 6330] OBJ_camellia_256_ccm */
+    0x03,0xA2,0x31,0x05,0x03,0x01,0x09,0x31,       /* [ 6338] OBJ_camellia_256_ctr */
+    0x03,0xA2,0x31,0x05,0x03,0x01,0x09,0x32,       /* [ 6346] OBJ_camellia_256_cmac */
+    0x2B,0x06,0x01,0x04,0x01,0xDA,0x47,0x04,0x0B,  /* [ 6354] OBJ_id_scrypt */
+    0x2A,0x85,0x03,0x07,0x01,                      /* [ 6363] OBJ_id_tc26 */
+    0x2A,0x85,0x03,0x07,0x01,0x01,                 /* [ 6368] OBJ_id_tc26_algorithms */
+    0x2A,0x85,0x03,0x07,0x01,0x01,0x01,            /* [ 6374] OBJ_id_tc26_sign */
+    0x2A,0x85,0x03,0x07,0x01,0x01,0x01,0x01,       /* [ 6381] OBJ_id_GostR3410_2012_256 */
+    0x2A,0x85,0x03,0x07,0x01,0x01,0x01,0x02,       /* [ 6389] OBJ_id_GostR3410_2012_512 */
+    0x2A,0x85,0x03,0x07,0x01,0x01,0x02,            /* [ 6397] OBJ_id_tc26_digest */
+    0x2A,0x85,0x03,0x07,0x01,0x01,0x02,0x02,       /* [ 6404] OBJ_id_GostR3411_2012_256 */
+    0x2A,0x85,0x03,0x07,0x01,0x01,0x02,0x03,       /* [ 6412] OBJ_id_GostR3411_2012_512 */
+    0x2A,0x85,0x03,0x07,0x01,0x01,0x03,            /* [ 6420] OBJ_id_tc26_signwithdigest */
+    0x2A,0x85,0x03,0x07,0x01,0x01,0x03,0x02,       /* [ 6427] OBJ_id_tc26_signwithdigest_gost3410_2012_256 */
+    0x2A,0x85,0x03,0x07,0x01,0x01,0x03,0x03,       /* [ 6435] OBJ_id_tc26_signwithdigest_gost3410_2012_512 */
+    0x2A,0x85,0x03,0x07,0x01,0x01,0x04,            /* [ 6443] OBJ_id_tc26_mac */
+    0x2A,0x85,0x03,0x07,0x01,0x01,0x04,0x01,       /* [ 6450] OBJ_id_tc26_hmac_gost_3411_2012_256 */
+    0x2A,0x85,0x03,0x07,0x01,0x01,0x04,0x02,       /* [ 6458] OBJ_id_tc26_hmac_gost_3411_2012_512 */
+    0x2A,0x85,0x03,0x07,0x01,0x01,0x05,            /* [ 6466] OBJ_id_tc26_cipher */
+    0x2A,0x85,0x03,0x07,0x01,0x01,0x06,            /* [ 6473] OBJ_id_tc26_agreement */
+    0x2A,0x85,0x03,0x07,0x01,0x01,0x06,0x01,       /* [ 6480] OBJ_id_tc26_agreement_gost_3410_2012_256 */
+    0x2A,0x85,0x03,0x07,0x01,0x01,0x06,0x02,       /* [ 6488] OBJ_id_tc26_agreement_gost_3410_2012_512 */
+    0x2A,0x85,0x03,0x07,0x01,0x02,                 /* [ 6496] OBJ_id_tc26_constants */
+    0x2A,0x85,0x03,0x07,0x01,0x02,0x01,            /* [ 6502] OBJ_id_tc26_sign_constants */
+    0x2A,0x85,0x03,0x07,0x01,0x02,0x01,0x02,       /* [ 6509] OBJ_id_tc26_gost_3410_2012_512_constants */
+    0x2A,0x85,0x03,0x07,0x01,0x02,0x01,0x02,0x00,  /* [ 6517] OBJ_id_tc26_gost_3410_2012_512_paramSetTest */
+    0x2A,0x85,0x03,0x07,0x01,0x02,0x01,0x02,0x01,  /* [ 6526] OBJ_id_tc26_gost_3410_2012_512_paramSetA */
+    0x2A,0x85,0x03,0x07,0x01,0x02,0x01,0x02,0x02,  /* [ 6535] OBJ_id_tc26_gost_3410_2012_512_paramSetB */
+    0x2A,0x85,0x03,0x07,0x01,0x02,0x02,            /* [ 6544] OBJ_id_tc26_digest_constants */
+    0x2A,0x85,0x03,0x07,0x01,0x02,0x05,            /* [ 6551] OBJ_id_tc26_cipher_constants */
+    0x2A,0x85,0x03,0x07,0x01,0x02,0x05,0x01,       /* [ 6558] OBJ_id_tc26_gost_28147_constants */
+    0x2A,0x85,0x03,0x07,0x01,0x02,0x05,0x01,0x01,  /* [ 6566] OBJ_id_tc26_gost_28147_param_Z */
+    0x2A,0x85,0x03,0x03,0x81,0x03,0x01,0x01,       /* [ 6575] OBJ_INN */
+    0x2A,0x85,0x03,0x64,0x01,                      /* [ 6583] OBJ_OGRN */
+    0x2A,0x85,0x03,0x64,0x03,                      /* [ 6588] OBJ_SNILS */
+    0x2A,0x85,0x03,0x64,0x6F,                      /* [ 6593] OBJ_subjectSignTool */
+    0x2A,0x85,0x03,0x64,0x70,                      /* [ 6598] OBJ_issuerSignTool */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x01,0x18,       /* [ 6603] OBJ_tlsfeature */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x03,0x11,       /* [ 6611] OBJ_ipsec_IKE */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x03,0x12,       /* [ 6619] OBJ_capwapAC */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x03,0x13,       /* [ 6627] OBJ_capwapWTP */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x03,0x15,       /* [ 6635] OBJ_sshClient */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x03,0x16,       /* [ 6643] OBJ_sshServer */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x03,0x17,       /* [ 6651] OBJ_sendRouter */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x03,0x18,       /* [ 6659] OBJ_sendProxiedRouter */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x03,0x19,       /* [ 6667] OBJ_sendOwner */
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x03,0x1A,       /* [ 6675] OBJ_sendProxiedOwner */
+    0x2B,0x06,0x01,0x05,0x02,0x03,                 /* [ 6683] OBJ_id_pkinit */
+    0x2B,0x06,0x01,0x05,0x02,0x03,0x04,            /* [ 6689] OBJ_pkInitClientAuth */
+    0x2B,0x06,0x01,0x05,0x02,0x03,0x05,            /* [ 6696] OBJ_pkInitKDC */
+    0x2B,0x65,0x6E,                                /* [ 6703] OBJ_X25519 */
+    0x2B,0x65,0x6F,                                /* [ 6706] OBJ_X448 */
+    0x2B,0x06,0x01,0x04,0x01,0x8D,0x3A,0x0C,0x02,0x01,0x10,  /* [ 6709] OBJ_blake2b512 */
+    0x2B,0x06,0x01,0x04,0x01,0x8D,0x3A,0x0C,0x02,0x02,0x08,  /* [ 6720] OBJ_blake2s256 */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x01,0x13,  /* [ 6731] OBJ_id_smime_ct_contentCollection */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x01,0x17,  /* [ 6742] OBJ_id_smime_ct_authEnvelopedData */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x01,0x1C,  /* [ 6753] OBJ_id_ct_xml */
+    0x2A,0x86,0x48,0xCE,0x3D,0x04,                 /* [ 6764] OBJ_X9_62_id_ecSigType */
+    0x2B,0x81,0x04,0x01,                           /* [ 6770] OBJ_secg_scheme */
+    0x2B,0x81,0x04,0x01,0x07,                      /* [ 6774] OBJ_ecies_recommendedParameters */
+    0x2B,0x81,0x04,0x01,0x08,                      /* [ 6779] OBJ_ecies_specifiedParameters */
+    0x2B,0x81,0x04,0x01,0x11,0x00,                 /* [ 6784] OBJ_x9_63_kdf */
+    0x2B,0x81,0x04,0x01,0x11,0x01,                 /* [ 6790] OBJ_nist_concatenation_kdf */
+    0x2B,0x81,0x04,0x01,0x11,0x02,                 /* [ 6796] OBJ_tls_kdf */
+    0x2B,0x81,0x04,0x01,0x11,0x03,                 /* [ 6802] OBJ_ikev2_kdf */
+    0x2B,0x81,0x04,0x01,0x12,                      /* [ 6808] OBJ_xor_in_ecies */
+    0x2B,0x81,0x04,0x01,0x13,                      /* [ 6813] OBJ_tdes_cbc_in_ecies */
+    0x2B,0x81,0x04,0x01,0x14,0x00,                 /* [ 6818] OBJ_aes128_cbc_in_ecies */
+    0x2B,0x81,0x04,0x01,0x14,0x01,                 /* [ 6824] OBJ_aes192_cbc_in_ecies */
+    0x2B,0x81,0x04,0x01,0x14,0x02,                 /* [ 6830] OBJ_aes256_cbc_in_ecies */
+    0x2B,0x81,0x04,0x01,0x15,0x00,                 /* [ 6836] OBJ_aes128_ctr_in_ecies */
+    0x2B,0x81,0x04,0x01,0x15,0x01,                 /* [ 6842] OBJ_aes192_ctr_in_ecies */
+    0x2B,0x81,0x04,0x01,0x15,0x02,                 /* [ 6848] OBJ_aes256_ctr_in_ecies */
+    0x2B,0x81,0x04,0x01,0x16,                      /* [ 6854] OBJ_hmac_full_ecies */
+    0x2B,0x81,0x04,0x01,0x17,                      /* [ 6859] OBJ_hmac_half_ecies */
+    0x2B,0x81,0x04,0x01,0x18,0x00,                 /* [ 6864] OBJ_cmac_aes128_ecies */
+    0x2B,0x81,0x04,0x01,0x18,0x01,                 /* [ 6870] OBJ_cmac_aes192_ecies */
+    0x2B,0x81,0x04,0x01,0x18,0x02,                 /* [ 6876] OBJ_cmac_aes256_ecies */
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x0C,0x01,0x64,  /* [ 6882] OBJ_pbe_WithSM3AndSMS4_CBC */
+    0x2B,0x81,0x04,0x01,0x08,0x01,                 /* [ 6892] OBJ_ecies_with_x9_63_sha1_xor_hmac */
+    0x2B,0x81,0x04,0x01,0x08,0x02,                 /* [ 6898] OBJ_ecies_with_x9_63_sha256_xor_hmac */
+    0x2B,0x81,0x04,0x01,0x08,0x03,                 /* [ 6904] OBJ_ecies_with_x9_63_sha512_xor_hmac */
+    0x2B,0x81,0x04,0x01,0x08,0x04,                 /* [ 6910] OBJ_ecies_with_x9_63_sha1_aes128_cbc_hmac */
+    0x2B,0x81,0x04,0x01,0x08,0x05,                 /* [ 6916] OBJ_ecies_with_x9_63_sha256_aes128_cbc_hmac */
+    0x2B,0x81,0x04,0x01,0x08,0x06,                 /* [ 6922] OBJ_ecies_with_x9_63_sha512_aes256_cbc_hmac */
+    0x2B,0x81,0x04,0x01,0x08,0x07,                 /* [ 6928] OBJ_ecies_with_x9_63_sha256_aes128_ctr_hmac */
+    0x2B,0x81,0x04,0x01,0x08,0x08,                 /* [ 6934] OBJ_ecies_with_x9_63_sha512_aes256_ctr_hmac */
+    0x2B,0x81,0x04,0x01,0x08,0x09,                 /* [ 6940] OBJ_ecies_with_x9_63_sha256_aes128_cbc_hmac_half */
+    0x2B,0x81,0x04,0x01,0x08,0x0A,                 /* [ 6946] OBJ_ecies_with_x9_63_sha512_aes256_cbc_hmac_half */
+    0x2B,0x81,0x04,0x01,0x08,0x0B,                 /* [ 6952] OBJ_ecies_with_x9_63_sha256_aes128_ctr_hmac_half */
+    0x2B,0x81,0x04,0x01,0x08,0x0C,                 /* [ 6958] OBJ_ecies_with_x9_63_sha512_aes256_ctr_hmac_half */
+    0x2B,0x81,0x04,0x01,0x08,0x0D,                 /* [ 6964] OBJ_ecies_with_x9_63_sha1_aes128_cbc_cmac */
+    0x2B,0x81,0x04,0x01,0x08,0x0E,                 /* [ 6970] OBJ_ecies_with_x9_63_sha256_aes128_cbc_cmac */
+    0x2B,0x81,0x04,0x01,0x08,0x0F,                 /* [ 6976] OBJ_ecies_with_x9_63_sha512_aes256_cbc_cmac */
+    0x2B,0x81,0x04,0x01,0x08,0x10,                 /* [ 6982] OBJ_ecies_with_x9_63_sha256_aes128_ctr_cmac */
+    0x2B,0x81,0x04,0x01,0x08,0x11,                 /* [ 6988] OBJ_ecies_with_x9_63_sha512_aes256_ctr_cmac */
+    0x2A,0x86,0x48,0x01,0x86,0xFD,0x1E,0x01,0x01,  /* [ 6994] OBJ_ibcs1 */
+    0x2A,0x86,0x48,0x01,0x86,0xFD,0x1E,0x01,0x01,0x01,0x01,  /* [ 7003] OBJ_type1curve */
+    0x2A,0x86,0x48,0x01,0x86,0xFD,0x1E,0x01,0x01,0x02,0x01,  /* [ 7014] OBJ_bfibe */
+    0x2A,0x86,0x48,0x01,0x86,0xFD,0x1E,0x01,0x01,0x02,0x02,  /* [ 7025] OBJ_bb1ibe */
+    0x2A,0x81,0x1C,                                /* [ 7036] OBJ_ISO_CN */
+    0x2A,0x81,0x1C,0xCF,0x55,                      /* [ 7039] OBJ_oscca */
+    0x2A,0x81,0x1C,0xCF,0x55,0x01,                 /* [ 7044] OBJ_sm_scheme */
+    0x2A,0x81,0x1C,0xCF,0x55,0x01,0x67,0x01,       /* [ 7050] OBJ_ssf33_ecb */
+    0x2A,0x81,0x1C,0xCF,0x55,0x01,0x67,0x02,       /* [ 7058] OBJ_ssf33_cbc */
+    0x2A,0x81,0x1C,0xCF,0x55,0x01,0x67,0x03,       /* [ 7066] OBJ_ssf33_ofb128 */
+    0x2A,0x81,0x1C,0xCF,0x55,0x01,0x67,0x04,       /* [ 7074] OBJ_ssf33_cfb128 */
+    0x2A,0x81,0x1C,0xCF,0x55,0x01,0x67,0x05,       /* [ 7082] OBJ_ssf33_cfb1 */
+    0x2A,0x81,0x1C,0xCF,0x55,0x01,0x67,0x06,       /* [ 7090] OBJ_ssf33_cfb8 */
+    0x2A,0x81,0x1C,0xCF,0x55,0x01,0x67,0x07,       /* [ 7098] OBJ_ssf33_cbc_mac */
+    0x2A,0x81,0x1C,0xCF,0x55,0x01,0x66,0x01,       /* [ 7106] OBJ_sm1_ecb */
+    0x2A,0x81,0x1C,0xCF,0x55,0x01,0x66,0x02,       /* [ 7114] OBJ_sm1_cbc */
+    0x2A,0x81,0x1C,0xCF,0x55,0x01,0x66,0x03,       /* [ 7122] OBJ_sm1_ofb128 */
+    0x2A,0x81,0x1C,0xCF,0x55,0x01,0x66,0x04,       /* [ 7130] OBJ_sm1_cfb128 */
+    0x2A,0x81,0x1C,0xCF,0x55,0x01,0x66,0x05,       /* [ 7138] OBJ_sm1_cfb1 */
+    0x2A,0x81,0x1C,0xCF,0x55,0x01,0x66,0x06,       /* [ 7146] OBJ_sm1_cfb8 */
+    0x2A,0x81,0x1C,0xCF,0x55,0x01,0x82,0x2D,       /* [ 7154] OBJ_sm2p256v1 */
+    0x2A,0x81,0x1C,0xCF,0x55,0x01,0x82,0x2D,0x01,  /* [ 7162] OBJ_sm2sign */
+    0x2A,0x81,0x1C,0xCF,0x55,0x01,0x82,0x2D,0x02,  /* [ 7171] OBJ_sm2exchange */
+    0x2A,0x81,0x1C,0xCF,0x55,0x01,0x82,0x2D,0x03,  /* [ 7180] OBJ_sm2encrypt */
+    0x2A,0x81,0x1C,0xCF,0x55,0x01,0x83,0x75,       /* [ 7189] OBJ_sm2sign_with_sm3 */
+    0x2A,0x81,0x1C,0xCF,0x55,0x01,0x83,0x76,       /* [ 7197] OBJ_sm2sign_with_sha1 */
+    0x2A,0x81,0x1C,0xCF,0x55,0x01,0x83,0x77,       /* [ 7205] OBJ_sm2sign_with_sha256 */
+    0x2A,0x81,0x1C,0xCF,0x55,0x01,0x83,0x78,       /* [ 7213] OBJ_sm2sign_with_sha512 */
+    0x2A,0x81,0x1C,0xCF,0x55,0x01,0x83,0x79,       /* [ 7221] OBJ_sm2sign_with_sha224 */
+    0x2A,0x81,0x1C,0xCF,0x55,0x01,0x83,0x7A,       /* [ 7229] OBJ_sm2sign_with_sha384 */
+    0x2A,0x81,0x1C,0xCF,0x55,0x01,0x83,0x7B,       /* [ 7237] OBJ_sm2sign_with_rmd160 */
+    0x2A,0x81,0x1C,0xCF,0x55,0x01,0x84,0x08,       /* [ 7245] OBJ_sm2sign_with_whirlpool */
+    0x2A,0x81,0x1C,0xCF,0x55,0x01,0x84,0x09,       /* [ 7253] OBJ_sm2sign_with_blake2b512 */
+    0x2A,0x81,0x1C,0xCF,0x55,0x01,0x84,0x0A,       /* [ 7261] OBJ_sm2sign_with_blake2s256 */
+    0x2A,0x81,0x1C,0xCF,0x55,0x01,0x82,0x2D,0x03,0x01,  /* [ 7269] OBJ_sm2encrypt_recommendedParameters */
+    0x2A,0x81,0x1C,0xCF,0x55,0x01,0x82,0x2D,0x03,0x02,  /* [ 7279] OBJ_sm2encrypt_specifiedParameters */
+    0x2A,0x81,0x1C,0xCF,0x55,0x01,0x82,0x2D,0x03,0x02,0x01,  /* [ 7289] OBJ_sm2encrypt_with_sm3 */
+    0x2A,0x81,0x1C,0xCF,0x55,0x01,0x82,0x2D,0x03,0x02,0x02,  /* [ 7300] OBJ_sm2encrypt_with_sha1 */
+    0x2A,0x81,0x1C,0xCF,0x55,0x01,0x82,0x2D,0x03,0x02,0x03,  /* [ 7311] OBJ_sm2encrypt_with_sha224 */
+    0x2A,0x81,0x1C,0xCF,0x55,0x01,0x82,0x2D,0x03,0x02,0x04,  /* [ 7322] OBJ_sm2encrypt_with_sha256 */
+    0x2A,0x81,0x1C,0xCF,0x55,0x01,0x82,0x2D,0x03,0x02,0x05,  /* [ 7333] OBJ_sm2encrypt_with_sha384 */
+    0x2A,0x81,0x1C,0xCF,0x55,0x01,0x82,0x2D,0x03,0x02,0x06,  /* [ 7344] OBJ_sm2encrypt_with_sha512 */
+    0x2A,0x81,0x1C,0xCF,0x55,0x01,0x82,0x2D,0x03,0x02,0x07,  /* [ 7355] OBJ_sm2encrypt_with_rmd160 */
+    0x2A,0x81,0x1C,0xCF,0x55,0x01,0x82,0x2D,0x03,0x02,0x08,  /* [ 7366] OBJ_sm2encrypt_with_whirlpool */
+    0x2A,0x81,0x1C,0xCF,0x55,0x01,0x82,0x2D,0x03,0x02,0x09,  /* [ 7377] OBJ_sm2encrypt_with_blake2b512 */
+    0x2A,0x81,0x1C,0xCF,0x55,0x01,0x82,0x2D,0x03,0x02,0x0A,  /* [ 7388] OBJ_sm2encrypt_with_blake2s256 */
+    0x2A,0x81,0x1C,0xCF,0x55,0x01,0x82,0x2D,0x03,0x02,0x0B,  /* [ 7399] OBJ_sm2encrypt_with_md5 */
+    0x2A,0x81,0x1C,0xCF,0x55,0x01,0x83,0x11,       /* [ 7410] OBJ_sm3 */
+    0x2A,0x81,0x1C,0xCF,0x55,0x01,0x83,0x11,0x02,  /* [ 7418] OBJ_hmac_sm3 */
+    0x2A,0x81,0x1C,0xCF,0x55,0x01,0x68,0x01,       /* [ 7427] OBJ_sms4_ecb */
+    0x2A,0x81,0x1C,0xCF,0x55,0x01,0x68,0x02,       /* [ 7435] OBJ_sms4_cbc */
+    0x2A,0x81,0x1C,0xCF,0x55,0x01,0x68,0x03,       /* [ 7443] OBJ_sms4_ofb128 */
+    0x2A,0x81,0x1C,0xCF,0x55,0x01,0x68,0x04,       /* [ 7451] OBJ_sms4_cfb128 */
+    0x2A,0x81,0x1C,0xCF,0x55,0x01,0x68,0x05,       /* [ 7459] OBJ_sms4_cfb1 */
+    0x2A,0x81,0x1C,0xCF,0x55,0x01,0x68,0x06,       /* [ 7467] OBJ_sms4_cfb8 */
+    0x2A,0x81,0x1C,0xCF,0x55,0x01,0x68,0x07,       /* [ 7475] OBJ_sms4_ctr */
+    0x2A,0x81,0x1C,0xCF,0x55,0x01,0x68,0x08,       /* [ 7483] OBJ_sms4_gcm */
+    0x2A,0x81,0x1C,0xCF,0x55,0x01,0x68,0x09,       /* [ 7491] OBJ_sms4_ccm */
+    0x2A,0x81,0x1C,0xCF,0x55,0x01,0x68,0x0A,       /* [ 7499] OBJ_sms4_xts */
+    0x2A,0x81,0x1C,0xCF,0x55,0x01,0x68,0x0B,       /* [ 7507] OBJ_sms4_wrap */
+    0x2A,0x81,0x1C,0xCF,0x55,0x01,0x68,0x0C,       /* [ 7515] OBJ_sms4_wrap_pad */
+    0x2A,0x81,0x1C,0xCF,0x55,0x01,0x68,0x64,       /* [ 7523] OBJ_sms4_ocb */
+    0x2A,0x81,0x1C,0xCF,0x55,0x01,0x81,0x49,       /* [ 7531] OBJ_sm5 */
+    0x2A,0x81,0x1C,0xCF,0x55,0x01,0x65,0x01,       /* [ 7539] OBJ_sm6_ecb */
+    0x2A,0x81,0x1C,0xCF,0x55,0x01,0x65,0x02,       /* [ 7547] OBJ_sm6_cbc */
+    0x2A,0x81,0x1C,0xCF,0x55,0x01,0x65,0x03,       /* [ 7555] OBJ_sm6_ofb128 */
+    0x2A,0x81,0x1C,0xCF,0x55,0x01,0x65,0x04,       /* [ 7563] OBJ_sm6_cfb128 */
+    0x2A,0x81,0x1C,0xCF,0x55,0x01,0x82,0x2E,       /* [ 7571] OBJ_id_sm9PublicKey */
+    0x2A,0x81,0x1C,0xCF,0x55,0x01,0x82,0x2E,0x01,  /* [ 7579] OBJ_sm9sign */
+    0x2A,0x81,0x1C,0xCF,0x55,0x01,0x82,0x2E,0x02,  /* [ 7588] OBJ_sm9keyagreement */
+    0x2A,0x81,0x1C,0xCF,0x55,0x01,0x82,0x2E,0x03,  /* [ 7597] OBJ_sm9encrypt */
+    0x2A,0x81,0x1C,0xCF,0x55,0x01,0x82,0x2E,0x04,  /* [ 7606] OBJ_sm9hash1 */
+    0x2A,0x81,0x1C,0xCF,0x55,0x01,0x82,0x2E,0x05,  /* [ 7615] OBJ_sm9kdf */
+    0x2A,0x81,0x1C,0xCF,0x55,0x01,0x82,0x2E,0x06,  /* [ 7624] OBJ_id_sm9MasterSecret */
+    0x2A,0x81,0x1C,0xCF,0x55,0x01,0x82,0x2E,0x06,0x01,  /* [ 7633] OBJ_sm9bn256v1 */
+    0x2A,0x81,0x1C,0xCF,0x55,0x01,0x82,0x2E,0x01,0x01,  /* [ 7643] OBJ_sm9sign_with_sm3 */
+    0x2A,0x81,0x1C,0xCF,0x55,0x01,0x82,0x2E,0x01,0x02,  /* [ 7653] OBJ_sm9sign_with_sha256 */
+    0x2A,0x81,0x1C,0xCF,0x55,0x01,0x82,0x2E,0x03,0x01,  /* [ 7663] OBJ_sm9encrypt_with_sm3_xor */
+    0x2A,0x81,0x1C,0xCF,0x55,0x01,0x82,0x2E,0x03,0x02,  /* [ 7673] OBJ_sm9encrypt_with_sm3_sms4_cbc */
+    0x2A,0x81,0x1C,0xCF,0x55,0x01,0x82,0x2E,0x03,0x03,  /* [ 7683] OBJ_sm9encrypt_with_sm3_sms4_ctr */
+    0x2A,0x81,0x1C,0xCF,0x55,0x01,0x82,0x2E,0x04,0x01,  /* [ 7693] OBJ_sm9hash1_with_sm3 */
+    0x2A,0x81,0x1C,0xCF,0x55,0x01,0x82,0x2E,0x04,0x02,  /* [ 7703] OBJ_sm9hash1_with_sha256 */
+    0x2A,0x81,0x1C,0xCF,0x55,0x01,0x82,0x2E,0x05,0x01,  /* [ 7713] OBJ_sm9kdf_with_sm3 */
+    0x2A,0x81,0x1C,0xCF,0x55,0x01,0x82,0x2E,0x05,0x02,  /* [ 7723] OBJ_sm9kdf_with_sha256 */
+    0x2A,0x81,0x1C,0xCF,0x55,0x01,0x86,0x20,       /* [ 7733] OBJ_zuc */
+    0x2A,0x81,0x1C,0xCF,0x55,0x01,0x86,0x20,0x01,  /* [ 7741] OBJ_zuc_128eea3 */
+    0x2A,0x81,0x1C,0xCF,0x55,0x01,0x86,0x20,0x02,  /* [ 7750] OBJ_zuc_128eia3 */
+    0x2A,0x81,0x1C,0xD7,0x63,                      /* [ 7759] OBJ_bwips */
+    0x2A,0x81,0x1C,0xD7,0x63,0x01,                 /* [ 7764] OBJ_wapi_crypto */
+    0x2A,0x81,0x1C,0xD7,0x63,0x01,0x01,            /* [ 7770] OBJ_wapi_ec */
+    0x2A,0x81,0x1C,0xD7,0x63,0x01,0x01,0x01,       /* [ 7777] OBJ_wapi_ecdsa192_sha256 */
+    0x2A,0x81,0x1C,0xD7,0x63,0x01,0x01,0x02,0x01,  /* [ 7785] OBJ_wapip192v1 */
+    0x2B,0x06,0x01,0x04,0x01,0x83,0x83,0x0D,       /* [ 7794] OBJ_GmSSL */
+    0x2B,0x06,0x01,0x04,0x01,0x83,0x83,0x0D,0x01,  /* [ 7802] OBJ_cpk */
+    0x2B,0x06,0x01,0x04,0x01,0x83,0x83,0x0D,0x01,0x01,  /* [ 7811] OBJ_cpk_map */
+    0x2B,0x06,0x01,0x04,0x01,0x83,0x83,0x0D,0x01,0x01,0x01,  /* [ 7821] OBJ_cpk_map_sha1 */
+    0x2B,0x06,0x01,0x04,0x01,0x83,0x83,0x0D,0x01,0x01,0x02,  /* [ 7832] OBJ_cpk_map_sha256 */
+    0x2B,0x06,0x01,0x04,0x01,0x83,0x83,0x0D,0x01,0x01,0x03,  /* [ 7843] OBJ_cpk_map_sm3 */
+    0x2B,0x06,0x01,0x04,0x01,0x83,0x83,0x0D,0x01,0x01,0x04,  /* [ 7854] OBJ_cpk_map_sha384 */
+    0x2B,0x06,0x01,0x04,0x01,0x83,0x83,0x0D,0x01,0x01,0x05,  /* [ 7865] OBJ_cpk_map_sha512 */
+    0x2B,0x06,0x01,0x04,0x01,0x83,0x83,0x0D,0x15,  /* [ 7876] OBJ_paillier */
+    0x2A,0x81,0x1C,0xCF,0x55,0x01,0x82,0x2F,0x07,  /* [ 7885] OBJ_sm9hash2 */
+    0x2A,0x81,0x1C,0xCF,0x55,0x01,0x82,0x2F,0x07,0x01,  /* [ 7894] OBJ_sm9hash2_with_sm3 */
+    0x2A,0x81,0x1C,0xCF,0x55,0x01,0x82,0x2F,0x07,0x02,  /* [ 7904] OBJ_sm9hash2_with_sha256 */
+    0x2A,0x81,0x1C,0xCF,0x55,0x01,0x86,0x20,0x04,  /* [ 7914] OBJ_zuc256 */
+    0x2A,0x81,0x1C,0xCF,0x55,0x01,0x86,0x20,0x03,  /* [ 7923] OBJ_zuc_mac */
+    0x2A,0x81,0x1C,0xCF,0x55,0x01,0x86,0x20,0x05,  /* [ 7932] OBJ_zuc256_mac */
+    0x2A,0x81,0x1C,0xCF,0x55,0x01,0x86,0x20,0x05,0x01,  /* [ 7941] OBJ_zuc256_mac32 */
+    0x2A,0x81,0x1C,0xCF,0x55,0x01,0x86,0x20,0x05,0x02,  /* [ 7951] OBJ_zuc256_mac64 */
+    0x2A,0x81,0x1C,0xCF,0x55,0x01,0x86,0x20,0x05,0x03,  /* [ 7961] OBJ_zuc256_mac128 */
+};
+
+#define NUM_NID 1218
+static const ASN1_OBJECT nid_objs[NUM_NID] = {
+    {"UNDEF", "undefined", NID_undef},
+    {"rsadsi", "RSA Data Security, Inc.", NID_rsadsi, 6, &so[0]},
+    {"pkcs", "RSA Data Security, Inc. PKCS", NID_pkcs, 7, &so[6]},
+    {"MD2", "md2", NID_md2, 8, &so[13]},
+    {"MD5", "md5", NID_md5, 8, &so[21]},
+    {"RC4", "rc4", NID_rc4, 8, &so[29]},
+    {"rsaEncryption", "rsaEncryption", NID_rsaEncryption, 9, &so[37]},
+    {"RSA-MD2", "md2WithRSAEncryption", NID_md2WithRSAEncryption, 9, &so[46]},
+    {"RSA-MD5", "md5WithRSAEncryption", NID_md5WithRSAEncryption, 9, &so[55]},
+    {"PBE-MD2-DES", "pbeWithMD2AndDES-CBC", NID_pbeWithMD2AndDES_CBC, 9, &so[64]},
+    {"PBE-MD5-DES", "pbeWithMD5AndDES-CBC", NID_pbeWithMD5AndDES_CBC, 9, &so[73]},
+    {"X500", "directory services (X.500)", NID_X500, 1, &so[82]},
+    {"X509", "X509", NID_X509, 2, &so[83]},
+    {"CN", "commonName", NID_commonName, 3, &so[85]},
+    {"C", "countryName", NID_countryName, 3, &so[88]},
+    {"L", "localityName", NID_localityName, 3, &so[91]},
+    {"ST", "stateOrProvinceName", NID_stateOrProvinceName, 3, &so[94]},
+    {"O", "organizationName", NID_organizationName, 3, &so[97]},
+    {"OU", "organizationalUnitName", NID_organizationalUnitName, 3, &so[100]},
+    {"RSA", "rsa", NID_rsa, 4, &so[103]},
+    {"pkcs7", "pkcs7", NID_pkcs7, 8, &so[107]},
+    {"pkcs7-data", "pkcs7-data", NID_pkcs7_data, 9, &so[115]},
+    {"pkcs7-signedData", "pkcs7-signedData", NID_pkcs7_signed, 9, &so[124]},
+    {"pkcs7-envelopedData", "pkcs7-envelopedData", NID_pkcs7_enveloped, 9, &so[133]},
+    {"pkcs7-signedAndEnvelopedData", "pkcs7-signedAndEnvelopedData", NID_pkcs7_signedAndEnveloped, 9, &so[142]},
+    {"pkcs7-digestData", "pkcs7-digestData", NID_pkcs7_digest, 9, &so[151]},
+    {"pkcs7-encryptedData", "pkcs7-encryptedData", NID_pkcs7_encrypted, 9, &so[160]},
+    {"pkcs3", "pkcs3", NID_pkcs3, 8, &so[169]},
+    {"dhKeyAgreement", "dhKeyAgreement", NID_dhKeyAgreement, 9, &so[177]},
+    {"DES-ECB", "des-ecb", NID_des_ecb, 5, &so[186]},
+    {"DES-CFB", "des-cfb", NID_des_cfb64, 5, &so[191]},
+    {"DES-CBC", "des-cbc", NID_des_cbc, 5, &so[196]},
+    {"DES-EDE", "des-ede", NID_des_ede_ecb, 5, &so[201]},
+    {"DES-EDE3", "des-ede3", NID_des_ede3_ecb},
+    {"IDEA-CBC", "idea-cbc", NID_idea_cbc, 11, &so[206]},
+    {"IDEA-CFB", "idea-cfb", NID_idea_cfb64},
+    {"IDEA-ECB", "idea-ecb", NID_idea_ecb},
+    {"RC2-CBC", "rc2-cbc", NID_rc2_cbc, 8, &so[217]},
+    {"RC2-ECB", "rc2-ecb", NID_rc2_ecb},
+    {"RC2-CFB", "rc2-cfb", NID_rc2_cfb64},
+    {"RC2-OFB", "rc2-ofb", NID_rc2_ofb64},
+    {"SHA", "sha", NID_sha, 5, &so[225]},
+    {"RSA-SHA", "shaWithRSAEncryption", NID_shaWithRSAEncryption, 5, &so[230]},
+    {"DES-EDE-CBC", "des-ede-cbc", NID_des_ede_cbc},
+    {"DES-EDE3-CBC", "des-ede3-cbc", NID_des_ede3_cbc, 8, &so[235]},
+    {"DES-OFB", "des-ofb", NID_des_ofb64, 5, &so[243]},
+    {"IDEA-OFB", "idea-ofb", NID_idea_ofb64},
+    {"pkcs9", "pkcs9", NID_pkcs9, 8, &so[248]},
+    {"emailAddress", "emailAddress", NID_pkcs9_emailAddress, 9, &so[256]},
+    {"unstructuredName", "unstructuredName", NID_pkcs9_unstructuredName, 9, &so[265]},
+    {"contentType", "contentType", NID_pkcs9_contentType, 9, &so[274]},
+    {"messageDigest", "messageDigest", NID_pkcs9_messageDigest, 9, &so[283]},
+    {"signingTime", "signingTime", NID_pkcs9_signingTime, 9, &so[292]},
+    {"countersignature", "countersignature", NID_pkcs9_countersignature, 9, &so[301]},
+    {"challengePassword", "challengePassword", NID_pkcs9_challengePassword, 9, &so[310]},
+    {"unstructuredAddress", "unstructuredAddress", NID_pkcs9_unstructuredAddress, 9, &so[319]},
+    {"extendedCertificateAttributes", "extendedCertificateAttributes", NID_pkcs9_extCertAttributes, 9, &so[328]},
+    {"Netscape", "Netscape Communications Corp.", NID_netscape, 7, &so[337]},
+    {"nsCertExt", "Netscape Certificate Extension", NID_netscape_cert_extension, 8, &so[344]},
+    {"nsDataType", "Netscape Data Type", NID_netscape_data_type, 8, &so[352]},
+    {"DES-EDE-CFB", "des-ede-cfb", NID_des_ede_cfb64},
+    {"DES-EDE3-CFB", "des-ede3-cfb", NID_des_ede3_cfb64},
+    {"DES-EDE-OFB", "des-ede-ofb", NID_des_ede_ofb64},
+    {"DES-EDE3-OFB", "des-ede3-ofb", NID_des_ede3_ofb64},
+    {"SHA1", "sha1", NID_sha1, 5, &so[360]},
+    {"RSA-SHA1", "sha1WithRSAEncryption", NID_sha1WithRSAEncryption, 9, &so[365]},
+    {"DSA-SHA", "dsaWithSHA", NID_dsaWithSHA, 5, &so[374]},
+    {"DSA-old", "dsaEncryption-old", NID_dsa_2, 5, &so[379]},
+    {"PBE-SHA1-RC2-64", "pbeWithSHA1AndRC2-CBC", NID_pbeWithSHA1AndRC2_CBC, 9, &so[384]},
+    {"PBKDF2", "PBKDF2", NID_id_pbkdf2, 9, &so[393]},
+    {"DSA-SHA1-old", "dsaWithSHA1-old", NID_dsaWithSHA1_2, 5, &so[402]},
+    {"nsCertType", "Netscape Cert Type", NID_netscape_cert_type, 9, &so[407]},
+    {"nsBaseUrl", "Netscape Base Url", NID_netscape_base_url, 9, &so[416]},
+    {"nsRevocationUrl", "Netscape Revocation Url", NID_netscape_revocation_url, 9, &so[425]},
+    {"nsCaRevocationUrl", "Netscape CA Revocation Url", NID_netscape_ca_revocation_url, 9, &so[434]},
+    {"nsRenewalUrl", "Netscape Renewal Url", NID_netscape_renewal_url, 9, &so[443]},
+    {"nsCaPolicyUrl", "Netscape CA Policy Url", NID_netscape_ca_policy_url, 9, &so[452]},
+    {"nsSslServerName", "Netscape SSL Server Name", NID_netscape_ssl_server_name, 9, &so[461]},
+    {"nsComment", "Netscape Comment", NID_netscape_comment, 9, &so[470]},
+    {"nsCertSequence", "Netscape Certificate Sequence", NID_netscape_cert_sequence, 9, &so[479]},
+    {"DESX-CBC", "desx-cbc", NID_desx_cbc},
+    {"id-ce", "id-ce", NID_id_ce, 2, &so[488]},
+    {"subjectKeyIdentifier", "X509v3 Subject Key Identifier", NID_subject_key_identifier, 3, &so[490]},
+    {"keyUsage", "X509v3 Key Usage", NID_key_usage, 3, &so[493]},
+    {"privateKeyUsagePeriod", "X509v3 Private Key Usage Period", NID_private_key_usage_period, 3, &so[496]},
+    {"subjectAltName", "X509v3 Subject Alternative Name", NID_subject_alt_name, 3, &so[499]},
+    {"issuerAltName", "X509v3 Issuer Alternative Name", NID_issuer_alt_name, 3, &so[502]},
+    {"basicConstraints", "X509v3 Basic Constraints", NID_basic_constraints, 3, &so[505]},
+    {"crlNumber", "X509v3 CRL Number", NID_crl_number, 3, &so[508]},
+    {"certificatePolicies", "X509v3 Certificate Policies", NID_certificate_policies, 3, &so[511]},
+    {"authorityKeyIdentifier", "X509v3 Authority Key Identifier", NID_authority_key_identifier, 3, &so[514]},
+    {"BF-CBC", "bf-cbc", NID_bf_cbc, 9, &so[517]},
+    {"BF-ECB", "bf-ecb", NID_bf_ecb},
+    {"BF-CFB", "bf-cfb", NID_bf_cfb64},
+    {"BF-OFB", "bf-ofb", NID_bf_ofb64},
+    {"MDC2", "mdc2", NID_mdc2, 4, &so[526]},
+    {"RSA-MDC2", "mdc2WithRSA", NID_mdc2WithRSA, 4, &so[530]},
+    {"RC4-40", "rc4-40", NID_rc4_40},
+    {"RC2-40-CBC", "rc2-40-cbc", NID_rc2_40_cbc},
+    {"GN", "givenName", NID_givenName, 3, &so[534]},
+    {"SN", "surname", NID_surname, 3, &so[537]},
+    {"initials", "initials", NID_initials, 3, &so[540]},
+    {"uid", "uniqueIdentifier", NID_uniqueIdentifier, 10, &so[543]},
+    {"crlDistributionPoints", "X509v3 CRL Distribution Points", NID_crl_distribution_points, 3, &so[553]},
+    {"RSA-NP-MD5", "md5WithRSA", NID_md5WithRSA, 5, &so[556]},
+    {"serialNumber", "serialNumber", NID_serialNumber, 3, &so[561]},
+    {"title", "title", NID_title, 3, &so[564]},
+    {"description", "description", NID_description, 3, &so[567]},
+    {"CAST5-CBC", "cast5-cbc", NID_cast5_cbc, 9, &so[570]},
+    {"CAST5-ECB", "cast5-ecb", NID_cast5_ecb},
+    {"CAST5-CFB", "cast5-cfb", NID_cast5_cfb64},
+    {"CAST5-OFB", "cast5-ofb", NID_cast5_ofb64},
+    {"pbeWithMD5AndCast5CBC", "pbeWithMD5AndCast5CBC", NID_pbeWithMD5AndCast5_CBC, 9, &so[579]},
+    {"DSA-SHA1", "dsaWithSHA1", NID_dsaWithSHA1, 7, &so[588]},
+    {"MD5-SHA1", "md5-sha1", NID_md5_sha1},
+    {"RSA-SHA1-2", "sha1WithRSA", NID_sha1WithRSA, 5, &so[595]},
+    {"DSA", "dsaEncryption", NID_dsa, 7, &so[600]},
+    {"RIPEMD160", "ripemd160", NID_ripemd160, 5, &so[607]},
+    { NULL, NULL, NID_undef },
+    {"RSA-RIPEMD160", "ripemd160WithRSA", NID_ripemd160WithRSA, 6, &so[612]},
+    {"RC5-CBC", "rc5-cbc", NID_rc5_cbc, 8, &so[618]},
+    {"RC5-ECB", "rc5-ecb", NID_rc5_ecb},
+    {"RC5-CFB", "rc5-cfb", NID_rc5_cfb64},
+    {"RC5-OFB", "rc5-ofb", NID_rc5_ofb64},
+    { NULL, NULL, NID_undef },
+    {"ZLIB", "zlib compression", NID_zlib_compression, 11, &so[626]},
+    {"extendedKeyUsage", "X509v3 Extended Key Usage", NID_ext_key_usage, 3, &so[637]},
+    {"PKIX", "PKIX", NID_id_pkix, 6, &so[640]},
+    {"id-kp", "id-kp", NID_id_kp, 7, &so[646]},
+    {"serverAuth", "TLS Web Server Authentication", NID_server_auth, 8, &so[653]},
+    {"clientAuth", "TLS Web Client Authentication", NID_client_auth, 8, &so[661]},
+    {"codeSigning", "Code Signing", NID_code_sign, 8, &so[669]},
+    {"emailProtection", "E-mail Protection", NID_email_protect, 8, &so[677]},
+    {"timeStamping", "Time Stamping", NID_time_stamp, 8, &so[685]},
+    {"msCodeInd", "Microsoft Individual Code Signing", NID_ms_code_ind, 10, &so[693]},
+    {"msCodeCom", "Microsoft Commercial Code Signing", NID_ms_code_com, 10, &so[703]},
+    {"msCTLSign", "Microsoft Trust List Signing", NID_ms_ctl_sign, 10, &so[713]},
+    {"msSGC", "Microsoft Server Gated Crypto", NID_ms_sgc, 10, &so[723]},
+    {"msEFS", "Microsoft Encrypted File System", NID_ms_efs, 10, &so[733]},
+    {"nsSGC", "Netscape Server Gated Crypto", NID_ns_sgc, 9, &so[743]},
+    {"deltaCRL", "X509v3 Delta CRL Indicator", NID_delta_crl, 3, &so[752]},
+    {"CRLReason", "X509v3 CRL Reason Code", NID_crl_reason, 3, &so[755]},
+    {"invalidityDate", "Invalidity Date", NID_invalidity_date, 3, &so[758]},
+    {"SXNetID", "Strong Extranet ID", NID_sxnet, 5, &so[761]},
+    {"PBE-SHA1-RC4-128", "pbeWithSHA1And128BitRC4", NID_pbe_WithSHA1And128BitRC4, 10, &so[766]},
+    {"PBE-SHA1-RC4-40", "pbeWithSHA1And40BitRC4", NID_pbe_WithSHA1And40BitRC4, 10, &so[776]},
+    {"PBE-SHA1-3DES", "pbeWithSHA1And3-KeyTripleDES-CBC", NID_pbe_WithSHA1And3_Key_TripleDES_CBC, 10, &so[786]},
+    {"PBE-SHA1-2DES", "pbeWithSHA1And2-KeyTripleDES-CBC", NID_pbe_WithSHA1And2_Key_TripleDES_CBC, 10, &so[796]},
+    {"PBE-SHA1-RC2-128", "pbeWithSHA1And128BitRC2-CBC", NID_pbe_WithSHA1And128BitRC2_CBC, 10, &so[806]},
+    {"PBE-SHA1-RC2-40", "pbeWithSHA1And40BitRC2-CBC", NID_pbe_WithSHA1And40BitRC2_CBC, 10, &so[816]},
+    {"keyBag", "keyBag", NID_keyBag, 11, &so[826]},
+    {"pkcs8ShroudedKeyBag", "pkcs8ShroudedKeyBag", NID_pkcs8ShroudedKeyBag, 11, &so[837]},
+    {"certBag", "certBag", NID_certBag, 11, &so[848]},
+    {"crlBag", "crlBag", NID_crlBag, 11, &so[859]},
+    {"secretBag", "secretBag", NID_secretBag, 11, &so[870]},
+    {"safeContentsBag", "safeContentsBag", NID_safeContentsBag, 11, &so[881]},
+    {"friendlyName", "friendlyName", NID_friendlyName, 9, &so[892]},
+    {"localKeyID", "localKeyID", NID_localKeyID, 9, &so[901]},
+    {"x509Certificate", "x509Certificate", NID_x509Certificate, 10, &so[910]},
+    {"sdsiCertificate", "sdsiCertificate", NID_sdsiCertificate, 10, &so[920]},
+    {"x509Crl", "x509Crl", NID_x509Crl, 10, &so[930]},
+    {"PBES2", "PBES2", NID_pbes2, 9, &so[940]},
+    {"PBMAC1", "PBMAC1", NID_pbmac1, 9, &so[949]},
+    {"hmacWithSHA1", "hmacWithSHA1", NID_hmacWithSHA1, 8, &so[958]},
+    {"id-qt-cps", "Policy Qualifier CPS", NID_id_qt_cps, 8, &so[966]},
+    {"id-qt-unotice", "Policy Qualifier User Notice", NID_id_qt_unotice, 8, &so[974]},
+    {"RC2-64-CBC", "rc2-64-cbc", NID_rc2_64_cbc},
+    {"SMIME-CAPS", "S/MIME Capabilities", NID_SMIMECapabilities, 9, &so[982]},
+    {"PBE-MD2-RC2-64", "pbeWithMD2AndRC2-CBC", NID_pbeWithMD2AndRC2_CBC, 9, &so[991]},
+    {"PBE-MD5-RC2-64", "pbeWithMD5AndRC2-CBC", NID_pbeWithMD5AndRC2_CBC, 9, &so[1000]},
+    {"PBE-SHA1-DES", "pbeWithSHA1AndDES-CBC", NID_pbeWithSHA1AndDES_CBC, 9, &so[1009]},
+    {"msExtReq", "Microsoft Extension Request", NID_ms_ext_req, 10, &so[1018]},
+    {"extReq", "Extension Request", NID_ext_req, 9, &so[1028]},
+    {"name", "name", NID_name, 3, &so[1037]},
+    {"dnQualifier", "dnQualifier", NID_dnQualifier, 3, &so[1040]},
+    {"id-pe", "id-pe", NID_id_pe, 7, &so[1043]},
+    {"id-ad", "id-ad", NID_id_ad, 7, &so[1050]},
+    {"authorityInfoAccess", "Authority Information Access", NID_info_access, 8, &so[1057]},
+    {"OCSP", "OCSP", NID_ad_OCSP, 8, &so[1065]},
+    {"caIssuers", "CA Issuers", NID_ad_ca_issuers, 8, &so[1073]},
+    {"OCSPSigning", "OCSP Signing", NID_OCSP_sign, 8, &so[1081]},
+    {"ISO", "iso", NID_iso},
+    {"member-body", "ISO Member Body", NID_member_body, 1, &so[1089]},
+    {"ISO-US", "ISO US Member Body", NID_ISO_US, 3, &so[1090]},
+    {"X9-57", "X9.57", NID_X9_57, 5, &so[1093]},
+    {"X9cm", "X9.57 CM ?", NID_X9cm, 6, &so[1098]},
+    {"pkcs1", "pkcs1", NID_pkcs1, 8, &so[1104]},
+    {"pkcs5", "pkcs5", NID_pkcs5, 8, &so[1112]},
+    {"SMIME", "S/MIME", NID_SMIME, 9, &so[1120]},
+    {"id-smime-mod", "id-smime-mod", NID_id_smime_mod, 10, &so[1129]},
+    {"id-smime-ct", "id-smime-ct", NID_id_smime_ct, 10, &so[1139]},
+    {"id-smime-aa", "id-smime-aa", NID_id_smime_aa, 10, &so[1149]},
+    {"id-smime-alg", "id-smime-alg", NID_id_smime_alg, 10, &so[1159]},
+    {"id-smime-cd", "id-smime-cd", NID_id_smime_cd, 10, &so[1169]},
+    {"id-smime-spq", "id-smime-spq", NID_id_smime_spq, 10, &so[1179]},
+    {"id-smime-cti", "id-smime-cti", NID_id_smime_cti, 10, &so[1189]},
+    {"id-smime-mod-cms", "id-smime-mod-cms", NID_id_smime_mod_cms, 11, &so[1199]},
+    {"id-smime-mod-ess", "id-smime-mod-ess", NID_id_smime_mod_ess, 11, &so[1210]},
+    {"id-smime-mod-oid", "id-smime-mod-oid", NID_id_smime_mod_oid, 11, &so[1221]},
+    {"id-smime-mod-msg-v3", "id-smime-mod-msg-v3", NID_id_smime_mod_msg_v3, 11, &so[1232]},
+    {"id-smime-mod-ets-eSignature-88", "id-smime-mod-ets-eSignature-88", NID_id_smime_mod_ets_eSignature_88, 11, &so[1243]},
+    {"id-smime-mod-ets-eSignature-97", "id-smime-mod-ets-eSignature-97", NID_id_smime_mod_ets_eSignature_97, 11, &so[1254]},
+    {"id-smime-mod-ets-eSigPolicy-88", "id-smime-mod-ets-eSigPolicy-88", NID_id_smime_mod_ets_eSigPolicy_88, 11, &so[1265]},
+    {"id-smime-mod-ets-eSigPolicy-97", "id-smime-mod-ets-eSigPolicy-97", NID_id_smime_mod_ets_eSigPolicy_97, 11, &so[1276]},
+    {"id-smime-ct-receipt", "id-smime-ct-receipt", NID_id_smime_ct_receipt, 11, &so[1287]},
+    {"id-smime-ct-authData", "id-smime-ct-authData", NID_id_smime_ct_authData, 11, &so[1298]},
+    {"id-smime-ct-publishCert", "id-smime-ct-publishCert", NID_id_smime_ct_publishCert, 11, &so[1309]},
+    {"id-smime-ct-TSTInfo", "id-smime-ct-TSTInfo", NID_id_smime_ct_TSTInfo, 11, &so[1320]},
+    {"id-smime-ct-TDTInfo", "id-smime-ct-TDTInfo", NID_id_smime_ct_TDTInfo, 11, &so[1331]},
+    {"id-smime-ct-contentInfo", "id-smime-ct-contentInfo", NID_id_smime_ct_contentInfo, 11, &so[1342]},
+    {"id-smime-ct-DVCSRequestData", "id-smime-ct-DVCSRequestData", NID_id_smime_ct_DVCSRequestData, 11, &so[1353]},
+    {"id-smime-ct-DVCSResponseData", "id-smime-ct-DVCSResponseData", NID_id_smime_ct_DVCSResponseData, 11, &so[1364]},
+    {"id-smime-aa-receiptRequest", "id-smime-aa-receiptRequest", NID_id_smime_aa_receiptRequest, 11, &so[1375]},
+    {"id-smime-aa-securityLabel", "id-smime-aa-securityLabel", NID_id_smime_aa_securityLabel, 11, &so[1386]},
+    {"id-smime-aa-mlExpandHistory", "id-smime-aa-mlExpandHistory", NID_id_smime_aa_mlExpandHistory, 11, &so[1397]},
+    {"id-smime-aa-contentHint", "id-smime-aa-contentHint", NID_id_smime_aa_contentHint, 11, &so[1408]},
+    {"id-smime-aa-msgSigDigest", "id-smime-aa-msgSigDigest", NID_id_smime_aa_msgSigDigest, 11, &so[1419]},
+    {"id-smime-aa-encapContentType", "id-smime-aa-encapContentType", NID_id_smime_aa_encapContentType, 11, &so[1430]},
+    {"id-smime-aa-contentIdentifier", "id-smime-aa-contentIdentifier", NID_id_smime_aa_contentIdentifier, 11, &so[1441]},
+    {"id-smime-aa-macValue", "id-smime-aa-macValue", NID_id_smime_aa_macValue, 11, &so[1452]},
+    {"id-smime-aa-equivalentLabels", "id-smime-aa-equivalentLabels", NID_id_smime_aa_equivalentLabels, 11, &so[1463]},
+    {"id-smime-aa-contentReference", "id-smime-aa-contentReference", NID_id_smime_aa_contentReference, 11, &so[1474]},
+    {"id-smime-aa-encrypKeyPref", "id-smime-aa-encrypKeyPref", NID_id_smime_aa_encrypKeyPref, 11, &so[1485]},
+    {"id-smime-aa-signingCertificate", "id-smime-aa-signingCertificate", NID_id_smime_aa_signingCertificate, 11, &so[1496]},
+    {"id-smime-aa-smimeEncryptCerts", "id-smime-aa-smimeEncryptCerts", NID_id_smime_aa_smimeEncryptCerts, 11, &so[1507]},
+    {"id-smime-aa-timeStampToken", "id-smime-aa-timeStampToken", NID_id_smime_aa_timeStampToken, 11, &so[1518]},
+    {"id-smime-aa-ets-sigPolicyId", "id-smime-aa-ets-sigPolicyId", NID_id_smime_aa_ets_sigPolicyId, 11, &so[1529]},
+    {"id-smime-aa-ets-commitmentType", "id-smime-aa-ets-commitmentType", NID_id_smime_aa_ets_commitmentType, 11, &so[1540]},
+    {"id-smime-aa-ets-signerLocation", "id-smime-aa-ets-signerLocation", NID_id_smime_aa_ets_signerLocation, 11, &so[1551]},
+    {"id-smime-aa-ets-signerAttr", "id-smime-aa-ets-signerAttr", NID_id_smime_aa_ets_signerAttr, 11, &so[1562]},
+    {"id-smime-aa-ets-otherSigCert", "id-smime-aa-ets-otherSigCert", NID_id_smime_aa_ets_otherSigCert, 11, &so[1573]},
+    {"id-smime-aa-ets-contentTimestamp", "id-smime-aa-ets-contentTimestamp", NID_id_smime_aa_ets_contentTimestamp, 11, &so[1584]},
+    {"id-smime-aa-ets-CertificateRefs", "id-smime-aa-ets-CertificateRefs", NID_id_smime_aa_ets_CertificateRefs, 11, &so[1595]},
+    {"id-smime-aa-ets-RevocationRefs", "id-smime-aa-ets-RevocationRefs", NID_id_smime_aa_ets_RevocationRefs, 11, &so[1606]},
+    {"id-smime-aa-ets-certValues", "id-smime-aa-ets-certValues", NID_id_smime_aa_ets_certValues, 11, &so[1617]},
+    {"id-smime-aa-ets-revocationValues", "id-smime-aa-ets-revocationValues", NID_id_smime_aa_ets_revocationValues, 11, &so[1628]},
+    {"id-smime-aa-ets-escTimeStamp", "id-smime-aa-ets-escTimeStamp", NID_id_smime_aa_ets_escTimeStamp, 11, &so[1639]},
+    {"id-smime-aa-ets-certCRLTimestamp", "id-smime-aa-ets-certCRLTimestamp", NID_id_smime_aa_ets_certCRLTimestamp, 11, &so[1650]},
+    {"id-smime-aa-ets-archiveTimeStamp", "id-smime-aa-ets-archiveTimeStamp", NID_id_smime_aa_ets_archiveTimeStamp, 11, &so[1661]},
+    {"id-smime-aa-signatureType", "id-smime-aa-signatureType", NID_id_smime_aa_signatureType, 11, &so[1672]},
+    {"id-smime-aa-dvcs-dvc", "id-smime-aa-dvcs-dvc", NID_id_smime_aa_dvcs_dvc, 11, &so[1683]},
+    {"id-smime-alg-ESDHwith3DES", "id-smime-alg-ESDHwith3DES", NID_id_smime_alg_ESDHwith3DES, 11, &so[1694]},
+    {"id-smime-alg-ESDHwithRC2", "id-smime-alg-ESDHwithRC2", NID_id_smime_alg_ESDHwithRC2, 11, &so[1705]},
+    {"id-smime-alg-3DESwrap", "id-smime-alg-3DESwrap", NID_id_smime_alg_3DESwrap, 11, &so[1716]},
+    {"id-smime-alg-RC2wrap", "id-smime-alg-RC2wrap", NID_id_smime_alg_RC2wrap, 11, &so[1727]},
+    {"id-smime-alg-ESDH", "id-smime-alg-ESDH", NID_id_smime_alg_ESDH, 11, &so[1738]},
+    {"id-smime-alg-CMS3DESwrap", "id-smime-alg-CMS3DESwrap", NID_id_smime_alg_CMS3DESwrap, 11, &so[1749]},
+    {"id-smime-alg-CMSRC2wrap", "id-smime-alg-CMSRC2wrap", NID_id_smime_alg_CMSRC2wrap, 11, &so[1760]},
+    {"id-smime-cd-ldap", "id-smime-cd-ldap", NID_id_smime_cd_ldap, 11, &so[1771]},
+    {"id-smime-spq-ets-sqt-uri", "id-smime-spq-ets-sqt-uri", NID_id_smime_spq_ets_sqt_uri, 11, &so[1782]},
+    {"id-smime-spq-ets-sqt-unotice", "id-smime-spq-ets-sqt-unotice", NID_id_smime_spq_ets_sqt_unotice, 11, &so[1793]},
+    {"id-smime-cti-ets-proofOfOrigin", "id-smime-cti-ets-proofOfOrigin", NID_id_smime_cti_ets_proofOfOrigin, 11, &so[1804]},
+    {"id-smime-cti-ets-proofOfReceipt", "id-smime-cti-ets-proofOfReceipt", NID_id_smime_cti_ets_proofOfReceipt, 11, &so[1815]},
+    {"id-smime-cti-ets-proofOfDelivery", "id-smime-cti-ets-proofOfDelivery", NID_id_smime_cti_ets_proofOfDelivery, 11, &so[1826]},
+    {"id-smime-cti-ets-proofOfSender", "id-smime-cti-ets-proofOfSender", NID_id_smime_cti_ets_proofOfSender, 11, &so[1837]},
+    {"id-smime-cti-ets-proofOfApproval", "id-smime-cti-ets-proofOfApproval", NID_id_smime_cti_ets_proofOfApproval, 11, &so[1848]},
+    {"id-smime-cti-ets-proofOfCreation", "id-smime-cti-ets-proofOfCreation", NID_id_smime_cti_ets_proofOfCreation, 11, &so[1859]},
+    {"MD4", "md4", NID_md4, 8, &so[1870]},
+    {"id-pkix-mod", "id-pkix-mod", NID_id_pkix_mod, 7, &so[1878]},
+    {"id-qt", "id-qt", NID_id_qt, 7, &so[1885]},
+    {"id-it", "id-it", NID_id_it, 7, &so[1892]},
+    {"id-pkip", "id-pkip", NID_id_pkip, 7, &so[1899]},
+    {"id-alg", "id-alg", NID_id_alg, 7, &so[1906]},
+    {"id-cmc", "id-cmc", NID_id_cmc, 7, &so[1913]},
+    {"id-on", "id-on", NID_id_on, 7, &so[1920]},
+    {"id-pda", "id-pda", NID_id_pda, 7, &so[1927]},
+    {"id-aca", "id-aca", NID_id_aca, 7, &so[1934]},
+    {"id-qcs", "id-qcs", NID_id_qcs, 7, &so[1941]},
+    {"id-cct", "id-cct", NID_id_cct, 7, &so[1948]},
+    {"id-pkix1-explicit-88", "id-pkix1-explicit-88", NID_id_pkix1_explicit_88, 8, &so[1955]},
+    {"id-pkix1-implicit-88", "id-pkix1-implicit-88", NID_id_pkix1_implicit_88, 8, &so[1963]},
+    {"id-pkix1-explicit-93", "id-pkix1-explicit-93", NID_id_pkix1_explicit_93, 8, &so[1971]},
+    {"id-pkix1-implicit-93", "id-pkix1-implicit-93", NID_id_pkix1_implicit_93, 8, &so[1979]},
+    {"id-mod-crmf", "id-mod-crmf", NID_id_mod_crmf, 8, &so[1987]},
+    {"id-mod-cmc", "id-mod-cmc", NID_id_mod_cmc, 8, &so[1995]},
+    {"id-mod-kea-profile-88", "id-mod-kea-profile-88", NID_id_mod_kea_profile_88, 8, &so[2003]},
+    {"id-mod-kea-profile-93", "id-mod-kea-profile-93", NID_id_mod_kea_profile_93, 8, &so[2011]},
+    {"id-mod-cmp", "id-mod-cmp", NID_id_mod_cmp, 8, &so[2019]},
+    {"id-mod-qualified-cert-88", "id-mod-qualified-cert-88", NID_id_mod_qualified_cert_88, 8, &so[2027]},
+    {"id-mod-qualified-cert-93", "id-mod-qualified-cert-93", NID_id_mod_qualified_cert_93, 8, &so[2035]},
+    {"id-mod-attribute-cert", "id-mod-attribute-cert", NID_id_mod_attribute_cert, 8, &so[2043]},
+    {"id-mod-timestamp-protocol", "id-mod-timestamp-protocol", NID_id_mod_timestamp_protocol, 8, &so[2051]},
+    {"id-mod-ocsp", "id-mod-ocsp", NID_id_mod_ocsp, 8, &so[2059]},
+    {"id-mod-dvcs", "id-mod-dvcs", NID_id_mod_dvcs, 8, &so[2067]},
+    {"id-mod-cmp2000", "id-mod-cmp2000", NID_id_mod_cmp2000, 8, &so[2075]},
+    {"biometricInfo", "Biometric Info", NID_biometricInfo, 8, &so[2083]},
+    {"qcStatements", "qcStatements", NID_qcStatements, 8, &so[2091]},
+    {"ac-auditEntity", "ac-auditEntity", NID_ac_auditEntity, 8, &so[2099]},
+    {"ac-targeting", "ac-targeting", NID_ac_targeting, 8, &so[2107]},
+    {"aaControls", "aaControls", NID_aaControls, 8, &so[2115]},
+    {"sbgp-ipAddrBlock", "sbgp-ipAddrBlock", NID_sbgp_ipAddrBlock, 8, &so[2123]},
+    {"sbgp-autonomousSysNum", "sbgp-autonomousSysNum", NID_sbgp_autonomousSysNum, 8, &so[2131]},
+    {"sbgp-routerIdentifier", "sbgp-routerIdentifier", NID_sbgp_routerIdentifier, 8, &so[2139]},
+    {"textNotice", "textNotice", NID_textNotice, 8, &so[2147]},
+    {"ipsecEndSystem", "IPSec End System", NID_ipsecEndSystem, 8, &so[2155]},
+    {"ipsecTunnel", "IPSec Tunnel", NID_ipsecTunnel, 8, &so[2163]},
+    {"ipsecUser", "IPSec User", NID_ipsecUser, 8, &so[2171]},
+    {"DVCS", "dvcs", NID_dvcs, 8, &so[2179]},
+    {"id-it-caProtEncCert", "id-it-caProtEncCert", NID_id_it_caProtEncCert, 8, &so[2187]},
+    {"id-it-signKeyPairTypes", "id-it-signKeyPairTypes", NID_id_it_signKeyPairTypes, 8, &so[2195]},
+    {"id-it-encKeyPairTypes", "id-it-encKeyPairTypes", NID_id_it_encKeyPairTypes, 8, &so[2203]},
+    {"id-it-preferredSymmAlg", "id-it-preferredSymmAlg", NID_id_it_preferredSymmAlg, 8, &so[2211]},
+    {"id-it-caKeyUpdateInfo", "id-it-caKeyUpdateInfo", NID_id_it_caKeyUpdateInfo, 8, &so[2219]},
+    {"id-it-currentCRL", "id-it-currentCRL", NID_id_it_currentCRL, 8, &so[2227]},
+    {"id-it-unsupportedOIDs", "id-it-unsupportedOIDs", NID_id_it_unsupportedOIDs, 8, &so[2235]},
+    {"id-it-subscriptionRequest", "id-it-subscriptionRequest", NID_id_it_subscriptionRequest, 8, &so[2243]},
+    {"id-it-subscriptionResponse", "id-it-subscriptionResponse", NID_id_it_subscriptionResponse, 8, &so[2251]},
+    {"id-it-keyPairParamReq", "id-it-keyPairParamReq", NID_id_it_keyPairParamReq, 8, &so[2259]},
+    {"id-it-keyPairParamRep", "id-it-keyPairParamRep", NID_id_it_keyPairParamRep, 8, &so[2267]},
+    {"id-it-revPassphrase", "id-it-revPassphrase", NID_id_it_revPassphrase, 8, &so[2275]},
+    {"id-it-implicitConfirm", "id-it-implicitConfirm", NID_id_it_implicitConfirm, 8, &so[2283]},
+    {"id-it-confirmWaitTime", "id-it-confirmWaitTime", NID_id_it_confirmWaitTime, 8, &so[2291]},
+    {"id-it-origPKIMessage", "id-it-origPKIMessage", NID_id_it_origPKIMessage, 8, &so[2299]},
+    {"id-regCtrl", "id-regCtrl", NID_id_regCtrl, 8, &so[2307]},
+    {"id-regInfo", "id-regInfo", NID_id_regInfo, 8, &so[2315]},
+    {"id-regCtrl-regToken", "id-regCtrl-regToken", NID_id_regCtrl_regToken, 9, &so[2323]},
+    {"id-regCtrl-authenticator", "id-regCtrl-authenticator", NID_id_regCtrl_authenticator, 9, &so[2332]},
+    {"id-regCtrl-pkiPublicationInfo", "id-regCtrl-pkiPublicationInfo", NID_id_regCtrl_pkiPublicationInfo, 9, &so[2341]},
+    {"id-regCtrl-pkiArchiveOptions", "id-regCtrl-pkiArchiveOptions", NID_id_regCtrl_pkiArchiveOptions, 9, &so[2350]},
+    {"id-regCtrl-oldCertID", "id-regCtrl-oldCertID", NID_id_regCtrl_oldCertID, 9, &so[2359]},
+    {"id-regCtrl-protocolEncrKey", "id-regCtrl-protocolEncrKey", NID_id_regCtrl_protocolEncrKey, 9, &so[2368]},
+    {"id-regInfo-utf8Pairs", "id-regInfo-utf8Pairs", NID_id_regInfo_utf8Pairs, 9, &so[2377]},
+    {"id-regInfo-certReq", "id-regInfo-certReq", NID_id_regInfo_certReq, 9, &so[2386]},
+    {"id-alg-des40", "id-alg-des40", NID_id_alg_des40, 8, &so[2395]},
+    {"id-alg-noSignature", "id-alg-noSignature", NID_id_alg_noSignature, 8, &so[2403]},
+    {"id-alg-dh-sig-hmac-sha1", "id-alg-dh-sig-hmac-sha1", NID_id_alg_dh_sig_hmac_sha1, 8, &so[2411]},
+    {"id-alg-dh-pop", "id-alg-dh-pop", NID_id_alg_dh_pop, 8, &so[2419]},
+    {"id-cmc-statusInfo", "id-cmc-statusInfo", NID_id_cmc_statusInfo, 8, &so[2427]},
+    {"id-cmc-identification", "id-cmc-identification", NID_id_cmc_identification, 8, &so[2435]},
+    {"id-cmc-identityProof", "id-cmc-identityProof", NID_id_cmc_identityProof, 8, &so[2443]},
+    {"id-cmc-dataReturn", "id-cmc-dataReturn", NID_id_cmc_dataReturn, 8, &so[2451]},
+    {"id-cmc-transactionId", "id-cmc-transactionId", NID_id_cmc_transactionId, 8, &so[2459]},
+    {"id-cmc-senderNonce", "id-cmc-senderNonce", NID_id_cmc_senderNonce, 8, &so[2467]},
+    {"id-cmc-recipientNonce", "id-cmc-recipientNonce", NID_id_cmc_recipientNonce, 8, &so[2475]},
+    {"id-cmc-addExtensions", "id-cmc-addExtensions", NID_id_cmc_addExtensions, 8, &so[2483]},
+    {"id-cmc-encryptedPOP", "id-cmc-encryptedPOP", NID_id_cmc_encryptedPOP, 8, &so[2491]},
+    {"id-cmc-decryptedPOP", "id-cmc-decryptedPOP", NID_id_cmc_decryptedPOP, 8, &so[2499]},
+    {"id-cmc-lraPOPWitness", "id-cmc-lraPOPWitness", NID_id_cmc_lraPOPWitness, 8, &so[2507]},
+    {"id-cmc-getCert", "id-cmc-getCert", NID_id_cmc_getCert, 8, &so[2515]},
+    {"id-cmc-getCRL", "id-cmc-getCRL", NID_id_cmc_getCRL, 8, &so[2523]},
+    {"id-cmc-revokeRequest", "id-cmc-revokeRequest", NID_id_cmc_revokeRequest, 8, &so[2531]},
+    {"id-cmc-regInfo", "id-cmc-regInfo", NID_id_cmc_regInfo, 8, &so[2539]},
+    {"id-cmc-responseInfo", "id-cmc-responseInfo", NID_id_cmc_responseInfo, 8, &so[2547]},
+    {"id-cmc-queryPending", "id-cmc-queryPending", NID_id_cmc_queryPending, 8, &so[2555]},
+    {"id-cmc-popLinkRandom", "id-cmc-popLinkRandom", NID_id_cmc_popLinkRandom, 8, &so[2563]},
+    {"id-cmc-popLinkWitness", "id-cmc-popLinkWitness", NID_id_cmc_popLinkWitness, 8, &so[2571]},
+    {"id-cmc-confirmCertAcceptance", "id-cmc-confirmCertAcceptance", NID_id_cmc_confirmCertAcceptance, 8, &so[2579]},
+    {"id-on-personalData", "id-on-personalData", NID_id_on_personalData, 8, &so[2587]},
+    {"id-pda-dateOfBirth", "id-pda-dateOfBirth", NID_id_pda_dateOfBirth, 8, &so[2595]},
+    {"id-pda-placeOfBirth", "id-pda-placeOfBirth", NID_id_pda_placeOfBirth, 8, &so[2603]},
+    { NULL, NULL, NID_undef },
+    {"id-pda-gender", "id-pda-gender", NID_id_pda_gender, 8, &so[2611]},
+    {"id-pda-countryOfCitizenship", "id-pda-countryOfCitizenship", NID_id_pda_countryOfCitizenship, 8, &so[2619]},
+    {"id-pda-countryOfResidence", "id-pda-countryOfResidence", NID_id_pda_countryOfResidence, 8, &so[2627]},
+    {"id-aca-authenticationInfo", "id-aca-authenticationInfo", NID_id_aca_authenticationInfo, 8, &so[2635]},
+    {"id-aca-accessIdentity", "id-aca-accessIdentity", NID_id_aca_accessIdentity, 8, &so[2643]},
+    {"id-aca-chargingIdentity", "id-aca-chargingIdentity", NID_id_aca_chargingIdentity, 8, &so[2651]},
+    {"id-aca-group", "id-aca-group", NID_id_aca_group, 8, &so[2659]},
+    {"id-aca-role", "id-aca-role", NID_id_aca_role, 8, &so[2667]},
+    {"id-qcs-pkixQCSyntax-v1", "id-qcs-pkixQCSyntax-v1", NID_id_qcs_pkixQCSyntax_v1, 8, &so[2675]},
+    {"id-cct-crs", "id-cct-crs", NID_id_cct_crs, 8, &so[2683]},
+    {"id-cct-PKIData", "id-cct-PKIData", NID_id_cct_PKIData, 8, &so[2691]},
+    {"id-cct-PKIResponse", "id-cct-PKIResponse", NID_id_cct_PKIResponse, 8, &so[2699]},
+    {"ad_timestamping", "AD Time Stamping", NID_ad_timeStamping, 8, &so[2707]},
+    {"AD_DVCS", "ad dvcs", NID_ad_dvcs, 8, &so[2715]},
+    {"basicOCSPResponse", "Basic OCSP Response", NID_id_pkix_OCSP_basic, 9, &so[2723]},
+    {"Nonce", "OCSP Nonce", NID_id_pkix_OCSP_Nonce, 9, &so[2732]},
+    {"CrlID", "OCSP CRL ID", NID_id_pkix_OCSP_CrlID, 9, &so[2741]},
+    {"acceptableResponses", "Acceptable OCSP Responses", NID_id_pkix_OCSP_acceptableResponses, 9, &so[2750]},
+    {"noCheck", "OCSP No Check", NID_id_pkix_OCSP_noCheck, 9, &so[2759]},
+    {"archiveCutoff", "OCSP Archive Cutoff", NID_id_pkix_OCSP_archiveCutoff, 9, &so[2768]},
+    {"serviceLocator", "OCSP Service Locator", NID_id_pkix_OCSP_serviceLocator, 9, &so[2777]},
+    {"extendedStatus", "Extended OCSP Status", NID_id_pkix_OCSP_extendedStatus, 9, &so[2786]},
+    {"valid", "valid", NID_id_pkix_OCSP_valid, 9, &so[2795]},
+    {"path", "path", NID_id_pkix_OCSP_path, 9, &so[2804]},
+    {"trustRoot", "Trust Root", NID_id_pkix_OCSP_trustRoot, 9, &so[2813]},
+    {"algorithm", "algorithm", NID_algorithm, 4, &so[2822]},
+    {"rsaSignature", "rsaSignature", NID_rsaSignature, 5, &so[2826]},
+    {"X500algorithms", "directory services - algorithms", NID_X500algorithms, 2, &so[2831]},
+    {"ORG", "org", NID_org, 1, &so[2833]},
+    {"DOD", "dod", NID_dod, 2, &so[2834]},
+    {"IANA", "iana", NID_iana, 3, &so[2836]},
+    {"directory", "Directory", NID_Directory, 4, &so[2839]},
+    {"mgmt", "Management", NID_Management, 4, &so[2843]},
+    {"experimental", "Experimental", NID_Experimental, 4, &so[2847]},
+    {"private", "Private", NID_Private, 4, &so[2851]},
+    {"security", "Security", NID_Security, 4, &so[2855]},
+    {"snmpv2", "SNMPv2", NID_SNMPv2, 4, &so[2859]},
+    {"Mail", "Mail", NID_Mail, 4, &so[2863]},
+    {"enterprises", "Enterprises", NID_Enterprises, 5, &so[2867]},
+    {"dcobject", "dcObject", NID_dcObject, 9, &so[2872]},
+    {"DC", "domainComponent", NID_domainComponent, 10, &so[2881]},
+    {"domain", "Domain", NID_Domain, 10, &so[2891]},
+    {"NULL", "NULL", NID_joint_iso_ccitt},
+    {"selected-attribute-types", "Selected Attribute Types", NID_selected_attribute_types, 3, &so[2901]},
+    {"clearance", "clearance", NID_clearance, 4, &so[2904]},
+    {"RSA-MD4", "md4WithRSAEncryption", NID_md4WithRSAEncryption, 9, &so[2908]},
+    {"ac-proxying", "ac-proxying", NID_ac_proxying, 8, &so[2917]},
+    {"subjectInfoAccess", "Subject Information Access", NID_sinfo_access, 8, &so[2925]},
+    {"id-aca-encAttrs", "id-aca-encAttrs", NID_id_aca_encAttrs, 8, &so[2933]},
+    {"role", "role", NID_role, 3, &so[2941]},
+    {"policyConstraints", "X509v3 Policy Constraints", NID_policy_constraints, 3, &so[2944]},
+    {"targetInformation", "X509v3 AC Targeting", NID_target_information, 3, &so[2947]},
+    {"noRevAvail", "X509v3 No Revocation Available", NID_no_rev_avail, 3, &so[2950]},
+    {"NULL", "NULL", NID_ccitt},
+    {"ansi-X9-62", "ANSI X9.62", NID_ansi_X9_62, 5, &so[2953]},
+    {"prime-field", "prime-field", NID_X9_62_prime_field, 7, &so[2958]},
+    {"characteristic-two-field", "characteristic-two-field", NID_X9_62_characteristic_two_field, 7, &so[2965]},
+    {"id-ecPublicKey", "id-ecPublicKey", NID_X9_62_id_ecPublicKey, 7, &so[2972]},
+    {"prime192v1", "prime192v1", NID_X9_62_prime192v1, 8, &so[2979]},
+    {"prime192v2", "prime192v2", NID_X9_62_prime192v2, 8, &so[2987]},
+    {"prime192v3", "prime192v3", NID_X9_62_prime192v3, 8, &so[2995]},
+    {"prime239v1", "prime239v1", NID_X9_62_prime239v1, 8, &so[3003]},
+    {"prime239v2", "prime239v2", NID_X9_62_prime239v2, 8, &so[3011]},
+    {"prime239v3", "prime239v3", NID_X9_62_prime239v3, 8, &so[3019]},
+    {"prime256v1", "prime256v1", NID_X9_62_prime256v1, 8, &so[3027]},
+    {"ecdsa-with-SHA1", "ecdsa-with-SHA1", NID_ecdsa_with_SHA1, 7, &so[3035]},
+    {"CSPName", "Microsoft CSP Name", NID_ms_csp_name, 9, &so[3042]},
+    {"AES-128-ECB", "aes-128-ecb", NID_aes_128_ecb, 9, &so[3051]},
+    {"AES-128-CBC", "aes-128-cbc", NID_aes_128_cbc, 9, &so[3060]},
+    {"AES-128-OFB", "aes-128-ofb", NID_aes_128_ofb128, 9, &so[3069]},
+    {"AES-128-CFB", "aes-128-cfb", NID_aes_128_cfb128, 9, &so[3078]},
+    {"AES-192-ECB", "aes-192-ecb", NID_aes_192_ecb, 9, &so[3087]},
+    {"AES-192-CBC", "aes-192-cbc", NID_aes_192_cbc, 9, &so[3096]},
+    {"AES-192-OFB", "aes-192-ofb", NID_aes_192_ofb128, 9, &so[3105]},
+    {"AES-192-CFB", "aes-192-cfb", NID_aes_192_cfb128, 9, &so[3114]},
+    {"AES-256-ECB", "aes-256-ecb", NID_aes_256_ecb, 9, &so[3123]},
+    {"AES-256-CBC", "aes-256-cbc", NID_aes_256_cbc, 9, &so[3132]},
+    {"AES-256-OFB", "aes-256-ofb", NID_aes_256_ofb128, 9, &so[3141]},
+    {"AES-256-CFB", "aes-256-cfb", NID_aes_256_cfb128, 9, &so[3150]},
+    {"holdInstructionCode", "Hold Instruction Code", NID_hold_instruction_code, 3, &so[3159]},
+    {"holdInstructionNone", "Hold Instruction None", NID_hold_instruction_none, 7, &so[3162]},
+    {"holdInstructionCallIssuer", "Hold Instruction Call Issuer", NID_hold_instruction_call_issuer, 7, &so[3169]},
+    {"holdInstructionReject", "Hold Instruction Reject", NID_hold_instruction_reject, 7, &so[3176]},
+    {"data", "data", NID_data, 1, &so[3183]},
+    {"pss", "pss", NID_pss, 3, &so[3184]},
+    {"ucl", "ucl", NID_ucl, 7, &so[3187]},
+    {"pilot", "pilot", NID_pilot, 8, &so[3194]},
+    {"pilotAttributeType", "pilotAttributeType", NID_pilotAttributeType, 9, &so[3202]},
+    {"pilotAttributeSyntax", "pilotAttributeSyntax", NID_pilotAttributeSyntax, 9, &so[3211]},
+    {"pilotObjectClass", "pilotObjectClass", NID_pilotObjectClass, 9, &so[3220]},
+    {"pilotGroups", "pilotGroups", NID_pilotGroups, 9, &so[3229]},
+    {"iA5StringSyntax", "iA5StringSyntax", NID_iA5StringSyntax, 10, &so[3238]},
+    {"caseIgnoreIA5StringSyntax", "caseIgnoreIA5StringSyntax", NID_caseIgnoreIA5StringSyntax, 10, &so[3248]},
+    {"pilotObject", "pilotObject", NID_pilotObject, 10, &so[3258]},
+    {"pilotPerson", "pilotPerson", NID_pilotPerson, 10, &so[3268]},
+    {"account", "account", NID_account, 10, &so[3278]},
+    {"document", "document", NID_document, 10, &so[3288]},
+    {"room", "room", NID_room, 10, &so[3298]},
+    {"documentSeries", "documentSeries", NID_documentSeries, 10, &so[3308]},
+    {"rFC822localPart", "rFC822localPart", NID_rFC822localPart, 10, &so[3318]},
+    {"dNSDomain", "dNSDomain", NID_dNSDomain, 10, &so[3328]},
+    {"domainRelatedObject", "domainRelatedObject", NID_domainRelatedObject, 10, &so[3338]},
+    {"friendlyCountry", "friendlyCountry", NID_friendlyCountry, 10, &so[3348]},
+    {"simpleSecurityObject", "simpleSecurityObject", NID_simpleSecurityObject, 10, &so[3358]},
+    {"pilotOrganization", "pilotOrganization", NID_pilotOrganization, 10, &so[3368]},
+    {"pilotDSA", "pilotDSA", NID_pilotDSA, 10, &so[3378]},
+    {"qualityLabelledData", "qualityLabelledData", NID_qualityLabelledData, 10, &so[3388]},
+    {"UID", "userId", NID_userId, 10, &so[3398]},
+    {"textEncodedORAddress", "textEncodedORAddress", NID_textEncodedORAddress, 10, &so[3408]},
+    {"mail", "rfc822Mailbox", NID_rfc822Mailbox, 10, &so[3418]},
+    {"info", "info", NID_info, 10, &so[3428]},
+    {"favouriteDrink", "favouriteDrink", NID_favouriteDrink, 10, &so[3438]},
+    {"roomNumber", "roomNumber", NID_roomNumber, 10, &so[3448]},
+    {"photo", "photo", NID_photo, 10, &so[3458]},
+    {"userClass", "userClass", NID_userClass, 10, &so[3468]},
+    {"host", "host", NID_host, 10, &so[3478]},
+    {"manager", "manager", NID_manager, 10, &so[3488]},
+    {"documentIdentifier", "documentIdentifier", NID_documentIdentifier, 10, &so[3498]},
+    {"documentTitle", "documentTitle", NID_documentTitle, 10, &so[3508]},
+    {"documentVersion", "documentVersion", NID_documentVersion, 10, &so[3518]},
+    {"documentAuthor", "documentAuthor", NID_documentAuthor, 10, &so[3528]},
+    {"documentLocation", "documentLocation", NID_documentLocation, 10, &so[3538]},
+    {"homeTelephoneNumber", "homeTelephoneNumber", NID_homeTelephoneNumber, 10, &so[3548]},
+    {"secretary", "secretary", NID_secretary, 10, &so[3558]},
+    {"otherMailbox", "otherMailbox", NID_otherMailbox, 10, &so[3568]},
+    {"lastModifiedTime", "lastModifiedTime", NID_lastModifiedTime, 10, &so[3578]},
+    {"lastModifiedBy", "lastModifiedBy", NID_lastModifiedBy, 10, &so[3588]},
+    {"aRecord", "aRecord", NID_aRecord, 10, &so[3598]},
+    {"pilotAttributeType27", "pilotAttributeType27", NID_pilotAttributeType27, 10, &so[3608]},
+    {"mXRecord", "mXRecord", NID_mXRecord, 10, &so[3618]},
+    {"nSRecord", "nSRecord", NID_nSRecord, 10, &so[3628]},
+    {"sOARecord", "sOARecord", NID_sOARecord, 10, &so[3638]},
+    {"cNAMERecord", "cNAMERecord", NID_cNAMERecord, 10, &so[3648]},
+    {"associatedDomain", "associatedDomain", NID_associatedDomain, 10, &so[3658]},
+    {"associatedName", "associatedName", NID_associatedName, 10, &so[3668]},
+    {"homePostalAddress", "homePostalAddress", NID_homePostalAddress, 10, &so[3678]},
+    {"personalTitle", "personalTitle", NID_personalTitle, 10, &so[3688]},
+    {"mobileTelephoneNumber", "mobileTelephoneNumber", NID_mobileTelephoneNumber, 10, &so[3698]},
+    {"pagerTelephoneNumber", "pagerTelephoneNumber", NID_pagerTelephoneNumber, 10, &so[3708]},
+    {"friendlyCountryName", "friendlyCountryName", NID_friendlyCountryName, 10, &so[3718]},
+    {"organizationalStatus", "organizationalStatus", NID_organizationalStatus, 10, &so[3728]},
+    {"janetMailbox", "janetMailbox", NID_janetMailbox, 10, &so[3738]},
+    {"mailPreferenceOption", "mailPreferenceOption", NID_mailPreferenceOption, 10, &so[3748]},
+    {"buildingName", "buildingName", NID_buildingName, 10, &so[3758]},
+    {"dSAQuality", "dSAQuality", NID_dSAQuality, 10, &so[3768]},
+    {"singleLevelQuality", "singleLevelQuality", NID_singleLevelQuality, 10, &so[3778]},
+    {"subtreeMinimumQuality", "subtreeMinimumQuality", NID_subtreeMinimumQuality, 10, &so[3788]},
+    {"subtreeMaximumQuality", "subtreeMaximumQuality", NID_subtreeMaximumQuality, 10, &so[3798]},
+    {"personalSignature", "personalSignature", NID_personalSignature, 10, &so[3808]},
+    {"dITRedirect", "dITRedirect", NID_dITRedirect, 10, &so[3818]},
+    {"audio", "audio", NID_audio, 10, &so[3828]},
+    {"documentPublisher", "documentPublisher", NID_documentPublisher, 10, &so[3838]},
+    {"x500UniqueIdentifier", "x500UniqueIdentifier", NID_x500UniqueIdentifier, 3, &so[3848]},
+    {"mime-mhs", "MIME MHS", NID_mime_mhs, 5, &so[3851]},
+    {"mime-mhs-headings", "mime-mhs-headings", NID_mime_mhs_headings, 6, &so[3856]},
+    {"mime-mhs-bodies", "mime-mhs-bodies", NID_mime_mhs_bodies, 6, &so[3862]},
+    {"id-hex-partial-message", "id-hex-partial-message", NID_id_hex_partial_message, 7, &so[3868]},
+    {"id-hex-multipart-message", "id-hex-multipart-message", NID_id_hex_multipart_message, 7, &so[3875]},
+    {"generationQualifier", "generationQualifier", NID_generationQualifier, 3, &so[3882]},
+    {"pseudonym", "pseudonym", NID_pseudonym, 3, &so[3885]},
+    { NULL, NULL, NID_undef },
+    {"id-set", "Secure Electronic Transactions", NID_id_set, 2, &so[3888]},
+    {"set-ctype", "content types", NID_set_ctype, 3, &so[3890]},
+    {"set-msgExt", "message extensions", NID_set_msgExt, 3, &so[3893]},
+    {"set-attr", "set-attr", NID_set_attr, 3, &so[3896]},
+    {"set-policy", "set-policy", NID_set_policy, 3, &so[3899]},
+    {"set-certExt", "certificate extensions", NID_set_certExt, 3, &so[3902]},
+    {"set-brand", "set-brand", NID_set_brand, 3, &so[3905]},
+    {"setct-PANData", "setct-PANData", NID_setct_PANData, 4, &so[3908]},
+    {"setct-PANToken", "setct-PANToken", NID_setct_PANToken, 4, &so[3912]},
+    {"setct-PANOnly", "setct-PANOnly", NID_setct_PANOnly, 4, &so[3916]},
+    {"setct-OIData", "setct-OIData", NID_setct_OIData, 4, &so[3920]},
+    {"setct-PI", "setct-PI", NID_setct_PI, 4, &so[3924]},
+    {"setct-PIData", "setct-PIData", NID_setct_PIData, 4, &so[3928]},
+    {"setct-PIDataUnsigned", "setct-PIDataUnsigned", NID_setct_PIDataUnsigned, 4, &so[3932]},
+    {"setct-HODInput", "setct-HODInput", NID_setct_HODInput, 4, &so[3936]},
+    {"setct-AuthResBaggage", "setct-AuthResBaggage", NID_setct_AuthResBaggage, 4, &so[3940]},
+    {"setct-AuthRevReqBaggage", "setct-AuthRevReqBaggage", NID_setct_AuthRevReqBaggage, 4, &so[3944]},
+    {"setct-AuthRevResBaggage", "setct-AuthRevResBaggage", NID_setct_AuthRevResBaggage, 4, &so[3948]},
+    {"setct-CapTokenSeq", "setct-CapTokenSeq", NID_setct_CapTokenSeq, 4, &so[3952]},
+    {"setct-PInitResData", "setct-PInitResData", NID_setct_PInitResData, 4, &so[3956]},
+    {"setct-PI-TBS", "setct-PI-TBS", NID_setct_PI_TBS, 4, &so[3960]},
+    {"setct-PResData", "setct-PResData", NID_setct_PResData, 4, &so[3964]},
+    {"setct-AuthReqTBS", "setct-AuthReqTBS", NID_setct_AuthReqTBS, 4, &so[3968]},
+    {"setct-AuthResTBS", "setct-AuthResTBS", NID_setct_AuthResTBS, 4, &so[3972]},
+    {"setct-AuthResTBSX", "setct-AuthResTBSX", NID_setct_AuthResTBSX, 4, &so[3976]},
+    {"setct-AuthTokenTBS", "setct-AuthTokenTBS", NID_setct_AuthTokenTBS, 4, &so[3980]},
+    {"setct-CapTokenData", "setct-CapTokenData", NID_setct_CapTokenData, 4, &so[3984]},
+    {"setct-CapTokenTBS", "setct-CapTokenTBS", NID_setct_CapTokenTBS, 4, &so[3988]},
+    {"setct-AcqCardCodeMsg", "setct-AcqCardCodeMsg", NID_setct_AcqCardCodeMsg, 4, &so[3992]},
+    {"setct-AuthRevReqTBS", "setct-AuthRevReqTBS", NID_setct_AuthRevReqTBS, 4, &so[3996]},
+    {"setct-AuthRevResData", "setct-AuthRevResData", NID_setct_AuthRevResData, 4, &so[4000]},
+    {"setct-AuthRevResTBS", "setct-AuthRevResTBS", NID_setct_AuthRevResTBS, 4, &so[4004]},
+    {"setct-CapReqTBS", "setct-CapReqTBS", NID_setct_CapReqTBS, 4, &so[4008]},
+    {"setct-CapReqTBSX", "setct-CapReqTBSX", NID_setct_CapReqTBSX, 4, &so[4012]},
+    {"setct-CapResData", "setct-CapResData", NID_setct_CapResData, 4, &so[4016]},
+    {"setct-CapRevReqTBS", "setct-CapRevReqTBS", NID_setct_CapRevReqTBS, 4, &so[4020]},
+    {"setct-CapRevReqTBSX", "setct-CapRevReqTBSX", NID_setct_CapRevReqTBSX, 4, &so[4024]},
+    {"setct-CapRevResData", "setct-CapRevResData", NID_setct_CapRevResData, 4, &so[4028]},
+    {"setct-CredReqTBS", "setct-CredReqTBS", NID_setct_CredReqTBS, 4, &so[4032]},
+    {"setct-CredReqTBSX", "setct-CredReqTBSX", NID_setct_CredReqTBSX, 4, &so[4036]},
+    {"setct-CredResData", "setct-CredResData", NID_setct_CredResData, 4, &so[4040]},
+    {"setct-CredRevReqTBS", "setct-CredRevReqTBS", NID_setct_CredRevReqTBS, 4, &so[4044]},
+    {"setct-CredRevReqTBSX", "setct-CredRevReqTBSX", NID_setct_CredRevReqTBSX, 4, &so[4048]},
+    {"setct-CredRevResData", "setct-CredRevResData", NID_setct_CredRevResData, 4, &so[4052]},
+    {"setct-PCertReqData", "setct-PCertReqData", NID_setct_PCertReqData, 4, &so[4056]},
+    {"setct-PCertResTBS", "setct-PCertResTBS", NID_setct_PCertResTBS, 4, &so[4060]},
+    {"setct-BatchAdminReqData", "setct-BatchAdminReqData", NID_setct_BatchAdminReqData, 4, &so[4064]},
+    {"setct-BatchAdminResData", "setct-BatchAdminResData", NID_setct_BatchAdminResData, 4, &so[4068]},
+    {"setct-CardCInitResTBS", "setct-CardCInitResTBS", NID_setct_CardCInitResTBS, 4, &so[4072]},
+    {"setct-MeAqCInitResTBS", "setct-MeAqCInitResTBS", NID_setct_MeAqCInitResTBS, 4, &so[4076]},
+    {"setct-RegFormResTBS", "setct-RegFormResTBS", NID_setct_RegFormResTBS, 4, &so[4080]},
+    {"setct-CertReqData", "setct-CertReqData", NID_setct_CertReqData, 4, &so[4084]},
+    {"setct-CertReqTBS", "setct-CertReqTBS", NID_setct_CertReqTBS, 4, &so[4088]},
+    {"setct-CertResData", "setct-CertResData", NID_setct_CertResData, 4, &so[4092]},
+    {"setct-CertInqReqTBS", "setct-CertInqReqTBS", NID_setct_CertInqReqTBS, 4, &so[4096]},
+    {"setct-ErrorTBS", "setct-ErrorTBS", NID_setct_ErrorTBS, 4, &so[4100]},
+    {"setct-PIDualSignedTBE", "setct-PIDualSignedTBE", NID_setct_PIDualSignedTBE, 4, &so[4104]},
+    {"setct-PIUnsignedTBE", "setct-PIUnsignedTBE", NID_setct_PIUnsignedTBE, 4, &so[4108]},
+    {"setct-AuthReqTBE", "setct-AuthReqTBE", NID_setct_AuthReqTBE, 4, &so[4112]},
+    {"setct-AuthResTBE", "setct-AuthResTBE", NID_setct_AuthResTBE, 4, &so[4116]},
+    {"setct-AuthResTBEX", "setct-AuthResTBEX", NID_setct_AuthResTBEX, 4, &so[4120]},
+    {"setct-AuthTokenTBE", "setct-AuthTokenTBE", NID_setct_AuthTokenTBE, 4, &so[4124]},
+    {"setct-CapTokenTBE", "setct-CapTokenTBE", NID_setct_CapTokenTBE, 4, &so[4128]},
+    {"setct-CapTokenTBEX", "setct-CapTokenTBEX", NID_setct_CapTokenTBEX, 4, &so[4132]},
+    {"setct-AcqCardCodeMsgTBE", "setct-AcqCardCodeMsgTBE", NID_setct_AcqCardCodeMsgTBE, 4, &so[4136]},
+    {"setct-AuthRevReqTBE", "setct-AuthRevReqTBE", NID_setct_AuthRevReqTBE, 4, &so[4140]},
+    {"setct-AuthRevResTBE", "setct-AuthRevResTBE", NID_setct_AuthRevResTBE, 4, &so[4144]},
+    {"setct-AuthRevResTBEB", "setct-AuthRevResTBEB", NID_setct_AuthRevResTBEB, 4, &so[4148]},
+    {"setct-CapReqTBE", "setct-CapReqTBE", NID_setct_CapReqTBE, 4, &so[4152]},
+    {"setct-CapReqTBEX", "setct-CapReqTBEX", NID_setct_CapReqTBEX, 4, &so[4156]},
+    {"setct-CapResTBE", "setct-CapResTBE", NID_setct_CapResTBE, 4, &so[4160]},
+    {"setct-CapRevReqTBE", "setct-CapRevReqTBE", NID_setct_CapRevReqTBE, 4, &so[4164]},
+    {"setct-CapRevReqTBEX", "setct-CapRevReqTBEX", NID_setct_CapRevReqTBEX, 4, &so[4168]},
+    {"setct-CapRevResTBE", "setct-CapRevResTBE", NID_setct_CapRevResTBE, 4, &so[4172]},
+    {"setct-CredReqTBE", "setct-CredReqTBE", NID_setct_CredReqTBE, 4, &so[4176]},
+    {"setct-CredReqTBEX", "setct-CredReqTBEX", NID_setct_CredReqTBEX, 4, &so[4180]},
+    {"setct-CredResTBE", "setct-CredResTBE", NID_setct_CredResTBE, 4, &so[4184]},
+    {"setct-CredRevReqTBE", "setct-CredRevReqTBE", NID_setct_CredRevReqTBE, 4, &so[4188]},
+    {"setct-CredRevReqTBEX", "setct-CredRevReqTBEX", NID_setct_CredRevReqTBEX, 4, &so[4192]},
+    {"setct-CredRevResTBE", "setct-CredRevResTBE", NID_setct_CredRevResTBE, 4, &so[4196]},
+    {"setct-BatchAdminReqTBE", "setct-BatchAdminReqTBE", NID_setct_BatchAdminReqTBE, 4, &so[4200]},
+    {"setct-BatchAdminResTBE", "setct-BatchAdminResTBE", NID_setct_BatchAdminResTBE, 4, &so[4204]},
+    {"setct-RegFormReqTBE", "setct-RegFormReqTBE", NID_setct_RegFormReqTBE, 4, &so[4208]},
+    {"setct-CertReqTBE", "setct-CertReqTBE", NID_setct_CertReqTBE, 4, &so[4212]},
+    {"setct-CertReqTBEX", "setct-CertReqTBEX", NID_setct_CertReqTBEX, 4, &so[4216]},
+    {"setct-CertResTBE", "setct-CertResTBE", NID_setct_CertResTBE, 4, &so[4220]},
+    {"setct-CRLNotificationTBS", "setct-CRLNotificationTBS", NID_setct_CRLNotificationTBS, 4, &so[4224]},
+    {"setct-CRLNotificationResTBS", "setct-CRLNotificationResTBS", NID_setct_CRLNotificationResTBS, 4, &so[4228]},
+    {"setct-BCIDistributionTBS", "setct-BCIDistributionTBS", NID_setct_BCIDistributionTBS, 4, &so[4232]},
+    {"setext-genCrypt", "generic cryptogram", NID_setext_genCrypt, 4, &so[4236]},
+    {"setext-miAuth", "merchant initiated auth", NID_setext_miAuth, 4, &so[4240]},
+    {"setext-pinSecure", "setext-pinSecure", NID_setext_pinSecure, 4, &so[4244]},
+    {"setext-pinAny", "setext-pinAny", NID_setext_pinAny, 4, &so[4248]},
+    {"setext-track2", "setext-track2", NID_setext_track2, 4, &so[4252]},
+    {"setext-cv", "additional verification", NID_setext_cv, 4, &so[4256]},
+    {"set-policy-root", "set-policy-root", NID_set_policy_root, 4, &so[4260]},
+    {"setCext-hashedRoot", "setCext-hashedRoot", NID_setCext_hashedRoot, 4, &so[4264]},
+    {"setCext-certType", "setCext-certType", NID_setCext_certType, 4, &so[4268]},
+    {"setCext-merchData", "setCext-merchData", NID_setCext_merchData, 4, &so[4272]},
+    {"setCext-cCertRequired", "setCext-cCertRequired", NID_setCext_cCertRequired, 4, &so[4276]},
+    {"setCext-tunneling", "setCext-tunneling", NID_setCext_tunneling, 4, &so[4280]},
+    {"setCext-setExt", "setCext-setExt", NID_setCext_setExt, 4, &so[4284]},
+    {"setCext-setQualf", "setCext-setQualf", NID_setCext_setQualf, 4, &so[4288]},
+    {"setCext-PGWYcapabilities", "setCext-PGWYcapabilities", NID_setCext_PGWYcapabilities, 4, &so[4292]},
+    {"setCext-TokenIdentifier", "setCext-TokenIdentifier", NID_setCext_TokenIdentifier, 4, &so[4296]},
+    {"setCext-Track2Data", "setCext-Track2Data", NID_setCext_Track2Data, 4, &so[4300]},
+    {"setCext-TokenType", "setCext-TokenType", NID_setCext_TokenType, 4, &so[4304]},
+    {"setCext-IssuerCapabilities", "setCext-IssuerCapabilities", NID_setCext_IssuerCapabilities, 4, &so[4308]},
+    {"setAttr-Cert", "setAttr-Cert", NID_setAttr_Cert, 4, &so[4312]},
+    {"setAttr-PGWYcap", "payment gateway capabilities", NID_setAttr_PGWYcap, 4, &so[4316]},
+    {"setAttr-TokenType", "setAttr-TokenType", NID_setAttr_TokenType, 4, &so[4320]},
+    {"setAttr-IssCap", "issuer capabilities", NID_setAttr_IssCap, 4, &so[4324]},
+    {"set-rootKeyThumb", "set-rootKeyThumb", NID_set_rootKeyThumb, 5, &so[4328]},
+    {"set-addPolicy", "set-addPolicy", NID_set_addPolicy, 5, &so[4333]},
+    {"setAttr-Token-EMV", "setAttr-Token-EMV", NID_setAttr_Token_EMV, 5, &so[4338]},
+    {"setAttr-Token-B0Prime", "setAttr-Token-B0Prime", NID_setAttr_Token_B0Prime, 5, &so[4343]},
+    {"setAttr-IssCap-CVM", "setAttr-IssCap-CVM", NID_setAttr_IssCap_CVM, 5, &so[4348]},
+    {"setAttr-IssCap-T2", "setAttr-IssCap-T2", NID_setAttr_IssCap_T2, 5, &so[4353]},
+    {"setAttr-IssCap-Sig", "setAttr-IssCap-Sig", NID_setAttr_IssCap_Sig, 5, &so[4358]},
+    {"setAttr-GenCryptgrm", "generate cryptogram", NID_setAttr_GenCryptgrm, 6, &so[4363]},
+    {"setAttr-T2Enc", "encrypted track 2", NID_setAttr_T2Enc, 6, &so[4369]},
+    {"setAttr-T2cleartxt", "cleartext track 2", NID_setAttr_T2cleartxt, 6, &so[4375]},
+    {"setAttr-TokICCsig", "ICC or token signature", NID_setAttr_TokICCsig, 6, &so[4381]},
+    {"setAttr-SecDevSig", "secure device signature", NID_setAttr_SecDevSig, 6, &so[4387]},
+    {"set-brand-IATA-ATA", "set-brand-IATA-ATA", NID_set_brand_IATA_ATA, 4, &so[4393]},
+    {"set-brand-Diners", "set-brand-Diners", NID_set_brand_Diners, 4, &so[4397]},
+    {"set-brand-AmericanExpress", "set-brand-AmericanExpress", NID_set_brand_AmericanExpress, 4, &so[4401]},
+    {"set-brand-JCB", "set-brand-JCB", NID_set_brand_JCB, 4, &so[4405]},
+    {"set-brand-Visa", "set-brand-Visa", NID_set_brand_Visa, 4, &so[4409]},
+    {"set-brand-MasterCard", "set-brand-MasterCard", NID_set_brand_MasterCard, 4, &so[4413]},
+    {"set-brand-Novus", "set-brand-Novus", NID_set_brand_Novus, 5, &so[4417]},
+    {"DES-CDMF", "des-cdmf", NID_des_cdmf, 8, &so[4422]},
+    {"rsaOAEPEncryptionSET", "rsaOAEPEncryptionSET", NID_rsaOAEPEncryptionSET, 9, &so[4430]},
+    {"ITU-T", "itu-t", NID_itu_t},
+    {"JOINT-ISO-ITU-T", "joint-iso-itu-t", NID_joint_iso_itu_t},
+    {"international-organizations", "International Organizations", NID_international_organizations, 1, &so[4439]},
+    {"msSmartcardLogin", "Microsoft Smartcardlogin", NID_ms_smartcard_login, 10, &so[4440]},
+    {"msUPN", "Microsoft Universal Principal Name", NID_ms_upn, 10, &so[4450]},
+    {"AES-128-CFB1", "aes-128-cfb1", NID_aes_128_cfb1},
+    {"AES-192-CFB1", "aes-192-cfb1", NID_aes_192_cfb1},
+    {"AES-256-CFB1", "aes-256-cfb1", NID_aes_256_cfb1},
+    {"AES-128-CFB8", "aes-128-cfb8", NID_aes_128_cfb8},
+    {"AES-192-CFB8", "aes-192-cfb8", NID_aes_192_cfb8},
+    {"AES-256-CFB8", "aes-256-cfb8", NID_aes_256_cfb8},
+    {"DES-CFB1", "des-cfb1", NID_des_cfb1},
+    {"DES-CFB8", "des-cfb8", NID_des_cfb8},
+    {"DES-EDE3-CFB1", "des-ede3-cfb1", NID_des_ede3_cfb1},
+    {"DES-EDE3-CFB8", "des-ede3-cfb8", NID_des_ede3_cfb8},
+    {"street", "streetAddress", NID_streetAddress, 3, &so[4460]},
+    {"postalCode", "postalCode", NID_postalCode, 3, &so[4463]},
+    {"id-ppl", "id-ppl", NID_id_ppl, 7, &so[4466]},
+    {"proxyCertInfo", "Proxy Certificate Information", NID_proxyCertInfo, 8, &so[4473]},
+    {"id-ppl-anyLanguage", "Any language", NID_id_ppl_anyLanguage, 8, &so[4481]},
+    {"id-ppl-inheritAll", "Inherit all", NID_id_ppl_inheritAll, 8, &so[4489]},
+    {"nameConstraints", "X509v3 Name Constraints", NID_name_constraints, 3, &so[4497]},
+    {"id-ppl-independent", "Independent", NID_Independent, 8, &so[4500]},
+    {"RSA-SHA256", "sha256WithRSAEncryption", NID_sha256WithRSAEncryption, 9, &so[4508]},
+    {"RSA-SHA384", "sha384WithRSAEncryption", NID_sha384WithRSAEncryption, 9, &so[4517]},
+    {"RSA-SHA512", "sha512WithRSAEncryption", NID_sha512WithRSAEncryption, 9, &so[4526]},
+    {"RSA-SHA224", "sha224WithRSAEncryption", NID_sha224WithRSAEncryption, 9, &so[4535]},
+    {"SHA256", "sha256", NID_sha256, 9, &so[4544]},
+    {"SHA384", "sha384", NID_sha384, 9, &so[4553]},
+    {"SHA512", "sha512", NID_sha512, 9, &so[4562]},
+    {"SHA224", "sha224", NID_sha224, 9, &so[4571]},
+    {"identified-organization", "identified-organization", NID_identified_organization, 1, &so[4580]},
+    {"certicom-arc", "certicom-arc", NID_certicom_arc, 3, &so[4581]},
+    {"wap", "wap", NID_wap, 2, &so[4584]},
+    {"wap-wsg", "wap-wsg", NID_wap_wsg, 3, &so[4586]},
+    {"id-characteristic-two-basis", "id-characteristic-two-basis", NID_X9_62_id_characteristic_two_basis, 8, &so[4589]},
+    {"onBasis", "onBasis", NID_X9_62_onBasis, 9, &so[4597]},
+    {"tpBasis", "tpBasis", NID_X9_62_tpBasis, 9, &so[4606]},
+    {"ppBasis", "ppBasis", NID_X9_62_ppBasis, 9, &so[4615]},
+    {"c2pnb163v1", "c2pnb163v1", NID_X9_62_c2pnb163v1, 8, &so[4624]},
+    {"c2pnb163v2", "c2pnb163v2", NID_X9_62_c2pnb163v2, 8, &so[4632]},
+    {"c2pnb163v3", "c2pnb163v3", NID_X9_62_c2pnb163v3, 8, &so[4640]},
+    {"c2pnb176v1", "c2pnb176v1", NID_X9_62_c2pnb176v1, 8, &so[4648]},
+    {"c2tnb191v1", "c2tnb191v1", NID_X9_62_c2tnb191v1, 8, &so[4656]},
+    {"c2tnb191v2", "c2tnb191v2", NID_X9_62_c2tnb191v2, 8, &so[4664]},
+    {"c2tnb191v3", "c2tnb191v3", NID_X9_62_c2tnb191v3, 8, &so[4672]},
+    {"c2onb191v4", "c2onb191v4", NID_X9_62_c2onb191v4, 8, &so[4680]},
+    {"c2onb191v5", "c2onb191v5", NID_X9_62_c2onb191v5, 8, &so[4688]},
+    {"c2pnb208w1", "c2pnb208w1", NID_X9_62_c2pnb208w1, 8, &so[4696]},
+    {"c2tnb239v1", "c2tnb239v1", NID_X9_62_c2tnb239v1, 8, &so[4704]},
+    {"c2tnb239v2", "c2tnb239v2", NID_X9_62_c2tnb239v2, 8, &so[4712]},
+    {"c2tnb239v3", "c2tnb239v3", NID_X9_62_c2tnb239v3, 8, &so[4720]},
+    {"c2onb239v4", "c2onb239v4", NID_X9_62_c2onb239v4, 8, &so[4728]},
+    {"c2onb239v5", "c2onb239v5", NID_X9_62_c2onb239v5, 8, &so[4736]},
+    {"c2pnb272w1", "c2pnb272w1", NID_X9_62_c2pnb272w1, 8, &so[4744]},
+    {"c2pnb304w1", "c2pnb304w1", NID_X9_62_c2pnb304w1, 8, &so[4752]},
+    {"c2tnb359v1", "c2tnb359v1", NID_X9_62_c2tnb359v1, 8, &so[4760]},
+    {"c2pnb368w1", "c2pnb368w1", NID_X9_62_c2pnb368w1, 8, &so[4768]},
+    {"c2tnb431r1", "c2tnb431r1", NID_X9_62_c2tnb431r1, 8, &so[4776]},
+    {"secp112r1", "secp112r1", NID_secp112r1, 5, &so[4784]},
+    {"secp112r2", "secp112r2", NID_secp112r2, 5, &so[4789]},
+    {"secp128r1", "secp128r1", NID_secp128r1, 5, &so[4794]},
+    {"secp128r2", "secp128r2", NID_secp128r2, 5, &so[4799]},
+    {"secp160k1", "secp160k1", NID_secp160k1, 5, &so[4804]},
+    {"secp160r1", "secp160r1", NID_secp160r1, 5, &so[4809]},
+    {"secp160r2", "secp160r2", NID_secp160r2, 5, &so[4814]},
+    {"secp192k1", "secp192k1", NID_secp192k1, 5, &so[4819]},
+    {"secp224k1", "secp224k1", NID_secp224k1, 5, &so[4824]},
+    {"secp224r1", "secp224r1", NID_secp224r1, 5, &so[4829]},
+    {"secp256k1", "secp256k1", NID_secp256k1, 5, &so[4834]},
+    {"secp384r1", "secp384r1", NID_secp384r1, 5, &so[4839]},
+    {"secp521r1", "secp521r1", NID_secp521r1, 5, &so[4844]},
+    {"sect113r1", "sect113r1", NID_sect113r1, 5, &so[4849]},
+    {"sect113r2", "sect113r2", NID_sect113r2, 5, &so[4854]},
+    {"sect131r1", "sect131r1", NID_sect131r1, 5, &so[4859]},
+    {"sect131r2", "sect131r2", NID_sect131r2, 5, &so[4864]},
+    {"sect163k1", "sect163k1", NID_sect163k1, 5, &so[4869]},
+    {"sect163r1", "sect163r1", NID_sect163r1, 5, &so[4874]},
+    {"sect163r2", "sect163r2", NID_sect163r2, 5, &so[4879]},
+    {"sect193r1", "sect193r1", NID_sect193r1, 5, &so[4884]},
+    {"sect193r2", "sect193r2", NID_sect193r2, 5, &so[4889]},
+    {"sect233k1", "sect233k1", NID_sect233k1, 5, &so[4894]},
+    {"sect233r1", "sect233r1", NID_sect233r1, 5, &so[4899]},
+    {"sect239k1", "sect239k1", NID_sect239k1, 5, &so[4904]},
+    {"sect283k1", "sect283k1", NID_sect283k1, 5, &so[4909]},
+    {"sect283r1", "sect283r1", NID_sect283r1, 5, &so[4914]},
+    {"sect409k1", "sect409k1", NID_sect409k1, 5, &so[4919]},
+    {"sect409r1", "sect409r1", NID_sect409r1, 5, &so[4924]},
+    {"sect571k1", "sect571k1", NID_sect571k1, 5, &so[4929]},
+    {"sect571r1", "sect571r1", NID_sect571r1, 5, &so[4934]},
+    {"wap-wsg-idm-ecid-wtls1", "wap-wsg-idm-ecid-wtls1", NID_wap_wsg_idm_ecid_wtls1, 5, &so[4939]},
+    {"wap-wsg-idm-ecid-wtls3", "wap-wsg-idm-ecid-wtls3", NID_wap_wsg_idm_ecid_wtls3, 5, &so[4944]},
+    {"wap-wsg-idm-ecid-wtls4", "wap-wsg-idm-ecid-wtls4", NID_wap_wsg_idm_ecid_wtls4, 5, &so[4949]},
+    {"wap-wsg-idm-ecid-wtls5", "wap-wsg-idm-ecid-wtls5", NID_wap_wsg_idm_ecid_wtls5, 5, &so[4954]},
+    {"wap-wsg-idm-ecid-wtls6", "wap-wsg-idm-ecid-wtls6", NID_wap_wsg_idm_ecid_wtls6, 5, &so[4959]},
+    {"wap-wsg-idm-ecid-wtls7", "wap-wsg-idm-ecid-wtls7", NID_wap_wsg_idm_ecid_wtls7, 5, &so[4964]},
+    {"wap-wsg-idm-ecid-wtls8", "wap-wsg-idm-ecid-wtls8", NID_wap_wsg_idm_ecid_wtls8, 5, &so[4969]},
+    {"wap-wsg-idm-ecid-wtls9", "wap-wsg-idm-ecid-wtls9", NID_wap_wsg_idm_ecid_wtls9, 5, &so[4974]},
+    {"wap-wsg-idm-ecid-wtls10", "wap-wsg-idm-ecid-wtls10", NID_wap_wsg_idm_ecid_wtls10, 5, &so[4979]},
+    {"wap-wsg-idm-ecid-wtls11", "wap-wsg-idm-ecid-wtls11", NID_wap_wsg_idm_ecid_wtls11, 5, &so[4984]},
+    {"wap-wsg-idm-ecid-wtls12", "wap-wsg-idm-ecid-wtls12", NID_wap_wsg_idm_ecid_wtls12, 5, &so[4989]},
+    {"anyPolicy", "X509v3 Any Policy", NID_any_policy, 4, &so[4994]},
+    {"policyMappings", "X509v3 Policy Mappings", NID_policy_mappings, 3, &so[4998]},
+    {"inhibitAnyPolicy", "X509v3 Inhibit Any Policy", NID_inhibit_any_policy, 3, &so[5001]},
+    {"Oakley-EC2N-3", "ipsec3", NID_ipsec3},
+    {"Oakley-EC2N-4", "ipsec4", NID_ipsec4},
+    {"CAMELLIA-128-CBC", "camellia-128-cbc", NID_camellia_128_cbc, 11, &so[5004]},
+    {"CAMELLIA-192-CBC", "camellia-192-cbc", NID_camellia_192_cbc, 11, &so[5015]},
+    {"CAMELLIA-256-CBC", "camellia-256-cbc", NID_camellia_256_cbc, 11, &so[5026]},
+    {"CAMELLIA-128-ECB", "camellia-128-ecb", NID_camellia_128_ecb, 8, &so[5037]},
+    {"CAMELLIA-192-ECB", "camellia-192-ecb", NID_camellia_192_ecb, 8, &so[5045]},
+    {"CAMELLIA-256-ECB", "camellia-256-ecb", NID_camellia_256_ecb, 8, &so[5053]},
+    {"CAMELLIA-128-CFB", "camellia-128-cfb", NID_camellia_128_cfb128, 8, &so[5061]},
+    {"CAMELLIA-192-CFB", "camellia-192-cfb", NID_camellia_192_cfb128, 8, &so[5069]},
+    {"CAMELLIA-256-CFB", "camellia-256-cfb", NID_camellia_256_cfb128, 8, &so[5077]},
+    {"CAMELLIA-128-CFB1", "camellia-128-cfb1", NID_camellia_128_cfb1},
+    {"CAMELLIA-192-CFB1", "camellia-192-cfb1", NID_camellia_192_cfb1},
+    {"CAMELLIA-256-CFB1", "camellia-256-cfb1", NID_camellia_256_cfb1},
+    {"CAMELLIA-128-CFB8", "camellia-128-cfb8", NID_camellia_128_cfb8},
+    {"CAMELLIA-192-CFB8", "camellia-192-cfb8", NID_camellia_192_cfb8},
+    {"CAMELLIA-256-CFB8", "camellia-256-cfb8", NID_camellia_256_cfb8},
+    {"CAMELLIA-128-OFB", "camellia-128-ofb", NID_camellia_128_ofb128, 8, &so[5085]},
+    {"CAMELLIA-192-OFB", "camellia-192-ofb", NID_camellia_192_ofb128, 8, &so[5093]},
+    {"CAMELLIA-256-OFB", "camellia-256-ofb", NID_camellia_256_ofb128, 8, &so[5101]},
+    {"subjectDirectoryAttributes", "X509v3 Subject Directory Attributes", NID_subject_directory_attributes, 3, &so[5109]},
+    {"issuingDistributionPoint", "X509v3 Issuing Distribution Point", NID_issuing_distribution_point, 3, &so[5112]},
+    {"certificateIssuer", "X509v3 Certificate Issuer", NID_certificate_issuer, 3, &so[5115]},
+    { NULL, NULL, NID_undef },
+    {"KISA", "kisa", NID_kisa, 6, &so[5118]},
+    { NULL, NULL, NID_undef },
+    { NULL, NULL, NID_undef },
+    {"SEED-ECB", "seed-ecb", NID_seed_ecb, 8, &so[5124]},
+    {"SEED-CBC", "seed-cbc", NID_seed_cbc, 8, &so[5132]},
+    {"SEED-OFB", "seed-ofb", NID_seed_ofb128, 8, &so[5140]},
+    {"SEED-CFB", "seed-cfb", NID_seed_cfb128, 8, &so[5148]},
+    {"HMAC-MD5", "hmac-md5", NID_hmac_md5, 8, &so[5156]},
+    {"HMAC-SHA1", "hmac-sha1", NID_hmac_sha1, 8, &so[5164]},
+    {"id-PasswordBasedMAC", "password based MAC", NID_id_PasswordBasedMAC, 9, &so[5172]},
+    {"id-DHBasedMac", "Diffie-Hellman based MAC", NID_id_DHBasedMac, 9, &so[5181]},
+    {"id-it-suppLangTags", "id-it-suppLangTags", NID_id_it_suppLangTags, 8, &so[5190]},
+    {"caRepository", "CA Repository", NID_caRepository, 8, &so[5198]},
+    {"id-smime-ct-compressedData", "id-smime-ct-compressedData", NID_id_smime_ct_compressedData, 11, &so[5206]},
+    {"id-ct-asciiTextWithCRLF", "id-ct-asciiTextWithCRLF", NID_id_ct_asciiTextWithCRLF, 11, &so[5217]},
+    {"id-aes128-wrap", "id-aes128-wrap", NID_id_aes128_wrap, 9, &so[5228]},
+    {"id-aes192-wrap", "id-aes192-wrap", NID_id_aes192_wrap, 9, &so[5237]},
+    {"id-aes256-wrap", "id-aes256-wrap", NID_id_aes256_wrap, 9, &so[5246]},
+    {"ecdsa-with-Recommended", "ecdsa-with-Recommended", NID_ecdsa_with_Recommended, 7, &so[5255]},
+    {"ecdsa-with-Specified", "ecdsa-with-Specified", NID_ecdsa_with_Specified, 7, &so[5262]},
+    {"ecdsa-with-SHA224", "ecdsa-with-SHA224", NID_ecdsa_with_SHA224, 8, &so[5269]},
+    {"ecdsa-with-SHA256", "ecdsa-with-SHA256", NID_ecdsa_with_SHA256, 8, &so[5277]},
+    {"ecdsa-with-SHA384", "ecdsa-with-SHA384", NID_ecdsa_with_SHA384, 8, &so[5285]},
+    {"ecdsa-with-SHA512", "ecdsa-with-SHA512", NID_ecdsa_with_SHA512, 8, &so[5293]},
+    {"hmacWithMD5", "hmacWithMD5", NID_hmacWithMD5, 8, &so[5301]},
+    {"hmacWithSHA224", "hmacWithSHA224", NID_hmacWithSHA224, 8, &so[5309]},
+    {"hmacWithSHA256", "hmacWithSHA256", NID_hmacWithSHA256, 8, &so[5317]},
+    {"hmacWithSHA384", "hmacWithSHA384", NID_hmacWithSHA384, 8, &so[5325]},
+    {"hmacWithSHA512", "hmacWithSHA512", NID_hmacWithSHA512, 8, &so[5333]},
+    {"dsa_with_SHA224", "dsa_with_SHA224", NID_dsa_with_SHA224, 9, &so[5341]},
+    {"dsa_with_SHA256", "dsa_with_SHA256", NID_dsa_with_SHA256, 9, &so[5350]},
+    {"whirlpool", "whirlpool", NID_whirlpool, 6, &so[5359]},
+    {"cryptopro", "cryptopro", NID_cryptopro, 5, &so[5365]},
+    {"cryptocom", "cryptocom", NID_cryptocom, 5, &so[5370]},
+    {"id-GostR3411-94-with-GostR3410-2001", "GOST R 34.11-94 with GOST R 34.10-2001", NID_id_GostR3411_94_with_GostR3410_2001, 6, &so[5375]},
+    {"id-GostR3411-94-with-GostR3410-94", "GOST R 34.11-94 with GOST R 34.10-94", NID_id_GostR3411_94_with_GostR3410_94, 6, &so[5381]},
+    {"md_gost94", "GOST R 34.11-94", NID_id_GostR3411_94, 6, &so[5387]},
+    {"id-HMACGostR3411-94", "HMAC GOST 34.11-94", NID_id_HMACGostR3411_94, 6, &so[5393]},
+    {"gost2001", "GOST R 34.10-2001", NID_id_GostR3410_2001, 6, &so[5399]},
+    {"gost94", "GOST R 34.10-94", NID_id_GostR3410_94, 6, &so[5405]},
+    {"gost89", "GOST 28147-89", NID_id_Gost28147_89, 6, &so[5411]},
+    {"gost89-cnt", "gost89-cnt", NID_gost89_cnt},
+    {"gost-mac", "GOST 28147-89 MAC", NID_id_Gost28147_89_MAC, 6, &so[5417]},
+    {"prf-gostr3411-94", "GOST R 34.11-94 PRF", NID_id_GostR3411_94_prf, 6, &so[5423]},
+    {"id-GostR3410-2001DH", "GOST R 34.10-2001 DH", NID_id_GostR3410_2001DH, 6, &so[5429]},
+    {"id-GostR3410-94DH", "GOST R 34.10-94 DH", NID_id_GostR3410_94DH, 6, &so[5435]},
+    {"id-Gost28147-89-CryptoPro-KeyMeshing", "id-Gost28147-89-CryptoPro-KeyMeshing", NID_id_Gost28147_89_CryptoPro_KeyMeshing, 7, &so[5441]},
+    {"id-Gost28147-89-None-KeyMeshing", "id-Gost28147-89-None-KeyMeshing", NID_id_Gost28147_89_None_KeyMeshing, 7, &so[5448]},
+    {"id-GostR3411-94-TestParamSet", "id-GostR3411-94-TestParamSet", NID_id_GostR3411_94_TestParamSet, 7, &so[5455]},
+    {"id-GostR3411-94-CryptoProParamSet", "id-GostR3411-94-CryptoProParamSet", NID_id_GostR3411_94_CryptoProParamSet, 7, &so[5462]},
+    {"id-Gost28147-89-TestParamSet", "id-Gost28147-89-TestParamSet", NID_id_Gost28147_89_TestParamSet, 7, &so[5469]},
+    {"id-Gost28147-89-CryptoPro-A-ParamSet", "id-Gost28147-89-CryptoPro-A-ParamSet", NID_id_Gost28147_89_CryptoPro_A_ParamSet, 7, &so[5476]},
+    {"id-Gost28147-89-CryptoPro-B-ParamSet", "id-Gost28147-89-CryptoPro-B-ParamSet", NID_id_Gost28147_89_CryptoPro_B_ParamSet, 7, &so[5483]},
+    {"id-Gost28147-89-CryptoPro-C-ParamSet", "id-Gost28147-89-CryptoPro-C-ParamSet", NID_id_Gost28147_89_CryptoPro_C_ParamSet, 7, &so[5490]},
+    {"id-Gost28147-89-CryptoPro-D-ParamSet", "id-Gost28147-89-CryptoPro-D-ParamSet", NID_id_Gost28147_89_CryptoPro_D_ParamSet, 7, &so[5497]},
+    {"id-Gost28147-89-CryptoPro-Oscar-1-1-ParamSet", "id-Gost28147-89-CryptoPro-Oscar-1-1-ParamSet", NID_id_Gost28147_89_CryptoPro_Oscar_1_1_ParamSet, 7, &so[5504]},
+    {"id-Gost28147-89-CryptoPro-Oscar-1-0-ParamSet", "id-Gost28147-89-CryptoPro-Oscar-1-0-ParamSet", NID_id_Gost28147_89_CryptoPro_Oscar_1_0_ParamSet, 7, &so[5511]},
+    {"id-Gost28147-89-CryptoPro-RIC-1-ParamSet", "id-Gost28147-89-CryptoPro-RIC-1-ParamSet", NID_id_Gost28147_89_CryptoPro_RIC_1_ParamSet, 7, &so[5518]},
+    {"id-GostR3410-94-TestParamSet", "id-GostR3410-94-TestParamSet", NID_id_GostR3410_94_TestParamSet, 7, &so[5525]},
+    {"id-GostR3410-94-CryptoPro-A-ParamSet", "id-GostR3410-94-CryptoPro-A-ParamSet", NID_id_GostR3410_94_CryptoPro_A_ParamSet, 7, &so[5532]},
+    {"id-GostR3410-94-CryptoPro-B-ParamSet", "id-GostR3410-94-CryptoPro-B-ParamSet", NID_id_GostR3410_94_CryptoPro_B_ParamSet, 7, &so[5539]},
+    {"id-GostR3410-94-CryptoPro-C-ParamSet", "id-GostR3410-94-CryptoPro-C-ParamSet", NID_id_GostR3410_94_CryptoPro_C_ParamSet, 7, &so[5546]},
+    {"id-GostR3410-94-CryptoPro-D-ParamSet", "id-GostR3410-94-CryptoPro-D-ParamSet", NID_id_GostR3410_94_CryptoPro_D_ParamSet, 7, &so[5553]},
+    {"id-GostR3410-94-CryptoPro-XchA-ParamSet", "id-GostR3410-94-CryptoPro-XchA-ParamSet", NID_id_GostR3410_94_CryptoPro_XchA_ParamSet, 7, &so[5560]},
+    {"id-GostR3410-94-CryptoPro-XchB-ParamSet", "id-GostR3410-94-CryptoPro-XchB-ParamSet", NID_id_GostR3410_94_CryptoPro_XchB_ParamSet, 7, &so[5567]},
+    {"id-GostR3410-94-CryptoPro-XchC-ParamSet", "id-GostR3410-94-CryptoPro-XchC-ParamSet", NID_id_GostR3410_94_CryptoPro_XchC_ParamSet, 7, &so[5574]},
+    {"id-GostR3410-2001-TestParamSet", "id-GostR3410-2001-TestParamSet", NID_id_GostR3410_2001_TestParamSet, 7, &so[5581]},
+    {"id-GostR3410-2001-CryptoPro-A-ParamSet", "id-GostR3410-2001-CryptoPro-A-ParamSet", NID_id_GostR3410_2001_CryptoPro_A_ParamSet, 7, &so[5588]},
+    {"id-GostR3410-2001-CryptoPro-B-ParamSet", "id-GostR3410-2001-CryptoPro-B-ParamSet", NID_id_GostR3410_2001_CryptoPro_B_ParamSet, 7, &so[5595]},
+    {"id-GostR3410-2001-CryptoPro-C-ParamSet", "id-GostR3410-2001-CryptoPro-C-ParamSet", NID_id_GostR3410_2001_CryptoPro_C_ParamSet, 7, &so[5602]},
+    {"id-GostR3410-2001-CryptoPro-XchA-ParamSet", "id-GostR3410-2001-CryptoPro-XchA-ParamSet", NID_id_GostR3410_2001_CryptoPro_XchA_ParamSet, 7, &so[5609]},
+    {"id-GostR3410-2001-CryptoPro-XchB-ParamSet", "id-GostR3410-2001-CryptoPro-XchB-ParamSet", NID_id_GostR3410_2001_CryptoPro_XchB_ParamSet, 7, &so[5616]},
+    {"id-GostR3410-94-a", "id-GostR3410-94-a", NID_id_GostR3410_94_a, 7, &so[5623]},
+    {"id-GostR3410-94-aBis", "id-GostR3410-94-aBis", NID_id_GostR3410_94_aBis, 7, &so[5630]},
+    {"id-GostR3410-94-b", "id-GostR3410-94-b", NID_id_GostR3410_94_b, 7, &so[5637]},
+    {"id-GostR3410-94-bBis", "id-GostR3410-94-bBis", NID_id_GostR3410_94_bBis, 7, &so[5644]},
+    {"id-Gost28147-89-cc", "GOST 28147-89 Cryptocom ParamSet", NID_id_Gost28147_89_cc, 8, &so[5651]},
+    {"gost94cc", "GOST 34.10-94 Cryptocom", NID_id_GostR3410_94_cc, 8, &so[5659]},
+    {"gost2001cc", "GOST 34.10-2001 Cryptocom", NID_id_GostR3410_2001_cc, 8, &so[5667]},
+    {"id-GostR3411-94-with-GostR3410-94-cc", "GOST R 34.11-94 with GOST R 34.10-94 Cryptocom", NID_id_GostR3411_94_with_GostR3410_94_cc, 8, &so[5675]},
+    {"id-GostR3411-94-with-GostR3410-2001-cc", "GOST R 34.11-94 with GOST R 34.10-2001 Cryptocom", NID_id_GostR3411_94_with_GostR3410_2001_cc, 8, &so[5683]},
+    {"id-GostR3410-2001-ParamSet-cc", "GOST R 3410-2001 Parameter Set Cryptocom", NID_id_GostR3410_2001_ParamSet_cc, 8, &so[5691]},
+    {"HMAC", "hmac", NID_hmac},
+    {"LocalKeySet", "Microsoft Local Key set", NID_LocalKeySet, 9, &so[5699]},
+    {"freshestCRL", "X509v3 Freshest CRL", NID_freshest_crl, 3, &so[5708]},
+    {"id-on-permanentIdentifier", "Permanent Identifier", NID_id_on_permanentIdentifier, 8, &so[5711]},
+    {"searchGuide", "searchGuide", NID_searchGuide, 3, &so[5719]},
+    {"businessCategory", "businessCategory", NID_businessCategory, 3, &so[5722]},
+    {"postalAddress", "postalAddress", NID_postalAddress, 3, &so[5725]},
+    {"postOfficeBox", "postOfficeBox", NID_postOfficeBox, 3, &so[5728]},
+    {"physicalDeliveryOfficeName", "physicalDeliveryOfficeName", NID_physicalDeliveryOfficeName, 3, &so[5731]},
+    {"telephoneNumber", "telephoneNumber", NID_telephoneNumber, 3, &so[5734]},
+    {"telexNumber", "telexNumber", NID_telexNumber, 3, &so[5737]},
+    {"teletexTerminalIdentifier", "teletexTerminalIdentifier", NID_teletexTerminalIdentifier, 3, &so[5740]},
+    {"facsimileTelephoneNumber", "facsimileTelephoneNumber", NID_facsimileTelephoneNumber, 3, &so[5743]},
+    {"x121Address", "x121Address", NID_x121Address, 3, &so[5746]},
+    {"internationaliSDNNumber", "internationaliSDNNumber", NID_internationaliSDNNumber, 3, &so[5749]},
+    {"registeredAddress", "registeredAddress", NID_registeredAddress, 3, &so[5752]},
+    {"destinationIndicator", "destinationIndicator", NID_destinationIndicator, 3, &so[5755]},
+    {"preferredDeliveryMethod", "preferredDeliveryMethod", NID_preferredDeliveryMethod, 3, &so[5758]},
+    {"presentationAddress", "presentationAddress", NID_presentationAddress, 3, &so[5761]},
+    {"supportedApplicationContext", "supportedApplicationContext", NID_supportedApplicationContext, 3, &so[5764]},
+    {"member", "member", NID_member, 3, &so[5767]},
+    {"owner", "owner", NID_owner, 3, &so[5770]},
+    {"roleOccupant", "roleOccupant", NID_roleOccupant, 3, &so[5773]},
+    {"seeAlso", "seeAlso", NID_seeAlso, 3, &so[5776]},
+    {"userPassword", "userPassword", NID_userPassword, 3, &so[5779]},
+    {"userCertificate", "userCertificate", NID_userCertificate, 3, &so[5782]},
+    {"cACertificate", "cACertificate", NID_cACertificate, 3, &so[5785]},
+    {"authorityRevocationList", "authorityRevocationList", NID_authorityRevocationList, 3, &so[5788]},
+    {"certificateRevocationList", "certificateRevocationList", NID_certificateRevocationList, 3, &so[5791]},
+    {"crossCertificatePair", "crossCertificatePair", NID_crossCertificatePair, 3, &so[5794]},
+    {"enhancedSearchGuide", "enhancedSearchGuide", NID_enhancedSearchGuide, 3, &so[5797]},
+    {"protocolInformation", "protocolInformation", NID_protocolInformation, 3, &so[5800]},
+    {"distinguishedName", "distinguishedName", NID_distinguishedName, 3, &so[5803]},
+    {"uniqueMember", "uniqueMember", NID_uniqueMember, 3, &so[5806]},
+    {"houseIdentifier", "houseIdentifier", NID_houseIdentifier, 3, &so[5809]},
+    {"supportedAlgorithms", "supportedAlgorithms", NID_supportedAlgorithms, 3, &so[5812]},
+    {"deltaRevocationList", "deltaRevocationList", NID_deltaRevocationList, 3, &so[5815]},
+    {"dmdName", "dmdName", NID_dmdName, 3, &so[5818]},
+    {"id-alg-PWRI-KEK", "id-alg-PWRI-KEK", NID_id_alg_PWRI_KEK, 11, &so[5821]},
+    {"CMAC", "cmac", NID_cmac},
+    {"id-aes128-GCM", "aes-128-gcm", NID_aes_128_gcm, 9, &so[5832]},
+    {"id-aes128-CCM", "aes-128-ccm", NID_aes_128_ccm, 9, &so[5841]},
+    {"id-aes128-wrap-pad", "id-aes128-wrap-pad", NID_id_aes128_wrap_pad, 9, &so[5850]},
+    {"id-aes192-GCM", "aes-192-gcm", NID_aes_192_gcm, 9, &so[5859]},
+    {"id-aes192-CCM", "aes-192-ccm", NID_aes_192_ccm, 9, &so[5868]},
+    {"id-aes192-wrap-pad", "id-aes192-wrap-pad", NID_id_aes192_wrap_pad, 9, &so[5877]},
+    {"id-aes256-GCM", "aes-256-gcm", NID_aes_256_gcm, 9, &so[5886]},
+    {"id-aes256-CCM", "aes-256-ccm", NID_aes_256_ccm, 9, &so[5895]},
+    {"id-aes256-wrap-pad", "id-aes256-wrap-pad", NID_id_aes256_wrap_pad, 9, &so[5904]},
+    {"AES-128-CTR", "aes-128-ctr", NID_aes_128_ctr},
+    {"AES-192-CTR", "aes-192-ctr", NID_aes_192_ctr},
+    {"AES-256-CTR", "aes-256-ctr", NID_aes_256_ctr},
+    {"id-camellia128-wrap", "id-camellia128-wrap", NID_id_camellia128_wrap, 11, &so[5913]},
+    {"id-camellia192-wrap", "id-camellia192-wrap", NID_id_camellia192_wrap, 11, &so[5924]},
+    {"id-camellia256-wrap", "id-camellia256-wrap", NID_id_camellia256_wrap, 11, &so[5935]},
+    {"anyExtendedKeyUsage", "Any Extended Key Usage", NID_anyExtendedKeyUsage, 4, &so[5946]},
+    {"MGF1", "mgf1", NID_mgf1, 9, &so[5950]},
+    {"RSASSA-PSS", "rsassaPss", NID_rsassaPss, 9, &so[5959]},
+    {"AES-128-XTS", "aes-128-xts", NID_aes_128_xts},
+    {"AES-256-XTS", "aes-256-xts", NID_aes_256_xts},
+    {"RC4-HMAC-MD5", "rc4-hmac-md5", NID_rc4_hmac_md5},
+    {"AES-128-CBC-HMAC-SHA1", "aes-128-cbc-hmac-sha1", NID_aes_128_cbc_hmac_sha1},
+    {"AES-192-CBC-HMAC-SHA1", "aes-192-cbc-hmac-sha1", NID_aes_192_cbc_hmac_sha1},
+    {"AES-256-CBC-HMAC-SHA1", "aes-256-cbc-hmac-sha1", NID_aes_256_cbc_hmac_sha1},
+    {"RSAES-OAEP", "rsaesOaep", NID_rsaesOaep, 9, &so[5968]},
+    {"dhpublicnumber", "X9.42 DH", NID_dhpublicnumber, 7, &so[5977]},
+    {"brainpoolP160r1", "brainpoolP160r1", NID_brainpoolP160r1, 9, &so[5984]},
+    {"brainpoolP160t1", "brainpoolP160t1", NID_brainpoolP160t1, 9, &so[5993]},
+    {"brainpoolP192r1", "brainpoolP192r1", NID_brainpoolP192r1, 9, &so[6002]},
+    {"brainpoolP192t1", "brainpoolP192t1", NID_brainpoolP192t1, 9, &so[6011]},
+    {"brainpoolP224r1", "brainpoolP224r1", NID_brainpoolP224r1, 9, &so[6020]},
+    {"brainpoolP224t1", "brainpoolP224t1", NID_brainpoolP224t1, 9, &so[6029]},
+    {"brainpoolP256r1", "brainpoolP256r1", NID_brainpoolP256r1, 9, &so[6038]},
+    {"brainpoolP256t1", "brainpoolP256t1", NID_brainpoolP256t1, 9, &so[6047]},
+    {"brainpoolP320r1", "brainpoolP320r1", NID_brainpoolP320r1, 9, &so[6056]},
+    {"brainpoolP320t1", "brainpoolP320t1", NID_brainpoolP320t1, 9, &so[6065]},
+    {"brainpoolP384r1", "brainpoolP384r1", NID_brainpoolP384r1, 9, &so[6074]},
+    {"brainpoolP384t1", "brainpoolP384t1", NID_brainpoolP384t1, 9, &so[6083]},
+    {"brainpoolP512r1", "brainpoolP512r1", NID_brainpoolP512r1, 9, &so[6092]},
+    {"brainpoolP512t1", "brainpoolP512t1", NID_brainpoolP512t1, 9, &so[6101]},
+    {"PSPECIFIED", "pSpecified", NID_pSpecified, 9, &so[6110]},
+    {"dhSinglePass-stdDH-sha1kdf-scheme", "dhSinglePass-stdDH-sha1kdf-scheme", NID_dhSinglePass_stdDH_sha1kdf_scheme, 9, &so[6119]},
+    {"dhSinglePass-stdDH-sha224kdf-scheme", "dhSinglePass-stdDH-sha224kdf-scheme", NID_dhSinglePass_stdDH_sha224kdf_scheme, 6, &so[6128]},
+    {"dhSinglePass-stdDH-sha256kdf-scheme", "dhSinglePass-stdDH-sha256kdf-scheme", NID_dhSinglePass_stdDH_sha256kdf_scheme, 6, &so[6134]},
+    {"dhSinglePass-stdDH-sha384kdf-scheme", "dhSinglePass-stdDH-sha384kdf-scheme", NID_dhSinglePass_stdDH_sha384kdf_scheme, 6, &so[6140]},
+    {"dhSinglePass-stdDH-sha512kdf-scheme", "dhSinglePass-stdDH-sha512kdf-scheme", NID_dhSinglePass_stdDH_sha512kdf_scheme, 6, &so[6146]},
+    {"dhSinglePass-cofactorDH-sha1kdf-scheme", "dhSinglePass-cofactorDH-sha1kdf-scheme", NID_dhSinglePass_cofactorDH_sha1kdf_scheme, 9, &so[6152]},
+    {"dhSinglePass-cofactorDH-sha224kdf-scheme", "dhSinglePass-cofactorDH-sha224kdf-scheme", NID_dhSinglePass_cofactorDH_sha224kdf_scheme, 6, &so[6161]},
+    {"dhSinglePass-cofactorDH-sha256kdf-scheme", "dhSinglePass-cofactorDH-sha256kdf-scheme", NID_dhSinglePass_cofactorDH_sha256kdf_scheme, 6, &so[6167]},
+    {"dhSinglePass-cofactorDH-sha384kdf-scheme", "dhSinglePass-cofactorDH-sha384kdf-scheme", NID_dhSinglePass_cofactorDH_sha384kdf_scheme, 6, &so[6173]},
+    {"dhSinglePass-cofactorDH-sha512kdf-scheme", "dhSinglePass-cofactorDH-sha512kdf-scheme", NID_dhSinglePass_cofactorDH_sha512kdf_scheme, 6, &so[6179]},
+    {"dh-std-kdf", "dh-std-kdf", NID_dh_std_kdf},
+    {"dh-cofactor-kdf", "dh-cofactor-kdf", NID_dh_cofactor_kdf},
+    {"AES-128-CBC-HMAC-SHA256", "aes-128-cbc-hmac-sha256", NID_aes_128_cbc_hmac_sha256},
+    {"AES-192-CBC-HMAC-SHA256", "aes-192-cbc-hmac-sha256", NID_aes_192_cbc_hmac_sha256},
+    {"AES-256-CBC-HMAC-SHA256", "aes-256-cbc-hmac-sha256", NID_aes_256_cbc_hmac_sha256},
+    {"ct_precert_scts", "CT Precertificate SCTs", NID_ct_precert_scts, 10, &so[6185]},
+    {"ct_precert_poison", "CT Precertificate Poison", NID_ct_precert_poison, 10, &so[6195]},
+    {"ct_precert_signer", "CT Precertificate Signer", NID_ct_precert_signer, 10, &so[6205]},
+    {"ct_cert_scts", "CT Certificate SCTs", NID_ct_cert_scts, 10, &so[6215]},
+    {"jurisdictionL", "jurisdictionLocalityName", NID_jurisdictionLocalityName, 11, &so[6225]},
+    {"jurisdictionST", "jurisdictionStateOrProvinceName", NID_jurisdictionStateOrProvinceName, 11, &so[6236]},
+    {"jurisdictionC", "jurisdictionCountryName", NID_jurisdictionCountryName, 11, &so[6247]},
+    {"AES-128-OCB", "aes-128-ocb", NID_aes_128_ocb},
+    {"AES-192-OCB", "aes-192-ocb", NID_aes_192_ocb},
+    {"AES-256-OCB", "aes-256-ocb", NID_aes_256_ocb},
+    {"CAMELLIA-128-GCM", "camellia-128-gcm", NID_camellia_128_gcm, 8, &so[6258]},
+    {"CAMELLIA-128-CCM", "camellia-128-ccm", NID_camellia_128_ccm, 8, &so[6266]},
+    {"CAMELLIA-128-CTR", "camellia-128-ctr", NID_camellia_128_ctr, 8, &so[6274]},
+    {"CAMELLIA-128-CMAC", "camellia-128-cmac", NID_camellia_128_cmac, 8, &so[6282]},
+    {"CAMELLIA-192-GCM", "camellia-192-gcm", NID_camellia_192_gcm, 8, &so[6290]},
+    {"CAMELLIA-192-CCM", "camellia-192-ccm", NID_camellia_192_ccm, 8, &so[6298]},
+    {"CAMELLIA-192-CTR", "camellia-192-ctr", NID_camellia_192_ctr, 8, &so[6306]},
+    {"CAMELLIA-192-CMAC", "camellia-192-cmac", NID_camellia_192_cmac, 8, &so[6314]},
+    {"CAMELLIA-256-GCM", "camellia-256-gcm", NID_camellia_256_gcm, 8, &so[6322]},
+    {"CAMELLIA-256-CCM", "camellia-256-ccm", NID_camellia_256_ccm, 8, &so[6330]},
+    {"CAMELLIA-256-CTR", "camellia-256-ctr", NID_camellia_256_ctr, 8, &so[6338]},
+    {"CAMELLIA-256-CMAC", "camellia-256-cmac", NID_camellia_256_cmac, 8, &so[6346]},
+    {"id-scrypt", "id-scrypt", NID_id_scrypt, 9, &so[6354]},
+    {"id-tc26", "id-tc26", NID_id_tc26, 5, &so[6363]},
+    {"gost89-cnt-12", "gost89-cnt-12", NID_gost89_cnt_12},
+    {"gost-mac-12", "gost-mac-12", NID_gost_mac_12},
+    {"id-tc26-algorithms", "id-tc26-algorithms", NID_id_tc26_algorithms, 6, &so[6368]},
+    {"id-tc26-sign", "id-tc26-sign", NID_id_tc26_sign, 7, &so[6374]},
+    {"gost2012_256", "GOST R 34.10-2012 with 256 bit modulus", NID_id_GostR3410_2012_256, 8, &so[6381]},
+    {"gost2012_512", "GOST R 34.10-2012 with 512 bit modulus", NID_id_GostR3410_2012_512, 8, &so[6389]},
+    {"id-tc26-digest", "id-tc26-digest", NID_id_tc26_digest, 7, &so[6397]},
+    {"md_gost12_256", "GOST R 34.11-2012 with 256 bit hash", NID_id_GostR3411_2012_256, 8, &so[6404]},
+    {"md_gost12_512", "GOST R 34.11-2012 with 512 bit hash", NID_id_GostR3411_2012_512, 8, &so[6412]},
+    {"id-tc26-signwithdigest", "id-tc26-signwithdigest", NID_id_tc26_signwithdigest, 7, &so[6420]},
+    {"id-tc26-signwithdigest-gost3410-2012-256", "GOST R 34.10-2012 with GOST R 34.11-2012 (256 bit)", NID_id_tc26_signwithdigest_gost3410_2012_256, 8, &so[6427]},
+    {"id-tc26-signwithdigest-gost3410-2012-512", "GOST R 34.10-2012 with GOST R 34.11-2012 (512 bit)", NID_id_tc26_signwithdigest_gost3410_2012_512, 8, &so[6435]},
+    {"id-tc26-mac", "id-tc26-mac", NID_id_tc26_mac, 7, &so[6443]},
+    {"id-tc26-hmac-gost-3411-2012-256", "HMAC GOST 34.11-2012 256 bit", NID_id_tc26_hmac_gost_3411_2012_256, 8, &so[6450]},
+    {"id-tc26-hmac-gost-3411-2012-512", "HMAC GOST 34.11-2012 512 bit", NID_id_tc26_hmac_gost_3411_2012_512, 8, &so[6458]},
+    {"id-tc26-cipher", "id-tc26-cipher", NID_id_tc26_cipher, 7, &so[6466]},
+    {"id-tc26-agreement", "id-tc26-agreement", NID_id_tc26_agreement, 7, &so[6473]},
+    {"id-tc26-agreement-gost-3410-2012-256", "id-tc26-agreement-gost-3410-2012-256", NID_id_tc26_agreement_gost_3410_2012_256, 8, &so[6480]},
+    {"id-tc26-agreement-gost-3410-2012-512", "id-tc26-agreement-gost-3410-2012-512", NID_id_tc26_agreement_gost_3410_2012_512, 8, &so[6488]},
+    {"id-tc26-constants", "id-tc26-constants", NID_id_tc26_constants, 6, &so[6496]},
+    {"id-tc26-sign-constants", "id-tc26-sign-constants", NID_id_tc26_sign_constants, 7, &so[6502]},
+    {"id-tc26-gost-3410-2012-512-constants", "id-tc26-gost-3410-2012-512-constants", NID_id_tc26_gost_3410_2012_512_constants, 8, &so[6509]},
+    {"id-tc26-gost-3410-2012-512-paramSetTest", "GOST R 34.10-2012 (512 bit) testing parameter set", NID_id_tc26_gost_3410_2012_512_paramSetTest, 9, &so[6517]},
+    {"id-tc26-gost-3410-2012-512-paramSetA", "GOST R 34.10-2012 (512 bit) ParamSet A", NID_id_tc26_gost_3410_2012_512_paramSetA, 9, &so[6526]},
+    {"id-tc26-gost-3410-2012-512-paramSetB", "GOST R 34.10-2012 (512 bit) ParamSet B", NID_id_tc26_gost_3410_2012_512_paramSetB, 9, &so[6535]},
+    {"id-tc26-digest-constants", "id-tc26-digest-constants", NID_id_tc26_digest_constants, 7, &so[6544]},
+    {"id-tc26-cipher-constants", "id-tc26-cipher-constants", NID_id_tc26_cipher_constants, 7, &so[6551]},
+    {"id-tc26-gost-28147-constants", "id-tc26-gost-28147-constants", NID_id_tc26_gost_28147_constants, 8, &so[6558]},
+    {"id-tc26-gost-28147-param-Z", "GOST 28147-89 TC26 parameter set", NID_id_tc26_gost_28147_param_Z, 9, &so[6566]},
+    {"INN", "INN", NID_INN, 8, &so[6575]},
+    {"OGRN", "OGRN", NID_OGRN, 5, &so[6583]},
+    {"SNILS", "SNILS", NID_SNILS, 5, &so[6588]},
+    {"subjectSignTool", "Signing Tool of Subject", NID_subjectSignTool, 5, &so[6593]},
+    {"issuerSignTool", "Signing Tool of Issuer", NID_issuerSignTool, 5, &so[6598]},
+    {"gost89-cbc", "gost89-cbc", NID_gost89_cbc},
+    {"gost89-ecb", "gost89-ecb", NID_gost89_ecb},
+    {"gost89-ctr", "gost89-ctr", NID_gost89_ctr},
+    {"grasshopper-ecb", "grasshopper-ecb", NID_grasshopper_ecb},
+    {"grasshopper-ctr", "grasshopper-ctr", NID_grasshopper_ctr},
+    {"grasshopper-ofb", "grasshopper-ofb", NID_grasshopper_ofb},
+    {"grasshopper-cbc", "grasshopper-cbc", NID_grasshopper_cbc},
+    {"grasshopper-cfb", "grasshopper-cfb", NID_grasshopper_cfb},
+    {"grasshopper-mac", "grasshopper-mac", NID_grasshopper_mac},
+    {"ChaCha20-Poly1305", "chacha20-poly1305", NID_chacha20_poly1305},
+    {"ChaCha20", "chacha20", NID_chacha20},
+    {"tlsfeature", "TLS Feature", NID_tlsfeature, 8, &so[6603]},
+    {"TLS1-PRF", "tls1-prf", NID_tls1_prf},
+    {"ipsecIKE", "ipsec Internet Key Exchange", NID_ipsec_IKE, 8, &so[6611]},
+    {"capwapAC", "Ctrl/provision WAP Access", NID_capwapAC, 8, &so[6619]},
+    {"capwapWTP", "Ctrl/Provision WAP Termination", NID_capwapWTP, 8, &so[6627]},
+    {"secureShellClient", "SSH Client", NID_sshClient, 8, &so[6635]},
+    {"secureShellServer", "SSH Server", NID_sshServer, 8, &so[6643]},
+    {"sendRouter", "Send Router", NID_sendRouter, 8, &so[6651]},
+    {"sendProxiedRouter", "Send Proxied Router", NID_sendProxiedRouter, 8, &so[6659]},
+    {"sendOwner", "Send Owner", NID_sendOwner, 8, &so[6667]},
+    {"sendProxiedOwner", "Send Proxied Owner", NID_sendProxiedOwner, 8, &so[6675]},
+    {"id-pkinit", "id-pkinit", NID_id_pkinit, 6, &so[6683]},
+    {"pkInitClientAuth", "PKINIT Client Auth", NID_pkInitClientAuth, 7, &so[6689]},
+    {"pkInitKDC", "Signing KDC Response", NID_pkInitKDC, 7, &so[6696]},
+    {"X25519", "X25519", NID_X25519, 3, &so[6703]},
+    {"X448", "X448", NID_X448, 3, &so[6706]},
+    {"HKDF", "hkdf", NID_hkdf},
+    {"KxRSA", "kx-rsa", NID_kx_rsa},
+    {"KxECDHE", "kx-ecdhe", NID_kx_ecdhe},
+    {"KxDHE", "kx-dhe", NID_kx_dhe},
+    {"KxECDHE-PSK", "kx-ecdhe-psk", NID_kx_ecdhe_psk},
+    {"KxDHE-PSK", "kx-dhe-psk", NID_kx_dhe_psk},
+    {"KxRSA_PSK", "kx-rsa-psk", NID_kx_rsa_psk},
+    {"KxPSK", "kx-psk", NID_kx_psk},
+    {"KxSRP", "kx-srp", NID_kx_srp},
+    {"KxGOST", "kx-gost", NID_kx_gost},
+    {"AuthRSA", "auth-rsa", NID_auth_rsa},
+    {"AuthECDSA", "auth-ecdsa", NID_auth_ecdsa},
+    {"AuthPSK", "auth-psk", NID_auth_psk},
+    {"AuthDSS", "auth-dss", NID_auth_dss},
+    {"AuthGOST01", "auth-gost01", NID_auth_gost01},
+    {"AuthGOST12", "auth-gost12", NID_auth_gost12},
+    {"AuthSRP", "auth-srp", NID_auth_srp},
+    {"AuthNULL", "auth-null", NID_auth_null},
+    { NULL, NULL, NID_undef },
+    { NULL, NULL, NID_undef },
+    {"BLAKE2b512", "blake2b512", NID_blake2b512, 11, &so[6709]},
+    {"BLAKE2s256", "blake2s256", NID_blake2s256, 11, &so[6720]},
+    {"id-smime-ct-contentCollection", "id-smime-ct-contentCollection", NID_id_smime_ct_contentCollection, 11, &so[6731]},
+    {"id-smime-ct-authEnvelopedData", "id-smime-ct-authEnvelopedData", NID_id_smime_ct_authEnvelopedData, 11, &so[6742]},
+    {"id-ct-xml", "id-ct-xml", NID_id_ct_xml, 11, &so[6753]},
+    {"id-ecSigType", "id-ecSigType", NID_X9_62_id_ecSigType, 6, &so[6764]},
+    {"secg-scheme", "secg-scheme", NID_secg_scheme, 4, &so[6770]},
+    {"ecies-recommendedParameters", "ecies-recommendedParameters", NID_ecies_recommendedParameters, 5, &so[6774]},
+    {"ecies-specifiedParameters", "ecies-specifiedParameters", NID_ecies_specifiedParameters, 5, &so[6779]},
+    {"x9-63-kdf", "x9-63-kdf", NID_x9_63_kdf, 6, &so[6784]},
+    {"nist-concatenation-kdf", "nist-concatenation-kdf", NID_nist_concatenation_kdf, 6, &so[6790]},
+    {"tls-kdf", "tls-kdf", NID_tls_kdf, 6, &so[6796]},
+    {"ikev2-kdf", "ikev2-kdf", NID_ikev2_kdf, 6, &so[6802]},
+    {"xor-in-ecies", "xor-in-ecies", NID_xor_in_ecies, 5, &so[6808]},
+    {"tdes-cbc-in-ecies", "tdes-cbc-in-ecies", NID_tdes_cbc_in_ecies, 5, &so[6813]},
+    {"aes128-cbc-in-ecies", "aes128-cbc-in-ecies", NID_aes128_cbc_in_ecies, 6, &so[6818]},
+    {"aes192-cbc-in-ecies", "aes192-cbc-in-ecies", NID_aes192_cbc_in_ecies, 6, &so[6824]},
+    {"aes256-cbc-in-ecies", "aes256-cbc-in-ecies", NID_aes256_cbc_in_ecies, 6, &so[6830]},
+    {"aes128-ctr-in-ecies", "aes128-ctr-in-ecies", NID_aes128_ctr_in_ecies, 6, &so[6836]},
+    {"aes192-ctr-in-ecies", "aes192-ctr-in-ecies", NID_aes192_ctr_in_ecies, 6, &so[6842]},
+    {"aes256-ctr-in-ecies", "aes256-ctr-in-ecies", NID_aes256_ctr_in_ecies, 6, &so[6848]},
+    {"hmac-full-ecies", "hmac-full-ecies", NID_hmac_full_ecies, 5, &so[6854]},
+    {"hmac-half-ecies", "hmac-half-ecies", NID_hmac_half_ecies, 5, &so[6859]},
+    {"cmac-aes128-ecies", "cmac-aes128-ecies", NID_cmac_aes128_ecies, 6, &so[6864]},
+    {"cmac-aes192-ecies", "cmac-aes192-ecies", NID_cmac_aes192_ecies, 6, &so[6870]},
+    {"cmac-aes256-ecies", "cmac-aes256-ecies", NID_cmac_aes256_ecies, 6, &so[6876]},
+    { NULL, NULL, NID_undef },
+    {"PBE-SM3-SMS4", "pbeWithSM3AndSMS4-CBC", NID_pbe_WithSM3AndSMS4_CBC, 10, &so[6882]},
+    {"ecies-with-x9-63-sha1-xor-hmac", "ecies-with-x9-63-sha1-xor-hmac", NID_ecies_with_x9_63_sha1_xor_hmac, 6, &so[6892]},
+    {"ecies-with-x9-63-sha256-xor-hmac", "ecies-with-x9-63-sha256-xor-hmac", NID_ecies_with_x9_63_sha256_xor_hmac, 6, &so[6898]},
+    {"ecies-with-x9-63-sha512-xor-hmac", "ecies-with-x9-63-sha512-xor-hmac", NID_ecies_with_x9_63_sha512_xor_hmac, 6, &so[6904]},
+    {"ecies-with-x9-63-sha1-aes128-cbc-hmac", "ecies-with-x9-63-sha1-aes128-cbc-hmac", NID_ecies_with_x9_63_sha1_aes128_cbc_hmac, 6, &so[6910]},
+    {"ecies-with-x9-63-sha256-aes128-cbc-hmac", "ecies-with-x9-63-sha256-aes128-cbc-hmac", NID_ecies_with_x9_63_sha256_aes128_cbc_hmac, 6, &so[6916]},
+    {"ecies-with-x9-63-sha512-aes256-cbc-hmac", "ecies-with-x9-63-sha512-aes256-cbc-hmac", NID_ecies_with_x9_63_sha512_aes256_cbc_hmac, 6, &so[6922]},
+    {"ecies-with-x9-63-sha256-aes128-ctr-hmac", "ecies-with-x9-63-sha256-aes128-ctr-hmac", NID_ecies_with_x9_63_sha256_aes128_ctr_hmac, 6, &so[6928]},
+    {"ecies-with-x9-63-sha512-aes256-ctr-hmac", "ecies-with-x9-63-sha512-aes256-ctr-hmac", NID_ecies_with_x9_63_sha512_aes256_ctr_hmac, 6, &so[6934]},
+    {"ecies-with-x9-63-sha256-aes128-cbc-hmac-half", "ecies-with-x9-63-sha256-aes128-cbc-hmac-half", NID_ecies_with_x9_63_sha256_aes128_cbc_hmac_half, 6, &so[6940]},
+    {"ecies-with-x9-63-sha512-aes256-cbc-hmac-half", "ecies-with-x9-63-sha512-aes256-cbc-hmac-half", NID_ecies_with_x9_63_sha512_aes256_cbc_hmac_half, 6, &so[6946]},
+    {"ecies-with-x9-63-sha256-aes128-ctr-hmac-half", "ecies-with-x9-63-sha256-aes128-ctr-hmac-half", NID_ecies_with_x9_63_sha256_aes128_ctr_hmac_half, 6, &so[6952]},
+    {"ecies-with-x9-63-sha512-aes256-ctr-hmac-half", "ecies-with-x9-63-sha512-aes256-ctr-hmac-half", NID_ecies_with_x9_63_sha512_aes256_ctr_hmac_half, 6, &so[6958]},
+    {"ecies-with-x9-63-sha1-aes128-cbc-cmac", "ecies-with-x9-63-sha1-aes128-cbc-cmac", NID_ecies_with_x9_63_sha1_aes128_cbc_cmac, 6, &so[6964]},
+    {"ecies-with-x9-63-sha256-aes128-cbc-cmac", "ecies-with-x9-63-sha256-aes128-cbc-cmac", NID_ecies_with_x9_63_sha256_aes128_cbc_cmac, 6, &so[6970]},
+    {"ecies-with-x9-63-sha512-aes256-cbc-cmac", "ecies-with-x9-63-sha512-aes256-cbc-cmac", NID_ecies_with_x9_63_sha512_aes256_cbc_cmac, 6, &so[6976]},
+    {"ecies-with-x9-63-sha256-aes128-ctr-cmac", "ecies-with-x9-63-sha256-aes128-ctr-cmac", NID_ecies_with_x9_63_sha256_aes128_ctr_cmac, 6, &so[6982]},
+    {"ecies-with-x9-63-sha512-aes256-ctr-cmac", "ecies-with-x9-63-sha512-aes256-ctr-cmac", NID_ecies_with_x9_63_sha512_aes256_ctr_cmac, 6, &so[6988]},
+    {"ibcs1", "ibcs1", NID_ibcs1, 9, &so[6994]},
+    {"type1curve", "type1curve", NID_type1curve, 11, &so[7003]},
+    {"bfibe", "bfibe", NID_bfibe, 11, &so[7014]},
+    {"bb1ibe", "bb1ibe", NID_bb1ibe, 11, &so[7025]},
+    {"ISO-CN", "ISO CN Member Body", NID_ISO_CN, 3, &so[7036]},
+    {"oscca", "oscca", NID_oscca, 5, &so[7039]},
+    {"sm-scheme", "sm-scheme", NID_sm_scheme, 6, &so[7044]},
+    {"SSF33-ECB", "ssf33-ecb", NID_ssf33_ecb, 8, &so[7050]},
+    {"SSF33-CBC", "ssf33-cbc", NID_ssf33_cbc, 8, &so[7058]},
+    {"SSF33-OFB", "ssf33-ofb", NID_ssf33_ofb128, 8, &so[7066]},
+    {"SSF33-CFB", "ssf33-cfb", NID_ssf33_cfb128, 8, &so[7074]},
+    {"SSF33-CFB1", "ssf33-cfb1", NID_ssf33_cfb1, 8, &so[7082]},
+    {"SSF33-CFB8", "ssf33-cfb8", NID_ssf33_cfb8, 8, &so[7090]},
+    {"SSF33-CBC-MAC", "ssf33-cbc-mac", NID_ssf33_cbc_mac, 8, &so[7098]},
+    {"SM1-ECB", "sm1-ecb", NID_sm1_ecb, 8, &so[7106]},
+    {"SM1-CBC", "sm1-cbc", NID_sm1_cbc, 8, &so[7114]},
+    {"SM1-OFB", "sm1-ofb", NID_sm1_ofb128, 8, &so[7122]},
+    {"SM1-CFB", "sm1-cfb", NID_sm1_cfb128, 8, &so[7130]},
+    {"SM1-CFB1", "sm1-cfb1", NID_sm1_cfb1, 8, &so[7138]},
+    {"SM1-CFB8", "sm1-cfb8", NID_sm1_cfb8, 8, &so[7146]},
+    {"sm2p256v1", "sm2p256v1", NID_sm2p256v1, 8, &so[7154]},
+    {"sm2sign", "sm2sign", NID_sm2sign, 9, &so[7162]},
+    {"sm2exchange", "sm2exchange", NID_sm2exchange, 9, &so[7171]},
+    {"sm2encrypt", "sm2encrypt", NID_sm2encrypt, 9, &so[7180]},
+    {"SM2Sign-with-SM3", "sm2sign-with-sm3", NID_sm2sign_with_sm3, 8, &so[7189]},
+    {"SM2Sign-with-SHA1", "sm2sign-with-sha1", NID_sm2sign_with_sha1, 8, &so[7197]},
+    {"SM2Sign-with-SHA256", "sm2sign-with-sha256", NID_sm2sign_with_sha256, 8, &so[7205]},
+    {"SM2Sign-with-SHA511", "sm2sign-with-sha512", NID_sm2sign_with_sha512, 8, &so[7213]},
+    {"SM2Sign-with-SHA224", "sm2sign-with-sha224", NID_sm2sign_with_sha224, 8, &so[7221]},
+    {"SM2Sign-with-SHA384", "sm2sign-with-sha384", NID_sm2sign_with_sha384, 8, &so[7229]},
+    {"SM2Sign-with-RMD160", "sm2sign-with-rmd160", NID_sm2sign_with_rmd160, 8, &so[7237]},
+    {"SM2Sign-with-Whirlpool", "sm2sign-with-whirlpool", NID_sm2sign_with_whirlpool, 8, &so[7245]},
+    {"SM2Sign-with-Blake2b512", "sm2sign-with-blake2b512", NID_sm2sign_with_blake2b512, 8, &so[7253]},
+    {"SM2Sign-with-Blake2s256", "sm2sign-with-blake2s256", NID_sm2sign_with_blake2s256, 8, &so[7261]},
+    {"sm2encrypt-recommendedParameters", "sm2encrypt-recommendedParameters", NID_sm2encrypt_recommendedParameters, 10, &so[7269]},
+    {"sm2encrypt-specifiedParameters", "sm2encrypt-specifiedParameters", NID_sm2encrypt_specifiedParameters, 10, &so[7279]},
+    {"sm2encrypt-with-sm3", "sm2encrypt-with-sm3", NID_sm2encrypt_with_sm3, 11, &so[7289]},
+    {"sm2encrypt-with-sha1", "sm2encrypt-with-sha1", NID_sm2encrypt_with_sha1, 11, &so[7300]},
+    {"sm2encrypt-with-sha224", "sm2encrypt-with-sha224", NID_sm2encrypt_with_sha224, 11, &so[7311]},
+    {"sm2encrypt-with-sha256", "sm2encrypt-with-sha256", NID_sm2encrypt_with_sha256, 11, &so[7322]},
+    {"sm2encrypt-with-sha384", "sm2encrypt-with-sha384", NID_sm2encrypt_with_sha384, 11, &so[7333]},
+    {"sm2encrypt-with-sha512", "sm2encrypt-with-sha512", NID_sm2encrypt_with_sha512, 11, &so[7344]},
+    {"sm2encrypt-with-rmd160", "sm2encrypt-with-rmd160", NID_sm2encrypt_with_rmd160, 11, &so[7355]},
+    {"sm2encrypt-with-whirlpool", "sm2encrypt-with-whirlpool", NID_sm2encrypt_with_whirlpool, 11, &so[7366]},
+    {"sm2encrypt-with-blake2b512", "sm2encrypt-with-blake2b512", NID_sm2encrypt_with_blake2b512, 11, &so[7377]},
+    {"sm2encrypt-with-blake2s256", "sm2encrypt-with-blake2s256", NID_sm2encrypt_with_blake2s256, 11, &so[7388]},
+    {"sm2encrypt-with-md5", "sm2encrypt-with-md5", NID_sm2encrypt_with_md5, 11, &so[7399]},
+    {"SM3", "sm3", NID_sm3, 8, &so[7410]},
+    {"HMAC-SM3", "hmac-sm3", NID_hmac_sm3, 9, &so[7418]},
+    {"SMS4-ECB", "sms4-ecb", NID_sms4_ecb, 8, &so[7427]},
+    {"SMS4-CBC", "sms4-cbc", NID_sms4_cbc, 8, &so[7435]},
+    {"SMS4-OFB", "sms4-ofb", NID_sms4_ofb128, 8, &so[7443]},
+    {"SMS4-CFB", "sms4-cfb", NID_sms4_cfb128, 8, &so[7451]},
+    {"SMS4-CFB1", "sms4-cfb1", NID_sms4_cfb1, 8, &so[7459]},
+    {"SMS4-CFB8", "sms4-cfb8", NID_sms4_cfb8, 8, &so[7467]},
+    {"SMS4-CTR", "sms4-ctr", NID_sms4_ctr, 8, &so[7475]},
+    {"SMS4-GCM", "sms4-gcm", NID_sms4_gcm, 8, &so[7483]},
+    {"SMS4-CCM", "sms4-ccm", NID_sms4_ccm, 8, &so[7491]},
+    {"SMS4-XTS", "sms4-xts", NID_sms4_xts, 8, &so[7499]},
+    {"SMS4-WRAP", "sms4-wrap", NID_sms4_wrap, 8, &so[7507]},
+    {"SMS4-WRAP-PAD", "sms4-wrap-pad", NID_sms4_wrap_pad, 8, &so[7515]},
+    {"SMS4-OCB", "sms4-ocb", NID_sms4_ocb, 8, &so[7523]},
+    {"SM5", "sm5", NID_sm5, 8, &so[7531]},
+    {"SM6-ECB", "sm6-ecb", NID_sm6_ecb, 8, &so[7539]},
+    {"SM6-CBC", "sm6-cbc", NID_sm6_cbc, 8, &so[7547]},
+    {"SM6-OFB", "sm6-ofb", NID_sm6_ofb128, 8, &so[7555]},
+    {"SM6-CFB", "sm6-cfb", NID_sm6_cfb128, 8, &so[7563]},
+    {"id-sm9PublicKey", "id-sm9PublicKey", NID_id_sm9PublicKey, 8, &so[7571]},
+    {"sm9sign", "sm9sign", NID_sm9sign, 9, &so[7579]},
+    {"sm9keyagreement", "sm9keyagreement", NID_sm9keyagreement, 9, &so[7588]},
+    {"sm9encrypt", "sm9encrypt", NID_sm9encrypt, 9, &so[7597]},
+    {"sm9hash1", "sm9hash1", NID_sm9hash1, 9, &so[7606]},
+    {"sm9kdf", "sm9kdf", NID_sm9kdf, 9, &so[7615]},
+    {"id-sm9MasterSecret", "id-sm9MasterSecret", NID_id_sm9MasterSecret, 9, &so[7624]},
+    {"sm9bn256v1", "sm9bn256v1", NID_sm9bn256v1, 10, &so[7633]},
+    {"sm9sign-with-sm3", "sm9sign-with-sm3", NID_sm9sign_with_sm3, 10, &so[7643]},
+    {"sm9sign-with-sha256", "sm9sign-with-sha256", NID_sm9sign_with_sha256, 10, &so[7653]},
+    {"sm9encrypt-with-sm3-xor", "sm9encrypt-with-sm3-xor", NID_sm9encrypt_with_sm3_xor, 10, &so[7663]},
+    {"sm9encrypt-with-sm3-sms4-cbc", "sm9encrypt-with-sm3-sms4-cbc", NID_sm9encrypt_with_sm3_sms4_cbc, 10, &so[7673]},
+    {"sm9encrypt-with-sm3-sms4-ctr", "sm9encrypt-with-sm3-sms4-ctr", NID_sm9encrypt_with_sm3_sms4_ctr, 10, &so[7683]},
+    {"sm9hash1-with-sm3", "sm9hash1-with-sm3", NID_sm9hash1_with_sm3, 10, &so[7693]},
+    {"sm9hash1-with-sha256", "sm9hash1-with-sha256", NID_sm9hash1_with_sha256, 10, &so[7703]},
+    {"sm9kdf-with-sm3", "sm9kdf-with-sm3", NID_sm9kdf_with_sm3, 10, &so[7713]},
+    {"sm9kdf-with-sha256", "sm9kdf-with-sha256", NID_sm9kdf_with_sha256, 10, &so[7723]},
+    {"ZUC", "zuc", NID_zuc, 8, &so[7733]},
+    {"zuc-128eea3", "zuc-128eea3", NID_zuc_128eea3, 9, &so[7741]},
+    {"zuc-128eia3", "zuc-128eia3", NID_zuc_128eia3, 9, &so[7750]},
+    {"bwips", "bwips", NID_bwips, 5, &so[7759]},
+    {"wapi-crypto", "wapi-crypto", NID_wapi_crypto, 6, &so[7764]},
+    {"wapi-ec", "wapi-ec", NID_wapi_ec, 7, &so[7770]},
+    {"wapi-ecdsa192-sha256", "wapi-ecdsa192-sha256", NID_wapi_ecdsa192_sha256, 8, &so[7777]},
+    {"wapip192v1", "wapip192v1", NID_wapip192v1, 9, &so[7785]},
+    {"KxSM2", "kx-sm2", NID_kx_sm2},
+    {"KxSM2DHE", "kx-sm2dhe", NID_kx_sm2dhe},
+    {"KxSM2-PSK", "kx-sm2-psk", NID_kx_sm2_psk},
+    {"KxSM9", "kx-sm9", NID_kx_sm9},
+    {"KxSM9DHE", "kx-sm9dhe", NID_kx_sm9dhe},
+    {"AuthSM2", "auth-sm2", NID_auth_sm2},
+    {"AuthSM9", "auth-sm9", NID_auth_sm9},
+    {"gmssl", "GmSSL", NID_GmSSL, 8, &so[7794]},
+    {"CPK", "cpk", NID_cpk, 9, &so[7802]},
+    {"cpk-map", "cpk-map", NID_cpk_map, 10, &so[7811]},
+    {"cpk-map-sha1", "cpk-map-sha1", NID_cpk_map_sha1, 11, &so[7821]},
+    {"cpk-map-sha256", "cpk-map-sha256", NID_cpk_map_sha256, 11, &so[7832]},
+    {"cpk-map-sm3", "cpk-map-sm3", NID_cpk_map_sm3, 11, &so[7843]},
+    {"cpk-map-sha384", "cpk-map-sha384", NID_cpk_map_sha384, 11, &so[7854]},
+    {"cpk-map-sha512", "cpk-map-sha512", NID_cpk_map_sha512, 11, &so[7865]},
+    {"paillier", "paillier", NID_paillier, 9, &so[7876]},
+    {"sm9hash2", "sm9hash2", NID_sm9hash2, 9, &so[7885]},
+    {"sm9hash2-with-sm3", "sm9hash2-with-sm3", NID_sm9hash2_with_sm3, 10, &so[7894]},
+    {"sm9hash2-with-sha256", "sm9hash2-with-sha256", NID_sm9hash2_with_sha256, 10, &so[7904]},
+    {"ZUC256", "zuc256", NID_zuc256, 9, &so[7914]},
+    {"ZUC-MAC", "zuc-mac", NID_zuc_mac, 9, &so[7923]},
+    {"ZUC256-MAC", "zuc256-mac", NID_zuc256_mac, 9, &so[7932]},
+    {"ZUC256-MAC32", "zuc256-mac32", NID_zuc256_mac32, 10, &so[7941]},
+    {"ZUC256-MAC64", "zuc256-mac64", NID_zuc256_mac64, 10, &so[7951]},
+    {"ZUC256-MAC128", "zuc256-mac128", NID_zuc256_mac128, 10, &so[7961]},
+};
+
+#define NUM_SN 1208
+static const unsigned int sn_objs[NUM_SN] = {
+     364,    /* "AD_DVCS" */
+     419,    /* "AES-128-CBC" */
+     916,    /* "AES-128-CBC-HMAC-SHA1" */
+     948,    /* "AES-128-CBC-HMAC-SHA256" */
+     421,    /* "AES-128-CFB" */
+     650,    /* "AES-128-CFB1" */
+     653,    /* "AES-128-CFB8" */
+     904,    /* "AES-128-CTR" */
+     418,    /* "AES-128-ECB" */
+     958,    /* "AES-128-OCB" */
+     420,    /* "AES-128-OFB" */
+     913,    /* "AES-128-XTS" */
+     423,    /* "AES-192-CBC" */
+     917,    /* "AES-192-CBC-HMAC-SHA1" */
+     949,    /* "AES-192-CBC-HMAC-SHA256" */
+     425,    /* "AES-192-CFB" */
+     651,    /* "AES-192-CFB1" */
+     654,    /* "AES-192-CFB8" */
+     905,    /* "AES-192-CTR" */
+     422,    /* "AES-192-ECB" */
+     959,    /* "AES-192-OCB" */
+     424,    /* "AES-192-OFB" */
+     427,    /* "AES-256-CBC" */
+     918,    /* "AES-256-CBC-HMAC-SHA1" */
+     950,    /* "AES-256-CBC-HMAC-SHA256" */
+     429,    /* "AES-256-CFB" */
+     652,    /* "AES-256-CFB1" */
+     655,    /* "AES-256-CFB8" */
+     906,    /* "AES-256-CTR" */
+     426,    /* "AES-256-ECB" */
+     960,    /* "AES-256-OCB" */
+     428,    /* "AES-256-OFB" */
+     914,    /* "AES-256-XTS" */
+    1049,    /* "AuthDSS" */
+    1047,    /* "AuthECDSA" */
+    1050,    /* "AuthGOST01" */
+    1051,    /* "AuthGOST12" */
+    1053,    /* "AuthNULL" */
+    1048,    /* "AuthPSK" */
+    1046,    /* "AuthRSA" */
+    1198,    /* "AuthSM2" */
+    1199,    /* "AuthSM9" */
+    1052,    /* "AuthSRP" */
+      91,    /* "BF-CBC" */
+      93,    /* "BF-CFB" */
+      92,    /* "BF-ECB" */
+      94,    /* "BF-OFB" */
+    1056,    /* "BLAKE2b512" */
+    1057,    /* "BLAKE2s256" */
+      14,    /* "C" */
+     751,    /* "CAMELLIA-128-CBC" */
+     962,    /* "CAMELLIA-128-CCM" */
+     757,    /* "CAMELLIA-128-CFB" */
+     760,    /* "CAMELLIA-128-CFB1" */
+     763,    /* "CAMELLIA-128-CFB8" */
+     964,    /* "CAMELLIA-128-CMAC" */
+     963,    /* "CAMELLIA-128-CTR" */
+     754,    /* "CAMELLIA-128-ECB" */
+     961,    /* "CAMELLIA-128-GCM" */
+     766,    /* "CAMELLIA-128-OFB" */
+     752,    /* "CAMELLIA-192-CBC" */
+     966,    /* "CAMELLIA-192-CCM" */
+     758,    /* "CAMELLIA-192-CFB" */
+     761,    /* "CAMELLIA-192-CFB1" */
+     764,    /* "CAMELLIA-192-CFB8" */
+     968,    /* "CAMELLIA-192-CMAC" */
+     967,    /* "CAMELLIA-192-CTR" */
+     755,    /* "CAMELLIA-192-ECB" */
+     965,    /* "CAMELLIA-192-GCM" */
+     767,    /* "CAMELLIA-192-OFB" */
+     753,    /* "CAMELLIA-256-CBC" */
+     970,    /* "CAMELLIA-256-CCM" */
+     759,    /* "CAMELLIA-256-CFB" */
+     762,    /* "CAMELLIA-256-CFB1" */
+     765,    /* "CAMELLIA-256-CFB8" */
+     972,    /* "CAMELLIA-256-CMAC" */
+     971,    /* "CAMELLIA-256-CTR" */
+     756,    /* "CAMELLIA-256-ECB" */
+     969,    /* "CAMELLIA-256-GCM" */
+     768,    /* "CAMELLIA-256-OFB" */
+     108,    /* "CAST5-CBC" */
+     110,    /* "CAST5-CFB" */
+     109,    /* "CAST5-ECB" */
+     111,    /* "CAST5-OFB" */
+     894,    /* "CMAC" */
+      13,    /* "CN" */
+    1201,    /* "CPK" */
+     141,    /* "CRLReason" */
+     417,    /* "CSPName" */
+    1019,    /* "ChaCha20" */
+    1018,    /* "ChaCha20-Poly1305" */
+     367,    /* "CrlID" */
+     391,    /* "DC" */
+      31,    /* "DES-CBC" */
+     643,    /* "DES-CDMF" */
+      30,    /* "DES-CFB" */
+     656,    /* "DES-CFB1" */
+     657,    /* "DES-CFB8" */
+      29,    /* "DES-ECB" */
+      32,    /* "DES-EDE" */
+      43,    /* "DES-EDE-CBC" */
+      60,    /* "DES-EDE-CFB" */
+      62,    /* "DES-EDE-OFB" */
+      33,    /* "DES-EDE3" */
+      44,    /* "DES-EDE3-CBC" */
+      61,    /* "DES-EDE3-CFB" */
+     658,    /* "DES-EDE3-CFB1" */
+     659,    /* "DES-EDE3-CFB8" */
+      63,    /* "DES-EDE3-OFB" */
+      45,    /* "DES-OFB" */
+      80,    /* "DESX-CBC" */
+     380,    /* "DOD" */
+     116,    /* "DSA" */
+      66,    /* "DSA-SHA" */
+     113,    /* "DSA-SHA1" */
+      70,    /* "DSA-SHA1-old" */
+      67,    /* "DSA-old" */
+     297,    /* "DVCS" */
+      99,    /* "GN" */
+    1036,    /* "HKDF" */
+     855,    /* "HMAC" */
+     780,    /* "HMAC-MD5" */
+     781,    /* "HMAC-SHA1" */
+    1149,    /* "HMAC-SM3" */
+     381,    /* "IANA" */
+      34,    /* "IDEA-CBC" */
+      35,    /* "IDEA-CFB" */
+      36,    /* "IDEA-ECB" */
+      46,    /* "IDEA-OFB" */
+    1004,    /* "INN" */
+     181,    /* "ISO" */
+    1105,    /* "ISO-CN" */
+     183,    /* "ISO-US" */
+     645,    /* "ITU-T" */
+     646,    /* "JOINT-ISO-ITU-T" */
+     773,    /* "KISA" */
+    1039,    /* "KxDHE" */
+    1041,    /* "KxDHE-PSK" */
+    1038,    /* "KxECDHE" */
+    1040,    /* "KxECDHE-PSK" */
+    1045,    /* "KxGOST" */
+    1043,    /* "KxPSK" */
+    1037,    /* "KxRSA" */
+    1042,    /* "KxRSA_PSK" */
+    1193,    /* "KxSM2" */
+    1195,    /* "KxSM2-PSK" */
+    1194,    /* "KxSM2DHE" */
+    1196,    /* "KxSM9" */
+    1197,    /* "KxSM9DHE" */
+    1044,    /* "KxSRP" */
+      15,    /* "L" */
+     856,    /* "LocalKeySet" */
+       3,    /* "MD2" */
+     257,    /* "MD4" */
+       4,    /* "MD5" */
+     114,    /* "MD5-SHA1" */
+      95,    /* "MDC2" */
+     911,    /* "MGF1" */
+     388,    /* "Mail" */
+     393,    /* "NULL" */
+     404,    /* "NULL" */
+      57,    /* "Netscape" */
+     366,    /* "Nonce" */
+      17,    /* "O" */
+     178,    /* "OCSP" */
+     180,    /* "OCSPSigning" */
+    1005,    /* "OGRN" */
+     379,    /* "ORG" */
+      18,    /* "OU" */
+     749,    /* "Oakley-EC2N-3" */
+     750,    /* "Oakley-EC2N-4" */
+       9,    /* "PBE-MD2-DES" */
+     168,    /* "PBE-MD2-RC2-64" */
+      10,    /* "PBE-MD5-DES" */
+     169,    /* "PBE-MD5-RC2-64" */
+     147,    /* "PBE-SHA1-2DES" */
+     146,    /* "PBE-SHA1-3DES" */
+     170,    /* "PBE-SHA1-DES" */
+     148,    /* "PBE-SHA1-RC2-128" */
+     149,    /* "PBE-SHA1-RC2-40" */
+      68,    /* "PBE-SHA1-RC2-64" */
+     144,    /* "PBE-SHA1-RC4-128" */
+     145,    /* "PBE-SHA1-RC4-40" */
+    1083,    /* "PBE-SM3-SMS4" */
+     161,    /* "PBES2" */
+      69,    /* "PBKDF2" */
+     162,    /* "PBMAC1" */
+     127,    /* "PKIX" */
+     935,    /* "PSPECIFIED" */
+      98,    /* "RC2-40-CBC" */
+     166,    /* "RC2-64-CBC" */
+      37,    /* "RC2-CBC" */
+      39,    /* "RC2-CFB" */
+      38,    /* "RC2-ECB" */
+      40,    /* "RC2-OFB" */
+       5,    /* "RC4" */
+      97,    /* "RC4-40" */
+     915,    /* "RC4-HMAC-MD5" */
+     120,    /* "RC5-CBC" */
+     122,    /* "RC5-CFB" */
+     121,    /* "RC5-ECB" */
+     123,    /* "RC5-OFB" */
+     117,    /* "RIPEMD160" */
+      19,    /* "RSA" */
+       7,    /* "RSA-MD2" */
+     396,    /* "RSA-MD4" */
+       8,    /* "RSA-MD5" */
+      96,    /* "RSA-MDC2" */
+     104,    /* "RSA-NP-MD5" */
+     119,    /* "RSA-RIPEMD160" */
+      42,    /* "RSA-SHA" */
+      65,    /* "RSA-SHA1" */
+     115,    /* "RSA-SHA1-2" */
+     671,    /* "RSA-SHA224" */
+     668,    /* "RSA-SHA256" */
+     669,    /* "RSA-SHA384" */
+     670,    /* "RSA-SHA512" */
+     919,    /* "RSAES-OAEP" */
+     912,    /* "RSASSA-PSS" */
+     777,    /* "SEED-CBC" */
+     779,    /* "SEED-CFB" */
+     776,    /* "SEED-ECB" */
+     778,    /* "SEED-OFB" */
+      41,    /* "SHA" */
+      64,    /* "SHA1" */
+     675,    /* "SHA224" */
+     672,    /* "SHA256" */
+     673,    /* "SHA384" */
+     674,    /* "SHA512" */
+    1116,    /* "SM1-CBC" */
+    1118,    /* "SM1-CFB" */
+    1119,    /* "SM1-CFB1" */
+    1120,    /* "SM1-CFB8" */
+    1115,    /* "SM1-ECB" */
+    1117,    /* "SM1-OFB" */
+    1133,    /* "SM2Sign-with-Blake2b512" */
+    1134,    /* "SM2Sign-with-Blake2s256" */
+    1131,    /* "SM2Sign-with-RMD160" */
+    1126,    /* "SM2Sign-with-SHA1" */
+    1129,    /* "SM2Sign-with-SHA224" */
+    1127,    /* "SM2Sign-with-SHA256" */
+    1130,    /* "SM2Sign-with-SHA384" */
+    1128,    /* "SM2Sign-with-SHA511" */
+    1125,    /* "SM2Sign-with-SM3" */
+    1132,    /* "SM2Sign-with-Whirlpool" */
+    1148,    /* "SM3" */
+    1163,    /* "SM5" */
+    1165,    /* "SM6-CBC" */
+    1167,    /* "SM6-CFB" */
+    1164,    /* "SM6-ECB" */
+    1166,    /* "SM6-OFB" */
+     188,    /* "SMIME" */
+     167,    /* "SMIME-CAPS" */
+    1151,    /* "SMS4-CBC" */
+    1158,    /* "SMS4-CCM" */
+    1153,    /* "SMS4-CFB" */
+    1154,    /* "SMS4-CFB1" */
+    1155,    /* "SMS4-CFB8" */
+    1156,    /* "SMS4-CTR" */
+    1150,    /* "SMS4-ECB" */
+    1157,    /* "SMS4-GCM" */
+    1162,    /* "SMS4-OCB" */
+    1152,    /* "SMS4-OFB" */
+    1160,    /* "SMS4-WRAP" */
+    1161,    /* "SMS4-WRAP-PAD" */
+    1159,    /* "SMS4-XTS" */
+     100,    /* "SN" */
+    1006,    /* "SNILS" */
+    1109,    /* "SSF33-CBC" */
+    1114,    /* "SSF33-CBC-MAC" */
+    1111,    /* "SSF33-CFB" */
+    1112,    /* "SSF33-CFB1" */
+    1113,    /* "SSF33-CFB8" */
+    1108,    /* "SSF33-ECB" */
+    1110,    /* "SSF33-OFB" */
+      16,    /* "ST" */
+     143,    /* "SXNetID" */
+    1021,    /* "TLS1-PRF" */
+     458,    /* "UID" */
+       0,    /* "UNDEF" */
+    1034,    /* "X25519" */
+    1035,    /* "X448" */
+      11,    /* "X500" */
+     378,    /* "X500algorithms" */
+      12,    /* "X509" */
+     184,    /* "X9-57" */
+     185,    /* "X9cm" */
+     125,    /* "ZLIB" */
+    1185,    /* "ZUC" */
+    1213,    /* "ZUC-MAC" */
+    1212,    /* "ZUC256" */
+    1214,    /* "ZUC256-MAC" */
+    1217,    /* "ZUC256-MAC128" */
+    1215,    /* "ZUC256-MAC32" */
+    1216,    /* "ZUC256-MAC64" */
+     478,    /* "aRecord" */
+     289,    /* "aaControls" */
+     287,    /* "ac-auditEntity" */
+     397,    /* "ac-proxying" */
+     288,    /* "ac-targeting" */
+     368,    /* "acceptableResponses" */
+     446,    /* "account" */
+     363,    /* "ad_timestamping" */
+    1071,    /* "aes128-cbc-in-ecies" */
+    1074,    /* "aes128-ctr-in-ecies" */
+    1072,    /* "aes192-cbc-in-ecies" */
+    1075,    /* "aes192-ctr-in-ecies" */
+    1073,    /* "aes256-cbc-in-ecies" */
+    1076,    /* "aes256-ctr-in-ecies" */
+     376,    /* "algorithm" */
+     405,    /* "ansi-X9-62" */
+     910,    /* "anyExtendedKeyUsage" */
+     746,    /* "anyPolicy" */
+     370,    /* "archiveCutoff" */
+     484,    /* "associatedDomain" */
+     485,    /* "associatedName" */
+     501,    /* "audio" */
+     177,    /* "authorityInfoAccess" */
+      90,    /* "authorityKeyIdentifier" */
+     882,    /* "authorityRevocationList" */
+      87,    /* "basicConstraints" */
+     365,    /* "basicOCSPResponse" */
+    1104,    /* "bb1ibe" */
+    1103,    /* "bfibe" */
+     285,    /* "biometricInfo" */
+     921,    /* "brainpoolP160r1" */
+     922,    /* "brainpoolP160t1" */
+     923,    /* "brainpoolP192r1" */
+     924,    /* "brainpoolP192t1" */
+     925,    /* "brainpoolP224r1" */
+     926,    /* "brainpoolP224t1" */
+     927,    /* "brainpoolP256r1" */
+     928,    /* "brainpoolP256t1" */
+     929,    /* "brainpoolP320r1" */
+     930,    /* "brainpoolP320t1" */
+     931,    /* "brainpoolP384r1" */
+     932,    /* "brainpoolP384t1" */
+     933,    /* "brainpoolP512r1" */
+     934,    /* "brainpoolP512t1" */
+     494,    /* "buildingName" */
+     860,    /* "businessCategory" */
+    1188,    /* "bwips" */
+     691,    /* "c2onb191v4" */
+     692,    /* "c2onb191v5" */
+     697,    /* "c2onb239v4" */
+     698,    /* "c2onb239v5" */
+     684,    /* "c2pnb163v1" */
+     685,    /* "c2pnb163v2" */
+     686,    /* "c2pnb163v3" */
+     687,    /* "c2pnb176v1" */
+     693,    /* "c2pnb208w1" */
+     699,    /* "c2pnb272w1" */
+     700,    /* "c2pnb304w1" */
+     702,    /* "c2pnb368w1" */
+     688,    /* "c2tnb191v1" */
+     689,    /* "c2tnb191v2" */
+     690,    /* "c2tnb191v3" */
+     694,    /* "c2tnb239v1" */
+     695,    /* "c2tnb239v2" */
+     696,    /* "c2tnb239v3" */
+     701,    /* "c2tnb359v1" */
+     703,    /* "c2tnb431r1" */
+     881,    /* "cACertificate" */
+     483,    /* "cNAMERecord" */
+     179,    /* "caIssuers" */
+     785,    /* "caRepository" */
+    1023,    /* "capwapAC" */
+    1024,    /* "capwapWTP" */
+     443,    /* "caseIgnoreIA5StringSyntax" */
+     152,    /* "certBag" */
+     677,    /* "certicom-arc" */
+     771,    /* "certificateIssuer" */
+      89,    /* "certificatePolicies" */
+     883,    /* "certificateRevocationList" */
+      54,    /* "challengePassword" */
+     407,    /* "characteristic-two-field" */
+     395,    /* "clearance" */
+     130,    /* "clientAuth" */
+    1079,    /* "cmac-aes128-ecies" */
+    1080,    /* "cmac-aes192-ecies" */
+    1081,    /* "cmac-aes256-ecies" */
+     131,    /* "codeSigning" */
+      50,    /* "contentType" */
+      53,    /* "countersignature" */
+    1202,    /* "cpk-map" */
+    1203,    /* "cpk-map-sha1" */
+    1204,    /* "cpk-map-sha256" */
+    1206,    /* "cpk-map-sha384" */
+    1207,    /* "cpk-map-sha512" */
+    1205,    /* "cpk-map-sm3" */
+     153,    /* "crlBag" */
+     103,    /* "crlDistributionPoints" */
+      88,    /* "crlNumber" */
+     884,    /* "crossCertificatePair" */
+     806,    /* "cryptocom" */
+     805,    /* "cryptopro" */
+     954,    /* "ct_cert_scts" */
+     952,    /* "ct_precert_poison" */
+     951,    /* "ct_precert_scts" */
+     953,    /* "ct_precert_signer" */
+     500,    /* "dITRedirect" */
+     451,    /* "dNSDomain" */
+     495,    /* "dSAQuality" */
+     434,    /* "data" */
+     390,    /* "dcobject" */
+     140,    /* "deltaCRL" */
+     891,    /* "deltaRevocationList" */
+     107,    /* "description" */
+     871,    /* "destinationIndicator" */
+     947,    /* "dh-cofactor-kdf" */
+     946,    /* "dh-std-kdf" */
+      28,    /* "dhKeyAgreement" */
+     941,    /* "dhSinglePass-cofactorDH-sha1kdf-scheme" */
+     942,    /* "dhSinglePass-cofactorDH-sha224kdf-scheme" */
+     943,    /* "dhSinglePass-cofactorDH-sha256kdf-scheme" */
+     944,    /* "dhSinglePass-cofactorDH-sha384kdf-scheme" */
+     945,    /* "dhSinglePass-cofactorDH-sha512kdf-scheme" */
+     936,    /* "dhSinglePass-stdDH-sha1kdf-scheme" */
+     937,    /* "dhSinglePass-stdDH-sha224kdf-scheme" */
+     938,    /* "dhSinglePass-stdDH-sha256kdf-scheme" */
+     939,    /* "dhSinglePass-stdDH-sha384kdf-scheme" */
+     940,    /* "dhSinglePass-stdDH-sha512kdf-scheme" */
+     920,    /* "dhpublicnumber" */
+     382,    /* "directory" */
+     887,    /* "distinguishedName" */
+     892,    /* "dmdName" */
+     174,    /* "dnQualifier" */
+     447,    /* "document" */
+     471,    /* "documentAuthor" */
+     468,    /* "documentIdentifier" */
+     472,    /* "documentLocation" */
+     502,    /* "documentPublisher" */
+     449,    /* "documentSeries" */
+     469,    /* "documentTitle" */
+     470,    /* "documentVersion" */
+     392,    /* "domain" */
+     452,    /* "domainRelatedObject" */
+     802,    /* "dsa_with_SHA224" */
+     803,    /* "dsa_with_SHA256" */
+     791,    /* "ecdsa-with-Recommended" */
+     416,    /* "ecdsa-with-SHA1" */
+     793,    /* "ecdsa-with-SHA224" */
+     794,    /* "ecdsa-with-SHA256" */
+     795,    /* "ecdsa-with-SHA384" */
+     796,    /* "ecdsa-with-SHA512" */
+     792,    /* "ecdsa-with-Specified" */
+    1063,    /* "ecies-recommendedParameters" */
+    1064,    /* "ecies-specifiedParameters" */
+    1096,    /* "ecies-with-x9-63-sha1-aes128-cbc-cmac" */
+    1087,    /* "ecies-with-x9-63-sha1-aes128-cbc-hmac" */
+    1084,    /* "ecies-with-x9-63-sha1-xor-hmac" */
+    1097,    /* "ecies-with-x9-63-sha256-aes128-cbc-cmac" */
+    1088,    /* "ecies-with-x9-63-sha256-aes128-cbc-hmac" */
+    1092,    /* "ecies-with-x9-63-sha256-aes128-cbc-hmac-half" */
+    1099,    /* "ecies-with-x9-63-sha256-aes128-ctr-cmac" */
+    1090,    /* "ecies-with-x9-63-sha256-aes128-ctr-hmac" */
+    1094,    /* "ecies-with-x9-63-sha256-aes128-ctr-hmac-half" */
+    1085,    /* "ecies-with-x9-63-sha256-xor-hmac" */
+    1098,    /* "ecies-with-x9-63-sha512-aes256-cbc-cmac" */
+    1089,    /* "ecies-with-x9-63-sha512-aes256-cbc-hmac" */
+    1093,    /* "ecies-with-x9-63-sha512-aes256-cbc-hmac-half" */
+    1100,    /* "ecies-with-x9-63-sha512-aes256-ctr-cmac" */
+    1091,    /* "ecies-with-x9-63-sha512-aes256-ctr-hmac" */
+    1095,    /* "ecies-with-x9-63-sha512-aes256-ctr-hmac-half" */
+    1086,    /* "ecies-with-x9-63-sha512-xor-hmac" */
+      48,    /* "emailAddress" */
+     132,    /* "emailProtection" */
+     885,    /* "enhancedSearchGuide" */
+     389,    /* "enterprises" */
+     384,    /* "experimental" */
+     172,    /* "extReq" */
+      56,    /* "extendedCertificateAttributes" */
+     126,    /* "extendedKeyUsage" */
+     372,    /* "extendedStatus" */
+     867,    /* "facsimileTelephoneNumber" */
+     462,    /* "favouriteDrink" */
+     857,    /* "freshestCRL" */
+     453,    /* "friendlyCountry" */
+     490,    /* "friendlyCountryName" */
+     156,    /* "friendlyName" */
+     509,    /* "generationQualifier" */
+    1200,    /* "gmssl" */
+     815,    /* "gost-mac" */
+     976,    /* "gost-mac-12" */
+     811,    /* "gost2001" */
+     851,    /* "gost2001cc" */
+     979,    /* "gost2012_256" */
+     980,    /* "gost2012_512" */
+     813,    /* "gost89" */
+    1009,    /* "gost89-cbc" */
+     814,    /* "gost89-cnt" */
+     975,    /* "gost89-cnt-12" */
+    1011,    /* "gost89-ctr" */
+    1010,    /* "gost89-ecb" */
+     812,    /* "gost94" */
+     850,    /* "gost94cc" */
+    1015,    /* "grasshopper-cbc" */
+    1016,    /* "grasshopper-cfb" */
+    1013,    /* "grasshopper-ctr" */
+    1012,    /* "grasshopper-ecb" */
+    1017,    /* "grasshopper-mac" */
+    1014,    /* "grasshopper-ofb" */
+    1077,    /* "hmac-full-ecies" */
+    1078,    /* "hmac-half-ecies" */
+     797,    /* "hmacWithMD5" */
+     163,    /* "hmacWithSHA1" */
+     798,    /* "hmacWithSHA224" */
+     799,    /* "hmacWithSHA256" */
+     800,    /* "hmacWithSHA384" */
+     801,    /* "hmacWithSHA512" */
+     432,    /* "holdInstructionCallIssuer" */
+     430,    /* "holdInstructionCode" */
+     431,    /* "holdInstructionNone" */
+     433,    /* "holdInstructionReject" */
+     486,    /* "homePostalAddress" */
+     473,    /* "homeTelephoneNumber" */
+     466,    /* "host" */
+     889,    /* "houseIdentifier" */
+     442,    /* "iA5StringSyntax" */
+    1101,    /* "ibcs1" */
+     783,    /* "id-DHBasedMac" */
+     824,    /* "id-Gost28147-89-CryptoPro-A-ParamSet" */
+     825,    /* "id-Gost28147-89-CryptoPro-B-ParamSet" */
+     826,    /* "id-Gost28147-89-CryptoPro-C-ParamSet" */
+     827,    /* "id-Gost28147-89-CryptoPro-D-ParamSet" */
+     819,    /* "id-Gost28147-89-CryptoPro-KeyMeshing" */
+     829,    /* "id-Gost28147-89-CryptoPro-Oscar-1-0-ParamSet" */
+     828,    /* "id-Gost28147-89-CryptoPro-Oscar-1-1-ParamSet" */
+     830,    /* "id-Gost28147-89-CryptoPro-RIC-1-ParamSet" */
+     820,    /* "id-Gost28147-89-None-KeyMeshing" */
+     823,    /* "id-Gost28147-89-TestParamSet" */
+     849,    /* "id-Gost28147-89-cc" */
+     840,    /* "id-GostR3410-2001-CryptoPro-A-ParamSet" */
+     841,    /* "id-GostR3410-2001-CryptoPro-B-ParamSet" */
+     842,    /* "id-GostR3410-2001-CryptoPro-C-ParamSet" */
+     843,    /* "id-GostR3410-2001-CryptoPro-XchA-ParamSet" */
+     844,    /* "id-GostR3410-2001-CryptoPro-XchB-ParamSet" */
+     854,    /* "id-GostR3410-2001-ParamSet-cc" */
+     839,    /* "id-GostR3410-2001-TestParamSet" */
+     817,    /* "id-GostR3410-2001DH" */
+     832,    /* "id-GostR3410-94-CryptoPro-A-ParamSet" */
+     833,    /* "id-GostR3410-94-CryptoPro-B-ParamSet" */
+     834,    /* "id-GostR3410-94-CryptoPro-C-ParamSet" */
+     835,    /* "id-GostR3410-94-CryptoPro-D-ParamSet" */
+     836,    /* "id-GostR3410-94-CryptoPro-XchA-ParamSet" */
+     837,    /* "id-GostR3410-94-CryptoPro-XchB-ParamSet" */
+     838,    /* "id-GostR3410-94-CryptoPro-XchC-ParamSet" */
+     831,    /* "id-GostR3410-94-TestParamSet" */
+     845,    /* "id-GostR3410-94-a" */
+     846,    /* "id-GostR3410-94-aBis" */
+     847,    /* "id-GostR3410-94-b" */
+     848,    /* "id-GostR3410-94-bBis" */
+     818,    /* "id-GostR3410-94DH" */
+     822,    /* "id-GostR3411-94-CryptoProParamSet" */
+     821,    /* "id-GostR3411-94-TestParamSet" */
+     807,    /* "id-GostR3411-94-with-GostR3410-2001" */
+     853,    /* "id-GostR3411-94-with-GostR3410-2001-cc" */
+     808,    /* "id-GostR3411-94-with-GostR3410-94" */
+     852,    /* "id-GostR3411-94-with-GostR3410-94-cc" */
+     810,    /* "id-HMACGostR3411-94" */
+     782,    /* "id-PasswordBasedMAC" */
+     266,    /* "id-aca" */
+     355,    /* "id-aca-accessIdentity" */
+     354,    /* "id-aca-authenticationInfo" */
+     356,    /* "id-aca-chargingIdentity" */
+     399,    /* "id-aca-encAttrs" */
+     357,    /* "id-aca-group" */
+     358,    /* "id-aca-role" */
+     176,    /* "id-ad" */
+     896,    /* "id-aes128-CCM" */
+     895,    /* "id-aes128-GCM" */
+     788,    /* "id-aes128-wrap" */
+     897,    /* "id-aes128-wrap-pad" */
+     899,    /* "id-aes192-CCM" */
+     898,    /* "id-aes192-GCM" */
+     789,    /* "id-aes192-wrap" */
+     900,    /* "id-aes192-wrap-pad" */
+     902,    /* "id-aes256-CCM" */
+     901,    /* "id-aes256-GCM" */
+     790,    /* "id-aes256-wrap" */
+     903,    /* "id-aes256-wrap-pad" */
+     262,    /* "id-alg" */
+     893,    /* "id-alg-PWRI-KEK" */
+     323,    /* "id-alg-des40" */
+     326,    /* "id-alg-dh-pop" */
+     325,    /* "id-alg-dh-sig-hmac-sha1" */
+     324,    /* "id-alg-noSignature" */
+     907,    /* "id-camellia128-wrap" */
+     908,    /* "id-camellia192-wrap" */
+     909,    /* "id-camellia256-wrap" */
+     268,    /* "id-cct" */
+     361,    /* "id-cct-PKIData" */
+     362,    /* "id-cct-PKIResponse" */
+     360,    /* "id-cct-crs" */
+      81,    /* "id-ce" */
+     680,    /* "id-characteristic-two-basis" */
+     263,    /* "id-cmc" */
+     334,    /* "id-cmc-addExtensions" */
+     346,    /* "id-cmc-confirmCertAcceptance" */
+     330,    /* "id-cmc-dataReturn" */
+     336,    /* "id-cmc-decryptedPOP" */
+     335,    /* "id-cmc-encryptedPOP" */
+     339,    /* "id-cmc-getCRL" */
+     338,    /* "id-cmc-getCert" */
+     328,    /* "id-cmc-identification" */
+     329,    /* "id-cmc-identityProof" */
+     337,    /* "id-cmc-lraPOPWitness" */
+     344,    /* "id-cmc-popLinkRandom" */
+     345,    /* "id-cmc-popLinkWitness" */
+     343,    /* "id-cmc-queryPending" */
+     333,    /* "id-cmc-recipientNonce" */
+     341,    /* "id-cmc-regInfo" */
+     342,    /* "id-cmc-responseInfo" */
+     340,    /* "id-cmc-revokeRequest" */
+     332,    /* "id-cmc-senderNonce" */
+     327,    /* "id-cmc-statusInfo" */
+     331,    /* "id-cmc-transactionId" */
+     787,    /* "id-ct-asciiTextWithCRLF" */
+    1060,    /* "id-ct-xml" */
+     408,    /* "id-ecPublicKey" */
+    1061,    /* "id-ecSigType" */
+     508,    /* "id-hex-multipart-message" */
+     507,    /* "id-hex-partial-message" */
+     260,    /* "id-it" */
+     302,    /* "id-it-caKeyUpdateInfo" */
+     298,    /* "id-it-caProtEncCert" */
+     311,    /* "id-it-confirmWaitTime" */
+     303,    /* "id-it-currentCRL" */
+     300,    /* "id-it-encKeyPairTypes" */
+     310,    /* "id-it-implicitConfirm" */
+     308,    /* "id-it-keyPairParamRep" */
+     307,    /* "id-it-keyPairParamReq" */
+     312,    /* "id-it-origPKIMessage" */
+     301,    /* "id-it-preferredSymmAlg" */
+     309,    /* "id-it-revPassphrase" */
+     299,    /* "id-it-signKeyPairTypes" */
+     305,    /* "id-it-subscriptionRequest" */
+     306,    /* "id-it-subscriptionResponse" */
+     784,    /* "id-it-suppLangTags" */
+     304,    /* "id-it-unsupportedOIDs" */
+     128,    /* "id-kp" */
+     280,    /* "id-mod-attribute-cert" */
+     274,    /* "id-mod-cmc" */
+     277,    /* "id-mod-cmp" */
+     284,    /* "id-mod-cmp2000" */
+     273,    /* "id-mod-crmf" */
+     283,    /* "id-mod-dvcs" */
+     275,    /* "id-mod-kea-profile-88" */
+     276,    /* "id-mod-kea-profile-93" */
+     282,    /* "id-mod-ocsp" */
+     278,    /* "id-mod-qualified-cert-88" */
+     279,    /* "id-mod-qualified-cert-93" */
+     281,    /* "id-mod-timestamp-protocol" */
+     264,    /* "id-on" */
+     858,    /* "id-on-permanentIdentifier" */
+     347,    /* "id-on-personalData" */
+     265,    /* "id-pda" */
+     352,    /* "id-pda-countryOfCitizenship" */
+     353,    /* "id-pda-countryOfResidence" */
+     348,    /* "id-pda-dateOfBirth" */
+     351,    /* "id-pda-gender" */
+     349,    /* "id-pda-placeOfBirth" */
+     175,    /* "id-pe" */
+    1031,    /* "id-pkinit" */
+     261,    /* "id-pkip" */
+     258,    /* "id-pkix-mod" */
+     269,    /* "id-pkix1-explicit-88" */
+     271,    /* "id-pkix1-explicit-93" */
+     270,    /* "id-pkix1-implicit-88" */
+     272,    /* "id-pkix1-implicit-93" */
+     662,    /* "id-ppl" */
+     664,    /* "id-ppl-anyLanguage" */
+     667,    /* "id-ppl-independent" */
+     665,    /* "id-ppl-inheritAll" */
+     267,    /* "id-qcs" */
+     359,    /* "id-qcs-pkixQCSyntax-v1" */
+     259,    /* "id-qt" */
+     164,    /* "id-qt-cps" */
+     165,    /* "id-qt-unotice" */
+     313,    /* "id-regCtrl" */
+     316,    /* "id-regCtrl-authenticator" */
+     319,    /* "id-regCtrl-oldCertID" */
+     318,    /* "id-regCtrl-pkiArchiveOptions" */
+     317,    /* "id-regCtrl-pkiPublicationInfo" */
+     320,    /* "id-regCtrl-protocolEncrKey" */
+     315,    /* "id-regCtrl-regToken" */
+     314,    /* "id-regInfo" */
+     322,    /* "id-regInfo-certReq" */
+     321,    /* "id-regInfo-utf8Pairs" */
+     973,    /* "id-scrypt" */
+     512,    /* "id-set" */
+    1174,    /* "id-sm9MasterSecret" */
+    1168,    /* "id-sm9PublicKey" */
+     191,    /* "id-smime-aa" */
+     215,    /* "id-smime-aa-contentHint" */
+     218,    /* "id-smime-aa-contentIdentifier" */
+     221,    /* "id-smime-aa-contentReference" */
+     240,    /* "id-smime-aa-dvcs-dvc" */
+     217,    /* "id-smime-aa-encapContentType" */
+     222,    /* "id-smime-aa-encrypKeyPref" */
+     220,    /* "id-smime-aa-equivalentLabels" */
+     232,    /* "id-smime-aa-ets-CertificateRefs" */
+     233,    /* "id-smime-aa-ets-RevocationRefs" */
+     238,    /* "id-smime-aa-ets-archiveTimeStamp" */
+     237,    /* "id-smime-aa-ets-certCRLTimestamp" */
+     234,    /* "id-smime-aa-ets-certValues" */
+     227,    /* "id-smime-aa-ets-commitmentType" */
+     231,    /* "id-smime-aa-ets-contentTimestamp" */
+     236,    /* "id-smime-aa-ets-escTimeStamp" */
+     230,    /* "id-smime-aa-ets-otherSigCert" */
+     235,    /* "id-smime-aa-ets-revocationValues" */
+     226,    /* "id-smime-aa-ets-sigPolicyId" */
+     229,    /* "id-smime-aa-ets-signerAttr" */
+     228,    /* "id-smime-aa-ets-signerLocation" */
+     219,    /* "id-smime-aa-macValue" */
+     214,    /* "id-smime-aa-mlExpandHistory" */
+     216,    /* "id-smime-aa-msgSigDigest" */
+     212,    /* "id-smime-aa-receiptRequest" */
+     213,    /* "id-smime-aa-securityLabel" */
+     239,    /* "id-smime-aa-signatureType" */
+     223,    /* "id-smime-aa-signingCertificate" */
+     224,    /* "id-smime-aa-smimeEncryptCerts" */
+     225,    /* "id-smime-aa-timeStampToken" */
+     192,    /* "id-smime-alg" */
+     243,    /* "id-smime-alg-3DESwrap" */
+     246,    /* "id-smime-alg-CMS3DESwrap" */
+     247,    /* "id-smime-alg-CMSRC2wrap" */
+     245,    /* "id-smime-alg-ESDH" */
+     241,    /* "id-smime-alg-ESDHwith3DES" */
+     242,    /* "id-smime-alg-ESDHwithRC2" */
+     244,    /* "id-smime-alg-RC2wrap" */
+     193,    /* "id-smime-cd" */
+     248,    /* "id-smime-cd-ldap" */
+     190,    /* "id-smime-ct" */
+     210,    /* "id-smime-ct-DVCSRequestData" */
+     211,    /* "id-smime-ct-DVCSResponseData" */
+     208,    /* "id-smime-ct-TDTInfo" */
+     207,    /* "id-smime-ct-TSTInfo" */
+     205,    /* "id-smime-ct-authData" */
+    1059,    /* "id-smime-ct-authEnvelopedData" */
+     786,    /* "id-smime-ct-compressedData" */
+    1058,    /* "id-smime-ct-contentCollection" */
+     209,    /* "id-smime-ct-contentInfo" */
+     206,    /* "id-smime-ct-publishCert" */
+     204,    /* "id-smime-ct-receipt" */
+     195,    /* "id-smime-cti" */
+     255,    /* "id-smime-cti-ets-proofOfApproval" */
+     256,    /* "id-smime-cti-ets-proofOfCreation" */
+     253,    /* "id-smime-cti-ets-proofOfDelivery" */
+     251,    /* "id-smime-cti-ets-proofOfOrigin" */
+     252,    /* "id-smime-cti-ets-proofOfReceipt" */
+     254,    /* "id-smime-cti-ets-proofOfSender" */
+     189,    /* "id-smime-mod" */
+     196,    /* "id-smime-mod-cms" */
+     197,    /* "id-smime-mod-ess" */
+     202,    /* "id-smime-mod-ets-eSigPolicy-88" */
+     203,    /* "id-smime-mod-ets-eSigPolicy-97" */
+     200,    /* "id-smime-mod-ets-eSignature-88" */
+     201,    /* "id-smime-mod-ets-eSignature-97" */
+     199,    /* "id-smime-mod-msg-v3" */
+     198,    /* "id-smime-mod-oid" */
+     194,    /* "id-smime-spq" */
+     250,    /* "id-smime-spq-ets-sqt-unotice" */
+     249,    /* "id-smime-spq-ets-sqt-uri" */
+     974,    /* "id-tc26" */
+     991,    /* "id-tc26-agreement" */
+     992,    /* "id-tc26-agreement-gost-3410-2012-256" */
+     993,    /* "id-tc26-agreement-gost-3410-2012-512" */
+     977,    /* "id-tc26-algorithms" */
+     990,    /* "id-tc26-cipher" */
+    1001,    /* "id-tc26-cipher-constants" */
+     994,    /* "id-tc26-constants" */
+     981,    /* "id-tc26-digest" */
+    1000,    /* "id-tc26-digest-constants" */
+    1002,    /* "id-tc26-gost-28147-constants" */
+    1003,    /* "id-tc26-gost-28147-param-Z" */
+     996,    /* "id-tc26-gost-3410-2012-512-constants" */
+     998,    /* "id-tc26-gost-3410-2012-512-paramSetA" */
+     999,    /* "id-tc26-gost-3410-2012-512-paramSetB" */
+     997,    /* "id-tc26-gost-3410-2012-512-paramSetTest" */
+     988,    /* "id-tc26-hmac-gost-3411-2012-256" */
+     989,    /* "id-tc26-hmac-gost-3411-2012-512" */
+     987,    /* "id-tc26-mac" */
+     978,    /* "id-tc26-sign" */
+     995,    /* "id-tc26-sign-constants" */
+     984,    /* "id-tc26-signwithdigest" */
+     985,    /* "id-tc26-signwithdigest-gost3410-2012-256" */
+     986,    /* "id-tc26-signwithdigest-gost3410-2012-512" */
+     676,    /* "identified-organization" */
+    1068,    /* "ikev2-kdf" */
+     461,    /* "info" */
+     748,    /* "inhibitAnyPolicy" */
+     101,    /* "initials" */
+     647,    /* "international-organizations" */
+     869,    /* "internationaliSDNNumber" */
+     142,    /* "invalidityDate" */
+     294,    /* "ipsecEndSystem" */
+    1022,    /* "ipsecIKE" */
+     295,    /* "ipsecTunnel" */
+     296,    /* "ipsecUser" */
+      86,    /* "issuerAltName" */
+    1008,    /* "issuerSignTool" */
+     770,    /* "issuingDistributionPoint" */
+     492,    /* "janetMailbox" */
+     957,    /* "jurisdictionC" */
+     955,    /* "jurisdictionL" */
+     956,    /* "jurisdictionST" */
+     150,    /* "keyBag" */
+      83,    /* "keyUsage" */
+     477,    /* "lastModifiedBy" */
+     476,    /* "lastModifiedTime" */
+     157,    /* "localKeyID" */
+     480,    /* "mXRecord" */
+     460,    /* "mail" */
+     493,    /* "mailPreferenceOption" */
+     467,    /* "manager" */
+     982,    /* "md_gost12_256" */
+     983,    /* "md_gost12_512" */
+     809,    /* "md_gost94" */
+     875,    /* "member" */
+     182,    /* "member-body" */
+      51,    /* "messageDigest" */
+     383,    /* "mgmt" */
+     504,    /* "mime-mhs" */
+     506,    /* "mime-mhs-bodies" */
+     505,    /* "mime-mhs-headings" */
+     488,    /* "mobileTelephoneNumber" */
+     136,    /* "msCTLSign" */
+     135,    /* "msCodeCom" */
+     134,    /* "msCodeInd" */
+     138,    /* "msEFS" */
+     171,    /* "msExtReq" */
+     137,    /* "msSGC" */
+     648,    /* "msSmartcardLogin" */
+     649,    /* "msUPN" */
+     481,    /* "nSRecord" */
+     173,    /* "name" */
+     666,    /* "nameConstraints" */
+    1066,    /* "nist-concatenation-kdf" */
+     369,    /* "noCheck" */
+     403,    /* "noRevAvail" */
+      72,    /* "nsBaseUrl" */
+      76,    /* "nsCaPolicyUrl" */
+      74,    /* "nsCaRevocationUrl" */
+      58,    /* "nsCertExt" */
+      79,    /* "nsCertSequence" */
+      71,    /* "nsCertType" */
+      78,    /* "nsComment" */
+      59,    /* "nsDataType" */
+      75,    /* "nsRenewalUrl" */
+      73,    /* "nsRevocationUrl" */
+     139,    /* "nsSGC" */
+      77,    /* "nsSslServerName" */
+     681,    /* "onBasis" */
+     491,    /* "organizationalStatus" */
+    1106,    /* "oscca" */
+     475,    /* "otherMailbox" */
+     876,    /* "owner" */
+     489,    /* "pagerTelephoneNumber" */
+    1208,    /* "paillier" */
+     374,    /* "path" */
+     112,    /* "pbeWithMD5AndCast5CBC" */
+     499,    /* "personalSignature" */
+     487,    /* "personalTitle" */
+     464,    /* "photo" */
+     863,    /* "physicalDeliveryOfficeName" */
+     437,    /* "pilot" */
+     439,    /* "pilotAttributeSyntax" */
+     438,    /* "pilotAttributeType" */
+     479,    /* "pilotAttributeType27" */
+     456,    /* "pilotDSA" */
+     441,    /* "pilotGroups" */
+     444,    /* "pilotObject" */
+     440,    /* "pilotObjectClass" */
+     455,    /* "pilotOrganization" */
+     445,    /* "pilotPerson" */
+    1032,    /* "pkInitClientAuth" */
+    1033,    /* "pkInitKDC" */
+       2,    /* "pkcs" */
+     186,    /* "pkcs1" */
+      27,    /* "pkcs3" */
+     187,    /* "pkcs5" */
+      20,    /* "pkcs7" */
+      21,    /* "pkcs7-data" */
+      25,    /* "pkcs7-digestData" */
+      26,    /* "pkcs7-encryptedData" */
+      23,    /* "pkcs7-envelopedData" */
+      24,    /* "pkcs7-signedAndEnvelopedData" */
+      22,    /* "pkcs7-signedData" */
+     151,    /* "pkcs8ShroudedKeyBag" */
+      47,    /* "pkcs9" */
+     401,    /* "policyConstraints" */
+     747,    /* "policyMappings" */
+     862,    /* "postOfficeBox" */
+     861,    /* "postalAddress" */
+     661,    /* "postalCode" */
+     683,    /* "ppBasis" */
+     872,    /* "preferredDeliveryMethod" */
+     873,    /* "presentationAddress" */
+     816,    /* "prf-gostr3411-94" */
+     406,    /* "prime-field" */
+     409,    /* "prime192v1" */
+     410,    /* "prime192v2" */
+     411,    /* "prime192v3" */
+     412,    /* "prime239v1" */
+     413,    /* "prime239v2" */
+     414,    /* "prime239v3" */
+     415,    /* "prime256v1" */
+     385,    /* "private" */
+      84,    /* "privateKeyUsagePeriod" */
+     886,    /* "protocolInformation" */
+     663,    /* "proxyCertInfo" */
+     510,    /* "pseudonym" */
+     435,    /* "pss" */
+     286,    /* "qcStatements" */
+     457,    /* "qualityLabelledData" */
+     450,    /* "rFC822localPart" */
+     870,    /* "registeredAddress" */
+     400,    /* "role" */
+     877,    /* "roleOccupant" */
+     448,    /* "room" */
+     463,    /* "roomNumber" */
+       6,    /* "rsaEncryption" */
+     644,    /* "rsaOAEPEncryptionSET" */
+     377,    /* "rsaSignature" */
+       1,    /* "rsadsi" */
+     482,    /* "sOARecord" */
+     155,    /* "safeContentsBag" */
+     291,    /* "sbgp-autonomousSysNum" */
+     290,    /* "sbgp-ipAddrBlock" */
+     292,    /* "sbgp-routerIdentifier" */
+     159,    /* "sdsiCertificate" */
+     859,    /* "searchGuide" */
+    1062,    /* "secg-scheme" */
+     704,    /* "secp112r1" */
+     705,    /* "secp112r2" */
+     706,    /* "secp128r1" */
+     707,    /* "secp128r2" */
+     708,    /* "secp160k1" */
+     709,    /* "secp160r1" */
+     710,    /* "secp160r2" */
+     711,    /* "secp192k1" */
+     712,    /* "secp224k1" */
+     713,    /* "secp224r1" */
+     714,    /* "secp256k1" */
+     715,    /* "secp384r1" */
+     716,    /* "secp521r1" */
+     154,    /* "secretBag" */
+     474,    /* "secretary" */
+     717,    /* "sect113r1" */
+     718,    /* "sect113r2" */
+     719,    /* "sect131r1" */
+     720,    /* "sect131r2" */
+     721,    /* "sect163k1" */
+     722,    /* "sect163r1" */
+     723,    /* "sect163r2" */
+     724,    /* "sect193r1" */
+     725,    /* "sect193r2" */
+     726,    /* "sect233k1" */
+     727,    /* "sect233r1" */
+     728,    /* "sect239k1" */
+     729,    /* "sect283k1" */
+     730,    /* "sect283r1" */
+     731,    /* "sect409k1" */
+     732,    /* "sect409r1" */
+     733,    /* "sect571k1" */
+     734,    /* "sect571r1" */
+    1025,    /* "secureShellClient" */
+    1026,    /* "secureShellServer" */
+     386,    /* "security" */
+     878,    /* "seeAlso" */
+     394,    /* "selected-attribute-types" */
+    1029,    /* "sendOwner" */
+    1030,    /* "sendProxiedOwner" */
+    1028,    /* "sendProxiedRouter" */
+    1027,    /* "sendRouter" */
+     105,    /* "serialNumber" */
+     129,    /* "serverAuth" */
+     371,    /* "serviceLocator" */
+     625,    /* "set-addPolicy" */
+     515,    /* "set-attr" */
+     518,    /* "set-brand" */
+     638,    /* "set-brand-AmericanExpress" */
+     637,    /* "set-brand-Diners" */
+     636,    /* "set-brand-IATA-ATA" */
+     639,    /* "set-brand-JCB" */
+     641,    /* "set-brand-MasterCard" */
+     642,    /* "set-brand-Novus" */
+     640,    /* "set-brand-Visa" */
+     517,    /* "set-certExt" */
+     513,    /* "set-ctype" */
+     514,    /* "set-msgExt" */
+     516,    /* "set-policy" */
+     607,    /* "set-policy-root" */
+     624,    /* "set-rootKeyThumb" */
+     620,    /* "setAttr-Cert" */
+     631,    /* "setAttr-GenCryptgrm" */
+     623,    /* "setAttr-IssCap" */
+     628,    /* "setAttr-IssCap-CVM" */
+     630,    /* "setAttr-IssCap-Sig" */
+     629,    /* "setAttr-IssCap-T2" */
+     621,    /* "setAttr-PGWYcap" */
+     635,    /* "setAttr-SecDevSig" */
+     632,    /* "setAttr-T2Enc" */
+     633,    /* "setAttr-T2cleartxt" */
+     634,    /* "setAttr-TokICCsig" */
+     627,    /* "setAttr-Token-B0Prime" */
+     626,    /* "setAttr-Token-EMV" */
+     622,    /* "setAttr-TokenType" */
+     619,    /* "setCext-IssuerCapabilities" */
+     615,    /* "setCext-PGWYcapabilities" */
+     616,    /* "setCext-TokenIdentifier" */
+     618,    /* "setCext-TokenType" */
+     617,    /* "setCext-Track2Data" */
+     611,    /* "setCext-cCertRequired" */
+     609,    /* "setCext-certType" */
+     608,    /* "setCext-hashedRoot" */
+     610,    /* "setCext-merchData" */
+     613,    /* "setCext-setExt" */
+     614,    /* "setCext-setQualf" */
+     612,    /* "setCext-tunneling" */
+     540,    /* "setct-AcqCardCodeMsg" */
+     576,    /* "setct-AcqCardCodeMsgTBE" */
+     570,    /* "setct-AuthReqTBE" */
+     534,    /* "setct-AuthReqTBS" */
+     527,    /* "setct-AuthResBaggage" */
+     571,    /* "setct-AuthResTBE" */
+     572,    /* "setct-AuthResTBEX" */
+     535,    /* "setct-AuthResTBS" */
+     536,    /* "setct-AuthResTBSX" */
+     528,    /* "setct-AuthRevReqBaggage" */
+     577,    /* "setct-AuthRevReqTBE" */
+     541,    /* "setct-AuthRevReqTBS" */
+     529,    /* "setct-AuthRevResBaggage" */
+     542,    /* "setct-AuthRevResData" */
+     578,    /* "setct-AuthRevResTBE" */
+     579,    /* "setct-AuthRevResTBEB" */
+     543,    /* "setct-AuthRevResTBS" */
+     573,    /* "setct-AuthTokenTBE" */
+     537,    /* "setct-AuthTokenTBS" */
+     600,    /* "setct-BCIDistributionTBS" */
+     558,    /* "setct-BatchAdminReqData" */
+     592,    /* "setct-BatchAdminReqTBE" */
+     559,    /* "setct-BatchAdminResData" */
+     593,    /* "setct-BatchAdminResTBE" */
+     599,    /* "setct-CRLNotificationResTBS" */
+     598,    /* "setct-CRLNotificationTBS" */
+     580,    /* "setct-CapReqTBE" */
+     581,    /* "setct-CapReqTBEX" */
+     544,    /* "setct-CapReqTBS" */
+     545,    /* "setct-CapReqTBSX" */
+     546,    /* "setct-CapResData" */
+     582,    /* "setct-CapResTBE" */
+     583,    /* "setct-CapRevReqTBE" */
+     584,    /* "setct-CapRevReqTBEX" */
+     547,    /* "setct-CapRevReqTBS" */
+     548,    /* "setct-CapRevReqTBSX" */
+     549,    /* "setct-CapRevResData" */
+     585,    /* "setct-CapRevResTBE" */
+     538,    /* "setct-CapTokenData" */
+     530,    /* "setct-CapTokenSeq" */
+     574,    /* "setct-CapTokenTBE" */
+     575,    /* "setct-CapTokenTBEX" */
+     539,    /* "setct-CapTokenTBS" */
+     560,    /* "setct-CardCInitResTBS" */
+     566,    /* "setct-CertInqReqTBS" */
+     563,    /* "setct-CertReqData" */
+     595,    /* "setct-CertReqTBE" */
+     596,    /* "setct-CertReqTBEX" */
+     564,    /* "setct-CertReqTBS" */
+     565,    /* "setct-CertResData" */
+     597,    /* "setct-CertResTBE" */
+     586,    /* "setct-CredReqTBE" */
+     587,    /* "setct-CredReqTBEX" */
+     550,    /* "setct-CredReqTBS" */
+     551,    /* "setct-CredReqTBSX" */
+     552,    /* "setct-CredResData" */
+     588,    /* "setct-CredResTBE" */
+     589,    /* "setct-CredRevReqTBE" */
+     590,    /* "setct-CredRevReqTBEX" */
+     553,    /* "setct-CredRevReqTBS" */
+     554,    /* "setct-CredRevReqTBSX" */
+     555,    /* "setct-CredRevResData" */
+     591,    /* "setct-CredRevResTBE" */
+     567,    /* "setct-ErrorTBS" */
+     526,    /* "setct-HODInput" */
+     561,    /* "setct-MeAqCInitResTBS" */
+     522,    /* "setct-OIData" */
+     519,    /* "setct-PANData" */
+     521,    /* "setct-PANOnly" */
+     520,    /* "setct-PANToken" */
+     556,    /* "setct-PCertReqData" */
+     557,    /* "setct-PCertResTBS" */
+     523,    /* "setct-PI" */
+     532,    /* "setct-PI-TBS" */
+     524,    /* "setct-PIData" */
+     525,    /* "setct-PIDataUnsigned" */
+     568,    /* "setct-PIDualSignedTBE" */
+     569,    /* "setct-PIUnsignedTBE" */
+     531,    /* "setct-PInitResData" */
+     533,    /* "setct-PResData" */
+     594,    /* "setct-RegFormReqTBE" */
+     562,    /* "setct-RegFormResTBS" */
+     606,    /* "setext-cv" */
+     601,    /* "setext-genCrypt" */
+     602,    /* "setext-miAuth" */
+     604,    /* "setext-pinAny" */
+     603,    /* "setext-pinSecure" */
+     605,    /* "setext-track2" */
+      52,    /* "signingTime" */
+     454,    /* "simpleSecurityObject" */
+     496,    /* "singleLevelQuality" */
+    1107,    /* "sm-scheme" */
+    1124,    /* "sm2encrypt" */
+    1135,    /* "sm2encrypt-recommendedParameters" */
+    1136,    /* "sm2encrypt-specifiedParameters" */
+    1145,    /* "sm2encrypt-with-blake2b512" */
+    1146,    /* "sm2encrypt-with-blake2s256" */
+    1147,    /* "sm2encrypt-with-md5" */
+    1143,    /* "sm2encrypt-with-rmd160" */
+    1138,    /* "sm2encrypt-with-sha1" */
+    1139,    /* "sm2encrypt-with-sha224" */
+    1140,    /* "sm2encrypt-with-sha256" */
+    1141,    /* "sm2encrypt-with-sha384" */
+    1142,    /* "sm2encrypt-with-sha512" */
+    1137,    /* "sm2encrypt-with-sm3" */
+    1144,    /* "sm2encrypt-with-whirlpool" */
+    1123,    /* "sm2exchange" */
+    1121,    /* "sm2p256v1" */
+    1122,    /* "sm2sign" */
+    1175,    /* "sm9bn256v1" */
+    1171,    /* "sm9encrypt" */
+    1179,    /* "sm9encrypt-with-sm3-sms4-cbc" */
+    1180,    /* "sm9encrypt-with-sm3-sms4-ctr" */
+    1178,    /* "sm9encrypt-with-sm3-xor" */
+    1172,    /* "sm9hash1" */
+    1182,    /* "sm9hash1-with-sha256" */
+    1181,    /* "sm9hash1-with-sm3" */
+    1209,    /* "sm9hash2" */
+    1211,    /* "sm9hash2-with-sha256" */
+    1210,    /* "sm9hash2-with-sm3" */
+    1173,    /* "sm9kdf" */
+    1184,    /* "sm9kdf-with-sha256" */
+    1183,    /* "sm9kdf-with-sm3" */
+    1170,    /* "sm9keyagreement" */
+    1169,    /* "sm9sign" */
+    1177,    /* "sm9sign-with-sha256" */
+    1176,    /* "sm9sign-with-sm3" */
+     387,    /* "snmpv2" */
+     660,    /* "street" */
+      85,    /* "subjectAltName" */
+     769,    /* "subjectDirectoryAttributes" */
+     398,    /* "subjectInfoAccess" */
+      82,    /* "subjectKeyIdentifier" */
+    1007,    /* "subjectSignTool" */
+     498,    /* "subtreeMaximumQuality" */
+     497,    /* "subtreeMinimumQuality" */
+     890,    /* "supportedAlgorithms" */
+     874,    /* "supportedApplicationContext" */
+     402,    /* "targetInformation" */
+    1070,    /* "tdes-cbc-in-ecies" */
+     864,    /* "telephoneNumber" */
+     866,    /* "teletexTerminalIdentifier" */
+     865,    /* "telexNumber" */
+     459,    /* "textEncodedORAddress" */
+     293,    /* "textNotice" */
+     133,    /* "timeStamping" */
+     106,    /* "title" */
+    1067,    /* "tls-kdf" */
+    1020,    /* "tlsfeature" */
+     682,    /* "tpBasis" */
+     375,    /* "trustRoot" */
+    1102,    /* "type1curve" */
+     436,    /* "ucl" */
+     102,    /* "uid" */
+     888,    /* "uniqueMember" */
+      55,    /* "unstructuredAddress" */
+      49,    /* "unstructuredName" */
+     880,    /* "userCertificate" */
+     465,    /* "userClass" */
+     879,    /* "userPassword" */
+     373,    /* "valid" */
+     678,    /* "wap" */
+     679,    /* "wap-wsg" */
+     735,    /* "wap-wsg-idm-ecid-wtls1" */
+     743,    /* "wap-wsg-idm-ecid-wtls10" */
+     744,    /* "wap-wsg-idm-ecid-wtls11" */
+     745,    /* "wap-wsg-idm-ecid-wtls12" */
+     736,    /* "wap-wsg-idm-ecid-wtls3" */
+     737,    /* "wap-wsg-idm-ecid-wtls4" */
+     738,    /* "wap-wsg-idm-ecid-wtls5" */
+     739,    /* "wap-wsg-idm-ecid-wtls6" */
+     740,    /* "wap-wsg-idm-ecid-wtls7" */
+     741,    /* "wap-wsg-idm-ecid-wtls8" */
+     742,    /* "wap-wsg-idm-ecid-wtls9" */
+    1189,    /* "wapi-crypto" */
+    1190,    /* "wapi-ec" */
+    1191,    /* "wapi-ecdsa192-sha256" */
+    1192,    /* "wapip192v1" */
+     804,    /* "whirlpool" */
+     868,    /* "x121Address" */
+     503,    /* "x500UniqueIdentifier" */
+     158,    /* "x509Certificate" */
+     160,    /* "x509Crl" */
+    1065,    /* "x9-63-kdf" */
+    1069,    /* "xor-in-ecies" */
+    1186,    /* "zuc-128eea3" */
+    1187,    /* "zuc-128eia3" */
+};
+
+#define NUM_LN 1208
+static const unsigned int ln_objs[NUM_LN] = {
+     363,    /* "AD Time Stamping" */
+     405,    /* "ANSI X9.62" */
+     368,    /* "Acceptable OCSP Responses" */
+     910,    /* "Any Extended Key Usage" */
+     664,    /* "Any language" */
+     177,    /* "Authority Information Access" */
+     365,    /* "Basic OCSP Response" */
+     285,    /* "Biometric Info" */
+     179,    /* "CA Issuers" */
+     785,    /* "CA Repository" */
+     954,    /* "CT Certificate SCTs" */
+     952,    /* "CT Precertificate Poison" */
+     951,    /* "CT Precertificate SCTs" */
+     953,    /* "CT Precertificate Signer" */
+     131,    /* "Code Signing" */
+    1024,    /* "Ctrl/Provision WAP Termination" */
+    1023,    /* "Ctrl/provision WAP Access" */
+     783,    /* "Diffie-Hellman based MAC" */
+     382,    /* "Directory" */
+     392,    /* "Domain" */
+     132,    /* "E-mail Protection" */
+     389,    /* "Enterprises" */
+     384,    /* "Experimental" */
+     372,    /* "Extended OCSP Status" */
+     172,    /* "Extension Request" */
+     813,    /* "GOST 28147-89" */
+     849,    /* "GOST 28147-89 Cryptocom ParamSet" */
+     815,    /* "GOST 28147-89 MAC" */
+    1003,    /* "GOST 28147-89 TC26 parameter set" */
+     851,    /* "GOST 34.10-2001 Cryptocom" */
+     850,    /* "GOST 34.10-94 Cryptocom" */
+     811,    /* "GOST R 34.10-2001" */
+     817,    /* "GOST R 34.10-2001 DH" */
+     998,    /* "GOST R 34.10-2012 (512 bit) ParamSet A" */
+     999,    /* "GOST R 34.10-2012 (512 bit) ParamSet B" */
+     997,    /* "GOST R 34.10-2012 (512 bit) testing parameter set" */
+     979,    /* "GOST R 34.10-2012 with 256 bit modulus" */
+     980,    /* "GOST R 34.10-2012 with 512 bit modulus" */
+     985,    /* "GOST R 34.10-2012 with GOST R 34.11-2012 (256 bit)" */
+     986,    /* "GOST R 34.10-2012 with GOST R 34.11-2012 (512 bit)" */
+     812,    /* "GOST R 34.10-94" */
+     818,    /* "GOST R 34.10-94 DH" */
+     982,    /* "GOST R 34.11-2012 with 256 bit hash" */
+     983,    /* "GOST R 34.11-2012 with 512 bit hash" */
+     809,    /* "GOST R 34.11-94" */
+     816,    /* "GOST R 34.11-94 PRF" */
+     807,    /* "GOST R 34.11-94 with GOST R 34.10-2001" */
+     853,    /* "GOST R 34.11-94 with GOST R 34.10-2001 Cryptocom" */
+     808,    /* "GOST R 34.11-94 with GOST R 34.10-94" */
+     852,    /* "GOST R 34.11-94 with GOST R 34.10-94 Cryptocom" */
+     854,    /* "GOST R 3410-2001 Parameter Set Cryptocom" */
+    1200,    /* "GmSSL" */
+     988,    /* "HMAC GOST 34.11-2012 256 bit" */
+     989,    /* "HMAC GOST 34.11-2012 512 bit" */
+     810,    /* "HMAC GOST 34.11-94" */
+     432,    /* "Hold Instruction Call Issuer" */
+     430,    /* "Hold Instruction Code" */
+     431,    /* "Hold Instruction None" */
+     433,    /* "Hold Instruction Reject" */
+     634,    /* "ICC or token signature" */
+    1004,    /* "INN" */
+     294,    /* "IPSec End System" */
+     295,    /* "IPSec Tunnel" */
+     296,    /* "IPSec User" */
+    1105,    /* "ISO CN Member Body" */
+     182,    /* "ISO Member Body" */
+     183,    /* "ISO US Member Body" */
+     667,    /* "Independent" */
+     665,    /* "Inherit all" */
+     647,    /* "International Organizations" */
+     142,    /* "Invalidity Date" */
+     504,    /* "MIME MHS" */
+     388,    /* "Mail" */
+     383,    /* "Management" */
+     417,    /* "Microsoft CSP Name" */
+     135,    /* "Microsoft Commercial Code Signing" */
+     138,    /* "Microsoft Encrypted File System" */
+     171,    /* "Microsoft Extension Request" */
+     134,    /* "Microsoft Individual Code Signing" */
+     856,    /* "Microsoft Local Key set" */
+     137,    /* "Microsoft Server Gated Crypto" */
+     648,    /* "Microsoft Smartcardlogin" */
+     136,    /* "Microsoft Trust List Signing" */
+     649,    /* "Microsoft Universal Principal Name" */
+     393,    /* "NULL" */
+     404,    /* "NULL" */
+      72,    /* "Netscape Base Url" */
+      76,    /* "Netscape CA Policy Url" */
+      74,    /* "Netscape CA Revocation Url" */
+      71,    /* "Netscape Cert Type" */
+      58,    /* "Netscape Certificate Extension" */
+      79,    /* "Netscape Certificate Sequence" */
+      78,    /* "Netscape Comment" */
+      57,    /* "Netscape Communications Corp." */
+      59,    /* "Netscape Data Type" */
+      75,    /* "Netscape Renewal Url" */
+      73,    /* "Netscape Revocation Url" */
+      77,    /* "Netscape SSL Server Name" */
+     139,    /* "Netscape Server Gated Crypto" */
+     178,    /* "OCSP" */
+     370,    /* "OCSP Archive Cutoff" */
+     367,    /* "OCSP CRL ID" */
+     369,    /* "OCSP No Check" */
+     366,    /* "OCSP Nonce" */
+     371,    /* "OCSP Service Locator" */
+     180,    /* "OCSP Signing" */
+    1005,    /* "OGRN" */
+     161,    /* "PBES2" */
+      69,    /* "PBKDF2" */
+     162,    /* "PBMAC1" */
+    1032,    /* "PKINIT Client Auth" */
+     127,    /* "PKIX" */
+     858,    /* "Permanent Identifier" */
+     164,    /* "Policy Qualifier CPS" */
+     165,    /* "Policy Qualifier User Notice" */
+     385,    /* "Private" */
+     663,    /* "Proxy Certificate Information" */
+       1,    /* "RSA Data Security, Inc." */
+       2,    /* "RSA Data Security, Inc. PKCS" */
+     188,    /* "S/MIME" */
+     167,    /* "S/MIME Capabilities" */
+    1006,    /* "SNILS" */
+     387,    /* "SNMPv2" */
+    1025,    /* "SSH Client" */
+    1026,    /* "SSH Server" */
+     512,    /* "Secure Electronic Transactions" */
+     386,    /* "Security" */
+     394,    /* "Selected Attribute Types" */
+    1029,    /* "Send Owner" */
+    1030,    /* "Send Proxied Owner" */
+    1028,    /* "Send Proxied Router" */
+    1027,    /* "Send Router" */
+    1033,    /* "Signing KDC Response" */
+    1008,    /* "Signing Tool of Issuer" */
+    1007,    /* "Signing Tool of Subject" */
+     143,    /* "Strong Extranet ID" */
+     398,    /* "Subject Information Access" */
+    1020,    /* "TLS Feature" */
+     130,    /* "TLS Web Client Authentication" */
+     129,    /* "TLS Web Server Authentication" */
+     133,    /* "Time Stamping" */
+     375,    /* "Trust Root" */
+    1034,    /* "X25519" */
+    1035,    /* "X448" */
+      12,    /* "X509" */
+     402,    /* "X509v3 AC Targeting" */
+     746,    /* "X509v3 Any Policy" */
+      90,    /* "X509v3 Authority Key Identifier" */
+      87,    /* "X509v3 Basic Constraints" */
+     103,    /* "X509v3 CRL Distribution Points" */
+      88,    /* "X509v3 CRL Number" */
+     141,    /* "X509v3 CRL Reason Code" */
+     771,    /* "X509v3 Certificate Issuer" */
+      89,    /* "X509v3 Certificate Policies" */
+     140,    /* "X509v3 Delta CRL Indicator" */
+     126,    /* "X509v3 Extended Key Usage" */
+     857,    /* "X509v3 Freshest CRL" */
+     748,    /* "X509v3 Inhibit Any Policy" */
+      86,    /* "X509v3 Issuer Alternative Name" */
+     770,    /* "X509v3 Issuing Distribution Point" */
+      83,    /* "X509v3 Key Usage" */
+     666,    /* "X509v3 Name Constraints" */
+     403,    /* "X509v3 No Revocation Available" */
+     401,    /* "X509v3 Policy Constraints" */
+     747,    /* "X509v3 Policy Mappings" */
+      84,    /* "X509v3 Private Key Usage Period" */
+      85,    /* "X509v3 Subject Alternative Name" */
+     769,    /* "X509v3 Subject Directory Attributes" */
+      82,    /* "X509v3 Subject Key Identifier" */
+     920,    /* "X9.42 DH" */
+     184,    /* "X9.57" */
+     185,    /* "X9.57 CM ?" */
+     478,    /* "aRecord" */
+     289,    /* "aaControls" */
+     287,    /* "ac-auditEntity" */
+     397,    /* "ac-proxying" */
+     288,    /* "ac-targeting" */
+     446,    /* "account" */
+     364,    /* "ad dvcs" */
+     606,    /* "additional verification" */
+     419,    /* "aes-128-cbc" */
+     916,    /* "aes-128-cbc-hmac-sha1" */
+     948,    /* "aes-128-cbc-hmac-sha256" */
+     896,    /* "aes-128-ccm" */
+     421,    /* "aes-128-cfb" */
+     650,    /* "aes-128-cfb1" */
+     653,    /* "aes-128-cfb8" */
+     904,    /* "aes-128-ctr" */
+     418,    /* "aes-128-ecb" */
+     895,    /* "aes-128-gcm" */
+     958,    /* "aes-128-ocb" */
+     420,    /* "aes-128-ofb" */
+     913,    /* "aes-128-xts" */
+     423,    /* "aes-192-cbc" */
+     917,    /* "aes-192-cbc-hmac-sha1" */
+     949,    /* "aes-192-cbc-hmac-sha256" */
+     899,    /* "aes-192-ccm" */
+     425,    /* "aes-192-cfb" */
+     651,    /* "aes-192-cfb1" */
+     654,    /* "aes-192-cfb8" */
+     905,    /* "aes-192-ctr" */
+     422,    /* "aes-192-ecb" */
+     898,    /* "aes-192-gcm" */
+     959,    /* "aes-192-ocb" */
+     424,    /* "aes-192-ofb" */
+     427,    /* "aes-256-cbc" */
+     918,    /* "aes-256-cbc-hmac-sha1" */
+     950,    /* "aes-256-cbc-hmac-sha256" */
+     902,    /* "aes-256-ccm" */
+     429,    /* "aes-256-cfb" */
+     652,    /* "aes-256-cfb1" */
+     655,    /* "aes-256-cfb8" */
+     906,    /* "aes-256-ctr" */
+     426,    /* "aes-256-ecb" */
+     901,    /* "aes-256-gcm" */
+     960,    /* "aes-256-ocb" */
+     428,    /* "aes-256-ofb" */
+     914,    /* "aes-256-xts" */
+    1071,    /* "aes128-cbc-in-ecies" */
+    1074,    /* "aes128-ctr-in-ecies" */
+    1072,    /* "aes192-cbc-in-ecies" */
+    1075,    /* "aes192-ctr-in-ecies" */
+    1073,    /* "aes256-cbc-in-ecies" */
+    1076,    /* "aes256-ctr-in-ecies" */
+     376,    /* "algorithm" */
+     484,    /* "associatedDomain" */
+     485,    /* "associatedName" */
+     501,    /* "audio" */
+    1049,    /* "auth-dss" */
+    1047,    /* "auth-ecdsa" */
+    1050,    /* "auth-gost01" */
+    1051,    /* "auth-gost12" */
+    1053,    /* "auth-null" */
+    1048,    /* "auth-psk" */
+    1046,    /* "auth-rsa" */
+    1198,    /* "auth-sm2" */
+    1199,    /* "auth-sm9" */
+    1052,    /* "auth-srp" */
+     882,    /* "authorityRevocationList" */
+    1104,    /* "bb1ibe" */
+      91,    /* "bf-cbc" */
+      93,    /* "bf-cfb" */
+      92,    /* "bf-ecb" */
+      94,    /* "bf-ofb" */
+    1103,    /* "bfibe" */
+    1056,    /* "blake2b512" */
+    1057,    /* "blake2s256" */
+     921,    /* "brainpoolP160r1" */
+     922,    /* "brainpoolP160t1" */
+     923,    /* "brainpoolP192r1" */
+     924,    /* "brainpoolP192t1" */
+     925,    /* "brainpoolP224r1" */
+     926,    /* "brainpoolP224t1" */
+     927,    /* "brainpoolP256r1" */
+     928,    /* "brainpoolP256t1" */
+     929,    /* "brainpoolP320r1" */
+     930,    /* "brainpoolP320t1" */
+     931,    /* "brainpoolP384r1" */
+     932,    /* "brainpoolP384t1" */
+     933,    /* "brainpoolP512r1" */
+     934,    /* "brainpoolP512t1" */
+     494,    /* "buildingName" */
+     860,    /* "businessCategory" */
+    1188,    /* "bwips" */
+     691,    /* "c2onb191v4" */
+     692,    /* "c2onb191v5" */
+     697,    /* "c2onb239v4" */
+     698,    /* "c2onb239v5" */
+     684,    /* "c2pnb163v1" */
+     685,    /* "c2pnb163v2" */
+     686,    /* "c2pnb163v3" */
+     687,    /* "c2pnb176v1" */
+     693,    /* "c2pnb208w1" */
+     699,    /* "c2pnb272w1" */
+     700,    /* "c2pnb304w1" */
+     702,    /* "c2pnb368w1" */
+     688,    /* "c2tnb191v1" */
+     689,    /* "c2tnb191v2" */
+     690,    /* "c2tnb191v3" */
+     694,    /* "c2tnb239v1" */
+     695,    /* "c2tnb239v2" */
+     696,    /* "c2tnb239v3" */
+     701,    /* "c2tnb359v1" */
+     703,    /* "c2tnb431r1" */
+     881,    /* "cACertificate" */
+     483,    /* "cNAMERecord" */
+     751,    /* "camellia-128-cbc" */
+     962,    /* "camellia-128-ccm" */
+     757,    /* "camellia-128-cfb" */
+     760,    /* "camellia-128-cfb1" */
+     763,    /* "camellia-128-cfb8" */
+     964,    /* "camellia-128-cmac" */
+     963,    /* "camellia-128-ctr" */
+     754,    /* "camellia-128-ecb" */
+     961,    /* "camellia-128-gcm" */
+     766,    /* "camellia-128-ofb" */
+     752,    /* "camellia-192-cbc" */
+     966,    /* "camellia-192-ccm" */
+     758,    /* "camellia-192-cfb" */
+     761,    /* "camellia-192-cfb1" */
+     764,    /* "camellia-192-cfb8" */
+     968,    /* "camellia-192-cmac" */
+     967,    /* "camellia-192-ctr" */
+     755,    /* "camellia-192-ecb" */
+     965,    /* "camellia-192-gcm" */
+     767,    /* "camellia-192-ofb" */
+     753,    /* "camellia-256-cbc" */
+     970,    /* "camellia-256-ccm" */
+     759,    /* "camellia-256-cfb" */
+     762,    /* "camellia-256-cfb1" */
+     765,    /* "camellia-256-cfb8" */
+     972,    /* "camellia-256-cmac" */
+     971,    /* "camellia-256-ctr" */
+     756,    /* "camellia-256-ecb" */
+     969,    /* "camellia-256-gcm" */
+     768,    /* "camellia-256-ofb" */
+     443,    /* "caseIgnoreIA5StringSyntax" */
+     108,    /* "cast5-cbc" */
+     110,    /* "cast5-cfb" */
+     109,    /* "cast5-ecb" */
+     111,    /* "cast5-ofb" */
+     152,    /* "certBag" */
+     677,    /* "certicom-arc" */
+     517,    /* "certificate extensions" */
+     883,    /* "certificateRevocationList" */
+    1019,    /* "chacha20" */
+    1018,    /* "chacha20-poly1305" */
+      54,    /* "challengePassword" */
+     407,    /* "characteristic-two-field" */
+     395,    /* "clearance" */
+     633,    /* "cleartext track 2" */
+     894,    /* "cmac" */
+    1079,    /* "cmac-aes128-ecies" */
+    1080,    /* "cmac-aes192-ecies" */
+    1081,    /* "cmac-aes256-ecies" */
+      13,    /* "commonName" */
+     513,    /* "content types" */
+      50,    /* "contentType" */
+      53,    /* "countersignature" */
+      14,    /* "countryName" */
+    1201,    /* "cpk" */
+    1202,    /* "cpk-map" */
+    1203,    /* "cpk-map-sha1" */
+    1204,    /* "cpk-map-sha256" */
+    1206,    /* "cpk-map-sha384" */
+    1207,    /* "cpk-map-sha512" */
+    1205,    /* "cpk-map-sm3" */
+     153,    /* "crlBag" */
+     884,    /* "crossCertificatePair" */
+     806,    /* "cryptocom" */
+     805,    /* "cryptopro" */
+     500,    /* "dITRedirect" */
+     451,    /* "dNSDomain" */
+     495,    /* "dSAQuality" */
+     434,    /* "data" */
+     390,    /* "dcObject" */
+     891,    /* "deltaRevocationList" */
+      31,    /* "des-cbc" */
+     643,    /* "des-cdmf" */
+      30,    /* "des-cfb" */
+     656,    /* "des-cfb1" */
+     657,    /* "des-cfb8" */
+      29,    /* "des-ecb" */
+      32,    /* "des-ede" */
+      43,    /* "des-ede-cbc" */
+      60,    /* "des-ede-cfb" */
+      62,    /* "des-ede-ofb" */
+      33,    /* "des-ede3" */
+      44,    /* "des-ede3-cbc" */
+      61,    /* "des-ede3-cfb" */
+     658,    /* "des-ede3-cfb1" */
+     659,    /* "des-ede3-cfb8" */
+      63,    /* "des-ede3-ofb" */
+      45,    /* "des-ofb" */
+     107,    /* "description" */
+     871,    /* "destinationIndicator" */
+      80,    /* "desx-cbc" */
+     947,    /* "dh-cofactor-kdf" */
+     946,    /* "dh-std-kdf" */
+      28,    /* "dhKeyAgreement" */
+     941,    /* "dhSinglePass-cofactorDH-sha1kdf-scheme" */
+     942,    /* "dhSinglePass-cofactorDH-sha224kdf-scheme" */
+     943,    /* "dhSinglePass-cofactorDH-sha256kdf-scheme" */
+     944,    /* "dhSinglePass-cofactorDH-sha384kdf-scheme" */
+     945,    /* "dhSinglePass-cofactorDH-sha512kdf-scheme" */
+     936,    /* "dhSinglePass-stdDH-sha1kdf-scheme" */
+     937,    /* "dhSinglePass-stdDH-sha224kdf-scheme" */
+     938,    /* "dhSinglePass-stdDH-sha256kdf-scheme" */
+     939,    /* "dhSinglePass-stdDH-sha384kdf-scheme" */
+     940,    /* "dhSinglePass-stdDH-sha512kdf-scheme" */
+      11,    /* "directory services (X.500)" */
+     378,    /* "directory services - algorithms" */
+     887,    /* "distinguishedName" */
+     892,    /* "dmdName" */
+     174,    /* "dnQualifier" */
+     447,    /* "document" */
+     471,    /* "documentAuthor" */
+     468,    /* "documentIdentifier" */
+     472,    /* "documentLocation" */
+     502,    /* "documentPublisher" */
+     449,    /* "documentSeries" */
+     469,    /* "documentTitle" */
+     470,    /* "documentVersion" */
+     380,    /* "dod" */
+     391,    /* "domainComponent" */
+     452,    /* "domainRelatedObject" */
+     116,    /* "dsaEncryption" */
+      67,    /* "dsaEncryption-old" */
+      66,    /* "dsaWithSHA" */
+     113,    /* "dsaWithSHA1" */
+      70,    /* "dsaWithSHA1-old" */
+     802,    /* "dsa_with_SHA224" */
+     803,    /* "dsa_with_SHA256" */
+     297,    /* "dvcs" */
+     791,    /* "ecdsa-with-Recommended" */
+     416,    /* "ecdsa-with-SHA1" */
+     793,    /* "ecdsa-with-SHA224" */
+     794,    /* "ecdsa-with-SHA256" */
+     795,    /* "ecdsa-with-SHA384" */
+     796,    /* "ecdsa-with-SHA512" */
+     792,    /* "ecdsa-with-Specified" */
+    1063,    /* "ecies-recommendedParameters" */
+    1064,    /* "ecies-specifiedParameters" */
+    1096,    /* "ecies-with-x9-63-sha1-aes128-cbc-cmac" */
+    1087,    /* "ecies-with-x9-63-sha1-aes128-cbc-hmac" */
+    1084,    /* "ecies-with-x9-63-sha1-xor-hmac" */
+    1097,    /* "ecies-with-x9-63-sha256-aes128-cbc-cmac" */
+    1088,    /* "ecies-with-x9-63-sha256-aes128-cbc-hmac" */
+    1092,    /* "ecies-with-x9-63-sha256-aes128-cbc-hmac-half" */
+    1099,    /* "ecies-with-x9-63-sha256-aes128-ctr-cmac" */
+    1090,    /* "ecies-with-x9-63-sha256-aes128-ctr-hmac" */
+    1094,    /* "ecies-with-x9-63-sha256-aes128-ctr-hmac-half" */
+    1085,    /* "ecies-with-x9-63-sha256-xor-hmac" */
+    1098,    /* "ecies-with-x9-63-sha512-aes256-cbc-cmac" */
+    1089,    /* "ecies-with-x9-63-sha512-aes256-cbc-hmac" */
+    1093,    /* "ecies-with-x9-63-sha512-aes256-cbc-hmac-half" */
+    1100,    /* "ecies-with-x9-63-sha512-aes256-ctr-cmac" */
+    1091,    /* "ecies-with-x9-63-sha512-aes256-ctr-hmac" */
+    1095,    /* "ecies-with-x9-63-sha512-aes256-ctr-hmac-half" */
+    1086,    /* "ecies-with-x9-63-sha512-xor-hmac" */
+      48,    /* "emailAddress" */
+     632,    /* "encrypted track 2" */
+     885,    /* "enhancedSearchGuide" */
+      56,    /* "extendedCertificateAttributes" */
+     867,    /* "facsimileTelephoneNumber" */
+     462,    /* "favouriteDrink" */
+     453,    /* "friendlyCountry" */
+     490,    /* "friendlyCountryName" */
+     156,    /* "friendlyName" */
+     631,    /* "generate cryptogram" */
+     509,    /* "generationQualifier" */
+     601,    /* "generic cryptogram" */
+      99,    /* "givenName" */
+     976,    /* "gost-mac-12" */
+    1009,    /* "gost89-cbc" */
+     814,    /* "gost89-cnt" */
+     975,    /* "gost89-cnt-12" */
+    1011,    /* "gost89-ctr" */
+    1010,    /* "gost89-ecb" */
+    1015,    /* "grasshopper-cbc" */
+    1016,    /* "grasshopper-cfb" */
+    1013,    /* "grasshopper-ctr" */
+    1012,    /* "grasshopper-ecb" */
+    1017,    /* "grasshopper-mac" */
+    1014,    /* "grasshopper-ofb" */
+    1036,    /* "hkdf" */
+     855,    /* "hmac" */
+    1077,    /* "hmac-full-ecies" */
+    1078,    /* "hmac-half-ecies" */
+     780,    /* "hmac-md5" */
+     781,    /* "hmac-sha1" */
+    1149,    /* "hmac-sm3" */
+     797,    /* "hmacWithMD5" */
+     163,    /* "hmacWithSHA1" */
+     798,    /* "hmacWithSHA224" */
+     799,    /* "hmacWithSHA256" */
+     800,    /* "hmacWithSHA384" */
+     801,    /* "hmacWithSHA512" */
+     486,    /* "homePostalAddress" */
+     473,    /* "homeTelephoneNumber" */
+     466,    /* "host" */
+     889,    /* "houseIdentifier" */
+     442,    /* "iA5StringSyntax" */
+     381,    /* "iana" */
+    1101,    /* "ibcs1" */
+     824,    /* "id-Gost28147-89-CryptoPro-A-ParamSet" */
+     825,    /* "id-Gost28147-89-CryptoPro-B-ParamSet" */
+     826,    /* "id-Gost28147-89-CryptoPro-C-ParamSet" */
+     827,    /* "id-Gost28147-89-CryptoPro-D-ParamSet" */
+     819,    /* "id-Gost28147-89-CryptoPro-KeyMeshing" */
+     829,    /* "id-Gost28147-89-CryptoPro-Oscar-1-0-ParamSet" */
+     828,    /* "id-Gost28147-89-CryptoPro-Oscar-1-1-ParamSet" */
+     830,    /* "id-Gost28147-89-CryptoPro-RIC-1-ParamSet" */
+     820,    /* "id-Gost28147-89-None-KeyMeshing" */
+     823,    /* "id-Gost28147-89-TestParamSet" */
+     840,    /* "id-GostR3410-2001-CryptoPro-A-ParamSet" */
+     841,    /* "id-GostR3410-2001-CryptoPro-B-ParamSet" */
+     842,    /* "id-GostR3410-2001-CryptoPro-C-ParamSet" */
+     843,    /* "id-GostR3410-2001-CryptoPro-XchA-ParamSet" */
+     844,    /* "id-GostR3410-2001-CryptoPro-XchB-ParamSet" */
+     839,    /* "id-GostR3410-2001-TestParamSet" */
+     832,    /* "id-GostR3410-94-CryptoPro-A-ParamSet" */
+     833,    /* "id-GostR3410-94-CryptoPro-B-ParamSet" */
+     834,    /* "id-GostR3410-94-CryptoPro-C-ParamSet" */
+     835,    /* "id-GostR3410-94-CryptoPro-D-ParamSet" */
+     836,    /* "id-GostR3410-94-CryptoPro-XchA-ParamSet" */
+     837,    /* "id-GostR3410-94-CryptoPro-XchB-ParamSet" */
+     838,    /* "id-GostR3410-94-CryptoPro-XchC-ParamSet" */
+     831,    /* "id-GostR3410-94-TestParamSet" */
+     845,    /* "id-GostR3410-94-a" */
+     846,    /* "id-GostR3410-94-aBis" */
+     847,    /* "id-GostR3410-94-b" */
+     848,    /* "id-GostR3410-94-bBis" */
+     822,    /* "id-GostR3411-94-CryptoProParamSet" */
+     821,    /* "id-GostR3411-94-TestParamSet" */
+     266,    /* "id-aca" */
+     355,    /* "id-aca-accessIdentity" */
+     354,    /* "id-aca-authenticationInfo" */
+     356,    /* "id-aca-chargingIdentity" */
+     399,    /* "id-aca-encAttrs" */
+     357,    /* "id-aca-group" */
+     358,    /* "id-aca-role" */
+     176,    /* "id-ad" */
+     788,    /* "id-aes128-wrap" */
+     897,    /* "id-aes128-wrap-pad" */
+     789,    /* "id-aes192-wrap" */
+     900,    /* "id-aes192-wrap-pad" */
+     790,    /* "id-aes256-wrap" */
+     903,    /* "id-aes256-wrap-pad" */
+     262,    /* "id-alg" */
+     893,    /* "id-alg-PWRI-KEK" */
+     323,    /* "id-alg-des40" */
+     326,    /* "id-alg-dh-pop" */
+     325,    /* "id-alg-dh-sig-hmac-sha1" */
+     324,    /* "id-alg-noSignature" */
+     907,    /* "id-camellia128-wrap" */
+     908,    /* "id-camellia192-wrap" */
+     909,    /* "id-camellia256-wrap" */
+     268,    /* "id-cct" */
+     361,    /* "id-cct-PKIData" */
+     362,    /* "id-cct-PKIResponse" */
+     360,    /* "id-cct-crs" */
+      81,    /* "id-ce" */
+     680,    /* "id-characteristic-two-basis" */
+     263,    /* "id-cmc" */
+     334,    /* "id-cmc-addExtensions" */
+     346,    /* "id-cmc-confirmCertAcceptance" */
+     330,    /* "id-cmc-dataReturn" */
+     336,    /* "id-cmc-decryptedPOP" */
+     335,    /* "id-cmc-encryptedPOP" */
+     339,    /* "id-cmc-getCRL" */
+     338,    /* "id-cmc-getCert" */
+     328,    /* "id-cmc-identification" */
+     329,    /* "id-cmc-identityProof" */
+     337,    /* "id-cmc-lraPOPWitness" */
+     344,    /* "id-cmc-popLinkRandom" */
+     345,    /* "id-cmc-popLinkWitness" */
+     343,    /* "id-cmc-queryPending" */
+     333,    /* "id-cmc-recipientNonce" */
+     341,    /* "id-cmc-regInfo" */
+     342,    /* "id-cmc-responseInfo" */
+     340,    /* "id-cmc-revokeRequest" */
+     332,    /* "id-cmc-senderNonce" */
+     327,    /* "id-cmc-statusInfo" */
+     331,    /* "id-cmc-transactionId" */
+     787,    /* "id-ct-asciiTextWithCRLF" */
+    1060,    /* "id-ct-xml" */
+     408,    /* "id-ecPublicKey" */
+    1061,    /* "id-ecSigType" */
+     508,    /* "id-hex-multipart-message" */
+     507,    /* "id-hex-partial-message" */
+     260,    /* "id-it" */
+     302,    /* "id-it-caKeyUpdateInfo" */
+     298,    /* "id-it-caProtEncCert" */
+     311,    /* "id-it-confirmWaitTime" */
+     303,    /* "id-it-currentCRL" */
+     300,    /* "id-it-encKeyPairTypes" */
+     310,    /* "id-it-implicitConfirm" */
+     308,    /* "id-it-keyPairParamRep" */
+     307,    /* "id-it-keyPairParamReq" */
+     312,    /* "id-it-origPKIMessage" */
+     301,    /* "id-it-preferredSymmAlg" */
+     309,    /* "id-it-revPassphrase" */
+     299,    /* "id-it-signKeyPairTypes" */
+     305,    /* "id-it-subscriptionRequest" */
+     306,    /* "id-it-subscriptionResponse" */
+     784,    /* "id-it-suppLangTags" */
+     304,    /* "id-it-unsupportedOIDs" */
+     128,    /* "id-kp" */
+     280,    /* "id-mod-attribute-cert" */
+     274,    /* "id-mod-cmc" */
+     277,    /* "id-mod-cmp" */
+     284,    /* "id-mod-cmp2000" */
+     273,    /* "id-mod-crmf" */
+     283,    /* "id-mod-dvcs" */
+     275,    /* "id-mod-kea-profile-88" */
+     276,    /* "id-mod-kea-profile-93" */
+     282,    /* "id-mod-ocsp" */
+     278,    /* "id-mod-qualified-cert-88" */
+     279,    /* "id-mod-qualified-cert-93" */
+     281,    /* "id-mod-timestamp-protocol" */
+     264,    /* "id-on" */
+     347,    /* "id-on-personalData" */
+     265,    /* "id-pda" */
+     352,    /* "id-pda-countryOfCitizenship" */
+     353,    /* "id-pda-countryOfResidence" */
+     348,    /* "id-pda-dateOfBirth" */
+     351,    /* "id-pda-gender" */
+     349,    /* "id-pda-placeOfBirth" */
+     175,    /* "id-pe" */
+    1031,    /* "id-pkinit" */
+     261,    /* "id-pkip" */
+     258,    /* "id-pkix-mod" */
+     269,    /* "id-pkix1-explicit-88" */
+     271,    /* "id-pkix1-explicit-93" */
+     270,    /* "id-pkix1-implicit-88" */
+     272,    /* "id-pkix1-implicit-93" */
+     662,    /* "id-ppl" */
+     267,    /* "id-qcs" */
+     359,    /* "id-qcs-pkixQCSyntax-v1" */
+     259,    /* "id-qt" */
+     313,    /* "id-regCtrl" */
+     316,    /* "id-regCtrl-authenticator" */
+     319,    /* "id-regCtrl-oldCertID" */
+     318,    /* "id-regCtrl-pkiArchiveOptions" */
+     317,    /* "id-regCtrl-pkiPublicationInfo" */
+     320,    /* "id-regCtrl-protocolEncrKey" */
+     315,    /* "id-regCtrl-regToken" */
+     314,    /* "id-regInfo" */
+     322,    /* "id-regInfo-certReq" */
+     321,    /* "id-regInfo-utf8Pairs" */
+     973,    /* "id-scrypt" */
+    1174,    /* "id-sm9MasterSecret" */
+    1168,    /* "id-sm9PublicKey" */
+     191,    /* "id-smime-aa" */
+     215,    /* "id-smime-aa-contentHint" */
+     218,    /* "id-smime-aa-contentIdentifier" */
+     221,    /* "id-smime-aa-contentReference" */
+     240,    /* "id-smime-aa-dvcs-dvc" */
+     217,    /* "id-smime-aa-encapContentType" */
+     222,    /* "id-smime-aa-encrypKeyPref" */
+     220,    /* "id-smime-aa-equivalentLabels" */
+     232,    /* "id-smime-aa-ets-CertificateRefs" */
+     233,    /* "id-smime-aa-ets-RevocationRefs" */
+     238,    /* "id-smime-aa-ets-archiveTimeStamp" */
+     237,    /* "id-smime-aa-ets-certCRLTimestamp" */
+     234,    /* "id-smime-aa-ets-certValues" */
+     227,    /* "id-smime-aa-ets-commitmentType" */
+     231,    /* "id-smime-aa-ets-contentTimestamp" */
+     236,    /* "id-smime-aa-ets-escTimeStamp" */
+     230,    /* "id-smime-aa-ets-otherSigCert" */
+     235,    /* "id-smime-aa-ets-revocationValues" */
+     226,    /* "id-smime-aa-ets-sigPolicyId" */
+     229,    /* "id-smime-aa-ets-signerAttr" */
+     228,    /* "id-smime-aa-ets-signerLocation" */
+     219,    /* "id-smime-aa-macValue" */
+     214,    /* "id-smime-aa-mlExpandHistory" */
+     216,    /* "id-smime-aa-msgSigDigest" */
+     212,    /* "id-smime-aa-receiptRequest" */
+     213,    /* "id-smime-aa-securityLabel" */
+     239,    /* "id-smime-aa-signatureType" */
+     223,    /* "id-smime-aa-signingCertificate" */
+     224,    /* "id-smime-aa-smimeEncryptCerts" */
+     225,    /* "id-smime-aa-timeStampToken" */
+     192,    /* "id-smime-alg" */
+     243,    /* "id-smime-alg-3DESwrap" */
+     246,    /* "id-smime-alg-CMS3DESwrap" */
+     247,    /* "id-smime-alg-CMSRC2wrap" */
+     245,    /* "id-smime-alg-ESDH" */
+     241,    /* "id-smime-alg-ESDHwith3DES" */
+     242,    /* "id-smime-alg-ESDHwithRC2" */
+     244,    /* "id-smime-alg-RC2wrap" */
+     193,    /* "id-smime-cd" */
+     248,    /* "id-smime-cd-ldap" */
+     190,    /* "id-smime-ct" */
+     210,    /* "id-smime-ct-DVCSRequestData" */
+     211,    /* "id-smime-ct-DVCSResponseData" */
+     208,    /* "id-smime-ct-TDTInfo" */
+     207,    /* "id-smime-ct-TSTInfo" */
+     205,    /* "id-smime-ct-authData" */
+    1059,    /* "id-smime-ct-authEnvelopedData" */
+     786,    /* "id-smime-ct-compressedData" */
+    1058,    /* "id-smime-ct-contentCollection" */
+     209,    /* "id-smime-ct-contentInfo" */
+     206,    /* "id-smime-ct-publishCert" */
+     204,    /* "id-smime-ct-receipt" */
+     195,    /* "id-smime-cti" */
+     255,    /* "id-smime-cti-ets-proofOfApproval" */
+     256,    /* "id-smime-cti-ets-proofOfCreation" */
+     253,    /* "id-smime-cti-ets-proofOfDelivery" */
+     251,    /* "id-smime-cti-ets-proofOfOrigin" */
+     252,    /* "id-smime-cti-ets-proofOfReceipt" */
+     254,    /* "id-smime-cti-ets-proofOfSender" */
+     189,    /* "id-smime-mod" */
+     196,    /* "id-smime-mod-cms" */
+     197,    /* "id-smime-mod-ess" */
+     202,    /* "id-smime-mod-ets-eSigPolicy-88" */
+     203,    /* "id-smime-mod-ets-eSigPolicy-97" */
+     200,    /* "id-smime-mod-ets-eSignature-88" */
+     201,    /* "id-smime-mod-ets-eSignature-97" */
+     199,    /* "id-smime-mod-msg-v3" */
+     198,    /* "id-smime-mod-oid" */
+     194,    /* "id-smime-spq" */
+     250,    /* "id-smime-spq-ets-sqt-unotice" */
+     249,    /* "id-smime-spq-ets-sqt-uri" */
+     974,    /* "id-tc26" */
+     991,    /* "id-tc26-agreement" */
+     992,    /* "id-tc26-agreement-gost-3410-2012-256" */
+     993,    /* "id-tc26-agreement-gost-3410-2012-512" */
+     977,    /* "id-tc26-algorithms" */
+     990,    /* "id-tc26-cipher" */
+    1001,    /* "id-tc26-cipher-constants" */
+     994,    /* "id-tc26-constants" */
+     981,    /* "id-tc26-digest" */
+    1000,    /* "id-tc26-digest-constants" */
+    1002,    /* "id-tc26-gost-28147-constants" */
+     996,    /* "id-tc26-gost-3410-2012-512-constants" */
+     987,    /* "id-tc26-mac" */
+     978,    /* "id-tc26-sign" */
+     995,    /* "id-tc26-sign-constants" */
+     984,    /* "id-tc26-signwithdigest" */
+      34,    /* "idea-cbc" */
+      35,    /* "idea-cfb" */
+      36,    /* "idea-ecb" */
+      46,    /* "idea-ofb" */
+     676,    /* "identified-organization" */
+    1068,    /* "ikev2-kdf" */
+     461,    /* "info" */
+     101,    /* "initials" */
+     869,    /* "internationaliSDNNumber" */
+    1022,    /* "ipsec Internet Key Exchange" */
+     749,    /* "ipsec3" */
+     750,    /* "ipsec4" */
+     181,    /* "iso" */
+     623,    /* "issuer capabilities" */
+     645,    /* "itu-t" */
+     492,    /* "janetMailbox" */
+     646,    /* "joint-iso-itu-t" */
+     957,    /* "jurisdictionCountryName" */
+     955,    /* "jurisdictionLocalityName" */
+     956,    /* "jurisdictionStateOrProvinceName" */
+     150,    /* "keyBag" */
+     773,    /* "kisa" */
+    1039,    /* "kx-dhe" */
+    1041,    /* "kx-dhe-psk" */
+    1038,    /* "kx-ecdhe" */
+    1040,    /* "kx-ecdhe-psk" */
+    1045,    /* "kx-gost" */
+    1043,    /* "kx-psk" */
+    1037,    /* "kx-rsa" */
+    1042,    /* "kx-rsa-psk" */
+    1193,    /* "kx-sm2" */
+    1195,    /* "kx-sm2-psk" */
+    1194,    /* "kx-sm2dhe" */
+    1196,    /* "kx-sm9" */
+    1197,    /* "kx-sm9dhe" */
+    1044,    /* "kx-srp" */
+     477,    /* "lastModifiedBy" */
+     476,    /* "lastModifiedTime" */
+     157,    /* "localKeyID" */
+      15,    /* "localityName" */
+     480,    /* "mXRecord" */
+     493,    /* "mailPreferenceOption" */
+     467,    /* "manager" */
+       3,    /* "md2" */
+       7,    /* "md2WithRSAEncryption" */
+     257,    /* "md4" */
+     396,    /* "md4WithRSAEncryption" */
+       4,    /* "md5" */
+     114,    /* "md5-sha1" */
+     104,    /* "md5WithRSA" */
+       8,    /* "md5WithRSAEncryption" */
+      95,    /* "mdc2" */
+      96,    /* "mdc2WithRSA" */
+     875,    /* "member" */
+     602,    /* "merchant initiated auth" */
+     514,    /* "message extensions" */
+      51,    /* "messageDigest" */
+     911,    /* "mgf1" */
+     506,    /* "mime-mhs-bodies" */
+     505,    /* "mime-mhs-headings" */
+     488,    /* "mobileTelephoneNumber" */
+     481,    /* "nSRecord" */
+     173,    /* "name" */
+    1066,    /* "nist-concatenation-kdf" */
+     681,    /* "onBasis" */
+     379,    /* "org" */
+      17,    /* "organizationName" */
+     491,    /* "organizationalStatus" */
+      18,    /* "organizationalUnitName" */
+    1106,    /* "oscca" */
+     475,    /* "otherMailbox" */
+     876,    /* "owner" */
+     935,    /* "pSpecified" */
+     489,    /* "pagerTelephoneNumber" */
+    1208,    /* "paillier" */
+     782,    /* "password based MAC" */
+     374,    /* "path" */
+     621,    /* "payment gateway capabilities" */
+       9,    /* "pbeWithMD2AndDES-CBC" */
+     168,    /* "pbeWithMD2AndRC2-CBC" */
+     112,    /* "pbeWithMD5AndCast5CBC" */
+      10,    /* "pbeWithMD5AndDES-CBC" */
+     169,    /* "pbeWithMD5AndRC2-CBC" */
+     148,    /* "pbeWithSHA1And128BitRC2-CBC" */
+     144,    /* "pbeWithSHA1And128BitRC4" */
+     147,    /* "pbeWithSHA1And2-KeyTripleDES-CBC" */
+     146,    /* "pbeWithSHA1And3-KeyTripleDES-CBC" */
+     149,    /* "pbeWithSHA1And40BitRC2-CBC" */
+     145,    /* "pbeWithSHA1And40BitRC4" */
+     170,    /* "pbeWithSHA1AndDES-CBC" */
+      68,    /* "pbeWithSHA1AndRC2-CBC" */
+    1083,    /* "pbeWithSM3AndSMS4-CBC" */
+     499,    /* "personalSignature" */
+     487,    /* "personalTitle" */
+     464,    /* "photo" */
+     863,    /* "physicalDeliveryOfficeName" */
+     437,    /* "pilot" */
+     439,    /* "pilotAttributeSyntax" */
+     438,    /* "pilotAttributeType" */
+     479,    /* "pilotAttributeType27" */
+     456,    /* "pilotDSA" */
+     441,    /* "pilotGroups" */
+     444,    /* "pilotObject" */
+     440,    /* "pilotObjectClass" */
+     455,    /* "pilotOrganization" */
+     445,    /* "pilotPerson" */
+     186,    /* "pkcs1" */
+      27,    /* "pkcs3" */
+     187,    /* "pkcs5" */
+      20,    /* "pkcs7" */
+      21,    /* "pkcs7-data" */
+      25,    /* "pkcs7-digestData" */
+      26,    /* "pkcs7-encryptedData" */
+      23,    /* "pkcs7-envelopedData" */
+      24,    /* "pkcs7-signedAndEnvelopedData" */
+      22,    /* "pkcs7-signedData" */
+     151,    /* "pkcs8ShroudedKeyBag" */
+      47,    /* "pkcs9" */
+     862,    /* "postOfficeBox" */
+     861,    /* "postalAddress" */
+     661,    /* "postalCode" */
+     683,    /* "ppBasis" */
+     872,    /* "preferredDeliveryMethod" */
+     873,    /* "presentationAddress" */
+     406,    /* "prime-field" */
+     409,    /* "prime192v1" */
+     410,    /* "prime192v2" */
+     411,    /* "prime192v3" */
+     412,    /* "prime239v1" */
+     413,    /* "prime239v2" */
+     414,    /* "prime239v3" */
+     415,    /* "prime256v1" */
+     886,    /* "protocolInformation" */
+     510,    /* "pseudonym" */
+     435,    /* "pss" */
+     286,    /* "qcStatements" */
+     457,    /* "qualityLabelledData" */
+     450,    /* "rFC822localPart" */
+      98,    /* "rc2-40-cbc" */
+     166,    /* "rc2-64-cbc" */
+      37,    /* "rc2-cbc" */
+      39,    /* "rc2-cfb" */
+      38,    /* "rc2-ecb" */
+      40,    /* "rc2-ofb" */
+       5,    /* "rc4" */
+      97,    /* "rc4-40" */
+     915,    /* "rc4-hmac-md5" */
+     120,    /* "rc5-cbc" */
+     122,    /* "rc5-cfb" */
+     121,    /* "rc5-ecb" */
+     123,    /* "rc5-ofb" */
+     870,    /* "registeredAddress" */
+     460,    /* "rfc822Mailbox" */
+     117,    /* "ripemd160" */
+     119,    /* "ripemd160WithRSA" */
+     400,    /* "role" */
+     877,    /* "roleOccupant" */
+     448,    /* "room" */
+     463,    /* "roomNumber" */
+      19,    /* "rsa" */
+       6,    /* "rsaEncryption" */
+     644,    /* "rsaOAEPEncryptionSET" */
+     377,    /* "rsaSignature" */
+     919,    /* "rsaesOaep" */
+     912,    /* "rsassaPss" */
+     482,    /* "sOARecord" */
+     155,    /* "safeContentsBag" */
+     291,    /* "sbgp-autonomousSysNum" */
+     290,    /* "sbgp-ipAddrBlock" */
+     292,    /* "sbgp-routerIdentifier" */
+     159,    /* "sdsiCertificate" */
+     859,    /* "searchGuide" */
+    1062,    /* "secg-scheme" */
+     704,    /* "secp112r1" */
+     705,    /* "secp112r2" */
+     706,    /* "secp128r1" */
+     707,    /* "secp128r2" */
+     708,    /* "secp160k1" */
+     709,    /* "secp160r1" */
+     710,    /* "secp160r2" */
+     711,    /* "secp192k1" */
+     712,    /* "secp224k1" */
+     713,    /* "secp224r1" */
+     714,    /* "secp256k1" */
+     715,    /* "secp384r1" */
+     716,    /* "secp521r1" */
+     154,    /* "secretBag" */
+     474,    /* "secretary" */
+     717,    /* "sect113r1" */
+     718,    /* "sect113r2" */
+     719,    /* "sect131r1" */
+     720,    /* "sect131r2" */
+     721,    /* "sect163k1" */
+     722,    /* "sect163r1" */
+     723,    /* "sect163r2" */
+     724,    /* "sect193r1" */
+     725,    /* "sect193r2" */
+     726,    /* "sect233k1" */
+     727,    /* "sect233r1" */
+     728,    /* "sect239k1" */
+     729,    /* "sect283k1" */
+     730,    /* "sect283r1" */
+     731,    /* "sect409k1" */
+     732,    /* "sect409r1" */
+     733,    /* "sect571k1" */
+     734,    /* "sect571r1" */
+     635,    /* "secure device signature" */
+     878,    /* "seeAlso" */
+     777,    /* "seed-cbc" */
+     779,    /* "seed-cfb" */
+     776,    /* "seed-ecb" */
+     778,    /* "seed-ofb" */
+     105,    /* "serialNumber" */
+     625,    /* "set-addPolicy" */
+     515,    /* "set-attr" */
+     518,    /* "set-brand" */
+     638,    /* "set-brand-AmericanExpress" */
+     637,    /* "set-brand-Diners" */
+     636,    /* "set-brand-IATA-ATA" */
+     639,    /* "set-brand-JCB" */
+     641,    /* "set-brand-MasterCard" */
+     642,    /* "set-brand-Novus" */
+     640,    /* "set-brand-Visa" */
+     516,    /* "set-policy" */
+     607,    /* "set-policy-root" */
+     624,    /* "set-rootKeyThumb" */
+     620,    /* "setAttr-Cert" */
+     628,    /* "setAttr-IssCap-CVM" */
+     630,    /* "setAttr-IssCap-Sig" */
+     629,    /* "setAttr-IssCap-T2" */
+     627,    /* "setAttr-Token-B0Prime" */
+     626,    /* "setAttr-Token-EMV" */
+     622,    /* "setAttr-TokenType" */
+     619,    /* "setCext-IssuerCapabilities" */
+     615,    /* "setCext-PGWYcapabilities" */
+     616,    /* "setCext-TokenIdentifier" */
+     618,    /* "setCext-TokenType" */
+     617,    /* "setCext-Track2Data" */
+     611,    /* "setCext-cCertRequired" */
+     609,    /* "setCext-certType" */
+     608,    /* "setCext-hashedRoot" */
+     610,    /* "setCext-merchData" */
+     613,    /* "setCext-setExt" */
+     614,    /* "setCext-setQualf" */
+     612,    /* "setCext-tunneling" */
+     540,    /* "setct-AcqCardCodeMsg" */
+     576,    /* "setct-AcqCardCodeMsgTBE" */
+     570,    /* "setct-AuthReqTBE" */
+     534,    /* "setct-AuthReqTBS" */
+     527,    /* "setct-AuthResBaggage" */
+     571,    /* "setct-AuthResTBE" */
+     572,    /* "setct-AuthResTBEX" */
+     535,    /* "setct-AuthResTBS" */
+     536,    /* "setct-AuthResTBSX" */
+     528,    /* "setct-AuthRevReqBaggage" */
+     577,    /* "setct-AuthRevReqTBE" */
+     541,    /* "setct-AuthRevReqTBS" */
+     529,    /* "setct-AuthRevResBaggage" */
+     542,    /* "setct-AuthRevResData" */
+     578,    /* "setct-AuthRevResTBE" */
+     579,    /* "setct-AuthRevResTBEB" */
+     543,    /* "setct-AuthRevResTBS" */
+     573,    /* "setct-AuthTokenTBE" */
+     537,    /* "setct-AuthTokenTBS" */
+     600,    /* "setct-BCIDistributionTBS" */
+     558,    /* "setct-BatchAdminReqData" */
+     592,    /* "setct-BatchAdminReqTBE" */
+     559,    /* "setct-BatchAdminResData" */
+     593,    /* "setct-BatchAdminResTBE" */
+     599,    /* "setct-CRLNotificationResTBS" */
+     598,    /* "setct-CRLNotificationTBS" */
+     580,    /* "setct-CapReqTBE" */
+     581,    /* "setct-CapReqTBEX" */
+     544,    /* "setct-CapReqTBS" */
+     545,    /* "setct-CapReqTBSX" */
+     546,    /* "setct-CapResData" */
+     582,    /* "setct-CapResTBE" */
+     583,    /* "setct-CapRevReqTBE" */
+     584,    /* "setct-CapRevReqTBEX" */
+     547,    /* "setct-CapRevReqTBS" */
+     548,    /* "setct-CapRevReqTBSX" */
+     549,    /* "setct-CapRevResData" */
+     585,    /* "setct-CapRevResTBE" */
+     538,    /* "setct-CapTokenData" */
+     530,    /* "setct-CapTokenSeq" */
+     574,    /* "setct-CapTokenTBE" */
+     575,    /* "setct-CapTokenTBEX" */
+     539,    /* "setct-CapTokenTBS" */
+     560,    /* "setct-CardCInitResTBS" */
+     566,    /* "setct-CertInqReqTBS" */
+     563,    /* "setct-CertReqData" */
+     595,    /* "setct-CertReqTBE" */
+     596,    /* "setct-CertReqTBEX" */
+     564,    /* "setct-CertReqTBS" */
+     565,    /* "setct-CertResData" */
+     597,    /* "setct-CertResTBE" */
+     586,    /* "setct-CredReqTBE" */
+     587,    /* "setct-CredReqTBEX" */
+     550,    /* "setct-CredReqTBS" */
+     551,    /* "setct-CredReqTBSX" */
+     552,    /* "setct-CredResData" */
+     588,    /* "setct-CredResTBE" */
+     589,    /* "setct-CredRevReqTBE" */
+     590,    /* "setct-CredRevReqTBEX" */
+     553,    /* "setct-CredRevReqTBS" */
+     554,    /* "setct-CredRevReqTBSX" */
+     555,    /* "setct-CredRevResData" */
+     591,    /* "setct-CredRevResTBE" */
+     567,    /* "setct-ErrorTBS" */
+     526,    /* "setct-HODInput" */
+     561,    /* "setct-MeAqCInitResTBS" */
+     522,    /* "setct-OIData" */
+     519,    /* "setct-PANData" */
+     521,    /* "setct-PANOnly" */
+     520,    /* "setct-PANToken" */
+     556,    /* "setct-PCertReqData" */
+     557,    /* "setct-PCertResTBS" */
+     523,    /* "setct-PI" */
+     532,    /* "setct-PI-TBS" */
+     524,    /* "setct-PIData" */
+     525,    /* "setct-PIDataUnsigned" */
+     568,    /* "setct-PIDualSignedTBE" */
+     569,    /* "setct-PIUnsignedTBE" */
+     531,    /* "setct-PInitResData" */
+     533,    /* "setct-PResData" */
+     594,    /* "setct-RegFormReqTBE" */
+     562,    /* "setct-RegFormResTBS" */
+     604,    /* "setext-pinAny" */
+     603,    /* "setext-pinSecure" */
+     605,    /* "setext-track2" */
+      41,    /* "sha" */
+      64,    /* "sha1" */
+     115,    /* "sha1WithRSA" */
+      65,    /* "sha1WithRSAEncryption" */
+     675,    /* "sha224" */
+     671,    /* "sha224WithRSAEncryption" */
+     672,    /* "sha256" */
+     668,    /* "sha256WithRSAEncryption" */
+     673,    /* "sha384" */
+     669,    /* "sha384WithRSAEncryption" */
+     674,    /* "sha512" */
+     670,    /* "sha512WithRSAEncryption" */
+      42,    /* "shaWithRSAEncryption" */
+      52,    /* "signingTime" */
+     454,    /* "simpleSecurityObject" */
+     496,    /* "singleLevelQuality" */
+    1107,    /* "sm-scheme" */
+    1116,    /* "sm1-cbc" */
+    1118,    /* "sm1-cfb" */
+    1119,    /* "sm1-cfb1" */
+    1120,    /* "sm1-cfb8" */
+    1115,    /* "sm1-ecb" */
+    1117,    /* "sm1-ofb" */
+    1124,    /* "sm2encrypt" */
+    1135,    /* "sm2encrypt-recommendedParameters" */
+    1136,    /* "sm2encrypt-specifiedParameters" */
+    1145,    /* "sm2encrypt-with-blake2b512" */
+    1146,    /* "sm2encrypt-with-blake2s256" */
+    1147,    /* "sm2encrypt-with-md5" */
+    1143,    /* "sm2encrypt-with-rmd160" */
+    1138,    /* "sm2encrypt-with-sha1" */
+    1139,    /* "sm2encrypt-with-sha224" */
+    1140,    /* "sm2encrypt-with-sha256" */
+    1141,    /* "sm2encrypt-with-sha384" */
+    1142,    /* "sm2encrypt-with-sha512" */
+    1137,    /* "sm2encrypt-with-sm3" */
+    1144,    /* "sm2encrypt-with-whirlpool" */
+    1123,    /* "sm2exchange" */
+    1121,    /* "sm2p256v1" */
+    1122,    /* "sm2sign" */
+    1133,    /* "sm2sign-with-blake2b512" */
+    1134,    /* "sm2sign-with-blake2s256" */
+    1131,    /* "sm2sign-with-rmd160" */
+    1126,    /* "sm2sign-with-sha1" */
+    1129,    /* "sm2sign-with-sha224" */
+    1127,    /* "sm2sign-with-sha256" */
+    1130,    /* "sm2sign-with-sha384" */
+    1128,    /* "sm2sign-with-sha512" */
+    1125,    /* "sm2sign-with-sm3" */
+    1132,    /* "sm2sign-with-whirlpool" */
+    1148,    /* "sm3" */
+    1163,    /* "sm5" */
+    1165,    /* "sm6-cbc" */
+    1167,    /* "sm6-cfb" */
+    1164,    /* "sm6-ecb" */
+    1166,    /* "sm6-ofb" */
+    1175,    /* "sm9bn256v1" */
+    1171,    /* "sm9encrypt" */
+    1179,    /* "sm9encrypt-with-sm3-sms4-cbc" */
+    1180,    /* "sm9encrypt-with-sm3-sms4-ctr" */
+    1178,    /* "sm9encrypt-with-sm3-xor" */
+    1172,    /* "sm9hash1" */
+    1182,    /* "sm9hash1-with-sha256" */
+    1181,    /* "sm9hash1-with-sm3" */
+    1209,    /* "sm9hash2" */
+    1211,    /* "sm9hash2-with-sha256" */
+    1210,    /* "sm9hash2-with-sm3" */
+    1173,    /* "sm9kdf" */
+    1184,    /* "sm9kdf-with-sha256" */
+    1183,    /* "sm9kdf-with-sm3" */
+    1170,    /* "sm9keyagreement" */
+    1169,    /* "sm9sign" */
+    1177,    /* "sm9sign-with-sha256" */
+    1176,    /* "sm9sign-with-sm3" */
+    1151,    /* "sms4-cbc" */
+    1158,    /* "sms4-ccm" */
+    1153,    /* "sms4-cfb" */
+    1154,    /* "sms4-cfb1" */
+    1155,    /* "sms4-cfb8" */
+    1156,    /* "sms4-ctr" */
+    1150,    /* "sms4-ecb" */
+    1157,    /* "sms4-gcm" */
+    1162,    /* "sms4-ocb" */
+    1152,    /* "sms4-ofb" */
+    1160,    /* "sms4-wrap" */
+    1161,    /* "sms4-wrap-pad" */
+    1159,    /* "sms4-xts" */
+    1109,    /* "ssf33-cbc" */
+    1114,    /* "ssf33-cbc-mac" */
+    1111,    /* "ssf33-cfb" */
+    1112,    /* "ssf33-cfb1" */
+    1113,    /* "ssf33-cfb8" */
+    1108,    /* "ssf33-ecb" */
+    1110,    /* "ssf33-ofb" */
+      16,    /* "stateOrProvinceName" */
+     660,    /* "streetAddress" */
+     498,    /* "subtreeMaximumQuality" */
+     497,    /* "subtreeMinimumQuality" */
+     890,    /* "supportedAlgorithms" */
+     874,    /* "supportedApplicationContext" */
+     100,    /* "surname" */
+    1070,    /* "tdes-cbc-in-ecies" */
+     864,    /* "telephoneNumber" */
+     866,    /* "teletexTerminalIdentifier" */
+     865,    /* "telexNumber" */
+     459,    /* "textEncodedORAddress" */
+     293,    /* "textNotice" */
+     106,    /* "title" */
+    1067,    /* "tls-kdf" */
+    1021,    /* "tls1-prf" */
+     682,    /* "tpBasis" */
+    1102,    /* "type1curve" */
+     436,    /* "ucl" */
+       0,    /* "undefined" */
+     102,    /* "uniqueIdentifier" */
+     888,    /* "uniqueMember" */
+      55,    /* "unstructuredAddress" */
+      49,    /* "unstructuredName" */
+     880,    /* "userCertificate" */
+     465,    /* "userClass" */
+     458,    /* "userId" */
+     879,    /* "userPassword" */
+     373,    /* "valid" */
+     678,    /* "wap" */
+     679,    /* "wap-wsg" */
+     735,    /* "wap-wsg-idm-ecid-wtls1" */
+     743,    /* "wap-wsg-idm-ecid-wtls10" */
+     744,    /* "wap-wsg-idm-ecid-wtls11" */
+     745,    /* "wap-wsg-idm-ecid-wtls12" */
+     736,    /* "wap-wsg-idm-ecid-wtls3" */
+     737,    /* "wap-wsg-idm-ecid-wtls4" */
+     738,    /* "wap-wsg-idm-ecid-wtls5" */
+     739,    /* "wap-wsg-idm-ecid-wtls6" */
+     740,    /* "wap-wsg-idm-ecid-wtls7" */
+     741,    /* "wap-wsg-idm-ecid-wtls8" */
+     742,    /* "wap-wsg-idm-ecid-wtls9" */
+    1189,    /* "wapi-crypto" */
+    1190,    /* "wapi-ec" */
+    1191,    /* "wapi-ecdsa192-sha256" */
+    1192,    /* "wapip192v1" */
+     804,    /* "whirlpool" */
+     868,    /* "x121Address" */
+     503,    /* "x500UniqueIdentifier" */
+     158,    /* "x509Certificate" */
+     160,    /* "x509Crl" */
+    1065,    /* "x9-63-kdf" */
+    1069,    /* "xor-in-ecies" */
+     125,    /* "zlib compression" */
+    1185,    /* "zuc" */
+    1186,    /* "zuc-128eea3" */
+    1187,    /* "zuc-128eia3" */
+    1213,    /* "zuc-mac" */
+    1212,    /* "zuc256" */
+    1214,    /* "zuc256-mac" */
+    1217,    /* "zuc256-mac128" */
+    1215,    /* "zuc256-mac32" */
+    1216,    /* "zuc256-mac64" */
+};
+
+#define NUM_OBJ 1105
+static const unsigned int obj_objs[NUM_OBJ] = {
+       0,    /* OBJ_undef                        0 */
+     181,    /* OBJ_iso                          1 */
+     393,    /* OBJ_joint_iso_ccitt              OBJ_joint_iso_itu_t */
+     404,    /* OBJ_ccitt                        OBJ_itu_t */
+     645,    /* OBJ_itu_t                        0 */
+     646,    /* OBJ_joint_iso_itu_t              2 */
+     434,    /* OBJ_data                         0 9 */
+     182,    /* OBJ_member_body                  1 2 */
+     379,    /* OBJ_org                          1 3 */
+     676,    /* OBJ_identified_organization      1 3 */
+      11,    /* OBJ_X500                         2 5 */
+     647,    /* OBJ_international_organizations  2 23 */
+     380,    /* OBJ_dod                          1 3 6 */
+      12,    /* OBJ_X509                         2 5 4 */
+     378,    /* OBJ_X500algorithms               2 5 8 */
+      81,    /* OBJ_id_ce                        2 5 29 */
+     512,    /* OBJ_id_set                       2 23 42 */
+     678,    /* OBJ_wap                          2 23 43 */
+     435,    /* OBJ_pss                          0 9 2342 */
+    1105,    /* OBJ_ISO_CN                       1 2 156 */
+     183,    /* OBJ_ISO_US                       1 2 840 */
+     381,    /* OBJ_iana                         1 3 6 1 */
+    1034,    /* OBJ_X25519                       1 3 101 110 */
+    1035,    /* OBJ_X448                         1 3 101 111 */
+     677,    /* OBJ_certicom_arc                 1 3 132 */
+     394,    /* OBJ_selected_attribute_types     2 5 1 5 */
+      13,    /* OBJ_commonName                   2 5 4 3 */
+     100,    /* OBJ_surname                      2 5 4 4 */
+     105,    /* OBJ_serialNumber                 2 5 4 5 */
+      14,    /* OBJ_countryName                  2 5 4 6 */
+      15,    /* OBJ_localityName                 2 5 4 7 */
+      16,    /* OBJ_stateOrProvinceName          2 5 4 8 */
+     660,    /* OBJ_streetAddress                2 5 4 9 */
+      17,    /* OBJ_organizationName             2 5 4 10 */
+      18,    /* OBJ_organizationalUnitName       2 5 4 11 */
+     106,    /* OBJ_title                        2 5 4 12 */
+     107,    /* OBJ_description                  2 5 4 13 */
+     859,    /* OBJ_searchGuide                  2 5 4 14 */
+     860,    /* OBJ_businessCategory             2 5 4 15 */
+     861,    /* OBJ_postalAddress                2 5 4 16 */
+     661,    /* OBJ_postalCode                   2 5 4 17 */
+     862,    /* OBJ_postOfficeBox                2 5 4 18 */
+     863,    /* OBJ_physicalDeliveryOfficeName   2 5 4 19 */
+     864,    /* OBJ_telephoneNumber              2 5 4 20 */
+     865,    /* OBJ_telexNumber                  2 5 4 21 */
+     866,    /* OBJ_teletexTerminalIdentifier    2 5 4 22 */
+     867,    /* OBJ_facsimileTelephoneNumber     2 5 4 23 */
+     868,    /* OBJ_x121Address                  2 5 4 24 */
+     869,    /* OBJ_internationaliSDNNumber      2 5 4 25 */
+     870,    /* OBJ_registeredAddress            2 5 4 26 */
+     871,    /* OBJ_destinationIndicator         2 5 4 27 */
+     872,    /* OBJ_preferredDeliveryMethod      2 5 4 28 */
+     873,    /* OBJ_presentationAddress          2 5 4 29 */
+     874,    /* OBJ_supportedApplicationContext  2 5 4 30 */
+     875,    /* OBJ_member                       2 5 4 31 */
+     876,    /* OBJ_owner                        2 5 4 32 */
+     877,    /* OBJ_roleOccupant                 2 5 4 33 */
+     878,    /* OBJ_seeAlso                      2 5 4 34 */
+     879,    /* OBJ_userPassword                 2 5 4 35 */
+     880,    /* OBJ_userCertificate              2 5 4 36 */
+     881,    /* OBJ_cACertificate                2 5 4 37 */
+     882,    /* OBJ_authorityRevocationList      2 5 4 38 */
+     883,    /* OBJ_certificateRevocationList    2 5 4 39 */
+     884,    /* OBJ_crossCertificatePair         2 5 4 40 */
+     173,    /* OBJ_name                         2 5 4 41 */
+      99,    /* OBJ_givenName                    2 5 4 42 */
+     101,    /* OBJ_initials                     2 5 4 43 */
+     509,    /* OBJ_generationQualifier          2 5 4 44 */
+     503,    /* OBJ_x500UniqueIdentifier         2 5 4 45 */
+     174,    /* OBJ_dnQualifier                  2 5 4 46 */
+     885,    /* OBJ_enhancedSearchGuide          2 5 4 47 */
+     886,    /* OBJ_protocolInformation          2 5 4 48 */
+     887,    /* OBJ_distinguishedName            2 5 4 49 */
+     888,    /* OBJ_uniqueMember                 2 5 4 50 */
+     889,    /* OBJ_houseIdentifier              2 5 4 51 */
+     890,    /* OBJ_supportedAlgorithms          2 5 4 52 */
+     891,    /* OBJ_deltaRevocationList          2 5 4 53 */
+     892,    /* OBJ_dmdName                      2 5 4 54 */
+     510,    /* OBJ_pseudonym                    2 5 4 65 */
+     400,    /* OBJ_role                         2 5 4 72 */
+     769,    /* OBJ_subject_directory_attributes 2 5 29 9 */
+      82,    /* OBJ_subject_key_identifier       2 5 29 14 */
+      83,    /* OBJ_key_usage                    2 5 29 15 */
+      84,    /* OBJ_private_key_usage_period     2 5 29 16 */
+      85,    /* OBJ_subject_alt_name             2 5 29 17 */
+      86,    /* OBJ_issuer_alt_name              2 5 29 18 */
+      87,    /* OBJ_basic_constraints            2 5 29 19 */
+      88,    /* OBJ_crl_number                   2 5 29 20 */
+     141,    /* OBJ_crl_reason                   2 5 29 21 */
+     430,    /* OBJ_hold_instruction_code        2 5 29 23 */
+     142,    /* OBJ_invalidity_date              2 5 29 24 */
+     140,    /* OBJ_delta_crl                    2 5 29 27 */
+     770,    /* OBJ_issuing_distribution_point   2 5 29 28 */
+     771,    /* OBJ_certificate_issuer           2 5 29 29 */
+     666,    /* OBJ_name_constraints             2 5 29 30 */
+     103,    /* OBJ_crl_distribution_points      2 5 29 31 */
+      89,    /* OBJ_certificate_policies         2 5 29 32 */
+     747,    /* OBJ_policy_mappings              2 5 29 33 */
+      90,    /* OBJ_authority_key_identifier     2 5 29 35 */
+     401,    /* OBJ_policy_constraints           2 5 29 36 */
+     126,    /* OBJ_ext_key_usage                2 5 29 37 */
+     857,    /* OBJ_freshest_crl                 2 5 29 46 */
+     748,    /* OBJ_inhibit_any_policy           2 5 29 54 */
+     402,    /* OBJ_target_information           2 5 29 55 */
+     403,    /* OBJ_no_rev_avail                 2 5 29 56 */
+     513,    /* OBJ_set_ctype                    2 23 42 0 */
+     514,    /* OBJ_set_msgExt                   2 23 42 1 */
+     515,    /* OBJ_set_attr                     2 23 42 3 */
+     516,    /* OBJ_set_policy                   2 23 42 5 */
+     517,    /* OBJ_set_certExt                  2 23 42 7 */
+     518,    /* OBJ_set_brand                    2 23 42 8 */
+     679,    /* OBJ_wap_wsg                      2 23 43 1 */
+     382,    /* OBJ_Directory                    1 3 6 1 1 */
+     383,    /* OBJ_Management                   1 3 6 1 2 */
+     384,    /* OBJ_Experimental                 1 3 6 1 3 */
+     385,    /* OBJ_Private                      1 3 6 1 4 */
+     386,    /* OBJ_Security                     1 3 6 1 5 */
+     387,    /* OBJ_SNMPv2                       1 3 6 1 6 */
+     388,    /* OBJ_Mail                         1 3 6 1 7 */
+     376,    /* OBJ_algorithm                    1 3 14 3 2 */
+    1062,    /* OBJ_secg_scheme                  1 3 132 1 */
+     395,    /* OBJ_clearance                    2 5 1 5 55 */
+      19,    /* OBJ_rsa                          2 5 8 1 1 */
+      96,    /* OBJ_mdc2WithRSA                  2 5 8 3 100 */
+      95,    /* OBJ_mdc2                         2 5 8 3 101 */
+     746,    /* OBJ_any_policy                   2 5 29 32 0 */
+     910,    /* OBJ_anyExtendedKeyUsage          2 5 29 37 0 */
+     519,    /* OBJ_setct_PANData                2 23 42 0 0 */
+     520,    /* OBJ_setct_PANToken               2 23 42 0 1 */
+     521,    /* OBJ_setct_PANOnly                2 23 42 0 2 */
+     522,    /* OBJ_setct_OIData                 2 23 42 0 3 */
+     523,    /* OBJ_setct_PI                     2 23 42 0 4 */
+     524,    /* OBJ_setct_PIData                 2 23 42 0 5 */
+     525,    /* OBJ_setct_PIDataUnsigned         2 23 42 0 6 */
+     526,    /* OBJ_setct_HODInput               2 23 42 0 7 */
+     527,    /* OBJ_setct_AuthResBaggage         2 23 42 0 8 */
+     528,    /* OBJ_setct_AuthRevReqBaggage      2 23 42 0 9 */
+     529,    /* OBJ_setct_AuthRevResBaggage      2 23 42 0 10 */
+     530,    /* OBJ_setct_CapTokenSeq            2 23 42 0 11 */
+     531,    /* OBJ_setct_PInitResData           2 23 42 0 12 */
+     532,    /* OBJ_setct_PI_TBS                 2 23 42 0 13 */
+     533,    /* OBJ_setct_PResData               2 23 42 0 14 */
+     534,    /* OBJ_setct_AuthReqTBS             2 23 42 0 16 */
+     535,    /* OBJ_setct_AuthResTBS             2 23 42 0 17 */
+     536,    /* OBJ_setct_AuthResTBSX            2 23 42 0 18 */
+     537,    /* OBJ_setct_AuthTokenTBS           2 23 42 0 19 */
+     538,    /* OBJ_setct_CapTokenData           2 23 42 0 20 */
+     539,    /* OBJ_setct_CapTokenTBS            2 23 42 0 21 */
+     540,    /* OBJ_setct_AcqCardCodeMsg         2 23 42 0 22 */
+     541,    /* OBJ_setct_AuthRevReqTBS          2 23 42 0 23 */
+     542,    /* OBJ_setct_AuthRevResData         2 23 42 0 24 */
+     543,    /* OBJ_setct_AuthRevResTBS          2 23 42 0 25 */
+     544,    /* OBJ_setct_CapReqTBS              2 23 42 0 26 */
+     545,    /* OBJ_setct_CapReqTBSX             2 23 42 0 27 */
+     546,    /* OBJ_setct_CapResData             2 23 42 0 28 */
+     547,    /* OBJ_setct_CapRevReqTBS           2 23 42 0 29 */
+     548,    /* OBJ_setct_CapRevReqTBSX          2 23 42 0 30 */
+     549,    /* OBJ_setct_CapRevResData          2 23 42 0 31 */
+     550,    /* OBJ_setct_CredReqTBS             2 23 42 0 32 */
+     551,    /* OBJ_setct_CredReqTBSX            2 23 42 0 33 */
+     552,    /* OBJ_setct_CredResData            2 23 42 0 34 */
+     553,    /* OBJ_setct_CredRevReqTBS          2 23 42 0 35 */
+     554,    /* OBJ_setct_CredRevReqTBSX         2 23 42 0 36 */
+     555,    /* OBJ_setct_CredRevResData         2 23 42 0 37 */
+     556,    /* OBJ_setct_PCertReqData           2 23 42 0 38 */
+     557,    /* OBJ_setct_PCertResTBS            2 23 42 0 39 */
+     558,    /* OBJ_setct_BatchAdminReqData      2 23 42 0 40 */
+     559,    /* OBJ_setct_BatchAdminResData      2 23 42 0 41 */
+     560,    /* OBJ_setct_CardCInitResTBS        2 23 42 0 42 */
+     561,    /* OBJ_setct_MeAqCInitResTBS        2 23 42 0 43 */
+     562,    /* OBJ_setct_RegFormResTBS          2 23 42 0 44 */
+     563,    /* OBJ_setct_CertReqData            2 23 42 0 45 */
+     564,    /* OBJ_setct_CertReqTBS             2 23 42 0 46 */
+     565,    /* OBJ_setct_CertResData            2 23 42 0 47 */
+     566,    /* OBJ_setct_CertInqReqTBS          2 23 42 0 48 */
+     567,    /* OBJ_setct_ErrorTBS               2 23 42 0 49 */
+     568,    /* OBJ_setct_PIDualSignedTBE        2 23 42 0 50 */
+     569,    /* OBJ_setct_PIUnsignedTBE          2 23 42 0 51 */
+     570,    /* OBJ_setct_AuthReqTBE             2 23 42 0 52 */
+     571,    /* OBJ_setct_AuthResTBE             2 23 42 0 53 */
+     572,    /* OBJ_setct_AuthResTBEX            2 23 42 0 54 */
+     573,    /* OBJ_setct_AuthTokenTBE           2 23 42 0 55 */
+     574,    /* OBJ_setct_CapTokenTBE            2 23 42 0 56 */
+     575,    /* OBJ_setct_CapTokenTBEX           2 23 42 0 57 */
+     576,    /* OBJ_setct_AcqCardCodeMsgTBE      2 23 42 0 58 */
+     577,    /* OBJ_setct_AuthRevReqTBE          2 23 42 0 59 */
+     578,    /* OBJ_setct_AuthRevResTBE          2 23 42 0 60 */
+     579,    /* OBJ_setct_AuthRevResTBEB         2 23 42 0 61 */
+     580,    /* OBJ_setct_CapReqTBE              2 23 42 0 62 */
+     581,    /* OBJ_setct_CapReqTBEX             2 23 42 0 63 */
+     582,    /* OBJ_setct_CapResTBE              2 23 42 0 64 */
+     583,    /* OBJ_setct_CapRevReqTBE           2 23 42 0 65 */
+     584,    /* OBJ_setct_CapRevReqTBEX          2 23 42 0 66 */
+     585,    /* OBJ_setct_CapRevResTBE           2 23 42 0 67 */
+     586,    /* OBJ_setct_CredReqTBE             2 23 42 0 68 */
+     587,    /* OBJ_setct_CredReqTBEX            2 23 42 0 69 */
+     588,    /* OBJ_setct_CredResTBE             2 23 42 0 70 */
+     589,    /* OBJ_setct_CredRevReqTBE          2 23 42 0 71 */
+     590,    /* OBJ_setct_CredRevReqTBEX         2 23 42 0 72 */
+     591,    /* OBJ_setct_CredRevResTBE          2 23 42 0 73 */
+     592,    /* OBJ_setct_BatchAdminReqTBE       2 23 42 0 74 */
+     593,    /* OBJ_setct_BatchAdminResTBE       2 23 42 0 75 */
+     594,    /* OBJ_setct_RegFormReqTBE          2 23 42 0 76 */
+     595,    /* OBJ_setct_CertReqTBE             2 23 42 0 77 */
+     596,    /* OBJ_setct_CertReqTBEX            2 23 42 0 78 */
+     597,    /* OBJ_setct_CertResTBE             2 23 42 0 79 */
+     598,    /* OBJ_setct_CRLNotificationTBS     2 23 42 0 80 */
+     599,    /* OBJ_setct_CRLNotificationResTBS  2 23 42 0 81 */
+     600,    /* OBJ_setct_BCIDistributionTBS     2 23 42 0 82 */
+     601,    /* OBJ_setext_genCrypt              2 23 42 1 1 */
+     602,    /* OBJ_setext_miAuth                2 23 42 1 3 */
+     603,    /* OBJ_setext_pinSecure             2 23 42 1 4 */
+     604,    /* OBJ_setext_pinAny                2 23 42 1 5 */
+     605,    /* OBJ_setext_track2                2 23 42 1 7 */
+     606,    /* OBJ_setext_cv                    2 23 42 1 8 */
+     620,    /* OBJ_setAttr_Cert                 2 23 42 3 0 */
+     621,    /* OBJ_setAttr_PGWYcap              2 23 42 3 1 */
+     622,    /* OBJ_setAttr_TokenType            2 23 42 3 2 */
+     623,    /* OBJ_setAttr_IssCap               2 23 42 3 3 */
+     607,    /* OBJ_set_policy_root              2 23 42 5 0 */
+     608,    /* OBJ_setCext_hashedRoot           2 23 42 7 0 */
+     609,    /* OBJ_setCext_certType             2 23 42 7 1 */
+     610,    /* OBJ_setCext_merchData            2 23 42 7 2 */
+     611,    /* OBJ_setCext_cCertRequired        2 23 42 7 3 */
+     612,    /* OBJ_setCext_tunneling            2 23 42 7 4 */
+     613,    /* OBJ_setCext_setExt               2 23 42 7 5 */
+     614,    /* OBJ_setCext_setQualf             2 23 42 7 6 */
+     615,    /* OBJ_setCext_PGWYcapabilities     2 23 42 7 7 */
+     616,    /* OBJ_setCext_TokenIdentifier      2 23 42 7 8 */
+     617,    /* OBJ_setCext_Track2Data           2 23 42 7 9 */
+     618,    /* OBJ_setCext_TokenType            2 23 42 7 10 */
+     619,    /* OBJ_setCext_IssuerCapabilities   2 23 42 7 11 */
+     636,    /* OBJ_set_brand_IATA_ATA           2 23 42 8 1 */
+     640,    /* OBJ_set_brand_Visa               2 23 42 8 4 */
+     641,    /* OBJ_set_brand_MasterCard         2 23 42 8 5 */
+     637,    /* OBJ_set_brand_Diners             2 23 42 8 30 */
+     638,    /* OBJ_set_brand_AmericanExpress    2 23 42 8 34 */
+     639,    /* OBJ_set_brand_JCB                2 23 42 8 35 */
+    1106,    /* OBJ_oscca                        1 2 156 10197 */
+    1188,    /* OBJ_bwips                        1 2 156 11235 */
+     805,    /* OBJ_cryptopro                    1 2 643 2 2 */
+     806,    /* OBJ_cryptocom                    1 2 643 2 9 */
+     974,    /* OBJ_id_tc26                      1 2 643 7 1 */
+    1005,    /* OBJ_OGRN                         1 2 643 100 1 */
+    1006,    /* OBJ_SNILS                        1 2 643 100 3 */
+    1007,    /* OBJ_subjectSignTool              1 2 643 100 111 */
+    1008,    /* OBJ_issuerSignTool               1 2 643 100 112 */
+     184,    /* OBJ_X9_57                        1 2 840 10040 */
+     405,    /* OBJ_ansi_X9_62                   1 2 840 10045 */
+     389,    /* OBJ_Enterprises                  1 3 6 1 4 1 */
+     504,    /* OBJ_mime_mhs                     1 3 6 1 7 1 */
+     104,    /* OBJ_md5WithRSA                   1 3 14 3 2 3 */
+      29,    /* OBJ_des_ecb                      1 3 14 3 2 6 */
+      31,    /* OBJ_des_cbc                      1 3 14 3 2 7 */
+      45,    /* OBJ_des_ofb64                    1 3 14 3 2 8 */
+      30,    /* OBJ_des_cfb64                    1 3 14 3 2 9 */
+     377,    /* OBJ_rsaSignature                 1 3 14 3 2 11 */
+      67,    /* OBJ_dsa_2                        1 3 14 3 2 12 */
+      66,    /* OBJ_dsaWithSHA                   1 3 14 3 2 13 */
+      42,    /* OBJ_shaWithRSAEncryption         1 3 14 3 2 15 */
+      32,    /* OBJ_des_ede_ecb                  1 3 14 3 2 17 */
+      41,    /* OBJ_sha                          1 3 14 3 2 18 */
+      64,    /* OBJ_sha1                         1 3 14 3 2 26 */
+      70,    /* OBJ_dsaWithSHA1_2                1 3 14 3 2 27 */
+     115,    /* OBJ_sha1WithRSA                  1 3 14 3 2 29 */
+     117,    /* OBJ_ripemd160                    1 3 36 3 2 1 */
+     143,    /* OBJ_sxnet                        1 3 101 1 4 1 */
+     721,    /* OBJ_sect163k1                    1 3 132 0 1 */
+     722,    /* OBJ_sect163r1                    1 3 132 0 2 */
+     728,    /* OBJ_sect239k1                    1 3 132 0 3 */
+     717,    /* OBJ_sect113r1                    1 3 132 0 4 */
+     718,    /* OBJ_sect113r2                    1 3 132 0 5 */
+     704,    /* OBJ_secp112r1                    1 3 132 0 6 */
+     705,    /* OBJ_secp112r2                    1 3 132 0 7 */
+     709,    /* OBJ_secp160r1                    1 3 132 0 8 */
+     708,    /* OBJ_secp160k1                    1 3 132 0 9 */
+     714,    /* OBJ_secp256k1                    1 3 132 0 10 */
+     723,    /* OBJ_sect163r2                    1 3 132 0 15 */
+     729,    /* OBJ_sect283k1                    1 3 132 0 16 */
+     730,    /* OBJ_sect283r1                    1 3 132 0 17 */
+     719,    /* OBJ_sect131r1                    1 3 132 0 22 */
+     720,    /* OBJ_sect131r2                    1 3 132 0 23 */
+     724,    /* OBJ_sect193r1                    1 3 132 0 24 */
+     725,    /* OBJ_sect193r2                    1 3 132 0 25 */
+     726,    /* OBJ_sect233k1                    1 3 132 0 26 */
+     727,    /* OBJ_sect233r1                    1 3 132 0 27 */
+     706,    /* OBJ_secp128r1                    1 3 132 0 28 */
+     707,    /* OBJ_secp128r2                    1 3 132 0 29 */
+     710,    /* OBJ_secp160r2                    1 3 132 0 30 */
+     711,    /* OBJ_secp192k1                    1 3 132 0 31 */
+     712,    /* OBJ_secp224k1                    1 3 132 0 32 */
+     713,    /* OBJ_secp224r1                    1 3 132 0 33 */
+     715,    /* OBJ_secp384r1                    1 3 132 0 34 */
+     716,    /* OBJ_secp521r1                    1 3 132 0 35 */
+     731,    /* OBJ_sect409k1                    1 3 132 0 36 */
+     732,    /* OBJ_sect409r1                    1 3 132 0 37 */
+     733,    /* OBJ_sect571k1                    1 3 132 0 38 */
+     734,    /* OBJ_sect571r1                    1 3 132 0 39 */
+    1063,    /* OBJ_ecies_recommendedParameters  1 3 132 1 7 */
+    1064,    /* OBJ_ecies_specifiedParameters    1 3 132 1 8 */
+    1069,    /* OBJ_xor_in_ecies                 1 3 132 1 18 */
+    1070,    /* OBJ_tdes_cbc_in_ecies            1 3 132 1 19 */
+    1077,    /* OBJ_hmac_full_ecies              1 3 132 1 22 */
+    1078,    /* OBJ_hmac_half_ecies              1 3 132 1 23 */
+     624,    /* OBJ_set_rootKeyThumb             2 23 42 3 0 0 */
+     625,    /* OBJ_set_addPolicy                2 23 42 3 0 1 */
+     626,    /* OBJ_setAttr_Token_EMV            2 23 42 3 2 1 */
+     627,    /* OBJ_setAttr_Token_B0Prime        2 23 42 3 2 2 */
+     628,    /* OBJ_setAttr_IssCap_CVM           2 23 42 3 3 3 */
+     629,    /* OBJ_setAttr_IssCap_T2            2 23 42 3 3 4 */
+     630,    /* OBJ_setAttr_IssCap_Sig           2 23 42 3 3 5 */
+     642,    /* OBJ_set_brand_Novus              2 23 42 8 6011 */
+     735,    /* OBJ_wap_wsg_idm_ecid_wtls1       2 23 43 1 4 1 */
+     736,    /* OBJ_wap_wsg_idm_ecid_wtls3       2 23 43 1 4 3 */
+     737,    /* OBJ_wap_wsg_idm_ecid_wtls4       2 23 43 1 4 4 */
+     738,    /* OBJ_wap_wsg_idm_ecid_wtls5       2 23 43 1 4 5 */
+     739,    /* OBJ_wap_wsg_idm_ecid_wtls6       2 23 43 1 4 6 */
+     740,    /* OBJ_wap_wsg_idm_ecid_wtls7       2 23 43 1 4 7 */
+     741,    /* OBJ_wap_wsg_idm_ecid_wtls8       2 23 43 1 4 8 */
+     742,    /* OBJ_wap_wsg_idm_ecid_wtls9       2 23 43 1 4 9 */
+     743,    /* OBJ_wap_wsg_idm_ecid_wtls10      2 23 43 1 4 10 */
+     744,    /* OBJ_wap_wsg_idm_ecid_wtls11      2 23 43 1 4 11 */
+     745,    /* OBJ_wap_wsg_idm_ecid_wtls12      2 23 43 1 4 12 */
+     804,    /* OBJ_whirlpool                    1 0 10118 3 0 55 */
+    1107,    /* OBJ_sm_scheme                    1 2 156 10197 1 */
+    1189,    /* OBJ_wapi_crypto                  1 2 156 11235 1 */
+     773,    /* OBJ_kisa                         1 2 410 200004 */
+     807,    /* OBJ_id_GostR3411_94_with_GostR3410_2001 1 2 643 2 2 3 */
+     808,    /* OBJ_id_GostR3411_94_with_GostR3410_94 1 2 643 2 2 4 */
+     809,    /* OBJ_id_GostR3411_94              1 2 643 2 2 9 */
+     810,    /* OBJ_id_HMACGostR3411_94          1 2 643 2 2 10 */
+     811,    /* OBJ_id_GostR3410_2001            1 2 643 2 2 19 */
+     812,    /* OBJ_id_GostR3410_94              1 2 643 2 2 20 */
+     813,    /* OBJ_id_Gost28147_89              1 2 643 2 2 21 */
+     815,    /* OBJ_id_Gost28147_89_MAC          1 2 643 2 2 22 */
+     816,    /* OBJ_id_GostR3411_94_prf          1 2 643 2 2 23 */
+     817,    /* OBJ_id_GostR3410_2001DH          1 2 643 2 2 98 */
+     818,    /* OBJ_id_GostR3410_94DH            1 2 643 2 2 99 */
+     977,    /* OBJ_id_tc26_algorithms           1 2 643 7 1 1 */
+     994,    /* OBJ_id_tc26_constants            1 2 643 7 1 2 */
+       1,    /* OBJ_rsadsi                       1 2 840 113549 */
+     185,    /* OBJ_X9cm                         1 2 840 10040 4 */
+    1061,    /* OBJ_X9_62_id_ecSigType           1 2 840 10045 4 */
+    1031,    /* OBJ_id_pkinit                    1 3 6 1 5 2 3 */
+     127,    /* OBJ_id_pkix                      1 3 6 1 5 5 7 */
+     505,    /* OBJ_mime_mhs_headings            1 3 6 1 7 1 1 */
+     506,    /* OBJ_mime_mhs_bodies              1 3 6 1 7 1 2 */
+     119,    /* OBJ_ripemd160WithRSA             1 3 36 3 3 1 2 */
+    1084,    /* OBJ_ecies_with_x9_63_sha1_xor_hmac 1 3 132 1 8 1 */
+    1085,    /* OBJ_ecies_with_x9_63_sha256_xor_hmac 1 3 132 1 8 2 */
+    1086,    /* OBJ_ecies_with_x9_63_sha512_xor_hmac 1 3 132 1 8 3 */
+    1087,    /* OBJ_ecies_with_x9_63_sha1_aes128_cbc_hmac 1 3 132 1 8 4 */
+    1088,    /* OBJ_ecies_with_x9_63_sha256_aes128_cbc_hmac 1 3 132 1 8 5 */
+    1089,    /* OBJ_ecies_with_x9_63_sha512_aes256_cbc_hmac 1 3 132 1 8 6 */
+    1090,    /* OBJ_ecies_with_x9_63_sha256_aes128_ctr_hmac 1 3 132 1 8 7 */
+    1091,    /* OBJ_ecies_with_x9_63_sha512_aes256_ctr_hmac 1 3 132 1 8 8 */
+    1092,    /* OBJ_ecies_with_x9_63_sha256_aes128_cbc_hmac_half 1 3 132 1 8 9 */
+    1093,    /* OBJ_ecies_with_x9_63_sha512_aes256_cbc_hmac_half 1 3 132 1 8 10 */
+    1094,    /* OBJ_ecies_with_x9_63_sha256_aes128_ctr_hmac_half 1 3 132 1 8 11 */
+    1095,    /* OBJ_ecies_with_x9_63_sha512_aes256_ctr_hmac_half 1 3 132 1 8 12 */
+    1096,    /* OBJ_ecies_with_x9_63_sha1_aes128_cbc_cmac 1 3 132 1 8 13 */
+    1097,    /* OBJ_ecies_with_x9_63_sha256_aes128_cbc_cmac 1 3 132 1 8 14 */
+    1098,    /* OBJ_ecies_with_x9_63_sha512_aes256_cbc_cmac 1 3 132 1 8 15 */
+    1099,    /* OBJ_ecies_with_x9_63_sha256_aes128_ctr_cmac 1 3 132 1 8 16 */
+    1100,    /* OBJ_ecies_with_x9_63_sha512_aes256_ctr_cmac 1 3 132 1 8 17 */
+     937,    /* OBJ_dhSinglePass_stdDH_sha224kdf_scheme 1 3 132 1 11 0 */
+     938,    /* OBJ_dhSinglePass_stdDH_sha256kdf_scheme 1 3 132 1 11 1 */
+     939,    /* OBJ_dhSinglePass_stdDH_sha384kdf_scheme 1 3 132 1 11 2 */
+     940,    /* OBJ_dhSinglePass_stdDH_sha512kdf_scheme 1 3 132 1 11 3 */
+     942,    /* OBJ_dhSinglePass_cofactorDH_sha224kdf_scheme 1 3 132 1 14 0 */
+     943,    /* OBJ_dhSinglePass_cofactorDH_sha256kdf_scheme 1 3 132 1 14 1 */
+     944,    /* OBJ_dhSinglePass_cofactorDH_sha384kdf_scheme 1 3 132 1 14 2 */
+     945,    /* OBJ_dhSinglePass_cofactorDH_sha512kdf_scheme 1 3 132 1 14 3 */
+    1065,    /* OBJ_x9_63_kdf                    1 3 132 1 17 0 */
+    1066,    /* OBJ_nist_concatenation_kdf       1 3 132 1 17 1 */
+    1067,    /* OBJ_tls_kdf                      1 3 132 1 17 2 */
+    1068,    /* OBJ_ikev2_kdf                    1 3 132 1 17 3 */
+    1071,    /* OBJ_aes128_cbc_in_ecies          1 3 132 1 20 0 */
+    1072,    /* OBJ_aes192_cbc_in_ecies          1 3 132 1 20 1 */
+    1073,    /* OBJ_aes256_cbc_in_ecies          1 3 132 1 20 2 */
+    1074,    /* OBJ_aes128_ctr_in_ecies          1 3 132 1 21 0 */
+    1075,    /* OBJ_aes192_ctr_in_ecies          1 3 132 1 21 1 */
+    1076,    /* OBJ_aes256_ctr_in_ecies          1 3 132 1 21 2 */
+    1079,    /* OBJ_cmac_aes128_ecies            1 3 132 1 24 0 */
+    1080,    /* OBJ_cmac_aes192_ecies            1 3 132 1 24 1 */
+    1081,    /* OBJ_cmac_aes256_ecies            1 3 132 1 24 2 */
+     631,    /* OBJ_setAttr_GenCryptgrm          2 23 42 3 3 3 1 */
+     632,    /* OBJ_setAttr_T2Enc                2 23 42 3 3 4 1 */
+     633,    /* OBJ_setAttr_T2cleartxt           2 23 42 3 3 4 2 */
+     634,    /* OBJ_setAttr_TokICCsig            2 23 42 3 3 5 1 */
+     635,    /* OBJ_setAttr_SecDevSig            2 23 42 3 3 5 2 */
+     436,    /* OBJ_ucl                          0 9 2342 19200300 */
+    1190,    /* OBJ_wapi_ec                      1 2 156 11235 1 1 */
+     820,    /* OBJ_id_Gost28147_89_None_KeyMeshing 1 2 643 2 2 14 0 */
+     819,    /* OBJ_id_Gost28147_89_CryptoPro_KeyMeshing 1 2 643 2 2 14 1 */
+     845,    /* OBJ_id_GostR3410_94_a            1 2 643 2 2 20 1 */
+     846,    /* OBJ_id_GostR3410_94_aBis         1 2 643 2 2 20 2 */
+     847,    /* OBJ_id_GostR3410_94_b            1 2 643 2 2 20 3 */
+     848,    /* OBJ_id_GostR3410_94_bBis         1 2 643 2 2 20 4 */
+     821,    /* OBJ_id_GostR3411_94_TestParamSet 1 2 643 2 2 30 0 */
+     822,    /* OBJ_id_GostR3411_94_CryptoProParamSet 1 2 643 2 2 30 1 */
+     823,    /* OBJ_id_Gost28147_89_TestParamSet 1 2 643 2 2 31 0 */
+     824,    /* OBJ_id_Gost28147_89_CryptoPro_A_ParamSet 1 2 643 2 2 31 1 */
+     825,    /* OBJ_id_Gost28147_89_CryptoPro_B_ParamSet 1 2 643 2 2 31 2 */
+     826,    /* OBJ_id_Gost28147_89_CryptoPro_C_ParamSet 1 2 643 2 2 31 3 */
+     827,    /* OBJ_id_Gost28147_89_CryptoPro_D_ParamSet 1 2 643 2 2 31 4 */
+     828,    /* OBJ_id_Gost28147_89_CryptoPro_Oscar_1_1_ParamSet 1 2 643 2 2 31 5 */
+     829,    /* OBJ_id_Gost28147_89_CryptoPro_Oscar_1_0_ParamSet 1 2 643 2 2 31 6 */
+     830,    /* OBJ_id_Gost28147_89_CryptoPro_RIC_1_ParamSet 1 2 643 2 2 31 7 */
+     831,    /* OBJ_id_GostR3410_94_TestParamSet 1 2 643 2 2 32 0 */
+     832,    /* OBJ_id_GostR3410_94_CryptoPro_A_ParamSet 1 2 643 2 2 32 2 */
+     833,    /* OBJ_id_GostR3410_94_CryptoPro_B_ParamSet 1 2 643 2 2 32 3 */
+     834,    /* OBJ_id_GostR3410_94_CryptoPro_C_ParamSet 1 2 643 2 2 32 4 */
+     835,    /* OBJ_id_GostR3410_94_CryptoPro_D_ParamSet 1 2 643 2 2 32 5 */
+     836,    /* OBJ_id_GostR3410_94_CryptoPro_XchA_ParamSet 1 2 643 2 2 33 1 */
+     837,    /* OBJ_id_GostR3410_94_CryptoPro_XchB_ParamSet 1 2 643 2 2 33 2 */
+     838,    /* OBJ_id_GostR3410_94_CryptoPro_XchC_ParamSet 1 2 643 2 2 33 3 */
+     839,    /* OBJ_id_GostR3410_2001_TestParamSet 1 2 643 2 2 35 0 */
+     840,    /* OBJ_id_GostR3410_2001_CryptoPro_A_ParamSet 1 2 643 2 2 35 1 */
+     841,    /* OBJ_id_GostR3410_2001_CryptoPro_B_ParamSet 1 2 643 2 2 35 2 */
+     842,    /* OBJ_id_GostR3410_2001_CryptoPro_C_ParamSet 1 2 643 2 2 35 3 */
+     843,    /* OBJ_id_GostR3410_2001_CryptoPro_XchA_ParamSet 1 2 643 2 2 36 0 */
+     844,    /* OBJ_id_GostR3410_2001_CryptoPro_XchB_ParamSet 1 2 643 2 2 36 1 */
+     978,    /* OBJ_id_tc26_sign                 1 2 643 7 1 1 1 */
+     981,    /* OBJ_id_tc26_digest               1 2 643 7 1 1 2 */
+     984,    /* OBJ_id_tc26_signwithdigest       1 2 643 7 1 1 3 */
+     987,    /* OBJ_id_tc26_mac                  1 2 643 7 1 1 4 */
+     990,    /* OBJ_id_tc26_cipher               1 2 643 7 1 1 5 */
+     991,    /* OBJ_id_tc26_agreement            1 2 643 7 1 1 6 */
+     995,    /* OBJ_id_tc26_sign_constants       1 2 643 7 1 2 1 */
+    1000,    /* OBJ_id_tc26_digest_constants     1 2 643 7 1 2 2 */
+    1001,    /* OBJ_id_tc26_cipher_constants     1 2 643 7 1 2 5 */
+       2,    /* OBJ_pkcs                         1 2 840 113549 1 */
+     431,    /* OBJ_hold_instruction_none        1 2 840 10040 2 1 */
+     432,    /* OBJ_hold_instruction_call_issuer 1 2 840 10040 2 2 */
+     433,    /* OBJ_hold_instruction_reject      1 2 840 10040 2 3 */
+     116,    /* OBJ_dsa                          1 2 840 10040 4 1 */
+     113,    /* OBJ_dsaWithSHA1                  1 2 840 10040 4 3 */
+     406,    /* OBJ_X9_62_prime_field            1 2 840 10045 1 1 */
+     407,    /* OBJ_X9_62_characteristic_two_field 1 2 840 10045 1 2 */
+     408,    /* OBJ_X9_62_id_ecPublicKey         1 2 840 10045 2 1 */
+     416,    /* OBJ_ecdsa_with_SHA1              1 2 840 10045 4 1 */
+     791,    /* OBJ_ecdsa_with_Recommended       1 2 840 10045 4 2 */
+     792,    /* OBJ_ecdsa_with_Specified         1 2 840 10045 4 3 */
+     920,    /* OBJ_dhpublicnumber               1 2 840 10046 2 1 */
+    1032,    /* OBJ_pkInitClientAuth             1 3 6 1 5 2 3 4 */
+    1033,    /* OBJ_pkInitKDC                    1 3 6 1 5 2 3 5 */
+     258,    /* OBJ_id_pkix_mod                  1 3 6 1 5 5 7 0 */
+     175,    /* OBJ_id_pe                        1 3 6 1 5 5 7 1 */
+     259,    /* OBJ_id_qt                        1 3 6 1 5 5 7 2 */
+     128,    /* OBJ_id_kp                        1 3 6 1 5 5 7 3 */
+     260,    /* OBJ_id_it                        1 3 6 1 5 5 7 4 */
+     261,    /* OBJ_id_pkip                      1 3 6 1 5 5 7 5 */
+     262,    /* OBJ_id_alg                       1 3 6 1 5 5 7 6 */
+     263,    /* OBJ_id_cmc                       1 3 6 1 5 5 7 7 */
+     264,    /* OBJ_id_on                        1 3 6 1 5 5 7 8 */
+     265,    /* OBJ_id_pda                       1 3 6 1 5 5 7 9 */
+     266,    /* OBJ_id_aca                       1 3 6 1 5 5 7 10 */
+     267,    /* OBJ_id_qcs                       1 3 6 1 5 5 7 11 */
+     268,    /* OBJ_id_cct                       1 3 6 1 5 5 7 12 */
+     662,    /* OBJ_id_ppl                       1 3 6 1 5 5 7 21 */
+     176,    /* OBJ_id_ad                        1 3 6 1 5 5 7 48 */
+     507,    /* OBJ_id_hex_partial_message       1 3 6 1 7 1 1 1 */
+     508,    /* OBJ_id_hex_multipart_message     1 3 6 1 7 1 1 2 */
+      57,    /* OBJ_netscape                     2 16 840 1 113730 */
+     754,    /* OBJ_camellia_128_ecb             0 3 4401 5 3 1 9 1 */
+     766,    /* OBJ_camellia_128_ofb128          0 3 4401 5 3 1 9 3 */
+     757,    /* OBJ_camellia_128_cfb128          0 3 4401 5 3 1 9 4 */
+     961,    /* OBJ_camellia_128_gcm             0 3 4401 5 3 1 9 6 */
+     962,    /* OBJ_camellia_128_ccm             0 3 4401 5 3 1 9 7 */
+     963,    /* OBJ_camellia_128_ctr             0 3 4401 5 3 1 9 9 */
+     964,    /* OBJ_camellia_128_cmac            0 3 4401 5 3 1 9 10 */
+     755,    /* OBJ_camellia_192_ecb             0 3 4401 5 3 1 9 21 */
+     767,    /* OBJ_camellia_192_ofb128          0 3 4401 5 3 1 9 23 */
+     758,    /* OBJ_camellia_192_cfb128          0 3 4401 5 3 1 9 24 */
+     965,    /* OBJ_camellia_192_gcm             0 3 4401 5 3 1 9 26 */
+     966,    /* OBJ_camellia_192_ccm             0 3 4401 5 3 1 9 27 */
+     967,    /* OBJ_camellia_192_ctr             0 3 4401 5 3 1 9 29 */
+     968,    /* OBJ_camellia_192_cmac            0 3 4401 5 3 1 9 30 */
+     756,    /* OBJ_camellia_256_ecb             0 3 4401 5 3 1 9 41 */
+     768,    /* OBJ_camellia_256_ofb128          0 3 4401 5 3 1 9 43 */
+     759,    /* OBJ_camellia_256_cfb128          0 3 4401 5 3 1 9 44 */
+     969,    /* OBJ_camellia_256_gcm             0 3 4401 5 3 1 9 46 */
+     970,    /* OBJ_camellia_256_ccm             0 3 4401 5 3 1 9 47 */
+     971,    /* OBJ_camellia_256_ctr             0 3 4401 5 3 1 9 49 */
+     972,    /* OBJ_camellia_256_cmac            0 3 4401 5 3 1 9 50 */
+     437,    /* OBJ_pilot                        0 9 2342 19200300 100 */
+    1164,    /* OBJ_sm6_ecb                      1 2 156 10197 1 101 1 */
+    1165,    /* OBJ_sm6_cbc                      1 2 156 10197 1 101 2 */
+    1166,    /* OBJ_sm6_ofb128                   1 2 156 10197 1 101 3 */
+    1167,    /* OBJ_sm6_cfb128                   1 2 156 10197 1 101 4 */
+    1115,    /* OBJ_sm1_ecb                      1 2 156 10197 1 102 1 */
+    1116,    /* OBJ_sm1_cbc                      1 2 156 10197 1 102 2 */
+    1117,    /* OBJ_sm1_ofb128                   1 2 156 10197 1 102 3 */
+    1118,    /* OBJ_sm1_cfb128                   1 2 156 10197 1 102 4 */
+    1119,    /* OBJ_sm1_cfb1                     1 2 156 10197 1 102 5 */
+    1120,    /* OBJ_sm1_cfb8                     1 2 156 10197 1 102 6 */
+    1108,    /* OBJ_ssf33_ecb                    1 2 156 10197 1 103 1 */
+    1109,    /* OBJ_ssf33_cbc                    1 2 156 10197 1 103 2 */
+    1110,    /* OBJ_ssf33_ofb128                 1 2 156 10197 1 103 3 */
+    1111,    /* OBJ_ssf33_cfb128                 1 2 156 10197 1 103 4 */
+    1112,    /* OBJ_ssf33_cfb1                   1 2 156 10197 1 103 5 */
+    1113,    /* OBJ_ssf33_cfb8                   1 2 156 10197 1 103 6 */
+    1114,    /* OBJ_ssf33_cbc_mac                1 2 156 10197 1 103 7 */
+    1150,    /* OBJ_sms4_ecb                     1 2 156 10197 1 104 1 */
+    1151,    /* OBJ_sms4_cbc                     1 2 156 10197 1 104 2 */
+    1152,    /* OBJ_sms4_ofb128                  1 2 156 10197 1 104 3 */
+    1153,    /* OBJ_sms4_cfb128                  1 2 156 10197 1 104 4 */
+    1154,    /* OBJ_sms4_cfb1                    1 2 156 10197 1 104 5 */
+    1155,    /* OBJ_sms4_cfb8                    1 2 156 10197 1 104 6 */
+    1156,    /* OBJ_sms4_ctr                     1 2 156 10197 1 104 7 */
+    1157,    /* OBJ_sms4_gcm                     1 2 156 10197 1 104 8 */
+    1158,    /* OBJ_sms4_ccm                     1 2 156 10197 1 104 9 */
+    1159,    /* OBJ_sms4_xts                     1 2 156 10197 1 104 10 */
+    1160,    /* OBJ_sms4_wrap                    1 2 156 10197 1 104 11 */
+    1161,    /* OBJ_sms4_wrap_pad                1 2 156 10197 1 104 12 */
+    1162,    /* OBJ_sms4_ocb                     1 2 156 10197 1 104 100 */
+    1163,    /* OBJ_sm5                          1 2 156 10197 1 201 */
+    1121,    /* OBJ_sm2p256v1                    1 2 156 10197 1 301 */
+    1168,    /* OBJ_id_sm9PublicKey              1 2 156 10197 1 302 */
+    1148,    /* OBJ_sm3                          1 2 156 10197 1 401 */
+    1125,    /* OBJ_sm2sign_with_sm3             1 2 156 10197 1 501 */
+    1126,    /* OBJ_sm2sign_with_sha1            1 2 156 10197 1 502 */
+    1127,    /* OBJ_sm2sign_with_sha256          1 2 156 10197 1 503 */
+    1128,    /* OBJ_sm2sign_with_sha512          1 2 156 10197 1 504 */
+    1129,    /* OBJ_sm2sign_with_sha224          1 2 156 10197 1 505 */
+    1130,    /* OBJ_sm2sign_with_sha384          1 2 156 10197 1 506 */
+    1131,    /* OBJ_sm2sign_with_rmd160          1 2 156 10197 1 507 */
+    1132,    /* OBJ_sm2sign_with_whirlpool       1 2 156 10197 1 520 */
+    1133,    /* OBJ_sm2sign_with_blake2b512      1 2 156 10197 1 521 */
+    1134,    /* OBJ_sm2sign_with_blake2s256      1 2 156 10197 1 522 */
+    1185,    /* OBJ_zuc                          1 2 156 10197 1 800 */
+    1191,    /* OBJ_wapi_ecdsa192_sha256         1 2 156 11235 1 1 1 */
+     776,    /* OBJ_seed_ecb                     1 2 410 200004 1 3 */
+     777,    /* OBJ_seed_cbc                     1 2 410 200004 1 4 */
+     779,    /* OBJ_seed_cfb128                  1 2 410 200004 1 5 */
+     778,    /* OBJ_seed_ofb128                  1 2 410 200004 1 6 */
+     852,    /* OBJ_id_GostR3411_94_with_GostR3410_94_cc 1 2 643 2 9 1 3 3 */
+     853,    /* OBJ_id_GostR3411_94_with_GostR3410_2001_cc 1 2 643 2 9 1 3 4 */
+     850,    /* OBJ_id_GostR3410_94_cc           1 2 643 2 9 1 5 3 */
+     851,    /* OBJ_id_GostR3410_2001_cc         1 2 643 2 9 1 5 4 */
+     849,    /* OBJ_id_Gost28147_89_cc           1 2 643 2 9 1 6 1 */
+     854,    /* OBJ_id_GostR3410_2001_ParamSet_cc 1 2 643 2 9 1 8 1 */
+    1004,    /* OBJ_INN                          1 2 643 3 131 1 1 */
+     979,    /* OBJ_id_GostR3410_2012_256        1 2 643 7 1 1 1 1 */
+     980,    /* OBJ_id_GostR3410_2012_512        1 2 643 7 1 1 1 2 */
+     982,    /* OBJ_id_GostR3411_2012_256        1 2 643 7 1 1 2 2 */
+     983,    /* OBJ_id_GostR3411_2012_512        1 2 643 7 1 1 2 3 */
+     985,    /* OBJ_id_tc26_signwithdigest_gost3410_2012_256 1 2 643 7 1 1 3 2 */
+     986,    /* OBJ_id_tc26_signwithdigest_gost3410_2012_512 1 2 643 7 1 1 3 3 */
+     988,    /* OBJ_id_tc26_hmac_gost_3411_2012_256 1 2 643 7 1 1 4 1 */
+     989,    /* OBJ_id_tc26_hmac_gost_3411_2012_512 1 2 643 7 1 1 4 2 */
+     992,    /* OBJ_id_tc26_agreement_gost_3410_2012_256 1 2 643 7 1 1 6 1 */
+     993,    /* OBJ_id_tc26_agreement_gost_3410_2012_512 1 2 643 7 1 1 6 2 */
+     996,    /* OBJ_id_tc26_gost_3410_2012_512_constants 1 2 643 7 1 2 1 2 */
+    1002,    /* OBJ_id_tc26_gost_28147_constants 1 2 643 7 1 2 5 1 */
+     186,    /* OBJ_pkcs1                        1 2 840 113549 1 1 */
+      27,    /* OBJ_pkcs3                        1 2 840 113549 1 3 */
+     187,    /* OBJ_pkcs5                        1 2 840 113549 1 5 */
+      20,    /* OBJ_pkcs7                        1 2 840 113549 1 7 */
+      47,    /* OBJ_pkcs9                        1 2 840 113549 1 9 */
+       3,    /* OBJ_md2                          1 2 840 113549 2 2 */
+     257,    /* OBJ_md4                          1 2 840 113549 2 4 */
+       4,    /* OBJ_md5                          1 2 840 113549 2 5 */
+     797,    /* OBJ_hmacWithMD5                  1 2 840 113549 2 6 */
+     163,    /* OBJ_hmacWithSHA1                 1 2 840 113549 2 7 */
+     798,    /* OBJ_hmacWithSHA224               1 2 840 113549 2 8 */
+     799,    /* OBJ_hmacWithSHA256               1 2 840 113549 2 9 */
+     800,    /* OBJ_hmacWithSHA384               1 2 840 113549 2 10 */
+     801,    /* OBJ_hmacWithSHA512               1 2 840 113549 2 11 */
+      37,    /* OBJ_rc2_cbc                      1 2 840 113549 3 2 */
+       5,    /* OBJ_rc4                          1 2 840 113549 3 4 */
+      44,    /* OBJ_des_ede3_cbc                 1 2 840 113549 3 7 */
+     120,    /* OBJ_rc5_cbc                      1 2 840 113549 3 8 */
+     643,    /* OBJ_des_cdmf                     1 2 840 113549 3 10 */
+     680,    /* OBJ_X9_62_id_characteristic_two_basis 1 2 840 10045 1 2 3 */
+     684,    /* OBJ_X9_62_c2pnb163v1             1 2 840 10045 3 0 1 */
+     685,    /* OBJ_X9_62_c2pnb163v2             1 2 840 10045 3 0 2 */
+     686,    /* OBJ_X9_62_c2pnb163v3             1 2 840 10045 3 0 3 */
+     687,    /* OBJ_X9_62_c2pnb176v1             1 2 840 10045 3 0 4 */
+     688,    /* OBJ_X9_62_c2tnb191v1             1 2 840 10045 3 0 5 */
+     689,    /* OBJ_X9_62_c2tnb191v2             1 2 840 10045 3 0 6 */
+     690,    /* OBJ_X9_62_c2tnb191v3             1 2 840 10045 3 0 7 */
+     691,    /* OBJ_X9_62_c2onb191v4             1 2 840 10045 3 0 8 */
+     692,    /* OBJ_X9_62_c2onb191v5             1 2 840 10045 3 0 9 */
+     693,    /* OBJ_X9_62_c2pnb208w1             1 2 840 10045 3 0 10 */
+     694,    /* OBJ_X9_62_c2tnb239v1             1 2 840 10045 3 0 11 */
+     695,    /* OBJ_X9_62_c2tnb239v2             1 2 840 10045 3 0 12 */
+     696,    /* OBJ_X9_62_c2tnb239v3             1 2 840 10045 3 0 13 */
+     697,    /* OBJ_X9_62_c2onb239v4             1 2 840 10045 3 0 14 */
+     698,    /* OBJ_X9_62_c2onb239v5             1 2 840 10045 3 0 15 */
+     699,    /* OBJ_X9_62_c2pnb272w1             1 2 840 10045 3 0 16 */
+     700,    /* OBJ_X9_62_c2pnb304w1             1 2 840 10045 3 0 17 */
+     701,    /* OBJ_X9_62_c2tnb359v1             1 2 840 10045 3 0 18 */
+     702,    /* OBJ_X9_62_c2pnb368w1             1 2 840 10045 3 0 19 */
+     703,    /* OBJ_X9_62_c2tnb431r1             1 2 840 10045 3 0 20 */
+     409,    /* OBJ_X9_62_prime192v1             1 2 840 10045 3 1 1 */
+     410,    /* OBJ_X9_62_prime192v2             1 2 840 10045 3 1 2 */
+     411,    /* OBJ_X9_62_prime192v3             1 2 840 10045 3 1 3 */
+     412,    /* OBJ_X9_62_prime239v1             1 2 840 10045 3 1 4 */
+     413,    /* OBJ_X9_62_prime239v2             1 2 840 10045 3 1 5 */
+     414,    /* OBJ_X9_62_prime239v3             1 2 840 10045 3 1 6 */
+     415,    /* OBJ_X9_62_prime256v1             1 2 840 10045 3 1 7 */
+     793,    /* OBJ_ecdsa_with_SHA224            1 2 840 10045 4 3 1 */
+     794,    /* OBJ_ecdsa_with_SHA256            1 2 840 10045 4 3 2 */
+     795,    /* OBJ_ecdsa_with_SHA384            1 2 840 10045 4 3 3 */
+     796,    /* OBJ_ecdsa_with_SHA512            1 2 840 10045 4 3 4 */
+    1200,    /* OBJ_GmSSL                        1 3 6 1 4 1 49549 */
+     269,    /* OBJ_id_pkix1_explicit_88         1 3 6 1 5 5 7 0 1 */
+     270,    /* OBJ_id_pkix1_implicit_88         1 3 6 1 5 5 7 0 2 */
+     271,    /* OBJ_id_pkix1_explicit_93         1 3 6 1 5 5 7 0 3 */
+     272,    /* OBJ_id_pkix1_implicit_93         1 3 6 1 5 5 7 0 4 */
+     273,    /* OBJ_id_mod_crmf                  1 3 6 1 5 5 7 0 5 */
+     274,    /* OBJ_id_mod_cmc                   1 3 6 1 5 5 7 0 6 */
+     275,    /* OBJ_id_mod_kea_profile_88        1 3 6 1 5 5 7 0 7 */
+     276,    /* OBJ_id_mod_kea_profile_93        1 3 6 1 5 5 7 0 8 */
+     277,    /* OBJ_id_mod_cmp                   1 3 6 1 5 5 7 0 9 */
+     278,    /* OBJ_id_mod_qualified_cert_88     1 3 6 1 5 5 7 0 10 */
+     279,    /* OBJ_id_mod_qualified_cert_93     1 3 6 1 5 5 7 0 11 */
+     280,    /* OBJ_id_mod_attribute_cert        1 3 6 1 5 5 7 0 12 */
+     281,    /* OBJ_id_mod_timestamp_protocol    1 3 6 1 5 5 7 0 13 */
+     282,    /* OBJ_id_mod_ocsp                  1 3 6 1 5 5 7 0 14 */
+     283,    /* OBJ_id_mod_dvcs                  1 3 6 1 5 5 7 0 15 */
+     284,    /* OBJ_id_mod_cmp2000               1 3 6 1 5 5 7 0 16 */
+     177,    /* OBJ_info_access                  1 3 6 1 5 5 7 1 1 */
+     285,    /* OBJ_biometricInfo                1 3 6 1 5 5 7 1 2 */
+     286,    /* OBJ_qcStatements                 1 3 6 1 5 5 7 1 3 */
+     287,    /* OBJ_ac_auditEntity               1 3 6 1 5 5 7 1 4 */
+     288,    /* OBJ_ac_targeting                 1 3 6 1 5 5 7 1 5 */
+     289,    /* OBJ_aaControls                   1 3 6 1 5 5 7 1 6 */
+     290,    /* OBJ_sbgp_ipAddrBlock             1 3 6 1 5 5 7 1 7 */
+     291,    /* OBJ_sbgp_autonomousSysNum        1 3 6 1 5 5 7 1 8 */
+     292,    /* OBJ_sbgp_routerIdentifier        1 3 6 1 5 5 7 1 9 */
+     397,    /* OBJ_ac_proxying                  1 3 6 1 5 5 7 1 10 */
+     398,    /* OBJ_sinfo_access                 1 3 6 1 5 5 7 1 11 */
+     663,    /* OBJ_proxyCertInfo                1 3 6 1 5 5 7 1 14 */
+    1020,    /* OBJ_tlsfeature                   1 3 6 1 5 5 7 1 24 */
+     164,    /* OBJ_id_qt_cps                    1 3 6 1 5 5 7 2 1 */
+     165,    /* OBJ_id_qt_unotice                1 3 6 1 5 5 7 2 2 */
+     293,    /* OBJ_textNotice                   1 3 6 1 5 5 7 2 3 */
+     129,    /* OBJ_server_auth                  1 3 6 1 5 5 7 3 1 */
+     130,    /* OBJ_client_auth                  1 3 6 1 5 5 7 3 2 */
+     131,    /* OBJ_code_sign                    1 3 6 1 5 5 7 3 3 */
+     132,    /* OBJ_email_protect                1 3 6 1 5 5 7 3 4 */
+     294,    /* OBJ_ipsecEndSystem               1 3 6 1 5 5 7 3 5 */
+     295,    /* OBJ_ipsecTunnel                  1 3 6 1 5 5 7 3 6 */
+     296,    /* OBJ_ipsecUser                    1 3 6 1 5 5 7 3 7 */
+     133,    /* OBJ_time_stamp                   1 3 6 1 5 5 7 3 8 */
+     180,    /* OBJ_OCSP_sign                    1 3 6 1 5 5 7 3 9 */
+     297,    /* OBJ_dvcs                         1 3 6 1 5 5 7 3 10 */
+    1022,    /* OBJ_ipsec_IKE                    1 3 6 1 5 5 7 3 17 */
+    1023,    /* OBJ_capwapAC                     1 3 6 1 5 5 7 3 18 */
+    1024,    /* OBJ_capwapWTP                    1 3 6 1 5 5 7 3 19 */
+    1025,    /* OBJ_sshClient                    1 3 6 1 5 5 7 3 21 */
+    1026,    /* OBJ_sshServer                    1 3 6 1 5 5 7 3 22 */
+    1027,    /* OBJ_sendRouter                   1 3 6 1 5 5 7 3 23 */
+    1028,    /* OBJ_sendProxiedRouter            1 3 6 1 5 5 7 3 24 */
+    1029,    /* OBJ_sendOwner                    1 3 6 1 5 5 7 3 25 */
+    1030,    /* OBJ_sendProxiedOwner             1 3 6 1 5 5 7 3 26 */
+     298,    /* OBJ_id_it_caProtEncCert          1 3 6 1 5 5 7 4 1 */
+     299,    /* OBJ_id_it_signKeyPairTypes       1 3 6 1 5 5 7 4 2 */
+     300,    /* OBJ_id_it_encKeyPairTypes        1 3 6 1 5 5 7 4 3 */
+     301,    /* OBJ_id_it_preferredSymmAlg       1 3 6 1 5 5 7 4 4 */
+     302,    /* OBJ_id_it_caKeyUpdateInfo        1 3 6 1 5 5 7 4 5 */
+     303,    /* OBJ_id_it_currentCRL             1 3 6 1 5 5 7 4 6 */
+     304,    /* OBJ_id_it_unsupportedOIDs        1 3 6 1 5 5 7 4 7 */
+     305,    /* OBJ_id_it_subscriptionRequest    1 3 6 1 5 5 7 4 8 */
+     306,    /* OBJ_id_it_subscriptionResponse   1 3 6 1 5 5 7 4 9 */
+     307,    /* OBJ_id_it_keyPairParamReq        1 3 6 1 5 5 7 4 10 */
+     308,    /* OBJ_id_it_keyPairParamRep        1 3 6 1 5 5 7 4 11 */
+     309,    /* OBJ_id_it_revPassphrase          1 3 6 1 5 5 7 4 12 */
+     310,    /* OBJ_id_it_implicitConfirm        1 3 6 1 5 5 7 4 13 */
+     311,    /* OBJ_id_it_confirmWaitTime        1 3 6 1 5 5 7 4 14 */
+     312,    /* OBJ_id_it_origPKIMessage         1 3 6 1 5 5 7 4 15 */
+     784,    /* OBJ_id_it_suppLangTags           1 3 6 1 5 5 7 4 16 */
+     313,    /* OBJ_id_regCtrl                   1 3 6 1 5 5 7 5 1 */
+     314,    /* OBJ_id_regInfo                   1 3 6 1 5 5 7 5 2 */
+     323,    /* OBJ_id_alg_des40                 1 3 6 1 5 5 7 6 1 */
+     324,    /* OBJ_id_alg_noSignature           1 3 6 1 5 5 7 6 2 */
+     325,    /* OBJ_id_alg_dh_sig_hmac_sha1      1 3 6 1 5 5 7 6 3 */
+     326,    /* OBJ_id_alg_dh_pop                1 3 6 1 5 5 7 6 4 */
+     327,    /* OBJ_id_cmc_statusInfo            1 3 6 1 5 5 7 7 1 */
+     328,    /* OBJ_id_cmc_identification        1 3 6 1 5 5 7 7 2 */
+     329,    /* OBJ_id_cmc_identityProof         1 3 6 1 5 5 7 7 3 */
+     330,    /* OBJ_id_cmc_dataReturn            1 3 6 1 5 5 7 7 4 */
+     331,    /* OBJ_id_cmc_transactionId         1 3 6 1 5 5 7 7 5 */
+     332,    /* OBJ_id_cmc_senderNonce           1 3 6 1 5 5 7 7 6 */
+     333,    /* OBJ_id_cmc_recipientNonce        1 3 6 1 5 5 7 7 7 */
+     334,    /* OBJ_id_cmc_addExtensions         1 3 6 1 5 5 7 7 8 */
+     335,    /* OBJ_id_cmc_encryptedPOP          1 3 6 1 5 5 7 7 9 */
+     336,    /* OBJ_id_cmc_decryptedPOP          1 3 6 1 5 5 7 7 10 */
+     337,    /* OBJ_id_cmc_lraPOPWitness         1 3 6 1 5 5 7 7 11 */
+     338,    /* OBJ_id_cmc_getCert               1 3 6 1 5 5 7 7 15 */
+     339,    /* OBJ_id_cmc_getCRL                1 3 6 1 5 5 7 7 16 */
+     340,    /* OBJ_id_cmc_revokeRequest         1 3 6 1 5 5 7 7 17 */
+     341,    /* OBJ_id_cmc_regInfo               1 3 6 1 5 5 7 7 18 */
+     342,    /* OBJ_id_cmc_responseInfo          1 3 6 1 5 5 7 7 19 */
+     343,    /* OBJ_id_cmc_queryPending          1 3 6 1 5 5 7 7 21 */
+     344,    /* OBJ_id_cmc_popLinkRandom         1 3 6 1 5 5 7 7 22 */
+     345,    /* OBJ_id_cmc_popLinkWitness        1 3 6 1 5 5 7 7 23 */
+     346,    /* OBJ_id_cmc_confirmCertAcceptance 1 3 6 1 5 5 7 7 24 */
+     347,    /* OBJ_id_on_personalData           1 3 6 1 5 5 7 8 1 */
+     858,    /* OBJ_id_on_permanentIdentifier    1 3 6 1 5 5 7 8 3 */
+     348,    /* OBJ_id_pda_dateOfBirth           1 3 6 1 5 5 7 9 1 */
+     349,    /* OBJ_id_pda_placeOfBirth          1 3 6 1 5 5 7 9 2 */
+     351,    /* OBJ_id_pda_gender                1 3 6 1 5 5 7 9 3 */
+     352,    /* OBJ_id_pda_countryOfCitizenship  1 3 6 1 5 5 7 9 4 */
+     353,    /* OBJ_id_pda_countryOfResidence    1 3 6 1 5 5 7 9 5 */
+     354,    /* OBJ_id_aca_authenticationInfo    1 3 6 1 5 5 7 10 1 */
+     355,    /* OBJ_id_aca_accessIdentity        1 3 6 1 5 5 7 10 2 */
+     356,    /* OBJ_id_aca_chargingIdentity      1 3 6 1 5 5 7 10 3 */
+     357,    /* OBJ_id_aca_group                 1 3 6 1 5 5 7 10 4 */
+     358,    /* OBJ_id_aca_role                  1 3 6 1 5 5 7 10 5 */
+     399,    /* OBJ_id_aca_encAttrs              1 3 6 1 5 5 7 10 6 */
+     359,    /* OBJ_id_qcs_pkixQCSyntax_v1       1 3 6 1 5 5 7 11 1 */
+     360,    /* OBJ_id_cct_crs                   1 3 6 1 5 5 7 12 1 */
+     361,    /* OBJ_id_cct_PKIData               1 3 6 1 5 5 7 12 2 */
+     362,    /* OBJ_id_cct_PKIResponse           1 3 6 1 5 5 7 12 3 */
+     664,    /* OBJ_id_ppl_anyLanguage           1 3 6 1 5 5 7 21 0 */
+     665,    /* OBJ_id_ppl_inheritAll            1 3 6 1 5 5 7 21 1 */
+     667,    /* OBJ_Independent                  1 3 6 1 5 5 7 21 2 */
+     178,    /* OBJ_ad_OCSP                      1 3 6 1 5 5 7 48 1 */
+     179,    /* OBJ_ad_ca_issuers                1 3 6 1 5 5 7 48 2 */
+     363,    /* OBJ_ad_timeStamping              1 3 6 1 5 5 7 48 3 */
+     364,    /* OBJ_ad_dvcs                      1 3 6 1 5 5 7 48 4 */
+     785,    /* OBJ_caRepository                 1 3 6 1 5 5 7 48 5 */
+     780,    /* OBJ_hmac_md5                     1 3 6 1 5 5 8 1 1 */
+     781,    /* OBJ_hmac_sha1                    1 3 6 1 5 5 8 1 2 */
+      58,    /* OBJ_netscape_cert_extension      2 16 840 1 113730 1 */
+      59,    /* OBJ_netscape_data_type           2 16 840 1 113730 2 */
+     438,    /* OBJ_pilotAttributeType           0 9 2342 19200300 100 1 */
+     439,    /* OBJ_pilotAttributeSyntax         0 9 2342 19200300 100 3 */
+     440,    /* OBJ_pilotObjectClass             0 9 2342 19200300 100 4 */
+     441,    /* OBJ_pilotGroups                  0 9 2342 19200300 100 10 */
+    1122,    /* OBJ_sm2sign                      1 2 156 10197 1 301 1 */
+    1123,    /* OBJ_sm2exchange                  1 2 156 10197 1 301 2 */
+    1124,    /* OBJ_sm2encrypt                   1 2 156 10197 1 301 3 */
+    1169,    /* OBJ_sm9sign                      1 2 156 10197 1 302 1 */
+    1170,    /* OBJ_sm9keyagreement              1 2 156 10197 1 302 2 */
+    1171,    /* OBJ_sm9encrypt                   1 2 156 10197 1 302 3 */
+    1172,    /* OBJ_sm9hash1                     1 2 156 10197 1 302 4 */
+    1173,    /* OBJ_sm9kdf                       1 2 156 10197 1 302 5 */
+    1174,    /* OBJ_id_sm9MasterSecret           1 2 156 10197 1 302 6 */
+    1209,    /* OBJ_sm9hash2                     1 2 156 10197 1 303 7 */
+    1149,    /* OBJ_hmac_sm3                     1 2 156 10197 1 401 2 */
+    1186,    /* OBJ_zuc_128eea3                  1 2 156 10197 1 800 1 */
+    1187,    /* OBJ_zuc_128eia3                  1 2 156 10197 1 800 2 */
+    1213,    /* OBJ_zuc_mac                      1 2 156 10197 1 800 3 */
+    1212,    /* OBJ_zuc256                       1 2 156 10197 1 800 4 */
+    1214,    /* OBJ_zuc256_mac                   1 2 156 10197 1 800 5 */
+    1192,    /* OBJ_wapip192v1                   1 2 156 11235 1 1 2 1 */
+     997,    /* OBJ_id_tc26_gost_3410_2012_512_paramSetTest 1 2 643 7 1 2 1 2 0 */
+     998,    /* OBJ_id_tc26_gost_3410_2012_512_paramSetA 1 2 643 7 1 2 1 2 1 */
+     999,    /* OBJ_id_tc26_gost_3410_2012_512_paramSetB 1 2 643 7 1 2 1 2 2 */
+    1003,    /* OBJ_id_tc26_gost_28147_param_Z   1 2 643 7 1 2 5 1 1 */
+    1101,    /* OBJ_ibcs1                        1 2 840 1 114334 1 1 */
+     108,    /* OBJ_cast5_cbc                    1 2 840 113533 7 66 10 */
+     112,    /* OBJ_pbeWithMD5AndCast5_CBC       1 2 840 113533 7 66 12 */
+     782,    /* OBJ_id_PasswordBasedMAC          1 2 840 113533 7 66 13 */
+     783,    /* OBJ_id_DHBasedMac                1 2 840 113533 7 66 30 */
+       6,    /* OBJ_rsaEncryption                1 2 840 113549 1 1 1 */
+       7,    /* OBJ_md2WithRSAEncryption         1 2 840 113549 1 1 2 */
+     396,    /* OBJ_md4WithRSAEncryption         1 2 840 113549 1 1 3 */
+       8,    /* OBJ_md5WithRSAEncryption         1 2 840 113549 1 1 4 */
+      65,    /* OBJ_sha1WithRSAEncryption        1 2 840 113549 1 1 5 */
+     644,    /* OBJ_rsaOAEPEncryptionSET         1 2 840 113549 1 1 6 */
+     919,    /* OBJ_rsaesOaep                    1 2 840 113549 1 1 7 */
+     911,    /* OBJ_mgf1                         1 2 840 113549 1 1 8 */
+     935,    /* OBJ_pSpecified                   1 2 840 113549 1 1 9 */
+     912,    /* OBJ_rsassaPss                    1 2 840 113549 1 1 10 */
+     668,    /* OBJ_sha256WithRSAEncryption      1 2 840 113549 1 1 11 */
+     669,    /* OBJ_sha384WithRSAEncryption      1 2 840 113549 1 1 12 */
+     670,    /* OBJ_sha512WithRSAEncryption      1 2 840 113549 1 1 13 */
+     671,    /* OBJ_sha224WithRSAEncryption      1 2 840 113549 1 1 14 */
+      28,    /* OBJ_dhKeyAgreement               1 2 840 113549 1 3 1 */
+       9,    /* OBJ_pbeWithMD2AndDES_CBC         1 2 840 113549 1 5 1 */
+      10,    /* OBJ_pbeWithMD5AndDES_CBC         1 2 840 113549 1 5 3 */
+     168,    /* OBJ_pbeWithMD2AndRC2_CBC         1 2 840 113549 1 5 4 */
+     169,    /* OBJ_pbeWithMD5AndRC2_CBC         1 2 840 113549 1 5 6 */
+     170,    /* OBJ_pbeWithSHA1AndDES_CBC        1 2 840 113549 1 5 10 */
+      68,    /* OBJ_pbeWithSHA1AndRC2_CBC        1 2 840 113549 1 5 11 */
+      69,    /* OBJ_id_pbkdf2                    1 2 840 113549 1 5 12 */
+     161,    /* OBJ_pbes2                        1 2 840 113549 1 5 13 */
+     162,    /* OBJ_pbmac1                       1 2 840 113549 1 5 14 */
+      21,    /* OBJ_pkcs7_data                   1 2 840 113549 1 7 1 */
+      22,    /* OBJ_pkcs7_signed                 1 2 840 113549 1 7 2 */
+      23,    /* OBJ_pkcs7_enveloped              1 2 840 113549 1 7 3 */
+      24,    /* OBJ_pkcs7_signedAndEnveloped     1 2 840 113549 1 7 4 */
+      25,    /* OBJ_pkcs7_digest                 1 2 840 113549 1 7 5 */
+      26,    /* OBJ_pkcs7_encrypted              1 2 840 113549 1 7 6 */
+      48,    /* OBJ_pkcs9_emailAddress           1 2 840 113549 1 9 1 */
+      49,    /* OBJ_pkcs9_unstructuredName       1 2 840 113549 1 9 2 */
+      50,    /* OBJ_pkcs9_contentType            1 2 840 113549 1 9 3 */
+      51,    /* OBJ_pkcs9_messageDigest          1 2 840 113549 1 9 4 */
+      52,    /* OBJ_pkcs9_signingTime            1 2 840 113549 1 9 5 */
+      53,    /* OBJ_pkcs9_countersignature       1 2 840 113549 1 9 6 */
+      54,    /* OBJ_pkcs9_challengePassword      1 2 840 113549 1 9 7 */
+      55,    /* OBJ_pkcs9_unstructuredAddress    1 2 840 113549 1 9 8 */
+      56,    /* OBJ_pkcs9_extCertAttributes      1 2 840 113549 1 9 9 */
+     172,    /* OBJ_ext_req                      1 2 840 113549 1 9 14 */
+     167,    /* OBJ_SMIMECapabilities            1 2 840 113549 1 9 15 */
+     188,    /* OBJ_SMIME                        1 2 840 113549 1 9 16 */
+     156,    /* OBJ_friendlyName                 1 2 840 113549 1 9 20 */
+     157,    /* OBJ_localKeyID                   1 2 840 113549 1 9 21 */
+     681,    /* OBJ_X9_62_onBasis                1 2 840 10045 1 2 3 1 */
+     682,    /* OBJ_X9_62_tpBasis                1 2 840 10045 1 2 3 2 */
+     683,    /* OBJ_X9_62_ppBasis                1 2 840 10045 1 2 3 3 */
+     417,    /* OBJ_ms_csp_name                  1 3 6 1 4 1 311 17 1 */
+     856,    /* OBJ_LocalKeySet                  1 3 6 1 4 1 311 17 2 */
+    1201,    /* OBJ_cpk                          1 3 6 1 4 1 49549 1 */
+    1208,    /* OBJ_paillier                     1 3 6 1 4 1 49549 21 */
+     390,    /* OBJ_dcObject                     1 3 6 1 4 1 1466 344 */
+      91,    /* OBJ_bf_cbc                       1 3 6 1 4 1 3029 1 2 */
+     973,    /* OBJ_id_scrypt                    1 3 6 1 4 1 11591 4 11 */
+     315,    /* OBJ_id_regCtrl_regToken          1 3 6 1 5 5 7 5 1 1 */
+     316,    /* OBJ_id_regCtrl_authenticator     1 3 6 1 5 5 7 5 1 2 */
+     317,    /* OBJ_id_regCtrl_pkiPublicationInfo 1 3 6 1 5 5 7 5 1 3 */
+     318,    /* OBJ_id_regCtrl_pkiArchiveOptions 1 3 6 1 5 5 7 5 1 4 */
+     319,    /* OBJ_id_regCtrl_oldCertID         1 3 6 1 5 5 7 5 1 5 */
+     320,    /* OBJ_id_regCtrl_protocolEncrKey   1 3 6 1 5 5 7 5 1 6 */
+     321,    /* OBJ_id_regInfo_utf8Pairs         1 3 6 1 5 5 7 5 2 1 */
+     322,    /* OBJ_id_regInfo_certReq           1 3 6 1 5 5 7 5 2 2 */
+     365,    /* OBJ_id_pkix_OCSP_basic           1 3 6 1 5 5 7 48 1 1 */
+     366,    /* OBJ_id_pkix_OCSP_Nonce           1 3 6 1 5 5 7 48 1 2 */
+     367,    /* OBJ_id_pkix_OCSP_CrlID           1 3 6 1 5 5 7 48 1 3 */
+     368,    /* OBJ_id_pkix_OCSP_acceptableResponses 1 3 6 1 5 5 7 48 1 4 */
+     369,    /* OBJ_id_pkix_OCSP_noCheck         1 3 6 1 5 5 7 48 1 5 */
+     370,    /* OBJ_id_pkix_OCSP_archiveCutoff   1 3 6 1 5 5 7 48 1 6 */
+     371,    /* OBJ_id_pkix_OCSP_serviceLocator  1 3 6 1 5 5 7 48 1 7 */
+     372,    /* OBJ_id_pkix_OCSP_extendedStatus  1 3 6 1 5 5 7 48 1 8 */
+     373,    /* OBJ_id_pkix_OCSP_valid           1 3 6 1 5 5 7 48 1 9 */
+     374,    /* OBJ_id_pkix_OCSP_path            1 3 6 1 5 5 7 48 1 10 */
+     375,    /* OBJ_id_pkix_OCSP_trustRoot       1 3 6 1 5 5 7 48 1 11 */
+     921,    /* OBJ_brainpoolP160r1              1 3 36 3 3 2 8 1 1 1 */
+     922,    /* OBJ_brainpoolP160t1              1 3 36 3 3 2 8 1 1 2 */
+     923,    /* OBJ_brainpoolP192r1              1 3 36 3 3 2 8 1 1 3 */
+     924,    /* OBJ_brainpoolP192t1              1 3 36 3 3 2 8 1 1 4 */
+     925,    /* OBJ_brainpoolP224r1              1 3 36 3 3 2 8 1 1 5 */
+     926,    /* OBJ_brainpoolP224t1              1 3 36 3 3 2 8 1 1 6 */
+     927,    /* OBJ_brainpoolP256r1              1 3 36 3 3 2 8 1 1 7 */
+     928,    /* OBJ_brainpoolP256t1              1 3 36 3 3 2 8 1 1 8 */
+     929,    /* OBJ_brainpoolP320r1              1 3 36 3 3 2 8 1 1 9 */
+     930,    /* OBJ_brainpoolP320t1              1 3 36 3 3 2 8 1 1 10 */
+     931,    /* OBJ_brainpoolP384r1              1 3 36 3 3 2 8 1 1 11 */
+     932,    /* OBJ_brainpoolP384t1              1 3 36 3 3 2 8 1 1 12 */
+     933,    /* OBJ_brainpoolP512r1              1 3 36 3 3 2 8 1 1 13 */
+     934,    /* OBJ_brainpoolP512t1              1 3 36 3 3 2 8 1 1 14 */
+     936,    /* OBJ_dhSinglePass_stdDH_sha1kdf_scheme 1 3 133 16 840 63 0 2 */
+     941,    /* OBJ_dhSinglePass_cofactorDH_sha1kdf_scheme 1 3 133 16 840 63 0 3 */
+     418,    /* OBJ_aes_128_ecb                  2 16 840 1 101 3 4 1 1 */
+     419,    /* OBJ_aes_128_cbc                  2 16 840 1 101 3 4 1 2 */
+     420,    /* OBJ_aes_128_ofb128               2 16 840 1 101 3 4 1 3 */
+     421,    /* OBJ_aes_128_cfb128               2 16 840 1 101 3 4 1 4 */
+     788,    /* OBJ_id_aes128_wrap               2 16 840 1 101 3 4 1 5 */
+     895,    /* OBJ_aes_128_gcm                  2 16 840 1 101 3 4 1 6 */
+     896,    /* OBJ_aes_128_ccm                  2 16 840 1 101 3 4 1 7 */
+     897,    /* OBJ_id_aes128_wrap_pad           2 16 840 1 101 3 4 1 8 */
+     422,    /* OBJ_aes_192_ecb                  2 16 840 1 101 3 4 1 21 */
+     423,    /* OBJ_aes_192_cbc                  2 16 840 1 101 3 4 1 22 */
+     424,    /* OBJ_aes_192_ofb128               2 16 840 1 101 3 4 1 23 */
+     425,    /* OBJ_aes_192_cfb128               2 16 840 1 101 3 4 1 24 */
+     789,    /* OBJ_id_aes192_wrap               2 16 840 1 101 3 4 1 25 */
+     898,    /* OBJ_aes_192_gcm                  2 16 840 1 101 3 4 1 26 */
+     899,    /* OBJ_aes_192_ccm                  2 16 840 1 101 3 4 1 27 */
+     900,    /* OBJ_id_aes192_wrap_pad           2 16 840 1 101 3 4 1 28 */
+     426,    /* OBJ_aes_256_ecb                  2 16 840 1 101 3 4 1 41 */
+     427,    /* OBJ_aes_256_cbc                  2 16 840 1 101 3 4 1 42 */
+     428,    /* OBJ_aes_256_ofb128               2 16 840 1 101 3 4 1 43 */
+     429,    /* OBJ_aes_256_cfb128               2 16 840 1 101 3 4 1 44 */
+     790,    /* OBJ_id_aes256_wrap               2 16 840 1 101 3 4 1 45 */
+     901,    /* OBJ_aes_256_gcm                  2 16 840 1 101 3 4 1 46 */
+     902,    /* OBJ_aes_256_ccm                  2 16 840 1 101 3 4 1 47 */
+     903,    /* OBJ_id_aes256_wrap_pad           2 16 840 1 101 3 4 1 48 */
+     672,    /* OBJ_sha256                       2 16 840 1 101 3 4 2 1 */
+     673,    /* OBJ_sha384                       2 16 840 1 101 3 4 2 2 */
+     674,    /* OBJ_sha512                       2 16 840 1 101 3 4 2 3 */
+     675,    /* OBJ_sha224                       2 16 840 1 101 3 4 2 4 */
+     802,    /* OBJ_dsa_with_SHA224              2 16 840 1 101 3 4 3 1 */
+     803,    /* OBJ_dsa_with_SHA256              2 16 840 1 101 3 4 3 2 */
+      71,    /* OBJ_netscape_cert_type           2 16 840 1 113730 1 1 */
+      72,    /* OBJ_netscape_base_url            2 16 840 1 113730 1 2 */
+      73,    /* OBJ_netscape_revocation_url      2 16 840 1 113730 1 3 */
+      74,    /* OBJ_netscape_ca_revocation_url   2 16 840 1 113730 1 4 */
+      75,    /* OBJ_netscape_renewal_url         2 16 840 1 113730 1 7 */
+      76,    /* OBJ_netscape_ca_policy_url       2 16 840 1 113730 1 8 */
+      77,    /* OBJ_netscape_ssl_server_name     2 16 840 1 113730 1 12 */
+      78,    /* OBJ_netscape_comment             2 16 840 1 113730 1 13 */
+      79,    /* OBJ_netscape_cert_sequence       2 16 840 1 113730 2 5 */
+     139,    /* OBJ_ns_sgc                       2 16 840 1 113730 4 1 */
+     458,    /* OBJ_userId                       0 9 2342 19200300 100 1 1 */
+     459,    /* OBJ_textEncodedORAddress         0 9 2342 19200300 100 1 2 */
+     460,    /* OBJ_rfc822Mailbox                0 9 2342 19200300 100 1 3 */
+     461,    /* OBJ_info                         0 9 2342 19200300 100 1 4 */
+     462,    /* OBJ_favouriteDrink               0 9 2342 19200300 100 1 5 */
+     463,    /* OBJ_roomNumber                   0 9 2342 19200300 100 1 6 */
+     464,    /* OBJ_photo                        0 9 2342 19200300 100 1 7 */
+     465,    /* OBJ_userClass                    0 9 2342 19200300 100 1 8 */
+     466,    /* OBJ_host                         0 9 2342 19200300 100 1 9 */
+     467,    /* OBJ_manager                      0 9 2342 19200300 100 1 10 */
+     468,    /* OBJ_documentIdentifier           0 9 2342 19200300 100 1 11 */
+     469,    /* OBJ_documentTitle                0 9 2342 19200300 100 1 12 */
+     470,    /* OBJ_documentVersion              0 9 2342 19200300 100 1 13 */
+     471,    /* OBJ_documentAuthor               0 9 2342 19200300 100 1 14 */
+     472,    /* OBJ_documentLocation             0 9 2342 19200300 100 1 15 */
+     473,    /* OBJ_homeTelephoneNumber          0 9 2342 19200300 100 1 20 */
+     474,    /* OBJ_secretary                    0 9 2342 19200300 100 1 21 */
+     475,    /* OBJ_otherMailbox                 0 9 2342 19200300 100 1 22 */
+     476,    /* OBJ_lastModifiedTime             0 9 2342 19200300 100 1 23 */
+     477,    /* OBJ_lastModifiedBy               0 9 2342 19200300 100 1 24 */
+     391,    /* OBJ_domainComponent              0 9 2342 19200300 100 1 25 */
+     478,    /* OBJ_aRecord                      0 9 2342 19200300 100 1 26 */
+     479,    /* OBJ_pilotAttributeType27         0 9 2342 19200300 100 1 27 */
+     480,    /* OBJ_mXRecord                     0 9 2342 19200300 100 1 28 */
+     481,    /* OBJ_nSRecord                     0 9 2342 19200300 100 1 29 */
+     482,    /* OBJ_sOARecord                    0 9 2342 19200300 100 1 30 */
+     483,    /* OBJ_cNAMERecord                  0 9 2342 19200300 100 1 31 */
+     484,    /* OBJ_associatedDomain             0 9 2342 19200300 100 1 37 */
+     485,    /* OBJ_associatedName               0 9 2342 19200300 100 1 38 */
+     486,    /* OBJ_homePostalAddress            0 9 2342 19200300 100 1 39 */
+     487,    /* OBJ_personalTitle                0 9 2342 19200300 100 1 40 */
+     488,    /* OBJ_mobileTelephoneNumber        0 9 2342 19200300 100 1 41 */
+     489,    /* OBJ_pagerTelephoneNumber         0 9 2342 19200300 100 1 42 */
+     490,    /* OBJ_friendlyCountryName          0 9 2342 19200300 100 1 43 */
+     102,    /* OBJ_uniqueIdentifier             0 9 2342 19200300 100 1 44 */
+     491,    /* OBJ_organizationalStatus         0 9 2342 19200300 100 1 45 */
+     492,    /* OBJ_janetMailbox                 0 9 2342 19200300 100 1 46 */
+     493,    /* OBJ_mailPreferenceOption         0 9 2342 19200300 100 1 47 */
+     494,    /* OBJ_buildingName                 0 9 2342 19200300 100 1 48 */
+     495,    /* OBJ_dSAQuality                   0 9 2342 19200300 100 1 49 */
+     496,    /* OBJ_singleLevelQuality           0 9 2342 19200300 100 1 50 */
+     497,    /* OBJ_subtreeMinimumQuality        0 9 2342 19200300 100 1 51 */
+     498,    /* OBJ_subtreeMaximumQuality        0 9 2342 19200300 100 1 52 */
+     499,    /* OBJ_personalSignature            0 9 2342 19200300 100 1 53 */
+     500,    /* OBJ_dITRedirect                  0 9 2342 19200300 100 1 54 */
+     501,    /* OBJ_audio                        0 9 2342 19200300 100 1 55 */
+     502,    /* OBJ_documentPublisher            0 9 2342 19200300 100 1 56 */
+     442,    /* OBJ_iA5StringSyntax              0 9 2342 19200300 100 3 4 */
+     443,    /* OBJ_caseIgnoreIA5StringSyntax    0 9 2342 19200300 100 3 5 */
+     444,    /* OBJ_pilotObject                  0 9 2342 19200300 100 4 3 */
+     445,    /* OBJ_pilotPerson                  0 9 2342 19200300 100 4 4 */
+     446,    /* OBJ_account                      0 9 2342 19200300 100 4 5 */
+     447,    /* OBJ_document                     0 9 2342 19200300 100 4 6 */
+     448,    /* OBJ_room                         0 9 2342 19200300 100 4 7 */
+     449,    /* OBJ_documentSeries               0 9 2342 19200300 100 4 9 */
+     392,    /* OBJ_Domain                       0 9 2342 19200300 100 4 13 */
+     450,    /* OBJ_rFC822localPart              0 9 2342 19200300 100 4 14 */
+     451,    /* OBJ_dNSDomain                    0 9 2342 19200300 100 4 15 */
+     452,    /* OBJ_domainRelatedObject          0 9 2342 19200300 100 4 17 */
+     453,    /* OBJ_friendlyCountry              0 9 2342 19200300 100 4 18 */
+     454,    /* OBJ_simpleSecurityObject         0 9 2342 19200300 100 4 19 */
+     455,    /* OBJ_pilotOrganization            0 9 2342 19200300 100 4 20 */
+     456,    /* OBJ_pilotDSA                     0 9 2342 19200300 100 4 21 */
+     457,    /* OBJ_qualityLabelledData          0 9 2342 19200300 100 4 22 */
+    1135,    /* OBJ_sm2encrypt_recommendedParameters 1 2 156 10197 1 301 3 1 */
+    1136,    /* OBJ_sm2encrypt_specifiedParameters 1 2 156 10197 1 301 3 2 */
+    1176,    /* OBJ_sm9sign_with_sm3             1 2 156 10197 1 302 1 1 */
+    1177,    /* OBJ_sm9sign_with_sha256          1 2 156 10197 1 302 1 2 */
+    1178,    /* OBJ_sm9encrypt_with_sm3_xor      1 2 156 10197 1 302 3 1 */
+    1179,    /* OBJ_sm9encrypt_with_sm3_sms4_cbc 1 2 156 10197 1 302 3 2 */
+    1180,    /* OBJ_sm9encrypt_with_sm3_sms4_ctr 1 2 156 10197 1 302 3 3 */
+    1181,    /* OBJ_sm9hash1_with_sm3            1 2 156 10197 1 302 4 1 */
+    1182,    /* OBJ_sm9hash1_with_sha256         1 2 156 10197 1 302 4 2 */
+    1183,    /* OBJ_sm9kdf_with_sm3              1 2 156 10197 1 302 5 1 */
+    1184,    /* OBJ_sm9kdf_with_sha256           1 2 156 10197 1 302 5 2 */
+    1175,    /* OBJ_sm9bn256v1                   1 2 156 10197 1 302 6 1 */
+    1210,    /* OBJ_sm9hash2_with_sm3            1 2 156 10197 1 303 7 1 */
+    1211,    /* OBJ_sm9hash2_with_sha256         1 2 156 10197 1 303 7 2 */
+    1215,    /* OBJ_zuc256_mac32                 1 2 156 10197 1 800 5 1 */
+    1216,    /* OBJ_zuc256_mac64                 1 2 156 10197 1 800 5 2 */
+    1217,    /* OBJ_zuc256_mac128                1 2 156 10197 1 800 5 3 */
+     189,    /* OBJ_id_smime_mod                 1 2 840 113549 1 9 16 0 */
+     190,    /* OBJ_id_smime_ct                  1 2 840 113549 1 9 16 1 */
+     191,    /* OBJ_id_smime_aa                  1 2 840 113549 1 9 16 2 */
+     192,    /* OBJ_id_smime_alg                 1 2 840 113549 1 9 16 3 */
+     193,    /* OBJ_id_smime_cd                  1 2 840 113549 1 9 16 4 */
+     194,    /* OBJ_id_smime_spq                 1 2 840 113549 1 9 16 5 */
+     195,    /* OBJ_id_smime_cti                 1 2 840 113549 1 9 16 6 */
+     158,    /* OBJ_x509Certificate              1 2 840 113549 1 9 22 1 */
+     159,    /* OBJ_sdsiCertificate              1 2 840 113549 1 9 22 2 */
+     160,    /* OBJ_x509Crl                      1 2 840 113549 1 9 23 1 */
+     144,    /* OBJ_pbe_WithSHA1And128BitRC4     1 2 840 113549 1 12 1 1 */
+     145,    /* OBJ_pbe_WithSHA1And40BitRC4      1 2 840 113549 1 12 1 2 */
+     146,    /* OBJ_pbe_WithSHA1And3_Key_TripleDES_CBC 1 2 840 113549 1 12 1 3 */
+     147,    /* OBJ_pbe_WithSHA1And2_Key_TripleDES_CBC 1 2 840 113549 1 12 1 4 */
+     148,    /* OBJ_pbe_WithSHA1And128BitRC2_CBC 1 2 840 113549 1 12 1 5 */
+     149,    /* OBJ_pbe_WithSHA1And40BitRC2_CBC  1 2 840 113549 1 12 1 6 */
+    1083,    /* OBJ_pbe_WithSM3AndSMS4_CBC       1 2 840 113549 1 12 1 100 */
+     171,    /* OBJ_ms_ext_req                   1 3 6 1 4 1 311 2 1 14 */
+     134,    /* OBJ_ms_code_ind                  1 3 6 1 4 1 311 2 1 21 */
+     135,    /* OBJ_ms_code_com                  1 3 6 1 4 1 311 2 1 22 */
+     136,    /* OBJ_ms_ctl_sign                  1 3 6 1 4 1 311 10 3 1 */
+     137,    /* OBJ_ms_sgc                       1 3 6 1 4 1 311 10 3 3 */
+     138,    /* OBJ_ms_efs                       1 3 6 1 4 1 311 10 3 4 */
+     648,    /* OBJ_ms_smartcard_login           1 3 6 1 4 1 311 20 2 2 */
+     649,    /* OBJ_ms_upn                       1 3 6 1 4 1 311 20 2 3 */
+    1202,    /* OBJ_cpk_map                      1 3 6 1 4 1 49549 1 1 */
+     951,    /* OBJ_ct_precert_scts              1 3 6 1 4 1 11129 2 4 2 */
+     952,    /* OBJ_ct_precert_poison            1 3 6 1 4 1 11129 2 4 3 */
+     953,    /* OBJ_ct_precert_signer            1 3 6 1 4 1 11129 2 4 4 */
+     954,    /* OBJ_ct_cert_scts                 1 3 6 1 4 1 11129 2 4 5 */
+    1137,    /* OBJ_sm2encrypt_with_sm3          1 2 156 10197 1 301 3 2 1 */
+    1138,    /* OBJ_sm2encrypt_with_sha1         1 2 156 10197 1 301 3 2 2 */
+    1139,    /* OBJ_sm2encrypt_with_sha224       1 2 156 10197 1 301 3 2 3 */
+    1140,    /* OBJ_sm2encrypt_with_sha256       1 2 156 10197 1 301 3 2 4 */
+    1141,    /* OBJ_sm2encrypt_with_sha384       1 2 156 10197 1 301 3 2 5 */
+    1142,    /* OBJ_sm2encrypt_with_sha512       1 2 156 10197 1 301 3 2 6 */
+    1143,    /* OBJ_sm2encrypt_with_rmd160       1 2 156 10197 1 301 3 2 7 */
+    1144,    /* OBJ_sm2encrypt_with_whirlpool    1 2 156 10197 1 301 3 2 8 */
+    1145,    /* OBJ_sm2encrypt_with_blake2b512   1 2 156 10197 1 301 3 2 9 */
+    1146,    /* OBJ_sm2encrypt_with_blake2s256   1 2 156 10197 1 301 3 2 10 */
+    1147,    /* OBJ_sm2encrypt_with_md5          1 2 156 10197 1 301 3 2 11 */
+     751,    /* OBJ_camellia_128_cbc             1 2 392 200011 61 1 1 1 2 */
+     752,    /* OBJ_camellia_192_cbc             1 2 392 200011 61 1 1 1 3 */
+     753,    /* OBJ_camellia_256_cbc             1 2 392 200011 61 1 1 1 4 */
+     907,    /* OBJ_id_camellia128_wrap          1 2 392 200011 61 1 1 3 2 */
+     908,    /* OBJ_id_camellia192_wrap          1 2 392 200011 61 1 1 3 3 */
+     909,    /* OBJ_id_camellia256_wrap          1 2 392 200011 61 1 1 3 4 */
+    1102,    /* OBJ_type1curve                   1 2 840 1 114334 1 1 1 1 */
+    1103,    /* OBJ_bfibe                        1 2 840 1 114334 1 1 2 1 */
+    1104,    /* OBJ_bb1ibe                       1 2 840 1 114334 1 1 2 2 */
+     196,    /* OBJ_id_smime_mod_cms             1 2 840 113549 1 9 16 0 1 */
+     197,    /* OBJ_id_smime_mod_ess             1 2 840 113549 1 9 16 0 2 */
+     198,    /* OBJ_id_smime_mod_oid             1 2 840 113549 1 9 16 0 3 */
+     199,    /* OBJ_id_smime_mod_msg_v3          1 2 840 113549 1 9 16 0 4 */
+     200,    /* OBJ_id_smime_mod_ets_eSignature_88 1 2 840 113549 1 9 16 0 5 */
+     201,    /* OBJ_id_smime_mod_ets_eSignature_97 1 2 840 113549 1 9 16 0 6 */
+     202,    /* OBJ_id_smime_mod_ets_eSigPolicy_88 1 2 840 113549 1 9 16 0 7 */
+     203,    /* OBJ_id_smime_mod_ets_eSigPolicy_97 1 2 840 113549 1 9 16 0 8 */
+     204,    /* OBJ_id_smime_ct_receipt          1 2 840 113549 1 9 16 1 1 */
+     205,    /* OBJ_id_smime_ct_authData         1 2 840 113549 1 9 16 1 2 */
+     206,    /* OBJ_id_smime_ct_publishCert      1 2 840 113549 1 9 16 1 3 */
+     207,    /* OBJ_id_smime_ct_TSTInfo          1 2 840 113549 1 9 16 1 4 */
+     208,    /* OBJ_id_smime_ct_TDTInfo          1 2 840 113549 1 9 16 1 5 */
+     209,    /* OBJ_id_smime_ct_contentInfo      1 2 840 113549 1 9 16 1 6 */
+     210,    /* OBJ_id_smime_ct_DVCSRequestData  1 2 840 113549 1 9 16 1 7 */
+     211,    /* OBJ_id_smime_ct_DVCSResponseData 1 2 840 113549 1 9 16 1 8 */
+     786,    /* OBJ_id_smime_ct_compressedData   1 2 840 113549 1 9 16 1 9 */
+    1058,    /* OBJ_id_smime_ct_contentCollection 1 2 840 113549 1 9 16 1 19 */
+    1059,    /* OBJ_id_smime_ct_authEnvelopedData 1 2 840 113549 1 9 16 1 23 */
+     787,    /* OBJ_id_ct_asciiTextWithCRLF      1 2 840 113549 1 9 16 1 27 */
+    1060,    /* OBJ_id_ct_xml                    1 2 840 113549 1 9 16 1 28 */
+     212,    /* OBJ_id_smime_aa_receiptRequest   1 2 840 113549 1 9 16 2 1 */
+     213,    /* OBJ_id_smime_aa_securityLabel    1 2 840 113549 1 9 16 2 2 */
+     214,    /* OBJ_id_smime_aa_mlExpandHistory  1 2 840 113549 1 9 16 2 3 */
+     215,    /* OBJ_id_smime_aa_contentHint      1 2 840 113549 1 9 16 2 4 */
+     216,    /* OBJ_id_smime_aa_msgSigDigest     1 2 840 113549 1 9 16 2 5 */
+     217,    /* OBJ_id_smime_aa_encapContentType 1 2 840 113549 1 9 16 2 6 */
+     218,    /* OBJ_id_smime_aa_contentIdentifier 1 2 840 113549 1 9 16 2 7 */
+     219,    /* OBJ_id_smime_aa_macValue         1 2 840 113549 1 9 16 2 8 */
+     220,    /* OBJ_id_smime_aa_equivalentLabels 1 2 840 113549 1 9 16 2 9 */
+     221,    /* OBJ_id_smime_aa_contentReference 1 2 840 113549 1 9 16 2 10 */
+     222,    /* OBJ_id_smime_aa_encrypKeyPref    1 2 840 113549 1 9 16 2 11 */
+     223,    /* OBJ_id_smime_aa_signingCertificate 1 2 840 113549 1 9 16 2 12 */
+     224,    /* OBJ_id_smime_aa_smimeEncryptCerts 1 2 840 113549 1 9 16 2 13 */
+     225,    /* OBJ_id_smime_aa_timeStampToken   1 2 840 113549 1 9 16 2 14 */
+     226,    /* OBJ_id_smime_aa_ets_sigPolicyId  1 2 840 113549 1 9 16 2 15 */
+     227,    /* OBJ_id_smime_aa_ets_commitmentType 1 2 840 113549 1 9 16 2 16 */
+     228,    /* OBJ_id_smime_aa_ets_signerLocation 1 2 840 113549 1 9 16 2 17 */
+     229,    /* OBJ_id_smime_aa_ets_signerAttr   1 2 840 113549 1 9 16 2 18 */
+     230,    /* OBJ_id_smime_aa_ets_otherSigCert 1 2 840 113549 1 9 16 2 19 */
+     231,    /* OBJ_id_smime_aa_ets_contentTimestamp 1 2 840 113549 1 9 16 2 20 */
+     232,    /* OBJ_id_smime_aa_ets_CertificateRefs 1 2 840 113549 1 9 16 2 21 */
+     233,    /* OBJ_id_smime_aa_ets_RevocationRefs 1 2 840 113549 1 9 16 2 22 */
+     234,    /* OBJ_id_smime_aa_ets_certValues   1 2 840 113549 1 9 16 2 23 */
+     235,    /* OBJ_id_smime_aa_ets_revocationValues 1 2 840 113549 1 9 16 2 24 */
+     236,    /* OBJ_id_smime_aa_ets_escTimeStamp 1 2 840 113549 1 9 16 2 25 */
+     237,    /* OBJ_id_smime_aa_ets_certCRLTimestamp 1 2 840 113549 1 9 16 2 26 */
+     238,    /* OBJ_id_smime_aa_ets_archiveTimeStamp 1 2 840 113549 1 9 16 2 27 */
+     239,    /* OBJ_id_smime_aa_signatureType    1 2 840 113549 1 9 16 2 28 */
+     240,    /* OBJ_id_smime_aa_dvcs_dvc         1 2 840 113549 1 9 16 2 29 */
+     241,    /* OBJ_id_smime_alg_ESDHwith3DES    1 2 840 113549 1 9 16 3 1 */
+     242,    /* OBJ_id_smime_alg_ESDHwithRC2     1 2 840 113549 1 9 16 3 2 */
+     243,    /* OBJ_id_smime_alg_3DESwrap        1 2 840 113549 1 9 16 3 3 */
+     244,    /* OBJ_id_smime_alg_RC2wrap         1 2 840 113549 1 9 16 3 4 */
+     245,    /* OBJ_id_smime_alg_ESDH            1 2 840 113549 1 9 16 3 5 */
+     246,    /* OBJ_id_smime_alg_CMS3DESwrap     1 2 840 113549 1 9 16 3 6 */
+     247,    /* OBJ_id_smime_alg_CMSRC2wrap      1 2 840 113549 1 9 16 3 7 */
+     125,    /* OBJ_zlib_compression             1 2 840 113549 1 9 16 3 8 */
+     893,    /* OBJ_id_alg_PWRI_KEK              1 2 840 113549 1 9 16 3 9 */
+     248,    /* OBJ_id_smime_cd_ldap             1 2 840 113549 1 9 16 4 1 */
+     249,    /* OBJ_id_smime_spq_ets_sqt_uri     1 2 840 113549 1 9 16 5 1 */
+     250,    /* OBJ_id_smime_spq_ets_sqt_unotice 1 2 840 113549 1 9 16 5 2 */
+     251,    /* OBJ_id_smime_cti_ets_proofOfOrigin 1 2 840 113549 1 9 16 6 1 */
+     252,    /* OBJ_id_smime_cti_ets_proofOfReceipt 1 2 840 113549 1 9 16 6 2 */
+     253,    /* OBJ_id_smime_cti_ets_proofOfDelivery 1 2 840 113549 1 9 16 6 3 */
+     254,    /* OBJ_id_smime_cti_ets_proofOfSender 1 2 840 113549 1 9 16 6 4 */
+     255,    /* OBJ_id_smime_cti_ets_proofOfApproval 1 2 840 113549 1 9 16 6 5 */
+     256,    /* OBJ_id_smime_cti_ets_proofOfCreation 1 2 840 113549 1 9 16 6 6 */
+     150,    /* OBJ_keyBag                       1 2 840 113549 1 12 10 1 1 */
+     151,    /* OBJ_pkcs8ShroudedKeyBag          1 2 840 113549 1 12 10 1 2 */
+     152,    /* OBJ_certBag                      1 2 840 113549 1 12 10 1 3 */
+     153,    /* OBJ_crlBag                       1 2 840 113549 1 12 10 1 4 */
+     154,    /* OBJ_secretBag                    1 2 840 113549 1 12 10 1 5 */
+     155,    /* OBJ_safeContentsBag              1 2 840 113549 1 12 10 1 6 */
+      34,    /* OBJ_idea_cbc                     1 3 6 1 4 1 188 7 1 1 2 */
+     955,    /* OBJ_jurisdictionLocalityName     1 3 6 1 4 1 311 60 2 1 1 */
+     956,    /* OBJ_jurisdictionStateOrProvinceName 1 3 6 1 4 1 311 60 2 1 2 */
+     957,    /* OBJ_jurisdictionCountryName      1 3 6 1 4 1 311 60 2 1 3 */
+    1203,    /* OBJ_cpk_map_sha1                 1 3 6 1 4 1 49549 1 1 1 */
+    1204,    /* OBJ_cpk_map_sha256               1 3 6 1 4 1 49549 1 1 2 */
+    1205,    /* OBJ_cpk_map_sm3                  1 3 6 1 4 1 49549 1 1 3 */
+    1206,    /* OBJ_cpk_map_sha384               1 3 6 1 4 1 49549 1 1 4 */
+    1207,    /* OBJ_cpk_map_sha512               1 3 6 1 4 1 49549 1 1 5 */
+    1056,    /* OBJ_blake2b512                   1 3 6 1 4 1 1722 12 2 1 16 */
+    1057,    /* OBJ_blake2s256                   1 3 6 1 4 1 1722 12 2 2 8 */
+};
diff --git a/crypto/objects/obj_dat.pl b/crypto/objects/obj_dat.pl
new file mode 100644
index 000000000..1cb3d1c9a
--- /dev/null
+++ b/crypto/objects/obj_dat.pl
@@ -0,0 +1,227 @@
+#! /usr/bin/env perl
+# Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+use integer;
+use strict;
+use warnings;
+
+# Generate the DER encoding for the given OID.
+sub der_it
+{
+    # Prologue
+    my ($v) = @_;
+    my @a = split(/\s+/, $v);
+    my $ret = pack("C*", $a[0] * 40 + $a[1]);
+    shift @a;
+    shift @a;
+
+    # Loop over rest of bytes; or in 0x80 for multi-byte numbers.
+    my $t;
+    foreach (@a) {
+        my @r = ();
+        $t = 0;
+        while ($_ >= 128) {
+            my $x = $_ % 128;
+            $_ /= 128;
+            push(@r, ($t++ ? 0x80 : 0) | $x);
+        }
+        push(@r, ($t++ ? 0x80 : 0) | $_);
+        $ret .= pack("C*", reverse(@r));
+    }
+    return $ret;
+}
+
+
+# Read input, parse all #define's into OID name and value.
+# Populate %ln and %sn with long and short names (%dupln and %dupsn)
+# are used to watch for duplicates.  Also %nid and %obj get the
+# NID and OBJ entries.
+my %ln;
+my %sn;
+my %dupln;
+my %dupsn;
+my %nid;
+my %obj;
+my %objd;
+open(IN, "$ARGV[0]") || die "Can't open input file $ARGV[0], $!";
+while () {
+    next unless /^\#define\s+(\S+)\s+(.*)$/;
+    my $v = $1;
+    my $d = $2;
+    $d =~ s/^\"//;
+    $d =~ s/\"$//;
+    if ($v =~ /^SN_(.*)$/) {
+        if (defined $dupsn{$d}) {
+            print "WARNING: Duplicate short name \"$d\"\n";
+        } else {
+            $dupsn{$d} = 1;
+        }
+        $sn{$1} = $d;
+    }
+    elsif ($v =~ /^LN_(.*)$/) {
+        if (defined $dupln{$d}) {
+            print "WARNING: Duplicate long name \"$d\"\n";
+        } else {
+            $dupln{$d} = 1;
+        }
+        $ln{$1} = $d;
+    }
+    elsif ($v =~ /^NID_(.*)$/) {
+        $nid{$d} = $1;
+    }
+    elsif ($v =~ /^OBJ_(.*)$/) {
+        $obj{$1} = $v;
+        $objd{$v} = $d;
+    }
+}
+close IN;
+
+# For every value in %obj, recursively expand OBJ_xxx values.  That is:
+#     #define OBJ_iso 1L
+#     #define OBJ_identified_organization OBJ_iso,3L
+# Modify %objd values in-place.  Create an %objn array that has
+my $changed;
+do {
+    $changed = 0;
+    foreach my $k (keys %objd) {
+        $changed = 1 if $objd{$k} =~ s/(OBJ_[^,]+),/$objd{$1},/;
+    }
+} while ($changed);
+
+my @a = sort { $a <=> $b } keys %nid;
+my $n = $a[$#a] + 1;
+my @lvalues = ();
+my $lvalues = 0;
+
+# Scan all defined objects, building up the @out array.
+# %obj_der holds the DER encoding as an array of bytes, and %obj_len
+# holds the length in bytes.
+my @out;
+my %obj_der;
+my %obj_len;
+for (my $i = 0; $i < $n; $i++) {
+    if (!defined $nid{$i}) {
+        push(@out, "    { NULL, NULL, NID_undef },\n");
+        next;
+    }
+
+    my $sn = defined $sn{$nid{$i}} ? "$sn{$nid{$i}}" : "NULL";
+    my $ln = defined $ln{$nid{$i}} ? "$ln{$nid{$i}}" : "NULL";
+    if ($sn eq "NULL") {
+        $sn = $ln;
+        $sn{$nid{$i}} = $ln;
+    }
+    if ($ln eq "NULL") {
+        $ln = $sn;
+        $ln{$nid{$i}} = $sn;
+    }
+
+    my $out = "    {\"$sn\", \"$ln\", NID_$nid{$i}";
+    if (defined $obj{$nid{$i}} && $objd{$obj{$nid{$i}}} =~ /,/) {
+        my $v = $objd{$obj{$nid{$i}}};
+        $v =~ s/L//g;
+        $v =~ s/,/ /g;
+        my $r = &der_it($v);
+        my $z = "";
+        my $length = 0;
+        # Format using fixed-with because we use strcmp later.
+        foreach (unpack("C*",$r)) {
+            $z .= sprintf("0x%02X,", $_);
+            $length++;
+        }
+        $obj_der{$obj{$nid{$i}}} = $z;
+        $obj_len{$obj{$nid{$i}}} = $length;
+
+        push(@lvalues,
+            sprintf("    %-45s  /* [%5d] %s */\n",
+                $z, $lvalues, $obj{$nid{$i}}));
+        $out .= ", $length, &so[$lvalues]";
+        $lvalues += $length;
+    }
+    $out .= "},\n";
+    push(@out, $out);
+}
+
+# Finally ready to generate the output.
+open(OUT, ">$ARGV[1]") || die "Can't open output file $ARGV[1], $!";
+print OUT <<'EOF';
+/*
+ * WARNING: do not edit!
+ * Generated by crypto/objects/obj_dat.pl
+ *
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+EOF
+
+print OUT "/* Serialized OID's */\n";
+printf OUT "static const unsigned char so[%d] = {\n", $lvalues + 1;
+print OUT @lvalues;
+print OUT "};\n\n";
+
+printf OUT "#define NUM_NID %d\n", $n;
+printf OUT "static const ASN1_OBJECT nid_objs[NUM_NID] = {\n";
+print OUT @out;
+print  OUT "};\n\n";
+
+{
+    no warnings "uninitialized";
+    @a = grep(defined $sn{$nid{$_}}, 0 .. $n);
+}
+printf OUT "#define NUM_SN %d\n", $#a + 1;
+printf OUT "static const unsigned int sn_objs[NUM_SN] = {\n";
+foreach (sort { $sn{$nid{$a}} cmp $sn{$nid{$b}} } @a) {
+    printf OUT "    %4d,    /* \"$sn{$nid{$_}}\" */\n", $_;
+}
+print  OUT "};\n\n";
+
+{
+    no warnings "uninitialized";
+    @a = grep(defined $ln{$nid{$_}}, 0 .. $n);
+}
+printf OUT "#define NUM_LN %d\n", $#a + 1;
+printf OUT "static const unsigned int ln_objs[NUM_LN] = {\n";
+foreach (sort { $ln{$nid{$a}} cmp $ln{$nid{$b}} } @a) {
+    printf OUT "    %4d,    /* \"$ln{$nid{$_}}\" */\n", $_;
+}
+print  OUT "};\n\n";
+
+{
+    no warnings "uninitialized";
+    @a = grep(defined $obj{$nid{$_}}, 0 .. $n);
+}
+printf OUT "#define NUM_OBJ %d\n", $#a + 1;
+printf OUT "static const unsigned int obj_objs[NUM_OBJ] = {\n";
+
+# Compare DER; prefer shorter; if some length, use the "smaller" encoding.
+sub obj_cmp
+{
+    no warnings "uninitialized";
+    my $A = $obj_len{$obj{$nid{$a}}};
+    my $B = $obj_len{$obj{$nid{$b}}};
+    my $r = $A - $B;
+    return $r if $r != 0;
+
+    $A = $obj_der{$obj{$nid{$a}}};
+    $B = $obj_der{$obj{$nid{$b}}};
+    return $A cmp $B;
+}
+foreach (sort obj_cmp @a) {
+    my $m = $obj{$nid{$_}};
+    my $v = $objd{$m};
+    $v =~ s/L//g;
+    $v =~ s/,/ /g;
+    printf OUT "    %4d,    /* %-32s %s */\n", $_, $m, $v;
+}
+print  OUT "};\n";
+
+close OUT;
diff --git a/crypto/objects/obj_err.c b/crypto/objects/obj_err.c
new file mode 100644
index 000000000..4677b6736
--- /dev/null
+++ b/crypto/objects/obj_err.c
@@ -0,0 +1,50 @@
+/*
+ * Generated by util/mkerr.pl DO NOT EDIT
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include 
+
+/* BEGIN ERROR CODES */
+#ifndef OPENSSL_NO_ERR
+
+# define ERR_FUNC(func) ERR_PACK(ERR_LIB_OBJ,func,0)
+# define ERR_REASON(reason) ERR_PACK(ERR_LIB_OBJ,0,reason)
+
+static ERR_STRING_DATA OBJ_str_functs[] = {
+    {ERR_FUNC(OBJ_F_OBJ_ADD_OBJECT), "OBJ_add_object"},
+    {ERR_FUNC(OBJ_F_OBJ_CREATE), "OBJ_create"},
+    {ERR_FUNC(OBJ_F_OBJ_DUP), "OBJ_dup"},
+    {ERR_FUNC(OBJ_F_OBJ_NAME_NEW_INDEX), "OBJ_NAME_new_index"},
+    {ERR_FUNC(OBJ_F_OBJ_NID2LN), "OBJ_nid2ln"},
+    {ERR_FUNC(OBJ_F_OBJ_NID2OBJ), "OBJ_nid2obj"},
+    {ERR_FUNC(OBJ_F_OBJ_NID2SN), "OBJ_nid2sn"},
+    {0, NULL}
+};
+
+static ERR_STRING_DATA OBJ_str_reasons[] = {
+    {ERR_REASON(OBJ_R_OID_EXISTS), "oid exists"},
+    {ERR_REASON(OBJ_R_UNKNOWN_NID), "unknown nid"},
+    {0, NULL}
+};
+
+#endif
+
+int ERR_load_OBJ_strings(void)
+{
+#ifndef OPENSSL_NO_ERR
+
+    if (ERR_func_error_string(OBJ_str_functs[0].error) == NULL) {
+        ERR_load_strings(0, OBJ_str_functs);
+        ERR_load_strings(0, OBJ_str_reasons);
+    }
+#endif
+    return 1;
+}
diff --git a/crypto/objects/obj_lcl.h b/crypto/objects/obj_lcl.h
new file mode 100644
index 000000000..a417f7c46
--- /dev/null
+++ b/crypto/objects/obj_lcl.h
@@ -0,0 +1,14 @@
+/*
+ * Copyright 2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+typedef struct name_funcs_st NAME_FUNCS;
+DEFINE_STACK_OF(NAME_FUNCS)
+DEFINE_LHASH_OF(OBJ_NAME);
+typedef struct added_obj_st ADDED_OBJ;
+DEFINE_LHASH_OF(ADDED_OBJ);
diff --git a/crypto/objects/obj_lib.c b/crypto/objects/obj_lib.c
new file mode 100644
index 000000000..33075e645
--- /dev/null
+++ b/crypto/objects/obj_lib.c
@@ -0,0 +1,66 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+#include 
+#include "internal/asn1_int.h"
+
+ASN1_OBJECT *OBJ_dup(const ASN1_OBJECT *o)
+{
+    ASN1_OBJECT *r;
+
+    if (o == NULL)
+        return NULL;
+    /* If object isn't dynamic it's an internal OID which is never freed */
+    if (!(o->flags & ASN1_OBJECT_FLAG_DYNAMIC))
+        return ((ASN1_OBJECT *)o);
+
+    r = ASN1_OBJECT_new();
+    if (r == NULL) {
+        OBJerr(OBJ_F_OBJ_DUP, ERR_R_ASN1_LIB);
+        return (NULL);
+    }
+
+    /* Set dynamic flags so everything gets freed up on error */
+
+    r->flags = o->flags | (ASN1_OBJECT_FLAG_DYNAMIC |
+                           ASN1_OBJECT_FLAG_DYNAMIC_STRINGS |
+                           ASN1_OBJECT_FLAG_DYNAMIC_DATA);
+
+    if (o->length > 0 && (r->data = OPENSSL_memdup(o->data, o->length)) == NULL)
+        goto err;
+
+    r->length = o->length;
+    r->nid = o->nid;
+
+    if (o->ln != NULL && (r->ln = OPENSSL_strdup(o->ln)) == NULL)
+        goto err;
+
+    if (o->sn != NULL && (r->sn = OPENSSL_strdup(o->sn)) == NULL)
+        goto err;
+
+    return r;
+ err:
+    ASN1_OBJECT_free(r);
+    OBJerr(OBJ_F_OBJ_DUP, ERR_R_MALLOC_FAILURE);
+    return NULL;
+}
+
+int OBJ_cmp(const ASN1_OBJECT *a, const ASN1_OBJECT *b)
+{
+    int ret;
+
+    ret = (a->length - b->length);
+    if (ret)
+        return (ret);
+    return (memcmp(a->data, b->data, a->length));
+}
diff --git a/crypto/objects/obj_mac.num b/crypto/objects/obj_mac.num
new file mode 100644
index 000000000..e8482a55f
--- /dev/null
+++ b/crypto/objects/obj_mac.num
@@ -0,0 +1,1217 @@
+undef		0
+rsadsi		1
+pkcs		2
+md2		3
+md5		4
+rc4		5
+rsaEncryption		6
+md2WithRSAEncryption		7
+md5WithRSAEncryption		8
+pbeWithMD2AndDES_CBC		9
+pbeWithMD5AndDES_CBC		10
+X500		11
+X509		12
+commonName		13
+countryName		14
+localityName		15
+stateOrProvinceName		16
+organizationName		17
+organizationalUnitName		18
+rsa		19
+pkcs7		20
+pkcs7_data		21
+pkcs7_signed		22
+pkcs7_enveloped		23
+pkcs7_signedAndEnveloped		24
+pkcs7_digest		25
+pkcs7_encrypted		26
+pkcs3		27
+dhKeyAgreement		28
+des_ecb		29
+des_cfb64		30
+des_cbc		31
+des_ede_ecb		32
+des_ede3_ecb		33
+idea_cbc		34
+idea_cfb64		35
+idea_ecb		36
+rc2_cbc		37
+rc2_ecb		38
+rc2_cfb64		39
+rc2_ofb64		40
+sha		41
+shaWithRSAEncryption		42
+des_ede_cbc		43
+des_ede3_cbc		44
+des_ofb64		45
+idea_ofb64		46
+pkcs9		47
+pkcs9_emailAddress		48
+pkcs9_unstructuredName		49
+pkcs9_contentType		50
+pkcs9_messageDigest		51
+pkcs9_signingTime		52
+pkcs9_countersignature		53
+pkcs9_challengePassword		54
+pkcs9_unstructuredAddress		55
+pkcs9_extCertAttributes		56
+netscape		57
+netscape_cert_extension		58
+netscape_data_type		59
+des_ede_cfb64		60
+des_ede3_cfb64		61
+des_ede_ofb64		62
+des_ede3_ofb64		63
+sha1		64
+sha1WithRSAEncryption		65
+dsaWithSHA		66
+dsa_2		67
+pbeWithSHA1AndRC2_CBC		68
+id_pbkdf2		69
+dsaWithSHA1_2		70
+netscape_cert_type		71
+netscape_base_url		72
+netscape_revocation_url		73
+netscape_ca_revocation_url		74
+netscape_renewal_url		75
+netscape_ca_policy_url		76
+netscape_ssl_server_name		77
+netscape_comment		78
+netscape_cert_sequence		79
+desx_cbc		80
+id_ce		81
+subject_key_identifier		82
+key_usage		83
+private_key_usage_period		84
+subject_alt_name		85
+issuer_alt_name		86
+basic_constraints		87
+crl_number		88
+certificate_policies		89
+authority_key_identifier		90
+bf_cbc		91
+bf_ecb		92
+bf_cfb64		93
+bf_ofb64		94
+mdc2		95
+mdc2WithRSA		96
+rc4_40		97
+rc2_40_cbc		98
+givenName		99
+surname		100
+initials		101
+uniqueIdentifier		102
+crl_distribution_points		103
+md5WithRSA		104
+serialNumber		105
+title		106
+description		107
+cast5_cbc		108
+cast5_ecb		109
+cast5_cfb64		110
+cast5_ofb64		111
+pbeWithMD5AndCast5_CBC		112
+dsaWithSHA1		113
+md5_sha1		114
+sha1WithRSA		115
+dsa		116
+ripemd160		117
+ripemd160WithRSA		119
+rc5_cbc		120
+rc5_ecb		121
+rc5_cfb64		122
+rc5_ofb64		123
+rle_compression		124
+zlib_compression		125
+ext_key_usage		126
+id_pkix		127
+id_kp		128
+server_auth		129
+client_auth		130
+code_sign		131
+email_protect		132
+time_stamp		133
+ms_code_ind		134
+ms_code_com		135
+ms_ctl_sign		136
+ms_sgc		137
+ms_efs		138
+ns_sgc		139
+delta_crl		140
+crl_reason		141
+invalidity_date		142
+sxnet		143
+pbe_WithSHA1And128BitRC4		144
+pbe_WithSHA1And40BitRC4		145
+pbe_WithSHA1And3_Key_TripleDES_CBC		146
+pbe_WithSHA1And2_Key_TripleDES_CBC		147
+pbe_WithSHA1And128BitRC2_CBC		148
+pbe_WithSHA1And40BitRC2_CBC		149
+keyBag		150
+pkcs8ShroudedKeyBag		151
+certBag		152
+crlBag		153
+secretBag		154
+safeContentsBag		155
+friendlyName		156
+localKeyID		157
+x509Certificate		158
+sdsiCertificate		159
+x509Crl		160
+pbes2		161
+pbmac1		162
+hmacWithSHA1		163
+id_qt_cps		164
+id_qt_unotice		165
+rc2_64_cbc		166
+SMIMECapabilities		167
+pbeWithMD2AndRC2_CBC		168
+pbeWithMD5AndRC2_CBC		169
+pbeWithSHA1AndDES_CBC		170
+ms_ext_req		171
+ext_req		172
+name		173
+dnQualifier		174
+id_pe		175
+id_ad		176
+info_access		177
+ad_OCSP		178
+ad_ca_issuers		179
+OCSP_sign		180
+iso		181
+member_body		182
+ISO_US		183
+X9_57		184
+X9cm		185
+pkcs1		186
+pkcs5		187
+SMIME		188
+id_smime_mod		189
+id_smime_ct		190
+id_smime_aa		191
+id_smime_alg		192
+id_smime_cd		193
+id_smime_spq		194
+id_smime_cti		195
+id_smime_mod_cms		196
+id_smime_mod_ess		197
+id_smime_mod_oid		198
+id_smime_mod_msg_v3		199
+id_smime_mod_ets_eSignature_88		200
+id_smime_mod_ets_eSignature_97		201
+id_smime_mod_ets_eSigPolicy_88		202
+id_smime_mod_ets_eSigPolicy_97		203
+id_smime_ct_receipt		204
+id_smime_ct_authData		205
+id_smime_ct_publishCert		206
+id_smime_ct_TSTInfo		207
+id_smime_ct_TDTInfo		208
+id_smime_ct_contentInfo		209
+id_smime_ct_DVCSRequestData		210
+id_smime_ct_DVCSResponseData		211
+id_smime_aa_receiptRequest		212
+id_smime_aa_securityLabel		213
+id_smime_aa_mlExpandHistory		214
+id_smime_aa_contentHint		215
+id_smime_aa_msgSigDigest		216
+id_smime_aa_encapContentType		217
+id_smime_aa_contentIdentifier		218
+id_smime_aa_macValue		219
+id_smime_aa_equivalentLabels		220
+id_smime_aa_contentReference		221
+id_smime_aa_encrypKeyPref		222
+id_smime_aa_signingCertificate		223
+id_smime_aa_smimeEncryptCerts		224
+id_smime_aa_timeStampToken		225
+id_smime_aa_ets_sigPolicyId		226
+id_smime_aa_ets_commitmentType		227
+id_smime_aa_ets_signerLocation		228
+id_smime_aa_ets_signerAttr		229
+id_smime_aa_ets_otherSigCert		230
+id_smime_aa_ets_contentTimestamp		231
+id_smime_aa_ets_CertificateRefs		232
+id_smime_aa_ets_RevocationRefs		233
+id_smime_aa_ets_certValues		234
+id_smime_aa_ets_revocationValues		235
+id_smime_aa_ets_escTimeStamp		236
+id_smime_aa_ets_certCRLTimestamp		237
+id_smime_aa_ets_archiveTimeStamp		238
+id_smime_aa_signatureType		239
+id_smime_aa_dvcs_dvc		240
+id_smime_alg_ESDHwith3DES		241
+id_smime_alg_ESDHwithRC2		242
+id_smime_alg_3DESwrap		243
+id_smime_alg_RC2wrap		244
+id_smime_alg_ESDH		245
+id_smime_alg_CMS3DESwrap		246
+id_smime_alg_CMSRC2wrap		247
+id_smime_cd_ldap		248
+id_smime_spq_ets_sqt_uri		249
+id_smime_spq_ets_sqt_unotice		250
+id_smime_cti_ets_proofOfOrigin		251
+id_smime_cti_ets_proofOfReceipt		252
+id_smime_cti_ets_proofOfDelivery		253
+id_smime_cti_ets_proofOfSender		254
+id_smime_cti_ets_proofOfApproval		255
+id_smime_cti_ets_proofOfCreation		256
+md4		257
+id_pkix_mod		258
+id_qt		259
+id_it		260
+id_pkip		261
+id_alg		262
+id_cmc		263
+id_on		264
+id_pda		265
+id_aca		266
+id_qcs		267
+id_cct		268
+id_pkix1_explicit_88		269
+id_pkix1_implicit_88		270
+id_pkix1_explicit_93		271
+id_pkix1_implicit_93		272
+id_mod_crmf		273
+id_mod_cmc		274
+id_mod_kea_profile_88		275
+id_mod_kea_profile_93		276
+id_mod_cmp		277
+id_mod_qualified_cert_88		278
+id_mod_qualified_cert_93		279
+id_mod_attribute_cert		280
+id_mod_timestamp_protocol		281
+id_mod_ocsp		282
+id_mod_dvcs		283
+id_mod_cmp2000		284
+biometricInfo		285
+qcStatements		286
+ac_auditEntity		287
+ac_targeting		288
+aaControls		289
+sbgp_ipAddrBlock		290
+sbgp_autonomousSysNum		291
+sbgp_routerIdentifier		292
+textNotice		293
+ipsecEndSystem		294
+ipsecTunnel		295
+ipsecUser		296
+dvcs		297
+id_it_caProtEncCert		298
+id_it_signKeyPairTypes		299
+id_it_encKeyPairTypes		300
+id_it_preferredSymmAlg		301
+id_it_caKeyUpdateInfo		302
+id_it_currentCRL		303
+id_it_unsupportedOIDs		304
+id_it_subscriptionRequest		305
+id_it_subscriptionResponse		306
+id_it_keyPairParamReq		307
+id_it_keyPairParamRep		308
+id_it_revPassphrase		309
+id_it_implicitConfirm		310
+id_it_confirmWaitTime		311
+id_it_origPKIMessage		312
+id_regCtrl		313
+id_regInfo		314
+id_regCtrl_regToken		315
+id_regCtrl_authenticator		316
+id_regCtrl_pkiPublicationInfo		317
+id_regCtrl_pkiArchiveOptions		318
+id_regCtrl_oldCertID		319
+id_regCtrl_protocolEncrKey		320
+id_regInfo_utf8Pairs		321
+id_regInfo_certReq		322
+id_alg_des40		323
+id_alg_noSignature		324
+id_alg_dh_sig_hmac_sha1		325
+id_alg_dh_pop		326
+id_cmc_statusInfo		327
+id_cmc_identification		328
+id_cmc_identityProof		329
+id_cmc_dataReturn		330
+id_cmc_transactionId		331
+id_cmc_senderNonce		332
+id_cmc_recipientNonce		333
+id_cmc_addExtensions		334
+id_cmc_encryptedPOP		335
+id_cmc_decryptedPOP		336
+id_cmc_lraPOPWitness		337
+id_cmc_getCert		338
+id_cmc_getCRL		339
+id_cmc_revokeRequest		340
+id_cmc_regInfo		341
+id_cmc_responseInfo		342
+id_cmc_queryPending		343
+id_cmc_popLinkRandom		344
+id_cmc_popLinkWitness		345
+id_cmc_confirmCertAcceptance		346
+id_on_personalData		347
+id_pda_dateOfBirth		348
+id_pda_placeOfBirth		349
+id_pda_pseudonym		350
+id_pda_gender		351
+id_pda_countryOfCitizenship		352
+id_pda_countryOfResidence		353
+id_aca_authenticationInfo		354
+id_aca_accessIdentity		355
+id_aca_chargingIdentity		356
+id_aca_group		357
+id_aca_role		358
+id_qcs_pkixQCSyntax_v1		359
+id_cct_crs		360
+id_cct_PKIData		361
+id_cct_PKIResponse		362
+ad_timeStamping		363
+ad_dvcs		364
+id_pkix_OCSP_basic		365
+id_pkix_OCSP_Nonce		366
+id_pkix_OCSP_CrlID		367
+id_pkix_OCSP_acceptableResponses		368
+id_pkix_OCSP_noCheck		369
+id_pkix_OCSP_archiveCutoff		370
+id_pkix_OCSP_serviceLocator		371
+id_pkix_OCSP_extendedStatus		372
+id_pkix_OCSP_valid		373
+id_pkix_OCSP_path		374
+id_pkix_OCSP_trustRoot		375
+algorithm		376
+rsaSignature		377
+X500algorithms		378
+org		379
+dod		380
+iana		381
+Directory		382
+Management		383
+Experimental		384
+Private		385
+Security		386
+SNMPv2		387
+Mail		388
+Enterprises		389
+dcObject		390
+domainComponent		391
+Domain		392
+joint_iso_ccitt		393
+selected_attribute_types		394
+clearance		395
+md4WithRSAEncryption		396
+ac_proxying		397
+sinfo_access		398
+id_aca_encAttrs		399
+role		400
+policy_constraints		401
+target_information		402
+no_rev_avail		403
+ccitt		404
+ansi_X9_62		405
+X9_62_prime_field		406
+X9_62_characteristic_two_field		407
+X9_62_id_ecPublicKey		408
+X9_62_prime192v1		409
+X9_62_prime192v2		410
+X9_62_prime192v3		411
+X9_62_prime239v1		412
+X9_62_prime239v2		413
+X9_62_prime239v3		414
+X9_62_prime256v1		415
+ecdsa_with_SHA1		416
+ms_csp_name		417
+aes_128_ecb		418
+aes_128_cbc		419
+aes_128_ofb128		420
+aes_128_cfb128		421
+aes_192_ecb		422
+aes_192_cbc		423
+aes_192_ofb128		424
+aes_192_cfb128		425
+aes_256_ecb		426
+aes_256_cbc		427
+aes_256_ofb128		428
+aes_256_cfb128		429
+hold_instruction_code		430
+hold_instruction_none		431
+hold_instruction_call_issuer		432
+hold_instruction_reject		433
+data		434
+pss		435
+ucl		436
+pilot		437
+pilotAttributeType		438
+pilotAttributeSyntax		439
+pilotObjectClass		440
+pilotGroups		441
+iA5StringSyntax		442
+caseIgnoreIA5StringSyntax		443
+pilotObject		444
+pilotPerson		445
+account		446
+document		447
+room		448
+documentSeries		449
+rFC822localPart		450
+dNSDomain		451
+domainRelatedObject		452
+friendlyCountry		453
+simpleSecurityObject		454
+pilotOrganization		455
+pilotDSA		456
+qualityLabelledData		457
+userId		458
+textEncodedORAddress		459
+rfc822Mailbox		460
+info		461
+favouriteDrink		462
+roomNumber		463
+photo		464
+userClass		465
+host		466
+manager		467
+documentIdentifier		468
+documentTitle		469
+documentVersion		470
+documentAuthor		471
+documentLocation		472
+homeTelephoneNumber		473
+secretary		474
+otherMailbox		475
+lastModifiedTime		476
+lastModifiedBy		477
+aRecord		478
+pilotAttributeType27		479
+mXRecord		480
+nSRecord		481
+sOARecord		482
+cNAMERecord		483
+associatedDomain		484
+associatedName		485
+homePostalAddress		486
+personalTitle		487
+mobileTelephoneNumber		488
+pagerTelephoneNumber		489
+friendlyCountryName		490
+organizationalStatus		491
+janetMailbox		492
+mailPreferenceOption		493
+buildingName		494
+dSAQuality		495
+singleLevelQuality		496
+subtreeMinimumQuality		497
+subtreeMaximumQuality		498
+personalSignature		499
+dITRedirect		500
+audio		501
+documentPublisher		502
+x500UniqueIdentifier		503
+mime_mhs		504
+mime_mhs_headings		505
+mime_mhs_bodies		506
+id_hex_partial_message		507
+id_hex_multipart_message		508
+generationQualifier		509
+pseudonym		510
+InternationalRA		511
+id_set		512
+set_ctype		513
+set_msgExt		514
+set_attr		515
+set_policy		516
+set_certExt		517
+set_brand		518
+setct_PANData		519
+setct_PANToken		520
+setct_PANOnly		521
+setct_OIData		522
+setct_PI		523
+setct_PIData		524
+setct_PIDataUnsigned		525
+setct_HODInput		526
+setct_AuthResBaggage		527
+setct_AuthRevReqBaggage		528
+setct_AuthRevResBaggage		529
+setct_CapTokenSeq		530
+setct_PInitResData		531
+setct_PI_TBS		532
+setct_PResData		533
+setct_AuthReqTBS		534
+setct_AuthResTBS		535
+setct_AuthResTBSX		536
+setct_AuthTokenTBS		537
+setct_CapTokenData		538
+setct_CapTokenTBS		539
+setct_AcqCardCodeMsg		540
+setct_AuthRevReqTBS		541
+setct_AuthRevResData		542
+setct_AuthRevResTBS		543
+setct_CapReqTBS		544
+setct_CapReqTBSX		545
+setct_CapResData		546
+setct_CapRevReqTBS		547
+setct_CapRevReqTBSX		548
+setct_CapRevResData		549
+setct_CredReqTBS		550
+setct_CredReqTBSX		551
+setct_CredResData		552
+setct_CredRevReqTBS		553
+setct_CredRevReqTBSX		554
+setct_CredRevResData		555
+setct_PCertReqData		556
+setct_PCertResTBS		557
+setct_BatchAdminReqData		558
+setct_BatchAdminResData		559
+setct_CardCInitResTBS		560
+setct_MeAqCInitResTBS		561
+setct_RegFormResTBS		562
+setct_CertReqData		563
+setct_CertReqTBS		564
+setct_CertResData		565
+setct_CertInqReqTBS		566
+setct_ErrorTBS		567
+setct_PIDualSignedTBE		568
+setct_PIUnsignedTBE		569
+setct_AuthReqTBE		570
+setct_AuthResTBE		571
+setct_AuthResTBEX		572
+setct_AuthTokenTBE		573
+setct_CapTokenTBE		574
+setct_CapTokenTBEX		575
+setct_AcqCardCodeMsgTBE		576
+setct_AuthRevReqTBE		577
+setct_AuthRevResTBE		578
+setct_AuthRevResTBEB		579
+setct_CapReqTBE		580
+setct_CapReqTBEX		581
+setct_CapResTBE		582
+setct_CapRevReqTBE		583
+setct_CapRevReqTBEX		584
+setct_CapRevResTBE		585
+setct_CredReqTBE		586
+setct_CredReqTBEX		587
+setct_CredResTBE		588
+setct_CredRevReqTBE		589
+setct_CredRevReqTBEX		590
+setct_CredRevResTBE		591
+setct_BatchAdminReqTBE		592
+setct_BatchAdminResTBE		593
+setct_RegFormReqTBE		594
+setct_CertReqTBE		595
+setct_CertReqTBEX		596
+setct_CertResTBE		597
+setct_CRLNotificationTBS		598
+setct_CRLNotificationResTBS		599
+setct_BCIDistributionTBS		600
+setext_genCrypt		601
+setext_miAuth		602
+setext_pinSecure		603
+setext_pinAny		604
+setext_track2		605
+setext_cv		606
+set_policy_root		607
+setCext_hashedRoot		608
+setCext_certType		609
+setCext_merchData		610
+setCext_cCertRequired		611
+setCext_tunneling		612
+setCext_setExt		613
+setCext_setQualf		614
+setCext_PGWYcapabilities		615
+setCext_TokenIdentifier		616
+setCext_Track2Data		617
+setCext_TokenType		618
+setCext_IssuerCapabilities		619
+setAttr_Cert		620
+setAttr_PGWYcap		621
+setAttr_TokenType		622
+setAttr_IssCap		623
+set_rootKeyThumb		624
+set_addPolicy		625
+setAttr_Token_EMV		626
+setAttr_Token_B0Prime		627
+setAttr_IssCap_CVM		628
+setAttr_IssCap_T2		629
+setAttr_IssCap_Sig		630
+setAttr_GenCryptgrm		631
+setAttr_T2Enc		632
+setAttr_T2cleartxt		633
+setAttr_TokICCsig		634
+setAttr_SecDevSig		635
+set_brand_IATA_ATA		636
+set_brand_Diners		637
+set_brand_AmericanExpress		638
+set_brand_JCB		639
+set_brand_Visa		640
+set_brand_MasterCard		641
+set_brand_Novus		642
+des_cdmf		643
+rsaOAEPEncryptionSET		644
+itu_t		645
+joint_iso_itu_t		646
+international_organizations		647
+ms_smartcard_login		648
+ms_upn		649
+aes_128_cfb1		650
+aes_192_cfb1		651
+aes_256_cfb1		652
+aes_128_cfb8		653
+aes_192_cfb8		654
+aes_256_cfb8		655
+des_cfb1		656
+des_cfb8		657
+des_ede3_cfb1		658
+des_ede3_cfb8		659
+streetAddress		660
+postalCode		661
+id_ppl		662
+proxyCertInfo		663
+id_ppl_anyLanguage		664
+id_ppl_inheritAll		665
+name_constraints		666
+Independent		667
+sha256WithRSAEncryption		668
+sha384WithRSAEncryption		669
+sha512WithRSAEncryption		670
+sha224WithRSAEncryption		671
+sha256		672
+sha384		673
+sha512		674
+sha224		675
+identified_organization		676
+certicom_arc		677
+wap		678
+wap_wsg		679
+X9_62_id_characteristic_two_basis		680
+X9_62_onBasis		681
+X9_62_tpBasis		682
+X9_62_ppBasis		683
+X9_62_c2pnb163v1		684
+X9_62_c2pnb163v2		685
+X9_62_c2pnb163v3		686
+X9_62_c2pnb176v1		687
+X9_62_c2tnb191v1		688
+X9_62_c2tnb191v2		689
+X9_62_c2tnb191v3		690
+X9_62_c2onb191v4		691
+X9_62_c2onb191v5		692
+X9_62_c2pnb208w1		693
+X9_62_c2tnb239v1		694
+X9_62_c2tnb239v2		695
+X9_62_c2tnb239v3		696
+X9_62_c2onb239v4		697
+X9_62_c2onb239v5		698
+X9_62_c2pnb272w1		699
+X9_62_c2pnb304w1		700
+X9_62_c2tnb359v1		701
+X9_62_c2pnb368w1		702
+X9_62_c2tnb431r1		703
+secp112r1		704
+secp112r2		705
+secp128r1		706
+secp128r2		707
+secp160k1		708
+secp160r1		709
+secp160r2		710
+secp192k1		711
+secp224k1		712
+secp224r1		713
+secp256k1		714
+secp384r1		715
+secp521r1		716
+sect113r1		717
+sect113r2		718
+sect131r1		719
+sect131r2		720
+sect163k1		721
+sect163r1		722
+sect163r2		723
+sect193r1		724
+sect193r2		725
+sect233k1		726
+sect233r1		727
+sect239k1		728
+sect283k1		729
+sect283r1		730
+sect409k1		731
+sect409r1		732
+sect571k1		733
+sect571r1		734
+wap_wsg_idm_ecid_wtls1		735
+wap_wsg_idm_ecid_wtls3		736
+wap_wsg_idm_ecid_wtls4		737
+wap_wsg_idm_ecid_wtls5		738
+wap_wsg_idm_ecid_wtls6		739
+wap_wsg_idm_ecid_wtls7		740
+wap_wsg_idm_ecid_wtls8		741
+wap_wsg_idm_ecid_wtls9		742
+wap_wsg_idm_ecid_wtls10		743
+wap_wsg_idm_ecid_wtls11		744
+wap_wsg_idm_ecid_wtls12		745
+any_policy		746
+policy_mappings		747
+inhibit_any_policy		748
+ipsec3		749
+ipsec4		750
+camellia_128_cbc		751
+camellia_192_cbc		752
+camellia_256_cbc		753
+camellia_128_ecb		754
+camellia_192_ecb		755
+camellia_256_ecb		756
+camellia_128_cfb128		757
+camellia_192_cfb128		758
+camellia_256_cfb128		759
+camellia_128_cfb1		760
+camellia_192_cfb1		761
+camellia_256_cfb1		762
+camellia_128_cfb8		763
+camellia_192_cfb8		764
+camellia_256_cfb8		765
+camellia_128_ofb128		766
+camellia_192_ofb128		767
+camellia_256_ofb128		768
+subject_directory_attributes		769
+issuing_distribution_point		770
+certificate_issuer		771
+korea		772
+kisa		773
+kftc		774
+npki_alg		775
+seed_ecb		776
+seed_cbc		777
+seed_ofb128		778
+seed_cfb128		779
+hmac_md5		780
+hmac_sha1		781
+id_PasswordBasedMAC		782
+id_DHBasedMac		783
+id_it_suppLangTags		784
+caRepository		785
+id_smime_ct_compressedData		786
+id_ct_asciiTextWithCRLF		787
+id_aes128_wrap		788
+id_aes192_wrap		789
+id_aes256_wrap		790
+ecdsa_with_Recommended		791
+ecdsa_with_Specified		792
+ecdsa_with_SHA224		793
+ecdsa_with_SHA256		794
+ecdsa_with_SHA384		795
+ecdsa_with_SHA512		796
+hmacWithMD5		797
+hmacWithSHA224		798
+hmacWithSHA256		799
+hmacWithSHA384		800
+hmacWithSHA512		801
+dsa_with_SHA224		802
+dsa_with_SHA256		803
+whirlpool		804
+cryptopro		805
+cryptocom		806
+id_GostR3411_94_with_GostR3410_2001		807
+id_GostR3411_94_with_GostR3410_94		808
+id_GostR3411_94		809
+id_HMACGostR3411_94		810
+id_GostR3410_2001		811
+id_GostR3410_94		812
+id_Gost28147_89		813
+gost89_cnt		814
+id_Gost28147_89_MAC		815
+id_GostR3411_94_prf		816
+id_GostR3410_2001DH		817
+id_GostR3410_94DH		818
+id_Gost28147_89_CryptoPro_KeyMeshing		819
+id_Gost28147_89_None_KeyMeshing		820
+id_GostR3411_94_TestParamSet		821
+id_GostR3411_94_CryptoProParamSet		822
+id_Gost28147_89_TestParamSet		823
+id_Gost28147_89_CryptoPro_A_ParamSet		824
+id_Gost28147_89_CryptoPro_B_ParamSet		825
+id_Gost28147_89_CryptoPro_C_ParamSet		826
+id_Gost28147_89_CryptoPro_D_ParamSet		827
+id_Gost28147_89_CryptoPro_Oscar_1_1_ParamSet		828
+id_Gost28147_89_CryptoPro_Oscar_1_0_ParamSet		829
+id_Gost28147_89_CryptoPro_RIC_1_ParamSet		830
+id_GostR3410_94_TestParamSet		831
+id_GostR3410_94_CryptoPro_A_ParamSet		832
+id_GostR3410_94_CryptoPro_B_ParamSet		833
+id_GostR3410_94_CryptoPro_C_ParamSet		834
+id_GostR3410_94_CryptoPro_D_ParamSet		835
+id_GostR3410_94_CryptoPro_XchA_ParamSet		836
+id_GostR3410_94_CryptoPro_XchB_ParamSet		837
+id_GostR3410_94_CryptoPro_XchC_ParamSet		838
+id_GostR3410_2001_TestParamSet		839
+id_GostR3410_2001_CryptoPro_A_ParamSet		840
+id_GostR3410_2001_CryptoPro_B_ParamSet		841
+id_GostR3410_2001_CryptoPro_C_ParamSet		842
+id_GostR3410_2001_CryptoPro_XchA_ParamSet		843
+id_GostR3410_2001_CryptoPro_XchB_ParamSet		844
+id_GostR3410_94_a		845
+id_GostR3410_94_aBis		846
+id_GostR3410_94_b		847
+id_GostR3410_94_bBis		848
+id_Gost28147_89_cc		849
+id_GostR3410_94_cc		850
+id_GostR3410_2001_cc		851
+id_GostR3411_94_with_GostR3410_94_cc		852
+id_GostR3411_94_with_GostR3410_2001_cc		853
+id_GostR3410_2001_ParamSet_cc		854
+hmac		855
+LocalKeySet		856
+freshest_crl		857
+id_on_permanentIdentifier		858
+searchGuide		859
+businessCategory		860
+postalAddress		861
+postOfficeBox		862
+physicalDeliveryOfficeName		863
+telephoneNumber		864
+telexNumber		865
+teletexTerminalIdentifier		866
+facsimileTelephoneNumber		867
+x121Address		868
+internationaliSDNNumber		869
+registeredAddress		870
+destinationIndicator		871
+preferredDeliveryMethod		872
+presentationAddress		873
+supportedApplicationContext		874
+member		875
+owner		876
+roleOccupant		877
+seeAlso		878
+userPassword		879
+userCertificate		880
+cACertificate		881
+authorityRevocationList		882
+certificateRevocationList		883
+crossCertificatePair		884
+enhancedSearchGuide		885
+protocolInformation		886
+distinguishedName		887
+uniqueMember		888
+houseIdentifier		889
+supportedAlgorithms		890
+deltaRevocationList		891
+dmdName		892
+id_alg_PWRI_KEK		893
+cmac		894
+aes_128_gcm		895
+aes_128_ccm		896
+id_aes128_wrap_pad		897
+aes_192_gcm		898
+aes_192_ccm		899
+id_aes192_wrap_pad		900
+aes_256_gcm		901
+aes_256_ccm		902
+id_aes256_wrap_pad		903
+aes_128_ctr		904
+aes_192_ctr		905
+aes_256_ctr		906
+id_camellia128_wrap		907
+id_camellia192_wrap		908
+id_camellia256_wrap		909
+anyExtendedKeyUsage		910
+mgf1		911
+rsassaPss		912
+aes_128_xts		913
+aes_256_xts		914
+rc4_hmac_md5		915
+aes_128_cbc_hmac_sha1		916
+aes_192_cbc_hmac_sha1		917
+aes_256_cbc_hmac_sha1		918
+rsaesOaep		919
+dhpublicnumber		920
+brainpoolP160r1		921
+brainpoolP160t1		922
+brainpoolP192r1		923
+brainpoolP192t1		924
+brainpoolP224r1		925
+brainpoolP224t1		926
+brainpoolP256r1		927
+brainpoolP256t1		928
+brainpoolP320r1		929
+brainpoolP320t1		930
+brainpoolP384r1		931
+brainpoolP384t1		932
+brainpoolP512r1		933
+brainpoolP512t1		934
+pSpecified		935
+dhSinglePass_stdDH_sha1kdf_scheme		936
+dhSinglePass_stdDH_sha224kdf_scheme		937
+dhSinglePass_stdDH_sha256kdf_scheme		938
+dhSinglePass_stdDH_sha384kdf_scheme		939
+dhSinglePass_stdDH_sha512kdf_scheme		940
+dhSinglePass_cofactorDH_sha1kdf_scheme		941
+dhSinglePass_cofactorDH_sha224kdf_scheme		942
+dhSinglePass_cofactorDH_sha256kdf_scheme		943
+dhSinglePass_cofactorDH_sha384kdf_scheme		944
+dhSinglePass_cofactorDH_sha512kdf_scheme		945
+dh_std_kdf		946
+dh_cofactor_kdf		947
+aes_128_cbc_hmac_sha256		948
+aes_192_cbc_hmac_sha256		949
+aes_256_cbc_hmac_sha256		950
+ct_precert_scts		951
+ct_precert_poison		952
+ct_precert_signer		953
+ct_cert_scts		954
+jurisdictionLocalityName		955
+jurisdictionStateOrProvinceName		956
+jurisdictionCountryName		957
+aes_128_ocb		958
+aes_192_ocb		959
+aes_256_ocb		960
+camellia_128_gcm		961
+camellia_128_ccm		962
+camellia_128_ctr		963
+camellia_128_cmac		964
+camellia_192_gcm		965
+camellia_192_ccm		966
+camellia_192_ctr		967
+camellia_192_cmac		968
+camellia_256_gcm		969
+camellia_256_ccm		970
+camellia_256_ctr		971
+camellia_256_cmac		972
+id_scrypt		973
+id_tc26		974
+gost89_cnt_12		975
+gost_mac_12		976
+id_tc26_algorithms		977
+id_tc26_sign		978
+id_GostR3410_2012_256		979
+id_GostR3410_2012_512		980
+id_tc26_digest		981
+id_GostR3411_2012_256		982
+id_GostR3411_2012_512		983
+id_tc26_signwithdigest		984
+id_tc26_signwithdigest_gost3410_2012_256		985
+id_tc26_signwithdigest_gost3410_2012_512		986
+id_tc26_mac		987
+id_tc26_hmac_gost_3411_2012_256		988
+id_tc26_hmac_gost_3411_2012_512		989
+id_tc26_cipher		990
+id_tc26_agreement		991
+id_tc26_agreement_gost_3410_2012_256		992
+id_tc26_agreement_gost_3410_2012_512		993
+id_tc26_constants		994
+id_tc26_sign_constants		995
+id_tc26_gost_3410_2012_512_constants		996
+id_tc26_gost_3410_2012_512_paramSetTest		997
+id_tc26_gost_3410_2012_512_paramSetA		998
+id_tc26_gost_3410_2012_512_paramSetB		999
+id_tc26_digest_constants		1000
+id_tc26_cipher_constants		1001
+id_tc26_gost_28147_constants		1002
+id_tc26_gost_28147_param_Z		1003
+INN		1004
+OGRN		1005
+SNILS		1006
+subjectSignTool		1007
+issuerSignTool		1008
+gost89_cbc		1009
+gost89_ecb		1010
+gost89_ctr		1011
+grasshopper_ecb		1012
+grasshopper_ctr		1013
+grasshopper_ofb		1014
+grasshopper_cbc		1015
+grasshopper_cfb		1016
+grasshopper_mac		1017
+chacha20_poly1305		1018
+chacha20		1019
+tlsfeature		1020
+tls1_prf		1021
+ipsec_IKE		1022
+capwapAC		1023
+capwapWTP		1024
+sshClient		1025
+sshServer		1026
+sendRouter		1027
+sendProxiedRouter		1028
+sendOwner		1029
+sendProxiedOwner		1030
+id_pkinit		1031
+pkInitClientAuth		1032
+pkInitKDC		1033
+X25519		1034
+X448		1035
+hkdf		1036
+kx_rsa		1037
+kx_ecdhe		1038
+kx_dhe		1039
+kx_ecdhe_psk		1040
+kx_dhe_psk		1041
+kx_rsa_psk		1042
+kx_psk		1043
+kx_srp		1044
+kx_gost		1045
+auth_rsa		1046
+auth_ecdsa		1047
+auth_psk		1048
+auth_dss		1049
+auth_gost01		1050
+auth_gost12		1051
+auth_srp		1052
+auth_null		1053
+fips_none		1054
+fips_140_2		1055
+blake2b512		1056
+blake2s256		1057
+id_smime_ct_contentCollection		1058
+id_smime_ct_authEnvelopedData		1059
+id_ct_xml		1060
+X9_62_id_ecSigType		1061
+secg_scheme		1062
+ecies_recommendedParameters		1063
+ecies_specifiedParameters		1064
+x9_63_kdf		1065
+nist_concatenation_kdf		1066
+tls_kdf		1067
+ikev2_kdf		1068
+xor_in_ecies		1069
+tdes_cbc_in_ecies		1070
+aes128_cbc_in_ecies		1071
+aes192_cbc_in_ecies		1072
+aes256_cbc_in_ecies		1073
+aes128_ctr_in_ecies		1074
+aes192_ctr_in_ecies		1075
+aes256_ctr_in_ecies		1076
+hmac_full_ecies		1077
+hmac_half_ecies		1078
+cmac_aes128_ecies		1079
+cmac_aes192_ecies		1080
+cmac_aes256_ecies		1081
+cbc_mac		1082
+pbe_WithSM3AndSMS4_CBC		1083
+ecies_with_x9_63_sha1_xor_hmac		1084
+ecies_with_x9_63_sha256_xor_hmac		1085
+ecies_with_x9_63_sha512_xor_hmac		1086
+ecies_with_x9_63_sha1_aes128_cbc_hmac		1087
+ecies_with_x9_63_sha256_aes128_cbc_hmac		1088
+ecies_with_x9_63_sha512_aes256_cbc_hmac		1089
+ecies_with_x9_63_sha256_aes128_ctr_hmac		1090
+ecies_with_x9_63_sha512_aes256_ctr_hmac		1091
+ecies_with_x9_63_sha256_aes128_cbc_hmac_half		1092
+ecies_with_x9_63_sha512_aes256_cbc_hmac_half		1093
+ecies_with_x9_63_sha256_aes128_ctr_hmac_half		1094
+ecies_with_x9_63_sha512_aes256_ctr_hmac_half		1095
+ecies_with_x9_63_sha1_aes128_cbc_cmac		1096
+ecies_with_x9_63_sha256_aes128_cbc_cmac		1097
+ecies_with_x9_63_sha512_aes256_cbc_cmac		1098
+ecies_with_x9_63_sha256_aes128_ctr_cmac		1099
+ecies_with_x9_63_sha512_aes256_ctr_cmac		1100
+ibcs1		1101
+type1curve		1102
+bfibe		1103
+bb1ibe		1104
+ISO_CN		1105
+oscca		1106
+sm_scheme		1107
+ssf33_ecb		1108
+ssf33_cbc		1109
+ssf33_ofb128		1110
+ssf33_cfb128		1111
+ssf33_cfb1		1112
+ssf33_cfb8		1113
+ssf33_cbc_mac		1114
+sm1_ecb		1115
+sm1_cbc		1116
+sm1_ofb128		1117
+sm1_cfb128		1118
+sm1_cfb1		1119
+sm1_cfb8		1120
+sm2p256v1		1121
+sm2sign		1122
+sm2exchange		1123
+sm2encrypt		1124
+sm2sign_with_sm3		1125
+sm2sign_with_sha1		1126
+sm2sign_with_sha256		1127
+sm2sign_with_sha512		1128
+sm2sign_with_sha224		1129
+sm2sign_with_sha384		1130
+sm2sign_with_rmd160		1131
+sm2sign_with_whirlpool		1132
+sm2sign_with_blake2b512		1133
+sm2sign_with_blake2s256		1134
+sm2encrypt_recommendedParameters		1135
+sm2encrypt_specifiedParameters		1136
+sm2encrypt_with_sm3		1137
+sm2encrypt_with_sha1		1138
+sm2encrypt_with_sha224		1139
+sm2encrypt_with_sha256		1140
+sm2encrypt_with_sha384		1141
+sm2encrypt_with_sha512		1142
+sm2encrypt_with_rmd160		1143
+sm2encrypt_with_whirlpool		1144
+sm2encrypt_with_blake2b512		1145
+sm2encrypt_with_blake2s256		1146
+sm2encrypt_with_md5		1147
+sm3		1148
+hmac_sm3		1149
+sms4_ecb		1150
+sms4_cbc		1151
+sms4_ofb128		1152
+sms4_cfb128		1153
+sms4_cfb1		1154
+sms4_cfb8		1155
+sms4_ctr		1156
+sms4_gcm		1157
+sms4_ccm		1158
+sms4_xts		1159
+sms4_wrap		1160
+sms4_wrap_pad		1161
+sms4_ocb		1162
+sm5		1163
+sm6_ecb		1164
+sm6_cbc		1165
+sm6_ofb128		1166
+sm6_cfb128		1167
+id_sm9PublicKey		1168
+sm9sign		1169
+sm9keyagreement		1170
+sm9encrypt		1171
+sm9hash1		1172
+sm9kdf		1173
+id_sm9MasterSecret		1174
+sm9bn256v1		1175
+sm9sign_with_sm3		1176
+sm9sign_with_sha256		1177
+sm9encrypt_with_sm3_xor		1178
+sm9encrypt_with_sm3_sms4_cbc		1179
+sm9encrypt_with_sm3_sms4_ctr		1180
+sm9hash1_with_sm3		1181
+sm9hash1_with_sha256		1182
+sm9kdf_with_sm3		1183
+sm9kdf_with_sha256		1184
+zuc		1185
+zuc_128eea3		1186
+zuc_128eia3		1187
+bwips		1188
+wapi_crypto		1189
+wapi_ec		1190
+wapi_ecdsa192_sha256		1191
+wapip192v1		1192
+kx_sm2		1193
+kx_sm2dhe		1194
+kx_sm2_psk		1195
+kx_sm9		1196
+kx_sm9dhe		1197
+auth_sm2		1198
+auth_sm9		1199
+GmSSL		1200
+cpk		1201
+cpk_map		1202
+cpk_map_sha1		1203
+cpk_map_sha256		1204
+cpk_map_sm3		1205
+cpk_map_sha384		1206
+cpk_map_sha512		1207
+paillier		1208
+sm9hash2		1209
+sm9hash2_with_sm3		1210
+sm9hash2_with_sha256		1211
+zuc256		1212
+zuc_mac		1213
+zuc256_mac		1214
+zuc256_mac32		1215
+zuc256_mac64		1216
+zuc256_mac128		1217
diff --git a/crypto/objects/obj_xref.c b/crypto/objects/obj_xref.c
new file mode 100644
index 000000000..627f5bca2
--- /dev/null
+++ b/crypto/objects/obj_xref.c
@@ -0,0 +1,165 @@
+/*
+ * Copyright 2006-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "obj_xref.h"
+#include "e_os.h"
+
+static STACK_OF(nid_triple) *sig_app, *sigx_app;
+
+static int sig_cmp(const nid_triple *a, const nid_triple *b)
+{
+    return a->sign_id - b->sign_id;
+}
+
+DECLARE_OBJ_BSEARCH_CMP_FN(nid_triple, nid_triple, sig);
+IMPLEMENT_OBJ_BSEARCH_CMP_FN(nid_triple, nid_triple, sig);
+
+static int sig_sk_cmp(const nid_triple *const *a, const nid_triple *const *b)
+{
+    return (*a)->sign_id - (*b)->sign_id;
+}
+
+DECLARE_OBJ_BSEARCH_CMP_FN(const nid_triple *, const nid_triple *, sigx);
+
+static int sigx_cmp(const nid_triple *const *a, const nid_triple *const *b)
+{
+    int ret;
+    ret = (*a)->hash_id - (*b)->hash_id;
+    if (ret)
+        return ret;
+    return (*a)->pkey_id - (*b)->pkey_id;
+}
+
+IMPLEMENT_OBJ_BSEARCH_CMP_FN(const nid_triple *, const nid_triple *, sigx);
+
+int OBJ_find_sigid_algs(int signid, int *pdig_nid, int *ppkey_nid)
+{
+    nid_triple tmp;
+    const nid_triple *rv = NULL;
+    tmp.sign_id = signid;
+
+    if (sig_app) {
+        int idx = sk_nid_triple_find(sig_app, &tmp);
+        if (idx >= 0)
+            rv = sk_nid_triple_value(sig_app, idx);
+    }
+#ifndef OBJ_XREF_TEST2
+    if (rv == NULL) {
+        rv = OBJ_bsearch_sig(&tmp, sigoid_srt, OSSL_NELEM(sigoid_srt));
+    }
+#endif
+    if (rv == NULL)
+        return 0;
+    if (pdig_nid)
+        *pdig_nid = rv->hash_id;
+    if (ppkey_nid)
+        *ppkey_nid = rv->pkey_id;
+    return 1;
+}
+
+int OBJ_find_sigid_by_algs(int *psignid, int dig_nid, int pkey_nid)
+{
+    nid_triple tmp;
+    const nid_triple *t = &tmp;
+    const nid_triple **rv = NULL;
+
+    tmp.hash_id = dig_nid;
+    tmp.pkey_id = pkey_nid;
+
+    if (sigx_app) {
+        int idx = sk_nid_triple_find(sigx_app, &tmp);
+        if (idx >= 0) {
+            t = sk_nid_triple_value(sigx_app, idx);
+            rv = &t;
+        }
+    }
+#ifndef OBJ_XREF_TEST2
+    if (rv == NULL) {
+        rv = OBJ_bsearch_sigx(&t, sigoid_srt_xref, OSSL_NELEM(sigoid_srt_xref));
+    }
+#endif
+    if (rv == NULL)
+        return 0;
+    if (psignid)
+        *psignid = (*rv)->sign_id;
+    return 1;
+}
+
+int OBJ_add_sigid(int signid, int dig_id, int pkey_id)
+{
+    nid_triple *ntr;
+    if (sig_app == NULL)
+        sig_app = sk_nid_triple_new(sig_sk_cmp);
+    if (sig_app == NULL)
+        return 0;
+    if (sigx_app == NULL)
+        sigx_app = sk_nid_triple_new(sigx_cmp);
+    if (sigx_app == NULL)
+        return 0;
+    ntr = OPENSSL_malloc(sizeof(*ntr));
+    if (ntr == NULL)
+        return 0;
+    ntr->sign_id = signid;
+    ntr->hash_id = dig_id;
+    ntr->pkey_id = pkey_id;
+
+    if (!sk_nid_triple_push(sig_app, ntr)) {
+        OPENSSL_free(ntr);
+        return 0;
+    }
+
+    if (!sk_nid_triple_push(sigx_app, ntr))
+        return 0;
+
+    sk_nid_triple_sort(sig_app);
+    sk_nid_triple_sort(sigx_app);
+
+    return 1;
+}
+
+static void sid_free(nid_triple *tt)
+{
+    OPENSSL_free(tt);
+}
+
+void OBJ_sigid_free(void)
+{
+    sk_nid_triple_pop_free(sig_app, sid_free);
+    sig_app = NULL;
+    sk_nid_triple_free(sigx_app);
+    sigx_app = NULL;
+}
+
+#ifdef OBJ_XREF_TEST
+
+main()
+{
+    int n1, n2, n3;
+
+    int i, rv;
+# ifdef OBJ_XREF_TEST2
+    for (i = 0; i < OSSL_NELEM(sigoid_srt); i++) {
+        OBJ_add_sigid(sigoid_srt[i][0], sigoid_srt[i][1], sigoid_srt[i][2]);
+    }
+# endif
+
+    for (i = 0; i < OSSL_NELEM(sigoid_srt); i++) {
+        n1 = sigoid_srt[i][0];
+        rv = OBJ_find_sigid_algs(n1, &n2, &n3);
+        printf("Forward: %d, %s %s %s\n", rv,
+               OBJ_nid2ln(n1), OBJ_nid2ln(n2), OBJ_nid2ln(n3));
+        n1 = 0;
+        rv = OBJ_find_sigid_by_algs(&n1, n2, n3);
+        printf("Reverse: %d, %s %s %s\n", rv,
+               OBJ_nid2ln(n1), OBJ_nid2ln(n2), OBJ_nid2ln(n3));
+    }
+}
+
+#endif
diff --git a/crypto/objects/obj_xref.h b/crypto/objects/obj_xref.h
new file mode 100644
index 000000000..840b5b5fe
--- /dev/null
+++ b/crypto/objects/obj_xref.h
@@ -0,0 +1,120 @@
+/*
+ * WARNING: do not edit!
+ * Generated by objxref.pl
+ *
+ * Copyright 1998-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+
+typedef struct {
+    int sign_id;
+    int hash_id;
+    int pkey_id;
+} nid_triple;
+
+DEFINE_STACK_OF(nid_triple)
+
+static const nid_triple sigoid_srt[] = {
+    {NID_md2WithRSAEncryption, NID_md2, NID_rsaEncryption},
+    {NID_md5WithRSAEncryption, NID_md5, NID_rsaEncryption},
+    {NID_shaWithRSAEncryption, NID_sha, NID_rsaEncryption},
+    {NID_sha1WithRSAEncryption, NID_sha1, NID_rsaEncryption},
+    {NID_dsaWithSHA, NID_sha, NID_dsa},
+    {NID_dsaWithSHA1_2, NID_sha1, NID_dsa_2},
+    {NID_mdc2WithRSA, NID_mdc2, NID_rsaEncryption},
+    {NID_md5WithRSA, NID_md5, NID_rsa},
+    {NID_dsaWithSHA1, NID_sha1, NID_dsa},
+    {NID_sha1WithRSA, NID_sha1, NID_rsa},
+    {NID_ripemd160WithRSA, NID_ripemd160, NID_rsaEncryption},
+    {NID_md4WithRSAEncryption, NID_md4, NID_rsaEncryption},
+    {NID_ecdsa_with_SHA1, NID_sha1, NID_X9_62_id_ecPublicKey},
+    {NID_sha256WithRSAEncryption, NID_sha256, NID_rsaEncryption},
+    {NID_sha384WithRSAEncryption, NID_sha384, NID_rsaEncryption},
+    {NID_sha512WithRSAEncryption, NID_sha512, NID_rsaEncryption},
+    {NID_sha224WithRSAEncryption, NID_sha224, NID_rsaEncryption},
+    {NID_ecdsa_with_Recommended, NID_undef, NID_X9_62_id_ecPublicKey},
+    {NID_ecdsa_with_Specified, NID_undef, NID_X9_62_id_ecPublicKey},
+    {NID_ecdsa_with_SHA224, NID_sha224, NID_X9_62_id_ecPublicKey},
+    {NID_ecdsa_with_SHA256, NID_sha256, NID_X9_62_id_ecPublicKey},
+    {NID_ecdsa_with_SHA384, NID_sha384, NID_X9_62_id_ecPublicKey},
+    {NID_ecdsa_with_SHA512, NID_sha512, NID_X9_62_id_ecPublicKey},
+    {NID_dsa_with_SHA224, NID_sha224, NID_dsa},
+    {NID_dsa_with_SHA256, NID_sha256, NID_dsa},
+    {NID_id_GostR3411_94_with_GostR3410_2001, NID_id_GostR3411_94,
+     NID_id_GostR3410_2001},
+    {NID_id_GostR3411_94_with_GostR3410_94, NID_id_GostR3411_94,
+     NID_id_GostR3410_94},
+    {NID_id_GostR3411_94_with_GostR3410_94_cc, NID_id_GostR3411_94,
+     NID_id_GostR3410_94_cc},
+    {NID_id_GostR3411_94_with_GostR3410_2001_cc, NID_id_GostR3411_94,
+     NID_id_GostR3410_2001_cc},
+    {NID_rsassaPss, NID_undef, NID_rsaEncryption},
+    {NID_dhSinglePass_stdDH_sha1kdf_scheme, NID_sha1, NID_dh_std_kdf},
+    {NID_dhSinglePass_stdDH_sha224kdf_scheme, NID_sha224, NID_dh_std_kdf},
+    {NID_dhSinglePass_stdDH_sha256kdf_scheme, NID_sha256, NID_dh_std_kdf},
+    {NID_dhSinglePass_stdDH_sha384kdf_scheme, NID_sha384, NID_dh_std_kdf},
+    {NID_dhSinglePass_stdDH_sha512kdf_scheme, NID_sha512, NID_dh_std_kdf},
+    {NID_dhSinglePass_cofactorDH_sha1kdf_scheme, NID_sha1,
+     NID_dh_cofactor_kdf},
+    {NID_dhSinglePass_cofactorDH_sha224kdf_scheme, NID_sha224,
+     NID_dh_cofactor_kdf},
+    {NID_dhSinglePass_cofactorDH_sha256kdf_scheme, NID_sha256,
+     NID_dh_cofactor_kdf},
+    {NID_dhSinglePass_cofactorDH_sha384kdf_scheme, NID_sha384,
+     NID_dh_cofactor_kdf},
+    {NID_dhSinglePass_cofactorDH_sha512kdf_scheme, NID_sha512,
+     NID_dh_cofactor_kdf},
+    {NID_id_tc26_signwithdigest_gost3410_2012_256, NID_id_GostR3411_2012_256,
+     NID_id_GostR3410_2012_256},
+    {NID_id_tc26_signwithdigest_gost3410_2012_512, NID_id_GostR3411_2012_512,
+     NID_id_GostR3410_2012_512},
+    {NID_sm2sign_with_sm3, NID_sm3, NID_X9_62_id_ecPublicKey},
+};
+
+static const nid_triple *const sigoid_srt_xref[] = {
+    &sigoid_srt[0],
+    &sigoid_srt[1],
+    &sigoid_srt[7],
+    &sigoid_srt[2],
+    &sigoid_srt[4],
+    &sigoid_srt[3],
+    &sigoid_srt[9],
+    &sigoid_srt[5],
+    &sigoid_srt[8],
+    &sigoid_srt[12],
+    &sigoid_srt[30],
+    &sigoid_srt[35],
+    &sigoid_srt[6],
+    &sigoid_srt[10],
+    &sigoid_srt[11],
+    &sigoid_srt[13],
+    &sigoid_srt[24],
+    &sigoid_srt[20],
+    &sigoid_srt[32],
+    &sigoid_srt[37],
+    &sigoid_srt[14],
+    &sigoid_srt[21],
+    &sigoid_srt[33],
+    &sigoid_srt[38],
+    &sigoid_srt[15],
+    &sigoid_srt[22],
+    &sigoid_srt[34],
+    &sigoid_srt[39],
+    &sigoid_srt[16],
+    &sigoid_srt[23],
+    &sigoid_srt[19],
+    &sigoid_srt[31],
+    &sigoid_srt[36],
+    &sigoid_srt[25],
+    &sigoid_srt[26],
+    &sigoid_srt[27],
+    &sigoid_srt[28],
+    &sigoid_srt[40],
+    &sigoid_srt[41],
+    &sigoid_srt[42],
+};
diff --git a/crypto/objects/obj_xref.txt b/crypto/objects/obj_xref.txt
new file mode 100644
index 000000000..9a7e8be07
--- /dev/null
+++ b/crypto/objects/obj_xref.txt
@@ -0,0 +1,61 @@
+# OID cross reference table.
+# Links signatures OIDs to their corresponding public key algorithms
+# and digests.
+
+md2WithRSAEncryption	md2	rsaEncryption
+md5WithRSAEncryption	md5	rsaEncryption
+shaWithRSAEncryption	sha	rsaEncryption
+sha1WithRSAEncryption	sha1	rsaEncryption
+md4WithRSAEncryption	md4	rsaEncryption
+sha256WithRSAEncryption sha256	rsaEncryption
+sha384WithRSAEncryption	sha384	rsaEncryption
+sha512WithRSAEncryption	sha512	rsaEncryption
+sha224WithRSAEncryption	sha224	rsaEncryption
+mdc2WithRSA		mdc2	rsaEncryption
+ripemd160WithRSA	ripemd160 rsaEncryption
+# For PSS the digest algorithm can vary and depends on the included
+# AlgorithmIdentifier. The digest "undef" indicates the public key
+# method should handle this explicitly.
+rsassaPss		undef	rsaEncryption
+
+# Alternative deprecated OIDs. By using the older "rsa" OID this
+# type will be recognized by not normally used.
+
+md5WithRSA		md5	rsa
+sha1WithRSA		sha1	rsa
+
+dsaWithSHA		sha	dsa
+dsaWithSHA1		sha1	dsa
+
+dsaWithSHA1_2		sha1	dsa_2
+
+ecdsa_with_SHA1		sha1	X9_62_id_ecPublicKey
+ecdsa_with_SHA224	sha224	X9_62_id_ecPublicKey
+ecdsa_with_SHA256	sha256	X9_62_id_ecPublicKey
+ecdsa_with_SHA384	sha384	X9_62_id_ecPublicKey
+ecdsa_with_SHA512	sha512	X9_62_id_ecPublicKey
+ecdsa_with_Recommended	undef	X9_62_id_ecPublicKey
+ecdsa_with_Specified	undef	X9_62_id_ecPublicKey
+sm2sign_with_sm3	sm3	X9_62_id_ecPublicKey
+
+dsa_with_SHA224		sha224	dsa
+dsa_with_SHA256		sha256	dsa
+
+id_GostR3411_94_with_GostR3410_2001	id_GostR3411_94 id_GostR3410_2001
+id_GostR3411_94_with_GostR3410_94	id_GostR3411_94 id_GostR3410_94
+id_GostR3411_94_with_GostR3410_94_cc	id_GostR3411_94 id_GostR3410_94_cc
+id_GostR3411_94_with_GostR3410_2001_cc	id_GostR3411_94 id_GostR3410_2001_cc
+id_tc26_signwithdigest_gost3410_2012_256 id_GostR3411_2012_256 id_GostR3410_2012_256
+id_tc26_signwithdigest_gost3410_2012_512 id_GostR3411_2012_512 id_GostR3410_2012_512
+# ECDH KDFs and their corresponding message digests and schemes
+dhSinglePass_stdDH_sha1kdf_scheme		sha1	dh_std_kdf
+dhSinglePass_stdDH_sha224kdf_scheme		sha224	dh_std_kdf
+dhSinglePass_stdDH_sha256kdf_scheme		sha256	dh_std_kdf
+dhSinglePass_stdDH_sha384kdf_scheme		sha384	dh_std_kdf
+dhSinglePass_stdDH_sha512kdf_scheme		sha512	dh_std_kdf
+
+dhSinglePass_cofactorDH_sha1kdf_scheme		sha1	dh_cofactor_kdf
+dhSinglePass_cofactorDH_sha224kdf_scheme	sha224	dh_cofactor_kdf
+dhSinglePass_cofactorDH_sha256kdf_scheme	sha256	dh_cofactor_kdf
+dhSinglePass_cofactorDH_sha384kdf_scheme	sha384	dh_cofactor_kdf
+dhSinglePass_cofactorDH_sha512kdf_scheme	sha512	dh_cofactor_kdf
diff --git a/crypto/objects/objects.pl b/crypto/objects/objects.pl
new file mode 100644
index 000000000..3b40277a2
--- /dev/null
+++ b/crypto/objects/objects.pl
@@ -0,0 +1,193 @@
+#! /usr/bin/env perl
+# Copyright 2000-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+open (NUMIN,"$ARGV[1]") || die "Can't open number file $ARGV[1]";
+$max_nid=0;
+$o=0;
+while()
+	{
+	s|\R$||;
+	$o++;
+	s/#.*$//;
+	next if /^\s*$/;
+	$_ = 'X'.$_;
+	($Cname,$mynum) = split;
+	$Cname =~ s/^X//;
+	if (defined($nidn{$mynum}))
+		{ die "$ARGV[1]:$o:There's already an object with NID ",$mynum," on line ",$order{$mynum},"\n"; }
+	if (defined($nid{$Cname}))
+		{ die "$ARGV[1]:$o:There's already an object with name ",$Cname," on line ",$order{$nid{$Cname}},"\n"; }
+	$nid{$Cname} = $mynum;
+	$nidn{$mynum} = $Cname;
+	$order{$mynum} = $o;
+	$max_nid = $mynum if $mynum > $max_nid;
+	}
+close NUMIN;
+
+open (IN,"$ARGV[0]") || die "Can't open input file $ARGV[0]";
+$Cname="";
+$o=0;
+while ()
+	{
+	s|\R$||;
+	$o++;
+        if (/^!module\s+(.*)$/)
+		{
+		$module = $1."-";
+		$module =~ s/\./_/g;
+		$module =~ s/-/_/g;
+		}
+        if (/^!global$/)
+		{ $module = ""; }
+	if (/^!Cname\s+(.*)$/)
+		{ $Cname = $1; }
+	if (/^!Alias\s+(.+?)\s+(.*)$/)
+		{
+		$Cname = $module.$1;
+		$myoid = $2;
+		$myoid = &process_oid($myoid);
+		$Cname =~ s/-/_/g;
+		$ordern{$o} = $Cname;
+		$order{$Cname} = $o;
+		$obj{$Cname} = $myoid;
+		$_ = "";
+		$Cname = "";
+		}
+	s/!.*$//;
+	s/#.*$//;
+	next if /^\s*$/;
+	($myoid,$mysn,$myln) = split ':';
+	$mysn =~ s/^\s*//;
+	$mysn =~ s/\s*$//;
+	$myln =~ s/^\s*//;
+	$myln =~ s/\s*$//;
+	$myoid =~ s/^\s*//;
+	$myoid =~ s/\s*$//;
+	if ($myoid ne "")
+		{
+		$myoid = &process_oid($myoid);
+		}
+
+	if ($Cname eq "" && ($myln =~ /^[_A-Za-z][\w.-]*$/ ))
+		{
+		$Cname = $myln;
+		$Cname =~ s/\./_/g;
+		$Cname =~ s/-/_/g;
+		if ($Cname ne "" && defined($ln{$module.$Cname}))
+			{ die "objects.txt:$o:There's already an object with long name ",$ln{$module.$Cname}," on line ",$order{$module.$Cname},"\n"; }
+		}
+	if ($Cname eq "")
+		{
+		$Cname = $mysn;
+		$Cname =~ s/-/_/g;
+		if ($Cname ne "" && defined($sn{$module.$Cname}))
+			{ die "objects.txt:$o:There's already an object with short name ",$sn{$module.$Cname}," on line ",$order{$module.$Cname},"\n"; }
+		}
+	if ($Cname eq "")
+		{
+		$Cname = $myln;
+		$Cname =~ s/-/_/g;
+		$Cname =~ s/\./_/g;
+		$Cname =~ s/ /_/g;
+		if ($Cname ne "" && defined($ln{$module.$Cname}))
+			{ die "objects.txt:$o:There's already an object with long name ",$ln{$module.$Cname}," on line ",$order{$module.$Cname},"\n"; }
+		}
+	$Cname =~ s/\./_/g;
+	$Cname =~ s/-/_/g;
+	$Cname = $module.$Cname;
+	$ordern{$o} = $Cname;
+	$order{$Cname} = $o;
+	$sn{$Cname} = $mysn;
+	$ln{$Cname} = $myln;
+	$obj{$Cname} = $myoid;
+	if (!defined($nid{$Cname}))
+		{
+		$max_nid++;
+		$nid{$Cname} = $max_nid;
+		$nidn{$max_nid} = $Cname;
+print STDERR "Added OID $Cname\n";
+		}
+	$Cname="";
+	}
+close IN;
+
+open (NUMOUT,">$ARGV[1]") || die "Can't open output file $ARGV[1]";
+foreach (sort { $a <=> $b } keys %nidn)
+	{
+	print NUMOUT $nidn{$_},"\t\t",$_,"\n";
+	}
+close NUMOUT;
+
+open (OUT,">$ARGV[2]") || die "Can't open output file $ARGV[2]";
+print OUT <<'EOF';
+/*
+ * WARNING: do not edit!
+ * Generated by crypto/objects/objects.pl
+ *
+ * Copyright 2000-2016 The OpenSSL Project Authors. All Rights Reserved.
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#define SN_undef                        "UNDEF"
+#define LN_undef                        "undefined"
+#define NID_undef                       0
+#define OBJ_undef                       0L
+EOF
+
+sub expand
+	{
+	my $string = shift;
+
+	1 while $string =~ s/\t+/' ' x (length($&) * 8 - length($`) % 8)/e;
+
+	return $string;
+	}
+
+foreach (sort { $a <=> $b } keys %ordern)
+	{
+	$Cname=$ordern{$_};
+	print OUT "\n";
+	print OUT expand("#define SN_$Cname\t\t\"$sn{$Cname}\"\n") if $sn{$Cname} ne "";
+	print OUT expand("#define LN_$Cname\t\t\"$ln{$Cname}\"\n") if $ln{$Cname} ne "";
+	print OUT expand("#define NID_$Cname\t\t$nid{$Cname}\n") if $nid{$Cname} ne "";
+	print OUT expand("#define OBJ_$Cname\t\t$obj{$Cname}\n") if $obj{$Cname} ne "";
+	}
+
+close OUT;
+
+sub process_oid
+	{
+	local($oid)=@_;
+	local(@a,$oid_pref);
+
+	@a = split(/\s+/,$myoid);
+	$pref_oid = "";
+	$pref_sep = "";
+	if (!($a[0] =~ /^[0-9]+$/))
+		{
+		$a[0] =~ s/-/_/g;
+		if (!defined($obj{$a[0]}))
+			{ die "$ARGV[0]:$o:Undefined identifier ",$a[0],"\n"; }
+		$pref_oid = "OBJ_" . $a[0];
+		$pref_sep = ",";
+		shift @a;
+		}
+	$oids = join('L,',@a) . "L";
+	if ($oids ne "L")
+		{
+		$oids = $pref_oid . $pref_sep . $oids;
+		}
+	else
+		{
+		$oids = $pref_oid;
+		}
+	return($oids);
+	}
diff --git a/crypto/objects/objects.txt b/crypto/objects/objects.txt
new file mode 100644
index 000000000..38cab9aab
--- /dev/null
+++ b/crypto/objects/objects.txt
@@ -0,0 +1,1694 @@
+# CCITT was renamed to ITU-T quite some time ago
+0			: ITU-T			: itu-t
+!Alias ccitt itu-t
+
+1			: ISO			: iso
+
+2			: JOINT-ISO-ITU-T	: joint-iso-itu-t
+!Alias joint-iso-ccitt joint-iso-itu-t
+
+iso 2			: member-body		: ISO Member Body
+
+iso 3			: identified-organization
+
+# HMAC OIDs
+identified-organization 6 1 5 5 8 1 1	: HMAC-MD5	: hmac-md5
+identified-organization 6 1 5 5 8 1 2	: HMAC-SHA1	: hmac-sha1
+
+identified-organization 132	: certicom-arc
+
+joint-iso-itu-t 23	: international-organizations	: International Organizations
+
+international-organizations 43	: wap
+wap 1			: wap-wsg
+
+joint-iso-itu-t 5 1 5	: selected-attribute-types	: Selected Attribute Types
+
+selected-attribute-types 55	: clearance
+
+member-body 840		: ISO-US		: ISO US Member Body
+ISO-US 10040		: X9-57			: X9.57
+X9-57 4			: X9cm			: X9.57 CM ?
+
+!Cname dsa
+X9cm 1			: DSA			: dsaEncryption
+X9cm 3			: DSA-SHA1		: dsaWithSHA1
+
+
+ISO-US 10045		: ansi-X9-62		: ANSI X9.62
+!module X9-62
+!Alias id-fieldType ansi-X9-62 1
+X9-62_id-fieldType 1		: prime-field
+X9-62_id-fieldType 2		: characteristic-two-field
+X9-62_characteristic-two-field 3 : id-characteristic-two-basis
+X9-62_id-characteristic-two-basis 1 : onBasis
+X9-62_id-characteristic-two-basis 2 : tpBasis
+X9-62_id-characteristic-two-basis 3 : ppBasis
+!Alias id-publicKeyType ansi-X9-62 2
+X9-62_id-publicKeyType 1	: id-ecPublicKey
+!Alias ellipticCurve ansi-X9-62 3
+!Alias c-TwoCurve X9-62_ellipticCurve 0
+X9-62_c-TwoCurve 1		: c2pnb163v1
+X9-62_c-TwoCurve 2		: c2pnb163v2
+X9-62_c-TwoCurve 3		: c2pnb163v3
+X9-62_c-TwoCurve 4		: c2pnb176v1
+X9-62_c-TwoCurve 5		: c2tnb191v1
+X9-62_c-TwoCurve 6		: c2tnb191v2
+X9-62_c-TwoCurve 7		: c2tnb191v3
+X9-62_c-TwoCurve 8		: c2onb191v4
+X9-62_c-TwoCurve 9		: c2onb191v5
+X9-62_c-TwoCurve 10		: c2pnb208w1
+X9-62_c-TwoCurve 11		: c2tnb239v1
+X9-62_c-TwoCurve 12		: c2tnb239v2
+X9-62_c-TwoCurve 13		: c2tnb239v3
+X9-62_c-TwoCurve 14		: c2onb239v4
+X9-62_c-TwoCurve 15		: c2onb239v5
+X9-62_c-TwoCurve 16		: c2pnb272w1
+X9-62_c-TwoCurve 17		: c2pnb304w1
+X9-62_c-TwoCurve 18		: c2tnb359v1
+X9-62_c-TwoCurve 19		: c2pnb368w1
+X9-62_c-TwoCurve 20		: c2tnb431r1
+!Alias primeCurve X9-62_ellipticCurve 1
+X9-62_primeCurve 1	 	: prime192v1
+X9-62_primeCurve 2	 	: prime192v2
+X9-62_primeCurve 3	 	: prime192v3
+X9-62_primeCurve 4	 	: prime239v1
+X9-62_primeCurve 5	 	: prime239v2
+X9-62_primeCurve 6	 	: prime239v3
+X9-62_primeCurve 7	 	: prime256v1
+# GMSSL export ecdsa type
+#!Alias id-ecSigType ansi-X9-62 4
+ansi-X9-62 4			: id-ecSigType
+!global
+X9-62_id-ecSigType 1		: ecdsa-with-SHA1
+X9-62_id-ecSigType 2		: ecdsa-with-Recommended
+X9-62_id-ecSigType 3		: ecdsa-with-Specified
+ecdsa-with-Specified 1		: ecdsa-with-SHA224
+ecdsa-with-Specified 2		: ecdsa-with-SHA256
+ecdsa-with-Specified 3		: ecdsa-with-SHA384
+ecdsa-with-Specified 4		: ecdsa-with-SHA512
+
+# SECG curve OIDs from "SEC 2: Recommended Elliptic Curve Domain Parameters"
+# (http://www.secg.org/)
+!Alias secg_ellipticCurve certicom-arc 0
+# SECG prime curves OIDs
+secg-ellipticCurve 6		: secp112r1
+secg-ellipticCurve 7		: secp112r2
+secg-ellipticCurve 28		: secp128r1
+secg-ellipticCurve 29		: secp128r2
+secg-ellipticCurve 9		: secp160k1
+secg-ellipticCurve 8		: secp160r1
+secg-ellipticCurve 30		: secp160r2
+secg-ellipticCurve 31		: secp192k1
+# NOTE: the curve secp192r1 is the same as prime192v1 defined above
+#       and is therefore omitted
+secg-ellipticCurve 32		: secp224k1
+secg-ellipticCurve 33		: secp224r1
+secg-ellipticCurve 10		: secp256k1
+# NOTE: the curve secp256r1 is the same as prime256v1 defined above
+#       and is therefore omitted
+secg-ellipticCurve 34		: secp384r1
+secg-ellipticCurve 35		: secp521r1
+# SECG characteristic two curves OIDs
+secg-ellipticCurve 4		: sect113r1
+secg-ellipticCurve 5		: sect113r2
+secg-ellipticCurve 22		: sect131r1
+secg-ellipticCurve 23		: sect131r2
+secg-ellipticCurve 1		: sect163k1
+secg-ellipticCurve 2		: sect163r1
+secg-ellipticCurve 15		: sect163r2
+secg-ellipticCurve 24		: sect193r1
+secg-ellipticCurve 25		: sect193r2
+secg-ellipticCurve 26		: sect233k1
+secg-ellipticCurve 27		: sect233r1
+secg-ellipticCurve 3		: sect239k1
+secg-ellipticCurve 16		: sect283k1
+secg-ellipticCurve 17		: sect283r1
+secg-ellipticCurve 36		: sect409k1
+secg-ellipticCurve 37		: sect409r1
+secg-ellipticCurve 38		: sect571k1
+secg-ellipticCurve 39		: sect571r1
+
+# WAP/TLS curve OIDs (http://www.wapforum.org/)
+!Alias wap-wsg-idm-ecid wap-wsg 4
+wap-wsg-idm-ecid 1	: wap-wsg-idm-ecid-wtls1
+wap-wsg-idm-ecid 3	: wap-wsg-idm-ecid-wtls3
+wap-wsg-idm-ecid 4	: wap-wsg-idm-ecid-wtls4
+wap-wsg-idm-ecid 5	: wap-wsg-idm-ecid-wtls5
+wap-wsg-idm-ecid 6	: wap-wsg-idm-ecid-wtls6
+wap-wsg-idm-ecid 7	: wap-wsg-idm-ecid-wtls7
+wap-wsg-idm-ecid 8	: wap-wsg-idm-ecid-wtls8
+wap-wsg-idm-ecid 9	: wap-wsg-idm-ecid-wtls9
+wap-wsg-idm-ecid 10	: wap-wsg-idm-ecid-wtls10
+wap-wsg-idm-ecid 11	: wap-wsg-idm-ecid-wtls11
+wap-wsg-idm-ecid 12	: wap-wsg-idm-ecid-wtls12
+
+
+ISO-US 113533 7 66 10	: CAST5-CBC		: cast5-cbc
+			: CAST5-ECB		: cast5-ecb
+!Cname cast5-cfb64
+			: CAST5-CFB		: cast5-cfb
+!Cname cast5-ofb64
+			: CAST5-OFB		: cast5-ofb
+!Cname pbeWithMD5AndCast5-CBC
+ISO-US 113533 7 66 12	:			: pbeWithMD5AndCast5CBC
+
+# Macs for CMP and CRMF
+ISO-US 113533 7 66 13	: id-PasswordBasedMAC	: password based MAC
+ISO-US 113533 7 66 30	: id-DHBasedMac		: Diffie-Hellman based MAC
+
+ISO-US 113549		: rsadsi		: RSA Data Security, Inc.
+
+rsadsi 1		: pkcs			: RSA Data Security, Inc. PKCS
+
+pkcs 1			: pkcs1
+pkcs1 1			:			: rsaEncryption
+pkcs1 2			: RSA-MD2		: md2WithRSAEncryption
+pkcs1 3			: RSA-MD4		: md4WithRSAEncryption
+pkcs1 4			: RSA-MD5		: md5WithRSAEncryption
+pkcs1 5			: RSA-SHA1		: sha1WithRSAEncryption
+# According to PKCS #1 version 2.1
+pkcs1 7			: RSAES-OAEP		: rsaesOaep
+pkcs1 8			: MGF1			: mgf1
+pkcs1 9			: PSPECIFIED		: pSpecified
+pkcs1 10		: RSASSA-PSS		: rsassaPss
+
+pkcs1 11		: RSA-SHA256		: sha256WithRSAEncryption
+pkcs1 12		: RSA-SHA384		: sha384WithRSAEncryption
+pkcs1 13		: RSA-SHA512		: sha512WithRSAEncryption
+pkcs1 14		: RSA-SHA224		: sha224WithRSAEncryption
+
+pkcs 3			: pkcs3
+pkcs3 1			:			: dhKeyAgreement
+
+pkcs 5			: pkcs5
+pkcs5 1			: PBE-MD2-DES		: pbeWithMD2AndDES-CBC
+pkcs5 3			: PBE-MD5-DES		: pbeWithMD5AndDES-CBC
+pkcs5 4			: PBE-MD2-RC2-64	: pbeWithMD2AndRC2-CBC
+pkcs5 6			: PBE-MD5-RC2-64	: pbeWithMD5AndRC2-CBC
+pkcs5 10		: PBE-SHA1-DES		: pbeWithSHA1AndDES-CBC
+pkcs5 11		: PBE-SHA1-RC2-64	: pbeWithSHA1AndRC2-CBC
+!Cname id_pbkdf2
+pkcs5 12		:			: PBKDF2
+!Cname pbes2
+pkcs5 13		:			: PBES2
+!Cname pbmac1
+pkcs5 14		:			: PBMAC1
+
+pkcs 7			: pkcs7
+pkcs7 1			:			: pkcs7-data
+!Cname pkcs7-signed
+pkcs7 2			:			: pkcs7-signedData
+!Cname pkcs7-enveloped
+pkcs7 3			:			: pkcs7-envelopedData
+!Cname pkcs7-signedAndEnveloped
+pkcs7 4			:			: pkcs7-signedAndEnvelopedData
+!Cname pkcs7-digest
+pkcs7 5			:			: pkcs7-digestData
+!Cname pkcs7-encrypted
+pkcs7 6			:			: pkcs7-encryptedData
+
+pkcs 9			: pkcs9
+!module pkcs9
+pkcs9 1			: 			: emailAddress
+pkcs9 2			:			: unstructuredName
+pkcs9 3			:			: contentType
+pkcs9 4			:			: messageDigest
+pkcs9 5			:			: signingTime
+pkcs9 6			:			: countersignature
+pkcs9 7			:			: challengePassword
+pkcs9 8			:			: unstructuredAddress
+!Cname extCertAttributes
+pkcs9 9			:			: extendedCertificateAttributes
+!global
+
+!Cname ext-req
+pkcs9 14		: extReq		: Extension Request
+
+!Cname SMIMECapabilities
+pkcs9 15		: SMIME-CAPS		: S/MIME Capabilities
+
+# S/MIME
+!Cname SMIME
+pkcs9 16		: SMIME			: S/MIME
+SMIME 0			: id-smime-mod
+SMIME 1			: id-smime-ct
+SMIME 2			: id-smime-aa
+SMIME 3			: id-smime-alg
+SMIME 4			: id-smime-cd
+SMIME 5			: id-smime-spq
+SMIME 6			: id-smime-cti
+
+# S/MIME Modules
+id-smime-mod 1		: id-smime-mod-cms
+id-smime-mod 2		: id-smime-mod-ess
+id-smime-mod 3		: id-smime-mod-oid
+id-smime-mod 4		: id-smime-mod-msg-v3
+id-smime-mod 5		: id-smime-mod-ets-eSignature-88
+id-smime-mod 6		: id-smime-mod-ets-eSignature-97
+id-smime-mod 7		: id-smime-mod-ets-eSigPolicy-88
+id-smime-mod 8		: id-smime-mod-ets-eSigPolicy-97
+
+# S/MIME Content Types
+id-smime-ct 1		: id-smime-ct-receipt
+id-smime-ct 2		: id-smime-ct-authData
+id-smime-ct 3		: id-smime-ct-publishCert
+id-smime-ct 4		: id-smime-ct-TSTInfo
+id-smime-ct 5		: id-smime-ct-TDTInfo
+id-smime-ct 6		: id-smime-ct-contentInfo
+id-smime-ct 7		: id-smime-ct-DVCSRequestData
+id-smime-ct 8		: id-smime-ct-DVCSResponseData
+id-smime-ct 9		: id-smime-ct-compressedData
+id-smime-ct 19		: id-smime-ct-contentCollection
+id-smime-ct 23		: id-smime-ct-authEnvelopedData
+id-smime-ct 27		: id-ct-asciiTextWithCRLF
+id-smime-ct 28		: id-ct-xml
+
+# S/MIME Attributes
+id-smime-aa 1		: id-smime-aa-receiptRequest
+id-smime-aa 2		: id-smime-aa-securityLabel
+id-smime-aa 3		: id-smime-aa-mlExpandHistory
+id-smime-aa 4		: id-smime-aa-contentHint
+id-smime-aa 5		: id-smime-aa-msgSigDigest
+# obsolete
+id-smime-aa 6		: id-smime-aa-encapContentType
+id-smime-aa 7		: id-smime-aa-contentIdentifier
+# obsolete
+id-smime-aa 8		: id-smime-aa-macValue
+id-smime-aa 9		: id-smime-aa-equivalentLabels
+id-smime-aa 10		: id-smime-aa-contentReference
+id-smime-aa 11		: id-smime-aa-encrypKeyPref
+id-smime-aa 12		: id-smime-aa-signingCertificate
+id-smime-aa 13		: id-smime-aa-smimeEncryptCerts
+id-smime-aa 14		: id-smime-aa-timeStampToken
+id-smime-aa 15		: id-smime-aa-ets-sigPolicyId
+id-smime-aa 16		: id-smime-aa-ets-commitmentType
+id-smime-aa 17		: id-smime-aa-ets-signerLocation
+id-smime-aa 18		: id-smime-aa-ets-signerAttr
+id-smime-aa 19		: id-smime-aa-ets-otherSigCert
+id-smime-aa 20		: id-smime-aa-ets-contentTimestamp
+id-smime-aa 21		: id-smime-aa-ets-CertificateRefs
+id-smime-aa 22		: id-smime-aa-ets-RevocationRefs
+id-smime-aa 23		: id-smime-aa-ets-certValues
+id-smime-aa 24		: id-smime-aa-ets-revocationValues
+id-smime-aa 25		: id-smime-aa-ets-escTimeStamp
+id-smime-aa 26		: id-smime-aa-ets-certCRLTimestamp
+id-smime-aa 27		: id-smime-aa-ets-archiveTimeStamp
+id-smime-aa 28		: id-smime-aa-signatureType
+id-smime-aa 29		: id-smime-aa-dvcs-dvc
+
+# S/MIME Algorithm Identifiers
+# obsolete
+id-smime-alg 1		: id-smime-alg-ESDHwith3DES
+# obsolete
+id-smime-alg 2		: id-smime-alg-ESDHwithRC2
+# obsolete
+id-smime-alg 3		: id-smime-alg-3DESwrap
+# obsolete
+id-smime-alg 4		: id-smime-alg-RC2wrap
+id-smime-alg 5		: id-smime-alg-ESDH
+id-smime-alg 6		: id-smime-alg-CMS3DESwrap
+id-smime-alg 7		: id-smime-alg-CMSRC2wrap
+id-smime-alg 9		: id-alg-PWRI-KEK
+
+# S/MIME Certificate Distribution
+id-smime-cd 1		: id-smime-cd-ldap
+
+# S/MIME Signature Policy Qualifier
+id-smime-spq 1		: id-smime-spq-ets-sqt-uri
+id-smime-spq 2		: id-smime-spq-ets-sqt-unotice
+
+# S/MIME Commitment Type Identifier
+id-smime-cti 1		: id-smime-cti-ets-proofOfOrigin
+id-smime-cti 2		: id-smime-cti-ets-proofOfReceipt
+id-smime-cti 3		: id-smime-cti-ets-proofOfDelivery
+id-smime-cti 4		: id-smime-cti-ets-proofOfSender
+id-smime-cti 5		: id-smime-cti-ets-proofOfApproval
+id-smime-cti 6		: id-smime-cti-ets-proofOfCreation
+
+pkcs9 20		:			: friendlyName
+pkcs9 21		:			: localKeyID
+!Cname ms-csp-name
+1 3 6 1 4 1 311 17 1	: CSPName		: Microsoft CSP Name
+1 3 6 1 4 1 311 17 2	: LocalKeySet		: Microsoft Local Key set
+!Alias certTypes pkcs9 22
+certTypes 1		:			: x509Certificate
+certTypes 2		:			: sdsiCertificate
+!Alias crlTypes pkcs9 23
+crlTypes 1		:			: x509Crl
+
+!Alias pkcs12 pkcs 12
+!Alias pkcs12-pbeids pkcs12 1
+
+!Cname pbe-WithSHA1And128BitRC4
+pkcs12-pbeids 1		: PBE-SHA1-RC4-128	: pbeWithSHA1And128BitRC4
+!Cname pbe-WithSHA1And40BitRC4
+pkcs12-pbeids 2		: PBE-SHA1-RC4-40	: pbeWithSHA1And40BitRC4
+!Cname pbe-WithSHA1And3_Key_TripleDES-CBC
+pkcs12-pbeids 3		: PBE-SHA1-3DES		: pbeWithSHA1And3-KeyTripleDES-CBC
+!Cname pbe-WithSHA1And2_Key_TripleDES-CBC
+pkcs12-pbeids 4		: PBE-SHA1-2DES		: pbeWithSHA1And2-KeyTripleDES-CBC
+!Cname pbe-WithSHA1And128BitRC2-CBC
+pkcs12-pbeids 5		: PBE-SHA1-RC2-128	: pbeWithSHA1And128BitRC2-CBC
+!Cname pbe-WithSHA1And40BitRC2-CBC
+pkcs12-pbeids 6		: PBE-SHA1-RC2-40	: pbeWithSHA1And40BitRC2-CBC
+
+# GmSSL Append
+!Cname pbe-WithSM3AndSMS4-CBC
+pkcs12-pbeids 100	: PBE-SM3-SMS4		: pbeWithSM3AndSMS4-CBC
+
+!Alias pkcs12-Version1 pkcs12 10
+!Alias pkcs12-BagIds pkcs12-Version1 1
+pkcs12-BagIds 1		:			: keyBag
+pkcs12-BagIds 2		:			: pkcs8ShroudedKeyBag
+pkcs12-BagIds 3		:			: certBag
+pkcs12-BagIds 4		:			: crlBag
+pkcs12-BagIds 5		:			: secretBag
+pkcs12-BagIds 6		:			: safeContentsBag
+
+rsadsi 2 2		: MD2			: md2
+rsadsi 2 4		: MD4			: md4
+rsadsi 2 5		: MD5			: md5
+			: MD5-SHA1		: md5-sha1
+rsadsi 2 6		:			: hmacWithMD5
+rsadsi 2 7		:			: hmacWithSHA1
+
+# From RFC4231
+rsadsi 2 8		:			: hmacWithSHA224
+rsadsi 2 9		:			: hmacWithSHA256
+rsadsi 2 10		:			: hmacWithSHA384
+rsadsi 2 11		:			: hmacWithSHA512
+
+rsadsi 3 2		: RC2-CBC		: rc2-cbc
+			: RC2-ECB		: rc2-ecb
+!Cname rc2-cfb64
+			: RC2-CFB		: rc2-cfb
+!Cname rc2-ofb64
+			: RC2-OFB		: rc2-ofb
+			: RC2-40-CBC		: rc2-40-cbc
+			: RC2-64-CBC		: rc2-64-cbc
+rsadsi 3 4		: RC4			: rc4
+			: RC4-40		: rc4-40
+rsadsi 3 7		: DES-EDE3-CBC		: des-ede3-cbc
+rsadsi 3 8		: RC5-CBC		: rc5-cbc
+			: RC5-ECB		: rc5-ecb
+!Cname rc5-cfb64
+			: RC5-CFB		: rc5-cfb
+!Cname rc5-ofb64
+			: RC5-OFB		: rc5-ofb
+
+!Cname ms-ext-req
+1 3 6 1 4 1 311 2 1 14	: msExtReq		: Microsoft Extension Request
+!Cname ms-code-ind
+1 3 6 1 4 1 311 2 1 21	: msCodeInd		: Microsoft Individual Code Signing
+!Cname ms-code-com
+1 3 6 1 4 1 311 2 1 22	: msCodeCom		: Microsoft Commercial Code Signing
+!Cname ms-ctl-sign
+1 3 6 1 4 1 311 10 3 1	: msCTLSign		: Microsoft Trust List Signing
+!Cname ms-sgc
+1 3 6 1 4 1 311 10 3 3	: msSGC			: Microsoft Server Gated Crypto
+!Cname ms-efs
+1 3 6 1 4 1 311 10 3 4	: msEFS			: Microsoft Encrypted File System
+!Cname ms-smartcard-login
+1 3 6 1 4 1 311 20 2 2	: msSmartcardLogin	: Microsoft Smartcardlogin
+!Cname ms-upn
+1 3 6 1 4 1 311 20 2 3	: msUPN			: Microsoft Universal Principal Name
+
+1 3 6 1 4 1 188 7 1 1 2	: IDEA-CBC		: idea-cbc
+			: IDEA-ECB		: idea-ecb
+!Cname idea-cfb64
+			: IDEA-CFB		: idea-cfb
+!Cname idea-ofb64
+			: IDEA-OFB		: idea-ofb
+
+1 3 6 1 4 1 3029 1 2	: BF-CBC		: bf-cbc
+			: BF-ECB		: bf-ecb
+!Cname bf-cfb64
+			: BF-CFB		: bf-cfb
+!Cname bf-ofb64
+			: BF-OFB		: bf-ofb
+
+!Cname id-pkix
+1 3 6 1 5 5 7		: PKIX
+
+# PKIX Arcs
+id-pkix 0		: id-pkix-mod
+id-pkix 1		: id-pe
+id-pkix 2		: id-qt
+id-pkix 3		: id-kp
+id-pkix 4		: id-it
+id-pkix 5		: id-pkip
+id-pkix 6		: id-alg
+id-pkix 7		: id-cmc
+id-pkix 8		: id-on
+id-pkix 9		: id-pda
+id-pkix 10		: id-aca
+id-pkix 11		: id-qcs
+id-pkix 12		: id-cct
+id-pkix 21		: id-ppl
+id-pkix 48		: id-ad
+
+# PKIX Modules
+id-pkix-mod 1		: id-pkix1-explicit-88
+id-pkix-mod 2		: id-pkix1-implicit-88
+id-pkix-mod 3		: id-pkix1-explicit-93
+id-pkix-mod 4		: id-pkix1-implicit-93
+id-pkix-mod 5		: id-mod-crmf
+id-pkix-mod 6		: id-mod-cmc
+id-pkix-mod 7		: id-mod-kea-profile-88
+id-pkix-mod 8		: id-mod-kea-profile-93
+id-pkix-mod 9		: id-mod-cmp
+id-pkix-mod 10		: id-mod-qualified-cert-88
+id-pkix-mod 11		: id-mod-qualified-cert-93
+id-pkix-mod 12		: id-mod-attribute-cert
+id-pkix-mod 13		: id-mod-timestamp-protocol
+id-pkix-mod 14		: id-mod-ocsp
+id-pkix-mod 15		: id-mod-dvcs
+id-pkix-mod 16		: id-mod-cmp2000
+
+# PKIX Private Extensions
+!Cname info-access
+id-pe 1			: authorityInfoAccess	: Authority Information Access
+id-pe 2			: biometricInfo		: Biometric Info
+id-pe 3			: qcStatements
+id-pe 4			: ac-auditEntity
+id-pe 5			: ac-targeting
+id-pe 6			: aaControls
+id-pe 7			: sbgp-ipAddrBlock
+id-pe 8			: sbgp-autonomousSysNum
+id-pe 9			: sbgp-routerIdentifier
+id-pe 10		: ac-proxying
+!Cname sinfo-access
+id-pe 11		: subjectInfoAccess	: Subject Information Access
+id-pe 14		: proxyCertInfo		: Proxy Certificate Information
+id-pe 24		: tlsfeature		: TLS Feature
+
+# PKIX policyQualifiers for Internet policy qualifiers
+id-qt 1			: id-qt-cps		: Policy Qualifier CPS
+id-qt 2			: id-qt-unotice		: Policy Qualifier User Notice
+id-qt 3			: textNotice
+
+# PKIX key purpose identifiers
+!Cname server-auth
+id-kp 1			: serverAuth		: TLS Web Server Authentication
+!Cname client-auth
+id-kp 2			: clientAuth		: TLS Web Client Authentication
+!Cname code-sign
+id-kp 3			: codeSigning		: Code Signing
+!Cname email-protect
+id-kp 4			: emailProtection	: E-mail Protection
+id-kp 5			: ipsecEndSystem	: IPSec End System
+id-kp 6			: ipsecTunnel		: IPSec Tunnel
+id-kp 7			: ipsecUser		: IPSec User
+!Cname time-stamp
+id-kp 8			: timeStamping		: Time Stamping
+# From OCSP spec RFC2560
+!Cname OCSP-sign
+id-kp 9			: OCSPSigning		: OCSP Signing
+id-kp 10		: DVCS			: dvcs
+!Cname ipsec-IKE
+id-kp 17                : ipsecIKE              : ipsec Internet Key Exchange
+id-kp 18                : capwapAC              : Ctrl/provision WAP Access
+id-kp 19                : capwapWTP             : Ctrl/Provision WAP Termination
+!Cname sshClient
+id-kp 21                : secureShellClient     : SSH Client
+!Cname sshServer
+id-kp 22                : secureShellServer     : SSH Server
+id-kp 23                : sendRouter            : Send Router
+id-kp 24                : sendProxiedRouter     : Send Proxied Router
+id-kp 25                : sendOwner             : Send Owner
+id-kp 26                : sendProxiedOwner      : Send Proxied Owner
+
+# CMP information types
+id-it 1			: id-it-caProtEncCert
+id-it 2			: id-it-signKeyPairTypes
+id-it 3			: id-it-encKeyPairTypes
+id-it 4			: id-it-preferredSymmAlg
+id-it 5			: id-it-caKeyUpdateInfo
+id-it 6			: id-it-currentCRL
+id-it 7			: id-it-unsupportedOIDs
+# obsolete
+id-it 8			: id-it-subscriptionRequest
+# obsolete
+id-it 9			: id-it-subscriptionResponse
+id-it 10		: id-it-keyPairParamReq
+id-it 11		: id-it-keyPairParamRep
+id-it 12		: id-it-revPassphrase
+id-it 13		: id-it-implicitConfirm
+id-it 14		: id-it-confirmWaitTime
+id-it 15		: id-it-origPKIMessage
+id-it 16		: id-it-suppLangTags
+
+# CRMF registration
+id-pkip 1		: id-regCtrl
+id-pkip 2		: id-regInfo
+
+# CRMF registration controls
+id-regCtrl 1		: id-regCtrl-regToken
+id-regCtrl 2		: id-regCtrl-authenticator
+id-regCtrl 3		: id-regCtrl-pkiPublicationInfo
+id-regCtrl 4		: id-regCtrl-pkiArchiveOptions
+id-regCtrl 5		: id-regCtrl-oldCertID
+id-regCtrl 6		: id-regCtrl-protocolEncrKey
+
+# CRMF registration information
+id-regInfo 1		: id-regInfo-utf8Pairs
+id-regInfo 2		: id-regInfo-certReq
+
+# algorithms
+id-alg 1		: id-alg-des40
+id-alg 2		: id-alg-noSignature
+id-alg 3		: id-alg-dh-sig-hmac-sha1
+id-alg 4		: id-alg-dh-pop
+
+# CMC controls
+id-cmc 1		: id-cmc-statusInfo
+id-cmc 2		: id-cmc-identification
+id-cmc 3		: id-cmc-identityProof
+id-cmc 4		: id-cmc-dataReturn
+id-cmc 5		: id-cmc-transactionId
+id-cmc 6		: id-cmc-senderNonce
+id-cmc 7		: id-cmc-recipientNonce
+id-cmc 8		: id-cmc-addExtensions
+id-cmc 9		: id-cmc-encryptedPOP
+id-cmc 10		: id-cmc-decryptedPOP
+id-cmc 11		: id-cmc-lraPOPWitness
+id-cmc 15		: id-cmc-getCert
+id-cmc 16		: id-cmc-getCRL
+id-cmc 17		: id-cmc-revokeRequest
+id-cmc 18		: id-cmc-regInfo
+id-cmc 19		: id-cmc-responseInfo
+id-cmc 21		: id-cmc-queryPending
+id-cmc 22		: id-cmc-popLinkRandom
+id-cmc 23		: id-cmc-popLinkWitness
+id-cmc 24		: id-cmc-confirmCertAcceptance 
+
+# other names
+id-on 1			: id-on-personalData
+id-on 3			: id-on-permanentIdentifier : Permanent Identifier
+
+# personal data attributes
+id-pda 1		: id-pda-dateOfBirth
+id-pda 2		: id-pda-placeOfBirth
+id-pda 3		: id-pda-gender
+id-pda 4		: id-pda-countryOfCitizenship
+id-pda 5		: id-pda-countryOfResidence
+
+# attribute certificate attributes
+id-aca 1		: id-aca-authenticationInfo
+id-aca 2		: id-aca-accessIdentity
+id-aca 3		: id-aca-chargingIdentity
+id-aca 4		: id-aca-group
+# attention : the following seems to be obsolete, replace by 'role'
+id-aca 5		: id-aca-role
+id-aca 6		: id-aca-encAttrs
+
+# qualified certificate statements
+id-qcs 1		: id-qcs-pkixQCSyntax-v1
+
+# CMC content types
+id-cct 1		: id-cct-crs
+id-cct 2		: id-cct-PKIData
+id-cct 3		: id-cct-PKIResponse
+
+# Predefined Proxy Certificate policy languages
+id-ppl 0		: id-ppl-anyLanguage	: Any language
+id-ppl 1		: id-ppl-inheritAll	: Inherit all
+id-ppl 2		: id-ppl-independent	: Independent
+
+# access descriptors for authority info access extension
+!Cname ad-OCSP
+id-ad 1			: OCSP			: OCSP
+!Cname ad-ca-issuers
+id-ad 2			: caIssuers		: CA Issuers
+!Cname ad-timeStamping
+id-ad 3			: ad_timestamping	: AD Time Stamping
+!Cname ad-dvcs
+id-ad 4			: AD_DVCS		: ad dvcs
+id-ad 5			: caRepository		: CA Repository
+
+
+!Alias id-pkix-OCSP ad-OCSP
+!module id-pkix-OCSP
+!Cname basic
+id-pkix-OCSP 1		: basicOCSPResponse	: Basic OCSP Response
+id-pkix-OCSP 2		: Nonce			: OCSP Nonce
+id-pkix-OCSP 3		: CrlID			: OCSP CRL ID
+id-pkix-OCSP 4		: acceptableResponses	: Acceptable OCSP Responses
+id-pkix-OCSP 5		: noCheck		: OCSP No Check
+id-pkix-OCSP 6		: archiveCutoff		: OCSP Archive Cutoff
+id-pkix-OCSP 7		: serviceLocator	: OCSP Service Locator
+id-pkix-OCSP 8		: extendedStatus	: Extended OCSP Status
+id-pkix-OCSP 9		: valid
+id-pkix-OCSP 10		: path
+id-pkix-OCSP 11		: trustRoot		: Trust Root
+!global
+
+1 3 14 3 2		: algorithm		: algorithm
+algorithm 3		: RSA-NP-MD5		: md5WithRSA
+algorithm 6		: DES-ECB		: des-ecb
+algorithm 7		: DES-CBC		: des-cbc
+!Cname des-ofb64
+algorithm 8		: DES-OFB		: des-ofb
+!Cname des-cfb64
+algorithm 9		: DES-CFB		: des-cfb
+algorithm 11		: rsaSignature
+!Cname dsa-2
+algorithm 12		: DSA-old		: dsaEncryption-old
+algorithm 13		: DSA-SHA		: dsaWithSHA
+algorithm 15		: RSA-SHA		: shaWithRSAEncryption
+!Cname des-ede-ecb
+algorithm 17		: DES-EDE		: des-ede
+!Cname des-ede3-ecb
+			: DES-EDE3		: des-ede3
+			: DES-EDE-CBC		: des-ede-cbc
+!Cname des-ede-cfb64
+			: DES-EDE-CFB		: des-ede-cfb
+!Cname des-ede3-cfb64
+			: DES-EDE3-CFB		: des-ede3-cfb
+!Cname des-ede-ofb64
+			: DES-EDE-OFB		: des-ede-ofb
+!Cname des-ede3-ofb64
+			: DES-EDE3-OFB		: des-ede3-ofb
+			: DESX-CBC		: desx-cbc
+algorithm 18		: SHA			: sha
+algorithm 26		: SHA1			: sha1
+!Cname dsaWithSHA1-2
+algorithm 27		: DSA-SHA1-old		: dsaWithSHA1-old
+algorithm 29		: RSA-SHA1-2		: sha1WithRSA
+
+1 3 36 3 2 1		: RIPEMD160		: ripemd160
+1 3 36 3 3 1 2		: RSA-RIPEMD160		: ripemd160WithRSA
+
+1 3 6 1 4 1 1722 12 2 1 16 : BLAKE2b512        : blake2b512
+1 3 6 1 4 1 1722 12 2 2 8  : BLAKE2s256        : blake2s256
+
+!Cname sxnet
+1 3 101 1 4 1		: SXNetID		: Strong Extranet ID
+
+2 5			: X500			: directory services (X.500)
+
+X500 4			: X509
+X509 3			: CN			: commonName
+X509 4			: SN			: surname
+X509 5			: 			: serialNumber
+X509 6			: C			: countryName
+X509 7			: L			: localityName
+X509 8			: ST			: stateOrProvinceName
+X509 9			: street		: streetAddress
+X509 10			: O			: organizationName
+X509 11			: OU			: organizationalUnitName
+X509 12			: title			: title
+X509 13			: 			: description
+X509 14			: 			: searchGuide
+X509 15			: 			: businessCategory
+X509 16			: 			: postalAddress
+X509 17			: 			: postalCode
+X509 18			: 			: postOfficeBox
+X509 19			: 			: physicalDeliveryOfficeName
+X509 20			: 			: telephoneNumber
+X509 21			: 			: telexNumber
+X509 22			: 			: teletexTerminalIdentifier
+X509 23			: 			: facsimileTelephoneNumber
+X509 24			: 			: x121Address
+X509 25			: 			: internationaliSDNNumber
+X509 26			: 			: registeredAddress
+X509 27			: 			: destinationIndicator
+X509 28			: 			: preferredDeliveryMethod
+X509 29			: 			: presentationAddress
+X509 30			: 			: supportedApplicationContext
+X509 31			: member		:
+X509 32			: owner			:
+X509 33			: 			: roleOccupant
+X509 34			: seeAlso		:
+X509 35			: 			: userPassword
+X509 36			: 			: userCertificate
+X509 37			: 			: cACertificate
+X509 38			: 			: authorityRevocationList
+X509 39			: 			: certificateRevocationList
+X509 40			: 			: crossCertificatePair
+X509 41			: name			: name
+X509 42			: GN			: givenName
+X509 43			: initials		: initials
+X509 44			: 			: generationQualifier
+X509 45			: 			: x500UniqueIdentifier
+X509 46			: dnQualifier		: dnQualifier
+X509 47			: 			: enhancedSearchGuide
+X509 48			: 			: protocolInformation
+X509 49			: 			: distinguishedName
+X509 50			: 			: uniqueMember
+X509 51			: 			: houseIdentifier
+X509 52			: 			: supportedAlgorithms
+X509 53			: 			: deltaRevocationList
+X509 54			: dmdName		:
+X509 65			:			: pseudonym
+X509 72			: role			: role
+
+X500 8			: X500algorithms	: directory services - algorithms
+X500algorithms 1 1	: RSA			: rsa
+X500algorithms 3 100	: RSA-MDC2		: mdc2WithRSA
+X500algorithms 3 101	: MDC2			: mdc2
+
+X500 29			: id-ce
+!Cname subject-directory-attributes
+id-ce 9			: subjectDirectoryAttributes : X509v3 Subject Directory Attributes
+!Cname subject-key-identifier
+id-ce 14		: subjectKeyIdentifier	: X509v3 Subject Key Identifier
+!Cname key-usage
+id-ce 15		: keyUsage		: X509v3 Key Usage
+!Cname private-key-usage-period
+id-ce 16		: privateKeyUsagePeriod	: X509v3 Private Key Usage Period
+!Cname subject-alt-name
+id-ce 17		: subjectAltName	: X509v3 Subject Alternative Name
+!Cname issuer-alt-name
+id-ce 18		: issuerAltName		: X509v3 Issuer Alternative Name
+!Cname basic-constraints
+id-ce 19		: basicConstraints	: X509v3 Basic Constraints
+!Cname crl-number
+id-ce 20		: crlNumber		: X509v3 CRL Number
+!Cname crl-reason
+id-ce 21		: CRLReason		: X509v3 CRL Reason Code
+!Cname invalidity-date
+id-ce 24		: invalidityDate	: Invalidity Date
+!Cname delta-crl
+id-ce 27		: deltaCRL		: X509v3 Delta CRL Indicator
+!Cname issuing-distribution-point
+id-ce 28		: issuingDistributionPoint : X509v3 Issuing Distribution Point
+!Cname certificate-issuer
+id-ce 29		: certificateIssuer	: X509v3 Certificate Issuer
+!Cname name-constraints
+id-ce 30		: nameConstraints	: X509v3 Name Constraints
+!Cname crl-distribution-points
+id-ce 31		: crlDistributionPoints	: X509v3 CRL Distribution Points
+!Cname certificate-policies
+id-ce 32		: certificatePolicies	: X509v3 Certificate Policies
+!Cname any-policy
+certificate-policies 0	: anyPolicy		: X509v3 Any Policy
+!Cname policy-mappings
+id-ce 33		: policyMappings	: X509v3 Policy Mappings
+!Cname authority-key-identifier
+id-ce 35		: authorityKeyIdentifier : X509v3 Authority Key Identifier
+!Cname policy-constraints
+id-ce 36		: policyConstraints	: X509v3 Policy Constraints
+!Cname ext-key-usage
+id-ce 37		: extendedKeyUsage	: X509v3 Extended Key Usage
+!Cname freshest-crl
+id-ce 46		: freshestCRL		: X509v3 Freshest CRL
+!Cname inhibit-any-policy
+id-ce 54		: inhibitAnyPolicy	: X509v3 Inhibit Any Policy
+!Cname target-information
+id-ce 55		: targetInformation	: X509v3 AC Targeting
+!Cname no-rev-avail
+id-ce 56		: noRevAvail		: X509v3 No Revocation Available
+
+# From RFC5280
+ext-key-usage 0		: anyExtendedKeyUsage	: Any Extended Key Usage
+
+
+!Cname netscape
+2 16 840 1 113730	: Netscape		: Netscape Communications Corp.
+!Cname netscape-cert-extension
+netscape 1		: nsCertExt		: Netscape Certificate Extension
+!Cname netscape-data-type
+netscape 2		: nsDataType		: Netscape Data Type
+!Cname netscape-cert-type
+netscape-cert-extension 1 : nsCertType		: Netscape Cert Type
+!Cname netscape-base-url
+netscape-cert-extension 2 : nsBaseUrl		: Netscape Base Url
+!Cname netscape-revocation-url
+netscape-cert-extension 3 : nsRevocationUrl	: Netscape Revocation Url
+!Cname netscape-ca-revocation-url
+netscape-cert-extension 4 : nsCaRevocationUrl	: Netscape CA Revocation Url
+!Cname netscape-renewal-url
+netscape-cert-extension 7 : nsRenewalUrl	: Netscape Renewal Url
+!Cname netscape-ca-policy-url
+netscape-cert-extension 8 : nsCaPolicyUrl	: Netscape CA Policy Url
+!Cname netscape-ssl-server-name
+netscape-cert-extension 12 : nsSslServerName	: Netscape SSL Server Name
+!Cname netscape-comment
+netscape-cert-extension 13 : nsComment		: Netscape Comment
+!Cname netscape-cert-sequence
+netscape-data-type 5	: nsCertSequence	: Netscape Certificate Sequence
+!Cname ns-sgc
+netscape 4 1		: nsSGC			: Netscape Server Gated Crypto
+
+# iso(1)
+iso 3			: ORG			: org
+org 6			: DOD			: dod
+dod 1			: IANA			: iana
+!Alias internet iana
+
+internet 1		: directory		: Directory
+internet 2		: mgmt			: Management
+internet 3		: experimental		: Experimental
+internet 4		: private		: Private
+internet 5		: security		: Security
+internet 6		: snmpv2		: SNMPv2
+# Documents refer to "internet 7" as "mail". This however leads to ambiguities
+# with RFC2798, Section 9.1.3, where "mail" is defined as the short name for
+# rfc822Mailbox. The short name is therefore here left out for a reason.
+# Subclasses of "mail", e.g. "MIME MHS" don't consitute a problem, as
+# references are realized via long name "Mail" (with capital M).
+internet 7		:			: Mail
+
+Private 1		: enterprises		: Enterprises
+
+# RFC 2247
+Enterprises 1466 344	: dcobject		: dcObject
+
+# RFC 1495
+Mail 1			: mime-mhs		: MIME MHS
+mime-mhs 1		: mime-mhs-headings	: mime-mhs-headings
+mime-mhs 2		: mime-mhs-bodies	: mime-mhs-bodies
+mime-mhs-headings 1	: id-hex-partial-message : id-hex-partial-message
+mime-mhs-headings 2	: id-hex-multipart-message : id-hex-multipart-message
+
+# RFC 3274
+!Cname zlib-compression
+id-smime-alg 8		: ZLIB			: zlib compression
+
+# AES aka Rijndael
+
+!Alias csor 2 16 840 1 101 3
+!Alias nistAlgorithms csor 4
+!Alias aes nistAlgorithms 1
+
+aes 1			: AES-128-ECB		: aes-128-ecb
+aes 2			: AES-128-CBC		: aes-128-cbc
+!Cname aes-128-ofb128
+aes 3			: AES-128-OFB		: aes-128-ofb
+!Cname aes-128-cfb128
+aes 4			: AES-128-CFB		: aes-128-cfb
+aes 5			: id-aes128-wrap
+aes 6			: id-aes128-GCM		: aes-128-gcm
+aes 7			: id-aes128-CCM		: aes-128-ccm
+aes 8			: id-aes128-wrap-pad
+
+aes 21			: AES-192-ECB		: aes-192-ecb
+aes 22			: AES-192-CBC		: aes-192-cbc
+!Cname aes-192-ofb128
+aes 23			: AES-192-OFB		: aes-192-ofb
+!Cname aes-192-cfb128
+aes 24			: AES-192-CFB		: aes-192-cfb
+aes 25			: id-aes192-wrap
+aes 26			: id-aes192-GCM		: aes-192-gcm
+aes 27			: id-aes192-CCM		: aes-192-ccm
+aes 28			: id-aes192-wrap-pad
+
+aes 41			: AES-256-ECB		: aes-256-ecb
+aes 42			: AES-256-CBC		: aes-256-cbc
+!Cname aes-256-ofb128
+aes 43			: AES-256-OFB		: aes-256-ofb
+!Cname aes-256-cfb128
+aes 44			: AES-256-CFB		: aes-256-cfb
+aes 45			: id-aes256-wrap
+aes 46			: id-aes256-GCM		: aes-256-gcm
+aes 47			: id-aes256-CCM		: aes-256-ccm
+aes 48			: id-aes256-wrap-pad
+
+# There are no OIDs for these modes...
+
+			: AES-128-CFB1		: aes-128-cfb1
+			: AES-192-CFB1		: aes-192-cfb1
+			: AES-256-CFB1		: aes-256-cfb1
+			: AES-128-CFB8		: aes-128-cfb8
+			: AES-192-CFB8		: aes-192-cfb8
+			: AES-256-CFB8		: aes-256-cfb8
+			: AES-128-CTR		: aes-128-ctr
+			: AES-192-CTR		: aes-192-ctr
+			: AES-256-CTR		: aes-256-ctr
+			: AES-128-OCB		: aes-128-ocb
+			: AES-192-OCB		: aes-192-ocb
+			: AES-256-OCB		: aes-256-ocb
+			: AES-128-XTS		: aes-128-xts
+			: AES-256-XTS		: aes-256-xts
+			: DES-CFB1		: des-cfb1
+			: DES-CFB8		: des-cfb8
+			: DES-EDE3-CFB1		: des-ede3-cfb1
+			: DES-EDE3-CFB8		: des-ede3-cfb8
+
+# OIDs for SHA224, SHA256, SHA385 and SHA512, according to x9.84.
+!Alias nist_hashalgs nistAlgorithms 2
+nist_hashalgs 1		: SHA256		: sha256
+nist_hashalgs 2		: SHA384		: sha384
+nist_hashalgs 3		: SHA512		: sha512
+nist_hashalgs 4		: SHA224		: sha224
+
+# OIDs for dsa-with-sha224 and dsa-with-sha256
+!Alias dsa_with_sha2 nistAlgorithms 3
+dsa_with_sha2 1		: dsa_with_SHA224
+dsa_with_sha2 2		: dsa_with_SHA256
+
+# Hold instruction CRL entry extension
+!Cname hold-instruction-code
+id-ce 23		: holdInstructionCode	: Hold Instruction Code
+!Alias holdInstruction	X9-57 2
+!Cname hold-instruction-none
+holdInstruction 1	: holdInstructionNone	: Hold Instruction None
+!Cname hold-instruction-call-issuer
+holdInstruction 2	: holdInstructionCallIssuer : Hold Instruction Call Issuer
+!Cname hold-instruction-reject
+holdInstruction 3	: holdInstructionReject	: Hold Instruction Reject
+
+# OID's from ITU-T.  Most of this is defined in RFC 1274.  A couple of
+# them are also mentioned in RFC 2247
+itu-t 9			: data
+data 2342		: pss
+pss 19200300		: ucl
+ucl 100			: pilot
+pilot 1			:			: pilotAttributeType
+pilot 3			:			: pilotAttributeSyntax
+pilot 4			:			: pilotObjectClass
+pilot 10		:			: pilotGroups
+pilotAttributeSyntax 4	:			: iA5StringSyntax
+pilotAttributeSyntax 5	:			: caseIgnoreIA5StringSyntax
+pilotObjectClass 3	:			: pilotObject
+pilotObjectClass 4	:			: pilotPerson
+pilotObjectClass 5	: account
+pilotObjectClass 6	: document
+pilotObjectClass 7	: room
+pilotObjectClass 9	:			: documentSeries
+pilotObjectClass 13	: domain		: Domain
+pilotObjectClass 14	:			: rFC822localPart
+pilotObjectClass 15	:			: dNSDomain
+pilotObjectClass 17	:			: domainRelatedObject
+pilotObjectClass 18	:			: friendlyCountry
+pilotObjectClass 19	:			: simpleSecurityObject
+pilotObjectClass 20	:			: pilotOrganization
+pilotObjectClass 21	:			: pilotDSA
+pilotObjectClass 22	:			: qualityLabelledData
+pilotAttributeType 1	: UID			: userId
+pilotAttributeType 2	:			: textEncodedORAddress
+pilotAttributeType 3	: mail			: rfc822Mailbox
+pilotAttributeType 4	: info
+pilotAttributeType 5	:			: favouriteDrink
+pilotAttributeType 6	:			: roomNumber
+pilotAttributeType 7	: photo
+pilotAttributeType 8	:			: userClass
+pilotAttributeType 9	: host
+pilotAttributeType 10	: manager
+pilotAttributeType 11	:			: documentIdentifier
+pilotAttributeType 12	:			: documentTitle
+pilotAttributeType 13	:			: documentVersion
+pilotAttributeType 14	:			: documentAuthor
+pilotAttributeType 15	:			: documentLocation
+pilotAttributeType 20	:			: homeTelephoneNumber
+pilotAttributeType 21	: secretary
+pilotAttributeType 22	:			: otherMailbox
+pilotAttributeType 23	:			: lastModifiedTime
+pilotAttributeType 24	:			: lastModifiedBy
+pilotAttributeType 25	: DC			: domainComponent
+pilotAttributeType 26	:			: aRecord
+pilotAttributeType 27	:			: pilotAttributeType27
+pilotAttributeType 28	:			: mXRecord
+pilotAttributeType 29	:			: nSRecord
+pilotAttributeType 30	:			: sOARecord
+pilotAttributeType 31	:			: cNAMERecord
+pilotAttributeType 37	:			: associatedDomain
+pilotAttributeType 38	:			: associatedName
+pilotAttributeType 39	:			: homePostalAddress
+pilotAttributeType 40	:			: personalTitle
+pilotAttributeType 41	:			: mobileTelephoneNumber
+pilotAttributeType 42	:			: pagerTelephoneNumber
+pilotAttributeType 43	:			: friendlyCountryName
+pilotAttributeType 44	: uid			: uniqueIdentifier
+pilotAttributeType 45	:			: organizationalStatus
+pilotAttributeType 46	:			: janetMailbox
+pilotAttributeType 47	:			: mailPreferenceOption
+pilotAttributeType 48	:			: buildingName
+pilotAttributeType 49	:			: dSAQuality
+pilotAttributeType 50	:			: singleLevelQuality
+pilotAttributeType 51	:			: subtreeMinimumQuality
+pilotAttributeType 52	:			: subtreeMaximumQuality
+pilotAttributeType 53	:			: personalSignature
+pilotAttributeType 54	:			: dITRedirect
+pilotAttributeType 55	: audio
+pilotAttributeType 56	:			: documentPublisher
+
+international-organizations 42	: id-set	: Secure Electronic Transactions
+
+id-set 0		: set-ctype		: content types
+id-set 1		: set-msgExt		: message extensions
+id-set 3		: set-attr
+id-set 5		: set-policy
+id-set 7		: set-certExt		: certificate extensions
+id-set 8		: set-brand
+
+set-ctype 0		: setct-PANData
+set-ctype 1		: setct-PANToken
+set-ctype 2		: setct-PANOnly
+set-ctype 3		: setct-OIData
+set-ctype 4		: setct-PI
+set-ctype 5		: setct-PIData
+set-ctype 6		: setct-PIDataUnsigned
+set-ctype 7		: setct-HODInput
+set-ctype 8		: setct-AuthResBaggage
+set-ctype 9		: setct-AuthRevReqBaggage
+set-ctype 10		: setct-AuthRevResBaggage
+set-ctype 11		: setct-CapTokenSeq
+set-ctype 12		: setct-PInitResData
+set-ctype 13		: setct-PI-TBS
+set-ctype 14		: setct-PResData
+set-ctype 16		: setct-AuthReqTBS
+set-ctype 17		: setct-AuthResTBS
+set-ctype 18		: setct-AuthResTBSX
+set-ctype 19		: setct-AuthTokenTBS
+set-ctype 20		: setct-CapTokenData
+set-ctype 21		: setct-CapTokenTBS
+set-ctype 22		: setct-AcqCardCodeMsg
+set-ctype 23		: setct-AuthRevReqTBS
+set-ctype 24		: setct-AuthRevResData
+set-ctype 25		: setct-AuthRevResTBS
+set-ctype 26		: setct-CapReqTBS
+set-ctype 27		: setct-CapReqTBSX
+set-ctype 28		: setct-CapResData
+set-ctype 29		: setct-CapRevReqTBS
+set-ctype 30		: setct-CapRevReqTBSX
+set-ctype 31		: setct-CapRevResData
+set-ctype 32		: setct-CredReqTBS
+set-ctype 33		: setct-CredReqTBSX
+set-ctype 34		: setct-CredResData
+set-ctype 35		: setct-CredRevReqTBS
+set-ctype 36		: setct-CredRevReqTBSX
+set-ctype 37		: setct-CredRevResData
+set-ctype 38		: setct-PCertReqData
+set-ctype 39		: setct-PCertResTBS
+set-ctype 40		: setct-BatchAdminReqData
+set-ctype 41		: setct-BatchAdminResData
+set-ctype 42		: setct-CardCInitResTBS
+set-ctype 43		: setct-MeAqCInitResTBS
+set-ctype 44		: setct-RegFormResTBS
+set-ctype 45		: setct-CertReqData
+set-ctype 46		: setct-CertReqTBS
+set-ctype 47		: setct-CertResData
+set-ctype 48		: setct-CertInqReqTBS
+set-ctype 49		: setct-ErrorTBS
+set-ctype 50		: setct-PIDualSignedTBE
+set-ctype 51		: setct-PIUnsignedTBE
+set-ctype 52		: setct-AuthReqTBE
+set-ctype 53		: setct-AuthResTBE
+set-ctype 54		: setct-AuthResTBEX
+set-ctype 55		: setct-AuthTokenTBE
+set-ctype 56		: setct-CapTokenTBE
+set-ctype 57		: setct-CapTokenTBEX
+set-ctype 58		: setct-AcqCardCodeMsgTBE
+set-ctype 59		: setct-AuthRevReqTBE
+set-ctype 60		: setct-AuthRevResTBE
+set-ctype 61		: setct-AuthRevResTBEB
+set-ctype 62		: setct-CapReqTBE
+set-ctype 63		: setct-CapReqTBEX
+set-ctype 64		: setct-CapResTBE
+set-ctype 65		: setct-CapRevReqTBE
+set-ctype 66		: setct-CapRevReqTBEX
+set-ctype 67		: setct-CapRevResTBE
+set-ctype 68		: setct-CredReqTBE
+set-ctype 69		: setct-CredReqTBEX
+set-ctype 70		: setct-CredResTBE
+set-ctype 71		: setct-CredRevReqTBE
+set-ctype 72		: setct-CredRevReqTBEX
+set-ctype 73		: setct-CredRevResTBE
+set-ctype 74		: setct-BatchAdminReqTBE
+set-ctype 75		: setct-BatchAdminResTBE
+set-ctype 76		: setct-RegFormReqTBE
+set-ctype 77		: setct-CertReqTBE
+set-ctype 78		: setct-CertReqTBEX
+set-ctype 79		: setct-CertResTBE
+set-ctype 80		: setct-CRLNotificationTBS
+set-ctype 81		: setct-CRLNotificationResTBS
+set-ctype 82		: setct-BCIDistributionTBS
+
+set-msgExt 1		: setext-genCrypt	: generic cryptogram
+set-msgExt 3		: setext-miAuth		: merchant initiated auth
+set-msgExt 4		: setext-pinSecure
+set-msgExt 5		: setext-pinAny
+set-msgExt 7		: setext-track2
+set-msgExt 8		: setext-cv		: additional verification
+
+set-policy 0		: set-policy-root
+
+set-certExt 0		: setCext-hashedRoot
+set-certExt 1		: setCext-certType
+set-certExt 2		: setCext-merchData
+set-certExt 3		: setCext-cCertRequired
+set-certExt 4		: setCext-tunneling
+set-certExt 5		: setCext-setExt
+set-certExt 6		: setCext-setQualf
+set-certExt 7		: setCext-PGWYcapabilities
+set-certExt 8		: setCext-TokenIdentifier
+set-certExt 9		: setCext-Track2Data
+set-certExt 10		: setCext-TokenType
+set-certExt 11		: setCext-IssuerCapabilities
+
+set-attr 0		: setAttr-Cert
+set-attr 1		: setAttr-PGWYcap	: payment gateway capabilities
+set-attr 2		: setAttr-TokenType
+set-attr 3		: setAttr-IssCap	: issuer capabilities
+
+setAttr-Cert 0		: set-rootKeyThumb
+setAttr-Cert 1		: set-addPolicy
+
+setAttr-TokenType 1	: setAttr-Token-EMV
+setAttr-TokenType 2	: setAttr-Token-B0Prime
+
+setAttr-IssCap 3	: setAttr-IssCap-CVM
+setAttr-IssCap 4	: setAttr-IssCap-T2
+setAttr-IssCap 5	: setAttr-IssCap-Sig
+
+setAttr-IssCap-CVM 1	: setAttr-GenCryptgrm	: generate cryptogram
+setAttr-IssCap-T2 1	: setAttr-T2Enc		: encrypted track 2
+setAttr-IssCap-T2 2	: setAttr-T2cleartxt	: cleartext track 2
+
+setAttr-IssCap-Sig 1	: setAttr-TokICCsig	: ICC or token signature
+setAttr-IssCap-Sig 2	: setAttr-SecDevSig	: secure device signature
+
+set-brand 1		: set-brand-IATA-ATA
+set-brand 30		: set-brand-Diners
+set-brand 34		: set-brand-AmericanExpress
+set-brand 35		: set-brand-JCB
+set-brand 4		: set-brand-Visa
+set-brand 5		: set-brand-MasterCard
+set-brand 6011		: set-brand-Novus
+
+rsadsi 3 10		: DES-CDMF		: des-cdmf
+rsadsi 1 1 6		: rsaOAEPEncryptionSET
+
+			: Oakley-EC2N-3		: ipsec3
+			: Oakley-EC2N-4		: ipsec4
+
+iso 0 10118 3 0 55	: whirlpool
+
+# GOST OIDs
+
+member-body 643 2 2	: cryptopro
+member-body 643 2 9	: cryptocom
+member-body 643 7 1	: id-tc26
+
+cryptopro 3		: id-GostR3411-94-with-GostR3410-2001 : GOST R 34.11-94 with GOST R 34.10-2001
+cryptopro 4		: id-GostR3411-94-with-GostR3410-94 : GOST R 34.11-94 with GOST R 34.10-94
+!Cname id-GostR3411-94
+cryptopro 9		: md_gost94		: GOST R 34.11-94
+cryptopro 10		: id-HMACGostR3411-94	: HMAC GOST 34.11-94
+!Cname id-GostR3410-2001
+cryptopro 19		: gost2001	: GOST R 34.10-2001
+!Cname id-GostR3410-94
+cryptopro 20		: gost94	: GOST R 34.10-94
+!Cname id-Gost28147-89
+cryptopro 21		: gost89 		: GOST 28147-89
+			: gost89-cnt
+			: gost89-cnt-12
+			: gost89-cbc
+			: gost89-ecb
+			: gost89-ctr
+!Cname id-Gost28147-89-MAC
+cryptopro 22		: gost-mac	: GOST 28147-89 MAC
+			: gost-mac-12
+!Cname id-GostR3411-94-prf
+cryptopro 23		: prf-gostr3411-94	: GOST R 34.11-94 PRF
+cryptopro 98		: id-GostR3410-2001DH	: GOST R 34.10-2001 DH
+cryptopro 99		: id-GostR3410-94DH	: GOST R 34.10-94 DH
+
+cryptopro 14 1		: id-Gost28147-89-CryptoPro-KeyMeshing
+cryptopro 14 0		: id-Gost28147-89-None-KeyMeshing
+
+# GOST parameter set OIDs
+
+cryptopro 30 0		: id-GostR3411-94-TestParamSet
+cryptopro 30 1		: id-GostR3411-94-CryptoProParamSet
+
+cryptopro 31 0		: id-Gost28147-89-TestParamSet
+cryptopro 31 1		: id-Gost28147-89-CryptoPro-A-ParamSet
+cryptopro 31 2		: id-Gost28147-89-CryptoPro-B-ParamSet
+cryptopro 31 3		: id-Gost28147-89-CryptoPro-C-ParamSet
+cryptopro 31 4		: id-Gost28147-89-CryptoPro-D-ParamSet
+cryptopro 31 5		: id-Gost28147-89-CryptoPro-Oscar-1-1-ParamSet
+cryptopro 31 6		: id-Gost28147-89-CryptoPro-Oscar-1-0-ParamSet
+cryptopro 31 7		: id-Gost28147-89-CryptoPro-RIC-1-ParamSet
+
+cryptopro 32 0		: id-GostR3410-94-TestParamSet
+cryptopro 32 2		: id-GostR3410-94-CryptoPro-A-ParamSet
+cryptopro 32 3		: id-GostR3410-94-CryptoPro-B-ParamSet
+cryptopro 32 4		: id-GostR3410-94-CryptoPro-C-ParamSet
+cryptopro 32 5		: id-GostR3410-94-CryptoPro-D-ParamSet
+
+cryptopro 33 1		: id-GostR3410-94-CryptoPro-XchA-ParamSet
+cryptopro 33 2		: id-GostR3410-94-CryptoPro-XchB-ParamSet
+cryptopro 33 3		: id-GostR3410-94-CryptoPro-XchC-ParamSet
+
+cryptopro 35 0		: id-GostR3410-2001-TestParamSet
+cryptopro 35 1		: id-GostR3410-2001-CryptoPro-A-ParamSet
+cryptopro 35 2		: id-GostR3410-2001-CryptoPro-B-ParamSet
+cryptopro 35 3		: id-GostR3410-2001-CryptoPro-C-ParamSet
+
+cryptopro 36 0		: id-GostR3410-2001-CryptoPro-XchA-ParamSet
+cryptopro 36 1		: id-GostR3410-2001-CryptoPro-XchB-ParamSet
+
+id-GostR3410-94 1	: id-GostR3410-94-a
+id-GostR3410-94 2	: id-GostR3410-94-aBis
+id-GostR3410-94 3	: id-GostR3410-94-b
+id-GostR3410-94 4	: id-GostR3410-94-bBis
+
+# Cryptocom LTD GOST OIDs
+
+cryptocom 1 6 1		: id-Gost28147-89-cc	: GOST 28147-89 Cryptocom ParamSet
+!Cname id-GostR3410-94-cc
+cryptocom 1 5 3		: gost94cc	: GOST 34.10-94 Cryptocom
+!Cname id-GostR3410-2001-cc
+cryptocom 1 5 4		: gost2001cc	: GOST 34.10-2001 Cryptocom
+
+cryptocom 1 3 3		: id-GostR3411-94-with-GostR3410-94-cc : GOST R 34.11-94 with GOST R 34.10-94 Cryptocom
+cryptocom 1 3 4		: id-GostR3411-94-with-GostR3410-2001-cc : GOST R 34.11-94 with GOST R 34.10-2001 Cryptocom
+
+cryptocom 1 8 1		: id-GostR3410-2001-ParamSet-cc : GOST R 3410-2001 Parameter Set Cryptocom
+
+# TC26 GOST OIDs
+
+id-tc26 1 		: id-tc26-algorithms
+id-tc26-algorithms 1	: id-tc26-sign
+!Cname id-GostR3410-2012-256
+id-tc26-sign 1		: gost2012_256: GOST R 34.10-2012 with 256 bit modulus
+!Cname id-GostR3410-2012-512
+id-tc26-sign 2		: gost2012_512: GOST R 34.10-2012 with 512 bit modulus
+
+id-tc26-algorithms 2	: id-tc26-digest
+!Cname id-GostR3411-2012-256
+id-tc26-digest 2	: md_gost12_256: GOST R 34.11-2012 with 256 bit hash
+!Cname id-GostR3411-2012-512
+id-tc26-digest 3	: md_gost12_512: GOST R 34.11-2012 with 512 bit hash
+
+id-tc26-algorithms 3	: id-tc26-signwithdigest
+id-tc26-signwithdigest 2: id-tc26-signwithdigest-gost3410-2012-256: GOST R 34.10-2012 with GOST R 34.11-2012 (256 bit)
+id-tc26-signwithdigest 3: id-tc26-signwithdigest-gost3410-2012-512: GOST R 34.10-2012 with GOST R 34.11-2012 (512 bit)
+
+id-tc26-algorithms 4	: id-tc26-mac
+id-tc26-mac 1		: id-tc26-hmac-gost-3411-2012-256 : HMAC GOST 34.11-2012 256 bit
+id-tc26-mac 2		: id-tc26-hmac-gost-3411-2012-512 : HMAC GOST 34.11-2012 512 bit
+
+id-tc26-algorithms 5	: id-tc26-cipher
+
+id-tc26-algorithms 6	: id-tc26-agreement
+id-tc26-agreement 1	: id-tc26-agreement-gost-3410-2012-256
+id-tc26-agreement 2	: id-tc26-agreement-gost-3410-2012-512
+
+id-tc26 2 		: id-tc26-constants
+
+id-tc26-constants 1	: id-tc26-sign-constants
+id-tc26-sign-constants 2: id-tc26-gost-3410-2012-512-constants
+id-tc26-gost-3410-2012-512-constants 0	: id-tc26-gost-3410-2012-512-paramSetTest: GOST R 34.10-2012 (512 bit) testing parameter set
+id-tc26-gost-3410-2012-512-constants 1	: id-tc26-gost-3410-2012-512-paramSetA: GOST R 34.10-2012 (512 bit) ParamSet A
+id-tc26-gost-3410-2012-512-constants 2	: id-tc26-gost-3410-2012-512-paramSetB: GOST R 34.10-2012 (512 bit) ParamSet B
+
+id-tc26-constants 2     : id-tc26-digest-constants
+id-tc26-constants 5     : id-tc26-cipher-constants
+id-tc26-cipher-constants 1	: id-tc26-gost-28147-constants
+id-tc26-gost-28147-constants 1	: id-tc26-gost-28147-param-Z : GOST 28147-89 TC26 parameter set
+
+member-body 643 3 131 1 1	: INN	: INN
+member-body 643 100 1		: OGRN	: OGRN
+member-body 643 100 3		: SNILS	: SNILS
+member-body 643 100 111	: subjectSignTool	: Signing Tool of Subject
+member-body 643 100 112	: issuerSignTool	: Signing Tool of Issuer
+
+#GOST R34.13-2015 Grasshopper "Kuznechik"
+			: grasshopper-ecb
+			: grasshopper-ctr
+			: grasshopper-ofb
+			: grasshopper-cbc
+			: grasshopper-cfb
+			: grasshopper-mac
+
+# Definitions for Camellia cipher - CBC MODE
+
+1 2 392 200011 61 1 1 1 2 : CAMELLIA-128-CBC		: camellia-128-cbc
+1 2 392 200011 61 1 1 1 3 : CAMELLIA-192-CBC		: camellia-192-cbc
+1 2 392 200011 61 1 1 1 4 : CAMELLIA-256-CBC		: camellia-256-cbc
+1 2 392 200011 61 1 1 3 2 : id-camellia128-wrap
+1 2 392 200011 61 1 1 3 3 : id-camellia192-wrap
+1 2 392 200011 61 1 1 3 4 : id-camellia256-wrap
+
+# Definitions for Camellia cipher - ECB, CFB, OFB MODE
+
+!Alias ntt-ds 0 3 4401 5
+!Alias camellia ntt-ds 3 1 9 
+
+camellia 1		: CAMELLIA-128-ECB		: camellia-128-ecb
+!Cname camellia-128-ofb128
+camellia 3		: CAMELLIA-128-OFB		: camellia-128-ofb
+!Cname camellia-128-cfb128
+camellia 4		: CAMELLIA-128-CFB		: camellia-128-cfb
+camellia 6		: CAMELLIA-128-GCM		: camellia-128-gcm
+camellia 7		: CAMELLIA-128-CCM		: camellia-128-ccm
+camellia 9		: CAMELLIA-128-CTR		: camellia-128-ctr
+camellia 10		: CAMELLIA-128-CMAC		: camellia-128-cmac
+
+camellia 21		: CAMELLIA-192-ECB		: camellia-192-ecb
+!Cname camellia-192-ofb128
+camellia 23		: CAMELLIA-192-OFB		: camellia-192-ofb
+!Cname camellia-192-cfb128
+camellia 24		: CAMELLIA-192-CFB		: camellia-192-cfb
+camellia 26		: CAMELLIA-192-GCM		: camellia-192-gcm
+camellia 27		: CAMELLIA-192-CCM		: camellia-192-ccm
+camellia 29		: CAMELLIA-192-CTR		: camellia-192-ctr
+camellia 30		: CAMELLIA-192-CMAC		: camellia-192-cmac
+
+camellia 41		: CAMELLIA-256-ECB		: camellia-256-ecb
+!Cname camellia-256-ofb128
+camellia 43		: CAMELLIA-256-OFB		: camellia-256-ofb
+!Cname camellia-256-cfb128
+camellia 44		: CAMELLIA-256-CFB		: camellia-256-cfb
+camellia 46		: CAMELLIA-256-GCM		: camellia-256-gcm
+camellia 47		: CAMELLIA-256-CCM		: camellia-256-ccm
+camellia 49		: CAMELLIA-256-CTR		: camellia-256-ctr
+camellia 50		: CAMELLIA-256-CMAC		: camellia-256-cmac
+
+# There are no OIDs for these modes...
+
+			: CAMELLIA-128-CFB1		: camellia-128-cfb1
+			: CAMELLIA-192-CFB1		: camellia-192-cfb1
+			: CAMELLIA-256-CFB1		: camellia-256-cfb1
+			: CAMELLIA-128-CFB8		: camellia-128-cfb8
+			: CAMELLIA-192-CFB8		: camellia-192-cfb8
+			: CAMELLIA-256-CFB8		: camellia-256-cfb8
+
+# Definitions for SEED cipher - ECB, CBC, OFB mode
+
+member-body 410 200004  : KISA          : kisa
+kisa 1 3                : SEED-ECB      : seed-ecb
+kisa 1 4                : SEED-CBC      : seed-cbc
+!Cname seed-cfb128
+kisa 1 5                : SEED-CFB      : seed-cfb
+!Cname seed-ofb128
+kisa 1 6                : SEED-OFB      : seed-ofb
+
+# There is no OID that just denotes "HMAC" oddly enough...
+
+			: HMAC				: hmac
+# Nor CMAC either
+			: CMAC				: cmac
+
+# Synthetic composite ciphersuites
+			: RC4-HMAC-MD5			: rc4-hmac-md5
+			: AES-128-CBC-HMAC-SHA1		: aes-128-cbc-hmac-sha1
+			: AES-192-CBC-HMAC-SHA1		: aes-192-cbc-hmac-sha1
+			: AES-256-CBC-HMAC-SHA1		: aes-256-cbc-hmac-sha1
+			: AES-128-CBC-HMAC-SHA256	: aes-128-cbc-hmac-sha256
+			: AES-192-CBC-HMAC-SHA256	: aes-192-cbc-hmac-sha256
+			: AES-256-CBC-HMAC-SHA256	: aes-256-cbc-hmac-sha256
+			: ChaCha20-Poly1305		: chacha20-poly1305
+			: ChaCha20			: chacha20
+
+ISO-US 10046 2 1	: dhpublicnumber		: X9.42 DH
+
+# RFC 5639 curve OIDs (see http://www.ietf.org/rfc/rfc5639.txt)
+# versionOne OBJECT IDENTIFIER ::= {
+# iso(1) identifified-organization(3) teletrust(36) algorithm(3)
+# signature-algorithm(3) ecSign(2) ecStdCurvesAndGeneration(8)
+# ellipticCurve(1) 1 }
+1 3 36 3 3 2 8 1 1 1 : brainpoolP160r1
+1 3 36 3 3 2 8 1 1 2 : brainpoolP160t1
+1 3 36 3 3 2 8 1 1 3 : brainpoolP192r1
+1 3 36 3 3 2 8 1 1 4 : brainpoolP192t1
+1 3 36 3 3 2 8 1 1 5 : brainpoolP224r1
+1 3 36 3 3 2 8 1 1 6 : brainpoolP224t1
+1 3 36 3 3 2 8 1 1 7 : brainpoolP256r1
+1 3 36 3 3 2 8 1 1 8 : brainpoolP256t1
+1 3 36 3 3 2 8 1 1 9 : brainpoolP320r1
+1 3 36 3 3 2 8 1 1 10 : brainpoolP320t1
+1 3 36 3 3 2 8 1 1 11 : brainpoolP384r1
+1 3 36 3 3 2 8 1 1 12 : brainpoolP384t1
+1 3 36 3 3 2 8 1 1 13 : brainpoolP512r1
+1 3 36 3 3 2 8 1 1 14 : brainpoolP512t1            
+
+# ECDH schemes from RFC5753
+!Alias x9-63-scheme 1 3 133 16 840 63 0
+# GMSSL export secg-scheme
+# !Alias secg-scheme certicom-arc 1
+certicom-arc 1   : secg-scheme
+
+x9-63-scheme 2   : dhSinglePass-stdDH-sha1kdf-scheme
+secg-scheme 11 0 : dhSinglePass-stdDH-sha224kdf-scheme
+secg-scheme 11 1 : dhSinglePass-stdDH-sha256kdf-scheme
+secg-scheme 11 2 : dhSinglePass-stdDH-sha384kdf-scheme
+secg-scheme 11 3 : dhSinglePass-stdDH-sha512kdf-scheme
+
+x9-63-scheme 3   : dhSinglePass-cofactorDH-sha1kdf-scheme
+secg-scheme 14 0 : dhSinglePass-cofactorDH-sha224kdf-scheme
+secg-scheme 14 1 : dhSinglePass-cofactorDH-sha256kdf-scheme
+secg-scheme 14 2 : dhSinglePass-cofactorDH-sha384kdf-scheme
+secg-scheme 14 3 : dhSinglePass-cofactorDH-sha512kdf-scheme
+# NIDs for use with lookup tables.
+                 : dh-std-kdf
+                 : dh-cofactor-kdf
+
+# RFC 6962 Extension OIDs (see http://www.ietf.org/rfc/rfc6962.txt)
+1 3 6 1 4 1 11129 2 4 2	: ct_precert_scts		: CT Precertificate SCTs
+1 3 6 1 4 1 11129 2 4 3	: ct_precert_poison		: CT Precertificate Poison
+1 3 6 1 4 1 11129 2 4 4	: ct_precert_signer		: CT Precertificate Signer
+1 3 6 1 4 1 11129 2 4 5	: ct_cert_scts			: CT Certificate SCTs
+
+# CABForum EV SSL Certificate Guidelines
+# (see https://cabforum.org/extended-validation/)
+# OIDs for Subject Jurisdiction of Incorporation or Registration
+1 3 6 1 4 1 311 60 2 1 1	: jurisdictionL		: jurisdictionLocalityName
+1 3 6 1 4 1 311 60 2 1 2	: jurisdictionST	: jurisdictionStateOrProvinceName
+1 3 6 1 4 1 311 60 2 1 3	: jurisdictionC		: jurisdictionCountryName
+
+# SCRYPT algorithm
+1 3 6 1 4 1 11591 4 11		: id-scrypt
+
+# NID for TLS1 PRF
+                            : TLS1-PRF          : tls1-prf
+
+# NID for HKDF
+                            : HKDF              : hkdf
+
+# RFC 4556
+1 3 6 1 5 2 3 : id-pkinit
+id-pkinit 4                     : pkInitClientAuth      : PKINIT Client Auth
+id-pkinit 5                     : pkInitKDC             : Signing KDC Response
+
+# New curves from draft-ietf-curdle-pkix-00
+1 3 101 110 : X25519
+1 3 101 111 : X448
+
+# NIDs for cipher key exchange
+                            : KxRSA        : kx-rsa
+                            : KxECDHE      : kx-ecdhe
+                            : KxDHE        : kx-dhe
+                            : KxECDHE-PSK  : kx-ecdhe-psk
+                            : KxDHE-PSK    : kx-dhe-psk
+                            : KxRSA_PSK    : kx-rsa-psk
+                            : KxPSK        : kx-psk
+                            : KxSRP        : kx-srp
+                            : KxGOST       : kx-gost
+
+# NIDs for cipher authentication
+                            : AuthRSA      : auth-rsa
+                            : AuthECDSA    : auth-ecdsa
+                            : AuthPSK      : auth-psk
+                            : AuthDSS      : auth-dss
+                            : AuthGOST01   : auth-gost01
+                            : AuthGOST12   : auth-gost12
+                            : AuthSRP      : auth-srp
+                            : AuthNULL     : auth-null
+
+# SECG ECIES OID
+secg-scheme 7		: ecies-recommendedParameters
+secg-scheme 8		: ecies-specifiedParameters
+secg-scheme 17 0	: x9-63-kdf
+secg-scheme 17 1	: nist-concatenation-kdf
+secg-scheme 17 2	: tls-kdf
+secg-scheme 17 3	: ikev2-kdf
+secg-scheme 18		: xor-in-ecies
+secg-scheme 19		: tdes-cbc-in-ecies
+secg-scheme 20 0	: aes128-cbc-in-ecies
+secg-scheme 20 1	: aes192-cbc-in-ecies
+secg-scheme 20 2	: aes256-cbc-in-ecies
+secg-scheme 21 0	: aes128-ctr-in-ecies
+secg-scheme 21 1	: aes192-ctr-in-ecies
+secg-scheme 21 2	: aes256-ctr-in-ecies
+secg-scheme 22		: hmac-full-ecies
+secg-scheme 23		: hmac-half-ecies
+secg-scheme 24 0	: cmac-aes128-ecies
+secg-scheme 24 1	: cmac-aes192-ecies
+secg-scheme 24 2	: cmac-aes256-ecies
+ecies-specifiedParameters 1	: ecies-with-x9-63-sha1-xor-hmac
+ecies-specifiedParameters 2	: ecies-with-x9-63-sha256-xor-hmac
+ecies-specifiedParameters 3	: ecies-with-x9-63-sha512-xor-hmac
+ecies-specifiedParameters 4	: ecies-with-x9-63-sha1-aes128-cbc-hmac
+ecies-specifiedParameters 5	: ecies-with-x9-63-sha256-aes128-cbc-hmac
+ecies-specifiedParameters 6	: ecies-with-x9-63-sha512-aes256-cbc-hmac
+ecies-specifiedParameters 7	: ecies-with-x9-63-sha256-aes128-ctr-hmac
+ecies-specifiedParameters 8	: ecies-with-x9-63-sha512-aes256-ctr-hmac
+ecies-specifiedParameters 9	: ecies-with-x9-63-sha256-aes128-cbc-hmac-half
+ecies-specifiedParameters 10	: ecies-with-x9-63-sha512-aes256-cbc-hmac-half
+ecies-specifiedParameters 11	: ecies-with-x9-63-sha256-aes128-ctr-hmac-half
+ecies-specifiedParameters 12	: ecies-with-x9-63-sha512-aes256-ctr-hmac-half
+ecies-specifiedParameters 13	: ecies-with-x9-63-sha1-aes128-cbc-cmac
+ecies-specifiedParameters 14	: ecies-with-x9-63-sha256-aes128-cbc-cmac
+ecies-specifiedParameters 15	: ecies-with-x9-63-sha512-aes256-cbc-cmac
+ecies-specifiedParameters 16	: ecies-with-x9-63-sha256-aes128-ctr-cmac
+ecies-specifiedParameters 17	: ecies-with-x9-63-sha512-aes256-ctr-cmac
+
+# RFC 5091 IBCS #1
+!Alias ibcs ISO-US 1 114334 1
+ibcs 1		: ibcs1
+ibcs1 1 1	: type1curve
+ibcs1 2 1	: bfibe
+ibcs1 2 2	: bb1ibe
+
+# GM/T
+member-body 156		: ISO-CN		: ISO CN Member Body
+ISO-CN 10197		: oscca
+oscca 1			: sm-scheme
+
+sm-scheme 103 1		: SSF33-ECB		: ssf33-ecb
+sm-scheme 103 2		: SSF33-CBC		: ssf33-cbc
+!Cname ssf33-ofb128
+sm-scheme 103 3		: SSF33-OFB		: ssf33-ofb
+!Cname ssf33-cfb128
+sm-scheme 103 4		: SSF33-CFB		: ssf33-cfb
+sm-scheme 103 5		: SSF33-CFB1		: ssf33-cfb1
+sm-scheme 103 6		: SSF33-CFB8		: ssf33-cfb8
+sm-scheme 103 7		: SSF33-CBC-MAC		: ssf33-cbc-mac
+
+sm-scheme 102 1		: SM1-ECB		: sm1-ecb
+sm-scheme 102 2		: SM1-CBC		: sm1-cbc
+!Cname sm1-ofb128
+sm-scheme 102 3		: SM1-OFB		: sm1-ofb
+!Cname sm1-cfb128
+sm-scheme 102 4		: SM1-CFB		: sm1-cfb
+sm-scheme 102 5		: SM1-CFB1		: sm1-cfb1
+sm-scheme 102 6		: SM1-CFB8		: sm1-cfb8
+
+
+# SM2 OIDs
+sm-scheme 301		: sm2p256v1
+sm-scheme 301 1		: sm2sign
+sm-scheme 301 2		: sm2exchange
+sm-scheme 301 3		: sm2encrypt
+
+sm-scheme 501		: SM2Sign-with-SM3	: sm2sign-with-sm3
+sm-scheme 502		: SM2Sign-with-SHA1 	: sm2sign-with-sha1
+sm-scheme 503		: SM2Sign-with-SHA256	: sm2sign-with-sha256
+sm-scheme 504		: SM2Sign-with-SHA511	: sm2sign-with-sha512
+sm-scheme 505		: SM2Sign-with-SHA224	: sm2sign-with-sha224
+sm-scheme 506		: SM2Sign-with-SHA384	: sm2sign-with-sha384
+sm-scheme 507		: SM2Sign-with-RMD160	: sm2sign-with-rmd160
+sm-scheme 520		: SM2Sign-with-Whirlpool : sm2sign-with-whirlpool
+sm-scheme 521		: SM2Sign-with-Blake2b512 : sm2sign-with-blake2b512
+sm-scheme 522		: SM2Sign-with-Blake2s256 : sm2sign-with-blake2s256
+
+sm2encrypt 1		: sm2encrypt-recommendedParameters
+sm2encrypt 2		: sm2encrypt-specifiedParameters
+sm2encrypt 2 1		: sm2encrypt-with-sm3
+sm2encrypt 2 2		: sm2encrypt-with-sha1
+sm2encrypt 2 3		: sm2encrypt-with-sha224
+sm2encrypt 2 4		: sm2encrypt-with-sha256
+sm2encrypt 2 5		: sm2encrypt-with-sha384
+sm2encrypt 2 6		: sm2encrypt-with-sha512
+sm2encrypt 2 7		: sm2encrypt-with-rmd160
+sm2encrypt 2 8		: sm2encrypt-with-whirlpool
+sm2encrypt 2 9		: sm2encrypt-with-blake2b512
+sm2encrypt 2 10		: sm2encrypt-with-blake2s256
+sm2encrypt 2 11		: sm2encrypt-with-md5
+
+# SM3
+sm-scheme 401		: SM3			: sm3
+sm-scheme 401 2		: HMAC-SM3		: hmac-sm3
+
+# SM4
+sm-scheme 104 1		: SMS4-ECB		: sms4-ecb
+sm-scheme 104 2		: SMS4-CBC		: sms4-cbc
+!Cname sms4-ofb128
+sm-scheme 104 3		: SMS4-OFB		: sms4-ofb
+!Cname sms4-cfb128
+sm-scheme 104 4		: SMS4-CFB		: sms4-cfb
+sm-scheme 104 5		: SMS4-CFB1		: sms4-cfb1
+sm-scheme 104 6		: SMS4-CFB8		: sms4-cfb8
+sm-scheme 104 7		: SMS4-CTR		: sms4-ctr
+sm-scheme 104 8		: SMS4-GCM		: sms4-gcm
+sm-scheme 104 9		: SMS4-CCM		: sms4-ccm
+sm-scheme 104 10	: SMS4-XTS		: sms4-xts
+sm-scheme 104 11	: SMS4-WRAP		: sms4-wrap
+sm-scheme 104 12	: SMS4-WRAP-PAD		: sms4-wrap-pad
+sm-scheme 104 100	: SMS4-OCB		: sms4-ocb
+
+# SM5/6/7/8
+sm-scheme 201		: SM5			: sm5
+sm-scheme 101 1		: SM6-ECB		: sm6-ecb
+sm-scheme 101 2		: SM6-CBC		: sm6-cbc
+!Cname sm6-ofb128
+sm-scheme 101 3		: SM6-OFB		: sm6-ofb
+!Cname sm6-cfb128
+sm-scheme 101 4		: SM6-CFB		: sm6-cfb
+!Alias sm7 sm-scheme 105
+!Alias sm8 sm-scheme 106
+
+# SM9
+sm-scheme 302		: id-sm9PublicKey
+sm-scheme 302 1		: sm9sign
+sm-scheme 302 2		: sm9keyagreement
+sm-scheme 302 3		: sm9encrypt
+sm-scheme 302 4		: sm9hash1
+sm-scheme 303 7		: sm9hash2
+sm-scheme 302 5		: sm9kdf
+sm-scheme 302 6		: id-sm9MasterSecret
+sm-scheme 302 6	1 	: sm9bn256v1
+sm9sign 1		: sm9sign-with-sm3
+sm9sign 2		: sm9sign-with-sha256
+sm9encrypt 1		: sm9encrypt-with-sm3-xor
+sm9encrypt 2		: sm9encrypt-with-sm3-sms4-cbc
+sm9encrypt 3		: sm9encrypt-with-sm3-sms4-ctr
+sm9hash1 1		: sm9hash1-with-sm3
+sm9hash1 2		: sm9hash1-with-sha256
+sm9hash2 1		: sm9hash2-with-sm3
+sm9hash2 2		: sm9hash2-with-sha256
+sm9kdf 1		: sm9kdf-with-sm3
+sm9kdf 2		: sm9kdf-with-sha256
+
+# ZUC
+sm-scheme 800	: ZUC	: zuc
+zuc 1		: zuc-128eea3
+zuc 2		: zuc-128eia3
+zuc 3		: ZUC-MAC	: zuc-mac
+zuc 4		: ZUC256	: zuc256
+zuc 5		: ZUC256-MAC	: zuc256-mac
+zuc256-mac 1	: ZUC256-MAC32	: zuc256-mac32
+zuc256-mac 2	: ZUC256-MAC64	: zuc256-mac64
+zuc256-mac 3	: ZUC256-MAC128	: zuc256-mac128
+
+
+# WAPI (GB 15629.11-2003-XG1-2006)
+ISO-CN 11235	: bwips
+bwips 1		: wapi-crypto
+wapi-crypto 1	: wapi-ec
+wapi-ec 1	: wapi-ecdsa192-sha256
+wapi-ec 2 1	: wapip192v1
+
+# NIDs for cipher key exchange
+                            : KxSM2        : kx-sm2
+                            : KxSM2DHE     : kx-sm2dhe
+                            : KxSM2-PSK    : kx-sm2-psk
+                            : KxSM9        : kx-sm9
+                            : KxSM9DHE     : kx-sm9dhe
+
+# NIDs for cipher authentication
+                            : AuthSM2      : auth-sm2
+                            : AuthSM9      : auth-sm9
+
+
+# gmssl.org
+Enterprises 49549	: gmssl		: GmSSL
+
+# CPK
+GmSSL 1			: CPK		: cpk
+cpk 1			: cpk-map
+cpk-map 1		: cpk-map-sha1
+cpk-map 2		: cpk-map-sha256
+cpk-map 3		: cpk-map-sm3
+cpk-map 4		: cpk-map-sha384
+cpk-map 5		: cpk-map-sha512
+
+# paillier
+GmSSL 21		: paillier
+
diff --git a/crypto/objects/objxref.pl b/crypto/objects/objxref.pl
new file mode 100644
index 000000000..53f9bd604
--- /dev/null
+++ b/crypto/objects/objxref.pl
@@ -0,0 +1,135 @@
+#! /usr/bin/env perl
+# Copyright 1998-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+
+use strict;
+
+my %xref_tbl;
+my %oid_tbl;
+
+my ($mac_file, $xref_file) = @ARGV;
+
+open(IN, $mac_file) || die "Can't open $mac_file, $!\n";
+
+# Read in OID nid values for a lookup table.
+
+while ()
+	{
+	s|\R$||;                # Better chomp
+	my ($name, $num) = /^(\S+)\s+(\S+)$/;
+	$oid_tbl{$name} = $num;
+	}
+close IN;
+
+open(IN, $xref_file) || die "Can't open $xref_file, $!\n";
+
+my $ln = 1;
+
+while ()
+	{
+	s|\R$||;                # Better chomp
+	s/#.*$//;
+	next if (/^\S*$/);
+	my ($xr, $p1, $p2) = /^(\S+)\s+(\S+)\s+(\S+)/;
+	check_oid($xr);
+	check_oid($p1);
+	check_oid($p2);
+	$xref_tbl{$xr} = [$p1, $p2, $ln];
+	}
+
+my @xrkeys = keys %xref_tbl;
+
+my @srt1 = sort { $oid_tbl{$a} <=> $oid_tbl{$b}} @xrkeys;
+
+my $i;
+for($i = 0; $i <= $#srt1; $i++)
+	{
+	$xref_tbl{$srt1[$i]}[2] = $i;
+	}
+
+my @srt2 = sort
+	{
+	my$ap1 = $oid_tbl{$xref_tbl{$a}[0]};
+	my$bp1 = $oid_tbl{$xref_tbl{$b}[0]};
+	return $ap1 - $bp1 if ($ap1 != $bp1);
+	my$ap2 = $oid_tbl{$xref_tbl{$a}[1]};
+	my$bp2 = $oid_tbl{$xref_tbl{$b}[1]};
+
+	return $ap2 - $bp2;
+	} @xrkeys;
+
+my $pname = $0;
+$pname =~ s|.*/||;
+
+print <
+#include 
+#include 
+#include "ocsp_lcl.h"
+
+ASN1_SEQUENCE(OCSP_SIGNATURE) = {
+        ASN1_EMBED(OCSP_SIGNATURE, signatureAlgorithm, X509_ALGOR),
+        ASN1_SIMPLE(OCSP_SIGNATURE, signature, ASN1_BIT_STRING),
+        ASN1_EXP_SEQUENCE_OF_OPT(OCSP_SIGNATURE, certs, X509, 0)
+} ASN1_SEQUENCE_END(OCSP_SIGNATURE)
+
+IMPLEMENT_ASN1_FUNCTIONS(OCSP_SIGNATURE)
+
+ASN1_SEQUENCE(OCSP_CERTID) = {
+        ASN1_EMBED(OCSP_CERTID, hashAlgorithm, X509_ALGOR),
+        ASN1_EMBED(OCSP_CERTID, issuerNameHash, ASN1_OCTET_STRING),
+        ASN1_EMBED(OCSP_CERTID, issuerKeyHash, ASN1_OCTET_STRING),
+        ASN1_EMBED(OCSP_CERTID, serialNumber, ASN1_INTEGER)
+} ASN1_SEQUENCE_END(OCSP_CERTID)
+
+IMPLEMENT_ASN1_FUNCTIONS(OCSP_CERTID)
+
+ASN1_SEQUENCE(OCSP_ONEREQ) = {
+        ASN1_SIMPLE(OCSP_ONEREQ, reqCert, OCSP_CERTID),
+        ASN1_EXP_SEQUENCE_OF_OPT(OCSP_ONEREQ, singleRequestExtensions, X509_EXTENSION, 0)
+} ASN1_SEQUENCE_END(OCSP_ONEREQ)
+
+IMPLEMENT_ASN1_FUNCTIONS(OCSP_ONEREQ)
+
+ASN1_SEQUENCE(OCSP_REQINFO) = {
+        ASN1_EXP_OPT(OCSP_REQINFO, version, ASN1_INTEGER, 0),
+        ASN1_EXP_OPT(OCSP_REQINFO, requestorName, GENERAL_NAME, 1),
+        ASN1_SEQUENCE_OF(OCSP_REQINFO, requestList, OCSP_ONEREQ),
+        ASN1_EXP_SEQUENCE_OF_OPT(OCSP_REQINFO, requestExtensions, X509_EXTENSION, 2)
+} ASN1_SEQUENCE_END(OCSP_REQINFO)
+
+IMPLEMENT_ASN1_FUNCTIONS(OCSP_REQINFO)
+
+ASN1_SEQUENCE(OCSP_REQUEST) = {
+        ASN1_EMBED(OCSP_REQUEST, tbsRequest, OCSP_REQINFO),
+        ASN1_EXP_OPT(OCSP_REQUEST, optionalSignature, OCSP_SIGNATURE, 0)
+} ASN1_SEQUENCE_END(OCSP_REQUEST)
+
+IMPLEMENT_ASN1_FUNCTIONS(OCSP_REQUEST)
+
+/* OCSP_RESPONSE templates */
+
+ASN1_SEQUENCE(OCSP_RESPBYTES) = {
+            ASN1_SIMPLE(OCSP_RESPBYTES, responseType, ASN1_OBJECT),
+            ASN1_SIMPLE(OCSP_RESPBYTES, response, ASN1_OCTET_STRING)
+} ASN1_SEQUENCE_END(OCSP_RESPBYTES)
+
+IMPLEMENT_ASN1_FUNCTIONS(OCSP_RESPBYTES)
+
+ASN1_SEQUENCE(OCSP_RESPONSE) = {
+        ASN1_SIMPLE(OCSP_RESPONSE, responseStatus, ASN1_ENUMERATED),
+        ASN1_EXP_OPT(OCSP_RESPONSE, responseBytes, OCSP_RESPBYTES, 0)
+} ASN1_SEQUENCE_END(OCSP_RESPONSE)
+
+IMPLEMENT_ASN1_FUNCTIONS(OCSP_RESPONSE)
+
+ASN1_CHOICE(OCSP_RESPID) = {
+           ASN1_EXP(OCSP_RESPID, value.byName, X509_NAME, 1),
+           ASN1_EXP(OCSP_RESPID, value.byKey, ASN1_OCTET_STRING, 2)
+} ASN1_CHOICE_END(OCSP_RESPID)
+
+IMPLEMENT_ASN1_FUNCTIONS(OCSP_RESPID)
+
+ASN1_SEQUENCE(OCSP_REVOKEDINFO) = {
+        ASN1_SIMPLE(OCSP_REVOKEDINFO, revocationTime, ASN1_GENERALIZEDTIME),
+        ASN1_EXP_OPT(OCSP_REVOKEDINFO, revocationReason, ASN1_ENUMERATED, 0)
+} ASN1_SEQUENCE_END(OCSP_REVOKEDINFO)
+
+IMPLEMENT_ASN1_FUNCTIONS(OCSP_REVOKEDINFO)
+
+ASN1_CHOICE(OCSP_CERTSTATUS) = {
+        ASN1_IMP(OCSP_CERTSTATUS, value.good, ASN1_NULL, 0),
+        ASN1_IMP(OCSP_CERTSTATUS, value.revoked, OCSP_REVOKEDINFO, 1),
+        ASN1_IMP(OCSP_CERTSTATUS, value.unknown, ASN1_NULL, 2)
+} ASN1_CHOICE_END(OCSP_CERTSTATUS)
+
+IMPLEMENT_ASN1_FUNCTIONS(OCSP_CERTSTATUS)
+
+ASN1_SEQUENCE(OCSP_SINGLERESP) = {
+           ASN1_SIMPLE(OCSP_SINGLERESP, certId, OCSP_CERTID),
+           ASN1_SIMPLE(OCSP_SINGLERESP, certStatus, OCSP_CERTSTATUS),
+           ASN1_SIMPLE(OCSP_SINGLERESP, thisUpdate, ASN1_GENERALIZEDTIME),
+           ASN1_EXP_OPT(OCSP_SINGLERESP, nextUpdate, ASN1_GENERALIZEDTIME, 0),
+           ASN1_EXP_SEQUENCE_OF_OPT(OCSP_SINGLERESP, singleExtensions, X509_EXTENSION, 1)
+} ASN1_SEQUENCE_END(OCSP_SINGLERESP)
+
+IMPLEMENT_ASN1_FUNCTIONS(OCSP_SINGLERESP)
+
+ASN1_SEQUENCE(OCSP_RESPDATA) = {
+           ASN1_EXP_OPT(OCSP_RESPDATA, version, ASN1_INTEGER, 0),
+           ASN1_EMBED(OCSP_RESPDATA, responderId, OCSP_RESPID),
+           ASN1_SIMPLE(OCSP_RESPDATA, producedAt, ASN1_GENERALIZEDTIME),
+           ASN1_SEQUENCE_OF(OCSP_RESPDATA, responses, OCSP_SINGLERESP),
+           ASN1_EXP_SEQUENCE_OF_OPT(OCSP_RESPDATA, responseExtensions, X509_EXTENSION, 1)
+} ASN1_SEQUENCE_END(OCSP_RESPDATA)
+
+IMPLEMENT_ASN1_FUNCTIONS(OCSP_RESPDATA)
+
+ASN1_SEQUENCE(OCSP_BASICRESP) = {
+           ASN1_EMBED(OCSP_BASICRESP, tbsResponseData, OCSP_RESPDATA),
+           ASN1_EMBED(OCSP_BASICRESP, signatureAlgorithm, X509_ALGOR),
+           ASN1_SIMPLE(OCSP_BASICRESP, signature, ASN1_BIT_STRING),
+           ASN1_EXP_SEQUENCE_OF_OPT(OCSP_BASICRESP, certs, X509, 0)
+} ASN1_SEQUENCE_END(OCSP_BASICRESP)
+
+IMPLEMENT_ASN1_FUNCTIONS(OCSP_BASICRESP)
+
+ASN1_SEQUENCE(OCSP_CRLID) = {
+           ASN1_EXP_OPT(OCSP_CRLID, crlUrl, ASN1_IA5STRING, 0),
+           ASN1_EXP_OPT(OCSP_CRLID, crlNum, ASN1_INTEGER, 1),
+           ASN1_EXP_OPT(OCSP_CRLID, crlTime, ASN1_GENERALIZEDTIME, 2)
+} ASN1_SEQUENCE_END(OCSP_CRLID)
+
+IMPLEMENT_ASN1_FUNCTIONS(OCSP_CRLID)
+
+ASN1_SEQUENCE(OCSP_SERVICELOC) = {
+        ASN1_SIMPLE(OCSP_SERVICELOC, issuer, X509_NAME),
+        ASN1_SEQUENCE_OF_OPT(OCSP_SERVICELOC, locator, ACCESS_DESCRIPTION)
+} ASN1_SEQUENCE_END(OCSP_SERVICELOC)
+
+IMPLEMENT_ASN1_FUNCTIONS(OCSP_SERVICELOC)
diff --git a/crypto/ocsp/ocsp_cl.c b/crypto/ocsp/ocsp_cl.c
new file mode 100644
index 000000000..a42b80fa5
--- /dev/null
+++ b/crypto/ocsp/ocsp_cl.c
@@ -0,0 +1,365 @@
+/*
+ * Copyright 2001-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+#include 
+#include 
+#include 
+#include "ocsp_lcl.h"
+
+/*
+ * Utility functions related to sending OCSP requests and extracting relevant
+ * information from the response.
+ */
+
+/*
+ * Add an OCSP_CERTID to an OCSP request. Return new OCSP_ONEREQ pointer:
+ * useful if we want to add extensions.
+ */
+
+OCSP_ONEREQ *OCSP_request_add0_id(OCSP_REQUEST *req, OCSP_CERTID *cid)
+{
+    OCSP_ONEREQ *one = NULL;
+
+    if ((one = OCSP_ONEREQ_new()) == NULL)
+        return NULL;
+    OCSP_CERTID_free(one->reqCert);
+    one->reqCert = cid;
+    if (req && !sk_OCSP_ONEREQ_push(req->tbsRequest.requestList, one)) {
+        one->reqCert = NULL; /* do not free on error */
+        goto err;
+    }
+    return one;
+ err:
+    OCSP_ONEREQ_free(one);
+    return NULL;
+}
+
+/* Set requestorName from an X509_NAME structure */
+
+int OCSP_request_set1_name(OCSP_REQUEST *req, X509_NAME *nm)
+{
+    GENERAL_NAME *gen;
+
+    gen = GENERAL_NAME_new();
+    if (gen == NULL)
+        return 0;
+    if (!X509_NAME_set(&gen->d.directoryName, nm)) {
+        GENERAL_NAME_free(gen);
+        return 0;
+    }
+    gen->type = GEN_DIRNAME;
+    GENERAL_NAME_free(req->tbsRequest.requestorName);
+    req->tbsRequest.requestorName = gen;
+    return 1;
+}
+
+/* Add a certificate to an OCSP request */
+
+int OCSP_request_add1_cert(OCSP_REQUEST *req, X509 *cert)
+{
+    OCSP_SIGNATURE *sig;
+    if (req->optionalSignature == NULL)
+        req->optionalSignature = OCSP_SIGNATURE_new();
+    sig = req->optionalSignature;
+    if (sig == NULL)
+        return 0;
+    if (cert == NULL)
+        return 1;
+    if (sig->certs == NULL
+        && (sig->certs = sk_X509_new_null()) == NULL)
+        return 0;
+
+    if (!sk_X509_push(sig->certs, cert))
+        return 0;
+    X509_up_ref(cert);
+    return 1;
+}
+
+/*
+ * Sign an OCSP request set the requestorName to the subject name of an
+ * optional signers certificate and include one or more optional certificates
+ * in the request. Behaves like PKCS7_sign().
+ */
+
+int OCSP_request_sign(OCSP_REQUEST *req,
+                      X509 *signer,
+                      EVP_PKEY *key,
+                      const EVP_MD *dgst,
+                      STACK_OF(X509) *certs, unsigned long flags)
+{
+    int i;
+    X509 *x;
+
+    if (!OCSP_request_set1_name(req, X509_get_subject_name(signer)))
+        goto err;
+
+    if ((req->optionalSignature = OCSP_SIGNATURE_new()) == NULL)
+        goto err;
+    if (key) {
+        if (!X509_check_private_key(signer, key)) {
+            OCSPerr(OCSP_F_OCSP_REQUEST_SIGN,
+                    OCSP_R_PRIVATE_KEY_DOES_NOT_MATCH_CERTIFICATE);
+            goto err;
+        }
+        if (!OCSP_REQUEST_sign(req, key, dgst))
+            goto err;
+    }
+
+    if (!(flags & OCSP_NOCERTS)) {
+        if (!OCSP_request_add1_cert(req, signer))
+            goto err;
+        for (i = 0; i < sk_X509_num(certs); i++) {
+            x = sk_X509_value(certs, i);
+            if (!OCSP_request_add1_cert(req, x))
+                goto err;
+        }
+    }
+
+    return 1;
+ err:
+    OCSP_SIGNATURE_free(req->optionalSignature);
+    req->optionalSignature = NULL;
+    return 0;
+}
+
+/* Get response status */
+
+int OCSP_response_status(OCSP_RESPONSE *resp)
+{
+    return ASN1_ENUMERATED_get(resp->responseStatus);
+}
+
+/*
+ * Extract basic response from OCSP_RESPONSE or NULL if no basic response
+ * present.
+ */
+
+OCSP_BASICRESP *OCSP_response_get1_basic(OCSP_RESPONSE *resp)
+{
+    OCSP_RESPBYTES *rb;
+    rb = resp->responseBytes;
+    if (!rb) {
+        OCSPerr(OCSP_F_OCSP_RESPONSE_GET1_BASIC, OCSP_R_NO_RESPONSE_DATA);
+        return NULL;
+    }
+    if (OBJ_obj2nid(rb->responseType) != NID_id_pkix_OCSP_basic) {
+        OCSPerr(OCSP_F_OCSP_RESPONSE_GET1_BASIC, OCSP_R_NOT_BASIC_RESPONSE);
+        return NULL;
+    }
+
+    return ASN1_item_unpack(rb->response, ASN1_ITEM_rptr(OCSP_BASICRESP));
+}
+
+const ASN1_OCTET_STRING *OCSP_resp_get0_signature(const OCSP_BASICRESP *bs)
+{
+    return bs->signature;
+}
+
+/*
+ * Return number of OCSP_SINGLERESP responses present in a basic response.
+ */
+
+int OCSP_resp_count(OCSP_BASICRESP *bs)
+{
+    if (!bs)
+        return -1;
+    return sk_OCSP_SINGLERESP_num(bs->tbsResponseData.responses);
+}
+
+/* Extract an OCSP_SINGLERESP response with a given index */
+
+OCSP_SINGLERESP *OCSP_resp_get0(OCSP_BASICRESP *bs, int idx)
+{
+    if (!bs)
+        return NULL;
+    return sk_OCSP_SINGLERESP_value(bs->tbsResponseData.responses, idx);
+}
+
+const ASN1_GENERALIZEDTIME *OCSP_resp_get0_produced_at(const OCSP_BASICRESP* bs)
+{
+    return bs->tbsResponseData.producedAt;
+}
+
+const STACK_OF(X509) *OCSP_resp_get0_certs(const OCSP_BASICRESP *bs)
+{
+    return bs->certs;
+}
+
+int OCSP_resp_get0_id(const OCSP_BASICRESP *bs,
+                      const ASN1_OCTET_STRING **pid,
+                      const X509_NAME **pname)
+
+{
+    const OCSP_RESPID *rid = &bs->tbsResponseData.responderId;
+    if (rid->type == V_OCSP_RESPID_NAME) {
+        *pname = rid->value.byName;
+        *pid = NULL;
+    } else if (rid->type == V_OCSP_RESPID_KEY) {
+        *pid = rid->value.byKey;
+        *pname = NULL;
+    } else {
+        return 0;
+    }
+    return 1;
+}
+
+/* Look single response matching a given certificate ID */
+
+int OCSP_resp_find(OCSP_BASICRESP *bs, OCSP_CERTID *id, int last)
+{
+    int i;
+    STACK_OF(OCSP_SINGLERESP) *sresp;
+    OCSP_SINGLERESP *single;
+    if (!bs)
+        return -1;
+    if (last < 0)
+        last = 0;
+    else
+        last++;
+    sresp = bs->tbsResponseData.responses;
+    for (i = last; i < sk_OCSP_SINGLERESP_num(sresp); i++) {
+        single = sk_OCSP_SINGLERESP_value(sresp, i);
+        if (!OCSP_id_cmp(id, single->certId))
+            return i;
+    }
+    return -1;
+}
+
+/*
+ * Extract status information from an OCSP_SINGLERESP structure. Note: the
+ * revtime and reason values are only set if the certificate status is
+ * revoked. Returns numerical value of status.
+ */
+
+int OCSP_single_get0_status(OCSP_SINGLERESP *single, int *reason,
+                            ASN1_GENERALIZEDTIME **revtime,
+                            ASN1_GENERALIZEDTIME **thisupd,
+                            ASN1_GENERALIZEDTIME **nextupd)
+{
+    int ret;
+    OCSP_CERTSTATUS *cst;
+    if (!single)
+        return -1;
+    cst = single->certStatus;
+    ret = cst->type;
+    if (ret == V_OCSP_CERTSTATUS_REVOKED) {
+        OCSP_REVOKEDINFO *rev = cst->value.revoked;
+        if (revtime)
+            *revtime = rev->revocationTime;
+        if (reason) {
+            if (rev->revocationReason)
+                *reason = ASN1_ENUMERATED_get(rev->revocationReason);
+            else
+                *reason = -1;
+        }
+    }
+    if (thisupd)
+        *thisupd = single->thisUpdate;
+    if (nextupd)
+        *nextupd = single->nextUpdate;
+    return ret;
+}
+
+/*
+ * This function combines the previous ones: look up a certificate ID and if
+ * found extract status information. Return 0 is successful.
+ */
+
+int OCSP_resp_find_status(OCSP_BASICRESP *bs, OCSP_CERTID *id, int *status,
+                          int *reason,
+                          ASN1_GENERALIZEDTIME **revtime,
+                          ASN1_GENERALIZEDTIME **thisupd,
+                          ASN1_GENERALIZEDTIME **nextupd)
+{
+    int i;
+    OCSP_SINGLERESP *single;
+    i = OCSP_resp_find(bs, id, -1);
+    /* Maybe check for multiple responses and give an error? */
+    if (i < 0)
+        return 0;
+    single = OCSP_resp_get0(bs, i);
+    i = OCSP_single_get0_status(single, reason, revtime, thisupd, nextupd);
+    if (status)
+        *status = i;
+    return 1;
+}
+
+/*
+ * Check validity of thisUpdate and nextUpdate fields. It is possible that
+ * the request will take a few seconds to process and/or the time won't be
+ * totally accurate. Therefore to avoid rejecting otherwise valid time we
+ * allow the times to be within 'nsec' of the current time. Also to avoid
+ * accepting very old responses without a nextUpdate field an optional maxage
+ * parameter specifies the maximum age the thisUpdate field can be.
+ */
+
+int OCSP_check_validity(ASN1_GENERALIZEDTIME *thisupd,
+                        ASN1_GENERALIZEDTIME *nextupd, long nsec, long maxsec)
+{
+    int ret = 1;
+    time_t t_now, t_tmp;
+    time(&t_now);
+    /* Check thisUpdate is valid and not more than nsec in the future */
+    if (!ASN1_GENERALIZEDTIME_check(thisupd)) {
+        OCSPerr(OCSP_F_OCSP_CHECK_VALIDITY, OCSP_R_ERROR_IN_THISUPDATE_FIELD);
+        ret = 0;
+    } else {
+        t_tmp = t_now + nsec;
+        if (X509_cmp_time(thisupd, &t_tmp) > 0) {
+            OCSPerr(OCSP_F_OCSP_CHECK_VALIDITY, OCSP_R_STATUS_NOT_YET_VALID);
+            ret = 0;
+        }
+
+        /*
+         * If maxsec specified check thisUpdate is not more than maxsec in
+         * the past
+         */
+        if (maxsec >= 0) {
+            t_tmp = t_now - maxsec;
+            if (X509_cmp_time(thisupd, &t_tmp) < 0) {
+                OCSPerr(OCSP_F_OCSP_CHECK_VALIDITY, OCSP_R_STATUS_TOO_OLD);
+                ret = 0;
+            }
+        }
+    }
+
+    if (!nextupd)
+        return ret;
+
+    /* Check nextUpdate is valid and not more than nsec in the past */
+    if (!ASN1_GENERALIZEDTIME_check(nextupd)) {
+        OCSPerr(OCSP_F_OCSP_CHECK_VALIDITY, OCSP_R_ERROR_IN_NEXTUPDATE_FIELD);
+        ret = 0;
+    } else {
+        t_tmp = t_now - nsec;
+        if (X509_cmp_time(nextupd, &t_tmp) < 0) {
+            OCSPerr(OCSP_F_OCSP_CHECK_VALIDITY, OCSP_R_STATUS_EXPIRED);
+            ret = 0;
+        }
+    }
+
+    /* Also don't allow nextUpdate to precede thisUpdate */
+    if (ASN1_STRING_cmp(nextupd, thisupd) < 0) {
+        OCSPerr(OCSP_F_OCSP_CHECK_VALIDITY,
+                OCSP_R_NEXTUPDATE_BEFORE_THISUPDATE);
+        ret = 0;
+    }
+
+    return ret;
+}
+
+const OCSP_CERTID *OCSP_SINGLERESP_get0_id(const OCSP_SINGLERESP *single)
+{
+    return single->certId;
+}
diff --git a/crypto/ocsp/ocsp_err.c b/crypto/ocsp/ocsp_err.c
new file mode 100644
index 000000000..a2d96e9c9
--- /dev/null
+++ b/crypto/ocsp/ocsp_err.c
@@ -0,0 +1,91 @@
+/*
+ * Generated by util/mkerr.pl DO NOT EDIT
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include 
+
+/* BEGIN ERROR CODES */
+#ifndef OPENSSL_NO_ERR
+
+# define ERR_FUNC(func) ERR_PACK(ERR_LIB_OCSP,func,0)
+# define ERR_REASON(reason) ERR_PACK(ERR_LIB_OCSP,0,reason)
+
+static ERR_STRING_DATA OCSP_str_functs[] = {
+    {ERR_FUNC(OCSP_F_D2I_OCSP_NONCE), "d2i_ocsp_nonce"},
+    {ERR_FUNC(OCSP_F_OCSP_BASIC_ADD1_STATUS), "OCSP_basic_add1_status"},
+    {ERR_FUNC(OCSP_F_OCSP_BASIC_SIGN), "OCSP_basic_sign"},
+    {ERR_FUNC(OCSP_F_OCSP_BASIC_VERIFY), "OCSP_basic_verify"},
+    {ERR_FUNC(OCSP_F_OCSP_CERT_ID_NEW), "OCSP_cert_id_new"},
+    {ERR_FUNC(OCSP_F_OCSP_CHECK_DELEGATED), "ocsp_check_delegated"},
+    {ERR_FUNC(OCSP_F_OCSP_CHECK_IDS), "ocsp_check_ids"},
+    {ERR_FUNC(OCSP_F_OCSP_CHECK_ISSUER), "ocsp_check_issuer"},
+    {ERR_FUNC(OCSP_F_OCSP_CHECK_VALIDITY), "OCSP_check_validity"},
+    {ERR_FUNC(OCSP_F_OCSP_MATCH_ISSUERID), "ocsp_match_issuerid"},
+    {ERR_FUNC(OCSP_F_OCSP_PARSE_URL), "OCSP_parse_url"},
+    {ERR_FUNC(OCSP_F_OCSP_REQUEST_SIGN), "OCSP_request_sign"},
+    {ERR_FUNC(OCSP_F_OCSP_REQUEST_VERIFY), "OCSP_request_verify"},
+    {ERR_FUNC(OCSP_F_OCSP_RESPONSE_GET1_BASIC), "OCSP_response_get1_basic"},
+    {ERR_FUNC(OCSP_F_PARSE_HTTP_LINE1), "parse_http_line1"},
+    {0, NULL}
+};
+
+static ERR_STRING_DATA OCSP_str_reasons[] = {
+    {ERR_REASON(OCSP_R_CERTIFICATE_VERIFY_ERROR), "certificate verify error"},
+    {ERR_REASON(OCSP_R_DIGEST_ERR), "digest err"},
+    {ERR_REASON(OCSP_R_ERROR_IN_NEXTUPDATE_FIELD),
+     "error in nextupdate field"},
+    {ERR_REASON(OCSP_R_ERROR_IN_THISUPDATE_FIELD),
+     "error in thisupdate field"},
+    {ERR_REASON(OCSP_R_ERROR_PARSING_URL), "error parsing url"},
+    {ERR_REASON(OCSP_R_MISSING_OCSPSIGNING_USAGE),
+     "missing ocspsigning usage"},
+    {ERR_REASON(OCSP_R_NEXTUPDATE_BEFORE_THISUPDATE),
+     "nextupdate before thisupdate"},
+    {ERR_REASON(OCSP_R_NOT_BASIC_RESPONSE), "not basic response"},
+    {ERR_REASON(OCSP_R_NO_CERTIFICATES_IN_CHAIN), "no certificates in chain"},
+    {ERR_REASON(OCSP_R_NO_RESPONSE_DATA), "no response data"},
+    {ERR_REASON(OCSP_R_NO_REVOKED_TIME), "no revoked time"},
+    {ERR_REASON(OCSP_R_NO_SIGNER_KEY), "no signer key"},
+    {ERR_REASON(OCSP_R_PRIVATE_KEY_DOES_NOT_MATCH_CERTIFICATE),
+     "private key does not match certificate"},
+    {ERR_REASON(OCSP_R_REQUEST_NOT_SIGNED), "request not signed"},
+    {ERR_REASON(OCSP_R_RESPONSE_CONTAINS_NO_REVOCATION_DATA),
+     "response contains no revocation data"},
+    {ERR_REASON(OCSP_R_ROOT_CA_NOT_TRUSTED), "root ca not trusted"},
+    {ERR_REASON(OCSP_R_SERVER_RESPONSE_ERROR), "server response error"},
+    {ERR_REASON(OCSP_R_SERVER_RESPONSE_PARSE_ERROR),
+     "server response parse error"},
+    {ERR_REASON(OCSP_R_SIGNATURE_FAILURE), "signature failure"},
+    {ERR_REASON(OCSP_R_SIGNER_CERTIFICATE_NOT_FOUND),
+     "signer certificate not found"},
+    {ERR_REASON(OCSP_R_STATUS_EXPIRED), "status expired"},
+    {ERR_REASON(OCSP_R_STATUS_NOT_YET_VALID), "status not yet valid"},
+    {ERR_REASON(OCSP_R_STATUS_TOO_OLD), "status too old"},
+    {ERR_REASON(OCSP_R_UNKNOWN_MESSAGE_DIGEST), "unknown message digest"},
+    {ERR_REASON(OCSP_R_UNKNOWN_NID), "unknown nid"},
+    {ERR_REASON(OCSP_R_UNSUPPORTED_REQUESTORNAME_TYPE),
+     "unsupported requestorname type"},
+    {0, NULL}
+};
+
+#endif
+
+int ERR_load_OCSP_strings(void)
+{
+#ifndef OPENSSL_NO_ERR
+
+    if (ERR_func_error_string(OCSP_str_functs[0].error) == NULL) {
+        ERR_load_strings(0, OCSP_str_functs);
+        ERR_load_strings(0, OCSP_str_reasons);
+    }
+#endif
+    return 1;
+}
diff --git a/crypto/ocsp/ocsp_ext.c b/crypto/ocsp/ocsp_ext.c
new file mode 100644
index 000000000..b829b2e4e
--- /dev/null
+++ b/crypto/ocsp/ocsp_ext.c
@@ -0,0 +1,472 @@
+/*
+ * Copyright 2000-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+#include 
+#include "ocsp_lcl.h"
+#include 
+#include 
+
+/* Standard wrapper functions for extensions */
+
+/* OCSP request extensions */
+
+int OCSP_REQUEST_get_ext_count(OCSP_REQUEST *x)
+{
+    return (X509v3_get_ext_count(x->tbsRequest.requestExtensions));
+}
+
+int OCSP_REQUEST_get_ext_by_NID(OCSP_REQUEST *x, int nid, int lastpos)
+{
+    return (X509v3_get_ext_by_NID
+            (x->tbsRequest.requestExtensions, nid, lastpos));
+}
+
+int OCSP_REQUEST_get_ext_by_OBJ(OCSP_REQUEST *x, const ASN1_OBJECT *obj,
+                                int lastpos)
+{
+    return (X509v3_get_ext_by_OBJ
+            (x->tbsRequest.requestExtensions, obj, lastpos));
+}
+
+int OCSP_REQUEST_get_ext_by_critical(OCSP_REQUEST *x, int crit, int lastpos)
+{
+    return (X509v3_get_ext_by_critical
+            (x->tbsRequest.requestExtensions, crit, lastpos));
+}
+
+X509_EXTENSION *OCSP_REQUEST_get_ext(OCSP_REQUEST *x, int loc)
+{
+    return (X509v3_get_ext(x->tbsRequest.requestExtensions, loc));
+}
+
+X509_EXTENSION *OCSP_REQUEST_delete_ext(OCSP_REQUEST *x, int loc)
+{
+    return (X509v3_delete_ext(x->tbsRequest.requestExtensions, loc));
+}
+
+void *OCSP_REQUEST_get1_ext_d2i(OCSP_REQUEST *x, int nid, int *crit, int *idx)
+{
+    return X509V3_get_d2i(x->tbsRequest.requestExtensions, nid, crit, idx);
+}
+
+int OCSP_REQUEST_add1_ext_i2d(OCSP_REQUEST *x, int nid, void *value, int crit,
+                              unsigned long flags)
+{
+    return X509V3_add1_i2d(&x->tbsRequest.requestExtensions, nid, value,
+                           crit, flags);
+}
+
+int OCSP_REQUEST_add_ext(OCSP_REQUEST *x, X509_EXTENSION *ex, int loc)
+{
+    return (X509v3_add_ext(&(x->tbsRequest.requestExtensions), ex, loc) !=
+            NULL);
+}
+
+/* Single extensions */
+
+int OCSP_ONEREQ_get_ext_count(OCSP_ONEREQ *x)
+{
+    return (X509v3_get_ext_count(x->singleRequestExtensions));
+}
+
+int OCSP_ONEREQ_get_ext_by_NID(OCSP_ONEREQ *x, int nid, int lastpos)
+{
+    return (X509v3_get_ext_by_NID(x->singleRequestExtensions, nid, lastpos));
+}
+
+int OCSP_ONEREQ_get_ext_by_OBJ(OCSP_ONEREQ *x, const ASN1_OBJECT *obj,
+                               int lastpos)
+{
+    return (X509v3_get_ext_by_OBJ(x->singleRequestExtensions, obj, lastpos));
+}
+
+int OCSP_ONEREQ_get_ext_by_critical(OCSP_ONEREQ *x, int crit, int lastpos)
+{
+    return (X509v3_get_ext_by_critical
+            (x->singleRequestExtensions, crit, lastpos));
+}
+
+X509_EXTENSION *OCSP_ONEREQ_get_ext(OCSP_ONEREQ *x, int loc)
+{
+    return (X509v3_get_ext(x->singleRequestExtensions, loc));
+}
+
+X509_EXTENSION *OCSP_ONEREQ_delete_ext(OCSP_ONEREQ *x, int loc)
+{
+    return (X509v3_delete_ext(x->singleRequestExtensions, loc));
+}
+
+void *OCSP_ONEREQ_get1_ext_d2i(OCSP_ONEREQ *x, int nid, int *crit, int *idx)
+{
+    return X509V3_get_d2i(x->singleRequestExtensions, nid, crit, idx);
+}
+
+int OCSP_ONEREQ_add1_ext_i2d(OCSP_ONEREQ *x, int nid, void *value, int crit,
+                             unsigned long flags)
+{
+    return X509V3_add1_i2d(&x->singleRequestExtensions, nid, value, crit,
+                           flags);
+}
+
+int OCSP_ONEREQ_add_ext(OCSP_ONEREQ *x, X509_EXTENSION *ex, int loc)
+{
+    return (X509v3_add_ext(&(x->singleRequestExtensions), ex, loc) != NULL);
+}
+
+/* OCSP Basic response */
+
+int OCSP_BASICRESP_get_ext_count(OCSP_BASICRESP *x)
+{
+    return (X509v3_get_ext_count(x->tbsResponseData.responseExtensions));
+}
+
+int OCSP_BASICRESP_get_ext_by_NID(OCSP_BASICRESP *x, int nid, int lastpos)
+{
+    return (X509v3_get_ext_by_NID
+            (x->tbsResponseData.responseExtensions, nid, lastpos));
+}
+
+int OCSP_BASICRESP_get_ext_by_OBJ(OCSP_BASICRESP *x, const ASN1_OBJECT *obj,
+                                  int lastpos)
+{
+    return (X509v3_get_ext_by_OBJ
+            (x->tbsResponseData.responseExtensions, obj, lastpos));
+}
+
+int OCSP_BASICRESP_get_ext_by_critical(OCSP_BASICRESP *x, int crit,
+                                       int lastpos)
+{
+    return (X509v3_get_ext_by_critical
+            (x->tbsResponseData.responseExtensions, crit, lastpos));
+}
+
+X509_EXTENSION *OCSP_BASICRESP_get_ext(OCSP_BASICRESP *x, int loc)
+{
+    return (X509v3_get_ext(x->tbsResponseData.responseExtensions, loc));
+}
+
+X509_EXTENSION *OCSP_BASICRESP_delete_ext(OCSP_BASICRESP *x, int loc)
+{
+    return (X509v3_delete_ext(x->tbsResponseData.responseExtensions, loc));
+}
+
+void *OCSP_BASICRESP_get1_ext_d2i(OCSP_BASICRESP *x, int nid, int *crit,
+                                  int *idx)
+{
+    return X509V3_get_d2i(x->tbsResponseData.responseExtensions, nid, crit,
+                          idx);
+}
+
+int OCSP_BASICRESP_add1_ext_i2d(OCSP_BASICRESP *x, int nid, void *value,
+                                int crit, unsigned long flags)
+{
+    return X509V3_add1_i2d(&x->tbsResponseData.responseExtensions, nid,
+                           value, crit, flags);
+}
+
+int OCSP_BASICRESP_add_ext(OCSP_BASICRESP *x, X509_EXTENSION *ex, int loc)
+{
+    return (X509v3_add_ext(&(x->tbsResponseData.responseExtensions), ex, loc)
+            != NULL);
+}
+
+/* OCSP single response extensions */
+
+int OCSP_SINGLERESP_get_ext_count(OCSP_SINGLERESP *x)
+{
+    return (X509v3_get_ext_count(x->singleExtensions));
+}
+
+int OCSP_SINGLERESP_get_ext_by_NID(OCSP_SINGLERESP *x, int nid, int lastpos)
+{
+    return (X509v3_get_ext_by_NID(x->singleExtensions, nid, lastpos));
+}
+
+int OCSP_SINGLERESP_get_ext_by_OBJ(OCSP_SINGLERESP *x, const ASN1_OBJECT *obj,
+                                   int lastpos)
+{
+    return (X509v3_get_ext_by_OBJ(x->singleExtensions, obj, lastpos));
+}
+
+int OCSP_SINGLERESP_get_ext_by_critical(OCSP_SINGLERESP *x, int crit,
+                                        int lastpos)
+{
+    return (X509v3_get_ext_by_critical(x->singleExtensions, crit, lastpos));
+}
+
+X509_EXTENSION *OCSP_SINGLERESP_get_ext(OCSP_SINGLERESP *x, int loc)
+{
+    return (X509v3_get_ext(x->singleExtensions, loc));
+}
+
+X509_EXTENSION *OCSP_SINGLERESP_delete_ext(OCSP_SINGLERESP *x, int loc)
+{
+    return (X509v3_delete_ext(x->singleExtensions, loc));
+}
+
+void *OCSP_SINGLERESP_get1_ext_d2i(OCSP_SINGLERESP *x, int nid, int *crit,
+                                   int *idx)
+{
+    return X509V3_get_d2i(x->singleExtensions, nid, crit, idx);
+}
+
+int OCSP_SINGLERESP_add1_ext_i2d(OCSP_SINGLERESP *x, int nid, void *value,
+                                 int crit, unsigned long flags)
+{
+    return X509V3_add1_i2d(&x->singleExtensions, nid, value, crit, flags);
+}
+
+int OCSP_SINGLERESP_add_ext(OCSP_SINGLERESP *x, X509_EXTENSION *ex, int loc)
+{
+    return (X509v3_add_ext(&(x->singleExtensions), ex, loc) != NULL);
+}
+
+/* also CRL Entry Extensions */
+
+/* Nonce handling functions */
+
+/*
+ * Add a nonce to an extension stack. A nonce can be specified or if NULL a
+ * random nonce will be generated. Note: OpenSSL 0.9.7d and later create an
+ * OCTET STRING containing the nonce, previous versions used the raw nonce.
+ */
+
+static int ocsp_add1_nonce(STACK_OF(X509_EXTENSION) **exts,
+                           unsigned char *val, int len)
+{
+    unsigned char *tmpval;
+    ASN1_OCTET_STRING os;
+    int ret = 0;
+    if (len <= 0)
+        len = OCSP_DEFAULT_NONCE_LENGTH;
+    /*
+     * Create the OCTET STRING manually by writing out the header and
+     * appending the content octets. This avoids an extra memory allocation
+     * operation in some cases. Applications should *NOT* do this because it
+     * relies on library internals.
+     */
+    os.length = ASN1_object_size(0, len, V_ASN1_OCTET_STRING);
+    if (os.length < 0)
+        return 0;
+
+    os.data = OPENSSL_malloc(os.length);
+    if (os.data == NULL)
+        goto err;
+    tmpval = os.data;
+    ASN1_put_object(&tmpval, 0, len, V_ASN1_OCTET_STRING, V_ASN1_UNIVERSAL);
+    if (val)
+        memcpy(tmpval, val, len);
+    else if (RAND_bytes(tmpval, len) <= 0)
+        goto err;
+    if (!X509V3_add1_i2d(exts, NID_id_pkix_OCSP_Nonce,
+                         &os, 0, X509V3_ADD_REPLACE))
+        goto err;
+    ret = 1;
+ err:
+    OPENSSL_free(os.data);
+    return ret;
+}
+
+/* Add nonce to an OCSP request */
+
+int OCSP_request_add1_nonce(OCSP_REQUEST *req, unsigned char *val, int len)
+{
+    return ocsp_add1_nonce(&req->tbsRequest.requestExtensions, val, len);
+}
+
+/* Same as above but for a response */
+
+int OCSP_basic_add1_nonce(OCSP_BASICRESP *resp, unsigned char *val, int len)
+{
+    return ocsp_add1_nonce(&resp->tbsResponseData.responseExtensions, val,
+                           len);
+}
+
+/*-
+ * Check nonce validity in a request and response.
+ * Return value reflects result:
+ *  1: nonces present and equal.
+ *  2: nonces both absent.
+ *  3: nonce present in response only.
+ *  0: nonces both present and not equal.
+ * -1: nonce in request only.
+ *
+ *  For most responders clients can check return > 0.
+ *  If responder doesn't handle nonces return != 0 may be
+ *  necessary. return == 0 is always an error.
+ */
+
+int OCSP_check_nonce(OCSP_REQUEST *req, OCSP_BASICRESP *bs)
+{
+    /*
+     * Since we are only interested in the presence or absence of
+     * the nonce and comparing its value there is no need to use
+     * the X509V3 routines: this way we can avoid them allocating an
+     * ASN1_OCTET_STRING structure for the value which would be
+     * freed immediately anyway.
+     */
+
+    int req_idx, resp_idx;
+    X509_EXTENSION *req_ext, *resp_ext;
+    req_idx = OCSP_REQUEST_get_ext_by_NID(req, NID_id_pkix_OCSP_Nonce, -1);
+    resp_idx = OCSP_BASICRESP_get_ext_by_NID(bs, NID_id_pkix_OCSP_Nonce, -1);
+    /* Check both absent */
+    if ((req_idx < 0) && (resp_idx < 0))
+        return 2;
+    /* Check in request only */
+    if ((req_idx >= 0) && (resp_idx < 0))
+        return -1;
+    /* Check in response but not request */
+    if ((req_idx < 0) && (resp_idx >= 0))
+        return 3;
+    /*
+     * Otherwise nonce in request and response so retrieve the extensions
+     */
+    req_ext = OCSP_REQUEST_get_ext(req, req_idx);
+    resp_ext = OCSP_BASICRESP_get_ext(bs, resp_idx);
+    if (ASN1_OCTET_STRING_cmp(X509_EXTENSION_get_data(req_ext),
+                              X509_EXTENSION_get_data(resp_ext)))
+        return 0;
+    return 1;
+}
+
+/*
+ * Copy the nonce value (if any) from an OCSP request to a response.
+ */
+
+int OCSP_copy_nonce(OCSP_BASICRESP *resp, OCSP_REQUEST *req)
+{
+    X509_EXTENSION *req_ext;
+    int req_idx;
+    /* Check for nonce in request */
+    req_idx = OCSP_REQUEST_get_ext_by_NID(req, NID_id_pkix_OCSP_Nonce, -1);
+    /* If no nonce that's OK */
+    if (req_idx < 0)
+        return 2;
+    req_ext = OCSP_REQUEST_get_ext(req, req_idx);
+    return OCSP_BASICRESP_add_ext(resp, req_ext, -1);
+}
+
+X509_EXTENSION *OCSP_crlID_new(const char *url, long *n, char *tim)
+{
+    X509_EXTENSION *x = NULL;
+    OCSP_CRLID *cid = NULL;
+
+    if ((cid = OCSP_CRLID_new()) == NULL)
+        goto err;
+    if (url) {
+        if ((cid->crlUrl = ASN1_IA5STRING_new()) == NULL)
+            goto err;
+        if (!(ASN1_STRING_set(cid->crlUrl, url, -1)))
+            goto err;
+    }
+    if (n) {
+        if ((cid->crlNum = ASN1_INTEGER_new()) == NULL)
+            goto err;
+        if (!(ASN1_INTEGER_set(cid->crlNum, *n)))
+            goto err;
+    }
+    if (tim) {
+        if ((cid->crlTime = ASN1_GENERALIZEDTIME_new()) == NULL)
+            goto err;
+        if (!(ASN1_GENERALIZEDTIME_set_string(cid->crlTime, tim)))
+            goto err;
+    }
+    x = X509V3_EXT_i2d(NID_id_pkix_OCSP_CrlID, 0, cid);
+ err:
+    OCSP_CRLID_free(cid);
+    return x;
+}
+
+/*   AcceptableResponses ::= SEQUENCE OF OBJECT IDENTIFIER */
+X509_EXTENSION *OCSP_accept_responses_new(char **oids)
+{
+    int nid;
+    STACK_OF(ASN1_OBJECT) *sk = NULL;
+    ASN1_OBJECT *o = NULL;
+    X509_EXTENSION *x = NULL;
+
+    if ((sk = sk_ASN1_OBJECT_new_null()) == NULL)
+        goto err;
+    while (oids && *oids) {
+        if ((nid = OBJ_txt2nid(*oids)) != NID_undef && (o = OBJ_nid2obj(nid)))
+            sk_ASN1_OBJECT_push(sk, o);
+        oids++;
+    }
+    x = X509V3_EXT_i2d(NID_id_pkix_OCSP_acceptableResponses, 0, sk);
+ err:
+    sk_ASN1_OBJECT_pop_free(sk, ASN1_OBJECT_free);
+    return x;
+}
+
+/*  ArchiveCutoff ::= GeneralizedTime */
+X509_EXTENSION *OCSP_archive_cutoff_new(char *tim)
+{
+    X509_EXTENSION *x = NULL;
+    ASN1_GENERALIZEDTIME *gt = NULL;
+
+    if ((gt = ASN1_GENERALIZEDTIME_new()) == NULL)
+        goto err;
+    if (!(ASN1_GENERALIZEDTIME_set_string(gt, tim)))
+        goto err;
+    x = X509V3_EXT_i2d(NID_id_pkix_OCSP_archiveCutoff, 0, gt);
+ err:
+    ASN1_GENERALIZEDTIME_free(gt);
+    return x;
+}
+
+/*
+ * per ACCESS_DESCRIPTION parameter are oids, of which there are currently
+ * two--NID_ad_ocsp, NID_id_ad_caIssuers--and GeneralName value.  This method
+ * forces NID_ad_ocsp and uniformResourceLocator [6] IA5String.
+ */
+X509_EXTENSION *OCSP_url_svcloc_new(X509_NAME *issuer, const char **urls)
+{
+    X509_EXTENSION *x = NULL;
+    ASN1_IA5STRING *ia5 = NULL;
+    OCSP_SERVICELOC *sloc = NULL;
+    ACCESS_DESCRIPTION *ad = NULL;
+
+    if ((sloc = OCSP_SERVICELOC_new()) == NULL)
+        goto err;
+    if ((sloc->issuer = X509_NAME_dup(issuer)) == NULL)
+        goto err;
+    if (urls && *urls
+        && (sloc->locator = sk_ACCESS_DESCRIPTION_new_null()) == NULL)
+        goto err;
+    while (urls && *urls) {
+        if ((ad = ACCESS_DESCRIPTION_new()) == NULL)
+            goto err;
+        if ((ad->method = OBJ_nid2obj(NID_ad_OCSP)) == NULL)
+            goto err;
+        if ((ad->location = GENERAL_NAME_new()) == NULL)
+            goto err;
+        if ((ia5 = ASN1_IA5STRING_new()) == NULL)
+            goto err;
+        if (!ASN1_STRING_set((ASN1_STRING *)ia5, *urls, -1))
+            goto err;
+        ad->location->type = GEN_URI;
+        ad->location->d.ia5 = ia5;
+        ia5 = NULL;
+        if (!sk_ACCESS_DESCRIPTION_push(sloc->locator, ad))
+            goto err;
+        ad = NULL;
+        urls++;
+    }
+    x = X509V3_EXT_i2d(NID_id_pkix_OCSP_serviceLocator, 0, sloc);
+ err:
+    ASN1_IA5STRING_free(ia5);
+    ACCESS_DESCRIPTION_free(ad);
+    OCSP_SERVICELOC_free(sloc);
+    return x;
+}
diff --git a/crypto/ocsp/ocsp_ht.c b/crypto/ocsp/ocsp_ht.c
new file mode 100644
index 000000000..680edfa5c
--- /dev/null
+++ b/crypto/ocsp/ocsp_ht.c
@@ -0,0 +1,499 @@
+/*
+ * Copyright 2001-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include "e_os.h"
+#include 
+#include 
+#include 
+#include 
+
+/* Stateful OCSP request code, supporting non-blocking I/O */
+
+/* Opaque OCSP request status structure */
+
+struct ocsp_req_ctx_st {
+    int state;                  /* Current I/O state */
+    unsigned char *iobuf;       /* Line buffer */
+    int iobuflen;               /* Line buffer length */
+    BIO *io;                    /* BIO to perform I/O with */
+    BIO *mem;                   /* Memory BIO response is built into */
+    unsigned long asn1_len;     /* ASN1 length of response */
+    unsigned long max_resp_len; /* Maximum length of response */
+};
+
+#define OCSP_MAX_RESP_LENGTH    (100 * 1024)
+#define OCSP_MAX_LINE_LEN       4096;
+
+/* OCSP states */
+
+/* If set no reading should be performed */
+#define OHS_NOREAD              0x1000
+/* Error condition */
+#define OHS_ERROR               (0 | OHS_NOREAD)
+/* First line being read */
+#define OHS_FIRSTLINE           1
+/* MIME headers being read */
+#define OHS_HEADERS             2
+/* OCSP initial header (tag + length) being read */
+#define OHS_ASN1_HEADER         3
+/* OCSP content octets being read */
+#define OHS_ASN1_CONTENT        4
+/* First call: ready to start I/O */
+#define OHS_ASN1_WRITE_INIT     (5 | OHS_NOREAD)
+/* Request being sent */
+#define OHS_ASN1_WRITE          (6 | OHS_NOREAD)
+/* Request being flushed */
+#define OHS_ASN1_FLUSH          (7 | OHS_NOREAD)
+/* Completed */
+#define OHS_DONE                (8 | OHS_NOREAD)
+/* Headers set, no final \r\n included */
+#define OHS_HTTP_HEADER         (9 | OHS_NOREAD)
+
+static int parse_http_line1(char *line);
+
+OCSP_REQ_CTX *OCSP_REQ_CTX_new(BIO *io, int maxline)
+{
+    OCSP_REQ_CTX *rctx = OPENSSL_zalloc(sizeof(*rctx));
+
+    if (rctx == NULL)
+        return NULL;
+    rctx->state = OHS_ERROR;
+    rctx->max_resp_len = OCSP_MAX_RESP_LENGTH;
+    rctx->mem = BIO_new(BIO_s_mem());
+    rctx->io = io;
+    if (maxline > 0)
+        rctx->iobuflen = maxline;
+    else
+        rctx->iobuflen = OCSP_MAX_LINE_LEN;
+    rctx->iobuf = OPENSSL_malloc(rctx->iobuflen);
+    if (rctx->iobuf == NULL || rctx->mem == NULL) {
+        OCSP_REQ_CTX_free(rctx);
+        return NULL;
+    }
+    return rctx;
+}
+
+void OCSP_REQ_CTX_free(OCSP_REQ_CTX *rctx)
+{
+    if (!rctx)
+        return;
+    BIO_free(rctx->mem);
+    OPENSSL_free(rctx->iobuf);
+    OPENSSL_free(rctx);
+}
+
+BIO *OCSP_REQ_CTX_get0_mem_bio(OCSP_REQ_CTX *rctx)
+{
+    return rctx->mem;
+}
+
+void OCSP_set_max_response_length(OCSP_REQ_CTX *rctx, unsigned long len)
+{
+    if (len == 0)
+        rctx->max_resp_len = OCSP_MAX_RESP_LENGTH;
+    else
+        rctx->max_resp_len = len;
+}
+
+int OCSP_REQ_CTX_i2d(OCSP_REQ_CTX *rctx, const ASN1_ITEM *it, ASN1_VALUE *val)
+{
+    static const char req_hdr[] =
+        "Content-Type: application/ocsp-request\r\n"
+        "Content-Length: %d\r\n\r\n";
+    int reqlen = ASN1_item_i2d(val, NULL, it);
+    if (BIO_printf(rctx->mem, req_hdr, reqlen) <= 0)
+        return 0;
+    if (ASN1_item_i2d_bio(it, rctx->mem, val) <= 0)
+        return 0;
+    rctx->state = OHS_ASN1_WRITE_INIT;
+    return 1;
+}
+
+int OCSP_REQ_CTX_nbio_d2i(OCSP_REQ_CTX *rctx,
+                          ASN1_VALUE **pval, const ASN1_ITEM *it)
+{
+    int rv, len;
+    const unsigned char *p;
+
+    rv = OCSP_REQ_CTX_nbio(rctx);
+    if (rv != 1)
+        return rv;
+
+    len = BIO_get_mem_data(rctx->mem, &p);
+    *pval = ASN1_item_d2i(NULL, &p, len, it);
+    if (*pval == NULL) {
+        rctx->state = OHS_ERROR;
+        return 0;
+    }
+    return 1;
+}
+
+int OCSP_REQ_CTX_http(OCSP_REQ_CTX *rctx, const char *op, const char *path)
+{
+    static const char http_hdr[] = "%s %s HTTP/1.0\r\n";
+
+    if (!path)
+        path = "/";
+
+    if (BIO_printf(rctx->mem, http_hdr, op, path) <= 0)
+        return 0;
+    rctx->state = OHS_HTTP_HEADER;
+    return 1;
+}
+
+int OCSP_REQ_CTX_set1_req(OCSP_REQ_CTX *rctx, OCSP_REQUEST *req)
+{
+    return OCSP_REQ_CTX_i2d(rctx, ASN1_ITEM_rptr(OCSP_REQUEST),
+                            (ASN1_VALUE *)req);
+}
+
+int OCSP_REQ_CTX_add1_header(OCSP_REQ_CTX *rctx,
+                             const char *name, const char *value)
+{
+    if (!name)
+        return 0;
+    if (BIO_puts(rctx->mem, name) <= 0)
+        return 0;
+    if (value) {
+        if (BIO_write(rctx->mem, ": ", 2) != 2)
+            return 0;
+        if (BIO_puts(rctx->mem, value) <= 0)
+            return 0;
+    }
+    if (BIO_write(rctx->mem, "\r\n", 2) != 2)
+        return 0;
+    rctx->state = OHS_HTTP_HEADER;
+    return 1;
+}
+
+OCSP_REQ_CTX *OCSP_sendreq_new(BIO *io, const char *path, OCSP_REQUEST *req,
+                               int maxline)
+{
+
+    OCSP_REQ_CTX *rctx = NULL;
+    rctx = OCSP_REQ_CTX_new(io, maxline);
+    if (rctx == NULL)
+        return NULL;
+
+    if (!OCSP_REQ_CTX_http(rctx, "POST", path))
+        goto err;
+
+    if (req && !OCSP_REQ_CTX_set1_req(rctx, req))
+        goto err;
+
+    return rctx;
+
+ err:
+    OCSP_REQ_CTX_free(rctx);
+    return NULL;
+}
+
+/*
+ * Parse the HTTP response. This will look like this: "HTTP/1.0 200 OK". We
+ * need to obtain the numeric code and (optional) informational message.
+ */
+
+static int parse_http_line1(char *line)
+{
+    int retcode;
+    char *p, *q, *r;
+    /* Skip to first white space (passed protocol info) */
+
+    for (p = line; *p && !isspace((unsigned char)*p); p++)
+        continue;
+    if (!*p) {
+        OCSPerr(OCSP_F_PARSE_HTTP_LINE1, OCSP_R_SERVER_RESPONSE_PARSE_ERROR);
+        return 0;
+    }
+
+    /* Skip past white space to start of response code */
+    while (*p && isspace((unsigned char)*p))
+        p++;
+
+    if (!*p) {
+        OCSPerr(OCSP_F_PARSE_HTTP_LINE1, OCSP_R_SERVER_RESPONSE_PARSE_ERROR);
+        return 0;
+    }
+
+    /* Find end of response code: first whitespace after start of code */
+    for (q = p; *q && !isspace((unsigned char)*q); q++)
+        continue;
+
+    if (!*q) {
+        OCSPerr(OCSP_F_PARSE_HTTP_LINE1, OCSP_R_SERVER_RESPONSE_PARSE_ERROR);
+        return 0;
+    }
+
+    /* Set end of response code and start of message */
+    *q++ = 0;
+
+    /* Attempt to parse numeric code */
+    retcode = strtoul(p, &r, 10);
+
+    if (*r)
+        return 0;
+
+    /* Skip over any leading white space in message */
+    while (*q && isspace((unsigned char)*q))
+        q++;
+
+    if (*q) {
+        /*
+         * Finally zap any trailing white space in message (include CRLF)
+         */
+
+        /* We know q has a non white space character so this is OK */
+        for (r = q + strlen(q) - 1; isspace((unsigned char)*r); r--)
+            *r = 0;
+    }
+    if (retcode != 200) {
+        OCSPerr(OCSP_F_PARSE_HTTP_LINE1, OCSP_R_SERVER_RESPONSE_ERROR);
+        if (!*q)
+            ERR_add_error_data(2, "Code=", p);
+        else
+            ERR_add_error_data(4, "Code=", p, ",Reason=", q);
+        return 0;
+    }
+
+    return 1;
+
+}
+
+int OCSP_REQ_CTX_nbio(OCSP_REQ_CTX *rctx)
+{
+    int i, n;
+    const unsigned char *p;
+ next_io:
+    if (!(rctx->state & OHS_NOREAD)) {
+        n = BIO_read(rctx->io, rctx->iobuf, rctx->iobuflen);
+
+        if (n <= 0) {
+            if (BIO_should_retry(rctx->io))
+                return -1;
+            return 0;
+        }
+
+        /* Write data to memory BIO */
+
+        if (BIO_write(rctx->mem, rctx->iobuf, n) != n)
+            return 0;
+    }
+
+    switch (rctx->state) {
+    case OHS_HTTP_HEADER:
+        /* Last operation was adding headers: need a final \r\n */
+        if (BIO_write(rctx->mem, "\r\n", 2) != 2) {
+            rctx->state = OHS_ERROR;
+            return 0;
+        }
+        rctx->state = OHS_ASN1_WRITE_INIT;
+
+    case OHS_ASN1_WRITE_INIT:
+        rctx->asn1_len = BIO_get_mem_data(rctx->mem, NULL);
+        rctx->state = OHS_ASN1_WRITE;
+
+    case OHS_ASN1_WRITE:
+        n = BIO_get_mem_data(rctx->mem, &p);
+
+        i = BIO_write(rctx->io, p + (n - rctx->asn1_len), rctx->asn1_len);
+
+        if (i <= 0) {
+            if (BIO_should_retry(rctx->io))
+                return -1;
+            rctx->state = OHS_ERROR;
+            return 0;
+        }
+
+        rctx->asn1_len -= i;
+
+        if (rctx->asn1_len > 0)
+            goto next_io;
+
+        rctx->state = OHS_ASN1_FLUSH;
+
+        (void)BIO_reset(rctx->mem);
+
+    case OHS_ASN1_FLUSH:
+
+        i = BIO_flush(rctx->io);
+
+        if (i > 0) {
+            rctx->state = OHS_FIRSTLINE;
+            goto next_io;
+        }
+
+        if (BIO_should_retry(rctx->io))
+            return -1;
+
+        rctx->state = OHS_ERROR;
+        return 0;
+
+    case OHS_ERROR:
+        return 0;
+
+    case OHS_FIRSTLINE:
+    case OHS_HEADERS:
+
+        /* Attempt to read a line in */
+
+ next_line:
+        /*
+         * Due to &%^*$" memory BIO behaviour with BIO_gets we have to check
+         * there's a complete line in there before calling BIO_gets or we'll
+         * just get a partial read.
+         */
+        n = BIO_get_mem_data(rctx->mem, &p);
+        if ((n <= 0) || !memchr(p, '\n', n)) {
+            if (n >= rctx->iobuflen) {
+                rctx->state = OHS_ERROR;
+                return 0;
+            }
+            goto next_io;
+        }
+        n = BIO_gets(rctx->mem, (char *)rctx->iobuf, rctx->iobuflen);
+
+        if (n <= 0) {
+            if (BIO_should_retry(rctx->mem))
+                goto next_io;
+            rctx->state = OHS_ERROR;
+            return 0;
+        }
+
+        /* Don't allow excessive lines */
+        if (n == rctx->iobuflen) {
+            rctx->state = OHS_ERROR;
+            return 0;
+        }
+
+        /* First line */
+        if (rctx->state == OHS_FIRSTLINE) {
+            if (parse_http_line1((char *)rctx->iobuf)) {
+                rctx->state = OHS_HEADERS;
+                goto next_line;
+            } else {
+                rctx->state = OHS_ERROR;
+                return 0;
+            }
+        } else {
+            /* Look for blank line: end of headers */
+            for (p = rctx->iobuf; *p; p++) {
+                if ((*p != '\r') && (*p != '\n'))
+                    break;
+            }
+            if (*p)
+                goto next_line;
+
+            rctx->state = OHS_ASN1_HEADER;
+
+        }
+
+        /* Fall thru */
+
+    case OHS_ASN1_HEADER:
+        /*
+         * Now reading ASN1 header: can read at least 2 bytes which is enough
+         * for ASN1 SEQUENCE header and either length field or at least the
+         * length of the length field.
+         */
+        n = BIO_get_mem_data(rctx->mem, &p);
+        if (n < 2)
+            goto next_io;
+
+        /* Check it is an ASN1 SEQUENCE */
+        if (*p++ != (V_ASN1_SEQUENCE | V_ASN1_CONSTRUCTED)) {
+            rctx->state = OHS_ERROR;
+            return 0;
+        }
+
+        /* Check out length field */
+        if (*p & 0x80) {
+            /*
+             * If MSB set on initial length octet we can now always read 6
+             * octets: make sure we have them.
+             */
+            if (n < 6)
+                goto next_io;
+            n = *p & 0x7F;
+            /* Not NDEF or excessive length */
+            if (!n || (n > 4)) {
+                rctx->state = OHS_ERROR;
+                return 0;
+            }
+            p++;
+            rctx->asn1_len = 0;
+            for (i = 0; i < n; i++) {
+                rctx->asn1_len <<= 8;
+                rctx->asn1_len |= *p++;
+            }
+
+            if (rctx->asn1_len > rctx->max_resp_len) {
+                rctx->state = OHS_ERROR;
+                return 0;
+            }
+
+            rctx->asn1_len += n + 2;
+        } else
+            rctx->asn1_len = *p + 2;
+
+        rctx->state = OHS_ASN1_CONTENT;
+
+        /* Fall thru */
+
+    case OHS_ASN1_CONTENT:
+        n = BIO_get_mem_data(rctx->mem, NULL);
+        if (n < (int)rctx->asn1_len)
+            goto next_io;
+
+        rctx->state = OHS_DONE;
+        return 1;
+
+    case OHS_DONE:
+        return 1;
+
+    }
+
+    return 0;
+
+}
+
+int OCSP_sendreq_nbio(OCSP_RESPONSE **presp, OCSP_REQ_CTX *rctx)
+{
+    return OCSP_REQ_CTX_nbio_d2i(rctx,
+                                 (ASN1_VALUE **)presp,
+                                 ASN1_ITEM_rptr(OCSP_RESPONSE));
+}
+
+/* Blocking OCSP request handler: now a special case of non-blocking I/O */
+
+OCSP_RESPONSE *OCSP_sendreq_bio(BIO *b, const char *path, OCSP_REQUEST *req)
+{
+    OCSP_RESPONSE *resp = NULL;
+    OCSP_REQ_CTX *ctx;
+    int rv;
+
+    ctx = OCSP_sendreq_new(b, path, req, -1);
+
+    if (ctx == NULL)
+        return NULL;
+
+    do {
+        rv = OCSP_sendreq_nbio(&resp, ctx);
+    } while ((rv == -1) && BIO_should_retry(b));
+
+    OCSP_REQ_CTX_free(ctx);
+
+    if (rv)
+        return resp;
+
+    return NULL;
+}
diff --git a/crypto/ocsp/ocsp_lcl.h b/crypto/ocsp/ocsp_lcl.h
new file mode 100644
index 000000000..f93a268e0
--- /dev/null
+++ b/crypto/ocsp/ocsp_lcl.h
@@ -0,0 +1,216 @@
+/*
+ * Copyright 2015-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*-  CertID ::= SEQUENCE {
+ *       hashAlgorithm            AlgorithmIdentifier,
+ *       issuerNameHash     OCTET STRING, -- Hash of Issuer's DN
+ *       issuerKeyHash      OCTET STRING, -- Hash of Issuers public key (excluding the tag & length fields)
+ *       serialNumber       CertificateSerialNumber }
+ */
+struct ocsp_cert_id_st {
+    X509_ALGOR hashAlgorithm;
+    ASN1_OCTET_STRING issuerNameHash;
+    ASN1_OCTET_STRING issuerKeyHash;
+    ASN1_INTEGER serialNumber;
+};
+
+/*-  Request ::=     SEQUENCE {
+ *       reqCert                    CertID,
+ *       singleRequestExtensions    [0] EXPLICIT Extensions OPTIONAL }
+ */
+struct ocsp_one_request_st {
+    OCSP_CERTID *reqCert;
+    STACK_OF(X509_EXTENSION) *singleRequestExtensions;
+};
+
+/*-  TBSRequest      ::=     SEQUENCE {
+ *       version             [0] EXPLICIT Version DEFAULT v1,
+ *       requestorName       [1] EXPLICIT GeneralName OPTIONAL,
+ *       requestList             SEQUENCE OF Request,
+ *       requestExtensions   [2] EXPLICIT Extensions OPTIONAL }
+ */
+struct ocsp_req_info_st {
+    ASN1_INTEGER *version;
+    GENERAL_NAME *requestorName;
+    STACK_OF(OCSP_ONEREQ) *requestList;
+    STACK_OF(X509_EXTENSION) *requestExtensions;
+};
+
+/*-  Signature       ::=     SEQUENCE {
+ *       signatureAlgorithm   AlgorithmIdentifier,
+ *       signature            BIT STRING,
+ *       certs                [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL }
+ */
+struct ocsp_signature_st {
+    X509_ALGOR signatureAlgorithm;
+    ASN1_BIT_STRING *signature;
+    STACK_OF(X509) *certs;
+};
+
+/*-  OCSPRequest     ::=     SEQUENCE {
+ *       tbsRequest                  TBSRequest,
+ *       optionalSignature   [0]     EXPLICIT Signature OPTIONAL }
+ */
+struct ocsp_request_st {
+    OCSP_REQINFO tbsRequest;
+    OCSP_SIGNATURE *optionalSignature; /* OPTIONAL */
+};
+
+/*-  OCSPResponseStatus ::= ENUMERATED {
+ *       successful            (0),      --Response has valid confirmations
+ *       malformedRequest      (1),      --Illegal confirmation request
+ *       internalError         (2),      --Internal error in issuer
+ *       tryLater              (3),      --Try again later
+ *                                       --(4) is not used
+ *       sigRequired           (5),      --Must sign the request
+ *       unauthorized          (6)       --Request unauthorized
+ *   }
+ */
+
+/*-  ResponseBytes ::=       SEQUENCE {
+ *       responseType   OBJECT IDENTIFIER,
+ *       response       OCTET STRING }
+ */
+struct ocsp_resp_bytes_st {
+    ASN1_OBJECT *responseType;
+    ASN1_OCTET_STRING *response;
+};
+
+/*-  OCSPResponse ::= SEQUENCE {
+ *      responseStatus         OCSPResponseStatus,
+ *      responseBytes          [0] EXPLICIT ResponseBytes OPTIONAL }
+ */
+struct ocsp_response_st {
+    ASN1_ENUMERATED *responseStatus;
+    OCSP_RESPBYTES *responseBytes;
+};
+
+/*-  ResponderID ::= CHOICE {
+ *      byName   [1] Name,
+ *      byKey    [2] KeyHash }
+ */
+struct ocsp_responder_id_st {
+    int type;
+    union {
+        X509_NAME *byName;
+        ASN1_OCTET_STRING *byKey;
+    } value;
+};
+
+/*-  KeyHash ::= OCTET STRING --SHA-1 hash of responder's public key
+ *                            --(excluding the tag and length fields)
+ */
+
+/*-  RevokedInfo ::= SEQUENCE {
+ *       revocationTime              GeneralizedTime,
+ *       revocationReason    [0]     EXPLICIT CRLReason OPTIONAL }
+ */
+struct ocsp_revoked_info_st {
+    ASN1_GENERALIZEDTIME *revocationTime;
+    ASN1_ENUMERATED *revocationReason;
+};
+
+/*-  CertStatus ::= CHOICE {
+ *       good                [0]     IMPLICIT NULL,
+ *       revoked             [1]     IMPLICIT RevokedInfo,
+ *       unknown             [2]     IMPLICIT UnknownInfo }
+ */
+struct ocsp_cert_status_st {
+    int type;
+    union {
+        ASN1_NULL *good;
+        OCSP_REVOKEDINFO *revoked;
+        ASN1_NULL *unknown;
+    } value;
+};
+
+/*-  SingleResponse ::= SEQUENCE {
+ *      certID                       CertID,
+ *      certStatus                   CertStatus,
+ *      thisUpdate                   GeneralizedTime,
+ *      nextUpdate           [0]     EXPLICIT GeneralizedTime OPTIONAL,
+ *      singleExtensions     [1]     EXPLICIT Extensions OPTIONAL }
+ */
+struct ocsp_single_response_st {
+    OCSP_CERTID *certId;
+    OCSP_CERTSTATUS *certStatus;
+    ASN1_GENERALIZEDTIME *thisUpdate;
+    ASN1_GENERALIZEDTIME *nextUpdate;
+    STACK_OF(X509_EXTENSION) *singleExtensions;
+};
+
+/*-  ResponseData ::= SEQUENCE {
+ *      version              [0] EXPLICIT Version DEFAULT v1,
+ *      responderID              ResponderID,
+ *      producedAt               GeneralizedTime,
+ *      responses                SEQUENCE OF SingleResponse,
+ *      responseExtensions   [1] EXPLICIT Extensions OPTIONAL }
+ */
+struct ocsp_response_data_st {
+    ASN1_INTEGER *version;
+    OCSP_RESPID responderId;
+    ASN1_GENERALIZEDTIME *producedAt;
+    STACK_OF(OCSP_SINGLERESP) *responses;
+    STACK_OF(X509_EXTENSION) *responseExtensions;
+};
+
+/*-  BasicOCSPResponse       ::= SEQUENCE {
+ *      tbsResponseData      ResponseData,
+ *      signatureAlgorithm   AlgorithmIdentifier,
+ *      signature            BIT STRING,
+ *      certs                [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL }
+ */
+  /*
+   * Note 1: The value for "signature" is specified in the OCSP rfc2560 as
+   * follows: "The value for the signature SHALL be computed on the hash of
+   * the DER encoding ResponseData." This means that you must hash the
+   * DER-encoded tbsResponseData, and then run it through a crypto-signing
+   * function, which will (at least w/RSA) do a hash-'n'-private-encrypt
+   * operation.  This seems a bit odd, but that's the spec.  Also note that
+   * the data structures do not leave anywhere to independently specify the
+   * algorithm used for the initial hash. So, we look at the
+   * signature-specification algorithm, and try to do something intelligent.
+   * -- Kathy Weinhold, CertCo
+   */
+  /*
+   * Note 2: It seems that the mentioned passage from RFC 2560 (section
+   * 4.2.1) is open for interpretation.  I've done tests against another
+   * responder, and found that it doesn't do the double hashing that the RFC
+   * seems to say one should.  Therefore, all relevant functions take a flag
+   * saying which variant should be used.  -- Richard Levitte, OpenSSL team
+   * and CeloCom
+   */
+struct ocsp_basic_response_st {
+    OCSP_RESPDATA tbsResponseData;
+    X509_ALGOR signatureAlgorithm;
+    ASN1_BIT_STRING *signature;
+    STACK_OF(X509) *certs;
+};
+
+/*-
+ * CrlID ::= SEQUENCE {
+ *     crlUrl               [0]     EXPLICIT IA5String OPTIONAL,
+ *     crlNum               [1]     EXPLICIT INTEGER OPTIONAL,
+ *     crlTime              [2]     EXPLICIT GeneralizedTime OPTIONAL }
+ */
+struct ocsp_crl_id_st {
+    ASN1_IA5STRING *crlUrl;
+    ASN1_INTEGER *crlNum;
+    ASN1_GENERALIZEDTIME *crlTime;
+};
+
+/*-
+ * ServiceLocator ::= SEQUENCE {
+ *      issuer    Name,
+ *      locator   AuthorityInfoAccessSyntax OPTIONAL }
+ */
+struct ocsp_service_locator_st {
+    X509_NAME *issuer;
+    STACK_OF(ACCESS_DESCRIPTION) *locator;
+};
diff --git a/crypto/ocsp/ocsp_lib.c b/crypto/ocsp/ocsp_lib.c
new file mode 100644
index 000000000..8edd70ac8
--- /dev/null
+++ b/crypto/ocsp/ocsp_lib.c
@@ -0,0 +1,222 @@
+/*
+ * Copyright 2000-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+#include 
+#include 
+#include 
+#include "ocsp_lcl.h"
+#include 
+
+/* Convert a certificate and its issuer to an OCSP_CERTID */
+
+OCSP_CERTID *OCSP_cert_to_id(const EVP_MD *dgst, const X509 *subject,
+                             const X509 *issuer)
+{
+    X509_NAME *iname;
+    const ASN1_INTEGER *serial;
+    ASN1_BIT_STRING *ikey;
+    if (!dgst)
+        dgst = EVP_sha1();
+    if (subject) {
+        iname = X509_get_issuer_name(subject);
+        serial = X509_get0_serialNumber(subject);
+    } else {
+        iname = X509_get_subject_name(issuer);
+        serial = NULL;
+    }
+    ikey = X509_get0_pubkey_bitstr(issuer);
+    return OCSP_cert_id_new(dgst, iname, ikey, serial);
+}
+
+OCSP_CERTID *OCSP_cert_id_new(const EVP_MD *dgst,
+                              const X509_NAME *issuerName,
+                              const ASN1_BIT_STRING *issuerKey,
+                              const ASN1_INTEGER *serialNumber)
+{
+    int nid;
+    unsigned int i;
+    X509_ALGOR *alg;
+    OCSP_CERTID *cid = NULL;
+    unsigned char md[EVP_MAX_MD_SIZE];
+
+    if ((cid = OCSP_CERTID_new()) == NULL)
+        goto err;
+
+    alg = &cid->hashAlgorithm;
+    ASN1_OBJECT_free(alg->algorithm);
+    if ((nid = EVP_MD_type(dgst)) == NID_undef) {
+        OCSPerr(OCSP_F_OCSP_CERT_ID_NEW, OCSP_R_UNKNOWN_NID);
+        goto err;
+    }
+    if ((alg->algorithm = OBJ_nid2obj(nid)) == NULL)
+        goto err;
+    if ((alg->parameter = ASN1_TYPE_new()) == NULL)
+        goto err;
+    alg->parameter->type = V_ASN1_NULL;
+
+    if (!X509_NAME_digest(issuerName, dgst, md, &i))
+        goto digerr;
+    if (!(ASN1_OCTET_STRING_set(&cid->issuerNameHash, md, i)))
+        goto err;
+
+    /* Calculate the issuerKey hash, excluding tag and length */
+    if (!EVP_Digest(issuerKey->data, issuerKey->length, md, &i, dgst, NULL))
+        goto err;
+
+    if (!(ASN1_OCTET_STRING_set(&cid->issuerKeyHash, md, i)))
+        goto err;
+
+    if (serialNumber) {
+        if (ASN1_STRING_copy(&cid->serialNumber, serialNumber) == 0)
+            goto err;
+    }
+    return cid;
+ digerr:
+    OCSPerr(OCSP_F_OCSP_CERT_ID_NEW, OCSP_R_DIGEST_ERR);
+ err:
+    OCSP_CERTID_free(cid);
+    return NULL;
+}
+
+int OCSP_id_issuer_cmp(OCSP_CERTID *a, OCSP_CERTID *b)
+{
+    int ret;
+    ret = OBJ_cmp(a->hashAlgorithm.algorithm, b->hashAlgorithm.algorithm);
+    if (ret)
+        return ret;
+    ret = ASN1_OCTET_STRING_cmp(&a->issuerNameHash, &b->issuerNameHash);
+    if (ret)
+        return ret;
+    return ASN1_OCTET_STRING_cmp(&a->issuerKeyHash, &b->issuerKeyHash);
+}
+
+int OCSP_id_cmp(OCSP_CERTID *a, OCSP_CERTID *b)
+{
+    int ret;
+    ret = OCSP_id_issuer_cmp(a, b);
+    if (ret)
+        return ret;
+    return ASN1_INTEGER_cmp(&a->serialNumber, &b->serialNumber);
+}
+
+/*
+ * Parse a URL and split it up into host, port and path components and
+ * whether it is SSL.
+ */
+
+int OCSP_parse_url(const char *url, char **phost, char **pport, char **ppath,
+                   int *pssl)
+{
+    char *p, *buf;
+
+    char *host, *port;
+
+    *phost = NULL;
+    *pport = NULL;
+    *ppath = NULL;
+
+    /* dup the buffer since we are going to mess with it */
+    buf = OPENSSL_strdup(url);
+    if (!buf)
+        goto mem_err;
+
+    /* Check for initial colon */
+    p = strchr(buf, ':');
+
+    if (!p)
+        goto parse_err;
+
+    *(p++) = '\0';
+
+    if (strcmp(buf, "http") == 0) {
+        *pssl = 0;
+        port = "80";
+    } else if (strcmp(buf, "https") == 0) {
+        *pssl = 1;
+        port = "443";
+    } else
+        goto parse_err;
+
+    /* Check for double slash */
+    if ((p[0] != '/') || (p[1] != '/'))
+        goto parse_err;
+
+    p += 2;
+
+    host = p;
+
+    /* Check for trailing part of path */
+
+    p = strchr(p, '/');
+
+    if (!p)
+        *ppath = OPENSSL_strdup("/");
+    else {
+        *ppath = OPENSSL_strdup(p);
+        /* Set start of path to 0 so hostname is valid */
+        *p = '\0';
+    }
+
+    if (!*ppath)
+        goto mem_err;
+
+    p = host;
+    if (host[0] == '[') {
+        /* ipv6 literal */
+        host++;
+        p = strchr(host, ']');
+        if (!p)
+            goto parse_err;
+        *p = '\0';
+        p++;
+    }
+
+    /* Look for optional ':' for port number */
+    if ((p = strchr(p, ':'))) {
+        *p = 0;
+        port = p + 1;
+    }
+
+    *pport = OPENSSL_strdup(port);
+    if (!*pport)
+        goto mem_err;
+
+    *phost = OPENSSL_strdup(host);
+
+    if (!*phost)
+        goto mem_err;
+
+    OPENSSL_free(buf);
+
+    return 1;
+
+ mem_err:
+    OCSPerr(OCSP_F_OCSP_PARSE_URL, ERR_R_MALLOC_FAILURE);
+    goto err;
+
+ parse_err:
+    OCSPerr(OCSP_F_OCSP_PARSE_URL, OCSP_R_ERROR_PARSING_URL);
+
+ err:
+    OPENSSL_free(buf);
+    OPENSSL_free(*ppath);
+    *ppath = NULL;
+    OPENSSL_free(*pport);
+    *pport = NULL;
+    OPENSSL_free(*phost);
+    *phost = NULL;
+    return 0;
+
+}
+
+IMPLEMENT_ASN1_DUP_FUNCTION(OCSP_CERTID)
diff --git a/crypto/ocsp/ocsp_prn.c b/crypto/ocsp/ocsp_prn.c
new file mode 100644
index 000000000..5605812ef
--- /dev/null
+++ b/crypto/ocsp/ocsp_prn.c
@@ -0,0 +1,246 @@
+/*
+ * Copyright 2000-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include 
+#include "ocsp_lcl.h"
+#include "internal/cryptlib.h"
+#include 
+
+static int ocsp_certid_print(BIO *bp, OCSP_CERTID *a, int indent)
+{
+    BIO_printf(bp, "%*sCertificate ID:\n", indent, "");
+    indent += 2;
+    BIO_printf(bp, "%*sHash Algorithm: ", indent, "");
+    i2a_ASN1_OBJECT(bp, a->hashAlgorithm.algorithm);
+    BIO_printf(bp, "\n%*sIssuer Name Hash: ", indent, "");
+    i2a_ASN1_STRING(bp, &a->issuerNameHash, 0);
+    BIO_printf(bp, "\n%*sIssuer Key Hash: ", indent, "");
+    i2a_ASN1_STRING(bp, &a->issuerKeyHash, 0);
+    BIO_printf(bp, "\n%*sSerial Number: ", indent, "");
+    i2a_ASN1_INTEGER(bp, &a->serialNumber);
+    BIO_printf(bp, "\n");
+    return 1;
+}
+
+typedef struct {
+    long t;
+    const char *m;
+} OCSP_TBLSTR;
+
+static const char *do_table2string(long s, const OCSP_TBLSTR *ts, size_t len)
+{
+    size_t i;
+    for (i = 0; i < len; i++, ts++)
+        if (ts->t == s)
+            return ts->m;
+    return "(UNKNOWN)";
+}
+
+#define table2string(s, tbl) do_table2string(s, tbl, OSSL_NELEM(tbl))
+
+const char *OCSP_response_status_str(long s)
+{
+    static const OCSP_TBLSTR rstat_tbl[] = {
+        {OCSP_RESPONSE_STATUS_SUCCESSFUL, "successful"},
+        {OCSP_RESPONSE_STATUS_MALFORMEDREQUEST, "malformedrequest"},
+        {OCSP_RESPONSE_STATUS_INTERNALERROR, "internalerror"},
+        {OCSP_RESPONSE_STATUS_TRYLATER, "trylater"},
+        {OCSP_RESPONSE_STATUS_SIGREQUIRED, "sigrequired"},
+        {OCSP_RESPONSE_STATUS_UNAUTHORIZED, "unauthorized"}
+    };
+    return table2string(s, rstat_tbl);
+}
+
+const char *OCSP_cert_status_str(long s)
+{
+    static const OCSP_TBLSTR cstat_tbl[] = {
+        {V_OCSP_CERTSTATUS_GOOD, "good"},
+        {V_OCSP_CERTSTATUS_REVOKED, "revoked"},
+        {V_OCSP_CERTSTATUS_UNKNOWN, "unknown"}
+    };
+    return table2string(s, cstat_tbl);
+}
+
+const char *OCSP_crl_reason_str(long s)
+{
+    static const OCSP_TBLSTR reason_tbl[] = {
+        {OCSP_REVOKED_STATUS_UNSPECIFIED, "unspecified"},
+        {OCSP_REVOKED_STATUS_KEYCOMPROMISE, "keyCompromise"},
+        {OCSP_REVOKED_STATUS_CACOMPROMISE, "cACompromise"},
+        {OCSP_REVOKED_STATUS_AFFILIATIONCHANGED, "affiliationChanged"},
+        {OCSP_REVOKED_STATUS_SUPERSEDED, "superseded"},
+        {OCSP_REVOKED_STATUS_CESSATIONOFOPERATION, "cessationOfOperation"},
+        {OCSP_REVOKED_STATUS_CERTIFICATEHOLD, "certificateHold"},
+        {OCSP_REVOKED_STATUS_REMOVEFROMCRL, "removeFromCRL"}
+    };
+    return table2string(s, reason_tbl);
+}
+
+int OCSP_REQUEST_print(BIO *bp, OCSP_REQUEST *o, unsigned long flags)
+{
+    int i;
+    long l;
+    OCSP_CERTID *cid = NULL;
+    OCSP_ONEREQ *one = NULL;
+    OCSP_REQINFO *inf = &o->tbsRequest;
+    OCSP_SIGNATURE *sig = o->optionalSignature;
+
+    if (BIO_write(bp, "OCSP Request Data:\n", 19) <= 0)
+        goto err;
+    l = ASN1_INTEGER_get(inf->version);
+    if (BIO_printf(bp, "    Version: %lu (0x%lx)", l + 1, l) <= 0)
+        goto err;
+    if (inf->requestorName != NULL) {
+        if (BIO_write(bp, "\n    Requestor Name: ", 21) <= 0)
+            goto err;
+        GENERAL_NAME_print(bp, inf->requestorName);
+    }
+    if (BIO_write(bp, "\n    Requestor List:\n", 21) <= 0)
+        goto err;
+    for (i = 0; i < sk_OCSP_ONEREQ_num(inf->requestList); i++) {
+        one = sk_OCSP_ONEREQ_value(inf->requestList, i);
+        cid = one->reqCert;
+        ocsp_certid_print(bp, cid, 8);
+        if (!X509V3_extensions_print(bp,
+                                     "Request Single Extensions",
+                                     one->singleRequestExtensions, flags, 8))
+            goto err;
+    }
+    if (!X509V3_extensions_print(bp, "Request Extensions",
+                                 inf->requestExtensions, flags, 4))
+        goto err;
+    if (sig) {
+        X509_signature_print(bp, &sig->signatureAlgorithm, sig->signature);
+        for (i = 0; i < sk_X509_num(sig->certs); i++) {
+            X509_print(bp, sk_X509_value(sig->certs, i));
+            PEM_write_bio_X509(bp, sk_X509_value(sig->certs, i));
+        }
+    }
+    return 1;
+ err:
+    return 0;
+}
+
+int OCSP_RESPONSE_print(BIO *bp, OCSP_RESPONSE *o, unsigned long flags)
+{
+    int i, ret = 0;
+    long l;
+    OCSP_CERTID *cid = NULL;
+    OCSP_BASICRESP *br = NULL;
+    OCSP_RESPID *rid = NULL;
+    OCSP_RESPDATA *rd = NULL;
+    OCSP_CERTSTATUS *cst = NULL;
+    OCSP_REVOKEDINFO *rev = NULL;
+    OCSP_SINGLERESP *single = NULL;
+    OCSP_RESPBYTES *rb = o->responseBytes;
+
+    if (BIO_puts(bp, "OCSP Response Data:\n") <= 0)
+        goto err;
+    l = ASN1_ENUMERATED_get(o->responseStatus);
+    if (BIO_printf(bp, "    OCSP Response Status: %s (0x%lx)\n",
+                   OCSP_response_status_str(l), l) <= 0)
+        goto err;
+    if (rb == NULL)
+        return 1;
+    if (BIO_puts(bp, "    Response Type: ") <= 0)
+        goto err;
+    if (i2a_ASN1_OBJECT(bp, rb->responseType) <= 0)
+        goto err;
+    if (OBJ_obj2nid(rb->responseType) != NID_id_pkix_OCSP_basic) {
+        BIO_puts(bp, " (unknown response type)\n");
+        return 1;
+    }
+
+    if ((br = OCSP_response_get1_basic(o)) == NULL)
+        goto err;
+    rd = &br->tbsResponseData;
+    l = ASN1_INTEGER_get(rd->version);
+    if (BIO_printf(bp, "\n    Version: %lu (0x%lx)\n", l + 1, l) <= 0)
+        goto err;
+    if (BIO_puts(bp, "    Responder Id: ") <= 0)
+        goto err;
+
+    rid = &rd->responderId;
+    switch (rid->type) {
+    case V_OCSP_RESPID_NAME:
+        X509_NAME_print_ex(bp, rid->value.byName, 0, XN_FLAG_ONELINE);
+        break;
+    case V_OCSP_RESPID_KEY:
+        i2a_ASN1_STRING(bp, rid->value.byKey, 0);
+        break;
+    }
+
+    if (BIO_printf(bp, "\n    Produced At: ") <= 0)
+        goto err;
+    if (!ASN1_GENERALIZEDTIME_print(bp, rd->producedAt))
+        goto err;
+    if (BIO_printf(bp, "\n    Responses:\n") <= 0)
+        goto err;
+    for (i = 0; i < sk_OCSP_SINGLERESP_num(rd->responses); i++) {
+        if (!sk_OCSP_SINGLERESP_value(rd->responses, i))
+            continue;
+        single = sk_OCSP_SINGLERESP_value(rd->responses, i);
+        cid = single->certId;
+        if (ocsp_certid_print(bp, cid, 4) <= 0)
+            goto err;
+        cst = single->certStatus;
+        if (BIO_printf(bp, "    Cert Status: %s",
+                       OCSP_cert_status_str(cst->type)) <= 0)
+            goto err;
+        if (cst->type == V_OCSP_CERTSTATUS_REVOKED) {
+            rev = cst->value.revoked;
+            if (BIO_printf(bp, "\n    Revocation Time: ") <= 0)
+                goto err;
+            if (!ASN1_GENERALIZEDTIME_print(bp, rev->revocationTime))
+                goto err;
+            if (rev->revocationReason) {
+                l = ASN1_ENUMERATED_get(rev->revocationReason);
+                if (BIO_printf(bp,
+                               "\n    Revocation Reason: %s (0x%lx)",
+                               OCSP_crl_reason_str(l), l) <= 0)
+                    goto err;
+            }
+        }
+        if (BIO_printf(bp, "\n    This Update: ") <= 0)
+            goto err;
+        if (!ASN1_GENERALIZEDTIME_print(bp, single->thisUpdate))
+            goto err;
+        if (single->nextUpdate) {
+            if (BIO_printf(bp, "\n    Next Update: ") <= 0)
+                goto err;
+            if (!ASN1_GENERALIZEDTIME_print(bp, single->nextUpdate))
+                goto err;
+        }
+        if (BIO_write(bp, "\n", 1) <= 0)
+            goto err;
+        if (!X509V3_extensions_print(bp,
+                                     "Response Single Extensions",
+                                     single->singleExtensions, flags, 8))
+            goto err;
+        if (BIO_write(bp, "\n", 1) <= 0)
+            goto err;
+    }
+    if (!X509V3_extensions_print(bp, "Response Extensions",
+                                 rd->responseExtensions, flags, 4))
+        goto err;
+    if (X509_signature_print(bp, &br->signatureAlgorithm, br->signature) <= 0)
+        goto err;
+
+    for (i = 0; i < sk_X509_num(br->certs); i++) {
+        X509_print(bp, sk_X509_value(br->certs, i));
+        PEM_write_bio_X509(bp, sk_X509_value(br->certs, i));
+    }
+
+    ret = 1;
+ err:
+    OCSP_BASICRESP_free(br);
+    return ret;
+}
diff --git a/crypto/ocsp/ocsp_srv.c b/crypto/ocsp/ocsp_srv.c
new file mode 100644
index 000000000..46a4bf785
--- /dev/null
+++ b/crypto/ocsp/ocsp_srv.c
@@ -0,0 +1,277 @@
+/*
+ * Copyright 2001-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+#include 
+#include 
+#include 
+#include "ocsp_lcl.h"
+
+/*
+ * Utility functions related to sending OCSP responses and extracting
+ * relevant information from the request.
+ */
+
+int OCSP_request_onereq_count(OCSP_REQUEST *req)
+{
+    return sk_OCSP_ONEREQ_num(req->tbsRequest.requestList);
+}
+
+OCSP_ONEREQ *OCSP_request_onereq_get0(OCSP_REQUEST *req, int i)
+{
+    return sk_OCSP_ONEREQ_value(req->tbsRequest.requestList, i);
+}
+
+OCSP_CERTID *OCSP_onereq_get0_id(OCSP_ONEREQ *one)
+{
+    return one->reqCert;
+}
+
+int OCSP_id_get0_info(ASN1_OCTET_STRING **piNameHash, ASN1_OBJECT **pmd,
+                      ASN1_OCTET_STRING **pikeyHash,
+                      ASN1_INTEGER **pserial, OCSP_CERTID *cid)
+{
+    if (!cid)
+        return 0;
+    if (pmd)
+        *pmd = cid->hashAlgorithm.algorithm;
+    if (piNameHash)
+        *piNameHash = &cid->issuerNameHash;
+    if (pikeyHash)
+        *pikeyHash = &cid->issuerKeyHash;
+    if (pserial)
+        *pserial = &cid->serialNumber;
+    return 1;
+}
+
+int OCSP_request_is_signed(OCSP_REQUEST *req)
+{
+    if (req->optionalSignature)
+        return 1;
+    return 0;
+}
+
+/* Create an OCSP response and encode an optional basic response */
+OCSP_RESPONSE *OCSP_response_create(int status, OCSP_BASICRESP *bs)
+{
+    OCSP_RESPONSE *rsp = NULL;
+
+    if ((rsp = OCSP_RESPONSE_new()) == NULL)
+        goto err;
+    if (!(ASN1_ENUMERATED_set(rsp->responseStatus, status)))
+        goto err;
+    if (!bs)
+        return rsp;
+    if ((rsp->responseBytes = OCSP_RESPBYTES_new()) == NULL)
+        goto err;
+    rsp->responseBytes->responseType = OBJ_nid2obj(NID_id_pkix_OCSP_basic);
+    if (!ASN1_item_pack
+        (bs, ASN1_ITEM_rptr(OCSP_BASICRESP), &rsp->responseBytes->response))
+         goto err;
+    return rsp;
+ err:
+    OCSP_RESPONSE_free(rsp);
+    return NULL;
+}
+
+OCSP_SINGLERESP *OCSP_basic_add1_status(OCSP_BASICRESP *rsp,
+                                        OCSP_CERTID *cid,
+                                        int status, int reason,
+                                        ASN1_TIME *revtime,
+                                        ASN1_TIME *thisupd,
+                                        ASN1_TIME *nextupd)
+{
+    OCSP_SINGLERESP *single = NULL;
+    OCSP_CERTSTATUS *cs;
+    OCSP_REVOKEDINFO *ri;
+
+    if (rsp->tbsResponseData.responses == NULL
+        && (rsp->tbsResponseData.responses
+                = sk_OCSP_SINGLERESP_new_null()) == NULL)
+        goto err;
+
+    if ((single = OCSP_SINGLERESP_new()) == NULL)
+        goto err;
+
+    if (!ASN1_TIME_to_generalizedtime(thisupd, &single->thisUpdate))
+        goto err;
+    if (nextupd &&
+        !ASN1_TIME_to_generalizedtime(nextupd, &single->nextUpdate))
+        goto err;
+
+    OCSP_CERTID_free(single->certId);
+
+    if ((single->certId = OCSP_CERTID_dup(cid)) == NULL)
+        goto err;
+
+    cs = single->certStatus;
+    switch (cs->type = status) {
+    case V_OCSP_CERTSTATUS_REVOKED:
+        if (!revtime) {
+            OCSPerr(OCSP_F_OCSP_BASIC_ADD1_STATUS, OCSP_R_NO_REVOKED_TIME);
+            goto err;
+        }
+        if ((cs->value.revoked = ri = OCSP_REVOKEDINFO_new()) == NULL)
+            goto err;
+        if (!ASN1_TIME_to_generalizedtime(revtime, &ri->revocationTime))
+            goto err;
+        if (reason != OCSP_REVOKED_STATUS_NOSTATUS) {
+            if ((ri->revocationReason = ASN1_ENUMERATED_new()) == NULL)
+                goto err;
+            if (!(ASN1_ENUMERATED_set(ri->revocationReason, reason)))
+                goto err;
+        }
+        break;
+
+    case V_OCSP_CERTSTATUS_GOOD:
+        if ((cs->value.good = ASN1_NULL_new()) == NULL)
+            goto err;
+        break;
+
+    case V_OCSP_CERTSTATUS_UNKNOWN:
+        if ((cs->value.unknown = ASN1_NULL_new()) == NULL)
+            goto err;
+        break;
+
+    default:
+        goto err;
+
+    }
+    if (!(sk_OCSP_SINGLERESP_push(rsp->tbsResponseData.responses, single)))
+        goto err;
+    return single;
+ err:
+    OCSP_SINGLERESP_free(single);
+    return NULL;
+}
+
+/* Add a certificate to an OCSP request */
+
+int OCSP_basic_add1_cert(OCSP_BASICRESP *resp, X509 *cert)
+{
+    if (resp->certs == NULL
+        && (resp->certs = sk_X509_new_null()) == NULL)
+        return 0;
+
+    if (!sk_X509_push(resp->certs, cert))
+        return 0;
+    X509_up_ref(cert);
+    return 1;
+}
+
+int OCSP_basic_sign(OCSP_BASICRESP *brsp,
+                    X509 *signer, EVP_PKEY *key, const EVP_MD *dgst,
+                    STACK_OF(X509) *certs, unsigned long flags)
+{
+    int i;
+    OCSP_RESPID *rid;
+
+    if (!X509_check_private_key(signer, key)) {
+        OCSPerr(OCSP_F_OCSP_BASIC_SIGN,
+                OCSP_R_PRIVATE_KEY_DOES_NOT_MATCH_CERTIFICATE);
+        goto err;
+    }
+
+    if (!(flags & OCSP_NOCERTS)) {
+        if (!OCSP_basic_add1_cert(brsp, signer))
+            goto err;
+        for (i = 0; i < sk_X509_num(certs); i++) {
+            X509 *tmpcert = sk_X509_value(certs, i);
+            if (!OCSP_basic_add1_cert(brsp, tmpcert))
+                goto err;
+        }
+    }
+
+    rid = &brsp->tbsResponseData.responderId;
+    if (flags & OCSP_RESPID_KEY) {
+        if (!OCSP_RESPID_set_by_key(rid, signer))
+            goto err;
+    } else if (!OCSP_RESPID_set_by_name(rid, signer)) {
+        goto err;
+    }
+
+    if (!(flags & OCSP_NOTIME) &&
+        !X509_gmtime_adj(brsp->tbsResponseData.producedAt, 0))
+        goto err;
+
+    /*
+     * Right now, I think that not doing double hashing is the right thing.
+     * -- Richard Levitte
+     */
+
+    if (!OCSP_BASICRESP_sign(brsp, key, dgst, 0))
+        goto err;
+
+    return 1;
+ err:
+    return 0;
+}
+
+int OCSP_RESPID_set_by_name(OCSP_RESPID *respid, X509 *cert)
+{
+    if (!X509_NAME_set(&respid->value.byName, X509_get_subject_name(cert)))
+        return 0;
+
+    respid->type = V_OCSP_RESPID_NAME;
+
+    return 1;
+}
+
+int OCSP_RESPID_set_by_key(OCSP_RESPID *respid, X509 *cert)
+{
+    ASN1_OCTET_STRING *byKey = NULL;
+    unsigned char md[SHA_DIGEST_LENGTH];
+
+    /* RFC2560 requires SHA1 */
+    if (!X509_pubkey_digest(cert, EVP_sha1(), md, NULL))
+        return 0;
+
+    byKey = ASN1_OCTET_STRING_new();
+    if (byKey == NULL)
+        return 0;
+
+    if (!(ASN1_OCTET_STRING_set(byKey, md, SHA_DIGEST_LENGTH))) {
+        ASN1_OCTET_STRING_free(byKey);
+        return 0;
+    }
+
+    respid->type = V_OCSP_RESPID_KEY;
+    respid->value.byKey = byKey;
+
+    return 1;
+}
+
+int OCSP_RESPID_match(OCSP_RESPID *respid, X509 *cert)
+{
+    if (respid->type == V_OCSP_RESPID_KEY) {
+        unsigned char md[SHA_DIGEST_LENGTH];
+
+        if (respid->value.byKey == NULL)
+            return 0;
+
+        /* RFC2560 requires SHA1 */
+        if (!X509_pubkey_digest(cert, EVP_sha1(), md, NULL))
+            return 0;
+
+        return (ASN1_STRING_length(respid->value.byKey) == SHA_DIGEST_LENGTH)
+            && (memcmp(ASN1_STRING_get0_data(respid->value.byKey), md,
+                       SHA_DIGEST_LENGTH) == 0);
+    } else if(respid->type == V_OCSP_RESPID_NAME) {
+        if (respid->value.byName == NULL)
+            return 0;
+
+        return X509_NAME_cmp(respid->value.byName,
+                             X509_get_subject_name(cert)) == 0;
+    }
+
+    return 0;
+}
diff --git a/crypto/ocsp/ocsp_vfy.c b/crypto/ocsp/ocsp_vfy.c
new file mode 100644
index 000000000..e2cfa6dda
--- /dev/null
+++ b/crypto/ocsp/ocsp_vfy.c
@@ -0,0 +1,424 @@
+/*
+ * Copyright 2001-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "ocsp_lcl.h"
+#include 
+#include 
+
+static int ocsp_find_signer(X509 **psigner, OCSP_BASICRESP *bs,
+                            STACK_OF(X509) *certs, unsigned long flags);
+static X509 *ocsp_find_signer_sk(STACK_OF(X509) *certs, OCSP_RESPID *id);
+static int ocsp_check_issuer(OCSP_BASICRESP *bs, STACK_OF(X509) *chain);
+static int ocsp_check_ids(STACK_OF(OCSP_SINGLERESP) *sresp,
+                          OCSP_CERTID **ret);
+static int ocsp_match_issuerid(X509 *cert, OCSP_CERTID *cid,
+                               STACK_OF(OCSP_SINGLERESP) *sresp);
+static int ocsp_check_delegated(X509 *x);
+static int ocsp_req_find_signer(X509 **psigner, OCSP_REQUEST *req,
+                                X509_NAME *nm, STACK_OF(X509) *certs,
+                                unsigned long flags);
+
+/* Verify a basic response message */
+
+int OCSP_basic_verify(OCSP_BASICRESP *bs, STACK_OF(X509) *certs,
+                      X509_STORE *st, unsigned long flags)
+{
+    X509 *signer, *x;
+    STACK_OF(X509) *chain = NULL;
+    STACK_OF(X509) *untrusted = NULL;
+    X509_STORE_CTX *ctx = NULL;
+    int i, ret = ocsp_find_signer(&signer, bs, certs, flags);
+
+    if (!ret) {
+        OCSPerr(OCSP_F_OCSP_BASIC_VERIFY,
+                OCSP_R_SIGNER_CERTIFICATE_NOT_FOUND);
+        goto end;
+    }
+    ctx = X509_STORE_CTX_new();
+    if (ctx == NULL) {
+        OCSPerr(OCSP_F_OCSP_BASIC_VERIFY, ERR_R_MALLOC_FAILURE);
+        goto f_err;
+    }
+    if ((ret == 2) && (flags & OCSP_TRUSTOTHER))
+        flags |= OCSP_NOVERIFY;
+    if (!(flags & OCSP_NOSIGS)) {
+        EVP_PKEY *skey;
+        skey = X509_get0_pubkey(signer);
+        if (skey == NULL) {
+            OCSPerr(OCSP_F_OCSP_BASIC_VERIFY, OCSP_R_NO_SIGNER_KEY);
+            goto err;
+        }
+        ret = OCSP_BASICRESP_verify(bs, skey, 0);
+        if (ret <= 0) {
+            OCSPerr(OCSP_F_OCSP_BASIC_VERIFY, OCSP_R_SIGNATURE_FAILURE);
+            goto end;
+        }
+    }
+    if (!(flags & OCSP_NOVERIFY)) {
+        int init_res;
+        if (flags & OCSP_NOCHAIN) {
+            untrusted = NULL;
+        } else if (bs->certs && certs) {
+            untrusted = sk_X509_dup(bs->certs);
+            for (i = 0; i < sk_X509_num(certs); i++) {
+                if (!sk_X509_push(untrusted, sk_X509_value(certs, i))) {
+                    OCSPerr(OCSP_F_OCSP_BASIC_VERIFY, ERR_R_MALLOC_FAILURE);
+                    goto f_err;
+                }
+            }
+        } else {
+            untrusted = bs->certs;
+        }
+        init_res = X509_STORE_CTX_init(ctx, st, signer, untrusted);
+        if (!init_res) {
+            OCSPerr(OCSP_F_OCSP_BASIC_VERIFY, ERR_R_X509_LIB);
+            goto f_err;
+        }
+
+        X509_STORE_CTX_set_purpose(ctx, X509_PURPOSE_OCSP_HELPER);
+        ret = X509_verify_cert(ctx);
+        chain = X509_STORE_CTX_get1_chain(ctx);
+        if (ret <= 0) {
+            i = X509_STORE_CTX_get_error(ctx);
+            OCSPerr(OCSP_F_OCSP_BASIC_VERIFY,
+                    OCSP_R_CERTIFICATE_VERIFY_ERROR);
+            ERR_add_error_data(2, "Verify error:",
+                               X509_verify_cert_error_string(i));
+            goto end;
+        }
+        if (flags & OCSP_NOCHECKS) {
+            ret = 1;
+            goto end;
+        }
+        /*
+         * At this point we have a valid certificate chain need to verify it
+         * against the OCSP issuer criteria.
+         */
+        ret = ocsp_check_issuer(bs, chain);
+
+        /* If fatal error or valid match then finish */
+        if (ret != 0)
+            goto end;
+
+        /*
+         * Easy case: explicitly trusted. Get root CA and check for explicit
+         * trust
+         */
+        if (flags & OCSP_NOEXPLICIT)
+            goto end;
+
+        x = sk_X509_value(chain, sk_X509_num(chain) - 1);
+        if (X509_check_trust(x, NID_OCSP_sign, 0) != X509_TRUST_TRUSTED) {
+            OCSPerr(OCSP_F_OCSP_BASIC_VERIFY, OCSP_R_ROOT_CA_NOT_TRUSTED);
+            goto err;
+        }
+        ret = 1;
+    }
+ end:
+    X509_STORE_CTX_free(ctx);
+    sk_X509_pop_free(chain, X509_free);
+    if (bs->certs && certs)
+        sk_X509_free(untrusted);
+    return ret;
+
+ err:
+    ret = 0;
+    goto end;
+ f_err:
+    ret = -1;
+    goto end;
+}
+
+static int ocsp_find_signer(X509 **psigner, OCSP_BASICRESP *bs,
+                            STACK_OF(X509) *certs, unsigned long flags)
+{
+    X509 *signer;
+    OCSP_RESPID *rid = &bs->tbsResponseData.responderId;
+    if ((signer = ocsp_find_signer_sk(certs, rid))) {
+        *psigner = signer;
+        return 2;
+    }
+    if (!(flags & OCSP_NOINTERN) &&
+        (signer = ocsp_find_signer_sk(bs->certs, rid))) {
+        *psigner = signer;
+        return 1;
+    }
+    /* Maybe lookup from store if by subject name */
+
+    *psigner = NULL;
+    return 0;
+}
+
+static X509 *ocsp_find_signer_sk(STACK_OF(X509) *certs, OCSP_RESPID *id)
+{
+    int i;
+    unsigned char tmphash[SHA_DIGEST_LENGTH], *keyhash;
+    X509 *x;
+
+    /* Easy if lookup by name */
+    if (id->type == V_OCSP_RESPID_NAME)
+        return X509_find_by_subject(certs, id->value.byName);
+
+    /* Lookup by key hash */
+
+    /* If key hash isn't SHA1 length then forget it */
+    if (id->value.byKey->length != SHA_DIGEST_LENGTH)
+        return NULL;
+    keyhash = id->value.byKey->data;
+    /* Calculate hash of each key and compare */
+    for (i = 0; i < sk_X509_num(certs); i++) {
+        x = sk_X509_value(certs, i);
+        X509_pubkey_digest(x, EVP_sha1(), tmphash, NULL);
+        if (!memcmp(keyhash, tmphash, SHA_DIGEST_LENGTH))
+            return x;
+    }
+    return NULL;
+}
+
+static int ocsp_check_issuer(OCSP_BASICRESP *bs, STACK_OF(X509) *chain)
+{
+    STACK_OF(OCSP_SINGLERESP) *sresp;
+    X509 *signer, *sca;
+    OCSP_CERTID *caid = NULL;
+    int i;
+    sresp = bs->tbsResponseData.responses;
+
+    if (sk_X509_num(chain) <= 0) {
+        OCSPerr(OCSP_F_OCSP_CHECK_ISSUER, OCSP_R_NO_CERTIFICATES_IN_CHAIN);
+        return -1;
+    }
+
+    /* See if the issuer IDs match. */
+    i = ocsp_check_ids(sresp, &caid);
+
+    /* If ID mismatch or other error then return */
+    if (i <= 0)
+        return i;
+
+    signer = sk_X509_value(chain, 0);
+    /* Check to see if OCSP responder CA matches request CA */
+    if (sk_X509_num(chain) > 1) {
+        sca = sk_X509_value(chain, 1);
+        i = ocsp_match_issuerid(sca, caid, sresp);
+        if (i < 0)
+            return i;
+        if (i) {
+            /* We have a match, if extensions OK then success */
+            if (ocsp_check_delegated(signer))
+                return 1;
+            return 0;
+        }
+    }
+
+    /* Otherwise check if OCSP request signed directly by request CA */
+    return ocsp_match_issuerid(signer, caid, sresp);
+}
+
+/*
+ * Check the issuer certificate IDs for equality. If there is a mismatch with
+ * the same algorithm then there's no point trying to match any certificates
+ * against the issuer. If the issuer IDs all match then we just need to check
+ * equality against one of them.
+ */
+
+static int ocsp_check_ids(STACK_OF(OCSP_SINGLERESP) *sresp, OCSP_CERTID **ret)
+{
+    OCSP_CERTID *tmpid, *cid;
+    int i, idcount;
+
+    idcount = sk_OCSP_SINGLERESP_num(sresp);
+    if (idcount <= 0) {
+        OCSPerr(OCSP_F_OCSP_CHECK_IDS,
+                OCSP_R_RESPONSE_CONTAINS_NO_REVOCATION_DATA);
+        return -1;
+    }
+
+    cid = sk_OCSP_SINGLERESP_value(sresp, 0)->certId;
+
+    *ret = NULL;
+
+    for (i = 1; i < idcount; i++) {
+        tmpid = sk_OCSP_SINGLERESP_value(sresp, i)->certId;
+        /* Check to see if IDs match */
+        if (OCSP_id_issuer_cmp(cid, tmpid)) {
+            /* If algorithm mismatch let caller deal with it */
+            if (OBJ_cmp(tmpid->hashAlgorithm.algorithm,
+                        cid->hashAlgorithm.algorithm))
+                return 2;
+            /* Else mismatch */
+            return 0;
+        }
+    }
+
+    /* All IDs match: only need to check one ID */
+    *ret = cid;
+    return 1;
+}
+
+static int ocsp_match_issuerid(X509 *cert, OCSP_CERTID *cid,
+                               STACK_OF(OCSP_SINGLERESP) *sresp)
+{
+    /* If only one ID to match then do it */
+    if (cid) {
+        const EVP_MD *dgst;
+        X509_NAME *iname;
+        int mdlen;
+        unsigned char md[EVP_MAX_MD_SIZE];
+        if ((dgst = EVP_get_digestbyobj(cid->hashAlgorithm.algorithm))
+                == NULL) {
+            OCSPerr(OCSP_F_OCSP_MATCH_ISSUERID,
+                    OCSP_R_UNKNOWN_MESSAGE_DIGEST);
+            return -1;
+        }
+
+        mdlen = EVP_MD_size(dgst);
+        if (mdlen < 0)
+            return -1;
+        if ((cid->issuerNameHash.length != mdlen) ||
+            (cid->issuerKeyHash.length != mdlen))
+            return 0;
+        iname = X509_get_subject_name(cert);
+        if (!X509_NAME_digest(iname, dgst, md, NULL))
+            return -1;
+        if (memcmp(md, cid->issuerNameHash.data, mdlen))
+            return 0;
+        X509_pubkey_digest(cert, dgst, md, NULL);
+        if (memcmp(md, cid->issuerKeyHash.data, mdlen))
+            return 0;
+
+        return 1;
+
+    } else {
+        /* We have to match the whole lot */
+        int i, ret;
+        OCSP_CERTID *tmpid;
+        for (i = 0; i < sk_OCSP_SINGLERESP_num(sresp); i++) {
+            tmpid = sk_OCSP_SINGLERESP_value(sresp, i)->certId;
+            ret = ocsp_match_issuerid(cert, tmpid, NULL);
+            if (ret <= 0)
+                return ret;
+        }
+        return 1;
+    }
+
+}
+
+static int ocsp_check_delegated(X509 *x)
+{
+    if ((X509_get_extension_flags(x) & EXFLAG_XKUSAGE)
+        && (X509_get_extended_key_usage(x) & XKU_OCSP_SIGN))
+        return 1;
+    OCSPerr(OCSP_F_OCSP_CHECK_DELEGATED, OCSP_R_MISSING_OCSPSIGNING_USAGE);
+    return 0;
+}
+
+/*
+ * Verify an OCSP request. This is fortunately much easier than OCSP response
+ * verify. Just find the signers certificate and verify it against a given
+ * trust value.
+ */
+
+int OCSP_request_verify(OCSP_REQUEST *req, STACK_OF(X509) *certs,
+                        X509_STORE *store, unsigned long flags)
+{
+    X509 *signer;
+    X509_NAME *nm;
+    GENERAL_NAME *gen;
+    int ret = 0;
+    X509_STORE_CTX *ctx = X509_STORE_CTX_new();
+
+    if (ctx == NULL) {
+        OCSPerr(OCSP_F_OCSP_REQUEST_VERIFY, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+
+    if (!req->optionalSignature) {
+        OCSPerr(OCSP_F_OCSP_REQUEST_VERIFY, OCSP_R_REQUEST_NOT_SIGNED);
+        goto err;
+    }
+    gen = req->tbsRequest.requestorName;
+    if (!gen || gen->type != GEN_DIRNAME) {
+        OCSPerr(OCSP_F_OCSP_REQUEST_VERIFY,
+                OCSP_R_UNSUPPORTED_REQUESTORNAME_TYPE);
+        goto err;
+    }
+    nm = gen->d.directoryName;
+    ret = ocsp_req_find_signer(&signer, req, nm, certs, flags);
+    if (ret <= 0) {
+        OCSPerr(OCSP_F_OCSP_REQUEST_VERIFY,
+                OCSP_R_SIGNER_CERTIFICATE_NOT_FOUND);
+        goto err;
+    }
+    if ((ret == 2) && (flags & OCSP_TRUSTOTHER))
+        flags |= OCSP_NOVERIFY;
+    if (!(flags & OCSP_NOSIGS)) {
+        EVP_PKEY *skey;
+        skey = X509_get0_pubkey(signer);
+        ret = OCSP_REQUEST_verify(req, skey);
+        if (ret <= 0) {
+            OCSPerr(OCSP_F_OCSP_REQUEST_VERIFY, OCSP_R_SIGNATURE_FAILURE);
+            goto err;
+        }
+    }
+    if (!(flags & OCSP_NOVERIFY)) {
+        int init_res;
+        if (flags & OCSP_NOCHAIN)
+            init_res = X509_STORE_CTX_init(ctx, store, signer, NULL);
+        else
+            init_res = X509_STORE_CTX_init(ctx, store, signer,
+                                           req->optionalSignature->certs);
+        if (!init_res) {
+            OCSPerr(OCSP_F_OCSP_REQUEST_VERIFY, ERR_R_X509_LIB);
+            goto err;
+        }
+
+        X509_STORE_CTX_set_purpose(ctx, X509_PURPOSE_OCSP_HELPER);
+        X509_STORE_CTX_set_trust(ctx, X509_TRUST_OCSP_REQUEST);
+        ret = X509_verify_cert(ctx);
+        if (ret <= 0) {
+            ret = X509_STORE_CTX_get_error(ctx);
+            OCSPerr(OCSP_F_OCSP_REQUEST_VERIFY,
+                    OCSP_R_CERTIFICATE_VERIFY_ERROR);
+            ERR_add_error_data(2, "Verify error:",
+                               X509_verify_cert_error_string(ret));
+            goto err;
+        }
+    }
+    ret = 1;
+    goto end;
+
+err:
+    ret = 0;
+end:
+    X509_STORE_CTX_free(ctx);
+    return ret;
+
+}
+
+static int ocsp_req_find_signer(X509 **psigner, OCSP_REQUEST *req,
+                                X509_NAME *nm, STACK_OF(X509) *certs,
+                                unsigned long flags)
+{
+    X509 *signer;
+    if (!(flags & OCSP_NOINTERN)) {
+        signer = X509_find_by_subject(req->optionalSignature->certs, nm);
+        if (signer) {
+            *psigner = signer;
+            return 1;
+        }
+    }
+
+    signer = X509_find_by_subject(certs, nm);
+    if (signer) {
+        *psigner = signer;
+        return 2;
+    }
+    return 0;
+}
diff --git a/crypto/ocsp/v3_ocsp.c b/crypto/ocsp/v3_ocsp.c
new file mode 100644
index 000000000..2d425a895
--- /dev/null
+++ b/crypto/ocsp/v3_ocsp.c
@@ -0,0 +1,264 @@
+/*
+ * Copyright 2000-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+# include 
+# include "internal/cryptlib.h"
+# include 
+# include 
+# include 
+# include "ocsp_lcl.h"
+# include 
+# include "../x509v3/ext_dat.h"
+
+/*
+ * OCSP extensions and a couple of CRL entry extensions
+ */
+
+static int i2r_ocsp_crlid(const X509V3_EXT_METHOD *method, void *nonce,
+                          BIO *out, int indent);
+static int i2r_ocsp_acutoff(const X509V3_EXT_METHOD *method, void *nonce,
+                            BIO *out, int indent);
+static int i2r_object(const X509V3_EXT_METHOD *method, void *obj, BIO *out,
+                      int indent);
+
+static void *ocsp_nonce_new(void);
+static int i2d_ocsp_nonce(void *a, unsigned char **pp);
+static void *d2i_ocsp_nonce(void *a, const unsigned char **pp, long length);
+static void ocsp_nonce_free(void *a);
+static int i2r_ocsp_nonce(const X509V3_EXT_METHOD *method, void *nonce,
+                          BIO *out, int indent);
+
+static int i2r_ocsp_nocheck(const X509V3_EXT_METHOD *method,
+                            void *nocheck, BIO *out, int indent);
+static void *s2i_ocsp_nocheck(const X509V3_EXT_METHOD *method,
+                              X509V3_CTX *ctx, const char *str);
+static int i2r_ocsp_serviceloc(const X509V3_EXT_METHOD *method, void *in,
+                               BIO *bp, int ind);
+
+const X509V3_EXT_METHOD v3_ocsp_crlid = {
+    NID_id_pkix_OCSP_CrlID, 0, ASN1_ITEM_ref(OCSP_CRLID),
+    0, 0, 0, 0,
+    0, 0,
+    0, 0,
+    i2r_ocsp_crlid, 0,
+    NULL
+};
+
+const X509V3_EXT_METHOD v3_ocsp_acutoff = {
+    NID_id_pkix_OCSP_archiveCutoff, 0, ASN1_ITEM_ref(ASN1_GENERALIZEDTIME),
+    0, 0, 0, 0,
+    0, 0,
+    0, 0,
+    i2r_ocsp_acutoff, 0,
+    NULL
+};
+
+const X509V3_EXT_METHOD v3_crl_invdate = {
+    NID_invalidity_date, 0, ASN1_ITEM_ref(ASN1_GENERALIZEDTIME),
+    0, 0, 0, 0,
+    0, 0,
+    0, 0,
+    i2r_ocsp_acutoff, 0,
+    NULL
+};
+
+const X509V3_EXT_METHOD v3_crl_hold = {
+    NID_hold_instruction_code, 0, ASN1_ITEM_ref(ASN1_OBJECT),
+    0, 0, 0, 0,
+    0, 0,
+    0, 0,
+    i2r_object, 0,
+    NULL
+};
+
+const X509V3_EXT_METHOD v3_ocsp_nonce = {
+    NID_id_pkix_OCSP_Nonce, 0, NULL,
+    ocsp_nonce_new,
+    ocsp_nonce_free,
+    d2i_ocsp_nonce,
+    i2d_ocsp_nonce,
+    0, 0,
+    0, 0,
+    i2r_ocsp_nonce, 0,
+    NULL
+};
+
+const X509V3_EXT_METHOD v3_ocsp_nocheck = {
+    NID_id_pkix_OCSP_noCheck, 0, ASN1_ITEM_ref(ASN1_NULL),
+    0, 0, 0, 0,
+    0, s2i_ocsp_nocheck,
+    0, 0,
+    i2r_ocsp_nocheck, 0,
+    NULL
+};
+
+const X509V3_EXT_METHOD v3_ocsp_serviceloc = {
+    NID_id_pkix_OCSP_serviceLocator, 0, ASN1_ITEM_ref(OCSP_SERVICELOC),
+    0, 0, 0, 0,
+    0, 0,
+    0, 0,
+    i2r_ocsp_serviceloc, 0,
+    NULL
+};
+
+static int i2r_ocsp_crlid(const X509V3_EXT_METHOD *method, void *in, BIO *bp,
+                          int ind)
+{
+    OCSP_CRLID *a = in;
+    if (a->crlUrl) {
+        if (BIO_printf(bp, "%*scrlUrl: ", ind, "") <= 0)
+            goto err;
+        if (!ASN1_STRING_print(bp, (ASN1_STRING *)a->crlUrl))
+            goto err;
+        if (BIO_write(bp, "\n", 1) <= 0)
+            goto err;
+    }
+    if (a->crlNum) {
+        if (BIO_printf(bp, "%*scrlNum: ", ind, "") <= 0)
+            goto err;
+        if (i2a_ASN1_INTEGER(bp, a->crlNum) <= 0)
+            goto err;
+        if (BIO_write(bp, "\n", 1) <= 0)
+            goto err;
+    }
+    if (a->crlTime) {
+        if (BIO_printf(bp, "%*scrlTime: ", ind, "") <= 0)
+            goto err;
+        if (!ASN1_GENERALIZEDTIME_print(bp, a->crlTime))
+            goto err;
+        if (BIO_write(bp, "\n", 1) <= 0)
+            goto err;
+    }
+    return 1;
+ err:
+    return 0;
+}
+
+static int i2r_ocsp_acutoff(const X509V3_EXT_METHOD *method, void *cutoff,
+                            BIO *bp, int ind)
+{
+    if (BIO_printf(bp, "%*s", ind, "") <= 0)
+        return 0;
+    if (!ASN1_GENERALIZEDTIME_print(bp, cutoff))
+        return 0;
+    return 1;
+}
+
+static int i2r_object(const X509V3_EXT_METHOD *method, void *oid, BIO *bp,
+                      int ind)
+{
+    if (BIO_printf(bp, "%*s", ind, "") <= 0)
+        return 0;
+    if (i2a_ASN1_OBJECT(bp, oid) <= 0)
+        return 0;
+    return 1;
+}
+
+/*
+ * OCSP nonce. This is needs special treatment because it doesn't have an
+ * ASN1 encoding at all: it just contains arbitrary data.
+ */
+
+static void *ocsp_nonce_new(void)
+{
+    return ASN1_OCTET_STRING_new();
+}
+
+static int i2d_ocsp_nonce(void *a, unsigned char **pp)
+{
+    ASN1_OCTET_STRING *os = a;
+    if (pp) {
+        memcpy(*pp, os->data, os->length);
+        *pp += os->length;
+    }
+    return os->length;
+}
+
+static void *d2i_ocsp_nonce(void *a, const unsigned char **pp, long length)
+{
+    ASN1_OCTET_STRING *os, **pos;
+    pos = a;
+    if (pos == NULL || *pos == NULL) {
+        os = ASN1_OCTET_STRING_new();
+        if (os == NULL)
+            goto err;
+    } else {
+        os = *pos;
+    }
+    if (!ASN1_OCTET_STRING_set(os, *pp, length))
+        goto err;
+
+    *pp += length;
+
+    if (pos)
+        *pos = os;
+    return os;
+
+ err:
+    if ((pos == NULL) || (*pos != os))
+        ASN1_OCTET_STRING_free(os);
+    OCSPerr(OCSP_F_D2I_OCSP_NONCE, ERR_R_MALLOC_FAILURE);
+    return NULL;
+}
+
+static void ocsp_nonce_free(void *a)
+{
+    ASN1_OCTET_STRING_free(a);
+}
+
+static int i2r_ocsp_nonce(const X509V3_EXT_METHOD *method, void *nonce,
+                          BIO *out, int indent)
+{
+    if (BIO_printf(out, "%*s", indent, "") <= 0)
+        return 0;
+    if (i2a_ASN1_STRING(out, nonce, V_ASN1_OCTET_STRING) <= 0)
+        return 0;
+    return 1;
+}
+
+/* Nocheck is just a single NULL. Don't print anything and always set it */
+
+static int i2r_ocsp_nocheck(const X509V3_EXT_METHOD *method, void *nocheck,
+                            BIO *out, int indent)
+{
+    return 1;
+}
+
+static void *s2i_ocsp_nocheck(const X509V3_EXT_METHOD *method,
+                              X509V3_CTX *ctx, const char *str)
+{
+    return ASN1_NULL_new();
+}
+
+static int i2r_ocsp_serviceloc(const X509V3_EXT_METHOD *method, void *in,
+                               BIO *bp, int ind)
+{
+    int i;
+    OCSP_SERVICELOC *a = in;
+    ACCESS_DESCRIPTION *ad;
+
+    if (BIO_printf(bp, "%*sIssuer: ", ind, "") <= 0)
+        goto err;
+    if (X509_NAME_print_ex(bp, a->issuer, 0, XN_FLAG_ONELINE) <= 0)
+        goto err;
+    for (i = 0; i < sk_ACCESS_DESCRIPTION_num(a->locator); i++) {
+        ad = sk_ACCESS_DESCRIPTION_value(a->locator, i);
+        if (BIO_printf(bp, "\n%*s", (2 * ind), "") <= 0)
+            goto err;
+        if (i2a_ASN1_OBJECT(bp, ad->method) <= 0)
+            goto err;
+        if (BIO_puts(bp, " - ") <= 0)
+            goto err;
+        if (GENERAL_NAME_print(bp, ad->location) <= 0)
+            goto err;
+    }
+    return 1;
+ err:
+    return 0;
+}
diff --git a/crypto/otp/build.info b/crypto/otp/build.info
new file mode 100644
index 000000000..919210059
--- /dev/null
+++ b/crypto/otp/build.info
@@ -0,0 +1,2 @@
+LIBS=../../libcrypto
+SOURCE[../../libcrypto]=otp_err.c otp_lib.c
diff --git a/crypto/otp/otp_err.c b/crypto/otp/otp_err.c
new file mode 100644
index 000000000..aa7cd0f34
--- /dev/null
+++ b/crypto/otp/otp_err.c
@@ -0,0 +1,44 @@
+/*
+ * Generated by util/mkerr.pl DO NOT EDIT
+ * Copyright 1995-2017 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include 
+
+/* BEGIN ERROR CODES */
+#ifndef OPENSSL_NO_ERR
+
+# define ERR_FUNC(func) ERR_PACK(ERR_LIB_OTP,func,0)
+# define ERR_REASON(reason) ERR_PACK(ERR_LIB_OTP,0,reason)
+
+static ERR_STRING_DATA OTP_str_functs[] = {
+    {ERR_FUNC(OTP_F_OTP_GENERATE), "OTP_generate"},
+    {0, NULL}
+};
+
+static ERR_STRING_DATA OTP_str_reasons[] = {
+    {ERR_REASON(OTP_R_CMAC_FAILURE), "cmac failure"},
+    {ERR_REASON(OTP_R_INVALID_PARAMS), "invalid params"},
+    {0, NULL}
+};
+
+#endif
+
+int ERR_load_OTP_strings(void)
+{
+#ifndef OPENSSL_NO_ERR
+
+    if (ERR_func_error_string(OTP_str_functs[0].error) == NULL) {
+        ERR_load_strings(0, OTP_str_functs);
+        ERR_load_strings(0, OTP_str_reasons);
+    }
+#endif
+    return 1;
+}
diff --git a/crypto/otp/otp_lib.c b/crypto/otp/otp_lib.c
new file mode 100644
index 000000000..eaacf3f8a
--- /dev/null
+++ b/crypto/otp/otp_lib.c
@@ -0,0 +1,187 @@
+/* ====================================================================
+ * Copyright (c) 2015 - 2016 The GmSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the GmSSL Project.
+ *    (http://gmssl.org/)"
+ *
+ * 4. The name "GmSSL Project" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    guanzhi1980@gmail.com.
+ *
+ * 5. Products derived from this software may not be called "GmSSL"
+ *    nor may "GmSSL" appear in their names without prior written
+ *    permission of the GmSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the GmSSL Project
+ *    (http://gmssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE GmSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE GmSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include "../modes/modes_lcl.h"
+
+static int pow_table[] = {
+	1,
+	10,
+	100,
+	1000,
+	10000,
+	100000,
+	1000000,
+	10000000,
+	100000000,
+};
+
+static int check_params(const OTP_PARAMS *params)
+{
+	if ((params->te < 1 || params->te > 60) ||
+		(params->type != NID_sm3 && params->type != NID_sms4_ecb) || /* about to change */
+		(params->otp_digits >= sizeof(pow_table) || params->otp_digits < 4)) {
+		return 0;
+	}
+	return 1;
+}
+
+int OTP_generate(const OTP_PARAMS *params, const void *event, size_t eventlen,
+	unsigned int *otp, const unsigned char *key, size_t keylen)
+{
+	int ret = 0;
+	time_t t = 0;
+	unsigned char *id = NULL;
+	size_t idlen;
+	const EVP_MD *md;
+	const EVP_CIPHER *cipher;
+	EVP_MD_CTX *mdctx = NULL;
+	CMAC_CTX *cmctx = NULL;
+	unsigned char s[EVP_MAX_MD_SIZE];
+	size_t slen;
+	uint32_t od;
+	int i, n;
+
+	OPENSSL_assert(sizeof(time_t) == 8);
+
+	if (!check_params(params)) {
+		OTPerr(OTP_F_OTP_GENERATE, OTP_R_INVALID_PARAMS);
+		return 0;
+	}
+
+	idlen = sizeof(uint64_t) + eventlen + params->option_size;
+	if (idlen < 16) {
+		idlen = 16;
+	}
+	if (!(id = OPENSSL_malloc(idlen))) {
+		OTPerr(OTP_F_OTP_GENERATE, ERR_R_MALLOC_FAILURE);
+		goto end;
+	}
+	memset(id, 0, idlen);
+
+	t = time(NULL) + params->offset;
+	t /= params->te;
+
+	memcpy(id, &t, sizeof(t));
+	memcpy(id + sizeof(t), event, eventlen);
+	memcpy(id + sizeof(t) + eventlen, params->option, params->option_size);
+
+
+	/* FIXME: try to get md and cipher, and check if cipher is ECB */
+	if (params->type == NID_sm3) {
+		md = EVP_get_digestbynid(params->type);
+		if (!(mdctx = EVP_MD_CTX_new())) {
+			OTPerr(OTP_F_OTP_GENERATE, ERR_R_MALLOC_FAILURE);
+			goto end;
+		}
+		if (!EVP_DigestInit_ex(mdctx, md, NULL)) {
+			OTPerr(OTP_F_OTP_GENERATE, ERR_R_EVP_LIB);
+			goto end;
+		}
+		if (!EVP_DigestUpdate(mdctx, key, keylen)) {
+			OTPerr(OTP_F_OTP_GENERATE, ERR_R_EVP_LIB);
+			goto end;
+		}
+		if (!EVP_DigestUpdate(mdctx, id, idlen)) {
+			OTPerr(OTP_F_OTP_GENERATE, ERR_R_EVP_LIB);
+			goto end;
+		}
+		if (!EVP_DigestFinal_ex(mdctx, s, (unsigned int *)&slen)) {
+			OTPerr(OTP_F_OTP_GENERATE, ERR_R_EVP_LIB);
+			goto end;
+		}
+	} else if (params->type == NID_sms4_ecb) {
+		cipher = EVP_get_cipherbynid(params->type);
+		if (!(cmctx = CMAC_CTX_new())) {
+			OTPerr(OTP_F_OTP_GENERATE, ERR_R_MALLOC_FAILURE);
+			goto end;
+		}
+		if (!CMAC_Init(cmctx, key, keylen, cipher, NULL)) {
+			OTPerr(OTP_F_OTP_GENERATE, OTP_R_CMAC_FAILURE);
+			goto end;
+		}
+		if (!CMAC_Update(cmctx, id, idlen)) {
+			OTPerr(OTP_F_OTP_GENERATE, OTP_R_CMAC_FAILURE);
+			goto end;
+		}
+		if (!CMAC_Final(cmctx, s, &slen)) {
+			OTPerr(OTP_F_OTP_GENERATE, OTP_R_CMAC_FAILURE);
+			goto end;
+		}
+	} else {
+		goto end;
+	}
+	OPENSSL_assert(slen % 4 == 0);
+
+	od = 0;
+
+	n = (int)slen;
+	for (i = 0; i < n/4; i++) {
+		od += GETU32(&s[i * 4]);
+	}
+
+	*otp = od % pow_table[params->otp_digits];
+	ret = 1;
+end:
+	OPENSSL_free(id);
+	EVP_MD_CTX_free(mdctx);
+	CMAC_CTX_free(cmctx);
+	return ret;
+}
+
diff --git a/crypto/paillier/build.info b/crypto/paillier/build.info
new file mode 100644
index 000000000..58e43106f
--- /dev/null
+++ b/crypto/paillier/build.info
@@ -0,0 +1,3 @@
+LIBS=../../libcrypto
+SOURCE[../../libcrypto]=pai_lib.c pai_err.c pai_asn1.c pai_ameth.c \
+	pai_pmeth.c
diff --git a/crypto/paillier/pai_ameth.c b/crypto/paillier/pai_ameth.c
new file mode 100644
index 000000000..d0eeca8dc
--- /dev/null
+++ b/crypto/paillier/pai_ameth.c
@@ -0,0 +1,263 @@
+/* ====================================================================
+ * Copyright (c) 2015 - 2017 The GmSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the GmSSL Project.
+ *    (http://gmssl.org/)"
+ *
+ * 4. The name "GmSSL Project" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    guanzhi1980@gmail.com.
+ *
+ * 5. Products derived from this software may not be called "GmSSL"
+ *    nor may "GmSSL" appear in their names without prior written
+ *    permission of the GmSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the GmSSL Project
+ *    (http://gmssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE GmSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE GmSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include "internal/cryptlib.h"
+#include "internal/asn1_int.h"
+#include "internal/evp_int.h"
+#include "pai_lcl.h"
+
+
+static int paillier_pub_encode(X509_PUBKEY *pubkey, const EVP_PKEY *pkey)
+{
+	unsigned char *penc = NULL;
+	int penclen;
+
+	if ((penclen = i2d_PaillierPublicKey(pkey->pkey.paillier, &penc)) <= 0) {
+		return 0;
+	}
+	if (X509_PUBKEY_set0_param(pubkey, OBJ_nid2obj(EVP_PKEY_PAILLIER),
+		V_ASN1_NULL, NULL, penc, penclen)) {
+		return 1;
+	}
+
+	OPENSSL_free(penc);
+	return 0;
+}
+
+static int paillier_pub_decode(EVP_PKEY *pkey, X509_PUBKEY *pubkey)
+{
+	const unsigned char *cp;
+	int len;
+	PAILLIER *paillier = NULL;
+
+	if (!X509_PUBKEY_get0_param(NULL, &cp, &len, NULL, pubkey)) {
+		return 0;
+	}
+	if (!(paillier = d2i_PaillierPublicKey(NULL, &cp, len))) {
+		PAILLIERerr(PAILLIER_F_PAILLIER_PUB_DECODE, ERR_R_PAILLIER_LIB);
+		return 0;
+	}
+
+	EVP_PKEY_assign_PAILLIER(pkey, paillier);
+	return 1;
+}
+
+static int paillier_pub_cmp(const EVP_PKEY *a, const EVP_PKEY *b)
+{
+	if (BN_cmp(a->pkey.paillier->n, b->pkey.paillier->n) != 0) {
+		return 0;
+	}
+	return 1;
+}
+
+static int do_paillier_print(BIO *bp, const PAILLIER *x, int off, int priv)
+{
+	char *str;
+	int ret = 0;
+	int bits;
+
+	if (!BIO_indent(bp, off, 128))
+		goto end;
+
+	bits = x->bits;
+	if (bits == 0)
+		bits = BN_num_bytes(x->n) * 8;
+
+	if (priv && x->lambda) {
+		if (BIO_printf(bp, "Private-Key: (%d bit)\n", bits) <= 0)
+			goto end;
+		str = "modulus";
+	} else {
+		if (BIO_printf(bp, "Public-Key: (%d bit)\n", bits) <= 0)
+			goto end;
+		str = "Modulus";
+	}
+
+	if (!ASN1_bn_print(bp, str, x->n, NULL, off))
+		goto end;
+	if (priv) {
+		if (!ASN1_bn_print(bp, "lambda:", x->lambda, NULL, off))
+			goto end;
+		if (x->x && !ASN1_bn_print(bp, "x:", x->x, NULL, off))
+			goto end;
+	}
+	ret = 1;
+
+end:
+	return ret;
+}
+
+static int paillier_pub_print(BIO *bp, const EVP_PKEY *pkey, int indent,
+	ASN1_PCTX *ctx)
+{
+	return do_paillier_print(bp, pkey->pkey.paillier, indent, 0);
+}
+
+static int paillier_priv_print(BIO *bp, const EVP_PKEY *pkey, int indent,
+	ASN1_PCTX *ctx)
+{
+	return do_paillier_print(bp, pkey->pkey.paillier, indent, 1);
+}
+
+static int paillier_priv_decode(EVP_PKEY *pkey, const PKCS8_PRIV_KEY_INFO *p8)
+{
+	const unsigned char *p;
+	int pklen;
+	PAILLIER *paillier;
+
+	if (!PKCS8_pkey_get0(NULL, &p, &pklen, NULL, p8))
+		return 0;
+	if (!(paillier = d2i_PaillierPrivateKey(NULL, &p, pklen))) {
+		PAILLIERerr(PAILLIER_F_PAILLIER_PRIV_DECODE, ERR_R_PAILLIER_LIB);
+		return 0;
+	}
+	paillier->bits = BN_num_bytes(paillier->n) * 8;
+	EVP_PKEY_assign_PAILLIER(pkey, paillier);
+	return 1;
+}
+
+static int paillier_priv_encode(PKCS8_PRIV_KEY_INFO *p8, const EVP_PKEY *pkey)
+{
+	unsigned char *rk = NULL;
+	int rklen;
+
+	if ((rklen = i2d_PaillierPrivateKey(pkey->pkey.paillier, &rk)) <= 0) {
+		PAILLIERerr(PAILLIER_F_PAILLIER_PRIV_ENCODE, ERR_R_MALLOC_FAILURE);
+		return 0;
+	}
+
+	if (!PKCS8_pkey_set0(p8, OBJ_nid2obj(NID_paillier), 0, V_ASN1_NULL, NULL,
+		rk, rklen)) {
+		PAILLIERerr(PAILLIER_F_PAILLIER_PRIV_ENCODE, ERR_R_MALLOC_FAILURE);
+		return 0;
+	}
+
+	return 1;
+}
+
+static int int_paillier_size(const EVP_PKEY *pkey)
+{
+	return PAILLIER_size(pkey->pkey.paillier);
+}
+
+static int paillier_bits(const EVP_PKEY *pkey)
+{
+	return pkey->pkey.paillier->bits;
+}
+
+static int paillier_security_bits(const EVP_PKEY *pkey)
+{
+	return PAILLIER_security_bits(pkey->pkey.paillier);
+}
+
+static void int_paillier_free(EVP_PKEY *pkey)
+{
+	PAILLIER_free(pkey->pkey.paillier);
+}
+
+static int paillier_pkey_ctrl(EVP_PKEY *pkey, int op, long arg1, void *arg2)
+{
+	return -2;
+}
+
+static int old_paillier_priv_decode(EVP_PKEY *pkey,
+	const unsigned char **pder, int derlen)
+{
+	PAILLIER *pai;
+	if ((pai = d2i_PaillierPrivateKey(NULL, pder, derlen)) == NULL) {
+		PAILLIERerr(PAILLIER_F_OLD_PAILLIER_PRIV_DECODE,
+			PAILLIER_R_DECODE_ERROR);
+		return 0;
+	}
+	EVP_PKEY_assign_PAILLIER(pkey, pai);
+	return 1;
+}
+
+static int old_paillier_priv_encode(const EVP_PKEY *pkey, unsigned char **pder)
+{
+	return i2d_PaillierPrivateKey(pkey->pkey.paillier, pder);
+}
+
+const EVP_PKEY_ASN1_METHOD paillier_asn1_meth = {
+	EVP_PKEY_PAILLIER,		/* pkey_id */
+	EVP_PKEY_PAILLIER,		/* pkey_base_id */
+	0,				/* pkey_flags */
+	"PAILLIER",			/* pem_str */
+	"GmSSL Paillier algorithm",	/* info */
+	paillier_pub_decode,		/* pub_decode */
+	paillier_pub_encode,		/* pub_encode */
+	paillier_pub_cmp,		/* pub_cmp */
+	paillier_pub_print,		/* pub_print */
+	paillier_priv_decode,		/* priv_decode */
+	paillier_priv_encode,		/* priv_encode */
+	paillier_priv_print,		/* priv_print */
+	int_paillier_size,		/* pkey_size */
+	paillier_bits,			/* pkey_bits */
+	paillier_security_bits,		/* pkey_security_bits */
+	NULL,				/* param_decode */
+	NULL,				/* param_encode */
+	NULL,				/* param_missing */
+	NULL,				/* param_copy */
+	NULL,				/* param_cmp */
+	NULL,				/* param_print */
+	NULL,				/* sig_print */
+	int_paillier_free,		/* pkey_free */
+	paillier_pkey_ctrl,		/* pkey_ctrl */
+	old_paillier_priv_decode,	/* old_priv_decode */
+	old_paillier_priv_encode,	/* old_priv_encode */
+	NULL,				/* item_verify */
+	NULL,				/* item_sign */
+};
diff --git a/crypto/paillier/pai_asn1.c b/crypto/paillier/pai_asn1.c
new file mode 100644
index 000000000..650ee866b
--- /dev/null
+++ b/crypto/paillier/pai_asn1.c
@@ -0,0 +1,84 @@
+/* ====================================================================
+ * Copyright (c) 2015 - 2016 The GmSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the GmSSL Project.
+ *    (http://gmssl.org/)"
+ *
+ * 4. The name "GmSSL Project" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    guanzhi1980@gmail.com.
+ *
+ * 5. Products derived from this software may not be called "GmSSL"
+ *    nor may "GmSSL" appear in their names without prior written
+ *    permission of the GmSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the GmSSL Project
+ *    (http://gmssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE GmSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE GmSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include "pai_lcl.h"
+
+static int paillier_cb(int operation, ASN1_VALUE **pval,
+	const ASN1_ITEM *it, void *exarg)
+{
+	if (operation == ASN1_OP_NEW_PRE) {
+		*pval = (ASN1_VALUE *)PAILLIER_new();
+		if (*pval)
+			return 2;
+		return 0;
+	} else if (operation == ASN1_OP_FREE_PRE) {
+		PAILLIER_free((PAILLIER *)*pval);
+		*pval = NULL;
+		return 2;
+	}
+	return 1;
+}
+
+ASN1_SEQUENCE_cb(PaillierPrivateKey, paillier_cb) = {
+	ASN1_SIMPLE(PAILLIER, n, BIGNUM),
+	ASN1_SIMPLE(PAILLIER, lambda, BIGNUM),
+	ASN1_SIMPLE(PAILLIER, x, BIGNUM)
+} ASN1_SEQUENCE_END_cb(PAILLIER, PaillierPrivateKey)
+
+ASN1_SEQUENCE_cb(PaillierPublicKey, paillier_cb) = {
+	ASN1_SIMPLE(PAILLIER, n, BIGNUM)
+} ASN1_SEQUENCE_END_cb(PAILLIER, PaillierPublicKey)
+
+IMPLEMENT_ASN1_ENCODE_FUNCTIONS_const_fname(PAILLIER, PaillierPrivateKey, PaillierPrivateKey)
+IMPLEMENT_ASN1_ENCODE_FUNCTIONS_const_fname(PAILLIER, PaillierPublicKey, PaillierPublicKey)
diff --git a/crypto/paillier/pai_err.c b/crypto/paillier/pai_err.c
new file mode 100644
index 000000000..b5a777146
--- /dev/null
+++ b/crypto/paillier/pai_err.c
@@ -0,0 +1,70 @@
+/*
+ * Generated by util/mkerr.pl DO NOT EDIT
+ * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include 
+
+/* BEGIN ERROR CODES */
+#ifndef OPENSSL_NO_ERR
+
+# define ERR_FUNC(func) ERR_PACK(ERR_LIB_PAILLIER,func,0)
+# define ERR_REASON(reason) ERR_PACK(ERR_LIB_PAILLIER,0,reason)
+
+static ERR_STRING_DATA PAILLIER_str_functs[] = {
+    {ERR_FUNC(PAILLIER_F_OLD_PAILLIER_PRIV_DECODE),
+     "old_paillier_priv_decode"},
+    {ERR_FUNC(PAILLIER_F_PAILLIER_CHECK_KEY), "PAILLIER_check_key"},
+    {ERR_FUNC(PAILLIER_F_PAILLIER_CIPHERTEXT_ADD), "PAILLIER_ciphertext_add"},
+    {ERR_FUNC(PAILLIER_F_PAILLIER_CIPHERTEXT_SCALAR_MUL),
+     "PAILLIER_ciphertext_scalar_mul"},
+    {ERR_FUNC(PAILLIER_F_PAILLIER_DECRYPT), "PAILLIER_decrypt"},
+    {ERR_FUNC(PAILLIER_F_PAILLIER_ENCRYPT), "PAILLIER_encrypt"},
+    {ERR_FUNC(PAILLIER_F_PAILLIER_GENERATE_KEY), "PAILLIER_generate_key"},
+    {ERR_FUNC(PAILLIER_F_PAILLIER_NEW), "PAILLIER_new"},
+    {ERR_FUNC(PAILLIER_F_PAILLIER_PLAINTEXT_SIZE), "paillier_plaintext_size"},
+    {ERR_FUNC(PAILLIER_F_PAILLIER_PRIV_DECODE), "paillier_priv_decode"},
+    {ERR_FUNC(PAILLIER_F_PAILLIER_PRIV_ENCODE), "paillier_priv_encode"},
+    {ERR_FUNC(PAILLIER_F_PAILLIER_PUB_DECODE), "paillier_pub_decode"},
+    {ERR_FUNC(PAILLIER_F_PAILLIER_SIZE), "PAILLIER_size"},
+    {ERR_FUNC(PAILLIER_F_PKEY_PAILLIER_CTRL), "pkey_paillier_ctrl"},
+    {ERR_FUNC(PAILLIER_F_PKEY_PAILLIER_CTRL_STR), "pkey_paillier_ctrl_str"},
+    {ERR_FUNC(PAILLIER_F_PKEY_PAILLIER_DECRYPT), "pkey_paillier_decrypt"},
+    {ERR_FUNC(PAILLIER_F_PKEY_PAILLIER_ENCRYPT), "pkey_paillier_encrypt"},
+    {ERR_FUNC(PAILLIER_F_PKEY_PAILLIER_INIT), "pkey_paillier_init"},
+    {ERR_FUNC(PAILLIER_F_PKEY_PAILLIER_KEYGEN), "pkey_paillier_keygen"},
+    {0, NULL}
+};
+
+static ERR_STRING_DATA PAILLIER_str_reasons[] = {
+    {ERR_REASON(PAILLIER_R_BUFFER_TOO_SMALL), "buffer too small"},
+    {ERR_REASON(PAILLIER_R_DECODE_ERROR), "decode error"},
+    {ERR_REASON(PAILLIER_R_GENERATE_PRIME_FAILED), "generate prime failed"},
+    {ERR_REASON(PAILLIER_R_INVALID_PLAINTEXT), "invalid plaintext"},
+    {ERR_REASON(PAILLIER_R_KEY_SIZE_TOO_SMALL), "key size too small"},
+    {ERR_REASON(PAILLIER_R_MALLOC_FAILED), "malloc failed"},
+    {ERR_REASON(PAILLIER_R_NOT_IMPLEMENTED), "not implemented"},
+    {ERR_REASON(PAILLIER_R_VALUE_MISSING), "value missing"},
+    {0, NULL}
+};
+
+#endif
+
+int ERR_load_PAILLIER_strings(void)
+{
+#ifndef OPENSSL_NO_ERR
+
+    if (ERR_func_error_string(PAILLIER_str_functs[0].error) == NULL) {
+        ERR_load_strings(0, PAILLIER_str_functs);
+        ERR_load_strings(0, PAILLIER_str_reasons);
+    }
+#endif
+    return 1;
+}
diff --git a/crypto/paillier/pai_lcl.h b/crypto/paillier/pai_lcl.h
new file mode 100644
index 000000000..5f16a2d0c
--- /dev/null
+++ b/crypto/paillier/pai_lcl.h
@@ -0,0 +1,69 @@
+/* ====================================================================
+ * Copyright (c) 2015 - 2016 The GmSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the GmSSL Project.
+ *    (http://gmssl.org/)"
+ *
+ * 4. The name "GmSSL Project" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    guanzhi1980@gmail.com.
+ *
+ * 5. Products derived from this software may not be called "GmSSL"
+ *    nor may "GmSSL" appear in their names without prior written
+ *    permission of the GmSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the GmSSL Project
+ *    (http://gmssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE GmSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE GmSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+
+#ifndef HEADER_PAI_LCL_H
+#define HEADER_PAI_LCL_H
+
+#include "e_os.h"
+
+struct paillier_st {
+	int bits;
+	BIGNUM *n;		/* public key */
+	BIGNUM *lambda;		/* private key, lambda(n) = lcm(p-1, q-1) */
+	BIGNUM *n_squared;	/* online */
+	BIGNUM *n_plusone;	/* online */
+	BIGNUM *x;		/* online */
+
+	int references;
+	int flags;
+	CRYPTO_EX_DATA ex_data;
+	CRYPTO_RWLOCK *lock;
+};
+
+#endif
diff --git a/crypto/paillier/pai_lib.c b/crypto/paillier/pai_lib.c
new file mode 100644
index 000000000..84c423c58
--- /dev/null
+++ b/crypto/paillier/pai_lib.c
@@ -0,0 +1,420 @@
+/* ====================================================================
+ * Copyright (c) 2015 - 2016 The GmSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the GmSSL Project.
+ *    (http://gmssl.org/)"
+ *
+ * 4. The name "GmSSL Project" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    guanzhi1980@gmail.com.
+ *
+ * 5. Products derived from this software may not be called "GmSSL"
+ *    nor may "GmSSL" appear in their names without prior written
+ *    permission of the GmSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the GmSSL Project
+ *    (http://gmssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE GmSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE GmSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include "pai_lcl.h"
+
+PAILLIER *PAILLIER_new(void)
+{
+	PAILLIER *ret = NULL;
+
+	if (!(ret = OPENSSL_zalloc(sizeof(*ret)))) {
+		PAILLIERerr(PAILLIER_F_PAILLIER_NEW, PAILLIER_R_MALLOC_FAILED);
+		return NULL;
+	}
+
+	return ret;
+}
+
+void PAILLIER_free(PAILLIER *key)
+{
+	if (key) {
+		BN_free(key->n);
+		BN_free(key->lambda);
+		BN_free(key->n_squared);
+		BN_free(key->n_plusone);
+		BN_free(key->x);
+	}
+	OPENSSL_clear_free(key, sizeof(*key));
+}
+
+int PAILLIER_size(const PAILLIER *key)
+{
+	ASN1_INTEGER a;
+	unsigned char buf[4] = {0xff};
+	int i;
+
+	if (!(i = BN_num_bytes(key->n))) {
+		PAILLIERerr(PAILLIER_F_PAILLIER_SIZE, ERR_R_BN_LIB);
+		return 0;
+	}
+
+	a.length = i * 2;
+	a.data = buf;
+	a.type = V_ASN1_INTEGER;
+
+	return i2d_ASN1_INTEGER(&a, NULL);
+}
+
+int PAILLIER_security_bits(const PAILLIER *key)
+{
+	return BN_security_bits(BN_num_bits(key->n)/2, -1);
+}
+
+int PAILLIER_generate_key(PAILLIER *key, int bits)
+{
+	int ret = 0;
+	BIGNUM *p = NULL;
+	BIGNUM *q = NULL;
+	BN_CTX *bn_ctx = NULL;
+
+	p = BN_new();
+	q = BN_new();
+	bn_ctx = BN_CTX_new();
+
+	if (!key->n)
+		key->n = BN_new();
+	if (!key->lambda)
+		key->lambda = BN_new();
+	if (!key->n_squared)
+		key->n_squared = BN_new();
+	if (!key->n_plusone)
+		key->n_plusone = BN_new();
+	if (!key->x)
+		key->x = BN_new();
+
+	if (!p || !q || !bn_ctx || !key->n || !key->lambda ||
+		!key->n_squared || !key->n_plusone || !key->x) {
+		PAILLIERerr(PAILLIER_F_PAILLIER_GENERATE_KEY, ERR_R_MALLOC_FAILURE);
+		goto end;
+	}
+
+	key->bits = bits;
+
+	do {
+		if (!BN_generate_prime_ex(p, bits/2, 0, NULL, NULL, NULL)) {
+			PAILLIERerr(PAILLIER_F_PAILLIER_GENERATE_KEY,
+				PAILLIER_R_GENERATE_PRIME_FAILED);
+			goto end;
+		}
+
+		if (!BN_generate_prime_ex(q, bits/2, 0, NULL, NULL, NULL)) {
+			PAILLIERerr(PAILLIER_F_PAILLIER_GENERATE_KEY,
+				PAILLIER_R_GENERATE_PRIME_FAILED);
+			goto end;
+		}
+
+		if (!BN_mul(key->n, p, q, bn_ctx)
+			|| !BN_sub_word(p, 1)
+			|| !BN_sub_word(q, 1)
+			/* lambda = (p - 1)*(q - 1) */
+			|| !BN_mul(key->lambda, p, q, bn_ctx)
+			/* n_squared = n^2 */
+			|| !BN_sqr(key->n_squared, key->n, bn_ctx)
+			/* n_plusone = n + 1 */
+			|| !BN_copy(key->n_plusone, key->n)
+			|| !BN_add_word(key->n_plusone, 1)
+			/* x = (((g^lambda mod n^2) - 1)/n)^-1 mod n */
+			|| !BN_mod_exp(key->x, key->n_plusone, key->lambda, key->n_squared, bn_ctx)
+			|| !BN_sub_word(key->x, 1)
+			|| !BN_div(key->x, NULL, key->x, key->n, bn_ctx)
+			|| !BN_mod_inverse(key->x, key->x, key->n, bn_ctx)
+			) {
+			PAILLIERerr(PAILLIER_F_PAILLIER_GENERATE_KEY, ERR_R_BN_LIB);
+			goto end;
+		}
+
+	} while (0);
+
+	ret = 1;
+
+end:
+	BN_clear_free(p);
+	BN_clear_free(q);
+	return ret;
+}
+
+int PAILLIER_check_key(PAILLIER *key)
+{
+	PAILLIERerr(PAILLIER_F_PAILLIER_CHECK_KEY, PAILLIER_R_NOT_IMPLEMENTED);
+	return 0;
+}
+
+int PAILLIER_encrypt(BIGNUM *c, const BIGNUM *m, PAILLIER *pub_key)
+{
+	int ret = 0;
+	BIGNUM *r = NULL;
+	BN_CTX *bn_ctx = NULL;
+
+	if (BN_cmp(m, pub_key->n) >= 0) {
+		PAILLIERerr(PAILLIER_F_PAILLIER_ENCRYPT, PAILLIER_R_INVALID_PLAINTEXT);
+		goto end;
+	}
+
+	r = BN_new();
+	bn_ctx = BN_CTX_new();
+	if (!r || !bn_ctx) {
+		PAILLIERerr(PAILLIER_F_PAILLIER_ENCRYPT, ERR_R_BN_LIB);
+		goto end;
+	}
+
+	do {
+		if (!BN_rand_range(r, pub_key->n)) {
+			PAILLIERerr(PAILLIER_F_PAILLIER_ENCRYPT, ERR_R_BN_LIB);
+			goto end;
+		}
+	} while (BN_is_zero(r));
+
+	if (!pub_key->n_plusone) {
+		if (!(pub_key->n_plusone = BN_dup(pub_key->n))) {
+			PAILLIERerr(PAILLIER_F_PAILLIER_ENCRYPT, ERR_R_BN_LIB);
+			goto end;
+		}
+		if (!BN_add_word(pub_key->n_plusone, 1)) {
+			PAILLIERerr(PAILLIER_F_PAILLIER_ENCRYPT, ERR_R_BN_LIB);
+			BN_free(pub_key->n_plusone);
+			pub_key->n_plusone = NULL;
+			goto end;
+		}
+	}
+
+	if (!pub_key->n_squared) {
+		if (!(pub_key->n_squared = BN_new())) {
+			PAILLIERerr(PAILLIER_F_PAILLIER_ENCRYPT, ERR_R_BN_LIB);
+			goto end;
+		}
+		if (!BN_sqr(pub_key->n_squared, pub_key->n, bn_ctx)) {
+			PAILLIERerr(PAILLIER_F_PAILLIER_ENCRYPT, ERR_R_BN_LIB);
+			BN_free(pub_key->n_squared);
+			pub_key->n_squared = NULL;
+			goto end;
+		}
+	}
+
+	if (!BN_mod_exp(c, pub_key->n_plusone, m, pub_key->n_squared, bn_ctx)) {
+		PAILLIERerr(PAILLIER_F_PAILLIER_ENCRYPT, ERR_R_BN_LIB);
+		goto end;
+	}
+
+	if (!BN_mod_exp(r, r, pub_key->n, pub_key->n_squared, bn_ctx)) {
+		PAILLIERerr(PAILLIER_F_PAILLIER_ENCRYPT, ERR_R_BN_LIB);
+		goto end;
+	}
+
+	if (!BN_mod_mul(c, c, r, pub_key->n_squared, bn_ctx)) {
+		PAILLIERerr(PAILLIER_F_PAILLIER_ENCRYPT, ERR_R_BN_LIB);
+		goto end;
+	}
+
+	ret = 1;
+end:
+	BN_clear_free(r);
+	BN_CTX_free(bn_ctx);
+	return ret;
+}
+
+int PAILLIER_decrypt(BIGNUM *m, const BIGNUM *c, PAILLIER *key)
+{
+	int ret = 0;
+	BN_CTX *bn_ctx = NULL;
+
+	if (!(bn_ctx = BN_CTX_new())) {
+		PAILLIERerr(PAILLIER_F_PAILLIER_DECRYPT, ERR_R_BN_LIB);
+		goto end;
+	}
+
+	if (!key->n_squared) {
+		if (!(key->n_squared = BN_new())) {
+			PAILLIERerr(PAILLIER_F_PAILLIER_DECRYPT, ERR_R_MALLOC_FAILURE);
+			goto end;
+		}
+		if (!BN_sqr(key->n_squared, key->n, bn_ctx)) {
+			PAILLIERerr(PAILLIER_F_PAILLIER_DECRYPT, ERR_R_BN_LIB);
+			goto end;
+		}
+	}
+
+	if (!BN_mod_exp(m, c, key->lambda, key->n_squared, bn_ctx)) {
+		PAILLIERerr(PAILLIER_F_PAILLIER_DECRYPT, ERR_R_BN_LIB);
+		goto end;
+	}
+
+	if (!BN_sub_word(m, 1)) {
+		PAILLIERerr(PAILLIER_F_PAILLIER_DECRYPT, ERR_R_BN_LIB);
+		goto end;
+	}
+
+	if (!BN_div(m, NULL, m, key->n, bn_ctx)) {
+		PAILLIERerr(PAILLIER_F_PAILLIER_DECRYPT, ERR_R_BN_LIB);
+		goto end;
+	}
+
+	if (!BN_mod_mul(m, m, key->x, key->n, bn_ctx)) {
+		PAILLIERerr(PAILLIER_F_PAILLIER_DECRYPT, ERR_R_BN_LIB);
+		goto end;
+	}
+
+	ret = 1;
+end:
+	BN_CTX_free(bn_ctx);
+	return ret;
+}
+
+int PAILLIER_ciphertext_add(BIGNUM *r, const BIGNUM *a, const BIGNUM *b, PAILLIER *key)
+{
+	int ret = 0;
+	BIGNUM *k = NULL;
+	BN_CTX *bn_ctx = NULL;
+
+	k = BN_new();
+	bn_ctx = BN_CTX_new();
+	if (!k || !bn_ctx) {
+		PAILLIERerr(PAILLIER_F_PAILLIER_CIPHERTEXT_ADD, ERR_R_BN_LIB);
+		goto end;
+	}
+
+	do {
+		if (!BN_rand_range(k, key->n)) {
+			PAILLIERerr(PAILLIER_F_PAILLIER_CIPHERTEXT_ADD, ERR_R_BN_LIB);
+			goto end;
+		}
+	} while (BN_is_zero(k));
+
+	if (!key->n_squared) {
+		if (!(key->n_squared = BN_new())) {
+			PAILLIERerr(PAILLIER_F_PAILLIER_CIPHERTEXT_ADD, ERR_R_MALLOC_FAILURE);
+			goto end;
+		}
+		if (!BN_sqr(key->n_squared, key->n, bn_ctx)) {
+			PAILLIERerr(PAILLIER_F_PAILLIER_CIPHERTEXT_ADD, ERR_R_BN_LIB);
+			goto end;
+		}
+	}
+
+	if (!BN_mod_exp(k, k, key->n, key->n_squared, bn_ctx)) {
+		PAILLIERerr(PAILLIER_F_PAILLIER_CIPHERTEXT_ADD, ERR_R_BN_LIB);
+		goto end;
+	}
+
+	if (!BN_mod_mul(r, a, b, key->n_squared, bn_ctx)) {
+		PAILLIERerr(PAILLIER_F_PAILLIER_CIPHERTEXT_ADD, ERR_R_BN_LIB);
+		goto end;
+	}
+
+	if (!BN_mod_mul(r, r, k, key->n_squared, bn_ctx)) {
+		PAILLIERerr(PAILLIER_F_PAILLIER_CIPHERTEXT_ADD, ERR_R_BN_LIB);
+		goto end;
+	}
+
+	ret = 1;
+end:
+	BN_clear_free(k);
+	BN_CTX_free(bn_ctx);
+	return ret;
+}
+
+int PAILLIER_ciphertext_scalar_mul(BIGNUM *r, const BIGNUM *scalar, const BIGNUM *a, PAILLIER *key)
+{
+	int ret = 0;
+	BIGNUM *k = NULL;
+	BN_CTX *bn_ctx = NULL;
+
+	k = BN_new();
+	bn_ctx = BN_CTX_new();
+	if (!k || !bn_ctx) {
+		PAILLIERerr(PAILLIER_F_PAILLIER_CIPHERTEXT_SCALAR_MUL, ERR_R_BN_LIB);
+		goto end;
+	}
+
+	do {
+		if (!BN_rand_range(k, key->n)) {
+			PAILLIERerr(PAILLIER_F_PAILLIER_CIPHERTEXT_SCALAR_MUL, ERR_R_BN_LIB);
+			goto end;
+		}
+	} while (BN_is_zero(k));
+
+	if (!key->n_squared) {
+		if (!(key->n_squared = BN_new())) {
+			PAILLIERerr(PAILLIER_F_PAILLIER_CIPHERTEXT_SCALAR_MUL, ERR_R_MALLOC_FAILURE);
+			goto end;
+		}
+		if (!BN_sqr(key->n_squared, key->n, bn_ctx)) {
+			PAILLIERerr(PAILLIER_F_PAILLIER_CIPHERTEXT_SCALAR_MUL, ERR_R_BN_LIB);
+			goto end;
+		}
+	}
+
+	if (!BN_mod_exp(k, k, key->n, key->n_squared, bn_ctx)) {
+		PAILLIERerr(PAILLIER_F_PAILLIER_CIPHERTEXT_SCALAR_MUL, ERR_R_BN_LIB);
+		goto end;
+	}
+
+	if (!BN_mod_exp(r, a, scalar, key->n_squared, bn_ctx)) {
+		PAILLIERerr(PAILLIER_F_PAILLIER_CIPHERTEXT_SCALAR_MUL, ERR_R_BN_LIB);
+		goto end;
+	}
+
+	if (!BN_mod_mul(r, r, k, key->n_squared, bn_ctx)) {
+		PAILLIERerr(PAILLIER_F_PAILLIER_CIPHERTEXT_SCALAR_MUL, ERR_R_BN_LIB);
+		goto end;
+	}
+
+	ret = 1;
+end:
+	BN_clear_free(k);
+	BN_CTX_free(bn_ctx);
+	return ret;
+}
+
+int PAILLIER_up_ref(PAILLIER *r)
+{
+	int i;
+
+	if (CRYPTO_atomic_add(&r->references, 1, &i, r->lock) <= 0)
+		return 0;
+
+	REF_PRINT_COUNT("PAILLIER", r);
+	REF_ASSERT_ISNT(i < 2);
+	return ((i > 1) ? 1 : 0);
+}
diff --git a/crypto/paillier/pai_pmeth.c b/crypto/paillier/pai_pmeth.c
new file mode 100644
index 000000000..79b2bad82
--- /dev/null
+++ b/crypto/paillier/pai_pmeth.c
@@ -0,0 +1,317 @@
+/* ====================================================================
+ * Copyright (c) 2015 - 2017 The GmSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the GmSSL Project.
+ *    (http://gmssl.org/)"
+ *
+ * 4. The name "GmSSL Project" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    guanzhi1980@gmail.com.
+ *
+ * 5. Products derived from this software may not be called "GmSSL"
+ *    nor may "GmSSL" appear in their names without prior written
+ *    permission of the GmSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the GmSSL Project
+ *    (http://gmssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE GmSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE GmSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include "internal/evp_int.h"
+#include "pai_lcl.h"
+
+typedef struct {
+	int bits;
+} PAILLIER_PKEY_CTX;
+
+static int pkey_paillier_init(EVP_PKEY_CTX *ctx)
+{
+	PAILLIER_PKEY_CTX *dctx;
+	if (!(dctx = OPENSSL_zalloc(sizeof(*dctx)))) {
+		PAILLIERerr(PAILLIER_F_PKEY_PAILLIER_INIT, ERR_R_MALLOC_FAILURE);
+		return 0;
+	}
+	dctx->bits = 4096;
+	(void)EVP_PKEY_CTX_set_data(ctx, dctx);
+	return 1;
+}
+
+static int pkey_paillier_copy(EVP_PKEY_CTX *dst, EVP_PKEY_CTX *src)
+{
+	PAILLIER_PKEY_CTX *dctx;
+	PAILLIER_PKEY_CTX *sctx;
+	if (!pkey_paillier_init(dst))
+		return 0;
+	dctx = EVP_PKEY_CTX_get_data(dst);
+	sctx = EVP_PKEY_CTX_get_data(src);
+	OPENSSL_assert(sctx);
+	*dctx = *sctx;
+	return 1;
+}
+
+static void pkey_paillier_cleanup(EVP_PKEY_CTX *ctx)
+{
+	PAILLIER_PKEY_CTX *dctx = EVP_PKEY_CTX_get_data(ctx);
+	if (dctx) {
+		OPENSSL_free(dctx);
+	}
+}
+
+static int pkey_paillier_keygen(EVP_PKEY_CTX *ctx, EVP_PKEY *pkey)
+{
+	PAILLIER_PKEY_CTX *dctx = EVP_PKEY_CTX_get_data(ctx);
+	PAILLIER *pai = NULL;
+	if (!(pai = PAILLIER_new())) {
+		PAILLIERerr(PAILLIER_F_PKEY_PAILLIER_KEYGEN, ERR_R_MALLOC_FAILURE);
+		return 0;
+	}
+	if (!EVP_PKEY_assign_PAILLIER(pkey, pai)) {
+		PAILLIERerr(PAILLIER_F_PKEY_PAILLIER_KEYGEN, ERR_R_EVP_LIB);
+		PAILLIER_free(pai);
+		return 0;
+	}
+	if (!PAILLIER_generate_key(EVP_PKEY_get0_PAILLIER(pkey), dctx->bits)) {
+		PAILLIERerr(PAILLIER_F_PKEY_PAILLIER_KEYGEN, ERR_R_PAILLIER_LIB);
+		return 0;
+	}
+	return 1;
+}
+
+static int pkey_paillier_encrypt(EVP_PKEY_CTX *ctx, unsigned char *out, size_t *outlen,
+	const unsigned char *in, size_t inlen)
+{
+	int ret = 0;
+	PAILLIER *key = EVP_PKEY_get0_PAILLIER(EVP_PKEY_CTX_get0_pkey(ctx));
+	char *buf = NULL;
+	BIGNUM *m = NULL;
+	BIGNUM *c = NULL;
+	ASN1_INTEGER *ai = NULL;
+	int len;
+
+	if (!out) {
+		*outlen = PAILLIER_size(key);
+		return 1;
+	} else if (*outlen < (size_t)PAILLIER_size(key)) {
+		PAILLIERerr(PAILLIER_F_PKEY_PAILLIER_ENCRYPT, PAILLIER_R_BUFFER_TOO_SMALL);
+		return 0;
+	}
+
+	/* parse plaintext in decimal string format */
+	if (!(buf = OPENSSL_malloc(inlen + 1))) {
+		PAILLIERerr(PAILLIER_F_PKEY_PAILLIER_ENCRYPT, ERR_R_MALLOC_FAILURE);
+		return 0;
+	}
+	memcpy(buf, in, inlen);
+	buf[inlen] = 0;
+	if (!BN_dec2bn(&m, buf)) {
+		PAILLIERerr(PAILLIER_F_PKEY_PAILLIER_ENCRYPT, PAILLIER_R_INVALID_PLAINTEXT);
+		goto end;
+	}
+
+	/* encrypt and encode in asn1 integer format */
+	if (!(c = BN_new())) {
+		PAILLIERerr(PAILLIER_F_PKEY_PAILLIER_ENCRYPT, ERR_R_MALLOC_FAILURE);
+		goto end;
+	}
+	if (!PAILLIER_encrypt(c, m, key)) {
+		PAILLIERerr(PAILLIER_F_PKEY_PAILLIER_ENCRYPT, ERR_R_PAILLIER_LIB);
+		goto end;
+	}
+	if (!(ai = BN_to_ASN1_INTEGER(c, NULL))) {
+		PAILLIERerr(PAILLIER_F_PKEY_PAILLIER_ENCRYPT, ERR_R_ASN1_LIB);
+		goto end;
+	}
+	if ((len = i2d_ASN1_INTEGER(ai, &out)) <= 0) {
+		PAILLIERerr(PAILLIER_F_PKEY_PAILLIER_ENCRYPT, ERR_R_ASN1_LIB);
+		goto end;
+	}
+	*outlen = len;
+	ret = 1;
+
+end:
+	OPENSSL_clear_free(buf, inlen);
+	BN_clear_free(m);
+	BN_free(c);
+	ASN1_INTEGER_free(ai);
+	return ret;
+}
+
+static size_t paillier_plaintext_size(PAILLIER *key)
+{
+	size_t ret = 0;
+	BIGNUM *m = NULL;
+	char *dec = NULL;
+	int i;
+
+	if (!(i = BN_num_bits(key->n))
+		|| !(m = BN_new())
+		|| !BN_one(m)
+		|| !BN_lshift(m, m, i * 2)
+		|| !(dec = BN_bn2dec(m))) {
+		PAILLIERerr(PAILLIER_F_PAILLIER_PLAINTEXT_SIZE, ERR_R_BN_LIB);
+		goto end;
+	}
+	ret = strlen(dec) + 1;
+
+end:
+	BN_free(m);
+	OPENSSL_free(dec);
+	return ret;
+}
+
+static int pkey_paillier_decrypt(EVP_PKEY_CTX *ctx, unsigned char *out, size_t *outlen,
+	const unsigned char *in, size_t inlen)
+{
+	int ret = 0;
+	PAILLIER *key = EVP_PKEY_get0_PAILLIER(EVP_PKEY_CTX_get0_pkey(ctx));
+	const unsigned char *p = in;
+	ASN1_INTEGER *ai = NULL;
+	BIGNUM *m = NULL;
+	BIGNUM *c = NULL;
+	char *str = NULL;
+	size_t maxlen;
+
+	if (!(maxlen = paillier_plaintext_size(key))) {
+		PAILLIERerr(PAILLIER_F_PKEY_PAILLIER_DECRYPT, ERR_R_PAILLIER_LIB);
+		return 0;
+	}
+
+	if (!out) {
+		*outlen = maxlen;
+		return 1;
+	} else if (*outlen < maxlen) {
+		PAILLIERerr(PAILLIER_F_PKEY_PAILLIER_DECRYPT, PAILLIER_R_BUFFER_TOO_SMALL);
+		return 0;
+	}
+
+	/* decode ciphertext from asn1 integer */
+	if (!(ai = d2i_ASN1_INTEGER(NULL, &p, inlen))) {
+		PAILLIERerr(PAILLIER_F_PKEY_PAILLIER_DECRYPT, ERR_R_ASN1_LIB);
+		return 0;
+	}
+	if (!(c = ASN1_INTEGER_to_BN(ai, NULL))) {
+		PAILLIERerr(PAILLIER_F_PKEY_PAILLIER_DECRYPT, ERR_R_ASN1_LIB);
+		goto end;
+	}
+
+	/* decrypt and convert to decimal string */
+	if (!(m = BN_new())) {
+		PAILLIERerr(PAILLIER_F_PKEY_PAILLIER_DECRYPT, ERR_R_MALLOC_FAILURE);
+		goto end;
+	}
+	if (!PAILLIER_decrypt(m, c, key)) {
+		PAILLIERerr(PAILLIER_F_PKEY_PAILLIER_DECRYPT, ERR_R_PAILLIER_LIB);
+		goto end;
+	}
+	if (!(str = BN_bn2dec(m))) {
+		PAILLIERerr(PAILLIER_F_PKEY_PAILLIER_DECRYPT, ERR_R_BN_LIB);
+		goto end;
+	}
+
+	strcpy((char *)out, str);
+	*outlen = strlen(str) + 1;
+	ret = 1;
+
+end:
+	ASN1_INTEGER_free(ai);
+	OPENSSL_free(str);
+	BN_free(m);
+	BN_free(c);
+	return ret;
+}
+
+static int pkey_paillier_ctrl(EVP_PKEY_CTX *ctx, int type, int p1, void *p2)
+{
+	PAILLIER_PKEY_CTX *dctx = EVP_PKEY_CTX_get_data(ctx);
+	switch (type) {
+	case EVP_PKEY_CTRL_PAILLIER_KEYGEN_BITS:
+		if (p1 < PAILLIER_MIN_KEY_BITS) {
+			PAILLIERerr(PAILLIER_F_PKEY_PAILLIER_CTRL, PAILLIER_R_KEY_SIZE_TOO_SMALL);
+			return -2;
+		}
+		dctx->bits = p1;
+		return 1;
+	}
+	return -2;
+}
+
+static int pkey_paillier_ctrl_str(EVP_PKEY_CTX *ctx, const char *type, const char *value)
+{
+	if (!value) {
+		PAILLIERerr(PAILLIER_F_PKEY_PAILLIER_CTRL_STR, PAILLIER_R_VALUE_MISSING);
+		return 0;
+	}
+	if (!strcmp(type, "bits")) {
+		int nbits = atoi(value);
+		return EVP_PKEY_CTX_set_paillier_keygen_bits(ctx, nbits);
+	}
+	return -2;
+}
+
+#define EVP_PKEY_PAILLIER NID_paillier
+
+
+const EVP_PKEY_METHOD paillier_pkey_meth = {
+	EVP_PKEY_PAILLIER,	/* pkey_id */
+	0,			/* flags */
+	pkey_paillier_init,	/* init */
+	pkey_paillier_copy,	/* copy */
+	pkey_paillier_cleanup,	/* cleanup */
+	NULL,			/* paramgen_init */
+	NULL,			/* paramgen */
+	NULL,			/* keygen_init */
+	pkey_paillier_keygen,	/* keygen */
+	NULL,			/* sign_init */
+	NULL,			/* sign */
+	NULL,			/* verify_init */
+	NULL,			/* verify */
+	NULL,			/* verify_recover_init */
+	NULL,			/* verify_recover */
+	NULL,			/* signctx_init */
+	NULL,			/* signctx */
+	NULL,			/* verifyctx_init */
+	NULL,			/* verifyctx */
+	NULL,			/* encrypt_init */
+	pkey_paillier_encrypt,	/* encrypt */
+	NULL,			/* decrypt_init */
+	pkey_paillier_decrypt,	/* decrypt */
+	NULL,			/* derive_init */
+	NULL,			/* derive */
+	pkey_paillier_ctrl,	/* ctrl */
+	pkey_paillier_ctrl_str	/* ctrl_str */
+};
diff --git a/crypto/pariscid.pl b/crypto/pariscid.pl
new file mode 100644
index 000000000..f82e27ac4
--- /dev/null
+++ b/crypto/pariscid.pl
@@ -0,0 +1,263 @@
+#! /usr/bin/env perl
+# Copyright 2009-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+
+$flavour = shift;
+$output = shift;
+open STDOUT,">$output";
+
+if ($flavour =~ /64/) {
+	$LEVEL		="2.0W";
+	$SIZE_T		=8;
+	$ST		="std";
+} else {
+	$LEVEL		="1.1";
+	$SIZE_T		=4;
+	$ST		="stw";
+}
+
+$rp="%r2";
+$sp="%r30";
+$rv="%r28";
+
+$code=<<___;
+	.LEVEL	$LEVEL
+	.SPACE	\$TEXT\$
+	.SUBSPA	\$CODE\$,QUAD=0,ALIGN=8,ACCESS=0x2C,CODE_ONLY
+
+	.EXPORT	OPENSSL_cpuid_setup,ENTRY
+	.ALIGN	8
+OPENSSL_cpuid_setup
+	.PROC
+	.CALLINFO	NO_CALLS
+	.ENTRY
+	bv	($rp)
+	.EXIT
+	nop
+	.PROCEND
+
+	.EXPORT	OPENSSL_rdtsc,ENTRY
+	.ALIGN	8
+OPENSSL_rdtsc
+	.PROC
+	.CALLINFO	NO_CALLS
+	.ENTRY
+	mfctl	%cr16,$rv
+	bv	($rp)
+	.EXIT
+	nop
+	.PROCEND
+
+	.EXPORT	OPENSSL_wipe_cpu,ENTRY
+	.ALIGN	8
+OPENSSL_wipe_cpu
+	.PROC
+	.CALLINFO	NO_CALLS
+	.ENTRY
+	xor		%r0,%r0,%r1
+	fcpy,dbl	%fr0,%fr4
+	xor		%r0,%r0,%r19
+	fcpy,dbl	%fr0,%fr5
+	xor		%r0,%r0,%r20
+	fcpy,dbl	%fr0,%fr6
+	xor		%r0,%r0,%r21
+	fcpy,dbl	%fr0,%fr7
+	xor		%r0,%r0,%r22
+	fcpy,dbl	%fr0,%fr8
+	xor		%r0,%r0,%r23
+	fcpy,dbl	%fr0,%fr9
+	xor		%r0,%r0,%r24
+	fcpy,dbl	%fr0,%fr10
+	xor		%r0,%r0,%r25
+	fcpy,dbl	%fr0,%fr11
+	xor		%r0,%r0,%r26
+	fcpy,dbl	%fr0,%fr22
+	xor		%r0,%r0,%r29
+	fcpy,dbl	%fr0,%fr23
+	xor		%r0,%r0,%r31
+	fcpy,dbl	%fr0,%fr24
+	fcpy,dbl	%fr0,%fr25
+	fcpy,dbl	%fr0,%fr26
+	fcpy,dbl	%fr0,%fr27
+	fcpy,dbl	%fr0,%fr28
+	fcpy,dbl	%fr0,%fr29
+	fcpy,dbl	%fr0,%fr30
+	fcpy,dbl	%fr0,%fr31
+	bv		($rp)
+	.EXIT
+	ldo		0($sp),$rv
+	.PROCEND
+___
+{
+my $inp="%r26";
+my $len="%r25";
+
+$code.=<<___;
+	.EXPORT	OPENSSL_cleanse,ENTRY,ARGW0=GR,ARGW1=GR
+	.ALIGN	8
+OPENSSL_cleanse
+	.PROC
+	.CALLINFO	NO_CALLS
+	.ENTRY
+	cmpib,*=	0,$len,L\$done
+	nop
+	cmpib,*>>=	15,$len,L\$ittle
+	ldi		$SIZE_T-1,%r1
+
+L\$align
+	and,*<>		$inp,%r1,%r28
+	b,n		L\$aligned
+	stb		%r0,0($inp)
+	ldo		-1($len),$len
+	b		L\$align
+	ldo		1($inp),$inp
+
+L\$aligned
+	andcm		$len,%r1,%r28
+L\$ot
+	$ST		%r0,0($inp)
+	addib,*<>	-$SIZE_T,%r28,L\$ot
+	ldo		$SIZE_T($inp),$inp
+
+	and,*<>		$len,%r1,$len
+	b,n		L\$done
+L\$ittle
+	stb		%r0,0($inp)
+	addib,*<>	-1,$len,L\$ittle
+	ldo		1($inp),$inp
+L\$done
+	bv		($rp)
+	.EXIT
+	nop
+	.PROCEND
+___
+}
+{
+my ($in1,$in2,$len)=("%r26","%r25","%r24");
+
+$code.=<<___;
+	.EXPORT	CRYPTO_memcmp,ENTRY,ARGW0=GR,ARGW1=GR,ARGW1=GR
+	.ALIGN	8
+CRYPTO_memcmp
+	.PROC
+	.CALLINFO	NO_CALLS
+	.ENTRY
+	cmpib,*=	0,$len,L\$no_data
+	xor		$rv,$rv,$rv
+
+L\$oop_cmp
+	ldb		0($in1),%r19
+	ldb		0($in2),%r20
+	ldo		1($in1),$in1
+	ldo		1($in2),$in2
+	xor		%r19,%r20,%r29
+	addib,*<>	-1,$len,L\$oop_cmp
+	or		%r29,$rv,$rv
+
+	sub		%r0,$rv,%r29
+	extru		%r29,31,1,$rv
+L\$no_data
+	bv		($rp)
+	.EXIT
+	nop
+	.PROCEND
+___
+}
+{
+my ($out,$cnt,$max)=("%r26","%r25","%r24");
+my ($tick,$lasttick)=("%r23","%r22");
+my ($diff,$lastdiff)=("%r21","%r20");
+
+$code.=<<___;
+	.EXPORT	OPENSSL_instrument_bus,ENTRY,ARGW0=GR,ARGW1=GR
+	.ALIGN	8
+OPENSSL_instrument_bus
+	.PROC
+	.CALLINFO	NO_CALLS
+	.ENTRY
+	copy		$cnt,$rv
+	mfctl		%cr16,$tick
+	copy		$tick,$lasttick
+	ldi		0,$diff
+
+	fdc		0($out)
+	ldw		0($out),$tick
+	add		$diff,$tick,$tick
+	stw		$tick,0($out)
+L\$oop
+	mfctl		%cr16,$tick
+	sub		$tick,$lasttick,$diff
+	copy		$tick,$lasttick
+
+	fdc		0($out)
+	ldw		0($out),$tick
+	add		$diff,$tick,$tick
+	stw		$tick,0($out)
+
+	addib,<>	-1,$cnt,L\$oop
+	addi		4,$out,$out
+
+	bv		($rp)
+	.EXIT
+	sub		$rv,$cnt,$rv
+	.PROCEND
+
+	.EXPORT	OPENSSL_instrument_bus2,ENTRY,ARGW0=GR,ARGW1=GR
+	.ALIGN	8
+OPENSSL_instrument_bus2
+	.PROC
+	.CALLINFO	NO_CALLS
+	.ENTRY
+	copy		$cnt,$rv
+	sub		%r0,$cnt,$cnt
+
+	mfctl		%cr16,$tick
+	copy		$tick,$lasttick
+	ldi		0,$diff
+
+	fdc		0($out)
+	ldw		0($out),$tick
+	add		$diff,$tick,$tick
+	stw		$tick,0($out)
+
+	mfctl		%cr16,$tick
+	sub		$tick,$lasttick,$diff
+	copy		$tick,$lasttick
+L\$oop2
+	copy		$diff,$lastdiff
+	fdc		0($out)
+	ldw		0($out),$tick
+	add		$diff,$tick,$tick
+	stw		$tick,0($out)
+
+	addib,=		-1,$max,L\$done2
+	nop
+
+	mfctl		%cr16,$tick
+	sub		$tick,$lasttick,$diff
+	copy		$tick,$lasttick
+	cmpclr,<>	$lastdiff,$diff,$tick
+	ldi		1,$tick
+
+	ldi		1,%r1
+	xor		%r1,$tick,$tick
+	addb,<>		$tick,$cnt,L\$oop2
+	shladd,l	$tick,2,$out,$out
+L\$done2
+	bv		($rp)
+	.EXIT
+	add		$rv,$cnt,$rv
+	.PROCEND
+___
+}
+$code =~ s/cmpib,\*/comib,/gm	if ($SIZE_T==4);
+$code =~ s/,\*/,/gm		if ($SIZE_T==4);
+$code =~ s/\bbv\b/bve/gm	if ($SIZE_T==8);
+print $code;
+close STDOUT;
+
diff --git a/crypto/pem/build.info b/crypto/pem/build.info
new file mode 100644
index 000000000..bd79026e5
--- /dev/null
+++ b/crypto/pem/build.info
@@ -0,0 +1,5 @@
+LIBS=../../libcrypto
+SOURCE[../../libcrypto]=\
+        pem_sign.c pem_info.c pem_lib.c pem_all.c pem_err.c \
+        pem_x509.c pem_xaux.c pem_oth.c pem_pk8.c pem_pkey.c pvkfmt.c \
+	pem3.c
diff --git a/crypto/pem/pem3.c b/crypto/pem/pem3.c
new file mode 100644
index 000000000..91243dadb
--- /dev/null
+++ b/crypto/pem/pem3.c
@@ -0,0 +1,192 @@
+/* ====================================================================
+ * Copyright (c) 2014 - 2017 The GmSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the GmSSL Project.
+ *    (http://gmssl.org/)"
+ *
+ * 4. The name "GmSSL Project" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    guanzhi1980@gmail.com.
+ *
+ * 5. Products derived from this software may not be called "GmSSL"
+ *    nor may "GmSSL" appear in their names without prior written
+ *    permission of the GmSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the GmSSL Project
+ *    (http://gmssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE GmSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE GmSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+#include 
+#ifndef OPENSSL_NO_PKCS7
+# include 
+#endif
+#include 
+#ifndef OPENSSL_NO_PAILLIER
+# include 
+#endif
+#ifndef OPENSSL_NO_SM9
+# include 
+#endif
+
+
+#ifndef OPENSSL_NO_PAILLIER
+static PAILLIER *pkey_get_paillier(EVP_PKEY *key, PAILLIER **paillier)
+{
+	PAILLIER *rtmp;
+	if (!key)
+		return NULL;
+	rtmp = EVP_PKEY_get1_PAILLIER(key);
+	EVP_PKEY_free(key);
+	if (!rtmp)
+		return NULL;
+	if (paillier) {
+		PAILLIER_free(*paillier);
+		*paillier = rtmp;
+	}
+	return rtmp;
+}
+
+PAILLIER *PEM_read_bio_PaillierPrivateKey(BIO *bp, PAILLIER **paillier,
+	pem_password_cb *cb, void *u)
+{
+	EVP_PKEY *pktmp;
+	pktmp = PEM_read_bio_PrivateKey(bp, NULL, cb, u);
+	return pkey_get_paillier(pktmp, paillier);
+}
+
+# ifndef OPENSSL_NO_STDIO
+PAILLIER *PEM_read_PaillierPrivateKey(FILE *fp, PAILLIER **paillier,
+	pem_password_cb *cb, void *u)
+{
+	EVP_PKEY *pktmp;
+	pktmp = PEM_read_PrivateKey(fp, NULL, cb, u);
+	return pkey_get_paillier(pktmp, paillier);
+}
+
+# endif
+
+IMPLEMENT_PEM_write_cb_const(PaillierPrivateKey, PAILLIER, PEM_STRING_PAILLIER,
+	PaillierPrivateKey)
+IMPLEMENT_PEM_rw_const(PaillierPublicKey, PAILLIER, PEM_STRING_PAILLIER_PUBLIC,
+	PaillierPublicKey)
+IMPLEMENT_PEM_rw(PAILLIER_PUBKEY, PAILLIER, PEM_STRING_PUBLIC, PAILLIER_PUBKEY)
+
+#endif /* OPENSSL_NO_PAILLIER */
+
+#ifndef OPENSSL_NO_SM9
+static SM9_MASTER_KEY *pkey_get_sm9_master(EVP_PKEY *key, SM9_MASTER_KEY **sm9_master)
+{
+	SM9_MASTER_KEY *rtmp;
+	if (!key)
+		return NULL;
+	rtmp = EVP_PKEY_get1_SM9_MASTER(key);
+	EVP_PKEY_free(key);
+	if (!rtmp)
+		return NULL;
+	if (sm9_master) {
+		SM9_MASTER_KEY_free(*sm9_master);
+		*sm9_master = rtmp;
+	}
+	return rtmp;
+}
+
+SM9_MASTER_KEY *PEM_read_bio_SM9MasterSecret(BIO *bp, SM9_MASTER_KEY **sm9_master,
+	pem_password_cb *cb, void *u)
+{
+	EVP_PKEY *pktmp;
+	pktmp = PEM_read_bio_PrivateKey(bp, NULL, cb, u);
+	return pkey_get_sm9_master(pktmp, sm9_master);
+}
+
+static SM9_KEY *pkey_get_sm9(EVP_PKEY *key, SM9_KEY **sm9)
+{
+	SM9_KEY *rtmp;
+	if (!key)
+		return NULL;
+	rtmp = EVP_PKEY_get1_SM9(key);
+	EVP_PKEY_free(key);
+	if (!rtmp)
+		return NULL;
+	if (sm9) {
+		SM9_KEY_free(*sm9);
+		*sm9 = rtmp;
+	}
+	return rtmp;
+}
+
+SM9_KEY *PEM_read_bio_SM9PrivateKey(BIO *bp, SM9_KEY **sm9,
+	pem_password_cb *cb, void *u)
+{
+	EVP_PKEY *pktmp;
+	pktmp = PEM_read_bio_PrivateKey(bp, NULL, cb, u);
+	return pkey_get_sm9(pktmp, sm9);
+}
+
+# ifndef OPENSSL_NO_STDIO
+SM9_MASTER_KEY *PEM_read_SM9MasterSecret(FILE *fp, SM9_MASTER_KEY **sm9_master,
+	pem_password_cb *cb, void *u)
+{
+	EVP_PKEY *pktmp;
+	pktmp = PEM_read_PrivateKey(fp, NULL, cb, u);
+	return pkey_get_sm9_master(pktmp, sm9_master);
+}
+
+SM9_KEY *PEM_read_SM9PrivateKey(FILE *fp, SM9_KEY **sm9,
+	pem_password_cb *cb, void *u)
+{
+	EVP_PKEY *pktmp;
+	pktmp = PEM_read_PrivateKey(fp, NULL, cb, u);
+	return pkey_get_sm9(pktmp, sm9);
+}
+# endif
+
+IMPLEMENT_PEM_write_cb_const(SM9MasterSecret, SM9_MASTER_KEY,
+	PEM_STRING_SM9_MASTER, SM9MasterSecret)
+IMPLEMENT_PEM_rw_const(SM9PublicParameters, SM9_MASTER_KEY,
+	PEM_STRING_SM9_MASTER_PUBLIC, SM9PublicParameters)
+IMPLEMENT_PEM_rw(SM9_MASTER_PUBKEY, SM9_MASTER_KEY,
+	PEM_STRING_PUBLIC, SM9_MASTER_PUBKEY)
+
+IMPLEMENT_PEM_write_cb_const(SM9PrivateKey, SM9_KEY,
+	PEM_STRING_SM9, SM9PrivateKey)
+IMPLEMENT_PEM_rw_const(SM9PublicKey, SM9_KEY,
+	PEM_STRING_SM9_PUBLIC, SM9PublicKey)
+IMPLEMENT_PEM_rw(SM9_PUBKEY, SM9_KEY,
+	PEM_STRING_PUBLIC, SM9_PUBKEY)
+
+#endif /* OPENSSL_NO_SM9 */
diff --git a/crypto/pem/pem_all.c b/crypto/pem/pem_all.c
new file mode 100644
index 000000000..cc5605b58
--- /dev/null
+++ b/crypto/pem/pem_all.c
@@ -0,0 +1,187 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+#include 
+#include 
+#include 
+#ifndef OPENSSL_NO_RSA
+# include 
+#endif
+#ifndef OPENSSL_NO_DSA
+# include 
+#endif
+#ifndef OPENSSL_NO_DH
+# include 
+#endif
+
+#ifndef OPENSSL_NO_RSA
+static RSA *pkey_get_rsa(EVP_PKEY *key, RSA **rsa);
+#endif
+#ifndef OPENSSL_NO_DSA
+static DSA *pkey_get_dsa(EVP_PKEY *key, DSA **dsa);
+#endif
+
+#ifndef OPENSSL_NO_EC
+static EC_KEY *pkey_get_eckey(EVP_PKEY *key, EC_KEY **eckey);
+#endif
+
+IMPLEMENT_PEM_rw(X509_REQ, X509_REQ, PEM_STRING_X509_REQ, X509_REQ)
+
+IMPLEMENT_PEM_write(X509_REQ_NEW, X509_REQ, PEM_STRING_X509_REQ_OLD, X509_REQ)
+IMPLEMENT_PEM_rw(X509_CRL, X509_CRL, PEM_STRING_X509_CRL, X509_CRL)
+IMPLEMENT_PEM_rw(PKCS7, PKCS7, PEM_STRING_PKCS7, PKCS7)
+
+IMPLEMENT_PEM_rw(NETSCAPE_CERT_SEQUENCE, NETSCAPE_CERT_SEQUENCE,
+                 PEM_STRING_X509, NETSCAPE_CERT_SEQUENCE)
+#ifndef OPENSSL_NO_RSA
+/*
+ * We treat RSA or DSA private keys as a special case. For private keys we
+ * read in an EVP_PKEY structure with PEM_read_bio_PrivateKey() and extract
+ * the relevant private key: this means can handle "traditional" and PKCS#8
+ * formats transparently.
+ */
+static RSA *pkey_get_rsa(EVP_PKEY *key, RSA **rsa)
+{
+    RSA *rtmp;
+    if (!key)
+        return NULL;
+    rtmp = EVP_PKEY_get1_RSA(key);
+    EVP_PKEY_free(key);
+    if (!rtmp)
+        return NULL;
+    if (rsa) {
+        RSA_free(*rsa);
+        *rsa = rtmp;
+    }
+    return rtmp;
+}
+
+RSA *PEM_read_bio_RSAPrivateKey(BIO *bp, RSA **rsa, pem_password_cb *cb,
+                                void *u)
+{
+    EVP_PKEY *pktmp;
+    pktmp = PEM_read_bio_PrivateKey(bp, NULL, cb, u);
+    return pkey_get_rsa(pktmp, rsa);
+}
+
+# ifndef OPENSSL_NO_STDIO
+
+RSA *PEM_read_RSAPrivateKey(FILE *fp, RSA **rsa, pem_password_cb *cb, void *u)
+{
+    EVP_PKEY *pktmp;
+    pktmp = PEM_read_PrivateKey(fp, NULL, cb, u);
+    return pkey_get_rsa(pktmp, rsa);
+}
+
+# endif
+
+IMPLEMENT_PEM_write_cb_const(RSAPrivateKey, RSA, PEM_STRING_RSA,
+                             RSAPrivateKey)
+
+
+IMPLEMENT_PEM_rw_const(RSAPublicKey, RSA, PEM_STRING_RSA_PUBLIC,
+                       RSAPublicKey) IMPLEMENT_PEM_rw(RSA_PUBKEY, RSA,
+                                                      PEM_STRING_PUBLIC,
+                                                      RSA_PUBKEY)
+#endif
+#ifndef OPENSSL_NO_DSA
+static DSA *pkey_get_dsa(EVP_PKEY *key, DSA **dsa)
+{
+    DSA *dtmp;
+    if (!key)
+        return NULL;
+    dtmp = EVP_PKEY_get1_DSA(key);
+    EVP_PKEY_free(key);
+    if (!dtmp)
+        return NULL;
+    if (dsa) {
+        DSA_free(*dsa);
+        *dsa = dtmp;
+    }
+    return dtmp;
+}
+
+DSA *PEM_read_bio_DSAPrivateKey(BIO *bp, DSA **dsa, pem_password_cb *cb,
+                                void *u)
+{
+    EVP_PKEY *pktmp;
+    pktmp = PEM_read_bio_PrivateKey(bp, NULL, cb, u);
+    return pkey_get_dsa(pktmp, dsa); /* will free pktmp */
+}
+
+IMPLEMENT_PEM_write_cb_const(DSAPrivateKey, DSA, PEM_STRING_DSA,
+                             DSAPrivateKey)
+    IMPLEMENT_PEM_rw(DSA_PUBKEY, DSA, PEM_STRING_PUBLIC, DSA_PUBKEY)
+# ifndef OPENSSL_NO_STDIO
+DSA *PEM_read_DSAPrivateKey(FILE *fp, DSA **dsa, pem_password_cb *cb, void *u)
+{
+    EVP_PKEY *pktmp;
+    pktmp = PEM_read_PrivateKey(fp, NULL, cb, u);
+    return pkey_get_dsa(pktmp, dsa); /* will free pktmp */
+}
+
+# endif
+
+IMPLEMENT_PEM_rw_const(DSAparams, DSA, PEM_STRING_DSAPARAMS, DSAparams)
+#endif
+#ifndef OPENSSL_NO_EC
+static EC_KEY *pkey_get_eckey(EVP_PKEY *key, EC_KEY **eckey)
+{
+    EC_KEY *dtmp;
+    if (!key)
+        return NULL;
+    dtmp = EVP_PKEY_get1_EC_KEY(key);
+    EVP_PKEY_free(key);
+    if (!dtmp)
+        return NULL;
+    if (eckey) {
+        EC_KEY_free(*eckey);
+        *eckey = dtmp;
+    }
+    return dtmp;
+}
+
+EC_KEY *PEM_read_bio_ECPrivateKey(BIO *bp, EC_KEY **key, pem_password_cb *cb,
+                                  void *u)
+{
+    EVP_PKEY *pktmp;
+    pktmp = PEM_read_bio_PrivateKey(bp, NULL, cb, u);
+    return pkey_get_eckey(pktmp, key); /* will free pktmp */
+}
+
+IMPLEMENT_PEM_rw_const(ECPKParameters, EC_GROUP, PEM_STRING_ECPARAMETERS,
+                       ECPKParameters)
+
+
+IMPLEMENT_PEM_write_cb(ECPrivateKey, EC_KEY, PEM_STRING_ECPRIVATEKEY,
+                       ECPrivateKey)
+IMPLEMENT_PEM_rw(EC_PUBKEY, EC_KEY, PEM_STRING_PUBLIC, EC_PUBKEY)
+# ifndef OPENSSL_NO_STDIO
+EC_KEY *PEM_read_ECPrivateKey(FILE *fp, EC_KEY **eckey, pem_password_cb *cb,
+                              void *u)
+{
+    EVP_PKEY *pktmp;
+    pktmp = PEM_read_PrivateKey(fp, NULL, cb, u);
+    return pkey_get_eckey(pktmp, eckey); /* will free pktmp */
+}
+
+# endif
+
+#endif
+
+#ifndef OPENSSL_NO_DH
+
+IMPLEMENT_PEM_write_const(DHparams, DH, PEM_STRING_DHPARAMS, DHparams)
+    IMPLEMENT_PEM_write_const(DHxparams, DH, PEM_STRING_DHXPARAMS, DHxparams)
+#endif
+IMPLEMENT_PEM_rw(PUBKEY, EVP_PKEY, PEM_STRING_PUBLIC, PUBKEY)
diff --git a/crypto/pem/pem_err.c b/crypto/pem/pem_err.c
new file mode 100644
index 000000000..f36d89324
--- /dev/null
+++ b/crypto/pem/pem_err.c
@@ -0,0 +1,115 @@
+/*
+ * Generated by util/mkerr.pl DO NOT EDIT
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include 
+
+/* BEGIN ERROR CODES */
+#ifndef OPENSSL_NO_ERR
+
+# define ERR_FUNC(func) ERR_PACK(ERR_LIB_PEM,func,0)
+# define ERR_REASON(reason) ERR_PACK(ERR_LIB_PEM,0,reason)
+
+static ERR_STRING_DATA PEM_str_functs[] = {
+    {ERR_FUNC(PEM_F_B2I_DSS), "b2i_dss"},
+    {ERR_FUNC(PEM_F_B2I_PVK_BIO), "b2i_PVK_bio"},
+    {ERR_FUNC(PEM_F_B2I_RSA), "b2i_rsa"},
+    {ERR_FUNC(PEM_F_CHECK_BITLEN_DSA), "check_bitlen_dsa"},
+    {ERR_FUNC(PEM_F_CHECK_BITLEN_RSA), "check_bitlen_rsa"},
+    {ERR_FUNC(PEM_F_D2I_PKCS8PRIVATEKEY_BIO), "d2i_PKCS8PrivateKey_bio"},
+    {ERR_FUNC(PEM_F_D2I_PKCS8PRIVATEKEY_FP), "d2i_PKCS8PrivateKey_fp"},
+    {ERR_FUNC(PEM_F_DO_B2I), "do_b2i"},
+    {ERR_FUNC(PEM_F_DO_B2I_BIO), "do_b2i_bio"},
+    {ERR_FUNC(PEM_F_DO_BLOB_HEADER), "do_blob_header"},
+    {ERR_FUNC(PEM_F_DO_PK8PKEY), "do_pk8pkey"},
+    {ERR_FUNC(PEM_F_DO_PK8PKEY_FP), "do_pk8pkey_fp"},
+    {ERR_FUNC(PEM_F_DO_PVK_BODY), "do_PVK_body"},
+    {ERR_FUNC(PEM_F_DO_PVK_HEADER), "do_PVK_header"},
+    {ERR_FUNC(PEM_F_I2B_PVK), "i2b_PVK"},
+    {ERR_FUNC(PEM_F_I2B_PVK_BIO), "i2b_PVK_bio"},
+    {ERR_FUNC(PEM_F_LOAD_IV), "load_iv"},
+    {ERR_FUNC(PEM_F_PEM_ASN1_READ), "PEM_ASN1_read"},
+    {ERR_FUNC(PEM_F_PEM_ASN1_READ_BIO), "PEM_ASN1_read_bio"},
+    {ERR_FUNC(PEM_F_PEM_ASN1_WRITE), "PEM_ASN1_write"},
+    {ERR_FUNC(PEM_F_PEM_ASN1_WRITE_BIO), "PEM_ASN1_write_bio"},
+    {ERR_FUNC(PEM_F_PEM_DEF_CALLBACK), "PEM_def_callback"},
+    {ERR_FUNC(PEM_F_PEM_DO_HEADER), "PEM_do_header"},
+    {ERR_FUNC(PEM_F_PEM_GET_EVP_CIPHER_INFO), "PEM_get_EVP_CIPHER_INFO"},
+    {ERR_FUNC(PEM_F_PEM_READ), "PEM_read"},
+    {ERR_FUNC(PEM_F_PEM_READ_BIO), "PEM_read_bio"},
+    {ERR_FUNC(PEM_F_PEM_READ_BIO_DHPARAMS), "PEM_read_bio_DHparams"},
+    {ERR_FUNC(PEM_F_PEM_READ_BIO_PARAMETERS), "PEM_read_bio_Parameters"},
+    {ERR_FUNC(PEM_F_PEM_READ_BIO_PRIVATEKEY), "PEM_read_bio_PrivateKey"},
+    {ERR_FUNC(PEM_F_PEM_READ_DHPARAMS), "PEM_read_DHparams"},
+    {ERR_FUNC(PEM_F_PEM_READ_PRIVATEKEY), "PEM_read_PrivateKey"},
+    {ERR_FUNC(PEM_F_PEM_SIGNFINAL), "PEM_SignFinal"},
+    {ERR_FUNC(PEM_F_PEM_WRITE), "PEM_write"},
+    {ERR_FUNC(PEM_F_PEM_WRITE_BIO), "PEM_write_bio"},
+    {ERR_FUNC(PEM_F_PEM_WRITE_PRIVATEKEY), "PEM_write_PrivateKey"},
+    {ERR_FUNC(PEM_F_PEM_X509_INFO_READ), "PEM_X509_INFO_read"},
+    {ERR_FUNC(PEM_F_PEM_X509_INFO_READ_BIO), "PEM_X509_INFO_read_bio"},
+    {ERR_FUNC(PEM_F_PEM_X509_INFO_WRITE_BIO), "PEM_X509_INFO_write_bio"},
+    {0, NULL}
+};
+
+static ERR_STRING_DATA PEM_str_reasons[] = {
+    {ERR_REASON(PEM_R_BAD_BASE64_DECODE), "bad base64 decode"},
+    {ERR_REASON(PEM_R_BAD_DECRYPT), "bad decrypt"},
+    {ERR_REASON(PEM_R_BAD_END_LINE), "bad end line"},
+    {ERR_REASON(PEM_R_BAD_IV_CHARS), "bad iv chars"},
+    {ERR_REASON(PEM_R_BAD_MAGIC_NUMBER), "bad magic number"},
+    {ERR_REASON(PEM_R_BAD_PASSWORD_READ), "bad password read"},
+    {ERR_REASON(PEM_R_BAD_VERSION_NUMBER), "bad version number"},
+    {ERR_REASON(PEM_R_BIO_WRITE_FAILURE), "bio write failure"},
+    {ERR_REASON(PEM_R_CIPHER_IS_NULL), "cipher is null"},
+    {ERR_REASON(PEM_R_ERROR_CONVERTING_PRIVATE_KEY),
+     "error converting private key"},
+    {ERR_REASON(PEM_R_EXPECTING_PRIVATE_KEY_BLOB),
+     "expecting private key blob"},
+    {ERR_REASON(PEM_R_EXPECTING_PUBLIC_KEY_BLOB),
+     "expecting public key blob"},
+    {ERR_REASON(PEM_R_HEADER_TOO_LONG), "header too long"},
+    {ERR_REASON(PEM_R_INCONSISTENT_HEADER), "inconsistent header"},
+    {ERR_REASON(PEM_R_KEYBLOB_HEADER_PARSE_ERROR),
+     "keyblob header parse error"},
+    {ERR_REASON(PEM_R_KEYBLOB_TOO_SHORT), "keyblob too short"},
+    {ERR_REASON(PEM_R_MISSING_DEK_IV), "missing dek iv"},
+    {ERR_REASON(PEM_R_NOT_DEK_INFO), "not dek info"},
+    {ERR_REASON(PEM_R_NOT_ENCRYPTED), "not encrypted"},
+    {ERR_REASON(PEM_R_NOT_PROC_TYPE), "not proc type"},
+    {ERR_REASON(PEM_R_NO_START_LINE), "no start line"},
+    {ERR_REASON(PEM_R_PROBLEMS_GETTING_PASSWORD),
+     "problems getting password"},
+    {ERR_REASON(PEM_R_PVK_DATA_TOO_SHORT), "pvk data too short"},
+    {ERR_REASON(PEM_R_PVK_TOO_SHORT), "pvk too short"},
+    {ERR_REASON(PEM_R_READ_KEY), "read key"},
+    {ERR_REASON(PEM_R_SHORT_HEADER), "short header"},
+    {ERR_REASON(PEM_R_UNEXPECTED_DEK_IV), "unexpected dek iv"},
+    {ERR_REASON(PEM_R_UNSUPPORTED_CIPHER), "unsupported cipher"},
+    {ERR_REASON(PEM_R_UNSUPPORTED_ENCRYPTION), "unsupported encryption"},
+    {ERR_REASON(PEM_R_UNSUPPORTED_KEY_COMPONENTS),
+     "unsupported key components"},
+    {0, NULL}
+};
+
+#endif
+
+int ERR_load_PEM_strings(void)
+{
+#ifndef OPENSSL_NO_ERR
+
+    if (ERR_func_error_string(PEM_str_functs[0].error) == NULL) {
+        ERR_load_strings(0, PEM_str_functs);
+        ERR_load_strings(0, PEM_str_reasons);
+    }
+#endif
+    return 1;
+}
diff --git a/crypto/pem/pem_info.c b/crypto/pem/pem_info.c
new file mode 100644
index 000000000..93269f1ee
--- /dev/null
+++ b/crypto/pem/pem_info.c
@@ -0,0 +1,338 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+#include 
+#include 
+#include 
+#ifndef OPENSSL_NO_RSA
+# include 
+#endif
+#ifndef OPENSSL_NO_DSA
+# include 
+#endif
+
+#ifndef OPENSSL_NO_STDIO
+STACK_OF(X509_INFO) *PEM_X509_INFO_read(FILE *fp, STACK_OF(X509_INFO) *sk,
+                                        pem_password_cb *cb, void *u)
+{
+    BIO *b;
+    STACK_OF(X509_INFO) *ret;
+
+    if ((b = BIO_new(BIO_s_file())) == NULL) {
+        PEMerr(PEM_F_PEM_X509_INFO_READ, ERR_R_BUF_LIB);
+        return (0);
+    }
+    BIO_set_fp(b, fp, BIO_NOCLOSE);
+    ret = PEM_X509_INFO_read_bio(b, sk, cb, u);
+    BIO_free(b);
+    return (ret);
+}
+#endif
+
+STACK_OF(X509_INFO) *PEM_X509_INFO_read_bio(BIO *bp, STACK_OF(X509_INFO) *sk,
+                                            pem_password_cb *cb, void *u)
+{
+    X509_INFO *xi = NULL;
+    char *name = NULL, *header = NULL;
+    void *pp;
+    unsigned char *data = NULL;
+    const unsigned char *p;
+    long len, error = 0;
+    int ok = 0;
+    STACK_OF(X509_INFO) *ret = NULL;
+    unsigned int i, raw, ptype;
+    d2i_of_void *d2i = 0;
+
+    if (sk == NULL) {
+        if ((ret = sk_X509_INFO_new_null()) == NULL) {
+            PEMerr(PEM_F_PEM_X509_INFO_READ_BIO, ERR_R_MALLOC_FAILURE);
+            goto err;
+        }
+    } else
+        ret = sk;
+
+    if ((xi = X509_INFO_new()) == NULL)
+        goto err;
+    for (;;) {
+        raw = 0;
+        ptype = 0;
+        i = PEM_read_bio(bp, &name, &header, &data, &len);
+        if (i == 0) {
+            error = ERR_GET_REASON(ERR_peek_last_error());
+            if (error == PEM_R_NO_START_LINE) {
+                ERR_clear_error();
+                break;
+            }
+            goto err;
+        }
+ start:
+        if ((strcmp(name, PEM_STRING_X509) == 0) ||
+            (strcmp(name, PEM_STRING_X509_OLD) == 0)) {
+            d2i = (D2I_OF(void)) d2i_X509;
+            if (xi->x509 != NULL) {
+                if (!sk_X509_INFO_push(ret, xi))
+                    goto err;
+                if ((xi = X509_INFO_new()) == NULL)
+                    goto err;
+                goto start;
+            }
+            pp = &(xi->x509);
+        } else if ((strcmp(name, PEM_STRING_X509_TRUSTED) == 0)) {
+            d2i = (D2I_OF(void)) d2i_X509_AUX;
+            if (xi->x509 != NULL) {
+                if (!sk_X509_INFO_push(ret, xi))
+                    goto err;
+                if ((xi = X509_INFO_new()) == NULL)
+                    goto err;
+                goto start;
+            }
+            pp = &(xi->x509);
+        } else if (strcmp(name, PEM_STRING_X509_CRL) == 0) {
+            d2i = (D2I_OF(void)) d2i_X509_CRL;
+            if (xi->crl != NULL) {
+                if (!sk_X509_INFO_push(ret, xi))
+                    goto err;
+                if ((xi = X509_INFO_new()) == NULL)
+                    goto err;
+                goto start;
+            }
+            pp = &(xi->crl);
+        } else
+#ifndef OPENSSL_NO_RSA
+        if (strcmp(name, PEM_STRING_RSA) == 0) {
+            d2i = (D2I_OF(void)) d2i_RSAPrivateKey;
+            if (xi->x_pkey != NULL) {
+                if (!sk_X509_INFO_push(ret, xi))
+                    goto err;
+                if ((xi = X509_INFO_new()) == NULL)
+                    goto err;
+                goto start;
+            }
+
+            xi->enc_data = NULL;
+            xi->enc_len = 0;
+
+            xi->x_pkey = X509_PKEY_new();
+            if (xi->x_pkey == NULL)
+                goto err;
+            ptype = EVP_PKEY_RSA;
+            pp = &xi->x_pkey->dec_pkey;
+            if ((int)strlen(header) > 10) /* assume encrypted */
+                raw = 1;
+        } else
+#endif
+#ifndef OPENSSL_NO_DSA
+        if (strcmp(name, PEM_STRING_DSA) == 0) {
+            d2i = (D2I_OF(void)) d2i_DSAPrivateKey;
+            if (xi->x_pkey != NULL) {
+                if (!sk_X509_INFO_push(ret, xi))
+                    goto err;
+                if ((xi = X509_INFO_new()) == NULL)
+                    goto err;
+                goto start;
+            }
+
+            xi->enc_data = NULL;
+            xi->enc_len = 0;
+
+            xi->x_pkey = X509_PKEY_new();
+            if (xi->x_pkey == NULL)
+                goto err;
+            ptype = EVP_PKEY_DSA;
+            pp = &xi->x_pkey->dec_pkey;
+            if ((int)strlen(header) > 10) /* assume encrypted */
+                raw = 1;
+        } else
+#endif
+#ifndef OPENSSL_NO_EC
+        if (strcmp(name, PEM_STRING_ECPRIVATEKEY) == 0) {
+            d2i = (D2I_OF(void)) d2i_ECPrivateKey;
+            if (xi->x_pkey != NULL) {
+                if (!sk_X509_INFO_push(ret, xi))
+                    goto err;
+                if ((xi = X509_INFO_new()) == NULL)
+                    goto err;
+                goto start;
+            }
+
+            xi->enc_data = NULL;
+            xi->enc_len = 0;
+
+            xi->x_pkey = X509_PKEY_new();
+            if (xi->x_pkey == NULL)
+                goto err;
+            ptype = EVP_PKEY_EC;
+            pp = &xi->x_pkey->dec_pkey;
+            if ((int)strlen(header) > 10) /* assume encrypted */
+                raw = 1;
+        } else
+#endif
+        {
+            d2i = NULL;
+            pp = NULL;
+        }
+
+        if (d2i != NULL) {
+            if (!raw) {
+                EVP_CIPHER_INFO cipher;
+
+                if (!PEM_get_EVP_CIPHER_INFO(header, &cipher))
+                    goto err;
+                if (!PEM_do_header(&cipher, data, &len, cb, u))
+                    goto err;
+                p = data;
+                if (ptype) {
+                    if (!d2i_PrivateKey(ptype, pp, &p, len)) {
+                        PEMerr(PEM_F_PEM_X509_INFO_READ_BIO, ERR_R_ASN1_LIB);
+                        goto err;
+                    }
+                } else if (d2i(pp, &p, len) == NULL) {
+                    PEMerr(PEM_F_PEM_X509_INFO_READ_BIO, ERR_R_ASN1_LIB);
+                    goto err;
+                }
+            } else {            /* encrypted RSA data */
+                if (!PEM_get_EVP_CIPHER_INFO(header, &xi->enc_cipher))
+                    goto err;
+                xi->enc_data = (char *)data;
+                xi->enc_len = (int)len;
+                data = NULL;
+            }
+        } else {
+            /* unknown */
+        }
+        OPENSSL_free(name);
+        name = NULL;
+        OPENSSL_free(header);
+        header = NULL;
+        OPENSSL_free(data);
+        data = NULL;
+    }
+
+    /*
+     * if the last one hasn't been pushed yet and there is anything in it
+     * then add it to the stack ...
+     */
+    if ((xi->x509 != NULL) || (xi->crl != NULL) ||
+        (xi->x_pkey != NULL) || (xi->enc_data != NULL)) {
+        if (!sk_X509_INFO_push(ret, xi))
+            goto err;
+        xi = NULL;
+    }
+    ok = 1;
+ err:
+    X509_INFO_free(xi);
+    if (!ok) {
+        for (i = 0; ((int)i) < sk_X509_INFO_num(ret); i++) {
+            xi = sk_X509_INFO_value(ret, i);
+            X509_INFO_free(xi);
+        }
+        if (ret != sk)
+            sk_X509_INFO_free(ret);
+        ret = NULL;
+    }
+
+    OPENSSL_free(name);
+    OPENSSL_free(header);
+    OPENSSL_free(data);
+    return (ret);
+}
+
+/* A TJH addition */
+int PEM_X509_INFO_write_bio(BIO *bp, X509_INFO *xi, EVP_CIPHER *enc,
+                            unsigned char *kstr, int klen,
+                            pem_password_cb *cb, void *u)
+{
+    int i, ret = 0;
+    unsigned char *data = NULL;
+    const char *objstr = NULL;
+    char buf[PEM_BUFSIZE];
+    unsigned char *iv = NULL;
+
+    if (enc != NULL) {
+        objstr = OBJ_nid2sn(EVP_CIPHER_nid(enc));
+        if (objstr == NULL) {
+            PEMerr(PEM_F_PEM_X509_INFO_WRITE_BIO, PEM_R_UNSUPPORTED_CIPHER);
+            goto err;
+        }
+    }
+
+    /*
+     * now for the fun part ... if we have a private key then we have to be
+     * able to handle a not-yet-decrypted key being written out correctly ...
+     * if it is decrypted or it is non-encrypted then we use the base code
+     */
+    if (xi->x_pkey != NULL) {
+        if ((xi->enc_data != NULL) && (xi->enc_len > 0)) {
+            if (enc == NULL) {
+                PEMerr(PEM_F_PEM_X509_INFO_WRITE_BIO, PEM_R_CIPHER_IS_NULL);
+                goto err;
+            }
+
+            /* copy from weirdo names into more normal things */
+            iv = xi->enc_cipher.iv;
+            data = (unsigned char *)xi->enc_data;
+            i = xi->enc_len;
+
+            /*
+             * we take the encryption data from the internal stuff rather
+             * than what the user has passed us ... as we have to match
+             * exactly for some strange reason
+             */
+            objstr = OBJ_nid2sn(EVP_CIPHER_nid(xi->enc_cipher.cipher));
+            if (objstr == NULL) {
+                PEMerr(PEM_F_PEM_X509_INFO_WRITE_BIO,
+                       PEM_R_UNSUPPORTED_CIPHER);
+                goto err;
+            }
+
+            /* create the right magic header stuff */
+            OPENSSL_assert(strlen(objstr) + 23
+                           + 2 * EVP_CIPHER_iv_length(enc) + 13 <=
+                           sizeof buf);
+            buf[0] = '\0';
+            PEM_proc_type(buf, PEM_TYPE_ENCRYPTED);
+            PEM_dek_info(buf, objstr, EVP_CIPHER_iv_length(enc),
+                         (char *)iv);
+
+            /* use the normal code to write things out */
+            i = PEM_write_bio(bp, PEM_STRING_RSA, buf, data, i);
+            if (i <= 0)
+                goto err;
+        } else {
+            /* Add DSA/DH */
+#ifndef OPENSSL_NO_RSA
+            /* normal optionally encrypted stuff */
+            if (PEM_write_bio_RSAPrivateKey(bp,
+                                            EVP_PKEY_get0_RSA(xi->x_pkey->dec_pkey),
+                                            enc, kstr, klen, cb, u) <= 0)
+                goto err;
+#endif
+        }
+    }
+
+    /* if we have a certificate then write it out now */
+    if ((xi->x509 != NULL) && (PEM_write_bio_X509(bp, xi->x509) <= 0))
+        goto err;
+
+    /*
+     * we are ignoring anything else that is loaded into the X509_INFO
+     * structure for the moment ... as I don't need it so I'm not coding it
+     * here and Eric can do it when this makes it into the base library --tjh
+     */
+
+    ret = 1;
+
+ err:
+    OPENSSL_cleanse(buf, PEM_BUFSIZE);
+    return (ret);
+}
diff --git a/crypto/pem/pem_lib.c b/crypto/pem/pem_lib.c
new file mode 100644
index 000000000..1523e8b2c
--- /dev/null
+++ b/crypto/pem/pem_lib.c
@@ -0,0 +1,861 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include "internal/asn1_int.h"
+#ifndef OPENSSL_NO_DES
+# include 
+#endif
+#ifndef OPENSSL_NO_ENGINE
+# include 
+#endif
+
+#define MIN_LENGTH      4
+
+static int load_iv(char **fromp, unsigned char *to, int num);
+static int check_pem(const char *nm, const char *name);
+int pem_check_suffix(const char *pem_str, const char *suffix);
+
+int PEM_def_callback(char *buf, int num, int w, void *key)
+{
+#if defined(OPENSSL_NO_STDIO) || defined(OPENSSL_NO_UI)
+    int i;
+#else
+    int i, j;
+    const char *prompt;
+#endif
+
+    if (key) {
+        i = strlen(key);
+        i = (i > num) ? num : i;
+        memcpy(buf, key, i);
+        return i;
+    }
+
+#if defined(OPENSSL_NO_STDIO) || defined(OPENSSL_NO_UI)
+    PEMerr(PEM_F_PEM_DEF_CALLBACK, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
+    return -1;
+#else
+    prompt = EVP_get_pw_prompt();
+    if (prompt == NULL)
+        prompt = "Enter PEM pass phrase:";
+
+    for (;;) {
+        /*
+         * We assume that w == 0 means decryption,
+         * while w == 1 means encryption
+         */
+        int min_len = w ? MIN_LENGTH : 0;
+
+        i = EVP_read_pw_string_min(buf, min_len, num, prompt, w);
+        if (i != 0) {
+            PEMerr(PEM_F_PEM_DEF_CALLBACK, PEM_R_PROBLEMS_GETTING_PASSWORD);
+            memset(buf, 0, (unsigned int)num);
+            return -1;
+        }
+        j = strlen(buf);
+        if (min_len && j < min_len) {
+            fprintf(stderr,
+                    "phrase is too short, needs to be at least %d chars\n",
+                    min_len);
+        } else
+            break;
+    }
+    return j;
+#endif
+}
+
+void PEM_proc_type(char *buf, int type)
+{
+    const char *str;
+
+    if (type == PEM_TYPE_ENCRYPTED)
+        str = "ENCRYPTED";
+    else if (type == PEM_TYPE_MIC_CLEAR)
+        str = "MIC-CLEAR";
+    else if (type == PEM_TYPE_MIC_ONLY)
+        str = "MIC-ONLY";
+    else
+        str = "BAD-TYPE";
+
+    OPENSSL_strlcat(buf, "Proc-Type: 4,", PEM_BUFSIZE);
+    OPENSSL_strlcat(buf, str, PEM_BUFSIZE);
+    OPENSSL_strlcat(buf, "\n", PEM_BUFSIZE);
+}
+
+void PEM_dek_info(char *buf, const char *type, int len, char *str)
+{
+    static const unsigned char map[17] = "0123456789ABCDEF";
+    long i;
+    int j;
+
+    OPENSSL_strlcat(buf, "DEK-Info: ", PEM_BUFSIZE);
+    OPENSSL_strlcat(buf, type, PEM_BUFSIZE);
+    OPENSSL_strlcat(buf, ",", PEM_BUFSIZE);
+    j = strlen(buf);
+    if (j + (len * 2) + 1 > PEM_BUFSIZE)
+        return;
+    for (i = 0; i < len; i++) {
+        buf[j + i * 2] = map[(str[i] >> 4) & 0x0f];
+        buf[j + i * 2 + 1] = map[(str[i]) & 0x0f];
+    }
+    buf[j + i * 2] = '\n';
+    buf[j + i * 2 + 1] = '\0';
+}
+
+#ifndef OPENSSL_NO_STDIO
+void *PEM_ASN1_read(d2i_of_void *d2i, const char *name, FILE *fp, void **x,
+                    pem_password_cb *cb, void *u)
+{
+    BIO *b;
+    void *ret;
+
+    if ((b = BIO_new(BIO_s_file())) == NULL) {
+        PEMerr(PEM_F_PEM_ASN1_READ, ERR_R_BUF_LIB);
+        return (0);
+    }
+    BIO_set_fp(b, fp, BIO_NOCLOSE);
+    ret = PEM_ASN1_read_bio(d2i, name, b, x, cb, u);
+    BIO_free(b);
+    return (ret);
+}
+#endif
+
+static int check_pem(const char *nm, const char *name)
+{
+    /* Normal matching nm and name */
+    if (strcmp(nm, name) == 0)
+        return 1;
+
+    /* Make PEM_STRING_EVP_PKEY match any private key */
+
+    if (strcmp(name, PEM_STRING_EVP_PKEY) == 0) {
+        int slen;
+        const EVP_PKEY_ASN1_METHOD *ameth;
+        if (strcmp(nm, PEM_STRING_PKCS8) == 0)
+            return 1;
+        if (strcmp(nm, PEM_STRING_PKCS8INF) == 0)
+            return 1;
+        slen = pem_check_suffix(nm, "PRIVATE KEY");
+        if (slen > 0) {
+            /*
+             * NB: ENGINE implementations won't contain a deprecated old
+             * private key decode function so don't look for them.
+             */
+            ameth = EVP_PKEY_asn1_find_str(NULL, nm, slen);
+            if (ameth && ameth->old_priv_decode)
+                return 1;
+        }
+        return 0;
+    }
+
+    if (strcmp(name, PEM_STRING_PARAMETERS) == 0) {
+        int slen;
+        const EVP_PKEY_ASN1_METHOD *ameth;
+        slen = pem_check_suffix(nm, "PARAMETERS");
+        if (slen > 0) {
+            ENGINE *e;
+            ameth = EVP_PKEY_asn1_find_str(&e, nm, slen);
+            if (ameth) {
+                int r;
+                if (ameth->param_decode)
+                    r = 1;
+                else
+                    r = 0;
+#ifndef OPENSSL_NO_ENGINE
+                ENGINE_finish(e);
+#endif
+                return r;
+            }
+        }
+        return 0;
+    }
+    /* If reading DH parameters handle X9.42 DH format too */
+    if (strcmp(nm, PEM_STRING_DHXPARAMS) == 0
+        && strcmp(name, PEM_STRING_DHPARAMS) == 0)
+        return 1;
+
+    /* Permit older strings */
+
+    if (strcmp(nm, PEM_STRING_X509_OLD) == 0
+        && strcmp(name, PEM_STRING_X509) == 0)
+        return 1;
+
+    if (strcmp(nm, PEM_STRING_X509_REQ_OLD) == 0
+        && strcmp(name, PEM_STRING_X509_REQ) == 0)
+        return 1;
+
+    /* Allow normal certs to be read as trusted certs */
+    if (strcmp(nm, PEM_STRING_X509) == 0
+        && strcmp(name, PEM_STRING_X509_TRUSTED) == 0)
+        return 1;
+
+    if (strcmp(nm, PEM_STRING_X509_OLD) == 0
+        && strcmp(name, PEM_STRING_X509_TRUSTED) == 0)
+        return 1;
+
+    /* Some CAs use PKCS#7 with CERTIFICATE headers */
+    if (strcmp(nm, PEM_STRING_X509) == 0
+        && strcmp(name, PEM_STRING_PKCS7) == 0)
+        return 1;
+
+    if (strcmp(nm, PEM_STRING_PKCS7_SIGNED) == 0
+        && strcmp(name, PEM_STRING_PKCS7) == 0)
+        return 1;
+
+#ifndef OPENSSL_NO_CMS
+    if (strcmp(nm, PEM_STRING_X509) == 0
+        && strcmp(name, PEM_STRING_CMS) == 0)
+        return 1;
+    /* Allow CMS to be read from PKCS#7 headers */
+    if (strcmp(nm, PEM_STRING_PKCS7) == 0
+        && strcmp(name, PEM_STRING_CMS) == 0)
+        return 1;
+#endif
+
+    return 0;
+}
+
+int PEM_bytes_read_bio(unsigned char **pdata, long *plen, char **pnm,
+                       const char *name, BIO *bp, pem_password_cb *cb,
+                       void *u)
+{
+    EVP_CIPHER_INFO cipher;
+    char *nm = NULL, *header = NULL;
+    unsigned char *data = NULL;
+    long len;
+    int ret = 0;
+
+    for (;;) {
+        if (!PEM_read_bio(bp, &nm, &header, &data, &len)) {
+            if (ERR_GET_REASON(ERR_peek_error()) == PEM_R_NO_START_LINE)
+                ERR_add_error_data(2, "Expecting: ", name);
+            return 0;
+        }
+        if (check_pem(nm, name))
+            break;
+        OPENSSL_free(nm);
+        OPENSSL_free(header);
+        OPENSSL_free(data);
+    }
+    if (!PEM_get_EVP_CIPHER_INFO(header, &cipher))
+        goto err;
+    if (!PEM_do_header(&cipher, data, &len, cb, u))
+        goto err;
+
+    *pdata = data;
+    *plen = len;
+
+    if (pnm)
+        *pnm = nm;
+
+    ret = 1;
+
+ err:
+    if (!ret || !pnm)
+        OPENSSL_free(nm);
+    OPENSSL_free(header);
+    if (!ret)
+        OPENSSL_free(data);
+    return ret;
+}
+
+#ifndef OPENSSL_NO_STDIO
+int PEM_ASN1_write(i2d_of_void *i2d, const char *name, FILE *fp,
+                   void *x, const EVP_CIPHER *enc, unsigned char *kstr,
+                   int klen, pem_password_cb *callback, void *u)
+{
+    BIO *b;
+    int ret;
+
+    if ((b = BIO_new(BIO_s_file())) == NULL) {
+        PEMerr(PEM_F_PEM_ASN1_WRITE, ERR_R_BUF_LIB);
+        return (0);
+    }
+    BIO_set_fp(b, fp, BIO_NOCLOSE);
+    ret = PEM_ASN1_write_bio(i2d, name, b, x, enc, kstr, klen, callback, u);
+    BIO_free(b);
+    return (ret);
+}
+#endif
+
+int PEM_ASN1_write_bio(i2d_of_void *i2d, const char *name, BIO *bp,
+                       void *x, const EVP_CIPHER *enc, unsigned char *kstr,
+                       int klen, pem_password_cb *callback, void *u)
+{
+    EVP_CIPHER_CTX *ctx = NULL;
+    int dsize = 0, i = 0, j = 0, ret = 0;
+    unsigned char *p, *data = NULL;
+    const char *objstr = NULL;
+    char buf[PEM_BUFSIZE];
+    unsigned char key[EVP_MAX_KEY_LENGTH];
+    unsigned char iv[EVP_MAX_IV_LENGTH];
+
+    if (enc != NULL) {
+        objstr = OBJ_nid2sn(EVP_CIPHER_nid(enc));
+        if (objstr == NULL || EVP_CIPHER_iv_length(enc) == 0) {
+            PEMerr(PEM_F_PEM_ASN1_WRITE_BIO, PEM_R_UNSUPPORTED_CIPHER);
+            goto err;
+        }
+    }
+
+    if ((dsize = i2d(x, NULL)) < 0) {
+        PEMerr(PEM_F_PEM_ASN1_WRITE_BIO, ERR_R_ASN1_LIB);
+        dsize = 0;
+        goto err;
+    }
+    /* dzise + 8 bytes are needed */
+    /* actually it needs the cipher block size extra... */
+    data = OPENSSL_malloc((unsigned int)dsize + 20);
+    if (data == NULL) {
+        PEMerr(PEM_F_PEM_ASN1_WRITE_BIO, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+    p = data;
+    i = i2d(x, &p);
+
+    if (enc != NULL) {
+        if (kstr == NULL) {
+            if (callback == NULL)
+                klen = PEM_def_callback(buf, PEM_BUFSIZE, 1, u);
+            else
+                klen = (*callback) (buf, PEM_BUFSIZE, 1, u);
+            if (klen <= 0) {
+                PEMerr(PEM_F_PEM_ASN1_WRITE_BIO, PEM_R_READ_KEY);
+                goto err;
+            }
+#ifdef CHARSET_EBCDIC
+            /* Convert the pass phrase from EBCDIC */
+            ebcdic2ascii(buf, buf, klen);
+#endif
+            kstr = (unsigned char *)buf;
+        }
+        RAND_add(data, i, 0);   /* put in the RSA key. */
+        OPENSSL_assert(EVP_CIPHER_iv_length(enc) <= (int)sizeof(iv));
+        if (RAND_bytes(iv, EVP_CIPHER_iv_length(enc)) <= 0) /* Generate a salt */
+            goto err;
+        /*
+         * The 'iv' is used as the iv and as a salt.  It is NOT taken from
+         * the BytesToKey function
+         */
+        if (!EVP_BytesToKey(enc, EVP_get_default_digest(), iv, kstr, klen, 1, key, NULL))
+            goto err;
+
+        if (kstr == (unsigned char *)buf)
+            OPENSSL_cleanse(buf, PEM_BUFSIZE);
+
+        OPENSSL_assert(strlen(objstr) + 23 + 2 * EVP_CIPHER_iv_length(enc) + 13
+                       <= sizeof buf);
+
+        buf[0] = '\0';
+        PEM_proc_type(buf, PEM_TYPE_ENCRYPTED);
+        PEM_dek_info(buf, objstr, EVP_CIPHER_iv_length(enc), (char *)iv);
+        /* k=strlen(buf); */
+
+        ret = 1;
+        if ((ctx = EVP_CIPHER_CTX_new()) == NULL
+            || !EVP_EncryptInit_ex(ctx, enc, NULL, key, iv)
+            || !EVP_EncryptUpdate(ctx, data, &j, data, i)
+            || !EVP_EncryptFinal_ex(ctx, &(data[j]), &i))
+            ret = 0;
+        if (ret == 0)
+            goto err;
+        i += j;
+    } else {
+        ret = 1;
+        buf[0] = '\0';
+    }
+    i = PEM_write_bio(bp, name, buf, data, i);
+    if (i <= 0)
+        ret = 0;
+ err:
+    OPENSSL_cleanse(key, sizeof(key));
+    OPENSSL_cleanse(iv, sizeof(iv));
+    EVP_CIPHER_CTX_free(ctx);
+    OPENSSL_cleanse(buf, PEM_BUFSIZE);
+    OPENSSL_clear_free(data, (unsigned int)dsize);
+    return (ret);
+}
+
+int PEM_do_header(EVP_CIPHER_INFO *cipher, unsigned char *data, long *plen,
+                  pem_password_cb *callback, void *u)
+{
+    int ok;
+    int keylen;
+    long len = *plen;
+    int ilen = (int) len;       /* EVP_DecryptUpdate etc. take int lengths */
+    EVP_CIPHER_CTX *ctx;
+    unsigned char key[EVP_MAX_KEY_LENGTH];
+    char buf[PEM_BUFSIZE];
+
+#if LONG_MAX > INT_MAX
+    /* Check that we did not truncate the length */
+    if (len > INT_MAX) {
+        PEMerr(PEM_F_PEM_DO_HEADER, PEM_R_HEADER_TOO_LONG);
+        return 0;
+    }
+#endif
+
+    if (cipher->cipher == NULL)
+        return 1;
+    if (callback == NULL)
+        keylen = PEM_def_callback(buf, PEM_BUFSIZE, 0, u);
+    else
+        keylen = callback(buf, PEM_BUFSIZE, 0, u);
+    if (keylen <= 0) {
+        PEMerr(PEM_F_PEM_DO_HEADER, PEM_R_BAD_PASSWORD_READ);
+        return 0;
+    }
+#ifdef CHARSET_EBCDIC
+    /* Convert the pass phrase from EBCDIC */
+    ebcdic2ascii(buf, buf, keylen);
+#endif
+
+    if (!EVP_BytesToKey(cipher->cipher, EVP_get_default_digest(), &(cipher->iv[0]),
+                        (unsigned char *)buf, keylen, 1, key, NULL))
+        return 0;
+
+    ctx = EVP_CIPHER_CTX_new();
+    if (ctx == NULL)
+        return 0;
+
+    ok = EVP_DecryptInit_ex(ctx, cipher->cipher, NULL, key, &(cipher->iv[0]));
+    if (ok)
+        ok = EVP_DecryptUpdate(ctx, data, &ilen, data, ilen);
+    if (ok) {
+        /* Squirrel away the length of data decrypted so far. */
+        *plen = ilen;
+        ok = EVP_DecryptFinal_ex(ctx, &(data[ilen]), &ilen);
+    }
+    if (ok)
+        *plen += ilen;
+    else
+        PEMerr(PEM_F_PEM_DO_HEADER, PEM_R_BAD_DECRYPT);
+
+    EVP_CIPHER_CTX_free(ctx);
+    OPENSSL_cleanse((char *)buf, sizeof(buf));
+    OPENSSL_cleanse((char *)key, sizeof(key));
+    return ok;
+}
+
+/*
+ * This implements a very limited PEM header parser that does not support the
+ * full grammar of rfc1421.  In particular, folded headers are not supported,
+ * nor is additional whitespace.
+ *
+ * A robust implementation would make use of a library that turns the headers
+ * into a BIO from which one folded line is read at a time, and is then split
+ * into a header label and content.  We would then parse the content of the
+ * headers we care about.  This is overkill for just this limited use-case, but
+ * presumably we also parse rfc822-style headers for S/MIME, so a common
+ * abstraction might well be more generally useful.
+ */
+int PEM_get_EVP_CIPHER_INFO(char *header, EVP_CIPHER_INFO *cipher)
+{
+    static const char ProcType[] = "Proc-Type:";
+    static const char ENCRYPTED[] = "ENCRYPTED";
+    static const char DEKInfo[] = "DEK-Info:";
+    const EVP_CIPHER *enc = NULL;
+    int ivlen;
+    char *dekinfostart, c;
+
+    cipher->cipher = NULL;
+    if ((header == NULL) || (*header == '\0') || (*header == '\n'))
+        return 1;
+
+    if (strncmp(header, ProcType, sizeof(ProcType)-1) != 0) {
+        PEMerr(PEM_F_PEM_GET_EVP_CIPHER_INFO, PEM_R_NOT_PROC_TYPE);
+        return 0;
+    }
+    header += sizeof(ProcType)-1;
+    header += strspn(header, " \t");
+
+    if (*header++ != '4' || *header++ != ',')
+        return 0;
+    header += strspn(header, " \t");
+
+    /* We expect "ENCRYPTED" followed by optional white-space + line break */
+    if (strncmp(header, ENCRYPTED, sizeof(ENCRYPTED)-1) != 0 ||
+        strspn(header+sizeof(ENCRYPTED)-1, " \t\r\n") == 0) {
+        PEMerr(PEM_F_PEM_GET_EVP_CIPHER_INFO, PEM_R_NOT_ENCRYPTED);
+        return 0;
+    }
+    header += sizeof(ENCRYPTED)-1;
+    header += strspn(header, " \t\r");
+    if (*header++ != '\n') {
+        PEMerr(PEM_F_PEM_GET_EVP_CIPHER_INFO, PEM_R_SHORT_HEADER);
+        return 0;
+    }
+
+    /*-
+     * https://tools.ietf.org/html/rfc1421#section-4.6.1.3
+     * We expect "DEK-Info: algo[,hex-parameters]"
+     */
+    if (strncmp(header, DEKInfo, sizeof(DEKInfo)-1) != 0) {
+        PEMerr(PEM_F_PEM_GET_EVP_CIPHER_INFO, PEM_R_NOT_DEK_INFO);
+        return 0;
+    }
+    header += sizeof(DEKInfo)-1;
+    header += strspn(header, " \t");
+
+    /*
+     * DEK-INFO is a comma-separated combination of algorithm name and optional
+     * parameters.
+     */
+    dekinfostart = header;
+    header += strcspn(header, " \t,");
+    c = *header;
+    *header = '\0';
+    cipher->cipher = enc = EVP_get_cipherbyname(dekinfostart);
+    *header = c;
+    header += strspn(header, " \t");
+
+    if (enc == NULL) {
+        PEMerr(PEM_F_PEM_GET_EVP_CIPHER_INFO, PEM_R_UNSUPPORTED_ENCRYPTION);
+        return 0;
+    }
+    ivlen = EVP_CIPHER_iv_length(enc);
+    if (ivlen > 0 && *header++ != ',') {
+        PEMerr(PEM_F_PEM_GET_EVP_CIPHER_INFO, PEM_R_MISSING_DEK_IV);
+        return 0;
+    } else if (ivlen == 0 && *header == ',') {
+        PEMerr(PEM_F_PEM_GET_EVP_CIPHER_INFO, PEM_R_UNEXPECTED_DEK_IV);
+        return 0;
+    }
+
+    if (!load_iv(&header, cipher->iv, EVP_CIPHER_iv_length(enc)))
+        return 0;
+
+    return 1;
+}
+
+static int load_iv(char **fromp, unsigned char *to, int num)
+{
+    int v, i;
+    char *from;
+
+    from = *fromp;
+    for (i = 0; i < num; i++)
+        to[i] = 0;
+    num *= 2;
+    for (i = 0; i < num; i++) {
+        v = OPENSSL_hexchar2int(*from);
+        if (v < 0) {
+            PEMerr(PEM_F_LOAD_IV, PEM_R_BAD_IV_CHARS);
+            return (0);
+        }
+        from++;
+        to[i / 2] |= v << (long)((!(i & 1)) * 4);
+    }
+
+    *fromp = from;
+    return (1);
+}
+
+#ifndef OPENSSL_NO_STDIO
+int PEM_write(FILE *fp, const char *name, const char *header,
+              const unsigned char *data, long len)
+{
+    BIO *b;
+    int ret;
+
+    if ((b = BIO_new(BIO_s_file())) == NULL) {
+        PEMerr(PEM_F_PEM_WRITE, ERR_R_BUF_LIB);
+        return (0);
+    }
+    BIO_set_fp(b, fp, BIO_NOCLOSE);
+    ret = PEM_write_bio(b, name, header, data, len);
+    BIO_free(b);
+    return (ret);
+}
+#endif
+
+int PEM_write_bio(BIO *bp, const char *name, const char *header,
+                  const unsigned char *data, long len)
+{
+    int nlen, n, i, j, outl;
+    unsigned char *buf = NULL;
+    EVP_ENCODE_CTX *ctx = EVP_ENCODE_CTX_new();
+    int reason = ERR_R_BUF_LIB;
+
+    if (ctx == NULL) {
+        reason = ERR_R_MALLOC_FAILURE;
+        goto err;
+    }
+
+    EVP_EncodeInit(ctx);
+    nlen = strlen(name);
+
+    if ((BIO_write(bp, "-----BEGIN ", 11) != 11) ||
+        (BIO_write(bp, name, nlen) != nlen) ||
+        (BIO_write(bp, "-----\n", 6) != 6))
+        goto err;
+
+    i = strlen(header);
+    if (i > 0) {
+        if ((BIO_write(bp, header, i) != i) || (BIO_write(bp, "\n", 1) != 1))
+            goto err;
+    }
+
+    buf = OPENSSL_malloc(PEM_BUFSIZE * 8);
+    if (buf == NULL) {
+        reason = ERR_R_MALLOC_FAILURE;
+        goto err;
+    }
+
+    i = j = 0;
+    while (len > 0) {
+        n = (int)((len > (PEM_BUFSIZE * 5)) ? (PEM_BUFSIZE * 5) : len);
+        if (!EVP_EncodeUpdate(ctx, buf, &outl, &(data[j]), n))
+            goto err;
+        if ((outl) && (BIO_write(bp, (char *)buf, outl) != outl))
+            goto err;
+        i += outl;
+        len -= n;
+        j += n;
+    }
+    EVP_EncodeFinal(ctx, buf, &outl);
+    if ((outl > 0) && (BIO_write(bp, (char *)buf, outl) != outl))
+        goto err;
+    if ((BIO_write(bp, "-----END ", 9) != 9) ||
+        (BIO_write(bp, name, nlen) != nlen) ||
+        (BIO_write(bp, "-----\n", 6) != 6))
+        goto err;
+    OPENSSL_clear_free(buf, PEM_BUFSIZE * 8);
+    EVP_ENCODE_CTX_free(ctx);
+    return (i + outl);
+ err:
+    OPENSSL_clear_free(buf, PEM_BUFSIZE * 8);
+    EVP_ENCODE_CTX_free(ctx);
+    PEMerr(PEM_F_PEM_WRITE_BIO, reason);
+    return (0);
+}
+
+#ifndef OPENSSL_NO_STDIO
+int PEM_read(FILE *fp, char **name, char **header, unsigned char **data,
+             long *len)
+{
+    BIO *b;
+    int ret;
+
+    if ((b = BIO_new(BIO_s_file())) == NULL) {
+        PEMerr(PEM_F_PEM_READ, ERR_R_BUF_LIB);
+        return (0);
+    }
+    BIO_set_fp(b, fp, BIO_NOCLOSE);
+    ret = PEM_read_bio(b, name, header, data, len);
+    BIO_free(b);
+    return (ret);
+}
+#endif
+
+int PEM_read_bio(BIO *bp, char **name, char **header, unsigned char **data,
+                 long *len)
+{
+    EVP_ENCODE_CTX *ctx = EVP_ENCODE_CTX_new();
+    int end = 0, i, k, bl = 0, hl = 0, nohead = 0;
+    char buf[256];
+    BUF_MEM *nameB;
+    BUF_MEM *headerB;
+    BUF_MEM *dataB, *tmpB;
+
+    if (ctx == NULL) {
+        PEMerr(PEM_F_PEM_READ_BIO, ERR_R_MALLOC_FAILURE);
+        return (0);
+    }
+
+    nameB = BUF_MEM_new();
+    headerB = BUF_MEM_new();
+    dataB = BUF_MEM_new();
+    if ((nameB == NULL) || (headerB == NULL) || (dataB == NULL)) {
+        goto err;
+    }
+
+    buf[254] = '\0';
+    for (;;) {
+        i = BIO_gets(bp, buf, 254);
+
+        if (i <= 0) {
+            PEMerr(PEM_F_PEM_READ_BIO, PEM_R_NO_START_LINE);
+            goto err;
+        }
+
+        while ((i >= 0) && (buf[i] <= ' '))
+            i--;
+        buf[++i] = '\n';
+        buf[++i] = '\0';
+
+        if (strncmp(buf, "-----BEGIN ", 11) == 0) {
+            i = strlen(&(buf[11]));
+
+            if (strncmp(&(buf[11 + i - 6]), "-----\n", 6) != 0)
+                continue;
+            if (!BUF_MEM_grow(nameB, i + 9)) {
+                PEMerr(PEM_F_PEM_READ_BIO, ERR_R_MALLOC_FAILURE);
+                goto err;
+            }
+            memcpy(nameB->data, &(buf[11]), i - 6);
+            nameB->data[i - 6] = '\0';
+            break;
+        }
+    }
+    hl = 0;
+    if (!BUF_MEM_grow(headerB, 256)) {
+        PEMerr(PEM_F_PEM_READ_BIO, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+    headerB->data[0] = '\0';
+    for (;;) {
+        i = BIO_gets(bp, buf, 254);
+        if (i <= 0)
+            break;
+
+        while ((i >= 0) && (buf[i] <= ' '))
+            i--;
+        buf[++i] = '\n';
+        buf[++i] = '\0';
+
+        if (buf[0] == '\n')
+            break;
+        if (!BUF_MEM_grow(headerB, hl + i + 9)) {
+            PEMerr(PEM_F_PEM_READ_BIO, ERR_R_MALLOC_FAILURE);
+            goto err;
+        }
+        if (strncmp(buf, "-----END ", 9) == 0) {
+            nohead = 1;
+            break;
+        }
+        memcpy(&(headerB->data[hl]), buf, i);
+        headerB->data[hl + i] = '\0';
+        hl += i;
+    }
+
+    bl = 0;
+    if (!BUF_MEM_grow(dataB, 1024)) {
+        PEMerr(PEM_F_PEM_READ_BIO, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+    dataB->data[0] = '\0';
+    if (!nohead) {
+        for (;;) {
+            i = BIO_gets(bp, buf, 254);
+            if (i <= 0)
+                break;
+
+            while ((i >= 0) && (buf[i] <= ' '))
+                i--;
+            buf[++i] = '\n';
+            buf[++i] = '\0';
+
+            if (i != 65)
+                end = 1;
+            if (strncmp(buf, "-----END ", 9) == 0)
+                break;
+            if (i > 65)
+                break;
+            if (!BUF_MEM_grow_clean(dataB, i + bl + 9)) {
+                PEMerr(PEM_F_PEM_READ_BIO, ERR_R_MALLOC_FAILURE);
+                goto err;
+            }
+            memcpy(&(dataB->data[bl]), buf, i);
+            dataB->data[bl + i] = '\0';
+            bl += i;
+            if (end) {
+                buf[0] = '\0';
+                i = BIO_gets(bp, buf, 254);
+                if (i <= 0)
+                    break;
+
+                while ((i >= 0) && (buf[i] <= ' '))
+                    i--;
+                buf[++i] = '\n';
+                buf[++i] = '\0';
+
+                break;
+            }
+        }
+    } else {
+        tmpB = headerB;
+        headerB = dataB;
+        dataB = tmpB;
+        bl = hl;
+    }
+    i = strlen(nameB->data);
+    if ((strncmp(buf, "-----END ", 9) != 0) ||
+        (strncmp(nameB->data, &(buf[9]), i) != 0) ||
+        (strncmp(&(buf[9 + i]), "-----\n", 6) != 0)) {
+        PEMerr(PEM_F_PEM_READ_BIO, PEM_R_BAD_END_LINE);
+        goto err;
+    }
+
+    EVP_DecodeInit(ctx);
+    i = EVP_DecodeUpdate(ctx,
+                         (unsigned char *)dataB->data, &bl,
+                         (unsigned char *)dataB->data, bl);
+    if (i < 0) {
+        PEMerr(PEM_F_PEM_READ_BIO, PEM_R_BAD_BASE64_DECODE);
+        goto err;
+    }
+    i = EVP_DecodeFinal(ctx, (unsigned char *)&(dataB->data[bl]), &k);
+    if (i < 0) {
+        PEMerr(PEM_F_PEM_READ_BIO, PEM_R_BAD_BASE64_DECODE);
+        goto err;
+    }
+    bl += k;
+
+    if (bl == 0)
+        goto err;
+    *name = nameB->data;
+    *header = headerB->data;
+    *data = (unsigned char *)dataB->data;
+    *len = bl;
+    OPENSSL_free(nameB);
+    OPENSSL_free(headerB);
+    OPENSSL_free(dataB);
+    EVP_ENCODE_CTX_free(ctx);
+    return (1);
+ err:
+    BUF_MEM_free(nameB);
+    BUF_MEM_free(headerB);
+    BUF_MEM_free(dataB);
+    EVP_ENCODE_CTX_free(ctx);
+    return (0);
+}
+
+/*
+ * Check pem string and return prefix length. If for example the pem_str ==
+ * "RSA PRIVATE KEY" and suffix = "PRIVATE KEY" the return value is 3 for the
+ * string "RSA".
+ */
+
+int pem_check_suffix(const char *pem_str, const char *suffix)
+{
+    int pem_len = strlen(pem_str);
+    int suffix_len = strlen(suffix);
+    const char *p;
+    if (suffix_len + 1 >= pem_len)
+        return 0;
+    p = pem_str + pem_len - suffix_len;
+    if (strcmp(p, suffix))
+        return 0;
+    p--;
+    if (*p != ' ')
+        return 0;
+    return p - pem_str;
+}
diff --git a/crypto/pem/pem_oth.c b/crypto/pem/pem_oth.c
new file mode 100644
index 000000000..cc7a8dbec
--- /dev/null
+++ b/crypto/pem/pem_oth.c
@@ -0,0 +1,36 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+#include 
+#include 
+#include 
+
+/* Handle 'other' PEMs: not private keys */
+
+void *PEM_ASN1_read_bio(d2i_of_void *d2i, const char *name, BIO *bp, void **x,
+                        pem_password_cb *cb, void *u)
+{
+    const unsigned char *p = NULL;
+    unsigned char *data = NULL;
+    long len;
+    char *ret = NULL;
+
+    if (!PEM_bytes_read_bio(&data, &len, NULL, name, bp, cb, u))
+        return NULL;
+    p = data;
+    ret = d2i(x, &p, len);
+    if (ret == NULL)
+        PEMerr(PEM_F_PEM_ASN1_READ_BIO, ERR_R_ASN1_LIB);
+    OPENSSL_free(data);
+    return (ret);
+}
diff --git a/crypto/pem/pem_pk8.c b/crypto/pem/pem_pk8.c
new file mode 100644
index 000000000..993c595a7
--- /dev/null
+++ b/crypto/pem/pem_pk8.c
@@ -0,0 +1,213 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+static int do_pk8pkey(BIO *bp, EVP_PKEY *x, int isder,
+                      int nid, const EVP_CIPHER *enc,
+                      char *kstr, int klen, pem_password_cb *cb, void *u);
+
+#ifndef OPENSSL_NO_STDIO
+static int do_pk8pkey_fp(FILE *bp, EVP_PKEY *x, int isder,
+                         int nid, const EVP_CIPHER *enc,
+                         char *kstr, int klen, pem_password_cb *cb, void *u);
+#endif
+/*
+ * These functions write a private key in PKCS#8 format: it is a "drop in"
+ * replacement for PEM_write_bio_PrivateKey() and friends. As usual if 'enc'
+ * is NULL then it uses the unencrypted private key form. The 'nid' versions
+ * uses PKCS#5 v1.5 PBE algorithms whereas the others use PKCS#5 v2.0.
+ */
+
+int PEM_write_bio_PKCS8PrivateKey_nid(BIO *bp, EVP_PKEY *x, int nid,
+                                      char *kstr, int klen,
+                                      pem_password_cb *cb, void *u)
+{
+    return do_pk8pkey(bp, x, 0, nid, NULL, kstr, klen, cb, u);
+}
+
+int PEM_write_bio_PKCS8PrivateKey(BIO *bp, EVP_PKEY *x, const EVP_CIPHER *enc,
+                                  char *kstr, int klen,
+                                  pem_password_cb *cb, void *u)
+{
+    return do_pk8pkey(bp, x, 0, -1, enc, kstr, klen, cb, u);
+}
+
+int i2d_PKCS8PrivateKey_bio(BIO *bp, EVP_PKEY *x, const EVP_CIPHER *enc,
+                            char *kstr, int klen,
+                            pem_password_cb *cb, void *u)
+{
+    return do_pk8pkey(bp, x, 1, -1, enc, kstr, klen, cb, u);
+}
+
+int i2d_PKCS8PrivateKey_nid_bio(BIO *bp, EVP_PKEY *x, int nid,
+                                char *kstr, int klen,
+                                pem_password_cb *cb, void *u)
+{
+    return do_pk8pkey(bp, x, 1, nid, NULL, kstr, klen, cb, u);
+}
+
+static int do_pk8pkey(BIO *bp, EVP_PKEY *x, int isder, int nid,
+                      const EVP_CIPHER *enc, char *kstr, int klen,
+                      pem_password_cb *cb, void *u)
+{
+    X509_SIG *p8;
+    PKCS8_PRIV_KEY_INFO *p8inf;
+    char buf[PEM_BUFSIZE];
+    int ret;
+
+    if ((p8inf = EVP_PKEY2PKCS8(x)) == NULL) {
+        PEMerr(PEM_F_DO_PK8PKEY, PEM_R_ERROR_CONVERTING_PRIVATE_KEY);
+        return 0;
+    }
+    if (enc || (nid != -1)) {
+        if (!kstr) {
+            if (!cb)
+                klen = PEM_def_callback(buf, PEM_BUFSIZE, 1, u);
+            else
+                klen = cb(buf, PEM_BUFSIZE, 1, u);
+            if (klen <= 0) {
+                PEMerr(PEM_F_DO_PK8PKEY, PEM_R_READ_KEY);
+                PKCS8_PRIV_KEY_INFO_free(p8inf);
+                return 0;
+            }
+
+            kstr = buf;
+        }
+        p8 = PKCS8_encrypt(nid, enc, kstr, klen, NULL, 0, 0, p8inf);
+        if (kstr == buf)
+            OPENSSL_cleanse(buf, klen);
+        PKCS8_PRIV_KEY_INFO_free(p8inf);
+        if (p8 == NULL)
+            return 0;
+        if (isder)
+            ret = i2d_PKCS8_bio(bp, p8);
+        else
+            ret = PEM_write_bio_PKCS8(bp, p8);
+        X509_SIG_free(p8);
+        return ret;
+    } else {
+        if (isder)
+            ret = i2d_PKCS8_PRIV_KEY_INFO_bio(bp, p8inf);
+        else
+            ret = PEM_write_bio_PKCS8_PRIV_KEY_INFO(bp, p8inf);
+        PKCS8_PRIV_KEY_INFO_free(p8inf);
+        return ret;
+    }
+}
+
+EVP_PKEY *d2i_PKCS8PrivateKey_bio(BIO *bp, EVP_PKEY **x, pem_password_cb *cb,
+                                  void *u)
+{
+    PKCS8_PRIV_KEY_INFO *p8inf = NULL;
+    X509_SIG *p8 = NULL;
+    int klen;
+    EVP_PKEY *ret;
+    char psbuf[PEM_BUFSIZE];
+    p8 = d2i_PKCS8_bio(bp, NULL);
+    if (!p8)
+        return NULL;
+    if (cb)
+        klen = cb(psbuf, PEM_BUFSIZE, 0, u);
+    else
+        klen = PEM_def_callback(psbuf, PEM_BUFSIZE, 0, u);
+    if (klen <= 0) {
+        PEMerr(PEM_F_D2I_PKCS8PRIVATEKEY_BIO, PEM_R_BAD_PASSWORD_READ);
+        X509_SIG_free(p8);
+        return NULL;
+    }
+    p8inf = PKCS8_decrypt(p8, psbuf, klen);
+    X509_SIG_free(p8);
+    if (!p8inf)
+        return NULL;
+    ret = EVP_PKCS82PKEY(p8inf);
+    PKCS8_PRIV_KEY_INFO_free(p8inf);
+    if (!ret)
+        return NULL;
+    if (x) {
+        EVP_PKEY_free(*x);
+        *x = ret;
+    }
+    return ret;
+}
+
+#ifndef OPENSSL_NO_STDIO
+
+int i2d_PKCS8PrivateKey_fp(FILE *fp, EVP_PKEY *x, const EVP_CIPHER *enc,
+                           char *kstr, int klen, pem_password_cb *cb, void *u)
+{
+    return do_pk8pkey_fp(fp, x, 1, -1, enc, kstr, klen, cb, u);
+}
+
+int i2d_PKCS8PrivateKey_nid_fp(FILE *fp, EVP_PKEY *x, int nid,
+                               char *kstr, int klen,
+                               pem_password_cb *cb, void *u)
+{
+    return do_pk8pkey_fp(fp, x, 1, nid, NULL, kstr, klen, cb, u);
+}
+
+int PEM_write_PKCS8PrivateKey_nid(FILE *fp, EVP_PKEY *x, int nid,
+                                  char *kstr, int klen,
+                                  pem_password_cb *cb, void *u)
+{
+    return do_pk8pkey_fp(fp, x, 0, nid, NULL, kstr, klen, cb, u);
+}
+
+int PEM_write_PKCS8PrivateKey(FILE *fp, EVP_PKEY *x, const EVP_CIPHER *enc,
+                              char *kstr, int klen, pem_password_cb *cb,
+                              void *u)
+{
+    return do_pk8pkey_fp(fp, x, 0, -1, enc, kstr, klen, cb, u);
+}
+
+static int do_pk8pkey_fp(FILE *fp, EVP_PKEY *x, int isder, int nid,
+                         const EVP_CIPHER *enc, char *kstr, int klen,
+                         pem_password_cb *cb, void *u)
+{
+    BIO *bp;
+    int ret;
+
+    if ((bp = BIO_new_fp(fp, BIO_NOCLOSE)) == NULL) {
+        PEMerr(PEM_F_DO_PK8PKEY_FP, ERR_R_BUF_LIB);
+        return (0);
+    }
+    ret = do_pk8pkey(bp, x, isder, nid, enc, kstr, klen, cb, u);
+    BIO_free(bp);
+    return ret;
+}
+
+EVP_PKEY *d2i_PKCS8PrivateKey_fp(FILE *fp, EVP_PKEY **x, pem_password_cb *cb,
+                                 void *u)
+{
+    BIO *bp;
+    EVP_PKEY *ret;
+
+    if ((bp = BIO_new_fp(fp, BIO_NOCLOSE)) == NULL) {
+        PEMerr(PEM_F_D2I_PKCS8PRIVATEKEY_FP, ERR_R_BUF_LIB);
+        return NULL;
+    }
+    ret = d2i_PKCS8PrivateKey_bio(bp, x, cb, u);
+    BIO_free(bp);
+    return ret;
+}
+
+#endif
+
+IMPLEMENT_PEM_rw(PKCS8, X509_SIG, PEM_STRING_PKCS8, X509_SIG)
+
+
+IMPLEMENT_PEM_rw(PKCS8_PRIV_KEY_INFO, PKCS8_PRIV_KEY_INFO, PEM_STRING_PKCS8INF,
+             PKCS8_PRIV_KEY_INFO)
diff --git a/crypto/pem/pem_pkey.c b/crypto/pem/pem_pkey.c
new file mode 100644
index 000000000..98c0fa368
--- /dev/null
+++ b/crypto/pem/pem_pkey.c
@@ -0,0 +1,247 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#ifndef OPENSSL_NO_ENGINE
+# include 
+#endif
+#ifndef OPENSSL_NO_DH
+# include 
+#endif
+#include "internal/asn1_int.h"
+#include "internal/evp_int.h"
+
+int pem_check_suffix(const char *pem_str, const char *suffix);
+
+EVP_PKEY *PEM_read_bio_PrivateKey(BIO *bp, EVP_PKEY **x, pem_password_cb *cb,
+                                  void *u)
+{
+    char *nm = NULL;
+    const unsigned char *p = NULL;
+    unsigned char *data = NULL;
+    long len;
+    int slen;
+    EVP_PKEY *ret = NULL;
+
+    if (!PEM_bytes_read_bio(&data, &len, &nm, PEM_STRING_EVP_PKEY, bp, cb, u))
+        return NULL;
+    p = data;
+
+    if (strcmp(nm, PEM_STRING_PKCS8INF) == 0) {
+        PKCS8_PRIV_KEY_INFO *p8inf;
+        p8inf = d2i_PKCS8_PRIV_KEY_INFO(NULL, &p, len);
+        if (!p8inf)
+            goto p8err;
+        ret = EVP_PKCS82PKEY(p8inf);
+        if (x) {
+            EVP_PKEY_free((EVP_PKEY *)*x);
+            *x = ret;
+        }
+        PKCS8_PRIV_KEY_INFO_free(p8inf);
+    } else if (strcmp(nm, PEM_STRING_PKCS8) == 0) {
+        PKCS8_PRIV_KEY_INFO *p8inf;
+        X509_SIG *p8;
+        int klen;
+        char psbuf[PEM_BUFSIZE];
+        p8 = d2i_X509_SIG(NULL, &p, len);
+        if (!p8)
+            goto p8err;
+        if (cb)
+            klen = cb(psbuf, PEM_BUFSIZE, 0, u);
+        else
+            klen = PEM_def_callback(psbuf, PEM_BUFSIZE, 0, u);
+        if (klen <= 0) {
+            PEMerr(PEM_F_PEM_READ_BIO_PRIVATEKEY, PEM_R_BAD_PASSWORD_READ);
+            X509_SIG_free(p8);
+            goto err;
+        }
+        p8inf = PKCS8_decrypt(p8, psbuf, klen);
+        X509_SIG_free(p8);
+        if (!p8inf)
+            goto p8err;
+        ret = EVP_PKCS82PKEY(p8inf);
+        if (x) {
+            EVP_PKEY_free((EVP_PKEY *)*x);
+            *x = ret;
+        }
+        PKCS8_PRIV_KEY_INFO_free(p8inf);
+    } else if ((slen = pem_check_suffix(nm, "PRIVATE KEY")) > 0) {
+        const EVP_PKEY_ASN1_METHOD *ameth;
+        ameth = EVP_PKEY_asn1_find_str(NULL, nm, slen);
+        if (!ameth || !ameth->old_priv_decode)
+            goto p8err;
+        ret = d2i_PrivateKey(ameth->pkey_id, x, &p, len);
+    }
+ p8err:
+    if (ret == NULL)
+        PEMerr(PEM_F_PEM_READ_BIO_PRIVATEKEY, ERR_R_ASN1_LIB);
+ err:
+    OPENSSL_free(nm);
+    OPENSSL_clear_free(data, len);
+    return (ret);
+}
+
+int PEM_write_bio_PrivateKey(BIO *bp, EVP_PKEY *x, const EVP_CIPHER *enc,
+                             unsigned char *kstr, int klen,
+                             pem_password_cb *cb, void *u)
+{
+    if (x->ameth == NULL || x->ameth->priv_encode != NULL)
+        return PEM_write_bio_PKCS8PrivateKey(bp, x, enc,
+                                             (char *)kstr, klen, cb, u);
+    return PEM_write_bio_PrivateKey_traditional(bp, x, enc, kstr, klen, cb, u);
+}
+
+int PEM_write_bio_PrivateKey_traditional(BIO *bp, EVP_PKEY *x,
+                                         const EVP_CIPHER *enc,
+                                         unsigned char *kstr, int klen,
+                                         pem_password_cb *cb, void *u)
+{
+    char pem_str[80];
+    BIO_snprintf(pem_str, 80, "%s PRIVATE KEY", x->ameth->pem_str);
+    return PEM_ASN1_write_bio((i2d_of_void *)i2d_PrivateKey,
+                              pem_str, bp, x, enc, kstr, klen, cb, u);
+}
+
+EVP_PKEY *PEM_read_bio_Parameters(BIO *bp, EVP_PKEY **x)
+{
+    char *nm = NULL;
+    const unsigned char *p = NULL;
+    unsigned char *data = NULL;
+    long len;
+    int slen;
+    EVP_PKEY *ret = NULL;
+
+    if (!PEM_bytes_read_bio(&data, &len, &nm, PEM_STRING_PARAMETERS,
+                            bp, 0, NULL))
+        return NULL;
+    p = data;
+
+    if ((slen = pem_check_suffix(nm, "PARAMETERS")) > 0) {
+        ret = EVP_PKEY_new();
+        if (ret == NULL)
+            goto err;
+        if (!EVP_PKEY_set_type_str(ret, nm, slen)
+            || !ret->ameth->param_decode
+            || !ret->ameth->param_decode(ret, &p, len)) {
+            EVP_PKEY_free(ret);
+            ret = NULL;
+            goto err;
+        }
+        if (x) {
+            EVP_PKEY_free((EVP_PKEY *)*x);
+            *x = ret;
+        }
+    }
+ err:
+    if (ret == NULL)
+        PEMerr(PEM_F_PEM_READ_BIO_PARAMETERS, ERR_R_ASN1_LIB);
+    OPENSSL_free(nm);
+    OPENSSL_free(data);
+    return (ret);
+}
+
+int PEM_write_bio_Parameters(BIO *bp, EVP_PKEY *x)
+{
+    char pem_str[80];
+    if (!x->ameth || !x->ameth->param_encode)
+        return 0;
+
+    BIO_snprintf(pem_str, 80, "%s PARAMETERS", x->ameth->pem_str);
+    return PEM_ASN1_write_bio((i2d_of_void *)x->ameth->param_encode,
+                              pem_str, bp, x, NULL, NULL, 0, 0, NULL);
+}
+
+#ifndef OPENSSL_NO_STDIO
+EVP_PKEY *PEM_read_PrivateKey(FILE *fp, EVP_PKEY **x, pem_password_cb *cb,
+                              void *u)
+{
+    BIO *b;
+    EVP_PKEY *ret;
+
+    if ((b = BIO_new(BIO_s_file())) == NULL) {
+        PEMerr(PEM_F_PEM_READ_PRIVATEKEY, ERR_R_BUF_LIB);
+        return (0);
+    }
+    BIO_set_fp(b, fp, BIO_NOCLOSE);
+    ret = PEM_read_bio_PrivateKey(b, x, cb, u);
+    BIO_free(b);
+    return (ret);
+}
+
+int PEM_write_PrivateKey(FILE *fp, EVP_PKEY *x, const EVP_CIPHER *enc,
+                         unsigned char *kstr, int klen,
+                         pem_password_cb *cb, void *u)
+{
+    BIO *b;
+    int ret;
+
+    if ((b = BIO_new_fp(fp, BIO_NOCLOSE)) == NULL) {
+        PEMerr(PEM_F_PEM_WRITE_PRIVATEKEY, ERR_R_BUF_LIB);
+        return 0;
+    }
+    ret = PEM_write_bio_PrivateKey(b, x, enc, kstr, klen, cb, u);
+    BIO_free(b);
+    return ret;
+}
+
+#endif
+
+#ifndef OPENSSL_NO_DH
+
+/* Transparently read in PKCS#3 or X9.42 DH parameters */
+
+DH *PEM_read_bio_DHparams(BIO *bp, DH **x, pem_password_cb *cb, void *u)
+{
+    char *nm = NULL;
+    const unsigned char *p = NULL;
+    unsigned char *data = NULL;
+    long len;
+    DH *ret = NULL;
+
+    if (!PEM_bytes_read_bio(&data, &len, &nm, PEM_STRING_DHPARAMS, bp, cb, u))
+        return NULL;
+    p = data;
+
+    if (strcmp(nm, PEM_STRING_DHXPARAMS) == 0)
+        ret = d2i_DHxparams(x, &p, len);
+    else
+        ret = d2i_DHparams(x, &p, len);
+
+    if (ret == NULL)
+        PEMerr(PEM_F_PEM_READ_BIO_DHPARAMS, ERR_R_ASN1_LIB);
+    OPENSSL_free(nm);
+    OPENSSL_free(data);
+    return ret;
+}
+
+# ifndef OPENSSL_NO_STDIO
+DH *PEM_read_DHparams(FILE *fp, DH **x, pem_password_cb *cb, void *u)
+{
+    BIO *b;
+    DH *ret;
+
+    if ((b = BIO_new(BIO_s_file())) == NULL) {
+        PEMerr(PEM_F_PEM_READ_DHPARAMS, ERR_R_BUF_LIB);
+        return (0);
+    }
+    BIO_set_fp(b, fp, BIO_NOCLOSE);
+    ret = PEM_read_bio_DHparams(b, x, cb, u);
+    BIO_free(b);
+    return (ret);
+}
+# endif
+
+#endif
diff --git a/crypto/pem/pem_sign.c b/crypto/pem/pem_sign.c
new file mode 100644
index 000000000..12ad97450
--- /dev/null
+++ b/crypto/pem/pem_sign.c
@@ -0,0 +1,50 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+#include 
+#include 
+
+int PEM_SignInit(EVP_MD_CTX *ctx, EVP_MD *type)
+{
+    return EVP_DigestInit_ex(ctx, type, NULL);
+}
+
+int PEM_SignUpdate(EVP_MD_CTX *ctx, unsigned char *data, unsigned int count)
+{
+    return EVP_DigestUpdate(ctx, data, count);
+}
+
+int PEM_SignFinal(EVP_MD_CTX *ctx, unsigned char *sigret,
+                  unsigned int *siglen, EVP_PKEY *pkey)
+{
+    unsigned char *m;
+    int i, ret = 0;
+    unsigned int m_len;
+
+    m = OPENSSL_malloc(EVP_PKEY_size(pkey) + 2);
+    if (m == NULL) {
+        PEMerr(PEM_F_PEM_SIGNFINAL, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+
+    if (EVP_SignFinal(ctx, m, &m_len, pkey) <= 0)
+        goto err;
+
+    i = EVP_EncodeBlock(sigret, m, m_len);
+    *siglen = i;
+    ret = 1;
+ err:
+    /* ctx has been zeroed by EVP_SignFinal() */
+    OPENSSL_free(m);
+    return (ret);
+}
diff --git a/crypto/pem/pem_x509.c b/crypto/pem/pem_x509.c
new file mode 100644
index 000000000..3a997564a
--- /dev/null
+++ b/crypto/pem/pem_x509.c
@@ -0,0 +1,18 @@
+/*
+ * Copyright 2001-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+#include 
+#include 
+#include 
+
+IMPLEMENT_PEM_rw(X509, X509, PEM_STRING_X509, X509)
diff --git a/crypto/pem/pem_xaux.c b/crypto/pem/pem_xaux.c
new file mode 100644
index 000000000..6d7e1db21
--- /dev/null
+++ b/crypto/pem/pem_xaux.c
@@ -0,0 +1,18 @@
+/*
+ * Copyright 2001-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+#include 
+#include 
+#include 
+
+IMPLEMENT_PEM_rw(X509_AUX, X509, PEM_STRING_X509_TRUSTED, X509_AUX)
diff --git a/crypto/pem/pvkfmt.c b/crypto/pem/pvkfmt.c
new file mode 100644
index 000000000..248704ec5
--- /dev/null
+++ b/crypto/pem/pvkfmt.c
@@ -0,0 +1,869 @@
+/*
+ * Copyright 2005-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * Support for PVK format keys and related structures (such a PUBLICKEYBLOB
+ * and PRIVATEKEYBLOB).
+ */
+
+#include "internal/cryptlib.h"
+#include 
+#include 
+#include 
+#if !defined(OPENSSL_NO_RSA) && !defined(OPENSSL_NO_DSA)
+# include 
+# include 
+
+/*
+ * Utility function: read a DWORD (4 byte unsigned integer) in little endian
+ * format
+ */
+
+static unsigned int read_ledword(const unsigned char **in)
+{
+    const unsigned char *p = *in;
+    unsigned int ret;
+    ret = *p++;
+    ret |= (*p++ << 8);
+    ret |= (*p++ << 16);
+    ret |= (*p++ << 24);
+    *in = p;
+    return ret;
+}
+
+/*
+ * Read a BIGNUM in little endian format. The docs say that this should take
+ * up bitlen/8 bytes.
+ */
+
+static int read_lebn(const unsigned char **in, unsigned int nbyte, BIGNUM **r)
+{
+    *r = BN_lebin2bn(*in, nbyte, NULL);
+    if (*r == NULL)
+        return 0;
+    *in += nbyte;
+    return 1;
+}
+
+/* Convert private key blob to EVP_PKEY: RSA and DSA keys supported */
+
+# define MS_PUBLICKEYBLOB        0x6
+# define MS_PRIVATEKEYBLOB       0x7
+# define MS_RSA1MAGIC            0x31415352L
+# define MS_RSA2MAGIC            0x32415352L
+# define MS_DSS1MAGIC            0x31535344L
+# define MS_DSS2MAGIC            0x32535344L
+
+# define MS_KEYALG_RSA_KEYX      0xa400
+# define MS_KEYALG_DSS_SIGN      0x2200
+
+# define MS_KEYTYPE_KEYX         0x1
+# define MS_KEYTYPE_SIGN         0x2
+
+/* Maximum length of a blob after header */
+# define BLOB_MAX_LENGTH          102400
+
+/* The PVK file magic number: seems to spell out "bobsfile", who is Bob? */
+# define MS_PVKMAGIC             0xb0b5f11eL
+/* Salt length for PVK files */
+# define PVK_SALTLEN             0x10
+/* Maximum length in PVK header */
+# define PVK_MAX_KEYLEN          102400
+/* Maximum salt length */
+# define PVK_MAX_SALTLEN         10240
+
+static EVP_PKEY *b2i_rsa(const unsigned char **in,
+                         unsigned int bitlen, int ispub);
+static EVP_PKEY *b2i_dss(const unsigned char **in,
+                         unsigned int bitlen, int ispub);
+
+static int do_blob_header(const unsigned char **in, unsigned int length,
+                          unsigned int *pmagic, unsigned int *pbitlen,
+                          int *pisdss, int *pispub)
+{
+    const unsigned char *p = *in;
+    if (length < 16)
+        return 0;
+    /* bType */
+    if (*p == MS_PUBLICKEYBLOB) {
+        if (*pispub == 0) {
+            PEMerr(PEM_F_DO_BLOB_HEADER, PEM_R_EXPECTING_PRIVATE_KEY_BLOB);
+            return 0;
+        }
+        *pispub = 1;
+    } else if (*p == MS_PRIVATEKEYBLOB) {
+        if (*pispub == 1) {
+            PEMerr(PEM_F_DO_BLOB_HEADER, PEM_R_EXPECTING_PUBLIC_KEY_BLOB);
+            return 0;
+        }
+        *pispub = 0;
+    } else
+        return 0;
+    p++;
+    /* Version */
+    if (*p++ != 0x2) {
+        PEMerr(PEM_F_DO_BLOB_HEADER, PEM_R_BAD_VERSION_NUMBER);
+        return 0;
+    }
+    /* Ignore reserved, aiKeyAlg */
+    p += 6;
+    *pmagic = read_ledword(&p);
+    *pbitlen = read_ledword(&p);
+    *pisdss = 0;
+    switch (*pmagic) {
+
+    case MS_DSS1MAGIC:
+        *pisdss = 1;
+    case MS_RSA1MAGIC:
+        if (*pispub == 0) {
+            PEMerr(PEM_F_DO_BLOB_HEADER, PEM_R_EXPECTING_PRIVATE_KEY_BLOB);
+            return 0;
+        }
+        break;
+
+    case MS_DSS2MAGIC:
+        *pisdss = 1;
+    case MS_RSA2MAGIC:
+        if (*pispub == 1) {
+            PEMerr(PEM_F_DO_BLOB_HEADER, PEM_R_EXPECTING_PUBLIC_KEY_BLOB);
+            return 0;
+        }
+        break;
+
+    default:
+        PEMerr(PEM_F_DO_BLOB_HEADER, PEM_R_BAD_MAGIC_NUMBER);
+        return -1;
+    }
+    *in = p;
+    return 1;
+}
+
+static unsigned int blob_length(unsigned bitlen, int isdss, int ispub)
+{
+    unsigned int nbyte, hnbyte;
+    nbyte = (bitlen + 7) >> 3;
+    hnbyte = (bitlen + 15) >> 4;
+    if (isdss) {
+
+        /*
+         * Expected length: 20 for q + 3 components bitlen each + 24 for seed
+         * structure.
+         */
+        if (ispub)
+            return 44 + 3 * nbyte;
+        /*
+         * Expected length: 20 for q, priv, 2 bitlen components + 24 for seed
+         * structure.
+         */
+        else
+            return 64 + 2 * nbyte;
+    } else {
+        /* Expected length: 4 for 'e' + 'n' */
+        if (ispub)
+            return 4 + nbyte;
+        else
+            /*
+             * Expected length: 4 for 'e' and 7 other components. 2
+             * components are bitlen size, 5 are bitlen/2
+             */
+            return 4 + 2 * nbyte + 5 * hnbyte;
+    }
+
+}
+
+static EVP_PKEY *do_b2i(const unsigned char **in, unsigned int length,
+                        int ispub)
+{
+    const unsigned char *p = *in;
+    unsigned int bitlen, magic;
+    int isdss;
+    if (do_blob_header(&p, length, &magic, &bitlen, &isdss, &ispub) <= 0) {
+        PEMerr(PEM_F_DO_B2I, PEM_R_KEYBLOB_HEADER_PARSE_ERROR);
+        return NULL;
+    }
+    length -= 16;
+    if (length < blob_length(bitlen, isdss, ispub)) {
+        PEMerr(PEM_F_DO_B2I, PEM_R_KEYBLOB_TOO_SHORT);
+        return NULL;
+    }
+    if (isdss)
+        return b2i_dss(&p, bitlen, ispub);
+    else
+        return b2i_rsa(&p, bitlen, ispub);
+}
+
+static EVP_PKEY *do_b2i_bio(BIO *in, int ispub)
+{
+    const unsigned char *p;
+    unsigned char hdr_buf[16], *buf = NULL;
+    unsigned int bitlen, magic, length;
+    int isdss;
+    EVP_PKEY *ret = NULL;
+    if (BIO_read(in, hdr_buf, 16) != 16) {
+        PEMerr(PEM_F_DO_B2I_BIO, PEM_R_KEYBLOB_TOO_SHORT);
+        return NULL;
+    }
+    p = hdr_buf;
+    if (do_blob_header(&p, 16, &magic, &bitlen, &isdss, &ispub) <= 0)
+        return NULL;
+
+    length = blob_length(bitlen, isdss, ispub);
+    if (length > BLOB_MAX_LENGTH) {
+        PEMerr(PEM_F_DO_B2I_BIO, PEM_R_HEADER_TOO_LONG);
+        return NULL;
+    }
+    buf = OPENSSL_malloc(length);
+    if (buf == NULL) {
+        PEMerr(PEM_F_DO_B2I_BIO, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+    p = buf;
+    if (BIO_read(in, buf, length) != (int)length) {
+        PEMerr(PEM_F_DO_B2I_BIO, PEM_R_KEYBLOB_TOO_SHORT);
+        goto err;
+    }
+
+    if (isdss)
+        ret = b2i_dss(&p, bitlen, ispub);
+    else
+        ret = b2i_rsa(&p, bitlen, ispub);
+
+ err:
+    OPENSSL_free(buf);
+    return ret;
+}
+
+static EVP_PKEY *b2i_dss(const unsigned char **in,
+                         unsigned int bitlen, int ispub)
+{
+    const unsigned char *p = *in;
+    EVP_PKEY *ret = NULL;
+    DSA *dsa = NULL;
+    BN_CTX *ctx = NULL;
+    unsigned int nbyte;
+    BIGNUM *pbn = NULL, *qbn = NULL, *gbn = NULL, *priv_key = NULL;
+    BIGNUM *pub_key = NULL;
+
+    nbyte = (bitlen + 7) >> 3;
+
+    dsa = DSA_new();
+    ret = EVP_PKEY_new();
+    if (dsa == NULL || ret == NULL)
+        goto memerr;
+    if (!read_lebn(&p, nbyte, &pbn))
+        goto memerr;
+
+    if (!read_lebn(&p, 20, &qbn))
+        goto memerr;
+
+    if (!read_lebn(&p, nbyte, &gbn))
+        goto memerr;
+
+    if (ispub) {
+        if (!read_lebn(&p, nbyte, &pub_key))
+            goto memerr;
+    } else {
+        if (!read_lebn(&p, 20, &priv_key))
+            goto memerr;
+
+        /* Calculate public key */
+        pub_key = BN_new();
+        if (pub_key == NULL)
+            goto memerr;
+        if ((ctx = BN_CTX_new()) == NULL)
+            goto memerr;
+
+        if (!BN_mod_exp(pub_key, gbn, priv_key, pbn, ctx))
+            goto memerr;
+
+        BN_CTX_free(ctx);
+    }
+    if (!DSA_set0_pqg(dsa, pbn, qbn, gbn))
+        goto memerr;
+    pbn = qbn = gbn = NULL;
+    if (!DSA_set0_key(dsa, pub_key, priv_key))
+        goto memerr;
+
+    EVP_PKEY_set1_DSA(ret, dsa);
+    DSA_free(dsa);
+    *in = p;
+    return ret;
+
+ memerr:
+    PEMerr(PEM_F_B2I_DSS, ERR_R_MALLOC_FAILURE);
+    DSA_free(dsa);
+    BN_free(pbn);
+    BN_free(qbn);
+    BN_free(gbn);
+    BN_free(pub_key);
+    BN_free(priv_key);
+    EVP_PKEY_free(ret);
+    BN_CTX_free(ctx);
+    return NULL;
+}
+
+static EVP_PKEY *b2i_rsa(const unsigned char **in,
+                         unsigned int bitlen, int ispub)
+{
+    const unsigned char *pin = *in;
+    EVP_PKEY *ret = NULL;
+    BIGNUM *e = NULL, *n = NULL, *d = NULL;
+    BIGNUM *p = NULL, *q = NULL, *dmp1 = NULL, *dmq1 = NULL, *iqmp = NULL;
+    RSA *rsa = NULL;
+    unsigned int nbyte, hnbyte;
+    nbyte = (bitlen + 7) >> 3;
+    hnbyte = (bitlen + 15) >> 4;
+    rsa = RSA_new();
+    ret = EVP_PKEY_new();
+    if (rsa == NULL || ret == NULL)
+        goto memerr;
+    e = BN_new();
+    if (e == NULL)
+        goto memerr;
+    if (!BN_set_word(e, read_ledword(&pin)))
+        goto memerr;
+    if (!read_lebn(&pin, nbyte, &n))
+        goto memerr;
+    if (!ispub) {
+        if (!read_lebn(&pin, hnbyte, &p))
+            goto memerr;
+        if (!read_lebn(&pin, hnbyte, &q))
+            goto memerr;
+        if (!read_lebn(&pin, hnbyte, &dmp1))
+            goto memerr;
+        if (!read_lebn(&pin, hnbyte, &dmq1))
+            goto memerr;
+        if (!read_lebn(&pin, hnbyte, &iqmp))
+            goto memerr;
+        if (!read_lebn(&pin, nbyte, &d))
+            goto memerr;
+        RSA_set0_factors(rsa, p, q);
+        RSA_set0_crt_params(rsa, dmp1, dmq1, iqmp);
+    }
+    RSA_set0_key(rsa, n, e, d);
+
+    EVP_PKEY_set1_RSA(ret, rsa);
+    RSA_free(rsa);
+    *in = pin;
+    return ret;
+ memerr:
+    PEMerr(PEM_F_B2I_RSA, ERR_R_MALLOC_FAILURE);
+    BN_free(e);
+    BN_free(n);
+    BN_free(p);
+    BN_free(q);
+    BN_free(dmp1);
+    BN_free(dmq1);
+    BN_free(iqmp);
+    BN_free(d);
+    RSA_free(rsa);
+    EVP_PKEY_free(ret);
+    return NULL;
+}
+
+EVP_PKEY *b2i_PrivateKey(const unsigned char **in, long length)
+{
+    return do_b2i(in, length, 0);
+}
+
+EVP_PKEY *b2i_PublicKey(const unsigned char **in, long length)
+{
+    return do_b2i(in, length, 1);
+}
+
+EVP_PKEY *b2i_PrivateKey_bio(BIO *in)
+{
+    return do_b2i_bio(in, 0);
+}
+
+EVP_PKEY *b2i_PublicKey_bio(BIO *in)
+{
+    return do_b2i_bio(in, 1);
+}
+
+static void write_ledword(unsigned char **out, unsigned int dw)
+{
+    unsigned char *p = *out;
+    *p++ = dw & 0xff;
+    *p++ = (dw >> 8) & 0xff;
+    *p++ = (dw >> 16) & 0xff;
+    *p++ = (dw >> 24) & 0xff;
+    *out = p;
+}
+
+static void write_lebn(unsigned char **out, const BIGNUM *bn, int len)
+{
+    BN_bn2lebinpad(bn, *out, len);
+    *out += len;
+}
+
+static int check_bitlen_rsa(RSA *rsa, int ispub, unsigned int *magic);
+static int check_bitlen_dsa(DSA *dsa, int ispub, unsigned int *magic);
+
+static void write_rsa(unsigned char **out, RSA *rsa, int ispub);
+static void write_dsa(unsigned char **out, DSA *dsa, int ispub);
+
+static int do_i2b(unsigned char **out, EVP_PKEY *pk, int ispub)
+{
+    unsigned char *p;
+    unsigned int bitlen, magic = 0, keyalg;
+    int outlen, noinc = 0;
+    int pktype = EVP_PKEY_id(pk);
+    if (pktype == EVP_PKEY_DSA) {
+        bitlen = check_bitlen_dsa(EVP_PKEY_get0_DSA(pk), ispub, &magic);
+        keyalg = MS_KEYALG_DSS_SIGN;
+    } else if (pktype == EVP_PKEY_RSA) {
+        bitlen = check_bitlen_rsa(EVP_PKEY_get0_RSA(pk), ispub, &magic);
+        keyalg = MS_KEYALG_RSA_KEYX;
+    } else
+        return -1;
+    if (bitlen == 0)
+        return -1;
+    outlen = 16 + blob_length(bitlen,
+                              keyalg == MS_KEYALG_DSS_SIGN ? 1 : 0, ispub);
+    if (out == NULL)
+        return outlen;
+    if (*out)
+        p = *out;
+    else {
+        p = OPENSSL_malloc(outlen);
+        if (p == NULL)
+            return -1;
+        *out = p;
+        noinc = 1;
+    }
+    if (ispub)
+        *p++ = MS_PUBLICKEYBLOB;
+    else
+        *p++ = MS_PRIVATEKEYBLOB;
+    *p++ = 0x2;
+    *p++ = 0;
+    *p++ = 0;
+    write_ledword(&p, keyalg);
+    write_ledword(&p, magic);
+    write_ledword(&p, bitlen);
+    if (keyalg == MS_KEYALG_DSS_SIGN)
+        write_dsa(&p, EVP_PKEY_get0_DSA(pk), ispub);
+    else
+        write_rsa(&p, EVP_PKEY_get0_RSA(pk), ispub);
+    if (!noinc)
+        *out += outlen;
+    return outlen;
+}
+
+static int do_i2b_bio(BIO *out, EVP_PKEY *pk, int ispub)
+{
+    unsigned char *tmp = NULL;
+    int outlen, wrlen;
+    outlen = do_i2b(&tmp, pk, ispub);
+    if (outlen < 0)
+        return -1;
+    wrlen = BIO_write(out, tmp, outlen);
+    OPENSSL_free(tmp);
+    if (wrlen == outlen)
+        return outlen;
+    return -1;
+}
+
+static int check_bitlen_dsa(DSA *dsa, int ispub, unsigned int *pmagic)
+{
+    int bitlen;
+    const BIGNUM *p = NULL, *q = NULL, *g = NULL;
+    const BIGNUM *pub_key = NULL, *priv_key = NULL;
+
+    DSA_get0_pqg(dsa, &p, &q, &g);
+    DSA_get0_key(dsa, &pub_key, &priv_key);
+    bitlen = BN_num_bits(p);
+    if ((bitlen & 7) || (BN_num_bits(q) != 160)
+        || (BN_num_bits(g) > bitlen))
+        goto badkey;
+    if (ispub) {
+        if (BN_num_bits(pub_key) > bitlen)
+            goto badkey;
+        *pmagic = MS_DSS1MAGIC;
+    } else {
+        if (BN_num_bits(priv_key) > 160)
+            goto badkey;
+        *pmagic = MS_DSS2MAGIC;
+    }
+
+    return bitlen;
+ badkey:
+    PEMerr(PEM_F_CHECK_BITLEN_DSA, PEM_R_UNSUPPORTED_KEY_COMPONENTS);
+    return 0;
+}
+
+static int check_bitlen_rsa(RSA *rsa, int ispub, unsigned int *pmagic)
+{
+    int nbyte, hnbyte, bitlen;
+    const BIGNUM *e;
+
+    RSA_get0_key(rsa, NULL, &e, NULL);
+    if (BN_num_bits(e) > 32)
+        goto badkey;
+    bitlen = RSA_bits(rsa);
+    nbyte = RSA_size(rsa);
+    hnbyte = (bitlen + 15) >> 4;
+    if (ispub) {
+        *pmagic = MS_RSA1MAGIC;
+        return bitlen;
+    } else {
+        const BIGNUM *d, *p, *q, *iqmp, *dmp1, *dmq1;
+
+        *pmagic = MS_RSA2MAGIC;
+
+        /*
+         * For private key each component must fit within nbyte or hnbyte.
+         */
+        RSA_get0_key(rsa, NULL, NULL, &d);
+        if (BN_num_bytes(d) > nbyte)
+            goto badkey;
+        RSA_get0_factors(rsa, &p, &q);
+        RSA_get0_crt_params(rsa, &dmp1, &dmq1, &iqmp);
+        if ((BN_num_bytes(iqmp) > hnbyte)
+            || (BN_num_bytes(p) > hnbyte)
+            || (BN_num_bytes(q) > hnbyte)
+            || (BN_num_bytes(dmp1) > hnbyte)
+            || (BN_num_bytes(dmq1) > hnbyte))
+            goto badkey;
+    }
+    return bitlen;
+ badkey:
+    PEMerr(PEM_F_CHECK_BITLEN_RSA, PEM_R_UNSUPPORTED_KEY_COMPONENTS);
+    return 0;
+}
+
+static void write_rsa(unsigned char **out, RSA *rsa, int ispub)
+{
+    int nbyte, hnbyte;
+    const BIGNUM *n, *d, *e, *p, *q, *iqmp, *dmp1, *dmq1;
+
+    nbyte = RSA_size(rsa);
+    hnbyte = (RSA_bits(rsa) + 15) >> 4;
+    RSA_get0_key(rsa, &n, &e, &d);
+    write_lebn(out, e, 4);
+    write_lebn(out, n, nbyte);
+    if (ispub)
+        return;
+    RSA_get0_factors(rsa, &p, &q);
+    RSA_get0_crt_params(rsa, &dmp1, &dmq1, &iqmp);
+    write_lebn(out, p, hnbyte);
+    write_lebn(out, q, hnbyte);
+    write_lebn(out, dmp1, hnbyte);
+    write_lebn(out, dmq1, hnbyte);
+    write_lebn(out, iqmp, hnbyte);
+    write_lebn(out, d, nbyte);
+}
+
+static void write_dsa(unsigned char **out, DSA *dsa, int ispub)
+{
+    int nbyte;
+    const BIGNUM *p = NULL, *q = NULL, *g = NULL;
+    const BIGNUM *pub_key = NULL, *priv_key = NULL;
+
+    DSA_get0_pqg(dsa, &p, &q, &g);
+    DSA_get0_key(dsa, &pub_key, &priv_key);
+    nbyte = BN_num_bytes(p);
+    write_lebn(out, p, nbyte);
+    write_lebn(out, q, 20);
+    write_lebn(out, g, nbyte);
+    if (ispub)
+        write_lebn(out, pub_key, nbyte);
+    else
+        write_lebn(out, priv_key, 20);
+    /* Set "invalid" for seed structure values */
+    memset(*out, 0xff, 24);
+    *out += 24;
+    return;
+}
+
+int i2b_PrivateKey_bio(BIO *out, EVP_PKEY *pk)
+{
+    return do_i2b_bio(out, pk, 0);
+}
+
+int i2b_PublicKey_bio(BIO *out, EVP_PKEY *pk)
+{
+    return do_i2b_bio(out, pk, 1);
+}
+
+# ifndef OPENSSL_NO_RC4
+
+static int do_PVK_header(const unsigned char **in, unsigned int length,
+                         int skip_magic,
+                         unsigned int *psaltlen, unsigned int *pkeylen)
+{
+    const unsigned char *p = *in;
+    unsigned int pvk_magic, is_encrypted;
+    if (skip_magic) {
+        if (length < 20) {
+            PEMerr(PEM_F_DO_PVK_HEADER, PEM_R_PVK_TOO_SHORT);
+            return 0;
+        }
+    } else {
+        if (length < 24) {
+            PEMerr(PEM_F_DO_PVK_HEADER, PEM_R_PVK_TOO_SHORT);
+            return 0;
+        }
+        pvk_magic = read_ledword(&p);
+        if (pvk_magic != MS_PVKMAGIC) {
+            PEMerr(PEM_F_DO_PVK_HEADER, PEM_R_BAD_MAGIC_NUMBER);
+            return 0;
+        }
+    }
+    /* Skip reserved */
+    p += 4;
+    /*
+     * keytype =
+     */ read_ledword(&p);
+    is_encrypted = read_ledword(&p);
+    *psaltlen = read_ledword(&p);
+    *pkeylen = read_ledword(&p);
+
+    if (*pkeylen > PVK_MAX_KEYLEN || *psaltlen > PVK_MAX_SALTLEN)
+        return 0;
+
+    if (is_encrypted && !*psaltlen) {
+        PEMerr(PEM_F_DO_PVK_HEADER, PEM_R_INCONSISTENT_HEADER);
+        return 0;
+    }
+
+    *in = p;
+    return 1;
+}
+
+static int derive_pvk_key(unsigned char *key,
+                          const unsigned char *salt, unsigned int saltlen,
+                          const unsigned char *pass, int passlen)
+{
+    EVP_MD_CTX *mctx = EVP_MD_CTX_new();
+    int rv = 1;
+    if (mctx == NULL
+        || !EVP_DigestInit_ex(mctx, EVP_sha1(), NULL)
+        || !EVP_DigestUpdate(mctx, salt, saltlen)
+        || !EVP_DigestUpdate(mctx, pass, passlen)
+        || !EVP_DigestFinal_ex(mctx, key, NULL))
+        rv = 0;
+
+    EVP_MD_CTX_free(mctx);
+    return rv;
+}
+
+static EVP_PKEY *do_PVK_body(const unsigned char **in,
+                             unsigned int saltlen, unsigned int keylen,
+                             pem_password_cb *cb, void *u)
+{
+    EVP_PKEY *ret = NULL;
+    const unsigned char *p = *in;
+    unsigned int magic;
+    unsigned char *enctmp = NULL, *q;
+
+    EVP_CIPHER_CTX *cctx = EVP_CIPHER_CTX_new();
+    if (saltlen) {
+        char psbuf[PEM_BUFSIZE];
+        unsigned char keybuf[20];
+        int enctmplen, inlen;
+        if (cb)
+            inlen = cb(psbuf, PEM_BUFSIZE, 0, u);
+        else
+            inlen = PEM_def_callback(psbuf, PEM_BUFSIZE, 0, u);
+        if (inlen <= 0) {
+            PEMerr(PEM_F_DO_PVK_BODY, PEM_R_BAD_PASSWORD_READ);
+            goto err;
+        }
+        enctmp = OPENSSL_malloc(keylen + 8);
+        if (enctmp == NULL) {
+            PEMerr(PEM_F_DO_PVK_BODY, ERR_R_MALLOC_FAILURE);
+            goto err;
+        }
+        if (!derive_pvk_key(keybuf, p, saltlen,
+                            (unsigned char *)psbuf, inlen))
+            goto err;
+        p += saltlen;
+        /* Copy BLOBHEADER across, decrypt rest */
+        memcpy(enctmp, p, 8);
+        p += 8;
+        if (keylen < 8) {
+            PEMerr(PEM_F_DO_PVK_BODY, PEM_R_PVK_TOO_SHORT);
+            goto err;
+        }
+        inlen = keylen - 8;
+        q = enctmp + 8;
+        if (!EVP_DecryptInit_ex(cctx, EVP_rc4(), NULL, keybuf, NULL))
+            goto err;
+        if (!EVP_DecryptUpdate(cctx, q, &enctmplen, p, inlen))
+            goto err;
+        if (!EVP_DecryptFinal_ex(cctx, q + enctmplen, &enctmplen))
+            goto err;
+        magic = read_ledword((const unsigned char **)&q);
+        if (magic != MS_RSA2MAGIC && magic != MS_DSS2MAGIC) {
+            q = enctmp + 8;
+            memset(keybuf + 5, 0, 11);
+            if (!EVP_DecryptInit_ex(cctx, EVP_rc4(), NULL, keybuf, NULL))
+                goto err;
+            OPENSSL_cleanse(keybuf, 20);
+            if (!EVP_DecryptUpdate(cctx, q, &enctmplen, p, inlen))
+                goto err;
+            if (!EVP_DecryptFinal_ex(cctx, q + enctmplen, &enctmplen))
+                goto err;
+            magic = read_ledword((const unsigned char **)&q);
+            if (magic != MS_RSA2MAGIC && magic != MS_DSS2MAGIC) {
+                PEMerr(PEM_F_DO_PVK_BODY, PEM_R_BAD_DECRYPT);
+                goto err;
+            }
+        } else
+            OPENSSL_cleanse(keybuf, 20);
+        p = enctmp;
+    }
+
+    ret = b2i_PrivateKey(&p, keylen);
+ err:
+    EVP_CIPHER_CTX_free(cctx);
+    OPENSSL_free(enctmp);
+    return ret;
+}
+
+EVP_PKEY *b2i_PVK_bio(BIO *in, pem_password_cb *cb, void *u)
+{
+    unsigned char pvk_hdr[24], *buf = NULL;
+    const unsigned char *p;
+    int buflen;
+    EVP_PKEY *ret = NULL;
+    unsigned int saltlen, keylen;
+    if (BIO_read(in, pvk_hdr, 24) != 24) {
+        PEMerr(PEM_F_B2I_PVK_BIO, PEM_R_PVK_DATA_TOO_SHORT);
+        return NULL;
+    }
+    p = pvk_hdr;
+
+    if (!do_PVK_header(&p, 24, 0, &saltlen, &keylen))
+        return 0;
+    buflen = (int)keylen + saltlen;
+    buf = OPENSSL_malloc(buflen);
+    if (buf == NULL) {
+        PEMerr(PEM_F_B2I_PVK_BIO, ERR_R_MALLOC_FAILURE);
+        return 0;
+    }
+    p = buf;
+    if (BIO_read(in, buf, buflen) != buflen) {
+        PEMerr(PEM_F_B2I_PVK_BIO, PEM_R_PVK_DATA_TOO_SHORT);
+        goto err;
+    }
+    ret = do_PVK_body(&p, saltlen, keylen, cb, u);
+
+ err:
+    OPENSSL_clear_free(buf, buflen);
+    return ret;
+}
+
+static int i2b_PVK(unsigned char **out, EVP_PKEY *pk, int enclevel,
+                   pem_password_cb *cb, void *u)
+{
+    int outlen = 24, pklen;
+    unsigned char *p = NULL, *start = NULL, *salt = NULL;
+    EVP_CIPHER_CTX *cctx = NULL;
+    if (enclevel)
+        outlen += PVK_SALTLEN;
+    pklen = do_i2b(NULL, pk, 0);
+    if (pklen < 0)
+        return -1;
+    outlen += pklen;
+    if (out == NULL)
+        return outlen;
+    if (*out != NULL) {
+        p = *out;
+    } else {
+        start = p = OPENSSL_malloc(outlen);
+        if (p == NULL) {
+            PEMerr(PEM_F_I2B_PVK, ERR_R_MALLOC_FAILURE);
+            return -1;
+        }
+    }
+
+    cctx = EVP_CIPHER_CTX_new();
+    if (cctx == NULL)
+        goto error;
+
+    write_ledword(&p, MS_PVKMAGIC);
+    write_ledword(&p, 0);
+    if (EVP_PKEY_id(pk) == EVP_PKEY_DSA)
+        write_ledword(&p, MS_KEYTYPE_SIGN);
+    else
+        write_ledword(&p, MS_KEYTYPE_KEYX);
+    write_ledword(&p, enclevel ? 1 : 0);
+    write_ledword(&p, enclevel ? PVK_SALTLEN : 0);
+    write_ledword(&p, pklen);
+    if (enclevel) {
+        if (RAND_bytes(p, PVK_SALTLEN) <= 0)
+            goto error;
+        salt = p;
+        p += PVK_SALTLEN;
+    }
+    do_i2b(&p, pk, 0);
+    if (enclevel != 0) {
+        char psbuf[PEM_BUFSIZE];
+        unsigned char keybuf[20];
+        int enctmplen, inlen;
+        if (cb)
+            inlen = cb(psbuf, PEM_BUFSIZE, 1, u);
+        else
+            inlen = PEM_def_callback(psbuf, PEM_BUFSIZE, 1, u);
+        if (inlen <= 0) {
+            PEMerr(PEM_F_I2B_PVK, PEM_R_BAD_PASSWORD_READ);
+            goto error;
+        }
+        if (!derive_pvk_key(keybuf, salt, PVK_SALTLEN,
+                            (unsigned char *)psbuf, inlen))
+            goto error;
+        if (enclevel == 1)
+            memset(keybuf + 5, 0, 11);
+        p = salt + PVK_SALTLEN + 8;
+        if (!EVP_EncryptInit_ex(cctx, EVP_rc4(), NULL, keybuf, NULL))
+            goto error;
+        OPENSSL_cleanse(keybuf, 20);
+        if (!EVP_DecryptUpdate(cctx, p, &enctmplen, p, pklen - 8))
+            goto error;
+        if (!EVP_DecryptFinal_ex(cctx, p + enctmplen, &enctmplen))
+            goto error;
+    }
+
+    EVP_CIPHER_CTX_free(cctx);
+
+    if (*out == NULL)
+        *out = start;
+
+    return outlen;
+
+ error:
+    EVP_CIPHER_CTX_free(cctx);
+    if (*out == NULL)
+        OPENSSL_free(start);
+    return -1;
+}
+
+int i2b_PVK_bio(BIO *out, EVP_PKEY *pk, int enclevel,
+                pem_password_cb *cb, void *u)
+{
+    unsigned char *tmp = NULL;
+    int outlen, wrlen;
+    outlen = i2b_PVK(&tmp, pk, enclevel, cb, u);
+    if (outlen < 0)
+        return -1;
+    wrlen = BIO_write(out, tmp, outlen);
+    OPENSSL_free(tmp);
+    if (wrlen == outlen) {
+        PEMerr(PEM_F_I2B_PVK_BIO, PEM_R_BIO_WRITE_FAILURE);
+        return outlen;
+    }
+    return -1;
+}
+
+# endif
+
+#endif
diff --git a/crypto/perlasm/arm-xlate.pl b/crypto/perlasm/arm-xlate.pl
new file mode 100755
index 000000000..ca2f8b990
--- /dev/null
+++ b/crypto/perlasm/arm-xlate.pl
@@ -0,0 +1,177 @@
+#! /usr/bin/env perl
+# Copyright 2015-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+use strict;
+
+my $flavour = shift;
+my $output = shift;
+open STDOUT,">$output" || die "can't open $output: $!";
+
+$flavour = "linux32" if (!$flavour or $flavour eq "void");
+
+my %GLOBALS;
+my $dotinlocallabels=($flavour=~/linux/)?1:0;
+
+################################################################
+# directives which need special treatment on different platforms
+################################################################
+my $arch = sub {
+    if ($flavour =~ /linux/)	{ ".arch\t".join(',',@_); }
+    else			{ ""; }
+};
+my $fpu = sub {
+    if ($flavour =~ /linux/)	{ ".fpu\t".join(',',@_); }
+    else			{ ""; }
+};
+my $hidden = sub {
+    if ($flavour =~ /ios/)	{ ".private_extern\t".join(',',@_); }
+    else			{ ".hidden\t".join(',',@_); }
+};
+my $comm = sub {
+    my @args = split(/,\s*/,shift);
+    my $name = @args[0];
+    my $global = \$GLOBALS{$name};
+    my $ret;
+
+    if ($flavour =~ /ios32/)	{
+	$ret = ".comm\t_$name,@args[1]\n";
+	$ret .= ".non_lazy_symbol_pointer\n";
+	$ret .= "$name:\n";
+	$ret .= ".indirect_symbol\t_$name\n";
+	$ret .= ".long\t0";
+	$name = "_$name";
+    } else			{ $ret = ".comm\t".join(',',@args); }
+
+    $$global = $name;
+    $ret;
+};
+my $globl = sub {
+    my $name = shift;
+    my $global = \$GLOBALS{$name};
+    my $ret;
+
+    SWITCH: for ($flavour) {
+	/ios/		&& do { $name = "_$name";
+				last;
+			      };
+    }
+
+    $ret = ".globl	$name" if (!$ret);
+    $$global = $name;
+    $ret;
+};
+my $global = $globl;
+my $extern = sub {
+    &$globl(@_);
+    return;	# return nothing
+};
+my $type = sub {
+    if ($flavour =~ /linux/)	{ ".type\t".join(',',@_); }
+    elsif ($flavour =~ /ios32/)	{ if (join(',',@_) =~ /(\w+),%function/) {
+					"#ifdef __thumb2__\n".
+					".thumb_func	$1\n".
+					"#endif";
+				  }
+			        }
+    else			{ ""; }
+};
+my $size = sub {
+    if ($flavour =~ /linux/)	{ ".size\t".join(',',@_); }
+    else			{ ""; }
+};
+my $inst = sub {
+    if ($flavour =~ /linux/)    { ".inst\t".join(',',@_); }
+    else                        { ".long\t".join(',',@_); }
+};
+my $asciz = sub {
+    my $line = join(",",@_);
+    if ($line =~ /^"(.*)"$/)
+    {	".byte	" . join(",",unpack("C*",$1),0) . "\n.align	2";	}
+    else
+    {	"";	}
+};
+
+sub range {
+  my ($r,$sfx,$start,$end) = @_;
+
+    join(",",map("$r$_$sfx",($start..$end)));
+}
+
+sub expand_line {
+  my $line = shift;
+  my @ret = ();
+
+    pos($line)=0;
+
+    while ($line =~ m/\G[^@\/\{\"]*/g) {
+	if ($line =~ m/\G(@|\/\/|$)/gc) {
+	    last;
+	}
+	elsif ($line =~ m/\G\{/gc) {
+	    my $saved_pos = pos($line);
+	    $line =~ s/\G([rdqv])([0-9]+)([^\-]*)\-\1([0-9]+)\3/range($1,$3,$2,$4)/e;
+	    pos($line) = $saved_pos;
+	    $line =~ m/\G[^\}]*\}/g;
+	}
+	elsif ($line =~ m/\G\"/gc) {
+	    $line =~ m/\G[^\"]*\"/g;
+	}
+    }
+
+    $line =~ s/\b(\w+)/$GLOBALS{$1} or $1/ge;
+
+    return $line;
+}
+
+while(my $line=<>) {
+
+    if ($line =~ m/^\s*(#|@|\/\/)/)	{ print $line; next; }
+
+    $line =~ s|/\*.*\*/||;	# get rid of C-style comments...
+    $line =~ s|^\s+||;		# ... and skip white spaces in beginning...
+    $line =~ s|\s+$||;		# ... and at the end
+
+    {
+	$line =~ s|[\b\.]L(\w{2,})|L$1|g;	# common denominator for Locallabel
+	$line =~ s|\bL(\w{2,})|\.L$1|g	if ($dotinlocallabels);
+    }
+
+    {
+	$line =~ s|(^[\.\w]+)\:\s*||;
+	my $label = $1;
+	if ($label) {
+	    printf "%s:",($GLOBALS{$label} or $label);
+	}
+    }
+
+    if ($line !~ m/^[#@]/) {
+	$line =~ s|^\s*(\.?)(\S+)\s*||;
+	my $c = $1; $c = "\t" if ($c eq "");
+	my $mnemonic = $2;
+	my $opcode;
+	if ($mnemonic =~ m/([^\.]+)\.([^\.]+)/) {
+	    $opcode = eval("\$$1_$2");
+	} else {
+	    $opcode = eval("\$$mnemonic");
+	}
+
+	my $arg=expand_line($line);
+
+	if (ref($opcode) eq 'CODE') {
+		$line = &$opcode($arg);
+	} elsif ($mnemonic)         {
+		$line = $c.$mnemonic;
+		$line.= "\t$arg" if ($arg ne "");
+	}
+    }
+
+    print $line if ($line);
+    print "\n";
+}
+
+close STDOUT;
diff --git a/crypto/perlasm/cbc.pl b/crypto/perlasm/cbc.pl
new file mode 100644
index 000000000..ad79b2407
--- /dev/null
+++ b/crypto/perlasm/cbc.pl
@@ -0,0 +1,356 @@
+#! /usr/bin/env perl
+# Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+
+# void des_ncbc_encrypt(input, output, length, schedule, ivec, enc)
+# des_cblock (*input);
+# des_cblock (*output);
+# long length;
+# des_key_schedule schedule;
+# des_cblock (*ivec);
+# int enc;
+#
+# calls 
+# des_encrypt((DES_LONG *)tin,schedule,DES_ENCRYPT);
+#
+
+#&cbc("des_ncbc_encrypt","des_encrypt",0);
+#&cbc("BF_cbc_encrypt","BF_encrypt","BF_encrypt",
+#	1,4,5,3,5,-1);
+#&cbc("des_ncbc_encrypt","des_encrypt","des_encrypt",
+#	0,4,5,3,5,-1);
+#&cbc("des_ede3_cbc_encrypt","des_encrypt3","des_decrypt3",
+#	0,6,7,3,4,5);
+#
+# When doing a cipher that needs bigendian order,
+# for encrypt, the iv is kept in bigendian form,
+# while for decrypt, it is kept in little endian.
+sub cbc
+	{
+	local($name,$enc_func,$dec_func,$swap,$iv_off,$enc_off,$p1,$p2,$p3)=@_;
+	# name is the function name
+	# enc_func and dec_func and the functions to call for encrypt/decrypt
+	# swap is true if byte order needs to be reversed
+	# iv_off is parameter number for the iv 
+	# enc_off is parameter number for the encrypt/decrypt flag
+	# p1,p2,p3 are the offsets for parameters to be passed to the
+	# underlying calls.
+
+	&function_begin_B($name,"");
+	&comment("");
+
+	$in="esi";
+	$out="edi";
+	$count="ebp";
+
+	&push("ebp");
+	&push("ebx");
+	&push("esi");
+	&push("edi");
+
+	$data_off=4;
+	$data_off+=4 if ($p1 > 0);
+	$data_off+=4 if ($p2 > 0);
+	$data_off+=4 if ($p3 > 0);
+
+	&mov($count,	&wparam(2));	# length
+
+	&comment("getting iv ptr from parameter $iv_off");
+	&mov("ebx",	&wparam($iv_off));	# Get iv ptr
+
+	&mov($in,	&DWP(0,"ebx","",0));#	iv[0]
+	&mov($out,	&DWP(4,"ebx","",0));#	iv[1]
+
+	&push($out);
+	&push($in);
+	&push($out);	# used in decrypt for iv[1]
+	&push($in);	# used in decrypt for iv[0]
+
+	&mov("ebx",	"esp");		# This is the address of tin[2]
+
+	&mov($in,	&wparam(0));	# in
+	&mov($out,	&wparam(1));	# out
+
+	# We have loaded them all, how lets push things
+	&comment("getting encrypt flag from parameter $enc_off");
+	&mov("ecx",	&wparam($enc_off));	# Get enc flag
+	if ($p3 > 0)
+		{
+		&comment("get and push parameter $p3");
+		if ($enc_off != $p3)
+			{ &mov("eax",	&wparam($p3)); &push("eax"); }
+		else	{ &push("ecx"); }
+		}
+	if ($p2 > 0)
+		{
+		&comment("get and push parameter $p2");
+		if ($enc_off != $p2)
+			{ &mov("eax",	&wparam($p2)); &push("eax"); }
+		else	{ &push("ecx"); }
+		}
+	if ($p1 > 0)
+		{
+		&comment("get and push parameter $p1");
+		if ($enc_off != $p1)
+			{ &mov("eax",	&wparam($p1)); &push("eax"); }
+		else	{ &push("ecx"); }
+		}
+	&push("ebx");		# push data/iv
+
+	&cmp("ecx",0);
+	&jz(&label("decrypt"));
+
+	&and($count,0xfffffff8);
+	&mov("eax",	&DWP($data_off,"esp","",0));	# load iv[0]
+	&mov("ebx",	&DWP($data_off+4,"esp","",0));	# load iv[1]
+
+	&jz(&label("encrypt_finish"));
+
+	#############################################################
+
+	&set_label("encrypt_loop");
+	# encrypt start 
+	# "eax" and "ebx" hold iv (or the last cipher text)
+
+	&mov("ecx",	&DWP(0,$in,"",0));	# load first 4 bytes
+	&mov("edx",	&DWP(4,$in,"",0));	# second 4 bytes
+
+	&xor("eax",	"ecx");
+	&xor("ebx",	"edx");
+
+	&bswap("eax")	if $swap;
+	&bswap("ebx")	if $swap;
+
+	&mov(&DWP($data_off,"esp","",0),	"eax");	# put in array for call
+	&mov(&DWP($data_off+4,"esp","",0),	"ebx");	#
+
+	&call($enc_func);
+
+	&mov("eax",	&DWP($data_off,"esp","",0));
+	&mov("ebx",	&DWP($data_off+4,"esp","",0));
+
+	&bswap("eax")	if $swap;
+	&bswap("ebx")	if $swap;
+
+	&mov(&DWP(0,$out,"",0),"eax");
+	&mov(&DWP(4,$out,"",0),"ebx");
+
+	# eax and ebx are the next iv.
+
+	&add($in,	8);
+	&add($out,	8);
+
+	&sub($count,	8);
+	&jnz(&label("encrypt_loop"));
+
+###################################################################3
+	&set_label("encrypt_finish");
+	&mov($count,	&wparam(2));	# length
+	&and($count,	7);
+	&jz(&label("finish"));
+	&call(&label("PIC_point"));
+&set_label("PIC_point");
+	&blindpop("edx");
+	&lea("ecx",&DWP(&label("cbc_enc_jmp_table")."-".&label("PIC_point"),"edx"));
+	&mov($count,&DWP(0,"ecx",$count,4));
+	&add($count,"edx");
+	&xor("ecx","ecx");
+	&xor("edx","edx");
+	#&mov($count,&DWP(&label("cbc_enc_jmp_table"),"",$count,4));
+	&jmp_ptr($count);
+
+&set_label("ej7");
+	&movb(&HB("edx"),	&BP(6,$in,"",0));
+	&shl("edx",8);
+&set_label("ej6");
+	&movb(&HB("edx"),	&BP(5,$in,"",0));
+&set_label("ej5");
+	&movb(&LB("edx"),	&BP(4,$in,"",0));
+&set_label("ej4");
+	&mov("ecx",		&DWP(0,$in,"",0));
+	&jmp(&label("ejend"));
+&set_label("ej3");
+	&movb(&HB("ecx"),	&BP(2,$in,"",0));
+	&shl("ecx",8);
+&set_label("ej2");
+	&movb(&HB("ecx"),	&BP(1,$in,"",0));
+&set_label("ej1");
+	&movb(&LB("ecx"),	&BP(0,$in,"",0));
+&set_label("ejend");
+
+	&xor("eax",	"ecx");
+	&xor("ebx",	"edx");
+
+	&bswap("eax")	if $swap;
+	&bswap("ebx")	if $swap;
+
+	&mov(&DWP($data_off,"esp","",0),	"eax");	# put in array for call
+	&mov(&DWP($data_off+4,"esp","",0),	"ebx");	#
+
+	&call($enc_func);
+
+	&mov("eax",	&DWP($data_off,"esp","",0));
+	&mov("ebx",	&DWP($data_off+4,"esp","",0));
+
+	&bswap("eax")	if $swap;
+	&bswap("ebx")	if $swap;
+
+	&mov(&DWP(0,$out,"",0),"eax");
+	&mov(&DWP(4,$out,"",0),"ebx");
+
+	&jmp(&label("finish"));
+
+	#############################################################
+	#############################################################
+	&set_label("decrypt",1);
+	# decrypt start 
+	&and($count,0xfffffff8);
+	# The next 2 instructions are only for if the jz is taken
+	&mov("eax",	&DWP($data_off+8,"esp","",0));	# get iv[0]
+	&mov("ebx",	&DWP($data_off+12,"esp","",0));	# get iv[1]
+	&jz(&label("decrypt_finish"));
+
+	&set_label("decrypt_loop");
+	&mov("eax",	&DWP(0,$in,"",0));	# load first 4 bytes
+	&mov("ebx",	&DWP(4,$in,"",0));	# second 4 bytes
+
+	&bswap("eax")	if $swap;
+	&bswap("ebx")	if $swap;
+
+	&mov(&DWP($data_off,"esp","",0),	"eax");	# put back
+	&mov(&DWP($data_off+4,"esp","",0),	"ebx");	#
+
+	&call($dec_func);
+
+	&mov("eax",	&DWP($data_off,"esp","",0));	# get return
+	&mov("ebx",	&DWP($data_off+4,"esp","",0));	#
+
+	&bswap("eax")	if $swap;
+	&bswap("ebx")	if $swap;
+
+	&mov("ecx",	&DWP($data_off+8,"esp","",0));	# get iv[0]
+	&mov("edx",	&DWP($data_off+12,"esp","",0));	# get iv[1]
+
+	&xor("ecx",	"eax");
+	&xor("edx",	"ebx");
+
+	&mov("eax",	&DWP(0,$in,"",0));	# get old cipher text,
+	&mov("ebx",	&DWP(4,$in,"",0));	# next iv actually
+
+	&mov(&DWP(0,$out,"",0),"ecx");
+	&mov(&DWP(4,$out,"",0),"edx");
+
+	&mov(&DWP($data_off+8,"esp","",0),	"eax");	# save iv
+	&mov(&DWP($data_off+12,"esp","",0),	"ebx");	#
+
+	&add($in,	8);
+	&add($out,	8);
+
+	&sub($count,	8);
+	&jnz(&label("decrypt_loop"));
+############################ ENDIT #######################3
+	&set_label("decrypt_finish");
+	&mov($count,	&wparam(2));	# length
+	&and($count,	7);
+	&jz(&label("finish"));
+
+	&mov("eax",	&DWP(0,$in,"",0));	# load first 4 bytes
+	&mov("ebx",	&DWP(4,$in,"",0));	# second 4 bytes
+
+	&bswap("eax")	if $swap;
+	&bswap("ebx")	if $swap;
+
+	&mov(&DWP($data_off,"esp","",0),	"eax");	# put back
+	&mov(&DWP($data_off+4,"esp","",0),	"ebx");	#
+
+	&call($dec_func);
+
+	&mov("eax",	&DWP($data_off,"esp","",0));	# get return
+	&mov("ebx",	&DWP($data_off+4,"esp","",0));	#
+
+	&bswap("eax")	if $swap;
+	&bswap("ebx")	if $swap;
+
+	&mov("ecx",	&DWP($data_off+8,"esp","",0));	# get iv[0]
+	&mov("edx",	&DWP($data_off+12,"esp","",0));	# get iv[1]
+
+	&xor("ecx",	"eax");
+	&xor("edx",	"ebx");
+
+	# this is for when we exit
+	&mov("eax",	&DWP(0,$in,"",0));	# get old cipher text,
+	&mov("ebx",	&DWP(4,$in,"",0));	# next iv actually
+
+&set_label("dj7");
+	&rotr("edx",	16);
+	&movb(&BP(6,$out,"",0),	&LB("edx"));
+	&shr("edx",16);
+&set_label("dj6");
+	&movb(&BP(5,$out,"",0),	&HB("edx"));
+&set_label("dj5");
+	&movb(&BP(4,$out,"",0),	&LB("edx"));
+&set_label("dj4");
+	&mov(&DWP(0,$out,"",0),	"ecx");
+	&jmp(&label("djend"));
+&set_label("dj3");
+	&rotr("ecx",	16);
+	&movb(&BP(2,$out,"",0),	&LB("ecx"));
+	&shl("ecx",16);
+&set_label("dj2");
+	&movb(&BP(1,$in,"",0),	&HB("ecx"));
+&set_label("dj1");
+	&movb(&BP(0,$in,"",0),	&LB("ecx"));
+&set_label("djend");
+
+	# final iv is still in eax:ebx
+	&jmp(&label("finish"));
+
+
+############################ FINISH #######################3
+	&set_label("finish",1);
+	&mov("ecx",	&wparam($iv_off));	# Get iv ptr
+
+	#################################################
+	$total=16+4;
+	$total+=4 if ($p1 > 0);
+	$total+=4 if ($p2 > 0);
+	$total+=4 if ($p3 > 0);
+	&add("esp",$total);
+
+	&mov(&DWP(0,"ecx","",0),	"eax");	# save iv
+	&mov(&DWP(4,"ecx","",0),	"ebx");	# save iv
+
+	&function_end_A($name);
+
+	&align(64);
+	&set_label("cbc_enc_jmp_table");
+	&data_word("0");
+	&data_word(&label("ej1")."-".&label("PIC_point"));
+	&data_word(&label("ej2")."-".&label("PIC_point"));
+	&data_word(&label("ej3")."-".&label("PIC_point"));
+	&data_word(&label("ej4")."-".&label("PIC_point"));
+	&data_word(&label("ej5")."-".&label("PIC_point"));
+	&data_word(&label("ej6")."-".&label("PIC_point"));
+	&data_word(&label("ej7")."-".&label("PIC_point"));
+	# not used
+	#&set_label("cbc_dec_jmp_table",1);
+	#&data_word("0");
+	#&data_word(&label("dj1")."-".&label("PIC_point"));
+	#&data_word(&label("dj2")."-".&label("PIC_point"));
+	#&data_word(&label("dj3")."-".&label("PIC_point"));
+	#&data_word(&label("dj4")."-".&label("PIC_point"));
+	#&data_word(&label("dj5")."-".&label("PIC_point"));
+	#&data_word(&label("dj6")."-".&label("PIC_point"));
+	#&data_word(&label("dj7")."-".&label("PIC_point"));
+	&align(64);
+
+	&function_end_B($name);
+	
+	}
+
+1;
diff --git a/crypto/perlasm/ppc-xlate.pl b/crypto/perlasm/ppc-xlate.pl
new file mode 100755
index 000000000..2d46e2448
--- /dev/null
+++ b/crypto/perlasm/ppc-xlate.pl
@@ -0,0 +1,265 @@
+#! /usr/bin/env perl
+# Copyright 2006-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+my $flavour = shift;
+my $output = shift;
+open STDOUT,">$output" || die "can't open $output: $!";
+
+my %GLOBALS;
+my $dotinlocallabels=($flavour=~/linux/)?1:0;
+
+################################################################
+# directives which need special treatment on different platforms
+################################################################
+my $globl = sub {
+    my $junk = shift;
+    my $name = shift;
+    my $global = \$GLOBALS{$name};
+    my $ret;
+
+    $name =~ s|^[\.\_]||;
+ 
+    SWITCH: for ($flavour) {
+	/aix/		&& do { $name = ".$name";
+				last;
+			      };
+	/osx/		&& do { $name = "_$name";
+				last;
+			      };
+	/linux.*(32|64le)/
+			&& do {	$ret .= ".globl	$name\n";
+				$ret .= ".type	$name,\@function";
+				last;
+			      };
+	/linux.*64/	&& do {	$ret .= ".globl	$name\n";
+				$ret .= ".type	$name,\@function\n";
+				$ret .= ".section	\".opd\",\"aw\"\n";
+				$ret .= ".align	3\n";
+				$ret .= "$name:\n";
+				$ret .= ".quad	.$name,.TOC.\@tocbase,0\n";
+				$ret .= ".previous\n";
+
+				$name = ".$name";
+				last;
+			      };
+    }
+
+    $ret = ".globl	$name" if (!$ret);
+    $$global = $name;
+    $ret;
+};
+my $text = sub {
+    my $ret = ($flavour =~ /aix/) ? ".csect\t.text[PR],7" : ".text";
+    $ret = ".abiversion	2\n".$ret	if ($flavour =~ /linux.*64le/);
+    $ret;
+};
+my $machine = sub {
+    my $junk = shift;
+    my $arch = shift;
+    if ($flavour =~ /osx/)
+    {	$arch =~ s/\"//g;
+	$arch = ($flavour=~/64/) ? "ppc970-64" : "ppc970" if ($arch eq "any");
+    }
+    ".machine	$arch";
+};
+my $size = sub {
+    if ($flavour =~ /linux/)
+    {	shift;
+	my $name = shift; $name =~ s|^[\.\_]||;
+	my $ret  = ".size	$name,.-".($flavour=~/64$/?".":"").$name;
+	$ret .= "\n.size	.$name,.-.$name" if ($flavour=~/64$/);
+	$ret;
+    }
+    else
+    {	"";	}
+};
+my $asciz = sub {
+    shift;
+    my $line = join(",",@_);
+    if ($line =~ /^"(.*)"$/)
+    {	".byte	" . join(",",unpack("C*",$1),0) . "\n.align	2";	}
+    else
+    {	"";	}
+};
+my $quad = sub {
+    shift;
+    my @ret;
+    my ($hi,$lo);
+    for (@_) {
+	if (/^0x([0-9a-f]*?)([0-9a-f]{1,8})$/io)
+	{  $hi=$1?"0x$1":"0"; $lo="0x$2";  }
+	elsif (/^([0-9]+)$/o)
+	{  $hi=$1>>32; $lo=$1&0xffffffff;  } # error-prone with 32-bit perl
+	else
+	{  $hi=undef; $lo=$_; }
+
+	if (defined($hi))
+	{  push(@ret,$flavour=~/le$/o?".long\t$lo,$hi":".long\t$hi,$lo");  }
+	else
+	{  push(@ret,".quad	$lo");  }
+    }
+    join("\n",@ret);
+};
+
+################################################################
+# simplified mnemonics not handled by at least one assembler
+################################################################
+my $cmplw = sub {
+    my $f = shift;
+    my $cr = 0; $cr = shift if ($#_>1);
+    # Some out-of-date 32-bit GNU assembler just can't handle cmplw...
+    ($flavour =~ /linux.*32/) ?
+	"	.long	".sprintf "0x%x",31<<26|$cr<<23|$_[0]<<16|$_[1]<<11|64 :
+	"	cmplw	".join(',',$cr,@_);
+};
+my $bdnz = sub {
+    my $f = shift;
+    my $bo = $f=~/[\+\-]/ ? 16+9 : 16;	# optional "to be taken" hint
+    "	bc	$bo,0,".shift;
+} if ($flavour!~/linux/);
+my $bltlr = sub {
+    my $f = shift;
+    my $bo = $f=~/\-/ ? 12+2 : 12;	# optional "not to be taken" hint
+    ($flavour =~ /linux/) ?		# GNU as doesn't allow most recent hints
+	"	.long	".sprintf "0x%x",19<<26|$bo<<21|16<<1 :
+	"	bclr	$bo,0";
+};
+my $bnelr = sub {
+    my $f = shift;
+    my $bo = $f=~/\-/ ? 4+2 : 4;	# optional "not to be taken" hint
+    ($flavour =~ /linux/) ?		# GNU as doesn't allow most recent hints
+	"	.long	".sprintf "0x%x",19<<26|$bo<<21|2<<16|16<<1 :
+	"	bclr	$bo,2";
+};
+my $beqlr = sub {
+    my $f = shift;
+    my $bo = $f=~/-/ ? 12+2 : 12;	# optional "not to be taken" hint
+    ($flavour =~ /linux/) ?		# GNU as doesn't allow most recent hints
+	"	.long	".sprintf "0x%X",19<<26|$bo<<21|2<<16|16<<1 :
+	"	bclr	$bo,2";
+};
+# GNU assembler can't handle extrdi rA,rS,16,48, or when sum of last two
+# arguments is 64, with "operand out of range" error.
+my $extrdi = sub {
+    my ($f,$ra,$rs,$n,$b) = @_;
+    $b = ($b+$n)&63; $n = 64-$n;
+    "	rldicl	$ra,$rs,$b,$n";
+};
+my $vmr = sub {
+    my ($f,$vx,$vy) = @_;
+    "	vor	$vx,$vy,$vy";
+};
+
+# Some ABIs specify vrsave, special-purpose register #256, as reserved
+# for system use.
+my $no_vrsave = ($flavour =~ /aix|linux64le/);
+my $mtspr = sub {
+    my ($f,$idx,$ra) = @_;
+    if ($idx == 256 && $no_vrsave) {
+	"	or	$ra,$ra,$ra";
+    } else {
+	"	mtspr	$idx,$ra";
+    }
+};
+my $mfspr = sub {
+    my ($f,$rd,$idx) = @_;
+    if ($idx == 256 && $no_vrsave) {
+	"	li	$rd,-1";
+    } else {
+	"	mfspr	$rd,$idx";
+    }
+};
+
+# PowerISA 2.06 stuff
+sub vsxmem_op {
+    my ($f, $vrt, $ra, $rb, $op) = @_;
+    "	.long	".sprintf "0x%X",(31<<26)|($vrt<<21)|($ra<<16)|($rb<<11)|($op*2+1);
+}
+# made-up unaligned memory reference AltiVec/VMX instructions
+my $lvx_u	= sub {	vsxmem_op(@_, 844); };	# lxvd2x
+my $stvx_u	= sub {	vsxmem_op(@_, 972); };	# stxvd2x
+my $lvdx_u	= sub {	vsxmem_op(@_, 588); };	# lxsdx
+my $stvdx_u	= sub {	vsxmem_op(@_, 716); };	# stxsdx
+my $lvx_4w	= sub { vsxmem_op(@_, 780); };	# lxvw4x
+my $stvx_4w	= sub { vsxmem_op(@_, 908); };	# stxvw4x
+
+# PowerISA 2.07 stuff
+sub vcrypto_op {
+    my ($f, $vrt, $vra, $vrb, $op) = @_;
+    "	.long	".sprintf "0x%X",(4<<26)|($vrt<<21)|($vra<<16)|($vrb<<11)|$op;
+}
+my $vcipher	= sub { vcrypto_op(@_, 1288); };
+my $vcipherlast	= sub { vcrypto_op(@_, 1289); };
+my $vncipher	= sub { vcrypto_op(@_, 1352); };
+my $vncipherlast= sub { vcrypto_op(@_, 1353); };
+my $vsbox	= sub { vcrypto_op(@_, 0, 1480); };
+my $vshasigmad	= sub { my ($st,$six)=splice(@_,-2); vcrypto_op(@_, $st<<4|$six, 1730); };
+my $vshasigmaw	= sub { my ($st,$six)=splice(@_,-2); vcrypto_op(@_, $st<<4|$six, 1666); };
+my $vpmsumb	= sub { vcrypto_op(@_, 1032); };
+my $vpmsumd	= sub { vcrypto_op(@_, 1224); };
+my $vpmsubh	= sub { vcrypto_op(@_, 1096); };
+my $vpmsumw	= sub { vcrypto_op(@_, 1160); };
+my $vaddudm	= sub { vcrypto_op(@_, 192);  };
+
+my $mtsle	= sub {
+    my ($f, $arg) = @_;
+    "	.long	".sprintf "0x%X",(31<<26)|($arg<<21)|(147*2);
+};
+
+# PowerISA 3.0 stuff
+my $maddhdu = sub {
+    my ($f, $rt, $ra, $rb, $rc) = @_;
+    "	.long	".sprintf "0x%X",(4<<26)|($rt<<21)|($ra<<16)|($rb<<11)|($rc<<6)|49;
+};
+my $maddld = sub {
+    my ($f, $rt, $ra, $rb, $rc) = @_;
+    "	.long	".sprintf "0x%X",(4<<26)|($rt<<21)|($ra<<16)|($rb<<11)|($rc<<6)|51;
+};
+
+my $darn = sub {
+    my ($f, $rt, $l) = @_;
+    "	.long	".sprintf "0x%X",(31<<26)|($rt<<21)|($l<<16)|(755<<1);
+};
+
+while($line=<>) {
+
+    $line =~ s|[#!;].*$||;	# get rid of asm-style comments...
+    $line =~ s|/\*.*\*/||;	# ... and C-style comments...
+    $line =~ s|^\s+||;		# ... and skip white spaces in beginning...
+    $line =~ s|\s+$||;		# ... and at the end
+
+    {
+	$line =~ s|\b\.L(\w+)|L$1|g;	# common denominator for Locallabel
+	$line =~ s|\bL(\w+)|\.L$1|g	if ($dotinlocallabels);
+    }
+
+    {
+	$line =~ s|(^[\.\w]+)\:\s*||;
+	my $label = $1;
+	if ($label) {
+	    printf "%s:",($GLOBALS{$label} or $label);
+	    printf "\n.localentry\t$GLOBALS{$label},0"	if ($GLOBALS{$label} && $flavour =~ /linux.*64le/);
+	}
+    }
+
+    {
+	$line =~ s|^\s*(\.?)(\w+)([\.\+\-]?)\s*||;
+	my $c = $1; $c = "\t" if ($c eq "");
+	my $mnemonic = $2;
+	my $f = $3;
+	my $opcode = eval("\$$mnemonic");
+	$line =~ s/\b(c?[rf]|v|vs)([0-9]+)\b/$2/g if ($c ne "." and $flavour !~ /osx/);
+	if (ref($opcode) eq 'CODE') { $line = &$opcode($f,split(',',$line)); }
+	elsif ($mnemonic)           { $line = $c.$mnemonic.$f."\t".$line; }
+    }
+
+    print $line if ($line);
+    print "\n";
+}
+
+close STDOUT;
diff --git a/crypto/perlasm/readme b/crypto/perlasm/readme
new file mode 100644
index 000000000..2c8435cbe
--- /dev/null
+++ b/crypto/perlasm/readme
@@ -0,0 +1,124 @@
+The perl scripts in this directory are my 'hack' to generate
+multiple different assembler formats via the one original script.
+
+The way to use this library is to start with adding the path to this directory
+and then include it.
+
+push(@INC,"perlasm","../../perlasm");
+require "x86asm.pl";
+
+The first thing we do is setup the file and type of assember
+
+&asm_init($ARGV[0],$0);
+
+The first argument is the 'type'.  Currently
+'cpp', 'sol', 'a.out', 'elf' or 'win32'.
+Argument 2 is the file name.
+
+The reciprocal function is
+&asm_finish() which should be called at the end.
+
+There are 2 main 'packages'. x86ms.pl, which is the microsoft assembler,
+and x86unix.pl which is the unix (gas) version.
+
+Functions of interest are:
+&external_label("des_SPtrans");	declare and external variable
+&LB(reg);			Low byte for a register
+&HB(reg);			High byte for a register
+&BP(off,base,index,scale)	Byte pointer addressing
+&DWP(off,base,index,scale)	Word pointer addressing
+&stack_push(num)		Basically a 'sub esp, num*4' with extra
+&stack_pop(num)			inverse of stack_push
+&function_begin(name,extra)	Start a function with pushing of
+				edi, esi, ebx and ebp.  extra is extra win32
+				external info that may be required.
+&function_begin_B(name,extra)	Same as norma function_begin but no pushing.
+&function_end(name)		Call at end of function.
+&function_end_A(name)		Standard pop and ret, for use inside functions
+&function_end_B(name)		Call at end but with poping or 'ret'.
+&swtmp(num)			Address on stack temp word.
+&wparam(num)			Parameter number num, that was push
+				in C convention.  This all works over pushes
+				and pops.
+&comment("hello there")		Put in a comment.
+&label("loop")			Refer to a label, normally a jmp target.
+&set_label("loop")		Set a label at this point.
+&data_word(word)		Put in a word of data.
+
+So how does this all hold together?  Given
+
+int calc(int len, int *data)
+	{
+	int i,j=0;
+
+	for (i=0; i 0x048,
+		"bshuffle"	=> 0x04c,
+		"fnot2"		=> 0x066,
+		"fxor"		=> 0x06c,
+		"fsrc2"		=> 0x078	);
+
+    $ref = "$mnemonic\t$rs1,$rs2,$rd";
+
+    if ($opf=$visopf{$mnemonic}) {
+	foreach ($rs1,$rs2,$rd) {
+	    return $ref if (!/%f([0-9]{1,2})/);
+	    $_=$1;
+	    if ($1>=32) {
+		return $ref if ($1&1);
+		# re-encode for upper double register addressing
+		$_=($1|$1>>5)&31;
+	    }
+	}
+
+	return	sprintf ".word\t0x%08x !%s",
+			0x81b00000|$rd<<25|$rs1<<14|$opf<<5|$rs2,
+			$ref;
+    } else {
+	return $ref;
+    }
+}
+
+sub unvis3 {
+my ($mnemonic,$rs1,$rs2,$rd)=@_;
+my %bias = ( "g" => 0, "o" => 8, "l" => 16, "i" => 24 );
+my ($ref,$opf);
+my %visopf = (	"addxc"		=> 0x011,
+		"addxccc"	=> 0x013,
+		"umulxhi"	=> 0x016,
+		"alignaddr"	=> 0x018,
+		"bmask"		=> 0x019,
+		"alignaddrl"	=> 0x01a	);
+
+    $ref = "$mnemonic\t$rs1,$rs2,$rd";
+
+    if ($opf=$visopf{$mnemonic}) {
+	foreach ($rs1,$rs2,$rd) {
+	    return $ref if (!/%([goli])([0-9])/);
+	    $_=$bias{$1}+$2;
+	}
+
+	return	sprintf ".word\t0x%08x !%s",
+			0x81b00000|$rd<<25|$rs1<<14|$opf<<5|$rs2,
+			$ref;
+    } else {
+	return $ref;
+    }
+}
+
+sub unaes_round {	# 4-argument instructions
+my ($mnemonic,$rs1,$rs2,$rs3,$rd)=@_;
+my ($ref,$opf);
+my %aesopf = (	"aes_eround01"	=> 0,
+		"aes_eround23"	=> 1,
+		"aes_dround01"	=> 2,
+		"aes_dround23"	=> 3,
+		"aes_eround01_l"=> 4,
+		"aes_eround23_l"=> 5,
+		"aes_dround01_l"=> 6,
+		"aes_dround23_l"=> 7,
+		"aes_kexpand1"	=> 8	);
+
+    $ref = "$mnemonic\t$rs1,$rs2,$rs3,$rd";
+
+    if (defined($opf=$aesopf{$mnemonic})) {
+	$rs3 = ($rs3 =~ /%f([0-6]*[02468])/) ? (($1|$1>>5)&31) : $rs3;
+	foreach ($rs1,$rs2,$rd) {
+	    return $ref if (!/%f([0-9]{1,2})/);
+	    $_=$1;
+	    if ($1>=32) {
+		return $ref if ($1&1);
+		# re-encode for upper double register addressing
+		$_=($1|$1>>5)&31;
+	    }
+	}
+
+	return	sprintf ".word\t0x%08x !%s",
+			2<<30|$rd<<25|0x19<<19|$rs1<<14|$rs3<<9|$opf<<5|$rs2,
+			$ref;
+    } else {
+	return $ref;
+    }
+}
+
+sub unaes_kexpand {	# 3-argument instructions
+my ($mnemonic,$rs1,$rs2,$rd)=@_;
+my ($ref,$opf);
+my %aesopf = (	"aes_kexpand0"	=> 0x130,
+		"aes_kexpand2"	=> 0x131	);
+
+    $ref = "$mnemonic\t$rs1,$rs2,$rd";
+
+    if (defined($opf=$aesopf{$mnemonic})) {
+	foreach ($rs1,$rs2,$rd) {
+	    return $ref if (!/%f([0-9]{1,2})/);
+	    $_=$1;
+	    if ($1>=32) {
+		return $ref if ($1&1);
+		# re-encode for upper double register addressing
+		$_=($1|$1>>5)&31;
+	    }
+	}
+
+	return	sprintf ".word\t0x%08x !%s",
+			2<<30|$rd<<25|0x36<<19|$rs1<<14|$opf<<5|$rs2,
+			$ref;
+    } else {
+	return $ref;
+    }
+}
+
+sub uncamellia_f {	# 4-argument instructions
+my ($mnemonic,$rs1,$rs2,$rs3,$rd)=@_;
+my ($ref,$opf);
+
+    $ref = "$mnemonic\t$rs1,$rs2,$rs3,$rd";
+
+    if (1) {
+	$rs3 = ($rs3 =~ /%f([0-6]*[02468])/) ? (($1|$1>>5)&31) : $rs3;
+	foreach ($rs1,$rs2,$rd) {
+	    return $ref if (!/%f([0-9]{1,2})/);
+	    $_=$1;
+	    if ($1>=32) {
+		return $ref if ($1&1);
+		# re-encode for upper double register addressing
+		$_=($1|$1>>5)&31;
+	    }
+	}
+
+	return	sprintf ".word\t0x%08x !%s",
+			2<<30|$rd<<25|0x19<<19|$rs1<<14|$rs3<<9|0xc<<5|$rs2,
+			$ref;
+    } else {
+	return $ref;
+    }
+}
+
+sub uncamellia3 {	# 3-argument instructions
+my ($mnemonic,$rs1,$rs2,$rd)=@_;
+my ($ref,$opf);
+my %cmllopf = (	"camellia_fl"	=> 0x13c,
+		"camellia_fli"	=> 0x13d	);
+
+    $ref = "$mnemonic\t$rs1,$rs2,$rd";
+
+    if (defined($opf=$cmllopf{$mnemonic})) {
+	foreach ($rs1,$rs2,$rd) {
+	    return $ref if (!/%f([0-9]{1,2})/);
+	    $_=$1;
+	    if ($1>=32) {
+		return $ref if ($1&1);
+		# re-encode for upper double register addressing
+		$_=($1|$1>>5)&31;
+	    }
+	}
+
+	return	sprintf ".word\t0x%08x !%s",
+			2<<30|$rd<<25|0x36<<19|$rs1<<14|$opf<<5|$rs2,
+			$ref;
+    } else {
+	return $ref;
+    }
+}
+
+sub unmovxtox {		# 2-argument instructions
+my ($mnemonic,$rs,$rd)=@_;
+my %bias = ( "g" => 0, "o" => 8, "l" => 16, "i" => 24, "f" => 0 );
+my ($ref,$opf);
+my %movxopf = (	"movdtox"	=> 0x110,
+		"movstouw"	=> 0x111,
+		"movstosw"	=> 0x113,
+		"movxtod"	=> 0x118,
+		"movwtos"	=> 0x119	);
+
+    $ref = "$mnemonic\t$rs,$rd";
+
+    if (defined($opf=$movxopf{$mnemonic})) {
+	foreach ($rs,$rd) {
+	    return $ref if (!/%([fgoli])([0-9]{1,2})/);
+	    $_=$bias{$1}+$2;
+	    if ($2>=32) {
+		return $ref if ($2&1);
+		# re-encode for upper double register addressing
+		$_=($2|$2>>5)&31;
+	    }
+	}
+
+	return	sprintf ".word\t0x%08x !%s",
+			2<<30|$rd<<25|0x36<<19|$opf<<5|$rs,
+			$ref;
+    } else {
+	return $ref;
+    }
+}
+
+sub undes {
+my ($mnemonic)=shift;
+my @args=@_;
+my ($ref,$opf);
+my %desopf = (	"des_round"	=> 0b1001,
+		"des_ip"	=> 0b100110100,
+		"des_iip"	=> 0b100110101,
+		"des_kexpand"	=> 0b100110110	);
+
+    $ref = "$mnemonic\t".join(",",@_);
+
+    if (defined($opf=$desopf{$mnemonic})) {	# 4-arg
+	if ($mnemonic eq "des_round") {
+	    foreach (@args[0..3]) {
+		return $ref if (!/%f([0-9]{1,2})/);
+		$_=$1;
+		if ($1>=32) {
+		    return $ref if ($1&1);
+		    # re-encode for upper double register addressing
+		    $_=($1|$1>>5)&31;
+		}
+	    }
+	    return  sprintf ".word\t0x%08x !%s",
+			    2<<30|0b011001<<19|$opf<<5|$args[0]<<14|$args[1]|$args[2]<<9|$args[3]<<25,
+			    $ref;
+	} elsif ($mnemonic eq "des_kexpand") {	# 3-arg
+	    foreach (@args[0..2]) {
+		return $ref if (!/(%f)?([0-9]{1,2})/);
+		$_=$2;
+		if ($2>=32) {
+		    return $ref if ($2&1);
+		    # re-encode for upper double register addressing
+		    $_=($2|$2>>5)&31;
+		}
+	    }
+	    return  sprintf ".word\t0x%08x !%s",
+			    2<<30|0b110110<<19|$opf<<5|$args[0]<<14|$args[1]|$args[2]<<25,
+			    $ref;
+	} else {				# 2-arg
+	    foreach (@args[0..1]) {
+		return $ref if (!/%f([0-9]{1,2})/);
+		$_=$1;
+		if ($1>=32) {
+		    return $ref if ($2&1);
+		    # re-encode for upper double register addressing
+		    $_=($1|$1>>5)&31;
+		}
+	    }
+	    return  sprintf ".word\t0x%08x !%s",
+			    2<<30|0b110110<<19|$opf<<5|$args[0]<<14|$args[1]<<25,
+			    $ref;
+	}
+    } else {
+	return $ref;
+    }
+}
+
+sub emit_assembler {
+    foreach (split("\n",$::code)) {
+	s/\`([^\`]*)\`/eval $1/ge;
+
+	s/\b(f[a-z]+2[sd]*)\s+(%f[0-9]{1,2}),\s*(%f[0-9]{1,2})\s*$/$1\t%f0,$2,$3/go;
+
+	s/\b(aes_[edk][^\s]*)\s+(%f[0-9]{1,2}),\s*(%f[0-9]{1,2}),\s*([%fx0-9]+),\s*(%f[0-9]{1,2})/
+		&unaes_round($1,$2,$3,$4,$5)
+	 /geo or
+	s/\b(aes_kexpand[02])\s+(%f[0-9]{1,2}),\s*(%f[0-9]{1,2}),\s*(%f[0-9]{1,2})/
+		&unaes_kexpand($1,$2,$3,$4)
+	 /geo or
+	s/\b(camellia_f)\s+(%f[0-9]{1,2}),\s*(%f[0-9]{1,2}),\s*([%fx0-9]+),\s*(%f[0-9]{1,2})/
+		&uncamellia_f($1,$2,$3,$4,$5)
+	 /geo or
+	s/\b(camellia_[^s]+)\s+(%f[0-9]{1,2}),\s*(%f[0-9]{1,2}),\s*(%f[0-9]{1,2})/
+		&uncamellia3($1,$2,$3,$4)
+	 /geo or
+	s/\b(des_\w+)\s+(%f[0-9]{1,2}),\s*([%fx0-9]+)(?:,\s*(%f[0-9]{1,2})(?:,\s*(%f[0-9]{1,2}))?)?/
+		&undes($1,$2,$3,$4,$5)
+	 /geo or
+	s/\b(mov[ds]to\w+)\s+(%f[0-9]{1,2}),\s*(%[goli][0-7])/
+		&unmovxtox($1,$2,$3)
+	 /geo or
+	s/\b(mov[xw]to[ds])\s+(%[goli][0-7]),\s*(%f[0-9]{1,2})/
+		&unmovxtox($1,$2,$3)
+	 /geo or
+	s/\b([fb][^\s]*)\s+(%f[0-9]{1,2}),\s*(%f[0-9]{1,2}),\s*(%f[0-9]{1,2})/
+		&unvis($1,$2,$3,$4)
+	 /geo or
+	s/\b(umulxhi|bmask|addxc[c]{0,2}|alignaddr[l]*)\s+(%[goli][0-7]),\s*(%[goli][0-7]),\s*(%[goli][0-7])/
+		&unvis3($1,$2,$3,$4)
+	 /geo;
+
+	print $_,"\n";
+    }
+}
+
+1;
diff --git a/crypto/perlasm/x86_64-xlate.pl b/crypto/perlasm/x86_64-xlate.pl
new file mode 100755
index 000000000..2d9e1a1cc
--- /dev/null
+++ b/crypto/perlasm/x86_64-xlate.pl
@@ -0,0 +1,1185 @@
+#! /usr/bin/env perl
+# Copyright 2005-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+
+# Ascetic x86_64 AT&T to MASM/NASM assembler translator by .
+#
+# Why AT&T to MASM and not vice versa? Several reasons. Because AT&T
+# format is way easier to parse. Because it's simpler to "gear" from
+# Unix ABI to Windows one [see cross-reference "card" at the end of
+# file]. Because Linux targets were available first...
+#
+# In addition the script also "distills" code suitable for GNU
+# assembler, so that it can be compiled with more rigid assemblers,
+# such as Solaris /usr/ccs/bin/as.
+#
+# This translator is not designed to convert *arbitrary* assembler
+# code from AT&T format to MASM one. It's designed to convert just
+# enough to provide for dual-ABI OpenSSL modules development...
+# There *are* limitations and you might have to modify your assembler
+# code or this script to achieve the desired result...
+#
+# Currently recognized limitations:
+#
+# - can't use multiple ops per line;
+#
+# Dual-ABI styling rules.
+#
+# 1. Adhere to Unix register and stack layout [see cross-reference
+#    ABI "card" at the end for explanation].
+# 2. Forget about "red zone," stick to more traditional blended
+#    stack frame allocation. If volatile storage is actually required
+#    that is. If not, just leave the stack as is.
+# 3. Functions tagged with ".type name,@function" get crafted with
+#    unified Win64 prologue and epilogue automatically. If you want
+#    to take care of ABI differences yourself, tag functions as
+#    ".type name,@abi-omnipotent" instead.
+# 4. To optimize the Win64 prologue you can specify number of input
+#    arguments as ".type name,@function,N." Keep in mind that if N is
+#    larger than 6, then you *have to* write "abi-omnipotent" code,
+#    because >6 cases can't be addressed with unified prologue.
+# 5. Name local labels as .L*, do *not* use dynamic labels such as 1:
+#    (sorry about latter).
+# 6. Don't use [or hand-code with .byte] "rep ret." "ret" mnemonic is
+#    required to identify the spots, where to inject Win64 epilogue!
+#    But on the pros, it's then prefixed with rep automatically:-)
+# 7. Stick to explicit ip-relative addressing. If you have to use
+#    GOTPCREL addressing, stick to mov symbol@GOTPCREL(%rip),%r??.
+#    Both are recognized and translated to proper Win64 addressing
+#    modes. To support legacy code a synthetic directive, .picmeup,
+#    is implemented. It puts address of the *next* instruction into
+#    target register, e.g.:
+#
+#		.picmeup	%rax
+#		lea		.Label-.(%rax),%rax
+#
+# 8. In order to provide for structured exception handling unified
+#    Win64 prologue copies %rsp value to %rax. For further details
+#    see SEH paragraph at the end.
+# 9. .init segment is allowed to contain calls to functions only.
+# a. If function accepts more than 4 arguments *and* >4th argument
+#    is declared as non 64-bit value, do clear its upper part.
+
+
+use strict;
+
+my $flavour = shift;
+my $output  = shift;
+if ($flavour =~ /\./) { $output = $flavour; undef $flavour; }
+
+open STDOUT,">$output" || die "can't open $output: $!"
+	if (defined($output));
+
+my $gas=1;	$gas=0 if ($output =~ /\.asm$/);
+my $elf=1;	$elf=0 if (!$gas);
+my $win64=0;
+my $prefix="";
+my $decor=".L";
+
+my $masmref=8 + 50727*2**-32;	# 8.00.50727 shipped with VS2005
+my $masm=0;
+my $PTR=" PTR";
+
+my $nasmref=2.03;
+my $nasm=0;
+
+if    ($flavour eq "mingw64")	{ $gas=1; $elf=0; $win64=1;
+				  $prefix=`echo __USER_LABEL_PREFIX__ | $ENV{CC} -E -P -`;
+				  $prefix =~ s|\R$||; # Better chomp
+				}
+elsif ($flavour eq "macosx")	{ $gas=1; $elf=0; $prefix="_"; $decor="L\$"; }
+elsif ($flavour eq "masm")	{ $gas=0; $elf=0; $masm=$masmref; $win64=1; $decor="\$L\$"; }
+elsif ($flavour eq "nasm")	{ $gas=0; $elf=0; $nasm=$nasmref; $win64=1; $decor="\$L\$"; $PTR=""; }
+elsif (!$gas)
+{   if ($ENV{ASM} =~ m/nasm/ && `nasm -v` =~ m/version ([0-9]+)\.([0-9]+)/i)
+    {	$nasm = $1 + $2*0.01; $PTR="";  }
+    elsif (`ml64 2>&1` =~ m/Version ([0-9]+)\.([0-9]+)(\.([0-9]+))?/)
+    {	$masm = $1 + $2*2**-16 + $4*2**-32;   }
+    die "no assembler found on %PATH" if (!($nasm || $masm));
+    $win64=1;
+    $elf=0;
+    $decor="\$L\$";
+}
+
+my $current_segment;
+my $current_function;
+my %globals;
+
+{ package opcode;	# pick up opcodes
+    sub re {
+	my	($class, $line) = @_;
+	my	$self = {};
+	my	$ret;
+
+	if ($$line =~ /^([a-z][a-z0-9]*)/i) {
+	    bless $self,$class;
+	    $self->{op} = $1;
+	    $ret = $self;
+	    $$line = substr($$line,@+[0]); $$line =~ s/^\s+//;
+
+	    undef $self->{sz};
+	    if ($self->{op} =~ /^(movz)x?([bw]).*/) {	# movz is pain...
+		$self->{op} = $1;
+		$self->{sz} = $2;
+	    } elsif ($self->{op} =~ /call|jmp/) {
+		$self->{sz} = "";
+	    } elsif ($self->{op} =~ /^p/ && $' !~ /^(ush|op|insrw)/) { # SSEn
+		$self->{sz} = "";
+	    } elsif ($self->{op} =~ /^v/) { # VEX
+		$self->{sz} = "";
+	    } elsif ($self->{op} =~ /mov[dq]/ && $$line =~ /%xmm/) {
+		$self->{sz} = "";
+	    } elsif ($self->{op} =~ /([a-z]{3,})([qlwb])$/) {
+		$self->{op} = $1;
+		$self->{sz} = $2;
+	    }
+	}
+	$ret;
+    }
+    sub size {
+	my ($self, $sz) = @_;
+	$self->{sz} = $sz if (defined($sz) && !defined($self->{sz}));
+	$self->{sz};
+    }
+    sub out {
+	my $self = shift;
+	if ($gas) {
+	    if ($self->{op} eq "movz") {	# movz is pain...
+		sprintf "%s%s%s",$self->{op},$self->{sz},shift;
+	    } elsif ($self->{op} =~ /^set/) { 
+		"$self->{op}";
+	    } elsif ($self->{op} eq "ret") {
+		my $epilogue = "";
+		if ($win64 && $current_function->{abi} eq "svr4") {
+		    $epilogue = "movq	8(%rsp),%rdi\n\t" .
+				"movq	16(%rsp),%rsi\n\t";
+		}
+	    	$epilogue . ".byte	0xf3,0xc3";
+	    } elsif ($self->{op} eq "call" && !$elf && $current_segment eq ".init") {
+		".p2align\t3\n\t.quad";
+	    } else {
+		"$self->{op}$self->{sz}";
+	    }
+	} else {
+	    $self->{op} =~ s/^movz/movzx/;
+	    if ($self->{op} eq "ret") {
+		$self->{op} = "";
+		if ($win64 && $current_function->{abi} eq "svr4") {
+		    $self->{op} = "mov	rdi,QWORD$PTR\[8+rsp\]\t;WIN64 epilogue\n\t".
+				  "mov	rsi,QWORD$PTR\[16+rsp\]\n\t";
+	    	}
+		$self->{op} .= "DB\t0F3h,0C3h\t\t;repret";
+	    } elsif ($self->{op} =~ /^(pop|push)f/) {
+		$self->{op} .= $self->{sz};
+	    } elsif ($self->{op} eq "call" && $current_segment eq ".CRT\$XCU") {
+		$self->{op} = "\tDQ";
+	    } 
+	    $self->{op};
+	}
+    }
+    sub mnemonic {
+	my ($self, $op) = @_;
+	$self->{op}=$op if (defined($op));
+	$self->{op};
+    }
+}
+{ package const;	# pick up constants, which start with $
+    sub re {
+	my	($class, $line) = @_;
+	my	$self = {};
+	my	$ret;
+
+	if ($$line =~ /^\$([^,]+)/) {
+	    bless $self, $class;
+	    $self->{value} = $1;
+	    $ret = $self;
+	    $$line = substr($$line,@+[0]); $$line =~ s/^\s+//;
+	}
+	$ret;
+    }
+    sub out {
+    	my $self = shift;
+
+	$self->{value} =~ s/\b(0b[0-1]+)/oct($1)/eig;
+	if ($gas) {
+	    # Solaris /usr/ccs/bin/as can't handle multiplications
+	    # in $self->{value}
+	    my $value = $self->{value};
+	    no warnings;    # oct might complain about overflow, ignore here...
+	    $value =~ s/(?{value} = $value;
+	    }
+	    sprintf "\$%s",$self->{value};
+	} else {
+	    $self->{value} =~ s/0x([0-9a-f]+)/0$1h/ig if ($masm);
+	    sprintf "%s",$self->{value};
+	}
+    }
+}
+{ package ea;		# pick up effective addresses: expr(%reg,%reg,scale)
+    sub re {
+	my	($class, $line, $opcode) = @_;
+	my	$self = {};
+	my	$ret;
+
+	# optional * ----vvv--- appears in indirect jmp/call
+	if ($$line =~ /^(\*?)([^\(,]*)\(([%\w,]+)\)/) {
+	    bless $self, $class;
+	    $self->{asterisk} = $1;
+	    $self->{label} = $2;
+	    ($self->{base},$self->{index},$self->{scale})=split(/,/,$3);
+	    $self->{scale} = 1 if (!defined($self->{scale}));
+	    $ret = $self;
+	    $$line = substr($$line,@+[0]); $$line =~ s/^\s+//;
+
+	    if ($win64 && $self->{label} =~ s/\@GOTPCREL//) {
+		die if ($opcode->mnemonic() ne "mov");
+		$opcode->mnemonic("lea");
+	    }
+	    $self->{base}  =~ s/^%//;
+	    $self->{index} =~ s/^%// if (defined($self->{index}));
+	    $self->{opcode} = $opcode;
+	}
+	$ret;
+    }
+    sub size {}
+    sub out {
+	my ($self, $sz) = @_;
+
+	$self->{label} =~ s/([_a-z][_a-z0-9]*)/$globals{$1} or $1/gei;
+	$self->{label} =~ s/\.L/$decor/g;
+
+	# Silently convert all EAs to 64-bit. This is required for
+	# elder GNU assembler and results in more compact code,
+	# *but* most importantly AES module depends on this feature!
+	$self->{index} =~ s/^[er](.?[0-9xpi])[d]?$/r\1/;
+	$self->{base}  =~ s/^[er](.?[0-9xpi])[d]?$/r\1/;
+
+	# Solaris /usr/ccs/bin/as can't handle multiplications
+	# in $self->{label}...
+	use integer;
+	$self->{label} =~ s/(?{label} =~ s/\b([0-9]+\s*[\*\/\%]\s*[0-9]+)\b/eval($1)/eg;
+
+	# Some assemblers insist on signed presentation of 32-bit
+	# offsets, but sign extension is a tricky business in perl...
+	if ((1<<31)<<1) {
+	    $self->{label} =~ s/\b([0-9]+)\b/$1<<32>>32/eg;
+	} else {
+	    $self->{label} =~ s/\b([0-9]+)\b/$1>>0/eg;
+	}
+
+	if (!$self->{label} && $self->{index} && $self->{scale}==1 &&
+	    $self->{base} =~ /(rbp|r13)/) {
+		$self->{base} = $self->{index}; $self->{index} = $1;
+	}
+
+	if ($gas) {
+	    $self->{label} =~ s/^___imp_/__imp__/   if ($flavour eq "mingw64");
+
+	    if (defined($self->{index})) {
+		sprintf "%s%s(%s,%%%s,%d)",$self->{asterisk},
+					$self->{label},
+					$self->{base}?"%$self->{base}":"",
+					$self->{index},$self->{scale};
+	    } else {
+		sprintf "%s%s(%%%s)",	$self->{asterisk},$self->{label},$self->{base};
+	    }
+	} else {
+	    my %szmap = (	b=>"BYTE$PTR",  w=>"WORD$PTR",
+			l=>"DWORD$PTR", d=>"DWORD$PTR",
+	    		q=>"QWORD$PTR", o=>"OWORD$PTR",
+			x=>"XMMWORD$PTR", y=>"YMMWORD$PTR", z=>"ZMMWORD$PTR" );
+
+	    $self->{label} =~ s/\./\$/g;
+	    $self->{label} =~ s/(?{label} = "($self->{label})" if ($self->{label} =~ /[\*\+\-\/]/);
+
+	    my $mnemonic = $self->{opcode}->mnemonic();
+	    ($self->{asterisk})				&& ($sz="q") ||
+	    ($mnemonic =~ /^v?mov([qd])$/)		&& ($sz=$1)  ||
+	    ($mnemonic =~ /^v?pinsr([qdwb])$/)		&& ($sz=$1)  ||
+	    ($mnemonic =~ /^vpbroadcast([qdwb])$/)	&& ($sz=$1)  ||
+	    ($mnemonic =~ /^v(?!perm)[a-z]+[fi]128$/)	&& ($sz="x");
+
+	    if (defined($self->{index})) {
+		sprintf "%s[%s%s*%d%s]",$szmap{$sz},
+					$self->{label}?"$self->{label}+":"",
+					$self->{index},$self->{scale},
+					$self->{base}?"+$self->{base}":"";
+	    } elsif ($self->{base} eq "rip") {
+		sprintf "%s[%s]",$szmap{$sz},$self->{label};
+	    } else {
+		sprintf "%s[%s%s]",$szmap{$sz},
+					$self->{label}?"$self->{label}+":"",
+					$self->{base};
+	    }
+	}
+    }
+}
+{ package register;	# pick up registers, which start with %.
+    sub re {
+	my	($class, $line, $opcode) = @_;
+	my	$self = {};
+	my	$ret;
+
+	# optional * ----vvv--- appears in indirect jmp/call
+	if ($$line =~ /^(\*?)%(\w+)/) {
+	    bless $self,$class;
+	    $self->{asterisk} = $1;
+	    $self->{value} = $2;
+	    $opcode->size($self->size());
+	    $ret = $self;
+	    $$line = substr($$line,@+[0]); $$line =~ s/^\s+//;
+	}
+	$ret;
+    }
+    sub size {
+	my	$self = shift;
+	my	$ret;
+
+	if    ($self->{value} =~ /^r[\d]+b$/i)	{ $ret="b"; }
+	elsif ($self->{value} =~ /^r[\d]+w$/i)	{ $ret="w"; }
+	elsif ($self->{value} =~ /^r[\d]+d$/i)	{ $ret="l"; }
+	elsif ($self->{value} =~ /^r[\w]+$/i)	{ $ret="q"; }
+	elsif ($self->{value} =~ /^[a-d][hl]$/i){ $ret="b"; }
+	elsif ($self->{value} =~ /^[\w]{2}l$/i)	{ $ret="b"; }
+	elsif ($self->{value} =~ /^[\w]{2}$/i)	{ $ret="w"; }
+	elsif ($self->{value} =~ /^e[a-z]{2}$/i){ $ret="l"; }
+
+	$ret;
+    }
+    sub out {
+    	my $self = shift;
+	if ($gas)	{ sprintf "%s%%%s",$self->{asterisk},$self->{value}; }
+	else		{ $self->{value}; }
+    }
+}
+{ package label;	# pick up labels, which end with :
+    sub re {
+	my	($class, $line) = @_;
+	my	$self = {};
+	my	$ret;
+
+	if ($$line =~ /(^[\.\w]+)\:/) {
+	    bless $self,$class;
+	    $self->{value} = $1;
+	    $ret = $self;
+	    $$line = substr($$line,@+[0]); $$line =~ s/^\s+//;
+
+	    $self->{value} =~ s/^\.L/$decor/;
+	}
+	$ret;
+    }
+    sub out {
+	my $self = shift;
+
+	if ($gas) {
+	    my $func = ($globals{$self->{value}} or $self->{value}) . ":";
+	    if ($win64	&&
+			$current_function->{name} eq $self->{value} &&
+			$current_function->{abi} eq "svr4") {
+		$func .= "\n";
+		$func .= "	movq	%rdi,8(%rsp)\n";
+		$func .= "	movq	%rsi,16(%rsp)\n";
+		$func .= "	movq	%rsp,%rax\n";
+		$func .= "${decor}SEH_begin_$current_function->{name}:\n";
+		my $narg = $current_function->{narg};
+		$narg=6 if (!defined($narg));
+		$func .= "	movq	%rcx,%rdi\n" if ($narg>0);
+		$func .= "	movq	%rdx,%rsi\n" if ($narg>1);
+		$func .= "	movq	%r8,%rdx\n"  if ($narg>2);
+		$func .= "	movq	%r9,%rcx\n"  if ($narg>3);
+		$func .= "	movq	40(%rsp),%r8\n" if ($narg>4);
+		$func .= "	movq	48(%rsp),%r9\n" if ($narg>5);
+	    }
+	    $func;
+	} elsif ($self->{value} ne "$current_function->{name}") {
+	    # Make all labels in masm global.
+	    $self->{value} .= ":" if ($masm);
+	    $self->{value} . ":";
+	} elsif ($win64 && $current_function->{abi} eq "svr4") {
+	    my $func =	"$current_function->{name}" .
+			($nasm ? ":" : "\tPROC $current_function->{scope}") .
+			"\n";
+	    $func .= "	mov	QWORD$PTR\[8+rsp\],rdi\t;WIN64 prologue\n";
+	    $func .= "	mov	QWORD$PTR\[16+rsp\],rsi\n";
+	    $func .= "	mov	rax,rsp\n";
+	    $func .= "${decor}SEH_begin_$current_function->{name}:";
+	    $func .= ":" if ($masm);
+	    $func .= "\n";
+	    my $narg = $current_function->{narg};
+	    $narg=6 if (!defined($narg));
+	    $func .= "	mov	rdi,rcx\n" if ($narg>0);
+	    $func .= "	mov	rsi,rdx\n" if ($narg>1);
+	    $func .= "	mov	rdx,r8\n"  if ($narg>2);
+	    $func .= "	mov	rcx,r9\n"  if ($narg>3);
+	    $func .= "	mov	r8,QWORD$PTR\[40+rsp\]\n" if ($narg>4);
+	    $func .= "	mov	r9,QWORD$PTR\[48+rsp\]\n" if ($narg>5);
+	    $func .= "\n";
+	} else {
+	   "$current_function->{name}".
+			($nasm ? ":" : "\tPROC $current_function->{scope}");
+	}
+    }
+}
+{ package expr;		# pick up expressioins
+    sub re {
+	my	($class, $line, $opcode) = @_;
+	my	$self = {};
+	my	$ret;
+
+	if ($$line =~ /(^[^,]+)/) {
+	    bless $self,$class;
+	    $self->{value} = $1;
+	    $ret = $self;
+	    $$line = substr($$line,@+[0]); $$line =~ s/^\s+//;
+
+	    $self->{value} =~ s/\@PLT// if (!$elf);
+	    $self->{value} =~ s/([_a-z][_a-z0-9]*)/$globals{$1} or $1/gei;
+	    $self->{value} =~ s/\.L/$decor/g;
+	    $self->{opcode} = $opcode;
+	}
+	$ret;
+    }
+    sub out {
+	my $self = shift;
+	if ($nasm && $self->{opcode}->mnemonic()=~m/^j(?![re]cxz)/) {
+	    "NEAR ".$self->{value};
+	} else {
+	    $self->{value};
+	}
+    }
+}
+{ package directive;	# pick up directives, which start with .
+    sub re {
+	my	($class, $line) = @_;
+	my	$self = {};
+	my	$ret;
+	my	$dir;
+	my	%opcode =	# lea 2f-1f(%rip),%dst; 1: nop; 2:
+		(	"%rax"=>0x01058d48,	"%rcx"=>0x010d8d48,
+			"%rdx"=>0x01158d48,	"%rbx"=>0x011d8d48,
+			"%rsp"=>0x01258d48,	"%rbp"=>0x012d8d48,
+			"%rsi"=>0x01358d48,	"%rdi"=>0x013d8d48,
+			"%r8" =>0x01058d4c,	"%r9" =>0x010d8d4c,
+			"%r10"=>0x01158d4c,	"%r11"=>0x011d8d4c,
+			"%r12"=>0x01258d4c,	"%r13"=>0x012d8d4c,
+			"%r14"=>0x01358d4c,	"%r15"=>0x013d8d4c	);
+
+	if ($$line =~ /^\s*(\.\w+)/) {
+	    bless $self,$class;
+	    $dir = $1;
+	    $ret = $self;
+	    undef $self->{value};
+	    $$line = substr($$line,@+[0]); $$line =~ s/^\s+//;
+
+	    SWITCH: for ($dir) {
+		/\.picmeup/ && do { if ($$line =~ /(%r[\w]+)/i) {
+			    		$dir="\t.long";
+					$$line=sprintf "0x%x,0x90000000",$opcode{$1};
+				    }
+				    last;
+				  };
+		/\.global|\.globl|\.extern/
+			    && do { $globals{$$line} = $prefix . $$line;
+				    $$line = $globals{$$line} if ($prefix);
+				    last;
+				  };
+		/\.type/    && do { my ($sym,$type,$narg) = split(',',$$line);
+				    if ($type eq "\@function") {
+					undef $current_function;
+					$current_function->{name} = $sym;
+					$current_function->{abi}  = "svr4";
+					$current_function->{narg} = $narg;
+					$current_function->{scope} = defined($globals{$sym})?"PUBLIC":"PRIVATE";
+				    } elsif ($type eq "\@abi-omnipotent") {
+					undef $current_function;
+					$current_function->{name} = $sym;
+					$current_function->{scope} = defined($globals{$sym})?"PUBLIC":"PRIVATE";
+				    }
+				    $$line =~ s/\@abi\-omnipotent/\@function/;
+				    $$line =~ s/\@function.*/\@function/;
+				    last;
+				  };
+		/\.asciz/   && do { if ($$line =~ /^"(.*)"$/) {
+					$dir  = ".byte";
+					$$line = join(",",unpack("C*",$1),0);
+				    }
+				    last;
+				  };
+		/\.rva|\.long|\.quad/
+			    && do { $$line =~ s/([_a-z][_a-z0-9]*)/$globals{$1} or $1/gei;
+				    $$line =~ s/\.L/$decor/g;
+				    last;
+				  };
+	    }
+
+	    if ($gas) {
+		$self->{value} = $dir . "\t" . $$line;
+
+		if ($dir =~ /\.extern/) {
+		    $self->{value} = ""; # swallow extern
+		} elsif (!$elf && $dir =~ /\.type/) {
+		    $self->{value} = "";
+		    $self->{value} = ".def\t" . ($globals{$1} or $1) . ";\t" .
+				(defined($globals{$1})?".scl 2;":".scl 3;") .
+				"\t.type 32;\t.endef"
+				if ($win64 && $$line =~ /([^,]+),\@function/);
+		} elsif (!$elf && $dir =~ /\.size/) {
+		    $self->{value} = "";
+		    if (defined($current_function)) {
+			$self->{value} .= "${decor}SEH_end_$current_function->{name}:"
+				if ($win64 && $current_function->{abi} eq "svr4");
+			undef $current_function;
+		    }
+		} elsif (!$elf && $dir =~ /\.align/) {
+		    $self->{value} = ".p2align\t" . (log($$line)/log(2));
+		} elsif ($dir eq ".section") {
+		    $current_segment=$$line;
+		    if (!$elf && $current_segment eq ".init") {
+			if	($flavour eq "macosx")	{ $self->{value} = ".mod_init_func"; }
+			elsif	($flavour eq "mingw64")	{ $self->{value} = ".section\t.ctors"; }
+		    }
+		} elsif ($dir =~ /\.(text|data)/) {
+		    $current_segment=".$1";
+		} elsif ($dir =~ /\.hidden/) {
+		    if    ($flavour eq "macosx")  { $self->{value} = ".private_extern\t$prefix$$line"; }
+		    elsif ($flavour eq "mingw64") { $self->{value} = ""; }
+		} elsif ($dir =~ /\.comm/) {
+		    $self->{value} = "$dir\t$prefix$$line";
+		    $self->{value} =~ s|,([0-9]+),([0-9]+)$|",$1,".log($2)/log(2)|e if ($flavour eq "macosx");
+		}
+		$$line = "";
+		return $self;
+	    }
+
+	    # non-gas case or nasm/masm
+	    SWITCH: for ($dir) {
+		/\.text/    && do { my $v=undef;
+				    if ($nasm) {
+					$v="section	.text code align=64\n";
+				    } else {
+					$v="$current_segment\tENDS\n" if ($current_segment);
+					$current_segment = ".text\$";
+					$v.="$current_segment\tSEGMENT ";
+					$v.=$masm>=$masmref ? "ALIGN(256)" : "PAGE";
+					$v.=" 'CODE'";
+				    }
+				    $self->{value} = $v;
+				    last;
+				  };
+		/\.data/    && do { my $v=undef;
+				    if ($nasm) {
+					$v="section	.data data align=8\n";
+				    } else {
+					$v="$current_segment\tENDS\n" if ($current_segment);
+					$current_segment = "_DATA";
+					$v.="$current_segment\tSEGMENT";
+				    }
+				    $self->{value} = $v;
+				    last;
+				  };
+		/\.section/ && do { my $v=undef;
+				    $$line =~ s/([^,]*).*/$1/;
+				    $$line = ".CRT\$XCU" if ($$line eq ".init");
+				    if ($nasm) {
+					$v="section	$$line";
+					if ($$line=~/\.([px])data/) {
+					    $v.=" rdata align=";
+					    $v.=$1 eq "p"? 4 : 8;
+					} elsif ($$line=~/\.CRT\$/i) {
+					    $v.=" rdata align=8";
+					}
+				    } else {
+					$v="$current_segment\tENDS\n" if ($current_segment);
+					$v.="$$line\tSEGMENT";
+					if ($$line=~/\.([px])data/) {
+					    $v.=" READONLY";
+					    $v.=" ALIGN(".($1 eq "p" ? 4 : 8).")" if ($masm>=$masmref);
+					} elsif ($$line=~/\.CRT\$/i) {
+					    $v.=" READONLY ";
+					    $v.=$masm>=$masmref ? "ALIGN(8)" : "DWORD";
+					}
+				    }
+				    $current_segment = $$line;
+				    $self->{value} = $v;
+				    last;
+				  };
+		/\.extern/  && do { $self->{value}  = "EXTERN\t".$$line;
+				    $self->{value} .= ":NEAR" if ($masm);
+				    last;
+				  };
+		/\.globl|.global/
+			    && do { $self->{value}  = $masm?"PUBLIC":"global";
+				    $self->{value} .= "\t".$$line;
+				    last;
+				  };
+		/\.size/    && do { if (defined($current_function)) {
+					undef $self->{value};
+					if ($current_function->{abi} eq "svr4") {
+					    $self->{value}="${decor}SEH_end_$current_function->{name}:";
+					    $self->{value}.=":\n" if($masm);
+					}
+					$self->{value}.="$current_function->{name}\tENDP" if($masm && $current_function->{name});
+					undef $current_function;
+				    }
+				    last;
+				  };
+		/\.align/   && do { my $max = ($masm && $masm>=$masmref) ? 256 : 4096;
+				    $self->{value} = "ALIGN\t".($$line>$max?$max:$$line);
+				    last;
+				  };
+		/\.(value|long|rva|quad)/
+			    && do { my $sz  = substr($1,0,1);
+				    my @arr = split(/,\s*/,$$line);
+				    my $last = pop(@arr);
+				    my $conv = sub  {	my $var=shift;
+							$var=~s/^(0b[0-1]+)/oct($1)/eig;
+							$var=~s/^0x([0-9a-f]+)/0$1h/ig if ($masm);
+							if ($sz eq "D" && ($current_segment=~/.[px]data/ || $dir eq ".rva"))
+							{ $var=~s/([_a-z\$\@][_a-z0-9\$\@]*)/$nasm?"$1 wrt ..imagebase":"imagerel $1"/egi; }
+							$var;
+						    };  
+
+				    $sz =~ tr/bvlrq/BWDDQ/;
+				    $self->{value} = "\tD$sz\t";
+				    for (@arr) { $self->{value} .= &$conv($_).","; }
+				    $self->{value} .= &$conv($last);
+				    last;
+				  };
+		/\.byte/    && do { my @str=split(/,\s*/,$$line);
+				    map(s/(0b[0-1]+)/oct($1)/eig,@str);
+				    map(s/0x([0-9a-f]+)/0$1h/ig,@str) if ($masm);	
+				    while ($#str>15) {
+					$self->{value}.="DB\t"
+						.join(",",@str[0..15])."\n";
+					foreach (0..15) { shift @str; }
+				    }
+				    $self->{value}.="DB\t"
+						.join(",",@str) if (@str);
+				    last;
+				  };
+		/\.comm/    && do { my @str=split(/,\s*/,$$line);
+				    my $v=undef;
+				    if ($nasm) {
+					$v.="common	$prefix@str[0] @str[1]";
+				    } else {
+					$v="$current_segment\tENDS\n" if ($current_segment);
+					$current_segment = "_DATA";
+					$v.="$current_segment\tSEGMENT\n";
+					$v.="COMM	@str[0]:DWORD:".@str[1]/4;
+				    }
+				    $self->{value} = $v;
+				    last;
+				  };
+	    }
+	    $$line = "";
+	}
+
+	$ret;
+    }
+    sub out {
+	my $self = shift;
+	$self->{value};
+    }
+}
+
+sub rex {
+ my $opcode=shift;
+ my ($dst,$src,$rex)=@_;
+
+   $rex|=0x04 if($dst>=8);
+   $rex|=0x01 if($src>=8);
+   push @$opcode,($rex|0x40) if ($rex);
+}
+
+# Upon initial x86_64 introduction SSE>2 extensions were not introduced
+# yet. In order not to be bothered by tracing exact assembler versions,
+# but at the same time to provide a bare security minimum of AES-NI, we
+# hard-code some instructions. Extensions past AES-NI on the other hand
+# are traced by examining assembler version in individual perlasm
+# modules...
+
+my %regrm = (	"%eax"=>0, "%ecx"=>1, "%edx"=>2, "%ebx"=>3,
+		"%esp"=>4, "%ebp"=>5, "%esi"=>6, "%edi"=>7	);
+
+my $movq = sub {	# elderly gas can't handle inter-register movq
+  my $arg = shift;
+  my @opcode=(0x66);
+    if ($arg =~ /%xmm([0-9]+),\s*%r(\w+)/) {
+	my ($src,$dst)=($1,$2);
+	if ($dst !~ /[0-9]+/)	{ $dst = $regrm{"%e$dst"}; }
+	rex(\@opcode,$src,$dst,0x8);
+	push @opcode,0x0f,0x7e;
+	push @opcode,0xc0|(($src&7)<<3)|($dst&7);	# ModR/M
+	@opcode;
+    } elsif ($arg =~ /%r(\w+),\s*%xmm([0-9]+)/) {
+	my ($src,$dst)=($2,$1);
+	if ($dst !~ /[0-9]+/)	{ $dst = $regrm{"%e$dst"}; }
+	rex(\@opcode,$src,$dst,0x8);
+	push @opcode,0x0f,0x6e;
+	push @opcode,0xc0|(($src&7)<<3)|($dst&7);	# ModR/M
+	@opcode;
+    } else {
+	();
+    }
+};
+
+my $pextrd = sub {
+    if (shift =~ /\$([0-9]+),\s*%xmm([0-9]+),\s*(%\w+)/) {
+      my @opcode=(0x66);
+	my $imm=$1;
+	my $src=$2;
+	my $dst=$3;
+	if ($dst =~ /%r([0-9]+)d/)	{ $dst = $1; }
+	elsif ($dst =~ /%e/)		{ $dst = $regrm{$dst}; }
+	rex(\@opcode,$src,$dst);
+	push @opcode,0x0f,0x3a,0x16;
+	push @opcode,0xc0|(($src&7)<<3)|($dst&7);	# ModR/M
+	push @opcode,$imm;
+	@opcode;
+    } else {
+	();
+    }
+};
+
+my $pinsrd = sub {
+    if (shift =~ /\$([0-9]+),\s*(%\w+),\s*%xmm([0-9]+)/) {
+      my @opcode=(0x66);
+	my $imm=$1;
+	my $src=$2;
+	my $dst=$3;
+	if ($src =~ /%r([0-9]+)/)	{ $src = $1; }
+	elsif ($src =~ /%e/)		{ $src = $regrm{$src}; }
+	rex(\@opcode,$dst,$src);
+	push @opcode,0x0f,0x3a,0x22;
+	push @opcode,0xc0|(($dst&7)<<3)|($src&7);	# ModR/M
+	push @opcode,$imm;
+	@opcode;
+    } else {
+	();
+    }
+};
+
+my $pshufb = sub {
+    if (shift =~ /%xmm([0-9]+),\s*%xmm([0-9]+)/) {
+      my @opcode=(0x66);
+	rex(\@opcode,$2,$1);
+	push @opcode,0x0f,0x38,0x00;
+	push @opcode,0xc0|($1&7)|(($2&7)<<3);		# ModR/M
+	@opcode;
+    } else {
+	();
+    }
+};
+
+my $palignr = sub {
+    if (shift =~ /\$([0-9]+),\s*%xmm([0-9]+),\s*%xmm([0-9]+)/) {
+      my @opcode=(0x66);
+	rex(\@opcode,$3,$2);
+	push @opcode,0x0f,0x3a,0x0f;
+	push @opcode,0xc0|($2&7)|(($3&7)<<3);		# ModR/M
+	push @opcode,$1;
+	@opcode;
+    } else {
+	();
+    }
+};
+
+my $pclmulqdq = sub {
+    if (shift =~ /\$([x0-9a-f]+),\s*%xmm([0-9]+),\s*%xmm([0-9]+)/) {
+      my @opcode=(0x66);
+	rex(\@opcode,$3,$2);
+	push @opcode,0x0f,0x3a,0x44;
+	push @opcode,0xc0|($2&7)|(($3&7)<<3);		# ModR/M
+	my $c=$1;
+	push @opcode,$c=~/^0/?oct($c):$c;
+	@opcode;
+    } else {
+	();
+    }
+};
+
+my $rdrand = sub {
+    if (shift =~ /%[er](\w+)/) {
+      my @opcode=();
+      my $dst=$1;
+	if ($dst !~ /[0-9]+/) { $dst = $regrm{"%e$dst"}; }
+	rex(\@opcode,0,$dst,8);
+	push @opcode,0x0f,0xc7,0xf0|($dst&7);
+	@opcode;
+    } else {
+	();
+    }
+};
+
+my $rdseed = sub {
+    if (shift =~ /%[er](\w+)/) {
+      my @opcode=();
+      my $dst=$1;
+	if ($dst !~ /[0-9]+/) { $dst = $regrm{"%e$dst"}; }
+	rex(\@opcode,0,$dst,8);
+	push @opcode,0x0f,0xc7,0xf8|($dst&7);
+	@opcode;
+    } else {
+	();
+    }
+};
+
+sub rxb {
+ my $opcode=shift;
+ my ($dst,$src1,$src2,$rxb)=@_;
+
+   $rxb|=0x7<<5;
+   $rxb&=~(0x04<<5) if($dst>=8);
+   $rxb&=~(0x01<<5) if($src1>=8);
+   $rxb&=~(0x02<<5) if($src2>=8);
+   push @$opcode,$rxb;
+}
+
+my $vprotd = sub {
+    if (shift =~ /\$([x0-9a-f]+),\s*%xmm([0-9]+),\s*%xmm([0-9]+)/) {
+      my @opcode=(0x8f);
+	rxb(\@opcode,$3,$2,-1,0x08);
+	push @opcode,0x78,0xc2;
+	push @opcode,0xc0|($2&7)|(($3&7)<<3);		# ModR/M
+	my $c=$1;
+	push @opcode,$c=~/^0/?oct($c):$c;
+	@opcode;
+    } else {
+	();
+    }
+};
+
+my $vprotq = sub {
+    if (shift =~ /\$([x0-9a-f]+),\s*%xmm([0-9]+),\s*%xmm([0-9]+)/) {
+      my @opcode=(0x8f);
+	rxb(\@opcode,$3,$2,-1,0x08);
+	push @opcode,0x78,0xc3;
+	push @opcode,0xc0|($2&7)|(($3&7)<<3);		# ModR/M
+	my $c=$1;
+	push @opcode,$c=~/^0/?oct($c):$c;
+	@opcode;
+    } else {
+	();
+    }
+};
+
+my $endbranch = sub {
+    (0xf3,0x0f,0x1e,0xfa);
+};
+
+if ($nasm) {
+    print <<___;
+default	rel
+%define XMMWORD
+%define YMMWORD
+%define ZMMWORD
+___
+} elsif ($masm) {
+    print <<___;
+OPTION	DOTNAME
+___
+}
+while(defined(my $line=<>)) {
+
+    $line =~ s|\R$||;           # Better chomp
+
+    $line =~ s|[#!].*$||;	# get rid of asm-style comments...
+    $line =~ s|/\*.*\*/||;	# ... and C-style comments...
+    $line =~ s|^\s+||;		# ... and skip white spaces in beginning
+    $line =~ s|\s+$||;		# ... and at the end
+
+    if (my $label=label->re(\$line))	{ print $label->out(); }
+
+    if (my $directive=directive->re(\$line)) {
+	printf "%s",$directive->out();
+    } elsif (my $opcode=opcode->re(\$line)) {
+	my $asm = eval("\$".$opcode->mnemonic());
+	
+	if ((ref($asm) eq 'CODE') && scalar(my @bytes=&$asm($line))) {
+	    print $gas?".byte\t":"DB\t",join(',',@bytes),"\n";
+	    next;
+	}
+
+	my @args;
+	ARGUMENT: while (1) {
+	    my $arg;
+
+	    ($arg=register->re(\$line, $opcode))||
+	    ($arg=const->re(\$line))		||
+	    ($arg=ea->re(\$line, $opcode))	||
+	    ($arg=expr->re(\$line, $opcode))	||
+	    last ARGUMENT;
+
+	    push @args,$arg;
+
+	    last ARGUMENT if ($line !~ /^,/);
+
+	    $line =~ s/^,\s*//;
+	} # ARGUMENT:
+
+	if ($#args>=0) {
+	    my $insn;
+	    my $sz=$opcode->size();
+
+	    if ($gas) {
+		$insn = $opcode->out($#args>=1?$args[$#args]->size():$sz);
+		@args = map($_->out($sz),@args);
+		printf "\t%s\t%s",$insn,join(",",@args);
+	    } else {
+		$insn = $opcode->out();
+		foreach (@args) {
+		    my $arg = $_->out();
+		    # $insn.=$sz compensates for movq, pinsrw, ...
+		    if ($arg =~ /^xmm[0-9]+$/) { $insn.=$sz; $sz="x" if(!$sz); last; }
+		    if ($arg =~ /^ymm[0-9]+$/) { $insn.=$sz; $sz="y" if(!$sz); last; }
+		    if ($arg =~ /^zmm[0-9]+$/) { $insn.=$sz; $sz="z" if(!$sz); last; }
+		    if ($arg =~ /^mm[0-9]+$/)  { $insn.=$sz; $sz="q" if(!$sz); last; }
+		}
+		@args = reverse(@args);
+		undef $sz if ($nasm && $opcode->mnemonic() eq "lea");
+		printf "\t%s\t%s",$insn,join(",",map($_->out($sz),@args));
+	    }
+	} else {
+	    printf "\t%s",$opcode->out();
+	}
+    }
+
+    print $line,"\n";
+}
+
+print "\n$current_segment\tENDS\n"	if ($current_segment && $masm);
+print "END\n"				if ($masm);
+
+close STDOUT;
+
+#################################################
+# Cross-reference x86_64 ABI "card"
+#
+# 		Unix		Win64
+# %rax		*		*
+# %rbx		-		-
+# %rcx		#4		#1
+# %rdx		#3		#2
+# %rsi		#2		-
+# %rdi		#1		-
+# %rbp		-		-
+# %rsp		-		-
+# %r8		#5		#3
+# %r9		#6		#4
+# %r10		*		*
+# %r11		*		*
+# %r12		-		-
+# %r13		-		-
+# %r14		-		-
+# %r15		-		-
+# 
+# (*)	volatile register
+# (-)	preserved by callee
+# (#)	Nth argument, volatile
+#
+# In Unix terms top of stack is argument transfer area for arguments
+# which could not be accommodated in registers. Or in other words 7th
+# [integer] argument resides at 8(%rsp) upon function entry point.
+# 128 bytes above %rsp constitute a "red zone" which is not touched
+# by signal handlers and can be used as temporal storage without
+# allocating a frame.
+#
+# In Win64 terms N*8 bytes on top of stack is argument transfer area,
+# which belongs to/can be overwritten by callee. N is the number of
+# arguments passed to callee, *but* not less than 4! This means that
+# upon function entry point 5th argument resides at 40(%rsp), as well
+# as that 32 bytes from 8(%rsp) can always be used as temporal
+# storage [without allocating a frame]. One can actually argue that
+# one can assume a "red zone" above stack pointer under Win64 as well.
+# Point is that at apparently no occasion Windows kernel would alter
+# the area above user stack pointer in true asynchronous manner...
+#
+# All the above means that if assembler programmer adheres to Unix
+# register and stack layout, but disregards the "red zone" existense,
+# it's possible to use following prologue and epilogue to "gear" from
+# Unix to Win64 ABI in leaf functions with not more than 6 arguments.
+#
+# omnipotent_function:
+# ifdef WIN64
+#	movq	%rdi,8(%rsp)
+#	movq	%rsi,16(%rsp)
+#	movq	%rcx,%rdi	; if 1st argument is actually present
+#	movq	%rdx,%rsi	; if 2nd argument is actually ...
+#	movq	%r8,%rdx	; if 3rd argument is ...
+#	movq	%r9,%rcx	; if 4th argument ...
+#	movq	40(%rsp),%r8	; if 5th ...
+#	movq	48(%rsp),%r9	; if 6th ...
+# endif
+#	...
+# ifdef WIN64
+#	movq	8(%rsp),%rdi
+#	movq	16(%rsp),%rsi
+# endif
+#	ret
+#
+#################################################
+# Win64 SEH, Structured Exception Handling.
+#
+# Unlike on Unix systems(*) lack of Win64 stack unwinding information
+# has undesired side-effect at run-time: if an exception is raised in
+# assembler subroutine such as those in question (basically we're
+# referring to segmentation violations caused by malformed input
+# parameters), the application is briskly terminated without invoking
+# any exception handlers, most notably without generating memory dump
+# or any user notification whatsoever. This poses a problem. It's
+# possible to address it by registering custom language-specific
+# handler that would restore processor context to the state at
+# subroutine entry point and return "exception is not handled, keep
+# unwinding" code. Writing such handler can be a challenge... But it's
+# doable, though requires certain coding convention. Consider following
+# snippet:
+#
+# .type	function,@function
+# function:
+#	movq	%rsp,%rax	# copy rsp to volatile register
+#	pushq	%r15		# save non-volatile registers
+#	pushq	%rbx
+#	pushq	%rbp
+#	movq	%rsp,%r11
+#	subq	%rdi,%r11	# prepare [variable] stack frame
+#	andq	$-64,%r11
+#	movq	%rax,0(%r11)	# check for exceptions
+#	movq	%r11,%rsp	# allocate [variable] stack frame
+#	movq	%rax,0(%rsp)	# save original rsp value
+# magic_point:
+#	...
+#	movq	0(%rsp),%rcx	# pull original rsp value
+#	movq	-24(%rcx),%rbp	# restore non-volatile registers
+#	movq	-16(%rcx),%rbx
+#	movq	-8(%rcx),%r15
+#	movq	%rcx,%rsp	# restore original rsp
+#	ret
+# .size function,.-function
+#
+# The key is that up to magic_point copy of original rsp value remains
+# in chosen volatile register and no non-volatile register, except for
+# rsp, is modified. While past magic_point rsp remains constant till
+# the very end of the function. In this case custom language-specific
+# exception handler would look like this:
+#
+# EXCEPTION_DISPOSITION handler (EXCEPTION_RECORD *rec,ULONG64 frame,
+#		CONTEXT *context,DISPATCHER_CONTEXT *disp)
+# {	ULONG64 *rsp = (ULONG64 *)context->Rax;
+#	if (context->Rip >= magic_point)
+#	{   rsp = ((ULONG64 **)context->Rsp)[0];
+#	    context->Rbp = rsp[-3];
+#	    context->Rbx = rsp[-2];
+#	    context->R15 = rsp[-1];
+#	}
+#	context->Rsp = (ULONG64)rsp;
+#	context->Rdi = rsp[1];
+#	context->Rsi = rsp[2];
+#
+#	memcpy (disp->ContextRecord,context,sizeof(CONTEXT));
+#	RtlVirtualUnwind(UNW_FLAG_NHANDLER,disp->ImageBase,
+#		dips->ControlPc,disp->FunctionEntry,disp->ContextRecord,
+#		&disp->HandlerData,&disp->EstablisherFrame,NULL);
+#	return ExceptionContinueSearch;
+# }
+#
+# It's appropriate to implement this handler in assembler, directly in
+# function's module. In order to do that one has to know members'
+# offsets in CONTEXT and DISPATCHER_CONTEXT structures and some constant
+# values. Here they are:
+#
+#	CONTEXT.Rax				120
+#	CONTEXT.Rcx				128
+#	CONTEXT.Rdx				136
+#	CONTEXT.Rbx				144
+#	CONTEXT.Rsp				152
+#	CONTEXT.Rbp				160
+#	CONTEXT.Rsi				168
+#	CONTEXT.Rdi				176
+#	CONTEXT.R8				184
+#	CONTEXT.R9				192
+#	CONTEXT.R10				200
+#	CONTEXT.R11				208
+#	CONTEXT.R12				216
+#	CONTEXT.R13				224
+#	CONTEXT.R14				232
+#	CONTEXT.R15				240
+#	CONTEXT.Rip				248
+#	CONTEXT.Xmm6				512
+#	sizeof(CONTEXT)				1232
+#	DISPATCHER_CONTEXT.ControlPc		0
+#	DISPATCHER_CONTEXT.ImageBase		8
+#	DISPATCHER_CONTEXT.FunctionEntry	16
+#	DISPATCHER_CONTEXT.EstablisherFrame	24
+#	DISPATCHER_CONTEXT.TargetIp		32
+#	DISPATCHER_CONTEXT.ContextRecord	40
+#	DISPATCHER_CONTEXT.LanguageHandler	48
+#	DISPATCHER_CONTEXT.HandlerData		56
+#	UNW_FLAG_NHANDLER			0
+#	ExceptionContinueSearch			1
+#
+# In order to tie the handler to the function one has to compose
+# couple of structures: one for .xdata segment and one for .pdata.
+#
+# UNWIND_INFO structure for .xdata segment would be
+#
+# function_unwind_info:
+#	.byte	9,0,0,0
+#	.rva	handler
+#
+# This structure designates exception handler for a function with
+# zero-length prologue, no stack frame or frame register.
+#
+# To facilitate composing of .pdata structures, auto-generated "gear"
+# prologue copies rsp value to rax and denotes next instruction with
+# .LSEH_begin_{function_name} label. This essentially defines the SEH
+# styling rule mentioned in the beginning. Position of this label is
+# chosen in such manner that possible exceptions raised in the "gear"
+# prologue would be accounted to caller and unwound from latter's frame.
+# End of function is marked with respective .LSEH_end_{function_name}
+# label. To summarize, .pdata segment would contain
+#
+#	.rva	.LSEH_begin_function
+#	.rva	.LSEH_end_function
+#	.rva	function_unwind_info
+#
+# Reference to function_unwind_info from .xdata segment is the anchor.
+# In case you wonder why references are 32-bit .rvas and not 64-bit
+# .quads. References put into these two segments are required to be
+# *relative* to the base address of the current binary module, a.k.a.
+# image base. No Win64 module, be it .exe or .dll, can be larger than
+# 2GB and thus such relative references can be and are accommodated in
+# 32 bits.
+#
+# Having reviewed the example function code, one can argue that "movq
+# %rsp,%rax" above is redundant. It is not! Keep in mind that on Unix
+# rax would contain an undefined value. If this "offends" you, use
+# another register and refrain from modifying rax till magic_point is
+# reached, i.e. as if it was a non-volatile register. If more registers
+# are required prior [variable] frame setup is completed, note that
+# nobody says that you can have only one "magic point." You can
+# "liberate" non-volatile registers by denoting last stack off-load
+# instruction and reflecting it in finer grade unwind logic in handler.
+# After all, isn't it why it's called *language-specific* handler...
+#
+# Attentive reader can notice that exceptions would be mishandled in
+# auto-generated "gear" epilogue. Well, exception effectively can't
+# occur there, because if memory area used by it was subject to
+# segmentation violation, then it would be raised upon call to the
+# function (and as already mentioned be accounted to caller, which is
+# not a problem). If you're still not comfortable, then define tail
+# "magic point" just prior ret instruction and have handler treat it...
+#
+# (*)	Note that we're talking about run-time, not debug-time. Lack of
+#	unwind information makes debugging hard on both Windows and
+#	Unix. "Unlike" referes to the fact that on Unix signal handler
+#	will always be invoked, core dumped and appropriate exit code
+#	returned to parent (for user notification).
diff --git a/crypto/perlasm/x86asm.pl b/crypto/perlasm/x86asm.pl
new file mode 100644
index 000000000..1ff46c92c
--- /dev/null
+++ b/crypto/perlasm/x86asm.pl
@@ -0,0 +1,310 @@
+#! /usr/bin/env perl
+# Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+
+# require 'x86asm.pl';
+# &asm_init(,"des-586.pl"[,$i386only]);
+# &function_begin("foo");
+# ...
+# &function_end("foo");
+# &asm_finish
+
+$out=();
+$i386=0;
+
+# AUTOLOAD is this context has quite unpleasant side effect, namely
+# that typos in function calls effectively go to assembler output,
+# but on the pros side we don't have to implement one subroutine per
+# each opcode...
+sub ::AUTOLOAD
+{ my $opcode = $AUTOLOAD;
+
+    die "more than 4 arguments passed to $opcode" if ($#_>3);
+
+    $opcode =~ s/.*:://;
+    if    ($opcode =~ /^push/) { $stack+=4; }
+    elsif ($opcode =~ /^pop/)  { $stack-=4; }
+
+    &generic($opcode,@_) or die "undefined subroutine \&$AUTOLOAD";
+}
+
+sub ::emit
+{ my $opcode=shift;
+
+    if ($#_==-1)    { push(@out,"\t$opcode\n");				}
+    else            { push(@out,"\t$opcode\t".join(',',@_)."\n");	}
+}
+
+sub ::LB
+{   $_[0] =~ m/^e?([a-d])x$/o or die "$_[0] does not have a 'low byte'";
+  $1."l";
+}
+sub ::HB
+{   $_[0] =~ m/^e?([a-d])x$/o or die "$_[0] does not have a 'high byte'";
+  $1."h";
+}
+sub ::stack_push{ my $num=$_[0]*4; $stack+=$num; &sub("esp",$num);	}
+sub ::stack_pop	{ my $num=$_[0]*4; $stack-=$num; &add("esp",$num);	}
+sub ::blindpop	{ &pop($_[0]); $stack+=4;				}
+sub ::wparam	{ &DWP($stack+4*$_[0],"esp");				}
+sub ::swtmp	{ &DWP(4*$_[0],"esp");					}
+
+sub ::bswap
+{   if ($i386)	# emulate bswap for i386
+    {	&comment("bswap @_");
+	&xchg(&HB(@_),&LB(@_));
+	&ror (@_,16);
+	&xchg(&HB(@_),&LB(@_));
+    }
+    else
+    {	&generic("bswap",@_);	}
+}
+# These are made-up opcodes introduced over the years essentially
+# by ignorance, just alias them to real ones...
+sub ::movb	{ &mov(@_);	}
+sub ::xorb	{ &xor(@_);	}
+sub ::rotl	{ &rol(@_);	}
+sub ::rotr	{ &ror(@_);	}
+sub ::exch	{ &xchg(@_);	}
+sub ::halt	{ &hlt;		}
+sub ::movz	{ &movzx(@_);	}
+sub ::pushf	{ &pushfd;	}
+sub ::popf	{ &popfd;	}
+
+# 3 argument instructions
+sub ::movq
+{ my($p1,$p2,$optimize)=@_;
+
+    if ($optimize && $p1=~/^mm[0-7]$/ && $p2=~/^mm[0-7]$/)
+    # movq between mmx registers can sink Intel CPUs
+    {	&::pshufw($p1,$p2,0xe4);		}
+    else
+    {	&::generic("movq",@_);			}
+}
+
+# SSE>2 instructions
+my %regrm = (	"eax"=>0, "ecx"=>1, "edx"=>2, "ebx"=>3,
+		"esp"=>4, "ebp"=>5, "esi"=>6, "edi"=>7	);
+sub ::pextrd
+{ my($dst,$src,$imm)=@_;
+    if ("$dst:$src" =~ /(e[a-dsd][ixp]):xmm([0-7])/)
+    {	&::data_byte(0x66,0x0f,0x3a,0x16,0xc0|($2<<3)|$regrm{$1},$imm);	}
+    else
+    {	&::generic("pextrd",@_);		}
+}
+
+sub ::pinsrd
+{ my($dst,$src,$imm)=@_;
+    if ("$dst:$src" =~ /xmm([0-7]):(e[a-dsd][ixp])/)
+    {	&::data_byte(0x66,0x0f,0x3a,0x22,0xc0|($1<<3)|$regrm{$2},$imm);	}
+    else
+    {	&::generic("pinsrd",@_);		}
+}
+
+sub ::pshufb
+{ my($dst,$src)=@_;
+    if ("$dst:$src" =~ /xmm([0-7]):xmm([0-7])/)
+    {	&data_byte(0x66,0x0f,0x38,0x00,0xc0|($1<<3)|$2);	}
+    else
+    {	&::generic("pshufb",@_);		}
+}
+
+sub ::palignr
+{ my($dst,$src,$imm)=@_;
+    if ("$dst:$src" =~ /xmm([0-7]):xmm([0-7])/)
+    {	&::data_byte(0x66,0x0f,0x3a,0x0f,0xc0|($1<<3)|$2,$imm);	}
+    else
+    {	&::generic("palignr",@_);		}
+}
+
+sub ::pclmulqdq
+{ my($dst,$src,$imm)=@_;
+    if ("$dst:$src" =~ /xmm([0-7]):xmm([0-7])/)
+    {	&::data_byte(0x66,0x0f,0x3a,0x44,0xc0|($1<<3)|$2,$imm);	}
+    else
+    {	&::generic("pclmulqdq",@_);		}
+}
+
+sub ::rdrand
+{ my ($dst)=@_;
+    if ($dst =~ /(e[a-dsd][ixp])/)
+    {	&::data_byte(0x0f,0xc7,0xf0|$regrm{$dst});	}
+    else
+    {	&::generic("rdrand",@_);	}
+}
+
+sub ::rdseed
+{ my ($dst)=@_;
+    if ($dst =~ /(e[a-dsd][ixp])/)
+    {	&::data_byte(0x0f,0xc7,0xf8|$regrm{$dst});	}
+    else
+    {	&::generic("rdrand",@_);	}
+}
+
+sub rxb {
+ local *opcode=shift;
+ my ($dst,$src1,$src2,$rxb)=@_;
+
+   $rxb|=0x7<<5;
+   $rxb&=~(0x04<<5) if($dst>=8);
+   $rxb&=~(0x01<<5) if($src1>=8);
+   $rxb&=~(0x02<<5) if($src2>=8);
+   push @opcode,$rxb;
+}
+
+sub ::vprotd
+{ my $args=join(',',@_);
+    if ($args =~ /xmm([0-7]),xmm([0-7]),([x0-9a-f]+)/)
+    { my @opcode=(0x8f);
+	rxb(\@opcode,$1,$2,-1,0x08);
+	push @opcode,0x78,0xc2;
+	push @opcode,0xc0|($2&7)|(($1&7)<<3);		# ModR/M
+	my $c=$3;
+	push @opcode,$c=~/^0/?oct($c):$c;
+	&::data_byte(@opcode);
+    }
+    else
+    {	&::generic("vprotd",@_);	}
+}
+
+sub ::endbranch
+{
+    &::data_byte(0xf3,0x0f,0x1e,0xfb);
+}
+
+# label management
+$lbdecor="L";		# local label decoration, set by package
+$label="000";
+
+sub ::islabel		# see is argument is a known label
+{ my $i;
+    foreach $i (values %label) { return $i if ($i eq $_[0]); }
+  $label{$_[0]};	# can be undef
+}
+
+sub ::label		# instantiate a function-scope label
+{   if (!defined($label{$_[0]}))
+    {	$label{$_[0]}="${lbdecor}${label}${_[0]}"; $label++;   }
+  $label{$_[0]};
+}
+
+sub ::LABEL		# instantiate a file-scope label
+{   $label{$_[0]}=$_[1] if (!defined($label{$_[0]}));
+  $label{$_[0]};
+}
+
+sub ::static_label	{ &::LABEL($_[0],$lbdecor.$_[0]); }
+
+sub ::set_label_B	{ push(@out,"@_:\n"); }
+sub ::set_label
+{ my $label=&::label($_[0]);
+    &::align($_[1]) if ($_[1]>1);
+    &::set_label_B($label);
+  $label;
+}
+
+sub ::wipe_labels	# wipes function-scope labels
+{   foreach $i (keys %label)
+    {	delete $label{$i} if ($label{$i} =~ /^\Q${lbdecor}\E[0-9]{3}/);	}
+}
+
+# subroutine management
+sub ::function_begin
+{   &function_begin_B(@_);
+    $stack=4;
+    &push("ebp");
+    &push("ebx");
+    &push("esi");
+    &push("edi");
+}
+
+sub ::function_end
+{   &pop("edi");
+    &pop("esi");
+    &pop("ebx");
+    &pop("ebp");
+    &ret();
+    &function_end_B(@_);
+    $stack=0;
+    &wipe_labels();
+}
+
+sub ::function_end_A
+{   &pop("edi");
+    &pop("esi");
+    &pop("ebx");
+    &pop("ebp");
+    &ret();
+    $stack+=16;	# readjust esp as if we didn't pop anything
+}
+
+sub ::asciz
+{ my @str=unpack("C*",shift);
+    push @str,0;
+    while ($#str>15) {
+	&data_byte(@str[0..15]);
+	foreach (0..15) { shift @str; }
+    }
+    &data_byte(@str) if (@str);
+}
+
+sub ::asm_finish
+{   &file_end();
+    print @out;
+}
+
+sub ::asm_init
+{ my ($type,$fn,$cpu)=@_;
+
+    $filename=$fn;
+    $i386=$cpu;
+
+    $elf=$cpp=$coff=$aout=$macosx=$win32=$netware=$mwerks=$android=0;
+    if    (($type eq "elf"))
+    {	$elf=1;			require "x86gas.pl";	}
+    elsif (($type eq "elf-1"))
+    {	$elf=-1;		require "x86gas.pl";	}
+    elsif (($type eq "a\.out"))
+    {	$aout=1;		require "x86gas.pl";	}
+    elsif (($type eq "coff" or $type eq "gaswin"))
+    {	$coff=1;		require "x86gas.pl";	}
+    elsif (($type eq "win32n"))
+    {	$win32=1;		require "x86nasm.pl";	}
+    elsif (($type eq "nw-nasm"))
+    {	$netware=1;		require "x86nasm.pl";	}
+    #elsif (($type eq "nw-mwasm"))
+    #{	$netware=1; $mwerks=1;	require "x86nasm.pl";	}
+    elsif (($type eq "win32"))
+    {	$win32=1;		require "x86masm.pl";	}
+    elsif (($type eq "macosx"))
+    {	$aout=1; $macosx=1;	require "x86gas.pl";	}
+    elsif (($type eq "android"))
+    {	$elf=1; $android=1;	require "x86gas.pl";	}
+    else
+    {	print STDERR <<"EOF";
+Pick one target type from
+	elf	- Linux, FreeBSD, Solaris x86, etc.
+	a.out	- DJGPP, elder OpenBSD, etc.
+	coff	- GAS/COFF such as Win32 targets
+	win32n	- Windows 95/Windows NT NASM format
+	nw-nasm - NetWare NASM format
+	macosx	- Mac OS X
+EOF
+	exit(1);
+    }
+
+    $pic=0;
+    for (@ARGV) { $pic=1 if (/\-[fK]PIC/i); }
+
+    $filename =~ s/\.pl$//;
+    &file($filename);
+}
+
+sub ::hidden {}
+
+1;
diff --git a/crypto/perlasm/x86gas.pl b/crypto/perlasm/x86gas.pl
new file mode 100644
index 000000000..2c8fce077
--- /dev/null
+++ b/crypto/perlasm/x86gas.pl
@@ -0,0 +1,265 @@
+#! /usr/bin/env perl
+# Copyright 2007-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+
+package x86gas;
+
+*out=\@::out;
+
+$::lbdecor=$::aout?"L":".L";		# local label decoration
+$nmdecor=($::aout or $::coff)?"_":"";	# external name decoration
+
+$initseg="";
+
+$align=16;
+$align=log($align)/log(2) if ($::aout);
+$com_start="#" if ($::aout or $::coff);
+
+sub opsize()
+{ my $reg=shift;
+    if    ($reg =~ m/^%e/o)		{ "l"; }
+    elsif ($reg =~ m/^%[a-d][hl]$/o)	{ "b"; }
+    elsif ($reg =~ m/^%[yxm]/o)		{ undef; }
+    else				{ "w"; }
+}
+
+# swap arguments;
+# expand opcode with size suffix;
+# prefix numeric constants with $;
+sub ::generic
+{ my($opcode,@arg)=@_;
+  my($suffix,$dst,$src);
+
+    @arg=reverse(@arg);
+
+    for (@arg)
+    {	s/^(\*?)(e?[a-dsixphl]{2})$/$1%$2/o;	# gp registers
+	s/^([xy]?mm[0-7])$/%$1/o;		# xmm/mmx registers
+	s/^(\-?[0-9]+)$/\$$1/o;			# constants
+	s/^(\-?0x[0-9a-f]+)$/\$$1/o;		# constants
+    }
+
+    $dst = $arg[$#arg]		if ($#arg>=0);
+    $src = $arg[$#arg-1]	if ($#arg>=1);
+    if    ($dst =~ m/^%/o)	{ $suffix=&opsize($dst); }
+    elsif ($src =~ m/^%/o)	{ $suffix=&opsize($src); }
+    else			{ $suffix="l";           }
+    undef $suffix if ($dst =~ m/^%[xm]/o || $src =~ m/^%[xm]/o);
+
+    if ($#_==0)				{ &::emit($opcode);		}
+    elsif ($#_==1 && $opcode =~ m/^(call|clflush|j|loop|set)/o)
+					{ &::emit($opcode,@arg);	}
+    else				{ &::emit($opcode.$suffix,@arg);}
+
+  1;
+}
+#
+# opcodes not covered by ::generic above, mostly inconsistent namings...
+#
+sub ::movzx	{ &::movzb(@_);			}
+sub ::pushfd	{ &::pushfl;			}
+sub ::popfd	{ &::popfl;			}
+sub ::cpuid	{ &::emit(".byte\t0x0f,0xa2");	}
+sub ::rdtsc	{ &::emit(".byte\t0x0f,0x31");	}
+
+sub ::call	{ &::emit("call",(&::islabel($_[0]) or "$nmdecor$_[0]")); }
+sub ::call_ptr	{ &::generic("call","*$_[0]");	}
+sub ::jmp_ptr	{ &::generic("jmp","*$_[0]");	}
+
+*::bswap = sub	{ &::emit("bswap","%$_[0]");	} if (!$::i386);
+
+sub ::DWP
+{ my($addr,$reg1,$reg2,$idx)=@_;
+  my $ret="";
+
+    if (!defined($idx) && 1*$reg2) { $idx=$reg2; $reg2=$reg1; undef $reg1; }
+
+    $addr =~ s/^\s+//;
+    # prepend global references with optional underscore
+    $addr =~ s/^([^\+\-0-9][^\+\-]*)/&::islabel($1) or "$nmdecor$1"/ige;
+
+    $reg1 = "%$reg1" if ($reg1);
+    $reg2 = "%$reg2" if ($reg2);
+
+    $ret .= $addr if (($addr ne "") && ($addr ne 0));
+
+    if ($reg2)
+    {	$idx!= 0 or $idx=1;
+	$ret .= "($reg1,$reg2,$idx)";
+    }
+    elsif ($reg1)
+    {	$ret .= "($reg1)";	}
+
+  $ret;
+}
+sub ::QWP	{ &::DWP(@_);	}
+sub ::BP	{ &::DWP(@_);	}
+sub ::WP	{ &::DWP(@_);	}
+sub ::BC	{ @_;		}
+sub ::DWC	{ @_;		}
+
+sub ::file
+{   push(@out,".file\t\"$_[0].s\"\n.text\n");	}
+
+sub ::function_begin_B
+{ my $func=shift;
+  my $global=($func !~ /^_/);
+  my $begin="${::lbdecor}_${func}_begin";
+
+    &::LABEL($func,$global?"$begin":"$nmdecor$func");
+    $func=$nmdecor.$func;
+
+    push(@out,".globl\t$func\n")	if ($global);
+    if ($::coff)
+    {	push(@out,".def\t$func;\t.scl\t".(3-$global).";\t.type\t32;\t.endef\n"); }
+    elsif (($::aout and !$::pic) or $::macosx)
+    { }
+    else
+    {	push(@out,".type	$func,\@function\n"); }
+    push(@out,".align\t$align\n");
+    push(@out,"$func:\n");
+    push(@out,"$begin:\n")		if ($global);
+    $::stack=4;
+}
+
+sub ::function_end_B
+{ my $func=shift;
+    push(@out,".size\t$nmdecor$func,.-".&::LABEL($func)."\n") if ($::elf);
+    $::stack=0;
+    &::wipe_labels();
+}
+
+sub ::comment
+	{
+	if (!defined($com_start) or $::elf)
+		{	# Regarding $::elf above...
+			# GNU and SVR4 as'es use different comment delimiters,
+		push(@out,"\n");	# so we just skip ELF comments...
+		return;
+		}
+	foreach (@_)
+		{
+		if (/^\s*$/)
+			{ push(@out,"\n"); }
+		else
+			{ push(@out,"\t$com_start $_ $com_end\n"); }
+		}
+	}
+
+sub ::external_label
+{   foreach(@_) { &::LABEL($_,$nmdecor.$_); }   }
+
+sub ::public_label
+{   push(@out,".globl\t".&::LABEL($_[0],$nmdecor.$_[0])."\n");   }
+
+sub ::file_end
+{   if ($::macosx)
+    {	if (%non_lazy_ptr)
+    	{   push(@out,".section __IMPORT,__pointers,non_lazy_symbol_pointers\n");
+	    foreach $i (keys %non_lazy_ptr)
+	    {	push(@out,"$non_lazy_ptr{$i}:\n.indirect_symbol\t$i\n.long\t0\n");   }
+	}
+    }
+    if (grep {/\b${nmdecor}OPENSSL_ia32cap_P\b/i} @out) {
+	my $tmp=".comm\t${nmdecor}OPENSSL_ia32cap_P,16";
+	if ($::macosx)	{ push (@out,"$tmp,2\n"); }
+	elsif ($::elf)	{ push (@out,"$tmp,4\n"); }
+	else		{ push (@out,"$tmp\n"); }
+    }
+    push(@out,$initseg) if ($initseg);
+}
+
+sub ::data_byte	{   push(@out,".byte\t".join(',',@_)."\n");   }
+sub ::data_short{   push(@out,".value\t".join(',',@_)."\n");  }
+sub ::data_word {   push(@out,".long\t".join(',',@_)."\n");   }
+
+sub ::align
+{ my $val=$_[0];
+    if ($::aout)
+    {	$val=int(log($val)/log(2));
+	$val.=",0x90";
+    }
+    push(@out,".align\t$val\n");
+}
+
+sub ::picmeup
+{ my($dst,$sym,$base,$reflabel)=@_;
+
+    if (($::pic && ($::elf || $::aout)) || $::macosx)
+    {	if (!defined($base))
+	{   &::call(&::label("PIC_me_up"));
+	    &::set_label("PIC_me_up");
+	    &::blindpop($dst);
+	    $base=$dst;
+	    $reflabel=&::label("PIC_me_up");
+	}
+	if ($::macosx)
+	{   my $indirect=&::static_label("$nmdecor$sym\$non_lazy_ptr");
+	    &::mov($dst,&::DWP("$indirect-$reflabel",$base));
+	    $non_lazy_ptr{"$nmdecor$sym"}=$indirect;
+	}
+	elsif ($sym eq "OPENSSL_ia32cap_P" && $::elf>0)
+	{   &::lea($dst,&::DWP("$sym-$reflabel",$base));   }
+	else
+	{   &::lea($dst,&::DWP("_GLOBAL_OFFSET_TABLE_+[.-$reflabel]",
+			    $base));
+	    &::mov($dst,&::DWP("$sym\@GOT",$dst));
+	}
+    }
+    else
+    {	&::lea($dst,&::DWP($sym));	}
+}
+
+sub ::initseg
+{ my $f=$nmdecor.shift;
+
+    if ($::android)
+    {	$initseg.=<<___;
+.section	.init_array
+.align	4
+.long	$f
+___
+    }
+    elsif ($::elf)
+    {	$initseg.=<<___;
+.section	.init
+	call	$f
+___
+    }
+    elsif ($::coff)
+    {   $initseg.=<<___;	# applies to both Cygwin and Mingw
+.section	.ctors
+.long	$f
+___
+    }
+    elsif ($::macosx)
+    {	$initseg.=<<___;
+.mod_init_func
+.align 2
+.long   $f
+___
+    }
+    elsif ($::aout)
+    {	my $ctor="${nmdecor}_GLOBAL_\$I\$$f";
+	$initseg.=".text\n";
+	$initseg.=".type	$ctor,\@function\n" if ($::pic);
+	$initseg.=<<___;	# OpenBSD way...
+.globl	$ctor
+.align	2
+$ctor:
+	jmp	$f
+___
+    }
+}
+
+sub ::dataseg
+{   push(@out,".data\n");   }
+
+*::hidden = sub { push(@out,".hidden\t$nmdecor$_[0]\n"); } if ($::elf);
+
+1;
diff --git a/crypto/perlasm/x86masm.pl b/crypto/perlasm/x86masm.pl
new file mode 100644
index 000000000..d352f4705
--- /dev/null
+++ b/crypto/perlasm/x86masm.pl
@@ -0,0 +1,207 @@
+#! /usr/bin/env perl
+# Copyright 2007-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+
+package x86masm;
+
+*out=\@::out;
+
+$::lbdecor="\$L";	# local label decoration
+$nmdecor="_";		# external name decoration
+
+$initseg="";
+$segment="";
+
+sub ::generic
+{ my ($opcode,@arg)=@_;
+
+    # fix hexadecimal constants
+    for (@arg) { s/(?= 0x02030000\n");
+    push(@out,"safeseh	".&::LABEL($nm,$nmdecor.$nm)."\n");
+    push(@out,"%endif\n");
+}
+
+1;
diff --git a/crypto/pkcs12/build.info b/crypto/pkcs12/build.info
new file mode 100644
index 000000000..b87299e6c
--- /dev/null
+++ b/crypto/pkcs12/build.info
@@ -0,0 +1,5 @@
+LIBS=../../libcrypto
+SOURCE[../../libcrypto]=\
+        p12_add.c p12_asn.c p12_attr.c p12_crpt.c p12_crt.c p12_decr.c \
+        p12_init.c p12_key.c p12_kiss.c p12_mutl.c p12_sbag.c \
+        p12_utl.c p12_npas.c pk12err.c p12_p8d.c p12_p8e.c
diff --git a/crypto/pkcs12/p12_add.c b/crypto/pkcs12/p12_add.c
new file mode 100644
index 000000000..193ed8097
--- /dev/null
+++ b/crypto/pkcs12/p12_add.c
@@ -0,0 +1,164 @@
+/*
+ * Copyright 1999-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include "p12_lcl.h"
+
+/* Pack an object into an OCTET STRING and turn into a safebag */
+
+PKCS12_SAFEBAG *PKCS12_item_pack_safebag(void *obj, const ASN1_ITEM *it,
+                                         int nid1, int nid2)
+{
+    PKCS12_BAGS *bag;
+    PKCS12_SAFEBAG *safebag;
+
+    if ((bag = PKCS12_BAGS_new()) == NULL) {
+        PKCS12err(PKCS12_F_PKCS12_ITEM_PACK_SAFEBAG, ERR_R_MALLOC_FAILURE);
+        return NULL;
+    }
+    bag->type = OBJ_nid2obj(nid1);
+    if (!ASN1_item_pack(obj, it, &bag->value.octet)) {
+        PKCS12err(PKCS12_F_PKCS12_ITEM_PACK_SAFEBAG, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+    if ((safebag = PKCS12_SAFEBAG_new()) == NULL) {
+        PKCS12err(PKCS12_F_PKCS12_ITEM_PACK_SAFEBAG, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+    safebag->value.bag = bag;
+    safebag->type = OBJ_nid2obj(nid2);
+    return safebag;
+
+ err:
+    PKCS12_BAGS_free(bag);
+    return NULL;
+}
+
+/* Turn a stack of SAFEBAGS into a PKCS#7 data Contentinfo */
+PKCS7 *PKCS12_pack_p7data(STACK_OF(PKCS12_SAFEBAG) *sk)
+{
+    PKCS7 *p7;
+
+    if ((p7 = PKCS7_new()) == NULL) {
+        PKCS12err(PKCS12_F_PKCS12_PACK_P7DATA, ERR_R_MALLOC_FAILURE);
+        return NULL;
+    }
+    p7->type = OBJ_nid2obj(NID_pkcs7_data);
+    if ((p7->d.data = ASN1_OCTET_STRING_new()) == NULL) {
+        PKCS12err(PKCS12_F_PKCS12_PACK_P7DATA, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+
+    if (!ASN1_item_pack(sk, ASN1_ITEM_rptr(PKCS12_SAFEBAGS), &p7->d.data)) {
+        PKCS12err(PKCS12_F_PKCS12_PACK_P7DATA, PKCS12_R_CANT_PACK_STRUCTURE);
+        goto err;
+    }
+    return p7;
+
+ err:
+    PKCS7_free(p7);
+    return NULL;
+}
+
+/* Unpack SAFEBAGS from PKCS#7 data ContentInfo */
+STACK_OF(PKCS12_SAFEBAG) *PKCS12_unpack_p7data(PKCS7 *p7)
+{
+    if (!PKCS7_type_is_data(p7)) {
+        PKCS12err(PKCS12_F_PKCS12_UNPACK_P7DATA,
+                  PKCS12_R_CONTENT_TYPE_NOT_DATA);
+        return NULL;
+    }
+    return ASN1_item_unpack(p7->d.data, ASN1_ITEM_rptr(PKCS12_SAFEBAGS));
+}
+
+/* Turn a stack of SAFEBAGS into a PKCS#7 encrypted data ContentInfo */
+
+PKCS7 *PKCS12_pack_p7encdata(int pbe_nid, const char *pass, int passlen,
+                             unsigned char *salt, int saltlen, int iter,
+                             STACK_OF(PKCS12_SAFEBAG) *bags)
+{
+    PKCS7 *p7;
+    X509_ALGOR *pbe;
+    const EVP_CIPHER *pbe_ciph;
+
+    if ((p7 = PKCS7_new()) == NULL) {
+        PKCS12err(PKCS12_F_PKCS12_PACK_P7ENCDATA, ERR_R_MALLOC_FAILURE);
+        return NULL;
+    }
+    if (!PKCS7_set_type(p7, NID_pkcs7_encrypted)) {
+        PKCS12err(PKCS12_F_PKCS12_PACK_P7ENCDATA,
+                  PKCS12_R_ERROR_SETTING_ENCRYPTED_DATA_TYPE);
+        goto err;
+    }
+
+    pbe_ciph = EVP_get_cipherbynid(pbe_nid);
+
+    if (pbe_ciph)
+        pbe = PKCS5_pbe2_set(pbe_ciph, iter, salt, saltlen);
+    else
+        pbe = PKCS5_pbe_set(pbe_nid, iter, salt, saltlen);
+
+    if (!pbe) {
+        PKCS12err(PKCS12_F_PKCS12_PACK_P7ENCDATA, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+    X509_ALGOR_free(p7->d.encrypted->enc_data->algorithm);
+    p7->d.encrypted->enc_data->algorithm = pbe;
+    ASN1_OCTET_STRING_free(p7->d.encrypted->enc_data->enc_data);
+    if (!(p7->d.encrypted->enc_data->enc_data =
+          PKCS12_item_i2d_encrypt(pbe, ASN1_ITEM_rptr(PKCS12_SAFEBAGS), pass,
+                                  passlen, bags, 1))) {
+        PKCS12err(PKCS12_F_PKCS12_PACK_P7ENCDATA, PKCS12_R_ENCRYPT_ERROR);
+        goto err;
+    }
+
+    return p7;
+
+ err:
+    PKCS7_free(p7);
+    return NULL;
+}
+
+STACK_OF(PKCS12_SAFEBAG) *PKCS12_unpack_p7encdata(PKCS7 *p7, const char *pass,
+                                                  int passlen)
+{
+    if (!PKCS7_type_is_encrypted(p7))
+        return NULL;
+    return PKCS12_item_decrypt_d2i(p7->d.encrypted->enc_data->algorithm,
+                                   ASN1_ITEM_rptr(PKCS12_SAFEBAGS),
+                                   pass, passlen,
+                                   p7->d.encrypted->enc_data->enc_data, 1);
+}
+
+PKCS8_PRIV_KEY_INFO *PKCS12_decrypt_skey(const PKCS12_SAFEBAG *bag,
+                                         const char *pass, int passlen)
+{
+    return PKCS8_decrypt(bag->value.shkeybag, pass, passlen);
+}
+
+int PKCS12_pack_authsafes(PKCS12 *p12, STACK_OF(PKCS7) *safes)
+{
+    if (ASN1_item_pack(safes, ASN1_ITEM_rptr(PKCS12_AUTHSAFES),
+                       &p12->authsafes->d.data))
+        return 1;
+    return 0;
+}
+
+STACK_OF(PKCS7) *PKCS12_unpack_authsafes(const PKCS12 *p12)
+{
+    if (!PKCS7_type_is_data(p12->authsafes)) {
+        PKCS12err(PKCS12_F_PKCS12_UNPACK_AUTHSAFES,
+                  PKCS12_R_CONTENT_TYPE_NOT_DATA);
+        return NULL;
+    }
+    return ASN1_item_unpack(p12->authsafes->d.data,
+                            ASN1_ITEM_rptr(PKCS12_AUTHSAFES));
+}
diff --git a/crypto/pkcs12/p12_asn.c b/crypto/pkcs12/p12_asn.c
new file mode 100644
index 000000000..f2bfe32eb
--- /dev/null
+++ b/crypto/pkcs12/p12_asn.c
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2000-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+#include "p12_lcl.h"
+
+/* PKCS#12 ASN1 module */
+
+ASN1_SEQUENCE(PKCS12) = {
+        ASN1_SIMPLE(PKCS12, version, ASN1_INTEGER),
+        ASN1_SIMPLE(PKCS12, authsafes, PKCS7),
+        ASN1_OPT(PKCS12, mac, PKCS12_MAC_DATA)
+} ASN1_SEQUENCE_END(PKCS12)
+
+IMPLEMENT_ASN1_FUNCTIONS(PKCS12)
+
+ASN1_SEQUENCE(PKCS12_MAC_DATA) = {
+        ASN1_SIMPLE(PKCS12_MAC_DATA, dinfo, X509_SIG),
+        ASN1_SIMPLE(PKCS12_MAC_DATA, salt, ASN1_OCTET_STRING),
+        ASN1_OPT(PKCS12_MAC_DATA, iter, ASN1_INTEGER)
+} ASN1_SEQUENCE_END(PKCS12_MAC_DATA)
+
+IMPLEMENT_ASN1_FUNCTIONS(PKCS12_MAC_DATA)
+
+ASN1_ADB_TEMPLATE(bag_default) = ASN1_EXP(PKCS12_BAGS, value.other, ASN1_ANY, 0);
+
+ASN1_ADB(PKCS12_BAGS) = {
+        ADB_ENTRY(NID_x509Certificate, ASN1_EXP(PKCS12_BAGS, value.x509cert, ASN1_OCTET_STRING, 0)),
+        ADB_ENTRY(NID_x509Crl, ASN1_EXP(PKCS12_BAGS, value.x509crl, ASN1_OCTET_STRING, 0)),
+        ADB_ENTRY(NID_sdsiCertificate, ASN1_EXP(PKCS12_BAGS, value.sdsicert, ASN1_IA5STRING, 0)),
+} ASN1_ADB_END(PKCS12_BAGS, 0, type, 0, &bag_default_tt, NULL);
+
+ASN1_SEQUENCE(PKCS12_BAGS) = {
+        ASN1_SIMPLE(PKCS12_BAGS, type, ASN1_OBJECT),
+        ASN1_ADB_OBJECT(PKCS12_BAGS),
+} ASN1_SEQUENCE_END(PKCS12_BAGS)
+
+IMPLEMENT_ASN1_FUNCTIONS(PKCS12_BAGS)
+
+ASN1_ADB_TEMPLATE(safebag_default) = ASN1_EXP(PKCS12_SAFEBAG, value.other, ASN1_ANY, 0);
+
+ASN1_ADB(PKCS12_SAFEBAG) = {
+        ADB_ENTRY(NID_keyBag, ASN1_EXP(PKCS12_SAFEBAG, value.keybag, PKCS8_PRIV_KEY_INFO, 0)),
+        ADB_ENTRY(NID_pkcs8ShroudedKeyBag, ASN1_EXP(PKCS12_SAFEBAG, value.shkeybag, X509_SIG, 0)),
+        ADB_ENTRY(NID_safeContentsBag, ASN1_EXP_SET_OF(PKCS12_SAFEBAG, value.safes, PKCS12_SAFEBAG, 0)),
+        ADB_ENTRY(NID_certBag, ASN1_EXP(PKCS12_SAFEBAG, value.bag, PKCS12_BAGS, 0)),
+        ADB_ENTRY(NID_crlBag, ASN1_EXP(PKCS12_SAFEBAG, value.bag, PKCS12_BAGS, 0)),
+        ADB_ENTRY(NID_secretBag, ASN1_EXP(PKCS12_SAFEBAG, value.bag, PKCS12_BAGS, 0))
+} ASN1_ADB_END(PKCS12_SAFEBAG, 0, type, 0, &safebag_default_tt, NULL);
+
+ASN1_SEQUENCE(PKCS12_SAFEBAG) = {
+        ASN1_SIMPLE(PKCS12_SAFEBAG, type, ASN1_OBJECT),
+        ASN1_ADB_OBJECT(PKCS12_SAFEBAG),
+        ASN1_SET_OF_OPT(PKCS12_SAFEBAG, attrib, X509_ATTRIBUTE)
+} ASN1_SEQUENCE_END(PKCS12_SAFEBAG)
+
+IMPLEMENT_ASN1_FUNCTIONS(PKCS12_SAFEBAG)
+
+/* SEQUENCE OF SafeBag */
+ASN1_ITEM_TEMPLATE(PKCS12_SAFEBAGS) =
+        ASN1_EX_TEMPLATE_TYPE(ASN1_TFLG_SEQUENCE_OF, 0, PKCS12_SAFEBAGS, PKCS12_SAFEBAG)
+ASN1_ITEM_TEMPLATE_END(PKCS12_SAFEBAGS)
+
+/* Authsafes: SEQUENCE OF PKCS7 */
+ASN1_ITEM_TEMPLATE(PKCS12_AUTHSAFES) =
+        ASN1_EX_TEMPLATE_TYPE(ASN1_TFLG_SEQUENCE_OF, 0, PKCS12_AUTHSAFES, PKCS7)
+ASN1_ITEM_TEMPLATE_END(PKCS12_AUTHSAFES)
diff --git a/crypto/pkcs12/p12_attr.c b/crypto/pkcs12/p12_attr.c
new file mode 100644
index 000000000..c324f5051
--- /dev/null
+++ b/crypto/pkcs12/p12_attr.c
@@ -0,0 +1,103 @@
+/*
+ * Copyright 1999-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include "p12_lcl.h"
+
+/* Add a local keyid to a safebag */
+
+int PKCS12_add_localkeyid(PKCS12_SAFEBAG *bag, unsigned char *name,
+                          int namelen)
+{
+    if (X509at_add1_attr_by_NID(&bag->attrib, NID_localKeyID,
+                                V_ASN1_OCTET_STRING, name, namelen))
+        return 1;
+    else
+        return 0;
+}
+
+/* Add key usage to PKCS#8 structure */
+
+int PKCS8_add_keyusage(PKCS8_PRIV_KEY_INFO *p8, int usage)
+{
+    unsigned char us_val = (unsigned char)usage;
+    return PKCS8_pkey_add1_attr_by_NID(p8, NID_key_usage,
+                                       V_ASN1_BIT_STRING, &us_val, 1);
+}
+
+/* Add a friendlyname to a safebag */
+
+int PKCS12_add_friendlyname_asc(PKCS12_SAFEBAG *bag, const char *name,
+                                int namelen)
+{
+    if (X509at_add1_attr_by_NID(&bag->attrib, NID_friendlyName,
+                                MBSTRING_ASC, (unsigned char *)name, namelen))
+        return 1;
+    else
+        return 0;
+}
+
+int PKCS12_add_friendlyname_utf8(PKCS12_SAFEBAG *bag, const char *name,
+                                int namelen)
+{
+    if (X509at_add1_attr_by_NID(&bag->attrib, NID_friendlyName,
+                                MBSTRING_UTF8, (unsigned char *)name, namelen))
+        return 1;
+    else
+        return 0;
+}
+
+int PKCS12_add_friendlyname_uni(PKCS12_SAFEBAG *bag,
+                                const unsigned char *name, int namelen)
+{
+    if (X509at_add1_attr_by_NID(&bag->attrib, NID_friendlyName,
+                                MBSTRING_BMP, name, namelen))
+        return 1;
+    else
+        return 0;
+}
+
+int PKCS12_add_CSPName_asc(PKCS12_SAFEBAG *bag, const char *name, int namelen)
+{
+    if (X509at_add1_attr_by_NID(&bag->attrib, NID_ms_csp_name,
+                                MBSTRING_ASC, (unsigned char *)name, namelen))
+        return 1;
+    else
+        return 0;
+}
+
+ASN1_TYPE *PKCS12_get_attr_gen(const STACK_OF(X509_ATTRIBUTE) *attrs,
+                               int attr_nid)
+{
+    X509_ATTRIBUTE *attrib;
+    int i;
+    i = X509at_get_attr_by_NID(attrs, attr_nid, -1);
+    attrib = X509at_get_attr(attrs, i);
+    return X509_ATTRIBUTE_get0_type(attrib, 0);
+}
+
+char *PKCS12_get_friendlyname(PKCS12_SAFEBAG *bag)
+{
+    const ASN1_TYPE *atype;
+
+    if ((atype = PKCS12_SAFEBAG_get0_attr(bag, NID_friendlyName)) == NULL)
+        return NULL;
+    if (atype->type != V_ASN1_BMPSTRING)
+        return NULL;
+    return OPENSSL_uni2utf8(atype->value.bmpstring->data,
+                            atype->value.bmpstring->length);
+}
+
+const STACK_OF(X509_ATTRIBUTE) *
+PKCS12_SAFEBAG_get0_attrs(const PKCS12_SAFEBAG *bag)
+{
+    return bag->attrib;
+}
diff --git a/crypto/pkcs12/p12_crpt.c b/crypto/pkcs12/p12_crpt.c
new file mode 100644
index 000000000..feef9d1fc
--- /dev/null
+++ b/crypto/pkcs12/p12_crpt.c
@@ -0,0 +1,70 @@
+/*
+ * Copyright 1999-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+
+/* PKCS#12 PBE algorithms now in static table */
+
+void PKCS12_PBE_add(void)
+{
+}
+
+int PKCS12_PBE_keyivgen(EVP_CIPHER_CTX *ctx, const char *pass, int passlen,
+                        ASN1_TYPE *param, const EVP_CIPHER *cipher,
+                        const EVP_MD *md, int en_de)
+{
+    PBEPARAM *pbe;
+    int saltlen, iter, ret;
+    unsigned char *salt;
+    unsigned char key[EVP_MAX_KEY_LENGTH], iv[EVP_MAX_IV_LENGTH];
+    int (*pkcs12_key_gen)(const char *pass, int passlen,
+                          unsigned char *salt, int slen,
+                          int id, int iter, int n,
+                          unsigned char *out,
+                          const EVP_MD *md_type);
+
+    pkcs12_key_gen = PKCS12_key_gen_utf8;
+
+    if (cipher == NULL)
+        return 0;
+
+    /* Extract useful info from parameter */
+
+    pbe = ASN1_TYPE_unpack_sequence(ASN1_ITEM_rptr(PBEPARAM), param);
+    if (pbe == NULL) {
+        PKCS12err(PKCS12_F_PKCS12_PBE_KEYIVGEN, PKCS12_R_DECODE_ERROR);
+        return 0;
+    }
+
+    if (!pbe->iter)
+        iter = 1;
+    else
+        iter = ASN1_INTEGER_get(pbe->iter);
+    salt = pbe->salt->data;
+    saltlen = pbe->salt->length;
+    if (!(*pkcs12_key_gen)(pass, passlen, salt, saltlen, PKCS12_KEY_ID,
+                           iter, EVP_CIPHER_key_length(cipher), key, md)) {
+        PKCS12err(PKCS12_F_PKCS12_PBE_KEYIVGEN, PKCS12_R_KEY_GEN_ERROR);
+        PBEPARAM_free(pbe);
+        return 0;
+    }
+    if (!(*pkcs12_key_gen)(pass, passlen, salt, saltlen, PKCS12_IV_ID,
+                           iter, EVP_CIPHER_iv_length(cipher), iv, md)) {
+        PKCS12err(PKCS12_F_PKCS12_PBE_KEYIVGEN, PKCS12_R_IV_GEN_ERROR);
+        PBEPARAM_free(pbe);
+        return 0;
+    }
+    PBEPARAM_free(pbe);
+    ret = EVP_CipherInit_ex(ctx, cipher, NULL, key, iv, en_de);
+    OPENSSL_cleanse(key, EVP_MAX_KEY_LENGTH);
+    OPENSSL_cleanse(iv, EVP_MAX_IV_LENGTH);
+    return ret;
+}
diff --git a/crypto/pkcs12/p12_crt.c b/crypto/pkcs12/p12_crt.c
new file mode 100644
index 000000000..f031a7237
--- /dev/null
+++ b/crypto/pkcs12/p12_crt.c
@@ -0,0 +1,314 @@
+/*
+ * Copyright 1999-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include "p12_lcl.h"
+
+static int pkcs12_add_bag(STACK_OF(PKCS12_SAFEBAG) **pbags,
+                          PKCS12_SAFEBAG *bag);
+
+static int copy_bag_attr(PKCS12_SAFEBAG *bag, EVP_PKEY *pkey, int nid)
+{
+    int idx;
+    X509_ATTRIBUTE *attr;
+    idx = EVP_PKEY_get_attr_by_NID(pkey, nid, -1);
+    if (idx < 0)
+        return 1;
+    attr = EVP_PKEY_get_attr(pkey, idx);
+    if (!X509at_add1_attr(&bag->attrib, attr))
+        return 0;
+    return 1;
+}
+
+PKCS12 *PKCS12_create(const char *pass, const char *name, EVP_PKEY *pkey, X509 *cert,
+                      STACK_OF(X509) *ca, int nid_key, int nid_cert, int iter,
+                      int mac_iter, int keytype)
+{
+    PKCS12 *p12 = NULL;
+    STACK_OF(PKCS7) *safes = NULL;
+    STACK_OF(PKCS12_SAFEBAG) *bags = NULL;
+    PKCS12_SAFEBAG *bag = NULL;
+    int i;
+    unsigned char keyid[EVP_MAX_MD_SIZE];
+    unsigned int keyidlen = 0;
+    const EVP_MD *md;
+
+#ifndef OPENSSL_NO_SHA
+    md = EVP_sha1();
+#elif !defined(OPENSSL_NO_SM3)
+    md = EVP_sm3();
+#else
+    PKCS12err(PKCS12_F_PKCS12_CREATE, PKCS12_R_NO_AVAIABLE_DIGEST);
+    return 0;
+#endif
+
+    /* Set defaults */
+    if (!nid_cert) {
+#if !defined(OPENSSL_NO_SHA) && !defined(OPENSSL_NO_DES)
+        nid_cert = NID_pbe_WithSHA1And3_Key_TripleDES_CBC;
+#elif !defined(OPENSSL_NO_SHA) && !defined(OPENSSL_NO_RC2)
+        nid_cert = NID_pbe_WithSHA1And40BitRC2_CBC;
+#elif !defined(OPENSSL_NO_SM3) && !defined(OPENSSL_NO_SMS4)
+        nid_cert = NID_pbe_WithSM3AndSMS4_CBC;
+#else
+        PKCS12err(PKCS12_F_PKCS12_CREATE, PKCS12_R_NO_AVAIABLE_CIPHER);
+        return 0;
+#endif
+}
+
+    if (!nid_key) {
+#if !defined(OPENSSL_NO_SHA) && !defined(OPENSSL_NO_DES)
+        nid_key = NID_pbe_WithSHA1And3_Key_TripleDES_CBC;
+#elif !defined(OPENSSL_NO_SM3) && !defined(OPENSSL_NO_SMS4)
+        nid_key = NID_pbe_WithSM3AndSMS4_CBC;
+#endif
+    }
+
+    if (!iter)
+        iter = PKCS12_DEFAULT_ITER;
+    if (!mac_iter)
+        mac_iter = 1;
+
+    if (!pkey && !cert && !ca) {
+        PKCS12err(PKCS12_F_PKCS12_CREATE, PKCS12_R_INVALID_NULL_ARGUMENT);
+        return NULL;
+    }
+
+    if (pkey && cert) {
+        if (!X509_check_private_key(cert, pkey))
+            return NULL;
+        X509_digest(cert, md, keyid, &keyidlen);
+    }
+
+    if (cert) {
+        bag = PKCS12_add_cert(&bags, cert);
+        if (name && !PKCS12_add_friendlyname(bag, name, -1))
+            goto err;
+        if (keyidlen && !PKCS12_add_localkeyid(bag, keyid, keyidlen))
+            goto err;
+    }
+
+    /* Add all other certificates */
+    for (i = 0; i < sk_X509_num(ca); i++) {
+        if (!PKCS12_add_cert(&bags, sk_X509_value(ca, i)))
+            goto err;
+    }
+
+    if (bags && !PKCS12_add_safe(&safes, bags, nid_cert, iter, pass))
+        goto err;
+
+    sk_PKCS12_SAFEBAG_pop_free(bags, PKCS12_SAFEBAG_free);
+    bags = NULL;
+
+    if (pkey) {
+        bag = PKCS12_add_key(&bags, pkey, keytype, iter, nid_key, pass);
+
+        if (!bag)
+            goto err;
+
+        if (!copy_bag_attr(bag, pkey, NID_ms_csp_name))
+            goto err;
+        if (!copy_bag_attr(bag, pkey, NID_LocalKeySet))
+            goto err;
+
+        if (name && !PKCS12_add_friendlyname(bag, name, -1))
+            goto err;
+        if (keyidlen && !PKCS12_add_localkeyid(bag, keyid, keyidlen))
+            goto err;
+    }
+
+    if (bags && !PKCS12_add_safe(&safes, bags, -1, 0, NULL))
+        goto err;
+
+    sk_PKCS12_SAFEBAG_pop_free(bags, PKCS12_SAFEBAG_free);
+    bags = NULL;
+
+    p12 = PKCS12_add_safes(safes, 0);
+
+    if (!p12)
+        goto err;
+
+    sk_PKCS7_pop_free(safes, PKCS7_free);
+
+    safes = NULL;
+
+    if ((mac_iter != -1) &&
+        !PKCS12_set_mac(p12, pass, -1, NULL, 0, mac_iter, NULL))
+        goto err;
+
+    return p12;
+
+ err:
+    PKCS12_free(p12);
+    sk_PKCS7_pop_free(safes, PKCS7_free);
+    sk_PKCS12_SAFEBAG_pop_free(bags, PKCS12_SAFEBAG_free);
+    return NULL;
+
+}
+
+PKCS12_SAFEBAG *PKCS12_add_cert(STACK_OF(PKCS12_SAFEBAG) **pbags, X509 *cert)
+{
+    PKCS12_SAFEBAG *bag = NULL;
+    char *name;
+    int namelen = -1;
+    unsigned char *keyid;
+    int keyidlen = -1;
+
+    /* Add user certificate */
+    if ((bag = PKCS12_SAFEBAG_create_cert(cert)) == NULL)
+        goto err;
+
+    /*
+     * Use friendlyName and localKeyID in certificate. (if present)
+     */
+
+    name = (char *)X509_alias_get0(cert, &namelen);
+
+    if (name && !PKCS12_add_friendlyname(bag, name, namelen))
+        goto err;
+
+    keyid = X509_keyid_get0(cert, &keyidlen);
+
+    if (keyid && !PKCS12_add_localkeyid(bag, keyid, keyidlen))
+        goto err;
+
+    if (!pkcs12_add_bag(pbags, bag))
+        goto err;
+
+    return bag;
+
+ err:
+    PKCS12_SAFEBAG_free(bag);
+    return NULL;
+
+}
+
+PKCS12_SAFEBAG *PKCS12_add_key(STACK_OF(PKCS12_SAFEBAG) **pbags,
+                               EVP_PKEY *key, int key_usage, int iter,
+                               int nid_key, const char *pass)
+{
+
+    PKCS12_SAFEBAG *bag = NULL;
+    PKCS8_PRIV_KEY_INFO *p8 = NULL;
+
+    /* Make a PKCS#8 structure */
+    if ((p8 = EVP_PKEY2PKCS8(key)) == NULL)
+        goto err;
+    if (key_usage && !PKCS8_add_keyusage(p8, key_usage))
+        goto err;
+    if (nid_key != -1) {
+        bag = PKCS12_SAFEBAG_create_pkcs8_encrypt(nid_key, pass, -1, NULL, 0,
+                                                  iter, p8);
+        PKCS8_PRIV_KEY_INFO_free(p8);
+    } else
+        bag = PKCS12_SAFEBAG_create0_p8inf(p8);
+
+    if (!bag)
+        goto err;
+
+    if (!pkcs12_add_bag(pbags, bag))
+        goto err;
+
+    return bag;
+
+ err:
+    PKCS12_SAFEBAG_free(bag);
+    return NULL;
+
+}
+
+int PKCS12_add_safe(STACK_OF(PKCS7) **psafes, STACK_OF(PKCS12_SAFEBAG) *bags,
+                    int nid_safe, int iter, const char *pass)
+{
+    PKCS7 *p7 = NULL;
+    int free_safes = 0;
+
+    if (!*psafes) {
+        *psafes = sk_PKCS7_new_null();
+        if (!*psafes)
+            return 0;
+        free_safes = 1;
+    } else
+        free_safes = 0;
+
+    if (nid_safe == 0)
+#ifdef OPENSSL_NO_RC2
+        nid_safe = NID_pbe_WithSHA1And3_Key_TripleDES_CBC;
+#else
+        nid_safe = NID_pbe_WithSHA1And40BitRC2_CBC;
+#endif
+
+    if (nid_safe == -1)
+        p7 = PKCS12_pack_p7data(bags);
+    else
+        p7 = PKCS12_pack_p7encdata(nid_safe, pass, -1, NULL, 0, iter, bags);
+    if (!p7)
+        goto err;
+
+    if (!sk_PKCS7_push(*psafes, p7))
+        goto err;
+
+    return 1;
+
+ err:
+    if (free_safes) {
+        sk_PKCS7_free(*psafes);
+        *psafes = NULL;
+    }
+    PKCS7_free(p7);
+    return 0;
+
+}
+
+static int pkcs12_add_bag(STACK_OF(PKCS12_SAFEBAG) **pbags,
+                          PKCS12_SAFEBAG *bag)
+{
+    int free_bags;
+    if (!pbags)
+        return 1;
+    if (!*pbags) {
+        *pbags = sk_PKCS12_SAFEBAG_new_null();
+        if (!*pbags)
+            return 0;
+        free_bags = 1;
+    } else
+        free_bags = 0;
+
+    if (!sk_PKCS12_SAFEBAG_push(*pbags, bag)) {
+        if (free_bags) {
+            sk_PKCS12_SAFEBAG_free(*pbags);
+            *pbags = NULL;
+        }
+        return 0;
+    }
+
+    return 1;
+
+}
+
+PKCS12 *PKCS12_add_safes(STACK_OF(PKCS7) *safes, int nid_p7)
+{
+    PKCS12 *p12;
+    if (nid_p7 <= 0)
+        nid_p7 = NID_pkcs7_data;
+    p12 = PKCS12_init(nid_p7);
+
+    if (!p12)
+        return NULL;
+
+    if (!PKCS12_pack_authsafes(p12, safes)) {
+        PKCS12_free(p12);
+        return NULL;
+    }
+
+    return p12;
+
+}
diff --git a/crypto/pkcs12/p12_decr.c b/crypto/pkcs12/p12_decr.c
new file mode 100644
index 000000000..3c860584e
--- /dev/null
+++ b/crypto/pkcs12/p12_decr.c
@@ -0,0 +1,155 @@
+/*
+ * Copyright 1999-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+
+/* Define this to dump decrypted output to files called DERnnn */
+/*
+ * #define OPENSSL_DEBUG_DECRYPT
+ */
+
+/*
+ * Encrypt/Decrypt a buffer based on password and algor, result in a
+ * OPENSSL_malloc'ed buffer
+ */
+unsigned char *PKCS12_pbe_crypt(const X509_ALGOR *algor,
+                                const char *pass, int passlen,
+                                const unsigned char *in, int inlen,
+                                unsigned char **data, int *datalen, int en_de)
+{
+    unsigned char *out = NULL;
+    int outlen, i;
+    EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
+
+    if (ctx == NULL) {
+        PKCS12err(PKCS12_F_PKCS12_PBE_CRYPT, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+
+    /* Decrypt data */
+    if (!EVP_PBE_CipherInit(algor->algorithm, pass, passlen,
+                            algor->parameter, ctx, en_de)) {
+        PKCS12err(PKCS12_F_PKCS12_PBE_CRYPT,
+                  PKCS12_R_PKCS12_ALGOR_CIPHERINIT_ERROR);
+        goto err;
+    }
+
+    if ((out = OPENSSL_malloc(inlen + EVP_CIPHER_CTX_block_size(ctx)))
+            == NULL) {
+        PKCS12err(PKCS12_F_PKCS12_PBE_CRYPT, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+
+    if (!EVP_CipherUpdate(ctx, out, &i, in, inlen)) {
+        OPENSSL_free(out);
+        out = NULL;
+        PKCS12err(PKCS12_F_PKCS12_PBE_CRYPT, ERR_R_EVP_LIB);
+        goto err;
+    }
+
+    outlen = i;
+    if (!EVP_CipherFinal_ex(ctx, out + i, &i)) {
+        OPENSSL_free(out);
+        out = NULL;
+        PKCS12err(PKCS12_F_PKCS12_PBE_CRYPT,
+                  PKCS12_R_PKCS12_CIPHERFINAL_ERROR);
+        goto err;
+    }
+    outlen += i;
+    if (datalen)
+        *datalen = outlen;
+    if (data)
+        *data = out;
+ err:
+    EVP_CIPHER_CTX_free(ctx);
+    return out;
+
+}
+
+/*
+ * Decrypt an OCTET STRING and decode ASN1 structure if zbuf set zero buffer
+ * after use.
+ */
+
+void *PKCS12_item_decrypt_d2i(const X509_ALGOR *algor, const ASN1_ITEM *it,
+                              const char *pass, int passlen,
+                              const ASN1_OCTET_STRING *oct, int zbuf)
+{
+    unsigned char *out;
+    const unsigned char *p;
+    void *ret;
+    int outlen;
+
+    if (!PKCS12_pbe_crypt(algor, pass, passlen, oct->data, oct->length,
+                          &out, &outlen, 0)) {
+        PKCS12err(PKCS12_F_PKCS12_ITEM_DECRYPT_D2I,
+                  PKCS12_R_PKCS12_PBE_CRYPT_ERROR);
+        return NULL;
+    }
+    p = out;
+#ifdef OPENSSL_DEBUG_DECRYPT
+    {
+        FILE *op;
+
+        char fname[30];
+        static int fnm = 1;
+        sprintf(fname, "DER%d", fnm++);
+        op = fopen(fname, "wb");
+        fwrite(p, 1, outlen, op);
+        fclose(op);
+    }
+#endif
+    ret = ASN1_item_d2i(NULL, &p, outlen, it);
+    if (zbuf)
+        OPENSSL_cleanse(out, outlen);
+    if (!ret)
+        PKCS12err(PKCS12_F_PKCS12_ITEM_DECRYPT_D2I, PKCS12_R_DECODE_ERROR);
+    OPENSSL_free(out);
+    return ret;
+}
+
+/*
+ * Encode ASN1 structure and encrypt, return OCTET STRING if zbuf set zero
+ * encoding.
+ */
+
+ASN1_OCTET_STRING *PKCS12_item_i2d_encrypt(X509_ALGOR *algor,
+                                           const ASN1_ITEM *it,
+                                           const char *pass, int passlen,
+                                           void *obj, int zbuf)
+{
+    ASN1_OCTET_STRING *oct = NULL;
+    unsigned char *in = NULL;
+    int inlen;
+
+    if ((oct = ASN1_OCTET_STRING_new()) == NULL) {
+        PKCS12err(PKCS12_F_PKCS12_ITEM_I2D_ENCRYPT, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+    inlen = ASN1_item_i2d(obj, &in, it);
+    if (!in) {
+        PKCS12err(PKCS12_F_PKCS12_ITEM_I2D_ENCRYPT, PKCS12_R_ENCODE_ERROR);
+        goto err;
+    }
+    if (!PKCS12_pbe_crypt(algor, pass, passlen, in, inlen, &oct->data,
+                          &oct->length, 1)) {
+        PKCS12err(PKCS12_F_PKCS12_ITEM_I2D_ENCRYPT, PKCS12_R_ENCRYPT_ERROR);
+        OPENSSL_free(in);
+        goto err;
+    }
+    if (zbuf)
+        OPENSSL_cleanse(in, inlen);
+    OPENSSL_free(in);
+    return oct;
+ err:
+    ASN1_OCTET_STRING_free(oct);
+    return NULL;
+}
diff --git a/crypto/pkcs12/p12_init.c b/crypto/pkcs12/p12_init.c
new file mode 100644
index 000000000..a78e183c9
--- /dev/null
+++ b/crypto/pkcs12/p12_init.c
@@ -0,0 +1,43 @@
+/*
+ * Copyright 1999-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include "p12_lcl.h"
+
+/* Initialise a PKCS12 structure to take data */
+
+PKCS12 *PKCS12_init(int mode)
+{
+    PKCS12 *pkcs12;
+
+    if ((pkcs12 = PKCS12_new()) == NULL) {
+        PKCS12err(PKCS12_F_PKCS12_INIT, ERR_R_MALLOC_FAILURE);
+        return NULL;
+    }
+    ASN1_INTEGER_set(pkcs12->version, 3);
+    pkcs12->authsafes->type = OBJ_nid2obj(mode);
+    switch (mode) {
+    case NID_pkcs7_data:
+        if ((pkcs12->authsafes->d.data = ASN1_OCTET_STRING_new()) == NULL) {
+            PKCS12err(PKCS12_F_PKCS12_INIT, ERR_R_MALLOC_FAILURE);
+            goto err;
+        }
+        break;
+    default:
+        PKCS12err(PKCS12_F_PKCS12_INIT, PKCS12_R_UNSUPPORTED_PKCS12_MODE);
+        goto err;
+    }
+    return pkcs12;
+
+ err:
+    PKCS12_free(pkcs12);
+    return NULL;
+}
diff --git a/crypto/pkcs12/p12_key.c b/crypto/pkcs12/p12_key.c
new file mode 100644
index 000000000..9c13a451e
--- /dev/null
+++ b/crypto/pkcs12/p12_key.c
@@ -0,0 +1,205 @@
+/*
+ * Copyright 1999-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+
+/* Uncomment out this line to get debugging info about key generation */
+/*
+ * #define OPENSSL_DEBUG_KEYGEN
+ */
+#ifdef OPENSSL_DEBUG_KEYGEN
+# include 
+extern BIO *bio_err;
+void h__dump(unsigned char *p, int len);
+#endif
+
+/* PKCS12 compatible key/IV generation */
+#ifndef min
+# define min(a,b) ((a) < (b) ? (a) : (b))
+#endif
+
+int PKCS12_key_gen_asc(const char *pass, int passlen, unsigned char *salt,
+                       int saltlen, int id, int iter, int n,
+                       unsigned char *out, const EVP_MD *md_type)
+{
+    int ret;
+    unsigned char *unipass;
+    int uniplen;
+
+    if (!pass) {
+        unipass = NULL;
+        uniplen = 0;
+    } else if (!OPENSSL_asc2uni(pass, passlen, &unipass, &uniplen)) {
+        PKCS12err(PKCS12_F_PKCS12_KEY_GEN_ASC, ERR_R_MALLOC_FAILURE);
+        return 0;
+    }
+    ret = PKCS12_key_gen_uni(unipass, uniplen, salt, saltlen,
+                             id, iter, n, out, md_type);
+    if (ret <= 0)
+        return 0;
+    OPENSSL_clear_free(unipass, uniplen);
+    return ret;
+}
+
+int PKCS12_key_gen_utf8(const char *pass, int passlen, unsigned char *salt,
+                        int saltlen, int id, int iter, int n,
+                        unsigned char *out, const EVP_MD *md_type)
+{
+    int ret;
+    unsigned char *unipass;
+    int uniplen;
+
+    if (!pass) {
+        unipass = NULL;
+        uniplen = 0;
+    } else if (!OPENSSL_utf82uni(pass, passlen, &unipass, &uniplen)) {
+        PKCS12err(PKCS12_F_PKCS12_KEY_GEN_UTF8, ERR_R_MALLOC_FAILURE);
+        return 0;
+    }
+    ret = PKCS12_key_gen_uni(unipass, uniplen, salt, saltlen,
+                             id, iter, n, out, md_type);
+    if (ret <= 0)
+        return 0;
+    OPENSSL_clear_free(unipass, uniplen);
+    return ret;
+}
+
+int PKCS12_key_gen_uni(unsigned char *pass, int passlen, unsigned char *salt,
+                       int saltlen, int id, int iter, int n,
+                       unsigned char *out, const EVP_MD *md_type)
+{
+    unsigned char *B = NULL, *D = NULL, *I = NULL, *p = NULL, *Ai = NULL;
+    int Slen, Plen, Ilen, Ijlen;
+    int i, j, u, v;
+    int ret = 0;
+    BIGNUM *Ij = NULL, *Bpl1 = NULL; /* These hold Ij and B + 1 */
+    EVP_MD_CTX *ctx = NULL;
+#ifdef  OPENSSL_DEBUG_KEYGEN
+    unsigned char *tmpout = out;
+    int tmpn = n;
+#endif
+
+    ctx = EVP_MD_CTX_new();
+    if (ctx == NULL)
+        goto err;
+
+#ifdef  OPENSSL_DEBUG_KEYGEN
+    fprintf(stderr, "KEYGEN DEBUG\n");
+    fprintf(stderr, "ID %d, ITER %d\n", id, iter);
+    fprintf(stderr, "Password (length %d):\n", passlen);
+    h__dump(pass, passlen);
+    fprintf(stderr, "Salt (length %d):\n", saltlen);
+    h__dump(salt, saltlen);
+#endif
+    v = EVP_MD_block_size(md_type);
+    u = EVP_MD_size(md_type);
+    if (u < 0 || v <= 0)
+        goto err;
+    D = OPENSSL_malloc(v);
+    Ai = OPENSSL_malloc(u);
+    B = OPENSSL_malloc(v + 1);
+    Slen = v * ((saltlen + v - 1) / v);
+    if (passlen)
+        Plen = v * ((passlen + v - 1) / v);
+    else
+        Plen = 0;
+    Ilen = Slen + Plen;
+    I = OPENSSL_malloc(Ilen);
+    Ij = BN_new();
+    Bpl1 = BN_new();
+    if (D == NULL || Ai == NULL || B == NULL || I == NULL || Ij == NULL
+            || Bpl1 == NULL)
+        goto err;
+    for (i = 0; i < v; i++)
+        D[i] = id;
+    p = I;
+    for (i = 0; i < Slen; i++)
+        *p++ = salt[i % saltlen];
+    for (i = 0; i < Plen; i++)
+        *p++ = pass[i % passlen];
+    for (;;) {
+        if (!EVP_DigestInit_ex(ctx, md_type, NULL)
+            || !EVP_DigestUpdate(ctx, D, v)
+            || !EVP_DigestUpdate(ctx, I, Ilen)
+            || !EVP_DigestFinal_ex(ctx, Ai, NULL))
+            goto err;
+        for (j = 1; j < iter; j++) {
+            if (!EVP_DigestInit_ex(ctx, md_type, NULL)
+                || !EVP_DigestUpdate(ctx, Ai, u)
+                || !EVP_DigestFinal_ex(ctx, Ai, NULL))
+                goto err;
+        }
+        memcpy(out, Ai, min(n, u));
+        if (u >= n) {
+#ifdef OPENSSL_DEBUG_KEYGEN
+            fprintf(stderr, "Output KEY (length %d)\n", tmpn);
+            h__dump(tmpout, tmpn);
+#endif
+            ret = 1;
+            goto end;
+        }
+        n -= u;
+        out += u;
+        for (j = 0; j < v; j++)
+            B[j] = Ai[j % u];
+        /* Work out B + 1 first then can use B as tmp space */
+        if (!BN_bin2bn(B, v, Bpl1))
+            goto err;
+        if (!BN_add_word(Bpl1, 1))
+            goto err;
+        for (j = 0; j < Ilen; j += v) {
+            if (!BN_bin2bn(I + j, v, Ij))
+                goto err;
+            if (!BN_add(Ij, Ij, Bpl1))
+                goto err;
+            if (!BN_bn2bin(Ij, B))
+                goto err;
+            Ijlen = BN_num_bytes(Ij);
+            /* If more than 2^(v*8) - 1 cut off MSB */
+            if (Ijlen > v) {
+                if (!BN_bn2bin(Ij, B))
+                    goto err;
+                memcpy(I + j, B + 1, v);
+#ifndef PKCS12_BROKEN_KEYGEN
+                /* If less than v bytes pad with zeroes */
+            } else if (Ijlen < v) {
+                memset(I + j, 0, v - Ijlen);
+                if (!BN_bn2bin(Ij, I + j + v - Ijlen))
+                    goto err;
+#endif
+            } else if (!BN_bn2bin(Ij, I + j))
+                goto err;
+        }
+    }
+
+ err:
+    PKCS12err(PKCS12_F_PKCS12_KEY_GEN_UNI, ERR_R_MALLOC_FAILURE);
+
+ end:
+    OPENSSL_free(Ai);
+    OPENSSL_free(B);
+    OPENSSL_free(D);
+    OPENSSL_free(I);
+    BN_free(Ij);
+    BN_free(Bpl1);
+    EVP_MD_CTX_free(ctx);
+    return ret;
+}
+
+#ifdef OPENSSL_DEBUG_KEYGEN
+void h__dump(unsigned char *p, int len)
+{
+    for (; len--; p++)
+        fprintf(stderr, "%02X", *p);
+    fprintf(stderr, "\n");
+}
+#endif
diff --git a/crypto/pkcs12/p12_kiss.c b/crypto/pkcs12/p12_kiss.c
new file mode 100644
index 000000000..62f5d1ec1
--- /dev/null
+++ b/crypto/pkcs12/p12_kiss.c
@@ -0,0 +1,245 @@
+/*
+ * Copyright 1999-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+
+/* Simplified PKCS#12 routines */
+
+static int parse_pk12(PKCS12 *p12, const char *pass, int passlen,
+                      EVP_PKEY **pkey, STACK_OF(X509) *ocerts);
+
+static int parse_bags(const STACK_OF(PKCS12_SAFEBAG) *bags, const char *pass,
+                      int passlen, EVP_PKEY **pkey, STACK_OF(X509) *ocerts);
+
+static int parse_bag(PKCS12_SAFEBAG *bag, const char *pass, int passlen,
+                     EVP_PKEY **pkey, STACK_OF(X509) *ocerts);
+
+/*
+ * Parse and decrypt a PKCS#12 structure returning user key, user cert and
+ * other (CA) certs. Note either ca should be NULL, *ca should be NULL, or it
+ * should point to a valid STACK structure. pkey and cert can be passed
+ * uninitialised.
+ */
+
+int PKCS12_parse(PKCS12 *p12, const char *pass, EVP_PKEY **pkey, X509 **cert,
+                 STACK_OF(X509) **ca)
+{
+    STACK_OF(X509) *ocerts = NULL;
+    X509 *x = NULL;
+    /* Check for NULL PKCS12 structure */
+
+    if (!p12) {
+        PKCS12err(PKCS12_F_PKCS12_PARSE,
+                  PKCS12_R_INVALID_NULL_PKCS12_POINTER);
+        return 0;
+    }
+
+    if (pkey)
+        *pkey = NULL;
+    if (cert)
+        *cert = NULL;
+
+    /* Check the mac */
+
+    /*
+     * If password is zero length or NULL then try verifying both cases to
+     * determine which password is correct. The reason for this is that under
+     * PKCS#12 password based encryption no password and a zero length
+     * password are two different things...
+     */
+
+    if (!pass || !*pass) {
+        if (PKCS12_verify_mac(p12, NULL, 0))
+            pass = NULL;
+        else if (PKCS12_verify_mac(p12, "", 0))
+            pass = "";
+        else {
+            PKCS12err(PKCS12_F_PKCS12_PARSE, PKCS12_R_MAC_VERIFY_FAILURE);
+            goto err;
+        }
+    } else if (!PKCS12_verify_mac(p12, pass, -1)) {
+        PKCS12err(PKCS12_F_PKCS12_PARSE, PKCS12_R_MAC_VERIFY_FAILURE);
+        goto err;
+    }
+
+    /* Allocate stack for other certificates */
+    ocerts = sk_X509_new_null();
+
+    if (!ocerts) {
+        PKCS12err(PKCS12_F_PKCS12_PARSE, ERR_R_MALLOC_FAILURE);
+        return 0;
+    }
+
+    if (!parse_pk12(p12, pass, -1, pkey, ocerts)) {
+        PKCS12err(PKCS12_F_PKCS12_PARSE, PKCS12_R_PARSE_ERROR);
+        goto err;
+    }
+
+    while ((x = sk_X509_pop(ocerts))) {
+        if (pkey && *pkey && cert && !*cert) {
+            ERR_set_mark();
+            if (X509_check_private_key(x, *pkey)) {
+                *cert = x;
+                x = NULL;
+            }
+            ERR_pop_to_mark();
+        }
+
+        if (ca && x) {
+            if (!*ca)
+                *ca = sk_X509_new_null();
+            if (!*ca)
+                goto err;
+            if (!sk_X509_push(*ca, x))
+                goto err;
+            x = NULL;
+        }
+        X509_free(x);
+    }
+
+    sk_X509_pop_free(ocerts, X509_free);
+
+    return 1;
+
+ err:
+
+    if (pkey)
+        EVP_PKEY_free(*pkey);
+    if (cert)
+        X509_free(*cert);
+    X509_free(x);
+    sk_X509_pop_free(ocerts, X509_free);
+    return 0;
+
+}
+
+/* Parse the outer PKCS#12 structure */
+
+static int parse_pk12(PKCS12 *p12, const char *pass, int passlen,
+                      EVP_PKEY **pkey, STACK_OF(X509) *ocerts)
+{
+    STACK_OF(PKCS7) *asafes;
+    STACK_OF(PKCS12_SAFEBAG) *bags;
+    int i, bagnid;
+    PKCS7 *p7;
+
+    if ((asafes = PKCS12_unpack_authsafes(p12)) == NULL)
+        return 0;
+    for (i = 0; i < sk_PKCS7_num(asafes); i++) {
+        p7 = sk_PKCS7_value(asafes, i);
+        bagnid = OBJ_obj2nid(p7->type);
+        if (bagnid == NID_pkcs7_data) {
+            bags = PKCS12_unpack_p7data(p7);
+        } else if (bagnid == NID_pkcs7_encrypted) {
+            bags = PKCS12_unpack_p7encdata(p7, pass, passlen);
+        } else
+            continue;
+        if (!bags) {
+            sk_PKCS7_pop_free(asafes, PKCS7_free);
+            return 0;
+        }
+        if (!parse_bags(bags, pass, passlen, pkey, ocerts)) {
+            sk_PKCS12_SAFEBAG_pop_free(bags, PKCS12_SAFEBAG_free);
+            sk_PKCS7_pop_free(asafes, PKCS7_free);
+            return 0;
+        }
+        sk_PKCS12_SAFEBAG_pop_free(bags, PKCS12_SAFEBAG_free);
+    }
+    sk_PKCS7_pop_free(asafes, PKCS7_free);
+    return 1;
+}
+
+static int parse_bags(const STACK_OF(PKCS12_SAFEBAG) *bags, const char *pass,
+                      int passlen, EVP_PKEY **pkey, STACK_OF(X509) *ocerts)
+{
+    int i;
+    for (i = 0; i < sk_PKCS12_SAFEBAG_num(bags); i++) {
+        if (!parse_bag(sk_PKCS12_SAFEBAG_value(bags, i),
+                       pass, passlen, pkey, ocerts))
+            return 0;
+    }
+    return 1;
+}
+
+static int parse_bag(PKCS12_SAFEBAG *bag, const char *pass, int passlen,
+                     EVP_PKEY **pkey, STACK_OF(X509) *ocerts)
+{
+    PKCS8_PRIV_KEY_INFO *p8;
+    X509 *x509;
+    const ASN1_TYPE *attrib;
+    ASN1_BMPSTRING *fname = NULL;
+    ASN1_OCTET_STRING *lkid = NULL;
+
+    if ((attrib = PKCS12_SAFEBAG_get0_attr(bag, NID_friendlyName)))
+        fname = attrib->value.bmpstring;
+
+    if ((attrib = PKCS12_SAFEBAG_get0_attr(bag, NID_localKeyID)))
+        lkid = attrib->value.octet_string;
+
+    switch (PKCS12_SAFEBAG_get_nid(bag)) {
+    case NID_keyBag:
+        if (!pkey || *pkey)
+            return 1;
+        *pkey = EVP_PKCS82PKEY(PKCS12_SAFEBAG_get0_p8inf(bag));
+        if (*pkey == NULL)
+            return 0;
+        break;
+
+    case NID_pkcs8ShroudedKeyBag:
+        if (!pkey || *pkey)
+            return 1;
+        if ((p8 = PKCS12_decrypt_skey(bag, pass, passlen)) == NULL)
+            return 0;
+        *pkey = EVP_PKCS82PKEY(p8);
+        PKCS8_PRIV_KEY_INFO_free(p8);
+        if (!(*pkey))
+            return 0;
+        break;
+
+    case NID_certBag:
+        if (PKCS12_SAFEBAG_get_bag_nid(bag) != NID_x509Certificate)
+            return 1;
+        if ((x509 = PKCS12_SAFEBAG_get1_cert(bag)) == NULL)
+            return 0;
+        if (lkid && !X509_keyid_set1(x509, lkid->data, lkid->length)) {
+            X509_free(x509);
+            return 0;
+        }
+        if (fname) {
+            int len, r;
+            unsigned char *data;
+            len = ASN1_STRING_to_UTF8(&data, fname);
+            if (len >= 0) {
+                r = X509_alias_set1(x509, data, len);
+                OPENSSL_free(data);
+                if (!r) {
+                    X509_free(x509);
+                    return 0;
+                }
+            }
+        }
+
+        if (!sk_X509_push(ocerts, x509)) {
+            X509_free(x509);
+            return 0;
+        }
+
+        break;
+
+    case NID_safeContentsBag:
+        return parse_bags(PKCS12_SAFEBAG_get0_safes(bag), pass, passlen, pkey,
+                          ocerts);
+
+    default:
+        return 1;
+    }
+    return 1;
+}
diff --git a/crypto/pkcs12/p12_lcl.h b/crypto/pkcs12/p12_lcl.h
new file mode 100644
index 000000000..0b52f1e1f
--- /dev/null
+++ b/crypto/pkcs12/p12_lcl.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+struct PKCS12_MAC_DATA_st {
+    X509_SIG *dinfo;
+    ASN1_OCTET_STRING *salt;
+    ASN1_INTEGER *iter;         /* defaults to 1 */
+};
+
+struct PKCS12_st {
+    ASN1_INTEGER *version;
+    PKCS12_MAC_DATA *mac;
+    PKCS7 *authsafes;
+};
+
+struct PKCS12_SAFEBAG_st {
+    ASN1_OBJECT *type;
+    union {
+        struct pkcs12_bag_st *bag; /* secret, crl and certbag */
+        struct pkcs8_priv_key_info_st *keybag; /* keybag */
+        X509_SIG *shkeybag;     /* shrouded key bag */
+        STACK_OF(PKCS12_SAFEBAG) *safes;
+        ASN1_TYPE *other;
+    } value;
+    STACK_OF(X509_ATTRIBUTE) *attrib;
+};
+
+struct pkcs12_bag_st {
+    ASN1_OBJECT *type;
+    union {
+        ASN1_OCTET_STRING *x509cert;
+        ASN1_OCTET_STRING *x509crl;
+        ASN1_OCTET_STRING *octet;
+        ASN1_IA5STRING *sdsicert;
+        ASN1_TYPE *other;       /* Secret or other bag */
+    } value;
+};
diff --git a/crypto/pkcs12/p12_mutl.c b/crypto/pkcs12/p12_mutl.c
new file mode 100644
index 000000000..fe54261ca
--- /dev/null
+++ b/crypto/pkcs12/p12_mutl.c
@@ -0,0 +1,247 @@
+/*
+ * Copyright 1999-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+# include 
+# include "internal/cryptlib.h"
+# include 
+# include 
+# include 
+# include 
+# include "p12_lcl.h"
+
+int PKCS12_mac_present(const PKCS12 *p12)
+{
+    return p12->mac ? 1 : 0;
+}
+
+void PKCS12_get0_mac(const ASN1_OCTET_STRING **pmac,
+                     const X509_ALGOR **pmacalg,
+                     const ASN1_OCTET_STRING **psalt,
+                     const ASN1_INTEGER **piter,
+                     const PKCS12 *p12)
+{
+    if (p12->mac) {
+        X509_SIG_get0(p12->mac->dinfo, pmacalg, pmac);
+        if (psalt)
+            *psalt = p12->mac->salt;
+        if (piter)
+            *piter = p12->mac->iter;
+    } else {
+        if (pmac)
+            *pmac = NULL;
+        if (pmacalg)
+            *pmacalg = NULL;
+        if (psalt)
+            *psalt = NULL;
+        if (piter)
+            *piter = NULL;
+    }
+}
+
+# define TK26_MAC_KEY_LEN 32
+
+static int pkcs12_gen_gost_mac_key(const char *pass, int passlen,
+                                   const unsigned char *salt, int saltlen,
+                                   int iter, int keylen, unsigned char *key,
+                                   const EVP_MD *digest)
+{
+    unsigned char out[96];
+
+    if (keylen != TK26_MAC_KEY_LEN) {
+        return 0;
+    }
+
+    if (!PKCS5_PBKDF2_HMAC(pass, passlen, salt, saltlen, iter,
+                           digest, sizeof(out), out)) {
+        return 0;
+    }
+    memcpy(key, out + sizeof(out) - TK26_MAC_KEY_LEN, TK26_MAC_KEY_LEN);
+    OPENSSL_cleanse(out, sizeof(out));
+    return 1;
+}
+
+/* Generate a MAC */
+static int pkcs12_gen_mac(PKCS12 *p12, const char *pass, int passlen,
+                          unsigned char *mac, unsigned int *maclen,
+                          int (*pkcs12_key_gen)(const char *pass, int passlen,
+                                                unsigned char *salt, int slen,
+                                                int id, int iter, int n,
+                                                unsigned char *out,
+                                                const EVP_MD *md_type))
+{
+    const EVP_MD *md_type;
+    HMAC_CTX *hmac = NULL;
+    unsigned char key[EVP_MAX_MD_SIZE], *salt;
+    int saltlen, iter;
+    int md_size = 0;
+    int md_type_nid;
+    const X509_ALGOR *macalg;
+    const ASN1_OBJECT *macoid;
+
+    if (pkcs12_key_gen == NULL)
+        pkcs12_key_gen = PKCS12_key_gen_utf8;
+
+    if (!PKCS7_type_is_data(p12->authsafes)) {
+        PKCS12err(PKCS12_F_PKCS12_GEN_MAC, PKCS12_R_CONTENT_TYPE_NOT_DATA);
+        return 0;
+    }
+
+    salt = p12->mac->salt->data;
+    saltlen = p12->mac->salt->length;
+    if (!p12->mac->iter)
+        iter = 1;
+    else
+        iter = ASN1_INTEGER_get(p12->mac->iter);
+    X509_SIG_get0(p12->mac->dinfo, &macalg, NULL);
+    X509_ALGOR_get0(&macoid, NULL, NULL, macalg);
+    if ((md_type = EVP_get_digestbyobj(macoid)) == NULL) {
+        PKCS12err(PKCS12_F_PKCS12_GEN_MAC, PKCS12_R_UNKNOWN_DIGEST_ALGORITHM);
+        return 0;
+    }
+    md_size = EVP_MD_size(md_type);
+    md_type_nid = EVP_MD_type(md_type);
+    if (md_size < 0)
+        return 0;
+    if ((md_type_nid == NID_id_GostR3411_94
+         || md_type_nid == NID_id_GostR3411_2012_256
+         || md_type_nid == NID_id_GostR3411_2012_512)
+        && !getenv("LEGACY_GOST_PKCS12")) {
+        md_size = TK26_MAC_KEY_LEN;
+        if (!pkcs12_gen_gost_mac_key(pass, passlen, salt, saltlen, iter,
+                                     md_size, key, md_type)) {
+            PKCS12err(PKCS12_F_PKCS12_GEN_MAC, PKCS12_R_KEY_GEN_ERROR);
+            return 0;
+        }
+    } else
+        if (!(*pkcs12_key_gen)(pass, passlen, salt, saltlen, PKCS12_MAC_ID,
+                               iter, md_size, key, md_type)) {
+        PKCS12err(PKCS12_F_PKCS12_GEN_MAC, PKCS12_R_KEY_GEN_ERROR);
+        return 0;
+    }
+    hmac = HMAC_CTX_new();
+    if (!HMAC_Init_ex(hmac, key, md_size, md_type, NULL)
+        || !HMAC_Update(hmac, p12->authsafes->d.data->data,
+                        p12->authsafes->d.data->length)
+        || !HMAC_Final(hmac, mac, maclen)) {
+        HMAC_CTX_free(hmac);
+        return 0;
+    }
+    HMAC_CTX_free(hmac);
+    return 1;
+}
+
+int PKCS12_gen_mac(PKCS12 *p12, const char *pass, int passlen,
+                   unsigned char *mac, unsigned int *maclen)
+{
+    return pkcs12_gen_mac(p12, pass, passlen, mac, maclen, NULL);
+}
+
+/* Verify the mac */
+int PKCS12_verify_mac(PKCS12 *p12, const char *pass, int passlen)
+{
+    unsigned char mac[EVP_MAX_MD_SIZE];
+    unsigned int maclen;
+    const ASN1_OCTET_STRING *macoct;
+
+    if (p12->mac == NULL) {
+        PKCS12err(PKCS12_F_PKCS12_VERIFY_MAC, PKCS12_R_MAC_ABSENT);
+        return 0;
+    }
+    if (!pkcs12_gen_mac(p12, pass, passlen, mac, &maclen,
+                        PKCS12_key_gen_utf8)) {
+        PKCS12err(PKCS12_F_PKCS12_VERIFY_MAC, PKCS12_R_MAC_GENERATION_ERROR);
+        return 0;
+    }
+    X509_SIG_get0(p12->mac->dinfo, NULL, &macoct);
+    if ((maclen != (unsigned int)ASN1_STRING_length(macoct))
+        || CRYPTO_memcmp(mac, ASN1_STRING_get0_data(macoct), maclen) != 0)
+        return 0;
+
+    return 1;
+}
+
+/* Set a mac */
+
+int PKCS12_set_mac(PKCS12 *p12, const char *pass, int passlen,
+                   unsigned char *salt, int saltlen, int iter,
+                   const EVP_MD *md_type)
+{
+    unsigned char mac[EVP_MAX_MD_SIZE];
+    unsigned int maclen;
+    ASN1_OCTET_STRING *macoct;
+
+    if (!md_type) {
+#ifndef OPENSSL_NO_SHA
+        md_type = EVP_sha1();
+#elif !defined(OPENSSL_NO_SM3)
+        md_type = EVP_sm3();
+#else
+        PKCS12err(PKCS12_F_PKCS12_SET_MAC, PKCS12_R_NO_AVAIABLE_DIGEST);
+        return 0;
+#endif
+    }
+    if (PKCS12_setup_mac(p12, iter, salt, saltlen, md_type) == PKCS12_ERROR) {
+        PKCS12err(PKCS12_F_PKCS12_SET_MAC, PKCS12_R_MAC_SETUP_ERROR);
+        return 0;
+    }
+    /*
+     * Note that output mac is forced to UTF-8...
+     */
+    if (!pkcs12_gen_mac(p12, pass, passlen, mac, &maclen,
+                        PKCS12_key_gen_utf8)) {
+        PKCS12err(PKCS12_F_PKCS12_SET_MAC, PKCS12_R_MAC_GENERATION_ERROR);
+        return 0;
+    }
+    X509_SIG_getm(p12->mac->dinfo, NULL, &macoct);
+    if (!ASN1_OCTET_STRING_set(macoct, mac, maclen)) {
+        PKCS12err(PKCS12_F_PKCS12_SET_MAC, PKCS12_R_MAC_STRING_SET_ERROR);
+        return 0;
+    }
+    return 1;
+}
+
+/* Set up a mac structure */
+int PKCS12_setup_mac(PKCS12 *p12, int iter, unsigned char *salt, int saltlen,
+                     const EVP_MD *md_type)
+{
+    X509_ALGOR *macalg;
+
+    if ((p12->mac = PKCS12_MAC_DATA_new()) == NULL)
+        return PKCS12_ERROR;
+    if (iter > 1) {
+        if ((p12->mac->iter = ASN1_INTEGER_new()) == NULL) {
+            PKCS12err(PKCS12_F_PKCS12_SETUP_MAC, ERR_R_MALLOC_FAILURE);
+            return 0;
+        }
+        if (!ASN1_INTEGER_set(p12->mac->iter, iter)) {
+            PKCS12err(PKCS12_F_PKCS12_SETUP_MAC, ERR_R_MALLOC_FAILURE);
+            return 0;
+        }
+    }
+    if (!saltlen)
+        saltlen = PKCS12_SALT_LEN;
+    if ((p12->mac->salt->data = OPENSSL_malloc(saltlen)) == NULL) {
+        PKCS12err(PKCS12_F_PKCS12_SETUP_MAC, ERR_R_MALLOC_FAILURE);
+        return 0;
+    }
+    p12->mac->salt->length = saltlen;
+    if (!salt) {
+        if (RAND_bytes(p12->mac->salt->data, saltlen) <= 0)
+            return 0;
+    } else
+        memcpy(p12->mac->salt->data, salt, saltlen);
+    X509_SIG_getm(p12->mac->dinfo, &macalg, NULL);
+    if (!X509_ALGOR_set0(macalg, OBJ_nid2obj(EVP_MD_type(md_type)),
+                         V_ASN1_NULL, NULL)) {
+        PKCS12err(PKCS12_F_PKCS12_SETUP_MAC, ERR_R_MALLOC_FAILURE);
+        return 0;
+    }
+
+    return 1;
+}
diff --git a/crypto/pkcs12/p12_npas.c b/crypto/pkcs12/p12_npas.c
new file mode 100644
index 000000000..0ce75ed33
--- /dev/null
+++ b/crypto/pkcs12/p12_npas.c
@@ -0,0 +1,184 @@
+/*
+ * Copyright 1999-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include "p12_lcl.h"
+
+/* PKCS#12 password change routine */
+
+static int newpass_p12(PKCS12 *p12, const char *oldpass, const char *newpass);
+static int newpass_bags(STACK_OF(PKCS12_SAFEBAG) *bags, const char *oldpass,
+                        const char *newpass);
+static int newpass_bag(PKCS12_SAFEBAG *bag, const char *oldpass,
+                        const char *newpass);
+static int alg_get(const X509_ALGOR *alg, int *pnid, int *piter,
+                   int *psaltlen);
+
+/*
+ * Change the password on a PKCS#12 structure.
+ */
+
+int PKCS12_newpass(PKCS12 *p12, const char *oldpass, const char *newpass)
+{
+    /* Check for NULL PKCS12 structure */
+
+    if (!p12) {
+        PKCS12err(PKCS12_F_PKCS12_NEWPASS,
+                  PKCS12_R_INVALID_NULL_PKCS12_POINTER);
+        return 0;
+    }
+
+    /* Check the mac */
+
+    if (!PKCS12_verify_mac(p12, oldpass, -1)) {
+        PKCS12err(PKCS12_F_PKCS12_NEWPASS, PKCS12_R_MAC_VERIFY_FAILURE);
+        return 0;
+    }
+
+    if (!newpass_p12(p12, oldpass, newpass)) {
+        PKCS12err(PKCS12_F_PKCS12_NEWPASS, PKCS12_R_PARSE_ERROR);
+        return 0;
+    }
+
+    return 1;
+}
+
+/* Parse the outer PKCS#12 structure */
+
+static int newpass_p12(PKCS12 *p12, const char *oldpass, const char *newpass)
+{
+    STACK_OF(PKCS7) *asafes = NULL, *newsafes = NULL;
+    STACK_OF(PKCS12_SAFEBAG) *bags = NULL;
+    int i, bagnid, pbe_nid = 0, pbe_iter = 0, pbe_saltlen = 0;
+    PKCS7 *p7, *p7new;
+    ASN1_OCTET_STRING *p12_data_tmp = NULL, *macoct = NULL;
+    unsigned char mac[EVP_MAX_MD_SIZE];
+    unsigned int maclen;
+    int rv = 0;
+
+    if ((asafes = PKCS12_unpack_authsafes(p12)) == NULL)
+        goto err;
+    if ((newsafes = sk_PKCS7_new_null()) == NULL)
+        goto err;
+    for (i = 0; i < sk_PKCS7_num(asafes); i++) {
+        p7 = sk_PKCS7_value(asafes, i);
+        bagnid = OBJ_obj2nid(p7->type);
+        if (bagnid == NID_pkcs7_data) {
+            bags = PKCS12_unpack_p7data(p7);
+        } else if (bagnid == NID_pkcs7_encrypted) {
+            bags = PKCS12_unpack_p7encdata(p7, oldpass, -1);
+            if (!alg_get(p7->d.encrypted->enc_data->algorithm,
+                         &pbe_nid, &pbe_iter, &pbe_saltlen))
+                goto err;
+        } else {
+            continue;
+        }
+        if (bags == NULL)
+            goto err;
+        if (!newpass_bags(bags, oldpass, newpass))
+            goto err;
+        /* Repack bag in same form with new password */
+        if (bagnid == NID_pkcs7_data)
+            p7new = PKCS12_pack_p7data(bags);
+        else
+            p7new = PKCS12_pack_p7encdata(pbe_nid, newpass, -1, NULL,
+                                          pbe_saltlen, pbe_iter, bags);
+        if (!p7new || !sk_PKCS7_push(newsafes, p7new))
+            goto err;
+        sk_PKCS12_SAFEBAG_pop_free(bags, PKCS12_SAFEBAG_free);
+        bags = NULL;
+    }
+
+    /* Repack safe: save old safe in case of error */
+
+    p12_data_tmp = p12->authsafes->d.data;
+    if ((p12->authsafes->d.data = ASN1_OCTET_STRING_new()) == NULL)
+        goto err;
+    if (!PKCS12_pack_authsafes(p12, newsafes))
+        goto err;
+
+    if (!PKCS12_gen_mac(p12, newpass, -1, mac, &maclen))
+        goto err;
+    X509_SIG_getm(p12->mac->dinfo, NULL, &macoct);
+    if (!ASN1_OCTET_STRING_set(macoct, mac, maclen))
+        goto err;
+
+    rv = 1;
+
+err:
+    /* Restore old safe if necessary */
+    if (rv == 1) {
+        ASN1_OCTET_STRING_free(p12_data_tmp);
+    } else if (p12_data_tmp != NULL) {
+        ASN1_OCTET_STRING_free(p12->authsafes->d.data);
+        p12->authsafes->d.data = p12_data_tmp;
+    }
+    sk_PKCS12_SAFEBAG_pop_free(bags, PKCS12_SAFEBAG_free);
+    sk_PKCS7_pop_free(asafes, PKCS7_free);
+    sk_PKCS7_pop_free(newsafes, PKCS7_free);
+    return rv;
+}
+
+static int newpass_bags(STACK_OF(PKCS12_SAFEBAG) *bags, const char *oldpass,
+                        const char *newpass)
+{
+    int i;
+    for (i = 0; i < sk_PKCS12_SAFEBAG_num(bags); i++) {
+        if (!newpass_bag(sk_PKCS12_SAFEBAG_value(bags, i), oldpass, newpass))
+            return 0;
+    }
+    return 1;
+}
+
+/* Change password of safebag: only needs handle shrouded keybags */
+
+static int newpass_bag(PKCS12_SAFEBAG *bag, const char *oldpass,
+                       const char *newpass)
+{
+    PKCS8_PRIV_KEY_INFO *p8;
+    X509_SIG *p8new;
+    int p8_nid, p8_saltlen, p8_iter;
+    const X509_ALGOR *shalg;
+
+    if (PKCS12_SAFEBAG_get_nid(bag) != NID_pkcs8ShroudedKeyBag)
+        return 1;
+
+    if ((p8 = PKCS8_decrypt(bag->value.shkeybag, oldpass, -1)) == NULL)
+        return 0;
+    X509_SIG_get0(bag->value.shkeybag, &shalg, NULL);
+    if (!alg_get(shalg, &p8_nid, &p8_iter, &p8_saltlen))
+        return 0;
+    p8new = PKCS8_encrypt(p8_nid, NULL, newpass, -1, NULL, p8_saltlen,
+                          p8_iter, p8);
+    PKCS8_PRIV_KEY_INFO_free(p8);
+    if (p8new == NULL)
+        return 0;
+    X509_SIG_free(bag->value.shkeybag);
+    bag->value.shkeybag = p8new;
+    return 1;
+}
+
+static int alg_get(const X509_ALGOR *alg, int *pnid, int *piter,
+                   int *psaltlen)
+{
+    PBEPARAM *pbe;
+    pbe = ASN1_TYPE_unpack_sequence(ASN1_ITEM_rptr(PBEPARAM), alg->parameter);
+    if (!pbe)
+        return 0;
+    *pnid = OBJ_obj2nid(alg->algorithm);
+    *piter = ASN1_INTEGER_get(pbe->iter);
+    *psaltlen = pbe->salt->length;
+    PBEPARAM_free(pbe);
+    return 1;
+}
diff --git a/crypto/pkcs12/p12_p8d.c b/crypto/pkcs12/p12_p8d.c
new file mode 100644
index 000000000..d926a77df
--- /dev/null
+++ b/crypto/pkcs12/p12_p8d.c
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2001-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+
+PKCS8_PRIV_KEY_INFO *PKCS8_decrypt(const X509_SIG *p8, const char *pass,
+                                   int passlen)
+{
+    const X509_ALGOR *dalg;
+    const ASN1_OCTET_STRING *doct;
+    X509_SIG_get0(p8, &dalg, &doct);
+    return PKCS12_item_decrypt_d2i(dalg,
+                                   ASN1_ITEM_rptr(PKCS8_PRIV_KEY_INFO), pass,
+                                   passlen, doct, 1);
+}
diff --git a/crypto/pkcs12/p12_p8e.c b/crypto/pkcs12/p12_p8e.c
new file mode 100644
index 000000000..86a07e133
--- /dev/null
+++ b/crypto/pkcs12/p12_p8e.c
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2001-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include "internal/x509_int.h"
+
+X509_SIG *PKCS8_encrypt(int pbe_nid, const EVP_CIPHER *cipher,
+                        const char *pass, int passlen,
+                        unsigned char *salt, int saltlen, int iter,
+                        PKCS8_PRIV_KEY_INFO *p8inf)
+{
+    X509_SIG *p8 = NULL;
+    X509_ALGOR *pbe;
+
+    if (pbe_nid == -1)
+        pbe = PKCS5_pbe2_set(cipher, iter, salt, saltlen);
+    else if (EVP_PBE_find(EVP_PBE_TYPE_PRF, pbe_nid, NULL, NULL, 0))
+        pbe = PKCS5_pbe2_set_iv(cipher, iter, salt, saltlen, NULL, pbe_nid);
+    else {
+        ERR_clear_error();
+        pbe = PKCS5_pbe_set(pbe_nid, iter, salt, saltlen);
+    }
+    if (!pbe) {
+        PKCS12err(PKCS12_F_PKCS8_ENCRYPT, ERR_R_ASN1_LIB);
+        return NULL;
+    }
+    p8 = PKCS8_set0_pbe(pass, passlen, p8inf, pbe);
+    if (p8 == NULL) {
+        X509_ALGOR_free(pbe);
+        return NULL;
+    }
+
+    return p8;
+}
+
+X509_SIG *PKCS8_set0_pbe(const char *pass, int passlen,
+                         PKCS8_PRIV_KEY_INFO *p8inf, X509_ALGOR *pbe)
+{
+    X509_SIG *p8;
+    ASN1_OCTET_STRING *enckey;
+
+    enckey =
+        PKCS12_item_i2d_encrypt(pbe, ASN1_ITEM_rptr(PKCS8_PRIV_KEY_INFO),
+                                pass, passlen, p8inf, 1);
+    if (!enckey) {
+        PKCS12err(PKCS12_F_PKCS8_SET0_PBE, PKCS12_R_ENCRYPT_ERROR);
+        return NULL;
+    }
+
+    p8 = OPENSSL_zalloc(sizeof(*p8));
+
+    if (p8 == NULL) {
+        PKCS12err(PKCS12_F_PKCS8_SET0_PBE, ERR_R_MALLOC_FAILURE);
+        ASN1_OCTET_STRING_free(enckey);
+        return NULL;
+    }
+    p8->algor = pbe;
+    p8->digest = enckey;
+
+    return p8;
+}
diff --git a/crypto/pkcs12/p12_sbag.c b/crypto/pkcs12/p12_sbag.c
new file mode 100644
index 000000000..4a3d25993
--- /dev/null
+++ b/crypto/pkcs12/p12_sbag.c
@@ -0,0 +1,170 @@
+/*
+ * Copyright 1999-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include "p12_lcl.h"
+
+#if OPENSSL_API_COMPAT < 0x10100000L
+ASN1_TYPE *PKCS12_get_attr(const PKCS12_SAFEBAG *bag, int attr_nid)
+{
+    return PKCS12_get_attr_gen(bag->attrib, attr_nid);
+}
+#endif
+
+const ASN1_TYPE *PKCS12_SAFEBAG_get0_attr(const PKCS12_SAFEBAG *bag,
+                                          int attr_nid)
+{
+    return PKCS12_get_attr_gen(bag->attrib, attr_nid);
+}
+
+ASN1_TYPE *PKCS8_get_attr(PKCS8_PRIV_KEY_INFO *p8, int attr_nid)
+{
+    return PKCS12_get_attr_gen(PKCS8_pkey_get0_attrs(p8), attr_nid);
+}
+
+const PKCS8_PRIV_KEY_INFO *PKCS12_SAFEBAG_get0_p8inf(const PKCS12_SAFEBAG *bag)
+{
+    if (PKCS12_SAFEBAG_get_nid(bag) != NID_keyBag)
+        return NULL;
+    return bag->value.keybag;
+}
+
+const X509_SIG *PKCS12_SAFEBAG_get0_pkcs8(const PKCS12_SAFEBAG *bag)
+{
+    if (OBJ_obj2nid(bag->type) != NID_pkcs8ShroudedKeyBag)
+        return NULL;
+    return bag->value.shkeybag;
+}
+
+const STACK_OF(PKCS12_SAFEBAG) *
+PKCS12_SAFEBAG_get0_safes(const PKCS12_SAFEBAG *bag)
+{
+    if (OBJ_obj2nid(bag->type) != NID_safeContentsBag)
+        return NULL;
+    return bag->value.safes;
+}
+
+const ASN1_OBJECT *PKCS12_SAFEBAG_get0_type(const PKCS12_SAFEBAG *bag)
+{
+    return bag->type;
+}
+
+int PKCS12_SAFEBAG_get_nid(const PKCS12_SAFEBAG *bag)
+{
+    return OBJ_obj2nid(bag->type);
+}
+
+int PKCS12_SAFEBAG_get_bag_nid(const PKCS12_SAFEBAG *bag)
+{
+    int btype = PKCS12_SAFEBAG_get_nid(bag);
+
+    if (btype != NID_certBag && btype != NID_crlBag && btype != NID_secretBag)
+        return -1;
+    return OBJ_obj2nid(bag->value.bag->type);
+}
+
+X509 *PKCS12_SAFEBAG_get1_cert(const PKCS12_SAFEBAG *bag)
+{
+    if (PKCS12_SAFEBAG_get_nid(bag) != NID_certBag)
+        return NULL;
+    if (OBJ_obj2nid(bag->value.bag->type) != NID_x509Certificate)
+        return NULL;
+    return ASN1_item_unpack(bag->value.bag->value.octet,
+                            ASN1_ITEM_rptr(X509));
+}
+
+X509_CRL *PKCS12_SAFEBAG_get1_crl(const PKCS12_SAFEBAG *bag)
+{
+    if (PKCS12_SAFEBAG_get_nid(bag) != NID_crlBag)
+        return NULL;
+    if (OBJ_obj2nid(bag->value.bag->type) != NID_x509Crl)
+        return NULL;
+    return ASN1_item_unpack(bag->value.bag->value.octet,
+                            ASN1_ITEM_rptr(X509_CRL));
+}
+
+PKCS12_SAFEBAG *PKCS12_SAFEBAG_create_cert(X509 *x509)
+{
+    return PKCS12_item_pack_safebag(x509, ASN1_ITEM_rptr(X509),
+                                    NID_x509Certificate, NID_certBag);
+}
+
+PKCS12_SAFEBAG *PKCS12_SAFEBAG_create_crl(X509_CRL *crl)
+{
+    return PKCS12_item_pack_safebag(crl, ASN1_ITEM_rptr(X509_CRL),
+                                    NID_x509Crl, NID_crlBag);
+}
+
+/* Turn PKCS8 object into a keybag */
+
+PKCS12_SAFEBAG *PKCS12_SAFEBAG_create0_p8inf(PKCS8_PRIV_KEY_INFO *p8)
+{
+    PKCS12_SAFEBAG *bag = PKCS12_SAFEBAG_new();
+
+    if (bag == NULL) {
+        PKCS12err(PKCS12_F_PKCS12_SAFEBAG_CREATE0_P8INF, ERR_R_MALLOC_FAILURE);
+        return NULL;
+    }
+    bag->type = OBJ_nid2obj(NID_keyBag);
+    bag->value.keybag = p8;
+    return bag;
+}
+
+/* Turn PKCS8 object into a shrouded keybag */
+
+PKCS12_SAFEBAG *PKCS12_SAFEBAG_create0_pkcs8(X509_SIG *p8)
+{
+    PKCS12_SAFEBAG *bag = PKCS12_SAFEBAG_new();
+
+    /* Set up the safe bag */
+    if (bag == NULL) {
+        PKCS12err(PKCS12_F_PKCS12_SAFEBAG_CREATE0_PKCS8, ERR_R_MALLOC_FAILURE);
+        return NULL;
+    }
+    bag->type = OBJ_nid2obj(NID_pkcs8ShroudedKeyBag);
+    bag->value.shkeybag = p8;
+    return bag;
+}
+
+PKCS12_SAFEBAG *PKCS12_SAFEBAG_create_pkcs8_encrypt(int pbe_nid,
+                                                    const char *pass,
+                                                    int passlen,
+                                                    unsigned char *salt,
+                                                    int saltlen, int iter,
+                                                    PKCS8_PRIV_KEY_INFO *p8inf)
+{
+    PKCS12_SAFEBAG *bag;
+    const EVP_CIPHER *pbe_ciph;
+    X509_SIG *p8;
+
+    pbe_ciph = EVP_get_cipherbynid(pbe_nid);
+
+    if (pbe_ciph)
+        pbe_nid = -1;
+
+    p8 = PKCS8_encrypt(pbe_nid, pbe_ciph, pass, passlen, salt, saltlen, iter,
+                       p8inf);
+
+    if (p8 == NULL) {
+        PKCS12err(PKCS12_F_PKCS12_SAFEBAG_CREATE_PKCS8_ENCRYPT, ERR_R_MALLOC_FAILURE);
+        return NULL;
+    }
+
+    bag = PKCS12_SAFEBAG_create0_pkcs8(p8);
+
+    if (bag == NULL) {
+        PKCS12err(PKCS12_F_PKCS12_SAFEBAG_CREATE_PKCS8_ENCRYPT, ERR_R_MALLOC_FAILURE);
+        X509_SIG_free(p8);
+        return NULL;
+    }
+
+    return bag;
+}
diff --git a/crypto/pkcs12/p12_utl.c b/crypto/pkcs12/p12_utl.c
new file mode 100644
index 000000000..07014786f
--- /dev/null
+++ b/crypto/pkcs12/p12_utl.c
@@ -0,0 +1,237 @@
+/*
+ * Copyright 1999-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+
+/* Cheap and nasty Unicode stuff */
+
+unsigned char *OPENSSL_asc2uni(const char *asc, int asclen,
+                               unsigned char **uni, int *unilen)
+{
+    int ulen, i;
+    unsigned char *unitmp;
+
+    if (asclen == -1)
+        asclen = strlen(asc);
+    ulen = asclen * 2 + 2;
+    if ((unitmp = OPENSSL_malloc(ulen)) == NULL)
+        return NULL;
+    for (i = 0; i < ulen - 2; i += 2) {
+        unitmp[i] = 0;
+        unitmp[i + 1] = asc[i >> 1];
+    }
+    /* Make result double null terminated */
+    unitmp[ulen - 2] = 0;
+    unitmp[ulen - 1] = 0;
+    if (unilen)
+        *unilen = ulen;
+    if (uni)
+        *uni = unitmp;
+    return unitmp;
+}
+
+char *OPENSSL_uni2asc(const unsigned char *uni, int unilen)
+{
+    int asclen, i;
+    char *asctmp;
+    /* string must contain an even number of bytes */
+    if (unilen & 1)
+        return NULL;
+    asclen = unilen / 2;
+    /* If no terminating zero allow for one */
+    if (!unilen || uni[unilen - 1])
+        asclen++;
+    uni++;
+    if ((asctmp = OPENSSL_malloc(asclen)) == NULL)
+        return NULL;
+    for (i = 0; i < unilen; i += 2)
+        asctmp[i >> 1] = uni[i];
+    asctmp[asclen - 1] = 0;
+    return asctmp;
+}
+
+/*
+ * OPENSSL_{utf82uni|uni2utf8} perform conversion between UTF-8 and
+ * PKCS#12 BMPString format, which is specified as big-endian UTF-16.
+ * One should keep in mind that even though BMPString is passed as
+ * unsigned char *, it's not the kind of string you can exercise e.g.
+ * strlen on. Caller also has to keep in mind that its length is
+ * expressed not in number of UTF-16 characters, but in number of
+ * bytes the string occupies, and treat it, the length, accordingly.
+ */
+unsigned char *OPENSSL_utf82uni(const char *asc, int asclen,
+                                unsigned char **uni, int *unilen)
+{
+    int ulen, i, j;
+    unsigned char *unitmp, *ret;
+    unsigned long utf32chr = 0;
+
+    if (asclen == -1)
+        asclen = strlen(asc);
+
+    for (ulen = 0, i = 0; i < asclen; i += j) {
+        j = UTF8_getc((const unsigned char *)asc+i, asclen-i, &utf32chr);
+
+        /*
+         * Following condition is somewhat opportunistic is sense that
+         * decoding failure is used as *indirect* indication that input
+         * string might in fact be extended ASCII/ANSI/ISO-8859-X. The
+         * fallback is taken in hope that it would allow to process
+         * files created with previous OpenSSL version, which used the
+         * naive OPENSSL_asc2uni all along. It might be worth noting
+         * that probability of false positive depends on language. In
+         * cases covered by ISO Latin 1 probability is very low, because
+         * any printable non-ASCII alphabet letter followed by another
+         * or any ASCII character will trigger failure and fallback.
+         * In other cases situation can be intensified by the fact that
+         * English letters are not part of alternative keyboard layout,
+         * but even then there should be plenty of pairs that trigger
+         * decoding failure...
+         */
+        if (j < 0)
+	    return OPENSSL_asc2uni(asc, asclen, uni, unilen);
+
+        if (utf32chr > 0x10FFFF)        /* UTF-16 cap */
+	    return NULL;
+
+        if (utf32chr >= 0x10000)        /* pair of UTF-16 characters */
+            ulen += 2*2;
+        else                            /* or just one */
+            ulen += 2;
+    }
+
+    ulen += 2;  /* for trailing UTF16 zero */
+
+    if ((ret = OPENSSL_malloc(ulen)) == NULL)
+        return NULL;
+
+    /* re-run the loop writing down UTF-16 characters in big-endian order */
+    for (unitmp = ret, i = 0; i < asclen; i += j) {
+        j = UTF8_getc((const unsigned char *)asc+i, asclen-i, &utf32chr);
+        if (utf32chr >= 0x10000) {      /* pair if UTF-16 characters */
+            unsigned int hi, lo;
+
+            utf32chr -= 0x10000;
+            hi = 0xD800 + (utf32chr>>10);
+            lo = 0xDC00 + (utf32chr&0x3ff);
+            *unitmp++ = (unsigned char)(hi>>8);
+            *unitmp++ = (unsigned char)(hi);
+            *unitmp++ = (unsigned char)(lo>>8);
+            *unitmp++ = (unsigned char)(lo);
+        } else {                        /* or just one */
+            *unitmp++ = (unsigned char)(utf32chr>>8);
+            *unitmp++ = (unsigned char)(utf32chr);
+        }
+    }
+    /* Make result double null terminated */
+    *unitmp++ = 0;
+    *unitmp++ = 0;
+    if (unilen)
+        *unilen = ulen;
+    if (uni)
+        *uni = ret;
+    return ret;
+}
+
+static int bmp_to_utf8(char *str, const unsigned char *utf16, int len)
+{
+    unsigned long utf32chr;
+
+    if (len == 0) return 0;
+
+    if (len < 2) return -1;
+
+    /* pull UTF-16 character in big-endian order */
+    utf32chr = (utf16[0]<<8) | utf16[1];
+
+    if (utf32chr >= 0xD800 && utf32chr < 0xE000) {   /* two chars */
+        unsigned int lo;
+
+        if (len < 4) return -1;
+
+        utf32chr -= 0xD800;
+        utf32chr <<= 10;
+        lo = (utf16[2]<<8) | utf16[3];
+        if (lo < 0xDC00 || lo >= 0xE000) return -1;
+        utf32chr |= lo-0xDC00;
+        utf32chr += 0x10000;
+    }
+
+    return UTF8_putc((unsigned char *)str, len > 4 ? 4 : len, utf32chr);
+}
+
+char *OPENSSL_uni2utf8(const unsigned char *uni, int unilen)
+{
+    int asclen, i, j;
+    char *asctmp;
+
+    /* string must contain an even number of bytes */
+    if (unilen & 1)
+        return NULL;
+
+    for (asclen = 0, i = 0; i < unilen; ) {
+        j = bmp_to_utf8(NULL, uni+i, unilen-i);
+        /*
+         * falling back to OPENSSL_uni2asc makes lesser sense [than
+         * falling back to OPENSSL_asc2uni in OPENSSL_utf82uni above],
+         * it's done rather to maintain symmetry...
+         */
+        if (j < 0) return OPENSSL_uni2asc(uni, unilen);
+        if (j == 4) i += 4;
+        else        i += 2;
+        asclen += j;
+    }
+
+    /* If no terminating zero allow for one */
+    if (!unilen || (uni[unilen-2]||uni[unilen - 1]))
+        asclen++;
+
+    if ((asctmp = OPENSSL_malloc(asclen)) == NULL)
+        return NULL;
+
+    /* re-run the loop emitting UTF-8 string */
+    for (asclen = 0, i = 0; i < unilen; ) {
+        j = bmp_to_utf8(asctmp+asclen, uni+i, unilen-i);
+        if (j == 4) i += 4;
+        else        i += 2;
+        asclen += j;
+    }
+
+    /* If no terminating zero write one */
+    if (!unilen || (uni[unilen-2]||uni[unilen - 1]))
+        asctmp[asclen] = '\0';
+
+    return asctmp;
+}
+
+int i2d_PKCS12_bio(BIO *bp, PKCS12 *p12)
+{
+    return ASN1_item_i2d_bio(ASN1_ITEM_rptr(PKCS12), bp, p12);
+}
+
+#ifndef OPENSSL_NO_STDIO
+int i2d_PKCS12_fp(FILE *fp, PKCS12 *p12)
+{
+    return ASN1_item_i2d_fp(ASN1_ITEM_rptr(PKCS12), fp, p12);
+}
+#endif
+
+PKCS12 *d2i_PKCS12_bio(BIO *bp, PKCS12 **p12)
+{
+    return ASN1_item_d2i_bio(ASN1_ITEM_rptr(PKCS12), bp, p12);
+}
+
+#ifndef OPENSSL_NO_STDIO
+PKCS12 *d2i_PKCS12_fp(FILE *fp, PKCS12 **p12)
+{
+    return ASN1_item_d2i_fp(ASN1_ITEM_rptr(PKCS12), fp, p12);
+}
+#endif
diff --git a/crypto/pkcs12/pk12err.c b/crypto/pkcs12/pk12err.c
new file mode 100644
index 000000000..ec6b4d058
--- /dev/null
+++ b/crypto/pkcs12/pk12err.c
@@ -0,0 +1,97 @@
+/*
+ * Generated by util/mkerr.pl DO NOT EDIT
+ * Copyright 1995-2017 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include 
+
+/* BEGIN ERROR CODES */
+#ifndef OPENSSL_NO_ERR
+
+# define ERR_FUNC(func) ERR_PACK(ERR_LIB_PKCS12,func,0)
+# define ERR_REASON(reason) ERR_PACK(ERR_LIB_PKCS12,0,reason)
+
+static ERR_STRING_DATA PKCS12_str_functs[] = {
+    {ERR_FUNC(PKCS12_F_PKCS12_CREATE), "PKCS12_create"},
+    {ERR_FUNC(PKCS12_F_PKCS12_GEN_MAC), "PKCS12_gen_mac"},
+    {ERR_FUNC(PKCS12_F_PKCS12_INIT), "PKCS12_init"},
+    {ERR_FUNC(PKCS12_F_PKCS12_ITEM_DECRYPT_D2I), "PKCS12_item_decrypt_d2i"},
+    {ERR_FUNC(PKCS12_F_PKCS12_ITEM_I2D_ENCRYPT), "PKCS12_item_i2d_encrypt"},
+    {ERR_FUNC(PKCS12_F_PKCS12_ITEM_PACK_SAFEBAG), "PKCS12_item_pack_safebag"},
+    {ERR_FUNC(PKCS12_F_PKCS12_KEY_GEN_ASC), "PKCS12_key_gen_asc"},
+    {ERR_FUNC(PKCS12_F_PKCS12_KEY_GEN_UNI), "PKCS12_key_gen_uni"},
+    {ERR_FUNC(PKCS12_F_PKCS12_KEY_GEN_UTF8), "PKCS12_key_gen_utf8"},
+    {ERR_FUNC(PKCS12_F_PKCS12_NEWPASS), "PKCS12_newpass"},
+    {ERR_FUNC(PKCS12_F_PKCS12_PACK_P7DATA), "PKCS12_pack_p7data"},
+    {ERR_FUNC(PKCS12_F_PKCS12_PACK_P7ENCDATA), "PKCS12_pack_p7encdata"},
+    {ERR_FUNC(PKCS12_F_PKCS12_PARSE), "PKCS12_parse"},
+    {ERR_FUNC(PKCS12_F_PKCS12_PBE_CRYPT), "PKCS12_pbe_crypt"},
+    {ERR_FUNC(PKCS12_F_PKCS12_PBE_KEYIVGEN), "PKCS12_PBE_keyivgen"},
+    {ERR_FUNC(PKCS12_F_PKCS12_SAFEBAG_CREATE0_P8INF),
+     "PKCS12_SAFEBAG_create0_p8inf"},
+    {ERR_FUNC(PKCS12_F_PKCS12_SAFEBAG_CREATE0_PKCS8),
+     "PKCS12_SAFEBAG_create0_pkcs8"},
+    {ERR_FUNC(PKCS12_F_PKCS12_SAFEBAG_CREATE_PKCS8_ENCRYPT),
+     "PKCS12_SAFEBAG_create_pkcs8_encrypt"},
+    {ERR_FUNC(PKCS12_F_PKCS12_SETUP_MAC), "PKCS12_setup_mac"},
+    {ERR_FUNC(PKCS12_F_PKCS12_SET_MAC), "PKCS12_set_mac"},
+    {ERR_FUNC(PKCS12_F_PKCS12_UNPACK_AUTHSAFES), "PKCS12_unpack_authsafes"},
+    {ERR_FUNC(PKCS12_F_PKCS12_UNPACK_P7DATA), "PKCS12_unpack_p7data"},
+    {ERR_FUNC(PKCS12_F_PKCS12_VERIFY_MAC), "PKCS12_verify_mac"},
+    {ERR_FUNC(PKCS12_F_PKCS8_ENCRYPT), "PKCS8_encrypt"},
+    {ERR_FUNC(PKCS12_F_PKCS8_SET0_PBE), "PKCS8_set0_pbe"},
+    {0, NULL}
+};
+
+static ERR_STRING_DATA PKCS12_str_reasons[] = {
+    {ERR_REASON(PKCS12_R_CANT_PACK_STRUCTURE), "cant pack structure"},
+    {ERR_REASON(PKCS12_R_CONTENT_TYPE_NOT_DATA), "content type not data"},
+    {ERR_REASON(PKCS12_R_DECODE_ERROR), "decode error"},
+    {ERR_REASON(PKCS12_R_ENCODE_ERROR), "encode error"},
+    {ERR_REASON(PKCS12_R_ENCRYPT_ERROR), "encrypt error"},
+    {ERR_REASON(PKCS12_R_ERROR_SETTING_ENCRYPTED_DATA_TYPE),
+     "error setting encrypted data type"},
+    {ERR_REASON(PKCS12_R_INVALID_NULL_ARGUMENT), "invalid null argument"},
+    {ERR_REASON(PKCS12_R_INVALID_NULL_PKCS12_POINTER),
+     "invalid null pkcs12 pointer"},
+    {ERR_REASON(PKCS12_R_IV_GEN_ERROR), "iv gen error"},
+    {ERR_REASON(PKCS12_R_KEY_GEN_ERROR), "key gen error"},
+    {ERR_REASON(PKCS12_R_MAC_ABSENT), "mac absent"},
+    {ERR_REASON(PKCS12_R_MAC_GENERATION_ERROR), "mac generation error"},
+    {ERR_REASON(PKCS12_R_MAC_SETUP_ERROR), "mac setup error"},
+    {ERR_REASON(PKCS12_R_MAC_STRING_SET_ERROR), "mac string set error"},
+    {ERR_REASON(PKCS12_R_MAC_VERIFY_FAILURE), "mac verify failure"},
+    {ERR_REASON(PKCS12_R_NO_AVAIABLE_CIPHER), "no avaiable cipher"},
+    {ERR_REASON(PKCS12_R_NO_AVAIABLE_DIGEST), "no avaiable digest"},
+    {ERR_REASON(PKCS12_R_PARSE_ERROR), "parse error"},
+    {ERR_REASON(PKCS12_R_PKCS12_ALGOR_CIPHERINIT_ERROR),
+     "pkcs12 algor cipherinit error"},
+    {ERR_REASON(PKCS12_R_PKCS12_CIPHERFINAL_ERROR),
+     "pkcs12 cipherfinal error"},
+    {ERR_REASON(PKCS12_R_PKCS12_PBE_CRYPT_ERROR), "pkcs12 pbe crypt error"},
+    {ERR_REASON(PKCS12_R_UNKNOWN_DIGEST_ALGORITHM),
+     "unknown digest algorithm"},
+    {ERR_REASON(PKCS12_R_UNSUPPORTED_PKCS12_MODE), "unsupported pkcs12 mode"},
+    {0, NULL}
+};
+
+#endif
+
+int ERR_load_PKCS12_strings(void)
+{
+#ifndef OPENSSL_NO_ERR
+
+    if (ERR_func_error_string(PKCS12_str_functs[0].error) == NULL) {
+        ERR_load_strings(0, PKCS12_str_functs);
+        ERR_load_strings(0, PKCS12_str_reasons);
+    }
+#endif
+    return 1;
+}
diff --git a/crypto/pkcs7/bio_pk7.c b/crypto/pkcs7/bio_pk7.c
new file mode 100644
index 000000000..29feaa354
--- /dev/null
+++ b/crypto/pkcs7/bio_pk7.c
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2006-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include 
+
+#if !defined(OPENSSL_SYS_VXWORKS)
+# include 
+#endif
+#include 
+
+/* Streaming encode support for PKCS#7 */
+
+BIO *BIO_new_PKCS7(BIO *out, PKCS7 *p7)
+{
+    return BIO_new_NDEF(out, (ASN1_VALUE *)p7, ASN1_ITEM_rptr(PKCS7));
+}
diff --git a/crypto/pkcs7/build.info b/crypto/pkcs7/build.info
new file mode 100644
index 000000000..2029d538f
--- /dev/null
+++ b/crypto/pkcs7/build.info
@@ -0,0 +1,4 @@
+LIBS=../../libcrypto
+SOURCE[../../libcrypto]=\
+        pk7_asn1.c pk7_lib.c pkcs7err.c pk7_doit.c pk7_smime.c pk7_attr.c \
+        pk7_mime.c bio_pk7.c
diff --git a/crypto/pkcs7/pk7_asn1.c b/crypto/pkcs7/pk7_asn1.c
new file mode 100644
index 000000000..315e1b813
--- /dev/null
+++ b/crypto/pkcs7/pk7_asn1.c
@@ -0,0 +1,201 @@
+/*
+ * Copyright 2000-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+#include 
+
+/* PKCS#7 ASN1 module */
+
+/* This is the ANY DEFINED BY table for the top level PKCS#7 structure */
+
+ASN1_ADB_TEMPLATE(p7default) = ASN1_EXP_OPT(PKCS7, d.other, ASN1_ANY, 0);
+
+ASN1_ADB(PKCS7) = {
+        ADB_ENTRY(NID_pkcs7_data, ASN1_NDEF_EXP_OPT(PKCS7, d.data, ASN1_OCTET_STRING_NDEF, 0)),
+        ADB_ENTRY(NID_pkcs7_signed, ASN1_NDEF_EXP_OPT(PKCS7, d.sign, PKCS7_SIGNED, 0)),
+        ADB_ENTRY(NID_pkcs7_enveloped, ASN1_NDEF_EXP_OPT(PKCS7, d.enveloped, PKCS7_ENVELOPE, 0)),
+        ADB_ENTRY(NID_pkcs7_signedAndEnveloped, ASN1_NDEF_EXP_OPT(PKCS7, d.signed_and_enveloped, PKCS7_SIGN_ENVELOPE, 0)),
+        ADB_ENTRY(NID_pkcs7_digest, ASN1_NDEF_EXP_OPT(PKCS7, d.digest, PKCS7_DIGEST, 0)),
+        ADB_ENTRY(NID_pkcs7_encrypted, ASN1_NDEF_EXP_OPT(PKCS7, d.encrypted, PKCS7_ENCRYPT, 0))
+} ASN1_ADB_END(PKCS7, 0, type, 0, &p7default_tt, NULL);
+
+/* PKCS#7 streaming support */
+static int pk7_cb(int operation, ASN1_VALUE **pval, const ASN1_ITEM *it,
+                  void *exarg)
+{
+    ASN1_STREAM_ARG *sarg = exarg;
+    PKCS7 **pp7 = (PKCS7 **)pval;
+
+    switch (operation) {
+
+    case ASN1_OP_STREAM_PRE:
+        if (PKCS7_stream(&sarg->boundary, *pp7) <= 0)
+            return 0;
+    case ASN1_OP_DETACHED_PRE:
+        sarg->ndef_bio = PKCS7_dataInit(*pp7, sarg->out);
+        if (!sarg->ndef_bio)
+            return 0;
+        break;
+
+    case ASN1_OP_STREAM_POST:
+    case ASN1_OP_DETACHED_POST:
+        if (PKCS7_dataFinal(*pp7, sarg->ndef_bio) <= 0)
+            return 0;
+        break;
+
+    }
+    return 1;
+}
+
+ASN1_NDEF_SEQUENCE_cb(PKCS7, pk7_cb) = {
+        ASN1_SIMPLE(PKCS7, type, ASN1_OBJECT),
+        ASN1_ADB_OBJECT(PKCS7)
+}ASN1_NDEF_SEQUENCE_END_cb(PKCS7, PKCS7)
+
+IMPLEMENT_ASN1_FUNCTIONS(PKCS7)
+
+IMPLEMENT_ASN1_NDEF_FUNCTION(PKCS7)
+
+IMPLEMENT_ASN1_DUP_FUNCTION(PKCS7)
+
+ASN1_NDEF_SEQUENCE(PKCS7_SIGNED) = {
+        ASN1_SIMPLE(PKCS7_SIGNED, version, ASN1_INTEGER),
+        ASN1_SET_OF(PKCS7_SIGNED, md_algs, X509_ALGOR),
+        ASN1_SIMPLE(PKCS7_SIGNED, contents, PKCS7),
+        ASN1_IMP_SEQUENCE_OF_OPT(PKCS7_SIGNED, cert, X509, 0),
+        ASN1_IMP_SET_OF_OPT(PKCS7_SIGNED, crl, X509_CRL, 1),
+        ASN1_SET_OF(PKCS7_SIGNED, signer_info, PKCS7_SIGNER_INFO)
+} ASN1_NDEF_SEQUENCE_END(PKCS7_SIGNED)
+
+IMPLEMENT_ASN1_FUNCTIONS(PKCS7_SIGNED)
+
+/* Minor tweak to operation: free up EVP_PKEY */
+static int si_cb(int operation, ASN1_VALUE **pval, const ASN1_ITEM *it,
+                 void *exarg)
+{
+    if (operation == ASN1_OP_FREE_POST) {
+        PKCS7_SIGNER_INFO *si = (PKCS7_SIGNER_INFO *)*pval;
+        EVP_PKEY_free(si->pkey);
+    }
+    return 1;
+}
+
+ASN1_SEQUENCE_cb(PKCS7_SIGNER_INFO, si_cb) = {
+        ASN1_SIMPLE(PKCS7_SIGNER_INFO, version, ASN1_INTEGER),
+        ASN1_SIMPLE(PKCS7_SIGNER_INFO, issuer_and_serial, PKCS7_ISSUER_AND_SERIAL),
+        ASN1_SIMPLE(PKCS7_SIGNER_INFO, digest_alg, X509_ALGOR),
+        /* NB this should be a SET OF but we use a SEQUENCE OF so the
+         * original order * is retained when the structure is reencoded.
+         * Since the attributes are implicitly tagged this will not affect
+         * the encoding.
+         */
+        ASN1_IMP_SEQUENCE_OF_OPT(PKCS7_SIGNER_INFO, auth_attr, X509_ATTRIBUTE, 0),
+        ASN1_SIMPLE(PKCS7_SIGNER_INFO, digest_enc_alg, X509_ALGOR),
+        ASN1_SIMPLE(PKCS7_SIGNER_INFO, enc_digest, ASN1_OCTET_STRING),
+        ASN1_IMP_SET_OF_OPT(PKCS7_SIGNER_INFO, unauth_attr, X509_ATTRIBUTE, 1)
+} ASN1_SEQUENCE_END_cb(PKCS7_SIGNER_INFO, PKCS7_SIGNER_INFO)
+
+IMPLEMENT_ASN1_FUNCTIONS(PKCS7_SIGNER_INFO)
+
+ASN1_SEQUENCE(PKCS7_ISSUER_AND_SERIAL) = {
+        ASN1_SIMPLE(PKCS7_ISSUER_AND_SERIAL, issuer, X509_NAME),
+        ASN1_SIMPLE(PKCS7_ISSUER_AND_SERIAL, serial, ASN1_INTEGER)
+} ASN1_SEQUENCE_END(PKCS7_ISSUER_AND_SERIAL)
+
+IMPLEMENT_ASN1_FUNCTIONS(PKCS7_ISSUER_AND_SERIAL)
+
+ASN1_NDEF_SEQUENCE(PKCS7_ENVELOPE) = {
+        ASN1_SIMPLE(PKCS7_ENVELOPE, version, ASN1_INTEGER),
+        ASN1_SET_OF(PKCS7_ENVELOPE, recipientinfo, PKCS7_RECIP_INFO),
+        ASN1_SIMPLE(PKCS7_ENVELOPE, enc_data, PKCS7_ENC_CONTENT)
+} ASN1_NDEF_SEQUENCE_END(PKCS7_ENVELOPE)
+
+IMPLEMENT_ASN1_FUNCTIONS(PKCS7_ENVELOPE)
+
+/* Minor tweak to operation: free up X509 */
+static int ri_cb(int operation, ASN1_VALUE **pval, const ASN1_ITEM *it,
+                 void *exarg)
+{
+    if (operation == ASN1_OP_FREE_POST) {
+        PKCS7_RECIP_INFO *ri = (PKCS7_RECIP_INFO *)*pval;
+        X509_free(ri->cert);
+    }
+    return 1;
+}
+
+ASN1_SEQUENCE_cb(PKCS7_RECIP_INFO, ri_cb) = {
+        ASN1_SIMPLE(PKCS7_RECIP_INFO, version, ASN1_INTEGER),
+        ASN1_SIMPLE(PKCS7_RECIP_INFO, issuer_and_serial, PKCS7_ISSUER_AND_SERIAL),
+        ASN1_SIMPLE(PKCS7_RECIP_INFO, key_enc_algor, X509_ALGOR),
+        ASN1_SIMPLE(PKCS7_RECIP_INFO, enc_key, ASN1_OCTET_STRING)
+} ASN1_SEQUENCE_END_cb(PKCS7_RECIP_INFO, PKCS7_RECIP_INFO)
+
+IMPLEMENT_ASN1_FUNCTIONS(PKCS7_RECIP_INFO)
+
+ASN1_NDEF_SEQUENCE(PKCS7_ENC_CONTENT) = {
+        ASN1_SIMPLE(PKCS7_ENC_CONTENT, content_type, ASN1_OBJECT),
+        ASN1_SIMPLE(PKCS7_ENC_CONTENT, algorithm, X509_ALGOR),
+        ASN1_IMP_OPT(PKCS7_ENC_CONTENT, enc_data, ASN1_OCTET_STRING_NDEF, 0)
+} ASN1_NDEF_SEQUENCE_END(PKCS7_ENC_CONTENT)
+
+IMPLEMENT_ASN1_FUNCTIONS(PKCS7_ENC_CONTENT)
+
+ASN1_NDEF_SEQUENCE(PKCS7_SIGN_ENVELOPE) = {
+        ASN1_SIMPLE(PKCS7_SIGN_ENVELOPE, version, ASN1_INTEGER),
+        ASN1_SET_OF(PKCS7_SIGN_ENVELOPE, recipientinfo, PKCS7_RECIP_INFO),
+        ASN1_SET_OF(PKCS7_SIGN_ENVELOPE, md_algs, X509_ALGOR),
+        ASN1_SIMPLE(PKCS7_SIGN_ENVELOPE, enc_data, PKCS7_ENC_CONTENT),
+        ASN1_IMP_SET_OF_OPT(PKCS7_SIGN_ENVELOPE, cert, X509, 0),
+        ASN1_IMP_SET_OF_OPT(PKCS7_SIGN_ENVELOPE, crl, X509_CRL, 1),
+        ASN1_SET_OF(PKCS7_SIGN_ENVELOPE, signer_info, PKCS7_SIGNER_INFO)
+} ASN1_NDEF_SEQUENCE_END(PKCS7_SIGN_ENVELOPE)
+
+IMPLEMENT_ASN1_FUNCTIONS(PKCS7_SIGN_ENVELOPE)
+
+ASN1_NDEF_SEQUENCE(PKCS7_ENCRYPT) = {
+        ASN1_SIMPLE(PKCS7_ENCRYPT, version, ASN1_INTEGER),
+        ASN1_SIMPLE(PKCS7_ENCRYPT, enc_data, PKCS7_ENC_CONTENT)
+} ASN1_NDEF_SEQUENCE_END(PKCS7_ENCRYPT)
+
+IMPLEMENT_ASN1_FUNCTIONS(PKCS7_ENCRYPT)
+
+ASN1_NDEF_SEQUENCE(PKCS7_DIGEST) = {
+        ASN1_SIMPLE(PKCS7_DIGEST, version, ASN1_INTEGER),
+        ASN1_SIMPLE(PKCS7_DIGEST, md, X509_ALGOR),
+        ASN1_SIMPLE(PKCS7_DIGEST, contents, PKCS7),
+        ASN1_SIMPLE(PKCS7_DIGEST, digest, ASN1_OCTET_STRING)
+} ASN1_NDEF_SEQUENCE_END(PKCS7_DIGEST)
+
+IMPLEMENT_ASN1_FUNCTIONS(PKCS7_DIGEST)
+
+/* Specials for authenticated attributes */
+
+/*
+ * When signing attributes we want to reorder them to match the sorted
+ * encoding.
+ */
+
+ASN1_ITEM_TEMPLATE(PKCS7_ATTR_SIGN) =
+        ASN1_EX_TEMPLATE_TYPE(ASN1_TFLG_SET_ORDER, 0, PKCS7_ATTRIBUTES, X509_ATTRIBUTE)
+ASN1_ITEM_TEMPLATE_END(PKCS7_ATTR_SIGN)
+
+/*
+ * When verifying attributes we need to use the received order. So we use
+ * SEQUENCE OF and tag it to SET OF
+ */
+
+ASN1_ITEM_TEMPLATE(PKCS7_ATTR_VERIFY) =
+        ASN1_EX_TEMPLATE_TYPE(ASN1_TFLG_SEQUENCE_OF | ASN1_TFLG_IMPTAG | ASN1_TFLG_UNIVERSAL,
+                                V_ASN1_SET, PKCS7_ATTRIBUTES, X509_ATTRIBUTE)
+ASN1_ITEM_TEMPLATE_END(PKCS7_ATTR_VERIFY)
+
+IMPLEMENT_ASN1_PRINT_FUNCTION(PKCS7)
diff --git a/crypto/pkcs7/pk7_attr.c b/crypto/pkcs7/pk7_attr.c
new file mode 100644
index 000000000..e90bf03c5
--- /dev/null
+++ b/crypto/pkcs7/pk7_attr.c
@@ -0,0 +1,121 @@
+/*
+ * Copyright 1999-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+int PKCS7_add_attrib_smimecap(PKCS7_SIGNER_INFO *si,
+                              STACK_OF(X509_ALGOR) *cap)
+{
+    ASN1_STRING *seq;
+
+    if ((seq = ASN1_STRING_new()) == NULL) {
+        PKCS7err(PKCS7_F_PKCS7_ADD_ATTRIB_SMIMECAP, ERR_R_MALLOC_FAILURE);
+        return 0;
+    }
+    seq->length = ASN1_item_i2d((ASN1_VALUE *)cap, &seq->data,
+                                ASN1_ITEM_rptr(X509_ALGORS));
+    return PKCS7_add_signed_attribute(si, NID_SMIMECapabilities,
+                                      V_ASN1_SEQUENCE, seq);
+}
+
+STACK_OF(X509_ALGOR) *PKCS7_get_smimecap(PKCS7_SIGNER_INFO *si)
+{
+    ASN1_TYPE *cap;
+    const unsigned char *p;
+
+    cap = PKCS7_get_signed_attribute(si, NID_SMIMECapabilities);
+    if (cap == NULL || (cap->type != V_ASN1_SEQUENCE))
+        return NULL;
+    p = cap->value.sequence->data;
+    return (STACK_OF(X509_ALGOR) *)
+        ASN1_item_d2i(NULL, &p, cap->value.sequence->length,
+                      ASN1_ITEM_rptr(X509_ALGORS));
+}
+
+/* Basic smime-capabilities OID and optional integer arg */
+int PKCS7_simple_smimecap(STACK_OF(X509_ALGOR) *sk, int nid, int arg)
+{
+    ASN1_INTEGER *nbit = NULL;
+    X509_ALGOR *alg;
+
+    if ((alg = X509_ALGOR_new()) == NULL) {
+        PKCS7err(PKCS7_F_PKCS7_SIMPLE_SMIMECAP, ERR_R_MALLOC_FAILURE);
+        return 0;
+    }
+    ASN1_OBJECT_free(alg->algorithm);
+    alg->algorithm = OBJ_nid2obj(nid);
+    if (arg > 0) {
+        if ((alg->parameter = ASN1_TYPE_new()) == NULL) {
+            goto err;
+        }
+        if ((nbit = ASN1_INTEGER_new()) == NULL) {
+            goto err;
+        }
+        if (!ASN1_INTEGER_set(nbit, arg)) {
+            goto err;
+        }
+        alg->parameter->value.integer = nbit;
+        alg->parameter->type = V_ASN1_INTEGER;
+        nbit = NULL;
+    }
+    if (!sk_X509_ALGOR_push(sk, alg)) {
+        goto err;
+    }
+    return 1;
+err:
+    PKCS7err(PKCS7_F_PKCS7_SIMPLE_SMIMECAP, ERR_R_MALLOC_FAILURE);
+    ASN1_INTEGER_free(nbit);
+    X509_ALGOR_free(alg);
+    return 0;
+}
+
+int PKCS7_add_attrib_content_type(PKCS7_SIGNER_INFO *si, ASN1_OBJECT *coid)
+{
+    if (PKCS7_get_signed_attribute(si, NID_pkcs9_contentType))
+        return 0;
+    if (!coid)
+        coid = OBJ_nid2obj(NID_pkcs7_data);
+    return PKCS7_add_signed_attribute(si, NID_pkcs9_contentType,
+                                      V_ASN1_OBJECT, coid);
+}
+
+int PKCS7_add0_attrib_signing_time(PKCS7_SIGNER_INFO *si, ASN1_TIME *t)
+{
+    if (t == NULL && (t = X509_gmtime_adj(NULL, 0)) == NULL) {
+        PKCS7err(PKCS7_F_PKCS7_ADD0_ATTRIB_SIGNING_TIME,
+                 ERR_R_MALLOC_FAILURE);
+        return 0;
+    }
+    return PKCS7_add_signed_attribute(si, NID_pkcs9_signingTime,
+                                      V_ASN1_UTCTIME, t);
+}
+
+int PKCS7_add1_attrib_digest(PKCS7_SIGNER_INFO *si,
+                             const unsigned char *md, int mdlen)
+{
+    ASN1_OCTET_STRING *os;
+    os = ASN1_OCTET_STRING_new();
+    if (os == NULL)
+        return 0;
+    if (!ASN1_STRING_set(os, md, mdlen)
+        || !PKCS7_add_signed_attribute(si, NID_pkcs9_messageDigest,
+                                       V_ASN1_OCTET_STRING, os)) {
+        ASN1_OCTET_STRING_free(os);
+        return 0;
+    }
+    return 1;
+}
diff --git a/crypto/pkcs7/pk7_dgst.c b/crypto/pkcs7/pk7_dgst.c
new file mode 100644
index 000000000..965fb37ea
--- /dev/null
+++ b/crypto/pkcs7/pk7_dgst.c
@@ -0,0 +1,15 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+#include 
+#include 
diff --git a/crypto/pkcs7/pk7_doit.c b/crypto/pkcs7/pk7_doit.c
new file mode 100644
index 000000000..435b77168
--- /dev/null
+++ b/crypto/pkcs7/pk7_doit.c
@@ -0,0 +1,1258 @@
+/* ====================================================================
+ * Copyright (c) 2016 - 2019 The GmSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the GmSSL Project.
+ *    (http://gmssl.org/)"
+ *
+ * 4. The name "GmSSL Project" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    guanzhi1980@gmail.com.
+ *
+ * 5. Products derived from this software may not be called "GmSSL"
+ *    nor may "GmSSL" appear in their names without prior written
+ *    permission of the GmSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the GmSSL Project
+ *    (http://gmssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE GmSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE GmSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+#include 
+#include 
+#include 
+#ifndef OPENSSL_NO_SM2
+# include 
+#endif
+
+static int add_attribute(STACK_OF(X509_ATTRIBUTE) **sk, int nid, int atrtype,
+                         void *value);
+static ASN1_TYPE *get_attribute(STACK_OF(X509_ATTRIBUTE) *sk, int nid);
+
+static int PKCS7_type_is_other(PKCS7 *p7)
+{
+    int isOther = 1;
+
+    int nid = OBJ_obj2nid(p7->type);
+
+    switch (nid) {
+    case NID_pkcs7_data:
+    case NID_pkcs7_signed:
+    case NID_pkcs7_enveloped:
+    case NID_pkcs7_signedAndEnveloped:
+    case NID_pkcs7_digest:
+    case NID_pkcs7_encrypted:
+        isOther = 0;
+        break;
+    default:
+        isOther = 1;
+    }
+
+    return isOther;
+
+}
+
+static ASN1_OCTET_STRING *PKCS7_get_octet_string(PKCS7 *p7)
+{
+    if (PKCS7_type_is_data(p7))
+        return p7->d.data;
+    if (PKCS7_type_is_other(p7) && p7->d.other
+        && (p7->d.other->type == V_ASN1_OCTET_STRING))
+        return p7->d.other->value.octet_string;
+    return NULL;
+}
+
+static int PKCS7_bio_add_digest(BIO **pbio, X509_ALGOR *alg)
+{
+    BIO *btmp;
+    const EVP_MD *md;
+    if ((btmp = BIO_new(BIO_f_md())) == NULL) {
+        PKCS7err(PKCS7_F_PKCS7_BIO_ADD_DIGEST, ERR_R_BIO_LIB);
+        goto err;
+    }
+
+    md = EVP_get_digestbyobj(alg->algorithm);
+    if (md == NULL) {
+        PKCS7err(PKCS7_F_PKCS7_BIO_ADD_DIGEST, PKCS7_R_UNKNOWN_DIGEST_TYPE);
+        goto err;
+    }
+
+    BIO_set_md(btmp, md);
+    if (*pbio == NULL)
+        *pbio = btmp;
+    else if (!BIO_push(*pbio, btmp)) {
+        PKCS7err(PKCS7_F_PKCS7_BIO_ADD_DIGEST, ERR_R_BIO_LIB);
+        goto err;
+    }
+    btmp = NULL;
+
+    return 1;
+
+ err:
+    BIO_free(btmp);
+    return 0;
+
+}
+
+static int pkcs7_encode_rinfo(PKCS7_RECIP_INFO *ri,
+                              unsigned char *key, int keylen)
+{
+    EVP_PKEY_CTX *pctx = NULL;
+    EVP_PKEY *pkey = NULL;
+    unsigned char *ek = NULL;
+    int ret = 0;
+    size_t eklen;
+
+    pkey = X509_get0_pubkey(ri->cert);
+
+    if (!pkey)
+        return 0;
+
+    pctx = EVP_PKEY_CTX_new(pkey, NULL);
+    if (!pctx)
+        return 0;
+
+    if (EVP_PKEY_encrypt_init(pctx) <= 0)
+        goto err;
+
+#ifndef OPENSSL_NO_SM2
+    if (OBJ_obj2nid(ri->key_enc_algor->algorithm) == NID_sm2encrypt_with_sm3) {
+        EVP_PKEY_CTX_set_ec_scheme(pctx, NID_sm_scheme);
+        EVP_PKEY_CTX_set_ec_encrypt_param(pctx, NID_sm3);
+    }
+#endif
+
+    if (EVP_PKEY_CTX_ctrl(pctx, -1, EVP_PKEY_OP_ENCRYPT,
+                          EVP_PKEY_CTRL_PKCS7_ENCRYPT, 0, ri) <= 0) {
+        PKCS7err(PKCS7_F_PKCS7_ENCODE_RINFO, PKCS7_R_CTRL_ERROR);
+        goto err;
+    }
+
+    if (EVP_PKEY_encrypt(pctx, NULL, &eklen, key, keylen) <= 0)
+        goto err;
+
+    ek = OPENSSL_malloc(eklen);
+
+    if (ek == NULL) {
+        PKCS7err(PKCS7_F_PKCS7_ENCODE_RINFO, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+
+    if (EVP_PKEY_encrypt(pctx, ek, &eklen, key, keylen) <= 0)
+        goto err;
+
+    ASN1_STRING_set0(ri->enc_key, ek, eklen);
+    ek = NULL;
+
+    ret = 1;
+
+ err:
+    EVP_PKEY_CTX_free(pctx);
+    OPENSSL_free(ek);
+    return ret;
+
+}
+
+static int pkcs7_decrypt_rinfo(unsigned char **pek, int *peklen,
+                               PKCS7_RECIP_INFO *ri, EVP_PKEY *pkey)
+{
+    EVP_PKEY_CTX *pctx = NULL;
+    unsigned char *ek = NULL;
+    size_t eklen;
+
+    int ret = -1;
+
+    pctx = EVP_PKEY_CTX_new(pkey, NULL);
+    if (!pctx)
+        return -1;
+
+    if (EVP_PKEY_decrypt_init(pctx) <= 0)
+        goto err;
+
+#ifndef OPENSSL_NO_SM2
+    if (OBJ_obj2nid(ri->key_enc_algor->algorithm) == NID_sm2encrypt_with_sm3) {
+        EVP_PKEY_CTX_set_ec_scheme(pctx, NID_sm_scheme);
+        EVP_PKEY_CTX_set_ec_encrypt_param(pctx, NID_sm3);
+    }
+#endif
+
+    if (EVP_PKEY_CTX_ctrl(pctx, -1, EVP_PKEY_OP_DECRYPT,
+                          EVP_PKEY_CTRL_PKCS7_DECRYPT, 0, ri) <= 0) {
+        PKCS7err(PKCS7_F_PKCS7_DECRYPT_RINFO, PKCS7_R_CTRL_ERROR);
+        goto err;
+    }
+
+    if (EVP_PKEY_decrypt(pctx, NULL, &eklen,
+                         ri->enc_key->data, ri->enc_key->length) <= 0)
+        goto err;
+
+    ek = OPENSSL_malloc(eklen);
+
+    if (ek == NULL) {
+        PKCS7err(PKCS7_F_PKCS7_DECRYPT_RINFO, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+
+    if (EVP_PKEY_decrypt(pctx, ek, &eklen,
+                         ri->enc_key->data, ri->enc_key->length) <= 0) {
+        ret = 0;
+        PKCS7err(PKCS7_F_PKCS7_DECRYPT_RINFO, ERR_R_EVP_LIB);
+        goto err;
+    }
+
+    ret = 1;
+
+    OPENSSL_clear_free(*pek, *peklen);
+    *pek = ek;
+    *peklen = eklen;
+
+ err:
+    EVP_PKEY_CTX_free(pctx);
+    if (!ret)
+        OPENSSL_free(ek);
+
+    return ret;
+}
+
+BIO *PKCS7_dataInit(PKCS7 *p7, BIO *bio)
+{
+    int i;
+    BIO *out = NULL, *btmp = NULL;
+    X509_ALGOR *xa = NULL;
+    const EVP_CIPHER *evp_cipher = NULL;
+    STACK_OF(X509_ALGOR) *md_sk = NULL;
+    STACK_OF(PKCS7_RECIP_INFO) *rsk = NULL;
+    X509_ALGOR *xalg = NULL;
+    PKCS7_RECIP_INFO *ri = NULL;
+    ASN1_OCTET_STRING *os = NULL;
+
+    if (p7 == NULL) {
+        PKCS7err(PKCS7_F_PKCS7_DATAINIT, PKCS7_R_INVALID_NULL_POINTER);
+        return NULL;
+    }
+    /*
+     * The content field in the PKCS7 ContentInfo is optional, but that really
+     * only applies to inner content (precisely, detached signatures).
+     *
+     * When reading content, missing outer content is therefore treated as an
+     * error.
+     *
+     * When creating content, PKCS7_content_new() must be called before
+     * calling this method, so a NULL p7->d is always an error.
+     */
+    if (p7->d.ptr == NULL) {
+        PKCS7err(PKCS7_F_PKCS7_DATAINIT, PKCS7_R_NO_CONTENT);
+        return NULL;
+    }
+
+    i = OBJ_obj2nid(p7->type);
+    p7->state = PKCS7_S_HEADER;
+
+    switch (i) {
+    case NID_pkcs7_signed:
+        md_sk = p7->d.sign->md_algs;
+        os = PKCS7_get_octet_string(p7->d.sign->contents);
+        break;
+    case NID_pkcs7_signedAndEnveloped:
+        rsk = p7->d.signed_and_enveloped->recipientinfo;
+        md_sk = p7->d.signed_and_enveloped->md_algs;
+        xalg = p7->d.signed_and_enveloped->enc_data->algorithm;
+        evp_cipher = p7->d.signed_and_enveloped->enc_data->cipher;
+        if (evp_cipher == NULL) {
+            PKCS7err(PKCS7_F_PKCS7_DATAINIT, PKCS7_R_CIPHER_NOT_INITIALIZED);
+            goto err;
+        }
+        break;
+    case NID_pkcs7_enveloped:
+        rsk = p7->d.enveloped->recipientinfo;
+        xalg = p7->d.enveloped->enc_data->algorithm;
+        evp_cipher = p7->d.enveloped->enc_data->cipher;
+        if (evp_cipher == NULL) {
+            PKCS7err(PKCS7_F_PKCS7_DATAINIT, PKCS7_R_CIPHER_NOT_INITIALIZED);
+            goto err;
+        }
+        break;
+    case NID_pkcs7_digest:
+        xa = p7->d.digest->md;
+        os = PKCS7_get_octet_string(p7->d.digest->contents);
+        break;
+    case NID_pkcs7_data:
+        break;
+    default:
+        PKCS7err(PKCS7_F_PKCS7_DATAINIT, PKCS7_R_UNSUPPORTED_CONTENT_TYPE);
+        goto err;
+    }
+
+    for (i = 0; i < sk_X509_ALGOR_num(md_sk); i++)
+        if (!PKCS7_bio_add_digest(&out, sk_X509_ALGOR_value(md_sk, i)))
+            goto err;
+
+    if (xa && !PKCS7_bio_add_digest(&out, xa))
+        goto err;
+
+    if (evp_cipher != NULL) {
+        unsigned char key[EVP_MAX_KEY_LENGTH];
+        unsigned char iv[EVP_MAX_IV_LENGTH];
+        int keylen, ivlen;
+        EVP_CIPHER_CTX *ctx;
+
+        if ((btmp = BIO_new(BIO_f_cipher())) == NULL) {
+            PKCS7err(PKCS7_F_PKCS7_DATAINIT, ERR_R_BIO_LIB);
+            goto err;
+        }
+        BIO_get_cipher_ctx(btmp, &ctx);
+        keylen = EVP_CIPHER_key_length(evp_cipher);
+        ivlen = EVP_CIPHER_iv_length(evp_cipher);
+        xalg->algorithm = OBJ_nid2obj(EVP_CIPHER_type(evp_cipher));
+        if (ivlen > 0)
+            if (RAND_bytes(iv, ivlen) <= 0)
+                goto err;
+        if (EVP_CipherInit_ex(ctx, evp_cipher, NULL, NULL, NULL, 1) <= 0)
+            goto err;
+        if (EVP_CIPHER_CTX_rand_key(ctx, key) <= 0)
+            goto err;
+        if (EVP_CipherInit_ex(ctx, NULL, NULL, key, iv, 1) <= 0)
+            goto err;
+
+        if (ivlen > 0) {
+            if (xalg->parameter == NULL) {
+                xalg->parameter = ASN1_TYPE_new();
+                if (xalg->parameter == NULL)
+                    goto err;
+            }
+            if (EVP_CIPHER_param_to_asn1(ctx, xalg->parameter) < 0)
+                goto err;
+        }
+
+        /* Lets do the pub key stuff :-) */
+        for (i = 0; i < sk_PKCS7_RECIP_INFO_num(rsk); i++) {
+            ri = sk_PKCS7_RECIP_INFO_value(rsk, i);
+            if (pkcs7_encode_rinfo(ri, key, keylen) <= 0)
+                goto err;
+        }
+        OPENSSL_cleanse(key, keylen);
+
+        if (out == NULL)
+            out = btmp;
+        else
+            BIO_push(out, btmp);
+        btmp = NULL;
+    }
+
+    if (bio == NULL) {
+        if (PKCS7_is_detached(p7))
+            bio = BIO_new(BIO_s_null());
+        else if (os && os->length > 0)
+            bio = BIO_new_mem_buf(os->data, os->length);
+        if (bio == NULL) {
+            bio = BIO_new(BIO_s_mem());
+            if (bio == NULL)
+                goto err;
+            BIO_set_mem_eof_return(bio, 0);
+        }
+    }
+    if (out)
+        BIO_push(out, bio);
+    else
+        out = bio;
+    return out;
+
+ err:
+    BIO_free_all(out);
+    BIO_free_all(btmp);
+    return NULL;
+}
+
+static int pkcs7_cmp_ri(PKCS7_RECIP_INFO *ri, X509 *pcert)
+{
+    int ret;
+    ret = X509_NAME_cmp(ri->issuer_and_serial->issuer,
+                        X509_get_issuer_name(pcert));
+    if (ret)
+        return ret;
+    return ASN1_INTEGER_cmp(X509_get_serialNumber(pcert),
+                            ri->issuer_and_serial->serial);
+}
+
+/* int */
+BIO *PKCS7_dataDecode(PKCS7 *p7, EVP_PKEY *pkey, BIO *in_bio, X509 *pcert)
+{
+    int i, j;
+    BIO *out = NULL, *btmp = NULL, *etmp = NULL, *bio = NULL;
+    X509_ALGOR *xa;
+    ASN1_OCTET_STRING *data_body = NULL;
+    const EVP_MD *evp_md;
+    const EVP_CIPHER *evp_cipher = NULL;
+    EVP_CIPHER_CTX *evp_ctx = NULL;
+    X509_ALGOR *enc_alg = NULL;
+    STACK_OF(X509_ALGOR) *md_sk = NULL;
+    STACK_OF(PKCS7_RECIP_INFO) *rsk = NULL;
+    PKCS7_RECIP_INFO *ri = NULL;
+    unsigned char *ek = NULL, *tkey = NULL;
+    int eklen = 0, tkeylen = 0;
+
+    if (p7 == NULL) {
+        PKCS7err(PKCS7_F_PKCS7_DATADECODE, PKCS7_R_INVALID_NULL_POINTER);
+        return NULL;
+    }
+
+    if (p7->d.ptr == NULL) {
+        PKCS7err(PKCS7_F_PKCS7_DATADECODE, PKCS7_R_NO_CONTENT);
+        return NULL;
+    }
+
+    i = OBJ_obj2nid(p7->type);
+    p7->state = PKCS7_S_HEADER;
+
+    switch (i) {
+    case NID_pkcs7_signed:
+        /*
+         * p7->d.sign->contents is a PKCS7 structure consisting of a contentType
+         * field and optional content.
+         * data_body is NULL if that structure has no (=detached) content
+         * or if the contentType is wrong (i.e., not "data").
+         */
+        data_body = PKCS7_get_octet_string(p7->d.sign->contents);
+        if (!PKCS7_is_detached(p7) && data_body == NULL) {
+            PKCS7err(PKCS7_F_PKCS7_DATADECODE,
+                     PKCS7_R_INVALID_SIGNED_DATA_TYPE);
+            goto err;
+        }
+        md_sk = p7->d.sign->md_algs;
+        break;
+    case NID_pkcs7_signedAndEnveloped:
+        rsk = p7->d.signed_and_enveloped->recipientinfo;
+        md_sk = p7->d.signed_and_enveloped->md_algs;
+        /* data_body is NULL if the optional EncryptedContent is missing. */
+        data_body = p7->d.signed_and_enveloped->enc_data->enc_data;
+        enc_alg = p7->d.signed_and_enveloped->enc_data->algorithm;
+        evp_cipher = EVP_get_cipherbyobj(enc_alg->algorithm);
+        if (evp_cipher == NULL) {
+            PKCS7err(PKCS7_F_PKCS7_DATADECODE,
+                     PKCS7_R_UNSUPPORTED_CIPHER_TYPE);
+            goto err;
+        }
+        break;
+    case NID_pkcs7_enveloped:
+        rsk = p7->d.enveloped->recipientinfo;
+        enc_alg = p7->d.enveloped->enc_data->algorithm;
+        /* data_body is NULL if the optional EncryptedContent is missing. */
+        data_body = p7->d.enveloped->enc_data->enc_data;
+        evp_cipher = EVP_get_cipherbyobj(enc_alg->algorithm);
+        if (evp_cipher == NULL) {
+            PKCS7err(PKCS7_F_PKCS7_DATADECODE,
+                     PKCS7_R_UNSUPPORTED_CIPHER_TYPE);
+            goto err;
+        }
+        break;
+    default:
+        PKCS7err(PKCS7_F_PKCS7_DATADECODE, PKCS7_R_UNSUPPORTED_CONTENT_TYPE);
+        goto err;
+    }
+
+    /* Detached content must be supplied via in_bio instead. */
+    if (data_body == NULL && in_bio == NULL) {
+        PKCS7err(PKCS7_F_PKCS7_DATADECODE, PKCS7_R_NO_CONTENT);
+        goto err;
+    }
+
+    /* We will be checking the signature */
+    if (md_sk != NULL) {
+        for (i = 0; i < sk_X509_ALGOR_num(md_sk); i++) {
+            xa = sk_X509_ALGOR_value(md_sk, i);
+            if ((btmp = BIO_new(BIO_f_md())) == NULL) {
+                PKCS7err(PKCS7_F_PKCS7_DATADECODE, ERR_R_BIO_LIB);
+                goto err;
+            }
+
+            j = OBJ_obj2nid(xa->algorithm);
+            evp_md = EVP_get_digestbynid(j);
+            if (evp_md == NULL) {
+                PKCS7err(PKCS7_F_PKCS7_DATADECODE,
+                         PKCS7_R_UNKNOWN_DIGEST_TYPE);
+                goto err;
+            }
+
+            BIO_set_md(btmp, evp_md);
+            if (out == NULL)
+                out = btmp;
+            else
+                BIO_push(out, btmp);
+            btmp = NULL;
+        }
+    }
+
+    if (evp_cipher != NULL) {
+        if ((etmp = BIO_new(BIO_f_cipher())) == NULL) {
+            PKCS7err(PKCS7_F_PKCS7_DATADECODE, ERR_R_BIO_LIB);
+            goto err;
+        }
+
+        /*
+         * It was encrypted, we need to decrypt the secret key with the
+         * private key
+         */
+
+        /*
+         * Find the recipientInfo which matches the passed certificate (if
+         * any)
+         */
+
+        if (pcert) {
+            for (i = 0; i < sk_PKCS7_RECIP_INFO_num(rsk); i++) {
+                ri = sk_PKCS7_RECIP_INFO_value(rsk, i);
+                if (!pkcs7_cmp_ri(ri, pcert))
+                    break;
+                ri = NULL;
+            }
+            if (ri == NULL) {
+                PKCS7err(PKCS7_F_PKCS7_DATADECODE,
+                         PKCS7_R_NO_RECIPIENT_MATCHES_CERTIFICATE);
+                goto err;
+            }
+        }
+
+        /* If we haven't got a certificate try each ri in turn */
+        if (pcert == NULL) {
+            /*
+             * Always attempt to decrypt all rinfo even after success as a
+             * defence against MMA timing attacks.
+             */
+            for (i = 0; i < sk_PKCS7_RECIP_INFO_num(rsk); i++) {
+                ri = sk_PKCS7_RECIP_INFO_value(rsk, i);
+
+                if (pkcs7_decrypt_rinfo(&ek, &eklen, ri, pkey) < 0)
+                    goto err;
+                ERR_clear_error();
+            }
+        } else {
+            /* Only exit on fatal errors, not decrypt failure */
+            if (pkcs7_decrypt_rinfo(&ek, &eklen, ri, pkey) < 0)
+                goto err;
+            ERR_clear_error();
+        }
+
+        evp_ctx = NULL;
+        BIO_get_cipher_ctx(etmp, &evp_ctx);
+        if (EVP_CipherInit_ex(evp_ctx, evp_cipher, NULL, NULL, NULL, 0) <= 0)
+            goto err;
+        if (EVP_CIPHER_asn1_to_param(evp_ctx, enc_alg->parameter) < 0)
+            goto err;
+        /* Generate random key as MMA defence */
+        tkeylen = EVP_CIPHER_CTX_key_length(evp_ctx);
+        tkey = OPENSSL_malloc(tkeylen);
+        if (tkey == NULL)
+            goto err;
+        if (EVP_CIPHER_CTX_rand_key(evp_ctx, tkey) <= 0)
+            goto err;
+        if (ek == NULL) {
+            ek = tkey;
+            eklen = tkeylen;
+            tkey = NULL;
+        }
+
+        if (eklen != EVP_CIPHER_CTX_key_length(evp_ctx)) {
+            /*
+             * Some S/MIME clients don't use the same key and effective key
+             * length. The key length is determined by the size of the
+             * decrypted RSA key.
+             */
+            if (!EVP_CIPHER_CTX_set_key_length(evp_ctx, eklen)) {
+                /* Use random key as MMA defence */
+                OPENSSL_clear_free(ek, eklen);
+                ek = tkey;
+                eklen = tkeylen;
+                tkey = NULL;
+            }
+        }
+        /* Clear errors so we don't leak information useful in MMA */
+        ERR_clear_error();
+        if (EVP_CipherInit_ex(evp_ctx, NULL, NULL, ek, NULL, 0) <= 0)
+            goto err;
+
+        OPENSSL_clear_free(ek, eklen);
+        ek = NULL;
+        OPENSSL_clear_free(tkey, tkeylen);
+        tkey = NULL;
+
+        if (out == NULL)
+            out = etmp;
+        else
+            BIO_push(out, etmp);
+        etmp = NULL;
+    }
+    if (in_bio != NULL) {
+        bio = in_bio;
+    } else {
+        if (data_body->length > 0)
+            bio = BIO_new_mem_buf(data_body->data, data_body->length);
+        else {
+            bio = BIO_new(BIO_s_mem());
+            if (bio == NULL)
+                goto err;
+            BIO_set_mem_eof_return(bio, 0);
+        }
+        if (bio == NULL)
+            goto err;
+    }
+    BIO_push(out, bio);
+    bio = NULL;
+    return out;
+
+ err:
+    OPENSSL_clear_free(ek, eklen);
+    OPENSSL_clear_free(tkey, tkeylen);
+    BIO_free_all(out);
+    BIO_free_all(btmp);
+    BIO_free_all(etmp);
+    BIO_free_all(bio);
+    return NULL;
+}
+
+static BIO *PKCS7_find_digest(EVP_MD_CTX **pmd, BIO *bio, int nid)
+{
+    for (;;) {
+        bio = BIO_find_type(bio, BIO_TYPE_MD);
+        if (bio == NULL) {
+            PKCS7err(PKCS7_F_PKCS7_FIND_DIGEST,
+                     PKCS7_R_UNABLE_TO_FIND_MESSAGE_DIGEST);
+            return NULL;
+        }
+        BIO_get_md_ctx(bio, pmd);
+        if (*pmd == NULL) {
+            PKCS7err(PKCS7_F_PKCS7_FIND_DIGEST, ERR_R_INTERNAL_ERROR);
+            return NULL;
+        }
+        if (EVP_MD_CTX_type(*pmd) == nid)
+            return bio;
+        bio = BIO_next(bio);
+    }
+    return NULL;
+}
+
+static int do_pkcs7_signed_attrib(PKCS7_SIGNER_INFO *si, EVP_MD_CTX *mctx)
+{
+    unsigned char md_data[EVP_MAX_MD_SIZE];
+    unsigned int md_len;
+
+    /* Add signing time if not already present */
+    if (!PKCS7_get_signed_attribute(si, NID_pkcs9_signingTime)) {
+        if (!PKCS7_add0_attrib_signing_time(si, NULL)) {
+            PKCS7err(PKCS7_F_DO_PKCS7_SIGNED_ATTRIB, ERR_R_MALLOC_FAILURE);
+            return 0;
+        }
+    }
+
+    /* Add digest */
+    if (!EVP_DigestFinal_ex(mctx, md_data, &md_len)) {
+        PKCS7err(PKCS7_F_DO_PKCS7_SIGNED_ATTRIB, ERR_R_EVP_LIB);
+        return 0;
+    }
+    if (!PKCS7_add1_attrib_digest(si, md_data, md_len)) {
+        PKCS7err(PKCS7_F_DO_PKCS7_SIGNED_ATTRIB, ERR_R_MALLOC_FAILURE);
+        return 0;
+    }
+
+    /* Now sign the attributes */
+    if (!PKCS7_SIGNER_INFO_sign(si))
+        return 0;
+
+    return 1;
+}
+
+int PKCS7_dataFinal(PKCS7 *p7, BIO *bio)
+{
+    int ret = 0;
+    int i, j;
+    BIO *btmp;
+    PKCS7_SIGNER_INFO *si;
+    EVP_MD_CTX *mdc, *ctx_tmp;
+    STACK_OF(X509_ATTRIBUTE) *sk;
+    STACK_OF(PKCS7_SIGNER_INFO) *si_sk = NULL;
+    ASN1_OCTET_STRING *os = NULL;
+
+    if (p7 == NULL) {
+        PKCS7err(PKCS7_F_PKCS7_DATAFINAL, PKCS7_R_INVALID_NULL_POINTER);
+        return 0;
+    }
+
+    if (p7->d.ptr == NULL) {
+        PKCS7err(PKCS7_F_PKCS7_DATAFINAL, PKCS7_R_NO_CONTENT);
+        return 0;
+    }
+
+    ctx_tmp = EVP_MD_CTX_new();
+    if (ctx_tmp == NULL) {
+        PKCS7err(PKCS7_F_PKCS7_DATAFINAL, ERR_R_MALLOC_FAILURE);
+        return 0;
+    }
+
+    i = OBJ_obj2nid(p7->type);
+    p7->state = PKCS7_S_HEADER;
+
+    switch (i) {
+    case NID_pkcs7_data:
+        os = p7->d.data;
+        break;
+    case NID_pkcs7_signedAndEnveloped:
+        /* XXXXXXXXXXXXXXXX */
+        si_sk = p7->d.signed_and_enveloped->signer_info;
+        os = p7->d.signed_and_enveloped->enc_data->enc_data;
+        if (os == NULL) {
+            os = ASN1_OCTET_STRING_new();
+            if (os == NULL) {
+                PKCS7err(PKCS7_F_PKCS7_DATAFINAL, ERR_R_MALLOC_FAILURE);
+                goto err;
+            }
+            p7->d.signed_and_enveloped->enc_data->enc_data = os;
+        }
+        break;
+    case NID_pkcs7_enveloped:
+        /* XXXXXXXXXXXXXXXX */
+        os = p7->d.enveloped->enc_data->enc_data;
+        if (os == NULL) {
+            os = ASN1_OCTET_STRING_new();
+            if (os == NULL) {
+                PKCS7err(PKCS7_F_PKCS7_DATAFINAL, ERR_R_MALLOC_FAILURE);
+                goto err;
+            }
+            p7->d.enveloped->enc_data->enc_data = os;
+        }
+        break;
+    case NID_pkcs7_signed:
+        si_sk = p7->d.sign->signer_info;
+        os = PKCS7_get_octet_string(p7->d.sign->contents);
+        /* If detached data then the content is excluded */
+        if (PKCS7_type_is_data(p7->d.sign->contents) && p7->detached) {
+            ASN1_OCTET_STRING_free(os);
+            os = NULL;
+            p7->d.sign->contents->d.data = NULL;
+        }
+        break;
+
+    case NID_pkcs7_digest:
+        os = PKCS7_get_octet_string(p7->d.digest->contents);
+        /* If detached data then the content is excluded */
+        if (PKCS7_type_is_data(p7->d.digest->contents) && p7->detached) {
+            ASN1_OCTET_STRING_free(os);
+            os = NULL;
+            p7->d.digest->contents->d.data = NULL;
+        }
+        break;
+
+    default:
+        PKCS7err(PKCS7_F_PKCS7_DATAFINAL, PKCS7_R_UNSUPPORTED_CONTENT_TYPE);
+        goto err;
+    }
+
+    if (si_sk != NULL) {
+        for (i = 0; i < sk_PKCS7_SIGNER_INFO_num(si_sk); i++) {
+            si = sk_PKCS7_SIGNER_INFO_value(si_sk, i);
+            if (si->pkey == NULL)
+                continue;
+
+            j = OBJ_obj2nid(si->digest_alg->algorithm);
+
+            btmp = bio;
+
+            btmp = PKCS7_find_digest(&mdc, btmp, j);
+
+            if (btmp == NULL)
+                goto err;
+
+            /*
+             * We now have the EVP_MD_CTX, lets do the signing.
+             */
+            if (!EVP_MD_CTX_copy_ex(ctx_tmp, mdc))
+                goto err;
+
+            sk = si->auth_attr;
+
+            /*
+             * If there are attributes, we add the digest attribute and only
+             * sign the attributes
+             */
+            if (sk_X509_ATTRIBUTE_num(sk) > 0) {
+                if (!do_pkcs7_signed_attrib(si, ctx_tmp))
+                    goto err;
+            } else {
+                unsigned char *abuf = NULL;
+                unsigned int abuflen;
+                abuflen = EVP_PKEY_size(si->pkey);
+                abuf = OPENSSL_malloc(abuflen);
+                if (abuf == NULL)
+                    goto err;
+
+                if (!EVP_SignFinal(ctx_tmp, abuf, &abuflen, si->pkey)) {
+                    OPENSSL_free(abuf);
+                    PKCS7err(PKCS7_F_PKCS7_DATAFINAL, ERR_R_EVP_LIB);
+                    goto err;
+                }
+                ASN1_STRING_set0(si->enc_digest, abuf, abuflen);
+            }
+        }
+    } else if (i == NID_pkcs7_digest) {
+        unsigned char md_data[EVP_MAX_MD_SIZE];
+        unsigned int md_len;
+        if (!PKCS7_find_digest(&mdc, bio,
+                               OBJ_obj2nid(p7->d.digest->md->algorithm)))
+            goto err;
+        if (!EVP_DigestFinal_ex(mdc, md_data, &md_len))
+            goto err;
+        if (!ASN1_OCTET_STRING_set(p7->d.digest->digest, md_data, md_len))
+            goto err;
+    }
+
+    if (!PKCS7_is_detached(p7)) {
+        /*
+         * NOTE(emilia): I think we only reach os == NULL here because detached
+         * digested data support is broken.
+         */
+        if (os == NULL)
+            goto err;
+        if (!(os->flags & ASN1_STRING_FLAG_NDEF)) {
+            char *cont;
+            long contlen;
+            btmp = BIO_find_type(bio, BIO_TYPE_MEM);
+            if (btmp == NULL) {
+                PKCS7err(PKCS7_F_PKCS7_DATAFINAL, PKCS7_R_UNABLE_TO_FIND_MEM_BIO);
+                goto err;
+            }
+            contlen = BIO_get_mem_data(btmp, &cont);
+            /*
+             * Mark the BIO read only then we can use its copy of the data
+             * instead of making an extra copy.
+             */
+            BIO_set_flags(btmp, BIO_FLAGS_MEM_RDONLY);
+            BIO_set_mem_eof_return(btmp, 0);
+            ASN1_STRING_set0(os, (unsigned char *)cont, contlen);
+        }
+    }
+    ret = 1;
+ err:
+    EVP_MD_CTX_free(ctx_tmp);
+    return (ret);
+}
+
+int PKCS7_SIGNER_INFO_sign(PKCS7_SIGNER_INFO *si)
+{
+    EVP_MD_CTX *mctx;
+    EVP_PKEY_CTX *pctx;
+    unsigned char *abuf = NULL;
+    int alen;
+    size_t siglen;
+    const EVP_MD *md = NULL;
+
+    md = EVP_get_digestbyobj(si->digest_alg->algorithm);
+    if (md == NULL)
+        return 0;
+
+    mctx = EVP_MD_CTX_new();
+    if (mctx == NULL) {
+        PKCS7err(PKCS7_F_PKCS7_SIGNER_INFO_SIGN, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+
+    if (EVP_DigestSignInit(mctx, &pctx, md, NULL, si->pkey) <= 0)
+        goto err;
+
+#ifndef OPENSSL_NO_SM2
+    if (OBJ_obj2nid(si->digest_enc_alg->algorithm) == NID_sm2sign_with_sm3) {
+        EVP_PKEY_CTX_set_ec_scheme(pctx, NID_sm_scheme);
+    }
+#endif
+
+    if (EVP_PKEY_CTX_ctrl(pctx, -1, EVP_PKEY_OP_SIGN,
+                          EVP_PKEY_CTRL_PKCS7_SIGN, 0, si) <= 0) {
+        PKCS7err(PKCS7_F_PKCS7_SIGNER_INFO_SIGN, PKCS7_R_CTRL_ERROR);
+        goto err;
+    }
+
+    alen = ASN1_item_i2d((ASN1_VALUE *)si->auth_attr, &abuf,
+                         ASN1_ITEM_rptr(PKCS7_ATTR_SIGN));
+    if (!abuf)
+        goto err;
+    if (EVP_DigestSignUpdate(mctx, abuf, alen) <= 0)
+        goto err;
+    OPENSSL_free(abuf);
+    abuf = NULL;
+    if (EVP_DigestSignFinal(mctx, NULL, &siglen) <= 0)
+        goto err;
+    abuf = OPENSSL_malloc(siglen);
+    if (abuf == NULL)
+        goto err;
+    if (EVP_DigestSignFinal(mctx, abuf, &siglen) <= 0)
+        goto err;
+
+    if (EVP_PKEY_CTX_ctrl(pctx, -1, EVP_PKEY_OP_SIGN,
+                          EVP_PKEY_CTRL_PKCS7_SIGN, 1, si) <= 0) {
+        PKCS7err(PKCS7_F_PKCS7_SIGNER_INFO_SIGN, PKCS7_R_CTRL_ERROR);
+        goto err;
+    }
+
+    EVP_MD_CTX_free(mctx);
+
+    ASN1_STRING_set0(si->enc_digest, abuf, siglen);
+
+    return 1;
+
+ err:
+    OPENSSL_free(abuf);
+    EVP_MD_CTX_free(mctx);
+    return 0;
+
+}
+
+int PKCS7_dataVerify(X509_STORE *cert_store, X509_STORE_CTX *ctx, BIO *bio,
+                     PKCS7 *p7, PKCS7_SIGNER_INFO *si)
+{
+    PKCS7_ISSUER_AND_SERIAL *ias;
+    int ret = 0, i;
+    STACK_OF(X509) *cert;
+    X509 *x509;
+
+    if (p7 == NULL) {
+        PKCS7err(PKCS7_F_PKCS7_DATAVERIFY, PKCS7_R_INVALID_NULL_POINTER);
+        return 0;
+    }
+
+    if (p7->d.ptr == NULL) {
+        PKCS7err(PKCS7_F_PKCS7_DATAVERIFY, PKCS7_R_NO_CONTENT);
+        return 0;
+    }
+
+    if (PKCS7_type_is_signed(p7)) {
+        cert = p7->d.sign->cert;
+    } else if (PKCS7_type_is_signedAndEnveloped(p7)) {
+        cert = p7->d.signed_and_enveloped->cert;
+    } else {
+        PKCS7err(PKCS7_F_PKCS7_DATAVERIFY, PKCS7_R_WRONG_PKCS7_TYPE);
+        goto err;
+    }
+    /* XXXXXXXXXXXXXXXXXXXXXXX */
+    ias = si->issuer_and_serial;
+
+    x509 = X509_find_by_issuer_and_serial(cert, ias->issuer, ias->serial);
+
+    /* were we able to find the cert in passed to us */
+    if (x509 == NULL) {
+        PKCS7err(PKCS7_F_PKCS7_DATAVERIFY,
+                 PKCS7_R_UNABLE_TO_FIND_CERTIFICATE);
+        goto err;
+    }
+
+    /* Lets verify */
+    if (!X509_STORE_CTX_init(ctx, cert_store, x509, cert)) {
+        PKCS7err(PKCS7_F_PKCS7_DATAVERIFY, ERR_R_X509_LIB);
+        goto err;
+    }
+    X509_STORE_CTX_set_purpose(ctx, X509_PURPOSE_SMIME_SIGN);
+    i = X509_verify_cert(ctx);
+    if (i <= 0) {
+        PKCS7err(PKCS7_F_PKCS7_DATAVERIFY, ERR_R_X509_LIB);
+        X509_STORE_CTX_cleanup(ctx);
+        goto err;
+    }
+    X509_STORE_CTX_cleanup(ctx);
+
+    return PKCS7_signatureVerify(bio, p7, si, x509);
+ err:
+    return ret;
+}
+
+int PKCS7_signatureVerify(BIO *bio, PKCS7 *p7, PKCS7_SIGNER_INFO *si,
+                          X509 *x509)
+{
+    ASN1_OCTET_STRING *os;
+    EVP_MD_CTX *mdc_tmp, *mdc;
+    int ret = 0, i;
+    int md_type;
+    STACK_OF(X509_ATTRIBUTE) *sk;
+    BIO *btmp;
+    EVP_PKEY *pkey;
+#ifndef OPENSSL_NO_SM2
+    EVP_PKEY_CTX *pctx;
+#endif
+
+    mdc_tmp = EVP_MD_CTX_new();
+    if (mdc_tmp == NULL) {
+        PKCS7err(PKCS7_F_PKCS7_SIGNATUREVERIFY, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+
+    if (!PKCS7_type_is_signed(p7) && !PKCS7_type_is_signedAndEnveloped(p7)) {
+        PKCS7err(PKCS7_F_PKCS7_SIGNATUREVERIFY, PKCS7_R_WRONG_PKCS7_TYPE);
+        goto err;
+    }
+
+    md_type = OBJ_obj2nid(si->digest_alg->algorithm);
+
+    btmp = bio;
+    for (;;) {
+        if ((btmp == NULL) ||
+            ((btmp = BIO_find_type(btmp, BIO_TYPE_MD)) == NULL)) {
+            PKCS7err(PKCS7_F_PKCS7_SIGNATUREVERIFY,
+                     PKCS7_R_UNABLE_TO_FIND_MESSAGE_DIGEST);
+            goto err;
+        }
+        BIO_get_md_ctx(btmp, &mdc);
+        if (mdc == NULL) {
+            PKCS7err(PKCS7_F_PKCS7_SIGNATUREVERIFY, ERR_R_INTERNAL_ERROR);
+            goto err;
+        }
+        if (EVP_MD_CTX_type(mdc) == md_type)
+            break;
+        /*
+         * Workaround for some broken clients that put the signature OID
+         * instead of the digest OID in digest_alg->algorithm
+         */
+        if (EVP_MD_pkey_type(EVP_MD_CTX_md(mdc)) == md_type)
+            break;
+        btmp = BIO_next(btmp);
+    }
+
+    /*
+     * mdc is the digest ctx that we want, unless there are attributes, in
+     * which case the digest is the signed attributes
+     */
+    if (!EVP_MD_CTX_copy_ex(mdc_tmp, mdc))
+        goto err;
+
+    os = si->enc_digest;
+    pkey = X509_get0_pubkey(x509);
+    if (!pkey) {
+        ret = -1;
+        goto err;
+    }
+
+    sk = si->auth_attr;
+    if ((sk != NULL) && (sk_X509_ATTRIBUTE_num(sk) != 0)) {
+        unsigned char md_dat[EVP_MAX_MD_SIZE], *abuf = NULL;
+        unsigned int md_len;
+        int alen;
+        ASN1_OCTET_STRING *message_digest;
+
+        if (!EVP_DigestFinal_ex(mdc_tmp, md_dat, &md_len))
+            goto err;
+        message_digest = PKCS7_digest_from_attributes(sk);
+        if (!message_digest) {
+            PKCS7err(PKCS7_F_PKCS7_SIGNATUREVERIFY,
+                     PKCS7_R_UNABLE_TO_FIND_MESSAGE_DIGEST);
+            goto err;
+        }
+        if ((message_digest->length != (int)md_len) ||
+            (memcmp(message_digest->data, md_dat, md_len))) {
+            PKCS7err(PKCS7_F_PKCS7_SIGNATUREVERIFY, PKCS7_R_DIGEST_FAILURE);
+            ret = -1;
+            goto err;
+        }
+
+        if (!EVP_DigestVerifyInit(mdc_tmp, &pctx,
+                                  EVP_get_digestbynid(md_type), NULL, pkey)) {
+            goto err;
+        }
+#ifndef OPENSSL_NO_SM2
+        if (OBJ_obj2nid(si->digest_enc_alg->algorithm) == NID_sm2sign_with_sm3) {
+            EVP_PKEY_CTX_set_ec_scheme(pctx, NID_sm_scheme);
+        }
+#endif
+        alen = ASN1_item_i2d((ASN1_VALUE *)sk, &abuf,
+                             ASN1_ITEM_rptr(PKCS7_ATTR_VERIFY));
+        if (alen <= 0) {
+            PKCS7err(PKCS7_F_PKCS7_SIGNATUREVERIFY, ERR_R_ASN1_LIB);
+            ret = -1;
+            goto err;
+        }
+        if (!EVP_DigestVerifyUpdate(mdc_tmp, abuf, alen))
+            goto err;
+
+        OPENSSL_free(abuf);
+    }
+
+    i = EVP_DigestVerifyFinal(mdc_tmp, os->data, os->length);
+    if (i <= 0) {
+        PKCS7err(PKCS7_F_PKCS7_SIGNATUREVERIFY, PKCS7_R_SIGNATURE_FAILURE);
+        ret = -1;
+        goto err;
+    }
+    ret = 1;
+ err:
+    EVP_MD_CTX_free(mdc_tmp);
+    return (ret);
+}
+
+PKCS7_ISSUER_AND_SERIAL *PKCS7_get_issuer_and_serial(PKCS7 *p7, int idx)
+{
+    STACK_OF(PKCS7_RECIP_INFO) *rsk;
+    PKCS7_RECIP_INFO *ri;
+    int i;
+
+    i = OBJ_obj2nid(p7->type);
+    if (i != NID_pkcs7_signedAndEnveloped)
+        return NULL;
+    if (p7->d.signed_and_enveloped == NULL)
+        return NULL;
+    rsk = p7->d.signed_and_enveloped->recipientinfo;
+    if (rsk == NULL)
+        return NULL;
+    if (sk_PKCS7_RECIP_INFO_num(rsk) <= idx)
+        return (NULL);
+    ri = sk_PKCS7_RECIP_INFO_value(rsk, idx);
+    return (ri->issuer_and_serial);
+}
+
+ASN1_TYPE *PKCS7_get_signed_attribute(PKCS7_SIGNER_INFO *si, int nid)
+{
+    return (get_attribute(si->auth_attr, nid));
+}
+
+ASN1_TYPE *PKCS7_get_attribute(PKCS7_SIGNER_INFO *si, int nid)
+{
+    return (get_attribute(si->unauth_attr, nid));
+}
+
+static ASN1_TYPE *get_attribute(STACK_OF(X509_ATTRIBUTE) *sk, int nid)
+{
+    int idx;
+    X509_ATTRIBUTE *xa;
+    idx = X509at_get_attr_by_NID(sk, nid, -1);
+    xa = X509at_get_attr(sk, idx);
+    return X509_ATTRIBUTE_get0_type(xa, 0);
+}
+
+ASN1_OCTET_STRING *PKCS7_digest_from_attributes(STACK_OF(X509_ATTRIBUTE) *sk)
+{
+    ASN1_TYPE *astype;
+    if ((astype = get_attribute(sk, NID_pkcs9_messageDigest)) == NULL)
+        return NULL;
+    return astype->value.octet_string;
+}
+
+int PKCS7_set_signed_attributes(PKCS7_SIGNER_INFO *p7si,
+                                STACK_OF(X509_ATTRIBUTE) *sk)
+{
+    int i;
+
+    sk_X509_ATTRIBUTE_pop_free(p7si->auth_attr, X509_ATTRIBUTE_free);
+    p7si->auth_attr = sk_X509_ATTRIBUTE_dup(sk);
+    if (p7si->auth_attr == NULL)
+        return 0;
+    for (i = 0; i < sk_X509_ATTRIBUTE_num(sk); i++) {
+        if ((sk_X509_ATTRIBUTE_set(p7si->auth_attr, i,
+                                   X509_ATTRIBUTE_dup(sk_X509_ATTRIBUTE_value
+                                                      (sk, i))))
+            == NULL)
+            return (0);
+    }
+    return (1);
+}
+
+int PKCS7_set_attributes(PKCS7_SIGNER_INFO *p7si,
+                         STACK_OF(X509_ATTRIBUTE) *sk)
+{
+    int i;
+
+    sk_X509_ATTRIBUTE_pop_free(p7si->unauth_attr, X509_ATTRIBUTE_free);
+    p7si->unauth_attr = sk_X509_ATTRIBUTE_dup(sk);
+    if (p7si->unauth_attr == NULL)
+        return 0;
+    for (i = 0; i < sk_X509_ATTRIBUTE_num(sk); i++) {
+        if ((sk_X509_ATTRIBUTE_set(p7si->unauth_attr, i,
+                                   X509_ATTRIBUTE_dup(sk_X509_ATTRIBUTE_value
+                                                      (sk, i))))
+            == NULL)
+            return (0);
+    }
+    return (1);
+}
+
+int PKCS7_add_signed_attribute(PKCS7_SIGNER_INFO *p7si, int nid, int atrtype,
+                               void *value)
+{
+    return (add_attribute(&(p7si->auth_attr), nid, atrtype, value));
+}
+
+int PKCS7_add_attribute(PKCS7_SIGNER_INFO *p7si, int nid, int atrtype,
+                        void *value)
+{
+    return (add_attribute(&(p7si->unauth_attr), nid, atrtype, value));
+}
+
+static int add_attribute(STACK_OF(X509_ATTRIBUTE) **sk, int nid, int atrtype,
+                         void *value)
+{
+    X509_ATTRIBUTE *attr = NULL;
+
+    if (*sk == NULL) {
+        if ((*sk = sk_X509_ATTRIBUTE_new_null()) == NULL)
+            return 0;
+ new_attrib:
+        if ((attr = X509_ATTRIBUTE_create(nid, atrtype, value)) == NULL)
+            return 0;
+        if (!sk_X509_ATTRIBUTE_push(*sk, attr)) {
+            X509_ATTRIBUTE_free(attr);
+            return 0;
+        }
+    } else {
+        int i;
+
+        for (i = 0; i < sk_X509_ATTRIBUTE_num(*sk); i++) {
+            attr = sk_X509_ATTRIBUTE_value(*sk, i);
+            if (OBJ_obj2nid(X509_ATTRIBUTE_get0_object(attr)) == nid) {
+                X509_ATTRIBUTE_free(attr);
+                attr = X509_ATTRIBUTE_create(nid, atrtype, value);
+                if (attr == NULL)
+                    return 0;
+                if (!sk_X509_ATTRIBUTE_set(*sk, i, attr)) {
+                    X509_ATTRIBUTE_free(attr);
+                    return 0;
+                }
+                goto end;
+            }
+        }
+        goto new_attrib;
+    }
+ end:
+    return (1);
+}
diff --git a/crypto/pkcs7/pk7_enc.c b/crypto/pkcs7/pk7_enc.c
new file mode 100644
index 000000000..3c59f9c8c
--- /dev/null
+++ b/crypto/pkcs7/pk7_enc.c
@@ -0,0 +1,25 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+#include 
+#include 
+
+PKCS7_in_bio(PKCS7 *p7, BIO *in);
+PKCS7_out_bio(PKCS7 *p7, BIO *out);
+
+PKCS7_add_signer(PKCS7 *p7, X509 *cert, EVP_PKEY *key);
+PKCS7_cipher(PKCS7 *p7, EVP_CIPHER *cipher);
+
+PKCS7_Init(PKCS7 *p7);
+PKCS7_Update(PKCS7 *p7);
+PKCS7_Finish(PKCS7 *p7);
diff --git a/crypto/pkcs7/pk7_lib.c b/crypto/pkcs7/pk7_lib.c
new file mode 100644
index 000000000..69c68cf5f
--- /dev/null
+++ b/crypto/pkcs7/pk7_lib.c
@@ -0,0 +1,589 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+#include "internal/asn1_int.h"
+#include "internal/evp_int.h"
+
+long PKCS7_ctrl(PKCS7 *p7, int cmd, long larg, char *parg)
+{
+    int nid;
+    long ret;
+
+    nid = OBJ_obj2nid(p7->type);
+
+    switch (cmd) {
+    /* NOTE(emilia): does not support detached digested data. */
+    case PKCS7_OP_SET_DETACHED_SIGNATURE:
+        if (nid == NID_pkcs7_signed) {
+            ret = p7->detached = (int)larg;
+            if (ret && PKCS7_type_is_data(p7->d.sign->contents)) {
+                ASN1_OCTET_STRING *os;
+                os = p7->d.sign->contents->d.data;
+                ASN1_OCTET_STRING_free(os);
+                p7->d.sign->contents->d.data = NULL;
+            }
+        } else {
+            PKCS7err(PKCS7_F_PKCS7_CTRL,
+                     PKCS7_R_OPERATION_NOT_SUPPORTED_ON_THIS_TYPE);
+            ret = 0;
+        }
+        break;
+    case PKCS7_OP_GET_DETACHED_SIGNATURE:
+        if (nid == NID_pkcs7_signed) {
+            if (!p7->d.sign || !p7->d.sign->contents->d.ptr)
+                ret = 1;
+            else
+                ret = 0;
+
+            p7->detached = ret;
+        } else {
+            PKCS7err(PKCS7_F_PKCS7_CTRL,
+                     PKCS7_R_OPERATION_NOT_SUPPORTED_ON_THIS_TYPE);
+            ret = 0;
+        }
+
+        break;
+    default:
+        PKCS7err(PKCS7_F_PKCS7_CTRL, PKCS7_R_UNKNOWN_OPERATION);
+        ret = 0;
+    }
+    return (ret);
+}
+
+int PKCS7_content_new(PKCS7 *p7, int type)
+{
+    PKCS7 *ret = NULL;
+
+    if ((ret = PKCS7_new()) == NULL)
+        goto err;
+    if (!PKCS7_set_type(ret, type))
+        goto err;
+    if (!PKCS7_set_content(p7, ret))
+        goto err;
+
+    return (1);
+ err:
+    PKCS7_free(ret);
+    return (0);
+}
+
+int PKCS7_set_content(PKCS7 *p7, PKCS7 *p7_data)
+{
+    int i;
+
+    i = OBJ_obj2nid(p7->type);
+    switch (i) {
+    case NID_pkcs7_signed:
+        PKCS7_free(p7->d.sign->contents);
+        p7->d.sign->contents = p7_data;
+        break;
+    case NID_pkcs7_digest:
+        PKCS7_free(p7->d.digest->contents);
+        p7->d.digest->contents = p7_data;
+        break;
+    case NID_pkcs7_data:
+    case NID_pkcs7_enveloped:
+    case NID_pkcs7_signedAndEnveloped:
+    case NID_pkcs7_encrypted:
+    default:
+        PKCS7err(PKCS7_F_PKCS7_SET_CONTENT, PKCS7_R_UNSUPPORTED_CONTENT_TYPE);
+        goto err;
+    }
+    return (1);
+ err:
+    return (0);
+}
+
+int PKCS7_set_type(PKCS7 *p7, int type)
+{
+    ASN1_OBJECT *obj;
+
+    /*
+     * PKCS7_content_free(p7);
+     */
+    obj = OBJ_nid2obj(type);    /* will not fail */
+
+    switch (type) {
+    case NID_pkcs7_signed:
+        p7->type = obj;
+        if ((p7->d.sign = PKCS7_SIGNED_new()) == NULL)
+            goto err;
+        if (!ASN1_INTEGER_set(p7->d.sign->version, 1)) {
+            PKCS7_SIGNED_free(p7->d.sign);
+            p7->d.sign = NULL;
+            goto err;
+        }
+        break;
+    case NID_pkcs7_data:
+        p7->type = obj;
+        if ((p7->d.data = ASN1_OCTET_STRING_new()) == NULL)
+            goto err;
+        break;
+    case NID_pkcs7_signedAndEnveloped:
+        p7->type = obj;
+        if ((p7->d.signed_and_enveloped = PKCS7_SIGN_ENVELOPE_new())
+            == NULL)
+            goto err;
+        ASN1_INTEGER_set(p7->d.signed_and_enveloped->version, 1);
+        if (!ASN1_INTEGER_set(p7->d.signed_and_enveloped->version, 1))
+            goto err;
+        p7->d.signed_and_enveloped->enc_data->content_type
+            = OBJ_nid2obj(NID_pkcs7_data);
+        break;
+    case NID_pkcs7_enveloped:
+        p7->type = obj;
+        if ((p7->d.enveloped = PKCS7_ENVELOPE_new())
+            == NULL)
+            goto err;
+        if (!ASN1_INTEGER_set(p7->d.enveloped->version, 0))
+            goto err;
+        p7->d.enveloped->enc_data->content_type = OBJ_nid2obj(NID_pkcs7_data);
+        break;
+    case NID_pkcs7_encrypted:
+        p7->type = obj;
+        if ((p7->d.encrypted = PKCS7_ENCRYPT_new())
+            == NULL)
+            goto err;
+        if (!ASN1_INTEGER_set(p7->d.encrypted->version, 0))
+            goto err;
+        p7->d.encrypted->enc_data->content_type = OBJ_nid2obj(NID_pkcs7_data);
+        break;
+
+    case NID_pkcs7_digest:
+        p7->type = obj;
+        if ((p7->d.digest = PKCS7_DIGEST_new())
+            == NULL)
+            goto err;
+        if (!ASN1_INTEGER_set(p7->d.digest->version, 0))
+            goto err;
+        break;
+    default:
+        PKCS7err(PKCS7_F_PKCS7_SET_TYPE, PKCS7_R_UNSUPPORTED_CONTENT_TYPE);
+        goto err;
+    }
+    return (1);
+ err:
+    return (0);
+}
+
+int PKCS7_set0_type_other(PKCS7 *p7, int type, ASN1_TYPE *other)
+{
+    p7->type = OBJ_nid2obj(type);
+    p7->d.other = other;
+    return 1;
+}
+
+int PKCS7_add_signer(PKCS7 *p7, PKCS7_SIGNER_INFO *psi)
+{
+    int i, j, nid;
+    X509_ALGOR *alg;
+    STACK_OF(PKCS7_SIGNER_INFO) *signer_sk;
+    STACK_OF(X509_ALGOR) *md_sk;
+
+    i = OBJ_obj2nid(p7->type);
+    switch (i) {
+    case NID_pkcs7_signed:
+        signer_sk = p7->d.sign->signer_info;
+        md_sk = p7->d.sign->md_algs;
+        break;
+    case NID_pkcs7_signedAndEnveloped:
+        signer_sk = p7->d.signed_and_enveloped->signer_info;
+        md_sk = p7->d.signed_and_enveloped->md_algs;
+        break;
+    default:
+        PKCS7err(PKCS7_F_PKCS7_ADD_SIGNER, PKCS7_R_WRONG_CONTENT_TYPE);
+        return (0);
+    }
+
+    nid = OBJ_obj2nid(psi->digest_alg->algorithm);
+
+    /* If the digest is not currently listed, add it */
+    j = 0;
+    for (i = 0; i < sk_X509_ALGOR_num(md_sk); i++) {
+        alg = sk_X509_ALGOR_value(md_sk, i);
+        if (OBJ_obj2nid(alg->algorithm) == nid) {
+            j = 1;
+            break;
+        }
+    }
+    if (!j) {                   /* we need to add another algorithm */
+        if ((alg = X509_ALGOR_new()) == NULL
+            || (alg->parameter = ASN1_TYPE_new()) == NULL) {
+            X509_ALGOR_free(alg);
+            PKCS7err(PKCS7_F_PKCS7_ADD_SIGNER, ERR_R_MALLOC_FAILURE);
+            return (0);
+        }
+        alg->algorithm = OBJ_nid2obj(nid);
+        alg->parameter->type = V_ASN1_NULL;
+        if (!sk_X509_ALGOR_push(md_sk, alg)) {
+            X509_ALGOR_free(alg);
+            return 0;
+        }
+    }
+
+    if (!sk_PKCS7_SIGNER_INFO_push(signer_sk, psi))
+        return 0;
+    return (1);
+}
+
+int PKCS7_add_certificate(PKCS7 *p7, X509 *x509)
+{
+    int i;
+    STACK_OF(X509) **sk;
+
+    i = OBJ_obj2nid(p7->type);
+    switch (i) {
+    case NID_pkcs7_signed:
+        sk = &(p7->d.sign->cert);
+        break;
+    case NID_pkcs7_signedAndEnveloped:
+        sk = &(p7->d.signed_and_enveloped->cert);
+        break;
+    default:
+        PKCS7err(PKCS7_F_PKCS7_ADD_CERTIFICATE, PKCS7_R_WRONG_CONTENT_TYPE);
+        return (0);
+    }
+
+    if (*sk == NULL)
+        *sk = sk_X509_new_null();
+    if (*sk == NULL) {
+        PKCS7err(PKCS7_F_PKCS7_ADD_CERTIFICATE, ERR_R_MALLOC_FAILURE);
+        return 0;
+    }
+    X509_up_ref(x509);
+    if (!sk_X509_push(*sk, x509)) {
+        X509_free(x509);
+        return 0;
+    }
+    return (1);
+}
+
+int PKCS7_add_crl(PKCS7 *p7, X509_CRL *crl)
+{
+    int i;
+    STACK_OF(X509_CRL) **sk;
+
+    i = OBJ_obj2nid(p7->type);
+    switch (i) {
+    case NID_pkcs7_signed:
+        sk = &(p7->d.sign->crl);
+        break;
+    case NID_pkcs7_signedAndEnveloped:
+        sk = &(p7->d.signed_and_enveloped->crl);
+        break;
+    default:
+        PKCS7err(PKCS7_F_PKCS7_ADD_CRL, PKCS7_R_WRONG_CONTENT_TYPE);
+        return (0);
+    }
+
+    if (*sk == NULL)
+        *sk = sk_X509_CRL_new_null();
+    if (*sk == NULL) {
+        PKCS7err(PKCS7_F_PKCS7_ADD_CRL, ERR_R_MALLOC_FAILURE);
+        return 0;
+    }
+
+    X509_CRL_up_ref(crl);
+    if (!sk_X509_CRL_push(*sk, crl)) {
+        X509_CRL_free(crl);
+        return 0;
+    }
+    return (1);
+}
+
+int PKCS7_SIGNER_INFO_set(PKCS7_SIGNER_INFO *p7i, X509 *x509, EVP_PKEY *pkey,
+                          const EVP_MD *dgst)
+{
+    int ret;
+
+    /* We now need to add another PKCS7_SIGNER_INFO entry */
+    if (!ASN1_INTEGER_set(p7i->version, 1))
+        goto err;
+    if (!X509_NAME_set(&p7i->issuer_and_serial->issuer,
+                       X509_get_issuer_name(x509)))
+        goto err;
+
+    /*
+     * because ASN1_INTEGER_set is used to set a 'long' we will do things the
+     * ugly way.
+     */
+    ASN1_INTEGER_free(p7i->issuer_and_serial->serial);
+    if (!(p7i->issuer_and_serial->serial =
+          ASN1_INTEGER_dup(X509_get_serialNumber(x509))))
+        goto err;
+
+    /* lets keep the pkey around for a while */
+    EVP_PKEY_up_ref(pkey);
+    p7i->pkey = pkey;
+
+    /* Set the algorithms */
+
+    X509_ALGOR_set0(p7i->digest_alg, OBJ_nid2obj(EVP_MD_type(dgst)),
+                    V_ASN1_NULL, NULL);
+
+    if (pkey->ameth && pkey->ameth->pkey_ctrl) {
+        ret = pkey->ameth->pkey_ctrl(pkey, ASN1_PKEY_CTRL_PKCS7_SIGN, 0, p7i);
+        if (ret > 0)
+            return 1;
+        if (ret != -2) {
+            PKCS7err(PKCS7_F_PKCS7_SIGNER_INFO_SET,
+                     PKCS7_R_SIGNING_CTRL_FAILURE);
+            return 0;
+        }
+    }
+    PKCS7err(PKCS7_F_PKCS7_SIGNER_INFO_SET,
+             PKCS7_R_SIGNING_NOT_SUPPORTED_FOR_THIS_KEY_TYPE);
+ err:
+    return 0;
+}
+
+PKCS7_SIGNER_INFO *PKCS7_add_signature(PKCS7 *p7, X509 *x509, EVP_PKEY *pkey,
+                                       const EVP_MD *dgst)
+{
+    PKCS7_SIGNER_INFO *si = NULL;
+
+    if (dgst == NULL) {
+        int def_nid;
+        if (EVP_PKEY_get_default_digest_nid(pkey, &def_nid) <= 0)
+            goto err;
+        dgst = EVP_get_digestbynid(def_nid);
+        if (dgst == NULL) {
+            PKCS7err(PKCS7_F_PKCS7_ADD_SIGNATURE, PKCS7_R_NO_DEFAULT_DIGEST);
+            goto err;
+        }
+    }
+
+    if ((si = PKCS7_SIGNER_INFO_new()) == NULL)
+        goto err;
+    if (!PKCS7_SIGNER_INFO_set(si, x509, pkey, dgst))
+        goto err;
+    if (!PKCS7_add_signer(p7, si))
+        goto err;
+    return (si);
+ err:
+    PKCS7_SIGNER_INFO_free(si);
+    return (NULL);
+}
+
+int PKCS7_set_digest(PKCS7 *p7, const EVP_MD *md)
+{
+    if (PKCS7_type_is_digest(p7)) {
+        if ((p7->d.digest->md->parameter = ASN1_TYPE_new()) == NULL) {
+            PKCS7err(PKCS7_F_PKCS7_SET_DIGEST, ERR_R_MALLOC_FAILURE);
+            return 0;
+        }
+        p7->d.digest->md->parameter->type = V_ASN1_NULL;
+        p7->d.digest->md->algorithm = OBJ_nid2obj(EVP_MD_nid(md));
+        return 1;
+    }
+
+    PKCS7err(PKCS7_F_PKCS7_SET_DIGEST, PKCS7_R_WRONG_CONTENT_TYPE);
+    return 1;
+}
+
+STACK_OF(PKCS7_SIGNER_INFO) *PKCS7_get_signer_info(PKCS7 *p7)
+{
+    if (p7 == NULL || p7->d.ptr == NULL)
+        return NULL;
+    if (PKCS7_type_is_signed(p7)) {
+        return (p7->d.sign->signer_info);
+    } else if (PKCS7_type_is_signedAndEnveloped(p7)) {
+        return (p7->d.signed_and_enveloped->signer_info);
+    } else
+        return (NULL);
+}
+
+void PKCS7_SIGNER_INFO_get0_algs(PKCS7_SIGNER_INFO *si, EVP_PKEY **pk,
+                                 X509_ALGOR **pdig, X509_ALGOR **psig)
+{
+    if (pk)
+        *pk = si->pkey;
+    if (pdig)
+        *pdig = si->digest_alg;
+    if (psig)
+        *psig = si->digest_enc_alg;
+}
+
+void PKCS7_RECIP_INFO_get0_alg(PKCS7_RECIP_INFO *ri, X509_ALGOR **penc)
+{
+    if (penc)
+        *penc = ri->key_enc_algor;
+}
+
+PKCS7_RECIP_INFO *PKCS7_add_recipient(PKCS7 *p7, X509 *x509)
+{
+    PKCS7_RECIP_INFO *ri;
+
+    if ((ri = PKCS7_RECIP_INFO_new()) == NULL)
+        goto err;
+    if (!PKCS7_RECIP_INFO_set(ri, x509))
+        goto err;
+    if (!PKCS7_add_recipient_info(p7, ri))
+        goto err;
+    return ri;
+ err:
+    PKCS7_RECIP_INFO_free(ri);
+    return NULL;
+}
+
+int PKCS7_add_recipient_info(PKCS7 *p7, PKCS7_RECIP_INFO *ri)
+{
+    int i;
+    STACK_OF(PKCS7_RECIP_INFO) *sk;
+
+    i = OBJ_obj2nid(p7->type);
+    switch (i) {
+    case NID_pkcs7_signedAndEnveloped:
+        sk = p7->d.signed_and_enveloped->recipientinfo;
+        break;
+    case NID_pkcs7_enveloped:
+        sk = p7->d.enveloped->recipientinfo;
+        break;
+    default:
+        PKCS7err(PKCS7_F_PKCS7_ADD_RECIPIENT_INFO,
+                 PKCS7_R_WRONG_CONTENT_TYPE);
+        return (0);
+    }
+
+    if (!sk_PKCS7_RECIP_INFO_push(sk, ri))
+        return 0;
+    return (1);
+}
+
+int PKCS7_RECIP_INFO_set(PKCS7_RECIP_INFO *p7i, X509 *x509)
+{
+    int ret;
+    EVP_PKEY *pkey = NULL;
+    if (!ASN1_INTEGER_set(p7i->version, 0))
+        return 0;
+    if (!X509_NAME_set(&p7i->issuer_and_serial->issuer,
+                       X509_get_issuer_name(x509)))
+        return 0;
+
+    ASN1_INTEGER_free(p7i->issuer_and_serial->serial);
+    if (!(p7i->issuer_and_serial->serial =
+          ASN1_INTEGER_dup(X509_get_serialNumber(x509))))
+        return 0;
+
+    pkey = X509_get0_pubkey(x509);
+
+    if (!pkey || !pkey->ameth || !pkey->ameth->pkey_ctrl) {
+        PKCS7err(PKCS7_F_PKCS7_RECIP_INFO_SET,
+                 PKCS7_R_ENCRYPTION_NOT_SUPPORTED_FOR_THIS_KEY_TYPE);
+        goto err;
+    }
+
+    ret = pkey->ameth->pkey_ctrl(pkey, ASN1_PKEY_CTRL_PKCS7_ENCRYPT, 0, p7i);
+    if (ret == -2) {
+        PKCS7err(PKCS7_F_PKCS7_RECIP_INFO_SET,
+                 PKCS7_R_ENCRYPTION_NOT_SUPPORTED_FOR_THIS_KEY_TYPE);
+        goto err;
+    }
+    if (ret <= 0) {
+        PKCS7err(PKCS7_F_PKCS7_RECIP_INFO_SET,
+                 PKCS7_R_ENCRYPTION_CTRL_FAILURE);
+        goto err;
+    }
+
+    X509_up_ref(x509);
+    p7i->cert = x509;
+
+    return 1;
+
+ err:
+    return 0;
+}
+
+X509 *PKCS7_cert_from_signer_info(PKCS7 *p7, PKCS7_SIGNER_INFO *si)
+{
+    if (PKCS7_type_is_signed(p7))
+        return (X509_find_by_issuer_and_serial(p7->d.sign->cert,
+                                               si->issuer_and_serial->issuer,
+                                               si->
+                                               issuer_and_serial->serial));
+    else
+        return (NULL);
+}
+
+int PKCS7_set_cipher(PKCS7 *p7, const EVP_CIPHER *cipher)
+{
+    int i;
+    PKCS7_ENC_CONTENT *ec;
+
+    i = OBJ_obj2nid(p7->type);
+    switch (i) {
+    case NID_pkcs7_signedAndEnveloped:
+        ec = p7->d.signed_and_enveloped->enc_data;
+        break;
+    case NID_pkcs7_enveloped:
+        ec = p7->d.enveloped->enc_data;
+        break;
+    default:
+        PKCS7err(PKCS7_F_PKCS7_SET_CIPHER, PKCS7_R_WRONG_CONTENT_TYPE);
+        return (0);
+    }
+
+    /* Check cipher OID exists and has data in it */
+    i = EVP_CIPHER_type(cipher);
+    if (i == NID_undef) {
+        PKCS7err(PKCS7_F_PKCS7_SET_CIPHER,
+                 PKCS7_R_CIPHER_HAS_NO_OBJECT_IDENTIFIER);
+        return (0);
+    }
+
+    ec->cipher = cipher;
+    return 1;
+}
+
+int PKCS7_stream(unsigned char ***boundary, PKCS7 *p7)
+{
+    ASN1_OCTET_STRING *os = NULL;
+
+    switch (OBJ_obj2nid(p7->type)) {
+    case NID_pkcs7_data:
+        os = p7->d.data;
+        break;
+
+    case NID_pkcs7_signedAndEnveloped:
+        os = p7->d.signed_and_enveloped->enc_data->enc_data;
+        if (os == NULL) {
+            os = ASN1_OCTET_STRING_new();
+            p7->d.signed_and_enveloped->enc_data->enc_data = os;
+        }
+        break;
+
+    case NID_pkcs7_enveloped:
+        os = p7->d.enveloped->enc_data->enc_data;
+        if (os == NULL) {
+            os = ASN1_OCTET_STRING_new();
+            p7->d.enveloped->enc_data->enc_data = os;
+        }
+        break;
+
+    case NID_pkcs7_signed:
+        os = p7->d.sign->contents->d.data;
+        break;
+
+    default:
+        os = NULL;
+        break;
+    }
+
+    if (os == NULL)
+        return 0;
+
+    os->flags |= ASN1_STRING_FLAG_NDEF;
+    *boundary = &os->data;
+
+    return 1;
+}
diff --git a/crypto/pkcs7/pk7_mime.c b/crypto/pkcs7/pk7_mime.c
new file mode 100644
index 000000000..97474cf51
--- /dev/null
+++ b/crypto/pkcs7/pk7_mime.c
@@ -0,0 +1,49 @@
+/*
+ * Copyright 1999-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+
+/* PKCS#7 wrappers round generalised stream and MIME routines */
+
+int i2d_PKCS7_bio_stream(BIO *out, PKCS7 *p7, BIO *in, int flags)
+{
+    return i2d_ASN1_bio_stream(out, (ASN1_VALUE *)p7, in, flags,
+                               ASN1_ITEM_rptr(PKCS7));
+}
+
+int PEM_write_bio_PKCS7_stream(BIO *out, PKCS7 *p7, BIO *in, int flags)
+{
+    return PEM_write_bio_ASN1_stream(out, (ASN1_VALUE *)p7, in, flags,
+                                     "PKCS7", ASN1_ITEM_rptr(PKCS7));
+}
+
+int SMIME_write_PKCS7(BIO *bio, PKCS7 *p7, BIO *data, int flags)
+{
+    STACK_OF(X509_ALGOR) *mdalgs;
+    int ctype_nid = OBJ_obj2nid(p7->type);
+    if (ctype_nid == NID_pkcs7_signed)
+        mdalgs = p7->d.sign->md_algs;
+    else
+        mdalgs = NULL;
+
+    flags ^= SMIME_OLDMIME;
+
+    return SMIME_write_ASN1(bio, (ASN1_VALUE *)p7, data, flags,
+                            ctype_nid, NID_undef, mdalgs,
+                            ASN1_ITEM_rptr(PKCS7));
+}
+
+PKCS7 *SMIME_read_PKCS7(BIO *bio, BIO **bcont)
+{
+    return (PKCS7 *)SMIME_read_ASN1(bio, bcont, ASN1_ITEM_rptr(PKCS7));
+}
diff --git a/crypto/pkcs7/pk7_smime.c b/crypto/pkcs7/pk7_smime.c
new file mode 100644
index 000000000..90f88f023
--- /dev/null
+++ b/crypto/pkcs7/pk7_smime.c
@@ -0,0 +1,600 @@
+/* ====================================================================
+ * Copyright (c) 2016 - 2019 The GmSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the GmSSL Project.
+ *    (http://gmssl.org/)"
+ *
+ * 4. The name "GmSSL Project" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    guanzhi1980@gmail.com.
+ *
+ * 5. Products derived from this software may not be called "GmSSL"
+ *    nor may "GmSSL" appear in their names without prior written
+ *    permission of the GmSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the GmSSL Project
+ *    (http://gmssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE GmSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE GmSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+/*
+ * Copyright 1999-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/* Simple PKCS#7 processing functions */
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+
+
+#define BUFFERSIZE 4096
+
+static int pkcs7_copy_existing_digest(PKCS7 *p7, PKCS7_SIGNER_INFO *si);
+
+PKCS7 *PKCS7_sign(X509 *signcert, EVP_PKEY *pkey, STACK_OF(X509) *certs,
+                  BIO *data, int flags)
+{
+    PKCS7 *p7;
+    int i;
+
+    if ((p7 = PKCS7_new()) == NULL) {
+        PKCS7err(PKCS7_F_PKCS7_SIGN, ERR_R_MALLOC_FAILURE);
+        return NULL;
+    }
+
+    if (!PKCS7_set_type(p7, NID_pkcs7_signed))
+        goto err;
+
+    if (!PKCS7_content_new(p7, NID_pkcs7_data))
+        goto err;
+
+    if (pkey && !PKCS7_sign_add_signer(p7, signcert, pkey, NULL, flags)) {
+        PKCS7err(PKCS7_F_PKCS7_SIGN, PKCS7_R_PKCS7_ADD_SIGNER_ERROR);
+        goto err;
+    }
+
+    if (!(flags & PKCS7_NOCERTS)) {
+        for (i = 0; i < sk_X509_num(certs); i++) {
+            if (!PKCS7_add_certificate(p7, sk_X509_value(certs, i)))
+                goto err;
+        }
+    }
+
+    if (flags & PKCS7_DETACHED)
+        PKCS7_set_detached(p7, 1);
+
+    if (flags & (PKCS7_STREAM | PKCS7_PARTIAL))
+        return p7;
+
+    if (PKCS7_final(p7, data, flags))
+        return p7;
+
+ err:
+    PKCS7_free(p7);
+    return NULL;
+}
+
+int PKCS7_final(PKCS7 *p7, BIO *data, int flags)
+{
+    BIO *p7bio;
+    int ret = 0;
+
+    if ((p7bio = PKCS7_dataInit(p7, NULL)) == NULL) {
+        PKCS7err(PKCS7_F_PKCS7_FINAL, ERR_R_MALLOC_FAILURE);
+        return 0;
+    }
+
+    SMIME_crlf_copy(data, p7bio, flags);
+
+    (void)BIO_flush(p7bio);
+
+    if (!PKCS7_dataFinal(p7, p7bio)) {
+        PKCS7err(PKCS7_F_PKCS7_FINAL, PKCS7_R_PKCS7_DATASIGN);
+        goto err;
+    }
+
+    ret = 1;
+
+ err:
+    BIO_free_all(p7bio);
+
+    return ret;
+
+}
+
+/* Check to see if a cipher exists and if so add S/MIME capabilities */
+
+static int add_cipher_smcap(STACK_OF(X509_ALGOR) *sk, int nid, int arg)
+{
+    if (EVP_get_cipherbynid(nid))
+        return PKCS7_simple_smimecap(sk, nid, arg);
+    return 1;
+}
+
+static int add_digest_smcap(STACK_OF(X509_ALGOR) *sk, int nid, int arg)
+{
+    if (EVP_get_digestbynid(nid))
+        return PKCS7_simple_smimecap(sk, nid, arg);
+    return 1;
+}
+
+PKCS7_SIGNER_INFO *PKCS7_sign_add_signer(PKCS7 *p7, X509 *signcert,
+                                         EVP_PKEY *pkey, const EVP_MD *md,
+                                         int flags)
+{
+    PKCS7_SIGNER_INFO *si = NULL;
+    STACK_OF(X509_ALGOR) *smcap = NULL;
+    if (!X509_check_private_key(signcert, pkey)) {
+        PKCS7err(PKCS7_F_PKCS7_SIGN_ADD_SIGNER,
+                 PKCS7_R_PRIVATE_KEY_DOES_NOT_MATCH_CERTIFICATE);
+        return NULL;
+    }
+
+    if ((si = PKCS7_add_signature(p7, signcert, pkey, md)) == NULL) {
+        PKCS7err(PKCS7_F_PKCS7_SIGN_ADD_SIGNER,
+                 PKCS7_R_PKCS7_ADD_SIGNATURE_ERROR);
+        return NULL;
+    }
+
+    if (!(flags & PKCS7_NOCERTS)) {
+        if (!PKCS7_add_certificate(p7, signcert))
+            goto err;
+    }
+
+    if (!(flags & PKCS7_NOATTR)) {
+        if (!PKCS7_add_attrib_content_type(si, NULL))
+            goto err;
+        /* Add SMIMECapabilities */
+        if (!(flags & PKCS7_NOSMIMECAP)) {
+            if ((smcap = sk_X509_ALGOR_new_null()) == NULL) {
+                PKCS7err(PKCS7_F_PKCS7_SIGN_ADD_SIGNER, ERR_R_MALLOC_FAILURE);
+                goto err;
+            }
+            if (!add_cipher_smcap(smcap, NID_aes_256_cbc, -1)
+                || !add_digest_smcap(smcap, NID_id_GostR3411_2012_256, -1)
+                || !add_digest_smcap(smcap, NID_id_GostR3411_2012_512, -1)
+                || !add_digest_smcap(smcap, NID_id_GostR3411_94, -1)
+                || !add_cipher_smcap(smcap, NID_id_Gost28147_89, -1)
+                || !add_cipher_smcap(smcap, NID_aes_192_cbc, -1)
+                || !add_cipher_smcap(smcap, NID_aes_128_cbc, -1)
+                || !add_cipher_smcap(smcap, NID_des_ede3_cbc, -1)
+#ifndef OPENSSL_NO_SMS4
+                || !add_cipher_smcap(smcap, NID_sms4_cbc, -1)
+#endif
+                || !add_cipher_smcap(smcap, NID_rc2_cbc, 128)
+                || !add_cipher_smcap(smcap, NID_rc2_cbc, 64)
+                || !add_cipher_smcap(smcap, NID_des_cbc, -1)
+                || !add_cipher_smcap(smcap, NID_rc2_cbc, 40)
+                || !PKCS7_add_attrib_smimecap(si, smcap))
+                goto err;
+            sk_X509_ALGOR_pop_free(smcap, X509_ALGOR_free);
+            smcap = NULL;
+        }
+        if (flags & PKCS7_REUSE_DIGEST) {
+            if (!pkcs7_copy_existing_digest(p7, si))
+                goto err;
+            if (!(flags & PKCS7_PARTIAL) && !PKCS7_SIGNER_INFO_sign(si))
+                goto err;
+        }
+    }
+    return si;
+ err:
+    sk_X509_ALGOR_pop_free(smcap, X509_ALGOR_free);
+    return NULL;
+}
+
+/*
+ * Search for a digest matching SignerInfo digest type and if found copy
+ * across.
+ */
+
+static int pkcs7_copy_existing_digest(PKCS7 *p7, PKCS7_SIGNER_INFO *si)
+{
+    int i;
+    STACK_OF(PKCS7_SIGNER_INFO) *sinfos;
+    PKCS7_SIGNER_INFO *sitmp;
+    ASN1_OCTET_STRING *osdig = NULL;
+    sinfos = PKCS7_get_signer_info(p7);
+    for (i = 0; i < sk_PKCS7_SIGNER_INFO_num(sinfos); i++) {
+        sitmp = sk_PKCS7_SIGNER_INFO_value(sinfos, i);
+        if (si == sitmp)
+            break;
+        if (sk_X509_ATTRIBUTE_num(sitmp->auth_attr) <= 0)
+            continue;
+        if (!OBJ_cmp(si->digest_alg->algorithm, sitmp->digest_alg->algorithm)) {
+            osdig = PKCS7_digest_from_attributes(sitmp->auth_attr);
+            break;
+        }
+
+    }
+
+    if (osdig)
+        return PKCS7_add1_attrib_digest(si, osdig->data, osdig->length);
+
+    PKCS7err(PKCS7_F_PKCS7_COPY_EXISTING_DIGEST,
+             PKCS7_R_NO_MATCHING_DIGEST_TYPE_FOUND);
+    return 0;
+}
+
+int PKCS7_verify(PKCS7 *p7, STACK_OF(X509) *certs, X509_STORE *store,
+                 BIO *indata, BIO *out, int flags)
+{
+    STACK_OF(X509) *signers;
+    X509 *signer;
+    STACK_OF(PKCS7_SIGNER_INFO) *sinfos;
+    PKCS7_SIGNER_INFO *si;
+    X509_STORE_CTX *cert_ctx = NULL;
+    char *buf = NULL;
+    int i, j = 0, k, ret = 0;
+    BIO *p7bio = NULL;
+    BIO *tmpin = NULL, *tmpout = NULL;
+
+    if (!p7) {
+        PKCS7err(PKCS7_F_PKCS7_VERIFY, PKCS7_R_INVALID_NULL_POINTER);
+        return 0;
+    }
+
+    if (!PKCS7_type_is_signed(p7)) {
+        PKCS7err(PKCS7_F_PKCS7_VERIFY, PKCS7_R_WRONG_CONTENT_TYPE);
+        return 0;
+    }
+
+    /* Check for no data and no content: no data to verify signature */
+    if (PKCS7_get_detached(p7) && !indata) {
+        PKCS7err(PKCS7_F_PKCS7_VERIFY, PKCS7_R_NO_CONTENT);
+        return 0;
+    }
+
+    if (flags & PKCS7_NO_DUAL_CONTENT) {
+        /*
+         * This was originally "#if 0" because we thought that only old broken
+         * Netscape did this.  It turns out that Authenticode uses this kind
+         * of "extended" PKCS7 format, and things like UEFI secure boot and
+         * tools like osslsigncode need it.  In Authenticode the verification
+         * process is different, but the existing PKCs7 verification works.
+         */
+        if (!PKCS7_get_detached(p7) && indata) {
+            PKCS7err(PKCS7_F_PKCS7_VERIFY, PKCS7_R_CONTENT_AND_DATA_PRESENT);
+            return 0;
+        }
+    }
+
+    sinfos = PKCS7_get_signer_info(p7);
+
+    if (!sinfos || !sk_PKCS7_SIGNER_INFO_num(sinfos)) {
+        PKCS7err(PKCS7_F_PKCS7_VERIFY, PKCS7_R_NO_SIGNATURES_ON_DATA);
+        return 0;
+    }
+
+    signers = PKCS7_get0_signers(p7, certs, flags);
+    if (!signers)
+        return 0;
+
+    /* Now verify the certificates */
+
+    cert_ctx = X509_STORE_CTX_new();
+    if (cert_ctx == NULL)
+        goto err;
+    if (!(flags & PKCS7_NOVERIFY))
+        for (k = 0; k < sk_X509_num(signers); k++) {
+            signer = sk_X509_value(signers, k);
+            if (!(flags & PKCS7_NOCHAIN)) {
+                if (!X509_STORE_CTX_init(cert_ctx, store, signer,
+                                         p7->d.sign->cert)) {
+                    PKCS7err(PKCS7_F_PKCS7_VERIFY, ERR_R_X509_LIB);
+                    goto err;
+                }
+                X509_STORE_CTX_set_default(cert_ctx, "smime_sign");
+            } else if (!X509_STORE_CTX_init(cert_ctx, store, signer, NULL)) {
+                PKCS7err(PKCS7_F_PKCS7_VERIFY, ERR_R_X509_LIB);
+                goto err;
+            }
+            if (!(flags & PKCS7_NOCRL))
+                X509_STORE_CTX_set0_crls(cert_ctx, p7->d.sign->crl);
+            i = X509_verify_cert(cert_ctx);
+            if (i <= 0)
+                j = X509_STORE_CTX_get_error(cert_ctx);
+            X509_STORE_CTX_cleanup(cert_ctx);
+            if (i <= 0) {
+                PKCS7err(PKCS7_F_PKCS7_VERIFY,
+                         PKCS7_R_CERTIFICATE_VERIFY_ERROR);
+                ERR_add_error_data(2, "Verify error:",
+                                   X509_verify_cert_error_string(j));
+                goto err;
+            }
+            /* Check for revocation status here */
+        }
+
+    /*
+     * Performance optimization: if the content is a memory BIO then store
+     * its contents in a temporary read only memory BIO. This avoids
+     * potentially large numbers of slow copies of data which will occur when
+     * reading from a read write memory BIO when signatures are calculated.
+     */
+
+    if (indata && (BIO_method_type(indata) == BIO_TYPE_MEM)) {
+        char *ptr;
+        long len;
+        len = BIO_get_mem_data(indata, &ptr);
+        tmpin = BIO_new_mem_buf(ptr, len);
+        if (tmpin == NULL) {
+            PKCS7err(PKCS7_F_PKCS7_VERIFY, ERR_R_MALLOC_FAILURE);
+            goto err;
+        }
+    } else
+        tmpin = indata;
+
+    if ((p7bio = PKCS7_dataInit(p7, tmpin)) == NULL)
+        goto err;
+
+    if (flags & PKCS7_TEXT) {
+        if ((tmpout = BIO_new(BIO_s_mem())) == NULL) {
+            PKCS7err(PKCS7_F_PKCS7_VERIFY, ERR_R_MALLOC_FAILURE);
+            goto err;
+        }
+        BIO_set_mem_eof_return(tmpout, 0);
+    } else
+        tmpout = out;
+
+    /* We now have to 'read' from p7bio to calculate digests etc. */
+    if ((buf = OPENSSL_malloc(BUFFERSIZE)) == NULL) {
+        PKCS7err(PKCS7_F_PKCS7_VERIFY, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+    for (;;) {
+        i = BIO_read(p7bio, buf, BUFFERSIZE);
+        if (i <= 0)
+            break;
+        if (tmpout)
+            BIO_write(tmpout, buf, i);
+    }
+
+    if (flags & PKCS7_TEXT) {
+        if (!SMIME_text(tmpout, out)) {
+            PKCS7err(PKCS7_F_PKCS7_VERIFY, PKCS7_R_SMIME_TEXT_ERROR);
+            BIO_free(tmpout);
+            goto err;
+        }
+        BIO_free(tmpout);
+    }
+
+    /* Now Verify All Signatures */
+    if (!(flags & PKCS7_NOSIGS))
+        for (i = 0; i < sk_PKCS7_SIGNER_INFO_num(sinfos); i++) {
+            si = sk_PKCS7_SIGNER_INFO_value(sinfos, i);
+            signer = sk_X509_value(signers, i);
+            j = PKCS7_signatureVerify(p7bio, p7, si, signer);
+            if (j <= 0) {
+                PKCS7err(PKCS7_F_PKCS7_VERIFY, PKCS7_R_SIGNATURE_FAILURE);
+                goto err;
+            }
+        }
+
+    ret = 1;
+
+ err:
+    X509_STORE_CTX_free(cert_ctx);
+    OPENSSL_free(buf);
+    if (tmpin == indata) {
+        if (indata)
+            BIO_pop(p7bio);
+    }
+    BIO_free_all(p7bio);
+    sk_X509_free(signers);
+    return ret;
+}
+
+STACK_OF(X509) *PKCS7_get0_signers(PKCS7 *p7, STACK_OF(X509) *certs,
+                                   int flags)
+{
+    STACK_OF(X509) *signers;
+    STACK_OF(PKCS7_SIGNER_INFO) *sinfos;
+    PKCS7_SIGNER_INFO *si;
+    PKCS7_ISSUER_AND_SERIAL *ias;
+    X509 *signer;
+    int i;
+
+    if (!p7) {
+        PKCS7err(PKCS7_F_PKCS7_GET0_SIGNERS, PKCS7_R_INVALID_NULL_POINTER);
+        return NULL;
+    }
+
+    if (!PKCS7_type_is_signed(p7)) {
+        PKCS7err(PKCS7_F_PKCS7_GET0_SIGNERS, PKCS7_R_WRONG_CONTENT_TYPE);
+        return NULL;
+    }
+
+    /* Collect all the signers together */
+
+    sinfos = PKCS7_get_signer_info(p7);
+
+    if (sk_PKCS7_SIGNER_INFO_num(sinfos) <= 0) {
+        PKCS7err(PKCS7_F_PKCS7_GET0_SIGNERS, PKCS7_R_NO_SIGNERS);
+        return 0;
+    }
+
+    if ((signers = sk_X509_new_null()) == NULL) {
+        PKCS7err(PKCS7_F_PKCS7_GET0_SIGNERS, ERR_R_MALLOC_FAILURE);
+        return NULL;
+    }
+
+    for (i = 0; i < sk_PKCS7_SIGNER_INFO_num(sinfos); i++) {
+        si = sk_PKCS7_SIGNER_INFO_value(sinfos, i);
+        ias = si->issuer_and_serial;
+        signer = NULL;
+        /* If any certificates passed they take priority */
+        if (certs)
+            signer = X509_find_by_issuer_and_serial(certs,
+                                                    ias->issuer, ias->serial);
+        if (!signer && !(flags & PKCS7_NOINTERN)
+            && p7->d.sign->cert)
+            signer =
+                X509_find_by_issuer_and_serial(p7->d.sign->cert,
+                                               ias->issuer, ias->serial);
+        if (!signer) {
+            PKCS7err(PKCS7_F_PKCS7_GET0_SIGNERS,
+                     PKCS7_R_SIGNER_CERTIFICATE_NOT_FOUND);
+            sk_X509_free(signers);
+            return 0;
+        }
+
+        if (!sk_X509_push(signers, signer)) {
+            sk_X509_free(signers);
+            return NULL;
+        }
+    }
+    return signers;
+}
+
+/* Build a complete PKCS#7 enveloped data */
+
+PKCS7 *PKCS7_encrypt(STACK_OF(X509) *certs, BIO *in, const EVP_CIPHER *cipher,
+                     int flags)
+{
+    PKCS7 *p7;
+    BIO *p7bio = NULL;
+    int i;
+    X509 *x509;
+    if ((p7 = PKCS7_new()) == NULL) {
+        PKCS7err(PKCS7_F_PKCS7_ENCRYPT, ERR_R_MALLOC_FAILURE);
+        return NULL;
+    }
+
+    if (!PKCS7_set_type(p7, NID_pkcs7_enveloped))
+        goto err;
+    if (!PKCS7_set_cipher(p7, cipher)) {
+        PKCS7err(PKCS7_F_PKCS7_ENCRYPT, PKCS7_R_ERROR_SETTING_CIPHER);
+        goto err;
+    }
+
+    for (i = 0; i < sk_X509_num(certs); i++) {
+        x509 = sk_X509_value(certs, i);
+        if (!PKCS7_add_recipient(p7, x509)) {
+            PKCS7err(PKCS7_F_PKCS7_ENCRYPT, PKCS7_R_ERROR_ADDING_RECIPIENT);
+            goto err;
+        }
+    }
+
+    if (flags & PKCS7_STREAM)
+        return p7;
+
+    if (PKCS7_final(p7, in, flags))
+        return p7;
+
+ err:
+
+    BIO_free_all(p7bio);
+    PKCS7_free(p7);
+    return NULL;
+
+}
+
+int PKCS7_decrypt(PKCS7 *p7, EVP_PKEY *pkey, X509 *cert, BIO *data, int flags)
+{
+    BIO *tmpmem;
+    int ret = 0, i;
+    char *buf = NULL;
+
+    if (!p7) {
+        PKCS7err(PKCS7_F_PKCS7_DECRYPT, PKCS7_R_INVALID_NULL_POINTER);
+        return 0;
+    }
+
+    if (!PKCS7_type_is_enveloped(p7)) {
+        PKCS7err(PKCS7_F_PKCS7_DECRYPT, PKCS7_R_WRONG_CONTENT_TYPE);
+        return 0;
+    }
+
+    if (cert && !X509_check_private_key(cert, pkey)) {
+        PKCS7err(PKCS7_F_PKCS7_DECRYPT,
+                 PKCS7_R_PRIVATE_KEY_DOES_NOT_MATCH_CERTIFICATE);
+        return 0;
+    }
+
+    if ((tmpmem = PKCS7_dataDecode(p7, pkey, NULL, cert)) == NULL) {
+        PKCS7err(PKCS7_F_PKCS7_DECRYPT, PKCS7_R_DECRYPT_ERROR);
+        return 0;
+    }
+
+    if (flags & PKCS7_TEXT) {
+        BIO *tmpbuf, *bread;
+        /* Encrypt BIOs can't do BIO_gets() so add a buffer BIO */
+        if ((tmpbuf = BIO_new(BIO_f_buffer())) == NULL) {
+            PKCS7err(PKCS7_F_PKCS7_DECRYPT, ERR_R_MALLOC_FAILURE);
+            BIO_free_all(tmpmem);
+            return 0;
+        }
+        if ((bread = BIO_push(tmpbuf, tmpmem)) == NULL) {
+            PKCS7err(PKCS7_F_PKCS7_DECRYPT, ERR_R_MALLOC_FAILURE);
+            BIO_free_all(tmpbuf);
+            BIO_free_all(tmpmem);
+            return 0;
+        }
+        ret = SMIME_text(bread, data);
+        if (ret > 0 && BIO_method_type(tmpmem) == BIO_TYPE_CIPHER) {
+            if (!BIO_get_cipher_status(tmpmem))
+                ret = 0;
+        }
+        BIO_free_all(bread);
+        return ret;
+    }
+    if ((buf = OPENSSL_malloc(BUFFERSIZE)) == NULL) {
+        PKCS7err(PKCS7_F_PKCS7_DECRYPT, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+    for (;;) {
+        i = BIO_read(tmpmem, buf, BUFFERSIZE);
+        if (i <= 0) {
+            ret = 1;
+            if (BIO_method_type(tmpmem) == BIO_TYPE_CIPHER) {
+                if (!BIO_get_cipher_status(tmpmem))
+                    ret = 0;
+            }
+
+            break;
+        }
+        if (BIO_write(data, buf, i) != i) {
+            break;
+        }
+    }
+err:
+    OPENSSL_free(buf);
+    BIO_free_all(tmpmem);
+    return ret;
+}
diff --git a/crypto/pkcs7/pkcs7err.c b/crypto/pkcs7/pkcs7err.c
new file mode 100644
index 000000000..d5baa9b83
--- /dev/null
+++ b/crypto/pkcs7/pkcs7err.c
@@ -0,0 +1,131 @@
+/*
+ * Generated by util/mkerr.pl DO NOT EDIT
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include 
+
+/* BEGIN ERROR CODES */
+#ifndef OPENSSL_NO_ERR
+
+# define ERR_FUNC(func) ERR_PACK(ERR_LIB_PKCS7,func,0)
+# define ERR_REASON(reason) ERR_PACK(ERR_LIB_PKCS7,0,reason)
+
+static ERR_STRING_DATA PKCS7_str_functs[] = {
+    {ERR_FUNC(PKCS7_F_DO_PKCS7_SIGNED_ATTRIB), "do_pkcs7_signed_attrib"},
+    {ERR_FUNC(PKCS7_F_PKCS7_ADD0_ATTRIB_SIGNING_TIME),
+     "PKCS7_add0_attrib_signing_time"},
+    {ERR_FUNC(PKCS7_F_PKCS7_ADD_ATTRIB_SMIMECAP),
+     "PKCS7_add_attrib_smimecap"},
+    {ERR_FUNC(PKCS7_F_PKCS7_ADD_CERTIFICATE), "PKCS7_add_certificate"},
+    {ERR_FUNC(PKCS7_F_PKCS7_ADD_CRL), "PKCS7_add_crl"},
+    {ERR_FUNC(PKCS7_F_PKCS7_ADD_RECIPIENT_INFO), "PKCS7_add_recipient_info"},
+    {ERR_FUNC(PKCS7_F_PKCS7_ADD_SIGNATURE), "PKCS7_add_signature"},
+    {ERR_FUNC(PKCS7_F_PKCS7_ADD_SIGNER), "PKCS7_add_signer"},
+    {ERR_FUNC(PKCS7_F_PKCS7_BIO_ADD_DIGEST), "PKCS7_bio_add_digest"},
+    {ERR_FUNC(PKCS7_F_PKCS7_COPY_EXISTING_DIGEST),
+     "pkcs7_copy_existing_digest"},
+    {ERR_FUNC(PKCS7_F_PKCS7_CTRL), "PKCS7_ctrl"},
+    {ERR_FUNC(PKCS7_F_PKCS7_DATADECODE), "PKCS7_dataDecode"},
+    {ERR_FUNC(PKCS7_F_PKCS7_DATAFINAL), "PKCS7_dataFinal"},
+    {ERR_FUNC(PKCS7_F_PKCS7_DATAINIT), "PKCS7_dataInit"},
+    {ERR_FUNC(PKCS7_F_PKCS7_DATAVERIFY), "PKCS7_dataVerify"},
+    {ERR_FUNC(PKCS7_F_PKCS7_DECRYPT), "PKCS7_decrypt"},
+    {ERR_FUNC(PKCS7_F_PKCS7_DECRYPT_RINFO), "pkcs7_decrypt_rinfo"},
+    {ERR_FUNC(PKCS7_F_PKCS7_ENCODE_RINFO), "pkcs7_encode_rinfo"},
+    {ERR_FUNC(PKCS7_F_PKCS7_ENCRYPT), "PKCS7_encrypt"},
+    {ERR_FUNC(PKCS7_F_PKCS7_FINAL), "PKCS7_final"},
+    {ERR_FUNC(PKCS7_F_PKCS7_FIND_DIGEST), "PKCS7_find_digest"},
+    {ERR_FUNC(PKCS7_F_PKCS7_GET0_SIGNERS), "PKCS7_get0_signers"},
+    {ERR_FUNC(PKCS7_F_PKCS7_RECIP_INFO_SET), "PKCS7_RECIP_INFO_set"},
+    {ERR_FUNC(PKCS7_F_PKCS7_SET_CIPHER), "PKCS7_set_cipher"},
+    {ERR_FUNC(PKCS7_F_PKCS7_SET_CONTENT), "PKCS7_set_content"},
+    {ERR_FUNC(PKCS7_F_PKCS7_SET_DIGEST), "PKCS7_set_digest"},
+    {ERR_FUNC(PKCS7_F_PKCS7_SET_TYPE), "PKCS7_set_type"},
+    {ERR_FUNC(PKCS7_F_PKCS7_SIGN), "PKCS7_sign"},
+    {ERR_FUNC(PKCS7_F_PKCS7_SIGNATUREVERIFY), "PKCS7_signatureVerify"},
+    {ERR_FUNC(PKCS7_F_PKCS7_SIGNER_INFO_SET), "PKCS7_SIGNER_INFO_set"},
+    {ERR_FUNC(PKCS7_F_PKCS7_SIGNER_INFO_SIGN), "PKCS7_SIGNER_INFO_sign"},
+    {ERR_FUNC(PKCS7_F_PKCS7_SIGN_ADD_SIGNER), "PKCS7_sign_add_signer"},
+    {ERR_FUNC(PKCS7_F_PKCS7_SIMPLE_SMIMECAP), "PKCS7_simple_smimecap"},
+    {ERR_FUNC(PKCS7_F_PKCS7_VERIFY), "PKCS7_verify"},
+    {0, NULL}
+};
+
+static ERR_STRING_DATA PKCS7_str_reasons[] = {
+    {ERR_REASON(PKCS7_R_CERTIFICATE_VERIFY_ERROR),
+     "certificate verify error"},
+    {ERR_REASON(PKCS7_R_CIPHER_HAS_NO_OBJECT_IDENTIFIER),
+     "cipher has no object identifier"},
+    {ERR_REASON(PKCS7_R_CIPHER_NOT_INITIALIZED), "cipher not initialized"},
+    {ERR_REASON(PKCS7_R_CONTENT_AND_DATA_PRESENT),
+     "content and data present"},
+    {ERR_REASON(PKCS7_R_CTRL_ERROR), "ctrl error"},
+    {ERR_REASON(PKCS7_R_DECRYPT_ERROR), "decrypt error"},
+    {ERR_REASON(PKCS7_R_DIGEST_FAILURE), "digest failure"},
+    {ERR_REASON(PKCS7_R_ENCRYPTION_CTRL_FAILURE), "encryption ctrl failure"},
+    {ERR_REASON(PKCS7_R_ENCRYPTION_NOT_SUPPORTED_FOR_THIS_KEY_TYPE),
+     "encryption not supported for this key type"},
+    {ERR_REASON(PKCS7_R_ERROR_ADDING_RECIPIENT), "error adding recipient"},
+    {ERR_REASON(PKCS7_R_ERROR_SETTING_CIPHER), "error setting cipher"},
+    {ERR_REASON(PKCS7_R_INVALID_NULL_POINTER), "invalid null pointer"},
+    {ERR_REASON(PKCS7_R_INVALID_SIGNED_DATA_TYPE),
+     "invalid signed data type"},
+    {ERR_REASON(PKCS7_R_NO_CONTENT), "no content"},
+    {ERR_REASON(PKCS7_R_NO_DEFAULT_DIGEST), "no default digest"},
+    {ERR_REASON(PKCS7_R_NO_MATCHING_DIGEST_TYPE_FOUND),
+     "no matching digest type found"},
+    {ERR_REASON(PKCS7_R_NO_RECIPIENT_MATCHES_CERTIFICATE),
+     "no recipient matches certificate"},
+    {ERR_REASON(PKCS7_R_NO_SIGNATURES_ON_DATA), "no signatures on data"},
+    {ERR_REASON(PKCS7_R_NO_SIGNERS), "no signers"},
+    {ERR_REASON(PKCS7_R_OPERATION_NOT_SUPPORTED_ON_THIS_TYPE),
+     "operation not supported on this type"},
+    {ERR_REASON(PKCS7_R_PKCS7_ADD_SIGNATURE_ERROR),
+     "pkcs7 add signature error"},
+    {ERR_REASON(PKCS7_R_PKCS7_ADD_SIGNER_ERROR), "pkcs7 add signer error"},
+    {ERR_REASON(PKCS7_R_PKCS7_DATASIGN), "pkcs7 datasign"},
+    {ERR_REASON(PKCS7_R_PRIVATE_KEY_DOES_NOT_MATCH_CERTIFICATE),
+     "private key does not match certificate"},
+    {ERR_REASON(PKCS7_R_SIGNATURE_FAILURE), "signature failure"},
+    {ERR_REASON(PKCS7_R_SIGNER_CERTIFICATE_NOT_FOUND),
+     "signer certificate not found"},
+    {ERR_REASON(PKCS7_R_SIGNING_CTRL_FAILURE), "signing ctrl failure"},
+    {ERR_REASON(PKCS7_R_SIGNING_NOT_SUPPORTED_FOR_THIS_KEY_TYPE),
+     "signing not supported for this key type"},
+    {ERR_REASON(PKCS7_R_SMIME_TEXT_ERROR), "smime text error"},
+    {ERR_REASON(PKCS7_R_UNABLE_TO_FIND_CERTIFICATE),
+     "unable to find certificate"},
+    {ERR_REASON(PKCS7_R_UNABLE_TO_FIND_MEM_BIO), "unable to find mem bio"},
+    {ERR_REASON(PKCS7_R_UNABLE_TO_FIND_MESSAGE_DIGEST),
+     "unable to find message digest"},
+    {ERR_REASON(PKCS7_R_UNKNOWN_DIGEST_TYPE), "unknown digest type"},
+    {ERR_REASON(PKCS7_R_UNKNOWN_OPERATION), "unknown operation"},
+    {ERR_REASON(PKCS7_R_UNSUPPORTED_CIPHER_TYPE), "unsupported cipher type"},
+    {ERR_REASON(PKCS7_R_UNSUPPORTED_CONTENT_TYPE),
+     "unsupported content type"},
+    {ERR_REASON(PKCS7_R_WRONG_CONTENT_TYPE), "wrong content type"},
+    {ERR_REASON(PKCS7_R_WRONG_PKCS7_TYPE), "wrong pkcs7 type"},
+    {0, NULL}
+};
+
+#endif
+
+int ERR_load_PKCS7_strings(void)
+{
+#ifndef OPENSSL_NO_ERR
+
+    if (ERR_func_error_string(PKCS7_str_functs[0].error) == NULL) {
+        ERR_load_strings(0, PKCS7_str_functs);
+        ERR_load_strings(0, PKCS7_str_reasons);
+    }
+#endif
+    return 1;
+}
diff --git a/crypto/poly1305/asm/poly1305-armv4.pl b/crypto/poly1305/asm/poly1305-armv4.pl
new file mode 100755
index 000000000..fc899ced8
--- /dev/null
+++ b/crypto/poly1305/asm/poly1305-armv4.pl
@@ -0,0 +1,1252 @@
+#! /usr/bin/env perl
+# Copyright 2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+#
+# ====================================================================
+# Written by Andy Polyakov  for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+#
+#			IALU(*)/gcc-4.4		NEON
+#
+# ARM11xx(ARMv6)	7.78/+100%		-
+# Cortex-A5		6.35/+130%		3.00
+# Cortex-A8		6.25/+115%		2.36
+# Cortex-A9		5.10/+95%		2.55
+# Cortex-A15		3.85/+85%		1.25(**)
+# Snapdragon S4		5.70/+100%		1.48(**)
+#
+# (*)	this is for -march=armv6, i.e. with bunch of ldrb loading data;
+# (**)	these are trade-off results, they can be improved by ~8% but at
+#	the cost of 15/12% regression on Cortex-A5/A7, it's even possible
+#	to improve Cortex-A9 result, but then A5/A7 loose more than 20%;
+
+$flavour = shift;
+if ($flavour=~/\w[\w\-]*\.\w+$/) { $output=$flavour; undef $flavour; }
+else { while (($output=shift) && ($output!~/\w[\w\-]*\.\w+$/)) {} }
+
+if ($flavour && $flavour ne "void") {
+    $0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+    ( $xlate="${dir}arm-xlate.pl" and -f $xlate ) or
+    ( $xlate="${dir}../../perlasm/arm-xlate.pl" and -f $xlate) or
+    die "can't locate arm-xlate.pl";
+
+    open STDOUT,"| \"$^X\" $xlate $flavour $output";
+} else {
+    open STDOUT,">$output";
+}
+
+($ctx,$inp,$len,$padbit)=map("r$_",(0..3));
+
+$code.=<<___;
+#include "arm_arch.h"
+
+.text
+#if defined(__thumb2__)
+.syntax	unified
+.thumb
+#else
+.code	32
+#endif
+
+.globl	poly1305_emit
+.globl	poly1305_blocks
+.globl	poly1305_init
+.type	poly1305_init,%function
+.align	5
+poly1305_init:
+.Lpoly1305_init:
+	stmdb	sp!,{r4-r11}
+
+	eor	r3,r3,r3
+	cmp	$inp,#0
+	str	r3,[$ctx,#0]		@ zero hash value
+	str	r3,[$ctx,#4]
+	str	r3,[$ctx,#8]
+	str	r3,[$ctx,#12]
+	str	r3,[$ctx,#16]
+	str	r3,[$ctx,#36]		@ is_base2_26
+	add	$ctx,$ctx,#20
+
+#ifdef	__thumb2__
+	it	eq
+#endif
+	moveq	r0,#0
+	beq	.Lno_key
+
+#if	__ARM_MAX_ARCH__>=7
+	adr	r11,.Lpoly1305_init
+	ldr	r12,.LOPENSSL_armcap
+#endif
+	ldrb	r4,[$inp,#0]
+	mov	r10,#0x0fffffff
+	ldrb	r5,[$inp,#1]
+	and	r3,r10,#-4		@ 0x0ffffffc
+	ldrb	r6,[$inp,#2]
+	ldrb	r7,[$inp,#3]
+	orr	r4,r4,r5,lsl#8
+	ldrb	r5,[$inp,#4]
+	orr	r4,r4,r6,lsl#16
+	ldrb	r6,[$inp,#5]
+	orr	r4,r4,r7,lsl#24
+	ldrb	r7,[$inp,#6]
+	and	r4,r4,r10
+
+#if	__ARM_MAX_ARCH__>=7
+	ldr	r12,[r11,r12]		@ OPENSSL_armcap_P
+# ifdef	__APPLE__
+	ldr	r12,[r12]
+# endif
+#endif
+	ldrb	r8,[$inp,#7]
+	orr	r5,r5,r6,lsl#8
+	ldrb	r6,[$inp,#8]
+	orr	r5,r5,r7,lsl#16
+	ldrb	r7,[$inp,#9]
+	orr	r5,r5,r8,lsl#24
+	ldrb	r8,[$inp,#10]
+	and	r5,r5,r3
+
+#if	__ARM_MAX_ARCH__>=7
+	tst	r12,#ARMV7_NEON		@ check for NEON
+# ifdef	__APPLE__
+	adr	r9,poly1305_blocks_neon
+	adr	r11,poly1305_blocks
+#  ifdef __thumb2__
+	it	ne
+#  endif
+	movne	r11,r9
+	adr	r12,poly1305_emit
+	adr	r10,poly1305_emit_neon
+#  ifdef __thumb2__
+	it	ne
+#  endif
+	movne	r12,r10
+# else
+#  ifdef __thumb2__
+	itete	eq
+#  endif
+	addeq	r12,r11,#(poly1305_emit-.Lpoly1305_init)
+	addne	r12,r11,#(poly1305_emit_neon-.Lpoly1305_init)
+	addeq	r11,r11,#(poly1305_blocks-.Lpoly1305_init)
+	addne	r11,r11,#(poly1305_blocks_neon-.Lpoly1305_init)
+# endif
+# ifdef	__thumb2__
+	orr	r12,r12,#1	@ thumb-ify address
+	orr	r11,r11,#1
+# endif
+#endif
+	ldrb	r9,[$inp,#11]
+	orr	r6,r6,r7,lsl#8
+	ldrb	r7,[$inp,#12]
+	orr	r6,r6,r8,lsl#16
+	ldrb	r8,[$inp,#13]
+	orr	r6,r6,r9,lsl#24
+	ldrb	r9,[$inp,#14]
+	and	r6,r6,r3
+
+	ldrb	r10,[$inp,#15]
+	orr	r7,r7,r8,lsl#8
+	str	r4,[$ctx,#0]
+	orr	r7,r7,r9,lsl#16
+	str	r5,[$ctx,#4]
+	orr	r7,r7,r10,lsl#24
+	str	r6,[$ctx,#8]
+	and	r7,r7,r3
+	str	r7,[$ctx,#12]
+#if	__ARM_MAX_ARCH__>=7
+	stmia	r2,{r11,r12}		@ fill functions table
+	mov	r0,#1
+#else
+	mov	r0,#0
+#endif
+.Lno_key:
+	ldmia	sp!,{r4-r11}
+#if	__ARM_ARCH__>=5
+	ret				@ bx	lr
+#else
+	tst	lr,#1
+	moveq	pc,lr			@ be binary compatible with V4, yet
+	bx	lr			@ interoperable with Thumb ISA:-)
+#endif
+.size	poly1305_init,.-poly1305_init
+___
+{
+my ($h0,$h1,$h2,$h3,$h4,$r0,$r1,$r2,$r3)=map("r$_",(4..12));
+my ($s1,$s2,$s3)=($r1,$r2,$r3);
+
+$code.=<<___;
+.type	poly1305_blocks,%function
+.align	5
+poly1305_blocks:
+	stmdb	sp!,{r3-r11,lr}
+
+	ands	$len,$len,#-16
+	beq	.Lno_data
+
+	cmp	$padbit,#0
+	add	$len,$len,$inp		@ end pointer
+	sub	sp,sp,#32
+
+	ldmia	$ctx,{$h0-$r3}		@ load context
+
+	str	$ctx,[sp,#12]		@ offload stuff
+	mov	lr,$inp
+	str	$len,[sp,#16]
+	str	$r1,[sp,#20]
+	str	$r2,[sp,#24]
+	str	$r3,[sp,#28]
+	b	.Loop
+
+.Loop:
+#if __ARM_ARCH__<7
+	ldrb	r0,[lr],#16		@ load input
+# ifdef	__thumb2__
+	it	hi
+# endif
+	addhi	$h4,$h4,#1		@ 1<<128
+	ldrb	r1,[lr,#-15]
+	ldrb	r2,[lr,#-14]
+	ldrb	r3,[lr,#-13]
+	orr	r1,r0,r1,lsl#8
+	ldrb	r0,[lr,#-12]
+	orr	r2,r1,r2,lsl#16
+	ldrb	r1,[lr,#-11]
+	orr	r3,r2,r3,lsl#24
+	ldrb	r2,[lr,#-10]
+	adds	$h0,$h0,r3		@ accumulate input
+
+	ldrb	r3,[lr,#-9]
+	orr	r1,r0,r1,lsl#8
+	ldrb	r0,[lr,#-8]
+	orr	r2,r1,r2,lsl#16
+	ldrb	r1,[lr,#-7]
+	orr	r3,r2,r3,lsl#24
+	ldrb	r2,[lr,#-6]
+	adcs	$h1,$h1,r3
+
+	ldrb	r3,[lr,#-5]
+	orr	r1,r0,r1,lsl#8
+	ldrb	r0,[lr,#-4]
+	orr	r2,r1,r2,lsl#16
+	ldrb	r1,[lr,#-3]
+	orr	r3,r2,r3,lsl#24
+	ldrb	r2,[lr,#-2]
+	adcs	$h2,$h2,r3
+
+	ldrb	r3,[lr,#-1]
+	orr	r1,r0,r1,lsl#8
+	str	lr,[sp,#8]		@ offload input pointer
+	orr	r2,r1,r2,lsl#16
+	add	$s1,$r1,$r1,lsr#2
+	orr	r3,r2,r3,lsl#24
+#else
+	ldr	r0,[lr],#16		@ load input
+# ifdef	__thumb2__
+	it	hi
+# endif
+	addhi	$h4,$h4,#1		@ padbit
+	ldr	r1,[lr,#-12]
+	ldr	r2,[lr,#-8]
+	ldr	r3,[lr,#-4]
+# ifdef	__ARMEB__
+	rev	r0,r0
+	rev	r1,r1
+	rev	r2,r2
+	rev	r3,r3
+# endif
+	adds	$h0,$h0,r0		@ accumulate input
+	str	lr,[sp,#8]		@ offload input pointer
+	adcs	$h1,$h1,r1
+	add	$s1,$r1,$r1,lsr#2
+	adcs	$h2,$h2,r2
+#endif
+	add	$s2,$r2,$r2,lsr#2
+	adcs	$h3,$h3,r3
+	add	$s3,$r3,$r3,lsr#2
+
+	umull	r2,r3,$h1,$r0
+	 adc	$h4,$h4,#0
+	umull	r0,r1,$h0,$r0
+	umlal	r2,r3,$h4,$s1
+	umlal	r0,r1,$h3,$s1
+	ldr	$r1,[sp,#20]		@ reload $r1
+	umlal	r2,r3,$h2,$s3
+	umlal	r0,r1,$h1,$s3
+	umlal	r2,r3,$h3,$s2
+	umlal	r0,r1,$h2,$s2
+	umlal	r2,r3,$h0,$r1
+	str	r0,[sp,#0]		@ future $h0
+	 mul	r0,$s2,$h4
+	ldr	$r2,[sp,#24]		@ reload $r2
+	adds	r2,r2,r1		@ d1+=d0>>32
+	 eor	r1,r1,r1
+	adc	lr,r3,#0		@ future $h2
+	str	r2,[sp,#4]		@ future $h1
+
+	mul	r2,$s3,$h4
+	eor	r3,r3,r3
+	umlal	r0,r1,$h3,$s3
+	ldr	$r3,[sp,#28]		@ reload $r3
+	umlal	r2,r3,$h3,$r0
+	umlal	r0,r1,$h2,$r0
+	umlal	r2,r3,$h2,$r1
+	umlal	r0,r1,$h1,$r1
+	umlal	r2,r3,$h1,$r2
+	umlal	r0,r1,$h0,$r2
+	umlal	r2,r3,$h0,$r3
+	ldr	$h0,[sp,#0]
+	mul	$h4,$r0,$h4
+	ldr	$h1,[sp,#4]
+
+	adds	$h2,lr,r0		@ d2+=d1>>32
+	ldr	lr,[sp,#8]		@ reload input pointer
+	adc	r1,r1,#0
+	adds	$h3,r2,r1		@ d3+=d2>>32
+	ldr	r0,[sp,#16]		@ reload end pointer
+	adc	r3,r3,#0
+	add	$h4,$h4,r3		@ h4+=d3>>32
+
+	and	r1,$h4,#-4
+	and	$h4,$h4,#3
+	add	r1,r1,r1,lsr#2		@ *=5
+	adds	$h0,$h0,r1
+	adcs	$h1,$h1,#0
+	adcs	$h2,$h2,#0
+	adcs	$h3,$h3,#0
+	adc	$h4,$h4,#0
+
+	cmp	r0,lr			@ done yet?
+	bhi	.Loop
+
+	ldr	$ctx,[sp,#12]
+	add	sp,sp,#32
+	stmia	$ctx,{$h0-$h4}		@ store the result
+
+.Lno_data:
+#if	__ARM_ARCH__>=5
+	ldmia	sp!,{r3-r11,pc}
+#else
+	ldmia	sp!,{r3-r11,lr}
+	tst	lr,#1
+	moveq	pc,lr			@ be binary compatible with V4, yet
+	bx	lr			@ interoperable with Thumb ISA:-)
+#endif
+.size	poly1305_blocks,.-poly1305_blocks
+___
+}
+{
+my ($ctx,$mac,$nonce)=map("r$_",(0..2));
+my ($h0,$h1,$h2,$h3,$h4,$g0,$g1,$g2,$g3)=map("r$_",(3..11));
+my $g4=$h4;
+
+$code.=<<___;
+.type	poly1305_emit,%function
+.align	5
+poly1305_emit:
+	stmdb	sp!,{r4-r11}
+.Lpoly1305_emit_enter:
+
+	ldmia	$ctx,{$h0-$h4}
+	adds	$g0,$h0,#5		@ compare to modulus
+	adcs	$g1,$h1,#0
+	adcs	$g2,$h2,#0
+	adcs	$g3,$h3,#0
+	adc	$g4,$h4,#0
+	tst	$g4,#4			@ did it carry/borrow?
+
+#ifdef	__thumb2__
+	it	ne
+#endif
+	movne	$h0,$g0
+	ldr	$g0,[$nonce,#0]
+#ifdef	__thumb2__
+	it	ne
+#endif
+	movne	$h1,$g1
+	ldr	$g1,[$nonce,#4]
+#ifdef	__thumb2__
+	it	ne
+#endif
+	movne	$h2,$g2
+	ldr	$g2,[$nonce,#8]
+#ifdef	__thumb2__
+	it	ne
+#endif
+	movne	$h3,$g3
+	ldr	$g3,[$nonce,#12]
+
+	adds	$h0,$h0,$g0
+	adcs	$h1,$h1,$g1
+	adcs	$h2,$h2,$g2
+	adc	$h3,$h3,$g3
+
+#if __ARM_ARCH__>=7
+# ifdef __ARMEB__
+	rev	$h0,$h0
+	rev	$h1,$h1
+	rev	$h2,$h2
+	rev	$h3,$h3
+# endif
+	str	$h0,[$mac,#0]
+	str	$h1,[$mac,#4]
+	str	$h2,[$mac,#8]
+	str	$h3,[$mac,#12]
+#else
+	strb	$h0,[$mac,#0]
+	mov	$h0,$h0,lsr#8
+	strb	$h1,[$mac,#4]
+	mov	$h1,$h1,lsr#8
+	strb	$h2,[$mac,#8]
+	mov	$h2,$h2,lsr#8
+	strb	$h3,[$mac,#12]
+	mov	$h3,$h3,lsr#8
+
+	strb	$h0,[$mac,#1]
+	mov	$h0,$h0,lsr#8
+	strb	$h1,[$mac,#5]
+	mov	$h1,$h1,lsr#8
+	strb	$h2,[$mac,#9]
+	mov	$h2,$h2,lsr#8
+	strb	$h3,[$mac,#13]
+	mov	$h3,$h3,lsr#8
+
+	strb	$h0,[$mac,#2]
+	mov	$h0,$h0,lsr#8
+	strb	$h1,[$mac,#6]
+	mov	$h1,$h1,lsr#8
+	strb	$h2,[$mac,#10]
+	mov	$h2,$h2,lsr#8
+	strb	$h3,[$mac,#14]
+	mov	$h3,$h3,lsr#8
+
+	strb	$h0,[$mac,#3]
+	strb	$h1,[$mac,#7]
+	strb	$h2,[$mac,#11]
+	strb	$h3,[$mac,#15]
+#endif
+	ldmia	sp!,{r4-r11}
+#if	__ARM_ARCH__>=5
+	ret				@ bx	lr
+#else
+	tst	lr,#1
+	moveq	pc,lr			@ be binary compatible with V4, yet
+	bx	lr			@ interoperable with Thumb ISA:-)
+#endif
+.size	poly1305_emit,.-poly1305_emit
+___
+{
+my ($R0,$R1,$S1,$R2,$S2,$R3,$S3,$R4,$S4) = map("d$_",(0..9));
+my ($D0,$D1,$D2,$D3,$D4, $H0,$H1,$H2,$H3,$H4) = map("q$_",(5..14));
+my ($T0,$T1,$MASK) = map("q$_",(15,4,0));
+
+my ($in2,$zeros,$tbl0,$tbl1) = map("r$_",(4..7));
+
+$code.=<<___;
+#if	__ARM_MAX_ARCH__>=7
+.fpu	neon
+
+.type	poly1305_init_neon,%function
+.align	5
+poly1305_init_neon:
+	ldr	r4,[$ctx,#20]		@ load key base 2^32
+	ldr	r5,[$ctx,#24]
+	ldr	r6,[$ctx,#28]
+	ldr	r7,[$ctx,#32]
+
+	and	r2,r4,#0x03ffffff	@ base 2^32 -> base 2^26
+	mov	r3,r4,lsr#26
+	mov	r4,r5,lsr#20
+	orr	r3,r3,r5,lsl#6
+	mov	r5,r6,lsr#14
+	orr	r4,r4,r6,lsl#12
+	mov	r6,r7,lsr#8
+	orr	r5,r5,r7,lsl#18
+	and	r3,r3,#0x03ffffff
+	and	r4,r4,#0x03ffffff
+	and	r5,r5,#0x03ffffff
+
+	vdup.32	$R0,r2			@ r^1 in both lanes
+	add	r2,r3,r3,lsl#2		@ *5
+	vdup.32	$R1,r3
+	add	r3,r4,r4,lsl#2
+	vdup.32	$S1,r2
+	vdup.32	$R2,r4
+	add	r4,r5,r5,lsl#2
+	vdup.32	$S2,r3
+	vdup.32	$R3,r5
+	add	r5,r6,r6,lsl#2
+	vdup.32	$S3,r4
+	vdup.32	$R4,r6
+	vdup.32	$S4,r5
+
+	mov	$zeros,#2		@ counter
+
+.Lsquare_neon:
+	@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+	@ d0 = h0*r0 + h4*5*r1 + h3*5*r2 + h2*5*r3 + h1*5*r4
+	@ d1 = h1*r0 + h0*r1   + h4*5*r2 + h3*5*r3 + h2*5*r4
+	@ d2 = h2*r0 + h1*r1   + h0*r2   + h4*5*r3 + h3*5*r4
+	@ d3 = h3*r0 + h2*r1   + h1*r2   + h0*r3   + h4*5*r4
+	@ d4 = h4*r0 + h3*r1   + h2*r2   + h1*r3   + h0*r4
+
+	vmull.u32	$D0,$R0,${R0}[1]
+	vmull.u32	$D1,$R1,${R0}[1]
+	vmull.u32	$D2,$R2,${R0}[1]
+	vmull.u32	$D3,$R3,${R0}[1]
+	vmull.u32	$D4,$R4,${R0}[1]
+
+	vmlal.u32	$D0,$R4,${S1}[1]
+	vmlal.u32	$D1,$R0,${R1}[1]
+	vmlal.u32	$D2,$R1,${R1}[1]
+	vmlal.u32	$D3,$R2,${R1}[1]
+	vmlal.u32	$D4,$R3,${R1}[1]
+
+	vmlal.u32	$D0,$R3,${S2}[1]
+	vmlal.u32	$D1,$R4,${S2}[1]
+	vmlal.u32	$D3,$R1,${R2}[1]
+	vmlal.u32	$D2,$R0,${R2}[1]
+	vmlal.u32	$D4,$R2,${R2}[1]
+
+	vmlal.u32	$D0,$R2,${S3}[1]
+	vmlal.u32	$D3,$R0,${R3}[1]
+	vmlal.u32	$D1,$R3,${S3}[1]
+	vmlal.u32	$D2,$R4,${S3}[1]
+	vmlal.u32	$D4,$R1,${R3}[1]
+
+	vmlal.u32	$D3,$R4,${S4}[1]
+	vmlal.u32	$D0,$R1,${S4}[1]
+	vmlal.u32	$D1,$R2,${S4}[1]
+	vmlal.u32	$D2,$R3,${S4}[1]
+	vmlal.u32	$D4,$R0,${R4}[1]
+
+	@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+	@ lazy reduction as discussed in "NEON crypto" by D.J. Bernstein
+	@ and P. Schwabe
+	@
+	@ H0>>+H1>>+H2>>+H3>>+H4
+	@ H3>>+H4>>*5+H0>>+H1
+	@
+	@ Trivia.
+	@
+	@ Result of multiplication of n-bit number by m-bit number is
+	@ n+m bits wide. However! Even though 2^n is a n+1-bit number,
+	@ m-bit number multiplied by 2^n is still n+m bits wide.
+	@
+	@ Sum of two n-bit numbers is n+1 bits wide, sum of three - n+2,
+	@ and so is sum of four. Sum of 2^m n-m-bit numbers and n-bit
+	@ one is n+1 bits wide.
+	@
+	@ >>+ denotes Hnext += Hn>>26, Hn &= 0x3ffffff. This means that
+	@ H0, H2, H3 are guaranteed to be 26 bits wide, while H1 and H4
+	@ can be 27. However! In cases when their width exceeds 26 bits
+	@ they are limited by 2^26+2^6. This in turn means that *sum*
+	@ of the products with these values can still be viewed as sum
+	@ of 52-bit numbers as long as the amount of addends is not a
+	@ power of 2. For example,
+	@
+	@ H4 = H4*R0 + H3*R1 + H2*R2 + H1*R3 + H0 * R4,
+	@
+	@ which can't be larger than 5 * (2^26 + 2^6) * (2^26 + 2^6), or
+	@ 5 * (2^52 + 2*2^32 + 2^12), which in turn is smaller than
+	@ 8 * (2^52) or 2^55. However, the value is then multiplied by
+	@ by 5, so we should be looking at 5 * 5 * (2^52 + 2^33 + 2^12),
+	@ which is less than 32 * (2^52) or 2^57. And when processing
+	@ data we are looking at triple as many addends...
+	@
+	@ In key setup procedure pre-reduced H0 is limited by 5*4+1 and
+	@ 5*H4 - by 5*5 52-bit addends, or 57 bits. But when hashing the
+	@ input H0 is limited by (5*4+1)*3 addends, or 58 bits, while
+	@ 5*H4 by 5*5*3, or 59[!] bits. How is this relevant? vmlal.u32
+	@ instruction accepts 2x32-bit input and writes 2x64-bit result.
+	@ This means that result of reduction have to be compressed upon
+	@ loop wrap-around. This can be done in the process of reduction
+	@ to minimize amount of instructions [as well as amount of
+	@ 128-bit instructions, which benefits low-end processors], but
+	@ one has to watch for H2 (which is narrower than H0) and 5*H4
+	@ not being wider than 58 bits, so that result of right shift
+	@ by 26 bits fits in 32 bits. This is also useful on x86,
+	@ because it allows to use paddd in place for paddq, which
+	@ benefits Atom, where paddq is ridiculously slow.
+
+	vshr.u64	$T0,$D3,#26
+	vmovn.i64	$D3#lo,$D3
+	 vshr.u64	$T1,$D0,#26
+	 vmovn.i64	$D0#lo,$D0
+	vadd.i64	$D4,$D4,$T0		@ h3 -> h4
+	vbic.i32	$D3#lo,#0xfc000000	@ &=0x03ffffff
+	 vadd.i64	$D1,$D1,$T1		@ h0 -> h1
+	 vbic.i32	$D0#lo,#0xfc000000
+
+	vshrn.u64	$T0#lo,$D4,#26
+	vmovn.i64	$D4#lo,$D4
+	 vshr.u64	$T1,$D1,#26
+	 vmovn.i64	$D1#lo,$D1
+	 vadd.i64	$D2,$D2,$T1		@ h1 -> h2
+	vbic.i32	$D4#lo,#0xfc000000
+	 vbic.i32	$D1#lo,#0xfc000000
+
+	vadd.i32	$D0#lo,$D0#lo,$T0#lo
+	vshl.u32	$T0#lo,$T0#lo,#2
+	 vshrn.u64	$T1#lo,$D2,#26
+	 vmovn.i64	$D2#lo,$D2
+	vadd.i32	$D0#lo,$D0#lo,$T0#lo	@ h4 -> h0
+	 vadd.i32	$D3#lo,$D3#lo,$T1#lo	@ h2 -> h3
+	 vbic.i32	$D2#lo,#0xfc000000
+
+	vshr.u32	$T0#lo,$D0#lo,#26
+	vbic.i32	$D0#lo,#0xfc000000
+	 vshr.u32	$T1#lo,$D3#lo,#26
+	 vbic.i32	$D3#lo,#0xfc000000
+	vadd.i32	$D1#lo,$D1#lo,$T0#lo	@ h0 -> h1
+	 vadd.i32	$D4#lo,$D4#lo,$T1#lo	@ h3 -> h4
+
+	subs		$zeros,$zeros,#1
+	beq		.Lsquare_break_neon
+
+	add		$tbl0,$ctx,#(48+0*9*4)
+	add		$tbl1,$ctx,#(48+1*9*4)
+
+	vtrn.32		$R0,$D0#lo		@ r^2:r^1
+	vtrn.32		$R2,$D2#lo
+	vtrn.32		$R3,$D3#lo
+	vtrn.32		$R1,$D1#lo
+	vtrn.32		$R4,$D4#lo
+
+	vshl.u32	$S2,$R2,#2		@ *5
+	vshl.u32	$S3,$R3,#2
+	vshl.u32	$S1,$R1,#2
+	vshl.u32	$S4,$R4,#2
+	vadd.i32	$S2,$S2,$R2
+	vadd.i32	$S1,$S1,$R1
+	vadd.i32	$S3,$S3,$R3
+	vadd.i32	$S4,$S4,$R4
+
+	vst4.32		{${R0}[0],${R1}[0],${S1}[0],${R2}[0]},[$tbl0]!
+	vst4.32		{${R0}[1],${R1}[1],${S1}[1],${R2}[1]},[$tbl1]!
+	vst4.32		{${S2}[0],${R3}[0],${S3}[0],${R4}[0]},[$tbl0]!
+	vst4.32		{${S2}[1],${R3}[1],${S3}[1],${R4}[1]},[$tbl1]!
+	vst1.32		{${S4}[0]},[$tbl0,:32]
+	vst1.32		{${S4}[1]},[$tbl1,:32]
+
+	b		.Lsquare_neon
+
+.align	4
+.Lsquare_break_neon:
+	add		$tbl0,$ctx,#(48+2*4*9)
+	add		$tbl1,$ctx,#(48+3*4*9)
+
+	vmov		$R0,$D0#lo		@ r^4:r^3
+	vshl.u32	$S1,$D1#lo,#2		@ *5
+	vmov		$R1,$D1#lo
+	vshl.u32	$S2,$D2#lo,#2
+	vmov		$R2,$D2#lo
+	vshl.u32	$S3,$D3#lo,#2
+	vmov		$R3,$D3#lo
+	vshl.u32	$S4,$D4#lo,#2
+	vmov		$R4,$D4#lo
+	vadd.i32	$S1,$S1,$D1#lo
+	vadd.i32	$S2,$S2,$D2#lo
+	vadd.i32	$S3,$S3,$D3#lo
+	vadd.i32	$S4,$S4,$D4#lo
+
+	vst4.32		{${R0}[0],${R1}[0],${S1}[0],${R2}[0]},[$tbl0]!
+	vst4.32		{${R0}[1],${R1}[1],${S1}[1],${R2}[1]},[$tbl1]!
+	vst4.32		{${S2}[0],${R3}[0],${S3}[0],${R4}[0]},[$tbl0]!
+	vst4.32		{${S2}[1],${R3}[1],${S3}[1],${R4}[1]},[$tbl1]!
+	vst1.32		{${S4}[0]},[$tbl0]
+	vst1.32		{${S4}[1]},[$tbl1]
+
+	ret				@ bx	lr
+.size	poly1305_init_neon,.-poly1305_init_neon
+
+.type	poly1305_blocks_neon,%function
+.align	5
+poly1305_blocks_neon:
+	ldr	ip,[$ctx,#36]		@ is_base2_26
+	ands	$len,$len,#-16
+	beq	.Lno_data_neon
+
+	cmp	$len,#64
+	bhs	.Lenter_neon
+	tst	ip,ip			@ is_base2_26?
+	beq	poly1305_blocks
+
+.Lenter_neon:
+	stmdb	sp!,{r4-r7}
+	vstmdb	sp!,{d8-d15}		@ ABI specification says so
+
+	tst	ip,ip			@ is_base2_26?
+	bne	.Lbase2_26_neon
+
+	stmdb	sp!,{r1-r3,lr}
+	bl	poly1305_init_neon
+
+	ldr	r4,[$ctx,#0]		@ load hash value base 2^32
+	ldr	r5,[$ctx,#4]
+	ldr	r6,[$ctx,#8]
+	ldr	r7,[$ctx,#12]
+	ldr	ip,[$ctx,#16]
+
+	and	r2,r4,#0x03ffffff	@ base 2^32 -> base 2^26
+	mov	r3,r4,lsr#26
+	 veor	$D0#lo,$D0#lo,$D0#lo
+	mov	r4,r5,lsr#20
+	orr	r3,r3,r5,lsl#6
+	 veor	$D1#lo,$D1#lo,$D1#lo
+	mov	r5,r6,lsr#14
+	orr	r4,r4,r6,lsl#12
+	 veor	$D2#lo,$D2#lo,$D2#lo
+	mov	r6,r7,lsr#8
+	orr	r5,r5,r7,lsl#18
+	 veor	$D3#lo,$D3#lo,$D3#lo
+	and	r3,r3,#0x03ffffff
+	orr	r6,r6,ip,lsl#24
+	 veor	$D4#lo,$D4#lo,$D4#lo
+	and	r4,r4,#0x03ffffff
+	mov	r1,#1
+	and	r5,r5,#0x03ffffff
+	str	r1,[$ctx,#36]		@ is_base2_26
+
+	vmov.32	$D0#lo[0],r2
+	vmov.32	$D1#lo[0],r3
+	vmov.32	$D2#lo[0],r4
+	vmov.32	$D3#lo[0],r5
+	vmov.32	$D4#lo[0],r6
+	adr	$zeros,.Lzeros
+
+	ldmia	sp!,{r1-r3,lr}
+	b	.Lbase2_32_neon
+
+.align	4
+.Lbase2_26_neon:
+	@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+	@ load hash value
+
+	veor		$D0#lo,$D0#lo,$D0#lo
+	veor		$D1#lo,$D1#lo,$D1#lo
+	veor		$D2#lo,$D2#lo,$D2#lo
+	veor		$D3#lo,$D3#lo,$D3#lo
+	veor		$D4#lo,$D4#lo,$D4#lo
+	vld4.32		{$D0#lo[0],$D1#lo[0],$D2#lo[0],$D3#lo[0]},[$ctx]!
+	adr		$zeros,.Lzeros
+	vld1.32		{$D4#lo[0]},[$ctx]
+	sub		$ctx,$ctx,#16		@ rewind
+
+.Lbase2_32_neon:
+	add		$in2,$inp,#32
+	mov		$padbit,$padbit,lsl#24
+	tst		$len,#31
+	beq		.Leven
+
+	vld4.32		{$H0#lo[0],$H1#lo[0],$H2#lo[0],$H3#lo[0]},[$inp]!
+	vmov.32		$H4#lo[0],$padbit
+	sub		$len,$len,#16
+	add		$in2,$inp,#32
+
+# ifdef	__ARMEB__
+	vrev32.8	$H0,$H0
+	vrev32.8	$H3,$H3
+	vrev32.8	$H1,$H1
+	vrev32.8	$H2,$H2
+# endif
+	vsri.u32	$H4#lo,$H3#lo,#8	@ base 2^32 -> base 2^26
+	vshl.u32	$H3#lo,$H3#lo,#18
+
+	vsri.u32	$H3#lo,$H2#lo,#14
+	vshl.u32	$H2#lo,$H2#lo,#12
+	vadd.i32	$H4#hi,$H4#lo,$D4#lo	@ add hash value and move to #hi
+
+	vbic.i32	$H3#lo,#0xfc000000
+	vsri.u32	$H2#lo,$H1#lo,#20
+	vshl.u32	$H1#lo,$H1#lo,#6
+
+	vbic.i32	$H2#lo,#0xfc000000
+	vsri.u32	$H1#lo,$H0#lo,#26
+	vadd.i32	$H3#hi,$H3#lo,$D3#lo
+
+	vbic.i32	$H0#lo,#0xfc000000
+	vbic.i32	$H1#lo,#0xfc000000
+	vadd.i32	$H2#hi,$H2#lo,$D2#lo
+
+	vadd.i32	$H0#hi,$H0#lo,$D0#lo
+	vadd.i32	$H1#hi,$H1#lo,$D1#lo
+
+	mov		$tbl1,$zeros
+	add		$tbl0,$ctx,#48
+
+	cmp		$len,$len
+	b		.Long_tail
+
+.align	4
+.Leven:
+	subs		$len,$len,#64
+	it		lo
+	movlo		$in2,$zeros
+
+	vmov.i32	$H4,#1<<24		@ padbit, yes, always
+	vld4.32		{$H0#lo,$H1#lo,$H2#lo,$H3#lo},[$inp]	@ inp[0:1]
+	add		$inp,$inp,#64
+	vld4.32		{$H0#hi,$H1#hi,$H2#hi,$H3#hi},[$in2]	@ inp[2:3] (or 0)
+	add		$in2,$in2,#64
+	itt		hi
+	addhi		$tbl1,$ctx,#(48+1*9*4)
+	addhi		$tbl0,$ctx,#(48+3*9*4)
+
+# ifdef	__ARMEB__
+	vrev32.8	$H0,$H0
+	vrev32.8	$H3,$H3
+	vrev32.8	$H1,$H1
+	vrev32.8	$H2,$H2
+# endif
+	vsri.u32	$H4,$H3,#8		@ base 2^32 -> base 2^26
+	vshl.u32	$H3,$H3,#18
+
+	vsri.u32	$H3,$H2,#14
+	vshl.u32	$H2,$H2,#12
+
+	vbic.i32	$H3,#0xfc000000
+	vsri.u32	$H2,$H1,#20
+	vshl.u32	$H1,$H1,#6
+
+	vbic.i32	$H2,#0xfc000000
+	vsri.u32	$H1,$H0,#26
+
+	vbic.i32	$H0,#0xfc000000
+	vbic.i32	$H1,#0xfc000000
+
+	bls		.Lskip_loop
+
+	vld4.32		{${R0}[1],${R1}[1],${S1}[1],${R2}[1]},[$tbl1]!	@ load r^2
+	vld4.32		{${R0}[0],${R1}[0],${S1}[0],${R2}[0]},[$tbl0]!	@ load r^4
+	vld4.32		{${S2}[1],${R3}[1],${S3}[1],${R4}[1]},[$tbl1]!
+	vld4.32		{${S2}[0],${R3}[0],${S3}[0],${R4}[0]},[$tbl0]!
+	b		.Loop_neon
+
+.align	5
+.Loop_neon:
+	@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+	@ ((inp[0]*r^4+inp[2]*r^2+inp[4])*r^4+inp[6]*r^2
+	@ ((inp[1]*r^4+inp[3]*r^2+inp[5])*r^3+inp[7]*r
+	@   \___________________/
+	@ ((inp[0]*r^4+inp[2]*r^2+inp[4])*r^4+inp[6]*r^2+inp[8])*r^2
+	@ ((inp[1]*r^4+inp[3]*r^2+inp[5])*r^4+inp[7]*r^2+inp[9])*r
+	@   \___________________/ \____________________/
+	@
+	@ Note that we start with inp[2:3]*r^2. This is because it
+	@ doesn't depend on reduction in previous iteration.
+	@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+	@ d4 = h4*r0 + h3*r1   + h2*r2   + h1*r3   + h0*r4
+	@ d3 = h3*r0 + h2*r1   + h1*r2   + h0*r3   + h4*5*r4
+	@ d2 = h2*r0 + h1*r1   + h0*r2   + h4*5*r3 + h3*5*r4
+	@ d1 = h1*r0 + h0*r1   + h4*5*r2 + h3*5*r3 + h2*5*r4
+	@ d0 = h0*r0 + h4*5*r1 + h3*5*r2 + h2*5*r3 + h1*5*r4
+
+	@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+	@ inp[2:3]*r^2
+
+	vadd.i32	$H2#lo,$H2#lo,$D2#lo	@ accumulate inp[0:1]
+	vmull.u32	$D2,$H2#hi,${R0}[1]
+	vadd.i32	$H0#lo,$H0#lo,$D0#lo
+	vmull.u32	$D0,$H0#hi,${R0}[1]
+	vadd.i32	$H3#lo,$H3#lo,$D3#lo
+	vmull.u32	$D3,$H3#hi,${R0}[1]
+	vmlal.u32	$D2,$H1#hi,${R1}[1]
+	vadd.i32	$H1#lo,$H1#lo,$D1#lo
+	vmull.u32	$D1,$H1#hi,${R0}[1]
+
+	vadd.i32	$H4#lo,$H4#lo,$D4#lo
+	vmull.u32	$D4,$H4#hi,${R0}[1]
+	subs		$len,$len,#64
+	vmlal.u32	$D0,$H4#hi,${S1}[1]
+	it		lo
+	movlo		$in2,$zeros
+	vmlal.u32	$D3,$H2#hi,${R1}[1]
+	vld1.32		${S4}[1],[$tbl1,:32]
+	vmlal.u32	$D1,$H0#hi,${R1}[1]
+	vmlal.u32	$D4,$H3#hi,${R1}[1]
+
+	vmlal.u32	$D0,$H3#hi,${S2}[1]
+	vmlal.u32	$D3,$H1#hi,${R2}[1]
+	vmlal.u32	$D4,$H2#hi,${R2}[1]
+	vmlal.u32	$D1,$H4#hi,${S2}[1]
+	vmlal.u32	$D2,$H0#hi,${R2}[1]
+
+	vmlal.u32	$D3,$H0#hi,${R3}[1]
+	vmlal.u32	$D0,$H2#hi,${S3}[1]
+	vmlal.u32	$D4,$H1#hi,${R3}[1]
+	vmlal.u32	$D1,$H3#hi,${S3}[1]
+	vmlal.u32	$D2,$H4#hi,${S3}[1]
+
+	vmlal.u32	$D3,$H4#hi,${S4}[1]
+	vmlal.u32	$D0,$H1#hi,${S4}[1]
+	vmlal.u32	$D4,$H0#hi,${R4}[1]
+	vmlal.u32	$D1,$H2#hi,${S4}[1]
+	vmlal.u32	$D2,$H3#hi,${S4}[1]
+
+	vld4.32		{$H0#hi,$H1#hi,$H2#hi,$H3#hi},[$in2]	@ inp[2:3] (or 0)
+	add		$in2,$in2,#64
+
+	@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+	@ (hash+inp[0:1])*r^4 and accumulate
+
+	vmlal.u32	$D3,$H3#lo,${R0}[0]
+	vmlal.u32	$D0,$H0#lo,${R0}[0]
+	vmlal.u32	$D4,$H4#lo,${R0}[0]
+	vmlal.u32	$D1,$H1#lo,${R0}[0]
+	vmlal.u32	$D2,$H2#lo,${R0}[0]
+	vld1.32		${S4}[0],[$tbl0,:32]
+
+	vmlal.u32	$D3,$H2#lo,${R1}[0]
+	vmlal.u32	$D0,$H4#lo,${S1}[0]
+	vmlal.u32	$D4,$H3#lo,${R1}[0]
+	vmlal.u32	$D1,$H0#lo,${R1}[0]
+	vmlal.u32	$D2,$H1#lo,${R1}[0]
+
+	vmlal.u32	$D3,$H1#lo,${R2}[0]
+	vmlal.u32	$D0,$H3#lo,${S2}[0]
+	vmlal.u32	$D4,$H2#lo,${R2}[0]
+	vmlal.u32	$D1,$H4#lo,${S2}[0]
+	vmlal.u32	$D2,$H0#lo,${R2}[0]
+
+	vmlal.u32	$D3,$H0#lo,${R3}[0]
+	vmlal.u32	$D0,$H2#lo,${S3}[0]
+	vmlal.u32	$D4,$H1#lo,${R3}[0]
+	vmlal.u32	$D1,$H3#lo,${S3}[0]
+	vmlal.u32	$D3,$H4#lo,${S4}[0]
+
+	vmlal.u32	$D2,$H4#lo,${S3}[0]
+	vmlal.u32	$D0,$H1#lo,${S4}[0]
+	vmlal.u32	$D4,$H0#lo,${R4}[0]
+	vmov.i32	$H4,#1<<24		@ padbit, yes, always
+	vmlal.u32	$D1,$H2#lo,${S4}[0]
+	vmlal.u32	$D2,$H3#lo,${S4}[0]
+
+	vld4.32		{$H0#lo,$H1#lo,$H2#lo,$H3#lo},[$inp]	@ inp[0:1]
+	add		$inp,$inp,#64
+# ifdef	__ARMEB__
+	vrev32.8	$H0,$H0
+	vrev32.8	$H1,$H1
+	vrev32.8	$H2,$H2
+	vrev32.8	$H3,$H3
+# endif
+
+	@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+	@ lazy reduction interleaved with base 2^32 -> base 2^26 of
+	@ inp[0:3] previously loaded to $H0-$H3 and smashed to $H0-$H4.
+
+	vshr.u64	$T0,$D3,#26
+	vmovn.i64	$D3#lo,$D3
+	 vshr.u64	$T1,$D0,#26
+	 vmovn.i64	$D0#lo,$D0
+	vadd.i64	$D4,$D4,$T0		@ h3 -> h4
+	vbic.i32	$D3#lo,#0xfc000000
+	  vsri.u32	$H4,$H3,#8		@ base 2^32 -> base 2^26
+	 vadd.i64	$D1,$D1,$T1		@ h0 -> h1
+	  vshl.u32	$H3,$H3,#18
+	 vbic.i32	$D0#lo,#0xfc000000
+
+	vshrn.u64	$T0#lo,$D4,#26
+	vmovn.i64	$D4#lo,$D4
+	 vshr.u64	$T1,$D1,#26
+	 vmovn.i64	$D1#lo,$D1
+	 vadd.i64	$D2,$D2,$T1		@ h1 -> h2
+	  vsri.u32	$H3,$H2,#14
+	vbic.i32	$D4#lo,#0xfc000000
+	  vshl.u32	$H2,$H2,#12
+	 vbic.i32	$D1#lo,#0xfc000000
+
+	vadd.i32	$D0#lo,$D0#lo,$T0#lo
+	vshl.u32	$T0#lo,$T0#lo,#2
+	  vbic.i32	$H3,#0xfc000000
+	 vshrn.u64	$T1#lo,$D2,#26
+	 vmovn.i64	$D2#lo,$D2
+	vaddl.u32	$D0,$D0#lo,$T0#lo	@ h4 -> h0 [widen for a sec]
+	  vsri.u32	$H2,$H1,#20
+	 vadd.i32	$D3#lo,$D3#lo,$T1#lo	@ h2 -> h3
+	  vshl.u32	$H1,$H1,#6
+	 vbic.i32	$D2#lo,#0xfc000000
+	  vbic.i32	$H2,#0xfc000000
+
+	vshrn.u64	$T0#lo,$D0,#26		@ re-narrow
+	vmovn.i64	$D0#lo,$D0
+	  vsri.u32	$H1,$H0,#26
+	  vbic.i32	$H0,#0xfc000000
+	 vshr.u32	$T1#lo,$D3#lo,#26
+	 vbic.i32	$D3#lo,#0xfc000000
+	vbic.i32	$D0#lo,#0xfc000000
+	vadd.i32	$D1#lo,$D1#lo,$T0#lo	@ h0 -> h1
+	 vadd.i32	$D4#lo,$D4#lo,$T1#lo	@ h3 -> h4
+	  vbic.i32	$H1,#0xfc000000
+
+	bhi		.Loop_neon
+
+.Lskip_loop:
+	@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+	@ multiply (inp[0:1]+hash) or inp[2:3] by r^2:r^1
+
+	add		$tbl1,$ctx,#(48+0*9*4)
+	add		$tbl0,$ctx,#(48+1*9*4)
+	adds		$len,$len,#32
+	it		ne
+	movne		$len,#0
+	bne		.Long_tail
+
+	vadd.i32	$H2#hi,$H2#lo,$D2#lo	@ add hash value and move to #hi
+	vadd.i32	$H0#hi,$H0#lo,$D0#lo
+	vadd.i32	$H3#hi,$H3#lo,$D3#lo
+	vadd.i32	$H1#hi,$H1#lo,$D1#lo
+	vadd.i32	$H4#hi,$H4#lo,$D4#lo
+
+.Long_tail:
+	vld4.32		{${R0}[1],${R1}[1],${S1}[1],${R2}[1]},[$tbl1]!	@ load r^1
+	vld4.32		{${R0}[0],${R1}[0],${S1}[0],${R2}[0]},[$tbl0]!	@ load r^2
+
+	vadd.i32	$H2#lo,$H2#lo,$D2#lo	@ can be redundant
+	vmull.u32	$D2,$H2#hi,$R0
+	vadd.i32	$H0#lo,$H0#lo,$D0#lo
+	vmull.u32	$D0,$H0#hi,$R0
+	vadd.i32	$H3#lo,$H3#lo,$D3#lo
+	vmull.u32	$D3,$H3#hi,$R0
+	vadd.i32	$H1#lo,$H1#lo,$D1#lo
+	vmull.u32	$D1,$H1#hi,$R0
+	vadd.i32	$H4#lo,$H4#lo,$D4#lo
+	vmull.u32	$D4,$H4#hi,$R0
+
+	vmlal.u32	$D0,$H4#hi,$S1
+	vld4.32		{${S2}[1],${R3}[1],${S3}[1],${R4}[1]},[$tbl1]!
+	vmlal.u32	$D3,$H2#hi,$R1
+	vld4.32		{${S2}[0],${R3}[0],${S3}[0],${R4}[0]},[$tbl0]!
+	vmlal.u32	$D1,$H0#hi,$R1
+	vmlal.u32	$D4,$H3#hi,$R1
+	vmlal.u32	$D2,$H1#hi,$R1
+
+	vmlal.u32	$D3,$H1#hi,$R2
+	vld1.32		${S4}[1],[$tbl1,:32]
+	vmlal.u32	$D0,$H3#hi,$S2
+	vld1.32		${S4}[0],[$tbl0,:32]
+	vmlal.u32	$D4,$H2#hi,$R2
+	vmlal.u32	$D1,$H4#hi,$S2
+	vmlal.u32	$D2,$H0#hi,$R2
+
+	vmlal.u32	$D3,$H0#hi,$R3
+	 it		ne
+	 addne		$tbl1,$ctx,#(48+2*9*4)
+	vmlal.u32	$D0,$H2#hi,$S3
+	 it		ne
+	 addne		$tbl0,$ctx,#(48+3*9*4)
+	vmlal.u32	$D4,$H1#hi,$R3
+	vmlal.u32	$D1,$H3#hi,$S3
+	vmlal.u32	$D2,$H4#hi,$S3
+
+	vmlal.u32	$D3,$H4#hi,$S4
+	 vorn		$MASK,$MASK,$MASK	@ all-ones, can be redundant
+	vmlal.u32	$D0,$H1#hi,$S4
+	 vshr.u64	$MASK,$MASK,#38
+	vmlal.u32	$D4,$H0#hi,$R4
+	vmlal.u32	$D1,$H2#hi,$S4
+	vmlal.u32	$D2,$H3#hi,$S4
+
+	beq		.Lshort_tail
+
+	@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+	@ (hash+inp[0:1])*r^4:r^3 and accumulate
+
+	vld4.32		{${R0}[1],${R1}[1],${S1}[1],${R2}[1]},[$tbl1]!	@ load r^3
+	vld4.32		{${R0}[0],${R1}[0],${S1}[0],${R2}[0]},[$tbl0]!	@ load r^4
+
+	vmlal.u32	$D2,$H2#lo,$R0
+	vmlal.u32	$D0,$H0#lo,$R0
+	vmlal.u32	$D3,$H3#lo,$R0
+	vmlal.u32	$D1,$H1#lo,$R0
+	vmlal.u32	$D4,$H4#lo,$R0
+
+	vmlal.u32	$D0,$H4#lo,$S1
+	vld4.32		{${S2}[1],${R3}[1],${S3}[1],${R4}[1]},[$tbl1]!
+	vmlal.u32	$D3,$H2#lo,$R1
+	vld4.32		{${S2}[0],${R3}[0],${S3}[0],${R4}[0]},[$tbl0]!
+	vmlal.u32	$D1,$H0#lo,$R1
+	vmlal.u32	$D4,$H3#lo,$R1
+	vmlal.u32	$D2,$H1#lo,$R1
+
+	vmlal.u32	$D3,$H1#lo,$R2
+	vld1.32		${S4}[1],[$tbl1,:32]
+	vmlal.u32	$D0,$H3#lo,$S2
+	vld1.32		${S4}[0],[$tbl0,:32]
+	vmlal.u32	$D4,$H2#lo,$R2
+	vmlal.u32	$D1,$H4#lo,$S2
+	vmlal.u32	$D2,$H0#lo,$R2
+
+	vmlal.u32	$D3,$H0#lo,$R3
+	vmlal.u32	$D0,$H2#lo,$S3
+	vmlal.u32	$D4,$H1#lo,$R3
+	vmlal.u32	$D1,$H3#lo,$S3
+	vmlal.u32	$D2,$H4#lo,$S3
+
+	vmlal.u32	$D3,$H4#lo,$S4
+	 vorn		$MASK,$MASK,$MASK	@ all-ones
+	vmlal.u32	$D0,$H1#lo,$S4
+	 vshr.u64	$MASK,$MASK,#38
+	vmlal.u32	$D4,$H0#lo,$R4
+	vmlal.u32	$D1,$H2#lo,$S4
+	vmlal.u32	$D2,$H3#lo,$S4
+
+.Lshort_tail:
+	@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+	@ horizontal addition
+
+	vadd.i64	$D3#lo,$D3#lo,$D3#hi
+	vadd.i64	$D0#lo,$D0#lo,$D0#hi
+	vadd.i64	$D4#lo,$D4#lo,$D4#hi
+	vadd.i64	$D1#lo,$D1#lo,$D1#hi
+	vadd.i64	$D2#lo,$D2#lo,$D2#hi
+
+	@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+	@ lazy reduction, but without narrowing
+
+	vshr.u64	$T0,$D3,#26
+	vand.i64	$D3,$D3,$MASK
+	 vshr.u64	$T1,$D0,#26
+	 vand.i64	$D0,$D0,$MASK
+	vadd.i64	$D4,$D4,$T0		@ h3 -> h4
+	 vadd.i64	$D1,$D1,$T1		@ h0 -> h1
+
+	vshr.u64	$T0,$D4,#26
+	vand.i64	$D4,$D4,$MASK
+	 vshr.u64	$T1,$D1,#26
+	 vand.i64	$D1,$D1,$MASK
+	 vadd.i64	$D2,$D2,$T1		@ h1 -> h2
+
+	vadd.i64	$D0,$D0,$T0
+	vshl.u64	$T0,$T0,#2
+	 vshr.u64	$T1,$D2,#26
+	 vand.i64	$D2,$D2,$MASK
+	vadd.i64	$D0,$D0,$T0		@ h4 -> h0
+	 vadd.i64	$D3,$D3,$T1		@ h2 -> h3
+
+	vshr.u64	$T0,$D0,#26
+	vand.i64	$D0,$D0,$MASK
+	 vshr.u64	$T1,$D3,#26
+	 vand.i64	$D3,$D3,$MASK
+	vadd.i64	$D1,$D1,$T0		@ h0 -> h1
+	 vadd.i64	$D4,$D4,$T1		@ h3 -> h4
+
+	cmp		$len,#0
+	bne		.Leven
+
+	@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+	@ store hash value
+
+	vst4.32		{$D0#lo[0],$D1#lo[0],$D2#lo[0],$D3#lo[0]},[$ctx]!
+	vst1.32		{$D4#lo[0]},[$ctx]
+
+	vldmia	sp!,{d8-d15}			@ epilogue
+	ldmia	sp!,{r4-r7}
+.Lno_data_neon:
+	ret					@ bx	lr
+.size	poly1305_blocks_neon,.-poly1305_blocks_neon
+
+.type	poly1305_emit_neon,%function
+.align	5
+poly1305_emit_neon:
+	ldr	ip,[$ctx,#36]		@ is_base2_26
+
+	stmdb	sp!,{r4-r11}
+
+	tst	ip,ip
+	beq	.Lpoly1305_emit_enter
+
+	ldmia	$ctx,{$h0-$h4}
+	eor	$g0,$g0,$g0
+
+	adds	$h0,$h0,$h1,lsl#26	@ base 2^26 -> base 2^32
+	mov	$h1,$h1,lsr#6
+	adcs	$h1,$h1,$h2,lsl#20
+	mov	$h2,$h2,lsr#12
+	adcs	$h2,$h2,$h3,lsl#14
+	mov	$h3,$h3,lsr#18
+	adcs	$h3,$h3,$h4,lsl#8
+	adc	$h4,$g0,$h4,lsr#24	@ can be partially reduced ...
+
+	and	$g0,$h4,#-4		@ ... so reduce
+	and	$h4,$h3,#3
+	add	$g0,$g0,$g0,lsr#2	@ *= 5
+	adds	$h0,$h0,$g0
+	adcs	$h1,$h1,#0
+	adcs	$h2,$h2,#0
+	adcs	$h3,$h3,#0
+	adc	$h4,$h4,#0
+
+	adds	$g0,$h0,#5		@ compare to modulus
+	adcs	$g1,$h1,#0
+	adcs	$g2,$h2,#0
+	adcs	$g3,$h3,#0
+	adc	$g4,$h4,#0
+	tst	$g4,#4			@ did it carry/borrow?
+
+	it	ne
+	movne	$h0,$g0
+	ldr	$g0,[$nonce,#0]
+	it	ne
+	movne	$h1,$g1
+	ldr	$g1,[$nonce,#4]
+	it	ne
+	movne	$h2,$g2
+	ldr	$g2,[$nonce,#8]
+	it	ne
+	movne	$h3,$g3
+	ldr	$g3,[$nonce,#12]
+
+	adds	$h0,$h0,$g0		@ accumulate nonce
+	adcs	$h1,$h1,$g1
+	adcs	$h2,$h2,$g2
+	adc	$h3,$h3,$g3
+
+# ifdef __ARMEB__
+	rev	$h0,$h0
+	rev	$h1,$h1
+	rev	$h2,$h2
+	rev	$h3,$h3
+# endif
+	str	$h0,[$mac,#0]		@ store the result
+	str	$h1,[$mac,#4]
+	str	$h2,[$mac,#8]
+	str	$h3,[$mac,#12]
+
+	ldmia	sp!,{r4-r11}
+	ret				@ bx	lr
+.size	poly1305_emit_neon,.-poly1305_emit_neon
+
+.align	5
+.Lzeros:
+.long	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+.LOPENSSL_armcap:
+.word	OPENSSL_armcap_P-.Lpoly1305_init
+#endif
+___
+}	}
+$code.=<<___;
+.asciz	"Poly1305 for ARMv4/NEON, CRYPTOGAMS by "
+.align	2
+#if	__ARM_MAX_ARCH__>=7
+.comm   OPENSSL_armcap_P,4,4
+#endif
+___
+
+foreach (split("\n",$code)) {
+	s/\`([^\`]*)\`/eval $1/geo;
+
+	s/\bq([0-9]+)#(lo|hi)/sprintf "d%d",2*$1+($2 eq "hi")/geo	or
+	s/\bret\b/bx	lr/go						or
+	s/\bbx\s+lr\b/.word\t0xe12fff1e/go;	# make it possible to compile with -march=armv4
+
+	print $_,"\n";
+}
+close STDOUT; # enforce flush
diff --git a/crypto/poly1305/asm/poly1305-armv8.pl b/crypto/poly1305/asm/poly1305-armv8.pl
new file mode 100755
index 000000000..607696cab
--- /dev/null
+++ b/crypto/poly1305/asm/poly1305-armv8.pl
@@ -0,0 +1,939 @@
+#! /usr/bin/env perl
+# Copyright 2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+#
+# ====================================================================
+# Written by Andy Polyakov  for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+#
+# This module implements Poly1305 hash for ARMv8.
+#
+# June 2015
+#
+# Numbers are cycles per processed byte with poly1305_blocks alone.
+#
+#		IALU/gcc-4.9	NEON
+#
+# Apple A7	1.86/+5%	0.72
+# Cortex-A53	2.69/+58%	1.47
+# Cortex-A57	2.70/+7%	1.14
+# Denver	1.64/+50%	1.18(*)
+# X-Gene	2.13/+68%	2.27
+# Mongoose	1.77/+75%	1.12
+#
+# (*)	estimate based on resources availability is less than 1.0,
+#	i.e. measured result is worse than expected, presumably binary
+#	translator is not almighty;
+
+$flavour=shift;
+$output=shift;
+
+$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+( $xlate="${dir}arm-xlate.pl" and -f $xlate ) or
+( $xlate="${dir}../../perlasm/arm-xlate.pl" and -f $xlate) or
+die "can't locate arm-xlate.pl";
+
+open OUT,"| \"$^X\" $xlate $flavour $output";
+*STDOUT=*OUT;
+
+my ($ctx,$inp,$len,$padbit) = map("x$_",(0..3));
+my ($mac,$nonce)=($inp,$len);
+
+my ($h0,$h1,$h2,$r0,$r1,$s1,$t0,$t1,$d0,$d1,$d2) = map("x$_",(4..14));
+
+$code.=<<___;
+#include "arm_arch.h"
+
+.text
+
+// forward "declarations" are required for Apple
+.extern	OPENSSL_armcap_P
+.globl	poly1305_blocks
+.globl	poly1305_emit
+
+.globl	poly1305_init
+.type	poly1305_init,%function
+.align	5
+poly1305_init:
+	cmp	$inp,xzr
+	stp	xzr,xzr,[$ctx]		// zero hash value
+	stp	xzr,xzr,[$ctx,#16]	// [along with is_base2_26]
+
+	csel	x0,xzr,x0,eq
+	b.eq	.Lno_key
+
+#ifdef	__ILP32__
+	ldrsw	$t1,.LOPENSSL_armcap_P
+#else
+	ldr	$t1,.LOPENSSL_armcap_P
+#endif
+	adr	$t0,.LOPENSSL_armcap_P
+
+	ldp	$r0,$r1,[$inp]		// load key
+	mov	$s1,#0xfffffffc0fffffff
+	movk	$s1,#0x0fff,lsl#48
+	ldr	w17,[$t0,$t1]
+#ifdef	__ARMEB__
+	rev	$r0,$r0			// flip bytes
+	rev	$r1,$r1
+#endif
+	and	$r0,$r0,$s1		// &=0ffffffc0fffffff
+	and	$s1,$s1,#-4
+	and	$r1,$r1,$s1		// &=0ffffffc0ffffffc
+	stp	$r0,$r1,[$ctx,#32]	// save key value
+
+	tst	w17,#ARMV7_NEON
+
+	adr	$d0,poly1305_blocks
+	adr	$r0,poly1305_blocks_neon
+	adr	$d1,poly1305_emit
+	adr	$r1,poly1305_emit_neon
+
+	csel	$d0,$d0,$r0,eq
+	csel	$d1,$d1,$r1,eq
+
+	stp	$d0,$d1,[$len]
+
+	mov	x0,#1
+.Lno_key:
+	ret
+.size	poly1305_init,.-poly1305_init
+
+.type	poly1305_blocks,%function
+.align	5
+poly1305_blocks:
+	ands	$len,$len,#-16
+	b.eq	.Lno_data
+
+	ldp	$h0,$h1,[$ctx]		// load hash value
+	ldp	$r0,$r1,[$ctx,#32]	// load key value
+	ldr	$h2,[$ctx,#16]
+	add	$s1,$r1,$r1,lsr#2	// s1 = r1 + (r1 >> 2)
+	b	.Loop
+
+.align	5
+.Loop:
+	ldp	$t0,$t1,[$inp],#16	// load input
+	sub	$len,$len,#16
+#ifdef	__ARMEB__
+	rev	$t0,$t0
+	rev	$t1,$t1
+#endif
+	adds	$h0,$h0,$t0		// accumulate input
+	adcs	$h1,$h1,$t1
+
+	mul	$d0,$h0,$r0		// h0*r0
+	adc	$h2,$h2,$padbit
+	umulh	$d1,$h0,$r0
+
+	mul	$t0,$h1,$s1		// h1*5*r1
+	umulh	$t1,$h1,$s1
+
+	adds	$d0,$d0,$t0
+	mul	$t0,$h0,$r1		// h0*r1
+	adc	$d1,$d1,$t1
+	umulh	$d2,$h0,$r1
+
+	adds	$d1,$d1,$t0
+	mul	$t0,$h1,$r0		// h1*r0
+	adc	$d2,$d2,xzr
+	umulh	$t1,$h1,$r0
+
+	adds	$d1,$d1,$t0
+	mul	$t0,$h2,$s1		// h2*5*r1
+	adc	$d2,$d2,$t1
+	mul	$t1,$h2,$r0		// h2*r0
+
+	adds	$d1,$d1,$t0
+	adc	$d2,$d2,$t1
+
+	and	$t0,$d2,#-4		// final reduction
+	and	$h2,$d2,#3
+	add	$t0,$t0,$d2,lsr#2
+	adds	$h0,$d0,$t0
+	adcs	$h1,$d1,xzr
+	adc	$h2,$h2,xzr
+
+	cbnz	$len,.Loop
+
+	stp	$h0,$h1,[$ctx]		// store hash value
+	str	$h2,[$ctx,#16]
+
+.Lno_data:
+	ret
+.size	poly1305_blocks,.-poly1305_blocks
+
+.type	poly1305_emit,%function
+.align	5
+poly1305_emit:
+	ldp	$h0,$h1,[$ctx]		// load hash base 2^64
+	ldr	$h2,[$ctx,#16]
+	ldp	$t0,$t1,[$nonce]	// load nonce
+
+	adds	$d0,$h0,#5		// compare to modulus
+	adcs	$d1,$h1,xzr
+	adc	$d2,$h2,xzr
+
+	tst	$d2,#-4			// see if it's carried/borrowed
+
+	csel	$h0,$h0,$d0,eq
+	csel	$h1,$h1,$d1,eq
+
+#ifdef	__ARMEB__
+	ror	$t0,$t0,#32		// flip nonce words
+	ror	$t1,$t1,#32
+#endif
+	adds	$h0,$h0,$t0		// accumulate nonce
+	adc	$h1,$h1,$t1
+#ifdef	__ARMEB__
+	rev	$h0,$h0			// flip output bytes
+	rev	$h1,$h1
+#endif
+	stp	$h0,$h1,[$mac]		// write result
+
+	ret
+.size	poly1305_emit,.-poly1305_emit
+___
+my ($R0,$R1,$S1,$R2,$S2,$R3,$S3,$R4,$S4) = map("v$_.4s",(0..8));
+my ($IN01_0,$IN01_1,$IN01_2,$IN01_3,$IN01_4) = map("v$_.2s",(9..13));
+my ($IN23_0,$IN23_1,$IN23_2,$IN23_3,$IN23_4) = map("v$_.2s",(14..18));
+my ($ACC0,$ACC1,$ACC2,$ACC3,$ACC4) = map("v$_.2d",(19..23));
+my ($H0,$H1,$H2,$H3,$H4) = map("v$_.2s",(24..28));
+my ($T0,$T1,$MASK) = map("v$_",(29..31));
+
+my ($in2,$zeros)=("x16","x17");
+my $is_base2_26 = $zeros;		# borrow
+
+$code.=<<___;
+.type	poly1305_mult,%function
+.align	5
+poly1305_mult:
+	mul	$d0,$h0,$r0		// h0*r0
+	umulh	$d1,$h0,$r0
+
+	mul	$t0,$h1,$s1		// h1*5*r1
+	umulh	$t1,$h1,$s1
+
+	adds	$d0,$d0,$t0
+	mul	$t0,$h0,$r1		// h0*r1
+	adc	$d1,$d1,$t1
+	umulh	$d2,$h0,$r1
+
+	adds	$d1,$d1,$t0
+	mul	$t0,$h1,$r0		// h1*r0
+	adc	$d2,$d2,xzr
+	umulh	$t1,$h1,$r0
+
+	adds	$d1,$d1,$t0
+	mul	$t0,$h2,$s1		// h2*5*r1
+	adc	$d2,$d2,$t1
+	mul	$t1,$h2,$r0		// h2*r0
+
+	adds	$d1,$d1,$t0
+	adc	$d2,$d2,$t1
+
+	and	$t0,$d2,#-4		// final reduction
+	and	$h2,$d2,#3
+	add	$t0,$t0,$d2,lsr#2
+	adds	$h0,$d0,$t0
+	adcs	$h1,$d1,xzr
+	adc	$h2,$h2,xzr
+
+	ret
+.size	poly1305_mult,.-poly1305_mult
+
+.type	poly1305_splat,%function
+.align	5
+poly1305_splat:
+	and	x12,$h0,#0x03ffffff	// base 2^64 -> base 2^26
+	ubfx	x13,$h0,#26,#26
+	extr	x14,$h1,$h0,#52
+	and	x14,x14,#0x03ffffff
+	ubfx	x15,$h1,#14,#26
+	extr	x16,$h2,$h1,#40
+
+	str	w12,[$ctx,#16*0]	// r0
+	add	w12,w13,w13,lsl#2	// r1*5
+	str	w13,[$ctx,#16*1]	// r1
+	add	w13,w14,w14,lsl#2	// r2*5
+	str	w12,[$ctx,#16*2]	// s1
+	str	w14,[$ctx,#16*3]	// r2
+	add	w14,w15,w15,lsl#2	// r3*5
+	str	w13,[$ctx,#16*4]	// s2
+	str	w15,[$ctx,#16*5]	// r3
+	add	w15,w16,w16,lsl#2	// r4*5
+	str	w14,[$ctx,#16*6]	// s3
+	str	w16,[$ctx,#16*7]	// r4
+	str	w15,[$ctx,#16*8]	// s4
+
+	ret
+.size	poly1305_splat,.-poly1305_splat
+
+.type	poly1305_blocks_neon,%function
+.align	5
+poly1305_blocks_neon:
+	ldr	$is_base2_26,[$ctx,#24]
+	cmp	$len,#128
+	b.hs	.Lblocks_neon
+	cbz	$is_base2_26,poly1305_blocks
+
+.Lblocks_neon:
+	stp	x29,x30,[sp,#-80]!
+	add	x29,sp,#0
+
+	ands	$len,$len,#-16
+	b.eq	.Lno_data_neon
+
+	cbz	$is_base2_26,.Lbase2_64_neon
+
+	ldp	w10,w11,[$ctx]		// load hash value base 2^26
+	ldp	w12,w13,[$ctx,#8]
+	ldr	w14,[$ctx,#16]
+
+	tst	$len,#31
+	b.eq	.Leven_neon
+
+	ldp	$r0,$r1,[$ctx,#32]	// load key value
+
+	add	$h0,x10,x11,lsl#26	// base 2^26 -> base 2^64
+	lsr	$h1,x12,#12
+	adds	$h0,$h0,x12,lsl#52
+	add	$h1,$h1,x13,lsl#14
+	adc	$h1,$h1,xzr
+	lsr	$h2,x14,#24
+	adds	$h1,$h1,x14,lsl#40
+	adc	$d2,$h2,xzr		// can be partially reduced...
+
+	ldp	$d0,$d1,[$inp],#16	// load input
+	sub	$len,$len,#16
+	add	$s1,$r1,$r1,lsr#2	// s1 = r1 + (r1 >> 2)
+
+	and	$t0,$d2,#-4		// ... so reduce
+	and	$h2,$d2,#3
+	add	$t0,$t0,$d2,lsr#2
+	adds	$h0,$h0,$t0
+	adcs	$h1,$h1,xzr
+	adc	$h2,$h2,xzr
+
+#ifdef	__ARMEB__
+	rev	$d0,$d0
+	rev	$d1,$d1
+#endif
+	adds	$h0,$h0,$d0		// accumulate input
+	adcs	$h1,$h1,$d1
+	adc	$h2,$h2,$padbit
+
+	bl	poly1305_mult
+	ldr	x30,[sp,#8]
+
+	cbz	$padbit,.Lstore_base2_64_neon
+
+	and	x10,$h0,#0x03ffffff	// base 2^64 -> base 2^26
+	ubfx	x11,$h0,#26,#26
+	extr	x12,$h1,$h0,#52
+	and	x12,x12,#0x03ffffff
+	ubfx	x13,$h1,#14,#26
+	extr	x14,$h2,$h1,#40
+
+	cbnz	$len,.Leven_neon
+
+	stp	w10,w11,[$ctx]		// store hash value base 2^26
+	stp	w12,w13,[$ctx,#8]
+	str	w14,[$ctx,#16]
+	b	.Lno_data_neon
+
+.align	4
+.Lstore_base2_64_neon:
+	stp	$h0,$h1,[$ctx]		// store hash value base 2^64
+	stp	$h2,xzr,[$ctx,#16]	// note that is_base2_26 is zeroed
+	b	.Lno_data_neon
+
+.align	4
+.Lbase2_64_neon:
+	ldp	$r0,$r1,[$ctx,#32]	// load key value
+
+	ldp	$h0,$h1,[$ctx]		// load hash value base 2^64
+	ldr	$h2,[$ctx,#16]
+
+	tst	$len,#31
+	b.eq	.Linit_neon
+
+	ldp	$d0,$d1,[$inp],#16	// load input
+	sub	$len,$len,#16
+	add	$s1,$r1,$r1,lsr#2	// s1 = r1 + (r1 >> 2)
+#ifdef	__ARMEB__
+	rev	$d0,$d0
+	rev	$d1,$d1
+#endif
+	adds	$h0,$h0,$d0		// accumulate input
+	adcs	$h1,$h1,$d1
+	adc	$h2,$h2,$padbit
+
+	bl	poly1305_mult
+
+.Linit_neon:
+	and	x10,$h0,#0x03ffffff	// base 2^64 -> base 2^26
+	ubfx	x11,$h0,#26,#26
+	extr	x12,$h1,$h0,#52
+	and	x12,x12,#0x03ffffff
+	ubfx	x13,$h1,#14,#26
+	extr	x14,$h2,$h1,#40
+
+	stp	d8,d9,[sp,#16]		// meet ABI requirements
+	stp	d10,d11,[sp,#32]
+	stp	d12,d13,[sp,#48]
+	stp	d14,d15,[sp,#64]
+
+	fmov	${H0},x10
+	fmov	${H1},x11
+	fmov	${H2},x12
+	fmov	${H3},x13
+	fmov	${H4},x14
+
+	////////////////////////////////// initialize r^n table
+	mov	$h0,$r0			// r^1
+	add	$s1,$r1,$r1,lsr#2	// s1 = r1 + (r1 >> 2)
+	mov	$h1,$r1
+	mov	$h2,xzr
+	add	$ctx,$ctx,#48+12
+	bl	poly1305_splat
+
+	bl	poly1305_mult		// r^2
+	sub	$ctx,$ctx,#4
+	bl	poly1305_splat
+
+	bl	poly1305_mult		// r^3
+	sub	$ctx,$ctx,#4
+	bl	poly1305_splat
+
+	bl	poly1305_mult		// r^4
+	sub	$ctx,$ctx,#4
+	bl	poly1305_splat
+	ldr	x30,[sp,#8]
+
+	add	$in2,$inp,#32
+	adr	$zeros,.Lzeros
+	subs	$len,$len,#64
+	csel	$in2,$zeros,$in2,lo
+
+	mov	x4,#1
+	str	x4,[$ctx,#-24]		// set is_base2_26
+	sub	$ctx,$ctx,#48		// restore original $ctx
+	b	.Ldo_neon
+
+.align	4
+.Leven_neon:
+	add	$in2,$inp,#32
+	adr	$zeros,.Lzeros
+	subs	$len,$len,#64
+	csel	$in2,$zeros,$in2,lo
+
+	stp	d8,d9,[sp,#16]		// meet ABI requirements
+	stp	d10,d11,[sp,#32]
+	stp	d12,d13,[sp,#48]
+	stp	d14,d15,[sp,#64]
+
+	fmov	${H0},x10
+	fmov	${H1},x11
+	fmov	${H2},x12
+	fmov	${H3},x13
+	fmov	${H4},x14
+
+.Ldo_neon:
+	ldp	x8,x12,[$in2],#16	// inp[2:3] (or zero)
+	ldp	x9,x13,[$in2],#48
+
+	lsl	$padbit,$padbit,#24
+	add	x15,$ctx,#48
+
+#ifdef	__ARMEB__
+	rev	x8,x8
+	rev	x12,x12
+	rev	x9,x9
+	rev	x13,x13
+#endif
+	and	x4,x8,#0x03ffffff	// base 2^64 -> base 2^26
+	and	x5,x9,#0x03ffffff
+	ubfx	x6,x8,#26,#26
+	ubfx	x7,x9,#26,#26
+	add	x4,x4,x5,lsl#32		// bfi	x4,x5,#32,#32
+	extr	x8,x12,x8,#52
+	extr	x9,x13,x9,#52
+	add	x6,x6,x7,lsl#32		// bfi	x6,x7,#32,#32
+	fmov	$IN23_0,x4
+	and	x8,x8,#0x03ffffff
+	and	x9,x9,#0x03ffffff
+	ubfx	x10,x12,#14,#26
+	ubfx	x11,x13,#14,#26
+	add	x12,$padbit,x12,lsr#40
+	add	x13,$padbit,x13,lsr#40
+	add	x8,x8,x9,lsl#32		// bfi	x8,x9,#32,#32
+	fmov	$IN23_1,x6
+	add	x10,x10,x11,lsl#32	// bfi	x10,x11,#32,#32
+	add	x12,x12,x13,lsl#32	// bfi	x12,x13,#32,#32
+	fmov	$IN23_2,x8
+	fmov	$IN23_3,x10
+	fmov	$IN23_4,x12
+
+	ldp	x8,x12,[$inp],#16	// inp[0:1]
+	ldp	x9,x13,[$inp],#48
+
+	ld1	{$R0,$R1,$S1,$R2},[x15],#64
+	ld1	{$S2,$R3,$S3,$R4},[x15],#64
+	ld1	{$S4},[x15]
+
+#ifdef	__ARMEB__
+	rev	x8,x8
+	rev	x12,x12
+	rev	x9,x9
+	rev	x13,x13
+#endif
+	and	x4,x8,#0x03ffffff	// base 2^64 -> base 2^26
+	and	x5,x9,#0x03ffffff
+	ubfx	x6,x8,#26,#26
+	ubfx	x7,x9,#26,#26
+	add	x4,x4,x5,lsl#32		// bfi	x4,x5,#32,#32
+	extr	x8,x12,x8,#52
+	extr	x9,x13,x9,#52
+	add	x6,x6,x7,lsl#32		// bfi	x6,x7,#32,#32
+	fmov	$IN01_0,x4
+	and	x8,x8,#0x03ffffff
+	and	x9,x9,#0x03ffffff
+	ubfx	x10,x12,#14,#26
+	ubfx	x11,x13,#14,#26
+	add	x12,$padbit,x12,lsr#40
+	add	x13,$padbit,x13,lsr#40
+	add	x8,x8,x9,lsl#32		// bfi	x8,x9,#32,#32
+	fmov	$IN01_1,x6
+	add	x10,x10,x11,lsl#32	// bfi	x10,x11,#32,#32
+	add	x12,x12,x13,lsl#32	// bfi	x12,x13,#32,#32
+	movi	$MASK.2d,#-1
+	fmov	$IN01_2,x8
+	fmov	$IN01_3,x10
+	fmov	$IN01_4,x12
+	ushr	$MASK.2d,$MASK.2d,#38
+
+	b.ls	.Lskip_loop
+
+.align	4
+.Loop_neon:
+	////////////////////////////////////////////////////////////////
+	// ((inp[0]*r^4+inp[2]*r^2+inp[4])*r^4+inp[6]*r^2
+	// ((inp[1]*r^4+inp[3]*r^2+inp[5])*r^3+inp[7]*r
+	//   \___________________/
+	// ((inp[0]*r^4+inp[2]*r^2+inp[4])*r^4+inp[6]*r^2+inp[8])*r^2
+	// ((inp[1]*r^4+inp[3]*r^2+inp[5])*r^4+inp[7]*r^2+inp[9])*r
+	//   \___________________/ \____________________/
+	//
+	// Note that we start with inp[2:3]*r^2. This is because it
+	// doesn't depend on reduction in previous iteration.
+	////////////////////////////////////////////////////////////////
+	// d4 = h0*r4 + h1*r3   + h2*r2   + h3*r1   + h4*r0
+	// d3 = h0*r3 + h1*r2   + h2*r1   + h3*r0   + h4*5*r4
+	// d2 = h0*r2 + h1*r1   + h2*r0   + h3*5*r4 + h4*5*r3
+	// d1 = h0*r1 + h1*r0   + h2*5*r4 + h3*5*r3 + h4*5*r2
+	// d0 = h0*r0 + h1*5*r4 + h2*5*r3 + h3*5*r2 + h4*5*r1
+
+	subs	$len,$len,#64
+	umull	$ACC4,$IN23_0,${R4}[2]
+	csel	$in2,$zeros,$in2,lo
+	umull	$ACC3,$IN23_0,${R3}[2]
+	umull	$ACC2,$IN23_0,${R2}[2]
+	 ldp	x8,x12,[$in2],#16	// inp[2:3] (or zero)
+	umull	$ACC1,$IN23_0,${R1}[2]
+	 ldp	x9,x13,[$in2],#48
+	umull	$ACC0,$IN23_0,${R0}[2]
+#ifdef	__ARMEB__
+	 rev	x8,x8
+	 rev	x12,x12
+	 rev	x9,x9
+	 rev	x13,x13
+#endif
+
+	umlal	$ACC4,$IN23_1,${R3}[2]
+	 and	x4,x8,#0x03ffffff	// base 2^64 -> base 2^26
+	umlal	$ACC3,$IN23_1,${R2}[2]
+	 and	x5,x9,#0x03ffffff
+	umlal	$ACC2,$IN23_1,${R1}[2]
+	 ubfx	x6,x8,#26,#26
+	umlal	$ACC1,$IN23_1,${R0}[2]
+	 ubfx	x7,x9,#26,#26
+	umlal	$ACC0,$IN23_1,${S4}[2]
+	 add	x4,x4,x5,lsl#32		// bfi	x4,x5,#32,#32
+
+	umlal	$ACC4,$IN23_2,${R2}[2]
+	 extr	x8,x12,x8,#52
+	umlal	$ACC3,$IN23_2,${R1}[2]
+	 extr	x9,x13,x9,#52
+	umlal	$ACC2,$IN23_2,${R0}[2]
+	 add	x6,x6,x7,lsl#32		// bfi	x6,x7,#32,#32
+	umlal	$ACC1,$IN23_2,${S4}[2]
+	 fmov	$IN23_0,x4
+	umlal	$ACC0,$IN23_2,${S3}[2]
+	 and	x8,x8,#0x03ffffff
+
+	umlal	$ACC4,$IN23_3,${R1}[2]
+	 and	x9,x9,#0x03ffffff
+	umlal	$ACC3,$IN23_3,${R0}[2]
+	 ubfx	x10,x12,#14,#26
+	umlal	$ACC2,$IN23_3,${S4}[2]
+	 ubfx	x11,x13,#14,#26
+	umlal	$ACC1,$IN23_3,${S3}[2]
+	 add	x8,x8,x9,lsl#32		// bfi	x8,x9,#32,#32
+	umlal	$ACC0,$IN23_3,${S2}[2]
+	 fmov	$IN23_1,x6
+
+	add	$IN01_2,$IN01_2,$H2
+	 add	x12,$padbit,x12,lsr#40
+	umlal	$ACC4,$IN23_4,${R0}[2]
+	 add	x13,$padbit,x13,lsr#40
+	umlal	$ACC3,$IN23_4,${S4}[2]
+	 add	x10,x10,x11,lsl#32	// bfi	x10,x11,#32,#32
+	umlal	$ACC2,$IN23_4,${S3}[2]
+	 add	x12,x12,x13,lsl#32	// bfi	x12,x13,#32,#32
+	umlal	$ACC1,$IN23_4,${S2}[2]
+	 fmov	$IN23_2,x8
+	umlal	$ACC0,$IN23_4,${S1}[2]
+	 fmov	$IN23_3,x10
+
+	////////////////////////////////////////////////////////////////
+	// (hash+inp[0:1])*r^4 and accumulate
+
+	add	$IN01_0,$IN01_0,$H0
+	 fmov	$IN23_4,x12
+	umlal	$ACC3,$IN01_2,${R1}[0]
+	 ldp	x8,x12,[$inp],#16	// inp[0:1]
+	umlal	$ACC0,$IN01_2,${S3}[0]
+	 ldp	x9,x13,[$inp],#48
+	umlal	$ACC4,$IN01_2,${R2}[0]
+	umlal	$ACC1,$IN01_2,${S4}[0]
+	umlal	$ACC2,$IN01_2,${R0}[0]
+#ifdef	__ARMEB__
+	 rev	x8,x8
+	 rev	x12,x12
+	 rev	x9,x9
+	 rev	x13,x13
+#endif
+
+	add	$IN01_1,$IN01_1,$H1
+	umlal	$ACC3,$IN01_0,${R3}[0]
+	umlal	$ACC4,$IN01_0,${R4}[0]
+	 and	x4,x8,#0x03ffffff	// base 2^64 -> base 2^26
+	umlal	$ACC2,$IN01_0,${R2}[0]
+	 and	x5,x9,#0x03ffffff
+	umlal	$ACC0,$IN01_0,${R0}[0]
+	 ubfx	x6,x8,#26,#26
+	umlal	$ACC1,$IN01_0,${R1}[0]
+	 ubfx	x7,x9,#26,#26
+
+	add	$IN01_3,$IN01_3,$H3
+	 add	x4,x4,x5,lsl#32		// bfi	x4,x5,#32,#32
+	umlal	$ACC3,$IN01_1,${R2}[0]
+	 extr	x8,x12,x8,#52
+	umlal	$ACC4,$IN01_1,${R3}[0]
+	 extr	x9,x13,x9,#52
+	umlal	$ACC0,$IN01_1,${S4}[0]
+	 add	x6,x6,x7,lsl#32		// bfi	x6,x7,#32,#32
+	umlal	$ACC2,$IN01_1,${R1}[0]
+	 fmov	$IN01_0,x4
+	umlal	$ACC1,$IN01_1,${R0}[0]
+	 and	x8,x8,#0x03ffffff
+
+	add	$IN01_4,$IN01_4,$H4
+	 and	x9,x9,#0x03ffffff
+	umlal	$ACC3,$IN01_3,${R0}[0]
+	 ubfx	x10,x12,#14,#26
+	umlal	$ACC0,$IN01_3,${S2}[0]
+	 ubfx	x11,x13,#14,#26
+	umlal	$ACC4,$IN01_3,${R1}[0]
+	 add	x8,x8,x9,lsl#32		// bfi	x8,x9,#32,#32
+	umlal	$ACC1,$IN01_3,${S3}[0]
+	 fmov	$IN01_1,x6
+	umlal	$ACC2,$IN01_3,${S4}[0]
+	 add	x12,$padbit,x12,lsr#40
+
+	umlal	$ACC3,$IN01_4,${S4}[0]
+	 add	x13,$padbit,x13,lsr#40
+	umlal	$ACC0,$IN01_4,${S1}[0]
+	 add	x10,x10,x11,lsl#32	// bfi	x10,x11,#32,#32
+	umlal	$ACC4,$IN01_4,${R0}[0]
+	 add	x12,x12,x13,lsl#32	// bfi	x12,x13,#32,#32
+	umlal	$ACC1,$IN01_4,${S2}[0]
+	 fmov	$IN01_2,x8
+	umlal	$ACC2,$IN01_4,${S3}[0]
+	 fmov	$IN01_3,x10
+	 fmov	$IN01_4,x12
+
+	/////////////////////////////////////////////////////////////////
+	// lazy reduction as discussed in "NEON crypto" by D.J. Bernstein
+	// and P. Schwabe
+	//
+	// [see discussion in poly1305-armv4 module]
+
+	ushr	$T0.2d,$ACC3,#26
+	xtn	$H3,$ACC3
+	 ushr	$T1.2d,$ACC0,#26
+	 and	$ACC0,$ACC0,$MASK.2d
+	add	$ACC4,$ACC4,$T0.2d	// h3 -> h4
+	bic	$H3,#0xfc,lsl#24	// &=0x03ffffff
+	 add	$ACC1,$ACC1,$T1.2d	// h0 -> h1
+
+	ushr	$T0.2d,$ACC4,#26
+	xtn	$H4,$ACC4
+	 ushr	$T1.2d,$ACC1,#26
+	 xtn	$H1,$ACC1
+	bic	$H4,#0xfc,lsl#24
+	 add	$ACC2,$ACC2,$T1.2d	// h1 -> h2
+
+	add	$ACC0,$ACC0,$T0.2d
+	shl	$T0.2d,$T0.2d,#2
+	 shrn	$T1.2s,$ACC2,#26
+	 xtn	$H2,$ACC2
+	add	$ACC0,$ACC0,$T0.2d	// h4 -> h0
+	 bic	$H1,#0xfc,lsl#24
+	 add	$H3,$H3,$T1.2s		// h2 -> h3
+	 bic	$H2,#0xfc,lsl#24
+
+	shrn	$T0.2s,$ACC0,#26
+	xtn	$H0,$ACC0
+	 ushr	$T1.2s,$H3,#26
+	 bic	$H3,#0xfc,lsl#24
+	 bic	$H0,#0xfc,lsl#24
+	add	$H1,$H1,$T0.2s		// h0 -> h1
+	 add	$H4,$H4,$T1.2s		// h3 -> h4
+
+	b.hi	.Loop_neon
+
+.Lskip_loop:
+	dup	$IN23_2,${IN23_2}[0]
+	add	$IN01_2,$IN01_2,$H2
+
+	////////////////////////////////////////////////////////////////
+	// multiply (inp[0:1]+hash) or inp[2:3] by r^2:r^1
+
+	adds	$len,$len,#32
+	b.ne	.Long_tail
+
+	dup	$IN23_2,${IN01_2}[0]
+	add	$IN23_0,$IN01_0,$H0
+	add	$IN23_3,$IN01_3,$H3
+	add	$IN23_1,$IN01_1,$H1
+	add	$IN23_4,$IN01_4,$H4
+
+.Long_tail:
+	dup	$IN23_0,${IN23_0}[0]
+	umull2	$ACC0,$IN23_2,${S3}
+	umull2	$ACC3,$IN23_2,${R1}
+	umull2	$ACC4,$IN23_2,${R2}
+	umull2	$ACC2,$IN23_2,${R0}
+	umull2	$ACC1,$IN23_2,${S4}
+
+	dup	$IN23_1,${IN23_1}[0]
+	umlal2	$ACC0,$IN23_0,${R0}
+	umlal2	$ACC2,$IN23_0,${R2}
+	umlal2	$ACC3,$IN23_0,${R3}
+	umlal2	$ACC4,$IN23_0,${R4}
+	umlal2	$ACC1,$IN23_0,${R1}
+
+	dup	$IN23_3,${IN23_3}[0]
+	umlal2	$ACC0,$IN23_1,${S4}
+	umlal2	$ACC3,$IN23_1,${R2}
+	umlal2	$ACC2,$IN23_1,${R1}
+	umlal2	$ACC4,$IN23_1,${R3}
+	umlal2	$ACC1,$IN23_1,${R0}
+
+	dup	$IN23_4,${IN23_4}[0]
+	umlal2	$ACC3,$IN23_3,${R0}
+	umlal2	$ACC4,$IN23_3,${R1}
+	umlal2	$ACC0,$IN23_3,${S2}
+	umlal2	$ACC1,$IN23_3,${S3}
+	umlal2	$ACC2,$IN23_3,${S4}
+
+	umlal2	$ACC3,$IN23_4,${S4}
+	umlal2	$ACC0,$IN23_4,${S1}
+	umlal2	$ACC4,$IN23_4,${R0}
+	umlal2	$ACC1,$IN23_4,${S2}
+	umlal2	$ACC2,$IN23_4,${S3}
+
+	b.eq	.Lshort_tail
+
+	////////////////////////////////////////////////////////////////
+	// (hash+inp[0:1])*r^4:r^3 and accumulate
+
+	add	$IN01_0,$IN01_0,$H0
+	umlal	$ACC3,$IN01_2,${R1}
+	umlal	$ACC0,$IN01_2,${S3}
+	umlal	$ACC4,$IN01_2,${R2}
+	umlal	$ACC1,$IN01_2,${S4}
+	umlal	$ACC2,$IN01_2,${R0}
+
+	add	$IN01_1,$IN01_1,$H1
+	umlal	$ACC3,$IN01_0,${R3}
+	umlal	$ACC0,$IN01_0,${R0}
+	umlal	$ACC4,$IN01_0,${R4}
+	umlal	$ACC1,$IN01_0,${R1}
+	umlal	$ACC2,$IN01_0,${R2}
+
+	add	$IN01_3,$IN01_3,$H3
+	umlal	$ACC3,$IN01_1,${R2}
+	umlal	$ACC0,$IN01_1,${S4}
+	umlal	$ACC4,$IN01_1,${R3}
+	umlal	$ACC1,$IN01_1,${R0}
+	umlal	$ACC2,$IN01_1,${R1}
+
+	add	$IN01_4,$IN01_4,$H4
+	umlal	$ACC3,$IN01_3,${R0}
+	umlal	$ACC0,$IN01_3,${S2}
+	umlal	$ACC4,$IN01_3,${R1}
+	umlal	$ACC1,$IN01_3,${S3}
+	umlal	$ACC2,$IN01_3,${S4}
+
+	umlal	$ACC3,$IN01_4,${S4}
+	umlal	$ACC0,$IN01_4,${S1}
+	umlal	$ACC4,$IN01_4,${R0}
+	umlal	$ACC1,$IN01_4,${S2}
+	umlal	$ACC2,$IN01_4,${S3}
+
+.Lshort_tail:
+	////////////////////////////////////////////////////////////////
+	// horizontal add
+
+	addp	$ACC3,$ACC3,$ACC3
+	 ldp	d8,d9,[sp,#16]		// meet ABI requirements
+	addp	$ACC0,$ACC0,$ACC0
+	 ldp	d10,d11,[sp,#32]
+	addp	$ACC4,$ACC4,$ACC4
+	 ldp	d12,d13,[sp,#48]
+	addp	$ACC1,$ACC1,$ACC1
+	 ldp	d14,d15,[sp,#64]
+	addp	$ACC2,$ACC2,$ACC2
+
+	////////////////////////////////////////////////////////////////
+	// lazy reduction, but without narrowing
+
+	ushr	$T0.2d,$ACC3,#26
+	and	$ACC3,$ACC3,$MASK.2d
+	 ushr	$T1.2d,$ACC0,#26
+	 and	$ACC0,$ACC0,$MASK.2d
+
+	add	$ACC4,$ACC4,$T0.2d	// h3 -> h4
+	 add	$ACC1,$ACC1,$T1.2d	// h0 -> h1
+
+	ushr	$T0.2d,$ACC4,#26
+	and	$ACC4,$ACC4,$MASK.2d
+	 ushr	$T1.2d,$ACC1,#26
+	 and	$ACC1,$ACC1,$MASK.2d
+	 add	$ACC2,$ACC2,$T1.2d	// h1 -> h2
+
+	add	$ACC0,$ACC0,$T0.2d
+	shl	$T0.2d,$T0.2d,#2
+	 ushr	$T1.2d,$ACC2,#26
+	 and	$ACC2,$ACC2,$MASK.2d
+	add	$ACC0,$ACC0,$T0.2d	// h4 -> h0
+	 add	$ACC3,$ACC3,$T1.2d	// h2 -> h3
+
+	ushr	$T0.2d,$ACC0,#26
+	and	$ACC0,$ACC0,$MASK.2d
+	 ushr	$T1.2d,$ACC3,#26
+	 and	$ACC3,$ACC3,$MASK.2d
+	add	$ACC1,$ACC1,$T0.2d	// h0 -> h1
+	 add	$ACC4,$ACC4,$T1.2d	// h3 -> h4
+
+	////////////////////////////////////////////////////////////////
+	// write the result, can be partially reduced
+
+	st4	{$ACC0,$ACC1,$ACC2,$ACC3}[0],[$ctx],#16
+	st1	{$ACC4}[0],[$ctx]
+
+.Lno_data_neon:
+	ldr	x29,[sp],#80
+	ret
+.size	poly1305_blocks_neon,.-poly1305_blocks_neon
+
+.type	poly1305_emit_neon,%function
+.align	5
+poly1305_emit_neon:
+	ldr	$is_base2_26,[$ctx,#24]
+	cbz	$is_base2_26,poly1305_emit
+
+	ldp	w10,w11,[$ctx]		// load hash value base 2^26
+	ldp	w12,w13,[$ctx,#8]
+	ldr	w14,[$ctx,#16]
+
+	add	$h0,x10,x11,lsl#26	// base 2^26 -> base 2^64
+	lsr	$h1,x12,#12
+	adds	$h0,$h0,x12,lsl#52
+	add	$h1,$h1,x13,lsl#14
+	adc	$h1,$h1,xzr
+	lsr	$h2,x14,#24
+	adds	$h1,$h1,x14,lsl#40
+	adc	$h2,$h2,xzr		// can be partially reduced...
+
+	ldp	$t0,$t1,[$nonce]	// load nonce
+
+	and	$d0,$h2,#-4		// ... so reduce
+	add	$d0,$d0,$h2,lsr#2
+	and	$h2,$h2,#3
+	adds	$h0,$h0,$d0
+	adcs	$h1,$h1,xzr
+	adc	$h2,$h2,xzr
+
+	adds	$d0,$h0,#5		// compare to modulus
+	adcs	$d1,$h1,xzr
+	adc	$d2,$h2,xzr
+
+	tst	$d2,#-4			// see if it's carried/borrowed
+
+	csel	$h0,$h0,$d0,eq
+	csel	$h1,$h1,$d1,eq
+
+#ifdef	__ARMEB__
+	ror	$t0,$t0,#32		// flip nonce words
+	ror	$t1,$t1,#32
+#endif
+	adds	$h0,$h0,$t0		// accumulate nonce
+	adc	$h1,$h1,$t1
+#ifdef	__ARMEB__
+	rev	$h0,$h0			// flip output bytes
+	rev	$h1,$h1
+#endif
+	stp	$h0,$h1,[$mac]		// write result
+
+	ret
+.size	poly1305_emit_neon,.-poly1305_emit_neon
+
+.align	5
+.Lzeros:
+.long	0,0,0,0,0,0,0,0
+.LOPENSSL_armcap_P:
+#ifdef	__ILP32__
+.long	OPENSSL_armcap_P-.
+#else
+.quad	OPENSSL_armcap_P-.
+#endif
+.asciz	"Poly1305 for ARMv8, CRYPTOGAMS by "
+.align	2
+___
+
+foreach (split("\n",$code)) {
+	s/\b(shrn\s+v[0-9]+)\.[24]d/$1.2s/			or
+	s/\b(fmov\s+)v([0-9]+)[^,]*,\s*x([0-9]+)/$1d$2,x$3/	or
+	(m/\bdup\b/ and (s/\.[24]s/.2d/g or 1))			or
+	(m/\b(eor|and)/ and (s/\.[248][sdh]/.16b/g or 1))	or
+	(m/\bum(ul|la)l\b/ and (s/\.4s/.2s/g or 1))		or
+	(m/\bum(ul|la)l2\b/ and (s/\.2s/.4s/g or 1))		or
+	(m/\bst[1-4]\s+{[^}]+}\[/ and (s/\.[24]d/.s/g or 1));
+
+	s/\.[124]([sd])\[/.$1\[/;
+
+	print $_,"\n";
+}
+close STDOUT;
diff --git a/crypto/poly1305/asm/poly1305-c64xplus.pl b/crypto/poly1305/asm/poly1305-c64xplus.pl
new file mode 100755
index 000000000..93fef37e6
--- /dev/null
+++ b/crypto/poly1305/asm/poly1305-c64xplus.pl
@@ -0,0 +1,331 @@
+#! /usr/bin/env perl
+# Copyright 2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+#
+# ====================================================================
+# Written by Andy Polyakov  for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+#
+# Poly1305 hash for C64x+.
+#
+# October 2015
+#
+# Performance is [incredible for a 32-bit processor] 1.82 cycles per
+# processed byte. Comparison to compiler-generated code is problematic,
+# because results were observed to vary from 2.1 to 7.6 cpb depending
+# on compiler's ability to inline small functions. Compiler also
+# disables interrupts for some reason, thus making interrupt response
+# time dependent on input length. This module on the other hand is free
+# from such limitation.
+
+$output=pop;
+open STDOUT,">$output";
+
+($CTXA,$INPB,$LEN,$PADBIT)=("A4","B4","A6","B6");
+($H0,$H1,$H2,$H3,$H4,$H4a)=("A8","B8","A10","B10","B2",$LEN);
+($D0,$D1,$D2,$D3)=         ("A9","B9","A11","B11");
+($R0,$R1,$R2,$R3,$S1,$S2,$S3,$S3b)=("A0","B0","A1","B1","A12","B12","A13","B13");
+($THREE,$R0b,$S2a)=("B7","B5","A5");
+
+$code.=<<___;
+	.text
+
+	.if	.ASSEMBLER_VERSION<7000000
+	.asg	0,__TI_EABI__
+	.endif
+	.if	__TI_EABI__
+	.asg	poly1305_init,_poly1305_init
+	.asg	poly1305_blocks,_poly1305_blocks
+	.asg	poly1305_emit,_poly1305_emit
+	.endif
+
+	.asg	B3,RA
+	.asg	A15,FP
+	.asg	B15,SP
+
+	.if	.LITTLE_ENDIAN
+	.asg	MV,SWAP2
+	.asg	MV.L,SWAP4
+	.endif
+
+	.global	_poly1305_init
+_poly1305_init:
+	.asmfunc
+	LDNDW	*${INPB}[0],B17:B16	; load key material
+	LDNDW	*${INPB}[1],A17:A16
+
+||	ZERO	B9:B8
+||	MVK	-1,B0
+	STDW	B9:B8,*${CTXA}[0]	; initialize h1:h0
+||	SHRU	B0,4,B0			; 0x0fffffff
+||	MVK	-4,B1
+	STDW	B9:B8,*${CTXA}[1]	; initialize h3:h2
+||	AND	B0,B1,B1		; 0x0ffffffc
+	STW	B8,*${CTXA}[4]		; initialize h4
+
+	.if	.BIG_ENDIAN
+	SWAP2	B16,B17
+||	SWAP2	B17,B16
+	SWAP2	A16,A17
+||	SWAP2	A17,A16
+	SWAP4	B16,B16
+||	SWAP4	A16,A16
+	SWAP4	B17,B17
+||	SWAP4	A17,A17
+	.endif
+
+	AND	B16,B0,B20		; r0 = key[0] & 0x0fffffff
+||	AND	B17,B1,B22		; r1 = key[1] & 0x0ffffffc
+||	EXTU	B17,4,6,B16		; r1>>2
+	AND	A16,B1,B21		; r2 = key[2] & 0x0ffffffc
+||	AND	A17,B1,A23		; r3 = key[3] & 0x0ffffffc
+||	BNOP	RA
+	SHRU	B21,2,B18
+||	ADD	B22,B16,B16		; s1 = r1 + r1>>2
+
+	STDW	B21:B20,*${CTXA}[3]	; save r2:r0
+||	ADD	B21,B18,B18		; s2 = r2 + r2>>2
+||	SHRU	A23,2,B17
+||	MV	A23,B23
+	STDW	B23:B22,*${CTXA}[4]	; save r3:r1
+||	ADD	B23,B17,B19		; s3 = r3 + r3>>2
+||	ADD	B23,B17,B17		; s3 = r3 + r3>>2
+	STDW	B17:B16,*${CTXA}[5]	; save s3:s1
+	STDW	B19:B18,*${CTXA}[6]	; save s3:s2
+||	ZERO	A4			; return 0
+	.endasmfunc
+
+	.global	_poly1305_blocks
+	.align	32
+_poly1305_blocks:
+	.asmfunc	stack_usage(40)
+	SHRU	$LEN,4,A2		; A2 is loop counter, number of blocks
+  [!A2]	BNOP	RA			; no data
+|| [A2]	STW	FP,*SP--(40)		; save frame pointer and alloca(40)
+|| [A2]	MV	SP,FP
+   [A2]	STDW	B13:B12,*SP[4]		; ABI says so
+|| [A2]	MV	$CTXA,$S3b		; borrow $S3b
+   [A2]	STDW	B11:B10,*SP[3]
+|| [A2]	STDW	A13:A12,*FP[-3]
+   [A2]	STDW	A11:A10,*FP[-4]
+
+|| [A2]	LDDW	*${S3b}[0],B25:B24	; load h1:h0
+   [A2]	LDNW	*${INPB}++[4],$D0	; load inp[0]
+   [A2]	LDNW	*${INPB}[-3],$D1	; load inp[1]
+
+	LDDW	*${CTXA}[1],B29:B28	; load h3:h2, B28 is h2
+	LDNW	*${INPB}[-2],$D2	; load inp[2]
+	LDNW	*${INPB}[-1],$D3	; load inp[3]
+
+	LDDW	*${CTXA}[3],$R2:$R0	; load r2:r0
+||	LDDW	*${S3b}[4],$R3:$R1	; load r3:r1
+||	SWAP2	$D0,$D0
+
+	LDDW	*${CTXA}[5],$S3:$S1	; load s3:s1
+||	LDDW	*${S3b}[6],$S3b:$S2	; load s3:s2
+||	SWAP4	$D0,$D0
+||	SWAP2	$D1,$D1
+
+	ADDU	$D0,B24,$D0:$H0		; h0+=inp[0]
+||	ADD	$D0,B24,B27		; B-copy of h0+inp[0]
+||	SWAP4	$D1,$D1
+	ADDU	$D1,B25,$D1:$H1		; h1+=inp[1]
+||	MVK	3,$THREE
+||	SWAP2	$D2,$D2
+	LDW	*${CTXA}[4],$H4		; load h4
+||	SWAP4	$D2,$D2
+||	MV	B29,B30			; B30 is h3
+	MV	$R0,$R0b
+
+loop?:
+	MPY32U	$H0,$R0,A17:A16
+||	MPY32U	B27,$R1,B17:B16		; MPY32U	$H0,$R1,B17:B16
+||	ADDU	$D0,$D1:$H1,B25:B24	; ADDU		$D0,$D1:$H1,$D1:$H1
+||	ADDU	$D2,B28,$D2:$H2		; h2+=inp[2]
+||	SWAP2	$D3,$D3
+	MPY32U	$H0,$R2,A19:A18
+||	MPY32U	B27,$R3,B19:B18		; MPY32U	$H0,$R3,B19:B18
+||	ADD	$D0,$H1,A24		; A-copy of B24
+||	SWAP4	$D3,$D3
+|| [A2]	SUB	A2,1,A2			; decrement loop counter
+
+	MPY32U	A24,$S3,A21:A20		; MPY32U	$H1,$S3,A21:A20
+||	MPY32U	B24,$R0b,B21:B20	; MPY32U	$H1,$R0,B21:B20
+||	ADDU	B25,$D2:$H2,$D2:$H2	; ADDU		$D1,$D2:$H2,$D2:$H2
+||	ADDU	$D3,B30,$D3:$H3		; h3+=inp[3]
+||	ADD	B25,$H2,B25		; B-copy of $H2
+	MPY32U	A24,$R1,A23:A22		; MPY32U	$H1,$R1,A23:A22
+||	MPY32U	B24,$R2,B23:B22		; MPY32U	$H1,$R2,B23:B22
+
+	MPY32U	$H2,$S2,A25:A24
+||	MPY32U	B25,$S3b,B25:B24	; MPY32U	$H2,$S3,B25:B24
+||	ADDU	$D2,$D3:$H3,$D3:$H3
+||	ADD	$PADBIT,$H4,$H4		; h4+=padbit
+	MPY32U	$H2,$R0,A27:A26
+||	MPY32U	$H2,$R1,B27:B26
+||	ADD	$D3,$H4,$H4
+||	MV	$S2,$S2a
+
+	MPY32U	$H3,$S1,A29:A28
+||	MPY32U	$H3,$S2,B29:B28
+||	ADD	A21,A17,A21		; start accumulating "d3:d0"
+||	ADD	B21,B17,B21
+||	ADDU	A20,A16,A17:A16
+||	ADDU	B20,B16,B17:B16
+|| [A2]	LDNW	*${INPB}++[4],$D0	; load inp[0]
+	MPY32U	$H3,$S3,A31:A30
+||	MPY32U	$H3,$R0b,B31:B30
+||	ADD	A23,A19,A23
+||	ADD	B23,B19,B23
+||	ADDU	A22,A18,A19:A18
+||	ADDU	B22,B18,B19:B18
+|| [A2]	LDNW	*${INPB}[-3],$D1	; load inp[1]
+
+	MPY32	$H4,$S1,B20
+||	MPY32	$H4,$S2a,A20
+||	ADD	A25,A21,A21
+||	ADD	B25,B21,B21
+||	ADDU	A24,A17:A16,A17:A16
+||	ADDU	B24,B17:B16,B17:B16
+|| [A2]	LDNW	*${INPB}[-2],$D2	; load inp[2]
+	MPY32	$H4,$S3b,B22
+||	ADD	A27,A23,A23
+||	ADD	B27,B23,B23
+||	ADDU	A26,A19:A18,A19:A18
+||	ADDU	B26,B19:B18,B19:B18
+|| [A2]	LDNW	*${INPB}[-1],$D3	; load inp[3]
+
+	MPY32	$H4,$R0b,$H4
+||	ADD	A29,A21,A21		; final hi("d0")
+||	ADD	B29,B21,B21		; final hi("d1")
+||	ADDU	A28,A17:A16,A17:A16	; final lo("d0")
+||	ADDU	B28,B17:B16,B17:B16
+	ADD	A31,A23,A23		; final hi("d2")
+||	ADD	B31,B23,B23		; final hi("d3")
+||	ADDU	A30,A19:A18,A19:A18
+||	ADDU	B30,B19:B18,B19:B18
+	ADDU	B20,B17:B16,B17:B16	; final lo("d1")
+||	ADDU	A20,A19:A18,A19:A18	; final lo("d2")
+	ADDU	B22,B19:B18,B19:B18	; final lo("d3")
+
+||	ADD	A17,A21,A21		; "flatten" "d3:d0"
+	MV	A19,B29			; move to avoid cross-path stalls
+	ADDU	A21,B17:B16,B27:B26	; B26 is h1
+	ADD	B21,B27,B27
+||	DMV	B29,A18,B29:B28		; move to avoid cross-path stalls
+	ADDU	B27,B29:B28,B29:B28	; B28 is h2
+|| [A2]	SWAP2	$D0,$D0
+	ADD	A23,B29,B29
+|| [A2]	SWAP4	$D0,$D0
+	ADDU	B29,B19:B18,B31:B30	; B30 is h3
+	ADD	B23,B31,B31
+||	MV	A16,B24			; B24 is h0
+|| [A2]	SWAP2	$D1,$D1
+	ADD	B31,$H4,$H4
+|| [A2]	SWAP4	$D1,$D1
+
+	SHRU	$H4,2,B16		; last reduction step
+||	AND	$H4,$THREE,$H4
+	ADDAW	B16,B16,B16		; 5*(h4>>2)
+|| [A2]	BNOP	loop?
+
+	ADDU	B24,B16,B25:B24		; B24 is h0
+|| [A2]	SWAP2	$D2,$D2
+	ADDU	B26,B25,B27:B26		; B26 is h1
+|| [A2]	SWAP4	$D2,$D2
+	ADDU	B28,B27,B29:B28		; B28 is h2
+|| [A2]	ADDU	$D0,B24,$D0:$H0		; h0+=inp[0]
+|| [A2]	ADD	$D0,B24,B27		; B-copy of h0+inp[0]
+	ADDU	B30,B29,B31:B30		; B30 is h3
+	ADD	B31,$H4,$H4
+|| [A2]	ADDU	$D1,B26,$D1:$H1		; h1+=inp[1]
+;;===== branch to loop? is taken here
+
+	LDDW	*FP[-4],A11:A10		; ABI says so
+	LDDW	*FP[-3],A13:A12
+||	LDDW	*SP[3],B11:B10
+	LDDW	*SP[4],B13:B12
+||	MV	B26,B25
+||	BNOP	RA
+	LDW	*++SP(40),FP		; restore frame pointer
+||	MV	B30,B29
+	STDW	B25:B24,*${CTXA}[0]	; save h1:h0
+	STDW	B29:B28,*${CTXA}[1]	; save h3:h2
+	STW	$H4,*${CTXA}[4]		; save h4
+	NOP	1
+	.endasmfunc
+___
+{
+my ($MAC,$NONCEA,$NONCEB)=($INPB,$LEN,$PADBIT);
+
+$code.=<<___;
+	.global	_poly1305_emit
+	.align	32
+_poly1305_emit:
+	.asmfunc
+	LDDW	*${CTXA}[0],A17:A16	; load h1:h0
+	LDDW	*${CTXA}[1],A19:A18	; load h3:h2
+	LDW	*${CTXA}[4],A20		; load h4
+	MV	$NONCEA,$NONCEB
+
+	MVK	5,A22			; compare to modulus
+	ADDU	A16,A22,A23:A22
+||	LDW	*${NONCEA}[0],A8
+||	LDW	*${NONCEB}[1],B8
+	ADDU	A17,A23,A25:A24
+||	LDW	*${NONCEA}[2],A9
+||	LDW	*${NONCEB}[3],B9
+	ADDU	A19,A25,A27:A26
+	ADDU	A19,A27,A29:A28
+	ADD	A20,A29,A29
+
+	SHRU	A29,2,A2		; check for overflow in 130-th bit
+
+   [A2]	MV	A22,A16			; select
+|| [A2]	MV	A24,A17
+   [A2]	MV	A26,A18
+|| [A2]	MV	A28,A19
+
+||	ADDU	A8,A16,A23:A22		; accumulate nonce
+	ADDU	B8,A17,A25:A24
+||	SWAP2	A22,A22
+	ADDU	A23,A25:A24,A25:A24
+	ADDU	A9,A18,A27:A26
+||	SWAP2	A24,A24
+	ADDU	A25,A27:A26,A27:A26
+||	ADD	B9,A19,A28
+	ADD	A27,A28,A28
+||	SWAP2	A26,A26
+
+	.if	.BIG_ENDIAN
+	SWAP2	A28,A28
+||	SWAP4	A22,A22
+||	SWAP4	A24,B24
+	SWAP4	A26,A26
+	SWAP4	A28,A28
+||	MV	B24,A24
+	.endif
+
+	BNOP	RA,1
+	STNW	A22,*${MAC}[0]		; write the result
+	STNW	A24,*${MAC}[1]
+	STNW	A26,*${MAC}[2]
+	STNW	A28,*${MAC}[3]
+	.endasmfunc
+___
+}
+$code.=<<___;
+	.sect	.const
+	.cstring "Poly1305 for C64x+, CRYPTOGAMS by "
+	.align	4
+___
+
+print $code;
diff --git a/crypto/poly1305/asm/poly1305-mips.pl b/crypto/poly1305/asm/poly1305-mips.pl
new file mode 100755
index 000000000..d2b3e90d9
--- /dev/null
+++ b/crypto/poly1305/asm/poly1305-mips.pl
@@ -0,0 +1,425 @@
+#! /usr/bin/env perl
+# Copyright 2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+
+# ====================================================================
+# Written by Andy Polyakov  for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+
+# Poly1305 hash for MIPS64.
+#
+# May 2016
+#
+# Numbers are cycles per processed byte with poly1305_blocks alone.
+#
+#		IALU/gcc
+# R1x000	5.64/+120%	(big-endian)
+# Octeon II	3.80/+280%	(little-endian)
+
+######################################################################
+# There is a number of MIPS ABI in use, O32 and N32/64 are most
+# widely used. Then there is a new contender: NUBI. It appears that if
+# one picks the latter, it's possible to arrange code in ABI neutral
+# manner. Therefore let's stick to NUBI register layout:
+#
+($zero,$at,$t0,$t1,$t2)=map("\$$_",(0..2,24,25));
+($a0,$a1,$a2,$a3,$a4,$a5,$a6,$a7)=map("\$$_",(4..11));
+($s0,$s1,$s2,$s3,$s4,$s5,$s6,$s7,$s8,$s9,$s10,$s11)=map("\$$_",(12..23));
+($gp,$tp,$sp,$fp,$ra)=map("\$$_",(3,28..31));
+#
+# The return value is placed in $a0. Following coding rules facilitate
+# interoperability:
+#
+# - never ever touch $tp, "thread pointer", former $gp [o32 can be
+#   excluded from the rule, because it's specified volatile];
+# - copy return value to $t0, former $v0 [or to $a0 if you're adapting
+#   old code];
+# - on O32 populate $a4-$a7 with 'lw $aN,4*N($sp)' if necessary;
+#
+# For reference here is register layout for N32/64 MIPS ABIs:
+#
+# ($zero,$at,$v0,$v1)=map("\$$_",(0..3));
+# ($a0,$a1,$a2,$a3,$a4,$a5,$a6,$a7)=map("\$$_",(4..11));
+# ($t0,$t1,$t2,$t3,$t8,$t9)=map("\$$_",(12..15,24,25));
+# ($s0,$s1,$s2,$s3,$s4,$s5,$s6,$s7)=map("\$$_",(16..23));
+# ($gp,$sp,$fp,$ra)=map("\$$_",(28..31));
+#
+# 
+#
+######################################################################
+
+$flavour = shift || "o32"; # supported flavours are o32,n32,64,nubi32,nubi64
+
+die "MIPS64 only" unless ($flavour =~ /64|n32/i);
+
+$v0 = ($flavour =~ /nubi/i) ? $a0 : $t0;
+$SAVED_REGS_MASK = ($flavour =~ /nubi/i) ? "0x0003f000" : "0x00030000";
+
+($ctx,$inp,$len,$padbit) = ($a0,$a1,$a2,$a3);
+($in0,$in1,$tmp0,$tmp1,$tmp2,$tmp3,$tmp4) = ($a4,$a5,$a6,$a7,$at,$t0,$t1);
+
+$code.=<<___;
+#ifdef MIPSEB
+# define MSB 0
+# define LSB 7
+#else
+# define MSB 7
+# define LSB 0
+#endif
+
+.text
+.set	noat
+.set	noreorder
+
+.align	5
+.globl	poly1305_init
+.ent	poly1305_init
+poly1305_init:
+	.frame	$sp,0,$ra
+	.set	reorder
+
+	sd	$zero,0($ctx)
+	sd	$zero,8($ctx)
+	sd	$zero,16($ctx)
+
+	beqz	$inp,.Lno_key
+
+	ldl	$in0,0+MSB($inp)
+	ldl	$in1,8+MSB($inp)
+	ldr	$in0,0+LSB($inp)
+	ldr	$in1,8+LSB($inp)
+#ifdef	MIPSEB
+# if defined(_MIPS_ARCH_MIPS64R2)
+	dsbh	$in0,$in0		# byte swap
+	 dsbh	$in1,$in1
+	dshd	$in0,$in0
+	 dshd	$in1,$in1
+# else
+	ori	$tmp0,$zero,0xFF
+	dsll	$tmp2,$tmp0,32
+	or	$tmp0,$tmp2		# 0x000000FF000000FF
+
+	and	$tmp1,$in0,$tmp0	# byte swap
+	 and	$tmp3,$in1,$tmp0
+	dsrl	$tmp2,$in0,24
+	 dsrl	$tmp4,$in1,24
+	dsll	$tmp1,24
+	 dsll	$tmp3,24
+	and	$tmp2,$tmp0
+	 and	$tmp4,$tmp0
+	dsll	$tmp0,8			# 0x0000FF000000FF00
+	or	$tmp1,$tmp2
+	 or	$tmp3,$tmp4
+	and	$tmp2,$in0,$tmp0
+	 and	$tmp4,$in1,$tmp0
+	dsrl	$in0,8
+	 dsrl	$in1,8
+	dsll	$tmp2,8
+	 dsll	$tmp4,8
+	and	$in0,$tmp0
+	 and	$in1,$tmp0
+	or	$tmp1,$tmp2
+	 or	$tmp3,$tmp4
+	or	$in0,$tmp1
+	 or	$in1,$tmp3
+	dsrl	$tmp1,$in0,32
+	 dsrl	$tmp3,$in1,32
+	dsll	$in0,32
+	 dsll	$in1,32
+	or	$in0,$tmp1
+	 or	$in1,$tmp3
+# endif
+#endif
+	li	$tmp0,1
+	dsll	$tmp0,32
+	daddiu	$tmp0,-63
+	dsll	$tmp0,28
+	daddiu	$tmp0,-1		# 0ffffffc0fffffff
+
+	and	$in0,$tmp0
+	daddiu	$tmp0,-3		# 0ffffffc0ffffffc
+	and	$in1,$tmp0
+
+	sd	$in0,24($ctx)
+	dsrl	$tmp0,$in1,2
+	sd	$in1,32($ctx)
+	daddu	$tmp0,$in1		# s1 = r1 + (r1 >> 2)
+	sd	$tmp0,40($ctx)
+
+.Lno_key:
+	li	$v0,0			# return 0
+	jr	$ra
+.end	poly1305_init
+___
+{
+my ($h0,$h1,$h2,$r0,$r1,$s1,$d0,$d1,$d2) =
+   ($s0,$s1,$s2,$s3,$s4,$s5,$in0,$in1,$t2);
+
+$code.=<<___;
+.align	5
+.globl	poly1305_blocks
+.ent	poly1305_blocks
+poly1305_blocks:
+	.set	noreorder
+	dsrl	$len,4			# number of complete blocks
+	bnez	$len,poly1305_blocks_internal
+	nop
+	jr	$ra
+	nop
+.end	poly1305_blocks
+
+.align	5
+.ent	poly1305_blocks_internal
+poly1305_blocks_internal:
+	.frame	$sp,6*8,$ra
+	.mask	$SAVED_REGS_MASK,-8
+	.set	noreorder
+	dsub	$sp,6*8
+	sd	$s5,40($sp)
+	sd	$s4,32($sp)
+___
+$code.=<<___ if ($flavour =~ /nubi/i);	# optimize non-nubi prologue
+	sd	$s3,24($sp)
+	sd	$s2,16($sp)
+	sd	$s1,8($sp)
+	sd	$s0,0($sp)
+___
+$code.=<<___;
+	.set	reorder
+
+	ld	$h0,0($ctx)		# load hash value
+	ld	$h1,8($ctx)
+	ld	$h2,16($ctx)
+
+	ld	$r0,24($ctx)		# load key
+	ld	$r1,32($ctx)
+	ld	$s1,40($ctx)
+
+.Loop:
+	ldl	$in0,0+MSB($inp)	# load input
+	ldl	$in1,8+MSB($inp)
+	ldr	$in0,0+LSB($inp)
+	daddiu	$len,-1
+	ldr	$in1,8+LSB($inp)
+	daddiu	$inp,16
+#ifdef	MIPSEB
+# if defined(_MIPS_ARCH_MIPS64R2)
+	dsbh	$in0,$in0		# byte swap
+	 dsbh	$in1,$in1
+	dshd	$in0,$in0
+	 dshd	$in1,$in1
+# else
+	ori	$tmp0,$zero,0xFF
+	dsll	$tmp2,$tmp0,32
+	or	$tmp0,$tmp2		# 0x000000FF000000FF
+
+	and	$tmp1,$in0,$tmp0	# byte swap
+	 and	$tmp3,$in1,$tmp0
+	dsrl	$tmp2,$in0,24
+	 dsrl	$tmp4,$in1,24
+	dsll	$tmp1,24
+	 dsll	$tmp3,24
+	and	$tmp2,$tmp0
+	 and	$tmp4,$tmp0
+	dsll	$tmp0,8			# 0x0000FF000000FF00
+	or	$tmp1,$tmp2
+	 or	$tmp3,$tmp4
+	and	$tmp2,$in0,$tmp0
+	 and	$tmp4,$in1,$tmp0
+	dsrl	$in0,8
+	 dsrl	$in1,8
+	dsll	$tmp2,8
+	 dsll	$tmp4,8
+	and	$in0,$tmp0
+	 and	$in1,$tmp0
+	or	$tmp1,$tmp2
+	 or	$tmp3,$tmp4
+	or	$in0,$tmp1
+	 or	$in1,$tmp3
+	dsrl	$tmp1,$in0,32
+	 dsrl	$tmp3,$in1,32
+	dsll	$in0,32
+	 dsll	$in1,32
+	or	$in0,$tmp1
+	 or	$in1,$tmp3
+# endif
+#endif
+	daddu	$h0,$in0		# accumulate input
+	daddu	$h1,$in1
+	sltu	$tmp0,$h0,$in0
+	sltu	$tmp1,$h1,$in1
+	daddu	$h1,$tmp0
+
+	dmultu	$r0,$h0			# h0*r0
+	 daddu	$h2,$padbit
+	 sltu	$tmp0,$h1,$tmp0
+	mflo	$d0
+	mfhi	$d1
+
+	dmultu	$s1,$h1			# h1*5*r1
+	 daddu	$tmp0,$tmp1
+	 daddu	$h2,$tmp0
+	mflo	$tmp0
+	mfhi	$tmp1
+
+	dmultu	$r1,$h0			# h0*r1
+	 daddu	$d0,$tmp0
+	 daddu	$d1,$tmp1
+	mflo	$tmp2
+	mfhi	$d2
+	 sltu	$tmp0,$d0,$tmp0
+	 daddu	$d1,$tmp0
+
+	dmultu	$r0,$h1			# h1*r0
+	 daddu	$d1,$tmp2
+	 sltu	$tmp2,$d1,$tmp2
+	mflo	$tmp0
+	mfhi	$tmp1
+	 daddu	$d2,$tmp2
+
+	dmultu	$s1,$h2			# h2*5*r1
+	 daddu	$d1,$tmp0
+	 daddu	$d2,$tmp1
+	mflo	$tmp2
+
+	dmultu	$r0,$h2			# h2*r0
+	 sltu	$tmp0,$d1,$tmp0
+	 daddu	$d2,$tmp0
+	mflo	$tmp3
+
+	daddu	$d1,$tmp2
+	daddu	$d2,$tmp3
+	sltu	$tmp2,$d1,$tmp2
+	daddu	$d2,$tmp2
+
+	li	$tmp0,-4		# final reduction
+	and	$tmp0,$d2
+	dsrl	$tmp1,$d2,2
+	andi	$h2,$d2,3
+	daddu	$tmp0,$tmp1
+	daddu	$h0,$d0,$tmp0
+	sltu	$tmp0,$h0,$tmp0
+	daddu	$h1,$d1,$tmp0
+	sltu	$tmp0,$h1,$tmp0
+	daddu	$h2,$h2,$tmp0
+
+	bnez	$len,.Loop
+
+	sd	$h0,0($ctx)		# store hash value
+	sd	$h1,8($ctx)
+	sd	$h2,16($ctx)
+
+	.set	noreorder
+	ld	$s5,40($sp)		# epilogue
+	ld	$s4,32($sp)
+___
+$code.=<<___ if ($flavour =~ /nubi/i);	# optimize non-nubi epilogue
+	ld	$s3,24($sp)
+	ld	$s2,16($sp)
+	ld	$s1,8($sp)
+	ld	$s0,0($sp)
+___
+$code.=<<___;
+	jr	$ra
+	dadd	$sp,6*8
+.end	poly1305_blocks_internal
+___
+}
+{
+my ($ctx,$mac,$nonce) = ($a0,$a1,$a2);
+
+$code.=<<___;
+.align	5
+.globl	poly1305_emit
+.ent	poly1305_emit
+poly1305_emit:
+	.frame	$sp,0,$ra
+	.set	reorder
+
+	ld	$tmp0,0($ctx)
+	ld	$tmp1,8($ctx)
+	ld	$tmp2,16($ctx)
+
+	daddiu	$in0,$tmp0,5		# compare to modulus
+	sltiu	$tmp3,$in0,5
+	daddu	$in1,$tmp1,$tmp3
+	sltu	$tmp3,$in1,$tmp3
+	daddu	$tmp2,$tmp2,$tmp3
+
+	dsrl	$tmp2,2			# see if it carried/borrowed
+	dsubu	$tmp2,$zero,$tmp2
+	nor	$tmp3,$zero,$tmp2
+
+	and	$in0,$tmp2
+	and	$tmp0,$tmp3
+	and	$in1,$tmp2
+	and	$tmp1,$tmp3
+	or	$in0,$tmp0
+	or	$in1,$tmp1
+
+	lwu	$tmp0,0($nonce)		# load nonce
+	lwu	$tmp1,4($nonce)
+	lwu	$tmp2,8($nonce)
+	lwu	$tmp3,12($nonce)
+	dsll	$tmp1,32
+	dsll	$tmp3,32
+	or	$tmp0,$tmp1
+	or	$tmp2,$tmp3
+
+	daddu	$in0,$tmp0		# accumulate nonce
+	daddu	$in1,$tmp2
+	sltu	$tmp0,$in0,$tmp0
+	daddu	$in1,$tmp0
+
+	dsrl	$tmp0,$in0,8		# write mac value
+	dsrl	$tmp1,$in0,16
+	dsrl	$tmp2,$in0,24
+	sb	$in0,0($mac)
+	dsrl	$tmp3,$in0,32
+	sb	$tmp0,1($mac)
+	dsrl	$tmp0,$in0,40
+	sb	$tmp1,2($mac)
+	dsrl	$tmp1,$in0,48
+	sb	$tmp2,3($mac)
+	dsrl	$tmp2,$in0,56
+	sb	$tmp3,4($mac)
+	dsrl	$tmp3,$in1,8
+	sb	$tmp0,5($mac)
+	dsrl	$tmp0,$in1,16
+	sb	$tmp1,6($mac)
+	dsrl	$tmp1,$in1,24
+	sb	$tmp2,7($mac)
+
+	sb	$in1,8($mac)
+	dsrl	$tmp2,$in1,32
+	sb	$tmp3,9($mac)
+	dsrl	$tmp3,$in1,40
+	sb	$tmp0,10($mac)
+	dsrl	$tmp0,$in1,48
+	sb	$tmp1,11($mac)
+	dsrl	$tmp1,$in1,56
+	sb	$tmp2,12($mac)
+	sb	$tmp3,13($mac)
+	sb	$tmp0,14($mac)
+	sb	$tmp1,15($mac)
+
+	jr	$ra
+.end	poly1305_emit
+.rdata
+.asciiz	"Poly1305 for MIPS64, CRYPTOGAMS by "
+.align	2
+___
+}
+
+$output=pop and open STDOUT,">$output";
+print $code;
+close STDOUT;
+
diff --git a/crypto/poly1305/asm/poly1305-ppc.pl b/crypto/poly1305/asm/poly1305-ppc.pl
new file mode 100755
index 000000000..ab6591028
--- /dev/null
+++ b/crypto/poly1305/asm/poly1305-ppc.pl
@@ -0,0 +1,644 @@
+#! /usr/bin/env perl
+# Copyright 2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+#
+# ====================================================================
+# Written by Andy Polyakov  for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+#
+# This module implements Poly1305 hash for PowerPC.
+#
+# June 2015
+#
+# Numbers are cycles per processed byte with poly1305_blocks alone,
+# and improvement coefficients relative to gcc-generated code.
+#
+#			-m32		-m64
+#
+# Freescale e300	14.8/+80%	-
+# PPC74x0		7.60/+60%	-
+# PPC970		7.00/+114%	3.51/+205%
+# POWER7		3.75/+260%	1.93/+100%
+# POWER8		-		2.03/+200%
+#
+# Do we need floating-point implementation for PPC? Results presented
+# in poly1305_ieee754.c are tricky to compare to, because they are for
+# compiler-generated code. On the other hand it's known that floating-
+# point performance can be dominated by FPU latency, which means that
+# there is limit even for ideally optimized (and even vectorized) code.
+# And this limit is estimated to be higher than above -m64 results. Or
+# in other words floating-point implementation can be meaningful to
+# consider only in 32-bit application context. We probably have to
+# recognize that 32-bit builds are getting less popular on high-end
+# systems and therefore tend to target embedded ones, which might not
+# even have FPU...
+#
+# On side note, Power ISA 2.07 enables vector base 2^26 implementation,
+# and POWER8 might have capacity to break 1.0 cycle per byte barrier...
+
+$flavour = shift;
+
+if ($flavour =~ /64/) {
+	$SIZE_T	=8;
+	$LRSAVE	=2*$SIZE_T;
+	$UCMP	="cmpld";
+	$STU	="stdu";
+	$POP	="ld";
+	$PUSH	="std";
+} elsif ($flavour =~ /32/) {
+	$SIZE_T	=4;
+	$LRSAVE	=$SIZE_T;
+	$UCMP	="cmplw";
+	$STU	="stwu";
+	$POP	="lwz";
+	$PUSH	="stw";
+} else { die "nonsense $flavour"; }
+
+# Define endianness based on flavour
+# i.e.: linux64le
+$LITTLE_ENDIAN = ($flavour=~/le$/) ? $SIZE_T : 0;
+
+$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+( $xlate="${dir}ppc-xlate.pl" and -f $xlate ) or
+( $xlate="${dir}../../perlasm/ppc-xlate.pl" and -f $xlate) or
+die "can't locate ppc-xlate.pl";
+
+open STDOUT,"| $^X $xlate $flavour ".shift || die "can't call $xlate: $!";
+
+$FRAME=24*$SIZE_T;
+
+$sp="r1";
+my ($ctx,$inp,$len,$padbit) = map("r$_",(3..6));
+my ($mac,$nonce)=($inp,$len);
+my $mask = "r0";
+
+$code=<<___;
+.machine	"any"
+.text
+___
+							if ($flavour =~ /64/) {
+###############################################################################
+# base 2^64 implementation
+
+my ($h0,$h1,$h2,$d0,$d1,$d2, $r0,$r1,$s1, $t0,$t1) = map("r$_",(7..12,27..31));
+
+$code.=<<___;
+.globl	.poly1305_init_int
+.align	4
+.poly1305_init_int:
+	xor	r0,r0,r0
+	std	r0,0($ctx)		# zero hash value
+	std	r0,8($ctx)
+	std	r0,16($ctx)
+
+	$UCMP	$inp,r0
+	beq-	Lno_key
+___
+$code.=<<___	if ($LITTLE_ENDIAN);
+	ld	$d0,0($inp)		# load key material
+	ld	$d1,8($inp)
+___
+$code.=<<___	if (!$LITTLE_ENDIAN);
+	li	$h0,4
+	lwbrx	$d0,0,$inp		# load key material
+	li	$d1,8
+	lwbrx	$h0,$h0,$inp
+	li	$h1,12
+	lwbrx	$d1,$d1,$inp
+	lwbrx	$h1,$h1,$inp
+	insrdi	$d0,$h0,32,0
+	insrdi	$d1,$h1,32,0
+___
+$code.=<<___;
+	lis	$h1,0xfff		# 0x0fff0000
+	ori	$h1,$h1,0xfffc		# 0x0ffffffc
+	insrdi	$h1,$h1,32,0		# 0x0ffffffc0ffffffc
+	ori	$h0,$h1,3		# 0x0ffffffc0fffffff
+
+	and	$d0,$d0,$h0
+	and	$d1,$d1,$h1
+
+	std	$d0,32($ctx)		# store key
+	std	$d1,40($ctx)
+
+Lno_key:
+	xor	r3,r3,r3
+	blr
+	.long	0
+	.byte	0,12,0x14,0,0,0,2,0
+.size	.poly1305_init_int,.-.poly1305_init_int
+
+.globl	.poly1305_blocks
+.align	4
+.poly1305_blocks:
+	srdi.	$len,$len,4
+	beq-	Labort
+
+	$STU	$sp,-$FRAME($sp)
+	mflr	r0
+	$PUSH	r27,`$FRAME-$SIZE_T*5`($sp)
+	$PUSH	r28,`$FRAME-$SIZE_T*4`($sp)
+	$PUSH	r29,`$FRAME-$SIZE_T*3`($sp)
+	$PUSH	r30,`$FRAME-$SIZE_T*2`($sp)
+	$PUSH	r31,`$FRAME-$SIZE_T*1`($sp)
+	$PUSH	r0,`$FRAME+$LRSAVE`($sp)
+
+	ld	$r0,32($ctx)		# load key
+	ld	$r1,40($ctx)
+
+	ld	$h0,0($ctx)		# load hash value
+	ld	$h1,8($ctx)
+	ld	$h2,16($ctx)
+
+	srdi	$s1,$r1,2
+	mtctr	$len
+	add	$s1,$s1,$r1		# s1 = r1 + r1>>2
+	li	$mask,3
+	b	Loop
+
+.align	4
+Loop:
+___
+$code.=<<___	if ($LITTLE_ENDIAN);
+	ld	$t0,0($inp)		# load input
+	ld	$t1,8($inp)
+___
+$code.=<<___	if (!$LITTLE_ENDIAN);
+	li	$d0,4
+	lwbrx	$t0,0,$inp		# load input
+	li	$t1,8
+	lwbrx	$d0,$d0,$inp
+	li	$d1,12
+	lwbrx	$t1,$t1,$inp
+	lwbrx	$d1,$d1,$inp
+	insrdi	$t0,$d0,32,0
+	insrdi	$t1,$d1,32,0
+___
+$code.=<<___;
+	addi	$inp,$inp,16
+
+	addc	$h0,$h0,$t0		# accumulate input
+	adde	$h1,$h1,$t1
+
+	mulld	$d0,$h0,$r0		# h0*r0
+	mulhdu	$d1,$h0,$r0
+	adde	$h2,$h2,$padbit
+
+	mulld	$t0,$h1,$s1		# h1*5*r1
+	mulhdu	$t1,$h1,$s1
+	addc	$d0,$d0,$t0
+	adde	$d1,$d1,$t1
+
+	mulld	$t0,$h0,$r1		# h0*r1
+	mulhdu	$d2,$h0,$r1
+	addc	$d1,$d1,$t0
+	addze	$d2,$d2
+
+	mulld	$t0,$h1,$r0		# h1*r0
+	mulhdu	$t1,$h1,$r0
+	addc	$d1,$d1,$t0
+	adde	$d2,$d2,$t1
+
+	mulld	$t0,$h2,$s1		# h2*5*r1
+	mulld	$t1,$h2,$r0		# h2*r0
+	addc	$d1,$d1,$t0
+	adde	$d2,$d2,$t1
+
+	andc	$t0,$d2,$mask		# final reduction step
+	and	$h2,$d2,$mask
+	srdi	$t1,$t0,2
+	add	$t0,$t0,$t1
+	addc	$h0,$d0,$t0
+	addze	$h1,$d1
+	addze	$h2,$h2
+
+	bdnz	Loop
+
+	std	$h0,0($ctx)		# store hash value
+	std	$h1,8($ctx)
+	std	$h2,16($ctx)
+
+	$POP	r27,`$FRAME-$SIZE_T*5`($sp)
+	$POP	r28,`$FRAME-$SIZE_T*4`($sp)
+	$POP	r29,`$FRAME-$SIZE_T*3`($sp)
+	$POP	r30,`$FRAME-$SIZE_T*2`($sp)
+	$POP	r31,`$FRAME-$SIZE_T*1`($sp)
+	addi	$sp,$sp,$FRAME
+Labort:
+	blr
+	.long	0
+	.byte	0,12,4,1,0x80,5,4,0
+.size	.poly1305_blocks,.-.poly1305_blocks
+
+.globl	.poly1305_emit
+.align	4
+.poly1305_emit:
+	ld	$h0,0($ctx)		# load hash
+	ld	$h1,8($ctx)
+	ld	$h2,16($ctx)
+	ld	$padbit,0($nonce)	# load nonce
+	ld	$nonce,8($nonce)
+
+	addic	$d0,$h0,5		# compare to modulus
+	addze	$d1,$h1
+	addze	$d2,$h2
+
+	srdi	$mask,$d2,2		# did it carry/borrow?
+	neg	$mask,$mask
+
+	andc	$h0,$h0,$mask
+	and	$d0,$d0,$mask
+	andc	$h1,$h1,$mask
+	and	$d1,$d1,$mask
+	or	$h0,$h0,$d0
+	or	$h1,$h1,$d1
+___
+$code.=<<___	if (!$LITTLE_ENDIAN);
+	rotldi	$padbit,$padbit,32	# flip nonce words
+	rotldi	$nonce,$nonce,32
+___
+$code.=<<___;
+	addc	$h0,$h0,$padbit		# accumulate nonce
+	adde	$h1,$h1,$nonce
+___
+$code.=<<___	if ($LITTLE_ENDIAN);
+	std	$h0,0($mac)		# write result
+	std	$h1,8($mac)
+___
+$code.=<<___	if (!$LITTLE_ENDIAN);
+	extrdi	r0,$h0,32,0
+	li	$d0,4
+	stwbrx	$h0,0,$mac		# write result
+	extrdi	$h0,$h1,32,0
+	li	$d1,8
+	stwbrx	r0,$d0,$mac
+	li	$d2,12
+	stwbrx	$h1,$d1,$mac
+	stwbrx	$h0,$d2,$mac
+___
+$code.=<<___;
+	blr
+	.long	0
+	.byte	0,12,0x14,0,0,0,3,0
+.size	.poly1305_emit,.-.poly1305_emit
+___
+							} else {
+###############################################################################
+# base 2^32 implementation
+
+my ($h0,$h1,$h2,$h3,$h4, $r0,$r1,$r2,$r3, $s1,$s2,$s3,
+    $t0,$t1,$t2,$t3, $D0,$D1,$D2,$D3, $d0,$d1,$d2,$d3
+   ) = map("r$_",(7..12,14..31));
+
+$code.=<<___;
+.globl	.poly1305_init_int
+.align	4
+.poly1305_init_int:
+	xor	r0,r0,r0
+	stw	r0,0($ctx)		# zero hash value
+	stw	r0,4($ctx)
+	stw	r0,8($ctx)
+	stw	r0,12($ctx)
+	stw	r0,16($ctx)
+
+	$UCMP	$inp,r0
+	beq-	Lno_key
+___
+$code.=<<___	if ($LITTLE_ENDIAN);
+	lw	$h0,0($inp)		# load key material
+	lw	$h1,4($inp)
+	lw	$h2,8($inp)
+	lw	$h3,12($inp)
+___
+$code.=<<___	if (!$LITTLE_ENDIAN);
+	li	$h1,4
+	lwbrx	$h0,0,$inp		# load key material
+	li	$h2,8
+	lwbrx	$h1,$h1,$inp
+	li	$h3,12
+	lwbrx	$h2,$h2,$inp
+	lwbrx	$h3,$h3,$inp
+___
+$code.=<<___;
+	lis	$mask,0xf000		# 0xf0000000
+	li	$r0,-4
+	andc	$r0,$r0,$mask		# 0x0ffffffc
+
+	andc	$h0,$h0,$mask
+	and	$h1,$h1,$r0
+	and	$h2,$h2,$r0
+	and	$h3,$h3,$r0
+
+	stw	$h0,32($ctx)		# store key
+	stw	$h1,36($ctx)
+	stw	$h2,40($ctx)
+	stw	$h3,44($ctx)
+
+Lno_key:
+	xor	r3,r3,r3
+	blr
+	.long	0
+	.byte	0,12,0x14,0,0,0,2,0
+.size	.poly1305_init_int,.-.poly1305_init_int
+
+.globl	.poly1305_blocks
+.align	4
+.poly1305_blocks:
+	srwi.	$len,$len,4
+	beq-	Labort
+
+	$STU	$sp,-$FRAME($sp)
+	mflr	r0
+	$PUSH	r14,`$FRAME-$SIZE_T*18`($sp)
+	$PUSH	r15,`$FRAME-$SIZE_T*17`($sp)
+	$PUSH	r16,`$FRAME-$SIZE_T*16`($sp)
+	$PUSH	r17,`$FRAME-$SIZE_T*15`($sp)
+	$PUSH	r18,`$FRAME-$SIZE_T*14`($sp)
+	$PUSH	r19,`$FRAME-$SIZE_T*13`($sp)
+	$PUSH	r20,`$FRAME-$SIZE_T*12`($sp)
+	$PUSH	r21,`$FRAME-$SIZE_T*11`($sp)
+	$PUSH	r22,`$FRAME-$SIZE_T*10`($sp)
+	$PUSH	r23,`$FRAME-$SIZE_T*9`($sp)
+	$PUSH	r24,`$FRAME-$SIZE_T*8`($sp)
+	$PUSH	r25,`$FRAME-$SIZE_T*7`($sp)
+	$PUSH	r26,`$FRAME-$SIZE_T*6`($sp)
+	$PUSH	r27,`$FRAME-$SIZE_T*5`($sp)
+	$PUSH	r28,`$FRAME-$SIZE_T*4`($sp)
+	$PUSH	r29,`$FRAME-$SIZE_T*3`($sp)
+	$PUSH	r30,`$FRAME-$SIZE_T*2`($sp)
+	$PUSH	r31,`$FRAME-$SIZE_T*1`($sp)
+	$PUSH	r0,`$FRAME+$LRSAVE`($sp)
+
+	lwz	$r0,32($ctx)		# load key
+	lwz	$r1,36($ctx)
+	lwz	$r2,40($ctx)
+	lwz	$r3,44($ctx)
+
+	lwz	$h0,0($ctx)		# load hash value
+	lwz	$h1,4($ctx)
+	lwz	$h2,8($ctx)
+	lwz	$h3,12($ctx)
+	lwz	$h4,16($ctx)
+
+	srwi	$s1,$r1,2
+	srwi	$s2,$r2,2
+	srwi	$s3,$r3,2
+	add	$s1,$s1,$r1		# si = ri + ri>>2
+	add	$s2,$s2,$r2
+	add	$s3,$s3,$r3
+	mtctr	$len
+	li	$mask,3
+	b	Loop
+
+.align	4
+Loop:
+___
+$code.=<<___	if ($LITTLE_ENDIAN);
+	lwz	$d0,0($inp)		# load input
+	lwz	$d1,4($inp)
+	lwz	$d2,8($inp)
+	lwz	$d3,12($inp)
+___
+$code.=<<___	if (!$LITTLE_ENDIAN);
+	li	$d1,4
+	lwbrx	$d0,0,$inp		# load input
+	li	$d2,8
+	lwbrx	$d1,$d1,$inp
+	li	$d3,12
+	lwbrx	$d2,$d2,$inp
+	lwbrx	$d3,$d3,$inp
+___
+$code.=<<___;
+	addi	$inp,$inp,16
+
+	addc	$h0,$h0,$d0		# accumulate input
+	adde	$h1,$h1,$d1
+	adde	$h2,$h2,$d2
+
+	mullw	$d0,$h0,$r0		# h0*r0
+	mulhwu	$D0,$h0,$r0
+
+	mullw	$d1,$h0,$r1		# h0*r1
+	mulhwu	$D1,$h0,$r1
+
+	mullw	$d2,$h0,$r2		# h0*r2
+	mulhwu	$D2,$h0,$r2
+
+	 adde	$h3,$h3,$d3
+	 adde	$h4,$h4,$padbit
+
+	mullw	$d3,$h0,$r3		# h0*r3
+	mulhwu	$D3,$h0,$r3
+
+	mullw	$t0,$h1,$s3		# h1*s3
+	mulhwu	$t1,$h1,$s3
+
+	mullw	$t2,$h1,$r0		# h1*r0
+	mulhwu	$t3,$h1,$r0
+	 addc	$d0,$d0,$t0
+	 adde	$D0,$D0,$t1
+
+	mullw	$t0,$h1,$r1		# h1*r1
+	mulhwu	$t1,$h1,$r1
+	 addc	$d1,$d1,$t2
+	 adde	$D1,$D1,$t3
+
+	mullw	$t2,$h1,$r2		# h1*r2
+	mulhwu	$t3,$h1,$r2
+	 addc	$d2,$d2,$t0
+	 adde	$D2,$D2,$t1
+
+	mullw	$t0,$h2,$s2		# h2*s2
+	mulhwu	$t1,$h2,$s2
+	 addc	$d3,$d3,$t2
+	 adde	$D3,$D3,$t3
+
+	mullw	$t2,$h2,$s3		# h2*s3
+	mulhwu	$t3,$h2,$s3
+	 addc	$d0,$d0,$t0
+	 adde	$D0,$D0,$t1
+
+	mullw	$t0,$h2,$r0		# h2*r0
+	mulhwu	$t1,$h2,$r0
+	 addc	$d1,$d1,$t2
+	 adde	$D1,$D1,$t3
+
+	mullw	$t2,$h2,$r1		# h2*r1
+	mulhwu	$t3,$h2,$r1
+	 addc	$d2,$d2,$t0
+	 adde	$D2,$D2,$t1
+
+	mullw	$t0,$h3,$s1		# h3*s1
+	mulhwu	$t1,$h3,$s1
+	 addc	$d3,$d3,$t2
+	 adde	$D3,$D3,$t3
+
+	mullw	$t2,$h3,$s2		# h3*s2
+	mulhwu	$t3,$h3,$s2
+	 addc	$d0,$d0,$t0
+	 adde	$D0,$D0,$t1
+
+	mullw	$t0,$h3,$s3		# h3*s3
+	mulhwu	$t1,$h3,$s3
+	 addc	$d1,$d1,$t2
+	 adde	$D1,$D1,$t3
+
+	mullw	$t2,$h3,$r0		# h3*r0
+	mulhwu	$t3,$h3,$r0
+	 addc	$d2,$d2,$t0
+	 adde	$D2,$D2,$t1
+
+	mullw	$t0,$h4,$s1		# h4*s1
+	 addc	$d3,$d3,$t2
+	 adde	$D3,$D3,$t3
+	addc	$d1,$d1,$t0
+
+	mullw	$t1,$h4,$s2		# h4*s2
+	 addze	$D1,$D1
+	addc	$d2,$d2,$t1
+	addze	$D2,$D2
+
+	mullw	$t2,$h4,$s3		# h4*s3
+	addc	$d3,$d3,$t2
+	addze	$D3,$D3
+
+	mullw	$h4,$h4,$r0		# h4*r0
+
+	addc	$h1,$d1,$D0
+	adde	$h2,$d2,$D1
+	adde	$h3,$d3,$D2
+	adde	$h4,$h4,$D3
+
+	andc	$D0,$h4,$mask		# final reduction step
+	and	$h4,$h4,$mask
+	srwi	$D1,$D0,2
+	add	$D0,$D0,$D1
+	addc	$h0,$d0,$D0
+	addze	$h1,$h1
+	addze	$h2,$h2
+	addze	$h3,$h3
+	addze	$h4,$h4
+
+	bdnz	Loop
+
+	stw	$h0,0($ctx)		# store hash value
+	stw	$h1,4($ctx)
+	stw	$h2,8($ctx)
+	stw	$h3,12($ctx)
+	stw	$h4,16($ctx)
+
+	$POP	r14,`$FRAME-$SIZE_T*18`($sp)
+	$POP	r15,`$FRAME-$SIZE_T*17`($sp)
+	$POP	r16,`$FRAME-$SIZE_T*16`($sp)
+	$POP	r17,`$FRAME-$SIZE_T*15`($sp)
+	$POP	r18,`$FRAME-$SIZE_T*14`($sp)
+	$POP	r19,`$FRAME-$SIZE_T*13`($sp)
+	$POP	r20,`$FRAME-$SIZE_T*12`($sp)
+	$POP	r21,`$FRAME-$SIZE_T*11`($sp)
+	$POP	r22,`$FRAME-$SIZE_T*10`($sp)
+	$POP	r23,`$FRAME-$SIZE_T*9`($sp)
+	$POP	r24,`$FRAME-$SIZE_T*8`($sp)
+	$POP	r25,`$FRAME-$SIZE_T*7`($sp)
+	$POP	r26,`$FRAME-$SIZE_T*6`($sp)
+	$POP	r27,`$FRAME-$SIZE_T*5`($sp)
+	$POP	r28,`$FRAME-$SIZE_T*4`($sp)
+	$POP	r29,`$FRAME-$SIZE_T*3`($sp)
+	$POP	r30,`$FRAME-$SIZE_T*2`($sp)
+	$POP	r31,`$FRAME-$SIZE_T*1`($sp)
+	addi	$sp,$sp,$FRAME
+Labort:
+	blr
+	.long	0
+	.byte	0,12,4,1,0x80,18,4,0
+.size	.poly1305_blocks,.-.poly1305_blocks
+
+.globl	.poly1305_emit
+.align	4
+.poly1305_emit:
+	$STU	$sp,-$FRAME($sp)
+	mflr	r0
+	$PUSH	r28,`$FRAME-$SIZE_T*4`($sp)
+	$PUSH	r29,`$FRAME-$SIZE_T*3`($sp)
+	$PUSH	r30,`$FRAME-$SIZE_T*2`($sp)
+	$PUSH	r31,`$FRAME-$SIZE_T*1`($sp)
+	$PUSH	r0,`$FRAME+$LRSAVE`($sp)
+
+	lwz	$h0,0($ctx)		# load hash
+	lwz	$h1,4($ctx)
+	lwz	$h2,8($ctx)
+	lwz	$h3,12($ctx)
+	lwz	$h4,16($ctx)
+
+	addic	$d0,$h0,5		# compare to modulus
+	addze	$d1,$h1
+	addze	$d2,$h2
+	addze	$d3,$h3
+	addze	$mask,$h4
+
+	srwi	$mask,$mask,2		# did it carry/borrow?
+	neg	$mask,$mask
+
+	andc	$h0,$h0,$mask
+	and	$d0,$d0,$mask
+	andc	$h1,$h1,$mask
+	and	$d1,$d1,$mask
+	or	$h0,$h0,$d0
+	lwz	$d0,0($nonce)		# load nonce
+	andc	$h2,$h2,$mask
+	and	$d2,$d2,$mask
+	or	$h1,$h1,$d1
+	lwz	$d1,4($nonce)
+	andc	$h3,$h3,$mask
+	and	$d3,$d3,$mask
+	or	$h2,$h2,$d2
+	lwz	$d2,8($nonce)
+	or	$h3,$h3,$d3
+	lwz	$d3,12($nonce)
+
+	addc	$h0,$h0,$d0		# accumulate nonce
+	adde	$h1,$h1,$d1
+	adde	$h2,$h2,$d2
+	adde	$h3,$h3,$d3
+___
+$code.=<<___	if ($LITTLE_ENDIAN);
+	stw	$h0,0($mac)		# write result
+	stw	$h1,4($mac)
+	stw	$h2,8($mac)
+	stw	$h3,12($mac)
+___
+$code.=<<___	if (!$LITTLE_ENDIAN);
+	li	$d1,4
+	stwbrx	$h0,0,$mac		# write result
+	li	$d2,8
+	stwbrx	$h1,$d1,$mac
+	li	$d3,12
+	stwbrx	$h2,$d2,$mac
+	stwbrx	$h3,$d3,$mac
+___
+$code.=<<___;
+	$POP	r28,`$FRAME-$SIZE_T*4`($sp)
+	$POP	r29,`$FRAME-$SIZE_T*3`($sp)
+	$POP	r30,`$FRAME-$SIZE_T*2`($sp)
+	$POP	r31,`$FRAME-$SIZE_T*1`($sp)
+	addi	$sp,$sp,$FRAME
+	blr
+	.long	0
+	.byte	0,12,4,1,0x80,4,3,0
+.size	.poly1305_emit,.-.poly1305_emit
+___
+							}
+$code.=<<___;
+.asciz	"Poly1305 for PPC, CRYPTOGAMS by "
+___
+
+$code =~ s/\`([^\`]*)\`/eval $1/gem;
+print $code;
+close STDOUT;
diff --git a/crypto/poly1305/asm/poly1305-ppcfp.pl b/crypto/poly1305/asm/poly1305-ppcfp.pl
new file mode 100755
index 000000000..49f70a8c0
--- /dev/null
+++ b/crypto/poly1305/asm/poly1305-ppcfp.pl
@@ -0,0 +1,739 @@
+#! /usr/bin/env perl
+# Copyright 2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+#
+# ====================================================================
+# Written by Andy Polyakov  for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+#
+# This module implements Poly1305 hash for PowerPC FPU.
+#
+# June 2015
+#
+# Numbers are cycles per processed byte with poly1305_blocks alone,
+# and improvement coefficients relative to gcc-generated code.
+#
+# Freescale e300	9.78/+30%
+# PPC74x0		6.92/+50%
+# PPC970		6.03/+80%
+# POWER7		3.50/+30%
+# POWER8		3.75/+10%
+
+$flavour = shift;
+
+if ($flavour =~ /64/) {
+	$SIZE_T	=8;
+	$LRSAVE	=2*$SIZE_T;
+	$UCMP	="cmpld";
+	$STU	="stdu";
+	$POP	="ld";
+	$PUSH	="std";
+} elsif ($flavour =~ /32/) {
+	$SIZE_T	=4;
+	$LRSAVE	=$SIZE_T;
+	$UCMP	="cmplw";
+	$STU	="stwu";
+	$POP	="lwz";
+	$PUSH	="stw";
+} else { die "nonsense $flavour"; }
+
+$LITTLE_ENDIAN = ($flavour=~/le$/) ? 4 : 0;
+
+$LWXLE = $LITTLE_ENDIAN ? "lwzx" : "lwbrx";
+
+$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+( $xlate="${dir}ppc-xlate.pl" and -f $xlate ) or
+( $xlate="${dir}../../perlasm/ppc-xlate.pl" and -f $xlate) or
+die "can't locate ppc-xlate.pl";
+
+open STDOUT,"| $^X $xlate $flavour ".shift || die "can't call $xlate: $!";
+
+$LOCALS=6*$SIZE_T;
+$FRAME=$LOCALS+6*8+18*8;
+
+my $sp="r1";
+
+my ($ctx,$inp,$len,$padbit) = map("r$_",(3..6));
+my ($in0,$in1,$in2,$in3,$i1,$i2,$i3) = map("r$_",(7..12,6));
+
+my ($h0lo,$h0hi,$h1lo,$h1hi,$h2lo,$h2hi,$h3lo,$h3hi,
+    $two0,$two32,$two64,$two96,$two130,$five_two130,
+    $r0lo,$r0hi,$r1lo,$r1hi,$r2lo,$r2hi,
+    $s2lo,$s2hi,$s3lo,$s3hi,
+    $c0lo,$c0hi,$c1lo,$c1hi,$c2lo,$c2hi,$c3lo,$c3hi) = map("f$_",(0..31));
+# borrowings
+my ($r3lo,$r3hi,$s1lo,$s1hi) = ($c0lo,$c0hi,$c1lo,$c1hi);
+my ($x0,$x1,$x2,$x3) = ($c2lo,$c2hi,$c3lo,$c3hi);
+my ($y0,$y1,$y2,$y3) = ($c3lo,$c3hi,$c1lo,$c1hi);
+
+$code.=<<___;
+.machine	"any"
+.text
+
+.globl	.poly1305_init_fpu
+.align	6
+.poly1305_init_fpu:
+	$STU	$sp,-$LOCALS($sp)		# minimal frame
+	mflr	$padbit
+	$PUSH	$padbit,`$LOCALS+$LRSAVE`($sp)
+
+	bl	LPICmeup
+
+	xor	r0,r0,r0
+	mtlr	$padbit				# restore lr
+
+	lfd	$two0,8*0($len)			# load constants
+	lfd	$two32,8*1($len)
+	lfd	$two64,8*2($len)
+	lfd	$two96,8*3($len)
+	lfd	$two130,8*4($len)
+	lfd	$five_two130,8*5($len)
+
+	stfd	$two0,8*0($ctx)			# initial hash value, biased 0
+	stfd	$two32,8*1($ctx)
+	stfd	$two64,8*2($ctx)
+	stfd	$two96,8*3($ctx)
+
+	$UCMP	$inp,r0
+	beq-	Lno_key
+
+	lfd	$h3lo,8*13($len)		# new fpscr
+	mffs	$h3hi				# old fpscr
+
+	stfd	$two0,8*4($ctx)			# key "template"
+	stfd	$two32,8*5($ctx)
+	stfd	$two64,8*6($ctx)
+	stfd	$two96,8*7($ctx)
+
+	li	$in1,4
+	li	$in2,8
+	li	$in3,12
+	$LWXLE	$in0,0,$inp			# load key
+	$LWXLE	$in1,$in1,$inp
+	$LWXLE	$in2,$in2,$inp
+	$LWXLE	$in3,$in3,$inp
+
+	lis	$i1,0xf000			#   0xf0000000
+	ori	$i2,$i1,3			#   0xf0000003
+	andc	$in0,$in0,$i1			# &=0x0fffffff
+	andc	$in1,$in1,$i2			# &=0x0ffffffc
+	andc	$in2,$in2,$i2
+	andc	$in3,$in3,$i2
+
+	stw	$in0,`8*4+(4^$LITTLE_ENDIAN)`($ctx)	# fill "template"
+	stw	$in1,`8*5+(4^$LITTLE_ENDIAN)`($ctx)
+	stw	$in2,`8*6+(4^$LITTLE_ENDIAN)`($ctx)
+	stw	$in3,`8*7+(4^$LITTLE_ENDIAN)`($ctx)
+
+	mtfsf	255,$h3lo			# fpscr
+	stfd	$two0,8*18($ctx)		# copy constants to context
+	stfd	$two32,8*19($ctx)
+	stfd	$two64,8*20($ctx)
+	stfd	$two96,8*21($ctx)
+	stfd	$two130,8*22($ctx)
+	stfd	$five_two130,8*23($ctx)
+
+	lfd	$h0lo,8*4($ctx)			# load [biased] key
+	lfd	$h1lo,8*5($ctx)
+	lfd	$h2lo,8*6($ctx)
+	lfd	$h3lo,8*7($ctx)
+
+	fsub	$h0lo,$h0lo,$two0		# r0
+	fsub	$h1lo,$h1lo,$two32		# r1
+	fsub	$h2lo,$h2lo,$two64		# r2
+	fsub	$h3lo,$h3lo,$two96		# r3
+
+	lfd	$two0,8*6($len)			# more constants
+	lfd	$two32,8*7($len)
+	lfd	$two64,8*8($len)
+	lfd	$two96,8*9($len)
+
+	fmul	$h1hi,$h1lo,$five_two130	# s1
+	fmul	$h2hi,$h2lo,$five_two130	# s2
+	 stfd	$h3hi,8*15($ctx)		# borrow slot for original fpscr
+	fmul	$h3hi,$h3lo,$five_two130	# s3
+
+	fadd	$h0hi,$h0lo,$two0
+	 stfd	$h1hi,8*12($ctx)		# put aside for now
+	fadd	$h1hi,$h1lo,$two32
+	 stfd	$h2hi,8*13($ctx)
+	fadd	$h2hi,$h2lo,$two64
+	 stfd	$h3hi,8*14($ctx)
+	fadd	$h3hi,$h3lo,$two96
+
+	fsub	$h0hi,$h0hi,$two0
+	fsub	$h1hi,$h1hi,$two32
+	fsub	$h2hi,$h2hi,$two64
+	fsub	$h3hi,$h3hi,$two96
+
+	lfd	$two0,8*10($len)		# more constants
+	lfd	$two32,8*11($len)
+	lfd	$two64,8*12($len)
+
+	fsub	$h0lo,$h0lo,$h0hi
+	fsub	$h1lo,$h1lo,$h1hi
+	fsub	$h2lo,$h2lo,$h2hi
+	fsub	$h3lo,$h3lo,$h3hi
+
+	stfd	$h0hi,8*5($ctx)			# r0hi
+	stfd	$h1hi,8*7($ctx)			# r1hi
+	stfd	$h2hi,8*9($ctx)			# r2hi
+	stfd	$h3hi,8*11($ctx)		# r3hi
+
+	stfd	$h0lo,8*4($ctx)			# r0lo
+	stfd	$h1lo,8*6($ctx)			# r1lo
+	stfd	$h2lo,8*8($ctx)			# r2lo
+	stfd	$h3lo,8*10($ctx)		# r3lo
+
+	lfd	$h1lo,8*12($ctx)		# s1
+	lfd	$h2lo,8*13($ctx)		# s2
+	lfd	$h3lo,8*14($ctx)		# s3
+	lfd	$h0lo,8*15($ctx)		# pull original fpscr
+
+	fadd	$h1hi,$h1lo,$two0
+	fadd	$h2hi,$h2lo,$two32
+	fadd	$h3hi,$h3lo,$two64
+
+	fsub	$h1hi,$h1hi,$two0
+	fsub	$h2hi,$h2hi,$two32
+	fsub	$h3hi,$h3hi,$two64
+
+	fsub	$h1lo,$h1lo,$h1hi
+	fsub	$h2lo,$h2lo,$h2hi
+	fsub	$h3lo,$h3lo,$h3hi
+
+	stfd	$h1hi,8*13($ctx)		# s1hi
+	stfd	$h2hi,8*15($ctx)		# s2hi
+	stfd	$h3hi,8*17($ctx)		# s3hi
+
+	stfd	$h1lo,8*12($ctx)		# s1lo
+	stfd	$h2lo,8*14($ctx)		# s2lo
+	stfd	$h3lo,8*16($ctx)		# s3lo
+
+	mtfsf	255,$h0lo			# restore fpscr
+Lno_key:
+	xor	r3,r3,r3
+	addi	$sp,$sp,$LOCALS
+	blr
+	.long	0
+	.byte	0,12,4,1,0x80,0,2,0
+.size	.poly1305_init_fpu,.-.poly1305_init_fpu
+
+.globl	.poly1305_blocks_fpu
+.align	4
+.poly1305_blocks_fpu:
+	srwi.	$len,$len,4
+	beq-	Labort
+
+	$STU	$sp,-$FRAME($sp)
+	mflr	r0
+	stfd	f14,`$FRAME-8*18`($sp)
+	stfd	f15,`$FRAME-8*17`($sp)
+	stfd	f16,`$FRAME-8*16`($sp)
+	stfd	f17,`$FRAME-8*15`($sp)
+	stfd	f18,`$FRAME-8*14`($sp)
+	stfd	f19,`$FRAME-8*13`($sp)
+	stfd	f20,`$FRAME-8*12`($sp)
+	stfd	f21,`$FRAME-8*11`($sp)
+	stfd	f22,`$FRAME-8*10`($sp)
+	stfd	f23,`$FRAME-8*9`($sp)
+	stfd	f24,`$FRAME-8*8`($sp)
+	stfd	f25,`$FRAME-8*7`($sp)
+	stfd	f26,`$FRAME-8*6`($sp)
+	stfd	f27,`$FRAME-8*5`($sp)
+	stfd	f28,`$FRAME-8*4`($sp)
+	stfd	f29,`$FRAME-8*3`($sp)
+	stfd	f30,`$FRAME-8*2`($sp)
+	stfd	f31,`$FRAME-8*1`($sp)
+	$PUSH	r0,`$FRAME+$LRSAVE`($sp)
+
+	xor	r0,r0,r0
+	li	$in3,1
+	mtctr	$len
+	neg	$len,$len
+	stw	r0,`$LOCALS+8*4+(0^$LITTLE_ENDIAN)`($sp)
+	stw	$in3,`$LOCALS+8*4+(4^$LITTLE_ENDIAN)`($sp)
+
+	lfd	$two0,8*18($ctx)		# load constants
+	lfd	$two32,8*19($ctx)
+	lfd	$two64,8*20($ctx)
+	lfd	$two96,8*21($ctx)
+	lfd	$two130,8*22($ctx)
+	lfd	$five_two130,8*23($ctx)
+
+	lfd	$h0lo,8*0($ctx)			# load [biased] hash value
+	lfd	$h1lo,8*1($ctx)
+	lfd	$h2lo,8*2($ctx)
+	lfd	$h3lo,8*3($ctx)
+
+	stfd	$two0,`$LOCALS+8*0`($sp)	# input "template"
+	oris	$in3,$padbit,`(1023+52+96)<<4`
+	stfd	$two32,`$LOCALS+8*1`($sp)
+	stfd	$two64,`$LOCALS+8*2`($sp)
+	stw	$in3,`$LOCALS+8*3+(0^$LITTLE_ENDIAN)`($sp)
+
+	li	$i1,4
+	li	$i2,8
+	li	$i3,12
+	$LWXLE	$in0,0,$inp			# load input
+	$LWXLE	$in1,$i1,$inp
+	$LWXLE	$in2,$i2,$inp
+	$LWXLE	$in3,$i3,$inp
+	addi	$inp,$inp,16
+
+	stw	$in0,`$LOCALS+8*0+(4^$LITTLE_ENDIAN)`($sp)	# fill "template"
+	stw	$in1,`$LOCALS+8*1+(4^$LITTLE_ENDIAN)`($sp)
+	stw	$in2,`$LOCALS+8*2+(4^$LITTLE_ENDIAN)`($sp)
+	stw	$in3,`$LOCALS+8*3+(4^$LITTLE_ENDIAN)`($sp)
+
+	mffs	$x0				# original fpscr
+	lfd	$x1,`$LOCALS+8*4`($sp)		# new fpscr
+	lfd	$r0lo,8*4($ctx)			# load key
+	lfd	$r0hi,8*5($ctx)
+	lfd	$r1lo,8*6($ctx)
+	lfd	$r1hi,8*7($ctx)
+	lfd	$r2lo,8*8($ctx)
+	lfd	$r2hi,8*9($ctx)
+	lfd	$r3lo,8*10($ctx)
+	lfd	$r3hi,8*11($ctx)
+	lfd	$s1lo,8*12($ctx)
+	lfd	$s1hi,8*13($ctx)
+	lfd	$s2lo,8*14($ctx)
+	lfd	$s2hi,8*15($ctx)
+	lfd	$s3lo,8*16($ctx)
+	lfd	$s3hi,8*17($ctx)
+
+	stfd	$x0,`$LOCALS+8*4`($sp)		# save original fpscr
+	mtfsf	255,$x1
+
+	addic	$len,$len,1
+	addze	r0,r0
+	slwi.	r0,r0,4
+	sub	$inp,$inp,r0			# conditional rewind
+
+	lfd	$x0,`$LOCALS+8*0`($sp)
+	lfd	$x1,`$LOCALS+8*1`($sp)
+	lfd	$x2,`$LOCALS+8*2`($sp)
+	lfd	$x3,`$LOCALS+8*3`($sp)
+
+	fsub	$h0lo,$h0lo,$two0		# de-bias hash value
+	 $LWXLE	$in0,0,$inp			# modulo-scheduled input load
+	fsub	$h1lo,$h1lo,$two32
+	 $LWXLE	$in1,$i1,$inp
+	fsub	$h2lo,$h2lo,$two64
+	 $LWXLE	$in2,$i2,$inp
+	fsub	$h3lo,$h3lo,$two96
+	 $LWXLE	$in3,$i3,$inp
+
+	fsub	$x0,$x0,$two0			# de-bias input
+	 addi	$inp,$inp,16
+	fsub	$x1,$x1,$two32
+	fsub	$x2,$x2,$two64
+	fsub	$x3,$x3,$two96
+
+	fadd	$x0,$x0,$h0lo			# accumulate input
+	 stw	$in0,`$LOCALS+8*0+(4^$LITTLE_ENDIAN)`($sp)
+	fadd	$x1,$x1,$h1lo
+	 stw	$in1,`$LOCALS+8*1+(4^$LITTLE_ENDIAN)`($sp)
+	fadd	$x2,$x2,$h2lo
+	 stw	$in2,`$LOCALS+8*2+(4^$LITTLE_ENDIAN)`($sp)
+	fadd	$x3,$x3,$h3lo
+	 stw	$in3,`$LOCALS+8*3+(4^$LITTLE_ENDIAN)`($sp)
+
+	b	Lentry
+
+.align	4
+Loop:
+	fsub	$y0,$y0,$two0			# de-bias input
+	 addic	$len,$len,1
+	fsub	$y1,$y1,$two32
+	 addze	r0,r0
+	fsub	$y2,$y2,$two64
+	 slwi.	r0,r0,4
+	fsub	$y3,$y3,$two96
+	 sub	$inp,$inp,r0			# conditional rewind
+
+	fadd	$h0lo,$h0lo,$y0			# accumulate input
+	fadd	$h0hi,$h0hi,$y1
+	fadd	$h2lo,$h2lo,$y2
+	fadd	$h2hi,$h2hi,$y3
+
+	######################################### base 2^48 -> base 2^32
+	fadd	$c1lo,$h1lo,$two64
+	 $LWXLE	$in0,0,$inp			# modulo-scheduled input load
+	fadd	$c1hi,$h1hi,$two64
+	 $LWXLE	$in1,$i1,$inp
+	fadd	$c3lo,$h3lo,$two130
+	 $LWXLE	$in2,$i2,$inp
+	fadd	$c3hi,$h3hi,$two130
+	 $LWXLE	$in3,$i3,$inp
+	fadd	$c0lo,$h0lo,$two32
+	 addi	$inp,$inp,16
+	fadd	$c0hi,$h0hi,$two32
+	fadd	$c2lo,$h2lo,$two96
+	fadd	$c2hi,$h2hi,$two96
+
+	fsub	$c1lo,$c1lo,$two64
+	 stw	$in0,`$LOCALS+8*0+(4^$LITTLE_ENDIAN)`($sp)	# fill "template"
+	fsub	$c1hi,$c1hi,$two64
+	 stw	$in1,`$LOCALS+8*1+(4^$LITTLE_ENDIAN)`($sp)
+	fsub	$c3lo,$c3lo,$two130
+	 stw	$in2,`$LOCALS+8*2+(4^$LITTLE_ENDIAN)`($sp)
+	fsub	$c3hi,$c3hi,$two130
+	 stw	$in3,`$LOCALS+8*3+(4^$LITTLE_ENDIAN)`($sp)
+	fsub	$c0lo,$c0lo,$two32
+	fsub	$c0hi,$c0hi,$two32
+	fsub	$c2lo,$c2lo,$two96
+	fsub	$c2hi,$c2hi,$two96
+
+	fsub	$h1lo,$h1lo,$c1lo
+	fsub	$h1hi,$h1hi,$c1hi
+	fsub	$h3lo,$h3lo,$c3lo
+	fsub	$h3hi,$h3hi,$c3hi
+	fsub	$h2lo,$h2lo,$c2lo
+	fsub	$h2hi,$h2hi,$c2hi
+	fsub	$h0lo,$h0lo,$c0lo
+	fsub	$h0hi,$h0hi,$c0hi
+
+	fadd	$h1lo,$h1lo,$c0lo
+	fadd	$h1hi,$h1hi,$c0hi
+	fadd	$h3lo,$h3lo,$c2lo
+	fadd	$h3hi,$h3hi,$c2hi
+	fadd	$h2lo,$h2lo,$c1lo
+	fadd	$h2hi,$h2hi,$c1hi
+	fmadd	$h0lo,$c3lo,$five_two130,$h0lo
+	fmadd	$h0hi,$c3hi,$five_two130,$h0hi
+
+	fadd	$x1,$h1lo,$h1hi
+	 lfd	$s1lo,8*12($ctx)		# reload constants
+	fadd	$x3,$h3lo,$h3hi
+	 lfd	$s1hi,8*13($ctx)
+	fadd	$x2,$h2lo,$h2hi
+	 lfd	$r3lo,8*10($ctx)
+	fadd	$x0,$h0lo,$h0hi
+	 lfd	$r3hi,8*11($ctx)
+Lentry:
+	fmul	$h0lo,$s3lo,$x1
+	fmul	$h0hi,$s3hi,$x1
+	fmul	$h2lo,$r1lo,$x1
+	fmul	$h2hi,$r1hi,$x1
+	fmul	$h1lo,$r0lo,$x1
+	fmul	$h1hi,$r0hi,$x1
+	fmul	$h3lo,$r2lo,$x1
+	fmul	$h3hi,$r2hi,$x1
+
+	fmadd	$h0lo,$s1lo,$x3,$h0lo
+	fmadd	$h0hi,$s1hi,$x3,$h0hi
+	fmadd	$h2lo,$s3lo,$x3,$h2lo
+	fmadd	$h2hi,$s3hi,$x3,$h2hi
+	fmadd	$h1lo,$s2lo,$x3,$h1lo
+	fmadd	$h1hi,$s2hi,$x3,$h1hi
+	fmadd	$h3lo,$r0lo,$x3,$h3lo
+	fmadd	$h3hi,$r0hi,$x3,$h3hi
+
+	fmadd	$h0lo,$s2lo,$x2,$h0lo
+	fmadd	$h0hi,$s2hi,$x2,$h0hi
+	fmadd	$h2lo,$r0lo,$x2,$h2lo
+	fmadd	$h2hi,$r0hi,$x2,$h2hi
+	fmadd	$h1lo,$s3lo,$x2,$h1lo
+	fmadd	$h1hi,$s3hi,$x2,$h1hi
+	fmadd	$h3lo,$r1lo,$x2,$h3lo
+	fmadd	$h3hi,$r1hi,$x2,$h3hi
+
+	fmadd	$h0lo,$r0lo,$x0,$h0lo
+	 lfd	$y0,`$LOCALS+8*0`($sp)		# load [biased] input
+	fmadd	$h0hi,$r0hi,$x0,$h0hi
+	 lfd	$y1,`$LOCALS+8*1`($sp)
+	fmadd	$h2lo,$r2lo,$x0,$h2lo
+	 lfd	$y2,`$LOCALS+8*2`($sp)
+	fmadd	$h2hi,$r2hi,$x0,$h2hi
+	 lfd	$y3,`$LOCALS+8*3`($sp)
+	fmadd	$h1lo,$r1lo,$x0,$h1lo
+	fmadd	$h1hi,$r1hi,$x0,$h1hi
+	fmadd	$h3lo,$r3lo,$x0,$h3lo
+	fmadd	$h3hi,$r3hi,$x0,$h3hi
+
+	bdnz	Loop
+
+	######################################### base 2^48 -> base 2^32
+	fadd	$c0lo,$h0lo,$two32
+	fadd	$c0hi,$h0hi,$two32
+	fadd	$c2lo,$h2lo,$two96
+	fadd	$c2hi,$h2hi,$two96
+	fadd	$c1lo,$h1lo,$two64
+	fadd	$c1hi,$h1hi,$two64
+	fadd	$c3lo,$h3lo,$two130
+	fadd	$c3hi,$h3hi,$two130
+
+	fsub	$c0lo,$c0lo,$two32
+	fsub	$c0hi,$c0hi,$two32
+	fsub	$c2lo,$c2lo,$two96
+	fsub	$c2hi,$c2hi,$two96
+	fsub	$c1lo,$c1lo,$two64
+	fsub	$c1hi,$c1hi,$two64
+	fsub	$c3lo,$c3lo,$two130
+	fsub	$c3hi,$c3hi,$two130
+
+	fsub	$h1lo,$h1lo,$c1lo
+	fsub	$h1hi,$h1hi,$c1hi
+	fsub	$h3lo,$h3lo,$c3lo
+	fsub	$h3hi,$h3hi,$c3hi
+	fsub	$h2lo,$h2lo,$c2lo
+	fsub	$h2hi,$h2hi,$c2hi
+	fsub	$h0lo,$h0lo,$c0lo
+	fsub	$h0hi,$h0hi,$c0hi
+
+	fadd	$h1lo,$h1lo,$c0lo
+	fadd	$h1hi,$h1hi,$c0hi
+	fadd	$h3lo,$h3lo,$c2lo
+	fadd	$h3hi,$h3hi,$c2hi
+	fadd	$h2lo,$h2lo,$c1lo
+	fadd	$h2hi,$h2hi,$c1hi
+	fmadd	$h0lo,$c3lo,$five_two130,$h0lo
+	fmadd	$h0hi,$c3hi,$five_two130,$h0hi
+
+	fadd	$x1,$h1lo,$h1hi
+	fadd	$x3,$h3lo,$h3hi
+	fadd	$x2,$h2lo,$h2hi
+	fadd	$x0,$h0lo,$h0hi
+
+	lfd	$h0lo,`$LOCALS+8*4`($sp)	# pull saved fpscr
+	fadd	$x1,$x1,$two32			# bias
+	fadd	$x3,$x3,$two96
+	fadd	$x2,$x2,$two64
+	fadd	$x0,$x0,$two0
+
+	stfd	$x1,8*1($ctx)			# store [biased] hash value
+	stfd	$x3,8*3($ctx)
+	stfd	$x2,8*2($ctx)
+	stfd	$x0,8*0($ctx)
+
+	mtfsf	255,$h0lo			# restore original fpscr
+	lfd	f14,`$FRAME-8*18`($sp)
+	lfd	f15,`$FRAME-8*17`($sp)
+	lfd	f16,`$FRAME-8*16`($sp)
+	lfd	f17,`$FRAME-8*15`($sp)
+	lfd	f18,`$FRAME-8*14`($sp)
+	lfd	f19,`$FRAME-8*13`($sp)
+	lfd	f20,`$FRAME-8*12`($sp)
+	lfd	f21,`$FRAME-8*11`($sp)
+	lfd	f22,`$FRAME-8*10`($sp)
+	lfd	f23,`$FRAME-8*9`($sp)
+	lfd	f24,`$FRAME-8*8`($sp)
+	lfd	f25,`$FRAME-8*7`($sp)
+	lfd	f26,`$FRAME-8*6`($sp)
+	lfd	f27,`$FRAME-8*5`($sp)
+	lfd	f28,`$FRAME-8*4`($sp)
+	lfd	f29,`$FRAME-8*3`($sp)
+	lfd	f30,`$FRAME-8*2`($sp)
+	lfd	f31,`$FRAME-8*1`($sp)
+	addi	$sp,$sp,$FRAME
+Labort:
+	blr
+	.long	0
+	.byte	0,12,4,1,0x80,0,4,0
+.size	.poly1305_blocks_fpu,.-.poly1305_blocks_fpu
+___
+{
+my ($mac,$nonce)=($inp,$len);
+
+my ($h0,$h1,$h2,$h3,$h4, $d0,$d1,$d2,$d3
+   ) = map("r$_",(7..11,28..31));
+my $mask = "r0";
+my $FRAME = (6+4)*$SIZE_T;
+
+$code.=<<___;
+.globl	.poly1305_emit_fpu
+.align	4
+.poly1305_emit_fpu:
+	$STU	$sp,-$FRAME($sp)
+	mflr	r0
+	$PUSH	r28,`$FRAME-$SIZE_T*4`($sp)
+	$PUSH	r29,`$FRAME-$SIZE_T*3`($sp)
+	$PUSH	r30,`$FRAME-$SIZE_T*2`($sp)
+	$PUSH	r31,`$FRAME-$SIZE_T*1`($sp)
+	$PUSH	r0,`$FRAME+$LRSAVE`($sp)
+
+	lwz	$d0,`8*0+(0^$LITTLE_ENDIAN)`($ctx)	# load hash
+	lwz	$h0,`8*0+(4^$LITTLE_ENDIAN)`($ctx)
+	lwz	$d1,`8*1+(0^$LITTLE_ENDIAN)`($ctx)
+	lwz	$h1,`8*1+(4^$LITTLE_ENDIAN)`($ctx)
+	lwz	$d2,`8*2+(0^$LITTLE_ENDIAN)`($ctx)
+	lwz	$h2,`8*2+(4^$LITTLE_ENDIAN)`($ctx)
+	lwz	$d3,`8*3+(0^$LITTLE_ENDIAN)`($ctx)
+	lwz	$h3,`8*3+(4^$LITTLE_ENDIAN)`($ctx)
+
+	lis	$mask,0xfff0
+	andc	$d0,$d0,$mask			# mask exponent
+	andc	$d1,$d1,$mask
+	andc	$d2,$d2,$mask
+	andc	$d3,$d3,$mask			# can be partially reduced...
+	li	$mask,3
+
+	srwi	$padbit,$d3,2			# ... so reduce
+	and	$h4,$d3,$mask
+	andc	$d3,$d3,$mask
+	add	$d3,$d3,$padbit
+___
+						if ($SIZE_T==4) {
+$code.=<<___;
+	addc	$h0,$h0,$d3
+	adde	$h1,$h1,$d0
+	adde	$h2,$h2,$d1
+	adde	$h3,$h3,$d2
+	addze	$h4,$h4
+
+	addic	$d0,$h0,5			# compare to modulus
+	addze	$d1,$h1
+	addze	$d2,$h2
+	addze	$d3,$h3
+	addze	$mask,$h4
+
+	srwi	$mask,$mask,2			# did it carry/borrow?
+	neg	$mask,$mask
+	srawi	$mask,$mask,31			# mask
+
+	andc	$h0,$h0,$mask
+	and	$d0,$d0,$mask
+	andc	$h1,$h1,$mask
+	and	$d1,$d1,$mask
+	or	$h0,$h0,$d0
+	lwz	$d0,0($nonce)			# load nonce
+	andc	$h2,$h2,$mask
+	and	$d2,$d2,$mask
+	or	$h1,$h1,$d1
+	lwz	$d1,4($nonce)
+	andc	$h3,$h3,$mask
+	and	$d3,$d3,$mask
+	or	$h2,$h2,$d2
+	lwz	$d2,8($nonce)
+	or	$h3,$h3,$d3
+	lwz	$d3,12($nonce)
+
+	addc	$h0,$h0,$d0			# accumulate nonce
+	adde	$h1,$h1,$d1
+	adde	$h2,$h2,$d2
+	adde	$h3,$h3,$d3
+___
+						} else {
+$code.=<<___;
+	add	$h0,$h0,$d3
+	add	$h1,$h1,$d0
+	add	$h2,$h2,$d1
+	add	$h3,$h3,$d2
+
+	srdi	$d0,$h0,32
+	add	$h1,$h1,$d0
+	srdi	$d1,$h1,32
+	add	$h2,$h2,$d1
+	srdi	$d2,$h2,32
+	add	$h3,$h3,$d2
+	srdi	$d3,$h3,32
+	add	$h4,$h4,$d3
+
+	insrdi	$h0,$h1,32,0
+	insrdi	$h2,$h3,32,0
+
+	addic	$d0,$h0,5			# compare to modulus
+	addze	$d1,$h2
+	addze	$d2,$h4
+
+	srdi	$mask,$d2,2			# did it carry/borrow?
+	neg	$mask,$mask
+	sradi	$mask,$mask,63			# mask
+	ld	$d2,0($nonce)			# load nonce
+	ld	$d3,8($nonce)
+
+	andc	$h0,$h0,$mask
+	and	$d0,$d0,$mask
+	andc	$h2,$h2,$mask
+	and	$d1,$d1,$mask
+	or	$h0,$h0,$d0
+	or	$h2,$h2,$d1
+___
+$code.=<<___	if (!$LITTLE_ENDIAN);
+	rotldi	$d2,$d2,32			# flip nonce words
+	rotldi	$d3,$d3,32
+___
+$code.=<<___;
+	addc	$h0,$h0,$d2			# accumulate nonce
+	adde	$h2,$h2,$d3
+
+	srdi	$h1,$h0,32
+	srdi	$h3,$h2,32
+___
+						}
+$code.=<<___	if ($LITTLE_ENDIAN);
+	stw	$h0,0($mac)			# write result
+	stw	$h1,4($mac)
+	stw	$h2,8($mac)
+	stw	$h3,12($mac)
+___
+$code.=<<___	if (!$LITTLE_ENDIAN);
+	li	$d1,4
+	stwbrx	$h0,0,$mac			# write result
+	li	$d2,8
+	stwbrx	$h1,$d1,$mac
+	li	$d3,12
+	stwbrx	$h2,$d2,$mac
+	stwbrx	$h3,$d3,$mac
+___
+$code.=<<___;
+	$POP	r28,`$FRAME-$SIZE_T*4`($sp)
+	$POP	r29,`$FRAME-$SIZE_T*3`($sp)
+	$POP	r30,`$FRAME-$SIZE_T*2`($sp)
+	$POP	r31,`$FRAME-$SIZE_T*1`($sp)
+	addi	$sp,$sp,$FRAME
+	blr
+	.long	0
+	.byte	0,12,4,1,0x80,4,3,0
+.size	.poly1305_emit_fpu,.-.poly1305_emit_fpu
+___
+}
+# Ugly hack here, because PPC assembler syntax seem to vary too
+# much from platforms to platform...
+$code.=<<___;
+.align	6
+LPICmeup:
+	mflr	r0
+	bcl	20,31,\$+4
+	mflr	$len	# vvvvvv "distance" between . and 1st data entry
+	addi	$len,$len,`64-8`	# borrow $len
+	mtlr	r0
+	blr
+	.long	0
+	.byte	0,12,0x14,0,0,0,0,0
+	.space	`64-9*4`
+
+.quad	0x4330000000000000		# 2^(52+0)
+.quad	0x4530000000000000		# 2^(52+32)
+.quad	0x4730000000000000		# 2^(52+64)
+.quad	0x4930000000000000		# 2^(52+96)
+.quad	0x4b50000000000000		# 2^(52+130)
+
+.quad	0x37f4000000000000		# 5/2^130
+
+.quad	0x4430000000000000		# 2^(52+16+0)
+.quad	0x4630000000000000		# 2^(52+16+32)
+.quad	0x4830000000000000		# 2^(52+16+64)
+.quad	0x4a30000000000000		# 2^(52+16+96)
+.quad	0x3e30000000000000		# 2^(52+16+0-96)
+.quad	0x4030000000000000		# 2^(52+16+32-96)
+.quad	0x4230000000000000		# 2^(52+16+64-96)
+
+.quad	0x0000000000000001		# fpscr: truncate, no exceptions
+.asciz	"Poly1305 for PPC FPU, CRYPTOGAMS by "
+.align	4
+___
+
+$code =~ s/\`([^\`]*)\`/eval $1/gem;
+print $code;
+close STDOUT;
diff --git a/crypto/poly1305/asm/poly1305-s390x.pl b/crypto/poly1305/asm/poly1305-s390x.pl
new file mode 100755
index 000000000..82d757d9a
--- /dev/null
+++ b/crypto/poly1305/asm/poly1305-s390x.pl
@@ -0,0 +1,227 @@
+#! /usr/bin/env perl
+# Copyright 2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+#
+# ====================================================================
+# Written by Andy Polyakov  for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+#
+# This module implements Poly1305 hash for s390x.
+#
+# June 2015
+#
+# ~6.6/2.3 cpb on z10/z196+, >2x improvement over compiler-generated
+# code. For older compiler improvement coefficient is >3x, because
+# then base 2^64 and base 2^32 implementations are compared.
+#
+# On side note, z13 enables vector base 2^26 implementation...
+
+$flavour = shift;
+
+if ($flavour =~ /3[12]/) {
+	$SIZE_T=4;
+	$g="";
+} else {
+	$SIZE_T=8;
+	$g="g";
+}
+
+while (($output=shift) && ($output!~/\w[\w\-]*\.\w+$/)) {}
+open STDOUT,">$output";
+
+$sp="%r15";
+
+my ($ctx,$inp,$len,$padbit) = map("%r$_",(2..5));
+
+$code.=<<___;
+.text
+
+.globl	poly1305_init
+.type	poly1305_init,\@function
+.align	16
+poly1305_init:
+	lghi	%r0,0
+	lghi	%r1,-1
+	stg	%r0,0($ctx)		# zero hash value
+	stg	%r0,8($ctx)
+	stg	%r0,16($ctx)
+
+	cl${g}r	$inp,%r0
+	je	.Lno_key
+
+	lrvg	%r4,0($inp)		# load little-endian key
+	lrvg	%r5,8($inp)
+
+	nihl	%r1,0xffc0		# 0xffffffc0ffffffff
+	srlg	%r0,%r1,4		# 0x0ffffffc0fffffff
+	srlg	%r1,%r1,4
+	nill	%r1,0xfffc		# 0x0ffffffc0ffffffc
+
+	ngr	%r4,%r0
+	ngr	%r5,%r1
+
+	stg	%r4,32($ctx)
+	stg	%r5,40($ctx)
+
+.Lno_key:
+	lghi	%r2,0
+	br	%r14
+.size	poly1305_init,.-poly1305_init
+___
+{
+my ($d0hi,$d0lo,$d1hi,$d1lo,$t0,$h0,$t1,$h1,$h2) = map("%r$_",(6..14));
+my ($r0,$r1,$s1) = map("%r$_",(0..2));
+
+$code.=<<___;
+.globl	poly1305_blocks
+.type	poly1305_blocks,\@function
+.align	16
+poly1305_blocks:
+	srl${g}	$len,4			# fixed-up in 64-bit build
+	lghi	%r0,0
+	cl${g}r	$len,%r0
+	je	.Lno_data
+
+	stm${g}	%r6,%r14,`6*$SIZE_T`($sp)
+
+	llgfr   $padbit,$padbit		# clear upper half, much needed with
+					# non-64-bit ABI
+	lg	$r0,32($ctx)		# load key
+	lg	$r1,40($ctx)
+
+	lg	$h0,0($ctx)		# load hash value
+	lg	$h1,8($ctx)
+	lg	$h2,16($ctx)
+
+	st$g	$ctx,`2*$SIZE_T`($sp)	# off-load $ctx
+	srlg	$s1,$r1,2
+	algr	$s1,$r1			# s1 = r1 + r1>>2
+	j	.Loop
+
+.align	16
+.Loop:
+	lrvg	$d0lo,0($inp)		# load little-endian input
+	lrvg	$d1lo,8($inp)
+	la	$inp,16($inp)
+
+	algr	$d0lo,$h0		# accumulate input
+	alcgr	$d1lo,$h1
+
+	lgr	$h0,$d0lo
+	mlgr	$d0hi,$r0		# h0*r0	  -> $d0hi:$d0lo
+	lgr	$h1,$d1lo
+	mlgr	$d1hi,$s1		# h1*5*r1 -> $d1hi:$d1lo
+
+	mlgr	$t0,$r1			# h0*r1   -> $t0:$h0
+	mlgr	$t1,$r0			# h1*r0   -> $t1:$h1
+	alcgr	$h2,$padbit
+
+	algr	$d0lo,$d1lo
+	lgr	$d1lo,$h2
+	alcgr	$d0hi,$d1hi
+	lghi	$d1hi,0
+
+	algr	$h1,$h0
+	alcgr	$t1,$t0
+
+	msgr	$d1lo,$s1		# h2*s1
+	msgr	$h2,$r0			# h2*r0
+
+	algr	$h1,$d1lo
+	alcgr	$t1,$d1hi		# $d1hi is zero
+
+	algr	$h1,$d0hi
+	alcgr	$h2,$t1
+
+	lghi	$h0,-4			# final reduction step
+	ngr	$h0,$h2
+	srlg	$t0,$h2,2
+	algr	$h0,$t0
+	lghi	$t1,3
+	ngr	$h2,$t1
+
+	algr	$h0,$d0lo
+	alcgr	$h1,$d1hi		# $d1hi is still zero
+	alcgr	$h2,$d1hi		# $d1hi is still zero
+
+	brct$g	$len,.Loop
+
+	l$g	$ctx,`2*$SIZE_T`($sp)	# restore $ctx
+
+	stg	$h0,0($ctx)		# store hash value
+	stg	$h1,8($ctx)
+	stg	$h2,16($ctx)
+
+	lm${g}	%r6,%r14,`6*$SIZE_T`($sp)
+.Lno_data:
+	br	%r14
+.size	poly1305_blocks,.-poly1305_blocks
+___
+}
+{
+my ($mac,$nonce)=($inp,$len);
+my ($h0,$h1,$h2,$d0,$d1)=map("%r$_",(5..9));
+
+$code.=<<___;
+.globl	poly1305_emit
+.type	poly1305_emit,\@function
+.align	16
+poly1305_emit:
+	stm${g}	%r6,%r9,`6*$SIZE_T`($sp)
+
+	lg	$h0,0($ctx)
+	lg	$h1,8($ctx)
+	lg	$h2,16($ctx)
+
+	lghi	%r0,5
+	lghi	%r1,0
+	lgr	$d0,$h0
+	lgr	$d1,$h1
+
+	algr	$h0,%r0			# compare to modulus
+	alcgr	$h1,%r1
+	alcgr	$h2,%r1
+
+	srlg	$h2,$h2,2		# did it borrow/carry?
+	slgr	%r1,$h2			# 0-$h2>>2
+	lg	$h2,0($nonce)		# load nonce
+	lghi	%r0,-1
+	lg	$ctx,8($nonce)
+	xgr	%r0,%r1			# ~%r1
+
+	ngr	$h0,%r1
+	ngr	$d0,%r0
+	ngr	$h1,%r1
+	ngr	$d1,%r0
+	ogr	$h0,$d0
+	rllg	$d0,$h2,32		# flip nonce words
+	ogr	$h1,$d1
+	rllg	$d1,$ctx,32
+
+	algr	$h0,$d0			# accumulate nonce
+	alcgr	$h1,$d1
+
+	strvg	$h0,0($mac)		# write little-endian result
+	strvg	$h1,8($mac)
+
+	lm${g}	%r6,%r9,`6*$SIZE_T`($sp)
+	br	%r14
+.size	poly1305_emit,.-poly1305_emit
+
+.string	"Poly1305 for s390x, CRYPTOGAMS by "
+___
+}
+
+$code =~ s/\`([^\`]*)\`/eval $1/gem;
+$code =~ s/\b(srlg\s+)(%r[0-9]+\s*,)\s*([0-9]+)/$1$2$2$3/gm;
+
+print $code;
+close STDOUT;
diff --git a/crypto/poly1305/asm/poly1305-sparcv9.pl b/crypto/poly1305/asm/poly1305-sparcv9.pl
new file mode 100755
index 000000000..0bdd04892
--- /dev/null
+++ b/crypto/poly1305/asm/poly1305-sparcv9.pl
@@ -0,0 +1,1120 @@
+#! /usr/bin/env perl
+# Copyright 2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+#
+# ====================================================================
+# Written by Andy Polyakov  for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+#
+# This module implements Poly1305 hash for SPARCv9, vanilla, as well
+# as VIS3 and FMA extensions.
+#
+# May, August 2015
+#
+# Numbers are cycles per processed byte with poly1305_blocks alone.
+#
+#			IALU(*)		FMA
+#
+# UltraSPARC III	12.3(**)
+# SPARC T3		7.92
+# SPARC T4		1.70(***)	6.55
+# SPARC64 X		5.60		3.64
+#
+# (*)	Comparison to compiler-generated code is really problematic,
+#	because latter's performance varies too much depending on too
+#	many variables. For example, one can measure from 5x to 15x
+#	improvement on T4 for gcc-4.6. Well, in T4 case it's a bit
+#	unfair comparison, because compiler doesn't use VIS3, but
+#	given same initial conditions coefficient varies from 3x to 9x.
+# (**)	Pre-III performance should be even worse; floating-point
+#	performance for UltraSPARC I-IV on the other hand is reported
+#	to be 4.25 for hand-coded assembly, but they are just too old
+#	to care about.
+# (***)	Multi-process benchmark saturates at ~12.5x single-process
+#	result on 8-core processor, or ~21GBps per 2.85GHz socket.
+
+my $output = pop;
+open STDOUT,">$output";
+
+my ($ctx,$inp,$len,$padbit,$shl,$shr)	= map("%i$_",(0..5));
+my ($r0,$r1,$r2,$r3,$s1,$s2,$s3,$h4)	= map("%l$_",(0..7));
+my ($h0,$h1,$h2,$h3, $t0,$t1,$t2)	= map("%o$_",(0..5,7));
+my ($d0,$d1,$d2,$d3)			= map("%g$_",(1..4));
+
+my $output = pop;
+open STDOUT,">$stdout";
+
+$code.=<<___;
+#include "sparc_arch.h"
+
+#ifdef	__arch64__
+.register	%g2,#scratch
+.register	%g3,#scratch
+# define	STPTR	stx
+# define	SIZE_T	8
+#else
+# define	STPTR	st
+# define	SIZE_T	4
+#endif
+#define	LOCALS	(STACK_BIAS+STACK_FRAME)
+
+.section	".text",#alloc,#execinstr
+
+#ifdef __PIC__
+SPARC_PIC_THUNK(%g1)
+#endif
+
+.globl	poly1305_init
+.align	32
+poly1305_init:
+	save	%sp,-STACK_FRAME-16,%sp
+	nop
+
+	SPARC_LOAD_ADDRESS(OPENSSL_sparcv9cap_P,%g1)
+	ld	[%g1],%g1
+
+	and	%g1,SPARCV9_FMADD|SPARCV9_VIS3,%g1
+	cmp	%g1,SPARCV9_FMADD
+	be	.Lpoly1305_init_fma
+	nop
+
+	stx	%g0,[$ctx+0]
+	stx	%g0,[$ctx+8]		! zero hash value
+	brz,pn	$inp,.Lno_key
+	stx	%g0,[$ctx+16]
+
+	and	$inp,7,$shr		! alignment factor
+	andn	$inp,7,$inp
+	sll	$shr,3,$shr		! *8
+	neg	$shr,$shl
+
+	sethi	%hi(0x0ffffffc),$t0
+	set	8,$h1
+	or	$t0,%lo(0x0ffffffc),$t0
+	set	16,$h2
+	sllx	$t0,32,$t1
+	or	$t0,$t1,$t1		! 0x0ffffffc0ffffffc
+	or	$t1,3,$t0		! 0x0ffffffc0fffffff
+
+	ldxa	[$inp+%g0]0x88,$h0	! load little-endian key
+	brz,pt	$shr,.Lkey_aligned
+	ldxa	[$inp+$h1]0x88,$h1
+
+	ldxa	[$inp+$h2]0x88,$h2
+	srlx	$h0,$shr,$h0
+	sllx	$h1,$shl,$t2
+	srlx	$h1,$shr,$h1
+	or	$t2,$h0,$h0
+	sllx	$h2,$shl,$h2
+	or	$h2,$h1,$h1
+
+.Lkey_aligned:
+	and	$t0,$h0,$h0
+	and	$t1,$h1,$h1
+	stx	$h0,[$ctx+32+0]		! store key
+	stx	$h1,[$ctx+32+8]
+
+	andcc	%g1,SPARCV9_VIS3,%g0
+	be	.Lno_key
+	nop
+
+1:	call	.+8
+	add	%o7,poly1305_blocks_vis3-1b,%o7
+
+	add	%o7,poly1305_emit-poly1305_blocks_vis3,%o5
+	STPTR	%o7,[%i2]
+	STPTR	%o5,[%i2+SIZE_T]
+
+	ret
+	restore	%g0,1,%o0		! return 1
+
+.Lno_key:
+	ret
+	restore	%g0,%g0,%o0		! return 0
+.type	poly1305_init,#function
+.size	poly1305_init,.-poly1305_init
+
+.globl	poly1305_blocks
+.align	32
+poly1305_blocks:
+	save	%sp,-STACK_FRAME,%sp
+	srln	$len,4,$len
+
+	brz,pn	$len,.Lno_data
+	nop
+
+	ld	[$ctx+32+0],$r1		! load key
+	ld	[$ctx+32+4],$r0
+	ld	[$ctx+32+8],$r3
+	ld	[$ctx+32+12],$r2
+
+	ld	[$ctx+0],$h1		! load hash value
+	ld	[$ctx+4],$h0
+	ld	[$ctx+8],$h3
+	ld	[$ctx+12],$h2
+	ld	[$ctx+16],$h4
+
+	and	$inp,7,$shr		! alignment factor
+	andn	$inp,7,$inp
+	set	8,$d1
+	sll	$shr,3,$shr		! *8
+	set	16,$d2
+	neg	$shr,$shl
+
+	srl	$r1,2,$s1
+	srl	$r2,2,$s2
+	add	$r1,$s1,$s1
+	srl	$r3,2,$s3
+	add	$r2,$s2,$s2
+	add	$r3,$s3,$s3
+
+.Loop:
+	ldxa	[$inp+%g0]0x88,$d0	! load little-endian input
+	brz,pt	$shr,.Linp_aligned
+	ldxa	[$inp+$d1]0x88,$d1
+
+	ldxa	[$inp+$d2]0x88,$d2
+	srlx	$d0,$shr,$d0
+	sllx	$d1,$shl,$t1
+	srlx	$d1,$shr,$d1
+	or	$t1,$d0,$d0
+	sllx	$d2,$shl,$d2
+	or	$d2,$d1,$d1
+
+.Linp_aligned:
+	srlx	$d0,32,$t0
+	addcc	$d0,$h0,$h0		! accumulate input
+	srlx	$d1,32,$t1
+	addccc	$t0,$h1,$h1
+	addccc	$d1,$h2,$h2
+	addccc	$t1,$h3,$h3
+	addc	$padbit,$h4,$h4
+
+	umul	$r0,$h0,$d0
+	umul	$r1,$h0,$d1
+	umul	$r2,$h0,$d2
+	umul	$r3,$h0,$d3
+	 sub	$len,1,$len
+	 add	$inp,16,$inp
+
+	umul	$s3,$h1,$t0
+	umul	$r0,$h1,$t1
+	umul	$r1,$h1,$t2
+	add	$t0,$d0,$d0
+	add	$t1,$d1,$d1
+	umul	$r2,$h1,$t0
+	add	$t2,$d2,$d2
+	add	$t0,$d3,$d3
+
+	umul	$s2,$h2,$t1
+	umul	$s3,$h2,$t2
+	umul	$r0,$h2,$t0
+	add	$t1,$d0,$d0
+	add	$t2,$d1,$d1
+	umul	$r1,$h2,$t1
+	add	$t0,$d2,$d2
+	add	$t1,$d3,$d3
+
+	umul	$s1,$h3,$t2
+	umul	$s2,$h3,$t0
+	umul	$s3,$h3,$t1
+	add	$t2,$d0,$d0
+	add	$t0,$d1,$d1
+	umul	$r0,$h3,$t2
+	add	$t1,$d2,$d2
+	add	$t2,$d3,$d3
+
+	umul	$s1,$h4,$t0
+	umul	$s2,$h4,$t1
+	umul	$s3,$h4,$t2
+	umul	$r0,$h4,$h4
+	add	$t0,$d1,$d1
+	add	$t1,$d2,$d2
+	srlx	$d0,32,$h1
+	add	$t2,$d3,$d3
+	srlx	$d1,32,$h2
+
+	addcc	$d1,$h1,$h1
+	srlx	$d2,32,$h3
+	 set	8,$d1
+	addccc	$d2,$h2,$h2
+	srlx	$d3,32,$t0
+	 set	16,$d2
+	addccc	$d3,$h3,$h3
+	addc	$t0,$h4,$h4
+
+	srl	$h4,2,$t0		! final reduction step
+	andn	$h4,3,$t1
+	and	$h4,3,$h4
+	add	$t1,$t0,$t0
+
+	addcc	$t0,$d0,$h0
+	addccc	%g0,$h1,$h1
+	addccc	%g0,$h2,$h2
+	addccc	%g0,$h3,$h3
+	brnz,pt	$len,.Loop
+	addc	%g0,$h4,$h4
+
+	st	$h1,[$ctx+0]		! store hash value
+	st	$h0,[$ctx+4]
+	st	$h3,[$ctx+8]
+	st	$h2,[$ctx+12]
+	st	$h4,[$ctx+16]
+
+.Lno_data:
+	ret
+	restore
+.type	poly1305_blocks,#function
+.size	poly1305_blocks,.-poly1305_blocks
+___
+########################################################################
+# VIS3 has umulxhi and addxc...
+{
+my ($H0,$H1,$H2,$R0,$R1,$S1,$T1) = map("%o$_",(0..5,7));
+my ($D0,$D1,$D2,$T0) = map("%g$_",(1..4));
+
+$code.=<<___;
+.align	32
+poly1305_blocks_vis3:
+	save	%sp,-STACK_FRAME,%sp
+	srln	$len,4,$len
+
+	brz,pn	$len,.Lno_data
+	nop
+
+	ldx	[$ctx+32+0],$R0		! load key
+	ldx	[$ctx+32+8],$R1
+
+	ldx	[$ctx+0],$H0		! load hash value
+	ldx	[$ctx+8],$H1
+	ld	[$ctx+16],$H2
+
+	and	$inp,7,$shr		! alignment factor
+	andn	$inp,7,$inp
+	set	8,$r1
+	sll	$shr,3,$shr		! *8
+	set	16,$r2
+	neg	$shr,$shl
+
+	srlx	$R1,2,$S1
+	b	.Loop_vis3
+	add	$R1,$S1,$S1
+
+.Loop_vis3:
+	ldxa	[$inp+%g0]0x88,$D0	! load little-endian input
+	brz,pt	$shr,.Linp_aligned_vis3
+	ldxa	[$inp+$r1]0x88,$D1
+
+	ldxa	[$inp+$r2]0x88,$D2
+	srlx	$D0,$shr,$D0
+	sllx	$D1,$shl,$T1
+	srlx	$D1,$shr,$D1
+	or	$T1,$D0,$D0
+	sllx	$D2,$shl,$D2
+	or	$D2,$D1,$D1
+
+.Linp_aligned_vis3:
+	addcc	$D0,$H0,$H0		! accumulate input
+	 sub	$len,1,$len
+	addxccc	$D1,$H1,$H1
+	 add	$inp,16,$inp
+
+	mulx	$R0,$H0,$D0		! r0*h0
+	addxc	$padbit,$H2,$H2
+	umulxhi	$R0,$H0,$D1
+	mulx	$S1,$H1,$T0		! s1*h1
+	umulxhi	$S1,$H1,$T1
+	addcc	$T0,$D0,$D0
+	mulx	$R1,$H0,$T0		! r1*h0
+	addxc	$T1,$D1,$D1
+	umulxhi	$R1,$H0,$D2
+	addcc	$T0,$D1,$D1
+	mulx	$R0,$H1,$T0		! r0*h1
+	addxc	%g0,$D2,$D2
+	umulxhi	$R0,$H1,$T1
+	addcc	$T0,$D1,$D1
+	mulx	$S1,$H2,$T0		! s1*h2
+	addxc	$T1,$D2,$D2
+	mulx	$R0,$H2,$T1		! r0*h2
+	addcc	$T0,$D1,$D1
+	addxc	$T1,$D2,$D2
+
+	srlx	$D2,2,$T0		! final reduction step
+	andn	$D2,3,$T1
+	and	$D2,3,$H2
+	add	$T1,$T0,$T0
+
+	addcc	$T0,$D0,$H0
+	addxccc	%g0,$D1,$H1
+	brnz,pt	$len,.Loop_vis3
+	addxc	%g0,$H2,$H2
+
+	stx	$H0,[$ctx+0]		! store hash value
+	stx	$H1,[$ctx+8]
+	st	$H2,[$ctx+16]
+
+	ret
+	restore
+.type	poly1305_blocks_vis3,#function
+.size	poly1305_blocks_vis3,.-poly1305_blocks_vis3
+___
+}
+my ($mac,$nonce) = ($inp,$len);
+
+$code.=<<___;
+.globl	poly1305_emit
+.align	32
+poly1305_emit:
+	save	%sp,-STACK_FRAME,%sp
+
+	ld	[$ctx+0],$h1		! load hash value
+	ld	[$ctx+4],$h0
+	ld	[$ctx+8],$h3
+	ld	[$ctx+12],$h2
+	ld	[$ctx+16],$h4
+
+	addcc	$h0,5,$r0		! compare to modulus
+	addccc	$h1,0,$r1
+	addccc	$h2,0,$r2
+	addccc	$h3,0,$r3
+	addc	$h4,0,$h4
+	andcc	$h4,4,%g0		! did it carry/borrow?
+
+	movnz	%icc,$r0,$h0
+	ld	[$nonce+0],$r0		! load nonce
+	movnz	%icc,$r1,$h1
+	ld	[$nonce+4],$r1
+	movnz	%icc,$r2,$h2
+	ld	[$nonce+8],$r2
+	movnz	%icc,$r3,$h3
+	ld	[$nonce+12],$r3
+
+	addcc	$r0,$h0,$h0		! accumulate nonce
+	addccc	$r1,$h1,$h1
+	addccc	$r2,$h2,$h2
+	addc	$r3,$h3,$h3
+
+	srl	$h0,8,$r0
+	stb	$h0,[$mac+0]		! store little-endian result
+	srl	$h0,16,$r1
+	stb	$r0,[$mac+1]
+	srl	$h0,24,$r2
+	stb	$r1,[$mac+2]
+	stb	$r2,[$mac+3]
+
+	srl	$h1,8,$r0
+	stb	$h1,[$mac+4]
+	srl	$h1,16,$r1
+	stb	$r0,[$mac+5]
+	srl	$h1,24,$r2
+	stb	$r1,[$mac+6]
+	stb	$r2,[$mac+7]
+
+	srl	$h2,8,$r0
+	stb	$h2,[$mac+8]
+	srl	$h2,16,$r1
+	stb	$r0,[$mac+9]
+	srl	$h2,24,$r2
+	stb	$r1,[$mac+10]
+	stb	$r2,[$mac+11]
+
+	srl	$h3,8,$r0
+	stb	$h3,[$mac+12]
+	srl	$h3,16,$r1
+	stb	$r0,[$mac+13]
+	srl	$h3,24,$r2
+	stb	$r1,[$mac+14]
+	stb	$r2,[$mac+15]
+
+	ret
+	restore
+.type	poly1305_emit,#function
+.size	poly1305_emit,.-poly1305_emit
+___
+
+{
+my ($ctx,$inp,$len,$padbit) = map("%i$_",(0..3));
+my ($in0,$in1,$in2,$in3,$in4) = map("%o$_",(0..4));
+my ($i1,$step,$shr,$shl) = map("%l$_",(0..7));
+my $i2=$step;
+
+my ($h0lo,$h0hi,$h1lo,$h1hi,$h2lo,$h2hi,$h3lo,$h3hi,
+    $two0,$two32,$two64,$two96,$two130,$five_two130,
+    $r0lo,$r0hi,$r1lo,$r1hi,$r2lo,$r2hi,
+    $s2lo,$s2hi,$s3lo,$s3hi,
+    $c0lo,$c0hi,$c1lo,$c1hi,$c2lo,$c2hi,$c3lo,$c3hi) = map("%f".2*$_,(0..31));
+# borrowings
+my ($r3lo,$r3hi,$s1lo,$s1hi) = ($c0lo,$c0hi,$c1lo,$c1hi);
+my ($x0,$x1,$x2,$x3) = ($c2lo,$c2hi,$c3lo,$c3hi);
+my ($y0,$y1,$y2,$y3) = ($c1lo,$c1hi,$c3hi,$c3lo);
+
+$code.=<<___;
+.align	32
+poly1305_init_fma:
+	save	%sp,-STACK_FRAME-16,%sp
+	nop
+
+.Lpoly1305_init_fma:
+1:	call	.+8
+	add	%o7,.Lconsts_fma-1b,%o7
+
+	ldd	[%o7+8*0],$two0			! load constants
+	ldd	[%o7+8*1],$two32
+	ldd	[%o7+8*2],$two64
+	ldd	[%o7+8*3],$two96
+	ldd	[%o7+8*5],$five_two130
+
+	std	$two0,[$ctx+8*0]		! initial hash value, biased 0
+	std	$two32,[$ctx+8*1]
+	std	$two64,[$ctx+8*2]
+	std	$two96,[$ctx+8*3]
+
+	brz,pn	$inp,.Lno_key_fma
+	nop
+
+	stx	%fsr,[%sp+LOCALS]		! save original %fsr
+	ldx	[%o7+8*6],%fsr			! load new %fsr
+
+	std	$two0,[$ctx+8*4] 		! key "template"
+	std	$two32,[$ctx+8*5]
+	std	$two64,[$ctx+8*6]
+	std	$two96,[$ctx+8*7]
+
+	and	$inp,7,$shr
+	andn	$inp,7,$inp			! align pointer
+	mov	8,$i1
+	sll	$shr,3,$shr
+	mov	16,$i2
+	neg	$shr,$shl
+
+	ldxa	[$inp+%g0]0x88,$in0		! load little-endian key
+	ldxa	[$inp+$i1]0x88,$in2
+
+	brz	$shr,.Lkey_aligned_fma
+	sethi	%hi(0xf0000000),$i1		!   0xf0000000
+
+	ldxa	[$inp+$i2]0x88,$in4
+
+	srlx	$in0,$shr,$in0			! align data
+	sllx	$in2,$shl,$in1
+	srlx	$in2,$shr,$in2
+	or	$in1,$in0,$in0
+	sllx	$in4,$shl,$in3
+	or	$in3,$in2,$in2
+
+.Lkey_aligned_fma:
+	or	$i1,3,$i2			!   0xf0000003
+	srlx	$in0,32,$in1
+	andn	$in0,$i1,$in0			! &=0x0fffffff
+	andn	$in1,$i2,$in1			! &=0x0ffffffc
+	srlx	$in2,32,$in3
+	andn	$in2,$i2,$in2
+	andn	$in3,$i2,$in3
+
+	st	$in0,[$ctx+`8*4+4`]		! fill "template"
+	st	$in1,[$ctx+`8*5+4`]
+	st	$in2,[$ctx+`8*6+4`]
+	st	$in3,[$ctx+`8*7+4`]
+
+	ldd	[$ctx+8*4],$h0lo 		! load [biased] key
+	ldd	[$ctx+8*5],$h1lo
+	ldd	[$ctx+8*6],$h2lo
+	ldd	[$ctx+8*7],$h3lo
+
+	fsubd	$h0lo,$two0, $h0lo		! r0
+	 ldd	[%o7+8*7],$two0 		! more constants
+	fsubd	$h1lo,$two32,$h1lo		! r1
+	 ldd	[%o7+8*8],$two32
+	fsubd	$h2lo,$two64,$h2lo		! r2
+	 ldd	[%o7+8*9],$two64
+	fsubd	$h3lo,$two96,$h3lo		! r3
+	 ldd	[%o7+8*10],$two96
+
+	fmuld	$five_two130,$h1lo,$s1lo	! s1
+	fmuld	$five_two130,$h2lo,$s2lo	! s2
+	fmuld	$five_two130,$h3lo,$s3lo	! s3
+
+	faddd	$h0lo,$two0, $h0hi
+	faddd	$h1lo,$two32,$h1hi
+	faddd	$h2lo,$two64,$h2hi
+	faddd	$h3lo,$two96,$h3hi
+
+	fsubd	$h0hi,$two0, $h0hi
+	 ldd	[%o7+8*11],$two0		! more constants
+	fsubd	$h1hi,$two32,$h1hi
+	 ldd	[%o7+8*12],$two32
+	fsubd	$h2hi,$two64,$h2hi
+	 ldd	[%o7+8*13],$two64
+	fsubd	$h3hi,$two96,$h3hi
+
+	fsubd	$h0lo,$h0hi,$h0lo
+	 std	$h0hi,[$ctx+8*5] 		! r0hi
+	fsubd	$h1lo,$h1hi,$h1lo
+	 std	$h1hi,[$ctx+8*7] 		! r1hi
+	fsubd	$h2lo,$h2hi,$h2lo
+	 std	$h2hi,[$ctx+8*9] 		! r2hi
+	fsubd	$h3lo,$h3hi,$h3lo
+	 std	$h3hi,[$ctx+8*11]		! r3hi
+
+	faddd	$s1lo,$two0, $s1hi
+	faddd	$s2lo,$two32,$s2hi
+	faddd	$s3lo,$two64,$s3hi
+
+	fsubd	$s1hi,$two0, $s1hi
+	fsubd	$s2hi,$two32,$s2hi
+	fsubd	$s3hi,$two64,$s3hi
+
+	fsubd	$s1lo,$s1hi,$s1lo
+	fsubd	$s2lo,$s2hi,$s2lo
+	fsubd	$s3lo,$s3hi,$s3lo
+
+	ldx	[%sp+LOCALS],%fsr		! restore %fsr
+
+	std	$h0lo,[$ctx+8*4] 		! r0lo
+	std	$h1lo,[$ctx+8*6] 		! r1lo
+	std	$h2lo,[$ctx+8*8] 		! r2lo
+	std	$h3lo,[$ctx+8*10]		! r3lo
+
+	std	$s1hi,[$ctx+8*13]
+	std	$s2hi,[$ctx+8*15]
+	std	$s3hi,[$ctx+8*17]
+
+	std	$s1lo,[$ctx+8*12]
+	std	$s2lo,[$ctx+8*14]
+	std	$s3lo,[$ctx+8*16]
+
+	add	%o7,poly1305_blocks_fma-.Lconsts_fma,%o0
+	add	%o7,poly1305_emit_fma-.Lconsts_fma,%o1
+	STPTR	%o0,[%i2]
+	STPTR	%o1,[%i2+SIZE_T]
+
+	ret
+	restore	%g0,1,%o0			! return 1
+
+.Lno_key_fma:
+	ret
+	restore	%g0,%g0,%o0			! return 0
+.type	poly1305_init_fma,#function
+.size	poly1305_init_fma,.-poly1305_init_fma
+
+.align	32
+poly1305_blocks_fma:
+	save	%sp,-STACK_FRAME-48,%sp
+	srln	$len,4,$len
+
+	brz,pn	$len,.Labort
+	sub	$len,1,$len
+
+1:	call	.+8
+	add	%o7,.Lconsts_fma-1b,%o7
+
+	ldd	[%o7+8*0],$two0			! load constants
+	ldd	[%o7+8*1],$two32
+	ldd	[%o7+8*2],$two64
+	ldd	[%o7+8*3],$two96
+	ldd	[%o7+8*4],$two130
+	ldd	[%o7+8*5],$five_two130
+
+	ldd	[$ctx+8*0],$h0lo 		! load [biased] hash value
+	ldd	[$ctx+8*1],$h1lo
+	ldd	[$ctx+8*2],$h2lo
+	ldd	[$ctx+8*3],$h3lo
+
+	std	$two0,[%sp+LOCALS+8*0]		! input "template"
+	sethi	%hi((1023+52+96)<<20),$in3
+	std	$two32,[%sp+LOCALS+8*1]
+	or	$padbit,$in3,$in3
+	std	$two64,[%sp+LOCALS+8*2]
+	st	$in3,[%sp+LOCALS+8*3]
+
+	and	$inp,7,$shr
+	andn	$inp,7,$inp			! align pointer
+	mov	8,$i1
+	sll	$shr,3,$shr
+	mov	16,$step
+	neg	$shr,$shl
+
+	ldxa	[$inp+%g0]0x88,$in0		! load little-endian input
+	brz	$shr,.Linp_aligned_fma
+	ldxa	[$inp+$i1]0x88,$in2
+
+	ldxa	[$inp+$step]0x88,$in4
+	add	$inp,8,$inp
+
+	srlx	$in0,$shr,$in0			! align data
+	sllx	$in2,$shl,$in1
+	srlx	$in2,$shr,$in2
+	or	$in1,$in0,$in0
+	sllx	$in4,$shl,$in3
+	srlx	$in4,$shr,$in4			! pre-shift
+	or	$in3,$in2,$in2
+
+.Linp_aligned_fma:
+	srlx	$in0,32,$in1
+	movrz	$len,0,$step
+	srlx	$in2,32,$in3
+	add	$step,$inp,$inp			! conditional advance
+
+	st	$in0,[%sp+LOCALS+8*0+4]		! fill "template"
+	st	$in1,[%sp+LOCALS+8*1+4]
+	st	$in2,[%sp+LOCALS+8*2+4]
+	st	$in3,[%sp+LOCALS+8*3+4]
+
+	ldd	[$ctx+8*4],$r0lo 		! load key
+	ldd	[$ctx+8*5],$r0hi
+	ldd	[$ctx+8*6],$r1lo
+	ldd	[$ctx+8*7],$r1hi
+	ldd	[$ctx+8*8],$r2lo
+	ldd	[$ctx+8*9],$r2hi
+	ldd	[$ctx+8*10],$r3lo
+	ldd	[$ctx+8*11],$r3hi
+	ldd	[$ctx+8*12],$s1lo
+	ldd	[$ctx+8*13],$s1hi
+	ldd	[$ctx+8*14],$s2lo
+	ldd	[$ctx+8*15],$s2hi
+	ldd	[$ctx+8*16],$s3lo
+	ldd	[$ctx+8*17],$s3hi
+
+	stx	%fsr,[%sp+LOCALS+8*4]		! save original %fsr
+	ldx	[%o7+8*6],%fsr			! load new %fsr
+
+	subcc	$len,1,$len
+	movrz	$len,0,$step
+
+	ldd	[%sp+LOCALS+8*0],$x0		! load biased input
+	ldd	[%sp+LOCALS+8*1],$x1
+	ldd	[%sp+LOCALS+8*2],$x2
+	ldd	[%sp+LOCALS+8*3],$x3
+
+	fsubd	$h0lo,$two0, $h0lo		! de-bias hash value
+	fsubd	$h1lo,$two32,$h1lo
+	 ldxa	[$inp+%g0]0x88,$in0		! modulo-scheduled input load
+	fsubd	$h2lo,$two64,$h2lo
+	fsubd	$h3lo,$two96,$h3lo
+	 ldxa	[$inp+$i1]0x88,$in2
+
+	fsubd	$x0,$two0, $x0  		! de-bias input
+	fsubd	$x1,$two32,$x1
+	fsubd	$x2,$two64,$x2
+	fsubd	$x3,$two96,$x3
+
+	brz	$shr,.Linp_aligned_fma2
+	add	$step,$inp,$inp			! conditional advance
+
+	sllx	$in0,$shl,$in1			! align data
+	srlx	$in0,$shr,$in3
+	or	$in1,$in4,$in0
+	sllx	$in2,$shl,$in1
+	srlx	$in2,$shr,$in4			! pre-shift
+	or	$in3,$in1,$in2
+.Linp_aligned_fma2:
+	srlx	$in0,32,$in1
+	srlx	$in2,32,$in3
+
+	faddd	$h0lo,$x0,$x0			! accumulate input
+	 stw	$in0,[%sp+LOCALS+8*0+4]
+	faddd	$h1lo,$x1,$x1
+	 stw	$in1,[%sp+LOCALS+8*1+4]
+	faddd	$h2lo,$x2,$x2
+	 stw	$in2,[%sp+LOCALS+8*2+4]
+	faddd	$h3lo,$x3,$x3
+	 stw	$in3,[%sp+LOCALS+8*3+4]
+
+	b	.Lentry_fma
+	nop
+
+.align	16
+.Loop_fma:
+	ldxa	[$inp+%g0]0x88,$in0		! modulo-scheduled input load
+	ldxa	[$inp+$i1]0x88,$in2
+	movrz	$len,0,$step
+
+	faddd	$y0,$h0lo,$h0lo 		! accumulate input
+	faddd	$y1,$h0hi,$h0hi
+	faddd	$y2,$h2lo,$h2lo
+	faddd	$y3,$h2hi,$h2hi
+
+	brz,pn	$shr,.Linp_aligned_fma3
+	add	$step,$inp,$inp			! conditional advance
+
+	sllx	$in0,$shl,$in1			! align data
+	srlx	$in0,$shr,$in3
+	or	$in1,$in4,$in0
+	sllx	$in2,$shl,$in1
+	srlx	$in2,$shr,$in4			! pre-shift
+	or	$in3,$in1,$in2
+
+.Linp_aligned_fma3:
+	!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! base 2^48 -> base 2^32
+	faddd	$two64,$h1lo,$c1lo
+	 srlx	$in0,32,$in1
+	faddd	$two64,$h1hi,$c1hi
+	 srlx	$in2,32,$in3
+	faddd	$two130,$h3lo,$c3lo
+	 st	$in0,[%sp+LOCALS+8*0+4]		! fill "template"
+	faddd	$two130,$h3hi,$c3hi
+	 st	$in1,[%sp+LOCALS+8*1+4]
+	faddd	$two32,$h0lo,$c0lo
+	 st	$in2,[%sp+LOCALS+8*2+4]
+	faddd	$two32,$h0hi,$c0hi
+	 st	$in3,[%sp+LOCALS+8*3+4]
+	faddd	$two96,$h2lo,$c2lo
+	faddd	$two96,$h2hi,$c2hi
+
+	fsubd	$c1lo,$two64,$c1lo
+	fsubd	$c1hi,$two64,$c1hi
+	fsubd	$c3lo,$two130,$c3lo
+	fsubd	$c3hi,$two130,$c3hi
+	fsubd	$c0lo,$two32,$c0lo
+	fsubd	$c0hi,$two32,$c0hi
+	fsubd	$c2lo,$two96,$c2lo
+	fsubd	$c2hi,$two96,$c2hi
+
+	fsubd	$h1lo,$c1lo,$h1lo
+	fsubd	$h1hi,$c1hi,$h1hi
+	fsubd	$h3lo,$c3lo,$h3lo
+	fsubd	$h3hi,$c3hi,$h3hi
+	fsubd	$h2lo,$c2lo,$h2lo
+	fsubd	$h2hi,$c2hi,$h2hi
+	fsubd	$h0lo,$c0lo,$h0lo
+	fsubd	$h0hi,$c0hi,$h0hi
+
+	faddd	$h1lo,$c0lo,$h1lo
+	faddd	$h1hi,$c0hi,$h1hi
+	faddd	$h3lo,$c2lo,$h3lo
+	faddd	$h3hi,$c2hi,$h3hi
+	faddd	$h2lo,$c1lo,$h2lo
+	faddd	$h2hi,$c1hi,$h2hi
+	fmaddd	$five_two130,$c3lo,$h0lo,$h0lo
+	fmaddd	$five_two130,$c3hi,$h0hi,$h0hi
+
+	faddd	$h1lo,$h1hi,$x1
+	 ldd	[$ctx+8*12],$s1lo		! reload constants
+	faddd	$h3lo,$h3hi,$x3
+	 ldd	[$ctx+8*13],$s1hi
+	faddd	$h2lo,$h2hi,$x2
+	 ldd	[$ctx+8*10],$r3lo
+	faddd	$h0lo,$h0hi,$x0
+	 ldd	[$ctx+8*11],$r3hi
+
+.Lentry_fma:
+	fmuld	$x1,$s3lo,$h0lo
+	fmuld	$x1,$s3hi,$h0hi
+	fmuld	$x1,$r1lo,$h2lo
+	fmuld	$x1,$r1hi,$h2hi
+	fmuld	$x1,$r0lo,$h1lo
+	fmuld	$x1,$r0hi,$h1hi
+	fmuld	$x1,$r2lo,$h3lo
+	fmuld	$x1,$r2hi,$h3hi
+
+	fmaddd	$x3,$s1lo,$h0lo,$h0lo
+	fmaddd	$x3,$s1hi,$h0hi,$h0hi
+	fmaddd	$x3,$s3lo,$h2lo,$h2lo
+	fmaddd	$x3,$s3hi,$h2hi,$h2hi
+	fmaddd	$x3,$s2lo,$h1lo,$h1lo
+	fmaddd	$x3,$s2hi,$h1hi,$h1hi
+	fmaddd	$x3,$r0lo,$h3lo,$h3lo
+	fmaddd	$x3,$r0hi,$h3hi,$h3hi
+
+	fmaddd	$x2,$s2lo,$h0lo,$h0lo
+	fmaddd	$x2,$s2hi,$h0hi,$h0hi
+	fmaddd	$x2,$r0lo,$h2lo,$h2lo
+	fmaddd	$x2,$r0hi,$h2hi,$h2hi
+	fmaddd	$x2,$s3lo,$h1lo,$h1lo
+	 ldd	[%sp+LOCALS+8*0],$y0		! load [biased] input
+	fmaddd	$x2,$s3hi,$h1hi,$h1hi
+	 ldd	[%sp+LOCALS+8*1],$y1
+	fmaddd	$x2,$r1lo,$h3lo,$h3lo
+	 ldd	[%sp+LOCALS+8*2],$y2
+	fmaddd	$x2,$r1hi,$h3hi,$h3hi
+	 ldd	[%sp+LOCALS+8*3],$y3
+
+	fmaddd	$x0,$r0lo,$h0lo,$h0lo
+	 fsubd	$y0,$two0, $y0  		! de-bias input
+	fmaddd	$x0,$r0hi,$h0hi,$h0hi
+	 fsubd	$y1,$two32,$y1
+	fmaddd	$x0,$r2lo,$h2lo,$h2lo
+	 fsubd	$y2,$two64,$y2
+	fmaddd	$x0,$r2hi,$h2hi,$h2hi
+	 fsubd	$y3,$two96,$y3
+	fmaddd	$x0,$r1lo,$h1lo,$h1lo
+	fmaddd	$x0,$r1hi,$h1hi,$h1hi
+	fmaddd	$x0,$r3lo,$h3lo,$h3lo
+	fmaddd	$x0,$r3hi,$h3hi,$h3hi
+
+	bcc	SIZE_T_CC,.Loop_fma
+	subcc	$len,1,$len
+
+	!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! base 2^48 -> base 2^32
+	faddd	$h0lo,$two32,$c0lo
+	faddd	$h0hi,$two32,$c0hi
+	faddd	$h2lo,$two96,$c2lo
+	faddd	$h2hi,$two96,$c2hi
+	faddd	$h1lo,$two64,$c1lo
+	faddd	$h1hi,$two64,$c1hi
+	faddd	$h3lo,$two130,$c3lo
+	faddd	$h3hi,$two130,$c3hi
+
+	fsubd	$c0lo,$two32,$c0lo
+	fsubd	$c0hi,$two32,$c0hi
+	fsubd	$c2lo,$two96,$c2lo
+	fsubd	$c2hi,$two96,$c2hi
+	fsubd	$c1lo,$two64,$c1lo
+	fsubd	$c1hi,$two64,$c1hi
+	fsubd	$c3lo,$two130,$c3lo
+	fsubd	$c3hi,$two130,$c3hi
+
+	fsubd	$h1lo,$c1lo,$h1lo
+	fsubd	$h1hi,$c1hi,$h1hi
+	fsubd	$h3lo,$c3lo,$h3lo
+	fsubd	$h3hi,$c3hi,$h3hi
+	fsubd	$h2lo,$c2lo,$h2lo
+	fsubd	$h2hi,$c2hi,$h2hi
+	fsubd	$h0lo,$c0lo,$h0lo
+	fsubd	$h0hi,$c0hi,$h0hi
+
+	faddd	$h1lo,$c0lo,$h1lo
+	faddd	$h1hi,$c0hi,$h1hi
+	faddd	$h3lo,$c2lo,$h3lo
+	faddd	$h3hi,$c2hi,$h3hi
+	faddd	$h2lo,$c1lo,$h2lo
+	faddd	$h2hi,$c1hi,$h2hi
+	fmaddd	$five_two130,$c3lo,$h0lo,$h0lo
+	fmaddd	$five_two130,$c3hi,$h0hi,$h0hi
+
+	faddd	$h1lo,$h1hi,$x1
+	faddd	$h3lo,$h3hi,$x3
+	faddd	$h2lo,$h2hi,$x2
+	faddd	$h0lo,$h0hi,$x0
+
+	faddd	$x1,$two32,$x1  		! bias
+	faddd	$x3,$two96,$x3
+	faddd	$x2,$two64,$x2
+	faddd	$x0,$two0, $x0
+
+	ldx	[%sp+LOCALS+8*4],%fsr		! restore saved %fsr
+
+	std	$x1,[$ctx+8*1]			! store [biased] hash value
+	std	$x3,[$ctx+8*3]
+	std	$x2,[$ctx+8*2]
+	std	$x0,[$ctx+8*0]
+
+.Labort:
+	ret
+	restore
+.type	poly1305_blocks_fma,#function
+.size	poly1305_blocks_fma,.-poly1305_blocks_fma
+___
+{
+my ($mac,$nonce)=($inp,$len);
+
+my ($h0,$h1,$h2,$h3,$h4, $d0,$d1,$d2,$d3, $mask
+   ) = (map("%l$_",(0..5)),map("%o$_",(0..4)));
+
+$code.=<<___;
+.align	32
+poly1305_emit_fma:
+	save	%sp,-STACK_FRAME,%sp
+
+	ld	[$ctx+8*0+0],$d0		! load hash
+	ld	[$ctx+8*0+4],$h0
+	ld	[$ctx+8*1+0],$d1
+	ld	[$ctx+8*1+4],$h1
+	ld	[$ctx+8*2+0],$d2
+	ld	[$ctx+8*2+4],$h2
+	ld	[$ctx+8*3+0],$d3
+	ld	[$ctx+8*3+4],$h3
+
+	sethi	%hi(0xfff00000),$mask
+	andn	$d0,$mask,$d0			! mask exponent
+	andn	$d1,$mask,$d1
+	andn	$d2,$mask,$d2
+	andn	$d3,$mask,$d3			! can be partially reduced...
+	mov	3,$mask
+
+	srl	$d3,2,$padbit			! ... so reduce
+	and	$d3,$mask,$h4
+	andn	$d3,$mask,$d3
+	add	$padbit,$d3,$d3
+
+	addcc	$d3,$h0,$h0
+	addccc	$d0,$h1,$h1
+	addccc	$d1,$h2,$h2
+	addccc	$d2,$h3,$h3
+	addc	%g0,$h4,$h4
+
+	addcc	$h0,5,$d0			! compare to modulus
+	addccc	$h1,0,$d1
+	addccc	$h2,0,$d2
+	addccc	$h3,0,$d3
+	addc	$h4,0,$mask
+
+	srl	$mask,2,$mask			! did it carry/borrow?
+	neg	$mask,$mask
+	sra	$mask,31,$mask			! mask
+
+	andn	$h0,$mask,$h0
+	and	$d0,$mask,$d0
+	andn	$h1,$mask,$h1
+	and	$d1,$mask,$d1
+	or	$d0,$h0,$h0
+	ld	[$nonce+0],$d0			! load nonce
+	andn	$h2,$mask,$h2
+	and	$d2,$mask,$d2
+	or	$d1,$h1,$h1
+	ld	[$nonce+4],$d1
+	andn	$h3,$mask,$h3
+	and	$d3,$mask,$d3
+	or	$d2,$h2,$h2
+	ld	[$nonce+8],$d2
+	or	$d3,$h3,$h3
+	ld	[$nonce+12],$d3
+
+	addcc	$d0,$h0,$h0			! accumulate nonce
+	addccc	$d1,$h1,$h1
+	addccc	$d2,$h2,$h2
+	addc	$d3,$h3,$h3
+
+	stb	$h0,[$mac+0]			! write little-endian result
+	srl	$h0,8,$h0
+	stb	$h1,[$mac+4]
+	srl	$h1,8,$h1
+	stb	$h2,[$mac+8]
+	srl	$h2,8,$h2
+	stb	$h3,[$mac+12]
+	srl	$h3,8,$h3
+
+	stb	$h0,[$mac+1]
+	srl	$h0,8,$h0
+	stb	$h1,[$mac+5]
+	srl	$h1,8,$h1
+	stb	$h2,[$mac+9]
+	srl	$h2,8,$h2
+	stb	$h3,[$mac+13]
+	srl	$h3,8,$h3
+
+	stb	$h0,[$mac+2]
+	srl	$h0,8,$h0
+	stb	$h1,[$mac+6]
+	srl	$h1,8,$h1
+	stb	$h2,[$mac+10]
+	srl	$h2,8,$h2
+	stb	$h3,[$mac+14]
+	srl	$h3,8,$h3
+
+	stb	$h0,[$mac+3]
+	stb	$h1,[$mac+7]
+	stb	$h2,[$mac+11]
+	stb	$h3,[$mac+15]
+
+	ret
+	restore
+.type	poly1305_emit_fma,#function
+.size	poly1305_emit_fma,.-poly1305_emit_fma
+___
+}
+
+$code.=<<___;
+.align	64
+.Lconsts_fma:
+.word	0x43300000,0x00000000		! 2^(52+0)
+.word	0x45300000,0x00000000		! 2^(52+32)
+.word	0x47300000,0x00000000		! 2^(52+64)
+.word	0x49300000,0x00000000		! 2^(52+96)
+.word	0x4b500000,0x00000000		! 2^(52+130)
+
+.word	0x37f40000,0x00000000		! 5/2^130
+.word	0,1<<30				! fsr: truncate, no exceptions
+
+.word	0x44300000,0x00000000		! 2^(52+16+0)
+.word	0x46300000,0x00000000		! 2^(52+16+32)
+.word	0x48300000,0x00000000		! 2^(52+16+64)
+.word	0x4a300000,0x00000000		! 2^(52+16+96)
+.word	0x3e300000,0x00000000		! 2^(52+16+0-96)
+.word	0x40300000,0x00000000		! 2^(52+16+32-96)
+.word	0x42300000,0x00000000		! 2^(52+16+64-96)
+.asciz	"Poly1305 for SPARCv9/VIS3/FMA, CRYPTOGAMS by "
+.align	4
+___
+}
+
+# Purpose of these subroutines is to explicitly encode VIS instructions,
+# so that one can compile the module without having to specify VIS
+# extensions on compiler command line, e.g. -xarch=v9 vs. -xarch=v9a.
+# Idea is to reserve for option to produce "universal" binary and let
+# programmer detect if current CPU is VIS capable at run-time.
+sub unvis3 {
+my ($mnemonic,$rs1,$rs2,$rd)=@_;
+my %bias = ( "g" => 0, "o" => 8, "l" => 16, "i" => 24 );
+my ($ref,$opf);
+my %visopf = (	"addxc"		=> 0x011,
+		"addxccc"	=> 0x013,
+		"umulxhi"	=> 0x016	);
+
+    $ref = "$mnemonic\t$rs1,$rs2,$rd";
+
+    if ($opf=$visopf{$mnemonic}) {
+	foreach ($rs1,$rs2,$rd) {
+	    return $ref if (!/%([goli])([0-9])/);
+	    $_=$bias{$1}+$2;
+	}
+
+	return	sprintf ".word\t0x%08x !%s",
+			0x81b00000|$rd<<25|$rs1<<14|$opf<<5|$rs2,
+			$ref;
+    } else {
+	return $ref;
+    }
+}
+
+sub unfma {
+my ($mnemonic,$rs1,$rs2,$rs3,$rd)=@_;
+my ($ref,$opf);
+my %fmaopf = (	"fmadds"	=> 0x1,
+		"fmaddd"	=> 0x2,
+		"fmsubs"	=> 0x5,
+		"fmsubd"	=> 0x6		);
+
+    $ref = "$mnemonic\t$rs1,$rs2,$rs3,$rd";
+
+    if ($opf=$fmaopf{$mnemonic}) {
+	foreach ($rs1,$rs2,$rs3,$rd) {
+	    return $ref if (!/%f([0-9]{1,2})/);
+	    $_=$1;
+	    if ($1>=32) {
+		return $ref if ($1&1);
+		# re-encode for upper double register addressing
+		$_=($1|$1>>5)&31;
+	    }
+	}
+
+	return	sprintf ".word\t0x%08x !%s",
+			0x81b80000|$rd<<25|$rs1<<14|$rs3<<9|$opf<<5|$rs2,
+			$ref;
+    } else {
+	return $ref;
+    }
+}
+
+foreach (split("\n",$code)) {
+	s/\`([^\`]*)\`/eval $1/ge;
+
+	s/\b(umulxhi|addxc[c]{0,2})\s+(%[goli][0-7]),\s*(%[goli][0-7]),\s*(%[goli][0-7])/
+		&unvis3($1,$2,$3,$4)
+	 /ge	or
+	s/\b(fmadd[sd])\s+(%f[0-9]+),\s*(%f[0-9]+),\s*(%f[0-9]+),\s*(%f[0-9]+)/
+		&unfma($1,$2,$3,$4,$5)
+	 /ge;
+
+	print $_,"\n";
+}
+
+close STDOUT;
diff --git a/crypto/poly1305/asm/poly1305-x86.pl b/crypto/poly1305/asm/poly1305-x86.pl
new file mode 100755
index 000000000..ab24dfcfa
--- /dev/null
+++ b/crypto/poly1305/asm/poly1305-x86.pl
@@ -0,0 +1,1814 @@
+#! /usr/bin/env perl
+# Copyright 2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+#
+# ====================================================================
+# Written by Andy Polyakov  for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+#
+# This module implements Poly1305 hash for x86.
+#
+# April 2015
+#
+# Numbers are cycles per processed byte with poly1305_blocks alone,
+# measured with rdtsc at fixed clock frequency.
+#
+#		IALU/gcc-3.4(*)	SSE2(**)	AVX2
+# Pentium	15.7/+80%	-
+# PIII		6.21/+90%	-
+# P4		19.8/+40%	3.24
+# Core 2	4.85/+90%	1.80
+# Westmere	4.58/+100%	1.43
+# Sandy Bridge	3.90/+100%	1.36
+# Haswell	3.88/+70%	1.18		0.72
+# Silvermont	11.0/+40%	4.80
+# Goldmont	4.10/+200%	2.10
+# VIA Nano	6.71/+90%	2.47
+# Sledgehammer	3.51/+180%	4.27
+# Bulldozer	4.53/+140%	1.31
+#
+# (*)	gcc 4.8 for some reason generated worse code;
+# (**)	besides SSE2 there are floating-point and AVX options; FP
+#	is deemed unnecessary, because pre-SSE2 processor are too
+#	old to care about, while it's not the fastest option on
+#	SSE2-capable ones; AVX is omitted, because it doesn't give
+#	a lot of improvement, 5-10% depending on processor;
+
+$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+push(@INC,"${dir}","${dir}../../perlasm");
+require "x86asm.pl";
+
+$output=pop;
+open STDOUT,">$output";
+
+&asm_init($ARGV[0],"poly1305-x86.pl",$ARGV[$#ARGV] eq "386");
+
+$sse2=$avx=0;
+for (@ARGV) { $sse2=1 if (/-DOPENSSL_IA32_SSE2/); }
+
+if ($sse2) {
+	&static_label("const_sse2");
+	&static_label("enter_blocks");
+	&static_label("enter_emit");
+	&external_label("OPENSSL_ia32cap_P");
+
+	if (`$ENV{CC} -Wa,-v -c -o /dev/null -x assembler /dev/null 2>&1`
+			=~ /GNU assembler version ([2-9]\.[0-9]+)/) {
+		$avx = ($1>=2.19) + ($1>=2.22);
+	}
+
+	if (!$avx && $ARGV[0] eq "win32n" &&
+	   `nasm -v 2>&1` =~ /NASM version ([2-9]\.[0-9]+)/) {
+	$avx = ($1>=2.09) + ($1>=2.10);
+	}
+
+	if (!$avx && `$ENV{CC} -v 2>&1` =~ /(^clang version|based on LLVM) ([3-9]\.[0-9]+)/) {
+		$avx = ($2>=3.0) + ($2>3.0);
+	}
+}
+
+########################################################################
+# Layout of opaque area is following.
+#
+#	unsigned __int32 h[5];		# current hash value base 2^32
+#	unsigned __int32 pad;		# is_base2_26 in vector context
+#	unsigned __int32 r[4];		# key value base 2^32
+
+&align(64);
+&function_begin("poly1305_init");
+	&mov	("edi",&wparam(0));		# context
+	&mov	("esi",&wparam(1));		# key
+	&mov	("ebp",&wparam(2));		# function table
+
+	&xor	("eax","eax");
+	&mov	(&DWP(4*0,"edi"),"eax");	# zero hash value
+	&mov	(&DWP(4*1,"edi"),"eax");
+	&mov	(&DWP(4*2,"edi"),"eax");
+	&mov	(&DWP(4*3,"edi"),"eax");
+	&mov	(&DWP(4*4,"edi"),"eax");
+	&mov	(&DWP(4*5,"edi"),"eax");	# is_base2_26
+
+	&cmp	("esi",0);
+	&je	(&label("nokey"));
+
+    if ($sse2) {
+	&call	(&label("pic_point"));
+    &set_label("pic_point");
+	&blindpop("ebx");
+
+	&lea	("eax",&DWP("poly1305_blocks-".&label("pic_point"),"ebx"));
+	&lea	("edx",&DWP("poly1305_emit-".&label("pic_point"),"ebx"));
+
+	&picmeup("edi","OPENSSL_ia32cap_P","ebx",&label("pic_point"));
+	&mov	("ecx",&DWP(0,"edi"));
+	&and	("ecx",1<<26|1<<24);
+	&cmp	("ecx",1<<26|1<<24);		# SSE2 and XMM?
+	&jne	(&label("no_sse2"));
+
+	&lea	("eax",&DWP("_poly1305_blocks_sse2-".&label("pic_point"),"ebx"));
+	&lea	("edx",&DWP("_poly1305_emit_sse2-".&label("pic_point"),"ebx"));
+
+      if ($avx>1) {
+	&mov	("ecx",&DWP(8,"edi"));
+	&test	("ecx",1<<5);			# AVX2?
+	&jz	(&label("no_sse2"));
+
+	&lea	("eax",&DWP("_poly1305_blocks_avx2-".&label("pic_point"),"ebx"));
+      }
+    &set_label("no_sse2");
+	&mov	("edi",&wparam(0));		# reload context
+	&mov	(&DWP(0,"ebp"),"eax");		# fill function table
+	&mov	(&DWP(4,"ebp"),"edx");
+    }
+
+	&mov	("eax",&DWP(4*0,"esi"));	# load input key
+	&mov	("ebx",&DWP(4*1,"esi"));
+	&mov	("ecx",&DWP(4*2,"esi"));
+	&mov	("edx",&DWP(4*3,"esi"));
+	&and	("eax",0x0fffffff);
+	&and	("ebx",0x0ffffffc);
+	&and	("ecx",0x0ffffffc);
+	&and	("edx",0x0ffffffc);
+	&mov	(&DWP(4*6,"edi"),"eax");
+	&mov	(&DWP(4*7,"edi"),"ebx");
+	&mov	(&DWP(4*8,"edi"),"ecx");
+	&mov	(&DWP(4*9,"edi"),"edx");
+
+	&mov	("eax",$sse2);
+&set_label("nokey");
+&function_end("poly1305_init");
+
+($h0,$h1,$h2,$h3,$h4,
+ $d0,$d1,$d2,$d3,
+ $r0,$r1,$r2,$r3,
+     $s1,$s2,$s3)=map(4*$_,(0..15));
+
+&function_begin("poly1305_blocks");
+	&mov	("edi",&wparam(0));		# ctx
+	&mov	("esi",&wparam(1));		# inp
+	&mov	("ecx",&wparam(2));		# len
+&set_label("enter_blocks");
+	&and	("ecx",-15);
+	&jz	(&label("nodata"));
+
+	&stack_push(16);
+	&mov	("eax",&DWP(4*6,"edi"));	# r0
+	&mov	("ebx",&DWP(4*7,"edi"));	# r1
+	 &lea	("ebp",&DWP(0,"esi","ecx"));	# end of input
+	&mov	("ecx",&DWP(4*8,"edi"));	# r2
+	&mov	("edx",&DWP(4*9,"edi"));	# r3
+
+	&mov	(&wparam(2),"ebp");
+	&mov	("ebp","esi");
+
+	&mov	(&DWP($r0,"esp"),"eax");	# r0
+	&mov	("eax","ebx");
+	&shr	("eax",2);
+	&mov	(&DWP($r1,"esp"),"ebx");	# r1
+	&add	("eax","ebx");			# s1
+	&mov	("ebx","ecx");
+	&shr	("ebx",2);
+	&mov	(&DWP($r2,"esp"),"ecx");	# r2
+	&add	("ebx","ecx");			# s2
+	&mov	("ecx","edx");
+	&shr	("ecx",2);
+	&mov	(&DWP($r3,"esp"),"edx");	# r3
+	&add	("ecx","edx");			# s3
+	&mov	(&DWP($s1,"esp"),"eax");	# s1
+	&mov	(&DWP($s2,"esp"),"ebx");	# s2
+	&mov	(&DWP($s3,"esp"),"ecx");	# s3
+
+	&mov	("eax",&DWP(4*0,"edi"));	# load hash value
+	&mov	("ebx",&DWP(4*1,"edi"));
+	&mov	("ecx",&DWP(4*2,"edi"));
+	&mov	("esi",&DWP(4*3,"edi"));
+	&mov	("edi",&DWP(4*4,"edi"));
+	&jmp	(&label("loop"));
+
+&set_label("loop",32);
+	&add	("eax",&DWP(4*0,"ebp"));	# accumulate input
+	&adc	("ebx",&DWP(4*1,"ebp"));
+	&adc	("ecx",&DWP(4*2,"ebp"));
+	&adc	("esi",&DWP(4*3,"ebp"));
+	&lea	("ebp",&DWP(4*4,"ebp"));
+	&adc	("edi",&wparam(3));		# padbit
+
+	&mov	(&DWP($h0,"esp"),"eax");	# put aside hash[+inp]
+	&mov	(&DWP($h3,"esp"),"esi");
+
+	&mul	(&DWP($r0,"esp"));		# h0*r0
+	 &mov	(&DWP($h4,"esp"),"edi");
+	&mov	("edi","eax");
+	&mov	("eax","ebx");			# h1
+	&mov	("esi","edx");
+	&mul	(&DWP($s3,"esp"));		# h1*s3
+	&add	("edi","eax");
+	&mov	("eax","ecx");			# h2
+	&adc	("esi","edx");
+	&mul	(&DWP($s2,"esp"));		# h2*s2
+	&add	("edi","eax");
+	&mov	("eax",&DWP($h3,"esp"));
+	&adc	("esi","edx");
+	&mul	(&DWP($s1,"esp"));		# h3*s1
+	&add	("edi","eax");
+	 &mov	("eax",&DWP($h0,"esp"));
+	&adc	("esi","edx");
+
+	&mul	(&DWP($r1,"esp"));		# h0*r1
+	 &mov	(&DWP($d0,"esp"),"edi");
+	&xor	("edi","edi");
+	&add	("esi","eax");
+	&mov	("eax","ebx");			# h1
+	&adc	("edi","edx");
+	&mul	(&DWP($r0,"esp"));		# h1*r0
+	&add	("esi","eax");
+	&mov	("eax","ecx");			# h2
+	&adc	("edi","edx");
+	&mul	(&DWP($s3,"esp"));		# h2*s3
+	&add	("esi","eax");
+	&mov	("eax",&DWP($h3,"esp"));
+	&adc	("edi","edx");
+	&mul	(&DWP($s2,"esp"));		# h3*s2
+	&add	("esi","eax");
+	&mov	("eax",&DWP($h4,"esp"));
+	&adc	("edi","edx");
+	&imul	("eax",&DWP($s1,"esp"));	# h4*s1
+	&add	("esi","eax");
+	 &mov	("eax",&DWP($h0,"esp"));
+	&adc	("edi",0);
+
+	&mul	(&DWP($r2,"esp"));		# h0*r2
+	 &mov	(&DWP($d1,"esp"),"esi");
+	&xor	("esi","esi");
+	&add	("edi","eax");
+	&mov	("eax","ebx");			# h1
+	&adc	("esi","edx");
+	&mul	(&DWP($r1,"esp"));		# h1*r1
+	&add	("edi","eax");
+	&mov	("eax","ecx");			# h2
+	&adc	("esi","edx");
+	&mul	(&DWP($r0,"esp"));		# h2*r0
+	&add	("edi","eax");
+	&mov	("eax",&DWP($h3,"esp"));
+	&adc	("esi","edx");
+	&mul	(&DWP($s3,"esp"));		# h3*s3
+	&add	("edi","eax");
+	&mov	("eax",&DWP($h4,"esp"));
+	&adc	("esi","edx");
+	&imul	("eax",&DWP($s2,"esp"));	# h4*s2
+	&add	("edi","eax");
+	 &mov	("eax",&DWP($h0,"esp"));
+	&adc	("esi",0);
+
+	&mul	(&DWP($r3,"esp"));		# h0*r3
+	 &mov	(&DWP($d2,"esp"),"edi");
+	&xor	("edi","edi");
+	&add	("esi","eax");
+	&mov	("eax","ebx");			# h1
+	&adc	("edi","edx");
+	&mul	(&DWP($r2,"esp"));		# h1*r2
+	&add	("esi","eax");
+	&mov	("eax","ecx");			# h2
+	&adc	("edi","edx");
+	&mul	(&DWP($r1,"esp"));		# h2*r1
+	&add	("esi","eax");
+	&mov	("eax",&DWP($h3,"esp"));
+	&adc	("edi","edx");
+	&mul	(&DWP($r0,"esp"));		# h3*r0
+	&add	("esi","eax");
+	 &mov	("ecx",&DWP($h4,"esp"));
+	&adc	("edi","edx");
+
+	&mov	("edx","ecx");
+	&imul	("ecx",&DWP($s3,"esp"));	# h4*s3
+	&add	("esi","ecx");
+	 &mov	("eax",&DWP($d0,"esp"));
+	&adc	("edi",0);
+
+	&imul	("edx",&DWP($r0,"esp"));	# h4*r0
+	&add	("edx","edi");
+
+	&mov	("ebx",&DWP($d1,"esp"));
+	&mov	("ecx",&DWP($d2,"esp"));
+
+	&mov	("edi","edx");			# last reduction step
+	&shr	("edx",2);
+	&and	("edi",3);
+	&lea	("edx",&DWP(0,"edx","edx",4));	# *5
+	&add	("eax","edx");
+	&adc	("ebx",0);
+	&adc	("ecx",0);
+	&adc	("esi",0);
+	&adc	("edi",0);
+
+	&cmp	("ebp",&wparam(2));		# done yet?
+	&jne	(&label("loop"));
+
+	&mov	("edx",&wparam(0));		# ctx
+	&stack_pop(16);
+	&mov	(&DWP(4*0,"edx"),"eax");	# store hash value
+	&mov	(&DWP(4*1,"edx"),"ebx");
+	&mov	(&DWP(4*2,"edx"),"ecx");
+	&mov	(&DWP(4*3,"edx"),"esi");
+	&mov	(&DWP(4*4,"edx"),"edi");
+&set_label("nodata");
+&function_end("poly1305_blocks");
+
+&function_begin("poly1305_emit");
+	&mov	("ebp",&wparam(0));		# context
+&set_label("enter_emit");
+	&mov	("edi",&wparam(1));		# output
+	&mov	("eax",&DWP(4*0,"ebp"));	# load hash value
+	&mov	("ebx",&DWP(4*1,"ebp"));
+	&mov	("ecx",&DWP(4*2,"ebp"));
+	&mov	("edx",&DWP(4*3,"ebp"));
+	&mov	("esi",&DWP(4*4,"ebp"));
+
+	&add	("eax",5);			# compare to modulus
+	&adc	("ebx",0);
+	&adc	("ecx",0);
+	&adc	("edx",0);
+	&adc	("esi",0);
+	&shr	("esi",2);			# did it carry/borrow?
+	&neg	("esi");			# do we choose hash-modulus?
+
+	&and	("eax","esi");
+	&and	("ebx","esi");
+	&and	("ecx","esi");
+	&and	("edx","esi");
+	&mov	(&DWP(4*0,"edi"),"eax");
+	&mov	(&DWP(4*1,"edi"),"ebx");
+	&mov	(&DWP(4*2,"edi"),"ecx");
+	&mov	(&DWP(4*3,"edi"),"edx");
+
+	¬	("esi");			# or original hash value?
+	&mov	("eax",&DWP(4*0,"ebp"));
+	&mov	("ebx",&DWP(4*1,"ebp"));
+	&mov	("ecx",&DWP(4*2,"ebp"));
+	&mov	("edx",&DWP(4*3,"ebp"));
+	&mov	("ebp",&wparam(2));
+	&and	("eax","esi");
+	&and	("ebx","esi");
+	&and	("ecx","esi");
+	&and	("edx","esi");
+	&or	("eax",&DWP(4*0,"edi"));
+	&or	("ebx",&DWP(4*1,"edi"));
+	&or	("ecx",&DWP(4*2,"edi"));
+	&or	("edx",&DWP(4*3,"edi"));
+
+	&add	("eax",&DWP(4*0,"ebp"));	# accumulate key
+	&adc	("ebx",&DWP(4*1,"ebp"));
+	&adc	("ecx",&DWP(4*2,"ebp"));
+	&adc	("edx",&DWP(4*3,"ebp"));
+
+	&mov	(&DWP(4*0,"edi"),"eax");
+	&mov	(&DWP(4*1,"edi"),"ebx");
+	&mov	(&DWP(4*2,"edi"),"ecx");
+	&mov	(&DWP(4*3,"edi"),"edx");
+&function_end("poly1305_emit");
+
+if ($sse2) {
+########################################################################
+# Layout of opaque area is following.
+#
+#	unsigned __int32 h[5];		# current hash value base 2^26
+#	unsigned __int32 is_base2_26;
+#	unsigned __int32 r[4];		# key value base 2^32
+#	unsigned __int32 pad[2];
+#	struct { unsigned __int32 r^4, r^3, r^2, r^1; } r[9];
+#
+# where r^n are base 2^26 digits of degrees of multiplier key. There are
+# 5 digits, but last four are interleaved with multiples of 5, totalling
+# in 9 elements: r0, r1, 5*r1, r2, 5*r2, r3, 5*r3, r4, 5*r4.
+
+my ($D0,$D1,$D2,$D3,$D4,$T0,$T1,$T2)=map("xmm$_",(0..7));
+my $MASK=$T2;	# borrow and keep in mind
+
+&align	(32);
+&function_begin_B("_poly1305_init_sse2");
+	&movdqu		($D4,&QWP(4*6,"edi"));		# key base 2^32
+	&lea		("edi",&DWP(16*3,"edi"));	# size optimization
+	&mov		("ebp","esp");
+	&sub		("esp",16*(9+5));
+	&and		("esp",-16);
+
+	#&pand		($D4,&QWP(96,"ebx"));		# magic mask
+	&movq		($MASK,&QWP(64,"ebx"));
+
+	&movdqa		($D0,$D4);
+	&movdqa		($D1,$D4);
+	&movdqa		($D2,$D4);
+
+	&pand		($D0,$MASK);			# -> base 2^26
+	&psrlq		($D1,26);
+	&psrldq		($D2,6);
+	&pand		($D1,$MASK);
+	&movdqa		($D3,$D2);
+	&psrlq		($D2,4)
+	&psrlq		($D3,30);
+	&pand		($D2,$MASK);
+	&pand		($D3,$MASK);
+	&psrldq		($D4,13);
+
+	&lea		("edx",&DWP(16*9,"esp"));	# size optimization
+	&mov		("ecx",2);
+&set_label("square");
+	&movdqa		(&QWP(16*0,"esp"),$D0);
+	&movdqa		(&QWP(16*1,"esp"),$D1);
+	&movdqa		(&QWP(16*2,"esp"),$D2);
+	&movdqa		(&QWP(16*3,"esp"),$D3);
+	&movdqa		(&QWP(16*4,"esp"),$D4);
+
+	&movdqa		($T1,$D1);
+	&movdqa		($T0,$D2);
+	&pslld		($T1,2);
+	&pslld		($T0,2);
+	&paddd		($T1,$D1);			# *5
+	&paddd		($T0,$D2);			# *5
+	&movdqa		(&QWP(16*5,"esp"),$T1);
+	&movdqa		(&QWP(16*6,"esp"),$T0);
+	&movdqa		($T1,$D3);
+	&movdqa		($T0,$D4);
+	&pslld		($T1,2);
+	&pslld		($T0,2);
+	&paddd		($T1,$D3);			# *5
+	&paddd		($T0,$D4);			# *5
+	&movdqa		(&QWP(16*7,"esp"),$T1);
+	&movdqa		(&QWP(16*8,"esp"),$T0);
+
+	&pshufd		($T1,$D0,0b01000100);
+	&movdqa		($T0,$D1);
+	&pshufd		($D1,$D1,0b01000100);
+	&pshufd		($D2,$D2,0b01000100);
+	&pshufd		($D3,$D3,0b01000100);
+	&pshufd		($D4,$D4,0b01000100);
+	&movdqa		(&QWP(16*0,"edx"),$T1);
+	&movdqa		(&QWP(16*1,"edx"),$D1);
+	&movdqa		(&QWP(16*2,"edx"),$D2);
+	&movdqa		(&QWP(16*3,"edx"),$D3);
+	&movdqa		(&QWP(16*4,"edx"),$D4);
+
+	################################################################
+	# d4 = h4*r0 + h3*r1   + h2*r2   + h1*r3   + h0*r4
+	# d3 = h3*r0 + h2*r1   + h1*r2   + h0*r3   + h4*5*r4
+	# d2 = h2*r0 + h1*r1   + h0*r2   + h4*5*r3 + h3*5*r4
+	# d1 = h1*r0 + h0*r1   + h4*5*r2 + h3*5*r3 + h2*5*r4
+	# d0 = h0*r0 + h4*5*r1 + h3*5*r2 + h2*5*r3 + h1*5*r4
+
+	&pmuludq	($D4,$D0);			# h4*r0
+	&pmuludq	($D3,$D0);			# h3*r0
+	&pmuludq	($D2,$D0);			# h2*r0
+	&pmuludq	($D1,$D0);			# h1*r0
+	&pmuludq	($D0,$T1);			# h0*r0
+
+sub pmuladd {
+my $load = shift;
+my $base = shift; $base = "esp" if (!defined($base));
+
+	################################################################
+	# As for choice to "rotate" $T0-$T2 in order to move paddq
+	# past next multiplication. While it makes code harder to read
+	# and doesn't have significant effect on most processors, it
+	# makes a lot of difference on Atom, up to 30% improvement.
+
+	&movdqa		($T1,$T0);
+	&pmuludq	($T0,&QWP(16*3,$base));		# r1*h3
+	&movdqa		($T2,$T1);
+	&pmuludq	($T1,&QWP(16*2,$base));		# r1*h2
+	&paddq		($D4,$T0);
+	&movdqa		($T0,$T2);
+	&pmuludq	($T2,&QWP(16*1,$base));		# r1*h1
+	&paddq		($D3,$T1);
+	&$load		($T1,5);			# s1
+	&pmuludq	($T0,&QWP(16*0,$base));		# r1*h0
+	&paddq		($D2,$T2);
+	&pmuludq	($T1,&QWP(16*4,$base));		# s1*h4
+	 &$load		($T2,2);			# r2^n
+	&paddq		($D1,$T0);
+
+	&movdqa		($T0,$T2);
+	&pmuludq	($T2,&QWP(16*2,$base));		# r2*h2
+	 &paddq		($D0,$T1);
+	&movdqa		($T1,$T0);
+	&pmuludq	($T0,&QWP(16*1,$base));		# r2*h1
+	&paddq		($D4,$T2);
+	&$load		($T2,6);			# s2^n
+	&pmuludq	($T1,&QWP(16*0,$base));		# r2*h0
+	&paddq		($D3,$T0);
+	&movdqa		($T0,$T2);
+	&pmuludq	($T2,&QWP(16*4,$base));		# s2*h4
+	&paddq		($D2,$T1);
+	&pmuludq	($T0,&QWP(16*3,$base));		# s2*h3
+	 &$load		($T1,3);			# r3^n
+	&paddq		($D1,$T2);
+
+	&movdqa		($T2,$T1);
+	&pmuludq	($T1,&QWP(16*1,$base));		# r3*h1
+	 &paddq		($D0,$T0);
+	&$load		($T0,7);			# s3^n
+	&pmuludq	($T2,&QWP(16*0,$base));		# r3*h0
+	&paddq		($D4,$T1);
+	&movdqa		($T1,$T0);
+	&pmuludq	($T0,&QWP(16*4,$base));		# s3*h4
+	&paddq		($D3,$T2);
+	&movdqa		($T2,$T1);
+	&pmuludq	($T1,&QWP(16*3,$base));		# s3*h3
+	&paddq		($D2,$T0);
+	&pmuludq	($T2,&QWP(16*2,$base));		# s3*h2
+	 &$load		($T0,4);			# r4^n
+	&paddq		($D1,$T1);
+
+	&$load		($T1,8);			# s4^n
+	&pmuludq	($T0,&QWP(16*0,$base));		# r4*h0
+	 &paddq		($D0,$T2);
+	&movdqa		($T2,$T1);
+	&pmuludq	($T1,&QWP(16*4,$base));		# s4*h4
+	&paddq		($D4,$T0);
+	&movdqa		($T0,$T2);
+	&pmuludq	($T2,&QWP(16*1,$base));		# s4*h1
+	&paddq		($D3,$T1);
+	&movdqa		($T1,$T0);
+	&pmuludq	($T0,&QWP(16*2,$base));		# s4*h2
+	&paddq		($D0,$T2);
+	&pmuludq	($T1,&QWP(16*3,$base));		# s4*h3
+	 &movdqa	($MASK,&QWP(64,"ebx"));
+	&paddq		($D1,$T0);
+	&paddq		($D2,$T1);
+}
+	&pmuladd	(sub {	my ($reg,$i)=@_;
+				&movdqa ($reg,&QWP(16*$i,"esp"));
+			     },"edx");
+
+sub lazy_reduction {
+my $extra = shift;
+
+	################################################################
+	# lazy reduction as discussed in "NEON crypto" by D.J. Bernstein
+	# and P. Schwabe
+	#
+	# [(*) see discussion in poly1305-armv4 module]
+
+	 &movdqa	($T0,$D3);
+	 &pand		($D3,$MASK);
+	 &psrlq		($T0,26);
+	 &$extra	()				if (defined($extra));
+	 &paddq		($T0,$D4);			# h3 -> h4
+	&movdqa		($T1,$D0);
+	&pand		($D0,$MASK);
+	&psrlq		($T1,26);
+	 &movdqa	($D4,$T0);
+	&paddq		($T1,$D1);			# h0 -> h1
+	 &psrlq		($T0,26);
+	 &pand		($D4,$MASK);
+	&movdqa		($D1,$T1);
+	&psrlq		($T1,26);
+	 &paddd		($D0,$T0);			# favour paddd when
+							# possible, because
+							# paddq is "broken"
+							# on Atom
+	 &psllq		($T0,2);
+	&paddq		($T1,$D2);			# h1 -> h2
+	 &paddq		($T0,$D0);			# h4 -> h0 (*)
+	&pand		($D1,$MASK);
+	&movdqa		($D2,$T1);
+	&psrlq		($T1,26);
+	&pand		($D2,$MASK);
+	&paddd		($T1,$D3);			# h2 -> h3
+	 &movdqa	($D0,$T0);
+	 &psrlq		($T0,26);
+	&movdqa		($D3,$T1);
+	&psrlq		($T1,26);
+	 &pand		($D0,$MASK);
+	 &paddd		($D1,$T0);			# h0 -> h1
+	&pand		($D3,$MASK);
+	&paddd		($D4,$T1);			# h3 -> h4
+}
+	&lazy_reduction	();
+
+	&dec		("ecx");
+	&jz		(&label("square_break"));
+
+	&punpcklqdq	($D0,&QWP(16*0,"esp"));		# 0:r^1:0:r^2
+	&punpcklqdq	($D1,&QWP(16*1,"esp"));
+	&punpcklqdq	($D2,&QWP(16*2,"esp"));
+	&punpcklqdq	($D3,&QWP(16*3,"esp"));
+	&punpcklqdq	($D4,&QWP(16*4,"esp"));
+	&jmp		(&label("square"));
+
+&set_label("square_break");
+	&psllq		($D0,32);			# -> r^3:0:r^4:0
+	&psllq		($D1,32);
+	&psllq		($D2,32);
+	&psllq		($D3,32);
+	&psllq		($D4,32);
+	&por		($D0,&QWP(16*0,"esp"));		# r^3:r^1:r^4:r^2
+	&por		($D1,&QWP(16*1,"esp"));
+	&por		($D2,&QWP(16*2,"esp"));
+	&por		($D3,&QWP(16*3,"esp"));
+	&por		($D4,&QWP(16*4,"esp"));
+
+	&pshufd		($D0,$D0,0b10001101);		# -> r^1:r^2:r^3:r^4
+	&pshufd		($D1,$D1,0b10001101);
+	&pshufd		($D2,$D2,0b10001101);
+	&pshufd		($D3,$D3,0b10001101);
+	&pshufd		($D4,$D4,0b10001101);
+
+	&movdqu		(&QWP(16*0,"edi"),$D0);		# save the table
+	&movdqu		(&QWP(16*1,"edi"),$D1);
+	&movdqu		(&QWP(16*2,"edi"),$D2);
+	&movdqu		(&QWP(16*3,"edi"),$D3);
+	&movdqu		(&QWP(16*4,"edi"),$D4);
+
+	&movdqa		($T1,$D1);
+	&movdqa		($T0,$D2);
+	&pslld		($T1,2);
+	&pslld		($T0,2);
+	&paddd		($T1,$D1);			# *5
+	&paddd		($T0,$D2);			# *5
+	&movdqu		(&QWP(16*5,"edi"),$T1);
+	&movdqu		(&QWP(16*6,"edi"),$T0);
+	&movdqa		($T1,$D3);
+	&movdqa		($T0,$D4);
+	&pslld		($T1,2);
+	&pslld		($T0,2);
+	&paddd		($T1,$D3);			# *5
+	&paddd		($T0,$D4);			# *5
+	&movdqu		(&QWP(16*7,"edi"),$T1);
+	&movdqu		(&QWP(16*8,"edi"),$T0);
+
+	&mov		("esp","ebp");
+	&lea		("edi",&DWP(-16*3,"edi"));	# size de-optimization
+	&ret		();
+&function_end_B("_poly1305_init_sse2");
+
+&align	(32);
+&function_begin("_poly1305_blocks_sse2");
+	&mov	("edi",&wparam(0));			# ctx
+	&mov	("esi",&wparam(1));			# inp
+	&mov	("ecx",&wparam(2));			# len
+
+	&mov	("eax",&DWP(4*5,"edi"));		# is_base2_26
+	&and	("ecx",-16);
+	&jz	(&label("nodata"));
+	&cmp	("ecx",64);
+	&jae	(&label("enter_sse2"));
+	&test	("eax","eax");				# is_base2_26?
+	&jz	(&label("enter_blocks"));
+
+&set_label("enter_sse2",16);
+	&call	(&label("pic_point"));
+&set_label("pic_point");
+	&blindpop("ebx");
+	&lea	("ebx",&DWP(&label("const_sse2")."-".&label("pic_point"),"ebx"));
+
+	&test	("eax","eax");				# is_base2_26?
+	&jnz	(&label("base2_26"));
+
+	&call	("_poly1305_init_sse2");
+
+	################################################# base 2^32 -> base 2^26
+	&mov	("eax",&DWP(0,"edi"));
+	&mov	("ecx",&DWP(3,"edi"));
+	&mov	("edx",&DWP(6,"edi"));
+	&mov	("esi",&DWP(9,"edi"));
+	&mov	("ebp",&DWP(13,"edi"));
+	&mov	(&DWP(4*5,"edi"),1);			# is_base2_26
+
+	&shr	("ecx",2);
+	&and	("eax",0x3ffffff);
+	&shr	("edx",4);
+	&and	("ecx",0x3ffffff);
+	&shr	("esi",6);
+	&and	("edx",0x3ffffff);
+
+	&movd	($D0,"eax");
+	&movd	($D1,"ecx");
+	&movd	($D2,"edx");
+	&movd	($D3,"esi");
+	&movd	($D4,"ebp");
+
+	&mov	("esi",&wparam(1));			# [reload] inp
+	&mov	("ecx",&wparam(2));			# [reload] len
+	&jmp	(&label("base2_32"));
+
+&set_label("base2_26",16);
+	&movd	($D0,&DWP(4*0,"edi"));			# load hash value
+	&movd	($D1,&DWP(4*1,"edi"));
+	&movd	($D2,&DWP(4*2,"edi"));
+	&movd	($D3,&DWP(4*3,"edi"));
+	&movd	($D4,&DWP(4*4,"edi"));
+	&movdqa	($MASK,&QWP(64,"ebx"));
+
+&set_label("base2_32");
+	&mov	("eax",&wparam(3));			# padbit
+	&mov	("ebp","esp");
+
+	&sub	("esp",16*(5+5+5+9+9));
+	&and	("esp",-16);
+
+	&lea	("edi",&DWP(16*3,"edi"));		# size optimization
+	&shl	("eax",24);				# padbit
+
+	&test	("ecx",31);
+	&jz	(&label("even"));
+
+	################################################################
+	# process single block, with SSE2, because it's still faster
+	# even though half of result is discarded
+
+	&movdqu		($T1,&QWP(0,"esi"));		# input
+	&lea		("esi",&DWP(16,"esi"));
+
+	&movdqa		($T0,$T1);			# -> base 2^26 ...
+	&pand		($T1,$MASK);
+	&paddd		($D0,$T1);			# ... and accumuate
+
+	&movdqa		($T1,$T0);
+	&psrlq		($T0,26);
+	&psrldq		($T1,6);
+	&pand		($T0,$MASK);
+	&paddd		($D1,$T0);
+
+	&movdqa		($T0,$T1);
+	&psrlq		($T1,4);
+	&pand		($T1,$MASK);
+	&paddd		($D2,$T1);
+
+	&movdqa		($T1,$T0);
+	&psrlq		($T0,30);
+	&pand		($T0,$MASK);
+	&psrldq		($T1,7);
+	&paddd		($D3,$T0);
+
+	&movd		($T0,"eax");			# padbit
+	&paddd		($D4,$T1);
+	 &movd		($T1,&DWP(16*0+12,"edi"));	# r0
+	&paddd		($D4,$T0);
+
+	&movdqa		(&QWP(16*0,"esp"),$D0);
+	&movdqa		(&QWP(16*1,"esp"),$D1);
+	&movdqa		(&QWP(16*2,"esp"),$D2);
+	&movdqa		(&QWP(16*3,"esp"),$D3);
+	&movdqa		(&QWP(16*4,"esp"),$D4);
+
+	################################################################
+	# d4 = h4*r0 + h3*r1   + h2*r2   + h1*r3   + h0*r4
+	# d3 = h3*r0 + h2*r1   + h1*r2   + h0*r3   + h4*5*r4
+	# d2 = h2*r0 + h1*r1   + h0*r2   + h4*5*r3 + h3*5*r4
+	# d1 = h1*r0 + h0*r1   + h4*5*r2 + h3*5*r3 + h2*5*r4
+	# d0 = h0*r0 + h4*5*r1 + h3*5*r2 + h2*5*r3 + h1*5*r4
+
+	&pmuludq	($D0,$T1);			# h4*r0
+	&pmuludq	($D1,$T1);			# h3*r0
+	&pmuludq	($D2,$T1);			# h2*r0
+	 &movd		($T0,&DWP(16*1+12,"edi"));	# r1
+	&pmuludq	($D3,$T1);			# h1*r0
+	&pmuludq	($D4,$T1);			# h0*r0
+
+	&pmuladd	(sub {	my ($reg,$i)=@_;
+				&movd ($reg,&DWP(16*$i+12,"edi"));
+			     });
+
+	&lazy_reduction	();
+
+	&sub		("ecx",16);
+	&jz		(&label("done"));
+
+&set_label("even");
+	&lea		("edx",&DWP(16*(5+5+5+9),"esp"));# size optimization
+	&lea		("eax",&DWP(-16*2,"esi"));
+	&sub		("ecx",64);
+
+	################################################################
+	# expand and copy pre-calculated table to stack
+
+	&movdqu		($T0,&QWP(16*0,"edi"));		# r^1:r^2:r^3:r^4
+	&pshufd		($T1,$T0,0b01000100);		# duplicate r^3:r^4
+	&cmovb		("esi","eax");
+	&pshufd		($T0,$T0,0b11101110);		# duplicate r^1:r^2
+	&movdqa		(&QWP(16*0,"edx"),$T1);
+	&lea		("eax",&DWP(16*10,"esp"));
+	&movdqu		($T1,&QWP(16*1,"edi"));
+	&movdqa		(&QWP(16*(0-9),"edx"),$T0);
+	&pshufd		($T0,$T1,0b01000100);
+	&pshufd		($T1,$T1,0b11101110);
+	&movdqa		(&QWP(16*1,"edx"),$T0);
+	&movdqu		($T0,&QWP(16*2,"edi"));
+	&movdqa		(&QWP(16*(1-9),"edx"),$T1);
+	&pshufd		($T1,$T0,0b01000100);
+	&pshufd		($T0,$T0,0b11101110);
+	&movdqa		(&QWP(16*2,"edx"),$T1);
+	&movdqu		($T1,&QWP(16*3,"edi"));
+	&movdqa		(&QWP(16*(2-9),"edx"),$T0);
+	&pshufd		($T0,$T1,0b01000100);
+	&pshufd		($T1,$T1,0b11101110);
+	&movdqa		(&QWP(16*3,"edx"),$T0);
+	&movdqu		($T0,&QWP(16*4,"edi"));
+	&movdqa		(&QWP(16*(3-9),"edx"),$T1);
+	&pshufd		($T1,$T0,0b01000100);
+	&pshufd		($T0,$T0,0b11101110);
+	&movdqa		(&QWP(16*4,"edx"),$T1);
+	&movdqu		($T1,&QWP(16*5,"edi"));
+	&movdqa		(&QWP(16*(4-9),"edx"),$T0);
+	&pshufd		($T0,$T1,0b01000100);
+	&pshufd		($T1,$T1,0b11101110);
+	&movdqa		(&QWP(16*5,"edx"),$T0);
+	&movdqu		($T0,&QWP(16*6,"edi"));
+	&movdqa		(&QWP(16*(5-9),"edx"),$T1);
+	&pshufd		($T1,$T0,0b01000100);
+	&pshufd		($T0,$T0,0b11101110);
+	&movdqa		(&QWP(16*6,"edx"),$T1);
+	&movdqu		($T1,&QWP(16*7,"edi"));
+	&movdqa		(&QWP(16*(6-9),"edx"),$T0);
+	&pshufd		($T0,$T1,0b01000100);
+	&pshufd		($T1,$T1,0b11101110);
+	&movdqa		(&QWP(16*7,"edx"),$T0);
+	&movdqu		($T0,&QWP(16*8,"edi"));
+	&movdqa		(&QWP(16*(7-9),"edx"),$T1);
+	&pshufd		($T1,$T0,0b01000100);
+	&pshufd		($T0,$T0,0b11101110);
+	&movdqa		(&QWP(16*8,"edx"),$T1);
+	&movdqa		(&QWP(16*(8-9),"edx"),$T0);
+
+sub load_input {
+my ($inpbase,$offbase)=@_;
+
+	&movdqu		($T0,&QWP($inpbase+0,"esi"));	# load input
+	&movdqu		($T1,&QWP($inpbase+16,"esi"));
+	&lea		("esi",&DWP(16*2,"esi"));
+
+	&movdqa		(&QWP($offbase+16*2,"esp"),$D2);
+	&movdqa		(&QWP($offbase+16*3,"esp"),$D3);
+	&movdqa		(&QWP($offbase+16*4,"esp"),$D4);
+
+	&movdqa		($D2,$T0);			# splat input
+	&movdqa		($D3,$T1);
+	&psrldq		($D2,6);
+	&psrldq		($D3,6);
+	&movdqa		($D4,$T0);
+	&punpcklqdq	($D2,$D3);			# 2:3
+	&punpckhqdq	($D4,$T1);			# 4
+	&punpcklqdq	($T0,$T1);			# 0:1
+
+	&movdqa		($D3,$D2);
+	&psrlq		($D2,4);
+	&psrlq		($D3,30);
+	&movdqa		($T1,$T0);
+	&psrlq		($D4,40);			# 4
+	&psrlq		($T1,26);
+	&pand		($T0,$MASK);			# 0
+	&pand		($T1,$MASK);			# 1
+	&pand		($D2,$MASK);			# 2
+	&pand		($D3,$MASK);			# 3
+	&por		($D4,&QWP(0,"ebx"));		# padbit, yes, always
+
+	&movdqa		(&QWP($offbase+16*0,"esp"),$D0)	if ($offbase);
+	&movdqa		(&QWP($offbase+16*1,"esp"),$D1)	if ($offbase);
+}
+	&load_input	(16*2,16*5);
+
+	&jbe		(&label("skip_loop"));
+	&jmp		(&label("loop"));
+
+&set_label("loop",32);
+	################################################################
+	# ((inp[0]*r^4+inp[2]*r^2+inp[4])*r^4+inp[6]*r^2
+	# ((inp[1]*r^4+inp[3]*r^2+inp[5])*r^3+inp[7]*r
+	#   \___________________/
+	# ((inp[0]*r^4+inp[2]*r^2+inp[4])*r^4+inp[6]*r^2+inp[8])*r^2
+	# ((inp[1]*r^4+inp[3]*r^2+inp[5])*r^4+inp[7]*r^2+inp[9])*r
+	#   \___________________/ \____________________/
+	################################################################
+
+	&movdqa		($T2,&QWP(16*(0-9),"edx"));	# r0^2
+	&movdqa		(&QWP(16*1,"eax"),$T1);
+	&movdqa		(&QWP(16*2,"eax"),$D2);
+	&movdqa		(&QWP(16*3,"eax"),$D3);
+	&movdqa		(&QWP(16*4,"eax"),$D4);
+
+	################################################################
+	# d4 = h4*r0 + h0*r4   + h1*r3   + h2*r2   + h3*r1
+	# d3 = h3*r0 + h0*r3   + h1*r2   + h2*r1   + h4*5*r4
+	# d2 = h2*r0 + h0*r2   + h1*r1   + h3*5*r4 + h4*5*r3
+	# d1 = h1*r0 + h0*r1   + h2*5*r4 + h3*5*r3 + h4*5*r2
+	# d0 = h0*r0 + h1*5*r4 + h2*5*r3 + h3*5*r2 + h4*5*r1
+
+	&movdqa		($D1,$T0);
+	&pmuludq	($T0,$T2);			# h0*r0
+	&movdqa		($D0,$T1);
+	&pmuludq	($T1,$T2);			# h1*r0
+	&pmuludq	($D2,$T2);			# h2*r0
+	&pmuludq	($D3,$T2);			# h3*r0
+	&pmuludq	($D4,$T2);			# h4*r0
+
+sub pmuladd_alt {
+my $addr = shift;
+
+	&pmuludq	($D0,&$addr(8));		# h1*s4
+	&movdqa		($T2,$D1);
+	&pmuludq	($D1,&$addr(1));		# h0*r1
+	&paddq		($D0,$T0);
+	&movdqa		($T0,$T2);
+	&pmuludq	($T2,&$addr(2));		# h0*r2
+	&paddq		($D1,$T1);
+	&movdqa		($T1,$T0);
+	&pmuludq	($T0,&$addr(3));		# h0*r3
+	&paddq		($D2,$T2);
+	 &movdqa	($T2,&QWP(16*1,"eax"));		# pull h1
+	&pmuludq	($T1,&$addr(4));		# h0*r4
+	&paddq		($D3,$T0);
+
+	&movdqa		($T0,$T2);
+	&pmuludq	($T2,&$addr(1));		# h1*r1
+	 &paddq		($D4,$T1);
+	&movdqa		($T1,$T0);
+	&pmuludq	($T0,&$addr(2));		# h1*r2
+	&paddq		($D2,$T2);
+	&movdqa		($T2,&QWP(16*2,"eax"));		# pull h2
+	&pmuludq	($T1,&$addr(3));		# h1*r3
+	&paddq		($D3,$T0);
+	&movdqa		($T0,$T2);
+	&pmuludq	($T2,&$addr(7));		# h2*s3
+	&paddq		($D4,$T1);
+	&movdqa		($T1,$T0);
+	&pmuludq	($T0,&$addr(8));		# h2*s4
+	&paddq		($D0,$T2);
+
+	&movdqa		($T2,$T1);
+	&pmuludq	($T1,&$addr(1));		# h2*r1
+	 &paddq		($D1,$T0);
+	&movdqa		($T0,&QWP(16*3,"eax"));		# pull h3
+	&pmuludq	($T2,&$addr(2));		# h2*r2
+	&paddq		($D3,$T1);
+	&movdqa		($T1,$T0);
+	&pmuludq	($T0,&$addr(6));		# h3*s2
+	&paddq		($D4,$T2);
+	&movdqa		($T2,$T1);
+	&pmuludq	($T1,&$addr(7));		# h3*s3
+	&paddq		($D0,$T0);
+	&movdqa		($T0,$T2);
+	&pmuludq	($T2,&$addr(8));		# h3*s4
+	&paddq		($D1,$T1);
+
+	&movdqa		($T1,&QWP(16*4,"eax"));		# pull h4
+	&pmuludq	($T0,&$addr(1));		# h3*r1
+	 &paddq		($D2,$T2);
+	&movdqa		($T2,$T1);
+	&pmuludq	($T1,&$addr(8));		# h4*s4
+	&paddq		($D4,$T0);
+	&movdqa		($T0,$T2);
+	&pmuludq	($T2,&$addr(5));		# h4*s1
+	&paddq		($D3,$T1);
+	&movdqa		($T1,$T0);
+	&pmuludq	($T0,&$addr(6));		# h4*s2
+	&paddq		($D0,$T2);
+	 &movdqa	($MASK,&QWP(64,"ebx"));
+	&pmuludq	($T1,&$addr(7));		# h4*s3
+	&paddq		($D1,$T0);
+	&paddq		($D2,$T1);
+}
+	&pmuladd_alt	(sub {	my $i=shift; &QWP(16*($i-9),"edx");	});
+
+	&load_input	(-16*2,0);
+	&lea		("eax",&DWP(-16*2,"esi"));
+	&sub		("ecx",64);
+
+	&paddd		($T0,&QWP(16*(5+0),"esp"));	# add hash value
+	&paddd		($T1,&QWP(16*(5+1),"esp"));
+	&paddd		($D2,&QWP(16*(5+2),"esp"));
+	&paddd		($D3,&QWP(16*(5+3),"esp"));
+	&paddd		($D4,&QWP(16*(5+4),"esp"));
+
+	&cmovb		("esi","eax");
+	&lea		("eax",&DWP(16*10,"esp"));
+
+	&movdqa		($T2,&QWP(16*0,"edx"));		# r0^4
+	&movdqa		(&QWP(16*1,"esp"),$D1);
+	&movdqa		(&QWP(16*1,"eax"),$T1);
+	&movdqa		(&QWP(16*2,"eax"),$D2);
+	&movdqa		(&QWP(16*3,"eax"),$D3);
+	&movdqa		(&QWP(16*4,"eax"),$D4);
+
+	################################################################
+	# d4 += h4*r0 + h0*r4   + h1*r3   + h2*r2   + h3*r1
+	# d3 += h3*r0 + h0*r3   + h1*r2   + h2*r1   + h4*5*r4
+	# d2 += h2*r0 + h0*r2   + h1*r1   + h3*5*r4 + h4*5*r3
+	# d1 += h1*r0 + h0*r1   + h2*5*r4 + h3*5*r3 + h4*5*r2
+	# d0 += h0*r0 + h1*5*r4 + h2*5*r3 + h3*5*r2 + h4*5*r1
+
+	&movdqa		($D1,$T0);
+	&pmuludq	($T0,$T2);			# h0*r0
+	&paddq		($T0,$D0);
+	&movdqa		($D0,$T1);
+	&pmuludq	($T1,$T2);			# h1*r0
+	&pmuludq	($D2,$T2);			# h2*r0
+	&pmuludq	($D3,$T2);			# h3*r0
+	&pmuludq	($D4,$T2);			# h4*r0
+
+	&paddq		($T1,&QWP(16*1,"esp"));
+	&paddq		($D2,&QWP(16*2,"esp"));
+	&paddq		($D3,&QWP(16*3,"esp"));
+	&paddq		($D4,&QWP(16*4,"esp"));
+
+	&pmuladd_alt	(sub {	my $i=shift; &QWP(16*$i,"edx");	});
+
+	&lazy_reduction	();
+
+	&load_input	(16*2,16*5);
+
+	&ja		(&label("loop"));
+
+&set_label("skip_loop");
+	################################################################
+	# multiply (inp[0:1]+hash) or inp[2:3] by r^2:r^1
+
+	 &pshufd	($T2,&QWP(16*(0-9),"edx"),0x10);# r0^n
+	&add		("ecx",32);
+	&jnz		(&label("long_tail"));
+
+	&paddd		($T0,$D0);			# add hash value
+	&paddd		($T1,$D1);
+	&paddd		($D2,&QWP(16*7,"esp"));
+	&paddd		($D3,&QWP(16*8,"esp"));
+	&paddd		($D4,&QWP(16*9,"esp"));
+
+&set_label("long_tail");
+
+	&movdqa		(&QWP(16*0,"eax"),$T0);
+	&movdqa		(&QWP(16*1,"eax"),$T1);
+	&movdqa		(&QWP(16*2,"eax"),$D2);
+	&movdqa		(&QWP(16*3,"eax"),$D3);
+	&movdqa		(&QWP(16*4,"eax"),$D4);
+
+	################################################################
+	# d4 = h4*r0 + h3*r1   + h2*r2   + h1*r3   + h0*r4
+	# d3 = h3*r0 + h2*r1   + h1*r2   + h0*r3   + h4*5*r4
+	# d2 = h2*r0 + h1*r1   + h0*r2   + h4*5*r3 + h3*5*r4
+	# d1 = h1*r0 + h0*r1   + h4*5*r2 + h3*5*r3 + h2*5*r4
+	# d0 = h0*r0 + h4*5*r1 + h3*5*r2 + h2*5*r3 + h1*5*r4
+
+	&pmuludq	($T0,$T2);			# h0*r0
+	&pmuludq	($T1,$T2);			# h1*r0
+	&pmuludq	($D2,$T2);			# h2*r0
+	&movdqa		($D0,$T0);
+	 &pshufd	($T0,&QWP(16*(1-9),"edx"),0x10);# r1^n
+	&pmuludq	($D3,$T2);			# h3*r0
+	&movdqa		($D1,$T1);
+	&pmuludq	($D4,$T2);			# h4*r0
+
+	&pmuladd	(sub {	my ($reg,$i)=@_;
+				&pshufd ($reg,&QWP(16*($i-9),"edx"),0x10);
+			     },"eax");
+
+	&jz		(&label("short_tail"));
+
+	&load_input	(-16*2,0);
+
+	 &pshufd	($T2,&QWP(16*0,"edx"),0x10);	# r0^n
+	&paddd		($T0,&QWP(16*5,"esp"));		# add hash value
+	&paddd		($T1,&QWP(16*6,"esp"));
+	&paddd		($D2,&QWP(16*7,"esp"));
+	&paddd		($D3,&QWP(16*8,"esp"));
+	&paddd		($D4,&QWP(16*9,"esp"));
+
+	################################################################
+	# multiply inp[0:1] by r^4:r^3 and accumulate
+
+	&movdqa		(&QWP(16*0,"esp"),$T0);
+	&pmuludq	($T0,$T2);			# h0*r0
+	&movdqa		(&QWP(16*1,"esp"),$T1);
+	&pmuludq	($T1,$T2);			# h1*r0
+	&paddq		($D0,$T0);
+	&movdqa		($T0,$D2);
+	&pmuludq	($D2,$T2);			# h2*r0
+	&paddq		($D1,$T1);
+	&movdqa		($T1,$D3);
+	&pmuludq	($D3,$T2);			# h3*r0
+	&paddq		($D2,&QWP(16*2,"esp"));
+	&movdqa		(&QWP(16*2,"esp"),$T0);
+	 &pshufd	($T0,&QWP(16*1,"edx"),0x10);	# r1^n
+	&paddq		($D3,&QWP(16*3,"esp"));
+	&movdqa		(&QWP(16*3,"esp"),$T1);
+	&movdqa		($T1,$D4);
+	&pmuludq	($D4,$T2);			# h4*r0
+	&paddq		($D4,&QWP(16*4,"esp"));
+	&movdqa		(&QWP(16*4,"esp"),$T1);
+
+	&pmuladd	(sub {	my ($reg,$i)=@_;
+				&pshufd ($reg,&QWP(16*$i,"edx"),0x10);
+			     });
+
+&set_label("short_tail");
+
+	################################################################
+	# horizontal addition
+
+	&pshufd		($T1,$D4,0b01001110);
+	&pshufd		($T0,$D3,0b01001110);
+	&paddq		($D4,$T1);
+	&paddq		($D3,$T0);
+	&pshufd		($T1,$D0,0b01001110);
+	&pshufd		($T0,$D1,0b01001110);
+	&paddq		($D0,$T1);
+	&paddq		($D1,$T0);
+	&pshufd		($T1,$D2,0b01001110);
+	#&paddq		($D2,$T1);
+
+	&lazy_reduction	(sub { &paddq ($D2,$T1) });
+
+&set_label("done");
+	&movd		(&DWP(-16*3+4*0,"edi"),$D0);	# store hash value
+	&movd		(&DWP(-16*3+4*1,"edi"),$D1);
+	&movd		(&DWP(-16*3+4*2,"edi"),$D2);
+	&movd		(&DWP(-16*3+4*3,"edi"),$D3);
+	&movd		(&DWP(-16*3+4*4,"edi"),$D4);
+	&mov	("esp","ebp");
+&set_label("nodata");
+&function_end("_poly1305_blocks_sse2");
+
+&align	(32);
+&function_begin("_poly1305_emit_sse2");
+	&mov	("ebp",&wparam(0));		# context
+
+	&cmp	(&DWP(4*5,"ebp"),0);		# is_base2_26?
+	&je	(&label("enter_emit"));
+
+	&mov	("eax",&DWP(4*0,"ebp"));	# load hash value
+	&mov	("edi",&DWP(4*1,"ebp"));
+	&mov	("ecx",&DWP(4*2,"ebp"));
+	&mov	("edx",&DWP(4*3,"ebp"));
+	&mov	("esi",&DWP(4*4,"ebp"));
+
+	&mov	("ebx","edi");			# base 2^26 -> base 2^32
+	&shl	("edi",26);
+	&shr	("ebx",6);
+	&add	("eax","edi");
+	&mov	("edi","ecx");
+	&adc	("ebx",0);
+
+	&shl	("edi",20);
+	&shr	("ecx",12);
+	&add	("ebx","edi");
+	&mov	("edi","edx");
+	&adc	("ecx",0);
+
+	&shl	("edi",14);
+	&shr	("edx",18);
+	&add	("ecx","edi");
+	&mov	("edi","esi");
+	&adc	("edx",0);
+
+	&shl	("edi",8);
+	&shr	("esi",24);
+	&add	("edx","edi");
+	&adc	("esi",0);			# can be partially reduced
+
+	&mov	("edi","esi");			# final reduction
+	&and	("esi",3);
+	&shr	("edi",2);
+	&lea	("ebp",&DWP(0,"edi","edi",4));	# *5
+	 &mov	("edi",&wparam(1));		# output
+	&add	("eax","ebp");
+	 &mov	("ebp",&wparam(2));		# key
+	&adc	("ebx",0);
+	&adc	("ecx",0);
+	&adc	("edx",0);
+	&adc	("esi",0);
+
+	&movd	($D0,"eax");			# offload original hash value
+	&add	("eax",5);			# compare to modulus
+	&movd	($D1,"ebx");
+	&adc	("ebx",0);
+	&movd	($D2,"ecx");
+	&adc	("ecx",0);
+	&movd	($D3,"edx");
+	&adc	("edx",0);
+	&adc	("esi",0);
+	&shr	("esi",2);			# did it carry/borrow?
+
+	&neg	("esi");			# do we choose (hash-modulus) ...
+	&and	("eax","esi");
+	&and	("ebx","esi");
+	&and	("ecx","esi");
+	&and	("edx","esi");
+	&mov	(&DWP(4*0,"edi"),"eax");
+	&movd	("eax",$D0);
+	&mov	(&DWP(4*1,"edi"),"ebx");
+	&movd	("ebx",$D1);
+	&mov	(&DWP(4*2,"edi"),"ecx");
+	&movd	("ecx",$D2);
+	&mov	(&DWP(4*3,"edi"),"edx");
+	&movd	("edx",$D3);
+
+	¬	("esi");			# ... or original hash value?
+	&and	("eax","esi");
+	&and	("ebx","esi");
+	&or	("eax",&DWP(4*0,"edi"));
+	&and	("ecx","esi");
+	&or	("ebx",&DWP(4*1,"edi"));
+	&and	("edx","esi");
+	&or	("ecx",&DWP(4*2,"edi"));
+	&or	("edx",&DWP(4*3,"edi"));
+
+	&add	("eax",&DWP(4*0,"ebp"));	# accumulate key
+	&adc	("ebx",&DWP(4*1,"ebp"));
+	&mov	(&DWP(4*0,"edi"),"eax");
+	&adc	("ecx",&DWP(4*2,"ebp"));
+	&mov	(&DWP(4*1,"edi"),"ebx");
+	&adc	("edx",&DWP(4*3,"ebp"));
+	&mov	(&DWP(4*2,"edi"),"ecx");
+	&mov	(&DWP(4*3,"edi"),"edx");
+&function_end("_poly1305_emit_sse2");
+
+if ($avx>1) {
+########################################################################
+# Note that poly1305_init_avx2 operates on %xmm, I could have used
+# poly1305_init_sse2...
+
+&align	(32);
+&function_begin_B("_poly1305_init_avx2");
+	&vmovdqu	($D4,&QWP(4*6,"edi"));		# key base 2^32
+	&lea		("edi",&DWP(16*3,"edi"));	# size optimization
+	&mov		("ebp","esp");
+	&sub		("esp",16*(9+5));
+	&and		("esp",-16);
+
+	#&vpand		($D4,$D4,&QWP(96,"ebx"));	# magic mask
+	&vmovdqa	($MASK,&QWP(64,"ebx"));
+
+	&vpand		($D0,$D4,$MASK);		# -> base 2^26
+	&vpsrlq		($D1,$D4,26);
+	&vpsrldq	($D3,$D4,6);
+	&vpand		($D1,$D1,$MASK);
+	&vpsrlq		($D2,$D3,4)
+	&vpsrlq		($D3,$D3,30);
+	&vpand		($D2,$D2,$MASK);
+	&vpand		($D3,$D3,$MASK);
+	&vpsrldq	($D4,$D4,13);
+
+	&lea		("edx",&DWP(16*9,"esp"));	# size optimization
+	&mov		("ecx",2);
+&set_label("square");
+	&vmovdqa	(&QWP(16*0,"esp"),$D0);
+	&vmovdqa	(&QWP(16*1,"esp"),$D1);
+	&vmovdqa	(&QWP(16*2,"esp"),$D2);
+	&vmovdqa	(&QWP(16*3,"esp"),$D3);
+	&vmovdqa	(&QWP(16*4,"esp"),$D4);
+
+	&vpslld		($T1,$D1,2);
+	&vpslld		($T0,$D2,2);
+	&vpaddd		($T1,$T1,$D1);			# *5
+	&vpaddd		($T0,$T0,$D2);			# *5
+	&vmovdqa	(&QWP(16*5,"esp"),$T1);
+	&vmovdqa	(&QWP(16*6,"esp"),$T0);
+	&vpslld		($T1,$D3,2);
+	&vpslld		($T0,$D4,2);
+	&vpaddd		($T1,$T1,$D3);			# *5
+	&vpaddd		($T0,$T0,$D4);			# *5
+	&vmovdqa	(&QWP(16*7,"esp"),$T1);
+	&vmovdqa	(&QWP(16*8,"esp"),$T0);
+
+	&vpshufd	($T0,$D0,0b01000100);
+	&vmovdqa	($T1,$D1);
+	&vpshufd	($D1,$D1,0b01000100);
+	&vpshufd	($D2,$D2,0b01000100);
+	&vpshufd	($D3,$D3,0b01000100);
+	&vpshufd	($D4,$D4,0b01000100);
+	&vmovdqa	(&QWP(16*0,"edx"),$T0);
+	&vmovdqa	(&QWP(16*1,"edx"),$D1);
+	&vmovdqa	(&QWP(16*2,"edx"),$D2);
+	&vmovdqa	(&QWP(16*3,"edx"),$D3);
+	&vmovdqa	(&QWP(16*4,"edx"),$D4);
+
+	################################################################
+	# d4 = h4*r0 + h3*r1   + h2*r2   + h1*r3   + h0*r4
+	# d3 = h3*r0 + h2*r1   + h1*r2   + h0*r3   + h4*5*r4
+	# d2 = h2*r0 + h1*r1   + h0*r2   + h4*5*r3 + h3*5*r4
+	# d1 = h1*r0 + h0*r1   + h4*5*r2 + h3*5*r3 + h2*5*r4
+	# d0 = h0*r0 + h4*5*r1 + h3*5*r2 + h2*5*r3 + h1*5*r4
+
+	&vpmuludq	($D4,$D4,$D0);			# h4*r0
+	&vpmuludq	($D3,$D3,$D0);			# h3*r0
+	&vpmuludq	($D2,$D2,$D0);			# h2*r0
+	&vpmuludq	($D1,$D1,$D0);			# h1*r0
+	&vpmuludq	($D0,$T0,$D0);			# h0*r0
+
+	&vpmuludq	($T0,$T1,&QWP(16*3,"edx"));	# r1*h3
+	&vpaddq		($D4,$D4,$T0);
+	&vpmuludq	($T2,$T1,&QWP(16*2,"edx"));	# r1*h2
+	&vpaddq		($D3,$D3,$T2);
+	&vpmuludq	($T0,$T1,&QWP(16*1,"edx"));	# r1*h1
+	&vpaddq		($D2,$D2,$T0);
+	&vmovdqa	($T2,&QWP(16*5,"esp"));		# s1
+	&vpmuludq	($T1,$T1,&QWP(16*0,"edx"));	# r1*h0
+	&vpaddq		($D1,$D1,$T1);
+	 &vmovdqa	($T0,&QWP(16*2,"esp"));		# r2
+	&vpmuludq	($T2,$T2,&QWP(16*4,"edx"));	# s1*h4
+	&vpaddq		($D0,$D0,$T2);
+
+	&vpmuludq	($T1,$T0,&QWP(16*2,"edx"));	# r2*h2
+	&vpaddq		($D4,$D4,$T1);
+	&vpmuludq	($T2,$T0,&QWP(16*1,"edx"));	# r2*h1
+	&vpaddq		($D3,$D3,$T2);
+	&vmovdqa	($T1,&QWP(16*6,"esp"));		# s2
+	&vpmuludq	($T0,$T0,&QWP(16*0,"edx"));	# r2*h0
+	&vpaddq		($D2,$D2,$T0);
+	&vpmuludq	($T2,$T1,&QWP(16*4,"edx"));	# s2*h4
+	&vpaddq		($D1,$D1,$T2);
+	 &vmovdqa	($T0,&QWP(16*3,"esp"));		# r3
+	&vpmuludq	($T1,$T1,&QWP(16*3,"edx"));	# s2*h3
+	&vpaddq		($D0,$D0,$T1);
+
+	&vpmuludq	($T2,$T0,&QWP(16*1,"edx"));	# r3*h1
+	&vpaddq		($D4,$D4,$T2);
+	&vmovdqa	($T1,&QWP(16*7,"esp"));		# s3
+	&vpmuludq	($T0,$T0,&QWP(16*0,"edx"));	# r3*h0
+	&vpaddq		($D3,$D3,$T0);
+	&vpmuludq	($T2,$T1,&QWP(16*4,"edx"));	# s3*h4
+	&vpaddq		($D2,$D2,$T2);
+	&vpmuludq	($T0,$T1,&QWP(16*3,"edx"));	# s3*h3
+	&vpaddq		($D1,$D1,$T0);
+	 &vmovdqa	($T2,&QWP(16*4,"esp"));		# r4
+	&vpmuludq	($T1,$T1,&QWP(16*2,"edx"));	# s3*h2
+	&vpaddq		($D0,$D0,$T1);
+
+	&vmovdqa	($T0,&QWP(16*8,"esp"));		# s4
+	&vpmuludq	($T2,$T2,&QWP(16*0,"edx"));	# r4*h0
+	&vpaddq		($D4,$D4,$T2);
+	&vpmuludq	($T1,$T0,&QWP(16*4,"edx"));	# s4*h4
+	&vpaddq		($D3,$D3,$T1);
+	&vpmuludq	($T2,$T0,&QWP(16*1,"edx"));	# s4*h1
+	&vpaddq		($D0,$D0,$T2);
+	&vpmuludq	($T1,$T0,&QWP(16*2,"edx"));	# s4*h2
+	&vpaddq		($D1,$D1,$T1);
+	 &vmovdqa	($MASK,&QWP(64,"ebx"));
+	&vpmuludq	($T0,$T0,&QWP(16*3,"edx"));	# s4*h3
+	&vpaddq		($D2,$D2,$T0);
+
+	################################################################
+	# lazy reduction
+	 &vpsrlq	($T0,$D3,26);
+	 &vpand		($D3,$D3,$MASK);
+	&vpsrlq		($T1,$D0,26);
+	&vpand		($D0,$D0,$MASK);
+	 &vpaddq	($D4,$D4,$T0);			# h3 -> h4
+	&vpaddq		($D1,$D1,$T1);			# h0 -> h1
+	 &vpsrlq	($T0,$D4,26);
+	 &vpand		($D4,$D4,$MASK);
+	&vpsrlq		($T1,$D1,26);
+	&vpand		($D1,$D1,$MASK);
+	&vpaddq		($D2,$D2,$T1);			# h1 -> h2
+	 &vpaddd	($D0,$D0,$T0);
+	 &vpsllq	($T0,$T0,2);
+	&vpsrlq		($T1,$D2,26);
+	&vpand		($D2,$D2,$MASK);
+	 &vpaddd	($D0,$D0,$T0);			# h4 -> h0
+	&vpaddd		($D3,$D3,$T1);			# h2 -> h3
+	&vpsrlq		($T1,$D3,26);
+	 &vpsrlq	($T0,$D0,26);
+	 &vpand		($D0,$D0,$MASK);
+	&vpand		($D3,$D3,$MASK);
+	 &vpaddd	($D1,$D1,$T0);			# h0 -> h1
+	&vpaddd		($D4,$D4,$T1);			# h3 -> h4
+
+	&dec		("ecx");
+	&jz		(&label("square_break"));
+
+	&vpunpcklqdq	($D0,$D0,&QWP(16*0,"esp"));	# 0:r^1:0:r^2
+	&vpunpcklqdq	($D1,$D1,&QWP(16*1,"esp"));
+	&vpunpcklqdq	($D2,$D2,&QWP(16*2,"esp"));
+	&vpunpcklqdq	($D3,$D3,&QWP(16*3,"esp"));
+	&vpunpcklqdq	($D4,$D4,&QWP(16*4,"esp"));
+	&jmp		(&label("square"));
+
+&set_label("square_break");
+	&vpsllq		($D0,$D0,32);			# -> r^3:0:r^4:0
+	&vpsllq		($D1,$D1,32);
+	&vpsllq		($D2,$D2,32);
+	&vpsllq		($D3,$D3,32);
+	&vpsllq		($D4,$D4,32);
+	&vpor		($D0,$D0,&QWP(16*0,"esp"));	# r^3:r^1:r^4:r^2
+	&vpor		($D1,$D1,&QWP(16*1,"esp"));
+	&vpor		($D2,$D2,&QWP(16*2,"esp"));
+	&vpor		($D3,$D3,&QWP(16*3,"esp"));
+	&vpor		($D4,$D4,&QWP(16*4,"esp"));
+
+	&vpshufd	($D0,$D0,0b10001101);		# -> r^1:r^2:r^3:r^4
+	&vpshufd	($D1,$D1,0b10001101);
+	&vpshufd	($D2,$D2,0b10001101);
+	&vpshufd	($D3,$D3,0b10001101);
+	&vpshufd	($D4,$D4,0b10001101);
+
+	&vmovdqu	(&QWP(16*0,"edi"),$D0);		# save the table
+	&vmovdqu	(&QWP(16*1,"edi"),$D1);
+	&vmovdqu	(&QWP(16*2,"edi"),$D2);
+	&vmovdqu	(&QWP(16*3,"edi"),$D3);
+	&vmovdqu	(&QWP(16*4,"edi"),$D4);
+
+	&vpslld		($T1,$D1,2);
+	&vpslld		($T0,$D2,2);
+	&vpaddd		($T1,$T1,$D1);			# *5
+	&vpaddd		($T0,$T0,$D2);			# *5
+	&vmovdqu	(&QWP(16*5,"edi"),$T1);
+	&vmovdqu	(&QWP(16*6,"edi"),$T0);
+	&vpslld		($T1,$D3,2);
+	&vpslld		($T0,$D4,2);
+	&vpaddd		($T1,$T1,$D3);			# *5
+	&vpaddd		($T0,$T0,$D4);			# *5
+	&vmovdqu	(&QWP(16*7,"edi"),$T1);
+	&vmovdqu	(&QWP(16*8,"edi"),$T0);
+
+	&mov		("esp","ebp");
+	&lea		("edi",&DWP(-16*3,"edi"));	# size de-optimization
+	&ret		();
+&function_end_B("_poly1305_init_avx2");
+
+########################################################################
+# now it's time to switch to %ymm
+
+my ($D0,$D1,$D2,$D3,$D4,$T0,$T1,$T2)=map("ymm$_",(0..7));
+my $MASK=$T2;
+
+sub X { my $reg=shift; $reg=~s/^ymm/xmm/; $reg; }
+
+&align	(32);
+&function_begin("_poly1305_blocks_avx2");
+	&mov	("edi",&wparam(0));			# ctx
+	&mov	("esi",&wparam(1));			# inp
+	&mov	("ecx",&wparam(2));			# len
+
+	&mov	("eax",&DWP(4*5,"edi"));		# is_base2_26
+	&and	("ecx",-16);
+	&jz	(&label("nodata"));
+	&cmp	("ecx",64);
+	&jae	(&label("enter_avx2"));
+	&test	("eax","eax");				# is_base2_26?
+	&jz	(&label("enter_blocks"));
+
+&set_label("enter_avx2");
+	&vzeroupper	();
+
+	&call	(&label("pic_point"));
+&set_label("pic_point");
+	&blindpop("ebx");
+	&lea	("ebx",&DWP(&label("const_sse2")."-".&label("pic_point"),"ebx"));
+
+	&test	("eax","eax");				# is_base2_26?
+	&jnz	(&label("base2_26"));
+
+	&call	("_poly1305_init_avx2");
+
+	################################################# base 2^32 -> base 2^26
+	&mov	("eax",&DWP(0,"edi"));
+	&mov	("ecx",&DWP(3,"edi"));
+	&mov	("edx",&DWP(6,"edi"));
+	&mov	("esi",&DWP(9,"edi"));
+	&mov	("ebp",&DWP(13,"edi"));
+
+	&shr	("ecx",2);
+	&and	("eax",0x3ffffff);
+	&shr	("edx",4);
+	&and	("ecx",0x3ffffff);
+	&shr	("esi",6);
+	&and	("edx",0x3ffffff);
+
+	&mov	(&DWP(4*0,"edi"),"eax");
+	&mov	(&DWP(4*1,"edi"),"ecx");
+	&mov	(&DWP(4*2,"edi"),"edx");
+	&mov	(&DWP(4*3,"edi"),"esi");
+	&mov	(&DWP(4*4,"edi"),"ebp");
+	&mov	(&DWP(4*5,"edi"),1);			# is_base2_26
+
+	&mov	("esi",&wparam(1));			# [reload] inp
+	&mov	("ecx",&wparam(2));			# [reload] len
+
+&set_label("base2_26");
+	&mov	("eax",&wparam(3));			# padbit
+	&mov	("ebp","esp");
+
+	&sub	("esp",32*(5+9));
+	&and	("esp",-512);				# ensure that frame
+							# doesn't cross page
+							# boundary, which is
+							# essential for
+							# misaligned 32-byte
+							# loads
+
+	################################################################
+        # expand and copy pre-calculated table to stack
+
+	&vmovdqu	(&X($D0),&QWP(16*(3+0),"edi"));
+	&lea		("edx",&DWP(32*5+128,"esp"));	# +128 size optimization
+	&vmovdqu	(&X($D1),&QWP(16*(3+1),"edi"));
+	&vmovdqu	(&X($D2),&QWP(16*(3+2),"edi"));
+	&vmovdqu	(&X($D3),&QWP(16*(3+3),"edi"));
+	&vmovdqu	(&X($D4),&QWP(16*(3+4),"edi"));
+	&lea		("edi",&DWP(16*3,"edi"));	# size optimization
+	&vpermq		($D0,$D0,0b01000000);		# 00001234 -> 12343434
+	&vpermq		($D1,$D1,0b01000000);
+	&vpermq		($D2,$D2,0b01000000);
+	&vpermq		($D3,$D3,0b01000000);
+	&vpermq		($D4,$D4,0b01000000);
+	&vpshufd	($D0,$D0,0b11001000);		# 12343434 -> 14243444
+	&vpshufd	($D1,$D1,0b11001000);
+	&vpshufd	($D2,$D2,0b11001000);
+	&vpshufd	($D3,$D3,0b11001000);
+	&vpshufd	($D4,$D4,0b11001000);
+	&vmovdqa	(&QWP(32*0-128,"edx"),$D0);
+	&vmovdqu	(&X($D0),&QWP(16*5,"edi"));
+	&vmovdqa	(&QWP(32*1-128,"edx"),$D1);
+	&vmovdqu	(&X($D1),&QWP(16*6,"edi"));
+	&vmovdqa	(&QWP(32*2-128,"edx"),$D2);
+	&vmovdqu	(&X($D2),&QWP(16*7,"edi"));
+	&vmovdqa	(&QWP(32*3-128,"edx"),$D3);
+	&vmovdqu	(&X($D3),&QWP(16*8,"edi"));
+	&vmovdqa	(&QWP(32*4-128,"edx"),$D4);
+	&vpermq		($D0,$D0,0b01000000);
+	&vpermq		($D1,$D1,0b01000000);
+	&vpermq		($D2,$D2,0b01000000);
+	&vpermq		($D3,$D3,0b01000000);
+	&vpshufd	($D0,$D0,0b11001000);
+	&vpshufd	($D1,$D1,0b11001000);
+	&vpshufd	($D2,$D2,0b11001000);
+	&vpshufd	($D3,$D3,0b11001000);
+	&vmovdqa	(&QWP(32*5-128,"edx"),$D0);
+	&vmovd		(&X($D0),&DWP(-16*3+4*0,"edi"));# load hash value
+	&vmovdqa	(&QWP(32*6-128,"edx"),$D1);
+	&vmovd		(&X($D1),&DWP(-16*3+4*1,"edi"));
+	&vmovdqa	(&QWP(32*7-128,"edx"),$D2);
+	&vmovd		(&X($D2),&DWP(-16*3+4*2,"edi"));
+	&vmovdqa	(&QWP(32*8-128,"edx"),$D3);
+	&vmovd		(&X($D3),&DWP(-16*3+4*3,"edi"));
+	&vmovd		(&X($D4),&DWP(-16*3+4*4,"edi"));
+	&vmovdqa	($MASK,&QWP(64,"ebx"));
+	&neg		("eax");			# padbit
+
+	&test		("ecx",63);
+	&jz		(&label("even"));
+
+	&mov		("edx","ecx");
+	&and		("ecx",-64);
+	&and		("edx",63);
+
+	&vmovdqu	(&X($T0),&QWP(16*0,"esi"));
+	&cmp		("edx",32);
+	&jb		(&label("one"));
+
+	&vmovdqu	(&X($T1),&QWP(16*1,"esi"));
+	&je		(&label("two"));
+
+	&vinserti128	($T0,$T0,&QWP(16*2,"esi"),1);
+	&lea		("esi",&DWP(16*3,"esi"));
+	&lea		("ebx",&DWP(8,"ebx"));		# three padbits
+	&lea		("edx",&DWP(32*5+128+8,"esp"));	# --:r^1:r^2:r^3 (*)
+	&jmp		(&label("tail"));
+
+&set_label("two");
+	&lea		("esi",&DWP(16*2,"esi"));
+	&lea		("ebx",&DWP(16,"ebx"));		# two padbits
+	&lea		("edx",&DWP(32*5+128+16,"esp"));# --:--:r^1:r^2 (*)
+	&jmp		(&label("tail"));
+
+&set_label("one");
+	&lea		("esi",&DWP(16*1,"esi"));
+	&vpxor		($T1,$T1,$T1);
+	&lea		("ebx",&DWP(32,"ebx","eax",8));	# one or no padbits
+	&lea		("edx",&DWP(32*5+128+24,"esp"));# --:--:--:r^1 (*)
+	&jmp		(&label("tail"));
+
+# (*)	spots marked with '--' are data from next table entry, but they
+#	are multiplied by 0 and therefore rendered insignificant
+
+&set_label("even",32);
+	&vmovdqu	(&X($T0),&QWP(16*0,"esi"));	# load input
+	&vmovdqu	(&X($T1),&QWP(16*1,"esi"));
+	&vinserti128	($T0,$T0,&QWP(16*2,"esi"),1);
+	&vinserti128	($T1,$T1,&QWP(16*3,"esi"),1);
+	&lea		("esi",&DWP(16*4,"esi"));
+	&sub		("ecx",64);
+	&jz		(&label("tail"));
+
+&set_label("loop");
+	################################################################
+	# ((inp[0]*r^4+r[4])*r^4+r[8])*r^4
+	# ((inp[1]*r^4+r[5])*r^4+r[9])*r^3
+	# ((inp[2]*r^4+r[6])*r^4+r[10])*r^2
+	# ((inp[3]*r^4+r[7])*r^4+r[11])*r^1
+	#   \________/ \_______/
+	################################################################
+
+sub vsplat_input {
+	&vmovdqa	(&QWP(32*2,"esp"),$D2);
+	&vpsrldq	($D2,$T0,6);			# splat input
+	&vmovdqa	(&QWP(32*0,"esp"),$D0);
+	&vpsrldq	($D0,$T1,6);
+	&vmovdqa	(&QWP(32*1,"esp"),$D1);
+	&vpunpckhqdq	($D1,$T0,$T1);			# 4
+	&vpunpcklqdq	($T0,$T0,$T1);			# 0:1
+	&vpunpcklqdq	($D2,$D2,$D0);			# 2:3
+
+	&vpsrlq		($D0,$D2,30);
+	&vpsrlq		($D2,$D2,4);
+	&vpsrlq		($T1,$T0,26);
+	&vpsrlq		($D1,$D1,40);			# 4
+	&vpand		($D2,$D2,$MASK);		# 2
+	&vpand		($T0,$T0,$MASK);		# 0
+	&vpand		($T1,$T1,$MASK);		# 1
+	&vpand		($D0,$D0,$MASK);		# 3 (*)
+	&vpor		($D1,$D1,&QWP(0,"ebx"));	# padbit, yes, always
+
+	# (*)	note that output is counterintuitive, inp[3:4] is
+	#	returned in $D1-2, while $D3-4 are preserved;
+}
+	&vsplat_input	();
+
+sub vpmuladd {
+my $addr = shift;
+
+	&vpaddq		($D2,$D2,&QWP(32*2,"esp"));	# add hash value
+	&vpaddq		($T0,$T0,&QWP(32*0,"esp"));
+	&vpaddq		($T1,$T1,&QWP(32*1,"esp"));
+	&vpaddq		($D0,$D0,$D3);
+	&vpaddq		($D1,$D1,$D4);
+
+	################################################################
+	# d3 = h2*r1   + h0*r3 + h1*r2   + h3*r0   + h4*5*r4
+	# d4 = h2*r2   + h0*r4 + h1*r3   + h3*r1   + h4*r0
+	# d0 = h2*5*r3 + h0*r0 + h1*5*r4 + h3*5*r2 + h4*5*r1
+	# d1 = h2*5*r4 + h0*r1 + h1*r0   + h3*5*r3 + h4*5*r2
+	# d2 = h2*r0   + h0*r2 + h1*r1   + h3*5*r4 + h4*5*r3
+
+	&vpmuludq	($D3,$D2,&$addr(1));		# d3 = h2*r1
+	 &vmovdqa	(QWP(32*1,"esp"),$T1);
+	&vpmuludq	($D4,$D2,&$addr(2));		# d4 = h2*r2
+	 &vmovdqa	(QWP(32*3,"esp"),$D0);
+	&vpmuludq	($D0,$D2,&$addr(7));		# d0 = h2*s3
+	 &vmovdqa	(QWP(32*4,"esp"),$D1);
+	&vpmuludq	($D1,$D2,&$addr(8));		# d1 = h2*s4
+	&vpmuludq	($D2,$D2,&$addr(0));		# d2 = h2*r0
+
+	&vpmuludq	($T2,$T0,&$addr(3));		# h0*r3
+	&vpaddq		($D3,$D3,$T2);			# d3 += h0*r3
+	&vpmuludq	($T1,$T0,&$addr(4));		# h0*r4
+	&vpaddq		($D4,$D4,$T1);			# d4 + h0*r4
+	&vpmuludq	($T2,$T0,&$addr(0));		# h0*r0
+	&vpaddq		($D0,$D0,$T2);			# d0 + h0*r0
+	 &vmovdqa	($T2,&QWP(32*1,"esp"));		# h1
+	&vpmuludq	($T1,$T0,&$addr(1));		# h0*r1
+	&vpaddq		($D1,$D1,$T1);			# d1 += h0*r1
+	&vpmuludq	($T0,$T0,&$addr(2));		# h0*r2
+	&vpaddq		($D2,$D2,$T0);			# d2 += h0*r2
+
+	&vpmuludq	($T1,$T2,&$addr(2));		# h1*r2
+	&vpaddq		($D3,$D3,$T1);			# d3 += h1*r2
+	&vpmuludq	($T0,$T2,&$addr(3));		# h1*r3
+	&vpaddq		($D4,$D4,$T0);			# d4 += h1*r3
+	&vpmuludq	($T1,$T2,&$addr(8));		# h1*s4
+	&vpaddq		($D0,$D0,$T1);			# d0 += h1*s4
+	 &vmovdqa	($T1,&QWP(32*3,"esp"));		# h3
+	&vpmuludq	($T0,$T2,&$addr(0));		# h1*r0
+	&vpaddq		($D1,$D1,$T0);			# d1 += h1*r0
+	&vpmuludq	($T2,$T2,&$addr(1));		# h1*r1
+	&vpaddq		($D2,$D2,$T2);			# d2 += h1*r1
+
+	&vpmuludq	($T0,$T1,&$addr(0));		# h3*r0
+	&vpaddq		($D3,$D3,$T0);			# d3 += h3*r0
+	&vpmuludq	($T2,$T1,&$addr(1));		# h3*r1
+	&vpaddq		($D4,$D4,$T2);			# d4 += h3*r1
+	&vpmuludq	($T0,$T1,&$addr(6));		# h3*s2
+	&vpaddq		($D0,$D0,$T0);			# d0 += h3*s2
+	 &vmovdqa	($T0,&QWP(32*4,"esp"));		# h4
+	&vpmuludq	($T2,$T1,&$addr(7));		# h3*s3
+	&vpaddq		($D1,$D1,$T2);			# d1+= h3*s3
+	&vpmuludq	($T1,$T1,&$addr(8));		# h3*s4
+	&vpaddq		($D2,$D2,$T1);			# d2 += h3*s4
+
+	&vpmuludq	($T2,$T0,&$addr(8));		# h4*s4
+	&vpaddq		($D3,$D3,$T2);			# d3 += h4*s4
+	&vpmuludq	($T1,$T0,&$addr(5));		# h4*s1
+	&vpaddq		($D0,$D0,$T1);			# d0 += h4*s1
+	&vpmuludq	($T2,$T0,&$addr(0));		# h4*r0
+	&vpaddq		($D4,$D4,$T2);			# d4 += h4*r0
+	 &vmovdqa	($MASK,&QWP(64,"ebx"));
+	&vpmuludq	($T1,$T0,&$addr(6));		# h4*s2
+	&vpaddq		($D1,$D1,$T1);			# d1 += h4*s2
+	&vpmuludq	($T0,$T0,&$addr(7));		# h4*s3
+	&vpaddq		($D2,$D2,$T0);			# d2 += h4*s3
+}
+	&vpmuladd	(sub {	my $i=shift; &QWP(32*$i-128,"edx");	});
+
+sub vlazy_reduction {
+	################################################################
+	# lazy reduction
+
+	 &vpsrlq	($T0,$D3,26);
+	 &vpand		($D3,$D3,$MASK);
+	&vpsrlq		($T1,$D0,26);
+	&vpand		($D0,$D0,$MASK);
+	 &vpaddq	($D4,$D4,$T0);			# h3 -> h4
+	&vpaddq		($D1,$D1,$T1);			# h0 -> h1
+	 &vpsrlq	($T0,$D4,26);
+	 &vpand		($D4,$D4,$MASK);
+	&vpsrlq		($T1,$D1,26);
+	&vpand		($D1,$D1,$MASK);
+	&vpaddq		($D2,$D2,$T1);			# h1 -> h2
+	 &vpaddq	($D0,$D0,$T0);
+	 &vpsllq	($T0,$T0,2);
+	&vpsrlq		($T1,$D2,26);
+	&vpand		($D2,$D2,$MASK);
+	 &vpaddq	($D0,$D0,$T0);			# h4 -> h0
+	&vpaddq		($D3,$D3,$T1);			# h2 -> h3
+	&vpsrlq		($T1,$D3,26);
+	 &vpsrlq	($T0,$D0,26);
+	 &vpand		($D0,$D0,$MASK);
+	&vpand		($D3,$D3,$MASK);
+	 &vpaddq	($D1,$D1,$T0);			# h0 -> h1
+	&vpaddq		($D4,$D4,$T1);			# h3 -> h4
+}
+	&vlazy_reduction();
+
+	&vmovdqu	(&X($T0),&QWP(16*0,"esi"));	# load input
+	&vmovdqu	(&X($T1),&QWP(16*1,"esi"));
+	&vinserti128	($T0,$T0,&QWP(16*2,"esi"),1);
+	&vinserti128	($T1,$T1,&QWP(16*3,"esi"),1);
+	&lea		("esi",&DWP(16*4,"esi"));
+	&sub		("ecx",64);
+	&jnz		(&label("loop"));
+
+&set_label("tail");
+	&vsplat_input	();
+	&and		("ebx",-64);			# restore pointer
+
+	&vpmuladd	(sub {	my $i=shift; &QWP(4+32*$i-128,"edx");	});
+
+	################################################################
+	# horizontal addition
+
+	&vpsrldq	($T0,$D4,8);
+	&vpsrldq	($T1,$D3,8);
+	&vpaddq		($D4,$D4,$T0);
+	&vpsrldq	($T0,$D0,8);
+	&vpaddq		($D3,$D3,$T1);
+	&vpsrldq	($T1,$D1,8);
+	&vpaddq		($D0,$D0,$T0);
+	&vpsrldq	($T0,$D2,8);
+	&vpaddq		($D1,$D1,$T1);
+	&vpermq		($T1,$D4,2);			# keep folding
+	&vpaddq		($D2,$D2,$T0);
+	&vpermq		($T0,$D3,2);
+	&vpaddq		($D4,$D4,$T1);
+	&vpermq		($T1,$D0,2);
+	&vpaddq		($D3,$D3,$T0);
+	&vpermq		($T0,$D1,2);
+	&vpaddq		($D0,$D0,$T1);
+	&vpermq		($T1,$D2,2);
+	&vpaddq		($D1,$D1,$T0);
+	&vpaddq		($D2,$D2,$T1);
+
+	&vlazy_reduction();
+
+	&cmp		("ecx",0);
+	&je		(&label("done"));
+
+	################################################################
+	# clear all but single word
+
+	&vpshufd	(&X($D0),&X($D0),0b11111100);
+	&lea		("edx",&DWP(32*5+128,"esp"));	# restore pointer
+	&vpshufd	(&X($D1),&X($D1),0b11111100);
+	&vpshufd	(&X($D2),&X($D2),0b11111100);
+	&vpshufd	(&X($D3),&X($D3),0b11111100);
+	&vpshufd	(&X($D4),&X($D4),0b11111100);
+	&jmp		(&label("even"));
+
+&set_label("done",16);
+	&vmovd		(&DWP(-16*3+4*0,"edi"),&X($D0));# store hash value
+	&vmovd		(&DWP(-16*3+4*1,"edi"),&X($D1));
+	&vmovd		(&DWP(-16*3+4*2,"edi"),&X($D2));
+	&vmovd		(&DWP(-16*3+4*3,"edi"),&X($D3));
+	&vmovd		(&DWP(-16*3+4*4,"edi"),&X($D4));
+	&vzeroupper	();
+	&mov	("esp","ebp");
+&set_label("nodata");
+&function_end("_poly1305_blocks_avx2");
+}
+&set_label("const_sse2",64);
+	&data_word(1<<24,0,	1<<24,0,	1<<24,0,	1<<24,0);
+	&data_word(0,0,		0,0,		0,0,		0,0);
+	&data_word(0x03ffffff,0,0x03ffffff,0,	0x03ffffff,0,	0x03ffffff,0);
+	&data_word(0x0fffffff,0x0ffffffc,0x0ffffffc,0x0ffffffc);
+}
+&asciz	("Poly1305 for x86, CRYPTOGAMS by ");
+&align	(4);
+
+&asm_finish();
+
+close STDOUT;
diff --git a/crypto/poly1305/asm/poly1305-x86_64.pl b/crypto/poly1305/asm/poly1305-x86_64.pl
new file mode 100755
index 000000000..4c22ded58
--- /dev/null
+++ b/crypto/poly1305/asm/poly1305-x86_64.pl
@@ -0,0 +1,2268 @@
+#! /usr/bin/env perl
+# Copyright 2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+#
+# ====================================================================
+# Written by Andy Polyakov  for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+#
+# This module implements Poly1305 hash for x86_64.
+#
+# March 2015
+#
+# Numbers are cycles per processed byte with poly1305_blocks alone,
+# measured with rdtsc at fixed clock frequency.
+#
+#		IALU/gcc-4.8(*)	AVX(**)		AVX2
+# P4		4.46/+120%	-
+# Core 2	2.41/+90%	-
+# Westmere	1.88/+120%	-
+# Sandy Bridge	1.39/+140%	1.10
+# Haswell	1.14/+175%	1.11		0.65
+# Skylake	1.13/+120%	0.96		0.51
+# Silvermont	2.83/+95%	-
+# Goldmont	1.70/+180%	-
+# VIA Nano	1.82/+150%	-
+# Sledgehammer	1.38/+160%	-
+# Bulldozer	2.30/+130%	0.97
+#
+# (*)	improvement coefficients relative to clang are more modest and
+#	are ~50% on most processors, in both cases we are comparing to
+#	__int128 code;
+# (**)	SSE2 implementation was attempted, but among non-AVX processors
+#	it was faster than integer-only code only on older Intel P4 and
+#	Core processors, 50-30%, less newer processor is, but slower on
+#	contemporary ones, for example almost 2x slower on Atom, and as
+#	former are naturally disappearing, SSE2 is deemed unnecessary;
+
+$flavour = shift;
+$output  = shift;
+if ($flavour =~ /\./) { $output = $flavour; undef $flavour; }
+
+$win64=0; $win64=1 if ($flavour =~ /[nm]asm|mingw64/ || $output =~ /\.asm$/);
+
+$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+( $xlate="${dir}x86_64-xlate.pl" and -f $xlate ) or
+( $xlate="${dir}../../perlasm/x86_64-xlate.pl" and -f $xlate) or
+die "can't locate x86_64-xlate.pl";
+
+if (`$ENV{CC} -Wa,-v -c -o /dev/null -x assembler /dev/null 2>&1`
+		=~ /GNU assembler version ([2-9]\.[0-9]+)/) {
+	$avx = ($1>=2.19) + ($1>=2.22);
+}
+
+if (!$avx && $win64 && ($flavour =~ /nasm/ || $ENV{ASM} =~ /nasm/) &&
+	   `nasm -v 2>&1` =~ /NASM version ([2-9]\.[0-9]+)/) {
+	$avx = ($1>=2.09) + ($1>=2.10);
+}
+
+if (!$avx && $win64 && ($flavour =~ /masm/ || $ENV{ASM} =~ /ml64/) &&
+	   `ml64 2>&1` =~ /Version ([0-9]+)\./) {
+	$avx = ($1>=10) + ($1>=12);
+}
+
+if (!$avx && `$ENV{CC} -v 2>&1` =~ /((?:^clang|LLVM) version|.*based on LLVM) ([3-9]\.[0-9]+)/) {
+	$avx = ($2>=3.0) + ($2>3.0);
+}
+
+open OUT,"| \"$^X\" \"$xlate\" $flavour \"$output\"";
+*STDOUT=*OUT;
+
+my ($ctx,$inp,$len,$padbit)=("%rdi","%rsi","%rdx","%rcx");
+my ($mac,$nonce)=($inp,$len);	# *_emit arguments
+my ($d1,$d2,$d3, $r0,$r1,$s1)=map("%r$_",(8..13));
+my ($h0,$h1,$h2)=("%r14","%rbx","%rbp");
+
+sub poly1305_iteration {
+# input:	copy of $r1 in %rax, $h0-$h2, $r0-$r1
+# output:	$h0-$h2 *= $r0-$r1
+$code.=<<___;
+	mulq	$h0			# h0*r1
+	mov	%rax,$d2
+	 mov	$r0,%rax
+	mov	%rdx,$d3
+
+	mulq	$h0			# h0*r0
+	mov	%rax,$h0		# future $h0
+	 mov	$r0,%rax
+	mov	%rdx,$d1
+
+	mulq	$h1			# h1*r0
+	add	%rax,$d2
+	 mov	$s1,%rax
+	adc	%rdx,$d3
+
+	mulq	$h1			# h1*s1
+	 mov	$h2,$h1			# borrow $h1
+	add	%rax,$h0
+	adc	%rdx,$d1
+
+	imulq	$s1,$h1			# h2*s1
+	add	$h1,$d2
+	 mov	$d1,$h1
+	adc	\$0,$d3
+
+	imulq	$r0,$h2			# h2*r0
+	add	$d2,$h1
+	mov	\$-4,%rax		# mask value
+	adc	$h2,$d3
+
+	and	$d3,%rax		# last reduction step
+	mov	$d3,$h2
+	shr	\$2,$d3
+	and	\$3,$h2
+	add	$d3,%rax
+	add	%rax,$h0
+	adc	\$0,$h1
+	adc	\$0,$h2
+___
+}
+
+########################################################################
+# Layout of opaque area is following.
+#
+#	unsigned __int64 h[3];		# current hash value base 2^64
+#	unsigned __int64 r[2];		# key value base 2^64
+
+$code.=<<___;
+.text
+
+.extern	OPENSSL_ia32cap_P
+
+.globl	poly1305_init
+.hidden	poly1305_init
+.globl	poly1305_blocks
+.hidden	poly1305_blocks
+.globl	poly1305_emit
+.hidden	poly1305_emit
+
+.type	poly1305_init,\@function,3
+.align	32
+poly1305_init:
+	xor	%rax,%rax
+	mov	%rax,0($ctx)		# initialize hash value
+	mov	%rax,8($ctx)
+	mov	%rax,16($ctx)
+
+	cmp	\$0,$inp
+	je	.Lno_key
+
+	lea	poly1305_blocks(%rip),%r10
+	lea	poly1305_emit(%rip),%r11
+___
+$code.=<<___	if ($avx);
+	mov	OPENSSL_ia32cap_P+4(%rip),%r9
+	lea	poly1305_blocks_avx(%rip),%rax
+	lea	poly1305_emit_avx(%rip),%rcx
+	bt	\$`60-32`,%r9		# AVX?
+	cmovc	%rax,%r10
+	cmovc	%rcx,%r11
+___
+$code.=<<___	if ($avx>1);
+	lea	poly1305_blocks_avx2(%rip),%rax
+	bt	\$`5+32`,%r9		# AVX2?
+	cmovc	%rax,%r10
+___
+$code.=<<___;
+	mov	\$0x0ffffffc0fffffff,%rax
+	mov	\$0x0ffffffc0ffffffc,%rcx
+	and	0($inp),%rax
+	and	8($inp),%rcx
+	mov	%rax,24($ctx)
+	mov	%rcx,32($ctx)
+___
+$code.=<<___	if ($flavour !~ /elf32/);
+	mov	%r10,0(%rdx)
+	mov	%r11,8(%rdx)
+___
+$code.=<<___	if ($flavour =~ /elf32/);
+	mov	%r10d,0(%rdx)
+	mov	%r11d,4(%rdx)
+___
+$code.=<<___;
+	mov	\$1,%eax
+.Lno_key:
+	ret
+.size	poly1305_init,.-poly1305_init
+
+.type	poly1305_blocks,\@function,4
+.align	32
+poly1305_blocks:
+.Lblocks:
+	shr	\$4,$len
+	jz	.Lno_data		# too short
+
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+.Lblocks_body:
+
+	mov	$len,%r15		# reassign $len
+
+	mov	24($ctx),$r0		# load r
+	mov	32($ctx),$s1
+
+	mov	0($ctx),$h0		# load hash value
+	mov	8($ctx),$h1
+	mov	16($ctx),$h2
+
+	mov	$s1,$r1
+	shr	\$2,$s1
+	mov	$r1,%rax
+	add	$r1,$s1			# s1 = r1 + (r1 >> 2)
+	jmp	.Loop
+
+.align	32
+.Loop:
+	add	0($inp),$h0		# accumulate input
+	adc	8($inp),$h1
+	lea	16($inp),$inp
+	adc	$padbit,$h2
+___
+	&poly1305_iteration();
+$code.=<<___;
+	mov	$r1,%rax
+	dec	%r15			# len-=16
+	jnz	.Loop
+
+	mov	$h0,0($ctx)		# store hash value
+	mov	$h1,8($ctx)
+	mov	$h2,16($ctx)
+
+	mov	0(%rsp),%r15
+	mov	8(%rsp),%r14
+	mov	16(%rsp),%r13
+	mov	24(%rsp),%r12
+	mov	32(%rsp),%rbp
+	mov	40(%rsp),%rbx
+	lea	48(%rsp),%rsp
+.Lno_data:
+.Lblocks_epilogue:
+	ret
+.size	poly1305_blocks,.-poly1305_blocks
+
+.type	poly1305_emit,\@function,3
+.align	32
+poly1305_emit:
+.Lemit:
+	mov	0($ctx),%r8	# load hash value
+	mov	8($ctx),%r9
+	mov	16($ctx),%r10
+
+	mov	%r8,%rax
+	add	\$5,%r8		# compare to modulus
+	mov	%r9,%rcx
+	adc	\$0,%r9
+	adc	\$0,%r10
+	shr	\$2,%r10	# did 130-bit value overfow?
+	cmovnz	%r8,%rax
+	cmovnz	%r9,%rcx
+
+	add	0($nonce),%rax	# accumulate nonce
+	adc	8($nonce),%rcx
+	mov	%rax,0($mac)	# write result
+	mov	%rcx,8($mac)
+
+	ret
+.size	poly1305_emit,.-poly1305_emit
+___
+if ($avx) {
+
+########################################################################
+# Layout of opaque area is following.
+#
+#	unsigned __int32 h[5];		# current hash value base 2^26
+#	unsigned __int32 is_base2_26;
+#	unsigned __int64 r[2];		# key value base 2^64
+#	unsigned __int64 pad;
+#	struct { unsigned __int32 r^2, r^1, r^4, r^3; } r[9];
+#
+# where r^n are base 2^26 digits of degrees of multiplier key. There are
+# 5 digits, but last four are interleaved with multiples of 5, totalling
+# in 9 elements: r0, r1, 5*r1, r2, 5*r2, r3, 5*r3, r4, 5*r4.
+
+my ($H0,$H1,$H2,$H3,$H4, $T0,$T1,$T2,$T3,$T4, $D0,$D1,$D2,$D3,$D4, $MASK) =
+    map("%xmm$_",(0..15));
+
+$code.=<<___;
+.type	__poly1305_block,\@abi-omnipotent
+.align	32
+__poly1305_block:
+___
+	&poly1305_iteration();
+$code.=<<___;
+	ret
+.size	__poly1305_block,.-__poly1305_block
+
+.type	__poly1305_init_avx,\@abi-omnipotent
+.align	32
+__poly1305_init_avx:
+	mov	$r0,$h0
+	mov	$r1,$h1
+	xor	$h2,$h2
+
+	lea	48+64($ctx),$ctx	# size optimization
+
+	mov	$r1,%rax
+	call	__poly1305_block	# r^2
+
+	mov	\$0x3ffffff,%eax	# save interleaved r^2 and r base 2^26
+	mov	\$0x3ffffff,%edx
+	mov	$h0,$d1
+	and	$h0#d,%eax
+	mov	$r0,$d2
+	and	$r0#d,%edx
+	mov	%eax,`16*0+0-64`($ctx)
+	shr	\$26,$d1
+	mov	%edx,`16*0+4-64`($ctx)
+	shr	\$26,$d2
+
+	mov	\$0x3ffffff,%eax
+	mov	\$0x3ffffff,%edx
+	and	$d1#d,%eax
+	and	$d2#d,%edx
+	mov	%eax,`16*1+0-64`($ctx)
+	lea	(%rax,%rax,4),%eax	# *5
+	mov	%edx,`16*1+4-64`($ctx)
+	lea	(%rdx,%rdx,4),%edx	# *5
+	mov	%eax,`16*2+0-64`($ctx)
+	shr	\$26,$d1
+	mov	%edx,`16*2+4-64`($ctx)
+	shr	\$26,$d2
+
+	mov	$h1,%rax
+	mov	$r1,%rdx
+	shl	\$12,%rax
+	shl	\$12,%rdx
+	or	$d1,%rax
+	or	$d2,%rdx
+	and	\$0x3ffffff,%eax
+	and	\$0x3ffffff,%edx
+	mov	%eax,`16*3+0-64`($ctx)
+	lea	(%rax,%rax,4),%eax	# *5
+	mov	%edx,`16*3+4-64`($ctx)
+	lea	(%rdx,%rdx,4),%edx	# *5
+	mov	%eax,`16*4+0-64`($ctx)
+	mov	$h1,$d1
+	mov	%edx,`16*4+4-64`($ctx)
+	mov	$r1,$d2
+
+	mov	\$0x3ffffff,%eax
+	mov	\$0x3ffffff,%edx
+	shr	\$14,$d1
+	shr	\$14,$d2
+	and	$d1#d,%eax
+	and	$d2#d,%edx
+	mov	%eax,`16*5+0-64`($ctx)
+	lea	(%rax,%rax,4),%eax	# *5
+	mov	%edx,`16*5+4-64`($ctx)
+	lea	(%rdx,%rdx,4),%edx	# *5
+	mov	%eax,`16*6+0-64`($ctx)
+	shr	\$26,$d1
+	mov	%edx,`16*6+4-64`($ctx)
+	shr	\$26,$d2
+
+	mov	$h2,%rax
+	shl	\$24,%rax
+	or	%rax,$d1
+	mov	$d1#d,`16*7+0-64`($ctx)
+	lea	($d1,$d1,4),$d1		# *5
+	mov	$d2#d,`16*7+4-64`($ctx)
+	lea	($d2,$d2,4),$d2		# *5
+	mov	$d1#d,`16*8+0-64`($ctx)
+	mov	$d2#d,`16*8+4-64`($ctx)
+
+	mov	$r1,%rax
+	call	__poly1305_block	# r^3
+
+	mov	\$0x3ffffff,%eax	# save r^3 base 2^26
+	mov	$h0,$d1
+	and	$h0#d,%eax
+	shr	\$26,$d1
+	mov	%eax,`16*0+12-64`($ctx)
+
+	mov	\$0x3ffffff,%edx
+	and	$d1#d,%edx
+	mov	%edx,`16*1+12-64`($ctx)
+	lea	(%rdx,%rdx,4),%edx	# *5
+	shr	\$26,$d1
+	mov	%edx,`16*2+12-64`($ctx)
+
+	mov	$h1,%rax
+	shl	\$12,%rax
+	or	$d1,%rax
+	and	\$0x3ffffff,%eax
+	mov	%eax,`16*3+12-64`($ctx)
+	lea	(%rax,%rax,4),%eax	# *5
+	mov	$h1,$d1
+	mov	%eax,`16*4+12-64`($ctx)
+
+	mov	\$0x3ffffff,%edx
+	shr	\$14,$d1
+	and	$d1#d,%edx
+	mov	%edx,`16*5+12-64`($ctx)
+	lea	(%rdx,%rdx,4),%edx	# *5
+	shr	\$26,$d1
+	mov	%edx,`16*6+12-64`($ctx)
+
+	mov	$h2,%rax
+	shl	\$24,%rax
+	or	%rax,$d1
+	mov	$d1#d,`16*7+12-64`($ctx)
+	lea	($d1,$d1,4),$d1		# *5
+	mov	$d1#d,`16*8+12-64`($ctx)
+
+	mov	$r1,%rax
+	call	__poly1305_block	# r^4
+
+	mov	\$0x3ffffff,%eax	# save r^4 base 2^26
+	mov	$h0,$d1
+	and	$h0#d,%eax
+	shr	\$26,$d1
+	mov	%eax,`16*0+8-64`($ctx)
+
+	mov	\$0x3ffffff,%edx
+	and	$d1#d,%edx
+	mov	%edx,`16*1+8-64`($ctx)
+	lea	(%rdx,%rdx,4),%edx	# *5
+	shr	\$26,$d1
+	mov	%edx,`16*2+8-64`($ctx)
+
+	mov	$h1,%rax
+	shl	\$12,%rax
+	or	$d1,%rax
+	and	\$0x3ffffff,%eax
+	mov	%eax,`16*3+8-64`($ctx)
+	lea	(%rax,%rax,4),%eax	# *5
+	mov	$h1,$d1
+	mov	%eax,`16*4+8-64`($ctx)
+
+	mov	\$0x3ffffff,%edx
+	shr	\$14,$d1
+	and	$d1#d,%edx
+	mov	%edx,`16*5+8-64`($ctx)
+	lea	(%rdx,%rdx,4),%edx	# *5
+	shr	\$26,$d1
+	mov	%edx,`16*6+8-64`($ctx)
+
+	mov	$h2,%rax
+	shl	\$24,%rax
+	or	%rax,$d1
+	mov	$d1#d,`16*7+8-64`($ctx)
+	lea	($d1,$d1,4),$d1		# *5
+	mov	$d1#d,`16*8+8-64`($ctx)
+
+	lea	-48-64($ctx),$ctx	# size [de-]optimization
+	ret
+.size	__poly1305_init_avx,.-__poly1305_init_avx
+
+.type	poly1305_blocks_avx,\@function,4
+.align	32
+poly1305_blocks_avx:
+	mov	20($ctx),%r8d		# is_base2_26
+	cmp	\$128,$len
+	jae	.Lblocks_avx
+	test	%r8d,%r8d
+	jz	.Lblocks
+
+.Lblocks_avx:
+	and	\$-16,$len
+	jz	.Lno_data_avx
+
+	vzeroupper
+
+	test	%r8d,%r8d
+	jz	.Lbase2_64_avx
+
+	test	\$31,$len
+	jz	.Leven_avx
+
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+.Lblocks_avx_body:
+
+	mov	$len,%r15		# reassign $len
+
+	mov	0($ctx),$d1		# load hash value
+	mov	8($ctx),$d2
+	mov	16($ctx),$h2#d
+
+	mov	24($ctx),$r0		# load r
+	mov	32($ctx),$s1
+
+	################################# base 2^26 -> base 2^64
+	mov	$d1#d,$h0#d
+	and	\$`-1*(1<<31)`,$d1
+	mov	$d2,$r1			# borrow $r1
+	mov	$d2#d,$h1#d
+	and	\$`-1*(1<<31)`,$d2
+
+	shr	\$6,$d1
+	shl	\$52,$r1
+	add	$d1,$h0
+	shr	\$12,$h1
+	shr	\$18,$d2
+	add	$r1,$h0
+	adc	$d2,$h1
+
+	mov	$h2,$d1
+	shl	\$40,$d1
+	shr	\$24,$h2
+	add	$d1,$h1
+	adc	\$0,$h2			# can be partially reduced...
+
+	mov	\$-4,$d2		# ... so reduce
+	mov	$h2,$d1
+	and	$h2,$d2
+	shr	\$2,$d1
+	and	\$3,$h2
+	add	$d2,$d1			# =*5
+	add	$d1,$h0
+	adc	\$0,$h1
+	adc	\$0,$h2
+
+	mov	$s1,$r1
+	mov	$s1,%rax
+	shr	\$2,$s1
+	add	$r1,$s1			# s1 = r1 + (r1 >> 2)
+
+	add	0($inp),$h0		# accumulate input
+	adc	8($inp),$h1
+	lea	16($inp),$inp
+	adc	$padbit,$h2
+
+	call	__poly1305_block
+
+	test	$padbit,$padbit		# if $padbit is zero,
+	jz	.Lstore_base2_64_avx	# store hash in base 2^64 format
+
+	################################# base 2^64 -> base 2^26
+	mov	$h0,%rax
+	mov	$h0,%rdx
+	shr	\$52,$h0
+	mov	$h1,$r0
+	mov	$h1,$r1
+	shr	\$26,%rdx
+	and	\$0x3ffffff,%rax	# h[0]
+	shl	\$12,$r0
+	and	\$0x3ffffff,%rdx	# h[1]
+	shr	\$14,$h1
+	or	$r0,$h0
+	shl	\$24,$h2
+	and	\$0x3ffffff,$h0		# h[2]
+	shr	\$40,$r1
+	and	\$0x3ffffff,$h1		# h[3]
+	or	$r1,$h2			# h[4]
+
+	sub	\$16,%r15
+	jz	.Lstore_base2_26_avx
+
+	vmovd	%rax#d,$H0
+	vmovd	%rdx#d,$H1
+	vmovd	$h0#d,$H2
+	vmovd	$h1#d,$H3
+	vmovd	$h2#d,$H4
+	jmp	.Lproceed_avx
+
+.align	32
+.Lstore_base2_64_avx:
+	mov	$h0,0($ctx)
+	mov	$h1,8($ctx)
+	mov	$h2,16($ctx)		# note that is_base2_26 is zeroed
+	jmp	.Ldone_avx
+
+.align	16
+.Lstore_base2_26_avx:
+	mov	%rax#d,0($ctx)		# store hash value base 2^26
+	mov	%rdx#d,4($ctx)
+	mov	$h0#d,8($ctx)
+	mov	$h1#d,12($ctx)
+	mov	$h2#d,16($ctx)
+.align	16
+.Ldone_avx:
+	mov	0(%rsp),%r15
+	mov	8(%rsp),%r14
+	mov	16(%rsp),%r13
+	mov	24(%rsp),%r12
+	mov	32(%rsp),%rbp
+	mov	40(%rsp),%rbx
+	lea	48(%rsp),%rsp
+.Lno_data_avx:
+.Lblocks_avx_epilogue:
+	ret
+
+.align	32
+.Lbase2_64_avx:
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+.Lbase2_64_avx_body:
+
+	mov	$len,%r15		# reassign $len
+
+	mov	24($ctx),$r0		# load r
+	mov	32($ctx),$s1
+
+	mov	0($ctx),$h0		# load hash value
+	mov	8($ctx),$h1
+	mov	16($ctx),$h2#d
+
+	mov	$s1,$r1
+	mov	$s1,%rax
+	shr	\$2,$s1
+	add	$r1,$s1			# s1 = r1 + (r1 >> 2)
+
+	test	\$31,$len
+	jz	.Linit_avx
+
+	add	0($inp),$h0		# accumulate input
+	adc	8($inp),$h1
+	lea	16($inp),$inp
+	adc	$padbit,$h2
+	sub	\$16,%r15
+
+	call	__poly1305_block
+
+.Linit_avx:
+	################################# base 2^64 -> base 2^26
+	mov	$h0,%rax
+	mov	$h0,%rdx
+	shr	\$52,$h0
+	mov	$h1,$d1
+	mov	$h1,$d2
+	shr	\$26,%rdx
+	and	\$0x3ffffff,%rax	# h[0]
+	shl	\$12,$d1
+	and	\$0x3ffffff,%rdx	# h[1]
+	shr	\$14,$h1
+	or	$d1,$h0
+	shl	\$24,$h2
+	and	\$0x3ffffff,$h0		# h[2]
+	shr	\$40,$d2
+	and	\$0x3ffffff,$h1		# h[3]
+	or	$d2,$h2			# h[4]
+
+	vmovd	%rax#d,$H0
+	vmovd	%rdx#d,$H1
+	vmovd	$h0#d,$H2
+	vmovd	$h1#d,$H3
+	vmovd	$h2#d,$H4
+	movl	\$1,20($ctx)		# set is_base2_26
+
+	call	__poly1305_init_avx
+
+.Lproceed_avx:
+	mov	%r15,$len
+
+	mov	0(%rsp),%r15
+	mov	8(%rsp),%r14
+	mov	16(%rsp),%r13
+	mov	24(%rsp),%r12
+	mov	32(%rsp),%rbp
+	mov	40(%rsp),%rbx
+	lea	48(%rsp),%rax
+	lea	48(%rsp),%rsp
+.Lbase2_64_avx_epilogue:
+	jmp	.Ldo_avx
+
+.align	32
+.Leven_avx:
+	vmovd		4*0($ctx),$H0		# load hash value
+	vmovd		4*1($ctx),$H1
+	vmovd		4*2($ctx),$H2
+	vmovd		4*3($ctx),$H3
+	vmovd		4*4($ctx),$H4
+
+.Ldo_avx:
+___
+$code.=<<___	if (!$win64);
+	lea		-0x58(%rsp),%r11
+	sub		\$0x178,%rsp
+___
+$code.=<<___	if ($win64);
+	lea		-0xf8(%rsp),%r11
+	sub		\$0x218,%rsp
+	vmovdqa		%xmm6,0x50(%r11)
+	vmovdqa		%xmm7,0x60(%r11)
+	vmovdqa		%xmm8,0x70(%r11)
+	vmovdqa		%xmm9,0x80(%r11)
+	vmovdqa		%xmm10,0x90(%r11)
+	vmovdqa		%xmm11,0xa0(%r11)
+	vmovdqa		%xmm12,0xb0(%r11)
+	vmovdqa		%xmm13,0xc0(%r11)
+	vmovdqa		%xmm14,0xd0(%r11)
+	vmovdqa		%xmm15,0xe0(%r11)
+.Ldo_avx_body:
+___
+$code.=<<___;
+	sub		\$64,$len
+	lea		-32($inp),%rax
+	cmovc		%rax,$inp
+
+	vmovdqu		`16*3`($ctx),$D4	# preload r0^2
+	lea		`16*3+64`($ctx),$ctx	# size optimization
+	lea		.Lconst(%rip),%rcx
+
+	################################################################
+	# load input
+	vmovdqu		16*2($inp),$T0
+	vmovdqu		16*3($inp),$T1
+	vmovdqa		64(%rcx),$MASK		# .Lmask26
+
+	vpsrldq		\$6,$T0,$T2		# splat input
+	vpsrldq		\$6,$T1,$T3
+	vpunpckhqdq	$T1,$T0,$T4		# 4
+	vpunpcklqdq	$T1,$T0,$T0		# 0:1
+	vpunpcklqdq	$T3,$T2,$T3		# 2:3
+
+	vpsrlq		\$40,$T4,$T4		# 4
+	vpsrlq		\$26,$T0,$T1
+	vpand		$MASK,$T0,$T0		# 0
+	vpsrlq		\$4,$T3,$T2
+	vpand		$MASK,$T1,$T1		# 1
+	vpsrlq		\$30,$T3,$T3
+	vpand		$MASK,$T2,$T2		# 2
+	vpand		$MASK,$T3,$T3		# 3
+	vpor		32(%rcx),$T4,$T4	# padbit, yes, always
+
+	jbe		.Lskip_loop_avx
+
+	# expand and copy pre-calculated table to stack
+	vmovdqu		`16*1-64`($ctx),$D1
+	vmovdqu		`16*2-64`($ctx),$D2
+	vpshufd		\$0xEE,$D4,$D3		# 34xx -> 3434
+	vpshufd		\$0x44,$D4,$D0		# xx12 -> 1212
+	vmovdqa		$D3,-0x90(%r11)
+	vmovdqa		$D0,0x00(%rsp)
+	vpshufd		\$0xEE,$D1,$D4
+	vmovdqu		`16*3-64`($ctx),$D0
+	vpshufd		\$0x44,$D1,$D1
+	vmovdqa		$D4,-0x80(%r11)
+	vmovdqa		$D1,0x10(%rsp)
+	vpshufd		\$0xEE,$D2,$D3
+	vmovdqu		`16*4-64`($ctx),$D1
+	vpshufd		\$0x44,$D2,$D2
+	vmovdqa		$D3,-0x70(%r11)
+	vmovdqa		$D2,0x20(%rsp)
+	vpshufd		\$0xEE,$D0,$D4
+	vmovdqu		`16*5-64`($ctx),$D2
+	vpshufd		\$0x44,$D0,$D0
+	vmovdqa		$D4,-0x60(%r11)
+	vmovdqa		$D0,0x30(%rsp)
+	vpshufd		\$0xEE,$D1,$D3
+	vmovdqu		`16*6-64`($ctx),$D0
+	vpshufd		\$0x44,$D1,$D1
+	vmovdqa		$D3,-0x50(%r11)
+	vmovdqa		$D1,0x40(%rsp)
+	vpshufd		\$0xEE,$D2,$D4
+	vmovdqu		`16*7-64`($ctx),$D1
+	vpshufd		\$0x44,$D2,$D2
+	vmovdqa		$D4,-0x40(%r11)
+	vmovdqa		$D2,0x50(%rsp)
+	vpshufd		\$0xEE,$D0,$D3
+	vmovdqu		`16*8-64`($ctx),$D2
+	vpshufd		\$0x44,$D0,$D0
+	vmovdqa		$D3,-0x30(%r11)
+	vmovdqa		$D0,0x60(%rsp)
+	vpshufd		\$0xEE,$D1,$D4
+	vpshufd		\$0x44,$D1,$D1
+	vmovdqa		$D4,-0x20(%r11)
+	vmovdqa		$D1,0x70(%rsp)
+	vpshufd		\$0xEE,$D2,$D3
+	 vmovdqa	0x00(%rsp),$D4		# preload r0^2
+	vpshufd		\$0x44,$D2,$D2
+	vmovdqa		$D3,-0x10(%r11)
+	vmovdqa		$D2,0x80(%rsp)
+
+	jmp		.Loop_avx
+
+.align	32
+.Loop_avx:
+	################################################################
+	# ((inp[0]*r^4+inp[2]*r^2+inp[4])*r^4+inp[6]*r^2
+	# ((inp[1]*r^4+inp[3]*r^2+inp[5])*r^3+inp[7]*r
+	#   \___________________/
+	# ((inp[0]*r^4+inp[2]*r^2+inp[4])*r^4+inp[6]*r^2+inp[8])*r^2
+	# ((inp[1]*r^4+inp[3]*r^2+inp[5])*r^4+inp[7]*r^2+inp[9])*r
+	#   \___________________/ \____________________/
+	#
+	# Note that we start with inp[2:3]*r^2. This is because it
+	# doesn't depend on reduction in previous iteration.
+	################################################################
+	# d4 = h4*r0 + h3*r1   + h2*r2   + h1*r3   + h0*r4
+	# d3 = h3*r0 + h2*r1   + h1*r2   + h0*r3   + h4*5*r4
+	# d2 = h2*r0 + h1*r1   + h0*r2   + h4*5*r3 + h3*5*r4
+	# d1 = h1*r0 + h0*r1   + h4*5*r2 + h3*5*r3 + h2*5*r4
+	# d0 = h0*r0 + h4*5*r1 + h3*5*r2 + h2*5*r3 + h1*5*r4
+	#
+	# though note that $Tx and $Hx are "reversed" in this section,
+	# and $D4 is preloaded with r0^2...
+
+	vpmuludq	$T0,$D4,$D0		# d0 = h0*r0
+	vpmuludq	$T1,$D4,$D1		# d1 = h1*r0
+	  vmovdqa	$H2,0x20(%r11)				# offload hash
+	vpmuludq	$T2,$D4,$D2		# d3 = h2*r0
+	 vmovdqa	0x10(%rsp),$H2		# r1^2
+	vpmuludq	$T3,$D4,$D3		# d3 = h3*r0
+	vpmuludq	$T4,$D4,$D4		# d4 = h4*r0
+
+	  vmovdqa	$H0,0x00(%r11)				#
+	vpmuludq	0x20(%rsp),$T4,$H0	# h4*s1
+	  vmovdqa	$H1,0x10(%r11)				#
+	vpmuludq	$T3,$H2,$H1		# h3*r1
+	vpaddq		$H0,$D0,$D0		# d0 += h4*s1
+	vpaddq		$H1,$D4,$D4		# d4 += h3*r1
+	  vmovdqa	$H3,0x30(%r11)				#
+	vpmuludq	$T2,$H2,$H0		# h2*r1
+	vpmuludq	$T1,$H2,$H1		# h1*r1
+	vpaddq		$H0,$D3,$D3		# d3 += h2*r1
+	 vmovdqa	0x30(%rsp),$H3		# r2^2
+	vpaddq		$H1,$D2,$D2		# d2 += h1*r1
+	  vmovdqa	$H4,0x40(%r11)				#
+	vpmuludq	$T0,$H2,$H2		# h0*r1
+	 vpmuludq	$T2,$H3,$H0		# h2*r2
+	vpaddq		$H2,$D1,$D1		# d1 += h0*r1
+
+	 vmovdqa	0x40(%rsp),$H4		# s2^2
+	vpaddq		$H0,$D4,$D4		# d4 += h2*r2
+	vpmuludq	$T1,$H3,$H1		# h1*r2
+	vpmuludq	$T0,$H3,$H3		# h0*r2
+	vpaddq		$H1,$D3,$D3		# d3 += h1*r2
+	 vmovdqa	0x50(%rsp),$H2		# r3^2
+	vpaddq		$H3,$D2,$D2		# d2 += h0*r2
+	vpmuludq	$T4,$H4,$H0		# h4*s2
+	vpmuludq	$T3,$H4,$H4		# h3*s2
+	vpaddq		$H0,$D1,$D1		# d1 += h4*s2
+	 vmovdqa	0x60(%rsp),$H3		# s3^2
+	vpaddq		$H4,$D0,$D0		# d0 += h3*s2
+
+	 vmovdqa	0x80(%rsp),$H4		# s4^2
+	vpmuludq	$T1,$H2,$H1		# h1*r3
+	vpmuludq	$T0,$H2,$H2		# h0*r3
+	vpaddq		$H1,$D4,$D4		# d4 += h1*r3
+	vpaddq		$H2,$D3,$D3		# d3 += h0*r3
+	vpmuludq	$T4,$H3,$H0		# h4*s3
+	vpmuludq	$T3,$H3,$H1		# h3*s3
+	vpaddq		$H0,$D2,$D2		# d2 += h4*s3
+	 vmovdqu	16*0($inp),$H0				# load input
+	vpaddq		$H1,$D1,$D1		# d1 += h3*s3
+	vpmuludq	$T2,$H3,$H3		# h2*s3
+	 vpmuludq	$T2,$H4,$T2		# h2*s4
+	vpaddq		$H3,$D0,$D0		# d0 += h2*s3
+
+	 vmovdqu	16*1($inp),$H1				#
+	vpaddq		$T2,$D1,$D1		# d1 += h2*s4
+	vpmuludq	$T3,$H4,$T3		# h3*s4
+	vpmuludq	$T4,$H4,$T4		# h4*s4
+	 vpsrldq	\$6,$H0,$H2				# splat input
+	vpaddq		$T3,$D2,$D2		# d2 += h3*s4
+	vpaddq		$T4,$D3,$D3		# d3 += h4*s4
+	 vpsrldq	\$6,$H1,$H3				#
+	vpmuludq	0x70(%rsp),$T0,$T4	# h0*r4
+	vpmuludq	$T1,$H4,$T0		# h1*s4
+	 vpunpckhqdq	$H1,$H0,$H4		# 4
+	vpaddq		$T4,$D4,$D4		# d4 += h0*r4
+	 vmovdqa	-0x90(%r11),$T4		# r0^4
+	vpaddq		$T0,$D0,$D0		# d0 += h1*s4
+
+	vpunpcklqdq	$H1,$H0,$H0		# 0:1
+	vpunpcklqdq	$H3,$H2,$H3		# 2:3
+
+	#vpsrlq		\$40,$H4,$H4		# 4
+	vpsrldq		\$`40/8`,$H4,$H4	# 4
+	vpsrlq		\$26,$H0,$H1
+	vpand		$MASK,$H0,$H0		# 0
+	vpsrlq		\$4,$H3,$H2
+	vpand		$MASK,$H1,$H1		# 1
+	vpand		0(%rcx),$H4,$H4		# .Lmask24
+	vpsrlq		\$30,$H3,$H3
+	vpand		$MASK,$H2,$H2		# 2
+	vpand		$MASK,$H3,$H3		# 3
+	vpor		32(%rcx),$H4,$H4	# padbit, yes, always
+
+	vpaddq		0x00(%r11),$H0,$H0	# add hash value
+	vpaddq		0x10(%r11),$H1,$H1
+	vpaddq		0x20(%r11),$H2,$H2
+	vpaddq		0x30(%r11),$H3,$H3
+	vpaddq		0x40(%r11),$H4,$H4
+
+	lea		16*2($inp),%rax
+	lea		16*4($inp),$inp
+	sub		\$64,$len
+	cmovc		%rax,$inp
+
+	################################################################
+	# Now we accumulate (inp[0:1]+hash)*r^4
+	################################################################
+	# d4 = h4*r0 + h3*r1   + h2*r2   + h1*r3   + h0*r4
+	# d3 = h3*r0 + h2*r1   + h1*r2   + h0*r3   + h4*5*r4
+	# d2 = h2*r0 + h1*r1   + h0*r2   + h4*5*r3 + h3*5*r4
+	# d1 = h1*r0 + h0*r1   + h4*5*r2 + h3*5*r3 + h2*5*r4
+	# d0 = h0*r0 + h4*5*r1 + h3*5*r2 + h2*5*r3 + h1*5*r4
+
+	vpmuludq	$H0,$T4,$T0		# h0*r0
+	vpmuludq	$H1,$T4,$T1		# h1*r0
+	vpaddq		$T0,$D0,$D0
+	vpaddq		$T1,$D1,$D1
+	 vmovdqa	-0x80(%r11),$T2		# r1^4
+	vpmuludq	$H2,$T4,$T0		# h2*r0
+	vpmuludq	$H3,$T4,$T1		# h3*r0
+	vpaddq		$T0,$D2,$D2
+	vpaddq		$T1,$D3,$D3
+	vpmuludq	$H4,$T4,$T4		# h4*r0
+	 vpmuludq	-0x70(%r11),$H4,$T0	# h4*s1
+	vpaddq		$T4,$D4,$D4
+
+	vpaddq		$T0,$D0,$D0		# d0 += h4*s1
+	vpmuludq	$H2,$T2,$T1		# h2*r1
+	vpmuludq	$H3,$T2,$T0		# h3*r1
+	vpaddq		$T1,$D3,$D3		# d3 += h2*r1
+	 vmovdqa	-0x60(%r11),$T3		# r2^4
+	vpaddq		$T0,$D4,$D4		# d4 += h3*r1
+	vpmuludq	$H1,$T2,$T1		# h1*r1
+	vpmuludq	$H0,$T2,$T2		# h0*r1
+	vpaddq		$T1,$D2,$D2		# d2 += h1*r1
+	vpaddq		$T2,$D1,$D1		# d1 += h0*r1
+
+	 vmovdqa	-0x50(%r11),$T4		# s2^4
+	vpmuludq	$H2,$T3,$T0		# h2*r2
+	vpmuludq	$H1,$T3,$T1		# h1*r2
+	vpaddq		$T0,$D4,$D4		# d4 += h2*r2
+	vpaddq		$T1,$D3,$D3		# d3 += h1*r2
+	 vmovdqa	-0x40(%r11),$T2		# r3^4
+	vpmuludq	$H0,$T3,$T3		# h0*r2
+	vpmuludq	$H4,$T4,$T0		# h4*s2
+	vpaddq		$T3,$D2,$D2		# d2 += h0*r2
+	vpaddq		$T0,$D1,$D1		# d1 += h4*s2
+	 vmovdqa	-0x30(%r11),$T3		# s3^4
+	vpmuludq	$H3,$T4,$T4		# h3*s2
+	 vpmuludq	$H1,$T2,$T1		# h1*r3
+	vpaddq		$T4,$D0,$D0		# d0 += h3*s2
+
+	 vmovdqa	-0x10(%r11),$T4		# s4^4
+	vpaddq		$T1,$D4,$D4		# d4 += h1*r3
+	vpmuludq	$H0,$T2,$T2		# h0*r3
+	vpmuludq	$H4,$T3,$T0		# h4*s3
+	vpaddq		$T2,$D3,$D3		# d3 += h0*r3
+	vpaddq		$T0,$D2,$D2		# d2 += h4*s3
+	 vmovdqu	16*2($inp),$T0				# load input
+	vpmuludq	$H3,$T3,$T2		# h3*s3
+	vpmuludq	$H2,$T3,$T3		# h2*s3
+	vpaddq		$T2,$D1,$D1		# d1 += h3*s3
+	 vmovdqu	16*3($inp),$T1				#
+	vpaddq		$T3,$D0,$D0		# d0 += h2*s3
+
+	vpmuludq	$H2,$T4,$H2		# h2*s4
+	vpmuludq	$H3,$T4,$H3		# h3*s4
+	 vpsrldq	\$6,$T0,$T2				# splat input
+	vpaddq		$H2,$D1,$D1		# d1 += h2*s4
+	vpmuludq	$H4,$T4,$H4		# h4*s4
+	 vpsrldq	\$6,$T1,$T3				#
+	vpaddq		$H3,$D2,$H2		# h2 = d2 + h3*s4
+	vpaddq		$H4,$D3,$H3		# h3 = d3 + h4*s4
+	vpmuludq	-0x20(%r11),$H0,$H4	# h0*r4
+	vpmuludq	$H1,$T4,$H0
+	 vpunpckhqdq	$T1,$T0,$T4		# 4
+	vpaddq		$H4,$D4,$H4		# h4 = d4 + h0*r4
+	vpaddq		$H0,$D0,$H0		# h0 = d0 + h1*s4
+
+	vpunpcklqdq	$T1,$T0,$T0		# 0:1
+	vpunpcklqdq	$T3,$T2,$T3		# 2:3
+
+	#vpsrlq		\$40,$T4,$T4		# 4
+	vpsrldq		\$`40/8`,$T4,$T4	# 4
+	vpsrlq		\$26,$T0,$T1
+	 vmovdqa	0x00(%rsp),$D4		# preload r0^2
+	vpand		$MASK,$T0,$T0		# 0
+	vpsrlq		\$4,$T3,$T2
+	vpand		$MASK,$T1,$T1		# 1
+	vpand		0(%rcx),$T4,$T4		# .Lmask24
+	vpsrlq		\$30,$T3,$T3
+	vpand		$MASK,$T2,$T2		# 2
+	vpand		$MASK,$T3,$T3		# 3
+	vpor		32(%rcx),$T4,$T4	# padbit, yes, always
+
+	################################################################
+	# lazy reduction as discussed in "NEON crypto" by D.J. Bernstein
+	# and P. Schwabe
+
+	vpsrlq		\$26,$H3,$D3
+	vpand		$MASK,$H3,$H3
+	vpaddq		$D3,$H4,$H4		# h3 -> h4
+
+	vpsrlq		\$26,$H0,$D0
+	vpand		$MASK,$H0,$H0
+	vpaddq		$D0,$D1,$H1		# h0 -> h1
+
+	vpsrlq		\$26,$H4,$D0
+	vpand		$MASK,$H4,$H4
+
+	vpsrlq		\$26,$H1,$D1
+	vpand		$MASK,$H1,$H1
+	vpaddq		$D1,$H2,$H2		# h1 -> h2
+
+	vpaddq		$D0,$H0,$H0
+	vpsllq		\$2,$D0,$D0
+	vpaddq		$D0,$H0,$H0		# h4 -> h0
+
+	vpsrlq		\$26,$H2,$D2
+	vpand		$MASK,$H2,$H2
+	vpaddq		$D2,$H3,$H3		# h2 -> h3
+
+	vpsrlq		\$26,$H0,$D0
+	vpand		$MASK,$H0,$H0
+	vpaddq		$D0,$H1,$H1		# h0 -> h1
+
+	vpsrlq		\$26,$H3,$D3
+	vpand		$MASK,$H3,$H3
+	vpaddq		$D3,$H4,$H4		# h3 -> h4
+
+	ja		.Loop_avx
+
+.Lskip_loop_avx:
+	################################################################
+	# multiply (inp[0:1]+hash) or inp[2:3] by r^2:r^1
+
+	vpshufd		\$0x10,$D4,$D4		# r0^n, xx12 -> x1x2
+	add		\$32,$len
+	jnz		.Long_tail_avx
+
+	vpaddq		$H2,$T2,$T2
+	vpaddq		$H0,$T0,$T0
+	vpaddq		$H1,$T1,$T1
+	vpaddq		$H3,$T3,$T3
+	vpaddq		$H4,$T4,$T4
+
+.Long_tail_avx:
+	vmovdqa		$H2,0x20(%r11)
+	vmovdqa		$H0,0x00(%r11)
+	vmovdqa		$H1,0x10(%r11)
+	vmovdqa		$H3,0x30(%r11)
+	vmovdqa		$H4,0x40(%r11)
+
+	# d4 = h4*r0 + h3*r1   + h2*r2   + h1*r3   + h0*r4
+	# d3 = h3*r0 + h2*r1   + h1*r2   + h0*r3   + h4*5*r4
+	# d2 = h2*r0 + h1*r1   + h0*r2   + h4*5*r3 + h3*5*r4
+	# d1 = h1*r0 + h0*r1   + h4*5*r2 + h3*5*r3 + h2*5*r4
+	# d0 = h0*r0 + h4*5*r1 + h3*5*r2 + h2*5*r3 + h1*5*r4
+
+	vpmuludq	$T2,$D4,$D2		# d2 = h2*r0
+	vpmuludq	$T0,$D4,$D0		# d0 = h0*r0
+	 vpshufd	\$0x10,`16*1-64`($ctx),$H2		# r1^n
+	vpmuludq	$T1,$D4,$D1		# d1 = h1*r0
+	vpmuludq	$T3,$D4,$D3		# d3 = h3*r0
+	vpmuludq	$T4,$D4,$D4		# d4 = h4*r0
+
+	vpmuludq	$T3,$H2,$H0		# h3*r1
+	vpaddq		$H0,$D4,$D4		# d4 += h3*r1
+	 vpshufd	\$0x10,`16*2-64`($ctx),$H3		# s1^n
+	vpmuludq	$T2,$H2,$H1		# h2*r1
+	vpaddq		$H1,$D3,$D3		# d3 += h2*r1
+	 vpshufd	\$0x10,`16*3-64`($ctx),$H4		# r2^n
+	vpmuludq	$T1,$H2,$H0		# h1*r1
+	vpaddq		$H0,$D2,$D2		# d2 += h1*r1
+	vpmuludq	$T0,$H2,$H2		# h0*r1
+	vpaddq		$H2,$D1,$D1		# d1 += h0*r1
+	vpmuludq	$T4,$H3,$H3		# h4*s1
+	vpaddq		$H3,$D0,$D0		# d0 += h4*s1
+
+	 vpshufd	\$0x10,`16*4-64`($ctx),$H2		# s2^n
+	vpmuludq	$T2,$H4,$H1		# h2*r2
+	vpaddq		$H1,$D4,$D4		# d4 += h2*r2
+	vpmuludq	$T1,$H4,$H0		# h1*r2
+	vpaddq		$H0,$D3,$D3		# d3 += h1*r2
+	 vpshufd	\$0x10,`16*5-64`($ctx),$H3		# r3^n
+	vpmuludq	$T0,$H4,$H4		# h0*r2
+	vpaddq		$H4,$D2,$D2		# d2 += h0*r2
+	vpmuludq	$T4,$H2,$H1		# h4*s2
+	vpaddq		$H1,$D1,$D1		# d1 += h4*s2
+	 vpshufd	\$0x10,`16*6-64`($ctx),$H4		# s3^n
+	vpmuludq	$T3,$H2,$H2		# h3*s2
+	vpaddq		$H2,$D0,$D0		# d0 += h3*s2
+
+	vpmuludq	$T1,$H3,$H0		# h1*r3
+	vpaddq		$H0,$D4,$D4		# d4 += h1*r3
+	vpmuludq	$T0,$H3,$H3		# h0*r3
+	vpaddq		$H3,$D3,$D3		# d3 += h0*r3
+	 vpshufd	\$0x10,`16*7-64`($ctx),$H2		# r4^n
+	vpmuludq	$T4,$H4,$H1		# h4*s3
+	vpaddq		$H1,$D2,$D2		# d2 += h4*s3
+	 vpshufd	\$0x10,`16*8-64`($ctx),$H3		# s4^n
+	vpmuludq	$T3,$H4,$H0		# h3*s3
+	vpaddq		$H0,$D1,$D1		# d1 += h3*s3
+	vpmuludq	$T2,$H4,$H4		# h2*s3
+	vpaddq		$H4,$D0,$D0		# d0 += h2*s3
+
+	vpmuludq	$T0,$H2,$H2		# h0*r4
+	vpaddq		$H2,$D4,$D4		# h4 = d4 + h0*r4
+	vpmuludq	$T4,$H3,$H1		# h4*s4
+	vpaddq		$H1,$D3,$D3		# h3 = d3 + h4*s4
+	vpmuludq	$T3,$H3,$H0		# h3*s4
+	vpaddq		$H0,$D2,$D2		# h2 = d2 + h3*s4
+	vpmuludq	$T2,$H3,$H1		# h2*s4
+	vpaddq		$H1,$D1,$D1		# h1 = d1 + h2*s4
+	vpmuludq	$T1,$H3,$H3		# h1*s4
+	vpaddq		$H3,$D0,$D0		# h0 = d0 + h1*s4
+
+	jz		.Lshort_tail_avx
+
+	vmovdqu		16*0($inp),$H0		# load input
+	vmovdqu		16*1($inp),$H1
+
+	vpsrldq		\$6,$H0,$H2		# splat input
+	vpsrldq		\$6,$H1,$H3
+	vpunpckhqdq	$H1,$H0,$H4		# 4
+	vpunpcklqdq	$H1,$H0,$H0		# 0:1
+	vpunpcklqdq	$H3,$H2,$H3		# 2:3
+
+	vpsrlq		\$40,$H4,$H4		# 4
+	vpsrlq		\$26,$H0,$H1
+	vpand		$MASK,$H0,$H0		# 0
+	vpsrlq		\$4,$H3,$H2
+	vpand		$MASK,$H1,$H1		# 1
+	vpsrlq		\$30,$H3,$H3
+	vpand		$MASK,$H2,$H2		# 2
+	vpand		$MASK,$H3,$H3		# 3
+	vpor		32(%rcx),$H4,$H4	# padbit, yes, always
+
+	vpshufd		\$0x32,`16*0-64`($ctx),$T4	# r0^n, 34xx -> x3x4
+	vpaddq		0x00(%r11),$H0,$H0
+	vpaddq		0x10(%r11),$H1,$H1
+	vpaddq		0x20(%r11),$H2,$H2
+	vpaddq		0x30(%r11),$H3,$H3
+	vpaddq		0x40(%r11),$H4,$H4
+
+	################################################################
+	# multiply (inp[0:1]+hash) by r^4:r^3 and accumulate
+
+	vpmuludq	$H0,$T4,$T0		# h0*r0
+	vpaddq		$T0,$D0,$D0		# d0 += h0*r0
+	vpmuludq	$H1,$T4,$T1		# h1*r0
+	vpaddq		$T1,$D1,$D1		# d1 += h1*r0
+	vpmuludq	$H2,$T4,$T0		# h2*r0
+	vpaddq		$T0,$D2,$D2		# d2 += h2*r0
+	 vpshufd	\$0x32,`16*1-64`($ctx),$T2		# r1^n
+	vpmuludq	$H3,$T4,$T1		# h3*r0
+	vpaddq		$T1,$D3,$D3		# d3 += h3*r0
+	vpmuludq	$H4,$T4,$T4		# h4*r0
+	vpaddq		$T4,$D4,$D4		# d4 += h4*r0
+
+	vpmuludq	$H3,$T2,$T0		# h3*r1
+	vpaddq		$T0,$D4,$D4		# d4 += h3*r1
+	 vpshufd	\$0x32,`16*2-64`($ctx),$T3		# s1
+	vpmuludq	$H2,$T2,$T1		# h2*r1
+	vpaddq		$T1,$D3,$D3		# d3 += h2*r1
+	 vpshufd	\$0x32,`16*3-64`($ctx),$T4		# r2
+	vpmuludq	$H1,$T2,$T0		# h1*r1
+	vpaddq		$T0,$D2,$D2		# d2 += h1*r1
+	vpmuludq	$H0,$T2,$T2		# h0*r1
+	vpaddq		$T2,$D1,$D1		# d1 += h0*r1
+	vpmuludq	$H4,$T3,$T3		# h4*s1
+	vpaddq		$T3,$D0,$D0		# d0 += h4*s1
+
+	 vpshufd	\$0x32,`16*4-64`($ctx),$T2		# s2
+	vpmuludq	$H2,$T4,$T1		# h2*r2
+	vpaddq		$T1,$D4,$D4		# d4 += h2*r2
+	vpmuludq	$H1,$T4,$T0		# h1*r2
+	vpaddq		$T0,$D3,$D3		# d3 += h1*r2
+	 vpshufd	\$0x32,`16*5-64`($ctx),$T3		# r3
+	vpmuludq	$H0,$T4,$T4		# h0*r2
+	vpaddq		$T4,$D2,$D2		# d2 += h0*r2
+	vpmuludq	$H4,$T2,$T1		# h4*s2
+	vpaddq		$T1,$D1,$D1		# d1 += h4*s2
+	 vpshufd	\$0x32,`16*6-64`($ctx),$T4		# s3
+	vpmuludq	$H3,$T2,$T2		# h3*s2
+	vpaddq		$T2,$D0,$D0		# d0 += h3*s2
+
+	vpmuludq	$H1,$T3,$T0		# h1*r3
+	vpaddq		$T0,$D4,$D4		# d4 += h1*r3
+	vpmuludq	$H0,$T3,$T3		# h0*r3
+	vpaddq		$T3,$D3,$D3		# d3 += h0*r3
+	 vpshufd	\$0x32,`16*7-64`($ctx),$T2		# r4
+	vpmuludq	$H4,$T4,$T1		# h4*s3
+	vpaddq		$T1,$D2,$D2		# d2 += h4*s3
+	 vpshufd	\$0x32,`16*8-64`($ctx),$T3		# s4
+	vpmuludq	$H3,$T4,$T0		# h3*s3
+	vpaddq		$T0,$D1,$D1		# d1 += h3*s3
+	vpmuludq	$H2,$T4,$T4		# h2*s3
+	vpaddq		$T4,$D0,$D0		# d0 += h2*s3
+
+	vpmuludq	$H0,$T2,$T2		# h0*r4
+	vpaddq		$T2,$D4,$D4		# d4 += h0*r4
+	vpmuludq	$H4,$T3,$T1		# h4*s4
+	vpaddq		$T1,$D3,$D3		# d3 += h4*s4
+	vpmuludq	$H3,$T3,$T0		# h3*s4
+	vpaddq		$T0,$D2,$D2		# d2 += h3*s4
+	vpmuludq	$H2,$T3,$T1		# h2*s4
+	vpaddq		$T1,$D1,$D1		# d1 += h2*s4
+	vpmuludq	$H1,$T3,$T3		# h1*s4
+	vpaddq		$T3,$D0,$D0		# d0 += h1*s4
+
+.Lshort_tail_avx:
+	################################################################
+	# horizontal addition
+
+	vpsrldq		\$8,$D4,$T4
+	vpsrldq		\$8,$D3,$T3
+	vpsrldq		\$8,$D1,$T1
+	vpsrldq		\$8,$D0,$T0
+	vpsrldq		\$8,$D2,$T2
+	vpaddq		$T3,$D3,$D3
+	vpaddq		$T4,$D4,$D4
+	vpaddq		$T0,$D0,$D0
+	vpaddq		$T1,$D1,$D1
+	vpaddq		$T2,$D2,$D2
+
+	################################################################
+	# lazy reduction
+
+	vpsrlq		\$26,$D3,$H3
+	vpand		$MASK,$D3,$D3
+	vpaddq		$H3,$D4,$D4		# h3 -> h4
+
+	vpsrlq		\$26,$D0,$H0
+	vpand		$MASK,$D0,$D0
+	vpaddq		$H0,$D1,$D1		# h0 -> h1
+
+	vpsrlq		\$26,$D4,$H4
+	vpand		$MASK,$D4,$D4
+
+	vpsrlq		\$26,$D1,$H1
+	vpand		$MASK,$D1,$D1
+	vpaddq		$H1,$D2,$D2		# h1 -> h2
+
+	vpaddq		$H4,$D0,$D0
+	vpsllq		\$2,$H4,$H4
+	vpaddq		$H4,$D0,$D0		# h4 -> h0
+
+	vpsrlq		\$26,$D2,$H2
+	vpand		$MASK,$D2,$D2
+	vpaddq		$H2,$D3,$D3		# h2 -> h3
+
+	vpsrlq		\$26,$D0,$H0
+	vpand		$MASK,$D0,$D0
+	vpaddq		$H0,$D1,$D1		# h0 -> h1
+
+	vpsrlq		\$26,$D3,$H3
+	vpand		$MASK,$D3,$D3
+	vpaddq		$H3,$D4,$D4		# h3 -> h4
+
+	vmovd		$D0,`4*0-48-64`($ctx)	# save partially reduced
+	vmovd		$D1,`4*1-48-64`($ctx)
+	vmovd		$D2,`4*2-48-64`($ctx)
+	vmovd		$D3,`4*3-48-64`($ctx)
+	vmovd		$D4,`4*4-48-64`($ctx)
+___
+$code.=<<___	if ($win64);
+	vmovdqa		0x50(%r11),%xmm6
+	vmovdqa		0x60(%r11),%xmm7
+	vmovdqa		0x70(%r11),%xmm8
+	vmovdqa		0x80(%r11),%xmm9
+	vmovdqa		0x90(%r11),%xmm10
+	vmovdqa		0xa0(%r11),%xmm11
+	vmovdqa		0xb0(%r11),%xmm12
+	vmovdqa		0xc0(%r11),%xmm13
+	vmovdqa		0xd0(%r11),%xmm14
+	vmovdqa		0xe0(%r11),%xmm15
+	lea		0xf8(%r11),%rsp
+.Ldo_avx_epilogue:
+___
+$code.=<<___	if (!$win64);
+	lea		0x58(%r11),%rsp
+___
+$code.=<<___;
+	vzeroupper
+	ret
+.size	poly1305_blocks_avx,.-poly1305_blocks_avx
+
+.type	poly1305_emit_avx,\@function,3
+.align	32
+poly1305_emit_avx:
+	cmpl	\$0,20($ctx)	# is_base2_26?
+	je	.Lemit
+
+	mov	0($ctx),%eax	# load hash value base 2^26
+	mov	4($ctx),%ecx
+	mov	8($ctx),%r8d
+	mov	12($ctx),%r11d
+	mov	16($ctx),%r10d
+
+	shl	\$26,%rcx	# base 2^26 -> base 2^64
+	mov	%r8,%r9
+	shl	\$52,%r8
+	add	%rcx,%rax
+	shr	\$12,%r9
+	add	%rax,%r8	# h0
+	adc	\$0,%r9
+
+	shl	\$14,%r11
+	mov	%r10,%rax
+	shr	\$24,%r10
+	add	%r11,%r9
+	shl	\$40,%rax
+	add	%rax,%r9	# h1
+	adc	\$0,%r10	# h2
+
+	mov	%r10,%rax	# could be partially reduced, so reduce
+	mov	%r10,%rcx
+	and	\$3,%r10
+	shr	\$2,%rax
+	and	\$-4,%rcx
+	add	%rcx,%rax
+	add	%rax,%r8
+	adc	\$0,%r9
+	adc	\$0,%r10
+
+	mov	%r8,%rax
+	add	\$5,%r8		# compare to modulus
+	mov	%r9,%rcx
+	adc	\$0,%r9
+	adc	\$0,%r10
+	shr	\$2,%r10	# did 130-bit value overfow?
+	cmovnz	%r8,%rax
+	cmovnz	%r9,%rcx
+
+	add	0($nonce),%rax	# accumulate nonce
+	adc	8($nonce),%rcx
+	mov	%rax,0($mac)	# write result
+	mov	%rcx,8($mac)
+
+	ret
+.size	poly1305_emit_avx,.-poly1305_emit_avx
+___
+
+if ($avx>1) {
+my ($H0,$H1,$H2,$H3,$H4, $MASK, $T4,$T0,$T1,$T2,$T3, $D0,$D1,$D2,$D3,$D4) =
+    map("%ymm$_",(0..15));
+my $S4=$MASK;
+
+$code.=<<___;
+.type	poly1305_blocks_avx2,\@function,4
+.align	32
+poly1305_blocks_avx2:
+	mov	20($ctx),%r8d		# is_base2_26
+	cmp	\$128,$len
+	jae	.Lblocks_avx2
+	test	%r8d,%r8d
+	jz	.Lblocks
+
+.Lblocks_avx2:
+	and	\$-16,$len
+	jz	.Lno_data_avx2
+
+	vzeroupper
+
+	test	%r8d,%r8d
+	jz	.Lbase2_64_avx2
+
+	test	\$63,$len
+	jz	.Leven_avx2
+
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+.Lblocks_avx2_body:
+
+	mov	$len,%r15		# reassign $len
+
+	mov	0($ctx),$d1		# load hash value
+	mov	8($ctx),$d2
+	mov	16($ctx),$h2#d
+
+	mov	24($ctx),$r0		# load r
+	mov	32($ctx),$s1
+
+	################################# base 2^26 -> base 2^64
+	mov	$d1#d,$h0#d
+	and	\$`-1*(1<<31)`,$d1
+	mov	$d2,$r1			# borrow $r1
+	mov	$d2#d,$h1#d
+	and	\$`-1*(1<<31)`,$d2
+
+	shr	\$6,$d1
+	shl	\$52,$r1
+	add	$d1,$h0
+	shr	\$12,$h1
+	shr	\$18,$d2
+	add	$r1,$h0
+	adc	$d2,$h1
+
+	mov	$h2,$d1
+	shl	\$40,$d1
+	shr	\$24,$h2
+	add	$d1,$h1
+	adc	\$0,$h2			# can be partially reduced...
+
+	mov	\$-4,$d2		# ... so reduce
+	mov	$h2,$d1
+	and	$h2,$d2
+	shr	\$2,$d1
+	and	\$3,$h2
+	add	$d2,$d1			# =*5
+	add	$d1,$h0
+	adc	\$0,$h1
+	adc	\$0,$h2
+
+	mov	$s1,$r1
+	mov	$s1,%rax
+	shr	\$2,$s1
+	add	$r1,$s1			# s1 = r1 + (r1 >> 2)
+
+.Lbase2_26_pre_avx2:
+	add	0($inp),$h0		# accumulate input
+	adc	8($inp),$h1
+	lea	16($inp),$inp
+	adc	$padbit,$h2
+	sub	\$16,%r15
+
+	call	__poly1305_block
+	mov	$r1,%rax
+
+	test	\$63,%r15
+	jnz	.Lbase2_26_pre_avx2
+
+	test	$padbit,$padbit		# if $padbit is zero,
+	jz	.Lstore_base2_64_avx2	# store hash in base 2^64 format
+
+	################################# base 2^64 -> base 2^26
+	mov	$h0,%rax
+	mov	$h0,%rdx
+	shr	\$52,$h0
+	mov	$h1,$r0
+	mov	$h1,$r1
+	shr	\$26,%rdx
+	and	\$0x3ffffff,%rax	# h[0]
+	shl	\$12,$r0
+	and	\$0x3ffffff,%rdx	# h[1]
+	shr	\$14,$h1
+	or	$r0,$h0
+	shl	\$24,$h2
+	and	\$0x3ffffff,$h0		# h[2]
+	shr	\$40,$r1
+	and	\$0x3ffffff,$h1		# h[3]
+	or	$r1,$h2			# h[4]
+
+	test	%r15,%r15
+	jz	.Lstore_base2_26_avx2
+
+	vmovd	%rax#d,%x#$H0
+	vmovd	%rdx#d,%x#$H1
+	vmovd	$h0#d,%x#$H2
+	vmovd	$h1#d,%x#$H3
+	vmovd	$h2#d,%x#$H4
+	jmp	.Lproceed_avx2
+
+.align	32
+.Lstore_base2_64_avx2:
+	mov	$h0,0($ctx)
+	mov	$h1,8($ctx)
+	mov	$h2,16($ctx)		# note that is_base2_26 is zeroed
+	jmp	.Ldone_avx2
+
+.align	16
+.Lstore_base2_26_avx2:
+	mov	%rax#d,0($ctx)		# store hash value base 2^26
+	mov	%rdx#d,4($ctx)
+	mov	$h0#d,8($ctx)
+	mov	$h1#d,12($ctx)
+	mov	$h2#d,16($ctx)
+.align	16
+.Ldone_avx2:
+	mov	0(%rsp),%r15
+	mov	8(%rsp),%r14
+	mov	16(%rsp),%r13
+	mov	24(%rsp),%r12
+	mov	32(%rsp),%rbp
+	mov	40(%rsp),%rbx
+	lea	48(%rsp),%rsp
+.Lno_data_avx2:
+.Lblocks_avx2_epilogue:
+	ret
+
+.align	32
+.Lbase2_64_avx2:
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+.Lbase2_64_avx2_body:
+
+	mov	$len,%r15		# reassign $len
+
+	mov	24($ctx),$r0		# load r
+	mov	32($ctx),$s1
+
+	mov	0($ctx),$h0		# load hash value
+	mov	8($ctx),$h1
+	mov	16($ctx),$h2#d
+
+	mov	$s1,$r1
+	mov	$s1,%rax
+	shr	\$2,$s1
+	add	$r1,$s1			# s1 = r1 + (r1 >> 2)
+
+	test	\$63,$len
+	jz	.Linit_avx2
+
+.Lbase2_64_pre_avx2:
+	add	0($inp),$h0		# accumulate input
+	adc	8($inp),$h1
+	lea	16($inp),$inp
+	adc	$padbit,$h2
+	sub	\$16,%r15
+
+	call	__poly1305_block
+	mov	$r1,%rax
+
+	test	\$63,%r15
+	jnz	.Lbase2_64_pre_avx2
+
+.Linit_avx2:
+	################################# base 2^64 -> base 2^26
+	mov	$h0,%rax
+	mov	$h0,%rdx
+	shr	\$52,$h0
+	mov	$h1,$d1
+	mov	$h1,$d2
+	shr	\$26,%rdx
+	and	\$0x3ffffff,%rax	# h[0]
+	shl	\$12,$d1
+	and	\$0x3ffffff,%rdx	# h[1]
+	shr	\$14,$h1
+	or	$d1,$h0
+	shl	\$24,$h2
+	and	\$0x3ffffff,$h0		# h[2]
+	shr	\$40,$d2
+	and	\$0x3ffffff,$h1		# h[3]
+	or	$d2,$h2			# h[4]
+
+	vmovd	%rax#d,%x#$H0
+	vmovd	%rdx#d,%x#$H1
+	vmovd	$h0#d,%x#$H2
+	vmovd	$h1#d,%x#$H3
+	vmovd	$h2#d,%x#$H4
+	movl	\$1,20($ctx)		# set is_base2_26
+
+	call	__poly1305_init_avx
+
+.Lproceed_avx2:
+	mov	%r15,$len
+
+	mov	0(%rsp),%r15
+	mov	8(%rsp),%r14
+	mov	16(%rsp),%r13
+	mov	24(%rsp),%r12
+	mov	32(%rsp),%rbp
+	mov	40(%rsp),%rbx
+	lea	48(%rsp),%rax
+	lea	48(%rsp),%rsp
+.Lbase2_64_avx2_epilogue:
+	jmp	.Ldo_avx2
+
+.align	32
+.Leven_avx2:
+	vmovd		4*0($ctx),%x#$H0	# load hash value base 2^26
+	vmovd		4*1($ctx),%x#$H1
+	vmovd		4*2($ctx),%x#$H2
+	vmovd		4*3($ctx),%x#$H3
+	vmovd		4*4($ctx),%x#$H4
+
+.Ldo_avx2:
+___
+$code.=<<___	if (!$win64);
+	lea		-8(%rsp),%r11
+	sub		\$0x128,%rsp
+___
+$code.=<<___	if ($win64);
+	lea		-0xf8(%rsp),%r11
+	sub		\$0x1c8,%rsp
+	vmovdqa		%xmm6,0x50(%r11)
+	vmovdqa		%xmm7,0x60(%r11)
+	vmovdqa		%xmm8,0x70(%r11)
+	vmovdqa		%xmm9,0x80(%r11)
+	vmovdqa		%xmm10,0x90(%r11)
+	vmovdqa		%xmm11,0xa0(%r11)
+	vmovdqa		%xmm12,0xb0(%r11)
+	vmovdqa		%xmm13,0xc0(%r11)
+	vmovdqa		%xmm14,0xd0(%r11)
+	vmovdqa		%xmm15,0xe0(%r11)
+.Ldo_avx2_body:
+___
+$code.=<<___;
+	lea		48+64($ctx),$ctx	# size optimization
+	lea		.Lconst(%rip),%rcx
+
+	# expand and copy pre-calculated table to stack
+	vmovdqu		`16*0-64`($ctx),%x#$T2
+	and		\$-512,%rsp
+	vmovdqu		`16*1-64`($ctx),%x#$T3
+	vmovdqu		`16*2-64`($ctx),%x#$T4
+	vmovdqu		`16*3-64`($ctx),%x#$D0
+	vmovdqu		`16*4-64`($ctx),%x#$D1
+	vmovdqu		`16*5-64`($ctx),%x#$D2
+	vmovdqu		`16*6-64`($ctx),%x#$D3
+	vpermq		\$0x15,$T2,$T2		# 00003412 -> 12343434
+	vmovdqu		`16*7-64`($ctx),%x#$D4
+	vpermq		\$0x15,$T3,$T3
+	vpshufd		\$0xc8,$T2,$T2		# 12343434 -> 14243444
+	vmovdqu		`16*8-64`($ctx),%x#$MASK
+	vpermq		\$0x15,$T4,$T4
+	vpshufd		\$0xc8,$T3,$T3
+	vmovdqa		$T2,0x00(%rsp)
+	vpermq		\$0x15,$D0,$D0
+	vpshufd		\$0xc8,$T4,$T4
+	vmovdqa		$T3,0x20(%rsp)
+	vpermq		\$0x15,$D1,$D1
+	vpshufd		\$0xc8,$D0,$D0
+	vmovdqa		$T4,0x40(%rsp)
+	vpermq		\$0x15,$D2,$D2
+	vpshufd		\$0xc8,$D1,$D1
+	vmovdqa		$D0,0x60(%rsp)
+	vpermq		\$0x15,$D3,$D3
+	vpshufd		\$0xc8,$D2,$D2
+	vmovdqa		$D1,0x80(%rsp)
+	vpermq		\$0x15,$D4,$D4
+	vpshufd		\$0xc8,$D3,$D3
+	vmovdqa		$D2,0xa0(%rsp)
+	vpermq		\$0x15,$MASK,$MASK
+	vpshufd		\$0xc8,$D4,$D4
+	vmovdqa		$D3,0xc0(%rsp)
+	vpshufd		\$0xc8,$MASK,$MASK
+	vmovdqa		$D4,0xe0(%rsp)
+	vmovdqa		$MASK,0x100(%rsp)
+	vmovdqa		64(%rcx),$MASK		# .Lmask26
+
+	################################################################
+	# load input
+	vmovdqu		16*0($inp),%x#$T0
+	vmovdqu		16*1($inp),%x#$T1
+	vinserti128	\$1,16*2($inp),$T0,$T0
+	vinserti128	\$1,16*3($inp),$T1,$T1
+	lea		16*4($inp),$inp
+
+	vpsrldq		\$6,$T0,$T2		# splat input
+	vpsrldq		\$6,$T1,$T3
+	vpunpckhqdq	$T1,$T0,$T4		# 4
+	vpunpcklqdq	$T3,$T2,$T2		# 2:3
+	vpunpcklqdq	$T1,$T0,$T0		# 0:1
+
+	vpsrlq		\$30,$T2,$T3
+	vpsrlq		\$4,$T2,$T2
+	vpsrlq		\$26,$T0,$T1
+	vpsrlq		\$40,$T4,$T4		# 4
+	vpand		$MASK,$T2,$T2		# 2
+	vpand		$MASK,$T0,$T0		# 0
+	vpand		$MASK,$T1,$T1		# 1
+	vpand		$MASK,$T3,$T3		# 3
+	vpor		32(%rcx),$T4,$T4	# padbit, yes, always
+
+	lea		0x90(%rsp),%rax		# size optimization
+	vpaddq		$H2,$T2,$H2		# accumulate input
+	sub		\$64,$len
+	jz		.Ltail_avx2
+	jmp		.Loop_avx2
+
+.align	32
+.Loop_avx2:
+	################################################################
+	# ((inp[0]*r^4+r[4])*r^4+r[8])*r^4
+	# ((inp[1]*r^4+r[5])*r^4+r[9])*r^3
+	# ((inp[2]*r^4+r[6])*r^4+r[10])*r^2
+	# ((inp[3]*r^4+r[7])*r^4+r[11])*r^1
+	#   \________/\________/
+	################################################################
+	#vpaddq		$H2,$T2,$H2		# accumulate input
+	vpaddq		$H0,$T0,$H0
+	vmovdqa		`32*0`(%rsp),$T0	# r0^4
+	vpaddq		$H1,$T1,$H1
+	vmovdqa		`32*1`(%rsp),$T1	# r1^4
+	vpaddq		$H3,$T3,$H3
+	vmovdqa		`32*3`(%rsp),$T2	# r2^4
+	vpaddq		$H4,$T4,$H4
+	vmovdqa		`32*6-0x90`(%rax),$T3	# s3^4
+	vmovdqa		`32*8-0x90`(%rax),$S4	# s4^4
+
+	# d4 = h4*r0 + h3*r1   + h2*r2   + h1*r3   + h0*r4
+	# d3 = h3*r0 + h2*r1   + h1*r2   + h0*r3   + h4*5*r4
+	# d2 = h2*r0 + h1*r1   + h0*r2   + h4*5*r3 + h3*5*r4
+	# d1 = h1*r0 + h0*r1   + h4*5*r2 + h3*5*r3 + h2*5*r4
+	# d0 = h0*r0 + h4*5*r1 + h3*5*r2 + h2*5*r3 + h1*5*r4
+	#
+	# however, as h2 is "chronologically" first one available pull
+	# corresponding operations up, so it's
+	#
+	# d4 = h2*r2   + h4*r0 + h3*r1             + h1*r3   + h0*r4
+	# d3 = h2*r1   + h3*r0           + h1*r2   + h0*r3   + h4*5*r4
+	# d2 = h2*r0           + h1*r1   + h0*r2   + h4*5*r3 + h3*5*r4
+	# d1 = h2*5*r4 + h1*r0 + h0*r1   + h4*5*r2 + h3*5*r3
+	# d0 = h2*5*r3 + h0*r0 + h4*5*r1 + h3*5*r2           + h1*5*r4
+
+	vpmuludq	$H2,$T0,$D2		# d2 = h2*r0
+	vpmuludq	$H2,$T1,$D3		# d3 = h2*r1
+	vpmuludq	$H2,$T2,$D4		# d4 = h2*r2
+	vpmuludq	$H2,$T3,$D0		# d0 = h2*s3
+	vpmuludq	$H2,$S4,$D1		# d1 = h2*s4
+
+	vpmuludq	$H0,$T1,$T4		# h0*r1
+	vpmuludq	$H1,$T1,$H2		# h1*r1, borrow $H2 as temp
+	vpaddq		$T4,$D1,$D1		# d1 += h0*r1
+	vpaddq		$H2,$D2,$D2		# d2 += h1*r1
+	vpmuludq	$H3,$T1,$T4		# h3*r1
+	vpmuludq	`32*2`(%rsp),$H4,$H2	# h4*s1
+	vpaddq		$T4,$D4,$D4		# d4 += h3*r1
+	vpaddq		$H2,$D0,$D0		# d0 += h4*s1
+	 vmovdqa	`32*4-0x90`(%rax),$T1	# s2
+
+	vpmuludq	$H0,$T0,$T4		# h0*r0
+	vpmuludq	$H1,$T0,$H2		# h1*r0
+	vpaddq		$T4,$D0,$D0		# d0 += h0*r0
+	vpaddq		$H2,$D1,$D1		# d1 += h1*r0
+	vpmuludq	$H3,$T0,$T4		# h3*r0
+	vpmuludq	$H4,$T0,$H2		# h4*r0
+	 vmovdqu	16*0($inp),%x#$T0	# load input
+	vpaddq		$T4,$D3,$D3		# d3 += h3*r0
+	vpaddq		$H2,$D4,$D4		# d4 += h4*r0
+	 vinserti128	\$1,16*2($inp),$T0,$T0
+
+	vpmuludq	$H3,$T1,$T4		# h3*s2
+	vpmuludq	$H4,$T1,$H2		# h4*s2
+	 vmovdqu	16*1($inp),%x#$T1
+	vpaddq		$T4,$D0,$D0		# d0 += h3*s2
+	vpaddq		$H2,$D1,$D1		# d1 += h4*s2
+	 vmovdqa	`32*5-0x90`(%rax),$H2	# r3
+	vpmuludq	$H1,$T2,$T4		# h1*r2
+	vpmuludq	$H0,$T2,$T2		# h0*r2
+	vpaddq		$T4,$D3,$D3		# d3 += h1*r2
+	vpaddq		$T2,$D2,$D2		# d2 += h0*r2
+	 vinserti128	\$1,16*3($inp),$T1,$T1
+	 lea		16*4($inp),$inp
+
+	vpmuludq	$H1,$H2,$T4		# h1*r3
+	vpmuludq	$H0,$H2,$H2		# h0*r3
+	 vpsrldq	\$6,$T0,$T2		# splat input
+	vpaddq		$T4,$D4,$D4		# d4 += h1*r3
+	vpaddq		$H2,$D3,$D3		# d3 += h0*r3
+	vpmuludq	$H3,$T3,$T4		# h3*s3
+	vpmuludq	$H4,$T3,$H2		# h4*s3
+	 vpsrldq	\$6,$T1,$T3
+	vpaddq		$T4,$D1,$D1		# d1 += h3*s3
+	vpaddq		$H2,$D2,$D2		# d2 += h4*s3
+	 vpunpckhqdq	$T1,$T0,$T4		# 4
+
+	vpmuludq	$H3,$S4,$H3		# h3*s4
+	vpmuludq	$H4,$S4,$H4		# h4*s4
+	 vpunpcklqdq	$T1,$T0,$T0		# 0:1
+	vpaddq		$H3,$D2,$H2		# h2 = d2 + h3*r4
+	vpaddq		$H4,$D3,$H3		# h3 = d3 + h4*r4
+	 vpunpcklqdq	$T3,$T2,$T3		# 2:3
+	vpmuludq	`32*7-0x90`(%rax),$H0,$H4	# h0*r4
+	vpmuludq	$H1,$S4,$H0		# h1*s4
+	vmovdqa		64(%rcx),$MASK		# .Lmask26
+	vpaddq		$H4,$D4,$H4		# h4 = d4 + h0*r4
+	vpaddq		$H0,$D0,$H0		# h0 = d0 + h1*s4
+
+	################################################################
+	# lazy reduction (interleaved with tail of input splat)
+
+	vpsrlq		\$26,$H3,$D3
+	vpand		$MASK,$H3,$H3
+	vpaddq		$D3,$H4,$H4		# h3 -> h4
+
+	vpsrlq		\$26,$H0,$D0
+	vpand		$MASK,$H0,$H0
+	vpaddq		$D0,$D1,$H1		# h0 -> h1
+
+	vpsrlq		\$26,$H4,$D4
+	vpand		$MASK,$H4,$H4
+
+	 vpsrlq		\$4,$T3,$T2
+
+	vpsrlq		\$26,$H1,$D1
+	vpand		$MASK,$H1,$H1
+	vpaddq		$D1,$H2,$H2		# h1 -> h2
+
+	vpaddq		$D4,$H0,$H0
+	vpsllq		\$2,$D4,$D4
+	vpaddq		$D4,$H0,$H0		# h4 -> h0
+
+	 vpand		$MASK,$T2,$T2		# 2
+	 vpsrlq		\$26,$T0,$T1
+
+	vpsrlq		\$26,$H2,$D2
+	vpand		$MASK,$H2,$H2
+	vpaddq		$D2,$H3,$H3		# h2 -> h3
+
+	 vpaddq		$T2,$H2,$H2		# modulo-scheduled
+	 vpsrlq		\$30,$T3,$T3
+
+	vpsrlq		\$26,$H0,$D0
+	vpand		$MASK,$H0,$H0
+	vpaddq		$D0,$H1,$H1		# h0 -> h1
+
+	 vpsrlq		\$40,$T4,$T4		# 4
+
+	vpsrlq		\$26,$H3,$D3
+	vpand		$MASK,$H3,$H3
+	vpaddq		$D3,$H4,$H4		# h3 -> h4
+
+	 vpand		$MASK,$T0,$T0		# 0
+	 vpand		$MASK,$T1,$T1		# 1
+	 vpand		$MASK,$T3,$T3		# 3
+	 vpor		32(%rcx),$T4,$T4	# padbit, yes, always
+
+	sub		\$64,$len
+	jnz		.Loop_avx2
+
+	.byte		0x66,0x90
+.Ltail_avx2:
+	################################################################
+	# while above multiplications were by r^4 in all lanes, in last
+	# iteration we multiply least significant lane by r^4 and most
+	# significant one by r, so copy of above except that references
+	# to the precomputed table are displaced by 4...
+
+	#vpaddq		$H2,$T2,$H2		# accumulate input
+	vpaddq		$H0,$T0,$H0
+	vmovdqu		`32*0+4`(%rsp),$T0	# r0^4
+	vpaddq		$H1,$T1,$H1
+	vmovdqu		`32*1+4`(%rsp),$T1	# r1^4
+	vpaddq		$H3,$T3,$H3
+	vmovdqu		`32*3+4`(%rsp),$T2	# r2^4
+	vpaddq		$H4,$T4,$H4
+	vmovdqu		`32*6+4-0x90`(%rax),$T3	# s3^4
+	vmovdqu		`32*8+4-0x90`(%rax),$S4	# s4^4
+
+	vpmuludq	$H2,$T0,$D2		# d2 = h2*r0
+	vpmuludq	$H2,$T1,$D3		# d3 = h2*r1
+	vpmuludq	$H2,$T2,$D4		# d4 = h2*r2
+	vpmuludq	$H2,$T3,$D0		# d0 = h2*s3
+	vpmuludq	$H2,$S4,$D1		# d1 = h2*s4
+
+	vpmuludq	$H0,$T1,$T4		# h0*r1
+	vpmuludq	$H1,$T1,$H2		# h1*r1
+	vpaddq		$T4,$D1,$D1		# d1 += h0*r1
+	vpaddq		$H2,$D2,$D2		# d2 += h1*r1
+	vpmuludq	$H3,$T1,$T4		# h3*r1
+	vpmuludq	`32*2+4`(%rsp),$H4,$H2	# h4*s1
+	vpaddq		$T4,$D4,$D4		# d4 += h3*r1
+	vpaddq		$H2,$D0,$D0		# d0 += h4*s1
+
+	vpmuludq	$H0,$T0,$T4		# h0*r0
+	vpmuludq	$H1,$T0,$H2		# h1*r0
+	vpaddq		$T4,$D0,$D0		# d0 += h0*r0
+	 vmovdqu	`32*4+4-0x90`(%rax),$T1	# s2
+	vpaddq		$H2,$D1,$D1		# d1 += h1*r0
+	vpmuludq	$H3,$T0,$T4		# h3*r0
+	vpmuludq	$H4,$T0,$H2		# h4*r0
+	vpaddq		$T4,$D3,$D3		# d3 += h3*r0
+	vpaddq		$H2,$D4,$D4		# d4 += h4*r0
+
+	vpmuludq	$H3,$T1,$T4		# h3*s2
+	vpmuludq	$H4,$T1,$H2		# h4*s2
+	vpaddq		$T4,$D0,$D0		# d0 += h3*s2
+	vpaddq		$H2,$D1,$D1		# d1 += h4*s2
+	 vmovdqu	`32*5+4-0x90`(%rax),$H2	# r3
+	vpmuludq	$H1,$T2,$T4		# h1*r2
+	vpmuludq	$H0,$T2,$T2		# h0*r2
+	vpaddq		$T4,$D3,$D3		# d3 += h1*r2
+	vpaddq		$T2,$D2,$D2		# d2 += h0*r2
+
+	vpmuludq	$H1,$H2,$T4		# h1*r3
+	vpmuludq	$H0,$H2,$H2		# h0*r3
+	vpaddq		$T4,$D4,$D4		# d4 += h1*r3
+	vpaddq		$H2,$D3,$D3		# d3 += h0*r3
+	vpmuludq	$H3,$T3,$T4		# h3*s3
+	vpmuludq	$H4,$T3,$H2		# h4*s3
+	vpaddq		$T4,$D1,$D1		# d1 += h3*s3
+	vpaddq		$H2,$D2,$D2		# d2 += h4*s3
+
+	vpmuludq	$H3,$S4,$H3		# h3*s4
+	vpmuludq	$H4,$S4,$H4		# h4*s4
+	vpaddq		$H3,$D2,$H2		# h2 = d2 + h3*r4
+	vpaddq		$H4,$D3,$H3		# h3 = d3 + h4*r4
+	vpmuludq	`32*7+4-0x90`(%rax),$H0,$H4		# h0*r4
+	vpmuludq	$H1,$S4,$H0		# h1*s4
+	vmovdqa		64(%rcx),$MASK		# .Lmask26
+	vpaddq		$H4,$D4,$H4		# h4 = d4 + h0*r4
+	vpaddq		$H0,$D0,$H0		# h0 = d0 + h1*s4
+
+	################################################################
+	# horizontal addition
+
+	vpsrldq		\$8,$D1,$T1
+	vpsrldq		\$8,$H2,$T2
+	vpsrldq		\$8,$H3,$T3
+	vpsrldq		\$8,$H4,$T4
+	vpsrldq		\$8,$H0,$T0
+	vpaddq		$T1,$D1,$D1
+	vpaddq		$T2,$H2,$H2
+	vpaddq		$T3,$H3,$H3
+	vpaddq		$T4,$H4,$H4
+	vpaddq		$T0,$H0,$H0
+
+	vpermq		\$0x2,$H3,$T3
+	vpermq		\$0x2,$H4,$T4
+	vpermq		\$0x2,$H0,$T0
+	vpermq		\$0x2,$D1,$T1
+	vpermq		\$0x2,$H2,$T2
+	vpaddq		$T3,$H3,$H3
+	vpaddq		$T4,$H4,$H4
+	vpaddq		$T0,$H0,$H0
+	vpaddq		$T1,$D1,$D1
+	vpaddq		$T2,$H2,$H2
+
+	################################################################
+	# lazy reduction
+
+	vpsrlq		\$26,$H3,$D3
+	vpand		$MASK,$H3,$H3
+	vpaddq		$D3,$H4,$H4		# h3 -> h4
+
+	vpsrlq		\$26,$H0,$D0
+	vpand		$MASK,$H0,$H0
+	vpaddq		$D0,$D1,$H1		# h0 -> h1
+
+	vpsrlq		\$26,$H4,$D4
+	vpand		$MASK,$H4,$H4
+
+	vpsrlq		\$26,$H1,$D1
+	vpand		$MASK,$H1,$H1
+	vpaddq		$D1,$H2,$H2		# h1 -> h2
+
+	vpaddq		$D4,$H0,$H0
+	vpsllq		\$2,$D4,$D4
+	vpaddq		$D4,$H0,$H0		# h4 -> h0
+
+	vpsrlq		\$26,$H2,$D2
+	vpand		$MASK,$H2,$H2
+	vpaddq		$D2,$H3,$H3		# h2 -> h3
+
+	vpsrlq		\$26,$H0,$D0
+	vpand		$MASK,$H0,$H0
+	vpaddq		$D0,$H1,$H1		# h0 -> h1
+
+	vpsrlq		\$26,$H3,$D3
+	vpand		$MASK,$H3,$H3
+	vpaddq		$D3,$H4,$H4		# h3 -> h4
+
+	vmovd		%x#$H0,`4*0-48-64`($ctx)# save partially reduced
+	vmovd		%x#$H1,`4*1-48-64`($ctx)
+	vmovd		%x#$H2,`4*2-48-64`($ctx)
+	vmovd		%x#$H3,`4*3-48-64`($ctx)
+	vmovd		%x#$H4,`4*4-48-64`($ctx)
+___
+$code.=<<___	if ($win64);
+	vmovdqa		0x50(%r11),%xmm6
+	vmovdqa		0x60(%r11),%xmm7
+	vmovdqa		0x70(%r11),%xmm8
+	vmovdqa		0x80(%r11),%xmm9
+	vmovdqa		0x90(%r11),%xmm10
+	vmovdqa		0xa0(%r11),%xmm11
+	vmovdqa		0xb0(%r11),%xmm12
+	vmovdqa		0xc0(%r11),%xmm13
+	vmovdqa		0xd0(%r11),%xmm14
+	vmovdqa		0xe0(%r11),%xmm15
+	lea		0xf8(%r11),%rsp
+.Ldo_avx2_epilogue:
+___
+$code.=<<___	if (!$win64);
+	lea		8(%r11),%rsp
+___
+$code.=<<___;
+	vzeroupper
+	ret
+.size	poly1305_blocks_avx2,.-poly1305_blocks_avx2
+___
+}
+$code.=<<___;
+.align	64
+.Lconst:
+.Lmask24:
+.long	0x0ffffff,0,0x0ffffff,0,0x0ffffff,0,0x0ffffff,0
+.L129:
+.long	`1<<24`,0,`1<<24`,0,`1<<24`,0,`1<<24`,0
+.Lmask26:
+.long	0x3ffffff,0,0x3ffffff,0,0x3ffffff,0,0x3ffffff,0
+.Lfive:
+.long	5,0,5,0,5,0,5,0
+___
+}
+
+$code.=<<___;
+.asciz	"Poly1305 for x86_64, CRYPTOGAMS by "
+.align	16
+___
+
+# EXCEPTION_DISPOSITION handler (EXCEPTION_RECORD *rec,ULONG64 frame,
+#		CONTEXT *context,DISPATCHER_CONTEXT *disp)
+if ($win64) {
+$rec="%rcx";
+$frame="%rdx";
+$context="%r8";
+$disp="%r9";
+
+$code.=<<___;
+.extern	__imp_RtlVirtualUnwind
+.type	se_handler,\@abi-omnipotent
+.align	16
+se_handler:
+	push	%rsi
+	push	%rdi
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+	pushfq
+	sub	\$64,%rsp
+
+	mov	120($context),%rax	# pull context->Rax
+	mov	248($context),%rbx	# pull context->Rip
+
+	mov	8($disp),%rsi		# disp->ImageBase
+	mov	56($disp),%r11		# disp->HandlerData
+
+	mov	0(%r11),%r10d		# HandlerData[0]
+	lea	(%rsi,%r10),%r10	# prologue label
+	cmp	%r10,%rbx		# context->Rip<.Lprologue
+	jb	.Lcommon_seh_tail
+
+	mov	152($context),%rax	# pull context->Rsp
+
+	mov	4(%r11),%r10d		# HandlerData[1]
+	lea	(%rsi,%r10),%r10	# epilogue label
+	cmp	%r10,%rbx		# context->Rip>=.Lepilogue
+	jae	.Lcommon_seh_tail
+
+	lea	48(%rax),%rax
+
+	mov	-8(%rax),%rbx
+	mov	-16(%rax),%rbp
+	mov	-24(%rax),%r12
+	mov	-32(%rax),%r13
+	mov	-40(%rax),%r14
+	mov	-48(%rax),%r15
+	mov	%rbx,144($context)	# restore context->Rbx
+	mov	%rbp,160($context)	# restore context->Rbp
+	mov	%r12,216($context)	# restore context->R12
+	mov	%r13,224($context)	# restore context->R13
+	mov	%r14,232($context)	# restore context->R14
+	mov	%r15,240($context)	# restore context->R14
+
+	jmp	.Lcommon_seh_tail
+.size	se_handler,.-se_handler
+
+.type	avx_handler,\@abi-omnipotent
+.align	16
+avx_handler:
+	push	%rsi
+	push	%rdi
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+	pushfq
+	sub	\$64,%rsp
+
+	mov	120($context),%rax	# pull context->Rax
+	mov	248($context),%rbx	# pull context->Rip
+
+	mov	8($disp),%rsi		# disp->ImageBase
+	mov	56($disp),%r11		# disp->HandlerData
+
+	mov	0(%r11),%r10d		# HandlerData[0]
+	lea	(%rsi,%r10),%r10	# prologue label
+	cmp	%r10,%rbx		# context->RipRsp
+
+	mov	4(%r11),%r10d		# HandlerData[1]
+	lea	(%rsi,%r10),%r10	# epilogue label
+	cmp	%r10,%rbx		# context->Rip>=epilogue label
+	jae	.Lcommon_seh_tail
+
+	mov	208($context),%rax	# pull context->R11
+
+	lea	0x50(%rax),%rsi
+	lea	0xf8(%rax),%rax
+	lea	512($context),%rdi	# &context.Xmm6
+	mov	\$20,%ecx
+	.long	0xa548f3fc		# cld; rep movsq
+
+.Lcommon_seh_tail:
+	mov	8(%rax),%rdi
+	mov	16(%rax),%rsi
+	mov	%rax,152($context)	# restore context->Rsp
+	mov	%rsi,168($context)	# restore context->Rsi
+	mov	%rdi,176($context)	# restore context->Rdi
+
+	mov	40($disp),%rdi		# disp->ContextRecord
+	mov	$context,%rsi		# context
+	mov	\$154,%ecx		# sizeof(CONTEXT)
+	.long	0xa548f3fc		# cld; rep movsq
+
+	mov	$disp,%rsi
+	xor	%rcx,%rcx		# arg1, UNW_FLAG_NHANDLER
+	mov	8(%rsi),%rdx		# arg2, disp->ImageBase
+	mov	0(%rsi),%r8		# arg3, disp->ControlPc
+	mov	16(%rsi),%r9		# arg4, disp->FunctionEntry
+	mov	40(%rsi),%r10		# disp->ContextRecord
+	lea	56(%rsi),%r11		# &disp->HandlerData
+	lea	24(%rsi),%r12		# &disp->EstablisherFrame
+	mov	%r10,32(%rsp)		# arg5
+	mov	%r11,40(%rsp)		# arg6
+	mov	%r12,48(%rsp)		# arg7
+	mov	%rcx,56(%rsp)		# arg8, (NULL)
+	call	*__imp_RtlVirtualUnwind(%rip)
+
+	mov	\$1,%eax		# ExceptionContinueSearch
+	add	\$64,%rsp
+	popfq
+	pop	%r15
+	pop	%r14
+	pop	%r13
+	pop	%r12
+	pop	%rbp
+	pop	%rbx
+	pop	%rdi
+	pop	%rsi
+	ret
+.size	avx_handler,.-avx_handler
+
+.section	.pdata
+.align	4
+	.rva	.LSEH_begin_poly1305_init
+	.rva	.LSEH_end_poly1305_init
+	.rva	.LSEH_info_poly1305_init
+
+	.rva	.LSEH_begin_poly1305_blocks
+	.rva	.LSEH_end_poly1305_blocks
+	.rva	.LSEH_info_poly1305_blocks
+
+	.rva	.LSEH_begin_poly1305_emit
+	.rva	.LSEH_end_poly1305_emit
+	.rva	.LSEH_info_poly1305_emit
+___
+$code.=<<___ if ($avx);
+	.rva	.LSEH_begin_poly1305_blocks_avx
+	.rva	.Lbase2_64_avx
+	.rva	.LSEH_info_poly1305_blocks_avx_1
+
+	.rva	.Lbase2_64_avx
+	.rva	.Leven_avx
+	.rva	.LSEH_info_poly1305_blocks_avx_2
+
+	.rva	.Leven_avx
+	.rva	.LSEH_end_poly1305_blocks_avx
+	.rva	.LSEH_info_poly1305_blocks_avx_3
+
+	.rva	.LSEH_begin_poly1305_emit_avx
+	.rva	.LSEH_end_poly1305_emit_avx
+	.rva	.LSEH_info_poly1305_emit_avx
+___
+$code.=<<___ if ($avx>1);
+	.rva	.LSEH_begin_poly1305_blocks_avx2
+	.rva	.Lbase2_64_avx2
+	.rva	.LSEH_info_poly1305_blocks_avx2_1
+
+	.rva	.Lbase2_64_avx2
+	.rva	.Leven_avx2
+	.rva	.LSEH_info_poly1305_blocks_avx2_2
+
+	.rva	.Leven_avx2
+	.rva	.LSEH_end_poly1305_blocks_avx2
+	.rva	.LSEH_info_poly1305_blocks_avx2_3
+___
+$code.=<<___;
+.section	.xdata
+.align	8
+.LSEH_info_poly1305_init:
+	.byte	9,0,0,0
+	.rva	se_handler
+	.rva	.LSEH_begin_poly1305_init,.LSEH_begin_poly1305_init
+
+.LSEH_info_poly1305_blocks:
+	.byte	9,0,0,0
+	.rva	se_handler
+	.rva	.Lblocks_body,.Lblocks_epilogue
+
+.LSEH_info_poly1305_emit:
+	.byte	9,0,0,0
+	.rva	se_handler
+	.rva	.LSEH_begin_poly1305_emit,.LSEH_begin_poly1305_emit
+___
+$code.=<<___ if ($avx);
+.LSEH_info_poly1305_blocks_avx_1:
+	.byte	9,0,0,0
+	.rva	se_handler
+	.rva	.Lblocks_avx_body,.Lblocks_avx_epilogue		# HandlerData[]
+
+.LSEH_info_poly1305_blocks_avx_2:
+	.byte	9,0,0,0
+	.rva	se_handler
+	.rva	.Lbase2_64_avx_body,.Lbase2_64_avx_epilogue	# HandlerData[]
+
+.LSEH_info_poly1305_blocks_avx_3:
+	.byte	9,0,0,0
+	.rva	avx_handler
+	.rva	.Ldo_avx_body,.Ldo_avx_epilogue			# HandlerData[]
+
+.LSEH_info_poly1305_emit_avx:
+	.byte	9,0,0,0
+	.rva	se_handler
+	.rva	.LSEH_begin_poly1305_emit_avx,.LSEH_begin_poly1305_emit_avx
+___
+$code.=<<___ if ($avx>1);
+.LSEH_info_poly1305_blocks_avx2_1:
+	.byte	9,0,0,0
+	.rva	se_handler
+	.rva	.Lblocks_avx2_body,.Lblocks_avx2_epilogue	# HandlerData[]
+
+.LSEH_info_poly1305_blocks_avx2_2:
+	.byte	9,0,0,0
+	.rva	se_handler
+	.rva	.Lbase2_64_avx2_body,.Lbase2_64_avx2_epilogue	# HandlerData[]
+
+.LSEH_info_poly1305_blocks_avx2_3:
+	.byte	9,0,0,0
+	.rva	avx_handler
+	.rva	.Ldo_avx2_body,.Ldo_avx2_epilogue		# HandlerData[]
+___
+}
+
+foreach (split('\n',$code)) {
+	s/\`([^\`]*)\`/eval($1)/ge;
+	s/%r([a-z]+)#d/%e$1/g;
+	s/%r([0-9]+)#d/%r$1d/g;
+	s/%x#%y/%x/g;
+
+	print $_,"\n";
+}
+close STDOUT;
diff --git a/crypto/poly1305/build.info b/crypto/poly1305/build.info
new file mode 100644
index 000000000..d575f5a63
--- /dev/null
+++ b/crypto/poly1305/build.info
@@ -0,0 +1,20 @@
+LIBS=../../libcrypto
+SOURCE[../../libcrypto]=\
+        poly1305.c {- $target{poly1305_asm_src} -}
+
+GENERATE[poly1305-sparcv9.S]=asm/poly1305-sparcv9.pl $(PERLASM_SCHEME)
+INCLUDE[poly1305-sparcv9.o]=..
+GENERATE[poly1305-x86.s]=asm/poly1305-x86.pl $(PERLASM_SCHEME) $(CFLAGS) $(LIB_CFLAGS) $(PROCESSOR)
+GENERATE[poly1305-x86_64.s]=asm/poly1305-x86_64.pl $(PERLASM_SCHEME)
+GENERATE[poly1305-ppc.s]=asm/poly1305-ppc.pl $(PERLASM_SCHEME)
+GENERATE[poly1305-ppcfp.s]=asm/poly1305-ppcfp.pl $(PERLASM_SCHEME)
+GENERATE[poly1305-armv4.S]=asm/poly1305-armv4.pl $(PERLASM_SCHEME)
+INCLUDE[poly1305-armv4.o]=..
+GENERATE[poly1305-armv8.S]=asm/poly1305-armv8.pl $(PERLASM_SCHEME)
+INCLUDE[poly1305-armv8.o]=..
+GENERATE[poly1305-mips.S]=asm/poly1305-mips.pl $(PERLASM_SCHEME)
+
+BEGINRAW[Makefile(unix)]
+{- $builddir -}/poly1305-%.S:	{- $sourcedir -}/asm/poly1305-%.pl
+	CC="$(CC)" $(PERL) $< $(PERLASM_SCHEME) $@
+ENDRAW[Makefile(unix)]
diff --git a/crypto/poly1305/poly1305.c b/crypto/poly1305/poly1305.c
new file mode 100644
index 000000000..eec4d67f0
--- /dev/null
+++ b/crypto/poly1305/poly1305.c
@@ -0,0 +1,1037 @@
+/*
+ * Copyright 2015-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include 
+
+#include "internal/poly1305.h"
+
+typedef void (*poly1305_blocks_f) (void *ctx, const unsigned char *inp,
+                                   size_t len, unsigned int padbit);
+typedef void (*poly1305_emit_f) (void *ctx, unsigned char mac[16],
+                                 const unsigned int nonce[4]);
+
+struct poly1305_context {
+    double opaque[24];  /* large enough to hold internal state, declared
+                         * 'double' to ensure at least 64-bit invariant
+                         * alignment across all platforms and
+                         * configurations */
+    unsigned int nonce[4];
+    unsigned char data[POLY1305_BLOCK_SIZE];
+    size_t num;
+    struct {
+        poly1305_blocks_f blocks;
+        poly1305_emit_f emit;
+    } func;
+};
+
+size_t Poly1305_ctx_size ()
+{
+    return sizeof(struct poly1305_context);
+}
+
+/* pick 32-bit unsigned integer in little endian order */
+static unsigned int U8TOU32(const unsigned char *p)
+{
+    return (((unsigned int)(p[0] & 0xff)) |
+            ((unsigned int)(p[1] & 0xff) << 8) |
+            ((unsigned int)(p[2] & 0xff) << 16) |
+            ((unsigned int)(p[3] & 0xff) << 24));
+}
+
+/*
+ * Implementations can be classified by amount of significant bits in
+ * words making up the multi-precision value, or in other words radix
+ * or base of numerical representation, e.g. base 2^64, base 2^32,
+ * base 2^26. Complementary characteristic is how wide is the result of
+ * multiplication of pair of digits, e.g. it would take 128 bits to
+ * accommodate multiplication result in base 2^64 case. These are used
+ * interchangeably. To describe implementation that is. But interface
+ * is designed to isolate this so that low-level primitives implemented
+ * in assembly can be self-contained/self-coherent.
+ */
+#ifndef POLY1305_ASM
+/*
+ * Even though there is __int128 reference implementation targeting
+ * 64-bit platforms provided below, it's not obvious that it's optimal
+ * choice for every one of them. Depending on instruction set overall
+ * amount of instructions can be comparable to one in __int64
+ * implementation. Amount of multiplication instructions would be lower,
+ * but not necessarily overall. And in out-of-order execution context,
+ * it is the latter that can be crucial...
+ *
+ * On related note. Poly1305 author, D. J. Bernstein, discusses and
+ * provides floating-point implementations of the algorithm in question.
+ * It made a lot of sense by the time of introduction, because most
+ * then-modern processors didn't have pipelined integer multiplier.
+ * [Not to mention that some had non-constant timing for integer
+ * multiplications.] Floating-point instructions on the other hand could
+ * be issued every cycle, which allowed to achieve better performance.
+ * Nowadays, with SIMD and/or out-or-order execution, shared or
+ * even emulated FPU, it's more complicated, and floating-point
+ * implementation is not necessarily optimal choice in every situation,
+ * rather contrary...
+ *
+ *                                              
+ */
+
+typedef unsigned int u32;
+
+/*
+ * poly1305_blocks processes a multiple of POLY1305_BLOCK_SIZE blocks
+ * of |inp| no longer than |len|. Behaviour for |len| not divisible by
+ * block size is unspecified in general case, even though in reference
+ * implementation the trailing chunk is simply ignored. Per algorithm
+ * specification, every input block, complete or last partial, is to be
+ * padded with a bit past most significant byte. The latter kind is then
+ * padded with zeros till block size. This last partial block padding
+ * is caller(*)'s responsibility, and because of this the last partial
+ * block is always processed with separate call with |len| set to
+ * POLY1305_BLOCK_SIZE and |padbit| to 0. In all other cases |padbit|
+ * should be set to 1 to perform implicit padding with 128th bit.
+ * poly1305_blocks does not actually check for this constraint though,
+ * it's caller(*)'s responsibility to comply.
+ *
+ * (*)  In the context "caller" is not application code, but higher
+ *      level Poly1305_* from this very module, so that quirks are
+ *      handled locally.
+ */
+static void
+poly1305_blocks(void *ctx, const unsigned char *inp, size_t len, u32 padbit);
+
+/*
+ * Type-agnostic "rip-off" from constant_time_locl.h
+ */
+# define CONSTANT_TIME_CARRY(a,b) ( \
+         (a ^ ((a ^ b) | ((a - b) ^ b))) >> (sizeof(a) * 8 - 1) \
+         )
+
+# if !defined(PEDANTIC) && \
+     (defined(__SIZEOF_INT128__) && __SIZEOF_INT128__==16) && \
+     (defined(__SIZEOF_LONG__) && __SIZEOF_LONG__==8)
+
+typedef unsigned long u64;
+typedef unsigned __int128 u128;
+
+typedef struct {
+    u64 h[3];
+    u64 r[2];
+} poly1305_internal;
+
+/* pick 32-bit unsigned integer in little endian order */
+static u64 U8TOU64(const unsigned char *p)
+{
+    return (((u64)(p[0] & 0xff)) |
+            ((u64)(p[1] & 0xff) << 8) |
+            ((u64)(p[2] & 0xff) << 16) |
+            ((u64)(p[3] & 0xff) << 24) |
+            ((u64)(p[4] & 0xff) << 32) |
+            ((u64)(p[5] & 0xff) << 40) |
+            ((u64)(p[6] & 0xff) << 48) |
+            ((u64)(p[7] & 0xff) << 56));
+}
+
+/* store a 32-bit unsigned integer in little endian */
+static void U64TO8(unsigned char *p, u64 v)
+{
+    p[0] = (unsigned char)((v) & 0xff);
+    p[1] = (unsigned char)((v >> 8) & 0xff);
+    p[2] = (unsigned char)((v >> 16) & 0xff);
+    p[3] = (unsigned char)((v >> 24) & 0xff);
+    p[4] = (unsigned char)((v >> 32) & 0xff);
+    p[5] = (unsigned char)((v >> 40) & 0xff);
+    p[6] = (unsigned char)((v >> 48) & 0xff);
+    p[7] = (unsigned char)((v >> 56) & 0xff);
+}
+
+static void poly1305_init(void *ctx, const unsigned char key[16])
+{
+    poly1305_internal *st = (poly1305_internal *) ctx;
+
+    /* h = 0 */
+    st->h[0] = 0;
+    st->h[1] = 0;
+    st->h[2] = 0;
+
+    /* r &= 0xffffffc0ffffffc0ffffffc0fffffff */
+    st->r[0] = U8TOU64(&key[0]) & 0x0ffffffc0fffffff;
+    st->r[1] = U8TOU64(&key[8]) & 0x0ffffffc0ffffffc;
+}
+
+static void
+poly1305_blocks(void *ctx, const unsigned char *inp, size_t len, u32 padbit)
+{
+    poly1305_internal *st = (poly1305_internal *)ctx;
+    u64 r0, r1;
+    u64 s1;
+    u64 h0, h1, h2, c;
+    u128 d0, d1;
+
+    r0 = st->r[0];
+    r1 = st->r[1];
+
+    s1 = r1 + (r1 >> 2);
+
+    h0 = st->h[0];
+    h1 = st->h[1];
+    h2 = st->h[2];
+
+    while (len >= POLY1305_BLOCK_SIZE) {
+        /* h += m[i] */
+        h0 = (u64)(d0 = (u128)h0 + U8TOU64(inp + 0));
+        h1 = (u64)(d1 = (u128)h1 + (d0 >> 64) + U8TOU64(inp + 8));
+        /*
+         * padbit can be zero only when original len was
+         * POLY1306_BLOCK_SIZE, but we don't check
+         */
+        h2 += (u64)(d1 >> 64) + padbit;
+
+        /* h *= r "%" p, where "%" stands for "partial remainder" */
+        d0 = ((u128)h0 * r0) +
+             ((u128)h1 * s1);
+        d1 = ((u128)h0 * r1) +
+             ((u128)h1 * r0) +
+             (h2 * s1);
+        h2 = (h2 * r0);
+
+        /* last reduction step: */
+        /* a) h2:h0 = h2<<128 + d1<<64 + d0 */
+        h0 = (u64)d0;
+        h1 = (u64)(d1 += d0 >> 64);
+        h2 += (u64)(d1 >> 64);
+        /* b) (h2:h0 += (h2:h0>>130) * 5) %= 2^130 */
+        c = (h2 >> 2) + (h2 & ~3UL);
+        h2 &= 3;
+        h0 += c;
+        h1 += (c = CONSTANT_TIME_CARRY(h0,c));
+        h2 += CONSTANT_TIME_CARRY(h1,c);
+        /*
+         * Occasional overflows to 3rd bit of h2 are taken care of
+         * "naturally". If after this point we end up at the top of
+         * this loop, then the overflow bit will be accounted for
+         * in next iteration. If we end up in poly1305_emit, then
+         * comparison to modulus below will still count as "carry
+         * into 131st bit", so that properly reduced value will be
+         * picked in conditional move.
+         */
+
+        inp += POLY1305_BLOCK_SIZE;
+        len -= POLY1305_BLOCK_SIZE;
+    }
+
+    st->h[0] = h0;
+    st->h[1] = h1;
+    st->h[2] = h2;
+}
+
+static void poly1305_emit(void *ctx, unsigned char mac[16],
+                          const u32 nonce[4])
+{
+    poly1305_internal *st = (poly1305_internal *) ctx;
+    u64 h0, h1, h2;
+    u64 g0, g1, g2;
+    u128 t;
+    u64 mask;
+
+    h0 = st->h[0];
+    h1 = st->h[1];
+    h2 = st->h[2];
+
+    /* compare to modulus by computing h + -p */
+    g0 = (u64)(t = (u128)h0 + 5);
+    g1 = (u64)(t = (u128)h1 + (t >> 64));
+    g2 = h2 + (u64)(t >> 64);
+
+    /* if there was carry into 131st bit, h1:h0 = g1:g0 */
+    mask = 0 - (g2 >> 2);
+    g0 &= mask;
+    g1 &= mask;
+    mask = ~mask;
+    h0 = (h0 & mask) | g0;
+    h1 = (h1 & mask) | g1;
+
+    /* mac = (h + nonce) % (2^128) */
+    h0 = (u64)(t = (u128)h0 + nonce[0] + ((u64)nonce[1]<<32));
+    h1 = (u64)(t = (u128)h1 + nonce[2] + ((u64)nonce[3]<<32) + (t >> 64));
+
+    U64TO8(mac + 0, h0);
+    U64TO8(mac + 8, h1);
+}
+
+# else
+
+#  if defined(_WIN32) && !defined(__MINGW32__)
+typedef unsigned __int64 u64;
+#  elif defined(__arch64__)
+typedef unsigned long u64;
+#  else
+typedef unsigned long long u64;
+#  endif
+
+typedef struct {
+    u32 h[5];
+    u32 r[4];
+} poly1305_internal;
+
+/* store a 32-bit unsigned integer in little endian */
+static void U32TO8(unsigned char *p, unsigned int v)
+{
+    p[0] = (unsigned char)((v) & 0xff);
+    p[1] = (unsigned char)((v >> 8) & 0xff);
+    p[2] = (unsigned char)((v >> 16) & 0xff);
+    p[3] = (unsigned char)((v >> 24) & 0xff);
+}
+
+static void poly1305_init(void *ctx, const unsigned char key[16])
+{
+    poly1305_internal *st = (poly1305_internal *) ctx;
+
+    /* h = 0 */
+    st->h[0] = 0;
+    st->h[1] = 0;
+    st->h[2] = 0;
+    st->h[3] = 0;
+    st->h[4] = 0;
+
+    /* r &= 0xffffffc0ffffffc0ffffffc0fffffff */
+    st->r[0] = U8TOU32(&key[0]) & 0x0fffffff;
+    st->r[1] = U8TOU32(&key[4]) & 0x0ffffffc;
+    st->r[2] = U8TOU32(&key[8]) & 0x0ffffffc;
+    st->r[3] = U8TOU32(&key[12]) & 0x0ffffffc;
+}
+
+static void
+poly1305_blocks(void *ctx, const unsigned char *inp, size_t len, u32 padbit)
+{
+    poly1305_internal *st = (poly1305_internal *)ctx;
+    u32 r0, r1, r2, r3;
+    u32 s1, s2, s3;
+    u32 h0, h1, h2, h3, h4, c;
+    u64 d0, d1, d2, d3;
+
+    r0 = st->r[0];
+    r1 = st->r[1];
+    r2 = st->r[2];
+    r3 = st->r[3];
+
+    s1 = r1 + (r1 >> 2);
+    s2 = r2 + (r2 >> 2);
+    s3 = r3 + (r3 >> 2);
+
+    h0 = st->h[0];
+    h1 = st->h[1];
+    h2 = st->h[2];
+    h3 = st->h[3];
+    h4 = st->h[4];
+
+    while (len >= POLY1305_BLOCK_SIZE) {
+        /* h += m[i] */
+        h0 = (u32)(d0 = (u64)h0 + U8TOU32(inp + 0));
+        h1 = (u32)(d1 = (u64)h1 + (d0 >> 32) + U8TOU32(inp + 4));
+        h2 = (u32)(d2 = (u64)h2 + (d1 >> 32) + U8TOU32(inp + 8));
+        h3 = (u32)(d3 = (u64)h3 + (d2 >> 32) + U8TOU32(inp + 12));
+        h4 += (u32)(d3 >> 32) + padbit;
+
+        /* h *= r "%" p, where "%" stands for "partial remainder" */
+        d0 = ((u64)h0 * r0) +
+             ((u64)h1 * s3) +
+             ((u64)h2 * s2) +
+             ((u64)h3 * s1);
+        d1 = ((u64)h0 * r1) +
+             ((u64)h1 * r0) +
+             ((u64)h2 * s3) +
+             ((u64)h3 * s2) +
+             (h4 * s1);
+        d2 = ((u64)h0 * r2) +
+             ((u64)h1 * r1) +
+             ((u64)h2 * r0) +
+             ((u64)h3 * s3) +
+             (h4 * s2);
+        d3 = ((u64)h0 * r3) +
+             ((u64)h1 * r2) +
+             ((u64)h2 * r1) +
+             ((u64)h3 * r0) +
+             (h4 * s3);
+        h4 = (h4 * r0);
+
+        /* last reduction step: */
+        /* a) h4:h0 = h4<<128 + d3<<96 + d2<<64 + d1<<32 + d0 */
+        h0 = (u32)d0;
+        h1 = (u32)(d1 += d0 >> 32);
+        h2 = (u32)(d2 += d1 >> 32);
+        h3 = (u32)(d3 += d2 >> 32);
+        h4 += (u32)(d3 >> 32);
+        /* b) (h4:h0 += (h4:h0>>130) * 5) %= 2^130 */
+        c = (h4 >> 2) + (h4 & ~3U);
+        h4 &= 3;
+        h0 += c;
+        h1 += (c = CONSTANT_TIME_CARRY(h0,c));
+        h2 += (c = CONSTANT_TIME_CARRY(h1,c));
+        h3 += (c = CONSTANT_TIME_CARRY(h2,c));
+        h4 += CONSTANT_TIME_CARRY(h3,c);
+        /*
+         * Occasional overflows to 3rd bit of h4 are taken care of
+         * "naturally". If after this point we end up at the top of
+         * this loop, then the overflow bit will be accounted for
+         * in next iteration. If we end up in poly1305_emit, then
+         * comparison to modulus below will still count as "carry
+         * into 131st bit", so that properly reduced value will be
+         * picked in conditional move.
+         */
+
+        inp += POLY1305_BLOCK_SIZE;
+        len -= POLY1305_BLOCK_SIZE;
+    }
+
+    st->h[0] = h0;
+    st->h[1] = h1;
+    st->h[2] = h2;
+    st->h[3] = h3;
+    st->h[4] = h4;
+}
+
+static void poly1305_emit(void *ctx, unsigned char mac[16],
+                          const u32 nonce[4])
+{
+    poly1305_internal *st = (poly1305_internal *) ctx;
+    u32 h0, h1, h2, h3, h4;
+    u32 g0, g1, g2, g3, g4;
+    u64 t;
+    u32 mask;
+
+    h0 = st->h[0];
+    h1 = st->h[1];
+    h2 = st->h[2];
+    h3 = st->h[3];
+    h4 = st->h[4];
+
+    /* compare to modulus by computing h + -p */
+    g0 = (u32)(t = (u64)h0 + 5);
+    g1 = (u32)(t = (u64)h1 + (t >> 32));
+    g2 = (u32)(t = (u64)h2 + (t >> 32));
+    g3 = (u32)(t = (u64)h3 + (t >> 32));
+    g4 = h4 + (u32)(t >> 32);
+
+    /* if there was carry into 131st bit, h3:h0 = g3:g0 */
+    mask = 0 - (g4 >> 2);
+    g0 &= mask;
+    g1 &= mask;
+    g2 &= mask;
+    g3 &= mask;
+    mask = ~mask;
+    h0 = (h0 & mask) | g0;
+    h1 = (h1 & mask) | g1;
+    h2 = (h2 & mask) | g2;
+    h3 = (h3 & mask) | g3;
+
+    /* mac = (h + nonce) % (2^128) */
+    h0 = (u32)(t = (u64)h0 + nonce[0]);
+    h1 = (u32)(t = (u64)h1 + (t >> 32) + nonce[1]);
+    h2 = (u32)(t = (u64)h2 + (t >> 32) + nonce[2]);
+    h3 = (u32)(t = (u64)h3 + (t >> 32) + nonce[3]);
+
+    U32TO8(mac + 0, h0);
+    U32TO8(mac + 4, h1);
+    U32TO8(mac + 8, h2);
+    U32TO8(mac + 12, h3);
+}
+# endif
+#else
+int poly1305_init(void *ctx, const unsigned char key[16], void *func);
+void poly1305_blocks(void *ctx, const unsigned char *inp, size_t len,
+                     unsigned int padbit);
+void poly1305_emit(void *ctx, unsigned char mac[16],
+                   const unsigned int nonce[4]);
+#endif
+
+void Poly1305_Init(POLY1305 *ctx, const unsigned char key[32])
+{
+    ctx->nonce[0] = U8TOU32(&key[16]);
+    ctx->nonce[1] = U8TOU32(&key[20]);
+    ctx->nonce[2] = U8TOU32(&key[24]);
+    ctx->nonce[3] = U8TOU32(&key[28]);
+
+#ifndef POLY1305_ASM
+    poly1305_init(ctx->opaque, key);
+#else
+    /*
+     * Unlike reference poly1305_init assembly counterpart is expected
+     * to return a value: non-zero if it initializes ctx->func, and zero
+     * otherwise. Latter is to simplify assembly in cases when there no
+     * multiple code paths to switch between.
+     */
+    if (!poly1305_init(ctx->opaque, key, &ctx->func)) {
+        ctx->func.blocks = poly1305_blocks;
+        ctx->func.emit = poly1305_emit;
+    }
+#endif
+
+    ctx->num = 0;
+
+}
+
+#ifdef POLY1305_ASM
+/*
+ * This "eclipses" poly1305_blocks and poly1305_emit, but it's
+ * conscious choice imposed by -Wshadow compiler warnings.
+ */
+# define poly1305_blocks (*poly1305_blocks_p)
+# define poly1305_emit   (*poly1305_emit_p)
+#endif
+
+void Poly1305_Update(POLY1305 *ctx, const unsigned char *inp, size_t len)
+{
+#ifdef POLY1305_ASM
+    /*
+     * As documented, poly1305_blocks is never called with input
+     * longer than single block and padbit argument set to 0. This
+     * property is fluently used in assembly modules to optimize
+     * padbit handling on loop boundary.
+     */
+    poly1305_blocks_f poly1305_blocks_p = ctx->func.blocks;
+#endif
+    size_t rem, num;
+
+    if ((num = ctx->num)) {
+        rem = POLY1305_BLOCK_SIZE - num;
+        if (len >= rem) {
+            memcpy(ctx->data + num, inp, rem);
+            poly1305_blocks(ctx->opaque, ctx->data, POLY1305_BLOCK_SIZE, 1);
+            inp += rem;
+            len -= rem;
+        } else {
+            /* Still not enough data to process a block. */
+            memcpy(ctx->data + num, inp, len);
+            ctx->num = num + len;
+            return;
+        }
+    }
+
+    rem = len % POLY1305_BLOCK_SIZE;
+    len -= rem;
+
+    if (len >= POLY1305_BLOCK_SIZE) {
+        poly1305_blocks(ctx->opaque, inp, len, 1);
+        inp += len;
+    }
+
+    if (rem)
+        memcpy(ctx->data, inp, rem);
+
+    ctx->num = rem;
+}
+
+void Poly1305_Final(POLY1305 *ctx, unsigned char mac[16])
+{
+#ifdef POLY1305_ASM
+    poly1305_blocks_f poly1305_blocks_p = ctx->func.blocks;
+    poly1305_emit_f poly1305_emit_p = ctx->func.emit;
+#endif
+    size_t num;
+
+    if ((num = ctx->num)) {
+        ctx->data[num++] = 1;   /* pad bit */
+        while (num < POLY1305_BLOCK_SIZE)
+            ctx->data[num++] = 0;
+        poly1305_blocks(ctx->opaque, ctx->data, POLY1305_BLOCK_SIZE, 0);
+    }
+
+    poly1305_emit(ctx->opaque, mac, ctx->nonce);
+
+    /* zero out the state */
+    OPENSSL_cleanse(ctx, sizeof(*ctx));
+}
+
+#ifdef SELFTEST
+#include 
+
+struct poly1305_test {
+    const char *inputhex;
+    const char *keyhex;
+    const char *outhex;
+};
+
+static const struct poly1305_test poly1305_tests[] = {
+    /*
+     * RFC7539
+     */
+    {
+     "43727970746f6772617068696320466f72756d2052657365617263682047726f"
+     "7570",
+     "85d6be7857556d337f4452fe42d506a8""0103808afb0db2fd4abff6af4149f51b",
+     "a8061dc1305136c6c22b8baf0c0127a9"
+    },
+    /*
+     * test vectors from "The Poly1305-AES message-authentication code"
+     */
+    {
+     "f3f6",
+     "851fc40c3467ac0be05cc20404f3f700""580b3b0f9447bb1e69d095b5928b6dbc",
+     "f4c633c3044fc145f84f335cb81953de"
+    },
+    {
+     "",
+     "a0f3080000f46400d0c7e9076c834403""dd3fab2251f11ac759f0887129cc2ee7",
+     "dd3fab2251f11ac759f0887129cc2ee7"
+    },
+    {
+     "663cea190ffb83d89593f3f476b6bc24d7e679107ea26adb8caf6652d0656136",
+     "48443d0bb0d21109c89a100b5ce2c208""83149c69b561dd88298a1798b10716ef",
+     "0ee1c16bb73f0f4fd19881753c01cdbe"
+    },
+    {
+     "ab0812724a7f1e342742cbed374d94d136c6b8795d45b3819830f2c04491faf0"
+     "990c62e48b8018b2c3e4a0fa3134cb67fa83e158c994d961c4cb21095c1bf9",
+     "12976a08c4426d0ce8a82407c4f48207""80f8c20aa71202d1e29179cbcb555a57",
+     "5154ad0d2cb26e01274fc51148491f1b"
+    },
+    /*
+     * self-generated vectors exercise "significant" lengths, such that
+     * are handled by different code paths
+     */
+    {
+     "ab0812724a7f1e342742cbed374d94d136c6b8795d45b3819830f2c04491faf0"
+     "990c62e48b8018b2c3e4a0fa3134cb67fa83e158c994d961c4cb21095c1bf9af",
+     "12976a08c4426d0ce8a82407c4f48207""80f8c20aa71202d1e29179cbcb555a57",
+     "812059a5da198637cac7c4a631bee466"
+    },
+    {
+     "ab0812724a7f1e342742cbed374d94d136c6b8795d45b3819830f2c04491faf0"
+     "990c62e48b8018b2c3e4a0fa3134cb67",
+     "12976a08c4426d0ce8a82407c4f48207""80f8c20aa71202d1e29179cbcb555a57",
+     "5b88d7f6228b11e2e28579a5c0c1f761"
+    },
+    {
+     "ab0812724a7f1e342742cbed374d94d136c6b8795d45b3819830f2c04491faf0"
+     "990c62e48b8018b2c3e4a0fa3134cb67fa83e158c994d961c4cb21095c1bf9af"
+     "663cea190ffb83d89593f3f476b6bc24d7e679107ea26adb8caf6652d0656136",
+     "12976a08c4426d0ce8a82407c4f48207""80f8c20aa71202d1e29179cbcb555a57",
+     "bbb613b2b6d753ba07395b916aaece15"
+    },
+    {
+     "ab0812724a7f1e342742cbed374d94d136c6b8795d45b3819830f2c04491faf0"
+     "990c62e48b8018b2c3e4a0fa3134cb67fa83e158c994d961c4cb21095c1bf9af"
+     "48443d0bb0d21109c89a100b5ce2c20883149c69b561dd88298a1798b10716ef"
+     "663cea190ffb83d89593f3f476b6bc24",
+     "12976a08c4426d0ce8a82407c4f48207""80f8c20aa71202d1e29179cbcb555a57",
+     "c794d7057d1778c4bbee0a39b3d97342"
+    },
+    {
+     "ab0812724a7f1e342742cbed374d94d136c6b8795d45b3819830f2c04491faf0"
+     "990c62e48b8018b2c3e4a0fa3134cb67fa83e158c994d961c4cb21095c1bf9af"
+     "48443d0bb0d21109c89a100b5ce2c20883149c69b561dd88298a1798b10716ef"
+     "663cea190ffb83d89593f3f476b6bc24d7e679107ea26adb8caf6652d0656136",
+     "12976a08c4426d0ce8a82407c4f48207""80f8c20aa71202d1e29179cbcb555a57",
+     "ffbcb9b371423152d7fca5ad042fbaa9"
+    },
+    {
+     "ab0812724a7f1e342742cbed374d94d136c6b8795d45b3819830f2c04491faf0"
+     "990c62e48b8018b2c3e4a0fa3134cb67fa83e158c994d961c4cb21095c1bf9af"
+     "48443d0bb0d21109c89a100b5ce2c20883149c69b561dd88298a1798b10716ef"
+     "663cea190ffb83d89593f3f476b6bc24d7e679107ea26adb8caf6652d0656136"
+     "812059a5da198637cac7c4a631bee466",
+     "12976a08c4426d0ce8a82407c4f48207""80f8c20aa71202d1e29179cbcb555a57",
+     "069ed6b8ef0f207b3e243bb1019fe632"
+    },
+    {
+     "ab0812724a7f1e342742cbed374d94d136c6b8795d45b3819830f2c04491faf0"
+     "990c62e48b8018b2c3e4a0fa3134cb67fa83e158c994d961c4cb21095c1bf9af"
+     "48443d0bb0d21109c89a100b5ce2c20883149c69b561dd88298a1798b10716ef"
+     "663cea190ffb83d89593f3f476b6bc24d7e679107ea26adb8caf6652d0656136"
+     "812059a5da198637cac7c4a631bee4665b88d7f6228b11e2e28579a5c0c1f761",
+     "12976a08c4426d0ce8a82407c4f48207""80f8c20aa71202d1e29179cbcb555a57",
+     "cca339d9a45fa2368c2c68b3a4179133"
+    },
+    {
+     "ab0812724a7f1e342742cbed374d94d136c6b8795d45b3819830f2c04491faf0"
+     "990c62e48b8018b2c3e4a0fa3134cb67fa83e158c994d961c4cb21095c1bf9af"
+     "48443d0bb0d21109c89a100b5ce2c20883149c69b561dd88298a1798b10716ef"
+     "663cea190ffb83d89593f3f476b6bc24d7e679107ea26adb8caf6652d0656136"
+     "812059a5da198637cac7c4a631bee4665b88d7f6228b11e2e28579a5c0c1f761"
+     "ab0812724a7f1e342742cbed374d94d136c6b8795d45b3819830f2c04491faf0"
+     "990c62e48b8018b2c3e4a0fa3134cb67fa83e158c994d961c4cb21095c1bf9af"
+     "48443d0bb0d21109c89a100b5ce2c20883149c69b561dd88298a1798b10716ef"
+     "663cea190ffb83d89593f3f476b6bc24d7e679107ea26adb8caf6652d0656136",
+     "12976a08c4426d0ce8a82407c4f48207""80f8c20aa71202d1e29179cbcb555a57",
+     "53f6e828a2f0fe0ee815bf0bd5841a34"
+    },
+    {
+     "ab0812724a7f1e342742cbed374d94d136c6b8795d45b3819830f2c04491faf0"
+     "990c62e48b8018b2c3e4a0fa3134cb67fa83e158c994d961c4cb21095c1bf9af"
+     "48443d0bb0d21109c89a100b5ce2c20883149c69b561dd88298a1798b10716ef"
+     "663cea190ffb83d89593f3f476b6bc24d7e679107ea26adb8caf6652d0656136"
+     "812059a5da198637cac7c4a631bee4665b88d7f6228b11e2e28579a5c0c1f761"
+     "ab0812724a7f1e342742cbed374d94d136c6b8795d45b3819830f2c04491faf0"
+     "990c62e48b8018b2c3e4a0fa3134cb67fa83e158c994d961c4cb21095c1bf9af"
+     "48443d0bb0d21109c89a100b5ce2c20883149c69b561dd88298a1798b10716ef"
+     "663cea190ffb83d89593f3f476b6bc24d7e679107ea26adb8caf6652d0656136"
+     "812059a5da198637cac7c4a631bee4665b88d7f6228b11e2e28579a5c0c1f761",
+     "12976a08c4426d0ce8a82407c4f48207""80f8c20aa71202d1e29179cbcb555a57",
+     "b846d44e9bbd53cedffbfbb6b7fa4933"
+    },
+    /*
+     * 4th power of the key spills to 131th bit in SIMD key setup
+     */
+    {
+     "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+     "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+     "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+     "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+     "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+     "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+     "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+     "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
+     "ad628107e8351d0f2c231a05dc4a4106""00000000000000000000000000000000",
+     "07145a4c02fe5fa32036de68fabe9066"
+    },
+    {
+    /*
+     * poly1305_ieee754.c failed this in final stage
+     */
+     "842364e156336c0998b933a6237726180d9e3fdcbde4cd5d17080fc3beb49614"
+     "d7122c037463ff104d73f19c12704628d417c4c54a3fe30d3c3d7714382d43b0"
+     "382a50a5dee54be844b076e8df88201a1cd43b90eb21643fa96f39b518aa8340"
+     "c942ff3c31baf7c9bdbf0f31ae3fa096bf8c63030609829fe72e179824890bc8"
+     "e08c315c1cce2a83144dbbff09f74e3efc770b54d0984a8f19b14719e6363564"
+     "1d6b1eedf63efbf080e1783d32445412114c20de0b837a0dfa33d6b82825fff4"
+     "4c9a70ea54ce47f07df698e6b03323b53079364a5fc3e9dd034392bdde86dccd"
+     "da94321c5e44060489336cb65bf3989c36f7282c2f5d2b882c171e74",
+     "95d5c005503e510d8cd0aa072c4a4d06""6eabc52d11653df47fbf63ab198bcc26",
+     "f248312e578d9d58f8b7bb4d19105431"
+    },
+    /*
+     * AVX2 in poly1305-x86.pl failed this with 176+32 split
+     */
+    {
+    "248ac31085b6c2adaaa38259a0d7192c5c35d1bb4ef39ad94c38d1c82479e2dd"
+    "2159a077024b0589bc8a20101b506f0a1ad0bbab76e83a83f1b94be6beae74e8"
+    "74cab692c5963a75436b776121ec9f62399a3e66b2d22707dae81933b6277f3c"
+    "8516bcbe26dbbd86f373103d7cf4cad1888c952118fbfbd0d7b4bedc4ae4936a"
+    "ff91157e7aa47c54442ea78d6ac251d324a0fbe49d89cc3521b66d16e9c66a37"
+    "09894e4eb0a4eedc4ae19468e66b81f2"
+    "71351b1d921ea551047abcc6b87a901fde7db79fa1818c11336dbc07244a40eb",
+    "000102030405060708090a0b0c0d0e0f""00000000000000000000000000000000",
+    "bc939bc5281480fa99c6d68c258ec42f"
+    },
+    /*
+     * test vectors from Google
+     */
+    {
+     "",
+     "c8afaac331ee372cd6082de134943b17""4710130e9f6fea8d72293850a667d86c",
+     "4710130e9f6fea8d72293850a667d86c",
+    },
+    {
+     "48656c6c6f20776f726c6421",
+     "746869732069732033322d6279746520""6b657920666f7220506f6c7931333035",
+     "a6f745008f81c916a20dcc74eef2b2f0"
+    },
+    {
+     "0000000000000000000000000000000000000000000000000000000000000000",
+     "746869732069732033322d6279746520""6b657920666f7220506f6c7931333035",
+     "49ec78090e481ec6c26b33b91ccc0307"
+    },
+    {
+     "89dab80b7717c1db5db437860a3f70218e93e1b8f461fb677f16f35f6f87e2a9"
+     "1c99bc3a47ace47640cc95c345be5ecca5a3523c35cc01893af0b64a62033427"
+     "0372ec12482d1b1e363561698a578b359803495bb4e2ef1930b17a5190b580f1"
+     "41300df30adbeca28f6427a8bc1a999fd51c554a017d095d8c3e3127daf9f595",
+     "2d773be37adb1e4d683bf0075e79c4ee""037918535a7f99ccb7040fb5f5f43aea",
+     "c85d15ed44c378d6b00e23064c7bcd51"
+    },
+    {
+     "000000000000000b1703030200000000"
+     "06db1f1f368d696a810a349c0c714c9a5e7850c2407d721acded95e018d7a852"
+     "66a6e1289cdb4aeb18da5ac8a2b0026d24a59ad485227f3eaedbb2e7e35e1c66"
+     "cd60f9abf716dcc9ac42682dd7dab287a7024c4eefc321cc0574e16793e37cec"
+     "03c5bda42b54c114a80b57af26416c7be742005e20855c73e21dc8e2edc9d435"
+     "cb6f6059280011c270b71570051c1c9b3052126620bc1e2730fa066c7a509d53"
+     "c60e5ae1b40aa6e39e49669228c90eecb4a50db32a50bc49e90b4f4b359a1dfd"
+     "11749cd3867fcf2fb7bb6cd4738f6a4ad6f7ca5058f7618845af9f020f6c3b96"
+     "7b8f4cd4a91e2813b507ae66f2d35c18284f7292186062e10fd5510d18775351"
+     "ef334e7634ab4743f5b68f49adcab384d3fd75f7390f4006ef2a295c8c7a076a"
+     "d54546cd25d2107fbe1436c840924aaebe5b370893cd63d1325b8616fc481088"
+     "6bc152c53221b6df373119393255ee72bcaa880174f1717f9184fa91646f17a2"
+     "4ac55d16bfddca9581a92eda479201f0edbf633600d6066d1ab36d5d2415d713"
+     "51bbcd608a25108d25641992c1f26c531cf9f90203bc4cc19f5927d834b0a471"
+     "16d3884bbb164b8ec883d1ac832e56b3918a98601a08d171881541d594db399c"
+     "6ae6151221745aec814c45b0b05b565436fd6f137aa10a0c0b643761dbd6f9a9"
+     "dcb99b1a6e690854ce0769cde39761d82fcdec15f0d92d7d8e94ade8eb83fbe0",
+     "99e5822dd4173c995e3dae0ddefb9774""3fde3b080134b39f76e9bf8d0e88d546",
+     "2637408fe13086ea73f971e3425e2820"
+    },
+    /*
+     * test vectors from Hanno Böck
+     */
+    {
+     "cccccccccccccccccccccccccccccccccccccccccccccccccc80cccccccccccc"
+     "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccecccccc"
+     "ccccccccccccccccccccccccccccccc5cccccccccccccccccccccccccccccccc"
+     "cccccccccce3cccccccccccccccccccccccccccccccccccccccccccccccccccc"
+     "ccccccccaccccccccccccccccccccce6cccccccccc000000afcccccccccccccc"
+     "ccccfffffff50000000000000000000000000000000000000000000000000000"
+     "00ffffffe7000000000000000000000000000000000000000000000000000000"
+     "0000000000000000000000000000000000000000000000000000719205a8521d"
+     "fc",
+     "7f1b0264000000000000000000000000""0000000000000000cccccccccccccccc",
+     "8559b876eceed66eb37798c0457baff9"
+    },
+    {
+     "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0000000000"
+     "00000000800264",
+     "e0001600000000000000000000000000""0000aaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+     "00bd1258978e205444c9aaaa82006fed"
+    },
+    {
+     "02fc",
+     "0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c""0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c",
+     "06120c0c0c0c0c0c0c0c0c0c0c0c0c0c"
+    },
+    {
+     "7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b"
+     "7b7b7b7b7b7b7a7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b"
+     "7b7b5c7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b"
+     "7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b6e7b007b7b7b7b7b7b7b7b7b"
+     "7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7a7b7b7b7b7b7b7b7b7b7b7b7b"
+     "7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b5c7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b"
+     "7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b"
+     "7b6e7b001300000000b300000000000000000000000000000000000000000000"
+     "f20000000000000000000000000000000000002000efff000900000000000000"
+     "0000000000100000000009000000640000000000000000000000001300000000"
+     "b300000000000000000000000000000000000000000000f20000000000000000"
+     "000000000000000000002000efff00090000000000000000007a000010000000"
+     "000900000064000000000000000000000000000000000000000000000000fc",
+     "00ff0000000000000000000000000000""00000000001e00000000000000007b7b",
+     "33205bbf9e9f8f7212ab9e2ab9b7e4a5"
+    },
+    {
+     "7777777777777777777777777777777777777777777777777777777777777777"
+     "7777777777777777777777777777777777777777777777777777777777777777"
+     "777777777777777777777777ffffffe9e9acacacacacacacacacacac0000acac"
+     "ec0100acacac2caca2acacacacacacacacacacac64f2",
+     "0000007f0000007f0100002000000000""0000cf77777777777777777777777777",
+     "02ee7c8c546ddeb1a467e4c3981158b9"
+    },
+    /*
+     * test vectors from Andrew Moon
+     */
+    {   /* nacl */
+     "8e993b9f48681273c29650ba32fc76ce48332ea7164d96a4476fb8c531a1186a"
+     "c0dfc17c98dce87b4da7f011ec48c97271d2c20f9b928fe2270d6fb863d51738"
+     "b48eeee314a7cc8ab932164548e526ae90224368517acfeabd6bb3732bc0e9da"
+     "99832b61ca01b6de56244a9e88d5f9b37973f622a43d14a6599b1f654cb45a74"
+     "e355a5",
+     "eea6a7251c1e72916d11c2cb214d3c25""2539121d8e234e652d651fa4c8cff880",
+     "f3ffc7703f9400e52a7dfb4b3d3305d9"
+    },
+    {   /* wrap 2^130-5 */
+     "ffffffffffffffffffffffffffffffff",
+     "02000000000000000000000000000000""00000000000000000000000000000000",
+     "03000000000000000000000000000000"
+    },
+    {   /* wrap 2^128 */
+     "02000000000000000000000000000000",
+     "02000000000000000000000000000000""ffffffffffffffffffffffffffffffff",
+     "03000000000000000000000000000000"
+    },
+    {   /* limb carry */
+     "fffffffffffffffffffffffffffffffff0ffffffffffffffffffffffffffffff"
+     "11000000000000000000000000000000",
+     "01000000000000000000000000000000""00000000000000000000000000000000",
+     "05000000000000000000000000000000"
+    },
+    {   /* 2^130-5 */
+     "fffffffffffffffffffffffffffffffffbfefefefefefefefefefefefefefefe"
+     "01010101010101010101010101010101",
+     "01000000000000000000000000000000""00000000000000000000000000000000",
+     "00000000000000000000000000000000"
+    },
+    {   /* 2^130-6 */
+     "fdffffffffffffffffffffffffffffff",
+     "02000000000000000000000000000000""00000000000000000000000000000000",
+     "faffffffffffffffffffffffffffffff"
+    },
+    {   /* 5*H+L reduction intermediate */
+     "e33594d7505e43b900000000000000003394d7505e4379cd0100000000000000"
+     "0000000000000000000000000000000001000000000000000000000000000000",
+     "01000000000000000400000000000000""00000000000000000000000000000000",
+     "14000000000000005500000000000000"
+    },
+    {   /* 5*H+L reduction final */
+     "e33594d7505e43b900000000000000003394d7505e4379cd0100000000000000"
+     "00000000000000000000000000000000",
+     "01000000000000000400000000000000""00000000000000000000000000000000",
+     "13000000000000000000000000000000"
+    }
+};
+
+static unsigned char hex_digit(char h)
+{
+    int i = OPENSSL_hexchar2int(h);
+
+    if (i < 0)
+        abort();
+    return i;
+}
+
+static void hex_decode(unsigned char *out, const char *hex)
+{
+    size_t j = 0;
+
+    while (*hex != 0) {
+        unsigned char v = hex_digit(*hex++);
+        v <<= 4;
+        v |= hex_digit(*hex++);
+        out[j++] = v;
+    }
+}
+
+static void hexdump(unsigned char *a, size_t len)
+{
+    size_t i;
+
+    for (i = 0; i < len; i++)
+        printf("%02x", a[i]);
+}
+
+int main()
+{
+    static const unsigned num_tests =
+        sizeof(poly1305_tests) / sizeof(struct poly1305_test);
+    unsigned i;
+    unsigned char key[32], out[16], expected[16];
+    POLY1305 poly1305;
+
+    for (i = 0; i < num_tests; i++) {
+        const struct poly1305_test *test = &poly1305_tests[i];
+        unsigned char *in;
+        size_t inlen = strlen(test->inputhex);
+
+        if (strlen(test->keyhex) != sizeof(key) * 2 ||
+            strlen(test->outhex) != sizeof(out) * 2 || (inlen & 1) == 1)
+            return 1;
+
+        inlen /= 2;
+
+        hex_decode(key, test->keyhex);
+        hex_decode(expected, test->outhex);
+
+        in = malloc(inlen);
+
+        hex_decode(in, test->inputhex);
+
+        Poly1305_Init(&poly1305, key);
+        Poly1305_Update(&poly1305, in, inlen);
+        Poly1305_Final(&poly1305, out);
+
+        if (memcmp(out, expected, sizeof(expected)) != 0) {
+            printf("Poly1305 test #%d failed.\n", i);
+            printf("got:      ");
+            hexdump(out, sizeof(out));
+            printf("\nexpected: ");
+            hexdump(expected, sizeof(expected));
+            printf("\n");
+            return 1;
+        }
+
+        if (inlen > 16) {
+            Poly1305_Init(&poly1305, key);
+            Poly1305_Update(&poly1305, in, 1);
+            Poly1305_Update(&poly1305, in+1, inlen-1);
+            Poly1305_Final(&poly1305, out);
+
+            if (memcmp(out, expected, sizeof(expected)) != 0) {
+                printf("Poly1305 test #%d/1+(N-1) failed.\n", i);
+                printf("got:      ");
+                hexdump(out, sizeof(out));
+                printf("\nexpected: ");
+                hexdump(expected, sizeof(expected));
+                printf("\n");
+                return 1;
+            }
+        }
+
+        if (inlen > 32) {
+            size_t half = inlen / 2;
+
+            Poly1305_Init(&poly1305, key);
+            Poly1305_Update(&poly1305, in, half);
+            Poly1305_Update(&poly1305, in+half, inlen-half);
+            Poly1305_Final(&poly1305, out);
+
+            if (memcmp(out, expected, sizeof(expected)) != 0) {
+                printf("Poly1305 test #%d/2 failed.\n", i);
+                printf("got:      ");
+                hexdump(out, sizeof(out));
+                printf("\nexpected: ");
+                hexdump(expected, sizeof(expected));
+                printf("\n");
+                return 1;
+            }
+
+            for (half = 16; half < inlen; half += 16) {
+                Poly1305_Init(&poly1305, key);
+                Poly1305_Update(&poly1305, in, half);
+                Poly1305_Update(&poly1305, in+half, inlen-half);
+                Poly1305_Final(&poly1305, out);
+
+                if (memcmp(out, expected, sizeof(expected)) != 0) {
+                    printf("Poly1305 test #%d/%d+%d failed.\n",
+                                           i, half, inlen-half);
+                    printf("got:      ");
+                    hexdump(out, sizeof(out));
+                    printf("\nexpected: ");
+                    hexdump(expected, sizeof(expected));
+                    printf("\n");
+                    return 1;
+                }
+            }
+        }
+
+        free(in);
+    }
+
+    printf("PASS\n");
+
+# ifdef OPENSSL_CPUID_OBJ
+    {
+        unsigned char buf[8192];
+        unsigned long long stopwatch;
+        unsigned long long OPENSSL_rdtsc();
+
+        memset (buf,0x55,sizeof(buf));
+        memset (key,0xAA,sizeof(key));
+
+        Poly1305_Init(&poly1305, key);
+
+        for (i=0;i<100000;i++)
+            Poly1305_Update(&poly1305,buf,sizeof(buf));
+
+        stopwatch = OPENSSL_rdtsc();
+        for (i=0;i<10000;i++)
+            Poly1305_Update(&poly1305,buf,sizeof(buf));
+        stopwatch = OPENSSL_rdtsc() - stopwatch;
+
+        printf("%g\n",stopwatch/(double)(i*sizeof(buf)));
+
+        stopwatch = OPENSSL_rdtsc();
+        for (i=0;i<10000;i++) {
+            Poly1305_Init(&poly1305, key);
+            Poly1305_Update(&poly1305,buf,16);
+            Poly1305_Final(&poly1305,buf);
+        }
+        stopwatch = OPENSSL_rdtsc() - stopwatch;
+
+        printf("%g\n",stopwatch/(double)(i));
+    }
+# endif
+    return 0;
+}
+#endif
diff --git a/crypto/poly1305/poly1305_ieee754.c b/crypto/poly1305/poly1305_ieee754.c
new file mode 100644
index 000000000..08a5b58c2
--- /dev/null
+++ b/crypto/poly1305/poly1305_ieee754.c
@@ -0,0 +1,472 @@
+/*
+ * Copyright 2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * This module is meant to be used as template for non-x87 floating-
+ * point assembly modules. The template itself is x86_64-specific
+ * though, as it was debugged on x86_64. So that implementor would
+ * have to recognize platform-specific parts, UxTOy and inline asm,
+ * and act accordingly.
+ *
+ * Huh? x86_64-specific code as template for non-x87? Note seven, which
+ * is not a typo, but reference to 80-bit precision. This module on the
+ * other hand relies on 64-bit precision operations, which are default
+ * for x86_64 code. And since we are at it, just for sense of it,
+ * large-block performance in cycles per processed byte for *this* code
+ * is:
+ *			gcc-4.8		icc-15.0	clang-3.4(*)
+ *
+ * Westmere		4.96		5.09		4.37
+ * Sandy Bridge		4.95		4.90		4.17
+ * Haswell		4.92		4.87		3.78
+ * Bulldozer		4.67		4.49		4.68
+ * VIA Nano		7.07		7.05		5.98
+ * Silvermont		10.6		9.61		12.6
+ *
+ * (*)	clang managed to discover parallelism and deployed SIMD;
+ *
+ * And for range of other platforms with unspecified gcc versions:
+ *
+ * Freescale e300	12.5
+ * PPC74x0		10.8
+ * POWER6		4.92
+ * POWER7		4.50
+ * POWER8		4.10
+ *
+ * z10			11.2
+ * z196+		7.30
+ *
+ * UltraSPARC III	16.0
+ * SPARC T4		16.1
+ */
+
+#if !(defined(__GNUC__) && __GNUC__>=2)
+# error "this is gcc-specific template"
+#endif
+
+#include 
+
+typedef unsigned char u8;
+typedef unsigned int u32;
+typedef unsigned long long u64;
+typedef union { double d; u64 u; } elem64;
+
+#define TWO(p)		((double)(1ULL<<(p)))
+#define TWO0		TWO(0)
+#define TWO32		TWO(32)
+#define TWO64		(TWO32*TWO(32))
+#define TWO96		(TWO64*TWO(32))
+#define TWO130		(TWO96*TWO(34))
+
+#define EXP(p)		((1023ULL+(p))<<52)
+
+#if defined(__x86_64__) || (defined(__PPC__) && defined(__LITTLE_ENDIAN__))
+# define U8TOU32(p)	(*(const u32 *)(p))
+# define U32TO8(p,v)	(*(u32 *)(p) = (v))
+#elif defined(__PPC__)
+# define U8TOU32(p)	({u32 ret; asm ("lwbrx	%0,0,%1":"=r"(ret):"b"(p)); ret; })
+# define U32TO8(p,v)	asm ("stwbrx %0,0,%1"::"r"(v),"b"(p):"memory")
+#elif defined(__s390x__)
+# define U8TOU32(p)	({u32 ret; asm ("lrv	%0,%1":"=d"(ret):"m"(*(u32 *)(p))); ret; })
+# define U32TO8(p,v)	asm ("strv	%1,%0":"=m"(*(u32 *)(p)):"d"(v))
+#endif
+
+#ifndef U8TOU32
+# define U8TOU32(p)	((u32)(p)[0]     | (u32)(p)[1]<<8 | \
+			 (u32)(p)[2]<<16 | (u32)(p)[3]<<24  )
+#endif
+#ifndef U32TO8
+# define U32TO8(p,v)	((p)[0] = (u8)(v),       (p)[1] = (u8)((v)>>8), \
+			 (p)[2] = (u8)((v)>>16), (p)[3] = (u8)((v)>>24) )
+#endif
+
+typedef struct {
+    elem64 h[4];
+    double r[8];
+    double s[6];
+} poly1305_internal;
+
+/* "round toward zero (truncate), mask all exceptions" */
+#if defined(__x86_64__)
+static const u32 mxcsr = 0x7f80;
+#elif defined(__PPC__)
+static const u64 one = 1;
+#elif defined(__s390x__)
+static const u32 fpc = 1;
+#elif defined(__sparc__)
+static const u64 fsr = 1ULL<<30;
+#else
+#error "unrecognized platform"
+#endif
+
+int poly1305_init(void *ctx, const unsigned char key[16])
+{
+    poly1305_internal *st = (poly1305_internal *) ctx;
+    elem64 r0, r1, r2, r3;
+
+    /* h = 0, biased */
+#if 0
+    st->h[0].d = TWO(52)*TWO0;
+    st->h[1].d = TWO(52)*TWO32;
+    st->h[2].d = TWO(52)*TWO64;
+    st->h[3].d = TWO(52)*TWO96;
+#else
+    st->h[0].u = EXP(52+0);
+    st->h[1].u = EXP(52+32);
+    st->h[2].u = EXP(52+64);
+    st->h[3].u = EXP(52+96);
+#endif
+
+    if (key) {
+        /*
+         * set "truncate" rounding mode
+         */
+#if defined(__x86_64__)
+        u32 mxcsr_orig;
+
+        asm volatile ("stmxcsr	%0":"=m"(mxcsr_orig));
+        asm volatile ("ldmxcsr	%0"::"m"(mxcsr));
+#elif defined(__PPC__)
+        double fpscr_orig, fpscr = *(double *)&one;
+
+        asm volatile ("mffs	%0":"=f"(fpscr_orig));
+        asm volatile ("mtfsf	255,%0"::"f"(fpscr));
+#elif defined(__s390x__)
+        u32 fpc_orig;
+
+        asm volatile ("stfpc	%0":"=m"(fpc_orig));
+        asm volatile ("lfpc	%0"::"m"(fpc));
+#elif defined(__sparc__)
+        u64 fsr_orig;
+
+        asm volatile ("stx	%%fsr,%0":"=m"(fsr_orig));
+        asm volatile ("ldx	%0,%%fsr"::"m"(fsr));
+#endif
+
+        /* r &= 0xffffffc0ffffffc0ffffffc0fffffff */
+        r0.u = EXP(52+0)  | (U8TOU32(&key[0])  & 0x0fffffff);
+        r1.u = EXP(52+32) | (U8TOU32(&key[4])  & 0x0ffffffc);
+        r2.u = EXP(52+64) | (U8TOU32(&key[8])  & 0x0ffffffc);
+        r3.u = EXP(52+96) | (U8TOU32(&key[12]) & 0x0ffffffc);
+
+        st->r[0] = r0.d - TWO(52)*TWO0;
+        st->r[2] = r1.d - TWO(52)*TWO32;
+        st->r[4] = r2.d - TWO(52)*TWO64;
+        st->r[6] = r3.d - TWO(52)*TWO96;
+
+        st->s[0] = st->r[2] * (5.0/TWO130);
+        st->s[2] = st->r[4] * (5.0/TWO130);
+        st->s[4] = st->r[6] * (5.0/TWO130);
+
+        /*
+         * base 2^32 -> base 2^16
+         */
+        st->r[1] = (st->r[0] + TWO(52)*TWO(16)*TWO0) -
+                               TWO(52)*TWO(16)*TWO0;
+        st->r[0] -= st->r[1];
+
+        st->r[3] = (st->r[2] + TWO(52)*TWO(16)*TWO32) -
+                               TWO(52)*TWO(16)*TWO32;
+        st->r[2] -= st->r[3];
+
+        st->r[5] = (st->r[4] + TWO(52)*TWO(16)*TWO64) -
+                               TWO(52)*TWO(16)*TWO64;
+        st->r[4] -= st->r[5];
+
+        st->r[7] = (st->r[6] + TWO(52)*TWO(16)*TWO96) -
+                               TWO(52)*TWO(16)*TWO96;
+        st->r[6] -= st->r[7];
+
+        st->s[1] = (st->s[0] + TWO(52)*TWO(16)*TWO0/TWO96) -
+                               TWO(52)*TWO(16)*TWO0/TWO96;
+        st->s[0] -= st->s[1];
+
+        st->s[3] = (st->s[2] + TWO(52)*TWO(16)*TWO32/TWO96) -
+                               TWO(52)*TWO(16)*TWO32/TWO96;
+        st->s[2] -= st->s[3];
+
+        st->s[5] = (st->s[4] + TWO(52)*TWO(16)*TWO64/TWO96) -
+                               TWO(52)*TWO(16)*TWO64/TWO96;
+        st->s[4] -= st->s[5];
+
+        /*
+         * restore original FPU control register
+         */
+#if defined(__x86_64__)
+        asm volatile ("ldmxcsr	%0"::"m"(mxcsr_orig));
+#elif defined(__PPC__)
+        asm volatile ("mtfsf	255,%0"::"f"(fpscr_orig));
+#elif defined(__s390x__)
+        asm volatile ("lfpc	%0"::"m"(fpc_orig));
+#elif defined(__sparc__)
+        asm volatile ("ldx	%0,%%fsr"::"m"(fsr_orig));
+#endif
+    }
+
+    return 0;
+}
+
+void poly1305_blocks(void *ctx, const unsigned char *inp, size_t len,
+                     int padbit)
+{
+    poly1305_internal *st = (poly1305_internal *)ctx;
+    elem64 in0, in1, in2, in3;
+    u64 pad = (u64)padbit<<32;
+
+    double x0, x1, x2, x3;
+    double h0lo, h0hi, h1lo, h1hi, h2lo, h2hi, h3lo, h3hi;
+    double c0lo, c0hi, c1lo, c1hi, c2lo, c2hi, c3lo, c3hi;
+
+    const double r0lo = st->r[0];
+    const double r0hi = st->r[1];
+    const double r1lo = st->r[2];
+    const double r1hi = st->r[3];
+    const double r2lo = st->r[4];
+    const double r2hi = st->r[5];
+    const double r3lo = st->r[6];
+    const double r3hi = st->r[7];
+
+    const double s1lo = st->s[0];
+    const double s1hi = st->s[1];
+    const double s2lo = st->s[2];
+    const double s2hi = st->s[3];
+    const double s3lo = st->s[4];
+    const double s3hi = st->s[5];
+
+    /*
+     * set "truncate" rounding mode
+     */
+#if defined(__x86_64__)
+    u32 mxcsr_orig;
+
+    asm volatile ("stmxcsr	%0":"=m"(mxcsr_orig));
+    asm volatile ("ldmxcsr	%0"::"m"(mxcsr));
+#elif defined(__PPC__)
+    double fpscr_orig, fpscr = *(double *)&one;
+
+    asm volatile ("mffs		%0":"=f"(fpscr_orig));
+    asm volatile ("mtfsf	255,%0"::"f"(fpscr));
+#elif defined(__s390x__)
+    u32 fpc_orig;
+
+    asm volatile ("stfpc	%0":"=m"(fpc_orig));
+    asm volatile ("lfpc		%0"::"m"(fpc));
+#elif defined(__sparc__)
+    u64 fsr_orig;
+
+    asm volatile ("stx		%%fsr,%0":"=m"(fsr_orig));
+    asm volatile ("ldx		%0,%%fsr"::"m"(fsr));
+#endif
+
+    /*
+     * load base 2^32 and de-bias
+     */
+    h0lo = st->h[0].d - TWO(52)*TWO0;
+    h1lo = st->h[1].d - TWO(52)*TWO32;
+    h2lo = st->h[2].d - TWO(52)*TWO64;
+    h3lo = st->h[3].d - TWO(52)*TWO96;
+
+#ifdef __clang__
+    h0hi = 0;
+    h1hi = 0;
+    h2hi = 0;
+    h3hi = 0;
+#else
+    in0.u = EXP(52+0)  | U8TOU32(&inp[0]);
+    in1.u = EXP(52+32) | U8TOU32(&inp[4]);
+    in2.u = EXP(52+64) | U8TOU32(&inp[8]);
+    in3.u = EXP(52+96) | U8TOU32(&inp[12]) | pad;
+
+    x0 = in0.d - TWO(52)*TWO0;
+    x1 = in1.d - TWO(52)*TWO32;
+    x2 = in2.d - TWO(52)*TWO64;
+    x3 = in3.d - TWO(52)*TWO96;
+
+    x0 += h0lo;
+    x1 += h1lo;
+    x2 += h2lo;
+    x3 += h3lo;
+
+    goto fast_entry;
+#endif
+
+    do {
+        in0.u = EXP(52+0)  | U8TOU32(&inp[0]);
+        in1.u = EXP(52+32) | U8TOU32(&inp[4]);
+        in2.u = EXP(52+64) | U8TOU32(&inp[8]);
+        in3.u = EXP(52+96) | U8TOU32(&inp[12]) | pad;
+
+        x0 = in0.d - TWO(52)*TWO0;
+        x1 = in1.d - TWO(52)*TWO32;
+        x2 = in2.d - TWO(52)*TWO64;
+        x3 = in3.d - TWO(52)*TWO96;
+
+        /*
+         * note that there are multiple ways to accumulate input, e.g.
+         * one can as well accumulate to h0lo-h1lo-h1hi-h2hi...
+         */
+        h0lo += x0;
+        h0hi += x1;
+        h2lo += x2;
+        h2hi += x3;
+
+        /*
+         * carries that cross 32n-bit (and 130-bit) boundaries
+         */
+        c0lo = (h0lo + TWO(52)*TWO32)  - TWO(52)*TWO32;
+        c1lo = (h1lo + TWO(52)*TWO64)  - TWO(52)*TWO64;
+        c2lo = (h2lo + TWO(52)*TWO96)  - TWO(52)*TWO96;
+        c3lo = (h3lo + TWO(52)*TWO130) - TWO(52)*TWO130;
+
+        c0hi = (h0hi + TWO(52)*TWO32)  - TWO(52)*TWO32;
+        c1hi = (h1hi + TWO(52)*TWO64)  - TWO(52)*TWO64;
+        c2hi = (h2hi + TWO(52)*TWO96)  - TWO(52)*TWO96;
+        c3hi = (h3hi + TWO(52)*TWO130) - TWO(52)*TWO130;
+
+        /*
+         * base 2^48 -> base 2^32 with last reduction step
+         */
+        x1 =  (h1lo - c1lo) + c0lo;
+        x2 =  (h2lo - c2lo) + c1lo;
+        x3 =  (h3lo - c3lo) + c2lo;
+        x0 =  (h0lo - c0lo) + c3lo * (5.0/TWO130);
+
+        x1 += (h1hi - c1hi) + c0hi;
+        x2 += (h2hi - c2hi) + c1hi;
+        x3 += (h3hi - c3hi) + c2hi;
+        x0 += (h0hi - c0hi) + c3hi * (5.0/TWO130);
+
+#ifndef __clang__
+    fast_entry:
+#endif
+	/*
+	 * base 2^32 * base 2^16 = base 2^48
+	 */
+        h0lo = s3lo * x1 + s2lo * x2 + s1lo * x3 + r0lo * x0;
+        h1lo = r0lo * x1 + s3lo * x2 + s2lo * x3 + r1lo * x0;
+        h2lo = r1lo * x1 + r0lo * x2 + s3lo * x3 + r2lo * x0;
+        h3lo = r2lo * x1 + r1lo * x2 + r0lo * x3 + r3lo * x0;
+
+        h0hi = s3hi * x1 + s2hi * x2 + s1hi * x3 + r0hi * x0;
+        h1hi = r0hi * x1 + s3hi * x2 + s2hi * x3 + r1hi * x0;
+        h2hi = r1hi * x1 + r0hi * x2 + s3hi * x3 + r2hi * x0;
+        h3hi = r2hi * x1 + r1hi * x2 + r0hi * x3 + r3hi * x0;
+
+        inp += 16;
+        len -= 16;
+
+    } while (len >= 16);
+
+    /*
+     * carries that cross 32n-bit (and 130-bit) boundaries
+     */
+    c0lo = (h0lo + TWO(52)*TWO32)  - TWO(52)*TWO32;
+    c1lo = (h1lo + TWO(52)*TWO64)  - TWO(52)*TWO64;
+    c2lo = (h2lo + TWO(52)*TWO96)  - TWO(52)*TWO96;
+    c3lo = (h3lo + TWO(52)*TWO130) - TWO(52)*TWO130;
+
+    c0hi = (h0hi + TWO(52)*TWO32)  - TWO(52)*TWO32;
+    c1hi = (h1hi + TWO(52)*TWO64)  - TWO(52)*TWO64;
+    c2hi = (h2hi + TWO(52)*TWO96)  - TWO(52)*TWO96;
+    c3hi = (h3hi + TWO(52)*TWO130) - TWO(52)*TWO130;
+
+    /*
+     * base 2^48 -> base 2^32 with last reduction step
+     */
+    x1 =  (h1lo - c1lo) + c0lo;
+    x2 =  (h2lo - c2lo) + c1lo;
+    x3 =  (h3lo - c3lo) + c2lo;
+    x0 =  (h0lo - c0lo) + c3lo * (5.0/TWO130);
+
+    x1 += (h1hi - c1hi) + c0hi;
+    x2 += (h2hi - c2hi) + c1hi;
+    x3 += (h3hi - c3hi) + c2hi;
+    x0 += (h0hi - c0hi) + c3hi * (5.0/TWO130);
+
+    /*
+     * store base 2^32, with bias
+     */
+    st->h[1].d = x1 + TWO(52)*TWO32;
+    st->h[2].d = x2 + TWO(52)*TWO64;
+    st->h[3].d = x3 + TWO(52)*TWO96;
+    st->h[0].d = x0 + TWO(52)*TWO0;
+
+    /*
+     * restore original FPU control register
+     */
+#if defined(__x86_64__)
+    asm volatile ("ldmxcsr	%0"::"m"(mxcsr_orig));
+#elif defined(__PPC__)
+    asm volatile ("mtfsf	255,%0"::"f"(fpscr_orig));
+#elif defined(__s390x__)
+    asm volatile ("lfpc		%0"::"m"(fpc_orig));
+#elif defined(__sparc__)
+    asm volatile ("ldx		%0,%%fsr"::"m"(fsr_orig));
+#endif
+}
+
+void poly1305_emit(void *ctx, unsigned char mac[16], const u32 nonce[4])
+{
+    poly1305_internal *st = (poly1305_internal *) ctx;
+    u64 h0, h1, h2, h3, h4;
+    u32 g0, g1, g2, g3, g4;
+    u64 t;
+    u32 mask;
+
+    /*
+     * thanks to bias masking exponent gives integer result
+     */
+    h0 = st->h[0].u & 0x000fffffffffffffULL;
+    h1 = st->h[1].u & 0x000fffffffffffffULL;
+    h2 = st->h[2].u & 0x000fffffffffffffULL;
+    h3 = st->h[3].u & 0x000fffffffffffffULL;
+
+    /*
+     * can be partially reduced, so reduce...
+     */
+    h4 = h3>>32; h3 &= 0xffffffffU;
+    g4 = h4&-4;
+    h4 &= 3;
+    g4 += g4>>2;
+
+    h0 += g4;
+    h1 += h0>>32; h0 &= 0xffffffffU;
+    h2 += h1>>32; h1 &= 0xffffffffU;
+    h3 += h2>>32; h2 &= 0xffffffffU;
+
+    /* compute h + -p */
+    g0 = (u32)(t = h0 + 5);
+    g1 = (u32)(t = h1 + (t >> 32));
+    g2 = (u32)(t = h2 + (t >> 32));
+    g3 = (u32)(t = h3 + (t >> 32));
+    g4 = h4 + (u32)(t >> 32);
+
+    /* if there was carry, select g0-g3 */
+    mask = 0 - (g4 >> 2);
+    g0 &= mask;
+    g1 &= mask;
+    g2 &= mask;
+    g3 &= mask;
+    mask = ~mask;
+    g0 |= (h0 & mask);
+    g1 |= (h1 & mask);
+    g2 |= (h2 & mask);
+    g3 |= (h3 & mask);
+
+    /* mac = (h + nonce) % (2^128) */
+    g0 = (u32)(t = (u64)g0 + nonce[0]);
+    g1 = (u32)(t = (u64)g1 + (t >> 32) + nonce[1]);
+    g2 = (u32)(t = (u64)g2 + (t >> 32) + nonce[2]);
+    g3 = (u32)(t = (u64)g3 + (t >> 32) + nonce[3]);
+
+    U32TO8(mac + 0, g0);
+    U32TO8(mac + 4, g1);
+    U32TO8(mac + 8, g2);
+    U32TO8(mac + 12, g3);
+}
diff --git a/crypto/ppc_arch.h b/crypto/ppc_arch.h
new file mode 100644
index 000000000..65cf96fc1
--- /dev/null
+++ b/crypto/ppc_arch.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2014-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#ifndef HEADER_PPC_ARCH_H
+# define HEADER_PPC_ARCH_H
+
+extern unsigned int OPENSSL_ppccap_P;
+
+/*
+ * Flags' usage can appear ambiguous, because they are set rather
+ * to reflect OpenSSL performance preferences than actual processor
+ * capabilities.
+ */
+# define PPC_FPU64       (1<<0)
+# define PPC_ALTIVEC     (1<<1)
+# define PPC_CRYPTO207   (1<<2)
+# define PPC_FPU         (1<<3)
+# define PPC_MADD300     (1<<4)
+
+#endif
diff --git a/crypto/ppccap.c b/crypto/ppccap.c
new file mode 100644
index 000000000..ef38b172d
--- /dev/null
+++ b/crypto/ppccap.c
@@ -0,0 +1,317 @@
+/*
+ * Copyright 2009-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#if defined(__linux) || defined(_AIX)
+# include 
+#endif
+#if defined(_AIX53)     /* defined even on post-5.3 */
+# include 
+# if !defined(__power_set)
+#  define __power_set(a) (_system_configuration.implementation & (a))
+# endif
+#endif
+#include 
+#include 
+
+#include "ppc_arch.h"
+
+unsigned int OPENSSL_ppccap_P = 0;
+
+static sigset_t all_masked;
+
+#ifdef OPENSSL_BN_ASM_MONT
+int bn_mul_mont(BN_ULONG *rp, const BN_ULONG *ap, const BN_ULONG *bp,
+                const BN_ULONG *np, const BN_ULONG *n0, int num)
+{
+    int bn_mul_mont_fpu64(BN_ULONG *rp, const BN_ULONG *ap,
+                          const BN_ULONG *bp, const BN_ULONG *np,
+                          const BN_ULONG *n0, int num);
+    int bn_mul_mont_int(BN_ULONG *rp, const BN_ULONG *ap, const BN_ULONG *bp,
+                        const BN_ULONG *np, const BN_ULONG *n0, int num);
+
+    if (sizeof(size_t) == 4) {
+# if 1 || (defined(__APPLE__) && defined(__MACH__))
+        if (num >= 8 && (num & 3) == 0 && (OPENSSL_ppccap_P & PPC_FPU64))
+            return bn_mul_mont_fpu64(rp, ap, bp, np, n0, num);
+# else
+        /*
+         * boundary of 32 was experimentally determined on Linux 2.6.22,
+         * might have to be adjusted on AIX...
+         */
+        if (num >= 32 && (num & 3) == 0 && (OPENSSL_ppccap_P & PPC_FPU64)) {
+            sigset_t oset;
+            int ret;
+
+            sigprocmask(SIG_SETMASK, &all_masked, &oset);
+            ret = bn_mul_mont_fpu64(rp, ap, bp, np, n0, num);
+            sigprocmask(SIG_SETMASK, &oset, NULL);
+
+            return ret;
+        }
+# endif
+    } else if ((OPENSSL_ppccap_P & PPC_FPU64))
+        /*
+         * this is a "must" on POWER6, but run-time detection is not
+         * implemented yet...
+         */
+        return bn_mul_mont_fpu64(rp, ap, bp, np, n0, num);
+
+    return bn_mul_mont_int(rp, ap, bp, np, n0, num);
+}
+#endif
+
+void sha256_block_p8(void *ctx, const void *inp, size_t len);
+void sha256_block_ppc(void *ctx, const void *inp, size_t len);
+void sha256_block_data_order(void *ctx, const void *inp, size_t len)
+{
+    OPENSSL_ppccap_P & PPC_CRYPTO207 ? sha256_block_p8(ctx, inp, len) :
+        sha256_block_ppc(ctx, inp, len);
+}
+
+void sha512_block_p8(void *ctx, const void *inp, size_t len);
+void sha512_block_ppc(void *ctx, const void *inp, size_t len);
+void sha512_block_data_order(void *ctx, const void *inp, size_t len)
+{
+    OPENSSL_ppccap_P & PPC_CRYPTO207 ? sha512_block_p8(ctx, inp, len) :
+        sha512_block_ppc(ctx, inp, len);
+}
+
+#ifndef OPENSSL_NO_CHACHA
+void ChaCha20_ctr32_int(unsigned char *out, const unsigned char *inp,
+                        size_t len, const unsigned int key[8],
+                        const unsigned int counter[4]);
+void ChaCha20_ctr32_vmx(unsigned char *out, const unsigned char *inp,
+                        size_t len, const unsigned int key[8],
+                        const unsigned int counter[4]);
+void ChaCha20_ctr32(unsigned char *out, const unsigned char *inp,
+                    size_t len, const unsigned int key[8],
+                    const unsigned int counter[4])
+{
+    OPENSSL_ppccap_P & PPC_ALTIVEC
+        ? ChaCha20_ctr32_vmx(out, inp, len, key, counter)
+        : ChaCha20_ctr32_int(out, inp, len, key, counter);
+}
+#endif
+
+#ifndef OPENSSL_NO_POLY1305
+void poly1305_init_int(void *ctx, const unsigned char key[16]);
+void poly1305_blocks(void *ctx, const unsigned char *inp, size_t len,
+                         unsigned int padbit);
+void poly1305_emit(void *ctx, unsigned char mac[16],
+                       const unsigned int nonce[4]);
+void poly1305_init_fpu(void *ctx, const unsigned char key[16]);
+void poly1305_blocks_fpu(void *ctx, const unsigned char *inp, size_t len,
+                         unsigned int padbit);
+void poly1305_emit_fpu(void *ctx, unsigned char mac[16],
+                       const unsigned int nonce[4]);
+int poly1305_init(void *ctx, const unsigned char key[16], void *func[2])
+{
+    if (sizeof(size_t) == 4 && (OPENSSL_ppccap_P & PPC_FPU)) {
+        poly1305_init_fpu(ctx, key);
+        func[0] = poly1305_blocks_fpu;
+        func[1] = poly1305_emit_fpu;
+    } else {
+        poly1305_init_int(ctx, key);
+        func[0] = poly1305_blocks;
+        func[1] = poly1305_emit;
+    }
+    return 1;
+}
+#endif
+
+static sigjmp_buf ill_jmp;
+static void ill_handler(int sig)
+{
+    siglongjmp(ill_jmp, sig);
+}
+
+void OPENSSL_fpu_probe(void);
+void OPENSSL_ppc64_probe(void);
+void OPENSSL_altivec_probe(void);
+void OPENSSL_crypto207_probe(void);
+void OPENSSL_madd300_probe(void);
+
+/*
+ * Use a weak reference to getauxval() so we can use it if it is available
+ * but don't break the build if it is not. Note that this is *link-time*
+ * feature detection, not *run-time*. In other words if we link with
+ * symbol present, it's expected to be present even at run-time.
+ */
+#if defined(__GNUC__) && __GNUC__>=2 && defined(__ELF__)
+extern unsigned long getauxval(unsigned long type) __attribute__ ((weak));
+#else
+static unsigned long (*getauxval) (unsigned long) = NULL;
+#endif
+
+/* I wish  was universally available */
+#define HWCAP                   16      /* AT_HWCAP */
+#define HWCAP_PPC64             (1U << 30)
+#define HWCAP_ALTIVEC           (1U << 28)
+#define HWCAP_FPU               (1U << 27)
+#define HWCAP_POWER6_EXT        (1U << 9)
+#define HWCAP_VSX               (1U << 7)
+
+#define HWCAP2                  26      /* AT_HWCAP2 */
+#define HWCAP_VEC_CRYPTO        (1U << 25)
+#define HWCAP_ARCH_3_00         (1U << 23)
+
+# if defined(__GNUC__) && __GNUC__>=2
+__attribute__ ((constructor))
+# endif
+void OPENSSL_cpuid_setup(void)
+{
+    char *e;
+    struct sigaction ill_oact, ill_act;
+    sigset_t oset;
+    static int trigger = 0;
+
+    if (trigger)
+        return;
+    trigger = 1;
+
+    if ((e = getenv("OPENSSL_ppccap"))) {
+        OPENSSL_ppccap_P = strtoul(e, NULL, 0);
+        return;
+    }
+
+    OPENSSL_ppccap_P = 0;
+
+#if defined(_AIX)
+    OPENSSL_ppccap_P |= PPC_FPU;
+
+    if (sizeof(size_t) == 4) {
+        struct utsname uts;
+# if defined(_SC_AIX_KERNEL_BITMODE)
+        if (sysconf(_SC_AIX_KERNEL_BITMODE) != 64)
+            return;
+# endif
+        if (uname(&uts) != 0 || atoi(uts.version) < 6)
+            return;
+    }
+
+# if defined(__power_set)
+    /*
+     * Value used in __power_set is a single-bit 1<
+#include 
+
+#include "e_os.h"
+
+#if !(defined(OPENSSL_SYS_WIN32) || defined(OPENSSL_SYS_VXWORKS) || defined(OPENSSL_SYS_DSPBIOS))
+# include 
+#endif
+#if defined(OPENSSL_SYS_VXWORKS)
+# include 
+#endif
+
+#include 
+#include 
+#include 
+#ifndef OPENSSL_NO_ASYNC
+# include 
+#endif
+#include "rand_lcl.h"
+
+#include 
+
+#include 
+
+#ifdef OPENSSL_FIPS
+# include 
+#endif
+
+#ifdef BN_DEBUG
+# define PREDICT
+#endif
+
+/* #define PREDICT      1 */
+
+#define STATE_SIZE      1023
+static size_t state_num = 0, state_index = 0;
+static unsigned char state[STATE_SIZE + MD_DIGEST_LENGTH];
+static unsigned char md[MD_DIGEST_LENGTH];
+static long md_count[2] = { 0, 0 };
+
+static double entropy = 0;
+static int initialized = 0;
+
+static CRYPTO_RWLOCK *rand_lock = NULL;
+static CRYPTO_RWLOCK *rand_tmp_lock = NULL;
+static CRYPTO_ONCE rand_lock_init = CRYPTO_ONCE_STATIC_INIT;
+
+/* May be set only when a thread holds rand_lock (to prevent double locking) */
+static unsigned int crypto_lock_rand = 0;
+/* access to locking_threadid is synchronized by rand_tmp_lock */
+/* valid iff crypto_lock_rand is set */
+static CRYPTO_THREAD_ID locking_threadid;
+
+#ifdef PREDICT
+int rand_predictable = 0;
+#endif
+
+static int rand_hw_seed(EVP_MD_CTX *ctx);
+
+static void rand_cleanup(void);
+static int rand_seed(const void *buf, int num);
+static int rand_add(const void *buf, int num, double add_entropy);
+static int rand_bytes(unsigned char *buf, int num, int pseudo);
+static int rand_nopseudo_bytes(unsigned char *buf, int num);
+#if OPENSSL_API_COMPAT < 0x10100000L
+static int rand_pseudo_bytes(unsigned char *buf, int num);
+#endif
+static int rand_status(void);
+
+static RAND_METHOD rand_meth = {
+    rand_seed,
+    rand_nopseudo_bytes,
+    rand_cleanup,
+    rand_add,
+#if OPENSSL_API_COMPAT < 0x10100000L
+    rand_pseudo_bytes,
+#else
+    NULL,
+#endif
+    rand_status
+};
+
+DEFINE_RUN_ONCE_STATIC(do_rand_lock_init)
+{
+    OPENSSL_init_crypto(0, NULL);
+    rand_lock = CRYPTO_THREAD_lock_new();
+    rand_tmp_lock = CRYPTO_THREAD_lock_new();
+    return rand_lock != NULL && rand_tmp_lock != NULL;
+}
+
+RAND_METHOD *RAND_OpenSSL(void)
+{
+    return (&rand_meth);
+}
+
+static void rand_cleanup(void)
+{
+    OPENSSL_cleanse(state, sizeof(state));
+    state_num = 0;
+    state_index = 0;
+    OPENSSL_cleanse(md, MD_DIGEST_LENGTH);
+    md_count[0] = 0;
+    md_count[1] = 0;
+    entropy = 0;
+    initialized = 0;
+    CRYPTO_THREAD_lock_free(rand_lock);
+    CRYPTO_THREAD_lock_free(rand_tmp_lock);
+}
+
+static int rand_add(const void *buf, int num, double add)
+{
+    int i, j, k, st_idx;
+    long md_c[2];
+    unsigned char local_md[MD_DIGEST_LENGTH];
+    EVP_MD_CTX *m;
+    int do_not_lock;
+    int rv = 0;
+
+    if (!num)
+        return 1;
+
+    /*
+     * (Based on the rand(3) manpage)
+     *
+     * The input is chopped up into units of 20 bytes (or less for
+     * the last block).  Each of these blocks is run through the hash
+     * function as follows:  The data passed to the hash function
+     * is the current 'md', the same number of bytes from the 'state'
+     * (the location determined by in incremented looping index) as
+     * the current 'block', the new key data 'block', and 'count'
+     * (which is incremented after each use).
+     * The result of this is kept in 'md' and also xored into the
+     * 'state' at the same locations that were used as input into the
+     * hash function.
+     */
+
+    m = EVP_MD_CTX_new();
+    if (m == NULL)
+        goto err;
+
+    if (!RUN_ONCE(&rand_lock_init, do_rand_lock_init))
+        goto err;
+
+    /* check if we already have the lock */
+    if (crypto_lock_rand) {
+        CRYPTO_THREAD_ID cur = CRYPTO_THREAD_get_current_id();
+        CRYPTO_THREAD_read_lock(rand_tmp_lock);
+        do_not_lock = CRYPTO_THREAD_compare_id(locking_threadid, cur);
+        CRYPTO_THREAD_unlock(rand_tmp_lock);
+    } else
+        do_not_lock = 0;
+
+    if (!do_not_lock)
+        CRYPTO_THREAD_write_lock(rand_lock);
+    st_idx = state_index;
+
+    /*
+     * use our own copies of the counters so that even if a concurrent thread
+     * seeds with exactly the same data and uses the same subarray there's
+     * _some_ difference
+     */
+    md_c[0] = md_count[0];
+    md_c[1] = md_count[1];
+
+    memcpy(local_md, md, sizeof md);
+
+    /* state_index <= state_num <= STATE_SIZE */
+    state_index += num;
+    if (state_index >= STATE_SIZE) {
+        state_index %= STATE_SIZE;
+        state_num = STATE_SIZE;
+    } else if (state_num < STATE_SIZE) {
+        if (state_index > state_num)
+            state_num = state_index;
+    }
+    /* state_index <= state_num <= STATE_SIZE */
+
+    /*
+     * state[st_idx], ..., state[(st_idx + num - 1) % STATE_SIZE] are what we
+     * will use now, but other threads may use them as well
+     */
+
+    md_count[1] += (num / MD_DIGEST_LENGTH) + (num % MD_DIGEST_LENGTH > 0);
+
+    if (!do_not_lock)
+        CRYPTO_THREAD_unlock(rand_lock);
+
+    for (i = 0; i < num; i += MD_DIGEST_LENGTH) {
+        j = (num - i);
+        j = (j > MD_DIGEST_LENGTH) ? MD_DIGEST_LENGTH : j;
+
+        if (!MD_Init(m))
+            goto err;
+        if (!MD_Update(m, local_md, MD_DIGEST_LENGTH))
+            goto err;
+        k = (st_idx + j) - STATE_SIZE;
+        if (k > 0) {
+            if (!MD_Update(m, &(state[st_idx]), j - k))
+                goto err;
+            if (!MD_Update(m, &(state[0]), k))
+                goto err;
+        } else if (!MD_Update(m, &(state[st_idx]), j))
+            goto err;
+
+        /* DO NOT REMOVE THE FOLLOWING CALL TO MD_Update()! */
+        if (!MD_Update(m, buf, j))
+            goto err;
+        /*
+         * We know that line may cause programs such as purify and valgrind
+         * to complain about use of uninitialized data.  The problem is not,
+         * it's with the caller.  Removing that line will make sure you get
+         * really bad randomness and thereby other problems such as very
+         * insecure keys.
+         */
+
+        if (!MD_Update(m, (unsigned char *)&(md_c[0]), sizeof(md_c)))
+            goto err;
+        if (!MD_Final(m, local_md))
+            goto err;
+        md_c[1]++;
+
+        buf = (const char *)buf + j;
+
+        for (k = 0; k < j; k++) {
+            /*
+             * Parallel threads may interfere with this, but always each byte
+             * of the new state is the XOR of some previous value of its and
+             * local_md (intermediate values may be lost). Alway using locking
+             * could hurt performance more than necessary given that
+             * conflicts occur only when the total seeding is longer than the
+             * random state.
+             */
+            state[st_idx++] ^= local_md[k];
+            if (st_idx >= STATE_SIZE)
+                st_idx = 0;
+        }
+    }
+
+    if (!do_not_lock)
+        CRYPTO_THREAD_write_lock(rand_lock);
+    /*
+     * Don't just copy back local_md into md -- this could mean that other
+     * thread's seeding remains without effect (except for the incremented
+     * counter).  By XORing it we keep at least as much entropy as fits into
+     * md.
+     */
+    for (k = 0; k < (int)sizeof(md); k++) {
+        md[k] ^= local_md[k];
+    }
+    if (entropy < ENTROPY_NEEDED) /* stop counting when we have enough */
+        entropy += add;
+    if (!do_not_lock)
+        CRYPTO_THREAD_unlock(rand_lock);
+
+    rv = 1;
+ err:
+    EVP_MD_CTX_free(m);
+    return rv;
+}
+
+static int rand_seed(const void *buf, int num)
+{
+    return rand_add(buf, num, (double)num);
+}
+
+static int rand_bytes(unsigned char *buf, int num, int pseudo)
+{
+    static volatile int stirred_pool = 0;
+    int i, j, k;
+    size_t num_ceil, st_idx, st_num;
+    int ok;
+    long md_c[2];
+    unsigned char local_md[MD_DIGEST_LENGTH];
+    EVP_MD_CTX *m;
+#ifndef GETPID_IS_MEANINGLESS
+    pid_t curr_pid = getpid();
+#endif
+    time_t curr_time = time(NULL);
+    int do_stir_pool = 0;
+/* time value for various platforms */
+#ifdef OPENSSL_SYS_WIN32
+    FILETIME tv;
+# ifdef _WIN32_WCE
+    SYSTEMTIME t;
+    GetSystemTime(&t);
+    SystemTimeToFileTime(&t, &tv);
+# else
+    GetSystemTimeAsFileTime(&tv);
+# endif
+#elif defined(OPENSSL_SYS_VXWORKS)
+    struct timespec tv;
+    clock_gettime(CLOCK_REALTIME, &ts);
+#elif defined(OPENSSL_SYS_DSPBIOS)
+    unsigned long long tv, OPENSSL_rdtsc();
+    tv = OPENSSL_rdtsc();
+#else
+    struct timeval tv;
+    gettimeofday(&tv, NULL);
+#endif
+
+#ifdef PREDICT
+    if (rand_predictable) {
+        static unsigned char val = 0;
+
+        for (i = 0; i < num; i++)
+            buf[i] = val++;
+        return (1);
+    }
+#endif
+
+    if (num <= 0)
+        return 1;
+
+    m = EVP_MD_CTX_new();
+    if (m == NULL)
+        goto err_mem;
+
+    /* round upwards to multiple of MD_DIGEST_LENGTH/2 */
+    num_ceil =
+        (1 + (num - 1) / (MD_DIGEST_LENGTH / 2)) * (MD_DIGEST_LENGTH / 2);
+
+    /*
+     * (Based on the rand(3) manpage:)
+     *
+     * For each group of 10 bytes (or less), we do the following:
+     *
+     * Input into the hash function the local 'md' (which is initialized from
+     * the global 'md' before any bytes are generated), the bytes that are to
+     * be overwritten by the random bytes, and bytes from the 'state'
+     * (incrementing looping index). From this digest output (which is kept
+     * in 'md'), the top (up to) 10 bytes are returned to the caller and the
+     * bottom 10 bytes are xored into the 'state'.
+     *
+     * Finally, after we have finished 'num' random bytes for the
+     * caller, 'count' (which is incremented) and the local and global 'md'
+     * are fed into the hash function and the results are kept in the
+     * global 'md'.
+     */
+
+    if (!RUN_ONCE(&rand_lock_init, do_rand_lock_init))
+        goto err_mem;
+
+    CRYPTO_THREAD_write_lock(rand_lock);
+    /*
+     * We could end up in an async engine while holding this lock so ensure
+     * we don't pause and cause a deadlock
+     */
+    ASYNC_block_pause();
+
+    /* prevent rand_bytes() from trying to obtain the lock again */
+    CRYPTO_THREAD_write_lock(rand_tmp_lock);
+    locking_threadid = CRYPTO_THREAD_get_current_id();
+    CRYPTO_THREAD_unlock(rand_tmp_lock);
+    crypto_lock_rand = 1;
+
+    if (!initialized) {
+        RAND_poll();
+        initialized = 1;
+    }
+
+    if (!stirred_pool)
+        do_stir_pool = 1;
+
+    ok = (entropy >= ENTROPY_NEEDED);
+    if (!ok) {
+        /*
+         * If the PRNG state is not yet unpredictable, then seeing the PRNG
+         * output may help attackers to determine the new state; thus we have
+         * to decrease the entropy estimate. Once we've had enough initial
+         * seeding we don't bother to adjust the entropy count, though,
+         * because we're not ambitious to provide *information-theoretic*
+         * randomness. NOTE: This approach fails if the program forks before
+         * we have enough entropy. Entropy should be collected in a separate
+         * input pool and be transferred to the output pool only when the
+         * entropy limit has been reached.
+         */
+        entropy -= num;
+        if (entropy < 0)
+            entropy = 0;
+    }
+
+    if (do_stir_pool) {
+        /*
+         * In the output function only half of 'md' remains secret, so we
+         * better make sure that the required entropy gets 'evenly
+         * distributed' through 'state', our randomness pool. The input
+         * function (rand_add) chains all of 'md', which makes it more
+         * suitable for this purpose.
+         */
+
+        int n = STATE_SIZE;     /* so that the complete pool gets accessed */
+        while (n > 0) {
+#if MD_DIGEST_LENGTH > 32
+# error "Please adjust DUMMY_SEED."
+#endif
+#define DUMMY_SEED "................................" /* at least MD_DIGEST_LENGTH */
+            /*
+             * Note that the seed does not matter, it's just that
+             * rand_add expects to have something to hash.
+             */
+            rand_add(DUMMY_SEED, MD_DIGEST_LENGTH, 0.0);
+            n -= MD_DIGEST_LENGTH;
+        }
+        if (ok)
+            stirred_pool = 1;
+    }
+
+    st_idx = state_index;
+    st_num = state_num;
+    md_c[0] = md_count[0];
+    md_c[1] = md_count[1];
+    memcpy(local_md, md, sizeof md);
+
+    state_index += num_ceil;
+    if (state_index > state_num)
+        state_index %= state_num;
+
+    /*
+     * state[st_idx], ..., state[(st_idx + num_ceil - 1) % st_num] are now
+     * ours (but other threads may use them too)
+     */
+
+    md_count[0] += 1;
+
+    /* before unlocking, we must clear 'crypto_lock_rand' */
+    crypto_lock_rand = 0;
+    ASYNC_unblock_pause();
+    CRYPTO_THREAD_unlock(rand_lock);
+
+    while (num > 0) {
+        /* num_ceil -= MD_DIGEST_LENGTH/2 */
+        j = (num >= MD_DIGEST_LENGTH / 2) ? MD_DIGEST_LENGTH / 2 : num;
+        num -= j;
+        if (!MD_Init(m))
+            goto err;
+#ifndef GETPID_IS_MEANINGLESS
+        if (curr_pid) {         /* just in the first iteration to save time */
+            if (!MD_Update(m, (unsigned char *)&curr_pid, sizeof curr_pid))
+                goto err;
+            curr_pid = 0;
+        }
+#endif
+        if (curr_time) {        /* just in the first iteration to save time */
+            if (!MD_Update(m, (unsigned char *)&curr_time, sizeof curr_time))
+                goto err;
+            if (!MD_Update(m, (unsigned char *)&tv, sizeof tv))
+                goto err;
+            curr_time = 0;
+            if (!rand_hw_seed(m))
+                goto err;
+        }
+        if (!MD_Update(m, local_md, MD_DIGEST_LENGTH))
+            goto err;
+        if (!MD_Update(m, (unsigned char *)&(md_c[0]), sizeof(md_c)))
+            goto err;
+
+        k = (st_idx + MD_DIGEST_LENGTH / 2) - st_num;
+        if (k > 0) {
+            if (!MD_Update(m, &(state[st_idx]), MD_DIGEST_LENGTH / 2 - k))
+                goto err;
+            if (!MD_Update(m, &(state[0]), k))
+                goto err;
+        } else if (!MD_Update(m, &(state[st_idx]), MD_DIGEST_LENGTH / 2))
+            goto err;
+        if (!MD_Final(m, local_md))
+            goto err;
+
+        for (i = 0; i < MD_DIGEST_LENGTH / 2; i++) {
+            /* may compete with other threads */
+            state[st_idx++] ^= local_md[i];
+            if (st_idx >= st_num)
+                st_idx = 0;
+            if (i < j)
+                *(buf++) = local_md[i + MD_DIGEST_LENGTH / 2];
+        }
+    }
+
+    if (!MD_Init(m)
+        || !MD_Update(m, (unsigned char *)&(md_c[0]), sizeof(md_c))
+        || !MD_Update(m, local_md, MD_DIGEST_LENGTH))
+        goto err;
+    CRYPTO_THREAD_write_lock(rand_lock);
+    /*
+     * Prevent deadlocks if we end up in an async engine
+     */
+    ASYNC_block_pause();
+    if (!MD_Update(m, md, MD_DIGEST_LENGTH) || !MD_Final(m, md)) {
+        CRYPTO_THREAD_unlock(rand_lock);
+        goto err;
+    }
+    ASYNC_unblock_pause();
+    CRYPTO_THREAD_unlock(rand_lock);
+
+    EVP_MD_CTX_free(m);
+    if (ok)
+        return (1);
+    else if (pseudo)
+        return 0;
+    else {
+        RANDerr(RAND_F_RAND_BYTES, RAND_R_PRNG_NOT_SEEDED);
+        ERR_add_error_data(1, "You need to read the OpenSSL FAQ, "
+                           "https://www.openssl.org/docs/faq.html");
+        return (0);
+    }
+ err:
+    RANDerr(RAND_F_RAND_BYTES, ERR_R_EVP_LIB);
+    EVP_MD_CTX_free(m);
+    return 0;
+ err_mem:
+    RANDerr(RAND_F_RAND_BYTES, ERR_R_MALLOC_FAILURE);
+    EVP_MD_CTX_free(m);
+    return 0;
+
+}
+
+static int rand_nopseudo_bytes(unsigned char *buf, int num)
+{
+    return rand_bytes(buf, num, 0);
+}
+
+#if OPENSSL_API_COMPAT < 0x10100000L
+/*
+ * pseudo-random bytes that are guaranteed to be unique but not unpredictable
+ */
+static int rand_pseudo_bytes(unsigned char *buf, int num)
+{
+    return rand_bytes(buf, num, 1);
+}
+#endif
+
+static int rand_status(void)
+{
+    CRYPTO_THREAD_ID cur;
+    int ret;
+    int do_not_lock;
+
+    if (!RUN_ONCE(&rand_lock_init, do_rand_lock_init))
+        return 0;
+
+    cur = CRYPTO_THREAD_get_current_id();
+    /*
+     * check if we already have the lock (could happen if a RAND_poll()
+     * implementation calls RAND_status())
+     */
+    if (crypto_lock_rand) {
+        CRYPTO_THREAD_read_lock(rand_tmp_lock);
+        do_not_lock = CRYPTO_THREAD_compare_id(locking_threadid, cur);
+        CRYPTO_THREAD_unlock(rand_tmp_lock);
+    } else
+        do_not_lock = 0;
+
+    if (!do_not_lock) {
+        CRYPTO_THREAD_write_lock(rand_lock);
+        /*
+         * Prevent deadlocks in case we end up in an async engine
+         */
+        ASYNC_block_pause();
+
+        /*
+         * prevent rand_bytes() from trying to obtain the lock again
+         */
+        CRYPTO_THREAD_write_lock(rand_tmp_lock);
+        locking_threadid = cur;
+        CRYPTO_THREAD_unlock(rand_tmp_lock);
+        crypto_lock_rand = 1;
+    }
+
+    if (!initialized) {
+        RAND_poll();
+        initialized = 1;
+    }
+
+    ret = entropy >= ENTROPY_NEEDED;
+
+    if (!do_not_lock) {
+        /* before unlocking, we must clear 'crypto_lock_rand' */
+        crypto_lock_rand = 0;
+
+        ASYNC_unblock_pause();
+        CRYPTO_THREAD_unlock(rand_lock);
+    }
+
+    return ret;
+}
+
+/*
+ * rand_hw_seed: get seed data from any available hardware RNG. only
+ * currently supports rdrand.
+ */
+
+/* Adapted from eng_rdrand.c */
+
+#if (defined(__i386)   || defined(__i386__)   || defined(_M_IX86) || \
+     defined(__x86_64) || defined(__x86_64__) || \
+     defined(_M_AMD64) || defined (_M_X64)) && defined(OPENSSL_CPUID_OBJ) \
+     && !defined(OPENSSL_NO_RDRAND)
+
+# define RDRAND_CALLS    4
+
+size_t OPENSSL_ia32_rdrand(void);
+extern unsigned int OPENSSL_ia32cap_P[];
+
+static int rand_hw_seed(EVP_MD_CTX *ctx)
+{
+    int i;
+    if (!(OPENSSL_ia32cap_P[1] & (1 << (62 - 32))))
+        return 1;
+    for (i = 0; i < RDRAND_CALLS; i++) {
+        size_t rnd;
+        rnd = OPENSSL_ia32_rdrand();
+        if (rnd == 0)
+            return 1;
+        if (!MD_Update(ctx, (unsigned char *)&rnd, sizeof(size_t)))
+            return 0;
+    }
+    return 1;
+}
+
+/* XOR an existing buffer with random data */
+
+void rand_hw_xor(unsigned char *buf, size_t num)
+{
+    size_t rnd;
+    if (!(OPENSSL_ia32cap_P[1] & (1 << (62 - 32))))
+        return;
+    while (num >= sizeof(size_t)) {
+        rnd = OPENSSL_ia32_rdrand();
+        if (rnd == 0)
+            return;
+        *((size_t *)buf) ^= rnd;
+        buf += sizeof(size_t);
+        num -= sizeof(size_t);
+    }
+    if (num) {
+        rnd = OPENSSL_ia32_rdrand();
+        if (rnd == 0)
+            return;
+        while (num) {
+            *buf ^= rnd & 0xff;
+            rnd >>= 8;
+            buf++;
+            num--;
+        }
+    }
+}
+
+#else
+
+static int rand_hw_seed(EVP_MD_CTX *ctx)
+{
+    return 1;
+}
+
+void rand_hw_xor(unsigned char *buf, size_t num)
+{
+    return;
+}
+
+#endif
diff --git a/crypto/rand/rand_egd.c b/crypto/rand/rand_egd.c
new file mode 100644
index 000000000..dd58b2149
--- /dev/null
+++ b/crypto/rand/rand_egd.c
@@ -0,0 +1,249 @@
+/*
+ * Copyright 2000-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#ifdef OPENSSL_NO_EGD
+NON_EMPTY_TRANSLATION_UNIT
+#else
+
+# include 
+# include 
+# include 
+
+/*-
+ * Query the EGD .
+ *
+ * This module supplies three routines:
+ *
+ * RAND_query_egd_bytes(path, buf, bytes)
+ *   will actually query "bytes" bytes of entropy form the egd-socket located
+ *   at path and will write them to buf (if supplied) or will directly feed
+ *   it to RAND_seed() if buf==NULL.
+ *   The number of bytes is not limited by the maximum chunk size of EGD,
+ *   which is 255 bytes. If more than 255 bytes are wanted, several chunks
+ *   of entropy bytes are requested. The connection is left open until the
+ *   query is competed.
+ *   RAND_query_egd_bytes() returns with
+ *     -1  if an error occurred during connection or communication.
+ *     num the number of bytes read from the EGD socket. This number is either
+ *         the number of bytes requested or smaller, if the EGD pool is
+ *         drained and the daemon signals that the pool is empty.
+ *   This routine does not touch any RAND_status(). This is necessary, since
+ *   PRNG functions may call it during initialization.
+ *
+ * RAND_egd_bytes(path, bytes) will query "bytes" bytes and have them
+ *   used to seed the PRNG.
+ *   RAND_egd_bytes() is a wrapper for RAND_query_egd_bytes() with buf=NULL.
+ *   Unlike RAND_query_egd_bytes(), RAND_status() is used to test the
+ *   seed status so that the return value can reflect the seed state:
+ *     -1  if an error occurred during connection or communication _or_
+ *         if the PRNG has still not received the required seeding.
+ *     num the number of bytes read from the EGD socket. This number is either
+ *         the number of bytes requested or smaller, if the EGD pool is
+ *         drained and the daemon signals that the pool is empty.
+ *
+ * RAND_egd(path) will query 255 bytes and use the bytes retrieved to seed
+ *   the PRNG.
+ *   RAND_egd() is a wrapper for RAND_egd_bytes() with numbytes=255.
+ */
+
+# if defined(OPENSSL_SYS_WIN32) || defined(OPENSSL_SYS_VMS) || defined(OPENSSL_SYS_MSDOS) || defined(OPENSSL_SYS_VXWORKS) || defined(OPENSSL_SYS_VOS) || defined(OPENSSL_SYS_UEFI)
+int RAND_query_egd_bytes(const char *path, unsigned char *buf, int bytes)
+{
+    return (-1);
+}
+
+int RAND_egd(const char *path)
+{
+    return (-1);
+}
+
+int RAND_egd_bytes(const char *path, int bytes)
+{
+    return (-1);
+}
+# else
+#  include 
+#  include OPENSSL_UNISTD
+#  include 
+#  include 
+#  include 
+#  ifndef NO_SYS_UN_H
+#   ifdef OPENSSL_SYS_VXWORKS
+#    include 
+#   else
+#    include 
+#   endif
+#  else
+struct sockaddr_un {
+    short sun_family;           /* AF_UNIX */
+    char sun_path[108];         /* path name (gag) */
+};
+#  endif                         /* NO_SYS_UN_H */
+#  include 
+#  include 
+
+int RAND_query_egd_bytes(const char *path, unsigned char *buf, int bytes)
+{
+    int ret = 0;
+    struct sockaddr_un addr;
+    int len, num, numbytes;
+    int fd = -1;
+    int success;
+    unsigned char egdbuf[2], tempbuf[255], *retrievebuf;
+
+    memset(&addr, 0, sizeof(addr));
+    addr.sun_family = AF_UNIX;
+    if (strlen(path) >= sizeof(addr.sun_path))
+        return (-1);
+    OPENSSL_strlcpy(addr.sun_path, path, sizeof addr.sun_path);
+    len = offsetof(struct sockaddr_un, sun_path) + strlen(path);
+    fd = socket(AF_UNIX, SOCK_STREAM, 0);
+    if (fd == -1)
+        return (-1);
+    success = 0;
+    while (!success) {
+        if (connect(fd, (struct sockaddr *)&addr, len) == 0)
+            success = 1;
+        else {
+            switch (errno) {
+#  ifdef EINTR
+            case EINTR:
+#  endif
+#  ifdef EAGAIN
+            case EAGAIN:
+#  endif
+#  ifdef EINPROGRESS
+            case EINPROGRESS:
+#  endif
+#  ifdef EALREADY
+            case EALREADY:
+#  endif
+                /* No error, try again */
+                break;
+#  ifdef EISCONN
+            case EISCONN:
+                success = 1;
+                break;
+#  endif
+            default:
+                ret = -1;
+                goto err;       /* failure */
+            }
+        }
+    }
+
+    while (bytes > 0) {
+        egdbuf[0] = 1;
+        egdbuf[1] = bytes < 255 ? bytes : 255;
+        numbytes = 0;
+        while (numbytes != 2) {
+            num = write(fd, egdbuf + numbytes, 2 - numbytes);
+            if (num >= 0)
+                numbytes += num;
+            else {
+                switch (errno) {
+#  ifdef EINTR
+                case EINTR:
+#  endif
+#  ifdef EAGAIN
+                case EAGAIN:
+#  endif
+                    /* No error, try again */
+                    break;
+                default:
+                    ret = -1;
+                    goto err;   /* failure */
+                }
+            }
+        }
+        numbytes = 0;
+        while (numbytes != 1) {
+            num = read(fd, egdbuf, 1);
+            if (num == 0)
+                goto err;       /* descriptor closed */
+            else if (num > 0)
+                numbytes += num;
+            else {
+                switch (errno) {
+#  ifdef EINTR
+                case EINTR:
+#  endif
+#  ifdef EAGAIN
+                case EAGAIN:
+#  endif
+                    /* No error, try again */
+                    break;
+                default:
+                    ret = -1;
+                    goto err;   /* failure */
+                }
+            }
+        }
+        if (egdbuf[0] == 0)
+            goto err;
+        if (buf)
+            retrievebuf = buf + ret;
+        else
+            retrievebuf = tempbuf;
+        numbytes = 0;
+        while (numbytes != egdbuf[0]) {
+            num = read(fd, retrievebuf + numbytes, egdbuf[0] - numbytes);
+            if (num == 0)
+                goto err;       /* descriptor closed */
+            else if (num > 0)
+                numbytes += num;
+            else {
+                switch (errno) {
+#  ifdef EINTR
+                case EINTR:
+#  endif
+#  ifdef EAGAIN
+                case EAGAIN:
+#  endif
+                    /* No error, try again */
+                    break;
+                default:
+                    ret = -1;
+                    goto err;   /* failure */
+                }
+            }
+        }
+        ret += egdbuf[0];
+        bytes -= egdbuf[0];
+        if (!buf)
+            RAND_seed(tempbuf, egdbuf[0]);
+    }
+ err:
+    if (fd != -1)
+        close(fd);
+    return (ret);
+}
+
+int RAND_egd_bytes(const char *path, int bytes)
+{
+    int num, ret = -1;
+
+    num = RAND_query_egd_bytes(path, NULL, bytes);
+    if (num < 0)
+        goto err;
+    if (RAND_status() == 1)
+        ret = num;
+ err:
+    return (ret);
+}
+
+int RAND_egd(const char *path)
+{
+    return (RAND_egd_bytes(path, 255));
+}
+
+# endif
+
+#endif
diff --git a/crypto/rand/rand_err.c b/crypto/rand/rand_err.c
new file mode 100644
index 000000000..55431264a
--- /dev/null
+++ b/crypto/rand/rand_err.c
@@ -0,0 +1,43 @@
+/*
+ * Generated by util/mkerr.pl DO NOT EDIT
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include 
+
+/* BEGIN ERROR CODES */
+#ifndef OPENSSL_NO_ERR
+
+# define ERR_FUNC(func) ERR_PACK(ERR_LIB_RAND,func,0)
+# define ERR_REASON(reason) ERR_PACK(ERR_LIB_RAND,0,reason)
+
+static ERR_STRING_DATA RAND_str_functs[] = {
+    {ERR_FUNC(RAND_F_RAND_BYTES), "RAND_bytes"},
+    {0, NULL}
+};
+
+static ERR_STRING_DATA RAND_str_reasons[] = {
+    {ERR_REASON(RAND_R_PRNG_NOT_SEEDED), "PRNG not seeded"},
+    {0, NULL}
+};
+
+#endif
+
+int ERR_load_RAND_strings(void)
+{
+#ifndef OPENSSL_NO_ERR
+
+    if (ERR_func_error_string(RAND_str_functs[0].error) == NULL) {
+        ERR_load_strings(0, RAND_str_functs);
+        ERR_load_strings(0, RAND_str_reasons);
+    }
+#endif
+    return 1;
+}
diff --git a/crypto/rand/rand_extra/deterministic.c b/crypto/rand/rand_extra/deterministic.c
new file mode 100644
index 000000000..5d3a9ce49
--- /dev/null
+++ b/crypto/rand/rand_extra/deterministic.c
@@ -0,0 +1,48 @@
+/* Copyright (c) 2016, Google Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
+
+#include 
+
+#if defined(BORINGSSL_UNSAFE_DETERMINISTIC_MODE)
+
+#include 
+
+#include 
+
+#include "../internal.h"
+#include "../fipsmodule/rand/internal.h"
+
+
+/* g_num_calls is the number of calls to |CRYPTO_sysrand| that have occurred.
+ *
+ * This is intentionally not thread-safe. If the fuzzer mode is ever used in a
+ * multi-threaded program, replace this with a thread-local. (A mutex would not
+ * be deterministic.) */
+static uint64_t g_num_calls = 0;
+
+void RAND_reset_for_fuzzing(void) { g_num_calls = 0; }
+
+void CRYPTO_sysrand(uint8_t *out, size_t requested) {
+  static const uint8_t kZeroKey[32];
+
+  uint8_t nonce[12];
+  OPENSSL_memset(nonce, 0, sizeof(nonce));
+  OPENSSL_memcpy(nonce, &g_num_calls, sizeof(g_num_calls));
+
+  OPENSSL_memset(out, 0, requested);
+  CRYPTO_chacha_20(out, out, requested, kZeroKey, nonce, 0);
+  g_num_calls++;
+}
+
+#endif  /* BORINGSSL_UNSAFE_DETERMINISTIC_MODE */
diff --git a/crypto/rand/rand_extra/forkunsafe.c b/crypto/rand/rand_extra/forkunsafe.c
new file mode 100644
index 000000000..1a3977f87
--- /dev/null
+++ b/crypto/rand/rand_extra/forkunsafe.c
@@ -0,0 +1,44 @@
+/* Copyright (c) 2017, Google Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
+
+#include 
+
+#include 
+
+#include "../fipsmodule/rand/internal.h"
+
+
+/* g_buffering_enabled is true if fork-unsafe buffering has been enabled. */
+static int g_buffering_enabled = 0;
+
+/* g_lock protects |g_buffering_enabled|. */
+static struct CRYPTO_STATIC_MUTEX g_lock = CRYPTO_STATIC_MUTEX_INIT;
+
+void RAND_enable_fork_unsafe_buffering(int fd) {
+  /* We no longer support setting the file-descriptor with this function. */
+  if (fd != -1) {
+    abort();
+  }
+
+  CRYPTO_STATIC_MUTEX_lock_write(&g_lock);
+  g_buffering_enabled = 1;
+  CRYPTO_STATIC_MUTEX_unlock_write(&g_lock);
+}
+
+int rand_fork_unsafe_buffering_enabled(void) {
+  CRYPTO_STATIC_MUTEX_lock_read(&g_lock);
+  const int ret = g_buffering_enabled;
+  CRYPTO_STATIC_MUTEX_unlock_read(&g_lock);
+  return ret;
+}
diff --git a/crypto/rand/rand_extra/fuchsia.c b/crypto/rand/rand_extra/fuchsia.c
new file mode 100644
index 000000000..fae29894f
--- /dev/null
+++ b/crypto/rand/rand_extra/fuchsia.c
@@ -0,0 +1,43 @@
+/* Copyright (c) 2017, Google Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
+
+#include 
+
+#if defined(OPENSSL_FUCHSIA) && !defined(BORINGSSL_UNSAFE_DETERMINISTIC_MODE)
+
+#include 
+#include 
+
+#include 
+
+#include "../fipsmodule/rand/internal.h"
+
+void CRYPTO_sysrand(uint8_t *out, size_t requested) {
+  while (requested > 0) {
+    size_t output_bytes_this_pass = MX_CPRNG_DRAW_MAX_LEN;
+    if (requested < output_bytes_this_pass) {
+      output_bytes_this_pass = requested;
+    }
+    size_t bytes_drawn;
+    mx_status_t status =
+        mx_cprng_draw(out, output_bytes_this_pass, &bytes_drawn);
+    if (status != NO_ERROR) {
+      abort();
+    }
+    requested -= bytes_drawn;
+    out += bytes_drawn;
+  }
+}
+
+#endif /* OPENSSL_FUCHSIA && !BORINGSSL_UNSAFE_DETERMINISTIC_MODE */
diff --git a/crypto/rand/rand_extra/windows.c b/crypto/rand/rand_extra/windows.c
new file mode 100644
index 000000000..fb9484726
--- /dev/null
+++ b/crypto/rand/rand_extra/windows.c
@@ -0,0 +1,53 @@
+/* Copyright (c) 2014, Google Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
+
+#include 
+
+#if defined(OPENSSL_WINDOWS) && !defined(BORINGSSL_UNSAFE_DETERMINISTIC_MODE)
+
+#include 
+#include 
+
+OPENSSL_MSVC_PRAGMA(warning(push, 3))
+
+#include 
+
+/* #define needed to link in RtlGenRandom(), a.k.a. SystemFunction036.  See the
+ * "Community Additions" comment on MSDN here:
+ * http://msdn.microsoft.com/en-us/library/windows/desktop/aa387694.aspx */
+#define SystemFunction036 NTAPI SystemFunction036
+#include 
+#undef SystemFunction036
+
+OPENSSL_MSVC_PRAGMA(warning(pop))
+
+#include "../fipsmodule/rand/internal.h"
+
+
+void CRYPTO_sysrand(uint8_t *out, size_t requested) {
+  while (requested > 0) {
+    ULONG output_bytes_this_pass = ULONG_MAX;
+    if (requested < output_bytes_this_pass) {
+      output_bytes_this_pass = (ULONG)requested;
+    }
+    if (RtlGenRandom(out, output_bytes_this_pass) == FALSE) {
+      abort();
+    }
+    requested -= output_bytes_this_pass;
+    out += output_bytes_this_pass;
+  }
+  return;
+}
+
+#endif  /* OPENSSL_WINDOWS && !BORINGSSL_UNSAFE_DETERMINISTIC_MODE */
diff --git a/crypto/rand/rand_lcl.h b/crypto/rand/rand_lcl.h
new file mode 100644
index 000000000..f97a9de94
--- /dev/null
+++ b/crypto/rand/rand_lcl.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#ifndef HEADER_RAND_LCL_H
+# define HEADER_RAND_LCL_H
+
+# define ENTROPY_NEEDED 32      /* require 256 bits = 32 bytes of randomness */
+
+# if !defined(USE_MD5_RAND) && !defined(USE_SHA1_RAND) && !defined(USE_MDC2_RAND) && !defined(USE_MD2_RAND)
+#  define USE_SM3_RAND
+# endif
+
+# include 
+# define MD_Update(a,b,c)        EVP_DigestUpdate(a,b,c)
+# define MD_Final(a,b)           EVP_DigestFinal_ex(a,b,NULL)
+# if defined(USE_SM3_RAND)
+#  include 
+#  define MD_DIGEST_LENGTH        SM3_DIGEST_LENGTH
+#  define MD_Init(a)              EVP_DigestInit_ex(a,EVP_sm3(), NULL)
+#  define MD(a,b,c)               EVP_Digest(a,b,c,NULL,EVP_sm3(), NULL)
+# elif defined(USE_MD5_RAND)
+#  include 
+#  define MD_DIGEST_LENGTH        MD5_DIGEST_LENGTH
+#  define MD_Init(a)              EVP_DigestInit_ex(a,EVP_md5(), NULL)
+#  define MD(a,b,c)               EVP_Digest(a,b,c,NULL,EVP_md5(), NULL)
+# elif defined(USE_SHA1_RAND)
+#  include 
+#  define MD_DIGEST_LENGTH        SHA_DIGEST_LENGTH
+#  define MD_Init(a)              EVP_DigestInit_ex(a,EVP_sha1(), NULL)
+#  define MD(a,b,c)               EVP_Digest(a,b,c,NULL,EVP_sha1(), NULL)
+# elif defined(USE_MDC2_RAND)
+#  include 
+#  define MD_DIGEST_LENGTH        MDC2_DIGEST_LENGTH
+#  define MD_Init(a)              EVP_DigestInit_ex(a,EVP_mdc2(), NULL)
+#  define MD(a,b,c)               EVP_Digest(a,b,c,NULL,EVP_mdc2(), NULL)
+# elif defined(USE_MD2_RAND)
+#  include 
+#  define MD_DIGEST_LENGTH        MD2_DIGEST_LENGTH
+#  define MD_Init(a)              EVP_DigestInit_ex(a,EVP_md2(), NULL)
+#  define MD(a,b,c)               EVP_Digest(a,b,c,NULL,EVP_md2(), NULL)
+# endif
+
+void rand_hw_xor(unsigned char *buf, size_t num);
+
+#endif
diff --git a/crypto/rand/rand_lib.c b/crypto/rand/rand_lib.c
new file mode 100644
index 000000000..dccdf2ca4
--- /dev/null
+++ b/crypto/rand/rand_lib.c
@@ -0,0 +1,128 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include "internal/rand.h"
+
+#ifndef OPENSSL_NO_ENGINE
+# include 
+#endif
+
+#ifdef OPENSSL_FIPS
+# include 
+# include 
+#endif
+
+#ifndef OPENSSL_NO_ENGINE
+/* non-NULL if default_RAND_meth is ENGINE-provided */
+static ENGINE *funct_ref = NULL;
+#endif
+static const RAND_METHOD *default_RAND_meth = NULL;
+
+int RAND_set_rand_method(const RAND_METHOD *meth)
+{
+#ifndef OPENSSL_NO_ENGINE
+    ENGINE_finish(funct_ref);
+    funct_ref = NULL;
+#endif
+    default_RAND_meth = meth;
+    return 1;
+}
+
+const RAND_METHOD *RAND_get_rand_method(void)
+{
+    if (!default_RAND_meth) {
+#ifndef OPENSSL_NO_ENGINE
+        ENGINE *e = ENGINE_get_default_RAND();
+        if (e) {
+            default_RAND_meth = ENGINE_get_RAND(e);
+            if (default_RAND_meth == NULL) {
+                ENGINE_finish(e);
+                e = NULL;
+            }
+        }
+        if (e)
+            funct_ref = e;
+        else
+#endif
+            default_RAND_meth = RAND_OpenSSL();
+    }
+    return default_RAND_meth;
+}
+
+#ifndef OPENSSL_NO_ENGINE
+int RAND_set_rand_engine(ENGINE *engine)
+{
+    const RAND_METHOD *tmp_meth = NULL;
+    if (engine) {
+        if (!ENGINE_init(engine))
+            return 0;
+        tmp_meth = ENGINE_get_RAND(engine);
+        if (tmp_meth == NULL) {
+            ENGINE_finish(engine);
+            return 0;
+        }
+    }
+    /* This function releases any prior ENGINE so call it first */
+    RAND_set_rand_method(tmp_meth);
+    funct_ref = engine;
+    return 1;
+}
+#endif
+
+void rand_cleanup_int(void)
+{
+    const RAND_METHOD *meth = RAND_get_rand_method();
+    if (meth && meth->cleanup)
+        meth->cleanup();
+    RAND_set_rand_method(NULL);
+}
+
+void RAND_seed(const void *buf, int num)
+{
+    const RAND_METHOD *meth = RAND_get_rand_method();
+    if (meth && meth->seed)
+        meth->seed(buf, num);
+}
+
+void RAND_add(const void *buf, int num, double entropy)
+{
+    const RAND_METHOD *meth = RAND_get_rand_method();
+    if (meth && meth->add)
+        meth->add(buf, num, entropy);
+}
+
+int RAND_bytes(unsigned char *buf, int num)
+{
+    const RAND_METHOD *meth = RAND_get_rand_method();
+    if (meth && meth->bytes)
+        return meth->bytes(buf, num);
+    return (-1);
+}
+
+#if OPENSSL_API_COMPAT < 0x10100000L
+int RAND_pseudo_bytes(unsigned char *buf, int num)
+{
+    const RAND_METHOD *meth = RAND_get_rand_method();
+    if (meth && meth->pseudorand)
+        return meth->pseudorand(buf, num);
+    return (-1);
+}
+#endif
+
+int RAND_status(void)
+{
+    const RAND_METHOD *meth = RAND_get_rand_method();
+    if (meth && meth->status)
+        return meth->status();
+    return 0;
+}
diff --git a/crypto/rand/rand_unix.c b/crypto/rand/rand_unix.c
new file mode 100644
index 000000000..ecba2dc98
--- /dev/null
+++ b/crypto/rand/rand_unix.c
@@ -0,0 +1,324 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+
+#define USE_SOCKETS
+#include "e_os.h"
+#include "internal/cryptlib.h"
+#include 
+#include "rand_lcl.h"
+
+#if !(defined(OPENSSL_SYS_WINDOWS) || defined(OPENSSL_SYS_WIN32) || defined(OPENSSL_SYS_VMS) || defined(OPENSSL_SYS_VXWORKS) || defined(OPENSSL_SYS_UEFI))
+
+# include 
+# include 
+# include 
+# include 
+# include 
+# include 
+# include 
+# if defined(OPENSSL_SYS_LINUX) /* should actually be available virtually
+                                 * everywhere */
+#  include 
+# endif
+# include 
+# ifndef FD_SETSIZE
+#  define FD_SETSIZE (8*sizeof(fd_set))
+# endif
+
+# if defined(OPENSSL_SYS_VOS)
+
+/*
+ * The following algorithm repeatedly samples the real-time clock (RTC) to
+ * generate a sequence of unpredictable data.  The algorithm relies upon the
+ * uneven execution speed of the code (due to factors such as cache misses,
+ * interrupts, bus activity, and scheduling) and upon the rather large
+ * relative difference between the speed of the clock and the rate at which
+ * it can be read.
+ *
+ * If this code is ported to an environment where execution speed is more
+ * constant or where the RTC ticks at a much slower rate, or the clock can be
+ * read with fewer instructions, it is likely that the results would be far
+ * more predictable.
+ *
+ * As a precaution, we generate 4 times the minimum required amount of seed
+ * data.
+ */
+
+int RAND_poll(void)
+{
+    short int code;
+    gid_t curr_gid;
+    pid_t curr_pid;
+    uid_t curr_uid;
+    int i, k;
+    struct timespec ts;
+    unsigned char v;
+
+#  ifdef OPENSSL_SYS_VOS_HPPA
+    long duration;
+    extern void s$sleep(long *_duration, short int *_code);
+#  else
+#   ifdef OPENSSL_SYS_VOS_IA32
+    long long duration;
+    extern void s$sleep2(long long *_duration, short int *_code);
+#   else
+#    error "Unsupported Platform."
+#   endif                       /* OPENSSL_SYS_VOS_IA32 */
+#  endif                        /* OPENSSL_SYS_VOS_HPPA */
+
+    /*
+     * Seed with the gid, pid, and uid, to ensure *some* variation between
+     * different processes.
+     */
+
+    curr_gid = getgid();
+    RAND_add(&curr_gid, sizeof curr_gid, 1);
+    curr_gid = 0;
+
+    curr_pid = getpid();
+    RAND_add(&curr_pid, sizeof curr_pid, 1);
+    curr_pid = 0;
+
+    curr_uid = getuid();
+    RAND_add(&curr_uid, sizeof curr_uid, 1);
+    curr_uid = 0;
+
+    for (i = 0; i < (ENTROPY_NEEDED * 4); i++) {
+        /*
+         * burn some cpu; hope for interrupts, cache collisions, bus
+         * interference, etc.
+         */
+        for (k = 0; k < 99; k++)
+            ts.tv_nsec = random();
+
+#  ifdef OPENSSL_SYS_VOS_HPPA
+        /* sleep for 1/1024 of a second (976 us).  */
+        duration = 1;
+        s$sleep(&duration, &code);
+#  else
+#   ifdef OPENSSL_SYS_VOS_IA32
+        /* sleep for 1/65536 of a second (15 us).  */
+        duration = 1;
+        s$sleep2(&duration, &code);
+#   endif                       /* OPENSSL_SYS_VOS_IA32 */
+#  endif                        /* OPENSSL_SYS_VOS_HPPA */
+
+        /* get wall clock time.  */
+        clock_gettime(CLOCK_REALTIME, &ts);
+
+        /* take 8 bits */
+        v = (unsigned char)(ts.tv_nsec % 256);
+        RAND_add(&v, sizeof v, 1);
+        v = 0;
+    }
+    return 1;
+}
+# elif defined __OpenBSD__
+int RAND_poll(void)
+{
+    u_int32_t rnd = 0, i;
+    unsigned char buf[ENTROPY_NEEDED];
+
+    for (i = 0; i < sizeof(buf); i++) {
+        if (i % 4 == 0)
+            rnd = arc4random();
+        buf[i] = rnd;
+        rnd >>= 8;
+    }
+    RAND_add(buf, sizeof(buf), ENTROPY_NEEDED);
+    OPENSSL_cleanse(buf, sizeof(buf));
+
+    return 1;
+}
+# else                          /* !defined(__OpenBSD__) */
+int RAND_poll(void)
+{
+    unsigned long l;
+    pid_t curr_pid = getpid();
+#  if defined(DEVRANDOM) || (!defined(OPENSS_NO_EGD) && defined(DEVRANDOM_EGD))
+    unsigned char tmpbuf[ENTROPY_NEEDED];
+    int n = 0;
+#  endif
+#  ifdef DEVRANDOM
+    static const char *randomfiles[] = { DEVRANDOM };
+    struct stat randomstats[OSSL_NELEM(randomfiles)];
+    int fd;
+    unsigned int i;
+#  endif
+#  if !defined(OPENSSL_NO_EGD) && defined(DEVRANDOM_EGD)
+    static const char *egdsockets[] = { DEVRANDOM_EGD, NULL };
+    const char **egdsocket = NULL;
+#  endif
+
+#  ifdef DEVRANDOM
+    memset(randomstats, 0, sizeof(randomstats));
+    /*
+     * Use a random entropy pool device. Linux, FreeBSD and OpenBSD have
+     * this. Use /dev/urandom if you can as /dev/random may block if it runs
+     * out of random entries.
+     */
+
+    for (i = 0; (i < OSSL_NELEM(randomfiles)) && (n < ENTROPY_NEEDED); i++) {
+        if ((fd = open(randomfiles[i], O_RDONLY
+#   ifdef O_NONBLOCK
+                       | O_NONBLOCK
+#   endif
+#   ifdef O_BINARY
+                       | O_BINARY
+#   endif
+#   ifdef O_NOCTTY              /* If it happens to be a TTY (god forbid), do
+                                 * not make it our controlling tty */
+                       | O_NOCTTY
+#   endif
+             )) >= 0) {
+            int usec = 10 * 1000; /* spend 10ms on each file */
+            int r;
+            unsigned int j;
+            struct stat *st = &randomstats[i];
+
+            /*
+             * Avoid using same input... Used to be O_NOFOLLOW above, but
+             * it's not universally appropriate...
+             */
+            if (fstat(fd, st) != 0) {
+                close(fd);
+                continue;
+            }
+            for (j = 0; j < i; j++) {
+                if (randomstats[j].st_ino == st->st_ino &&
+                    randomstats[j].st_dev == st->st_dev)
+                    break;
+            }
+            if (j < i) {
+                close(fd);
+                continue;
+            }
+
+            do {
+                int try_read = 0;
+
+#   if defined(OPENSSL_SYS_LINUX)
+                /* use poll() */
+                struct pollfd pset;
+
+                pset.fd = fd;
+                pset.events = POLLIN;
+                pset.revents = 0;
+
+                if (poll(&pset, 1, usec / 1000) < 0)
+                    usec = 0;
+                else
+                    try_read = (pset.revents & POLLIN) != 0;
+
+#   else
+                /* use select() */
+                fd_set fset;
+                struct timeval t;
+
+                t.tv_sec = 0;
+                t.tv_usec = usec;
+
+                if (FD_SETSIZE > 0 && (unsigned)fd >= FD_SETSIZE) {
+                    /*
+                     * can't use select, so just try to read once anyway
+                     */
+                    try_read = 1;
+                } else {
+                    FD_ZERO(&fset);
+                    FD_SET(fd, &fset);
+
+                    if (select(fd + 1, &fset, NULL, NULL, &t) >= 0) {
+                        usec = t.tv_usec;
+                        if (FD_ISSET(fd, &fset))
+                            try_read = 1;
+                    } else
+                        usec = 0;
+                }
+#   endif
+
+                if (try_read) {
+                    r = read(fd, (unsigned char *)tmpbuf + n,
+                             ENTROPY_NEEDED - n);
+                    if (r > 0)
+                        n += r;
+                } else
+                    r = -1;
+
+                /*
+                 * Some Unixen will update t in select(), some won't.  For
+                 * those who won't, or if we didn't use select() in the first
+                 * place, give up here, otherwise, we will do this once again
+                 * for the remaining time.
+                 */
+                if (usec == 10 * 1000)
+                    usec = 0;
+            }
+            while ((r > 0 ||
+                    (errno == EINTR || errno == EAGAIN)) && usec != 0
+                   && n < ENTROPY_NEEDED);
+
+            close(fd);
+        }
+    }
+#  endif                        /* defined(DEVRANDOM) */
+
+#  if !defined(OPENSSL_NO_EGD) && defined(DEVRANDOM_EGD)
+    /*
+     * Use an EGD socket to read entropy from an EGD or PRNGD entropy
+     * collecting daemon.
+     */
+
+    for (egdsocket = egdsockets; *egdsocket && n < ENTROPY_NEEDED;
+         egdsocket++) {
+        int r;
+
+        r = RAND_query_egd_bytes(*egdsocket, (unsigned char *)tmpbuf + n,
+                                 ENTROPY_NEEDED - n);
+        if (r > 0)
+            n += r;
+    }
+#  endif                        /* defined(DEVRANDOM_EGD) */
+
+#  if defined(DEVRANDOM) || (!defined(OPENSSL_NO_EGD) && defined(DEVRANDOM_EGD))
+    if (n > 0) {
+        RAND_add(tmpbuf, sizeof tmpbuf, (double)n);
+        OPENSSL_cleanse(tmpbuf, n);
+    }
+#  endif
+
+    /* put in some default random data, we need more than just this */
+    l = curr_pid;
+    RAND_add(&l, sizeof(l), 0.0);
+    l = getuid();
+    RAND_add(&l, sizeof(l), 0.0);
+
+    l = time(NULL);
+    RAND_add(&l, sizeof(l), 0.0);
+
+#  if defined(DEVRANDOM) || (!defined(OPENSSL_NO_EGD) && defined(DEVRANDOM_EGD))
+    return 1;
+#  else
+    return 0;
+#  endif
+}
+
+# endif                         /* defined(__OpenBSD__) */
+#endif                          /* !(defined(OPENSSL_SYS_WINDOWS) ||
+                                 * defined(OPENSSL_SYS_WIN32) ||
+                                 * defined(OPENSSL_SYS_VMS) ||
+                                 * defined(OPENSSL_SYS_VXWORKS) */
+
+#if defined(OPENSSL_SYS_VXWORKS) || defined(OPENSSL_SYS_UEFI)
+int RAND_poll(void)
+{
+    return 0;
+}
+#endif
diff --git a/crypto/rand/rand_vms.c b/crypto/rand/rand_vms.c
new file mode 100644
index 000000000..9c462dd37
--- /dev/null
+++ b/crypto/rand/rand_vms.c
@@ -0,0 +1,133 @@
+/*
+ * Copyright 2001-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * Modified by VMS Software, Inc (2016)
+ *    Eliminate looping through all processes (performance)
+ *    Add additional randomizations using rand() function
+ */
+
+#include 
+#include "rand_lcl.h"
+
+#if defined(OPENSSL_SYS_VMS)
+# include 
+# include 
+# include 
+# include 
+# include 
+# ifdef __DECC
+#  pragma message disable DOLLARID
+# endif
+
+/*
+ * Use 32-bit pointers almost everywhere.  Define the type to which to cast a
+ * pointer passed to an external function.
+ */
+# if __INITIAL_POINTER_SIZE == 64
+#  define PTR_T __void_ptr64
+#  pragma pointer_size save
+#  pragma pointer_size 32
+# else                          /* __INITIAL_POINTER_SIZE == 64 */
+#  define PTR_T void *
+# endif                         /* __INITIAL_POINTER_SIZE == 64 [else] */
+
+static struct items_data_st {
+    short length, code;         /* length is number of bytes */
+} items_data[] = {
+    {4, JPI$_BUFIO},
+    {4, JPI$_CPUTIM},
+    {4, JPI$_DIRIO},
+    {4, JPI$_IMAGECOUNT},
+    {8, JPI$_LAST_LOGIN_I},
+    {8, JPI$_LOGINTIM},
+    {4, JPI$_PAGEFLTS},
+    {4, JPI$_PID},
+    {4, JPI$_PPGCNT},
+    {4, JPI$_WSPEAK},
+    {4, JPI$_FINALEXC},
+    {0, 0}                      /* zero terminated */
+};
+
+int RAND_poll(void)
+{
+
+    /* determine the number of items in the JPI array */
+
+    struct items_data_st item_entry;
+    int item_entry_count = sizeof(items_data)/sizeof(item_entry);
+
+    /* Create the JPI itemlist array to hold item_data content */
+
+    struct {
+        short length, code;
+        int *buffer;
+        int *retlen;
+    } item[item_entry_count], *pitem; /* number of entries in items_data */
+
+    struct items_data_st *pitems_data;
+    int data_buffer[(item_entry_count*2)+4]; /* 8 bytes per entry max */
+    int iosb[2];
+    int sys_time[2];
+    int *ptr;
+    int i, j ;
+    int tmp_length   = 0;
+    int total_length = 0;
+
+    pitems_data = items_data;
+    pitem = item;
+
+
+    /* Setup itemlist for GETJPI */
+    while (pitems_data->length) {
+        pitem->length = pitems_data->length;
+        pitem->code   = pitems_data->code;
+        pitem->buffer = &data_buffer[total_length];
+        pitem->retlen = 0;
+        /* total_length is in longwords */
+        total_length += pitems_data->length/4;
+        pitems_data++;
+        pitem ++;
+    }
+    pitem->length = pitem->code = 0;
+
+    /* Fill data_buffer with various info bits from this process */
+    /* and twist that data to seed the SSL random number init    */
+
+    if (sys$getjpiw(EFN$C_ENF, NULL, NULL, item, &iosb, 0, 0) == SS$_NORMAL) {
+        for (i = 0; i < total_length; i++) {
+            sys$gettim((struct _generic_64 *)&sys_time[0]);
+            srand(sys_time[0] * data_buffer[0] * data_buffer[1] + i);
+
+            if (i == (total_length - 1)) { /* for JPI$_FINALEXC */
+                ptr = &data_buffer[i];
+                for (j = 0; j < 4; j++) {
+                    data_buffer[i + j] = ptr[j];
+                    /* OK to use rand() just to scramble the seed */
+                    data_buffer[i + j] ^= (sys_time[0] ^ rand());
+                    tmp_length++;
+                }
+            } else {
+                /* OK to use rand() just to scramble the seed */
+                data_buffer[i] ^= (sys_time[0] ^ rand());
+            }
+        }
+
+        total_length += (tmp_length - 1);
+
+        /* size of seed is total_length*4 bytes (64bytes) */
+        RAND_add((PTR_T) data_buffer, total_length*4, total_length * 2);
+    } else {
+        return 0;
+    }
+
+    return 1;
+}
+
+#endif
diff --git a/crypto/rand/rand_win.c b/crypto/rand/rand_win.c
new file mode 100644
index 000000000..1be0ed3c9
--- /dev/null
+++ b/crypto/rand/rand_win.c
@@ -0,0 +1,135 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "internal/cryptlib.h"
+#include 
+#include "rand_lcl.h"
+
+#if defined(OPENSSL_SYS_WINDOWS) || defined(OPENSSL_SYS_WIN32)
+# include 
+/* On Windows 7 or higher use BCrypt instead of the legacy CryptoAPI */
+# if defined(_MSC_VER) && defined(_WIN32_WINNT) && _WIN32_WINNT>=0x0601
+#  define RAND_WINDOWS_USE_BCRYPT
+# endif
+
+# ifdef RAND_WINDOWS_USE_BCRYPT
+#  include 
+#  pragma comment(lib, "bcrypt.lib")
+#  ifndef STATUS_SUCCESS
+#   define STATUS_SUCCESS ((NTSTATUS)0x00000000L)
+#  endif
+# else
+#  include 
+/*
+ * Intel hardware RNG CSP -- available from
+ * http://developer.intel.com/design/security/rng/redist_license.htm
+ */
+#  define PROV_INTEL_SEC 22
+#  define INTEL_DEF_PROV L"Intel Hardware Cryptographic Service Provider"
+# endif
+
+static void readtimer(void);
+
+int RAND_poll(void)
+{
+    MEMORYSTATUS mst;
+# ifndef RAND_WINDOWS_USE_BCRYPT
+    HCRYPTPROV hProvider;
+# endif
+    DWORD w;
+    BYTE buf[64];
+
+# ifdef RAND_WINDOWS_USE_BCRYPT
+    if (BCryptGenRandom(NULL, buf, (ULONG)sizeof(buf), BCRYPT_USE_SYSTEM_PREFERRED_RNG) == STATUS_SUCCESS) {
+        RAND_add(buf, sizeof(buf), sizeof(buf));
+    }
+# else
+    /* poll the CryptoAPI PRNG */
+    /* The CryptoAPI returns sizeof(buf) bytes of randomness */
+    if (CryptAcquireContextW(&hProvider, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) {
+        if (CryptGenRandom(hProvider, (DWORD)sizeof(buf), buf) != 0) {
+            RAND_add(buf, sizeof(buf), sizeof(buf));
+        }
+        CryptReleaseContext(hProvider, 0);
+    }
+
+    /* poll the Pentium PRG with CryptoAPI */
+    if (CryptAcquireContextW(&hProvider, NULL, INTEL_DEF_PROV, PROV_INTEL_SEC, CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) {
+        if (CryptGenRandom(hProvider, (DWORD)sizeof(buf), buf) != 0) {
+            RAND_add(buf, sizeof(buf), sizeof(buf));
+        }
+        CryptReleaseContext(hProvider, 0);
+    }
+# endif
+
+    /* timer data */
+    readtimer();
+
+    /* memory usage statistics */
+    GlobalMemoryStatus(&mst);
+    RAND_add(&mst, sizeof(mst), 1);
+
+    /* process ID */
+    w = GetCurrentProcessId();
+    RAND_add(&w, sizeof(w), 1);
+
+    return (1);
+}
+
+#if OPENSSL_API_COMPAT < 0x10100000L
+int RAND_event(UINT iMsg, WPARAM wParam, LPARAM lParam)
+{
+    RAND_poll();
+    return RAND_status();
+}
+
+void RAND_screen(void)
+{
+    RAND_poll();
+}
+#endif
+
+/* feed timing information to the PRNG */
+static void readtimer(void)
+{
+    DWORD w;
+    LARGE_INTEGER l;
+    static int have_perfc = 1;
+# if defined(_MSC_VER) && defined(_M_X86)
+    static int have_tsc = 1;
+    DWORD cyclecount;
+
+    if (have_tsc) {
+        __try {
+            __asm {
+            _emit 0x0f _emit 0x31 mov cyclecount, eax}
+            RAND_add(&cyclecount, sizeof(cyclecount), 1);
+        }
+        __except(EXCEPTION_EXECUTE_HANDLER) {
+            have_tsc = 0;
+        }
+    }
+# else
+#  define have_tsc 0
+# endif
+
+    if (have_perfc) {
+        if (QueryPerformanceCounter(&l) == 0)
+            have_perfc = 0;
+        else
+            RAND_add(&l, sizeof(l), 0);
+    }
+
+    if (!have_tsc && !have_perfc) {
+        w = GetTickCount();
+        RAND_add(&w, sizeof(w), 0);
+    }
+}
+
+#endif
diff --git a/crypto/rand/randfile.c b/crypto/rand/randfile.c
new file mode 100644
index 000000000..15fa9dce5
--- /dev/null
+++ b/crypto/rand/randfile.c
@@ -0,0 +1,366 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "internal/cryptlib.h"
+
+#include 
+#include 
+#include 
+#include 
+
+#include 
+#include 
+#include 
+
+#ifdef OPENSSL_SYS_VMS
+# include 
+#endif
+#ifndef NO_SYS_TYPES_H
+# include 
+#endif
+#ifndef OPENSSL_NO_POSIX_IO
+# include 
+# include 
+/*
+ * Following should not be needed, and we could have been stricter
+ * and demand S_IS*. But some systems just don't comply... Formally
+ * below macros are "anatomically incorrect", because normally they
+ * would look like ((m) & MASK == TYPE), but since MASK availability
+ * is as questionable, we settle for this poor-man fallback...
+ */
+# if !defined(S_ISBLK)
+#  if defined(_S_IFBLK)
+#   define S_ISBLK(m) ((m) & _S_IFBLK)
+#  elif defined(S_IFBLK)
+#   define S_ISBLK(m) ((m) & S_IFBLK)
+#  elif defined(_WIN32)
+#   define S_ISBLK(m) 0 /* no concept of block devices on Windows */
+#  endif
+# endif
+# if !defined(S_ISCHR)
+#  if defined(_S_IFCHR)
+#   define S_ISCHR(m) ((m) & _S_IFCHR)
+#  elif defined(S_IFCHR)
+#   define S_ISCHR(m) ((m) & S_IFCHR)
+#  endif
+# endif
+#endif
+
+#ifdef _WIN32
+# define stat    _stat
+# define chmod   _chmod
+# define open    _open
+# define fdopen  _fdopen
+# define fstat   _fstat
+# define fileno  _fileno
+#endif
+
+#undef BUFSIZE
+#define BUFSIZE 1024
+#define RAND_DATA 1024
+
+#ifdef OPENSSL_SYS_VMS
+/*
+ * Misc hacks needed for specific cases.
+ *
+ * __FILE_ptr32 is a type provided by DEC C headers (types.h specifically)
+ * to make sure the FILE* is a 32-bit pointer no matter what.  We know that
+ * stdio function return this type (a study of stdio.h proves it).
+ * Additionally, we create a similar char pointer type for the sake of
+ * vms_setbuf below.
+ */
+# if __INITIAL_POINTER_SIZE == 64
+#  pragma pointer_size save
+#  pragma pointer_size 32
+typedef char *char_ptr32;
+#  pragma pointer_size restore
+/*
+ * On VMS, setbuf() will only take 32-bit pointers, and a compilation
+ * with /POINTER_SIZE=64 will give off a MAYLOSEDATA2 warning here.
+ * Since we know that the FILE* really is a 32-bit pointer expanded to
+ * 64 bits, we also know it's safe to convert it back to a 32-bit pointer.
+ * As for the buffer parameter, we only use NULL here, so that passes as
+ * well...
+ */
+#  define setbuf(fp,buf) (setbuf)((__FILE_ptr32)(fp), (char_ptr32)(buf))
+# endif
+
+/*
+ * This declaration is a nasty hack to get around vms' extension to fopen for
+ * passing in sharing options being disabled by /STANDARD=ANSI89
+ */
+static __FILE_ptr32 (*const vms_fopen)(const char *, const char *, ...) =
+      (__FILE_ptr32 (*)(const char *, const char *, ...))fopen;
+# define VMS_OPEN_ATTRS "shr=get,put,upd,del","ctx=bin,stm","rfm=stm","rat=none","mrs=0"
+
+# define openssl_fopen(fname,mode) vms_fopen((fname), (mode), VMS_OPEN_ATTRS)
+#endif
+
+#define RFILE ".rnd"
+
+/*
+ * Note that these functions are intended for seed files only. Entropy
+ * devices and EGD sockets are handled in rand_unix.c
+ */
+
+int RAND_load_file(const char *file, long bytes)
+{
+    /*-
+     * If bytes >= 0, read up to 'bytes' bytes.
+     * if bytes == -1, read complete file.
+     */
+
+    unsigned char buf[BUFSIZE];
+#ifndef OPENSSL_NO_POSIX_IO
+    struct stat sb;
+#endif
+    int i, ret = 0, n;
+    FILE *in = NULL;
+
+    if (file == NULL)
+        return 0;
+
+    if (bytes == 0)
+        return ret;
+
+    in = openssl_fopen(file, "rb");
+    if (in == NULL)
+        goto err;
+
+#ifndef OPENSSL_NO_POSIX_IO
+    /*
+     * struct stat can have padding and unused fields that may not be
+     * initialized in the call to stat(). We need to clear the entire
+     * structure before calling RAND_add() to avoid complaints from
+     * applications such as Valgrind.
+     */
+    memset(&sb, 0, sizeof(sb));
+    if (fstat(fileno(in), &sb) < 0)
+        goto err;
+    RAND_add(&sb, sizeof(sb), 0.0);
+
+# if defined(S_ISBLK) && defined(S_ISCHR)
+    if (S_ISBLK(sb.st_mode) || S_ISCHR(sb.st_mode)) {
+        /*
+         * this file is a device. we don't want read an infinite number of
+         * bytes from a random device, nor do we want to use buffered I/O
+         * because we will waste system entropy.
+         */
+        bytes = (bytes == -1) ? 2048 : bytes; /* ok, is 2048 enough? */
+        setbuf(in, NULL); /* don't do buffered reads */
+    }
+# endif
+#endif
+    for (;;) {
+        if (bytes > 0)
+            n = (bytes < BUFSIZE) ? (int)bytes : BUFSIZE;
+        else
+            n = BUFSIZE;
+        i = fread(buf, 1, n, in);
+        if (i <= 0)
+            break;
+
+        RAND_add(buf, i, (double)i);
+        ret += i;
+        if (bytes > 0) {
+            bytes -= n;
+            if (bytes <= 0)
+                break;
+        }
+    }
+    OPENSSL_cleanse(buf, BUFSIZE);
+ err:
+    if (in != NULL)
+        fclose(in);
+    return ret;
+}
+
+int RAND_write_file(const char *file)
+{
+    unsigned char buf[BUFSIZE];
+    int i, ret = 0, rand_err = 0;
+    FILE *out = NULL;
+    int n;
+#ifndef OPENSSL_NO_POSIX_IO
+    struct stat sb;
+
+# if defined(S_ISBLK) && defined(S_ISCHR)
+# ifdef _WIN32
+    /*
+     * Check for |file| being a driver as "ASCII-safe" on Windows,
+     * because driver paths are always ASCII.
+     */
+# endif
+    i = stat(file, &sb);
+    if (i != -1) {
+        if (S_ISBLK(sb.st_mode) || S_ISCHR(sb.st_mode)) {
+            /*
+             * this file is a device. we don't write back to it. we
+             * "succeed" on the assumption this is some sort of random
+             * device. Otherwise attempting to write to and chmod the device
+             * causes problems.
+             */
+            return 1;
+        }
+    }
+# endif
+#endif
+
+#if defined(O_CREAT) && !defined(OPENSSL_NO_POSIX_IO) && \
+    !defined(OPENSSL_SYS_VMS) && !defined(OPENSSL_SYS_WINDOWS)
+    {
+# ifndef O_BINARY
+#  define O_BINARY 0
+# endif
+        /*
+         * chmod(..., 0600) is too late to protect the file, permissions
+         * should be restrictive from the start
+         */
+        int fd = open(file, O_WRONLY | O_CREAT | O_BINARY, 0600);
+        if (fd != -1)
+            out = fdopen(fd, "wb");
+    }
+#endif
+
+#ifdef OPENSSL_SYS_VMS
+    /*
+     * VMS NOTE: Prior versions of this routine created a _new_ version of
+     * the rand file for each call into this routine, then deleted all
+     * existing versions named ;-1, and finally renamed the current version
+     * as ';1'. Under concurrent usage, this resulted in an RMS race
+     * condition in rename() which could orphan files (see vms message help
+     * for RMS$_REENT). With the fopen() calls below, openssl/VMS now shares
+     * the top-level version of the rand file. Note that there may still be
+     * conditions where the top-level rand file is locked. If so, this code
+     * will then create a new version of the rand file. Without the delete
+     * and rename code, this can result in ascending file versions that stop
+     * at version 32767, and this routine will then return an error. The
+     * remedy for this is to recode the calling application to avoid
+     * concurrent use of the rand file, or synchronize usage at the
+     * application level. Also consider whether or not you NEED a persistent
+     * rand file in a concurrent use situation.
+     */
+
+    out = openssl_fopen(file, "rb+");
+#endif
+    if (out == NULL)
+        out = openssl_fopen(file, "wb");
+    if (out == NULL)
+        goto err;
+
+#if !defined(NO_CHMOD) && !defined(OPENSSL_NO_POSIX_IO)
+    chmod(file, 0600);
+#endif
+    n = RAND_DATA;
+    for (;;) {
+        i = (n > BUFSIZE) ? BUFSIZE : n;
+        n -= BUFSIZE;
+        if (RAND_bytes(buf, i) <= 0)
+            rand_err = 1;
+        i = fwrite(buf, 1, i, out);
+        if (i <= 0) {
+            ret = 0;
+            break;
+        }
+        ret += i;
+        if (n <= 0)
+            break;
+    }
+
+    fclose(out);
+    OPENSSL_cleanse(buf, BUFSIZE);
+ err:
+    return (rand_err ? -1 : ret);
+}
+
+const char *RAND_file_name(char *buf, size_t size)
+{
+    char *s = NULL;
+    int use_randfile = 1;
+#ifdef __OpenBSD__
+    struct stat sb;
+#endif
+
+#if defined(_WIN32) && defined(CP_UTF8)
+    DWORD len;
+    WCHAR *var, *val;
+
+    if ((var = L"RANDFILE",
+         len = GetEnvironmentVariableW(var, NULL, 0)) == 0
+        && (var = L"HOME", use_randfile = 0,
+            len = GetEnvironmentVariableW(var, NULL, 0)) == 0
+        && (var = L"USERPROFILE",
+            len = GetEnvironmentVariableW(var, NULL, 0)) == 0) {
+        var = L"SYSTEMROOT",
+        len = GetEnvironmentVariableW(var, NULL, 0);
+    }
+
+    if (len != 0) {
+        int sz;
+
+        val = _alloca(len * sizeof(WCHAR));
+
+        if (GetEnvironmentVariableW(var, val, len) < len
+            && (sz = WideCharToMultiByte(CP_UTF8, 0, val, -1, NULL, 0,
+                                         NULL, NULL)) != 0) {
+            s = _alloca(sz);
+            if (WideCharToMultiByte(CP_UTF8, 0, val, -1, s, sz,
+                                    NULL, NULL) == 0)
+                s = NULL;
+        }
+    }
+#else
+    if (OPENSSL_issetugid() != 0) {
+        use_randfile = 0;
+    } else {
+        s = getenv("RANDFILE");
+        if (s == NULL || *s == '\0') {
+            use_randfile = 0;
+            s = getenv("HOME");
+        }
+    }
+#endif
+#ifdef DEFAULT_HOME
+    if (!use_randfile && s == NULL) {
+        s = DEFAULT_HOME;
+    }
+#endif
+    if (s != NULL && *s) {
+        size_t len = strlen(s);
+
+        if (use_randfile && len + 1 < size) {
+            if (OPENSSL_strlcpy(buf, s, size) >= size)
+                return NULL;
+        } else if (len + strlen(RFILE) + 2 < size) {
+            OPENSSL_strlcpy(buf, s, size);
+#ifndef OPENSSL_SYS_VMS
+            OPENSSL_strlcat(buf, "/", size);
+#endif
+            OPENSSL_strlcat(buf, RFILE, size);
+        }
+    } else {
+        buf[0] = '\0';      /* no file name */
+    }
+
+#ifdef __OpenBSD__
+    /*
+     * given that all random loads just fail if the file can't be seen on a
+     * stat, we stat the file we're returning, if it fails, use /dev/arandom
+     * instead. this allows the user to use their own source for good random
+     * data, but defaults to something hopefully decent if that isn't
+     * available.
+     */
+
+    if (!buf[0] || stat(buf, &sb) == -1)
+        if (OPENSSL_strlcpy(buf, "/dev/arandom", size) >= size) {
+            return NULL;
+        }
+#endif
+    return buf[0] ? buf : NULL;
+}
diff --git a/crypto/rc2/build.info b/crypto/rc2/build.info
new file mode 100644
index 000000000..47a3fd0d4
--- /dev/null
+++ b/crypto/rc2/build.info
@@ -0,0 +1,3 @@
+LIBS=../../libcrypto
+SOURCE[../../libcrypto]=\
+        rc2_ecb.c rc2_skey.c rc2_cbc.c rc2cfb64.c rc2ofb64.c
diff --git a/crypto/rc2/rc2_cbc.c b/crypto/rc2/rc2_cbc.c
new file mode 100644
index 000000000..2b59353b1
--- /dev/null
+++ b/crypto/rc2/rc2_cbc.c
@@ -0,0 +1,179 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "rc2_locl.h"
+
+void RC2_cbc_encrypt(const unsigned char *in, unsigned char *out, long length,
+                     RC2_KEY *ks, unsigned char *iv, int encrypt)
+{
+    register unsigned long tin0, tin1;
+    register unsigned long tout0, tout1, xor0, xor1;
+    register long l = length;
+    unsigned long tin[2];
+
+    if (encrypt) {
+        c2l(iv, tout0);
+        c2l(iv, tout1);
+        iv -= 8;
+        for (l -= 8; l >= 0; l -= 8) {
+            c2l(in, tin0);
+            c2l(in, tin1);
+            tin0 ^= tout0;
+            tin1 ^= tout1;
+            tin[0] = tin0;
+            tin[1] = tin1;
+            RC2_encrypt(tin, ks);
+            tout0 = tin[0];
+            l2c(tout0, out);
+            tout1 = tin[1];
+            l2c(tout1, out);
+        }
+        if (l != -8) {
+            c2ln(in, tin0, tin1, l + 8);
+            tin0 ^= tout0;
+            tin1 ^= tout1;
+            tin[0] = tin0;
+            tin[1] = tin1;
+            RC2_encrypt(tin, ks);
+            tout0 = tin[0];
+            l2c(tout0, out);
+            tout1 = tin[1];
+            l2c(tout1, out);
+        }
+        l2c(tout0, iv);
+        l2c(tout1, iv);
+    } else {
+        c2l(iv, xor0);
+        c2l(iv, xor1);
+        iv -= 8;
+        for (l -= 8; l >= 0; l -= 8) {
+            c2l(in, tin0);
+            tin[0] = tin0;
+            c2l(in, tin1);
+            tin[1] = tin1;
+            RC2_decrypt(tin, ks);
+            tout0 = tin[0] ^ xor0;
+            tout1 = tin[1] ^ xor1;
+            l2c(tout0, out);
+            l2c(tout1, out);
+            xor0 = tin0;
+            xor1 = tin1;
+        }
+        if (l != -8) {
+            c2l(in, tin0);
+            tin[0] = tin0;
+            c2l(in, tin1);
+            tin[1] = tin1;
+            RC2_decrypt(tin, ks);
+            tout0 = tin[0] ^ xor0;
+            tout1 = tin[1] ^ xor1;
+            l2cn(tout0, tout1, out, l + 8);
+            xor0 = tin0;
+            xor1 = tin1;
+        }
+        l2c(xor0, iv);
+        l2c(xor1, iv);
+    }
+    tin0 = tin1 = tout0 = tout1 = xor0 = xor1 = 0;
+    tin[0] = tin[1] = 0;
+}
+
+void RC2_encrypt(unsigned long *d, RC2_KEY *key)
+{
+    int i, n;
+    register RC2_INT *p0, *p1;
+    register RC2_INT x0, x1, x2, x3, t;
+    unsigned long l;
+
+    l = d[0];
+    x0 = (RC2_INT) l & 0xffff;
+    x1 = (RC2_INT) (l >> 16L);
+    l = d[1];
+    x2 = (RC2_INT) l & 0xffff;
+    x3 = (RC2_INT) (l >> 16L);
+
+    n = 3;
+    i = 5;
+
+    p0 = p1 = &(key->data[0]);
+    for (;;) {
+        t = (x0 + (x1 & ~x3) + (x2 & x3) + *(p0++)) & 0xffff;
+        x0 = (t << 1) | (t >> 15);
+        t = (x1 + (x2 & ~x0) + (x3 & x0) + *(p0++)) & 0xffff;
+        x1 = (t << 2) | (t >> 14);
+        t = (x2 + (x3 & ~x1) + (x0 & x1) + *(p0++)) & 0xffff;
+        x2 = (t << 3) | (t >> 13);
+        t = (x3 + (x0 & ~x2) + (x1 & x2) + *(p0++)) & 0xffff;
+        x3 = (t << 5) | (t >> 11);
+
+        if (--i == 0) {
+            if (--n == 0)
+                break;
+            i = (n == 2) ? 6 : 5;
+
+            x0 += p1[x3 & 0x3f];
+            x1 += p1[x0 & 0x3f];
+            x2 += p1[x1 & 0x3f];
+            x3 += p1[x2 & 0x3f];
+        }
+    }
+
+    d[0] =
+        (unsigned long)(x0 & 0xffff) | ((unsigned long)(x1 & 0xffff) << 16L);
+    d[1] =
+        (unsigned long)(x2 & 0xffff) | ((unsigned long)(x3 & 0xffff) << 16L);
+}
+
+void RC2_decrypt(unsigned long *d, RC2_KEY *key)
+{
+    int i, n;
+    register RC2_INT *p0, *p1;
+    register RC2_INT x0, x1, x2, x3, t;
+    unsigned long l;
+
+    l = d[0];
+    x0 = (RC2_INT) l & 0xffff;
+    x1 = (RC2_INT) (l >> 16L);
+    l = d[1];
+    x2 = (RC2_INT) l & 0xffff;
+    x3 = (RC2_INT) (l >> 16L);
+
+    n = 3;
+    i = 5;
+
+    p0 = &(key->data[63]);
+    p1 = &(key->data[0]);
+    for (;;) {
+        t = ((x3 << 11) | (x3 >> 5)) & 0xffff;
+        x3 = (t - (x0 & ~x2) - (x1 & x2) - *(p0--)) & 0xffff;
+        t = ((x2 << 13) | (x2 >> 3)) & 0xffff;
+        x2 = (t - (x3 & ~x1) - (x0 & x1) - *(p0--)) & 0xffff;
+        t = ((x1 << 14) | (x1 >> 2)) & 0xffff;
+        x1 = (t - (x2 & ~x0) - (x3 & x0) - *(p0--)) & 0xffff;
+        t = ((x0 << 15) | (x0 >> 1)) & 0xffff;
+        x0 = (t - (x1 & ~x3) - (x2 & x3) - *(p0--)) & 0xffff;
+
+        if (--i == 0) {
+            if (--n == 0)
+                break;
+            i = (n == 2) ? 6 : 5;
+
+            x3 = (x3 - p1[x2 & 0x3f]) & 0xffff;
+            x2 = (x2 - p1[x1 & 0x3f]) & 0xffff;
+            x1 = (x1 - p1[x0 & 0x3f]) & 0xffff;
+            x0 = (x0 - p1[x3 & 0x3f]) & 0xffff;
+        }
+    }
+
+    d[0] =
+        (unsigned long)(x0 & 0xffff) | ((unsigned long)(x1 & 0xffff) << 16L);
+    d[1] =
+        (unsigned long)(x2 & 0xffff) | ((unsigned long)(x3 & 0xffff) << 16L);
+}
diff --git a/crypto/rc2/rc2_ecb.c b/crypto/rc2/rc2_ecb.c
new file mode 100644
index 000000000..b87931f2a
--- /dev/null
+++ b/crypto/rc2/rc2_ecb.c
@@ -0,0 +1,41 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "rc2_locl.h"
+#include 
+
+/*-
+ * RC2 as implemented frm a posting from
+ * Newsgroups: sci.crypt
+ * Sender: pgut01@cs.auckland.ac.nz (Peter Gutmann)
+ * Subject: Specification for Ron Rivests Cipher No.2
+ * Message-ID: <4fk39f$f70@net.auckland.ac.nz>
+ * Date: 11 Feb 1996 06:45:03 GMT
+ */
+
+void RC2_ecb_encrypt(const unsigned char *in, unsigned char *out, RC2_KEY *ks,
+                     int encrypt)
+{
+    unsigned long l, d[2];
+
+    c2l(in, l);
+    d[0] = l;
+    c2l(in, l);
+    d[1] = l;
+    if (encrypt)
+        RC2_encrypt(d, ks);
+    else
+        RC2_decrypt(d, ks);
+    l = d[0];
+    l2c(l, out);
+    l = d[1];
+    l2c(l, out);
+    l = d[0] = d[1] = 0;
+}
diff --git a/crypto/rc2/rc2_locl.h b/crypto/rc2/rc2_locl.h
new file mode 100644
index 000000000..a9a57d612
--- /dev/null
+++ b/crypto/rc2/rc2_locl.h
@@ -0,0 +1,106 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#undef c2l
+#define c2l(c,l)        (l =((unsigned long)(*((c)++)))    , \
+                         l|=((unsigned long)(*((c)++)))<< 8L, \
+                         l|=((unsigned long)(*((c)++)))<<16L, \
+                         l|=((unsigned long)(*((c)++)))<<24L)
+
+/* NOTE - c is not incremented as per c2l */
+#undef c2ln
+#define c2ln(c,l1,l2,n) { \
+                        c+=n; \
+                        l1=l2=0; \
+                        switch (n) { \
+                        case 8: l2 =((unsigned long)(*(--(c))))<<24L; \
+                        case 7: l2|=((unsigned long)(*(--(c))))<<16L; \
+                        case 6: l2|=((unsigned long)(*(--(c))))<< 8L; \
+                        case 5: l2|=((unsigned long)(*(--(c))));     \
+                        case 4: l1 =((unsigned long)(*(--(c))))<<24L; \
+                        case 3: l1|=((unsigned long)(*(--(c))))<<16L; \
+                        case 2: l1|=((unsigned long)(*(--(c))))<< 8L; \
+                        case 1: l1|=((unsigned long)(*(--(c))));     \
+                                } \
+                        }
+
+#undef l2c
+#define l2c(l,c)        (*((c)++)=(unsigned char)(((l)     )&0xff), \
+                         *((c)++)=(unsigned char)(((l)>> 8L)&0xff), \
+                         *((c)++)=(unsigned char)(((l)>>16L)&0xff), \
+                         *((c)++)=(unsigned char)(((l)>>24L)&0xff))
+
+/* NOTE - c is not incremented as per l2c */
+#undef l2cn
+#define l2cn(l1,l2,c,n) { \
+                        c+=n; \
+                        switch (n) { \
+                        case 8: *(--(c))=(unsigned char)(((l2)>>24L)&0xff); \
+                        case 7: *(--(c))=(unsigned char)(((l2)>>16L)&0xff); \
+                        case 6: *(--(c))=(unsigned char)(((l2)>> 8L)&0xff); \
+                        case 5: *(--(c))=(unsigned char)(((l2)     )&0xff); \
+                        case 4: *(--(c))=(unsigned char)(((l1)>>24L)&0xff); \
+                        case 3: *(--(c))=(unsigned char)(((l1)>>16L)&0xff); \
+                        case 2: *(--(c))=(unsigned char)(((l1)>> 8L)&0xff); \
+                        case 1: *(--(c))=(unsigned char)(((l1)     )&0xff); \
+                                } \
+                        }
+
+/* NOTE - c is not incremented as per n2l */
+#define n2ln(c,l1,l2,n) { \
+                        c+=n; \
+                        l1=l2=0; \
+                        switch (n) { \
+                        case 8: l2 =((unsigned long)(*(--(c))))    ; \
+                        case 7: l2|=((unsigned long)(*(--(c))))<< 8; \
+                        case 6: l2|=((unsigned long)(*(--(c))))<<16; \
+                        case 5: l2|=((unsigned long)(*(--(c))))<<24; \
+                        case 4: l1 =((unsigned long)(*(--(c))))    ; \
+                        case 3: l1|=((unsigned long)(*(--(c))))<< 8; \
+                        case 2: l1|=((unsigned long)(*(--(c))))<<16; \
+                        case 1: l1|=((unsigned long)(*(--(c))))<<24; \
+                                } \
+                        }
+
+/* NOTE - c is not incremented as per l2n */
+#define l2nn(l1,l2,c,n) { \
+                        c+=n; \
+                        switch (n) { \
+                        case 8: *(--(c))=(unsigned char)(((l2)    )&0xff); \
+                        case 7: *(--(c))=(unsigned char)(((l2)>> 8)&0xff); \
+                        case 6: *(--(c))=(unsigned char)(((l2)>>16)&0xff); \
+                        case 5: *(--(c))=(unsigned char)(((l2)>>24)&0xff); \
+                        case 4: *(--(c))=(unsigned char)(((l1)    )&0xff); \
+                        case 3: *(--(c))=(unsigned char)(((l1)>> 8)&0xff); \
+                        case 2: *(--(c))=(unsigned char)(((l1)>>16)&0xff); \
+                        case 1: *(--(c))=(unsigned char)(((l1)>>24)&0xff); \
+                                } \
+                        }
+
+#undef n2l
+#define n2l(c,l)        (l =((unsigned long)(*((c)++)))<<24L, \
+                         l|=((unsigned long)(*((c)++)))<<16L, \
+                         l|=((unsigned long)(*((c)++)))<< 8L, \
+                         l|=((unsigned long)(*((c)++))))
+
+#undef l2n
+#define l2n(l,c)        (*((c)++)=(unsigned char)(((l)>>24L)&0xff), \
+                         *((c)++)=(unsigned char)(((l)>>16L)&0xff), \
+                         *((c)++)=(unsigned char)(((l)>> 8L)&0xff), \
+                         *((c)++)=(unsigned char)(((l)     )&0xff))
+
+#define C_RC2(n) \
+        t=(x0+(x1& ~x3)+(x2&x3)+ *(p0++))&0xffff; \
+        x0=(t<<1)|(t>>15); \
+        t=(x1+(x2& ~x0)+(x3&x0)+ *(p0++))&0xffff; \
+        x1=(t<<2)|(t>>14); \
+        t=(x2+(x3& ~x1)+(x0&x1)+ *(p0++))&0xffff; \
+        x2=(t<<3)|(t>>13); \
+        t=(x3+(x0& ~x2)+(x1&x2)+ *(p0++))&0xffff; \
+        x3=(t<<5)|(t>>11);
diff --git a/crypto/rc2/rc2_skey.c b/crypto/rc2/rc2_skey.c
new file mode 100644
index 000000000..55d8ba371
--- /dev/null
+++ b/crypto/rc2/rc2_skey.c
@@ -0,0 +1,98 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "rc2_locl.h"
+
+static const unsigned char key_table[256] = {
+    0xd9, 0x78, 0xf9, 0xc4, 0x19, 0xdd, 0xb5, 0xed, 0x28, 0xe9, 0xfd, 0x79,
+    0x4a, 0xa0, 0xd8, 0x9d, 0xc6, 0x7e, 0x37, 0x83, 0x2b, 0x76, 0x53, 0x8e,
+    0x62, 0x4c, 0x64, 0x88, 0x44, 0x8b, 0xfb, 0xa2, 0x17, 0x9a, 0x59, 0xf5,
+    0x87, 0xb3, 0x4f, 0x13, 0x61, 0x45, 0x6d, 0x8d, 0x09, 0x81, 0x7d, 0x32,
+    0xbd, 0x8f, 0x40, 0xeb, 0x86, 0xb7, 0x7b, 0x0b, 0xf0, 0x95, 0x21, 0x22,
+    0x5c, 0x6b, 0x4e, 0x82, 0x54, 0xd6, 0x65, 0x93, 0xce, 0x60, 0xb2, 0x1c,
+    0x73, 0x56, 0xc0, 0x14, 0xa7, 0x8c, 0xf1, 0xdc, 0x12, 0x75, 0xca, 0x1f,
+    0x3b, 0xbe, 0xe4, 0xd1, 0x42, 0x3d, 0xd4, 0x30, 0xa3, 0x3c, 0xb6, 0x26,
+    0x6f, 0xbf, 0x0e, 0xda, 0x46, 0x69, 0x07, 0x57, 0x27, 0xf2, 0x1d, 0x9b,
+    0xbc, 0x94, 0x43, 0x03, 0xf8, 0x11, 0xc7, 0xf6, 0x90, 0xef, 0x3e, 0xe7,
+    0x06, 0xc3, 0xd5, 0x2f, 0xc8, 0x66, 0x1e, 0xd7, 0x08, 0xe8, 0xea, 0xde,
+    0x80, 0x52, 0xee, 0xf7, 0x84, 0xaa, 0x72, 0xac, 0x35, 0x4d, 0x6a, 0x2a,
+    0x96, 0x1a, 0xd2, 0x71, 0x5a, 0x15, 0x49, 0x74, 0x4b, 0x9f, 0xd0, 0x5e,
+    0x04, 0x18, 0xa4, 0xec, 0xc2, 0xe0, 0x41, 0x6e, 0x0f, 0x51, 0xcb, 0xcc,
+    0x24, 0x91, 0xaf, 0x50, 0xa1, 0xf4, 0x70, 0x39, 0x99, 0x7c, 0x3a, 0x85,
+    0x23, 0xb8, 0xb4, 0x7a, 0xfc, 0x02, 0x36, 0x5b, 0x25, 0x55, 0x97, 0x31,
+    0x2d, 0x5d, 0xfa, 0x98, 0xe3, 0x8a, 0x92, 0xae, 0x05, 0xdf, 0x29, 0x10,
+    0x67, 0x6c, 0xba, 0xc9, 0xd3, 0x00, 0xe6, 0xcf, 0xe1, 0x9e, 0xa8, 0x2c,
+    0x63, 0x16, 0x01, 0x3f, 0x58, 0xe2, 0x89, 0xa9, 0x0d, 0x38, 0x34, 0x1b,
+    0xab, 0x33, 0xff, 0xb0, 0xbb, 0x48, 0x0c, 0x5f, 0xb9, 0xb1, 0xcd, 0x2e,
+    0xc5, 0xf3, 0xdb, 0x47, 0xe5, 0xa5, 0x9c, 0x77, 0x0a, 0xa6, 0x20, 0x68,
+    0xfe, 0x7f, 0xc1, 0xad,
+};
+
+#if defined(_MSC_VER) && defined(_ARM_)
+# pragma optimize("g",off)
+#endif
+
+/*
+ * It has come to my attention that there are 2 versions of the RC2 key
+ * schedule.  One which is normal, and anther which has a hook to use a
+ * reduced key length. BSAFE uses the 'retarded' version.  What I previously
+ * shipped is the same as specifying 1024 for the 'bits' parameter.  Bsafe
+ * uses a version where the bits parameter is the same as len*8
+ */
+void RC2_set_key(RC2_KEY *key, int len, const unsigned char *data, int bits)
+{
+    int i, j;
+    unsigned char *k;
+    RC2_INT *ki;
+    unsigned int c, d;
+
+    k = (unsigned char *)&(key->data[0]);
+    *k = 0;                     /* for if there is a zero length key */
+
+    if (len > 128)
+        len = 128;
+    if (bits <= 0)
+        bits = 1024;
+    if (bits > 1024)
+        bits = 1024;
+
+    for (i = 0; i < len; i++)
+        k[i] = data[i];
+
+    /* expand table */
+    d = k[len - 1];
+    j = 0;
+    for (i = len; i < 128; i++, j++) {
+        d = key_table[(k[j] + d) & 0xff];
+        k[i] = d;
+    }
+
+    /* hmm.... key reduction to 'bits' bits */
+
+    j = (bits + 7) >> 3;
+    i = 128 - j;
+    c = (0xff >> (-bits & 0x07));
+
+    d = key_table[k[i] & c];
+    k[i] = d;
+    while (i--) {
+        d = key_table[k[i + j] ^ d];
+        k[i] = d;
+    }
+
+    /* copy from bytes into RC2_INT's */
+    ki = &(key->data[63]);
+    for (i = 127; i >= 0; i -= 2)
+        *(ki--) = ((k[i] << 8) | k[i - 1]) & 0xffff;
+}
+
+#if defined(_MSC_VER)
+# pragma optimize("",on)
+#endif
diff --git a/crypto/rc2/rc2cfb64.c b/crypto/rc2/rc2cfb64.c
new file mode 100644
index 000000000..e11093db9
--- /dev/null
+++ b/crypto/rc2/rc2cfb64.c
@@ -0,0 +1,74 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "rc2_locl.h"
+
+/*
+ * The input and output encrypted as though 64bit cfb mode is being used.
+ * The extra state information to record how much of the 64bit block we have
+ * used is contained in *num;
+ */
+
+void RC2_cfb64_encrypt(const unsigned char *in, unsigned char *out,
+                       long length, RC2_KEY *schedule, unsigned char *ivec,
+                       int *num, int encrypt)
+{
+    register unsigned long v0, v1, t;
+    register int n = *num;
+    register long l = length;
+    unsigned long ti[2];
+    unsigned char *iv, c, cc;
+
+    iv = (unsigned char *)ivec;
+    if (encrypt) {
+        while (l--) {
+            if (n == 0) {
+                c2l(iv, v0);
+                ti[0] = v0;
+                c2l(iv, v1);
+                ti[1] = v1;
+                RC2_encrypt((unsigned long *)ti, schedule);
+                iv = (unsigned char *)ivec;
+                t = ti[0];
+                l2c(t, iv);
+                t = ti[1];
+                l2c(t, iv);
+                iv = (unsigned char *)ivec;
+            }
+            c = *(in++) ^ iv[n];
+            *(out++) = c;
+            iv[n] = c;
+            n = (n + 1) & 0x07;
+        }
+    } else {
+        while (l--) {
+            if (n == 0) {
+                c2l(iv, v0);
+                ti[0] = v0;
+                c2l(iv, v1);
+                ti[1] = v1;
+                RC2_encrypt((unsigned long *)ti, schedule);
+                iv = (unsigned char *)ivec;
+                t = ti[0];
+                l2c(t, iv);
+                t = ti[1];
+                l2c(t, iv);
+                iv = (unsigned char *)ivec;
+            }
+            cc = *(in++);
+            c = iv[n];
+            iv[n] = cc;
+            *(out++) = c ^ cc;
+            n = (n + 1) & 0x07;
+        }
+    }
+    v0 = v1 = ti[0] = ti[1] = t = c = cc = 0;
+    *num = n;
+}
diff --git a/crypto/rc2/rc2ofb64.c b/crypto/rc2/rc2ofb64.c
new file mode 100644
index 000000000..d610278a9
--- /dev/null
+++ b/crypto/rc2/rc2ofb64.c
@@ -0,0 +1,61 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "rc2_locl.h"
+
+/*
+ * The input and output encrypted as though 64bit ofb mode is being used.
+ * The extra state information to record how much of the 64bit block we have
+ * used is contained in *num;
+ */
+void RC2_ofb64_encrypt(const unsigned char *in, unsigned char *out,
+                       long length, RC2_KEY *schedule, unsigned char *ivec,
+                       int *num)
+{
+    register unsigned long v0, v1, t;
+    register int n = *num;
+    register long l = length;
+    unsigned char d[8];
+    register char *dp;
+    unsigned long ti[2];
+    unsigned char *iv;
+    int save = 0;
+
+    iv = (unsigned char *)ivec;
+    c2l(iv, v0);
+    c2l(iv, v1);
+    ti[0] = v0;
+    ti[1] = v1;
+    dp = (char *)d;
+    l2c(v0, dp);
+    l2c(v1, dp);
+    while (l--) {
+        if (n == 0) {
+            RC2_encrypt((unsigned long *)ti, schedule);
+            dp = (char *)d;
+            t = ti[0];
+            l2c(t, dp);
+            t = ti[1];
+            l2c(t, dp);
+            save++;
+        }
+        *(out++) = *(in++) ^ d[n];
+        n = (n + 1) & 0x07;
+    }
+    if (save) {
+        v0 = ti[0];
+        v1 = ti[1];
+        iv = (unsigned char *)ivec;
+        l2c(v0, iv);
+        l2c(v1, iv);
+    }
+    t = v0 = v1 = ti[0] = ti[1] = 0;
+    *num = n;
+}
diff --git a/crypto/rc2/tab.c b/crypto/rc2/tab.c
new file mode 100644
index 000000000..bc95dc404
--- /dev/null
+++ b/crypto/rc2/tab.c
@@ -0,0 +1,93 @@
+/*
+ * Copyright 1998-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+
+unsigned char ebits_to_num[256] = {
+    0xbd, 0x56, 0xea, 0xf2, 0xa2, 0xf1, 0xac, 0x2a,
+    0xb0, 0x93, 0xd1, 0x9c, 0x1b, 0x33, 0xfd, 0xd0,
+    0x30, 0x04, 0xb6, 0xdc, 0x7d, 0xdf, 0x32, 0x4b,
+    0xf7, 0xcb, 0x45, 0x9b, 0x31, 0xbb, 0x21, 0x5a,
+    0x41, 0x9f, 0xe1, 0xd9, 0x4a, 0x4d, 0x9e, 0xda,
+    0xa0, 0x68, 0x2c, 0xc3, 0x27, 0x5f, 0x80, 0x36,
+    0x3e, 0xee, 0xfb, 0x95, 0x1a, 0xfe, 0xce, 0xa8,
+    0x34, 0xa9, 0x13, 0xf0, 0xa6, 0x3f, 0xd8, 0x0c,
+    0x78, 0x24, 0xaf, 0x23, 0x52, 0xc1, 0x67, 0x17,
+    0xf5, 0x66, 0x90, 0xe7, 0xe8, 0x07, 0xb8, 0x60,
+    0x48, 0xe6, 0x1e, 0x53, 0xf3, 0x92, 0xa4, 0x72,
+    0x8c, 0x08, 0x15, 0x6e, 0x86, 0x00, 0x84, 0xfa,
+    0xf4, 0x7f, 0x8a, 0x42, 0x19, 0xf6, 0xdb, 0xcd,
+    0x14, 0x8d, 0x50, 0x12, 0xba, 0x3c, 0x06, 0x4e,
+    0xec, 0xb3, 0x35, 0x11, 0xa1, 0x88, 0x8e, 0x2b,
+    0x94, 0x99, 0xb7, 0x71, 0x74, 0xd3, 0xe4, 0xbf,
+    0x3a, 0xde, 0x96, 0x0e, 0xbc, 0x0a, 0xed, 0x77,
+    0xfc, 0x37, 0x6b, 0x03, 0x79, 0x89, 0x62, 0xc6,
+    0xd7, 0xc0, 0xd2, 0x7c, 0x6a, 0x8b, 0x22, 0xa3,
+    0x5b, 0x05, 0x5d, 0x02, 0x75, 0xd5, 0x61, 0xe3,
+    0x18, 0x8f, 0x55, 0x51, 0xad, 0x1f, 0x0b, 0x5e,
+    0x85, 0xe5, 0xc2, 0x57, 0x63, 0xca, 0x3d, 0x6c,
+    0xb4, 0xc5, 0xcc, 0x70, 0xb2, 0x91, 0x59, 0x0d,
+    0x47, 0x20, 0xc8, 0x4f, 0x58, 0xe0, 0x01, 0xe2,
+    0x16, 0x38, 0xc4, 0x6f, 0x3b, 0x0f, 0x65, 0x46,
+    0xbe, 0x7e, 0x2d, 0x7b, 0x82, 0xf9, 0x40, 0xb5,
+    0x1d, 0x73, 0xf8, 0xeb, 0x26, 0xc7, 0x87, 0x97,
+    0x25, 0x54, 0xb1, 0x28, 0xaa, 0x98, 0x9d, 0xa5,
+    0x64, 0x6d, 0x7a, 0xd4, 0x10, 0x81, 0x44, 0xef,
+    0x49, 0xd6, 0xae, 0x2e, 0xdd, 0x76, 0x5c, 0x2f,
+    0xa7, 0x1c, 0xc9, 0x09, 0x69, 0x9a, 0x83, 0xcf,
+    0x29, 0x39, 0xb9, 0xe9, 0x4c, 0xff, 0x43, 0xab,
+};
+
+unsigned char num_to_ebits[256] = {
+    0x5d, 0xbe, 0x9b, 0x8b, 0x11, 0x99, 0x6e, 0x4d,
+    0x59, 0xf3, 0x85, 0xa6, 0x3f, 0xb7, 0x83, 0xc5,
+    0xe4, 0x73, 0x6b, 0x3a, 0x68, 0x5a, 0xc0, 0x47,
+    0xa0, 0x64, 0x34, 0x0c, 0xf1, 0xd0, 0x52, 0xa5,
+    0xb9, 0x1e, 0x96, 0x43, 0x41, 0xd8, 0xd4, 0x2c,
+    0xdb, 0xf8, 0x07, 0x77, 0x2a, 0xca, 0xeb, 0xef,
+    0x10, 0x1c, 0x16, 0x0d, 0x38, 0x72, 0x2f, 0x89,
+    0xc1, 0xf9, 0x80, 0xc4, 0x6d, 0xae, 0x30, 0x3d,
+    0xce, 0x20, 0x63, 0xfe, 0xe6, 0x1a, 0xc7, 0xb8,
+    0x50, 0xe8, 0x24, 0x17, 0xfc, 0x25, 0x6f, 0xbb,
+    0x6a, 0xa3, 0x44, 0x53, 0xd9, 0xa2, 0x01, 0xab,
+    0xbc, 0xb6, 0x1f, 0x98, 0xee, 0x9a, 0xa7, 0x2d,
+    0x4f, 0x9e, 0x8e, 0xac, 0xe0, 0xc6, 0x49, 0x46,
+    0x29, 0xf4, 0x94, 0x8a, 0xaf, 0xe1, 0x5b, 0xc3,
+    0xb3, 0x7b, 0x57, 0xd1, 0x7c, 0x9c, 0xed, 0x87,
+    0x40, 0x8c, 0xe2, 0xcb, 0x93, 0x14, 0xc9, 0x61,
+    0x2e, 0xe5, 0xcc, 0xf6, 0x5e, 0xa8, 0x5c, 0xd6,
+    0x75, 0x8d, 0x62, 0x95, 0x58, 0x69, 0x76, 0xa1,
+    0x4a, 0xb5, 0x55, 0x09, 0x78, 0x33, 0x82, 0xd7,
+    0xdd, 0x79, 0xf5, 0x1b, 0x0b, 0xde, 0x26, 0x21,
+    0x28, 0x74, 0x04, 0x97, 0x56, 0xdf, 0x3c, 0xf0,
+    0x37, 0x39, 0xdc, 0xff, 0x06, 0xa4, 0xea, 0x42,
+    0x08, 0xda, 0xb4, 0x71, 0xb0, 0xcf, 0x12, 0x7a,
+    0x4e, 0xfa, 0x6c, 0x1d, 0x84, 0x00, 0xc8, 0x7f,
+    0x91, 0x45, 0xaa, 0x2b, 0xc2, 0xb1, 0x8f, 0xd5,
+    0xba, 0xf2, 0xad, 0x19, 0xb2, 0x67, 0x36, 0xf7,
+    0x0f, 0x0a, 0x92, 0x7d, 0xe3, 0x9d, 0xe9, 0x90,
+    0x3e, 0x23, 0x27, 0x66, 0x13, 0xec, 0x81, 0x15,
+    0xbd, 0x22, 0xbf, 0x9f, 0x7e, 0xa9, 0x51, 0x4b,
+    0x4c, 0xfb, 0x02, 0xd3, 0x70, 0x86, 0x31, 0xe7,
+    0x3b, 0x05, 0x03, 0x54, 0x60, 0x48, 0x65, 0x18,
+    0xd2, 0xcd, 0x5f, 0x32, 0x88, 0x0e, 0x35, 0xfd,
+};
+
+main()
+{
+    int i, j;
+
+    for (i = 0; i < 256; i++) {
+        for (j = 0; j < 256; j++)
+            if (ebits_to_num[j] == i) {
+                printf("0x%02x,", j);
+                break;
+            }
+    }
+}
diff --git a/crypto/rc4/asm/rc4-586.pl b/crypto/rc4/asm/rc4-586.pl
new file mode 100644
index 000000000..7d6f97c59
--- /dev/null
+++ b/crypto/rc4/asm/rc4-586.pl
@@ -0,0 +1,428 @@
+#! /usr/bin/env perl
+# Copyright 1998-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+
+# ====================================================================
+# [Re]written by Andy Polyakov  for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+
+# At some point it became apparent that the original SSLeay RC4
+# assembler implementation performs suboptimally on latest IA-32
+# microarchitectures. After re-tuning performance has changed as
+# following:
+#
+# Pentium	-10%
+# Pentium III	+12%
+# AMD		+50%(*)
+# P4		+250%(**)
+#
+# (*)	This number is actually a trade-off:-) It's possible to
+#	achieve	+72%, but at the cost of -48% off PIII performance.
+#	In other words code performing further 13% faster on AMD
+#	would perform almost 2 times slower on Intel PIII...
+#	For reference! This code delivers ~80% of rc4-amd64.pl
+#	performance on the same Opteron machine.
+# (**)	This number requires compressed key schedule set up by
+#	RC4_set_key [see commentary below for further details].
+#
+#					
+
+# May 2011
+#
+# Optimize for Core2 and Westmere [and incidentally Opteron]. Current
+# performance in cycles per processed byte (less is better) and
+# improvement relative to previous version of this module is:
+#
+# Pentium	10.2			# original numbers
+# Pentium III	7.8(*)
+# Intel P4	7.5
+#
+# Opteron	6.1/+20%		# new MMX numbers
+# Core2		5.3/+67%(**)
+# Westmere	5.1/+94%(**)
+# Sandy Bridge	5.0/+8%
+# Atom		12.6/+6%
+# VIA Nano	6.4/+9%
+# Ivy Bridge	4.9/±0%
+# Bulldozer	4.9/+15%
+#
+# (*)	PIII can actually deliver 6.6 cycles per byte with MMX code,
+#	but this specific code performs poorly on Core2. And vice
+#	versa, below MMX/SSE code delivering 5.8/7.1 on Core2 performs
+#	poorly on PIII, at 8.0/14.5:-( As PIII is not a "hot" CPU
+#	[anymore], I chose to discard PIII-specific code path and opt
+#	for original IALU-only code, which is why MMX/SSE code path
+#	is guarded by SSE2 bit (see below), not MMX/SSE.
+# (**)	Performance vs. block size on Core2 and Westmere had a maximum
+#	at ... 64 bytes block size. And it was quite a maximum, 40-60%
+#	in comparison to largest 8KB block size. Above improvement
+#	coefficients are for the largest block size.
+
+$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+push(@INC,"${dir}","${dir}../../perlasm");
+require "x86asm.pl";
+
+$output=pop;
+open STDOUT,">$output";
+
+&asm_init($ARGV[0],"rc4-586.pl",$x86only = $ARGV[$#ARGV] eq "386");
+
+$xx="eax";
+$yy="ebx";
+$tx="ecx";
+$ty="edx";
+$inp="esi";
+$out="ebp";
+$dat="edi";
+
+sub RC4_loop {
+  my $i=shift;
+  my $func = ($i==0)?*mov:*or;
+
+	&add	(&LB($yy),&LB($tx));
+	&mov	($ty,&DWP(0,$dat,$yy,4));
+	&mov	(&DWP(0,$dat,$yy,4),$tx);
+	&mov	(&DWP(0,$dat,$xx,4),$ty);
+	&add	($ty,$tx);
+	&inc	(&LB($xx));
+	&and	($ty,0xff);
+	&ror	($out,8)	if ($i!=0);
+	if ($i<3) {
+	  &mov	($tx,&DWP(0,$dat,$xx,4));
+	} else {
+	  &mov	($tx,&wparam(3));	# reload [re-biased] out
+	}
+	&$func	($out,&DWP(0,$dat,$ty,4));
+}
+
+if ($alt=0) {
+  # >20% faster on Atom and Sandy Bridge[!], 8% faster on Opteron,
+  # but ~40% slower on Core2 and Westmere... Attempt to add movz
+  # brings down Opteron by 25%, Atom and Sandy Bridge by 15%, yet
+  # on Core2 with movz it's almost 20% slower than below alternative
+  # code... Yes, it's a total mess...
+  my @XX=($xx,$out);
+  $RC4_loop_mmx = sub {		# SSE actually...
+    my $i=shift;
+    my $j=$i<=0?0:$i>>1;
+    my $mm=$i<=0?"mm0":"mm".($i&1);
+
+	&add	(&LB($yy),&LB($tx));
+	&lea	(@XX[1],&DWP(1,@XX[0]));
+	&pxor	("mm2","mm0")				if ($i==0);
+	&psllq	("mm1",8)				if ($i==0);
+	&and	(@XX[1],0xff);
+	&pxor	("mm0","mm0")				if ($i<=0);
+	&mov	($ty,&DWP(0,$dat,$yy,4));
+	&mov	(&DWP(0,$dat,$yy,4),$tx);
+	&pxor	("mm1","mm2")				if ($i==0);
+	&mov	(&DWP(0,$dat,$XX[0],4),$ty);
+	&add	(&LB($ty),&LB($tx));
+	&movd	(@XX[0],"mm7")				if ($i==0);
+	&mov	($tx,&DWP(0,$dat,@XX[1],4));
+	&pxor	("mm1","mm1")				if ($i==1);
+	&movq	("mm2",&QWP(0,$inp))			if ($i==1);
+	&movq	(&QWP(-8,(@XX[0],$inp)),"mm1")		if ($i==0);
+	&pinsrw	($mm,&DWP(0,$dat,$ty,4),$j);
+
+	push	(@XX,shift(@XX))			if ($i>=0);
+  }
+} else {
+  # Using pinsrw here improves performane on Intel CPUs by 2-3%, but
+  # brings down AMD by 7%...
+  $RC4_loop_mmx = sub {
+    my $i=shift;
+
+	&add	(&LB($yy),&LB($tx));
+	&psllq	("mm1",8*(($i-1)&7))			if (abs($i)!=1);
+	&mov	($ty,&DWP(0,$dat,$yy,4));
+	&mov	(&DWP(0,$dat,$yy,4),$tx);
+	&mov	(&DWP(0,$dat,$xx,4),$ty);
+	&inc	($xx);
+	&add	($ty,$tx);
+	&movz	($xx,&LB($xx));				# (*)
+	&movz	($ty,&LB($ty));				# (*)
+	&pxor	("mm2",$i==1?"mm0":"mm1")		if ($i>=0);
+	&movq	("mm0",&QWP(0,$inp))			if ($i<=0);
+	&movq	(&QWP(-8,($out,$inp)),"mm2")		if ($i==0);
+	&mov	($tx,&DWP(0,$dat,$xx,4));
+	&movd	($i>0?"mm1":"mm2",&DWP(0,$dat,$ty,4));
+
+	# (*)	This is the key to Core2 and Westmere performance.
+	#	Without movz out-of-order execution logic confuses
+	#	itself and fails to reorder loads and stores. Problem
+	#	appears to be fixed in Sandy Bridge...
+  }
+}
+
+&external_label("OPENSSL_ia32cap_P");
+
+# void RC4(RC4_KEY *key,size_t len,const unsigned char *inp,unsigned char *out);
+&function_begin("RC4");
+	&mov	($dat,&wparam(0));	# load key schedule pointer
+	&mov	($ty, &wparam(1));	# load len
+	&mov	($inp,&wparam(2));	# load inp
+	&mov	($out,&wparam(3));	# load out
+
+	&xor	($xx,$xx);		# avoid partial register stalls
+	&xor	($yy,$yy);
+
+	&cmp	($ty,0);		# safety net
+	&je	(&label("abort"));
+
+	&mov	(&LB($xx),&BP(0,$dat));	# load key->x
+	&mov	(&LB($yy),&BP(4,$dat));	# load key->y
+	&add	($dat,8);
+
+	&lea	($tx,&DWP(0,$inp,$ty));
+	&sub	($out,$inp);		# re-bias out
+	&mov	(&wparam(1),$tx);	# save input+len
+
+	&inc	(&LB($xx));
+
+	# detect compressed key schedule...
+	&cmp	(&DWP(256,$dat),-1);
+	&je	(&label("RC4_CHAR"));
+
+	&mov	($tx,&DWP(0,$dat,$xx,4));
+
+	&and	($ty,-4);		# how many 4-byte chunks?
+	&jz	(&label("loop1"));
+
+	&mov	(&wparam(3),$out);	# $out as accumulator in these loops
+					if ($x86only) {
+	&jmp	(&label("go4loop4"));
+					} else {
+	&test	($ty,-8);
+	&jz	(&label("go4loop4"));
+
+	&picmeup($out,"OPENSSL_ia32cap_P");
+	&bt	(&DWP(0,$out),26);	# check SSE2 bit [could have been MMX]
+	&jnc	(&label("go4loop4"));
+
+	&mov	($out,&wparam(3))	if (!$alt);
+	&movd	("mm7",&wparam(3))	if ($alt);
+	&and	($ty,-8);
+	&lea	($ty,&DWP(-8,$inp,$ty));
+	&mov	(&DWP(-4,$dat),$ty);	# save input+(len/8)*8-8
+
+	&$RC4_loop_mmx(-1);
+	&jmp(&label("loop_mmx_enter"));
+
+	&set_label("loop_mmx",16);
+		&$RC4_loop_mmx(0);
+	&set_label("loop_mmx_enter");
+		for 	($i=1;$i<8;$i++) { &$RC4_loop_mmx($i); }
+		&mov	($ty,$yy);
+		&xor	($yy,$yy);		# this is second key to Core2
+		&mov	(&LB($yy),&LB($ty));	# and Westmere performance...
+		&cmp	($inp,&DWP(-4,$dat));
+		&lea	($inp,&DWP(8,$inp));
+	&jb	(&label("loop_mmx"));
+
+    if ($alt) {
+	&movd	($out,"mm7");
+	&pxor	("mm2","mm0");
+	&psllq	("mm1",8);
+	&pxor	("mm1","mm2");
+	&movq	(&QWP(-8,$out,$inp),"mm1");
+    } else {
+	&psllq	("mm1",56);
+	&pxor	("mm2","mm1");
+	&movq	(&QWP(-8,$out,$inp),"mm2");
+    }
+	&emms	();
+
+	&cmp	($inp,&wparam(1));	# compare to input+len
+	&je	(&label("done"));
+	&jmp	(&label("loop1"));
+					}
+
+&set_label("go4loop4",16);
+	&lea	($ty,&DWP(-4,$inp,$ty));
+	&mov	(&wparam(2),$ty);	# save input+(len/4)*4-4
+
+	&set_label("loop4");
+		for ($i=0;$i<4;$i++) { RC4_loop($i); }
+		&ror	($out,8);
+		&xor	($out,&DWP(0,$inp));
+		&cmp	($inp,&wparam(2));	# compare to input+(len/4)*4-4
+		&mov	(&DWP(0,$tx,$inp),$out);# $tx holds re-biased out here
+		&lea	($inp,&DWP(4,$inp));
+		&mov	($tx,&DWP(0,$dat,$xx,4));
+	&jb	(&label("loop4"));
+
+	&cmp	($inp,&wparam(1));	# compare to input+len
+	&je	(&label("done"));
+	&mov	($out,&wparam(3));	# restore $out
+
+	&set_label("loop1",16);
+		&add	(&LB($yy),&LB($tx));
+		&mov	($ty,&DWP(0,$dat,$yy,4));
+		&mov	(&DWP(0,$dat,$yy,4),$tx);
+		&mov	(&DWP(0,$dat,$xx,4),$ty);
+		&add	($ty,$tx);
+		&inc	(&LB($xx));
+		&and	($ty,0xff);
+		&mov	($ty,&DWP(0,$dat,$ty,4));
+		&xor	(&LB($ty),&BP(0,$inp));
+		&lea	($inp,&DWP(1,$inp));
+		&mov	($tx,&DWP(0,$dat,$xx,4));
+		&cmp	($inp,&wparam(1));	# compare to input+len
+		&mov	(&BP(-1,$out,$inp),&LB($ty));
+	&jb	(&label("loop1"));
+
+	&jmp	(&label("done"));
+
+# this is essentially Intel P4 specific codepath...
+&set_label("RC4_CHAR",16);
+	&movz	($tx,&BP(0,$dat,$xx));
+	# strangely enough unrolled loop performs over 20% slower...
+	&set_label("cloop1");
+		&add	(&LB($yy),&LB($tx));
+		&movz	($ty,&BP(0,$dat,$yy));
+		&mov	(&BP(0,$dat,$yy),&LB($tx));
+		&mov	(&BP(0,$dat,$xx),&LB($ty));
+		&add	(&LB($ty),&LB($tx));
+		&movz	($ty,&BP(0,$dat,$ty));
+		&add	(&LB($xx),1);
+		&xor	(&LB($ty),&BP(0,$inp));
+		&lea	($inp,&DWP(1,$inp));
+		&movz	($tx,&BP(0,$dat,$xx));
+		&cmp	($inp,&wparam(1));
+		&mov	(&BP(-1,$out,$inp),&LB($ty));
+	&jb	(&label("cloop1"));
+
+&set_label("done");
+	&dec	(&LB($xx));
+	&mov	(&DWP(-4,$dat),$yy);		# save key->y
+	&mov	(&BP(-8,$dat),&LB($xx));	# save key->x
+&set_label("abort");
+&function_end("RC4");
+
+########################################################################
+
+$inp="esi";
+$out="edi";
+$idi="ebp";
+$ido="ecx";
+$idx="edx";
+
+# void RC4_set_key(RC4_KEY *key,int len,const unsigned char *data);
+&function_begin("RC4_set_key");
+	&mov	($out,&wparam(0));		# load key
+	&mov	($idi,&wparam(1));		# load len
+	&mov	($inp,&wparam(2));		# load data
+	&picmeup($idx,"OPENSSL_ia32cap_P");
+
+	&lea	($out,&DWP(2*4,$out));		# &key->data
+	&lea	($inp,&DWP(0,$inp,$idi));	# $inp to point at the end
+	&neg	($idi);
+	&xor	("eax","eax");
+	&mov	(&DWP(-4,$out),$idi);		# borrow key->y
+
+	&bt	(&DWP(0,$idx),20);		# check for bit#20
+	&jc	(&label("c1stloop"));
+
+&set_label("w1stloop",16);
+	&mov	(&DWP(0,$out,"eax",4),"eax");	# key->data[i]=i;
+	&add	(&LB("eax"),1);			# i++;
+	&jnc	(&label("w1stloop"));
+
+	&xor	($ido,$ido);
+	&xor	($idx,$idx);
+
+&set_label("w2ndloop",16);
+	&mov	("eax",&DWP(0,$out,$ido,4));
+	&add	(&LB($idx),&BP(0,$inp,$idi));
+	&add	(&LB($idx),&LB("eax"));
+	&add	($idi,1);
+	&mov	("ebx",&DWP(0,$out,$idx,4));
+	&jnz	(&label("wnowrap"));
+	  &mov	($idi,&DWP(-4,$out));
+	&set_label("wnowrap");
+	&mov	(&DWP(0,$out,$idx,4),"eax");
+	&mov	(&DWP(0,$out,$ido,4),"ebx");
+	&add	(&LB($ido),1);
+	&jnc	(&label("w2ndloop"));
+&jmp	(&label("exit"));
+
+# Unlike all other x86 [and x86_64] implementations, Intel P4 core
+# [including EM64T] was found to perform poorly with above "32-bit" key
+# schedule, a.k.a. RC4_INT. Performance improvement for IA-32 hand-coded
+# assembler turned out to be 3.5x if re-coded for compressed 8-bit one,
+# a.k.a. RC4_CHAR! It's however inappropriate to just switch to 8-bit
+# schedule for x86[_64], because non-P4 implementations suffer from
+# significant performance losses then, e.g. PIII exhibits >2x
+# deterioration, and so does Opteron. In order to assure optimal
+# all-round performance, we detect P4 at run-time and set up compressed
+# key schedule, which is recognized by RC4 procedure.
+
+&set_label("c1stloop",16);
+	&mov	(&BP(0,$out,"eax"),&LB("eax"));	# key->data[i]=i;
+	&add	(&LB("eax"),1);			# i++;
+	&jnc	(&label("c1stloop"));
+
+	&xor	($ido,$ido);
+	&xor	($idx,$idx);
+	&xor	("ebx","ebx");
+
+&set_label("c2ndloop",16);
+	&mov	(&LB("eax"),&BP(0,$out,$ido));
+	&add	(&LB($idx),&BP(0,$inp,$idi));
+	&add	(&LB($idx),&LB("eax"));
+	&add	($idi,1);
+	&mov	(&LB("ebx"),&BP(0,$out,$idx));
+	&jnz	(&label("cnowrap"));
+	  &mov	($idi,&DWP(-4,$out));
+	&set_label("cnowrap");
+	&mov	(&BP(0,$out,$idx),&LB("eax"));
+	&mov	(&BP(0,$out,$ido),&LB("ebx"));
+	&add	(&LB($ido),1);
+	&jnc	(&label("c2ndloop"));
+
+	&mov	(&DWP(256,$out),-1);		# mark schedule as compressed
+
+&set_label("exit");
+	&xor	("eax","eax");
+	&mov	(&DWP(-8,$out),"eax");		# key->x=0;
+	&mov	(&DWP(-4,$out),"eax");		# key->y=0;
+&function_end("RC4_set_key");
+
+# const char *RC4_options(void);
+&function_begin_B("RC4_options");
+	&call	(&label("pic_point"));
+&set_label("pic_point");
+	&blindpop("eax");
+	&lea	("eax",&DWP(&label("opts")."-".&label("pic_point"),"eax"));
+	&picmeup("edx","OPENSSL_ia32cap_P");
+	&mov	("edx",&DWP(0,"edx"));
+	&bt	("edx",20);
+	&jc	(&label("1xchar"));
+	&bt	("edx",26);
+	&jnc	(&label("ret"));
+	&add	("eax",25);
+	&ret	();
+&set_label("1xchar");
+	&add	("eax",12);
+&set_label("ret");
+	&ret	();
+&set_label("opts",64);
+&asciz	("rc4(4x,int)");
+&asciz	("rc4(1x,char)");
+&asciz	("rc4(8x,mmx)");
+&asciz	("RC4 for x86, CRYPTOGAMS by ");
+&align	(64);
+&function_end_B("RC4_options");
+
+&asm_finish();
+
+close STDOUT;
diff --git a/crypto/rc4/asm/rc4-c64xplus.pl b/crypto/rc4/asm/rc4-c64xplus.pl
new file mode 100644
index 000000000..daed75c75
--- /dev/null
+++ b/crypto/rc4/asm/rc4-c64xplus.pl
@@ -0,0 +1,190 @@
+#! /usr/bin/env perl
+# Copyright 2014-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+#
+# ====================================================================
+# Written by Andy Polyakov  for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+#
+# RC4 for C64x+.
+#
+# April 2014
+#
+# RC4 subroutine processes one byte in 7.0 cycles, which is 3x faster
+# than TI CGT-generated code. Loop is scheduled in such way that
+# there is only one reference to memory in each cycle. This is done
+# to avoid L1D memory banking conflicts, see SPRU871 TI publication
+# for further details. Otherwise it should be possible to schedule
+# the loop for iteration interval of 6...
+
+($KEY,$LEN,$INP,$OUT)=("A4","B4","A6","B6");
+
+($KEYA,$XX,$TY,$xx,$ONE,$ret)=map("A$_",(5,7,8,9,1,2));
+($KEYB,$YY,$TX,$tx,$SUM,$dat)=map("B$_",(5,7,8,9,1,2));
+
+$code.=<<___;
+	.text
+
+	.if	.ASSEMBLER_VERSION<7000000
+	.asg	0,__TI_EABI__
+	.endif
+	.if	__TI_EABI__
+	.nocmp
+	.asg	RC4,_RC4
+	.asg	RC4_set_key,_RC4_set_key
+	.asg	RC4_options,_RC4_options
+	.endif
+
+	.global	_RC4
+	.align	16
+_RC4:
+	.asmfunc
+	MV	$LEN,B0
+  [!B0]	BNOP	B3			; if (len==0) return;
+||[B0]	ADD	$KEY,2,$KEYA
+||[B0]	ADD	$KEY,2,$KEYB
+  [B0]	MVK	1,$ONE
+||[B0]	LDBU	*${KEYA}[-2],$XX	; key->x
+  [B0]	LDBU	*${KEYB}[-1],$YY	; key->y
+||	NOP	4
+
+	ADD4	$ONE,$XX,$XX
+	LDBU	*${KEYA}[$XX],$TX
+||	MVC	$LEN,ILC
+	NOP	4
+;;==================================================
+	SPLOOP	7
+||	ADD4	$TX,$YY,$YY
+
+	LDBU	*${KEYB}[$YY],$TY
+||	MVD	$XX,$xx
+||	ADD4	$ONE,$XX,$XX
+	LDBU	*${KEYA}[$XX],$tx
+	CMPEQ	$YY,$XX,B0
+||	NOP	3
+	STB	$TX,*${KEYB}[$YY]
+||[B0]	ADD4	$TX,$YY,$YY
+	STB	$TY,*${KEYA}[$xx]
+||[!B0]	ADD4	$tx,$YY,$YY
+||[!B0]	MVD	$tx,$TX
+	ADD4	$TY,$TX,$SUM		; [0,0] $TX is not replaced by $tx yet!
+||	NOP	2
+	LDBU	*$INP++,$dat
+||	NOP	2
+	LDBU	*${KEYB}[$SUM],$ret
+||	NOP	5
+	XOR.L	$dat,$ret,$ret
+	SPKERNEL
+||	STB	$ret,*$OUT++
+;;==================================================
+	SUB4	$XX,$ONE,$XX
+||	NOP	5
+	STB	$XX,*${KEYA}[-2]	; key->x
+||	SUB4	$YY,$TX,$YY
+||	BNOP	B3	
+	STB	$YY,*${KEYB}[-1]	; key->y
+||	NOP	5
+	.endasmfunc
+
+	.global	_RC4_set_key
+	.align	16
+_RC4_set_key:
+	.asmfunc
+	.if	.BIG_ENDIAN
+	MVK	0x00000404,$ONE
+||	MVK	0x00000203,B0
+	MVKH	0x04040000,$ONE
+||	MVKH	0x00010000,B0
+	.else
+	MVK	0x00000404,$ONE
+||	MVK	0x00000100,B0
+	MVKH	0x04040000,$ONE
+||	MVKH	0x03020000,B0
+	.endif
+	ADD	$KEY,2,$KEYA
+||	ADD	$KEY,2,$KEYB
+||	ADD	$INP,$LEN,$ret		; end of input
+	LDBU	*${INP}++,$dat
+||	MVK	0,$TX
+	STH	$TX,*${KEY}++		; key->x=key->y=0
+||	MV	B0,A0
+||	MVK	64-4,B0
+
+;;==================================================
+	SPLOOPD	1
+||	MVC	B0,ILC
+
+	STNW	A0,*${KEY}++
+||	ADD4	$ONE,A0,A0
+	SPKERNEL
+;;==================================================
+
+	MVK	0,$YY
+||	MVK	0,$XX
+	MVK	1,$ONE
+||	MVK	256-1,B0
+
+;;==================================================
+	SPLOOPD	8
+||	MVC	B0,ILC
+
+	ADD4	$dat,$YY,$YY
+||	CMPEQ	$INP,$ret,A0		; end of input?
+	LDBU	*${KEYB}[$YY],$TY
+||	MVD	$XX,$xx
+||	ADD4	$ONE,$XX,$XX
+	LDBU	*${KEYA}[$XX],$tx
+||[A0]	SUB	$INP,$LEN,$INP		; rewind
+	LDBU	*${INP}++,$dat
+||	CMPEQ	$YY,$XX,B0
+||	NOP	3
+	STB	$TX,*${KEYB}[$YY]
+||[B0]	ADD4	$TX,$YY,$YY
+	STB	$TY,*${KEYA}[$xx]
+||[!B0]	ADD4	$tx,$YY,$YY
+||[!B0]	MV	$tx,$TX
+	SPKERNEL
+;;==================================================
+
+	BNOP	B3,5
+	.endasmfunc
+
+	.global	_RC4_options
+	.align	16
+_RC4_options:
+_rc4_options:
+	.asmfunc
+	BNOP	B3,1
+	ADDKPC	_rc4_options,B4
+	.if	__TI_EABI__
+	MVKL	\$PCR_OFFSET(rc4_options,_rc4_options),A4
+	MVKH	\$PCR_OFFSET(rc4_options,_rc4_options),A4
+	.else
+	MVKL	(rc4_options-_rc4_options),A4
+	MVKH	(rc4_options-_rc4_options),A4
+	.endif
+	ADD	B4,A4,A4
+	.endasmfunc
+
+	.if	__TI_EABI__
+	.sect	".text:rc4_options.const"
+	.else
+	.sect	".const:rc4_options"
+	.endif
+	.align	4
+rc4_options:
+	.cstring "rc4(sploop,char)"
+	.cstring "RC4 for C64+, CRYPTOGAMS by "
+	.align	4
+___
+
+print $code;
+close STDOUT;
diff --git a/crypto/rc4/asm/rc4-ia64.pl b/crypto/rc4/asm/rc4-ia64.pl
new file mode 100644
index 000000000..5e8f5f55b
--- /dev/null
+++ b/crypto/rc4/asm/rc4-ia64.pl
@@ -0,0 +1,767 @@
+#! /usr/bin/env perl
+# Copyright 2005-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+#
+# ====================================================================
+# Written by David Mosberger  based on the
+# Itanium optimized Crypto code which was released by HP Labs at
+# http://www.hpl.hp.com/research/linux/crypto/.
+#
+# Copyright (c) 2005 Hewlett-Packard Development Company, L.P.
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.  */
+
+
+
+# This is a little helper program which generates a software-pipelined
+# for RC4 encryption.  The basic algorithm looks like this:
+#
+#   for (counter = 0; counter < len; ++counter)
+#     {
+#       in = inp[counter];
+#       SI = S[I];
+#       J = (SI + J) & 0xff;
+#       SJ = S[J];
+#       T = (SI + SJ) & 0xff;
+#       S[I] = SJ, S[J] = SI;
+#       ST = S[T];
+#       outp[counter] = in ^ ST;
+#       I = (I + 1) & 0xff;
+#     }
+#
+# Pipelining this loop isn't easy, because the stores to the S[] array
+# need to be observed in the right order.  The loop generated by the
+# code below has the following pipeline diagram:
+#
+#      cycle
+#     | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |10 |11 |12 |13 |14 |15 |16 |17 |
+# iter
+#   1: xxx LDI xxx xxx xxx LDJ xxx SWP xxx LDT xxx xxx
+#   2:             xxx LDI xxx xxx xxx LDJ xxx SWP xxx LDT xxx xxx
+#   3:                         xxx LDI xxx xxx xxx LDJ xxx SWP xxx LDT xxx xxx
+#
+#   where:
+# 	LDI = load of S[I]
+# 	LDJ = load of S[J]
+# 	SWP = swap of S[I] and S[J]
+# 	LDT = load of S[T]
+#
+# Note that in the above diagram, the major trouble-spot is that LDI
+# of the 2nd iteration is performed BEFORE the SWP of the first
+# iteration.  Fortunately, this is easy to detect (I of the 1st
+# iteration will be equal to J of the 2nd iteration) and when this
+# happens, we simply forward the proper value from the 1st iteration
+# to the 2nd one.  The proper value in this case is simply the value
+# of S[I] from the first iteration (thanks to the fact that SWP
+# simply swaps the contents of S[I] and S[J]).
+#
+# Another potential trouble-spot is in cycle 7, where SWP of the 1st
+# iteration issues at the same time as the LDI of the 3rd iteration.
+# However, thanks to IA-64 execution semantics, this can be taken
+# care of simply by placing LDI later in the instruction-group than
+# SWP.  IA-64 CPUs will automatically forward the value if they
+# detect that the SWP and LDI are accessing the same memory-location.
+
+# The core-loop that can be pipelined then looks like this (annotated
+# with McKinley/Madison issue port & latency numbers, assuming L1
+# cache hits for the most part):
+
+# operation:	    instruction:		    issue-ports:  latency
+# ------------------  -----------------------------   ------------- -------
+
+# Data = *inp++       ld1 data = [inp], 1             M0-M1         1 cyc     c0
+#                     shladd Iptr = I, KeyTable, 3    M0-M3, I0, I1 1 cyc
+# I = (I + 1) & 0xff  padd1 nextI = I, one            M0-M3, I0, I1 3 cyc
+#                     ;;
+# SI = S[I]           ld8 SI = [Iptr]                 M0-M1         1 cyc     c1 * after SWAP!
+#                     ;;
+#                     cmp.eq.unc pBypass = I, J                                  * after J is valid!
+# J = SI + J          add J = J, SI                   M0-M3, I0, I1 1 cyc     c2
+#                     (pBypass) br.cond.spnt Bypass
+#                     ;;
+# ---------------------------------------------------------------------------------------
+# J = J & 0xff        zxt1 J = J                      I0, I1, 1 cyc           c3
+#                     ;;
+#                     shladd Jptr = J, KeyTable, 3    M0-M3, I0, I1 1 cyc     c4
+#                     ;;
+# SJ = S[J]           ld8 SJ = [Jptr]                 M0-M1         1 cyc     c5
+#                     ;;
+# ---------------------------------------------------------------------------------------
+# T = (SI + SJ)       add T = SI, SJ                  M0-M3, I0, I1 1 cyc     c6
+#                     ;;
+# T = T & 0xff        zxt1 T = T                      I0, I1        1 cyc
+# S[I] = SJ           st8 [Iptr] = SJ                 M2-M3                   c7
+# S[J] = SI           st8 [Jptr] = SI                 M2-M3
+#                     ;;
+#                     shladd Tptr = T, KeyTable, 3    M0-M3, I0, I1 1 cyc     c8
+#                     ;;
+# ---------------------------------------------------------------------------------------
+# T = S[T]            ld8 T = [Tptr]                  M0-M1         1 cyc     c9
+#                     ;;
+# data ^= T           xor data = data, T              M0-M3, I0, I1 1 cyc     c10
+#                     ;;
+# *out++ = Data ^ T   dep word = word, data, 8, POS   I0, I1        1 cyc     c11
+#                     ;;
+# ---------------------------------------------------------------------------------------
+
+# There are several points worth making here:
+
+#   - Note that due to the bypass/forwarding-path, the first two
+#     phases of the loop are strangly mingled together.  In
+#     particular, note that the first stage of the pipeline is
+#     using the value of "J", as calculated by the second stage.
+#   - Each bundle-pair will have exactly 6 instructions.
+#   - Pipelined, the loop can execute in 3 cycles/iteration and
+#     4 stages.  However, McKinley/Madison can issue "st1" to
+#     the same bank at a rate of at most one per 4 cycles.  Thus,
+#     instead of storing each byte, we accumulate them in a word
+#     and then write them back at once with a single "st8" (this
+#     implies that the setup code needs to ensure that the output
+#     buffer is properly aligned, if need be, by encoding the
+#     first few bytes separately).
+#   - There is no space for a "br.ctop" instruction.  For this
+#     reason we can't use module-loop support in IA-64 and have
+#     to do a traditional, purely software-pipelined loop.
+#   - We can't replace any of the remaining "add/zxt1" pairs with
+#     "padd1" because the latency for that instruction is too high
+#     and would push the loop to the point where more bypasses
+#     would be needed, which we don't have space for.
+#   - The above loop runs at around 3.26 cycles/byte, or roughly
+#     440 MByte/sec on a 1.5GHz Madison.  This is well below the
+#     system bus bandwidth and hence with judicious use of
+#     "lfetch" this loop can run at (almost) peak speed even when
+#     the input and output data reside in memory.  The
+#     max. latency that can be tolerated is (PREFETCH_DISTANCE *
+#     L2_LINE_SIZE * 3 cyc), or about 384 cycles assuming (at
+#     least) 1-ahead prefetching of 128 byte cache-lines.  Note
+#     that we do NOT prefetch into L1, since that would only
+#     interfere with the S[] table values stored there.  This is
+#     acceptable because there is a 10 cycle latency between
+#     load and first use of the input data.
+#   - We use a branch to out-of-line bypass-code of cycle-pressure:
+#     we calculate the next J, check for the need to activate the
+#     bypass path, and activate the bypass path ALL IN THE SAME
+#     CYCLE.  If we didn't have these constraints, we could do
+#     the bypass with a simple conditional move instruction.
+#     Fortunately, the bypass paths get activated relatively
+#     infrequently, so the extra branches don't cost all that much
+#     (about 0.04 cycles/byte, measured on a 16396 byte file with
+#     random input data).
+#
+
+$output = pop;
+open STDOUT,">$output";
+
+$phases = 4;		# number of stages/phases in the pipelined-loop
+$unroll_count = 6;	# number of times we unrolled it
+$pComI = (1 << 0);
+$pComJ = (1 << 1);
+$pComT = (1 << 2);
+$pOut  = (1 << 3);
+
+$NData = 4;
+$NIP = 3;
+$NJP = 2;
+$NI = 2;
+$NSI = 3;
+$NSJ = 2;
+$NT = 2;
+$NOutWord = 2;
+
+#
+# $threshold is the minimum length before we attempt to use the
+# big software-pipelined loop.  It MUST be greater-or-equal
+# to:
+#  		PHASES * (UNROLL_COUNT + 1) + 7
+#
+# The "+ 7" comes from the fact we may have to encode up to
+#   7 bytes separately before the output pointer is aligned.
+#
+$threshold = (3 * ($phases * ($unroll_count + 1)) + 7);
+
+sub I {
+    local *code = shift;
+    local $format = shift;
+    $code .= sprintf ("\t\t".$format."\n", @_);
+}
+
+sub P {
+    local *code = shift;
+    local $format = shift;
+    $code .= sprintf ($format."\n", @_);
+}
+
+sub STOP {
+    local *code = shift;
+    $code .=<<___;
+		;;
+___
+}
+
+sub emit_body {
+    local *c = shift;
+    local *bypass = shift;
+    local ($iteration, $p) = @_;
+
+    local $i0 = $iteration;
+    local $i1 = $iteration - 1;
+    local $i2 = $iteration - 2;
+    local $i3 = $iteration - 3;
+    local $iw0 = ($iteration - 3) / 8;
+    local $iw1 = ($iteration > 3) ? ($iteration - 4) / 8 : 1;
+    local $byte_num = ($iteration - 3) % 8;
+    local $label = $iteration + 1;
+    local $pAny = ($p & 0xf) == 0xf;
+    local $pByp = (($p & $pComI) && ($iteration > 0));
+
+    $c.=<<___;
+//////////////////////////////////////////////////
+___
+
+    if (($p & 0xf) == 0) {
+	$c.="#ifdef HOST_IS_BIG_ENDIAN\n";
+	&I(\$c,"shr.u	OutWord[%u] = OutWord[%u], 32;;",
+				$iw1 % $NOutWord, $iw1 % $NOutWord);
+	$c.="#endif\n";
+	&I(\$c, "st4 [OutPtr] = OutWord[%u], 4", $iw1 % $NOutWord);
+	return;
+    }
+
+    # Cycle 0
+    &I(\$c, "{ .mmi")					      if ($pAny);
+    &I(\$c, "ld1    Data[%u] = [InPtr], 1", $i0 % $NData)     if ($p & $pComI);
+    &I(\$c, "padd1  I[%u] = One, I[%u]", $i0 % $NI, $i1 % $NI)if ($p & $pComI);
+    &I(\$c, "zxt1   J = J")				      if ($p & $pComJ);
+    &I(\$c, "}")					      if ($pAny);
+    &I(\$c, "{ .mmi")					      if ($pAny);
+    &I(\$c, "LKEY   T[%u] = [T[%u]]", $i1 % $NT, $i1 % $NT)   if ($p & $pOut);
+    &I(\$c, "add    T[%u] = SI[%u], SJ[%u]",
+       $i0 % $NT, $i2 % $NSI, $i1 % $NSJ)		      if ($p & $pComT);
+    &I(\$c, "KEYADDR(IPr[%u], I[%u])", $i0 % $NIP, $i1 % $NI) if ($p & $pComI);
+    &I(\$c, "}")					      if ($pAny);
+    &STOP(\$c);
+
+    # Cycle 1
+    &I(\$c, "{ .mmi")					      if ($pAny);
+    &I(\$c, "SKEY   [IPr[%u]] = SJ[%u]", $i2 % $NIP, $i1%$NSJ)if ($p & $pComT);
+    &I(\$c, "SKEY   [JP[%u]] = SI[%u]", $i1 % $NJP, $i2%$NSI) if ($p & $pComT);
+    &I(\$c, "zxt1   T[%u] = T[%u]", $i0 % $NT, $i0 % $NT)     if ($p & $pComT);
+    &I(\$c, "}")					      if ($pAny);
+    &I(\$c, "{ .mmi")					      if ($pAny);
+    &I(\$c, "LKEY   SI[%u] = [IPr[%u]]", $i0 % $NSI, $i0%$NIP)if ($p & $pComI);
+    &I(\$c, "KEYADDR(JP[%u], J)", $i0 % $NJP)		      if ($p & $pComJ);
+    &I(\$c, "xor    Data[%u] = Data[%u], T[%u]",
+       $i3 % $NData, $i3 % $NData, $i1 % $NT)		      if ($p & $pOut);
+    &I(\$c, "}")					      if ($pAny);
+    &STOP(\$c);
+
+    # Cycle 2
+    &I(\$c, "{ .mmi")					      if ($pAny);
+    &I(\$c, "LKEY   SJ[%u] = [JP[%u]]", $i0 % $NSJ, $i0%$NJP) if ($p & $pComJ);
+    &I(\$c, "cmp.eq pBypass, p0 = I[%u], J", $i1 % $NI)	      if ($pByp);
+    &I(\$c, "dep OutWord[%u] = Data[%u], OutWord[%u], BYTE_POS(%u), 8",
+       $iw0%$NOutWord, $i3%$NData, $iw1%$NOutWord, $byte_num) if ($p & $pOut);
+    &I(\$c, "}")					      if ($pAny);
+    &I(\$c, "{ .mmb")					      if ($pAny);
+    &I(\$c, "add    J = J, SI[%u]", $i0 % $NSI)		      if ($p & $pComI);
+    &I(\$c, "KEYADDR(T[%u], T[%u])", $i0 % $NT, $i0 % $NT)    if ($p & $pComT);
+    &P(\$c, "(pBypass)\tbr.cond.spnt.many .rc4Bypass%u",$label)if ($pByp);
+    &I(\$c, "}") if ($pAny);
+    &STOP(\$c);
+
+    &P(\$c, ".rc4Resume%u:", $label)			      if ($pByp);
+    if ($byte_num == 0 && $iteration >= $phases) {
+	&I(\$c, "st8 [OutPtr] = OutWord[%u], 8",
+	   $iw1 % $NOutWord)				      if ($p & $pOut);
+	if ($iteration == (1 + $unroll_count) * $phases - 1) {
+	    if ($unroll_count == 6) {
+		&I(\$c, "mov OutWord[%u] = OutWord[%u]",
+		   $iw1 % $NOutWord, $iw0 % $NOutWord);
+	    }
+	    &I(\$c, "lfetch.nt1 [InPrefetch], %u",
+	       $unroll_count * $phases);
+	    &I(\$c, "lfetch.excl.nt1 [OutPrefetch], %u",
+	       $unroll_count * $phases);
+	    &I(\$c, "br.cloop.sptk.few .rc4Loop");
+	}
+    }
+
+    if ($pByp) {
+	&P(\$bypass, ".rc4Bypass%u:", $label);
+	&I(\$bypass, "sub J = J, SI[%u]", $i0 % $NSI);
+	&I(\$bypass, "nop 0");
+	&I(\$bypass, "nop 0");
+	&I(\$bypass, ";;");
+	&I(\$bypass, "add J = J, SI[%u]", $i1 % $NSI);
+	&I(\$bypass, "mov SI[%u] = SI[%u]", $i0 % $NSI, $i1 % $NSI);
+	&I(\$bypass, "br.sptk.many .rc4Resume%u\n", $label);
+	&I(\$bypass, ";;");
+    }
+}
+
+$code=<<___;
+.ident \"rc4-ia64.s, version 3.0\"
+.ident \"Copyright (c) 2005 Hewlett-Packard Development Company, L.P.\"
+
+#define LCSave		r8
+#define PRSave		r9
+
+/* Inputs become invalid once rotation begins!  */
+
+#define StateTable	in0
+#define DataLen		in1
+#define InputBuffer	in2
+#define OutputBuffer	in3
+
+#define KTable		r14
+#define J		r15
+#define InPtr		r16
+#define OutPtr		r17
+#define InPrefetch	r18
+#define OutPrefetch	r19
+#define One		r20
+#define LoopCount	r21
+#define Remainder	r22
+#define IFinal		r23
+#define EndPtr		r24
+
+#define tmp0		r25
+#define tmp1		r26
+
+#define pBypass		p6
+#define pDone		p7
+#define pSmall		p8
+#define pAligned	p9
+#define pUnaligned	p10
+
+#define pComputeI	pPhase[0]
+#define pComputeJ	pPhase[1]
+#define pComputeT	pPhase[2]
+#define pOutput		pPhase[3]
+
+#define RetVal		r8
+#define L_OK		p7
+#define L_NOK		p8
+
+#define	_NINPUTS	4
+#define	_NOUTPUT	0
+
+#define	_NROTATE	24
+#define	_NLOCALS	(_NROTATE - _NINPUTS - _NOUTPUT)
+
+#ifndef SZ
+# define SZ	4	// this must be set to sizeof(RC4_INT)
+#endif
+
+#if SZ == 1
+# define LKEY			ld1
+# define SKEY			st1
+# define KEYADDR(dst, i)	add dst = i, KTable
+#elif SZ == 2
+# define LKEY			ld2
+# define SKEY			st2
+# define KEYADDR(dst, i)	shladd dst = i, 1, KTable
+#elif SZ == 4
+# define LKEY			ld4
+# define SKEY			st4
+# define KEYADDR(dst, i)	shladd dst = i, 2, KTable
+#else
+# define LKEY			ld8
+# define SKEY			st8
+# define KEYADDR(dst, i)	shladd dst = i, 3, KTable
+#endif
+
+#if defined(_HPUX_SOURCE) && !defined(_LP64)
+# define ADDP	addp4
+#else
+# define ADDP	add
+#endif
+
+/* Define a macro for the bit number of the n-th byte: */
+
+#if defined(_HPUX_SOURCE) || defined(B_ENDIAN)
+# define HOST_IS_BIG_ENDIAN
+# define BYTE_POS(n)	(56 - (8 * (n)))
+#else
+# define BYTE_POS(n)	(8 * (n))
+#endif
+
+/*
+   We must perform the first phase of the pipeline explicitly since
+   we will always load from the stable the first time. The br.cexit
+   will never be taken since regardless of the number of bytes because
+   the epilogue count is 4.
+*/
+/* MODSCHED_RC4 macro was split to _PROLOGUE and _LOOP, because HP-UX
+   assembler failed on original macro with syntax error.  */
+#define MODSCHED_RC4_PROLOGUE						   \\
+	{								   \\
+				ld1		Data[0] = [InPtr], 1;	   \\
+				add		IFinal = 1, I[1];	   \\
+				KEYADDR(IPr[0], I[1]);			   \\
+	} ;;								   \\
+	{								   \\
+				LKEY		SI[0] = [IPr[0]];	   \\
+				mov		pr.rot = 0x10000;	   \\
+				mov		ar.ec = 4;		   \\
+	} ;;								   \\
+	{								   \\
+				add		J = J, SI[0];		   \\
+				zxt1		I[0] = IFinal;		   \\
+				br.cexit.spnt.few .+16; /* never taken */  \\
+	} ;;
+#define MODSCHED_RC4_LOOP(label)					   \\
+label:									   \\
+	{	.mmi;							   \\
+		(pComputeI)	ld1		Data[0] = [InPtr], 1;	   \\
+		(pComputeI)	add		IFinal = 1, I[1];	   \\
+		(pComputeJ)	zxt1		J = J;			   \\
+	}{	.mmi;							   \\
+		(pOutput)	LKEY		T[1] = [T[1]];		   \\
+		(pComputeT)	add		T[0] = SI[2], SJ[1];	   \\
+		(pComputeI)	KEYADDR(IPr[0], I[1]);			   \\
+	} ;;								   \\
+	{	.mmi;							   \\
+		(pComputeT)	SKEY		[IPr[2]] = SJ[1];	   \\
+		(pComputeT)	SKEY		[JP[1]] = SI[2];	   \\
+		(pComputeT)	zxt1		T[0] = T[0];		   \\
+	}{	.mmi;							   \\
+		(pComputeI)	LKEY		SI[0] = [IPr[0]];	   \\
+		(pComputeJ)	KEYADDR(JP[0], J);			   \\
+		(pComputeI)	cmp.eq.unc	pBypass, p0 = I[1], J;	   \\
+	} ;;								   \\
+	{	.mmi;							   \\
+		(pComputeJ)	LKEY		SJ[0] = [JP[0]];	   \\
+		(pOutput)	xor		Data[3] = Data[3], T[1];   \\
+				nop		0x0;			   \\
+	}{	.mmi;							   \\
+		(pComputeT)	KEYADDR(T[0], T[0]);			   \\
+		(pBypass)	mov		SI[0] = SI[1];		   \\
+		(pComputeI)	zxt1		I[0] = IFinal;		   \\
+	} ;;								   \\
+	{	.mmb;							   \\
+		(pOutput)	st1		[OutPtr] = Data[3], 1;	   \\
+		(pComputeI)	add		J = J, SI[0];		   \\
+				br.ctop.sptk.few label;			   \\
+	} ;;
+
+	.text
+
+	.align	32
+
+	.type	RC4, \@function
+	.global	RC4
+
+	.proc	RC4
+	.prologue
+
+RC4:
+	{
+	  	.mmi
+		alloc	r2 = ar.pfs, _NINPUTS, _NLOCALS, _NOUTPUT, _NROTATE
+
+		.rotr Data[4], I[2], IPr[3], SI[3], JP[2], SJ[2], T[2], \\
+		      OutWord[2]
+		.rotp pPhase[4]
+
+		ADDP		InPrefetch = 0, InputBuffer
+		ADDP		KTable = 0, StateTable
+	}
+	{
+		.mmi
+		ADDP		InPtr = 0, InputBuffer
+		ADDP		OutPtr = 0, OutputBuffer
+		mov		RetVal = r0
+	}
+	;;
+	{
+		.mmi
+		lfetch.nt1	[InPrefetch], 0x80
+		ADDP		OutPrefetch = 0, OutputBuffer
+	}
+	{               // Return 0 if the input length is nonsensical
+        	.mib
+		ADDP		StateTable = 0, StateTable
+        	cmp.ge.unc  	L_NOK, L_OK = r0, DataLen
+	(L_NOK) br.ret.sptk.few rp
+	}
+	;;
+	{
+        	.mib
+        	cmp.eq.or  	L_NOK, L_OK = r0, InPtr
+        	cmp.eq.or  	L_NOK, L_OK = r0, OutPtr
+		nop		0x0
+	}
+	{
+		.mib
+        	cmp.eq.or  	L_NOK, L_OK = r0, StateTable
+		nop		0x0
+	(L_NOK) br.ret.sptk.few rp
+	}
+	;;
+		LKEY		I[1] = [KTable], SZ
+/* Prefetch the state-table. It contains 256 elements of size SZ */
+
+#if SZ == 1
+		ADDP		tmp0 = 1*128, StateTable
+#elif SZ == 2
+		ADDP		tmp0 = 3*128, StateTable
+		ADDP		tmp1 = 2*128, StateTable
+#elif SZ == 4
+		ADDP		tmp0 = 7*128, StateTable
+		ADDP		tmp1 = 6*128, StateTable
+#elif SZ == 8
+		ADDP		tmp0 = 15*128, StateTable
+		ADDP		tmp1 = 14*128, StateTable
+#endif
+		;;
+#if SZ >= 8
+		lfetch.fault.nt1		[tmp0], -256	// 15
+		lfetch.fault.nt1		[tmp1], -256;;
+		lfetch.fault.nt1		[tmp0], -256	// 13
+		lfetch.fault.nt1		[tmp1], -256;;
+		lfetch.fault.nt1		[tmp0], -256	// 11
+		lfetch.fault.nt1		[tmp1], -256;;
+		lfetch.fault.nt1		[tmp0], -256	//  9
+		lfetch.fault.nt1		[tmp1], -256;;
+#endif
+#if SZ >= 4
+		lfetch.fault.nt1		[tmp0], -256	//  7
+		lfetch.fault.nt1		[tmp1], -256;;
+		lfetch.fault.nt1		[tmp0], -256	//  5
+		lfetch.fault.nt1		[tmp1], -256;;
+#endif
+#if SZ >= 2
+		lfetch.fault.nt1		[tmp0], -256	//  3
+		lfetch.fault.nt1		[tmp1], -256;;
+#endif
+	{
+		.mii
+		lfetch.fault.nt1		[tmp0]		//  1
+		add		I[1]=1,I[1];;
+		zxt1		I[1]=I[1]
+	}
+	{
+		.mmi
+		lfetch.nt1	[InPrefetch], 0x80
+		lfetch.excl.nt1	[OutPrefetch], 0x80
+		.save		pr, PRSave
+		mov		PRSave = pr
+	} ;;
+	{
+		.mmi
+		lfetch.excl.nt1	[OutPrefetch], 0x80
+		LKEY		J = [KTable], SZ
+		ADDP		EndPtr = DataLen, InPtr
+	}  ;;
+	{
+		.mmi
+		ADDP		EndPtr = -1, EndPtr	// Make it point to
+							// last data byte.
+		mov		One = 1
+		.save		ar.lc, LCSave
+		mov		LCSave = ar.lc
+		.body
+	} ;;
+	{
+		.mmb
+		sub		Remainder = 0, OutPtr
+		cmp.gtu		pSmall, p0 = $threshold, DataLen
+(pSmall)	br.cond.dpnt	.rc4Remainder		// Data too small for
+							// big loop.
+	} ;;
+	{
+		.mmi
+		and		Remainder = 0x7, Remainder
+		;;
+		cmp.eq		pAligned, pUnaligned = Remainder, r0
+		nop		0x0
+	} ;;
+	{
+		.mmb
+.pred.rel	"mutex",pUnaligned,pAligned
+(pUnaligned)	add		Remainder = -1, Remainder
+(pAligned)	sub		Remainder = EndPtr, InPtr
+(pAligned)	br.cond.dptk.many .rc4Aligned
+	} ;;
+	{
+		.mmi
+		nop		0x0
+		nop		0x0
+		mov.i		ar.lc = Remainder
+	}
+
+/* Do the initial few bytes via the compact, modulo-scheduled loop
+   until the output pointer is 8-byte-aligned.  */
+
+		MODSCHED_RC4_PROLOGUE
+		MODSCHED_RC4_LOOP(.RC4AlignLoop)
+
+	{
+		.mib
+		sub		Remainder = EndPtr, InPtr
+		zxt1		IFinal = IFinal
+		clrrrb				// Clear CFM.rrb.pr so
+		;;				// next "mov pr.rot = N"
+						// does the right thing.
+	}
+	{
+		.mmi
+		mov		I[1] = IFinal
+		nop		0x0
+		nop		0x0
+	} ;;
+
+
+.rc4Aligned:
+
+/*
+   Unrolled loop count = (Remainder - ($unroll_count+1)*$phases)/($unroll_count*$phases)
+ */
+
+	{
+		.mlx
+		add	LoopCount = 1 - ($unroll_count + 1)*$phases, Remainder
+		movl		Remainder = 0xaaaaaaaaaaaaaaab
+	} ;;
+	{
+		.mmi
+		setf.sig	f6 = LoopCount		// M2, M3	6 cyc
+		setf.sig	f7 = Remainder		// M2, M3	6 cyc
+		nop		0x0
+	} ;;
+	{
+		.mfb
+		nop		0x0
+		xmpy.hu		f6 = f6, f7
+		nop		0x0
+	} ;;
+	{
+		.mmi
+		getf.sig	LoopCount = f6;;	// M2		5 cyc
+		nop		0x0
+		shr.u		LoopCount = LoopCount, 4
+	} ;;
+	{
+		.mmi
+		nop		0x0
+		nop		0x0
+		mov.i		ar.lc = LoopCount
+	} ;;
+
+/* Now comes the unrolled loop: */
+
+.rc4Prologue:
+___
+
+$iteration = 0;
+
+# Generate the prologue:
+$predicates = 1;
+for ($i = 0; $i < $phases; ++$i) {
+    &emit_body (\$code, \$bypass, $iteration++, $predicates);
+    $predicates = ($predicates << 1) | 1;
+}
+
+$code.=<<___;
+.rc4Loop:
+___
+
+# Generate the body:
+for ($i = 0; $i < $unroll_count*$phases; ++$i) {
+    &emit_body (\$code, \$bypass, $iteration++, $predicates);
+}
+
+$code.=<<___;
+.rc4Epilogue:
+___
+
+# Generate the epilogue:
+for ($i = 0; $i < $phases; ++$i) {
+    $predicates <<= 1;
+    &emit_body (\$code, \$bypass, $iteration++, $predicates);
+}
+
+$code.=<<___;
+	{
+		.mmi
+		lfetch.nt1	[EndPtr]	// fetch line with last byte
+		mov		IFinal = I[1]
+		nop		0x0
+	}
+
+.rc4Remainder:
+	{
+		.mmi
+		sub		Remainder = EndPtr, InPtr	// Calculate
+								// # of bytes
+								// left - 1
+		nop		0x0
+		nop		0x0
+	} ;;
+	{
+		.mib
+		cmp.eq		pDone, p0 = -1, Remainder // done already?
+		mov.i		ar.lc = Remainder
+(pDone)		br.cond.dptk.few .rc4Complete
+	}
+
+/* Do the remaining bytes via the compact, modulo-scheduled loop */
+
+		MODSCHED_RC4_PROLOGUE
+		MODSCHED_RC4_LOOP(.RC4RestLoop)
+
+.rc4Complete:
+	{
+		.mmi
+		add		KTable = -SZ, KTable
+		add		IFinal = -1, IFinal
+		mov		ar.lc = LCSave
+	} ;;
+	{
+		.mii
+		SKEY		[KTable] = J,-SZ
+		zxt1		IFinal = IFinal
+		mov		pr = PRSave, 0x1FFFF
+	} ;;
+	{
+		.mib
+		SKEY		[KTable] = IFinal
+		add		RetVal = 1, r0
+		br.ret.sptk.few	rp
+	} ;;
+___
+
+# Last but not least, emit the code for the bypass-code of the unrolled loop:
+
+$code.=$bypass;
+
+$code.=<<___;
+	.endp RC4
+___
+
+print $code;
+
+close STDOUT;
diff --git a/crypto/rc4/asm/rc4-md5-x86_64.pl b/crypto/rc4/asm/rc4-md5-x86_64.pl
new file mode 100644
index 000000000..890161bac
--- /dev/null
+++ b/crypto/rc4/asm/rc4-md5-x86_64.pl
@@ -0,0 +1,645 @@
+#! /usr/bin/env perl
+# Copyright 2011-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+#
+# ====================================================================
+# Written by Andy Polyakov  for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+
+# June 2011
+#
+# This is RC4+MD5 "stitch" implementation. The idea, as spelled in
+# http://download.intel.com/design/intarch/papers/323686.pdf, is that
+# since both algorithms exhibit instruction-level parallelism, ILP,
+# below theoretical maximum, interleaving them would allow to utilize
+# processor resources better and achieve better performance. RC4
+# instruction sequence is virtually identical to rc4-x86_64.pl, which
+# is heavily based on submission by Maxim Perminov, Maxim Locktyukhin
+# and Jim Guilford of Intel. MD5 is fresh implementation aiming to
+# minimize register usage, which was used as "main thread" with RC4
+# weaved into it, one RC4 round per one MD5 round. In addition to the
+# stiched subroutine the script can generate standalone replacement
+# md5_block_asm_data_order and RC4. Below are performance numbers in
+# cycles per processed byte, less is better, for these the standalone
+# subroutines, sum of them, and stitched one:
+#
+#		RC4	MD5	RC4+MD5	stitch	gain
+# Opteron	6.5(*)	5.4	11.9	7.0	+70%(*)
+# Core2		6.5	5.8	12.3	7.7	+60%
+# Westmere	4.3	5.2	9.5	7.0	+36%
+# Sandy Bridge	4.2	5.5	9.7	6.8	+43%
+# Ivy Bridge	4.1	5.2	9.3	6.0	+54%
+# Haswell	4.0	5.0	9.0	5.7	+60%
+# Skylake	6.3(**)	5.0	11.3	5.3	+110%
+# Atom		9.3	6.5	15.8	11.1	+42%
+# VIA Nano	6.3	5.4	11.7	8.6	+37%
+# Bulldozer	4.5	5.4	9.9	7.7	+29%
+#
+# (*)	rc4-x86_64.pl delivers 5.3 on Opteron, so real improvement
+#	is +53%...
+# (**)	unidentified anomaly;
+
+my ($rc4,$md5)=(1,1);	# what to generate?
+my $D="#" if (!$md5);	# if set to "#", MD5 is stitched into RC4(),
+			# but its result is discarded. Idea here is
+			# to be able to use 'openssl speed rc4' for
+			# benchmarking the stitched subroutine... 
+
+my $flavour = shift;
+my $output  = shift;
+if ($flavour =~ /\./) { $output = $flavour; undef $flavour; }
+
+my $win64=0; $win64=1 if ($flavour =~ /[nm]asm|mingw64/ || $output =~ /\.asm$/);
+
+$0 =~ m/(.*[\/\\])[^\/\\]+$/; my $dir=$1; my $xlate;
+( $xlate="${dir}x86_64-xlate.pl" and -f $xlate ) or
+( $xlate="${dir}../../perlasm/x86_64-xlate.pl" and -f $xlate) or
+die "can't locate x86_64-xlate.pl";
+
+open OUT,"| \"$^X\" \"$xlate\" $flavour \"$output\"";
+*STDOUT=*OUT;
+
+my ($dat,$in0,$out,$ctx,$inp,$len, $func,$nargs);
+
+if ($rc4 && !$md5) {
+  ($dat,$len,$in0,$out) = ("%rdi","%rsi","%rdx","%rcx");
+  $func="RC4";				$nargs=4;
+} elsif ($md5 && !$rc4) {
+  ($ctx,$inp,$len) = ("%rdi","%rsi","%rdx");
+  $func="md5_block_asm_data_order";	$nargs=3;
+} else {
+  ($dat,$in0,$out,$ctx,$inp,$len) = ("%rdi","%rsi","%rdx","%rcx","%r8","%r9");
+  $func="rc4_md5_enc";			$nargs=6;
+  # void rc4_md5_enc(
+  #		RC4_KEY *key,		#
+  #		const void *in0,	# RC4 input
+  #		void *out,		# RC4 output
+  #		MD5_CTX *ctx,		#
+  #		const void *inp,	# MD5 input
+  #		size_t len);		# number of 64-byte blocks
+}
+
+my @K=(	0xd76aa478,0xe8c7b756,0x242070db,0xc1bdceee,
+	0xf57c0faf,0x4787c62a,0xa8304613,0xfd469501,
+	0x698098d8,0x8b44f7af,0xffff5bb1,0x895cd7be,
+	0x6b901122,0xfd987193,0xa679438e,0x49b40821,
+
+	0xf61e2562,0xc040b340,0x265e5a51,0xe9b6c7aa,
+	0xd62f105d,0x02441453,0xd8a1e681,0xe7d3fbc8,
+	0x21e1cde6,0xc33707d6,0xf4d50d87,0x455a14ed,
+	0xa9e3e905,0xfcefa3f8,0x676f02d9,0x8d2a4c8a,
+
+	0xfffa3942,0x8771f681,0x6d9d6122,0xfde5380c,
+	0xa4beea44,0x4bdecfa9,0xf6bb4b60,0xbebfbc70,
+	0x289b7ec6,0xeaa127fa,0xd4ef3085,0x04881d05,
+	0xd9d4d039,0xe6db99e5,0x1fa27cf8,0xc4ac5665,
+
+	0xf4292244,0x432aff97,0xab9423a7,0xfc93a039,
+	0x655b59c3,0x8f0ccc92,0xffeff47d,0x85845dd1,
+	0x6fa87e4f,0xfe2ce6e0,0xa3014314,0x4e0811a1,
+	0xf7537e82,0xbd3af235,0x2ad7d2bb,0xeb86d391	);
+
+my @V=("%r8d","%r9d","%r10d","%r11d");	# MD5 registers
+my $tmp="%r12d";
+
+my @XX=("%rbp","%rsi");			# RC4 registers
+my @TX=("%rax","%rbx");
+my $YY="%rcx";
+my $TY="%rdx";
+
+my $MOD=32;				# 16, 32 or 64
+
+$code.=<<___;
+.text
+.align 16
+
+.globl	$func
+.type	$func,\@function,$nargs
+$func:
+	cmp	\$0,$len
+	je	.Labort
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+	sub	\$40,%rsp
+.Lbody:
+___
+if ($rc4) {
+$code.=<<___;
+$D#md5#	mov	$ctx,%r11		# reassign arguments
+	mov	$len,%r12
+	mov	$in0,%r13
+	mov	$out,%r14
+$D#md5#	mov	$inp,%r15
+___
+    $ctx="%r11"	if ($md5);		# reassign arguments
+    $len="%r12";
+    $in0="%r13";
+    $out="%r14";
+    $inp="%r15"	if ($md5);
+    $inp=$in0	if (!$md5);
+$code.=<<___;
+	xor	$XX[0],$XX[0]
+	xor	$YY,$YY
+
+	lea	8($dat),$dat
+	mov	-8($dat),$XX[0]#b
+	mov	-4($dat),$YY#b
+
+	inc	$XX[0]#b
+	sub	$in0,$out
+	movl	($dat,$XX[0],4),$TX[0]#d
+___
+$code.=<<___ if (!$md5);
+	xor	$TX[1],$TX[1]
+	test	\$-128,$len
+	jz	.Loop1
+	sub	$XX[0],$TX[1]
+	and	\$`$MOD-1`,$TX[1]
+	jz	.Loop${MOD}_is_hot
+	sub	$TX[1],$len
+.Loop${MOD}_warmup:
+	add	$TX[0]#b,$YY#b
+	movl	($dat,$YY,4),$TY#d
+	movl	$TX[0]#d,($dat,$YY,4)
+	movl	$TY#d,($dat,$XX[0],4)
+	add	$TY#b,$TX[0]#b
+	inc	$XX[0]#b
+	movl	($dat,$TX[0],4),$TY#d
+	movl	($dat,$XX[0],4),$TX[0]#d
+	xorb	($in0),$TY#b
+	movb	$TY#b,($out,$in0)
+	lea	1($in0),$in0
+	dec	$TX[1]
+	jnz	.Loop${MOD}_warmup
+
+	mov	$YY,$TX[1]
+	xor	$YY,$YY
+	mov	$TX[1]#b,$YY#b
+
+.Loop${MOD}_is_hot:
+	mov	$len,32(%rsp)		# save original $len
+	shr	\$6,$len		# number of 64-byte blocks
+___
+  if ($D && !$md5) {			# stitch in dummy MD5
+    $md5=1;
+    $ctx="%r11";
+    $inp="%r15";
+    $code.=<<___;
+	mov	%rsp,$ctx
+	mov	$in0,$inp
+___
+  }
+}
+$code.=<<___;
+#rc4#	add	$TX[0]#b,$YY#b
+#rc4#	lea	($dat,$XX[0],4),$XX[1]
+	shl	\$6,$len
+	add	$inp,$len		# pointer to the end of input
+	mov	$len,16(%rsp)
+
+#md5#	mov	$ctx,24(%rsp)		# save pointer to MD5_CTX
+#md5#	mov	0*4($ctx),$V[0]		# load current hash value from MD5_CTX
+#md5#	mov	1*4($ctx),$V[1]
+#md5#	mov	2*4($ctx),$V[2]
+#md5#	mov	3*4($ctx),$V[3]
+	jmp	.Loop
+
+.align	16
+.Loop:
+#md5#	mov	$V[0],0*4(%rsp)		# put aside current hash value
+#md5#	mov	$V[1],1*4(%rsp)
+#md5#	mov	$V[2],2*4(%rsp)
+#md5#	mov	$V[3],$tmp		# forward reference
+#md5#	mov	$V[3],3*4(%rsp)
+___
+
+sub R0 {
+  my ($i,$a,$b,$c,$d)=@_;
+  my @rot0=(7,12,17,22);
+  my $j=$i%16;
+  my $k=$i%$MOD;
+  my $xmm="%xmm".($j&1);
+    $code.="	movdqu	($in0),%xmm2\n"		if ($rc4 && $j==15);
+    $code.="	add	\$$MOD,$XX[0]#b\n"	if ($rc4 && $j==15 && $k==$MOD-1);
+    $code.="	pxor	$xmm,$xmm\n"		if ($rc4 && $j<=1);
+    $code.=<<___;
+#rc4#	movl	($dat,$YY,4),$TY#d
+#md5#	xor	$c,$tmp
+#rc4#	movl	$TX[0]#d,($dat,$YY,4)
+#md5#	and	$b,$tmp
+#md5#	add	4*`$j`($inp),$a
+#rc4#	add	$TY#b,$TX[0]#b
+#rc4#	movl	`4*(($k+1)%$MOD)`(`$k==$MOD-1?"$dat,$XX[0],4":"$XX[1]"`),$TX[1]#d
+#md5#	add	\$$K[$i],$a
+#md5#	xor	$d,$tmp
+#rc4#	movz	$TX[0]#b,$TX[0]#d
+#rc4#	movl	$TY#d,4*$k($XX[1])
+#md5#	add	$tmp,$a
+#rc4#	add	$TX[1]#b,$YY#b
+#md5#	rol	\$$rot0[$j%4],$a
+#md5#	mov	`$j==15?"$b":"$c"`,$tmp		# forward reference
+#rc4#	pinsrw	\$`($j>>1)&7`,($dat,$TX[0],4),$xmm\n
+#md5#	add	$b,$a
+___
+    $code.=<<___ if ($rc4 && $j==15 && $k==$MOD-1);
+	mov	$YY,$XX[1]
+	xor	$YY,$YY				# keyword to partial register
+	mov	$XX[1]#b,$YY#b
+	lea	($dat,$XX[0],4),$XX[1]
+___
+    $code.=<<___ if ($rc4 && $j==15);
+	psllq	\$8,%xmm1
+	pxor	%xmm0,%xmm2
+	pxor	%xmm1,%xmm2
+___
+}
+sub R1 {
+  my ($i,$a,$b,$c,$d)=@_;
+  my @rot1=(5,9,14,20);
+  my $j=$i%16;
+  my $k=$i%$MOD;
+  my $xmm="%xmm".($j&1);
+    $code.="	movdqu	16($in0),%xmm3\n"	if ($rc4 && $j==15);
+    $code.="	add	\$$MOD,$XX[0]#b\n"	if ($rc4 && $j==15 && $k==$MOD-1);
+    $code.="	pxor	$xmm,$xmm\n"		if ($rc4 && $j<=1);
+    $code.=<<___;
+#rc4#	movl	($dat,$YY,4),$TY#d
+#md5#	xor	$b,$tmp
+#rc4#	movl	$TX[0]#d,($dat,$YY,4)
+#md5#	and	$d,$tmp
+#md5#	add	4*`((1+5*$j)%16)`($inp),$a
+#rc4#	add	$TY#b,$TX[0]#b
+#rc4#	movl	`4*(($k+1)%$MOD)`(`$k==$MOD-1?"$dat,$XX[0],4":"$XX[1]"`),$TX[1]#d
+#md5#	add	\$$K[$i],$a
+#md5#	xor	$c,$tmp
+#rc4#	movz	$TX[0]#b,$TX[0]#d
+#rc4#	movl	$TY#d,4*$k($XX[1])
+#md5#	add	$tmp,$a
+#rc4#	add	$TX[1]#b,$YY#b
+#md5#	rol	\$$rot1[$j%4],$a
+#md5#	mov	`$j==15?"$c":"$b"`,$tmp		# forward reference
+#rc4#	pinsrw	\$`($j>>1)&7`,($dat,$TX[0],4),$xmm\n
+#md5#	add	$b,$a
+___
+    $code.=<<___ if ($rc4 && $j==15 && $k==$MOD-1);
+	mov	$YY,$XX[1]
+	xor	$YY,$YY				# keyword to partial register
+	mov	$XX[1]#b,$YY#b
+	lea	($dat,$XX[0],4),$XX[1]
+___
+    $code.=<<___ if ($rc4 && $j==15);
+	psllq	\$8,%xmm1
+	pxor	%xmm0,%xmm3
+	pxor	%xmm1,%xmm3
+___
+}
+sub R2 {
+  my ($i,$a,$b,$c,$d)=@_;
+  my @rot2=(4,11,16,23);
+  my $j=$i%16;
+  my $k=$i%$MOD;
+  my $xmm="%xmm".($j&1);
+    $code.="	movdqu	32($in0),%xmm4\n"	if ($rc4 && $j==15);
+    $code.="	add	\$$MOD,$XX[0]#b\n"	if ($rc4 && $j==15 && $k==$MOD-1);
+    $code.="	pxor	$xmm,$xmm\n"		if ($rc4 && $j<=1);
+    $code.=<<___;
+#rc4#	movl	($dat,$YY,4),$TY#d
+#md5#	xor	$c,$tmp
+#rc4#	movl	$TX[0]#d,($dat,$YY,4)
+#md5#	xor	$b,$tmp
+#md5#	add	4*`((5+3*$j)%16)`($inp),$a
+#rc4#	add	$TY#b,$TX[0]#b
+#rc4#	movl	`4*(($k+1)%$MOD)`(`$k==$MOD-1?"$dat,$XX[0],4":"$XX[1]"`),$TX[1]#d
+#md5#	add	\$$K[$i],$a
+#rc4#	movz	$TX[0]#b,$TX[0]#d
+#md5#	add	$tmp,$a
+#rc4#	movl	$TY#d,4*$k($XX[1])
+#rc4#	add	$TX[1]#b,$YY#b
+#md5#	rol	\$$rot2[$j%4],$a
+#md5#	mov	`$j==15?"\\\$-1":"$c"`,$tmp	# forward reference
+#rc4#	pinsrw	\$`($j>>1)&7`,($dat,$TX[0],4),$xmm\n
+#md5#	add	$b,$a
+___
+    $code.=<<___ if ($rc4 && $j==15 && $k==$MOD-1);
+	mov	$YY,$XX[1]
+	xor	$YY,$YY				# keyword to partial register
+	mov	$XX[1]#b,$YY#b
+	lea	($dat,$XX[0],4),$XX[1]
+___
+    $code.=<<___ if ($rc4 && $j==15);
+	psllq	\$8,%xmm1
+	pxor	%xmm0,%xmm4
+	pxor	%xmm1,%xmm4
+___
+}
+sub R3 {
+  my ($i,$a,$b,$c,$d)=@_;
+  my @rot3=(6,10,15,21);
+  my $j=$i%16;
+  my $k=$i%$MOD;
+  my $xmm="%xmm".($j&1);
+    $code.="	movdqu	48($in0),%xmm5\n"	if ($rc4 && $j==15);
+    $code.="	add	\$$MOD,$XX[0]#b\n"	if ($rc4 && $j==15 && $k==$MOD-1);
+    $code.="	pxor	$xmm,$xmm\n"		if ($rc4 && $j<=1);
+    $code.=<<___;
+#rc4#	movl	($dat,$YY,4),$TY#d
+#md5#	xor	$d,$tmp
+#rc4#	movl	$TX[0]#d,($dat,$YY,4)
+#md5#	or	$b,$tmp
+#md5#	add	4*`((7*$j)%16)`($inp),$a
+#rc4#	add	$TY#b,$TX[0]#b
+#rc4#	movl	`4*(($k+1)%$MOD)`(`$k==$MOD-1?"$dat,$XX[0],4":"$XX[1]"`),$TX[1]#d
+#md5#	add	\$$K[$i],$a
+#rc4#	movz	$TX[0]#b,$TX[0]#d
+#md5#	xor	$c,$tmp
+#rc4#	movl	$TY#d,4*$k($XX[1])
+#md5#	add	$tmp,$a
+#rc4#	add	$TX[1]#b,$YY#b
+#md5#	rol	\$$rot3[$j%4],$a
+#md5#	mov	\$-1,$tmp			# forward reference
+#rc4#	pinsrw	\$`($j>>1)&7`,($dat,$TX[0],4),$xmm\n
+#md5#	add	$b,$a
+___
+    $code.=<<___ if ($rc4 && $j==15);
+	mov	$XX[0],$XX[1]
+	xor	$XX[0],$XX[0]			# keyword to partial register
+	mov	$XX[1]#b,$XX[0]#b
+	mov	$YY,$XX[1]
+	xor	$YY,$YY				# keyword to partial register
+	mov	$XX[1]#b,$YY#b
+	lea	($dat,$XX[0],4),$XX[1]
+	psllq	\$8,%xmm1
+	pxor	%xmm0,%xmm5
+	pxor	%xmm1,%xmm5
+___
+}
+
+my $i=0;
+for(;$i<16;$i++) { R0($i,@V); unshift(@V,pop(@V)); push(@TX,shift(@TX)); }
+for(;$i<32;$i++) { R1($i,@V); unshift(@V,pop(@V)); push(@TX,shift(@TX)); }
+for(;$i<48;$i++) { R2($i,@V); unshift(@V,pop(@V)); push(@TX,shift(@TX)); }
+for(;$i<64;$i++) { R3($i,@V); unshift(@V,pop(@V)); push(@TX,shift(@TX)); }
+
+$code.=<<___;
+#md5#	add	0*4(%rsp),$V[0]		# accumulate hash value
+#md5#	add	1*4(%rsp),$V[1]
+#md5#	add	2*4(%rsp),$V[2]
+#md5#	add	3*4(%rsp),$V[3]
+
+#rc4#	movdqu	%xmm2,($out,$in0)	# write RC4 output
+#rc4#	movdqu	%xmm3,16($out,$in0)
+#rc4#	movdqu	%xmm4,32($out,$in0)
+#rc4#	movdqu	%xmm5,48($out,$in0)
+#md5#	lea	64($inp),$inp
+#rc4#	lea	64($in0),$in0
+	cmp	16(%rsp),$inp		# are we done?
+	jb	.Loop
+
+#md5#	mov	24(%rsp),$len		# restore pointer to MD5_CTX
+#rc4#	sub	$TX[0]#b,$YY#b		# correct $YY
+#md5#	mov	$V[0],0*4($len)		# write MD5_CTX
+#md5#	mov	$V[1],1*4($len)
+#md5#	mov	$V[2],2*4($len)
+#md5#	mov	$V[3],3*4($len)
+___
+$code.=<<___ if ($rc4 && (!$md5 || $D));
+	mov	32(%rsp),$len		# restore original $len
+	and	\$63,$len		# remaining bytes
+	jnz	.Loop1
+	jmp	.Ldone
+	
+.align	16
+.Loop1:
+	add	$TX[0]#b,$YY#b
+	movl	($dat,$YY,4),$TY#d
+	movl	$TX[0]#d,($dat,$YY,4)
+	movl	$TY#d,($dat,$XX[0],4)
+	add	$TY#b,$TX[0]#b
+	inc	$XX[0]#b
+	movl	($dat,$TX[0],4),$TY#d
+	movl	($dat,$XX[0],4),$TX[0]#d
+	xorb	($in0),$TY#b
+	movb	$TY#b,($out,$in0)
+	lea	1($in0),$in0
+	dec	$len
+	jnz	.Loop1
+
+.Ldone:
+___
+$code.=<<___;
+#rc4#	sub	\$1,$XX[0]#b
+#rc4#	movl	$XX[0]#d,-8($dat)
+#rc4#	movl	$YY#d,-4($dat)
+
+	mov	40(%rsp),%r15
+	mov	48(%rsp),%r14
+	mov	56(%rsp),%r13
+	mov	64(%rsp),%r12
+	mov	72(%rsp),%rbp
+	mov	80(%rsp),%rbx
+	lea	88(%rsp),%rsp
+.Lepilogue:
+.Labort:
+	ret
+.size $func,.-$func
+___
+
+if ($rc4 && $D) {	# sole purpose of this section is to provide
+			# option to use the generated module as drop-in
+			# replacement for rc4-x86_64.pl for debugging
+			# and testing purposes...
+my ($idx,$ido)=("%r8","%r9");
+my ($dat,$len,$inp)=("%rdi","%rsi","%rdx");
+
+$code.=<<___;
+.globl	RC4_set_key
+.type	RC4_set_key,\@function,3
+.align	16
+RC4_set_key:
+	lea	8($dat),$dat
+	lea	($inp,$len),$inp
+	neg	$len
+	mov	$len,%rcx
+	xor	%eax,%eax
+	xor	$ido,$ido
+	xor	%r10,%r10
+	xor	%r11,%r11
+	jmp	.Lw1stloop
+
+.align	16
+.Lw1stloop:
+	mov	%eax,($dat,%rax,4)
+	add	\$1,%al
+	jnc	.Lw1stloop
+
+	xor	$ido,$ido
+	xor	$idx,$idx
+.align	16
+.Lw2ndloop:
+	mov	($dat,$ido,4),%r10d
+	add	($inp,$len,1),$idx#b
+	add	%r10b,$idx#b
+	add	\$1,$len
+	mov	($dat,$idx,4),%r11d
+	cmovz	%rcx,$len
+	mov	%r10d,($dat,$idx,4)
+	mov	%r11d,($dat,$ido,4)
+	add	\$1,$ido#b
+	jnc	.Lw2ndloop
+
+	xor	%eax,%eax
+	mov	%eax,-8($dat)
+	mov	%eax,-4($dat)
+	ret
+.size	RC4_set_key,.-RC4_set_key
+
+.globl	RC4_options
+.type	RC4_options,\@abi-omnipotent
+.align	16
+RC4_options:
+	lea	.Lopts(%rip),%rax
+	ret
+.align	64
+.Lopts:
+.asciz	"rc4(64x,int)"
+.align	64
+.size	RC4_options,.-RC4_options
+___
+}
+# EXCEPTION_DISPOSITION handler (EXCEPTION_RECORD *rec,ULONG64 frame,
+#		CONTEXT *context,DISPATCHER_CONTEXT *disp)
+if ($win64) {
+my $rec="%rcx";
+my $frame="%rdx";
+my $context="%r8";
+my $disp="%r9";
+
+$code.=<<___;
+.extern	__imp_RtlVirtualUnwind
+.type	se_handler,\@abi-omnipotent
+.align	16
+se_handler:
+	push	%rsi
+	push	%rdi
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+	pushfq
+	sub	\$64,%rsp
+
+	mov	120($context),%rax	# pull context->Rax
+	mov	248($context),%rbx	# pull context->Rip
+
+	lea	.Lbody(%rip),%r10
+	cmp	%r10,%rbx		# context->Rip<.Lbody
+	jb	.Lin_prologue
+
+	mov	152($context),%rax	# pull context->Rsp
+
+	lea	.Lepilogue(%rip),%r10
+	cmp	%r10,%rbx		# context->Rip>=.Lepilogue
+	jae	.Lin_prologue
+
+	mov	40(%rax),%r15
+	mov	48(%rax),%r14
+	mov	56(%rax),%r13
+	mov	64(%rax),%r12
+	mov	72(%rax),%rbp
+	mov	80(%rax),%rbx
+	lea	88(%rax),%rax
+
+	mov	%rbx,144($context)	# restore context->Rbx
+	mov	%rbp,160($context)	# restore context->Rbp
+	mov	%r12,216($context)	# restore context->R12
+	mov	%r13,224($context)	# restore context->R12
+	mov	%r14,232($context)	# restore context->R14
+	mov	%r15,240($context)	# restore context->R15
+
+.Lin_prologue:
+	mov	8(%rax),%rdi
+	mov	16(%rax),%rsi
+	mov	%rax,152($context)	# restore context->Rsp
+	mov	%rsi,168($context)	# restore context->Rsi
+	mov	%rdi,176($context)	# restore context->Rdi
+
+	mov	40($disp),%rdi		# disp->ContextRecord
+	mov	$context,%rsi		# context
+	mov	\$154,%ecx		# sizeof(CONTEXT)
+	.long	0xa548f3fc		# cld; rep movsq
+
+	mov	$disp,%rsi
+	xor	%rcx,%rcx		# arg1, UNW_FLAG_NHANDLER
+	mov	8(%rsi),%rdx		# arg2, disp->ImageBase
+	mov	0(%rsi),%r8		# arg3, disp->ControlPc
+	mov	16(%rsi),%r9		# arg4, disp->FunctionEntry
+	mov	40(%rsi),%r10		# disp->ContextRecord
+	lea	56(%rsi),%r11		# &disp->HandlerData
+	lea	24(%rsi),%r12		# &disp->EstablisherFrame
+	mov	%r10,32(%rsp)		# arg5
+	mov	%r11,40(%rsp)		# arg6
+	mov	%r12,48(%rsp)		# arg7
+	mov	%rcx,56(%rsp)		# arg8, (NULL)
+	call	*__imp_RtlVirtualUnwind(%rip)
+
+	mov	\$1,%eax		# ExceptionContinueSearch
+	add	\$64,%rsp
+	popfq
+	pop	%r15
+	pop	%r14
+	pop	%r13
+	pop	%r12
+	pop	%rbp
+	pop	%rbx
+	pop	%rdi
+	pop	%rsi
+	ret
+.size	se_handler,.-se_handler
+
+.section	.pdata
+.align	4
+	.rva	.LSEH_begin_$func
+	.rva	.LSEH_end_$func
+	.rva	.LSEH_info_$func
+
+.section	.xdata
+.align	8
+.LSEH_info_$func:
+	.byte	9,0,0,0
+	.rva	se_handler
+___
+}
+
+sub reg_part {
+my ($reg,$conv)=@_;
+    if ($reg =~ /%r[0-9]+/)     { $reg .= $conv; }
+    elsif ($conv eq "b")        { $reg =~ s/%[er]([^x]+)x?/%$1l/;       }
+    elsif ($conv eq "w")        { $reg =~ s/%[er](.+)/%$1/;             }
+    elsif ($conv eq "d")        { $reg =~ s/%[er](.+)/%e$1/;            }
+    return $reg;
+}
+
+$code =~ s/(%[a-z0-9]+)#([bwd])/reg_part($1,$2)/gem;
+$code =~ s/\`([^\`]*)\`/eval $1/gem;
+$code =~ s/pinsrw\s+\$0,/movd	/gm;
+
+$code =~ s/#md5#//gm	if ($md5);
+$code =~ s/#rc4#//gm	if ($rc4);
+
+print $code;
+
+close STDOUT;
diff --git a/crypto/rc4/asm/rc4-parisc.pl b/crypto/rc4/asm/rc4-parisc.pl
new file mode 100644
index 000000000..006b6b01a
--- /dev/null
+++ b/crypto/rc4/asm/rc4-parisc.pl
@@ -0,0 +1,321 @@
+#! /usr/bin/env perl
+# Copyright 2009-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+
+# ====================================================================
+# Written by Andy Polyakov  for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+
+# RC4 for PA-RISC.
+
+# June 2009.
+#
+# Performance is 33% better than gcc 3.2 generated code on PA-7100LC.
+# For reference, [4x] unrolled loop is >40% faster than folded one.
+# It's possible to unroll loop 8 times on PA-RISC 2.0, but improvement
+# is believed to be not sufficient to justify the effort...
+#
+# Special thanks to polarhome.com for providing HP-UX account.
+
+$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+
+$flavour = shift;
+$output = shift;
+open STDOUT,">$output";
+
+if ($flavour =~ /64/) {
+	$LEVEL		="2.0W";
+	$SIZE_T		=8;
+	$FRAME_MARKER	=80;
+	$SAVED_RP	=16;
+	$PUSH		="std";
+	$PUSHMA		="std,ma";
+	$POP		="ldd";
+	$POPMB		="ldd,mb";
+} else {
+	$LEVEL		="1.0";
+	$SIZE_T		=4;
+	$FRAME_MARKER	=48;
+	$SAVED_RP	=20;
+	$PUSH		="stw";
+	$PUSHMA		="stwm";
+	$POP		="ldw";
+	$POPMB		="ldwm";
+}
+
+$FRAME=4*$SIZE_T+$FRAME_MARKER;	# 4 saved regs + frame marker
+				#                [+ argument transfer]
+$SZ=1;				# defaults to RC4_CHAR
+if (open CONF,"<${dir}../../opensslconf.h") {
+    while() {
+	if (m/#\s*define\s+RC4_INT\s+(.*)/) {
+	    $SZ = ($1=~/char$/) ? 1 : 4;
+	    last;
+	}
+    }
+    close CONF;
+}
+
+if ($SZ==1) {	# RC4_CHAR
+    $LD="ldb";
+    $LDX="ldbx";
+    $MKX="addl";
+    $ST="stb";
+} else {	# RC4_INT (~5% faster than RC4_CHAR on PA-7100LC)
+    $LD="ldw";
+    $LDX="ldwx,s";
+    $MKX="sh2addl";
+    $ST="stw";
+}
+
+$key="%r26";
+$len="%r25";
+$inp="%r24";
+$out="%r23";
+
+@XX=("%r19","%r20");
+@TX=("%r21","%r22");
+$YY="%r28";
+$TY="%r29";
+
+$acc="%r1";
+$ix="%r2";
+$iy="%r3";
+$dat0="%r4";
+$dat1="%r5";
+$rem="%r6";
+$mask="%r31";
+
+sub unrolledloopbody {
+for ($i=0;$i<4;$i++) {
+$code.=<<___;
+	ldo	1($XX[0]),$XX[1]
+	`sprintf("$LDX	%$TY(%$key),%$dat1") if ($i>0)`	
+	and	$mask,$XX[1],$XX[1]
+	$LDX	$YY($key),$TY
+	$MKX	$YY,$key,$ix
+	$LDX	$XX[1]($key),$TX[1]
+	$MKX	$XX[0],$key,$iy
+	$ST	$TX[0],0($ix)
+	comclr,<> $XX[1],$YY,%r0	; conditional
+	copy	$TX[0],$TX[1]		; move
+	`sprintf("%sdep	%$dat1,%d,8,%$acc",$i==1?"z":"",8*($i-1)+7) if ($i>0)`
+	$ST	$TY,0($iy)
+	addl	$TX[0],$TY,$TY
+	addl	$TX[1],$YY,$YY
+	and	$mask,$TY,$TY
+	and	$mask,$YY,$YY
+___
+push(@TX,shift(@TX)); push(@XX,shift(@XX));	# "rotate" registers
+} }
+
+sub foldedloop {
+my ($label,$count)=@_;
+$code.=<<___;
+$label
+	$MKX	$YY,$key,$iy
+	$LDX	$YY($key),$TY
+	$MKX	$XX[0],$key,$ix
+	$ST	$TX[0],0($iy)
+	ldo	1($XX[0]),$XX[0]
+	$ST	$TY,0($ix)
+	addl	$TX[0],$TY,$TY
+	ldbx	$inp($out),$dat1
+	and	$mask,$TY,$TY
+	and	$mask,$XX[0],$XX[0]
+	$LDX	$TY($key),$acc
+	$LDX	$XX[0]($key),$TX[0]
+	ldo	1($out),$out
+	xor	$dat1,$acc,$acc
+	addl	$TX[0],$YY,$YY
+	stb	$acc,-1($out)
+	addib,<> -1,$count,$label	; $count is always small
+	and	$mask,$YY,$YY
+___
+}
+
+$code=<<___;
+	.LEVEL	$LEVEL
+	.SPACE	\$TEXT\$
+	.SUBSPA	\$CODE\$,QUAD=0,ALIGN=8,ACCESS=0x2C,CODE_ONLY
+
+	.EXPORT	RC4,ENTRY,ARGW0=GR,ARGW1=GR,ARGW2=GR,ARGW3=GR
+RC4
+	.PROC
+	.CALLINFO	FRAME=`$FRAME-4*$SIZE_T`,NO_CALLS,SAVE_RP,ENTRY_GR=6
+	.ENTRY
+	$PUSH	%r2,-$SAVED_RP(%sp)	; standard prologue
+	$PUSHMA	%r3,$FRAME(%sp)
+	$PUSH	%r4,`-$FRAME+1*$SIZE_T`(%sp)
+	$PUSH	%r5,`-$FRAME+2*$SIZE_T`(%sp)
+	$PUSH	%r6,`-$FRAME+3*$SIZE_T`(%sp)
+
+	cmpib,*= 0,$len,L\$abort
+	sub	$inp,$out,$inp		; distance between $inp and $out
+
+	$LD	`0*$SZ`($key),$XX[0]
+	$LD	`1*$SZ`($key),$YY
+	ldo	`2*$SZ`($key),$key
+
+	ldi	0xff,$mask
+	ldi	3,$dat0		
+
+	ldo	1($XX[0]),$XX[0]	; warm up loop
+	and	$mask,$XX[0],$XX[0]
+	$LDX	$XX[0]($key),$TX[0]
+	addl	$TX[0],$YY,$YY
+	cmpib,*>>= 6,$len,L\$oop1	; is $len large enough to bother?
+	and	$mask,$YY,$YY
+
+	and,<>	$out,$dat0,$rem		; is $out aligned?
+	b	L\$alignedout
+	subi	4,$rem,$rem
+	sub	$len,$rem,$len
+___
+&foldedloop("L\$alignout",$rem);	# process till $out is aligned
+
+$code.=<<___;
+L\$alignedout				; $len is at least 4 here
+	and,<>	$inp,$dat0,$acc		; is $inp aligned?
+	b	L\$oop4
+	sub	$inp,$acc,$rem		; align $inp
+
+	sh3addl	$acc,%r0,$acc
+	subi	32,$acc,$acc
+	mtctl	$acc,%cr11		; load %sar with vshd align factor
+	ldwx	$rem($out),$dat0
+	ldo	4($rem),$rem
+L\$oop4misalignedinp
+___
+&unrolledloopbody();
+$code.=<<___;
+	$LDX	$TY($key),$ix
+	ldwx	$rem($out),$dat1
+	ldo	-4($len),$len
+	or	$ix,$acc,$acc		; last piece, no need to dep
+	vshd	$dat0,$dat1,$iy		; align data
+	copy	$dat1,$dat0
+	xor	$iy,$acc,$acc
+	stw	$acc,0($out)
+	cmpib,*<< 3,$len,L\$oop4misalignedinp
+	ldo	4($out),$out
+	cmpib,*= 0,$len,L\$done
+	nop
+	b	L\$oop1
+	nop
+
+	.ALIGN	8
+L\$oop4
+___
+&unrolledloopbody();
+$code.=<<___;
+	$LDX	$TY($key),$ix
+	ldwx	$inp($out),$dat0
+	ldo	-4($len),$len
+	or	$ix,$acc,$acc		; last piece, no need to dep
+	xor	$dat0,$acc,$acc
+	stw	$acc,0($out)
+	cmpib,*<< 3,$len,L\$oop4
+	ldo	4($out),$out
+	cmpib,*= 0,$len,L\$done
+	nop
+___
+&foldedloop("L\$oop1",$len);
+$code.=<<___;
+L\$done
+	$POP	`-$FRAME-$SAVED_RP`(%sp),%r2
+	ldo	-1($XX[0]),$XX[0]	; chill out loop
+	sub	$YY,$TX[0],$YY
+	and	$mask,$XX[0],$XX[0]
+	and	$mask,$YY,$YY
+	$ST	$XX[0],`-2*$SZ`($key)
+	$ST	$YY,`-1*$SZ`($key)
+	$POP	`-$FRAME+1*$SIZE_T`(%sp),%r4
+	$POP	`-$FRAME+2*$SIZE_T`(%sp),%r5
+	$POP	`-$FRAME+3*$SIZE_T`(%sp),%r6
+L\$abort
+	bv	(%r2)
+	.EXIT
+	$POPMB	-$FRAME(%sp),%r3
+	.PROCEND
+___
+
+$code.=<<___;
+
+	.EXPORT	RC4_set_key,ENTRY,ARGW0=GR,ARGW1=GR,ARGW2=GR
+	.ALIGN	8
+RC4_set_key
+	.PROC
+	.CALLINFO	NO_CALLS
+	.ENTRY
+	$ST	%r0,`0*$SZ`($key)
+	$ST	%r0,`1*$SZ`($key)
+	ldo	`2*$SZ`($key),$key
+	copy	%r0,@XX[0]
+L\$1st
+	$ST	@XX[0],0($key)
+	ldo	1(@XX[0]),@XX[0]
+	bb,>=	@XX[0],`31-8`,L\$1st	; @XX[0]<256
+	ldo	$SZ($key),$key
+
+	ldo	`-256*$SZ`($key),$key	; rewind $key
+	addl	$len,$inp,$inp		; $inp to point at the end
+	sub	%r0,$len,%r23		; inverse index
+	copy	%r0,@XX[0]
+	copy	%r0,@XX[1]
+	ldi	0xff,$mask
+
+L\$2nd
+	$LDX	@XX[0]($key),@TX[0]
+	ldbx	%r23($inp),@TX[1]
+	addi,nuv 1,%r23,%r23		; increment and conditional
+	sub	%r0,$len,%r23		; inverse index
+	addl	@TX[0],@XX[1],@XX[1]
+	addl	@TX[1],@XX[1],@XX[1]
+	and	$mask,@XX[1],@XX[1]
+	$MKX	@XX[0],$key,$TY
+	$LDX	@XX[1]($key),@TX[1]
+	$MKX	@XX[1],$key,$YY
+	ldo	1(@XX[0]),@XX[0]
+	$ST	@TX[0],0($YY)
+	bb,>=	@XX[0],`31-8`,L\$2nd	; @XX[0]<256
+	$ST	@TX[1],0($TY)
+
+	bv,n	(%r2)
+	.EXIT
+	nop
+	.PROCEND
+
+	.EXPORT	RC4_options,ENTRY
+	.ALIGN	8
+RC4_options
+	.PROC
+	.CALLINFO	NO_CALLS
+	.ENTRY
+	blr	%r0,%r28
+	ldi	3,%r1
+L\$pic
+	andcm	%r28,%r1,%r28
+	bv	(%r2)
+	.EXIT
+	ldo	L\$opts-L\$pic(%r28),%r28
+	.PROCEND
+	.ALIGN	8
+L\$opts
+	.STRINGZ "rc4(4x,`$SZ==1?"char":"int"`)"
+	.STRINGZ "RC4 for PA-RISC, CRYPTOGAMS by "
+___
+$code =~ s/\`([^\`]*)\`/eval $1/gem;
+$code =~ s/cmpib,\*/comib,/gm	if ($SIZE_T==4);
+$code =~ s/\bbv\b/bve/gm	if ($SIZE_T==8);
+
+print $code;
+close STDOUT;
diff --git a/crypto/rc4/asm/rc4-s390x.pl b/crypto/rc4/asm/rc4-s390x.pl
new file mode 100644
index 000000000..5589503aa
--- /dev/null
+++ b/crypto/rc4/asm/rc4-s390x.pl
@@ -0,0 +1,241 @@
+#! /usr/bin/env perl
+# Copyright 2009-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+#
+# ====================================================================
+# Written by Andy Polyakov  for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+#
+# February 2009
+#
+# Performance is 2x of gcc 3.4.6 on z10. Coding "secret" is to
+# "cluster" Address Generation Interlocks, so that one pipeline stall
+# resolves several dependencies.
+
+# November 2010.
+#
+# Adapt for -m31 build. If kernel supports what's called "highgprs"
+# feature on Linux [see /proc/cpuinfo], it's possible to use 64-bit
+# instructions and achieve "64-bit" performance even in 31-bit legacy
+# application context. The feature is not specific to any particular
+# processor, as long as it's "z-CPU". Latter implies that the code
+# remains z/Architecture specific. On z990 it was measured to perform
+# 50% better than code generated by gcc 4.3.
+
+$flavour = shift;
+
+if ($flavour =~ /3[12]/) {
+	$SIZE_T=4;
+	$g="";
+} else {
+	$SIZE_T=8;
+	$g="g";
+}
+
+while (($output=shift) && ($output!~/\w[\w\-]*\.\w+$/)) {}
+open STDOUT,">$output";
+
+$rp="%r14";
+$sp="%r15";
+$code=<<___;
+.text
+
+___
+
+# void RC4(RC4_KEY *key,size_t len,const void *inp,void *out)
+{
+$acc="%r0";
+$cnt="%r1";
+$key="%r2";
+$len="%r3";
+$inp="%r4";
+$out="%r5";
+
+@XX=("%r6","%r7");
+@TX=("%r8","%r9");
+$YY="%r10";
+$TY="%r11";
+
+$code.=<<___;
+.globl	RC4
+.type	RC4,\@function
+.align	64
+RC4:
+	stm${g}	%r6,%r11,6*$SIZE_T($sp)
+___
+$code.=<<___ if ($flavour =~ /3[12]/);
+	llgfr	$len,$len
+___
+$code.=<<___;
+	llgc	$XX[0],0($key)
+	llgc	$YY,1($key)
+	la	$XX[0],1($XX[0])
+	nill	$XX[0],0xff
+	srlg	$cnt,$len,3
+	ltgr	$cnt,$cnt
+	llgc	$TX[0],2($XX[0],$key)
+	jz	.Lshort
+	j	.Loop8
+
+.align	64
+.Loop8:
+___
+for ($i=0;$i<8;$i++) {
+$code.=<<___;
+	la	$YY,0($YY,$TX[0])	# $i
+	nill	$YY,255
+	la	$XX[1],1($XX[0])
+	nill	$XX[1],255
+___
+$code.=<<___ if ($i==1);
+	llgc	$acc,2($TY,$key)
+___
+$code.=<<___ if ($i>1);
+	sllg	$acc,$acc,8
+	ic	$acc,2($TY,$key)
+___
+$code.=<<___;
+	llgc	$TY,2($YY,$key)
+	stc	$TX[0],2($YY,$key)
+	llgc	$TX[1],2($XX[1],$key)
+	stc	$TY,2($XX[0],$key)
+	cr	$XX[1],$YY
+	jne	.Lcmov$i
+	la	$TX[1],0($TX[0])
+.Lcmov$i:
+	la	$TY,0($TY,$TX[0])
+	nill	$TY,255
+___
+push(@TX,shift(@TX)); push(@XX,shift(@XX));     # "rotate" registers
+}
+
+$code.=<<___;
+	lg	$TX[1],0($inp)
+	sllg	$acc,$acc,8
+	la	$inp,8($inp)
+	ic	$acc,2($TY,$key)
+	xgr	$acc,$TX[1]
+	stg	$acc,0($out)
+	la	$out,8($out)
+	brctg	$cnt,.Loop8
+
+.Lshort:
+	lghi	$acc,7
+	ngr	$len,$acc
+	jz	.Lexit
+	j	.Loop1
+
+.align	16
+.Loop1:
+	la	$YY,0($YY,$TX[0])
+	nill	$YY,255
+	llgc	$TY,2($YY,$key)
+	stc	$TX[0],2($YY,$key)
+	stc	$TY,2($XX[0],$key)
+	ar	$TY,$TX[0]
+	ahi	$XX[0],1
+	nill	$TY,255
+	nill	$XX[0],255
+	llgc	$acc,0($inp)
+	la	$inp,1($inp)
+	llgc	$TY,2($TY,$key)
+	llgc	$TX[0],2($XX[0],$key)
+	xr	$acc,$TY
+	stc	$acc,0($out)
+	la	$out,1($out)
+	brct	$len,.Loop1
+
+.Lexit:
+	ahi	$XX[0],-1
+	stc	$XX[0],0($key)
+	stc	$YY,1($key)
+	lm${g}	%r6,%r11,6*$SIZE_T($sp)
+	br	$rp
+.size	RC4,.-RC4
+.string	"RC4 for s390x, CRYPTOGAMS by "
+
+___
+}
+
+# void RC4_set_key(RC4_KEY *key,unsigned int len,const void *inp)
+{
+$cnt="%r0";
+$idx="%r1";
+$key="%r2";
+$len="%r3";
+$inp="%r4";
+$acc="%r5";
+$dat="%r6";
+$ikey="%r7";
+$iinp="%r8";
+
+$code.=<<___;
+.globl	RC4_set_key
+.type	RC4_set_key,\@function
+.align	64
+RC4_set_key:
+	stm${g}	%r6,%r8,6*$SIZE_T($sp)
+	lhi	$cnt,256
+	la	$idx,0(%r0)
+	sth	$idx,0($key)
+.align	4
+.L1stloop:
+	stc	$idx,2($idx,$key)
+	la	$idx,1($idx)
+	brct	$cnt,.L1stloop
+
+	lghi	$ikey,-256
+	lr	$cnt,$len
+	la	$iinp,0(%r0)
+	la	$idx,0(%r0)
+.align	16
+.L2ndloop:
+	llgc	$acc,2+256($ikey,$key)
+	llgc	$dat,0($iinp,$inp)
+	la	$idx,0($idx,$acc)
+	la	$ikey,1($ikey)
+	la	$idx,0($idx,$dat)
+	nill	$idx,255
+	la	$iinp,1($iinp)
+	tml	$ikey,255
+	llgc	$dat,2($idx,$key)
+	stc	$dat,2+256-1($ikey,$key)
+	stc	$acc,2($idx,$key)
+	jz	.Ldone
+	brct	$cnt,.L2ndloop
+	lr	$cnt,$len
+	la	$iinp,0(%r0)
+	j	.L2ndloop
+.Ldone:
+	lm${g}	%r6,%r8,6*$SIZE_T($sp)
+	br	$rp
+.size	RC4_set_key,.-RC4_set_key
+
+___
+}
+
+# const char *RC4_options()
+$code.=<<___;
+.globl	RC4_options
+.type	RC4_options,\@function
+.align	16
+RC4_options:
+	larl	%r2,.Loptions
+	br	%r14
+.size	RC4_options,.-RC4_options
+.section	.rodata
+.Loptions:
+.align	8
+.string	"rc4(8x,char)"
+___
+
+print $code;
+close STDOUT;	# force flush
diff --git a/crypto/rc4/asm/rc4-x86_64.pl b/crypto/rc4/asm/rc4-x86_64.pl
new file mode 100755
index 000000000..aaed2b1e6
--- /dev/null
+++ b/crypto/rc4/asm/rc4-x86_64.pl
@@ -0,0 +1,687 @@
+#! /usr/bin/env perl
+# Copyright 2005-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+#
+# ====================================================================
+# Written by Andy Polyakov  for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+#
+# July 2004
+#
+# 2.22x RC4 tune-up:-) It should be noted though that my hand [as in
+# "hand-coded assembler"] doesn't stand for the whole improvement
+# coefficient. It turned out that eliminating RC4_CHAR from config
+# line results in ~40% improvement (yes, even for C implementation).
+# Presumably it has everything to do with AMD cache architecture and
+# RAW or whatever penalties. Once again! The module *requires* config
+# line *without* RC4_CHAR! As for coding "secret," I bet on partial
+# register arithmetics. For example instead of 'inc %r8; and $255,%r8'
+# I simply 'inc %r8b'. Even though optimization manual discourages
+# to operate on partial registers, it turned out to be the best bet.
+# At least for AMD... How IA32E would perform remains to be seen...
+
+# November 2004
+#
+# As was shown by Marc Bevand reordering of couple of load operations
+# results in even higher performance gain of 3.3x:-) At least on
+# Opteron... For reference, 1x in this case is RC4_CHAR C-code
+# compiled with gcc 3.3.2, which performs at ~54MBps per 1GHz clock.
+# Latter means that if you want to *estimate* what to expect from
+# *your* Opteron, then multiply 54 by 3.3 and clock frequency in GHz.
+
+# November 2004
+#
+# Intel P4 EM64T core was found to run the AMD64 code really slow...
+# The only way to achieve comparable performance on P4 was to keep
+# RC4_CHAR. Kind of ironic, huh? As it's apparently impossible to
+# compose blended code, which would perform even within 30% marginal
+# on either AMD and Intel platforms, I implement both cases. See
+# rc4_skey.c for further details...
+
+# April 2005
+#
+# P4 EM64T core appears to be "allergic" to 64-bit inc/dec. Replacing 
+# those with add/sub results in 50% performance improvement of folded
+# loop...
+
+# May 2005
+#
+# As was shown by Zou Nanhai loop unrolling can improve Intel EM64T
+# performance by >30% [unlike P4 32-bit case that is]. But this is
+# provided that loads are reordered even more aggressively! Both code
+# paths, AMD64 and EM64T, reorder loads in essentially same manner
+# as my IA-64 implementation. On Opteron this resulted in modest 5%
+# improvement [I had to test it], while final Intel P4 performance
+# achieves respectful 432MBps on 2.8GHz processor now. For reference.
+# If executed on Xeon, current RC4_CHAR code-path is 2.7x faster than
+# RC4_INT code-path. While if executed on Opteron, it's only 25%
+# slower than the RC4_INT one [meaning that if CPU µ-arch detection
+# is not implemented, then this final RC4_CHAR code-path should be
+# preferred, as it provides better *all-round* performance].
+
+# March 2007
+#
+# Intel Core2 was observed to perform poorly on both code paths:-( It
+# apparently suffers from some kind of partial register stall, which
+# occurs in 64-bit mode only [as virtually identical 32-bit loop was
+# observed to outperform 64-bit one by almost 50%]. Adding two movzb to
+# cloop1 boosts its performance by 80%! This loop appears to be optimal
+# fit for Core2 and therefore the code was modified to skip cloop8 on
+# this CPU.
+
+# May 2010
+#
+# Intel Westmere was observed to perform suboptimally. Adding yet
+# another movzb to cloop1 improved performance by almost 50%! Core2
+# performance is improved too, but nominally...
+
+# May 2011
+#
+# The only code path that was not modified is P4-specific one. Non-P4
+# Intel code path optimization is heavily based on submission by Maxim
+# Perminov, Maxim Locktyukhin and Jim Guilford of Intel. I've used
+# some of the ideas even in attempt to optmize the original RC4_INT
+# code path... Current performance in cycles per processed byte (less
+# is better) and improvement coefficients relative to previous
+# version of this module are:
+#
+# Opteron	5.3/+0%(*)
+# P4		6.5
+# Core2		6.2/+15%(**)
+# Westmere	4.2/+60%
+# Sandy Bridge	4.2/+120%
+# Atom		9.3/+80%
+# VIA Nano	6.4/+4%
+# Ivy Bridge	4.1/+30%
+# Bulldozer	4.5/+30%(*)
+#
+# (*)	But corresponding loop has less instructions, which should have
+#	positive effect on upcoming Bulldozer, which has one less ALU.
+#	For reference, Intel code runs at 6.8 cpb rate on Opteron.
+# (**)	Note that Core2 result is ~15% lower than corresponding result
+#	for 32-bit code, meaning that it's possible to improve it,
+#	but more than likely at the cost of the others (see rc4-586.pl
+#	to get the idea)...
+
+$flavour = shift;
+$output  = shift;
+if ($flavour =~ /\./) { $output = $flavour; undef $flavour; }
+
+$win64=0; $win64=1 if ($flavour =~ /[nm]asm|mingw64/ || $output =~ /\.asm$/);
+
+$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+( $xlate="${dir}x86_64-xlate.pl" and -f $xlate ) or
+( $xlate="${dir}../../perlasm/x86_64-xlate.pl" and -f $xlate) or
+die "can't locate x86_64-xlate.pl";
+
+open OUT,"| \"$^X\" \"$xlate\" $flavour \"$output\"";
+*STDOUT=*OUT;
+
+$dat="%rdi";	    # arg1
+$len="%rsi";	    # arg2
+$inp="%rdx";	    # arg3
+$out="%rcx";	    # arg4
+
+{
+$code=<<___;
+.text
+.extern	OPENSSL_ia32cap_P
+
+.globl	RC4
+.type	RC4,\@function,4
+.align	16
+RC4:	or	$len,$len
+	jne	.Lentry
+	ret
+.Lentry:
+	push	%rbx
+	push	%r12
+	push	%r13
+.Lprologue:
+	mov	$len,%r11
+	mov	$inp,%r12
+	mov	$out,%r13
+___
+my $len="%r11";		# reassign input arguments
+my $inp="%r12";
+my $out="%r13";
+
+my @XX=("%r10","%rsi");
+my @TX=("%rax","%rbx");
+my $YY="%rcx";
+my $TY="%rdx";
+
+$code.=<<___;
+	xor	$XX[0],$XX[0]
+	xor	$YY,$YY
+
+	lea	8($dat),$dat
+	mov	-8($dat),$XX[0]#b
+	mov	-4($dat),$YY#b
+	cmpl	\$-1,256($dat)
+	je	.LRC4_CHAR
+	mov	OPENSSL_ia32cap_P(%rip),%r8d
+	xor	$TX[1],$TX[1]
+	inc	$XX[0]#b
+	sub	$XX[0],$TX[1]
+	sub	$inp,$out
+	movl	($dat,$XX[0],4),$TX[0]#d
+	test	\$-16,$len
+	jz	.Lloop1
+	bt	\$30,%r8d	# Intel CPU?
+	jc	.Lintel
+	and	\$7,$TX[1]
+	lea	1($XX[0]),$XX[1]
+	jz	.Loop8
+	sub	$TX[1],$len
+.Loop8_warmup:
+	add	$TX[0]#b,$YY#b
+	movl	($dat,$YY,4),$TY#d
+	movl	$TX[0]#d,($dat,$YY,4)
+	movl	$TY#d,($dat,$XX[0],4)
+	add	$TY#b,$TX[0]#b
+	inc	$XX[0]#b
+	movl	($dat,$TX[0],4),$TY#d
+	movl	($dat,$XX[0],4),$TX[0]#d
+	xorb	($inp),$TY#b
+	movb	$TY#b,($out,$inp)
+	lea	1($inp),$inp
+	dec	$TX[1]
+	jnz	.Loop8_warmup
+
+	lea	1($XX[0]),$XX[1]
+	jmp	.Loop8
+.align	16
+.Loop8:
+___
+for ($i=0;$i<8;$i++) {
+$code.=<<___ if ($i==7);
+	add	\$8,$XX[1]#b
+___
+$code.=<<___;
+	add	$TX[0]#b,$YY#b
+	movl	($dat,$YY,4),$TY#d
+	movl	$TX[0]#d,($dat,$YY,4)
+	movl	`4*($i==7?-1:$i)`($dat,$XX[1],4),$TX[1]#d
+	ror	\$8,%r8				# ror is redundant when $i=0
+	movl	$TY#d,4*$i($dat,$XX[0],4)
+	add	$TX[0]#b,$TY#b
+	movb	($dat,$TY,4),%r8b
+___
+push(@TX,shift(@TX)); #push(@XX,shift(@XX));	# "rotate" registers
+}
+$code.=<<___;
+	add	\$8,$XX[0]#b
+	ror	\$8,%r8
+	sub	\$8,$len
+
+	xor	($inp),%r8
+	mov	%r8,($out,$inp)
+	lea	8($inp),$inp
+
+	test	\$-8,$len
+	jnz	.Loop8
+	cmp	\$0,$len
+	jne	.Lloop1
+	jmp	.Lexit
+
+.align	16
+.Lintel:
+	test	\$-32,$len
+	jz	.Lloop1
+	and	\$15,$TX[1]
+	jz	.Loop16_is_hot
+	sub	$TX[1],$len
+.Loop16_warmup:
+	add	$TX[0]#b,$YY#b
+	movl	($dat,$YY,4),$TY#d
+	movl	$TX[0]#d,($dat,$YY,4)
+	movl	$TY#d,($dat,$XX[0],4)
+	add	$TY#b,$TX[0]#b
+	inc	$XX[0]#b
+	movl	($dat,$TX[0],4),$TY#d
+	movl	($dat,$XX[0],4),$TX[0]#d
+	xorb	($inp),$TY#b
+	movb	$TY#b,($out,$inp)
+	lea	1($inp),$inp
+	dec	$TX[1]
+	jnz	.Loop16_warmup
+
+	mov	$YY,$TX[1]
+	xor	$YY,$YY
+	mov	$TX[1]#b,$YY#b
+
+.Loop16_is_hot:
+	lea	($dat,$XX[0],4),$XX[1]
+___
+sub RC4_loop {
+  my $i=shift;
+  my $j=$i<0?0:$i;
+  my $xmm="%xmm".($j&1);
+
+    $code.="	add	\$16,$XX[0]#b\n"		if ($i==15);
+    $code.="	movdqu	($inp),%xmm2\n"			if ($i==15);
+    $code.="	add	$TX[0]#b,$YY#b\n"		if ($i<=0);
+    $code.="	movl	($dat,$YY,4),$TY#d\n";
+    $code.="	pxor	%xmm0,%xmm2\n"			if ($i==0);
+    $code.="	psllq	\$8,%xmm1\n"			if ($i==0);
+    $code.="	pxor	$xmm,$xmm\n"			if ($i<=1);
+    $code.="	movl	$TX[0]#d,($dat,$YY,4)\n";
+    $code.="	add	$TY#b,$TX[0]#b\n";
+    $code.="	movl	`4*($j+1)`($XX[1]),$TX[1]#d\n"	if ($i<15);
+    $code.="	movz	$TX[0]#b,$TX[0]#d\n";
+    $code.="	movl	$TY#d,4*$j($XX[1])\n";
+    $code.="	pxor	%xmm1,%xmm2\n"			if ($i==0);
+    $code.="	lea	($dat,$XX[0],4),$XX[1]\n"	if ($i==15);
+    $code.="	add	$TX[1]#b,$YY#b\n"		if ($i<15);
+    $code.="	pinsrw	\$`($j>>1)&7`,($dat,$TX[0],4),$xmm\n";
+    $code.="	movdqu	%xmm2,($out,$inp)\n"		if ($i==0);
+    $code.="	lea	16($inp),$inp\n"		if ($i==0);
+    $code.="	movl	($XX[1]),$TX[1]#d\n"		if ($i==15);
+}
+	RC4_loop(-1);
+$code.=<<___;
+	jmp	.Loop16_enter
+.align	16
+.Loop16:
+___
+
+for ($i=0;$i<16;$i++) {
+    $code.=".Loop16_enter:\n"		if ($i==1);
+	RC4_loop($i);
+	push(@TX,shift(@TX)); 		# "rotate" registers
+}
+$code.=<<___;
+	mov	$YY,$TX[1]
+	xor	$YY,$YY			# keyword to partial register
+	sub	\$16,$len
+	mov	$TX[1]#b,$YY#b
+	test	\$-16,$len
+	jnz	.Loop16
+
+	psllq	\$8,%xmm1
+	pxor	%xmm0,%xmm2
+	pxor	%xmm1,%xmm2
+	movdqu	%xmm2,($out,$inp)
+	lea	16($inp),$inp
+
+	cmp	\$0,$len
+	jne	.Lloop1
+	jmp	.Lexit
+
+.align	16
+.Lloop1:
+	add	$TX[0]#b,$YY#b
+	movl	($dat,$YY,4),$TY#d
+	movl	$TX[0]#d,($dat,$YY,4)
+	movl	$TY#d,($dat,$XX[0],4)
+	add	$TY#b,$TX[0]#b
+	inc	$XX[0]#b
+	movl	($dat,$TX[0],4),$TY#d
+	movl	($dat,$XX[0],4),$TX[0]#d
+	xorb	($inp),$TY#b
+	movb	$TY#b,($out,$inp)
+	lea	1($inp),$inp
+	dec	$len
+	jnz	.Lloop1
+	jmp	.Lexit
+
+.align	16
+.LRC4_CHAR:
+	add	\$1,$XX[0]#b
+	movzb	($dat,$XX[0]),$TX[0]#d
+	test	\$-8,$len
+	jz	.Lcloop1
+	jmp	.Lcloop8
+.align	16
+.Lcloop8:
+	mov	($inp),%r8d
+	mov	4($inp),%r9d
+___
+# unroll 2x4-wise, because 64-bit rotates kill Intel P4...
+for ($i=0;$i<4;$i++) {
+$code.=<<___;
+	add	$TX[0]#b,$YY#b
+	lea	1($XX[0]),$XX[1]
+	movzb	($dat,$YY),$TY#d
+	movzb	$XX[1]#b,$XX[1]#d
+	movzb	($dat,$XX[1]),$TX[1]#d
+	movb	$TX[0]#b,($dat,$YY)
+	cmp	$XX[1],$YY
+	movb	$TY#b,($dat,$XX[0])
+	jne	.Lcmov$i			# Intel cmov is sloooow...
+	mov	$TX[0],$TX[1]
+.Lcmov$i:
+	add	$TX[0]#b,$TY#b
+	xor	($dat,$TY),%r8b
+	ror	\$8,%r8d
+___
+push(@TX,shift(@TX)); push(@XX,shift(@XX));	# "rotate" registers
+}
+for ($i=4;$i<8;$i++) {
+$code.=<<___;
+	add	$TX[0]#b,$YY#b
+	lea	1($XX[0]),$XX[1]
+	movzb	($dat,$YY),$TY#d
+	movzb	$XX[1]#b,$XX[1]#d
+	movzb	($dat,$XX[1]),$TX[1]#d
+	movb	$TX[0]#b,($dat,$YY)
+	cmp	$XX[1],$YY
+	movb	$TY#b,($dat,$XX[0])
+	jne	.Lcmov$i			# Intel cmov is sloooow...
+	mov	$TX[0],$TX[1]
+.Lcmov$i:
+	add	$TX[0]#b,$TY#b
+	xor	($dat,$TY),%r9b
+	ror	\$8,%r9d
+___
+push(@TX,shift(@TX)); push(@XX,shift(@XX));	# "rotate" registers
+}
+$code.=<<___;
+	lea	-8($len),$len
+	mov	%r8d,($out)
+	lea	8($inp),$inp
+	mov	%r9d,4($out)
+	lea	8($out),$out
+
+	test	\$-8,$len
+	jnz	.Lcloop8
+	cmp	\$0,$len
+	jne	.Lcloop1
+	jmp	.Lexit
+___
+$code.=<<___;
+.align	16
+.Lcloop1:
+	add	$TX[0]#b,$YY#b
+	movzb	$YY#b,$YY#d
+	movzb	($dat,$YY),$TY#d
+	movb	$TX[0]#b,($dat,$YY)
+	movb	$TY#b,($dat,$XX[0])
+	add	$TX[0]#b,$TY#b
+	add	\$1,$XX[0]#b
+	movzb	$TY#b,$TY#d
+	movzb	$XX[0]#b,$XX[0]#d
+	movzb	($dat,$TY),$TY#d
+	movzb	($dat,$XX[0]),$TX[0]#d
+	xorb	($inp),$TY#b
+	lea	1($inp),$inp
+	movb	$TY#b,($out)
+	lea	1($out),$out
+	sub	\$1,$len
+	jnz	.Lcloop1
+	jmp	.Lexit
+
+.align	16
+.Lexit:
+	sub	\$1,$XX[0]#b
+	movl	$XX[0]#d,-8($dat)
+	movl	$YY#d,-4($dat)
+
+	mov	(%rsp),%r13
+	mov	8(%rsp),%r12
+	mov	16(%rsp),%rbx
+	add	\$24,%rsp
+.Lepilogue:
+	ret
+.size	RC4,.-RC4
+___
+}
+
+$idx="%r8";
+$ido="%r9";
+
+$code.=<<___;
+.globl	RC4_set_key
+.type	RC4_set_key,\@function,3
+.align	16
+RC4_set_key:
+	lea	8($dat),$dat
+	lea	($inp,$len),$inp
+	neg	$len
+	mov	$len,%rcx
+	xor	%eax,%eax
+	xor	$ido,$ido
+	xor	%r10,%r10
+	xor	%r11,%r11
+
+	mov	OPENSSL_ia32cap_P(%rip),$idx#d
+	bt	\$20,$idx#d	# RC4_CHAR?
+	jc	.Lc1stloop
+	jmp	.Lw1stloop
+
+.align	16
+.Lw1stloop:
+	mov	%eax,($dat,%rax,4)
+	add	\$1,%al
+	jnc	.Lw1stloop
+
+	xor	$ido,$ido
+	xor	$idx,$idx
+.align	16
+.Lw2ndloop:
+	mov	($dat,$ido,4),%r10d
+	add	($inp,$len,1),$idx#b
+	add	%r10b,$idx#b
+	add	\$1,$len
+	mov	($dat,$idx,4),%r11d
+	cmovz	%rcx,$len
+	mov	%r10d,($dat,$idx,4)
+	mov	%r11d,($dat,$ido,4)
+	add	\$1,$ido#b
+	jnc	.Lw2ndloop
+	jmp	.Lexit_key
+
+.align	16
+.Lc1stloop:
+	mov	%al,($dat,%rax)
+	add	\$1,%al
+	jnc	.Lc1stloop
+
+	xor	$ido,$ido
+	xor	$idx,$idx
+.align	16
+.Lc2ndloop:
+	mov	($dat,$ido),%r10b
+	add	($inp,$len),$idx#b
+	add	%r10b,$idx#b
+	add	\$1,$len
+	mov	($dat,$idx),%r11b
+	jnz	.Lcnowrap
+	mov	%rcx,$len
+.Lcnowrap:
+	mov	%r10b,($dat,$idx)
+	mov	%r11b,($dat,$ido)
+	add	\$1,$ido#b
+	jnc	.Lc2ndloop
+	movl	\$-1,256($dat)
+
+.align	16
+.Lexit_key:
+	xor	%eax,%eax
+	mov	%eax,-8($dat)
+	mov	%eax,-4($dat)
+	ret
+.size	RC4_set_key,.-RC4_set_key
+
+.globl	RC4_options
+.type	RC4_options,\@abi-omnipotent
+.align	16
+RC4_options:
+	lea	.Lopts(%rip),%rax
+	mov	OPENSSL_ia32cap_P(%rip),%edx
+	bt	\$20,%edx
+	jc	.L8xchar
+	bt	\$30,%edx
+	jnc	.Ldone
+	add	\$25,%rax
+	ret
+.L8xchar:
+	add	\$12,%rax
+.Ldone:
+	ret
+.align	64
+.Lopts:
+.asciz	"rc4(8x,int)"
+.asciz	"rc4(8x,char)"
+.asciz	"rc4(16x,int)"
+.asciz	"RC4 for x86_64, CRYPTOGAMS by "
+.align	64
+.size	RC4_options,.-RC4_options
+___
+
+# EXCEPTION_DISPOSITION handler (EXCEPTION_RECORD *rec,ULONG64 frame,
+#		CONTEXT *context,DISPATCHER_CONTEXT *disp)
+if ($win64) {
+$rec="%rcx";
+$frame="%rdx";
+$context="%r8";
+$disp="%r9";
+
+$code.=<<___;
+.extern	__imp_RtlVirtualUnwind
+.type	stream_se_handler,\@abi-omnipotent
+.align	16
+stream_se_handler:
+	push	%rsi
+	push	%rdi
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+	pushfq
+	sub	\$64,%rsp
+
+	mov	120($context),%rax	# pull context->Rax
+	mov	248($context),%rbx	# pull context->Rip
+
+	lea	.Lprologue(%rip),%r10
+	cmp	%r10,%rbx		# context->RipRsp
+
+	lea	.Lepilogue(%rip),%r10
+	cmp	%r10,%rbx		# context->Rip>=epilogue label
+	jae	.Lin_prologue
+
+	lea	24(%rax),%rax
+
+	mov	-8(%rax),%rbx
+	mov	-16(%rax),%r12
+	mov	-24(%rax),%r13
+	mov	%rbx,144($context)	# restore context->Rbx
+	mov	%r12,216($context)	# restore context->R12
+	mov	%r13,224($context)	# restore context->R13
+
+.Lin_prologue:
+	mov	8(%rax),%rdi
+	mov	16(%rax),%rsi
+	mov	%rax,152($context)	# restore context->Rsp
+	mov	%rsi,168($context)	# restore context->Rsi
+	mov	%rdi,176($context)	# restore context->Rdi
+
+	jmp	.Lcommon_seh_exit
+.size	stream_se_handler,.-stream_se_handler
+
+.type	key_se_handler,\@abi-omnipotent
+.align	16
+key_se_handler:
+	push	%rsi
+	push	%rdi
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+	pushfq
+	sub	\$64,%rsp
+
+	mov	152($context),%rax	# pull context->Rsp
+	mov	8(%rax),%rdi
+	mov	16(%rax),%rsi
+	mov	%rsi,168($context)	# restore context->Rsi
+	mov	%rdi,176($context)	# restore context->Rdi
+
+.Lcommon_seh_exit:
+
+	mov	40($disp),%rdi		# disp->ContextRecord
+	mov	$context,%rsi		# context
+	mov	\$154,%ecx		# sizeof(CONTEXT)
+	.long	0xa548f3fc		# cld; rep movsq
+
+	mov	$disp,%rsi
+	xor	%rcx,%rcx		# arg1, UNW_FLAG_NHANDLER
+	mov	8(%rsi),%rdx		# arg2, disp->ImageBase
+	mov	0(%rsi),%r8		# arg3, disp->ControlPc
+	mov	16(%rsi),%r9		# arg4, disp->FunctionEntry
+	mov	40(%rsi),%r10		# disp->ContextRecord
+	lea	56(%rsi),%r11		# &disp->HandlerData
+	lea	24(%rsi),%r12		# &disp->EstablisherFrame
+	mov	%r10,32(%rsp)		# arg5
+	mov	%r11,40(%rsp)		# arg6
+	mov	%r12,48(%rsp)		# arg7
+	mov	%rcx,56(%rsp)		# arg8, (NULL)
+	call	*__imp_RtlVirtualUnwind(%rip)
+
+	mov	\$1,%eax		# ExceptionContinueSearch
+	add	\$64,%rsp
+	popfq
+	pop	%r15
+	pop	%r14
+	pop	%r13
+	pop	%r12
+	pop	%rbp
+	pop	%rbx
+	pop	%rdi
+	pop	%rsi
+	ret
+.size	key_se_handler,.-key_se_handler
+
+.section	.pdata
+.align	4
+	.rva	.LSEH_begin_RC4
+	.rva	.LSEH_end_RC4
+	.rva	.LSEH_info_RC4
+
+	.rva	.LSEH_begin_RC4_set_key
+	.rva	.LSEH_end_RC4_set_key
+	.rva	.LSEH_info_RC4_set_key
+
+.section	.xdata
+.align	8
+.LSEH_info_RC4:
+	.byte	9,0,0,0
+	.rva	stream_se_handler
+.LSEH_info_RC4_set_key:
+	.byte	9,0,0,0
+	.rva	key_se_handler
+___
+}
+
+sub reg_part {
+my ($reg,$conv)=@_;
+    if ($reg =~ /%r[0-9]+/)	{ $reg .= $conv; }
+    elsif ($conv eq "b")	{ $reg =~ s/%[er]([^x]+)x?/%$1l/;	}
+    elsif ($conv eq "w")	{ $reg =~ s/%[er](.+)/%$1/;		}
+    elsif ($conv eq "d")	{ $reg =~ s/%[er](.+)/%e$1/;		}
+    return $reg;
+}
+
+$code =~ s/(%[a-z0-9]+)#([bwd])/reg_part($1,$2)/gem;
+$code =~ s/\`([^\`]*)\`/eval $1/gem;
+
+print $code;
+
+close STDOUT;
diff --git a/crypto/rc4/build.info b/crypto/rc4/build.info
new file mode 100644
index 000000000..6c488890f
--- /dev/null
+++ b/crypto/rc4/build.info
@@ -0,0 +1,33 @@
+LIBS=../../libcrypto
+SOURCE[../../libcrypto]=\
+        {- $target{rc4_asm_src} -}
+
+GENERATE[rc4-586.s]=asm/rc4-586.pl $(PERLASM_SCHEME) $(CFLAGS) $(LIB_CFLAGS) $(PROCESSOR)
+DEPEND[rc4-586.s]=../perlasm/x86asm.pl
+
+GENERATE[rc4-x86_64.s]=asm/rc4-x86_64.pl $(PERLASM_SCHEME)
+GENERATE[rc4-md5-x86_64.s]=asm/rc4-md5-x86_64.pl $(PERLASM_SCHEME)
+
+GENERATE[rc4-parisc.s]=asm/rc4-parisc.pl $(PERLASM_SCHEME)
+
+BEGINRAW[makefile(windows)]
+{- $builddir -}\rc4-ia64.asm: {- $sourcedir -}\asm\rc4-ia64.pl
+	$(PERL) {- $sourcedir -}\asm\rc4-ia64.pl $@.S
+	$(CC) -DSZ=4 -EP $@.S > $@.i && move /Y $@.i $@
+	del /Q $@.S
+ENDRAW[makefile(windows)]
+
+BEGINRAW[Makefile]
+{- $builddir -}/rc4-ia64.s: {- $sourcedir -}/asm/rc4-ia64.pl
+	@(trap "rm $@.*" INT 0; \
+	  $(PERL) $< $(CFLAGS) $(LIB_CFLAGS) $@.S; \
+	  case `awk '/^#define RC4_INT/{print$$NF}' $(BLDDIR)/include/openssl/opensslconf.h` in \
+	  int)	set -x; $(CC) $(CFLAGS) $(LIB_CFLAGS) -DSZ=4 -E $@.S > $@.i && mv -f $@.i $@;; \
+	  char)	set -x; $(CC) $(CFLAGS) $(LIB_CFLAGS) -DSZ=1 -E $@.S > $@.i && mv -f $@.i $@;; \
+	  *)	exit 1 ;; \
+	  esac )
+
+# GNU make "catch all"
+{- $builddir -}/rc4-%.s:	{- $sourcedir -}/asm/rc4-%.pl
+	CC="$(CC)" $(PERL) $< $(PERLASM_SCHEME) $@
+ENDRAW[Makefile]
diff --git a/crypto/rc4/rc4_enc.c b/crypto/rc4/rc4_enc.c
new file mode 100644
index 000000000..be11bade7
--- /dev/null
+++ b/crypto/rc4/rc4_enc.c
@@ -0,0 +1,86 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "rc4_locl.h"
+
+/*-
+ * RC4 as implemented from a posting from
+ * Newsgroups: sci.crypt
+ * From: sterndark@netcom.com (David Sterndark)
+ * Subject: RC4 Algorithm revealed.
+ * Message-ID: 
+ * Date: Wed, 14 Sep 1994 06:35:31 GMT
+ */
+
+void RC4(RC4_KEY *key, size_t len, const unsigned char *indata,
+         unsigned char *outdata)
+{
+    register RC4_INT *d;
+    register RC4_INT x, y, tx, ty;
+    size_t i;
+
+    x = key->x;
+    y = key->y;
+    d = key->data;
+
+#define LOOP(in,out) \
+                x=((x+1)&0xff); \
+                tx=d[x]; \
+                y=(tx+y)&0xff; \
+                d[x]=ty=d[y]; \
+                d[y]=tx; \
+                (out) = d[(tx+ty)&0xff]^ (in);
+
+    i = len >> 3;
+    if (i) {
+        for (;;) {
+            LOOP(indata[0], outdata[0]);
+            LOOP(indata[1], outdata[1]);
+            LOOP(indata[2], outdata[2]);
+            LOOP(indata[3], outdata[3]);
+            LOOP(indata[4], outdata[4]);
+            LOOP(indata[5], outdata[5]);
+            LOOP(indata[6], outdata[6]);
+            LOOP(indata[7], outdata[7]);
+            indata += 8;
+            outdata += 8;
+            if (--i == 0)
+                break;
+        }
+    }
+    i = len & 0x07;
+    if (i) {
+        for (;;) {
+            LOOP(indata[0], outdata[0]);
+            if (--i == 0)
+                break;
+            LOOP(indata[1], outdata[1]);
+            if (--i == 0)
+                break;
+            LOOP(indata[2], outdata[2]);
+            if (--i == 0)
+                break;
+            LOOP(indata[3], outdata[3]);
+            if (--i == 0)
+                break;
+            LOOP(indata[4], outdata[4]);
+            if (--i == 0)
+                break;
+            LOOP(indata[5], outdata[5]);
+            if (--i == 0)
+                break;
+            LOOP(indata[6], outdata[6]);
+            if (--i == 0)
+                break;
+        }
+    }
+    key->x = x;
+    key->y = y;
+}
diff --git a/crypto/rc4/rc4_locl.h b/crypto/rc4/rc4_locl.h
new file mode 100644
index 000000000..4380addbc
--- /dev/null
+++ b/crypto/rc4/rc4_locl.h
@@ -0,0 +1,16 @@
+/*
+ * Copyright 1998-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#ifndef HEADER_RC4_LOCL_H
+# define HEADER_RC4_LOCL_H
+
+# include 
+# include "internal/cryptlib.h"
+
+#endif
diff --git a/crypto/rc4/rc4_skey.c b/crypto/rc4/rc4_skey.c
new file mode 100644
index 000000000..16f81a4d3
--- /dev/null
+++ b/crypto/rc4/rc4_skey.c
@@ -0,0 +1,58 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "rc4_locl.h"
+#include 
+
+const char *RC4_options(void)
+{
+    if (sizeof(RC4_INT) == 1)
+        return ("rc4(char)");
+    else
+        return ("rc4(int)");
+}
+
+/*-
+ * RC4 as implemented from a posting from
+ * Newsgroups: sci.crypt
+ * From: sterndark@netcom.com (David Sterndark)
+ * Subject: RC4 Algorithm revealed.
+ * Message-ID: 
+ * Date: Wed, 14 Sep 1994 06:35:31 GMT
+ */
+
+void RC4_set_key(RC4_KEY *key, int len, const unsigned char *data)
+{
+    register RC4_INT tmp;
+    register int id1, id2;
+    register RC4_INT *d;
+    unsigned int i;
+
+    d = &(key->data[0]);
+    key->x = 0;
+    key->y = 0;
+    id1 = id2 = 0;
+
+#define SK_LOOP(d,n) { \
+                tmp=d[(n)]; \
+                id2 = (data[id1] + tmp + id2) & 0xff; \
+                if (++id1 == len) id1=0; \
+                d[(n)]=d[id2]; \
+                d[id2]=tmp; }
+
+    for (i = 0; i < 256; i++)
+        d[i] = i;
+    for (i = 0; i < 256; i += 4) {
+        SK_LOOP(d, i + 0);
+        SK_LOOP(d, i + 1);
+        SK_LOOP(d, i + 2);
+        SK_LOOP(d, i + 3);
+    }
+}
diff --git a/crypto/rc5/asm/rc5-586.pl b/crypto/rc5/asm/rc5-586.pl
new file mode 100644
index 000000000..e3e1c6424
--- /dev/null
+++ b/crypto/rc5/asm/rc5-586.pl
@@ -0,0 +1,122 @@
+#! /usr/bin/env perl
+# Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+
+$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+push(@INC,"${dir}","${dir}../../perlasm");
+require "x86asm.pl";
+require "cbc.pl";
+
+$output = pop;
+open STDOUT,">$output";
+
+&asm_init($ARGV[0],"rc5-586.pl");
+
+$RC5_MAX_ROUNDS=16;
+$RC5_32_OFF=($RC5_MAX_ROUNDS+2)*4;
+$A="edi";
+$B="esi";
+$S="ebp";
+$tmp1="eax";
+$r="ebx";
+$tmpc="ecx";
+$tmp4="edx";
+
+&RC5_32_encrypt("RC5_32_encrypt",1);
+&RC5_32_encrypt("RC5_32_decrypt",0);
+&cbc("RC5_32_cbc_encrypt","RC5_32_encrypt","RC5_32_decrypt",0,4,5,3,-1,-1);
+&asm_finish();
+
+close STDOUT;
+
+sub RC5_32_encrypt
+	{
+	local($name,$enc)=@_;
+
+	&function_begin_B($name,"");
+
+	&comment("");
+
+	&push("ebp");
+	 &push("esi");
+	&push("edi");
+	 &mov($tmp4,&wparam(0));
+	&mov($S,&wparam(1));
+
+	&comment("Load the 2 words");
+	 &mov($A,&DWP(0,$tmp4,"",0));
+	&mov($B,&DWP(4,$tmp4,"",0));
+
+	&push($r);
+	 &mov($r,	&DWP(0,$S,"",0));
+
+	# encrypting part
+
+	if ($enc)
+		{
+		 &add($A,	&DWP(4+0,$S,"",0));
+		&add($B,	&DWP(4+4,$S,"",0));
+
+		for ($i=0; $i<$RC5_MAX_ROUNDS; $i++)
+			{
+			 &xor($A,	$B);
+			&mov($tmp1,	&DWP(12+$i*8,$S,"",0));
+			 &mov($tmpc,	$B);
+			&rotl($A,	&LB("ecx"));
+			&add($A,	$tmp1);
+
+			 &xor($B,	$A);
+			&mov($tmp1,	&DWP(16+$i*8,$S,"",0));
+			 &mov($tmpc,	$A);
+			&rotl($B,	&LB("ecx"));
+			&add($B,	$tmp1);
+			if (($i == 7) || ($i == 11))
+				{
+			 &cmp($r,	$i+1);
+			&je(&label("rc5_exit"));
+				}
+			}
+		}
+	else
+		{
+		 &cmp($r,	12);
+		&je(&label("rc5_dec_12"));
+		 &cmp($r,	8);
+		&je(&label("rc5_dec_8"));
+		for ($i=$RC5_MAX_ROUNDS; $i > 0; $i--)
+			{
+			&set_label("rc5_dec_$i") if ($i == 12) || ($i == 8);
+			 &mov($tmp1,	&DWP($i*8+8,$S,"",0));
+			&sub($B,	$tmp1);
+			 &mov($tmpc,	$A);
+			&rotr($B,	&LB("ecx"));
+			&xor($B,	$A);
+
+			 &mov($tmp1,	&DWP($i*8+4,$S,"",0));
+			&sub($A,	$tmp1);
+			 &mov($tmpc,	$B);
+			&rotr($A,	&LB("ecx"));
+			&xor($A,	$B);
+			}
+		 &sub($B,	&DWP(4+4,$S,"",0));
+		&sub($A,	&DWP(4+0,$S,"",0));
+		}
+
+	&set_label("rc5_exit");
+	 &mov(&DWP(0,$tmp4,"",0),$A);
+	&mov(&DWP(4,$tmp4,"",0),$B);
+
+	 &pop("ebx");
+	&pop("edi");
+	 &pop("esi");
+	&pop("ebp");
+	 &ret();
+	&function_end_B($name);
+	}
+
+
diff --git a/crypto/rc5/build.info b/crypto/rc5/build.info
new file mode 100644
index 000000000..baf8a0eff
--- /dev/null
+++ b/crypto/rc5/build.info
@@ -0,0 +1,6 @@
+LIBS=../../libcrypto
+SOURCE[../../libcrypto]=\
+        rc5_skey.c rc5_ecb.c {- $target{rc5_asm_src} -} rc5cfb64.c rc5ofb64.c
+
+GENERATE[rc5-586.s]=asm/rc5-586.pl $(PERLASM_SCHEME) $(CFLAGS) $(LIB_CFLAGS)
+DEPEND[rc5-586.s]=../perlasm/x86asm.pl ../perlasm/cbc.pl
diff --git a/crypto/rc5/rc5_ecb.c b/crypto/rc5/rc5_ecb.c
new file mode 100644
index 000000000..c32f38e47
--- /dev/null
+++ b/crypto/rc5/rc5_ecb.c
@@ -0,0 +1,32 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "rc5_locl.h"
+#include 
+
+void RC5_32_ecb_encrypt(const unsigned char *in, unsigned char *out,
+                        RC5_32_KEY *ks, int encrypt)
+{
+    unsigned long l, d[2];
+
+    c2l(in, l);
+    d[0] = l;
+    c2l(in, l);
+    d[1] = l;
+    if (encrypt)
+        RC5_32_encrypt(d, ks);
+    else
+        RC5_32_decrypt(d, ks);
+    l = d[0];
+    l2c(l, out);
+    l = d[1];
+    l2c(l, out);
+    l = d[0] = d[1] = 0;
+}
diff --git a/crypto/rc5/rc5_enc.c b/crypto/rc5/rc5_enc.c
new file mode 100644
index 000000000..58631dee2
--- /dev/null
+++ b/crypto/rc5/rc5_enc.c
@@ -0,0 +1,160 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include "rc5_locl.h"
+
+void RC5_32_cbc_encrypt(const unsigned char *in, unsigned char *out,
+                        long length, RC5_32_KEY *ks, unsigned char *iv,
+                        int encrypt)
+{
+    register unsigned long tin0, tin1;
+    register unsigned long tout0, tout1, xor0, xor1;
+    register long l = length;
+    unsigned long tin[2];
+
+    if (encrypt) {
+        c2l(iv, tout0);
+        c2l(iv, tout1);
+        iv -= 8;
+        for (l -= 8; l >= 0; l -= 8) {
+            c2l(in, tin0);
+            c2l(in, tin1);
+            tin0 ^= tout0;
+            tin1 ^= tout1;
+            tin[0] = tin0;
+            tin[1] = tin1;
+            RC5_32_encrypt(tin, ks);
+            tout0 = tin[0];
+            l2c(tout0, out);
+            tout1 = tin[1];
+            l2c(tout1, out);
+        }
+        if (l != -8) {
+            c2ln(in, tin0, tin1, l + 8);
+            tin0 ^= tout0;
+            tin1 ^= tout1;
+            tin[0] = tin0;
+            tin[1] = tin1;
+            RC5_32_encrypt(tin, ks);
+            tout0 = tin[0];
+            l2c(tout0, out);
+            tout1 = tin[1];
+            l2c(tout1, out);
+        }
+        l2c(tout0, iv);
+        l2c(tout1, iv);
+    } else {
+        c2l(iv, xor0);
+        c2l(iv, xor1);
+        iv -= 8;
+        for (l -= 8; l >= 0; l -= 8) {
+            c2l(in, tin0);
+            tin[0] = tin0;
+            c2l(in, tin1);
+            tin[1] = tin1;
+            RC5_32_decrypt(tin, ks);
+            tout0 = tin[0] ^ xor0;
+            tout1 = tin[1] ^ xor1;
+            l2c(tout0, out);
+            l2c(tout1, out);
+            xor0 = tin0;
+            xor1 = tin1;
+        }
+        if (l != -8) {
+            c2l(in, tin0);
+            tin[0] = tin0;
+            c2l(in, tin1);
+            tin[1] = tin1;
+            RC5_32_decrypt(tin, ks);
+            tout0 = tin[0] ^ xor0;
+            tout1 = tin[1] ^ xor1;
+            l2cn(tout0, tout1, out, l + 8);
+            xor0 = tin0;
+            xor1 = tin1;
+        }
+        l2c(xor0, iv);
+        l2c(xor1, iv);
+    }
+    tin0 = tin1 = tout0 = tout1 = xor0 = xor1 = 0;
+    tin[0] = tin[1] = 0;
+}
+
+void RC5_32_encrypt(unsigned long *d, RC5_32_KEY *key)
+{
+    RC5_32_INT a, b, *s;
+
+    s = key->data;
+
+    a = d[0] + s[0];
+    b = d[1] + s[1];
+    E_RC5_32(a, b, s, 2);
+    E_RC5_32(a, b, s, 4);
+    E_RC5_32(a, b, s, 6);
+    E_RC5_32(a, b, s, 8);
+    E_RC5_32(a, b, s, 10);
+    E_RC5_32(a, b, s, 12);
+    E_RC5_32(a, b, s, 14);
+    E_RC5_32(a, b, s, 16);
+    if (key->rounds == 12) {
+        E_RC5_32(a, b, s, 18);
+        E_RC5_32(a, b, s, 20);
+        E_RC5_32(a, b, s, 22);
+        E_RC5_32(a, b, s, 24);
+    } else if (key->rounds == 16) {
+        /* Do a full expansion to avoid a jump */
+        E_RC5_32(a, b, s, 18);
+        E_RC5_32(a, b, s, 20);
+        E_RC5_32(a, b, s, 22);
+        E_RC5_32(a, b, s, 24);
+        E_RC5_32(a, b, s, 26);
+        E_RC5_32(a, b, s, 28);
+        E_RC5_32(a, b, s, 30);
+        E_RC5_32(a, b, s, 32);
+    }
+    d[0] = a;
+    d[1] = b;
+}
+
+void RC5_32_decrypt(unsigned long *d, RC5_32_KEY *key)
+{
+    RC5_32_INT a, b, *s;
+
+    s = key->data;
+
+    a = d[0];
+    b = d[1];
+    if (key->rounds == 16) {
+        D_RC5_32(a, b, s, 32);
+        D_RC5_32(a, b, s, 30);
+        D_RC5_32(a, b, s, 28);
+        D_RC5_32(a, b, s, 26);
+        /* Do a full expansion to avoid a jump */
+        D_RC5_32(a, b, s, 24);
+        D_RC5_32(a, b, s, 22);
+        D_RC5_32(a, b, s, 20);
+        D_RC5_32(a, b, s, 18);
+    } else if (key->rounds == 12) {
+        D_RC5_32(a, b, s, 24);
+        D_RC5_32(a, b, s, 22);
+        D_RC5_32(a, b, s, 20);
+        D_RC5_32(a, b, s, 18);
+    }
+    D_RC5_32(a, b, s, 16);
+    D_RC5_32(a, b, s, 14);
+    D_RC5_32(a, b, s, 12);
+    D_RC5_32(a, b, s, 10);
+    D_RC5_32(a, b, s, 8);
+    D_RC5_32(a, b, s, 6);
+    D_RC5_32(a, b, s, 4);
+    D_RC5_32(a, b, s, 2);
+    d[0] = a - s[0];
+    d[1] = b - s[1];
+}
diff --git a/crypto/rc5/rc5_locl.h b/crypto/rc5/rc5_locl.h
new file mode 100644
index 000000000..33a709b45
--- /dev/null
+++ b/crypto/rc5/rc5_locl.h
@@ -0,0 +1,158 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+
+#undef c2l
+#define c2l(c,l)        (l =((unsigned long)(*((c)++)))    , \
+                         l|=((unsigned long)(*((c)++)))<< 8L, \
+                         l|=((unsigned long)(*((c)++)))<<16L, \
+                         l|=((unsigned long)(*((c)++)))<<24L)
+
+/* NOTE - c is not incremented as per c2l */
+#undef c2ln
+#define c2ln(c,l1,l2,n) { \
+                        c+=n; \
+                        l1=l2=0; \
+                        switch (n) { \
+                        case 8: l2 =((unsigned long)(*(--(c))))<<24L; \
+                        case 7: l2|=((unsigned long)(*(--(c))))<<16L; \
+                        case 6: l2|=((unsigned long)(*(--(c))))<< 8L; \
+                        case 5: l2|=((unsigned long)(*(--(c))));     \
+                        case 4: l1 =((unsigned long)(*(--(c))))<<24L; \
+                        case 3: l1|=((unsigned long)(*(--(c))))<<16L; \
+                        case 2: l1|=((unsigned long)(*(--(c))))<< 8L; \
+                        case 1: l1|=((unsigned long)(*(--(c))));     \
+                                } \
+                        }
+
+#undef l2c
+#define l2c(l,c)        (*((c)++)=(unsigned char)(((l)     )&0xff), \
+                         *((c)++)=(unsigned char)(((l)>> 8L)&0xff), \
+                         *((c)++)=(unsigned char)(((l)>>16L)&0xff), \
+                         *((c)++)=(unsigned char)(((l)>>24L)&0xff))
+
+/* NOTE - c is not incremented as per l2c */
+#undef l2cn
+#define l2cn(l1,l2,c,n) { \
+                        c+=n; \
+                        switch (n) { \
+                        case 8: *(--(c))=(unsigned char)(((l2)>>24L)&0xff); \
+                        case 7: *(--(c))=(unsigned char)(((l2)>>16L)&0xff); \
+                        case 6: *(--(c))=(unsigned char)(((l2)>> 8L)&0xff); \
+                        case 5: *(--(c))=(unsigned char)(((l2)     )&0xff); \
+                        case 4: *(--(c))=(unsigned char)(((l1)>>24L)&0xff); \
+                        case 3: *(--(c))=(unsigned char)(((l1)>>16L)&0xff); \
+                        case 2: *(--(c))=(unsigned char)(((l1)>> 8L)&0xff); \
+                        case 1: *(--(c))=(unsigned char)(((l1)     )&0xff); \
+                                } \
+                        }
+
+/* NOTE - c is not incremented as per n2l */
+#define n2ln(c,l1,l2,n) { \
+                        c+=n; \
+                        l1=l2=0; \
+                        switch (n) { \
+                        case 8: l2 =((unsigned long)(*(--(c))))    ; \
+                        case 7: l2|=((unsigned long)(*(--(c))))<< 8; \
+                        case 6: l2|=((unsigned long)(*(--(c))))<<16; \
+                        case 5: l2|=((unsigned long)(*(--(c))))<<24; \
+                        case 4: l1 =((unsigned long)(*(--(c))))    ; \
+                        case 3: l1|=((unsigned long)(*(--(c))))<< 8; \
+                        case 2: l1|=((unsigned long)(*(--(c))))<<16; \
+                        case 1: l1|=((unsigned long)(*(--(c))))<<24; \
+                                } \
+                        }
+
+/* NOTE - c is not incremented as per l2n */
+#define l2nn(l1,l2,c,n) { \
+                        c+=n; \
+                        switch (n) { \
+                        case 8: *(--(c))=(unsigned char)(((l2)    )&0xff); \
+                        case 7: *(--(c))=(unsigned char)(((l2)>> 8)&0xff); \
+                        case 6: *(--(c))=(unsigned char)(((l2)>>16)&0xff); \
+                        case 5: *(--(c))=(unsigned char)(((l2)>>24)&0xff); \
+                        case 4: *(--(c))=(unsigned char)(((l1)    )&0xff); \
+                        case 3: *(--(c))=(unsigned char)(((l1)>> 8)&0xff); \
+                        case 2: *(--(c))=(unsigned char)(((l1)>>16)&0xff); \
+                        case 1: *(--(c))=(unsigned char)(((l1)>>24)&0xff); \
+                                } \
+                        }
+
+#undef n2l
+#define n2l(c,l)        (l =((unsigned long)(*((c)++)))<<24L, \
+                         l|=((unsigned long)(*((c)++)))<<16L, \
+                         l|=((unsigned long)(*((c)++)))<< 8L, \
+                         l|=((unsigned long)(*((c)++))))
+
+#undef l2n
+#define l2n(l,c)        (*((c)++)=(unsigned char)(((l)>>24L)&0xff), \
+                         *((c)++)=(unsigned char)(((l)>>16L)&0xff), \
+                         *((c)++)=(unsigned char)(((l)>> 8L)&0xff), \
+                         *((c)++)=(unsigned char)(((l)     )&0xff))
+
+#if (defined(OPENSSL_SYS_WIN32) && defined(_MSC_VER))
+# define ROTATE_l32(a,n)     _lrotl(a,n)
+# define ROTATE_r32(a,n)     _lrotr(a,n)
+#elif defined(__ICC)
+# define ROTATE_l32(a,n)     _rotl(a,n)
+# define ROTATE_r32(a,n)     _rotr(a,n)
+#elif defined(__GNUC__) && __GNUC__>=2 && !defined(__STRICT_ANSI__) && !defined(OPENSSL_NO_ASM) && !defined(OPENSSL_NO_INLINE_ASM) && !defined(PEDANTIC)
+# if defined(__i386) || defined(__i386__) || defined(__x86_64) || defined(__x86_64__)
+#  define ROTATE_l32(a,n)       ({ register unsigned int ret;   \
+                                        asm ("roll %%cl,%0"     \
+                                                : "=r"(ret)     \
+                                                : "c"(n),"0"((unsigned int)(a)) \
+                                                : "cc");        \
+                                        ret;                    \
+                                })
+#  define ROTATE_r32(a,n)       ({ register unsigned int ret;   \
+                                        asm ("rorl %%cl,%0"     \
+                                                : "=r"(ret)     \
+                                                : "c"(n),"0"((unsigned int)(a)) \
+                                                : "cc");        \
+                                        ret;                    \
+                                })
+# endif
+#endif
+#ifndef ROTATE_l32
+# define ROTATE_l32(a,n)     (((a)<<(n&0x1f))|(((a)&0xffffffff)>>((32-n)&0x1f)))
+#endif
+#ifndef ROTATE_r32
+# define ROTATE_r32(a,n)     (((a)<<((32-n)&0x1f))|(((a)&0xffffffff)>>(n&0x1f)))
+#endif
+
+#define RC5_32_MASK     0xffffffffL
+
+#define RC5_16_P        0xB7E1
+#define RC5_16_Q        0x9E37
+#define RC5_32_P        0xB7E15163L
+#define RC5_32_Q        0x9E3779B9L
+#define RC5_64_P        0xB7E151628AED2A6BLL
+#define RC5_64_Q        0x9E3779B97F4A7C15LL
+
+#define E_RC5_32(a,b,s,n) \
+        a^=b; \
+        a=ROTATE_l32(a,b); \
+        a+=s[n]; \
+        a&=RC5_32_MASK; \
+        b^=a; \
+        b=ROTATE_l32(b,a); \
+        b+=s[n+1]; \
+        b&=RC5_32_MASK;
+
+#define D_RC5_32(a,b,s,n) \
+        b-=s[n+1]; \
+        b&=RC5_32_MASK; \
+        b=ROTATE_r32(b,a); \
+        b^=a; \
+        a-=s[n]; \
+        a&=RC5_32_MASK; \
+        a=ROTATE_r32(a,b); \
+        a^=b;
diff --git a/crypto/rc5/rc5_skey.c b/crypto/rc5/rc5_skey.c
new file mode 100644
index 000000000..943a7849b
--- /dev/null
+++ b/crypto/rc5/rc5_skey.c
@@ -0,0 +1,61 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "rc5_locl.h"
+
+void RC5_32_set_key(RC5_32_KEY *key, int len, const unsigned char *data,
+                    int rounds)
+{
+    RC5_32_INT L[64], l, ll, A, B, *S, k;
+    int i, j, m, c, t, ii, jj;
+
+    if ((rounds != RC5_16_ROUNDS) &&
+        (rounds != RC5_12_ROUNDS) && (rounds != RC5_8_ROUNDS))
+        rounds = RC5_16_ROUNDS;
+
+    key->rounds = rounds;
+    S = &(key->data[0]);
+    j = 0;
+    for (i = 0; i <= (len - 8); i += 8) {
+        c2l(data, l);
+        L[j++] = l;
+        c2l(data, l);
+        L[j++] = l;
+    }
+    ii = len - i;
+    if (ii) {
+        k = len & 0x07;
+        c2ln(data, l, ll, k);
+        L[j + 0] = l;
+        L[j + 1] = ll;
+    }
+
+    c = (len + 3) / 4;
+    t = (rounds + 1) * 2;
+    S[0] = RC5_32_P;
+    for (i = 1; i < t; i++)
+        S[i] = (S[i - 1] + RC5_32_Q) & RC5_32_MASK;
+
+    j = (t > c) ? t : c;
+    j *= 3;
+    ii = jj = 0;
+    A = B = 0;
+    for (i = 0; i < j; i++) {
+        k = (S[ii] + A + B) & RC5_32_MASK;
+        A = S[ii] = ROTATE_l32(k, 3);
+        m = (int)(A + B);
+        k = (L[jj] + A + B) & RC5_32_MASK;
+        B = L[jj] = ROTATE_l32(k, m);
+        if (++ii >= t)
+            ii = 0;
+        if (++jj >= c)
+            jj = 0;
+    }
+}
diff --git a/crypto/rc5/rc5cfb64.c b/crypto/rc5/rc5cfb64.c
new file mode 100644
index 000000000..9a8aa6b24
--- /dev/null
+++ b/crypto/rc5/rc5cfb64.c
@@ -0,0 +1,74 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "rc5_locl.h"
+
+/*
+ * The input and output encrypted as though 64bit cfb mode is being used.
+ * The extra state information to record how much of the 64bit block we have
+ * used is contained in *num;
+ */
+
+void RC5_32_cfb64_encrypt(const unsigned char *in, unsigned char *out,
+                          long length, RC5_32_KEY *schedule,
+                          unsigned char *ivec, int *num, int encrypt)
+{
+    register unsigned long v0, v1, t;
+    register int n = *num;
+    register long l = length;
+    unsigned long ti[2];
+    unsigned char *iv, c, cc;
+
+    iv = (unsigned char *)ivec;
+    if (encrypt) {
+        while (l--) {
+            if (n == 0) {
+                c2l(iv, v0);
+                ti[0] = v0;
+                c2l(iv, v1);
+                ti[1] = v1;
+                RC5_32_encrypt((unsigned long *)ti, schedule);
+                iv = (unsigned char *)ivec;
+                t = ti[0];
+                l2c(t, iv);
+                t = ti[1];
+                l2c(t, iv);
+                iv = (unsigned char *)ivec;
+            }
+            c = *(in++) ^ iv[n];
+            *(out++) = c;
+            iv[n] = c;
+            n = (n + 1) & 0x07;
+        }
+    } else {
+        while (l--) {
+            if (n == 0) {
+                c2l(iv, v0);
+                ti[0] = v0;
+                c2l(iv, v1);
+                ti[1] = v1;
+                RC5_32_encrypt((unsigned long *)ti, schedule);
+                iv = (unsigned char *)ivec;
+                t = ti[0];
+                l2c(t, iv);
+                t = ti[1];
+                l2c(t, iv);
+                iv = (unsigned char *)ivec;
+            }
+            cc = *(in++);
+            c = iv[n];
+            iv[n] = cc;
+            *(out++) = c ^ cc;
+            n = (n + 1) & 0x07;
+        }
+    }
+    v0 = v1 = ti[0] = ti[1] = t = c = cc = 0;
+    *num = n;
+}
diff --git a/crypto/rc5/rc5ofb64.c b/crypto/rc5/rc5ofb64.c
new file mode 100644
index 000000000..3a41d773c
--- /dev/null
+++ b/crypto/rc5/rc5ofb64.c
@@ -0,0 +1,61 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "rc5_locl.h"
+
+/*
+ * The input and output encrypted as though 64bit ofb mode is being used.
+ * The extra state information to record how much of the 64bit block we have
+ * used is contained in *num;
+ */
+void RC5_32_ofb64_encrypt(const unsigned char *in, unsigned char *out,
+                          long length, RC5_32_KEY *schedule,
+                          unsigned char *ivec, int *num)
+{
+    register unsigned long v0, v1, t;
+    register int n = *num;
+    register long l = length;
+    unsigned char d[8];
+    register char *dp;
+    unsigned long ti[2];
+    unsigned char *iv;
+    int save = 0;
+
+    iv = (unsigned char *)ivec;
+    c2l(iv, v0);
+    c2l(iv, v1);
+    ti[0] = v0;
+    ti[1] = v1;
+    dp = (char *)d;
+    l2c(v0, dp);
+    l2c(v1, dp);
+    while (l--) {
+        if (n == 0) {
+            RC5_32_encrypt((unsigned long *)ti, schedule);
+            dp = (char *)d;
+            t = ti[0];
+            l2c(t, dp);
+            t = ti[1];
+            l2c(t, dp);
+            save++;
+        }
+        *(out++) = *(in++) ^ d[n];
+        n = (n + 1) & 0x07;
+    }
+    if (save) {
+        v0 = ti[0];
+        v1 = ti[1];
+        iv = (unsigned char *)ivec;
+        l2c(v0, iv);
+        l2c(v1, iv);
+    }
+    t = v0 = v1 = ti[0] = ti[1] = 0;
+    *num = n;
+}
diff --git a/crypto/ripemd/asm/rmd-586.pl b/crypto/ripemd/asm/rmd-586.pl
new file mode 100644
index 000000000..544c496f0
--- /dev/null
+++ b/crypto/ripemd/asm/rmd-586.pl
@@ -0,0 +1,603 @@
+#! /usr/bin/env perl
+# Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+
+# Normal is the
+# ripemd160_block_asm_data_order(RIPEMD160_CTX *c, ULONG *X,int blocks);
+
+$normal=0;
+
+$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+push(@INC,"${dir}","${dir}../../perlasm");
+require "x86asm.pl";
+
+$output=pop;
+open STDOUT,">$output";
+
+&asm_init($ARGV[0],$0);
+
+$A="ecx";
+$B="esi";
+$C="edi";
+$D="ebx";
+$E="ebp";
+$tmp1="eax";
+$tmp2="edx";
+
+$KL1=0x5A827999;
+$KL2=0x6ED9EBA1;
+$KL3=0x8F1BBCDC;
+$KL4=0xA953FD4E;
+$KR0=0x50A28BE6;
+$KR1=0x5C4DD124; 
+$KR2=0x6D703EF3;
+$KR3=0x7A6D76E9;
+
+
+@wl=(	 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,
+	 7, 4,13, 1,10, 6,15, 3,12, 0, 9, 5, 2,14,11, 8,
+	 3,10,14, 4, 9,15, 8, 1, 2, 7, 0, 6,13,11, 5,12,
+	 1, 9,11,10, 0, 8,12, 4,13, 3, 7,15,14, 5, 6, 2,
+	 4, 0, 5, 9, 7,12, 2,10,14, 1, 3, 8,11, 6,15,13,
+	 );
+
+@wr=(	 5,14, 7, 0, 9, 2,11, 4,13, 6,15, 8, 1,10, 3,12,
+	 6,11, 3, 7, 0,13, 5,10,14,15, 8,12, 4, 9, 1, 2,
+	15, 5, 1, 3, 7,14, 6, 9,11, 8,12, 2,10, 0, 4,13,
+	 8, 6, 4, 1, 3,11,15, 0, 5,12, 2,13, 9, 7,10,14,
+	12,15,10, 4, 1, 5, 8, 7, 6, 2,13,14, 0, 3, 9,11,
+	);
+
+@sl=(	11,14,15,12, 5, 8, 7, 9,11,13,14,15, 6, 7, 9, 8,
+	 7, 6, 8,13,11, 9, 7,15, 7,12,15, 9,11, 7,13,12,
+	11,13, 6, 7,14, 9,13,15,14, 8,13, 6, 5,12, 7, 5,
+	11,12,14,15,14,15, 9, 8, 9,14, 5, 6, 8, 6, 5,12,
+	 9,15, 5,11, 6, 8,13,12, 5,12,13,14,11, 8, 5, 6,
+	 );
+
+@sr=(	 8, 9, 9,11,13,15,15, 5, 7, 7, 8,11,14,14,12, 6,
+	 9,13,15, 7,12, 8, 9,11, 7, 7,12, 7, 6,15,13,11,
+	 9, 7,15,11, 8, 6, 6,14,12,13, 5,14,13,13, 7, 5,
+	15, 5, 8,11,14,14, 6,14, 6, 9,12, 9,12, 5,15, 8,
+	 8, 5,12, 9,12, 5,14, 6, 8,13, 6, 5,15,13,11,11,
+ 	);
+
+&ripemd160_block("ripemd160_block_asm_data_order");
+&asm_finish();
+
+close STDOUT;
+
+sub Xv
+	{
+	local($n)=@_;
+	return(&swtmp($n));
+	# tmp on stack
+	}
+
+sub Np
+	{
+	local($p)=@_;
+	local(%n)=($A,$E,$B,$A,$C,$B,$D,$C,$E,$D);
+	return($n{$p});
+	}
+
+sub RIP1
+	{
+	local($a,$b,$c,$d,$e,$pos,$s,$o,$pos2)=@_;
+
+	&comment($p++);
+	if ($p & 1)
+		{
+	 #&mov($tmp1,	$c) if $o == -1;
+	&xor($tmp1,	$d) if $o == -1;
+	 &mov($tmp2,	&Xv($pos));
+	&xor($tmp1,	$b);
+	 &add($a,	$tmp2);
+	&rotl($c,	10);
+	&add($a,	$tmp1);
+	 &mov($tmp1,	&Np($c));	# NEXT
+	 # XXX
+	&rotl($a,	$s);
+	&add($a,	$e);
+		}
+	else
+		{
+	 &xor($tmp1,	$d);
+	&mov($tmp2,	&Xv($pos));
+	 &xor($tmp1,	$b);
+	&add($a,	$tmp1);
+	 &mov($tmp1,	&Np($c)) if $o <= 0;
+	 &mov($tmp1,	-1) if $o == 1;
+	 # XXX if $o == 2;
+	&rotl($c,	10);
+	&add($a,	$tmp2);
+	 &xor($tmp1,	&Np($d)) if $o <= 0;
+	 &mov($tmp2,	&Xv($pos2)) if $o == 1;
+	 &mov($tmp2,	&wparam(0)) if $o == 2;
+	&rotl($a,	$s);
+	&add($a,	$e);
+		}
+	}
+
+sub RIP2
+	{
+	local($a,$b,$c,$d,$e,$pos,$pos2,$s,$K,$o)=@_;
+
+# XXXXXX
+	&comment($p++);
+	if ($p & 1)
+		{
+#	 &mov($tmp2,	&Xv($pos)) if $o < -1;
+#	&mov($tmp1,	-1) if $o < -1;
+
+	 &add($a,	$tmp2);
+	&mov($tmp2,	$c);
+	 &sub($tmp1,	$b);
+	&and($tmp2,	$b);
+	 &and($tmp1,	$d);
+	&or($tmp2,	$tmp1);
+	 &mov($tmp1,	&Xv($pos2)) if $o <= 0; # XXXXXXXXXXXXXX
+	 # XXX
+	&rotl($c,	10);
+	&lea($a,	&DWP($K,$a,$tmp2,1));
+	 &mov($tmp2,	-1) if $o <= 0;
+	 # XXX
+	&rotl($a,	$s);
+	&add($a,	$e);
+		}
+	else
+		{
+	 # XXX
+	 &add($a,	$tmp1);
+	&mov($tmp1,	$c);
+	 &sub($tmp2,	$b);
+	&and($tmp1,	$b);
+	 &and($tmp2,	$d);
+	if ($o != 2)
+		{
+	&or($tmp1,	$tmp2);
+	 &mov($tmp2,	&Xv($pos2)) if $o <= 0;
+	 &mov($tmp2,	-1) if $o == 1;
+	&rotl($c,	10);
+	&lea($a,	&DWP($K,$a,$tmp1,1));
+	 &mov($tmp1,	-1) if $o <= 0;
+	 &sub($tmp2,	&Np($c)) if $o == 1;
+		} else {
+	&or($tmp2,	$tmp1);
+	 &mov($tmp1,	&Np($c));
+	&rotl($c,	10);
+	&lea($a,	&DWP($K,$a,$tmp2,1));
+	 &xor($tmp1,	&Np($d));
+		}
+	&rotl($a,	$s);
+	&add($a,	$e);
+		}
+	}
+
+sub RIP3
+	{
+	local($a,$b,$c,$d,$e,$pos,$s,$K,$o,$pos2)=@_;
+
+	&comment($p++);
+	if ($p & 1)
+		{
+#	 &mov($tmp2,	-1) if $o < -1;
+#	&sub($tmp2,	$c) if $o < -1;
+	 &mov($tmp1,	&Xv($pos));
+	&or($tmp2,	$b);
+	 &add($a,	$tmp1);
+	&xor($tmp2,	$d);
+	 &mov($tmp1,	-1) if $o <= 0;		# NEXT
+	 # XXX
+	&rotl($c,	10);
+	&lea($a,	&DWP($K,$a,$tmp2,1));
+	 &sub($tmp1,	&Np($c)) if $o <= 0;	# NEXT
+	 # XXX
+	&rotl($a,	$s);
+	&add($a,	$e);
+		}
+	else
+		{
+	 &mov($tmp2,	&Xv($pos));
+	&or($tmp1,	$b);
+	 &add($a,	$tmp2);
+	&xor($tmp1,	$d);
+	 &mov($tmp2,	-1) if $o <= 0;		# NEXT
+	 &mov($tmp2,	-1) if $o == 1;
+	 &mov($tmp2,	&Xv($pos2)) if $o == 2;
+	&rotl($c,	10);
+	&lea($a,	&DWP($K,$a,$tmp1,1));
+	 &sub($tmp2,	&Np($c)) if $o <= 0;	# NEXT
+	 &mov($tmp1,	&Np($d)) if $o == 1;
+	 &mov($tmp1,	-1) if $o == 2;
+	&rotl($a,	$s);
+	&add($a,	$e);
+		}
+	}
+
+sub RIP4
+	{
+	local($a,$b,$c,$d,$e,$pos,$s,$K,$o)=@_;
+
+	&comment($p++);
+	if ($p & 1)
+		{
+#	 &mov($tmp2,	-1) if $o == -2;
+#	&mov($tmp1,	$d) if $o == -2;
+	 &sub($tmp2,	$d);
+	&and($tmp1,	$b);
+	 &and($tmp2,	$c);
+	&or($tmp2,	$tmp1);
+	 &mov($tmp1,	&Xv($pos));
+	&rotl($c,	10);
+	&lea($a,	&DWP($K,$a,$tmp2));
+	 &mov($tmp2,	-1) unless $o > 0;	# NEXT
+	 # XXX
+	&add($a,	$tmp1);
+	 &mov($tmp1,	&Np($d)) unless $o > 0; # NEXT
+	 # XXX
+	&rotl($a,	$s);
+	&add($a,	$e);
+		}
+	else
+		{
+	 &sub($tmp2,	$d);
+	&and($tmp1,	$b);
+	 &and($tmp2,	$c);
+	&or($tmp2,	$tmp1);
+	 &mov($tmp1,	&Xv($pos));
+	&rotl($c,	10);
+	&lea($a,	&DWP($K,$a,$tmp2));
+	 &mov($tmp2,	-1) if $o == 0;	# NEXT
+	 &mov($tmp2,	-1) if $o == 1;
+	 &mov($tmp2,	-1) if $o == 2;
+	 # XXX
+	&add($a,	$tmp1);
+	 &mov($tmp1,	&Np($d)) if $o == 0;	# NEXT
+	 &sub($tmp2,	&Np($d)) if $o == 1;
+	 &sub($tmp2,	&Np($c)) if $o == 2;
+	 # XXX
+	&rotl($a,	$s);
+	&add($a,	$e);
+		}
+	}
+
+sub RIP5
+	{
+	local($a,$b,$c,$d,$e,$pos,$s,$K,$o)=@_;
+
+	&comment($p++);
+	if ($p & 1)
+		{
+	 &mov($tmp2,	-1) if $o == -2;
+	&sub($tmp2,	$d) if $o == -2;
+	 &mov($tmp1,	&Xv($pos));
+	&or($tmp2,	$c);
+	 &add($a,	$tmp1);
+	&xor($tmp2,	$b);
+	 &mov($tmp1,	-1) if $o <= 0;
+	 # XXX
+	&rotl($c,	10);
+	&lea($a,	&DWP($K,$a,$tmp2,1));
+	 &sub($tmp1,	&Np($d)) if $o <= 0;
+	 # XXX
+	&rotl($a,	$s);
+	&add($a,	$e);
+		}
+	else
+		{
+	 &mov($tmp2,	&Xv($pos));
+	&or($tmp1,	$c);
+	 &add($a,	$tmp2);
+	&xor($tmp1,	$b);
+	 &mov($tmp2,	-1) if $o <= 0;
+	 &mov($tmp2,	&wparam(0)) if $o == 1;	# Middle code
+	 &mov($tmp2,	-1) if $o == 2;
+	&rotl($c,	10);
+	&lea($a,	&DWP($K,$a,$tmp1,1));
+	 &sub($tmp2,	&Np($d)) if $o <= 0;
+	 &mov(&swtmp(16),	$A) if $o == 1;
+	 &mov($tmp1,	&Np($d)) if $o == 2;
+	&rotl($a,	$s);
+	&add($a,	$e);
+		}
+	}
+
+sub ripemd160_block
+	{
+	local($name)=@_;
+
+	&function_begin_B($name,"",3);
+
+	# parameter 1 is the RIPEMD160_CTX structure.
+	# A	0
+	# B	4
+	# C	8
+	# D 	12
+	# E 	16
+
+	&mov($tmp2,	&wparam(0));
+	 &mov($tmp1,	&wparam(1));
+	&push("esi");
+	 &mov($A,	&DWP( 0,$tmp2,"",0));
+	&push("edi");
+	 &mov($B,	&DWP( 4,$tmp2,"",0));
+	&push("ebp");
+	 &mov($C,	&DWP( 8,$tmp2,"",0));
+	&push("ebx");
+	 &stack_push(16+5+6);
+			  # Special comment about the figure of 6.
+			  # Idea is to pad the current frame so
+			  # that the top of the stack gets fairly
+			  # aligned. Well, as you realize it would
+			  # always depend on how the frame below is
+			  # aligned. The good news are that gcc-2.95
+			  # and later does keep first argument at
+			  # least double-wise aligned.
+			  #			
+
+	&set_label("start") unless $normal;
+	&comment("");
+
+	# &mov($tmp1,	&wparam(1)); # Done at end of loop
+	# &mov($tmp2,	&wparam(0)); # Done at end of loop
+
+	for ($z=0; $z<16; $z+=2)
+		{
+		&mov($D,		&DWP( $z*4,$tmp1,"",0));
+		 &mov($E,		&DWP( ($z+1)*4,$tmp1,"",0));
+		&mov(&swtmp($z),	$D);
+		 &mov(&swtmp($z+1),	$E);
+		}
+	&mov($tmp1,	$C);
+	 &mov($D,	&DWP(12,$tmp2,"",0));
+	&mov($E,	&DWP(16,$tmp2,"",0));
+
+	&RIP1($A,$B,$C,$D,$E,$wl[ 0],$sl[ 0],-1);
+	&RIP1($E,$A,$B,$C,$D,$wl[ 1],$sl[ 1],0);
+	&RIP1($D,$E,$A,$B,$C,$wl[ 2],$sl[ 2],0);
+	&RIP1($C,$D,$E,$A,$B,$wl[ 3],$sl[ 3],0);
+	&RIP1($B,$C,$D,$E,$A,$wl[ 4],$sl[ 4],0);
+	&RIP1($A,$B,$C,$D,$E,$wl[ 5],$sl[ 5],0);
+	&RIP1($E,$A,$B,$C,$D,$wl[ 6],$sl[ 6],0);
+	&RIP1($D,$E,$A,$B,$C,$wl[ 7],$sl[ 7],0);
+	&RIP1($C,$D,$E,$A,$B,$wl[ 8],$sl[ 8],0);
+	&RIP1($B,$C,$D,$E,$A,$wl[ 9],$sl[ 9],0);
+	&RIP1($A,$B,$C,$D,$E,$wl[10],$sl[10],0);
+	&RIP1($E,$A,$B,$C,$D,$wl[11],$sl[11],0);
+	&RIP1($D,$E,$A,$B,$C,$wl[12],$sl[12],0);
+	&RIP1($C,$D,$E,$A,$B,$wl[13],$sl[13],0);
+	&RIP1($B,$C,$D,$E,$A,$wl[14],$sl[14],0);
+	&RIP1($A,$B,$C,$D,$E,$wl[15],$sl[15],1,$wl[16]);
+
+	&RIP2($E,$A,$B,$C,$D,$wl[16],$wl[17],$sl[16],$KL1,-1);
+	&RIP2($D,$E,$A,$B,$C,$wl[17],$wl[18],$sl[17],$KL1,0);
+	&RIP2($C,$D,$E,$A,$B,$wl[18],$wl[19],$sl[18],$KL1,0);
+	&RIP2($B,$C,$D,$E,$A,$wl[19],$wl[20],$sl[19],$KL1,0);
+	&RIP2($A,$B,$C,$D,$E,$wl[20],$wl[21],$sl[20],$KL1,0);
+	&RIP2($E,$A,$B,$C,$D,$wl[21],$wl[22],$sl[21],$KL1,0);
+	&RIP2($D,$E,$A,$B,$C,$wl[22],$wl[23],$sl[22],$KL1,0);
+	&RIP2($C,$D,$E,$A,$B,$wl[23],$wl[24],$sl[23],$KL1,0);
+	&RIP2($B,$C,$D,$E,$A,$wl[24],$wl[25],$sl[24],$KL1,0);
+	&RIP2($A,$B,$C,$D,$E,$wl[25],$wl[26],$sl[25],$KL1,0);
+	&RIP2($E,$A,$B,$C,$D,$wl[26],$wl[27],$sl[26],$KL1,0);
+	&RIP2($D,$E,$A,$B,$C,$wl[27],$wl[28],$sl[27],$KL1,0);
+	&RIP2($C,$D,$E,$A,$B,$wl[28],$wl[29],$sl[28],$KL1,0);
+	&RIP2($B,$C,$D,$E,$A,$wl[29],$wl[30],$sl[29],$KL1,0);
+	&RIP2($A,$B,$C,$D,$E,$wl[30],$wl[31],$sl[30],$KL1,0);
+	&RIP2($E,$A,$B,$C,$D,$wl[31],$wl[32],$sl[31],$KL1,1);
+
+	&RIP3($D,$E,$A,$B,$C,$wl[32],$sl[32],$KL2,-1);
+	&RIP3($C,$D,$E,$A,$B,$wl[33],$sl[33],$KL2,0);
+	&RIP3($B,$C,$D,$E,$A,$wl[34],$sl[34],$KL2,0);
+	&RIP3($A,$B,$C,$D,$E,$wl[35],$sl[35],$KL2,0);
+	&RIP3($E,$A,$B,$C,$D,$wl[36],$sl[36],$KL2,0);
+	&RIP3($D,$E,$A,$B,$C,$wl[37],$sl[37],$KL2,0);
+	&RIP3($C,$D,$E,$A,$B,$wl[38],$sl[38],$KL2,0);
+	&RIP3($B,$C,$D,$E,$A,$wl[39],$sl[39],$KL2,0);
+	&RIP3($A,$B,$C,$D,$E,$wl[40],$sl[40],$KL2,0);
+	&RIP3($E,$A,$B,$C,$D,$wl[41],$sl[41],$KL2,0);
+	&RIP3($D,$E,$A,$B,$C,$wl[42],$sl[42],$KL2,0);
+	&RIP3($C,$D,$E,$A,$B,$wl[43],$sl[43],$KL2,0);
+	&RIP3($B,$C,$D,$E,$A,$wl[44],$sl[44],$KL2,0);
+	&RIP3($A,$B,$C,$D,$E,$wl[45],$sl[45],$KL2,0);
+	&RIP3($E,$A,$B,$C,$D,$wl[46],$sl[46],$KL2,0);
+	&RIP3($D,$E,$A,$B,$C,$wl[47],$sl[47],$KL2,1);
+
+	&RIP4($C,$D,$E,$A,$B,$wl[48],$sl[48],$KL3,-1);
+	&RIP4($B,$C,$D,$E,$A,$wl[49],$sl[49],$KL3,0);
+	&RIP4($A,$B,$C,$D,$E,$wl[50],$sl[50],$KL3,0);
+	&RIP4($E,$A,$B,$C,$D,$wl[51],$sl[51],$KL3,0);
+	&RIP4($D,$E,$A,$B,$C,$wl[52],$sl[52],$KL3,0);
+	&RIP4($C,$D,$E,$A,$B,$wl[53],$sl[53],$KL3,0);
+	&RIP4($B,$C,$D,$E,$A,$wl[54],$sl[54],$KL3,0);
+	&RIP4($A,$B,$C,$D,$E,$wl[55],$sl[55],$KL3,0);
+	&RIP4($E,$A,$B,$C,$D,$wl[56],$sl[56],$KL3,0);
+	&RIP4($D,$E,$A,$B,$C,$wl[57],$sl[57],$KL3,0);
+	&RIP4($C,$D,$E,$A,$B,$wl[58],$sl[58],$KL3,0);
+	&RIP4($B,$C,$D,$E,$A,$wl[59],$sl[59],$KL3,0);
+	&RIP4($A,$B,$C,$D,$E,$wl[60],$sl[60],$KL3,0);
+	&RIP4($E,$A,$B,$C,$D,$wl[61],$sl[61],$KL3,0);
+	&RIP4($D,$E,$A,$B,$C,$wl[62],$sl[62],$KL3,0);
+	&RIP4($C,$D,$E,$A,$B,$wl[63],$sl[63],$KL3,1);
+
+	&RIP5($B,$C,$D,$E,$A,$wl[64],$sl[64],$KL4,-1);
+	&RIP5($A,$B,$C,$D,$E,$wl[65],$sl[65],$KL4,0);
+	&RIP5($E,$A,$B,$C,$D,$wl[66],$sl[66],$KL4,0);
+	&RIP5($D,$E,$A,$B,$C,$wl[67],$sl[67],$KL4,0);
+	&RIP5($C,$D,$E,$A,$B,$wl[68],$sl[68],$KL4,0);
+	&RIP5($B,$C,$D,$E,$A,$wl[69],$sl[69],$KL4,0);
+	&RIP5($A,$B,$C,$D,$E,$wl[70],$sl[70],$KL4,0);
+	&RIP5($E,$A,$B,$C,$D,$wl[71],$sl[71],$KL4,0);
+	&RIP5($D,$E,$A,$B,$C,$wl[72],$sl[72],$KL4,0);
+	&RIP5($C,$D,$E,$A,$B,$wl[73],$sl[73],$KL4,0);
+	&RIP5($B,$C,$D,$E,$A,$wl[74],$sl[74],$KL4,0);
+	&RIP5($A,$B,$C,$D,$E,$wl[75],$sl[75],$KL4,0);
+	&RIP5($E,$A,$B,$C,$D,$wl[76],$sl[76],$KL4,0);
+	&RIP5($D,$E,$A,$B,$C,$wl[77],$sl[77],$KL4,0);
+	&RIP5($C,$D,$E,$A,$B,$wl[78],$sl[78],$KL4,0);
+	&RIP5($B,$C,$D,$E,$A,$wl[79],$sl[79],$KL4,1);
+
+	# &mov($tmp2,	&wparam(0)); # moved into last RIP5
+	# &mov(&swtmp(16),	$A);
+	 &mov($A,	&DWP( 0,$tmp2,"",0));
+	&mov(&swtmp(16+1),	$B);
+	 &mov(&swtmp(16+2),	$C);
+	&mov($B,	&DWP( 4,$tmp2,"",0));
+	 &mov(&swtmp(16+3),	$D);
+	&mov($C,	&DWP( 8,$tmp2,"",0));
+	 &mov(&swtmp(16+4),	$E);
+	&mov($D,	&DWP(12,$tmp2,"",0));
+	 &mov($E,	&DWP(16,$tmp2,"",0));
+
+	&RIP5($A,$B,$C,$D,$E,$wr[ 0],$sr[ 0],$KR0,-2);
+	&RIP5($E,$A,$B,$C,$D,$wr[ 1],$sr[ 1],$KR0,0);
+	&RIP5($D,$E,$A,$B,$C,$wr[ 2],$sr[ 2],$KR0,0);
+	&RIP5($C,$D,$E,$A,$B,$wr[ 3],$sr[ 3],$KR0,0);
+	&RIP5($B,$C,$D,$E,$A,$wr[ 4],$sr[ 4],$KR0,0);
+	&RIP5($A,$B,$C,$D,$E,$wr[ 5],$sr[ 5],$KR0,0);
+	&RIP5($E,$A,$B,$C,$D,$wr[ 6],$sr[ 6],$KR0,0);
+	&RIP5($D,$E,$A,$B,$C,$wr[ 7],$sr[ 7],$KR0,0);
+	&RIP5($C,$D,$E,$A,$B,$wr[ 8],$sr[ 8],$KR0,0);
+	&RIP5($B,$C,$D,$E,$A,$wr[ 9],$sr[ 9],$KR0,0);
+	&RIP5($A,$B,$C,$D,$E,$wr[10],$sr[10],$KR0,0);
+	&RIP5($E,$A,$B,$C,$D,$wr[11],$sr[11],$KR0,0);
+	&RIP5($D,$E,$A,$B,$C,$wr[12],$sr[12],$KR0,0);
+	&RIP5($C,$D,$E,$A,$B,$wr[13],$sr[13],$KR0,0);
+	&RIP5($B,$C,$D,$E,$A,$wr[14],$sr[14],$KR0,0);
+	&RIP5($A,$B,$C,$D,$E,$wr[15],$sr[15],$KR0,2);
+
+	&RIP4($E,$A,$B,$C,$D,$wr[16],$sr[16],$KR1,-2);
+	&RIP4($D,$E,$A,$B,$C,$wr[17],$sr[17],$KR1,0);
+	&RIP4($C,$D,$E,$A,$B,$wr[18],$sr[18],$KR1,0);
+	&RIP4($B,$C,$D,$E,$A,$wr[19],$sr[19],$KR1,0);
+	&RIP4($A,$B,$C,$D,$E,$wr[20],$sr[20],$KR1,0);
+	&RIP4($E,$A,$B,$C,$D,$wr[21],$sr[21],$KR1,0);
+	&RIP4($D,$E,$A,$B,$C,$wr[22],$sr[22],$KR1,0);
+	&RIP4($C,$D,$E,$A,$B,$wr[23],$sr[23],$KR1,0);
+	&RIP4($B,$C,$D,$E,$A,$wr[24],$sr[24],$KR1,0);
+	&RIP4($A,$B,$C,$D,$E,$wr[25],$sr[25],$KR1,0);
+	&RIP4($E,$A,$B,$C,$D,$wr[26],$sr[26],$KR1,0);
+	&RIP4($D,$E,$A,$B,$C,$wr[27],$sr[27],$KR1,0);
+	&RIP4($C,$D,$E,$A,$B,$wr[28],$sr[28],$KR1,0);
+	&RIP4($B,$C,$D,$E,$A,$wr[29],$sr[29],$KR1,0);
+	&RIP4($A,$B,$C,$D,$E,$wr[30],$sr[30],$KR1,0);
+	&RIP4($E,$A,$B,$C,$D,$wr[31],$sr[31],$KR1,2);
+
+	&RIP3($D,$E,$A,$B,$C,$wr[32],$sr[32],$KR2,-2);
+	&RIP3($C,$D,$E,$A,$B,$wr[33],$sr[33],$KR2,0);
+	&RIP3($B,$C,$D,$E,$A,$wr[34],$sr[34],$KR2,0);
+	&RIP3($A,$B,$C,$D,$E,$wr[35],$sr[35],$KR2,0);
+	&RIP3($E,$A,$B,$C,$D,$wr[36],$sr[36],$KR2,0);
+	&RIP3($D,$E,$A,$B,$C,$wr[37],$sr[37],$KR2,0);
+	&RIP3($C,$D,$E,$A,$B,$wr[38],$sr[38],$KR2,0);
+	&RIP3($B,$C,$D,$E,$A,$wr[39],$sr[39],$KR2,0);
+	&RIP3($A,$B,$C,$D,$E,$wr[40],$sr[40],$KR2,0);
+	&RIP3($E,$A,$B,$C,$D,$wr[41],$sr[41],$KR2,0);
+	&RIP3($D,$E,$A,$B,$C,$wr[42],$sr[42],$KR2,0);
+	&RIP3($C,$D,$E,$A,$B,$wr[43],$sr[43],$KR2,0);
+	&RIP3($B,$C,$D,$E,$A,$wr[44],$sr[44],$KR2,0);
+	&RIP3($A,$B,$C,$D,$E,$wr[45],$sr[45],$KR2,0);
+	&RIP3($E,$A,$B,$C,$D,$wr[46],$sr[46],$KR2,0);
+	&RIP3($D,$E,$A,$B,$C,$wr[47],$sr[47],$KR2,2,$wr[48]);
+
+	&RIP2($C,$D,$E,$A,$B,$wr[48],$wr[49],$sr[48],$KR3,-2);
+	&RIP2($B,$C,$D,$E,$A,$wr[49],$wr[50],$sr[49],$KR3,0);
+	&RIP2($A,$B,$C,$D,$E,$wr[50],$wr[51],$sr[50],$KR3,0);
+	&RIP2($E,$A,$B,$C,$D,$wr[51],$wr[52],$sr[51],$KR3,0);
+	&RIP2($D,$E,$A,$B,$C,$wr[52],$wr[53],$sr[52],$KR3,0);
+	&RIP2($C,$D,$E,$A,$B,$wr[53],$wr[54],$sr[53],$KR3,0);
+	&RIP2($B,$C,$D,$E,$A,$wr[54],$wr[55],$sr[54],$KR3,0);
+	&RIP2($A,$B,$C,$D,$E,$wr[55],$wr[56],$sr[55],$KR3,0);
+	&RIP2($E,$A,$B,$C,$D,$wr[56],$wr[57],$sr[56],$KR3,0);
+	&RIP2($D,$E,$A,$B,$C,$wr[57],$wr[58],$sr[57],$KR3,0);
+	&RIP2($C,$D,$E,$A,$B,$wr[58],$wr[59],$sr[58],$KR3,0);
+	&RIP2($B,$C,$D,$E,$A,$wr[59],$wr[60],$sr[59],$KR3,0);
+	&RIP2($A,$B,$C,$D,$E,$wr[60],$wr[61],$sr[60],$KR3,0);
+	&RIP2($E,$A,$B,$C,$D,$wr[61],$wr[62],$sr[61],$KR3,0);
+	&RIP2($D,$E,$A,$B,$C,$wr[62],$wr[63],$sr[62],$KR3,0);
+	&RIP2($C,$D,$E,$A,$B,$wr[63],$wr[64],$sr[63],$KR3,2);
+
+	&RIP1($B,$C,$D,$E,$A,$wr[64],$sr[64],-2);
+	&RIP1($A,$B,$C,$D,$E,$wr[65],$sr[65],0);
+	&RIP1($E,$A,$B,$C,$D,$wr[66],$sr[66],0);
+	&RIP1($D,$E,$A,$B,$C,$wr[67],$sr[67],0);
+	&RIP1($C,$D,$E,$A,$B,$wr[68],$sr[68],0);
+	&RIP1($B,$C,$D,$E,$A,$wr[69],$sr[69],0);
+	&RIP1($A,$B,$C,$D,$E,$wr[70],$sr[70],0);
+	&RIP1($E,$A,$B,$C,$D,$wr[71],$sr[71],0);
+	&RIP1($D,$E,$A,$B,$C,$wr[72],$sr[72],0);
+	&RIP1($C,$D,$E,$A,$B,$wr[73],$sr[73],0);
+	&RIP1($B,$C,$D,$E,$A,$wr[74],$sr[74],0);
+	&RIP1($A,$B,$C,$D,$E,$wr[75],$sr[75],0);
+	&RIP1($E,$A,$B,$C,$D,$wr[76],$sr[76],0);
+	&RIP1($D,$E,$A,$B,$C,$wr[77],$sr[77],0);
+	&RIP1($C,$D,$E,$A,$B,$wr[78],$sr[78],0);
+	&RIP1($B,$C,$D,$E,$A,$wr[79],$sr[79],2);
+
+	# &mov($tmp2,	&wparam(0)); # Moved into last round
+
+	 &mov($tmp1,	&DWP( 4,$tmp2,"",0));	# ctx->B
+ 	&add($D,	$tmp1);	
+	 &mov($tmp1,	&swtmp(16+2));		# $c
+	&add($D,	$tmp1);
+
+	 &mov($tmp1,	&DWP( 8,$tmp2,"",0));	# ctx->C
+	&add($E,	$tmp1);	
+	 &mov($tmp1,	&swtmp(16+3));		# $d
+	&add($E,	$tmp1);
+
+	 &mov($tmp1,	&DWP(12,$tmp2,"",0));	# ctx->D
+	&add($A,	$tmp1);	
+	 &mov($tmp1,	&swtmp(16+4));		# $e
+	&add($A,	$tmp1);
+
+
+	 &mov($tmp1,	&DWP(16,$tmp2,"",0));	# ctx->E
+	&add($B,	$tmp1);	
+	 &mov($tmp1,	&swtmp(16+0));		# $a
+	&add($B,	$tmp1);
+
+	 &mov($tmp1,	&DWP( 0,$tmp2,"",0));	# ctx->A
+	&add($C,	$tmp1);	
+	 &mov($tmp1,	&swtmp(16+1));		# $b
+	&add($C,	$tmp1);
+
+	 &mov($tmp1,	&wparam(2));
+
+	&mov(&DWP( 0,$tmp2,"",0),	$D);
+	 &mov(&DWP( 4,$tmp2,"",0),	$E);
+	&mov(&DWP( 8,$tmp2,"",0),	$A);
+	 &sub($tmp1,1);
+	&mov(&DWP(12,$tmp2,"",0),	$B);
+	 &mov(&DWP(16,$tmp2,"",0),	$C);
+
+	&jle(&label("get_out"));
+
+	&mov(&wparam(2),$tmp1);
+	 &mov($C,	$A);
+	&mov($tmp1,	&wparam(1));
+	 &mov($A,	$D);
+	&add($tmp1,	64);
+	 &mov($B,	$E);
+	&mov(&wparam(1),$tmp1);
+
+	&jmp(&label("start"));
+
+	&set_label("get_out");
+
+	&stack_pop(16+5+6);
+
+	&pop("ebx");
+	&pop("ebp");
+	&pop("edi");
+	&pop("esi");
+	&ret();
+	&function_end_B($name);
+	}
+
diff --git a/crypto/ripemd/build.info b/crypto/ripemd/build.info
new file mode 100644
index 000000000..c45050cb2
--- /dev/null
+++ b/crypto/ripemd/build.info
@@ -0,0 +1,6 @@
+LIBS=../../libcrypto
+SOURCE[../../libcrypto]=\
+        rmd_dgst.c rmd_one.c {- $target{rmd160_asm_src} -}
+
+GENERATE[rmd-586.s]=asm/rmd-586.pl $(PERLASM_SCHEME) $(CFLAGS) $(LIB_CFLAGS)
+DEPEND[rmd-586.s]=../perlasm/x86asm.pl
diff --git a/crypto/ripemd/rmd_dgst.c b/crypto/ripemd/rmd_dgst.c
new file mode 100644
index 000000000..a1670c7fb
--- /dev/null
+++ b/crypto/ripemd/rmd_dgst.c
@@ -0,0 +1,282 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "rmd_locl.h"
+#include 
+
+#ifdef RMD160_ASM
+void ripemd160_block_x86(RIPEMD160_CTX *c, unsigned long *p, size_t num);
+# define ripemd160_block ripemd160_block_x86
+#else
+void ripemd160_block(RIPEMD160_CTX *c, unsigned long *p, size_t num);
+#endif
+
+int RIPEMD160_Init(RIPEMD160_CTX *c)
+{
+    memset(c, 0, sizeof(*c));
+    c->A = RIPEMD160_A;
+    c->B = RIPEMD160_B;
+    c->C = RIPEMD160_C;
+    c->D = RIPEMD160_D;
+    c->E = RIPEMD160_E;
+    return 1;
+}
+
+#ifndef ripemd160_block_data_order
+# ifdef X
+#  undef X
+# endif
+void ripemd160_block_data_order(RIPEMD160_CTX *ctx, const void *p, size_t num)
+{
+    const unsigned char *data = p;
+    register unsigned MD32_REG_T A, B, C, D, E;
+    unsigned MD32_REG_T a, b, c, d, e, l;
+# ifndef MD32_XARRAY
+    /* See comment in crypto/sha/sha_locl.h for details. */
+    unsigned MD32_REG_T XX0, XX1, XX2, XX3, XX4, XX5, XX6, XX7,
+        XX8, XX9, XX10, XX11, XX12, XX13, XX14, XX15;
+#  define X(i)   XX##i
+# else
+    RIPEMD160_LONG XX[16];
+#  define X(i)   XX[i]
+# endif
+
+    for (; num--;) {
+
+        A = ctx->A;
+        B = ctx->B;
+        C = ctx->C;
+        D = ctx->D;
+        E = ctx->E;
+
+        (void)HOST_c2l(data, l);
+        X(0) = l;
+        (void)HOST_c2l(data, l);
+        X(1) = l;
+        RIP1(A, B, C, D, E, WL00, SL00);
+        (void)HOST_c2l(data, l);
+        X(2) = l;
+        RIP1(E, A, B, C, D, WL01, SL01);
+        (void)HOST_c2l(data, l);
+        X(3) = l;
+        RIP1(D, E, A, B, C, WL02, SL02);
+        (void)HOST_c2l(data, l);
+        X(4) = l;
+        RIP1(C, D, E, A, B, WL03, SL03);
+        (void)HOST_c2l(data, l);
+        X(5) = l;
+        RIP1(B, C, D, E, A, WL04, SL04);
+        (void)HOST_c2l(data, l);
+        X(6) = l;
+        RIP1(A, B, C, D, E, WL05, SL05);
+        (void)HOST_c2l(data, l);
+        X(7) = l;
+        RIP1(E, A, B, C, D, WL06, SL06);
+        (void)HOST_c2l(data, l);
+        X(8) = l;
+        RIP1(D, E, A, B, C, WL07, SL07);
+        (void)HOST_c2l(data, l);
+        X(9) = l;
+        RIP1(C, D, E, A, B, WL08, SL08);
+        (void)HOST_c2l(data, l);
+        X(10) = l;
+        RIP1(B, C, D, E, A, WL09, SL09);
+        (void)HOST_c2l(data, l);
+        X(11) = l;
+        RIP1(A, B, C, D, E, WL10, SL10);
+        (void)HOST_c2l(data, l);
+        X(12) = l;
+        RIP1(E, A, B, C, D, WL11, SL11);
+        (void)HOST_c2l(data, l);
+        X(13) = l;
+        RIP1(D, E, A, B, C, WL12, SL12);
+        (void)HOST_c2l(data, l);
+        X(14) = l;
+        RIP1(C, D, E, A, B, WL13, SL13);
+        (void)HOST_c2l(data, l);
+        X(15) = l;
+        RIP1(B, C, D, E, A, WL14, SL14);
+        RIP1(A, B, C, D, E, WL15, SL15);
+
+        RIP2(E, A, B, C, D, WL16, SL16, KL1);
+        RIP2(D, E, A, B, C, WL17, SL17, KL1);
+        RIP2(C, D, E, A, B, WL18, SL18, KL1);
+        RIP2(B, C, D, E, A, WL19, SL19, KL1);
+        RIP2(A, B, C, D, E, WL20, SL20, KL1);
+        RIP2(E, A, B, C, D, WL21, SL21, KL1);
+        RIP2(D, E, A, B, C, WL22, SL22, KL1);
+        RIP2(C, D, E, A, B, WL23, SL23, KL1);
+        RIP2(B, C, D, E, A, WL24, SL24, KL1);
+        RIP2(A, B, C, D, E, WL25, SL25, KL1);
+        RIP2(E, A, B, C, D, WL26, SL26, KL1);
+        RIP2(D, E, A, B, C, WL27, SL27, KL1);
+        RIP2(C, D, E, A, B, WL28, SL28, KL1);
+        RIP2(B, C, D, E, A, WL29, SL29, KL1);
+        RIP2(A, B, C, D, E, WL30, SL30, KL1);
+        RIP2(E, A, B, C, D, WL31, SL31, KL1);
+
+        RIP3(D, E, A, B, C, WL32, SL32, KL2);
+        RIP3(C, D, E, A, B, WL33, SL33, KL2);
+        RIP3(B, C, D, E, A, WL34, SL34, KL2);
+        RIP3(A, B, C, D, E, WL35, SL35, KL2);
+        RIP3(E, A, B, C, D, WL36, SL36, KL2);
+        RIP3(D, E, A, B, C, WL37, SL37, KL2);
+        RIP3(C, D, E, A, B, WL38, SL38, KL2);
+        RIP3(B, C, D, E, A, WL39, SL39, KL2);
+        RIP3(A, B, C, D, E, WL40, SL40, KL2);
+        RIP3(E, A, B, C, D, WL41, SL41, KL2);
+        RIP3(D, E, A, B, C, WL42, SL42, KL2);
+        RIP3(C, D, E, A, B, WL43, SL43, KL2);
+        RIP3(B, C, D, E, A, WL44, SL44, KL2);
+        RIP3(A, B, C, D, E, WL45, SL45, KL2);
+        RIP3(E, A, B, C, D, WL46, SL46, KL2);
+        RIP3(D, E, A, B, C, WL47, SL47, KL2);
+
+        RIP4(C, D, E, A, B, WL48, SL48, KL3);
+        RIP4(B, C, D, E, A, WL49, SL49, KL3);
+        RIP4(A, B, C, D, E, WL50, SL50, KL3);
+        RIP4(E, A, B, C, D, WL51, SL51, KL3);
+        RIP4(D, E, A, B, C, WL52, SL52, KL3);
+        RIP4(C, D, E, A, B, WL53, SL53, KL3);
+        RIP4(B, C, D, E, A, WL54, SL54, KL3);
+        RIP4(A, B, C, D, E, WL55, SL55, KL3);
+        RIP4(E, A, B, C, D, WL56, SL56, KL3);
+        RIP4(D, E, A, B, C, WL57, SL57, KL3);
+        RIP4(C, D, E, A, B, WL58, SL58, KL3);
+        RIP4(B, C, D, E, A, WL59, SL59, KL3);
+        RIP4(A, B, C, D, E, WL60, SL60, KL3);
+        RIP4(E, A, B, C, D, WL61, SL61, KL3);
+        RIP4(D, E, A, B, C, WL62, SL62, KL3);
+        RIP4(C, D, E, A, B, WL63, SL63, KL3);
+
+        RIP5(B, C, D, E, A, WL64, SL64, KL4);
+        RIP5(A, B, C, D, E, WL65, SL65, KL4);
+        RIP5(E, A, B, C, D, WL66, SL66, KL4);
+        RIP5(D, E, A, B, C, WL67, SL67, KL4);
+        RIP5(C, D, E, A, B, WL68, SL68, KL4);
+        RIP5(B, C, D, E, A, WL69, SL69, KL4);
+        RIP5(A, B, C, D, E, WL70, SL70, KL4);
+        RIP5(E, A, B, C, D, WL71, SL71, KL4);
+        RIP5(D, E, A, B, C, WL72, SL72, KL4);
+        RIP5(C, D, E, A, B, WL73, SL73, KL4);
+        RIP5(B, C, D, E, A, WL74, SL74, KL4);
+        RIP5(A, B, C, D, E, WL75, SL75, KL4);
+        RIP5(E, A, B, C, D, WL76, SL76, KL4);
+        RIP5(D, E, A, B, C, WL77, SL77, KL4);
+        RIP5(C, D, E, A, B, WL78, SL78, KL4);
+        RIP5(B, C, D, E, A, WL79, SL79, KL4);
+
+        a = A;
+        b = B;
+        c = C;
+        d = D;
+        e = E;
+        /* Do other half */
+        A = ctx->A;
+        B = ctx->B;
+        C = ctx->C;
+        D = ctx->D;
+        E = ctx->E;
+
+        RIP5(A, B, C, D, E, WR00, SR00, KR0);
+        RIP5(E, A, B, C, D, WR01, SR01, KR0);
+        RIP5(D, E, A, B, C, WR02, SR02, KR0);
+        RIP5(C, D, E, A, B, WR03, SR03, KR0);
+        RIP5(B, C, D, E, A, WR04, SR04, KR0);
+        RIP5(A, B, C, D, E, WR05, SR05, KR0);
+        RIP5(E, A, B, C, D, WR06, SR06, KR0);
+        RIP5(D, E, A, B, C, WR07, SR07, KR0);
+        RIP5(C, D, E, A, B, WR08, SR08, KR0);
+        RIP5(B, C, D, E, A, WR09, SR09, KR0);
+        RIP5(A, B, C, D, E, WR10, SR10, KR0);
+        RIP5(E, A, B, C, D, WR11, SR11, KR0);
+        RIP5(D, E, A, B, C, WR12, SR12, KR0);
+        RIP5(C, D, E, A, B, WR13, SR13, KR0);
+        RIP5(B, C, D, E, A, WR14, SR14, KR0);
+        RIP5(A, B, C, D, E, WR15, SR15, KR0);
+
+        RIP4(E, A, B, C, D, WR16, SR16, KR1);
+        RIP4(D, E, A, B, C, WR17, SR17, KR1);
+        RIP4(C, D, E, A, B, WR18, SR18, KR1);
+        RIP4(B, C, D, E, A, WR19, SR19, KR1);
+        RIP4(A, B, C, D, E, WR20, SR20, KR1);
+        RIP4(E, A, B, C, D, WR21, SR21, KR1);
+        RIP4(D, E, A, B, C, WR22, SR22, KR1);
+        RIP4(C, D, E, A, B, WR23, SR23, KR1);
+        RIP4(B, C, D, E, A, WR24, SR24, KR1);
+        RIP4(A, B, C, D, E, WR25, SR25, KR1);
+        RIP4(E, A, B, C, D, WR26, SR26, KR1);
+        RIP4(D, E, A, B, C, WR27, SR27, KR1);
+        RIP4(C, D, E, A, B, WR28, SR28, KR1);
+        RIP4(B, C, D, E, A, WR29, SR29, KR1);
+        RIP4(A, B, C, D, E, WR30, SR30, KR1);
+        RIP4(E, A, B, C, D, WR31, SR31, KR1);
+
+        RIP3(D, E, A, B, C, WR32, SR32, KR2);
+        RIP3(C, D, E, A, B, WR33, SR33, KR2);
+        RIP3(B, C, D, E, A, WR34, SR34, KR2);
+        RIP3(A, B, C, D, E, WR35, SR35, KR2);
+        RIP3(E, A, B, C, D, WR36, SR36, KR2);
+        RIP3(D, E, A, B, C, WR37, SR37, KR2);
+        RIP3(C, D, E, A, B, WR38, SR38, KR2);
+        RIP3(B, C, D, E, A, WR39, SR39, KR2);
+        RIP3(A, B, C, D, E, WR40, SR40, KR2);
+        RIP3(E, A, B, C, D, WR41, SR41, KR2);
+        RIP3(D, E, A, B, C, WR42, SR42, KR2);
+        RIP3(C, D, E, A, B, WR43, SR43, KR2);
+        RIP3(B, C, D, E, A, WR44, SR44, KR2);
+        RIP3(A, B, C, D, E, WR45, SR45, KR2);
+        RIP3(E, A, B, C, D, WR46, SR46, KR2);
+        RIP3(D, E, A, B, C, WR47, SR47, KR2);
+
+        RIP2(C, D, E, A, B, WR48, SR48, KR3);
+        RIP2(B, C, D, E, A, WR49, SR49, KR3);
+        RIP2(A, B, C, D, E, WR50, SR50, KR3);
+        RIP2(E, A, B, C, D, WR51, SR51, KR3);
+        RIP2(D, E, A, B, C, WR52, SR52, KR3);
+        RIP2(C, D, E, A, B, WR53, SR53, KR3);
+        RIP2(B, C, D, E, A, WR54, SR54, KR3);
+        RIP2(A, B, C, D, E, WR55, SR55, KR3);
+        RIP2(E, A, B, C, D, WR56, SR56, KR3);
+        RIP2(D, E, A, B, C, WR57, SR57, KR3);
+        RIP2(C, D, E, A, B, WR58, SR58, KR3);
+        RIP2(B, C, D, E, A, WR59, SR59, KR3);
+        RIP2(A, B, C, D, E, WR60, SR60, KR3);
+        RIP2(E, A, B, C, D, WR61, SR61, KR3);
+        RIP2(D, E, A, B, C, WR62, SR62, KR3);
+        RIP2(C, D, E, A, B, WR63, SR63, KR3);
+
+        RIP1(B, C, D, E, A, WR64, SR64);
+        RIP1(A, B, C, D, E, WR65, SR65);
+        RIP1(E, A, B, C, D, WR66, SR66);
+        RIP1(D, E, A, B, C, WR67, SR67);
+        RIP1(C, D, E, A, B, WR68, SR68);
+        RIP1(B, C, D, E, A, WR69, SR69);
+        RIP1(A, B, C, D, E, WR70, SR70);
+        RIP1(E, A, B, C, D, WR71, SR71);
+        RIP1(D, E, A, B, C, WR72, SR72);
+        RIP1(C, D, E, A, B, WR73, SR73);
+        RIP1(B, C, D, E, A, WR74, SR74);
+        RIP1(A, B, C, D, E, WR75, SR75);
+        RIP1(E, A, B, C, D, WR76, SR76);
+        RIP1(D, E, A, B, C, WR77, SR77);
+        RIP1(C, D, E, A, B, WR78, SR78);
+        RIP1(B, C, D, E, A, WR79, SR79);
+
+        D = ctx->B + c + D;
+        ctx->B = ctx->C + d + E;
+        ctx->C = ctx->D + e + A;
+        ctx->D = ctx->E + a + B;
+        ctx->E = ctx->A + b + C;
+        ctx->A = D;
+
+    }
+}
+#endif
diff --git a/crypto/ripemd/rmd_locl.h b/crypto/ripemd/rmd_locl.h
new file mode 100644
index 000000000..9c5ba1513
--- /dev/null
+++ b/crypto/ripemd/rmd_locl.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include 
+#include 
+
+/*
+ * DO EXAMINE COMMENTS IN crypto/md5/md5_locl.h & crypto/md5/md5_dgst.c
+ * FOR EXPLANATIONS ON FOLLOWING "CODE."
+ *                                      
+ */
+#ifdef RMD160_ASM
+# if defined(__i386) || defined(__i386__) || defined(_M_IX86)
+#  define ripemd160_block_data_order ripemd160_block_asm_data_order
+# endif
+#endif
+
+void ripemd160_block_data_order(RIPEMD160_CTX *c, const void *p, size_t num);
+
+#define DATA_ORDER_IS_LITTLE_ENDIAN
+
+#define HASH_LONG               RIPEMD160_LONG
+#define HASH_CTX                RIPEMD160_CTX
+#define HASH_CBLOCK             RIPEMD160_CBLOCK
+#define HASH_UPDATE             RIPEMD160_Update
+#define HASH_TRANSFORM          RIPEMD160_Transform
+#define HASH_FINAL              RIPEMD160_Final
+#define HASH_MAKE_STRING(c,s)   do {    \
+        unsigned long ll;               \
+        ll=(c)->A; (void)HOST_l2c(ll,(s));      \
+        ll=(c)->B; (void)HOST_l2c(ll,(s));      \
+        ll=(c)->C; (void)HOST_l2c(ll,(s));      \
+        ll=(c)->D; (void)HOST_l2c(ll,(s));      \
+        ll=(c)->E; (void)HOST_l2c(ll,(s));      \
+        } while (0)
+#define HASH_BLOCK_DATA_ORDER   ripemd160_block_data_order
+
+#include "internal/md32_common.h"
+
+/*
+ * Transformed F2 and F4 are courtesy of Wei Dai 
+ */
+#define F1(x,y,z)       ((x) ^ (y) ^ (z))
+#define F2(x,y,z)       ((((y) ^ (z)) & (x)) ^ (z))
+#define F3(x,y,z)       (((~(y)) | (x)) ^ (z))
+#define F4(x,y,z)       ((((x) ^ (y)) & (z)) ^ (y))
+#define F5(x,y,z)       (((~(z)) | (y)) ^ (x))
+
+#define RIPEMD160_A     0x67452301L
+#define RIPEMD160_B     0xEFCDAB89L
+#define RIPEMD160_C     0x98BADCFEL
+#define RIPEMD160_D     0x10325476L
+#define RIPEMD160_E     0xC3D2E1F0L
+
+#include "rmdconst.h"
+
+#define RIP1(a,b,c,d,e,w,s) { \
+        a+=F1(b,c,d)+X(w); \
+        a=ROTATE(a,s)+e; \
+        c=ROTATE(c,10); }
+
+#define RIP2(a,b,c,d,e,w,s,K) { \
+        a+=F2(b,c,d)+X(w)+K; \
+        a=ROTATE(a,s)+e; \
+        c=ROTATE(c,10); }
+
+#define RIP3(a,b,c,d,e,w,s,K) { \
+        a+=F3(b,c,d)+X(w)+K; \
+        a=ROTATE(a,s)+e; \
+        c=ROTATE(c,10); }
+
+#define RIP4(a,b,c,d,e,w,s,K) { \
+        a+=F4(b,c,d)+X(w)+K; \
+        a=ROTATE(a,s)+e; \
+        c=ROTATE(c,10); }
+
+#define RIP5(a,b,c,d,e,w,s,K) { \
+        a+=F5(b,c,d)+X(w)+K; \
+        a=ROTATE(a,s)+e; \
+        c=ROTATE(c,10); }
diff --git a/crypto/ripemd/rmd_one.c b/crypto/ripemd/rmd_one.c
new file mode 100644
index 000000000..c3193bd72
--- /dev/null
+++ b/crypto/ripemd/rmd_one.c
@@ -0,0 +1,28 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include 
+#include 
+
+unsigned char *RIPEMD160(const unsigned char *d, size_t n, unsigned char *md)
+{
+    RIPEMD160_CTX c;
+    static unsigned char m[RIPEMD160_DIGEST_LENGTH];
+
+    if (md == NULL)
+        md = m;
+    if (!RIPEMD160_Init(&c))
+        return NULL;
+    RIPEMD160_Update(&c, d, n);
+    RIPEMD160_Final(md, &c);
+    OPENSSL_cleanse(&c, sizeof(c)); /* security consideration */
+    return (md);
+}
diff --git a/crypto/ripemd/rmdconst.h b/crypto/ripemd/rmdconst.h
new file mode 100644
index 000000000..b81013239
--- /dev/null
+++ b/crypto/ripemd/rmdconst.h
@@ -0,0 +1,350 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#define KL0 0x00000000L
+#define KL1 0x5A827999L
+#define KL2 0x6ED9EBA1L
+#define KL3 0x8F1BBCDCL
+#define KL4 0xA953FD4EL
+
+#define KR0 0x50A28BE6L
+#define KR1 0x5C4DD124L
+#define KR2 0x6D703EF3L
+#define KR3 0x7A6D76E9L
+#define KR4 0x00000000L
+
+#define WL00  0
+#define SL00 11
+#define WL01  1
+#define SL01 14
+#define WL02  2
+#define SL02 15
+#define WL03  3
+#define SL03 12
+#define WL04  4
+#define SL04  5
+#define WL05  5
+#define SL05  8
+#define WL06  6
+#define SL06  7
+#define WL07  7
+#define SL07  9
+#define WL08  8
+#define SL08 11
+#define WL09  9
+#define SL09 13
+#define WL10 10
+#define SL10 14
+#define WL11 11
+#define SL11 15
+#define WL12 12
+#define SL12  6
+#define WL13 13
+#define SL13  7
+#define WL14 14
+#define SL14  9
+#define WL15 15
+#define SL15  8
+
+#define WL16  7
+#define SL16  7
+#define WL17  4
+#define SL17  6
+#define WL18 13
+#define SL18  8
+#define WL19  1
+#define SL19 13
+#define WL20 10
+#define SL20 11
+#define WL21  6
+#define SL21  9
+#define WL22 15
+#define SL22  7
+#define WL23  3
+#define SL23 15
+#define WL24 12
+#define SL24  7
+#define WL25  0
+#define SL25 12
+#define WL26  9
+#define SL26 15
+#define WL27  5
+#define SL27  9
+#define WL28  2
+#define SL28 11
+#define WL29 14
+#define SL29  7
+#define WL30 11
+#define SL30 13
+#define WL31  8
+#define SL31 12
+
+#define WL32  3
+#define SL32 11
+#define WL33 10
+#define SL33 13
+#define WL34 14
+#define SL34  6
+#define WL35  4
+#define SL35  7
+#define WL36  9
+#define SL36 14
+#define WL37 15
+#define SL37  9
+#define WL38  8
+#define SL38 13
+#define WL39  1
+#define SL39 15
+#define WL40  2
+#define SL40 14
+#define WL41  7
+#define SL41  8
+#define WL42  0
+#define SL42 13
+#define WL43  6
+#define SL43  6
+#define WL44 13
+#define SL44  5
+#define WL45 11
+#define SL45 12
+#define WL46  5
+#define SL46  7
+#define WL47 12
+#define SL47  5
+
+#define WL48  1
+#define SL48 11
+#define WL49  9
+#define SL49 12
+#define WL50 11
+#define SL50 14
+#define WL51 10
+#define SL51 15
+#define WL52  0
+#define SL52 14
+#define WL53  8
+#define SL53 15
+#define WL54 12
+#define SL54  9
+#define WL55  4
+#define SL55  8
+#define WL56 13
+#define SL56  9
+#define WL57  3
+#define SL57 14
+#define WL58  7
+#define SL58  5
+#define WL59 15
+#define SL59  6
+#define WL60 14
+#define SL60  8
+#define WL61  5
+#define SL61  6
+#define WL62  6
+#define SL62  5
+#define WL63  2
+#define SL63 12
+
+#define WL64  4
+#define SL64  9
+#define WL65  0
+#define SL65 15
+#define WL66  5
+#define SL66  5
+#define WL67  9
+#define SL67 11
+#define WL68  7
+#define SL68  6
+#define WL69 12
+#define SL69  8
+#define WL70  2
+#define SL70 13
+#define WL71 10
+#define SL71 12
+#define WL72 14
+#define SL72  5
+#define WL73  1
+#define SL73 12
+#define WL74  3
+#define SL74 13
+#define WL75  8
+#define SL75 14
+#define WL76 11
+#define SL76 11
+#define WL77  6
+#define SL77  8
+#define WL78 15
+#define SL78  5
+#define WL79 13
+#define SL79  6
+
+#define WR00  5
+#define SR00  8
+#define WR01 14
+#define SR01  9
+#define WR02  7
+#define SR02  9
+#define WR03  0
+#define SR03 11
+#define WR04  9
+#define SR04 13
+#define WR05  2
+#define SR05 15
+#define WR06 11
+#define SR06 15
+#define WR07  4
+#define SR07  5
+#define WR08 13
+#define SR08  7
+#define WR09  6
+#define SR09  7
+#define WR10 15
+#define SR10  8
+#define WR11  8
+#define SR11 11
+#define WR12  1
+#define SR12 14
+#define WR13 10
+#define SR13 14
+#define WR14  3
+#define SR14 12
+#define WR15 12
+#define SR15  6
+
+#define WR16  6
+#define SR16  9
+#define WR17 11
+#define SR17 13
+#define WR18  3
+#define SR18 15
+#define WR19  7
+#define SR19  7
+#define WR20  0
+#define SR20 12
+#define WR21 13
+#define SR21  8
+#define WR22  5
+#define SR22  9
+#define WR23 10
+#define SR23 11
+#define WR24 14
+#define SR24  7
+#define WR25 15
+#define SR25  7
+#define WR26  8
+#define SR26 12
+#define WR27 12
+#define SR27  7
+#define WR28  4
+#define SR28  6
+#define WR29  9
+#define SR29 15
+#define WR30  1
+#define SR30 13
+#define WR31  2
+#define SR31 11
+
+#define WR32 15
+#define SR32  9
+#define WR33  5
+#define SR33  7
+#define WR34  1
+#define SR34 15
+#define WR35  3
+#define SR35 11
+#define WR36  7
+#define SR36  8
+#define WR37 14
+#define SR37  6
+#define WR38  6
+#define SR38  6
+#define WR39  9
+#define SR39 14
+#define WR40 11
+#define SR40 12
+#define WR41  8
+#define SR41 13
+#define WR42 12
+#define SR42  5
+#define WR43  2
+#define SR43 14
+#define WR44 10
+#define SR44 13
+#define WR45  0
+#define SR45 13
+#define WR46  4
+#define SR46  7
+#define WR47 13
+#define SR47  5
+
+#define WR48  8
+#define SR48 15
+#define WR49  6
+#define SR49  5
+#define WR50  4
+#define SR50  8
+#define WR51  1
+#define SR51 11
+#define WR52  3
+#define SR52 14
+#define WR53 11
+#define SR53 14
+#define WR54 15
+#define SR54  6
+#define WR55  0
+#define SR55 14
+#define WR56  5
+#define SR56  6
+#define WR57 12
+#define SR57  9
+#define WR58  2
+#define SR58 12
+#define WR59 13
+#define SR59  9
+#define WR60  9
+#define SR60 12
+#define WR61  7
+#define SR61  5
+#define WR62 10
+#define SR62 15
+#define WR63 14
+#define SR63  8
+
+#define WR64 12
+#define SR64  8
+#define WR65 15
+#define SR65  5
+#define WR66 10
+#define SR66 12
+#define WR67  4
+#define SR67  9
+#define WR68  1
+#define SR68 12
+#define WR69  5
+#define SR69  5
+#define WR70  8
+#define SR70 14
+#define WR71  7
+#define SR71  6
+#define WR72  6
+#define SR72  8
+#define WR73  2
+#define SR73 13
+#define WR74 13
+#define SR74  6
+#define WR75 14
+#define SR75  5
+#define WR76  0
+#define SR76 15
+#define WR77  3
+#define SR77 13
+#define WR78  9
+#define SR78 11
+#define WR79 11
+#define SR79 11
diff --git a/crypto/rsa/build.info b/crypto/rsa/build.info
new file mode 100644
index 000000000..39b7464b0
--- /dev/null
+++ b/crypto/rsa/build.info
@@ -0,0 +1,6 @@
+LIBS=../../libcrypto
+SOURCE[../../libcrypto]=\
+        rsa_ossl.c rsa_gen.c rsa_lib.c rsa_sign.c rsa_saos.c rsa_err.c \
+        rsa_pk1.c rsa_ssl.c rsa_none.c rsa_oaep.c rsa_chk.c rsa_null.c \
+        rsa_pss.c rsa_x931.c rsa_asn1.c rsa_depr.c rsa_ameth.c rsa_prn.c \
+        rsa_pmeth.c rsa_crpt.c rsa_x931g.c rsa_meth.c
diff --git a/crypto/rsa/rsa_ameth.c b/crypto/rsa/rsa_ameth.c
new file mode 100644
index 000000000..5694140af
--- /dev/null
+++ b/crypto/rsa/rsa_ameth.c
@@ -0,0 +1,866 @@
+/*
+ * Copyright 2006-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+#include 
+#include 
+#include "internal/asn1_int.h"
+#include "internal/evp_int.h"
+#include "rsa_locl.h"
+
+#ifndef OPENSSL_NO_CMS
+static int rsa_cms_sign(CMS_SignerInfo *si);
+static int rsa_cms_verify(CMS_SignerInfo *si);
+static int rsa_cms_decrypt(CMS_RecipientInfo *ri);
+static int rsa_cms_encrypt(CMS_RecipientInfo *ri);
+#endif
+
+static int rsa_pub_encode(X509_PUBKEY *pk, const EVP_PKEY *pkey)
+{
+    unsigned char *penc = NULL;
+    int penclen;
+    penclen = i2d_RSAPublicKey(pkey->pkey.rsa, &penc);
+    if (penclen <= 0)
+        return 0;
+    if (X509_PUBKEY_set0_param(pk, OBJ_nid2obj(EVP_PKEY_RSA),
+                               V_ASN1_NULL, NULL, penc, penclen))
+        return 1;
+
+    OPENSSL_free(penc);
+    return 0;
+}
+
+static int rsa_pub_decode(EVP_PKEY *pkey, X509_PUBKEY *pubkey)
+{
+    const unsigned char *p;
+    int pklen;
+    RSA *rsa = NULL;
+
+    if (!X509_PUBKEY_get0_param(NULL, &p, &pklen, NULL, pubkey))
+        return 0;
+    if ((rsa = d2i_RSAPublicKey(NULL, &p, pklen)) == NULL) {
+        RSAerr(RSA_F_RSA_PUB_DECODE, ERR_R_RSA_LIB);
+        return 0;
+    }
+    EVP_PKEY_assign_RSA(pkey, rsa);
+    return 1;
+}
+
+static int rsa_pub_cmp(const EVP_PKEY *a, const EVP_PKEY *b)
+{
+    if (BN_cmp(b->pkey.rsa->n, a->pkey.rsa->n) != 0
+        || BN_cmp(b->pkey.rsa->e, a->pkey.rsa->e) != 0)
+        return 0;
+    return 1;
+}
+
+static int old_rsa_priv_decode(EVP_PKEY *pkey,
+                               const unsigned char **pder, int derlen)
+{
+    RSA *rsa;
+
+    if ((rsa = d2i_RSAPrivateKey(NULL, pder, derlen)) == NULL) {
+        RSAerr(RSA_F_OLD_RSA_PRIV_DECODE, ERR_R_RSA_LIB);
+        return 0;
+    }
+    EVP_PKEY_assign_RSA(pkey, rsa);
+    return 1;
+}
+
+static int old_rsa_priv_encode(const EVP_PKEY *pkey, unsigned char **pder)
+{
+    return i2d_RSAPrivateKey(pkey->pkey.rsa, pder);
+}
+
+static int rsa_priv_encode(PKCS8_PRIV_KEY_INFO *p8, const EVP_PKEY *pkey)
+{
+    unsigned char *rk = NULL;
+    int rklen;
+    rklen = i2d_RSAPrivateKey(pkey->pkey.rsa, &rk);
+
+    if (rklen <= 0) {
+        RSAerr(RSA_F_RSA_PRIV_ENCODE, ERR_R_MALLOC_FAILURE);
+        return 0;
+    }
+
+    if (!PKCS8_pkey_set0(p8, OBJ_nid2obj(NID_rsaEncryption), 0,
+                         V_ASN1_NULL, NULL, rk, rklen)) {
+        RSAerr(RSA_F_RSA_PRIV_ENCODE, ERR_R_MALLOC_FAILURE);
+        return 0;
+    }
+
+    return 1;
+}
+
+static int rsa_priv_decode(EVP_PKEY *pkey, const PKCS8_PRIV_KEY_INFO *p8)
+{
+    const unsigned char *p;
+    int pklen;
+    if (!PKCS8_pkey_get0(NULL, &p, &pklen, NULL, p8))
+        return 0;
+    return old_rsa_priv_decode(pkey, &p, pklen);
+}
+
+static int int_rsa_size(const EVP_PKEY *pkey)
+{
+    return RSA_size(pkey->pkey.rsa);
+}
+
+static int rsa_bits(const EVP_PKEY *pkey)
+{
+    return BN_num_bits(pkey->pkey.rsa->n);
+}
+
+static int rsa_security_bits(const EVP_PKEY *pkey)
+{
+    return RSA_security_bits(pkey->pkey.rsa);
+}
+
+static void int_rsa_free(EVP_PKEY *pkey)
+{
+    RSA_free(pkey->pkey.rsa);
+}
+
+static int do_rsa_print(BIO *bp, const RSA *x, int off, int priv)
+{
+    char *str;
+    const char *s;
+    int ret = 0, mod_len = 0;
+
+    if (x->n != NULL)
+        mod_len = BN_num_bits(x->n);
+
+    if (!BIO_indent(bp, off, 128))
+        goto err;
+
+    if (priv && x->d) {
+        if (BIO_printf(bp, "Private-Key: (%d bit)\n", mod_len) <= 0)
+            goto err;
+        str = "modulus:";
+        s = "publicExponent:";
+    } else {
+        if (BIO_printf(bp, "Public-Key: (%d bit)\n", mod_len) <= 0)
+            goto err;
+        str = "Modulus:";
+        s = "Exponent:";
+    }
+    if (!ASN1_bn_print(bp, str, x->n, NULL, off))
+        goto err;
+    if (!ASN1_bn_print(bp, s, x->e, NULL, off))
+        goto err;
+    if (priv) {
+        if (!ASN1_bn_print(bp, "privateExponent:", x->d, NULL, off))
+            goto err;
+        if (!ASN1_bn_print(bp, "prime1:", x->p, NULL, off))
+            goto err;
+        if (!ASN1_bn_print(bp, "prime2:", x->q, NULL, off))
+            goto err;
+        if (!ASN1_bn_print(bp, "exponent1:", x->dmp1, NULL, off))
+            goto err;
+        if (!ASN1_bn_print(bp, "exponent2:", x->dmq1, NULL, off))
+            goto err;
+        if (!ASN1_bn_print(bp, "coefficient:", x->iqmp, NULL, off))
+            goto err;
+    }
+    ret = 1;
+ err:
+    return (ret);
+}
+
+static int rsa_pub_print(BIO *bp, const EVP_PKEY *pkey, int indent,
+                         ASN1_PCTX *ctx)
+{
+    return do_rsa_print(bp, pkey->pkey.rsa, indent, 0);
+}
+
+static int rsa_priv_print(BIO *bp, const EVP_PKEY *pkey, int indent,
+                          ASN1_PCTX *ctx)
+{
+    return do_rsa_print(bp, pkey->pkey.rsa, indent, 1);
+}
+
+/* Given an MGF1 Algorithm ID decode to an Algorithm Identifier */
+static X509_ALGOR *rsa_mgf1_decode(X509_ALGOR *alg)
+{
+    if (alg == NULL)
+        return NULL;
+    if (OBJ_obj2nid(alg->algorithm) != NID_mgf1)
+        return NULL;
+    return ASN1_TYPE_unpack_sequence(ASN1_ITEM_rptr(X509_ALGOR),
+                                     alg->parameter);
+}
+
+static RSA_PSS_PARAMS *rsa_pss_decode(const X509_ALGOR *alg,
+                                      X509_ALGOR **pmaskHash)
+{
+    RSA_PSS_PARAMS *pss;
+
+    *pmaskHash = NULL;
+
+    pss = ASN1_TYPE_unpack_sequence(ASN1_ITEM_rptr(RSA_PSS_PARAMS),
+                                    alg->parameter);
+
+    if (!pss)
+        return NULL;
+
+    *pmaskHash = rsa_mgf1_decode(pss->maskGenAlgorithm);
+
+    return pss;
+}
+
+static int rsa_pss_param_print(BIO *bp, RSA_PSS_PARAMS *pss,
+                               X509_ALGOR *maskHash, int indent)
+{
+    int rv = 0;
+    if (!pss) {
+        if (BIO_puts(bp, " (INVALID PSS PARAMETERS)\n") <= 0)
+            return 0;
+        return 1;
+    }
+    if (BIO_puts(bp, "\n") <= 0)
+        goto err;
+    if (!BIO_indent(bp, indent, 128))
+        goto err;
+    if (BIO_puts(bp, "Hash Algorithm: ") <= 0)
+        goto err;
+
+    if (pss->hashAlgorithm) {
+        if (i2a_ASN1_OBJECT(bp, pss->hashAlgorithm->algorithm) <= 0)
+            goto err;
+    } else if (BIO_puts(bp, "sha1 (default)") <= 0)
+        goto err;
+
+    if (BIO_puts(bp, "\n") <= 0)
+        goto err;
+
+    if (!BIO_indent(bp, indent, 128))
+        goto err;
+
+    if (BIO_puts(bp, "Mask Algorithm: ") <= 0)
+        goto err;
+    if (pss->maskGenAlgorithm) {
+        if (i2a_ASN1_OBJECT(bp, pss->maskGenAlgorithm->algorithm) <= 0)
+            goto err;
+        if (BIO_puts(bp, " with ") <= 0)
+            goto err;
+        if (maskHash) {
+            if (i2a_ASN1_OBJECT(bp, maskHash->algorithm) <= 0)
+                goto err;
+        } else if (BIO_puts(bp, "INVALID") <= 0)
+            goto err;
+    } else if (BIO_puts(bp, "mgf1 with sha1 (default)") <= 0)
+        goto err;
+    BIO_puts(bp, "\n");
+
+    if (!BIO_indent(bp, indent, 128))
+        goto err;
+    if (BIO_puts(bp, "Salt Length: 0x") <= 0)
+        goto err;
+    if (pss->saltLength) {
+        if (i2a_ASN1_INTEGER(bp, pss->saltLength) <= 0)
+            goto err;
+    } else if (BIO_puts(bp, "14 (default)") <= 0)
+        goto err;
+    BIO_puts(bp, "\n");
+
+    if (!BIO_indent(bp, indent, 128))
+        goto err;
+    if (BIO_puts(bp, "Trailer Field: 0x") <= 0)
+        goto err;
+    if (pss->trailerField) {
+        if (i2a_ASN1_INTEGER(bp, pss->trailerField) <= 0)
+            goto err;
+    } else if (BIO_puts(bp, "BC (default)") <= 0)
+        goto err;
+    BIO_puts(bp, "\n");
+
+    rv = 1;
+
+ err:
+    return rv;
+
+}
+
+static int rsa_sig_print(BIO *bp, const X509_ALGOR *sigalg,
+                         const ASN1_STRING *sig, int indent, ASN1_PCTX *pctx)
+{
+    if (OBJ_obj2nid(sigalg->algorithm) == NID_rsassaPss) {
+        int rv;
+        RSA_PSS_PARAMS *pss;
+        X509_ALGOR *maskHash;
+        pss = rsa_pss_decode(sigalg, &maskHash);
+        rv = rsa_pss_param_print(bp, pss, maskHash, indent);
+        RSA_PSS_PARAMS_free(pss);
+        X509_ALGOR_free(maskHash);
+        if (!rv)
+            return 0;
+    } else if (!sig && BIO_puts(bp, "\n") <= 0)
+        return 0;
+    if (sig)
+        return X509_signature_dump(bp, sig, indent);
+    return 1;
+}
+
+static int rsa_pkey_ctrl(EVP_PKEY *pkey, int op, long arg1, void *arg2)
+{
+    X509_ALGOR *alg = NULL;
+    switch (op) {
+
+    case ASN1_PKEY_CTRL_PKCS7_SIGN:
+        if (arg1 == 0)
+            PKCS7_SIGNER_INFO_get0_algs(arg2, NULL, NULL, &alg);
+        break;
+
+    case ASN1_PKEY_CTRL_PKCS7_ENCRYPT:
+        if (arg1 == 0)
+            PKCS7_RECIP_INFO_get0_alg(arg2, &alg);
+        break;
+#ifndef OPENSSL_NO_CMS
+    case ASN1_PKEY_CTRL_CMS_SIGN:
+        if (arg1 == 0)
+            return rsa_cms_sign(arg2);
+        else if (arg1 == 1)
+            return rsa_cms_verify(arg2);
+        break;
+
+    case ASN1_PKEY_CTRL_CMS_ENVELOPE:
+        if (arg1 == 0)
+            return rsa_cms_encrypt(arg2);
+        else if (arg1 == 1)
+            return rsa_cms_decrypt(arg2);
+        break;
+
+    case ASN1_PKEY_CTRL_CMS_RI_TYPE:
+        *(int *)arg2 = CMS_RECIPINFO_TRANS;
+        return 1;
+#endif
+
+    case ASN1_PKEY_CTRL_DEFAULT_MD_NID:
+        *(int *)arg2 = NID_sha256;
+        return 1;
+
+    default:
+        return -2;
+
+    }
+
+    if (alg)
+        X509_ALGOR_set0(alg, OBJ_nid2obj(NID_rsaEncryption), V_ASN1_NULL, 0);
+
+    return 1;
+
+}
+
+/* allocate and set algorithm ID from EVP_MD, default SHA1 */
+static int rsa_md_to_algor(X509_ALGOR **palg, const EVP_MD *md)
+{
+    if (EVP_MD_type(md) == NID_sha1)
+        return 1;
+    *palg = X509_ALGOR_new();
+    if (*palg == NULL)
+        return 0;
+    X509_ALGOR_set_md(*palg, md);
+    return 1;
+}
+
+/* Allocate and set MGF1 algorithm ID from EVP_MD */
+static int rsa_md_to_mgf1(X509_ALGOR **palg, const EVP_MD *mgf1md)
+{
+    X509_ALGOR *algtmp = NULL;
+    ASN1_STRING *stmp = NULL;
+    *palg = NULL;
+    if (EVP_MD_type(mgf1md) == NID_sha1)
+        return 1;
+    /* need to embed algorithm ID inside another */
+    if (!rsa_md_to_algor(&algtmp, mgf1md))
+        goto err;
+    if (!ASN1_item_pack(algtmp, ASN1_ITEM_rptr(X509_ALGOR), &stmp))
+         goto err;
+    *palg = X509_ALGOR_new();
+    if (*palg == NULL)
+        goto err;
+    X509_ALGOR_set0(*palg, OBJ_nid2obj(NID_mgf1), V_ASN1_SEQUENCE, stmp);
+    stmp = NULL;
+ err:
+    ASN1_STRING_free(stmp);
+    X509_ALGOR_free(algtmp);
+    if (*palg)
+        return 1;
+    return 0;
+}
+
+/* convert algorithm ID to EVP_MD, default SHA1 */
+static const EVP_MD *rsa_algor_to_md(X509_ALGOR *alg)
+{
+    const EVP_MD *md;
+    if (!alg)
+        return EVP_sha1();
+    md = EVP_get_digestbyobj(alg->algorithm);
+    if (md == NULL)
+        RSAerr(RSA_F_RSA_ALGOR_TO_MD, RSA_R_UNKNOWN_DIGEST);
+    return md;
+}
+
+/* convert MGF1 algorithm ID to EVP_MD, default SHA1 */
+static const EVP_MD *rsa_mgf1_to_md(X509_ALGOR *alg, X509_ALGOR *maskHash)
+{
+    const EVP_MD *md;
+    if (!alg)
+        return EVP_sha1();
+    /* Check mask and lookup mask hash algorithm */
+    if (OBJ_obj2nid(alg->algorithm) != NID_mgf1) {
+        RSAerr(RSA_F_RSA_MGF1_TO_MD, RSA_R_UNSUPPORTED_MASK_ALGORITHM);
+        return NULL;
+    }
+    if (!maskHash) {
+        RSAerr(RSA_F_RSA_MGF1_TO_MD, RSA_R_UNSUPPORTED_MASK_PARAMETER);
+        return NULL;
+    }
+    md = EVP_get_digestbyobj(maskHash->algorithm);
+    if (md == NULL) {
+        RSAerr(RSA_F_RSA_MGF1_TO_MD, RSA_R_UNKNOWN_MASK_DIGEST);
+        return NULL;
+    }
+    return md;
+}
+
+/*
+ * Convert EVP_PKEY_CTX is PSS mode into corresponding algorithm parameter,
+ * suitable for setting an AlgorithmIdentifier.
+ */
+
+static ASN1_STRING *rsa_ctx_to_pss(EVP_PKEY_CTX *pkctx)
+{
+    const EVP_MD *sigmd, *mgf1md;
+    RSA_PSS_PARAMS *pss = NULL;
+    ASN1_STRING *os = NULL;
+    EVP_PKEY *pk = EVP_PKEY_CTX_get0_pkey(pkctx);
+    int saltlen, rv = 0;
+    if (EVP_PKEY_CTX_get_signature_md(pkctx, &sigmd) <= 0)
+        goto err;
+    if (EVP_PKEY_CTX_get_rsa_mgf1_md(pkctx, &mgf1md) <= 0)
+        goto err;
+    if (!EVP_PKEY_CTX_get_rsa_pss_saltlen(pkctx, &saltlen))
+        goto err;
+    if (saltlen == -1)
+        saltlen = EVP_MD_size(sigmd);
+    else if (saltlen == -2) {
+        saltlen = EVP_PKEY_size(pk) - EVP_MD_size(sigmd) - 2;
+        if (((EVP_PKEY_bits(pk) - 1) & 0x7) == 0)
+            saltlen--;
+    }
+    pss = RSA_PSS_PARAMS_new();
+    if (pss == NULL)
+        goto err;
+    if (saltlen != 20) {
+        pss->saltLength = ASN1_INTEGER_new();
+        if (pss->saltLength == NULL)
+            goto err;
+        if (!ASN1_INTEGER_set(pss->saltLength, saltlen))
+            goto err;
+    }
+    if (!rsa_md_to_algor(&pss->hashAlgorithm, sigmd))
+        goto err;
+    if (!rsa_md_to_mgf1(&pss->maskGenAlgorithm, mgf1md))
+        goto err;
+    /* Finally create string with pss parameter encoding. */
+    if (!ASN1_item_pack(pss, ASN1_ITEM_rptr(RSA_PSS_PARAMS), &os))
+         goto err;
+    rv = 1;
+ err:
+    RSA_PSS_PARAMS_free(pss);
+    if (rv)
+        return os;
+    ASN1_STRING_free(os);
+    return NULL;
+}
+
+/*
+ * From PSS AlgorithmIdentifier set public key parameters. If pkey isn't NULL
+ * then the EVP_MD_CTX is setup and initialised. If it is NULL parameters are
+ * passed to pkctx instead.
+ */
+
+static int rsa_pss_to_ctx(EVP_MD_CTX *ctx, EVP_PKEY_CTX *pkctx,
+                          X509_ALGOR *sigalg, EVP_PKEY *pkey)
+{
+    int rv = -1;
+    int saltlen;
+    const EVP_MD *mgf1md = NULL, *md = NULL;
+    RSA_PSS_PARAMS *pss;
+    X509_ALGOR *maskHash;
+    /* Sanity check: make sure it is PSS */
+    if (OBJ_obj2nid(sigalg->algorithm) != NID_rsassaPss) {
+        RSAerr(RSA_F_RSA_PSS_TO_CTX, RSA_R_UNSUPPORTED_SIGNATURE_TYPE);
+        return -1;
+    }
+    /* Decode PSS parameters */
+    pss = rsa_pss_decode(sigalg, &maskHash);
+
+    if (pss == NULL) {
+        RSAerr(RSA_F_RSA_PSS_TO_CTX, RSA_R_INVALID_PSS_PARAMETERS);
+        goto err;
+    }
+    mgf1md = rsa_mgf1_to_md(pss->maskGenAlgorithm, maskHash);
+    if (!mgf1md)
+        goto err;
+    md = rsa_algor_to_md(pss->hashAlgorithm);
+    if (!md)
+        goto err;
+
+    if (pss->saltLength) {
+        saltlen = ASN1_INTEGER_get(pss->saltLength);
+
+        /*
+         * Could perform more salt length sanity checks but the main RSA
+         * routines will trap other invalid values anyway.
+         */
+        if (saltlen < 0) {
+            RSAerr(RSA_F_RSA_PSS_TO_CTX, RSA_R_INVALID_SALT_LENGTH);
+            goto err;
+        }
+    } else
+        saltlen = 20;
+
+    /*
+     * low-level routines support only trailer field 0xbc (value 1) and
+     * PKCS#1 says we should reject any other value anyway.
+     */
+    if (pss->trailerField && ASN1_INTEGER_get(pss->trailerField) != 1) {
+        RSAerr(RSA_F_RSA_PSS_TO_CTX, RSA_R_INVALID_TRAILER);
+        goto err;
+    }
+
+    /* We have all parameters now set up context */
+
+    if (pkey) {
+        if (!EVP_DigestVerifyInit(ctx, &pkctx, md, NULL, pkey))
+            goto err;
+    } else {
+        const EVP_MD *checkmd;
+        if (EVP_PKEY_CTX_get_signature_md(pkctx, &checkmd) <= 0)
+            goto err;
+        if (EVP_MD_type(md) != EVP_MD_type(checkmd)) {
+            RSAerr(RSA_F_RSA_PSS_TO_CTX, RSA_R_DIGEST_DOES_NOT_MATCH);
+            goto err;
+        }
+    }
+
+    if (EVP_PKEY_CTX_set_rsa_padding(pkctx, RSA_PKCS1_PSS_PADDING) <= 0)
+        goto err;
+
+    if (EVP_PKEY_CTX_set_rsa_pss_saltlen(pkctx, saltlen) <= 0)
+        goto err;
+
+    if (EVP_PKEY_CTX_set_rsa_mgf1_md(pkctx, mgf1md) <= 0)
+        goto err;
+    /* Carry on */
+    rv = 1;
+
+ err:
+    RSA_PSS_PARAMS_free(pss);
+    X509_ALGOR_free(maskHash);
+    return rv;
+}
+
+#ifndef OPENSSL_NO_CMS
+static int rsa_cms_verify(CMS_SignerInfo *si)
+{
+    int nid, nid2;
+    X509_ALGOR *alg;
+    EVP_PKEY_CTX *pkctx = CMS_SignerInfo_get0_pkey_ctx(si);
+    CMS_SignerInfo_get0_algs(si, NULL, NULL, NULL, &alg);
+    nid = OBJ_obj2nid(alg->algorithm);
+    if (nid == NID_rsaEncryption)
+        return 1;
+    if (nid == NID_rsassaPss)
+        return rsa_pss_to_ctx(NULL, pkctx, alg, NULL);
+    /* Workaround for some implementation that use a signature OID */
+    if (OBJ_find_sigid_algs(nid, NULL, &nid2)) {
+        if (nid2 == NID_rsaEncryption)
+            return 1;
+    }
+    return 0;
+}
+#endif
+
+/*
+ * Customised RSA item verification routine. This is called when a signature
+ * is encountered requiring special handling. We currently only handle PSS.
+ */
+
+static int rsa_item_verify(EVP_MD_CTX *ctx, const ASN1_ITEM *it, void *asn,
+                           X509_ALGOR *sigalg, ASN1_BIT_STRING *sig,
+                           EVP_PKEY *pkey)
+{
+    /* Sanity check: make sure it is PSS */
+    if (OBJ_obj2nid(sigalg->algorithm) != NID_rsassaPss) {
+        RSAerr(RSA_F_RSA_ITEM_VERIFY, RSA_R_UNSUPPORTED_SIGNATURE_TYPE);
+        return -1;
+    }
+    if (rsa_pss_to_ctx(ctx, NULL, sigalg, pkey) > 0) {
+        /* Carry on */
+        return 2;
+    }
+    return -1;
+}
+
+#ifndef OPENSSL_NO_CMS
+static int rsa_cms_sign(CMS_SignerInfo *si)
+{
+    int pad_mode = RSA_PKCS1_PADDING;
+    X509_ALGOR *alg;
+    EVP_PKEY_CTX *pkctx = CMS_SignerInfo_get0_pkey_ctx(si);
+    ASN1_STRING *os = NULL;
+    CMS_SignerInfo_get0_algs(si, NULL, NULL, NULL, &alg);
+    if (pkctx) {
+        if (EVP_PKEY_CTX_get_rsa_padding(pkctx, &pad_mode) <= 0)
+            return 0;
+    }
+    if (pad_mode == RSA_PKCS1_PADDING) {
+        X509_ALGOR_set0(alg, OBJ_nid2obj(NID_rsaEncryption), V_ASN1_NULL, 0);
+        return 1;
+    }
+    /* We don't support it */
+    if (pad_mode != RSA_PKCS1_PSS_PADDING)
+        return 0;
+    os = rsa_ctx_to_pss(pkctx);
+    if (!os)
+        return 0;
+    X509_ALGOR_set0(alg, OBJ_nid2obj(NID_rsassaPss), V_ASN1_SEQUENCE, os);
+    return 1;
+}
+#endif
+
+static int rsa_item_sign(EVP_MD_CTX *ctx, const ASN1_ITEM *it, void *asn,
+                         X509_ALGOR *alg1, X509_ALGOR *alg2,
+                         ASN1_BIT_STRING *sig)
+{
+    int pad_mode;
+    EVP_PKEY_CTX *pkctx = EVP_MD_CTX_pkey_ctx(ctx);
+    if (EVP_PKEY_CTX_get_rsa_padding(pkctx, &pad_mode) <= 0)
+        return 0;
+    if (pad_mode == RSA_PKCS1_PADDING)
+        return 2;
+    if (pad_mode == RSA_PKCS1_PSS_PADDING) {
+        ASN1_STRING *os1 = NULL;
+        os1 = rsa_ctx_to_pss(pkctx);
+        if (!os1)
+            return 0;
+        /* Duplicate parameters if we have to */
+        if (alg2) {
+            ASN1_STRING *os2 = ASN1_STRING_dup(os1);
+            if (!os2) {
+                ASN1_STRING_free(os1);
+                return 0;
+            }
+            X509_ALGOR_set0(alg2, OBJ_nid2obj(NID_rsassaPss),
+                            V_ASN1_SEQUENCE, os2);
+        }
+        X509_ALGOR_set0(alg1, OBJ_nid2obj(NID_rsassaPss),
+                        V_ASN1_SEQUENCE, os1);
+        return 3;
+    }
+    return 2;
+}
+
+#ifndef OPENSSL_NO_CMS
+static RSA_OAEP_PARAMS *rsa_oaep_decode(const X509_ALGOR *alg,
+                                        X509_ALGOR **pmaskHash)
+{
+    RSA_OAEP_PARAMS *pss;
+
+    *pmaskHash = NULL;
+
+    pss = ASN1_TYPE_unpack_sequence(ASN1_ITEM_rptr(RSA_OAEP_PARAMS),
+                                    alg->parameter);
+
+    if (!pss)
+        return NULL;
+
+    *pmaskHash = rsa_mgf1_decode(pss->maskGenFunc);
+
+    return pss;
+}
+
+static int rsa_cms_decrypt(CMS_RecipientInfo *ri)
+{
+    EVP_PKEY_CTX *pkctx;
+    X509_ALGOR *cmsalg;
+    int nid;
+    int rv = -1;
+    unsigned char *label = NULL;
+    int labellen = 0;
+    const EVP_MD *mgf1md = NULL, *md = NULL;
+    RSA_OAEP_PARAMS *oaep;
+    X509_ALGOR *maskHash;
+    pkctx = CMS_RecipientInfo_get0_pkey_ctx(ri);
+    if (!pkctx)
+        return 0;
+    if (!CMS_RecipientInfo_ktri_get0_algs(ri, NULL, NULL, &cmsalg))
+        return -1;
+    nid = OBJ_obj2nid(cmsalg->algorithm);
+    if (nid == NID_rsaEncryption)
+        return 1;
+    if (nid != NID_rsaesOaep) {
+        RSAerr(RSA_F_RSA_CMS_DECRYPT, RSA_R_UNSUPPORTED_ENCRYPTION_TYPE);
+        return -1;
+    }
+    /* Decode OAEP parameters */
+    oaep = rsa_oaep_decode(cmsalg, &maskHash);
+
+    if (oaep == NULL) {
+        RSAerr(RSA_F_RSA_CMS_DECRYPT, RSA_R_INVALID_OAEP_PARAMETERS);
+        goto err;
+    }
+
+    mgf1md = rsa_mgf1_to_md(oaep->maskGenFunc, maskHash);
+    if (!mgf1md)
+        goto err;
+    md = rsa_algor_to_md(oaep->hashFunc);
+    if (!md)
+        goto err;
+
+    if (oaep->pSourceFunc) {
+        X509_ALGOR *plab = oaep->pSourceFunc;
+        if (OBJ_obj2nid(plab->algorithm) != NID_pSpecified) {
+            RSAerr(RSA_F_RSA_CMS_DECRYPT, RSA_R_UNSUPPORTED_LABEL_SOURCE);
+            goto err;
+        }
+        if (plab->parameter->type != V_ASN1_OCTET_STRING) {
+            RSAerr(RSA_F_RSA_CMS_DECRYPT, RSA_R_INVALID_LABEL);
+            goto err;
+        }
+
+        label = plab->parameter->value.octet_string->data;
+        /* Stop label being freed when OAEP parameters are freed */
+        plab->parameter->value.octet_string->data = NULL;
+        labellen = plab->parameter->value.octet_string->length;
+    }
+
+    if (EVP_PKEY_CTX_set_rsa_padding(pkctx, RSA_PKCS1_OAEP_PADDING) <= 0)
+        goto err;
+    if (EVP_PKEY_CTX_set_rsa_oaep_md(pkctx, md) <= 0)
+        goto err;
+    if (EVP_PKEY_CTX_set_rsa_mgf1_md(pkctx, mgf1md) <= 0)
+        goto err;
+    if (EVP_PKEY_CTX_set0_rsa_oaep_label(pkctx, label, labellen) <= 0)
+        goto err;
+    /* Carry on */
+    rv = 1;
+
+ err:
+    RSA_OAEP_PARAMS_free(oaep);
+    X509_ALGOR_free(maskHash);
+    return rv;
+}
+
+static int rsa_cms_encrypt(CMS_RecipientInfo *ri)
+{
+    const EVP_MD *md, *mgf1md;
+    RSA_OAEP_PARAMS *oaep = NULL;
+    ASN1_STRING *os = NULL;
+    X509_ALGOR *alg;
+    EVP_PKEY_CTX *pkctx = CMS_RecipientInfo_get0_pkey_ctx(ri);
+    int pad_mode = RSA_PKCS1_PADDING, rv = 0, labellen;
+    unsigned char *label;
+    CMS_RecipientInfo_ktri_get0_algs(ri, NULL, NULL, &alg);
+    if (pkctx) {
+        if (EVP_PKEY_CTX_get_rsa_padding(pkctx, &pad_mode) <= 0)
+            return 0;
+    }
+    if (pad_mode == RSA_PKCS1_PADDING) {
+        X509_ALGOR_set0(alg, OBJ_nid2obj(NID_rsaEncryption), V_ASN1_NULL, 0);
+        return 1;
+    }
+    /* Not supported */
+    if (pad_mode != RSA_PKCS1_OAEP_PADDING)
+        return 0;
+    if (EVP_PKEY_CTX_get_rsa_oaep_md(pkctx, &md) <= 0)
+        goto err;
+    if (EVP_PKEY_CTX_get_rsa_mgf1_md(pkctx, &mgf1md) <= 0)
+        goto err;
+    labellen = EVP_PKEY_CTX_get0_rsa_oaep_label(pkctx, &label);
+    if (labellen < 0)
+        goto err;
+    oaep = RSA_OAEP_PARAMS_new();
+    if (oaep == NULL)
+        goto err;
+    if (!rsa_md_to_algor(&oaep->hashFunc, md))
+        goto err;
+    if (!rsa_md_to_mgf1(&oaep->maskGenFunc, mgf1md))
+        goto err;
+    if (labellen > 0) {
+        ASN1_OCTET_STRING *los;
+        oaep->pSourceFunc = X509_ALGOR_new();
+        if (oaep->pSourceFunc == NULL)
+            goto err;
+        los = ASN1_OCTET_STRING_new();
+        if (los == NULL)
+            goto err;
+        if (!ASN1_OCTET_STRING_set(los, label, labellen)) {
+            ASN1_OCTET_STRING_free(los);
+            goto err;
+        }
+        X509_ALGOR_set0(oaep->pSourceFunc, OBJ_nid2obj(NID_pSpecified),
+                        V_ASN1_OCTET_STRING, los);
+    }
+    /* create string with pss parameter encoding. */
+    if (!ASN1_item_pack(oaep, ASN1_ITEM_rptr(RSA_OAEP_PARAMS), &os))
+         goto err;
+    X509_ALGOR_set0(alg, OBJ_nid2obj(NID_rsaesOaep), V_ASN1_SEQUENCE, os);
+    os = NULL;
+    rv = 1;
+ err:
+    RSA_OAEP_PARAMS_free(oaep);
+    ASN1_STRING_free(os);
+    return rv;
+}
+#endif
+
+const EVP_PKEY_ASN1_METHOD rsa_asn1_meths[2] = {
+    {
+     EVP_PKEY_RSA,
+     EVP_PKEY_RSA,
+     ASN1_PKEY_SIGPARAM_NULL,
+
+     "RSA",
+     "OpenSSL RSA method",
+
+     rsa_pub_decode,
+     rsa_pub_encode,
+     rsa_pub_cmp,
+     rsa_pub_print,
+
+     rsa_priv_decode,
+     rsa_priv_encode,
+     rsa_priv_print,
+
+     int_rsa_size,
+     rsa_bits,
+     rsa_security_bits,
+
+     0, 0, 0, 0, 0, 0,
+
+     rsa_sig_print,
+     int_rsa_free,
+     rsa_pkey_ctrl,
+     old_rsa_priv_decode,
+     old_rsa_priv_encode,
+     rsa_item_verify,
+     rsa_item_sign},
+
+    {
+     EVP_PKEY_RSA2,
+     EVP_PKEY_RSA,
+     ASN1_PKEY_ALIAS}
+};
diff --git a/crypto/rsa/rsa_asn1.c b/crypto/rsa/rsa_asn1.c
new file mode 100644
index 000000000..20f8ebfa8
--- /dev/null
+++ b/crypto/rsa/rsa_asn1.c
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2000-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+#include 
+#include "rsa_locl.h"
+
+/* Override the default free and new methods */
+static int rsa_cb(int operation, ASN1_VALUE **pval, const ASN1_ITEM *it,
+                  void *exarg)
+{
+    if (operation == ASN1_OP_NEW_PRE) {
+        *pval = (ASN1_VALUE *)RSA_new();
+        if (*pval != NULL)
+            return 2;
+        return 0;
+    } else if (operation == ASN1_OP_FREE_PRE) {
+        RSA_free((RSA *)*pval);
+        *pval = NULL;
+        return 2;
+    }
+    return 1;
+}
+
+ASN1_SEQUENCE_cb(RSAPrivateKey, rsa_cb) = {
+        ASN1_SIMPLE(RSA, version, LONG),
+        ASN1_SIMPLE(RSA, n, BIGNUM),
+        ASN1_SIMPLE(RSA, e, BIGNUM),
+        ASN1_SIMPLE(RSA, d, CBIGNUM),
+        ASN1_SIMPLE(RSA, p, CBIGNUM),
+        ASN1_SIMPLE(RSA, q, CBIGNUM),
+        ASN1_SIMPLE(RSA, dmp1, CBIGNUM),
+        ASN1_SIMPLE(RSA, dmq1, CBIGNUM),
+        ASN1_SIMPLE(RSA, iqmp, CBIGNUM)
+} ASN1_SEQUENCE_END_cb(RSA, RSAPrivateKey)
+
+
+ASN1_SEQUENCE_cb(RSAPublicKey, rsa_cb) = {
+        ASN1_SIMPLE(RSA, n, BIGNUM),
+        ASN1_SIMPLE(RSA, e, BIGNUM),
+} ASN1_SEQUENCE_END_cb(RSA, RSAPublicKey)
+
+ASN1_SEQUENCE(RSA_PSS_PARAMS) = {
+        ASN1_EXP_OPT(RSA_PSS_PARAMS, hashAlgorithm, X509_ALGOR,0),
+        ASN1_EXP_OPT(RSA_PSS_PARAMS, maskGenAlgorithm, X509_ALGOR,1),
+        ASN1_EXP_OPT(RSA_PSS_PARAMS, saltLength, ASN1_INTEGER,2),
+        ASN1_EXP_OPT(RSA_PSS_PARAMS, trailerField, ASN1_INTEGER,3)
+} ASN1_SEQUENCE_END(RSA_PSS_PARAMS)
+
+IMPLEMENT_ASN1_FUNCTIONS(RSA_PSS_PARAMS)
+
+ASN1_SEQUENCE(RSA_OAEP_PARAMS) = {
+        ASN1_EXP_OPT(RSA_OAEP_PARAMS, hashFunc, X509_ALGOR, 0),
+        ASN1_EXP_OPT(RSA_OAEP_PARAMS, maskGenFunc, X509_ALGOR, 1),
+        ASN1_EXP_OPT(RSA_OAEP_PARAMS, pSourceFunc, X509_ALGOR, 2),
+} ASN1_SEQUENCE_END(RSA_OAEP_PARAMS)
+
+IMPLEMENT_ASN1_FUNCTIONS(RSA_OAEP_PARAMS)
+
+IMPLEMENT_ASN1_ENCODE_FUNCTIONS_const_fname(RSA, RSAPrivateKey, RSAPrivateKey)
+
+IMPLEMENT_ASN1_ENCODE_FUNCTIONS_const_fname(RSA, RSAPublicKey, RSAPublicKey)
+
+RSA *RSAPublicKey_dup(RSA *rsa)
+{
+    return ASN1_item_dup(ASN1_ITEM_rptr(RSAPublicKey), rsa);
+}
+
+RSA *RSAPrivateKey_dup(RSA *rsa)
+{
+    return ASN1_item_dup(ASN1_ITEM_rptr(RSAPrivateKey), rsa);
+}
diff --git a/crypto/rsa/rsa_chk.c b/crypto/rsa/rsa_chk.c
new file mode 100644
index 000000000..00260fb18
--- /dev/null
+++ b/crypto/rsa/rsa_chk.c
@@ -0,0 +1,156 @@
+/*
+ * Copyright 1999-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include "rsa_locl.h"
+
+int RSA_check_key(const RSA *key)
+{
+    return RSA_check_key_ex(key, NULL);
+}
+
+int RSA_check_key_ex(const RSA *key, BN_GENCB *cb)
+{
+    BIGNUM *i, *j, *k, *l, *m;
+    BN_CTX *ctx;
+    int ret = 1;
+
+    if (key->p == NULL || key->q == NULL || key->n == NULL
+            || key->e == NULL || key->d == NULL) {
+        RSAerr(RSA_F_RSA_CHECK_KEY_EX, RSA_R_VALUE_MISSING);
+        return 0;
+    }
+
+    i = BN_new();
+    j = BN_new();
+    k = BN_new();
+    l = BN_new();
+    m = BN_new();
+    ctx = BN_CTX_new();
+    if (i == NULL || j == NULL || k == NULL || l == NULL
+            || m == NULL || ctx == NULL) {
+        ret = -1;
+        RSAerr(RSA_F_RSA_CHECK_KEY_EX, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+
+    if (BN_is_one(key->e)) {
+        ret = 0;
+        RSAerr(RSA_F_RSA_CHECK_KEY_EX, RSA_R_BAD_E_VALUE);
+    }
+    if (!BN_is_odd(key->e)) {
+        ret = 0;
+        RSAerr(RSA_F_RSA_CHECK_KEY_EX, RSA_R_BAD_E_VALUE);
+    }
+
+    /* p prime? */
+    if (BN_is_prime_ex(key->p, BN_prime_checks, NULL, cb) != 1) {
+        ret = 0;
+        RSAerr(RSA_F_RSA_CHECK_KEY_EX, RSA_R_P_NOT_PRIME);
+    }
+
+    /* q prime? */
+    if (BN_is_prime_ex(key->q, BN_prime_checks, NULL, cb) != 1) {
+        ret = 0;
+        RSAerr(RSA_F_RSA_CHECK_KEY_EX, RSA_R_Q_NOT_PRIME);
+    }
+
+    /* n = p*q? */
+    if (!BN_mul(i, key->p, key->q, ctx)) {
+        ret = -1;
+        goto err;
+    }
+    if (BN_cmp(i, key->n) != 0) {
+        ret = 0;
+        RSAerr(RSA_F_RSA_CHECK_KEY_EX, RSA_R_N_DOES_NOT_EQUAL_P_Q);
+    }
+
+    /* d*e = 1  mod lcm(p-1,q-1)? */
+    if (!BN_sub(i, key->p, BN_value_one())) {
+        ret = -1;
+        goto err;
+    }
+    if (!BN_sub(j, key->q, BN_value_one())) {
+        ret = -1;
+        goto err;
+    }
+
+    /* now compute k = lcm(i,j) */
+    if (!BN_mul(l, i, j, ctx)) {
+        ret = -1;
+        goto err;
+    }
+    if (!BN_gcd(m, i, j, ctx)) {
+        ret = -1;
+        goto err;
+    }
+    if (!BN_div(k, NULL, l, m, ctx)) { /* remainder is 0 */
+        ret = -1;
+        goto err;
+    }
+    if (!BN_mod_mul(i, key->d, key->e, k, ctx)) {
+        ret = -1;
+        goto err;
+    }
+
+    if (!BN_is_one(i)) {
+        ret = 0;
+        RSAerr(RSA_F_RSA_CHECK_KEY_EX, RSA_R_D_E_NOT_CONGRUENT_TO_1);
+    }
+
+    if (key->dmp1 != NULL && key->dmq1 != NULL && key->iqmp != NULL) {
+        /* dmp1 = d mod (p-1)? */
+        if (!BN_sub(i, key->p, BN_value_one())) {
+            ret = -1;
+            goto err;
+        }
+        if (!BN_mod(j, key->d, i, ctx)) {
+            ret = -1;
+            goto err;
+        }
+        if (BN_cmp(j, key->dmp1) != 0) {
+            ret = 0;
+            RSAerr(RSA_F_RSA_CHECK_KEY_EX, RSA_R_DMP1_NOT_CONGRUENT_TO_D);
+        }
+
+        /* dmq1 = d mod (q-1)? */
+        if (!BN_sub(i, key->q, BN_value_one())) {
+            ret = -1;
+            goto err;
+        }
+        if (!BN_mod(j, key->d, i, ctx)) {
+            ret = -1;
+            goto err;
+        }
+        if (BN_cmp(j, key->dmq1) != 0) {
+            ret = 0;
+            RSAerr(RSA_F_RSA_CHECK_KEY_EX, RSA_R_DMQ1_NOT_CONGRUENT_TO_D);
+        }
+
+        /* iqmp = q^-1 mod p? */
+        if (!BN_mod_inverse(i, key->q, key->p, ctx)) {
+            ret = -1;
+            goto err;
+        }
+        if (BN_cmp(i, key->iqmp) != 0) {
+            ret = 0;
+            RSAerr(RSA_F_RSA_CHECK_KEY_EX, RSA_R_IQMP_NOT_INVERSE_OF_Q);
+        }
+    }
+
+ err:
+    BN_free(i);
+    BN_free(j);
+    BN_free(k);
+    BN_free(l);
+    BN_free(m);
+    BN_CTX_free(ctx);
+    return ret;
+}
diff --git a/crypto/rsa/rsa_crpt.c b/crypto/rsa/rsa_crpt.c
new file mode 100644
index 000000000..9cd733b2c
--- /dev/null
+++ b/crypto/rsa/rsa_crpt.c
@@ -0,0 +1,178 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include "internal/bn_int.h"
+#include 
+#include "rsa_locl.h"
+
+int RSA_bits(const RSA *r)
+{
+    return (BN_num_bits(r->n));
+}
+
+int RSA_size(const RSA *r)
+{
+    return (BN_num_bytes(r->n));
+}
+
+int RSA_public_encrypt(int flen, const unsigned char *from, unsigned char *to,
+                       RSA *rsa, int padding)
+{
+    return (rsa->meth->rsa_pub_enc(flen, from, to, rsa, padding));
+}
+
+int RSA_private_encrypt(int flen, const unsigned char *from,
+                        unsigned char *to, RSA *rsa, int padding)
+{
+    return (rsa->meth->rsa_priv_enc(flen, from, to, rsa, padding));
+}
+
+int RSA_private_decrypt(int flen, const unsigned char *from,
+                        unsigned char *to, RSA *rsa, int padding)
+{
+    return (rsa->meth->rsa_priv_dec(flen, from, to, rsa, padding));
+}
+
+int RSA_public_decrypt(int flen, const unsigned char *from, unsigned char *to,
+                       RSA *rsa, int padding)
+{
+    return (rsa->meth->rsa_pub_dec(flen, from, to, rsa, padding));
+}
+
+int RSA_flags(const RSA *r)
+{
+    return ((r == NULL) ? 0 : r->meth->flags);
+}
+
+void RSA_blinding_off(RSA *rsa)
+{
+    BN_BLINDING_free(rsa->blinding);
+    rsa->blinding = NULL;
+    rsa->flags &= ~RSA_FLAG_BLINDING;
+    rsa->flags |= RSA_FLAG_NO_BLINDING;
+}
+
+int RSA_blinding_on(RSA *rsa, BN_CTX *ctx)
+{
+    int ret = 0;
+
+    if (rsa->blinding != NULL)
+        RSA_blinding_off(rsa);
+
+    rsa->blinding = RSA_setup_blinding(rsa, ctx);
+    if (rsa->blinding == NULL)
+        goto err;
+
+    rsa->flags |= RSA_FLAG_BLINDING;
+    rsa->flags &= ~RSA_FLAG_NO_BLINDING;
+    ret = 1;
+ err:
+    return (ret);
+}
+
+static BIGNUM *rsa_get_public_exp(const BIGNUM *d, const BIGNUM *p,
+                                  const BIGNUM *q, BN_CTX *ctx)
+{
+    BIGNUM *ret = NULL, *r0, *r1, *r2;
+
+    if (d == NULL || p == NULL || q == NULL)
+        return NULL;
+
+    BN_CTX_start(ctx);
+    r0 = BN_CTX_get(ctx);
+    r1 = BN_CTX_get(ctx);
+    r2 = BN_CTX_get(ctx);
+    if (r2 == NULL)
+        goto err;
+
+    if (!BN_sub(r1, p, BN_value_one()))
+        goto err;
+    if (!BN_sub(r2, q, BN_value_one()))
+        goto err;
+    if (!BN_mul(r0, r1, r2, ctx))
+        goto err;
+
+    ret = BN_mod_inverse(NULL, d, r0, ctx);
+ err:
+    BN_CTX_end(ctx);
+    return ret;
+}
+
+BN_BLINDING *RSA_setup_blinding(RSA *rsa, BN_CTX *in_ctx)
+{
+    BIGNUM *e;
+    BN_CTX *ctx;
+    BN_BLINDING *ret = NULL;
+
+    if (in_ctx == NULL) {
+        if ((ctx = BN_CTX_new()) == NULL)
+            return 0;
+    } else
+        ctx = in_ctx;
+
+    BN_CTX_start(ctx);
+    e = BN_CTX_get(ctx);
+    if (e == NULL) {
+        RSAerr(RSA_F_RSA_SETUP_BLINDING, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+
+    if (rsa->e == NULL) {
+        e = rsa_get_public_exp(rsa->d, rsa->p, rsa->q, ctx);
+        if (e == NULL) {
+            RSAerr(RSA_F_RSA_SETUP_BLINDING, RSA_R_NO_PUBLIC_EXPONENT);
+            goto err;
+        }
+    } else
+        e = rsa->e;
+
+    if ((RAND_status() == 0) && rsa->d != NULL
+        && bn_get_words(rsa->d) != NULL) {
+        /*
+         * if PRNG is not properly seeded, resort to secret exponent as
+         * unpredictable seed
+         */
+        RAND_add(bn_get_words(rsa->d), bn_get_dmax(rsa->d) * sizeof(BN_ULONG),
+                 0.0);
+    }
+
+    {
+        BIGNUM *n = BN_new();
+
+        if (n == NULL) {
+            RSAerr(RSA_F_RSA_SETUP_BLINDING, ERR_R_MALLOC_FAILURE);
+            goto err;
+        }
+        BN_with_flags(n, rsa->n, BN_FLG_CONSTTIME);
+
+        ret = BN_BLINDING_create_param(NULL, e, n, ctx, rsa->meth->bn_mod_exp,
+                                       rsa->_method_mod_n);
+        /* We MUST free n before any further use of rsa->n */
+        BN_free(n);
+    }
+    if (ret == NULL) {
+        RSAerr(RSA_F_RSA_SETUP_BLINDING, ERR_R_BN_LIB);
+        goto err;
+    }
+
+    BN_BLINDING_set_current_thread(ret);
+
+ err:
+    BN_CTX_end(ctx);
+    if (ctx != in_ctx)
+        BN_CTX_free(ctx);
+    if (e != rsa->e)
+        BN_free(e);
+
+    return ret;
+}
diff --git a/crypto/rsa/rsa_depr.c b/crypto/rsa/rsa_depr.c
new file mode 100644
index 000000000..21e056252
--- /dev/null
+++ b/crypto/rsa/rsa_depr.c
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2002-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * NB: This file contains deprecated functions (compatibility wrappers to the
+ * "new" versions).
+ */
+
+#include 
+#if OPENSSL_API_COMPAT >= 0x00908000L
+NON_EMPTY_TRANSLATION_UNIT
+
+#else
+
+# include 
+# include 
+# include "internal/cryptlib.h"
+# include 
+# include 
+
+RSA *RSA_generate_key(int bits, unsigned long e_value,
+                      void (*callback) (int, int, void *), void *cb_arg)
+{
+    int i;
+    BN_GENCB *cb = BN_GENCB_new();
+    RSA *rsa = RSA_new();
+    BIGNUM *e = BN_new();
+
+    if (cb == NULL || rsa == NULL || e == NULL)
+        goto err;
+
+    /*
+     * The problem is when building with 8, 16, or 32 BN_ULONG, unsigned long
+     * can be larger
+     */
+    for (i = 0; i < (int)sizeof(unsigned long) * 8; i++) {
+        if (e_value & (1UL << i))
+            if (BN_set_bit(e, i) == 0)
+                goto err;
+    }
+
+    BN_GENCB_set_old(cb, callback, cb_arg);
+
+    if (RSA_generate_key_ex(rsa, bits, e, cb)) {
+        BN_free(e);
+        BN_GENCB_free(cb);
+        return rsa;
+    }
+ err:
+    BN_free(e);
+    RSA_free(rsa);
+    BN_GENCB_free(cb);
+    return 0;
+}
+#endif
diff --git a/crypto/rsa/rsa_err.c b/crypto/rsa/rsa_err.c
new file mode 100644
index 000000000..1002b2aff
--- /dev/null
+++ b/crypto/rsa/rsa_err.c
@@ -0,0 +1,199 @@
+/*
+ * Generated by util/mkerr.pl DO NOT EDIT
+ * Copyright 1995-2017 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include 
+
+/* BEGIN ERROR CODES */
+#ifndef OPENSSL_NO_ERR
+
+# define ERR_FUNC(func) ERR_PACK(ERR_LIB_RSA,func,0)
+# define ERR_REASON(reason) ERR_PACK(ERR_LIB_RSA,0,reason)
+
+static ERR_STRING_DATA RSA_str_functs[] = {
+    {ERR_FUNC(RSA_F_CHECK_PADDING_MD), "check_padding_md"},
+    {ERR_FUNC(RSA_F_ENCODE_PKCS1), "encode_pkcs1"},
+    {ERR_FUNC(RSA_F_INT_RSA_VERIFY), "int_rsa_verify"},
+    {ERR_FUNC(RSA_F_MARSHAL_INTEGER), "marshal_integer"},
+    {ERR_FUNC(RSA_F_OLD_RSA_PRIV_DECODE), "old_rsa_priv_decode"},
+    {ERR_FUNC(RSA_F_PARSE_PUBLIC_KEY), "parse_public_key"},
+    {ERR_FUNC(RSA_F_PKEY_RSA_CTRL), "pkey_rsa_ctrl"},
+    {ERR_FUNC(RSA_F_PKEY_RSA_CTRL_STR), "pkey_rsa_ctrl_str"},
+    {ERR_FUNC(RSA_F_PKEY_RSA_SIGN), "pkey_rsa_sign"},
+    {ERR_FUNC(RSA_F_PKEY_RSA_VERIFY), "pkey_rsa_verify"},
+    {ERR_FUNC(RSA_F_PKEY_RSA_VERIFYRECOVER), "pkey_rsa_verifyrecover"},
+    {ERR_FUNC(RSA_F_RSA_ALGOR_TO_MD), "rsa_algor_to_md"},
+    {ERR_FUNC(RSA_F_RSA_BUILTIN_KEYGEN), "rsa_builtin_keygen"},
+    {ERR_FUNC(RSA_F_RSA_CHECK_KEY), "RSA_check_key"},
+    {ERR_FUNC(RSA_F_RSA_CHECK_KEY_EX), "RSA_check_key_ex"},
+    {ERR_FUNC(RSA_F_RSA_CMS_DECRYPT), "rsa_cms_decrypt"},
+    {ERR_FUNC(RSA_F_RSA_ITEM_VERIFY), "rsa_item_verify"},
+    {ERR_FUNC(RSA_F_RSA_MARSHAL_PRIVATE_KEY), "RSA_marshal_private_key"},
+    {ERR_FUNC(RSA_F_RSA_MARSHAL_PUBLIC_KEY), "RSA_marshal_public_key"},
+    {ERR_FUNC(RSA_F_RSA_METH_DUP), "RSA_meth_dup"},
+    {ERR_FUNC(RSA_F_RSA_METH_NEW), "RSA_meth_new"},
+    {ERR_FUNC(RSA_F_RSA_METH_SET1_NAME), "RSA_meth_set1_name"},
+    {ERR_FUNC(RSA_F_RSA_MGF1_TO_MD), "rsa_mgf1_to_md"},
+    {ERR_FUNC(RSA_F_RSA_NEW_METHOD), "RSA_new_method"},
+    {ERR_FUNC(RSA_F_RSA_NULL), "RSA_NULL"},
+    {ERR_FUNC(RSA_F_RSA_NULL_PRIVATE_DECRYPT), "RSA_null_private_decrypt"},
+    {ERR_FUNC(RSA_F_RSA_NULL_PRIVATE_ENCRYPT), "RSA_null_private_encrypt"},
+    {ERR_FUNC(RSA_F_RSA_NULL_PUBLIC_DECRYPT), "RSA_null_public_decrypt"},
+    {ERR_FUNC(RSA_F_RSA_NULL_PUBLIC_ENCRYPT), "RSA_null_public_encrypt"},
+    {ERR_FUNC(RSA_F_RSA_OSSL_PRIVATE_DECRYPT), "rsa_ossl_private_decrypt"},
+    {ERR_FUNC(RSA_F_RSA_OSSL_PRIVATE_ENCRYPT), "rsa_ossl_private_encrypt"},
+    {ERR_FUNC(RSA_F_RSA_OSSL_PUBLIC_DECRYPT), "rsa_ossl_public_decrypt"},
+    {ERR_FUNC(RSA_F_RSA_OSSL_PUBLIC_ENCRYPT), "rsa_ossl_public_encrypt"},
+    {ERR_FUNC(RSA_F_RSA_PADDING_ADD_NONE), "RSA_padding_add_none"},
+    {ERR_FUNC(RSA_F_RSA_PADDING_ADD_PKCS1_OAEP),
+     "RSA_padding_add_PKCS1_OAEP"},
+    {ERR_FUNC(RSA_F_RSA_PADDING_ADD_PKCS1_OAEP_MGF1),
+     "RSA_padding_add_PKCS1_OAEP_mgf1"},
+    {ERR_FUNC(RSA_F_RSA_PADDING_ADD_PKCS1_PSS), "RSA_padding_add_PKCS1_PSS"},
+    {ERR_FUNC(RSA_F_RSA_PADDING_ADD_PKCS1_PSS_MGF1),
+     "RSA_padding_add_PKCS1_PSS_mgf1"},
+    {ERR_FUNC(RSA_F_RSA_PADDING_ADD_PKCS1_TYPE_1),
+     "RSA_padding_add_PKCS1_type_1"},
+    {ERR_FUNC(RSA_F_RSA_PADDING_ADD_PKCS1_TYPE_2),
+     "RSA_padding_add_PKCS1_type_2"},
+    {ERR_FUNC(RSA_F_RSA_PADDING_ADD_SSLV23), "RSA_padding_add_SSLv23"},
+    {ERR_FUNC(RSA_F_RSA_PADDING_ADD_X931), "RSA_padding_add_X931"},
+    {ERR_FUNC(RSA_F_RSA_PADDING_CHECK_NONE), "RSA_padding_check_none"},
+    {ERR_FUNC(RSA_F_RSA_PADDING_CHECK_PKCS1_OAEP),
+     "RSA_padding_check_PKCS1_OAEP"},
+    {ERR_FUNC(RSA_F_RSA_PADDING_CHECK_PKCS1_OAEP_MGF1),
+     "RSA_padding_check_PKCS1_OAEP_mgf1"},
+    {ERR_FUNC(RSA_F_RSA_PADDING_CHECK_PKCS1_TYPE_1),
+     "RSA_padding_check_PKCS1_type_1"},
+    {ERR_FUNC(RSA_F_RSA_PADDING_CHECK_PKCS1_TYPE_2),
+     "RSA_padding_check_PKCS1_type_2"},
+    {ERR_FUNC(RSA_F_RSA_PADDING_CHECK_SSLV23), "RSA_padding_check_SSLv23"},
+    {ERR_FUNC(RSA_F_RSA_PADDING_CHECK_X931), "RSA_padding_check_X931"},
+    {ERR_FUNC(RSA_F_RSA_PARSE_PRIVATE_KEY), "RSA_parse_private_key"},
+    {ERR_FUNC(RSA_F_RSA_PRINT), "RSA_print"},
+    {ERR_FUNC(RSA_F_RSA_PRINT_FP), "RSA_print_fp"},
+    {ERR_FUNC(RSA_F_RSA_PRIVATE_KEY_FROM_BYTES),
+     "RSA_private_key_from_bytes"},
+    {ERR_FUNC(RSA_F_RSA_PRIVATE_KEY_TO_BYTES), "RSA_private_key_to_bytes"},
+    {ERR_FUNC(RSA_F_RSA_PRIV_ENCODE), "rsa_priv_encode"},
+    {ERR_FUNC(RSA_F_RSA_PSS_TO_CTX), "rsa_pss_to_ctx"},
+    {ERR_FUNC(RSA_F_RSA_PUBLIC_KEY_FROM_BYTES), "RSA_public_key_from_bytes"},
+    {ERR_FUNC(RSA_F_RSA_PUBLIC_KEY_TO_BYTES), "RSA_public_key_to_bytes"},
+    {ERR_FUNC(RSA_F_RSA_PUB_DECODE), "rsa_pub_decode"},
+    {ERR_FUNC(RSA_F_RSA_SETUP_BLINDING), "RSA_setup_blinding"},
+    {ERR_FUNC(RSA_F_RSA_SIGN), "RSA_sign"},
+    {ERR_FUNC(RSA_F_RSA_SIGN_ASN1_OCTET_STRING),
+     "RSA_sign_ASN1_OCTET_STRING"},
+    {ERR_FUNC(RSA_F_RSA_VERIFY), "RSA_verify"},
+    {ERR_FUNC(RSA_F_RSA_VERIFY_ASN1_OCTET_STRING),
+     "RSA_verify_ASN1_OCTET_STRING"},
+    {ERR_FUNC(RSA_F_RSA_VERIFY_PKCS1_PSS_MGF1), "RSA_verify_PKCS1_PSS_mgf1"},
+    {0, NULL}
+};
+
+static ERR_STRING_DATA RSA_str_reasons[] = {
+    {ERR_REASON(RSA_R_ALGORITHM_MISMATCH), "algorithm mismatch"},
+    {ERR_REASON(RSA_R_BAD_ENCODING), "bad encoding"},
+    {ERR_REASON(RSA_R_BAD_E_VALUE), "bad e value"},
+    {ERR_REASON(RSA_R_BAD_FIXED_HEADER_DECRYPT), "bad fixed header decrypt"},
+    {ERR_REASON(RSA_R_BAD_PAD_BYTE_COUNT), "bad pad byte count"},
+    {ERR_REASON(RSA_R_BAD_RSA_PARAMETERS), "bad rsa parameters"},
+    {ERR_REASON(RSA_R_BAD_SIGNATURE), "bad signature"},
+    {ERR_REASON(RSA_R_BAD_VERSION), "bad version"},
+    {ERR_REASON(RSA_R_BLOCK_TYPE_IS_NOT_01), "block type is not 01"},
+    {ERR_REASON(RSA_R_BLOCK_TYPE_IS_NOT_02), "block type is not 02"},
+    {ERR_REASON(RSA_R_DATA_GREATER_THAN_MOD_LEN),
+     "data greater than mod len"},
+    {ERR_REASON(RSA_R_DATA_TOO_LARGE), "data too large"},
+    {ERR_REASON(RSA_R_DATA_TOO_LARGE_FOR_KEY_SIZE),
+     "data too large for key size"},
+    {ERR_REASON(RSA_R_DATA_TOO_LARGE_FOR_MODULUS),
+     "data too large for modulus"},
+    {ERR_REASON(RSA_R_DATA_TOO_SMALL), "data too small"},
+    {ERR_REASON(RSA_R_DATA_TOO_SMALL_FOR_KEY_SIZE),
+     "data too small for key size"},
+    {ERR_REASON(RSA_R_DIGEST_DOES_NOT_MATCH), "digest does not match"},
+    {ERR_REASON(RSA_R_DIGEST_TOO_BIG_FOR_RSA_KEY),
+     "digest too big for rsa key"},
+    {ERR_REASON(RSA_R_DMP1_NOT_CONGRUENT_TO_D), "dmp1 not congruent to d"},
+    {ERR_REASON(RSA_R_DMQ1_NOT_CONGRUENT_TO_D), "dmq1 not congruent to d"},
+    {ERR_REASON(RSA_R_D_E_NOT_CONGRUENT_TO_1), "d e not congruent to 1"},
+    {ERR_REASON(RSA_R_ENCODE_ERROR), "encode error"},
+    {ERR_REASON(RSA_R_FIRST_OCTET_INVALID), "first octet invalid"},
+    {ERR_REASON(RSA_R_ILLEGAL_OR_UNSUPPORTED_PADDING_MODE),
+     "illegal or unsupported padding mode"},
+    {ERR_REASON(RSA_R_INVALID_DIGEST), "invalid digest"},
+    {ERR_REASON(RSA_R_INVALID_DIGEST_LENGTH), "invalid digest length"},
+    {ERR_REASON(RSA_R_INVALID_HEADER), "invalid header"},
+    {ERR_REASON(RSA_R_INVALID_LABEL), "invalid label"},
+    {ERR_REASON(RSA_R_INVALID_MESSAGE_LENGTH), "invalid message length"},
+    {ERR_REASON(RSA_R_INVALID_MGF1_MD), "invalid mgf1 md"},
+    {ERR_REASON(RSA_R_INVALID_OAEP_PARAMETERS), "invalid oaep parameters"},
+    {ERR_REASON(RSA_R_INVALID_PADDING), "invalid padding"},
+    {ERR_REASON(RSA_R_INVALID_PADDING_MODE), "invalid padding mode"},
+    {ERR_REASON(RSA_R_INVALID_PSS_PARAMETERS), "invalid pss parameters"},
+    {ERR_REASON(RSA_R_INVALID_PSS_SALTLEN), "invalid pss saltlen"},
+    {ERR_REASON(RSA_R_INVALID_SALT_LENGTH), "invalid salt length"},
+    {ERR_REASON(RSA_R_INVALID_TRAILER), "invalid trailer"},
+    {ERR_REASON(RSA_R_INVALID_X931_DIGEST), "invalid x931 digest"},
+    {ERR_REASON(RSA_R_IQMP_NOT_INVERSE_OF_Q), "iqmp not inverse of q"},
+    {ERR_REASON(RSA_R_KEY_SIZE_TOO_SMALL), "key size too small"},
+    {ERR_REASON(RSA_R_LAST_OCTET_INVALID), "last octet invalid"},
+    {ERR_REASON(RSA_R_MODULUS_TOO_LARGE), "modulus too large"},
+    {ERR_REASON(RSA_R_NO_PUBLIC_EXPONENT), "no public exponent"},
+    {ERR_REASON(RSA_R_NULL_BEFORE_BLOCK_MISSING),
+     "null before block missing"},
+    {ERR_REASON(RSA_R_N_DOES_NOT_EQUAL_P_Q), "n does not equal p q"},
+    {ERR_REASON(RSA_R_OAEP_DECODING_ERROR), "oaep decoding error"},
+    {ERR_REASON(RSA_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE),
+     "operation not supported for this keytype"},
+    {ERR_REASON(RSA_R_PADDING_CHECK_FAILED), "padding check failed"},
+    {ERR_REASON(RSA_R_PKCS_DECODING_ERROR), "pkcs decoding error"},
+    {ERR_REASON(RSA_R_P_NOT_PRIME), "p not prime"},
+    {ERR_REASON(RSA_R_Q_NOT_PRIME), "q not prime"},
+    {ERR_REASON(RSA_R_RSA_OPERATIONS_NOT_SUPPORTED),
+     "rsa operations not supported"},
+    {ERR_REASON(RSA_R_SLEN_CHECK_FAILED), "salt length check failed"},
+    {ERR_REASON(RSA_R_SLEN_RECOVERY_FAILED), "salt length recovery failed"},
+    {ERR_REASON(RSA_R_SSLV3_ROLLBACK_ATTACK), "sslv3 rollback attack"},
+    {ERR_REASON(RSA_R_THE_ASN1_OBJECT_IDENTIFIER_IS_NOT_KNOWN_FOR_THIS_MD),
+     "the asn1 object identifier is not known for this md"},
+    {ERR_REASON(RSA_R_UNKNOWN_ALGORITHM_TYPE), "unknown algorithm type"},
+    {ERR_REASON(RSA_R_UNKNOWN_DIGEST), "unknown digest"},
+    {ERR_REASON(RSA_R_UNKNOWN_MASK_DIGEST), "unknown mask digest"},
+    {ERR_REASON(RSA_R_UNKNOWN_PADDING_TYPE), "unknown padding type"},
+    {ERR_REASON(RSA_R_UNSUPPORTED_ENCRYPTION_TYPE),
+     "unsupported encryption type"},
+    {ERR_REASON(RSA_R_UNSUPPORTED_LABEL_SOURCE), "unsupported label source"},
+    {ERR_REASON(RSA_R_UNSUPPORTED_MASK_ALGORITHM),
+     "unsupported mask algorithm"},
+    {ERR_REASON(RSA_R_UNSUPPORTED_MASK_PARAMETER),
+     "unsupported mask parameter"},
+    {ERR_REASON(RSA_R_UNSUPPORTED_SIGNATURE_TYPE),
+     "unsupported signature type"},
+    {ERR_REASON(RSA_R_VALUE_MISSING), "value missing"},
+    {ERR_REASON(RSA_R_WRONG_SIGNATURE_LENGTH), "wrong signature length"},
+    {0, NULL}
+};
+
+#endif
+
+int ERR_load_RSA_strings(void)
+{
+#ifndef OPENSSL_NO_ERR
+
+    if (ERR_func_error_string(RSA_str_functs[0].error) == NULL) {
+        ERR_load_strings(0, RSA_str_functs);
+        ERR_load_strings(0, RSA_str_reasons);
+    }
+#endif
+    return 1;
+}
diff --git a/crypto/rsa/rsa_gen.c b/crypto/rsa/rsa_gen.c
new file mode 100644
index 000000000..0d1d56bfe
--- /dev/null
+++ b/crypto/rsa/rsa_gen.c
@@ -0,0 +1,199 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * NB: these functions have been "upgraded", the deprecated versions (which
+ * are compatibility wrappers using these functions) are in rsa_depr.c. -
+ * Geoff
+ */
+
+#include 
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include "rsa_locl.h"
+
+static int rsa_builtin_keygen(RSA *rsa, int bits, BIGNUM *e_value,
+                              BN_GENCB *cb);
+
+/*
+ * NB: this wrapper would normally be placed in rsa_lib.c and the static
+ * implementation would probably be in rsa_eay.c. Nonetheless, is kept here
+ * so that we don't introduce a new linker dependency. Eg. any application
+ * that wasn't previously linking object code related to key-generation won't
+ * have to now just because key-generation is part of RSA_METHOD.
+ */
+int RSA_generate_key_ex(RSA *rsa, int bits, BIGNUM *e_value, BN_GENCB *cb)
+{
+    if (rsa->meth->rsa_keygen)
+        return rsa->meth->rsa_keygen(rsa, bits, e_value, cb);
+    return rsa_builtin_keygen(rsa, bits, e_value, cb);
+}
+
+static int rsa_builtin_keygen(RSA *rsa, int bits, BIGNUM *e_value,
+                              BN_GENCB *cb)
+{
+    BIGNUM *r0 = NULL, *r1 = NULL, *r2 = NULL, *r3 = NULL, *tmp;
+    int bitsp, bitsq, ok = -1, n = 0;
+    BN_CTX *ctx = NULL;
+
+    ctx = BN_CTX_new();
+    if (ctx == NULL)
+        goto err;
+    BN_CTX_start(ctx);
+    r0 = BN_CTX_get(ctx);
+    r1 = BN_CTX_get(ctx);
+    r2 = BN_CTX_get(ctx);
+    r3 = BN_CTX_get(ctx);
+    if (r3 == NULL)
+        goto err;
+
+    bitsp = (bits + 1) / 2;
+    bitsq = bits - bitsp;
+
+    /* We need the RSA components non-NULL */
+    if (!rsa->n && ((rsa->n = BN_new()) == NULL))
+        goto err;
+    if (!rsa->d && ((rsa->d = BN_secure_new()) == NULL))
+        goto err;
+    if (!rsa->e && ((rsa->e = BN_new()) == NULL))
+        goto err;
+    if (!rsa->p && ((rsa->p = BN_secure_new()) == NULL))
+        goto err;
+    if (!rsa->q && ((rsa->q = BN_secure_new()) == NULL))
+        goto err;
+    if (!rsa->dmp1 && ((rsa->dmp1 = BN_secure_new()) == NULL))
+        goto err;
+    if (!rsa->dmq1 && ((rsa->dmq1 = BN_secure_new()) == NULL))
+        goto err;
+    if (!rsa->iqmp && ((rsa->iqmp = BN_secure_new()) == NULL))
+        goto err;
+
+    if (BN_copy(rsa->e, e_value) == NULL)
+        goto err;
+
+    /* generate p and q */
+    for (;;) {
+        if (!BN_generate_prime_ex(rsa->p, bitsp, 0, NULL, NULL, cb))
+            goto err;
+        if (!BN_sub(r2, rsa->p, BN_value_one()))
+            goto err;
+        if (!BN_gcd(r1, r2, rsa->e, ctx))
+            goto err;
+        if (BN_is_one(r1))
+            break;
+        if (!BN_GENCB_call(cb, 2, n++))
+            goto err;
+    }
+    if (!BN_GENCB_call(cb, 3, 0))
+        goto err;
+    for (;;) {
+        /*
+         * When generating ridiculously small keys, we can get stuck
+         * continually regenerating the same prime values. Check for this and
+         * bail if it happens 3 times.
+         */
+        unsigned int degenerate = 0;
+        do {
+            if (!BN_generate_prime_ex(rsa->q, bitsq, 0, NULL, NULL, cb))
+                goto err;
+        } while ((BN_cmp(rsa->p, rsa->q) == 0) && (++degenerate < 3));
+        if (degenerate == 3) {
+            ok = 0;             /* we set our own err */
+            RSAerr(RSA_F_RSA_BUILTIN_KEYGEN, RSA_R_KEY_SIZE_TOO_SMALL);
+            goto err;
+        }
+        if (!BN_sub(r2, rsa->q, BN_value_one()))
+            goto err;
+        if (!BN_gcd(r1, r2, rsa->e, ctx))
+            goto err;
+        if (BN_is_one(r1))
+            break;
+        if (!BN_GENCB_call(cb, 2, n++))
+            goto err;
+    }
+    if (!BN_GENCB_call(cb, 3, 1))
+        goto err;
+    if (BN_cmp(rsa->p, rsa->q) < 0) {
+        tmp = rsa->p;
+        rsa->p = rsa->q;
+        rsa->q = tmp;
+    }
+
+    /* calculate n */
+    if (!BN_mul(rsa->n, rsa->p, rsa->q, ctx))
+        goto err;
+
+    /* calculate d */
+    if (!BN_sub(r1, rsa->p, BN_value_one()))
+        goto err;               /* p-1 */
+    if (!BN_sub(r2, rsa->q, BN_value_one()))
+        goto err;               /* q-1 */
+    if (!BN_mul(r0, r1, r2, ctx))
+        goto err;               /* (p-1)(q-1) */
+    {
+        BIGNUM *pr0 = BN_new();
+
+        if (pr0 == NULL)
+            goto err;
+        BN_with_flags(pr0, r0, BN_FLG_CONSTTIME);
+        if (!BN_mod_inverse(rsa->d, rsa->e, pr0, ctx)) {
+            BN_free(pr0);
+            goto err;               /* d */
+        }
+        /* We MUST free pr0 before any further use of r0 */
+        BN_free(pr0);
+    }
+
+    {
+        BIGNUM *d = BN_new();
+
+        if (d == NULL)
+            goto err;
+        BN_with_flags(d, rsa->d, BN_FLG_CONSTTIME);
+
+        if (   /* calculate d mod (p-1) */
+               !BN_mod(rsa->dmp1, d, r1, ctx)
+               /* calculate d mod (q-1) */
+            || !BN_mod(rsa->dmq1, d, r2, ctx)) {
+            BN_free(d);
+            goto err;
+        }
+        /* We MUST free d before any further use of rsa->d */
+        BN_free(d);
+    }
+
+    {
+        BIGNUM *p = BN_new();
+
+        if (p == NULL)
+            goto err;
+        BN_with_flags(p, rsa->p, BN_FLG_CONSTTIME);
+
+        /* calculate inverse of q mod p */
+        if (!BN_mod_inverse(rsa->iqmp, rsa->q, p, ctx)) {
+            BN_free(p);
+            goto err;
+        }
+        /* We MUST free p before any further use of rsa->p */
+        BN_free(p);
+    }
+
+    ok = 1;
+ err:
+    if (ok == -1) {
+        RSAerr(RSA_F_RSA_BUILTIN_KEYGEN, ERR_LIB_BN);
+        ok = 0;
+    }
+    if (ctx != NULL)
+        BN_CTX_end(ctx);
+    BN_CTX_free(ctx);
+
+    return ok;
+}
diff --git a/crypto/rsa/rsa_lib.c b/crypto/rsa/rsa_lib.c
new file mode 100644
index 000000000..48e9100a9
--- /dev/null
+++ b/crypto/rsa/rsa_lib.c
@@ -0,0 +1,310 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include "internal/bn_int.h"
+#include 
+#include "rsa_locl.h"
+
+static const RSA_METHOD *default_RSA_meth = NULL;
+
+RSA *RSA_new(void)
+{
+    RSA *r = RSA_new_method(NULL);
+
+    return r;
+}
+
+void RSA_set_default_method(const RSA_METHOD *meth)
+{
+    default_RSA_meth = meth;
+}
+
+const RSA_METHOD *RSA_get_default_method(void)
+{
+    if (default_RSA_meth == NULL) {
+#ifdef RSA_NULL
+        default_RSA_meth = RSA_null_method();
+#else
+        default_RSA_meth = RSA_PKCS1_OpenSSL();
+#endif
+    }
+
+    return default_RSA_meth;
+}
+
+const RSA_METHOD *RSA_get_method(const RSA *rsa)
+{
+    return rsa->meth;
+}
+
+int RSA_set_method(RSA *rsa, const RSA_METHOD *meth)
+{
+    /*
+     * NB: The caller is specifically setting a method, so it's not up to us
+     * to deal with which ENGINE it comes from.
+     */
+    const RSA_METHOD *mtmp;
+    mtmp = rsa->meth;
+    if (mtmp->finish)
+        mtmp->finish(rsa);
+#ifndef OPENSSL_NO_ENGINE
+    ENGINE_finish(rsa->engine);
+    rsa->engine = NULL;
+#endif
+    rsa->meth = meth;
+    if (meth->init)
+        meth->init(rsa);
+    return 1;
+}
+
+RSA *RSA_new_method(ENGINE *engine)
+{
+    RSA *ret = OPENSSL_zalloc(sizeof(*ret));
+
+    if (ret == NULL) {
+        RSAerr(RSA_F_RSA_NEW_METHOD, ERR_R_MALLOC_FAILURE);
+        return NULL;
+    }
+
+    ret->references = 1;
+    ret->lock = CRYPTO_THREAD_lock_new();
+    if (ret->lock == NULL) {
+        RSAerr(RSA_F_RSA_NEW_METHOD, ERR_R_MALLOC_FAILURE);
+        OPENSSL_free(ret);
+        return NULL;
+    }
+
+    ret->meth = RSA_get_default_method();
+#ifndef OPENSSL_NO_ENGINE
+    ret->flags = ret->meth->flags & ~RSA_FLAG_NON_FIPS_ALLOW;
+    if (engine) {
+        if (!ENGINE_init(engine)) {
+            RSAerr(RSA_F_RSA_NEW_METHOD, ERR_R_ENGINE_LIB);
+            goto err;
+        }
+        ret->engine = engine;
+    } else
+        ret->engine = ENGINE_get_default_RSA();
+    if (ret->engine) {
+        ret->meth = ENGINE_get_RSA(ret->engine);
+        if (ret->meth == NULL) {
+            RSAerr(RSA_F_RSA_NEW_METHOD, ERR_R_ENGINE_LIB);
+            goto err;
+        }
+    }
+#endif
+
+    ret->flags = ret->meth->flags & ~RSA_FLAG_NON_FIPS_ALLOW;
+    if (!CRYPTO_new_ex_data(CRYPTO_EX_INDEX_RSA, ret, &ret->ex_data)) {
+        goto err;
+    }
+
+    if ((ret->meth->init != NULL) && !ret->meth->init(ret)) {
+        RSAerr(RSA_F_RSA_NEW_METHOD, ERR_R_INIT_FAIL);
+        goto err;
+    }
+
+    return ret;
+
+err:
+    RSA_free(ret);
+    return NULL;
+}
+
+void RSA_free(RSA *r)
+{
+    int i;
+
+    if (r == NULL)
+        return;
+
+    CRYPTO_atomic_add(&r->references, -1, &i, r->lock);
+    REF_PRINT_COUNT("RSA", r);
+    if (i > 0)
+        return;
+    REF_ASSERT_ISNT(i < 0);
+
+    if (r->meth->finish)
+        r->meth->finish(r);
+#ifndef OPENSSL_NO_ENGINE
+    ENGINE_finish(r->engine);
+#endif
+
+    CRYPTO_free_ex_data(CRYPTO_EX_INDEX_RSA, r, &r->ex_data);
+
+    CRYPTO_THREAD_lock_free(r->lock);
+
+    BN_clear_free(r->n);
+    BN_clear_free(r->e);
+    BN_clear_free(r->d);
+    BN_clear_free(r->p);
+    BN_clear_free(r->q);
+    BN_clear_free(r->dmp1);
+    BN_clear_free(r->dmq1);
+    BN_clear_free(r->iqmp);
+    BN_BLINDING_free(r->blinding);
+    BN_BLINDING_free(r->mt_blinding);
+    OPENSSL_free(r->bignum_data);
+    OPENSSL_free(r);
+}
+
+int RSA_up_ref(RSA *r)
+{
+    int i;
+
+    if (CRYPTO_atomic_add(&r->references, 1, &i, r->lock) <= 0)
+        return 0;
+
+    REF_PRINT_COUNT("RSA", r);
+    REF_ASSERT_ISNT(i < 2);
+    return ((i > 1) ? 1 : 0);
+}
+
+int RSA_set_ex_data(RSA *r, int idx, void *arg)
+{
+    return (CRYPTO_set_ex_data(&r->ex_data, idx, arg));
+}
+
+void *RSA_get_ex_data(const RSA *r, int idx)
+{
+    return (CRYPTO_get_ex_data(&r->ex_data, idx));
+}
+
+int RSA_security_bits(const RSA *rsa)
+{
+    return BN_security_bits(BN_num_bits(rsa->n), -1);
+}
+
+int RSA_set0_key(RSA *r, BIGNUM *n, BIGNUM *e, BIGNUM *d)
+{
+    /* If the fields n and e in r are NULL, the corresponding input
+     * parameters MUST be non-NULL for n and e.  d may be
+     * left NULL (in case only the public key is used).
+     */
+    if ((r->n == NULL && n == NULL)
+        || (r->e == NULL && e == NULL))
+        return 0;
+
+    if (n != NULL) {
+        BN_free(r->n);
+        r->n = n;
+    }
+    if (e != NULL) {
+        BN_free(r->e);
+        r->e = e;
+    }
+    if (d != NULL) {
+        BN_free(r->d);
+        r->d = d;
+    }
+
+    return 1;
+}
+
+int RSA_set0_factors(RSA *r, BIGNUM *p, BIGNUM *q)
+{
+    /* If the fields p and q in r are NULL, the corresponding input
+     * parameters MUST be non-NULL.
+     */
+    if ((r->p == NULL && p == NULL)
+        || (r->q == NULL && q == NULL))
+        return 0;
+
+    if (p != NULL) {
+        BN_free(r->p);
+        r->p = p;
+    }
+    if (q != NULL) {
+        BN_free(r->q);
+        r->q = q;
+    }
+
+    return 1;
+}
+
+int RSA_set0_crt_params(RSA *r, BIGNUM *dmp1, BIGNUM *dmq1, BIGNUM *iqmp)
+{
+    /* If the fields dmp1, dmq1 and iqmp in r are NULL, the corresponding input
+     * parameters MUST be non-NULL.
+     */
+    if ((r->dmp1 == NULL && dmp1 == NULL)
+        || (r->dmq1 == NULL && dmq1 == NULL)
+        || (r->iqmp == NULL && iqmp == NULL))
+        return 0;
+
+    if (dmp1 != NULL) {
+        BN_free(r->dmp1);
+        r->dmp1 = dmp1;
+    }
+    if (dmq1 != NULL) {
+        BN_free(r->dmq1);
+        r->dmq1 = dmq1;
+    }
+    if (iqmp != NULL) {
+        BN_free(r->iqmp);
+        r->iqmp = iqmp;
+    }
+
+    return 1;
+}
+
+void RSA_get0_key(const RSA *r,
+                  const BIGNUM **n, const BIGNUM **e, const BIGNUM **d)
+{
+    if (n != NULL)
+        *n = r->n;
+    if (e != NULL)
+        *e = r->e;
+    if (d != NULL)
+        *d = r->d;
+}
+
+void RSA_get0_factors(const RSA *r, const BIGNUM **p, const BIGNUM **q)
+{
+    if (p != NULL)
+        *p = r->p;
+    if (q != NULL)
+        *q = r->q;
+}
+
+void RSA_get0_crt_params(const RSA *r,
+                         const BIGNUM **dmp1, const BIGNUM **dmq1,
+                         const BIGNUM **iqmp)
+{
+    if (dmp1 != NULL)
+        *dmp1 = r->dmp1;
+    if (dmq1 != NULL)
+        *dmq1 = r->dmq1;
+    if (iqmp != NULL)
+        *iqmp = r->iqmp;
+}
+
+void RSA_clear_flags(RSA *r, int flags)
+{
+    r->flags &= ~flags;
+}
+
+int RSA_test_flags(const RSA *r, int flags)
+{
+    return r->flags & flags;
+}
+
+void RSA_set_flags(RSA *r, int flags)
+{
+    r->flags |= flags;
+}
+
+ENGINE *RSA_get0_engine(const RSA *r)
+{
+    return r->engine;
+}
diff --git a/crypto/rsa/rsa_locl.h b/crypto/rsa/rsa_locl.h
new file mode 100644
index 000000000..5d16aa6f4
--- /dev/null
+++ b/crypto/rsa/rsa_locl.h
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2006-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+
+struct rsa_st {
+    /*
+     * The first parameter is used to pickup errors where this is passed
+     * instead of aEVP_PKEY, it is set to 0
+     */
+    int pad;
+    long version;
+    const RSA_METHOD *meth;
+    /* functional reference if 'meth' is ENGINE-provided */
+    ENGINE *engine;
+    BIGNUM *n;
+    BIGNUM *e;
+    BIGNUM *d;
+    BIGNUM *p;
+    BIGNUM *q;
+    BIGNUM *dmp1;
+    BIGNUM *dmq1;
+    BIGNUM *iqmp;
+    /* be careful using this if the RSA structure is shared */
+    CRYPTO_EX_DATA ex_data;
+    int references;
+    int flags;
+    /* Used to cache montgomery values */
+    BN_MONT_CTX *_method_mod_n;
+    BN_MONT_CTX *_method_mod_p;
+    BN_MONT_CTX *_method_mod_q;
+    /*
+     * all BIGNUM values are actually in the following data, if it is not
+     * NULL
+     */
+    char *bignum_data;
+    BN_BLINDING *blinding;
+    BN_BLINDING *mt_blinding;
+    CRYPTO_RWLOCK *lock;
+};
+
+struct rsa_meth_st {
+    char *name;
+    int (*rsa_pub_enc) (int flen, const unsigned char *from,
+                        unsigned char *to, RSA *rsa, int padding);
+    int (*rsa_pub_dec) (int flen, const unsigned char *from,
+                        unsigned char *to, RSA *rsa, int padding);
+    int (*rsa_priv_enc) (int flen, const unsigned char *from,
+                         unsigned char *to, RSA *rsa, int padding);
+    int (*rsa_priv_dec) (int flen, const unsigned char *from,
+                         unsigned char *to, RSA *rsa, int padding);
+    /* Can be null */
+    int (*rsa_mod_exp) (BIGNUM *r0, const BIGNUM *I, RSA *rsa, BN_CTX *ctx);
+    /* Can be null */
+    int (*bn_mod_exp) (BIGNUM *r, const BIGNUM *a, const BIGNUM *p,
+                       const BIGNUM *m, BN_CTX *ctx, BN_MONT_CTX *m_ctx);
+    /* called at new */
+    int (*init) (RSA *rsa);
+    /* called at free */
+    int (*finish) (RSA *rsa);
+    /* RSA_METHOD_FLAG_* things */
+    int flags;
+    /* may be needed! */
+    char *app_data;
+    /*
+     * New sign and verify functions: some libraries don't allow arbitrary
+     * data to be signed/verified: this allows them to be used. Note: for
+     * this to work the RSA_public_decrypt() and RSA_private_encrypt() should
+     * *NOT* be used RSA_sign(), RSA_verify() should be used instead.
+     */
+    int (*rsa_sign) (int type,
+                     const unsigned char *m, unsigned int m_length,
+                     unsigned char *sigret, unsigned int *siglen,
+                     const RSA *rsa);
+    int (*rsa_verify) (int dtype, const unsigned char *m,
+                       unsigned int m_length, const unsigned char *sigbuf,
+                       unsigned int siglen, const RSA *rsa);
+    /*
+     * If this callback is NULL, the builtin software RSA key-gen will be
+     * used. This is for behavioural compatibility whilst the code gets
+     * rewired, but one day it would be nice to assume there are no such
+     * things as "builtin software" implementations.
+     */
+    int (*rsa_keygen) (RSA *rsa, int bits, BIGNUM *e, BN_GENCB *cb);
+};
+
+extern int int_rsa_verify(int dtype, const unsigned char *m,
+                          unsigned int m_len, unsigned char *rm,
+                          size_t *prm_len, const unsigned char *sigbuf,
+                          size_t siglen, RSA *rsa);
diff --git a/crypto/rsa/rsa_meth.c b/crypto/rsa/rsa_meth.c
new file mode 100644
index 000000000..9480abd70
--- /dev/null
+++ b/crypto/rsa/rsa_meth.c
@@ -0,0 +1,273 @@
+/*
+ * Copyright 2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "rsa_locl.h"
+#include 
+
+RSA_METHOD *RSA_meth_new(const char *name, int flags)
+{
+    RSA_METHOD *meth = OPENSSL_zalloc(sizeof(*meth));
+
+    if (meth != NULL) {
+        meth->flags = flags;
+
+        meth->name = OPENSSL_strdup(name);
+        if (meth->name != NULL)
+            return meth;
+
+        OPENSSL_free(meth);
+    }
+
+    RSAerr(RSA_F_RSA_METH_NEW, ERR_R_MALLOC_FAILURE);
+    return NULL;
+}
+
+void RSA_meth_free(RSA_METHOD *meth)
+{
+    if (meth != NULL) {
+        OPENSSL_free(meth->name);
+        OPENSSL_free(meth);
+    }
+}
+
+RSA_METHOD *RSA_meth_dup(const RSA_METHOD *meth)
+{
+    RSA_METHOD *ret = OPENSSL_malloc(sizeof(*ret));
+
+    if (ret != NULL) {
+        memcpy(ret, meth, sizeof(*meth));
+
+        ret->name = OPENSSL_strdup(meth->name);
+        if (ret->name != NULL)
+            return ret;
+
+        OPENSSL_free(ret);
+    }
+
+    RSAerr(RSA_F_RSA_METH_DUP, ERR_R_MALLOC_FAILURE);
+    return NULL;
+}
+
+const char *RSA_meth_get0_name(const RSA_METHOD *meth)
+{
+    return meth->name;
+}
+
+int RSA_meth_set1_name(RSA_METHOD *meth, const char *name)
+{
+    char *tmpname = OPENSSL_strdup(name);
+
+    if (tmpname == NULL) {
+        RSAerr(RSA_F_RSA_METH_SET1_NAME, ERR_R_MALLOC_FAILURE);
+        return 0;
+    }
+
+    OPENSSL_free(meth->name);
+    meth->name = tmpname;
+
+    return 1;
+}
+
+int RSA_meth_get_flags(RSA_METHOD *meth)
+{
+    return meth->flags;
+}
+
+int RSA_meth_set_flags(RSA_METHOD *meth, int flags)
+{
+    meth->flags = flags;
+    return 1;
+}
+
+void *RSA_meth_get0_app_data(const RSA_METHOD *meth)
+{
+    return meth->app_data;
+}
+
+int RSA_meth_set0_app_data(RSA_METHOD *meth, void *app_data)
+{
+    meth->app_data = app_data;
+    return 1;
+}
+
+int (*RSA_meth_get_pub_enc(const RSA_METHOD *meth))
+    (int flen, const unsigned char *from,
+     unsigned char *to, RSA *rsa, int padding)
+{
+    return meth->rsa_pub_enc;
+}
+
+int RSA_meth_set_pub_enc(RSA_METHOD *meth,
+                         int (*pub_enc) (int flen, const unsigned char *from,
+                                         unsigned char *to, RSA *rsa,
+                                         int padding))
+{
+    meth->rsa_pub_enc = pub_enc;
+    return 1;
+}
+
+int (*RSA_meth_get_pub_dec(const RSA_METHOD *meth))
+    (int flen, const unsigned char *from,
+     unsigned char *to, RSA *rsa, int padding)
+{
+    return meth->rsa_pub_dec;
+}
+
+int RSA_meth_set_pub_dec(RSA_METHOD *meth,
+                         int (*pub_dec) (int flen, const unsigned char *from,
+                                         unsigned char *to, RSA *rsa,
+                                         int padding))
+{
+    meth->rsa_pub_dec = pub_dec;
+    return 1;
+}
+
+int (*RSA_meth_get_priv_enc(const RSA_METHOD *meth))
+    (int flen, const unsigned char *from,
+     unsigned char *to, RSA *rsa, int padding)
+{
+    return meth->rsa_priv_enc;
+}
+
+int RSA_meth_set_priv_enc(RSA_METHOD *meth,
+                          int (*priv_enc) (int flen, const unsigned char *from,
+                                           unsigned char *to, RSA *rsa,
+                                           int padding))
+{
+    meth->rsa_priv_enc = priv_enc;
+    return 1;
+}
+
+int (*RSA_meth_get_priv_dec(const RSA_METHOD *meth))
+    (int flen, const unsigned char *from,
+     unsigned char *to, RSA *rsa, int padding)
+{
+    return meth->rsa_priv_dec;
+}
+
+int RSA_meth_set_priv_dec(RSA_METHOD *meth,
+                          int (*priv_dec) (int flen, const unsigned char *from,
+                                           unsigned char *to, RSA *rsa,
+                                           int padding))
+{
+    meth->rsa_priv_dec = priv_dec;
+    return 1;
+}
+
+    /* Can be null */
+int (*RSA_meth_get_mod_exp(const RSA_METHOD *meth))
+    (BIGNUM *r0, const BIGNUM *I, RSA *rsa, BN_CTX *ctx)
+{
+    return meth->rsa_mod_exp;
+}
+
+int RSA_meth_set_mod_exp(RSA_METHOD *meth,
+                         int (*mod_exp) (BIGNUM *r0, const BIGNUM *I, RSA *rsa,
+                                         BN_CTX *ctx))
+{
+    meth->rsa_mod_exp = mod_exp;
+    return 1;
+}
+
+    /* Can be null */
+int (*RSA_meth_get_bn_mod_exp(const RSA_METHOD *meth))
+    (BIGNUM *r, const BIGNUM *a, const BIGNUM *p,
+     const BIGNUM *m, BN_CTX *ctx, BN_MONT_CTX *m_ctx)
+{
+    return meth->bn_mod_exp;
+}
+
+int RSA_meth_set_bn_mod_exp(RSA_METHOD *meth,
+                            int (*bn_mod_exp) (BIGNUM *r,
+                                               const BIGNUM *a,
+                                               const BIGNUM *p,
+                                               const BIGNUM *m,
+                                               BN_CTX *ctx,
+                                               BN_MONT_CTX *m_ctx))
+{
+    meth->bn_mod_exp = bn_mod_exp;
+    return 1;
+}
+
+    /* called at new */
+int (*RSA_meth_get_init(const RSA_METHOD *meth)) (RSA *rsa)
+{
+    return meth->init;
+}
+
+int RSA_meth_set_init(RSA_METHOD *meth, int (*init) (RSA *rsa))
+{
+    meth->init = init;
+    return 1;
+}
+
+    /* called at free */
+int (*RSA_meth_get_finish(const RSA_METHOD *meth)) (RSA *rsa)
+{
+    return meth->finish;
+}
+
+int RSA_meth_set_finish(RSA_METHOD *meth, int (*finish) (RSA *rsa))
+{
+    meth->finish = finish;
+    return 1;
+}
+
+int (*RSA_meth_get_sign(const RSA_METHOD *meth))
+    (int type,
+     const unsigned char *m, unsigned int m_length,
+     unsigned char *sigret, unsigned int *siglen,
+     const RSA *rsa)
+{
+    return meth->rsa_sign;
+}
+
+int RSA_meth_set_sign(RSA_METHOD *meth,
+                      int (*sign) (int type, const unsigned char *m,
+                                   unsigned int m_length,
+                                   unsigned char *sigret, unsigned int *siglen,
+                                   const RSA *rsa))
+{
+    meth->rsa_sign = sign;
+    return 1;
+}
+
+int (*RSA_meth_get_verify(const RSA_METHOD *meth))
+    (int dtype, const unsigned char *m,
+     unsigned int m_length, const unsigned char *sigbuf,
+     unsigned int siglen, const RSA *rsa)
+{
+    return meth->rsa_verify;
+}
+
+int RSA_meth_set_verify(RSA_METHOD *meth,
+                        int (*verify) (int dtype, const unsigned char *m,
+                                       unsigned int m_length,
+                                       const unsigned char *sigbuf,
+                                       unsigned int siglen, const RSA *rsa))
+{
+    meth->rsa_verify = verify;
+    return 1;
+}
+
+int (*RSA_meth_get_keygen(const RSA_METHOD *meth))
+    (RSA *rsa, int bits, BIGNUM *e, BN_GENCB *cb)
+{
+    return meth->rsa_keygen;
+}
+
+int RSA_meth_set_keygen(RSA_METHOD *meth,
+                        int (*keygen) (RSA *rsa, int bits, BIGNUM *e,
+                                       BN_GENCB *cb))
+{
+    meth->rsa_keygen = keygen;
+    return 1;
+}
+
diff --git a/crypto/rsa/rsa_none.c b/crypto/rsa/rsa_none.c
new file mode 100644
index 000000000..b78756d18
--- /dev/null
+++ b/crypto/rsa/rsa_none.c
@@ -0,0 +1,43 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "internal/cryptlib.h"
+#include 
+#include 
+
+int RSA_padding_add_none(unsigned char *to, int tlen,
+                         const unsigned char *from, int flen)
+{
+    if (flen > tlen) {
+        RSAerr(RSA_F_RSA_PADDING_ADD_NONE, RSA_R_DATA_TOO_LARGE_FOR_KEY_SIZE);
+        return (0);
+    }
+
+    if (flen < tlen) {
+        RSAerr(RSA_F_RSA_PADDING_ADD_NONE, RSA_R_DATA_TOO_SMALL_FOR_KEY_SIZE);
+        return (0);
+    }
+
+    memcpy(to, from, (unsigned int)flen);
+    return (1);
+}
+
+int RSA_padding_check_none(unsigned char *to, int tlen,
+                           const unsigned char *from, int flen, int num)
+{
+
+    if (flen > tlen) {
+        RSAerr(RSA_F_RSA_PADDING_CHECK_NONE, RSA_R_DATA_TOO_LARGE);
+        return (-1);
+    }
+
+    memset(to, 0, tlen - flen);
+    memcpy(to + tlen - flen, from, flen);
+    return (tlen);
+}
diff --git a/crypto/rsa/rsa_null.c b/crypto/rsa/rsa_null.c
new file mode 100644
index 000000000..d33949412
--- /dev/null
+++ b/crypto/rsa/rsa_null.c
@@ -0,0 +1,93 @@
+/*
+ * Copyright 1999-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include "rsa_locl.h"
+
+/*
+ * This is a dummy RSA implementation that just returns errors when called.
+ * It is designed to allow some RSA functions to work while stopping those
+ * covered by the RSA patent. That is RSA, encryption, decryption, signing
+ * and verify is not allowed but RSA key generation, key checking and other
+ * operations (like storing RSA keys) are permitted.
+ */
+
+static int RSA_null_public_encrypt(int flen, const unsigned char *from,
+                                   unsigned char *to, RSA *rsa, int padding);
+static int RSA_null_private_encrypt(int flen, const unsigned char *from,
+                                    unsigned char *to, RSA *rsa, int padding);
+static int RSA_null_public_decrypt(int flen, const unsigned char *from,
+                                   unsigned char *to, RSA *rsa, int padding);
+static int RSA_null_private_decrypt(int flen, const unsigned char *from,
+                                    unsigned char *to, RSA *rsa, int padding);
+static int RSA_null_init(RSA *rsa);
+static int RSA_null_finish(RSA *rsa);
+static RSA_METHOD rsa_null_meth = {
+    "Null RSA",
+    RSA_null_public_encrypt,
+    RSA_null_public_decrypt,
+    RSA_null_private_encrypt,
+    RSA_null_private_decrypt,
+    NULL,
+    NULL,
+    RSA_null_init,
+    RSA_null_finish,
+    0,
+    NULL,
+    NULL,
+    NULL,
+    NULL
+};
+
+const RSA_METHOD *RSA_null_method(void)
+{
+    return (&rsa_null_meth);
+}
+
+static int RSA_null_public_encrypt(int flen, const unsigned char *from,
+                                   unsigned char *to, RSA *rsa, int padding)
+{
+    RSAerr(RSA_F_RSA_NULL_PUBLIC_ENCRYPT, RSA_R_RSA_OPERATIONS_NOT_SUPPORTED);
+    return -1;
+}
+
+static int RSA_null_private_encrypt(int flen, const unsigned char *from,
+                                    unsigned char *to, RSA *rsa, int padding)
+{
+    RSAerr(RSA_F_RSA_NULL_PRIVATE_ENCRYPT,
+           RSA_R_RSA_OPERATIONS_NOT_SUPPORTED);
+    return -1;
+}
+
+static int RSA_null_private_decrypt(int flen, const unsigned char *from,
+                                    unsigned char *to, RSA *rsa, int padding)
+{
+    RSAerr(RSA_F_RSA_NULL_PRIVATE_DECRYPT,
+           RSA_R_RSA_OPERATIONS_NOT_SUPPORTED);
+    return -1;
+}
+
+static int RSA_null_public_decrypt(int flen, const unsigned char *from,
+                                   unsigned char *to, RSA *rsa, int padding)
+{
+    RSAerr(RSA_F_RSA_NULL_PUBLIC_DECRYPT, RSA_R_RSA_OPERATIONS_NOT_SUPPORTED);
+    return -1;
+}
+
+static int RSA_null_init(RSA *rsa)
+{
+    return (1);
+}
+
+static int RSA_null_finish(RSA *rsa)
+{
+    return (1);
+}
diff --git a/crypto/rsa/rsa_oaep.c b/crypto/rsa/rsa_oaep.c
new file mode 100644
index 000000000..868104f1b
--- /dev/null
+++ b/crypto/rsa/rsa_oaep.c
@@ -0,0 +1,286 @@
+/*
+ * Copyright 1999-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/* EME-OAEP as defined in RFC 2437 (PKCS #1 v2.0) */
+
+/*
+ * See Victor Shoup, "OAEP reconsidered," Nov. 2000,  for problems with the security
+ * proof for the original OAEP scheme, which EME-OAEP is based on. A new
+ * proof can be found in E. Fujisaki, T. Okamoto, D. Pointcheval, J. Stern,
+ * "RSA-OEAP is Still Alive!", Dec. 2000, . The new proof has stronger requirements
+ * for the underlying permutation: "partial-one-wayness" instead of
+ * one-wayness.  For the RSA function, this is an equivalent notion.
+ */
+
+#include "internal/constant_time_locl.h"
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+#include 
+#include 
+#include "rsa_locl.h"
+
+int RSA_padding_add_PKCS1_OAEP(unsigned char *to, int tlen,
+                               const unsigned char *from, int flen,
+                               const unsigned char *param, int plen)
+{
+    return RSA_padding_add_PKCS1_OAEP_mgf1(to, tlen, from, flen,
+                                           param, plen, NULL, NULL);
+}
+
+int RSA_padding_add_PKCS1_OAEP_mgf1(unsigned char *to, int tlen,
+                                    const unsigned char *from, int flen,
+                                    const unsigned char *param, int plen,
+                                    const EVP_MD *md, const EVP_MD *mgf1md)
+{
+    int i, emlen = tlen - 1;
+    unsigned char *db, *seed;
+    unsigned char *dbmask, seedmask[EVP_MAX_MD_SIZE];
+    int mdlen;
+
+    if (md == NULL)
+        md = EVP_sha1();
+    if (mgf1md == NULL)
+        mgf1md = md;
+
+    mdlen = EVP_MD_size(md);
+
+    if (flen > emlen - 2 * mdlen - 1) {
+        RSAerr(RSA_F_RSA_PADDING_ADD_PKCS1_OAEP_MGF1,
+               RSA_R_DATA_TOO_LARGE_FOR_KEY_SIZE);
+        return 0;
+    }
+
+    if (emlen < 2 * mdlen + 1) {
+        RSAerr(RSA_F_RSA_PADDING_ADD_PKCS1_OAEP_MGF1,
+               RSA_R_KEY_SIZE_TOO_SMALL);
+        return 0;
+    }
+
+    to[0] = 0;
+    seed = to + 1;
+    db = to + mdlen + 1;
+
+    if (!EVP_Digest((void *)param, plen, db, NULL, md, NULL))
+        return 0;
+    memset(db + mdlen, 0, emlen - flen - 2 * mdlen - 1);
+    db[emlen - flen - mdlen - 1] = 0x01;
+    memcpy(db + emlen - flen - mdlen, from, (unsigned int)flen);
+    if (RAND_bytes(seed, mdlen) <= 0)
+        return 0;
+#ifdef PKCS_TESTVECT
+    memcpy(seed,
+           "\xaa\xfd\x12\xf6\x59\xca\xe6\x34\x89\xb4\x79\xe5\x07\x6d\xde\xc2\xf0\x6c\xb5\x8f",
+           20);
+#endif
+
+    dbmask = OPENSSL_malloc(emlen - mdlen);
+    if (dbmask == NULL) {
+        RSAerr(RSA_F_RSA_PADDING_ADD_PKCS1_OAEP_MGF1, ERR_R_MALLOC_FAILURE);
+        return 0;
+    }
+
+    if (PKCS1_MGF1(dbmask, emlen - mdlen, seed, mdlen, mgf1md) < 0)
+        goto err;
+    for (i = 0; i < emlen - mdlen; i++)
+        db[i] ^= dbmask[i];
+
+    if (PKCS1_MGF1(seedmask, mdlen, db, emlen - mdlen, mgf1md) < 0)
+        goto err;
+    for (i = 0; i < mdlen; i++)
+        seed[i] ^= seedmask[i];
+
+    OPENSSL_free(dbmask);
+    return 1;
+
+ err:
+    OPENSSL_free(dbmask);
+    return 0;
+}
+
+int RSA_padding_check_PKCS1_OAEP(unsigned char *to, int tlen,
+                                 const unsigned char *from, int flen, int num,
+                                 const unsigned char *param, int plen)
+{
+    return RSA_padding_check_PKCS1_OAEP_mgf1(to, tlen, from, flen, num,
+                                             param, plen, NULL, NULL);
+}
+
+int RSA_padding_check_PKCS1_OAEP_mgf1(unsigned char *to, int tlen,
+                                      const unsigned char *from, int flen,
+                                      int num, const unsigned char *param,
+                                      int plen, const EVP_MD *md,
+                                      const EVP_MD *mgf1md)
+{
+    int i, dblen, mlen = -1, one_index = 0, msg_index;
+    unsigned int good, found_one_byte;
+    const unsigned char *maskedseed, *maskeddb;
+    /*
+     * |em| is the encoded message, zero-padded to exactly |num| bytes: em =
+     * Y || maskedSeed || maskedDB
+     */
+    unsigned char *db = NULL, *em = NULL, seed[EVP_MAX_MD_SIZE],
+        phash[EVP_MAX_MD_SIZE];
+    int mdlen;
+
+    if (md == NULL)
+        md = EVP_sha1();
+    if (mgf1md == NULL)
+        mgf1md = md;
+
+    mdlen = EVP_MD_size(md);
+
+    if (tlen <= 0 || flen <= 0)
+        return -1;
+    /*
+     * |num| is the length of the modulus; |flen| is the length of the
+     * encoded message. Therefore, for any |from| that was obtained by
+     * decrypting a ciphertext, we must have |flen| <= |num|. Similarly,
+     * num < 2 * mdlen + 2 must hold for the modulus irrespective of
+     * the ciphertext, see PKCS #1 v2.2, section 7.1.2.
+     * This does not leak any side-channel information.
+     */
+    if (num < flen || num < 2 * mdlen + 2)
+        goto decoding_err;
+
+    dblen = num - mdlen - 1;
+    db = OPENSSL_malloc(dblen);
+    em = OPENSSL_malloc(num);
+    if (db == NULL || em == NULL) {
+        RSAerr(RSA_F_RSA_PADDING_CHECK_PKCS1_OAEP_MGF1, ERR_R_MALLOC_FAILURE);
+        goto cleanup;
+    }
+
+    /*
+     * Always do this zero-padding copy (even when num == flen) to avoid
+     * leaking that information. The copy still leaks some side-channel
+     * information, but it's impossible to have a fixed  memory access
+     * pattern since we can't read out of the bounds of |from|.
+     *
+     * TODO(emilia): Consider porting BN_bn2bin_padded from BoringSSL.
+     */
+    memset(em, 0, num);
+    memcpy(em + num - flen, from, flen);
+
+    /*
+     * The first byte must be zero, however we must not leak if this is
+     * true. See James H. Manger, "A Chosen Ciphertext  Attack on RSA
+     * Optimal Asymmetric Encryption Padding (OAEP) [...]", CRYPTO 2001).
+     */
+    good = constant_time_is_zero(em[0]);
+
+    maskedseed = em + 1;
+    maskeddb = em + 1 + mdlen;
+
+    if (PKCS1_MGF1(seed, mdlen, maskeddb, dblen, mgf1md))
+        goto cleanup;
+    for (i = 0; i < mdlen; i++)
+        seed[i] ^= maskedseed[i];
+
+    if (PKCS1_MGF1(db, dblen, seed, mdlen, mgf1md))
+        goto cleanup;
+    for (i = 0; i < dblen; i++)
+        db[i] ^= maskeddb[i];
+
+    if (!EVP_Digest((void *)param, plen, phash, NULL, md, NULL))
+        goto cleanup;
+
+    good &= constant_time_is_zero(CRYPTO_memcmp(db, phash, mdlen));
+
+    found_one_byte = 0;
+    for (i = mdlen; i < dblen; i++) {
+        /*
+         * Padding consists of a number of 0-bytes, followed by a 1.
+         */
+        unsigned int equals1 = constant_time_eq(db[i], 1);
+        unsigned int equals0 = constant_time_is_zero(db[i]);
+        one_index = constant_time_select_int(~found_one_byte & equals1,
+                                             i, one_index);
+        found_one_byte |= equals1;
+        good &= (found_one_byte | equals0);
+    }
+
+    good &= found_one_byte;
+
+    /*
+     * At this point |good| is zero unless the plaintext was valid,
+     * so plaintext-awareness ensures timing side-channels are no longer a
+     * concern.
+     */
+    if (!good)
+        goto decoding_err;
+
+    msg_index = one_index + 1;
+    mlen = dblen - msg_index;
+
+    if (tlen < mlen) {
+        RSAerr(RSA_F_RSA_PADDING_CHECK_PKCS1_OAEP_MGF1, RSA_R_DATA_TOO_LARGE);
+        mlen = -1;
+    } else {
+        memcpy(to, db + msg_index, mlen);
+        goto cleanup;
+    }
+
+ decoding_err:
+    /*
+     * To avoid chosen ciphertext attacks, the error message should not
+     * reveal which kind of decoding error happened.
+     */
+    RSAerr(RSA_F_RSA_PADDING_CHECK_PKCS1_OAEP_MGF1,
+           RSA_R_OAEP_DECODING_ERROR);
+ cleanup:
+    OPENSSL_free(db);
+    OPENSSL_free(em);
+    return mlen;
+}
+
+int PKCS1_MGF1(unsigned char *mask, long len,
+               const unsigned char *seed, long seedlen, const EVP_MD *dgst)
+{
+    long i, outlen = 0;
+    unsigned char cnt[4];
+    EVP_MD_CTX *c = EVP_MD_CTX_new();
+    unsigned char md[EVP_MAX_MD_SIZE];
+    int mdlen;
+    int rv = -1;
+
+    if (c == NULL)
+        goto err;
+    mdlen = EVP_MD_size(dgst);
+    if (mdlen < 0)
+        goto err;
+    for (i = 0; outlen < len; i++) {
+        cnt[0] = (unsigned char)((i >> 24) & 255);
+        cnt[1] = (unsigned char)((i >> 16) & 255);
+        cnt[2] = (unsigned char)((i >> 8)) & 255;
+        cnt[3] = (unsigned char)(i & 255);
+        if (!EVP_DigestInit_ex(c, dgst, NULL)
+            || !EVP_DigestUpdate(c, seed, seedlen)
+            || !EVP_DigestUpdate(c, cnt, 4))
+            goto err;
+        if (outlen + mdlen <= len) {
+            if (!EVP_DigestFinal_ex(c, mask + outlen, NULL))
+                goto err;
+            outlen += mdlen;
+        } else {
+            if (!EVP_DigestFinal_ex(c, md, NULL))
+                goto err;
+            memcpy(mask + outlen, md, len - outlen);
+            outlen = len;
+        }
+    }
+    rv = 0;
+ err:
+    EVP_MD_CTX_free(c);
+    return rv;
+}
diff --git a/crypto/rsa/rsa_ossl.c b/crypto/rsa/rsa_ossl.c
new file mode 100644
index 000000000..782606645
--- /dev/null
+++ b/crypto/rsa/rsa_ossl.c
@@ -0,0 +1,790 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "internal/cryptlib.h"
+#include "internal/bn_int.h"
+#include "rsa_locl.h"
+
+#ifndef RSA_NULL
+
+static int rsa_ossl_public_encrypt(int flen, const unsigned char *from,
+                                  unsigned char *to, RSA *rsa, int padding);
+static int rsa_ossl_private_encrypt(int flen, const unsigned char *from,
+                                   unsigned char *to, RSA *rsa, int padding);
+static int rsa_ossl_public_decrypt(int flen, const unsigned char *from,
+                                  unsigned char *to, RSA *rsa, int padding);
+static int rsa_ossl_private_decrypt(int flen, const unsigned char *from,
+                                   unsigned char *to, RSA *rsa, int padding);
+static int rsa_ossl_mod_exp(BIGNUM *r0, const BIGNUM *i, RSA *rsa,
+                           BN_CTX *ctx);
+static int rsa_ossl_init(RSA *rsa);
+static int rsa_ossl_finish(RSA *rsa);
+static RSA_METHOD rsa_pkcs1_ossl_meth = {
+    "OpenSSL PKCS#1 RSA (from Eric Young)",
+    rsa_ossl_public_encrypt,
+    rsa_ossl_public_decrypt,     /* signature verification */
+    rsa_ossl_private_encrypt,    /* signing */
+    rsa_ossl_private_decrypt,
+    rsa_ossl_mod_exp,
+    BN_mod_exp_mont,            /* XXX probably we should not use Montgomery
+                                 * if e == 3 */
+    rsa_ossl_init,
+    rsa_ossl_finish,
+    RSA_FLAG_FIPS_METHOD,       /* flags */
+    NULL,
+    0,                          /* rsa_sign */
+    0,                          /* rsa_verify */
+    NULL                        /* rsa_keygen */
+};
+
+const RSA_METHOD *RSA_PKCS1_OpenSSL(void)
+{
+    return &rsa_pkcs1_ossl_meth;
+}
+
+static int rsa_ossl_public_encrypt(int flen, const unsigned char *from,
+                                  unsigned char *to, RSA *rsa, int padding)
+{
+    BIGNUM *f, *ret;
+    int i, j, k, num = 0, r = -1;
+    unsigned char *buf = NULL;
+    BN_CTX *ctx = NULL;
+
+    if (BN_num_bits(rsa->n) > OPENSSL_RSA_MAX_MODULUS_BITS) {
+        RSAerr(RSA_F_RSA_OSSL_PUBLIC_ENCRYPT, RSA_R_MODULUS_TOO_LARGE);
+        return -1;
+    }
+
+    if (BN_ucmp(rsa->n, rsa->e) <= 0) {
+        RSAerr(RSA_F_RSA_OSSL_PUBLIC_ENCRYPT, RSA_R_BAD_E_VALUE);
+        return -1;
+    }
+
+    /* for large moduli, enforce exponent limit */
+    if (BN_num_bits(rsa->n) > OPENSSL_RSA_SMALL_MODULUS_BITS) {
+        if (BN_num_bits(rsa->e) > OPENSSL_RSA_MAX_PUBEXP_BITS) {
+            RSAerr(RSA_F_RSA_OSSL_PUBLIC_ENCRYPT, RSA_R_BAD_E_VALUE);
+            return -1;
+        }
+    }
+
+    if ((ctx = BN_CTX_new()) == NULL)
+        goto err;
+    BN_CTX_start(ctx);
+    f = BN_CTX_get(ctx);
+    ret = BN_CTX_get(ctx);
+    num = BN_num_bytes(rsa->n);
+    buf = OPENSSL_malloc(num);
+    if (f == NULL || ret == NULL || buf == NULL) {
+        RSAerr(RSA_F_RSA_OSSL_PUBLIC_ENCRYPT, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+
+    switch (padding) {
+    case RSA_PKCS1_PADDING:
+        i = RSA_padding_add_PKCS1_type_2(buf, num, from, flen);
+        break;
+    case RSA_PKCS1_OAEP_PADDING:
+        i = RSA_padding_add_PKCS1_OAEP(buf, num, from, flen, NULL, 0);
+        break;
+    case RSA_SSLV23_PADDING:
+        i = RSA_padding_add_SSLv23(buf, num, from, flen);
+        break;
+    case RSA_NO_PADDING:
+        i = RSA_padding_add_none(buf, num, from, flen);
+        break;
+    default:
+        RSAerr(RSA_F_RSA_OSSL_PUBLIC_ENCRYPT, RSA_R_UNKNOWN_PADDING_TYPE);
+        goto err;
+    }
+    if (i <= 0)
+        goto err;
+
+    if (BN_bin2bn(buf, num, f) == NULL)
+        goto err;
+
+    if (BN_ucmp(f, rsa->n) >= 0) {
+        /* usually the padding functions would catch this */
+        RSAerr(RSA_F_RSA_OSSL_PUBLIC_ENCRYPT,
+               RSA_R_DATA_TOO_LARGE_FOR_MODULUS);
+        goto err;
+    }
+
+    if (rsa->flags & RSA_FLAG_CACHE_PUBLIC)
+        if (!BN_MONT_CTX_set_locked
+            (&rsa->_method_mod_n, rsa->lock, rsa->n, ctx))
+            goto err;
+
+    if (!rsa->meth->bn_mod_exp(ret, f, rsa->e, rsa->n, ctx,
+                               rsa->_method_mod_n))
+        goto err;
+
+    /*
+     * put in leading 0 bytes if the number is less than the length of the
+     * modulus
+     */
+    j = BN_num_bytes(ret);
+    i = BN_bn2bin(ret, &(to[num - j]));
+    for (k = 0; k < (num - i); k++)
+        to[k] = 0;
+
+    r = num;
+ err:
+    if (ctx != NULL)
+        BN_CTX_end(ctx);
+    BN_CTX_free(ctx);
+    OPENSSL_clear_free(buf, num);
+    return (r);
+}
+
+static BN_BLINDING *rsa_get_blinding(RSA *rsa, int *local, BN_CTX *ctx)
+{
+    BN_BLINDING *ret;
+
+    CRYPTO_THREAD_write_lock(rsa->lock);
+
+    if (rsa->blinding == NULL) {
+        rsa->blinding = RSA_setup_blinding(rsa, ctx);
+    }
+
+    ret = rsa->blinding;
+    if (ret == NULL)
+        goto err;
+
+    if (BN_BLINDING_is_current_thread(ret)) {
+        /* rsa->blinding is ours! */
+
+        *local = 1;
+    } else {
+        /* resort to rsa->mt_blinding instead */
+
+        /*
+         * instructs rsa_blinding_convert(), rsa_blinding_invert() that the
+         * BN_BLINDING is shared, meaning that accesses require locks, and
+         * that the blinding factor must be stored outside the BN_BLINDING
+         */
+        *local = 0;
+
+        if (rsa->mt_blinding == NULL) {
+            rsa->mt_blinding = RSA_setup_blinding(rsa, ctx);
+        }
+        ret = rsa->mt_blinding;
+    }
+
+ err:
+    CRYPTO_THREAD_unlock(rsa->lock);
+    return ret;
+}
+
+static int rsa_blinding_convert(BN_BLINDING *b, BIGNUM *f, BIGNUM *unblind,
+                                BN_CTX *ctx)
+{
+    if (unblind == NULL)
+        /*
+         * Local blinding: store the unblinding factor in BN_BLINDING.
+         */
+        return BN_BLINDING_convert_ex(f, NULL, b, ctx);
+    else {
+        /*
+         * Shared blinding: store the unblinding factor outside BN_BLINDING.
+         */
+        int ret;
+
+        BN_BLINDING_lock(b);
+        ret = BN_BLINDING_convert_ex(f, unblind, b, ctx);
+        BN_BLINDING_unlock(b);
+
+        return ret;
+    }
+}
+
+static int rsa_blinding_invert(BN_BLINDING *b, BIGNUM *f, BIGNUM *unblind,
+                               BN_CTX *ctx)
+{
+    /*
+     * For local blinding, unblind is set to NULL, and BN_BLINDING_invert_ex
+     * will use the unblinding factor stored in BN_BLINDING. If BN_BLINDING
+     * is shared between threads, unblind must be non-null:
+     * BN_BLINDING_invert_ex will then use the local unblinding factor, and
+     * will only read the modulus from BN_BLINDING. In both cases it's safe
+     * to access the blinding without a lock.
+     */
+    return BN_BLINDING_invert_ex(f, unblind, b, ctx);
+}
+
+/* signing */
+static int rsa_ossl_private_encrypt(int flen, const unsigned char *from,
+                                   unsigned char *to, RSA *rsa, int padding)
+{
+    BIGNUM *f, *ret, *res;
+    int i, j, k, num = 0, r = -1;
+    unsigned char *buf = NULL;
+    BN_CTX *ctx = NULL;
+    int local_blinding = 0;
+    /*
+     * Used only if the blinding structure is shared. A non-NULL unblind
+     * instructs rsa_blinding_convert() and rsa_blinding_invert() to store
+     * the unblinding factor outside the blinding structure.
+     */
+    BIGNUM *unblind = NULL;
+    BN_BLINDING *blinding = NULL;
+
+    if ((ctx = BN_CTX_new()) == NULL)
+        goto err;
+    BN_CTX_start(ctx);
+    f = BN_CTX_get(ctx);
+    ret = BN_CTX_get(ctx);
+    num = BN_num_bytes(rsa->n);
+    buf = OPENSSL_malloc(num);
+    if (f == NULL || ret == NULL || buf == NULL) {
+        RSAerr(RSA_F_RSA_OSSL_PRIVATE_ENCRYPT, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+
+    switch (padding) {
+    case RSA_PKCS1_PADDING:
+        i = RSA_padding_add_PKCS1_type_1(buf, num, from, flen);
+        break;
+    case RSA_X931_PADDING:
+        i = RSA_padding_add_X931(buf, num, from, flen);
+        break;
+    case RSA_NO_PADDING:
+        i = RSA_padding_add_none(buf, num, from, flen);
+        break;
+    case RSA_SSLV23_PADDING:
+    default:
+        RSAerr(RSA_F_RSA_OSSL_PRIVATE_ENCRYPT, RSA_R_UNKNOWN_PADDING_TYPE);
+        goto err;
+    }
+    if (i <= 0)
+        goto err;
+
+    if (BN_bin2bn(buf, num, f) == NULL)
+        goto err;
+
+    if (BN_ucmp(f, rsa->n) >= 0) {
+        /* usually the padding functions would catch this */
+        RSAerr(RSA_F_RSA_OSSL_PRIVATE_ENCRYPT,
+               RSA_R_DATA_TOO_LARGE_FOR_MODULUS);
+        goto err;
+    }
+
+    if (!(rsa->flags & RSA_FLAG_NO_BLINDING)) {
+        blinding = rsa_get_blinding(rsa, &local_blinding, ctx);
+        if (blinding == NULL) {
+            RSAerr(RSA_F_RSA_OSSL_PRIVATE_ENCRYPT, ERR_R_INTERNAL_ERROR);
+            goto err;
+        }
+    }
+
+    if (blinding != NULL) {
+        if (!local_blinding && ((unblind = BN_CTX_get(ctx)) == NULL)) {
+            RSAerr(RSA_F_RSA_OSSL_PRIVATE_ENCRYPT, ERR_R_MALLOC_FAILURE);
+            goto err;
+        }
+        if (!rsa_blinding_convert(blinding, f, unblind, ctx))
+            goto err;
+    }
+
+    if ((rsa->flags & RSA_FLAG_EXT_PKEY) ||
+        ((rsa->p != NULL) &&
+         (rsa->q != NULL) &&
+         (rsa->dmp1 != NULL) && (rsa->dmq1 != NULL) && (rsa->iqmp != NULL))) {
+        if (!rsa->meth->rsa_mod_exp(ret, f, rsa, ctx))
+            goto err;
+    } else {
+        BIGNUM *d = BN_new();
+        if (d == NULL) {
+            RSAerr(RSA_F_RSA_OSSL_PRIVATE_ENCRYPT, ERR_R_MALLOC_FAILURE);
+            goto err;
+        }
+        BN_with_flags(d, rsa->d, BN_FLG_CONSTTIME);
+
+        if (rsa->flags & RSA_FLAG_CACHE_PUBLIC)
+            if (!BN_MONT_CTX_set_locked
+                (&rsa->_method_mod_n, rsa->lock, rsa->n, ctx)) {
+                BN_free(d);
+                goto err;
+            }
+
+        if (!rsa->meth->bn_mod_exp(ret, f, d, rsa->n, ctx,
+                                   rsa->_method_mod_n)) {
+            BN_free(d);
+            goto err;
+        }
+        /* We MUST free d before any further use of rsa->d */
+        BN_free(d);
+    }
+
+    if (blinding)
+        if (!rsa_blinding_invert(blinding, ret, unblind, ctx))
+            goto err;
+
+    if (padding == RSA_X931_PADDING) {
+        BN_sub(f, rsa->n, ret);
+        if (BN_cmp(ret, f) > 0)
+            res = f;
+        else
+            res = ret;
+    } else
+        res = ret;
+
+    /*
+     * put in leading 0 bytes if the number is less than the length of the
+     * modulus
+     */
+    j = BN_num_bytes(res);
+    i = BN_bn2bin(res, &(to[num - j]));
+    for (k = 0; k < (num - i); k++)
+        to[k] = 0;
+
+    r = num;
+ err:
+    if (ctx != NULL)
+        BN_CTX_end(ctx);
+    BN_CTX_free(ctx);
+    OPENSSL_clear_free(buf, num);
+    return (r);
+}
+
+static int rsa_ossl_private_decrypt(int flen, const unsigned char *from,
+                                   unsigned char *to, RSA *rsa, int padding)
+{
+    BIGNUM *f, *ret;
+    int j, num = 0, r = -1;
+    unsigned char *p;
+    unsigned char *buf = NULL;
+    BN_CTX *ctx = NULL;
+    int local_blinding = 0;
+    /*
+     * Used only if the blinding structure is shared. A non-NULL unblind
+     * instructs rsa_blinding_convert() and rsa_blinding_invert() to store
+     * the unblinding factor outside the blinding structure.
+     */
+    BIGNUM *unblind = NULL;
+    BN_BLINDING *blinding = NULL;
+
+    if ((ctx = BN_CTX_new()) == NULL)
+        goto err;
+    BN_CTX_start(ctx);
+    f = BN_CTX_get(ctx);
+    ret = BN_CTX_get(ctx);
+    num = BN_num_bytes(rsa->n);
+    buf = OPENSSL_malloc(num);
+    if (f == NULL || ret == NULL || buf == NULL) {
+        RSAerr(RSA_F_RSA_OSSL_PRIVATE_DECRYPT, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+
+    /*
+     * This check was for equality but PGP does evil things and chops off the
+     * top '0' bytes
+     */
+    if (flen > num) {
+        RSAerr(RSA_F_RSA_OSSL_PRIVATE_DECRYPT,
+               RSA_R_DATA_GREATER_THAN_MOD_LEN);
+        goto err;
+    }
+
+    /* make data into a big number */
+    if (BN_bin2bn(from, (int)flen, f) == NULL)
+        goto err;
+
+    if (BN_ucmp(f, rsa->n) >= 0) {
+        RSAerr(RSA_F_RSA_OSSL_PRIVATE_DECRYPT,
+               RSA_R_DATA_TOO_LARGE_FOR_MODULUS);
+        goto err;
+    }
+
+    if (!(rsa->flags & RSA_FLAG_NO_BLINDING)) {
+        blinding = rsa_get_blinding(rsa, &local_blinding, ctx);
+        if (blinding == NULL) {
+            RSAerr(RSA_F_RSA_OSSL_PRIVATE_DECRYPT, ERR_R_INTERNAL_ERROR);
+            goto err;
+        }
+    }
+
+    if (blinding != NULL) {
+        if (!local_blinding && ((unblind = BN_CTX_get(ctx)) == NULL)) {
+            RSAerr(RSA_F_RSA_OSSL_PRIVATE_DECRYPT, ERR_R_MALLOC_FAILURE);
+            goto err;
+        }
+        if (!rsa_blinding_convert(blinding, f, unblind, ctx))
+            goto err;
+    }
+
+    /* do the decrypt */
+    if ((rsa->flags & RSA_FLAG_EXT_PKEY) ||
+        ((rsa->p != NULL) &&
+         (rsa->q != NULL) &&
+         (rsa->dmp1 != NULL) && (rsa->dmq1 != NULL) && (rsa->iqmp != NULL))) {
+        if (!rsa->meth->rsa_mod_exp(ret, f, rsa, ctx))
+            goto err;
+    } else {
+        BIGNUM *d = BN_new();
+        if (d == NULL) {
+            RSAerr(RSA_F_RSA_OSSL_PRIVATE_DECRYPT, ERR_R_MALLOC_FAILURE);
+            goto err;
+        }
+        BN_with_flags(d, rsa->d, BN_FLG_CONSTTIME);
+
+        if (rsa->flags & RSA_FLAG_CACHE_PUBLIC)
+            if (!BN_MONT_CTX_set_locked
+                (&rsa->_method_mod_n, rsa->lock, rsa->n, ctx)) {
+                BN_free(d);
+                goto err;
+            }
+        if (!rsa->meth->bn_mod_exp(ret, f, d, rsa->n, ctx,
+                                   rsa->_method_mod_n)) {
+            BN_free(d);
+            goto err;
+        }
+        /* We MUST free d before any further use of rsa->d */
+        BN_free(d);
+    }
+
+    if (blinding)
+        if (!rsa_blinding_invert(blinding, ret, unblind, ctx))
+            goto err;
+
+    p = buf;
+    j = BN_bn2bin(ret, p);      /* j is only used with no-padding mode */
+
+    switch (padding) {
+    case RSA_PKCS1_PADDING:
+        r = RSA_padding_check_PKCS1_type_2(to, num, buf, j, num);
+        break;
+    case RSA_PKCS1_OAEP_PADDING:
+        r = RSA_padding_check_PKCS1_OAEP(to, num, buf, j, num, NULL, 0);
+        break;
+    case RSA_SSLV23_PADDING:
+        r = RSA_padding_check_SSLv23(to, num, buf, j, num);
+        break;
+    case RSA_NO_PADDING:
+        r = RSA_padding_check_none(to, num, buf, j, num);
+        break;
+    default:
+        RSAerr(RSA_F_RSA_OSSL_PRIVATE_DECRYPT, RSA_R_UNKNOWN_PADDING_TYPE);
+        goto err;
+    }
+    if (r < 0)
+        RSAerr(RSA_F_RSA_OSSL_PRIVATE_DECRYPT, RSA_R_PADDING_CHECK_FAILED);
+
+ err:
+    if (ctx != NULL)
+        BN_CTX_end(ctx);
+    BN_CTX_free(ctx);
+    OPENSSL_clear_free(buf, num);
+    return (r);
+}
+
+/* signature verification */
+static int rsa_ossl_public_decrypt(int flen, const unsigned char *from,
+                                  unsigned char *to, RSA *rsa, int padding)
+{
+    BIGNUM *f, *ret;
+    int i, num = 0, r = -1;
+    unsigned char *p;
+    unsigned char *buf = NULL;
+    BN_CTX *ctx = NULL;
+
+    if (BN_num_bits(rsa->n) > OPENSSL_RSA_MAX_MODULUS_BITS) {
+        RSAerr(RSA_F_RSA_OSSL_PUBLIC_DECRYPT, RSA_R_MODULUS_TOO_LARGE);
+        return -1;
+    }
+
+    if (BN_ucmp(rsa->n, rsa->e) <= 0) {
+        RSAerr(RSA_F_RSA_OSSL_PUBLIC_DECRYPT, RSA_R_BAD_E_VALUE);
+        return -1;
+    }
+
+    /* for large moduli, enforce exponent limit */
+    if (BN_num_bits(rsa->n) > OPENSSL_RSA_SMALL_MODULUS_BITS) {
+        if (BN_num_bits(rsa->e) > OPENSSL_RSA_MAX_PUBEXP_BITS) {
+            RSAerr(RSA_F_RSA_OSSL_PUBLIC_DECRYPT, RSA_R_BAD_E_VALUE);
+            return -1;
+        }
+    }
+
+    if ((ctx = BN_CTX_new()) == NULL)
+        goto err;
+    BN_CTX_start(ctx);
+    f = BN_CTX_get(ctx);
+    ret = BN_CTX_get(ctx);
+    num = BN_num_bytes(rsa->n);
+    buf = OPENSSL_malloc(num);
+    if (f == NULL || ret == NULL || buf == NULL) {
+        RSAerr(RSA_F_RSA_OSSL_PUBLIC_DECRYPT, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+
+    /*
+     * This check was for equality but PGP does evil things and chops off the
+     * top '0' bytes
+     */
+    if (flen > num) {
+        RSAerr(RSA_F_RSA_OSSL_PUBLIC_DECRYPT, RSA_R_DATA_GREATER_THAN_MOD_LEN);
+        goto err;
+    }
+
+    if (BN_bin2bn(from, flen, f) == NULL)
+        goto err;
+
+    if (BN_ucmp(f, rsa->n) >= 0) {
+        RSAerr(RSA_F_RSA_OSSL_PUBLIC_DECRYPT,
+               RSA_R_DATA_TOO_LARGE_FOR_MODULUS);
+        goto err;
+    }
+
+    if (rsa->flags & RSA_FLAG_CACHE_PUBLIC)
+        if (!BN_MONT_CTX_set_locked
+            (&rsa->_method_mod_n, rsa->lock, rsa->n, ctx))
+            goto err;
+
+    if (!rsa->meth->bn_mod_exp(ret, f, rsa->e, rsa->n, ctx,
+                               rsa->_method_mod_n))
+        goto err;
+
+    if ((padding == RSA_X931_PADDING) && ((bn_get_words(ret)[0] & 0xf) != 12))
+        if (!BN_sub(ret, rsa->n, ret))
+            goto err;
+
+    p = buf;
+    i = BN_bn2bin(ret, p);
+
+    switch (padding) {
+    case RSA_PKCS1_PADDING:
+        r = RSA_padding_check_PKCS1_type_1(to, num, buf, i, num);
+        break;
+    case RSA_X931_PADDING:
+        r = RSA_padding_check_X931(to, num, buf, i, num);
+        break;
+    case RSA_NO_PADDING:
+        r = RSA_padding_check_none(to, num, buf, i, num);
+        break;
+    default:
+        RSAerr(RSA_F_RSA_OSSL_PUBLIC_DECRYPT, RSA_R_UNKNOWN_PADDING_TYPE);
+        goto err;
+    }
+    if (r < 0)
+        RSAerr(RSA_F_RSA_OSSL_PUBLIC_DECRYPT, RSA_R_PADDING_CHECK_FAILED);
+
+ err:
+    if (ctx != NULL)
+        BN_CTX_end(ctx);
+    BN_CTX_free(ctx);
+    OPENSSL_clear_free(buf, num);
+    return (r);
+}
+
+static int rsa_ossl_mod_exp(BIGNUM *r0, const BIGNUM *I, RSA *rsa, BN_CTX *ctx)
+{
+    BIGNUM *r1, *m1, *vrfy;
+    int ret = 0;
+
+    BN_CTX_start(ctx);
+
+    r1 = BN_CTX_get(ctx);
+    m1 = BN_CTX_get(ctx);
+    vrfy = BN_CTX_get(ctx);
+
+    {
+        BIGNUM *p = BN_new(), *q = BN_new();
+
+        /*
+         * Make sure BN_mod_inverse in Montgomery initialization uses the
+         * BN_FLG_CONSTTIME flag
+         */
+        if (p == NULL || q == NULL) {
+            BN_free(p);
+            BN_free(q);
+            goto err;
+        }
+        BN_with_flags(p, rsa->p, BN_FLG_CONSTTIME);
+        BN_with_flags(q, rsa->q, BN_FLG_CONSTTIME);
+
+        if (rsa->flags & RSA_FLAG_CACHE_PRIVATE) {
+            if (!BN_MONT_CTX_set_locked
+                (&rsa->_method_mod_p, rsa->lock, p, ctx)
+                || !BN_MONT_CTX_set_locked(&rsa->_method_mod_q,
+                                           rsa->lock, q, ctx)) {
+                BN_free(p);
+                BN_free(q);
+                goto err;
+            }
+        }
+        /*
+         * We MUST free p and q before any further use of rsa->p and rsa->q
+         */
+        BN_free(p);
+        BN_free(q);
+    }
+
+    if (rsa->flags & RSA_FLAG_CACHE_PUBLIC)
+        if (!BN_MONT_CTX_set_locked
+            (&rsa->_method_mod_n, rsa->lock, rsa->n, ctx))
+            goto err;
+
+    /* compute I mod q */
+    {
+        BIGNUM *c = BN_new();
+        if (c == NULL)
+            goto err;
+        BN_with_flags(c, I, BN_FLG_CONSTTIME);
+
+        if (!BN_mod(r1, c, rsa->q, ctx)) {
+            BN_free(c);
+            goto err;
+        }
+
+        {
+            BIGNUM *dmq1 = BN_new();
+            if (dmq1 == NULL) {
+                BN_free(c);
+                goto err;
+            }
+            BN_with_flags(dmq1, rsa->dmq1, BN_FLG_CONSTTIME);
+
+            /* compute r1^dmq1 mod q */
+            if (!rsa->meth->bn_mod_exp(m1, r1, dmq1, rsa->q, ctx,
+                rsa->_method_mod_q)) {
+                BN_free(c);
+                BN_free(dmq1);
+                goto err;
+            }
+            /* We MUST free dmq1 before any further use of rsa->dmq1 */
+            BN_free(dmq1);
+        }
+
+        /* compute I mod p */
+        if (!BN_mod(r1, c, rsa->p, ctx)) {
+            BN_free(c);
+            goto err;
+        }
+        /* We MUST free c before any further use of I */
+        BN_free(c);
+    }
+
+    {
+        BIGNUM *dmp1 = BN_new();
+        if (dmp1 == NULL)
+            goto err;
+        BN_with_flags(dmp1, rsa->dmp1, BN_FLG_CONSTTIME);
+
+        /* compute r1^dmp1 mod p */
+        if (!rsa->meth->bn_mod_exp(r0, r1, dmp1, rsa->p, ctx,
+                                   rsa->_method_mod_p)) {
+            BN_free(dmp1);
+            goto err;
+        }
+        /* We MUST free dmp1 before any further use of rsa->dmp1 */
+        BN_free(dmp1);
+    }
+
+    if (!BN_sub(r0, r0, m1))
+        goto err;
+    /*
+     * This will help stop the size of r0 increasing, which does affect the
+     * multiply if it optimised for a power of 2 size
+     */
+    if (BN_is_negative(r0))
+        if (!BN_add(r0, r0, rsa->p))
+            goto err;
+
+    if (!BN_mul(r1, r0, rsa->iqmp, ctx))
+        goto err;
+
+    {
+        BIGNUM *pr1 = BN_new();
+        if (pr1 == NULL)
+            goto err;
+        BN_with_flags(pr1, r1, BN_FLG_CONSTTIME);
+
+        if (!BN_mod(r0, pr1, rsa->p, ctx)) {
+            BN_free(pr1);
+            goto err;
+        }
+        /* We MUST free pr1 before any further use of r1 */
+        BN_free(pr1);
+    }
+
+    /*
+     * If p < q it is occasionally possible for the correction of adding 'p'
+     * if r0 is negative above to leave the result still negative. This can
+     * break the private key operations: the following second correction
+     * should *always* correct this rare occurrence. This will *never* happen
+     * with OpenSSL generated keys because they ensure p > q [steve]
+     */
+    if (BN_is_negative(r0))
+        if (!BN_add(r0, r0, rsa->p))
+            goto err;
+    if (!BN_mul(r1, r0, rsa->q, ctx))
+        goto err;
+    if (!BN_add(r0, r1, m1))
+        goto err;
+
+    if (rsa->e && rsa->n) {
+        if (!rsa->meth->bn_mod_exp(vrfy, r0, rsa->e, rsa->n, ctx,
+                                   rsa->_method_mod_n))
+            goto err;
+        /*
+         * If 'I' was greater than (or equal to) rsa->n, the operation will
+         * be equivalent to using 'I mod n'. However, the result of the
+         * verify will *always* be less than 'n' so we don't check for
+         * absolute equality, just congruency.
+         */
+        if (!BN_sub(vrfy, vrfy, I))
+            goto err;
+        if (!BN_mod(vrfy, vrfy, rsa->n, ctx))
+            goto err;
+        if (BN_is_negative(vrfy))
+            if (!BN_add(vrfy, vrfy, rsa->n))
+                goto err;
+        if (!BN_is_zero(vrfy)) {
+            /*
+             * 'I' and 'vrfy' aren't congruent mod n. Don't leak
+             * miscalculated CRT output, just do a raw (slower) mod_exp and
+             * return that instead.
+             */
+
+            BIGNUM *d = BN_new();
+            if (d == NULL)
+                goto err;
+            BN_with_flags(d, rsa->d, BN_FLG_CONSTTIME);
+
+            if (!rsa->meth->bn_mod_exp(r0, I, d, rsa->n, ctx,
+                                       rsa->_method_mod_n)) {
+                BN_free(d);
+                goto err;
+            }
+            /* We MUST free d before any further use of rsa->d */
+            BN_free(d);
+        }
+    }
+    ret = 1;
+ err:
+    BN_CTX_end(ctx);
+    return (ret);
+}
+
+static int rsa_ossl_init(RSA *rsa)
+{
+    rsa->flags |= RSA_FLAG_CACHE_PUBLIC | RSA_FLAG_CACHE_PRIVATE;
+    return (1);
+}
+
+static int rsa_ossl_finish(RSA *rsa)
+{
+    BN_MONT_CTX_free(rsa->_method_mod_n);
+    BN_MONT_CTX_free(rsa->_method_mod_p);
+    BN_MONT_CTX_free(rsa->_method_mod_q);
+    return (1);
+}
+
+#endif
diff --git a/crypto/rsa/rsa_pk1.c b/crypto/rsa/rsa_pk1.c
new file mode 100644
index 000000000..efb16a02e
--- /dev/null
+++ b/crypto/rsa/rsa_pk1.c
@@ -0,0 +1,245 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "internal/constant_time_locl.h"
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+#include 
+
+int RSA_padding_add_PKCS1_type_1(unsigned char *to, int tlen,
+                                 const unsigned char *from, int flen)
+{
+    int j;
+    unsigned char *p;
+
+    if (flen > (tlen - RSA_PKCS1_PADDING_SIZE)) {
+        RSAerr(RSA_F_RSA_PADDING_ADD_PKCS1_TYPE_1,
+               RSA_R_DATA_TOO_LARGE_FOR_KEY_SIZE);
+        return (0);
+    }
+
+    p = (unsigned char *)to;
+
+    *(p++) = 0;
+    *(p++) = 1;                 /* Private Key BT (Block Type) */
+
+    /* pad out with 0xff data */
+    j = tlen - 3 - flen;
+    memset(p, 0xff, j);
+    p += j;
+    *(p++) = '\0';
+    memcpy(p, from, (unsigned int)flen);
+    return (1);
+}
+
+int RSA_padding_check_PKCS1_type_1(unsigned char *to, int tlen,
+                                   const unsigned char *from, int flen,
+                                   int num)
+{
+    int i, j;
+    const unsigned char *p;
+
+    p = from;
+
+    /*
+     * The format is
+     * 00 || 01 || PS || 00 || D
+     * PS - padding string, at least 8 bytes of FF
+     * D  - data.
+     */
+
+    if (num < 11)
+        return -1;
+
+    /* Accept inputs with and without the leading 0-byte. */
+    if (num == flen) {
+        if ((*p++) != 0x00) {
+            RSAerr(RSA_F_RSA_PADDING_CHECK_PKCS1_TYPE_1,
+                   RSA_R_INVALID_PADDING);
+            return -1;
+        }
+        flen--;
+    }
+
+    if ((num != (flen + 1)) || (*(p++) != 0x01)) {
+        RSAerr(RSA_F_RSA_PADDING_CHECK_PKCS1_TYPE_1,
+               RSA_R_BLOCK_TYPE_IS_NOT_01);
+        return (-1);
+    }
+
+    /* scan over padding data */
+    j = flen - 1;               /* one for type. */
+    for (i = 0; i < j; i++) {
+        if (*p != 0xff) {       /* should decrypt to 0xff */
+            if (*p == 0) {
+                p++;
+                break;
+            } else {
+                RSAerr(RSA_F_RSA_PADDING_CHECK_PKCS1_TYPE_1,
+                       RSA_R_BAD_FIXED_HEADER_DECRYPT);
+                return (-1);
+            }
+        }
+        p++;
+    }
+
+    if (i == j) {
+        RSAerr(RSA_F_RSA_PADDING_CHECK_PKCS1_TYPE_1,
+               RSA_R_NULL_BEFORE_BLOCK_MISSING);
+        return (-1);
+    }
+
+    if (i < 8) {
+        RSAerr(RSA_F_RSA_PADDING_CHECK_PKCS1_TYPE_1,
+               RSA_R_BAD_PAD_BYTE_COUNT);
+        return (-1);
+    }
+    i++;                        /* Skip over the '\0' */
+    j -= i;
+    if (j > tlen) {
+        RSAerr(RSA_F_RSA_PADDING_CHECK_PKCS1_TYPE_1, RSA_R_DATA_TOO_LARGE);
+        return (-1);
+    }
+    memcpy(to, p, (unsigned int)j);
+
+    return (j);
+}
+
+int RSA_padding_add_PKCS1_type_2(unsigned char *to, int tlen,
+                                 const unsigned char *from, int flen)
+{
+    int i, j;
+    unsigned char *p;
+
+    if (flen > (tlen - 11)) {
+        RSAerr(RSA_F_RSA_PADDING_ADD_PKCS1_TYPE_2,
+               RSA_R_DATA_TOO_LARGE_FOR_KEY_SIZE);
+        return (0);
+    }
+
+    p = (unsigned char *)to;
+
+    *(p++) = 0;
+    *(p++) = 2;                 /* Public Key BT (Block Type) */
+
+    /* pad out with non-zero random data */
+    j = tlen - 3 - flen;
+
+    if (RAND_bytes(p, j) <= 0)
+        return (0);
+    for (i = 0; i < j; i++) {
+        if (*p == '\0')
+            do {
+                if (RAND_bytes(p, 1) <= 0)
+                    return (0);
+            } while (*p == '\0');
+        p++;
+    }
+
+    *(p++) = '\0';
+
+    memcpy(p, from, (unsigned int)flen);
+    return (1);
+}
+
+int RSA_padding_check_PKCS1_type_2(unsigned char *to, int tlen,
+                                   const unsigned char *from, int flen,
+                                   int num)
+{
+    int i;
+    /* |em| is the encoded message, zero-padded to exactly |num| bytes */
+    unsigned char *em = NULL;
+    unsigned int good, found_zero_byte;
+    int zero_index = 0, msg_index, mlen = -1;
+
+    if (tlen < 0 || flen < 0)
+        return -1;
+
+    /*
+     * PKCS#1 v1.5 decryption. See "PKCS #1 v2.2: RSA Cryptography Standard",
+     * section 7.2.2.
+     */
+
+    if (flen > num)
+        goto err;
+
+    if (num < 11)
+        goto err;
+
+    em = OPENSSL_zalloc(num);
+    if (em == NULL) {
+        RSAerr(RSA_F_RSA_PADDING_CHECK_PKCS1_TYPE_2, ERR_R_MALLOC_FAILURE);
+        return -1;
+    }
+    /*
+     * Always do this zero-padding copy (even when num == flen) to avoid
+     * leaking that information. The copy still leaks some side-channel
+     * information, but it's impossible to have a fixed memory access
+     * pattern since we can't read out of the bounds of |from|.
+     *
+     * TODO(emilia): Consider porting BN_bn2bin_padded from BoringSSL.
+     */
+    memcpy(em + num - flen, from, flen);
+
+    good = constant_time_is_zero(em[0]);
+    good &= constant_time_eq(em[1], 2);
+
+    found_zero_byte = 0;
+    for (i = 2; i < num; i++) {
+        unsigned int equals0 = constant_time_is_zero(em[i]);
+        zero_index =
+            constant_time_select_int(~found_zero_byte & equals0, i,
+                                     zero_index);
+        found_zero_byte |= equals0;
+    }
+
+    /*
+     * PS must be at least 8 bytes long, and it starts two bytes into |em|.
+     * If we never found a 0-byte, then |zero_index| is 0 and the check
+     * also fails.
+     */
+    good &= constant_time_ge((unsigned int)(zero_index), 2 + 8);
+
+    /*
+     * Skip the zero byte. This is incorrect if we never found a zero-byte
+     * but in this case we also do not copy the message out.
+     */
+    msg_index = zero_index + 1;
+    mlen = num - msg_index;
+
+    /*
+     * For good measure, do this check in constant time as well; it could
+     * leak something if |tlen| was assuming valid padding.
+     */
+    good &= constant_time_ge((unsigned int)(tlen), (unsigned int)(mlen));
+
+    /*
+     * We can't continue in constant-time because we need to copy the result
+     * and we cannot fake its length. This unavoidably leaks timing
+     * information at the API boundary.
+     * TODO(emilia): this could be addressed at the call site,
+     * see BoringSSL commit 0aa0767340baf925bda4804882aab0cb974b2d26.
+     */
+    if (!good) {
+        mlen = -1;
+        goto err;
+    }
+
+    memcpy(to, em + msg_index, mlen);
+
+ err:
+    OPENSSL_free(em);
+    if (mlen == -1)
+        RSAerr(RSA_F_RSA_PADDING_CHECK_PKCS1_TYPE_2,
+               RSA_R_PKCS_DECODING_ERROR);
+    return mlen;
+}
diff --git a/crypto/rsa/rsa_pmeth.c b/crypto/rsa/rsa_pmeth.c
new file mode 100644
index 000000000..4c98a68a0
--- /dev/null
+++ b/crypto/rsa/rsa_pmeth.c
@@ -0,0 +1,677 @@
+/*
+ * Copyright 2006-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include "internal/evp_int.h"
+#include "rsa_locl.h"
+
+/* RSA pkey context structure */
+
+typedef struct {
+    /* Key gen parameters */
+    int nbits;
+    BIGNUM *pub_exp;
+    /* Keygen callback info */
+    int gentmp[2];
+    /* RSA padding mode */
+    int pad_mode;
+    /* message digest */
+    const EVP_MD *md;
+    /* message digest for MGF1 */
+    const EVP_MD *mgf1md;
+    /* PSS salt length */
+    int saltlen;
+    /* Temp buffer */
+    unsigned char *tbuf;
+    /* OAEP label */
+    unsigned char *oaep_label;
+    size_t oaep_labellen;
+} RSA_PKEY_CTX;
+
+static int pkey_rsa_init(EVP_PKEY_CTX *ctx)
+{
+    RSA_PKEY_CTX *rctx;
+    rctx = OPENSSL_zalloc(sizeof(*rctx));
+    if (rctx == NULL)
+        return 0;
+    rctx->nbits = 1024;
+    rctx->pad_mode = RSA_PKCS1_PADDING;
+    rctx->saltlen = -2;
+    ctx->data = rctx;
+    ctx->keygen_info = rctx->gentmp;
+    ctx->keygen_info_count = 2;
+
+    return 1;
+}
+
+static int pkey_rsa_copy(EVP_PKEY_CTX *dst, EVP_PKEY_CTX *src)
+{
+    RSA_PKEY_CTX *dctx, *sctx;
+    if (!pkey_rsa_init(dst))
+        return 0;
+    sctx = src->data;
+    dctx = dst->data;
+    dctx->nbits = sctx->nbits;
+    if (sctx->pub_exp) {
+        dctx->pub_exp = BN_dup(sctx->pub_exp);
+        if (!dctx->pub_exp)
+            return 0;
+    }
+    dctx->pad_mode = sctx->pad_mode;
+    dctx->md = sctx->md;
+    dctx->mgf1md = sctx->mgf1md;
+    if (sctx->oaep_label) {
+        OPENSSL_free(dctx->oaep_label);
+        dctx->oaep_label = OPENSSL_memdup(sctx->oaep_label, sctx->oaep_labellen);
+        if (!dctx->oaep_label)
+            return 0;
+        dctx->oaep_labellen = sctx->oaep_labellen;
+    }
+    return 1;
+}
+
+static int setup_tbuf(RSA_PKEY_CTX *ctx, EVP_PKEY_CTX *pk)
+{
+    if (ctx->tbuf)
+        return 1;
+    ctx->tbuf = OPENSSL_malloc(EVP_PKEY_size(pk->pkey));
+    if (ctx->tbuf == NULL)
+        return 0;
+    return 1;
+}
+
+static void pkey_rsa_cleanup(EVP_PKEY_CTX *ctx)
+{
+    RSA_PKEY_CTX *rctx = ctx->data;
+    if (rctx) {
+        BN_free(rctx->pub_exp);
+        OPENSSL_free(rctx->tbuf);
+        OPENSSL_free(rctx->oaep_label);
+        OPENSSL_free(rctx);
+    }
+}
+
+static int pkey_rsa_sign(EVP_PKEY_CTX *ctx, unsigned char *sig,
+                         size_t *siglen, const unsigned char *tbs,
+                         size_t tbslen)
+{
+    int ret;
+    RSA_PKEY_CTX *rctx = ctx->data;
+    RSA *rsa = ctx->pkey->pkey.rsa;
+
+    if (rctx->md) {
+        if (tbslen != (size_t)EVP_MD_size(rctx->md)) {
+            RSAerr(RSA_F_PKEY_RSA_SIGN, RSA_R_INVALID_DIGEST_LENGTH);
+            return -1;
+        }
+
+        if (EVP_MD_type(rctx->md) == NID_mdc2) {
+            unsigned int sltmp;
+            if (rctx->pad_mode != RSA_PKCS1_PADDING)
+                return -1;
+            ret = RSA_sign_ASN1_OCTET_STRING(0,
+                                             tbs, tbslen, sig, &sltmp, rsa);
+
+            if (ret <= 0)
+                return ret;
+            ret = sltmp;
+        } else if (rctx->pad_mode == RSA_X931_PADDING) {
+            if ((size_t)EVP_PKEY_size(ctx->pkey) < tbslen + 1) {
+                RSAerr(RSA_F_PKEY_RSA_SIGN, RSA_R_KEY_SIZE_TOO_SMALL);
+                return -1;
+            }
+            if (!setup_tbuf(rctx, ctx)) {
+                RSAerr(RSA_F_PKEY_RSA_SIGN, ERR_R_MALLOC_FAILURE);
+                return -1;
+            }
+            memcpy(rctx->tbuf, tbs, tbslen);
+            rctx->tbuf[tbslen] = RSA_X931_hash_id(EVP_MD_type(rctx->md));
+            ret = RSA_private_encrypt(tbslen + 1, rctx->tbuf,
+                                      sig, rsa, RSA_X931_PADDING);
+        } else if (rctx->pad_mode == RSA_PKCS1_PADDING) {
+            unsigned int sltmp;
+            ret = RSA_sign(EVP_MD_type(rctx->md),
+                           tbs, tbslen, sig, &sltmp, rsa);
+            if (ret <= 0)
+                return ret;
+            ret = sltmp;
+        } else if (rctx->pad_mode == RSA_PKCS1_PSS_PADDING) {
+            if (!setup_tbuf(rctx, ctx))
+                return -1;
+            if (!RSA_padding_add_PKCS1_PSS_mgf1(rsa,
+                                                rctx->tbuf, tbs,
+                                                rctx->md, rctx->mgf1md,
+                                                rctx->saltlen))
+                return -1;
+            ret = RSA_private_encrypt(RSA_size(rsa), rctx->tbuf,
+                                      sig, rsa, RSA_NO_PADDING);
+        } else
+            return -1;
+    } else
+        ret = RSA_private_encrypt(tbslen, tbs, sig, ctx->pkey->pkey.rsa,
+                                  rctx->pad_mode);
+
+    if (ret < 0)
+        return ret;
+    *siglen = ret;
+    return 1;
+}
+
+static int pkey_rsa_verifyrecover(EVP_PKEY_CTX *ctx,
+                                  unsigned char *rout, size_t *routlen,
+                                  const unsigned char *sig, size_t siglen)
+{
+    int ret;
+    RSA_PKEY_CTX *rctx = ctx->data;
+
+    if (rctx->md) {
+        if (rctx->pad_mode == RSA_X931_PADDING) {
+            if (!setup_tbuf(rctx, ctx))
+                return -1;
+            ret = RSA_public_decrypt(siglen, sig,
+                                     rctx->tbuf, ctx->pkey->pkey.rsa,
+                                     RSA_X931_PADDING);
+            if (ret < 1)
+                return 0;
+            ret--;
+            if (rctx->tbuf[ret] != RSA_X931_hash_id(EVP_MD_type(rctx->md))) {
+                RSAerr(RSA_F_PKEY_RSA_VERIFYRECOVER,
+                       RSA_R_ALGORITHM_MISMATCH);
+                return 0;
+            }
+            if (ret != EVP_MD_size(rctx->md)) {
+                RSAerr(RSA_F_PKEY_RSA_VERIFYRECOVER,
+                       RSA_R_INVALID_DIGEST_LENGTH);
+                return 0;
+            }
+            if (rout)
+                memcpy(rout, rctx->tbuf, ret);
+        } else if (rctx->pad_mode == RSA_PKCS1_PADDING) {
+            size_t sltmp;
+            ret = int_rsa_verify(EVP_MD_type(rctx->md),
+                                 NULL, 0, rout, &sltmp,
+                                 sig, siglen, ctx->pkey->pkey.rsa);
+            if (ret <= 0)
+                return 0;
+            ret = sltmp;
+        } else
+            return -1;
+    } else
+        ret = RSA_public_decrypt(siglen, sig, rout, ctx->pkey->pkey.rsa,
+                                 rctx->pad_mode);
+    if (ret < 0)
+        return ret;
+    *routlen = ret;
+    return 1;
+}
+
+static int pkey_rsa_verify(EVP_PKEY_CTX *ctx,
+                           const unsigned char *sig, size_t siglen,
+                           const unsigned char *tbs, size_t tbslen)
+{
+    RSA_PKEY_CTX *rctx = ctx->data;
+    RSA *rsa = ctx->pkey->pkey.rsa;
+    size_t rslen;
+    if (rctx->md) {
+        if (rctx->pad_mode == RSA_PKCS1_PADDING)
+            return RSA_verify(EVP_MD_type(rctx->md), tbs, tbslen,
+                              sig, siglen, rsa);
+        if (tbslen != (size_t)EVP_MD_size(rctx->md)) {
+            RSAerr(RSA_F_PKEY_RSA_VERIFY, RSA_R_INVALID_DIGEST_LENGTH);
+            return -1;
+        }
+        if (rctx->pad_mode == RSA_X931_PADDING) {
+            if (pkey_rsa_verifyrecover(ctx, NULL, &rslen, sig, siglen) <= 0)
+                return 0;
+        } else if (rctx->pad_mode == RSA_PKCS1_PSS_PADDING) {
+            int ret;
+            if (!setup_tbuf(rctx, ctx))
+                return -1;
+            ret = RSA_public_decrypt(siglen, sig, rctx->tbuf,
+                                     rsa, RSA_NO_PADDING);
+            if (ret <= 0)
+                return 0;
+            ret = RSA_verify_PKCS1_PSS_mgf1(rsa, tbs,
+                                            rctx->md, rctx->mgf1md,
+                                            rctx->tbuf, rctx->saltlen);
+            if (ret <= 0)
+                return 0;
+            return 1;
+        } else
+            return -1;
+    } else {
+        if (!setup_tbuf(rctx, ctx))
+            return -1;
+        rslen = RSA_public_decrypt(siglen, sig, rctx->tbuf,
+                                   rsa, rctx->pad_mode);
+        if (rslen == 0)
+            return 0;
+    }
+
+    if ((rslen != tbslen) || memcmp(tbs, rctx->tbuf, rslen))
+        return 0;
+
+    return 1;
+
+}
+
+static int pkey_rsa_encrypt(EVP_PKEY_CTX *ctx,
+                            unsigned char *out, size_t *outlen,
+                            const unsigned char *in, size_t inlen)
+{
+    int ret;
+    RSA_PKEY_CTX *rctx = ctx->data;
+    if (rctx->pad_mode == RSA_PKCS1_OAEP_PADDING) {
+        int klen = RSA_size(ctx->pkey->pkey.rsa);
+        if (!setup_tbuf(rctx, ctx))
+            return -1;
+        if (!RSA_padding_add_PKCS1_OAEP_mgf1(rctx->tbuf, klen,
+                                             in, inlen,
+                                             rctx->oaep_label,
+                                             rctx->oaep_labellen,
+                                             rctx->md, rctx->mgf1md))
+            return -1;
+        ret = RSA_public_encrypt(klen, rctx->tbuf, out,
+                                 ctx->pkey->pkey.rsa, RSA_NO_PADDING);
+    } else
+        ret = RSA_public_encrypt(inlen, in, out, ctx->pkey->pkey.rsa,
+                                 rctx->pad_mode);
+    if (ret < 0)
+        return ret;
+    *outlen = ret;
+    return 1;
+}
+
+static int pkey_rsa_decrypt(EVP_PKEY_CTX *ctx,
+                            unsigned char *out, size_t *outlen,
+                            const unsigned char *in, size_t inlen)
+{
+    int ret;
+    RSA_PKEY_CTX *rctx = ctx->data;
+    if (rctx->pad_mode == RSA_PKCS1_OAEP_PADDING) {
+        int i;
+        if (!setup_tbuf(rctx, ctx))
+            return -1;
+        ret = RSA_private_decrypt(inlen, in, rctx->tbuf,
+                                  ctx->pkey->pkey.rsa, RSA_NO_PADDING);
+        if (ret <= 0)
+            return ret;
+        for (i = 0; i < ret; i++) {
+            if (rctx->tbuf[i])
+                break;
+        }
+        ret = RSA_padding_check_PKCS1_OAEP_mgf1(out, ret, rctx->tbuf + i,
+                                                ret - i, ret,
+                                                rctx->oaep_label,
+                                                rctx->oaep_labellen,
+                                                rctx->md, rctx->mgf1md);
+    } else
+        ret = RSA_private_decrypt(inlen, in, out, ctx->pkey->pkey.rsa,
+                                  rctx->pad_mode);
+    if (ret < 0)
+        return ret;
+    *outlen = ret;
+    return 1;
+}
+
+static int check_padding_md(const EVP_MD *md, int padding)
+{
+    int mdnid;
+    if (!md)
+        return 1;
+
+    mdnid = EVP_MD_type(md);
+
+    if (padding == RSA_NO_PADDING) {
+        RSAerr(RSA_F_CHECK_PADDING_MD, RSA_R_INVALID_PADDING_MODE);
+        return 0;
+    }
+
+    if (padding == RSA_X931_PADDING) {
+        if (RSA_X931_hash_id(mdnid) == -1) {
+            RSAerr(RSA_F_CHECK_PADDING_MD, RSA_R_INVALID_X931_DIGEST);
+            return 0;
+        }
+    } else {
+        switch(mdnid) {
+        /* List of all supported RSA digests */
+        case NID_sha1:
+        case NID_sha224:
+        case NID_sha256:
+        case NID_sha384:
+        case NID_sha512:
+        case NID_md5:
+        case NID_md5_sha1:
+        case NID_md2:
+        case NID_md4:
+        case NID_mdc2:
+        case NID_ripemd160:
+#ifndef OPENSSL_NO_SM3
+        case NID_sm3:
+#endif
+            return 1;
+
+        default:
+            RSAerr(RSA_F_CHECK_PADDING_MD, RSA_R_INVALID_DIGEST);
+            return 0;
+
+        }
+    }
+
+    return 1;
+}
+
+static int pkey_rsa_ctrl(EVP_PKEY_CTX *ctx, int type, int p1, void *p2)
+{
+    RSA_PKEY_CTX *rctx = ctx->data;
+    switch (type) {
+    case EVP_PKEY_CTRL_RSA_PADDING:
+        if ((p1 >= RSA_PKCS1_PADDING) && (p1 <= RSA_PKCS1_PSS_PADDING)) {
+            if (!check_padding_md(rctx->md, p1))
+                return 0;
+            if (p1 == RSA_PKCS1_PSS_PADDING) {
+                if (!(ctx->operation &
+                      (EVP_PKEY_OP_SIGN | EVP_PKEY_OP_VERIFY)))
+                    goto bad_pad;
+                if (!rctx->md)
+                    rctx->md = EVP_sha1();
+            }
+            if (p1 == RSA_PKCS1_OAEP_PADDING) {
+                if (!(ctx->operation & EVP_PKEY_OP_TYPE_CRYPT))
+                    goto bad_pad;
+                if (!rctx->md)
+                    rctx->md = EVP_sha1();
+            }
+            rctx->pad_mode = p1;
+            return 1;
+        }
+ bad_pad:
+        RSAerr(RSA_F_PKEY_RSA_CTRL,
+               RSA_R_ILLEGAL_OR_UNSUPPORTED_PADDING_MODE);
+        return -2;
+
+    case EVP_PKEY_CTRL_GET_RSA_PADDING:
+        *(int *)p2 = rctx->pad_mode;
+        return 1;
+
+    case EVP_PKEY_CTRL_RSA_PSS_SALTLEN:
+    case EVP_PKEY_CTRL_GET_RSA_PSS_SALTLEN:
+        if (rctx->pad_mode != RSA_PKCS1_PSS_PADDING) {
+            RSAerr(RSA_F_PKEY_RSA_CTRL, RSA_R_INVALID_PSS_SALTLEN);
+            return -2;
+        }
+        if (type == EVP_PKEY_CTRL_GET_RSA_PSS_SALTLEN)
+            *(int *)p2 = rctx->saltlen;
+        else {
+            if (p1 < -2)
+                return -2;
+            rctx->saltlen = p1;
+        }
+        return 1;
+
+    case EVP_PKEY_CTRL_RSA_KEYGEN_BITS:
+        if (p1 < 512) {
+            RSAerr(RSA_F_PKEY_RSA_CTRL, RSA_R_KEY_SIZE_TOO_SMALL);
+            return -2;
+        }
+        rctx->nbits = p1;
+        return 1;
+
+    case EVP_PKEY_CTRL_RSA_KEYGEN_PUBEXP:
+        if (p2 == NULL || !BN_is_odd((BIGNUM *)p2) || BN_is_one((BIGNUM *)p2)) {
+            RSAerr(RSA_F_PKEY_RSA_CTRL, RSA_R_BAD_E_VALUE);
+            return -2;
+        }
+        BN_free(rctx->pub_exp);
+        rctx->pub_exp = p2;
+        return 1;
+
+    case EVP_PKEY_CTRL_RSA_OAEP_MD:
+    case EVP_PKEY_CTRL_GET_RSA_OAEP_MD:
+        if (rctx->pad_mode != RSA_PKCS1_OAEP_PADDING) {
+            RSAerr(RSA_F_PKEY_RSA_CTRL, RSA_R_INVALID_PADDING_MODE);
+            return -2;
+        }
+        if (type == EVP_PKEY_CTRL_GET_RSA_OAEP_MD)
+            *(const EVP_MD **)p2 = rctx->md;
+        else
+            rctx->md = p2;
+        return 1;
+
+    case EVP_PKEY_CTRL_MD:
+        if (!check_padding_md(p2, rctx->pad_mode))
+            return 0;
+        rctx->md = p2;
+        return 1;
+
+    case EVP_PKEY_CTRL_GET_MD:
+        *(const EVP_MD **)p2 = rctx->md;
+        return 1;
+
+    case EVP_PKEY_CTRL_RSA_MGF1_MD:
+    case EVP_PKEY_CTRL_GET_RSA_MGF1_MD:
+        if (rctx->pad_mode != RSA_PKCS1_PSS_PADDING
+            && rctx->pad_mode != RSA_PKCS1_OAEP_PADDING) {
+            RSAerr(RSA_F_PKEY_RSA_CTRL, RSA_R_INVALID_MGF1_MD);
+            return -2;
+        }
+        if (type == EVP_PKEY_CTRL_GET_RSA_MGF1_MD) {
+            if (rctx->mgf1md)
+                *(const EVP_MD **)p2 = rctx->mgf1md;
+            else
+                *(const EVP_MD **)p2 = rctx->md;
+        } else
+            rctx->mgf1md = p2;
+        return 1;
+
+    case EVP_PKEY_CTRL_RSA_OAEP_LABEL:
+        if (rctx->pad_mode != RSA_PKCS1_OAEP_PADDING) {
+            RSAerr(RSA_F_PKEY_RSA_CTRL, RSA_R_INVALID_PADDING_MODE);
+            return -2;
+        }
+        OPENSSL_free(rctx->oaep_label);
+        if (p2 && p1 > 0) {
+            rctx->oaep_label = p2;
+            rctx->oaep_labellen = p1;
+        } else {
+            rctx->oaep_label = NULL;
+            rctx->oaep_labellen = 0;
+        }
+        return 1;
+
+    case EVP_PKEY_CTRL_GET_RSA_OAEP_LABEL:
+        if (rctx->pad_mode != RSA_PKCS1_OAEP_PADDING) {
+            RSAerr(RSA_F_PKEY_RSA_CTRL, RSA_R_INVALID_PADDING_MODE);
+            return -2;
+        }
+        *(unsigned char **)p2 = rctx->oaep_label;
+        return rctx->oaep_labellen;
+
+    case EVP_PKEY_CTRL_DIGESTINIT:
+    case EVP_PKEY_CTRL_PKCS7_ENCRYPT:
+    case EVP_PKEY_CTRL_PKCS7_DECRYPT:
+    case EVP_PKEY_CTRL_PKCS7_SIGN:
+        return 1;
+#ifndef OPENSSL_NO_CMS
+    case EVP_PKEY_CTRL_CMS_DECRYPT:
+    case EVP_PKEY_CTRL_CMS_ENCRYPT:
+    case EVP_PKEY_CTRL_CMS_SIGN:
+        return 1;
+#endif
+    case EVP_PKEY_CTRL_PEER_KEY:
+        RSAerr(RSA_F_PKEY_RSA_CTRL,
+               RSA_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE);
+        return -2;
+
+    default:
+        return -2;
+
+    }
+}
+
+static int pkey_rsa_ctrl_str(EVP_PKEY_CTX *ctx,
+                             const char *type, const char *value)
+{
+    if (!value) {
+        RSAerr(RSA_F_PKEY_RSA_CTRL_STR, RSA_R_VALUE_MISSING);
+        return 0;
+    }
+    if (strcmp(type, "rsa_padding_mode") == 0) {
+        int pm;
+        if (strcmp(value, "pkcs1") == 0)
+            pm = RSA_PKCS1_PADDING;
+        else if (strcmp(value, "sslv23") == 0)
+            pm = RSA_SSLV23_PADDING;
+        else if (strcmp(value, "none") == 0)
+            pm = RSA_NO_PADDING;
+        else if (strcmp(value, "oeap") == 0)
+            pm = RSA_PKCS1_OAEP_PADDING;
+        else if (strcmp(value, "oaep") == 0)
+            pm = RSA_PKCS1_OAEP_PADDING;
+        else if (strcmp(value, "x931") == 0)
+            pm = RSA_X931_PADDING;
+        else if (strcmp(value, "pss") == 0)
+            pm = RSA_PKCS1_PSS_PADDING;
+        else {
+            RSAerr(RSA_F_PKEY_RSA_CTRL_STR, RSA_R_UNKNOWN_PADDING_TYPE);
+            return -2;
+        }
+        return EVP_PKEY_CTX_set_rsa_padding(ctx, pm);
+    }
+
+    if (strcmp(type, "rsa_pss_saltlen") == 0) {
+        int saltlen;
+        saltlen = atoi(value);
+        return EVP_PKEY_CTX_set_rsa_pss_saltlen(ctx, saltlen);
+    }
+
+    if (strcmp(type, "rsa_keygen_bits") == 0) {
+        int nbits;
+        nbits = atoi(value);
+        return EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, nbits);
+    }
+
+    if (strcmp(type, "rsa_keygen_pubexp") == 0) {
+        int ret;
+        BIGNUM *pubexp = NULL;
+        if (!BN_asc2bn(&pubexp, value))
+            return 0;
+        ret = EVP_PKEY_CTX_set_rsa_keygen_pubexp(ctx, pubexp);
+        if (ret <= 0)
+            BN_free(pubexp);
+        return ret;
+    }
+
+    if (strcmp(type, "rsa_mgf1_md") == 0) {
+        const EVP_MD *md;
+        if ((md = EVP_get_digestbyname(value)) == NULL) {
+            RSAerr(RSA_F_PKEY_RSA_CTRL_STR, RSA_R_INVALID_DIGEST);
+            return 0;
+        }
+        return EVP_PKEY_CTX_set_rsa_mgf1_md(ctx, md);
+    }
+
+    if (strcmp(type, "rsa_oaep_md") == 0) {
+        const EVP_MD *md;
+        if ((md = EVP_get_digestbyname(value)) == NULL) {
+            RSAerr(RSA_F_PKEY_RSA_CTRL_STR, RSA_R_INVALID_DIGEST);
+            return 0;
+        }
+        return EVP_PKEY_CTX_set_rsa_oaep_md(ctx, md);
+    }
+    if (strcmp(type, "rsa_oaep_label") == 0) {
+        unsigned char *lab;
+        long lablen;
+        int ret;
+        lab = OPENSSL_hexstr2buf(value, &lablen);
+        if (!lab)
+            return 0;
+        ret = EVP_PKEY_CTX_set0_rsa_oaep_label(ctx, lab, lablen);
+        if (ret <= 0)
+            OPENSSL_free(lab);
+        return ret;
+    }
+
+    return -2;
+}
+
+static int pkey_rsa_keygen(EVP_PKEY_CTX *ctx, EVP_PKEY *pkey)
+{
+    RSA *rsa = NULL;
+    RSA_PKEY_CTX *rctx = ctx->data;
+    BN_GENCB *pcb;
+    int ret;
+    if (rctx->pub_exp == NULL) {
+        rctx->pub_exp = BN_new();
+        if (rctx->pub_exp == NULL || !BN_set_word(rctx->pub_exp, RSA_F4))
+            return 0;
+    }
+    rsa = RSA_new();
+    if (rsa == NULL)
+        return 0;
+    if (ctx->pkey_gencb) {
+        pcb = BN_GENCB_new();
+        if (pcb == NULL) {
+            RSA_free(rsa);
+            return 0;
+        }
+        evp_pkey_set_cb_translate(pcb, ctx);
+    } else
+        pcb = NULL;
+    ret = RSA_generate_key_ex(rsa, rctx->nbits, rctx->pub_exp, pcb);
+    BN_GENCB_free(pcb);
+    if (ret > 0)
+        EVP_PKEY_assign_RSA(pkey, rsa);
+    else
+        RSA_free(rsa);
+    return ret;
+}
+
+const EVP_PKEY_METHOD rsa_pkey_meth = {
+    EVP_PKEY_RSA,
+    EVP_PKEY_FLAG_AUTOARGLEN,
+    pkey_rsa_init,
+    pkey_rsa_copy,
+    pkey_rsa_cleanup,
+
+    0, 0,
+
+    0,
+    pkey_rsa_keygen,
+
+    0,
+    pkey_rsa_sign,
+
+    0,
+    pkey_rsa_verify,
+
+    0,
+    pkey_rsa_verifyrecover,
+
+    0, 0, 0, 0,
+
+    0,
+    pkey_rsa_encrypt,
+
+    0,
+    pkey_rsa_decrypt,
+
+    0, 0,
+
+    pkey_rsa_ctrl,
+    pkey_rsa_ctrl_str
+};
diff --git a/crypto/rsa/rsa_prn.c b/crypto/rsa/rsa_prn.c
new file mode 100644
index 000000000..5e6c599e4
--- /dev/null
+++ b/crypto/rsa/rsa_prn.c
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2006-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+
+#ifndef OPENSSL_NO_STDIO
+int RSA_print_fp(FILE *fp, const RSA *x, int off)
+{
+    BIO *b;
+    int ret;
+
+    if ((b = BIO_new(BIO_s_file())) == NULL) {
+        RSAerr(RSA_F_RSA_PRINT_FP, ERR_R_BUF_LIB);
+        return (0);
+    }
+    BIO_set_fp(b, fp, BIO_NOCLOSE);
+    ret = RSA_print(b, x, off);
+    BIO_free(b);
+    return (ret);
+}
+#endif
+
+int RSA_print(BIO *bp, const RSA *x, int off)
+{
+    EVP_PKEY *pk;
+    int ret;
+    pk = EVP_PKEY_new();
+    if (pk == NULL || !EVP_PKEY_set1_RSA(pk, (RSA *)x))
+        return 0;
+    ret = EVP_PKEY_print_private(bp, pk, off, NULL);
+    EVP_PKEY_free(pk);
+    return ret;
+}
diff --git a/crypto/rsa/rsa_pss.c b/crypto/rsa/rsa_pss.c
new file mode 100644
index 000000000..0ec63b2ec
--- /dev/null
+++ b/crypto/rsa/rsa_pss.c
@@ -0,0 +1,244 @@
+/*
+ * Copyright 2005-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+#include 
+#include 
+#include 
+#include "rsa_locl.h"
+
+static const unsigned char zeroes[] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+
+#if defined(_MSC_VER) && defined(_ARM_)
+# pragma optimize("g", off)
+#endif
+
+int RSA_verify_PKCS1_PSS(RSA *rsa, const unsigned char *mHash,
+                         const EVP_MD *Hash, const unsigned char *EM,
+                         int sLen)
+{
+    return RSA_verify_PKCS1_PSS_mgf1(rsa, mHash, Hash, NULL, EM, sLen);
+}
+
+int RSA_verify_PKCS1_PSS_mgf1(RSA *rsa, const unsigned char *mHash,
+                              const EVP_MD *Hash, const EVP_MD *mgf1Hash,
+                              const unsigned char *EM, int sLen)
+{
+    int i;
+    int ret = 0;
+    int hLen, maskedDBLen, MSBits, emLen;
+    const unsigned char *H;
+    unsigned char *DB = NULL;
+    EVP_MD_CTX *ctx = EVP_MD_CTX_new();
+    unsigned char H_[EVP_MAX_MD_SIZE];
+
+
+    if (ctx == NULL)
+        goto err;
+
+    if (mgf1Hash == NULL)
+        mgf1Hash = Hash;
+
+    hLen = EVP_MD_size(Hash);
+    if (hLen < 0)
+        goto err;
+    /*-
+     * Negative sLen has special meanings:
+     *      -1      sLen == hLen
+     *      -2      salt length is autorecovered from signature
+     *      -N      reserved
+     */
+    if (sLen == -1)
+        sLen = hLen;
+    else if (sLen == -2)
+        sLen = -2;
+    else if (sLen < -2) {
+        RSAerr(RSA_F_RSA_VERIFY_PKCS1_PSS_MGF1, RSA_R_SLEN_CHECK_FAILED);
+        goto err;
+    }
+
+    MSBits = (BN_num_bits(rsa->n) - 1) & 0x7;
+    emLen = RSA_size(rsa);
+    if (EM[0] & (0xFF << MSBits)) {
+        RSAerr(RSA_F_RSA_VERIFY_PKCS1_PSS_MGF1, RSA_R_FIRST_OCTET_INVALID);
+        goto err;
+    }
+    if (MSBits == 0) {
+        EM++;
+        emLen--;
+    }
+    if (emLen < (hLen + sLen + 2)) { /* sLen can be small negative */
+        RSAerr(RSA_F_RSA_VERIFY_PKCS1_PSS_MGF1, RSA_R_DATA_TOO_LARGE);
+        goto err;
+    }
+    if (EM[emLen - 1] != 0xbc) {
+        RSAerr(RSA_F_RSA_VERIFY_PKCS1_PSS_MGF1, RSA_R_LAST_OCTET_INVALID);
+        goto err;
+    }
+    maskedDBLen = emLen - hLen - 1;
+    H = EM + maskedDBLen;
+    DB = OPENSSL_malloc(maskedDBLen);
+    if (DB == NULL) {
+        RSAerr(RSA_F_RSA_VERIFY_PKCS1_PSS_MGF1, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+    if (PKCS1_MGF1(DB, maskedDBLen, H, hLen, mgf1Hash) < 0)
+        goto err;
+    for (i = 0; i < maskedDBLen; i++)
+        DB[i] ^= EM[i];
+    if (MSBits)
+        DB[0] &= 0xFF >> (8 - MSBits);
+    for (i = 0; DB[i] == 0 && i < (maskedDBLen - 1); i++) ;
+    if (DB[i++] != 0x1) {
+        RSAerr(RSA_F_RSA_VERIFY_PKCS1_PSS_MGF1, RSA_R_SLEN_RECOVERY_FAILED);
+        goto err;
+    }
+    if (sLen >= 0 && (maskedDBLen - i) != sLen) {
+        RSAerr(RSA_F_RSA_VERIFY_PKCS1_PSS_MGF1, RSA_R_SLEN_CHECK_FAILED);
+        goto err;
+    }
+    if (!EVP_DigestInit_ex(ctx, Hash, NULL)
+        || !EVP_DigestUpdate(ctx, zeroes, sizeof zeroes)
+        || !EVP_DigestUpdate(ctx, mHash, hLen))
+        goto err;
+    if (maskedDBLen - i) {
+        if (!EVP_DigestUpdate(ctx, DB + i, maskedDBLen - i))
+            goto err;
+    }
+    if (!EVP_DigestFinal_ex(ctx, H_, NULL))
+        goto err;
+    if (memcmp(H_, H, hLen)) {
+        RSAerr(RSA_F_RSA_VERIFY_PKCS1_PSS_MGF1, RSA_R_BAD_SIGNATURE);
+        ret = 0;
+    } else
+        ret = 1;
+
+ err:
+    OPENSSL_free(DB);
+    EVP_MD_CTX_free(ctx);
+
+    return ret;
+
+}
+
+int RSA_padding_add_PKCS1_PSS(RSA *rsa, unsigned char *EM,
+                              const unsigned char *mHash,
+                              const EVP_MD *Hash, int sLen)
+{
+    return RSA_padding_add_PKCS1_PSS_mgf1(rsa, EM, mHash, Hash, NULL, sLen);
+}
+
+int RSA_padding_add_PKCS1_PSS_mgf1(RSA *rsa, unsigned char *EM,
+                                   const unsigned char *mHash,
+                                   const EVP_MD *Hash, const EVP_MD *mgf1Hash,
+                                   int sLen)
+{
+    int i;
+    int ret = 0;
+    int hLen, maskedDBLen, MSBits, emLen;
+    unsigned char *H, *salt = NULL, *p;
+    EVP_MD_CTX *ctx = NULL;
+
+    if (mgf1Hash == NULL)
+        mgf1Hash = Hash;
+
+    hLen = EVP_MD_size(Hash);
+    if (hLen < 0)
+        goto err;
+    /*-
+     * Negative sLen has special meanings:
+     *      -1      sLen == hLen
+     *      -2      salt length is maximized
+     *      -N      reserved
+     */
+    if (sLen == -1)
+        sLen = hLen;
+    else if (sLen == -2)
+        sLen = -2;
+    else if (sLen < -2) {
+        RSAerr(RSA_F_RSA_PADDING_ADD_PKCS1_PSS_MGF1, RSA_R_SLEN_CHECK_FAILED);
+        goto err;
+    }
+
+    MSBits = (BN_num_bits(rsa->n) - 1) & 0x7;
+    emLen = RSA_size(rsa);
+    if (MSBits == 0) {
+        *EM++ = 0;
+        emLen--;
+    }
+    if (sLen == -2) {
+        sLen = emLen - hLen - 2;
+    } else if (emLen < (hLen + sLen + 2)) {
+        RSAerr(RSA_F_RSA_PADDING_ADD_PKCS1_PSS_MGF1,
+               RSA_R_DATA_TOO_LARGE_FOR_KEY_SIZE);
+        goto err;
+    }
+    if (sLen > 0) {
+        salt = OPENSSL_malloc(sLen);
+        if (salt == NULL) {
+            RSAerr(RSA_F_RSA_PADDING_ADD_PKCS1_PSS_MGF1,
+                   ERR_R_MALLOC_FAILURE);
+            goto err;
+        }
+        if (RAND_bytes(salt, sLen) <= 0)
+            goto err;
+    }
+    maskedDBLen = emLen - hLen - 1;
+    H = EM + maskedDBLen;
+    ctx = EVP_MD_CTX_new();
+    if (ctx == NULL)
+        goto err;
+    if (!EVP_DigestInit_ex(ctx, Hash, NULL)
+        || !EVP_DigestUpdate(ctx, zeroes, sizeof zeroes)
+        || !EVP_DigestUpdate(ctx, mHash, hLen))
+        goto err;
+    if (sLen && !EVP_DigestUpdate(ctx, salt, sLen))
+        goto err;
+    if (!EVP_DigestFinal_ex(ctx, H, NULL))
+        goto err;
+
+    /* Generate dbMask in place then perform XOR on it */
+    if (PKCS1_MGF1(EM, maskedDBLen, H, hLen, mgf1Hash))
+        goto err;
+
+    p = EM;
+
+    /*
+     * Initial PS XORs with all zeroes which is a NOP so just update pointer.
+     * Note from a test above this value is guaranteed to be non-negative.
+     */
+    p += emLen - sLen - hLen - 2;
+    *p++ ^= 0x1;
+    if (sLen > 0) {
+        for (i = 0; i < sLen; i++)
+            *p++ ^= salt[i];
+    }
+    if (MSBits)
+        EM[0] &= 0xFF >> (8 - MSBits);
+
+    /* H is already in place so just set final 0xbc */
+
+    EM[emLen - 1] = 0xbc;
+
+    ret = 1;
+
+ err:
+    EVP_MD_CTX_free(ctx);
+    OPENSSL_free(salt);
+
+    return ret;
+
+}
+
+#if defined(_MSC_VER)
+# pragma optimize("",on)
+#endif
diff --git a/crypto/rsa/rsa_saos.c b/crypto/rsa/rsa_saos.c
new file mode 100644
index 000000000..9e5fff450
--- /dev/null
+++ b/crypto/rsa/rsa_saos.c
@@ -0,0 +1,94 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+#include 
+#include 
+
+int RSA_sign_ASN1_OCTET_STRING(int type,
+                               const unsigned char *m, unsigned int m_len,
+                               unsigned char *sigret, unsigned int *siglen,
+                               RSA *rsa)
+{
+    ASN1_OCTET_STRING sig;
+    int i, j, ret = 1;
+    unsigned char *p, *s;
+
+    sig.type = V_ASN1_OCTET_STRING;
+    sig.length = m_len;
+    sig.data = (unsigned char *)m;
+
+    i = i2d_ASN1_OCTET_STRING(&sig, NULL);
+    j = RSA_size(rsa);
+    if (i > (j - RSA_PKCS1_PADDING_SIZE)) {
+        RSAerr(RSA_F_RSA_SIGN_ASN1_OCTET_STRING,
+               RSA_R_DIGEST_TOO_BIG_FOR_RSA_KEY);
+        return (0);
+    }
+    s = OPENSSL_malloc((unsigned int)j + 1);
+    if (s == NULL) {
+        RSAerr(RSA_F_RSA_SIGN_ASN1_OCTET_STRING, ERR_R_MALLOC_FAILURE);
+        return (0);
+    }
+    p = s;
+    i2d_ASN1_OCTET_STRING(&sig, &p);
+    i = RSA_private_encrypt(i, s, sigret, rsa, RSA_PKCS1_PADDING);
+    if (i <= 0)
+        ret = 0;
+    else
+        *siglen = i;
+
+    OPENSSL_clear_free(s, (unsigned int)j + 1);
+    return (ret);
+}
+
+int RSA_verify_ASN1_OCTET_STRING(int dtype,
+                                 const unsigned char *m,
+                                 unsigned int m_len, unsigned char *sigbuf,
+                                 unsigned int siglen, RSA *rsa)
+{
+    int i, ret = 0;
+    unsigned char *s;
+    const unsigned char *p;
+    ASN1_OCTET_STRING *sig = NULL;
+
+    if (siglen != (unsigned int)RSA_size(rsa)) {
+        RSAerr(RSA_F_RSA_VERIFY_ASN1_OCTET_STRING,
+               RSA_R_WRONG_SIGNATURE_LENGTH);
+        return (0);
+    }
+
+    s = OPENSSL_malloc((unsigned int)siglen);
+    if (s == NULL) {
+        RSAerr(RSA_F_RSA_VERIFY_ASN1_OCTET_STRING, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+    i = RSA_public_decrypt((int)siglen, sigbuf, s, rsa, RSA_PKCS1_PADDING);
+
+    if (i <= 0)
+        goto err;
+
+    p = s;
+    sig = d2i_ASN1_OCTET_STRING(NULL, &p, (long)i);
+    if (sig == NULL)
+        goto err;
+
+    if (((unsigned int)sig->length != m_len) ||
+        (memcmp(m, sig->data, m_len) != 0)) {
+        RSAerr(RSA_F_RSA_VERIFY_ASN1_OCTET_STRING, RSA_R_BAD_SIGNATURE);
+    } else
+        ret = 1;
+ err:
+    ASN1_OCTET_STRING_free(sig);
+    OPENSSL_clear_free(s, (unsigned int)siglen);
+    return (ret);
+}
diff --git a/crypto/rsa/rsa_sign.c b/crypto/rsa/rsa_sign.c
new file mode 100644
index 000000000..952d24fb8
--- /dev/null
+++ b/crypto/rsa/rsa_sign.c
@@ -0,0 +1,248 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+#include 
+#include 
+#include "internal/x509_int.h"
+#include "rsa_locl.h"
+
+/* Size of an SSL signature: MD5+SHA1 */
+#define SSL_SIG_LENGTH  36
+
+/*
+ * encode_pkcs1 encodes a DigestInfo prefix of hash |type| and digest |m|, as
+ * described in EMSA-PKCS1-v1_5-ENCODE, RFC 3447 section 9.2 step 2. This
+ * encodes the DigestInfo (T and tLen) but does not add the padding.
+ *
+ * On success, it returns one and sets |*out| to a newly allocated buffer
+ * containing the result and |*out_len| to its length. The caller must free
+ * |*out| with |OPENSSL_free|. Otherwise, it returns zero.
+ */
+static int encode_pkcs1(unsigned char **out, int *out_len, int type,
+                        const unsigned char *m, unsigned int m_len)
+{
+    X509_SIG sig;
+    X509_ALGOR algor;
+    ASN1_TYPE parameter;
+    ASN1_OCTET_STRING digest;
+    uint8_t *der = NULL;
+    int len;
+
+    sig.algor = &algor;
+    sig.algor->algorithm = OBJ_nid2obj(type);
+    if (sig.algor->algorithm == NULL) {
+        RSAerr(RSA_F_ENCODE_PKCS1, RSA_R_UNKNOWN_ALGORITHM_TYPE);
+        return 0;
+    }
+    if (OBJ_length(sig.algor->algorithm) == 0) {
+        RSAerr(RSA_F_ENCODE_PKCS1,
+               RSA_R_THE_ASN1_OBJECT_IDENTIFIER_IS_NOT_KNOWN_FOR_THIS_MD);
+        return 0;
+    }
+    parameter.type = V_ASN1_NULL;
+    parameter.value.ptr = NULL;
+    sig.algor->parameter = ¶meter;
+
+    sig.digest = &digest;
+    sig.digest->data = (unsigned char *)m;
+    sig.digest->length = m_len;
+
+    len = i2d_X509_SIG(&sig, &der);
+    if (len < 0)
+        return 0;
+
+    *out = der;
+    *out_len = len;
+    return 1;
+}
+
+int RSA_sign(int type, const unsigned char *m, unsigned int m_len,
+             unsigned char *sigret, unsigned int *siglen, RSA *rsa)
+{
+    int encrypt_len, encoded_len = 0, ret = 0;
+    unsigned char *tmps = NULL;
+    const unsigned char *encoded = NULL;
+
+    if (rsa->meth->rsa_sign) {
+        return rsa->meth->rsa_sign(type, m, m_len, sigret, siglen, rsa);
+    }
+
+    /* Compute the encoded digest. */
+    if (type == NID_md5_sha1) {
+        /*
+         * NID_md5_sha1 corresponds to the MD5/SHA1 combination in TLS 1.1 and
+         * earlier. It has no DigestInfo wrapper but otherwise is
+         * RSASSA-PKCS1-v1_5.
+         */
+        if (m_len != SSL_SIG_LENGTH) {
+            RSAerr(RSA_F_RSA_SIGN, RSA_R_INVALID_MESSAGE_LENGTH);
+            return 0;
+        }
+        encoded_len = SSL_SIG_LENGTH;
+        encoded = m;
+    } else {
+        if (!encode_pkcs1(&tmps, &encoded_len, type, m, m_len))
+            goto err;
+        encoded = tmps;
+    }
+
+    if (encoded_len > RSA_size(rsa) - RSA_PKCS1_PADDING_SIZE) {
+        RSAerr(RSA_F_RSA_SIGN, RSA_R_DIGEST_TOO_BIG_FOR_RSA_KEY);
+        goto err;
+    }
+    encrypt_len = RSA_private_encrypt(encoded_len, encoded, sigret, rsa,
+                                      RSA_PKCS1_PADDING);
+    if (encrypt_len <= 0)
+        goto err;
+
+    *siglen = encrypt_len;
+    ret = 1;
+
+err:
+    OPENSSL_clear_free(tmps, (size_t)encoded_len);
+    return ret;
+}
+
+/*
+ * int_rsa_verify verifies an RSA signature in |sigbuf| using |rsa|. It may be
+ * called in two modes. If |rm| is NULL, it verifies the signature for digest
+ * |m|. Otherwise, it recovers the digest from the signature, writing the digest
+ * to |rm| and the length to |*prm_len|. |type| is the NID of the digest
+ * algorithm to use. It returns one on successful verification and zero
+ * otherwise.
+ */
+int int_rsa_verify(int type, const unsigned char *m, unsigned int m_len,
+                   unsigned char *rm, size_t *prm_len,
+                   const unsigned char *sigbuf, size_t siglen, RSA *rsa)
+{
+    int decrypt_len, ret = 0, encoded_len = 0;
+    unsigned char *decrypt_buf = NULL, *encoded = NULL;
+
+    if (siglen != (size_t)RSA_size(rsa)) {
+        RSAerr(RSA_F_INT_RSA_VERIFY, RSA_R_WRONG_SIGNATURE_LENGTH);
+        return 0;
+    }
+
+    /* Recover the encoded digest. */
+    decrypt_buf = OPENSSL_malloc(siglen);
+    if (decrypt_buf == NULL) {
+        RSAerr(RSA_F_INT_RSA_VERIFY, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+
+    decrypt_len = RSA_public_decrypt((int)siglen, sigbuf, decrypt_buf, rsa,
+                                     RSA_PKCS1_PADDING);
+    if (decrypt_len <= 0)
+        goto err;
+
+    if (type == NID_md5_sha1) {
+        /*
+         * NID_md5_sha1 corresponds to the MD5/SHA1 combination in TLS 1.1 and
+         * earlier. It has no DigestInfo wrapper but otherwise is
+         * RSASSA-PKCS1-v1_5.
+         */
+        if (decrypt_len != SSL_SIG_LENGTH) {
+            RSAerr(RSA_F_INT_RSA_VERIFY, RSA_R_BAD_SIGNATURE);
+            goto err;
+        }
+
+        if (rm != NULL) {
+            memcpy(rm, decrypt_buf, SSL_SIG_LENGTH);
+            *prm_len = SSL_SIG_LENGTH;
+        } else {
+            if (m_len != SSL_SIG_LENGTH) {
+                RSAerr(RSA_F_INT_RSA_VERIFY, RSA_R_INVALID_MESSAGE_LENGTH);
+                goto err;
+            }
+
+            if (memcmp(decrypt_buf, m, SSL_SIG_LENGTH) != 0) {
+                RSAerr(RSA_F_INT_RSA_VERIFY, RSA_R_BAD_SIGNATURE);
+                goto err;
+            }
+        }
+    } else if (type == NID_mdc2 && decrypt_len == 2 + 16
+               && decrypt_buf[0] == 0x04 && decrypt_buf[1] == 0x10) {
+        /*
+         * Oddball MDC2 case: signature can be OCTET STRING. check for correct
+         * tag and length octets.
+         */
+        if (rm != NULL) {
+            memcpy(rm, decrypt_buf + 2, 16);
+            *prm_len = 16;
+        } else {
+            if (m_len != 16) {
+                RSAerr(RSA_F_INT_RSA_VERIFY, RSA_R_INVALID_MESSAGE_LENGTH);
+                goto err;
+            }
+
+            if (memcmp(m, decrypt_buf + 2, 16) != 0) {
+                RSAerr(RSA_F_INT_RSA_VERIFY, RSA_R_BAD_SIGNATURE);
+                goto err;
+            }
+        }
+    } else {
+        /*
+         * If recovering the digest, extract a digest-sized output from the end
+         * of |decrypt_buf| for |encode_pkcs1|, then compare the decryption
+         * output as in a standard verification.
+         */
+        if (rm != NULL) {
+            const EVP_MD *md = EVP_get_digestbynid(type);
+            if (md == NULL) {
+                RSAerr(RSA_F_INT_RSA_VERIFY, RSA_R_UNKNOWN_ALGORITHM_TYPE);
+                goto err;
+            }
+
+            m_len = EVP_MD_size(md);
+            if (m_len > (size_t)decrypt_len) {
+                RSAerr(RSA_F_INT_RSA_VERIFY, RSA_R_INVALID_DIGEST_LENGTH);
+                goto err;
+            }
+            m = decrypt_buf + decrypt_len - m_len;
+        }
+
+        /* Construct the encoded digest and ensure it matches. */
+        if (!encode_pkcs1(&encoded, &encoded_len, type, m, m_len))
+            goto err;
+
+        if (encoded_len != decrypt_len
+            || memcmp(encoded, decrypt_buf, encoded_len) != 0) {
+            RSAerr(RSA_F_INT_RSA_VERIFY, RSA_R_BAD_SIGNATURE);
+            goto err;
+        }
+
+        /* Output the recovered digest. */
+        if (rm != NULL) {
+            memcpy(rm, m, m_len);
+            *prm_len = m_len;
+        }
+    }
+
+    ret = 1;
+
+err:
+    OPENSSL_clear_free(encoded, (size_t)encoded_len);
+    OPENSSL_clear_free(decrypt_buf, siglen);
+    return ret;
+}
+
+int RSA_verify(int type, const unsigned char *m, unsigned int m_len,
+               const unsigned char *sigbuf, unsigned int siglen, RSA *rsa)
+{
+
+    if (rsa->meth->rsa_verify) {
+        return rsa->meth->rsa_verify(type, m, m_len, sigbuf, siglen, rsa);
+    }
+
+    return int_rsa_verify(type, m, m_len, NULL, NULL, sigbuf, siglen, rsa);
+}
diff --git a/crypto/rsa/rsa_ssl.c b/crypto/rsa/rsa_ssl.c
new file mode 100644
index 000000000..9ef6b80ea
--- /dev/null
+++ b/crypto/rsa/rsa_ssl.c
@@ -0,0 +1,100 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+#include 
+
+int RSA_padding_add_SSLv23(unsigned char *to, int tlen,
+                           const unsigned char *from, int flen)
+{
+    int i, j;
+    unsigned char *p;
+
+    if (flen > (tlen - 11)) {
+        RSAerr(RSA_F_RSA_PADDING_ADD_SSLV23,
+               RSA_R_DATA_TOO_LARGE_FOR_KEY_SIZE);
+        return (0);
+    }
+
+    p = (unsigned char *)to;
+
+    *(p++) = 0;
+    *(p++) = 2;                 /* Public Key BT (Block Type) */
+
+    /* pad out with non-zero random data */
+    j = tlen - 3 - 8 - flen;
+
+    if (RAND_bytes(p, j) <= 0)
+        return (0);
+    for (i = 0; i < j; i++) {
+        if (*p == '\0')
+            do {
+                if (RAND_bytes(p, 1) <= 0)
+                    return (0);
+            } while (*p == '\0');
+        p++;
+    }
+
+    memset(p, 3, 8);
+    p += 8;
+    *(p++) = '\0';
+
+    memcpy(p, from, (unsigned int)flen);
+    return (1);
+}
+
+int RSA_padding_check_SSLv23(unsigned char *to, int tlen,
+                             const unsigned char *from, int flen, int num)
+{
+    int i, j, k;
+    const unsigned char *p;
+
+    p = from;
+    if (flen < 10) {
+        RSAerr(RSA_F_RSA_PADDING_CHECK_SSLV23, RSA_R_DATA_TOO_SMALL);
+        return (-1);
+    }
+    if ((num != (flen + 1)) || (*(p++) != 02)) {
+        RSAerr(RSA_F_RSA_PADDING_CHECK_SSLV23, RSA_R_BLOCK_TYPE_IS_NOT_02);
+        return (-1);
+    }
+
+    /* scan over padding data */
+    j = flen - 1;               /* one for type */
+    for (i = 0; i < j; i++)
+        if (*(p++) == 0)
+            break;
+
+    if ((i == j) || (i < 8)) {
+        RSAerr(RSA_F_RSA_PADDING_CHECK_SSLV23,
+               RSA_R_NULL_BEFORE_BLOCK_MISSING);
+        return (-1);
+    }
+    for (k = -9; k < -1; k++) {
+        if (p[k] != 0x03)
+            break;
+    }
+    if (k == -1) {
+        RSAerr(RSA_F_RSA_PADDING_CHECK_SSLV23, RSA_R_SSLV3_ROLLBACK_ATTACK);
+        return (-1);
+    }
+
+    i++;                        /* Skip over the '\0' */
+    j -= i;
+    if (j > tlen) {
+        RSAerr(RSA_F_RSA_PADDING_CHECK_SSLV23, RSA_R_DATA_TOO_LARGE);
+        return (-1);
+    }
+    memcpy(to, p, (unsigned int)j);
+
+    return (j);
+}
diff --git a/crypto/rsa/rsa_x931.c b/crypto/rsa/rsa_x931.c
new file mode 100644
index 000000000..b9301f372
--- /dev/null
+++ b/crypto/rsa/rsa_x931.c
@@ -0,0 +1,116 @@
+/*
+ * Copyright 2005-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+#include 
+
+int RSA_padding_add_X931(unsigned char *to, int tlen,
+                         const unsigned char *from, int flen)
+{
+    int j;
+    unsigned char *p;
+
+    /*
+     * Absolute minimum amount of padding is 1 header nibble, 1 padding
+     * nibble and 2 trailer bytes: but 1 hash if is already in 'from'.
+     */
+
+    j = tlen - flen - 2;
+
+    if (j < 0) {
+        RSAerr(RSA_F_RSA_PADDING_ADD_X931, RSA_R_DATA_TOO_LARGE_FOR_KEY_SIZE);
+        return -1;
+    }
+
+    p = (unsigned char *)to;
+
+    /* If no padding start and end nibbles are in one byte */
+    if (j == 0)
+        *p++ = 0x6A;
+    else {
+        *p++ = 0x6B;
+        if (j > 1) {
+            memset(p, 0xBB, j - 1);
+            p += j - 1;
+        }
+        *p++ = 0xBA;
+    }
+    memcpy(p, from, (unsigned int)flen);
+    p += flen;
+    *p = 0xCC;
+    return (1);
+}
+
+int RSA_padding_check_X931(unsigned char *to, int tlen,
+                           const unsigned char *from, int flen, int num)
+{
+    int i = 0, j;
+    const unsigned char *p;
+
+    p = from;
+    if ((num != flen) || ((*p != 0x6A) && (*p != 0x6B))) {
+        RSAerr(RSA_F_RSA_PADDING_CHECK_X931, RSA_R_INVALID_HEADER);
+        return -1;
+    }
+
+    if (*p++ == 0x6B) {
+        j = flen - 3;
+        for (i = 0; i < j; i++) {
+            unsigned char c = *p++;
+            if (c == 0xBA)
+                break;
+            if (c != 0xBB) {
+                RSAerr(RSA_F_RSA_PADDING_CHECK_X931, RSA_R_INVALID_PADDING);
+                return -1;
+            }
+        }
+
+        j -= i;
+
+        if (i == 0) {
+            RSAerr(RSA_F_RSA_PADDING_CHECK_X931, RSA_R_INVALID_PADDING);
+            return -1;
+        }
+
+    } else
+        j = flen - 2;
+
+    if (p[j] != 0xCC) {
+        RSAerr(RSA_F_RSA_PADDING_CHECK_X931, RSA_R_INVALID_TRAILER);
+        return -1;
+    }
+
+    memcpy(to, p, (unsigned int)j);
+
+    return (j);
+}
+
+/* Translate between X931 hash ids and NIDs */
+
+int RSA_X931_hash_id(int nid)
+{
+    switch (nid) {
+    case NID_sha1:
+        return 0x33;
+
+    case NID_sha256:
+        return 0x34;
+
+    case NID_sha384:
+        return 0x36;
+
+    case NID_sha512:
+        return 0x35;
+
+    }
+    return -1;
+}
diff --git a/crypto/rsa/rsa_x931g.c b/crypto/rsa/rsa_x931g.c
new file mode 100644
index 000000000..9dd993fbc
--- /dev/null
+++ b/crypto/rsa/rsa_x931g.c
@@ -0,0 +1,195 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include "rsa_locl.h"
+
+/* X9.31 RSA key derivation and generation */
+
+int RSA_X931_derive_ex(RSA *rsa, BIGNUM *p1, BIGNUM *p2, BIGNUM *q1,
+                       BIGNUM *q2, const BIGNUM *Xp1, const BIGNUM *Xp2,
+                       const BIGNUM *Xp, const BIGNUM *Xq1, const BIGNUM *Xq2,
+                       const BIGNUM *Xq, const BIGNUM *e, BN_GENCB *cb)
+{
+    BIGNUM *r0 = NULL, *r1 = NULL, *r2 = NULL, *r3 = NULL;
+    BN_CTX *ctx = NULL, *ctx2 = NULL;
+    int ret = 0;
+
+    if (!rsa)
+        goto err;
+
+    ctx = BN_CTX_new();
+    if (ctx == NULL)
+        goto err;
+    BN_CTX_start(ctx);
+
+    r0 = BN_CTX_get(ctx);
+    r1 = BN_CTX_get(ctx);
+    r2 = BN_CTX_get(ctx);
+    r3 = BN_CTX_get(ctx);
+
+    if (r3 == NULL)
+        goto err;
+    if (!rsa->e) {
+        rsa->e = BN_dup(e);
+        if (!rsa->e)
+            goto err;
+    } else
+        e = rsa->e;
+
+    /*
+     * If not all parameters present only calculate what we can. This allows
+     * test programs to output selective parameters.
+     */
+
+    if (Xp && rsa->p == NULL) {
+        rsa->p = BN_new();
+        if (rsa->p == NULL)
+            goto err;
+
+        if (!BN_X931_derive_prime_ex(rsa->p, p1, p2,
+                                     Xp, Xp1, Xp2, e, ctx, cb))
+            goto err;
+    }
+
+    if (Xq && rsa->q == NULL) {
+        rsa->q = BN_new();
+        if (rsa->q == NULL)
+            goto err;
+        if (!BN_X931_derive_prime_ex(rsa->q, q1, q2,
+                                     Xq, Xq1, Xq2, e, ctx, cb))
+            goto err;
+    }
+
+    if (rsa->p == NULL || rsa->q == NULL) {
+        BN_CTX_end(ctx);
+        BN_CTX_free(ctx);
+        return 2;
+    }
+
+    /*
+     * Since both primes are set we can now calculate all remaining
+     * components.
+     */
+
+    /* calculate n */
+    rsa->n = BN_new();
+    if (rsa->n == NULL)
+        goto err;
+    if (!BN_mul(rsa->n, rsa->p, rsa->q, ctx))
+        goto err;
+
+    /* calculate d */
+    if (!BN_sub(r1, rsa->p, BN_value_one()))
+        goto err;               /* p-1 */
+    if (!BN_sub(r2, rsa->q, BN_value_one()))
+        goto err;               /* q-1 */
+    if (!BN_mul(r0, r1, r2, ctx))
+        goto err;               /* (p-1)(q-1) */
+
+    if (!BN_gcd(r3, r1, r2, ctx))
+        goto err;
+
+    if (!BN_div(r0, NULL, r0, r3, ctx))
+        goto err;               /* LCM((p-1)(q-1)) */
+
+    ctx2 = BN_CTX_new();
+    if (ctx2 == NULL)
+        goto err;
+
+    rsa->d = BN_mod_inverse(NULL, rsa->e, r0, ctx2); /* d */
+    if (rsa->d == NULL)
+        goto err;
+
+    /* calculate d mod (p-1) */
+    rsa->dmp1 = BN_new();
+    if (rsa->dmp1 == NULL)
+        goto err;
+    if (!BN_mod(rsa->dmp1, rsa->d, r1, ctx))
+        goto err;
+
+    /* calculate d mod (q-1) */
+    rsa->dmq1 = BN_new();
+    if (rsa->dmq1 == NULL)
+        goto err;
+    if (!BN_mod(rsa->dmq1, rsa->d, r2, ctx))
+        goto err;
+
+    /* calculate inverse of q mod p */
+    rsa->iqmp = BN_mod_inverse(NULL, rsa->q, rsa->p, ctx2);
+
+    ret = 1;
+ err:
+    if (ctx)
+        BN_CTX_end(ctx);
+    BN_CTX_free(ctx);
+    BN_CTX_free(ctx2);
+
+    return ret;
+
+}
+
+int RSA_X931_generate_key_ex(RSA *rsa, int bits, const BIGNUM *e,
+                             BN_GENCB *cb)
+{
+    int ok = 0;
+    BIGNUM *Xp = NULL, *Xq = NULL;
+    BN_CTX *ctx = NULL;
+
+    ctx = BN_CTX_new();
+    if (ctx == NULL)
+        goto error;
+
+    BN_CTX_start(ctx);
+    Xp = BN_CTX_get(ctx);
+    Xq = BN_CTX_get(ctx);
+    if (!BN_X931_generate_Xpq(Xp, Xq, bits, ctx))
+        goto error;
+
+    rsa->p = BN_new();
+    rsa->q = BN_new();
+    if (rsa->p == NULL || rsa->q == NULL)
+        goto error;
+
+    /* Generate two primes from Xp, Xq */
+
+    if (!BN_X931_generate_prime_ex(rsa->p, NULL, NULL, NULL, NULL, Xp,
+                                   e, ctx, cb))
+        goto error;
+
+    if (!BN_X931_generate_prime_ex(rsa->q, NULL, NULL, NULL, NULL, Xq,
+                                   e, ctx, cb))
+        goto error;
+
+    /*
+     * Since rsa->p and rsa->q are valid this call will just derive remaining
+     * RSA components.
+     */
+
+    if (!RSA_X931_derive_ex(rsa, NULL, NULL, NULL, NULL,
+                            NULL, NULL, NULL, NULL, NULL, NULL, e, cb))
+        goto error;
+
+    ok = 1;
+
+ error:
+    if (ctx)
+        BN_CTX_end(ctx);
+    BN_CTX_free(ctx);
+
+    if (ok)
+        return 1;
+
+    return 0;
+
+}
diff --git a/crypto/s390xcap.c b/crypto/s390xcap.c
new file mode 100644
index 000000000..675f2ecb9
--- /dev/null
+++ b/crypto/s390xcap.c
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2010-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+
+extern unsigned long OPENSSL_s390xcap_P[];
+
+static sigjmp_buf ill_jmp;
+static void ill_handler(int sig)
+{
+    siglongjmp(ill_jmp, sig);
+}
+
+unsigned long OPENSSL_s390x_facilities(void);
+
+void OPENSSL_cpuid_setup(void)
+{
+    sigset_t oset;
+    struct sigaction ill_act, oact;
+
+    if (OPENSSL_s390xcap_P[0])
+        return;
+
+    OPENSSL_s390xcap_P[0] = 1UL << (8 * sizeof(unsigned long) - 1);
+
+    memset(&ill_act, 0, sizeof(ill_act));
+    ill_act.sa_handler = ill_handler;
+    sigfillset(&ill_act.sa_mask);
+    sigdelset(&ill_act.sa_mask, SIGILL);
+    sigdelset(&ill_act.sa_mask, SIGTRAP);
+    sigprocmask(SIG_SETMASK, &ill_act.sa_mask, &oset);
+    sigaction(SIGILL, &ill_act, &oact);
+
+    /* protection against missing store-facility-list-extended */
+    if (sigsetjmp(ill_jmp, 1) == 0)
+        OPENSSL_s390x_facilities();
+
+    sigaction(SIGILL, &oact, NULL);
+    sigprocmask(SIG_SETMASK, &oset, NULL);
+}
diff --git a/crypto/s390xcpuid.S b/crypto/s390xcpuid.S
new file mode 100644
index 000000000..8859e9e64
--- /dev/null
+++ b/crypto/s390xcpuid.S
@@ -0,0 +1,180 @@
+.text
+// Copyright 2009-2016 The OpenSSL Project Authors. All Rights Reserved.
+//
+// Licensed under the OpenSSL license (the "License").  You may not use
+// this file except in compliance with the License.  You can obtain a copy
+// in the file LICENSE in the source distribution or at
+// https://www.openssl.org/source/license.html
+
+.globl	OPENSSL_s390x_facilities
+.type	OPENSSL_s390x_facilities,@function
+.align	16
+OPENSSL_s390x_facilities:
+	lghi	%r0,0
+	larl	%r4,OPENSSL_s390xcap_P
+	stg	%r0,8(%r4)	# wipe capability vectors
+	stg	%r0,16(%r4)
+	stg	%r0,24(%r4)
+	stg	%r0,32(%r4)
+	stg	%r0,40(%r4)
+	stg	%r0,48(%r4)
+	stg	%r0,56(%r4)
+	stg	%r0,64(%r4)
+	stg	%r0,72(%r4)
+
+	.long	0xb2b04000	# stfle	0(%r4)
+	brc	8,.Ldone
+	lghi	%r0,1
+	.long	0xb2b04000	# stfle 0(%r4)
+.Ldone:
+	lmg	%r2,%r3,0(%r4)
+	tmhl	%r2,0x4000	# check for message-security-assist
+	jz	.Lret
+
+	lghi	%r0,0		# query kimd capabilities
+	la	%r1,16(%r4)
+	.long	0xb93e0002	# kimd %r0,%r2
+
+	lghi	%r0,0		# query km capability vector
+	la	%r1,32(%r4)
+	.long	0xb92e0042	# km %r4,%r2
+
+	lghi	%r0,0		# query kmc capability vector
+	la	%r1,48(%r4)
+	.long	0xb92f0042	# kmc %r4,%r2
+
+	tmhh	%r3,0x0004	# check for message-security-assist-4
+	jz	.Lret
+
+	lghi	%r0,0		# query kmctr capability vector
+	la	%r1,64(%r4)
+	.long	0xb92d2042	# kmctr %r4,%r2,%r2
+
+.Lret:
+	br	%r14
+.size	OPENSSL_s390x_facilities,.-OPENSSL_s390x_facilities
+
+.globl	OPENSSL_rdtsc
+.type	OPENSSL_rdtsc,@function
+.align	16
+OPENSSL_rdtsc:
+	stck	16(%r15)
+	lg	%r2,16(%r15)
+	br	%r14
+.size	OPENSSL_rdtsc,.-OPENSSL_rdtsc
+
+.globl	OPENSSL_atomic_add
+.type	OPENSSL_atomic_add,@function
+.align	16
+OPENSSL_atomic_add:
+	l	%r1,0(%r2)
+.Lspin:	lr	%r0,%r1
+	ar	%r0,%r3
+	cs	%r1,%r0,0(%r2)
+	brc	4,.Lspin
+	lgfr	%r2,%r0		# OpenSSL expects the new value
+	br	%r14
+.size	OPENSSL_atomic_add,.-OPENSSL_atomic_add
+
+.globl	OPENSSL_wipe_cpu
+.type	OPENSSL_wipe_cpu,@function
+.align	16
+OPENSSL_wipe_cpu:
+	xgr	%r0,%r0
+	xgr	%r1,%r1
+	lgr	%r2,%r15
+	xgr	%r3,%r3
+	xgr	%r4,%r4
+	lzdr	%f0
+	lzdr	%f1
+	lzdr	%f2
+	lzdr	%f3
+	lzdr	%f4
+	lzdr	%f5
+	lzdr	%f6
+	lzdr	%f7
+	br	%r14
+.size	OPENSSL_wipe_cpu,.-OPENSSL_wipe_cpu
+
+.globl	OPENSSL_cleanse
+.type	OPENSSL_cleanse,@function
+.align	16
+OPENSSL_cleanse:
+#if !defined(__s390x__) && !defined(__s390x)
+	llgfr	%r3,%r3
+#endif
+	lghi	%r4,15
+	lghi	%r0,0
+	clgr	%r3,%r4
+	jh	.Lot
+	clgr	%r3,%r0
+	bcr	8,%r14
+.Little:
+	stc	%r0,0(%r2)
+	la	%r2,1(%r2)
+	brctg	%r3,.Little
+	br	%r14
+.align	4
+.Lot:	tmll	%r2,7
+	jz	.Laligned
+	stc	%r0,0(%r2)
+	la	%r2,1(%r2)
+	brctg	%r3,.Lot
+.Laligned:
+	srlg	%r4,%r3,3
+.Loop:	stg	%r0,0(%r2)
+	la	%r2,8(%r2)
+	brctg	%r4,.Loop
+	lghi	%r4,7
+	ngr	%r3,%r4
+	jnz	.Little
+	br	%r14
+.size	OPENSSL_cleanse,.-OPENSSL_cleanse
+
+.globl	CRYPTO_memcmp
+.type	CRYPTO_memcmp,@function
+.align	16
+CRYPTO_memcmp:
+#if !defined(__s390x__) && !defined(__s390x)
+	llgfr	%r4,%r4
+#endif
+	lghi	%r5,0
+	clgr	%r4,%r5
+	je	.Lno_data
+
+.Loop_cmp:
+	llgc	%r0,0(%r2)
+	la	%r2,1(%r2)
+	llgc	%r1,0(%r3)
+	la	%r3,1(%r3)
+	xr	%r1,%r0
+	or	%r5,%r1
+	brctg	%r4,.Loop_cmp
+
+	lnr	%r5,%r5
+	srl	%r5,31
+.Lno_data:
+	lgr	%r2,%r5
+	br	%r14
+.size	CRYPTO_memcmp,.-CRYPTO_memcmp
+
+.globl	OPENSSL_instrument_bus
+.type	OPENSSL_instrument_bus,@function
+.align	16
+OPENSSL_instrument_bus:
+	lghi	%r2,0
+	br	%r14
+.size	OPENSSL_instrument_bus,.-OPENSSL_instrument_bus
+
+.globl	OPENSSL_instrument_bus2
+.type	OPENSSL_instrument_bus2,@function
+.align	16
+OPENSSL_instrument_bus2:
+	lghi	%r2,0
+	br	%r14
+.size	OPENSSL_instrument_bus2,.-OPENSSL_instrument_bus2
+
+.section	.init
+	brasl	%r14,OPENSSL_cpuid_setup
+
+.comm	OPENSSL_s390xcap_P,80,8
diff --git a/crypto/sdf/build.info b/crypto/sdf/build.info
new file mode 100644
index 000000000..b37d49fc5
--- /dev/null
+++ b/crypto/sdf/build.info
@@ -0,0 +1,2 @@
+LIBS=../../libcrypto
+SOURCE[../../libcrypto]=sdf_err.c sdf_lib.c sdf_ext.c sdf_meth.c sdf_sansec.c
diff --git a/crypto/sdf/sdf_err.c b/crypto/sdf/sdf_err.c
new file mode 100644
index 000000000..9db84f55f
--- /dev/null
+++ b/crypto/sdf/sdf_err.c
@@ -0,0 +1,263 @@
+/*
+ * Generated by util/mkerr.pl DO NOT EDIT
+ * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include 
+
+/* BEGIN ERROR CODES */
+#ifndef OPENSSL_NO_ERR
+
+# define ERR_FUNC(func) ERR_PACK(ERR_LIB_SDF,func,0)
+# define ERR_REASON(reason) ERR_PACK(ERR_LIB_SDF,0,reason)
+
+static ERR_STRING_DATA SDF_str_functs[] = {
+    {ERR_FUNC(SDF_F_SANSEC_DECODE_ECCCIPHER), "sansec_decode_ecccipher"},
+    {ERR_FUNC(SDF_F_SANSEC_ENCODE_ECCCIPHER), "sansec_encode_ecccipher"},
+    {ERR_FUNC(SDF_F_SDF_CALCULATEMAC), "SDF_CalculateMAC"},
+    {ERR_FUNC(SDF_F_SDF_CLOSEDEVICE), "SDF_CloseDevice"},
+    {ERR_FUNC(SDF_F_SDF_CLOSESESSION), "SDF_CloseSession"},
+    {ERR_FUNC(SDF_F_SDF_CREATEFILE), "SDF_CreateFile"},
+    {ERR_FUNC(SDF_F_SDF_DECRYPT), "SDF_Decrypt"},
+    {ERR_FUNC(SDF_F_SDF_DELETEFILE), "SDF_DeleteFile"},
+    {ERR_FUNC(SDF_F_SDF_DESTROYKEY), "SDF_DestroyKey"},
+    {ERR_FUNC(SDF_F_SDF_ENCRYPT), "SDF_Encrypt"},
+    {ERR_FUNC(SDF_F_SDF_EXCHANGEDIGITENVELOPEBASEONECC),
+     "SDF_ExchangeDigitEnvelopeBaseOnECC"},
+    {ERR_FUNC(SDF_F_SDF_EXCHANGEDIGITENVELOPEBASEONRSA),
+     "SDF_ExchangeDigitEnvelopeBaseOnRSA"},
+    {ERR_FUNC(SDF_F_SDF_EXPORTENCPUBLICKEY_ECC),
+     "SDF_ExportEncPublicKey_ECC"},
+    {ERR_FUNC(SDF_F_SDF_EXPORTENCPUBLICKEY_RSA),
+     "SDF_ExportEncPublicKey_RSA"},
+    {ERR_FUNC(SDF_F_SDF_EXPORTSIGNPUBLICKEY_ECC),
+     "SDF_ExportSignPublicKey_ECC"},
+    {ERR_FUNC(SDF_F_SDF_EXPORTSIGNPUBLICKEY_RSA),
+     "SDF_ExportSignPublicKey_RSA"},
+    {ERR_FUNC(SDF_F_SDF_EXTERNALENCRYPT_ECC), "SDF_ExternalEncrypt_ECC"},
+    {ERR_FUNC(SDF_F_SDF_EXTERNALPUBLICKEYOPERATION_RSA),
+     "SDF_ExternalPublicKeyOperation_RSA"},
+    {ERR_FUNC(SDF_F_SDF_EXTERNALVERIFY_ECC), "SDF_ExternalVerify_ECC"},
+    {ERR_FUNC(SDF_F_SDF_GENERATEAGREEMENTDATAANDKEYWITHECC),
+     "SDF_GenerateAgreementDataAndKeyWithECC"},
+    {ERR_FUNC(SDF_F_SDF_GENERATEAGREEMENTDATAWITHECC),
+     "SDF_GenerateAgreementDataWithECC"},
+    {ERR_FUNC(SDF_F_SDF_GENERATEKEYPAIR_ECC), "SDF_GenerateKeyPair_ECC"},
+    {ERR_FUNC(SDF_F_SDF_GENERATEKEYPAIR_RSA), "SDF_GenerateKeyPair_RSA"},
+    {ERR_FUNC(SDF_F_SDF_GENERATEKEYWITHECC), "SDF_GenerateKeyWithECC"},
+    {ERR_FUNC(SDF_F_SDF_GENERATEKEYWITHEPK_ECC),
+     "SDF_GenerateKeyWithEPK_ECC"},
+    {ERR_FUNC(SDF_F_SDF_GENERATEKEYWITHEPK_RSA),
+     "SDF_GenerateKeyWithEPK_RSA"},
+    {ERR_FUNC(SDF_F_SDF_GENERATEKEYWITHIPK_ECC),
+     "SDF_GenerateKeyWithIPK_ECC"},
+    {ERR_FUNC(SDF_F_SDF_GENERATEKEYWITHIPK_RSA),
+     "SDF_GenerateKeyWithIPK_RSA"},
+    {ERR_FUNC(SDF_F_SDF_GENERATEKEYWITHKEK), "SDF_GenerateKeyWithKEK"},
+    {ERR_FUNC(SDF_F_SDF_GENERATERANDOM), "SDF_GenerateRandom"},
+    {ERR_FUNC(SDF_F_SDF_GETDEVICEINFO), "SDF_GetDeviceInfo"},
+    {ERR_FUNC(SDF_F_SDF_GETPRIVATEKEYACCESSRIGHT),
+     "SDF_GetPrivateKeyAccessRight"},
+    {ERR_FUNC(SDF_F_SDF_HASHFINAL), "SDF_HashFinal"},
+    {ERR_FUNC(SDF_F_SDF_HASHINIT), "SDF_HashInit"},
+    {ERR_FUNC(SDF_F_SDF_HASHUPDATE), "SDF_HashUpdate"},
+    {ERR_FUNC(SDF_F_SDF_IMPORTKEY), "SDF_ImportKey"},
+    {ERR_FUNC(SDF_F_SDF_IMPORTKEYWITHISK_ECC), "SDF_ImportKeyWithISK_ECC"},
+    {ERR_FUNC(SDF_F_SDF_IMPORTKEYWITHISK_RSA), "SDF_ImportKeyWithISK_RSA"},
+    {ERR_FUNC(SDF_F_SDF_IMPORTKEYWITHKEK), "SDF_ImportKeyWithKEK"},
+    {ERR_FUNC(SDF_F_SDF_INTERNALDECRYPT_ECC), "SDF_InternalDecrypt_ECC"},
+    {ERR_FUNC(SDF_F_SDF_INTERNALENCRYPT_ECC), "SDF_InternalEncrypt_ECC"},
+    {ERR_FUNC(SDF_F_SDF_INTERNALPRIVATEKEYOPERATION_RSA),
+     "SDF_InternalPrivateKeyOperation_RSA"},
+    {ERR_FUNC(SDF_F_SDF_INTERNALPUBLICKEYOPERATION_RSA),
+     "SDF_InternalPublicKeyOperation_RSA"},
+    {ERR_FUNC(SDF_F_SDF_INTERNALSIGN_ECC), "SDF_InternalSign_ECC"},
+    {ERR_FUNC(SDF_F_SDF_INTERNALVERIFY_ECC), "SDF_InternalVerify_ECC"},
+    {ERR_FUNC(SDF_F_SDF_LOADLIBRARY), "SDF_LoadLibrary"},
+    {ERR_FUNC(SDF_F_SDF_METHOD_LOAD_LIBRARY), "SDF_METHOD_load_library"},
+    {ERR_FUNC(SDF_F_SDF_NEWECCCIPHER), "SDF_NewECCCipher"},
+    {ERR_FUNC(SDF_F_SDF_OPENDEVICE), "SDF_OpenDevice"},
+    {ERR_FUNC(SDF_F_SDF_OPENSESSION), "SDF_OpenSession"},
+    {ERR_FUNC(SDF_F_SDF_READFILE), "SDF_ReadFile"},
+    {ERR_FUNC(SDF_F_SDF_RELEASEPRIVATEKEYACCESSRIGHT),
+     "SDF_ReleasePrivateKeyAccessRight"},
+    {ERR_FUNC(SDF_F_SDF_WRITEFILE), "SDF_WriteFile"},
+    {0, NULL}
+};
+
+static ERR_STRING_DATA SDF_str_reasons[] = {
+    {ERR_REASON(SDF_R_ALGORITHM_MODE_NOT_SUPPORTED),
+     "algorithm mode not supported"},
+    {ERR_REASON(SDF_R_ALGORITHM_NOT_SUPPORTED), "algorithm not supported"},
+    {ERR_REASON(SDF_R_BUFFER_TOO_SMALL), "buffer too small"},
+    {ERR_REASON(SDF_R_COMMUNICATION_FAILURE), "communication failure"},
+    {ERR_REASON(SDF_R_DSO_LOAD_FAILURE), "dso load failure"},
+    {ERR_REASON(SDF_R_ENCRYPT_DATA_ERROR), "encrypt data error"},
+    {ERR_REASON(SDF_R_ERROR), "error"},
+    {ERR_REASON(SDF_R_FILE_ALREADY_EXIST), "file already exist"},
+    {ERR_REASON(SDF_R_FILE_NOT_EXIST), "file not exist"},
+    {ERR_REASON(SDF_R_HARDWARE_ERROR), "hardware error"},
+    {ERR_REASON(SDF_R_INVALID_FILE_OFFSET), "invalid file offset"},
+    {ERR_REASON(SDF_R_INVALID_FILE_SIZE), "invalid file size"},
+    {ERR_REASON(SDF_R_INVALID_INPUT_ARGUMENT), "invalid input argument"},
+    {ERR_REASON(SDF_R_INVALID_KEY), "invalid key"},
+    {ERR_REASON(SDF_R_INVALID_KEY_TYPE), "invalid key type"},
+    {ERR_REASON(SDF_R_INVALID_OUTPUT_ARGUMENT), "invalid output argument"},
+    {ERR_REASON(SDF_R_INVALID_SANSEC_ECCCIPHER_LENGTH),
+     "invalid sansec ecccipher length"},
+    {ERR_REASON(SDF_R_INVALID_SM2_CIPHERTEXT_LENGTH),
+     "invalid sm2 ciphertext length"},
+    {ERR_REASON(SDF_R_KEY_NOT_EXIST), "key not exist"},
+    {ERR_REASON(SDF_R_LOAD_LIBRARY_FAILURE), "load library failure"},
+    {ERR_REASON(SDF_R_MAC_ERROR), "mac error"},
+    {ERR_REASON(SDF_R_MULTI_STEP_OPERATION_ERROR),
+     "multi step operation error"},
+    {ERR_REASON(SDF_R_NOT_IMPLEMENTED), "not implemented"},
+    {ERR_REASON(SDF_R_NOT_INITIALIZED), "not initialized"},
+    {ERR_REASON(SDF_R_NOT_SUPPORTED_CIPHER_ALGOR),
+     "not supported cipher algor"},
+    {ERR_REASON(SDF_R_NOT_SUPPORTED_DIGEST_ALGOR),
+     "not supported digest algor"},
+    {ERR_REASON(SDF_R_NOT_SUPPORTED_ECC_ALGOR), "not supported ecc algor"},
+    {ERR_REASON(SDF_R_NOT_SUPPORTED_PKEY_ALGOR), "not supported pkey algor"},
+    {ERR_REASON(SDF_R_NO_PRIVATE_KEY_ACCESS_RIGHT),
+     "no private key access right"},
+    {ERR_REASON(SDF_R_OPEN_DEVICE_FAILURE), "open device failure"},
+    {ERR_REASON(SDF_R_OPEN_SESSION_FAILURE), "open session failure"},
+    {ERR_REASON(SDF_R_OPERATION_NOT_SUPPORTED), "operation not supported"},
+    {ERR_REASON(SDF_R_PRIVATE_KEY_OPERATION_FAILURE),
+     "private key operation failure"},
+    {ERR_REASON(SDF_R_PRKERR), "prkerr"},
+    {ERR_REASON(SDF_R_PUBLIC_KEY_OPERATION_FAILURE),
+     "public key operation failure"},
+    {ERR_REASON(SDF_R_RANDOM_GENERATION_ERROR), "random generation error"},
+    {ERR_REASON(SDF_R_SANSEC_BASE), "sansec base"},
+    {ERR_REASON(SDF_R_SANSEC_CARD_ALGOR_NOT_SUPPORTED),
+     "sansec card algor not supported"},
+    {ERR_REASON(SDF_R_SANSEC_CARD_ALG_MODE_NOT_SUPPORTED),
+     "sansec card alg mode not supported"},
+    {ERR_REASON(SDF_R_SANSEC_CARD_BASE), "sansec card base"},
+    {ERR_REASON(SDF_R_SANSEC_CARD_BUFFER_TOO_SMALL),
+     "sansec card buffer too small"},
+    {ERR_REASON(SDF_R_SANSEC_CARD_COMMMUCATION_FAILED),
+     "sansec card commmucation failed"},
+    {ERR_REASON(SDF_R_SANSEC_CARD_CRYPTO_NOT_INITED),
+     "sansec card crypto not inited"},
+    {ERR_REASON(SDF_R_SANSEC_CARD_DATA_PADDING_ERROR),
+     "sansec card data padding error"},
+    {ERR_REASON(SDF_R_SANSEC_CARD_DATA_SIZE), "sansec card data size"},
+    {ERR_REASON(SDF_R_SANSEC_CARD_DEVICE_STATUS_ERROR),
+     "sansec card device status error"},
+    {ERR_REASON(SDF_R_SANSEC_CARD_DEVICE_STATUS_ERROR_05),
+     "sansec card device status error 05"},
+    {ERR_REASON(SDF_R_SANSEC_CARD_FILE_NOT_EXIST),
+     "sansec card file not exist"},
+    {ERR_REASON(SDF_R_SANSEC_CARD_FILE_OFFSET_ERROR),
+     "sansec card file offset error"},
+    {ERR_REASON(SDF_R_SANSEC_CARD_FILE_SIZE_ERROR),
+     "sansec card file size error"},
+    {ERR_REASON(SDF_R_SANSEC_CARD_HARDWARE_FAILURE),
+     "sansec card hardware failure"},
+    {ERR_REASON(SDF_R_SANSEC_CARD_KEY_ERROR), "sansec card key error"},
+    {ERR_REASON(SDF_R_SANSEC_CARD_KEY_NOT_EXIST),
+     "sansec card key not exist"},
+    {ERR_REASON(SDF_R_SANSEC_CARD_KEY_TYPE_ERROR),
+     "sansec card key type error"},
+    {ERR_REASON(SDF_R_SANSEC_CARD_LOGIN_ERROR), "sansec card login error"},
+    {ERR_REASON(SDF_R_SANSEC_CARD_LOGIN_ERROR_05),
+     "sansec card login error 05"},
+    {ERR_REASON(SDF_R_SANSEC_CARD_MANAGEMENT_DENYED),
+     "sansec card management denyed"},
+    {ERR_REASON(SDF_R_SANSEC_CARD_MANAGEMENT_DENYED_05),
+     "sansec card management denyed 05"},
+    {ERR_REASON(SDF_R_SANSEC_CARD_NOT_SUPPORTED),
+     "sansec card not supported"},
+    {ERR_REASON(SDF_R_SANSEC_CARD_OPEN_DEVICE_FAILED),
+     "sansec card open device failed"},
+    {ERR_REASON(SDF_R_SANSEC_CARD_OPEN_SESSION_FAILED),
+     "sansec card open session failed"},
+    {ERR_REASON(SDF_R_SANSEC_CARD_OPERATION_DENYED),
+     "sansec card operation denyed"},
+    {ERR_REASON(SDF_R_SANSEC_CARD_OPERATION_DENYED_05),
+     "sansec card operation denyed 05"},
+    {ERR_REASON(SDF_R_SANSEC_CARD_PARAMENT_ERROR),
+     "sansec card parament error"},
+    {ERR_REASON(SDF_R_SANSEC_CARD_PARAMENT_ERROR_05),
+     "sansec card parament error 05"},
+    {ERR_REASON(SDF_R_SANSEC_CARD_PRIVATE_KEY_ACCESS_DENYED),
+     "sansec card private key access denyed"},
+    {ERR_REASON(SDF_R_SANSEC_CARD_PRIVATE_KEY_OPERATION_ERROR),
+     "sansec card private key operation error"},
+    {ERR_REASON(SDF_R_SANSEC_CARD_PUBLIC_KEY_OPERATION_ERROR),
+     "sansec card public key operation error"},
+    {ERR_REASON(SDF_R_SANSEC_CARD_READER_BASE), "sansec card reader base"},
+    {ERR_REASON(SDF_R_SANSEC_CARD_READER_CARD_INSERT),
+     "sansec card reader card insert"},
+    {ERR_REASON(SDF_R_SANSEC_CARD_READER_CARD_INSERT_TYPE),
+     "sansec card reader card insert type"},
+    {ERR_REASON(SDF_R_SANSEC_CARD_READER_NO_CARD),
+     "sansec card reader no card"},
+    {ERR_REASON(SDF_R_SANSEC_CARD_READER_PIN_ERROR),
+     "sansec card reader pin error"},
+    {ERR_REASON(SDF_R_SANSEC_CARD_SIGN_ERROR), "sansec card sign error"},
+    {ERR_REASON(SDF_R_SANSEC_CARD_STEP_ERROR), "sansec card step error"},
+    {ERR_REASON(SDF_R_SANSEC_CARD_SYMMETRIC_ALGOR_ERROR),
+     "sansec card symmetric algor error"},
+    {ERR_REASON(SDF_R_SANSEC_CARD_UNKNOW_ERROR), "sansec card unknow error"},
+    {ERR_REASON(SDF_R_SANSEC_CARD_USERID_ERROR), "sansec card userid error"},
+    {ERR_REASON(SDF_R_SANSEC_CARD_USERID_ERROR_05),
+     "sansec card userid error 05"},
+    {ERR_REASON(SDF_R_SANSEC_CARD_VERIFY_ERROR), "sansec card verify error"},
+    {ERR_REASON(SDF_R_SANSEC_CONFIG_ERROR), "sansec config error"},
+    {ERR_REASON(SDF_R_SANSEC_CONNECT_ERROR), "sansec connect error"},
+    {ERR_REASON(SDF_R_SANSEC_FILE_ALREADY_EXIST),
+     "sansec file already exist"},
+    {ERR_REASON(SDF_R_SANSEC_INVALID_AUTHENCODE),
+     "sansec invalid authencode"},
+    {ERR_REASON(SDF_R_SANSEC_INVALID_COMMAND), "sansec invalid command"},
+    {ERR_REASON(SDF_R_SANSEC_INVALID_PARAMETERS),
+     "sansec invalid parameters"},
+    {ERR_REASON(SDF_R_SANSEC_INVALID_USER), "sansec invalid user"},
+    {ERR_REASON(SDF_R_SANSEC_NO_AVAILABLE_CSM), "sansec no available csm"},
+    {ERR_REASON(SDF_R_SANSEC_NO_AVAILABLE_HSM), "sansec no available hsm"},
+    {ERR_REASON(SDF_R_SANSEC_PROTOCOL_VERSION_ERROR),
+     "sansec protocol version error"},
+    {ERR_REASON(SDF_R_SANSEC_SEM_TIMEOUT), "sansec sem timeout"},
+    {ERR_REASON(SDF_R_SANSEC_SET_SOCKET_OPTION_ERROR),
+     "sansec set socket option error"},
+    {ERR_REASON(SDF_R_SANSEC_SOCKET_RECV_0), "sansec socket recv 0"},
+    {ERR_REASON(SDF_R_SANSEC_SOCKET_RECV_ERROR), "sansec socket recv error"},
+    {ERR_REASON(SDF_R_SANSEC_SOCKET_SEND_ERROR), "sansec socket send error"},
+    {ERR_REASON(SDF_R_SANSEC_SOCKET_TIMEOUT), "sansec socket timeout"},
+    {ERR_REASON(SDF_R_SANSEC_SYNC_ERROR), "sansec sync error"},
+    {ERR_REASON(SDF_R_SANSEC_SYNC_LOGIN_ERROR), "sansec sync login error"},
+    {ERR_REASON(SDF_R_SIGNING_FAILURE), "signing failure"},
+    {ERR_REASON(SDF_R_SUCCESS), "success"},
+    {ERR_REASON(SDF_R_SYMMETRIC_OPERATION_FAILURE),
+     "symmetric operation failure"},
+    {ERR_REASON(SDF_R_UNNOWN_ERROR), "unnown error"},
+    {ERR_REASON(SDF_R_VERIFICATION_FAILURE), "verification failure"},
+    {ERR_REASON(SDF_R_WRITE_FILE_FAILURE), "write file failure"},
+    {0, NULL}
+};
+
+#endif
+
+int ERR_load_SDF_strings(void)
+{
+#ifndef OPENSSL_NO_ERR
+
+    if (ERR_func_error_string(SDF_str_functs[0].error) == NULL) {
+        ERR_load_strings(0, SDF_str_functs);
+        ERR_load_strings(0, SDF_str_reasons);
+    }
+#endif
+    return 1;
+}
diff --git a/crypto/sdf/sdf_ext.c b/crypto/sdf/sdf_ext.c
new file mode 100644
index 000000000..5f4cdcc26
--- /dev/null
+++ b/crypto/sdf/sdf_ext.c
@@ -0,0 +1,282 @@
+/* ====================================================================
+ * Copyright (c) 2016 - 2017 The GmSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the GmSSL Project.
+ *    (http://gmssl.org/)"
+ *
+ * 4. The name "GmSSL Project" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    guanzhi1980@gmail.com.
+ *
+ * 5. Products derived from this software may not be called "GmSSL"
+ *    nor may "GmSSL" appear in their names without prior written
+ *    permission of the GmSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the GmSSL Project
+ *    (http://gmssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE GmSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE GmSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+
+#include 
+#include 
+#include 
+#include "internal/sdf_int.h"
+#include "../../e_os.h"
+#include "sdf_sansec.h"
+
+
+typedef struct {
+	ULONG id;
+	char *name;
+} table_item_t;
+
+static table_item_t sdf_cipher_caps[] = {
+	{ SGD_SM1_ECB, "sm1-ecb" },
+	{ SGD_SM1_CBC, "sm1-cbc" },
+	{ SGD_SM1_CFB, "sm1-cfb" },
+	{ SGD_SM1_OFB, "sm1-ofb128" },
+	{ SGD_SM1_MAC, "cbcmac-sm1" },
+	{ SGD_SSF33_ECB, "ssf33-ecb" },
+	{ SGD_SSF33_CBC, "ssf33-cbc" },
+	{ SGD_SSF33_CFB, "ssf33-cfb" },
+	{ SGD_SSF33_OFB, "ssf33-ofb128" },
+	{ SGD_SSF33_MAC, "cbcmac-ssf33" },
+	{ SGD_SM4_ECB, "sms4-ecb" },
+	{ SGD_SM4_CBC, "sms4-cbc" },
+	{ SGD_SM4_CFB, "sms4-cfb" },
+	{ SGD_SM4_OFB, "sms4-ofb128" },
+	{ SGD_SM4_MAC, "cbcmac-sms4" },
+	{ SGD_ZUC_EEA3, "zuc_128eea3" },
+	{ SGD_ZUC_EIA3, "zuc_128eia3" }
+};
+
+static table_item_t sdf_digest_caps[] = {
+	{ SGD_SM3,  "sm3" },
+	{ SGD_SHA1, "sha1" },
+	{ SGD_SHA256, "sha256" },
+};
+
+static table_item_t sdf_pkey_caps[] = {
+	{ SGD_RSA_SIGN, "rsa" },
+	{ SGD_RSA_ENC, "rsaEncryption" },
+	{ SGD_SM2_1, "sm2sign" },
+	{ SGD_SM2_2, "sm2exchange" },
+	{ SGD_SM2_3, "sm2encrypt" }
+};
+
+int SDF_PrintDeviceInfo(BIO *out, DEVICEINFO *pstDeviceInfo)
+{
+	size_t i, n;
+	DEVICEINFO buf;
+	DEVICEINFO *devInfo = &buf;
+
+	memcpy(devInfo, pstDeviceInfo, sizeof(DEVICEINFO));
+	devInfo->IssuerName[39] = 0;
+	devInfo->DeviceName[15] = 0;
+	devInfo->DeviceSerial[15] = 0;
+
+	BIO_printf(out, "  %-18s : %s\n", "Device Name", devInfo->DeviceName);
+	BIO_printf(out, "  %-18s : %s\n", "Serial Number", devInfo->DeviceSerial);
+	BIO_printf(out, "  %-18s : %s\n", "Issuer", devInfo->IssuerName);
+	BIO_printf(out, "  %-18s : %u\n", "Hardware Version", devInfo->DeviceVersion);
+	BIO_printf(out, "  %-18s : %u\n", "Standard Version", devInfo->StandardVersion);
+	BIO_printf(out, "  %-18s : ", "Public Key Algors");
+	for (i = n = 0; i < OSSL_NELEM(sdf_pkey_caps); i++) {
+		if ((devInfo->AsymAlgAbility[0] & sdf_pkey_caps[i].id) ==
+			sdf_pkey_caps[i].id) {
+			BIO_printf(out, "%s%s", n ? "," : "", sdf_pkey_caps[i].name);
+			n++;
+		}
+	}
+	BIO_puts(out, "\n");
+
+	BIO_printf(out, "  %-18s : ", "Ciphers");
+	for (i = n = 0; i < OSSL_NELEM(sdf_cipher_caps); i++) {
+		if ((devInfo->SymAlgAbility & sdf_cipher_caps[i].id) ==
+			sdf_cipher_caps[i].id) {
+			BIO_printf(out, "%s%s", n ? "," : "", sdf_cipher_caps[i].name);
+			n++;
+		}
+	}
+	BIO_puts(out, "\n");
+
+	BIO_printf(out, "  %-18s : ", "Digests");
+	for (i = n = 0; i < OSSL_NELEM(sdf_digest_caps); i++) {
+		if ((devInfo->HashAlgAbility & sdf_digest_caps[i].id) ==
+			sdf_digest_caps[i].id) {
+			BIO_printf(out, "%s%s", n ? "," : "", sdf_digest_caps[i].name);
+			n++;
+		}
+	}
+	BIO_puts(out, "\n");
+	BIO_puts(out, "\n");
+
+	return SDR_OK;
+}
+
+int SDF_PrintRSAPublicKey(BIO *out, RSArefPublicKey *blob)
+{
+	(void)BIO_printf(out, "bits: %d\n", blob->bits);
+	(void)BIO_printf(out, "m:\n    ");
+	(void)BIO_hex_string(out, 4, 16, blob->m, sizeof(blob->m));
+	(void)BIO_printf(out, "\n");
+	(void)BIO_printf(out, "e:\n    ");
+	(void)BIO_hex_string(out, 4, 16, blob->e, sizeof(blob->e));
+	(void)BIO_printf(out, "\n");
+	return SDR_OK;
+}
+
+int SDF_PrintRSAPrivateKey(BIO *bio, RSArefPrivateKey *blob)
+{
+	(void)BIO_printf(bio, "bits: %d", blob->bits);
+	(void)BIO_printf(bio, "\n%s:\n    ", "m");
+	(void)BIO_hex_string(bio, 4, 16, blob->m, sizeof(blob->m));
+	(void)BIO_printf(bio, "\n%s:\n    ", "e");
+	(void)BIO_hex_string(bio, 4, 16, blob->e, sizeof(blob->e));
+	(void)BIO_printf(bio, "\n%s:\n    ", "d");
+	(void)BIO_hex_string(bio, 4, 16, blob->d, sizeof(blob->d));
+	(void)BIO_printf(bio, "\n%s:\n    ", "prime[0]");
+	(void)BIO_hex_string(bio, 4, 16, blob->prime[0], sizeof(blob->prime[0]));
+	(void)BIO_printf(bio, "\n%s:\n    ", "prime[1]");
+	(void)BIO_hex_string(bio, 4, 16, blob->prime[1], sizeof(blob->prime[1]));
+	(void)BIO_printf(bio, "\n%s:\n    ", "pexp[0]");
+	(void)BIO_hex_string(bio, 4, 16, blob->pexp[0], sizeof(blob->pexp[0]));
+	(void)BIO_printf(bio, "\n%s:\n    ", "pexp[1]");
+	(void)BIO_hex_string(bio, 4, 16, blob->pexp[1], sizeof(blob->pexp[1]));
+	(void)BIO_printf(bio, "\n%s:\n    ", "coef");
+	(void)BIO_hex_string(bio, 4, 16, blob->coef, sizeof(blob->coef));
+	(void)BIO_printf(bio, "\n");
+
+	return SDR_OK;
+}
+
+int SDF_PrintECCPublicKey(BIO *bio, ECCrefPublicKey *blob)
+{
+	(void)BIO_printf(bio, "bits: %d", blob->bits);
+	(void)BIO_printf(bio, "\n%s:\n    ", "x");
+	(void)BIO_hex_string(bio, 4, 16, blob->x, sizeof(blob->x));
+	(void)BIO_printf(bio, "\n%s:\n    ", "y");
+	(void)BIO_hex_string(bio, 4, 16, blob->y, sizeof(blob->y));
+	(void)BIO_printf(bio, "\n");
+
+	return SDR_OK;
+}
+
+int SDF_PrintECCPrivateKey(BIO *bio, ECCrefPrivateKey *blob)
+{
+	(void)BIO_printf(bio, "bits: %d", blob->bits);
+	(void)BIO_printf(bio, "\n%s:\n    ", "K");
+	(void)BIO_hex_string(bio, 4, 16, blob->K, sizeof(blob->K));
+	(void)BIO_printf(bio, "\n");
+
+	return SDR_OK;
+}
+
+int SDF_PrintECCCipher(BIO *bio, ECCCipher *blob)
+{
+	(void)BIO_printf(bio, "%s:\n    ", "x");
+	(void)BIO_hex_string(bio, 4, 16, blob->x, sizeof(blob->x));
+	(void)BIO_printf(bio, "\n%s:\n    ", "y");
+	(void)BIO_hex_string(bio, 4, 16, blob->y, sizeof(blob->y));
+	(void)BIO_printf(bio, "\n%s:\n    ", "M");
+	(void)BIO_hex_string(bio, 4, 16, blob->M, sizeof(blob->M));
+	(void)BIO_printf(bio, "\nL: %d", blob->L);
+	(void)BIO_printf(bio, "\n%s:\n    ", "C");
+	(void)BIO_hex_string(bio, 4, 16, blob->C, sizeof(blob->C));
+	(void)BIO_printf(bio, "\n");
+
+	return SDR_OK;
+}
+
+int SDF_PrintECCSignature(BIO *bio, ECCSignature *blob)
+{
+	(void)BIO_printf(bio, "%s:\n    ", "r");
+	(void)BIO_hex_string(bio, 4, 16, blob->r, sizeof(blob->r));
+	(void)BIO_printf(bio, "\n%s:\n    ", "s");
+	(void)BIO_hex_string(bio, 4, 16, blob->s, sizeof(blob->s));
+	(void)BIO_printf(bio, "\n");
+
+	return SDR_OK;
+}
+
+int SDF_ImportKey(
+	void *hSessionHandle,
+	unsigned char *pucKey,
+	unsigned int uiKeyLength,
+	void **phKeyHandle)
+{
+	(void)hSessionHandle;
+	(void)pucKey;
+	(void)uiKeyLength;
+	(void)phKeyHandle;
+	SDFerr(SDF_F_SDF_IMPORTKEY, SDF_R_NOT_IMPLEMENTED);
+	return SDR_NOTSUPPORT;
+}
+
+int SDF_NewECCCipher(ECCCipher **cipher, size_t ulDataLen)
+{
+	ECCCipher *ecc_cipher = NULL;
+	size_t len;
+
+	if (!cipher) {
+		SDFerr(SDF_F_SDF_NEWECCCIPHER, ERR_R_PASSED_NULL_PARAMETER);
+		return SDR_INARGERR;
+	}
+
+	if (!ulDataLen || ulDataLen > INT_MAX) {
+		SDFerr(SDF_F_SDF_NEWECCCIPHER,
+			SDF_R_INVALID_SM2_CIPHERTEXT_LENGTH);
+		return SDR_INARGERR;
+	}
+
+	len = sizeof(ECCCipher) - 1 + ulDataLen;
+	if (len < sizeof(SANSEC_ECCCipher)) {
+		len = sizeof(SANSEC_ECCCipher);
+	}
+
+	if (!(ecc_cipher = OPENSSL_zalloc(len))) {
+		SDFerr(SDF_F_SDF_NEWECCCIPHER, ERR_R_MALLOC_FAILURE);
+		return SDR_NOBUFFER;
+	}
+
+	ecc_cipher->L = (unsigned int)ulDataLen;
+
+	*cipher = ecc_cipher;
+	return SDR_OK;
+}
+
+int SDF_FreeECCCipher(ECCCipher *cipher)
+{
+	OPENSSL_free(cipher);
+	return SDR_OK;
+}
diff --git a/crypto/sdf/sdf_lib.c b/crypto/sdf/sdf_lib.c
new file mode 100644
index 000000000..5932e378a
--- /dev/null
+++ b/crypto/sdf/sdf_lib.c
@@ -0,0 +1,1604 @@
+/* ====================================================================
+ * Copyright (c) 2016 - 2017 The GmSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the GmSSL Project.
+ *    (http://gmssl.org/)"
+ *
+ * 4. The name "GmSSL Project" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    guanzhi1980@gmail.com.
+ *
+ * 5. Products derived from this software may not be called "GmSSL"
+ *    nor may "GmSSL" appear in their names without prior written
+ *    permission of the GmSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the GmSSL Project
+ *    (http://gmssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE GmSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE GmSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+
+#include 
+#include 
+#include 
+#include "internal/sdf_int.h"
+#include "../../e_os.h"
+
+SDF_METHOD *sdf_method = NULL;
+SDF_VENDOR *sdf_vendor = NULL;
+extern SDF_VENDOR sdf_sansec;
+
+
+int SDF_LoadLibrary(char *so_path, char *vendor)
+{
+	if (sdf_method) {
+		SDF_METHOD_free(sdf_method);
+		sdf_method = NULL;
+	}
+
+	if (!(sdf_method = SDF_METHOD_load_library(so_path))) {
+		SDFerr(SDF_F_SDF_LOADLIBRARY, SDF_R_LOAD_LIBRARY_FAILURE);
+		return SDR_BASE;
+	}
+
+	if (vendor) {
+		if (strcmp(vendor, sdf_sansec.name) == 0) {
+			sdf_vendor = &sdf_sansec;
+		}
+	}
+
+	return SDR_OK;
+}
+
+int SDF_UnloadLibrary(void)
+{
+	SDF_METHOD_free(sdf_method);
+	sdf_method = NULL;
+	sdf_vendor = NULL;
+	return SDR_OK;
+}
+
+static SDF_ERR_REASON sdf_errors[] = {
+	{ SDR_OK,		SDF_R_SUCCESS },
+	{ SDR_BASE,		SDF_R_ERROR },
+	{ SDR_UNKNOWERR,	SDF_R_UNNOWN_ERROR },
+	{ SDR_NOTSUPPORT,	SDF_R_OPERATION_NOT_SUPPORTED },
+	{ SDR_COMMFAIL,		SDF_R_COMMUNICATION_FAILURE },
+	{ SDR_HARDFAIL,		SDF_R_HARDWARE_ERROR },
+	{ SDR_OPENDEVICE,	SDF_R_OPEN_DEVICE_FAILURE },
+	{ SDR_OPENSESSION,	SDF_R_OPEN_SESSION_FAILURE },
+	{ SDR_PARDENY,		SDF_R_NO_PRIVATE_KEY_ACCESS_RIGHT },
+	{ SDR_KEYNOTEXIST,	SDF_R_KEY_NOT_EXIST },
+	{ SDR_ALGNOTSUPPORT,	SDF_R_ALGORITHM_NOT_SUPPORTED },
+	{ SDR_ALGMODNOTSUPPORT,	SDF_R_ALGORITHM_MODE_NOT_SUPPORTED },
+	{ SDR_PKOPERR,		SDF_R_PUBLIC_KEY_OPERATION_FAILURE },
+	{ SDR_SKOPERR,		SDF_R_PRIVATE_KEY_OPERATION_FAILURE },
+	{ SDR_SIGNERR,		SDF_R_SIGNING_FAILURE },
+	{ SDR_VERIFYERR,	SDF_R_VERIFICATION_FAILURE },
+	{ SDR_SYMOPERR,		SDF_R_SYMMETRIC_OPERATION_FAILURE },
+	{ SDR_STEPERR,		SDF_R_MULTI_STEP_OPERATION_ERROR },
+	{ SDR_FILESIZEERR,	SDF_R_INVALID_FILE_SIZE },
+	{ SDR_FILENOEXIST,	SDF_R_FILE_NOT_EXIST },
+	{ SDR_FILEOFSERR,	SDF_R_INVALID_FILE_OFFSET },
+	{ SDR_KEYTYPEERR,	SDF_R_INVALID_KEY_TYPE },
+	{ SDR_KEYERR,		SDF_R_INVALID_KEY },
+	{ SDR_ENCDATAERR,	SDF_R_ENCRYPT_DATA_ERROR },
+	{ SDR_RANDERR,		SDF_R_RANDOM_GENERATION_ERROR },
+	{ SDR_PRKRERR,		SDF_R_PRKERR },
+	{ SDR_MACERR,		SDF_R_MAC_ERROR },
+	{ SDR_FILEEXSITS,	SDF_R_FILE_ALREADY_EXIST },
+	{ SDR_FILEWERR,		SDF_R_WRITE_FILE_FAILURE },
+	{ SDR_NOBUFFER,		SDF_R_BUFFER_TOO_SMALL },
+	{ SDR_INARGERR,		SDF_R_INVALID_INPUT_ARGUMENT },
+	{ SDR_OUTARGERR,	SDF_R_INVALID_OUTPUT_ARGUMENT },
+};
+
+static unsigned long sdf_get_error_reason(int err)
+{
+	size_t i;
+	for (i = 0; i < OSSL_NELEM(sdf_errors); i++) {
+		if (err == sdf_errors[i].err) {
+			return sdf_errors[i].reason;
+		}
+	}
+	if (sdf_vendor) {
+		return sdf_vendor->get_error_reason(err);
+	}
+	return 0;
+}
+
+int SDF_GetErrorString(int err, char **str)
+{
+	unsigned long reason;
+
+	if ((reason = sdf_get_error_reason(err)) != 0) {
+		*str = (char*)ERR_reason_error_string(reason);
+	} else {
+		*str = "(unknown)";
+	}
+
+	return SDR_OK;
+}
+
+int SDF_OpenDevice(
+	void **phDeviceHandle)
+{
+	int ret = SDR_UNKNOWERR;
+
+	if (!sdf_method  || !sdf_method->OpenDevice) {
+		SDFerr(SDF_F_SDF_OPENDEVICE, SDF_R_NOT_INITIALIZED);
+		return SDR_NOTSUPPORT;
+	}
+
+	if ((ret = sdf_method->OpenDevice(
+		phDeviceHandle)) != SDR_OK) {
+		SDFerr(SDF_F_SDF_OPENDEVICE, sdf_get_error_reason(ret));
+		return ret;
+	}
+
+	return SDR_OK;
+}
+
+int SDF_CloseDevice(
+	void *hDeviceHandle)
+{
+	int ret = SDR_UNKNOWERR;
+
+	if (!sdf_method || !sdf_method->CloseDevice) {
+		SDFerr(SDF_F_SDF_CLOSEDEVICE, SDF_R_NOT_INITIALIZED);
+		return SDR_NOTSUPPORT;
+	}
+
+	if ((ret = sdf_method->CloseDevice(
+		hDeviceHandle)) != SDR_OK) {
+		SDFerr(SDF_F_SDF_CLOSEDEVICE, sdf_get_error_reason(ret));
+		return ret;
+	}
+
+	return SDR_OK;
+}
+
+int SDF_OpenSession(
+	void *hDeviceHandle,
+	void **phSessionHandle)
+{
+	int ret = SDR_UNKNOWERR;
+
+	if (!sdf_method || !sdf_method->OpenSession) {
+		SDFerr(SDF_F_SDF_OPENSESSION, SDF_R_NOT_INITIALIZED);
+		return SDR_NOTSUPPORT;
+	}
+
+	if ((ret = sdf_method->OpenSession(
+		hDeviceHandle,
+		phSessionHandle)) != SDR_OK) {
+		SDFerr(SDF_F_SDF_OPENSESSION, sdf_get_error_reason(ret));
+		return ret;
+	}
+
+	return SDR_OK;
+}
+
+int SDF_CloseSession(
+	void *hSessionHandle)
+{
+	int ret = SDR_UNKNOWERR;
+
+	if (!sdf_method || !sdf_method->CloseSession) {
+		SDFerr(SDF_F_SDF_CLOSESESSION, SDF_R_NOT_INITIALIZED);
+		return SDR_NOTSUPPORT;
+	}
+
+	if ((ret = sdf_method->CloseSession(
+		hSessionHandle)) != SDR_OK) {
+		SDFerr(SDF_F_SDF_CLOSESESSION, sdf_get_error_reason(ret));
+		return ret;
+	}
+
+	return SDR_OK;
+}
+
+int SDF_GetDeviceInfo(
+	void *hSessionHandle,
+	DEVICEINFO *pstDeviceInfo)
+{
+	int ret = SDR_UNKNOWERR;
+
+	if (!sdf_method || !sdf_method->GetDeviceInfo) {
+		SDFerr(SDF_F_SDF_GETDEVICEINFO, SDF_R_NOT_INITIALIZED);
+		return SDR_NOTSUPPORT;
+	}
+
+	if ((ret = sdf_method->GetDeviceInfo(
+		hSessionHandle,
+		pstDeviceInfo)) != SDR_OK) {
+		SDFerr(SDF_F_SDF_GETDEVICEINFO, sdf_get_error_reason(ret));
+		return ret;
+	}
+
+	return SDR_OK;
+}
+
+int SDF_GenerateRandom(
+	void *hSessionHandle,
+	unsigned int uiLength,
+	unsigned char *pucRandom)
+{
+	int ret = SDR_UNKNOWERR;
+
+	if (!sdf_method || !sdf_method->GenerateRandom) {
+		SDFerr(SDF_F_SDF_GENERATERANDOM, SDF_R_NOT_INITIALIZED);
+		return SDR_NOTSUPPORT;
+	}
+
+	if ((ret = sdf_method->GenerateRandom(
+		hSessionHandle,
+		uiLength,
+		pucRandom)) != SDR_OK) {
+		SDFerr(SDF_F_SDF_GENERATERANDOM, sdf_get_error_reason(ret));
+		return ret;
+	}
+
+	return SDR_OK;
+}
+
+int SDF_GetPrivateKeyAccessRight(
+	void *hSessionHandle,
+	unsigned int uiKeyIndex,
+	unsigned char *pucPassword,
+	unsigned int uiPwdLength)
+{
+	int ret = SDR_UNKNOWERR;
+
+	if (!sdf_method || !sdf_method->GetPrivateKeyAccessRight) {
+		SDFerr(SDF_F_SDF_GETPRIVATEKEYACCESSRIGHT, SDF_R_NOT_INITIALIZED);
+		return SDR_NOTSUPPORT;
+	}
+
+	if ((ret = sdf_method->GetPrivateKeyAccessRight(
+		hSessionHandle,
+		uiKeyIndex,
+		pucPassword,
+		uiPwdLength)) != SDR_OK) {
+		SDFerr(SDF_F_SDF_GETPRIVATEKEYACCESSRIGHT, sdf_get_error_reason(ret));
+		return ret;
+	}
+
+	return SDR_OK;
+}
+
+int SDF_ReleasePrivateKeyAccessRight(
+	void *hSessionHandle,
+	unsigned int uiKeyIndex)
+{
+	int ret = SDR_UNKNOWERR;
+
+	if (!sdf_method || !sdf_method->ReleasePrivateKeyAccessRight) {
+		SDFerr(SDF_F_SDF_RELEASEPRIVATEKEYACCESSRIGHT,
+			SDF_R_NOT_INITIALIZED);
+		return SDR_NOTSUPPORT;
+	}
+
+	if ((ret = sdf_method->ReleasePrivateKeyAccessRight(
+		hSessionHandle,
+		uiKeyIndex)) != SDR_OK) {
+		SDFerr(SDF_F_SDF_RELEASEPRIVATEKEYACCESSRIGHT,
+			sdf_get_error_reason(ret));
+		return ret;
+	}
+
+	return SDR_OK;
+}
+
+int SDF_ExportSignPublicKey_RSA(
+	void *hSessionHandle,
+	unsigned int uiKeyIndex,
+	RSArefPublicKey *pucPublicKey)
+{
+	int ret = SDR_UNKNOWERR;
+
+	if (!sdf_method || !sdf_method->ExportSignPublicKey_RSA) {
+		SDFerr(SDF_F_SDF_EXPORTSIGNPUBLICKEY_RSA, SDF_R_NOT_INITIALIZED);
+		return SDR_NOTSUPPORT;
+	}
+
+	if ((ret = sdf_method->ExportSignPublicKey_RSA(
+		hSessionHandle,
+		uiKeyIndex,
+		pucPublicKey)) != SDR_OK) {
+		SDFerr(SDF_F_SDF_EXPORTSIGNPUBLICKEY_RSA,
+			sdf_get_error_reason(ret));
+		return ret;
+	}
+
+	return SDR_OK;
+}
+
+int SDF_ExportEncPublicKey_RSA(
+	void *hSessionHandle,
+	unsigned int uiKeyIndex,
+	RSArefPublicKey *pucPublicKey)
+{
+	int ret = SDR_UNKNOWERR;
+
+	if (!sdf_method || !sdf_method->ExportEncPublicKey_RSA) {
+		SDFerr(SDF_F_SDF_EXPORTENCPUBLICKEY_RSA, SDF_R_NOT_INITIALIZED);
+		return SDR_NOTSUPPORT;
+	}
+
+	if ((ret = sdf_method->ExportEncPublicKey_RSA(
+		hSessionHandle,
+		uiKeyIndex,
+		pucPublicKey)) != SDR_OK) {
+		SDFerr(SDF_F_SDF_EXPORTENCPUBLICKEY_RSA,
+			sdf_get_error_reason(ret));
+		return ret;
+	}
+
+	return SDR_OK;
+}
+
+int SDF_GenerateKeyPair_RSA(
+	void *hSessionHandle,
+	unsigned int uiKeyBits,
+	RSArefPublicKey *pucPublicKey,
+	RSArefPrivateKey *pucPrivateKey)
+{
+	int ret = SDR_UNKNOWERR;
+
+	if (!sdf_method || !sdf_method->GenerateKeyPair_RSA) {
+		SDFerr(SDF_F_SDF_GENERATEKEYPAIR_RSA, SDF_R_NOT_INITIALIZED);
+		return SDR_NOTSUPPORT;
+	}
+
+	if ((ret = sdf_method->GenerateKeyPair_RSA(
+		hSessionHandle,
+		uiKeyBits,
+		pucPublicKey,
+		pucPrivateKey)) != SDR_OK) {
+		SDFerr(SDF_F_SDF_GENERATEKEYPAIR_RSA,
+			sdf_get_error_reason(ret));
+		return ret;
+	}
+
+	return SDR_OK;
+}
+
+int SDF_GenerateKeyWithIPK_RSA(
+	void *hSessionHandle,
+	unsigned int uiIPKIndex,
+	unsigned int uiKeyBits,
+	unsigned char *pucKey,
+	unsigned int *puiKeyLength,
+	void **phKeyHandle)
+{
+	int ret = SDR_UNKNOWERR;
+
+	if (!sdf_method || !sdf_method->GenerateKeyWithIPK_RSA) {
+		SDFerr(SDF_F_SDF_GENERATEKEYWITHIPK_RSA,
+			SDF_R_NOT_INITIALIZED);
+		return SDR_NOTSUPPORT;
+	}
+
+	if ((ret = sdf_method->GenerateKeyWithIPK_RSA(
+		hSessionHandle,
+		uiIPKIndex,
+		uiKeyBits,
+		pucKey,
+		puiKeyLength,
+		phKeyHandle)) != SDR_OK) {
+		SDFerr(SDF_F_SDF_GENERATEKEYWITHIPK_RSA,
+			sdf_get_error_reason(ret));
+		return ret;
+	}
+
+	return SDR_OK;
+}
+
+int SDF_GenerateKeyWithEPK_RSA(
+	void *hSessionHandle,
+	unsigned int uiKeyBits,
+	RSArefPublicKey *pucPublicKey,
+	unsigned char *pucKey,
+	unsigned int *puiKeyLength,
+	void **phKeyHandle)
+{
+	int ret = SDR_UNKNOWERR;
+
+	if (!sdf_method || !sdf_method->GenerateKeyWithEPK_RSA) {
+		SDFerr(SDF_F_SDF_GENERATEKEYWITHEPK_RSA, SDF_R_NOT_INITIALIZED);
+		return SDR_NOTSUPPORT;
+	}
+
+	if ((ret = sdf_method->GenerateKeyWithEPK_RSA(
+		hSessionHandle,
+		uiKeyBits,
+		pucPublicKey,
+		pucKey,
+		puiKeyLength,
+		phKeyHandle)) != SDR_OK) {
+		SDFerr(SDF_F_SDF_GENERATEKEYWITHEPK_RSA,
+			sdf_get_error_reason(ret));
+		return ret;
+	}
+
+	return SDR_OK;
+}
+
+int SDF_ImportKeyWithISK_RSA(
+	void *hSessionHandle,
+	unsigned int uiISKIndex,
+	unsigned char *pucKey,
+	unsigned int uiKeyLength,
+	void **phKeyHandle)
+{
+	int ret = SDR_UNKNOWERR;
+
+	if (!sdf_method || !sdf_method->ImportKeyWithISK_RSA) {
+		SDFerr(SDF_F_SDF_IMPORTKEYWITHISK_RSA, SDF_R_NOT_INITIALIZED);
+		return SDR_NOTSUPPORT;
+	}
+
+	if ((ret = sdf_method->ImportKeyWithISK_RSA(
+		hSessionHandle,
+		uiISKIndex,
+		pucKey,
+		uiKeyLength,
+		phKeyHandle)) != SDR_OK) {
+		SDFerr(SDF_F_SDF_IMPORTKEYWITHISK_RSA, sdf_get_error_reason(ret));
+		return ret;
+	}
+
+	return SDR_OK;
+}
+
+int SDF_ExchangeDigitEnvelopeBaseOnRSA(
+	void *hSessionHandle,
+	unsigned int uiKeyIndex,
+	RSArefPublicKey *pucPublicKey,
+	unsigned char *pucDEInput,
+	unsigned int uiDELength,
+	unsigned char *pucDEOutput,
+	unsigned int *puiDELength)
+{
+	int ret = SDR_UNKNOWERR;
+
+	if (!sdf_method || !sdf_method->ExchangeDigitEnvelopeBaseOnRSA) {
+		SDFerr(SDF_F_SDF_EXCHANGEDIGITENVELOPEBASEONRSA,
+			SDF_R_NOT_INITIALIZED);
+		return SDR_NOTSUPPORT;
+	}
+
+	if ((ret = sdf_method->ExchangeDigitEnvelopeBaseOnRSA(
+		hSessionHandle,
+		uiKeyIndex,
+		pucPublicKey,
+		pucDEInput,
+		uiDELength,
+		pucDEOutput,
+		puiDELength)) != SDR_OK) {
+		SDFerr(SDF_F_SDF_EXCHANGEDIGITENVELOPEBASEONRSA,
+			sdf_get_error_reason(ret));
+		return ret;
+	}
+
+	return SDR_OK;
+}
+
+int SDF_ExportSignPublicKey_ECC(
+	void *hSessionHandle,
+	unsigned int uiKeyIndex,
+	ECCrefPublicKey *pucPublicKey)
+{
+	int ret = SDR_UNKNOWERR;
+
+	if (!sdf_method || !sdf_method->ExportSignPublicKey_ECC) {
+		SDFerr(SDF_F_SDF_EXPORTSIGNPUBLICKEY_ECC, SDF_R_NOT_INITIALIZED);
+		return SDR_NOTSUPPORT;
+	}
+
+	if ((ret = sdf_method->ExportSignPublicKey_ECC(
+		hSessionHandle,
+		uiKeyIndex,
+		pucPublicKey)) != SDR_OK) {
+		SDFerr(SDF_F_SDF_EXPORTSIGNPUBLICKEY_ECC,
+			sdf_get_error_reason(ret));
+		return ret;
+	}
+
+	return SDR_OK;
+}
+
+int SDF_ExportEncPublicKey_ECC(
+	void *hSessionHandle,
+	unsigned int uiKeyIndex,
+	ECCrefPublicKey *pucPublicKey)
+{
+	int ret = SDR_UNKNOWERR;
+
+	if (!sdf_method || !sdf_method->ExportEncPublicKey_ECC) {
+		SDFerr(SDF_F_SDF_EXPORTENCPUBLICKEY_ECC, SDF_R_NOT_INITIALIZED);
+		return SDR_NOTSUPPORT;
+	}
+
+	if ((ret = sdf_method->ExportEncPublicKey_ECC(
+		hSessionHandle,
+		uiKeyIndex,
+		pucPublicKey)) != SDR_OK) {
+		SDFerr(SDF_F_SDF_EXPORTENCPUBLICKEY_ECC,
+			sdf_get_error_reason(ret));
+		return ret;
+	}
+
+	return SDR_OK;
+}
+
+int SDF_GenerateKeyPair_ECC(
+	void *hSessionHandle,
+	unsigned int uiAlgID,
+	unsigned int  uiKeyBits,
+	ECCrefPublicKey *pucPublicKey,
+	ECCrefPrivateKey *pucPrivateKey)
+{
+	int ret = SDR_UNKNOWERR;
+
+	if (!sdf_method || !sdf_method->GenerateKeyPair_ECC) {
+		SDFerr(SDF_F_SDF_GENERATEKEYPAIR_ECC, SDF_R_NOT_INITIALIZED);
+		return SDR_NOTSUPPORT;
+	}
+
+	if (sdf_vendor) {
+		if (!(uiAlgID = sdf_vendor->pkey_std2vendor(uiAlgID))) {
+			SDFerr(SDF_F_SDF_GENERATEKEYPAIR_ECC,
+				SDF_R_NOT_SUPPORTED_ECC_ALGOR);
+			return SDR_ALGNOTSUPPORT;
+		}
+	}
+
+	if ((ret = sdf_method->GenerateKeyPair_ECC(
+		hSessionHandle,
+		uiAlgID,
+		uiKeyBits,
+		pucPublicKey,
+		pucPrivateKey)) != SDR_OK) {
+		SDFerr(SDF_F_SDF_GENERATEKEYPAIR_ECC, sdf_get_error_reason(ret));
+		return ret;
+	}
+
+	return SDR_OK;
+}
+
+int SDF_GenerateKeyWithIPK_ECC(
+	void *hSessionHandle,
+	unsigned int uiIPKIndex,
+	unsigned int uiKeyBits,
+	ECCCipher *pucKey,
+	void **phKeyHandle)
+{
+	int ret = SDR_UNKNOWERR;
+
+	if (!sdf_method || !sdf_method->GenerateKeyWithIPK_ECC) {
+		SDFerr(SDF_F_SDF_GENERATEKEYWITHIPK_ECC, SDF_R_NOT_INITIALIZED);
+		return SDR_NOTSUPPORT;
+	}
+
+	if ((ret = sdf_method->GenerateKeyWithIPK_ECC(
+		hSessionHandle,
+		uiIPKIndex,
+		uiKeyBits,
+		pucKey,
+		phKeyHandle)) != SDR_OK) {
+		SDFerr(SDF_F_SDF_GENERATEKEYWITHIPK_ECC,
+			sdf_get_error_reason(ret));
+		return ret;
+	}
+
+	return SDR_OK;
+}
+
+int SDF_GenerateKeyWithEPK_ECC(
+	void *hSessionHandle,
+	unsigned int uiKeyBits,
+	unsigned int uiAlgID,
+	ECCrefPublicKey *pucPublicKey,
+	ECCCipher *pucKey,
+	void **phKeyHandle)
+{
+	int ret = SDR_UNKNOWERR;
+
+	if (!sdf_method || !sdf_method->GenerateKeyWithEPK_ECC) {
+		SDFerr(SDF_F_SDF_GENERATEKEYWITHEPK_ECC, SDF_R_NOT_INITIALIZED);
+		return SDR_NOTSUPPORT;
+	}
+
+	if (sdf_vendor) {
+		if (!(uiAlgID = sdf_vendor->cipher_std2vendor(uiAlgID))) {
+			SDFerr(SDF_F_SDF_GENERATEKEYWITHEPK_ECC,
+				SDF_R_NOT_SUPPORTED_CIPHER_ALGOR);
+			return SDR_ALGNOTSUPPORT;
+		}
+	}
+
+	if ((ret = sdf_method->GenerateKeyWithEPK_ECC(
+		hSessionHandle,
+		uiKeyBits,
+		uiAlgID,
+		pucPublicKey,
+		pucKey,
+		phKeyHandle)) != SDR_OK) {
+		SDFerr(SDF_F_SDF_GENERATEKEYWITHEPK_ECC,
+			sdf_get_error_reason(ret));
+		return ret;
+	}
+
+	return SDR_OK;
+}
+
+int SDF_ImportKeyWithISK_ECC(
+	void *hSessionHandle,
+	unsigned int uiISKIndex,
+	ECCCipher *pucKey,
+	void **phKeyHandle)
+{
+	int ret = SDR_UNKNOWERR;
+
+	if (!sdf_method || !sdf_method->ImportKeyWithISK_ECC) {
+		SDFerr(SDF_F_SDF_IMPORTKEYWITHISK_ECC, SDF_R_NOT_INITIALIZED);
+		return SDR_NOTSUPPORT;
+	}
+
+	if ((ret = sdf_method->ImportKeyWithISK_ECC(
+		hSessionHandle,
+		uiISKIndex,
+		pucKey,
+		phKeyHandle)) != SDR_OK) {
+		SDFerr(SDF_F_SDF_IMPORTKEYWITHISK_ECC,
+			sdf_get_error_reason(ret));
+		return ret;
+	}
+
+	return SDR_OK;
+}
+
+int SDF_GenerateAgreementDataWithECC(
+	void *hSessionHandle,
+	unsigned int uiISKIndex,
+	unsigned int uiKeyBits,
+	unsigned char *pucSponsorID,
+	unsigned int uiSponsorIDLength,
+	ECCrefPublicKey *pucSponsorPublicKey,
+	ECCrefPublicKey *pucSponsorTmpPublicKey,
+	void **phAgreementHandle)
+{
+	int ret = SDR_UNKNOWERR;
+
+	if (!sdf_method || !sdf_method->GenerateAgreementDataWithECC) {
+		SDFerr(SDF_F_SDF_GENERATEAGREEMENTDATAWITHECC,
+			SDF_R_NOT_INITIALIZED);
+		return SDR_NOTSUPPORT;
+	}
+
+	if ((ret = sdf_method->GenerateAgreementDataWithECC(
+		hSessionHandle,
+		uiISKIndex,
+		uiKeyBits,
+		pucSponsorID,
+		uiSponsorIDLength,
+		pucSponsorPublicKey,
+		pucSponsorTmpPublicKey,
+		phAgreementHandle)) != SDR_OK) {
+		SDFerr(SDF_F_SDF_GENERATEAGREEMENTDATAWITHECC,
+			sdf_get_error_reason(ret));
+		return ret;
+	}
+
+	return SDR_OK;
+}
+
+int SDF_GenerateKeyWithECC(
+	void *hSessionHandle,
+	unsigned char *pucResponseID,
+	unsigned int uiResponseIDLength,
+	ECCrefPublicKey *pucResponsePublicKey,
+	ECCrefPublicKey *pucResponseTmpPublicKey,
+	void *hAgreementHandle,
+	void **phKeyHandle)
+{
+	int ret = SDR_UNKNOWERR;
+
+	if (!sdf_method || !sdf_method->GenerateKeyWithECC) {
+		SDFerr(SDF_F_SDF_GENERATEKEYWITHECC, SDF_R_NOT_INITIALIZED);
+		return SDR_NOTSUPPORT;
+	}
+
+	if ((ret = sdf_method->GenerateKeyWithECC(
+		hSessionHandle,
+		pucResponseID,
+		uiResponseIDLength,
+		pucResponsePublicKey,
+		pucResponseTmpPublicKey,
+		hAgreementHandle,
+		phKeyHandle)) != SDR_OK) {
+		SDFerr(SDF_F_SDF_GENERATEKEYWITHECC,
+			sdf_get_error_reason(ret));
+		return ret;
+	}
+
+	return SDR_OK;
+}
+
+int SDF_GenerateAgreementDataAndKeyWithECC(
+	void *hSessionHandle,
+	unsigned int uiISKIndex,
+	unsigned int uiKeyBits,
+	unsigned char *pucResponseID,
+	unsigned int uiResponseIDLength,
+	unsigned char *pucSponsorID,
+	unsigned int uiSponsorIDLength,
+	ECCrefPublicKey *pucSponsorPublicKey,
+	ECCrefPublicKey *pucSponsorTmpPublicKey,
+	ECCrefPublicKey *pucResponsePublicKey,
+	ECCrefPublicKey *pucResponseTmpPublicKey,
+	void **phKeyHandle)
+{
+	int ret = SDR_UNKNOWERR;
+
+	if (!sdf_method || !sdf_method->GenerateAgreementDataAndKeyWithECC) {
+		SDFerr(SDF_F_SDF_GENERATEAGREEMENTDATAANDKEYWITHECC,
+			SDF_R_NOT_INITIALIZED);
+		return SDR_NOTSUPPORT;
+	}
+
+	if ((ret = sdf_method->GenerateAgreementDataAndKeyWithECC(
+		hSessionHandle,
+		uiISKIndex,
+		uiKeyBits,
+		pucResponseID,
+		uiResponseIDLength,
+		pucSponsorID,
+		uiSponsorIDLength,
+		pucSponsorPublicKey,
+		pucSponsorTmpPublicKey,
+		pucResponsePublicKey,
+		pucResponseTmpPublicKey,
+		phKeyHandle)) != SDR_OK) {
+		SDFerr(SDF_F_SDF_GENERATEAGREEMENTDATAANDKEYWITHECC,
+			sdf_get_error_reason(ret));
+		return ret;
+	}
+
+	return SDR_OK;
+}
+
+int SDF_ExchangeDigitEnvelopeBaseOnECC(
+	void *hSessionHandle,
+	unsigned int uiKeyIndex,
+	unsigned int uiAlgID,
+	ECCrefPublicKey *pucPublicKey,
+	ECCCipher *pucEncDataIn,
+	ECCCipher *pucEncDataOut)
+{
+	int ret = SDR_UNKNOWERR;
+
+	if (!sdf_method || !sdf_method->ExchangeDigitEnvelopeBaseOnECC) {
+		SDFerr(SDF_F_SDF_EXCHANGEDIGITENVELOPEBASEONECC,
+			SDF_R_NOT_INITIALIZED);
+		return SDR_NOTSUPPORT;
+	}
+
+	if (sdf_vendor) {
+		if (!(uiAlgID = sdf_vendor->cipher_std2vendor(uiAlgID))) {
+			SDFerr(SDF_F_SDF_EXCHANGEDIGITENVELOPEBASEONECC,
+				SDF_R_NOT_SUPPORTED_CIPHER_ALGOR);
+			return SDR_ALGNOTSUPPORT;
+		}
+	}
+
+	if ((ret = sdf_method->ExchangeDigitEnvelopeBaseOnECC(
+		hSessionHandle,
+		uiKeyIndex,
+		uiAlgID,
+		pucPublicKey,
+		pucEncDataIn,
+		pucEncDataOut)) != SDR_OK) {
+		SDFerr(SDF_F_SDF_EXCHANGEDIGITENVELOPEBASEONECC,
+			sdf_get_error_reason(ret));
+		return ret;
+	}
+
+	return SDR_OK;
+}
+
+int SDF_GenerateKeyWithKEK(
+	void *hSessionHandle,
+	unsigned int uiKeyBits,
+	unsigned int uiAlgID,
+	unsigned int uiKEKIndex,
+	unsigned char *pucKey,
+	unsigned int *puiKeyLength,
+	void **phKeyHandle)
+{
+	int ret = SDR_UNKNOWERR;
+
+	if (!sdf_method || !sdf_method->GenerateKeyWithKEK) {
+		SDFerr(SDF_F_SDF_GENERATEKEYWITHKEK,
+			SDF_R_NOT_INITIALIZED);
+		return SDR_NOTSUPPORT;
+	}
+
+	if (sdf_vendor) {
+		if (!(uiAlgID = sdf_vendor->cipher_std2vendor(uiAlgID))) {
+			SDFerr(SDF_F_SDF_GENERATEKEYWITHKEK,
+				SDF_R_NOT_SUPPORTED_CIPHER_ALGOR);
+			return SDR_ALGNOTSUPPORT;
+		}
+	}
+
+	if ((ret = sdf_method->GenerateKeyWithKEK(
+		hSessionHandle,
+		uiKeyBits,
+		uiAlgID,
+		uiKEKIndex,
+		pucKey,
+		puiKeyLength,
+		phKeyHandle)) != SDR_OK) {
+		SDFerr(SDF_F_SDF_GENERATEKEYWITHKEK,
+			sdf_get_error_reason(ret));
+		return ret;
+	}
+
+	return SDR_OK;
+}
+
+int SDF_ImportKeyWithKEK(
+	void *hSessionHandle,
+	unsigned int uiAlgID,
+	unsigned int uiKEKIndex,
+	unsigned char *pucKey,
+	unsigned int uiKeyLength,
+	void **phKeyHandle)
+{
+	int ret = SDR_UNKNOWERR;
+
+	if (!sdf_method || !sdf_method->ImportKeyWithKEK) {
+		SDFerr(SDF_F_SDF_IMPORTKEYWITHKEK, SDF_R_NOT_INITIALIZED);
+		return SDR_NOTSUPPORT;
+	}
+
+	if (sdf_vendor) {
+		if (!(uiAlgID = sdf_vendor->cipher_std2vendor(uiAlgID))) {
+			SDFerr(SDF_F_SDF_IMPORTKEYWITHKEK,
+				SDF_R_NOT_SUPPORTED_CIPHER_ALGOR);
+			return SDR_ALGNOTSUPPORT;
+		}
+	}
+
+	if ((ret = sdf_method->ImportKeyWithKEK(
+		hSessionHandle,
+		uiAlgID,
+		uiKEKIndex,
+		pucKey,
+		uiKeyLength,
+		phKeyHandle)) != SDR_OK) {
+		SDFerr(SDF_F_SDF_IMPORTKEYWITHKEK, sdf_get_error_reason(ret));
+		return ret;
+	}
+
+	return SDR_OK;
+}
+
+int SDF_DestroyKey(
+	void *hSessionHandle,
+	void *hKeyHandle)
+{
+	int ret = SDR_UNKNOWERR;
+
+	if (!sdf_method || !sdf_method->DestroyKey) {
+		SDFerr(SDF_F_SDF_DESTROYKEY, SDF_R_NOT_INITIALIZED);
+		return SDR_NOTSUPPORT;
+	}
+
+	if ((ret = sdf_method->DestroyKey(
+		hSessionHandle,
+		hKeyHandle)) != SDR_OK) {
+		SDFerr(SDF_F_SDF_DESTROYKEY, sdf_get_error_reason(ret));
+		return ret;
+	}
+
+	return SDR_OK;
+}
+
+int SDF_ExternalPublicKeyOperation_RSA(
+	void *hSessionHandle,
+	RSArefPublicKey *pucPublicKey,
+	unsigned char *pucDataInput,
+	unsigned int uiInputLength,
+	unsigned char *pucDataOutput,
+	unsigned int *puiOutputLength)
+{
+	int ret = SDR_UNKNOWERR;
+
+	if (!sdf_method || !sdf_method->ExternalPublicKeyOperation_RSA) {
+		SDFerr(SDF_F_SDF_EXTERNALPUBLICKEYOPERATION_RSA,
+			SDF_R_NOT_INITIALIZED);
+		return SDR_NOTSUPPORT;
+	}
+
+	if ((ret = sdf_method->ExternalPublicKeyOperation_RSA(
+		hSessionHandle,
+		pucPublicKey,
+		pucDataInput,
+		uiInputLength,
+		pucDataOutput,
+		puiOutputLength)) != SDR_OK) {
+		SDFerr(SDF_F_SDF_EXTERNALPUBLICKEYOPERATION_RSA,
+			sdf_get_error_reason(ret));
+		return ret;
+	}
+
+	return SDR_OK;
+}
+
+int SDF_InternalPublicKeyOperation_RSA(
+	void *hSessionHandle,
+	unsigned int uiKeyIndex,
+	unsigned char *pucDataInput,
+	unsigned int uiInputLength,
+	unsigned char *pucDataOutput,
+	unsigned int *puiOutputLength)
+{
+	int ret = SDR_UNKNOWERR;
+
+	if (!sdf_method || !sdf_method->InternalPublicKeyOperation_RSA) {
+		SDFerr(SDF_F_SDF_INTERNALPUBLICKEYOPERATION_RSA,
+			SDF_R_NOT_INITIALIZED);
+		return SDR_NOTSUPPORT;
+	}
+
+	if ((ret = sdf_method->InternalPublicKeyOperation_RSA(
+		hSessionHandle,
+		uiKeyIndex,
+		pucDataInput,
+		uiInputLength,
+		pucDataOutput,
+		puiOutputLength)) != SDR_OK) {
+		SDFerr(SDF_F_SDF_INTERNALPUBLICKEYOPERATION_RSA,
+			sdf_get_error_reason(ret));
+		return ret;
+	}
+
+	return SDR_OK;
+}
+
+int SDF_InternalPrivateKeyOperation_RSA(
+	void *hSessionHandle,
+	unsigned int uiKeyIndex,
+	unsigned char *pucDataInput,
+	unsigned int uiInputLength,
+	unsigned char *pucDataOutput,
+	unsigned int *puiOutputLength)
+{
+	int ret = SDR_UNKNOWERR;
+
+	if (!sdf_method || !sdf_method->InternalPrivateKeyOperation_RSA) {
+		SDFerr(SDF_F_SDF_INTERNALPRIVATEKEYOPERATION_RSA,
+			SDF_R_NOT_INITIALIZED);
+		return SDR_NOTSUPPORT;
+	}
+
+	if ((ret = sdf_method->InternalPrivateKeyOperation_RSA(
+		hSessionHandle,
+		uiKeyIndex,
+		pucDataInput,
+		uiInputLength,
+		pucDataOutput,
+		puiOutputLength)) != SDR_OK) {
+		SDFerr(SDF_F_SDF_INTERNALPRIVATEKEYOPERATION_RSA,
+			sdf_get_error_reason(ret));
+		return ret;
+	}
+
+	return SDR_OK;
+}
+
+int SDF_ExternalVerify_ECC(
+	void *hSessionHandle,
+	unsigned int uiAlgID,
+	ECCrefPublicKey *pucPublicKey,
+	unsigned char *pucDataInput,
+	unsigned int uiInputLength,
+	ECCSignature *pucSignature)
+{
+	int ret = SDR_UNKNOWERR;
+
+	if (!sdf_method || !sdf_method->ExternalVerify_ECC) {
+		SDFerr(SDF_F_SDF_EXTERNALVERIFY_ECC, SDF_R_NOT_INITIALIZED);
+		return SDR_NOTSUPPORT;
+	}
+
+	if (sdf_vendor) {
+		if (!(uiAlgID = sdf_vendor->pkey_std2vendor(uiAlgID))) {
+			SDFerr(SDF_F_SDF_EXTERNALVERIFY_ECC,
+				SDF_R_NOT_SUPPORTED_PKEY_ALGOR);
+			return SDR_ALGNOTSUPPORT;
+		}
+	}
+
+	if ((ret = sdf_method->ExternalVerify_ECC(
+		hSessionHandle,
+		uiAlgID,
+		pucPublicKey,
+		pucDataInput,
+		uiInputLength,
+		pucSignature)) != SDR_OK) {
+		SDFerr(SDF_F_SDF_EXTERNALVERIFY_ECC,
+			sdf_get_error_reason(ret));
+		return ret;
+	}
+
+	return SDR_OK;
+}
+
+int SDF_InternalSign_ECC(
+	void *hSessionHandle,
+	unsigned int uiISKIndex,
+	unsigned char *pucData,
+	unsigned int uiDataLength,
+	ECCSignature *pucSignature)
+{
+	int ret = SDR_UNKNOWERR;
+
+	if (!sdf_method || !sdf_method->InternalSign_ECC) {
+		SDFerr(SDF_F_SDF_INTERNALSIGN_ECC, SDF_R_NOT_INITIALIZED);
+		return SDR_NOTSUPPORT;
+	}
+
+	if ((ret = sdf_method->InternalSign_ECC(
+		hSessionHandle,
+		uiISKIndex,
+		pucData,
+		uiDataLength,
+		pucSignature)) != SDR_OK) {
+		SDFerr(SDF_F_SDF_INTERNALSIGN_ECC,
+			sdf_get_error_reason(ret));
+		return ret;
+	}
+
+	return SDR_OK;
+}
+
+int SDF_InternalVerify_ECC(
+	void *hSessionHandle,
+	unsigned int uiIPKIndex,
+	unsigned char *pucData,
+	unsigned int uiDataLength,
+	ECCSignature *pucSignature)
+{
+	int ret = SDR_UNKNOWERR;
+
+	if (!sdf_method || !sdf_method->InternalVerify_ECC) {
+		SDFerr(SDF_F_SDF_INTERNALVERIFY_ECC, SDF_R_NOT_INITIALIZED);
+		return SDR_NOTSUPPORT;
+	}
+
+	if ((ret = sdf_method->InternalVerify_ECC(
+		hSessionHandle,
+		uiIPKIndex,
+		pucData,
+		uiDataLength,
+		pucSignature)) != SDR_OK) {
+		SDFerr(SDF_F_SDF_INTERNALVERIFY_ECC,
+			sdf_get_error_reason(ret));
+		return ret;
+	}
+
+	return SDR_OK;
+}
+
+int SDF_ExternalEncrypt_ECC(
+	void *hSessionHandle,
+	unsigned int uiAlgID,
+	ECCrefPublicKey *pucPublicKey,
+	unsigned char *pucData,
+	unsigned int uiDataLength,
+	ECCCipher *pucEncData)
+{
+	int ret = SDR_UNKNOWERR;
+
+	if (!sdf_method || !sdf_method->ExternalEncrypt_ECC) {
+		SDFerr(SDF_F_SDF_EXTERNALENCRYPT_ECC, SDF_R_NOT_INITIALIZED);
+		return SDR_NOTSUPPORT;
+	}
+
+	if (sdf_vendor) {
+		if (!(uiAlgID = sdf_vendor->pkey_std2vendor(uiAlgID))) {
+			SDFerr(SDF_F_SDF_EXTERNALENCRYPT_ECC,
+				SDF_R_NOT_SUPPORTED_PKEY_ALGOR);
+			return SDR_ALGNOTSUPPORT;
+		}
+	}
+
+	if ((ret = sdf_method->ExternalEncrypt_ECC(
+		hSessionHandle,
+		uiAlgID,
+		pucPublicKey,
+		pucData,
+		uiDataLength,
+		pucEncData)) != SDR_OK) {
+		SDFerr(SDF_F_SDF_EXTERNALENCRYPT_ECC,
+			sdf_get_error_reason(ret));
+		return ret;
+	}
+
+	return SDR_OK;
+}
+
+int SDF_InternalEncrypt_ECC(
+	void *hSessionHandle,
+	unsigned int uiIPKIndex,
+	unsigned int uiAlgID,
+	unsigned char *pucData,
+	unsigned int uiDataLength,
+	ECCCipher *pucEncData)
+{
+	int ret = SDR_UNKNOWERR;
+	ECCCipher *buf = pucEncData;
+
+	if (!sdf_method || !sdf_method->InternalEncrypt_ECC) {
+		SDFerr(SDF_F_SDF_INTERNALENCRYPT_ECC, SDF_R_NOT_INITIALIZED);
+		return SDR_NOTSUPPORT;
+	}
+
+	if (pucEncData->L < uiDataLength) {
+		SDFerr(SDF_F_SDF_INTERNALENCRYPT_ECC, SDF_R_BUFFER_TOO_SMALL);
+		return SDR_NOBUFFER;
+	}
+
+	if (sdf_vendor && sdf_vendor->decode_ecccipher) {
+		if (SDF_NewECCCipher(&buf, uiDataLength) != SDR_OK) {
+			SDFerr(SDF_F_SDF_INTERNALENCRYPT_ECC, ERR_R_SDF_LIB);
+			return SDR_UNKNOWERR;
+		}
+	}
+
+	if (sdf_vendor && sdf_vendor->pkey_std2vendor) {
+		if (!(uiAlgID = sdf_vendor->pkey_std2vendor(uiAlgID))) {
+			SDFerr(SDF_F_SDF_INTERNALENCRYPT_ECC,
+				SDF_R_NOT_SUPPORTED_PKEY_ALGOR);
+			ret = SDR_ALGNOTSUPPORT;
+			goto end;
+		}
+	}
+
+	if ((ret = sdf_method->InternalEncrypt_ECC(
+		hSessionHandle,
+		uiIPKIndex,
+		uiAlgID,
+		pucData,
+		uiDataLength,
+		buf)) != SDR_OK) {
+		SDFerr(SDF_F_SDF_INTERNALENCRYPT_ECC,
+			sdf_get_error_reason(ret));
+		goto end;
+	}
+
+	if (sdf_vendor && sdf_vendor->decode_ecccipher) {
+		if (!sdf_vendor->decode_ecccipher(pucEncData, buf)) {
+			SDFerr(SDF_F_SDF_INTERNALENCRYPT_ECC, ERR_R_SDF_LIB);
+			ret = SDR_UNKNOWERR;
+			goto end;
+		}
+	}
+
+	/*
+	{
+		int i;
+		unsigned char *p = (unsigned char *)pucEncData;
+		for (i = 0; i < sizeof(ECCCipher) -1 + uiDataLength; i++) {
+			printf("%02x", p[i]);
+		}
+		printf("\n");
+	}
+	*/
+
+	ret = SDR_OK;
+
+end:
+	if (sdf_vendor && sdf_vendor->decode_ecccipher && buf) {
+		SDF_FreeECCCipher(buf);
+	}
+	return ret;
+}
+
+int SDF_InternalDecrypt_ECC(
+	void *hSessionHandle,
+	unsigned int uiISKIndex,
+	unsigned int uiAlgID,
+	ECCCipher *pucEncData,
+	unsigned char *pucData,
+	unsigned int *uiDataLength)
+{
+	int ret = SDR_UNKNOWERR;
+	ECCCipher *buf = pucEncData;
+
+	if (!sdf_method || !sdf_method->InternalDecrypt_ECC) {
+		SDFerr(SDF_F_SDF_INTERNALDECRYPT_ECC, SDF_R_NOT_INITIALIZED);
+		return SDR_NOTSUPPORT;
+	}
+
+	if (sdf_vendor && sdf_vendor->pkey_std2vendor) {
+		if (!(uiAlgID = sdf_vendor->pkey_std2vendor(uiAlgID))) {
+			SDFerr(SDF_F_SDF_INTERNALDECRYPT_ECC,
+				SDF_R_NOT_SUPPORTED_PKEY_ALGOR);
+			return SDR_ALGNOTSUPPORT;
+		}
+	}
+
+	if (sdf_vendor && sdf_vendor->encode_ecccipher) {
+		if (SDF_NewECCCipher(&buf, pucEncData->L) != SDR_OK) {
+			SDFerr(SDF_F_SDF_INTERNALDECRYPT_ECC, ERR_R_SDF_LIB);
+			return SDR_UNKNOWERR;
+		}
+
+		if (!sdf_vendor->encode_ecccipher(pucEncData, buf)) {
+			SDFerr(SDF_F_SDF_INTERNALDECRYPT_ECC, ERR_R_SDF_LIB);
+			ret = SDR_UNKNOWERR;
+			goto end;
+		}
+	}
+
+	if ((ret = sdf_method->InternalDecrypt_ECC(
+		hSessionHandle,
+		uiISKIndex,
+		uiAlgID,
+		buf,
+		pucData,
+		uiDataLength)) != SDR_OK) {
+		SDFerr(SDF_F_SDF_INTERNALDECRYPT_ECC,
+			sdf_get_error_reason(ret));
+		goto end;
+	}
+
+end:
+	if (sdf_vendor && sdf_vendor->encode_ecccipher && buf) {
+		SDF_FreeECCCipher(buf);
+	}
+	return ret;
+}
+
+int SDF_Encrypt(
+	void *hSessionHandle,
+	void *hKeyHandle,
+	unsigned int uiAlgID,
+	unsigned char *pucIV,
+	unsigned char *pucData,
+	unsigned int uiDataLength,
+	unsigned char *pucEncData,
+	unsigned int *puiEncDataLength)
+{
+	int ret = SDR_UNKNOWERR;
+
+	if (!sdf_method || !sdf_method->Encrypt) {
+		SDFerr(SDF_F_SDF_ENCRYPT, SDF_R_NOT_INITIALIZED);
+		return SDR_NOTSUPPORT;
+	}
+
+	if (sdf_vendor) {
+		if (!(uiAlgID = sdf_vendor->cipher_std2vendor(uiAlgID))) {
+			SDFerr(SDF_F_SDF_ENCRYPT,
+				SDF_R_NOT_SUPPORTED_CIPHER_ALGOR);
+			return SDR_ALGNOTSUPPORT;
+		}
+	}
+
+	if ((ret = sdf_method->Encrypt(
+		hSessionHandle,
+		hKeyHandle,
+		uiAlgID,
+		pucIV,
+		pucData,
+		uiDataLength,
+		pucEncData,
+		puiEncDataLength)) != SDR_OK) {
+		SDFerr(SDF_F_SDF_ENCRYPT, sdf_get_error_reason(ret));
+		return ret;
+	}
+
+	return SDR_OK;
+}
+
+int SDF_Decrypt(
+	void *hSessionHandle,
+	void *hKeyHandle,
+	unsigned int uiAlgID,
+	unsigned char *pucIV,
+	unsigned char *pucEncData,
+	unsigned int uiEncDataLength,
+	unsigned char *pucData,
+	unsigned int *puiDataLength)
+{
+	int ret = SDR_UNKNOWERR;
+
+	if (!sdf_method || !sdf_method->Decrypt) {
+		SDFerr(SDF_F_SDF_DECRYPT, SDF_R_NOT_INITIALIZED);
+		return SDR_NOTSUPPORT;
+	}
+
+	if (sdf_vendor) {
+		if (!(uiAlgID = sdf_vendor->cipher_std2vendor(uiAlgID))) {
+			SDFerr(SDF_F_SDF_DECRYPT, SDF_R_NOT_SUPPORTED_CIPHER_ALGOR);
+			return SDR_ALGNOTSUPPORT;
+		}
+	}
+
+	if ((ret = sdf_method->Decrypt(
+		hSessionHandle,
+		hKeyHandle,
+		uiAlgID,
+		pucIV,
+		pucEncData,
+		uiEncDataLength,
+		pucData,
+		puiDataLength)) != SDR_OK) {
+		SDFerr(SDF_F_SDF_DECRYPT, sdf_get_error_reason(ret));
+		return ret;
+	}
+
+	return SDR_OK;
+}
+
+int SDF_CalculateMAC(
+	void *hSessionHandle,
+	void *hKeyHandle,
+	unsigned int uiAlgID,
+	unsigned char *pucIV,
+	unsigned char *pucData,
+	unsigned int uiDataLength,
+	unsigned char *pucMAC,
+	unsigned int *puiMACLength)
+{
+	int ret = SDR_UNKNOWERR;
+
+	if (!sdf_method || !sdf_method->CalculateMAC) {
+		SDFerr(SDF_F_SDF_CALCULATEMAC, SDF_R_NOT_INITIALIZED);
+		return SDR_NOTSUPPORT;
+	}
+
+	if (sdf_vendor) {
+		if (!(uiAlgID = sdf_vendor->cipher_std2vendor(uiAlgID))) {
+			SDFerr(SDF_F_SDF_CALCULATEMAC,
+				SDF_R_NOT_SUPPORTED_CIPHER_ALGOR);
+			return SDR_ALGNOTSUPPORT;
+		}
+	}
+
+	if ((ret = sdf_method->CalculateMAC(
+		hSessionHandle,
+		hKeyHandle,
+		uiAlgID,
+		pucIV,
+		pucData,
+		uiDataLength,
+		pucMAC,
+		puiMACLength)) != SDR_OK) {
+		SDFerr(SDF_F_SDF_CALCULATEMAC, sdf_get_error_reason(ret));
+		return ret;
+	}
+
+	return SDR_OK;
+}
+
+int SDF_HashInit(
+	void *hSessionHandle,
+	unsigned int uiAlgID,
+	ECCrefPublicKey *pucPublicKey,
+	unsigned char *pucID,
+	unsigned int uiIDLength)
+{
+	int ret = SDR_UNKNOWERR;
+
+	if (!sdf_method || !sdf_method->HashInit) {
+		SDFerr(SDF_F_SDF_HASHINIT, SDF_R_NOT_INITIALIZED);
+		return SDR_NOTSUPPORT;
+	}
+
+	if (sdf_vendor) {
+		if (!(uiAlgID = sdf_vendor->digest_std2vendor(uiAlgID))) {
+			SDFerr(SDF_F_SDF_HASHINIT, SDF_R_NOT_SUPPORTED_DIGEST_ALGOR);
+			return SDR_ALGNOTSUPPORT;
+		}
+	}
+
+	if ((ret = sdf_method->HashInit(
+		hSessionHandle,
+		uiAlgID,
+		pucPublicKey,
+		pucID,
+		uiIDLength)) != SDR_OK) {
+		SDFerr(SDF_F_SDF_HASHINIT, sdf_get_error_reason(ret));
+		return ret;
+	}
+
+	return SDR_OK;
+}
+
+int SDF_HashUpdate(
+	void *hSessionHandle,
+	unsigned char *pucData,
+	unsigned int uiDataLength)
+{
+	int ret = SDR_UNKNOWERR;
+
+	if (!sdf_method || !sdf_method->HashUpdate) {
+		SDFerr(SDF_F_SDF_HASHUPDATE, SDF_R_NOT_INITIALIZED);
+		return SDR_NOTSUPPORT;
+	}
+
+	if ((ret = sdf_method->HashUpdate(
+		hSessionHandle,
+		pucData,
+		uiDataLength)) != SDR_OK) {
+		SDFerr(SDF_F_SDF_HASHUPDATE, sdf_get_error_reason(ret));
+		return ret;
+	}
+
+	return SDR_OK;
+}
+
+int SDF_HashFinal(
+	void *hSessionHandle,
+	unsigned char *pucHash,
+	unsigned int *puiHashLength)
+{
+	int ret = SDR_UNKNOWERR;
+
+	if (!sdf_method || !sdf_method->HashFinal) {
+		SDFerr(SDF_F_SDF_HASHFINAL, SDF_R_NOT_INITIALIZED);
+		return SDR_NOTSUPPORT;
+	}
+
+	if ((ret = sdf_method->HashFinal(
+		hSessionHandle,
+		pucHash,
+		puiHashLength)) != SDR_OK) {
+		SDFerr(SDF_F_SDF_HASHFINAL, sdf_get_error_reason(ret));
+		return ret;
+	}
+
+	return SDR_OK;
+}
+
+int SDF_CreateFile(
+	void *hSessionHandle,
+	unsigned char *pucFileName,
+	unsigned int uiNameLen,
+	unsigned int uiFileSize)
+{
+	int ret = SDR_UNKNOWERR;
+
+	if (!sdf_method || !sdf_method->CreateObject) {
+		SDFerr(SDF_F_SDF_CREATEFILE, SDF_R_NOT_INITIALIZED);
+		return SDR_NOTSUPPORT;
+	}
+
+	if ((ret = sdf_method->CreateObject(
+		hSessionHandle,
+		pucFileName,
+		uiNameLen,
+		uiFileSize)) != SDR_OK) {
+		SDFerr(SDF_F_SDF_CREATEFILE, sdf_get_error_reason(ret));
+		return ret;
+	}
+
+	return SDR_OK;
+}
+
+int SDF_ReadFile(
+	void *hSessionHandle,
+	unsigned char *pucFileName,
+	unsigned int uiNameLen,
+	unsigned int uiOffset,
+	unsigned int *puiReadLength,
+	unsigned char *pucBuffer)
+{
+	int ret = SDR_UNKNOWERR;
+
+	if (!sdf_method || !sdf_method->ReadObject) {
+		SDFerr(SDF_F_SDF_READFILE, SDF_R_NOT_INITIALIZED);
+		return SDR_NOTSUPPORT;
+	}
+
+	if ((ret = sdf_method->ReadObject(
+		hSessionHandle,
+		pucFileName,
+		uiNameLen,
+		uiOffset,
+		puiReadLength,
+		pucBuffer)) != SDR_OK) {
+		SDFerr(SDF_F_SDF_READFILE, sdf_get_error_reason(ret));
+		return ret;
+	}
+
+	return SDR_OK;
+}
+
+int SDF_WriteFile(
+	void *hSessionHandle,
+	unsigned char *pucFileName,
+	unsigned int uiNameLen,
+	unsigned int uiOffset,
+	unsigned int uiWriteLength,
+	unsigned char *pucBuffer)
+{
+	int ret = SDR_UNKNOWERR;
+
+	if (!sdf_method || !sdf_method->WriteObject) {
+		SDFerr(SDF_F_SDF_WRITEFILE, SDF_R_NOT_INITIALIZED);
+		return SDR_NOTSUPPORT;
+	}
+
+	if ((ret = sdf_method->WriteObject(
+		hSessionHandle,
+		pucFileName,
+		uiNameLen,
+		uiOffset,
+		uiWriteLength,
+		pucBuffer)) != SDR_OK) {
+		SDFerr(SDF_F_SDF_WRITEFILE, sdf_get_error_reason(ret));
+		return ret;
+	}
+
+	return SDR_OK;
+}
+
+int SDF_DeleteFile(
+	void *hSessionHandle,
+	unsigned char *pucFileName,
+	unsigned int uiNameLen)
+{
+	int ret = SDR_UNKNOWERR;
+
+	if (!sdf_method || !sdf_method->DeleteObject) {
+		SDFerr(SDF_F_SDF_DELETEFILE, SDF_R_NOT_INITIALIZED);
+		return SDR_NOTSUPPORT;
+	}
+
+	if ((ret = sdf_method->DeleteObject(
+		hSessionHandle,
+		pucFileName,
+		uiNameLen)) != SDR_OK) {
+		SDFerr(SDF_F_SDF_DELETEFILE, sdf_get_error_reason(ret));
+		return ret;
+	}
+
+	return SDR_OK;
+}
diff --git a/crypto/sdf/sdf_meth.c b/crypto/sdf/sdf_meth.c
new file mode 100644
index 000000000..b01142e5d
--- /dev/null
+++ b/crypto/sdf/sdf_meth.c
@@ -0,0 +1,139 @@
+/* ====================================================================
+ * Copyright (c) 2016 The GmSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the GmSSL Project.
+ *    (http://gmssl.org/)"
+ *
+ * 4. The name "GmSSL Project" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    guanzhi1980@gmail.com.
+ *
+ * 5. Products derived from this software may not be called "GmSSL"
+ *    nor may "GmSSL" appear in their names without prior written
+ *    permission of the GmSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the GmSSL Project
+ *    (http://gmssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE GmSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE GmSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+
+#include 
+#include 
+#include 
+#include "internal/dso.h"
+#include "internal/sdf_int.h"
+
+#define SDF_METHOD_BIND_FUNCTION_EX(func,name) \
+	sdf->func = (SDF_##func##_FuncPtr)DSO_bind_func(sdf->dso, "SDF_"#name)
+
+#define SDF_METHOD_BIND_FUNCTION(func) \
+	SDF_METHOD_BIND_FUNCTION_EX(func,func)
+
+SDF_METHOD *SDF_METHOD_load_library(const char *so_path)
+{
+	SDF_METHOD *ret = NULL;
+	SDF_METHOD *sdf = NULL;
+
+	if (!(sdf = OPENSSL_zalloc(sizeof(*sdf)))) {
+		SDFerr(SDF_F_SDF_METHOD_LOAD_LIBRARY, ERR_R_MALLOC_FAILURE);
+		goto end;
+	}
+	if (!(sdf->dso = DSO_load(NULL, so_path, NULL, 0))) {
+		SDFerr(SDF_F_SDF_METHOD_LOAD_LIBRARY, SDF_R_DSO_LOAD_FAILURE);
+		goto end;
+	}
+
+	SDF_METHOD_BIND_FUNCTION(OpenDevice);
+	SDF_METHOD_BIND_FUNCTION(CloseDevice);
+	SDF_METHOD_BIND_FUNCTION(OpenSession);
+	SDF_METHOD_BIND_FUNCTION(CloseSession);
+	SDF_METHOD_BIND_FUNCTION(GetDeviceInfo);
+	SDF_METHOD_BIND_FUNCTION(GenerateRandom);
+	SDF_METHOD_BIND_FUNCTION(GetPrivateKeyAccessRight);
+	SDF_METHOD_BIND_FUNCTION(ReleasePrivateKeyAccessRight);
+	SDF_METHOD_BIND_FUNCTION(ExportSignPublicKey_RSA);
+	SDF_METHOD_BIND_FUNCTION(ExportEncPublicKey_RSA);
+	SDF_METHOD_BIND_FUNCTION(GenerateKeyPair_RSA);
+	SDF_METHOD_BIND_FUNCTION(GenerateKeyWithIPK_RSA);
+	SDF_METHOD_BIND_FUNCTION(GenerateKeyWithEPK_RSA);
+	SDF_METHOD_BIND_FUNCTION(ImportKeyWithISK_RSA);
+	SDF_METHOD_BIND_FUNCTION(ExchangeDigitEnvelopeBaseOnRSA);
+	SDF_METHOD_BIND_FUNCTION(ExportSignPublicKey_ECC);
+	SDF_METHOD_BIND_FUNCTION(ExportEncPublicKey_ECC);
+	SDF_METHOD_BIND_FUNCTION(GenerateKeyPair_ECC);
+	SDF_METHOD_BIND_FUNCTION(GenerateKeyWithIPK_ECC);
+	SDF_METHOD_BIND_FUNCTION(GenerateKeyWithEPK_ECC);
+	SDF_METHOD_BIND_FUNCTION(ImportKeyWithISK_ECC);
+	SDF_METHOD_BIND_FUNCTION(GenerateAgreementDataWithECC);
+	SDF_METHOD_BIND_FUNCTION(GenerateKeyWithECC);
+	SDF_METHOD_BIND_FUNCTION(GenerateAgreementDataAndKeyWithECC);
+	SDF_METHOD_BIND_FUNCTION(ExchangeDigitEnvelopeBaseOnECC);
+	SDF_METHOD_BIND_FUNCTION(GenerateKeyWithKEK);
+	SDF_METHOD_BIND_FUNCTION(ImportKeyWithKEK);
+	SDF_METHOD_BIND_FUNCTION(DestroyKey);
+	SDF_METHOD_BIND_FUNCTION(ExternalPublicKeyOperation_RSA);
+	//SDF_METHOD_BIND_FUNCTION(InternalPublicKeyOperation_RSA);
+	SDF_METHOD_BIND_FUNCTION(InternalPrivateKeyOperation_RSA);
+	SDF_METHOD_BIND_FUNCTION(ExternalVerify_ECC);
+	SDF_METHOD_BIND_FUNCTION(InternalSign_ECC);
+	SDF_METHOD_BIND_FUNCTION(InternalVerify_ECC);
+	SDF_METHOD_BIND_FUNCTION(ExternalEncrypt_ECC);
+	//SDF_METHOD_BIND_FUNCTION(ExternalDecrypt_ECC);
+	SDF_METHOD_BIND_FUNCTION(InternalEncrypt_ECC);
+	SDF_METHOD_BIND_FUNCTION(InternalDecrypt_ECC);
+	SDF_METHOD_BIND_FUNCTION(Encrypt);
+	SDF_METHOD_BIND_FUNCTION(Decrypt);
+	SDF_METHOD_BIND_FUNCTION(CalculateMAC);
+	SDF_METHOD_BIND_FUNCTION(HashInit);
+	SDF_METHOD_BIND_FUNCTION(HashUpdate);
+	SDF_METHOD_BIND_FUNCTION(HashFinal);
+	SDF_METHOD_BIND_FUNCTION_EX(CreateObject,CreateFile);
+	SDF_METHOD_BIND_FUNCTION_EX(ReadObject,ReadFile);
+	SDF_METHOD_BIND_FUNCTION_EX(WriteObject,WriteFile);
+	SDF_METHOD_BIND_FUNCTION_EX(DeleteObject,DeleteFile);
+
+	ret = sdf;
+	sdf = NULL;
+
+end:
+	SDF_METHOD_free(sdf);
+	return ret;
+}
+
+void SDF_METHOD_free(SDF_METHOD *meth)
+{
+	if (meth) DSO_free(meth->dso);
+	OPENSSL_free(meth);
+}
+
+
diff --git a/crypto/sdf/sdf_sansec.c b/crypto/sdf/sdf_sansec.c
new file mode 100644
index 000000000..d52d5a3c2
--- /dev/null
+++ b/crypto/sdf/sdf_sansec.c
@@ -0,0 +1,367 @@
+/* ====================================================================
+ * Copyright (c) 2016 - 2017 The GmSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the GmSSL Project.
+ *    (http://gmssl.org/)"
+ *
+ * 4. The name "GmSSL Project" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    guanzhi1980@gmail.com.
+ *
+ * 5. Products derived from this software may not be called "GmSSL"
+ *    nor may "GmSSL" appear in their names without prior written
+ *    permission of the GmSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the GmSSL Project
+ *    (http://gmssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE GmSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE GmSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+
+#include 
+#include 
+#include 
+#include "internal/sdf_int.h"
+#include "../../e_os.h"
+#include "sdf_sansec.h"
+
+typedef struct {
+	unsigned int std_id;
+	unsigned int vendor_id;
+} SDF_ALGOR_PAIR;
+
+static SDF_ALGOR_PAIR sansec_ciphers[] = {
+	{ SGD_SM1, SANSEC_SM1 },
+	{ SGD_SM1_ECB, SANSEC_SM1_ECB },
+	{ SGD_SM1_CBC, SANSEC_SM1_CBC },
+	{ SGD_SM1_CFB, SANSEC_SM1_CFB },
+	{ SGD_SM1_OFB, SANSEC_SM1_OFB },
+	{ SGD_SM1_MAC, SANSEC_SM1_MAC },
+	{ SGD_SM4, SANSEC_SM4 },
+	{ SGD_SM4_ECB, SANSEC_SM4_ECB },
+	{ SGD_SM4_CBC, SANSEC_SM4_CBC },
+	{ SGD_SM4_CFB, SANSEC_SM4_CFB },
+	{ SGD_SM4_OFB, SANSEC_SM4_OFB },
+	{ SGD_SM4_MAC, SANSEC_SM4_MAC },
+	{ SGD_SSF33, SANSEC_SSF33 },
+	{ SGD_SSF33_ECB, SANSEC_SSF33_ECB },
+	{ SGD_SSF33_CBC, SANSEC_SSF33_CBC },
+	{ SGD_SSF33_CFB, SANSEC_SSF33_CFB },
+	{ SGD_SSF33_OFB, SANSEC_SSF33_OFB },
+	{ SGD_SSF33_MAC, SANSEC_SSF33_MAC },
+	{ 0, SANSEC_AES },
+	{ 0, SANSEC_AES_ECB },
+	{ 0, SANSEC_AES_CBC },
+	{ 0, SANSEC_AES_CFB },
+	{ 0, SANSEC_AES_OFB },
+	{ 0, SANSEC_AES_MAC },
+	{ 0, SANSEC_DES },
+	{ 0, SANSEC_DES_ECB },
+	{ 0, SANSEC_DES_CBC },
+	{ 0, SANSEC_DES_CFB },
+	{ 0, SANSEC_DES_OFB },
+	{ 0, SANSEC_DES_MAC },
+	{ 0, SANSEC_3DES },
+	{ 0, SANSEC_3DES_ECB },
+	{ 0, SANSEC_3DES_CBC },
+	{ 0, SANSEC_3DES_CFB },
+	{ 0, SANSEC_3DES_OFB },
+	{ 0, SANSEC_3DES_MAC },
+};
+
+static unsigned int sansec_cipher_vendor2std(unsigned int vendor_id)
+{
+	size_t i;
+	for (i = 0; i < OSSL_NELEM(sansec_ciphers); i++) {
+		if (vendor_id == sansec_ciphers[i].vendor_id) {
+			return sansec_ciphers[i].std_id;
+		}
+	}
+	return 0;
+}
+
+static unsigned int sansec_cipher_std2vendor(unsigned int std_id)
+{
+	size_t i;
+	for (i = 0; i < OSSL_NELEM(sansec_ciphers); i++) {
+		if (std_id == sansec_ciphers[i].std_id) {
+			return sansec_ciphers[i].vendor_id;
+		}
+	}
+	return 0;
+}
+
+static unsigned int sansec_cipher_cap(unsigned int vendor_cap)
+{
+	unsigned int std_cap = 0;
+	size_t i;
+
+	for (i = 0; i < OSSL_NELEM(sansec_ciphers); i++) {
+		if (vendor_cap & sansec_ciphers[i].vendor_id) {
+			std_cap |= sansec_ciphers[i].std_id;
+		}
+	}
+
+	return std_cap;
+}
+
+static SDF_ALGOR_PAIR sansec_digests[] = {
+	{ SGD_SM3, SANSEC_SM3 },
+	{ SGD_SHA1, SANSEC_SHA1 },
+	{ SGD_SHA256, SANSEC_SHA256 },
+	{ 0, SANSEC_SHA512 },
+	{ 0, SANSEC_SHA384 },
+	{ 0, SANSEC_SHA224 },
+	{ 0, SANSEC_MD5 },
+};
+
+static unsigned int sansec_digest_vendor2std(unsigned int vendor_id)
+{
+	size_t i;
+	for (i = 0; i < OSSL_NELEM(sansec_digests); i++) {
+		if (vendor_id == sansec_digests[i].vendor_id) {
+			return sansec_digests[i].std_id;
+		}
+	}
+	return 0;
+}
+
+static unsigned int sansec_digest_std2vendor(unsigned int std_id)
+{
+	size_t i;
+	for (i = 0; i < OSSL_NELEM(sansec_digests); i++) {
+		if (std_id == sansec_digests[i].std_id) {
+			return sansec_digests[i].vendor_id;
+		}
+	}
+	return 0;
+}
+
+static unsigned int sansec_digest_cap(unsigned int vendor_cap)
+{
+	unsigned int std_cap = 0;
+	size_t i;
+
+	for (i = 0; i < OSSL_NELEM(sansec_digests); i++) {
+		if (vendor_cap & sansec_digests[i].vendor_id) {
+			std_cap |= sansec_digests[i].std_id;
+		}
+	}
+
+	return std_cap;
+}
+
+static SDF_ALGOR_PAIR sansec_pkeys[] = {
+	{ SGD_RSA,SANSEC_RSA },
+	{ SGD_RSA_SIGN,SANSEC_RSA_SIGN },
+	{ SGD_RSA_ENC,SANSEC_RSA_ENC },
+	{ SGD_SM2,SANSEC_SM2 },
+	{ SGD_SM2_1,SANSEC_SM2_1 },
+	{ SGD_SM2_2,SANSEC_SM2_2 },
+	{ SGD_SM2_3,SANSEC_SM2_3 },
+};
+
+static unsigned int sansec_pkey_vendor2std(unsigned int vendor_id)
+{
+	size_t i;
+	for (i = 0; i < OSSL_NELEM(sansec_pkeys); i++) {
+		if (vendor_id == sansec_pkeys[i].vendor_id) {
+			return sansec_pkeys[i].std_id;
+		}
+	}
+	return 0;
+}
+
+static unsigned int sansec_pkey_std2vendor(unsigned int std_id)
+{
+	size_t i;
+	for (i = 0; i < OSSL_NELEM(sansec_pkeys); i++) {
+		if (std_id == sansec_pkeys[i].std_id) {
+			return sansec_pkeys[i].vendor_id;
+		}
+	}
+	return 0;
+}
+
+static unsigned int sansec_pkey_cap(unsigned int vendor_cap)
+{
+	unsigned int std_cap = 0;
+	size_t i;
+
+	for (i = 0; i < OSSL_NELEM(sansec_pkeys); i++) {
+		if (vendor_cap & sansec_pkeys[i].vendor_id) {
+			std_cap |= sansec_pkeys[i].std_id;
+		}
+	}
+
+	return std_cap;
+}
+
+static int sansec_encode_ecccipher(const ECCCipher *ec, void *vendor)
+{
+	int ret;
+	SANSEC_ECCCipher *sansec = vendor;
+	ret = sizeof(SANSEC_ECCCipher);
+
+	if (ec->L > sizeof(sansec->C)) {
+		SDFerr(SDF_F_SANSEC_ENCODE_ECCCIPHER,
+			SDF_R_INVALID_SANSEC_ECCCIPHER_LENGTH);
+		return 0;
+	}
+
+	if (vendor) {
+		sansec->clength = ec->L;
+		memcpy(sansec->x, ec->x, sizeof(ec->x));
+		memcpy(sansec->y, ec->y, sizeof(ec->y));
+		memcpy(sansec->M, ec->M, sizeof(ec->M));
+		memset(sansec->M + sizeof(ec->M), 0, sizeof(sansec->M) - sizeof(ec->M));
+		memcpy(sansec->C, ec->C, ec->L);
+		memset(sansec->C + ec->L, 0, sizeof(sansec->C) - ec->L);
+	}
+
+	return ret;
+}
+
+static int sansec_decode_ecccipher(ECCCipher *ec, const void *vendor)
+{
+	int ret;
+	const SANSEC_ECCCipher *sansec = vendor;
+	ret = sizeof(ECCCipher) -1 + sansec->clength;
+
+	if (sansec->clength > sizeof(sansec->C)) {
+		SDFerr(SDF_F_SANSEC_DECODE_ECCCIPHER,
+			SDF_R_INVALID_SANSEC_ECCCIPHER_LENGTH);
+		return 0;
+	}
+
+	if (ec) {
+		memcpy(ec->x, sansec->x, sizeof(ec->x));
+		memcpy(ec->y, sansec->y, sizeof(ec->y));
+		memcpy(ec->M, sansec->M, sizeof(ec->M));
+		ec->L = sansec->clength;
+		memcpy(ec->C, sansec->C, sansec->clength);
+	}
+
+	return ret;
+}
+
+static SDF_ERR_REASON sansec_errors[] = {
+	{ SANSEC_BASE, SDF_R_SANSEC_BASE },
+	{ SANSEC_INVALID_USER, SDF_R_SANSEC_INVALID_USER },
+	{ SANSEC_INVALID_AUTHENCODE, SDF_R_SANSEC_INVALID_AUTHENCODE },
+	{ SANSEC_PROTOCOL_VERSION_ERROR, SDF_R_SANSEC_PROTOCOL_VERSION_ERROR },
+	{ SANSEC_INVALID_COMMAND, SDF_R_SANSEC_INVALID_COMMAND },
+	{ SANSEC_INVALID_PARAMETERS, SDF_R_SANSEC_INVALID_PARAMETERS },
+	{ SANSEC_FILE_ALREADY_EXIST, SDF_R_SANSEC_FILE_ALREADY_EXIST },
+	{ SANSEC_SYNC_ERROR, SDF_R_SANSEC_SYNC_ERROR },
+	{ SANSEC_SYNC_LOGIN_ERROR, SDF_R_SANSEC_SYNC_LOGIN_ERROR },
+	{ SANSEC_SOCKET_TIMEOUT, SDF_R_SANSEC_SOCKET_TIMEOUT },
+	{ SANSEC_CONNECT_ERROR, SDF_R_SANSEC_CONNECT_ERROR },
+	{ SANSEC_SET_SOCKET_OPTION_ERROR, SDF_R_SANSEC_SET_SOCKET_OPTION_ERROR },
+	{ SANSEC_SOCKET_SEND_ERROR, SDF_R_SANSEC_SOCKET_SEND_ERROR },
+	{ SANSEC_SOCKET_RECV_ERROR, SDF_R_SANSEC_SOCKET_RECV_ERROR },
+	{ SANSEC_SOCKET_RECV_0, SDF_R_SANSEC_SOCKET_RECV_0 },
+	{ SANSEC_SEM_TIMEOUT, SDF_R_SANSEC_SEM_TIMEOUT },
+	{ SANSEC_NO_AVAILABLE_HSM, SDF_R_SANSEC_NO_AVAILABLE_HSM },
+	{ SANSEC_NO_AVAILABLE_CSM, SDF_R_SANSEC_NO_AVAILABLE_CSM },
+	{ SANSEC_CONFIG_ERROR, SDF_R_SANSEC_CONFIG_ERROR },
+	{ SANSEC_CARD_BASE, SDF_R_SANSEC_CARD_BASE },
+	{ SANSEC_CARD_UNKNOW_ERROR, SDF_R_SANSEC_CARD_UNKNOW_ERROR },
+	{ SANSEC_CARD_NOT_SUPPORTED, SDF_R_SANSEC_CARD_NOT_SUPPORTED },
+	{ SANSEC_CARD_COMMMUCATION_FAILED, SDF_R_SANSEC_CARD_COMMMUCATION_FAILED },
+	{ SANSEC_CARD_HARDWARE_FAILURE, SDF_R_SANSEC_CARD_HARDWARE_FAILURE },
+	{ SANSEC_CARD_OPEN_DEVICE_FAILED, SDF_R_SANSEC_CARD_OPEN_DEVICE_FAILED },
+	{ SANSEC_CARD_OPEN_SESSION_FAILED, SDF_R_SANSEC_CARD_OPEN_SESSION_FAILED },
+	{ SANSEC_CARD_PRIVATE_KEY_ACCESS_DENYED, SDF_R_SANSEC_CARD_PRIVATE_KEY_ACCESS_DENYED },
+	{ SANSEC_CARD_KEY_NOT_EXIST, SDF_R_SANSEC_CARD_KEY_NOT_EXIST },
+	{ SANSEC_CARD_ALGOR_NOT_SUPPORTED, SDF_R_SANSEC_CARD_ALGOR_NOT_SUPPORTED },
+	{ SANSEC_CARD_ALG_MODE_NOT_SUPPORTED, SDF_R_SANSEC_CARD_ALG_MODE_NOT_SUPPORTED },
+	{ SANSEC_CARD_PUBLIC_KEY_OPERATION_ERROR, SDF_R_SANSEC_CARD_PUBLIC_KEY_OPERATION_ERROR },
+	{ SANSEC_CARD_PRIVATE_KEY_OPERATION_ERROR, SDF_R_SANSEC_CARD_PRIVATE_KEY_OPERATION_ERROR },
+	{ SANSEC_CARD_SIGN_ERROR, SDF_R_SANSEC_CARD_SIGN_ERROR },
+	{ SANSEC_CARD_VERIFY_ERROR, SDF_R_SANSEC_CARD_VERIFY_ERROR },
+	{ SANSEC_CARD_SYMMETRIC_ALGOR_ERROR, SDF_R_SANSEC_CARD_SYMMETRIC_ALGOR_ERROR },
+	{ SANSEC_CARD_STEP_ERROR, SDF_R_SANSEC_CARD_STEP_ERROR },
+	{ SANSEC_CARD_FILE_SIZE_ERROR, SDF_R_SANSEC_CARD_FILE_SIZE_ERROR },
+	{ SANSEC_CARD_FILE_NOT_EXIST, SDF_R_SANSEC_CARD_FILE_NOT_EXIST },
+	{ SANSEC_CARD_FILE_OFFSET_ERROR, SDF_R_SANSEC_CARD_FILE_OFFSET_ERROR },
+	{ SANSEC_CARD_KEY_TYPE_ERROR, SDF_R_SANSEC_CARD_KEY_TYPE_ERROR },
+	{ SANSEC_CARD_KEY_ERROR, SDF_R_SANSEC_CARD_KEY_ERROR },
+	{ SANSEC_CARD_BUFFER_TOO_SMALL, SDF_R_SANSEC_CARD_BUFFER_TOO_SMALL },
+	{ SANSEC_CARD_DATA_PADDING_ERROR, SDF_R_SANSEC_CARD_DATA_PADDING_ERROR },
+	{ SANSEC_CARD_DATA_SIZE, SDF_R_SANSEC_CARD_DATA_SIZE },
+	{ SANSEC_CARD_CRYPTO_NOT_INITED, SDF_R_SANSEC_CARD_CRYPTO_NOT_INITED },
+	{ SANSEC_CARD_MANAGEMENT_DENYED, SDF_R_SANSEC_CARD_MANAGEMENT_DENYED },
+	{ SANSEC_CARD_OPERATION_DENYED, SDF_R_SANSEC_CARD_OPERATION_DENYED },
+	{ SANSEC_CARD_DEVICE_STATUS_ERROR, SDF_R_SANSEC_CARD_DEVICE_STATUS_ERROR },
+	{ SANSEC_CARD_LOGIN_ERROR, SDF_R_SANSEC_CARD_LOGIN_ERROR },
+	{ SANSEC_CARD_USERID_ERROR, SDF_R_SANSEC_CARD_USERID_ERROR },
+	{ SANSEC_CARD_PARAMENT_ERROR, SDF_R_SANSEC_CARD_PARAMENT_ERROR },
+	{ SANSEC_CARD_MANAGEMENT_DENYED_05, SDF_R_SANSEC_CARD_MANAGEMENT_DENYED_05 },
+	{ SANSEC_CARD_OPERATION_DENYED_05, SDF_R_SANSEC_CARD_OPERATION_DENYED_05 },
+	{ SANSEC_CARD_DEVICE_STATUS_ERROR_05, SDF_R_SANSEC_CARD_DEVICE_STATUS_ERROR_05 },
+	{ SANSEC_CARD_LOGIN_ERROR_05, SDF_R_SANSEC_CARD_LOGIN_ERROR_05 },
+	{ SANSEC_CARD_USERID_ERROR_05, SDF_R_SANSEC_CARD_USERID_ERROR_05 },
+	{ SANSEC_CARD_PARAMENT_ERROR_05, SDF_R_SANSEC_CARD_PARAMENT_ERROR_05 },
+	{ SANSEC_CARD_READER_BASE, SDF_R_SANSEC_CARD_READER_BASE },
+	{ SANSEC_CARD_READER_PIN_ERROR, SDF_R_SANSEC_CARD_READER_PIN_ERROR },
+	{ SANSEC_CARD_READER_NO_CARD, SDF_R_SANSEC_CARD_READER_NO_CARD },
+	{ SANSEC_CARD_READER_CARD_INSERT, SDF_R_SANSEC_CARD_READER_CARD_INSERT },
+	{ SANSEC_CARD_READER_CARD_INSERT_TYPE, SDF_R_SANSEC_CARD_READER_CARD_INSERT_TYPE },
+};
+
+static unsigned long sansec_get_error_reason(int err)
+{
+	size_t i = 0;
+	for (i = 0; i < OSSL_NELEM(sansec_errors); i++) {
+		if (err == sansec_errors[i].err) {
+			return sansec_errors[i].reason;
+		}
+	}
+	return 0;
+}
+
+SDF_VENDOR sdf_sansec = {
+	"sansec",
+	sansec_cipher_vendor2std,
+	sansec_cipher_std2vendor,
+	sansec_cipher_cap,
+	sansec_digest_vendor2std,
+	sansec_digest_std2vendor,
+	sansec_digest_cap,
+	sansec_pkey_vendor2std,
+	sansec_pkey_std2vendor,
+	sansec_pkey_cap,
+	sansec_encode_ecccipher,
+	sansec_decode_ecccipher,
+	sansec_get_error_reason,
+};
diff --git a/crypto/sdf/sdf_sansec.h b/crypto/sdf/sdf_sansec.h
new file mode 100644
index 000000000..0d86db8bc
--- /dev/null
+++ b/crypto/sdf/sdf_sansec.h
@@ -0,0 +1,192 @@
+/* ====================================================================
+ * Copyright (c) 2016 - 2017 The GmSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the GmSSL Project.
+ *    (http://gmssl.org/)"
+ *
+ * 4. The name "GmSSL Project" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    guanzhi1980@gmail.com.
+ *
+ * 5. Products derived from this software may not be called "GmSSL"
+ *    nor may "GmSSL" appear in their names without prior written
+ *    permission of the GmSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the GmSSL Project
+ *    (http://gmssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE GmSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE GmSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+#ifndef HEADER_SDF_SANSEC_H
+#define HEADER_SDF_SANSEC_H
+
+#include 
+
+#define SANSEC_SM1		(SGD_SM1)
+#define SANSEC_SM1_ECB		(SANSEC_SM1|SGD_ECB)
+#define SANSEC_SM1_CBC		(SANSEC_SM1|SGD_CBC)
+#define SANSEC_SM1_CFB		(SANSEC_SM1|SGD_CFB)
+#define SANSEC_SM1_OFB		(SANSEC_SM1|SGD_OFB)
+#define SANSEC_SM1_MAC		(SANSEC_SM1|SGD_MAC)
+
+#define SANSEC_SM4		0x00002000
+#define SANSEC_SM4_ECB		(SANSEC_SM4|SGD_ECB)
+#define SANSEC_SM4_CBC		(SANSEC_SM4|SGD_CBC)
+#define SANSEC_SM4_CFB		(SANSEC_SM4|SGD_CFB)
+#define SANSEC_SM4_OFB		(SANSEC_SM4|SGD_OFB)
+#define SANSEC_SM4_MAC		(SANSEC_SM4|SGD_MAC)
+
+#define SANSEC_SSF33		(SGD_SSF33)
+#define SANSEC_SSF33_ECB	(SANSEC_SSF33|SGD_ECB)
+#define SANSEC_SSF33_CBC	(SANSEC_SSF33|SGD_CBC)
+#define SANSEC_SSF33_CFB	(SANSEC_SSF33|SGD_CFB)
+#define SANSEC_SSF33_OFB	(SANSEC_SSF33|SGD_OFB)
+#define SANSEC_SSF33_MAC	(SANSEC_SSF33|SGD_MAC)
+
+#define SANSEC_AES		0x00000400
+#define SANSEC_AES_ECB		(SANSEC_AES|SGD_ECB)
+#define SANSEC_AES_CBC		(SANSEC_AES|SGD_CBC)
+#define SANSEC_AES_CFB		(SANSEC_AES|SGD_CFB)
+#define SANSEC_AES_OFB		(SANSEC_AES|SGD_OFB)
+#define SANSEC_AES_MAC		(SANSEC_AES|SGD_MAC)
+
+#define SANSEC_DES		0x00004000
+#define SANSEC_DES_ECB		(SANSEC_DES|SGD_ECB)
+#define SANSEC_DES_CBC		(SANSEC_DES|SGD_CBC)
+#define SANSEC_DES_CFB		(SANSEC_DES|SGD_CFB)
+#define SANSEC_DES_OFB		(SANSEC_DES|SGD_OFB)
+#define SANSEC_DES_MAC		(SANSEC_DES|SGD_MAC)
+
+#define SANSEC_3DES		0x00000800
+#define SANSEC_3DES_ECB		(SANSEC_3DES|SGD_ECB)
+#define SANSEC_3DES_CBC		(SANSEC_3DES|SGD_CBC)
+#define SANSEC_3DES_CFB		(SANSEC_3DES|SGD_CFB)
+#define SANSEC_3DES_OFB		(SANSEC_3DES|SGD_OFB)
+#define SANSEC_3DES_MAC		(SANSEC_3DES|SGD_MAC)
+
+#define SANSEC_SM3		(SGD_SM3)
+#define SANSEC_SHA1		(SGD_SHA1)
+#define SANSEC_SHA256		(SGD_SHA256)
+#define SANSEC_SHA512		0x00000008
+#define SANSEC_SHA384		0x00000010
+#define SANSEC_SHA224		0x00000020
+#define SANSEC_MD5		0x00000080
+
+#define SANSEC_RSA		(SGD_RSA)
+#define SANSEC_RSA_SIGN		(SGD_RSA_SIGN)
+#define SANSEC_RSA_ENC		0x00010200
+#define SANSEC_SM2		(SGD_SM2)
+#define SANSEC_SM2_1		(SGD_SM2_1)
+#define SANSEC_SM2_2		(SGD_SM2_2)
+#define SANSEC_SM2_3		(SGD_SM2_3)
+
+#define SANSEC_BASE				(SDR_BASE + 0x00010000)
+#define SANSEC_INVALID_USER			(SANSEC_BASE + 0x00000001)
+#define SANSEC_INVALID_AUTHENCODE		(SANSEC_BASE + 0x00000002)
+#define SANSEC_PROTOCOL_VERSION_ERROR		(SANSEC_BASE + 0x00000003)
+#define SANSEC_INVALID_COMMAND			(SANSEC_BASE + 0x00000004)
+#define SANSEC_INVALID_PARAMETERS		(SANSEC_BASE + 0x00000005)
+#define SANSEC_FILE_ALREADY_EXIST		(SANSEC_BASE + 0x00000006)
+#define SANSEC_SYNC_ERROR			(SANSEC_BASE + 0x00000007)
+#define SANSEC_SYNC_LOGIN_ERROR			(SANSEC_BASE + 0x00000008)
+#define SANSEC_SOCKET_TIMEOUT			(SANSEC_BASE + 0x00000100)
+#define SANSEC_CONNECT_ERROR			(SANSEC_BASE + 0x00000101)
+#define SANSEC_SET_SOCKET_OPTION_ERROR		(SANSEC_BASE + 0x00000102)
+#define SANSEC_SOCKET_SEND_ERROR		(SANSEC_BASE + 0x00000104)
+#define SANSEC_SOCKET_RECV_ERROR		(SANSEC_BASE + 0x00000105)
+#define SANSEC_SOCKET_RECV_0			(SANSEC_BASE + 0x00000106)
+#define SANSEC_SEM_TIMEOUT			(SANSEC_BASE + 0x00000200)
+#define SANSEC_NO_AVAILABLE_HSM			(SANSEC_BASE + 0x00000201)
+#define SANSEC_NO_AVAILABLE_CSM			(SANSEC_BASE + 0x00000202)
+#define SANSEC_CONFIG_ERROR			(SANSEC_BASE + 0x00000301)
+#define SANSEC_CARD_BASE			(SDR_BASE + 0x00020000)
+#define SANSEC_CARD_UNKNOW_ERROR		(SANSEC_CARD_BASE + 0x00000001)
+#define SANSEC_CARD_NOT_SUPPORTED		(SANSEC_CARD_BASE + 0x00000002)
+#define SANSEC_CARD_COMMMUCATION_FAILED		(SANSEC_CARD_BASE + 0x00000003)
+#define SANSEC_CARD_HARDWARE_FAILURE		(SANSEC_CARD_BASE + 0x00000004)
+#define SANSEC_CARD_OPEN_DEVICE_FAILED		(SANSEC_CARD_BASE + 0x00000005)
+#define SANSEC_CARD_OPEN_SESSION_FAILED		(SANSEC_CARD_BASE + 0x00000006)
+#define SANSEC_CARD_PRIVATE_KEY_ACCESS_DENYED	(SANSEC_CARD_BASE + 0x00000007)
+#define SANSEC_CARD_KEY_NOT_EXIST		(SANSEC_CARD_BASE + 0x00000008)
+#define SANSEC_CARD_ALGOR_NOT_SUPPORTED		(SANSEC_CARD_BASE + 0x00000009)
+#define SANSEC_CARD_ALG_MODE_NOT_SUPPORTED	(SANSEC_CARD_BASE + 0x00000010)
+#define SANSEC_CARD_PUBLIC_KEY_OPERATION_ERROR	(SANSEC_CARD_BASE + 0x00000011)
+#define SANSEC_CARD_PRIVATE_KEY_OPERATION_ERROR	(SANSEC_CARD_BASE + 0x00000012)
+#define SANSEC_CARD_SIGN_ERROR			(SANSEC_CARD_BASE + 0x00000013)
+#define SANSEC_CARD_VERIFY_ERROR		(SANSEC_CARD_BASE + 0x00000014)
+#define SANSEC_CARD_SYMMETRIC_ALGOR_ERROR	(SANSEC_CARD_BASE + 0x00000015)
+#define SANSEC_CARD_STEP_ERROR			(SANSEC_CARD_BASE + 0x00000016)
+#define SANSEC_CARD_FILE_SIZE_ERROR		(SANSEC_CARD_BASE + 0x00000017)
+#define SANSEC_CARD_FILE_NOT_EXIST		(SANSEC_CARD_BASE + 0x00000018)
+#define SANSEC_CARD_FILE_OFFSET_ERROR		(SANSEC_CARD_BASE + 0x00000019)
+#define SANSEC_CARD_KEY_TYPE_ERROR		(SANSEC_CARD_BASE + 0x00000020)
+#define SANSEC_CARD_KEY_ERROR			(SANSEC_CARD_BASE + 0x00000021)
+#define SANSEC_CARD_BUFFER_TOO_SMALL		(SANSEC_CARD_BASE + 0x00000101)
+#define SANSEC_CARD_DATA_PADDING_ERROR		(SANSEC_CARD_BASE + 0x00000102)
+#define SANSEC_CARD_DATA_SIZE			(SANSEC_CARD_BASE + 0x00000103)
+#define SANSEC_CARD_CRYPTO_NOT_INITED		(SANSEC_CARD_BASE + 0x00000104)
+#define SANSEC_CARD_MANAGEMENT_DENYED		(SANSEC_CARD_BASE + 0x00001001)
+#define SANSEC_CARD_OPERATION_DENYED		(SANSEC_CARD_BASE + 0x00001002)
+#define SANSEC_CARD_DEVICE_STATUS_ERROR		(SANSEC_CARD_BASE + 0x00001003)
+#define SANSEC_CARD_LOGIN_ERROR			(SANSEC_CARD_BASE + 0x00001011)
+#define SANSEC_CARD_USERID_ERROR		(SANSEC_CARD_BASE + 0x00001012)
+#define SANSEC_CARD_PARAMENT_ERROR		(SANSEC_CARD_BASE + 0x00001013)
+#define SANSEC_CARD_MANAGEMENT_DENYED_05	(SANSEC_CARD_BASE + 0x00000801)
+#define SANSEC_CARD_OPERATION_DENYED_05		(SANSEC_CARD_BASE + 0x00000802)
+#define SANSEC_CARD_DEVICE_STATUS_ERROR_05	(SANSEC_CARD_BASE + 0x00000803)
+#define SANSEC_CARD_LOGIN_ERROR_05		(SANSEC_CARD_BASE + 0x00000811)
+#define SANSEC_CARD_USERID_ERROR_05		(SANSEC_CARD_BASE + 0x00000812)
+#define SANSEC_CARD_PARAMENT_ERROR_05		(SANSEC_CARD_BASE + 0x00000813)
+#define SANSEC_CARD_READER_BASE			(SDR_BASE + 0x00030000)
+#define SANSEC_CARD_READER_PIN_ERROR		(SANSEC_CARD_READER_BASE + 0x000063CE)
+#define SANSEC_CARD_READER_NO_CARD		(SANSEC_CARD_READER_BASE + 0x0000FF01)
+#define SANSEC_CARD_READER_CARD_INSERT		(SANSEC_CARD_READER_BASE + 0x0000FF02)
+#define SANSEC_CARD_READER_CARD_INSERT_TYPE 	(SANSEC_CARD_READER_BASE + 0x0000FF03)
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#pragma pack(1)
+typedef struct {
+	unsigned int  clength;
+	unsigned char x[ECCref_MAX_LEN];
+	unsigned char y[ECCref_MAX_LEN];
+	unsigned char C[136];
+	unsigned char M[ECCref_MAX_LEN];
+} SANSEC_ECCCipher;
+#pragma pack()
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/crypto/seed/build.info b/crypto/seed/build.info
new file mode 100644
index 000000000..abdcbcaa9
--- /dev/null
+++ b/crypto/seed/build.info
@@ -0,0 +1,2 @@
+LIBS=../../libcrypto
+SOURCE[../../libcrypto]=seed.c seed_ecb.c seed_cbc.c seed_cfb.c seed_ofb.c
diff --git a/crypto/seed/seed.c b/crypto/seed/seed.c
new file mode 100644
index 000000000..c1e928516
--- /dev/null
+++ b/crypto/seed/seed.c
@@ -0,0 +1,590 @@
+/*
+ * Copyright 2007-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * Copyright (c) 2007 KISA(Korea Information Security Agency). All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Neither the name of author nor the names of its contributors may
+ *    be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+#ifndef OPENSSL_NO_SEED
+
+# include 
+# include 
+# include 
+# ifdef WIN32
+#  include 
+# endif
+
+# include 
+# include "seed_locl.h"
+
+# ifdef SS                      /* can get defined on Solaris by inclusion of
+                                 *  */
+#  undef SS
+# endif
+
+# if !defined(OPENSSL_SMALL_FOOTPRINT)
+
+#  define G_FUNC(v)       \
+        SS[0][(unsigned char)      (v) & 0xff] ^ \
+        SS[1][(unsigned char) ((v)>>8) & 0xff] ^ \
+        SS[2][(unsigned char)((v)>>16) & 0xff] ^ \
+        SS[3][(unsigned char)((v)>>24) & 0xff]
+
+static const seed_word SS[4][256] = {
+    { 0x2989a1a8, 0x05858184, 0x16c6d2d4, 0x13c3d3d0,
+      0x14445054, 0x1d0d111c, 0x2c8ca0ac, 0x25052124,
+      0x1d4d515c, 0x03434340, 0x18081018, 0x1e0e121c,
+      0x11415150, 0x3cccf0fc, 0x0acac2c8, 0x23436360,
+      0x28082028, 0x04444044, 0x20002020, 0x1d8d919c,
+      0x20c0e0e0, 0x22c2e2e0, 0x08c8c0c8, 0x17071314,
+      0x2585a1a4, 0x0f8f838c, 0x03030300, 0x3b4b7378,
+      0x3b8bb3b8, 0x13031310, 0x12c2d2d0, 0x2ecee2ec,
+      0x30407070, 0x0c8c808c, 0x3f0f333c, 0x2888a0a8,
+      0x32023230, 0x1dcdd1dc, 0x36c6f2f4, 0x34447074,
+      0x2ccce0ec, 0x15859194, 0x0b0b0308, 0x17475354,
+      0x1c4c505c, 0x1b4b5358, 0x3d8db1bc, 0x01010100,
+      0x24042024, 0x1c0c101c, 0x33437370, 0x18889098,
+      0x10001010, 0x0cccc0cc, 0x32c2f2f0, 0x19c9d1d8,
+      0x2c0c202c, 0x27c7e3e4, 0x32427270, 0x03838380,
+      0x1b8b9398, 0x11c1d1d0, 0x06868284, 0x09c9c1c8,
+      0x20406060, 0x10405050, 0x2383a3a0, 0x2bcbe3e8,
+      0x0d0d010c, 0x3686b2b4, 0x1e8e929c, 0x0f4f434c,
+      0x3787b3b4, 0x1a4a5258, 0x06c6c2c4, 0x38487078,
+      0x2686a2a4, 0x12021210, 0x2f8fa3ac, 0x15c5d1d4,
+      0x21416160, 0x03c3c3c0, 0x3484b0b4, 0x01414140,
+      0x12425250, 0x3d4d717c, 0x0d8d818c, 0x08080008,
+      0x1f0f131c, 0x19899198, 0x00000000, 0x19091118,
+      0x04040004, 0x13435350, 0x37c7f3f4, 0x21c1e1e0,
+      0x3dcdf1fc, 0x36467274, 0x2f0f232c, 0x27072324,
+      0x3080b0b0, 0x0b8b8388, 0x0e0e020c, 0x2b8ba3a8,
+      0x2282a2a0, 0x2e4e626c, 0x13839390, 0x0d4d414c,
+      0x29496168, 0x3c4c707c, 0x09090108, 0x0a0a0208,
+      0x3f8fb3bc, 0x2fcfe3ec, 0x33c3f3f0, 0x05c5c1c4,
+      0x07878384, 0x14041014, 0x3ecef2fc, 0x24446064,
+      0x1eced2dc, 0x2e0e222c, 0x0b4b4348, 0x1a0a1218,
+      0x06060204, 0x21012120, 0x2b4b6368, 0x26466264,
+      0x02020200, 0x35c5f1f4, 0x12829290, 0x0a8a8288,
+      0x0c0c000c, 0x3383b3b0, 0x3e4e727c, 0x10c0d0d0,
+      0x3a4a7278, 0x07474344, 0x16869294, 0x25c5e1e4,
+      0x26062224, 0x00808080, 0x2d8da1ac, 0x1fcfd3dc,
+      0x2181a1a0, 0x30003030, 0x37073334, 0x2e8ea2ac,
+      0x36063234, 0x15051114, 0x22022220, 0x38083038,
+      0x34c4f0f4, 0x2787a3a4, 0x05454144, 0x0c4c404c,
+      0x01818180, 0x29c9e1e8, 0x04848084, 0x17879394,
+      0x35053134, 0x0bcbc3c8, 0x0ecec2cc, 0x3c0c303c,
+      0x31417170, 0x11011110, 0x07c7c3c4, 0x09898188,
+      0x35457174, 0x3bcbf3f8, 0x1acad2d8, 0x38c8f0f8,
+      0x14849094, 0x19495158, 0x02828280, 0x04c4c0c4,
+      0x3fcff3fc, 0x09494148, 0x39093138, 0x27476364,
+      0x00c0c0c0, 0x0fcfc3cc, 0x17c7d3d4, 0x3888b0b8,
+      0x0f0f030c, 0x0e8e828c, 0x02424240, 0x23032320,
+      0x11819190, 0x2c4c606c, 0x1bcbd3d8, 0x2484a0a4,
+      0x34043034, 0x31c1f1f0, 0x08484048, 0x02c2c2c0,
+      0x2f4f636c, 0x3d0d313c, 0x2d0d212c, 0x00404040,
+      0x3e8eb2bc, 0x3e0e323c, 0x3c8cb0bc, 0x01c1c1c0,
+      0x2a8aa2a8, 0x3a8ab2b8, 0x0e4e424c, 0x15455154,
+      0x3b0b3338, 0x1cccd0dc, 0x28486068, 0x3f4f737c,
+      0x1c8c909c, 0x18c8d0d8, 0x0a4a4248, 0x16465254,
+      0x37477374, 0x2080a0a0, 0x2dcde1ec, 0x06464244,
+      0x3585b1b4, 0x2b0b2328, 0x25456164, 0x3acaf2f8,
+      0x23c3e3e0, 0x3989b1b8, 0x3181b1b0, 0x1f8f939c,
+      0x1e4e525c, 0x39c9f1f8, 0x26c6e2e4, 0x3282b2b0,
+      0x31013130, 0x2acae2e8, 0x2d4d616c, 0x1f4f535c,
+      0x24c4e0e4, 0x30c0f0f0, 0x0dcdc1cc, 0x08888088,
+      0x16061214, 0x3a0a3238, 0x18485058, 0x14c4d0d4,
+      0x22426260, 0x29092128, 0x07070304, 0x33033330,
+      0x28c8e0e8, 0x1b0b1318, 0x05050104, 0x39497178,
+      0x10809090, 0x2a4a6268, 0x2a0a2228, 0x1a8a9298
+    },
+    { 0x38380830, 0xe828c8e0, 0x2c2d0d21, 0xa42686a2,
+      0xcc0fcfc3, 0xdc1eced2, 0xb03383b3, 0xb83888b0,
+      0xac2f8fa3, 0x60204060, 0x54154551, 0xc407c7c3,
+      0x44044440, 0x6c2f4f63, 0x682b4b63, 0x581b4b53,
+      0xc003c3c3, 0x60224262, 0x30330333, 0xb43585b1,
+      0x28290921, 0xa02080a0, 0xe022c2e2, 0xa42787a3,
+      0xd013c3d3, 0x90118191, 0x10110111, 0x04060602,
+      0x1c1c0c10, 0xbc3c8cb0, 0x34360632, 0x480b4b43,
+      0xec2fcfe3, 0x88088880, 0x6c2c4c60, 0xa82888a0,
+      0x14170713, 0xc404c4c0, 0x14160612, 0xf434c4f0,
+      0xc002c2c2, 0x44054541, 0xe021c1e1, 0xd416c6d2,
+      0x3c3f0f33, 0x3c3d0d31, 0x8c0e8e82, 0x98188890,
+      0x28280820, 0x4c0e4e42, 0xf436c6f2, 0x3c3e0e32,
+      0xa42585a1, 0xf839c9f1, 0x0c0d0d01, 0xdc1fcfd3,
+      0xd818c8d0, 0x282b0b23, 0x64264662, 0x783a4a72,
+      0x24270723, 0x2c2f0f23, 0xf031c1f1, 0x70324272,
+      0x40024242, 0xd414c4d0, 0x40014141, 0xc000c0c0,
+      0x70334373, 0x64274763, 0xac2c8ca0, 0x880b8b83,
+      0xf437c7f3, 0xac2d8da1, 0x80008080, 0x1c1f0f13,
+      0xc80acac2, 0x2c2c0c20, 0xa82a8aa2, 0x34340430,
+      0xd012c2d2, 0x080b0b03, 0xec2ecee2, 0xe829c9e1,
+      0x5c1d4d51, 0x94148490, 0x18180810, 0xf838c8f0,
+      0x54174753, 0xac2e8ea2, 0x08080800, 0xc405c5c1,
+      0x10130313, 0xcc0dcdc1, 0x84068682, 0xb83989b1,
+      0xfc3fcff3, 0x7c3d4d71, 0xc001c1c1, 0x30310131,
+      0xf435c5f1, 0x880a8a82, 0x682a4a62, 0xb03181b1,
+      0xd011c1d1, 0x20200020, 0xd417c7d3, 0x00020202,
+      0x20220222, 0x04040400, 0x68284860, 0x70314171,
+      0x04070703, 0xd81bcbd3, 0x9c1d8d91, 0x98198991,
+      0x60214161, 0xbc3e8eb2, 0xe426c6e2, 0x58194951,
+      0xdc1dcdd1, 0x50114151, 0x90108090, 0xdc1cccd0,
+      0x981a8a92, 0xa02383a3, 0xa82b8ba3, 0xd010c0d0,
+      0x80018181, 0x0c0f0f03, 0x44074743, 0x181a0a12,
+      0xe023c3e3, 0xec2ccce0, 0x8c0d8d81, 0xbc3f8fb3,
+      0x94168692, 0x783b4b73, 0x5c1c4c50, 0xa02282a2,
+      0xa02181a1, 0x60234363, 0x20230323, 0x4c0d4d41,
+      0xc808c8c0, 0x9c1e8e92, 0x9c1c8c90, 0x383a0a32,
+      0x0c0c0c00, 0x2c2e0e22, 0xb83a8ab2, 0x6c2e4e62,
+      0x9c1f8f93, 0x581a4a52, 0xf032c2f2, 0x90128292,
+      0xf033c3f3, 0x48094941, 0x78384870, 0xcc0cccc0,
+      0x14150511, 0xf83bcbf3, 0x70304070, 0x74354571,
+      0x7c3f4f73, 0x34350531, 0x10100010, 0x00030303,
+      0x64244460, 0x6c2d4d61, 0xc406c6c2, 0x74344470,
+      0xd415c5d1, 0xb43484b0, 0xe82acae2, 0x08090901,
+      0x74364672, 0x18190911, 0xfc3ecef2, 0x40004040,
+      0x10120212, 0xe020c0e0, 0xbc3d8db1, 0x04050501,
+      0xf83acaf2, 0x00010101, 0xf030c0f0, 0x282a0a22,
+      0x5c1e4e52, 0xa82989a1, 0x54164652, 0x40034343,
+      0x84058581, 0x14140410, 0x88098981, 0x981b8b93,
+      0xb03080b0, 0xe425c5e1, 0x48084840, 0x78394971,
+      0x94178793, 0xfc3cccf0, 0x1c1e0e12, 0x80028282,
+      0x20210121, 0x8c0c8c80, 0x181b0b13, 0x5c1f4f53,
+      0x74374773, 0x54144450, 0xb03282b2, 0x1c1d0d11,
+      0x24250521, 0x4c0f4f43, 0x00000000, 0x44064642,
+      0xec2dcde1, 0x58184850, 0x50124252, 0xe82bcbe3,
+      0x7c3e4e72, 0xd81acad2, 0xc809c9c1, 0xfc3dcdf1,
+      0x30300030, 0x94158591, 0x64254561, 0x3c3c0c30,
+      0xb43686b2, 0xe424c4e0, 0xb83b8bb3, 0x7c3c4c70,
+      0x0c0e0e02, 0x50104050, 0x38390931, 0x24260622,
+      0x30320232, 0x84048480, 0x68294961, 0x90138393,
+      0x34370733, 0xe427c7e3, 0x24240420, 0xa42484a0,
+      0xc80bcbc3, 0x50134353, 0x080a0a02, 0x84078783,
+      0xd819c9d1, 0x4c0c4c40, 0x80038383, 0x8c0f8f83,
+      0xcc0ecec2, 0x383b0b33, 0x480a4a42, 0xb43787b3
+    },
+    { 0xa1a82989, 0x81840585, 0xd2d416c6, 0xd3d013c3,
+      0x50541444, 0x111c1d0d, 0xa0ac2c8c, 0x21242505,
+      0x515c1d4d, 0x43400343, 0x10181808, 0x121c1e0e,
+      0x51501141, 0xf0fc3ccc, 0xc2c80aca, 0x63602343,
+      0x20282808, 0x40440444, 0x20202000, 0x919c1d8d,
+      0xe0e020c0, 0xe2e022c2, 0xc0c808c8, 0x13141707,
+      0xa1a42585, 0x838c0f8f, 0x03000303, 0x73783b4b,
+      0xb3b83b8b, 0x13101303, 0xd2d012c2, 0xe2ec2ece,
+      0x70703040, 0x808c0c8c, 0x333c3f0f, 0xa0a82888,
+      0x32303202, 0xd1dc1dcd, 0xf2f436c6, 0x70743444,
+      0xe0ec2ccc, 0x91941585, 0x03080b0b, 0x53541747,
+      0x505c1c4c, 0x53581b4b, 0xb1bc3d8d, 0x01000101,
+      0x20242404, 0x101c1c0c, 0x73703343, 0x90981888,
+      0x10101000, 0xc0cc0ccc, 0xf2f032c2, 0xd1d819c9,
+      0x202c2c0c, 0xe3e427c7, 0x72703242, 0x83800383,
+      0x93981b8b, 0xd1d011c1, 0x82840686, 0xc1c809c9,
+      0x60602040, 0x50501040, 0xa3a02383, 0xe3e82bcb,
+      0x010c0d0d, 0xb2b43686, 0x929c1e8e, 0x434c0f4f,
+      0xb3b43787, 0x52581a4a, 0xc2c406c6, 0x70783848,
+      0xa2a42686, 0x12101202, 0xa3ac2f8f, 0xd1d415c5,
+      0x61602141, 0xc3c003c3, 0xb0b43484, 0x41400141,
+      0x52501242, 0x717c3d4d, 0x818c0d8d, 0x00080808,
+      0x131c1f0f, 0x91981989, 0x00000000, 0x11181909,
+      0x00040404, 0x53501343, 0xf3f437c7, 0xe1e021c1,
+      0xf1fc3dcd, 0x72743646, 0x232c2f0f, 0x23242707,
+      0xb0b03080, 0x83880b8b, 0x020c0e0e, 0xa3a82b8b,
+      0xa2a02282, 0x626c2e4e, 0x93901383, 0x414c0d4d,
+      0x61682949, 0x707c3c4c, 0x01080909, 0x02080a0a,
+      0xb3bc3f8f, 0xe3ec2fcf, 0xf3f033c3, 0xc1c405c5,
+      0x83840787, 0x10141404, 0xf2fc3ece, 0x60642444,
+      0xd2dc1ece, 0x222c2e0e, 0x43480b4b, 0x12181a0a,
+      0x02040606, 0x21202101, 0x63682b4b, 0x62642646,
+      0x02000202, 0xf1f435c5, 0x92901282, 0x82880a8a,
+      0x000c0c0c, 0xb3b03383, 0x727c3e4e, 0xd0d010c0,
+      0x72783a4a, 0x43440747, 0x92941686, 0xe1e425c5,
+      0x22242606, 0x80800080, 0xa1ac2d8d, 0xd3dc1fcf,
+      0xa1a02181, 0x30303000, 0x33343707, 0xa2ac2e8e,
+      0x32343606, 0x11141505, 0x22202202, 0x30383808,
+      0xf0f434c4, 0xa3a42787, 0x41440545, 0x404c0c4c,
+      0x81800181, 0xe1e829c9, 0x80840484, 0x93941787,
+      0x31343505, 0xc3c80bcb, 0xc2cc0ece, 0x303c3c0c,
+      0x71703141, 0x11101101, 0xc3c407c7, 0x81880989,
+      0x71743545, 0xf3f83bcb, 0xd2d81aca, 0xf0f838c8,
+      0x90941484, 0x51581949, 0x82800282, 0xc0c404c4,
+      0xf3fc3fcf, 0x41480949, 0x31383909, 0x63642747,
+      0xc0c000c0, 0xc3cc0fcf, 0xd3d417c7, 0xb0b83888,
+      0x030c0f0f, 0x828c0e8e, 0x42400242, 0x23202303,
+      0x91901181, 0x606c2c4c, 0xd3d81bcb, 0xa0a42484,
+      0x30343404, 0xf1f031c1, 0x40480848, 0xc2c002c2,
+      0x636c2f4f, 0x313c3d0d, 0x212c2d0d, 0x40400040,
+      0xb2bc3e8e, 0x323c3e0e, 0xb0bc3c8c, 0xc1c001c1,
+      0xa2a82a8a, 0xb2b83a8a, 0x424c0e4e, 0x51541545,
+      0x33383b0b, 0xd0dc1ccc, 0x60682848, 0x737c3f4f,
+      0x909c1c8c, 0xd0d818c8, 0x42480a4a, 0x52541646,
+      0x73743747, 0xa0a02080, 0xe1ec2dcd, 0x42440646,
+      0xb1b43585, 0x23282b0b, 0x61642545, 0xf2f83aca,
+      0xe3e023c3, 0xb1b83989, 0xb1b03181, 0x939c1f8f,
+      0x525c1e4e, 0xf1f839c9, 0xe2e426c6, 0xb2b03282,
+      0x31303101, 0xe2e82aca, 0x616c2d4d, 0x535c1f4f,
+      0xe0e424c4, 0xf0f030c0, 0xc1cc0dcd, 0x80880888,
+      0x12141606, 0x32383a0a, 0x50581848, 0xd0d414c4,
+      0x62602242, 0x21282909, 0x03040707, 0x33303303,
+      0xe0e828c8, 0x13181b0b, 0x01040505, 0x71783949,
+      0x90901080, 0x62682a4a, 0x22282a0a, 0x92981a8a
+    },
+    { 0x08303838, 0xc8e0e828, 0x0d212c2d, 0x86a2a426,
+      0xcfc3cc0f, 0xced2dc1e, 0x83b3b033, 0x88b0b838,
+      0x8fa3ac2f, 0x40606020, 0x45515415, 0xc7c3c407,
+      0x44404404, 0x4f636c2f, 0x4b63682b, 0x4b53581b,
+      0xc3c3c003, 0x42626022, 0x03333033, 0x85b1b435,
+      0x09212829, 0x80a0a020, 0xc2e2e022, 0x87a3a427,
+      0xc3d3d013, 0x81919011, 0x01111011, 0x06020406,
+      0x0c101c1c, 0x8cb0bc3c, 0x06323436, 0x4b43480b,
+      0xcfe3ec2f, 0x88808808, 0x4c606c2c, 0x88a0a828,
+      0x07131417, 0xc4c0c404, 0x06121416, 0xc4f0f434,
+      0xc2c2c002, 0x45414405, 0xc1e1e021, 0xc6d2d416,
+      0x0f333c3f, 0x0d313c3d, 0x8e828c0e, 0x88909818,
+      0x08202828, 0x4e424c0e, 0xc6f2f436, 0x0e323c3e,
+      0x85a1a425, 0xc9f1f839, 0x0d010c0d, 0xcfd3dc1f,
+      0xc8d0d818, 0x0b23282b, 0x46626426, 0x4a72783a,
+      0x07232427, 0x0f232c2f, 0xc1f1f031, 0x42727032,
+      0x42424002, 0xc4d0d414, 0x41414001, 0xc0c0c000,
+      0x43737033, 0x47636427, 0x8ca0ac2c, 0x8b83880b,
+      0xc7f3f437, 0x8da1ac2d, 0x80808000, 0x0f131c1f,
+      0xcac2c80a, 0x0c202c2c, 0x8aa2a82a, 0x04303434,
+      0xc2d2d012, 0x0b03080b, 0xcee2ec2e, 0xc9e1e829,
+      0x4d515c1d, 0x84909414, 0x08101818, 0xc8f0f838,
+      0x47535417, 0x8ea2ac2e, 0x08000808, 0xc5c1c405,
+      0x03131013, 0xcdc1cc0d, 0x86828406, 0x89b1b839,
+      0xcff3fc3f, 0x4d717c3d, 0xc1c1c001, 0x01313031,
+      0xc5f1f435, 0x8a82880a, 0x4a62682a, 0x81b1b031,
+      0xc1d1d011, 0x00202020, 0xc7d3d417, 0x02020002,
+      0x02222022, 0x04000404, 0x48606828, 0x41717031,
+      0x07030407, 0xcbd3d81b, 0x8d919c1d, 0x89919819,
+      0x41616021, 0x8eb2bc3e, 0xc6e2e426, 0x49515819,
+      0xcdd1dc1d, 0x41515011, 0x80909010, 0xccd0dc1c,
+      0x8a92981a, 0x83a3a023, 0x8ba3a82b, 0xc0d0d010,
+      0x81818001, 0x0f030c0f, 0x47434407, 0x0a12181a,
+      0xc3e3e023, 0xcce0ec2c, 0x8d818c0d, 0x8fb3bc3f,
+      0x86929416, 0x4b73783b, 0x4c505c1c, 0x82a2a022,
+      0x81a1a021, 0x43636023, 0x03232023, 0x4d414c0d,
+      0xc8c0c808, 0x8e929c1e, 0x8c909c1c, 0x0a32383a,
+      0x0c000c0c, 0x0e222c2e, 0x8ab2b83a, 0x4e626c2e,
+      0x8f939c1f, 0x4a52581a, 0xc2f2f032, 0x82929012,
+      0xc3f3f033, 0x49414809, 0x48707838, 0xccc0cc0c,
+      0x05111415, 0xcbf3f83b, 0x40707030, 0x45717435,
+      0x4f737c3f, 0x05313435, 0x00101010, 0x03030003,
+      0x44606424, 0x4d616c2d, 0xc6c2c406, 0x44707434,
+      0xc5d1d415, 0x84b0b434, 0xcae2e82a, 0x09010809,
+      0x46727436, 0x09111819, 0xcef2fc3e, 0x40404000,
+      0x02121012, 0xc0e0e020, 0x8db1bc3d, 0x05010405,
+      0xcaf2f83a, 0x01010001, 0xc0f0f030, 0x0a22282a,
+      0x4e525c1e, 0x89a1a829, 0x46525416, 0x43434003,
+      0x85818405, 0x04101414, 0x89818809, 0x8b93981b,
+      0x80b0b030, 0xc5e1e425, 0x48404808, 0x49717839,
+      0x87939417, 0xccf0fc3c, 0x0e121c1e, 0x82828002,
+      0x01212021, 0x8c808c0c, 0x0b13181b, 0x4f535c1f,
+      0x47737437, 0x44505414, 0x82b2b032, 0x0d111c1d,
+      0x05212425, 0x4f434c0f, 0x00000000, 0x46424406,
+      0xcde1ec2d, 0x48505818, 0x42525012, 0xcbe3e82b,
+      0x4e727c3e, 0xcad2d81a, 0xc9c1c809, 0xcdf1fc3d,
+      0x00303030, 0x85919415, 0x45616425, 0x0c303c3c,
+      0x86b2b436, 0xc4e0e424, 0x8bb3b83b, 0x4c707c3c,
+      0x0e020c0e, 0x40505010, 0x09313839, 0x06222426,
+      0x02323032, 0x84808404, 0x49616829, 0x83939013,
+      0x07333437, 0xc7e3e427, 0x04202424, 0x84a0a424,
+      0xcbc3c80b, 0x43535013, 0x0a02080a, 0x87838407,
+      0xc9d1d819, 0x4c404c0c, 0x83838003, 0x8f838c0f,
+      0xcec2cc0e, 0x0b33383b, 0x4a42480a, 0x87b3b437
+    }
+};
+
+#else
+
+/* on x86_64 >5x size reduction at 40% performance penalty */
+static const unsigned char SEED_Sbox[2][256] = {
+{
+      0xA9, 0x85, 0xD6, 0xD3, 0x54, 0x1D, 0xAC, 0x25,
+      0x5D, 0x43, 0x18, 0x1E, 0x51, 0xFC, 0xCA, 0x63,
+      0x28, 0x44, 0x20, 0x9D, 0xE0, 0xE2, 0xC8, 0x17,
+      0xA5, 0x8F, 0x03, 0x7B, 0xBB, 0x13, 0xD2, 0xEE,
+      0x70, 0x8C, 0x3F, 0xA8, 0x32, 0xDD, 0xF6, 0x74,
+      0xEC, 0x95, 0x0B, 0x57, 0x5C, 0x5B, 0xBD, 0x01,
+      0x24, 0x1C, 0x73, 0x98, 0x10, 0xCC, 0xF2, 0xD9,
+      0x2C, 0xE7, 0x72, 0x83, 0x9B, 0xD1, 0x86, 0xC9,
+      0x60, 0x50, 0xA3, 0xEB, 0x0D, 0xB6, 0x9E, 0x4F,
+      0xB7, 0x5A, 0xC6, 0x78, 0xA6, 0x12, 0xAF, 0xD5,
+      0x61, 0xC3, 0xB4, 0x41, 0x52, 0x7D, 0x8D, 0x08,
+      0x1F, 0x99, 0x00, 0x19, 0x04, 0x53, 0xF7, 0xE1,
+      0xFD, 0x76, 0x2F, 0x27, 0xB0, 0x8B, 0x0E, 0xAB,
+      0xA2, 0x6E, 0x93, 0x4D, 0x69, 0x7C, 0x09, 0x0A,
+      0xBF, 0xEF, 0xF3, 0xC5, 0x87, 0x14, 0xFE, 0x64,
+      0xDE, 0x2E, 0x4B, 0x1A, 0x06, 0x21, 0x6B, 0x66,
+      0x02, 0xF5, 0x92, 0x8A, 0x0C, 0xB3, 0x7E, 0xD0,
+      0x7A, 0x47, 0x96, 0xE5, 0x26, 0x80, 0xAD, 0xDF,
+      0xA1, 0x30, 0x37, 0xAE, 0x36, 0x15, 0x22, 0x38,
+      0xF4, 0xA7, 0x45, 0x4C, 0x81, 0xE9, 0x84, 0x97,
+      0x35, 0xCB, 0xCE, 0x3C, 0x71, 0x11, 0xC7, 0x89,
+      0x75, 0xFB, 0xDA, 0xF8, 0x94, 0x59, 0x82, 0xC4,
+      0xFF, 0x49, 0x39, 0x67, 0xC0, 0xCF, 0xD7, 0xB8,
+      0x0F, 0x8E, 0x42, 0x23, 0x91, 0x6C, 0xDB, 0xA4,
+      0x34, 0xF1, 0x48, 0xC2, 0x6F, 0x3D, 0x2D, 0x40,
+      0xBE, 0x3E, 0xBC, 0xC1, 0xAA, 0xBA, 0x4E, 0x55,
+      0x3B, 0xDC, 0x68, 0x7F, 0x9C, 0xD8, 0x4A, 0x56,
+      0x77, 0xA0, 0xED, 0x46, 0xB5, 0x2B, 0x65, 0xFA,
+      0xE3, 0xB9, 0xB1, 0x9F, 0x5E, 0xF9, 0xE6, 0xB2,
+      0x31, 0xEA, 0x6D, 0x5F, 0xE4, 0xF0, 0xCD, 0x88,
+      0x16, 0x3A, 0x58, 0xD4, 0x62, 0x29, 0x07, 0x33,
+      0xE8, 0x1B, 0x05, 0x79, 0x90, 0x6A, 0x2A, 0x9A
+    },
+    {
+      0x38, 0xE8, 0x2D, 0xA6, 0xCF, 0xDE, 0xB3, 0xB8,
+      0xAF, 0x60, 0x55, 0xC7, 0x44, 0x6F, 0x6B, 0x5B,
+      0xC3, 0x62, 0x33, 0xB5, 0x29, 0xA0, 0xE2, 0xA7,
+      0xD3, 0x91, 0x11, 0x06, 0x1C, 0xBC, 0x36, 0x4B,
+      0xEF, 0x88, 0x6C, 0xA8, 0x17, 0xC4, 0x16, 0xF4,
+      0xC2, 0x45, 0xE1, 0xD6, 0x3F, 0x3D, 0x8E, 0x98,
+      0x28, 0x4E, 0xF6, 0x3E, 0xA5, 0xF9, 0x0D, 0xDF,
+      0xD8, 0x2B, 0x66, 0x7A, 0x27, 0x2F, 0xF1, 0x72,
+      0x42, 0xD4, 0x41, 0xC0, 0x73, 0x67, 0xAC, 0x8B,
+      0xF7, 0xAD, 0x80, 0x1F, 0xCA, 0x2C, 0xAA, 0x34,
+      0xD2, 0x0B, 0xEE, 0xE9, 0x5D, 0x94, 0x18, 0xF8,
+      0x57, 0xAE, 0x08, 0xC5, 0x13, 0xCD, 0x86, 0xB9,
+      0xFF, 0x7D, 0xC1, 0x31, 0xF5, 0x8A, 0x6A, 0xB1,
+      0xD1, 0x20, 0xD7, 0x02, 0x22, 0x04, 0x68, 0x71,
+      0x07, 0xDB, 0x9D, 0x99, 0x61, 0xBE, 0xE6, 0x59,
+      0xDD, 0x51, 0x90, 0xDC, 0x9A, 0xA3, 0xAB, 0xD0,
+      0x81, 0x0F, 0x47, 0x1A, 0xE3, 0xEC, 0x8D, 0xBF,
+      0x96, 0x7B, 0x5C, 0xA2, 0xA1, 0x63, 0x23, 0x4D,
+      0xC8, 0x9E, 0x9C, 0x3A, 0x0C, 0x2E, 0xBA, 0x6E,
+      0x9F, 0x5A, 0xF2, 0x92, 0xF3, 0x49, 0x78, 0xCC,
+      0x15, 0xFB, 0x70, 0x75, 0x7F, 0x35, 0x10, 0x03,
+      0x64, 0x6D, 0xC6, 0x74, 0xD5, 0xB4, 0xEA, 0x09,
+      0x76, 0x19, 0xFE, 0x40, 0x12, 0xE0, 0xBD, 0x05,
+      0xFA, 0x01, 0xF0, 0x2A, 0x5E, 0xA9, 0x56, 0x43,
+      0x85, 0x14, 0x89, 0x9B, 0xB0, 0xE5, 0x48, 0x79,
+      0x97, 0xFC, 0x1E, 0x82, 0x21, 0x8C, 0x1B, 0x5F,
+      0x77, 0x54, 0xB2, 0x1D, 0x25, 0x4F, 0x00, 0x46,
+      0xED, 0x58, 0x52, 0xEB, 0x7E, 0xDA, 0xC9, 0xFD,
+      0x30, 0x95, 0x65, 0x3C, 0xB6, 0xE4, 0xBB, 0x7C,
+      0x0E, 0x50, 0x39, 0x26, 0x32, 0x84, 0x69, 0x93,
+      0x37, 0xE7, 0x24, 0xA4, 0xCB, 0x53, 0x0A, 0x87,
+      0xD9, 0x4C, 0x83, 0x8F, 0xCE, 0x3B, 0x4A, 0xB7
+    }
+};
+
+static unsigned int G_FUNC(unsigned int v)
+{
+    unsigned int s0, s1, s2, s3, ret;
+
+    s0 = SEED_Sbox[0][(unsigned char)      (v) & 0xff];
+    s1 = SEED_Sbox[1][(unsigned char)((v)>> 8) & 0xff];
+    s2 = SEED_Sbox[0][(unsigned char)((v)>>16) & 0xff];
+    s3 = SEED_Sbox[1][(unsigned char)((v)>>24) & 0xff];
+
+    ret  = ((s0 & 0xFC) ^ (s1 & 0xF3) ^ (s2 & 0xCF) ^ (s3 & 0x3F));
+    ret |= ((s0 & 0xF3) ^ (s1 & 0xCF) ^ (s2 & 0x3F) ^ (s3 & 0xFC)) << 8;
+    ret |= ((s0 & 0xCF) ^ (s1 & 0x3F) ^ (s2 & 0xFC) ^ (s3 & 0xF3)) << 16;
+    ret |= ((s0 & 0x3F) ^ (s1 & 0xFC) ^ (s2 & 0xF3) ^ (s3 & 0xCF)) << 24;
+
+    return ret;
+}
+# endif
+
+/* key schedule constants - golden ratio */
+# define KC0     0x9e3779b9
+# define KC1     0x3c6ef373
+# define KC2     0x78dde6e6
+# define KC3     0xf1bbcdcc
+# define KC4     0xe3779b99
+# define KC5     0xc6ef3733
+# define KC6     0x8dde6e67
+# define KC7     0x1bbcdccf
+# define KC8     0x3779b99e
+# define KC9     0x6ef3733c
+# define KC10    0xdde6e678
+# define KC11    0xbbcdccf1
+# define KC12    0x779b99e3
+# define KC13    0xef3733c6
+# define KC14    0xde6e678d
+# define KC15    0xbcdccf1b
+
+# if defined(OPENSSL_SMALL_FOOTPRINT)
+static const seed_word KC[] = {
+    KC0, KC1, KC2, KC3, KC4, KC5, KC6, KC7,
+    KC8, KC9, KC10, KC11, KC12, KC13, KC14, KC15
+};
+# endif
+
+void SEED_set_key(const unsigned char rawkey[SEED_KEY_LENGTH],
+                  SEED_KEY_SCHEDULE *ks)
+{
+    seed_word x1, x2, x3, x4;
+    seed_word t0, t1;
+
+    char2word(rawkey, x1);
+    char2word(rawkey + 4, x2);
+    char2word(rawkey + 8, x3);
+    char2word(rawkey + 12, x4);
+
+    t0 = (x1 + x3 - KC0) & 0xffffffff;
+    t1 = (x2 - x4 + KC0) & 0xffffffff;
+    KEYUPDATE_TEMP(t0, t1, &ks->data[0]);
+    KEYSCHEDULE_UPDATE1(t0, t1, x1, x2, x3, x4, KC1);
+    KEYUPDATE_TEMP(t0, t1, &ks->data[2]);
+
+# if !defined(OPENSSL_SMALL_FOOTPRINT)
+    KEYSCHEDULE_UPDATE0(t0, t1, x1, x2, x3, x4, KC2);
+    KEYUPDATE_TEMP(t0, t1, &ks->data[4]);
+    KEYSCHEDULE_UPDATE1(t0, t1, x1, x2, x3, x4, KC3);
+    KEYUPDATE_TEMP(t0, t1, &ks->data[6]);
+    KEYSCHEDULE_UPDATE0(t0, t1, x1, x2, x3, x4, KC4);
+    KEYUPDATE_TEMP(t0, t1, &ks->data[8]);
+    KEYSCHEDULE_UPDATE1(t0, t1, x1, x2, x3, x4, KC5);
+    KEYUPDATE_TEMP(t0, t1, &ks->data[10]);
+    KEYSCHEDULE_UPDATE0(t0, t1, x1, x2, x3, x4, KC6);
+    KEYUPDATE_TEMP(t0, t1, &ks->data[12]);
+    KEYSCHEDULE_UPDATE1(t0, t1, x1, x2, x3, x4, KC7);
+    KEYUPDATE_TEMP(t0, t1, &ks->data[14]);
+    KEYSCHEDULE_UPDATE0(t0, t1, x1, x2, x3, x4, KC8);
+    KEYUPDATE_TEMP(t0, t1, &ks->data[16]);
+    KEYSCHEDULE_UPDATE1(t0, t1, x1, x2, x3, x4, KC9);
+    KEYUPDATE_TEMP(t0, t1, &ks->data[18]);
+    KEYSCHEDULE_UPDATE0(t0, t1, x1, x2, x3, x4, KC10);
+    KEYUPDATE_TEMP(t0, t1, &ks->data[20]);
+    KEYSCHEDULE_UPDATE1(t0, t1, x1, x2, x3, x4, KC11);
+    KEYUPDATE_TEMP(t0, t1, &ks->data[22]);
+    KEYSCHEDULE_UPDATE0(t0, t1, x1, x2, x3, x4, KC12);
+    KEYUPDATE_TEMP(t0, t1, &ks->data[24]);
+    KEYSCHEDULE_UPDATE1(t0, t1, x1, x2, x3, x4, KC13);
+    KEYUPDATE_TEMP(t0, t1, &ks->data[26]);
+    KEYSCHEDULE_UPDATE0(t0, t1, x1, x2, x3, x4, KC14);
+    KEYUPDATE_TEMP(t0, t1, &ks->data[28]);
+    KEYSCHEDULE_UPDATE1(t0, t1, x1, x2, x3, x4, KC15);
+    KEYUPDATE_TEMP(t0, t1, &ks->data[30]);
+# else
+    {
+        int i;
+        for (i = 2; i < 16; i += 2) {
+            KEYSCHEDULE_UPDATE0(t0, t1, x1, x2, x3, x4, KC[i]);
+            KEYUPDATE_TEMP(t0, t1, &ks->data[i * 2]);
+            KEYSCHEDULE_UPDATE1(t0, t1, x1, x2, x3, x4, KC[i + 1]);
+            KEYUPDATE_TEMP(t0, t1, &ks->data[i * 2 + 2]);
+        }
+    }
+# endif
+}
+
+void SEED_encrypt(const unsigned char s[SEED_BLOCK_SIZE],
+                  unsigned char d[SEED_BLOCK_SIZE],
+                  const SEED_KEY_SCHEDULE *ks)
+{
+    seed_word x1, x2, x3, x4;
+    seed_word t0, t1;
+
+    char2word(s, x1);
+    char2word(s + 4, x2);
+    char2word(s + 8, x3);
+    char2word(s + 12, x4);
+
+# if !defined(OPENSSL_SMALL_FOOTPRINT)
+    E_SEED(t0, t1, x1, x2, x3, x4, 0);
+    E_SEED(t0, t1, x3, x4, x1, x2, 2);
+    E_SEED(t0, t1, x1, x2, x3, x4, 4);
+    E_SEED(t0, t1, x3, x4, x1, x2, 6);
+    E_SEED(t0, t1, x1, x2, x3, x4, 8);
+    E_SEED(t0, t1, x3, x4, x1, x2, 10);
+    E_SEED(t0, t1, x1, x2, x3, x4, 12);
+    E_SEED(t0, t1, x3, x4, x1, x2, 14);
+    E_SEED(t0, t1, x1, x2, x3, x4, 16);
+    E_SEED(t0, t1, x3, x4, x1, x2, 18);
+    E_SEED(t0, t1, x1, x2, x3, x4, 20);
+    E_SEED(t0, t1, x3, x4, x1, x2, 22);
+    E_SEED(t0, t1, x1, x2, x3, x4, 24);
+    E_SEED(t0, t1, x3, x4, x1, x2, 26);
+    E_SEED(t0, t1, x1, x2, x3, x4, 28);
+    E_SEED(t0, t1, x3, x4, x1, x2, 30);
+# else
+    {
+        int i;
+        for (i = 0; i < 30; i += 4) {
+            E_SEED(t0, t1, x1, x2, x3, x4, i);
+            E_SEED(t0, t1, x3, x4, x1, x2, i + 2);
+        }
+    }
+# endif
+
+    word2char(x3, d);
+    word2char(x4, d + 4);
+    word2char(x1, d + 8);
+    word2char(x2, d + 12);
+}
+
+void SEED_decrypt(const unsigned char s[SEED_BLOCK_SIZE],
+                  unsigned char d[SEED_BLOCK_SIZE],
+                  const SEED_KEY_SCHEDULE *ks)
+{
+    seed_word x1, x2, x3, x4;
+    seed_word t0, t1;
+
+    char2word(s, x1);
+    char2word(s + 4, x2);
+    char2word(s + 8, x3);
+    char2word(s + 12, x4);
+
+# if !defined(OPENSSL_SMALL_FOOTPRINT)
+    E_SEED(t0, t1, x1, x2, x3, x4, 30);
+    E_SEED(t0, t1, x3, x4, x1, x2, 28);
+    E_SEED(t0, t1, x1, x2, x3, x4, 26);
+    E_SEED(t0, t1, x3, x4, x1, x2, 24);
+    E_SEED(t0, t1, x1, x2, x3, x4, 22);
+    E_SEED(t0, t1, x3, x4, x1, x2, 20);
+    E_SEED(t0, t1, x1, x2, x3, x4, 18);
+    E_SEED(t0, t1, x3, x4, x1, x2, 16);
+    E_SEED(t0, t1, x1, x2, x3, x4, 14);
+    E_SEED(t0, t1, x3, x4, x1, x2, 12);
+    E_SEED(t0, t1, x1, x2, x3, x4, 10);
+    E_SEED(t0, t1, x3, x4, x1, x2, 8);
+    E_SEED(t0, t1, x1, x2, x3, x4, 6);
+    E_SEED(t0, t1, x3, x4, x1, x2, 4);
+    E_SEED(t0, t1, x1, x2, x3, x4, 2);
+    E_SEED(t0, t1, x3, x4, x1, x2, 0);
+# else
+    {
+        int i;
+        for (i = 30; i > 0; i -= 4) {
+            E_SEED(t0, t1, x1, x2, x3, x4, i);
+            E_SEED(t0, t1, x3, x4, x1, x2, i - 2);
+
+        }
+    }
+# endif
+
+    word2char(x3, d);
+    word2char(x4, d + 4);
+    word2char(x1, d + 8);
+    word2char(x2, d + 12);
+}
+
+#endif                          /* OPENSSL_NO_SEED */
diff --git a/crypto/seed/seed_cbc.c b/crypto/seed/seed_cbc.c
new file mode 100644
index 000000000..c9a4fe217
--- /dev/null
+++ b/crypto/seed/seed_cbc.c
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2007-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+
+void SEED_cbc_encrypt(const unsigned char *in, unsigned char *out,
+                      size_t len, const SEED_KEY_SCHEDULE *ks,
+                      unsigned char ivec[SEED_BLOCK_SIZE], int enc)
+{
+    if (enc)
+        CRYPTO_cbc128_encrypt(in, out, len, ks, ivec,
+                              (block128_f) SEED_encrypt);
+    else
+        CRYPTO_cbc128_decrypt(in, out, len, ks, ivec,
+                              (block128_f) SEED_decrypt);
+}
diff --git a/crypto/seed/seed_cfb.c b/crypto/seed/seed_cfb.c
new file mode 100644
index 000000000..2aee1ffe3
--- /dev/null
+++ b/crypto/seed/seed_cfb.c
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2007-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+
+void SEED_cfb128_encrypt(const unsigned char *in, unsigned char *out,
+                         size_t len, const SEED_KEY_SCHEDULE *ks,
+                         unsigned char ivec[SEED_BLOCK_SIZE], int *num,
+                         int enc)
+{
+    CRYPTO_cfb128_encrypt(in, out, len, ks, ivec, num, enc,
+                          (block128_f) SEED_encrypt);
+}
diff --git a/crypto/seed/seed_ecb.c b/crypto/seed/seed_ecb.c
new file mode 100644
index 000000000..b6e301ccd
--- /dev/null
+++ b/crypto/seed/seed_ecb.c
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2007-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+
+void SEED_ecb_encrypt(const unsigned char *in, unsigned char *out,
+                      const SEED_KEY_SCHEDULE *ks, int enc)
+{
+    if (enc)
+        SEED_encrypt(in, out, ks);
+    else
+        SEED_decrypt(in, out, ks);
+}
diff --git a/crypto/seed/seed_locl.h b/crypto/seed/seed_locl.h
new file mode 100644
index 000000000..d4a03fc4a
--- /dev/null
+++ b/crypto/seed/seed_locl.h
@@ -0,0 +1,120 @@
+/*
+ * Copyright 2007-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * Copyright (c) 2007 KISA(Korea Information Security Agency). All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Neither the name of author nor the names of its contributors may
+ *    be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+#ifndef HEADER_SEED_LOCL_H
+# define HEADER_SEED_LOCL_H
+
+# include "openssl/e_os2.h"
+# include 
+
+# ifdef SEED_LONG               /* need 32-bit type */
+typedef unsigned long seed_word;
+# else
+typedef unsigned int seed_word;
+# endif
+
+
+#ifdef  __cplusplus
+extern "C" {
+#endif
+
+# define char2word(c, i)  \
+        (i) = ((((seed_word)(c)[0]) << 24) | (((seed_word)(c)[1]) << 16) | (((seed_word)(c)[2]) << 8) | ((seed_word)(c)[3]))
+
+# define word2char(l, c)  \
+        *((c)+0) = (unsigned char)((l)>>24) & 0xff; \
+        *((c)+1) = (unsigned char)((l)>>16) & 0xff; \
+        *((c)+2) = (unsigned char)((l)>> 8) & 0xff; \
+        *((c)+3) = (unsigned char)((l))     & 0xff
+
+# define KEYSCHEDULE_UPDATE0(T0, T1, X1, X2, X3, X4, KC)  \
+        (T0) = (X3);                                     \
+        (X3) = (((X3)<<8) ^ ((X4)>>24)) & 0xffffffff;    \
+        (X4) = (((X4)<<8) ^ ((T0)>>24)) & 0xffffffff;    \
+        (T0) = ((X1) + (X3) - (KC))     & 0xffffffff;    \
+        (T1) = ((X2) + (KC) - (X4))     & 0xffffffff
+
+# define KEYSCHEDULE_UPDATE1(T0, T1, X1, X2, X3, X4, KC)  \
+        (T0) = (X1);                                     \
+        (X1) = (((X1)>>8) ^ ((X2)<<24)) & 0xffffffff;    \
+        (X2) = (((X2)>>8) ^ ((T0)<<24)) & 0xffffffff;    \
+        (T0) = ((X1) + (X3) - (KC))     & 0xffffffff;     \
+        (T1) = ((X2) + (KC) - (X4))     & 0xffffffff
+
+# define KEYUPDATE_TEMP(T0, T1, K)   \
+        (K)[0] = G_FUNC((T0));      \
+        (K)[1] = G_FUNC((T1))
+
+# define XOR_SEEDBLOCK(DST, SRC)      \
+        ((DST))[0] ^= ((SRC))[0];    \
+        ((DST))[1] ^= ((SRC))[1];    \
+        ((DST))[2] ^= ((SRC))[2];    \
+        ((DST))[3] ^= ((SRC))[3]
+
+# define MOV_SEEDBLOCK(DST, SRC)      \
+        ((DST))[0] = ((SRC))[0];     \
+        ((DST))[1] = ((SRC))[1];     \
+        ((DST))[2] = ((SRC))[2];     \
+        ((DST))[3] = ((SRC))[3]
+
+# define CHAR2WORD(C, I)              \
+        char2word((C),    (I)[0]);    \
+        char2word((C+4),  (I)[1]);    \
+        char2word((C+8),  (I)[2]);    \
+        char2word((C+12), (I)[3])
+
+# define WORD2CHAR(I, C)              \
+        word2char((I)[0], (C));       \
+        word2char((I)[1], (C+4));     \
+        word2char((I)[2], (C+8));     \
+        word2char((I)[3], (C+12))
+
+# define E_SEED(T0, T1, X1, X2, X3, X4, rbase)   \
+        (T0) = (X3) ^ (ks->data)[(rbase)];       \
+        (T1) = (X4) ^ (ks->data)[(rbase)+1];     \
+        (T1) ^= (T0);                            \
+        (T1) = G_FUNC((T1));                     \
+        (T0) = ((T0) + (T1)) & 0xffffffff;       \
+        (T0) = G_FUNC((T0));                     \
+        (T1) = ((T1) + (T0)) & 0xffffffff;       \
+        (T1) = G_FUNC((T1));                     \
+        (T0) = ((T0) + (T1)) & 0xffffffff;       \
+        (X1) ^= (T0);                            \
+        (X2) ^= (T1)
+
+#ifdef  __cplusplus
+}
+#endif
+
+#endif                          /* HEADER_SEED_LOCL_H */
diff --git a/crypto/seed/seed_ofb.c b/crypto/seed/seed_ofb.c
new file mode 100644
index 000000000..b45554058
--- /dev/null
+++ b/crypto/seed/seed_ofb.c
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2007-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+
+void SEED_ofb128_encrypt(const unsigned char *in, unsigned char *out,
+                         size_t len, const SEED_KEY_SCHEDULE *ks,
+                         unsigned char ivec[SEED_BLOCK_SIZE], int *num)
+{
+    CRYPTO_ofb128_encrypt(in, out, len, ks, ivec, num,
+                          (block128_f) SEED_encrypt);
+}
diff --git a/crypto/sha/asm/sha1-586.pl b/crypto/sha/asm/sha1-586.pl
new file mode 100644
index 000000000..0efed70a3
--- /dev/null
+++ b/crypto/sha/asm/sha1-586.pl
@@ -0,0 +1,1488 @@
+#! /usr/bin/env perl
+# Copyright 1998-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+
+# ====================================================================
+# [Re]written by Andy Polyakov  for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+
+# "[Re]written" was achieved in two major overhauls. In 2004 BODY_*
+# functions were re-implemented to address P4 performance issue [see
+# commentary below], and in 2006 the rest was rewritten in order to
+# gain freedom to liberate licensing terms.
+
+# January, September 2004.
+#
+# It was noted that Intel IA-32 C compiler generates code which
+# performs ~30% *faster* on P4 CPU than original *hand-coded*
+# SHA1 assembler implementation. To address this problem (and
+# prove that humans are still better than machines:-), the
+# original code was overhauled, which resulted in following
+# performance changes:
+#
+#		compared with original	compared with Intel cc
+#		assembler impl.		generated code
+# Pentium	-16%			+48%
+# PIII/AMD	+8%			+16%
+# P4		+85%(!)			+45%
+#
+# As you can see Pentium came out as looser:-( Yet I reckoned that
+# improvement on P4 outweights the loss and incorporate this
+# re-tuned code to 0.9.7 and later.
+# ----------------------------------------------------------------
+#					
+
+# August 2009.
+#
+# George Spelvin has tipped that F_40_59(b,c,d) can be rewritten as
+# '(c&d) + (b&(c^d))', which allows to accumulate partial results
+# and lighten "pressure" on scratch registers. This resulted in
+# >12% performance improvement on contemporary AMD cores (with no
+# degradation on other CPUs:-). Also, the code was revised to maximize
+# "distance" between instructions producing input to 'lea' instruction
+# and the 'lea' instruction itself, which is essential for Intel Atom
+# core and resulted in ~15% improvement.
+
+# October 2010.
+#
+# Add SSSE3, Supplemental[!] SSE3, implementation. The idea behind it
+# is to offload message schedule denoted by Wt in NIST specification,
+# or Xupdate in OpenSSL source, to SIMD unit. The idea is not novel,
+# and in SSE2 context was first explored by Dean Gaudet in 2004, see
+# http://arctic.org/~dean/crypto/sha1.html. Since then several things
+# have changed that made it interesting again:
+#
+# a) XMM units became faster and wider;
+# b) instruction set became more versatile;
+# c) an important observation was made by Max Locktykhin, which made
+#    it possible to reduce amount of instructions required to perform
+#    the operation in question, for further details see
+#    http://software.intel.com/en-us/articles/improving-the-performance-of-the-secure-hash-algorithm-1/.
+
+# April 2011.
+#
+# Add AVX code path, probably most controversial... The thing is that
+# switch to AVX alone improves performance by as little as 4% in
+# comparison to SSSE3 code path. But below result doesn't look like
+# 4% improvement... Trouble is that Sandy Bridge decodes 'ro[rl]' as
+# pair of µ-ops, and it's the additional µ-ops, two per round, that
+# make it run slower than Core2 and Westmere. But 'sh[rl]d' is decoded
+# as single µ-op by Sandy Bridge and it's replacing 'ro[rl]' with
+# equivalent 'sh[rl]d' that is responsible for the impressive 5.1
+# cycles per processed byte. But 'sh[rl]d' is not something that used
+# to be fast, nor does it appear to be fast in upcoming Bulldozer
+# [according to its optimization manual]. Which is why AVX code path
+# is guarded by *both* AVX and synthetic bit denoting Intel CPUs.
+# One can argue that it's unfair to AMD, but without 'sh[rl]d' it
+# makes no sense to keep the AVX code path. If somebody feels that
+# strongly, it's probably more appropriate to discuss possibility of
+# using vector rotate XOP on AMD...
+
+# March 2014.
+#
+# Add support for Intel SHA Extensions.
+
+######################################################################
+# Current performance is summarized in following table. Numbers are
+# CPU clock cycles spent to process single byte (less is better).
+#
+#		x86		SSSE3		AVX
+# Pentium	15.7		-
+# PIII		11.5		-
+# P4		10.6		-
+# AMD K8	7.1		-
+# Core2		7.3		6.0/+22%	-
+# Westmere	7.3		5.5/+33%	-
+# Sandy Bridge	8.8		6.2/+40%	5.1(**)/+73%
+# Ivy Bridge	7.2		4.8/+51%	4.7(**)/+53%
+# Haswell	6.5		4.3/+51%	4.1(**)/+58%
+# Bulldozer	11.6		6.0/+92%
+# VIA Nano	10.6		7.5/+41%
+# Atom		12.5		9.3(*)/+35%
+# Silvermont	14.5		9.9(*)/+46%
+#
+# (*)	Loop is 1056 instructions long and expected result is ~8.25.
+#	The discrepancy is because of front-end limitations, so
+#	called MS-ROM penalties, and on Silvermont even rotate's
+#	limited parallelism.
+#
+# (**)	As per above comment, the result is for AVX *plus* sh[rl]d.
+
+$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+push(@INC,"${dir}","${dir}../../perlasm");
+require "x86asm.pl";
+
+$output=pop;
+open STDOUT,">$output";
+
+&asm_init($ARGV[0],"sha1-586.pl",$ARGV[$#ARGV] eq "386");
+
+$xmm=$ymm=0;
+for (@ARGV) { $xmm=1 if (/-DOPENSSL_IA32_SSE2/); }
+
+$ymm=1 if ($xmm &&
+		`$ENV{CC} -Wa,-v -c -o /dev/null -x assembler /dev/null 2>&1`
+			=~ /GNU assembler version ([2-9]\.[0-9]+)/ &&
+		$1>=2.19);	# first version supporting AVX
+
+$ymm=1 if ($xmm && !$ymm && $ARGV[0] eq "win32n" && 
+		`nasm -v 2>&1` =~ /NASM version ([2-9]\.[0-9]+)/ &&
+		$1>=2.03);	# first version supporting AVX
+
+$ymm=1 if ($xmm && !$ymm && $ARGV[0] eq "win32" &&
+		`ml 2>&1` =~ /Version ([0-9]+)\./ &&
+		$1>=10);	# first version supporting AVX
+
+$ymm=1 if ($xmm && !$ymm && `$ENV{CC} -v 2>&1` =~ /(^clang version|based on LLVM) ([3-9]\.[0-9]+)/ &&
+		$2>=3.0);	# first version supporting AVX
+
+$shaext=$xmm;	### set to zero if compiling for 1.0.1
+
+&external_label("OPENSSL_ia32cap_P") if ($xmm);
+
+
+$A="eax";
+$B="ebx";
+$C="ecx";
+$D="edx";
+$E="edi";
+$T="esi";
+$tmp1="ebp";
+
+@V=($A,$B,$C,$D,$E,$T);
+
+$alt=0;	# 1 denotes alternative IALU implementation, which performs
+	# 8% *worse* on P4, same on Westmere and Atom, 2% better on
+	# Sandy Bridge...
+
+sub BODY_00_15
+	{
+	local($n,$a,$b,$c,$d,$e,$f)=@_;
+
+	&comment("00_15 $n");
+
+	&mov($f,$c);			# f to hold F_00_19(b,c,d)
+	 if ($n==0)  { &mov($tmp1,$a); }
+	 else        { &mov($a,$tmp1); }
+	&rotl($tmp1,5);			# tmp1=ROTATE(a,5)
+	 &xor($f,$d);
+	&add($tmp1,$e);			# tmp1+=e;
+	 &mov($e,&swtmp($n%16));	# e becomes volatile and is loaded
+	 				# with xi, also note that e becomes
+					# f in next round...
+	&and($f,$b);
+	&rotr($b,2);			# b=ROTATE(b,30)
+	 &xor($f,$d);			# f holds F_00_19(b,c,d)
+	&lea($tmp1,&DWP(0x5a827999,$tmp1,$e));	# tmp1+=K_00_19+xi
+
+	if ($n==15) { &mov($e,&swtmp(($n+1)%16));# pre-fetch f for next round
+		      &add($f,$tmp1); }	# f+=tmp1
+	else        { &add($tmp1,$f); }	# f becomes a in next round
+	&mov($tmp1,$a)			if ($alt && $n==15);
+	}
+
+sub BODY_16_19
+	{
+	local($n,$a,$b,$c,$d,$e,$f)=@_;
+
+	&comment("16_19 $n");
+
+if ($alt) {
+	&xor($c,$d);
+	 &xor($f,&swtmp(($n+2)%16));	# f to hold Xupdate(xi,xa,xb,xc,xd)
+	&and($tmp1,$c);			# tmp1 to hold F_00_19(b,c,d), b&=c^d
+	 &xor($f,&swtmp(($n+8)%16));
+	&xor($tmp1,$d);			# tmp1=F_00_19(b,c,d)
+	 &xor($f,&swtmp(($n+13)%16));	# f holds xa^xb^xc^xd
+	&rotl($f,1);			# f=ROTATE(f,1)
+	 &add($e,$tmp1);		# e+=F_00_19(b,c,d)
+	&xor($c,$d);			# restore $c
+	 &mov($tmp1,$a);		# b in next round
+	&rotr($b,$n==16?2:7);		# b=ROTATE(b,30)
+	 &mov(&swtmp($n%16),$f);	# xi=f
+	&rotl($a,5);			# ROTATE(a,5)
+	 &lea($f,&DWP(0x5a827999,$f,$e));# f+=F_00_19(b,c,d)+e
+	&mov($e,&swtmp(($n+1)%16));	# pre-fetch f for next round
+	 &add($f,$a);			# f+=ROTATE(a,5)
+} else {
+	&mov($tmp1,$c);			# tmp1 to hold F_00_19(b,c,d)
+	 &xor($f,&swtmp(($n+2)%16));	# f to hold Xupdate(xi,xa,xb,xc,xd)
+	&xor($tmp1,$d);
+	 &xor($f,&swtmp(($n+8)%16));
+	&and($tmp1,$b);
+	 &xor($f,&swtmp(($n+13)%16));	# f holds xa^xb^xc^xd
+	&rotl($f,1);			# f=ROTATE(f,1)
+	 &xor($tmp1,$d);		# tmp1=F_00_19(b,c,d)
+	&add($e,$tmp1);			# e+=F_00_19(b,c,d)
+	 &mov($tmp1,$a);
+	&rotr($b,2);			# b=ROTATE(b,30)
+	 &mov(&swtmp($n%16),$f);	# xi=f
+	&rotl($tmp1,5);			# ROTATE(a,5)
+	 &lea($f,&DWP(0x5a827999,$f,$e));# f+=F_00_19(b,c,d)+e
+	&mov($e,&swtmp(($n+1)%16));	# pre-fetch f for next round
+	 &add($f,$tmp1);		# f+=ROTATE(a,5)
+}
+	}
+
+sub BODY_20_39
+	{
+	local($n,$a,$b,$c,$d,$e,$f)=@_;
+	local $K=($n<40)?0x6ed9eba1:0xca62c1d6;
+
+	&comment("20_39 $n");
+
+if ($alt) {
+	&xor($tmp1,$c);			# tmp1 to hold F_20_39(b,c,d), b^=c
+	 &xor($f,&swtmp(($n+2)%16));	# f to hold Xupdate(xi,xa,xb,xc,xd)
+	&xor($tmp1,$d);			# tmp1 holds F_20_39(b,c,d)
+	 &xor($f,&swtmp(($n+8)%16));
+	&add($e,$tmp1);			# e+=F_20_39(b,c,d)
+	 &xor($f,&swtmp(($n+13)%16));	# f holds xa^xb^xc^xd
+	&rotl($f,1);			# f=ROTATE(f,1)
+	 &mov($tmp1,$a);		# b in next round
+	&rotr($b,7);			# b=ROTATE(b,30)
+	 &mov(&swtmp($n%16),$f)		if($n<77);# xi=f
+	&rotl($a,5);			# ROTATE(a,5)
+	 &xor($b,$c)			if($n==39);# warm up for BODY_40_59
+	&and($tmp1,$b)			if($n==39);
+	 &lea($f,&DWP($K,$f,$e));	# f+=e+K_XX_YY
+	&mov($e,&swtmp(($n+1)%16))	if($n<79);# pre-fetch f for next round
+	 &add($f,$a);			# f+=ROTATE(a,5)
+	&rotr($a,5)			if ($n==79);
+} else {
+	&mov($tmp1,$b);			# tmp1 to hold F_20_39(b,c,d)
+	 &xor($f,&swtmp(($n+2)%16));	# f to hold Xupdate(xi,xa,xb,xc,xd)
+	&xor($tmp1,$c);
+	 &xor($f,&swtmp(($n+8)%16));
+	&xor($tmp1,$d);			# tmp1 holds F_20_39(b,c,d)
+	 &xor($f,&swtmp(($n+13)%16));	# f holds xa^xb^xc^xd
+	&rotl($f,1);			# f=ROTATE(f,1)
+	 &add($e,$tmp1);		# e+=F_20_39(b,c,d)
+	&rotr($b,2);			# b=ROTATE(b,30)
+	 &mov($tmp1,$a);
+	&rotl($tmp1,5);			# ROTATE(a,5)
+	 &mov(&swtmp($n%16),$f) if($n<77);# xi=f
+	&lea($f,&DWP($K,$f,$e));	# f+=e+K_XX_YY
+	 &mov($e,&swtmp(($n+1)%16)) if($n<79);# pre-fetch f for next round
+	&add($f,$tmp1);			# f+=ROTATE(a,5)
+}
+	}
+
+sub BODY_40_59
+	{
+	local($n,$a,$b,$c,$d,$e,$f)=@_;
+
+	&comment("40_59 $n");
+
+if ($alt) {
+	&add($e,$tmp1);			# e+=b&(c^d)
+	 &xor($f,&swtmp(($n+2)%16));	# f to hold Xupdate(xi,xa,xb,xc,xd)
+	&mov($tmp1,$d);
+	 &xor($f,&swtmp(($n+8)%16));
+	&xor($c,$d);			# restore $c
+	 &xor($f,&swtmp(($n+13)%16));	# f holds xa^xb^xc^xd
+	&rotl($f,1);			# f=ROTATE(f,1)
+	 &and($tmp1,$c);
+	&rotr($b,7);			# b=ROTATE(b,30)
+	 &add($e,$tmp1);		# e+=c&d
+	&mov($tmp1,$a);			# b in next round
+	 &mov(&swtmp($n%16),$f);	# xi=f
+	&rotl($a,5);			# ROTATE(a,5)
+	 &xor($b,$c)			if ($n<59);
+	&and($tmp1,$b)			if ($n<59);# tmp1 to hold F_40_59(b,c,d)
+	 &lea($f,&DWP(0x8f1bbcdc,$f,$e));# f+=K_40_59+e+(b&(c^d))
+	&mov($e,&swtmp(($n+1)%16));	# pre-fetch f for next round
+	 &add($f,$a);			# f+=ROTATE(a,5)
+} else {
+	&mov($tmp1,$c);			# tmp1 to hold F_40_59(b,c,d)
+	 &xor($f,&swtmp(($n+2)%16));	# f to hold Xupdate(xi,xa,xb,xc,xd)
+	&xor($tmp1,$d);
+	 &xor($f,&swtmp(($n+8)%16));
+	&and($tmp1,$b);
+	 &xor($f,&swtmp(($n+13)%16));	# f holds xa^xb^xc^xd
+	&rotl($f,1);			# f=ROTATE(f,1)
+	 &add($tmp1,$e);		# b&(c^d)+=e
+	&rotr($b,2);			# b=ROTATE(b,30)
+	 &mov($e,$a);			# e becomes volatile
+	&rotl($e,5);			# ROTATE(a,5)
+	 &mov(&swtmp($n%16),$f);	# xi=f
+	&lea($f,&DWP(0x8f1bbcdc,$f,$tmp1));# f+=K_40_59+e+(b&(c^d))
+	 &mov($tmp1,$c);
+	&add($f,$e);			# f+=ROTATE(a,5)
+	 &and($tmp1,$d);
+	&mov($e,&swtmp(($n+1)%16));	# pre-fetch f for next round
+	 &add($f,$tmp1);		# f+=c&d
+}
+	}
+
+&function_begin("sha1_block_data_order");
+if ($xmm) {
+  &static_label("shaext_shortcut")	if ($shaext);
+  &static_label("ssse3_shortcut");
+  &static_label("avx_shortcut")		if ($ymm);
+  &static_label("K_XX_XX");
+
+	&call	(&label("pic_point"));	# make it PIC!
+  &set_label("pic_point");
+	&blindpop($tmp1);
+	&picmeup($T,"OPENSSL_ia32cap_P",$tmp1,&label("pic_point"));
+	&lea	($tmp1,&DWP(&label("K_XX_XX")."-".&label("pic_point"),$tmp1));
+
+	&mov	($A,&DWP(0,$T));
+	&mov	($D,&DWP(4,$T));
+	&test	($D,1<<9);		# check SSSE3 bit
+	&jz	(&label("x86"));
+	&mov	($C,&DWP(8,$T));
+	&test	($A,1<<24);		# check FXSR bit
+	&jz	(&label("x86"));
+	if ($shaext) {
+		&test	($C,1<<29);		# check SHA bit
+		&jnz	(&label("shaext_shortcut"));
+	}
+	if ($ymm) {
+		&and	($D,1<<28);		# mask AVX bit
+		&and	($A,1<<30);		# mask "Intel CPU" bit
+		&or	($A,$D);
+		&cmp	($A,1<<28|1<<30);
+		&je	(&label("avx_shortcut"));
+	}
+	&jmp	(&label("ssse3_shortcut"));
+  &set_label("x86",16);
+}
+	&mov($tmp1,&wparam(0));	# SHA_CTX *c
+	&mov($T,&wparam(1));	# const void *input
+	&mov($A,&wparam(2));	# size_t num
+	&stack_push(16+3);	# allocate X[16]
+	&shl($A,6);
+	&add($A,$T);
+	&mov(&wparam(2),$A);	# pointer beyond the end of input
+	&mov($E,&DWP(16,$tmp1));# pre-load E
+	&jmp(&label("loop"));
+
+&set_label("loop",16);
+
+	# copy input chunk to X, but reversing byte order!
+	for ($i=0; $i<16; $i+=4)
+		{
+		&mov($A,&DWP(4*($i+0),$T));
+		&mov($B,&DWP(4*($i+1),$T));
+		&mov($C,&DWP(4*($i+2),$T));
+		&mov($D,&DWP(4*($i+3),$T));
+		&bswap($A);
+		&bswap($B);
+		&bswap($C);
+		&bswap($D);
+		&mov(&swtmp($i+0),$A);
+		&mov(&swtmp($i+1),$B);
+		&mov(&swtmp($i+2),$C);
+		&mov(&swtmp($i+3),$D);
+		}
+	&mov(&wparam(1),$T);	# redundant in 1st spin
+
+	&mov($A,&DWP(0,$tmp1));	# load SHA_CTX
+	&mov($B,&DWP(4,$tmp1));
+	&mov($C,&DWP(8,$tmp1));
+	&mov($D,&DWP(12,$tmp1));
+	# E is pre-loaded
+
+	for($i=0;$i<16;$i++)	{ &BODY_00_15($i,@V); unshift(@V,pop(@V)); }
+	for(;$i<20;$i++)	{ &BODY_16_19($i,@V); unshift(@V,pop(@V)); }
+	for(;$i<40;$i++)	{ &BODY_20_39($i,@V); unshift(@V,pop(@V)); }
+	for(;$i<60;$i++)	{ &BODY_40_59($i,@V); unshift(@V,pop(@V)); }
+	for(;$i<80;$i++)	{ &BODY_20_39($i,@V); unshift(@V,pop(@V)); }
+
+	(($V[5] eq $D) and ($V[0] eq $E)) or die;	# double-check
+
+	&mov($tmp1,&wparam(0));	# re-load SHA_CTX*
+	&mov($D,&wparam(1));	# D is last "T" and is discarded
+
+	&add($E,&DWP(0,$tmp1));	# E is last "A"...
+	&add($T,&DWP(4,$tmp1));
+	&add($A,&DWP(8,$tmp1));
+	&add($B,&DWP(12,$tmp1));
+	&add($C,&DWP(16,$tmp1));
+
+	&mov(&DWP(0,$tmp1),$E);	# update SHA_CTX
+	 &add($D,64);		# advance input pointer
+	&mov(&DWP(4,$tmp1),$T);
+	 &cmp($D,&wparam(2));	# have we reached the end yet?
+	&mov(&DWP(8,$tmp1),$A);
+	 &mov($E,$C);		# C is last "E" which needs to be "pre-loaded"
+	&mov(&DWP(12,$tmp1),$B);
+	 &mov($T,$D);		# input pointer
+	&mov(&DWP(16,$tmp1),$C);
+	&jb(&label("loop"));
+
+	&stack_pop(16+3);
+&function_end("sha1_block_data_order");
+
+if ($xmm) {
+if ($shaext) {
+######################################################################
+# Intel SHA Extensions implementation of SHA1 update function.
+#
+my ($ctx,$inp,$num)=("edi","esi","ecx");
+my ($ABCD,$E,$E_,$BSWAP)=map("xmm$_",(0..3));
+my @MSG=map("xmm$_",(4..7));
+
+sub sha1rnds4 {
+ my ($dst,$src,$imm)=@_;
+    if ("$dst:$src" =~ /xmm([0-7]):xmm([0-7])/)
+    {	&data_byte(0x0f,0x3a,0xcc,0xc0|($1<<3)|$2,$imm);	}
+}
+sub sha1op38 {
+ my ($opcodelet,$dst,$src)=@_;
+    if ("$dst:$src" =~ /xmm([0-7]):xmm([0-7])/)
+    {	&data_byte(0x0f,0x38,$opcodelet,0xc0|($1<<3)|$2);	}
+}
+sub sha1nexte	{ sha1op38(0xc8,@_); }
+sub sha1msg1	{ sha1op38(0xc9,@_); }
+sub sha1msg2	{ sha1op38(0xca,@_); }
+
+&function_begin("_sha1_block_data_order_shaext");
+	&call	(&label("pic_point"));	# make it PIC!
+	&set_label("pic_point");
+	&blindpop($tmp1);
+	&lea	($tmp1,&DWP(&label("K_XX_XX")."-".&label("pic_point"),$tmp1));
+&set_label("shaext_shortcut");
+	&mov	($ctx,&wparam(0));
+	&mov	("ebx","esp");
+	&mov	($inp,&wparam(1));
+	&mov	($num,&wparam(2));
+	&sub	("esp",32);
+
+	&movdqu	($ABCD,&QWP(0,$ctx));
+	&movd	($E,&DWP(16,$ctx));
+	&and	("esp",-32);
+	&movdqa	($BSWAP,&QWP(0x50,$tmp1));	# byte-n-word swap
+
+	&movdqu	(@MSG[0],&QWP(0,$inp));
+	&pshufd	($ABCD,$ABCD,0b00011011);	# flip word order
+	&movdqu	(@MSG[1],&QWP(0x10,$inp));
+	&pshufd	($E,$E,0b00011011);		# flip word order
+	&movdqu	(@MSG[2],&QWP(0x20,$inp));
+	&pshufb	(@MSG[0],$BSWAP);
+	&movdqu	(@MSG[3],&QWP(0x30,$inp));
+	&pshufb	(@MSG[1],$BSWAP);
+	&pshufb	(@MSG[2],$BSWAP);
+	&pshufb	(@MSG[3],$BSWAP);
+	&jmp	(&label("loop_shaext"));
+
+&set_label("loop_shaext",16);
+	&dec		($num);
+	&lea		("eax",&DWP(0x40,$inp));
+	&movdqa		(&QWP(0,"esp"),$E);	# offload $E
+	&paddd		($E,@MSG[0]);
+	&cmovne		($inp,"eax");
+	&movdqa		(&QWP(16,"esp"),$ABCD);	# offload $ABCD
+
+for($i=0;$i<20-4;$i+=2) {
+	&sha1msg1	(@MSG[0],@MSG[1]);
+	&movdqa		($E_,$ABCD);
+	&sha1rnds4	($ABCD,$E,int($i/5));	# 0-3...
+	&sha1nexte	($E_,@MSG[1]);
+	&pxor		(@MSG[0],@MSG[2]);
+	&sha1msg1	(@MSG[1],@MSG[2]);
+	&sha1msg2	(@MSG[0],@MSG[3]);
+
+	&movdqa		($E,$ABCD);
+	&sha1rnds4	($ABCD,$E_,int(($i+1)/5));
+	&sha1nexte	($E,@MSG[2]);
+	&pxor		(@MSG[1],@MSG[3]);
+	&sha1msg2	(@MSG[1],@MSG[0]);
+
+	push(@MSG,shift(@MSG));	push(@MSG,shift(@MSG));
+}
+	&movdqu		(@MSG[0],&QWP(0,$inp));
+	&movdqa		($E_,$ABCD);
+	&sha1rnds4	($ABCD,$E,3);		# 64-67
+	&sha1nexte	($E_,@MSG[1]);
+	&movdqu		(@MSG[1],&QWP(0x10,$inp));
+	&pshufb		(@MSG[0],$BSWAP);
+
+	&movdqa		($E,$ABCD);
+	&sha1rnds4	($ABCD,$E_,3);		# 68-71
+	&sha1nexte	($E,@MSG[2]);
+	&movdqu		(@MSG[2],&QWP(0x20,$inp));
+	&pshufb		(@MSG[1],$BSWAP);
+
+	&movdqa		($E_,$ABCD);
+	&sha1rnds4	($ABCD,$E,3);		# 72-75
+	&sha1nexte	($E_,@MSG[3]);
+	&movdqu		(@MSG[3],&QWP(0x30,$inp));
+	&pshufb		(@MSG[2],$BSWAP);
+
+	&movdqa		($E,$ABCD);
+	&sha1rnds4	($ABCD,$E_,3);		# 76-79
+	&movdqa		($E_,&QWP(0,"esp"));
+	&pshufb		(@MSG[3],$BSWAP);
+	&sha1nexte	($E,$E_);
+	&paddd		($ABCD,&QWP(16,"esp"));
+
+	&jnz		(&label("loop_shaext"));
+
+	&pshufd	($ABCD,$ABCD,0b00011011);
+	&pshufd	($E,$E,0b00011011);
+	&movdqu	(&QWP(0,$ctx),$ABCD)
+	&movd	(&DWP(16,$ctx),$E);
+	&mov	("esp","ebx");
+&function_end("_sha1_block_data_order_shaext");
+}
+######################################################################
+# The SSSE3 implementation.
+#
+# %xmm[0-7] are used as ring @X[] buffer containing quadruples of last
+# 32 elements of the message schedule or Xupdate outputs. First 4
+# quadruples are simply byte-swapped input, next 4 are calculated
+# according to method originally suggested by Dean Gaudet (modulo
+# being implemented in SSSE3). Once 8 quadruples or 32 elements are
+# collected, it switches to routine proposed by Max Locktyukhin.
+#
+# Calculations inevitably require temporary reqisters, and there are
+# no %xmm registers left to spare. For this reason part of the ring
+# buffer, X[2..4] to be specific, is offloaded to 3 quadriples ring
+# buffer on the stack. Keep in mind that X[2] is alias X[-6], X[3] -
+# X[-5], and X[4] - X[-4]...
+#
+# Another notable optimization is aggressive stack frame compression
+# aiming to minimize amount of 9-byte instructions...
+#
+# Yet another notable optimization is "jumping" $B variable. It means
+# that there is no register permanently allocated for $B value. This
+# allowed to eliminate one instruction from body_20_39...
+#
+my $Xi=4;			# 4xSIMD Xupdate round, start pre-seeded
+my @X=map("xmm$_",(4..7,0..3));	# pre-seeded for $Xi=4
+my @V=($A,$B,$C,$D,$E);
+my $j=0;			# hash round
+my $rx=0;
+my @T=($T,$tmp1);
+my $inp;
+
+my $_rol=sub { &rol(@_) };
+my $_ror=sub { &ror(@_) };
+
+&function_begin("_sha1_block_data_order_ssse3");
+	&call	(&label("pic_point"));	# make it PIC!
+	&set_label("pic_point");
+	&blindpop($tmp1);
+	&lea	($tmp1,&DWP(&label("K_XX_XX")."-".&label("pic_point"),$tmp1));
+&set_label("ssse3_shortcut");
+
+	&movdqa	(@X[3],&QWP(0,$tmp1));		# K_00_19
+	&movdqa	(@X[4],&QWP(16,$tmp1));		# K_20_39
+	&movdqa	(@X[5],&QWP(32,$tmp1));		# K_40_59
+	&movdqa	(@X[6],&QWP(48,$tmp1));		# K_60_79
+	&movdqa	(@X[2],&QWP(64,$tmp1));		# pbswap mask
+
+	&mov	($E,&wparam(0));		# load argument block
+	&mov	($inp=@T[1],&wparam(1));
+	&mov	($D,&wparam(2));
+	&mov	(@T[0],"esp");
+
+	# stack frame layout
+	#
+	# +0	X[0]+K	X[1]+K	X[2]+K	X[3]+K	# XMM->IALU xfer area
+	#	X[4]+K	X[5]+K	X[6]+K	X[7]+K
+	#	X[8]+K	X[9]+K	X[10]+K	X[11]+K
+	#	X[12]+K	X[13]+K	X[14]+K	X[15]+K
+	#
+	# +64	X[0]	X[1]	X[2]	X[3]	# XMM->XMM backtrace area
+	#	X[4]	X[5]	X[6]	X[7]
+	#	X[8]	X[9]	X[10]	X[11]	# even borrowed for K_00_19
+	#
+	# +112	K_20_39	K_20_39	K_20_39	K_20_39	# constants
+	#	K_40_59	K_40_59	K_40_59	K_40_59
+	#	K_60_79	K_60_79	K_60_79	K_60_79
+	#	K_00_19	K_00_19	K_00_19	K_00_19
+	#	pbswap mask
+	#
+	# +192	ctx				# argument block
+	# +196	inp
+	# +200	end
+	# +204	esp
+	&sub	("esp",208);
+	&and	("esp",-64);
+
+	&movdqa	(&QWP(112+0,"esp"),@X[4]);	# copy constants
+	&movdqa	(&QWP(112+16,"esp"),@X[5]);
+	&movdqa	(&QWP(112+32,"esp"),@X[6]);
+	&shl	($D,6);				# len*64
+	&movdqa	(&QWP(112+48,"esp"),@X[3]);
+	&add	($D,$inp);			# end of input
+	&movdqa	(&QWP(112+64,"esp"),@X[2]);
+	&add	($inp,64);
+	&mov	(&DWP(192+0,"esp"),$E);		# save argument block
+	&mov	(&DWP(192+4,"esp"),$inp);
+	&mov	(&DWP(192+8,"esp"),$D);
+	&mov	(&DWP(192+12,"esp"),@T[0]);	# save original %esp
+
+	&mov	($A,&DWP(0,$E));		# load context
+	&mov	($B,&DWP(4,$E));
+	&mov	($C,&DWP(8,$E));
+	&mov	($D,&DWP(12,$E));
+	&mov	($E,&DWP(16,$E));
+	&mov	(@T[0],$B);			# magic seed
+
+	&movdqu	(@X[-4&7],&QWP(-64,$inp));	# load input to %xmm[0-3]
+	&movdqu	(@X[-3&7],&QWP(-48,$inp));
+	&movdqu	(@X[-2&7],&QWP(-32,$inp));
+	&movdqu	(@X[-1&7],&QWP(-16,$inp));
+	&pshufb	(@X[-4&7],@X[2]);		# byte swap
+	&pshufb	(@X[-3&7],@X[2]);
+	&pshufb	(@X[-2&7],@X[2]);
+	&movdqa	(&QWP(112-16,"esp"),@X[3]);	# borrow last backtrace slot
+	&pshufb	(@X[-1&7],@X[2]);
+	&paddd	(@X[-4&7],@X[3]);		# add K_00_19
+	&paddd	(@X[-3&7],@X[3]);
+	&paddd	(@X[-2&7],@X[3]);
+	&movdqa	(&QWP(0,"esp"),@X[-4&7]);	# X[]+K xfer to IALU
+	&psubd	(@X[-4&7],@X[3]);		# restore X[]
+	&movdqa	(&QWP(0+16,"esp"),@X[-3&7]);
+	&psubd	(@X[-3&7],@X[3]);
+	&movdqa	(&QWP(0+32,"esp"),@X[-2&7]);
+	&mov	(@T[1],$C);
+	&psubd	(@X[-2&7],@X[3]);
+	&xor	(@T[1],$D);
+	&pshufd	(@X[0],@X[-4&7],0xee);		# was &movdqa	(@X[0],@X[-3&7]);
+	&and	(@T[0],@T[1]);
+	&jmp	(&label("loop"));
+
+######################################################################
+# SSE instruction sequence is first broken to groups of indepentent
+# instructions, independent in respect to their inputs and shifter
+# (not all architectures have more than one). Then IALU instructions
+# are "knitted in" between the SSE groups. Distance is maintained for
+# SSE latency of 2 in hope that it fits better upcoming AMD Bulldozer
+# [which allegedly also implements SSSE3]...
+#
+# Temporary registers usage. X[2] is volatile at the entry and at the
+# end is restored from backtrace ring buffer. X[3] is expected to
+# contain current K_XX_XX constant and is used to caclulate X[-1]+K
+# from previous round, it becomes volatile the moment the value is
+# saved to stack for transfer to IALU. X[4] becomes volatile whenever
+# X[-4] is accumulated and offloaded to backtrace ring buffer, at the
+# end it is loaded with next K_XX_XX [which becomes X[3] in next
+# round]...
+#
+sub Xupdate_ssse3_16_31()		# recall that $Xi starts wtih 4
+{ use integer;
+  my $body = shift;
+  my @insns = (&$body,&$body,&$body,&$body);	# 40 instructions
+  my ($a,$b,$c,$d,$e);
+
+	 eval(shift(@insns));		# ror
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&punpcklqdq(@X[0],@X[-3&7]);	# compose "X[-14]" in "X[0]", was &palignr(@X[0],@X[-4&7],8);
+	&movdqa	(@X[2],@X[-1&7]);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	  &paddd	(@X[3],@X[-1&7]);
+	  &movdqa	(&QWP(64+16*(($Xi-4)%3),"esp"),@X[-4&7]);# save X[] to backtrace buffer
+	 eval(shift(@insns));		# rol
+	 eval(shift(@insns));
+	&psrldq	(@X[2],4);		# "X[-3]", 3 dwords
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&pxor	(@X[0],@X[-4&7]);	# "X[0]"^="X[-16]"
+	 eval(shift(@insns));
+	 eval(shift(@insns));		# ror
+
+	&pxor	(@X[2],@X[-2&7]);	# "X[-3]"^"X[-8]"
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	&pxor	(@X[0],@X[2]);		# "X[0]"^="X[-3]"^"X[-8]"
+	 eval(shift(@insns));
+	 eval(shift(@insns));		# rol
+	  &movdqa	(&QWP(0+16*(($Xi-1)&3),"esp"),@X[3]);	# X[]+K xfer to IALU
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	&movdqa	(@X[4],@X[0]);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));		# ror
+	&movdqa (@X[2],@X[0]);
+	 eval(shift(@insns));
+
+	&pslldq	(@X[4],12);		# "X[0]"<<96, extract one dword
+	&paddd	(@X[0],@X[0]);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	&psrld	(@X[2],31);
+	 eval(shift(@insns));
+	 eval(shift(@insns));		# rol
+	&movdqa	(@X[3],@X[4]);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	&psrld	(@X[4],30);
+	 eval(shift(@insns));
+	 eval(shift(@insns));		# ror
+	&por	(@X[0],@X[2]);		# "X[0]"<<<=1
+	 eval(shift(@insns));
+	  &movdqa	(@X[2],&QWP(64+16*(($Xi-6)%3),"esp")) if ($Xi>5);	# restore X[] from backtrace buffer
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	&pslld	(@X[3],2);
+	 eval(shift(@insns));
+	 eval(shift(@insns));		# rol
+	&pxor   (@X[0],@X[4]);
+	  &movdqa	(@X[4],&QWP(112-16+16*(($Xi)/5),"esp"));	# K_XX_XX
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	&pxor	(@X[0],@X[3]);		# "X[0]"^=("X[0]"<<96)<<<2
+	  &pshufd	(@X[1],@X[-3&7],0xee)	if ($Xi<7);	# was &movdqa	(@X[1],@X[-2&7])
+	  &pshufd	(@X[3],@X[-1&7],0xee)	if ($Xi==7);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	 foreach (@insns) { eval; }	# remaining instructions [if any]
+
+  $Xi++;	push(@X,shift(@X));	# "rotate" X[]
+}
+
+sub Xupdate_ssse3_32_79()
+{ use integer;
+  my $body = shift;
+  my @insns = (&$body,&$body,&$body,&$body);	# 32 to 44 instructions
+  my ($a,$b,$c,$d,$e);
+
+	 eval(shift(@insns));		# body_20_39
+	&pxor	(@X[0],@X[-4&7]);	# "X[0]"="X[-32]"^"X[-16]"
+	&punpcklqdq(@X[2],@X[-1&7]);	# compose "X[-6]", was &palignr(@X[2],@X[-2&7],8)
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));		# rol
+
+	&pxor	(@X[0],@X[-7&7]);	# "X[0]"^="X[-28]"
+	  &movdqa	(&QWP(64+16*(($Xi-4)%3),"esp"),@X[-4&7]);	# save X[] to backtrace buffer
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns))		if (@insns[0] =~ /_rol/);
+	 if ($Xi%5) {
+	  &movdqa	(@X[4],@X[3]);	# "perpetuate" K_XX_XX...
+	 } else {			# ... or load next one
+	  &movdqa	(@X[4],&QWP(112-16+16*($Xi/5),"esp"));
+	 }
+	 eval(shift(@insns));		# ror
+	  &paddd	(@X[3],@X[-1&7]);
+	 eval(shift(@insns));
+
+	&pxor	(@X[0],@X[2]);		# "X[0]"^="X[-6]"
+	 eval(shift(@insns));		# body_20_39
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));		# rol
+
+	&movdqa	(@X[2],@X[0]);
+	  &movdqa	(&QWP(0+16*(($Xi-1)&3),"esp"),@X[3]);	# X[]+K xfer to IALU
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));		# ror
+	 eval(shift(@insns));
+	 eval(shift(@insns))		if (@insns[0] =~ /_rol/);
+
+	&pslld	(@X[0],2);
+	 eval(shift(@insns));		# body_20_39
+	 eval(shift(@insns));
+	&psrld	(@X[2],30);
+	 eval(shift(@insns));
+	 eval(shift(@insns));		# rol
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));		# ror
+	 eval(shift(@insns));
+	 eval(shift(@insns))		if (@insns[1] =~ /_rol/);
+	 eval(shift(@insns))		if (@insns[0] =~ /_rol/);
+
+	&por	(@X[0],@X[2]);		# "X[0]"<<<=2
+	 eval(shift(@insns));		# body_20_39
+	 eval(shift(@insns));
+	  &movdqa	(@X[2],&QWP(64+16*(($Xi-6)%3),"esp")) if($Xi<19);	# restore X[] from backtrace buffer
+	 eval(shift(@insns));
+	 eval(shift(@insns));		# rol
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));		# ror
+	  &pshufd	(@X[3],@X[-1],0xee)	if ($Xi<19);	# was &movdqa	(@X[3],@X[0])
+	 eval(shift(@insns));
+
+	 foreach (@insns) { eval; }	# remaining instructions
+
+  $Xi++;	push(@X,shift(@X));	# "rotate" X[]
+}
+
+sub Xuplast_ssse3_80()
+{ use integer;
+  my $body = shift;
+  my @insns = (&$body,&$body,&$body,&$body);	# 32 instructions
+  my ($a,$b,$c,$d,$e);
+
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	  &paddd	(@X[3],@X[-1&7]);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	  &movdqa	(&QWP(0+16*(($Xi-1)&3),"esp"),@X[3]);	# X[]+K xfer IALU
+
+	 foreach (@insns) { eval; }		# remaining instructions
+
+	&mov	($inp=@T[1],&DWP(192+4,"esp"));
+	&cmp	($inp,&DWP(192+8,"esp"));
+	&je	(&label("done"));
+
+	&movdqa	(@X[3],&QWP(112+48,"esp"));	# K_00_19
+	&movdqa	(@X[2],&QWP(112+64,"esp"));	# pbswap mask
+	&movdqu	(@X[-4&7],&QWP(0,$inp));	# load input
+	&movdqu	(@X[-3&7],&QWP(16,$inp));
+	&movdqu	(@X[-2&7],&QWP(32,$inp));
+	&movdqu	(@X[-1&7],&QWP(48,$inp));
+	&add	($inp,64);
+	&pshufb	(@X[-4&7],@X[2]);		# byte swap
+	&mov	(&DWP(192+4,"esp"),$inp);
+	&movdqa	(&QWP(112-16,"esp"),@X[3]);	# borrow last backtrace slot
+
+  $Xi=0;
+}
+
+sub Xloop_ssse3()
+{ use integer;
+  my $body = shift;
+  my @insns = (&$body,&$body,&$body,&$body);	# 32 instructions
+  my ($a,$b,$c,$d,$e);
+
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&pshufb	(@X[($Xi-3)&7],@X[2]);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&paddd	(@X[($Xi-4)&7],@X[3]);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&movdqa	(&QWP(0+16*$Xi,"esp"),@X[($Xi-4)&7]);	# X[]+K xfer to IALU
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&psubd	(@X[($Xi-4)&7],@X[3]);
+
+	foreach (@insns) { eval; }
+  $Xi++;
+}
+
+sub Xtail_ssse3()
+{ use integer;
+  my $body = shift;
+  my @insns = (&$body,&$body,&$body,&$body);	# 32 instructions
+  my ($a,$b,$c,$d,$e);
+
+	foreach (@insns) { eval; }
+}
+
+sub body_00_19 () {	# ((c^d)&b)^d
+	# on start @T[0]=(c^d)&b
+	return &body_20_39()	if ($rx==19);	$rx++;
+	(
+	'($a,$b,$c,$d,$e)=@V;'.
+	'&$_ror	($b,$j?7:2);',	# $b>>>2
+	'&xor	(@T[0],$d);',
+	'&mov	(@T[1],$a);',	# $b in next round
+
+	'&add	($e,&DWP(4*($j&15),"esp"));',	# X[]+K xfer
+	'&xor	($b,$c);',	# $c^$d for next round
+
+	'&$_rol	($a,5);',
+	'&add	($e,@T[0]);',
+	'&and	(@T[1],$b);',	# ($b&($c^$d)) for next round
+
+	'&xor	($b,$c);',	# restore $b
+	'&add	($e,$a);'	.'$j++; unshift(@V,pop(@V)); unshift(@T,pop(@T));'
+	);
+}
+
+sub body_20_39 () {	# b^d^c
+	# on entry @T[0]=b^d
+	return &body_40_59()	if ($rx==39);	$rx++;
+	(
+	'($a,$b,$c,$d,$e)=@V;'.
+	'&add	($e,&DWP(4*($j&15),"esp"));',	# X[]+K xfer
+	'&xor	(@T[0],$d)	if($j==19);'.
+	'&xor	(@T[0],$c)	if($j> 19);',	# ($b^$d^$c)
+	'&mov	(@T[1],$a);',	# $b in next round
+
+	'&$_rol	($a,5);',
+	'&add	($e,@T[0]);',
+	'&xor	(@T[1],$c)	if ($j< 79);',	# $b^$d for next round
+
+	'&$_ror	($b,7);',	# $b>>>2
+	'&add	($e,$a);'	.'$j++; unshift(@V,pop(@V)); unshift(@T,pop(@T));'
+	);
+}
+
+sub body_40_59 () {	# ((b^c)&(c^d))^c
+	# on entry @T[0]=(b^c), (c^=d)
+	$rx++;
+	(
+	'($a,$b,$c,$d,$e)=@V;'.
+	'&add	($e,&DWP(4*($j&15),"esp"));',	# X[]+K xfer
+	'&and	(@T[0],$c)	if ($j>=40);',	# (b^c)&(c^d)
+	'&xor	($c,$d)		if ($j>=40);',	# restore $c
+
+	'&$_ror	($b,7);',	# $b>>>2
+	'&mov	(@T[1],$a);',	# $b for next round
+	'&xor	(@T[0],$c);',
+
+	'&$_rol	($a,5);',
+	'&add	($e,@T[0]);',
+	'&xor	(@T[1],$c)	if ($j==59);'.
+	'&xor	(@T[1],$b)	if ($j< 59);',	# b^c for next round
+
+	'&xor	($b,$c)		if ($j< 59);',	# c^d for next round
+	'&add	($e,$a);'	.'$j++; unshift(@V,pop(@V)); unshift(@T,pop(@T));'
+	);
+}
+######
+sub bodyx_00_19 () {	# ((c^d)&b)^d
+	# on start @T[0]=(b&c)^(~b&d), $e+=X[]+K
+	return &bodyx_20_39()	if ($rx==19);	$rx++;
+	(
+	'($a,$b,$c,$d,$e)=@V;'.
+
+	'&rorx	($b,$b,2)			if ($j==0);'.	# $b>>>2
+	'&rorx	($b,@T[1],7)			if ($j!=0);',	# $b>>>2
+	'&lea	($e,&DWP(0,$e,@T[0]));',
+	'&rorx	(@T[0],$a,5);',
+
+	'&andn	(@T[1],$a,$c);',
+	'&and	($a,$b)',
+	'&add	($d,&DWP(4*(($j+1)&15),"esp"));',	# X[]+K xfer
+
+	'&xor	(@T[1],$a)',
+	'&add	($e,@T[0]);'	.'$j++; unshift(@V,pop(@V)); unshift(@T,pop(@T));'
+	);
+}
+
+sub bodyx_20_39 () {	# b^d^c
+	# on start $b=b^c^d
+	return &bodyx_40_59()	if ($rx==39);	$rx++;
+	(
+	'($a,$b,$c,$d,$e)=@V;'.
+
+	'&add	($e,($j==19?@T[0]:$b))',
+	'&rorx	($b,@T[1],7);',	# $b>>>2
+	'&rorx	(@T[0],$a,5);',
+
+	'&xor	($a,$b)				if ($j<79);',
+	'&add	($d,&DWP(4*(($j+1)&15),"esp"))	if ($j<79);',	# X[]+K xfer
+	'&xor	($a,$c)				if ($j<79);',
+	'&add	($e,@T[0]);'	.'$j++; unshift(@V,pop(@V)); unshift(@T,pop(@T));'
+	);
+}
+
+sub bodyx_40_59 () {	# ((b^c)&(c^d))^c
+	# on start $b=((b^c)&(c^d))^c
+	return &bodyx_20_39()	if ($rx==59);	$rx++;
+	(
+	'($a,$b,$c,$d,$e)=@V;'.
+
+	'&rorx	(@T[0],$a,5)',
+	'&lea	($e,&DWP(0,$e,$b))',
+	'&rorx	($b,@T[1],7)',	# $b>>>2
+	'&add	($d,&DWP(4*(($j+1)&15),"esp"))',	# X[]+K xfer
+
+	'&mov	(@T[1],$c)',
+	'&xor	($a,$b)',	# b^c for next round
+	'&xor	(@T[1],$b)',	# c^d for next round
+
+	'&and	($a,@T[1])',
+	'&add	($e,@T[0])',
+	'&xor	($a,$b)'	.'$j++; unshift(@V,pop(@V)); unshift(@T,pop(@T));'
+	);
+}
+
+&set_label("loop",16);
+	&Xupdate_ssse3_16_31(\&body_00_19);
+	&Xupdate_ssse3_16_31(\&body_00_19);
+	&Xupdate_ssse3_16_31(\&body_00_19);
+	&Xupdate_ssse3_16_31(\&body_00_19);
+	&Xupdate_ssse3_32_79(\&body_00_19);
+	&Xupdate_ssse3_32_79(\&body_20_39);
+	&Xupdate_ssse3_32_79(\&body_20_39);
+	&Xupdate_ssse3_32_79(\&body_20_39);
+	&Xupdate_ssse3_32_79(\&body_20_39);
+	&Xupdate_ssse3_32_79(\&body_20_39);
+	&Xupdate_ssse3_32_79(\&body_40_59);
+	&Xupdate_ssse3_32_79(\&body_40_59);
+	&Xupdate_ssse3_32_79(\&body_40_59);
+	&Xupdate_ssse3_32_79(\&body_40_59);
+	&Xupdate_ssse3_32_79(\&body_40_59);
+	&Xupdate_ssse3_32_79(\&body_20_39);
+	&Xuplast_ssse3_80(\&body_20_39);	# can jump to "done"
+
+				$saved_j=$j; @saved_V=@V;
+
+	&Xloop_ssse3(\&body_20_39);
+	&Xloop_ssse3(\&body_20_39);
+	&Xloop_ssse3(\&body_20_39);
+
+	&mov	(@T[1],&DWP(192,"esp"));	# update context
+	&add	($A,&DWP(0,@T[1]));
+	&add	(@T[0],&DWP(4,@T[1]));		# $b
+	&add	($C,&DWP(8,@T[1]));
+	&mov	(&DWP(0,@T[1]),$A);
+	&add	($D,&DWP(12,@T[1]));
+	&mov	(&DWP(4,@T[1]),@T[0]);
+	&add	($E,&DWP(16,@T[1]));
+	&mov	(&DWP(8,@T[1]),$C);
+	&mov	($B,$C);
+	&mov	(&DWP(12,@T[1]),$D);
+	&xor	($B,$D);
+	&mov	(&DWP(16,@T[1]),$E);
+	&mov	(@T[1],@T[0]);
+	&pshufd	(@X[0],@X[-4&7],0xee);		# was &movdqa	(@X[0],@X[-3&7]);
+	&and	(@T[0],$B);
+	&mov	($B,$T[1]);
+
+	&jmp	(&label("loop"));
+
+&set_label("done",16);		$j=$saved_j; @V=@saved_V;
+
+	&Xtail_ssse3(\&body_20_39);
+	&Xtail_ssse3(\&body_20_39);
+	&Xtail_ssse3(\&body_20_39);
+
+	&mov	(@T[1],&DWP(192,"esp"));	# update context
+	&add	($A,&DWP(0,@T[1]));
+	&mov	("esp",&DWP(192+12,"esp"));	# restore %esp
+	&add	(@T[0],&DWP(4,@T[1]));		# $b
+	&add	($C,&DWP(8,@T[1]));
+	&mov	(&DWP(0,@T[1]),$A);
+	&add	($D,&DWP(12,@T[1]));
+	&mov	(&DWP(4,@T[1]),@T[0]);
+	&add	($E,&DWP(16,@T[1]));
+	&mov	(&DWP(8,@T[1]),$C);
+	&mov	(&DWP(12,@T[1]),$D);
+	&mov	(&DWP(16,@T[1]),$E);
+
+&function_end("_sha1_block_data_order_ssse3");
+
+$rx=0;	# reset
+
+if ($ymm) {
+my $Xi=4;			# 4xSIMD Xupdate round, start pre-seeded
+my @X=map("xmm$_",(4..7,0..3));	# pre-seeded for $Xi=4
+my @V=($A,$B,$C,$D,$E);
+my $j=0;			# hash round
+my @T=($T,$tmp1);
+my $inp;
+
+my $_rol=sub { &shld(@_[0],@_) };
+my $_ror=sub { &shrd(@_[0],@_) };
+
+&function_begin("_sha1_block_data_order_avx");
+	&call	(&label("pic_point"));	# make it PIC!
+	&set_label("pic_point");
+	&blindpop($tmp1);
+	&lea	($tmp1,&DWP(&label("K_XX_XX")."-".&label("pic_point"),$tmp1));
+&set_label("avx_shortcut");
+	&vzeroall();
+
+	&vmovdqa(@X[3],&QWP(0,$tmp1));		# K_00_19
+	&vmovdqa(@X[4],&QWP(16,$tmp1));		# K_20_39
+	&vmovdqa(@X[5],&QWP(32,$tmp1));		# K_40_59
+	&vmovdqa(@X[6],&QWP(48,$tmp1));		# K_60_79
+	&vmovdqa(@X[2],&QWP(64,$tmp1));		# pbswap mask
+
+	&mov	($E,&wparam(0));		# load argument block
+	&mov	($inp=@T[1],&wparam(1));
+	&mov	($D,&wparam(2));
+	&mov	(@T[0],"esp");
+
+	# stack frame layout
+	#
+	# +0	X[0]+K	X[1]+K	X[2]+K	X[3]+K	# XMM->IALU xfer area
+	#	X[4]+K	X[5]+K	X[6]+K	X[7]+K
+	#	X[8]+K	X[9]+K	X[10]+K	X[11]+K
+	#	X[12]+K	X[13]+K	X[14]+K	X[15]+K
+	#
+	# +64	X[0]	X[1]	X[2]	X[3]	# XMM->XMM backtrace area
+	#	X[4]	X[5]	X[6]	X[7]
+	#	X[8]	X[9]	X[10]	X[11]	# even borrowed for K_00_19
+	#
+	# +112	K_20_39	K_20_39	K_20_39	K_20_39	# constants
+	#	K_40_59	K_40_59	K_40_59	K_40_59
+	#	K_60_79	K_60_79	K_60_79	K_60_79
+	#	K_00_19	K_00_19	K_00_19	K_00_19
+	#	pbswap mask
+	#
+	# +192	ctx				# argument block
+	# +196	inp
+	# +200	end
+	# +204	esp
+	&sub	("esp",208);
+	&and	("esp",-64);
+
+	&vmovdqa(&QWP(112+0,"esp"),@X[4]);	# copy constants
+	&vmovdqa(&QWP(112+16,"esp"),@X[5]);
+	&vmovdqa(&QWP(112+32,"esp"),@X[6]);
+	&shl	($D,6);				# len*64
+	&vmovdqa(&QWP(112+48,"esp"),@X[3]);
+	&add	($D,$inp);			# end of input
+	&vmovdqa(&QWP(112+64,"esp"),@X[2]);
+	&add	($inp,64);
+	&mov	(&DWP(192+0,"esp"),$E);		# save argument block
+	&mov	(&DWP(192+4,"esp"),$inp);
+	&mov	(&DWP(192+8,"esp"),$D);
+	&mov	(&DWP(192+12,"esp"),@T[0]);	# save original %esp
+
+	&mov	($A,&DWP(0,$E));		# load context
+	&mov	($B,&DWP(4,$E));
+	&mov	($C,&DWP(8,$E));
+	&mov	($D,&DWP(12,$E));
+	&mov	($E,&DWP(16,$E));
+	&mov	(@T[0],$B);			# magic seed
+
+	&vmovdqu(@X[-4&7],&QWP(-64,$inp));	# load input to %xmm[0-3]
+	&vmovdqu(@X[-3&7],&QWP(-48,$inp));
+	&vmovdqu(@X[-2&7],&QWP(-32,$inp));
+	&vmovdqu(@X[-1&7],&QWP(-16,$inp));
+	&vpshufb(@X[-4&7],@X[-4&7],@X[2]);	# byte swap
+	&vpshufb(@X[-3&7],@X[-3&7],@X[2]);
+	&vpshufb(@X[-2&7],@X[-2&7],@X[2]);
+	&vmovdqa(&QWP(112-16,"esp"),@X[3]);	# borrow last backtrace slot
+	&vpshufb(@X[-1&7],@X[-1&7],@X[2]);
+	&vpaddd	(@X[0],@X[-4&7],@X[3]);		# add K_00_19
+	&vpaddd	(@X[1],@X[-3&7],@X[3]);
+	&vpaddd	(@X[2],@X[-2&7],@X[3]);
+	&vmovdqa(&QWP(0,"esp"),@X[0]);		# X[]+K xfer to IALU
+	&mov	(@T[1],$C);
+	&vmovdqa(&QWP(0+16,"esp"),@X[1]);
+	&xor	(@T[1],$D);
+	&vmovdqa(&QWP(0+32,"esp"),@X[2]);
+	&and	(@T[0],@T[1]);
+	&jmp	(&label("loop"));
+
+sub Xupdate_avx_16_31()		# recall that $Xi starts wtih 4
+{ use integer;
+  my $body = shift;
+  my @insns = (&$body,&$body,&$body,&$body);	# 40 instructions
+  my ($a,$b,$c,$d,$e);
+
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&vpalignr(@X[0],@X[-3&7],@X[-4&7],8);	# compose "X[-14]" in "X[0]"
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	  &vpaddd	(@X[3],@X[3],@X[-1&7]);
+	  &vmovdqa	(&QWP(64+16*(($Xi-4)%3),"esp"),@X[-4&7]);# save X[] to backtrace buffer
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&vpsrldq(@X[2],@X[-1&7],4);		# "X[-3]", 3 dwords
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&vpxor	(@X[0],@X[0],@X[-4&7]);		# "X[0]"^="X[-16]"
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	&vpxor	(@X[2],@X[2],@X[-2&7]);		# "X[-3]"^"X[-8]"
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	  &vmovdqa	(&QWP(0+16*(($Xi-1)&3),"esp"),@X[3]);	# X[]+K xfer to IALU
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	&vpxor	(@X[0],@X[0],@X[2]);		# "X[0]"^="X[-3]"^"X[-8]"
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	&vpsrld	(@X[2],@X[0],31);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	&vpslldq(@X[4],@X[0],12);		# "X[0]"<<96, extract one dword
+	&vpaddd	(@X[0],@X[0],@X[0]);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	&vpsrld	(@X[3],@X[4],30);
+	&vpor	(@X[0],@X[0],@X[2]);		# "X[0]"<<<=1
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	&vpslld	(@X[4],@X[4],2);
+	  &vmovdqa	(@X[2],&QWP(64+16*(($Xi-6)%3),"esp")) if ($Xi>5);	# restore X[] from backtrace buffer
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&vpxor	(@X[0],@X[0],@X[3]);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	&vpxor	(@X[0],@X[0],@X[4]);		# "X[0]"^=("X[0]"<<96)<<<2
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	  &vmovdqa	(@X[4],&QWP(112-16+16*(($Xi)/5),"esp"));	# K_XX_XX
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	 foreach (@insns) { eval; }	# remaining instructions [if any]
+
+  $Xi++;	push(@X,shift(@X));	# "rotate" X[]
+}
+
+sub Xupdate_avx_32_79()
+{ use integer;
+  my $body = shift;
+  my @insns = (&$body,&$body,&$body,&$body);	# 32 to 44 instructions
+  my ($a,$b,$c,$d,$e);
+
+	&vpalignr(@X[2],@X[-1&7],@X[-2&7],8);	# compose "X[-6]"
+	&vpxor	(@X[0],@X[0],@X[-4&7]);	# "X[0]"="X[-32]"^"X[-16]"
+	 eval(shift(@insns));		# body_20_39
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));		# rol
+
+	&vpxor	(@X[0],@X[0],@X[-7&7]);	# "X[0]"^="X[-28]"
+	  &vmovdqa	(&QWP(64+16*(($Xi-4)%3),"esp"),@X[-4&7]);	# save X[] to backtrace buffer
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 if ($Xi%5) {
+	  &vmovdqa	(@X[4],@X[3]);	# "perpetuate" K_XX_XX...
+	 } else {			# ... or load next one
+	  &vmovdqa	(@X[4],&QWP(112-16+16*($Xi/5),"esp"));
+	 }
+	  &vpaddd	(@X[3],@X[3],@X[-1&7]);
+	 eval(shift(@insns));		# ror
+	 eval(shift(@insns));
+
+	&vpxor	(@X[0],@X[0],@X[2]);		# "X[0]"^="X[-6]"
+	 eval(shift(@insns));		# body_20_39
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));		# rol
+
+	&vpsrld	(@X[2],@X[0],30);
+	  &vmovdqa	(&QWP(0+16*(($Xi-1)&3),"esp"),@X[3]);	# X[]+K xfer to IALU
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));		# ror
+	 eval(shift(@insns));
+
+	&vpslld	(@X[0],@X[0],2);
+	 eval(shift(@insns));		# body_20_39
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));		# rol
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));		# ror
+	 eval(shift(@insns));
+
+	&vpor	(@X[0],@X[0],@X[2]);	# "X[0]"<<<=2
+	 eval(shift(@insns));		# body_20_39
+	 eval(shift(@insns));
+	  &vmovdqa	(@X[2],&QWP(64+16*(($Xi-6)%3),"esp")) if($Xi<19);	# restore X[] from backtrace buffer
+	 eval(shift(@insns));
+	 eval(shift(@insns));		# rol
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));		# ror
+	 eval(shift(@insns));
+
+	 foreach (@insns) { eval; }	# remaining instructions
+
+  $Xi++;	push(@X,shift(@X));	# "rotate" X[]
+}
+
+sub Xuplast_avx_80()
+{ use integer;
+  my $body = shift;
+  my @insns = (&$body,&$body,&$body,&$body);	# 32 instructions
+  my ($a,$b,$c,$d,$e);
+
+	 eval(shift(@insns));
+	  &vpaddd	(@X[3],@X[3],@X[-1&7]);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	  &vmovdqa	(&QWP(0+16*(($Xi-1)&3),"esp"),@X[3]);	# X[]+K xfer IALU
+
+	 foreach (@insns) { eval; }		# remaining instructions
+
+	&mov	($inp=@T[1],&DWP(192+4,"esp"));
+	&cmp	($inp,&DWP(192+8,"esp"));
+	&je	(&label("done"));
+
+	&vmovdqa(@X[3],&QWP(112+48,"esp"));	# K_00_19
+	&vmovdqa(@X[2],&QWP(112+64,"esp"));	# pbswap mask
+	&vmovdqu(@X[-4&7],&QWP(0,$inp));	# load input
+	&vmovdqu(@X[-3&7],&QWP(16,$inp));
+	&vmovdqu(@X[-2&7],&QWP(32,$inp));
+	&vmovdqu(@X[-1&7],&QWP(48,$inp));
+	&add	($inp,64);
+	&vpshufb(@X[-4&7],@X[-4&7],@X[2]);		# byte swap
+	&mov	(&DWP(192+4,"esp"),$inp);
+	&vmovdqa(&QWP(112-16,"esp"),@X[3]);	# borrow last backtrace slot
+
+  $Xi=0;
+}
+
+sub Xloop_avx()
+{ use integer;
+  my $body = shift;
+  my @insns = (&$body,&$body,&$body,&$body);	# 32 instructions
+  my ($a,$b,$c,$d,$e);
+
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&vpshufb	(@X[($Xi-3)&7],@X[($Xi-3)&7],@X[2]);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&vpaddd	(@X[$Xi&7],@X[($Xi-4)&7],@X[3]);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&vmovdqa	(&QWP(0+16*$Xi,"esp"),@X[$Xi&7]);	# X[]+K xfer to IALU
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	foreach (@insns) { eval; }
+  $Xi++;
+}
+
+sub Xtail_avx()
+{ use integer;
+  my $body = shift;
+  my @insns = (&$body,&$body,&$body,&$body);	# 32 instructions
+  my ($a,$b,$c,$d,$e);
+
+	foreach (@insns) { eval; }
+}
+
+&set_label("loop",16);
+	&Xupdate_avx_16_31(\&body_00_19);
+	&Xupdate_avx_16_31(\&body_00_19);
+	&Xupdate_avx_16_31(\&body_00_19);
+	&Xupdate_avx_16_31(\&body_00_19);
+	&Xupdate_avx_32_79(\&body_00_19);
+	&Xupdate_avx_32_79(\&body_20_39);
+	&Xupdate_avx_32_79(\&body_20_39);
+	&Xupdate_avx_32_79(\&body_20_39);
+	&Xupdate_avx_32_79(\&body_20_39);
+	&Xupdate_avx_32_79(\&body_20_39);
+	&Xupdate_avx_32_79(\&body_40_59);
+	&Xupdate_avx_32_79(\&body_40_59);
+	&Xupdate_avx_32_79(\&body_40_59);
+	&Xupdate_avx_32_79(\&body_40_59);
+	&Xupdate_avx_32_79(\&body_40_59);
+	&Xupdate_avx_32_79(\&body_20_39);
+	&Xuplast_avx_80(\&body_20_39);	# can jump to "done"
+
+				$saved_j=$j; @saved_V=@V;
+
+	&Xloop_avx(\&body_20_39);
+	&Xloop_avx(\&body_20_39);
+	&Xloop_avx(\&body_20_39);
+
+	&mov	(@T[1],&DWP(192,"esp"));	# update context
+	&add	($A,&DWP(0,@T[1]));
+	&add	(@T[0],&DWP(4,@T[1]));		# $b
+	&add	($C,&DWP(8,@T[1]));
+	&mov	(&DWP(0,@T[1]),$A);
+	&add	($D,&DWP(12,@T[1]));
+	&mov	(&DWP(4,@T[1]),@T[0]);
+	&add	($E,&DWP(16,@T[1]));
+	&mov	($B,$C);
+	&mov	(&DWP(8,@T[1]),$C);
+	&xor	($B,$D);
+	&mov	(&DWP(12,@T[1]),$D);
+	&mov	(&DWP(16,@T[1]),$E);
+	&mov	(@T[1],@T[0]);
+	&and	(@T[0],$B);
+	&mov	($B,@T[1]);
+
+	&jmp	(&label("loop"));
+
+&set_label("done",16);		$j=$saved_j; @V=@saved_V;
+
+	&Xtail_avx(\&body_20_39);
+	&Xtail_avx(\&body_20_39);
+	&Xtail_avx(\&body_20_39);
+
+	&vzeroall();
+
+	&mov	(@T[1],&DWP(192,"esp"));	# update context
+	&add	($A,&DWP(0,@T[1]));
+	&mov	("esp",&DWP(192+12,"esp"));	# restore %esp
+	&add	(@T[0],&DWP(4,@T[1]));		# $b
+	&add	($C,&DWP(8,@T[1]));
+	&mov	(&DWP(0,@T[1]),$A);
+	&add	($D,&DWP(12,@T[1]));
+	&mov	(&DWP(4,@T[1]),@T[0]);
+	&add	($E,&DWP(16,@T[1]));
+	&mov	(&DWP(8,@T[1]),$C);
+	&mov	(&DWP(12,@T[1]),$D);
+	&mov	(&DWP(16,@T[1]),$E);
+&function_end("_sha1_block_data_order_avx");
+}
+&set_label("K_XX_XX",64);
+&data_word(0x5a827999,0x5a827999,0x5a827999,0x5a827999);	# K_00_19
+&data_word(0x6ed9eba1,0x6ed9eba1,0x6ed9eba1,0x6ed9eba1);	# K_20_39
+&data_word(0x8f1bbcdc,0x8f1bbcdc,0x8f1bbcdc,0x8f1bbcdc);	# K_40_59
+&data_word(0xca62c1d6,0xca62c1d6,0xca62c1d6,0xca62c1d6);	# K_60_79
+&data_word(0x00010203,0x04050607,0x08090a0b,0x0c0d0e0f);	# pbswap mask
+&data_byte(0xf,0xe,0xd,0xc,0xb,0xa,0x9,0x8,0x7,0x6,0x5,0x4,0x3,0x2,0x1,0x0);
+}
+&asciz("SHA1 block transform for x86, CRYPTOGAMS by ");
+
+&asm_finish();
+
+close STDOUT;
diff --git a/crypto/sha/asm/sha1-alpha.pl b/crypto/sha/asm/sha1-alpha.pl
new file mode 100644
index 000000000..4124958f7
--- /dev/null
+++ b/crypto/sha/asm/sha1-alpha.pl
@@ -0,0 +1,329 @@
+#! /usr/bin/env perl
+# Copyright 2009-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+
+# ====================================================================
+# Written by Andy Polyakov  for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+
+# SHA1 block procedure for Alpha.
+
+# On 21264 performance is 33% better than code generated by vendor
+# compiler, and 75% better than GCC [3.4], and in absolute terms is
+# 8.7 cycles per processed byte. Implementation features vectorized
+# byte swap, but not Xupdate.
+
+@X=(	"\$0",	"\$1",	"\$2",	"\$3",	"\$4",	"\$5",	"\$6",	"\$7",
+	"\$8",	"\$9",	"\$10",	"\$11",	"\$12",	"\$13",	"\$14",	"\$15");
+$ctx="a0";	# $16
+$inp="a1";
+$num="a2";
+$A="a3";
+$B="a4";	# 20
+$C="a5";
+$D="t8";
+$E="t9";	@V=($A,$B,$C,$D,$E);
+$t0="t10";	# 24
+$t1="t11";
+$t2="ra";
+$t3="t12";
+$K="AT";	# 28
+
+sub BODY_00_19 {
+my ($i,$a,$b,$c,$d,$e)=@_;
+my $j=$i+1;
+$code.=<<___ if ($i==0);
+	ldq_u	@X[0],0+0($inp)
+	ldq_u	@X[1],0+7($inp)
+___
+$code.=<<___ if (!($i&1) && $i<14);
+	ldq_u	@X[$i+2],($i+2)*4+0($inp)
+	ldq_u	@X[$i+3],($i+2)*4+7($inp)
+___
+$code.=<<___ if (!($i&1) && $i<15);
+	extql	@X[$i],$inp,@X[$i]
+	extqh	@X[$i+1],$inp,@X[$i+1]
+
+	or	@X[$i+1],@X[$i],@X[$i]	# pair of 32-bit values are fetched
+
+	srl	@X[$i],24,$t0		# vectorized byte swap
+	srl	@X[$i],8,$t2
+
+	sll	@X[$i],8,$t3
+	sll	@X[$i],24,@X[$i]
+	zapnot	$t0,0x11,$t0
+	zapnot	$t2,0x22,$t2
+
+	zapnot	@X[$i],0x88,@X[$i]
+	or	$t0,$t2,$t0
+	zapnot	$t3,0x44,$t3
+	sll	$a,5,$t1
+
+	or	@X[$i],$t0,@X[$i]
+	addl	$K,$e,$e
+	and	$b,$c,$t2
+	zapnot	$a,0xf,$a
+
+	or	@X[$i],$t3,@X[$i]
+	srl	$a,27,$t0
+	bic	$d,$b,$t3
+	sll	$b,30,$b
+
+	extll	@X[$i],4,@X[$i+1]	# extract upper half
+	or	$t2,$t3,$t2
+	addl	@X[$i],$e,$e
+
+	addl	$t1,$e,$e
+	srl	$b,32,$t3
+	zapnot	@X[$i],0xf,@X[$i]
+
+	addl	$t0,$e,$e
+	addl	$t2,$e,$e
+	or	$t3,$b,$b
+___
+$code.=<<___ if (($i&1) && $i<15);
+	sll	$a,5,$t1
+	addl	$K,$e,$e
+	and	$b,$c,$t2
+	zapnot	$a,0xf,$a
+
+	srl	$a,27,$t0
+	addl	@X[$i%16],$e,$e
+	bic	$d,$b,$t3
+	sll	$b,30,$b
+
+	or	$t2,$t3,$t2
+	addl	$t1,$e,$e
+	srl	$b,32,$t3
+	zapnot	@X[$i],0xf,@X[$i]
+
+	addl	$t0,$e,$e
+	addl	$t2,$e,$e
+	or	$t3,$b,$b
+___
+$code.=<<___ if ($i>=15);	# with forward Xupdate
+	sll	$a,5,$t1
+	addl	$K,$e,$e
+	and	$b,$c,$t2
+	xor	@X[($j+2)%16],@X[$j%16],@X[$j%16]
+
+	zapnot	$a,0xf,$a
+	addl	@X[$i%16],$e,$e
+	bic	$d,$b,$t3
+	xor	@X[($j+8)%16],@X[$j%16],@X[$j%16]
+
+	srl	$a,27,$t0
+	addl	$t1,$e,$e
+	or	$t2,$t3,$t2
+	xor	@X[($j+13)%16],@X[$j%16],@X[$j%16]
+
+	sll	$b,30,$b
+	addl	$t0,$e,$e
+	srl	@X[$j%16],31,$t1
+
+	addl	$t2,$e,$e
+	srl	$b,32,$t3
+	addl	@X[$j%16],@X[$j%16],@X[$j%16]
+
+	or	$t3,$b,$b
+	zapnot	@X[$i%16],0xf,@X[$i%16]
+	or	$t1,@X[$j%16],@X[$j%16]
+___
+}
+
+sub BODY_20_39 {
+my ($i,$a,$b,$c,$d,$e)=@_;
+my $j=$i+1;
+$code.=<<___ if ($i<79);	# with forward Xupdate
+	sll	$a,5,$t1
+	addl	$K,$e,$e
+	zapnot	$a,0xf,$a
+	xor	@X[($j+2)%16],@X[$j%16],@X[$j%16]
+
+	sll	$b,30,$t3
+	addl	$t1,$e,$e
+	xor	$b,$c,$t2
+	xor	@X[($j+8)%16],@X[$j%16],@X[$j%16]
+
+	srl	$b,2,$b
+	addl	@X[$i%16],$e,$e
+	xor	$d,$t2,$t2
+	xor	@X[($j+13)%16],@X[$j%16],@X[$j%16]
+
+	srl	@X[$j%16],31,$t1
+	addl	$t2,$e,$e
+	srl	$a,27,$t0
+	addl	@X[$j%16],@X[$j%16],@X[$j%16]
+
+	or	$t3,$b,$b
+	addl	$t0,$e,$e
+	or	$t1,@X[$j%16],@X[$j%16]
+___
+$code.=<<___ if ($i<77);
+	zapnot	@X[$i%16],0xf,@X[$i%16]
+___
+$code.=<<___ if ($i==79);	# with context fetch
+	sll	$a,5,$t1
+	addl	$K,$e,$e
+	zapnot	$a,0xf,$a
+	ldl	@X[0],0($ctx)
+
+	sll	$b,30,$t3
+	addl	$t1,$e,$e
+	xor	$b,$c,$t2
+	ldl	@X[1],4($ctx)
+
+	srl	$b,2,$b
+	addl	@X[$i%16],$e,$e
+	xor	$d,$t2,$t2
+	ldl	@X[2],8($ctx)
+
+	srl	$a,27,$t0
+	addl	$t2,$e,$e
+	ldl	@X[3],12($ctx)
+
+	or	$t3,$b,$b
+	addl	$t0,$e,$e
+	ldl	@X[4],16($ctx)
+___
+}
+
+sub BODY_40_59 {
+my ($i,$a,$b,$c,$d,$e)=@_;
+my $j=$i+1;
+$code.=<<___;	# with forward Xupdate
+	sll	$a,5,$t1
+	addl	$K,$e,$e
+	zapnot	$a,0xf,$a
+	xor	@X[($j+2)%16],@X[$j%16],@X[$j%16]
+
+	srl	$a,27,$t0
+	and	$b,$c,$t2
+	and	$b,$d,$t3
+	xor	@X[($j+8)%16],@X[$j%16],@X[$j%16]
+
+	sll	$b,30,$b
+	addl	$t1,$e,$e
+	xor	@X[($j+13)%16],@X[$j%16],@X[$j%16]
+
+	srl	@X[$j%16],31,$t1
+	addl	$t0,$e,$e
+	or	$t2,$t3,$t2
+	and	$c,$d,$t3
+
+	or	$t2,$t3,$t2
+	srl	$b,32,$t3
+	addl	@X[$i%16],$e,$e
+	addl	@X[$j%16],@X[$j%16],@X[$j%16]
+
+	or	$t3,$b,$b
+	addl	$t2,$e,$e
+	or	$t1,@X[$j%16],@X[$j%16]
+	zapnot	@X[$i%16],0xf,@X[$i%16]
+___
+}
+
+$code=<<___;
+#ifdef __linux__
+#include 
+#else
+#include 
+#include 
+#endif
+
+.text
+
+.set	noat
+.set	noreorder
+.globl	sha1_block_data_order
+.align	5
+.ent	sha1_block_data_order
+sha1_block_data_order:
+	lda	sp,-64(sp)
+	stq	ra,0(sp)
+	stq	s0,8(sp)
+	stq	s1,16(sp)
+	stq	s2,24(sp)
+	stq	s3,32(sp)
+	stq	s4,40(sp)
+	stq	s5,48(sp)
+	stq	fp,56(sp)
+	.mask	0x0400fe00,-64
+	.frame	sp,64,ra
+	.prologue 0
+
+	ldl	$A,0($ctx)
+	ldl	$B,4($ctx)
+	sll	$num,6,$num
+	ldl	$C,8($ctx)
+	ldl	$D,12($ctx)
+	ldl	$E,16($ctx)
+	addq	$inp,$num,$num
+
+.Lloop:
+	.set	noreorder
+	ldah	$K,23170(zero)
+	zapnot	$B,0xf,$B
+	lda	$K,31129($K)	# K_00_19
+___
+for ($i=0;$i<20;$i++) { &BODY_00_19($i,@V); unshift(@V,pop(@V)); }
+
+$code.=<<___;
+	ldah	$K,28378(zero)
+	lda	$K,-5215($K)	# K_20_39
+___
+for (;$i<40;$i++) { &BODY_20_39($i,@V); unshift(@V,pop(@V)); }
+
+$code.=<<___;
+	ldah	$K,-28900(zero)
+	lda	$K,-17188($K)	# K_40_59
+___
+for (;$i<60;$i++) { &BODY_40_59($i,@V); unshift(@V,pop(@V)); }
+
+$code.=<<___;
+	ldah	$K,-13725(zero)
+	lda	$K,-15914($K)	# K_60_79
+___
+for (;$i<80;$i++) { &BODY_20_39($i,@V); unshift(@V,pop(@V)); }
+
+$code.=<<___;
+	addl	@X[0],$A,$A
+	addl	@X[1],$B,$B
+	addl	@X[2],$C,$C
+	addl	@X[3],$D,$D
+	addl	@X[4],$E,$E
+	stl	$A,0($ctx)
+	stl	$B,4($ctx)
+	addq	$inp,64,$inp
+	stl	$C,8($ctx)
+	stl	$D,12($ctx)
+	stl	$E,16($ctx)
+	cmpult	$inp,$num,$t1
+	bne	$t1,.Lloop
+
+	.set	noreorder
+	ldq	ra,0(sp)
+	ldq	s0,8(sp)
+	ldq	s1,16(sp)
+	ldq	s2,24(sp)
+	ldq	s3,32(sp)
+	ldq	s4,40(sp)
+	ldq	s5,48(sp)
+	ldq	fp,56(sp)
+	lda	sp,64(sp)
+	ret	(ra)
+.end	sha1_block_data_order
+.ascii	"SHA1 block transform for Alpha, CRYPTOGAMS by "
+.align	2
+___
+$output=pop and open STDOUT,">$output";
+print $code;
+close STDOUT;
diff --git a/crypto/sha/asm/sha1-armv4-large.pl b/crypto/sha/asm/sha1-armv4-large.pl
new file mode 100644
index 000000000..7ff5bfbba
--- /dev/null
+++ b/crypto/sha/asm/sha1-armv4-large.pl
@@ -0,0 +1,742 @@
+#! /usr/bin/env perl
+# Copyright 2007-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+
+# ====================================================================
+# Written by Andy Polyakov  for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+
+# sha1_block procedure for ARMv4.
+#
+# January 2007.
+
+# Size/performance trade-off
+# ====================================================================
+# impl		size in bytes	comp cycles[*]	measured performance
+# ====================================================================
+# thumb		304		3212		4420
+# armv4-small	392/+29%	1958/+64%	2250/+96%
+# armv4-compact	740/+89%	1552/+26%	1840/+22%
+# armv4-large	1420/+92%	1307/+19%	1370/+34%[***]
+# full unroll	~5100/+260%	~1260/+4%	~1300/+5%
+# ====================================================================
+# thumb		= same as 'small' but in Thumb instructions[**] and
+#		  with recurring code in two private functions;
+# small		= detached Xload/update, loops are folded;
+# compact	= detached Xload/update, 5x unroll;
+# large		= interleaved Xload/update, 5x unroll;
+# full unroll	= interleaved Xload/update, full unroll, estimated[!];
+#
+# [*]	Manually counted instructions in "grand" loop body. Measured
+#	performance is affected by prologue and epilogue overhead,
+#	i-cache availability, branch penalties, etc.
+# [**]	While each Thumb instruction is twice smaller, they are not as
+#	diverse as ARM ones: e.g., there are only two arithmetic
+#	instructions with 3 arguments, no [fixed] rotate, addressing
+#	modes are limited. As result it takes more instructions to do
+#	the same job in Thumb, therefore the code is never twice as
+#	small and always slower.
+# [***]	which is also ~35% better than compiler generated code. Dual-
+#	issue Cortex A8 core was measured to process input block in
+#	~990 cycles.
+
+# August 2010.
+#
+# Rescheduling for dual-issue pipeline resulted in 13% improvement on
+# Cortex A8 core and in absolute terms ~870 cycles per input block
+# [or 13.6 cycles per byte].
+
+# February 2011.
+#
+# Profiler-assisted and platform-specific optimization resulted in 10%
+# improvement on Cortex A8 core and 12.2 cycles per byte.
+
+# September 2013.
+#
+# Add NEON implementation (see sha1-586.pl for background info). On
+# Cortex A8 it was measured to process one byte in 6.7 cycles or >80%
+# faster than integer-only code. Because [fully unrolled] NEON code
+# is ~2.5x larger and there are some redundant instructions executed
+# when processing last block, improvement is not as big for smallest
+# blocks, only ~30%. Snapdragon S4 is a tad faster, 6.4 cycles per
+# byte, which is also >80% faster than integer-only code. Cortex-A15
+# is even faster spending 5.6 cycles per byte outperforming integer-
+# only code by factor of 2.
+
+# May 2014.
+#
+# Add ARMv8 code path performing at 2.35 cpb on Apple A7.
+
+$flavour = shift;
+if ($flavour=~/\w[\w\-]*\.\w+$/) { $output=$flavour; undef $flavour; }
+else { while (($output=shift) && ($output!~/\w[\w\-]*\.\w+$/)) {} }
+
+if ($flavour && $flavour ne "void") {
+    $0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+    ( $xlate="${dir}arm-xlate.pl" and -f $xlate ) or
+    ( $xlate="${dir}../../perlasm/arm-xlate.pl" and -f $xlate) or
+    die "can't locate arm-xlate.pl";
+
+    open STDOUT,"| \"$^X\" $xlate $flavour $output";
+} else {
+    open STDOUT,">$output";
+}
+
+$ctx="r0";
+$inp="r1";
+$len="r2";
+$a="r3";
+$b="r4";
+$c="r5";
+$d="r6";
+$e="r7";
+$K="r8";
+$t0="r9";
+$t1="r10";
+$t2="r11";
+$t3="r12";
+$Xi="r14";
+@V=($a,$b,$c,$d,$e);
+
+sub Xupdate {
+my ($a,$b,$c,$d,$e,$opt1,$opt2)=@_;
+$code.=<<___;
+	ldr	$t0,[$Xi,#15*4]
+	ldr	$t1,[$Xi,#13*4]
+	ldr	$t2,[$Xi,#7*4]
+	add	$e,$K,$e,ror#2			@ E+=K_xx_xx
+	ldr	$t3,[$Xi,#2*4]
+	eor	$t0,$t0,$t1
+	eor	$t2,$t2,$t3			@ 1 cycle stall
+	eor	$t1,$c,$d			@ F_xx_xx
+	mov	$t0,$t0,ror#31
+	add	$e,$e,$a,ror#27			@ E+=ROR(A,27)
+	eor	$t0,$t0,$t2,ror#31
+	str	$t0,[$Xi,#-4]!
+	$opt1					@ F_xx_xx
+	$opt2					@ F_xx_xx
+	add	$e,$e,$t0			@ E+=X[i]
+___
+}
+
+sub BODY_00_15 {
+my ($a,$b,$c,$d,$e)=@_;
+$code.=<<___;
+#if __ARM_ARCH__<7
+	ldrb	$t1,[$inp,#2]
+	ldrb	$t0,[$inp,#3]
+	ldrb	$t2,[$inp,#1]
+	add	$e,$K,$e,ror#2			@ E+=K_00_19
+	ldrb	$t3,[$inp],#4
+	orr	$t0,$t0,$t1,lsl#8
+	eor	$t1,$c,$d			@ F_xx_xx
+	orr	$t0,$t0,$t2,lsl#16
+	add	$e,$e,$a,ror#27			@ E+=ROR(A,27)
+	orr	$t0,$t0,$t3,lsl#24
+#else
+	ldr	$t0,[$inp],#4			@ handles unaligned
+	add	$e,$K,$e,ror#2			@ E+=K_00_19
+	eor	$t1,$c,$d			@ F_xx_xx
+	add	$e,$e,$a,ror#27			@ E+=ROR(A,27)
+#ifdef __ARMEL__
+	rev	$t0,$t0				@ byte swap
+#endif
+#endif
+	and	$t1,$b,$t1,ror#2
+	add	$e,$e,$t0			@ E+=X[i]
+	eor	$t1,$t1,$d,ror#2		@ F_00_19(B,C,D)
+	str	$t0,[$Xi,#-4]!
+	add	$e,$e,$t1			@ E+=F_00_19(B,C,D)
+___
+}
+
+sub BODY_16_19 {
+my ($a,$b,$c,$d,$e)=@_;
+	&Xupdate(@_,"and $t1,$b,$t1,ror#2");
+$code.=<<___;
+	eor	$t1,$t1,$d,ror#2		@ F_00_19(B,C,D)
+	add	$e,$e,$t1			@ E+=F_00_19(B,C,D)
+___
+}
+
+sub BODY_20_39 {
+my ($a,$b,$c,$d,$e)=@_;
+	&Xupdate(@_,"eor $t1,$b,$t1,ror#2");
+$code.=<<___;
+	add	$e,$e,$t1			@ E+=F_20_39(B,C,D)
+___
+}
+
+sub BODY_40_59 {
+my ($a,$b,$c,$d,$e)=@_;
+	&Xupdate(@_,"and $t1,$b,$t1,ror#2","and $t2,$c,$d");
+$code.=<<___;
+	add	$e,$e,$t1			@ E+=F_40_59(B,C,D)
+	add	$e,$e,$t2,ror#2
+___
+}
+
+$code=<<___;
+#include "arm_arch.h"
+
+.text
+#if defined(__thumb2__)
+.syntax	unified
+.thumb
+#else
+.code	32
+#endif
+
+.global	sha1_block_data_order
+.type	sha1_block_data_order,%function
+
+.align	5
+sha1_block_data_order:
+#if __ARM_MAX_ARCH__>=7
+.Lsha1_block:
+	adr	r3,.Lsha1_block
+	ldr	r12,.LOPENSSL_armcap
+	ldr	r12,[r3,r12]		@ OPENSSL_armcap_P
+#ifdef	__APPLE__
+	ldr	r12,[r12]
+#endif
+	tst	r12,#ARMV8_SHA1
+	bne	.LARMv8
+	tst	r12,#ARMV7_NEON
+	bne	.LNEON
+#endif
+	stmdb	sp!,{r4-r12,lr}
+	add	$len,$inp,$len,lsl#6	@ $len to point at the end of $inp
+	ldmia	$ctx,{$a,$b,$c,$d,$e}
+.Lloop:
+	ldr	$K,.LK_00_19
+	mov	$Xi,sp
+	sub	sp,sp,#15*4
+	mov	$c,$c,ror#30
+	mov	$d,$d,ror#30
+	mov	$e,$e,ror#30		@ [6]
+.L_00_15:
+___
+for($i=0;$i<5;$i++) {
+	&BODY_00_15(@V);	unshift(@V,pop(@V));
+}
+$code.=<<___;
+#if defined(__thumb2__)
+	mov	$t3,sp
+	teq	$Xi,$t3
+#else
+	teq	$Xi,sp
+#endif
+	bne	.L_00_15		@ [((11+4)*5+2)*3]
+	sub	sp,sp,#25*4
+___
+	&BODY_00_15(@V);	unshift(@V,pop(@V));
+	&BODY_16_19(@V);	unshift(@V,pop(@V));
+	&BODY_16_19(@V);	unshift(@V,pop(@V));
+	&BODY_16_19(@V);	unshift(@V,pop(@V));
+	&BODY_16_19(@V);	unshift(@V,pop(@V));
+$code.=<<___;
+
+	ldr	$K,.LK_20_39		@ [+15+16*4]
+	cmn	sp,#0			@ [+3], clear carry to denote 20_39
+.L_20_39_or_60_79:
+___
+for($i=0;$i<5;$i++) {
+	&BODY_20_39(@V);	unshift(@V,pop(@V));
+}
+$code.=<<___;
+#if defined(__thumb2__)
+	mov	$t3,sp
+	teq	$Xi,$t3
+#else
+	teq	$Xi,sp			@ preserve carry
+#endif
+	bne	.L_20_39_or_60_79	@ [+((12+3)*5+2)*4]
+	bcs	.L_done			@ [+((12+3)*5+2)*4], spare 300 bytes
+
+	ldr	$K,.LK_40_59
+	sub	sp,sp,#20*4		@ [+2]
+.L_40_59:
+___
+for($i=0;$i<5;$i++) {
+	&BODY_40_59(@V);	unshift(@V,pop(@V));
+}
+$code.=<<___;
+#if defined(__thumb2__)
+	mov	$t3,sp
+	teq	$Xi,$t3
+#else
+	teq	$Xi,sp
+#endif
+	bne	.L_40_59		@ [+((12+5)*5+2)*4]
+
+	ldr	$K,.LK_60_79
+	sub	sp,sp,#20*4
+	cmp	sp,#0			@ set carry to denote 60_79
+	b	.L_20_39_or_60_79	@ [+4], spare 300 bytes
+.L_done:
+	add	sp,sp,#80*4		@ "deallocate" stack frame
+	ldmia	$ctx,{$K,$t0,$t1,$t2,$t3}
+	add	$a,$K,$a
+	add	$b,$t0,$b
+	add	$c,$t1,$c,ror#2
+	add	$d,$t2,$d,ror#2
+	add	$e,$t3,$e,ror#2
+	stmia	$ctx,{$a,$b,$c,$d,$e}
+	teq	$inp,$len
+	bne	.Lloop			@ [+18], total 1307
+
+#if __ARM_ARCH__>=5
+	ldmia	sp!,{r4-r12,pc}
+#else
+	ldmia	sp!,{r4-r12,lr}
+	tst	lr,#1
+	moveq	pc,lr			@ be binary compatible with V4, yet
+	bx	lr			@ interoperable with Thumb ISA:-)
+#endif
+.size	sha1_block_data_order,.-sha1_block_data_order
+
+.align	5
+.LK_00_19:	.word	0x5a827999
+.LK_20_39:	.word	0x6ed9eba1
+.LK_40_59:	.word	0x8f1bbcdc
+.LK_60_79:	.word	0xca62c1d6
+#if __ARM_MAX_ARCH__>=7
+.LOPENSSL_armcap:
+.word	OPENSSL_armcap_P-.Lsha1_block
+#endif
+.asciz	"SHA1 block transform for ARMv4/NEON/ARMv8, CRYPTOGAMS by "
+.align	5
+___
+#####################################################################
+# NEON stuff
+#
+{{{
+my @V=($a,$b,$c,$d,$e);
+my ($K_XX_XX,$Ki,$t0,$t1,$Xfer,$saved_sp)=map("r$_",(8..12,14));
+my $Xi=4;
+my @X=map("q$_",(8..11,0..3));
+my @Tx=("q12","q13");
+my ($K,$zero)=("q14","q15");
+my $j=0;
+
+sub AUTOLOAD()          # thunk [simplified] x86-style perlasm
+{ my $opcode = $AUTOLOAD; $opcode =~ s/.*:://; $opcode =~ s/_/\./;
+  my $arg = pop;
+    $arg = "#$arg" if ($arg*1 eq $arg);
+    $code .= "\t$opcode\t".join(',',@_,$arg)."\n";
+}
+
+sub body_00_19 () {
+	(
+	'($a,$b,$c,$d,$e)=@V;'.		# '$code.="@ $j\n";'.
+	'&bic	($t0,$d,$b)',
+	'&add	($e,$e,$Ki)',		# e+=X[i]+K
+	'&and	($t1,$c,$b)',
+	'&ldr	($Ki,sprintf "[sp,#%d]",4*(($j+1)&15))',
+	'&add	($e,$e,$a,"ror#27")',	# e+=ROR(A,27)
+	'&eor	($t1,$t1,$t0)',		# F_00_19
+	'&mov	($b,$b,"ror#2")',	# b=ROR(b,2)
+	'&add	($e,$e,$t1);'.		# e+=F_00_19
+	'$j++;	unshift(@V,pop(@V));'
+	)
+}
+sub body_20_39 () {
+	(
+	'($a,$b,$c,$d,$e)=@V;'.		# '$code.="@ $j\n";'.
+	'&eor	($t0,$b,$d)',
+	'&add	($e,$e,$Ki)',		# e+=X[i]+K
+	'&ldr	($Ki,sprintf "[sp,#%d]",4*(($j+1)&15)) if ($j<79)',
+	'&eor	($t1,$t0,$c)',		# F_20_39
+	'&add	($e,$e,$a,"ror#27")',	# e+=ROR(A,27)
+	'&mov	($b,$b,"ror#2")',	# b=ROR(b,2)
+	'&add	($e,$e,$t1);'.		# e+=F_20_39
+	'$j++;	unshift(@V,pop(@V));'
+	)
+}
+sub body_40_59 () {
+	(
+	'($a,$b,$c,$d,$e)=@V;'.		# '$code.="@ $j\n";'.
+	'&add	($e,$e,$Ki)',		# e+=X[i]+K
+	'&and	($t0,$c,$d)',
+	'&ldr	($Ki,sprintf "[sp,#%d]",4*(($j+1)&15))',
+	'&add	($e,$e,$a,"ror#27")',	# e+=ROR(A,27)
+	'&eor	($t1,$c,$d)',
+	'&add	($e,$e,$t0)',
+	'&and	($t1,$t1,$b)',
+	'&mov	($b,$b,"ror#2")',	# b=ROR(b,2)
+	'&add	($e,$e,$t1);'.		# e+=F_40_59
+	'$j++;	unshift(@V,pop(@V));'
+	)
+}
+
+sub Xupdate_16_31 ()
+{ use integer;
+  my $body = shift;
+  my @insns = (&$body,&$body,&$body,&$body);
+  my ($a,$b,$c,$d,$e);
+
+	&vext_8		(@X[0],@X[-4&7],@X[-3&7],8);	# compose "X[-14]" in "X[0]"
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	  &vadd_i32	(@Tx[1],@X[-1&7],$K);
+	 eval(shift(@insns));
+	  &vld1_32	("{$K\[]}","[$K_XX_XX,:32]!")	if ($Xi%5==0);
+	 eval(shift(@insns));
+	&vext_8		(@Tx[0],@X[-1&7],$zero,4);	# "X[-3]", 3 words
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&veor		(@X[0],@X[0],@X[-4&7]);		# "X[0]"^="X[-16]"
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&veor		(@Tx[0],@Tx[0],@X[-2&7]);	# "X[-3]"^"X[-8]"
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&veor		(@Tx[0],@Tx[0],@X[0]);		# "X[0]"^="X[-3]"^"X[-8]
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	  &vst1_32	("{@Tx[1]}","[$Xfer,:128]!");	# X[]+K xfer
+	  &sub		($Xfer,$Xfer,64)		if ($Xi%4==0);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&vext_8		(@Tx[1],$zero,@Tx[0],4);	# "X[0]"<<96, extract one dword
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&vadd_i32	(@X[0],@Tx[0],@Tx[0]);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&vsri_32	(@X[0],@Tx[0],31);		# "X[0]"<<<=1
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&vshr_u32	(@Tx[0],@Tx[1],30);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&vshl_u32	(@Tx[1],@Tx[1],2);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&veor		(@X[0],@X[0],@Tx[0]);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&veor		(@X[0],@X[0],@Tx[1]);		# "X[0]"^=("X[0]">>96)<<<2
+
+	foreach (@insns) { eval; }	# remaining instructions [if any]
+
+  $Xi++;	push(@X,shift(@X));	# "rotate" X[]
+}
+
+sub Xupdate_32_79 ()
+{ use integer;
+  my $body = shift;
+  my @insns = (&$body,&$body,&$body,&$body);
+  my ($a,$b,$c,$d,$e);
+
+	&vext_8		(@Tx[0],@X[-2&7],@X[-1&7],8);	# compose "X[-6]"
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&veor		(@X[0],@X[0],@X[-4&7]);		# "X[0]"="X[-32]"^"X[-16]"
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&veor		(@X[0],@X[0],@X[-7&7]);		# "X[0]"^="X[-28]"
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	  &vadd_i32	(@Tx[1],@X[-1&7],$K);
+	 eval(shift(@insns));
+	  &vld1_32	("{$K\[]}","[$K_XX_XX,:32]!")	if ($Xi%5==0);
+	 eval(shift(@insns));
+	&veor		(@Tx[0],@Tx[0],@X[0]);		# "X[-6]"^="X[0]"
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&vshr_u32	(@X[0],@Tx[0],30);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	  &vst1_32	("{@Tx[1]}","[$Xfer,:128]!");	# X[]+K xfer
+	  &sub		($Xfer,$Xfer,64)		if ($Xi%4==0);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&vsli_32	(@X[0],@Tx[0],2);		# "X[0]"="X[-6]"<<<2
+
+	foreach (@insns) { eval; }	# remaining instructions [if any]
+
+  $Xi++;	push(@X,shift(@X));	# "rotate" X[]
+}
+
+sub Xuplast_80 ()
+{ use integer;
+  my $body = shift;
+  my @insns = (&$body,&$body,&$body,&$body);
+  my ($a,$b,$c,$d,$e);
+
+	&vadd_i32	(@Tx[1],@X[-1&7],$K);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&vst1_32	("{@Tx[1]}","[$Xfer,:128]!");
+	&sub		($Xfer,$Xfer,64);
+
+	&teq		($inp,$len);
+	&sub		($K_XX_XX,$K_XX_XX,16);	# rewind $K_XX_XX
+	&it		("eq");
+	&subeq		($inp,$inp,64);		# reload last block to avoid SEGV
+	&vld1_8		("{@X[-4&7]-@X[-3&7]}","[$inp]!");
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&vld1_8		("{@X[-2&7]-@X[-1&7]}","[$inp]!");
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&vld1_32	("{$K\[]}","[$K_XX_XX,:32]!");	# load K_00_19
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&vrev32_8	(@X[-4&7],@X[-4&7]);
+
+	foreach (@insns) { eval; }		# remaining instructions
+
+   $Xi=0;
+}
+
+sub Xloop()
+{ use integer;
+  my $body = shift;
+  my @insns = (&$body,&$body,&$body,&$body);
+  my ($a,$b,$c,$d,$e);
+
+	&vrev32_8	(@X[($Xi-3)&7],@X[($Xi-3)&7]);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&vadd_i32	(@X[$Xi&7],@X[($Xi-4)&7],$K);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&vst1_32	("{@X[$Xi&7]}","[$Xfer,:128]!");# X[]+K xfer to IALU
+
+	foreach (@insns) { eval; }
+
+  $Xi++;
+}
+
+$code.=<<___;
+#if __ARM_MAX_ARCH__>=7
+.arch	armv7-a
+.fpu	neon
+
+.type	sha1_block_data_order_neon,%function
+.align	4
+sha1_block_data_order_neon:
+.LNEON:
+	stmdb	sp!,{r4-r12,lr}
+	add	$len,$inp,$len,lsl#6	@ $len to point at the end of $inp
+	@ dmb				@ errata #451034 on early Cortex A8
+	@ vstmdb	sp!,{d8-d15}	@ ABI specification says so
+	mov	$saved_sp,sp
+	sub	$Xfer,sp,#64
+	adr	$K_XX_XX,.LK_00_19
+	bic	$Xfer,$Xfer,#15		@ align for 128-bit stores
+
+	ldmia	$ctx,{$a,$b,$c,$d,$e}	@ load context
+	mov	sp,$Xfer		@ alloca
+
+	vld1.8		{@X[-4&7]-@X[-3&7]},[$inp]!	@ handles unaligned
+	veor		$zero,$zero,$zero
+	vld1.8		{@X[-2&7]-@X[-1&7]},[$inp]!
+	vld1.32		{${K}\[]},[$K_XX_XX,:32]!	@ load K_00_19
+	vrev32.8	@X[-4&7],@X[-4&7]		@ yes, even on
+	vrev32.8	@X[-3&7],@X[-3&7]		@ big-endian...
+	vrev32.8	@X[-2&7],@X[-2&7]
+	vadd.i32	@X[0],@X[-4&7],$K
+	vrev32.8	@X[-1&7],@X[-1&7]
+	vadd.i32	@X[1],@X[-3&7],$K
+	vst1.32		{@X[0]},[$Xfer,:128]!
+	vadd.i32	@X[2],@X[-2&7],$K
+	vst1.32		{@X[1]},[$Xfer,:128]!
+	vst1.32		{@X[2]},[$Xfer,:128]!
+	ldr		$Ki,[sp]			@ big RAW stall
+
+.Loop_neon:
+___
+	&Xupdate_16_31(\&body_00_19);
+	&Xupdate_16_31(\&body_00_19);
+	&Xupdate_16_31(\&body_00_19);
+	&Xupdate_16_31(\&body_00_19);
+	&Xupdate_32_79(\&body_00_19);
+	&Xupdate_32_79(\&body_20_39);
+	&Xupdate_32_79(\&body_20_39);
+	&Xupdate_32_79(\&body_20_39);
+	&Xupdate_32_79(\&body_20_39);
+	&Xupdate_32_79(\&body_20_39);
+	&Xupdate_32_79(\&body_40_59);
+	&Xupdate_32_79(\&body_40_59);
+	&Xupdate_32_79(\&body_40_59);
+	&Xupdate_32_79(\&body_40_59);
+	&Xupdate_32_79(\&body_40_59);
+	&Xupdate_32_79(\&body_20_39);
+	&Xuplast_80(\&body_20_39);
+	&Xloop(\&body_20_39);
+	&Xloop(\&body_20_39);
+	&Xloop(\&body_20_39);
+$code.=<<___;
+	ldmia	$ctx,{$Ki,$t0,$t1,$Xfer}	@ accumulate context
+	add	$a,$a,$Ki
+	ldr	$Ki,[$ctx,#16]
+	add	$b,$b,$t0
+	add	$c,$c,$t1
+	add	$d,$d,$Xfer
+	it	eq
+	moveq	sp,$saved_sp
+	add	$e,$e,$Ki
+	it	ne
+	ldrne	$Ki,[sp]
+	stmia	$ctx,{$a,$b,$c,$d,$e}
+	itt	ne
+	addne	$Xfer,sp,#3*16
+	bne	.Loop_neon
+
+	@ vldmia	sp!,{d8-d15}
+	ldmia	sp!,{r4-r12,pc}
+.size	sha1_block_data_order_neon,.-sha1_block_data_order_neon
+#endif
+___
+}}}
+#####################################################################
+# ARMv8 stuff
+#
+{{{
+my ($ABCD,$E,$E0,$E1)=map("q$_",(0..3));
+my @MSG=map("q$_",(4..7));
+my @Kxx=map("q$_",(8..11));
+my ($W0,$W1,$ABCD_SAVE)=map("q$_",(12..14));
+
+$code.=<<___;
+#if __ARM_MAX_ARCH__>=7
+
+# if defined(__thumb2__)
+#  define INST(a,b,c,d)	.byte	c,d|0xf,a,b
+# else
+#  define INST(a,b,c,d)	.byte	a,b,c,d|0x10
+# endif
+
+.type	sha1_block_data_order_armv8,%function
+.align	5
+sha1_block_data_order_armv8:
+.LARMv8:
+	vstmdb	sp!,{d8-d15}		@ ABI specification says so
+
+	veor	$E,$E,$E
+	adr	r3,.LK_00_19
+	vld1.32	{$ABCD},[$ctx]!
+	vld1.32	{$E\[0]},[$ctx]
+	sub	$ctx,$ctx,#16
+	vld1.32	{@Kxx[0]\[]},[r3,:32]!
+	vld1.32	{@Kxx[1]\[]},[r3,:32]!
+	vld1.32	{@Kxx[2]\[]},[r3,:32]!
+	vld1.32	{@Kxx[3]\[]},[r3,:32]
+
+.Loop_v8:
+	vld1.8		{@MSG[0]-@MSG[1]},[$inp]!
+	vld1.8		{@MSG[2]-@MSG[3]},[$inp]!
+	vrev32.8	@MSG[0],@MSG[0]
+	vrev32.8	@MSG[1],@MSG[1]
+
+	vadd.i32	$W0,@Kxx[0],@MSG[0]
+	vrev32.8	@MSG[2],@MSG[2]
+	vmov		$ABCD_SAVE,$ABCD	@ offload
+	subs		$len,$len,#1
+
+	vadd.i32	$W1,@Kxx[0],@MSG[1]
+	vrev32.8	@MSG[3],@MSG[3]
+	sha1h		$E1,$ABCD		@ 0
+	sha1c		$ABCD,$E,$W0
+	vadd.i32	$W0,@Kxx[$j],@MSG[2]
+	sha1su0		@MSG[0],@MSG[1],@MSG[2]
+___
+for ($j=0,$i=1;$i<20-3;$i++) {
+my $f=("c","p","m","p")[$i/5];
+$code.=<<___;
+	sha1h		$E0,$ABCD		@ $i
+	sha1$f		$ABCD,$E1,$W1
+	vadd.i32	$W1,@Kxx[$j],@MSG[3]
+	sha1su1		@MSG[0],@MSG[3]
+___
+$code.=<<___ if ($i<20-4);
+	sha1su0		@MSG[1],@MSG[2],@MSG[3]
+___
+	($E0,$E1)=($E1,$E0);	($W0,$W1)=($W1,$W0);
+	push(@MSG,shift(@MSG));	$j++ if ((($i+3)%5)==0);
+}
+$code.=<<___;
+	sha1h		$E0,$ABCD		@ $i
+	sha1p		$ABCD,$E1,$W1
+	vadd.i32	$W1,@Kxx[$j],@MSG[3]
+
+	sha1h		$E1,$ABCD		@ 18
+	sha1p		$ABCD,$E0,$W0
+
+	sha1h		$E0,$ABCD		@ 19
+	sha1p		$ABCD,$E1,$W1
+
+	vadd.i32	$E,$E,$E0
+	vadd.i32	$ABCD,$ABCD,$ABCD_SAVE
+	bne		.Loop_v8
+
+	vst1.32		{$ABCD},[$ctx]!
+	vst1.32		{$E\[0]},[$ctx]
+
+	vldmia	sp!,{d8-d15}
+	ret					@ bx lr
+.size	sha1_block_data_order_armv8,.-sha1_block_data_order_armv8
+#endif
+___
+}}}
+$code.=<<___;
+#if __ARM_MAX_ARCH__>=7
+.comm	OPENSSL_armcap_P,4,4
+#endif
+___
+
+{   my  %opcode = (
+	"sha1c"		=> 0xf2000c40,	"sha1p"		=> 0xf2100c40,
+	"sha1m"		=> 0xf2200c40,	"sha1su0"	=> 0xf2300c40,
+	"sha1h"		=> 0xf3b902c0,	"sha1su1"	=> 0xf3ba0380	);
+
+    sub unsha1 {
+	my ($mnemonic,$arg)=@_;
+
+	if ($arg =~ m/q([0-9]+)(?:,\s*q([0-9]+))?,\s*q([0-9]+)/o) {
+	    my $word = $opcode{$mnemonic}|(($1&7)<<13)|(($1&8)<<19)
+					 |(($2&7)<<17)|(($2&8)<<4)
+					 |(($3&7)<<1) |(($3&8)<<2);
+	    # since ARMv7 instructions are always encoded little-endian.
+	    # correct solution is to use .inst directive, but older
+	    # assemblers don't implement it:-(
+
+	    # this fix-up provides Thumb encoding in conjunction with INST
+	    $word &= ~0x10000000 if (($word & 0x0f000000) == 0x02000000);
+	    sprintf "INST(0x%02x,0x%02x,0x%02x,0x%02x)\t@ %s %s",
+			$word&0xff,($word>>8)&0xff,
+			($word>>16)&0xff,($word>>24)&0xff,
+			$mnemonic,$arg;
+	}
+    }
+}
+
+foreach (split($/,$code)) {
+	s/{q([0-9]+)\[\]}/sprintf "{d%d[],d%d[]}",2*$1,2*$1+1/eo	or
+	s/{q([0-9]+)\[0\]}/sprintf "{d%d[0]}",2*$1/eo;
+
+	s/\b(sha1\w+)\s+(q.*)/unsha1($1,$2)/geo;
+
+	s/\bret\b/bx	lr/o		or
+	s/\bbx\s+lr\b/.word\t0xe12fff1e/o;	# make it possible to compile with -march=armv4
+
+	print $_,$/;
+}
+
+close STDOUT; # enforce flush
diff --git a/crypto/sha/asm/sha1-armv8.pl b/crypto/sha/asm/sha1-armv8.pl
new file mode 100644
index 000000000..84a00bf2a
--- /dev/null
+++ b/crypto/sha/asm/sha1-armv8.pl
@@ -0,0 +1,363 @@
+#! /usr/bin/env perl
+# Copyright 2014-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+#
+# ====================================================================
+# Written by Andy Polyakov  for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+#
+# SHA1 for ARMv8.
+#
+# Performance in cycles per processed byte and improvement coefficient
+# over code generated with "default" compiler:
+#
+#		hardware-assisted	software(*)
+# Apple A7	2.31			4.13 (+14%)
+# Cortex-A53	2.24			8.03 (+97%)
+# Cortex-A57	2.35			7.88 (+74%)
+# Denver	2.13			3.97 (+0%)(**)
+# X-Gene				8.80 (+200%)
+# Mongoose	2.05			6.50 (+160%)
+#
+# (*)	Software results are presented mostly for reference purposes.
+# (**)	Keep in mind that Denver relies on binary translation, which
+#	optimizes compiler output at run-time.
+
+$flavour = shift;
+$output  = shift;
+
+$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+( $xlate="${dir}arm-xlate.pl" and -f $xlate ) or
+( $xlate="${dir}../../perlasm/arm-xlate.pl" and -f $xlate) or
+die "can't locate arm-xlate.pl";
+
+open OUT,"| \"$^X\" $xlate $flavour $output";
+*STDOUT=*OUT;
+
+($ctx,$inp,$num)=("x0","x1","x2");
+@Xw=map("w$_",(3..17,19));
+@Xx=map("x$_",(3..17,19));
+@V=($A,$B,$C,$D,$E)=map("w$_",(20..24));
+($t0,$t1,$t2,$K)=map("w$_",(25..28));
+
+
+sub BODY_00_19 {
+my ($i,$a,$b,$c,$d,$e)=@_;
+my $j=($i+2)&15;
+
+$code.=<<___ if ($i<15 && !($i&1));
+	lsr	@Xx[$i+1],@Xx[$i],#32
+___
+$code.=<<___ if ($i<14 && !($i&1));
+	ldr	@Xx[$i+2],[$inp,#`($i+2)*4-64`]
+___
+$code.=<<___ if ($i<14 && ($i&1));
+#ifdef	__ARMEB__
+	ror	@Xx[$i+1],@Xx[$i+1],#32
+#else
+	rev32	@Xx[$i+1],@Xx[$i+1]
+#endif
+___
+$code.=<<___ if ($i<14);
+	bic	$t0,$d,$b
+	and	$t1,$c,$b
+	ror	$t2,$a,#27
+	add	$d,$d,$K		// future e+=K
+	orr	$t0,$t0,$t1
+	add	$e,$e,$t2		// e+=rot(a,5)
+	ror	$b,$b,#2
+	add	$d,$d,@Xw[($i+1)&15]	// future e+=X[i]
+	add	$e,$e,$t0		// e+=F(b,c,d)
+___
+$code.=<<___ if ($i==19);
+	movz	$K,#0xeba1
+	movk	$K,#0x6ed9,lsl#16
+___
+$code.=<<___ if ($i>=14);
+	 eor	@Xw[$j],@Xw[$j],@Xw[($j+2)&15]
+	bic	$t0,$d,$b
+	and	$t1,$c,$b
+	ror	$t2,$a,#27
+	 eor	@Xw[$j],@Xw[$j],@Xw[($j+8)&15]
+	add	$d,$d,$K		// future e+=K
+	orr	$t0,$t0,$t1
+	add	$e,$e,$t2		// e+=rot(a,5)
+	 eor	@Xw[$j],@Xw[$j],@Xw[($j+13)&15]
+	ror	$b,$b,#2
+	add	$d,$d,@Xw[($i+1)&15]	// future e+=X[i]
+	add	$e,$e,$t0		// e+=F(b,c,d)
+	 ror	@Xw[$j],@Xw[$j],#31
+___
+}
+
+sub BODY_40_59 {
+my ($i,$a,$b,$c,$d,$e)=@_;
+my $j=($i+2)&15;
+
+$code.=<<___ if ($i==59);
+	movz	$K,#0xc1d6
+	movk	$K,#0xca62,lsl#16
+___
+$code.=<<___;
+	orr	$t0,$b,$c
+	and	$t1,$b,$c
+	 eor	@Xw[$j],@Xw[$j],@Xw[($j+2)&15]
+	ror	$t2,$a,#27
+	and	$t0,$t0,$d
+	add	$d,$d,$K		// future e+=K
+	 eor	@Xw[$j],@Xw[$j],@Xw[($j+8)&15]
+	add	$e,$e,$t2		// e+=rot(a,5)
+	orr	$t0,$t0,$t1
+	ror	$b,$b,#2
+	 eor	@Xw[$j],@Xw[$j],@Xw[($j+13)&15]
+	add	$d,$d,@Xw[($i+1)&15]	// future e+=X[i]
+	add	$e,$e,$t0		// e+=F(b,c,d)
+	 ror	@Xw[$j],@Xw[$j],#31
+___
+}
+
+sub BODY_20_39 {
+my ($i,$a,$b,$c,$d,$e)=@_;
+my $j=($i+2)&15;
+
+$code.=<<___ if ($i==39);
+	movz	$K,#0xbcdc
+	movk	$K,#0x8f1b,lsl#16
+___
+$code.=<<___ if ($i<78);
+	 eor	@Xw[$j],@Xw[$j],@Xw[($j+2)&15]
+	eor	$t0,$d,$b
+	ror	$t2,$a,#27
+	add	$d,$d,$K		// future e+=K
+	 eor	@Xw[$j],@Xw[$j],@Xw[($j+8)&15]
+	eor	$t0,$t0,$c
+	add	$e,$e,$t2		// e+=rot(a,5)
+	ror	$b,$b,#2
+	 eor	@Xw[$j],@Xw[$j],@Xw[($j+13)&15]
+	add	$d,$d,@Xw[($i+1)&15]	// future e+=X[i]
+	add	$e,$e,$t0		// e+=F(b,c,d)
+	 ror	@Xw[$j],@Xw[$j],#31
+___
+$code.=<<___ if ($i==78);
+	ldp	@Xw[1],@Xw[2],[$ctx]
+	eor	$t0,$d,$b
+	ror	$t2,$a,#27
+	add	$d,$d,$K		// future e+=K
+	eor	$t0,$t0,$c
+	add	$e,$e,$t2		// e+=rot(a,5)
+	ror	$b,$b,#2
+	add	$d,$d,@Xw[($i+1)&15]	// future e+=X[i]
+	add	$e,$e,$t0		// e+=F(b,c,d)
+___
+$code.=<<___ if ($i==79);
+	ldp	@Xw[3],@Xw[4],[$ctx,#8]
+	eor	$t0,$d,$b
+	ror	$t2,$a,#27
+	eor	$t0,$t0,$c
+	add	$e,$e,$t2		// e+=rot(a,5)
+	ror	$b,$b,#2
+	ldr	@Xw[5],[$ctx,#16]
+	add	$e,$e,$t0		// e+=F(b,c,d)
+___
+}
+
+$code.=<<___;
+#include "arm_arch.h"
+
+.text
+
+.extern	OPENSSL_armcap_P
+.globl	sha1_block_data_order
+.type	sha1_block_data_order,%function
+.align	6
+sha1_block_data_order:
+#ifdef	__ILP32__
+	ldrsw	x16,.LOPENSSL_armcap_P
+#else
+	ldr	x16,.LOPENSSL_armcap_P
+#endif
+	adr	x17,.LOPENSSL_armcap_P
+	add	x16,x16,x17
+	ldr	w16,[x16]
+	tst	w16,#ARMV8_SHA1
+	b.ne	.Lv8_entry
+
+	stp	x29,x30,[sp,#-96]!
+	add	x29,sp,#0
+	stp	x19,x20,[sp,#16]
+	stp	x21,x22,[sp,#32]
+	stp	x23,x24,[sp,#48]
+	stp	x25,x26,[sp,#64]
+	stp	x27,x28,[sp,#80]
+
+	ldp	$A,$B,[$ctx]
+	ldp	$C,$D,[$ctx,#8]
+	ldr	$E,[$ctx,#16]
+
+.Loop:
+	ldr	@Xx[0],[$inp],#64
+	movz	$K,#0x7999
+	sub	$num,$num,#1
+	movk	$K,#0x5a82,lsl#16
+#ifdef	__ARMEB__
+	ror	$Xx[0],@Xx[0],#32
+#else
+	rev32	@Xx[0],@Xx[0]
+#endif
+	add	$E,$E,$K		// warm it up
+	add	$E,$E,@Xw[0]
+___
+for($i=0;$i<20;$i++)	{ &BODY_00_19($i,@V); unshift(@V,pop(@V)); }
+for(;$i<40;$i++)	{ &BODY_20_39($i,@V); unshift(@V,pop(@V)); }
+for(;$i<60;$i++)	{ &BODY_40_59($i,@V); unshift(@V,pop(@V)); }
+for(;$i<80;$i++)	{ &BODY_20_39($i,@V); unshift(@V,pop(@V)); }
+$code.=<<___;
+	add	$B,$B,@Xw[2]
+	add	$C,$C,@Xw[3]
+	add	$A,$A,@Xw[1]
+	add	$D,$D,@Xw[4]
+	add	$E,$E,@Xw[5]
+	stp	$A,$B,[$ctx]
+	stp	$C,$D,[$ctx,#8]
+	str	$E,[$ctx,#16]
+	cbnz	$num,.Loop
+
+	ldp	x19,x20,[sp,#16]
+	ldp	x21,x22,[sp,#32]
+	ldp	x23,x24,[sp,#48]
+	ldp	x25,x26,[sp,#64]
+	ldp	x27,x28,[sp,#80]
+	ldr	x29,[sp],#96
+	ret
+.size	sha1_block_data_order,.-sha1_block_data_order
+___
+{{{
+my ($ABCD,$E,$E0,$E1)=map("v$_.16b",(0..3));
+my @MSG=map("v$_.16b",(4..7));
+my @Kxx=map("v$_.4s",(16..19));
+my ($W0,$W1)=("v20.4s","v21.4s");
+my $ABCD_SAVE="v22.16b";
+
+$code.=<<___;
+.type	sha1_block_armv8,%function
+.align	6
+sha1_block_armv8:
+.Lv8_entry:
+	stp	x29,x30,[sp,#-16]!
+	add	x29,sp,#0
+
+	adr	x4,.Lconst
+	eor	$E,$E,$E
+	ld1.32	{$ABCD},[$ctx],#16
+	ld1.32	{$E}[0],[$ctx]
+	sub	$ctx,$ctx,#16
+	ld1.32	{@Kxx[0]-@Kxx[3]},[x4]
+
+.Loop_hw:
+	ld1	{@MSG[0]-@MSG[3]},[$inp],#64
+	sub	$num,$num,#1
+	rev32	@MSG[0],@MSG[0]
+	rev32	@MSG[1],@MSG[1]
+
+	add.i32	$W0,@Kxx[0],@MSG[0]
+	rev32	@MSG[2],@MSG[2]
+	orr	$ABCD_SAVE,$ABCD,$ABCD	// offload
+
+	add.i32	$W1,@Kxx[0],@MSG[1]
+	rev32	@MSG[3],@MSG[3]
+	sha1h	$E1,$ABCD
+	sha1c	$ABCD,$E,$W0		// 0
+	add.i32	$W0,@Kxx[$j],@MSG[2]
+	sha1su0	@MSG[0],@MSG[1],@MSG[2]
+___
+for ($j=0,$i=1;$i<20-3;$i++) {
+my $f=("c","p","m","p")[$i/5];
+$code.=<<___;
+	sha1h	$E0,$ABCD		// $i
+	sha1$f	$ABCD,$E1,$W1
+	add.i32	$W1,@Kxx[$j],@MSG[3]
+	sha1su1	@MSG[0],@MSG[3]
+___
+$code.=<<___ if ($i<20-4);
+	sha1su0	@MSG[1],@MSG[2],@MSG[3]
+___
+	($E0,$E1)=($E1,$E0);		($W0,$W1)=($W1,$W0);
+	push(@MSG,shift(@MSG));		$j++ if ((($i+3)%5)==0);
+}
+$code.=<<___;
+	sha1h	$E0,$ABCD		// $i
+	sha1p	$ABCD,$E1,$W1
+	add.i32	$W1,@Kxx[$j],@MSG[3]
+
+	sha1h	$E1,$ABCD		// 18
+	sha1p	$ABCD,$E0,$W0
+
+	sha1h	$E0,$ABCD		// 19
+	sha1p	$ABCD,$E1,$W1
+
+	add.i32	$E,$E,$E0
+	add.i32	$ABCD,$ABCD,$ABCD_SAVE
+
+	cbnz	$num,.Loop_hw
+
+	st1.32	{$ABCD},[$ctx],#16
+	st1.32	{$E}[0],[$ctx]
+
+	ldr	x29,[sp],#16
+	ret
+.size	sha1_block_armv8,.-sha1_block_armv8
+.align	6
+.Lconst:
+.long	0x5a827999,0x5a827999,0x5a827999,0x5a827999	//K_00_19
+.long	0x6ed9eba1,0x6ed9eba1,0x6ed9eba1,0x6ed9eba1	//K_20_39
+.long	0x8f1bbcdc,0x8f1bbcdc,0x8f1bbcdc,0x8f1bbcdc	//K_40_59
+.long	0xca62c1d6,0xca62c1d6,0xca62c1d6,0xca62c1d6	//K_60_79
+.LOPENSSL_armcap_P:
+#ifdef	__ILP32__
+.long	OPENSSL_armcap_P-.
+#else
+.quad	OPENSSL_armcap_P-.
+#endif
+.asciz	"SHA1 block transform for ARMv8, CRYPTOGAMS by "
+.align	2
+.comm	OPENSSL_armcap_P,4,4
+___
+}}}
+
+{   my	%opcode = (
+	"sha1c"		=> 0x5e000000,	"sha1p"		=> 0x5e001000,
+	"sha1m"		=> 0x5e002000,	"sha1su0"	=> 0x5e003000,
+	"sha1h"		=> 0x5e280800,	"sha1su1"	=> 0x5e281800	);
+
+    sub unsha1 {
+	my ($mnemonic,$arg)=@_;
+
+	$arg =~ m/[qv]([0-9]+)[^,]*,\s*[qv]([0-9]+)[^,]*(?:,\s*[qv]([0-9]+))?/o
+	&&
+	sprintf ".inst\t0x%08x\t//%s %s",
+			$opcode{$mnemonic}|$1|($2<<5)|($3<<16),
+			$mnemonic,$arg;
+    }
+}
+
+foreach(split("\n",$code)) {
+
+	s/\`([^\`]*)\`/eval($1)/geo;
+
+	s/\b(sha1\w+)\s+([qv].*)/unsha1($1,$2)/geo;
+
+	s/\.\w?32\b//o		and s/\.16b/\.4s/go;
+	m/(ld|st)1[^\[]+\[0\]/o	and s/\.4s/\.s/go;
+
+	print $_,"\n";
+}
+
+close STDOUT;
diff --git a/crypto/sha/asm/sha1-c64xplus.pl b/crypto/sha/asm/sha1-c64xplus.pl
new file mode 100644
index 000000000..4db2bcb06
--- /dev/null
+++ b/crypto/sha/asm/sha1-c64xplus.pl
@@ -0,0 +1,337 @@
+#! /usr/bin/env perl
+# Copyright 2012-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+#
+# ====================================================================
+# Written by Andy Polyakov  for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+#
+# SHA1 for C64x+.
+#
+# November 2011
+#
+# If compared to compiler-generated code with similar characteristics,
+# i.e. compiled with OPENSSL_SMALL_FOOTPRINT and utilizing SPLOOPs,
+# this implementation is 25% smaller and >2x faster. In absolute terms
+# performance is (quite impressive) ~6.5 cycles per processed byte.
+# Fully unrolled assembler would be ~5x larger and is likely to be
+# ~15% faster. It would be free from references to intermediate ring
+# buffer, but put more pressure on L1P [both because the code would be
+# larger and won't be using SPLOOP buffer]. There are no plans to
+# realize fully unrolled variant though...
+#
+# !!! Note that this module uses AMR, which means that all interrupt
+# service routines are expected to preserve it and for own well-being
+# zero it upon entry.
+
+while (($output=shift) && ($output!~/\w[\w\-]*\.\w+$/)) {}
+open STDOUT,">$output";
+
+($CTX,$INP,$NUM) = ("A4","B4","A6");		# arguments
+
+($A,$B,$C,$D,$E, $Arot,$F,$F0,$T,$K) = map("A$_",(16..20, 21..25));
+($X0,$X2,$X8,$X13) = ("A26","B26","A27","B27");
+($TX0,$TX1,$TX2,$TX3) = map("B$_",(28..31));
+($XPA,$XPB) = ("A5","B5");			# X circular buffer
+($Actx,$Bctx,$Cctx,$Dctx,$Ectx) = map("A$_",(3,6..9));	# zaps $NUM
+
+$code=<<___;
+	.text
+
+	.if	.ASSEMBLER_VERSION<7000000
+	.asg	0,__TI_EABI__
+	.endif
+	.if	__TI_EABI__
+	.asg	sha1_block_data_order,_sha1_block_data_order
+	.endif
+
+	.asg	B3,RA
+	.asg	A15,FP
+	.asg	B15,SP
+
+	.if	.BIG_ENDIAN
+	.asg	MV,SWAP2
+	.asg	MV,SWAP4
+	.endif
+
+	.global	_sha1_block_data_order
+_sha1_block_data_order:
+	.asmfunc stack_usage(64)
+	MV	$NUM,A0			; reassign $NUM
+||	MVK	-64,B0
+  [!A0]	BNOP	RA			; if ($NUM==0) return;
+|| [A0]	STW	FP,*SP--[16]		; save frame pointer and alloca(64)
+|| [A0]	MV	SP,FP
+   [A0]	LDW	*${CTX}[0],$A		; load A-E...
+|| [A0]	AND	B0,SP,SP		; align stack at 64 bytes
+   [A0]	LDW	*${CTX}[1],$B
+|| [A0]	SUBAW	SP,2,SP			; reserve two words above buffer
+   [A0]	LDW	*${CTX}[2],$C
+|| [A0]	MVK	0x00404,B0
+   [A0]	LDW	*${CTX}[3],$D
+|| [A0]	MVKH	0x50000,B0		; 0x050404, 64 bytes for $XP[AB]
+   [A0]	LDW	*${CTX}[4],$E
+|| [A0]	MVC	B0,AMR			; setup circular addressing
+	LDNW	*${INP}++,$TX1		; pre-fetch input
+	NOP	1
+
+loop?:
+	MVK	0x00007999,$K
+||	ADDAW	SP,2,$XPA
+||	SUB	A0,1,A0
+||	MVK	13,B0
+	MVKH	0x5a820000,$K		; K_00_19
+||	ADDAW	SP,2,$XPB
+||	MV	$A,$Actx
+||	MV	$B,$Bctx
+;;==================================================
+	SPLOOPD	5			; BODY_00_13
+||	MV	$C,$Cctx
+||	MV	$D,$Dctx
+||	MV	$E,$Ectx
+||	MVC	B0,ILC
+
+	ROTL	$A,5,$Arot
+||	AND	$C,$B,$F
+||	ANDN	$D,$B,$F0
+||	ADD	$K,$E,$T		; T=E+K
+
+	XOR	$F0,$F,$F		; F_00_19(B,C,D)
+||	MV	$D,$E			; E=D
+||	MV	$C,$D			; D=C
+||	SWAP2	$TX1,$TX2
+||	LDNW	*${INP}++,$TX1
+
+	ADD	$F,$T,$T		; T+=F_00_19(B,C,D)
+||	ROTL	$B,30,$C		; C=ROL(B,30)
+||	SWAP4	$TX2,$TX3		; byte swap
+
+	ADD	$Arot,$T,$T		; T+=ROL(A,5)
+||	MV	$A,$B			; B=A
+
+	ADD	$TX3,$T,$A		; A=T+Xi
+||	STW	$TX3,*${XPB}++
+	SPKERNEL
+;;==================================================
+	ROTL	$A,5,$Arot		; BODY_14
+||	AND	$C,$B,$F
+||	ANDN	$D,$B,$F0
+||	ADD	$K,$E,$T		; T=E+K
+
+	XOR	$F0,$F,$F		; F_00_19(B,C,D)
+||	MV	$D,$E			; E=D
+||	MV	$C,$D			; D=C
+||	SWAP2	$TX1,$TX2
+||	LDNW	*${INP}++,$TX1
+
+	ADD	$F,$T,$T		; T+=F_00_19(B,C,D)
+||	ROTL	$B,30,$C		; C=ROL(B,30)
+||	SWAP4	$TX2,$TX2		; byte swap
+||	LDW	*${XPA}++,$X0		; fetches from X ring buffer are
+||	LDW	*${XPB}[4],$X2		; 2 iterations ahead
+
+	ADD	$Arot,$T,$T		; T+=ROL(A,5)
+||	MV	$A,$B			; B=A
+||	LDW	*${XPA}[7],$X8
+||	MV	$TX3,$X13		; ||	LDW	*${XPB}[15],$X13
+||	MV	$TX2,$TX3
+
+	ADD	$TX2,$T,$A		; A=T+Xi
+||	STW	$TX2,*${XPB}++
+;;==================================================
+	ROTL	$A,5,$Arot		; BODY_15
+||	AND	$C,$B,$F
+||	ANDN	$D,$B,$F0
+||	ADD	$K,$E,$T		; T=E+K
+
+	XOR	$F0,$F,$F		; F_00_19(B,C,D)
+||	MV	$D,$E			; E=D
+||	MV	$C,$D			; D=C
+||	SWAP2	$TX1,$TX2
+
+	ADD	$F,$T,$T		; T+=F_00_19(B,C,D)
+||	ROTL	$B,30,$C		; C=ROL(B,30)
+||	SWAP4	$TX2,$TX2		; byte swap
+||	XOR	$X0,$X2,$TX0		; Xupdate XORs are 1 iteration ahead
+||	LDW	*${XPA}++,$X0
+||	LDW	*${XPB}[4],$X2
+
+	ADD	$Arot,$T,$T		; T+=ROL(A,5)
+||	MV	$A,$B			; B=A
+||	XOR	$X8,$X13,$TX1
+||	LDW	*${XPA}[7],$X8
+||	MV	$TX3,$X13		; ||	LDW	*${XPB}[15],$X13
+||	MV	$TX2,$TX3
+
+	ADD	$TX2,$T,$A		; A=T+Xi
+||	STW	$TX2,*${XPB}++
+||	XOR	$TX0,$TX1,$TX1
+||	MVK	3,B0
+;;==================================================
+	SPLOOPD	5			; BODY_16_19
+||	MVC	B0,ILC
+
+	ROTL	$A,5,$Arot
+||	AND	$C,$B,$F
+||	ANDN	$D,$B,$F0
+||	ADD	$K,$E,$T		; T=E+K
+||	ROTL	$TX1,1,$TX2		; Xupdate output
+
+	XOR	$F0,$F,$F		; F_00_19(B,C,D)
+||	MV	$D,$E			; E=D
+||	MV	$C,$D			; D=C
+
+	ADD	$F,$T,$T		; T+=F_00_19(B,C,D)
+||	ROTL	$B,30,$C		; C=ROL(B,30)
+||	XOR	$X0,$X2,$TX0
+||	LDW	*${XPA}++,$X0
+||	LDW	*${XPB}[4],$X2
+
+	ADD	$Arot,$T,$T		; T+=ROL(A,5)
+||	MV	$A,$B			; B=A
+||	XOR	$X8,$X13,$TX1
+||	LDW	*${XPA}[7],$X8
+||	MV	$TX3,$X13		; ||	LDW	*${XPB}[15],$X13
+||	MV	$TX2,$TX3
+
+	ADD	$TX2,$T,$A		; A=T+Xi
+||	STW	$TX2,*${XPB}++
+||	XOR	$TX0,$TX1,$TX1
+	SPKERNEL
+
+	MVK	0xffffeba1,$K
+||	MVK	19,B0
+	MVKH	0x6ed90000,$K		; K_20_39
+___
+sub BODY_20_39 {
+$code.=<<___;
+;;==================================================
+	SPLOOPD	5			; BODY_20_39
+||	MVC	B0,ILC
+
+	ROTL	$A,5,$Arot
+||	XOR	$B,$C,$F
+||	ADD	$K,$E,$T		; T=E+K
+||	ROTL	$TX1,1,$TX2		; Xupdate output
+
+	XOR	$D,$F,$F		; F_20_39(B,C,D)
+||	MV	$D,$E			; E=D
+||	MV	$C,$D			; D=C
+
+	ADD	$F,$T,$T		; T+=F_20_39(B,C,D)
+||	ROTL	$B,30,$C		; C=ROL(B,30)
+||	XOR	$X0,$X2,$TX0
+||	LDW	*${XPA}++,$X0
+||	LDW	*${XPB}[4],$X2
+
+	ADD	$Arot,$T,$T		; T+=ROL(A,5)
+||	MV	$A,$B			; B=A
+||	XOR	$X8,$X13,$TX1
+||	LDW	*${XPA}[7],$X8
+||	MV	$TX3,$X13		; ||	LDW	*${XPB}[15],$X13
+||	MV	$TX2,$TX3
+
+	ADD	$TX2,$T,$A		; A=T+Xi
+||	STW	$TX2,*${XPB}++		; last one is redundant
+||	XOR	$TX0,$TX1,$TX1
+	SPKERNEL
+___
+$code.=<<___ if (!shift);
+	MVK	0xffffbcdc,$K
+	MVKH	0x8f1b0000,$K		; K_40_59
+___
+}	&BODY_20_39();
+$code.=<<___;
+;;==================================================
+	SPLOOPD	5			; BODY_40_59
+||	MVC	B0,ILC
+||	AND	$B,$C,$F
+||	AND	$B,$D,$F0
+
+	ROTL	$A,5,$Arot
+||	XOR	$F0,$F,$F
+||	AND	$C,$D,$F0
+||	ADD	$K,$E,$T		; T=E+K
+||	ROTL	$TX1,1,$TX2		; Xupdate output
+
+	XOR	$F0,$F,$F		; F_40_59(B,C,D)
+||	MV	$D,$E			; E=D
+||	MV	$C,$D			; D=C
+
+	ADD	$F,$T,$T		; T+=F_40_59(B,C,D)
+||	ROTL	$B,30,$C		; C=ROL(B,30)
+||	XOR	$X0,$X2,$TX0
+||	LDW	*${XPA}++,$X0
+||	LDW	*${XPB}[4],$X2
+
+	ADD	$Arot,$T,$T		; T+=ROL(A,5)
+||	MV	$A,$B			; B=A
+||	XOR	$X8,$X13,$TX1
+||	LDW	*${XPA}[7],$X8
+||	MV	$TX3,$X13		; ||	LDW	*${XPB}[15],$X13
+||	MV	$TX2,$TX3
+
+	ADD	$TX2,$T,$A		; A=T+Xi
+||	STW	$TX2,*${XPB}++
+||	XOR	$TX0,$TX1,$TX1
+||	AND	$B,$C,$F
+||	AND	$B,$D,$F0
+	SPKERNEL
+
+	MVK	0xffffc1d6,$K
+||	MVK	18,B0
+	MVKH	0xca620000,$K		; K_60_79
+___
+	&BODY_20_39(-1);		# BODY_60_78
+$code.=<<___;
+;;==================================================
+   [A0]	B	loop?
+||	ROTL	$A,5,$Arot		; BODY_79
+||	XOR	$B,$C,$F
+||	ROTL	$TX1,1,$TX2		; Xupdate output
+
+   [A0]	LDNW	*${INP}++,$TX1		; pre-fetch input
+||	ADD	$K,$E,$T		; T=E+K
+||	XOR	$D,$F,$F		; F_20_39(B,C,D)
+
+	ADD	$F,$T,$T		; T+=F_20_39(B,C,D)
+||	ADD	$Ectx,$D,$E		; E=D,E+=Ectx
+||	ADD	$Dctx,$C,$D		; D=C,D+=Dctx
+||	ROTL	$B,30,$C		; C=ROL(B,30)
+
+	ADD	$Arot,$T,$T		; T+=ROL(A,5)
+||	ADD	$Bctx,$A,$B		; B=A,B+=Bctx
+
+	ADD	$TX2,$T,$A		; A=T+Xi
+
+	ADD	$Actx,$A,$A		; A+=Actx
+||	ADD	$Cctx,$C,$C		; C+=Cctx
+;; end of loop?
+
+	BNOP	RA			; return
+||	MV	FP,SP			; restore stack pointer
+||	LDW	*FP[0],FP		; restore frame pointer
+	STW	$A,*${CTX}[0]		; emit A-E...
+||	MVK	0,B0
+	STW	$B,*${CTX}[1]
+||	MVC	B0,AMR			; clear AMR
+	STW	$C,*${CTX}[2]
+	STW	$D,*${CTX}[3]
+	STW	$E,*${CTX}[4]
+	.endasmfunc
+
+	.sect	.const
+	.cstring "SHA1 block transform for C64x+, CRYPTOGAMS by "
+	.align	4
+___
+
+print $code;
+close STDOUT;
diff --git a/crypto/sha/asm/sha1-ia64.pl b/crypto/sha/asm/sha1-ia64.pl
new file mode 100644
index 000000000..dec21f92d
--- /dev/null
+++ b/crypto/sha/asm/sha1-ia64.pl
@@ -0,0 +1,314 @@
+#! /usr/bin/env perl
+# Copyright 2004-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+#
+# ====================================================================
+# Written by Andy Polyakov  for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+#
+# Eternal question is what's wrong with compiler generated code? The
+# trick is that it's possible to reduce the number of shifts required
+# to perform rotations by maintaining copy of 32-bit value in upper
+# bits of 64-bit register. Just follow mux2 and shrp instructions...
+# Performance under big-endian OS such as HP-UX is 179MBps*1GHz, which
+# is >50% better than HP C and >2x better than gcc.
+
+$output = pop;
+
+$code=<<___;
+.ident  \"sha1-ia64.s, version 1.3\"
+.ident  \"IA-64 ISA artwork by Andy Polyakov \"
+.explicit
+
+___
+
+
+if ($^O eq "hpux") {
+    $ADDP="addp4";
+    for (@ARGV) { $ADDP="add" if (/[\+DD|\-mlp]64/); }
+} else { $ADDP="add"; }
+
+#$human=1;
+if ($human) {	# useful for visual code auditing...
+	($A,$B,$C,$D,$E)   = ("A","B","C","D","E");
+	($h0,$h1,$h2,$h3,$h4) = ("h0","h1","h2","h3","h4");
+	($K_00_19, $K_20_39, $K_40_59, $K_60_79) =
+	    (	"K_00_19","K_20_39","K_40_59","K_60_79"	);
+	@X= (	"X0", "X1", "X2", "X3", "X4", "X5", "X6", "X7",
+		"X8", "X9","X10","X11","X12","X13","X14","X15"	);
+}
+else {
+	($A,$B,$C,$D,$E)   =    ("loc0","loc1","loc2","loc3","loc4");
+	($h0,$h1,$h2,$h3,$h4) = ("loc5","loc6","loc7","loc8","loc9");
+	($K_00_19, $K_20_39, $K_40_59, $K_60_79) =
+	    (	"r14", "r15", "loc10", "loc11"	);
+	@X= (	"r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23",
+		"r24", "r25", "r26", "r27", "r28", "r29", "r30", "r31"	);
+}
+
+sub BODY_00_15 {
+local	*code=shift;
+my	($i,$a,$b,$c,$d,$e)=@_;
+my	$j=$i+1;
+my	$Xn=@X[$j%16];
+
+$code.=<<___ if ($i==0);
+{ .mmi;	ld1	$X[$i]=[inp],2		    // MSB
+	ld1	tmp2=[tmp3],2		};;
+{ .mmi;	ld1	tmp0=[inp],2
+	ld1	tmp4=[tmp3],2		    // LSB
+	dep	$X[$i]=$X[$i],tmp2,8,8	};;
+___
+if ($i<15) {
+	$code.=<<___;
+{ .mmi;	ld1	$Xn=[inp],2		    // forward Xload
+	nop.m	0x0
+	dep	tmp1=tmp0,tmp4,8,8	};;
+{ .mmi;	ld1	tmp2=[tmp3],2		    // forward Xload
+	and	tmp4=$c,$b
+	dep	$X[$i]=$X[$i],tmp1,16,16} //;;
+{ .mmi;	add	$e=$e,$K_00_19		    // e+=K_00_19
+	andcm	tmp1=$d,$b
+	dep.z	tmp5=$a,5,27		};; // a<<5
+{ .mmi;	add	$e=$e,$X[$i]		    // e+=Xload
+	or	tmp4=tmp4,tmp1		    // F_00_19(b,c,d)=(b&c)|(~b&d)
+	extr.u	tmp1=$a,27,5		};; // a>>27
+{ .mmi;	ld1	tmp0=[inp],2		    // forward Xload
+	add	$e=$e,tmp4		    // e+=F_00_19(b,c,d)
+	shrp	$b=tmp6,tmp6,2		}   // b=ROTATE(b,30)
+{ .mmi;	ld1	tmp4=[tmp3],2		    // forward Xload
+	or	tmp5=tmp1,tmp5		    // ROTATE(a,5)
+	mux2	tmp6=$a,0x44		};; // see b in next iteration
+{ .mii;	add	$e=$e,tmp5		    // e+=ROTATE(a,5)
+	dep	$Xn=$Xn,tmp2,8,8	    // forward Xload
+	mux2	$X[$i]=$X[$i],0x44	} //;;
+
+___
+	}
+else	{
+	$code.=<<___;
+{ .mii;	and	tmp3=$c,$b
+	dep	tmp1=tmp0,tmp4,8,8;;
+	dep	$X[$i]=$X[$i],tmp1,16,16} //;;
+{ .mmi;	add	$e=$e,$K_00_19		    // e+=K_00_19
+	andcm	tmp1=$d,$b
+	dep.z	tmp5=$a,5,27		};; // a<<5
+{ .mmi;	add	$e=$e,$X[$i]		    // e+=Xupdate
+	or	tmp4=tmp3,tmp1		    // F_00_19(b,c,d)=(b&c)|(~b&d)
+	extr.u	tmp1=$a,27,5		}   // a>>27
+{ .mmi;	xor	$Xn=$Xn,$X[($j+2)%16]	    // forward Xupdate
+	xor	tmp3=$X[($j+8)%16],$X[($j+13)%16] // forward Xupdate
+	nop.i	0			};;
+{ .mmi;	add	$e=$e,tmp4		    // e+=F_00_19(b,c,d)
+	xor	$Xn=$Xn,tmp3		    // forward Xupdate
+	shrp	$b=tmp6,tmp6,2		}   // b=ROTATE(b,30)
+{ .mmi; or	tmp1=tmp1,tmp5		    // ROTATE(a,5)
+	mux2	tmp6=$a,0x44		};; // see b in next iteration
+{ .mii;	add	$e=$e,tmp1		    // e+=ROTATE(a,5)
+	shrp	$Xn=$Xn,$Xn,31		    // ROTATE(x[0]^x[2]^x[8]^x[13],1)
+	mux2	$X[$i]=$X[$i],0x44	};;
+
+___
+	}
+}
+
+sub BODY_16_19 {
+local	*code=shift;
+my	($i,$a,$b,$c,$d,$e)=@_;
+my	$j=$i+1;
+my	$Xn=@X[$j%16];
+
+$code.=<<___;
+{ .mib;	add	$e=$e,$K_00_19		    // e+=K_00_19
+	dep.z	tmp5=$a,5,27		}   // a<<5
+{ .mib;	andcm	tmp1=$d,$b
+	and	tmp0=$c,$b		};;
+{ .mmi;	add	$e=$e,$X[$i%16]		    // e+=Xupdate
+	or	tmp0=tmp0,tmp1		    // F_00_19(b,c,d)=(b&c)|(~b&d)
+	extr.u	tmp1=$a,27,5		}   // a>>27
+{ .mmi;	xor	$Xn=$Xn,$X[($j+2)%16]	    // forward Xupdate
+	xor	tmp3=$X[($j+8)%16],$X[($j+13)%16]	// forward Xupdate
+	nop.i	0			};;
+{ .mmi;	add	$e=$e,tmp0		    // f+=F_00_19(b,c,d)
+	xor	$Xn=$Xn,tmp3		    // forward Xupdate
+	shrp	$b=tmp6,tmp6,2		}   // b=ROTATE(b,30)
+{ .mmi;	or	tmp1=tmp1,tmp5		    // ROTATE(a,5)
+	mux2	tmp6=$a,0x44		};; // see b in next iteration
+{ .mii;	add	$e=$e,tmp1		    // e+=ROTATE(a,5)
+	shrp	$Xn=$Xn,$Xn,31		    // ROTATE(x[0]^x[2]^x[8]^x[13],1)
+	nop.i	0			};;
+
+___
+}
+
+sub BODY_20_39 {
+local	*code=shift;
+my	($i,$a,$b,$c,$d,$e,$Konst)=@_;
+	$Konst = $K_20_39 if (!defined($Konst));
+my	$j=$i+1;
+my	$Xn=@X[$j%16];
+
+if ($i<79) {
+$code.=<<___;
+{ .mib;	add	$e=$e,$Konst		    // e+=K_XX_XX
+	dep.z	tmp5=$a,5,27		}   // a<<5
+{ .mib;	xor	tmp0=$c,$b
+	xor	$Xn=$Xn,$X[($j+2)%16]	};; // forward Xupdate
+{ .mib;	add	$e=$e,$X[$i%16]		    // e+=Xupdate
+	extr.u	tmp1=$a,27,5		}   // a>>27
+{ .mib;	xor	tmp0=tmp0,$d		    // F_20_39(b,c,d)=b^c^d
+	xor	$Xn=$Xn,$X[($j+8)%16]	};; // forward Xupdate
+{ .mmi;	add	$e=$e,tmp0		    // e+=F_20_39(b,c,d)
+	xor	$Xn=$Xn,$X[($j+13)%16]	    // forward Xupdate
+	shrp	$b=tmp6,tmp6,2		}   // b=ROTATE(b,30)
+{ .mmi;	or	tmp1=tmp1,tmp5		    // ROTATE(a,5)
+	mux2	tmp6=$a,0x44		};; // see b in next iteration
+{ .mii;	add	$e=$e,tmp1		    // e+=ROTATE(a,5)
+	shrp	$Xn=$Xn,$Xn,31		    // ROTATE(x[0]^x[2]^x[8]^x[13],1)
+	nop.i	0			};;
+
+___
+}
+else {
+$code.=<<___;
+{ .mib;	add	$e=$e,$Konst		    // e+=K_60_79
+	dep.z	tmp5=$a,5,27		}   // a<<5
+{ .mib;	xor	tmp0=$c,$b
+	add	$h1=$h1,$a		};; // wrap up
+{ .mib;	add	$e=$e,$X[$i%16]		    // e+=Xupdate
+	extr.u	tmp1=$a,27,5		}   // a>>27
+{ .mib;	xor	tmp0=tmp0,$d		    // F_20_39(b,c,d)=b^c^d
+	add	$h3=$h3,$c		};; // wrap up
+{ .mmi;	add	$e=$e,tmp0		    // e+=F_20_39(b,c,d)
+	or	tmp1=tmp1,tmp5		    // ROTATE(a,5)
+	shrp	$b=tmp6,tmp6,2		};; // b=ROTATE(b,30) ;;?
+{ .mmi;	add	$e=$e,tmp1		    // e+=ROTATE(a,5)
+	add	tmp3=1,inp		    // used in unaligned codepath
+	add	$h4=$h4,$d		};; // wrap up
+
+___
+}
+}
+
+sub BODY_40_59 {
+local	*code=shift;
+my	($i,$a,$b,$c,$d,$e)=@_;
+my	$j=$i+1;
+my	$Xn=@X[$j%16];
+
+$code.=<<___;
+{ .mib;	add	$e=$e,$K_40_59		    // e+=K_40_59
+	dep.z	tmp5=$a,5,27		}   // a<<5
+{ .mib;	and	tmp1=$c,$d
+	xor	tmp0=$c,$d		};;
+{ .mmi;	add	$e=$e,$X[$i%16]		    // e+=Xupdate
+	add	tmp5=tmp5,tmp1		    // a<<5+(c&d)
+	extr.u	tmp1=$a,27,5		}   // a>>27
+{ .mmi;	and	tmp0=tmp0,$b
+	xor	$Xn=$Xn,$X[($j+2)%16]	    // forward Xupdate
+	xor	tmp3=$X[($j+8)%16],$X[($j+13)%16] };;	// forward Xupdate
+{ .mmi;	add	$e=$e,tmp0		    // e+=b&(c^d)
+	add	tmp5=tmp5,tmp1		    // ROTATE(a,5)+(c&d)
+	shrp	$b=tmp6,tmp6,2		}   // b=ROTATE(b,30)
+{ .mmi;	xor	$Xn=$Xn,tmp3
+	mux2	tmp6=$a,0x44		};; // see b in next iteration
+{ .mii;	add	$e=$e,tmp5		    // e+=ROTATE(a,5)+(c&d)
+	shrp	$Xn=$Xn,$Xn,31		    // ROTATE(x[0]^x[2]^x[8]^x[13],1)
+	nop.i	0x0			};;
+
+___
+}
+sub BODY_60_79	{ &BODY_20_39(@_,$K_60_79); }
+
+$code.=<<___;
+.text
+
+tmp0=r8;
+tmp1=r9;
+tmp2=r10;
+tmp3=r11;
+ctx=r32;	// in0
+inp=r33;	// in1
+
+// void sha1_block_data_order(SHA_CTX *c,const void *p,size_t num);
+.global	sha1_block_data_order#
+.proc	sha1_block_data_order#
+.align	32
+sha1_block_data_order:
+	.prologue
+{ .mmi;	alloc	tmp1=ar.pfs,3,14,0,0
+	$ADDP	tmp0=4,ctx
+	.save	ar.lc,r3
+	mov	r3=ar.lc		}
+{ .mmi;	$ADDP	ctx=0,ctx
+	$ADDP	inp=0,inp
+	mov	r2=pr			};;
+tmp4=in2;
+tmp5=loc12;
+tmp6=loc13;
+	.body
+{ .mlx;	ld4	$h0=[ctx],8
+	movl	$K_00_19=0x5a827999	}
+{ .mlx;	ld4	$h1=[tmp0],8
+	movl	$K_20_39=0x6ed9eba1	};;
+{ .mlx;	ld4	$h2=[ctx],8
+	movl	$K_40_59=0x8f1bbcdc	}
+{ .mlx;	ld4	$h3=[tmp0]
+	movl	$K_60_79=0xca62c1d6	};;
+{ .mmi;	ld4	$h4=[ctx],-16
+	add	in2=-1,in2		    // adjust num for ar.lc
+	mov	ar.ec=1			};;
+{ .mmi;	nop.m	0
+	add	tmp3=1,inp
+	mov	ar.lc=in2		};; // brp.loop.imp: too far
+
+.Ldtop:
+{ .mmi;	mov	$A=$h0
+	mov	$B=$h1
+	mux2	tmp6=$h1,0x44		}
+{ .mmi;	mov	$C=$h2
+	mov	$D=$h3
+	mov	$E=$h4			};;
+
+___
+
+{ my $i;
+  my @V=($A,$B,$C,$D,$E);
+
+	for($i=0;$i<16;$i++)	{ &BODY_00_15(\$code,$i,@V); unshift(@V,pop(@V)); }
+	for(;$i<20;$i++)	{ &BODY_16_19(\$code,$i,@V); unshift(@V,pop(@V)); }
+	for(;$i<40;$i++)	{ &BODY_20_39(\$code,$i,@V); unshift(@V,pop(@V)); }
+	for(;$i<60;$i++)	{ &BODY_40_59(\$code,$i,@V); unshift(@V,pop(@V)); }
+	for(;$i<80;$i++)	{ &BODY_60_79(\$code,$i,@V); unshift(@V,pop(@V)); }
+
+	(($V[0] eq $A) and ($V[4] eq $E)) or die;	# double-check
+}
+
+$code.=<<___;
+{ .mmb;	add	$h0=$h0,$A
+	add	$h2=$h2,$C
+	br.ctop.dptk.many	.Ldtop	};;
+.Ldend:
+{ .mmi;	add	tmp0=4,ctx
+	mov	ar.lc=r3		};;
+{ .mmi;	st4	[ctx]=$h0,8
+	st4	[tmp0]=$h1,8		};;
+{ .mmi;	st4	[ctx]=$h2,8
+	st4	[tmp0]=$h3		};;
+{ .mib;	st4	[ctx]=$h4,-16
+	mov	pr=r2,0x1ffff
+	br.ret.sptk.many	b0	};;
+.endp	sha1_block_data_order#
+stringz	"SHA1 block transform for IA64, CRYPTOGAMS by "
+___
+
+open STDOUT,">$output" if $output;
+print $code;
diff --git a/crypto/sha/asm/sha1-mb-x86_64.pl b/crypto/sha/asm/sha1-mb-x86_64.pl
new file mode 100644
index 000000000..51c73c05a
--- /dev/null
+++ b/crypto/sha/asm/sha1-mb-x86_64.pl
@@ -0,0 +1,1582 @@
+#! /usr/bin/env perl
+# Copyright 2013-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+
+# ====================================================================
+# Written by Andy Polyakov  for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+
+# Multi-buffer SHA1 procedure processes n buffers in parallel by
+# placing buffer data to designated lane of SIMD register. n is
+# naturally limited to 4 on pre-AVX2 processors and to 8 on
+# AVX2-capable processors such as Haswell.
+#
+#		this	+aesni(i)	sha1	aesni-sha1	gain(iv)
+# -------------------------------------------------------------------
+# Westmere(ii)	10.7/n	+1.28=3.96(n=4)	5.30	6.66		+68%
+# Atom(ii)	18.1/n	+3.93=8.46(n=4)	9.37	12.8		+51%
+# Sandy Bridge	(8.16	+5.15=13.3)/n	4.99	5.98		+80%
+# Ivy Bridge	(8.08	+5.14=13.2)/n	4.60	5.54		+68%
+# Haswell(iii)	(8.96	+5.00=14.0)/n	3.57	4.55		+160%
+# Skylake	(8.70	+5.00=13.7)/n	3.64	4.20		+145%
+# Bulldozer	(9.76	+5.76=15.5)/n	5.95	6.37		+64%
+#
+# (i)	multi-block CBC encrypt with 128-bit key;
+# (ii)	(HASH+AES)/n does not apply to Westmere for n>3 and Atom,
+#	because of lower AES-NI instruction throughput;
+# (iii)	"this" is for n=8, when we gather twice as much data, result
+#	for n=4 is 8.00+4.44=12.4;
+# (iv)	presented improvement coefficients are asymptotic limits and
+#	in real-life application are somewhat lower, e.g. for 2KB
+#	fragments they range from 30% to 100% (on Haswell);
+
+$flavour = shift;
+$output  = shift;
+if ($flavour =~ /\./) { $output = $flavour; undef $flavour; }
+
+$win64=0; $win64=1 if ($flavour =~ /[nm]asm|mingw64/ || $output =~ /\.asm$/);
+
+$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+( $xlate="${dir}x86_64-xlate.pl" and -f $xlate ) or
+( $xlate="${dir}../../perlasm/x86_64-xlate.pl" and -f $xlate) or
+die "can't locate x86_64-xlate.pl";
+
+$avx=0;
+
+if (`$ENV{CC} -Wa,-v -c -o /dev/null -x assembler /dev/null 2>&1`
+		=~ /GNU assembler version ([2-9]\.[0-9]+)/) {
+	$avx = ($1>=2.19) + ($1>=2.22);
+}
+
+if (!$avx && $win64 && ($flavour =~ /nasm/ || $ENV{ASM} =~ /nasm/) &&
+	   `nasm -v 2>&1` =~ /NASM version ([2-9]\.[0-9]+)/) {
+	$avx = ($1>=2.09) + ($1>=2.10);
+}
+
+if (!$avx && $win64 && ($flavour =~ /masm/ || $ENV{ASM} =~ /ml64/) &&
+	   `ml64 2>&1` =~ /Version ([0-9]+)\./) {
+	$avx = ($1>=10) + ($1>=11);
+}
+
+if (!$avx && `$ENV{CC} -v 2>&1` =~ /((?:^clang|LLVM) version|.*based on LLVM) ([3-9]\.[0-9]+)/) {
+	$avx = ($2>=3.0) + ($2>3.0);
+}
+
+open OUT,"| \"$^X\" \"$xlate\" $flavour \"$output\"";
+*STDOUT=*OUT;
+
+# void sha1_multi_block (
+#     struct {	unsigned int A[8];
+#		unsigned int B[8];
+#		unsigned int C[8];
+#		unsigned int D[8];
+#		unsigned int E[8];	} *ctx,
+#     struct {	void *ptr; int blocks;	} inp[8],
+#     int num);		/* 1 or 2 */
+#
+$ctx="%rdi";	# 1st arg
+$inp="%rsi";	# 2nd arg
+$num="%edx";
+@ptr=map("%r$_",(8..11));
+$Tbl="%rbp";
+
+@V=($A,$B,$C,$D,$E)=map("%xmm$_",(0..4));
+($t0,$t1,$t2,$t3,$tx)=map("%xmm$_",(5..9));
+@Xi=map("%xmm$_",(10..14));
+$K="%xmm15";
+
+if (1) {
+    # Atom-specific optimization aiming to eliminate pshufb with high
+    # registers [and thus get rid of 48 cycles accumulated penalty] 
+    @Xi=map("%xmm$_",(0..4));
+    ($tx,$t0,$t1,$t2,$t3)=map("%xmm$_",(5..9));
+    @V=($A,$B,$C,$D,$E)=map("%xmm$_",(10..14));
+}
+
+$REG_SZ=16;
+
+sub Xi_off {
+my $off = shift;
+
+    $off %= 16; $off *= $REG_SZ;
+    $off<256 ? "$off-128(%rax)" : "$off-256-128(%rbx)";
+}
+
+sub BODY_00_19 {
+my ($i,$a,$b,$c,$d,$e)=@_;
+my $j=$i+1;
+my $k=$i+2;
+
+# Loads are performed 2+3/4 iterations in advance. 3/4 means that out
+# of 4 words you would expect to be loaded per given iteration one is
+# spilled to next iteration. In other words indices in four input
+# streams are distributed as following:
+#
+# $i==0:	0,0,0,0,1,1,1,1,2,2,2,
+# $i==1:	2,3,3,3,
+# $i==2:	3,4,4,4,
+# ...
+# $i==13:	14,15,15,15,
+# $i==14:	15
+# 
+# Then at $i==15 Xupdate is applied one iteration in advance...
+$code.=<<___ if ($i==0);
+	movd		(@ptr[0]),@Xi[0]
+	 lea		`16*4`(@ptr[0]),@ptr[0]
+	movd		(@ptr[1]),@Xi[2]	# borrow @Xi[2]
+	 lea		`16*4`(@ptr[1]),@ptr[1]
+	movd		(@ptr[2]),@Xi[3]	# borrow @Xi[3]
+	 lea		`16*4`(@ptr[2]),@ptr[2]
+	movd		(@ptr[3]),@Xi[4]	# borrow @Xi[4]
+	 lea		`16*4`(@ptr[3]),@ptr[3]
+	punpckldq	@Xi[3],@Xi[0]
+	 movd		`4*$j-16*4`(@ptr[0]),@Xi[1]
+	punpckldq	@Xi[4],@Xi[2]
+	 movd		`4*$j-16*4`(@ptr[1]),$t3
+	punpckldq	@Xi[2],@Xi[0]
+	 movd		`4*$j-16*4`(@ptr[2]),$t2
+	pshufb		$tx,@Xi[0]
+___
+$code.=<<___ if ($i<14);			# just load input
+	 movd		`4*$j-16*4`(@ptr[3]),$t1
+	 punpckldq	$t2,@Xi[1]
+	movdqa	$a,$t2
+	paddd	$K,$e				# e+=K_00_19
+	 punpckldq	$t1,$t3
+	movdqa	$b,$t1
+	movdqa	$b,$t0
+	pslld	\$5,$t2
+	pandn	$d,$t1
+	pand	$c,$t0
+	 punpckldq	$t3,@Xi[1]
+	movdqa	$a,$t3
+
+	movdqa	@Xi[0],`&Xi_off($i)`
+	paddd	@Xi[0],$e			# e+=X[i]
+	 movd		`4*$k-16*4`(@ptr[0]),@Xi[2]
+	psrld	\$27,$t3
+	pxor	$t1,$t0				# Ch(b,c,d)
+	movdqa	$b,$t1
+
+	por	$t3,$t2				# rol(a,5)
+	 movd		`4*$k-16*4`(@ptr[1]),$t3
+	pslld	\$30,$t1
+	paddd	$t0,$e				# e+=Ch(b,c,d)
+
+	psrld	\$2,$b
+	paddd	$t2,$e				# e+=rol(a,5)
+	 pshufb	$tx,@Xi[1]
+	 movd		`4*$k-16*4`(@ptr[2]),$t2
+	por	$t1,$b				# b=rol(b,30)
+___
+$code.=<<___ if ($i==14);			# just load input
+	 movd		`4*$j-16*4`(@ptr[3]),$t1
+	 punpckldq	$t2,@Xi[1]
+	movdqa	$a,$t2
+	paddd	$K,$e				# e+=K_00_19
+	 punpckldq	$t1,$t3
+	movdqa	$b,$t1
+	movdqa	$b,$t0
+	pslld	\$5,$t2
+	 prefetcht0	63(@ptr[0])
+	pandn	$d,$t1
+	pand	$c,$t0
+	 punpckldq	$t3,@Xi[1]
+	movdqa	$a,$t3
+
+	movdqa	@Xi[0],`&Xi_off($i)`
+	paddd	@Xi[0],$e			# e+=X[i]
+	psrld	\$27,$t3
+	pxor	$t1,$t0				# Ch(b,c,d)
+	movdqa	$b,$t1
+	 prefetcht0	63(@ptr[1])
+
+	por	$t3,$t2				# rol(a,5)
+	pslld	\$30,$t1
+	paddd	$t0,$e				# e+=Ch(b,c,d)
+	 prefetcht0	63(@ptr[2])
+
+	psrld	\$2,$b
+	paddd	$t2,$e				# e+=rol(a,5)
+	 pshufb	$tx,@Xi[1]
+	 prefetcht0	63(@ptr[3])
+	por	$t1,$b				# b=rol(b,30)
+___
+$code.=<<___ if ($i>=13 && $i<15);
+	movdqa	`&Xi_off($j+2)`,@Xi[3]		# preload "X[2]"
+___
+$code.=<<___ if ($i>=15);			# apply Xupdate
+	pxor	@Xi[-2],@Xi[1]			# "X[13]"
+	movdqa	`&Xi_off($j+2)`,@Xi[3]		# "X[2]"
+
+	movdqa	$a,$t2
+	 pxor	`&Xi_off($j+8)`,@Xi[1]
+	paddd	$K,$e				# e+=K_00_19
+	movdqa	$b,$t1
+	pslld	\$5,$t2
+	 pxor	@Xi[3],@Xi[1]
+	movdqa	$b,$t0
+	pandn	$d,$t1
+	 movdqa	@Xi[1],$tx
+	pand	$c,$t0
+	movdqa	$a,$t3
+	 psrld	\$31,$tx
+	 paddd	@Xi[1],@Xi[1]
+
+	movdqa	@Xi[0],`&Xi_off($i)`
+	paddd	@Xi[0],$e			# e+=X[i]
+	psrld	\$27,$t3
+	pxor	$t1,$t0				# Ch(b,c,d)
+
+	movdqa	$b,$t1
+	por	$t3,$t2				# rol(a,5)
+	pslld	\$30,$t1
+	paddd	$t0,$e				# e+=Ch(b,c,d)
+
+	psrld	\$2,$b
+	paddd	$t2,$e				# e+=rol(a,5)
+	 por	$tx,@Xi[1]			# rol	\$1,@Xi[1]
+	por	$t1,$b				# b=rol(b,30)
+___
+push(@Xi,shift(@Xi));
+}
+
+sub BODY_20_39 {
+my ($i,$a,$b,$c,$d,$e)=@_;
+my $j=$i+1;
+
+$code.=<<___ if ($i<79);
+	pxor	@Xi[-2],@Xi[1]			# "X[13]"
+	movdqa	`&Xi_off($j+2)`,@Xi[3]		# "X[2]"
+
+	movdqa	$a,$t2
+	movdqa	$d,$t0
+	 pxor	`&Xi_off($j+8)`,@Xi[1]
+	paddd	$K,$e				# e+=K_20_39
+	pslld	\$5,$t2
+	pxor	$b,$t0
+
+	movdqa	$a,$t3
+___
+$code.=<<___ if ($i<72);
+	movdqa	@Xi[0],`&Xi_off($i)`
+___
+$code.=<<___ if ($i<79);
+	paddd	@Xi[0],$e			# e+=X[i]
+	 pxor	@Xi[3],@Xi[1]
+	psrld	\$27,$t3
+	pxor	$c,$t0				# Parity(b,c,d)
+	movdqa	$b,$t1
+
+	pslld	\$30,$t1
+	 movdqa	@Xi[1],$tx
+	por	$t3,$t2				# rol(a,5)
+	 psrld	\$31,$tx
+	paddd	$t0,$e				# e+=Parity(b,c,d)
+	 paddd	@Xi[1],@Xi[1]
+
+	psrld	\$2,$b
+	paddd	$t2,$e				# e+=rol(a,5)
+	 por	$tx,@Xi[1]			# rol(@Xi[1],1)
+	por	$t1,$b				# b=rol(b,30)
+___
+$code.=<<___ if ($i==79);
+	movdqa	$a,$t2
+	paddd	$K,$e				# e+=K_20_39
+	movdqa	$d,$t0
+	pslld	\$5,$t2
+	pxor	$b,$t0
+
+	movdqa	$a,$t3
+	paddd	@Xi[0],$e			# e+=X[i]
+	psrld	\$27,$t3
+	movdqa	$b,$t1
+	pxor	$c,$t0				# Parity(b,c,d)
+
+	pslld	\$30,$t1
+	por	$t3,$t2				# rol(a,5)
+	paddd	$t0,$e				# e+=Parity(b,c,d)
+
+	psrld	\$2,$b
+	paddd	$t2,$e				# e+=rol(a,5)
+	por	$t1,$b				# b=rol(b,30)
+___
+push(@Xi,shift(@Xi));
+}
+
+sub BODY_40_59 {
+my ($i,$a,$b,$c,$d,$e)=@_;
+my $j=$i+1;
+
+$code.=<<___;
+	pxor	@Xi[-2],@Xi[1]			# "X[13]"
+	movdqa	`&Xi_off($j+2)`,@Xi[3]		# "X[2]"
+
+	movdqa	$a,$t2
+	movdqa	$d,$t1
+	 pxor	`&Xi_off($j+8)`,@Xi[1]
+	pxor	@Xi[3],@Xi[1]
+	paddd	$K,$e				# e+=K_40_59
+	pslld	\$5,$t2
+	movdqa	$a,$t3
+	pand	$c,$t1
+
+	movdqa	$d,$t0
+	 movdqa	@Xi[1],$tx
+	psrld	\$27,$t3
+	paddd	$t1,$e
+	pxor	$c,$t0
+
+	movdqa	@Xi[0],`&Xi_off($i)`
+	paddd	@Xi[0],$e			# e+=X[i]
+	por	$t3,$t2				# rol(a,5)
+	 psrld	\$31,$tx
+	pand	$b,$t0
+	movdqa	$b,$t1
+
+	pslld	\$30,$t1
+	 paddd	@Xi[1],@Xi[1]
+	paddd	$t0,$e				# e+=Maj(b,d,c)
+
+	psrld	\$2,$b
+	paddd	$t2,$e				# e+=rol(a,5)
+	 por	$tx,@Xi[1]			# rol(@X[1],1)
+	por	$t1,$b				# b=rol(b,30)
+___
+push(@Xi,shift(@Xi));
+}
+
+$code.=<<___;
+.text
+
+.extern	OPENSSL_ia32cap_P
+
+.globl	sha1_multi_block
+.type	sha1_multi_block,\@function,3
+.align	32
+sha1_multi_block:
+	mov	OPENSSL_ia32cap_P+4(%rip),%rcx
+	bt	\$61,%rcx			# check SHA bit
+	jc	_shaext_shortcut
+___
+$code.=<<___ if ($avx);
+	test	\$`1<<28`,%ecx
+	jnz	_avx_shortcut
+___
+$code.=<<___;
+	mov	%rsp,%rax
+	push	%rbx
+	push	%rbp
+___
+$code.=<<___ if ($win64);
+	lea	-0xa8(%rsp),%rsp
+	movaps	%xmm6,(%rsp)
+	movaps	%xmm7,0x10(%rsp)
+	movaps	%xmm8,0x20(%rsp)
+	movaps	%xmm9,0x30(%rsp)
+	movaps	%xmm10,-0x78(%rax)
+	movaps	%xmm11,-0x68(%rax)
+	movaps	%xmm12,-0x58(%rax)
+	movaps	%xmm13,-0x48(%rax)
+	movaps	%xmm14,-0x38(%rax)
+	movaps	%xmm15,-0x28(%rax)
+___
+$code.=<<___;
+	sub	\$`$REG_SZ*18`,%rsp
+	and	\$-256,%rsp
+	mov	%rax,`$REG_SZ*17`(%rsp)		# original %rsp
+.Lbody:
+	lea	K_XX_XX(%rip),$Tbl
+	lea	`$REG_SZ*16`(%rsp),%rbx
+
+.Loop_grande:
+	mov	$num,`$REG_SZ*17+8`(%rsp)	# original $num
+	xor	$num,$num
+___
+for($i=0;$i<4;$i++) {
+    $code.=<<___;
+	mov	`16*$i+0`($inp),@ptr[$i]	# input pointer
+	mov	`16*$i+8`($inp),%ecx		# number of blocks
+	cmp	$num,%ecx
+	cmovg	%ecx,$num			# find maximum
+	test	%ecx,%ecx
+	mov	%ecx,`4*$i`(%rbx)		# initialize counters
+	cmovle	$Tbl,@ptr[$i]			# cancel input
+___
+}
+$code.=<<___;
+	test	$num,$num
+	jz	.Ldone
+
+	movdqu	0x00($ctx),$A			# load context
+	 lea	128(%rsp),%rax
+	movdqu	0x20($ctx),$B
+	movdqu	0x40($ctx),$C
+	movdqu	0x60($ctx),$D
+	movdqu	0x80($ctx),$E
+	movdqa	0x60($Tbl),$tx			# pbswap_mask
+	movdqa	-0x20($Tbl),$K			# K_00_19
+	jmp	.Loop
+
+.align	32
+.Loop:
+___
+for($i=0;$i<20;$i++)	{ &BODY_00_19($i,@V); unshift(@V,pop(@V)); }
+$code.="	movdqa	0x00($Tbl),$K\n";	# K_20_39
+for(;$i<40;$i++)	{ &BODY_20_39($i,@V); unshift(@V,pop(@V)); }
+$code.="	movdqa	0x20($Tbl),$K\n";	# K_40_59
+for(;$i<60;$i++)	{ &BODY_40_59($i,@V); unshift(@V,pop(@V)); }
+$code.="	movdqa	0x40($Tbl),$K\n";	# K_60_79
+for(;$i<80;$i++)	{ &BODY_20_39($i,@V); unshift(@V,pop(@V)); }
+$code.=<<___;
+	movdqa	(%rbx),@Xi[0]			# pull counters
+	mov	\$1,%ecx
+	cmp	4*0(%rbx),%ecx			# examinte counters
+	pxor	$t2,$t2
+	cmovge	$Tbl,@ptr[0]			# cancel input
+	cmp	4*1(%rbx),%ecx
+	movdqa	@Xi[0],@Xi[1]
+	cmovge	$Tbl,@ptr[1]
+	cmp	4*2(%rbx),%ecx
+	pcmpgtd	$t2,@Xi[1]			# mask value
+	cmovge	$Tbl,@ptr[2]
+	cmp	4*3(%rbx),%ecx
+	paddd	@Xi[1],@Xi[0]			# counters--
+	cmovge	$Tbl,@ptr[3]
+
+	movdqu	0x00($ctx),$t0
+	pand	@Xi[1],$A
+	movdqu	0x20($ctx),$t1
+	pand	@Xi[1],$B
+	paddd	$t0,$A
+	movdqu	0x40($ctx),$t2
+	pand	@Xi[1],$C
+	paddd	$t1,$B
+	movdqu	0x60($ctx),$t3
+	pand	@Xi[1],$D
+	paddd	$t2,$C
+	movdqu	0x80($ctx),$tx
+	pand	@Xi[1],$E
+	movdqu	$A,0x00($ctx)
+	paddd	$t3,$D
+	movdqu	$B,0x20($ctx)
+	paddd	$tx,$E
+	movdqu	$C,0x40($ctx)
+	movdqu	$D,0x60($ctx)
+	movdqu	$E,0x80($ctx)
+
+	movdqa	@Xi[0],(%rbx)			# save counters
+	movdqa	0x60($Tbl),$tx			# pbswap_mask
+	movdqa	-0x20($Tbl),$K			# K_00_19
+	dec	$num
+	jnz	.Loop
+
+	mov	`$REG_SZ*17+8`(%rsp),$num
+	lea	$REG_SZ($ctx),$ctx
+	lea	`16*$REG_SZ/4`($inp),$inp
+	dec	$num
+	jnz	.Loop_grande
+
+.Ldone:
+	mov	`$REG_SZ*17`(%rsp),%rax		# original %rsp
+___
+$code.=<<___ if ($win64);
+	movaps	-0xb8(%rax),%xmm6
+	movaps	-0xa8(%rax),%xmm7
+	movaps	-0x98(%rax),%xmm8
+	movaps	-0x88(%rax),%xmm9
+	movaps	-0x78(%rax),%xmm10
+	movaps	-0x68(%rax),%xmm11
+	movaps	-0x58(%rax),%xmm12
+	movaps	-0x48(%rax),%xmm13
+	movaps	-0x38(%rax),%xmm14
+	movaps	-0x28(%rax),%xmm15
+___
+$code.=<<___;
+	mov	-16(%rax),%rbp
+	mov	-8(%rax),%rbx
+	lea	(%rax),%rsp
+.Lepilogue:
+	ret
+.size	sha1_multi_block,.-sha1_multi_block
+___
+						{{{
+my ($ABCD0,$E0,$E0_,$BSWAP,$ABCD1,$E1,$E1_)=map("%xmm$_",(0..3,8..10));
+my @MSG0=map("%xmm$_",(4..7));
+my @MSG1=map("%xmm$_",(11..14));
+
+$code.=<<___;
+.type	sha1_multi_block_shaext,\@function,3
+.align	32
+sha1_multi_block_shaext:
+_shaext_shortcut:
+	mov	%rsp,%rax
+	push	%rbx
+	push	%rbp
+___
+$code.=<<___ if ($win64);
+	lea	-0xa8(%rsp),%rsp
+	movaps	%xmm6,(%rsp)
+	movaps	%xmm7,0x10(%rsp)
+	movaps	%xmm8,0x20(%rsp)
+	movaps	%xmm9,0x30(%rsp)
+	movaps	%xmm10,-0x78(%rax)
+	movaps	%xmm11,-0x68(%rax)
+	movaps	%xmm12,-0x58(%rax)
+	movaps	%xmm13,-0x48(%rax)
+	movaps	%xmm14,-0x38(%rax)
+	movaps	%xmm15,-0x28(%rax)
+___
+$code.=<<___;
+	sub	\$`$REG_SZ*18`,%rsp
+	shl	\$1,$num			# we process pair at a time
+	and	\$-256,%rsp
+	lea	0x40($ctx),$ctx			# size optimization
+	mov	%rax,`$REG_SZ*17`(%rsp)		# original %rsp
+.Lbody_shaext:
+	lea	`$REG_SZ*16`(%rsp),%rbx
+	movdqa	K_XX_XX+0x80(%rip),$BSWAP	# byte-n-word swap
+
+.Loop_grande_shaext:
+	mov	$num,`$REG_SZ*17+8`(%rsp)	# original $num
+	xor	$num,$num
+___
+for($i=0;$i<2;$i++) {
+    $code.=<<___;
+	mov	`16*$i+0`($inp),@ptr[$i]	# input pointer
+	mov	`16*$i+8`($inp),%ecx		# number of blocks
+	cmp	$num,%ecx
+	cmovg	%ecx,$num			# find maximum
+	test	%ecx,%ecx
+	mov	%ecx,`4*$i`(%rbx)		# initialize counters
+	cmovle	%rsp,@ptr[$i]			# cancel input
+___
+}
+$code.=<<___;
+	test	$num,$num
+	jz	.Ldone_shaext
+
+	movq		0x00-0x40($ctx),$ABCD0	# a1.a0
+	movq		0x20-0x40($ctx),@MSG0[0]# b1.b0
+	movq		0x40-0x40($ctx),@MSG0[1]# c1.c0
+	movq		0x60-0x40($ctx),@MSG0[2]# d1.d0
+	movq		0x80-0x40($ctx),@MSG0[3]# e1.e0
+
+	punpckldq	@MSG0[0],$ABCD0		# b1.a1.b0.a0
+	punpckldq	@MSG0[2],@MSG0[1]	# d1.c1.d0.c0
+
+	movdqa		$ABCD0,$ABCD1
+	punpcklqdq	@MSG0[1],$ABCD0		# d0.c0.b0.a0
+	punpckhqdq	@MSG0[1],$ABCD1		# d1.c1.b1.a1
+
+	pshufd		\$0b00111111,@MSG0[3],$E0
+	pshufd		\$0b01111111,@MSG0[3],$E1
+	pshufd		\$0b00011011,$ABCD0,$ABCD0
+	pshufd		\$0b00011011,$ABCD1,$ABCD1
+	jmp		.Loop_shaext
+
+.align	32
+.Loop_shaext:
+	movdqu		0x00(@ptr[0]),@MSG0[0]
+	 movdqu		0x00(@ptr[1]),@MSG1[0]
+	movdqu		0x10(@ptr[0]),@MSG0[1]
+	 movdqu		0x10(@ptr[1]),@MSG1[1]
+	movdqu		0x20(@ptr[0]),@MSG0[2]
+	pshufb		$BSWAP,@MSG0[0]
+	 movdqu		0x20(@ptr[1]),@MSG1[2]
+	 pshufb		$BSWAP,@MSG1[0]
+	movdqu		0x30(@ptr[0]),@MSG0[3]
+	lea		0x40(@ptr[0]),@ptr[0]
+	pshufb		$BSWAP,@MSG0[1]
+	 movdqu		0x30(@ptr[1]),@MSG1[3]
+	 lea		0x40(@ptr[1]),@ptr[1]
+	 pshufb		$BSWAP,@MSG1[1]
+
+	movdqa		$E0,0x50(%rsp)		# offload
+	paddd		@MSG0[0],$E0
+	 movdqa		$E1,0x70(%rsp)
+	 paddd		@MSG1[0],$E1
+	movdqa		$ABCD0,0x40(%rsp)	# offload
+	movdqa		$ABCD0,$E0_
+	 movdqa		$ABCD1,0x60(%rsp)
+	 movdqa		$ABCD1,$E1_
+	sha1rnds4	\$0,$E0,$ABCD0		# 0-3
+	sha1nexte	@MSG0[1],$E0_
+	 sha1rnds4	\$0,$E1,$ABCD1		# 0-3
+	 sha1nexte	@MSG1[1],$E1_
+	pshufb		$BSWAP,@MSG0[2]
+	prefetcht0	127(@ptr[0])
+	sha1msg1	@MSG0[1],@MSG0[0]
+	 pshufb		$BSWAP,@MSG1[2]
+	 prefetcht0	127(@ptr[1])
+	 sha1msg1	@MSG1[1],@MSG1[0]
+
+	pshufb		$BSWAP,@MSG0[3]
+	movdqa		$ABCD0,$E0
+	 pshufb		$BSWAP,@MSG1[3]
+	 movdqa		$ABCD1,$E1
+	sha1rnds4	\$0,$E0_,$ABCD0		# 4-7
+	sha1nexte	@MSG0[2],$E0
+	 sha1rnds4	\$0,$E1_,$ABCD1		# 4-7
+	 sha1nexte	@MSG1[2],$E1
+	pxor		@MSG0[2],@MSG0[0]
+	sha1msg1	@MSG0[2],@MSG0[1]
+	 pxor		@MSG1[2],@MSG1[0]
+	 sha1msg1	@MSG1[2],@MSG1[1]
+___
+for($i=2;$i<20-4;$i++) {
+$code.=<<___;
+	movdqa		$ABCD0,$E0_
+	 movdqa		$ABCD1,$E1_
+	sha1rnds4	\$`int($i/5)`,$E0,$ABCD0	# 8-11
+	sha1nexte	@MSG0[3],$E0_
+	 sha1rnds4	\$`int($i/5)`,$E1,$ABCD1	# 8-11
+	 sha1nexte	@MSG1[3],$E1_
+	sha1msg2	@MSG0[3],@MSG0[0]
+	 sha1msg2	@MSG1[3],@MSG1[0]
+	pxor		@MSG0[3],@MSG0[1]
+	sha1msg1	@MSG0[3],@MSG0[2]
+	 pxor		@MSG1[3],@MSG1[1]
+	 sha1msg1	@MSG1[3],@MSG1[2]
+___
+	($E0,$E0_)=($E0_,$E0);		($E1,$E1_)=($E1_,$E1);
+	push(@MSG0,shift(@MSG0));	push(@MSG1,shift(@MSG1));
+}
+$code.=<<___;
+	movdqa		$ABCD0,$E0_
+	 movdqa		$ABCD1,$E1_
+	sha1rnds4	\$3,$E0,$ABCD0		# 64-67
+	sha1nexte	@MSG0[3],$E0_
+	 sha1rnds4	\$3,$E1,$ABCD1		# 64-67
+	 sha1nexte	@MSG1[3],$E1_
+	sha1msg2	@MSG0[3],@MSG0[0]
+	 sha1msg2	@MSG1[3],@MSG1[0]
+	pxor		@MSG0[3],@MSG0[1]
+	 pxor		@MSG1[3],@MSG1[1]
+
+	mov		\$1,%ecx
+	pxor		@MSG0[2],@MSG0[2]	# zero
+	cmp		4*0(%rbx),%ecx		# examine counters
+	cmovge		%rsp,@ptr[0]		# cancel input
+
+	movdqa		$ABCD0,$E0
+	 movdqa		$ABCD1,$E1
+	sha1rnds4	\$3,$E0_,$ABCD0		# 68-71
+	sha1nexte	@MSG0[0],$E0
+	 sha1rnds4	\$3,$E1_,$ABCD1		# 68-71
+	 sha1nexte	@MSG1[0],$E1
+	sha1msg2	@MSG0[0],@MSG0[1]
+	 sha1msg2	@MSG1[0],@MSG1[1]
+
+	cmp		4*1(%rbx),%ecx
+	cmovge		%rsp,@ptr[1]
+	movq		(%rbx),@MSG0[0]		# pull counters
+
+	movdqa		$ABCD0,$E0_
+	 movdqa		$ABCD1,$E1_
+	sha1rnds4	\$3,$E0,$ABCD0		# 72-75
+	sha1nexte	@MSG0[1],$E0_
+	 sha1rnds4	\$3,$E1,$ABCD1		# 72-75
+	 sha1nexte	@MSG1[1],$E1_
+
+	pshufd		\$0x00,@MSG0[0],@MSG1[2]
+	pshufd		\$0x55,@MSG0[0],@MSG1[3]
+	movdqa		@MSG0[0],@MSG0[1]
+	pcmpgtd		@MSG0[2],@MSG1[2]
+	pcmpgtd		@MSG0[2],@MSG1[3]
+
+	movdqa		$ABCD0,$E0
+	 movdqa		$ABCD1,$E1
+	sha1rnds4	\$3,$E0_,$ABCD0		# 76-79
+	sha1nexte	$MSG0[2],$E0
+	 sha1rnds4	\$3,$E1_,$ABCD1		# 76-79
+	 sha1nexte	$MSG0[2],$E1
+
+	pcmpgtd		@MSG0[2],@MSG0[1]	# counter mask
+	pand		@MSG1[2],$ABCD0
+	pand		@MSG1[2],$E0
+	 pand		@MSG1[3],$ABCD1
+	 pand		@MSG1[3],$E1
+	paddd		@MSG0[1],@MSG0[0]	# counters--
+
+	paddd		0x40(%rsp),$ABCD0
+	paddd		0x50(%rsp),$E0
+	 paddd		0x60(%rsp),$ABCD1
+	 paddd		0x70(%rsp),$E1
+
+	movq		@MSG0[0],(%rbx)		# save counters
+	dec		$num
+	jnz		.Loop_shaext
+
+	mov		`$REG_SZ*17+8`(%rsp),$num
+
+	pshufd		\$0b00011011,$ABCD0,$ABCD0
+	pshufd		\$0b00011011,$ABCD1,$ABCD1
+
+	movdqa		$ABCD0,@MSG0[0]
+	punpckldq	$ABCD1,$ABCD0		# b1.b0.a1.a0
+	punpckhdq	$ABCD1,@MSG0[0]		# d1.d0.c1.c0
+	punpckhdq	$E1,$E0			# e1.e0.xx.xx
+	movq		$ABCD0,0x00-0x40($ctx)	# a1.a0
+	psrldq		\$8,$ABCD0
+	movq		@MSG0[0],0x40-0x40($ctx)# c1.c0
+	psrldq		\$8,@MSG0[0]
+	movq		$ABCD0,0x20-0x40($ctx)	# b1.b0
+	psrldq		\$8,$E0
+	movq		@MSG0[0],0x60-0x40($ctx)# d1.d0
+	movq		$E0,0x80-0x40($ctx)	# e1.e0
+
+	lea	`$REG_SZ/2`($ctx),$ctx
+	lea	`16*2`($inp),$inp
+	dec	$num
+	jnz	.Loop_grande_shaext
+
+.Ldone_shaext:
+	#mov	`$REG_SZ*17`(%rsp),%rax		# original %rsp
+___
+$code.=<<___ if ($win64);
+	movaps	-0xb8(%rax),%xmm6
+	movaps	-0xa8(%rax),%xmm7
+	movaps	-0x98(%rax),%xmm8
+	movaps	-0x88(%rax),%xmm9
+	movaps	-0x78(%rax),%xmm10
+	movaps	-0x68(%rax),%xmm11
+	movaps	-0x58(%rax),%xmm12
+	movaps	-0x48(%rax),%xmm13
+	movaps	-0x38(%rax),%xmm14
+	movaps	-0x28(%rax),%xmm15
+___
+$code.=<<___;
+	mov	-16(%rax),%rbp
+	mov	-8(%rax),%rbx
+	lea	(%rax),%rsp
+.Lepilogue_shaext:
+	ret
+.size	sha1_multi_block_shaext,.-sha1_multi_block_shaext
+___
+						}}}
+
+						if ($avx) {{{
+sub BODY_00_19_avx {
+my ($i,$a,$b,$c,$d,$e)=@_;
+my $j=$i+1;
+my $k=$i+2;
+my $vpack = $REG_SZ==16 ? "vpunpckldq" : "vinserti128";
+my $ptr_n = $REG_SZ==16 ? @ptr[1] : @ptr[4];
+
+$code.=<<___ if ($i==0 && $REG_SZ==16);
+	vmovd		(@ptr[0]),@Xi[0]
+	 lea		`16*4`(@ptr[0]),@ptr[0]
+	vmovd		(@ptr[1]),@Xi[2]	# borrow Xi[2]
+	 lea		`16*4`(@ptr[1]),@ptr[1]
+	vpinsrd		\$1,(@ptr[2]),@Xi[0],@Xi[0]
+	 lea		`16*4`(@ptr[2]),@ptr[2]
+	vpinsrd		\$1,(@ptr[3]),@Xi[2],@Xi[2]
+	 lea		`16*4`(@ptr[3]),@ptr[3]
+	 vmovd		`4*$j-16*4`(@ptr[0]),@Xi[1]
+	vpunpckldq	@Xi[2],@Xi[0],@Xi[0]
+	 vmovd		`4*$j-16*4`($ptr_n),$t3
+	vpshufb		$tx,@Xi[0],@Xi[0]
+___
+$code.=<<___ if ($i<15 && $REG_SZ==16);		# just load input
+	 vpinsrd	\$1,`4*$j-16*4`(@ptr[2]),@Xi[1],@Xi[1]
+	 vpinsrd	\$1,`4*$j-16*4`(@ptr[3]),$t3,$t3
+___
+$code.=<<___ if ($i==0 && $REG_SZ==32);
+	vmovd		(@ptr[0]),@Xi[0]
+	 lea		`16*4`(@ptr[0]),@ptr[0]
+	vmovd		(@ptr[4]),@Xi[2]	# borrow Xi[2]
+	 lea		`16*4`(@ptr[4]),@ptr[4]
+	vmovd		(@ptr[1]),$t2
+	 lea		`16*4`(@ptr[1]),@ptr[1]
+	vmovd		(@ptr[5]),$t1
+	 lea		`16*4`(@ptr[5]),@ptr[5]
+	vpinsrd		\$1,(@ptr[2]),@Xi[0],@Xi[0]
+	 lea		`16*4`(@ptr[2]),@ptr[2]
+	vpinsrd		\$1,(@ptr[6]),@Xi[2],@Xi[2]
+	 lea		`16*4`(@ptr[6]),@ptr[6]
+	vpinsrd		\$1,(@ptr[3]),$t2,$t2
+	 lea		`16*4`(@ptr[3]),@ptr[3]
+	vpunpckldq	$t2,@Xi[0],@Xi[0]
+	vpinsrd		\$1,(@ptr[7]),$t1,$t1
+	 lea		`16*4`(@ptr[7]),@ptr[7]
+	vpunpckldq	$t1,@Xi[2],@Xi[2]
+	 vmovd		`4*$j-16*4`(@ptr[0]),@Xi[1]
+	vinserti128	@Xi[2],@Xi[0],@Xi[0]
+	 vmovd		`4*$j-16*4`($ptr_n),$t3
+	vpshufb		$tx,@Xi[0],@Xi[0]
+___
+$code.=<<___ if ($i<15 && $REG_SZ==32);		# just load input
+	 vmovd		`4*$j-16*4`(@ptr[1]),$t2
+	 vmovd		`4*$j-16*4`(@ptr[5]),$t1
+	 vpinsrd	\$1,`4*$j-16*4`(@ptr[2]),@Xi[1],@Xi[1]
+	 vpinsrd	\$1,`4*$j-16*4`(@ptr[6]),$t3,$t3
+	 vpinsrd	\$1,`4*$j-16*4`(@ptr[3]),$t2,$t2
+	 vpunpckldq	$t2,@Xi[1],@Xi[1]
+	 vpinsrd	\$1,`4*$j-16*4`(@ptr[7]),$t1,$t1
+	 vpunpckldq	$t1,$t3,$t3
+___
+$code.=<<___ if ($i<14);
+	vpaddd	$K,$e,$e			# e+=K_00_19
+	vpslld	\$5,$a,$t2
+	vpandn	$d,$b,$t1
+	vpand	$c,$b,$t0
+
+	vmovdqa	@Xi[0],`&Xi_off($i)`
+	vpaddd	@Xi[0],$e,$e			# e+=X[i]
+	 $vpack		$t3,@Xi[1],@Xi[1]
+	vpsrld	\$27,$a,$t3
+	vpxor	$t1,$t0,$t0			# Ch(b,c,d)
+	 vmovd		`4*$k-16*4`(@ptr[0]),@Xi[2]
+
+	vpslld	\$30,$b,$t1
+	vpor	$t3,$t2,$t2			# rol(a,5)
+	 vmovd		`4*$k-16*4`($ptr_n),$t3
+	vpaddd	$t0,$e,$e			# e+=Ch(b,c,d)
+
+	vpsrld	\$2,$b,$b
+	vpaddd	$t2,$e,$e			# e+=rol(a,5)
+	 vpshufb	$tx,@Xi[1],@Xi[1]
+	vpor	$t1,$b,$b			# b=rol(b,30)
+___
+$code.=<<___ if ($i==14);
+	vpaddd	$K,$e,$e			# e+=K_00_19
+	 prefetcht0	63(@ptr[0])
+	vpslld	\$5,$a,$t2
+	vpandn	$d,$b,$t1
+	vpand	$c,$b,$t0
+
+	vmovdqa	@Xi[0],`&Xi_off($i)`
+	vpaddd	@Xi[0],$e,$e			# e+=X[i]
+	 $vpack		$t3,@Xi[1],@Xi[1]
+	vpsrld	\$27,$a,$t3
+	 prefetcht0	63(@ptr[1])
+	vpxor	$t1,$t0,$t0			# Ch(b,c,d)
+
+	vpslld	\$30,$b,$t1
+	vpor	$t3,$t2,$t2			# rol(a,5)
+	 prefetcht0	63(@ptr[2])
+	vpaddd	$t0,$e,$e			# e+=Ch(b,c,d)
+
+	vpsrld	\$2,$b,$b
+	vpaddd	$t2,$e,$e			# e+=rol(a,5)
+	 prefetcht0	63(@ptr[3])
+	 vpshufb	$tx,@Xi[1],@Xi[1]
+	vpor	$t1,$b,$b			# b=rol(b,30)
+___
+$code.=<<___ if ($i>=13 && $i<15);
+	vmovdqa	`&Xi_off($j+2)`,@Xi[3]		# preload "X[2]"
+___
+$code.=<<___ if ($i>=15);			# apply Xupdate
+	vpxor	@Xi[-2],@Xi[1],@Xi[1]		# "X[13]"
+	vmovdqa	`&Xi_off($j+2)`,@Xi[3]		# "X[2]"
+
+	vpaddd	$K,$e,$e			# e+=K_00_19
+	vpslld	\$5,$a,$t2
+	vpandn	$d,$b,$t1
+	 `"prefetcht0	63(@ptr[4])"		if ($i==15 && $REG_SZ==32)`
+	vpand	$c,$b,$t0
+
+	vmovdqa	@Xi[0],`&Xi_off($i)`
+	vpaddd	@Xi[0],$e,$e			# e+=X[i]
+	 vpxor	`&Xi_off($j+8)`,@Xi[1],@Xi[1]
+	vpsrld	\$27,$a,$t3
+	vpxor	$t1,$t0,$t0			# Ch(b,c,d)
+	 vpxor	@Xi[3],@Xi[1],@Xi[1]
+	 `"prefetcht0	63(@ptr[5])"		if ($i==15 && $REG_SZ==32)`
+
+	vpslld	\$30,$b,$t1
+	vpor	$t3,$t2,$t2			# rol(a,5)
+	vpaddd	$t0,$e,$e			# e+=Ch(b,c,d)
+	 `"prefetcht0	63(@ptr[6])"		if ($i==15 && $REG_SZ==32)`
+	 vpsrld	\$31,@Xi[1],$tx
+	 vpaddd	@Xi[1],@Xi[1],@Xi[1]
+
+	vpsrld	\$2,$b,$b
+	 `"prefetcht0	63(@ptr[7])"		if ($i==15 && $REG_SZ==32)`
+	vpaddd	$t2,$e,$e			# e+=rol(a,5)
+	 vpor	$tx,@Xi[1],@Xi[1]		# rol	\$1,@Xi[1]
+	vpor	$t1,$b,$b			# b=rol(b,30)
+___
+push(@Xi,shift(@Xi));
+}
+
+sub BODY_20_39_avx {
+my ($i,$a,$b,$c,$d,$e)=@_;
+my $j=$i+1;
+
+$code.=<<___ if ($i<79);
+	vpxor	@Xi[-2],@Xi[1],@Xi[1]		# "X[13]"
+	vmovdqa	`&Xi_off($j+2)`,@Xi[3]		# "X[2]"
+
+	vpslld	\$5,$a,$t2
+	vpaddd	$K,$e,$e			# e+=K_20_39
+	vpxor	$b,$d,$t0
+___
+$code.=<<___ if ($i<72);
+	vmovdqa	@Xi[0],`&Xi_off($i)`
+___
+$code.=<<___ if ($i<79);
+	vpaddd	@Xi[0],$e,$e			# e+=X[i]
+	 vpxor	`&Xi_off($j+8)`,@Xi[1],@Xi[1]
+	vpsrld	\$27,$a,$t3
+	vpxor	$c,$t0,$t0			# Parity(b,c,d)
+	 vpxor	@Xi[3],@Xi[1],@Xi[1]
+
+	vpslld	\$30,$b,$t1
+	vpor	$t3,$t2,$t2			# rol(a,5)
+	vpaddd	$t0,$e,$e			# e+=Parity(b,c,d)
+	 vpsrld	\$31,@Xi[1],$tx
+	 vpaddd	@Xi[1],@Xi[1],@Xi[1]
+
+	vpsrld	\$2,$b,$b
+	vpaddd	$t2,$e,$e			# e+=rol(a,5)
+	 vpor	$tx,@Xi[1],@Xi[1]		# rol(@Xi[1],1)
+	vpor	$t1,$b,$b			# b=rol(b,30)
+___
+$code.=<<___ if ($i==79);
+	vpslld	\$5,$a,$t2
+	vpaddd	$K,$e,$e			# e+=K_20_39
+	vpxor	$b,$d,$t0
+
+	vpsrld	\$27,$a,$t3
+	vpaddd	@Xi[0],$e,$e			# e+=X[i]
+	vpxor	$c,$t0,$t0			# Parity(b,c,d)
+
+	vpslld	\$30,$b,$t1
+	vpor	$t3,$t2,$t2			# rol(a,5)
+	vpaddd	$t0,$e,$e			# e+=Parity(b,c,d)
+
+	vpsrld	\$2,$b,$b
+	vpaddd	$t2,$e,$e			# e+=rol(a,5)
+	vpor	$t1,$b,$b			# b=rol(b,30)
+___
+push(@Xi,shift(@Xi));
+}
+
+sub BODY_40_59_avx {
+my ($i,$a,$b,$c,$d,$e)=@_;
+my $j=$i+1;
+
+$code.=<<___;
+	vpxor	@Xi[-2],@Xi[1],@Xi[1]		# "X[13]"
+	vmovdqa	`&Xi_off($j+2)`,@Xi[3]		# "X[2]"
+
+	vpaddd	$K,$e,$e			# e+=K_40_59
+	vpslld	\$5,$a,$t2
+	vpand	$c,$d,$t1
+	 vpxor	`&Xi_off($j+8)`,@Xi[1],@Xi[1]
+
+	vpaddd	$t1,$e,$e
+	vpsrld	\$27,$a,$t3
+	vpxor	$c,$d,$t0
+	 vpxor	@Xi[3],@Xi[1],@Xi[1]
+
+	vmovdqu	@Xi[0],`&Xi_off($i)`
+	vpaddd	@Xi[0],$e,$e			# e+=X[i]
+	vpor	$t3,$t2,$t2			# rol(a,5)
+	 vpsrld	\$31,@Xi[1],$tx
+	vpand	$b,$t0,$t0
+	 vpaddd	@Xi[1],@Xi[1],@Xi[1]
+
+	vpslld	\$30,$b,$t1
+	vpaddd	$t0,$e,$e			# e+=Maj(b,d,c)
+
+	vpsrld	\$2,$b,$b
+	vpaddd	$t2,$e,$e			# e+=rol(a,5)
+	 vpor	$tx,@Xi[1],@Xi[1]		# rol(@X[1],1)
+	vpor	$t1,$b,$b			# b=rol(b,30)
+___
+push(@Xi,shift(@Xi));
+}
+
+$code.=<<___;
+.type	sha1_multi_block_avx,\@function,3
+.align	32
+sha1_multi_block_avx:
+_avx_shortcut:
+___
+$code.=<<___ if ($avx>1);
+	shr	\$32,%rcx
+	cmp	\$2,$num
+	jb	.Lavx
+	test	\$`1<<5`,%ecx
+	jnz	_avx2_shortcut
+	jmp	.Lavx
+.align	32
+.Lavx:
+___
+$code.=<<___;
+	mov	%rsp,%rax
+	push	%rbx
+	push	%rbp
+___
+$code.=<<___ if ($win64);
+	lea	-0xa8(%rsp),%rsp
+	movaps	%xmm6,(%rsp)
+	movaps	%xmm7,0x10(%rsp)
+	movaps	%xmm8,0x20(%rsp)
+	movaps	%xmm9,0x30(%rsp)
+	movaps	%xmm10,-0x78(%rax)
+	movaps	%xmm11,-0x68(%rax)
+	movaps	%xmm12,-0x58(%rax)
+	movaps	%xmm13,-0x48(%rax)
+	movaps	%xmm14,-0x38(%rax)
+	movaps	%xmm15,-0x28(%rax)
+___
+$code.=<<___;
+	sub	\$`$REG_SZ*18`, %rsp
+	and	\$-256,%rsp
+	mov	%rax,`$REG_SZ*17`(%rsp)		# original %rsp
+.Lbody_avx:
+	lea	K_XX_XX(%rip),$Tbl
+	lea	`$REG_SZ*16`(%rsp),%rbx
+
+	vzeroupper
+.Loop_grande_avx:
+	mov	$num,`$REG_SZ*17+8`(%rsp)	# original $num
+	xor	$num,$num
+___
+for($i=0;$i<4;$i++) {
+    $code.=<<___;
+	mov	`16*$i+0`($inp),@ptr[$i]	# input pointer
+	mov	`16*$i+8`($inp),%ecx		# number of blocks
+	cmp	$num,%ecx
+	cmovg	%ecx,$num			# find maximum
+	test	%ecx,%ecx
+	mov	%ecx,`4*$i`(%rbx)		# initialize counters
+	cmovle	$Tbl,@ptr[$i]			# cancel input
+___
+}
+$code.=<<___;
+	test	$num,$num
+	jz	.Ldone_avx
+
+	vmovdqu	0x00($ctx),$A			# load context
+	 lea	128(%rsp),%rax
+	vmovdqu	0x20($ctx),$B
+	vmovdqu	0x40($ctx),$C
+	vmovdqu	0x60($ctx),$D
+	vmovdqu	0x80($ctx),$E
+	vmovdqu	0x60($Tbl),$tx			# pbswap_mask
+	jmp	.Loop_avx
+
+.align	32
+.Loop_avx:
+___
+$code.="	vmovdqa	-0x20($Tbl),$K\n";	# K_00_19
+for($i=0;$i<20;$i++)	{ &BODY_00_19_avx($i,@V); unshift(@V,pop(@V)); }
+$code.="	vmovdqa	0x00($Tbl),$K\n";	# K_20_39
+for(;$i<40;$i++)	{ &BODY_20_39_avx($i,@V); unshift(@V,pop(@V)); }
+$code.="	vmovdqa	0x20($Tbl),$K\n";	# K_40_59
+for(;$i<60;$i++)	{ &BODY_40_59_avx($i,@V); unshift(@V,pop(@V)); }
+$code.="	vmovdqa	0x40($Tbl),$K\n";	# K_60_79
+for(;$i<80;$i++)	{ &BODY_20_39_avx($i,@V); unshift(@V,pop(@V)); }
+$code.=<<___;
+	mov	\$1,%ecx
+___
+for($i=0;$i<4;$i++) {
+    $code.=<<___;
+	cmp	`4*$i`(%rbx),%ecx		# examine counters
+	cmovge	$Tbl,@ptr[$i]			# cancel input
+___
+}
+$code.=<<___;
+	vmovdqu	(%rbx),$t0			# pull counters
+	vpxor	$t2,$t2,$t2
+	vmovdqa	$t0,$t1
+	vpcmpgtd $t2,$t1,$t1			# mask value
+	vpaddd	$t1,$t0,$t0			# counters--
+
+	vpand	$t1,$A,$A
+	vpand	$t1,$B,$B
+	vpaddd	0x00($ctx),$A,$A
+	vpand	$t1,$C,$C
+	vpaddd	0x20($ctx),$B,$B
+	vpand	$t1,$D,$D
+	vpaddd	0x40($ctx),$C,$C
+	vpand	$t1,$E,$E
+	vpaddd	0x60($ctx),$D,$D
+	vpaddd	0x80($ctx),$E,$E
+	vmovdqu	$A,0x00($ctx)
+	vmovdqu	$B,0x20($ctx)
+	vmovdqu	$C,0x40($ctx)
+	vmovdqu	$D,0x60($ctx)
+	vmovdqu	$E,0x80($ctx)
+
+	vmovdqu	$t0,(%rbx)			# save counters
+	vmovdqu	0x60($Tbl),$tx			# pbswap_mask
+	dec	$num
+	jnz	.Loop_avx
+
+	mov	`$REG_SZ*17+8`(%rsp),$num
+	lea	$REG_SZ($ctx),$ctx
+	lea	`16*$REG_SZ/4`($inp),$inp
+	dec	$num
+	jnz	.Loop_grande_avx
+
+.Ldone_avx:
+	mov	`$REG_SZ*17`(%rsp),%rax		# original %rsp
+	vzeroupper
+___
+$code.=<<___ if ($win64);
+	movaps	-0xb8(%rax),%xmm6
+	movaps	-0xa8(%rax),%xmm7
+	movaps	-0x98(%rax),%xmm8
+	movaps	-0x88(%rax),%xmm9
+	movaps	-0x78(%rax),%xmm10
+	movaps	-0x68(%rax),%xmm11
+	movaps	-0x58(%rax),%xmm12
+	movaps	-0x48(%rax),%xmm13
+	movaps	-0x38(%rax),%xmm14
+	movaps	-0x28(%rax),%xmm15
+___
+$code.=<<___;
+	mov	-16(%rax),%rbp
+	mov	-8(%rax),%rbx
+	lea	(%rax),%rsp
+.Lepilogue_avx:
+	ret
+.size	sha1_multi_block_avx,.-sha1_multi_block_avx
+___
+
+						if ($avx>1) {
+$code =~ s/\`([^\`]*)\`/eval $1/gem;
+
+$REG_SZ=32;
+
+@ptr=map("%r$_",(12..15,8..11));
+
+@V=($A,$B,$C,$D,$E)=map("%ymm$_",(0..4));
+($t0,$t1,$t2,$t3,$tx)=map("%ymm$_",(5..9));
+@Xi=map("%ymm$_",(10..14));
+$K="%ymm15";
+
+$code.=<<___;
+.type	sha1_multi_block_avx2,\@function,3
+.align	32
+sha1_multi_block_avx2:
+_avx2_shortcut:
+	mov	%rsp,%rax
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+___
+$code.=<<___ if ($win64);
+	lea	-0xa8(%rsp),%rsp
+	movaps	%xmm6,(%rsp)
+	movaps	%xmm7,0x10(%rsp)
+	movaps	%xmm8,0x20(%rsp)
+	movaps	%xmm9,0x30(%rsp)
+	movaps	%xmm10,0x40(%rsp)
+	movaps	%xmm11,0x50(%rsp)
+	movaps	%xmm12,-0x78(%rax)
+	movaps	%xmm13,-0x68(%rax)
+	movaps	%xmm14,-0x58(%rax)
+	movaps	%xmm15,-0x48(%rax)
+___
+$code.=<<___;
+	sub	\$`$REG_SZ*18`, %rsp
+	and	\$-256,%rsp
+	mov	%rax,`$REG_SZ*17`(%rsp)		# original %rsp
+.Lbody_avx2:
+	lea	K_XX_XX(%rip),$Tbl
+	shr	\$1,$num
+
+	vzeroupper
+.Loop_grande_avx2:
+	mov	$num,`$REG_SZ*17+8`(%rsp)	# original $num
+	xor	$num,$num
+	lea	`$REG_SZ*16`(%rsp),%rbx
+___
+for($i=0;$i<8;$i++) {
+    $code.=<<___;
+	mov	`16*$i+0`($inp),@ptr[$i]	# input pointer
+	mov	`16*$i+8`($inp),%ecx		# number of blocks
+	cmp	$num,%ecx
+	cmovg	%ecx,$num			# find maximum
+	test	%ecx,%ecx
+	mov	%ecx,`4*$i`(%rbx)		# initialize counters
+	cmovle	$Tbl,@ptr[$i]			# cancel input
+___
+}
+$code.=<<___;
+	vmovdqu	0x00($ctx),$A			# load context
+	 lea	128(%rsp),%rax
+	vmovdqu	0x20($ctx),$B
+	 lea	256+128(%rsp),%rbx
+	vmovdqu	0x40($ctx),$C
+	vmovdqu	0x60($ctx),$D
+	vmovdqu	0x80($ctx),$E
+	vmovdqu	0x60($Tbl),$tx			# pbswap_mask
+	jmp	.Loop_avx2
+
+.align	32
+.Loop_avx2:
+___
+$code.="	vmovdqa	-0x20($Tbl),$K\n";	# K_00_19
+for($i=0;$i<20;$i++)	{ &BODY_00_19_avx($i,@V); unshift(@V,pop(@V)); }
+$code.="	vmovdqa	0x00($Tbl),$K\n";	# K_20_39
+for(;$i<40;$i++)	{ &BODY_20_39_avx($i,@V); unshift(@V,pop(@V)); }
+$code.="	vmovdqa	0x20($Tbl),$K\n";	# K_40_59
+for(;$i<60;$i++)	{ &BODY_40_59_avx($i,@V); unshift(@V,pop(@V)); }
+$code.="	vmovdqa	0x40($Tbl),$K\n";	# K_60_79
+for(;$i<80;$i++)	{ &BODY_20_39_avx($i,@V); unshift(@V,pop(@V)); }
+$code.=<<___;
+	mov	\$1,%ecx
+	lea	`$REG_SZ*16`(%rsp),%rbx
+___
+for($i=0;$i<8;$i++) {
+    $code.=<<___;
+	cmp	`4*$i`(%rbx),%ecx		# examine counters
+	cmovge	$Tbl,@ptr[$i]			# cancel input
+___
+}
+$code.=<<___;
+	vmovdqu	(%rbx),$t0		# pull counters
+	vpxor	$t2,$t2,$t2
+	vmovdqa	$t0,$t1
+	vpcmpgtd $t2,$t1,$t1			# mask value
+	vpaddd	$t1,$t0,$t0			# counters--
+
+	vpand	$t1,$A,$A
+	vpand	$t1,$B,$B
+	vpaddd	0x00($ctx),$A,$A
+	vpand	$t1,$C,$C
+	vpaddd	0x20($ctx),$B,$B
+	vpand	$t1,$D,$D
+	vpaddd	0x40($ctx),$C,$C
+	vpand	$t1,$E,$E
+	vpaddd	0x60($ctx),$D,$D
+	vpaddd	0x80($ctx),$E,$E
+	vmovdqu	$A,0x00($ctx)
+	vmovdqu	$B,0x20($ctx)
+	vmovdqu	$C,0x40($ctx)
+	vmovdqu	$D,0x60($ctx)
+	vmovdqu	$E,0x80($ctx)
+
+	vmovdqu	$t0,(%rbx)			# save counters
+	lea	256+128(%rsp),%rbx
+	vmovdqu	0x60($Tbl),$tx			# pbswap_mask
+	dec	$num
+	jnz	.Loop_avx2
+
+	#mov	`$REG_SZ*17+8`(%rsp),$num
+	#lea	$REG_SZ($ctx),$ctx
+	#lea	`16*$REG_SZ/4`($inp),$inp
+	#dec	$num
+	#jnz	.Loop_grande_avx2
+
+.Ldone_avx2:
+	mov	`$REG_SZ*17`(%rsp),%rax		# original %rsp
+	vzeroupper
+___
+$code.=<<___ if ($win64);
+	movaps	-0xd8(%rax),%xmm6
+	movaps	-0xc8(%rax),%xmm7
+	movaps	-0xb8(%rax),%xmm8
+	movaps	-0xa8(%rax),%xmm9
+	movaps	-0x98(%rax),%xmm10
+	movaps	-0x88(%rax),%xmm11
+	movaps	-0x78(%rax),%xmm12
+	movaps	-0x68(%rax),%xmm13
+	movaps	-0x58(%rax),%xmm14
+	movaps	-0x48(%rax),%xmm15
+___
+$code.=<<___;
+	mov	-48(%rax),%r15
+	mov	-40(%rax),%r14
+	mov	-32(%rax),%r13
+	mov	-24(%rax),%r12
+	mov	-16(%rax),%rbp
+	mov	-8(%rax),%rbx
+	lea	(%rax),%rsp
+.Lepilogue_avx2:
+	ret
+.size	sha1_multi_block_avx2,.-sha1_multi_block_avx2
+___
+						}	}}}
+$code.=<<___;
+
+.align	256
+	.long	0x5a827999,0x5a827999,0x5a827999,0x5a827999	# K_00_19
+	.long	0x5a827999,0x5a827999,0x5a827999,0x5a827999	# K_00_19
+K_XX_XX:
+	.long	0x6ed9eba1,0x6ed9eba1,0x6ed9eba1,0x6ed9eba1	# K_20_39
+	.long	0x6ed9eba1,0x6ed9eba1,0x6ed9eba1,0x6ed9eba1	# K_20_39
+	.long	0x8f1bbcdc,0x8f1bbcdc,0x8f1bbcdc,0x8f1bbcdc	# K_40_59
+	.long	0x8f1bbcdc,0x8f1bbcdc,0x8f1bbcdc,0x8f1bbcdc	# K_40_59
+	.long	0xca62c1d6,0xca62c1d6,0xca62c1d6,0xca62c1d6	# K_60_79
+	.long	0xca62c1d6,0xca62c1d6,0xca62c1d6,0xca62c1d6	# K_60_79
+	.long	0x00010203,0x04050607,0x08090a0b,0x0c0d0e0f	# pbswap
+	.long	0x00010203,0x04050607,0x08090a0b,0x0c0d0e0f	# pbswap
+	.byte	0xf,0xe,0xd,0xc,0xb,0xa,0x9,0x8,0x7,0x6,0x5,0x4,0x3,0x2,0x1,0x0
+	.asciz	"SHA1 multi-block transform for x86_64, CRYPTOGAMS by "
+___
+
+if ($win64) {
+# EXCEPTION_DISPOSITION handler (EXCEPTION_RECORD *rec,ULONG64 frame,
+#		CONTEXT *context,DISPATCHER_CONTEXT *disp)
+$rec="%rcx";
+$frame="%rdx";
+$context="%r8";
+$disp="%r9";
+
+$code.=<<___;
+.extern	__imp_RtlVirtualUnwind
+.type	se_handler,\@abi-omnipotent
+.align	16
+se_handler:
+	push	%rsi
+	push	%rdi
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+	pushfq
+	sub	\$64,%rsp
+
+	mov	120($context),%rax	# pull context->Rax
+	mov	248($context),%rbx	# pull context->Rip
+
+	mov	8($disp),%rsi		# disp->ImageBase
+	mov	56($disp),%r11		# disp->HandlerData
+
+	mov	0(%r11),%r10d		# HandlerData[0]
+	lea	(%rsi,%r10),%r10	# end of prologue label
+	cmp	%r10,%rbx		# context->Rip<.Lbody
+	jb	.Lin_prologue
+
+	mov	152($context),%rax	# pull context->Rsp
+
+	mov	4(%r11),%r10d		# HandlerData[1]
+	lea	(%rsi,%r10),%r10	# epilogue label
+	cmp	%r10,%rbx		# context->Rip>=.Lepilogue
+	jae	.Lin_prologue
+
+	mov	`16*17`(%rax),%rax	# pull saved stack pointer
+
+	mov	-8(%rax),%rbx
+	mov	-16(%rax),%rbp
+	mov	%rbx,144($context)	# restore context->Rbx
+	mov	%rbp,160($context)	# restore context->Rbp
+
+	lea	-24-10*16(%rax),%rsi
+	lea	512($context),%rdi	# &context.Xmm6
+	mov	\$20,%ecx
+	.long	0xa548f3fc		# cld; rep movsq
+
+.Lin_prologue:
+	mov	8(%rax),%rdi
+	mov	16(%rax),%rsi
+	mov	%rax,152($context)	# restore context->Rsp
+	mov	%rsi,168($context)	# restore context->Rsi
+	mov	%rdi,176($context)	# restore context->Rdi
+
+	mov	40($disp),%rdi		# disp->ContextRecord
+	mov	$context,%rsi		# context
+	mov	\$154,%ecx		# sizeof(CONTEXT)
+	.long	0xa548f3fc		# cld; rep movsq
+
+	mov	$disp,%rsi
+	xor	%rcx,%rcx		# arg1, UNW_FLAG_NHANDLER
+	mov	8(%rsi),%rdx		# arg2, disp->ImageBase
+	mov	0(%rsi),%r8		# arg3, disp->ControlPc
+	mov	16(%rsi),%r9		# arg4, disp->FunctionEntry
+	mov	40(%rsi),%r10		# disp->ContextRecord
+	lea	56(%rsi),%r11		# &disp->HandlerData
+	lea	24(%rsi),%r12		# &disp->EstablisherFrame
+	mov	%r10,32(%rsp)		# arg5
+	mov	%r11,40(%rsp)		# arg6
+	mov	%r12,48(%rsp)		# arg7
+	mov	%rcx,56(%rsp)		# arg8, (NULL)
+	call	*__imp_RtlVirtualUnwind(%rip)
+
+	mov	\$1,%eax		# ExceptionContinueSearch
+	add	\$64,%rsp
+	popfq
+	pop	%r15
+	pop	%r14
+	pop	%r13
+	pop	%r12
+	pop	%rbp
+	pop	%rbx
+	pop	%rdi
+	pop	%rsi
+	ret
+.size	se_handler,.-se_handler
+___
+$code.=<<___ if ($avx>1);
+.type	avx2_handler,\@abi-omnipotent
+.align	16
+avx2_handler:
+	push	%rsi
+	push	%rdi
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+	pushfq
+	sub	\$64,%rsp
+
+	mov	120($context),%rax	# pull context->Rax
+	mov	248($context),%rbx	# pull context->Rip
+
+	mov	8($disp),%rsi		# disp->ImageBase
+	mov	56($disp),%r11		# disp->HandlerData
+
+	mov	0(%r11),%r10d		# HandlerData[0]
+	lea	(%rsi,%r10),%r10	# end of prologue label
+	cmp	%r10,%rbx		# context->RipRsp
+
+	mov	4(%r11),%r10d		# HandlerData[1]
+	lea	(%rsi,%r10),%r10	# epilogue label
+	cmp	%r10,%rbx		# context->Rip>=epilogue label
+	jae	.Lin_prologue
+
+	mov	`32*17`($context),%rax	# pull saved stack pointer
+
+	mov	-8(%rax),%rbx
+	mov	-16(%rax),%rbp
+	mov	-24(%rax),%r12
+	mov	-32(%rax),%r13
+	mov	-40(%rax),%r14
+	mov	-48(%rax),%r15
+	mov	%rbx,144($context)	# restore context->Rbx
+	mov	%rbp,160($context)	# restore context->Rbp
+	mov	%r12,216($context)	# restore cotnext->R12
+	mov	%r13,224($context)	# restore cotnext->R13
+	mov	%r14,232($context)	# restore cotnext->R14
+	mov	%r15,240($context)	# restore cotnext->R15
+
+	lea	-56-10*16(%rax),%rsi
+	lea	512($context),%rdi	# &context.Xmm6
+	mov	\$20,%ecx
+	.long	0xa548f3fc		# cld; rep movsq
+
+	jmp	.Lin_prologue
+.size	avx2_handler,.-avx2_handler
+___
+$code.=<<___;
+.section	.pdata
+.align	4
+	.rva	.LSEH_begin_sha1_multi_block
+	.rva	.LSEH_end_sha1_multi_block
+	.rva	.LSEH_info_sha1_multi_block
+	.rva	.LSEH_begin_sha1_multi_block_shaext
+	.rva	.LSEH_end_sha1_multi_block_shaext
+	.rva	.LSEH_info_sha1_multi_block_shaext
+___
+$code.=<<___ if ($avx);
+	.rva	.LSEH_begin_sha1_multi_block_avx
+	.rva	.LSEH_end_sha1_multi_block_avx
+	.rva	.LSEH_info_sha1_multi_block_avx
+___
+$code.=<<___ if ($avx>1);
+	.rva	.LSEH_begin_sha1_multi_block_avx2
+	.rva	.LSEH_end_sha1_multi_block_avx2
+	.rva	.LSEH_info_sha1_multi_block_avx2
+___
+$code.=<<___;
+.section	.xdata
+.align	8
+.LSEH_info_sha1_multi_block:
+	.byte	9,0,0,0
+	.rva	se_handler
+	.rva	.Lbody,.Lepilogue			# HandlerData[]
+.LSEH_info_sha1_multi_block_shaext:
+	.byte	9,0,0,0
+	.rva	se_handler
+	.rva	.Lbody_shaext,.Lepilogue_shaext	# HandlerData[]
+___
+$code.=<<___ if ($avx);
+.LSEH_info_sha1_multi_block_avx:
+	.byte	9,0,0,0
+	.rva	se_handler
+	.rva	.Lbody_avx,.Lepilogue_avx		# HandlerData[]
+___
+$code.=<<___ if ($avx>1);
+.LSEH_info_sha1_multi_block_avx2:
+	.byte	9,0,0,0
+	.rva	avx2_handler
+	.rva	.Lbody_avx2,.Lepilogue_avx2		# HandlerData[]
+___
+}
+####################################################################
+
+sub rex {
+  local *opcode=shift;
+  my ($dst,$src)=@_;
+  my $rex=0;
+
+    $rex|=0x04			if ($dst>=8);
+    $rex|=0x01			if ($src>=8);
+    unshift @opcode,$rex|0x40	if ($rex);
+}
+
+sub sha1rnds4 {
+    if (@_[0] =~ /\$([x0-9a-f]+),\s*%xmm([0-9]+),\s*%xmm([0-9]+)/) {
+      my @opcode=(0x0f,0x3a,0xcc);
+	rex(\@opcode,$3,$2);
+	push @opcode,0xc0|($2&7)|(($3&7)<<3);		# ModR/M
+	my $c=$1;
+	push @opcode,$c=~/^0/?oct($c):$c;
+	return ".byte\t".join(',',@opcode);
+    } else {
+	return "sha1rnds4\t".@_[0];
+    }
+}
+
+sub sha1op38 {
+    my $instr = shift;
+    my %opcodelet = (
+		"sha1nexte" => 0xc8,
+  		"sha1msg1"  => 0xc9,
+		"sha1msg2"  => 0xca	);
+
+    if (defined($opcodelet{$instr}) && @_[0] =~ /%xmm([0-9]+),\s*%xmm([0-9]+)/) {
+      my @opcode=(0x0f,0x38);
+	rex(\@opcode,$2,$1);
+	push @opcode,$opcodelet{$instr};
+	push @opcode,0xc0|($1&7)|(($2&7)<<3);		# ModR/M
+	return ".byte\t".join(',',@opcode);
+    } else {
+	return $instr."\t".@_[0];
+    }
+}
+
+foreach (split("\n",$code)) {
+	s/\`([^\`]*)\`/eval($1)/ge;
+
+	s/\b(sha1rnds4)\s+(.*)/sha1rnds4($2)/geo		or
+	s/\b(sha1[^\s]*)\s+(.*)/sha1op38($1,$2)/geo		or
+
+	s/\b(vmov[dq])\b(.+)%ymm([0-9]+)/$1$2%xmm$3/go		or
+	s/\b(vmovdqu)\b(.+)%x%ymm([0-9]+)/$1$2%xmm$3/go		or
+	s/\b(vpinsr[qd])\b(.+)%ymm([0-9]+),%ymm([0-9]+)/$1$2%xmm$3,%xmm$4/go	or
+	s/\b(vpextr[qd])\b(.+)%ymm([0-9]+)/$1$2%xmm$3/go	or
+	s/\b(vinserti128)\b(\s+)%ymm/$1$2\$1,%xmm/go		or
+	s/\b(vpbroadcast[qd]\s+)%ymm([0-9]+)/$1%xmm$2/go;
+
+	print $_,"\n";
+}
+
+close STDOUT;
diff --git a/crypto/sha/asm/sha1-mips.pl b/crypto/sha/asm/sha1-mips.pl
new file mode 100644
index 000000000..882f9731c
--- /dev/null
+++ b/crypto/sha/asm/sha1-mips.pl
@@ -0,0 +1,457 @@
+#! /usr/bin/env perl
+# Copyright 2009-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+
+# ====================================================================
+# Written by Andy Polyakov  for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+
+# SHA1 block procedure for MIPS.
+
+# Performance improvement is 30% on unaligned input. The "secret" is
+# to deploy lwl/lwr pair to load unaligned input. One could have
+# vectorized Xupdate on MIPSIII/IV, but the goal was to code MIPS32-
+# compatible subroutine. There is room for minor optimization on
+# little-endian platforms...
+
+# September 2012.
+#
+# Add MIPS32r2 code (>25% less instructions).
+
+######################################################################
+# There is a number of MIPS ABI in use, O32 and N32/64 are most
+# widely used. Then there is a new contender: NUBI. It appears that if
+# one picks the latter, it's possible to arrange code in ABI neutral
+# manner. Therefore let's stick to NUBI register layout:
+#
+($zero,$at,$t0,$t1,$t2)=map("\$$_",(0..2,24,25));
+($a0,$a1,$a2,$a3,$a4,$a5,$a6,$a7)=map("\$$_",(4..11));
+($s0,$s1,$s2,$s3,$s4,$s5,$s6,$s7,$s8,$s9,$s10,$s11)=map("\$$_",(12..23));
+($gp,$tp,$sp,$fp,$ra)=map("\$$_",(3,28..31));
+#
+# The return value is placed in $a0. Following coding rules facilitate
+# interoperability:
+#
+# - never ever touch $tp, "thread pointer", former $gp;
+# - copy return value to $t0, former $v0 [or to $a0 if you're adapting
+#   old code];
+# - on O32 populate $a4-$a7 with 'lw $aN,4*N($sp)' if necessary;
+#
+# For reference here is register layout for N32/64 MIPS ABIs:
+#
+# ($zero,$at,$v0,$v1)=map("\$$_",(0..3));
+# ($a0,$a1,$a2,$a3,$a4,$a5,$a6,$a7)=map("\$$_",(4..11));
+# ($t0,$t1,$t2,$t3,$t8,$t9)=map("\$$_",(12..15,24,25));
+# ($s0,$s1,$s2,$s3,$s4,$s5,$s6,$s7)=map("\$$_",(16..23));
+# ($gp,$sp,$fp,$ra)=map("\$$_",(28..31));
+#
+$flavour = shift || "o32"; # supported flavours are o32,n32,64,nubi32,nubi64
+
+if ($flavour =~ /64|n32/i) {
+	$PTR_ADD="dadd";	# incidentally works even on n32
+	$PTR_SUB="dsub";	# incidentally works even on n32
+	$REG_S="sd";
+	$REG_L="ld";
+	$PTR_SLL="dsll";	# incidentally works even on n32
+	$SZREG=8;
+} else {
+	$PTR_ADD="add";
+	$PTR_SUB="sub";
+	$REG_S="sw";
+	$REG_L="lw";
+	$PTR_SLL="sll";
+	$SZREG=4;
+}
+#
+# 
+#
+######################################################################
+
+$big_endian=(`echo MIPSEL | $ENV{CC} -E -`=~/MIPSEL/)?1:0 if ($ENV{CC});
+
+for (@ARGV) {	$output=$_ if (/\w[\w\-]*\.\w+$/);   }
+open STDOUT,">$output";
+
+if (!defined($big_endian))
+            {   $big_endian=(unpack('L',pack('N',1))==1);   }
+
+# offsets of the Most and Least Significant Bytes
+$MSB=$big_endian?0:3;
+$LSB=3&~$MSB;
+
+@X=map("\$$_",(8..23));	# a4-a7,s0-s11
+
+$ctx=$a0;
+$inp=$a1;
+$num=$a2;
+$A="\$1";
+$B="\$2";
+$C="\$3";
+$D="\$7";
+$E="\$24";	@V=($A,$B,$C,$D,$E);
+$t0="\$25";
+$t1=$num;	# $num is offloaded to stack
+$t2="\$30";	# fp
+$K="\$31";	# ra
+
+sub BODY_00_14 {
+my ($i,$a,$b,$c,$d,$e)=@_;
+my $j=$i+1;
+$code.=<<___	if (!$big_endian);
+#if defined(_MIPS_ARCH_MIPS32R2) || defined(_MIPS_ARCH_MIPS64R2)
+	wsbh	@X[$i],@X[$i]	# byte swap($i)
+	rotr	@X[$i],@X[$i],16
+#else
+	srl	$t0,@X[$i],24	# byte swap($i)
+	srl	$t1,@X[$i],8
+	andi	$t2,@X[$i],0xFF00
+	sll	@X[$i],@X[$i],24
+	andi	$t1,0xFF00
+	sll	$t2,$t2,8
+	or	@X[$i],$t0
+	or	$t1,$t2
+	or	@X[$i],$t1
+#endif
+___
+$code.=<<___;
+#if defined(_MIPS_ARCH_MIPS32R2) || defined(_MIPS_ARCH_MIPS64R2)
+	addu	$e,$K		# $i
+	xor	$t0,$c,$d
+	rotr	$t1,$a,27
+	 lwl	@X[$j],$j*4+$MSB($inp)
+	and	$t0,$b
+	addu	$e,$t1
+	 lwr	@X[$j],$j*4+$LSB($inp)
+	xor	$t0,$d
+	addu	$e,@X[$i]
+	rotr	$b,$b,2
+	addu	$e,$t0
+#else
+	 lwl	@X[$j],$j*4+$MSB($inp)
+	sll	$t0,$a,5	# $i
+	addu	$e,$K
+	 lwr	@X[$j],$j*4+$LSB($inp)
+	srl	$t1,$a,27
+	addu	$e,$t0
+	xor	$t0,$c,$d
+	addu	$e,$t1
+	sll	$t2,$b,30
+	and	$t0,$b
+	srl	$b,$b,2
+	xor	$t0,$d
+	addu	$e,@X[$i]
+	or	$b,$t2
+	addu	$e,$t0
+#endif
+___
+}
+
+sub BODY_15_19 {
+my ($i,$a,$b,$c,$d,$e)=@_;
+my $j=$i+1;
+
+$code.=<<___	if (!$big_endian && $i==15);
+#if defined(_MIPS_ARCH_MIPS32R2) || defined(_MIPS_ARCH_MIPS64R2)
+	wsbh	@X[$i],@X[$i]	# byte swap($i)
+	rotr	@X[$i],@X[$i],16
+#else
+	srl	$t0,@X[$i],24	# byte swap($i)
+	srl	$t1,@X[$i],8
+	andi	$t2,@X[$i],0xFF00
+	sll	@X[$i],@X[$i],24
+	andi	$t1,0xFF00
+	sll	$t2,$t2,8
+	or	@X[$i],$t0
+	or	@X[$i],$t1
+	or	@X[$i],$t2
+#endif
+___
+$code.=<<___;
+#if defined(_MIPS_ARCH_MIPS32R2) || defined(_MIPS_ARCH_MIPS64R2)
+	addu	$e,$K		# $i
+	 xor	@X[$j%16],@X[($j+2)%16]
+	xor	$t0,$c,$d
+	rotr	$t1,$a,27
+	 xor	@X[$j%16],@X[($j+8)%16]
+	and	$t0,$b
+	addu	$e,$t1
+	 xor	@X[$j%16],@X[($j+13)%16]
+	xor	$t0,$d
+	addu	$e,@X[$i%16]
+	 rotr	@X[$j%16],@X[$j%16],31
+	rotr	$b,$b,2
+	addu	$e,$t0
+#else
+	 xor	@X[$j%16],@X[($j+2)%16]
+	sll	$t0,$a,5	# $i
+	addu	$e,$K
+	srl	$t1,$a,27
+	addu	$e,$t0
+	 xor	@X[$j%16],@X[($j+8)%16]
+	xor	$t0,$c,$d
+	addu	$e,$t1
+	 xor	@X[$j%16],@X[($j+13)%16]
+	sll	$t2,$b,30
+	and	$t0,$b
+	 srl	$t1,@X[$j%16],31
+	 addu	@X[$j%16],@X[$j%16]
+	srl	$b,$b,2
+	xor	$t0,$d
+	 or	@X[$j%16],$t1
+	addu	$e,@X[$i%16]
+	or	$b,$t2
+	addu	$e,$t0
+#endif
+___
+}
+
+sub BODY_20_39 {
+my ($i,$a,$b,$c,$d,$e)=@_;
+my $j=$i+1;
+$code.=<<___ if ($i<79);
+#if defined(_MIPS_ARCH_MIPS32R2) || defined(_MIPS_ARCH_MIPS64R2)
+	 xor	@X[$j%16],@X[($j+2)%16]
+	addu	$e,$K		# $i
+	rotr	$t1,$a,27
+	 xor	@X[$j%16],@X[($j+8)%16]
+	xor	$t0,$c,$d
+	addu	$e,$t1
+	 xor	@X[$j%16],@X[($j+13)%16]
+	xor	$t0,$b
+	addu	$e,@X[$i%16]
+	 rotr	@X[$j%16],@X[$j%16],31
+	rotr	$b,$b,2
+	addu	$e,$t0
+#else
+	 xor	@X[$j%16],@X[($j+2)%16]
+	sll	$t0,$a,5	# $i
+	addu	$e,$K
+	srl	$t1,$a,27
+	addu	$e,$t0
+	 xor	@X[$j%16],@X[($j+8)%16]
+	xor	$t0,$c,$d
+	addu	$e,$t1
+	 xor	@X[$j%16],@X[($j+13)%16]
+	sll	$t2,$b,30
+	xor	$t0,$b
+	 srl	$t1,@X[$j%16],31
+	 addu	@X[$j%16],@X[$j%16]
+	srl	$b,$b,2
+	addu	$e,@X[$i%16]
+	 or	@X[$j%16],$t1
+	or	$b,$t2
+	addu	$e,$t0
+#endif
+___
+$code.=<<___ if ($i==79);
+#if defined(_MIPS_ARCH_MIPS32R2) || defined(_MIPS_ARCH_MIPS64R2)
+	 lw	@X[0],0($ctx)
+	addu	$e,$K		# $i
+	 lw	@X[1],4($ctx)
+	rotr	$t1,$a,27
+	 lw	@X[2],8($ctx)
+	xor	$t0,$c,$d
+	addu	$e,$t1
+	 lw	@X[3],12($ctx)
+	xor	$t0,$b
+	addu	$e,@X[$i%16]
+	 lw	@X[4],16($ctx)
+	rotr	$b,$b,2
+	addu	$e,$t0
+#else
+	 lw	@X[0],0($ctx)
+	sll	$t0,$a,5	# $i
+	addu	$e,$K
+	 lw	@X[1],4($ctx)
+	srl	$t1,$a,27
+	addu	$e,$t0
+	 lw	@X[2],8($ctx)
+	xor	$t0,$c,$d
+	addu	$e,$t1
+	 lw	@X[3],12($ctx)
+	sll	$t2,$b,30
+	xor	$t0,$b
+	 lw	@X[4],16($ctx)
+	srl	$b,$b,2
+	addu	$e,@X[$i%16]
+	or	$b,$t2
+	addu	$e,$t0
+#endif
+___
+}
+
+sub BODY_40_59 {
+my ($i,$a,$b,$c,$d,$e)=@_;
+my $j=$i+1;
+$code.=<<___ if ($i<79);
+#if defined(_MIPS_ARCH_MIPS32R2) || defined(_MIPS_ARCH_MIPS64R2)
+	addu	$e,$K		# $i
+	and	$t0,$c,$d
+	 xor	@X[$j%16],@X[($j+2)%16]
+	rotr	$t1,$a,27
+	addu	$e,$t0
+	 xor	@X[$j%16],@X[($j+8)%16]
+	xor	$t0,$c,$d
+	addu	$e,$t1
+	 xor	@X[$j%16],@X[($j+13)%16]
+	and	$t0,$b
+	addu	$e,@X[$i%16]
+	 rotr	@X[$j%16],@X[$j%16],31
+	rotr	$b,$b,2
+	addu	$e,$t0
+#else
+	 xor	@X[$j%16],@X[($j+2)%16]
+	sll	$t0,$a,5	# $i
+	addu	$e,$K
+	srl	$t1,$a,27
+	addu	$e,$t0
+	 xor	@X[$j%16],@X[($j+8)%16]
+	and	$t0,$c,$d
+	addu	$e,$t1
+	 xor	@X[$j%16],@X[($j+13)%16]
+	sll	$t2,$b,30
+	addu	$e,$t0
+	 srl	$t1,@X[$j%16],31
+	xor	$t0,$c,$d
+	 addu	@X[$j%16],@X[$j%16]
+	and	$t0,$b
+	srl	$b,$b,2
+	 or	@X[$j%16],$t1
+	addu	$e,@X[$i%16]
+	or	$b,$t2
+	addu	$e,$t0
+#endif
+___
+}
+
+$FRAMESIZE=16;	# large enough to accommodate NUBI saved registers
+$SAVED_REGS_MASK = ($flavour =~ /nubi/i) ? "0xc0fff008" : "0xc0ff0000";
+
+$code=<<___;
+#ifdef OPENSSL_FIPSCANISTER
+# include 
+#endif
+
+#if defined(__mips_smartmips) && !defined(_MIPS_ARCH_MIPS32R2)
+#define _MIPS_ARCH_MIPS32R2
+#endif
+
+.text
+
+.set	noat
+.set	noreorder
+.align	5
+.globl	sha1_block_data_order
+.ent	sha1_block_data_order
+sha1_block_data_order:
+	.frame	$sp,$FRAMESIZE*$SZREG,$ra
+	.mask	$SAVED_REGS_MASK,-$SZREG
+	.set	noreorder
+	$PTR_SUB $sp,$FRAMESIZE*$SZREG
+	$REG_S	$ra,($FRAMESIZE-1)*$SZREG($sp)
+	$REG_S	$fp,($FRAMESIZE-2)*$SZREG($sp)
+	$REG_S	$s11,($FRAMESIZE-3)*$SZREG($sp)
+	$REG_S	$s10,($FRAMESIZE-4)*$SZREG($sp)
+	$REG_S	$s9,($FRAMESIZE-5)*$SZREG($sp)
+	$REG_S	$s8,($FRAMESIZE-6)*$SZREG($sp)
+	$REG_S	$s7,($FRAMESIZE-7)*$SZREG($sp)
+	$REG_S	$s6,($FRAMESIZE-8)*$SZREG($sp)
+	$REG_S	$s5,($FRAMESIZE-9)*$SZREG($sp)
+	$REG_S	$s4,($FRAMESIZE-10)*$SZREG($sp)
+___
+$code.=<<___ if ($flavour =~ /nubi/i);	# optimize non-nubi prologue
+	$REG_S	$s3,($FRAMESIZE-11)*$SZREG($sp)
+	$REG_S	$s2,($FRAMESIZE-12)*$SZREG($sp)
+	$REG_S	$s1,($FRAMESIZE-13)*$SZREG($sp)
+	$REG_S	$s0,($FRAMESIZE-14)*$SZREG($sp)
+	$REG_S	$gp,($FRAMESIZE-15)*$SZREG($sp)
+___
+$code.=<<___;
+	$PTR_SLL $num,6
+	$PTR_ADD $num,$inp
+	$REG_S	$num,0($sp)
+	lw	$A,0($ctx)
+	lw	$B,4($ctx)
+	lw	$C,8($ctx)
+	lw	$D,12($ctx)
+	b	.Loop
+	lw	$E,16($ctx)
+.align	4
+.Loop:
+	.set	reorder
+	lwl	@X[0],$MSB($inp)
+	lui	$K,0x5a82
+	lwr	@X[0],$LSB($inp)
+	ori	$K,0x7999	# K_00_19
+___
+for ($i=0;$i<15;$i++)	{ &BODY_00_14($i,@V); unshift(@V,pop(@V)); }
+for (;$i<20;$i++)	{ &BODY_15_19($i,@V); unshift(@V,pop(@V)); }
+$code.=<<___;
+	lui	$K,0x6ed9
+	ori	$K,0xeba1	# K_20_39
+___
+for (;$i<40;$i++)	{ &BODY_20_39($i,@V); unshift(@V,pop(@V)); }
+$code.=<<___;
+	lui	$K,0x8f1b
+	ori	$K,0xbcdc	# K_40_59
+___
+for (;$i<60;$i++)	{ &BODY_40_59($i,@V); unshift(@V,pop(@V)); }
+$code.=<<___;
+	lui	$K,0xca62
+	ori	$K,0xc1d6	# K_60_79
+___
+for (;$i<80;$i++)	{ &BODY_20_39($i,@V); unshift(@V,pop(@V)); }
+$code.=<<___;
+	$PTR_ADD $inp,64
+	$REG_L	$num,0($sp)
+
+	addu	$A,$X[0]
+	addu	$B,$X[1]
+	sw	$A,0($ctx)
+	addu	$C,$X[2]
+	addu	$D,$X[3]
+	sw	$B,4($ctx)
+	addu	$E,$X[4]
+	sw	$C,8($ctx)
+	sw	$D,12($ctx)
+	sw	$E,16($ctx)
+	.set	noreorder
+	bne	$inp,$num,.Loop
+	nop
+
+	.set	noreorder
+	$REG_L	$ra,($FRAMESIZE-1)*$SZREG($sp)
+	$REG_L	$fp,($FRAMESIZE-2)*$SZREG($sp)
+	$REG_L	$s11,($FRAMESIZE-3)*$SZREG($sp)
+	$REG_L	$s10,($FRAMESIZE-4)*$SZREG($sp)
+	$REG_L	$s9,($FRAMESIZE-5)*$SZREG($sp)
+	$REG_L	$s8,($FRAMESIZE-6)*$SZREG($sp)
+	$REG_L	$s7,($FRAMESIZE-7)*$SZREG($sp)
+	$REG_L	$s6,($FRAMESIZE-8)*$SZREG($sp)
+	$REG_L	$s5,($FRAMESIZE-9)*$SZREG($sp)
+	$REG_L	$s4,($FRAMESIZE-10)*$SZREG($sp)
+___
+$code.=<<___ if ($flavour =~ /nubi/i);
+	$REG_L	$s3,($FRAMESIZE-11)*$SZREG($sp)
+	$REG_L	$s2,($FRAMESIZE-12)*$SZREG($sp)
+	$REG_L	$s1,($FRAMESIZE-13)*$SZREG($sp)
+	$REG_L	$s0,($FRAMESIZE-14)*$SZREG($sp)
+	$REG_L	$gp,($FRAMESIZE-15)*$SZREG($sp)
+___
+$code.=<<___;
+	jr	$ra
+	$PTR_ADD $sp,$FRAMESIZE*$SZREG
+.end	sha1_block_data_order
+.rdata
+.asciiz	"SHA1 for MIPS, CRYPTOGAMS by "
+___
+print $code;
+close STDOUT;
diff --git a/crypto/sha/asm/sha1-parisc.pl b/crypto/sha/asm/sha1-parisc.pl
new file mode 100644
index 000000000..a85d126ff
--- /dev/null
+++ b/crypto/sha/asm/sha1-parisc.pl
@@ -0,0 +1,267 @@
+#! /usr/bin/env perl
+# Copyright 2009-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+
+# ====================================================================
+# Written by Andy Polyakov  for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+
+# SHA1 block procedure for PA-RISC.
+
+# June 2009.
+#
+# On PA-7100LC performance is >30% better than gcc 3.2 generated code
+# for aligned input and >50% better for unaligned. Compared to vendor
+# compiler on PA-8600 it's almost 60% faster in 64-bit build and just
+# few percent faster in 32-bit one (this for aligned input, data for
+# unaligned input is not available).
+#
+# Special thanks to polarhome.com for providing HP-UX account.
+
+$flavour = shift;
+$output = shift;
+open STDOUT,">$output";
+
+if ($flavour =~ /64/) {
+	$LEVEL		="2.0W";
+	$SIZE_T		=8;
+	$FRAME_MARKER	=80;
+	$SAVED_RP	=16;
+	$PUSH		="std";
+	$PUSHMA		="std,ma";
+	$POP		="ldd";
+	$POPMB		="ldd,mb";
+} else {
+	$LEVEL		="1.0";
+	$SIZE_T		=4;
+	$FRAME_MARKER	=48;
+	$SAVED_RP	=20;
+	$PUSH		="stw";
+	$PUSHMA		="stwm";
+	$POP		="ldw";
+	$POPMB		="ldwm";
+}
+
+$FRAME=14*$SIZE_T+$FRAME_MARKER;# 14 saved regs + frame marker
+				#                 [+ argument transfer]
+$ctx="%r26";		# arg0
+$inp="%r25";		# arg1
+$num="%r24";		# arg2
+
+$t0="%r28";
+$t1="%r29";
+$K="%r31";
+
+@X=("%r1", "%r2", "%r3", "%r4", "%r5", "%r6", "%r7", "%r8",
+    "%r9", "%r10","%r11","%r12","%r13","%r14","%r15","%r16",$t0);
+
+@V=($A,$B,$C,$D,$E)=("%r19","%r20","%r21","%r22","%r23");
+
+sub BODY_00_19 {
+my ($i,$a,$b,$c,$d,$e)=@_;
+my $j=$i+1;
+$code.=<<___ if ($i<15);
+	addl	$K,$e,$e	; $i
+	shd	$a,$a,27,$t1
+	addl	@X[$i],$e,$e
+	and	$c,$b,$t0
+	addl	$t1,$e,$e
+	andcm	$d,$b,$t1
+	shd	$b,$b,2,$b
+	or	$t1,$t0,$t0
+	addl	$t0,$e,$e
+___
+$code.=<<___ if ($i>=15);	# with forward Xupdate
+	addl	$K,$e,$e	; $i
+	shd	$a,$a,27,$t1
+	xor	@X[($j+2)%16],@X[$j%16],@X[$j%16]
+	addl	@X[$i%16],$e,$e
+	and	$c,$b,$t0
+	xor	@X[($j+8)%16],@X[$j%16],@X[$j%16]
+	addl	$t1,$e,$e
+	andcm	$d,$b,$t1
+	shd	$b,$b,2,$b
+	or	$t1,$t0,$t0
+	xor	@X[($j+13)%16],@X[$j%16],@X[$j%16]
+	add	$t0,$e,$e
+	shd	@X[$j%16],@X[$j%16],31,@X[$j%16]
+___
+}
+
+sub BODY_20_39 {
+my ($i,$a,$b,$c,$d,$e)=@_;
+my $j=$i+1;
+$code.=<<___ if ($i<79);
+	xor	@X[($j+2)%16],@X[$j%16],@X[$j%16]	; $i
+	addl	$K,$e,$e
+	shd	$a,$a,27,$t1
+	xor	@X[($j+8)%16],@X[$j%16],@X[$j%16]
+	addl	@X[$i%16],$e,$e
+	xor	$b,$c,$t0
+	xor	@X[($j+13)%16],@X[$j%16],@X[$j%16]
+	addl	$t1,$e,$e
+	shd	$b,$b,2,$b
+	xor	$d,$t0,$t0
+	shd	@X[$j%16],@X[$j%16],31,@X[$j%16]
+	addl	$t0,$e,$e
+___
+$code.=<<___ if ($i==79);	# with context load
+	ldw	0($ctx),@X[0]	; $i
+	addl	$K,$e,$e
+	shd	$a,$a,27,$t1
+	ldw	4($ctx),@X[1]
+	addl	@X[$i%16],$e,$e
+	xor	$b,$c,$t0
+	ldw	8($ctx),@X[2]
+	addl	$t1,$e,$e
+	shd	$b,$b,2,$b
+	xor	$d,$t0,$t0
+	ldw	12($ctx),@X[3]
+	addl	$t0,$e,$e
+	ldw	16($ctx),@X[4]
+___
+}
+
+sub BODY_40_59 {
+my ($i,$a,$b,$c,$d,$e)=@_;
+my $j=$i+1;
+$code.=<<___;
+	shd	$a,$a,27,$t1	; $i
+	addl	$K,$e,$e
+	xor	@X[($j+2)%16],@X[$j%16],@X[$j%16]
+	xor	$d,$c,$t0
+	addl	@X[$i%16],$e,$e
+	xor	@X[($j+8)%16],@X[$j%16],@X[$j%16]
+	and	$b,$t0,$t0
+	addl	$t1,$e,$e
+	shd	$b,$b,2,$b
+	xor	@X[($j+13)%16],@X[$j%16],@X[$j%16]
+	addl	$t0,$e,$e
+	and	$d,$c,$t1
+	shd	@X[$j%16],@X[$j%16],31,@X[$j%16]
+	addl	$t1,$e,$e
+___
+}
+
+$code=<<___;
+	.LEVEL	$LEVEL
+	.SPACE	\$TEXT\$
+	.SUBSPA	\$CODE\$,QUAD=0,ALIGN=8,ACCESS=0x2C,CODE_ONLY
+
+	.EXPORT	sha1_block_data_order,ENTRY,ARGW0=GR,ARGW1=GR,ARGW2=GR
+sha1_block_data_order
+	.PROC
+	.CALLINFO	FRAME=`$FRAME-14*$SIZE_T`,NO_CALLS,SAVE_RP,ENTRY_GR=16
+	.ENTRY
+	$PUSH	%r2,-$SAVED_RP(%sp)	; standard prologue
+	$PUSHMA	%r3,$FRAME(%sp)
+	$PUSH	%r4,`-$FRAME+1*$SIZE_T`(%sp)
+	$PUSH	%r5,`-$FRAME+2*$SIZE_T`(%sp)
+	$PUSH	%r6,`-$FRAME+3*$SIZE_T`(%sp)
+	$PUSH	%r7,`-$FRAME+4*$SIZE_T`(%sp)
+	$PUSH	%r8,`-$FRAME+5*$SIZE_T`(%sp)
+	$PUSH	%r9,`-$FRAME+6*$SIZE_T`(%sp)
+	$PUSH	%r10,`-$FRAME+7*$SIZE_T`(%sp)
+	$PUSH	%r11,`-$FRAME+8*$SIZE_T`(%sp)
+	$PUSH	%r12,`-$FRAME+9*$SIZE_T`(%sp)
+	$PUSH	%r13,`-$FRAME+10*$SIZE_T`(%sp)
+	$PUSH	%r14,`-$FRAME+11*$SIZE_T`(%sp)
+	$PUSH	%r15,`-$FRAME+12*$SIZE_T`(%sp)
+	$PUSH	%r16,`-$FRAME+13*$SIZE_T`(%sp)
+
+	ldw	0($ctx),$A
+	ldw	4($ctx),$B
+	ldw	8($ctx),$C
+	ldw	12($ctx),$D
+	ldw	16($ctx),$E
+
+	extru	$inp,31,2,$t0		; t0=inp&3;
+	sh3addl	$t0,%r0,$t0		; t0*=8;
+	subi	32,$t0,$t0		; t0=32-t0;
+	mtctl	$t0,%cr11		; %sar=t0;
+
+L\$oop
+	ldi	3,$t0
+	andcm	$inp,$t0,$t0		; 64-bit neutral
+___
+	for ($i=0;$i<15;$i++) {		# load input block
+	$code.="\tldw	`4*$i`($t0),@X[$i]\n";		}
+$code.=<<___;
+	cmpb,*=	$inp,$t0,L\$aligned
+	ldw	60($t0),@X[15]
+	ldw	64($t0),@X[16]
+___
+	for ($i=0;$i<16;$i++) {		# align input
+	$code.="\tvshd	@X[$i],@X[$i+1],@X[$i]\n";	}
+$code.=<<___;
+L\$aligned
+	ldil	L'0x5a827000,$K		; K_00_19
+	ldo	0x999($K),$K
+___
+for ($i=0;$i<20;$i++)   { &BODY_00_19($i,@V); unshift(@V,pop(@V)); }
+$code.=<<___;
+	ldil	L'0x6ed9e000,$K		; K_20_39
+	ldo	0xba1($K),$K
+___
+
+for (;$i<40;$i++)       { &BODY_20_39($i,@V); unshift(@V,pop(@V)); }
+$code.=<<___;
+	ldil	L'0x8f1bb000,$K		; K_40_59
+	ldo	0xcdc($K),$K
+___
+
+for (;$i<60;$i++)       { &BODY_40_59($i,@V); unshift(@V,pop(@V)); }
+$code.=<<___;
+	ldil	L'0xca62c000,$K		; K_60_79
+	ldo	0x1d6($K),$K
+___
+for (;$i<80;$i++)       { &BODY_20_39($i,@V); unshift(@V,pop(@V)); }
+
+$code.=<<___;
+	addl	@X[0],$A,$A
+	addl	@X[1],$B,$B
+	addl	@X[2],$C,$C
+	addl	@X[3],$D,$D
+	addl	@X[4],$E,$E
+	stw	$A,0($ctx)
+	stw	$B,4($ctx)
+	stw	$C,8($ctx)
+	stw	$D,12($ctx)
+	stw	$E,16($ctx)
+	addib,*<> -1,$num,L\$oop
+	ldo	64($inp),$inp
+
+	$POP	`-$FRAME-$SAVED_RP`(%sp),%r2	; standard epilogue
+	$POP	`-$FRAME+1*$SIZE_T`(%sp),%r4
+	$POP	`-$FRAME+2*$SIZE_T`(%sp),%r5
+	$POP	`-$FRAME+3*$SIZE_T`(%sp),%r6
+	$POP	`-$FRAME+4*$SIZE_T`(%sp),%r7
+	$POP	`-$FRAME+5*$SIZE_T`(%sp),%r8
+	$POP	`-$FRAME+6*$SIZE_T`(%sp),%r9
+	$POP	`-$FRAME+7*$SIZE_T`(%sp),%r10
+	$POP	`-$FRAME+8*$SIZE_T`(%sp),%r11
+	$POP	`-$FRAME+9*$SIZE_T`(%sp),%r12
+	$POP	`-$FRAME+10*$SIZE_T`(%sp),%r13
+	$POP	`-$FRAME+11*$SIZE_T`(%sp),%r14
+	$POP	`-$FRAME+12*$SIZE_T`(%sp),%r15
+	$POP	`-$FRAME+13*$SIZE_T`(%sp),%r16
+	bv	(%r2)
+	.EXIT
+	$POPMB	-$FRAME(%sp),%r3
+	.PROCEND
+	.STRINGZ "SHA1 block transform for PA-RISC, CRYPTOGAMS by "
+___
+
+$code =~ s/\`([^\`]*)\`/eval $1/gem;
+$code =~ s/,\*/,/gm		if ($SIZE_T==4);
+$code =~ s/\bbv\b/bve/gm	if ($SIZE_T==8);
+print $code;
+close STDOUT;
diff --git a/crypto/sha/asm/sha1-ppc.pl b/crypto/sha/asm/sha1-ppc.pl
new file mode 100755
index 000000000..add5a9ea5
--- /dev/null
+++ b/crypto/sha/asm/sha1-ppc.pl
@@ -0,0 +1,351 @@
+#! /usr/bin/env perl
+# Copyright 2006-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+
+# ====================================================================
+# Written by Andy Polyakov  for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+
+# I let hardware handle unaligned input(*), except on page boundaries
+# (see below for details). Otherwise straightforward implementation
+# with X vector in register bank.
+#
+# (*) this means that this module is inappropriate for PPC403? Does
+#     anybody know if pre-POWER3 can sustain unaligned load?
+
+# 			-m64	-m32
+# ----------------------------------
+# PPC970,gcc-4.0.0	+76%	+59%
+# Power6,xlc-7		+68%	+33%
+
+$flavour = shift;
+
+if ($flavour =~ /64/) {
+	$SIZE_T	=8;
+	$LRSAVE	=2*$SIZE_T;
+	$UCMP	="cmpld";
+	$STU	="stdu";
+	$POP	="ld";
+	$PUSH	="std";
+} elsif ($flavour =~ /32/) {
+	$SIZE_T	=4;
+	$LRSAVE	=$SIZE_T;
+	$UCMP	="cmplw";
+	$STU	="stwu";
+	$POP	="lwz";
+	$PUSH	="stw";
+} else { die "nonsense $flavour"; }
+
+# Define endianness based on flavour
+# i.e.: linux64le
+$LITTLE_ENDIAN = ($flavour=~/le$/) ? $SIZE_T : 0;
+
+$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+( $xlate="${dir}ppc-xlate.pl" and -f $xlate ) or
+( $xlate="${dir}../../perlasm/ppc-xlate.pl" and -f $xlate) or
+die "can't locate ppc-xlate.pl";
+
+open STDOUT,"| $^X $xlate $flavour ".shift || die "can't call $xlate: $!";
+
+$FRAME=24*$SIZE_T+64;
+$LOCALS=6*$SIZE_T;
+
+$K  ="r0";
+$sp ="r1";
+$toc="r2";
+$ctx="r3";
+$inp="r4";
+$num="r5";
+$t0 ="r15";
+$t1 ="r6";
+
+$A  ="r7";
+$B  ="r8";
+$C  ="r9";
+$D  ="r10";
+$E  ="r11";
+$T  ="r12";
+
+@V=($A,$B,$C,$D,$E,$T);
+@X=("r16","r17","r18","r19","r20","r21","r22","r23",
+    "r24","r25","r26","r27","r28","r29","r30","r31");
+
+sub loadbe {
+my ($dst, $src, $temp_reg) = @_;
+$code.=<<___ if (!$LITTLE_ENDIAN);
+	lwz	$dst,$src
+___
+$code.=<<___ if ($LITTLE_ENDIAN);
+	lwz	$temp_reg,$src
+	rotlwi	$dst,$temp_reg,8
+	rlwimi	$dst,$temp_reg,24,0,7
+	rlwimi	$dst,$temp_reg,24,16,23
+___
+}
+
+sub BODY_00_19 {
+my ($i,$a,$b,$c,$d,$e,$f)=@_;
+my $j=$i+1;
+
+	# Since the last value of $f is discarded, we can use
+	# it as a temp reg to swap byte-order when needed.
+	loadbe("@X[$i]","`$i*4`($inp)",$f) if ($i==0);
+	loadbe("@X[$j]","`$j*4`($inp)",$f) if ($i<15);
+$code.=<<___ if ($i<15);
+	add	$f,$K,$e
+	rotlwi	$e,$a,5
+	add	$f,$f,@X[$i]
+	and	$t0,$c,$b
+	add	$f,$f,$e
+	andc	$t1,$d,$b
+	rotlwi	$b,$b,30
+	or	$t0,$t0,$t1
+	add	$f,$f,$t0
+___
+$code.=<<___ if ($i>=15);
+	add	$f,$K,$e
+	rotlwi	$e,$a,5
+	xor	@X[$j%16],@X[$j%16],@X[($j+2)%16]
+	add	$f,$f,@X[$i%16]
+	and	$t0,$c,$b
+	xor	@X[$j%16],@X[$j%16],@X[($j+8)%16]
+	add	$f,$f,$e
+	andc	$t1,$d,$b
+	rotlwi	$b,$b,30
+	or	$t0,$t0,$t1
+	xor	@X[$j%16],@X[$j%16],@X[($j+13)%16]
+	add	$f,$f,$t0
+	rotlwi	@X[$j%16],@X[$j%16],1
+___
+}
+
+sub BODY_20_39 {
+my ($i,$a,$b,$c,$d,$e,$f)=@_;
+my $j=$i+1;
+$code.=<<___ if ($i<79);
+	add	$f,$K,$e
+	xor	$t0,$b,$d
+	rotlwi	$e,$a,5
+	xor	@X[$j%16],@X[$j%16],@X[($j+2)%16]
+	add	$f,$f,@X[$i%16]
+	xor	$t0,$t0,$c
+	xor	@X[$j%16],@X[$j%16],@X[($j+8)%16]
+	add	$f,$f,$t0
+	rotlwi	$b,$b,30
+	xor	@X[$j%16],@X[$j%16],@X[($j+13)%16]
+	add	$f,$f,$e
+	rotlwi	@X[$j%16],@X[$j%16],1
+___
+$code.=<<___ if ($i==79);
+	add	$f,$K,$e
+	xor	$t0,$b,$d
+	rotlwi	$e,$a,5
+	lwz	r16,0($ctx)
+	add	$f,$f,@X[$i%16]
+	xor	$t0,$t0,$c
+	lwz	r17,4($ctx)
+	add	$f,$f,$t0
+	rotlwi	$b,$b,30
+	lwz	r18,8($ctx)
+	lwz	r19,12($ctx)
+	add	$f,$f,$e
+	lwz	r20,16($ctx)
+___
+}
+
+sub BODY_40_59 {
+my ($i,$a,$b,$c,$d,$e,$f)=@_;
+my $j=$i+1;
+$code.=<<___;
+	add	$f,$K,$e
+	rotlwi	$e,$a,5
+	xor	@X[$j%16],@X[$j%16],@X[($j+2)%16]
+	add	$f,$f,@X[$i%16]
+	and	$t0,$b,$c
+	xor	@X[$j%16],@X[$j%16],@X[($j+8)%16]
+	add	$f,$f,$e
+	or	$t1,$b,$c
+	rotlwi	$b,$b,30
+	xor	@X[$j%16],@X[$j%16],@X[($j+13)%16]
+	and	$t1,$t1,$d
+	or	$t0,$t0,$t1
+	rotlwi	@X[$j%16],@X[$j%16],1
+	add	$f,$f,$t0
+___
+}
+
+$code=<<___;
+.machine	"any"
+.text
+
+.globl	.sha1_block_data_order
+.align	4
+.sha1_block_data_order:
+	$STU	$sp,-$FRAME($sp)
+	mflr	r0
+	$PUSH	r15,`$FRAME-$SIZE_T*17`($sp)
+	$PUSH	r16,`$FRAME-$SIZE_T*16`($sp)
+	$PUSH	r17,`$FRAME-$SIZE_T*15`($sp)
+	$PUSH	r18,`$FRAME-$SIZE_T*14`($sp)
+	$PUSH	r19,`$FRAME-$SIZE_T*13`($sp)
+	$PUSH	r20,`$FRAME-$SIZE_T*12`($sp)
+	$PUSH	r21,`$FRAME-$SIZE_T*11`($sp)
+	$PUSH	r22,`$FRAME-$SIZE_T*10`($sp)
+	$PUSH	r23,`$FRAME-$SIZE_T*9`($sp)
+	$PUSH	r24,`$FRAME-$SIZE_T*8`($sp)
+	$PUSH	r25,`$FRAME-$SIZE_T*7`($sp)
+	$PUSH	r26,`$FRAME-$SIZE_T*6`($sp)
+	$PUSH	r27,`$FRAME-$SIZE_T*5`($sp)
+	$PUSH	r28,`$FRAME-$SIZE_T*4`($sp)
+	$PUSH	r29,`$FRAME-$SIZE_T*3`($sp)
+	$PUSH	r30,`$FRAME-$SIZE_T*2`($sp)
+	$PUSH	r31,`$FRAME-$SIZE_T*1`($sp)
+	$PUSH	r0,`$FRAME+$LRSAVE`($sp)
+	lwz	$A,0($ctx)
+	lwz	$B,4($ctx)
+	lwz	$C,8($ctx)
+	lwz	$D,12($ctx)
+	lwz	$E,16($ctx)
+	andi.	r0,$inp,3
+	bne	Lunaligned
+Laligned:
+	mtctr	$num
+	bl	Lsha1_block_private
+	b	Ldone
+
+; PowerPC specification allows an implementation to be ill-behaved
+; upon unaligned access which crosses page boundary. "Better safe
+; than sorry" principle makes me treat it specially. But I don't
+; look for particular offending word, but rather for 64-byte input
+; block which crosses the boundary. Once found that block is aligned
+; and hashed separately...
+.align	4
+Lunaligned:
+	subfic	$t1,$inp,4096
+	andi.	$t1,$t1,4095	; distance to closest page boundary
+	srwi.	$t1,$t1,6	; t1/=64
+	beq	Lcross_page
+	$UCMP	$num,$t1
+	ble	Laligned	; didn't cross the page boundary
+	mtctr	$t1
+	subfc	$num,$t1,$num
+	bl	Lsha1_block_private
+Lcross_page:
+	li	$t1,16
+	mtctr	$t1
+	addi	r20,$sp,$LOCALS	; spot within the frame
+Lmemcpy:
+	lbz	r16,0($inp)
+	lbz	r17,1($inp)
+	lbz	r18,2($inp)
+	lbz	r19,3($inp)
+	addi	$inp,$inp,4
+	stb	r16,0(r20)
+	stb	r17,1(r20)
+	stb	r18,2(r20)
+	stb	r19,3(r20)
+	addi	r20,r20,4
+	bdnz	Lmemcpy
+
+	$PUSH	$inp,`$FRAME-$SIZE_T*18`($sp)
+	li	$t1,1
+	addi	$inp,$sp,$LOCALS
+	mtctr	$t1
+	bl	Lsha1_block_private
+	$POP	$inp,`$FRAME-$SIZE_T*18`($sp)
+	addic.	$num,$num,-1
+	bne	Lunaligned
+
+Ldone:
+	$POP	r0,`$FRAME+$LRSAVE`($sp)
+	$POP	r15,`$FRAME-$SIZE_T*17`($sp)
+	$POP	r16,`$FRAME-$SIZE_T*16`($sp)
+	$POP	r17,`$FRAME-$SIZE_T*15`($sp)
+	$POP	r18,`$FRAME-$SIZE_T*14`($sp)
+	$POP	r19,`$FRAME-$SIZE_T*13`($sp)
+	$POP	r20,`$FRAME-$SIZE_T*12`($sp)
+	$POP	r21,`$FRAME-$SIZE_T*11`($sp)
+	$POP	r22,`$FRAME-$SIZE_T*10`($sp)
+	$POP	r23,`$FRAME-$SIZE_T*9`($sp)
+	$POP	r24,`$FRAME-$SIZE_T*8`($sp)
+	$POP	r25,`$FRAME-$SIZE_T*7`($sp)
+	$POP	r26,`$FRAME-$SIZE_T*6`($sp)
+	$POP	r27,`$FRAME-$SIZE_T*5`($sp)
+	$POP	r28,`$FRAME-$SIZE_T*4`($sp)
+	$POP	r29,`$FRAME-$SIZE_T*3`($sp)
+	$POP	r30,`$FRAME-$SIZE_T*2`($sp)
+	$POP	r31,`$FRAME-$SIZE_T*1`($sp)
+	mtlr	r0
+	addi	$sp,$sp,$FRAME
+	blr
+	.long	0
+	.byte	0,12,4,1,0x80,18,3,0
+	.long	0
+___
+
+# This is private block function, which uses tailored calling
+# interface, namely upon entry SHA_CTX is pre-loaded to given
+# registers and counter register contains amount of chunks to
+# digest...
+$code.=<<___;
+.align	4
+Lsha1_block_private:
+___
+$code.=<<___;	# load K_00_19
+	lis	$K,0x5a82
+	ori	$K,$K,0x7999
+___
+for($i=0;$i<20;$i++)	{ &BODY_00_19($i,@V); unshift(@V,pop(@V)); }
+$code.=<<___;	# load K_20_39
+	lis	$K,0x6ed9
+	ori	$K,$K,0xeba1
+___
+for(;$i<40;$i++)	{ &BODY_20_39($i,@V); unshift(@V,pop(@V)); }
+$code.=<<___;	# load K_40_59
+	lis	$K,0x8f1b
+	ori	$K,$K,0xbcdc
+___
+for(;$i<60;$i++)	{ &BODY_40_59($i,@V); unshift(@V,pop(@V)); }
+$code.=<<___;	# load K_60_79
+	lis	$K,0xca62
+	ori	$K,$K,0xc1d6
+___
+for(;$i<80;$i++)	{ &BODY_20_39($i,@V); unshift(@V,pop(@V)); }
+$code.=<<___;
+	add	r16,r16,$E
+	add	r17,r17,$T
+	add	r18,r18,$A
+	add	r19,r19,$B
+	add	r20,r20,$C
+	stw	r16,0($ctx)
+	mr	$A,r16
+	stw	r17,4($ctx)
+	mr	$B,r17
+	stw	r18,8($ctx)
+	mr	$C,r18
+	stw	r19,12($ctx)
+	mr	$D,r19
+	stw	r20,16($ctx)
+	mr	$E,r20
+	addi	$inp,$inp,`16*4`
+	bdnz	Lsha1_block_private
+	blr
+	.long	0
+	.byte	0,12,0x14,0,0,0,0,0
+.size	.sha1_block_data_order,.-.sha1_block_data_order
+___
+$code.=<<___;
+.asciz	"SHA1 block transform for PPC, CRYPTOGAMS by "
+___
+
+$code =~ s/\`([^\`]*)\`/eval $1/gem;
+print $code;
+close STDOUT;
diff --git a/crypto/sha/asm/sha1-s390x.pl b/crypto/sha/asm/sha1-s390x.pl
new file mode 100644
index 000000000..b19606c5c
--- /dev/null
+++ b/crypto/sha/asm/sha1-s390x.pl
@@ -0,0 +1,251 @@
+#! /usr/bin/env perl
+# Copyright 2007-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+
+# ====================================================================
+# Written by Andy Polyakov  for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+
+# SHA1 block procedure for s390x.
+
+# April 2007.
+#
+# Performance is >30% better than gcc 3.3 generated code. But the real
+# twist is that SHA1 hardware support is detected and utilized. In
+# which case performance can reach further >4.5x for larger chunks.
+
+# January 2009.
+#
+# Optimize Xupdate for amount of memory references and reschedule
+# instructions to favour dual-issue z10 pipeline. On z10 hardware is
+# "only" ~2.3x faster than software.
+
+# November 2010.
+#
+# Adapt for -m31 build. If kernel supports what's called "highgprs"
+# feature on Linux [see /proc/cpuinfo], it's possible to use 64-bit
+# instructions and achieve "64-bit" performance even in 31-bit legacy
+# application context. The feature is not specific to any particular
+# processor, as long as it's "z-CPU". Latter implies that the code
+# remains z/Architecture specific. On z990 it was measured to perform
+# 23% better than code generated by gcc 4.3.
+
+$kimdfunc=1;	# magic function code for kimd instruction
+
+$flavour = shift;
+
+if ($flavour =~ /3[12]/) {
+	$SIZE_T=4;
+	$g="";
+} else {
+	$SIZE_T=8;
+	$g="g";
+}
+
+while (($output=shift) && ($output!~/\w[\w\-]*\.\w+$/)) {}
+open STDOUT,">$output";
+
+$K_00_39="%r0"; $K=$K_00_39;
+$K_40_79="%r1";
+$ctx="%r2";	$prefetch="%r2";
+$inp="%r3";
+$len="%r4";
+
+$A="%r5";
+$B="%r6";
+$C="%r7";
+$D="%r8";
+$E="%r9";	@V=($A,$B,$C,$D,$E);
+$t0="%r10";
+$t1="%r11";
+@X=("%r12","%r13","%r14");
+$sp="%r15";
+
+$stdframe=16*$SIZE_T+4*8;
+$frame=$stdframe+16*4;
+
+sub Xupdate {
+my $i=shift;
+
+$code.=<<___ if ($i==15);
+	lg	$prefetch,$stdframe($sp)	### Xupdate(16) warm-up
+	lr	$X[0],$X[2]
+___
+return if ($i&1);	# Xupdate is vectorized and executed every 2nd cycle
+$code.=<<___ if ($i<16);
+	lg	$X[0],`$i*4`($inp)	### Xload($i)
+	rllg	$X[1],$X[0],32
+___
+$code.=<<___ if ($i>=16);
+	xgr	$X[0],$prefetch		### Xupdate($i)
+	lg	$prefetch,`$stdframe+4*(($i+2)%16)`($sp)
+	xg	$X[0],`$stdframe+4*(($i+8)%16)`($sp)
+	xgr	$X[0],$prefetch
+	rll	$X[0],$X[0],1
+	rllg	$X[1],$X[0],32
+	rll	$X[1],$X[1],1
+	rllg	$X[0],$X[1],32
+	lr	$X[2],$X[1]		# feedback
+___
+$code.=<<___ if ($i<=70);
+	stg	$X[0],`$stdframe+4*($i%16)`($sp)
+___
+unshift(@X,pop(@X));
+}
+
+sub BODY_00_19 {
+my ($i,$a,$b,$c,$d,$e)=@_;
+my $xi=$X[1];
+
+	&Xupdate($i);
+$code.=<<___;
+	alr	$e,$K		### $i
+	rll	$t1,$a,5
+	lr	$t0,$d
+	xr	$t0,$c
+	alr	$e,$t1
+	nr	$t0,$b
+	alr	$e,$xi
+	xr	$t0,$d
+	rll	$b,$b,30
+	alr	$e,$t0
+___
+}
+
+sub BODY_20_39 {
+my ($i,$a,$b,$c,$d,$e)=@_;
+my $xi=$X[1];
+
+	&Xupdate($i);
+$code.=<<___;
+	alr	$e,$K		### $i
+	rll	$t1,$a,5
+	lr	$t0,$b
+	alr	$e,$t1
+	xr	$t0,$c
+	alr	$e,$xi
+	xr	$t0,$d
+	rll	$b,$b,30
+	alr	$e,$t0
+___
+}
+
+sub BODY_40_59 {
+my ($i,$a,$b,$c,$d,$e)=@_;
+my $xi=$X[1];
+
+	&Xupdate($i);
+$code.=<<___;
+	alr	$e,$K		### $i
+	rll	$t1,$a,5
+	lr	$t0,$b
+	alr	$e,$t1
+	or	$t0,$c
+	lr	$t1,$b
+	nr	$t0,$d
+	nr	$t1,$c
+	alr	$e,$xi
+	or	$t0,$t1
+	rll	$b,$b,30
+	alr	$e,$t0
+___
+}
+
+$code.=<<___;
+.text
+.align	64
+.type	Ktable,\@object
+Ktable: .long	0x5a827999,0x6ed9eba1,0x8f1bbcdc,0xca62c1d6
+	.skip	48	#.long	0,0,0,0,0,0,0,0,0,0,0,0
+.size	Ktable,.-Ktable
+.globl	sha1_block_data_order
+.type	sha1_block_data_order,\@function
+sha1_block_data_order:
+___
+$code.=<<___ if ($kimdfunc);
+	larl	%r1,OPENSSL_s390xcap_P
+	lg	%r0,0(%r1)
+	tmhl	%r0,0x4000	# check for message-security assist
+	jz	.Lsoftware
+	lg	%r0,16(%r1)	# check kimd capabilities
+	tmhh	%r0,`0x8000>>$kimdfunc`
+	jz	.Lsoftware
+	lghi	%r0,$kimdfunc
+	lgr	%r1,$ctx
+	lgr	%r2,$inp
+	sllg	%r3,$len,6
+	.long	0xb93e0002	# kimd %r0,%r2
+	brc	1,.-4		# pay attention to "partial completion"
+	br	%r14
+.align	16
+.Lsoftware:
+___
+$code.=<<___;
+	lghi	%r1,-$frame
+	st${g}	$ctx,`2*$SIZE_T`($sp)
+	stm${g}	%r6,%r15,`6*$SIZE_T`($sp)
+	lgr	%r0,$sp
+	la	$sp,0(%r1,$sp)
+	st${g}	%r0,0($sp)
+
+	larl	$t0,Ktable
+	llgf	$A,0($ctx)
+	llgf	$B,4($ctx)
+	llgf	$C,8($ctx)
+	llgf	$D,12($ctx)
+	llgf	$E,16($ctx)
+
+	lg	$K_00_39,0($t0)
+	lg	$K_40_79,8($t0)
+
+.Lloop:
+	rllg	$K_00_39,$K_00_39,32
+___
+for ($i=0;$i<20;$i++)	{ &BODY_00_19($i,@V); unshift(@V,pop(@V)); }
+$code.=<<___;
+	rllg	$K_00_39,$K_00_39,32
+___
+for (;$i<40;$i++)	{ &BODY_20_39($i,@V); unshift(@V,pop(@V)); }
+$code.=<<___;	$K=$K_40_79;
+	rllg	$K_40_79,$K_40_79,32
+___
+for (;$i<60;$i++)	{ &BODY_40_59($i,@V); unshift(@V,pop(@V)); }
+$code.=<<___;
+	rllg	$K_40_79,$K_40_79,32
+___
+for (;$i<80;$i++)	{ &BODY_20_39($i,@V); unshift(@V,pop(@V)); }
+$code.=<<___;
+
+	l${g}	$ctx,`$frame+2*$SIZE_T`($sp)
+	la	$inp,64($inp)
+	al	$A,0($ctx)
+	al	$B,4($ctx)
+	al	$C,8($ctx)
+	al	$D,12($ctx)
+	al	$E,16($ctx)
+	st	$A,0($ctx)
+	st	$B,4($ctx)
+	st	$C,8($ctx)
+	st	$D,12($ctx)
+	st	$E,16($ctx)
+	brct${g} $len,.Lloop
+
+	lm${g}	%r6,%r15,`$frame+6*$SIZE_T`($sp)
+	br	%r14
+.size	sha1_block_data_order,.-sha1_block_data_order
+.string	"SHA1 block transform for s390x, CRYPTOGAMS by "
+.comm	OPENSSL_s390xcap_P,80,8
+___
+
+$code =~ s/\`([^\`]*)\`/eval $1/gem;
+
+print $code;
+close STDOUT;
diff --git a/crypto/sha/asm/sha1-sparcv9.pl b/crypto/sha/asm/sha1-sparcv9.pl
new file mode 100644
index 000000000..7437ff4f0
--- /dev/null
+++ b/crypto/sha/asm/sha1-sparcv9.pl
@@ -0,0 +1,434 @@
+#! /usr/bin/env perl
+# Copyright 2007-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+
+# ====================================================================
+# Written by Andy Polyakov  for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+#
+# Hardware SPARC T4 support by David S. Miller .
+# ====================================================================
+
+# Performance improvement is not really impressive on pre-T1 CPU: +8%
+# over Sun C and +25% over gcc [3.3]. While on T1, a.k.a. Niagara, it
+# turned to be 40% faster than 64-bit code generated by Sun C 5.8 and
+# >2x than 64-bit code generated by gcc 3.4. And there is a gimmick.
+# X[16] vector is packed to 8 64-bit registers and as result nothing
+# is spilled on stack. In addition input data is loaded in compact
+# instruction sequence, thus minimizing the window when the code is
+# subject to [inter-thread] cache-thrashing hazard. The goal is to
+# ensure scalability on UltraSPARC T1, or rather to avoid decay when
+# amount of active threads exceeds the number of physical cores.
+
+# SPARC T4 SHA1 hardware achieves 3.72 cycles per byte, which is 3.1x
+# faster than software. Multi-process benchmark saturates at 11x
+# single-process result on 8-core processor, or ~9GBps per 2.85GHz
+# socket.
+
+$output=pop;
+open STDOUT,">$output";
+
+@X=("%o0","%o1","%o2","%o3","%o4","%o5","%g1","%o7");
+$rot1m="%g2";
+$tmp64="%g3";
+$Xi="%g4";
+$A="%l0";
+$B="%l1";
+$C="%l2";
+$D="%l3";
+$E="%l4";
+@V=($A,$B,$C,$D,$E);
+$K_00_19="%l5";
+$K_20_39="%l6";
+$K_40_59="%l7";
+$K_60_79="%g5";
+@K=($K_00_19,$K_20_39,$K_40_59,$K_60_79);
+
+$ctx="%i0";
+$inp="%i1";
+$len="%i2";
+$tmp0="%i3";
+$tmp1="%i4";
+$tmp2="%i5";
+
+sub BODY_00_15 {
+my ($i,$a,$b,$c,$d,$e)=@_;
+my $xi=($i&1)?@X[($i/2)%8]:$Xi;
+
+$code.=<<___;
+	sll	$a,5,$tmp0		!! $i
+	add	@K[$i/20],$e,$e
+	srl	$a,27,$tmp1
+	add	$tmp0,$e,$e
+	and	$c,$b,$tmp0
+	add	$tmp1,$e,$e
+	sll	$b,30,$tmp2
+	andn	$d,$b,$tmp1
+	srl	$b,2,$b
+	or	$tmp1,$tmp0,$tmp1
+	or	$tmp2,$b,$b
+	add	$xi,$e,$e
+___
+if ($i&1 && $i<15) {
+	$code.=
+	"	srlx	@X[(($i+1)/2)%8],32,$Xi\n";
+}
+$code.=<<___;
+	add	$tmp1,$e,$e
+___
+}
+
+sub Xupdate {
+my ($i,$a,$b,$c,$d,$e)=@_;
+my $j=$i/2;
+
+if ($i&1) {
+$code.=<<___;
+	sll	$a,5,$tmp0		!! $i
+	add	@K[$i/20],$e,$e
+	srl	$a,27,$tmp1
+___
+} else {
+$code.=<<___;
+	sllx	@X[($j+6)%8],32,$Xi	! Xupdate($i)
+	xor	@X[($j+1)%8],@X[$j%8],@X[$j%8]
+	srlx	@X[($j+7)%8],32,$tmp1
+	xor	@X[($j+4)%8],@X[$j%8],@X[$j%8]
+	sll	$a,5,$tmp0		!! $i
+	or	$tmp1,$Xi,$Xi
+	add	@K[$i/20],$e,$e		!!
+	xor	$Xi,@X[$j%8],@X[$j%8]
+	srlx	@X[$j%8],31,$Xi
+	add	@X[$j%8],@X[$j%8],@X[$j%8]
+	and	$Xi,$rot1m,$Xi
+	andn	@X[$j%8],$rot1m,@X[$j%8]
+	srl	$a,27,$tmp1		!!
+	or	$Xi,@X[$j%8],@X[$j%8]
+___
+}
+}
+
+sub BODY_16_19 {
+my ($i,$a,$b,$c,$d,$e)=@_;
+
+	&Xupdate(@_);
+    if ($i&1) {
+	$xi=@X[($i/2)%8];
+    } else {
+	$xi=$Xi;
+	$code.="\tsrlx	@X[($i/2)%8],32,$xi\n";
+    }
+$code.=<<___;
+	add	$tmp0,$e,$e		!!
+	and	$c,$b,$tmp0
+	add	$tmp1,$e,$e
+	sll	$b,30,$tmp2
+	add	$xi,$e,$e
+	andn	$d,$b,$tmp1
+	srl	$b,2,$b
+	or	$tmp1,$tmp0,$tmp1
+	or	$tmp2,$b,$b
+	add	$tmp1,$e,$e
+___
+}
+
+sub BODY_20_39 {
+my ($i,$a,$b,$c,$d,$e)=@_;
+my $xi;
+	&Xupdate(@_);
+    if ($i&1) {
+	$xi=@X[($i/2)%8];
+    } else {
+	$xi=$Xi;
+	$code.="\tsrlx	@X[($i/2)%8],32,$xi\n";
+    }
+$code.=<<___;
+	add	$tmp0,$e,$e		!!
+	xor	$c,$b,$tmp0
+	add	$tmp1,$e,$e
+	sll	$b,30,$tmp2
+	xor	$d,$tmp0,$tmp1
+	srl	$b,2,$b
+	add	$tmp1,$e,$e
+	or	$tmp2,$b,$b
+	add	$xi,$e,$e
+___
+}
+
+sub BODY_40_59 {
+my ($i,$a,$b,$c,$d,$e)=@_;
+my $xi;
+	&Xupdate(@_);
+    if ($i&1) {
+	$xi=@X[($i/2)%8];
+    } else {
+	$xi=$Xi;
+	$code.="\tsrlx	@X[($i/2)%8],32,$xi\n";
+    }
+$code.=<<___;
+	add	$tmp0,$e,$e		!!
+	and	$c,$b,$tmp0
+	add	$tmp1,$e,$e
+	sll	$b,30,$tmp2
+	or	$c,$b,$tmp1
+	srl	$b,2,$b
+	and	$d,$tmp1,$tmp1
+	add	$xi,$e,$e
+	or	$tmp1,$tmp0,$tmp1
+	or	$tmp2,$b,$b
+	add	$tmp1,$e,$e
+___
+}
+
+$code.=<<___;
+#include "sparc_arch.h"
+
+#ifdef __arch64__
+.register	%g2,#scratch
+.register	%g3,#scratch
+#endif
+
+.section	".text",#alloc,#execinstr
+
+#ifdef __PIC__
+SPARC_PIC_THUNK(%g1)
+#endif
+
+.align	32
+.globl	sha1_block_data_order
+sha1_block_data_order:
+	SPARC_LOAD_ADDRESS_LEAF(OPENSSL_sparcv9cap_P,%g1,%g5)
+	ld	[%g1+4],%g1		! OPENSSL_sparcv9cap_P[1]
+
+	andcc	%g1, CFR_SHA1, %g0
+	be	.Lsoftware
+	nop
+
+	ld	[%o0 + 0x00], %f0	! load context
+	ld	[%o0 + 0x04], %f1
+	ld	[%o0 + 0x08], %f2
+	andcc	%o1, 0x7, %g0
+	ld	[%o0 + 0x0c], %f3
+	bne,pn	%icc, .Lhwunaligned
+	 ld	[%o0 + 0x10], %f4
+
+.Lhw_loop:
+	ldd	[%o1 + 0x00], %f8
+	ldd	[%o1 + 0x08], %f10
+	ldd	[%o1 + 0x10], %f12
+	ldd	[%o1 + 0x18], %f14
+	ldd	[%o1 + 0x20], %f16
+	ldd	[%o1 + 0x28], %f18
+	ldd	[%o1 + 0x30], %f20
+	subcc	%o2, 1, %o2		! done yet? 
+	ldd	[%o1 + 0x38], %f22
+	add	%o1, 0x40, %o1
+	prefetch [%o1 + 63], 20
+
+	.word	0x81b02820		! SHA1
+
+	bne,pt	SIZE_T_CC, .Lhw_loop
+	nop
+
+.Lhwfinish:
+	st	%f0, [%o0 + 0x00]	! store context
+	st	%f1, [%o0 + 0x04]
+	st	%f2, [%o0 + 0x08]
+	st	%f3, [%o0 + 0x0c]
+	retl
+	st	%f4, [%o0 + 0x10]
+
+.align	8
+.Lhwunaligned:
+	alignaddr %o1, %g0, %o1
+
+	ldd	[%o1 + 0x00], %f10
+.Lhwunaligned_loop:
+	ldd	[%o1 + 0x08], %f12
+	ldd	[%o1 + 0x10], %f14
+	ldd	[%o1 + 0x18], %f16
+	ldd	[%o1 + 0x20], %f18
+	ldd	[%o1 + 0x28], %f20
+	ldd	[%o1 + 0x30], %f22
+	ldd	[%o1 + 0x38], %f24
+	subcc	%o2, 1, %o2		! done yet?
+	ldd	[%o1 + 0x40], %f26
+	add	%o1, 0x40, %o1
+	prefetch [%o1 + 63], 20
+
+	faligndata %f10, %f12, %f8
+	faligndata %f12, %f14, %f10
+	faligndata %f14, %f16, %f12
+	faligndata %f16, %f18, %f14
+	faligndata %f18, %f20, %f16
+	faligndata %f20, %f22, %f18
+	faligndata %f22, %f24, %f20
+	faligndata %f24, %f26, %f22
+
+	.word	0x81b02820		! SHA1
+
+	bne,pt	SIZE_T_CC, .Lhwunaligned_loop
+	for	%f26, %f26, %f10	! %f10=%f26
+
+	ba	.Lhwfinish
+	nop
+
+.align	16
+.Lsoftware:
+	save	%sp,-STACK_FRAME,%sp
+	sllx	$len,6,$len
+	add	$inp,$len,$len
+
+	or	%g0,1,$rot1m
+	sllx	$rot1m,32,$rot1m
+	or	$rot1m,1,$rot1m
+
+	ld	[$ctx+0],$A
+	ld	[$ctx+4],$B
+	ld	[$ctx+8],$C
+	ld	[$ctx+12],$D
+	ld	[$ctx+16],$E
+	andn	$inp,7,$tmp0
+
+	sethi	%hi(0x5a827999),$K_00_19
+	or	$K_00_19,%lo(0x5a827999),$K_00_19
+	sethi	%hi(0x6ed9eba1),$K_20_39
+	or	$K_20_39,%lo(0x6ed9eba1),$K_20_39
+	sethi	%hi(0x8f1bbcdc),$K_40_59
+	or	$K_40_59,%lo(0x8f1bbcdc),$K_40_59
+	sethi	%hi(0xca62c1d6),$K_60_79
+	or	$K_60_79,%lo(0xca62c1d6),$K_60_79
+
+.Lloop:
+	ldx	[$tmp0+0],@X[0]
+	ldx	[$tmp0+16],@X[2]
+	ldx	[$tmp0+32],@X[4]
+	ldx	[$tmp0+48],@X[6]
+	and	$inp,7,$tmp1
+	ldx	[$tmp0+8],@X[1]
+	sll	$tmp1,3,$tmp1
+	ldx	[$tmp0+24],@X[3]
+	subcc	%g0,$tmp1,$tmp2	! should be 64-$tmp1, but -$tmp1 works too
+	ldx	[$tmp0+40],@X[5]
+	bz,pt	%icc,.Laligned
+	ldx	[$tmp0+56],@X[7]
+
+	sllx	@X[0],$tmp1,@X[0]
+	ldx	[$tmp0+64],$tmp64
+___
+for($i=0;$i<7;$i++)
+{   $code.=<<___;
+	srlx	@X[$i+1],$tmp2,$Xi
+	sllx	@X[$i+1],$tmp1,@X[$i+1]
+	or	$Xi,@X[$i],@X[$i]
+___
+}
+$code.=<<___;
+	srlx	$tmp64,$tmp2,$tmp64
+	or	$tmp64,@X[7],@X[7]
+.Laligned:
+	srlx	@X[0],32,$Xi
+___
+for ($i=0;$i<16;$i++)	{ &BODY_00_15($i,@V); unshift(@V,pop(@V)); }
+for (;$i<20;$i++)	{ &BODY_16_19($i,@V); unshift(@V,pop(@V)); }
+for (;$i<40;$i++)	{ &BODY_20_39($i,@V); unshift(@V,pop(@V)); }
+for (;$i<60;$i++)	{ &BODY_40_59($i,@V); unshift(@V,pop(@V)); }
+for (;$i<80;$i++)	{ &BODY_20_39($i,@V); unshift(@V,pop(@V)); }
+$code.=<<___;
+
+	ld	[$ctx+0],@X[0]
+	ld	[$ctx+4],@X[1]
+	ld	[$ctx+8],@X[2]
+	ld	[$ctx+12],@X[3]
+	add	$inp,64,$inp
+	ld	[$ctx+16],@X[4]
+	cmp	$inp,$len
+
+	add	$A,@X[0],$A
+	st	$A,[$ctx+0]
+	add	$B,@X[1],$B
+	st	$B,[$ctx+4]
+	add	$C,@X[2],$C
+	st	$C,[$ctx+8]
+	add	$D,@X[3],$D
+	st	$D,[$ctx+12]
+	add	$E,@X[4],$E
+	st	$E,[$ctx+16]
+
+	bne	SIZE_T_CC,.Lloop
+	andn	$inp,7,$tmp0
+
+	ret
+	restore
+.type	sha1_block_data_order,#function
+.size	sha1_block_data_order,(.-sha1_block_data_order)
+.asciz	"SHA1 block transform for SPARCv9, CRYPTOGAMS by "
+.align	4
+___
+
+# Purpose of these subroutines is to explicitly encode VIS instructions,
+# so that one can compile the module without having to specify VIS
+# extensions on compiler command line, e.g. -xarch=v9 vs. -xarch=v9a.
+# Idea is to reserve for option to produce "universal" binary and let
+# programmer detect if current CPU is VIS capable at run-time.
+sub unvis {
+my ($mnemonic,$rs1,$rs2,$rd)=@_;
+my $ref,$opf;
+my %visopf = (	"faligndata"	=> 0x048,
+		"for"		=> 0x07c	);
+
+    $ref = "$mnemonic\t$rs1,$rs2,$rd";
+
+    if ($opf=$visopf{$mnemonic}) {
+	foreach ($rs1,$rs2,$rd) {
+	    return $ref if (!/%f([0-9]{1,2})/);
+	    $_=$1;
+	    if ($1>=32) {
+		return $ref if ($1&1);
+		# re-encode for upper double register addressing
+		$_=($1|$1>>5)&31;
+	    }
+	}
+
+	return	sprintf ".word\t0x%08x !%s",
+			0x81b00000|$rd<<25|$rs1<<14|$opf<<5|$rs2,
+			$ref;
+    } else {
+	return $ref;
+    }
+}
+sub unalignaddr {
+my ($mnemonic,$rs1,$rs2,$rd)=@_;
+my %bias = ( "g" => 0, "o" => 8, "l" => 16, "i" => 24 );
+my $ref="$mnemonic\t$rs1,$rs2,$rd";
+
+    foreach ($rs1,$rs2,$rd) {
+	if (/%([goli])([0-7])/)	{ $_=$bias{$1}+$2; }
+	else			{ return $ref; }
+    }
+    return  sprintf ".word\t0x%08x !%s",
+		    0x81b00300|$rd<<25|$rs1<<14|$rs2,
+		    $ref;
+}
+
+foreach (split("\n",$code)) {
+	s/\`([^\`]*)\`/eval $1/ge;
+
+	s/\b(f[^\s]*)\s+(%f[0-9]{1,2}),\s*(%f[0-9]{1,2}),\s*(%f[0-9]{1,2})/
+		&unvis($1,$2,$3,$4)
+	 /ge;
+	s/\b(alignaddr)\s+(%[goli][0-7]),\s*(%[goli][0-7]),\s*(%[goli][0-7])/
+		&unalignaddr($1,$2,$3,$4)
+	 /ge;
+
+	print $_,"\n";
+}
+
+close STDOUT;
diff --git a/crypto/sha/asm/sha1-sparcv9a.pl b/crypto/sha/asm/sha1-sparcv9a.pl
new file mode 100644
index 000000000..f9ed5630e
--- /dev/null
+++ b/crypto/sha/asm/sha1-sparcv9a.pl
@@ -0,0 +1,608 @@
+#! /usr/bin/env perl
+# Copyright 2009-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+
+# ====================================================================
+# Written by Andy Polyakov  for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+
+# January 2009
+#
+# Provided that UltraSPARC VIS instructions are pipe-lined(*) and
+# pairable(*) with IALU ones, offloading of Xupdate to the UltraSPARC
+# Graphic Unit would make it possible to achieve higher instruction-
+# level parallelism, ILP, and thus higher performance. It should be
+# explicitly noted that ILP is the keyword, and it means that this
+# code would be unsuitable for cores like UltraSPARC-Tx. The idea is
+# not really novel, Sun had VIS-powered implementation for a while.
+# Unlike Sun's implementation this one can process multiple unaligned
+# input blocks, and as such works as drop-in replacement for OpenSSL
+# sha1_block_data_order. Performance improvement was measured to be
+# 40% over pure IALU sha1-sparcv9.pl on UltraSPARC-IIi, but 12% on
+# UltraSPARC-III. See below for discussion...
+#
+# The module does not present direct interest for OpenSSL, because
+# it doesn't provide better performance on contemporary SPARCv9 CPUs,
+# UltraSPARC-Tx and SPARC64-V[II] to be specific. Those who feel they
+# absolutely must score on UltraSPARC-I-IV can simply replace
+# crypto/sha/asm/sha1-sparcv9.pl with this module.
+#
+# (*)	"Pipe-lined" means that even if it takes several cycles to
+#	complete, next instruction using same functional unit [but not
+#	depending on the result of the current instruction] can start
+#	execution without having to wait for the unit. "Pairable"
+#	means that two [or more] independent instructions can be
+#	issued at the very same time.
+
+$bits=32;
+for (@ARGV)	{ $bits=64 if (/\-m64/ || /\-xarch\=v9/); }
+if ($bits==64)	{ $bias=2047; $frame=192; }
+else		{ $bias=0;    $frame=112; }
+
+$output=shift;
+open STDOUT,">$output";
+
+$ctx="%i0";
+$inp="%i1";
+$len="%i2";
+$tmp0="%i3";
+$tmp1="%i4";
+$tmp2="%i5";
+$tmp3="%g5";
+
+$base="%g1";
+$align="%g4";
+$Xfer="%o5";
+$nXfer=$tmp3;
+$Xi="%o7";
+
+$A="%l0";
+$B="%l1";
+$C="%l2";
+$D="%l3";
+$E="%l4";
+@V=($A,$B,$C,$D,$E);
+
+$Actx="%o0";
+$Bctx="%o1";
+$Cctx="%o2";
+$Dctx="%o3";
+$Ectx="%o4";
+
+$fmul="%f32";
+$VK_00_19="%f34";
+$VK_20_39="%f36";
+$VK_40_59="%f38";
+$VK_60_79="%f40";
+@VK=($VK_00_19,$VK_20_39,$VK_40_59,$VK_60_79);
+@X=("%f0", "%f1", "%f2", "%f3", "%f4", "%f5", "%f6", "%f7",
+    "%f8", "%f9","%f10","%f11","%f12","%f13","%f14","%f15","%f16");
+
+# This is reference 2x-parallelized VIS-powered Xupdate procedure. It
+# covers even K_NN_MM addition...
+sub Xupdate {
+my ($i)=@_;
+my $K=@VK[($i+16)/20];
+my $j=($i+16)%16;
+
+#	[ provided that GSR.alignaddr_offset is 5, $mul contains
+#	  0x100ULL<<32|0x100 value and K_NN_MM are pre-loaded to
+#	  chosen registers... ]
+$code.=<<___;
+	fxors		@X[($j+13)%16],@X[$j],@X[$j]	!-1/-1/-1:X[0]^=X[13]
+	fxors		@X[($j+14)%16],@X[$j+1],@X[$j+1]! 0/ 0/ 0:X[1]^=X[14]
+	fxor		@X[($j+2)%16],@X[($j+8)%16],%f18! 1/ 1/ 1:Tmp=X[2,3]^X[8,9]
+	fxor		%f18,@X[$j],@X[$j]		! 2/ 4/ 3:X[0,1]^=X[2,3]^X[8,9]
+	faligndata	@X[$j],@X[$j],%f18		! 3/ 7/ 5:Tmp=X[0,1]>>>24
+	fpadd32		@X[$j],@X[$j],@X[$j]		! 4/ 8/ 6:X[0,1]<<=1
+	fmul8ulx16	%f18,$fmul,%f18			! 5/10/ 7:Tmp>>=7, Tmp&=1
+	![fxors		%f15,%f2,%f2]
+	for		%f18,@X[$j],@X[$j]		! 8/14/10:X[0,1]|=Tmp
+	![fxors		%f0,%f3,%f3]			!10/17/12:X[0] dependency
+	fpadd32		$K,@X[$j],%f20
+	std		%f20,[$Xfer+`4*$j`]
+___
+# The numbers delimited with slash are the earliest possible dispatch
+# cycles for given instruction assuming 1 cycle latency for simple VIS
+# instructions, such as on UltraSPARC-I&II, 3 cycles latency, such as
+# on UltraSPARC-III&IV, and 2 cycles latency(*), respectively. Being
+# 2x-parallelized the procedure is "worth" 5, 8.5 or 6 ticks per SHA1
+# round. As [long as] FPU/VIS instructions are perfectly pairable with
+# IALU ones, the round timing is defined by the maximum between VIS
+# and IALU timings. The latter varies from round to round and averages
+# out at 6.25 ticks. This means that USI&II should operate at IALU
+# rate, while USIII&IV - at VIS rate. This explains why performance
+# improvement varies among processors. Well, given that pure IALU
+# sha1-sparcv9.pl module exhibits virtually uniform performance of
+# ~9.3 cycles per SHA1 round. Timings mentioned above are theoretical
+# lower limits. Real-life performance was measured to be 6.6 cycles
+# per SHA1 round on USIIi and 8.3 on USIII. The latter is lower than
+# half-round VIS timing, because there are 16 Xupdate-free rounds,
+# which "push down" average theoretical timing to 8 cycles...
+
+# (*)	SPARC64-V[II] was originally believed to have 2 cycles VIS
+#	latency. Well, it might have, but it doesn't have dedicated
+#	VIS-unit. Instead, VIS instructions are executed by other
+#	functional units, ones used here - by IALU. This doesn't
+#	improve effective ILP...
+}
+
+# The reference Xupdate procedure is then "strained" over *pairs* of
+# BODY_NN_MM and kind of modulo-scheduled in respect to X[n]^=X[n+13]
+# and K_NN_MM addition. It's "running" 15 rounds ahead, which leaves
+# plenty of room to amortize for read-after-write hazard, as well as
+# to fetch and align input for the next spin. The VIS instructions are
+# scheduled for latency of 2 cycles, because there are not enough IALU
+# instructions to schedule for latency of 3, while scheduling for 1
+# would give no gain on USI&II anyway.
+
+sub BODY_00_19 {
+my ($i,$a,$b,$c,$d,$e)=@_;
+my $j=$i&~1;
+my $k=($j+16+2)%16;	# ahead reference
+my $l=($j+16-2)%16;	# behind reference
+my $K=@VK[($j+16-2)/20];
+
+$j=($j+16)%16;
+
+$code.=<<___ if (!($i&1));
+	sll		$a,5,$tmp0			!! $i
+	and		$c,$b,$tmp3
+	ld		[$Xfer+`4*($i%16)`],$Xi
+	 fxors		@X[($j+14)%16],@X[$j+1],@X[$j+1]! 0/ 0/ 0:X[1]^=X[14]
+	srl		$a,27,$tmp1
+	add		$tmp0,$e,$e
+	 fxor		@X[($j+2)%16],@X[($j+8)%16],%f18! 1/ 1/ 1:Tmp=X[2,3]^X[8,9]
+	sll		$b,30,$tmp2
+	add		$tmp1,$e,$e
+	andn		$d,$b,$tmp1
+	add		$Xi,$e,$e
+	 fxor		%f18,@X[$j],@X[$j]		! 2/ 4/ 3:X[0,1]^=X[2,3]^X[8,9]
+	srl		$b,2,$b
+	or		$tmp1,$tmp3,$tmp1
+	or		$tmp2,$b,$b
+	add		$tmp1,$e,$e
+	 faligndata	@X[$j],@X[$j],%f18		! 3/ 7/ 5:Tmp=X[0,1]>>>24
+___
+$code.=<<___ if ($i&1);
+	sll		$a,5,$tmp0			!! $i
+	and		$c,$b,$tmp3
+	ld		[$Xfer+`4*($i%16)`],$Xi
+	 fpadd32	@X[$j],@X[$j],@X[$j]		! 4/ 8/ 6:X[0,1]<<=1
+	srl		$a,27,$tmp1
+	add		$tmp0,$e,$e
+	 fmul8ulx16	%f18,$fmul,%f18			! 5/10/ 7:Tmp>>=7, Tmp&=1
+	sll		$b,30,$tmp2
+	add		$tmp1,$e,$e
+	 fpadd32	$K,@X[$l],%f20			!
+	andn		$d,$b,$tmp1
+	add		$Xi,$e,$e
+	 fxors		@X[($k+13)%16],@X[$k],@X[$k]	!-1/-1/-1:X[0]^=X[13]
+	srl		$b,2,$b
+	or		$tmp1,$tmp3,$tmp1
+	 fxor		%f18,@X[$j],@X[$j]		! 8/14/10:X[0,1]|=Tmp
+	or		$tmp2,$b,$b
+	add		$tmp1,$e,$e
+___
+$code.=<<___ if ($i&1 && $i>=2);
+	 std		%f20,[$Xfer+`4*$l`]		!
+___
+}
+
+sub BODY_20_39 {
+my ($i,$a,$b,$c,$d,$e)=@_;
+my $j=$i&~1;
+my $k=($j+16+2)%16;	# ahead reference
+my $l=($j+16-2)%16;	# behind reference
+my $K=@VK[($j+16-2)/20];
+
+$j=($j+16)%16;
+
+$code.=<<___ if (!($i&1) && $i<64);
+	sll		$a,5,$tmp0			!! $i
+	ld		[$Xfer+`4*($i%16)`],$Xi
+	 fxors		@X[($j+14)%16],@X[$j+1],@X[$j+1]! 0/ 0/ 0:X[1]^=X[14]
+	srl		$a,27,$tmp1
+	add		$tmp0,$e,$e
+	 fxor		@X[($j+2)%16],@X[($j+8)%16],%f18! 1/ 1/ 1:Tmp=X[2,3]^X[8,9]
+	xor		$c,$b,$tmp0
+	add		$tmp1,$e,$e
+	sll		$b,30,$tmp2
+	xor		$d,$tmp0,$tmp1
+	 fxor		%f18,@X[$j],@X[$j]		! 2/ 4/ 3:X[0,1]^=X[2,3]^X[8,9]
+	srl		$b,2,$b
+	add		$tmp1,$e,$e
+	or		$tmp2,$b,$b
+	add		$Xi,$e,$e
+	 faligndata	@X[$j],@X[$j],%f18		! 3/ 7/ 5:Tmp=X[0,1]>>>24
+___
+$code.=<<___ if ($i&1 && $i<64);
+	sll		$a,5,$tmp0			!! $i
+	ld		[$Xfer+`4*($i%16)`],$Xi
+	 fpadd32	@X[$j],@X[$j],@X[$j]		! 4/ 8/ 6:X[0,1]<<=1
+	srl		$a,27,$tmp1
+	add		$tmp0,$e,$e
+	 fmul8ulx16	%f18,$fmul,%f18			! 5/10/ 7:Tmp>>=7, Tmp&=1
+	xor		$c,$b,$tmp0
+	add		$tmp1,$e,$e
+	 fpadd32	$K,@X[$l],%f20			!
+	sll		$b,30,$tmp2
+	xor		$d,$tmp0,$tmp1
+	 fxors		@X[($k+13)%16],@X[$k],@X[$k]	!-1/-1/-1:X[0]^=X[13]
+	srl		$b,2,$b
+	add		$tmp1,$e,$e
+	 fxor		%f18,@X[$j],@X[$j]		! 8/14/10:X[0,1]|=Tmp
+	or		$tmp2,$b,$b
+	add		$Xi,$e,$e
+	 std		%f20,[$Xfer+`4*$l`]		!
+___
+$code.=<<___ if ($i==64);
+	sll		$a,5,$tmp0			!! $i
+	ld		[$Xfer+`4*($i%16)`],$Xi
+	 fpadd32	$K,@X[$l],%f20
+	srl		$a,27,$tmp1
+	add		$tmp0,$e,$e
+	xor		$c,$b,$tmp0
+	add		$tmp1,$e,$e
+	sll		$b,30,$tmp2
+	xor		$d,$tmp0,$tmp1
+	 std		%f20,[$Xfer+`4*$l`]
+	srl		$b,2,$b
+	add		$tmp1,$e,$e
+	or		$tmp2,$b,$b
+	add		$Xi,$e,$e
+___
+$code.=<<___ if ($i>64);
+	sll		$a,5,$tmp0			!! $i
+	ld		[$Xfer+`4*($i%16)`],$Xi
+	srl		$a,27,$tmp1
+	add		$tmp0,$e,$e
+	xor		$c,$b,$tmp0
+	add		$tmp1,$e,$e
+	sll		$b,30,$tmp2
+	xor		$d,$tmp0,$tmp1
+	srl		$b,2,$b
+	add		$tmp1,$e,$e
+	or		$tmp2,$b,$b
+	add		$Xi,$e,$e
+___
+}
+
+sub BODY_40_59 {
+my ($i,$a,$b,$c,$d,$e)=@_;
+my $j=$i&~1;
+my $k=($j+16+2)%16;	# ahead reference
+my $l=($j+16-2)%16;	# behind reference
+my $K=@VK[($j+16-2)/20];
+
+$j=($j+16)%16;
+
+$code.=<<___ if (!($i&1));
+	sll		$a,5,$tmp0			!! $i
+	ld		[$Xfer+`4*($i%16)`],$Xi
+	 fxors		@X[($j+14)%16],@X[$j+1],@X[$j+1]! 0/ 0/ 0:X[1]^=X[14]
+	srl		$a,27,$tmp1
+	add		$tmp0,$e,$e
+	 fxor		@X[($j+2)%16],@X[($j+8)%16],%f18! 1/ 1/ 1:Tmp=X[2,3]^X[8,9]
+	and		$c,$b,$tmp0
+	add		$tmp1,$e,$e
+	sll		$b,30,$tmp2
+	or		$c,$b,$tmp1
+	 fxor		%f18,@X[$j],@X[$j]		! 2/ 4/ 3:X[0,1]^=X[2,3]^X[8,9]
+	srl		$b,2,$b
+	and		$d,$tmp1,$tmp1
+	add		$Xi,$e,$e
+	or		$tmp1,$tmp0,$tmp1
+	 faligndata	@X[$j],@X[$j],%f18		! 3/ 7/ 5:Tmp=X[0,1]>>>24
+	or		$tmp2,$b,$b
+	add		$tmp1,$e,$e
+	 fpadd32	@X[$j],@X[$j],@X[$j]		! 4/ 8/ 6:X[0,1]<<=1
+___
+$code.=<<___ if ($i&1);
+	sll		$a,5,$tmp0			!! $i
+	ld		[$Xfer+`4*($i%16)`],$Xi
+	srl		$a,27,$tmp1
+	add		$tmp0,$e,$e
+	 fmul8ulx16	%f18,$fmul,%f18			! 5/10/ 7:Tmp>>=7, Tmp&=1
+	and		$c,$b,$tmp0
+	add		$tmp1,$e,$e
+	 fpadd32	$K,@X[$l],%f20			!
+	sll		$b,30,$tmp2
+	or		$c,$b,$tmp1
+	 fxors		@X[($k+13)%16],@X[$k],@X[$k]	!-1/-1/-1:X[0]^=X[13]
+	srl		$b,2,$b
+	and		$d,$tmp1,$tmp1
+	 fxor		%f18,@X[$j],@X[$j]		! 8/14/10:X[0,1]|=Tmp
+	add		$Xi,$e,$e
+	or		$tmp1,$tmp0,$tmp1
+	or		$tmp2,$b,$b
+	add		$tmp1,$e,$e
+	 std		%f20,[$Xfer+`4*$l`]		!
+___
+}
+
+# If there is more data to process, then we pre-fetch the data for
+# next iteration in last ten rounds...
+sub BODY_70_79 {
+my ($i,$a,$b,$c,$d,$e)=@_;
+my $j=$i&~1;
+my $m=($i%8)*2;
+
+$j=($j+16)%16;
+
+$code.=<<___ if ($i==70);
+	sll		$a,5,$tmp0			!! $i
+	ld		[$Xfer+`4*($i%16)`],$Xi
+	srl		$a,27,$tmp1
+	add		$tmp0,$e,$e
+	 ldd		[$inp+64],@X[0]
+	xor		$c,$b,$tmp0
+	add		$tmp1,$e,$e
+	sll		$b,30,$tmp2
+	xor		$d,$tmp0,$tmp1
+	srl		$b,2,$b
+	add		$tmp1,$e,$e
+	or		$tmp2,$b,$b
+	add		$Xi,$e,$e
+
+	and		$inp,-64,$nXfer
+	inc		64,$inp
+	and		$nXfer,255,$nXfer
+	alignaddr	%g0,$align,%g0
+	add		$base,$nXfer,$nXfer
+___
+$code.=<<___ if ($i==71);
+	sll		$a,5,$tmp0			!! $i
+	ld		[$Xfer+`4*($i%16)`],$Xi
+	srl		$a,27,$tmp1
+	add		$tmp0,$e,$e
+	xor		$c,$b,$tmp0
+	add		$tmp1,$e,$e
+	sll		$b,30,$tmp2
+	xor		$d,$tmp0,$tmp1
+	srl		$b,2,$b
+	add		$tmp1,$e,$e
+	or		$tmp2,$b,$b
+	add		$Xi,$e,$e
+___
+$code.=<<___ if ($i>=72);
+	 faligndata	@X[$m],@X[$m+2],@X[$m]
+	sll		$a,5,$tmp0			!! $i
+	ld		[$Xfer+`4*($i%16)`],$Xi
+	srl		$a,27,$tmp1
+	add		$tmp0,$e,$e
+	xor		$c,$b,$tmp0
+	add		$tmp1,$e,$e
+	 fpadd32	$VK_00_19,@X[$m],%f20
+	sll		$b,30,$tmp2
+	xor		$d,$tmp0,$tmp1
+	srl		$b,2,$b
+	add		$tmp1,$e,$e
+	or		$tmp2,$b,$b
+	add		$Xi,$e,$e
+___
+$code.=<<___ if ($i<77);
+	 ldd		[$inp+`8*($i+1-70)`],@X[2*($i+1-70)]
+___
+$code.=<<___ if ($i==77);	# redundant if $inp was aligned
+	 add		$align,63,$tmp0
+	 and		$tmp0,-8,$tmp0
+	 ldd		[$inp+$tmp0],@X[16]
+___
+$code.=<<___ if ($i>=72);
+	 std		%f20,[$nXfer+`4*$m`]
+___
+}
+
+$code.=<<___;
+.section	".text",#alloc,#execinstr
+
+.align	64
+vis_const:
+.long	0x5a827999,0x5a827999	! K_00_19
+.long	0x6ed9eba1,0x6ed9eba1	! K_20_39
+.long	0x8f1bbcdc,0x8f1bbcdc	! K_40_59
+.long	0xca62c1d6,0xca62c1d6	! K_60_79
+.long	0x00000100,0x00000100
+.align	64
+.type	vis_const,#object
+.size	vis_const,(.-vis_const)
+
+.globl	sha1_block_data_order
+sha1_block_data_order:
+	save	%sp,-$frame,%sp
+	add	%fp,$bias-256,$base
+
+1:	call	.+8
+	add	%o7,vis_const-1b,$tmp0
+
+	ldd	[$tmp0+0],$VK_00_19
+	ldd	[$tmp0+8],$VK_20_39
+	ldd	[$tmp0+16],$VK_40_59
+	ldd	[$tmp0+24],$VK_60_79
+	ldd	[$tmp0+32],$fmul
+
+	ld	[$ctx+0],$Actx
+	and	$base,-256,$base
+	ld	[$ctx+4],$Bctx
+	sub	$base,$bias+$frame,%sp
+	ld	[$ctx+8],$Cctx
+	and	$inp,7,$align
+	ld	[$ctx+12],$Dctx
+	and	$inp,-8,$inp
+	ld	[$ctx+16],$Ectx
+
+	! X[16] is maintained in FP register bank
+	alignaddr	%g0,$align,%g0
+	ldd		[$inp+0],@X[0]
+	sub		$inp,-64,$Xfer
+	ldd		[$inp+8],@X[2]
+	and		$Xfer,-64,$Xfer
+	ldd		[$inp+16],@X[4]
+	and		$Xfer,255,$Xfer
+	ldd		[$inp+24],@X[6]
+	add		$base,$Xfer,$Xfer
+	ldd		[$inp+32],@X[8]
+	ldd		[$inp+40],@X[10]
+	ldd		[$inp+48],@X[12]
+	brz,pt		$align,.Laligned
+	ldd		[$inp+56],@X[14]
+
+	ldd		[$inp+64],@X[16]
+	faligndata	@X[0],@X[2],@X[0]
+	faligndata	@X[2],@X[4],@X[2]
+	faligndata	@X[4],@X[6],@X[4]
+	faligndata	@X[6],@X[8],@X[6]
+	faligndata	@X[8],@X[10],@X[8]
+	faligndata	@X[10],@X[12],@X[10]
+	faligndata	@X[12],@X[14],@X[12]
+	faligndata	@X[14],@X[16],@X[14]
+
+.Laligned:
+	mov		5,$tmp0
+	dec		1,$len
+	alignaddr	%g0,$tmp0,%g0
+	fpadd32		$VK_00_19,@X[0],%f16
+	fpadd32		$VK_00_19,@X[2],%f18
+	fpadd32		$VK_00_19,@X[4],%f20
+	fpadd32		$VK_00_19,@X[6],%f22
+	fpadd32		$VK_00_19,@X[8],%f24
+	fpadd32		$VK_00_19,@X[10],%f26
+	fpadd32		$VK_00_19,@X[12],%f28
+	fpadd32		$VK_00_19,@X[14],%f30
+	std		%f16,[$Xfer+0]
+	mov		$Actx,$A
+	std		%f18,[$Xfer+8]
+	mov		$Bctx,$B
+	std		%f20,[$Xfer+16]
+	mov		$Cctx,$C
+	std		%f22,[$Xfer+24]
+	mov		$Dctx,$D
+	std		%f24,[$Xfer+32]
+	mov		$Ectx,$E
+	std		%f26,[$Xfer+40]
+	fxors		@X[13],@X[0],@X[0]
+	std		%f28,[$Xfer+48]
+	ba		.Loop
+	std		%f30,[$Xfer+56]
+.align	32
+.Loop:
+___
+for ($i=0;$i<20;$i++)	{ &BODY_00_19($i,@V); unshift(@V,pop(@V)); }
+for (;$i<40;$i++)	{ &BODY_20_39($i,@V); unshift(@V,pop(@V)); }
+for (;$i<60;$i++)	{ &BODY_40_59($i,@V); unshift(@V,pop(@V)); }
+for (;$i<70;$i++)	{ &BODY_20_39($i,@V); unshift(@V,pop(@V)); }
+$code.=<<___;
+	tst		$len
+	bz,pn		`$bits==32?"%icc":"%xcc"`,.Ltail
+	nop
+___
+for (;$i<80;$i++)	{ &BODY_70_79($i,@V); unshift(@V,pop(@V)); }
+$code.=<<___;
+	add		$A,$Actx,$Actx
+	add		$B,$Bctx,$Bctx
+	add		$C,$Cctx,$Cctx
+	add		$D,$Dctx,$Dctx
+	add		$E,$Ectx,$Ectx
+	mov		5,$tmp0
+	fxors		@X[13],@X[0],@X[0]
+	mov		$Actx,$A
+	mov		$Bctx,$B
+	mov		$Cctx,$C
+	mov		$Dctx,$D
+	mov		$Ectx,$E
+	alignaddr	%g0,$tmp0,%g0	
+	dec		1,$len
+	ba		.Loop
+	mov		$nXfer,$Xfer
+
+.align	32
+.Ltail:
+___
+for($i=70;$i<80;$i++)	{ &BODY_20_39($i,@V); unshift(@V,pop(@V)); }
+$code.=<<___;
+	add	$A,$Actx,$Actx
+	add	$B,$Bctx,$Bctx
+	add	$C,$Cctx,$Cctx
+	add	$D,$Dctx,$Dctx
+	add	$E,$Ectx,$Ectx
+
+	st	$Actx,[$ctx+0]
+	st	$Bctx,[$ctx+4]
+	st	$Cctx,[$ctx+8]
+	st	$Dctx,[$ctx+12]
+	st	$Ectx,[$ctx+16]
+
+	ret
+	restore
+.type	sha1_block_data_order,#function
+.size	sha1_block_data_order,(.-sha1_block_data_order)
+.asciz	"SHA1 block transform for SPARCv9a, CRYPTOGAMS by "
+.align	4
+___
+
+# Purpose of these subroutines is to explicitly encode VIS instructions,
+# so that one can compile the module without having to specify VIS
+# extensions on compiler command line, e.g. -xarch=v9 vs. -xarch=v9a.
+# Idea is to reserve for option to produce "universal" binary and let
+# programmer detect if current CPU is VIS capable at run-time.
+sub unvis {
+my ($mnemonic,$rs1,$rs2,$rd)=@_;
+my ($ref,$opf);
+my %visopf = (	"fmul8ulx16"	=> 0x037,
+		"faligndata"	=> 0x048,
+		"fpadd32"	=> 0x052,
+		"fxor"		=> 0x06c,
+		"fxors"		=> 0x06d	);
+
+    $ref = "$mnemonic\t$rs1,$rs2,$rd";
+
+    if ($opf=$visopf{$mnemonic}) {
+	foreach ($rs1,$rs2,$rd) {
+	    return $ref if (!/%f([0-9]{1,2})/);
+	    $_=$1;
+	    if ($1>=32) {
+		return $ref if ($1&1);
+		# re-encode for upper double register addressing
+		$_=($1|$1>>5)&31;
+	    }
+	}
+
+	return	sprintf ".word\t0x%08x !%s",
+			0x81b00000|$rd<<25|$rs1<<14|$opf<<5|$rs2,
+			$ref;
+    } else {
+	return $ref;
+    }
+}
+sub unalignaddr {
+my ($mnemonic,$rs1,$rs2,$rd)=@_;
+my %bias = ( "g" => 0, "o" => 8, "l" => 16, "i" => 24 );
+my $ref="$mnemonic\t$rs1,$rs2,$rd";
+
+    foreach ($rs1,$rs2,$rd) {
+	if (/%([goli])([0-7])/)	{ $_=$bias{$1}+$2; }
+	else			{ return $ref; }
+    }
+    return  sprintf ".word\t0x%08x !%s",
+		    0x81b00300|$rd<<25|$rs1<<14|$rs2,
+		    $ref;
+}
+
+$code =~ s/\`([^\`]*)\`/eval $1/gem;
+$code =~ s/\b(f[^\s]*)\s+(%f[0-9]{1,2}),(%f[0-9]{1,2}),(%f[0-9]{1,2})/
+		&unvis($1,$2,$3,$4)
+	  /gem;
+$code =~ s/\b(alignaddr)\s+(%[goli][0-7]),(%[goli][0-7]),(%[goli][0-7])/
+		&unalignaddr($1,$2,$3,$4)
+	  /gem;
+print $code;
+close STDOUT;
diff --git a/crypto/sha/asm/sha1-thumb.pl b/crypto/sha/asm/sha1-thumb.pl
new file mode 100644
index 000000000..661fd9f9f
--- /dev/null
+++ b/crypto/sha/asm/sha1-thumb.pl
@@ -0,0 +1,266 @@
+#! /usr/bin/env perl
+# Copyright 2007-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+
+# ====================================================================
+# Written by Andy Polyakov  for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+
+# sha1_block for Thumb.
+#
+# January 2007.
+#
+# The code does not present direct interest to OpenSSL, because of low
+# performance. Its purpose is to establish _size_ benchmark. Pretty
+# useless one I must say, because 30% or 88 bytes larger ARMv4 code
+# [avialable on demand] is almost _twice_ as fast. It should also be
+# noted that in-lining of .Lcommon and .Lrotate improves performance
+# by over 40%, while code increases by only 10% or 32 bytes. But once
+# again, the goal was to establish _size_ benchmark, not performance.
+
+$output=shift;
+open STDOUT,">$output";
+
+$inline=0;
+#$cheat_on_binutils=1;
+
+$t0="r0";
+$t1="r1";
+$t2="r2";
+$a="r3";
+$b="r4";
+$c="r5";
+$d="r6";
+$e="r7";
+$K="r8";	# "upper" registers can be used in add/sub and mov insns
+$ctx="r9";
+$inp="r10";
+$len="r11";
+$Xi="r12";
+
+sub common {
+<<___;
+	sub	$t0,#4
+	ldr	$t1,[$t0]
+	add	$e,$K			@ E+=K_xx_xx
+	lsl	$t2,$a,#5
+	add	$t2,$e
+	lsr	$e,$a,#27
+	add	$t2,$e			@ E+=ROR(A,27)
+	add	$t2,$t1			@ E+=X[i]
+___
+}
+sub rotate {
+<<___;
+	mov	$e,$d			@ E=D
+	mov	$d,$c			@ D=C
+	lsl	$c,$b,#30
+	lsr	$b,$b,#2
+	orr	$c,$b			@ C=ROR(B,2)
+	mov	$b,$a			@ B=A
+	add	$a,$t2,$t1		@ A=E+F_xx_xx(B,C,D)
+___
+}
+
+sub BODY_00_19 {
+$code.=$inline?&common():"\tbl	.Lcommon\n";
+$code.=<<___;
+	mov	$t1,$c
+	eor	$t1,$d
+	and	$t1,$b
+	eor	$t1,$d			@ F_00_19(B,C,D)
+___
+$code.=$inline?&rotate():"\tbl	.Lrotate\n";
+}
+
+sub BODY_20_39 {
+$code.=$inline?&common():"\tbl	.Lcommon\n";
+$code.=<<___;
+	mov	$t1,$b
+	eor	$t1,$c
+	eor	$t1,$d			@ F_20_39(B,C,D)
+___
+$code.=$inline?&rotate():"\tbl	.Lrotate\n";
+}
+
+sub BODY_40_59 {
+$code.=$inline?&common():"\tbl	.Lcommon\n";
+$code.=<<___;
+	mov	$t1,$b
+	and	$t1,$c
+	mov	$e,$b
+	orr	$e,$c
+	and	$e,$d
+	orr	$t1,$e			@ F_40_59(B,C,D)
+___
+$code.=$inline?&rotate():"\tbl	.Lrotate\n";
+}
+
+$code=<<___;
+.text
+.code	16
+
+.global	sha1_block_data_order
+.type	sha1_block_data_order,%function
+
+.align	2
+sha1_block_data_order:
+___
+if ($cheat_on_binutils) {
+$code.=<<___;
+.code	32
+	add	r3,pc,#1
+	bx	r3			@ switch to Thumb ISA
+.code	16
+___
+}
+$code.=<<___;
+	push	{r4-r7}
+	mov	r3,r8
+	mov	r4,r9
+	mov	r5,r10
+	mov	r6,r11
+	mov	r7,r12
+	push	{r3-r7,lr}
+	lsl	r2,#6
+	mov	$ctx,r0			@ save context
+	mov	$inp,r1			@ save inp
+	mov	$len,r2			@ save len
+	add	$len,$inp		@ $len to point at inp end
+
+.Lloop:
+	mov	$Xi,sp
+	mov	$t2,sp
+	sub	$t2,#16*4		@ [3]
+.LXload:
+	ldrb	$a,[$t1,#0]		@ $t1 is r1 and holds inp
+	ldrb	$b,[$t1,#1]
+	ldrb	$c,[$t1,#2]
+	ldrb	$d,[$t1,#3]
+	lsl	$a,#24
+	lsl	$b,#16
+	lsl	$c,#8
+	orr	$a,$b
+	orr	$a,$c
+	orr	$a,$d
+	add	$t1,#4
+	push	{$a}
+	cmp	sp,$t2
+	bne	.LXload			@ [+14*16]
+
+	mov	$inp,$t1		@ update $inp
+	sub	$t2,#32*4
+	sub	$t2,#32*4
+	mov	$e,#31			@ [+4]
+.LXupdate:
+	ldr	$a,[sp,#15*4]
+	ldr	$b,[sp,#13*4]
+	ldr	$c,[sp,#7*4]
+	ldr	$d,[sp,#2*4]
+	eor	$a,$b
+	eor	$a,$c
+	eor	$a,$d
+	ror	$a,$e
+	push	{$a}
+	cmp	sp,$t2
+	bne	.LXupdate		@ [+(11+1)*64]
+
+	ldmia	$t0!,{$a,$b,$c,$d,$e}	@ $t0 is r0 and holds ctx
+	mov	$t0,$Xi
+
+	ldr	$t2,.LK_00_19
+	mov	$t1,$t0
+	sub	$t1,#20*4
+	mov	$Xi,$t1
+	mov	$K,$t2			@ [+7+4]
+.L_00_19:
+___
+	&BODY_00_19();
+$code.=<<___;
+	cmp	$Xi,$t0
+	bne	.L_00_19		@ [+(2+9+4+2+8+2)*20]
+
+	ldr	$t2,.LK_20_39
+	mov	$t1,$t0
+	sub	$t1,#20*4
+	mov	$Xi,$t1
+	mov	$K,$t2			@ [+5]
+.L_20_39_or_60_79:
+___
+	&BODY_20_39();
+$code.=<<___;
+	cmp	$Xi,$t0
+	bne	.L_20_39_or_60_79	@ [+(2+9+3+2+8+2)*20*2]
+	cmp	sp,$t0
+	beq	.Ldone			@ [+2]
+
+	ldr	$t2,.LK_40_59
+	mov	$t1,$t0
+	sub	$t1,#20*4
+	mov	$Xi,$t1
+	mov	$K,$t2			@ [+5]
+.L_40_59:
+___
+	&BODY_40_59();
+$code.=<<___;
+	cmp	$Xi,$t0
+	bne	.L_40_59		@ [+(2+9+6+2+8+2)*20]
+
+	ldr	$t2,.LK_60_79
+	mov	$Xi,sp
+	mov	$K,$t2
+	b	.L_20_39_or_60_79	@ [+4]
+.Ldone:
+	mov	$t0,$ctx
+	ldr	$t1,[$t0,#0]
+	ldr	$t2,[$t0,#4]
+	add	$a,$t1
+	ldr	$t1,[$t0,#8]
+	add	$b,$t2
+	ldr	$t2,[$t0,#12]
+	add	$c,$t1
+	ldr	$t1,[$t0,#16]
+	add	$d,$t2
+	add	$e,$t1
+	stmia	$t0!,{$a,$b,$c,$d,$e}	@ [+20]
+
+	add	sp,#80*4		@ deallocate stack frame
+	mov	$t0,$ctx		@ restore ctx
+	mov	$t1,$inp		@ restore inp
+	cmp	$t1,$len
+	beq	.Lexit
+	b	.Lloop			@ [+6] total 3212 cycles
+.Lexit:
+	pop	{r2-r7}
+	mov	r8,r2
+	mov	r9,r3
+	mov	r10,r4
+	mov	r11,r5
+	mov	r12,r6
+	mov	lr,r7
+	pop	{r4-r7}
+	bx	lr
+.align	2
+___
+$code.=".Lcommon:\n".&common()."\tmov	pc,lr\n" if (!$inline);
+$code.=".Lrotate:\n".&rotate()."\tmov	pc,lr\n" if (!$inline);
+$code.=<<___;
+.align	2
+.LK_00_19:	.word	0x5a827999
+.LK_20_39:	.word	0x6ed9eba1
+.LK_40_59:	.word	0x8f1bbcdc
+.LK_60_79:	.word	0xca62c1d6
+.size	sha1_block_data_order,.-sha1_block_data_order
+.asciz	"SHA1 block transform for Thumb, CRYPTOGAMS by "
+___
+
+print $code;
+close STDOUT; # enforce flush
diff --git a/crypto/sha/asm/sha1-x86_64.pl b/crypto/sha/asm/sha1-x86_64.pl
new file mode 100755
index 000000000..e11c6e474
--- /dev/null
+++ b/crypto/sha/asm/sha1-x86_64.pl
@@ -0,0 +1,2077 @@
+#! /usr/bin/env perl
+# Copyright 2006-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+#
+# ====================================================================
+# Written by Andy Polyakov  for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+#
+# sha1_block procedure for x86_64.
+#
+# It was brought to my attention that on EM64T compiler-generated code
+# was far behind 32-bit assembler implementation. This is unlike on
+# Opteron where compiler-generated code was only 15% behind 32-bit
+# assembler, which originally made it hard to motivate the effort.
+# There was suggestion to mechanically translate 32-bit code, but I
+# dismissed it, reasoning that x86_64 offers enough register bank
+# capacity to fully utilize SHA-1 parallelism. Therefore this fresh
+# implementation:-) However! While 64-bit code does perform better
+# on Opteron, I failed to beat 32-bit assembler on EM64T core. Well,
+# x86_64 does offer larger *addressable* bank, but out-of-order core
+# reaches for even more registers through dynamic aliasing, and EM64T
+# core must have managed to run-time optimize even 32-bit code just as
+# good as 64-bit one. Performance improvement is summarized in the
+# following table:
+#
+#		gcc 3.4		32-bit asm	cycles/byte
+# Opteron	+45%		+20%		6.8
+# Xeon P4	+65%		+0%		9.9
+# Core2		+60%		+10%		7.0
+
+# August 2009.
+#
+# The code was revised to minimize code size and to maximize
+# "distance" between instructions producing input to 'lea'
+# instruction and the 'lea' instruction itself, which is essential
+# for Intel Atom core.
+
+# October 2010.
+#
+# Add SSSE3, Supplemental[!] SSE3, implementation. The idea behind it
+# is to offload message schedule denoted by Wt in NIST specification,
+# or Xupdate in OpenSSL source, to SIMD unit. See sha1-586.pl module
+# for background and implementation details. The only difference from
+# 32-bit code is that 64-bit code doesn't have to spill @X[] elements
+# to free temporary registers.
+
+# April 2011.
+#
+# Add AVX code path. See sha1-586.pl for further information.
+
+# May 2013.
+#
+# Add AVX2+BMI code path. Initial attempt (utilizing BMI instructions
+# and loading pair of consecutive blocks to 256-bit %ymm registers)
+# did not provide impressive performance improvement till a crucial
+# hint regarding the number of Xupdate iterations to pre-compute in
+# advance was provided by Ilya Albrekht of Intel Corp.
+
+# March 2014.
+#
+# Add support for Intel SHA Extensions.
+
+######################################################################
+# Current performance is summarized in following table. Numbers are
+# CPU clock cycles spent to process single byte (less is better).
+#
+#		x86_64		SSSE3		AVX[2]
+# P4		9.05		-
+# Opteron	6.26		-
+# Core2		6.55		6.05/+8%	-
+# Westmere	6.73		5.30/+27%	-
+# Sandy Bridge	7.70		6.10/+26%	4.99/+54%
+# Ivy Bridge	6.06		4.67/+30%	4.60/+32%
+# Haswell	5.45		4.15/+31%	3.57/+53%
+# Skylake	5.18		4.06/+28%	3.54/+46%
+# Bulldozer	9.11		5.95/+53%
+# VIA Nano	9.32		7.15/+30%
+# Atom		10.3		9.17/+12%
+# Silvermont	13.1(*)		9.37/+40%
+# Goldmont	8.13		6.42/+27%	1.70/+380%(**)
+#
+# (*)	obviously suboptimal result, nothing was done about it,
+#	because SSSE3 code is compiled unconditionally;
+# (**)	SHAEXT result
+
+$flavour = shift;
+$output  = shift;
+if ($flavour =~ /\./) { $output = $flavour; undef $flavour; }
+
+$win64=0; $win64=1 if ($flavour =~ /[nm]asm|mingw64/ || $output =~ /\.asm$/);
+
+$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+( $xlate="${dir}x86_64-xlate.pl" and -f $xlate ) or
+( $xlate="${dir}../../perlasm/x86_64-xlate.pl" and -f $xlate) or
+die "can't locate x86_64-xlate.pl";
+
+if (`$ENV{CC} -Wa,-v -c -o /dev/null -x assembler /dev/null 2>&1`
+		=~ /GNU assembler version ([2-9]\.[0-9]+)/) {
+	$avx = ($1>=2.19) + ($1>=2.22);
+}
+
+if (!$avx && $win64 && ($flavour =~ /nasm/ || $ENV{ASM} =~ /nasm/) &&
+	   `nasm -v 2>&1` =~ /NASM version ([2-9]\.[0-9]+)/) {
+	$avx = ($1>=2.09) + ($1>=2.10);
+}
+
+if (!$avx && $win64 && ($flavour =~ /masm/ || $ENV{ASM} =~ /ml64/) &&
+	   `ml64 2>&1` =~ /Version ([0-9]+)\./) {
+	$avx = ($1>=10) + ($1>=11);
+}
+
+if (!$avx && `$ENV{CC} -v 2>&1` =~ /((?:^clang|LLVM) version|.*based on LLVM) ([2-9]\.[0-9]+)/) {
+	$avx = ($2>=3.0) + ($2>3.0);
+}
+
+$shaext=1;	### set to zero if compiling for 1.0.1
+$avx=1		if (!$shaext && $avx);
+
+open OUT,"| \"$^X\" \"$xlate\" $flavour \"$output\"";
+*STDOUT=*OUT;
+
+$ctx="%rdi";	# 1st arg
+$inp="%rsi";	# 2nd arg
+$num="%rdx";	# 3rd arg
+
+# reassign arguments in order to produce more compact code
+$ctx="%r8";
+$inp="%r9";
+$num="%r10";
+
+$t0="%eax";
+$t1="%ebx";
+$t2="%ecx";
+@xi=("%edx","%ebp","%r14d");
+$A="%esi";
+$B="%edi";
+$C="%r11d";
+$D="%r12d";
+$E="%r13d";
+
+@V=($A,$B,$C,$D,$E);
+
+sub BODY_00_19 {
+my ($i,$a,$b,$c,$d,$e)=@_;
+my $j=$i+1;
+$code.=<<___ if ($i==0);
+	mov	`4*$i`($inp),$xi[0]
+	bswap	$xi[0]
+___
+$code.=<<___ if ($i<15);
+	mov	`4*$j`($inp),$xi[1]
+	mov	$d,$t0
+	mov	$xi[0],`4*$i`(%rsp)
+	mov	$a,$t2
+	bswap	$xi[1]
+	xor	$c,$t0
+	rol	\$5,$t2
+	and	$b,$t0
+	lea	0x5a827999($xi[0],$e),$e
+	add	$t2,$e
+	xor	$d,$t0
+	rol	\$30,$b
+	add	$t0,$e
+___
+$code.=<<___ if ($i>=15);
+	xor	`4*($j%16)`(%rsp),$xi[1]
+	mov	$d,$t0
+	mov	$xi[0],`4*($i%16)`(%rsp)
+	mov	$a,$t2
+	xor	`4*(($j+2)%16)`(%rsp),$xi[1]
+	xor	$c,$t0
+	rol	\$5,$t2
+	xor	`4*(($j+8)%16)`(%rsp),$xi[1]
+	and	$b,$t0
+	lea	0x5a827999($xi[0],$e),$e
+	rol	\$30,$b
+	xor	$d,$t0
+	add	$t2,$e
+	rol	\$1,$xi[1]
+	add	$t0,$e
+___
+push(@xi,shift(@xi));
+}
+
+sub BODY_20_39 {
+my ($i,$a,$b,$c,$d,$e)=@_;
+my $j=$i+1;
+my $K=($i<40)?0x6ed9eba1:0xca62c1d6;
+$code.=<<___ if ($i<79);
+	xor	`4*($j%16)`(%rsp),$xi[1]
+	mov	$b,$t0
+	`"mov	$xi[0],".4*($i%16)."(%rsp)"	if ($i<72)`
+	mov	$a,$t2
+	xor	`4*(($j+2)%16)`(%rsp),$xi[1]
+	xor	$d,$t0
+	rol	\$5,$t2
+	xor	`4*(($j+8)%16)`(%rsp),$xi[1]
+	lea	$K($xi[0],$e),$e
+	xor	$c,$t0
+	add	$t2,$e
+	rol	\$30,$b
+	add	$t0,$e
+	rol	\$1,$xi[1]
+___
+$code.=<<___ if ($i==79);
+	mov	$b,$t0
+	mov	$a,$t2
+	xor	$d,$t0
+	lea	$K($xi[0],$e),$e
+	rol	\$5,$t2
+	xor	$c,$t0
+	add	$t2,$e
+	rol	\$30,$b
+	add	$t0,$e
+___
+push(@xi,shift(@xi));
+}
+
+sub BODY_40_59 {
+my ($i,$a,$b,$c,$d,$e)=@_;
+my $j=$i+1;
+$code.=<<___;
+	xor	`4*($j%16)`(%rsp),$xi[1]
+	mov	$d,$t0
+	mov	$xi[0],`4*($i%16)`(%rsp)
+	mov	$d,$t1
+	xor	`4*(($j+2)%16)`(%rsp),$xi[1]
+	and	$c,$t0
+	mov	$a,$t2
+	xor	`4*(($j+8)%16)`(%rsp),$xi[1]
+	lea	0x8f1bbcdc($xi[0],$e),$e
+	xor	$c,$t1
+	rol	\$5,$t2
+	add	$t0,$e
+	rol	\$1,$xi[1]
+	and	$b,$t1
+	add	$t2,$e
+	rol	\$30,$b
+	add	$t1,$e
+___
+push(@xi,shift(@xi));
+}
+
+$code.=<<___;
+.text
+.extern	OPENSSL_ia32cap_P
+
+.globl	sha1_block_data_order
+.type	sha1_block_data_order,\@function,3
+.align	16
+sha1_block_data_order:
+	mov	OPENSSL_ia32cap_P+0(%rip),%r9d
+	mov	OPENSSL_ia32cap_P+4(%rip),%r8d
+	mov	OPENSSL_ia32cap_P+8(%rip),%r10d
+	test	\$`1<<9`,%r8d		# check SSSE3 bit
+	jz	.Lialu
+___
+$code.=<<___ if ($shaext);
+	test	\$`1<<29`,%r10d		# check SHA bit	
+	jnz	_shaext_shortcut
+___
+$code.=<<___ if ($avx>1);
+	and	\$`1<<3|1<<5|1<<8`,%r10d	# check AVX2+BMI1+BMI2
+	cmp	\$`1<<3|1<<5|1<<8`,%r10d
+	je	_avx2_shortcut
+___
+$code.=<<___ if ($avx);
+	and	\$`1<<28`,%r8d		# mask AVX bit
+	and	\$`1<<30`,%r9d		# mask "Intel CPU" bit
+	or	%r9d,%r8d
+	cmp	\$`1<<28|1<<30`,%r8d
+	je	_avx_shortcut
+___
+$code.=<<___;
+	jmp	_ssse3_shortcut
+
+.align	16
+.Lialu:
+	mov	%rsp,%rax
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	mov	%rdi,$ctx	# reassigned argument
+	sub	\$`8+16*4`,%rsp
+	mov	%rsi,$inp	# reassigned argument
+	and	\$-64,%rsp
+	mov	%rdx,$num	# reassigned argument
+	mov	%rax,`16*4`(%rsp)
+.Lprologue:
+
+	mov	0($ctx),$A
+	mov	4($ctx),$B
+	mov	8($ctx),$C
+	mov	12($ctx),$D
+	mov	16($ctx),$E
+	jmp	.Lloop
+
+.align	16
+.Lloop:
+___
+for($i=0;$i<20;$i++)	{ &BODY_00_19($i,@V); unshift(@V,pop(@V)); }
+for(;$i<40;$i++)	{ &BODY_20_39($i,@V); unshift(@V,pop(@V)); }
+for(;$i<60;$i++)	{ &BODY_40_59($i,@V); unshift(@V,pop(@V)); }
+for(;$i<80;$i++)	{ &BODY_20_39($i,@V); unshift(@V,pop(@V)); }
+$code.=<<___;
+	add	0($ctx),$A
+	add	4($ctx),$B
+	add	8($ctx),$C
+	add	12($ctx),$D
+	add	16($ctx),$E
+	mov	$A,0($ctx)
+	mov	$B,4($ctx)
+	mov	$C,8($ctx)
+	mov	$D,12($ctx)
+	mov	$E,16($ctx)
+
+	sub	\$1,$num
+	lea	`16*4`($inp),$inp
+	jnz	.Lloop
+
+	mov	`16*4`(%rsp),%rsi
+	mov	-40(%rsi),%r14
+	mov	-32(%rsi),%r13
+	mov	-24(%rsi),%r12
+	mov	-16(%rsi),%rbp
+	mov	-8(%rsi),%rbx
+	lea	(%rsi),%rsp
+.Lepilogue:
+	ret
+.size	sha1_block_data_order,.-sha1_block_data_order
+___
+if ($shaext) {{{
+######################################################################
+# Intel SHA Extensions implementation of SHA1 update function.
+#
+my ($ctx,$inp,$num)=("%rdi","%rsi","%rdx");
+my ($ABCD,$E,$E_,$BSWAP,$ABCD_SAVE,$E_SAVE)=map("%xmm$_",(0..3,8,9));
+my @MSG=map("%xmm$_",(4..7));
+
+$code.=<<___;
+.type	sha1_block_data_order_shaext,\@function,3
+.align	32
+sha1_block_data_order_shaext:
+_shaext_shortcut:
+___
+$code.=<<___ if ($win64);
+	lea	`-8-4*16`(%rsp),%rsp
+	movaps	%xmm6,-8-4*16(%rax)
+	movaps	%xmm7,-8-3*16(%rax)
+	movaps	%xmm8,-8-2*16(%rax)
+	movaps	%xmm9,-8-1*16(%rax)
+.Lprologue_shaext:
+___
+$code.=<<___;
+	movdqu	($ctx),$ABCD
+	movd	16($ctx),$E
+	movdqa	K_XX_XX+0xa0(%rip),$BSWAP	# byte-n-word swap
+
+	movdqu	($inp),@MSG[0]
+	pshufd	\$0b00011011,$ABCD,$ABCD	# flip word order
+	movdqu	0x10($inp),@MSG[1]
+	pshufd	\$0b00011011,$E,$E		# flip word order
+	movdqu	0x20($inp),@MSG[2]
+	pshufb	$BSWAP,@MSG[0]
+	movdqu	0x30($inp),@MSG[3]
+	pshufb	$BSWAP,@MSG[1]
+	pshufb	$BSWAP,@MSG[2]
+	movdqa	$E,$E_SAVE			# offload $E
+	pshufb	$BSWAP,@MSG[3]
+	jmp	.Loop_shaext
+
+.align	16
+.Loop_shaext:
+	dec		$num
+	lea		0x40($inp),%r8		# next input block
+	paddd		@MSG[0],$E
+	cmovne		%r8,$inp
+	movdqa		$ABCD,$ABCD_SAVE	# offload $ABCD
+___
+for($i=0;$i<20-4;$i+=2) {
+$code.=<<___;
+	sha1msg1	@MSG[1],@MSG[0]
+	movdqa		$ABCD,$E_
+	sha1rnds4	\$`int($i/5)`,$E,$ABCD	# 0-3...
+	sha1nexte	@MSG[1],$E_
+	pxor		@MSG[2],@MSG[0]
+	sha1msg1	@MSG[2],@MSG[1]
+	sha1msg2	@MSG[3],@MSG[0]
+
+	movdqa		$ABCD,$E
+	sha1rnds4	\$`int(($i+1)/5)`,$E_,$ABCD
+	sha1nexte	@MSG[2],$E
+	pxor		@MSG[3],@MSG[1]
+	sha1msg2	@MSG[0],@MSG[1]
+___
+	push(@MSG,shift(@MSG));	push(@MSG,shift(@MSG));
+}
+$code.=<<___;
+	movdqu		($inp),@MSG[0]
+	movdqa		$ABCD,$E_
+	sha1rnds4	\$3,$E,$ABCD		# 64-67
+	sha1nexte	@MSG[1],$E_
+	movdqu		0x10($inp),@MSG[1]
+	pshufb		$BSWAP,@MSG[0]
+
+	movdqa		$ABCD,$E
+	sha1rnds4	\$3,$E_,$ABCD		# 68-71
+	sha1nexte	@MSG[2],$E
+	movdqu		0x20($inp),@MSG[2]
+	pshufb		$BSWAP,@MSG[1]
+
+	movdqa		$ABCD,$E_
+	sha1rnds4	\$3,$E,$ABCD		# 72-75
+	sha1nexte	@MSG[3],$E_
+	movdqu		0x30($inp),@MSG[3]
+	pshufb		$BSWAP,@MSG[2]
+
+	movdqa		$ABCD,$E
+	sha1rnds4	\$3,$E_,$ABCD		# 76-79
+	sha1nexte	$E_SAVE,$E
+	pshufb		$BSWAP,@MSG[3]
+
+	paddd		$ABCD_SAVE,$ABCD
+	movdqa		$E,$E_SAVE		# offload $E
+
+	jnz		.Loop_shaext
+
+	pshufd	\$0b00011011,$ABCD,$ABCD
+	pshufd	\$0b00011011,$E,$E
+	movdqu	$ABCD,($ctx)
+	movd	$E,16($ctx)
+___
+$code.=<<___ if ($win64);
+	movaps	-8-4*16(%rax),%xmm6
+	movaps	-8-3*16(%rax),%xmm7
+	movaps	-8-2*16(%rax),%xmm8
+	movaps	-8-1*16(%rax),%xmm9
+	mov	%rax,%rsp
+.Lepilogue_shaext:
+___
+$code.=<<___;
+	ret
+.size	sha1_block_data_order_shaext,.-sha1_block_data_order_shaext
+___
+}}}
+{{{
+my $Xi=4;
+my @X=map("%xmm$_",(4..7,0..3));
+my @Tx=map("%xmm$_",(8..10));
+my $Kx="%xmm11";
+my @V=($A,$B,$C,$D,$E)=("%eax","%ebx","%ecx","%edx","%ebp");	# size optimization
+my @T=("%esi","%edi");
+my $j=0;
+my $rx=0;
+my $K_XX_XX="%r11";
+
+my $_rol=sub { &rol(@_) };
+my $_ror=sub { &ror(@_) };
+
+{ my $sn;
+sub align32() {
+  ++$sn;
+$code.=<<___;
+	jmp	.Lalign32_$sn	# see "Decoded ICache" in manual
+.align	32
+.Lalign32_$sn:
+___
+}
+}
+
+$code.=<<___;
+.type	sha1_block_data_order_ssse3,\@function,3
+.align	16
+sha1_block_data_order_ssse3:
+_ssse3_shortcut:
+	mov	%rsp,%rax
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13		# redundant, done to share Win64 SE handler
+	push	%r14
+	lea	`-64-($win64?6*16:0)`(%rsp),%rsp
+___
+$code.=<<___ if ($win64);
+	movaps	%xmm6,-40-6*16(%rax)
+	movaps	%xmm7,-40-5*16(%rax)
+	movaps	%xmm8,-40-4*16(%rax)
+	movaps	%xmm9,-40-3*16(%rax)
+	movaps	%xmm10,-40-2*16(%rax)
+	movaps	%xmm11,-40-1*16(%rax)
+.Lprologue_ssse3:
+___
+$code.=<<___;
+	mov	%rax,%r14	# original %rsp
+	and	\$-64,%rsp
+	mov	%rdi,$ctx	# reassigned argument
+	mov	%rsi,$inp	# reassigned argument
+	mov	%rdx,$num	# reassigned argument
+
+	shl	\$6,$num
+	add	$inp,$num
+	lea	K_XX_XX+64(%rip),$K_XX_XX
+
+	mov	0($ctx),$A		# load context
+	mov	4($ctx),$B
+	mov	8($ctx),$C
+	mov	12($ctx),$D
+	mov	$B,@T[0]		# magic seed
+	mov	16($ctx),$E
+	mov	$C,@T[1]
+	xor	$D,@T[1]
+	and	@T[1],@T[0]
+
+	movdqa	64($K_XX_XX),@X[2]	# pbswap mask
+	movdqa	-64($K_XX_XX),@Tx[1]	# K_00_19
+	movdqu	0($inp),@X[-4&7]	# load input to %xmm[0-3]
+	movdqu	16($inp),@X[-3&7]
+	movdqu	32($inp),@X[-2&7]
+	movdqu	48($inp),@X[-1&7]
+	pshufb	@X[2],@X[-4&7]		# byte swap
+	pshufb	@X[2],@X[-3&7]
+	pshufb	@X[2],@X[-2&7]
+	add	\$64,$inp
+	paddd	@Tx[1],@X[-4&7]		# add K_00_19
+	pshufb	@X[2],@X[-1&7]
+	paddd	@Tx[1],@X[-3&7]
+	paddd	@Tx[1],@X[-2&7]
+	movdqa	@X[-4&7],0(%rsp)	# X[]+K xfer to IALU
+	psubd	@Tx[1],@X[-4&7]		# restore X[]
+	movdqa	@X[-3&7],16(%rsp)
+	psubd	@Tx[1],@X[-3&7]
+	movdqa	@X[-2&7],32(%rsp)
+	psubd	@Tx[1],@X[-2&7]
+	jmp	.Loop_ssse3
+___
+
+sub AUTOLOAD()		# thunk [simplified] 32-bit style perlasm
+{ my $opcode = $AUTOLOAD; $opcode =~ s/.*:://;
+  my $arg = pop;
+    $arg = "\$$arg" if ($arg*1 eq $arg);
+    $code .= "\t$opcode\t".join(',',$arg,reverse @_)."\n";
+}
+
+sub Xupdate_ssse3_16_31()		# recall that $Xi starts wtih 4
+{ use integer;
+  my $body = shift;
+  my @insns = (&$body,&$body,&$body,&$body);	# 40 instructions
+  my ($a,$b,$c,$d,$e);
+
+	 eval(shift(@insns));		# ror
+	&pshufd	(@X[0],@X[-4&7],0xee);	# was &movdqa	(@X[0],@X[-3&7]);
+	 eval(shift(@insns));
+	&movdqa	(@Tx[0],@X[-1&7]);
+	  &paddd	(@Tx[1],@X[-1&7]);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	&punpcklqdq(@X[0],@X[-3&7]);	# compose "X[-14]" in "X[0]", was &palignr(@X[0],@X[-4&7],8);
+	 eval(shift(@insns));
+	 eval(shift(@insns));		# rol
+	 eval(shift(@insns));
+	&psrldq	(@Tx[0],4);		# "X[-3]", 3 dwords
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	&pxor	(@X[0],@X[-4&7]);	# "X[0]"^="X[-16]"
+	 eval(shift(@insns));
+	 eval(shift(@insns));		# ror
+	&pxor	(@Tx[0],@X[-2&7]);	# "X[-3]"^"X[-8]"
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	&pxor	(@X[0],@Tx[0]);		# "X[0]"^="X[-3]"^"X[-8]"
+	 eval(shift(@insns));
+	 eval(shift(@insns));		# rol
+	  &movdqa	(eval(16*(($Xi-1)&3))."(%rsp)",@Tx[1]);	# X[]+K xfer to IALU
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	&movdqa	(@Tx[2],@X[0]);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));		# ror
+	&movdqa	(@Tx[0],@X[0]);
+	 eval(shift(@insns));
+
+	&pslldq	(@Tx[2],12);		# "X[0]"<<96, extract one dword
+	&paddd	(@X[0],@X[0]);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	&psrld	(@Tx[0],31);
+	 eval(shift(@insns));
+	 eval(shift(@insns));		# rol
+	 eval(shift(@insns));
+	&movdqa	(@Tx[1],@Tx[2]);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	&psrld	(@Tx[2],30);
+	 eval(shift(@insns));
+	 eval(shift(@insns));		# ror
+	&por	(@X[0],@Tx[0]);		# "X[0]"<<<=1
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	&pslld	(@Tx[1],2);
+	&pxor	(@X[0],@Tx[2]);
+	 eval(shift(@insns));
+	  &movdqa	(@Tx[2],eval(2*16*(($Xi)/5)-64)."($K_XX_XX)");	# K_XX_XX
+	 eval(shift(@insns));		# rol
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	&pxor	(@X[0],@Tx[1]);		# "X[0]"^=("X[0]">>96)<<<2
+	&pshufd (@Tx[1],@X[-1&7],0xee)	if ($Xi==7);	# was &movdqa	(@Tx[0],@X[-1&7]) in Xupdate_ssse3_32_79
+
+	 foreach (@insns) { eval; }	# remaining instructions [if any]
+
+  $Xi++;	push(@X,shift(@X));	# "rotate" X[]
+		push(@Tx,shift(@Tx));
+}
+
+sub Xupdate_ssse3_32_79()
+{ use integer;
+  my $body = shift;
+  my @insns = (&$body,&$body,&$body,&$body);	# 32 to 44 instructions
+  my ($a,$b,$c,$d,$e);
+
+	 eval(shift(@insns))		if ($Xi==8);
+	&pxor	(@X[0],@X[-4&7]);	# "X[0]"="X[-32]"^"X[-16]"
+	 eval(shift(@insns))		if ($Xi==8);
+	 eval(shift(@insns));		# body_20_39
+	 eval(shift(@insns));
+	 eval(shift(@insns))		if (@insns[1] =~ /_ror/);
+	 eval(shift(@insns))		if (@insns[0] =~ /_ror/);
+	&punpcklqdq(@Tx[0],@X[-1&7]);	# compose "X[-6]", was &palignr(@Tx[0],@X[-2&7],8);
+	 eval(shift(@insns));
+	 eval(shift(@insns));		# rol
+
+	&pxor	(@X[0],@X[-7&7]);	# "X[0]"^="X[-28]"
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	if ($Xi%5) {
+	  &movdqa	(@Tx[2],@Tx[1]);# "perpetuate" K_XX_XX...
+	} else {			# ... or load next one
+	  &movdqa	(@Tx[2],eval(2*16*($Xi/5)-64)."($K_XX_XX)");
+	}
+	 eval(shift(@insns));		# ror
+	  &paddd	(@Tx[1],@X[-1&7]);
+	 eval(shift(@insns));
+
+	&pxor	(@X[0],@Tx[0]);		# "X[0]"^="X[-6]"
+	 eval(shift(@insns));		# body_20_39
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));		# rol
+	 eval(shift(@insns))		if (@insns[0] =~ /_ror/);
+
+	&movdqa	(@Tx[0],@X[0]);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	  &movdqa	(eval(16*(($Xi-1)&3))."(%rsp)",@Tx[1]);	# X[]+K xfer to IALU
+	 eval(shift(@insns));		# ror
+	 eval(shift(@insns));
+	 eval(shift(@insns));		# body_20_39
+
+	&pslld	(@X[0],2);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&psrld	(@Tx[0],30);
+	 eval(shift(@insns))		if (@insns[0] =~ /_rol/);# rol
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));		# ror
+
+	&por	(@X[0],@Tx[0]);		# "X[0]"<<<=2
+	 eval(shift(@insns));
+	 eval(shift(@insns));		# body_20_39
+	 eval(shift(@insns))		if (@insns[1] =~ /_rol/);
+	 eval(shift(@insns))		if (@insns[0] =~ /_rol/);
+	  &pshufd(@Tx[1],@X[-1&7],0xee)	if ($Xi<19);	# was &movdqa	(@Tx[1],@X[0])
+	 eval(shift(@insns));
+	 eval(shift(@insns));		# rol
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));		# rol
+	 eval(shift(@insns));
+
+	 foreach (@insns) { eval; }	# remaining instructions
+
+  $Xi++;	push(@X,shift(@X));	# "rotate" X[]
+		push(@Tx,shift(@Tx));
+}
+
+sub Xuplast_ssse3_80()
+{ use integer;
+  my $body = shift;
+  my @insns = (&$body,&$body,&$body,&$body);	# 32 instructions
+  my ($a,$b,$c,$d,$e);
+
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	  &paddd	(@Tx[1],@X[-1&7]);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	  &movdqa	(eval(16*(($Xi-1)&3))."(%rsp)",@Tx[1]);	# X[]+K xfer IALU
+
+	 foreach (@insns) { eval; }		# remaining instructions
+
+	&cmp	($inp,$num);
+	&je	(".Ldone_ssse3");
+
+	unshift(@Tx,pop(@Tx));
+
+	&movdqa	(@X[2],"64($K_XX_XX)");		# pbswap mask
+	&movdqa	(@Tx[1],"-64($K_XX_XX)");	# K_00_19
+	&movdqu	(@X[-4&7],"0($inp)");		# load input
+	&movdqu	(@X[-3&7],"16($inp)");
+	&movdqu	(@X[-2&7],"32($inp)");
+	&movdqu	(@X[-1&7],"48($inp)");
+	&pshufb	(@X[-4&7],@X[2]);		# byte swap
+	&add	($inp,64);
+
+  $Xi=0;
+}
+
+sub Xloop_ssse3()
+{ use integer;
+  my $body = shift;
+  my @insns = (&$body,&$body,&$body,&$body);	# 32 instructions
+  my ($a,$b,$c,$d,$e);
+
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&pshufb	(@X[($Xi-3)&7],@X[2]);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&paddd	(@X[($Xi-4)&7],@Tx[1]);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&movdqa	(eval(16*$Xi)."(%rsp)",@X[($Xi-4)&7]);	# X[]+K xfer to IALU
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&psubd	(@X[($Xi-4)&7],@Tx[1]);
+
+	foreach (@insns) { eval; }
+  $Xi++;
+}
+
+sub Xtail_ssse3()
+{ use integer;
+  my $body = shift;
+  my @insns = (&$body,&$body,&$body,&$body);	# 32 instructions
+  my ($a,$b,$c,$d,$e);
+
+	foreach (@insns) { eval; }
+}
+
+sub body_00_19 () {	# ((c^d)&b)^d
+	# on start @T[0]=(c^d)&b
+	return &body_20_39() if ($rx==19); $rx++;
+	(
+	'($a,$b,$c,$d,$e)=@V;'.
+	'&$_ror	($b,$j?7:2)',	# $b>>>2
+	'&xor	(@T[0],$d)',
+	'&mov	(@T[1],$a)',	# $b for next round
+
+	'&add	($e,eval(4*($j&15))."(%rsp)")',	# X[]+K xfer
+	'&xor	($b,$c)',	# $c^$d for next round
+
+	'&$_rol	($a,5)',
+	'&add	($e,@T[0])',
+	'&and	(@T[1],$b)',	# ($b&($c^$d)) for next round
+
+	'&xor	($b,$c)',	# restore $b
+	'&add	($e,$a);'	.'$j++; unshift(@V,pop(@V)); unshift(@T,pop(@T));'
+	);
+}
+
+sub body_20_39 () {	# b^d^c
+	# on entry @T[0]=b^d
+	return &body_40_59() if ($rx==39); $rx++;
+	(
+	'($a,$b,$c,$d,$e)=@V;'.
+	'&add	($e,eval(4*($j&15))."(%rsp)")',	# X[]+K xfer
+	'&xor	(@T[0],$d)	if($j==19);'.
+	'&xor	(@T[0],$c)	if($j> 19)',	# ($b^$d^$c)
+	'&mov	(@T[1],$a)',	# $b for next round
+
+	'&$_rol	($a,5)',
+	'&add	($e,@T[0])',
+	'&xor	(@T[1],$c)	if ($j< 79)',	# $b^$d for next round
+
+	'&$_ror	($b,7)',	# $b>>>2
+	'&add	($e,$a);'	.'$j++; unshift(@V,pop(@V)); unshift(@T,pop(@T));'
+	);
+}
+
+sub body_40_59 () {	# ((b^c)&(c^d))^c
+	# on entry @T[0]=(b^c), (c^=d)
+	$rx++;
+	(
+	'($a,$b,$c,$d,$e)=@V;'.
+	'&add	($e,eval(4*($j&15))."(%rsp)")',	# X[]+K xfer
+	'&and	(@T[0],$c)	if ($j>=40)',	# (b^c)&(c^d)
+	'&xor	($c,$d)		if ($j>=40)',	# restore $c
+
+	'&$_ror	($b,7)',	# $b>>>2
+	'&mov	(@T[1],$a)',	# $b for next round
+	'&xor	(@T[0],$c)',
+
+	'&$_rol	($a,5)',
+	'&add	($e,@T[0])',
+	'&xor	(@T[1],$c)	if ($j==59);'.
+	'&xor	(@T[1],$b)	if ($j< 59)',	# b^c for next round
+
+	'&xor	($b,$c)		if ($j< 59)',	# c^d for next round
+	'&add	($e,$a);'	.'$j++; unshift(@V,pop(@V)); unshift(@T,pop(@T));'
+	);
+}
+$code.=<<___;
+.align	16
+.Loop_ssse3:
+___
+	&Xupdate_ssse3_16_31(\&body_00_19);
+	&Xupdate_ssse3_16_31(\&body_00_19);
+	&Xupdate_ssse3_16_31(\&body_00_19);
+	&Xupdate_ssse3_16_31(\&body_00_19);
+	&Xupdate_ssse3_32_79(\&body_00_19);
+	&Xupdate_ssse3_32_79(\&body_20_39);
+	&Xupdate_ssse3_32_79(\&body_20_39);
+	&Xupdate_ssse3_32_79(\&body_20_39);
+	&Xupdate_ssse3_32_79(\&body_20_39);
+	&Xupdate_ssse3_32_79(\&body_20_39);
+	&Xupdate_ssse3_32_79(\&body_40_59);
+	&Xupdate_ssse3_32_79(\&body_40_59);
+	&Xupdate_ssse3_32_79(\&body_40_59);
+	&Xupdate_ssse3_32_79(\&body_40_59);
+	&Xupdate_ssse3_32_79(\&body_40_59);
+	&Xupdate_ssse3_32_79(\&body_20_39);
+	&Xuplast_ssse3_80(\&body_20_39);	# can jump to "done"
+
+				$saved_j=$j; @saved_V=@V;
+
+	&Xloop_ssse3(\&body_20_39);
+	&Xloop_ssse3(\&body_20_39);
+	&Xloop_ssse3(\&body_20_39);
+
+$code.=<<___;
+	add	0($ctx),$A			# update context
+	add	4($ctx),@T[0]
+	add	8($ctx),$C
+	add	12($ctx),$D
+	mov	$A,0($ctx)
+	add	16($ctx),$E
+	mov	@T[0],4($ctx)
+	mov	@T[0],$B			# magic seed
+	mov	$C,8($ctx)
+	mov	$C,@T[1]
+	mov	$D,12($ctx)
+	xor	$D,@T[1]
+	mov	$E,16($ctx)
+	and	@T[1],@T[0]
+	jmp	.Loop_ssse3
+
+.align	16
+.Ldone_ssse3:
+___
+				$j=$saved_j; @V=@saved_V;
+
+	&Xtail_ssse3(\&body_20_39);
+	&Xtail_ssse3(\&body_20_39);
+	&Xtail_ssse3(\&body_20_39);
+
+$code.=<<___;
+	add	0($ctx),$A			# update context
+	add	4($ctx),@T[0]
+	add	8($ctx),$C
+	mov	$A,0($ctx)
+	add	12($ctx),$D
+	mov	@T[0],4($ctx)
+	add	16($ctx),$E
+	mov	$C,8($ctx)
+	mov	$D,12($ctx)
+	mov	$E,16($ctx)
+___
+$code.=<<___ if ($win64);
+	movaps	-40-6*16(%r14),%xmm6
+	movaps	-40-5*16(%r14),%xmm7
+	movaps	-40-4*16(%r14),%xmm8
+	movaps	-40-3*16(%r14),%xmm9
+	movaps	-40-2*16(%r14),%xmm10
+	movaps	-40-1*16(%r14),%xmm11
+___
+$code.=<<___;
+	lea	(%r14),%rsi
+	mov	-40(%rsi),%r14
+	mov	-32(%rsi),%r13
+	mov	-24(%rsi),%r12
+	mov	-16(%rsi),%rbp
+	mov	-8(%rsi),%rbx
+	lea	(%rsi),%rsp
+.Lepilogue_ssse3:
+	ret
+.size	sha1_block_data_order_ssse3,.-sha1_block_data_order_ssse3
+___
+
+if ($avx) {
+$Xi=4;				# reset variables
+@X=map("%xmm$_",(4..7,0..3));
+@Tx=map("%xmm$_",(8..10));
+$j=0;
+$rx=0;
+
+my $done_avx_label=".Ldone_avx";
+
+my $_rol=sub { &shld(@_[0],@_) };
+my $_ror=sub { &shrd(@_[0],@_) };
+
+$code.=<<___;
+.type	sha1_block_data_order_avx,\@function,3
+.align	16
+sha1_block_data_order_avx:
+_avx_shortcut:
+	mov	%rsp,%rax
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13		# redundant, done to share Win64 SE handler
+	push	%r14
+	lea	`-64-($win64?6*16:0)`(%rsp),%rsp
+	vzeroupper
+___
+$code.=<<___ if ($win64);
+	vmovaps	%xmm6,-40-6*16(%rax)
+	vmovaps	%xmm7,-40-5*16(%rax)
+	vmovaps	%xmm8,-40-4*16(%rax)
+	vmovaps	%xmm9,-40-3*16(%rax)
+	vmovaps	%xmm10,-40-2*16(%rax)
+	vmovaps	%xmm11,-40-1*16(%rax)
+.Lprologue_avx:
+___
+$code.=<<___;
+	mov	%rax,%r14	# original %rsp
+	and	\$-64,%rsp
+	mov	%rdi,$ctx	# reassigned argument
+	mov	%rsi,$inp	# reassigned argument
+	mov	%rdx,$num	# reassigned argument
+
+	shl	\$6,$num
+	add	$inp,$num
+	lea	K_XX_XX+64(%rip),$K_XX_XX
+
+	mov	0($ctx),$A		# load context
+	mov	4($ctx),$B
+	mov	8($ctx),$C
+	mov	12($ctx),$D
+	mov	$B,@T[0]		# magic seed
+	mov	16($ctx),$E
+	mov	$C,@T[1]
+	xor	$D,@T[1]
+	and	@T[1],@T[0]
+
+	vmovdqa	64($K_XX_XX),@X[2]	# pbswap mask
+	vmovdqa	-64($K_XX_XX),$Kx	# K_00_19
+	vmovdqu	0($inp),@X[-4&7]	# load input to %xmm[0-3]
+	vmovdqu	16($inp),@X[-3&7]
+	vmovdqu	32($inp),@X[-2&7]
+	vmovdqu	48($inp),@X[-1&7]
+	vpshufb	@X[2],@X[-4&7],@X[-4&7]	# byte swap
+	add	\$64,$inp
+	vpshufb	@X[2],@X[-3&7],@X[-3&7]
+	vpshufb	@X[2],@X[-2&7],@X[-2&7]
+	vpshufb	@X[2],@X[-1&7],@X[-1&7]
+	vpaddd	$Kx,@X[-4&7],@X[0]	# add K_00_19
+	vpaddd	$Kx,@X[-3&7],@X[1]
+	vpaddd	$Kx,@X[-2&7],@X[2]
+	vmovdqa	@X[0],0(%rsp)		# X[]+K xfer to IALU
+	vmovdqa	@X[1],16(%rsp)
+	vmovdqa	@X[2],32(%rsp)
+	jmp	.Loop_avx
+___
+
+sub Xupdate_avx_16_31()		# recall that $Xi starts wtih 4
+{ use integer;
+  my $body = shift;
+  my @insns = (&$body,&$body,&$body,&$body);	# 40 instructions
+  my ($a,$b,$c,$d,$e);
+
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&vpalignr(@X[0],@X[-3&7],@X[-4&7],8);	# compose "X[-14]" in "X[0]"
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	  &vpaddd	(@Tx[1],$Kx,@X[-1&7]);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&vpsrldq(@Tx[0],@X[-1&7],4);		# "X[-3]", 3 dwords
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&vpxor	(@X[0],@X[0],@X[-4&7]);		# "X[0]"^="X[-16]"
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	&vpxor	(@Tx[0],@Tx[0],@X[-2&7]);	# "X[-3]"^"X[-8]"
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	&vpxor	(@X[0],@X[0],@Tx[0]);		# "X[0]"^="X[-3]"^"X[-8]"
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	  &vmovdqa	(eval(16*(($Xi-1)&3))."(%rsp)",@Tx[1]);	# X[]+K xfer to IALU
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	&vpsrld	(@Tx[0],@X[0],31);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	&vpslldq(@Tx[2],@X[0],12);		# "X[0]"<<96, extract one dword
+	&vpaddd	(@X[0],@X[0],@X[0]);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	&vpsrld	(@Tx[1],@Tx[2],30);
+	&vpor	(@X[0],@X[0],@Tx[0]);		# "X[0]"<<<=1
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	&vpslld	(@Tx[2],@Tx[2],2);
+	&vpxor	(@X[0],@X[0],@Tx[1]);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	&vpxor	(@X[0],@X[0],@Tx[2]);		# "X[0]"^=("X[0]">>96)<<<2
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	  &vmovdqa	($Kx,eval(2*16*(($Xi)/5)-64)."($K_XX_XX)")	if ($Xi%5==0);	# K_XX_XX
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+
+	 foreach (@insns) { eval; }	# remaining instructions [if any]
+
+  $Xi++;	push(@X,shift(@X));	# "rotate" X[]
+}
+
+sub Xupdate_avx_32_79()
+{ use integer;
+  my $body = shift;
+  my @insns = (&$body,&$body,&$body,&$body);	# 32 to 44 instructions
+  my ($a,$b,$c,$d,$e);
+
+	&vpalignr(@Tx[0],@X[-1&7],@X[-2&7],8);	# compose "X[-6]"
+	&vpxor	(@X[0],@X[0],@X[-4&7]);		# "X[0]"="X[-32]"^"X[-16]"
+	 eval(shift(@insns));		# body_20_39
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));		# rol
+
+	&vpxor	(@X[0],@X[0],@X[-7&7]);		# "X[0]"^="X[-28]"
+	 eval(shift(@insns));
+	 eval(shift(@insns))	if (@insns[0] !~ /&ro[rl]/);
+	  &vpaddd	(@Tx[1],$Kx,@X[-1&7]);
+	  &vmovdqa	($Kx,eval(2*16*($Xi/5)-64)."($K_XX_XX)")	if ($Xi%5==0);
+	 eval(shift(@insns));		# ror
+	 eval(shift(@insns));
+
+	&vpxor	(@X[0],@X[0],@Tx[0]);		# "X[0]"^="X[-6]"
+	 eval(shift(@insns));		# body_20_39
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));		# rol
+
+	&vpsrld	(@Tx[0],@X[0],30);
+	  &vmovdqa	(eval(16*(($Xi-1)&3))."(%rsp)",@Tx[1]);	# X[]+K xfer to IALU
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));		# ror
+	 eval(shift(@insns));
+
+	&vpslld	(@X[0],@X[0],2);
+	 eval(shift(@insns));		# body_20_39
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));		# rol
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));		# ror
+	 eval(shift(@insns));
+
+	&vpor	(@X[0],@X[0],@Tx[0]);		# "X[0]"<<<=2
+	 eval(shift(@insns));		# body_20_39
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));		# rol
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));		# rol
+	 eval(shift(@insns));
+
+	 foreach (@insns) { eval; }	# remaining instructions
+
+  $Xi++;	push(@X,shift(@X));	# "rotate" X[]
+}
+
+sub Xuplast_avx_80()
+{ use integer;
+  my $body = shift;
+  my @insns = (&$body,&$body,&$body,&$body);	# 32 instructions
+  my ($a,$b,$c,$d,$e);
+
+	 eval(shift(@insns));
+	  &vpaddd	(@Tx[1],$Kx,@X[-1&7]);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	  &vmovdqa	(eval(16*(($Xi-1)&3))."(%rsp)",@Tx[1]);	# X[]+K xfer IALU
+
+	 foreach (@insns) { eval; }		# remaining instructions
+
+	&cmp	($inp,$num);
+	&je	($done_avx_label);
+
+	&vmovdqa(@X[2],"64($K_XX_XX)");		# pbswap mask
+	&vmovdqa($Kx,"-64($K_XX_XX)");		# K_00_19
+	&vmovdqu(@X[-4&7],"0($inp)");		# load input
+	&vmovdqu(@X[-3&7],"16($inp)");
+	&vmovdqu(@X[-2&7],"32($inp)");
+	&vmovdqu(@X[-1&7],"48($inp)");
+	&vpshufb(@X[-4&7],@X[-4&7],@X[2]);	# byte swap
+	&add	($inp,64);
+
+  $Xi=0;
+}
+
+sub Xloop_avx()
+{ use integer;
+  my $body = shift;
+  my @insns = (&$body,&$body,&$body,&$body);	# 32 instructions
+  my ($a,$b,$c,$d,$e);
+
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&vpshufb(@X[($Xi-3)&7],@X[($Xi-3)&7],@X[2]);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&vpaddd	(@X[$Xi&7],@X[($Xi-4)&7],$Kx);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&vmovdqa(eval(16*$Xi)."(%rsp)",@X[$Xi&7]);	# X[]+K xfer to IALU
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	foreach (@insns) { eval; }
+  $Xi++;
+}
+
+sub Xtail_avx()
+{ use integer;
+  my $body = shift;
+  my @insns = (&$body,&$body,&$body,&$body);	# 32 instructions
+  my ($a,$b,$c,$d,$e);
+
+	foreach (@insns) { eval; }
+}
+
+$code.=<<___;
+.align	16
+.Loop_avx:
+___
+	&Xupdate_avx_16_31(\&body_00_19);
+	&Xupdate_avx_16_31(\&body_00_19);
+	&Xupdate_avx_16_31(\&body_00_19);
+	&Xupdate_avx_16_31(\&body_00_19);
+	&Xupdate_avx_32_79(\&body_00_19);
+	&Xupdate_avx_32_79(\&body_20_39);
+	&Xupdate_avx_32_79(\&body_20_39);
+	&Xupdate_avx_32_79(\&body_20_39);
+	&Xupdate_avx_32_79(\&body_20_39);
+	&Xupdate_avx_32_79(\&body_20_39);
+	&Xupdate_avx_32_79(\&body_40_59);
+	&Xupdate_avx_32_79(\&body_40_59);
+	&Xupdate_avx_32_79(\&body_40_59);
+	&Xupdate_avx_32_79(\&body_40_59);
+	&Xupdate_avx_32_79(\&body_40_59);
+	&Xupdate_avx_32_79(\&body_20_39);
+	&Xuplast_avx_80(\&body_20_39);	# can jump to "done"
+
+				$saved_j=$j; @saved_V=@V;
+
+	&Xloop_avx(\&body_20_39);
+	&Xloop_avx(\&body_20_39);
+	&Xloop_avx(\&body_20_39);
+
+$code.=<<___;
+	add	0($ctx),$A			# update context
+	add	4($ctx),@T[0]
+	add	8($ctx),$C
+	add	12($ctx),$D
+	mov	$A,0($ctx)
+	add	16($ctx),$E
+	mov	@T[0],4($ctx)
+	mov	@T[0],$B			# magic seed
+	mov	$C,8($ctx)
+	mov	$C,@T[1]
+	mov	$D,12($ctx)
+	xor	$D,@T[1]
+	mov	$E,16($ctx)
+	and	@T[1],@T[0]
+	jmp	.Loop_avx
+
+.align	16
+$done_avx_label:
+___
+				$j=$saved_j; @V=@saved_V;
+
+	&Xtail_avx(\&body_20_39);
+	&Xtail_avx(\&body_20_39);
+	&Xtail_avx(\&body_20_39);
+
+$code.=<<___;
+	vzeroupper
+
+	add	0($ctx),$A			# update context
+	add	4($ctx),@T[0]
+	add	8($ctx),$C
+	mov	$A,0($ctx)
+	add	12($ctx),$D
+	mov	@T[0],4($ctx)
+	add	16($ctx),$E
+	mov	$C,8($ctx)
+	mov	$D,12($ctx)
+	mov	$E,16($ctx)
+___
+$code.=<<___ if ($win64);
+	movaps	-40-6*16(%r14),%xmm6
+	movaps	-40-5*16(%r14),%xmm7
+	movaps	-40-4*16(%r14),%xmm8
+	movaps	-40-3*16(%r14),%xmm9
+	movaps	-40-2*16(%r14),%xmm10
+	movaps	-40-1*16(%r14),%xmm11
+___
+$code.=<<___;
+	lea	(%r14),%rsi
+	mov	-40(%rsi),%r14
+	mov	-32(%rsi),%r13
+	mov	-24(%rsi),%r12
+	mov	-16(%rsi),%rbp
+	mov	-8(%rsi),%rbx
+	lea	(%rsi),%rsp
+.Lepilogue_avx:
+	ret
+.size	sha1_block_data_order_avx,.-sha1_block_data_order_avx
+___
+
+if ($avx>1) {
+use integer;
+$Xi=4;					# reset variables
+@X=map("%ymm$_",(4..7,0..3));
+@Tx=map("%ymm$_",(8..10));
+$Kx="%ymm11";
+$j=0;
+
+my @ROTX=("%eax","%ebp","%ebx","%ecx","%edx","%esi");
+my ($a5,$t0)=("%r12d","%edi");
+
+my ($A,$F,$B,$C,$D,$E)=@ROTX;
+my $rx=0;
+my $frame="%r13";
+
+$code.=<<___;
+.type	sha1_block_data_order_avx2,\@function,3
+.align	16
+sha1_block_data_order_avx2:
+_avx2_shortcut:
+	mov	%rsp,%rax
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	vzeroupper
+___
+$code.=<<___ if ($win64);
+	lea	-6*16(%rsp),%rsp
+	vmovaps	%xmm6,-40-6*16(%rax)
+	vmovaps	%xmm7,-40-5*16(%rax)
+	vmovaps	%xmm8,-40-4*16(%rax)
+	vmovaps	%xmm9,-40-3*16(%rax)
+	vmovaps	%xmm10,-40-2*16(%rax)
+	vmovaps	%xmm11,-40-1*16(%rax)
+.Lprologue_avx2:
+___
+$code.=<<___;
+	mov	%rax,%r14		# original %rsp
+	mov	%rdi,$ctx		# reassigned argument
+	mov	%rsi,$inp		# reassigned argument
+	mov	%rdx,$num		# reassigned argument
+
+	lea	-640(%rsp),%rsp
+	shl	\$6,$num
+	 lea	64($inp),$frame
+	and	\$-128,%rsp
+	add	$inp,$num
+	lea	K_XX_XX+64(%rip),$K_XX_XX
+
+	mov	0($ctx),$A		# load context
+	 cmp	$num,$frame
+	 cmovae	$inp,$frame		# next or same block
+	mov	4($ctx),$F
+	mov	8($ctx),$C
+	mov	12($ctx),$D
+	mov	16($ctx),$E
+	vmovdqu	64($K_XX_XX),@X[2]	# pbswap mask
+
+	vmovdqu		($inp),%xmm0
+	vmovdqu		16($inp),%xmm1
+	vmovdqu		32($inp),%xmm2
+	vmovdqu		48($inp),%xmm3
+	lea		64($inp),$inp
+	vinserti128	\$1,($frame),@X[-4&7],@X[-4&7]
+	vinserti128	\$1,16($frame),@X[-3&7],@X[-3&7]
+	vpshufb		@X[2],@X[-4&7],@X[-4&7]
+	vinserti128	\$1,32($frame),@X[-2&7],@X[-2&7]
+	vpshufb		@X[2],@X[-3&7],@X[-3&7]
+	vinserti128	\$1,48($frame),@X[-1&7],@X[-1&7]
+	vpshufb		@X[2],@X[-2&7],@X[-2&7]
+	vmovdqu		-64($K_XX_XX),$Kx	# K_00_19
+	vpshufb		@X[2],@X[-1&7],@X[-1&7]
+
+	vpaddd	$Kx,@X[-4&7],@X[0]	# add K_00_19
+	vpaddd	$Kx,@X[-3&7],@X[1]
+	vmovdqu	@X[0],0(%rsp)		# X[]+K xfer to IALU
+	vpaddd	$Kx,@X[-2&7],@X[2]
+	vmovdqu	@X[1],32(%rsp)
+	vpaddd	$Kx,@X[-1&7],@X[3]
+	vmovdqu	@X[2],64(%rsp)
+	vmovdqu	@X[3],96(%rsp)
+___
+for (;$Xi<8;$Xi++) {	# Xupdate_avx2_16_31
+    use integer;
+
+	&vpalignr(@X[0],@X[-3&7],@X[-4&7],8);	# compose "X[-14]" in "X[0]"
+	&vpsrldq(@Tx[0],@X[-1&7],4);		# "X[-3]", 3 dwords
+	&vpxor	(@X[0],@X[0],@X[-4&7]);		# "X[0]"^="X[-16]"
+	&vpxor	(@Tx[0],@Tx[0],@X[-2&7]);	# "X[-3]"^"X[-8]"
+	&vpxor	(@X[0],@X[0],@Tx[0]);		# "X[0]"^="X[-3]"^"X[-8]"
+	&vpsrld	(@Tx[0],@X[0],31);
+	&vmovdqu($Kx,eval(2*16*(($Xi)/5)-64)."($K_XX_XX)")	if ($Xi%5==0);	# K_XX_XX
+	&vpslldq(@Tx[2],@X[0],12);		# "X[0]"<<96, extract one dword
+	&vpaddd	(@X[0],@X[0],@X[0]);
+	&vpsrld	(@Tx[1],@Tx[2],30);
+	&vpor	(@X[0],@X[0],@Tx[0]);		# "X[0]"<<<=1
+	&vpslld	(@Tx[2],@Tx[2],2);
+	&vpxor	(@X[0],@X[0],@Tx[1]);
+	&vpxor	(@X[0],@X[0],@Tx[2]);		# "X[0]"^=("X[0]">>96)<<<2
+	&vpaddd	(@Tx[1],@X[0],$Kx);
+	&vmovdqu("32*$Xi(%rsp)",@Tx[1]);	# X[]+K xfer to IALU
+
+	push(@X,shift(@X));	# "rotate" X[]
+}
+$code.=<<___;
+	lea	128(%rsp),$frame
+	jmp	.Loop_avx2
+.align	32
+.Loop_avx2:
+	rorx	\$2,$F,$B
+	andn	$D,$F,$t0
+	and	$C,$F
+	xor	$t0,$F
+___
+sub bodyx_00_19 () {	# 8 instructions, 3 cycles critical path
+	# at start $f=(b&c)^(~b&d), $b>>>=2
+	return &bodyx_20_39() if ($rx==19); $rx++;
+	(
+	'($a,$f,$b,$c,$d,$e)=@ROTX;'.
+
+	'&add	($e,((32*($j/4)+4*($j%4))%256-128)."($frame)");'.	# e+=X[i]+K
+	 '&lea	($frame,"256($frame)")	if ($j%32==31);',
+	'&andn	($t0,$a,$c)',			# ~b&d for next round
+
+	'&add	($e,$f)',			# e+=(b&c)^(~b&d)
+	'&rorx	($a5,$a,27)',			# a<<<5
+	'&rorx	($f,$a,2)',			# b>>>2 for next round
+	'&and	($a,$b)',			# b&c for next round
+
+	'&add	($e,$a5)',			# e+=a<<<5
+	'&xor	($a,$t0);'.			# f=(b&c)^(~b&d) for next round
+
+	'unshift(@ROTX,pop(@ROTX)); $j++;'
+	)
+}
+
+sub bodyx_20_39 () {	# 7 instructions, 2 cycles critical path
+	# on entry $f=b^c^d, $b>>>=2
+	return &bodyx_40_59() if ($rx==39); $rx++;
+	(
+	'($a,$f,$b,$c,$d,$e)=@ROTX;'.
+
+	'&add	($e,((32*($j/4)+4*($j%4))%256-128)."($frame)");'.	# e+=X[i]+K
+	 '&lea	($frame,"256($frame)")	if ($j%32==31);',
+
+	'&lea	($e,"($e,$f)")',		# e+=b^c^d
+	'&rorx	($a5,$a,27)',			# a<<<5
+	'&rorx	($f,$a,2)	if ($j<79)',	# b>>>2 in next round
+	'&xor	($a,$b)		if ($j<79)',	# b^c for next round
+
+	'&add	($e,$a5)',			# e+=a<<<5
+	'&xor	($a,$c)		if ($j<79);'.	# f=b^c^d for next round
+
+	'unshift(@ROTX,pop(@ROTX)); $j++;'
+	)
+}
+
+sub bodyx_40_59 () {	# 10 instructions, 3 cycles critical path
+	# on entry $f=((b^c)&(c^d)), $b>>>=2
+	$rx++;
+	(
+	'($a,$f,$b,$c,$d,$e)=@ROTX;'.
+
+	'&add	($e,((32*($j/4)+4*($j%4))%256-128)."($frame)");'.	# e+=X[i]+K
+	 '&lea	($frame,"256($frame)")	if ($j%32==31);',
+	'&xor	($f,$c)		if ($j>39)',	# (b^c)&(c^d)^c
+	'&mov	($t0,$b)	if ($j<59)',	# count on zero latency
+	'&xor	($t0,$c)	if ($j<59)',	# c^d for next round
+
+	'&lea	($e,"($e,$f)")',		# e+=(b^c)&(c^d)^c
+	'&rorx	($a5,$a,27)',			# a<<<5
+	'&rorx	($f,$a,2)',			# b>>>2 in next round
+	'&xor	($a,$b)',			# b^c for next round
+
+	'&add	($e,$a5)',			# e+=a<<<5
+	'&and	($a,$t0)	if ($j< 59);'.	# f=(b^c)&(c^d) for next round
+	'&xor	($a,$c)		if ($j==59);'.	# f=b^c^d for next round
+
+	'unshift(@ROTX,pop(@ROTX)); $j++;'
+	)
+}
+
+sub Xupdate_avx2_16_31()		# recall that $Xi starts wtih 4
+{ use integer;
+  my $body = shift;
+  my @insns = (&$body,&$body,&$body,&$body,&$body);	# 35 instructions
+  my ($a,$b,$c,$d,$e);
+
+	&vpalignr(@X[0],@X[-3&7],@X[-4&7],8);	# compose "X[-14]" in "X[0]"
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	&vpsrldq(@Tx[0],@X[-1&7],4);		# "X[-3]", 3 dwords
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	&vpxor	(@X[0],@X[0],@X[-4&7]);		# "X[0]"^="X[-16]"
+	&vpxor	(@Tx[0],@Tx[0],@X[-2&7]);	# "X[-3]"^"X[-8]"
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	&vpxor	(@X[0],@X[0],@Tx[0]);		# "X[0]"^="X[-3]"^"X[-8]"
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	&vpsrld	(@Tx[0],@X[0],31);
+	&vmovdqu($Kx,eval(2*16*(($Xi)/5)-64)."($K_XX_XX)")	if ($Xi%5==0);	# K_XX_XX
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	&vpslldq(@Tx[2],@X[0],12);		# "X[0]"<<96, extract one dword
+	&vpaddd	(@X[0],@X[0],@X[0]);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	&vpsrld	(@Tx[1],@Tx[2],30);
+	&vpor	(@X[0],@X[0],@Tx[0]);		# "X[0]"<<<=1
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	&vpslld	(@Tx[2],@Tx[2],2);
+	&vpxor	(@X[0],@X[0],@Tx[1]);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	&vpxor	(@X[0],@X[0],@Tx[2]);		# "X[0]"^=("X[0]">>96)<<<2
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	&vpaddd	(@Tx[1],@X[0],$Kx);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&vmovdqu(eval(32*($Xi))."(%rsp)",@Tx[1]);	# X[]+K xfer to IALU
+
+	 foreach (@insns) { eval; }	# remaining instructions [if any]
+
+	$Xi++;
+	push(@X,shift(@X));	# "rotate" X[]
+}
+
+sub Xupdate_avx2_32_79()
+{ use integer;
+  my $body = shift;
+  my @insns = (&$body,&$body,&$body,&$body,&$body);	# 35 to 50 instructions
+  my ($a,$b,$c,$d,$e);
+
+	&vpalignr(@Tx[0],@X[-1&7],@X[-2&7],8);	# compose "X[-6]"
+	&vpxor	(@X[0],@X[0],@X[-4&7]);		# "X[0]"="X[-32]"^"X[-16]"
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	&vpxor	(@X[0],@X[0],@X[-7&7]);		# "X[0]"^="X[-28]"
+	&vmovdqu($Kx,eval(2*16*($Xi/5)-64)."($K_XX_XX)")	if ($Xi%5==0);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	&vpxor	(@X[0],@X[0],@Tx[0]);		# "X[0]"^="X[-6]"
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	&vpsrld	(@Tx[0],@X[0],30);
+	&vpslld	(@X[0],@X[0],2);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	#&vpslld	(@X[0],@X[0],2);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	&vpor	(@X[0],@X[0],@Tx[0]);		# "X[0]"<<<=2
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	&vpaddd	(@Tx[1],@X[0],$Kx);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	&vmovdqu("32*$Xi(%rsp)",@Tx[1]);	# X[]+K xfer to IALU
+
+	 foreach (@insns) { eval; }	# remaining instructions
+
+	$Xi++;
+	push(@X,shift(@X));	# "rotate" X[]
+}
+
+sub Xloop_avx2()
+{ use integer;
+  my $body = shift;
+  my @insns = (&$body,&$body,&$body,&$body,&$body);	# 32 instructions
+  my ($a,$b,$c,$d,$e);
+
+	 foreach (@insns) { eval; }
+}
+
+	&align32();
+	&Xupdate_avx2_32_79(\&bodyx_00_19);
+	&Xupdate_avx2_32_79(\&bodyx_00_19);
+	&Xupdate_avx2_32_79(\&bodyx_00_19);
+	&Xupdate_avx2_32_79(\&bodyx_00_19);
+
+	&Xupdate_avx2_32_79(\&bodyx_20_39);
+	&Xupdate_avx2_32_79(\&bodyx_20_39);
+	&Xupdate_avx2_32_79(\&bodyx_20_39);
+	&Xupdate_avx2_32_79(\&bodyx_20_39);
+
+	&align32();
+	&Xupdate_avx2_32_79(\&bodyx_40_59);
+	&Xupdate_avx2_32_79(\&bodyx_40_59);
+	&Xupdate_avx2_32_79(\&bodyx_40_59);
+	&Xupdate_avx2_32_79(\&bodyx_40_59);
+
+	&Xloop_avx2(\&bodyx_20_39);
+	&Xloop_avx2(\&bodyx_20_39);
+	&Xloop_avx2(\&bodyx_20_39);
+	&Xloop_avx2(\&bodyx_20_39);
+
+$code.=<<___;
+	lea	128($inp),$frame
+	lea	128($inp),%rdi			# borrow $t0
+	cmp	$num,$frame
+	cmovae	$inp,$frame			# next or previous block
+
+	# output is d-e-[a]-f-b-c => A=d,F=e,C=f,D=b,E=c
+	add	0($ctx),@ROTX[0]		# update context
+	add	4($ctx),@ROTX[1]
+	add	8($ctx),@ROTX[3]
+	mov	@ROTX[0],0($ctx)
+	add	12($ctx),@ROTX[4]
+	mov	@ROTX[1],4($ctx)
+	 mov	@ROTX[0],$A			# A=d
+	add	16($ctx),@ROTX[5]
+	 mov	@ROTX[3],$a5
+	mov	@ROTX[3],8($ctx)
+	 mov	@ROTX[4],$D			# D=b
+	 #xchg	@ROTX[5],$F			# F=c, C=f
+	mov	@ROTX[4],12($ctx)
+	 mov	@ROTX[1],$F			# F=e
+	mov	@ROTX[5],16($ctx)
+	#mov	$F,16($ctx)
+	 mov	@ROTX[5],$E			# E=c
+	 mov	$a5,$C				# C=f
+	 #xchg	$F,$E				# E=c, F=e
+
+	cmp	$num,$inp
+	je	.Ldone_avx2
+___
+
+$Xi=4;				# reset variables
+@X=map("%ymm$_",(4..7,0..3));
+
+$code.=<<___;
+	vmovdqu	64($K_XX_XX),@X[2]		# pbswap mask
+	cmp	$num,%rdi			# borrowed $t0
+	ja	.Last_avx2
+
+	vmovdqu		-64(%rdi),%xmm0		# low part of @X[-4&7]
+	vmovdqu		-48(%rdi),%xmm1
+	vmovdqu		-32(%rdi),%xmm2
+	vmovdqu		-16(%rdi),%xmm3
+	vinserti128	\$1,0($frame),@X[-4&7],@X[-4&7]
+	vinserti128	\$1,16($frame),@X[-3&7],@X[-3&7]
+	vinserti128	\$1,32($frame),@X[-2&7],@X[-2&7]
+	vinserti128	\$1,48($frame),@X[-1&7],@X[-1&7]
+	jmp	.Last_avx2
+
+.align	32
+.Last_avx2:
+	lea	128+16(%rsp),$frame
+	rorx	\$2,$F,$B
+	andn	$D,$F,$t0
+	and	$C,$F
+	xor	$t0,$F
+	sub	\$-128,$inp
+___
+	$rx=$j=0;	@ROTX=($A,$F,$B,$C,$D,$E);
+
+	&Xloop_avx2	(\&bodyx_00_19);
+	&Xloop_avx2	(\&bodyx_00_19);
+	&Xloop_avx2	(\&bodyx_00_19);
+	&Xloop_avx2	(\&bodyx_00_19);
+
+	&Xloop_avx2	(\&bodyx_20_39);
+	  &vmovdqu	($Kx,"-64($K_XX_XX)");		# K_00_19
+	  &vpshufb	(@X[-4&7],@X[-4&7],@X[2]);	# byte swap
+	&Xloop_avx2	(\&bodyx_20_39);
+	  &vpshufb	(@X[-3&7],@X[-3&7],@X[2]);
+	  &vpaddd	(@Tx[0],@X[-4&7],$Kx);		# add K_00_19
+	&Xloop_avx2	(\&bodyx_20_39);
+	  &vmovdqu	("0(%rsp)",@Tx[0]);
+	  &vpshufb	(@X[-2&7],@X[-2&7],@X[2]);
+	  &vpaddd	(@Tx[1],@X[-3&7],$Kx);
+	&Xloop_avx2	(\&bodyx_20_39);
+	  &vmovdqu	("32(%rsp)",@Tx[1]);
+	  &vpshufb	(@X[-1&7],@X[-1&7],@X[2]);
+	  &vpaddd	(@X[2],@X[-2&7],$Kx);
+
+	&Xloop_avx2	(\&bodyx_40_59);
+	&align32	();
+	  &vmovdqu	("64(%rsp)",@X[2]);
+	  &vpaddd	(@X[3],@X[-1&7],$Kx);
+	&Xloop_avx2	(\&bodyx_40_59);
+	  &vmovdqu	("96(%rsp)",@X[3]);
+	&Xloop_avx2	(\&bodyx_40_59);
+	&Xupdate_avx2_16_31(\&bodyx_40_59);
+
+	&Xupdate_avx2_16_31(\&bodyx_20_39);
+	&Xupdate_avx2_16_31(\&bodyx_20_39);
+	&Xupdate_avx2_16_31(\&bodyx_20_39);
+	&Xloop_avx2	(\&bodyx_20_39);
+
+$code.=<<___;
+	lea	128(%rsp),$frame
+
+	# output is d-e-[a]-f-b-c => A=d,F=e,C=f,D=b,E=c
+	add	0($ctx),@ROTX[0]		# update context
+	add	4($ctx),@ROTX[1]
+	add	8($ctx),@ROTX[3]
+	mov	@ROTX[0],0($ctx)
+	add	12($ctx),@ROTX[4]
+	mov	@ROTX[1],4($ctx)
+	 mov	@ROTX[0],$A			# A=d
+	add	16($ctx),@ROTX[5]
+	 mov	@ROTX[3],$a5
+	mov	@ROTX[3],8($ctx)
+	 mov	@ROTX[4],$D			# D=b
+	 #xchg	@ROTX[5],$F			# F=c, C=f
+	mov	@ROTX[4],12($ctx)
+	 mov	@ROTX[1],$F			# F=e
+	mov	@ROTX[5],16($ctx)
+	#mov	$F,16($ctx)
+	 mov	@ROTX[5],$E			# E=c
+	 mov	$a5,$C				# C=f
+	 #xchg	$F,$E				# E=c, F=e
+
+	cmp	$num,$inp
+	jbe	.Loop_avx2
+
+.Ldone_avx2:
+	vzeroupper
+___
+$code.=<<___ if ($win64);
+	movaps	-40-6*16(%r14),%xmm6
+	movaps	-40-5*16(%r14),%xmm7
+	movaps	-40-4*16(%r14),%xmm8
+	movaps	-40-3*16(%r14),%xmm9
+	movaps	-40-2*16(%r14),%xmm10
+	movaps	-40-1*16(%r14),%xmm11
+___
+$code.=<<___;
+	lea	(%r14),%rsi
+	mov	-40(%rsi),%r14
+	mov	-32(%rsi),%r13
+	mov	-24(%rsi),%r12
+	mov	-16(%rsi),%rbp
+	mov	-8(%rsi),%rbx
+	lea	(%rsi),%rsp
+.Lepilogue_avx2:
+	ret
+.size	sha1_block_data_order_avx2,.-sha1_block_data_order_avx2
+___
+}
+}
+$code.=<<___;
+.align	64
+K_XX_XX:
+.long	0x5a827999,0x5a827999,0x5a827999,0x5a827999	# K_00_19
+.long	0x5a827999,0x5a827999,0x5a827999,0x5a827999	# K_00_19
+.long	0x6ed9eba1,0x6ed9eba1,0x6ed9eba1,0x6ed9eba1	# K_20_39
+.long	0x6ed9eba1,0x6ed9eba1,0x6ed9eba1,0x6ed9eba1	# K_20_39
+.long	0x8f1bbcdc,0x8f1bbcdc,0x8f1bbcdc,0x8f1bbcdc	# K_40_59
+.long	0x8f1bbcdc,0x8f1bbcdc,0x8f1bbcdc,0x8f1bbcdc	# K_40_59
+.long	0xca62c1d6,0xca62c1d6,0xca62c1d6,0xca62c1d6	# K_60_79
+.long	0xca62c1d6,0xca62c1d6,0xca62c1d6,0xca62c1d6	# K_60_79
+.long	0x00010203,0x04050607,0x08090a0b,0x0c0d0e0f	# pbswap mask
+.long	0x00010203,0x04050607,0x08090a0b,0x0c0d0e0f	# pbswap mask
+.byte	0xf,0xe,0xd,0xc,0xb,0xa,0x9,0x8,0x7,0x6,0x5,0x4,0x3,0x2,0x1,0x0
+___
+}}}
+$code.=<<___;
+.asciz	"SHA1 block transform for x86_64, CRYPTOGAMS by "
+.align	64
+___
+
+# EXCEPTION_DISPOSITION handler (EXCEPTION_RECORD *rec,ULONG64 frame,
+#		CONTEXT *context,DISPATCHER_CONTEXT *disp)
+if ($win64) {
+$rec="%rcx";
+$frame="%rdx";
+$context="%r8";
+$disp="%r9";
+
+$code.=<<___;
+.extern	__imp_RtlVirtualUnwind
+.type	se_handler,\@abi-omnipotent
+.align	16
+se_handler:
+	push	%rsi
+	push	%rdi
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+	pushfq
+	sub	\$64,%rsp
+
+	mov	120($context),%rax	# pull context->Rax
+	mov	248($context),%rbx	# pull context->Rip
+
+	lea	.Lprologue(%rip),%r10
+	cmp	%r10,%rbx		# context->Rip<.Lprologue
+	jb	.Lcommon_seh_tail
+
+	mov	152($context),%rax	# pull context->Rsp
+
+	lea	.Lepilogue(%rip),%r10
+	cmp	%r10,%rbx		# context->Rip>=.Lepilogue
+	jae	.Lcommon_seh_tail
+
+	mov	`16*4`(%rax),%rax	# pull saved stack pointer
+
+	mov	-8(%rax),%rbx
+	mov	-16(%rax),%rbp
+	mov	-24(%rax),%r12
+	mov	-32(%rax),%r13
+	mov	-40(%rax),%r14
+	mov	%rbx,144($context)	# restore context->Rbx
+	mov	%rbp,160($context)	# restore context->Rbp
+	mov	%r12,216($context)	# restore context->R12
+	mov	%r13,224($context)	# restore context->R13
+	mov	%r14,232($context)	# restore context->R14
+
+	jmp	.Lcommon_seh_tail
+.size	se_handler,.-se_handler
+___
+
+$code.=<<___ if ($shaext);
+.type	shaext_handler,\@abi-omnipotent
+.align	16
+shaext_handler:
+	push	%rsi
+	push	%rdi
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+	pushfq
+	sub	\$64,%rsp
+
+	mov	120($context),%rax	# pull context->Rax
+	mov	248($context),%rbx	# pull context->Rip
+
+	lea	.Lprologue_shaext(%rip),%r10
+	cmp	%r10,%rbx		# context->Rip<.Lprologue
+	jb	.Lcommon_seh_tail
+
+	lea	.Lepilogue_shaext(%rip),%r10
+	cmp	%r10,%rbx		# context->Rip>=.Lepilogue
+	jae	.Lcommon_seh_tail
+
+	lea	-8-4*16(%rax),%rsi
+	lea	512($context),%rdi	# &context.Xmm6
+	mov	\$8,%ecx
+	.long	0xa548f3fc		# cld; rep movsq
+
+	jmp	.Lcommon_seh_tail
+.size	shaext_handler,.-shaext_handler
+___
+
+$code.=<<___;
+.type	ssse3_handler,\@abi-omnipotent
+.align	16
+ssse3_handler:
+	push	%rsi
+	push	%rdi
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+	pushfq
+	sub	\$64,%rsp
+
+	mov	120($context),%rax	# pull context->Rax
+	mov	248($context),%rbx	# pull context->Rip
+
+	mov	8($disp),%rsi		# disp->ImageBase
+	mov	56($disp),%r11		# disp->HandlerData
+
+	mov	0(%r11),%r10d		# HandlerData[0]
+	lea	(%rsi,%r10),%r10	# prologue label
+	cmp	%r10,%rbx		# context->RipRsp
+
+	mov	4(%r11),%r10d		# HandlerData[1]
+	lea	(%rsi,%r10),%r10	# epilogue label
+	cmp	%r10,%rbx		# context->Rip>=epilogue label
+	jae	.Lcommon_seh_tail
+
+	mov	232($context),%rax	# pull context->R14
+
+	lea	-40-6*16(%rax),%rsi
+	lea	512($context),%rdi	# &context.Xmm6
+	mov	\$12,%ecx
+	.long	0xa548f3fc		# cld; rep movsq
+
+	mov	-8(%rax),%rbx
+	mov	-16(%rax),%rbp
+	mov	-24(%rax),%r12
+	mov	-32(%rax),%r13
+	mov	-40(%rax),%r14
+	mov	%rbx,144($context)	# restore context->Rbx
+	mov	%rbp,160($context)	# restore context->Rbp
+	mov	%r12,216($context)	# restore cotnext->R12
+	mov	%r13,224($context)	# restore cotnext->R13
+	mov	%r14,232($context)	# restore cotnext->R14
+
+.Lcommon_seh_tail:
+	mov	8(%rax),%rdi
+	mov	16(%rax),%rsi
+	mov	%rax,152($context)	# restore context->Rsp
+	mov	%rsi,168($context)	# restore context->Rsi
+	mov	%rdi,176($context)	# restore context->Rdi
+
+	mov	40($disp),%rdi		# disp->ContextRecord
+	mov	$context,%rsi		# context
+	mov	\$154,%ecx		# sizeof(CONTEXT)
+	.long	0xa548f3fc		# cld; rep movsq
+
+	mov	$disp,%rsi
+	xor	%rcx,%rcx		# arg1, UNW_FLAG_NHANDLER
+	mov	8(%rsi),%rdx		# arg2, disp->ImageBase
+	mov	0(%rsi),%r8		# arg3, disp->ControlPc
+	mov	16(%rsi),%r9		# arg4, disp->FunctionEntry
+	mov	40(%rsi),%r10		# disp->ContextRecord
+	lea	56(%rsi),%r11		# &disp->HandlerData
+	lea	24(%rsi),%r12		# &disp->EstablisherFrame
+	mov	%r10,32(%rsp)		# arg5
+	mov	%r11,40(%rsp)		# arg6
+	mov	%r12,48(%rsp)		# arg7
+	mov	%rcx,56(%rsp)		# arg8, (NULL)
+	call	*__imp_RtlVirtualUnwind(%rip)
+
+	mov	\$1,%eax		# ExceptionContinueSearch
+	add	\$64,%rsp
+	popfq
+	pop	%r15
+	pop	%r14
+	pop	%r13
+	pop	%r12
+	pop	%rbp
+	pop	%rbx
+	pop	%rdi
+	pop	%rsi
+	ret
+.size	ssse3_handler,.-ssse3_handler
+
+.section	.pdata
+.align	4
+	.rva	.LSEH_begin_sha1_block_data_order
+	.rva	.LSEH_end_sha1_block_data_order
+	.rva	.LSEH_info_sha1_block_data_order
+___
+$code.=<<___ if ($shaext);
+	.rva	.LSEH_begin_sha1_block_data_order_shaext
+	.rva	.LSEH_end_sha1_block_data_order_shaext
+	.rva	.LSEH_info_sha1_block_data_order_shaext
+___
+$code.=<<___;
+	.rva	.LSEH_begin_sha1_block_data_order_ssse3
+	.rva	.LSEH_end_sha1_block_data_order_ssse3
+	.rva	.LSEH_info_sha1_block_data_order_ssse3
+___
+$code.=<<___ if ($avx);
+	.rva	.LSEH_begin_sha1_block_data_order_avx
+	.rva	.LSEH_end_sha1_block_data_order_avx
+	.rva	.LSEH_info_sha1_block_data_order_avx
+___
+$code.=<<___ if ($avx>1);
+	.rva	.LSEH_begin_sha1_block_data_order_avx2
+	.rva	.LSEH_end_sha1_block_data_order_avx2
+	.rva	.LSEH_info_sha1_block_data_order_avx2
+___
+$code.=<<___;
+.section	.xdata
+.align	8
+.LSEH_info_sha1_block_data_order:
+	.byte	9,0,0,0
+	.rva	se_handler
+___
+$code.=<<___ if ($shaext);
+.LSEH_info_sha1_block_data_order_shaext:
+	.byte	9,0,0,0
+	.rva	shaext_handler
+___
+$code.=<<___;
+.LSEH_info_sha1_block_data_order_ssse3:
+	.byte	9,0,0,0
+	.rva	ssse3_handler
+	.rva	.Lprologue_ssse3,.Lepilogue_ssse3	# HandlerData[]
+___
+$code.=<<___ if ($avx);
+.LSEH_info_sha1_block_data_order_avx:
+	.byte	9,0,0,0
+	.rva	ssse3_handler
+	.rva	.Lprologue_avx,.Lepilogue_avx		# HandlerData[]
+___
+$code.=<<___ if ($avx>1);
+.LSEH_info_sha1_block_data_order_avx2:
+	.byte	9,0,0,0
+	.rva	ssse3_handler
+	.rva	.Lprologue_avx2,.Lepilogue_avx2		# HandlerData[]
+___
+}
+
+####################################################################
+
+sub sha1rnds4 {
+    if (@_[0] =~ /\$([x0-9a-f]+),\s*%xmm([0-7]),\s*%xmm([0-7])/) {
+      my @opcode=(0x0f,0x3a,0xcc);
+	push @opcode,0xc0|($2&7)|(($3&7)<<3);		# ModR/M
+	my $c=$1;
+	push @opcode,$c=~/^0/?oct($c):$c;
+	return ".byte\t".join(',',@opcode);
+    } else {
+	return "sha1rnds4\t".@_[0];
+    }
+}
+
+sub sha1op38 {
+    my $instr = shift;
+    my %opcodelet = (
+		"sha1nexte" => 0xc8,
+  		"sha1msg1"  => 0xc9,
+		"sha1msg2"  => 0xca	);
+
+    if (defined($opcodelet{$instr}) && @_[0] =~ /%xmm([0-9]+),\s*%xmm([0-9]+)/) {
+      my @opcode=(0x0f,0x38);
+      my $rex=0;
+	$rex|=0x04			if ($2>=8);
+	$rex|=0x01			if ($1>=8);
+	unshift @opcode,0x40|$rex	if ($rex);
+	push @opcode,$opcodelet{$instr};
+	push @opcode,0xc0|($1&7)|(($2&7)<<3);		# ModR/M
+	return ".byte\t".join(',',@opcode);
+    } else {
+	return $instr."\t".@_[0];
+    }
+}
+
+foreach (split("\n",$code)) {
+	s/\`([^\`]*)\`/eval $1/geo;
+
+	s/\b(sha1rnds4)\s+(.*)/sha1rnds4($2)/geo	or
+	s/\b(sha1[^\s]*)\s+(.*)/sha1op38($1,$2)/geo;
+
+	print $_,"\n";
+}
+close STDOUT;
diff --git a/crypto/sha/asm/sha256-586.pl b/crypto/sha/asm/sha256-586.pl
new file mode 100644
index 000000000..6af1d84be
--- /dev/null
+++ b/crypto/sha/asm/sha256-586.pl
@@ -0,0 +1,1293 @@
+#! /usr/bin/env perl
+# Copyright 2007-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+#
+# ====================================================================
+# Written by Andy Polyakov  for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+#
+# SHA256 block transform for x86. September 2007.
+#
+# Performance improvement over compiler generated code varies from
+# 10% to 40% [see below]. Not very impressive on some µ-archs, but
+# it's 5 times smaller and optimizies amount of writes.
+#
+# May 2012.
+#
+# Optimization including two of Pavel Semjanov's ideas, alternative
+# Maj and full unroll, resulted in ~20-25% improvement on most CPUs,
+# ~7% on Pentium, ~40% on Atom. As fully unrolled loop body is almost
+# 15x larger, 8KB vs. 560B, it's fired only for longer inputs. But not
+# on P4, where it kills performance, nor Sandy Bridge, where folded
+# loop is approximately as fast...
+#
+# June 2012.
+#
+# Add AMD XOP-specific code path, >30% improvement on Bulldozer over
+# May version, >60% over original. Add AVX+shrd code path, >25%
+# improvement on Sandy Bridge over May version, 60% over original.
+#
+# May 2013.
+#
+# Replace AMD XOP code path with SSSE3 to cover more processors.
+# (Biggest improvement coefficient is on upcoming Atom Silvermont,
+# not shown.) Add AVX+BMI code path.
+#
+# March 2014.
+#
+# Add support for Intel SHA Extensions.
+#
+# Performance in clock cycles per processed byte (less is better):
+#
+#		gcc	icc	x86 asm(*)	SIMD	x86_64 asm(**)	
+# Pentium	46	57	40/38		-	-
+# PIII		36	33	27/24		-	-
+# P4		41	38	28		-	17.3
+# AMD K8	27	25	19/15.5		-	14.9
+# Core2		26	23	18/15.6		14.3	13.8
+# Westmere	27	-	19/15.7		13.4	12.3
+# Sandy Bridge	25	-	15.9		12.4	11.6
+# Ivy Bridge	24	-	15.0		11.4	10.3
+# Haswell	22	-	13.9		9.46	7.80
+# Bulldozer	36	-	27/22		17.0	13.6
+# VIA Nano	36	-	25/22		16.8	16.5
+# Atom		50	-	30/25		21.9	18.9
+# Silvermont	40	-	34/31		22.9	20.6
+#
+# (*)	numbers after slash are for unrolled loop, where applicable;
+# (**)	x86_64 assembly performance is presented for reference
+#	purposes, results are best-available;
+
+$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+push(@INC,"${dir}","${dir}../../perlasm");
+require "x86asm.pl";
+
+$output=pop;
+open STDOUT,">$output";
+
+&asm_init($ARGV[0],"sha512-586.pl",$ARGV[$#ARGV] eq "386");
+
+$xmm=$avx=0;
+for (@ARGV) { $xmm=1 if (/-DOPENSSL_IA32_SSE2/); }
+
+if ($xmm &&	`$ENV{CC} -Wa,-v -c -o /dev/null -x assembler /dev/null 2>&1`
+			=~ /GNU assembler version ([2-9]\.[0-9]+)/) {
+	$avx = ($1>=2.19) + ($1>=2.22);
+}
+
+if ($xmm && !$avx && $ARGV[0] eq "win32n" &&
+		`nasm -v 2>&1` =~ /NASM version ([2-9]\.[0-9]+)/) {
+	$avx = ($1>=2.03) + ($1>=2.10);
+}
+
+if ($xmm && !$avx && $ARGV[0] eq "win32" &&
+		`ml 2>&1` =~ /Version ([0-9]+)\./) {
+	$avx = ($1>=10) + ($1>=11);
+}
+
+if ($xmm && !$avx && `$ENV{CC} -v 2>&1` =~ /(^clang version|based on LLVM) ([3-9]\.[0-9]+)/) {
+	$avx = ($2>=3.0) + ($2>3.0);
+}
+
+$shaext=$xmm;	### set to zero if compiling for 1.0.1
+
+$unroll_after = 64*4;	# If pre-evicted from L1P cache first spin of
+			# fully unrolled loop was measured to run about
+			# 3-4x slower. If slowdown coefficient is N and
+			# unrolled loop is m times faster, then you break
+			# even at (N-1)/(m-1) blocks. Then it needs to be
+			# adjusted for probability of code being evicted,
+			# code size/cache size=1/4. Typical m is 1.15...
+
+$A="eax";
+$E="edx";
+$T="ebx";
+$Aoff=&DWP(4,"esp");
+$Boff=&DWP(8,"esp");
+$Coff=&DWP(12,"esp");
+$Doff=&DWP(16,"esp");
+$Eoff=&DWP(20,"esp");
+$Foff=&DWP(24,"esp");
+$Goff=&DWP(28,"esp");
+$Hoff=&DWP(32,"esp");
+$Xoff=&DWP(36,"esp");
+$K256="ebp";
+
+sub BODY_16_63() {
+	&mov	($T,"ecx");			# "ecx" is preloaded
+	 &mov	("esi",&DWP(4*(9+15+16-14),"esp"));
+	&ror	("ecx",18-7);
+	 &mov	("edi","esi");
+	&ror	("esi",19-17);
+	 &xor	("ecx",$T);
+	 &shr	($T,3);
+	&ror	("ecx",7);
+	 &xor	("esi","edi");
+	 &xor	($T,"ecx");			# T = sigma0(X[-15])
+	&ror	("esi",17);
+	 &add	($T,&DWP(4*(9+15+16),"esp"));	# T += X[-16]
+	&shr	("edi",10);
+	 &add	($T,&DWP(4*(9+15+16-9),"esp"));	# T += X[-7]
+	#&xor	("edi","esi")			# sigma1(X[-2])
+	# &add	($T,"edi");			# T += sigma1(X[-2])
+	# &mov	(&DWP(4*(9+15),"esp"),$T);	# save X[0]
+
+	&BODY_00_15(1);
+}
+sub BODY_00_15() {
+    my $in_16_63=shift;
+
+	&mov	("ecx",$E);
+	 &xor	("edi","esi")			if ($in_16_63);	# sigma1(X[-2])
+	 &mov	("esi",$Foff);
+	&ror	("ecx",25-11);
+	 &add	($T,"edi")			if ($in_16_63);	# T += sigma1(X[-2])
+	 &mov	("edi",$Goff);
+	&xor	("ecx",$E);
+	 &xor	("esi","edi");
+	 &mov	($T,&DWP(4*(9+15),"esp"))	if (!$in_16_63);
+	 &mov	(&DWP(4*(9+15),"esp"),$T)	if ($in_16_63);	# save X[0]
+	&ror	("ecx",11-6);
+	 &and	("esi",$E);
+	 &mov	($Eoff,$E);		# modulo-scheduled
+	&xor	($E,"ecx");
+	 &add	($T,$Hoff);		# T += h
+	 &xor	("esi","edi");		# Ch(e,f,g)
+	&ror	($E,6);			# Sigma1(e)
+	 &mov	("ecx",$A);
+	 &add	($T,"esi");		# T += Ch(e,f,g)
+
+	&ror	("ecx",22-13);
+	 &add	($T,$E);		# T += Sigma1(e)
+	 &mov	("edi",$Boff);
+	&xor	("ecx",$A);
+	 &mov	($Aoff,$A);		# modulo-scheduled
+	 &lea	("esp",&DWP(-4,"esp"));
+	&ror	("ecx",13-2);
+	 &mov	("esi",&DWP(0,$K256));
+	&xor	("ecx",$A);
+	 &mov	($E,$Eoff);		# e in next iteration, d in this one
+	 &xor	($A,"edi");		# a ^= b
+	&ror	("ecx",2);		# Sigma0(a)
+
+	 &add	($T,"esi");		# T+= K[i]
+	 &mov	(&DWP(0,"esp"),$A);	# (b^c) in next round
+	&add	($E,$T);		# d += T
+	 &and	($A,&DWP(4,"esp"));	# a &= (b^c)
+	&add	($T,"ecx");		# T += Sigma0(a)
+	 &xor	($A,"edi");		# h = Maj(a,b,c) = Ch(a^b,c,b)
+	 &mov	("ecx",&DWP(4*(9+15+16-1),"esp"))	if ($in_16_63);	# preload T
+	&add	($K256,4);
+	 &add	($A,$T);		# h += T
+}
+
+&external_label("OPENSSL_ia32cap_P")		if (!$i386);
+
+&function_begin("sha256_block_data_order");
+	&mov	("esi",wparam(0));	# ctx
+	&mov	("edi",wparam(1));	# inp
+	&mov	("eax",wparam(2));	# num
+	&mov	("ebx","esp");		# saved sp
+
+	&call	(&label("pic_point"));	# make it PIC!
+&set_label("pic_point");
+	&blindpop($K256);
+	&lea	($K256,&DWP(&label("K256")."-".&label("pic_point"),$K256));
+
+	&sub	("esp",16);
+	&and	("esp",-64);
+
+	&shl	("eax",6);
+	&add	("eax","edi");
+	&mov	(&DWP(0,"esp"),"esi");	# ctx
+	&mov	(&DWP(4,"esp"),"edi");	# inp
+	&mov	(&DWP(8,"esp"),"eax");	# inp+num*128
+	&mov	(&DWP(12,"esp"),"ebx");	# saved sp
+						if (!$i386 && $xmm) {
+	&picmeup("edx","OPENSSL_ia32cap_P",$K256,&label("K256"));
+	&mov	("ecx",&DWP(0,"edx"));
+	&mov	("ebx",&DWP(4,"edx"));
+	&test	("ecx",1<<20);		# check for P4
+	&jnz	(&label("loop"));
+	&mov	("edx",&DWP(8,"edx"))	if ($xmm);
+	&test	("ecx",1<<24);		# check for FXSR
+	&jz	($unroll_after?&label("no_xmm"):&label("loop"));
+	&and	("ecx",1<<30);		# mask "Intel CPU" bit
+	&and	("ebx",1<<28|1<<9);	# mask AVX and SSSE3 bits
+	&test	("edx",1<<29)		if ($shaext);	# check for SHA
+	&jnz	(&label("shaext"))	if ($shaext);
+	&or	("ecx","ebx");
+	&and	("ecx",1<<28|1<<30);
+	&cmp	("ecx",1<<28|1<<30);
+					if ($xmm) {
+	&je	(&label("AVX"))		if ($avx);
+	&test	("ebx",1<<9);		# check for SSSE3
+	&jnz	(&label("SSSE3"));
+					} else {
+	&je	(&label("loop_shrd"));
+					}
+						if ($unroll_after) {
+&set_label("no_xmm");
+	&sub	("eax","edi");
+	&cmp	("eax",$unroll_after);
+	&jae	(&label("unrolled"));
+						} }
+	&jmp	(&label("loop"));
+
+sub COMPACT_LOOP() {
+my $suffix=shift;
+
+&set_label("loop$suffix",$suffix?32:16);
+    # copy input block to stack reversing byte and dword order
+    for($i=0;$i<4;$i++) {
+	&mov	("eax",&DWP($i*16+0,"edi"));
+	&mov	("ebx",&DWP($i*16+4,"edi"));
+	&mov	("ecx",&DWP($i*16+8,"edi"));
+	&bswap	("eax");
+	&mov	("edx",&DWP($i*16+12,"edi"));
+	&bswap	("ebx");
+	&push	("eax");
+	&bswap	("ecx");
+	&push	("ebx");
+	&bswap	("edx");
+	&push	("ecx");
+	&push	("edx");
+    }
+	&add	("edi",64);
+	&lea	("esp",&DWP(-4*9,"esp"));# place for A,B,C,D,E,F,G,H
+	&mov	(&DWP(4*(9+16)+4,"esp"),"edi");
+
+	# copy ctx->h[0-7] to A,B,C,D,E,F,G,H on stack
+	&mov	($A,&DWP(0,"esi"));
+	&mov	("ebx",&DWP(4,"esi"));
+	&mov	("ecx",&DWP(8,"esi"));
+	&mov	("edi",&DWP(12,"esi"));
+	# &mov	($Aoff,$A);
+	&mov	($Boff,"ebx");
+	&xor	("ebx","ecx");
+	&mov	($Coff,"ecx");
+	&mov	($Doff,"edi");
+	&mov	(&DWP(0,"esp"),"ebx");	# magic
+	&mov	($E,&DWP(16,"esi"));	
+	&mov	("ebx",&DWP(20,"esi"));
+	&mov	("ecx",&DWP(24,"esi"));
+	&mov	("edi",&DWP(28,"esi"));
+	# &mov	($Eoff,$E);
+	&mov	($Foff,"ebx");
+	&mov	($Goff,"ecx");
+	&mov	($Hoff,"edi");
+
+&set_label("00_15$suffix",16);
+
+	&BODY_00_15();
+
+	&cmp	("esi",0xc19bf174);
+	&jne	(&label("00_15$suffix"));
+
+	&mov	("ecx",&DWP(4*(9+15+16-1),"esp"));	# preloaded in BODY_00_15(1)
+	&jmp	(&label("16_63$suffix"));
+
+&set_label("16_63$suffix",16);
+
+	&BODY_16_63();
+
+	&cmp	("esi",0xc67178f2);
+	&jne	(&label("16_63$suffix"));
+
+	&mov	("esi",&DWP(4*(9+16+64)+0,"esp"));#ctx
+	# &mov	($A,$Aoff);
+	&mov	("ebx",$Boff);
+	# &mov	("edi",$Coff);
+	&mov	("ecx",$Doff);
+	&add	($A,&DWP(0,"esi"));
+	&add	("ebx",&DWP(4,"esi"));
+	&add	("edi",&DWP(8,"esi"));
+	&add	("ecx",&DWP(12,"esi"));
+	&mov	(&DWP(0,"esi"),$A);
+	&mov	(&DWP(4,"esi"),"ebx");
+	&mov	(&DWP(8,"esi"),"edi");
+	&mov	(&DWP(12,"esi"),"ecx");
+	# &mov	($E,$Eoff);
+	&mov	("eax",$Foff);
+	&mov	("ebx",$Goff);
+	&mov	("ecx",$Hoff);
+	&mov	("edi",&DWP(4*(9+16+64)+4,"esp"));#inp
+	&add	($E,&DWP(16,"esi"));
+	&add	("eax",&DWP(20,"esi"));
+	&add	("ebx",&DWP(24,"esi"));
+	&add	("ecx",&DWP(28,"esi"));
+	&mov	(&DWP(16,"esi"),$E);
+	&mov	(&DWP(20,"esi"),"eax");
+	&mov	(&DWP(24,"esi"),"ebx");
+	&mov	(&DWP(28,"esi"),"ecx");
+
+	&lea	("esp",&DWP(4*(9+16+64),"esp"));# destroy frame
+	&sub	($K256,4*64);			# rewind K
+
+	&cmp	("edi",&DWP(8,"esp"));		# are we done yet?
+	&jb	(&label("loop$suffix"));
+}
+	&COMPACT_LOOP();
+	&mov	("esp",&DWP(12,"esp"));		# restore sp
+&function_end_A();
+						if (!$i386 && !$xmm) {
+	# ~20% improvement on Sandy Bridge
+	local *ror = sub { &shrd(@_[0],@_) };
+	&COMPACT_LOOP("_shrd");
+	&mov	("esp",&DWP(12,"esp"));		# restore sp
+&function_end_A();
+						}
+
+&set_label("K256",64);	# Yes! I keep it in the code segment!
+@K256=(	0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5,
+	0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5,
+	0xd807aa98,0x12835b01,0x243185be,0x550c7dc3,
+	0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174,
+	0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc,
+	0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da,
+	0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7,
+	0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967,
+	0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13,
+	0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85,
+	0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3,
+	0xd192e819,0xd6990624,0xf40e3585,0x106aa070,
+	0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5,
+	0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3,
+	0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208,
+	0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2	);
+&data_word(@K256);
+&data_word(0x00010203,0x04050607,0x08090a0b,0x0c0d0e0f);	# byte swap mask
+&asciz("SHA256 block transform for x86, CRYPTOGAMS by ");
+
+($a,$b,$c,$d,$e,$f,$g,$h)=(0..7);	# offsets
+sub off { &DWP(4*(((shift)-$i)&7),"esp"); }
+
+if (!$i386 && $unroll_after) {
+my @AH=($A,$K256);
+
+&set_label("unrolled",16);
+	&lea	("esp",&DWP(-96,"esp"));
+	# copy ctx->h[0-7] to A,B,C,D,E,F,G,H on stack
+	&mov	($AH[0],&DWP(0,"esi"));
+	&mov	($AH[1],&DWP(4,"esi"));
+	&mov	("ecx",&DWP(8,"esi"));
+	&mov	("ebx",&DWP(12,"esi"));
+	#&mov	(&DWP(0,"esp"),$AH[0]);
+	&mov	(&DWP(4,"esp"),$AH[1]);
+	&xor	($AH[1],"ecx");		# magic
+	&mov	(&DWP(8,"esp"),"ecx");
+	&mov	(&DWP(12,"esp"),"ebx");
+	&mov	($E,&DWP(16,"esi"));	
+	&mov	("ebx",&DWP(20,"esi"));
+	&mov	("ecx",&DWP(24,"esi"));
+	&mov	("esi",&DWP(28,"esi"));
+	#&mov	(&DWP(16,"esp"),$E);
+	&mov	(&DWP(20,"esp"),"ebx");
+	&mov	(&DWP(24,"esp"),"ecx");
+	&mov	(&DWP(28,"esp"),"esi");
+	&jmp	(&label("grand_loop"));
+
+&set_label("grand_loop",16);
+    # copy input block to stack reversing byte order
+    for($i=0;$i<5;$i++) {
+	&mov	("ebx",&DWP(12*$i+0,"edi"));
+	&mov	("ecx",&DWP(12*$i+4,"edi"));
+	&bswap	("ebx");
+	&mov	("esi",&DWP(12*$i+8,"edi"));
+	&bswap	("ecx");
+	&mov	(&DWP(32+12*$i+0,"esp"),"ebx");
+	&bswap	("esi");
+	&mov	(&DWP(32+12*$i+4,"esp"),"ecx");
+	&mov	(&DWP(32+12*$i+8,"esp"),"esi");
+    }
+	&mov	("ebx",&DWP($i*12,"edi"));
+	&add	("edi",64);
+	&bswap	("ebx");
+	&mov	(&DWP(96+4,"esp"),"edi");
+	&mov	(&DWP(32+12*$i,"esp"),"ebx");
+
+    my ($t1,$t2) = ("ecx","esi");
+
+    for ($i=0;$i<64;$i++) {
+
+      if ($i>=16) {
+	&mov	($T,$t1);			# $t1 is preloaded
+	# &mov	($t2,&DWP(32+4*(($i+14)&15),"esp"));
+	&ror	($t1,18-7);
+	 &mov	("edi",$t2);
+	&ror	($t2,19-17);
+	 &xor	($t1,$T);
+	 &shr	($T,3);
+	&ror	($t1,7);
+	 &xor	($t2,"edi");
+	 &xor	($T,$t1);			# T = sigma0(X[-15])
+	&ror	($t2,17);
+	 &add	($T,&DWP(32+4*($i&15),"esp"));	# T += X[-16]
+	&shr	("edi",10);
+	 &add	($T,&DWP(32+4*(($i+9)&15),"esp"));	# T += X[-7]
+	#&xor	("edi",$t2)			# sigma1(X[-2])
+	# &add	($T,"edi");			# T += sigma1(X[-2])
+	# &mov	(&DWP(4*(9+15),"esp"),$T);	# save X[0]
+      }
+	&mov	($t1,$E);
+	 &xor	("edi",$t2)			if ($i>=16);	# sigma1(X[-2])
+	 &mov	($t2,&off($f));
+	&ror	($E,25-11);
+	 &add	($T,"edi")			if ($i>=16);	# T += sigma1(X[-2])
+	 &mov	("edi",&off($g));
+	&xor	($E,$t1);
+	 &mov	($T,&DWP(32+4*($i&15),"esp"))	if ($i<16);	# X[i]
+	 &mov	(&DWP(32+4*($i&15),"esp"),$T)	if ($i>=16 && $i<62);	# save X[0]
+	 &xor	($t2,"edi");
+	&ror	($E,11-6);
+	 &and	($t2,$t1);
+	 &mov	(&off($e),$t1);		# save $E, modulo-scheduled
+	&xor	($E,$t1);
+	 &add	($T,&off($h));		# T += h
+	 &xor	("edi",$t2);		# Ch(e,f,g)
+	&ror	($E,6);			# Sigma1(e)
+	 &mov	($t1,$AH[0]);
+	 &add	($T,"edi");		# T += Ch(e,f,g)
+
+	&ror	($t1,22-13);
+	 &mov	($t2,$AH[0]);
+	 &mov	("edi",&off($b));
+	&xor	($t1,$AH[0]);
+	 &mov	(&off($a),$AH[0]);	# save $A, modulo-scheduled
+	 &xor	($AH[0],"edi");		# a ^= b, (b^c) in next round
+	&ror	($t1,13-2);
+	 &and	($AH[1],$AH[0]);	# (b^c) &= (a^b)
+	 &lea	($E,&DWP(@K256[$i],$T,$E));	# T += Sigma1(1)+K[i]
+	&xor	($t1,$t2);
+	 &xor	($AH[1],"edi");		# h = Maj(a,b,c) = Ch(a^b,c,b)
+	 &mov	($t2,&DWP(32+4*(($i+2)&15),"esp"))	if ($i>=15 && $i<63);
+	&ror	($t1,2);		# Sigma0(a)
+
+	 &add	($AH[1],$E);		# h += T
+	 &add	($E,&off($d));		# d += T
+	&add	($AH[1],$t1);		# h += Sigma0(a)
+	 &mov	($t1,&DWP(32+4*(($i+15)&15),"esp"))	if ($i>=15 && $i<63);
+
+	@AH = reverse(@AH);		# rotate(a,h)
+	($t1,$t2) = ($t2,$t1);		# rotate(t1,t2)
+    }
+	&mov	("esi",&DWP(96,"esp"));	#ctx
+					#&mov	($AH[0],&DWP(0,"esp"));
+	&xor	($AH[1],"edi");		#&mov	($AH[1],&DWP(4,"esp"));
+					#&mov	("edi", &DWP(8,"esp"));
+	&mov	("ecx",&DWP(12,"esp"));
+	&add	($AH[0],&DWP(0,"esi"));
+	&add	($AH[1],&DWP(4,"esi"));
+	&add	("edi",&DWP(8,"esi"));
+	&add	("ecx",&DWP(12,"esi"));
+	&mov	(&DWP(0,"esi"),$AH[0]);
+	&mov	(&DWP(4,"esi"),$AH[1]);
+	&mov	(&DWP(8,"esi"),"edi");
+	&mov	(&DWP(12,"esi"),"ecx");
+	 #&mov	(&DWP(0,"esp"),$AH[0]);
+	 &mov	(&DWP(4,"esp"),$AH[1]);
+	 &xor	($AH[1],"edi");		# magic
+	 &mov	(&DWP(8,"esp"),"edi");
+	 &mov	(&DWP(12,"esp"),"ecx");
+	#&mov	($E,&DWP(16,"esp"));
+	&mov	("edi",&DWP(20,"esp"));
+	&mov	("ebx",&DWP(24,"esp"));
+	&mov	("ecx",&DWP(28,"esp"));
+	&add	($E,&DWP(16,"esi"));
+	&add	("edi",&DWP(20,"esi"));
+	&add	("ebx",&DWP(24,"esi"));
+	&add	("ecx",&DWP(28,"esi"));
+	&mov	(&DWP(16,"esi"),$E);
+	&mov	(&DWP(20,"esi"),"edi");
+	&mov	(&DWP(24,"esi"),"ebx");
+	&mov	(&DWP(28,"esi"),"ecx");
+	 #&mov	(&DWP(16,"esp"),$E);
+	 &mov	(&DWP(20,"esp"),"edi");
+	&mov	("edi",&DWP(96+4,"esp"));	# inp
+	 &mov	(&DWP(24,"esp"),"ebx");
+	 &mov	(&DWP(28,"esp"),"ecx");
+
+	&cmp	("edi",&DWP(96+8,"esp"));	# are we done yet?
+	&jb	(&label("grand_loop"));
+
+	&mov	("esp",&DWP(96+12,"esp"));	# restore sp
+&function_end_A();
+}
+						if (!$i386 && $xmm) {{{
+if ($shaext) {
+######################################################################
+# Intel SHA Extensions implementation of SHA256 update function.
+#
+my ($ctx,$inp,$end)=("esi","edi","eax");
+my ($Wi,$ABEF,$CDGH,$TMP)=map("xmm$_",(0..2,7));
+my @MSG=map("xmm$_",(3..6));
+
+sub sha256op38 {
+ my ($opcodelet,$dst,$src)=@_;
+    if ("$dst:$src" =~ /xmm([0-7]):xmm([0-7])/)
+    {	&data_byte(0x0f,0x38,$opcodelet,0xc0|($1<<3)|$2);	}
+}
+sub sha256rnds2	{ sha256op38(0xcb,@_); }
+sub sha256msg1	{ sha256op38(0xcc,@_); }
+sub sha256msg2	{ sha256op38(0xcd,@_); }
+
+&set_label("shaext",32);
+	&sub		("esp",32);
+
+	&movdqu		($ABEF,&QWP(0,$ctx));		# DCBA
+	&lea		($K256,&DWP(0x80,$K256));
+	&movdqu		($CDGH,&QWP(16,$ctx));		# HGFE
+	&movdqa		($TMP,&QWP(0x100-0x80,$K256));	# byte swap mask
+
+	&pshufd		($Wi,$ABEF,0x1b);		# ABCD
+	&pshufd		($ABEF,$ABEF,0xb1);		# CDAB
+	&pshufd		($CDGH,$CDGH,0x1b);		# EFGH
+	&palignr	($ABEF,$CDGH,8);		# ABEF
+	&punpcklqdq	($CDGH,$Wi);			# CDGH
+	&jmp		(&label("loop_shaext"));
+
+&set_label("loop_shaext",16);
+	&movdqu		(@MSG[0],&QWP(0,$inp));
+	&movdqu		(@MSG[1],&QWP(0x10,$inp));
+	&movdqu		(@MSG[2],&QWP(0x20,$inp));
+	&pshufb		(@MSG[0],$TMP);
+	&movdqu		(@MSG[3],&QWP(0x30,$inp));
+	&movdqa		(&QWP(16,"esp"),$CDGH);		# offload
+
+	&movdqa		($Wi,&QWP(0*16-0x80,$K256));
+	&paddd		($Wi,@MSG[0]);
+	&pshufb		(@MSG[1],$TMP);
+	&sha256rnds2	($CDGH,$ABEF);			# 0-3
+	&pshufd		($Wi,$Wi,0x0e);
+	&nop		();
+	&movdqa		(&QWP(0,"esp"),$ABEF);		# offload
+	&sha256rnds2	($ABEF,$CDGH);
+
+	&movdqa		($Wi,&QWP(1*16-0x80,$K256));
+	&paddd		($Wi,@MSG[1]);
+	&pshufb		(@MSG[2],$TMP);
+	&sha256rnds2	($CDGH,$ABEF);			# 4-7
+	&pshufd		($Wi,$Wi,0x0e);
+	&lea		($inp,&DWP(0x40,$inp));
+	&sha256msg1	(@MSG[0],@MSG[1]);
+	&sha256rnds2	($ABEF,$CDGH);
+
+	&movdqa		($Wi,&QWP(2*16-0x80,$K256));
+	&paddd		($Wi,@MSG[2]);
+	&pshufb		(@MSG[3],$TMP);
+	&sha256rnds2	($CDGH,$ABEF);			# 8-11
+	&pshufd		($Wi,$Wi,0x0e);
+	&movdqa		($TMP,@MSG[3]);
+	&palignr	($TMP,@MSG[2],4);
+	&nop		();
+	&paddd		(@MSG[0],$TMP);
+	&sha256msg1	(@MSG[1],@MSG[2]);
+	&sha256rnds2	($ABEF,$CDGH);
+
+	&movdqa		($Wi,&QWP(3*16-0x80,$K256));
+	&paddd		($Wi,@MSG[3]);
+	&sha256msg2	(@MSG[0],@MSG[3]);
+	&sha256rnds2	($CDGH,$ABEF);			# 12-15
+	&pshufd		($Wi,$Wi,0x0e);
+	&movdqa		($TMP,@MSG[0]);
+	&palignr	($TMP,@MSG[3],4);
+	&nop		();
+	&paddd		(@MSG[1],$TMP);
+	&sha256msg1	(@MSG[2],@MSG[3]);
+	&sha256rnds2	($ABEF,$CDGH);
+
+for($i=4;$i<16-3;$i++) {
+	&movdqa		($Wi,&QWP($i*16-0x80,$K256));
+	&paddd		($Wi,@MSG[0]);
+	&sha256msg2	(@MSG[1],@MSG[0]);
+	&sha256rnds2	($CDGH,$ABEF);			# 16-19...
+	&pshufd		($Wi,$Wi,0x0e);
+	&movdqa		($TMP,@MSG[1]);
+	&palignr	($TMP,@MSG[0],4);
+	&nop		();
+	&paddd		(@MSG[2],$TMP);
+	&sha256msg1	(@MSG[3],@MSG[0]);
+	&sha256rnds2	($ABEF,$CDGH);
+
+	push(@MSG,shift(@MSG));
+}
+	&movdqa		($Wi,&QWP(13*16-0x80,$K256));
+	&paddd		($Wi,@MSG[0]);
+	&sha256msg2	(@MSG[1],@MSG[0]);
+	&sha256rnds2	($CDGH,$ABEF);			# 52-55
+	&pshufd		($Wi,$Wi,0x0e);
+	&movdqa		($TMP,@MSG[1])
+	&palignr	($TMP,@MSG[0],4);
+	&sha256rnds2	($ABEF,$CDGH);
+	&paddd		(@MSG[2],$TMP);
+
+	&movdqa		($Wi,&QWP(14*16-0x80,$K256));
+	&paddd		($Wi,@MSG[1]);
+	&sha256rnds2	($CDGH,$ABEF);			# 56-59
+	&pshufd		($Wi,$Wi,0x0e);
+	&sha256msg2	(@MSG[2],@MSG[1]);
+	&movdqa		($TMP,&QWP(0x100-0x80,$K256));	# byte swap mask
+	&sha256rnds2	($ABEF,$CDGH);
+
+	&movdqa		($Wi,&QWP(15*16-0x80,$K256));
+	&paddd		($Wi,@MSG[2]);
+	&nop		();
+	&sha256rnds2	($CDGH,$ABEF);			# 60-63
+	&pshufd		($Wi,$Wi,0x0e);
+	&cmp		($end,$inp);
+	&nop		();
+	&sha256rnds2	($ABEF,$CDGH);
+
+	&paddd		($CDGH,&QWP(16,"esp"));
+	&paddd		($ABEF,&QWP(0,"esp"));
+	&jnz		(&label("loop_shaext"));
+
+	&pshufd		($CDGH,$CDGH,0xb1);		# DCHG
+	&pshufd		($TMP,$ABEF,0x1b);		# FEBA
+	&pshufd		($ABEF,$ABEF,0xb1);		# BAFE
+	&punpckhqdq	($ABEF,$CDGH);			# DCBA
+	&palignr	($CDGH,$TMP,8);			# HGFE
+
+	&mov		("esp",&DWP(32+12,"esp"));
+	&movdqu		(&QWP(0,$ctx),$ABEF);
+	&movdqu		(&QWP(16,$ctx),$CDGH);
+&function_end_A();
+}
+
+my @X = map("xmm$_",(0..3));
+my ($t0,$t1,$t2,$t3) = map("xmm$_",(4..7));
+my @AH = ($A,$T);
+
+&set_label("SSSE3",32);
+	&lea	("esp",&DWP(-96,"esp"));
+	# copy ctx->h[0-7] to A,B,C,D,E,F,G,H on stack
+	&mov	($AH[0],&DWP(0,"esi"));
+	&mov	($AH[1],&DWP(4,"esi"));
+	&mov	("ecx",&DWP(8,"esi"));
+	&mov	("edi",&DWP(12,"esi"));
+	#&mov	(&DWP(0,"esp"),$AH[0]);
+	&mov	(&DWP(4,"esp"),$AH[1]);
+	&xor	($AH[1],"ecx");			# magic
+	&mov	(&DWP(8,"esp"),"ecx");
+	&mov	(&DWP(12,"esp"),"edi");
+	&mov	($E,&DWP(16,"esi"));
+	&mov	("edi",&DWP(20,"esi"));
+	&mov	("ecx",&DWP(24,"esi"));
+	&mov	("esi",&DWP(28,"esi"));
+	#&mov	(&DWP(16,"esp"),$E);
+	&mov	(&DWP(20,"esp"),"edi");
+	&mov	("edi",&DWP(96+4,"esp"));	# inp
+	&mov	(&DWP(24,"esp"),"ecx");
+	&mov	(&DWP(28,"esp"),"esi");
+	&movdqa	($t3,&QWP(256,$K256));
+	&jmp	(&label("grand_ssse3"));
+
+&set_label("grand_ssse3",16);
+	# load input, reverse byte order, add K256[0..15], save to stack
+	&movdqu	(@X[0],&QWP(0,"edi"));
+	&movdqu	(@X[1],&QWP(16,"edi"));
+	&movdqu	(@X[2],&QWP(32,"edi"));
+	&movdqu	(@X[3],&QWP(48,"edi"));
+	&add	("edi",64);
+	&pshufb	(@X[0],$t3);
+	&mov	(&DWP(96+4,"esp"),"edi");
+	&pshufb	(@X[1],$t3);
+	&movdqa	($t0,&QWP(0,$K256));
+	&pshufb	(@X[2],$t3);
+	&movdqa	($t1,&QWP(16,$K256));
+	&paddd	($t0,@X[0]);
+	&pshufb	(@X[3],$t3);
+	&movdqa	($t2,&QWP(32,$K256));
+	&paddd	($t1,@X[1]);
+	&movdqa	($t3,&QWP(48,$K256));
+	&movdqa	(&QWP(32+0,"esp"),$t0);
+	&paddd	($t2,@X[2]);
+	&movdqa	(&QWP(32+16,"esp"),$t1);
+	&paddd	($t3,@X[3]);
+	&movdqa	(&QWP(32+32,"esp"),$t2);
+	&movdqa	(&QWP(32+48,"esp"),$t3);
+	&jmp	(&label("ssse3_00_47"));
+
+&set_label("ssse3_00_47",16);
+	&add		($K256,64);
+
+sub SSSE3_00_47 () {
+my $j = shift;
+my $body = shift;
+my @X = @_;
+my @insns = (&$body,&$body,&$body,&$body);	# 120 instructions
+
+	  eval(shift(@insns));
+	&movdqa		($t0,@X[1]);
+	  eval(shift(@insns));			# @
+	  eval(shift(@insns));
+	&movdqa		($t3,@X[3]);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	&palignr	($t0,@X[0],4);		# X[1..4]
+	  eval(shift(@insns));
+	  eval(shift(@insns));			# @
+	  eval(shift(@insns));
+	 &palignr	($t3,@X[2],4);		# X[9..12]
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	&movdqa		($t1,$t0);
+	  eval(shift(@insns));			# @
+	  eval(shift(@insns));
+	&movdqa		($t2,$t0);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	&psrld		($t0,3);
+	  eval(shift(@insns));
+	  eval(shift(@insns));			# @
+	 &paddd		(@X[0],$t3);		# X[0..3] += X[9..12]
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	&psrld		($t2,7);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));			# @
+	  eval(shift(@insns));
+	 &pshufd	($t3,@X[3],0b11111010);	# X[14..15]
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	&pslld		($t1,32-18);
+	  eval(shift(@insns));
+	  eval(shift(@insns));			# @
+	&pxor		($t0,$t2);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	&psrld		($t2,18-7);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));			# @
+	&pxor		($t0,$t1);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	&pslld		($t1,18-7);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));			# @
+	&pxor		($t0,$t2);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	 &movdqa	($t2,$t3);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));			# @
+	&pxor		($t0,$t1);		# sigma0(X[1..4])
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	 &psrld		($t3,10);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));			# @
+	&paddd		(@X[0],$t0);		# X[0..3] += sigma0(X[1..4])
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	 &psrlq		($t2,17);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));			# @
+	 &pxor		($t3,$t2);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	 &psrlq		($t2,19-17);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));			# @
+	 &pxor		($t3,$t2);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	 &pshufd	($t3,$t3,0b10000000);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));			# @
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));			# @
+	  eval(shift(@insns));
+	 &psrldq	($t3,8);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	&paddd		(@X[0],$t3);		# X[0..1] += sigma1(X[14..15])
+	  eval(shift(@insns));			# @
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));			# @
+	  eval(shift(@insns));
+	 &pshufd	($t3,@X[0],0b01010000);	# X[16..17]
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	 &movdqa	($t2,$t3);
+	  eval(shift(@insns));			# @
+	 &psrld		($t3,10);
+	  eval(shift(@insns));
+	 &psrlq		($t2,17);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));			# @
+	 &pxor		($t3,$t2);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	 &psrlq		($t2,19-17);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));			# @
+	 &pxor		($t3,$t2);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	 &pshufd	($t3,$t3,0b00001000);
+	  eval(shift(@insns));
+	  eval(shift(@insns));			# @
+	&movdqa		($t2,&QWP(16*$j,$K256));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	 &pslldq	($t3,8);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));			# @
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));			# @
+	&paddd		(@X[0],$t3);		# X[2..3] += sigma1(X[16..17])
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	&paddd		($t2,@X[0]);
+	  eval(shift(@insns));			# @
+
+	foreach (@insns) { eval; }		# remaining instructions
+
+	&movdqa		(&QWP(32+16*$j,"esp"),$t2);
+}
+
+sub body_00_15 () {
+	(
+	'&mov	("ecx",$E);',
+	'&ror	($E,25-11);',
+	 '&mov	("esi",&off($f));',
+	'&xor	($E,"ecx");',
+	 '&mov	("edi",&off($g));',
+	 '&xor	("esi","edi");',
+	'&ror	($E,11-6);',
+	 '&and	("esi","ecx");',
+	 '&mov	(&off($e),"ecx");',	# save $E, modulo-scheduled
+	'&xor	($E,"ecx");',
+	 '&xor	("edi","esi");',	# Ch(e,f,g)
+	'&ror	($E,6);',		# T = Sigma1(e)
+	 '&mov	("ecx",$AH[0]);',
+	 '&add	($E,"edi");',		# T += Ch(e,f,g)
+	 '&mov	("edi",&off($b));',
+	'&mov	("esi",$AH[0]);',
+
+	'&ror	("ecx",22-13);',
+	 '&mov	(&off($a),$AH[0]);',	# save $A, modulo-scheduled
+	'&xor	("ecx",$AH[0]);',
+	 '&xor	($AH[0],"edi");',	# a ^= b, (b^c) in next round
+	 '&add	($E,&off($h));',	# T += h
+	'&ror	("ecx",13-2);',
+	 '&and	($AH[1],$AH[0]);',	# (b^c) &= (a^b)
+	'&xor	("ecx","esi");',
+	 '&add	($E,&DWP(32+4*($i&15),"esp"));',	# T += K[i]+X[i]
+	 '&xor	($AH[1],"edi");',	# h = Maj(a,b,c) = Ch(a^b,c,b)
+	'&ror	("ecx",2);',		# Sigma0(a)
+
+	 '&add	($AH[1],$E);',		# h += T
+	 '&add	($E,&off($d));',	# d += T
+	'&add	($AH[1],"ecx");'.	# h += Sigma0(a)
+
+	'@AH = reverse(@AH); $i++;'	# rotate(a,h)
+	);
+}
+
+    for ($i=0,$j=0; $j<4; $j++) {
+	&SSSE3_00_47($j,\&body_00_15,@X);
+	push(@X,shift(@X));		# rotate(@X)
+    }
+	&cmp	(&DWP(16*$j,$K256),0x00010203);
+	&jne	(&label("ssse3_00_47"));
+
+    for ($i=0; $i<16; ) {
+	foreach(body_00_15()) { eval; }
+    }
+
+	&mov	("esi",&DWP(96,"esp"));	#ctx
+					#&mov	($AH[0],&DWP(0,"esp"));
+	&xor	($AH[1],"edi");		#&mov	($AH[1],&DWP(4,"esp"));
+					#&mov	("edi", &DWP(8,"esp"));
+	&mov	("ecx",&DWP(12,"esp"));
+	&add	($AH[0],&DWP(0,"esi"));
+	&add	($AH[1],&DWP(4,"esi"));
+	&add	("edi",&DWP(8,"esi"));
+	&add	("ecx",&DWP(12,"esi"));
+	&mov	(&DWP(0,"esi"),$AH[0]);
+	&mov	(&DWP(4,"esi"),$AH[1]);
+	&mov	(&DWP(8,"esi"),"edi");
+	&mov	(&DWP(12,"esi"),"ecx");
+	 #&mov	(&DWP(0,"esp"),$AH[0]);
+	 &mov	(&DWP(4,"esp"),$AH[1]);
+	 &xor	($AH[1],"edi");			# magic
+	 &mov	(&DWP(8,"esp"),"edi");
+	 &mov	(&DWP(12,"esp"),"ecx");
+	#&mov	($E,&DWP(16,"esp"));
+	&mov	("edi",&DWP(20,"esp"));
+	&mov	("ecx",&DWP(24,"esp"));
+	&add	($E,&DWP(16,"esi"));
+	&add	("edi",&DWP(20,"esi"));
+	&add	("ecx",&DWP(24,"esi"));
+	&mov	(&DWP(16,"esi"),$E);
+	&mov	(&DWP(20,"esi"),"edi");
+	 &mov	(&DWP(20,"esp"),"edi");
+	&mov	("edi",&DWP(28,"esp"));
+	&mov	(&DWP(24,"esi"),"ecx");
+	 #&mov	(&DWP(16,"esp"),$E);
+	&add	("edi",&DWP(28,"esi"));
+	 &mov	(&DWP(24,"esp"),"ecx");
+	&mov	(&DWP(28,"esi"),"edi");
+	 &mov	(&DWP(28,"esp"),"edi");
+	&mov	("edi",&DWP(96+4,"esp"));	# inp
+
+	&movdqa	($t3,&QWP(64,$K256));
+	&sub	($K256,3*64);			# rewind K
+	&cmp	("edi",&DWP(96+8,"esp"));	# are we done yet?
+	&jb	(&label("grand_ssse3"));
+
+	&mov	("esp",&DWP(96+12,"esp"));	# restore sp
+&function_end_A();
+						if ($avx) {
+&set_label("AVX",32);
+						if ($avx>1) {
+	&and	("edx",1<<8|1<<3);		# check for BMI2+BMI1
+	&cmp	("edx",1<<8|1<<3);
+	&je	(&label("AVX_BMI"));
+						}
+	&lea	("esp",&DWP(-96,"esp"));
+	&vzeroall	();
+	# copy ctx->h[0-7] to A,B,C,D,E,F,G,H on stack
+	&mov	($AH[0],&DWP(0,"esi"));
+	&mov	($AH[1],&DWP(4,"esi"));
+	&mov	("ecx",&DWP(8,"esi"));
+	&mov	("edi",&DWP(12,"esi"));
+	#&mov	(&DWP(0,"esp"),$AH[0]);
+	&mov	(&DWP(4,"esp"),$AH[1]);
+	&xor	($AH[1],"ecx");			# magic
+	&mov	(&DWP(8,"esp"),"ecx");
+	&mov	(&DWP(12,"esp"),"edi");
+	&mov	($E,&DWP(16,"esi"));
+	&mov	("edi",&DWP(20,"esi"));
+	&mov	("ecx",&DWP(24,"esi"));
+	&mov	("esi",&DWP(28,"esi"));
+	#&mov	(&DWP(16,"esp"),$E);
+	&mov	(&DWP(20,"esp"),"edi");
+	&mov	("edi",&DWP(96+4,"esp"));	# inp
+	&mov	(&DWP(24,"esp"),"ecx");
+	&mov	(&DWP(28,"esp"),"esi");
+	&vmovdqa	($t3,&QWP(256,$K256));
+	&jmp	(&label("grand_avx"));
+
+&set_label("grand_avx",32);
+	# load input, reverse byte order, add K256[0..15], save to stack
+	&vmovdqu	(@X[0],&QWP(0,"edi"));
+	&vmovdqu	(@X[1],&QWP(16,"edi"));
+	&vmovdqu	(@X[2],&QWP(32,"edi"));
+	&vmovdqu	(@X[3],&QWP(48,"edi"));
+	&add		("edi",64);
+	&vpshufb	(@X[0],@X[0],$t3);
+	&mov		(&DWP(96+4,"esp"),"edi");
+	&vpshufb	(@X[1],@X[1],$t3);
+	&vpshufb	(@X[2],@X[2],$t3);
+	&vpaddd		($t0,@X[0],&QWP(0,$K256));
+	&vpshufb	(@X[3],@X[3],$t3);
+	&vpaddd		($t1,@X[1],&QWP(16,$K256));
+	&vpaddd		($t2,@X[2],&QWP(32,$K256));
+	&vpaddd		($t3,@X[3],&QWP(48,$K256));
+	&vmovdqa	(&QWP(32+0,"esp"),$t0);
+	&vmovdqa	(&QWP(32+16,"esp"),$t1);
+	&vmovdqa	(&QWP(32+32,"esp"),$t2);
+	&vmovdqa	(&QWP(32+48,"esp"),$t3);
+	&jmp		(&label("avx_00_47"));
+
+&set_label("avx_00_47",16);
+	&add		($K256,64);
+
+sub Xupdate_AVX () {
+	(
+	'&vpalignr	($t0,@X[1],@X[0],4);',	# X[1..4]
+	 '&vpalignr	($t3,@X[3],@X[2],4);',	# X[9..12]
+	'&vpsrld	($t2,$t0,7);',
+	 '&vpaddd	(@X[0],@X[0],$t3);',	# X[0..3] += X[9..16]
+	'&vpsrld	($t3,$t0,3);',
+	'&vpslld	($t1,$t0,14);',
+	'&vpxor		($t0,$t3,$t2);',
+	 '&vpshufd	($t3,@X[3],0b11111010)',# X[14..15]
+	'&vpsrld	($t2,$t2,18-7);',
+	'&vpxor		($t0,$t0,$t1);',
+	'&vpslld	($t1,$t1,25-14);',
+	'&vpxor		($t0,$t0,$t2);',
+	 '&vpsrld	($t2,$t3,10);',
+	'&vpxor		($t0,$t0,$t1);',	# sigma0(X[1..4])
+	 '&vpsrlq	($t1,$t3,17);',
+	'&vpaddd	(@X[0],@X[0],$t0);',	# X[0..3] += sigma0(X[1..4])
+	 '&vpxor	($t2,$t2,$t1);',
+	 '&vpsrlq	($t3,$t3,19);',
+	 '&vpxor	($t2,$t2,$t3);',	# sigma1(X[14..15]
+	 '&vpshufd	($t3,$t2,0b10000100);',
+	'&vpsrldq	($t3,$t3,8);',
+	'&vpaddd	(@X[0],@X[0],$t3);',	# X[0..1] += sigma1(X[14..15])
+	 '&vpshufd	($t3,@X[0],0b01010000)',# X[16..17]
+	 '&vpsrld	($t2,$t3,10);',
+	 '&vpsrlq	($t1,$t3,17);',
+	 '&vpxor	($t2,$t2,$t1);',
+	 '&vpsrlq	($t3,$t3,19);',
+	 '&vpxor	($t2,$t2,$t3);',	# sigma1(X[16..17]
+	 '&vpshufd	($t3,$t2,0b11101000);',
+	'&vpslldq	($t3,$t3,8);',
+	'&vpaddd	(@X[0],@X[0],$t3);'	# X[2..3] += sigma1(X[16..17])
+	);
+}
+
+local *ror = sub { &shrd(@_[0],@_) };
+sub AVX_00_47 () {
+my $j = shift;
+my $body = shift;
+my @X = @_;
+my @insns = (&$body,&$body,&$body,&$body);	# 120 instructions
+my $insn;
+
+	foreach (Xupdate_AVX()) {		# 31 instructions
+	    eval;
+	    eval(shift(@insns));
+	    eval(shift(@insns));
+	    eval($insn = shift(@insns));
+	    eval(shift(@insns)) if ($insn =~ /rorx/ && @insns[0] =~ /rorx/);
+	}
+	&vpaddd		($t2,@X[0],&QWP(16*$j,$K256));
+	foreach (@insns) { eval; }		# remaining instructions
+	&vmovdqa	(&QWP(32+16*$j,"esp"),$t2);
+}
+
+    for ($i=0,$j=0; $j<4; $j++) {
+	&AVX_00_47($j,\&body_00_15,@X);
+	push(@X,shift(@X));		# rotate(@X)
+    }
+	&cmp	(&DWP(16*$j,$K256),0x00010203);
+	&jne	(&label("avx_00_47"));
+
+    for ($i=0; $i<16; ) {
+	foreach(body_00_15()) { eval; }
+    }
+
+	&mov	("esi",&DWP(96,"esp"));	#ctx
+					#&mov	($AH[0],&DWP(0,"esp"));
+	&xor	($AH[1],"edi");		#&mov	($AH[1],&DWP(4,"esp"));
+					#&mov	("edi", &DWP(8,"esp"));
+	&mov	("ecx",&DWP(12,"esp"));
+	&add	($AH[0],&DWP(0,"esi"));
+	&add	($AH[1],&DWP(4,"esi"));
+	&add	("edi",&DWP(8,"esi"));
+	&add	("ecx",&DWP(12,"esi"));
+	&mov	(&DWP(0,"esi"),$AH[0]);
+	&mov	(&DWP(4,"esi"),$AH[1]);
+	&mov	(&DWP(8,"esi"),"edi");
+	&mov	(&DWP(12,"esi"),"ecx");
+	 #&mov	(&DWP(0,"esp"),$AH[0]);
+	 &mov	(&DWP(4,"esp"),$AH[1]);
+	 &xor	($AH[1],"edi");			# magic
+	 &mov	(&DWP(8,"esp"),"edi");
+	 &mov	(&DWP(12,"esp"),"ecx");
+	#&mov	($E,&DWP(16,"esp"));
+	&mov	("edi",&DWP(20,"esp"));
+	&mov	("ecx",&DWP(24,"esp"));
+	&add	($E,&DWP(16,"esi"));
+	&add	("edi",&DWP(20,"esi"));
+	&add	("ecx",&DWP(24,"esi"));
+	&mov	(&DWP(16,"esi"),$E);
+	&mov	(&DWP(20,"esi"),"edi");
+	 &mov	(&DWP(20,"esp"),"edi");
+	&mov	("edi",&DWP(28,"esp"));
+	&mov	(&DWP(24,"esi"),"ecx");
+	 #&mov	(&DWP(16,"esp"),$E);
+	&add	("edi",&DWP(28,"esi"));
+	 &mov	(&DWP(24,"esp"),"ecx");
+	&mov	(&DWP(28,"esi"),"edi");
+	 &mov	(&DWP(28,"esp"),"edi");
+	&mov	("edi",&DWP(96+4,"esp"));	# inp
+
+	&vmovdqa	($t3,&QWP(64,$K256));
+	&sub	($K256,3*64);			# rewind K
+	&cmp	("edi",&DWP(96+8,"esp"));	# are we done yet?
+	&jb	(&label("grand_avx"));
+
+	&mov	("esp",&DWP(96+12,"esp"));	# restore sp
+	&vzeroall	();
+&function_end_A();
+						if ($avx>1) {
+sub bodyx_00_15 () {			# +10%
+	(
+	'&rorx	("ecx",$E,6)',
+	'&rorx	("esi",$E,11)',
+	 '&mov	(&off($e),$E)',		# save $E, modulo-scheduled
+	'&rorx	("edi",$E,25)',
+	'&xor	("ecx","esi")',
+	 '&andn	("esi",$E,&off($g))',
+	'&xor	("ecx","edi")',		# Sigma1(e)
+	 '&and	($E,&off($f))',
+	 '&mov	(&off($a),$AH[0]);',	# save $A, modulo-scheduled
+	 '&or	($E,"esi")',		# T = Ch(e,f,g)
+
+	'&rorx	("edi",$AH[0],2)',
+	'&rorx	("esi",$AH[0],13)',
+	 '&lea	($E,&DWP(0,$E,"ecx"))',	# T += Sigma1(e)
+	'&rorx	("ecx",$AH[0],22)',
+	'&xor	("esi","edi")',
+	 '&mov	("edi",&off($b))',
+	'&xor	("ecx","esi")',		# Sigma0(a)
+
+	 '&xor	($AH[0],"edi")',	# a ^= b, (b^c) in next round
+	 '&add	($E,&off($h))',		# T += h
+	 '&and	($AH[1],$AH[0])',	# (b^c) &= (a^b)
+	 '&add	($E,&DWP(32+4*($i&15),"esp"))',	# T += K[i]+X[i]
+	 '&xor	($AH[1],"edi")',	# h = Maj(a,b,c) = Ch(a^b,c,b)
+
+	 '&add	("ecx",$E)',		# h += T
+	 '&add	($E,&off($d))',		# d += T
+	'&lea	($AH[1],&DWP(0,$AH[1],"ecx"));'.	# h += Sigma0(a)
+
+	'@AH = reverse(@AH); $i++;'	# rotate(a,h)
+	);
+}
+
+&set_label("AVX_BMI",32);
+	&lea	("esp",&DWP(-96,"esp"));
+	&vzeroall	();
+	# copy ctx->h[0-7] to A,B,C,D,E,F,G,H on stack
+	&mov	($AH[0],&DWP(0,"esi"));
+	&mov	($AH[1],&DWP(4,"esi"));
+	&mov	("ecx",&DWP(8,"esi"));
+	&mov	("edi",&DWP(12,"esi"));
+	#&mov	(&DWP(0,"esp"),$AH[0]);
+	&mov	(&DWP(4,"esp"),$AH[1]);
+	&xor	($AH[1],"ecx");			# magic
+	&mov	(&DWP(8,"esp"),"ecx");
+	&mov	(&DWP(12,"esp"),"edi");
+	&mov	($E,&DWP(16,"esi"));
+	&mov	("edi",&DWP(20,"esi"));
+	&mov	("ecx",&DWP(24,"esi"));
+	&mov	("esi",&DWP(28,"esi"));
+	#&mov	(&DWP(16,"esp"),$E);
+	&mov	(&DWP(20,"esp"),"edi");
+	&mov	("edi",&DWP(96+4,"esp"));	# inp
+	&mov	(&DWP(24,"esp"),"ecx");
+	&mov	(&DWP(28,"esp"),"esi");
+	&vmovdqa	($t3,&QWP(256,$K256));
+	&jmp	(&label("grand_avx_bmi"));
+
+&set_label("grand_avx_bmi",32);
+	# load input, reverse byte order, add K256[0..15], save to stack
+	&vmovdqu	(@X[0],&QWP(0,"edi"));
+	&vmovdqu	(@X[1],&QWP(16,"edi"));
+	&vmovdqu	(@X[2],&QWP(32,"edi"));
+	&vmovdqu	(@X[3],&QWP(48,"edi"));
+	&add		("edi",64);
+	&vpshufb	(@X[0],@X[0],$t3);
+	&mov		(&DWP(96+4,"esp"),"edi");
+	&vpshufb	(@X[1],@X[1],$t3);
+	&vpshufb	(@X[2],@X[2],$t3);
+	&vpaddd		($t0,@X[0],&QWP(0,$K256));
+	&vpshufb	(@X[3],@X[3],$t3);
+	&vpaddd		($t1,@X[1],&QWP(16,$K256));
+	&vpaddd		($t2,@X[2],&QWP(32,$K256));
+	&vpaddd		($t3,@X[3],&QWP(48,$K256));
+	&vmovdqa	(&QWP(32+0,"esp"),$t0);
+	&vmovdqa	(&QWP(32+16,"esp"),$t1);
+	&vmovdqa	(&QWP(32+32,"esp"),$t2);
+	&vmovdqa	(&QWP(32+48,"esp"),$t3);
+	&jmp		(&label("avx_bmi_00_47"));
+
+&set_label("avx_bmi_00_47",16);
+	&add		($K256,64);
+
+    for ($i=0,$j=0; $j<4; $j++) {
+	&AVX_00_47($j,\&bodyx_00_15,@X);
+	push(@X,shift(@X));		# rotate(@X)
+    }
+	&cmp	(&DWP(16*$j,$K256),0x00010203);
+	&jne	(&label("avx_bmi_00_47"));
+
+    for ($i=0; $i<16; ) {
+	foreach(bodyx_00_15()) { eval; }
+    }
+
+	&mov	("esi",&DWP(96,"esp"));	#ctx
+					#&mov	($AH[0],&DWP(0,"esp"));
+	&xor	($AH[1],"edi");		#&mov	($AH[1],&DWP(4,"esp"));
+					#&mov	("edi", &DWP(8,"esp"));
+	&mov	("ecx",&DWP(12,"esp"));
+	&add	($AH[0],&DWP(0,"esi"));
+	&add	($AH[1],&DWP(4,"esi"));
+	&add	("edi",&DWP(8,"esi"));
+	&add	("ecx",&DWP(12,"esi"));
+	&mov	(&DWP(0,"esi"),$AH[0]);
+	&mov	(&DWP(4,"esi"),$AH[1]);
+	&mov	(&DWP(8,"esi"),"edi");
+	&mov	(&DWP(12,"esi"),"ecx");
+	 #&mov	(&DWP(0,"esp"),$AH[0]);
+	 &mov	(&DWP(4,"esp"),$AH[1]);
+	 &xor	($AH[1],"edi");			# magic
+	 &mov	(&DWP(8,"esp"),"edi");
+	 &mov	(&DWP(12,"esp"),"ecx");
+	#&mov	($E,&DWP(16,"esp"));
+	&mov	("edi",&DWP(20,"esp"));
+	&mov	("ecx",&DWP(24,"esp"));
+	&add	($E,&DWP(16,"esi"));
+	&add	("edi",&DWP(20,"esi"));
+	&add	("ecx",&DWP(24,"esi"));
+	&mov	(&DWP(16,"esi"),$E);
+	&mov	(&DWP(20,"esi"),"edi");
+	 &mov	(&DWP(20,"esp"),"edi");
+	&mov	("edi",&DWP(28,"esp"));
+	&mov	(&DWP(24,"esi"),"ecx");
+	 #&mov	(&DWP(16,"esp"),$E);
+	&add	("edi",&DWP(28,"esi"));
+	 &mov	(&DWP(24,"esp"),"ecx");
+	&mov	(&DWP(28,"esi"),"edi");
+	 &mov	(&DWP(28,"esp"),"edi");
+	&mov	("edi",&DWP(96+4,"esp"));	# inp
+
+	&vmovdqa	($t3,&QWP(64,$K256));
+	&sub	($K256,3*64);			# rewind K
+	&cmp	("edi",&DWP(96+8,"esp"));	# are we done yet?
+	&jb	(&label("grand_avx_bmi"));
+
+	&mov	("esp",&DWP(96+12,"esp"));	# restore sp
+	&vzeroall	();
+&function_end_A();
+						}
+						}
+						}}}
+&function_end_B("sha256_block_data_order");
+
+&asm_finish();
+
+close STDOUT;
diff --git a/crypto/sha/asm/sha256-armv4.pl b/crypto/sha/asm/sha256-armv4.pl
new file mode 100644
index 000000000..55d30cba3
--- /dev/null
+++ b/crypto/sha/asm/sha256-armv4.pl
@@ -0,0 +1,732 @@
+#! /usr/bin/env perl
+# Copyright 2007-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+
+# ====================================================================
+# Written by Andy Polyakov  for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+#
+# Permission to use under GPL terms is granted.
+# ====================================================================
+
+# SHA256 block procedure for ARMv4. May 2007.
+
+# Performance is ~2x better than gcc 3.4 generated code and in "abso-
+# lute" terms is ~2250 cycles per 64-byte block or ~35 cycles per
+# byte [on single-issue Xscale PXA250 core].
+
+# July 2010.
+#
+# Rescheduling for dual-issue pipeline resulted in 22% improvement on
+# Cortex A8 core and ~20 cycles per processed byte.
+
+# February 2011.
+#
+# Profiler-assisted and platform-specific optimization resulted in 16%
+# improvement on Cortex A8 core and ~15.4 cycles per processed byte.
+
+# September 2013.
+#
+# Add NEON implementation. On Cortex A8 it was measured to process one
+# byte in 12.5 cycles or 23% faster than integer-only code. Snapdragon
+# S4 does it in 12.5 cycles too, but it's 50% faster than integer-only
+# code (meaning that latter performs sub-optimally, nothing was done
+# about it).
+
+# May 2014.
+#
+# Add ARMv8 code path performing at 2.0 cpb on Apple A7.
+
+$flavour = shift;
+if ($flavour=~/\w[\w\-]*\.\w+$/) { $output=$flavour; undef $flavour; }
+else { while (($output=shift) && ($output!~/\w[\w\-]*\.\w+$/)) {} }
+
+if ($flavour && $flavour ne "void") {
+    $0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+    ( $xlate="${dir}arm-xlate.pl" and -f $xlate ) or
+    ( $xlate="${dir}../../perlasm/arm-xlate.pl" and -f $xlate) or
+    die "can't locate arm-xlate.pl";
+
+    open STDOUT,"| \"$^X\" $xlate $flavour $output";
+} else {
+    open STDOUT,">$output";
+}
+
+$ctx="r0";	$t0="r0";
+$inp="r1";	$t4="r1";
+$len="r2";	$t1="r2";
+$T1="r3";	$t3="r3";
+$A="r4";
+$B="r5";
+$C="r6";
+$D="r7";
+$E="r8";
+$F="r9";
+$G="r10";
+$H="r11";
+@V=($A,$B,$C,$D,$E,$F,$G,$H);
+$t2="r12";
+$Ktbl="r14";
+
+@Sigma0=( 2,13,22);
+@Sigma1=( 6,11,25);
+@sigma0=( 7,18, 3);
+@sigma1=(17,19,10);
+
+sub BODY_00_15 {
+my ($i,$a,$b,$c,$d,$e,$f,$g,$h) = @_;
+
+$code.=<<___ if ($i<16);
+#if __ARM_ARCH__>=7
+	@ ldr	$t1,[$inp],#4			@ $i
+# if $i==15
+	str	$inp,[sp,#17*4]			@ make room for $t4
+# endif
+	eor	$t0,$e,$e,ror#`$Sigma1[1]-$Sigma1[0]`
+	add	$a,$a,$t2			@ h+=Maj(a,b,c) from the past
+	eor	$t0,$t0,$e,ror#`$Sigma1[2]-$Sigma1[0]`	@ Sigma1(e)
+# ifndef __ARMEB__
+	rev	$t1,$t1
+# endif
+#else
+	@ ldrb	$t1,[$inp,#3]			@ $i
+	add	$a,$a,$t2			@ h+=Maj(a,b,c) from the past
+	ldrb	$t2,[$inp,#2]
+	ldrb	$t0,[$inp,#1]
+	orr	$t1,$t1,$t2,lsl#8
+	ldrb	$t2,[$inp],#4
+	orr	$t1,$t1,$t0,lsl#16
+# if $i==15
+	str	$inp,[sp,#17*4]			@ make room for $t4
+# endif
+	eor	$t0,$e,$e,ror#`$Sigma1[1]-$Sigma1[0]`
+	orr	$t1,$t1,$t2,lsl#24
+	eor	$t0,$t0,$e,ror#`$Sigma1[2]-$Sigma1[0]`	@ Sigma1(e)
+#endif
+___
+$code.=<<___;
+	ldr	$t2,[$Ktbl],#4			@ *K256++
+	add	$h,$h,$t1			@ h+=X[i]
+	str	$t1,[sp,#`$i%16`*4]
+	eor	$t1,$f,$g
+	add	$h,$h,$t0,ror#$Sigma1[0]	@ h+=Sigma1(e)
+	and	$t1,$t1,$e
+	add	$h,$h,$t2			@ h+=K256[i]
+	eor	$t1,$t1,$g			@ Ch(e,f,g)
+	eor	$t0,$a,$a,ror#`$Sigma0[1]-$Sigma0[0]`
+	add	$h,$h,$t1			@ h+=Ch(e,f,g)
+#if $i==31
+	and	$t2,$t2,#0xff
+	cmp	$t2,#0xf2			@ done?
+#endif
+#if $i<15
+# if __ARM_ARCH__>=7
+	ldr	$t1,[$inp],#4			@ prefetch
+# else
+	ldrb	$t1,[$inp,#3]
+# endif
+	eor	$t2,$a,$b			@ a^b, b^c in next round
+#else
+	ldr	$t1,[sp,#`($i+2)%16`*4]		@ from future BODY_16_xx
+	eor	$t2,$a,$b			@ a^b, b^c in next round
+	ldr	$t4,[sp,#`($i+15)%16`*4]	@ from future BODY_16_xx
+#endif
+	eor	$t0,$t0,$a,ror#`$Sigma0[2]-$Sigma0[0]`	@ Sigma0(a)
+	and	$t3,$t3,$t2			@ (b^c)&=(a^b)
+	add	$d,$d,$h			@ d+=h
+	eor	$t3,$t3,$b			@ Maj(a,b,c)
+	add	$h,$h,$t0,ror#$Sigma0[0]	@ h+=Sigma0(a)
+	@ add	$h,$h,$t3			@ h+=Maj(a,b,c)
+___
+	($t2,$t3)=($t3,$t2);
+}
+
+sub BODY_16_XX {
+my ($i,$a,$b,$c,$d,$e,$f,$g,$h) = @_;
+
+$code.=<<___;
+	@ ldr	$t1,[sp,#`($i+1)%16`*4]		@ $i
+	@ ldr	$t4,[sp,#`($i+14)%16`*4]
+	mov	$t0,$t1,ror#$sigma0[0]
+	add	$a,$a,$t2			@ h+=Maj(a,b,c) from the past
+	mov	$t2,$t4,ror#$sigma1[0]
+	eor	$t0,$t0,$t1,ror#$sigma0[1]
+	eor	$t2,$t2,$t4,ror#$sigma1[1]
+	eor	$t0,$t0,$t1,lsr#$sigma0[2]	@ sigma0(X[i+1])
+	ldr	$t1,[sp,#`($i+0)%16`*4]
+	eor	$t2,$t2,$t4,lsr#$sigma1[2]	@ sigma1(X[i+14])
+	ldr	$t4,[sp,#`($i+9)%16`*4]
+
+	add	$t2,$t2,$t0
+	eor	$t0,$e,$e,ror#`$Sigma1[1]-$Sigma1[0]`	@ from BODY_00_15
+	add	$t1,$t1,$t2
+	eor	$t0,$t0,$e,ror#`$Sigma1[2]-$Sigma1[0]`	@ Sigma1(e)
+	add	$t1,$t1,$t4			@ X[i]
+___
+	&BODY_00_15(@_);
+}
+
+$code=<<___;
+#ifndef __KERNEL__
+# include "arm_arch.h"
+#else
+# define __ARM_ARCH__ __LINUX_ARM_ARCH__
+# define __ARM_MAX_ARCH__ 7
+#endif
+
+.text
+#if defined(__thumb2__)
+.syntax unified
+.thumb
+#else
+.code   32
+#endif
+
+.type	K256,%object
+.align	5
+K256:
+.word	0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5
+.word	0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5
+.word	0xd807aa98,0x12835b01,0x243185be,0x550c7dc3
+.word	0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174
+.word	0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc
+.word	0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da
+.word	0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7
+.word	0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967
+.word	0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13
+.word	0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85
+.word	0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3
+.word	0xd192e819,0xd6990624,0xf40e3585,0x106aa070
+.word	0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5
+.word	0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3
+.word	0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208
+.word	0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2
+.size	K256,.-K256
+.word	0				@ terminator
+#if __ARM_MAX_ARCH__>=7 && !defined(__KERNEL__)
+.LOPENSSL_armcap:
+.word	OPENSSL_armcap_P-.Lsha256_block_data_order
+#endif
+.align	5
+
+.global	sha256_block_data_order
+.type	sha256_block_data_order,%function
+sha256_block_data_order:
+.Lsha256_block_data_order:
+#if __ARM_ARCH__<7 && !defined(__thumb2__)
+	sub	r3,pc,#8		@ sha256_block_data_order
+#else
+	adr	r3,.Lsha256_block_data_order
+#endif
+#if __ARM_MAX_ARCH__>=7 && !defined(__KERNEL__)
+	ldr	r12,.LOPENSSL_armcap
+	ldr	r12,[r3,r12]		@ OPENSSL_armcap_P
+#ifdef	__APPLE__
+	ldr	r12,[r12]
+#endif
+	tst	r12,#ARMV8_SHA256
+	bne	.LARMv8
+	tst	r12,#ARMV7_NEON
+	bne	.LNEON
+#endif
+	add	$len,$inp,$len,lsl#6	@ len to point at the end of inp
+	stmdb	sp!,{$ctx,$inp,$len,r4-r11,lr}
+	ldmia	$ctx,{$A,$B,$C,$D,$E,$F,$G,$H}
+	sub	$Ktbl,r3,#256+32	@ K256
+	sub	sp,sp,#16*4		@ alloca(X[16])
+.Loop:
+# if __ARM_ARCH__>=7
+	ldr	$t1,[$inp],#4
+# else
+	ldrb	$t1,[$inp,#3]
+# endif
+	eor	$t3,$B,$C		@ magic
+	eor	$t2,$t2,$t2
+___
+for($i=0;$i<16;$i++)	{ &BODY_00_15($i,@V); unshift(@V,pop(@V)); }
+$code.=".Lrounds_16_xx:\n";
+for (;$i<32;$i++)	{ &BODY_16_XX($i,@V); unshift(@V,pop(@V)); }
+$code.=<<___;
+#if __ARM_ARCH__>=7
+	ite	eq			@ Thumb2 thing, sanity check in ARM
+#endif
+	ldreq	$t3,[sp,#16*4]		@ pull ctx
+	bne	.Lrounds_16_xx
+
+	add	$A,$A,$t2		@ h+=Maj(a,b,c) from the past
+	ldr	$t0,[$t3,#0]
+	ldr	$t1,[$t3,#4]
+	ldr	$t2,[$t3,#8]
+	add	$A,$A,$t0
+	ldr	$t0,[$t3,#12]
+	add	$B,$B,$t1
+	ldr	$t1,[$t3,#16]
+	add	$C,$C,$t2
+	ldr	$t2,[$t3,#20]
+	add	$D,$D,$t0
+	ldr	$t0,[$t3,#24]
+	add	$E,$E,$t1
+	ldr	$t1,[$t3,#28]
+	add	$F,$F,$t2
+	ldr	$inp,[sp,#17*4]		@ pull inp
+	ldr	$t2,[sp,#18*4]		@ pull inp+len
+	add	$G,$G,$t0
+	add	$H,$H,$t1
+	stmia	$t3,{$A,$B,$C,$D,$E,$F,$G,$H}
+	cmp	$inp,$t2
+	sub	$Ktbl,$Ktbl,#256	@ rewind Ktbl
+	bne	.Loop
+
+	add	sp,sp,#`16+3`*4	@ destroy frame
+#if __ARM_ARCH__>=5
+	ldmia	sp!,{r4-r11,pc}
+#else
+	ldmia	sp!,{r4-r11,lr}
+	tst	lr,#1
+	moveq	pc,lr			@ be binary compatible with V4, yet
+	bx	lr			@ interoperable with Thumb ISA:-)
+#endif
+.size	sha256_block_data_order,.-sha256_block_data_order
+___
+######################################################################
+# NEON stuff
+#
+{{{
+my @X=map("q$_",(0..3));
+my ($T0,$T1,$T2,$T3,$T4,$T5)=("q8","q9","q10","q11","d24","d25");
+my $Xfer=$t4;
+my $j=0;
+
+sub Dlo()   { shift=~m|q([1]?[0-9])|?"d".($1*2):"";     }
+sub Dhi()   { shift=~m|q([1]?[0-9])|?"d".($1*2+1):"";   }
+
+sub AUTOLOAD()          # thunk [simplified] x86-style perlasm
+{ my $opcode = $AUTOLOAD; $opcode =~ s/.*:://; $opcode =~ s/_/\./;
+  my $arg = pop;
+    $arg = "#$arg" if ($arg*1 eq $arg);
+    $code .= "\t$opcode\t".join(',',@_,$arg)."\n";
+}
+
+sub Xupdate()
+{ use integer;
+  my $body = shift;
+  my @insns = (&$body,&$body,&$body,&$body);
+  my ($a,$b,$c,$d,$e,$f,$g,$h);
+
+	&vext_8		($T0,@X[0],@X[1],4);	# X[1..4]
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&vext_8		($T1,@X[2],@X[3],4);	# X[9..12]
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&vshr_u32	($T2,$T0,$sigma0[0]);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&vadd_i32	(@X[0],@X[0],$T1);	# X[0..3] += X[9..12]
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&vshr_u32	($T1,$T0,$sigma0[2]);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&vsli_32	($T2,$T0,32-$sigma0[0]);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&vshr_u32	($T3,$T0,$sigma0[1]);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&veor		($T1,$T1,$T2);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&vsli_32	($T3,$T0,32-$sigma0[1]);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	  &vshr_u32	($T4,&Dhi(@X[3]),$sigma1[0]);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&veor		($T1,$T1,$T3);		# sigma0(X[1..4])
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	  &vsli_32	($T4,&Dhi(@X[3]),32-$sigma1[0]);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	  &vshr_u32	($T5,&Dhi(@X[3]),$sigma1[2]);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&vadd_i32	(@X[0],@X[0],$T1);	# X[0..3] += sigma0(X[1..4])
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	  &veor		($T5,$T5,$T4);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	  &vshr_u32	($T4,&Dhi(@X[3]),$sigma1[1]);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	  &vsli_32	($T4,&Dhi(@X[3]),32-$sigma1[1]);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	  &veor		($T5,$T5,$T4);		# sigma1(X[14..15])
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&vadd_i32	(&Dlo(@X[0]),&Dlo(@X[0]),$T5);# X[0..1] += sigma1(X[14..15])
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	  &vshr_u32	($T4,&Dlo(@X[0]),$sigma1[0]);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	  &vsli_32	($T4,&Dlo(@X[0]),32-$sigma1[0]);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	  &vshr_u32	($T5,&Dlo(@X[0]),$sigma1[2]);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	  &veor		($T5,$T5,$T4);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	  &vshr_u32	($T4,&Dlo(@X[0]),$sigma1[1]);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&vld1_32	("{$T0}","[$Ktbl,:128]!");
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	  &vsli_32	($T4,&Dlo(@X[0]),32-$sigma1[1]);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	  &veor		($T5,$T5,$T4);		# sigma1(X[16..17])
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&vadd_i32	(&Dhi(@X[0]),&Dhi(@X[0]),$T5);# X[2..3] += sigma1(X[16..17])
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&vadd_i32	($T0,$T0,@X[0]);
+	 while($#insns>=2) { eval(shift(@insns)); }
+	&vst1_32	("{$T0}","[$Xfer,:128]!");
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+
+	push(@X,shift(@X));		# "rotate" X[]
+}
+
+sub Xpreload()
+{ use integer;
+  my $body = shift;
+  my @insns = (&$body,&$body,&$body,&$body);
+  my ($a,$b,$c,$d,$e,$f,$g,$h);
+
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&vld1_32	("{$T0}","[$Ktbl,:128]!");
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&vrev32_8	(@X[0],@X[0]);
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&vadd_i32	($T0,$T0,@X[0]);
+	 foreach (@insns) { eval; }	# remaining instructions
+	&vst1_32	("{$T0}","[$Xfer,:128]!");
+
+	push(@X,shift(@X));		# "rotate" X[]
+}
+
+sub body_00_15 () {
+	(
+	'($a,$b,$c,$d,$e,$f,$g,$h)=@V;'.
+	'&add	($h,$h,$t1)',			# h+=X[i]+K[i]
+	'&eor	($t1,$f,$g)',
+	'&eor	($t0,$e,$e,"ror#".($Sigma1[1]-$Sigma1[0]))',
+	'&add	($a,$a,$t2)',			# h+=Maj(a,b,c) from the past
+	'&and	($t1,$t1,$e)',
+	'&eor	($t2,$t0,$e,"ror#".($Sigma1[2]-$Sigma1[0]))',	# Sigma1(e)
+	'&eor	($t0,$a,$a,"ror#".($Sigma0[1]-$Sigma0[0]))',
+	'&eor	($t1,$t1,$g)',			# Ch(e,f,g)
+	'&add	($h,$h,$t2,"ror#$Sigma1[0]")',	# h+=Sigma1(e)
+	'&eor	($t2,$a,$b)',			# a^b, b^c in next round
+	'&eor	($t0,$t0,$a,"ror#".($Sigma0[2]-$Sigma0[0]))',	# Sigma0(a)
+	'&add	($h,$h,$t1)',			# h+=Ch(e,f,g)
+	'&ldr	($t1,sprintf "[sp,#%d]",4*(($j+1)&15))	if (($j&15)!=15);'.
+	'&ldr	($t1,"[$Ktbl]")				if ($j==15);'.
+	'&ldr	($t1,"[sp,#64]")			if ($j==31)',
+	'&and	($t3,$t3,$t2)',			# (b^c)&=(a^b)
+	'&add	($d,$d,$h)',			# d+=h
+	'&add	($h,$h,$t0,"ror#$Sigma0[0]");'.	# h+=Sigma0(a)
+	'&eor	($t3,$t3,$b)',			# Maj(a,b,c)
+	'$j++;	unshift(@V,pop(@V)); ($t2,$t3)=($t3,$t2);'
+	)
+}
+
+$code.=<<___;
+#if __ARM_MAX_ARCH__>=7
+.arch	armv7-a
+.fpu	neon
+
+.global	sha256_block_data_order_neon
+.type	sha256_block_data_order_neon,%function
+.align	5
+.skip	16
+sha256_block_data_order_neon:
+.LNEON:
+	stmdb	sp!,{r4-r12,lr}
+
+	sub	$H,sp,#16*4+16
+	adr	$Ktbl,K256
+	bic	$H,$H,#15		@ align for 128-bit stores
+	mov	$t2,sp
+	mov	sp,$H			@ alloca
+	add	$len,$inp,$len,lsl#6	@ len to point at the end of inp
+
+	vld1.8		{@X[0]},[$inp]!
+	vld1.8		{@X[1]},[$inp]!
+	vld1.8		{@X[2]},[$inp]!
+	vld1.8		{@X[3]},[$inp]!
+	vld1.32		{$T0},[$Ktbl,:128]!
+	vld1.32		{$T1},[$Ktbl,:128]!
+	vld1.32		{$T2},[$Ktbl,:128]!
+	vld1.32		{$T3},[$Ktbl,:128]!
+	vrev32.8	@X[0],@X[0]		@ yes, even on
+	str		$ctx,[sp,#64]
+	vrev32.8	@X[1],@X[1]		@ big-endian
+	str		$inp,[sp,#68]
+	mov		$Xfer,sp
+	vrev32.8	@X[2],@X[2]
+	str		$len,[sp,#72]
+	vrev32.8	@X[3],@X[3]
+	str		$t2,[sp,#76]		@ save original sp
+	vadd.i32	$T0,$T0,@X[0]
+	vadd.i32	$T1,$T1,@X[1]
+	vst1.32		{$T0},[$Xfer,:128]!
+	vadd.i32	$T2,$T2,@X[2]
+	vst1.32		{$T1},[$Xfer,:128]!
+	vadd.i32	$T3,$T3,@X[3]
+	vst1.32		{$T2},[$Xfer,:128]!
+	vst1.32		{$T3},[$Xfer,:128]!
+
+	ldmia		$ctx,{$A-$H}
+	sub		$Xfer,$Xfer,#64
+	ldr		$t1,[sp,#0]
+	eor		$t2,$t2,$t2
+	eor		$t3,$B,$C
+	b		.L_00_48
+
+.align	4
+.L_00_48:
+___
+	&Xupdate(\&body_00_15);
+	&Xupdate(\&body_00_15);
+	&Xupdate(\&body_00_15);
+	&Xupdate(\&body_00_15);
+$code.=<<___;
+	teq	$t1,#0				@ check for K256 terminator
+	ldr	$t1,[sp,#0]
+	sub	$Xfer,$Xfer,#64
+	bne	.L_00_48
+
+	ldr		$inp,[sp,#68]
+	ldr		$t0,[sp,#72]
+	sub		$Ktbl,$Ktbl,#256	@ rewind $Ktbl
+	teq		$inp,$t0
+	it		eq
+	subeq		$inp,$inp,#64		@ avoid SEGV
+	vld1.8		{@X[0]},[$inp]!		@ load next input block
+	vld1.8		{@X[1]},[$inp]!
+	vld1.8		{@X[2]},[$inp]!
+	vld1.8		{@X[3]},[$inp]!
+	it		ne
+	strne		$inp,[sp,#68]
+	mov		$Xfer,sp
+___
+	&Xpreload(\&body_00_15);
+	&Xpreload(\&body_00_15);
+	&Xpreload(\&body_00_15);
+	&Xpreload(\&body_00_15);
+$code.=<<___;
+	ldr	$t0,[$t1,#0]
+	add	$A,$A,$t2			@ h+=Maj(a,b,c) from the past
+	ldr	$t2,[$t1,#4]
+	ldr	$t3,[$t1,#8]
+	ldr	$t4,[$t1,#12]
+	add	$A,$A,$t0			@ accumulate
+	ldr	$t0,[$t1,#16]
+	add	$B,$B,$t2
+	ldr	$t2,[$t1,#20]
+	add	$C,$C,$t3
+	ldr	$t3,[$t1,#24]
+	add	$D,$D,$t4
+	ldr	$t4,[$t1,#28]
+	add	$E,$E,$t0
+	str	$A,[$t1],#4
+	add	$F,$F,$t2
+	str	$B,[$t1],#4
+	add	$G,$G,$t3
+	str	$C,[$t1],#4
+	add	$H,$H,$t4
+	str	$D,[$t1],#4
+	stmia	$t1,{$E-$H}
+
+	ittte	ne
+	movne	$Xfer,sp
+	ldrne	$t1,[sp,#0]
+	eorne	$t2,$t2,$t2
+	ldreq	sp,[sp,#76]			@ restore original sp
+	itt	ne
+	eorne	$t3,$B,$C
+	bne	.L_00_48
+
+	ldmia	sp!,{r4-r12,pc}
+.size	sha256_block_data_order_neon,.-sha256_block_data_order_neon
+#endif
+___
+}}}
+######################################################################
+# ARMv8 stuff
+#
+{{{
+my ($ABCD,$EFGH,$abcd)=map("q$_",(0..2));
+my @MSG=map("q$_",(8..11));
+my ($W0,$W1,$ABCD_SAVE,$EFGH_SAVE)=map("q$_",(12..15));
+my $Ktbl="r3";
+
+$code.=<<___;
+#if __ARM_MAX_ARCH__>=7 && !defined(__KERNEL__)
+
+# if defined(__thumb2__)
+#  define INST(a,b,c,d)	.byte	c,d|0xc,a,b
+# else
+#  define INST(a,b,c,d)	.byte	a,b,c,d
+# endif
+
+.type	sha256_block_data_order_armv8,%function
+.align	5
+sha256_block_data_order_armv8:
+.LARMv8:
+	vld1.32	{$ABCD,$EFGH},[$ctx]
+	sub	$Ktbl,$Ktbl,#256+32
+	add	$len,$inp,$len,lsl#6	@ len to point at the end of inp
+	b	.Loop_v8
+
+.align	4
+.Loop_v8:
+	vld1.8		{@MSG[0]-@MSG[1]},[$inp]!
+	vld1.8		{@MSG[2]-@MSG[3]},[$inp]!
+	vld1.32		{$W0},[$Ktbl]!
+	vrev32.8	@MSG[0],@MSG[0]
+	vrev32.8	@MSG[1],@MSG[1]
+	vrev32.8	@MSG[2],@MSG[2]
+	vrev32.8	@MSG[3],@MSG[3]
+	vmov		$ABCD_SAVE,$ABCD	@ offload
+	vmov		$EFGH_SAVE,$EFGH
+	teq		$inp,$len
+___
+for($i=0;$i<12;$i++) {
+$code.=<<___;
+	vld1.32		{$W1},[$Ktbl]!
+	vadd.i32	$W0,$W0,@MSG[0]
+	sha256su0	@MSG[0],@MSG[1]
+	vmov		$abcd,$ABCD
+	sha256h		$ABCD,$EFGH,$W0
+	sha256h2	$EFGH,$abcd,$W0
+	sha256su1	@MSG[0],@MSG[2],@MSG[3]
+___
+	($W0,$W1)=($W1,$W0);	push(@MSG,shift(@MSG));
+}
+$code.=<<___;
+	vld1.32		{$W1},[$Ktbl]!
+	vadd.i32	$W0,$W0,@MSG[0]
+	vmov		$abcd,$ABCD
+	sha256h		$ABCD,$EFGH,$W0
+	sha256h2	$EFGH,$abcd,$W0
+
+	vld1.32		{$W0},[$Ktbl]!
+	vadd.i32	$W1,$W1,@MSG[1]
+	vmov		$abcd,$ABCD
+	sha256h		$ABCD,$EFGH,$W1
+	sha256h2	$EFGH,$abcd,$W1
+
+	vld1.32		{$W1},[$Ktbl]
+	vadd.i32	$W0,$W0,@MSG[2]
+	sub		$Ktbl,$Ktbl,#256-16	@ rewind
+	vmov		$abcd,$ABCD
+	sha256h		$ABCD,$EFGH,$W0
+	sha256h2	$EFGH,$abcd,$W0
+
+	vadd.i32	$W1,$W1,@MSG[3]
+	vmov		$abcd,$ABCD
+	sha256h		$ABCD,$EFGH,$W1
+	sha256h2	$EFGH,$abcd,$W1
+
+	vadd.i32	$ABCD,$ABCD,$ABCD_SAVE
+	vadd.i32	$EFGH,$EFGH,$EFGH_SAVE
+	it		ne
+	bne		.Loop_v8
+
+	vst1.32		{$ABCD,$EFGH},[$ctx]
+
+	ret		@ bx lr
+.size	sha256_block_data_order_armv8,.-sha256_block_data_order_armv8
+#endif
+___
+}}}
+$code.=<<___;
+.asciz  "SHA256 block transform for ARMv4/NEON/ARMv8, CRYPTOGAMS by "
+.align	2
+#if __ARM_MAX_ARCH__>=7 && !defined(__KERNEL__)
+.comm   OPENSSL_armcap_P,4,4
+#endif
+___
+
+open SELF,$0;
+while() {
+	next if (/^#!/);
+	last if (!s/^#/@/ and !/^$/);
+	print;
+}
+close SELF;
+
+{   my  %opcode = (
+	"sha256h"	=> 0xf3000c40,	"sha256h2"	=> 0xf3100c40,
+	"sha256su0"	=> 0xf3ba03c0,	"sha256su1"	=> 0xf3200c40	);
+
+    sub unsha256 {
+	my ($mnemonic,$arg)=@_;
+
+	if ($arg =~ m/q([0-9]+)(?:,\s*q([0-9]+))?,\s*q([0-9]+)/o) {
+	    my $word = $opcode{$mnemonic}|(($1&7)<<13)|(($1&8)<<19)
+					 |(($2&7)<<17)|(($2&8)<<4)
+					 |(($3&7)<<1) |(($3&8)<<2);
+	    # since ARMv7 instructions are always encoded little-endian.
+	    # correct solution is to use .inst directive, but older
+	    # assemblers don't implement it:-(
+	    sprintf "INST(0x%02x,0x%02x,0x%02x,0x%02x)\t@ %s %s",
+			$word&0xff,($word>>8)&0xff,
+			($word>>16)&0xff,($word>>24)&0xff,
+			$mnemonic,$arg;
+	}
+    }
+}
+
+foreach (split($/,$code)) {
+
+	s/\`([^\`]*)\`/eval $1/geo;
+
+	s/\b(sha256\w+)\s+(q.*)/unsha256($1,$2)/geo;
+
+	s/\bret\b/bx	lr/go		or
+	s/\bbx\s+lr\b/.word\t0xe12fff1e/go;	# make it possible to compile with -march=armv4
+
+	print $_,"\n";
+}
+
+close STDOUT; # enforce flush
diff --git a/crypto/sha/asm/sha256-c64xplus.pl b/crypto/sha/asm/sha256-c64xplus.pl
new file mode 100644
index 000000000..3ab7d9b68
--- /dev/null
+++ b/crypto/sha/asm/sha256-c64xplus.pl
@@ -0,0 +1,320 @@
+#! /usr/bin/env perl
+# Copyright 2012-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+#
+# ====================================================================
+# Written by Andy Polyakov  for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+#
+# SHA256 for C64x+.
+#
+# January 2012
+#
+# Performance is just below 10 cycles per processed byte, which is
+# almost 40% faster than compiler-generated code. Unroll is unlikely
+# to give more than ~8% improvement...
+#
+# !!! Note that this module uses AMR, which means that all interrupt
+# service routines are expected to preserve it and for own well-being
+# zero it upon entry.
+
+while (($output=shift) && ($output!~/\w[\w\-]*\.\w+$/)) {}
+open STDOUT,">$output";
+
+($CTXA,$INP,$NUM) = ("A4","B4","A6");            # arguments
+ $K256="A3";
+
+($A,$Actx,$B,$Bctx,$C,$Cctx,$D,$Dctx,$T2,$S0,$s1,$t0a,$t1a,$t2a,$X9,$X14)
+	=map("A$_",(16..31));
+($E,$Ectx,$F,$Fctx,$G,$Gctx,$H,$Hctx,$T1,$S1,$s0,$t0e,$t1e,$t2e,$X1,$X15)
+	=map("B$_",(16..31));
+
+($Xia,$Xib)=("A5","B5");			# circular/ring buffer
+ $CTXB=$t2e;
+
+($Xn,$X0,$K)=("B7","B8","B9");
+($Maj,$Ch)=($T2,"B6");
+
+$code.=<<___;
+	.text
+
+	.if	.ASSEMBLER_VERSION<7000000
+	.asg	0,__TI_EABI__
+	.endif
+	.if	__TI_EABI__
+	.nocmp
+	.asg	sha256_block_data_order,_sha256_block_data_order
+	.endif
+
+	.asg	B3,RA
+	.asg	A15,FP
+	.asg	B15,SP
+
+	.if	.BIG_ENDIAN
+	.asg	SWAP2,MV
+	.asg	SWAP4,MV
+	.endif
+
+	.global	_sha256_block_data_order
+_sha256_block_data_order:
+__sha256_block:
+	.asmfunc stack_usage(64)
+	MV	$NUM,A0				; reassign $NUM
+||	MVK	-64,B0
+  [!A0]	BNOP	RA				; if ($NUM==0) return;
+|| [A0]	STW	FP,*SP--[16]			; save frame pointer and alloca(64)
+|| [A0]	MV	SP,FP
+   [A0]	ADDKPC	__sha256_block,B2
+|| [A0]	AND	B0,SP,SP			; align stack at 64 bytes
+	.if	__TI_EABI__
+   [A0]	MVK	0x00404,B1
+|| [A0]	MVKL	\$PCR_OFFSET(K256,__sha256_block),$K256
+   [A0]	MVKH	0x50000,B1
+|| [A0]	MVKH	\$PCR_OFFSET(K256,__sha256_block),$K256
+	.else
+   [A0]	MVK	0x00404,B1
+|| [A0]	MVKL	(K256-__sha256_block),$K256
+   [A0]	MVKH	0x50000,B1
+|| [A0]	MVKH	(K256-__sha256_block),$K256
+	.endif
+   [A0]	MVC	B1,AMR				; setup circular addressing
+|| [A0]	MV	SP,$Xia
+   [A0]	MV	SP,$Xib
+|| [A0]	ADD	B2,$K256,$K256
+|| [A0]	MV	$CTXA,$CTXB
+|| [A0]	SUBAW	SP,2,SP				; reserve two words above buffer
+	LDW	*${CTXA}[0],$A			; load ctx
+||	LDW	*${CTXB}[4],$E
+	LDW	*${CTXA}[1],$B
+||	LDW	*${CTXB}[5],$F
+	LDW	*${CTXA}[2],$C
+||	LDW	*${CTXB}[6],$G
+	LDW	*${CTXA}[3],$D
+||	LDW	*${CTXB}[7],$H
+
+	LDNW	*$INP++,$Xn			; pre-fetch input
+	LDW	*$K256++,$K			; pre-fetch K256[0]
+	MVK	14,B0				; loop counters
+	MVK	47,B1
+||	ADDAW	$Xia,9,$Xia
+outerloop?:
+	SUB	A0,1,A0
+||	MV	$A,$Actx
+||	MV	$E,$Ectx
+||	MVD	$B,$Bctx
+||	MVD	$F,$Fctx
+	MV	$C,$Cctx
+||	MV	$G,$Gctx
+||	MVD	$D,$Dctx
+||	MVD	$H,$Hctx
+||	SWAP4	$Xn,$X0
+
+	SPLOOPD	8				; BODY_00_14
+||	MVC	B0,ILC
+||	SWAP2	$X0,$X0
+
+	LDNW	*$INP++,$Xn
+||	ROTL	$A,30,$S0
+||	OR	$A,$B,$Maj
+||	AND	$A,$B,$t2a
+||	ROTL	$E,26,$S1
+||	AND	$F,$E,$Ch
+||	ANDN	$G,$E,$t2e
+	ROTL	$A,19,$t0a
+||	AND	$C,$Maj,$Maj
+||	ROTL	$E,21,$t0e
+||	XOR	$t2e,$Ch,$Ch			; Ch(e,f,g) = (e&f)^(~e&g)
+	ROTL	$A,10,$t1a
+||	OR	$t2a,$Maj,$Maj			; Maj(a,b,c) = ((a|b)&c)|(a&b)
+||	ROTL	$E,7,$t1e
+||	ADD	$K,$H,$T1			; T1 = h + K256[i]
+	ADD	$X0,$T1,$T1			; T1 += X[i];
+||	STW	$X0,*$Xib++
+||	XOR	$t0a,$S0,$S0
+||	XOR	$t0e,$S1,$S1
+	XOR	$t1a,$S0,$S0			; Sigma0(a)
+||	XOR	$t1e,$S1,$S1			; Sigma1(e)
+||	LDW	*$K256++,$K			; pre-fetch K256[i+1]
+||	ADD	$Ch,$T1,$T1			; T1 += Ch(e,f,g)
+	ADD	$S1,$T1,$T1			; T1 += Sigma1(e)
+||	ADD	$S0,$Maj,$T2			; T2 = Sigma0(a) + Maj(a,b,c)
+||	ROTL	$G,0,$H				; h = g
+||	MV	$F,$G				; g = f
+||	MV	$X0,$X14
+||	SWAP4	$Xn,$X0
+	SWAP2	$X0,$X0
+||	MV	$E,$F				; f = e
+||	ADD	$D,$T1,$E			; e = d + T1
+||	MV	$C,$D				; d = c
+	MV	$B,$C				; c = b
+||	MV	$A,$B				; b = a
+||	ADD	$T1,$T2,$A			; a = T1 + T2
+	SPKERNEL
+
+	ROTL	$A,30,$S0			; BODY_15
+||	OR	$A,$B,$Maj
+||	AND	$A,$B,$t2a
+||	ROTL	$E,26,$S1
+||	AND	$F,$E,$Ch
+||	ANDN	$G,$E,$t2e
+||	LDW	*${Xib}[1],$Xn			; modulo-scheduled
+	ROTL	$A,19,$t0a
+||	AND	$C,$Maj,$Maj
+||	ROTL	$E,21,$t0e
+||	XOR	$t2e,$Ch,$Ch			; Ch(e,f,g) = (e&f)^(~e&g)
+||	LDW	*${Xib}[2],$X1			; modulo-scheduled
+	ROTL	$A,10,$t1a
+||	OR	$t2a,$Maj,$Maj			; Maj(a,b,c) = ((a|b)&c)|(a&b)
+||	ROTL	$E,7,$t1e
+||	ADD	$K,$H,$T1			; T1 = h + K256[i]
+	ADD	$X0,$T1,$T1			; T1 += X[i];
+||	STW	$X0,*$Xib++
+||	XOR	$t0a,$S0,$S0
+||	XOR	$t0e,$S1,$S1
+	XOR	$t1a,$S0,$S0			; Sigma0(a)
+||	XOR	$t1e,$S1,$S1			; Sigma1(e)
+||	LDW	*$K256++,$K			; pre-fetch K256[i+1]
+||	ADD	$Ch,$T1,$T1			; T1 += Ch(e,f,g)
+	ADD	$S1,$T1,$T1			; T1 += Sigma1(e)
+||	ADD	$S0,$Maj,$T2			; T2 = Sigma0(a) + Maj(a,b,c)
+||	ROTL	$G,0,$H				; h = g
+||	MV	$F,$G				; g = f
+||	MV	$X0,$X15
+	MV	$E,$F				; f = e
+||	ADD	$D,$T1,$E			; e = d + T1
+||	MV	$C,$D				; d = c
+||	MV	$Xn,$X0				; modulo-scheduled
+||	LDW	*$Xia,$X9			; modulo-scheduled
+||	ROTL	$X1,25,$t0e			; modulo-scheduled
+||	ROTL	$X14,15,$t0a			; modulo-scheduled
+	SHRU	$X1,3,$s0			; modulo-scheduled
+||	SHRU	$X14,10,$s1			; modulo-scheduled
+||	ROTL	$B,0,$C				; c = b
+||	MV	$A,$B				; b = a
+||	ADD	$T1,$T2,$A			; a = T1 + T2
+
+	SPLOOPD	10				; BODY_16_63
+||	MVC	B1,ILC
+||	ROTL	$X1,14,$t1e			; modulo-scheduled
+||	ROTL	$X14,13,$t1a			; modulo-scheduled
+
+	XOR	$t0e,$s0,$s0
+||	XOR	$t0a,$s1,$s1
+||	MV	$X15,$X14
+||	MV	$X1,$Xn
+	XOR	$t1e,$s0,$s0			; sigma0(X[i+1])
+||	XOR	$t1a,$s1,$s1			; sigma1(X[i+14])
+||	LDW	*${Xib}[2],$X1			; module-scheduled
+	ROTL	$A,30,$S0
+||	OR	$A,$B,$Maj
+||	AND	$A,$B,$t2a
+||	ROTL	$E,26,$S1
+||	AND	$F,$E,$Ch
+||	ANDN	$G,$E,$t2e
+||	ADD	$X9,$X0,$X0			; X[i] += X[i+9]
+	ROTL	$A,19,$t0a
+||	AND	$C,$Maj,$Maj
+||	ROTL	$E,21,$t0e
+||	XOR	$t2e,$Ch,$Ch			; Ch(e,f,g) = (e&f)^(~e&g)
+||	ADD	$s0,$X0,$X0			; X[i] += sigma1(X[i+1])
+	ROTL	$A,10,$t1a
+||	OR	$t2a,$Maj,$Maj			; Maj(a,b,c) = ((a|b)&c)|(a&b)
+||	ROTL	$E,7,$t1e
+||	ADD	$H,$K,$T1			; T1 = h + K256[i]
+||	ADD	$s1,$X0,$X0			; X[i] += sigma1(X[i+14])
+	XOR	$t0a,$S0,$S0
+||	XOR	$t0e,$S1,$S1
+||	ADD	$X0,$T1,$T1			; T1 += X[i]
+||	STW	$X0,*$Xib++
+	XOR	$t1a,$S0,$S0			; Sigma0(a)
+||	XOR	$t1e,$S1,$S1			; Sigma1(e)
+||	ADD	$Ch,$T1,$T1			; T1 += Ch(e,f,g)
+||	MV	$X0,$X15
+||	ROTL	$G,0,$H				; h = g
+||	LDW	*$K256++,$K			; pre-fetch K256[i+1]
+	ADD	$S1,$T1,$T1			; T1 += Sigma1(e)
+||	ADD	$S0,$Maj,$T2			; T2 = Sigma0(a) + Maj(a,b,c)
+||	MV	$F,$G				; g = f
+||	MV	$Xn,$X0				; modulo-scheduled
+||	LDW	*++$Xia,$X9			; modulo-scheduled
+||	ROTL	$X1,25,$t0e			; module-scheduled
+||	ROTL	$X14,15,$t0a			; modulo-scheduled
+	ROTL	$X1,14,$t1e			; modulo-scheduled
+||	ROTL	$X14,13,$t1a			; modulo-scheduled
+||	MV	$E,$F				; f = e
+||	ADD	$D,$T1,$E			; e = d + T1
+||	MV	$C,$D				; d = c
+||	MV	$B,$C				; c = b
+	MV	$A,$B				; b = a
+||	ADD	$T1,$T2,$A			; a = T1 + T2
+||	SHRU	$X1,3,$s0			; modulo-scheduled
+||	SHRU	$X14,10,$s1			; modulo-scheduled
+	SPKERNEL
+
+   [A0]	B	outerloop?
+|| [A0]	LDNW	*$INP++,$Xn			; pre-fetch input
+|| [A0]	ADDK	-260,$K256			; rewind K256
+||	ADD	$Actx,$A,$A			; accumulate ctx
+||	ADD	$Ectx,$E,$E
+||	ADD	$Bctx,$B,$B
+	ADD	$Fctx,$F,$F
+||	ADD	$Cctx,$C,$C
+||	ADD	$Gctx,$G,$G
+||	ADD	$Dctx,$D,$D
+||	ADD	$Hctx,$H,$H
+|| [A0]	LDW	*$K256++,$K			; pre-fetch K256[0]
+
+  [!A0]	BNOP	RA
+||[!A0]	MV	$CTXA,$CTXB
+  [!A0]	MV	FP,SP				; restore stack pointer
+||[!A0]	LDW	*FP[0],FP			; restore frame pointer
+  [!A0]	STW	$A,*${CTXA}[0]  		; save ctx
+||[!A0]	STW	$E,*${CTXB}[4]
+||[!A0]	MVK	0,B0
+  [!A0]	STW	$B,*${CTXA}[1]
+||[!A0]	STW	$F,*${CTXB}[5]
+||[!A0]	MVC	B0,AMR				; clear AMR
+	STW	$C,*${CTXA}[2]
+||	STW	$G,*${CTXB}[6]
+	STW	$D,*${CTXA}[3]
+||	STW	$H,*${CTXB}[7]
+	.endasmfunc
+
+	.if	__TI_EABI__
+	.sect	".text:sha_asm.const"
+	.else
+	.sect	".const:sha_asm"
+	.endif
+	.align	128
+K256:
+	.uword	0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5
+	.uword	0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5
+	.uword	0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3
+	.uword	0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174
+	.uword	0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc
+	.uword	0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da
+	.uword	0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7
+	.uword	0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967
+	.uword	0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13
+	.uword	0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85
+	.uword	0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3
+	.uword	0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070
+	.uword	0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5
+	.uword	0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3
+	.uword	0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208
+	.uword	0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
+	.cstring "SHA256 block transform for C64x+, CRYPTOGAMS by "
+	.align	4
+
+___
+
+print $code;
+close STDOUT;
diff --git a/crypto/sha/asm/sha256-mb-x86_64.pl b/crypto/sha/asm/sha256-mb-x86_64.pl
new file mode 100644
index 000000000..fbcd29f2e
--- /dev/null
+++ b/crypto/sha/asm/sha256-mb-x86_64.pl
@@ -0,0 +1,1568 @@
+#! /usr/bin/env perl
+# Copyright 2013-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+
+# ====================================================================
+# Written by Andy Polyakov  for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+
+# Multi-buffer SHA256 procedure processes n buffers in parallel by
+# placing buffer data to designated lane of SIMD register. n is
+# naturally limited to 4 on pre-AVX2 processors and to 8 on
+# AVX2-capable processors such as Haswell.
+#
+#		this	+aesni(i)	sha256	aesni-sha256	gain(iv)
+# -------------------------------------------------------------------
+# Westmere(ii)	23.3/n	+1.28=7.11(n=4)	12.3	+3.75=16.1	+126%
+# Atom(ii)	38.7/n	+3.93=13.6(n=4)	20.8	+5.69=26.5	+95%
+# Sandy Bridge	(20.5	+5.15=25.7)/n	11.6	13.0		+103%
+# Ivy Bridge	(20.4	+5.14=25.5)/n	10.3	11.6		+82%
+# Haswell(iii)	(21.0	+5.00=26.0)/n	7.80	8.79		+170%
+# Skylake	(18.9	+5.00=23.9)/n	7.70	8.17		+170%
+# Bulldozer	(21.6	+5.76=27.4)/n	13.6	13.7		+100%
+#
+# (i)	multi-block CBC encrypt with 128-bit key;
+# (ii)	(HASH+AES)/n does not apply to Westmere for n>3 and Atom,
+#	because of lower AES-NI instruction throughput, nor is there
+#	AES-NI-SHA256 stitch for these processors;
+# (iii)	"this" is for n=8, when we gather twice as much data, result
+#	for n=4 is 20.3+4.44=24.7;
+# (iv)	presented improvement coefficients are asymptotic limits and
+#	in real-life application are somewhat lower, e.g. for 2KB 
+#	fragments they range from 75% to 130% (on Haswell);
+
+$flavour = shift;
+$output  = shift;
+if ($flavour =~ /\./) { $output = $flavour; undef $flavour; }
+
+$win64=0; $win64=1 if ($flavour =~ /[nm]asm|mingw64/ || $output =~ /\.asm$/);
+
+$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+( $xlate="${dir}x86_64-xlate.pl" and -f $xlate ) or
+( $xlate="${dir}../../perlasm/x86_64-xlate.pl" and -f $xlate) or
+die "can't locate x86_64-xlate.pl";
+
+$avx=0;
+
+if (`$ENV{CC} -Wa,-v -c -o /dev/null -x assembler /dev/null 2>&1`
+		=~ /GNU assembler version ([2-9]\.[0-9]+)/) {
+	$avx = ($1>=2.19) + ($1>=2.22);
+}
+
+if (!$avx && $win64 && ($flavour =~ /nasm/ || $ENV{ASM} =~ /nasm/) &&
+	   `nasm -v 2>&1` =~ /NASM version ([2-9]\.[0-9]+)/) {
+	$avx = ($1>=2.09) + ($1>=2.10);
+}
+
+if (!$avx && $win64 && ($flavour =~ /masm/ || $ENV{ASM} =~ /ml64/) &&
+	   `ml64 2>&1` =~ /Version ([0-9]+)\./) {
+	$avx = ($1>=10) + ($1>=11);
+}
+
+if (!$avx && `$ENV{CC} -v 2>&1` =~ /((?:^clang|LLVM) version|.*based on LLVM) ([3-9]\.[0-9]+)/) {
+	$avx = ($2>=3.0) + ($2>3.0);
+}
+
+open OUT,"| \"$^X\" \"$xlate\" $flavour \"$output\"";
+*STDOUT=*OUT;
+
+# void sha256_multi_block (
+#     struct {	unsigned int A[8];
+#		unsigned int B[8];
+#		unsigned int C[8];
+#		unsigned int D[8];
+#		unsigned int E[8];
+#		unsigned int F[8];
+#		unsigned int G[8];
+#		unsigned int H[8];	} *ctx,
+#     struct {	void *ptr; int blocks;	} inp[8],
+#     int num);		/* 1 or 2 */
+#
+$ctx="%rdi";	# 1st arg
+$inp="%rsi";	# 2nd arg
+$num="%edx";	# 3rd arg
+@ptr=map("%r$_",(8..11));
+$Tbl="%rbp";
+
+@V=($A,$B,$C,$D,$E,$F,$G,$H)=map("%xmm$_",(8..15));
+($t1,$t2,$t3,$axb,$bxc,$Xi,$Xn,$sigma)=map("%xmm$_",(0..7));
+
+$REG_SZ=16;
+
+sub Xi_off {
+my $off = shift;
+
+    $off %= 16; $off *= $REG_SZ;
+    $off<256 ? "$off-128(%rax)" : "$off-256-128(%rbx)";
+}
+
+sub ROUND_00_15 {
+my ($i,$a,$b,$c,$d,$e,$f,$g,$h)=@_;
+
+$code.=<<___ if ($i<15);
+	movd		`4*$i`(@ptr[0]),$Xi
+	movd		`4*$i`(@ptr[1]),$t1
+	movd		`4*$i`(@ptr[2]),$t2
+	movd		`4*$i`(@ptr[3]),$t3
+	punpckldq	$t2,$Xi
+	punpckldq	$t3,$t1
+	punpckldq	$t1,$Xi
+___
+$code.=<<___ if ($i==15);
+	movd		`4*$i`(@ptr[0]),$Xi
+	 lea		`16*4`(@ptr[0]),@ptr[0]
+	movd		`4*$i`(@ptr[1]),$t1
+	 lea		`16*4`(@ptr[1]),@ptr[1]
+	movd		`4*$i`(@ptr[2]),$t2
+	 lea		`16*4`(@ptr[2]),@ptr[2]
+	movd		`4*$i`(@ptr[3]),$t3
+	 lea		`16*4`(@ptr[3]),@ptr[3]
+	punpckldq	$t2,$Xi
+	punpckldq	$t3,$t1
+	punpckldq	$t1,$Xi
+___
+$code.=<<___;
+	movdqa	$e,$sigma
+	`"pshufb	$Xn,$Xi"		if ($i<=15 && ($i&1)==0)`
+	movdqa	$e,$t3
+	`"pshufb	$Xn,$Xi"		if ($i<=15 && ($i&1)==1)`
+	psrld	\$6,$sigma
+	movdqa	$e,$t2
+	pslld	\$7,$t3
+	movdqa	$Xi,`&Xi_off($i)`
+	 paddd	$h,$Xi				# Xi+=h
+
+	psrld	\$11,$t2
+	pxor	$t3,$sigma
+	pslld	\$21-7,$t3
+	 paddd	`32*($i%8)-128`($Tbl),$Xi	# Xi+=K[round]
+	pxor	$t2,$sigma
+
+	psrld	\$25-11,$t2
+	 movdqa	$e,$t1
+	 `"prefetcht0	63(@ptr[0])"		if ($i==15)`
+	pxor	$t3,$sigma
+	 movdqa	$e,$axb				# borrow $axb
+	pslld	\$26-21,$t3
+	 pandn	$g,$t1
+	 pand	$f,$axb
+	pxor	$t2,$sigma
+
+	 `"prefetcht0	63(@ptr[1])"		if ($i==15)`
+	movdqa	$a,$t2
+	pxor	$t3,$sigma			# Sigma1(e)
+	movdqa	$a,$t3
+	psrld	\$2,$t2
+	paddd	$sigma,$Xi			# Xi+=Sigma1(e)
+	 pxor	$axb,$t1			# Ch(e,f,g)
+	 movdqa	$b,$axb
+	movdqa	$a,$sigma
+	pslld	\$10,$t3
+	 pxor	$a,$axb				# a^b, b^c in next round
+
+	 `"prefetcht0	63(@ptr[2])"		if ($i==15)`
+	psrld	\$13,$sigma
+	pxor	$t3,$t2
+	 paddd	$t1,$Xi				# Xi+=Ch(e,f,g)
+	pslld	\$19-10,$t3
+	 pand	$axb,$bxc
+	pxor	$sigma,$t2
+
+	 `"prefetcht0	63(@ptr[3])"		if ($i==15)`
+	psrld	\$22-13,$sigma
+	pxor	$t3,$t2
+	 movdqa	$b,$h
+	pslld	\$30-19,$t3
+	pxor	$t2,$sigma
+	 pxor	$bxc,$h				# h=Maj(a,b,c)=Ch(a^b,c,b)
+	 paddd	$Xi,$d				# d+=Xi
+	pxor	$t3,$sigma			# Sigma0(a)
+
+	paddd	$Xi,$h				# h+=Xi
+	paddd	$sigma,$h			# h+=Sigma0(a)
+___
+$code.=<<___ if (($i%8)==7);
+	lea	`32*8`($Tbl),$Tbl
+___
+	($axb,$bxc)=($bxc,$axb);
+}
+
+sub ROUND_16_XX {
+my $i=shift;
+
+$code.=<<___;
+	movdqa	`&Xi_off($i+1)`,$Xn
+	paddd	`&Xi_off($i+9)`,$Xi		# Xi+=X[i+9]
+
+	movdqa	$Xn,$sigma
+	movdqa	$Xn,$t2
+	psrld	\$3,$sigma
+	movdqa	$Xn,$t3
+
+	psrld	\$7,$t2
+	movdqa	`&Xi_off($i+14)`,$t1
+	pslld	\$14,$t3
+	pxor	$t2,$sigma
+	psrld	\$18-7,$t2
+	movdqa	$t1,$axb			# borrow $axb
+	pxor	$t3,$sigma
+	pslld	\$25-14,$t3
+	pxor	$t2,$sigma
+	psrld	\$10,$t1
+	movdqa	$axb,$t2
+
+	psrld	\$17,$axb
+	pxor	$t3,$sigma			# sigma0(X[i+1])
+	pslld	\$13,$t2
+	 paddd	$sigma,$Xi			# Xi+=sigma0(e)
+	pxor	$axb,$t1
+	psrld	\$19-17,$axb
+	pxor	$t2,$t1
+	pslld	\$15-13,$t2
+	pxor	$axb,$t1
+	pxor	$t2,$t1				# sigma0(X[i+14])
+	paddd	$t1,$Xi				# Xi+=sigma1(X[i+14])
+___
+	&ROUND_00_15($i,@_);
+	($Xi,$Xn)=($Xn,$Xi);
+}
+
+$code.=<<___;
+.text
+
+.extern	OPENSSL_ia32cap_P
+
+.globl	sha256_multi_block
+.type	sha256_multi_block,\@function,3
+.align	32
+sha256_multi_block:
+	mov	OPENSSL_ia32cap_P+4(%rip),%rcx
+	bt	\$61,%rcx			# check SHA bit
+	jc	_shaext_shortcut
+___
+$code.=<<___ if ($avx);
+	test	\$`1<<28`,%ecx
+	jnz	_avx_shortcut
+___
+$code.=<<___;
+	mov	%rsp,%rax
+	push	%rbx
+	push	%rbp
+___
+$code.=<<___ if ($win64);
+	lea	-0xa8(%rsp),%rsp
+	movaps	%xmm6,(%rsp)
+	movaps	%xmm7,0x10(%rsp)
+	movaps	%xmm8,0x20(%rsp)
+	movaps	%xmm9,0x30(%rsp)
+	movaps	%xmm10,-0x78(%rax)
+	movaps	%xmm11,-0x68(%rax)
+	movaps	%xmm12,-0x58(%rax)
+	movaps	%xmm13,-0x48(%rax)
+	movaps	%xmm14,-0x38(%rax)
+	movaps	%xmm15,-0x28(%rax)
+___
+$code.=<<___;
+	sub	\$`$REG_SZ*18`, %rsp
+	and	\$-256,%rsp
+	mov	%rax,`$REG_SZ*17`(%rsp)		# original %rsp
+.Lbody:
+	lea	K256+128(%rip),$Tbl
+	lea	`$REG_SZ*16`(%rsp),%rbx
+	lea	0x80($ctx),$ctx			# size optimization
+
+.Loop_grande:
+	mov	$num,`$REG_SZ*17+8`(%rsp)	# original $num
+	xor	$num,$num
+___
+for($i=0;$i<4;$i++) {
+    $code.=<<___;
+	mov	`16*$i+0`($inp),@ptr[$i]	# input pointer
+	mov	`16*$i+8`($inp),%ecx		# number of blocks
+	cmp	$num,%ecx
+	cmovg	%ecx,$num			# find maximum
+	test	%ecx,%ecx
+	mov	%ecx,`4*$i`(%rbx)		# initialize counters
+	cmovle	$Tbl,@ptr[$i]			# cancel input
+___
+}
+$code.=<<___;
+	test	$num,$num
+	jz	.Ldone
+
+	movdqu	0x00-0x80($ctx),$A		# load context
+	 lea	128(%rsp),%rax
+	movdqu	0x20-0x80($ctx),$B
+	movdqu	0x40-0x80($ctx),$C
+	movdqu	0x60-0x80($ctx),$D
+	movdqu	0x80-0x80($ctx),$E
+	movdqu	0xa0-0x80($ctx),$F
+	movdqu	0xc0-0x80($ctx),$G
+	movdqu	0xe0-0x80($ctx),$H
+	movdqu	.Lpbswap(%rip),$Xn
+	jmp	.Loop
+
+.align	32
+.Loop:
+	movdqa	$C,$bxc
+	pxor	$B,$bxc				# magic seed
+___
+for($i=0;$i<16;$i++)	{ &ROUND_00_15($i,@V); unshift(@V,pop(@V)); }
+$code.=<<___;
+	movdqu	`&Xi_off($i)`,$Xi
+	mov	\$3,%ecx
+	jmp	.Loop_16_xx
+.align	32
+.Loop_16_xx:
+___
+for(;$i<32;$i++)	{ &ROUND_16_XX($i,@V); unshift(@V,pop(@V)); }
+$code.=<<___;
+	dec	%ecx
+	jnz	.Loop_16_xx
+
+	mov	\$1,%ecx
+	lea	K256+128(%rip),$Tbl
+
+	movdqa	(%rbx),$sigma			# pull counters
+	cmp	4*0(%rbx),%ecx			# examine counters
+	pxor	$t1,$t1
+	cmovge	$Tbl,@ptr[0]			# cancel input
+	cmp	4*1(%rbx),%ecx
+	movdqa	$sigma,$Xn
+	cmovge	$Tbl,@ptr[1]
+	cmp	4*2(%rbx),%ecx
+	pcmpgtd	$t1,$Xn				# mask value
+	cmovge	$Tbl,@ptr[2]
+	cmp	4*3(%rbx),%ecx
+	paddd	$Xn,$sigma			# counters--
+	cmovge	$Tbl,@ptr[3]
+
+	movdqu	0x00-0x80($ctx),$t1
+	pand	$Xn,$A
+	movdqu	0x20-0x80($ctx),$t2
+	pand	$Xn,$B
+	movdqu	0x40-0x80($ctx),$t3
+	pand	$Xn,$C
+	movdqu	0x60-0x80($ctx),$Xi
+	pand	$Xn,$D
+	paddd	$t1,$A
+	movdqu	0x80-0x80($ctx),$t1
+	pand	$Xn,$E
+	paddd	$t2,$B
+	movdqu	0xa0-0x80($ctx),$t2
+	pand	$Xn,$F
+	paddd	$t3,$C
+	movdqu	0xc0-0x80($ctx),$t3
+	pand	$Xn,$G
+	paddd	$Xi,$D
+	movdqu	0xe0-0x80($ctx),$Xi
+	pand	$Xn,$H
+	paddd	$t1,$E
+	paddd	$t2,$F
+	movdqu	$A,0x00-0x80($ctx)
+	paddd	$t3,$G
+	movdqu	$B,0x20-0x80($ctx)
+	paddd	$Xi,$H
+	movdqu	$C,0x40-0x80($ctx)
+	movdqu	$D,0x60-0x80($ctx)
+	movdqu	$E,0x80-0x80($ctx)
+	movdqu	$F,0xa0-0x80($ctx)
+	movdqu	$G,0xc0-0x80($ctx)
+	movdqu	$H,0xe0-0x80($ctx)
+
+	movdqa	$sigma,(%rbx)			# save counters
+	movdqa	.Lpbswap(%rip),$Xn
+	dec	$num
+	jnz	.Loop
+
+	mov	`$REG_SZ*17+8`(%rsp),$num
+	lea	$REG_SZ($ctx),$ctx
+	lea	`16*$REG_SZ/4`($inp),$inp
+	dec	$num
+	jnz	.Loop_grande
+
+.Ldone:
+	mov	`$REG_SZ*17`(%rsp),%rax		# original %rsp
+___
+$code.=<<___ if ($win64);
+	movaps	-0xb8(%rax),%xmm6
+	movaps	-0xa8(%rax),%xmm7
+	movaps	-0x98(%rax),%xmm8
+	movaps	-0x88(%rax),%xmm9
+	movaps	-0x78(%rax),%xmm10
+	movaps	-0x68(%rax),%xmm11
+	movaps	-0x58(%rax),%xmm12
+	movaps	-0x48(%rax),%xmm13
+	movaps	-0x38(%rax),%xmm14
+	movaps	-0x28(%rax),%xmm15
+___
+$code.=<<___;
+	mov	-16(%rax),%rbp
+	mov	-8(%rax),%rbx
+	lea	(%rax),%rsp
+.Lepilogue:
+	ret
+.size	sha256_multi_block,.-sha256_multi_block
+___
+						{{{
+my ($Wi,$TMP0,$TMP1,$TMPx,$ABEF0,$CDGH0,$ABEF1,$CDGH1)=map("%xmm$_",(0..3,12..15));
+my @MSG0=map("%xmm$_",(4..7));
+my @MSG1=map("%xmm$_",(8..11));
+
+$code.=<<___;
+.type	sha256_multi_block_shaext,\@function,3
+.align	32
+sha256_multi_block_shaext:
+_shaext_shortcut:
+	mov	%rsp,%rax
+	push	%rbx
+	push	%rbp
+___
+$code.=<<___ if ($win64);
+	lea	-0xa8(%rsp),%rsp
+	movaps	%xmm6,(%rsp)
+	movaps	%xmm7,0x10(%rsp)
+	movaps	%xmm8,0x20(%rsp)
+	movaps	%xmm9,0x30(%rsp)
+	movaps	%xmm10,-0x78(%rax)
+	movaps	%xmm11,-0x68(%rax)
+	movaps	%xmm12,-0x58(%rax)
+	movaps	%xmm13,-0x48(%rax)
+	movaps	%xmm14,-0x38(%rax)
+	movaps	%xmm15,-0x28(%rax)
+___
+$code.=<<___;
+	sub	\$`$REG_SZ*18`,%rsp
+	shl	\$1,$num			# we process pair at a time
+	and	\$-256,%rsp
+	lea	0x80($ctx),$ctx			# size optimization
+	mov	%rax,`$REG_SZ*17`(%rsp)		# original %rsp
+.Lbody_shaext:
+	lea	`$REG_SZ*16`(%rsp),%rbx
+	lea	K256_shaext+0x80(%rip),$Tbl
+
+.Loop_grande_shaext:
+	mov	$num,`$REG_SZ*17+8`(%rsp)	# original $num
+	xor	$num,$num
+___
+for($i=0;$i<2;$i++) {
+    $code.=<<___;
+	mov	`16*$i+0`($inp),@ptr[$i]	# input pointer
+	mov	`16*$i+8`($inp),%ecx		# number of blocks
+	cmp	$num,%ecx
+	cmovg	%ecx,$num			# find maximum
+	test	%ecx,%ecx
+	mov	%ecx,`4*$i`(%rbx)		# initialize counters
+	cmovle	%rsp,@ptr[$i]			# cancel input
+___
+}
+$code.=<<___;
+	test	$num,$num
+	jz	.Ldone_shaext
+
+	movq		0x00-0x80($ctx),$ABEF0		# A1.A0
+	movq		0x20-0x80($ctx),@MSG0[0]	# B1.B0
+	movq		0x40-0x80($ctx),$CDGH0		# C1.C0
+	movq		0x60-0x80($ctx),@MSG0[1]	# D1.D0
+	movq		0x80-0x80($ctx),@MSG1[0]	# E1.E0
+	movq		0xa0-0x80($ctx),@MSG1[1]	# F1.F0
+	movq		0xc0-0x80($ctx),@MSG1[2]	# G1.G0
+	movq		0xe0-0x80($ctx),@MSG1[3]	# H1.H0
+
+	punpckldq	@MSG0[0],$ABEF0			# B1.A1.B0.A0
+	punpckldq	@MSG0[1],$CDGH0			# D1.C1.D0.C0
+	punpckldq	@MSG1[1],@MSG1[0]		# F1.E1.F0.E0
+	punpckldq	@MSG1[3],@MSG1[2]		# H1.G1.H0.G0
+	movdqa		K256_shaext-0x10(%rip),$TMPx	# byte swap
+
+	movdqa		$ABEF0,$ABEF1
+	movdqa		$CDGH0,$CDGH1
+	punpcklqdq	@MSG1[0],$ABEF0			# F0.E0.B0.A0
+	punpcklqdq	@MSG1[2],$CDGH0			# H0.G0.D0.C0
+	punpckhqdq	@MSG1[0],$ABEF1			# F1.E1.B1.A1
+	punpckhqdq	@MSG1[2],$CDGH1			# H1.G1.D1.C1
+
+	pshufd		\$0b00011011,$ABEF0,$ABEF0
+	pshufd		\$0b00011011,$CDGH0,$CDGH0
+	pshufd		\$0b00011011,$ABEF1,$ABEF1
+	pshufd		\$0b00011011,$CDGH1,$CDGH1
+	jmp		.Loop_shaext
+
+.align	32
+.Loop_shaext:
+	movdqu		0x00(@ptr[0]),@MSG0[0]
+	 movdqu		0x00(@ptr[1]),@MSG1[0]
+	movdqu		0x10(@ptr[0]),@MSG0[1]
+	 movdqu		0x10(@ptr[1]),@MSG1[1]
+	movdqu		0x20(@ptr[0]),@MSG0[2]
+	pshufb		$TMPx,@MSG0[0]
+	 movdqu		0x20(@ptr[1]),@MSG1[2]
+	 pshufb		$TMPx,@MSG1[0]
+	movdqu		0x30(@ptr[0]),@MSG0[3]
+	lea		0x40(@ptr[0]),@ptr[0]
+	 movdqu		0x30(@ptr[1]),@MSG1[3]
+	 lea		0x40(@ptr[1]),@ptr[1]
+
+	movdqa		0*16-0x80($Tbl),$Wi
+	pshufb		$TMPx,@MSG0[1]
+	paddd		@MSG0[0],$Wi
+	pxor		$ABEF0,@MSG0[0]		# black magic
+	movdqa		$Wi,$TMP0
+	 movdqa		0*16-0x80($Tbl),$TMP1
+	 pshufb		$TMPx,@MSG1[1]
+	 paddd		@MSG1[0],$TMP1
+	movdqa		$CDGH0,0x50(%rsp)	# offload
+	sha256rnds2	$ABEF0,$CDGH0		# 0-3
+	 pxor		$ABEF1,@MSG1[0]		# black magic
+	 movdqa		$TMP1,$Wi
+	 movdqa		$CDGH1,0x70(%rsp)
+	 sha256rnds2	$ABEF1,$CDGH1		# 0-3
+	pshufd		\$0x0e,$TMP0,$Wi
+	pxor		$ABEF0,@MSG0[0]		# black magic
+	movdqa		$ABEF0,0x40(%rsp)	# offload
+	sha256rnds2	$CDGH0,$ABEF0
+	 pshufd		\$0x0e,$TMP1,$Wi
+	 pxor		$ABEF1,@MSG1[0]		# black magic
+	 movdqa		$ABEF1,0x60(%rsp)
+	movdqa		1*16-0x80($Tbl),$TMP0
+	paddd		@MSG0[1],$TMP0
+	pshufb		$TMPx,@MSG0[2]
+	 sha256rnds2	$CDGH1,$ABEF1
+
+	movdqa		$TMP0,$Wi
+	 movdqa		1*16-0x80($Tbl),$TMP1
+	 paddd		@MSG1[1],$TMP1
+	sha256rnds2	$ABEF0,$CDGH0		# 4-7
+	 movdqa		$TMP1,$Wi
+	prefetcht0	127(@ptr[0])
+	pshufb		$TMPx,@MSG0[3]
+	 pshufb		$TMPx,@MSG1[2]
+	 prefetcht0	127(@ptr[1])
+	 sha256rnds2	$ABEF1,$CDGH1		# 4-7
+	pshufd		\$0x0e,$TMP0,$Wi
+	 pshufb		$TMPx,@MSG1[3]
+	sha256msg1	@MSG0[1],@MSG0[0]
+	sha256rnds2	$CDGH0,$ABEF0
+	 pshufd		\$0x0e,$TMP1,$Wi
+	movdqa		2*16-0x80($Tbl),$TMP0
+	paddd		@MSG0[2],$TMP0
+	 sha256rnds2	$CDGH1,$ABEF1
+
+	movdqa		$TMP0,$Wi
+	 movdqa		2*16-0x80($Tbl),$TMP1
+	 paddd		@MSG1[2],$TMP1
+	sha256rnds2	$ABEF0,$CDGH0		# 8-11
+	 sha256msg1	@MSG1[1],@MSG1[0]
+	 movdqa		$TMP1,$Wi
+	movdqa		@MSG0[3],$TMPx
+	 sha256rnds2	$ABEF1,$CDGH1		# 8-11
+	pshufd		\$0x0e,$TMP0,$Wi
+	palignr		\$4,@MSG0[2],$TMPx
+	paddd		$TMPx,@MSG0[0]
+	 movdqa		@MSG1[3],$TMPx
+	 palignr	\$4,@MSG1[2],$TMPx
+	sha256msg1	@MSG0[2],@MSG0[1]
+	sha256rnds2	$CDGH0,$ABEF0
+	 pshufd		\$0x0e,$TMP1,$Wi
+	movdqa		3*16-0x80($Tbl),$TMP0
+	paddd		@MSG0[3],$TMP0
+	 sha256rnds2	$CDGH1,$ABEF1
+	 sha256msg1	@MSG1[2],@MSG1[1]
+
+	movdqa		$TMP0,$Wi
+	 movdqa		3*16-0x80($Tbl),$TMP1
+	 paddd		$TMPx,@MSG1[0]
+	 paddd		@MSG1[3],$TMP1
+	sha256msg2	@MSG0[3],@MSG0[0]
+	sha256rnds2	$ABEF0,$CDGH0		# 12-15
+	 movdqa		$TMP1,$Wi
+	movdqa		@MSG0[0],$TMPx
+	palignr		\$4,@MSG0[3],$TMPx
+	 sha256rnds2	$ABEF1,$CDGH1		# 12-15
+	 sha256msg2	@MSG1[3],@MSG1[0]
+	pshufd		\$0x0e,$TMP0,$Wi
+	paddd		$TMPx,@MSG0[1]
+	 movdqa		@MSG1[0],$TMPx
+	 palignr	\$4,@MSG1[3],$TMPx
+	sha256msg1	@MSG0[3],@MSG0[2]
+	sha256rnds2	$CDGH0,$ABEF0
+	 pshufd		\$0x0e,$TMP1,$Wi
+	movdqa		4*16-0x80($Tbl),$TMP0
+	paddd		@MSG0[0],$TMP0
+	 sha256rnds2	$CDGH1,$ABEF1
+	 sha256msg1	@MSG1[3],@MSG1[2]
+___
+for($i=4;$i<16-3;$i++) {
+$code.=<<___;
+	movdqa		$TMP0,$Wi
+	 movdqa		$i*16-0x80($Tbl),$TMP1
+	 paddd		$TMPx,@MSG1[1]
+	 paddd		@MSG1[0],$TMP1
+	sha256msg2	@MSG0[0],@MSG0[1]
+	sha256rnds2	$ABEF0,$CDGH0		# 16-19...
+	 movdqa		$TMP1,$Wi
+	movdqa		@MSG0[1],$TMPx
+	palignr		\$4,@MSG0[0],$TMPx
+	 sha256rnds2	$ABEF1,$CDGH1		# 16-19...
+	 sha256msg2	@MSG1[0],@MSG1[1]
+	pshufd		\$0x0e,$TMP0,$Wi
+	paddd		$TMPx,@MSG0[2]
+	 movdqa		@MSG1[1],$TMPx
+	 palignr	\$4,@MSG1[0],$TMPx
+	sha256msg1	@MSG0[0],@MSG0[3]
+	sha256rnds2	$CDGH0,$ABEF0
+	 pshufd		\$0x0e,$TMP1,$Wi
+	movdqa		`($i+1)*16`-0x80($Tbl),$TMP0
+	paddd		@MSG0[1],$TMP0
+	 sha256rnds2	$CDGH1,$ABEF1
+	 sha256msg1	@MSG1[0],@MSG1[3]
+___
+	push(@MSG0,shift(@MSG0));	push(@MSG1,shift(@MSG1));
+}
+$code.=<<___;
+	movdqa		$TMP0,$Wi
+	 movdqa		13*16-0x80($Tbl),$TMP1
+	 paddd		$TMPx,@MSG1[1]
+	 paddd		@MSG1[0],$TMP1
+	sha256msg2	@MSG0[0],@MSG0[1]
+	sha256rnds2	$ABEF0,$CDGH0		# 52-55
+	 movdqa		$TMP1,$Wi
+	movdqa		@MSG0[1],$TMPx
+	palignr		\$4,@MSG0[0],$TMPx
+	 sha256rnds2	$ABEF1,$CDGH1		# 52-55
+	 sha256msg2	@MSG1[0],@MSG1[1]
+	pshufd		\$0x0e,$TMP0,$Wi
+	paddd		$TMPx,@MSG0[2]
+	 movdqa		@MSG1[1],$TMPx
+	 palignr	\$4,@MSG1[0],$TMPx
+	nop
+	sha256rnds2	$CDGH0,$ABEF0
+	 pshufd		\$0x0e,$TMP1,$Wi
+	movdqa		14*16-0x80($Tbl),$TMP0
+	paddd		@MSG0[1],$TMP0
+	 sha256rnds2	$CDGH1,$ABEF1
+
+	movdqa		$TMP0,$Wi
+	 movdqa		14*16-0x80($Tbl),$TMP1
+	 paddd		$TMPx,@MSG1[2]
+	 paddd		@MSG1[1],$TMP1
+	sha256msg2	@MSG0[1],@MSG0[2]
+	nop
+	sha256rnds2	$ABEF0,$CDGH0		# 56-59
+	 movdqa		$TMP1,$Wi
+	  mov		\$1,%ecx
+	  pxor		@MSG0[1],@MSG0[1]	# zero
+	 sha256rnds2	$ABEF1,$CDGH1		# 56-59
+	 sha256msg2	@MSG1[1],@MSG1[2]
+	pshufd		\$0x0e,$TMP0,$Wi
+	movdqa		15*16-0x80($Tbl),$TMP0
+	paddd		@MSG0[2],$TMP0
+	  movq		(%rbx),@MSG0[2]		# pull counters
+	  nop
+	sha256rnds2	$CDGH0,$ABEF0
+	 pshufd		\$0x0e,$TMP1,$Wi
+	 movdqa		15*16-0x80($Tbl),$TMP1
+	 paddd		@MSG1[2],$TMP1
+	 sha256rnds2	$CDGH1,$ABEF1
+
+	movdqa		$TMP0,$Wi
+	  cmp		4*0(%rbx),%ecx		# examine counters
+	  cmovge	%rsp,@ptr[0]		# cancel input
+	  cmp		4*1(%rbx),%ecx
+	  cmovge	%rsp,@ptr[1]
+	  pshufd	\$0x00,@MSG0[2],@MSG1[0]
+	sha256rnds2	$ABEF0,$CDGH0		# 60-63
+	 movdqa		$TMP1,$Wi
+	  pshufd	\$0x55,@MSG0[2],@MSG1[1]
+	  movdqa	@MSG0[2],@MSG1[2]
+	 sha256rnds2	$ABEF1,$CDGH1		# 60-63
+	pshufd		\$0x0e,$TMP0,$Wi
+	  pcmpgtd	@MSG0[1],@MSG1[0]
+	  pcmpgtd	@MSG0[1],@MSG1[1]
+	sha256rnds2	$CDGH0,$ABEF0
+	 pshufd		\$0x0e,$TMP1,$Wi
+	  pcmpgtd	@MSG0[1],@MSG1[2]	# counter mask
+	  movdqa	K256_shaext-0x10(%rip),$TMPx
+	 sha256rnds2	$CDGH1,$ABEF1
+
+	pand		@MSG1[0],$CDGH0
+	 pand		@MSG1[1],$CDGH1
+	pand		@MSG1[0],$ABEF0
+	 pand		@MSG1[1],$ABEF1
+	paddd		@MSG0[2],@MSG1[2]	# counters--
+
+	paddd		0x50(%rsp),$CDGH0
+	 paddd		0x70(%rsp),$CDGH1
+	paddd		0x40(%rsp),$ABEF0
+	 paddd		0x60(%rsp),$ABEF1
+
+	movq		@MSG1[2],(%rbx)		# save counters
+	dec		$num
+	jnz		.Loop_shaext
+
+	mov		`$REG_SZ*17+8`(%rsp),$num
+
+	pshufd		\$0b00011011,$ABEF0,$ABEF0
+	pshufd		\$0b00011011,$CDGH0,$CDGH0
+	pshufd		\$0b00011011,$ABEF1,$ABEF1
+	pshufd		\$0b00011011,$CDGH1,$CDGH1
+
+	movdqa		$ABEF0,@MSG0[0]
+	movdqa		$CDGH0,@MSG0[1]
+	punpckldq	$ABEF1,$ABEF0			# B1.B0.A1.A0
+	punpckhdq	$ABEF1,@MSG0[0]			# F1.F0.E1.E0
+	punpckldq	$CDGH1,$CDGH0			# D1.D0.C1.C0
+	punpckhdq	$CDGH1,@MSG0[1]			# H1.H0.G1.G0
+
+	movq		$ABEF0,0x00-0x80($ctx)		# A1.A0
+	psrldq		\$8,$ABEF0
+	movq		@MSG0[0],0x80-0x80($ctx)	# E1.E0
+	psrldq		\$8,@MSG0[0]
+	movq		$ABEF0,0x20-0x80($ctx)		# B1.B0
+	movq		@MSG0[0],0xa0-0x80($ctx)	# F1.F0
+
+	movq		$CDGH0,0x40-0x80($ctx)		# C1.C0
+	psrldq		\$8,$CDGH0
+	movq		@MSG0[1],0xc0-0x80($ctx)	# G1.G0
+	psrldq		\$8,@MSG0[1]
+	movq		$CDGH0,0x60-0x80($ctx)		# D1.D0
+	movq		@MSG0[1],0xe0-0x80($ctx)	# H1.H0
+
+	lea	`$REG_SZ/2`($ctx),$ctx
+	lea	`16*2`($inp),$inp
+	dec	$num
+	jnz	.Loop_grande_shaext
+
+.Ldone_shaext:
+	#mov	`$REG_SZ*17`(%rsp),%rax		# original %rsp
+___
+$code.=<<___ if ($win64);
+	movaps	-0xb8(%rax),%xmm6
+	movaps	-0xa8(%rax),%xmm7
+	movaps	-0x98(%rax),%xmm8
+	movaps	-0x88(%rax),%xmm9
+	movaps	-0x78(%rax),%xmm10
+	movaps	-0x68(%rax),%xmm11
+	movaps	-0x58(%rax),%xmm12
+	movaps	-0x48(%rax),%xmm13
+	movaps	-0x38(%rax),%xmm14
+	movaps	-0x28(%rax),%xmm15
+___
+$code.=<<___;
+	mov	-16(%rax),%rbp
+	mov	-8(%rax),%rbx
+	lea	(%rax),%rsp
+.Lepilogue_shaext:
+	ret
+.size	sha256_multi_block_shaext,.-sha256_multi_block_shaext
+___
+						}}}
+						if ($avx) {{{
+sub ROUND_00_15_avx {
+my ($i,$a,$b,$c,$d,$e,$f,$g,$h)=@_;
+
+$code.=<<___ if ($i<15 && $REG_SZ==16);
+	vmovd		`4*$i`(@ptr[0]),$Xi
+	vmovd		`4*$i`(@ptr[1]),$t1
+	vpinsrd		\$1,`4*$i`(@ptr[2]),$Xi,$Xi
+	vpinsrd		\$1,`4*$i`(@ptr[3]),$t1,$t1
+	vpunpckldq	$t1,$Xi,$Xi
+	vpshufb		$Xn,$Xi,$Xi
+___
+$code.=<<___ if ($i==15 && $REG_SZ==16);
+	vmovd		`4*$i`(@ptr[0]),$Xi
+	 lea		`16*4`(@ptr[0]),@ptr[0]
+	vmovd		`4*$i`(@ptr[1]),$t1
+	 lea		`16*4`(@ptr[1]),@ptr[1]
+	vpinsrd		\$1,`4*$i`(@ptr[2]),$Xi,$Xi
+	 lea		`16*4`(@ptr[2]),@ptr[2]
+	vpinsrd		\$1,`4*$i`(@ptr[3]),$t1,$t1
+	 lea		`16*4`(@ptr[3]),@ptr[3]
+	vpunpckldq	$t1,$Xi,$Xi
+	vpshufb		$Xn,$Xi,$Xi
+___
+$code.=<<___ if ($i<15 && $REG_SZ==32);
+	vmovd		`4*$i`(@ptr[0]),$Xi
+	vmovd		`4*$i`(@ptr[4]),$t1
+	vmovd		`4*$i`(@ptr[1]),$t2
+	vmovd		`4*$i`(@ptr[5]),$t3
+	vpinsrd		\$1,`4*$i`(@ptr[2]),$Xi,$Xi
+	vpinsrd		\$1,`4*$i`(@ptr[6]),$t1,$t1
+	vpinsrd		\$1,`4*$i`(@ptr[3]),$t2,$t2
+	vpunpckldq	$t2,$Xi,$Xi
+	vpinsrd		\$1,`4*$i`(@ptr[7]),$t3,$t3
+	vpunpckldq	$t3,$t1,$t1
+	vinserti128	$t1,$Xi,$Xi
+	vpshufb		$Xn,$Xi,$Xi
+___
+$code.=<<___ if ($i==15 && $REG_SZ==32);
+	vmovd		`4*$i`(@ptr[0]),$Xi
+	 lea		`16*4`(@ptr[0]),@ptr[0]
+	vmovd		`4*$i`(@ptr[4]),$t1
+	 lea		`16*4`(@ptr[4]),@ptr[4]
+	vmovd		`4*$i`(@ptr[1]),$t2
+	 lea		`16*4`(@ptr[1]),@ptr[1]
+	vmovd		`4*$i`(@ptr[5]),$t3
+	 lea		`16*4`(@ptr[5]),@ptr[5]
+	vpinsrd		\$1,`4*$i`(@ptr[2]),$Xi,$Xi
+	 lea		`16*4`(@ptr[2]),@ptr[2]
+	vpinsrd		\$1,`4*$i`(@ptr[6]),$t1,$t1
+	 lea		`16*4`(@ptr[6]),@ptr[6]
+	vpinsrd		\$1,`4*$i`(@ptr[3]),$t2,$t2
+	 lea		`16*4`(@ptr[3]),@ptr[3]
+	vpunpckldq	$t2,$Xi,$Xi
+	vpinsrd		\$1,`4*$i`(@ptr[7]),$t3,$t3
+	 lea		`16*4`(@ptr[7]),@ptr[7]
+	vpunpckldq	$t3,$t1,$t1
+	vinserti128	$t1,$Xi,$Xi
+	vpshufb		$Xn,$Xi,$Xi
+___
+$code.=<<___;
+	vpsrld	\$6,$e,$sigma
+	vpslld	\$26,$e,$t3
+	vmovdqu	$Xi,`&Xi_off($i)`
+	 vpaddd	$h,$Xi,$Xi			# Xi+=h
+
+	vpsrld	\$11,$e,$t2
+	vpxor	$t3,$sigma,$sigma
+	vpslld	\$21,$e,$t3
+	 vpaddd	`32*($i%8)-128`($Tbl),$Xi,$Xi	# Xi+=K[round]
+	vpxor	$t2,$sigma,$sigma
+
+	vpsrld	\$25,$e,$t2
+	vpxor	$t3,$sigma,$sigma
+	 `"prefetcht0	63(@ptr[0])"		if ($i==15)`
+	vpslld	\$7,$e,$t3
+	 vpandn	$g,$e,$t1
+	 vpand	$f,$e,$axb			# borrow $axb
+	 `"prefetcht0	63(@ptr[1])"		if ($i==15)`
+	vpxor	$t2,$sigma,$sigma
+
+	vpsrld	\$2,$a,$h			# borrow $h
+	vpxor	$t3,$sigma,$sigma		# Sigma1(e)
+	 `"prefetcht0	63(@ptr[2])"		if ($i==15)`
+	vpslld	\$30,$a,$t2
+	 vpxor	$axb,$t1,$t1			# Ch(e,f,g)
+	 vpxor	$a,$b,$axb			# a^b, b^c in next round
+	 `"prefetcht0	63(@ptr[3])"		if ($i==15)`
+	vpxor	$t2,$h,$h
+	vpaddd	$sigma,$Xi,$Xi			# Xi+=Sigma1(e)
+
+	vpsrld	\$13,$a,$t2
+	 `"prefetcht0	63(@ptr[4])"		if ($i==15 && $REG_SZ==32)`
+	vpslld	\$19,$a,$t3
+	 vpaddd	$t1,$Xi,$Xi			# Xi+=Ch(e,f,g)
+	 vpand	$axb,$bxc,$bxc
+	 `"prefetcht0	63(@ptr[5])"		if ($i==15 && $REG_SZ==32)`
+	vpxor	$t2,$h,$sigma
+
+	vpsrld	\$22,$a,$t2
+	vpxor	$t3,$sigma,$sigma
+	 `"prefetcht0	63(@ptr[6])"		if ($i==15 && $REG_SZ==32)`
+	vpslld	\$10,$a,$t3
+	 vpxor	$bxc,$b,$h			# h=Maj(a,b,c)=Ch(a^b,c,b)
+	 vpaddd	$Xi,$d,$d			# d+=Xi
+	 `"prefetcht0	63(@ptr[7])"		if ($i==15 && $REG_SZ==32)`
+	vpxor	$t2,$sigma,$sigma
+	vpxor	$t3,$sigma,$sigma		# Sigma0(a)
+
+	vpaddd	$Xi,$h,$h			# h+=Xi
+	vpaddd	$sigma,$h,$h			# h+=Sigma0(a)
+___
+$code.=<<___ if (($i%8)==7);
+	add	\$`32*8`,$Tbl
+___
+	($axb,$bxc)=($bxc,$axb);
+}
+
+sub ROUND_16_XX_avx {
+my $i=shift;
+
+$code.=<<___;
+	vmovdqu	`&Xi_off($i+1)`,$Xn
+	vpaddd	`&Xi_off($i+9)`,$Xi,$Xi		# Xi+=X[i+9]
+
+	vpsrld	\$3,$Xn,$sigma
+	vpsrld	\$7,$Xn,$t2
+	vpslld	\$25,$Xn,$t3
+	vpxor	$t2,$sigma,$sigma
+	vpsrld	\$18,$Xn,$t2
+	vpxor	$t3,$sigma,$sigma
+	vpslld	\$14,$Xn,$t3
+	vmovdqu	`&Xi_off($i+14)`,$t1
+	vpsrld	\$10,$t1,$axb			# borrow $axb
+
+	vpxor	$t2,$sigma,$sigma
+	vpsrld	\$17,$t1,$t2
+	vpxor	$t3,$sigma,$sigma		# sigma0(X[i+1])
+	vpslld	\$15,$t1,$t3
+	 vpaddd	$sigma,$Xi,$Xi			# Xi+=sigma0(e)
+	vpxor	$t2,$axb,$sigma
+	vpsrld	\$19,$t1,$t2
+	vpxor	$t3,$sigma,$sigma
+	vpslld	\$13,$t1,$t3
+	vpxor	$t2,$sigma,$sigma
+	vpxor	$t3,$sigma,$sigma		# sigma0(X[i+14])
+	vpaddd	$sigma,$Xi,$Xi			# Xi+=sigma1(X[i+14])
+___
+	&ROUND_00_15_avx($i,@_);
+	($Xi,$Xn)=($Xn,$Xi);
+}
+
+$code.=<<___;
+.type	sha256_multi_block_avx,\@function,3
+.align	32
+sha256_multi_block_avx:
+_avx_shortcut:
+___
+$code.=<<___ if ($avx>1);
+	shr	\$32,%rcx
+	cmp	\$2,$num
+	jb	.Lavx
+	test	\$`1<<5`,%ecx
+	jnz	_avx2_shortcut
+	jmp	.Lavx
+.align	32
+.Lavx:
+___
+$code.=<<___;
+	mov	%rsp,%rax
+	push	%rbx
+	push	%rbp
+___
+$code.=<<___ if ($win64);
+	lea	-0xa8(%rsp),%rsp
+	movaps	%xmm6,(%rsp)
+	movaps	%xmm7,0x10(%rsp)
+	movaps	%xmm8,0x20(%rsp)
+	movaps	%xmm9,0x30(%rsp)
+	movaps	%xmm10,-0x78(%rax)
+	movaps	%xmm11,-0x68(%rax)
+	movaps	%xmm12,-0x58(%rax)
+	movaps	%xmm13,-0x48(%rax)
+	movaps	%xmm14,-0x38(%rax)
+	movaps	%xmm15,-0x28(%rax)
+___
+$code.=<<___;
+	sub	\$`$REG_SZ*18`, %rsp
+	and	\$-256,%rsp
+	mov	%rax,`$REG_SZ*17`(%rsp)		# original %rsp
+.Lbody_avx:
+	lea	K256+128(%rip),$Tbl
+	lea	`$REG_SZ*16`(%rsp),%rbx
+	lea	0x80($ctx),$ctx			# size optimization
+
+.Loop_grande_avx:
+	mov	$num,`$REG_SZ*17+8`(%rsp)	# original $num
+	xor	$num,$num
+___
+for($i=0;$i<4;$i++) {
+    $code.=<<___;
+	mov	`16*$i+0`($inp),@ptr[$i]	# input pointer
+	mov	`16*$i+8`($inp),%ecx		# number of blocks
+	cmp	$num,%ecx
+	cmovg	%ecx,$num			# find maximum
+	test	%ecx,%ecx
+	mov	%ecx,`4*$i`(%rbx)		# initialize counters
+	cmovle	$Tbl,@ptr[$i]			# cancel input
+___
+}
+$code.=<<___;
+	test	$num,$num
+	jz	.Ldone_avx
+
+	vmovdqu	0x00-0x80($ctx),$A		# load context
+	 lea	128(%rsp),%rax
+	vmovdqu	0x20-0x80($ctx),$B
+	vmovdqu	0x40-0x80($ctx),$C
+	vmovdqu	0x60-0x80($ctx),$D
+	vmovdqu	0x80-0x80($ctx),$E
+	vmovdqu	0xa0-0x80($ctx),$F
+	vmovdqu	0xc0-0x80($ctx),$G
+	vmovdqu	0xe0-0x80($ctx),$H
+	vmovdqu	.Lpbswap(%rip),$Xn
+	jmp	.Loop_avx
+
+.align	32
+.Loop_avx:
+	vpxor	$B,$C,$bxc			# magic seed
+___
+for($i=0;$i<16;$i++)	{ &ROUND_00_15_avx($i,@V); unshift(@V,pop(@V)); }
+$code.=<<___;
+	vmovdqu	`&Xi_off($i)`,$Xi
+	mov	\$3,%ecx
+	jmp	.Loop_16_xx_avx
+.align	32
+.Loop_16_xx_avx:
+___
+for(;$i<32;$i++)	{ &ROUND_16_XX_avx($i,@V); unshift(@V,pop(@V)); }
+$code.=<<___;
+	dec	%ecx
+	jnz	.Loop_16_xx_avx
+
+	mov	\$1,%ecx
+	lea	K256+128(%rip),$Tbl
+___
+for($i=0;$i<4;$i++) {
+    $code.=<<___;
+	cmp	`4*$i`(%rbx),%ecx		# examine counters
+	cmovge	$Tbl,@ptr[$i]			# cancel input
+___
+}
+$code.=<<___;
+	vmovdqa	(%rbx),$sigma			# pull counters
+	vpxor	$t1,$t1,$t1
+	vmovdqa	$sigma,$Xn
+	vpcmpgtd $t1,$Xn,$Xn			# mask value
+	vpaddd	$Xn,$sigma,$sigma		# counters--
+
+	vmovdqu	0x00-0x80($ctx),$t1
+	vpand	$Xn,$A,$A
+	vmovdqu	0x20-0x80($ctx),$t2
+	vpand	$Xn,$B,$B
+	vmovdqu	0x40-0x80($ctx),$t3
+	vpand	$Xn,$C,$C
+	vmovdqu	0x60-0x80($ctx),$Xi
+	vpand	$Xn,$D,$D
+	vpaddd	$t1,$A,$A
+	vmovdqu	0x80-0x80($ctx),$t1
+	vpand	$Xn,$E,$E
+	vpaddd	$t2,$B,$B
+	vmovdqu	0xa0-0x80($ctx),$t2
+	vpand	$Xn,$F,$F
+	vpaddd	$t3,$C,$C
+	vmovdqu	0xc0-0x80($ctx),$t3
+	vpand	$Xn,$G,$G
+	vpaddd	$Xi,$D,$D
+	vmovdqu	0xe0-0x80($ctx),$Xi
+	vpand	$Xn,$H,$H
+	vpaddd	$t1,$E,$E
+	vpaddd	$t2,$F,$F
+	vmovdqu	$A,0x00-0x80($ctx)
+	vpaddd	$t3,$G,$G
+	vmovdqu	$B,0x20-0x80($ctx)
+	vpaddd	$Xi,$H,$H
+	vmovdqu	$C,0x40-0x80($ctx)
+	vmovdqu	$D,0x60-0x80($ctx)
+	vmovdqu	$E,0x80-0x80($ctx)
+	vmovdqu	$F,0xa0-0x80($ctx)
+	vmovdqu	$G,0xc0-0x80($ctx)
+	vmovdqu	$H,0xe0-0x80($ctx)
+
+	vmovdqu	$sigma,(%rbx)			# save counters
+	vmovdqu	.Lpbswap(%rip),$Xn
+	dec	$num
+	jnz	.Loop_avx
+
+	mov	`$REG_SZ*17+8`(%rsp),$num
+	lea	$REG_SZ($ctx),$ctx
+	lea	`16*$REG_SZ/4`($inp),$inp
+	dec	$num
+	jnz	.Loop_grande_avx
+
+.Ldone_avx:
+	mov	`$REG_SZ*17`(%rsp),%rax		# original %rsp
+	vzeroupper
+___
+$code.=<<___ if ($win64);
+	movaps	-0xb8(%rax),%xmm6
+	movaps	-0xa8(%rax),%xmm7
+	movaps	-0x98(%rax),%xmm8
+	movaps	-0x88(%rax),%xmm9
+	movaps	-0x78(%rax),%xmm10
+	movaps	-0x68(%rax),%xmm11
+	movaps	-0x58(%rax),%xmm12
+	movaps	-0x48(%rax),%xmm13
+	movaps	-0x38(%rax),%xmm14
+	movaps	-0x28(%rax),%xmm15
+___
+$code.=<<___;
+	mov	-16(%rax),%rbp
+	mov	-8(%rax),%rbx
+	lea	(%rax),%rsp
+.Lepilogue_avx:
+	ret
+.size	sha256_multi_block_avx,.-sha256_multi_block_avx
+___
+						if ($avx>1) {
+$code =~ s/\`([^\`]*)\`/eval $1/gem;
+
+$REG_SZ=32;
+@ptr=map("%r$_",(12..15,8..11));
+
+@V=($A,$B,$C,$D,$E,$F,$G,$H)=map("%ymm$_",(8..15));
+($t1,$t2,$t3,$axb,$bxc,$Xi,$Xn,$sigma)=map("%ymm$_",(0..7));
+
+$code.=<<___;
+.type	sha256_multi_block_avx2,\@function,3
+.align	32
+sha256_multi_block_avx2:
+_avx2_shortcut:
+	mov	%rsp,%rax
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+___
+$code.=<<___ if ($win64);
+	lea	-0xa8(%rsp),%rsp
+	movaps	%xmm6,(%rsp)
+	movaps	%xmm7,0x10(%rsp)
+	movaps	%xmm8,0x20(%rsp)
+	movaps	%xmm9,0x30(%rsp)
+	movaps	%xmm10,0x40(%rsp)
+	movaps	%xmm11,0x50(%rsp)
+	movaps	%xmm12,-0x78(%rax)
+	movaps	%xmm13,-0x68(%rax)
+	movaps	%xmm14,-0x58(%rax)
+	movaps	%xmm15,-0x48(%rax)
+___
+$code.=<<___;
+	sub	\$`$REG_SZ*18`, %rsp
+	and	\$-256,%rsp
+	mov	%rax,`$REG_SZ*17`(%rsp)		# original %rsp
+.Lbody_avx2:
+	lea	K256+128(%rip),$Tbl
+	lea	0x80($ctx),$ctx			# size optimization
+
+.Loop_grande_avx2:
+	mov	$num,`$REG_SZ*17+8`(%rsp)	# original $num
+	xor	$num,$num
+	lea	`$REG_SZ*16`(%rsp),%rbx
+___
+for($i=0;$i<8;$i++) {
+    $code.=<<___;
+	mov	`16*$i+0`($inp),@ptr[$i]	# input pointer
+	mov	`16*$i+8`($inp),%ecx		# number of blocks
+	cmp	$num,%ecx
+	cmovg	%ecx,$num			# find maximum
+	test	%ecx,%ecx
+	mov	%ecx,`4*$i`(%rbx)		# initialize counters
+	cmovle	$Tbl,@ptr[$i]			# cancel input
+___
+}
+$code.=<<___;
+	vmovdqu	0x00-0x80($ctx),$A		# load context
+	 lea	128(%rsp),%rax
+	vmovdqu	0x20-0x80($ctx),$B
+	 lea	256+128(%rsp),%rbx
+	vmovdqu	0x40-0x80($ctx),$C
+	vmovdqu	0x60-0x80($ctx),$D
+	vmovdqu	0x80-0x80($ctx),$E
+	vmovdqu	0xa0-0x80($ctx),$F
+	vmovdqu	0xc0-0x80($ctx),$G
+	vmovdqu	0xe0-0x80($ctx),$H
+	vmovdqu	.Lpbswap(%rip),$Xn
+	jmp	.Loop_avx2
+
+.align	32
+.Loop_avx2:
+	vpxor	$B,$C,$bxc			# magic seed
+___
+for($i=0;$i<16;$i++)	{ &ROUND_00_15_avx($i,@V); unshift(@V,pop(@V)); }
+$code.=<<___;
+	vmovdqu	`&Xi_off($i)`,$Xi
+	mov	\$3,%ecx
+	jmp	.Loop_16_xx_avx2
+.align	32
+.Loop_16_xx_avx2:
+___
+for(;$i<32;$i++)	{ &ROUND_16_XX_avx($i,@V); unshift(@V,pop(@V)); }
+$code.=<<___;
+	dec	%ecx
+	jnz	.Loop_16_xx_avx2
+
+	mov	\$1,%ecx
+	lea	`$REG_SZ*16`(%rsp),%rbx
+	lea	K256+128(%rip),$Tbl
+___
+for($i=0;$i<8;$i++) {
+    $code.=<<___;
+	cmp	`4*$i`(%rbx),%ecx		# examine counters
+	cmovge	$Tbl,@ptr[$i]			# cancel input
+___
+}
+$code.=<<___;
+	vmovdqa	(%rbx),$sigma			# pull counters
+	vpxor	$t1,$t1,$t1
+	vmovdqa	$sigma,$Xn
+	vpcmpgtd $t1,$Xn,$Xn			# mask value
+	vpaddd	$Xn,$sigma,$sigma		# counters--
+
+	vmovdqu	0x00-0x80($ctx),$t1
+	vpand	$Xn,$A,$A
+	vmovdqu	0x20-0x80($ctx),$t2
+	vpand	$Xn,$B,$B
+	vmovdqu	0x40-0x80($ctx),$t3
+	vpand	$Xn,$C,$C
+	vmovdqu	0x60-0x80($ctx),$Xi
+	vpand	$Xn,$D,$D
+	vpaddd	$t1,$A,$A
+	vmovdqu	0x80-0x80($ctx),$t1
+	vpand	$Xn,$E,$E
+	vpaddd	$t2,$B,$B
+	vmovdqu	0xa0-0x80($ctx),$t2
+	vpand	$Xn,$F,$F
+	vpaddd	$t3,$C,$C
+	vmovdqu	0xc0-0x80($ctx),$t3
+	vpand	$Xn,$G,$G
+	vpaddd	$Xi,$D,$D
+	vmovdqu	0xe0-0x80($ctx),$Xi
+	vpand	$Xn,$H,$H
+	vpaddd	$t1,$E,$E
+	vpaddd	$t2,$F,$F
+	vmovdqu	$A,0x00-0x80($ctx)
+	vpaddd	$t3,$G,$G
+	vmovdqu	$B,0x20-0x80($ctx)
+	vpaddd	$Xi,$H,$H
+	vmovdqu	$C,0x40-0x80($ctx)
+	vmovdqu	$D,0x60-0x80($ctx)
+	vmovdqu	$E,0x80-0x80($ctx)
+	vmovdqu	$F,0xa0-0x80($ctx)
+	vmovdqu	$G,0xc0-0x80($ctx)
+	vmovdqu	$H,0xe0-0x80($ctx)
+
+	vmovdqu	$sigma,(%rbx)			# save counters
+	lea	256+128(%rsp),%rbx
+	vmovdqu	.Lpbswap(%rip),$Xn
+	dec	$num
+	jnz	.Loop_avx2
+
+	#mov	`$REG_SZ*17+8`(%rsp),$num
+	#lea	$REG_SZ($ctx),$ctx
+	#lea	`16*$REG_SZ/4`($inp),$inp
+	#dec	$num
+	#jnz	.Loop_grande_avx2
+
+.Ldone_avx2:
+	mov	`$REG_SZ*17`(%rsp),%rax		# original %rsp
+	vzeroupper
+___
+$code.=<<___ if ($win64);
+	movaps	-0xd8(%rax),%xmm6
+	movaps	-0xc8(%rax),%xmm7
+	movaps	-0xb8(%rax),%xmm8
+	movaps	-0xa8(%rax),%xmm9
+	movaps	-0x98(%rax),%xmm10
+	movaps	-0x88(%rax),%xmm11
+	movaps	-0x78(%rax),%xmm12
+	movaps	-0x68(%rax),%xmm13
+	movaps	-0x58(%rax),%xmm14
+	movaps	-0x48(%rax),%xmm15
+___
+$code.=<<___;
+	mov	-48(%rax),%r15
+	mov	-40(%rax),%r14
+	mov	-32(%rax),%r13
+	mov	-24(%rax),%r12
+	mov	-16(%rax),%rbp
+	mov	-8(%rax),%rbx
+	lea	(%rax),%rsp
+.Lepilogue_avx2:
+	ret
+.size	sha256_multi_block_avx2,.-sha256_multi_block_avx2
+___
+					}	}}}
+$code.=<<___;
+.align	256
+K256:
+___
+sub TABLE {
+    foreach (@_) {
+	$code.=<<___;
+	.long	$_,$_,$_,$_
+	.long	$_,$_,$_,$_
+___
+    }
+}
+&TABLE(	0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5,
+	0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5,
+	0xd807aa98,0x12835b01,0x243185be,0x550c7dc3,
+	0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174,
+	0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc,
+	0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da,
+	0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7,
+	0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967,
+	0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13,
+	0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85,
+	0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3,
+	0xd192e819,0xd6990624,0xf40e3585,0x106aa070,
+	0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5,
+	0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3,
+	0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208,
+	0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2 );
+$code.=<<___;
+.Lpbswap:
+	.long	0x00010203,0x04050607,0x08090a0b,0x0c0d0e0f	# pbswap
+	.long	0x00010203,0x04050607,0x08090a0b,0x0c0d0e0f	# pbswap
+K256_shaext:
+	.long	0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5
+	.long	0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5
+	.long	0xd807aa98,0x12835b01,0x243185be,0x550c7dc3
+	.long	0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174
+	.long	0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc
+	.long	0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da
+	.long	0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7
+	.long	0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967
+	.long	0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13
+	.long	0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85
+	.long	0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3
+	.long	0xd192e819,0xd6990624,0xf40e3585,0x106aa070
+	.long	0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5
+	.long	0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3
+	.long	0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208
+	.long	0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2
+	.asciz	"SHA256 multi-block transform for x86_64, CRYPTOGAMS by "
+___
+
+if ($win64) {
+# EXCEPTION_DISPOSITION handler (EXCEPTION_RECORD *rec,ULONG64 frame,
+#		CONTEXT *context,DISPATCHER_CONTEXT *disp)
+$rec="%rcx";
+$frame="%rdx";
+$context="%r8";
+$disp="%r9";
+
+$code.=<<___;
+.extern	__imp_RtlVirtualUnwind
+.type	se_handler,\@abi-omnipotent
+.align	16
+se_handler:
+	push	%rsi
+	push	%rdi
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+	pushfq
+	sub	\$64,%rsp
+
+	mov	120($context),%rax	# pull context->Rax
+	mov	248($context),%rbx	# pull context->Rip
+
+	mov	8($disp),%rsi		# disp->ImageBase
+	mov	56($disp),%r11		# disp->HandlerData
+
+	mov	0(%r11),%r10d		# HandlerData[0]
+	lea	(%rsi,%r10),%r10	# end of prologue label
+	cmp	%r10,%rbx		# context->Rip<.Lbody
+	jb	.Lin_prologue
+
+	mov	152($context),%rax	# pull context->Rsp
+
+	mov	4(%r11),%r10d		# HandlerData[1]
+	lea	(%rsi,%r10),%r10	# epilogue label
+	cmp	%r10,%rbx		# context->Rip>=.Lepilogue
+	jae	.Lin_prologue
+
+	mov	`16*17`(%rax),%rax	# pull saved stack pointer
+
+	mov	-8(%rax),%rbx
+	mov	-16(%rax),%rbp
+	mov	%rbx,144($context)	# restore context->Rbx
+	mov	%rbp,160($context)	# restore context->Rbp
+
+	lea	-24-10*16(%rax),%rsi
+	lea	512($context),%rdi	# &context.Xmm6
+	mov	\$20,%ecx
+	.long	0xa548f3fc		# cld; rep movsq
+
+.Lin_prologue:
+	mov	8(%rax),%rdi
+	mov	16(%rax),%rsi
+	mov	%rax,152($context)	# restore context->Rsp
+	mov	%rsi,168($context)	# restore context->Rsi
+	mov	%rdi,176($context)	# restore context->Rdi
+
+	mov	40($disp),%rdi		# disp->ContextRecord
+	mov	$context,%rsi		# context
+	mov	\$154,%ecx		# sizeof(CONTEXT)
+	.long	0xa548f3fc		# cld; rep movsq
+
+	mov	$disp,%rsi
+	xor	%rcx,%rcx		# arg1, UNW_FLAG_NHANDLER
+	mov	8(%rsi),%rdx		# arg2, disp->ImageBase
+	mov	0(%rsi),%r8		# arg3, disp->ControlPc
+	mov	16(%rsi),%r9		# arg4, disp->FunctionEntry
+	mov	40(%rsi),%r10		# disp->ContextRecord
+	lea	56(%rsi),%r11		# &disp->HandlerData
+	lea	24(%rsi),%r12		# &disp->EstablisherFrame
+	mov	%r10,32(%rsp)		# arg5
+	mov	%r11,40(%rsp)		# arg6
+	mov	%r12,48(%rsp)		# arg7
+	mov	%rcx,56(%rsp)		# arg8, (NULL)
+	call	*__imp_RtlVirtualUnwind(%rip)
+
+	mov	\$1,%eax		# ExceptionContinueSearch
+	add	\$64,%rsp
+	popfq
+	pop	%r15
+	pop	%r14
+	pop	%r13
+	pop	%r12
+	pop	%rbp
+	pop	%rbx
+	pop	%rdi
+	pop	%rsi
+	ret
+.size	se_handler,.-se_handler
+___
+$code.=<<___ if ($avx>1);
+.type	avx2_handler,\@abi-omnipotent
+.align	16
+avx2_handler:
+	push	%rsi
+	push	%rdi
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+	pushfq
+	sub	\$64,%rsp
+
+	mov	120($context),%rax	# pull context->Rax
+	mov	248($context),%rbx	# pull context->Rip
+
+	mov	8($disp),%rsi		# disp->ImageBase
+	mov	56($disp),%r11		# disp->HandlerData
+
+	mov	0(%r11),%r10d		# HandlerData[0]
+	lea	(%rsi,%r10),%r10	# end of prologue label
+	cmp	%r10,%rbx		# context->RipRsp
+
+	mov	4(%r11),%r10d		# HandlerData[1]
+	lea	(%rsi,%r10),%r10	# epilogue label
+	cmp	%r10,%rbx		# context->Rip>=epilogue label
+	jae	.Lin_prologue
+
+	mov	`32*17`($context),%rax	# pull saved stack pointer
+
+	mov	-8(%rax),%rbx
+	mov	-16(%rax),%rbp
+	mov	-24(%rax),%r12
+	mov	-32(%rax),%r13
+	mov	-40(%rax),%r14
+	mov	-48(%rax),%r15
+	mov	%rbx,144($context)	# restore context->Rbx
+	mov	%rbp,160($context)	# restore context->Rbp
+	mov	%r12,216($context)	# restore cotnext->R12
+	mov	%r13,224($context)	# restore cotnext->R13
+	mov	%r14,232($context)	# restore cotnext->R14
+	mov	%r15,240($context)	# restore cotnext->R15
+
+	lea	-56-10*16(%rax),%rsi
+	lea	512($context),%rdi	# &context.Xmm6
+	mov	\$20,%ecx
+	.long	0xa548f3fc		# cld; rep movsq
+
+	jmp	.Lin_prologue
+.size	avx2_handler,.-avx2_handler
+___
+$code.=<<___;
+.section	.pdata
+.align	4
+	.rva	.LSEH_begin_sha256_multi_block
+	.rva	.LSEH_end_sha256_multi_block
+	.rva	.LSEH_info_sha256_multi_block
+	.rva	.LSEH_begin_sha256_multi_block_shaext
+	.rva	.LSEH_end_sha256_multi_block_shaext
+	.rva	.LSEH_info_sha256_multi_block_shaext
+___
+$code.=<<___ if ($avx);
+	.rva	.LSEH_begin_sha256_multi_block_avx
+	.rva	.LSEH_end_sha256_multi_block_avx
+	.rva	.LSEH_info_sha256_multi_block_avx
+___
+$code.=<<___ if ($avx>1);
+	.rva	.LSEH_begin_sha256_multi_block_avx2
+	.rva	.LSEH_end_sha256_multi_block_avx2
+	.rva	.LSEH_info_sha256_multi_block_avx2
+___
+$code.=<<___;
+.section	.xdata
+.align	8
+.LSEH_info_sha256_multi_block:
+	.byte	9,0,0,0
+	.rva	se_handler
+	.rva	.Lbody,.Lepilogue			# HandlerData[]
+.LSEH_info_sha256_multi_block_shaext:
+	.byte	9,0,0,0
+	.rva	se_handler
+	.rva	.Lbody_shaext,.Lepilogue_shaext		# HandlerData[]
+___
+$code.=<<___ if ($avx);
+.LSEH_info_sha256_multi_block_avx:
+	.byte	9,0,0,0
+	.rva	se_handler
+	.rva	.Lbody_avx,.Lepilogue_avx		# HandlerData[]
+___
+$code.=<<___ if ($avx>1);
+.LSEH_info_sha256_multi_block_avx2:
+	.byte	9,0,0,0
+	.rva	avx2_handler
+	.rva	.Lbody_avx2,.Lepilogue_avx2		# HandlerData[]
+___
+}
+####################################################################
+
+sub rex {
+  local *opcode=shift;
+  my ($dst,$src)=@_;
+  my $rex=0;
+
+    $rex|=0x04			if ($dst>=8);
+    $rex|=0x01			if ($src>=8);
+    unshift @opcode,$rex|0x40	if ($rex);
+}
+
+sub sha256op38 {
+    my $instr = shift;
+    my %opcodelet = (
+		"sha256rnds2" => 0xcb,
+  		"sha256msg1"  => 0xcc,
+		"sha256msg2"  => 0xcd	);
+
+    if (defined($opcodelet{$instr}) && @_[0] =~ /%xmm([0-9]+),\s*%xmm([0-9]+)/) {
+      my @opcode=(0x0f,0x38);
+	rex(\@opcode,$2,$1);
+	push @opcode,$opcodelet{$instr};
+	push @opcode,0xc0|($1&7)|(($2&7)<<3);		# ModR/M
+	return ".byte\t".join(',',@opcode);
+    } else {
+	return $instr."\t".@_[0];
+    }
+}
+
+foreach (split("\n",$code)) {
+	s/\`([^\`]*)\`/eval($1)/ge;
+
+	s/\b(sha256[^\s]*)\s+(.*)/sha256op38($1,$2)/geo		or
+
+	s/\b(vmov[dq])\b(.+)%ymm([0-9]+)/$1$2%xmm$3/go		or
+	s/\b(vmovdqu)\b(.+)%x%ymm([0-9]+)/$1$2%xmm$3/go		or
+	s/\b(vpinsr[qd])\b(.+)%ymm([0-9]+),%ymm([0-9]+)/$1$2%xmm$3,%xmm$4/go	or
+	s/\b(vpextr[qd])\b(.+)%ymm([0-9]+)/$1$2%xmm$3/go	or
+	s/\b(vinserti128)\b(\s+)%ymm/$1$2\$1,%xmm/go		or
+	s/\b(vpbroadcast[qd]\s+)%ymm([0-9]+)/$1%xmm$2/go;
+
+	print $_,"\n";
+}
+
+close STDOUT;
diff --git a/crypto/sha/asm/sha512-586.pl b/crypto/sha/asm/sha512-586.pl
new file mode 100644
index 000000000..3873934b6
--- /dev/null
+++ b/crypto/sha/asm/sha512-586.pl
@@ -0,0 +1,924 @@
+#! /usr/bin/env perl
+# Copyright 2007-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+#
+# ====================================================================
+# Written by Andy Polyakov  for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+#
+# SHA512 block transform for x86. September 2007.
+#
+# May 2013.
+#
+# Add SSSE3 code path, 20-25% improvement [over original SSE2 code].
+#
+# Performance in clock cycles per processed byte (less is better):
+#
+#		gcc	icc	x86 asm	SIMD(*)	x86_64(**)
+# Pentium	100	97	61	-	-
+# PIII		75	77	56	-	-
+# P4		116	95	82	34.6	30.8
+# AMD K8	54	55	36	20.7	9.57
+# Core2		66	57	40	15.9	9.97
+# Westmere	70	-	38	12.2	9.58
+# Sandy Bridge	58	-	35	11.9	11.2
+# Ivy Bridge	50	-	33	11.5	8.17
+# Haswell	46	-	29	11.3	7.66
+# Bulldozer	121	-	50	14.0	13.5
+# VIA Nano	91	-	52	33	14.7
+# Atom		126	-	68	48(***)	14.7
+# Silvermont	97	-	58	42(***)	17.5
+# Goldmont	80	-	48	19.5	12.0
+#
+# (*)	whichever best applicable.
+# (**)	x86_64 assembler performance is presented for reference
+#	purposes, the results are for integer-only code.
+# (***)	paddq is increadibly slow on Atom.
+#
+# IALU code-path is optimized for elder Pentiums. On vanilla Pentium
+# performance improvement over compiler generated code reaches ~60%,
+# while on PIII - ~35%. On newer µ-archs improvement varies from 15%
+# to 50%, but it's less important as they are expected to execute SSE2
+# code-path, which is commonly ~2-3x faster [than compiler generated
+# code]. SSE2 code-path is as fast as original sha512-sse2.pl, even
+# though it does not use 128-bit operations. The latter means that
+# SSE2-aware kernel is no longer required to execute the code. Another
+# difference is that new code optimizes amount of writes, but at the
+# cost of increased data cache "footprint" by 1/2KB.
+
+$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+push(@INC,"${dir}","${dir}../../perlasm");
+require "x86asm.pl";
+
+$output=pop;
+open STDOUT,">$output";
+
+&asm_init($ARGV[0],"sha512-586.pl",$ARGV[$#ARGV] eq "386");
+
+$sse2=0;
+for (@ARGV) { $sse2=1 if (/-DOPENSSL_IA32_SSE2/); }
+
+&external_label("OPENSSL_ia32cap_P") if ($sse2);
+
+$Tlo=&DWP(0,"esp");	$Thi=&DWP(4,"esp");
+$Alo=&DWP(8,"esp");	$Ahi=&DWP(8+4,"esp");
+$Blo=&DWP(16,"esp");	$Bhi=&DWP(16+4,"esp");
+$Clo=&DWP(24,"esp");	$Chi=&DWP(24+4,"esp");
+$Dlo=&DWP(32,"esp");	$Dhi=&DWP(32+4,"esp");
+$Elo=&DWP(40,"esp");	$Ehi=&DWP(40+4,"esp");
+$Flo=&DWP(48,"esp");	$Fhi=&DWP(48+4,"esp");
+$Glo=&DWP(56,"esp");	$Ghi=&DWP(56+4,"esp");
+$Hlo=&DWP(64,"esp");	$Hhi=&DWP(64+4,"esp");
+$K512="ebp";
+
+$Asse2=&QWP(0,"esp");
+$Bsse2=&QWP(8,"esp");
+$Csse2=&QWP(16,"esp");
+$Dsse2=&QWP(24,"esp");
+$Esse2=&QWP(32,"esp");
+$Fsse2=&QWP(40,"esp");
+$Gsse2=&QWP(48,"esp");
+$Hsse2=&QWP(56,"esp");
+
+$A="mm0";	# B-D and
+$E="mm4";	# F-H are commonly loaded to respectively mm1-mm3 and
+		# mm5-mm7, but it's done on on-demand basis...
+$BxC="mm2";	# ... except for B^C
+
+sub BODY_00_15_sse2 {
+    my $phase=shift;
+
+	#&movq	("mm5",$Fsse2);			# load f
+	#&movq	("mm6",$Gsse2);			# load g
+
+	&movq	("mm1",$E);			# %mm1 is sliding right
+	 &pxor	("mm5","mm6");			# f^=g
+	&psrlq	("mm1",14);
+	 &movq	($Esse2,$E);			# modulo-scheduled save e
+	 &pand	("mm5",$E);			# f&=e
+	&psllq	($E,23);			# $E is sliding left
+	 &movq	($A,"mm3")			if ($phase<2);
+	 &movq	(&QWP(8*9,"esp"),"mm7")		# save X[i]
+	&movq	("mm3","mm1");			# %mm3 is T1
+	 &psrlq	("mm1",4);
+	 &pxor	("mm5","mm6");			# Ch(e,f,g)
+	&pxor	("mm3",$E);
+	 &psllq	($E,23);
+	&pxor	("mm3","mm1");
+	 &movq	($Asse2,$A);			# modulo-scheduled save a
+	 &paddq	("mm7","mm5");			# X[i]+=Ch(e,f,g)
+	&pxor	("mm3",$E);
+	 &psrlq	("mm1",23);
+	 &paddq	("mm7",$Hsse2);			# X[i]+=h
+	&pxor	("mm3","mm1");
+	 &psllq	($E,4);
+	 &paddq	("mm7",QWP(0,$K512));		# X[i]+=K512[i]
+	&pxor	("mm3",$E);			# T1=Sigma1_512(e)
+
+	 &movq	($E,$Dsse2);			# e = load d, e in next round
+	&paddq	("mm3","mm7");			# T1+=X[i]
+	 &movq	("mm5",$A);			# %mm5 is sliding right
+	 &psrlq	("mm5",28);
+	&paddq	($E,"mm3");			# d += T1
+	 &movq	("mm6",$A);			# %mm6 is sliding left
+	 &movq	("mm7","mm5");
+	 &psllq	("mm6",25);
+	&movq	("mm1",$Bsse2);			# load b
+	 &psrlq	("mm5",6);
+	 &pxor	("mm7","mm6");
+	&sub	("esp",8);
+	 &psllq	("mm6",5);
+	 &pxor	("mm7","mm5");
+	&pxor	($A,"mm1");			# a^b, b^c in next round
+	 &psrlq	("mm5",5);
+	 &pxor	("mm7","mm6");
+	&pand	($BxC,$A);			# (b^c)&(a^b)
+	 &psllq	("mm6",6);
+	 &pxor	("mm7","mm5");
+	&pxor	($BxC,"mm1");			# [h=]Maj(a,b,c)
+	 &pxor	("mm6","mm7");			# Sigma0_512(a)
+	 &movq	("mm7",&QWP(8*(9+16-1),"esp"))	if ($phase!=0);	# pre-fetch
+	 &movq	("mm5",$Fsse2)			if ($phase==0);	# load f
+
+    if ($phase>1) {
+	&paddq	($BxC,"mm6");			# h+=Sigma0(a)
+	 &add	($K512,8);
+	#&paddq	($BxC,"mm3");			# h+=T1
+
+	($A,$BxC) = ($BxC,$A);			# rotate registers
+    } else {
+	&paddq	("mm3",$BxC);			# T1+=Maj(a,b,c)
+	 &movq	($BxC,$A);
+	 &add	($K512,8);
+	&paddq	("mm3","mm6");			# T1+=Sigma0(a)
+	 &movq	("mm6",$Gsse2)			if ($phase==0);	# load g
+	#&movq	($A,"mm3");			# h=T1
+    }
+}
+
+sub BODY_00_15_x86 {
+	#define Sigma1(x)	(ROTR((x),14) ^ ROTR((x),18)  ^ ROTR((x),41))
+	#	LO		lo>>14^hi<<18 ^ lo>>18^hi<<14 ^ hi>>9^lo<<23
+	#	HI		hi>>14^lo<<18 ^ hi>>18^lo<<14 ^ lo>>9^hi<<23
+	&mov	("ecx",$Elo);
+	&mov	("edx",$Ehi);
+	&mov	("esi","ecx");
+
+	&shr	("ecx",9);	# lo>>9
+	&mov	("edi","edx");
+	&shr	("edx",9);	# hi>>9
+	&mov	("ebx","ecx");
+	&shl	("esi",14);	# lo<<14
+	&mov	("eax","edx");
+	&shl	("edi",14);	# hi<<14
+	&xor	("ebx","esi");
+
+	&shr	("ecx",14-9);	# lo>>14
+	&xor	("eax","edi");
+	&shr	("edx",14-9);	# hi>>14
+	&xor	("eax","ecx");
+	&shl	("esi",18-14);	# lo<<18
+	&xor	("ebx","edx");
+	&shl	("edi",18-14);	# hi<<18
+	&xor	("ebx","esi");
+
+	&shr	("ecx",18-14);	# lo>>18
+	&xor	("eax","edi");
+	&shr	("edx",18-14);	# hi>>18
+	&xor	("eax","ecx");
+	&shl	("esi",23-18);	# lo<<23
+	&xor	("ebx","edx");
+	&shl	("edi",23-18);	# hi<<23
+	&xor	("eax","esi");
+	&xor	("ebx","edi");			# T1 = Sigma1(e)
+
+	&mov	("ecx",$Flo);
+	&mov	("edx",$Fhi);
+	&mov	("esi",$Glo);
+	&mov	("edi",$Ghi);
+	 &add	("eax",$Hlo);
+	 &adc	("ebx",$Hhi);			# T1 += h
+	&xor	("ecx","esi");
+	&xor	("edx","edi");
+	&and	("ecx",$Elo);
+	&and	("edx",$Ehi);
+	 &add	("eax",&DWP(8*(9+15)+0,"esp"));
+	 &adc	("ebx",&DWP(8*(9+15)+4,"esp"));	# T1 += X[0]
+	&xor	("ecx","esi");
+	&xor	("edx","edi");			# Ch(e,f,g) = (f^g)&e)^g
+
+	&mov	("esi",&DWP(0,$K512));
+	&mov	("edi",&DWP(4,$K512));		# K[i]
+	&add	("eax","ecx");
+	&adc	("ebx","edx");			# T1 += Ch(e,f,g)
+	&mov	("ecx",$Dlo);
+	&mov	("edx",$Dhi);
+	&add	("eax","esi");
+	&adc	("ebx","edi");			# T1 += K[i]
+	&mov	($Tlo,"eax");
+	&mov	($Thi,"ebx");			# put T1 away
+	&add	("eax","ecx");
+	&adc	("ebx","edx");			# d += T1
+
+	#define Sigma0(x)	(ROTR((x),28) ^ ROTR((x),34) ^ ROTR((x),39))
+	#	LO		lo>>28^hi<<4  ^ hi>>2^lo<<30 ^ hi>>7^lo<<25
+	#	HI		hi>>28^lo<<4  ^ lo>>2^hi<<30 ^ lo>>7^hi<<25
+	&mov	("ecx",$Alo);
+	&mov	("edx",$Ahi);
+	&mov	($Dlo,"eax");
+	&mov	($Dhi,"ebx");
+	&mov	("esi","ecx");
+
+	&shr	("ecx",2);	# lo>>2
+	&mov	("edi","edx");
+	&shr	("edx",2);	# hi>>2
+	&mov	("ebx","ecx");
+	&shl	("esi",4);	# lo<<4
+	&mov	("eax","edx");
+	&shl	("edi",4);	# hi<<4
+	&xor	("ebx","esi");
+
+	&shr	("ecx",7-2);	# lo>>7
+	&xor	("eax","edi");
+	&shr	("edx",7-2);	# hi>>7
+	&xor	("ebx","ecx");
+	&shl	("esi",25-4);	# lo<<25
+	&xor	("eax","edx");
+	&shl	("edi",25-4);	# hi<<25
+	&xor	("eax","esi");
+
+	&shr	("ecx",28-7);	# lo>>28
+	&xor	("ebx","edi");
+	&shr	("edx",28-7);	# hi>>28
+	&xor	("eax","ecx");
+	&shl	("esi",30-25);	# lo<<30
+	&xor	("ebx","edx");
+	&shl	("edi",30-25);	# hi<<30
+	&xor	("eax","esi");
+	&xor	("ebx","edi");			# Sigma0(a)
+
+	&mov	("ecx",$Alo);
+	&mov	("edx",$Ahi);
+	&mov	("esi",$Blo);
+	&mov	("edi",$Bhi);
+	&add	("eax",$Tlo);
+	&adc	("ebx",$Thi);			# T1 = Sigma0(a)+T1
+	&or	("ecx","esi");
+	&or	("edx","edi");
+	&and	("ecx",$Clo);
+	&and	("edx",$Chi);
+	&and	("esi",$Alo);
+	&and	("edi",$Ahi);
+	&or	("ecx","esi");
+	&or	("edx","edi");			# Maj(a,b,c) = ((a|b)&c)|(a&b)
+
+	&add	("eax","ecx");
+	&adc	("ebx","edx");			# T1 += Maj(a,b,c)
+	&mov	($Tlo,"eax");
+	&mov	($Thi,"ebx");
+
+	&mov	(&LB("edx"),&BP(0,$K512));	# pre-fetch LSB of *K
+	&sub	("esp",8);
+	&lea	($K512,&DWP(8,$K512));		# K++
+}
+
+
+&function_begin("sha512_block_data_order");
+	&mov	("esi",wparam(0));	# ctx
+	&mov	("edi",wparam(1));	# inp
+	&mov	("eax",wparam(2));	# num
+	&mov	("ebx","esp");		# saved sp
+
+	&call	(&label("pic_point"));	# make it PIC!
+&set_label("pic_point");
+	&blindpop($K512);
+	&lea	($K512,&DWP(&label("K512")."-".&label("pic_point"),$K512));
+
+	&sub	("esp",16);
+	&and	("esp",-64);
+
+	&shl	("eax",7);
+	&add	("eax","edi");
+	&mov	(&DWP(0,"esp"),"esi");	# ctx
+	&mov	(&DWP(4,"esp"),"edi");	# inp
+	&mov	(&DWP(8,"esp"),"eax");	# inp+num*128
+	&mov	(&DWP(12,"esp"),"ebx");	# saved sp
+
+if ($sse2) {
+	&picmeup("edx","OPENSSL_ia32cap_P",$K512,&label("K512"));
+	&mov	("ecx",&DWP(0,"edx"));
+	&test	("ecx",1<<26);
+	&jz	(&label("loop_x86"));
+
+	&mov	("edx",&DWP(4,"edx"));
+
+	# load ctx->h[0-7]
+	&movq	($A,&QWP(0,"esi"));
+	 &and	("ecx",1<<24);		# XMM registers availability
+	&movq	("mm1",&QWP(8,"esi"));
+	 &and	("edx",1<<9);		# SSSE3 bit
+	&movq	($BxC,&QWP(16,"esi"));
+	 &or	("ecx","edx");
+	&movq	("mm3",&QWP(24,"esi"));
+	&movq	($E,&QWP(32,"esi"));
+	&movq	("mm5",&QWP(40,"esi"));
+	&movq	("mm6",&QWP(48,"esi"));
+	&movq	("mm7",&QWP(56,"esi"));
+	&cmp	("ecx",1<<24|1<<9);
+	&je	(&label("SSSE3"));
+	&sub	("esp",8*10);
+	&jmp	(&label("loop_sse2"));
+
+&set_label("loop_sse2",16);
+	#&movq	($Asse2,$A);
+	&movq	($Bsse2,"mm1");
+	&movq	($Csse2,$BxC);
+	&movq	($Dsse2,"mm3");
+	#&movq	($Esse2,$E);
+	&movq	($Fsse2,"mm5");
+	&movq	($Gsse2,"mm6");
+	&pxor	($BxC,"mm1");			# magic
+	&movq	($Hsse2,"mm7");
+	&movq	("mm3",$A);			# magic
+
+	&mov	("eax",&DWP(0,"edi"));
+	&mov	("ebx",&DWP(4,"edi"));
+	&add	("edi",8);
+	&mov	("edx",15);			# counter
+	&bswap	("eax");
+	&bswap	("ebx");
+	&jmp	(&label("00_14_sse2"));
+
+&set_label("00_14_sse2",16);
+	&movd	("mm1","eax");
+	&mov	("eax",&DWP(0,"edi"));
+	&movd	("mm7","ebx");
+	&mov	("ebx",&DWP(4,"edi"));
+	&add	("edi",8);
+	&bswap	("eax");
+	&bswap	("ebx");
+	&punpckldq("mm7","mm1");
+
+	&BODY_00_15_sse2();
+
+	&dec	("edx");
+	&jnz	(&label("00_14_sse2"));
+
+	&movd	("mm1","eax");
+	&movd	("mm7","ebx");
+	&punpckldq("mm7","mm1");
+
+	&BODY_00_15_sse2(1);
+
+	&pxor	($A,$A);			# A is in %mm3
+	&mov	("edx",32);			# counter
+	&jmp	(&label("16_79_sse2"));
+
+&set_label("16_79_sse2",16);
+    for ($j=0;$j<2;$j++) {			# 2x unroll
+	#&movq	("mm7",&QWP(8*(9+16-1),"esp"));	# prefetched in BODY_00_15 
+	&movq	("mm5",&QWP(8*(9+16-14),"esp"));
+	&movq	("mm1","mm7");
+	&psrlq	("mm7",1);
+	 &movq	("mm6","mm5");
+	 &psrlq	("mm5",6);
+	&psllq	("mm1",56);
+	 &paddq	($A,"mm3");			# from BODY_00_15
+	 &movq	("mm3","mm7");
+	&psrlq	("mm7",7-1);
+	 &pxor	("mm3","mm1");
+	 &psllq	("mm1",63-56);
+	&pxor	("mm3","mm7");
+	 &psrlq	("mm7",8-7);
+	&pxor	("mm3","mm1");
+	 &movq	("mm1","mm5");
+	 &psrlq	("mm5",19-6);
+	&pxor	("mm7","mm3");			# sigma0
+
+	 &psllq	("mm6",3);
+	 &pxor	("mm1","mm5");
+	&paddq	("mm7",&QWP(8*(9+16),"esp"));
+	 &pxor	("mm1","mm6");
+	 &psrlq	("mm5",61-19);
+	&paddq	("mm7",&QWP(8*(9+16-9),"esp"));
+	 &pxor	("mm1","mm5");
+	 &psllq	("mm6",45-3);
+	&movq	("mm5",$Fsse2);			# load f
+	 &pxor	("mm1","mm6");			# sigma1
+	&movq	("mm6",$Gsse2);			# load g
+
+	&paddq	("mm7","mm1");			# X[i]
+	#&movq	(&QWP(8*9,"esp"),"mm7");	# moved to BODY_00_15
+
+	&BODY_00_15_sse2(2);
+    }
+	&dec	("edx");
+	&jnz	(&label("16_79_sse2"));
+
+	#&movq	($A,$Asse2);
+	&paddq	($A,"mm3");			# from BODY_00_15
+	&movq	("mm1",$Bsse2);
+	#&movq	($BxC,$Csse2);
+	&movq	("mm3",$Dsse2);
+	#&movq	($E,$Esse2);
+	&movq	("mm5",$Fsse2);
+	&movq	("mm6",$Gsse2);
+	&movq	("mm7",$Hsse2);
+
+	&pxor	($BxC,"mm1");			# de-magic
+	&paddq	($A,&QWP(0,"esi"));
+	&paddq	("mm1",&QWP(8,"esi"));
+	&paddq	($BxC,&QWP(16,"esi"));
+	&paddq	("mm3",&QWP(24,"esi"));
+	&paddq	($E,&QWP(32,"esi"));
+	&paddq	("mm5",&QWP(40,"esi"));
+	&paddq	("mm6",&QWP(48,"esi"));
+	&paddq	("mm7",&QWP(56,"esi"));
+
+	&mov	("eax",8*80);
+	&movq	(&QWP(0,"esi"),$A);
+	&movq	(&QWP(8,"esi"),"mm1");
+	&movq	(&QWP(16,"esi"),$BxC);
+	&movq	(&QWP(24,"esi"),"mm3");
+	&movq	(&QWP(32,"esi"),$E);
+	&movq	(&QWP(40,"esi"),"mm5");
+	&movq	(&QWP(48,"esi"),"mm6");
+	&movq	(&QWP(56,"esi"),"mm7");
+
+	&lea	("esp",&DWP(0,"esp","eax"));	# destroy frame
+	&sub	($K512,"eax");			# rewind K
+
+	&cmp	("edi",&DWP(8*10+8,"esp"));	# are we done yet?
+	&jb	(&label("loop_sse2"));
+
+	&mov	("esp",&DWP(8*10+12,"esp"));	# restore sp
+	&emms	();
+&function_end_A();
+
+&set_label("SSSE3",32);
+{ my ($cnt,$frame)=("ecx","edx");
+  my @X=map("xmm$_",(0..7));
+  my $j;
+  my $i=0;
+
+	&lea	($frame,&DWP(-64,"esp"));
+	&sub	("esp",256);
+
+	# fixed stack frame layout
+	#
+	# +0	A B C D E F G H		# backing store
+	# +64	X[0]+K[i] .. X[15]+K[i]	# XMM->MM xfer area
+	# +192				# XMM off-load ring buffer
+	# +256				# saved parameters
+
+	&movdqa		(@X[1],&QWP(80*8,$K512));		# byte swap mask
+	&movdqu		(@X[0],&QWP(0,"edi"));
+	&pshufb		(@X[0],@X[1]);
+    for ($j=0;$j<8;$j++) {
+	&movdqa		(&QWP(16*(($j-1)%4),$frame),@X[3])	if ($j>4); # off-load
+	&movdqa		(@X[3],&QWP(16*($j%8),$K512));
+	&movdqa		(@X[2],@X[1])				if ($j<7); # perpetuate byte swap mask
+	&movdqu		(@X[1],&QWP(16*($j+1),"edi"))		if ($j<7); # next input
+	&movdqa		(@X[1],&QWP(16*(($j+1)%4),$frame))	if ($j==7);# restore @X[0]
+	&paddq		(@X[3],@X[0]);
+	&pshufb		(@X[1],@X[2])				if ($j<7);
+	&movdqa		(&QWP(16*($j%8)-128,$frame),@X[3]);	# xfer X[i]+K[i]
+
+	push(@X,shift(@X));					# rotate(@X)
+    }
+	#&jmp		(&label("loop_ssse3"));
+	&nop		();
+
+&set_label("loop_ssse3",32);
+	&movdqa		(@X[2],&QWP(16*(($j+1)%4),$frame));	# pre-restore @X[1]
+	&movdqa		(&QWP(16*(($j-1)%4),$frame),@X[3]);	# off-load @X[3]
+	&lea		($K512,&DWP(16*8,$K512));
+
+	#&movq	($Asse2,$A);			# off-load A-H
+	&movq	($Bsse2,"mm1");
+	 &mov	("ebx","edi");
+	&movq	($Csse2,$BxC);
+	 &lea	("edi",&DWP(128,"edi"));	# advance input
+	&movq	($Dsse2,"mm3");
+	 &cmp	("edi","eax");
+	#&movq	($Esse2,$E);
+	&movq	($Fsse2,"mm5");
+	 &cmovb	("ebx","edi");
+	&movq	($Gsse2,"mm6");
+	 &mov	("ecx",4);			# loop counter
+	&pxor	($BxC,"mm1");			# magic
+	&movq	($Hsse2,"mm7");
+	&pxor	("mm3","mm3");			# magic
+
+	&jmp		(&label("00_47_ssse3"));
+
+sub BODY_00_15_ssse3 {		# "phase-less" copy of BODY_00_15_sse2
+	(
+	'&movq	("mm1",$E)',				# %mm1 is sliding right
+	'&movq	("mm7",&QWP(((-8*$i)%128)-128,$frame))',# X[i]+K[i]
+	 '&pxor	("mm5","mm6")',				# f^=g
+	'&psrlq	("mm1",14)',
+	 '&movq	(&QWP(8*($i+4)%64,"esp"),$E)',		# modulo-scheduled save e
+	 '&pand	("mm5",$E)',				# f&=e
+	'&psllq	($E,23)',				# $E is sliding left
+	'&paddq	($A,"mm3")',				# [h+=Maj(a,b,c)]
+	'&movq	("mm3","mm1")',				# %mm3 is T1
+	 '&psrlq("mm1",4)',
+	 '&pxor	("mm5","mm6")',				# Ch(e,f,g)
+	'&pxor	("mm3",$E)',
+	 '&psllq($E,23)',
+	'&pxor	("mm3","mm1")',
+	 '&movq	(&QWP(8*$i%64,"esp"),$A)',		# modulo-scheduled save a
+	 '&paddq("mm7","mm5")',				# X[i]+=Ch(e,f,g)
+	'&pxor	("mm3",$E)',
+	 '&psrlq("mm1",23)',
+	 '&paddq("mm7",&QWP(8*($i+7)%64,"esp"))',	# X[i]+=h
+	'&pxor	("mm3","mm1")',
+	 '&psllq($E,4)',
+	'&pxor	("mm3",$E)',				# T1=Sigma1_512(e)
+
+	 '&movq	($E,&QWP(8*($i+3)%64,"esp"))',		# e = load d, e in next round
+	'&paddq	("mm3","mm7")',				# T1+=X[i]
+	 '&movq	("mm5",$A)',				# %mm5 is sliding right
+	 '&psrlq("mm5",28)',
+	'&paddq	($E,"mm3")',				# d += T1
+	 '&movq	("mm6",$A)',				# %mm6 is sliding left
+	 '&movq	("mm7","mm5")',
+	 '&psllq("mm6",25)',
+	'&movq	("mm1",&QWP(8*($i+1)%64,"esp"))',	# load b
+	 '&psrlq("mm5",6)',
+	 '&pxor	("mm7","mm6")',
+	 '&psllq("mm6",5)',
+	 '&pxor	("mm7","mm5")',
+	'&pxor	($A,"mm1")',				# a^b, b^c in next round
+	 '&psrlq("mm5",5)',
+	 '&pxor	("mm7","mm6")',
+	'&pand	($BxC,$A)',				# (b^c)&(a^b)
+	 '&psllq("mm6",6)',
+	 '&pxor	("mm7","mm5")',
+	'&pxor	($BxC,"mm1")',				# [h=]Maj(a,b,c)
+	 '&pxor	("mm6","mm7")',				# Sigma0_512(a)
+	 '&movq	("mm5",&QWP(8*($i+5-1)%64,"esp"))',	# pre-load f
+	'&paddq	($BxC,"mm6")',				# h+=Sigma0(a)
+	 '&movq	("mm6",&QWP(8*($i+6-1)%64,"esp"))',	# pre-load g
+
+	'($A,$BxC) = ($BxC,$A); $i--;'
+	);
+}
+
+&set_label("00_47_ssse3",32);
+
+    for(;$j<16;$j++) {
+	my ($t0,$t2,$t1)=@X[2..4];
+	my @insns = (&BODY_00_15_ssse3(),&BODY_00_15_ssse3());
+
+	&movdqa		($t2,@X[5]);
+	&movdqa		(@X[1],$t0);			# restore @X[1]
+	&palignr	($t0,@X[0],8);			# X[1..2]
+	&movdqa		(&QWP(16*($j%4),$frame),@X[4]);	# off-load @X[4]
+	 &palignr	($t2,@X[4],8);			# X[9..10]
+
+	&movdqa		($t1,$t0);
+	&psrlq		($t0,7);
+	 &paddq		(@X[0],$t2);			# X[0..1] += X[9..10]
+	&movdqa		($t2,$t1);
+	&psrlq		($t1,1);
+	&psllq		($t2,64-8);
+	&pxor		($t0,$t1);
+	&psrlq		($t1,8-1);
+	&pxor		($t0,$t2);
+	&psllq		($t2,8-1);
+	&pxor		($t0,$t1);
+	 &movdqa	($t1,@X[7]);
+	&pxor		($t0,$t2);			# sigma0(X[1..2])
+	 &movdqa	($t2,@X[7]);
+	 &psrlq		($t1,6);
+	&paddq		(@X[0],$t0);			# X[0..1] += sigma0(X[1..2])
+
+	&movdqa		($t0,@X[7]);
+	&psrlq		($t2,19);
+	&psllq		($t0,64-61);
+	&pxor		($t1,$t2);
+	&psrlq		($t2,61-19);
+	&pxor		($t1,$t0);
+	&psllq		($t0,61-19);
+	&pxor		($t1,$t2);
+	&movdqa		($t2,&QWP(16*(($j+2)%4),$frame));# pre-restore @X[1]
+	&pxor		($t1,$t0);			# sigma0(X[1..2])
+	&movdqa		($t0,&QWP(16*($j%8),$K512));
+	 eval(shift(@insns));
+	&paddq		(@X[0],$t1);			# X[0..1] += sigma0(X[14..15])
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	 eval(shift(@insns));
+	&paddq		($t0,@X[0]);
+	 foreach(@insns) { eval; }
+	&movdqa		(&QWP(16*($j%8)-128,$frame),$t0);# xfer X[i]+K[i]
+
+	push(@X,shift(@X));				# rotate(@X)
+    }
+	&lea		($K512,&DWP(16*8,$K512));
+	&dec		("ecx");
+	&jnz		(&label("00_47_ssse3"));
+
+	&movdqa		(@X[1],&QWP(0,$K512));		# byte swap mask
+	&lea		($K512,&DWP(-80*8,$K512));	# rewind
+	&movdqu		(@X[0],&QWP(0,"ebx"));
+	&pshufb		(@X[0],@X[1]);
+
+    for ($j=0;$j<8;$j++) {	# load next or same block
+	my @insns = (&BODY_00_15_ssse3(),&BODY_00_15_ssse3());
+
+	&movdqa		(&QWP(16*(($j-1)%4),$frame),@X[3])	if ($j>4); # off-load
+	&movdqa		(@X[3],&QWP(16*($j%8),$K512));
+	&movdqa		(@X[2],@X[1])				if ($j<7); # perpetuate byte swap mask
+	&movdqu		(@X[1],&QWP(16*($j+1),"ebx"))		if ($j<7); # next input
+	&movdqa		(@X[1],&QWP(16*(($j+1)%4),$frame))	if ($j==7);# restore @X[0]
+	&paddq		(@X[3],@X[0]);
+	&pshufb		(@X[1],@X[2])				if ($j<7);
+	 foreach(@insns) { eval; }
+	&movdqa		(&QWP(16*($j%8)-128,$frame),@X[3]);# xfer X[i]+K[i]
+
+	push(@X,shift(@X));				# rotate(@X)
+    }
+
+	#&movq	($A,$Asse2);			# load A-H
+	&movq	("mm1",$Bsse2);
+	&paddq	($A,"mm3");			# from BODY_00_15
+	#&movq	($BxC,$Csse2);
+	&movq	("mm3",$Dsse2);
+	#&movq	($E,$Esse2);
+	#&movq	("mm5",$Fsse2);
+	#&movq	("mm6",$Gsse2);
+	&movq	("mm7",$Hsse2);
+
+	&pxor	($BxC,"mm1");			# de-magic
+	&paddq	($A,&QWP(0,"esi"));
+	&paddq	("mm1",&QWP(8,"esi"));
+	&paddq	($BxC,&QWP(16,"esi"));
+	&paddq	("mm3",&QWP(24,"esi"));
+	&paddq	($E,&QWP(32,"esi"));
+	&paddq	("mm5",&QWP(40,"esi"));
+	&paddq	("mm6",&QWP(48,"esi"));
+	&paddq	("mm7",&QWP(56,"esi"));
+
+	&movq	(&QWP(0,"esi"),$A);
+	&movq	(&QWP(8,"esi"),"mm1");
+	&movq	(&QWP(16,"esi"),$BxC);
+	&movq	(&QWP(24,"esi"),"mm3");
+	&movq	(&QWP(32,"esi"),$E);
+	&movq	(&QWP(40,"esi"),"mm5");
+	&movq	(&QWP(48,"esi"),"mm6");
+	&movq	(&QWP(56,"esi"),"mm7");
+
+    	&cmp	("edi","eax")			# are we done yet?
+	&jb	(&label("loop_ssse3"));
+
+	&mov	("esp",&DWP(64+12,$frame));	# restore sp
+	&emms	();
+}
+&function_end_A();
+}
+&set_label("loop_x86",16);
+    # copy input block to stack reversing byte and qword order
+    for ($i=0;$i<8;$i++) {
+	&mov	("eax",&DWP($i*16+0,"edi"));
+	&mov	("ebx",&DWP($i*16+4,"edi"));
+	&mov	("ecx",&DWP($i*16+8,"edi"));
+	&mov	("edx",&DWP($i*16+12,"edi"));
+	&bswap	("eax");
+	&bswap	("ebx");
+	&bswap	("ecx");
+	&bswap	("edx");
+	&push	("eax");
+	&push	("ebx");
+	&push	("ecx");
+	&push	("edx");
+    }
+	&add	("edi",128);
+	&sub	("esp",9*8);		# place for T,A,B,C,D,E,F,G,H
+	&mov	(&DWP(8*(9+16)+4,"esp"),"edi");
+
+	# copy ctx->h[0-7] to A,B,C,D,E,F,G,H on stack
+	&lea	("edi",&DWP(8,"esp"));
+	&mov	("ecx",16);
+	&data_word(0xA5F3F689);		# rep movsd
+
+&set_label("00_15_x86",16);
+	&BODY_00_15_x86();
+
+	&cmp	(&LB("edx"),0x94);
+	&jne	(&label("00_15_x86"));
+
+&set_label("16_79_x86",16);
+	#define sigma0(x)	(ROTR((x),1)  ^ ROTR((x),8)  ^ ((x)>>7))
+	#	LO		lo>>1^hi<<31  ^ lo>>8^hi<<24 ^ lo>>7^hi<<25
+	#	HI		hi>>1^lo<<31  ^ hi>>8^lo<<24 ^ hi>>7
+	&mov	("ecx",&DWP(8*(9+15+16-1)+0,"esp"));
+	&mov	("edx",&DWP(8*(9+15+16-1)+4,"esp"));
+	&mov	("esi","ecx");
+
+	&shr	("ecx",1);	# lo>>1
+	&mov	("edi","edx");
+	&shr	("edx",1);	# hi>>1
+	&mov	("eax","ecx");
+	&shl	("esi",24);	# lo<<24
+	&mov	("ebx","edx");
+	&shl	("edi",24);	# hi<<24
+	&xor	("ebx","esi");
+
+	&shr	("ecx",7-1);	# lo>>7
+	&xor	("eax","edi");
+	&shr	("edx",7-1);	# hi>>7
+	&xor	("eax","ecx");
+	&shl	("esi",31-24);	# lo<<31
+	&xor	("ebx","edx");
+	&shl	("edi",25-24);	# hi<<25
+	&xor	("ebx","esi");
+
+	&shr	("ecx",8-7);	# lo>>8
+	&xor	("eax","edi");
+	&shr	("edx",8-7);	# hi>>8
+	&xor	("eax","ecx");
+	&shl	("edi",31-25);	# hi<<31
+	&xor	("ebx","edx");
+	&xor	("eax","edi");			# T1 = sigma0(X[-15])
+
+	&mov	(&DWP(0,"esp"),"eax");
+	&mov	(&DWP(4,"esp"),"ebx");		# put T1 away
+
+	#define sigma1(x)	(ROTR((x),19) ^ ROTR((x),61) ^ ((x)>>6))
+	#	LO		lo>>19^hi<<13 ^ hi>>29^lo<<3 ^ lo>>6^hi<<26
+	#	HI		hi>>19^lo<<13 ^ lo>>29^hi<<3 ^ hi>>6
+	&mov	("ecx",&DWP(8*(9+15+16-14)+0,"esp"));
+	&mov	("edx",&DWP(8*(9+15+16-14)+4,"esp"));
+	&mov	("esi","ecx");
+
+	&shr	("ecx",6);	# lo>>6
+	&mov	("edi","edx");
+	&shr	("edx",6);	# hi>>6
+	&mov	("eax","ecx");
+	&shl	("esi",3);	# lo<<3
+	&mov	("ebx","edx");
+	&shl	("edi",3);	# hi<<3
+	&xor	("eax","esi");
+
+	&shr	("ecx",19-6);	# lo>>19
+	&xor	("ebx","edi");
+	&shr	("edx",19-6);	# hi>>19
+	&xor	("eax","ecx");
+	&shl	("esi",13-3);	# lo<<13
+	&xor	("ebx","edx");
+	&shl	("edi",13-3);	# hi<<13
+	&xor	("ebx","esi");
+
+	&shr	("ecx",29-19);	# lo>>29
+	&xor	("eax","edi");
+	&shr	("edx",29-19);	# hi>>29
+	&xor	("ebx","ecx");
+	&shl	("edi",26-13);	# hi<<26
+	&xor	("eax","edx");
+	&xor	("eax","edi");			# sigma1(X[-2])
+
+	&mov	("ecx",&DWP(8*(9+15+16)+0,"esp"));
+	&mov	("edx",&DWP(8*(9+15+16)+4,"esp"));
+	&add	("eax",&DWP(0,"esp"));
+	&adc	("ebx",&DWP(4,"esp"));		# T1 = sigma1(X[-2])+T1
+	&mov	("esi",&DWP(8*(9+15+16-9)+0,"esp"));
+	&mov	("edi",&DWP(8*(9+15+16-9)+4,"esp"));
+	&add	("eax","ecx");
+	&adc	("ebx","edx");			# T1 += X[-16]
+	&add	("eax","esi");
+	&adc	("ebx","edi");			# T1 += X[-7]
+	&mov	(&DWP(8*(9+15)+0,"esp"),"eax");
+	&mov	(&DWP(8*(9+15)+4,"esp"),"ebx");	# save X[0]
+
+	&BODY_00_15_x86();
+
+	&cmp	(&LB("edx"),0x17);
+	&jne	(&label("16_79_x86"));
+
+	&mov	("esi",&DWP(8*(9+16+80)+0,"esp"));# ctx
+	&mov	("edi",&DWP(8*(9+16+80)+4,"esp"));# inp
+    for($i=0;$i<4;$i++) {
+	&mov	("eax",&DWP($i*16+0,"esi"));
+	&mov	("ebx",&DWP($i*16+4,"esi"));
+	&mov	("ecx",&DWP($i*16+8,"esi"));
+	&mov	("edx",&DWP($i*16+12,"esi"));
+	&add	("eax",&DWP(8+($i*16)+0,"esp"));
+	&adc	("ebx",&DWP(8+($i*16)+4,"esp"));
+	&mov	(&DWP($i*16+0,"esi"),"eax");
+	&mov	(&DWP($i*16+4,"esi"),"ebx");
+	&add	("ecx",&DWP(8+($i*16)+8,"esp"));
+	&adc	("edx",&DWP(8+($i*16)+12,"esp"));
+	&mov	(&DWP($i*16+8,"esi"),"ecx");
+	&mov	(&DWP($i*16+12,"esi"),"edx");
+    }
+	&add	("esp",8*(9+16+80));		# destroy frame
+	&sub	($K512,8*80);			# rewind K
+
+	&cmp	("edi",&DWP(8,"esp"));		# are we done yet?
+	&jb	(&label("loop_x86"));
+
+	&mov	("esp",&DWP(12,"esp"));		# restore sp
+&function_end_A();
+
+&set_label("K512",64);	# Yes! I keep it in the code segment!
+	&data_word(0xd728ae22,0x428a2f98);	# u64
+	&data_word(0x23ef65cd,0x71374491);	# u64
+	&data_word(0xec4d3b2f,0xb5c0fbcf);	# u64
+	&data_word(0x8189dbbc,0xe9b5dba5);	# u64
+	&data_word(0xf348b538,0x3956c25b);	# u64
+	&data_word(0xb605d019,0x59f111f1);	# u64
+	&data_word(0xaf194f9b,0x923f82a4);	# u64
+	&data_word(0xda6d8118,0xab1c5ed5);	# u64
+	&data_word(0xa3030242,0xd807aa98);	# u64
+	&data_word(0x45706fbe,0x12835b01);	# u64
+	&data_word(0x4ee4b28c,0x243185be);	# u64
+	&data_word(0xd5ffb4e2,0x550c7dc3);	# u64
+	&data_word(0xf27b896f,0x72be5d74);	# u64
+	&data_word(0x3b1696b1,0x80deb1fe);	# u64
+	&data_word(0x25c71235,0x9bdc06a7);	# u64
+	&data_word(0xcf692694,0xc19bf174);	# u64
+	&data_word(0x9ef14ad2,0xe49b69c1);	# u64
+	&data_word(0x384f25e3,0xefbe4786);	# u64
+	&data_word(0x8b8cd5b5,0x0fc19dc6);	# u64
+	&data_word(0x77ac9c65,0x240ca1cc);	# u64
+	&data_word(0x592b0275,0x2de92c6f);	# u64
+	&data_word(0x6ea6e483,0x4a7484aa);	# u64
+	&data_word(0xbd41fbd4,0x5cb0a9dc);	# u64
+	&data_word(0x831153b5,0x76f988da);	# u64
+	&data_word(0xee66dfab,0x983e5152);	# u64
+	&data_word(0x2db43210,0xa831c66d);	# u64
+	&data_word(0x98fb213f,0xb00327c8);	# u64
+	&data_word(0xbeef0ee4,0xbf597fc7);	# u64
+	&data_word(0x3da88fc2,0xc6e00bf3);	# u64
+	&data_word(0x930aa725,0xd5a79147);	# u64
+	&data_word(0xe003826f,0x06ca6351);	# u64
+	&data_word(0x0a0e6e70,0x14292967);	# u64
+	&data_word(0x46d22ffc,0x27b70a85);	# u64
+	&data_word(0x5c26c926,0x2e1b2138);	# u64
+	&data_word(0x5ac42aed,0x4d2c6dfc);	# u64
+	&data_word(0x9d95b3df,0x53380d13);	# u64
+	&data_word(0x8baf63de,0x650a7354);	# u64
+	&data_word(0x3c77b2a8,0x766a0abb);	# u64
+	&data_word(0x47edaee6,0x81c2c92e);	# u64
+	&data_word(0x1482353b,0x92722c85);	# u64
+	&data_word(0x4cf10364,0xa2bfe8a1);	# u64
+	&data_word(0xbc423001,0xa81a664b);	# u64
+	&data_word(0xd0f89791,0xc24b8b70);	# u64
+	&data_word(0x0654be30,0xc76c51a3);	# u64
+	&data_word(0xd6ef5218,0xd192e819);	# u64
+	&data_word(0x5565a910,0xd6990624);	# u64
+	&data_word(0x5771202a,0xf40e3585);	# u64
+	&data_word(0x32bbd1b8,0x106aa070);	# u64
+	&data_word(0xb8d2d0c8,0x19a4c116);	# u64
+	&data_word(0x5141ab53,0x1e376c08);	# u64
+	&data_word(0xdf8eeb99,0x2748774c);	# u64
+	&data_word(0xe19b48a8,0x34b0bcb5);	# u64
+	&data_word(0xc5c95a63,0x391c0cb3);	# u64
+	&data_word(0xe3418acb,0x4ed8aa4a);	# u64
+	&data_word(0x7763e373,0x5b9cca4f);	# u64
+	&data_word(0xd6b2b8a3,0x682e6ff3);	# u64
+	&data_word(0x5defb2fc,0x748f82ee);	# u64
+	&data_word(0x43172f60,0x78a5636f);	# u64
+	&data_word(0xa1f0ab72,0x84c87814);	# u64
+	&data_word(0x1a6439ec,0x8cc70208);	# u64
+	&data_word(0x23631e28,0x90befffa);	# u64
+	&data_word(0xde82bde9,0xa4506ceb);	# u64
+	&data_word(0xb2c67915,0xbef9a3f7);	# u64
+	&data_word(0xe372532b,0xc67178f2);	# u64
+	&data_word(0xea26619c,0xca273ece);	# u64
+	&data_word(0x21c0c207,0xd186b8c7);	# u64
+	&data_word(0xcde0eb1e,0xeada7dd6);	# u64
+	&data_word(0xee6ed178,0xf57d4f7f);	# u64
+	&data_word(0x72176fba,0x06f067aa);	# u64
+	&data_word(0xa2c898a6,0x0a637dc5);	# u64
+	&data_word(0xbef90dae,0x113f9804);	# u64
+	&data_word(0x131c471b,0x1b710b35);	# u64
+	&data_word(0x23047d84,0x28db77f5);	# u64
+	&data_word(0x40c72493,0x32caab7b);	# u64
+	&data_word(0x15c9bebc,0x3c9ebe0a);	# u64
+	&data_word(0x9c100d4c,0x431d67c4);	# u64
+	&data_word(0xcb3e42b6,0x4cc5d4be);	# u64
+	&data_word(0xfc657e2a,0x597f299c);	# u64
+	&data_word(0x3ad6faec,0x5fcb6fab);	# u64
+	&data_word(0x4a475817,0x6c44198c);	# u64
+
+	&data_word(0x04050607,0x00010203);	# byte swap
+	&data_word(0x0c0d0e0f,0x08090a0b);	# mask
+&function_end_B("sha512_block_data_order");
+&asciz("SHA512 block transform for x86, CRYPTOGAMS by ");
+
+&asm_finish();
+
+close STDOUT;
diff --git a/crypto/sha/asm/sha512-armv4.pl b/crypto/sha/asm/sha512-armv4.pl
new file mode 100644
index 000000000..22b5a9d0b
--- /dev/null
+++ b/crypto/sha/asm/sha512-armv4.pl
@@ -0,0 +1,668 @@
+#! /usr/bin/env perl
+# Copyright 2007-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+
+# ====================================================================
+# Written by Andy Polyakov  for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+#
+# Permission to use under GPL terms is granted.
+# ====================================================================
+
+# SHA512 block procedure for ARMv4. September 2007.
+
+# This code is ~4.5 (four and a half) times faster than code generated
+# by gcc 3.4 and it spends ~72 clock cycles per byte [on single-issue
+# Xscale PXA250 core].
+#
+# July 2010.
+#
+# Rescheduling for dual-issue pipeline resulted in 6% improvement on
+# Cortex A8 core and ~40 cycles per processed byte.
+
+# February 2011.
+#
+# Profiler-assisted and platform-specific optimization resulted in 7%
+# improvement on Coxtex A8 core and ~38 cycles per byte.
+
+# March 2011.
+#
+# Add NEON implementation. On Cortex A8 it was measured to process
+# one byte in 23.3 cycles or ~60% faster than integer-only code.
+
+# August 2012.
+#
+# Improve NEON performance by 12% on Snapdragon S4. In absolute
+# terms it's 22.6 cycles per byte, which is disappointing result.
+# Technical writers asserted that 3-way S4 pipeline can sustain
+# multiple NEON instructions per cycle, but dual NEON issue could
+# not be observed, see http://www.openssl.org/~appro/Snapdragon-S4.html
+# for further details. On side note Cortex-A15 processes one byte in
+# 16 cycles.
+
+# Byte order [in]dependence. =========================================
+#
+# Originally caller was expected to maintain specific *dword* order in
+# h[0-7], namely with most significant dword at *lower* address, which
+# was reflected in below two parameters as 0 and 4. Now caller is
+# expected to maintain native byte order for whole 64-bit values.
+$hi="HI";
+$lo="LO";
+# ====================================================================
+
+$flavour = shift;
+if ($flavour=~/\w[\w\-]*\.\w+$/) { $output=$flavour; undef $flavour; }
+else { while (($output=shift) && ($output!~/\w[\w\-]*\.\w+$/)) {} }
+
+if ($flavour && $flavour ne "void") {
+    $0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+    ( $xlate="${dir}arm-xlate.pl" and -f $xlate ) or
+    ( $xlate="${dir}../../perlasm/arm-xlate.pl" and -f $xlate) or
+    die "can't locate arm-xlate.pl";
+
+    open STDOUT,"| \"$^X\" $xlate $flavour $output";
+} else {
+    open STDOUT,">$output";
+}
+
+$ctx="r0";	# parameter block
+$inp="r1";
+$len="r2";
+
+$Tlo="r3";
+$Thi="r4";
+$Alo="r5";
+$Ahi="r6";
+$Elo="r7";
+$Ehi="r8";
+$t0="r9";
+$t1="r10";
+$t2="r11";
+$t3="r12";
+############	r13 is stack pointer
+$Ktbl="r14";
+############	r15 is program counter
+
+$Aoff=8*0;
+$Boff=8*1;
+$Coff=8*2;
+$Doff=8*3;
+$Eoff=8*4;
+$Foff=8*5;
+$Goff=8*6;
+$Hoff=8*7;
+$Xoff=8*8;
+
+sub BODY_00_15() {
+my $magic = shift;
+$code.=<<___;
+	@ Sigma1(x)	(ROTR((x),14) ^ ROTR((x),18)  ^ ROTR((x),41))
+	@ LO		lo>>14^hi<<18 ^ lo>>18^hi<<14 ^ hi>>9^lo<<23
+	@ HI		hi>>14^lo<<18 ^ hi>>18^lo<<14 ^ lo>>9^hi<<23
+	mov	$t0,$Elo,lsr#14
+	str	$Tlo,[sp,#$Xoff+0]
+	mov	$t1,$Ehi,lsr#14
+	str	$Thi,[sp,#$Xoff+4]
+	eor	$t0,$t0,$Ehi,lsl#18
+	ldr	$t2,[sp,#$Hoff+0]	@ h.lo
+	eor	$t1,$t1,$Elo,lsl#18
+	ldr	$t3,[sp,#$Hoff+4]	@ h.hi
+	eor	$t0,$t0,$Elo,lsr#18
+	eor	$t1,$t1,$Ehi,lsr#18
+	eor	$t0,$t0,$Ehi,lsl#14
+	eor	$t1,$t1,$Elo,lsl#14
+	eor	$t0,$t0,$Ehi,lsr#9
+	eor	$t1,$t1,$Elo,lsr#9
+	eor	$t0,$t0,$Elo,lsl#23
+	eor	$t1,$t1,$Ehi,lsl#23	@ Sigma1(e)
+	adds	$Tlo,$Tlo,$t0
+	ldr	$t0,[sp,#$Foff+0]	@ f.lo
+	adc	$Thi,$Thi,$t1		@ T += Sigma1(e)
+	ldr	$t1,[sp,#$Foff+4]	@ f.hi
+	adds	$Tlo,$Tlo,$t2
+	ldr	$t2,[sp,#$Goff+0]	@ g.lo
+	adc	$Thi,$Thi,$t3		@ T += h
+	ldr	$t3,[sp,#$Goff+4]	@ g.hi
+
+	eor	$t0,$t0,$t2
+	str	$Elo,[sp,#$Eoff+0]
+	eor	$t1,$t1,$t3
+	str	$Ehi,[sp,#$Eoff+4]
+	and	$t0,$t0,$Elo
+	str	$Alo,[sp,#$Aoff+0]
+	and	$t1,$t1,$Ehi
+	str	$Ahi,[sp,#$Aoff+4]
+	eor	$t0,$t0,$t2
+	ldr	$t2,[$Ktbl,#$lo]	@ K[i].lo
+	eor	$t1,$t1,$t3		@ Ch(e,f,g)
+	ldr	$t3,[$Ktbl,#$hi]	@ K[i].hi
+
+	adds	$Tlo,$Tlo,$t0
+	ldr	$Elo,[sp,#$Doff+0]	@ d.lo
+	adc	$Thi,$Thi,$t1		@ T += Ch(e,f,g)
+	ldr	$Ehi,[sp,#$Doff+4]	@ d.hi
+	adds	$Tlo,$Tlo,$t2
+	and	$t0,$t2,#0xff
+	adc	$Thi,$Thi,$t3		@ T += K[i]
+	adds	$Elo,$Elo,$Tlo
+	ldr	$t2,[sp,#$Boff+0]	@ b.lo
+	adc	$Ehi,$Ehi,$Thi		@ d += T
+	teq	$t0,#$magic
+
+	ldr	$t3,[sp,#$Coff+0]	@ c.lo
+#if __ARM_ARCH__>=7
+	it	eq			@ Thumb2 thing, sanity check in ARM
+#endif
+	orreq	$Ktbl,$Ktbl,#1
+	@ Sigma0(x)	(ROTR((x),28) ^ ROTR((x),34) ^ ROTR((x),39))
+	@ LO		lo>>28^hi<<4  ^ hi>>2^lo<<30 ^ hi>>7^lo<<25
+	@ HI		hi>>28^lo<<4  ^ lo>>2^hi<<30 ^ lo>>7^hi<<25
+	mov	$t0,$Alo,lsr#28
+	mov	$t1,$Ahi,lsr#28
+	eor	$t0,$t0,$Ahi,lsl#4
+	eor	$t1,$t1,$Alo,lsl#4
+	eor	$t0,$t0,$Ahi,lsr#2
+	eor	$t1,$t1,$Alo,lsr#2
+	eor	$t0,$t0,$Alo,lsl#30
+	eor	$t1,$t1,$Ahi,lsl#30
+	eor	$t0,$t0,$Ahi,lsr#7
+	eor	$t1,$t1,$Alo,lsr#7
+	eor	$t0,$t0,$Alo,lsl#25
+	eor	$t1,$t1,$Ahi,lsl#25	@ Sigma0(a)
+	adds	$Tlo,$Tlo,$t0
+	and	$t0,$Alo,$t2
+	adc	$Thi,$Thi,$t1		@ T += Sigma0(a)
+
+	ldr	$t1,[sp,#$Boff+4]	@ b.hi
+	orr	$Alo,$Alo,$t2
+	ldr	$t2,[sp,#$Coff+4]	@ c.hi
+	and	$Alo,$Alo,$t3
+	and	$t3,$Ahi,$t1
+	orr	$Ahi,$Ahi,$t1
+	orr	$Alo,$Alo,$t0		@ Maj(a,b,c).lo
+	and	$Ahi,$Ahi,$t2
+	adds	$Alo,$Alo,$Tlo
+	orr	$Ahi,$Ahi,$t3		@ Maj(a,b,c).hi
+	sub	sp,sp,#8
+	adc	$Ahi,$Ahi,$Thi		@ h += T
+	tst	$Ktbl,#1
+	add	$Ktbl,$Ktbl,#8
+___
+}
+$code=<<___;
+#ifndef __KERNEL__
+# include "arm_arch.h"
+# define VFP_ABI_PUSH	vstmdb	sp!,{d8-d15}
+# define VFP_ABI_POP	vldmia	sp!,{d8-d15}
+#else
+# define __ARM_ARCH__ __LINUX_ARM_ARCH__
+# define __ARM_MAX_ARCH__ 7
+# define VFP_ABI_PUSH
+# define VFP_ABI_POP
+#endif
+
+#ifdef __ARMEL__
+# define LO 0
+# define HI 4
+# define WORD64(hi0,lo0,hi1,lo1)	.word	lo0,hi0, lo1,hi1
+#else
+# define HI 0
+# define LO 4
+# define WORD64(hi0,lo0,hi1,lo1)	.word	hi0,lo0, hi1,lo1
+#endif
+
+.text
+#if defined(__thumb2__)
+.syntax unified
+.thumb
+# define adrl adr
+#else
+.code	32
+#endif
+
+.type	K512,%object
+.align	5
+K512:
+WORD64(0x428a2f98,0xd728ae22, 0x71374491,0x23ef65cd)
+WORD64(0xb5c0fbcf,0xec4d3b2f, 0xe9b5dba5,0x8189dbbc)
+WORD64(0x3956c25b,0xf348b538, 0x59f111f1,0xb605d019)
+WORD64(0x923f82a4,0xaf194f9b, 0xab1c5ed5,0xda6d8118)
+WORD64(0xd807aa98,0xa3030242, 0x12835b01,0x45706fbe)
+WORD64(0x243185be,0x4ee4b28c, 0x550c7dc3,0xd5ffb4e2)
+WORD64(0x72be5d74,0xf27b896f, 0x80deb1fe,0x3b1696b1)
+WORD64(0x9bdc06a7,0x25c71235, 0xc19bf174,0xcf692694)
+WORD64(0xe49b69c1,0x9ef14ad2, 0xefbe4786,0x384f25e3)
+WORD64(0x0fc19dc6,0x8b8cd5b5, 0x240ca1cc,0x77ac9c65)
+WORD64(0x2de92c6f,0x592b0275, 0x4a7484aa,0x6ea6e483)
+WORD64(0x5cb0a9dc,0xbd41fbd4, 0x76f988da,0x831153b5)
+WORD64(0x983e5152,0xee66dfab, 0xa831c66d,0x2db43210)
+WORD64(0xb00327c8,0x98fb213f, 0xbf597fc7,0xbeef0ee4)
+WORD64(0xc6e00bf3,0x3da88fc2, 0xd5a79147,0x930aa725)
+WORD64(0x06ca6351,0xe003826f, 0x14292967,0x0a0e6e70)
+WORD64(0x27b70a85,0x46d22ffc, 0x2e1b2138,0x5c26c926)
+WORD64(0x4d2c6dfc,0x5ac42aed, 0x53380d13,0x9d95b3df)
+WORD64(0x650a7354,0x8baf63de, 0x766a0abb,0x3c77b2a8)
+WORD64(0x81c2c92e,0x47edaee6, 0x92722c85,0x1482353b)
+WORD64(0xa2bfe8a1,0x4cf10364, 0xa81a664b,0xbc423001)
+WORD64(0xc24b8b70,0xd0f89791, 0xc76c51a3,0x0654be30)
+WORD64(0xd192e819,0xd6ef5218, 0xd6990624,0x5565a910)
+WORD64(0xf40e3585,0x5771202a, 0x106aa070,0x32bbd1b8)
+WORD64(0x19a4c116,0xb8d2d0c8, 0x1e376c08,0x5141ab53)
+WORD64(0x2748774c,0xdf8eeb99, 0x34b0bcb5,0xe19b48a8)
+WORD64(0x391c0cb3,0xc5c95a63, 0x4ed8aa4a,0xe3418acb)
+WORD64(0x5b9cca4f,0x7763e373, 0x682e6ff3,0xd6b2b8a3)
+WORD64(0x748f82ee,0x5defb2fc, 0x78a5636f,0x43172f60)
+WORD64(0x84c87814,0xa1f0ab72, 0x8cc70208,0x1a6439ec)
+WORD64(0x90befffa,0x23631e28, 0xa4506ceb,0xde82bde9)
+WORD64(0xbef9a3f7,0xb2c67915, 0xc67178f2,0xe372532b)
+WORD64(0xca273ece,0xea26619c, 0xd186b8c7,0x21c0c207)
+WORD64(0xeada7dd6,0xcde0eb1e, 0xf57d4f7f,0xee6ed178)
+WORD64(0x06f067aa,0x72176fba, 0x0a637dc5,0xa2c898a6)
+WORD64(0x113f9804,0xbef90dae, 0x1b710b35,0x131c471b)
+WORD64(0x28db77f5,0x23047d84, 0x32caab7b,0x40c72493)
+WORD64(0x3c9ebe0a,0x15c9bebc, 0x431d67c4,0x9c100d4c)
+WORD64(0x4cc5d4be,0xcb3e42b6, 0x597f299c,0xfc657e2a)
+WORD64(0x5fcb6fab,0x3ad6faec, 0x6c44198c,0x4a475817)
+.size	K512,.-K512
+#if __ARM_MAX_ARCH__>=7 && !defined(__KERNEL__)
+.LOPENSSL_armcap:
+.word	OPENSSL_armcap_P-.Lsha512_block_data_order
+.skip	32-4
+#else
+.skip	32
+#endif
+
+.global	sha512_block_data_order
+.type	sha512_block_data_order,%function
+sha512_block_data_order:
+.Lsha512_block_data_order:
+#if __ARM_ARCH__<7 && !defined(__thumb2__)
+	sub	r3,pc,#8		@ sha512_block_data_order
+#else
+	adr	r3,.Lsha512_block_data_order
+#endif
+#if __ARM_MAX_ARCH__>=7 && !defined(__KERNEL__)
+	ldr	r12,.LOPENSSL_armcap
+	ldr	r12,[r3,r12]		@ OPENSSL_armcap_P
+#ifdef	__APPLE__
+	ldr	r12,[r12]
+#endif
+	tst	r12,#ARMV7_NEON
+	bne	.LNEON
+#endif
+	add	$len,$inp,$len,lsl#7	@ len to point at the end of inp
+	stmdb	sp!,{r4-r12,lr}
+	sub	$Ktbl,r3,#672		@ K512
+	sub	sp,sp,#9*8
+
+	ldr	$Elo,[$ctx,#$Eoff+$lo]
+	ldr	$Ehi,[$ctx,#$Eoff+$hi]
+	ldr	$t0, [$ctx,#$Goff+$lo]
+	ldr	$t1, [$ctx,#$Goff+$hi]
+	ldr	$t2, [$ctx,#$Hoff+$lo]
+	ldr	$t3, [$ctx,#$Hoff+$hi]
+.Loop:
+	str	$t0, [sp,#$Goff+0]
+	str	$t1, [sp,#$Goff+4]
+	str	$t2, [sp,#$Hoff+0]
+	str	$t3, [sp,#$Hoff+4]
+	ldr	$Alo,[$ctx,#$Aoff+$lo]
+	ldr	$Ahi,[$ctx,#$Aoff+$hi]
+	ldr	$Tlo,[$ctx,#$Boff+$lo]
+	ldr	$Thi,[$ctx,#$Boff+$hi]
+	ldr	$t0, [$ctx,#$Coff+$lo]
+	ldr	$t1, [$ctx,#$Coff+$hi]
+	ldr	$t2, [$ctx,#$Doff+$lo]
+	ldr	$t3, [$ctx,#$Doff+$hi]
+	str	$Tlo,[sp,#$Boff+0]
+	str	$Thi,[sp,#$Boff+4]
+	str	$t0, [sp,#$Coff+0]
+	str	$t1, [sp,#$Coff+4]
+	str	$t2, [sp,#$Doff+0]
+	str	$t3, [sp,#$Doff+4]
+	ldr	$Tlo,[$ctx,#$Foff+$lo]
+	ldr	$Thi,[$ctx,#$Foff+$hi]
+	str	$Tlo,[sp,#$Foff+0]
+	str	$Thi,[sp,#$Foff+4]
+
+.L00_15:
+#if __ARM_ARCH__<7
+	ldrb	$Tlo,[$inp,#7]
+	ldrb	$t0, [$inp,#6]
+	ldrb	$t1, [$inp,#5]
+	ldrb	$t2, [$inp,#4]
+	ldrb	$Thi,[$inp,#3]
+	ldrb	$t3, [$inp,#2]
+	orr	$Tlo,$Tlo,$t0,lsl#8
+	ldrb	$t0, [$inp,#1]
+	orr	$Tlo,$Tlo,$t1,lsl#16
+	ldrb	$t1, [$inp],#8
+	orr	$Tlo,$Tlo,$t2,lsl#24
+	orr	$Thi,$Thi,$t3,lsl#8
+	orr	$Thi,$Thi,$t0,lsl#16
+	orr	$Thi,$Thi,$t1,lsl#24
+#else
+	ldr	$Tlo,[$inp,#4]
+	ldr	$Thi,[$inp],#8
+#ifdef __ARMEL__
+	rev	$Tlo,$Tlo
+	rev	$Thi,$Thi
+#endif
+#endif
+___
+	&BODY_00_15(0x94);
+$code.=<<___;
+	tst	$Ktbl,#1
+	beq	.L00_15
+	ldr	$t0,[sp,#`$Xoff+8*(16-1)`+0]
+	ldr	$t1,[sp,#`$Xoff+8*(16-1)`+4]
+	bic	$Ktbl,$Ktbl,#1
+.L16_79:
+	@ sigma0(x)	(ROTR((x),1)  ^ ROTR((x),8)  ^ ((x)>>7))
+	@ LO		lo>>1^hi<<31  ^ lo>>8^hi<<24 ^ lo>>7^hi<<25
+	@ HI		hi>>1^lo<<31  ^ hi>>8^lo<<24 ^ hi>>7
+	mov	$Tlo,$t0,lsr#1
+	ldr	$t2,[sp,#`$Xoff+8*(16-14)`+0]
+	mov	$Thi,$t1,lsr#1
+	ldr	$t3,[sp,#`$Xoff+8*(16-14)`+4]
+	eor	$Tlo,$Tlo,$t1,lsl#31
+	eor	$Thi,$Thi,$t0,lsl#31
+	eor	$Tlo,$Tlo,$t0,lsr#8
+	eor	$Thi,$Thi,$t1,lsr#8
+	eor	$Tlo,$Tlo,$t1,lsl#24
+	eor	$Thi,$Thi,$t0,lsl#24
+	eor	$Tlo,$Tlo,$t0,lsr#7
+	eor	$Thi,$Thi,$t1,lsr#7
+	eor	$Tlo,$Tlo,$t1,lsl#25
+
+	@ sigma1(x)	(ROTR((x),19) ^ ROTR((x),61) ^ ((x)>>6))
+	@ LO		lo>>19^hi<<13 ^ hi>>29^lo<<3 ^ lo>>6^hi<<26
+	@ HI		hi>>19^lo<<13 ^ lo>>29^hi<<3 ^ hi>>6
+	mov	$t0,$t2,lsr#19
+	mov	$t1,$t3,lsr#19
+	eor	$t0,$t0,$t3,lsl#13
+	eor	$t1,$t1,$t2,lsl#13
+	eor	$t0,$t0,$t3,lsr#29
+	eor	$t1,$t1,$t2,lsr#29
+	eor	$t0,$t0,$t2,lsl#3
+	eor	$t1,$t1,$t3,lsl#3
+	eor	$t0,$t0,$t2,lsr#6
+	eor	$t1,$t1,$t3,lsr#6
+	ldr	$t2,[sp,#`$Xoff+8*(16-9)`+0]
+	eor	$t0,$t0,$t3,lsl#26
+
+	ldr	$t3,[sp,#`$Xoff+8*(16-9)`+4]
+	adds	$Tlo,$Tlo,$t0
+	ldr	$t0,[sp,#`$Xoff+8*16`+0]
+	adc	$Thi,$Thi,$t1
+
+	ldr	$t1,[sp,#`$Xoff+8*16`+4]
+	adds	$Tlo,$Tlo,$t2
+	adc	$Thi,$Thi,$t3
+	adds	$Tlo,$Tlo,$t0
+	adc	$Thi,$Thi,$t1
+___
+	&BODY_00_15(0x17);
+$code.=<<___;
+#if __ARM_ARCH__>=7
+	ittt	eq			@ Thumb2 thing, sanity check in ARM
+#endif
+	ldreq	$t0,[sp,#`$Xoff+8*(16-1)`+0]
+	ldreq	$t1,[sp,#`$Xoff+8*(16-1)`+4]
+	beq	.L16_79
+	bic	$Ktbl,$Ktbl,#1
+
+	ldr	$Tlo,[sp,#$Boff+0]
+	ldr	$Thi,[sp,#$Boff+4]
+	ldr	$t0, [$ctx,#$Aoff+$lo]
+	ldr	$t1, [$ctx,#$Aoff+$hi]
+	ldr	$t2, [$ctx,#$Boff+$lo]
+	ldr	$t3, [$ctx,#$Boff+$hi]
+	adds	$t0,$Alo,$t0
+	str	$t0, [$ctx,#$Aoff+$lo]
+	adc	$t1,$Ahi,$t1
+	str	$t1, [$ctx,#$Aoff+$hi]
+	adds	$t2,$Tlo,$t2
+	str	$t2, [$ctx,#$Boff+$lo]
+	adc	$t3,$Thi,$t3
+	str	$t3, [$ctx,#$Boff+$hi]
+
+	ldr	$Alo,[sp,#$Coff+0]
+	ldr	$Ahi,[sp,#$Coff+4]
+	ldr	$Tlo,[sp,#$Doff+0]
+	ldr	$Thi,[sp,#$Doff+4]
+	ldr	$t0, [$ctx,#$Coff+$lo]
+	ldr	$t1, [$ctx,#$Coff+$hi]
+	ldr	$t2, [$ctx,#$Doff+$lo]
+	ldr	$t3, [$ctx,#$Doff+$hi]
+	adds	$t0,$Alo,$t0
+	str	$t0, [$ctx,#$Coff+$lo]
+	adc	$t1,$Ahi,$t1
+	str	$t1, [$ctx,#$Coff+$hi]
+	adds	$t2,$Tlo,$t2
+	str	$t2, [$ctx,#$Doff+$lo]
+	adc	$t3,$Thi,$t3
+	str	$t3, [$ctx,#$Doff+$hi]
+
+	ldr	$Tlo,[sp,#$Foff+0]
+	ldr	$Thi,[sp,#$Foff+4]
+	ldr	$t0, [$ctx,#$Eoff+$lo]
+	ldr	$t1, [$ctx,#$Eoff+$hi]
+	ldr	$t2, [$ctx,#$Foff+$lo]
+	ldr	$t3, [$ctx,#$Foff+$hi]
+	adds	$Elo,$Elo,$t0
+	str	$Elo,[$ctx,#$Eoff+$lo]
+	adc	$Ehi,$Ehi,$t1
+	str	$Ehi,[$ctx,#$Eoff+$hi]
+	adds	$t2,$Tlo,$t2
+	str	$t2, [$ctx,#$Foff+$lo]
+	adc	$t3,$Thi,$t3
+	str	$t3, [$ctx,#$Foff+$hi]
+
+	ldr	$Alo,[sp,#$Goff+0]
+	ldr	$Ahi,[sp,#$Goff+4]
+	ldr	$Tlo,[sp,#$Hoff+0]
+	ldr	$Thi,[sp,#$Hoff+4]
+	ldr	$t0, [$ctx,#$Goff+$lo]
+	ldr	$t1, [$ctx,#$Goff+$hi]
+	ldr	$t2, [$ctx,#$Hoff+$lo]
+	ldr	$t3, [$ctx,#$Hoff+$hi]
+	adds	$t0,$Alo,$t0
+	str	$t0, [$ctx,#$Goff+$lo]
+	adc	$t1,$Ahi,$t1
+	str	$t1, [$ctx,#$Goff+$hi]
+	adds	$t2,$Tlo,$t2
+	str	$t2, [$ctx,#$Hoff+$lo]
+	adc	$t3,$Thi,$t3
+	str	$t3, [$ctx,#$Hoff+$hi]
+
+	add	sp,sp,#640
+	sub	$Ktbl,$Ktbl,#640
+
+	teq	$inp,$len
+	bne	.Loop
+
+	add	sp,sp,#8*9		@ destroy frame
+#if __ARM_ARCH__>=5
+	ldmia	sp!,{r4-r12,pc}
+#else
+	ldmia	sp!,{r4-r12,lr}
+	tst	lr,#1
+	moveq	pc,lr			@ be binary compatible with V4, yet
+	bx	lr			@ interoperable with Thumb ISA:-)
+#endif
+.size	sha512_block_data_order,.-sha512_block_data_order
+___
+
+{
+my @Sigma0=(28,34,39);
+my @Sigma1=(14,18,41);
+my @sigma0=(1, 8, 7);
+my @sigma1=(19,61,6);
+
+my $Ktbl="r3";
+my $cnt="r12";	# volatile register known as ip, intra-procedure-call scratch
+
+my @X=map("d$_",(0..15));
+my @V=($A,$B,$C,$D,$E,$F,$G,$H)=map("d$_",(16..23));
+
+sub NEON_00_15() {
+my $i=shift;
+my ($a,$b,$c,$d,$e,$f,$g,$h)=@_;
+my ($t0,$t1,$t2,$T1,$K,$Ch,$Maj)=map("d$_",(24..31));	# temps
+
+$code.=<<___ if ($i<16 || $i&1);
+	vshr.u64	$t0,$e,#@Sigma1[0]	@ $i
+#if $i<16
+	vld1.64		{@X[$i%16]},[$inp]!	@ handles unaligned
+#endif
+	vshr.u64	$t1,$e,#@Sigma1[1]
+#if $i>0
+	 vadd.i64	$a,$Maj			@ h+=Maj from the past
+#endif
+	vshr.u64	$t2,$e,#@Sigma1[2]
+___
+$code.=<<___;
+	vld1.64		{$K},[$Ktbl,:64]!	@ K[i++]
+	vsli.64		$t0,$e,#`64-@Sigma1[0]`
+	vsli.64		$t1,$e,#`64-@Sigma1[1]`
+	vmov		$Ch,$e
+	vsli.64		$t2,$e,#`64-@Sigma1[2]`
+#if $i<16 && defined(__ARMEL__)
+	vrev64.8	@X[$i],@X[$i]
+#endif
+	veor		$t1,$t0
+	vbsl		$Ch,$f,$g		@ Ch(e,f,g)
+	vshr.u64	$t0,$a,#@Sigma0[0]
+	veor		$t2,$t1			@ Sigma1(e)
+	vadd.i64	$T1,$Ch,$h
+	vshr.u64	$t1,$a,#@Sigma0[1]
+	vsli.64		$t0,$a,#`64-@Sigma0[0]`
+	vadd.i64	$T1,$t2
+	vshr.u64	$t2,$a,#@Sigma0[2]
+	vadd.i64	$K,@X[$i%16]
+	vsli.64		$t1,$a,#`64-@Sigma0[1]`
+	veor		$Maj,$a,$b
+	vsli.64		$t2,$a,#`64-@Sigma0[2]`
+	veor		$h,$t0,$t1
+	vadd.i64	$T1,$K
+	vbsl		$Maj,$c,$b		@ Maj(a,b,c)
+	veor		$h,$t2			@ Sigma0(a)
+	vadd.i64	$d,$T1
+	vadd.i64	$Maj,$T1
+	@ vadd.i64	$h,$Maj
+___
+}
+
+sub NEON_16_79() {
+my $i=shift;
+
+if ($i&1)	{ &NEON_00_15($i,@_); return; }
+
+# 2x-vectorized, therefore runs every 2nd round
+my @X=map("q$_",(0..7));			# view @X as 128-bit vector
+my ($t0,$t1,$s0,$s1) = map("q$_",(12..15));	# temps
+my ($d0,$d1,$d2) = map("d$_",(24..26));		# temps from NEON_00_15
+my $e=@_[4];					# $e from NEON_00_15
+$i /= 2;
+$code.=<<___;
+	vshr.u64	$t0,@X[($i+7)%8],#@sigma1[0]
+	vshr.u64	$t1,@X[($i+7)%8],#@sigma1[1]
+	 vadd.i64	@_[0],d30			@ h+=Maj from the past
+	vshr.u64	$s1,@X[($i+7)%8],#@sigma1[2]
+	vsli.64		$t0,@X[($i+7)%8],#`64-@sigma1[0]`
+	vext.8		$s0,@X[$i%8],@X[($i+1)%8],#8	@ X[i+1]
+	vsli.64		$t1,@X[($i+7)%8],#`64-@sigma1[1]`
+	veor		$s1,$t0
+	vshr.u64	$t0,$s0,#@sigma0[0]
+	veor		$s1,$t1				@ sigma1(X[i+14])
+	vshr.u64	$t1,$s0,#@sigma0[1]
+	vadd.i64	@X[$i%8],$s1
+	vshr.u64	$s1,$s0,#@sigma0[2]
+	vsli.64		$t0,$s0,#`64-@sigma0[0]`
+	vsli.64		$t1,$s0,#`64-@sigma0[1]`
+	vext.8		$s0,@X[($i+4)%8],@X[($i+5)%8],#8	@ X[i+9]
+	veor		$s1,$t0
+	vshr.u64	$d0,$e,#@Sigma1[0]		@ from NEON_00_15
+	vadd.i64	@X[$i%8],$s0
+	vshr.u64	$d1,$e,#@Sigma1[1]		@ from NEON_00_15
+	veor		$s1,$t1				@ sigma0(X[i+1])
+	vshr.u64	$d2,$e,#@Sigma1[2]		@ from NEON_00_15
+	vadd.i64	@X[$i%8],$s1
+___
+	&NEON_00_15(2*$i,@_);
+}
+
+$code.=<<___;
+#if __ARM_MAX_ARCH__>=7
+.arch	armv7-a
+.fpu	neon
+
+.global	sha512_block_data_order_neon
+.type	sha512_block_data_order_neon,%function
+.align	4
+sha512_block_data_order_neon:
+.LNEON:
+	dmb				@ errata #451034 on early Cortex A8
+	add	$len,$inp,$len,lsl#7	@ len to point at the end of inp
+	adr	$Ktbl,K512
+	VFP_ABI_PUSH
+	vldmia	$ctx,{$A-$H}		@ load context
+.Loop_neon:
+___
+for($i=0;$i<16;$i++)	{ &NEON_00_15($i,@V); unshift(@V,pop(@V)); }
+$code.=<<___;
+	mov		$cnt,#4
+.L16_79_neon:
+	subs		$cnt,#1
+___
+for(;$i<32;$i++)	{ &NEON_16_79($i,@V); unshift(@V,pop(@V)); }
+$code.=<<___;
+	bne		.L16_79_neon
+
+	 vadd.i64	$A,d30		@ h+=Maj from the past
+	vldmia		$ctx,{d24-d31}	@ load context to temp
+	vadd.i64	q8,q12		@ vectorized accumulate
+	vadd.i64	q9,q13
+	vadd.i64	q10,q14
+	vadd.i64	q11,q15
+	vstmia		$ctx,{$A-$H}	@ save context
+	teq		$inp,$len
+	sub		$Ktbl,#640	@ rewind K512
+	bne		.Loop_neon
+
+	VFP_ABI_POP
+	ret				@ bx lr
+.size	sha512_block_data_order_neon,.-sha512_block_data_order_neon
+#endif
+___
+}
+$code.=<<___;
+.asciz	"SHA512 block transform for ARMv4/NEON, CRYPTOGAMS by "
+.align	2
+#if __ARM_MAX_ARCH__>=7 && !defined(__KERNEL__)
+.comm	OPENSSL_armcap_P,4,4
+#endif
+___
+
+$code =~ s/\`([^\`]*)\`/eval $1/gem;
+$code =~ s/\bbx\s+lr\b/.word\t0xe12fff1e/gm;	# make it possible to compile with -march=armv4
+$code =~ s/\bret\b/bx	lr/gm;
+
+open SELF,$0;
+while() {
+	next if (/^#!/);
+	last if (!s/^#/@/ and !/^$/);
+	print;
+}
+close SELF;
+
+print $code;
+close STDOUT; # enforce flush
diff --git a/crypto/sha/asm/sha512-armv8.pl b/crypto/sha/asm/sha512-armv8.pl
new file mode 100644
index 000000000..c1aaf778f
--- /dev/null
+++ b/crypto/sha/asm/sha512-armv8.pl
@@ -0,0 +1,446 @@
+#! /usr/bin/env perl
+# Copyright 2014-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+#
+# ====================================================================
+# Written by Andy Polyakov  for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+#
+# SHA256/512 for ARMv8.
+#
+# Performance in cycles per processed byte and improvement coefficient
+# over code generated with "default" compiler:
+#
+#		SHA256-hw	SHA256(*)	SHA512
+# Apple A7	1.97		10.5 (+33%)	6.73 (-1%(**))
+# Cortex-A53	2.38		15.5 (+115%)	10.0 (+150%(***))
+# Cortex-A57	2.31		11.6 (+86%)	7.51 (+260%(***))
+# Denver	2.01		10.5 (+26%)	6.70 (+8%)
+# X-Gene			20.0 (+100%)	12.8 (+300%(***))
+# Mongoose	2.36		13.0 (+50%)	8.36 (+33%)
+# 
+# (*)	Software SHA256 results are of lesser relevance, presented
+#	mostly for informational purposes.
+# (**)	The result is a trade-off: it's possible to improve it by
+#	10% (or by 1 cycle per round), but at the cost of 20% loss
+#	on Cortex-A53 (or by 4 cycles per round).
+# (***)	Super-impressive coefficients over gcc-generated code are
+#	indication of some compiler "pathology", most notably code
+#	generated with -mgeneral-regs-only is significanty faster
+#	and the gap is only 40-90%.
+
+$flavour=shift;
+$output=shift;
+
+$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+( $xlate="${dir}arm-xlate.pl" and -f $xlate ) or
+( $xlate="${dir}../../perlasm/arm-xlate.pl" and -f $xlate) or
+die "can't locate arm-xlate.pl";
+
+open OUT,"| \"$^X\" $xlate $flavour $output";
+*STDOUT=*OUT;
+
+if ($output =~ /512/) {
+	$BITS=512;
+	$SZ=8;
+	@Sigma0=(28,34,39);
+	@Sigma1=(14,18,41);
+	@sigma0=(1,  8, 7);
+	@sigma1=(19,61, 6);
+	$rounds=80;
+	$reg_t="x";
+} else {
+	$BITS=256;
+	$SZ=4;
+	@Sigma0=( 2,13,22);
+	@Sigma1=( 6,11,25);
+	@sigma0=( 7,18, 3);
+	@sigma1=(17,19,10);
+	$rounds=64;
+	$reg_t="w";
+}
+
+$func="sha${BITS}_block_data_order";
+
+($ctx,$inp,$num,$Ktbl)=map("x$_",(0..2,30));
+
+@X=map("$reg_t$_",(3..15,0..2));
+@V=($A,$B,$C,$D,$E,$F,$G,$H)=map("$reg_t$_",(20..27));
+($t0,$t1,$t2,$t3)=map("$reg_t$_",(16,17,19,28));
+
+sub BODY_00_xx {
+my ($i,$a,$b,$c,$d,$e,$f,$g,$h)=@_;
+my $j=($i+1)&15;
+my ($T0,$T1,$T2)=(@X[($i-8)&15],@X[($i-9)&15],@X[($i-10)&15]);
+   $T0=@X[$i+3] if ($i<11);
+
+$code.=<<___	if ($i<16);
+#ifndef	__ARMEB__
+	rev	@X[$i],@X[$i]			// $i
+#endif
+___
+$code.=<<___	if ($i<13 && ($i&1));
+	ldp	@X[$i+1],@X[$i+2],[$inp],#2*$SZ
+___
+$code.=<<___	if ($i==13);
+	ldp	@X[14],@X[15],[$inp]
+___
+$code.=<<___	if ($i>=14);
+	ldr	@X[($i-11)&15],[sp,#`$SZ*(($i-11)%4)`]
+___
+$code.=<<___	if ($i>0 && $i<16);
+	add	$a,$a,$t1			// h+=Sigma0(a)
+___
+$code.=<<___	if ($i>=11);
+	str	@X[($i-8)&15],[sp,#`$SZ*(($i-8)%4)`]
+___
+# While ARMv8 specifies merged rotate-n-logical operation such as
+# 'eor x,y,z,ror#n', it was found to negatively affect performance
+# on Apple A7. The reason seems to be that it requires even 'y' to
+# be available earlier. This means that such merged instruction is
+# not necessarily best choice on critical path... On the other hand
+# Cortex-A5x handles merged instructions much better than disjoint
+# rotate and logical... See (**) footnote above.
+$code.=<<___	if ($i<15);
+	ror	$t0,$e,#$Sigma1[0]
+	add	$h,$h,$t2			// h+=K[i]
+	eor	$T0,$e,$e,ror#`$Sigma1[2]-$Sigma1[1]`
+	and	$t1,$f,$e
+	bic	$t2,$g,$e
+	add	$h,$h,@X[$i&15]			// h+=X[i]
+	orr	$t1,$t1,$t2			// Ch(e,f,g)
+	eor	$t2,$a,$b			// a^b, b^c in next round
+	eor	$t0,$t0,$T0,ror#$Sigma1[1]	// Sigma1(e)
+	ror	$T0,$a,#$Sigma0[0]
+	add	$h,$h,$t1			// h+=Ch(e,f,g)
+	eor	$t1,$a,$a,ror#`$Sigma0[2]-$Sigma0[1]`
+	add	$h,$h,$t0			// h+=Sigma1(e)
+	and	$t3,$t3,$t2			// (b^c)&=(a^b)
+	add	$d,$d,$h			// d+=h
+	eor	$t3,$t3,$b			// Maj(a,b,c)
+	eor	$t1,$T0,$t1,ror#$Sigma0[1]	// Sigma0(a)
+	add	$h,$h,$t3			// h+=Maj(a,b,c)
+	ldr	$t3,[$Ktbl],#$SZ		// *K++, $t2 in next round
+	//add	$h,$h,$t1			// h+=Sigma0(a)
+___
+$code.=<<___	if ($i>=15);
+	ror	$t0,$e,#$Sigma1[0]
+	add	$h,$h,$t2			// h+=K[i]
+	ror	$T1,@X[($j+1)&15],#$sigma0[0]
+	and	$t1,$f,$e
+	ror	$T2,@X[($j+14)&15],#$sigma1[0]
+	bic	$t2,$g,$e
+	ror	$T0,$a,#$Sigma0[0]
+	add	$h,$h,@X[$i&15]			// h+=X[i]
+	eor	$t0,$t0,$e,ror#$Sigma1[1]
+	eor	$T1,$T1,@X[($j+1)&15],ror#$sigma0[1]
+	orr	$t1,$t1,$t2			// Ch(e,f,g)
+	eor	$t2,$a,$b			// a^b, b^c in next round
+	eor	$t0,$t0,$e,ror#$Sigma1[2]	// Sigma1(e)
+	eor	$T0,$T0,$a,ror#$Sigma0[1]
+	add	$h,$h,$t1			// h+=Ch(e,f,g)
+	and	$t3,$t3,$t2			// (b^c)&=(a^b)
+	eor	$T2,$T2,@X[($j+14)&15],ror#$sigma1[1]
+	eor	$T1,$T1,@X[($j+1)&15],lsr#$sigma0[2]	// sigma0(X[i+1])
+	add	$h,$h,$t0			// h+=Sigma1(e)
+	eor	$t3,$t3,$b			// Maj(a,b,c)
+	eor	$t1,$T0,$a,ror#$Sigma0[2]	// Sigma0(a)
+	eor	$T2,$T2,@X[($j+14)&15],lsr#$sigma1[2]	// sigma1(X[i+14])
+	add	@X[$j],@X[$j],@X[($j+9)&15]
+	add	$d,$d,$h			// d+=h
+	add	$h,$h,$t3			// h+=Maj(a,b,c)
+	ldr	$t3,[$Ktbl],#$SZ		// *K++, $t2 in next round
+	add	@X[$j],@X[$j],$T1
+	add	$h,$h,$t1			// h+=Sigma0(a)
+	add	@X[$j],@X[$j],$T2
+___
+	($t2,$t3)=($t3,$t2);
+}
+
+$code.=<<___;
+#include "arm_arch.h"
+
+.text
+
+.extern	OPENSSL_armcap_P
+.globl	$func
+.type	$func,%function
+.align	6
+$func:
+___
+$code.=<<___	if ($SZ==4);
+#ifdef	__ILP32__
+	ldrsw	x16,.LOPENSSL_armcap_P
+#else
+	ldr	x16,.LOPENSSL_armcap_P
+#endif
+	adr	x17,.LOPENSSL_armcap_P
+	add	x16,x16,x17
+	ldr	w16,[x16]
+	tst	w16,#ARMV8_SHA256
+	b.ne	.Lv8_entry
+___
+$code.=<<___;
+	stp	x29,x30,[sp,#-128]!
+	add	x29,sp,#0
+
+	stp	x19,x20,[sp,#16]
+	stp	x21,x22,[sp,#32]
+	stp	x23,x24,[sp,#48]
+	stp	x25,x26,[sp,#64]
+	stp	x27,x28,[sp,#80]
+	sub	sp,sp,#4*$SZ
+
+	ldp	$A,$B,[$ctx]				// load context
+	ldp	$C,$D,[$ctx,#2*$SZ]
+	ldp	$E,$F,[$ctx,#4*$SZ]
+	add	$num,$inp,$num,lsl#`log(16*$SZ)/log(2)`	// end of input
+	ldp	$G,$H,[$ctx,#6*$SZ]
+	adr	$Ktbl,.LK$BITS
+	stp	$ctx,$num,[x29,#96]
+
+.Loop:
+	ldp	@X[0],@X[1],[$inp],#2*$SZ
+	ldr	$t2,[$Ktbl],#$SZ			// *K++
+	eor	$t3,$B,$C				// magic seed
+	str	$inp,[x29,#112]
+___
+for ($i=0;$i<16;$i++)	{ &BODY_00_xx($i,@V); unshift(@V,pop(@V)); }
+$code.=".Loop_16_xx:\n";
+for (;$i<32;$i++)	{ &BODY_00_xx($i,@V); unshift(@V,pop(@V)); }
+$code.=<<___;
+	cbnz	$t2,.Loop_16_xx
+
+	ldp	$ctx,$num,[x29,#96]
+	ldr	$inp,[x29,#112]
+	sub	$Ktbl,$Ktbl,#`$SZ*($rounds+1)`		// rewind
+
+	ldp	@X[0],@X[1],[$ctx]
+	ldp	@X[2],@X[3],[$ctx,#2*$SZ]
+	add	$inp,$inp,#14*$SZ			// advance input pointer
+	ldp	@X[4],@X[5],[$ctx,#4*$SZ]
+	add	$A,$A,@X[0]
+	ldp	@X[6],@X[7],[$ctx,#6*$SZ]
+	add	$B,$B,@X[1]
+	add	$C,$C,@X[2]
+	add	$D,$D,@X[3]
+	stp	$A,$B,[$ctx]
+	add	$E,$E,@X[4]
+	add	$F,$F,@X[5]
+	stp	$C,$D,[$ctx,#2*$SZ]
+	add	$G,$G,@X[6]
+	add	$H,$H,@X[7]
+	cmp	$inp,$num
+	stp	$E,$F,[$ctx,#4*$SZ]
+	stp	$G,$H,[$ctx,#6*$SZ]
+	b.ne	.Loop
+
+	ldp	x19,x20,[x29,#16]
+	add	sp,sp,#4*$SZ
+	ldp	x21,x22,[x29,#32]
+	ldp	x23,x24,[x29,#48]
+	ldp	x25,x26,[x29,#64]
+	ldp	x27,x28,[x29,#80]
+	ldp	x29,x30,[sp],#128
+	ret
+.size	$func,.-$func
+
+.align	6
+.type	.LK$BITS,%object
+.LK$BITS:
+___
+$code.=<<___ if ($SZ==8);
+	.quad	0x428a2f98d728ae22,0x7137449123ef65cd
+	.quad	0xb5c0fbcfec4d3b2f,0xe9b5dba58189dbbc
+	.quad	0x3956c25bf348b538,0x59f111f1b605d019
+	.quad	0x923f82a4af194f9b,0xab1c5ed5da6d8118
+	.quad	0xd807aa98a3030242,0x12835b0145706fbe
+	.quad	0x243185be4ee4b28c,0x550c7dc3d5ffb4e2
+	.quad	0x72be5d74f27b896f,0x80deb1fe3b1696b1
+	.quad	0x9bdc06a725c71235,0xc19bf174cf692694
+	.quad	0xe49b69c19ef14ad2,0xefbe4786384f25e3
+	.quad	0x0fc19dc68b8cd5b5,0x240ca1cc77ac9c65
+	.quad	0x2de92c6f592b0275,0x4a7484aa6ea6e483
+	.quad	0x5cb0a9dcbd41fbd4,0x76f988da831153b5
+	.quad	0x983e5152ee66dfab,0xa831c66d2db43210
+	.quad	0xb00327c898fb213f,0xbf597fc7beef0ee4
+	.quad	0xc6e00bf33da88fc2,0xd5a79147930aa725
+	.quad	0x06ca6351e003826f,0x142929670a0e6e70
+	.quad	0x27b70a8546d22ffc,0x2e1b21385c26c926
+	.quad	0x4d2c6dfc5ac42aed,0x53380d139d95b3df
+	.quad	0x650a73548baf63de,0x766a0abb3c77b2a8
+	.quad	0x81c2c92e47edaee6,0x92722c851482353b
+	.quad	0xa2bfe8a14cf10364,0xa81a664bbc423001
+	.quad	0xc24b8b70d0f89791,0xc76c51a30654be30
+	.quad	0xd192e819d6ef5218,0xd69906245565a910
+	.quad	0xf40e35855771202a,0x106aa07032bbd1b8
+	.quad	0x19a4c116b8d2d0c8,0x1e376c085141ab53
+	.quad	0x2748774cdf8eeb99,0x34b0bcb5e19b48a8
+	.quad	0x391c0cb3c5c95a63,0x4ed8aa4ae3418acb
+	.quad	0x5b9cca4f7763e373,0x682e6ff3d6b2b8a3
+	.quad	0x748f82ee5defb2fc,0x78a5636f43172f60
+	.quad	0x84c87814a1f0ab72,0x8cc702081a6439ec
+	.quad	0x90befffa23631e28,0xa4506cebde82bde9
+	.quad	0xbef9a3f7b2c67915,0xc67178f2e372532b
+	.quad	0xca273eceea26619c,0xd186b8c721c0c207
+	.quad	0xeada7dd6cde0eb1e,0xf57d4f7fee6ed178
+	.quad	0x06f067aa72176fba,0x0a637dc5a2c898a6
+	.quad	0x113f9804bef90dae,0x1b710b35131c471b
+	.quad	0x28db77f523047d84,0x32caab7b40c72493
+	.quad	0x3c9ebe0a15c9bebc,0x431d67c49c100d4c
+	.quad	0x4cc5d4becb3e42b6,0x597f299cfc657e2a
+	.quad	0x5fcb6fab3ad6faec,0x6c44198c4a475817
+	.quad	0	// terminator
+___
+$code.=<<___ if ($SZ==4);
+	.long	0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5
+	.long	0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5
+	.long	0xd807aa98,0x12835b01,0x243185be,0x550c7dc3
+	.long	0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174
+	.long	0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc
+	.long	0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da
+	.long	0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7
+	.long	0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967
+	.long	0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13
+	.long	0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85
+	.long	0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3
+	.long	0xd192e819,0xd6990624,0xf40e3585,0x106aa070
+	.long	0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5
+	.long	0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3
+	.long	0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208
+	.long	0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2
+	.long	0	//terminator
+___
+$code.=<<___;
+.size	.LK$BITS,.-.LK$BITS
+.align	3
+.LOPENSSL_armcap_P:
+#ifdef	__ILP32__
+	.long	OPENSSL_armcap_P-.
+#else
+	.quad	OPENSSL_armcap_P-.
+#endif
+.asciz	"SHA$BITS block transform for ARMv8, CRYPTOGAMS by "
+.align	2
+___
+
+if ($SZ==4) {
+my $Ktbl="x3";
+
+my ($ABCD,$EFGH,$abcd)=map("v$_.16b",(0..2));
+my @MSG=map("v$_.16b",(4..7));
+my ($W0,$W1)=("v16.4s","v17.4s");
+my ($ABCD_SAVE,$EFGH_SAVE)=("v18.16b","v19.16b");
+
+$code.=<<___;
+.type	sha256_block_armv8,%function
+.align	6
+sha256_block_armv8:
+.Lv8_entry:
+	stp		x29,x30,[sp,#-16]!
+	add		x29,sp,#0
+
+	ld1.32		{$ABCD,$EFGH},[$ctx]
+	adr		$Ktbl,.LK256
+
+.Loop_hw:
+	ld1		{@MSG[0]-@MSG[3]},[$inp],#64
+	sub		$num,$num,#1
+	ld1.32		{$W0},[$Ktbl],#16
+	rev32		@MSG[0],@MSG[0]
+	rev32		@MSG[1],@MSG[1]
+	rev32		@MSG[2],@MSG[2]
+	rev32		@MSG[3],@MSG[3]
+	orr		$ABCD_SAVE,$ABCD,$ABCD		// offload
+	orr		$EFGH_SAVE,$EFGH,$EFGH
+___
+for($i=0;$i<12;$i++) {
+$code.=<<___;
+	ld1.32		{$W1},[$Ktbl],#16
+	add.i32		$W0,$W0,@MSG[0]
+	sha256su0	@MSG[0],@MSG[1]
+	orr		$abcd,$ABCD,$ABCD
+	sha256h		$ABCD,$EFGH,$W0
+	sha256h2	$EFGH,$abcd,$W0
+	sha256su1	@MSG[0],@MSG[2],@MSG[3]
+___
+	($W0,$W1)=($W1,$W0);	push(@MSG,shift(@MSG));
+}
+$code.=<<___;
+	ld1.32		{$W1},[$Ktbl],#16
+	add.i32		$W0,$W0,@MSG[0]
+	orr		$abcd,$ABCD,$ABCD
+	sha256h		$ABCD,$EFGH,$W0
+	sha256h2	$EFGH,$abcd,$W0
+
+	ld1.32		{$W0},[$Ktbl],#16
+	add.i32		$W1,$W1,@MSG[1]
+	orr		$abcd,$ABCD,$ABCD
+	sha256h		$ABCD,$EFGH,$W1
+	sha256h2	$EFGH,$abcd,$W1
+
+	ld1.32		{$W1},[$Ktbl]
+	add.i32		$W0,$W0,@MSG[2]
+	sub		$Ktbl,$Ktbl,#$rounds*$SZ-16	// rewind
+	orr		$abcd,$ABCD,$ABCD
+	sha256h		$ABCD,$EFGH,$W0
+	sha256h2	$EFGH,$abcd,$W0
+
+	add.i32		$W1,$W1,@MSG[3]
+	orr		$abcd,$ABCD,$ABCD
+	sha256h		$ABCD,$EFGH,$W1
+	sha256h2	$EFGH,$abcd,$W1
+
+	add.i32		$ABCD,$ABCD,$ABCD_SAVE
+	add.i32		$EFGH,$EFGH,$EFGH_SAVE
+
+	cbnz		$num,.Loop_hw
+
+	st1.32		{$ABCD,$EFGH},[$ctx]
+
+	ldr		x29,[sp],#16
+	ret
+.size	sha256_block_armv8,.-sha256_block_armv8
+___
+}
+
+$code.=<<___;
+.comm	OPENSSL_armcap_P,4,4
+___
+
+{   my  %opcode = (
+	"sha256h"	=> 0x5e004000,	"sha256h2"	=> 0x5e005000,
+	"sha256su0"	=> 0x5e282800,	"sha256su1"	=> 0x5e006000	);
+
+    sub unsha256 {
+	my ($mnemonic,$arg)=@_;
+
+	$arg =~ m/[qv]([0-9]+)[^,]*,\s*[qv]([0-9]+)[^,]*(?:,\s*[qv]([0-9]+))?/o
+	&&
+	sprintf ".inst\t0x%08x\t//%s %s",
+			$opcode{$mnemonic}|$1|($2<<5)|($3<<16),
+			$mnemonic,$arg;
+    }
+}
+
+foreach(split("\n",$code)) {
+
+	s/\`([^\`]*)\`/eval($1)/geo;
+
+	s/\b(sha256\w+)\s+([qv].*)/unsha256($1,$2)/geo;
+
+	s/\.\w?32\b//o		and s/\.16b/\.4s/go;
+	m/(ld|st)1[^\[]+\[0\]/o	and s/\.4s/\.s/go;
+
+	print $_,"\n";
+}
+
+close STDOUT;
diff --git a/crypto/sha/asm/sha512-c64xplus.pl b/crypto/sha/asm/sha512-c64xplus.pl
new file mode 100644
index 000000000..9ebfc92e2
--- /dev/null
+++ b/crypto/sha/asm/sha512-c64xplus.pl
@@ -0,0 +1,438 @@
+#! /usr/bin/env perl
+# Copyright 2012-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+#
+# ====================================================================
+# Written by Andy Polyakov  for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+#
+# SHA512 for C64x+.
+#
+# January 2012
+#
+# Performance is 19 cycles per processed byte. Compared to block
+# transform function from sha512.c compiled with cl6x with -mv6400+
+# -o2 -DOPENSSL_SMALL_FOOTPRINT it's almost 7x faster and 2x smaller.
+# Loop unroll won't make it, this implementation, any faster, because
+# it's effectively dominated by SHRU||SHL pairs and you can't schedule
+# more of them.
+#
+# !!! Note that this module uses AMR, which means that all interrupt
+# service routines are expected to preserve it and for own well-being
+# zero it upon entry.
+
+while (($output=shift) && ($output!~/\w[\w\-]*\.\w+$/)) {}
+open STDOUT,">$output";
+
+($CTXA,$INP,$NUM) = ("A4","B4","A6");            # arguments
+ $K512="A3";
+
+($Ahi,$Actxhi,$Bhi,$Bctxhi,$Chi,$Cctxhi,$Dhi,$Dctxhi,
+ $Ehi,$Ectxhi,$Fhi,$Fctxhi,$Ghi,$Gctxhi,$Hhi,$Hctxhi)=map("A$_",(16..31));
+($Alo,$Actxlo,$Blo,$Bctxlo,$Clo,$Cctxlo,$Dlo,$Dctxlo,
+ $Elo,$Ectxlo,$Flo,$Fctxlo,$Glo,$Gctxlo,$Hlo,$Hctxlo)=map("B$_",(16..31));
+
+($S1hi,$CHhi,$S0hi,$t0hi)=map("A$_",(10..13));
+($S1lo,$CHlo,$S0lo,$t0lo)=map("B$_",(10..13));
+($T1hi,         $T2hi)=         ("A6","A7");
+($T1lo,$T1carry,$T2lo,$T2carry)=("B6","B7","B8","B9");
+($Khi,$Klo)=("A9","A8");
+($MAJhi,$MAJlo)=($T2hi,$T2lo);
+($t1hi,$t1lo)=($Khi,"B2");
+ $CTXB=$t1lo;
+
+($Xihi,$Xilo)=("A5","B5");			# circular/ring buffer
+
+$code.=<<___;
+	.text
+
+	.if	.ASSEMBLER_VERSION<7000000
+	.asg	0,__TI_EABI__
+	.endif
+	.if	__TI_EABI__
+	.nocmp
+	.asg	sha512_block_data_order,_sha512_block_data_order
+	.endif
+
+	.asg	B3,RA
+	.asg	A15,FP
+	.asg	B15,SP
+
+	.if	.BIG_ENDIAN
+	.asg	$Khi,KHI
+	.asg	$Klo,KLO
+	.else
+	.asg	$Khi,KLO
+	.asg	$Klo,KHI
+	.endif
+
+	.global	_sha512_block_data_order
+_sha512_block_data_order:
+__sha512_block:
+	.asmfunc stack_usage(40+128)
+	MV	$NUM,A0				; reassign $NUM
+||	MVK	-128,B0
+  [!A0]	BNOP	RA				; if ($NUM==0) return;
+|| [A0]	STW	FP,*SP--(40)			; save frame pointer
+|| [A0]	MV	SP,FP
+   [A0]	STDW	B13:B12,*SP[4]
+|| [A0]	MVK	0x00404,B1
+   [A0]	STDW	B11:B10,*SP[3]
+|| [A0]	STDW	A13:A12,*FP[-3]
+|| [A0]	MVKH	0x60000,B1
+   [A0]	STDW	A11:A10,*SP[1]
+|| [A0]	MVC	B1,AMR				; setup circular addressing
+|| [A0]	ADD	B0,SP,SP			; alloca(128)
+	.if	__TI_EABI__
+   [A0]	AND	B0,SP,SP			; align stack at 128 bytes
+|| [A0]	ADDKPC	__sha512_block,B1
+|| [A0]	MVKL	\$PCR_OFFSET(K512,__sha512_block),$K512
+   [A0]	MVKH	\$PCR_OFFSET(K512,__sha512_block),$K512
+|| [A0]	SUBAW	SP,2,SP				; reserve two words above buffer
+	.else
+   [A0]	AND	B0,SP,SP			; align stack at 128 bytes
+|| [A0]	ADDKPC	__sha512_block,B1
+|| [A0]	MVKL	(K512-__sha512_block),$K512
+   [A0]	MVKH	(K512-__sha512_block),$K512
+|| [A0]	SUBAW	SP,2,SP				; reserve two words above buffer
+	.endif
+	ADDAW	SP,3,$Xilo
+	ADDAW	SP,2,$Xihi
+
+||	MV	$CTXA,$CTXB
+	LDW	*${CTXA}[0^.LITTLE_ENDIAN],$Ahi	; load ctx
+||	LDW	*${CTXB}[1^.LITTLE_ENDIAN],$Alo
+||	ADD	B1,$K512,$K512
+	LDW	*${CTXA}[2^.LITTLE_ENDIAN],$Bhi
+||	LDW	*${CTXB}[3^.LITTLE_ENDIAN],$Blo
+	LDW	*${CTXA}[4^.LITTLE_ENDIAN],$Chi
+||	LDW	*${CTXB}[5^.LITTLE_ENDIAN],$Clo
+	LDW	*${CTXA}[6^.LITTLE_ENDIAN],$Dhi
+||	LDW	*${CTXB}[7^.LITTLE_ENDIAN],$Dlo
+	LDW	*${CTXA}[8^.LITTLE_ENDIAN],$Ehi
+||	LDW	*${CTXB}[9^.LITTLE_ENDIAN],$Elo
+	LDW	*${CTXA}[10^.LITTLE_ENDIAN],$Fhi
+||	LDW	*${CTXB}[11^.LITTLE_ENDIAN],$Flo
+	LDW	*${CTXA}[12^.LITTLE_ENDIAN],$Ghi
+||	LDW	*${CTXB}[13^.LITTLE_ENDIAN],$Glo
+	LDW	*${CTXA}[14^.LITTLE_ENDIAN],$Hhi
+||	LDW	*${CTXB}[15^.LITTLE_ENDIAN],$Hlo
+
+	LDNDW	*$INP++,B11:B10			; pre-fetch input
+	LDDW	*$K512++,$Khi:$Klo		; pre-fetch K512[0]
+outerloop?:
+	MVK	15,B0				; loop counters
+||	MVK	64,B1
+||	SUB	A0,1,A0
+	MV	$Ahi,$Actxhi
+||	MV	$Alo,$Actxlo
+||	MV	$Bhi,$Bctxhi
+||	MV	$Blo,$Bctxlo
+||	MV	$Chi,$Cctxhi
+||	MV	$Clo,$Cctxlo
+||	MVD	$Dhi,$Dctxhi
+||	MVD	$Dlo,$Dctxlo
+	MV	$Ehi,$Ectxhi
+||	MV	$Elo,$Ectxlo
+||	MV	$Fhi,$Fctxhi
+||	MV	$Flo,$Fctxlo
+||	MV	$Ghi,$Gctxhi
+||	MV	$Glo,$Gctxlo
+||	MVD	$Hhi,$Hctxhi
+||	MVD	$Hlo,$Hctxlo
+loop0_15?:
+	.if	.BIG_ENDIAN
+	MV	B11,$T1hi
+||	MV	B10,$T1lo
+	.else
+	SWAP4	B10,$T1hi
+||	SWAP4	B11,$T1lo
+	SWAP2	$T1hi,$T1hi
+||	SWAP2	$T1lo,$T1lo
+	.endif
+loop16_79?:
+	STW	$T1hi,*$Xihi++[2]
+||	STW	$T1lo,*$Xilo++[2]			; X[i] = T1
+||	ADD	$Hhi,$T1hi,$T1hi
+||	ADDU	$Hlo,$T1lo,$T1carry:$T1lo		; T1 += h
+||	SHRU	$Ehi,14,$S1hi
+||	SHL	$Ehi,32-14,$S1lo
+	XOR	$Fhi,$Ghi,$CHhi
+||	XOR	$Flo,$Glo,$CHlo
+||	ADD	KHI,$T1hi,$T1hi
+||	ADDU	KLO,$T1carry:$T1lo,$T1carry:$T1lo	; T1 += K512[i]
+||	SHRU	$Elo,14,$t0lo
+||	SHL	$Elo,32-14,$t0hi
+	XOR	$t0hi,$S1hi,$S1hi
+||	XOR	$t0lo,$S1lo,$S1lo
+||	AND	$Ehi,$CHhi,$CHhi
+||	AND	$Elo,$CHlo,$CHlo
+||	ROTL	$Ghi,0,$Hhi
+||	ROTL	$Glo,0,$Hlo				; h = g
+||	SHRU	$Ehi,18,$t0hi
+||	SHL	$Ehi,32-18,$t0lo
+	XOR	$t0hi,$S1hi,$S1hi
+||	XOR	$t0lo,$S1lo,$S1lo
+||	XOR	$Ghi,$CHhi,$CHhi
+||	XOR	$Glo,$CHlo,$CHlo			; Ch(e,f,g) = ((f^g)&e)^g
+||	ROTL	$Fhi,0,$Ghi
+||	ROTL	$Flo,0,$Glo				; g = f
+||	SHRU	$Elo,18,$t0lo
+||	SHL	$Elo,32-18,$t0hi
+	XOR	$t0hi,$S1hi,$S1hi
+||	XOR	$t0lo,$S1lo,$S1lo
+||	OR	$Ahi,$Bhi,$MAJhi
+||	OR	$Alo,$Blo,$MAJlo
+||	ROTL	$Ehi,0,$Fhi
+||	ROTL	$Elo,0,$Flo				; f = e
+||	SHRU	$Ehi,41-32,$t0lo
+||	SHL	$Ehi,64-41,$t0hi
+	XOR	$t0hi,$S1hi,$S1hi
+||	XOR	$t0lo,$S1lo,$S1lo
+||	AND	$Chi,$MAJhi,$MAJhi
+||	AND	$Clo,$MAJlo,$MAJlo
+||	ROTL	$Dhi,0,$Ehi
+||	ROTL	$Dlo,0,$Elo				; e = d
+||	SHRU	$Elo,41-32,$t0hi
+||	SHL	$Elo,64-41,$t0lo
+	XOR	$t0hi,$S1hi,$S1hi
+||	XOR	$t0lo,$S1lo,$S1lo			; Sigma1(e)
+||	AND	$Ahi,$Bhi,$t1hi
+||	AND	$Alo,$Blo,$t1lo
+||	ROTL	$Chi,0,$Dhi
+||	ROTL	$Clo,0,$Dlo				; d = c
+||	SHRU	$Ahi,28,$S0hi
+||	SHL	$Ahi,32-28,$S0lo
+	OR	$t1hi,$MAJhi,$MAJhi
+||	OR	$t1lo,$MAJlo,$MAJlo			; Maj(a,b,c) = ((a|b)&c)|(a&b)
+||	ADD	$CHhi,$T1hi,$T1hi
+||	ADDU	$CHlo,$T1carry:$T1lo,$T1carry:$T1lo	; T1 += Ch(e,f,g)
+||	ROTL	$Bhi,0,$Chi
+||	ROTL	$Blo,0,$Clo				; c = b
+||	SHRU	$Alo,28,$t0lo
+||	SHL	$Alo,32-28,$t0hi
+	XOR	$t0hi,$S0hi,$S0hi
+||	XOR	$t0lo,$S0lo,$S0lo
+||	ADD	$S1hi,$T1hi,$T1hi
+||	ADDU	$S1lo,$T1carry:$T1lo,$T1carry:$T1lo	; T1 += Sigma1(e)
+||	ROTL	$Ahi,0,$Bhi
+||	ROTL	$Alo,0,$Blo				; b = a
+||	SHRU	$Ahi,34-32,$t0lo
+||	SHL	$Ahi,64-34,$t0hi
+	XOR	$t0hi,$S0hi,$S0hi
+||	XOR	$t0lo,$S0lo,$S0lo
+||	ADD	$MAJhi,$T1hi,$T2hi
+||	ADDU	$MAJlo,$T1carry:$T1lo,$T2carry:$T2lo	; T2 = T1+Maj(a,b,c)
+||	SHRU	$Alo,34-32,$t0hi
+||	SHL	$Alo,64-34,$t0lo
+	XOR	$t0hi,$S0hi,$S0hi
+||	XOR	$t0lo,$S0lo,$S0lo
+||	ADD	$Ehi,$T1hi,$T1hi
+||	ADDU	$Elo,$T1carry:$T1lo,$T1carry:$T1lo	; T1 += e
+|| [B0]	BNOP	loop0_15?
+||	SHRU	$Ahi,39-32,$t0lo
+||	SHL	$Ahi,64-39,$t0hi
+	XOR	$t0hi,$S0hi,$S0hi
+||	XOR	$t0lo,$S0lo,$S0lo
+|| [B0]	LDNDW	*$INP++,B11:B10				; pre-fetch input
+||[!B1]	BNOP	break?
+||	SHRU	$Alo,39-32,$t0hi
+||	SHL	$Alo,64-39,$t0lo
+	XOR	$t0hi,$S0hi,$S0hi
+||	XOR	$t0lo,$S0lo,$S0lo			; Sigma0(a)
+||	ADD	$T1carry,$T1hi,$Ehi
+||	MV	$T1lo,$Elo				; e = T1
+||[!B0]	LDW	*${Xihi}[28],$T1hi
+||[!B0]	LDW	*${Xilo}[28],$T1lo			; X[i+14]
+	ADD	$S0hi,$T2hi,$T2hi
+||	ADDU	$S0lo,$T2carry:$T2lo,$T2carry:$T2lo	; T2 += Sigma0(a)
+|| [B1]	LDDW	*$K512++,$Khi:$Klo			; pre-fetch K512[i]
+	NOP						; avoid cross-path stall
+	ADD	$T2carry,$T2hi,$Ahi
+||	MV	$T2lo,$Alo				; a = T2
+|| [B0]	SUB	B0,1,B0
+;;===== branch to loop00_15? is taken here
+	NOP
+;;===== branch to break? is taken here
+	LDW	*${Xihi}[2],$T2hi
+||	LDW	*${Xilo}[2],$T2lo			; X[i+1]
+||	SHRU	$T1hi,19,$S1hi
+||	SHL	$T1hi,32-19,$S1lo
+	SHRU	$T1lo,19,$t0lo
+||	SHL	$T1lo,32-19,$t0hi
+	XOR	$t0hi,$S1hi,$S1hi
+||	XOR	$t0lo,$S1lo,$S1lo
+||	SHRU	$T1hi,61-32,$t0lo
+||	SHL	$T1hi,64-61,$t0hi
+	XOR	$t0hi,$S1hi,$S1hi
+||	XOR	$t0lo,$S1lo,$S1lo
+||	SHRU	$T1lo,61-32,$t0hi
+||	SHL	$T1lo,64-61,$t0lo
+	XOR	$t0hi,$S1hi,$S1hi
+||	XOR	$t0lo,$S1lo,$S1lo
+||	SHRU	$T1hi,6,$t0hi
+||	SHL	$T1hi,32-6,$t0lo
+	XOR	$t0hi,$S1hi,$S1hi
+||	XOR	$t0lo,$S1lo,$S1lo
+||	SHRU	$T1lo,6,$t0lo
+||	LDW	*${Xihi}[18],$T1hi
+||	LDW	*${Xilo}[18],$T1lo			; X[i+9]
+	XOR	$t0lo,$S1lo,$S1lo			; sigma1(Xi[i+14])
+
+||	LDW	*${Xihi}[0],$CHhi
+||	LDW	*${Xilo}[0],$CHlo			; X[i]
+||	SHRU	$T2hi,1,$S0hi
+||	SHL	$T2hi,32-1,$S0lo
+	SHRU	$T2lo,1,$t0lo
+||	SHL	$T2lo,32-1,$t0hi
+	XOR	$t0hi,$S0hi,$S0hi
+||	XOR	$t0lo,$S0lo,$S0lo
+||	SHRU	$T2hi,8,$t0hi
+||	SHL	$T2hi,32-8,$t0lo
+	XOR	$t0hi,$S0hi,$S0hi
+||	XOR	$t0lo,$S0lo,$S0lo
+||	SHRU	$T2lo,8,$t0lo
+||	SHL	$T2lo,32-8,$t0hi
+	XOR	$t0hi,$S0hi,$S0hi
+||	XOR	$t0lo,$S0lo,$S0lo
+||	ADD	$S1hi,$T1hi,$T1hi
+||	ADDU	$S1lo,$T1lo,$T1carry:$T1lo		; T1 = X[i+9]+sigma1()
+|| [B1]	BNOP	loop16_79?
+||	SHRU	$T2hi,7,$t0hi
+||	SHL	$T2hi,32-7,$t0lo
+	XOR	$t0hi,$S0hi,$S0hi
+||	XOR	$t0lo,$S0lo,$S0lo
+||	ADD	$CHhi,$T1hi,$T1hi
+||	ADDU	$CHlo,$T1carry:$T1lo,$T1carry:$T1lo	; T1 += X[i]
+||	SHRU	$T2lo,7,$t0lo
+	XOR	$t0lo,$S0lo,$S0lo			; sigma0(Xi[i+1]
+
+	ADD	$S0hi,$T1hi,$T1hi
+||	ADDU	$S0lo,$T1carry:$T1lo,$T1carry:$T1lo	; T1 += sigma0()
+|| [B1]	SUB	B1,1,B1
+	NOP						; avoid cross-path stall
+	ADD	$T1carry,$T1hi,$T1hi
+;;===== branch to loop16_79? is taken here
+
+break?:
+	ADD	$Ahi,$Actxhi,$Ahi		; accumulate ctx
+||	ADDU	$Alo,$Actxlo,$Actxlo:$Alo
+|| [A0]	LDNDW	*$INP++,B11:B10			; pre-fetch input
+|| [A0]	ADDK	-640,$K512			; rewind pointer to K512
+	ADD	$Bhi,$Bctxhi,$Bhi
+||	ADDU	$Blo,$Bctxlo,$Bctxlo:$Blo
+|| [A0]	LDDW	*$K512++,$Khi:$Klo		; pre-fetch K512[0]
+	ADD	$Chi,$Cctxhi,$Chi
+||	ADDU	$Clo,$Cctxlo,$Cctxlo:$Clo
+||	ADD	$Actxlo,$Ahi,$Ahi
+||[!A0]	MV	$CTXA,$CTXB
+	ADD	$Dhi,$Dctxhi,$Dhi
+||	ADDU	$Dlo,$Dctxlo,$Dctxlo:$Dlo
+||	ADD	$Bctxlo,$Bhi,$Bhi
+||[!A0]	STW	$Ahi,*${CTXA}[0^.LITTLE_ENDIAN]	; save ctx
+||[!A0]	STW	$Alo,*${CTXB}[1^.LITTLE_ENDIAN]
+	ADD	$Ehi,$Ectxhi,$Ehi
+||	ADDU	$Elo,$Ectxlo,$Ectxlo:$Elo
+||	ADD	$Cctxlo,$Chi,$Chi
+|| [A0]	BNOP	outerloop?
+||[!A0]	STW	$Bhi,*${CTXA}[2^.LITTLE_ENDIAN]
+||[!A0]	STW	$Blo,*${CTXB}[3^.LITTLE_ENDIAN]
+	ADD	$Fhi,$Fctxhi,$Fhi
+||	ADDU	$Flo,$Fctxlo,$Fctxlo:$Flo
+||	ADD	$Dctxlo,$Dhi,$Dhi
+||[!A0]	STW	$Chi,*${CTXA}[4^.LITTLE_ENDIAN]
+||[!A0]	STW	$Clo,*${CTXB}[5^.LITTLE_ENDIAN]
+	ADD	$Ghi,$Gctxhi,$Ghi
+||	ADDU	$Glo,$Gctxlo,$Gctxlo:$Glo
+||	ADD	$Ectxlo,$Ehi,$Ehi
+||[!A0]	STW	$Dhi,*${CTXA}[6^.LITTLE_ENDIAN]
+||[!A0]	STW	$Dlo,*${CTXB}[7^.LITTLE_ENDIAN]
+	ADD	$Hhi,$Hctxhi,$Hhi
+||	ADDU	$Hlo,$Hctxlo,$Hctxlo:$Hlo
+||	ADD	$Fctxlo,$Fhi,$Fhi
+||[!A0]	STW	$Ehi,*${CTXA}[8^.LITTLE_ENDIAN]
+||[!A0]	STW	$Elo,*${CTXB}[9^.LITTLE_ENDIAN]
+	ADD	$Gctxlo,$Ghi,$Ghi
+||[!A0]	STW	$Fhi,*${CTXA}[10^.LITTLE_ENDIAN]
+||[!A0]	STW	$Flo,*${CTXB}[11^.LITTLE_ENDIAN]
+	ADD	$Hctxlo,$Hhi,$Hhi
+||[!A0]	STW	$Ghi,*${CTXA}[12^.LITTLE_ENDIAN]
+||[!A0]	STW	$Glo,*${CTXB}[13^.LITTLE_ENDIAN]
+;;===== branch to outerloop? is taken here
+
+	STW	$Hhi,*${CTXA}[14^.LITTLE_ENDIAN]
+||	STW	$Hlo,*${CTXB}[15^.LITTLE_ENDIAN]
+||	MVK	-40,B0
+	ADD	FP,B0,SP			; destroy circular buffer
+||	LDDW	*FP[-4],A11:A10
+	LDDW	*SP[2],A13:A12
+||	LDDW	*FP[-2],B11:B10
+	LDDW	*SP[4],B13:B12
+||	BNOP	RA
+	LDW	*++SP(40),FP			; restore frame pointer
+	MVK	0,B0
+	MVC	B0,AMR				; clear AMR
+	NOP	2				; wait till FP is committed
+	.endasmfunc
+
+	.if	__TI_EABI__
+	.sect	".text:sha_asm.const"
+	.else
+	.sect	".const:sha_asm"
+	.endif
+	.align	128
+K512:
+	.uword	0x428a2f98,0xd728ae22, 0x71374491,0x23ef65cd
+	.uword	0xb5c0fbcf,0xec4d3b2f, 0xe9b5dba5,0x8189dbbc
+	.uword	0x3956c25b,0xf348b538, 0x59f111f1,0xb605d019
+	.uword	0x923f82a4,0xaf194f9b, 0xab1c5ed5,0xda6d8118
+	.uword	0xd807aa98,0xa3030242, 0x12835b01,0x45706fbe
+	.uword	0x243185be,0x4ee4b28c, 0x550c7dc3,0xd5ffb4e2
+	.uword	0x72be5d74,0xf27b896f, 0x80deb1fe,0x3b1696b1
+	.uword	0x9bdc06a7,0x25c71235, 0xc19bf174,0xcf692694
+	.uword	0xe49b69c1,0x9ef14ad2, 0xefbe4786,0x384f25e3
+	.uword	0x0fc19dc6,0x8b8cd5b5, 0x240ca1cc,0x77ac9c65
+	.uword	0x2de92c6f,0x592b0275, 0x4a7484aa,0x6ea6e483
+	.uword	0x5cb0a9dc,0xbd41fbd4, 0x76f988da,0x831153b5
+	.uword	0x983e5152,0xee66dfab, 0xa831c66d,0x2db43210
+	.uword	0xb00327c8,0x98fb213f, 0xbf597fc7,0xbeef0ee4
+	.uword	0xc6e00bf3,0x3da88fc2, 0xd5a79147,0x930aa725
+	.uword	0x06ca6351,0xe003826f, 0x14292967,0x0a0e6e70
+	.uword	0x27b70a85,0x46d22ffc, 0x2e1b2138,0x5c26c926
+	.uword	0x4d2c6dfc,0x5ac42aed, 0x53380d13,0x9d95b3df
+	.uword	0x650a7354,0x8baf63de, 0x766a0abb,0x3c77b2a8
+	.uword	0x81c2c92e,0x47edaee6, 0x92722c85,0x1482353b
+	.uword	0xa2bfe8a1,0x4cf10364, 0xa81a664b,0xbc423001
+	.uword	0xc24b8b70,0xd0f89791, 0xc76c51a3,0x0654be30
+	.uword	0xd192e819,0xd6ef5218, 0xd6990624,0x5565a910
+	.uword	0xf40e3585,0x5771202a, 0x106aa070,0x32bbd1b8
+	.uword	0x19a4c116,0xb8d2d0c8, 0x1e376c08,0x5141ab53
+	.uword	0x2748774c,0xdf8eeb99, 0x34b0bcb5,0xe19b48a8
+	.uword	0x391c0cb3,0xc5c95a63, 0x4ed8aa4a,0xe3418acb
+	.uword	0x5b9cca4f,0x7763e373, 0x682e6ff3,0xd6b2b8a3
+	.uword	0x748f82ee,0x5defb2fc, 0x78a5636f,0x43172f60
+	.uword	0x84c87814,0xa1f0ab72, 0x8cc70208,0x1a6439ec
+	.uword	0x90befffa,0x23631e28, 0xa4506ceb,0xde82bde9
+	.uword	0xbef9a3f7,0xb2c67915, 0xc67178f2,0xe372532b
+	.uword	0xca273ece,0xea26619c, 0xd186b8c7,0x21c0c207
+	.uword	0xeada7dd6,0xcde0eb1e, 0xf57d4f7f,0xee6ed178
+	.uword	0x06f067aa,0x72176fba, 0x0a637dc5,0xa2c898a6
+	.uword	0x113f9804,0xbef90dae, 0x1b710b35,0x131c471b
+	.uword	0x28db77f5,0x23047d84, 0x32caab7b,0x40c72493
+	.uword	0x3c9ebe0a,0x15c9bebc, 0x431d67c4,0x9c100d4c
+	.uword	0x4cc5d4be,0xcb3e42b6, 0x597f299c,0xfc657e2a
+	.uword	0x5fcb6fab,0x3ad6faec, 0x6c44198c,0x4a475817
+	.cstring "SHA512 block transform for C64x+, CRYPTOGAMS by "
+	.align	4
+___
+
+print $code;
+close STDOUT;
diff --git a/crypto/sha/asm/sha512-ia64.pl b/crypto/sha/asm/sha512-ia64.pl
new file mode 100755
index 000000000..356a46ace
--- /dev/null
+++ b/crypto/sha/asm/sha512-ia64.pl
@@ -0,0 +1,692 @@
+#! /usr/bin/env perl
+# Copyright 2004-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+#
+# ====================================================================
+# Written by Andy Polyakov  for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+#
+# SHA256/512_Transform for Itanium.
+#
+# sha512_block runs in 1003 cycles on Itanium 2, which is almost 50%
+# faster than gcc and >60%(!) faster than code generated by HP-UX
+# compiler (yes, HP-UX is generating slower code, because unlike gcc,
+# it failed to deploy "shift right pair," 'shrp' instruction, which
+# substitutes for 64-bit rotate).
+#
+# 924 cycles long sha256_block outperforms gcc by over factor of 2(!)
+# and HP-UX compiler - by >40% (yes, gcc won sha512_block, but lost
+# this one big time). Note that "formally" 924 is about 100 cycles
+# too much. I mean it's 64 32-bit rounds vs. 80 virtually identical
+# 64-bit ones and 1003*64/80 gives 802. Extra cycles, 2 per round,
+# are spent on extra work to provide for 32-bit rotations. 32-bit
+# rotations are still handled by 'shrp' instruction and for this
+# reason lower 32 bits are deposited to upper half of 64-bit register
+# prior 'shrp' issue. And in order to minimize the amount of such
+# operations, X[16] values are *maintained* with copies of lower
+# halves in upper halves, which is why you'll spot such instructions
+# as custom 'mux2', "parallel 32-bit add," 'padd4' and "parallel
+# 32-bit unsigned right shift," 'pshr4.u' instructions here.
+#
+# Rules of engagement.
+#
+# There is only one integer shifter meaning that if I have two rotate,
+# deposit or extract instructions in adjacent bundles, they shall
+# split [at run-time if they have to]. But note that variable and
+# parallel shifts are performed by multi-media ALU and *are* pairable
+# with rotates [and alike]. On the backside MMALU is rather slow: it
+# takes 2 extra cycles before the result of integer operation is
+# available *to* MMALU and 2(*) extra cycles before the result of MM
+# operation is available "back" *to* integer ALU, not to mention that
+# MMALU itself has 2 cycles latency. However! I explicitly scheduled
+# these MM instructions to avoid MM stalls, so that all these extra
+# latencies get "hidden" in instruction-level parallelism.
+#
+# (*) 2 cycles on Itanium 1 and 1 cycle on Itanium 2. But I schedule
+#     for 2 in order to provide for best *overall* performance,
+#     because on Itanium 1 stall on MM result is accompanied by
+#     pipeline flush, which takes 6 cycles:-(
+#
+# June 2012
+#
+# Improve performance by 15-20%. Note about "rules of engagement"
+# above. Contemporary cores are equipped with additional shifter,
+# so that they should perform even better than below, presumably
+# by ~10%.
+#
+######################################################################
+# Current performance in cycles per processed byte for Itanium 2
+# pre-9000 series [little-endian] system:
+#
+# SHA1(*)	5.7
+# SHA256	12.6
+# SHA512	6.7
+#
+# (*) SHA1 result is presented purely for reference purposes.
+#
+# To generate code, pass the file name with either 256 or 512 in its
+# name and compiler flags.
+
+$output=pop;
+
+if ($output =~ /512.*\.[s|asm]/) {
+	$SZ=8;
+	$BITS=8*$SZ;
+	$LDW="ld8";
+	$STW="st8";
+	$ADD="add";
+	$SHRU="shr.u";
+	$TABLE="K512";
+	$func="sha512_block_data_order";
+	@Sigma0=(28,34,39);
+	@Sigma1=(14,18,41);
+	@sigma0=(1,  8, 7);
+	@sigma1=(19,61, 6);
+	$rounds=80;
+} elsif ($output =~ /256.*\.[s|asm]/) {
+	$SZ=4;
+	$BITS=8*$SZ;
+	$LDW="ld4";
+	$STW="st4";
+	$ADD="padd4";
+	$SHRU="pshr4.u";
+	$TABLE="K256";
+	$func="sha256_block_data_order";
+	@Sigma0=( 2,13,22);
+	@Sigma1=( 6,11,25);
+	@sigma0=( 7,18, 3);
+	@sigma1=(17,19,10);
+	$rounds=64;
+} else { die "nonsense $output"; }
+
+open STDOUT,">$output" || die "can't open $output: $!";
+
+if ($^O eq "hpux") {
+    $ADDP="addp4";
+    for (@ARGV) { $ADDP="add" if (/[\+DD|\-mlp]64/); }
+} else { $ADDP="add"; }
+for (@ARGV)  {	$big_endian=1 if (/\-DB_ENDIAN/);
+		$big_endian=0 if (/\-DL_ENDIAN/);  }
+if (!defined($big_endian))
+             {	$big_endian=(unpack('L',pack('N',1))==1);  }
+
+$code=<<___;
+.ident  \"$output, version 2.0\"
+.ident  \"IA-64 ISA artwork by Andy Polyakov \"
+.explicit
+.text
+
+pfssave=r2;
+lcsave=r3;
+prsave=r14;
+K=r15;
+A_=r16; B_=r17; C_=r18; D_=r19;
+E_=r20; F_=r21; G_=r22; H_=r23;
+T1=r24;	T2=r25;
+s0=r26;	s1=r27;	t0=r28;	t1=r29;
+Ktbl=r30;
+ctx=r31;	// 1st arg
+input=r56;	// 2nd arg
+num=r57;	// 3rd arg
+sgm0=r58;	sgm1=r59;	// small constants
+
+// void $func (SHA_CTX *ctx, const void *in,size_t num[,int host])
+.global	$func#
+.proc	$func#
+.align	32
+.skip	16
+$func:
+	.prologue
+	.save	ar.pfs,pfssave
+{ .mmi;	alloc	pfssave=ar.pfs,3,25,0,24
+	$ADDP	ctx=0,r32		// 1st arg
+	.save	ar.lc,lcsave
+	mov	lcsave=ar.lc	}
+{ .mmi;	$ADDP	input=0,r33		// 2nd arg
+	mov	num=r34			// 3rd arg
+	.save	pr,prsave
+	mov	prsave=pr	};;
+
+	.body
+{ .mib;	add	r8=0*$SZ,ctx
+	add	r9=1*$SZ,ctx	}
+{ .mib;	add	r10=2*$SZ,ctx
+	add	r11=3*$SZ,ctx	};;
+
+// load A-H
+.Lpic_point:
+{ .mmi;	$LDW	A_=[r8],4*$SZ
+	$LDW	B_=[r9],4*$SZ
+	mov	Ktbl=ip		}
+{ .mmi;	$LDW	C_=[r10],4*$SZ
+	$LDW	D_=[r11],4*$SZ
+	mov	sgm0=$sigma0[2]	};;
+{ .mmi;	$LDW	E_=[r8]
+	$LDW	F_=[r9]
+	add	Ktbl=($TABLE#-.Lpic_point),Ktbl		}
+{ .mmi;	$LDW	G_=[r10]
+	$LDW	H_=[r11]
+	cmp.ne	p0,p16=0,r0	};;
+___
+$code.=<<___ if ($BITS==64);
+{ .mii;	and	r8=7,input
+	and	input=~7,input;;
+	cmp.eq	p9,p0=1,r8	}
+{ .mmi;	cmp.eq	p10,p0=2,r8
+	cmp.eq	p11,p0=3,r8
+	cmp.eq	p12,p0=4,r8	}
+{ .mmi;	cmp.eq	p13,p0=5,r8
+	cmp.eq	p14,p0=6,r8
+	cmp.eq	p15,p0=7,r8	};;
+___
+$code.=<<___;
+.L_outer:
+.rotr	R[8],X[16]
+A=R[0]; B=R[1]; C=R[2]; D=R[3]; E=R[4]; F=R[5]; G=R[6]; H=R[7]
+{ .mmi;	ld1	X[15]=[input],$SZ		// eliminated in sha512
+	mov	A=A_
+	mov	ar.lc=14	}
+{ .mmi;	mov	B=B_
+	mov	C=C_
+	mov	D=D_		}
+{ .mmi;	mov	E=E_
+	mov	F=F_
+	mov	ar.ec=2		};;
+{ .mmi;	mov	G=G_
+	mov	H=H_
+	mov	sgm1=$sigma1[2]	}
+{ .mib;	mov	r8=0
+	add	r9=1-$SZ,input
+	brp.loop.imp	.L_first16,.L_first16_end-16	};;
+___
+$t0="A", $t1="E", $code.=<<___ if ($BITS==64);
+// in sha512 case I load whole X[16] at once and take care of alignment...
+{ .mmi;	add	r8=1*$SZ,input
+	add	r9=2*$SZ,input
+	add	r10=3*$SZ,input		};;
+{ .mmb;	$LDW	X[15]=[input],4*$SZ
+	$LDW	X[14]=[r8],4*$SZ
+(p9)	br.cond.dpnt.many	.L1byte	};;
+{ .mmb;	$LDW	X[13]=[r9],4*$SZ
+	$LDW	X[12]=[r10],4*$SZ
+(p10)	br.cond.dpnt.many	.L2byte	};;
+{ .mmb;	$LDW	X[11]=[input],4*$SZ
+	$LDW	X[10]=[r8],4*$SZ
+(p11)	br.cond.dpnt.many	.L3byte	};;
+{ .mmb;	$LDW	X[ 9]=[r9],4*$SZ
+	$LDW	X[ 8]=[r10],4*$SZ
+(p12)	br.cond.dpnt.many	.L4byte	};;
+{ .mmb;	$LDW	X[ 7]=[input],4*$SZ
+	$LDW	X[ 6]=[r8],4*$SZ
+(p13)	br.cond.dpnt.many	.L5byte	};;
+{ .mmb;	$LDW	X[ 5]=[r9],4*$SZ
+	$LDW	X[ 4]=[r10],4*$SZ
+(p14)	br.cond.dpnt.many	.L6byte	};;
+{ .mmb;	$LDW	X[ 3]=[input],4*$SZ
+	$LDW	X[ 2]=[r8],4*$SZ
+(p15)	br.cond.dpnt.many	.L7byte	};;
+{ .mmb;	$LDW	X[ 1]=[r9],4*$SZ
+	$LDW	X[ 0]=[r10],4*$SZ	}
+{ .mib;	mov	r8=0
+	mux1	X[15]=X[15],\@rev		// eliminated on big-endian
+	br.many	.L_first16		};;
+.L1byte:
+{ .mmi;	$LDW	X[13]=[r9],4*$SZ
+	$LDW	X[12]=[r10],4*$SZ
+	shrp	X[15]=X[15],X[14],56	};;
+{ .mmi;	$LDW	X[11]=[input],4*$SZ
+	$LDW	X[10]=[r8],4*$SZ
+	shrp	X[14]=X[14],X[13],56	}
+{ .mmi;	$LDW	X[ 9]=[r9],4*$SZ
+	$LDW	X[ 8]=[r10],4*$SZ
+	shrp	X[13]=X[13],X[12],56	};;
+{ .mmi;	$LDW	X[ 7]=[input],4*$SZ
+	$LDW	X[ 6]=[r8],4*$SZ
+	shrp	X[12]=X[12],X[11],56	}
+{ .mmi;	$LDW	X[ 5]=[r9],4*$SZ
+	$LDW	X[ 4]=[r10],4*$SZ
+	shrp	X[11]=X[11],X[10],56	};;
+{ .mmi;	$LDW	X[ 3]=[input],4*$SZ
+	$LDW	X[ 2]=[r8],4*$SZ
+	shrp	X[10]=X[10],X[ 9],56	}
+{ .mmi;	$LDW	X[ 1]=[r9],4*$SZ
+	$LDW	X[ 0]=[r10],4*$SZ
+	shrp	X[ 9]=X[ 9],X[ 8],56	};;
+{ .mii;	$LDW	T1=[input]
+	shrp	X[ 8]=X[ 8],X[ 7],56
+	shrp	X[ 7]=X[ 7],X[ 6],56	}
+{ .mii;	shrp	X[ 6]=X[ 6],X[ 5],56
+	shrp	X[ 5]=X[ 5],X[ 4],56	};;
+{ .mii;	shrp	X[ 4]=X[ 4],X[ 3],56
+	shrp	X[ 3]=X[ 3],X[ 2],56	}
+{ .mii;	shrp	X[ 2]=X[ 2],X[ 1],56
+	shrp	X[ 1]=X[ 1],X[ 0],56	}
+{ .mib;	shrp	X[ 0]=X[ 0],T1,56	}
+{ .mib;	mov	r8=0
+	mux1	X[15]=X[15],\@rev		// eliminated on big-endian
+	br.many	.L_first16		};;
+.L2byte:
+{ .mmi;	$LDW	X[11]=[input],4*$SZ
+	$LDW	X[10]=[r8],4*$SZ
+	shrp	X[15]=X[15],X[14],48	}
+{ .mmi;	$LDW	X[ 9]=[r9],4*$SZ
+	$LDW	X[ 8]=[r10],4*$SZ
+	shrp	X[14]=X[14],X[13],48	};;
+{ .mmi;	$LDW	X[ 7]=[input],4*$SZ
+	$LDW	X[ 6]=[r8],4*$SZ
+	shrp	X[13]=X[13],X[12],48	}
+{ .mmi;	$LDW	X[ 5]=[r9],4*$SZ
+	$LDW	X[ 4]=[r10],4*$SZ
+	shrp	X[12]=X[12],X[11],48	};;
+{ .mmi;	$LDW	X[ 3]=[input],4*$SZ
+	$LDW	X[ 2]=[r8],4*$SZ
+	shrp	X[11]=X[11],X[10],48	}
+{ .mmi;	$LDW	X[ 1]=[r9],4*$SZ
+	$LDW	X[ 0]=[r10],4*$SZ
+	shrp	X[10]=X[10],X[ 9],48	};;
+{ .mii;	$LDW	T1=[input]
+	shrp	X[ 9]=X[ 9],X[ 8],48
+	shrp	X[ 8]=X[ 8],X[ 7],48	}
+{ .mii;	shrp	X[ 7]=X[ 7],X[ 6],48
+	shrp	X[ 6]=X[ 6],X[ 5],48	};;
+{ .mii;	shrp	X[ 5]=X[ 5],X[ 4],48
+	shrp	X[ 4]=X[ 4],X[ 3],48	}
+{ .mii;	shrp	X[ 3]=X[ 3],X[ 2],48
+	shrp	X[ 2]=X[ 2],X[ 1],48	}
+{ .mii;	shrp	X[ 1]=X[ 1],X[ 0],48
+	shrp	X[ 0]=X[ 0],T1,48	}
+{ .mib;	mov	r8=0
+	mux1	X[15]=X[15],\@rev		// eliminated on big-endian
+	br.many	.L_first16		};;
+.L3byte:
+{ .mmi;	$LDW	X[ 9]=[r9],4*$SZ
+	$LDW	X[ 8]=[r10],4*$SZ
+	shrp	X[15]=X[15],X[14],40	};;
+{ .mmi;	$LDW	X[ 7]=[input],4*$SZ
+	$LDW	X[ 6]=[r8],4*$SZ
+	shrp	X[14]=X[14],X[13],40	}
+{ .mmi;	$LDW	X[ 5]=[r9],4*$SZ
+	$LDW	X[ 4]=[r10],4*$SZ
+	shrp	X[13]=X[13],X[12],40	};;
+{ .mmi;	$LDW	X[ 3]=[input],4*$SZ
+	$LDW	X[ 2]=[r8],4*$SZ
+	shrp	X[12]=X[12],X[11],40	}
+{ .mmi;	$LDW	X[ 1]=[r9],4*$SZ
+	$LDW	X[ 0]=[r10],4*$SZ
+	shrp	X[11]=X[11],X[10],40	};;
+{ .mii;	$LDW	T1=[input]
+	shrp	X[10]=X[10],X[ 9],40
+	shrp	X[ 9]=X[ 9],X[ 8],40	}
+{ .mii;	shrp	X[ 8]=X[ 8],X[ 7],40
+	shrp	X[ 7]=X[ 7],X[ 6],40	};;
+{ .mii;	shrp	X[ 6]=X[ 6],X[ 5],40
+	shrp	X[ 5]=X[ 5],X[ 4],40	}
+{ .mii;	shrp	X[ 4]=X[ 4],X[ 3],40
+	shrp	X[ 3]=X[ 3],X[ 2],40	}
+{ .mii;	shrp	X[ 2]=X[ 2],X[ 1],40
+	shrp	X[ 1]=X[ 1],X[ 0],40	}
+{ .mib;	shrp	X[ 0]=X[ 0],T1,40	}
+{ .mib;	mov	r8=0
+	mux1	X[15]=X[15],\@rev		// eliminated on big-endian
+	br.many	.L_first16		};;
+.L4byte:
+{ .mmi;	$LDW	X[ 7]=[input],4*$SZ
+	$LDW	X[ 6]=[r8],4*$SZ
+	shrp	X[15]=X[15],X[14],32	}
+{ .mmi;	$LDW	X[ 5]=[r9],4*$SZ
+	$LDW	X[ 4]=[r10],4*$SZ
+	shrp	X[14]=X[14],X[13],32	};;
+{ .mmi;	$LDW	X[ 3]=[input],4*$SZ
+	$LDW	X[ 2]=[r8],4*$SZ
+	shrp	X[13]=X[13],X[12],32	}
+{ .mmi;	$LDW	X[ 1]=[r9],4*$SZ
+	$LDW	X[ 0]=[r10],4*$SZ
+	shrp	X[12]=X[12],X[11],32	};;
+{ .mii;	$LDW	T1=[input]
+	shrp	X[11]=X[11],X[10],32
+	shrp	X[10]=X[10],X[ 9],32	}
+{ .mii;	shrp	X[ 9]=X[ 9],X[ 8],32
+	shrp	X[ 8]=X[ 8],X[ 7],32	};;
+{ .mii;	shrp	X[ 7]=X[ 7],X[ 6],32
+	shrp	X[ 6]=X[ 6],X[ 5],32	}
+{ .mii;	shrp	X[ 5]=X[ 5],X[ 4],32
+	shrp	X[ 4]=X[ 4],X[ 3],32	}
+{ .mii;	shrp	X[ 3]=X[ 3],X[ 2],32
+	shrp	X[ 2]=X[ 2],X[ 1],32	}
+{ .mii;	shrp	X[ 1]=X[ 1],X[ 0],32
+	shrp	X[ 0]=X[ 0],T1,32	}
+{ .mib;	mov	r8=0
+	mux1	X[15]=X[15],\@rev		// eliminated on big-endian
+	br.many	.L_first16		};;
+.L5byte:
+{ .mmi;	$LDW	X[ 5]=[r9],4*$SZ
+	$LDW	X[ 4]=[r10],4*$SZ
+	shrp	X[15]=X[15],X[14],24	};;
+{ .mmi;	$LDW	X[ 3]=[input],4*$SZ
+	$LDW	X[ 2]=[r8],4*$SZ
+	shrp	X[14]=X[14],X[13],24	}
+{ .mmi;	$LDW	X[ 1]=[r9],4*$SZ
+	$LDW	X[ 0]=[r10],4*$SZ
+	shrp	X[13]=X[13],X[12],24	};;
+{ .mii;	$LDW	T1=[input]
+	shrp	X[12]=X[12],X[11],24
+	shrp	X[11]=X[11],X[10],24	}
+{ .mii;	shrp	X[10]=X[10],X[ 9],24
+	shrp	X[ 9]=X[ 9],X[ 8],24	};;
+{ .mii;	shrp	X[ 8]=X[ 8],X[ 7],24
+	shrp	X[ 7]=X[ 7],X[ 6],24	}
+{ .mii;	shrp	X[ 6]=X[ 6],X[ 5],24
+	shrp	X[ 5]=X[ 5],X[ 4],24	}
+{ .mii;	shrp	X[ 4]=X[ 4],X[ 3],24
+	shrp	X[ 3]=X[ 3],X[ 2],24	}
+{ .mii;	shrp	X[ 2]=X[ 2],X[ 1],24
+	shrp	X[ 1]=X[ 1],X[ 0],24	}
+{ .mib;	shrp	X[ 0]=X[ 0],T1,24	}
+{ .mib;	mov	r8=0
+	mux1	X[15]=X[15],\@rev		// eliminated on big-endian
+	br.many	.L_first16		};;
+.L6byte:
+{ .mmi;	$LDW	X[ 3]=[input],4*$SZ
+	$LDW	X[ 2]=[r8],4*$SZ
+	shrp	X[15]=X[15],X[14],16	}
+{ .mmi;	$LDW	X[ 1]=[r9],4*$SZ
+	$LDW	X[ 0]=[r10],4*$SZ
+	shrp	X[14]=X[14],X[13],16	};;
+{ .mii;	$LDW	T1=[input]
+	shrp	X[13]=X[13],X[12],16
+	shrp	X[12]=X[12],X[11],16	}
+{ .mii;	shrp	X[11]=X[11],X[10],16
+	shrp	X[10]=X[10],X[ 9],16	};;
+{ .mii;	shrp	X[ 9]=X[ 9],X[ 8],16
+	shrp	X[ 8]=X[ 8],X[ 7],16	}
+{ .mii;	shrp	X[ 7]=X[ 7],X[ 6],16
+	shrp	X[ 6]=X[ 6],X[ 5],16	}
+{ .mii;	shrp	X[ 5]=X[ 5],X[ 4],16
+	shrp	X[ 4]=X[ 4],X[ 3],16	}
+{ .mii;	shrp	X[ 3]=X[ 3],X[ 2],16
+	shrp	X[ 2]=X[ 2],X[ 1],16	}
+{ .mii;	shrp	X[ 1]=X[ 1],X[ 0],16
+	shrp	X[ 0]=X[ 0],T1,16	}
+{ .mib;	mov	r8=0
+	mux1	X[15]=X[15],\@rev		// eliminated on big-endian
+	br.many	.L_first16		};;
+.L7byte:
+{ .mmi;	$LDW	X[ 1]=[r9],4*$SZ
+	$LDW	X[ 0]=[r10],4*$SZ
+	shrp	X[15]=X[15],X[14],8	};;
+{ .mii;	$LDW	T1=[input]
+	shrp	X[14]=X[14],X[13],8
+	shrp	X[13]=X[13],X[12],8	}
+{ .mii;	shrp	X[12]=X[12],X[11],8
+	shrp	X[11]=X[11],X[10],8	};;
+{ .mii;	shrp	X[10]=X[10],X[ 9],8
+	shrp	X[ 9]=X[ 9],X[ 8],8	}
+{ .mii;	shrp	X[ 8]=X[ 8],X[ 7],8
+	shrp	X[ 7]=X[ 7],X[ 6],8	}
+{ .mii;	shrp	X[ 6]=X[ 6],X[ 5],8
+	shrp	X[ 5]=X[ 5],X[ 4],8	}
+{ .mii;	shrp	X[ 4]=X[ 4],X[ 3],8
+	shrp	X[ 3]=X[ 3],X[ 2],8	}
+{ .mii;	shrp	X[ 2]=X[ 2],X[ 1],8
+	shrp	X[ 1]=X[ 1],X[ 0],8	}
+{ .mib;	shrp	X[ 0]=X[ 0],T1,8	}
+{ .mib;	mov	r8=0
+	mux1	X[15]=X[15],\@rev	};;	// eliminated on big-endian
+
+.align	32
+.L_first16:
+{ .mmi;		$LDW	K=[Ktbl],$SZ
+		add	A=A,r8			// H+=Sigma(0) from the past
+		_rotr	r10=$t1,$Sigma1[0]  }	// ROTR(e,14)
+{ .mmi;		and	T1=F,E
+		andcm	r8=G,E
+	(p16)	mux1	X[14]=X[14],\@rev   };;	// eliminated on big-endian
+{ .mmi;		and	T2=A,B
+		and	r9=A,C
+		_rotr	r11=$t1,$Sigma1[1]  }	// ROTR(e,41)
+{ .mmi;		xor	T1=T1,r8		// T1=((e & f) ^ (~e & g))
+		and	r8=B,C		    };;
+___
+$t0="t0", $t1="t1", $code.=<<___ if ($BITS==32);
+.align	32
+.L_first16:
+{ .mmi;		add	A=A,r8			// H+=Sigma(0) from the past
+		add	r10=2-$SZ,input
+		add	r11=3-$SZ,input	};;
+{ .mmi;		ld1	r9=[r9]
+		ld1	r10=[r10]
+		dep.z	$t1=E,32,32	}
+{ .mmi;		ld1	r11=[r11]
+		$LDW	K=[Ktbl],$SZ
+		zxt4	E=E		};;
+{ .mii;		or	$t1=$t1,E
+		dep	X[15]=X[15],r9,8,8
+		mux2	$t0=A,0x44	};;	// copy lower half to upper
+{ .mmi;		and	T1=F,E
+		andcm	r8=G,E
+		dep	r11=r10,r11,8,8	};;
+{ .mmi;		and	T2=A,B
+		and	r9=A,C
+		dep	X[15]=X[15],r11,16,16	};;
+{ .mmi;	(p16)	ld1	X[15-1]=[input],$SZ	// prefetch
+		xor	T1=T1,r8		// T1=((e & f) ^ (~e & g))
+		_rotr	r10=$t1,$Sigma1[0] }	// ROTR(e,14)
+{ .mmi;		and	r8=B,C
+		_rotr	r11=$t1,$Sigma1[1] };;	// ROTR(e,18)
+___
+$code.=<<___;
+{ .mmi;		add	T1=T1,H			// T1=Ch(e,f,g)+h
+		xor	r10=r10,r11
+		_rotr	r11=$t1,$Sigma1[2]  }	// ROTR(e,41)
+{ .mmi;		xor	T2=T2,r9
+		add	K=K,X[15]	    };;
+{ .mmi;		add	T1=T1,K			// T1+=K[i]+X[i]
+		xor	T2=T2,r8		// T2=((a & b) ^ (a & c) ^ (b & c))
+		_rotr	r8=$t0,$Sigma0[0]   }	// ROTR(a,28)
+{ .mmi;		xor	r11=r11,r10		// Sigma1(e)
+		_rotr	r9=$t0,$Sigma0[1]   };;	// ROTR(a,34)
+{ .mmi;		add	T1=T1,r11		// T+=Sigma1(e)
+		xor	r8=r8,r9
+		_rotr	r9=$t0,$Sigma0[2]   };;	// ROTR(a,39)
+{ .mmi;		xor	r8=r8,r9		// Sigma0(a)
+		add	D=D,T1
+		mux2	H=X[15],0x44	    }	// mov H=X[15] in sha512
+{ .mib;	(p16)	add	r9=1-$SZ,input		// not used in sha512
+		add	X[15]=T1,T2		// H=T1+Maj(a,b,c)
+	br.ctop.sptk	.L_first16	    };;
+.L_first16_end:
+
+{ .mib;	mov	ar.lc=$rounds-17
+	brp.loop.imp	.L_rest,.L_rest_end-16		}
+{ .mib;	mov	ar.ec=1
+	br.many	.L_rest			};;
+
+.align	32
+.L_rest:
+{ .mmi;		$LDW	K=[Ktbl],$SZ
+		add	A=A,r8			// H+=Sigma0(a) from the past
+		_rotr	r8=X[15-1],$sigma0[0] }	// ROTR(s0,1)
+{ .mmi; 	add	X[15]=X[15],X[15-9]	// X[i&0xF]+=X[(i+9)&0xF]
+		$SHRU	s0=X[15-1],sgm0	    };;	// s0=X[(i+1)&0xF]>>7
+{ .mib;		and	T1=F,E
+		_rotr	r9=X[15-1],$sigma0[1] }	// ROTR(s0,8)
+{ .mib;		andcm	r10=G,E
+		$SHRU	s1=X[15-14],sgm1    };;	// s1=X[(i+14)&0xF]>>6
+// Pair of mmi; splits on Itanium 1 and prevents pipeline flush
+// upon $SHRU output usage
+{ .mmi;		xor	T1=T1,r10		// T1=((e & f) ^ (~e & g))
+		xor	r9=r8,r9
+		_rotr	r10=X[15-14],$sigma1[0] }// ROTR(s1,19)
+{ .mmi;		and	T2=A,B
+		and	r8=A,C
+		_rotr	r11=X[15-14],$sigma1[1] };;// ROTR(s1,61)
+___
+$t0="t0", $t1="t1", $code.=<<___ if ($BITS==32);
+{ .mib;		xor	s0=s0,r9		// s0=sigma0(X[(i+1)&0xF])
+		dep.z	$t1=E,32,32	    }
+{ .mib;		xor	r10=r11,r10
+		zxt4	E=E		    };;
+{ .mii;		xor	s1=s1,r10		// s1=sigma1(X[(i+14)&0xF])
+		shrp	r9=E,$t1,32+$Sigma1[0]	// ROTR(e,14)
+		mux2	$t0=A,0x44	    };;	// copy lower half to upper
+// Pair of mmi; splits on Itanium 1 and prevents pipeline flush
+// upon mux2 output usage
+{ .mmi;		xor	T2=T2,r8
+		shrp	r8=E,$t1,32+$Sigma1[1]}	// ROTR(e,18)
+{ .mmi;		and	r10=B,C
+		add	T1=T1,H			// T1=Ch(e,f,g)+h
+		or	$t1=$t1,E   	    };;
+___
+$t0="A", $t1="E", $code.=<<___ if ($BITS==64);
+{ .mib;		xor	s0=s0,r9		// s0=sigma0(X[(i+1)&0xF])
+		_rotr	r9=$t1,$Sigma1[0]   }	// ROTR(e,14)
+{ .mib;		xor	r10=r11,r10
+		xor	T2=T2,r8	    };;
+{ .mib;		xor	s1=s1,r10		// s1=sigma1(X[(i+14)&0xF])
+		_rotr	r8=$t1,$Sigma1[1]   }	// ROTR(e,18)
+{ .mib;		and	r10=B,C
+		add	T1=T1,H		    };;	// T1+=H
+___
+$code.=<<___;
+{ .mib;		xor	r9=r9,r8
+		_rotr	r8=$t1,$Sigma1[2]   }	// ROTR(e,41)
+{ .mib;		xor	T2=T2,r10		// T2=((a & b) ^ (a & c) ^ (b & c))
+		add	X[15]=X[15],s0	    };;	// X[i]+=sigma0(X[i+1])
+{ .mmi;		xor	r9=r9,r8		// Sigma1(e)
+		add	X[15]=X[15],s1		// X[i]+=sigma0(X[i+14])
+		_rotr	r8=$t0,$Sigma0[0]   };;	// ROTR(a,28)
+{ .mmi;		add	K=K,X[15]
+		add	T1=T1,r9		// T1+=Sigma1(e)
+		_rotr	r9=$t0,$Sigma0[1]   };;	// ROTR(a,34)
+{ .mmi;		add	T1=T1,K			// T1+=K[i]+X[i]
+		xor	r8=r8,r9
+		_rotr	r9=$t0,$Sigma0[2]   };;	// ROTR(a,39)
+{ .mib;		add	D=D,T1
+		mux2	H=X[15],0x44	    }	// mov H=X[15] in sha512
+{ .mib;		xor	r8=r8,r9		// Sigma0(a)
+		add	X[15]=T1,T2		// H=T1+Maj(a,b,c)
+	br.ctop.sptk	.L_rest		    };;
+.L_rest_end:
+
+{ .mmi;	add	A=A,r8			};;	// H+=Sigma0(a) from the past
+{ .mmi;	add	A_=A_,A
+	add	B_=B_,B
+	add	C_=C_,C			}
+{ .mmi;	add	D_=D_,D
+	add	E_=E_,E
+	cmp.ltu	p16,p0=1,num		};;
+{ .mmi;	add	F_=F_,F
+	add	G_=G_,G
+	add	H_=H_,H			}
+{ .mmb;	add	Ktbl=-$SZ*$rounds,Ktbl
+(p16)	add	num=-1,num
+(p16)	br.dptk.many	.L_outer	};;
+
+{ .mib;	add	r8=0*$SZ,ctx
+	add	r9=1*$SZ,ctx		}
+{ .mib;	add	r10=2*$SZ,ctx
+	add	r11=3*$SZ,ctx		};;
+{ .mmi;	$STW	[r8]=A_,4*$SZ
+	$STW	[r9]=B_,4*$SZ
+	mov	ar.lc=lcsave		}
+{ .mmi;	$STW	[r10]=C_,4*$SZ
+	$STW	[r11]=D_,4*$SZ
+	mov	pr=prsave,0x1ffff	};;
+{ .mmb;	$STW	[r8]=E_
+	$STW	[r9]=F_			}
+{ .mmb;	$STW	[r10]=G_
+	$STW	[r11]=H_
+	br.ret.sptk.many	b0	};;
+.endp	$func#
+___
+
+foreach(split($/,$code)) {
+    s/\`([^\`]*)\`/eval $1/gem;
+    s/_rotr(\s+)([^=]+)=([^,]+),([0-9]+)/shrp$1$2=$3,$3,$4/gm;
+    if ($BITS==64) {
+	s/mux2(\s+)([^=]+)=([^,]+),\S+/mov$1 $2=$3/gm;
+	s/mux1(\s+)\S+/nop.i$1 0x0/gm	if ($big_endian);
+	s/(shrp\s+X\[[^=]+)=([^,]+),([^,]+),([1-9]+)/$1=$3,$2,64-$4/gm
+    						if (!$big_endian);
+	s/ld1(\s+)X\[\S+/nop.m$1 0x0/gm;
+    }
+
+    print $_,"\n";
+}
+
+print<<___ if ($BITS==32);
+.align	64
+.type	K256#,\@object
+K256:	data4	0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5
+	data4	0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5
+	data4	0xd807aa98,0x12835b01,0x243185be,0x550c7dc3
+	data4	0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174
+	data4	0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc
+	data4	0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da
+	data4	0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7
+	data4	0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967
+	data4	0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13
+	data4	0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85
+	data4	0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3
+	data4	0xd192e819,0xd6990624,0xf40e3585,0x106aa070
+	data4	0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5
+	data4	0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3
+	data4	0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208
+	data4	0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2
+.size	K256#,$SZ*$rounds
+stringz	"SHA256 block transform for IA64, CRYPTOGAMS by "
+___
+print<<___ if ($BITS==64);
+.align	64
+.type	K512#,\@object
+K512:	data8	0x428a2f98d728ae22,0x7137449123ef65cd
+	data8	0xb5c0fbcfec4d3b2f,0xe9b5dba58189dbbc
+	data8	0x3956c25bf348b538,0x59f111f1b605d019
+	data8	0x923f82a4af194f9b,0xab1c5ed5da6d8118
+	data8	0xd807aa98a3030242,0x12835b0145706fbe
+	data8	0x243185be4ee4b28c,0x550c7dc3d5ffb4e2
+	data8	0x72be5d74f27b896f,0x80deb1fe3b1696b1
+	data8	0x9bdc06a725c71235,0xc19bf174cf692694
+	data8	0xe49b69c19ef14ad2,0xefbe4786384f25e3
+	data8	0x0fc19dc68b8cd5b5,0x240ca1cc77ac9c65
+	data8	0x2de92c6f592b0275,0x4a7484aa6ea6e483
+	data8	0x5cb0a9dcbd41fbd4,0x76f988da831153b5
+	data8	0x983e5152ee66dfab,0xa831c66d2db43210
+	data8	0xb00327c898fb213f,0xbf597fc7beef0ee4
+	data8	0xc6e00bf33da88fc2,0xd5a79147930aa725
+	data8	0x06ca6351e003826f,0x142929670a0e6e70
+	data8	0x27b70a8546d22ffc,0x2e1b21385c26c926
+	data8	0x4d2c6dfc5ac42aed,0x53380d139d95b3df
+	data8	0x650a73548baf63de,0x766a0abb3c77b2a8
+	data8	0x81c2c92e47edaee6,0x92722c851482353b
+	data8	0xa2bfe8a14cf10364,0xa81a664bbc423001
+	data8	0xc24b8b70d0f89791,0xc76c51a30654be30
+	data8	0xd192e819d6ef5218,0xd69906245565a910
+	data8	0xf40e35855771202a,0x106aa07032bbd1b8
+	data8	0x19a4c116b8d2d0c8,0x1e376c085141ab53
+	data8	0x2748774cdf8eeb99,0x34b0bcb5e19b48a8
+	data8	0x391c0cb3c5c95a63,0x4ed8aa4ae3418acb
+	data8	0x5b9cca4f7763e373,0x682e6ff3d6b2b8a3
+	data8	0x748f82ee5defb2fc,0x78a5636f43172f60
+	data8	0x84c87814a1f0ab72,0x8cc702081a6439ec
+	data8	0x90befffa23631e28,0xa4506cebde82bde9
+	data8	0xbef9a3f7b2c67915,0xc67178f2e372532b
+	data8	0xca273eceea26619c,0xd186b8c721c0c207
+	data8	0xeada7dd6cde0eb1e,0xf57d4f7fee6ed178
+	data8	0x06f067aa72176fba,0x0a637dc5a2c898a6
+	data8	0x113f9804bef90dae,0x1b710b35131c471b
+	data8	0x28db77f523047d84,0x32caab7b40c72493
+	data8	0x3c9ebe0a15c9bebc,0x431d67c49c100d4c
+	data8	0x4cc5d4becb3e42b6,0x597f299cfc657e2a
+	data8	0x5fcb6fab3ad6faec,0x6c44198c4a475817
+.size	K512#,$SZ*$rounds
+stringz	"SHA512 block transform for IA64, CRYPTOGAMS by "
+___
diff --git a/crypto/sha/asm/sha512-mips.pl b/crypto/sha/asm/sha512-mips.pl
new file mode 100644
index 000000000..5c2d23faa
--- /dev/null
+++ b/crypto/sha/asm/sha512-mips.pl
@@ -0,0 +1,519 @@
+#! /usr/bin/env perl
+# Copyright 2010-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+
+# ====================================================================
+# Written by Andy Polyakov  for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+
+# SHA2 block procedures for MIPS.
+
+# October 2010.
+#
+# SHA256 performance improvement on MIPS R5000 CPU is ~27% over gcc-
+# generated code in o32 build and ~55% in n32/64 build. SHA512 [which
+# for now can only be compiled for MIPS64 ISA] improvement is modest
+# ~17%, but it comes for free, because it's same instruction sequence.
+# Improvement coefficients are for aligned input.
+
+# September 2012.
+#
+# Add MIPS[32|64]R2 code (>25% less instructions).
+
+######################################################################
+# There is a number of MIPS ABI in use, O32 and N32/64 are most
+# widely used. Then there is a new contender: NUBI. It appears that if
+# one picks the latter, it's possible to arrange code in ABI neutral
+# manner. Therefore let's stick to NUBI register layout:
+#
+($zero,$at,$t0,$t1,$t2)=map("\$$_",(0..2,24,25));
+($a0,$a1,$a2,$a3,$a4,$a5,$a6,$a7)=map("\$$_",(4..11));
+($s0,$s1,$s2,$s3,$s4,$s5,$s6,$s7,$s8,$s9,$s10,$s11)=map("\$$_",(12..23));
+($gp,$tp,$sp,$fp,$ra)=map("\$$_",(3,28..31));
+#
+# The return value is placed in $a0. Following coding rules facilitate
+# interoperability:
+#
+# - never ever touch $tp, "thread pointer", former $gp [o32 can be
+#   excluded from the rule, because it's specified volatile];
+# - copy return value to $t0, former $v0 [or to $a0 if you're adapting
+#   old code];
+# - on O32 populate $a4-$a7 with 'lw $aN,4*N($sp)' if necessary;
+#
+# For reference here is register layout for N32/64 MIPS ABIs:
+#
+# ($zero,$at,$v0,$v1)=map("\$$_",(0..3));
+# ($a0,$a1,$a2,$a3,$a4,$a5,$a6,$a7)=map("\$$_",(4..11));
+# ($t0,$t1,$t2,$t3,$t8,$t9)=map("\$$_",(12..15,24,25));
+# ($s0,$s1,$s2,$s3,$s4,$s5,$s6,$s7)=map("\$$_",(16..23));
+# ($gp,$sp,$fp,$ra)=map("\$$_",(28..31));
+#
+$flavour = shift || "o32"; # supported flavours are o32,n32,64,nubi32,nubi64
+
+if ($flavour =~ /64|n32/i) {
+	$PTR_LA="dla";
+	$PTR_ADD="dadd";	# incidentally works even on n32
+	$PTR_SUB="dsub";	# incidentally works even on n32
+	$REG_S="sd";
+	$REG_L="ld";
+	$PTR_SLL="dsll";	# incidentally works even on n32
+	$SZREG=8;
+} else {
+	$PTR_LA="la";
+	$PTR_ADD="add";
+	$PTR_SUB="sub";
+	$REG_S="sw";
+	$REG_L="lw";
+	$PTR_SLL="sll";
+	$SZREG=4;
+}
+$pf = ($flavour =~ /nubi/i) ? $t0 : $t2;
+#
+# 
+#
+######################################################################
+
+$big_endian=(`echo MIPSEL | $ENV{CC} -E -`=~/MIPSEL/)?1:0 if ($ENV{CC});
+
+for (@ARGV) {	$output=$_ if (/\w[\w\-]*\.\w+$/);	}
+open STDOUT,">$output";
+
+if (!defined($big_endian)) { $big_endian=(unpack('L',pack('N',1))==1); }
+
+if ($output =~ /512/) {
+	$label="512";
+	$SZ=8;
+	$LD="ld";		# load from memory
+	$ST="sd";		# store to memory
+	$SLL="dsll";		# shift left logical
+	$SRL="dsrl";		# shift right logical
+	$ADDU="daddu";
+	$ROTR="drotr";
+	@Sigma0=(28,34,39);
+	@Sigma1=(14,18,41);
+	@sigma0=( 7, 1, 8);	# right shift first
+	@sigma1=( 6,19,61);	# right shift first
+	$lastK=0x817;
+	$rounds=80;
+} else {
+	$label="256";
+	$SZ=4;
+	$LD="lw";		# load from memory
+	$ST="sw";		# store to memory
+	$SLL="sll";		# shift left logical
+	$SRL="srl";		# shift right logical
+	$ADDU="addu";
+	$ROTR="rotr";
+	@Sigma0=( 2,13,22);
+	@Sigma1=( 6,11,25);
+	@sigma0=( 3, 7,18);	# right shift first
+	@sigma1=(10,17,19);	# right shift first
+	$lastK=0x8f2;
+	$rounds=64;
+}
+
+$MSB = $big_endian ? 0 : ($SZ-1);
+$LSB = ($SZ-1)&~$MSB;
+
+@V=($A,$B,$C,$D,$E,$F,$G,$H)=map("\$$_",(1,2,3,7,24,25,30,31));
+@X=map("\$$_",(8..23));
+
+$ctx=$a0;
+$inp=$a1;
+$len=$a2;	$Ktbl=$len;
+
+sub BODY_00_15 {
+my ($i,$a,$b,$c,$d,$e,$f,$g,$h)=@_;
+my ($T1,$tmp0,$tmp1,$tmp2)=(@X[4],@X[5],@X[6],@X[7]);
+
+$code.=<<___ if ($i<15);
+	${LD}l	@X[1],`($i+1)*$SZ+$MSB`($inp)
+	${LD}r	@X[1],`($i+1)*$SZ+$LSB`($inp)
+___
+$code.=<<___	if (!$big_endian && $i<16 && $SZ==4);
+#if defined(_MIPS_ARCH_MIPS32R2) || defined(_MIPS_ARCH_MIPS64R2)
+	wsbh	@X[0],@X[0]		# byte swap($i)
+	rotr	@X[0],@X[0],16
+#else
+	srl	$tmp0,@X[0],24		# byte swap($i)
+	srl	$tmp1,@X[0],8
+	andi	$tmp2,@X[0],0xFF00
+	sll	@X[0],@X[0],24
+	andi	$tmp1,0xFF00
+	sll	$tmp2,$tmp2,8
+	or	@X[0],$tmp0
+	or	$tmp1,$tmp2
+	or	@X[0],$tmp1
+#endif
+___
+$code.=<<___	if (!$big_endian && $i<16 && $SZ==8);
+#if defined(_MIPS_ARCH_MIPS64R2)
+	dsbh	@X[0],@X[0]		# byte swap($i)
+	dshd	@X[0],@X[0]
+#else
+	ori	$tmp0,$zero,0xFF
+	dsll	$tmp2,$tmp0,32
+	or	$tmp0,$tmp2		# 0x000000FF000000FF
+	and	$tmp1,@X[0],$tmp0	# byte swap($i)
+	dsrl	$tmp2,@X[0],24
+	dsll	$tmp1,24
+	and	$tmp2,$tmp0
+	dsll	$tmp0,8			# 0x0000FF000000FF00
+	or	$tmp1,$tmp2
+	and	$tmp2,@X[0],$tmp0
+	dsrl	@X[0],8
+	dsll	$tmp2,8
+	and	@X[0],$tmp0
+	or	$tmp1,$tmp2
+	or	@X[0],$tmp1
+	dsrl	$tmp1,@X[0],32
+	dsll	@X[0],32
+	or	@X[0],$tmp1
+#endif
+___
+$code.=<<___;
+#if defined(_MIPS_ARCH_MIPS32R2) || defined(_MIPS_ARCH_MIPS64R2)
+	xor	$tmp2,$f,$g			# $i
+	$ROTR	$tmp0,$e,@Sigma1[0]
+	$ADDU	$T1,$X[0],$h
+	$ROTR	$tmp1,$e,@Sigma1[1]
+	and	$tmp2,$e
+	$ROTR	$h,$e,@Sigma1[2]
+	xor	$tmp0,$tmp1
+	$ROTR	$tmp1,$a,@Sigma0[0]
+	xor	$tmp2,$g			# Ch(e,f,g)
+	xor	$tmp0,$h			# Sigma1(e)
+
+	$ROTR	$h,$a,@Sigma0[1]
+	$ADDU	$T1,$tmp2
+	$LD	$tmp2,`$i*$SZ`($Ktbl)		# K[$i]
+	xor	$h,$tmp1
+	$ROTR	$tmp1,$a,@Sigma0[2]
+	$ADDU	$T1,$tmp0
+	and	$tmp0,$b,$c
+	xor	$h,$tmp1			# Sigma0(a)
+	xor	$tmp1,$b,$c
+#else
+	$ADDU	$T1,$X[0],$h			# $i
+	$SRL	$h,$e,@Sigma1[0]
+	xor	$tmp2,$f,$g
+	$SLL	$tmp1,$e,`$SZ*8-@Sigma1[2]`
+	and	$tmp2,$e
+	$SRL	$tmp0,$e,@Sigma1[1]
+	xor	$h,$tmp1
+	$SLL	$tmp1,$e,`$SZ*8-@Sigma1[1]`
+	xor	$h,$tmp0
+	$SRL	$tmp0,$e,@Sigma1[2]
+	xor	$h,$tmp1
+	$SLL	$tmp1,$e,`$SZ*8-@Sigma1[0]`
+	xor	$h,$tmp0
+	xor	$tmp2,$g			# Ch(e,f,g)
+	xor	$tmp0,$tmp1,$h			# Sigma1(e)
+
+	$SRL	$h,$a,@Sigma0[0]
+	$ADDU	$T1,$tmp2
+	$LD	$tmp2,`$i*$SZ`($Ktbl)		# K[$i]
+	$SLL	$tmp1,$a,`$SZ*8-@Sigma0[2]`
+	$ADDU	$T1,$tmp0
+	$SRL	$tmp0,$a,@Sigma0[1]
+	xor	$h,$tmp1
+	$SLL	$tmp1,$a,`$SZ*8-@Sigma0[1]`
+	xor	$h,$tmp0
+	$SRL	$tmp0,$a,@Sigma0[2]
+	xor	$h,$tmp1
+	$SLL	$tmp1,$a,`$SZ*8-@Sigma0[0]`
+	xor	$h,$tmp0
+	and	$tmp0,$b,$c
+	xor	$h,$tmp1			# Sigma0(a)
+	xor	$tmp1,$b,$c
+#endif
+	$ST	@X[0],`($i%16)*$SZ`($sp)	# offload to ring buffer
+	$ADDU	$h,$tmp0
+	and	$tmp1,$a
+	$ADDU	$T1,$tmp2			# +=K[$i]
+	$ADDU	$h,$tmp1			# +=Maj(a,b,c)
+	$ADDU	$d,$T1
+	$ADDU	$h,$T1
+___
+$code.=<<___ if ($i>=13);
+	$LD	@X[3],`(($i+3)%16)*$SZ`($sp)	# prefetch from ring buffer
+___
+}
+
+sub BODY_16_XX {
+my $i=@_[0];
+my ($tmp0,$tmp1,$tmp2,$tmp3)=(@X[4],@X[5],@X[6],@X[7]);
+
+$code.=<<___;
+#if defined(_MIPS_ARCH_MIPS32R2) || defined(_MIPS_ARCH_MIPS64R2)
+	$SRL	$tmp2,@X[1],@sigma0[0]		# Xupdate($i)
+	$ROTR	$tmp0,@X[1],@sigma0[1]
+	$ADDU	@X[0],@X[9]			# +=X[i+9]
+	xor	$tmp2,$tmp0
+	$ROTR	$tmp0,@X[1],@sigma0[2]
+
+	$SRL	$tmp3,@X[14],@sigma1[0]
+	$ROTR	$tmp1,@X[14],@sigma1[1]
+	xor	$tmp2,$tmp0			# sigma0(X[i+1])
+	$ROTR	$tmp0,@X[14],@sigma1[2]
+	xor	$tmp3,$tmp1
+	$ADDU	@X[0],$tmp2
+#else
+	$SRL	$tmp2,@X[1],@sigma0[0]		# Xupdate($i)
+	$ADDU	@X[0],@X[9]			# +=X[i+9]
+	$SLL	$tmp1,@X[1],`$SZ*8-@sigma0[2]`
+	$SRL	$tmp0,@X[1],@sigma0[1]
+	xor	$tmp2,$tmp1
+	$SLL	$tmp1,`@sigma0[2]-@sigma0[1]`
+	xor	$tmp2,$tmp0
+	$SRL	$tmp0,@X[1],@sigma0[2]
+	xor	$tmp2,$tmp1
+
+	$SRL	$tmp3,@X[14],@sigma1[0]
+	xor	$tmp2,$tmp0			# sigma0(X[i+1])
+	$SLL	$tmp1,@X[14],`$SZ*8-@sigma1[2]`
+	$ADDU	@X[0],$tmp2
+	$SRL	$tmp0,@X[14],@sigma1[1]
+	xor	$tmp3,$tmp1
+	$SLL	$tmp1,`@sigma1[2]-@sigma1[1]`
+	xor	$tmp3,$tmp0
+	$SRL	$tmp0,@X[14],@sigma1[2]
+	xor	$tmp3,$tmp1
+#endif
+	xor	$tmp3,$tmp0			# sigma1(X[i+14])
+	$ADDU	@X[0],$tmp3
+___
+	&BODY_00_15(@_);
+}
+
+$FRAMESIZE=16*$SZ+16*$SZREG;
+$SAVED_REGS_MASK = ($flavour =~ /nubi/i) ? "0xc0fff008" : "0xc0ff0000";
+
+$code.=<<___;
+#ifdef OPENSSL_FIPSCANISTER
+# include 
+#endif
+
+#if defined(__mips_smartmips) && !defined(_MIPS_ARCH_MIPS32R2)
+#define _MIPS_ARCH_MIPS32R2
+#endif
+
+.text
+.set	noat
+#if !defined(__mips_eabi) && (!defined(__vxworks) || defined(__pic__))
+.option	pic2
+#endif
+
+.align	5
+.globl	sha${label}_block_data_order
+.ent	sha${label}_block_data_order
+sha${label}_block_data_order:
+	.frame	$sp,$FRAMESIZE,$ra
+	.mask	$SAVED_REGS_MASK,-$SZREG
+	.set	noreorder
+___
+$code.=<<___ if ($flavour =~ /o32/i);	# o32 PIC-ification
+	.cpload	$pf
+___
+$code.=<<___;
+	$PTR_SUB $sp,$FRAMESIZE
+	$REG_S	$ra,$FRAMESIZE-1*$SZREG($sp)
+	$REG_S	$fp,$FRAMESIZE-2*$SZREG($sp)
+	$REG_S	$s11,$FRAMESIZE-3*$SZREG($sp)
+	$REG_S	$s10,$FRAMESIZE-4*$SZREG($sp)
+	$REG_S	$s9,$FRAMESIZE-5*$SZREG($sp)
+	$REG_S	$s8,$FRAMESIZE-6*$SZREG($sp)
+	$REG_S	$s7,$FRAMESIZE-7*$SZREG($sp)
+	$REG_S	$s6,$FRAMESIZE-8*$SZREG($sp)
+	$REG_S	$s5,$FRAMESIZE-9*$SZREG($sp)
+	$REG_S	$s4,$FRAMESIZE-10*$SZREG($sp)
+___
+$code.=<<___ if ($flavour =~ /nubi/i);	# optimize non-nubi prologue
+	$REG_S	$s3,$FRAMESIZE-11*$SZREG($sp)
+	$REG_S	$s2,$FRAMESIZE-12*$SZREG($sp)
+	$REG_S	$s1,$FRAMESIZE-13*$SZREG($sp)
+	$REG_S	$s0,$FRAMESIZE-14*$SZREG($sp)
+	$REG_S	$gp,$FRAMESIZE-15*$SZREG($sp)
+___
+$code.=<<___;
+	$PTR_SLL @X[15],$len,`log(16*$SZ)/log(2)`
+___
+$code.=<<___ if ($flavour !~ /o32/i);	# non-o32 PIC-ification
+	.cplocal	$Ktbl
+	.cpsetup	$pf,$zero,sha${label}_block_data_order
+___
+$code.=<<___;
+	.set	reorder
+	$PTR_LA	$Ktbl,K${label}		# PIC-ified 'load address'
+
+	$LD	$A,0*$SZ($ctx)		# load context
+	$LD	$B,1*$SZ($ctx)
+	$LD	$C,2*$SZ($ctx)
+	$LD	$D,3*$SZ($ctx)
+	$LD	$E,4*$SZ($ctx)
+	$LD	$F,5*$SZ($ctx)
+	$LD	$G,6*$SZ($ctx)
+	$LD	$H,7*$SZ($ctx)
+
+	$PTR_ADD @X[15],$inp		# pointer to the end of input
+	$REG_S	@X[15],16*$SZ($sp)
+	b	.Loop
+
+.align	5
+.Loop:
+	${LD}l	@X[0],$MSB($inp)
+	${LD}r	@X[0],$LSB($inp)
+___
+for ($i=0;$i<16;$i++)
+{ &BODY_00_15($i,@V); unshift(@V,pop(@V)); push(@X,shift(@X)); }
+$code.=<<___;
+	b	.L16_xx
+.align	4
+.L16_xx:
+___
+for (;$i<32;$i++)
+{ &BODY_16_XX($i,@V); unshift(@V,pop(@V)); push(@X,shift(@X)); }
+$code.=<<___;
+	and	@X[6],0xfff
+	li	@X[7],$lastK
+	.set	noreorder
+	bne	@X[6],@X[7],.L16_xx
+	$PTR_ADD $Ktbl,16*$SZ		# Ktbl+=16
+
+	$REG_L	@X[15],16*$SZ($sp)	# restore pointer to the end of input
+	$LD	@X[0],0*$SZ($ctx)
+	$LD	@X[1],1*$SZ($ctx)
+	$LD	@X[2],2*$SZ($ctx)
+	$PTR_ADD $inp,16*$SZ
+	$LD	@X[3],3*$SZ($ctx)
+	$ADDU	$A,@X[0]
+	$LD	@X[4],4*$SZ($ctx)
+	$ADDU	$B,@X[1]
+	$LD	@X[5],5*$SZ($ctx)
+	$ADDU	$C,@X[2]
+	$LD	@X[6],6*$SZ($ctx)
+	$ADDU	$D,@X[3]
+	$LD	@X[7],7*$SZ($ctx)
+	$ADDU	$E,@X[4]
+	$ST	$A,0*$SZ($ctx)
+	$ADDU	$F,@X[5]
+	$ST	$B,1*$SZ($ctx)
+	$ADDU	$G,@X[6]
+	$ST	$C,2*$SZ($ctx)
+	$ADDU	$H,@X[7]
+	$ST	$D,3*$SZ($ctx)
+	$ST	$E,4*$SZ($ctx)
+	$ST	$F,5*$SZ($ctx)
+	$ST	$G,6*$SZ($ctx)
+	$ST	$H,7*$SZ($ctx)
+
+	bne	$inp,@X[15],.Loop
+	$PTR_SUB $Ktbl,`($rounds-16)*$SZ`	# rewind $Ktbl
+
+	$REG_L	$ra,$FRAMESIZE-1*$SZREG($sp)
+	$REG_L	$fp,$FRAMESIZE-2*$SZREG($sp)
+	$REG_L	$s11,$FRAMESIZE-3*$SZREG($sp)
+	$REG_L	$s10,$FRAMESIZE-4*$SZREG($sp)
+	$REG_L	$s9,$FRAMESIZE-5*$SZREG($sp)
+	$REG_L	$s8,$FRAMESIZE-6*$SZREG($sp)
+	$REG_L	$s7,$FRAMESIZE-7*$SZREG($sp)
+	$REG_L	$s6,$FRAMESIZE-8*$SZREG($sp)
+	$REG_L	$s5,$FRAMESIZE-9*$SZREG($sp)
+	$REG_L	$s4,$FRAMESIZE-10*$SZREG($sp)
+___
+$code.=<<___ if ($flavour =~ /nubi/i);
+	$REG_L	$s3,$FRAMESIZE-11*$SZREG($sp)
+	$REG_L	$s2,$FRAMESIZE-12*$SZREG($sp)
+	$REG_L	$s1,$FRAMESIZE-13*$SZREG($sp)
+	$REG_L	$s0,$FRAMESIZE-14*$SZREG($sp)
+	$REG_L	$gp,$FRAMESIZE-15*$SZREG($sp)
+___
+$code.=<<___;
+	jr	$ra
+	$PTR_ADD $sp,$FRAMESIZE
+.end	sha${label}_block_data_order
+
+.rdata
+.align	5
+K${label}:
+___
+if ($SZ==4) {
+$code.=<<___;
+	.word	0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5
+	.word	0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5
+	.word	0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3
+	.word	0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174
+	.word	0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc
+	.word	0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da
+	.word	0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7
+	.word	0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967
+	.word	0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13
+	.word	0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85
+	.word	0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3
+	.word	0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070
+	.word	0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5
+	.word	0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3
+	.word	0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208
+	.word	0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
+___
+} else {
+$code.=<<___;
+	.dword	0x428a2f98d728ae22, 0x7137449123ef65cd
+	.dword	0xb5c0fbcfec4d3b2f, 0xe9b5dba58189dbbc
+	.dword	0x3956c25bf348b538, 0x59f111f1b605d019
+	.dword	0x923f82a4af194f9b, 0xab1c5ed5da6d8118
+	.dword	0xd807aa98a3030242, 0x12835b0145706fbe
+	.dword	0x243185be4ee4b28c, 0x550c7dc3d5ffb4e2
+	.dword	0x72be5d74f27b896f, 0x80deb1fe3b1696b1
+	.dword	0x9bdc06a725c71235, 0xc19bf174cf692694
+	.dword	0xe49b69c19ef14ad2, 0xefbe4786384f25e3
+	.dword	0x0fc19dc68b8cd5b5, 0x240ca1cc77ac9c65
+	.dword	0x2de92c6f592b0275, 0x4a7484aa6ea6e483
+	.dword	0x5cb0a9dcbd41fbd4, 0x76f988da831153b5
+	.dword	0x983e5152ee66dfab, 0xa831c66d2db43210
+	.dword	0xb00327c898fb213f, 0xbf597fc7beef0ee4
+	.dword	0xc6e00bf33da88fc2, 0xd5a79147930aa725
+	.dword	0x06ca6351e003826f, 0x142929670a0e6e70
+	.dword	0x27b70a8546d22ffc, 0x2e1b21385c26c926
+	.dword	0x4d2c6dfc5ac42aed, 0x53380d139d95b3df
+	.dword	0x650a73548baf63de, 0x766a0abb3c77b2a8
+	.dword	0x81c2c92e47edaee6, 0x92722c851482353b
+	.dword	0xa2bfe8a14cf10364, 0xa81a664bbc423001
+	.dword	0xc24b8b70d0f89791, 0xc76c51a30654be30
+	.dword	0xd192e819d6ef5218, 0xd69906245565a910
+	.dword	0xf40e35855771202a, 0x106aa07032bbd1b8
+	.dword	0x19a4c116b8d2d0c8, 0x1e376c085141ab53
+	.dword	0x2748774cdf8eeb99, 0x34b0bcb5e19b48a8
+	.dword	0x391c0cb3c5c95a63, 0x4ed8aa4ae3418acb
+	.dword	0x5b9cca4f7763e373, 0x682e6ff3d6b2b8a3
+	.dword	0x748f82ee5defb2fc, 0x78a5636f43172f60
+	.dword	0x84c87814a1f0ab72, 0x8cc702081a6439ec
+	.dword	0x90befffa23631e28, 0xa4506cebde82bde9
+	.dword	0xbef9a3f7b2c67915, 0xc67178f2e372532b
+	.dword	0xca273eceea26619c, 0xd186b8c721c0c207
+	.dword	0xeada7dd6cde0eb1e, 0xf57d4f7fee6ed178
+	.dword	0x06f067aa72176fba, 0x0a637dc5a2c898a6
+	.dword	0x113f9804bef90dae, 0x1b710b35131c471b
+	.dword	0x28db77f523047d84, 0x32caab7b40c72493
+	.dword	0x3c9ebe0a15c9bebc, 0x431d67c49c100d4c
+	.dword	0x4cc5d4becb3e42b6, 0x597f299cfc657e2a
+	.dword	0x5fcb6fab3ad6faec, 0x6c44198c4a475817
+___
+}
+$code.=<<___;
+.asciiz	"SHA${label} for MIPS, CRYPTOGAMS by "
+.align	5
+
+___
+
+$code =~ s/\`([^\`]*)\`/eval $1/gem;
+print $code;
+close STDOUT;
diff --git a/crypto/sha/asm/sha512-parisc.pl b/crypto/sha/asm/sha512-parisc.pl
new file mode 100755
index 000000000..fcb615790
--- /dev/null
+++ b/crypto/sha/asm/sha512-parisc.pl
@@ -0,0 +1,800 @@
+#! /usr/bin/env perl
+# Copyright 2009-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+
+# ====================================================================
+# Written by Andy Polyakov  for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+
+# SHA256/512 block procedure for PA-RISC.
+
+# June 2009.
+#
+# SHA256 performance is >75% better than gcc 3.2 generated code on
+# PA-7100LC. Compared to code generated by vendor compiler this
+# implementation is almost 70% faster in 64-bit build, but delivers
+# virtually same performance in 32-bit build on PA-8600.
+#
+# SHA512 performance is >2.9x better than gcc 3.2 generated code on
+# PA-7100LC, PA-RISC 1.1 processor. Then implementation detects if the
+# code is executed on PA-RISC 2.0 processor and switches to 64-bit
+# code path delivering adequate performance even in "blended" 32-bit
+# build. Though 64-bit code is not any faster than code generated by
+# vendor compiler on PA-8600...
+#
+# Special thanks to polarhome.com for providing HP-UX account.
+
+$flavour = shift;
+$output = shift;
+open STDOUT,">$output";
+
+if ($flavour =~ /64/) {
+	$LEVEL		="2.0W";
+	$SIZE_T		=8;
+	$FRAME_MARKER	=80;
+	$SAVED_RP	=16;
+	$PUSH		="std";
+	$PUSHMA		="std,ma";
+	$POP		="ldd";
+	$POPMB		="ldd,mb";
+} else {
+	$LEVEL		="1.0";
+	$SIZE_T		=4;
+	$FRAME_MARKER	=48;
+	$SAVED_RP	=20;
+	$PUSH		="stw";
+	$PUSHMA		="stwm";
+	$POP		="ldw";
+	$POPMB		="ldwm";
+}
+
+if ($output =~ /512/) {
+	$func="sha512_block_data_order";
+	$SZ=8;
+	@Sigma0=(28,34,39);
+	@Sigma1=(14,18,41);
+	@sigma0=(1,  8, 7);
+	@sigma1=(19,61, 6);
+	$rounds=80;
+	$LAST10BITS=0x017;
+	$LD="ldd";
+	$LDM="ldd,ma";
+	$ST="std";
+} else {
+	$func="sha256_block_data_order";
+	$SZ=4;
+	@Sigma0=( 2,13,22);
+	@Sigma1=( 6,11,25);
+	@sigma0=( 7,18, 3);
+	@sigma1=(17,19,10);
+	$rounds=64;
+	$LAST10BITS=0x0f2;
+	$LD="ldw";
+	$LDM="ldwm";
+	$ST="stw";
+}
+
+$FRAME=16*$SIZE_T+$FRAME_MARKER;# 16 saved regs + frame marker
+				#                 [+ argument transfer]
+$XOFF=16*$SZ+32;		# local variables
+$FRAME+=$XOFF;
+$XOFF+=$FRAME_MARKER;		# distance between %sp and local variables
+
+$ctx="%r26";	# zapped by $a0
+$inp="%r25";	# zapped by $a1
+$num="%r24";	# zapped by $t0
+
+$a0 ="%r26";
+$a1 ="%r25";
+$t0 ="%r24";
+$t1 ="%r29";
+$Tbl="%r31";
+
+@V=($A,$B,$C,$D,$E,$F,$G,$H)=("%r17","%r18","%r19","%r20","%r21","%r22","%r23","%r28");
+
+@X=("%r1", "%r2", "%r3", "%r4", "%r5", "%r6", "%r7", "%r8",
+    "%r9", "%r10","%r11","%r12","%r13","%r14","%r15","%r16",$inp);
+
+sub ROUND_00_15 {
+my ($i,$a,$b,$c,$d,$e,$f,$g,$h)=@_;
+$code.=<<___;
+	_ror	$e,$Sigma1[0],$a0
+	and	$f,$e,$t0
+	_ror	$e,$Sigma1[1],$a1
+	addl	$t1,$h,$h
+	andcm	$g,$e,$t1
+	xor	$a1,$a0,$a0
+	_ror	$a1,`$Sigma1[2]-$Sigma1[1]`,$a1
+	or	$t0,$t1,$t1		; Ch(e,f,g)
+	addl	@X[$i%16],$h,$h
+	xor	$a0,$a1,$a1		; Sigma1(e)
+	addl	$t1,$h,$h
+	_ror	$a,$Sigma0[0],$a0
+	addl	$a1,$h,$h
+
+	_ror	$a,$Sigma0[1],$a1
+	and	$a,$b,$t0
+	and	$a,$c,$t1
+	xor	$a1,$a0,$a0
+	_ror	$a1,`$Sigma0[2]-$Sigma0[1]`,$a1
+	xor	$t1,$t0,$t0
+	and	$b,$c,$t1
+	xor	$a0,$a1,$a1		; Sigma0(a)
+	addl	$h,$d,$d
+	xor	$t1,$t0,$t0		; Maj(a,b,c)
+	`"$LDM	$SZ($Tbl),$t1" if ($i<15)`
+	addl	$a1,$h,$h
+	addl	$t0,$h,$h
+
+___
+}
+
+sub ROUND_16_xx {
+my ($i,$a,$b,$c,$d,$e,$f,$g,$h)=@_;
+$i-=16;
+$code.=<<___;
+	_ror	@X[($i+1)%16],$sigma0[0],$a0
+	_ror	@X[($i+1)%16],$sigma0[1],$a1
+	addl	@X[($i+9)%16],@X[$i],@X[$i]
+	_ror	@X[($i+14)%16],$sigma1[0],$t0
+	_ror	@X[($i+14)%16],$sigma1[1],$t1
+	xor	$a1,$a0,$a0
+	_shr	@X[($i+1)%16],$sigma0[2],$a1
+	xor	$t1,$t0,$t0
+	_shr	@X[($i+14)%16],$sigma1[2],$t1
+	xor	$a1,$a0,$a0		; sigma0(X[(i+1)&0x0f])
+	xor	$t1,$t0,$t0		; sigma1(X[(i+14)&0x0f])
+	$LDM	$SZ($Tbl),$t1
+	addl	$a0,@X[$i],@X[$i]
+	addl	$t0,@X[$i],@X[$i]
+___
+$code.=<<___ if ($i==15);
+	extru	$t1,31,10,$a1
+	comiclr,<> $LAST10BITS,$a1,%r0
+	ldo	1($Tbl),$Tbl		; signal end of $Tbl
+___
+&ROUND_00_15($i+16,$a,$b,$c,$d,$e,$f,$g,$h);
+}
+
+$code=<<___;
+	.LEVEL	$LEVEL
+	.SPACE	\$TEXT\$
+	.SUBSPA	\$CODE\$,QUAD=0,ALIGN=8,ACCESS=0x2C,CODE_ONLY
+
+	.ALIGN	64
+L\$table
+___
+$code.=<<___ if ($SZ==8);
+	.WORD	0x428a2f98,0xd728ae22,0x71374491,0x23ef65cd
+	.WORD	0xb5c0fbcf,0xec4d3b2f,0xe9b5dba5,0x8189dbbc
+	.WORD	0x3956c25b,0xf348b538,0x59f111f1,0xb605d019
+	.WORD	0x923f82a4,0xaf194f9b,0xab1c5ed5,0xda6d8118
+	.WORD	0xd807aa98,0xa3030242,0x12835b01,0x45706fbe
+	.WORD	0x243185be,0x4ee4b28c,0x550c7dc3,0xd5ffb4e2
+	.WORD	0x72be5d74,0xf27b896f,0x80deb1fe,0x3b1696b1
+	.WORD	0x9bdc06a7,0x25c71235,0xc19bf174,0xcf692694
+	.WORD	0xe49b69c1,0x9ef14ad2,0xefbe4786,0x384f25e3
+	.WORD	0x0fc19dc6,0x8b8cd5b5,0x240ca1cc,0x77ac9c65
+	.WORD	0x2de92c6f,0x592b0275,0x4a7484aa,0x6ea6e483
+	.WORD	0x5cb0a9dc,0xbd41fbd4,0x76f988da,0x831153b5
+	.WORD	0x983e5152,0xee66dfab,0xa831c66d,0x2db43210
+	.WORD	0xb00327c8,0x98fb213f,0xbf597fc7,0xbeef0ee4
+	.WORD	0xc6e00bf3,0x3da88fc2,0xd5a79147,0x930aa725
+	.WORD	0x06ca6351,0xe003826f,0x14292967,0x0a0e6e70
+	.WORD	0x27b70a85,0x46d22ffc,0x2e1b2138,0x5c26c926
+	.WORD	0x4d2c6dfc,0x5ac42aed,0x53380d13,0x9d95b3df
+	.WORD	0x650a7354,0x8baf63de,0x766a0abb,0x3c77b2a8
+	.WORD	0x81c2c92e,0x47edaee6,0x92722c85,0x1482353b
+	.WORD	0xa2bfe8a1,0x4cf10364,0xa81a664b,0xbc423001
+	.WORD	0xc24b8b70,0xd0f89791,0xc76c51a3,0x0654be30
+	.WORD	0xd192e819,0xd6ef5218,0xd6990624,0x5565a910
+	.WORD	0xf40e3585,0x5771202a,0x106aa070,0x32bbd1b8
+	.WORD	0x19a4c116,0xb8d2d0c8,0x1e376c08,0x5141ab53
+	.WORD	0x2748774c,0xdf8eeb99,0x34b0bcb5,0xe19b48a8
+	.WORD	0x391c0cb3,0xc5c95a63,0x4ed8aa4a,0xe3418acb
+	.WORD	0x5b9cca4f,0x7763e373,0x682e6ff3,0xd6b2b8a3
+	.WORD	0x748f82ee,0x5defb2fc,0x78a5636f,0x43172f60
+	.WORD	0x84c87814,0xa1f0ab72,0x8cc70208,0x1a6439ec
+	.WORD	0x90befffa,0x23631e28,0xa4506ceb,0xde82bde9
+	.WORD	0xbef9a3f7,0xb2c67915,0xc67178f2,0xe372532b
+	.WORD	0xca273ece,0xea26619c,0xd186b8c7,0x21c0c207
+	.WORD	0xeada7dd6,0xcde0eb1e,0xf57d4f7f,0xee6ed178
+	.WORD	0x06f067aa,0x72176fba,0x0a637dc5,0xa2c898a6
+	.WORD	0x113f9804,0xbef90dae,0x1b710b35,0x131c471b
+	.WORD	0x28db77f5,0x23047d84,0x32caab7b,0x40c72493
+	.WORD	0x3c9ebe0a,0x15c9bebc,0x431d67c4,0x9c100d4c
+	.WORD	0x4cc5d4be,0xcb3e42b6,0x597f299c,0xfc657e2a
+	.WORD	0x5fcb6fab,0x3ad6faec,0x6c44198c,0x4a475817
+___
+$code.=<<___ if ($SZ==4);
+	.WORD	0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5
+	.WORD	0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5
+	.WORD	0xd807aa98,0x12835b01,0x243185be,0x550c7dc3
+	.WORD	0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174
+	.WORD	0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc
+	.WORD	0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da
+	.WORD	0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7
+	.WORD	0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967
+	.WORD	0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13
+	.WORD	0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85
+	.WORD	0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3
+	.WORD	0xd192e819,0xd6990624,0xf40e3585,0x106aa070
+	.WORD	0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5
+	.WORD	0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3
+	.WORD	0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208
+	.WORD	0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2
+___
+$code.=<<___;
+
+	.EXPORT	$func,ENTRY,ARGW0=GR,ARGW1=GR,ARGW2=GR
+	.ALIGN	64
+$func
+	.PROC
+	.CALLINFO	FRAME=`$FRAME-16*$SIZE_T`,NO_CALLS,SAVE_RP,ENTRY_GR=18
+	.ENTRY
+	$PUSH	%r2,-$SAVED_RP(%sp)	; standard prologue
+	$PUSHMA	%r3,$FRAME(%sp)
+	$PUSH	%r4,`-$FRAME+1*$SIZE_T`(%sp)
+	$PUSH	%r5,`-$FRAME+2*$SIZE_T`(%sp)
+	$PUSH	%r6,`-$FRAME+3*$SIZE_T`(%sp)
+	$PUSH	%r7,`-$FRAME+4*$SIZE_T`(%sp)
+	$PUSH	%r8,`-$FRAME+5*$SIZE_T`(%sp)
+	$PUSH	%r9,`-$FRAME+6*$SIZE_T`(%sp)
+	$PUSH	%r10,`-$FRAME+7*$SIZE_T`(%sp)
+	$PUSH	%r11,`-$FRAME+8*$SIZE_T`(%sp)
+	$PUSH	%r12,`-$FRAME+9*$SIZE_T`(%sp)
+	$PUSH	%r13,`-$FRAME+10*$SIZE_T`(%sp)
+	$PUSH	%r14,`-$FRAME+11*$SIZE_T`(%sp)
+	$PUSH	%r15,`-$FRAME+12*$SIZE_T`(%sp)
+	$PUSH	%r16,`-$FRAME+13*$SIZE_T`(%sp)
+	$PUSH	%r17,`-$FRAME+14*$SIZE_T`(%sp)
+	$PUSH	%r18,`-$FRAME+15*$SIZE_T`(%sp)
+
+	_shl	$num,`log(16*$SZ)/log(2)`,$num
+	addl	$inp,$num,$num		; $num to point at the end of $inp
+
+	$PUSH	$num,`-$FRAME_MARKER-4*$SIZE_T`(%sp)	; save arguments
+	$PUSH	$inp,`-$FRAME_MARKER-3*$SIZE_T`(%sp)
+	$PUSH	$ctx,`-$FRAME_MARKER-2*$SIZE_T`(%sp)
+
+	blr	%r0,$Tbl
+	ldi	3,$t1
+L\$pic
+	andcm	$Tbl,$t1,$Tbl		; wipe privilege level
+	ldo	L\$table-L\$pic($Tbl),$Tbl
+___
+$code.=<<___ if ($SZ==8 && $SIZE_T==4);
+	ldi	31,$t1
+	mtctl	$t1,%cr11
+	extrd,u,*= $t1,%sar,1,$t1	; executes on PA-RISC 1.0
+	b	L\$parisc1
+	nop
+___
+$code.=<<___;
+	$LD	`0*$SZ`($ctx),$A	; load context
+	$LD	`1*$SZ`($ctx),$B
+	$LD	`2*$SZ`($ctx),$C
+	$LD	`3*$SZ`($ctx),$D
+	$LD	`4*$SZ`($ctx),$E
+	$LD	`5*$SZ`($ctx),$F
+	$LD	`6*$SZ`($ctx),$G
+	$LD	`7*$SZ`($ctx),$H
+
+	extru	$inp,31,`log($SZ)/log(2)`,$t0
+	sh3addl	$t0,%r0,$t0
+	subi	`8*$SZ`,$t0,$t0
+	mtctl	$t0,%cr11		; load %sar with align factor
+
+L\$oop
+	ldi	`$SZ-1`,$t0
+	$LDM	$SZ($Tbl),$t1
+	andcm	$inp,$t0,$t0		; align $inp
+___
+	for ($i=0;$i<15;$i++) {		# load input block
+	$code.="\t$LD	`$SZ*$i`($t0),@X[$i]\n";		}
+$code.=<<___;
+	cmpb,*=	$inp,$t0,L\$aligned
+	$LD	`$SZ*15`($t0),@X[15]
+	$LD	`$SZ*16`($t0),@X[16]
+___
+	for ($i=0;$i<16;$i++) {		# align data
+	$code.="\t_align	@X[$i],@X[$i+1],@X[$i]\n";	}
+$code.=<<___;
+L\$aligned
+	nop	; otherwise /usr/ccs/bin/as is confused by below .WORD
+___
+
+for($i=0;$i<16;$i++)	{ &ROUND_00_15($i,@V); unshift(@V,pop(@V)); }
+$code.=<<___;
+L\$rounds
+	nop	; otherwise /usr/ccs/bin/as is confused by below .WORD
+___
+for(;$i<32;$i++)	{ &ROUND_16_xx($i,@V); unshift(@V,pop(@V)); }
+$code.=<<___;
+	bb,>=	$Tbl,31,L\$rounds	; end of $Tbl signalled?
+	nop
+
+	$POP	`-$FRAME_MARKER-2*$SIZE_T`(%sp),$ctx	; restore arguments
+	$POP	`-$FRAME_MARKER-3*$SIZE_T`(%sp),$inp
+	$POP	`-$FRAME_MARKER-4*$SIZE_T`(%sp),$num
+	ldo	`-$rounds*$SZ-1`($Tbl),$Tbl		; rewind $Tbl
+
+	$LD	`0*$SZ`($ctx),@X[0]	; load context
+	$LD	`1*$SZ`($ctx),@X[1]
+	$LD	`2*$SZ`($ctx),@X[2]
+	$LD	`3*$SZ`($ctx),@X[3]
+	$LD	`4*$SZ`($ctx),@X[4]
+	$LD	`5*$SZ`($ctx),@X[5]
+	addl	@X[0],$A,$A
+	$LD	`6*$SZ`($ctx),@X[6]
+	addl	@X[1],$B,$B
+	$LD	`7*$SZ`($ctx),@X[7]
+	ldo	`16*$SZ`($inp),$inp	; advance $inp
+
+	$ST	$A,`0*$SZ`($ctx)	; save context
+	addl	@X[2],$C,$C
+	$ST	$B,`1*$SZ`($ctx)
+	addl	@X[3],$D,$D
+	$ST	$C,`2*$SZ`($ctx)
+	addl	@X[4],$E,$E
+	$ST	$D,`3*$SZ`($ctx)
+	addl	@X[5],$F,$F
+	$ST	$E,`4*$SZ`($ctx)
+	addl	@X[6],$G,$G
+	$ST	$F,`5*$SZ`($ctx)
+	addl	@X[7],$H,$H
+	$ST	$G,`6*$SZ`($ctx)
+	$ST	$H,`7*$SZ`($ctx)
+
+	cmpb,*<>,n $inp,$num,L\$oop
+	$PUSH	$inp,`-$FRAME_MARKER-3*$SIZE_T`(%sp)	; save $inp
+___
+if ($SZ==8 && $SIZE_T==4)	# SHA512 for 32-bit PA-RISC 1.0
+{{
+$code.=<<___;
+	b	L\$done
+	nop
+
+	.ALIGN	64
+L\$parisc1
+___
+
+@V=(  $Ahi,  $Alo,  $Bhi,  $Blo,  $Chi,  $Clo,  $Dhi,  $Dlo,
+      $Ehi,  $Elo,  $Fhi,  $Flo,  $Ghi,  $Glo,  $Hhi,  $Hlo) = 
+   ( "%r1", "%r2", "%r3", "%r4", "%r5", "%r6", "%r7", "%r8",
+     "%r9","%r10","%r11","%r12","%r13","%r14","%r15","%r16");
+$a0 ="%r17";
+$a1 ="%r18";
+$a2 ="%r19";
+$a3 ="%r20";
+$t0 ="%r21";
+$t1 ="%r22";
+$t2 ="%r28";
+$t3 ="%r29";
+$Tbl="%r31";
+
+@X=("%r23","%r24","%r25","%r26");	# zaps $num,$inp,$ctx
+
+sub ROUND_00_15_pa1 {
+my ($i,$ahi,$alo,$bhi,$blo,$chi,$clo,$dhi,$dlo,
+       $ehi,$elo,$fhi,$flo,$ghi,$glo,$hhi,$hlo,$flag)=@_;
+my ($Xhi,$Xlo,$Xnhi,$Xnlo) = @X;
+
+$code.=<<___ if (!$flag);
+	ldw	`-$XOFF+8*(($i+1)%16)`(%sp),$Xnhi
+	ldw	`-$XOFF+8*(($i+1)%16)+4`(%sp),$Xnlo	; load X[i+1]
+___
+$code.=<<___;
+	shd	$ehi,$elo,$Sigma1[0],$t0
+	 add	$Xlo,$hlo,$hlo
+	shd	$elo,$ehi,$Sigma1[0],$t1
+	 addc	$Xhi,$hhi,$hhi		; h += X[i]
+	shd	$ehi,$elo,$Sigma1[1],$t2
+	 ldwm	8($Tbl),$Xhi
+	shd	$elo,$ehi,$Sigma1[1],$t3
+	 ldw	-4($Tbl),$Xlo		; load K[i]
+	xor	$t2,$t0,$t0
+	xor	$t3,$t1,$t1
+	 and	$flo,$elo,$a0
+	 and	$fhi,$ehi,$a1
+	shd	$ehi,$elo,$Sigma1[2],$t2
+	 andcm	$glo,$elo,$a2
+	shd	$elo,$ehi,$Sigma1[2],$t3
+	 andcm	$ghi,$ehi,$a3
+	xor	$t2,$t0,$t0
+	xor	$t3,$t1,$t1		; Sigma1(e)
+	add	$Xlo,$hlo,$hlo
+	 xor	$a2,$a0,$a0
+	addc	$Xhi,$hhi,$hhi		; h += K[i]
+	 xor	$a3,$a1,$a1		; Ch(e,f,g)
+
+	 add	$t0,$hlo,$hlo
+	shd	$ahi,$alo,$Sigma0[0],$t0
+	 addc	$t1,$hhi,$hhi		; h += Sigma1(e)
+	shd	$alo,$ahi,$Sigma0[0],$t1	
+	 add	$a0,$hlo,$hlo
+	shd	$ahi,$alo,$Sigma0[1],$t2
+	 addc	$a1,$hhi,$hhi		; h += Ch(e,f,g)
+	shd	$alo,$ahi,$Sigma0[1],$t3
+
+	xor	$t2,$t0,$t0
+	xor	$t3,$t1,$t1
+	shd	$ahi,$alo,$Sigma0[2],$t2
+	and	$alo,$blo,$a0
+	shd	$alo,$ahi,$Sigma0[2],$t3
+	and	$ahi,$bhi,$a1
+	xor	$t2,$t0,$t0
+	xor	$t3,$t1,$t1		; Sigma0(a)
+
+	and	$alo,$clo,$a2
+	and	$ahi,$chi,$a3
+	xor	$a2,$a0,$a0
+	 add	$hlo,$dlo,$dlo
+	xor	$a3,$a1,$a1
+	 addc	$hhi,$dhi,$dhi		; d += h
+	and	$blo,$clo,$a2
+	 add	$t0,$hlo,$hlo
+	and	$bhi,$chi,$a3
+	 addc	$t1,$hhi,$hhi		; h += Sigma0(a)
+	xor	$a2,$a0,$a0
+	 add	$a0,$hlo,$hlo
+	xor	$a3,$a1,$a1		; Maj(a,b,c)
+	 addc	$a1,$hhi,$hhi		; h += Maj(a,b,c)
+
+___
+$code.=<<___ if ($i==15 && $flag);
+	extru	$Xlo,31,10,$Xlo
+	comiclr,= $LAST10BITS,$Xlo,%r0
+	b	L\$rounds_pa1
+	nop
+___
+push(@X,shift(@X)); push(@X,shift(@X));
+}
+
+sub ROUND_16_xx_pa1 {
+my ($Xhi,$Xlo,$Xnhi,$Xnlo) = @X;
+my ($i)=shift;
+$i-=16;
+$code.=<<___;
+	ldw	`-$XOFF+8*(($i+1)%16)`(%sp),$Xnhi
+	ldw	`-$XOFF+8*(($i+1)%16)+4`(%sp),$Xnlo	; load X[i+1]
+	ldw	`-$XOFF+8*(($i+9)%16)`(%sp),$a1
+	ldw	`-$XOFF+8*(($i+9)%16)+4`(%sp),$a0	; load X[i+9]
+	ldw	`-$XOFF+8*(($i+14)%16)`(%sp),$a3
+	ldw	`-$XOFF+8*(($i+14)%16)+4`(%sp),$a2	; load X[i+14]
+	shd	$Xnhi,$Xnlo,$sigma0[0],$t0
+	shd	$Xnlo,$Xnhi,$sigma0[0],$t1
+	 add	$a0,$Xlo,$Xlo
+	shd	$Xnhi,$Xnlo,$sigma0[1],$t2
+	 addc	$a1,$Xhi,$Xhi
+	shd	$Xnlo,$Xnhi,$sigma0[1],$t3
+	xor	$t2,$t0,$t0
+	shd	$Xnhi,$Xnlo,$sigma0[2],$t2
+	xor	$t3,$t1,$t1
+	extru	$Xnhi,`31-$sigma0[2]`,`32-$sigma0[2]`,$t3
+	xor	$t2,$t0,$t0
+	 shd	$a3,$a2,$sigma1[0],$a0
+	xor	$t3,$t1,$t1		; sigma0(X[i+1)&0x0f])
+	 shd	$a2,$a3,$sigma1[0],$a1
+	add	$t0,$Xlo,$Xlo
+	 shd	$a3,$a2,$sigma1[1],$t2
+	addc	$t1,$Xhi,$Xhi
+	 shd	$a2,$a3,$sigma1[1],$t3
+	xor	$t2,$a0,$a0
+	shd	$a3,$a2,$sigma1[2],$t2
+	xor	$t3,$a1,$a1
+	extru	$a3,`31-$sigma1[2]`,`32-$sigma1[2]`,$t3
+	xor	$t2,$a0,$a0
+	xor	$t3,$a1,$a1		; sigma0(X[i+14)&0x0f])
+	add	$a0,$Xlo,$Xlo
+	addc	$a1,$Xhi,$Xhi
+
+	stw	$Xhi,`-$XOFF+8*($i%16)`(%sp)
+	stw	$Xlo,`-$XOFF+8*($i%16)+4`(%sp)
+___
+&ROUND_00_15_pa1($i,@_,1);
+}
+$code.=<<___;
+	ldw	`0*4`($ctx),$Ahi		; load context
+	ldw	`1*4`($ctx),$Alo
+	ldw	`2*4`($ctx),$Bhi
+	ldw	`3*4`($ctx),$Blo
+	ldw	`4*4`($ctx),$Chi
+	ldw	`5*4`($ctx),$Clo
+	ldw	`6*4`($ctx),$Dhi
+	ldw	`7*4`($ctx),$Dlo
+	ldw	`8*4`($ctx),$Ehi
+	ldw	`9*4`($ctx),$Elo
+	ldw	`10*4`($ctx),$Fhi
+	ldw	`11*4`($ctx),$Flo
+	ldw	`12*4`($ctx),$Ghi
+	ldw	`13*4`($ctx),$Glo
+	ldw	`14*4`($ctx),$Hhi
+	ldw	`15*4`($ctx),$Hlo
+
+	extru	$inp,31,2,$t0
+	sh3addl	$t0,%r0,$t0
+	subi	32,$t0,$t0
+	mtctl	$t0,%cr11		; load %sar with align factor
+
+L\$oop_pa1
+	extru	$inp,31,2,$a3
+	comib,=	0,$a3,L\$aligned_pa1
+	sub	$inp,$a3,$inp
+
+	ldw	`0*4`($inp),$X[0]
+	ldw	`1*4`($inp),$X[1]
+	ldw	`2*4`($inp),$t2
+	ldw	`3*4`($inp),$t3
+	ldw	`4*4`($inp),$a0
+	ldw	`5*4`($inp),$a1
+	ldw	`6*4`($inp),$a2
+	ldw	`7*4`($inp),$a3
+	vshd	$X[0],$X[1],$X[0]
+	vshd	$X[1],$t2,$X[1]
+	stw	$X[0],`-$XOFF+0*4`(%sp)
+	ldw	`8*4`($inp),$t0
+	vshd	$t2,$t3,$t2
+	stw	$X[1],`-$XOFF+1*4`(%sp)
+	ldw	`9*4`($inp),$t1
+	vshd	$t3,$a0,$t3
+___
+{
+my @t=($t2,$t3,$a0,$a1,$a2,$a3,$t0,$t1);
+for ($i=2;$i<=(128/4-8);$i++) {
+$code.=<<___;
+	stw	$t[0],`-$XOFF+$i*4`(%sp)
+	ldw	`(8+$i)*4`($inp),$t[0]
+	vshd	$t[1],$t[2],$t[1]
+___
+push(@t,shift(@t));
+}
+for (;$i<(128/4-1);$i++) {
+$code.=<<___;
+	stw	$t[0],`-$XOFF+$i*4`(%sp)
+	vshd	$t[1],$t[2],$t[1]
+___
+push(@t,shift(@t));
+}
+$code.=<<___;
+	b	L\$collected_pa1
+	stw	$t[0],`-$XOFF+$i*4`(%sp)
+
+___
+}
+$code.=<<___;
+L\$aligned_pa1
+	ldw	`0*4`($inp),$X[0]
+	ldw	`1*4`($inp),$X[1]
+	ldw	`2*4`($inp),$t2
+	ldw	`3*4`($inp),$t3
+	ldw	`4*4`($inp),$a0
+	ldw	`5*4`($inp),$a1
+	ldw	`6*4`($inp),$a2
+	ldw	`7*4`($inp),$a3
+	stw	$X[0],`-$XOFF+0*4`(%sp)
+	ldw	`8*4`($inp),$t0
+	stw	$X[1],`-$XOFF+1*4`(%sp)
+	ldw	`9*4`($inp),$t1
+___
+{
+my @t=($t2,$t3,$a0,$a1,$a2,$a3,$t0,$t1);
+for ($i=2;$i<(128/4-8);$i++) {
+$code.=<<___;
+	stw	$t[0],`-$XOFF+$i*4`(%sp)
+	ldw	`(8+$i)*4`($inp),$t[0]
+___
+push(@t,shift(@t));
+}
+for (;$i<128/4;$i++) {
+$code.=<<___;
+	stw	$t[0],`-$XOFF+$i*4`(%sp)
+___
+push(@t,shift(@t));
+}
+$code.="L\$collected_pa1\n";
+}
+
+for($i=0;$i<16;$i++)	{ &ROUND_00_15_pa1($i,@V); unshift(@V,pop(@V)); unshift(@V,pop(@V)); }
+$code.="L\$rounds_pa1\n";
+for(;$i<32;$i++)	{ &ROUND_16_xx_pa1($i,@V); unshift(@V,pop(@V)); unshift(@V,pop(@V)); }
+
+$code.=<<___;
+	$POP	`-$FRAME_MARKER-2*$SIZE_T`(%sp),$ctx	; restore arguments
+	$POP	`-$FRAME_MARKER-3*$SIZE_T`(%sp),$inp
+	$POP	`-$FRAME_MARKER-4*$SIZE_T`(%sp),$num
+	ldo	`-$rounds*$SZ`($Tbl),$Tbl		; rewind $Tbl
+
+	ldw	`0*4`($ctx),$t1		; update context
+	ldw	`1*4`($ctx),$t0
+	ldw	`2*4`($ctx),$t3
+	ldw	`3*4`($ctx),$t2
+	ldw	`4*4`($ctx),$a1
+	ldw	`5*4`($ctx),$a0
+	ldw	`6*4`($ctx),$a3
+	add	$t0,$Alo,$Alo
+	ldw	`7*4`($ctx),$a2
+	addc	$t1,$Ahi,$Ahi
+	ldw	`8*4`($ctx),$t1
+	add	$t2,$Blo,$Blo
+	ldw	`9*4`($ctx),$t0
+	addc	$t3,$Bhi,$Bhi
+	ldw	`10*4`($ctx),$t3
+	add	$a0,$Clo,$Clo
+	ldw	`11*4`($ctx),$t2
+	addc	$a1,$Chi,$Chi
+	ldw	`12*4`($ctx),$a1
+	add	$a2,$Dlo,$Dlo
+	ldw	`13*4`($ctx),$a0
+	addc	$a3,$Dhi,$Dhi
+	ldw	`14*4`($ctx),$a3
+	add	$t0,$Elo,$Elo
+	ldw	`15*4`($ctx),$a2
+	addc	$t1,$Ehi,$Ehi
+	stw	$Ahi,`0*4`($ctx)
+	add	$t2,$Flo,$Flo
+	stw	$Alo,`1*4`($ctx)
+	addc	$t3,$Fhi,$Fhi
+	stw	$Bhi,`2*4`($ctx)
+	add	$a0,$Glo,$Glo
+	stw	$Blo,`3*4`($ctx)
+	addc	$a1,$Ghi,$Ghi
+	stw	$Chi,`4*4`($ctx)
+	add	$a2,$Hlo,$Hlo
+	stw	$Clo,`5*4`($ctx)
+	addc	$a3,$Hhi,$Hhi
+	stw	$Dhi,`6*4`($ctx)
+	ldo	`16*$SZ`($inp),$inp	; advance $inp
+	stw	$Dlo,`7*4`($ctx)
+	stw	$Ehi,`8*4`($ctx)
+	stw	$Elo,`9*4`($ctx)
+	stw	$Fhi,`10*4`($ctx)
+	stw	$Flo,`11*4`($ctx)
+	stw	$Ghi,`12*4`($ctx)
+	stw	$Glo,`13*4`($ctx)
+	stw	$Hhi,`14*4`($ctx)
+	comb,=	$inp,$num,L\$done
+	stw	$Hlo,`15*4`($ctx)
+	b	L\$oop_pa1
+	$PUSH	$inp,`-$FRAME_MARKER-3*$SIZE_T`(%sp)	; save $inp
+L\$done
+___
+}}
+$code.=<<___;
+	$POP	`-$FRAME-$SAVED_RP`(%sp),%r2		; standard epilogue
+	$POP	`-$FRAME+1*$SIZE_T`(%sp),%r4
+	$POP	`-$FRAME+2*$SIZE_T`(%sp),%r5
+	$POP	`-$FRAME+3*$SIZE_T`(%sp),%r6
+	$POP	`-$FRAME+4*$SIZE_T`(%sp),%r7
+	$POP	`-$FRAME+5*$SIZE_T`(%sp),%r8
+	$POP	`-$FRAME+6*$SIZE_T`(%sp),%r9
+	$POP	`-$FRAME+7*$SIZE_T`(%sp),%r10
+	$POP	`-$FRAME+8*$SIZE_T`(%sp),%r11
+	$POP	`-$FRAME+9*$SIZE_T`(%sp),%r12
+	$POP	`-$FRAME+10*$SIZE_T`(%sp),%r13
+	$POP	`-$FRAME+11*$SIZE_T`(%sp),%r14
+	$POP	`-$FRAME+12*$SIZE_T`(%sp),%r15
+	$POP	`-$FRAME+13*$SIZE_T`(%sp),%r16
+	$POP	`-$FRAME+14*$SIZE_T`(%sp),%r17
+	$POP	`-$FRAME+15*$SIZE_T`(%sp),%r18
+	bv	(%r2)
+	.EXIT
+	$POPMB	-$FRAME(%sp),%r3
+	.PROCEND
+	.STRINGZ "SHA`64*$SZ` block transform for PA-RISC, CRYPTOGAMS by "
+___
+
+# Explicitly encode PA-RISC 2.0 instructions used in this module, so
+# that it can be compiled with .LEVEL 1.0. It should be noted that I
+# wouldn't have to do this, if GNU assembler understood .ALLOW 2.0
+# directive...
+
+my $ldd = sub {
+  my ($mod,$args) = @_;
+  my $orig = "ldd$mod\t$args";
+
+    if ($args =~ /(\-?[0-9]+)\(%r([0-9]+)\),%r([0-9]+)/) # format 3 suffices
+    {	my $opcode=(0x14<<26)|($2<<21)|($3<<16)|(($1&0x1FF8)<<1)|(($1>>13)&1);
+	$opcode|=(1<<3) if ($mod =~ /^,m/);
+	$opcode|=(1<<2) if ($mod =~ /^,mb/);
+	sprintf "\t.WORD\t0x%08x\t; %s",$opcode,$orig;
+    }
+    else { "\t".$orig; }
+};
+
+my $std = sub {
+  my ($mod,$args) = @_;
+  my $orig = "std$mod\t$args";
+
+    if ($args =~ /%r([0-9]+),(\-?[0-9]+)\(%r([0-9]+)\)/) # format 3 suffices
+    {	my $opcode=(0x1c<<26)|($3<<21)|($1<<16)|(($2&0x1FF8)<<1)|(($2>>13)&1);
+	sprintf "\t.WORD\t0x%08x\t; %s",$opcode,$orig;
+    }
+    else { "\t".$orig; }
+};
+
+my $extrd = sub {
+  my ($mod,$args) = @_;
+  my $orig = "extrd$mod\t$args";
+
+    # I only have ",u" completer, it's implicitly encoded...
+    if ($args =~ /%r([0-9]+),([0-9]+),([0-9]+),%r([0-9]+)/)	# format 15
+    {	my $opcode=(0x36<<26)|($1<<21)|($4<<16);
+	my $len=32-$3;
+	$opcode |= (($2&0x20)<<6)|(($2&0x1f)<<5);		# encode pos
+	$opcode |= (($len&0x20)<<7)|($len&0x1f);		# encode len
+	sprintf "\t.WORD\t0x%08x\t; %s",$opcode,$orig;
+    }
+    elsif ($args =~ /%r([0-9]+),%sar,([0-9]+),%r([0-9]+)/)	# format 12
+    {	my $opcode=(0x34<<26)|($1<<21)|($3<<16)|(2<<11)|(1<<9);
+	my $len=32-$2;
+	$opcode |= (($len&0x20)<<3)|($len&0x1f);		# encode len
+	$opcode |= (1<<13) if ($mod =~ /,\**=/);
+	sprintf "\t.WORD\t0x%08x\t; %s",$opcode,$orig;
+    }
+    else { "\t".$orig; }
+};
+
+my $shrpd = sub {
+  my ($mod,$args) = @_;
+  my $orig = "shrpd$mod\t$args";
+
+    if ($args =~ /%r([0-9]+),%r([0-9]+),([0-9]+),%r([0-9]+)/)	# format 14
+    {	my $opcode=(0x34<<26)|($2<<21)|($1<<16)|(1<<10)|$4;
+	my $cpos=63-$3;
+	$opcode |= (($cpos&0x20)<<6)|(($cpos&0x1f)<<5);		# encode sa
+	sprintf "\t.WORD\t0x%08x\t; %s",$opcode,$orig;
+    }
+    elsif ($args =~ /%r([0-9]+),%r([0-9]+),%sar,%r([0-9]+)/)	# format 11
+    {	sprintf "\t.WORD\t0x%08x\t; %s",
+		(0x34<<26)|($2<<21)|($1<<16)|(1<<9)|$3,$orig;
+    }
+    else { "\t".$orig; }
+};
+
+sub assemble {
+  my ($mnemonic,$mod,$args)=@_;
+  my $opcode = eval("\$$mnemonic");
+
+    ref($opcode) eq 'CODE' ? &$opcode($mod,$args) : "\t$mnemonic$mod\t$args";
+}
+
+foreach (split("\n",$code)) {
+	s/\`([^\`]*)\`/eval $1/ge;
+
+	s/shd\s+(%r[0-9]+),(%r[0-9]+),([0-9]+)/
+		$3>31 ? sprintf("shd\t%$2,%$1,%d",$3-32)	# rotation for >=32
+		:       sprintf("shd\t%$1,%$2,%d",$3)/e			or
+	# translate made up instructons: _ror, _shr, _align, _shl
+	s/_ror(\s+)(%r[0-9]+),/
+		($SZ==4 ? "shd" : "shrpd")."$1$2,$2,"/e			or
+
+	s/_shr(\s+%r[0-9]+),([0-9]+),/
+		$SZ==4 ? sprintf("extru%s,%d,%d,",$1,31-$2,32-$2)
+		:        sprintf("extrd,u%s,%d,%d,",$1,63-$2,64-$2)/e	or
+
+	s/_align(\s+%r[0-9]+,%r[0-9]+),/
+		($SZ==4 ? "vshd$1," : "shrpd$1,%sar,")/e		or
+
+	s/_shl(\s+%r[0-9]+),([0-9]+),/
+		$SIZE_T==4 ? sprintf("zdep%s,%d,%d,",$1,31-$2,32-$2)
+		:            sprintf("depd,z%s,%d,%d,",$1,63-$2,64-$2)/e;
+
+	s/^\s+([a-z]+)([\S]*)\s+([\S]*)/&assemble($1,$2,$3)/e if ($SIZE_T==4);
+
+	s/cmpb,\*/comb,/ if ($SIZE_T==4);
+
+	s/\bbv\b/bve/    if ($SIZE_T==8);
+
+	print $_,"\n";
+}
+
+close STDOUT;
diff --git a/crypto/sha/asm/sha512-ppc.pl b/crypto/sha/asm/sha512-ppc.pl
new file mode 100755
index 000000000..fe95b0150
--- /dev/null
+++ b/crypto/sha/asm/sha512-ppc.pl
@@ -0,0 +1,799 @@
+#! /usr/bin/env perl
+# Copyright 2006-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+
+# ====================================================================
+# Written by Andy Polyakov  for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+
+# I let hardware handle unaligned input, except on page boundaries
+# (see below for details). Otherwise straightforward implementation
+# with X vector in register bank.
+
+#			sha256		|	sha512
+# 			-m64	-m32	|	-m64	-m32
+# --------------------------------------+-----------------------
+# PPC970,gcc-4.0.0	+50%	+38%	|	+40%	+410%(*)
+# Power6,xlc-7		+150%	+90%	|	+100%	+430%(*)
+#
+# (*)	64-bit code in 32-bit application context, which actually is
+#	on TODO list. It should be noted that for safe deployment in
+#	32-bit *mutli-threaded* context asyncronous signals should be
+#	blocked upon entry to SHA512 block routine. This is because
+#	32-bit signaling procedure invalidates upper halves of GPRs.
+#	Context switch procedure preserves them, but not signaling:-(
+
+# Second version is true multi-thread safe. Trouble with the original
+# version was that it was using thread local storage pointer register.
+# Well, it scrupulously preserved it, but the problem would arise the
+# moment asynchronous signal was delivered and signal handler would
+# dereference the TLS pointer. While it's never the case in openssl
+# application or test suite, we have to respect this scenario and not
+# use TLS pointer register. Alternative would be to require caller to
+# block signals prior calling this routine. For the record, in 32-bit
+# context R2 serves as TLS pointer, while in 64-bit context - R13.
+
+$flavour=shift;
+$output =shift;
+
+if ($flavour =~ /64/) {
+	$SIZE_T=8;
+	$LRSAVE=2*$SIZE_T;
+	$STU="stdu";
+	$UCMP="cmpld";
+	$SHL="sldi";
+	$POP="ld";
+	$PUSH="std";
+} elsif ($flavour =~ /32/) {
+	$SIZE_T=4;
+	$LRSAVE=$SIZE_T;
+	$STU="stwu";
+	$UCMP="cmplw";
+	$SHL="slwi";
+	$POP="lwz";
+	$PUSH="stw";
+} else { die "nonsense $flavour"; }
+
+$LITTLE_ENDIAN = ($flavour=~/le$/) ? $SIZE_T : 0;
+
+$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+( $xlate="${dir}ppc-xlate.pl" and -f $xlate ) or
+( $xlate="${dir}../../perlasm/ppc-xlate.pl" and -f $xlate) or
+die "can't locate ppc-xlate.pl";
+
+open STDOUT,"| $^X $xlate $flavour $output" || die "can't call $xlate: $!";
+
+if ($output =~ /512/) {
+	$func="sha512_block_ppc";
+	$SZ=8;
+	@Sigma0=(28,34,39);
+	@Sigma1=(14,18,41);
+	@sigma0=(1,  8, 7);
+	@sigma1=(19,61, 6);
+	$rounds=80;
+	$LD="ld";
+	$ST="std";
+	$ROR="rotrdi";
+	$SHR="srdi";
+} else {
+	$func="sha256_block_ppc";
+	$SZ=4;
+	@Sigma0=( 2,13,22);
+	@Sigma1=( 6,11,25);
+	@sigma0=( 7,18, 3);
+	@sigma1=(17,19,10);
+	$rounds=64;
+	$LD="lwz";
+	$ST="stw";
+	$ROR="rotrwi";
+	$SHR="srwi";
+}
+
+$FRAME=32*$SIZE_T+16*$SZ;
+$LOCALS=6*$SIZE_T;
+
+$sp ="r1";
+$toc="r2";
+$ctx="r3";	# zapped by $a0
+$inp="r4";	# zapped by $a1
+$num="r5";	# zapped by $t0
+
+$T  ="r0";
+$a0 ="r3";
+$a1 ="r4";
+$t0 ="r5";
+$t1 ="r6";
+$Tbl="r7";
+
+$A  ="r8";
+$B  ="r9";
+$C  ="r10";
+$D  ="r11";
+$E  ="r12";
+$F  =$t1;	$t1 = "r0";	# stay away from "r13";
+$G  ="r14";
+$H  ="r15";
+
+@V=($A,$B,$C,$D,$E,$F,$G,$H);
+@X=("r16","r17","r18","r19","r20","r21","r22","r23",
+    "r24","r25","r26","r27","r28","r29","r30","r31");
+
+$inp="r31" if($SZ==4 || $SIZE_T==8);	# reassigned $inp! aliases with @X[15]
+
+sub ROUND_00_15 {
+my ($i,$a,$b,$c,$d,$e,$f,$g,$h)=@_;
+$code.=<<___;
+	$ROR	$a0,$e,$Sigma1[0]
+	$ROR	$a1,$e,$Sigma1[1]
+	and	$t0,$f,$e
+	xor	$a0,$a0,$a1
+	add	$h,$h,$t1
+	andc	$t1,$g,$e
+	$ROR	$a1,$a1,`$Sigma1[2]-$Sigma1[1]`
+	or	$t0,$t0,$t1		; Ch(e,f,g)
+	add	$h,$h,@X[$i%16]
+	xor	$a0,$a0,$a1		; Sigma1(e)
+	add	$h,$h,$t0
+	add	$h,$h,$a0
+
+	$ROR	$a0,$a,$Sigma0[0]
+	$ROR	$a1,$a,$Sigma0[1]
+	and	$t0,$a,$b
+	and	$t1,$a,$c
+	xor	$a0,$a0,$a1
+	$ROR	$a1,$a1,`$Sigma0[2]-$Sigma0[1]`
+	xor	$t0,$t0,$t1
+	and	$t1,$b,$c
+	xor	$a0,$a0,$a1		; Sigma0(a)
+	add	$d,$d,$h
+	xor	$t0,$t0,$t1		; Maj(a,b,c)
+___
+$code.=<<___ if ($i<15);
+	$LD	$t1,`($i+1)*$SZ`($Tbl)
+___
+$code.=<<___;
+	add	$h,$h,$a0
+	add	$h,$h,$t0
+
+___
+}
+
+sub ROUND_16_xx {
+my ($i,$a,$b,$c,$d,$e,$f,$g,$h)=@_;
+$i-=16;
+$code.=<<___;
+	$ROR	$a0,@X[($i+1)%16],$sigma0[0]
+	$ROR	$a1,@X[($i+1)%16],$sigma0[1]
+	$ROR	$t0,@X[($i+14)%16],$sigma1[0]
+	$ROR	$t1,@X[($i+14)%16],$sigma1[1]
+	xor	$a0,$a0,$a1
+	$SHR	$a1,@X[($i+1)%16],$sigma0[2]
+	xor	$t0,$t0,$t1
+	$SHR	$t1,@X[($i+14)%16],$sigma1[2]
+	add	@X[$i],@X[$i],@X[($i+9)%16]
+	xor	$a0,$a0,$a1		; sigma0(X[(i+1)&0x0f])
+	xor	$t0,$t0,$t1		; sigma1(X[(i+14)&0x0f])
+	$LD	$t1,`$i*$SZ`($Tbl)
+	add	@X[$i],@X[$i],$a0
+	add	@X[$i],@X[$i],$t0
+___
+&ROUND_00_15($i+16,$a,$b,$c,$d,$e,$f,$g,$h);
+}
+
+$code=<<___;
+.machine	"any"
+.text
+
+.globl	$func
+.align	6
+$func:
+	$STU	$sp,-$FRAME($sp)
+	mflr	r0
+	$SHL	$num,$num,`log(16*$SZ)/log(2)`
+
+	$PUSH	$ctx,`$FRAME-$SIZE_T*22`($sp)
+
+	$PUSH	r14,`$FRAME-$SIZE_T*18`($sp)
+	$PUSH	r15,`$FRAME-$SIZE_T*17`($sp)
+	$PUSH	r16,`$FRAME-$SIZE_T*16`($sp)
+	$PUSH	r17,`$FRAME-$SIZE_T*15`($sp)
+	$PUSH	r18,`$FRAME-$SIZE_T*14`($sp)
+	$PUSH	r19,`$FRAME-$SIZE_T*13`($sp)
+	$PUSH	r20,`$FRAME-$SIZE_T*12`($sp)
+	$PUSH	r21,`$FRAME-$SIZE_T*11`($sp)
+	$PUSH	r22,`$FRAME-$SIZE_T*10`($sp)
+	$PUSH	r23,`$FRAME-$SIZE_T*9`($sp)
+	$PUSH	r24,`$FRAME-$SIZE_T*8`($sp)
+	$PUSH	r25,`$FRAME-$SIZE_T*7`($sp)
+	$PUSH	r26,`$FRAME-$SIZE_T*6`($sp)
+	$PUSH	r27,`$FRAME-$SIZE_T*5`($sp)
+	$PUSH	r28,`$FRAME-$SIZE_T*4`($sp)
+	$PUSH	r29,`$FRAME-$SIZE_T*3`($sp)
+	$PUSH	r30,`$FRAME-$SIZE_T*2`($sp)
+	$PUSH	r31,`$FRAME-$SIZE_T*1`($sp)
+	$PUSH	r0,`$FRAME+$LRSAVE`($sp)
+___
+
+if ($SZ==4 || $SIZE_T==8) {
+$code.=<<___;
+	$LD	$A,`0*$SZ`($ctx)
+	mr	$inp,r4				; incarnate $inp
+	$LD	$B,`1*$SZ`($ctx)
+	$LD	$C,`2*$SZ`($ctx)
+	$LD	$D,`3*$SZ`($ctx)
+	$LD	$E,`4*$SZ`($ctx)
+	$LD	$F,`5*$SZ`($ctx)
+	$LD	$G,`6*$SZ`($ctx)
+	$LD	$H,`7*$SZ`($ctx)
+___
+} else {
+  for ($i=16;$i<32;$i++) {
+    $code.=<<___;
+	lwz	r$i,`$LITTLE_ENDIAN^(4*($i-16))`($ctx)
+___
+  }
+}
+
+$code.=<<___;
+	bl	LPICmeup
+LPICedup:
+	andi.	r0,$inp,3
+	bne	Lunaligned
+Laligned:
+	add	$num,$inp,$num
+	$PUSH	$num,`$FRAME-$SIZE_T*24`($sp)	; end pointer
+	$PUSH	$inp,`$FRAME-$SIZE_T*23`($sp)	; inp pointer
+	bl	Lsha2_block_private
+	b	Ldone
+
+; PowerPC specification allows an implementation to be ill-behaved
+; upon unaligned access which crosses page boundary. "Better safe
+; than sorry" principle makes me treat it specially. But I don't
+; look for particular offending word, but rather for the input
+; block which crosses the boundary. Once found that block is aligned
+; and hashed separately...
+.align	4
+Lunaligned:
+	subfic	$t1,$inp,4096
+	andi.	$t1,$t1,`4096-16*$SZ`	; distance to closest page boundary
+	beq	Lcross_page
+	$UCMP	$num,$t1
+	ble	Laligned		; didn't cross the page boundary
+	subfc	$num,$t1,$num
+	add	$t1,$inp,$t1
+	$PUSH	$num,`$FRAME-$SIZE_T*25`($sp)	; save real remaining num
+	$PUSH	$t1,`$FRAME-$SIZE_T*24`($sp)	; intermediate end pointer
+	$PUSH	$inp,`$FRAME-$SIZE_T*23`($sp)	; inp pointer
+	bl	Lsha2_block_private
+	; $inp equals to the intermediate end pointer here
+	$POP	$num,`$FRAME-$SIZE_T*25`($sp)	; restore real remaining num
+Lcross_page:
+	li	$t1,`16*$SZ/4`
+	mtctr	$t1
+___
+if ($SZ==4 || $SIZE_T==8) {
+$code.=<<___;
+	addi	r20,$sp,$LOCALS			; aligned spot below the frame
+Lmemcpy:
+	lbz	r16,0($inp)
+	lbz	r17,1($inp)
+	lbz	r18,2($inp)
+	lbz	r19,3($inp)
+	addi	$inp,$inp,4
+	stb	r16,0(r20)
+	stb	r17,1(r20)
+	stb	r18,2(r20)
+	stb	r19,3(r20)
+	addi	r20,r20,4
+	bdnz	Lmemcpy
+___
+} else {
+$code.=<<___;
+	addi	r12,$sp,$LOCALS			; aligned spot below the frame
+Lmemcpy:
+	lbz	r8,0($inp)
+	lbz	r9,1($inp)
+	lbz	r10,2($inp)
+	lbz	r11,3($inp)
+	addi	$inp,$inp,4
+	stb	r8,0(r12)
+	stb	r9,1(r12)
+	stb	r10,2(r12)
+	stb	r11,3(r12)
+	addi	r12,r12,4
+	bdnz	Lmemcpy
+___
+}
+
+$code.=<<___;
+	$PUSH	$inp,`$FRAME-$SIZE_T*26`($sp)	; save real inp
+	addi	$t1,$sp,`$LOCALS+16*$SZ`	; fictitious end pointer
+	addi	$inp,$sp,$LOCALS		; fictitious inp pointer
+	$PUSH	$num,`$FRAME-$SIZE_T*25`($sp)	; save real num
+	$PUSH	$t1,`$FRAME-$SIZE_T*24`($sp)	; end pointer
+	$PUSH	$inp,`$FRAME-$SIZE_T*23`($sp)	; inp pointer
+	bl	Lsha2_block_private
+	$POP	$inp,`$FRAME-$SIZE_T*26`($sp)	; restore real inp
+	$POP	$num,`$FRAME-$SIZE_T*25`($sp)	; restore real num
+	addic.	$num,$num,`-16*$SZ`		; num--
+	bne	Lunaligned
+
+Ldone:
+	$POP	r0,`$FRAME+$LRSAVE`($sp)
+	$POP	r14,`$FRAME-$SIZE_T*18`($sp)
+	$POP	r15,`$FRAME-$SIZE_T*17`($sp)
+	$POP	r16,`$FRAME-$SIZE_T*16`($sp)
+	$POP	r17,`$FRAME-$SIZE_T*15`($sp)
+	$POP	r18,`$FRAME-$SIZE_T*14`($sp)
+	$POP	r19,`$FRAME-$SIZE_T*13`($sp)
+	$POP	r20,`$FRAME-$SIZE_T*12`($sp)
+	$POP	r21,`$FRAME-$SIZE_T*11`($sp)
+	$POP	r22,`$FRAME-$SIZE_T*10`($sp)
+	$POP	r23,`$FRAME-$SIZE_T*9`($sp)
+	$POP	r24,`$FRAME-$SIZE_T*8`($sp)
+	$POP	r25,`$FRAME-$SIZE_T*7`($sp)
+	$POP	r26,`$FRAME-$SIZE_T*6`($sp)
+	$POP	r27,`$FRAME-$SIZE_T*5`($sp)
+	$POP	r28,`$FRAME-$SIZE_T*4`($sp)
+	$POP	r29,`$FRAME-$SIZE_T*3`($sp)
+	$POP	r30,`$FRAME-$SIZE_T*2`($sp)
+	$POP	r31,`$FRAME-$SIZE_T*1`($sp)
+	mtlr	r0
+	addi	$sp,$sp,$FRAME
+	blr
+	.long	0
+	.byte	0,12,4,1,0x80,18,3,0
+	.long	0
+___
+
+if ($SZ==4 || $SIZE_T==8) {
+$code.=<<___;
+.align	4
+Lsha2_block_private:
+	$LD	$t1,0($Tbl)
+___
+for($i=0;$i<16;$i++) {
+$code.=<<___ if ($SZ==4 && !$LITTLE_ENDIAN);
+	lwz	@X[$i],`$i*$SZ`($inp)
+___
+$code.=<<___ if ($SZ==4 && $LITTLE_ENDIAN);
+	lwz	$a0,`$i*$SZ`($inp)
+	rotlwi	@X[$i],$a0,8
+	rlwimi	@X[$i],$a0,24,0,7
+	rlwimi	@X[$i],$a0,24,16,23
+___
+# 64-bit loads are split to 2x32-bit ones, as CPU can't handle
+# unaligned 64-bit loads, only 32-bit ones...
+$code.=<<___ if ($SZ==8 && !$LITTLE_ENDIAN);
+	lwz	$t0,`$i*$SZ`($inp)
+	lwz	@X[$i],`$i*$SZ+4`($inp)
+	insrdi	@X[$i],$t0,32,0
+___
+$code.=<<___ if ($SZ==8 && $LITTLE_ENDIAN);
+	lwz	$a0,`$i*$SZ`($inp)
+	 lwz	$a1,`$i*$SZ+4`($inp)
+	rotlwi	$t0,$a0,8
+	 rotlwi	@X[$i],$a1,8
+	rlwimi	$t0,$a0,24,0,7
+	 rlwimi	@X[$i],$a1,24,0,7
+	rlwimi	$t0,$a0,24,16,23
+	 rlwimi	@X[$i],$a1,24,16,23
+	insrdi	@X[$i],$t0,32,0
+___
+	&ROUND_00_15($i,@V);
+	unshift(@V,pop(@V));
+}
+$code.=<<___;
+	li	$t0,`$rounds/16-1`
+	mtctr	$t0
+.align	4
+Lrounds:
+	addi	$Tbl,$Tbl,`16*$SZ`
+___
+for(;$i<32;$i++) {
+	&ROUND_16_xx($i,@V);
+	unshift(@V,pop(@V));
+}
+$code.=<<___;
+	bdnz	Lrounds
+
+	$POP	$ctx,`$FRAME-$SIZE_T*22`($sp)
+	$POP	$inp,`$FRAME-$SIZE_T*23`($sp)	; inp pointer
+	$POP	$num,`$FRAME-$SIZE_T*24`($sp)	; end pointer
+	subi	$Tbl,$Tbl,`($rounds-16)*$SZ`	; rewind Tbl
+
+	$LD	r16,`0*$SZ`($ctx)
+	$LD	r17,`1*$SZ`($ctx)
+	$LD	r18,`2*$SZ`($ctx)
+	$LD	r19,`3*$SZ`($ctx)
+	$LD	r20,`4*$SZ`($ctx)
+	$LD	r21,`5*$SZ`($ctx)
+	$LD	r22,`6*$SZ`($ctx)
+	addi	$inp,$inp,`16*$SZ`		; advance inp
+	$LD	r23,`7*$SZ`($ctx)
+	add	$A,$A,r16
+	add	$B,$B,r17
+	$PUSH	$inp,`$FRAME-$SIZE_T*23`($sp)
+	add	$C,$C,r18
+	$ST	$A,`0*$SZ`($ctx)
+	add	$D,$D,r19
+	$ST	$B,`1*$SZ`($ctx)
+	add	$E,$E,r20
+	$ST	$C,`2*$SZ`($ctx)
+	add	$F,$F,r21
+	$ST	$D,`3*$SZ`($ctx)
+	add	$G,$G,r22
+	$ST	$E,`4*$SZ`($ctx)
+	add	$H,$H,r23
+	$ST	$F,`5*$SZ`($ctx)
+	$ST	$G,`6*$SZ`($ctx)
+	$UCMP	$inp,$num
+	$ST	$H,`7*$SZ`($ctx)
+	bne	Lsha2_block_private
+	blr
+	.long	0
+	.byte	0,12,0x14,0,0,0,0,0
+.size	$func,.-$func
+___
+} else {
+########################################################################
+# SHA512 for PPC32, X vector is off-loaded to stack...
+#
+#			|	sha512
+#			|	-m32
+# ----------------------+-----------------------
+# PPC74x0,gcc-4.0.1	|	+48%
+# POWER6,gcc-4.4.6	|	+124%(*)
+# POWER7,gcc-4.4.6	|	+79%(*)
+# e300,gcc-4.1.0	|	+167%
+#
+# (*)	~1/3 of -m64 result [and ~20% better than -m32 code generated
+#	by xlc-12.1]
+
+my $XOFF=$LOCALS;
+
+my @V=map("r$_",(16..31));	# A..H
+
+my ($s0,$s1,$t0,$t1,$t2,$t3,$a0,$a1,$a2,$a3)=map("r$_",(0,5,6,8..12,14,15));
+my ($x0,$x1)=("r3","r4");	# zaps $ctx and $inp
+
+sub ROUND_00_15_ppc32 {
+my ($i,	$ahi,$alo,$bhi,$blo,$chi,$clo,$dhi,$dlo,
+	$ehi,$elo,$fhi,$flo,$ghi,$glo,$hhi,$hlo)=@_;
+
+$code.=<<___;
+	lwz	$t2,`$SZ*($i%16)+($LITTLE_ENDIAN^4)`($Tbl)
+	 xor	$a0,$flo,$glo
+	lwz	$t3,`$SZ*($i%16)+($LITTLE_ENDIAN^0)`($Tbl)
+	 xor	$a1,$fhi,$ghi
+	addc	$hlo,$hlo,$t0			; h+=x[i]
+	stw	$t0,`$XOFF+0+$SZ*($i%16)`($sp)	; save x[i]
+
+	srwi	$s0,$elo,$Sigma1[0]
+	srwi	$s1,$ehi,$Sigma1[0]
+	 and	$a0,$a0,$elo
+	adde	$hhi,$hhi,$t1
+	 and	$a1,$a1,$ehi
+	stw	$t1,`$XOFF+4+$SZ*($i%16)`($sp)
+	srwi	$t0,$elo,$Sigma1[1]
+	srwi	$t1,$ehi,$Sigma1[1]
+	 addc	$hlo,$hlo,$t2			; h+=K512[i]
+	insrwi	$s0,$ehi,$Sigma1[0],0
+	insrwi	$s1,$elo,$Sigma1[0],0
+	 xor	$a0,$a0,$glo			; Ch(e,f,g)
+	 adde	$hhi,$hhi,$t3
+	 xor	$a1,$a1,$ghi
+	insrwi	$t0,$ehi,$Sigma1[1],0
+	insrwi	$t1,$elo,$Sigma1[1],0
+	 addc	$hlo,$hlo,$a0			; h+=Ch(e,f,g)
+	srwi	$t2,$ehi,$Sigma1[2]-32
+	srwi	$t3,$elo,$Sigma1[2]-32
+	xor	$s0,$s0,$t0
+	xor	$s1,$s1,$t1
+	insrwi	$t2,$elo,$Sigma1[2]-32,0
+	insrwi	$t3,$ehi,$Sigma1[2]-32,0
+	 xor	$a0,$alo,$blo			; a^b, b^c in next round
+	 adde	$hhi,$hhi,$a1
+	 xor	$a1,$ahi,$bhi
+	xor	$s0,$s0,$t2			; Sigma1(e)
+	xor	$s1,$s1,$t3
+
+	srwi	$t0,$alo,$Sigma0[0]
+	 and	$a2,$a2,$a0
+	 addc	$hlo,$hlo,$s0			; h+=Sigma1(e)
+	 and	$a3,$a3,$a1
+	srwi	$t1,$ahi,$Sigma0[0]
+	srwi	$s0,$ahi,$Sigma0[1]-32
+	 adde	$hhi,$hhi,$s1
+	srwi	$s1,$alo,$Sigma0[1]-32
+	insrwi	$t0,$ahi,$Sigma0[0],0
+	insrwi	$t1,$alo,$Sigma0[0],0
+	 xor	$a2,$a2,$blo			; Maj(a,b,c)
+	 addc	$dlo,$dlo,$hlo			; d+=h
+	 xor	$a3,$a3,$bhi
+	insrwi	$s0,$alo,$Sigma0[1]-32,0
+	insrwi	$s1,$ahi,$Sigma0[1]-32,0
+	 adde	$dhi,$dhi,$hhi
+	srwi	$t2,$ahi,$Sigma0[2]-32
+	srwi	$t3,$alo,$Sigma0[2]-32
+	xor	$s0,$s0,$t0
+	 addc	$hlo,$hlo,$a2			; h+=Maj(a,b,c)
+	xor	$s1,$s1,$t1
+	insrwi	$t2,$alo,$Sigma0[2]-32,0
+	insrwi	$t3,$ahi,$Sigma0[2]-32,0
+	 adde	$hhi,$hhi,$a3
+___
+$code.=<<___ if ($i>=15);
+	lwz	$t0,`$XOFF+0+$SZ*(($i+2)%16)`($sp)
+	lwz	$t1,`$XOFF+4+$SZ*(($i+2)%16)`($sp)
+___
+$code.=<<___ if ($i<15 && !$LITTLE_ENDIAN);
+	lwz	$t1,`$SZ*($i+1)+0`($inp)
+	lwz	$t0,`$SZ*($i+1)+4`($inp)
+___
+$code.=<<___ if ($i<15 && $LITTLE_ENDIAN);
+	lwz	$a2,`$SZ*($i+1)+0`($inp)
+	 lwz	$a3,`$SZ*($i+1)+4`($inp)
+	rotlwi	$t1,$a2,8
+	 rotlwi	$t0,$a3,8
+	rlwimi	$t1,$a2,24,0,7
+	 rlwimi	$t0,$a3,24,0,7
+	rlwimi	$t1,$a2,24,16,23
+	 rlwimi	$t0,$a3,24,16,23
+___
+$code.=<<___;
+	xor	$s0,$s0,$t2			; Sigma0(a)
+	xor	$s1,$s1,$t3
+	addc	$hlo,$hlo,$s0			; h+=Sigma0(a)
+	adde	$hhi,$hhi,$s1
+___
+$code.=<<___ if ($i==15);
+	lwz	$x0,`$XOFF+0+$SZ*(($i+1)%16)`($sp)
+	lwz	$x1,`$XOFF+4+$SZ*(($i+1)%16)`($sp)
+___
+}
+sub ROUND_16_xx_ppc32 {
+my ($i,	$ahi,$alo,$bhi,$blo,$chi,$clo,$dhi,$dlo,
+	$ehi,$elo,$fhi,$flo,$ghi,$glo,$hhi,$hlo)=@_;
+
+$code.=<<___;
+	srwi	$s0,$t0,$sigma0[0]
+	srwi	$s1,$t1,$sigma0[0]
+	srwi	$t2,$t0,$sigma0[1]
+	srwi	$t3,$t1,$sigma0[1]
+	insrwi	$s0,$t1,$sigma0[0],0
+	insrwi	$s1,$t0,$sigma0[0],0
+	srwi	$a0,$t0,$sigma0[2]
+	insrwi	$t2,$t1,$sigma0[1],0
+	insrwi	$t3,$t0,$sigma0[1],0
+	insrwi	$a0,$t1,$sigma0[2],0
+	xor	$s0,$s0,$t2
+	 lwz	$t2,`$XOFF+0+$SZ*(($i+14)%16)`($sp)
+	srwi	$a1,$t1,$sigma0[2]
+	xor	$s1,$s1,$t3
+	 lwz	$t3,`$XOFF+4+$SZ*(($i+14)%16)`($sp)
+	xor	$a0,$a0,$s0
+	 srwi	$s0,$t2,$sigma1[0]
+	xor	$a1,$a1,$s1
+	 srwi	$s1,$t3,$sigma1[0]
+	addc	$x0,$x0,$a0			; x[i]+=sigma0(x[i+1])
+	 srwi	$a0,$t3,$sigma1[1]-32
+	insrwi	$s0,$t3,$sigma1[0],0
+	insrwi	$s1,$t2,$sigma1[0],0
+	adde	$x1,$x1,$a1
+	 srwi	$a1,$t2,$sigma1[1]-32
+
+	insrwi	$a0,$t2,$sigma1[1]-32,0
+	srwi	$t2,$t2,$sigma1[2]
+	insrwi	$a1,$t3,$sigma1[1]-32,0
+	insrwi	$t2,$t3,$sigma1[2],0
+	xor	$s0,$s0,$a0
+	 lwz	$a0,`$XOFF+0+$SZ*(($i+9)%16)`($sp)
+	srwi	$t3,$t3,$sigma1[2]
+	xor	$s1,$s1,$a1
+	 lwz	$a1,`$XOFF+4+$SZ*(($i+9)%16)`($sp)
+	xor	$s0,$s0,$t2
+	 addc	$x0,$x0,$a0			; x[i]+=x[i+9]
+	xor	$s1,$s1,$t3
+	 adde	$x1,$x1,$a1
+	addc	$x0,$x0,$s0			; x[i]+=sigma1(x[i+14])
+	adde	$x1,$x1,$s1
+___
+	($t0,$t1,$x0,$x1) = ($x0,$x1,$t0,$t1);
+	&ROUND_00_15_ppc32(@_);
+}
+
+$code.=<<___;
+.align	4
+Lsha2_block_private:
+___
+$code.=<<___ if (!$LITTLE_ENDIAN);
+	lwz	$t1,0($inp)
+	xor	$a2,@V[3],@V[5]		; B^C, magic seed
+	lwz	$t0,4($inp)
+	xor	$a3,@V[2],@V[4]
+___
+$code.=<<___ if ($LITTLE_ENDIAN);
+	lwz	$a1,0($inp)
+	xor	$a2,@V[3],@V[5]		; B^C, magic seed
+	lwz	$a0,4($inp)
+	xor	$a3,@V[2],@V[4]
+	rotlwi	$t1,$a1,8
+	 rotlwi	$t0,$a0,8
+	rlwimi	$t1,$a1,24,0,7
+	 rlwimi	$t0,$a0,24,0,7
+	rlwimi	$t1,$a1,24,16,23
+	 rlwimi	$t0,$a0,24,16,23
+___
+for($i=0;$i<16;$i++) {
+	&ROUND_00_15_ppc32($i,@V);
+	unshift(@V,pop(@V));	unshift(@V,pop(@V));
+	($a0,$a1,$a2,$a3) = ($a2,$a3,$a0,$a1);
+}
+$code.=<<___;
+	li	$a0,`$rounds/16-1`
+	mtctr	$a0
+.align	4
+Lrounds:
+	addi	$Tbl,$Tbl,`16*$SZ`
+___
+for(;$i<32;$i++) {
+	&ROUND_16_xx_ppc32($i,@V);
+	unshift(@V,pop(@V));	unshift(@V,pop(@V));
+	($a0,$a1,$a2,$a3) = ($a2,$a3,$a0,$a1);
+}
+$code.=<<___;
+	bdnz	Lrounds
+
+	$POP	$ctx,`$FRAME-$SIZE_T*22`($sp)
+	$POP	$inp,`$FRAME-$SIZE_T*23`($sp)	; inp pointer
+	$POP	$num,`$FRAME-$SIZE_T*24`($sp)	; end pointer
+	subi	$Tbl,$Tbl,`($rounds-16)*$SZ`	; rewind Tbl
+
+	lwz	$t0,`$LITTLE_ENDIAN^0`($ctx)
+	lwz	$t1,`$LITTLE_ENDIAN^4`($ctx)
+	lwz	$t2,`$LITTLE_ENDIAN^8`($ctx)
+	lwz	$t3,`$LITTLE_ENDIAN^12`($ctx)
+	lwz	$a0,`$LITTLE_ENDIAN^16`($ctx)
+	lwz	$a1,`$LITTLE_ENDIAN^20`($ctx)
+	lwz	$a2,`$LITTLE_ENDIAN^24`($ctx)
+	addc	@V[1],@V[1],$t1
+	lwz	$a3,`$LITTLE_ENDIAN^28`($ctx)
+	adde	@V[0],@V[0],$t0
+	lwz	$t0,`$LITTLE_ENDIAN^32`($ctx)
+	addc	@V[3],@V[3],$t3
+	lwz	$t1,`$LITTLE_ENDIAN^36`($ctx)
+	adde	@V[2],@V[2],$t2
+	lwz	$t2,`$LITTLE_ENDIAN^40`($ctx)
+	addc	@V[5],@V[5],$a1
+	lwz	$t3,`$LITTLE_ENDIAN^44`($ctx)
+	adde	@V[4],@V[4],$a0
+	lwz	$a0,`$LITTLE_ENDIAN^48`($ctx)
+	addc	@V[7],@V[7],$a3
+	lwz	$a1,`$LITTLE_ENDIAN^52`($ctx)
+	adde	@V[6],@V[6],$a2
+	lwz	$a2,`$LITTLE_ENDIAN^56`($ctx)
+	addc	@V[9],@V[9],$t1
+	lwz	$a3,`$LITTLE_ENDIAN^60`($ctx)
+	adde	@V[8],@V[8],$t0
+	stw	@V[0],`$LITTLE_ENDIAN^0`($ctx)
+	stw	@V[1],`$LITTLE_ENDIAN^4`($ctx)
+	addc	@V[11],@V[11],$t3
+	stw	@V[2],`$LITTLE_ENDIAN^8`($ctx)
+	stw	@V[3],`$LITTLE_ENDIAN^12`($ctx)
+	adde	@V[10],@V[10],$t2
+	stw	@V[4],`$LITTLE_ENDIAN^16`($ctx)
+	stw	@V[5],`$LITTLE_ENDIAN^20`($ctx)
+	addc	@V[13],@V[13],$a1
+	stw	@V[6],`$LITTLE_ENDIAN^24`($ctx)
+	stw	@V[7],`$LITTLE_ENDIAN^28`($ctx)
+	adde	@V[12],@V[12],$a0
+	stw	@V[8],`$LITTLE_ENDIAN^32`($ctx)
+	stw	@V[9],`$LITTLE_ENDIAN^36`($ctx)
+	addc	@V[15],@V[15],$a3
+	stw	@V[10],`$LITTLE_ENDIAN^40`($ctx)
+	stw	@V[11],`$LITTLE_ENDIAN^44`($ctx)
+	adde	@V[14],@V[14],$a2
+	stw	@V[12],`$LITTLE_ENDIAN^48`($ctx)
+	stw	@V[13],`$LITTLE_ENDIAN^52`($ctx)
+	stw	@V[14],`$LITTLE_ENDIAN^56`($ctx)
+	stw	@V[15],`$LITTLE_ENDIAN^60`($ctx)
+
+	addi	$inp,$inp,`16*$SZ`		; advance inp
+	$PUSH	$inp,`$FRAME-$SIZE_T*23`($sp)
+	$UCMP	$inp,$num
+	bne	Lsha2_block_private
+	blr
+	.long	0
+	.byte	0,12,0x14,0,0,0,0,0
+.size	$func,.-$func
+___
+}
+
+# Ugly hack here, because PPC assembler syntax seem to vary too
+# much from platforms to platform...
+$code.=<<___;
+.align	6
+LPICmeup:
+	mflr	r0
+	bcl	20,31,\$+4
+	mflr	$Tbl	; vvvvvv "distance" between . and 1st data entry
+	addi	$Tbl,$Tbl,`64-8`
+	mtlr	r0
+	blr
+	.long	0
+	.byte	0,12,0x14,0,0,0,0,0
+	.space	`64-9*4`
+___
+$code.=<<___ if ($SZ==8);
+	.quad	0x428a2f98d728ae22,0x7137449123ef65cd
+	.quad	0xb5c0fbcfec4d3b2f,0xe9b5dba58189dbbc
+	.quad	0x3956c25bf348b538,0x59f111f1b605d019
+	.quad	0x923f82a4af194f9b,0xab1c5ed5da6d8118
+	.quad	0xd807aa98a3030242,0x12835b0145706fbe
+	.quad	0x243185be4ee4b28c,0x550c7dc3d5ffb4e2
+	.quad	0x72be5d74f27b896f,0x80deb1fe3b1696b1
+	.quad	0x9bdc06a725c71235,0xc19bf174cf692694
+	.quad	0xe49b69c19ef14ad2,0xefbe4786384f25e3
+	.quad	0x0fc19dc68b8cd5b5,0x240ca1cc77ac9c65
+	.quad	0x2de92c6f592b0275,0x4a7484aa6ea6e483
+	.quad	0x5cb0a9dcbd41fbd4,0x76f988da831153b5
+	.quad	0x983e5152ee66dfab,0xa831c66d2db43210
+	.quad	0xb00327c898fb213f,0xbf597fc7beef0ee4
+	.quad	0xc6e00bf33da88fc2,0xd5a79147930aa725
+	.quad	0x06ca6351e003826f,0x142929670a0e6e70
+	.quad	0x27b70a8546d22ffc,0x2e1b21385c26c926
+	.quad	0x4d2c6dfc5ac42aed,0x53380d139d95b3df
+	.quad	0x650a73548baf63de,0x766a0abb3c77b2a8
+	.quad	0x81c2c92e47edaee6,0x92722c851482353b
+	.quad	0xa2bfe8a14cf10364,0xa81a664bbc423001
+	.quad	0xc24b8b70d0f89791,0xc76c51a30654be30
+	.quad	0xd192e819d6ef5218,0xd69906245565a910
+	.quad	0xf40e35855771202a,0x106aa07032bbd1b8
+	.quad	0x19a4c116b8d2d0c8,0x1e376c085141ab53
+	.quad	0x2748774cdf8eeb99,0x34b0bcb5e19b48a8
+	.quad	0x391c0cb3c5c95a63,0x4ed8aa4ae3418acb
+	.quad	0x5b9cca4f7763e373,0x682e6ff3d6b2b8a3
+	.quad	0x748f82ee5defb2fc,0x78a5636f43172f60
+	.quad	0x84c87814a1f0ab72,0x8cc702081a6439ec
+	.quad	0x90befffa23631e28,0xa4506cebde82bde9
+	.quad	0xbef9a3f7b2c67915,0xc67178f2e372532b
+	.quad	0xca273eceea26619c,0xd186b8c721c0c207
+	.quad	0xeada7dd6cde0eb1e,0xf57d4f7fee6ed178
+	.quad	0x06f067aa72176fba,0x0a637dc5a2c898a6
+	.quad	0x113f9804bef90dae,0x1b710b35131c471b
+	.quad	0x28db77f523047d84,0x32caab7b40c72493
+	.quad	0x3c9ebe0a15c9bebc,0x431d67c49c100d4c
+	.quad	0x4cc5d4becb3e42b6,0x597f299cfc657e2a
+	.quad	0x5fcb6fab3ad6faec,0x6c44198c4a475817
+___
+$code.=<<___ if ($SZ==4);
+	.long	0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5
+	.long	0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5
+	.long	0xd807aa98,0x12835b01,0x243185be,0x550c7dc3
+	.long	0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174
+	.long	0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc
+	.long	0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da
+	.long	0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7
+	.long	0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967
+	.long	0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13
+	.long	0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85
+	.long	0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3
+	.long	0xd192e819,0xd6990624,0xf40e3585,0x106aa070
+	.long	0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5
+	.long	0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3
+	.long	0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208
+	.long	0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2
+___
+
+$code =~ s/\`([^\`]*)\`/eval $1/gem;
+print $code;
+close STDOUT;
diff --git a/crypto/sha/asm/sha512-s390x.pl b/crypto/sha/asm/sha512-s390x.pl
new file mode 100644
index 000000000..582d393ce
--- /dev/null
+++ b/crypto/sha/asm/sha512-s390x.pl
@@ -0,0 +1,326 @@
+#! /usr/bin/env perl
+# Copyright 2007-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+
+# ====================================================================
+# Written by Andy Polyakov  for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+
+# SHA256/512 block procedures for s390x.
+
+# April 2007.
+#
+# sha256_block_data_order is reportedly >3 times faster than gcc 3.3
+# generated code (must be a bug in compiler, as improvement is
+# "pathologically" high, in particular in comparison to other SHA
+# modules). But the real twist is that it detects if hardware support
+# for SHA256 is available and in such case utilizes it. Then the
+# performance can reach >6.5x of assembler one for larger chunks.
+#
+# sha512_block_data_order is ~70% faster than gcc 3.3 generated code.
+
+# January 2009.
+#
+# Add support for hardware SHA512 and reschedule instructions to
+# favour dual-issue z10 pipeline. Hardware SHA256/512 is ~4.7x faster
+# than software.
+
+# November 2010.
+#
+# Adapt for -m31 build. If kernel supports what's called "highgprs"
+# feature on Linux [see /proc/cpuinfo], it's possible to use 64-bit
+# instructions and achieve "64-bit" performance even in 31-bit legacy
+# application context. The feature is not specific to any particular
+# processor, as long as it's "z-CPU". Latter implies that the code
+# remains z/Architecture specific. On z990 SHA256 was measured to
+# perform 2.4x and SHA512 - 13x better than code generated by gcc 4.3.
+
+$flavour = shift;
+
+if ($flavour =~ /3[12]/) {
+	$SIZE_T=4;
+	$g="";
+} else {
+	$SIZE_T=8;
+	$g="g";
+}
+
+$t0="%r0";
+$t1="%r1";
+$ctx="%r2";	$t2="%r2";
+$inp="%r3";
+$len="%r4";	# used as index in inner loop
+
+$A="%r5";
+$B="%r6";
+$C="%r7";
+$D="%r8";
+$E="%r9";
+$F="%r10";
+$G="%r11";
+$H="%r12";	@V=($A,$B,$C,$D,$E,$F,$G,$H);
+$tbl="%r13";
+$T1="%r14";
+$sp="%r15";
+
+while (($output=shift) && ($output!~/\w[\w\-]*\.\w+$/)) {}
+open STDOUT,">$output";
+
+if ($output =~ /512/) {
+	$label="512";
+	$SZ=8;
+	$LD="lg";	# load from memory
+	$ST="stg";	# store to memory
+	$ADD="alg";	# add with memory operand
+	$ROT="rllg";	# rotate left
+	$SHR="srlg";	# logical right shift [see even at the end]
+	@Sigma0=(25,30,36);
+	@Sigma1=(23,46,50);
+	@sigma0=(56,63, 7);
+	@sigma1=( 3,45, 6);
+	$rounds=80;
+	$kimdfunc=3;	# 0 means unknown/unsupported/unimplemented/disabled
+} else {
+	$label="256";
+	$SZ=4;
+	$LD="llgf";	# load from memory
+	$ST="st";	# store to memory
+	$ADD="al";	# add with memory operand
+	$ROT="rll";	# rotate left
+	$SHR="srl";	# logical right shift
+	@Sigma0=(10,19,30);
+	@Sigma1=( 7,21,26);
+	@sigma0=(14,25, 3);
+	@sigma1=(13,15,10);
+	$rounds=64;
+	$kimdfunc=2;	# magic function code for kimd instruction
+}
+$Func="sha${label}_block_data_order";
+$Table="K${label}";
+$stdframe=16*$SIZE_T+4*8;
+$frame=$stdframe+16*$SZ;
+
+sub BODY_00_15 {
+my ($i,$a,$b,$c,$d,$e,$f,$g,$h) = @_;
+
+$code.=<<___ if ($i<16);
+	$LD	$T1,`$i*$SZ`($inp)	### $i
+___
+$code.=<<___;
+	$ROT	$t0,$e,$Sigma1[0]
+	$ROT	$t1,$e,$Sigma1[1]
+	 lgr	$t2,$f
+	xgr	$t0,$t1
+	$ROT	$t1,$t1,`$Sigma1[2]-$Sigma1[1]`
+	 xgr	$t2,$g
+	$ST	$T1,`$stdframe+$SZ*($i%16)`($sp)
+	xgr	$t0,$t1			# Sigma1(e)
+	algr	$T1,$h			# T1+=h
+	 ngr	$t2,$e
+	 lgr	$t1,$a
+	algr	$T1,$t0			# T1+=Sigma1(e)
+	$ROT	$h,$a,$Sigma0[0]
+	 xgr	$t2,$g			# Ch(e,f,g)
+	$ADD	$T1,`$i*$SZ`($len,$tbl)	# T1+=K[i]
+	$ROT	$t0,$a,$Sigma0[1]
+	algr	$T1,$t2			# T1+=Ch(e,f,g)
+	 ogr	$t1,$b
+	xgr	$h,$t0
+	 lgr	$t2,$a
+	 ngr	$t1,$c
+	$ROT	$t0,$t0,`$Sigma0[2]-$Sigma0[1]`
+	xgr	$h,$t0			# h=Sigma0(a)
+	 ngr	$t2,$b
+	algr	$h,$T1			# h+=T1
+	 ogr	$t2,$t1			# Maj(a,b,c)
+	algr	$d,$T1			# d+=T1
+	algr	$h,$t2			# h+=Maj(a,b,c)
+___
+}
+
+sub BODY_16_XX {
+my ($i,$a,$b,$c,$d,$e,$f,$g,$h) = @_;
+
+$code.=<<___;
+	$LD	$T1,`$stdframe+$SZ*(($i+1)%16)`($sp)	### $i
+	$LD	$t1,`$stdframe+$SZ*(($i+14)%16)`($sp)
+	$ROT	$t0,$T1,$sigma0[0]
+	$SHR	$T1,$sigma0[2]
+	$ROT	$t2,$t0,`$sigma0[1]-$sigma0[0]`
+	xgr	$T1,$t0
+	$ROT	$t0,$t1,$sigma1[0]
+	xgr	$T1,$t2					# sigma0(X[i+1])
+	$SHR	$t1,$sigma1[2]
+	$ADD	$T1,`$stdframe+$SZ*($i%16)`($sp)	# +=X[i]
+	xgr	$t1,$t0
+	$ROT	$t0,$t0,`$sigma1[1]-$sigma1[0]`
+	$ADD	$T1,`$stdframe+$SZ*(($i+9)%16)`($sp)	# +=X[i+9]
+	xgr	$t1,$t0				# sigma1(X[i+14])
+	algr	$T1,$t1				# +=sigma1(X[i+14])
+___
+	&BODY_00_15(@_);
+}
+
+$code.=<<___;
+.text
+.align	64
+.type	$Table,\@object
+$Table:
+___
+$code.=<<___ if ($SZ==4);
+	.long	0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5
+	.long	0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5
+	.long	0xd807aa98,0x12835b01,0x243185be,0x550c7dc3
+	.long	0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174
+	.long	0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc
+	.long	0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da
+	.long	0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7
+	.long	0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967
+	.long	0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13
+	.long	0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85
+	.long	0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3
+	.long	0xd192e819,0xd6990624,0xf40e3585,0x106aa070
+	.long	0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5
+	.long	0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3
+	.long	0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208
+	.long	0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2
+___
+$code.=<<___ if ($SZ==8);
+	.quad	0x428a2f98d728ae22,0x7137449123ef65cd
+	.quad	0xb5c0fbcfec4d3b2f,0xe9b5dba58189dbbc
+	.quad	0x3956c25bf348b538,0x59f111f1b605d019
+	.quad	0x923f82a4af194f9b,0xab1c5ed5da6d8118
+	.quad	0xd807aa98a3030242,0x12835b0145706fbe
+	.quad	0x243185be4ee4b28c,0x550c7dc3d5ffb4e2
+	.quad	0x72be5d74f27b896f,0x80deb1fe3b1696b1
+	.quad	0x9bdc06a725c71235,0xc19bf174cf692694
+	.quad	0xe49b69c19ef14ad2,0xefbe4786384f25e3
+	.quad	0x0fc19dc68b8cd5b5,0x240ca1cc77ac9c65
+	.quad	0x2de92c6f592b0275,0x4a7484aa6ea6e483
+	.quad	0x5cb0a9dcbd41fbd4,0x76f988da831153b5
+	.quad	0x983e5152ee66dfab,0xa831c66d2db43210
+	.quad	0xb00327c898fb213f,0xbf597fc7beef0ee4
+	.quad	0xc6e00bf33da88fc2,0xd5a79147930aa725
+	.quad	0x06ca6351e003826f,0x142929670a0e6e70
+	.quad	0x27b70a8546d22ffc,0x2e1b21385c26c926
+	.quad	0x4d2c6dfc5ac42aed,0x53380d139d95b3df
+	.quad	0x650a73548baf63de,0x766a0abb3c77b2a8
+	.quad	0x81c2c92e47edaee6,0x92722c851482353b
+	.quad	0xa2bfe8a14cf10364,0xa81a664bbc423001
+	.quad	0xc24b8b70d0f89791,0xc76c51a30654be30
+	.quad	0xd192e819d6ef5218,0xd69906245565a910
+	.quad	0xf40e35855771202a,0x106aa07032bbd1b8
+	.quad	0x19a4c116b8d2d0c8,0x1e376c085141ab53
+	.quad	0x2748774cdf8eeb99,0x34b0bcb5e19b48a8
+	.quad	0x391c0cb3c5c95a63,0x4ed8aa4ae3418acb
+	.quad	0x5b9cca4f7763e373,0x682e6ff3d6b2b8a3
+	.quad	0x748f82ee5defb2fc,0x78a5636f43172f60
+	.quad	0x84c87814a1f0ab72,0x8cc702081a6439ec
+	.quad	0x90befffa23631e28,0xa4506cebde82bde9
+	.quad	0xbef9a3f7b2c67915,0xc67178f2e372532b
+	.quad	0xca273eceea26619c,0xd186b8c721c0c207
+	.quad	0xeada7dd6cde0eb1e,0xf57d4f7fee6ed178
+	.quad	0x06f067aa72176fba,0x0a637dc5a2c898a6
+	.quad	0x113f9804bef90dae,0x1b710b35131c471b
+	.quad	0x28db77f523047d84,0x32caab7b40c72493
+	.quad	0x3c9ebe0a15c9bebc,0x431d67c49c100d4c
+	.quad	0x4cc5d4becb3e42b6,0x597f299cfc657e2a
+	.quad	0x5fcb6fab3ad6faec,0x6c44198c4a475817
+___
+$code.=<<___;
+.size	$Table,.-$Table
+.globl	$Func
+.type	$Func,\@function
+$Func:
+	sllg	$len,$len,`log(16*$SZ)/log(2)`
+___
+$code.=<<___ if ($kimdfunc);
+	larl	%r1,OPENSSL_s390xcap_P
+	lg	%r0,0(%r1)
+	tmhl	%r0,0x4000	# check for message-security assist
+	jz	.Lsoftware
+	lg	%r0,16(%r1)	# check kimd capabilities
+	tmhh	%r0,`0x8000>>$kimdfunc`
+	jz	.Lsoftware
+	lghi	%r0,$kimdfunc
+	lgr	%r1,$ctx
+	lgr	%r2,$inp
+	lgr	%r3,$len
+	.long	0xb93e0002	# kimd %r0,%r2
+	brc	1,.-4		# pay attention to "partial completion"
+	br	%r14
+.align	16
+.Lsoftware:
+___
+$code.=<<___;
+	lghi	%r1,-$frame
+	la	$len,0($len,$inp)
+	stm${g}	$ctx,%r15,`2*$SIZE_T`($sp)
+	lgr	%r0,$sp
+	la	$sp,0(%r1,$sp)
+	st${g}	%r0,0($sp)
+
+	larl	$tbl,$Table
+	$LD	$A,`0*$SZ`($ctx)
+	$LD	$B,`1*$SZ`($ctx)
+	$LD	$C,`2*$SZ`($ctx)
+	$LD	$D,`3*$SZ`($ctx)
+	$LD	$E,`4*$SZ`($ctx)
+	$LD	$F,`5*$SZ`($ctx)
+	$LD	$G,`6*$SZ`($ctx)
+	$LD	$H,`7*$SZ`($ctx)
+
+.Lloop:
+	lghi	$len,0
+___
+for ($i=0;$i<16;$i++)	{ &BODY_00_15($i,@V); unshift(@V,pop(@V)); }
+$code.=".Lrounds_16_xx:\n";
+for (;$i<32;$i++)	{ &BODY_16_XX($i,@V); unshift(@V,pop(@V)); }
+$code.=<<___;
+	aghi	$len,`16*$SZ`
+	lghi	$t0,`($rounds-16)*$SZ`
+	clgr	$len,$t0
+	jne	.Lrounds_16_xx
+
+	l${g}	$ctx,`$frame+2*$SIZE_T`($sp)
+	la	$inp,`16*$SZ`($inp)
+	$ADD	$A,`0*$SZ`($ctx)
+	$ADD	$B,`1*$SZ`($ctx)
+	$ADD	$C,`2*$SZ`($ctx)
+	$ADD	$D,`3*$SZ`($ctx)
+	$ADD	$E,`4*$SZ`($ctx)
+	$ADD	$F,`5*$SZ`($ctx)
+	$ADD	$G,`6*$SZ`($ctx)
+	$ADD	$H,`7*$SZ`($ctx)
+	$ST	$A,`0*$SZ`($ctx)
+	$ST	$B,`1*$SZ`($ctx)
+	$ST	$C,`2*$SZ`($ctx)
+	$ST	$D,`3*$SZ`($ctx)
+	$ST	$E,`4*$SZ`($ctx)
+	$ST	$F,`5*$SZ`($ctx)
+	$ST	$G,`6*$SZ`($ctx)
+	$ST	$H,`7*$SZ`($ctx)
+	cl${g}	$inp,`$frame+4*$SIZE_T`($sp)
+	jne	.Lloop
+
+	lm${g}	%r6,%r15,`$frame+6*$SIZE_T`($sp)	
+	br	%r14
+.size	$Func,.-$Func
+.string	"SHA${label} block transform for s390x, CRYPTOGAMS by "
+.comm	OPENSSL_s390xcap_P,80,8
+___
+
+$code =~ s/\`([^\`]*)\`/eval $1/gem;
+# unlike 32-bit shift 64-bit one takes three arguments
+$code =~ s/(srlg\s+)(%r[0-9]+),/$1$2,$2,/gm;
+
+print $code;
+close STDOUT;
diff --git a/crypto/sha/asm/sha512-sparcv9.pl b/crypto/sha/asm/sha512-sparcv9.pl
new file mode 100644
index 000000000..4a1ce5fe3
--- /dev/null
+++ b/crypto/sha/asm/sha512-sparcv9.pl
@@ -0,0 +1,857 @@
+#! /usr/bin/env perl
+# Copyright 2007-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+
+# ====================================================================
+# Written by Andy Polyakov  for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+#
+# Hardware SPARC T4 support by David S. Miller .
+# ====================================================================
+
+# SHA256 performance improvement over compiler generated code varies
+# from 40% for Sun C [32-bit build] to 70% for gcc [3.3, 64-bit
+# build]. Just like in SHA1 module I aim to ensure scalability on
+# UltraSPARC T1 by packing X[16] to 8 64-bit registers.
+
+# SHA512 on pre-T1 UltraSPARC.
+#
+# Performance is >75% better than 64-bit code generated by Sun C and
+# over 2x than 32-bit code. X[16] resides on stack, but access to it
+# is scheduled for L2 latency and staged through 32 least significant
+# bits of %l0-%l7. The latter is done to achieve 32-/64-bit ABI
+# duality. Nevetheless it's ~40% faster than SHA256, which is pretty
+# good [optimal coefficient is 50%].
+#
+# SHA512 on UltraSPARC T1.
+#
+# It's not any faster than 64-bit code generated by Sun C 5.8. This is
+# because 64-bit code generator has the advantage of using 64-bit
+# loads(*) to access X[16], which I consciously traded for 32-/64-bit
+# ABI duality [as per above]. But it surpasses 32-bit Sun C generated
+# code by 60%, not to mention that it doesn't suffer from severe decay
+# when running 4 times physical cores threads and that it leaves gcc
+# [3.4] behind by over 4x factor! If compared to SHA256, single thread
+# performance is only 10% better, but overall throughput for maximum
+# amount of threads for given CPU exceeds corresponding one of SHA256
+# by 30% [again, optimal coefficient is 50%].
+#
+# (*)	Unlike pre-T1 UltraSPARC loads on T1 are executed strictly
+#	in-order, i.e. load instruction has to complete prior next
+#	instruction in given thread is executed, even if the latter is
+#	not dependent on load result! This means that on T1 two 32-bit
+#	loads are always slower than one 64-bit load. Once again this
+#	is unlike pre-T1 UltraSPARC, where, if scheduled appropriately,
+#	2x32-bit loads can be as fast as 1x64-bit ones.
+#
+# SPARC T4 SHA256/512 hardware achieves 3.17/2.01 cycles per byte,
+# which is 9.3x/11.1x faster than software. Multi-process benchmark
+# saturates at 11.5x single-process result on 8-core processor, or
+# ~11/16GBps per 2.85GHz socket.
+
+$output=pop;
+open STDOUT,">$output";
+
+if ($output =~ /512/) {
+	$label="512";
+	$SZ=8;
+	$LD="ldx";		# load from memory
+	$ST="stx";		# store to memory
+	$SLL="sllx";		# shift left logical
+	$SRL="srlx";		# shift right logical
+	@Sigma0=(28,34,39);
+	@Sigma1=(14,18,41);
+	@sigma0=( 7, 1, 8);	# right shift first
+	@sigma1=( 6,19,61);	# right shift first
+	$lastK=0x817;
+	$rounds=80;
+	$align=4;
+
+	$locals=16*$SZ;		# X[16]
+
+	$A="%o0";
+	$B="%o1";
+	$C="%o2";
+	$D="%o3";
+	$E="%o4";
+	$F="%o5";
+	$G="%g1";
+	$H="%o7";
+	@V=($A,$B,$C,$D,$E,$F,$G,$H);
+} else {
+	$label="256";
+	$SZ=4;
+	$LD="ld";		# load from memory
+	$ST="st";		# store to memory
+	$SLL="sll";		# shift left logical
+	$SRL="srl";		# shift right logical
+	@Sigma0=( 2,13,22);
+	@Sigma1=( 6,11,25);
+	@sigma0=( 3, 7,18);	# right shift first
+	@sigma1=(10,17,19);	# right shift first
+	$lastK=0x8f2;
+	$rounds=64;
+	$align=8;
+
+	$locals=0;		# X[16] is register resident
+	@X=("%o0","%o1","%o2","%o3","%o4","%o5","%g1","%o7");
+	
+	$A="%l0";
+	$B="%l1";
+	$C="%l2";
+	$D="%l3";
+	$E="%l4";
+	$F="%l5";
+	$G="%l6";
+	$H="%l7";
+	@V=($A,$B,$C,$D,$E,$F,$G,$H);
+}
+$T1="%g2";
+$tmp0="%g3";
+$tmp1="%g4";
+$tmp2="%g5";
+
+$ctx="%i0";
+$inp="%i1";
+$len="%i2";
+$Ktbl="%i3";
+$tmp31="%i4";
+$tmp32="%i5";
+
+########### SHA256
+$Xload = sub {
+my ($i,$a,$b,$c,$d,$e,$f,$g,$h)=@_;
+
+    if ($i==0) {
+$code.=<<___;
+	ldx	[$inp+0],@X[0]
+	ldx	[$inp+16],@X[2]
+	ldx	[$inp+32],@X[4]
+	ldx	[$inp+48],@X[6]
+	ldx	[$inp+8],@X[1]
+	ldx	[$inp+24],@X[3]
+	subcc	%g0,$tmp31,$tmp32 ! should be 64-$tmp31, but -$tmp31 works too
+	ldx	[$inp+40],@X[5]
+	bz,pt	%icc,.Laligned
+	ldx	[$inp+56],@X[7]
+
+	sllx	@X[0],$tmp31,@X[0]
+	ldx	[$inp+64],$T1
+___
+for($j=0;$j<7;$j++)
+{   $code.=<<___;
+	srlx	@X[$j+1],$tmp32,$tmp1
+	sllx	@X[$j+1],$tmp31,@X[$j+1]
+	or	$tmp1,@X[$j],@X[$j]
+___
+}
+$code.=<<___;
+	srlx	$T1,$tmp32,$T1
+	or	$T1,@X[7],@X[7]
+.Laligned:
+___
+    }
+
+    if ($i&1) {
+	$code.="\tadd	@X[$i/2],$h,$T1\n";
+    } else {
+	$code.="\tsrlx	@X[$i/2],32,$T1\n\tadd	$h,$T1,$T1\n";
+    }
+} if ($SZ==4);
+
+########### SHA512
+$Xload = sub {
+my ($i,$a,$b,$c,$d,$e,$f,$g,$h)=@_;
+my @pair=("%l".eval(($i*2)%8),"%l".eval(($i*2)%8+1),"%l".eval((($i+1)*2)%8));
+
+$code.=<<___ if ($i==0);
+	ld	[$inp+0],%l0
+	ld	[$inp+4],%l1
+	ld	[$inp+8],%l2
+	ld	[$inp+12],%l3
+	ld	[$inp+16],%l4
+	ld	[$inp+20],%l5
+	ld	[$inp+24],%l6
+	cmp	$tmp31,0
+	ld	[$inp+28],%l7
+___
+$code.=<<___ if ($i<15);
+	sllx	@pair[1],$tmp31,$tmp2	! Xload($i)
+	add	$tmp31,32,$tmp0
+	sllx	@pair[0],$tmp0,$tmp1
+	`"ld	[$inp+".eval(32+0+$i*8)."],@pair[0]"	if ($i<12)`
+	srlx	@pair[2],$tmp32,@pair[1]
+	or	$tmp1,$tmp2,$tmp2
+	or	@pair[1],$tmp2,$tmp2
+	`"ld	[$inp+".eval(32+4+$i*8)."],@pair[1]"	if ($i<12)`
+	add	$h,$tmp2,$T1
+	$ST	$tmp2,[%sp+STACK_BIAS+STACK_FRAME+`$i*$SZ`]
+___
+$code.=<<___ if ($i==12);
+	bnz,a,pn	%icc,.+8
+	ld	[$inp+128],%l0
+___
+$code.=<<___ if ($i==15);
+	ld	[%sp+STACK_BIAS+STACK_FRAME+`(($i+1+1)%16)*$SZ+0`],%l2
+	sllx	@pair[1],$tmp31,$tmp2	! Xload($i)
+	add	$tmp31,32,$tmp0
+	ld	[%sp+STACK_BIAS+STACK_FRAME+`(($i+1+1)%16)*$SZ+4`],%l3
+	sllx	@pair[0],$tmp0,$tmp1
+	ld	[%sp+STACK_BIAS+STACK_FRAME+`(($i+1+9)%16)*$SZ+0`],%l4
+	srlx	@pair[2],$tmp32,@pair[1]
+	or	$tmp1,$tmp2,$tmp2
+	ld	[%sp+STACK_BIAS+STACK_FRAME+`(($i+1+9)%16)*$SZ+4`],%l5
+	or	@pair[1],$tmp2,$tmp2
+	ld	[%sp+STACK_BIAS+STACK_FRAME+`(($i+1+14)%16)*$SZ+0`],%l6
+	add	$h,$tmp2,$T1
+	$ST	$tmp2,[%sp+STACK_BIAS+STACK_FRAME+`$i*$SZ`]
+	ld	[%sp+STACK_BIAS+STACK_FRAME+`(($i+1+14)%16)*$SZ+4`],%l7
+	ld	[%sp+STACK_BIAS+STACK_FRAME+`(($i+1+0)%16)*$SZ+0`],%l0
+	ld	[%sp+STACK_BIAS+STACK_FRAME+`(($i+1+0)%16)*$SZ+4`],%l1
+___
+} if ($SZ==8);
+
+########### common
+sub BODY_00_15 {
+my ($i,$a,$b,$c,$d,$e,$f,$g,$h)=@_;
+
+    if ($i<16) {
+	&$Xload(@_);
+    } else {
+	$code.="\tadd	$h,$T1,$T1\n";
+    }
+
+$code.=<<___;
+	$SRL	$e,@Sigma1[0],$h	!! $i
+	xor	$f,$g,$tmp2
+	$SLL	$e,`$SZ*8-@Sigma1[2]`,$tmp1
+	and	$e,$tmp2,$tmp2
+	$SRL	$e,@Sigma1[1],$tmp0
+	xor	$tmp1,$h,$h
+	$SLL	$e,`$SZ*8-@Sigma1[1]`,$tmp1
+	xor	$tmp0,$h,$h
+	$SRL	$e,@Sigma1[2],$tmp0
+	xor	$tmp1,$h,$h
+	$SLL	$e,`$SZ*8-@Sigma1[0]`,$tmp1
+	xor	$tmp0,$h,$h
+	xor	$g,$tmp2,$tmp2		! Ch(e,f,g)
+	xor	$tmp1,$h,$tmp0		! Sigma1(e)
+
+	$SRL	$a,@Sigma0[0],$h
+	add	$tmp2,$T1,$T1
+	$LD	[$Ktbl+`$i*$SZ`],$tmp2	! K[$i]
+	$SLL	$a,`$SZ*8-@Sigma0[2]`,$tmp1
+	add	$tmp0,$T1,$T1
+	$SRL	$a,@Sigma0[1],$tmp0
+	xor	$tmp1,$h,$h
+	$SLL	$a,`$SZ*8-@Sigma0[1]`,$tmp1
+	xor	$tmp0,$h,$h
+	$SRL	$a,@Sigma0[2],$tmp0
+	xor	$tmp1,$h,$h	
+	$SLL	$a,`$SZ*8-@Sigma0[0]`,$tmp1
+	xor	$tmp0,$h,$h
+	xor	$tmp1,$h,$h		! Sigma0(a)
+
+	or	$a,$b,$tmp0
+	and	$a,$b,$tmp1
+	and	$c,$tmp0,$tmp0
+	or	$tmp0,$tmp1,$tmp1	! Maj(a,b,c)
+	add	$tmp2,$T1,$T1		! +=K[$i]
+	add	$tmp1,$h,$h
+
+	add	$T1,$d,$d
+	add	$T1,$h,$h
+___
+}
+
+########### SHA256
+$BODY_16_XX = sub {
+my $i=@_[0];
+my $xi;
+
+    if ($i&1) {
+	$xi=$tmp32;
+	$code.="\tsrlx	@X[(($i+1)/2)%8],32,$xi\n";
+    } else {
+	$xi=@X[(($i+1)/2)%8];
+    }
+$code.=<<___;
+	srl	$xi,@sigma0[0],$T1		!! Xupdate($i)
+	sll	$xi,`32-@sigma0[2]`,$tmp1
+	srl	$xi,@sigma0[1],$tmp0
+	xor	$tmp1,$T1,$T1
+	sll	$tmp1,`@sigma0[2]-@sigma0[1]`,$tmp1
+	xor	$tmp0,$T1,$T1
+	srl	$xi,@sigma0[2],$tmp0
+	xor	$tmp1,$T1,$T1
+___
+    if ($i&1) {
+	$xi=@X[(($i+14)/2)%8];
+    } else {
+	$xi=$tmp32;
+	$code.="\tsrlx	@X[(($i+14)/2)%8],32,$xi\n";
+    }
+$code.=<<___;
+	srl	$xi,@sigma1[0],$tmp2
+	xor	$tmp0,$T1,$T1			! T1=sigma0(X[i+1])
+	sll	$xi,`32-@sigma1[2]`,$tmp1
+	srl	$xi,@sigma1[1],$tmp0
+	xor	$tmp1,$tmp2,$tmp2
+	sll	$tmp1,`@sigma1[2]-@sigma1[1]`,$tmp1
+	xor	$tmp0,$tmp2,$tmp2
+	srl	$xi,@sigma1[2],$tmp0
+	xor	$tmp1,$tmp2,$tmp2
+___
+    if ($i&1) {
+	$xi=@X[($i/2)%8];
+$code.=<<___;
+	srlx	@X[(($i+9)/2)%8],32,$tmp1	! X[i+9]
+	xor	$tmp0,$tmp2,$tmp2		! sigma1(X[i+14])
+	srl	@X[($i/2)%8],0,$tmp0
+	add	$tmp2,$tmp1,$tmp1
+	add	$xi,$T1,$T1			! +=X[i]
+	xor	$tmp0,@X[($i/2)%8],@X[($i/2)%8]
+	add	$tmp1,$T1,$T1
+
+	srl	$T1,0,$T1
+	or	$T1,@X[($i/2)%8],@X[($i/2)%8]
+___
+    } else {
+	$xi=@X[(($i+9)/2)%8];
+$code.=<<___;
+	srlx	@X[($i/2)%8],32,$tmp1		! X[i]
+	xor	$tmp0,$tmp2,$tmp2		! sigma1(X[i+14])
+	add	$xi,$T1,$T1			! +=X[i+9]
+	add	$tmp2,$tmp1,$tmp1
+	srl	@X[($i/2)%8],0,@X[($i/2)%8]
+	add	$tmp1,$T1,$T1
+
+	sllx	$T1,32,$tmp0
+	or	$tmp0,@X[($i/2)%8],@X[($i/2)%8]
+___
+    }
+    &BODY_00_15(@_);
+} if ($SZ==4);
+
+########### SHA512
+$BODY_16_XX = sub {
+my $i=@_[0];
+my @pair=("%l".eval(($i*2)%8),"%l".eval(($i*2)%8+1));
+
+$code.=<<___;
+	sllx	%l2,32,$tmp0		!! Xupdate($i)
+	or	%l3,$tmp0,$tmp0
+
+	srlx	$tmp0,@sigma0[0],$T1
+	ld	[%sp+STACK_BIAS+STACK_FRAME+`(($i+1+1)%16)*$SZ+0`],%l2
+	sllx	$tmp0,`64-@sigma0[2]`,$tmp1
+	ld	[%sp+STACK_BIAS+STACK_FRAME+`(($i+1+1)%16)*$SZ+4`],%l3
+	srlx	$tmp0,@sigma0[1],$tmp0
+	xor	$tmp1,$T1,$T1
+	sllx	$tmp1,`@sigma0[2]-@sigma0[1]`,$tmp1
+	xor	$tmp0,$T1,$T1
+	srlx	$tmp0,`@sigma0[2]-@sigma0[1]`,$tmp0
+	xor	$tmp1,$T1,$T1
+	sllx	%l6,32,$tmp2
+	xor	$tmp0,$T1,$T1		! sigma0(X[$i+1])
+	or	%l7,$tmp2,$tmp2
+
+	srlx	$tmp2,@sigma1[0],$tmp1
+	ld	[%sp+STACK_BIAS+STACK_FRAME+`(($i+1+14)%16)*$SZ+0`],%l6
+	sllx	$tmp2,`64-@sigma1[2]`,$tmp0
+	ld	[%sp+STACK_BIAS+STACK_FRAME+`(($i+1+14)%16)*$SZ+4`],%l7
+	srlx	$tmp2,@sigma1[1],$tmp2
+	xor	$tmp0,$tmp1,$tmp1
+	sllx	$tmp0,`@sigma1[2]-@sigma1[1]`,$tmp0
+	xor	$tmp2,$tmp1,$tmp1
+	srlx	$tmp2,`@sigma1[2]-@sigma1[1]`,$tmp2
+	xor	$tmp0,$tmp1,$tmp1
+	sllx	%l4,32,$tmp0
+	xor	$tmp2,$tmp1,$tmp1	! sigma1(X[$i+14])
+	ld	[%sp+STACK_BIAS+STACK_FRAME+`(($i+1+9)%16)*$SZ+0`],%l4
+	or	%l5,$tmp0,$tmp0
+	ld	[%sp+STACK_BIAS+STACK_FRAME+`(($i+1+9)%16)*$SZ+4`],%l5
+
+	sllx	%l0,32,$tmp2
+	add	$tmp1,$T1,$T1
+	ld	[%sp+STACK_BIAS+STACK_FRAME+`(($i+1+0)%16)*$SZ+0`],%l0
+	or	%l1,$tmp2,$tmp2
+	add	$tmp0,$T1,$T1		! +=X[$i+9]
+	ld	[%sp+STACK_BIAS+STACK_FRAME+`(($i+1+0)%16)*$SZ+4`],%l1
+	add	$tmp2,$T1,$T1		! +=X[$i]
+	$ST	$T1,[%sp+STACK_BIAS+STACK_FRAME+`($i%16)*$SZ`]
+___
+    &BODY_00_15(@_);
+} if ($SZ==8);
+
+$code.=<<___;
+#include "sparc_arch.h"
+
+#ifdef __arch64__
+.register	%g2,#scratch
+.register	%g3,#scratch
+#endif
+
+.section	".text",#alloc,#execinstr
+
+.align	64
+K${label}:
+.type	K${label},#object
+___
+if ($SZ==4) {
+$code.=<<___;
+	.long	0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5
+	.long	0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5
+	.long	0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3
+	.long	0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174
+	.long	0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc
+	.long	0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da
+	.long	0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7
+	.long	0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967
+	.long	0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13
+	.long	0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85
+	.long	0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3
+	.long	0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070
+	.long	0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5
+	.long	0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3
+	.long	0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208
+	.long	0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
+___
+} else {
+$code.=<<___;
+	.long	0x428a2f98,0xd728ae22, 0x71374491,0x23ef65cd
+	.long	0xb5c0fbcf,0xec4d3b2f, 0xe9b5dba5,0x8189dbbc
+	.long	0x3956c25b,0xf348b538, 0x59f111f1,0xb605d019
+	.long	0x923f82a4,0xaf194f9b, 0xab1c5ed5,0xda6d8118
+	.long	0xd807aa98,0xa3030242, 0x12835b01,0x45706fbe
+	.long	0x243185be,0x4ee4b28c, 0x550c7dc3,0xd5ffb4e2
+	.long	0x72be5d74,0xf27b896f, 0x80deb1fe,0x3b1696b1
+	.long	0x9bdc06a7,0x25c71235, 0xc19bf174,0xcf692694
+	.long	0xe49b69c1,0x9ef14ad2, 0xefbe4786,0x384f25e3
+	.long	0x0fc19dc6,0x8b8cd5b5, 0x240ca1cc,0x77ac9c65
+	.long	0x2de92c6f,0x592b0275, 0x4a7484aa,0x6ea6e483
+	.long	0x5cb0a9dc,0xbd41fbd4, 0x76f988da,0x831153b5
+	.long	0x983e5152,0xee66dfab, 0xa831c66d,0x2db43210
+	.long	0xb00327c8,0x98fb213f, 0xbf597fc7,0xbeef0ee4
+	.long	0xc6e00bf3,0x3da88fc2, 0xd5a79147,0x930aa725
+	.long	0x06ca6351,0xe003826f, 0x14292967,0x0a0e6e70
+	.long	0x27b70a85,0x46d22ffc, 0x2e1b2138,0x5c26c926
+	.long	0x4d2c6dfc,0x5ac42aed, 0x53380d13,0x9d95b3df
+	.long	0x650a7354,0x8baf63de, 0x766a0abb,0x3c77b2a8
+	.long	0x81c2c92e,0x47edaee6, 0x92722c85,0x1482353b
+	.long	0xa2bfe8a1,0x4cf10364, 0xa81a664b,0xbc423001
+	.long	0xc24b8b70,0xd0f89791, 0xc76c51a3,0x0654be30
+	.long	0xd192e819,0xd6ef5218, 0xd6990624,0x5565a910
+	.long	0xf40e3585,0x5771202a, 0x106aa070,0x32bbd1b8
+	.long	0x19a4c116,0xb8d2d0c8, 0x1e376c08,0x5141ab53
+	.long	0x2748774c,0xdf8eeb99, 0x34b0bcb5,0xe19b48a8
+	.long	0x391c0cb3,0xc5c95a63, 0x4ed8aa4a,0xe3418acb
+	.long	0x5b9cca4f,0x7763e373, 0x682e6ff3,0xd6b2b8a3
+	.long	0x748f82ee,0x5defb2fc, 0x78a5636f,0x43172f60
+	.long	0x84c87814,0xa1f0ab72, 0x8cc70208,0x1a6439ec
+	.long	0x90befffa,0x23631e28, 0xa4506ceb,0xde82bde9
+	.long	0xbef9a3f7,0xb2c67915, 0xc67178f2,0xe372532b
+	.long	0xca273ece,0xea26619c, 0xd186b8c7,0x21c0c207
+	.long	0xeada7dd6,0xcde0eb1e, 0xf57d4f7f,0xee6ed178
+	.long	0x06f067aa,0x72176fba, 0x0a637dc5,0xa2c898a6
+	.long	0x113f9804,0xbef90dae, 0x1b710b35,0x131c471b
+	.long	0x28db77f5,0x23047d84, 0x32caab7b,0x40c72493
+	.long	0x3c9ebe0a,0x15c9bebc, 0x431d67c4,0x9c100d4c
+	.long	0x4cc5d4be,0xcb3e42b6, 0x597f299c,0xfc657e2a
+	.long	0x5fcb6fab,0x3ad6faec, 0x6c44198c,0x4a475817
+___
+}
+$code.=<<___;
+.size	K${label},.-K${label}
+
+#ifdef __PIC__
+SPARC_PIC_THUNK(%g1)
+#endif
+
+.globl	sha${label}_block_data_order
+.align	32
+sha${label}_block_data_order:
+	SPARC_LOAD_ADDRESS_LEAF(OPENSSL_sparcv9cap_P,%g1,%g5)
+	ld	[%g1+4],%g1		! OPENSSL_sparcv9cap_P[1]
+
+	andcc	%g1, CFR_SHA${label}, %g0
+	be	.Lsoftware
+	nop
+___
+$code.=<<___ if ($SZ==8); 		# SHA512
+	ldd	[%o0 + 0x00], %f0	! load context
+	ldd	[%o0 + 0x08], %f2
+	ldd	[%o0 + 0x10], %f4
+	ldd	[%o0 + 0x18], %f6
+	ldd	[%o0 + 0x20], %f8
+	ldd	[%o0 + 0x28], %f10
+	andcc	%o1, 0x7, %g0
+	ldd	[%o0 + 0x30], %f12
+	bne,pn	%icc, .Lhwunaligned
+	 ldd	[%o0 + 0x38], %f14
+
+.Lhwaligned_loop:
+	ldd	[%o1 + 0x00], %f16
+	ldd	[%o1 + 0x08], %f18
+	ldd	[%o1 + 0x10], %f20
+	ldd	[%o1 + 0x18], %f22
+	ldd	[%o1 + 0x20], %f24
+	ldd	[%o1 + 0x28], %f26
+	ldd	[%o1 + 0x30], %f28
+	ldd	[%o1 + 0x38], %f30
+	ldd	[%o1 + 0x40], %f32
+	ldd	[%o1 + 0x48], %f34
+	ldd	[%o1 + 0x50], %f36
+	ldd	[%o1 + 0x58], %f38
+	ldd	[%o1 + 0x60], %f40
+	ldd	[%o1 + 0x68], %f42
+	ldd	[%o1 + 0x70], %f44
+	subcc	%o2, 1, %o2		! done yet?
+	ldd	[%o1 + 0x78], %f46
+	add	%o1, 0x80, %o1
+	prefetch [%o1 + 63], 20
+	prefetch [%o1 + 64+63], 20
+
+	.word	0x81b02860		! SHA512
+
+	bne,pt	SIZE_T_CC, .Lhwaligned_loop
+	nop
+
+.Lhwfinish:
+	std	%f0, [%o0 + 0x00]	! store context
+	std	%f2, [%o0 + 0x08]
+	std	%f4, [%o0 + 0x10]
+	std	%f6, [%o0 + 0x18]
+	std	%f8, [%o0 + 0x20]
+	std	%f10, [%o0 + 0x28]
+	std	%f12, [%o0 + 0x30]
+	retl
+	 std	%f14, [%o0 + 0x38]
+
+.align	16
+.Lhwunaligned:
+	alignaddr %o1, %g0, %o1
+
+	ldd	[%o1 + 0x00], %f18
+.Lhwunaligned_loop:
+	ldd	[%o1 + 0x08], %f20
+	ldd	[%o1 + 0x10], %f22
+	ldd	[%o1 + 0x18], %f24
+	ldd	[%o1 + 0x20], %f26
+	ldd	[%o1 + 0x28], %f28
+	ldd	[%o1 + 0x30], %f30
+	ldd	[%o1 + 0x38], %f32
+	ldd	[%o1 + 0x40], %f34
+	ldd	[%o1 + 0x48], %f36
+	ldd	[%o1 + 0x50], %f38
+	ldd	[%o1 + 0x58], %f40
+	ldd	[%o1 + 0x60], %f42
+	ldd	[%o1 + 0x68], %f44
+	ldd	[%o1 + 0x70], %f46
+	ldd	[%o1 + 0x78], %f48
+	subcc	%o2, 1, %o2		! done yet?
+	ldd	[%o1 + 0x80], %f50
+	add	%o1, 0x80, %o1
+	prefetch [%o1 + 63], 20
+	prefetch [%o1 + 64+63], 20
+
+	faligndata %f18, %f20, %f16
+	faligndata %f20, %f22, %f18
+	faligndata %f22, %f24, %f20
+	faligndata %f24, %f26, %f22
+	faligndata %f26, %f28, %f24
+	faligndata %f28, %f30, %f26
+	faligndata %f30, %f32, %f28
+	faligndata %f32, %f34, %f30
+	faligndata %f34, %f36, %f32
+	faligndata %f36, %f38, %f34
+	faligndata %f38, %f40, %f36
+	faligndata %f40, %f42, %f38
+	faligndata %f42, %f44, %f40
+	faligndata %f44, %f46, %f42
+	faligndata %f46, %f48, %f44
+	faligndata %f48, %f50, %f46
+
+	.word	0x81b02860		! SHA512
+
+	bne,pt	SIZE_T_CC, .Lhwunaligned_loop
+	for	%f50, %f50, %f18	! %f18=%f50
+
+	ba	.Lhwfinish
+	nop
+___
+$code.=<<___ if ($SZ==4); 		# SHA256
+	ld	[%o0 + 0x00], %f0
+	ld	[%o0 + 0x04], %f1
+	ld	[%o0 + 0x08], %f2
+	ld	[%o0 + 0x0c], %f3
+	ld	[%o0 + 0x10], %f4
+	ld	[%o0 + 0x14], %f5
+	andcc	%o1, 0x7, %g0
+	ld	[%o0 + 0x18], %f6
+	bne,pn	%icc, .Lhwunaligned
+	 ld	[%o0 + 0x1c], %f7
+
+.Lhwloop:
+	ldd	[%o1 + 0x00], %f8
+	ldd	[%o1 + 0x08], %f10
+	ldd	[%o1 + 0x10], %f12
+	ldd	[%o1 + 0x18], %f14
+	ldd	[%o1 + 0x20], %f16
+	ldd	[%o1 + 0x28], %f18
+	ldd	[%o1 + 0x30], %f20
+	subcc	%o2, 1, %o2		! done yet?
+	ldd	[%o1 + 0x38], %f22
+	add	%o1, 0x40, %o1
+	prefetch [%o1 + 63], 20
+
+	.word	0x81b02840		! SHA256
+
+	bne,pt	SIZE_T_CC, .Lhwloop
+	nop
+
+.Lhwfinish:
+	st	%f0, [%o0 + 0x00]	! store context
+	st	%f1, [%o0 + 0x04]
+	st	%f2, [%o0 + 0x08]
+	st	%f3, [%o0 + 0x0c]
+	st	%f4, [%o0 + 0x10]
+	st	%f5, [%o0 + 0x14]
+	st	%f6, [%o0 + 0x18]
+	retl
+	 st	%f7, [%o0 + 0x1c]
+
+.align	8
+.Lhwunaligned:
+	alignaddr %o1, %g0, %o1
+
+	ldd	[%o1 + 0x00], %f10
+.Lhwunaligned_loop:
+	ldd	[%o1 + 0x08], %f12
+	ldd	[%o1 + 0x10], %f14
+	ldd	[%o1 + 0x18], %f16
+	ldd	[%o1 + 0x20], %f18
+	ldd	[%o1 + 0x28], %f20
+	ldd	[%o1 + 0x30], %f22
+	ldd	[%o1 + 0x38], %f24
+	subcc	%o2, 1, %o2		! done yet?
+	ldd	[%o1 + 0x40], %f26
+	add	%o1, 0x40, %o1
+	prefetch [%o1 + 63], 20
+
+	faligndata %f10, %f12, %f8
+	faligndata %f12, %f14, %f10
+	faligndata %f14, %f16, %f12
+	faligndata %f16, %f18, %f14
+	faligndata %f18, %f20, %f16
+	faligndata %f20, %f22, %f18
+	faligndata %f22, %f24, %f20
+	faligndata %f24, %f26, %f22
+
+	.word	0x81b02840		! SHA256
+
+	bne,pt	SIZE_T_CC, .Lhwunaligned_loop
+	for	%f26, %f26, %f10	! %f10=%f26
+
+	ba	.Lhwfinish
+	nop
+___
+$code.=<<___;
+.align	16
+.Lsoftware:
+	save	%sp,-STACK_FRAME-$locals,%sp
+	and	$inp,`$align-1`,$tmp31
+	sllx	$len,`log(16*$SZ)/log(2)`,$len
+	andn	$inp,`$align-1`,$inp
+	sll	$tmp31,3,$tmp31
+	add	$inp,$len,$len
+___
+$code.=<<___ if ($SZ==8); # SHA512
+	mov	32,$tmp32
+	sub	$tmp32,$tmp31,$tmp32
+___
+$code.=<<___;
+.Lpic:	call	.+8
+	add	%o7,K${label}-.Lpic,$Ktbl
+
+	$LD	[$ctx+`0*$SZ`],$A
+	$LD	[$ctx+`1*$SZ`],$B
+	$LD	[$ctx+`2*$SZ`],$C
+	$LD	[$ctx+`3*$SZ`],$D
+	$LD	[$ctx+`4*$SZ`],$E
+	$LD	[$ctx+`5*$SZ`],$F
+	$LD	[$ctx+`6*$SZ`],$G
+	$LD	[$ctx+`7*$SZ`],$H
+
+.Lloop:
+___
+for ($i=0;$i<16;$i++)	{ &BODY_00_15($i,@V); unshift(@V,pop(@V)); }
+$code.=".L16_xx:\n";
+for (;$i<32;$i++)	{ &$BODY_16_XX($i,@V); unshift(@V,pop(@V)); }
+$code.=<<___;
+	and	$tmp2,0xfff,$tmp2
+	cmp	$tmp2,$lastK
+	bne	.L16_xx
+	add	$Ktbl,`16*$SZ`,$Ktbl	! Ktbl+=16
+
+___
+$code.=<<___ if ($SZ==4); # SHA256
+	$LD	[$ctx+`0*$SZ`],@X[0]
+	$LD	[$ctx+`1*$SZ`],@X[1]
+	$LD	[$ctx+`2*$SZ`],@X[2]
+	$LD	[$ctx+`3*$SZ`],@X[3]
+	$LD	[$ctx+`4*$SZ`],@X[4]
+	$LD	[$ctx+`5*$SZ`],@X[5]
+	$LD	[$ctx+`6*$SZ`],@X[6]
+	$LD	[$ctx+`7*$SZ`],@X[7]
+
+	add	$A,@X[0],$A
+	$ST	$A,[$ctx+`0*$SZ`]
+	add	$B,@X[1],$B
+	$ST	$B,[$ctx+`1*$SZ`]
+	add	$C,@X[2],$C
+	$ST	$C,[$ctx+`2*$SZ`]
+	add	$D,@X[3],$D
+	$ST	$D,[$ctx+`3*$SZ`]
+	add	$E,@X[4],$E
+	$ST	$E,[$ctx+`4*$SZ`]
+	add	$F,@X[5],$F
+	$ST	$F,[$ctx+`5*$SZ`]
+	add	$G,@X[6],$G
+	$ST	$G,[$ctx+`6*$SZ`]
+	add	$H,@X[7],$H
+	$ST	$H,[$ctx+`7*$SZ`]
+___
+$code.=<<___ if ($SZ==8); # SHA512
+	ld	[$ctx+`0*$SZ+0`],%l0
+	ld	[$ctx+`0*$SZ+4`],%l1
+	ld	[$ctx+`1*$SZ+0`],%l2
+	ld	[$ctx+`1*$SZ+4`],%l3
+	ld	[$ctx+`2*$SZ+0`],%l4
+	ld	[$ctx+`2*$SZ+4`],%l5
+	ld	[$ctx+`3*$SZ+0`],%l6
+
+	sllx	%l0,32,$tmp0
+	ld	[$ctx+`3*$SZ+4`],%l7
+	sllx	%l2,32,$tmp1
+	or	%l1,$tmp0,$tmp0
+	or	%l3,$tmp1,$tmp1
+	add	$tmp0,$A,$A
+	add	$tmp1,$B,$B
+	$ST	$A,[$ctx+`0*$SZ`]
+	sllx	%l4,32,$tmp2
+	$ST	$B,[$ctx+`1*$SZ`]
+	sllx	%l6,32,$T1
+	or	%l5,$tmp2,$tmp2
+	or	%l7,$T1,$T1
+	add	$tmp2,$C,$C
+	$ST	$C,[$ctx+`2*$SZ`]
+	add	$T1,$D,$D
+	$ST	$D,[$ctx+`3*$SZ`]
+
+	ld	[$ctx+`4*$SZ+0`],%l0
+	ld	[$ctx+`4*$SZ+4`],%l1
+	ld	[$ctx+`5*$SZ+0`],%l2
+	ld	[$ctx+`5*$SZ+4`],%l3
+	ld	[$ctx+`6*$SZ+0`],%l4
+	ld	[$ctx+`6*$SZ+4`],%l5
+	ld	[$ctx+`7*$SZ+0`],%l6
+
+	sllx	%l0,32,$tmp0
+	ld	[$ctx+`7*$SZ+4`],%l7
+	sllx	%l2,32,$tmp1
+	or	%l1,$tmp0,$tmp0
+	or	%l3,$tmp1,$tmp1
+	add	$tmp0,$E,$E
+	add	$tmp1,$F,$F
+	$ST	$E,[$ctx+`4*$SZ`]
+	sllx	%l4,32,$tmp2
+	$ST	$F,[$ctx+`5*$SZ`]
+	sllx	%l6,32,$T1
+	or	%l5,$tmp2,$tmp2
+	or	%l7,$T1,$T1
+	add	$tmp2,$G,$G
+	$ST	$G,[$ctx+`6*$SZ`]
+	add	$T1,$H,$H
+	$ST	$H,[$ctx+`7*$SZ`]
+___
+$code.=<<___;
+	add	$inp,`16*$SZ`,$inp		! advance inp
+	cmp	$inp,$len
+	bne	SIZE_T_CC,.Lloop
+	sub	$Ktbl,`($rounds-16)*$SZ`,$Ktbl	! rewind Ktbl
+
+	ret
+	restore
+.type	sha${label}_block_data_order,#function
+.size	sha${label}_block_data_order,(.-sha${label}_block_data_order)
+.asciz	"SHA${label} block transform for SPARCv9, CRYPTOGAMS by "
+.align	4
+___
+
+# Purpose of these subroutines is to explicitly encode VIS instructions,
+# so that one can compile the module without having to specify VIS
+# extensions on compiler command line, e.g. -xarch=v9 vs. -xarch=v9a.
+# Idea is to reserve for option to produce "universal" binary and let
+# programmer detect if current CPU is VIS capable at run-time.
+sub unvis {
+my ($mnemonic,$rs1,$rs2,$rd)=@_;
+my $ref,$opf;
+my %visopf = (	"faligndata"	=> 0x048,
+		"for"		=> 0x07c	);
+
+    $ref = "$mnemonic\t$rs1,$rs2,$rd";
+
+    if ($opf=$visopf{$mnemonic}) {
+	foreach ($rs1,$rs2,$rd) {
+	    return $ref if (!/%f([0-9]{1,2})/);
+	    $_=$1;
+	    if ($1>=32) {
+		return $ref if ($1&1);
+		# re-encode for upper double register addressing
+		$_=($1|$1>>5)&31;
+	    }
+	}
+
+	return	sprintf ".word\t0x%08x !%s",
+			0x81b00000|$rd<<25|$rs1<<14|$opf<<5|$rs2,
+			$ref;
+    } else {
+	return $ref;
+    }
+}
+sub unalignaddr {
+my ($mnemonic,$rs1,$rs2,$rd)=@_;
+my %bias = ( "g" => 0, "o" => 8, "l" => 16, "i" => 24 );
+my $ref="$mnemonic\t$rs1,$rs2,$rd";
+
+    foreach ($rs1,$rs2,$rd) {
+	if (/%([goli])([0-7])/)	{ $_=$bias{$1}+$2; }
+	else			{ return $ref; }
+    }
+    return  sprintf ".word\t0x%08x !%s",
+		    0x81b00300|$rd<<25|$rs1<<14|$rs2,
+		    $ref;
+}
+
+foreach (split("\n",$code)) {
+	s/\`([^\`]*)\`/eval $1/ge;
+
+	s/\b(f[^\s]*)\s+(%f[0-9]{1,2}),\s*(%f[0-9]{1,2}),\s*(%f[0-9]{1,2})/
+		&unvis($1,$2,$3,$4)
+	 /ge;
+	s/\b(alignaddr)\s+(%[goli][0-7]),\s*(%[goli][0-7]),\s*(%[goli][0-7])/
+		&unalignaddr($1,$2,$3,$4)
+	 /ge;
+
+	print $_,"\n";
+}
+
+close STDOUT;
diff --git a/crypto/sha/asm/sha512-x86_64.pl b/crypto/sha/asm/sha512-x86_64.pl
new file mode 100755
index 000000000..c9b7b2812
--- /dev/null
+++ b/crypto/sha/asm/sha512-x86_64.pl
@@ -0,0 +1,2407 @@
+#! /usr/bin/env perl
+# Copyright 2005-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+#
+# ====================================================================
+# Written by Andy Polyakov  for the OpenSSL
+# project. Rights for redistribution and usage in source and binary
+# forms are granted according to the OpenSSL license.
+# ====================================================================
+#
+# sha256/512_block procedure for x86_64.
+#
+# 40% improvement over compiler-generated code on Opteron. On EM64T
+# sha256 was observed to run >80% faster and sha512 - >40%. No magical
+# tricks, just straight implementation... I really wonder why gcc
+# [being armed with inline assembler] fails to generate as fast code.
+# The only thing which is cool about this module is that it's very
+# same instruction sequence used for both SHA-256 and SHA-512. In
+# former case the instructions operate on 32-bit operands, while in
+# latter - on 64-bit ones. All I had to do is to get one flavor right,
+# the other one passed the test right away:-)
+#
+# sha256_block runs in ~1005 cycles on Opteron, which gives you
+# asymptotic performance of 64*1000/1005=63.7MBps times CPU clock
+# frequency in GHz. sha512_block runs in ~1275 cycles, which results
+# in 128*1000/1275=100MBps per GHz. Is there room for improvement?
+# Well, if you compare it to IA-64 implementation, which maintains
+# X[16] in register bank[!], tends to 4 instructions per CPU clock
+# cycle and runs in 1003 cycles, 1275 is very good result for 3-way
+# issue Opteron pipeline and X[16] maintained in memory. So that *if*
+# there is a way to improve it, *then* the only way would be to try to
+# offload X[16] updates to SSE unit, but that would require "deeper"
+# loop unroll, which in turn would naturally cause size blow-up, not
+# to mention increased complexity! And once again, only *if* it's
+# actually possible to noticeably improve overall ILP, instruction
+# level parallelism, on a given CPU implementation in this case.
+#
+# Special note on Intel EM64T. While Opteron CPU exhibits perfect
+# performance ratio of 1.5 between 64- and 32-bit flavors [see above],
+# [currently available] EM64T CPUs apparently are far from it. On the
+# contrary, 64-bit version, sha512_block, is ~30% *slower* than 32-bit
+# sha256_block:-( This is presumably because 64-bit shifts/rotates
+# apparently are not atomic instructions, but implemented in microcode.
+#
+# May 2012.
+#
+# Optimization including one of Pavel Semjanov's ideas, alternative
+# Maj, resulted in >=5% improvement on most CPUs, +20% SHA256 and
+# unfortunately -2% SHA512 on P4 [which nobody should care about
+# that much].
+#
+# June 2012.
+#
+# Add SIMD code paths, see below for improvement coefficients. SSSE3
+# code path was not attempted for SHA512, because improvement is not
+# estimated to be high enough, noticeably less than 9%, to justify
+# the effort, not on pre-AVX processors. [Obviously with exclusion
+# for VIA Nano, but it has SHA512 instruction that is faster and
+# should be used instead.] For reference, corresponding estimated
+# upper limit for improvement for SSSE3 SHA256 is 28%. The fact that
+# higher coefficients are observed on VIA Nano and Bulldozer has more
+# to do with specifics of their architecture [which is topic for
+# separate discussion].
+#
+# November 2012.
+#
+# Add AVX2 code path. Two consecutive input blocks are loaded to
+# 256-bit %ymm registers, with data from first block to least
+# significant 128-bit halves and data from second to most significant.
+# The data is then processed with same SIMD instruction sequence as
+# for AVX, but with %ymm as operands. Side effect is increased stack
+# frame, 448 additional bytes in SHA256 and 1152 in SHA512, and 1.2KB
+# code size increase.
+#
+# March 2014.
+#
+# Add support for Intel SHA Extensions.
+
+######################################################################
+# Current performance in cycles per processed byte (less is better):
+#
+#		SHA256	SSSE3       AVX/XOP(*)	    SHA512  AVX/XOP(*)
+#
+# AMD K8	14.9	-	    -		    9.57    -
+# P4		17.3	-	    -		    30.8    -
+# Core 2	15.6	13.8(+13%)  -		    9.97    -
+# Westmere	14.8	12.3(+19%)  -		    9.58    -
+# Sandy Bridge	17.4	14.2(+23%)  11.6(+50%(**))  11.2    8.10(+38%(**))
+# Ivy Bridge	12.6	10.5(+20%)  10.3(+22%)	    8.17    7.22(+13%)
+# Haswell	12.2	9.28(+31%)  7.80(+56%)	    7.66    5.40(+42%)
+# Skylake	11.4	9.03(+26%)  7.70(+48%)      7.25    5.20(+40%)
+# Bulldozer	21.1	13.6(+54%)  13.6(+54%(***)) 13.5    8.58(+57%)
+# VIA Nano	23.0	16.5(+39%)  -		    14.7    -
+# Atom		23.0	18.9(+22%)  -		    14.7    -
+# Silvermont	27.4	20.6(+33%)  -               17.5    -
+# Goldmont	18.9	14.3(+32%)  4.16(+350%)     12.0    -
+#
+# (*)	whichever best applicable, including SHAEXT;
+# (**)	switch from ror to shrd stands for fair share of improvement;
+# (***)	execution time is fully determined by remaining integer-only
+#	part, body_00_15; reducing the amount of SIMD instructions
+#	below certain limit makes no difference/sense; to conserve
+#	space SHA256 XOP code path is therefore omitted;
+
+$flavour = shift;
+$output  = shift;
+if ($flavour =~ /\./) { $output = $flavour; undef $flavour; }
+
+$win64=0; $win64=1 if ($flavour =~ /[nm]asm|mingw64/ || $output =~ /\.asm$/);
+
+$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+( $xlate="${dir}x86_64-xlate.pl" and -f $xlate ) or
+( $xlate="${dir}../../perlasm/x86_64-xlate.pl" and -f $xlate) or
+die "can't locate x86_64-xlate.pl";
+
+if (`$ENV{CC} -Wa,-v -c -o /dev/null -x assembler /dev/null 2>&1`
+		=~ /GNU assembler version ([2-9]\.[0-9]+)/) {
+	$avx = ($1>=2.19) + ($1>=2.22);
+}
+
+if (!$avx && $win64 && ($flavour =~ /nasm/ || $ENV{ASM} =~ /nasm/) &&
+	   `nasm -v 2>&1` =~ /NASM version ([2-9]\.[0-9]+)/) {
+	$avx = ($1>=2.09) + ($1>=2.10);
+}
+
+if (!$avx && $win64 && ($flavour =~ /masm/ || $ENV{ASM} =~ /ml64/) &&
+	   `ml64 2>&1` =~ /Version ([0-9]+)\./) {
+	$avx = ($1>=10) + ($1>=11);
+}
+
+if (!$avx && `$ENV{CC} -v 2>&1` =~ /((?:^clang|LLVM) version|.*based on LLVM) ([3-9]\.[0-9]+)/) {
+	$avx = ($2>=3.0) + ($2>3.0);
+}
+
+$shaext=1;	### set to zero if compiling for 1.0.1
+$avx=1		if (!$shaext && $avx);
+
+open OUT,"| \"$^X\" \"$xlate\" $flavour \"$output\"";
+*STDOUT=*OUT;
+
+if ($output =~ /512/) {
+	$func="sha512_block_data_order";
+	$TABLE="K512";
+	$SZ=8;
+	@ROT=($A,$B,$C,$D,$E,$F,$G,$H)=("%rax","%rbx","%rcx","%rdx",
+					"%r8", "%r9", "%r10","%r11");
+	($T1,$a0,$a1,$a2,$a3)=("%r12","%r13","%r14","%r15","%rdi");
+	@Sigma0=(28,34,39);
+	@Sigma1=(14,18,41);
+	@sigma0=(1,  8, 7);
+	@sigma1=(19,61, 6);
+	$rounds=80;
+} else {
+	$func="sha256_block_data_order";
+	$TABLE="K256";
+	$SZ=4;
+	@ROT=($A,$B,$C,$D,$E,$F,$G,$H)=("%eax","%ebx","%ecx","%edx",
+					"%r8d","%r9d","%r10d","%r11d");
+	($T1,$a0,$a1,$a2,$a3)=("%r12d","%r13d","%r14d","%r15d","%edi");
+	@Sigma0=( 2,13,22);
+	@Sigma1=( 6,11,25);
+	@sigma0=( 7,18, 3);
+	@sigma1=(17,19,10);
+	$rounds=64;
+}
+
+$ctx="%rdi";	# 1st arg, zapped by $a3
+$inp="%rsi";	# 2nd arg
+$Tbl="%rbp";
+
+$_ctx="16*$SZ+0*8(%rsp)";
+$_inp="16*$SZ+1*8(%rsp)";
+$_end="16*$SZ+2*8(%rsp)";
+$_rsp="16*$SZ+3*8(%rsp)";
+$framesz="16*$SZ+4*8";
+
+
+sub ROUND_00_15()
+{ my ($i,$a,$b,$c,$d,$e,$f,$g,$h) = @_;
+  my $STRIDE=$SZ;
+     $STRIDE += 16 if ($i%(16/$SZ)==(16/$SZ-1));
+
+$code.=<<___;
+	ror	\$`$Sigma1[2]-$Sigma1[1]`,$a0
+	mov	$f,$a2
+
+	xor	$e,$a0
+	ror	\$`$Sigma0[2]-$Sigma0[1]`,$a1
+	xor	$g,$a2			# f^g
+
+	mov	$T1,`$SZ*($i&0xf)`(%rsp)
+	xor	$a,$a1
+	and	$e,$a2			# (f^g)&e
+
+	ror	\$`$Sigma1[1]-$Sigma1[0]`,$a0
+	add	$h,$T1			# T1+=h
+	xor	$g,$a2			# Ch(e,f,g)=((f^g)&e)^g
+
+	ror	\$`$Sigma0[1]-$Sigma0[0]`,$a1
+	xor	$e,$a0
+	add	$a2,$T1			# T1+=Ch(e,f,g)
+
+	mov	$a,$a2
+	add	($Tbl),$T1		# T1+=K[round]
+	xor	$a,$a1
+
+	xor	$b,$a2			# a^b, b^c in next round
+	ror	\$$Sigma1[0],$a0	# Sigma1(e)
+	mov	$b,$h
+
+	and	$a2,$a3
+	ror	\$$Sigma0[0],$a1	# Sigma0(a)
+	add	$a0,$T1			# T1+=Sigma1(e)
+
+	xor	$a3,$h			# h=Maj(a,b,c)=Ch(a^b,c,b)
+	add	$T1,$d			# d+=T1
+	add	$T1,$h			# h+=T1
+
+	lea	$STRIDE($Tbl),$Tbl	# round++
+___
+$code.=<<___ if ($i<15);
+	add	$a1,$h			# h+=Sigma0(a)
+___
+	($a2,$a3) = ($a3,$a2);
+}
+
+sub ROUND_16_XX()
+{ my ($i,$a,$b,$c,$d,$e,$f,$g,$h) = @_;
+
+$code.=<<___;
+	mov	`$SZ*(($i+1)&0xf)`(%rsp),$a0
+	mov	`$SZ*(($i+14)&0xf)`(%rsp),$a2
+
+	mov	$a0,$T1
+	ror	\$`$sigma0[1]-$sigma0[0]`,$a0
+	add	$a1,$a			# modulo-scheduled h+=Sigma0(a)
+	mov	$a2,$a1
+	ror	\$`$sigma1[1]-$sigma1[0]`,$a2
+
+	xor	$T1,$a0
+	shr	\$$sigma0[2],$T1
+	ror	\$$sigma0[0],$a0
+	xor	$a1,$a2
+	shr	\$$sigma1[2],$a1
+
+	ror	\$$sigma1[0],$a2
+	xor	$a0,$T1			# sigma0(X[(i+1)&0xf])
+	xor	$a1,$a2			# sigma1(X[(i+14)&0xf])
+	add	`$SZ*(($i+9)&0xf)`(%rsp),$T1
+
+	add	`$SZ*($i&0xf)`(%rsp),$T1
+	mov	$e,$a0
+	add	$a2,$T1
+	mov	$a,$a1
+___
+	&ROUND_00_15(@_);
+}
+
+$code=<<___;
+.text
+
+.extern	OPENSSL_ia32cap_P
+.globl	$func
+.type	$func,\@function,3
+.align	16
+$func:
+___
+$code.=<<___ if ($SZ==4 || $avx);
+	lea	OPENSSL_ia32cap_P(%rip),%r11
+	mov	0(%r11),%r9d
+	mov	4(%r11),%r10d
+	mov	8(%r11),%r11d
+___
+$code.=<<___ if ($SZ==4 && $shaext);
+	test	\$`1<<29`,%r11d		# check for SHA
+	jnz	_shaext_shortcut
+___
+$code.=<<___ if ($avx && $SZ==8);
+	test	\$`1<<11`,%r10d		# check for XOP
+	jnz	.Lxop_shortcut
+___
+$code.=<<___ if ($avx>1);
+	and	\$`1<<8|1<<5|1<<3`,%r11d	# check for BMI2+AVX2+BMI1
+	cmp	\$`1<<8|1<<5|1<<3`,%r11d
+	je	.Lavx2_shortcut
+___
+$code.=<<___ if ($avx);
+	and	\$`1<<30`,%r9d		# mask "Intel CPU" bit
+	and	\$`1<<28|1<<9`,%r10d	# mask AVX and SSSE3 bits
+	or	%r9d,%r10d
+	cmp	\$`1<<28|1<<9|1<<30`,%r10d
+	je	.Lavx_shortcut
+___
+$code.=<<___ if ($SZ==4);
+	test	\$`1<<9`,%r10d
+	jnz	.Lssse3_shortcut
+___
+$code.=<<___;
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+	mov	%rsp,%r11		# copy %rsp
+	shl	\$4,%rdx		# num*16
+	sub	\$$framesz,%rsp
+	lea	($inp,%rdx,$SZ),%rdx	# inp+num*16*$SZ
+	and	\$-64,%rsp		# align stack frame
+	mov	$ctx,$_ctx		# save ctx, 1st arg
+	mov	$inp,$_inp		# save inp, 2nd arh
+	mov	%rdx,$_end		# save end pointer, "3rd" arg
+	mov	%r11,$_rsp		# save copy of %rsp
+.Lprologue:
+
+	mov	$SZ*0($ctx),$A
+	mov	$SZ*1($ctx),$B
+	mov	$SZ*2($ctx),$C
+	mov	$SZ*3($ctx),$D
+	mov	$SZ*4($ctx),$E
+	mov	$SZ*5($ctx),$F
+	mov	$SZ*6($ctx),$G
+	mov	$SZ*7($ctx),$H
+	jmp	.Lloop
+
+.align	16
+.Lloop:
+	mov	$B,$a3
+	lea	$TABLE(%rip),$Tbl
+	xor	$C,$a3			# magic
+___
+	for($i=0;$i<16;$i++) {
+		$code.="	mov	$SZ*$i($inp),$T1\n";
+		$code.="	mov	@ROT[4],$a0\n";
+		$code.="	mov	@ROT[0],$a1\n";
+		$code.="	bswap	$T1\n";
+		&ROUND_00_15($i,@ROT);
+		unshift(@ROT,pop(@ROT));
+	}
+$code.=<<___;
+	jmp	.Lrounds_16_xx
+.align	16
+.Lrounds_16_xx:
+___
+	for(;$i<32;$i++) {
+		&ROUND_16_XX($i,@ROT);
+		unshift(@ROT,pop(@ROT));
+	}
+
+$code.=<<___;
+	cmpb	\$0,`$SZ-1`($Tbl)
+	jnz	.Lrounds_16_xx
+
+	mov	$_ctx,$ctx
+	add	$a1,$A			# modulo-scheduled h+=Sigma0(a)
+	lea	16*$SZ($inp),$inp
+
+	add	$SZ*0($ctx),$A
+	add	$SZ*1($ctx),$B
+	add	$SZ*2($ctx),$C
+	add	$SZ*3($ctx),$D
+	add	$SZ*4($ctx),$E
+	add	$SZ*5($ctx),$F
+	add	$SZ*6($ctx),$G
+	add	$SZ*7($ctx),$H
+
+	cmp	$_end,$inp
+
+	mov	$A,$SZ*0($ctx)
+	mov	$B,$SZ*1($ctx)
+	mov	$C,$SZ*2($ctx)
+	mov	$D,$SZ*3($ctx)
+	mov	$E,$SZ*4($ctx)
+	mov	$F,$SZ*5($ctx)
+	mov	$G,$SZ*6($ctx)
+	mov	$H,$SZ*7($ctx)
+	jb	.Lloop
+
+	mov	$_rsp,%rsi
+	mov	(%rsi),%r15
+	mov	8(%rsi),%r14
+	mov	16(%rsi),%r13
+	mov	24(%rsi),%r12
+	mov	32(%rsi),%rbp
+	mov	40(%rsi),%rbx
+	lea	48(%rsi),%rsp
+.Lepilogue:
+	ret
+.size	$func,.-$func
+___
+
+if ($SZ==4) {
+$code.=<<___;
+.align	64
+.type	$TABLE,\@object
+$TABLE:
+	.long	0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5
+	.long	0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5
+	.long	0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5
+	.long	0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5
+	.long	0xd807aa98,0x12835b01,0x243185be,0x550c7dc3
+	.long	0xd807aa98,0x12835b01,0x243185be,0x550c7dc3
+	.long	0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174
+	.long	0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174
+	.long	0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc
+	.long	0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc
+	.long	0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da
+	.long	0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da
+	.long	0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7
+	.long	0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7
+	.long	0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967
+	.long	0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967
+	.long	0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13
+	.long	0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13
+	.long	0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85
+	.long	0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85
+	.long	0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3
+	.long	0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3
+	.long	0xd192e819,0xd6990624,0xf40e3585,0x106aa070
+	.long	0xd192e819,0xd6990624,0xf40e3585,0x106aa070
+	.long	0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5
+	.long	0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5
+	.long	0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3
+	.long	0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3
+	.long	0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208
+	.long	0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208
+	.long	0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2
+	.long	0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2
+
+	.long	0x00010203,0x04050607,0x08090a0b,0x0c0d0e0f
+	.long	0x00010203,0x04050607,0x08090a0b,0x0c0d0e0f
+	.long	0x03020100,0x0b0a0908,0xffffffff,0xffffffff
+	.long	0x03020100,0x0b0a0908,0xffffffff,0xffffffff
+	.long	0xffffffff,0xffffffff,0x03020100,0x0b0a0908
+	.long	0xffffffff,0xffffffff,0x03020100,0x0b0a0908
+	.asciz	"SHA256 block transform for x86_64, CRYPTOGAMS by "
+___
+} else {
+$code.=<<___;
+.align	64
+.type	$TABLE,\@object
+$TABLE:
+	.quad	0x428a2f98d728ae22,0x7137449123ef65cd
+	.quad	0x428a2f98d728ae22,0x7137449123ef65cd
+	.quad	0xb5c0fbcfec4d3b2f,0xe9b5dba58189dbbc
+	.quad	0xb5c0fbcfec4d3b2f,0xe9b5dba58189dbbc
+	.quad	0x3956c25bf348b538,0x59f111f1b605d019
+	.quad	0x3956c25bf348b538,0x59f111f1b605d019
+	.quad	0x923f82a4af194f9b,0xab1c5ed5da6d8118
+	.quad	0x923f82a4af194f9b,0xab1c5ed5da6d8118
+	.quad	0xd807aa98a3030242,0x12835b0145706fbe
+	.quad	0xd807aa98a3030242,0x12835b0145706fbe
+	.quad	0x243185be4ee4b28c,0x550c7dc3d5ffb4e2
+	.quad	0x243185be4ee4b28c,0x550c7dc3d5ffb4e2
+	.quad	0x72be5d74f27b896f,0x80deb1fe3b1696b1
+	.quad	0x72be5d74f27b896f,0x80deb1fe3b1696b1
+	.quad	0x9bdc06a725c71235,0xc19bf174cf692694
+	.quad	0x9bdc06a725c71235,0xc19bf174cf692694
+	.quad	0xe49b69c19ef14ad2,0xefbe4786384f25e3
+	.quad	0xe49b69c19ef14ad2,0xefbe4786384f25e3
+	.quad	0x0fc19dc68b8cd5b5,0x240ca1cc77ac9c65
+	.quad	0x0fc19dc68b8cd5b5,0x240ca1cc77ac9c65
+	.quad	0x2de92c6f592b0275,0x4a7484aa6ea6e483
+	.quad	0x2de92c6f592b0275,0x4a7484aa6ea6e483
+	.quad	0x5cb0a9dcbd41fbd4,0x76f988da831153b5
+	.quad	0x5cb0a9dcbd41fbd4,0x76f988da831153b5
+	.quad	0x983e5152ee66dfab,0xa831c66d2db43210
+	.quad	0x983e5152ee66dfab,0xa831c66d2db43210
+	.quad	0xb00327c898fb213f,0xbf597fc7beef0ee4
+	.quad	0xb00327c898fb213f,0xbf597fc7beef0ee4
+	.quad	0xc6e00bf33da88fc2,0xd5a79147930aa725
+	.quad	0xc6e00bf33da88fc2,0xd5a79147930aa725
+	.quad	0x06ca6351e003826f,0x142929670a0e6e70
+	.quad	0x06ca6351e003826f,0x142929670a0e6e70
+	.quad	0x27b70a8546d22ffc,0x2e1b21385c26c926
+	.quad	0x27b70a8546d22ffc,0x2e1b21385c26c926
+	.quad	0x4d2c6dfc5ac42aed,0x53380d139d95b3df
+	.quad	0x4d2c6dfc5ac42aed,0x53380d139d95b3df
+	.quad	0x650a73548baf63de,0x766a0abb3c77b2a8
+	.quad	0x650a73548baf63de,0x766a0abb3c77b2a8
+	.quad	0x81c2c92e47edaee6,0x92722c851482353b
+	.quad	0x81c2c92e47edaee6,0x92722c851482353b
+	.quad	0xa2bfe8a14cf10364,0xa81a664bbc423001
+	.quad	0xa2bfe8a14cf10364,0xa81a664bbc423001
+	.quad	0xc24b8b70d0f89791,0xc76c51a30654be30
+	.quad	0xc24b8b70d0f89791,0xc76c51a30654be30
+	.quad	0xd192e819d6ef5218,0xd69906245565a910
+	.quad	0xd192e819d6ef5218,0xd69906245565a910
+	.quad	0xf40e35855771202a,0x106aa07032bbd1b8
+	.quad	0xf40e35855771202a,0x106aa07032bbd1b8
+	.quad	0x19a4c116b8d2d0c8,0x1e376c085141ab53
+	.quad	0x19a4c116b8d2d0c8,0x1e376c085141ab53
+	.quad	0x2748774cdf8eeb99,0x34b0bcb5e19b48a8
+	.quad	0x2748774cdf8eeb99,0x34b0bcb5e19b48a8
+	.quad	0x391c0cb3c5c95a63,0x4ed8aa4ae3418acb
+	.quad	0x391c0cb3c5c95a63,0x4ed8aa4ae3418acb
+	.quad	0x5b9cca4f7763e373,0x682e6ff3d6b2b8a3
+	.quad	0x5b9cca4f7763e373,0x682e6ff3d6b2b8a3
+	.quad	0x748f82ee5defb2fc,0x78a5636f43172f60
+	.quad	0x748f82ee5defb2fc,0x78a5636f43172f60
+	.quad	0x84c87814a1f0ab72,0x8cc702081a6439ec
+	.quad	0x84c87814a1f0ab72,0x8cc702081a6439ec
+	.quad	0x90befffa23631e28,0xa4506cebde82bde9
+	.quad	0x90befffa23631e28,0xa4506cebde82bde9
+	.quad	0xbef9a3f7b2c67915,0xc67178f2e372532b
+	.quad	0xbef9a3f7b2c67915,0xc67178f2e372532b
+	.quad	0xca273eceea26619c,0xd186b8c721c0c207
+	.quad	0xca273eceea26619c,0xd186b8c721c0c207
+	.quad	0xeada7dd6cde0eb1e,0xf57d4f7fee6ed178
+	.quad	0xeada7dd6cde0eb1e,0xf57d4f7fee6ed178
+	.quad	0x06f067aa72176fba,0x0a637dc5a2c898a6
+	.quad	0x06f067aa72176fba,0x0a637dc5a2c898a6
+	.quad	0x113f9804bef90dae,0x1b710b35131c471b
+	.quad	0x113f9804bef90dae,0x1b710b35131c471b
+	.quad	0x28db77f523047d84,0x32caab7b40c72493
+	.quad	0x28db77f523047d84,0x32caab7b40c72493
+	.quad	0x3c9ebe0a15c9bebc,0x431d67c49c100d4c
+	.quad	0x3c9ebe0a15c9bebc,0x431d67c49c100d4c
+	.quad	0x4cc5d4becb3e42b6,0x597f299cfc657e2a
+	.quad	0x4cc5d4becb3e42b6,0x597f299cfc657e2a
+	.quad	0x5fcb6fab3ad6faec,0x6c44198c4a475817
+	.quad	0x5fcb6fab3ad6faec,0x6c44198c4a475817
+
+	.quad	0x0001020304050607,0x08090a0b0c0d0e0f
+	.quad	0x0001020304050607,0x08090a0b0c0d0e0f
+	.asciz	"SHA512 block transform for x86_64, CRYPTOGAMS by "
+___
+}
+
+######################################################################
+# SIMD code paths
+#
+if ($SZ==4 && $shaext) {{{
+######################################################################
+# Intel SHA Extensions implementation of SHA256 update function.
+#
+my ($ctx,$inp,$num,$Tbl)=("%rdi","%rsi","%rdx","%rcx");
+
+my ($Wi,$ABEF,$CDGH,$TMP,$BSWAP,$ABEF_SAVE,$CDGH_SAVE)=map("%xmm$_",(0..2,7..10));
+my @MSG=map("%xmm$_",(3..6));
+
+$code.=<<___;
+.type	sha256_block_data_order_shaext,\@function,3
+.align	64
+sha256_block_data_order_shaext:
+_shaext_shortcut:
+___
+$code.=<<___ if ($win64);
+	lea	`-8-5*16`(%rsp),%rsp
+	movaps	%xmm6,-8-5*16(%rax)
+	movaps	%xmm7,-8-4*16(%rax)
+	movaps	%xmm8,-8-3*16(%rax)
+	movaps	%xmm9,-8-2*16(%rax)
+	movaps	%xmm10,-8-1*16(%rax)
+.Lprologue_shaext:
+___
+$code.=<<___;
+	lea		K256+0x80(%rip),$Tbl
+	movdqu		($ctx),$ABEF		# DCBA
+	movdqu		16($ctx),$CDGH		# HGFE
+	movdqa		0x200-0x80($Tbl),$TMP	# byte swap mask
+
+	pshufd		\$0x1b,$ABEF,$Wi	# ABCD
+	pshufd		\$0xb1,$ABEF,$ABEF	# CDAB
+	pshufd		\$0x1b,$CDGH,$CDGH	# EFGH
+	movdqa		$TMP,$BSWAP		# offload
+	palignr		\$8,$CDGH,$ABEF		# ABEF
+	punpcklqdq	$Wi,$CDGH		# CDGH
+	jmp		.Loop_shaext
+
+.align	16
+.Loop_shaext:
+	movdqu		($inp),@MSG[0]
+	movdqu		0x10($inp),@MSG[1]
+	movdqu		0x20($inp),@MSG[2]
+	pshufb		$TMP,@MSG[0]
+	movdqu		0x30($inp),@MSG[3]
+
+	movdqa		0*32-0x80($Tbl),$Wi
+	paddd		@MSG[0],$Wi
+	pshufb		$TMP,@MSG[1]
+	movdqa		$CDGH,$CDGH_SAVE	# offload
+	sha256rnds2	$ABEF,$CDGH		# 0-3
+	pshufd		\$0x0e,$Wi,$Wi
+	nop
+	movdqa		$ABEF,$ABEF_SAVE	# offload
+	sha256rnds2	$CDGH,$ABEF
+
+	movdqa		1*32-0x80($Tbl),$Wi
+	paddd		@MSG[1],$Wi
+	pshufb		$TMP,@MSG[2]
+	sha256rnds2	$ABEF,$CDGH		# 4-7
+	pshufd		\$0x0e,$Wi,$Wi
+	lea		0x40($inp),$inp
+	sha256msg1	@MSG[1],@MSG[0]
+	sha256rnds2	$CDGH,$ABEF
+
+	movdqa		2*32-0x80($Tbl),$Wi
+	paddd		@MSG[2],$Wi
+	pshufb		$TMP,@MSG[3]
+	sha256rnds2	$ABEF,$CDGH		# 8-11
+	pshufd		\$0x0e,$Wi,$Wi
+	movdqa		@MSG[3],$TMP
+	palignr		\$4,@MSG[2],$TMP
+	nop
+	paddd		$TMP,@MSG[0]
+	sha256msg1	@MSG[2],@MSG[1]
+	sha256rnds2	$CDGH,$ABEF
+
+	movdqa		3*32-0x80($Tbl),$Wi
+	paddd		@MSG[3],$Wi
+	sha256msg2	@MSG[3],@MSG[0]
+	sha256rnds2	$ABEF,$CDGH		# 12-15
+	pshufd		\$0x0e,$Wi,$Wi
+	movdqa		@MSG[0],$TMP
+	palignr		\$4,@MSG[3],$TMP
+	nop
+	paddd		$TMP,@MSG[1]
+	sha256msg1	@MSG[3],@MSG[2]
+	sha256rnds2	$CDGH,$ABEF
+___
+for($i=4;$i<16-3;$i++) {
+$code.=<<___;
+	movdqa		$i*32-0x80($Tbl),$Wi
+	paddd		@MSG[0],$Wi
+	sha256msg2	@MSG[0],@MSG[1]
+	sha256rnds2	$ABEF,$CDGH		# 16-19...
+	pshufd		\$0x0e,$Wi,$Wi
+	movdqa		@MSG[1],$TMP
+	palignr		\$4,@MSG[0],$TMP
+	nop
+	paddd		$TMP,@MSG[2]
+	sha256msg1	@MSG[0],@MSG[3]
+	sha256rnds2	$CDGH,$ABEF
+___
+	push(@MSG,shift(@MSG));
+}
+$code.=<<___;
+	movdqa		13*32-0x80($Tbl),$Wi
+	paddd		@MSG[0],$Wi
+	sha256msg2	@MSG[0],@MSG[1]
+	sha256rnds2	$ABEF,$CDGH		# 52-55
+	pshufd		\$0x0e,$Wi,$Wi
+	movdqa		@MSG[1],$TMP
+	palignr		\$4,@MSG[0],$TMP
+	sha256rnds2	$CDGH,$ABEF
+	paddd		$TMP,@MSG[2]
+
+	movdqa		14*32-0x80($Tbl),$Wi
+	paddd		@MSG[1],$Wi
+	sha256rnds2	$ABEF,$CDGH		# 56-59
+	pshufd		\$0x0e,$Wi,$Wi
+	sha256msg2	@MSG[1],@MSG[2]
+	movdqa		$BSWAP,$TMP
+	sha256rnds2	$CDGH,$ABEF
+
+	movdqa		15*32-0x80($Tbl),$Wi
+	paddd		@MSG[2],$Wi
+	nop
+	sha256rnds2	$ABEF,$CDGH		# 60-63
+	pshufd		\$0x0e,$Wi,$Wi
+	dec		$num
+	nop
+	sha256rnds2	$CDGH,$ABEF
+
+	paddd		$CDGH_SAVE,$CDGH
+	paddd		$ABEF_SAVE,$ABEF
+	jnz		.Loop_shaext
+
+	pshufd		\$0xb1,$CDGH,$CDGH	# DCHG
+	pshufd		\$0x1b,$ABEF,$TMP	# FEBA
+	pshufd		\$0xb1,$ABEF,$ABEF	# BAFE
+	punpckhqdq	$CDGH,$ABEF		# DCBA
+	palignr		\$8,$TMP,$CDGH		# HGFE
+
+	movdqu	$ABEF,($ctx)
+	movdqu	$CDGH,16($ctx)
+___
+$code.=<<___ if ($win64);
+	movaps	-8-5*16(%rax),%xmm6
+	movaps	-8-4*16(%rax),%xmm7
+	movaps	-8-3*16(%rax),%xmm8
+	movaps	-8-2*16(%rax),%xmm9
+	movaps	-8-1*16(%rax),%xmm10
+	mov	%rax,%rsp
+.Lepilogue_shaext:
+___
+$code.=<<___;
+	ret
+.size	sha256_block_data_order_shaext,.-sha256_block_data_order_shaext
+___
+}}}
+{{{
+
+my $a4=$T1;
+my ($a,$b,$c,$d,$e,$f,$g,$h);
+
+sub AUTOLOAD()		# thunk [simplified] 32-bit style perlasm
+{ my $opcode = $AUTOLOAD; $opcode =~ s/.*:://;
+  my $arg = pop;
+    $arg = "\$$arg" if ($arg*1 eq $arg);
+    $code .= "\t$opcode\t".join(',',$arg,reverse @_)."\n";
+}
+
+sub body_00_15 () {
+	(
+	'($a,$b,$c,$d,$e,$f,$g,$h)=@ROT;'.
+
+	'&ror	($a0,$Sigma1[2]-$Sigma1[1])',
+	'&mov	($a,$a1)',
+	'&mov	($a4,$f)',
+
+	'&ror	($a1,$Sigma0[2]-$Sigma0[1])',
+	'&xor	($a0,$e)',
+	'&xor	($a4,$g)',			# f^g
+
+	'&ror	($a0,$Sigma1[1]-$Sigma1[0])',
+	'&xor	($a1,$a)',
+	'&and	($a4,$e)',			# (f^g)&e
+
+	'&xor	($a0,$e)',
+	'&add	($h,$SZ*($i&15)."(%rsp)")',	# h+=X[i]+K[i]
+	'&mov	($a2,$a)',
+
+	'&xor	($a4,$g)',			# Ch(e,f,g)=((f^g)&e)^g
+	'&ror	($a1,$Sigma0[1]-$Sigma0[0])',
+	'&xor	($a2,$b)',			# a^b, b^c in next round
+
+	'&add	($h,$a4)',			# h+=Ch(e,f,g)
+	'&ror	($a0,$Sigma1[0])',		# Sigma1(e)
+	'&and	($a3,$a2)',			# (b^c)&(a^b)
+
+	'&xor	($a1,$a)',
+	'&add	($h,$a0)',			# h+=Sigma1(e)
+	'&xor	($a3,$b)',			# Maj(a,b,c)=Ch(a^b,c,b)
+
+	'&ror	($a1,$Sigma0[0])',		# Sigma0(a)
+	'&add	($d,$h)',			# d+=h
+	'&add	($h,$a3)',			# h+=Maj(a,b,c)
+
+	'&mov	($a0,$d)',
+	'&add	($a1,$h);'.			# h+=Sigma0(a)
+	'($a2,$a3) = ($a3,$a2); unshift(@ROT,pop(@ROT)); $i++;'
+	);
+}
+
+######################################################################
+# SSSE3 code path
+#
+if ($SZ==4) {	# SHA256 only
+my @X = map("%xmm$_",(0..3));
+my ($t0,$t1,$t2,$t3, $t4,$t5) = map("%xmm$_",(4..9));
+
+$code.=<<___;
+.type	${func}_ssse3,\@function,3
+.align	64
+${func}_ssse3:
+.Lssse3_shortcut:
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+	mov	%rsp,%r11		# copy %rsp
+	shl	\$4,%rdx		# num*16
+	sub	\$`$framesz+$win64*16*4`,%rsp
+	lea	($inp,%rdx,$SZ),%rdx	# inp+num*16*$SZ
+	and	\$-64,%rsp		# align stack frame
+	mov	$ctx,$_ctx		# save ctx, 1st arg
+	mov	$inp,$_inp		# save inp, 2nd arh
+	mov	%rdx,$_end		# save end pointer, "3rd" arg
+	mov	%r11,$_rsp		# save copy of %rsp
+___
+$code.=<<___ if ($win64);
+	movaps	%xmm6,16*$SZ+32(%rsp)
+	movaps	%xmm7,16*$SZ+48(%rsp)
+	movaps	%xmm8,16*$SZ+64(%rsp)
+	movaps	%xmm9,16*$SZ+80(%rsp)
+___
+$code.=<<___;
+.Lprologue_ssse3:
+
+	mov	$SZ*0($ctx),$A
+	mov	$SZ*1($ctx),$B
+	mov	$SZ*2($ctx),$C
+	mov	$SZ*3($ctx),$D
+	mov	$SZ*4($ctx),$E
+	mov	$SZ*5($ctx),$F
+	mov	$SZ*6($ctx),$G
+	mov	$SZ*7($ctx),$H
+___
+
+$code.=<<___;
+	#movdqa	$TABLE+`$SZ*2*$rounds`+32(%rip),$t4
+	#movdqa	$TABLE+`$SZ*2*$rounds`+64(%rip),$t5
+	jmp	.Lloop_ssse3
+.align	16
+.Lloop_ssse3:
+	movdqa	$TABLE+`$SZ*2*$rounds`(%rip),$t3
+	movdqu	0x00($inp),@X[0]
+	movdqu	0x10($inp),@X[1]
+	movdqu	0x20($inp),@X[2]
+	pshufb	$t3,@X[0]
+	movdqu	0x30($inp),@X[3]
+	lea	$TABLE(%rip),$Tbl
+	pshufb	$t3,@X[1]
+	movdqa	0x00($Tbl),$t0
+	movdqa	0x20($Tbl),$t1
+	pshufb	$t3,@X[2]
+	paddd	@X[0],$t0
+	movdqa	0x40($Tbl),$t2
+	pshufb	$t3,@X[3]
+	movdqa	0x60($Tbl),$t3
+	paddd	@X[1],$t1
+	paddd	@X[2],$t2
+	paddd	@X[3],$t3
+	movdqa	$t0,0x00(%rsp)
+	mov	$A,$a1
+	movdqa	$t1,0x10(%rsp)
+	mov	$B,$a3
+	movdqa	$t2,0x20(%rsp)
+	xor	$C,$a3			# magic
+	movdqa	$t3,0x30(%rsp)
+	mov	$E,$a0
+	jmp	.Lssse3_00_47
+
+.align	16
+.Lssse3_00_47:
+	sub	\$`-16*2*$SZ`,$Tbl	# size optimization
+___
+sub Xupdate_256_SSSE3 () {
+	(
+	'&movdqa	($t0,@X[1]);',
+	'&movdqa	($t3,@X[3])',
+	'&palignr	($t0,@X[0],$SZ)',	# X[1..4]
+	 '&palignr	($t3,@X[2],$SZ);',	# X[9..12]
+	'&movdqa	($t1,$t0)',
+	'&movdqa	($t2,$t0);',
+	'&psrld		($t0,$sigma0[2])',
+	 '&paddd	(@X[0],$t3);',		# X[0..3] += X[9..12]
+	'&psrld		($t2,$sigma0[0])',
+	 '&pshufd	($t3,@X[3],0b11111010)',# X[14..15]
+	'&pslld		($t1,8*$SZ-$sigma0[1]);'.
+	'&pxor		($t0,$t2)',
+	'&psrld		($t2,$sigma0[1]-$sigma0[0]);'.
+	'&pxor		($t0,$t1)',
+	'&pslld		($t1,$sigma0[1]-$sigma0[0]);'.
+	'&pxor		($t0,$t2);',
+	 '&movdqa	($t2,$t3)',
+	'&pxor		($t0,$t1);',		# sigma0(X[1..4])
+	 '&psrld	($t3,$sigma1[2])',
+	'&paddd		(@X[0],$t0);',		# X[0..3] += sigma0(X[1..4])
+	 '&psrlq	($t2,$sigma1[0])',
+	 '&pxor		($t3,$t2);',
+	 '&psrlq	($t2,$sigma1[1]-$sigma1[0])',
+	 '&pxor		($t3,$t2)',
+	 '&pshufb	($t3,$t4)',		# sigma1(X[14..15])
+	'&paddd		(@X[0],$t3)',		# X[0..1] += sigma1(X[14..15])
+	 '&pshufd	($t3,@X[0],0b01010000)',# X[16..17]
+	 '&movdqa	($t2,$t3);',
+	 '&psrld	($t3,$sigma1[2])',
+	 '&psrlq	($t2,$sigma1[0])',
+	 '&pxor		($t3,$t2);',
+	 '&psrlq	($t2,$sigma1[1]-$sigma1[0])',
+	 '&pxor		($t3,$t2);',
+	'&movdqa	($t2,16*2*$j."($Tbl)")',
+	 '&pshufb	($t3,$t5)',
+	'&paddd		(@X[0],$t3)'		# X[2..3] += sigma1(X[16..17])
+	);
+}
+
+sub SSSE3_256_00_47 () {
+my $j = shift;
+my $body = shift;
+my @X = @_;
+my @insns = (&$body,&$body,&$body,&$body);	# 104 instructions
+
+    if (0) {
+	foreach (Xupdate_256_SSSE3()) {		# 36 instructions
+	    eval;
+	    eval(shift(@insns));
+	    eval(shift(@insns));
+	    eval(shift(@insns));
+	}
+    } else {			# squeeze extra 4% on Westmere and 19% on Atom
+	  eval(shift(@insns));	#@
+	&movdqa		($t0,@X[1]);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	&movdqa		($t3,@X[3]);
+	  eval(shift(@insns));	#@
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));	#@
+	  eval(shift(@insns));
+	&palignr	($t0,@X[0],$SZ);	# X[1..4]
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	 &palignr	($t3,@X[2],$SZ);	# X[9..12]
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));	#@
+	&movdqa		($t1,$t0);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	&movdqa		($t2,$t0);
+	  eval(shift(@insns));	#@
+	  eval(shift(@insns));
+	&psrld		($t0,$sigma0[2]);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	 &paddd		(@X[0],$t3);		# X[0..3] += X[9..12]
+	  eval(shift(@insns));	#@
+	  eval(shift(@insns));
+	&psrld		($t2,$sigma0[0]);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	 &pshufd	($t3,@X[3],0b11111010);	# X[4..15]
+	  eval(shift(@insns));
+	  eval(shift(@insns));	#@
+	&pslld		($t1,8*$SZ-$sigma0[1]);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	&pxor		($t0,$t2);
+	  eval(shift(@insns));	#@
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));	#@
+	&psrld		($t2,$sigma0[1]-$sigma0[0]);
+	  eval(shift(@insns));
+	&pxor		($t0,$t1);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	&pslld		($t1,$sigma0[1]-$sigma0[0]);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	&pxor		($t0,$t2);
+	  eval(shift(@insns));
+	  eval(shift(@insns));	#@
+	 &movdqa	($t2,$t3);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	&pxor		($t0,$t1);		# sigma0(X[1..4])
+	  eval(shift(@insns));	#@
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	 &psrld		($t3,$sigma1[2]);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	&paddd		(@X[0],$t0);		# X[0..3] += sigma0(X[1..4])
+	  eval(shift(@insns));	#@
+	  eval(shift(@insns));
+	 &psrlq		($t2,$sigma1[0]);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	 &pxor		($t3,$t2);
+	  eval(shift(@insns));	#@
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));	#@
+	 &psrlq		($t2,$sigma1[1]-$sigma1[0]);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	 &pxor		($t3,$t2);
+	  eval(shift(@insns));	#@
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	 #&pshufb	($t3,$t4);		# sigma1(X[14..15])
+	 &pshufd	($t3,$t3,0b10000000);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	 &psrldq	($t3,8);
+	  eval(shift(@insns));
+	  eval(shift(@insns));	#@
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));	#@
+	&paddd		(@X[0],$t3);		# X[0..1] += sigma1(X[14..15])
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	 &pshufd	($t3,@X[0],0b01010000);	# X[16..17]
+	  eval(shift(@insns));
+	  eval(shift(@insns));	#@
+	  eval(shift(@insns));
+	 &movdqa	($t2,$t3);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	 &psrld		($t3,$sigma1[2]);
+	  eval(shift(@insns));
+	  eval(shift(@insns));	#@
+	 &psrlq		($t2,$sigma1[0]);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	 &pxor		($t3,$t2);
+	  eval(shift(@insns));	#@
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));	#@
+	  eval(shift(@insns));
+	 &psrlq		($t2,$sigma1[1]-$sigma1[0]);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	 &pxor		($t3,$t2);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));	#@
+	 #&pshufb	($t3,$t5);
+	 &pshufd	($t3,$t3,0b00001000);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	&movdqa		($t2,16*2*$j."($Tbl)");
+	  eval(shift(@insns));	#@
+	  eval(shift(@insns));
+	 &pslldq	($t3,8);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	&paddd		(@X[0],$t3);		# X[2..3] += sigma1(X[16..17])
+	  eval(shift(@insns));	#@
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+    }
+	&paddd		($t2,@X[0]);
+	  foreach (@insns) { eval; }		# remaining instructions
+	&movdqa		(16*$j."(%rsp)",$t2);
+}
+
+    for ($i=0,$j=0; $j<4; $j++) {
+	&SSSE3_256_00_47($j,\&body_00_15,@X);
+	push(@X,shift(@X));			# rotate(@X)
+    }
+	&cmpb	($SZ-1+16*2*$SZ."($Tbl)",0);
+	&jne	(".Lssse3_00_47");
+
+    for ($i=0; $i<16; ) {
+	foreach(body_00_15()) { eval; }
+    }
+$code.=<<___;
+	mov	$_ctx,$ctx
+	mov	$a1,$A
+
+	add	$SZ*0($ctx),$A
+	lea	16*$SZ($inp),$inp
+	add	$SZ*1($ctx),$B
+	add	$SZ*2($ctx),$C
+	add	$SZ*3($ctx),$D
+	add	$SZ*4($ctx),$E
+	add	$SZ*5($ctx),$F
+	add	$SZ*6($ctx),$G
+	add	$SZ*7($ctx),$H
+
+	cmp	$_end,$inp
+
+	mov	$A,$SZ*0($ctx)
+	mov	$B,$SZ*1($ctx)
+	mov	$C,$SZ*2($ctx)
+	mov	$D,$SZ*3($ctx)
+	mov	$E,$SZ*4($ctx)
+	mov	$F,$SZ*5($ctx)
+	mov	$G,$SZ*6($ctx)
+	mov	$H,$SZ*7($ctx)
+	jb	.Lloop_ssse3
+
+	mov	$_rsp,%rsi
+___
+$code.=<<___ if ($win64);
+	movaps	16*$SZ+32(%rsp),%xmm6
+	movaps	16*$SZ+48(%rsp),%xmm7
+	movaps	16*$SZ+64(%rsp),%xmm8
+	movaps	16*$SZ+80(%rsp),%xmm9
+___
+$code.=<<___;
+	mov	(%rsi),%r15
+	mov	8(%rsi),%r14
+	mov	16(%rsi),%r13
+	mov	24(%rsi),%r12
+	mov	32(%rsi),%rbp
+	mov	40(%rsi),%rbx
+	lea	48(%rsi),%rsp
+.Lepilogue_ssse3:
+	ret
+.size	${func}_ssse3,.-${func}_ssse3
+___
+}
+
+if ($avx) {{
+######################################################################
+# XOP code path
+#
+if ($SZ==8) {	# SHA512 only
+$code.=<<___;
+.type	${func}_xop,\@function,3
+.align	64
+${func}_xop:
+.Lxop_shortcut:
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+	mov	%rsp,%r11		# copy %rsp
+	shl	\$4,%rdx		# num*16
+	sub	\$`$framesz+$win64*16*($SZ==4?4:6)`,%rsp
+	lea	($inp,%rdx,$SZ),%rdx	# inp+num*16*$SZ
+	and	\$-64,%rsp		# align stack frame
+	mov	$ctx,$_ctx		# save ctx, 1st arg
+	mov	$inp,$_inp		# save inp, 2nd arh
+	mov	%rdx,$_end		# save end pointer, "3rd" arg
+	mov	%r11,$_rsp		# save copy of %rsp
+___
+$code.=<<___ if ($win64);
+	movaps	%xmm6,16*$SZ+32(%rsp)
+	movaps	%xmm7,16*$SZ+48(%rsp)
+	movaps	%xmm8,16*$SZ+64(%rsp)
+	movaps	%xmm9,16*$SZ+80(%rsp)
+___
+$code.=<<___ if ($win64 && $SZ>4);
+	movaps	%xmm10,16*$SZ+96(%rsp)
+	movaps	%xmm11,16*$SZ+112(%rsp)
+___
+$code.=<<___;
+.Lprologue_xop:
+
+	vzeroupper
+	mov	$SZ*0($ctx),$A
+	mov	$SZ*1($ctx),$B
+	mov	$SZ*2($ctx),$C
+	mov	$SZ*3($ctx),$D
+	mov	$SZ*4($ctx),$E
+	mov	$SZ*5($ctx),$F
+	mov	$SZ*6($ctx),$G
+	mov	$SZ*7($ctx),$H
+	jmp	.Lloop_xop
+___
+					if ($SZ==4) {	# SHA256
+    my @X = map("%xmm$_",(0..3));
+    my ($t0,$t1,$t2,$t3) = map("%xmm$_",(4..7));
+
+$code.=<<___;
+.align	16
+.Lloop_xop:
+	vmovdqa	$TABLE+`$SZ*2*$rounds`(%rip),$t3
+	vmovdqu	0x00($inp),@X[0]
+	vmovdqu	0x10($inp),@X[1]
+	vmovdqu	0x20($inp),@X[2]
+	vmovdqu	0x30($inp),@X[3]
+	vpshufb	$t3,@X[0],@X[0]
+	lea	$TABLE(%rip),$Tbl
+	vpshufb	$t3,@X[1],@X[1]
+	vpshufb	$t3,@X[2],@X[2]
+	vpaddd	0x00($Tbl),@X[0],$t0
+	vpshufb	$t3,@X[3],@X[3]
+	vpaddd	0x20($Tbl),@X[1],$t1
+	vpaddd	0x40($Tbl),@X[2],$t2
+	vpaddd	0x60($Tbl),@X[3],$t3
+	vmovdqa	$t0,0x00(%rsp)
+	mov	$A,$a1
+	vmovdqa	$t1,0x10(%rsp)
+	mov	$B,$a3
+	vmovdqa	$t2,0x20(%rsp)
+	xor	$C,$a3			# magic
+	vmovdqa	$t3,0x30(%rsp)
+	mov	$E,$a0
+	jmp	.Lxop_00_47
+
+.align	16
+.Lxop_00_47:
+	sub	\$`-16*2*$SZ`,$Tbl	# size optimization
+___
+sub XOP_256_00_47 () {
+my $j = shift;
+my $body = shift;
+my @X = @_;
+my @insns = (&$body,&$body,&$body,&$body);	# 104 instructions
+
+	&vpalignr	($t0,@X[1],@X[0],$SZ);	# X[1..4]
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	 &vpalignr	($t3,@X[3],@X[2],$SZ);	# X[9..12]
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	&vprotd		($t1,$t0,8*$SZ-$sigma0[1]);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	&vpsrld		($t0,$t0,$sigma0[2]);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	 &vpaddd	(@X[0],@X[0],$t3);	# X[0..3] += X[9..12]
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	&vprotd		($t2,$t1,$sigma0[1]-$sigma0[0]);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	&vpxor		($t0,$t0,$t1);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	 &vprotd	($t3,@X[3],8*$SZ-$sigma1[1]);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	&vpxor		($t0,$t0,$t2);		# sigma0(X[1..4])
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	 &vpsrld	($t2,@X[3],$sigma1[2]);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	&vpaddd		(@X[0],@X[0],$t0);	# X[0..3] += sigma0(X[1..4])
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	 &vprotd	($t1,$t3,$sigma1[1]-$sigma1[0]);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	 &vpxor		($t3,$t3,$t2);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	 &vpxor		($t3,$t3,$t1);		# sigma1(X[14..15])
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	&vpsrldq	($t3,$t3,8);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	&vpaddd		(@X[0],@X[0],$t3);	# X[0..1] += sigma1(X[14..15])
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	 &vprotd	($t3,@X[0],8*$SZ-$sigma1[1]);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	 &vpsrld	($t2,@X[0],$sigma1[2]);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	 &vprotd	($t1,$t3,$sigma1[1]-$sigma1[0]);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	 &vpxor		($t3,$t3,$t2);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	 &vpxor		($t3,$t3,$t1);		# sigma1(X[16..17])
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	&vpslldq	($t3,$t3,8);		# 22 instructions
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	&vpaddd		(@X[0],@X[0],$t3);	# X[2..3] += sigma1(X[16..17])
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	&vpaddd		($t2,@X[0],16*2*$j."($Tbl)");
+	  foreach (@insns) { eval; }		# remaining instructions
+	&vmovdqa	(16*$j."(%rsp)",$t2);
+}
+
+    for ($i=0,$j=0; $j<4; $j++) {
+	&XOP_256_00_47($j,\&body_00_15,@X);
+	push(@X,shift(@X));			# rotate(@X)
+    }
+	&cmpb	($SZ-1+16*2*$SZ."($Tbl)",0);
+	&jne	(".Lxop_00_47");
+
+    for ($i=0; $i<16; ) {
+	foreach(body_00_15()) { eval; }
+    }
+
+					} else {	# SHA512
+    my @X = map("%xmm$_",(0..7));
+    my ($t0,$t1,$t2,$t3) = map("%xmm$_",(8..11));
+
+$code.=<<___;
+.align	16
+.Lloop_xop:
+	vmovdqa	$TABLE+`$SZ*2*$rounds`(%rip),$t3
+	vmovdqu	0x00($inp),@X[0]
+	lea	$TABLE+0x80(%rip),$Tbl	# size optimization
+	vmovdqu	0x10($inp),@X[1]
+	vmovdqu	0x20($inp),@X[2]
+	vpshufb	$t3,@X[0],@X[0]
+	vmovdqu	0x30($inp),@X[3]
+	vpshufb	$t3,@X[1],@X[1]
+	vmovdqu	0x40($inp),@X[4]
+	vpshufb	$t3,@X[2],@X[2]
+	vmovdqu	0x50($inp),@X[5]
+	vpshufb	$t3,@X[3],@X[3]
+	vmovdqu	0x60($inp),@X[6]
+	vpshufb	$t3,@X[4],@X[4]
+	vmovdqu	0x70($inp),@X[7]
+	vpshufb	$t3,@X[5],@X[5]
+	vpaddq	-0x80($Tbl),@X[0],$t0
+	vpshufb	$t3,@X[6],@X[6]
+	vpaddq	-0x60($Tbl),@X[1],$t1
+	vpshufb	$t3,@X[7],@X[7]
+	vpaddq	-0x40($Tbl),@X[2],$t2
+	vpaddq	-0x20($Tbl),@X[3],$t3
+	vmovdqa	$t0,0x00(%rsp)
+	vpaddq	0x00($Tbl),@X[4],$t0
+	vmovdqa	$t1,0x10(%rsp)
+	vpaddq	0x20($Tbl),@X[5],$t1
+	vmovdqa	$t2,0x20(%rsp)
+	vpaddq	0x40($Tbl),@X[6],$t2
+	vmovdqa	$t3,0x30(%rsp)
+	vpaddq	0x60($Tbl),@X[7],$t3
+	vmovdqa	$t0,0x40(%rsp)
+	mov	$A,$a1
+	vmovdqa	$t1,0x50(%rsp)
+	mov	$B,$a3
+	vmovdqa	$t2,0x60(%rsp)
+	xor	$C,$a3			# magic
+	vmovdqa	$t3,0x70(%rsp)
+	mov	$E,$a0
+	jmp	.Lxop_00_47
+
+.align	16
+.Lxop_00_47:
+	add	\$`16*2*$SZ`,$Tbl
+___
+sub XOP_512_00_47 () {
+my $j = shift;
+my $body = shift;
+my @X = @_;
+my @insns = (&$body,&$body);			# 52 instructions
+
+	&vpalignr	($t0,@X[1],@X[0],$SZ);	# X[1..2]
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	 &vpalignr	($t3,@X[5],@X[4],$SZ);	# X[9..10]
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	&vprotq		($t1,$t0,8*$SZ-$sigma0[1]);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	&vpsrlq		($t0,$t0,$sigma0[2]);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	 &vpaddq	(@X[0],@X[0],$t3);	# X[0..1] += X[9..10]
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	&vprotq		($t2,$t1,$sigma0[1]-$sigma0[0]);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	&vpxor		($t0,$t0,$t1);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	 &vprotq	($t3,@X[7],8*$SZ-$sigma1[1]);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	&vpxor		($t0,$t0,$t2);		# sigma0(X[1..2])
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	 &vpsrlq	($t2,@X[7],$sigma1[2]);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	&vpaddq		(@X[0],@X[0],$t0);	# X[0..1] += sigma0(X[1..2])
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	 &vprotq	($t1,$t3,$sigma1[1]-$sigma1[0]);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	 &vpxor		($t3,$t3,$t2);
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	 &vpxor		($t3,$t3,$t1);		# sigma1(X[14..15])
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	&vpaddq		(@X[0],@X[0],$t3);	# X[0..1] += sigma1(X[14..15])
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	  eval(shift(@insns));
+	&vpaddq		($t2,@X[0],16*2*$j-0x80."($Tbl)");
+	  foreach (@insns) { eval; }		# remaining instructions
+	&vmovdqa	(16*$j."(%rsp)",$t2);
+}
+
+    for ($i=0,$j=0; $j<8; $j++) {
+	&XOP_512_00_47($j,\&body_00_15,@X);
+	push(@X,shift(@X));			# rotate(@X)
+    }
+	&cmpb	($SZ-1+16*2*$SZ-0x80."($Tbl)",0);
+	&jne	(".Lxop_00_47");
+
+    for ($i=0; $i<16; ) {
+	foreach(body_00_15()) { eval; }
+    }
+}
+$code.=<<___;
+	mov	$_ctx,$ctx
+	mov	$a1,$A
+
+	add	$SZ*0($ctx),$A
+	lea	16*$SZ($inp),$inp
+	add	$SZ*1($ctx),$B
+	add	$SZ*2($ctx),$C
+	add	$SZ*3($ctx),$D
+	add	$SZ*4($ctx),$E
+	add	$SZ*5($ctx),$F
+	add	$SZ*6($ctx),$G
+	add	$SZ*7($ctx),$H
+
+	cmp	$_end,$inp
+
+	mov	$A,$SZ*0($ctx)
+	mov	$B,$SZ*1($ctx)
+	mov	$C,$SZ*2($ctx)
+	mov	$D,$SZ*3($ctx)
+	mov	$E,$SZ*4($ctx)
+	mov	$F,$SZ*5($ctx)
+	mov	$G,$SZ*6($ctx)
+	mov	$H,$SZ*7($ctx)
+	jb	.Lloop_xop
+
+	mov	$_rsp,%rsi
+	vzeroupper
+___
+$code.=<<___ if ($win64);
+	movaps	16*$SZ+32(%rsp),%xmm6
+	movaps	16*$SZ+48(%rsp),%xmm7
+	movaps	16*$SZ+64(%rsp),%xmm8
+	movaps	16*$SZ+80(%rsp),%xmm9
+___
+$code.=<<___ if ($win64 && $SZ>4);
+	movaps	16*$SZ+96(%rsp),%xmm10
+	movaps	16*$SZ+112(%rsp),%xmm11
+___
+$code.=<<___;
+	mov	(%rsi),%r15
+	mov	8(%rsi),%r14
+	mov	16(%rsi),%r13
+	mov	24(%rsi),%r12
+	mov	32(%rsi),%rbp
+	mov	40(%rsi),%rbx
+	lea	48(%rsi),%rsp
+.Lepilogue_xop:
+	ret
+.size	${func}_xop,.-${func}_xop
+___
+}
+######################################################################
+# AVX+shrd code path
+#
+local *ror = sub { &shrd(@_[0],@_) };
+
+$code.=<<___;
+.type	${func}_avx,\@function,3
+.align	64
+${func}_avx:
+.Lavx_shortcut:
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+	mov	%rsp,%r11		# copy %rsp
+	shl	\$4,%rdx		# num*16
+	sub	\$`$framesz+$win64*16*($SZ==4?4:6)`,%rsp
+	lea	($inp,%rdx,$SZ),%rdx	# inp+num*16*$SZ
+	and	\$-64,%rsp		# align stack frame
+	mov	$ctx,$_ctx		# save ctx, 1st arg
+	mov	$inp,$_inp		# save inp, 2nd arh
+	mov	%rdx,$_end		# save end pointer, "3rd" arg
+	mov	%r11,$_rsp		# save copy of %rsp
+___
+$code.=<<___ if ($win64);
+	movaps	%xmm6,16*$SZ+32(%rsp)
+	movaps	%xmm7,16*$SZ+48(%rsp)
+	movaps	%xmm8,16*$SZ+64(%rsp)
+	movaps	%xmm9,16*$SZ+80(%rsp)
+___
+$code.=<<___ if ($win64 && $SZ>4);
+	movaps	%xmm10,16*$SZ+96(%rsp)
+	movaps	%xmm11,16*$SZ+112(%rsp)
+___
+$code.=<<___;
+.Lprologue_avx:
+
+	vzeroupper
+	mov	$SZ*0($ctx),$A
+	mov	$SZ*1($ctx),$B
+	mov	$SZ*2($ctx),$C
+	mov	$SZ*3($ctx),$D
+	mov	$SZ*4($ctx),$E
+	mov	$SZ*5($ctx),$F
+	mov	$SZ*6($ctx),$G
+	mov	$SZ*7($ctx),$H
+___
+					if ($SZ==4) {	# SHA256
+    my @X = map("%xmm$_",(0..3));
+    my ($t0,$t1,$t2,$t3, $t4,$t5) = map("%xmm$_",(4..9));
+
+$code.=<<___;
+	vmovdqa	$TABLE+`$SZ*2*$rounds`+32(%rip),$t4
+	vmovdqa	$TABLE+`$SZ*2*$rounds`+64(%rip),$t5
+	jmp	.Lloop_avx
+.align	16
+.Lloop_avx:
+	vmovdqa	$TABLE+`$SZ*2*$rounds`(%rip),$t3
+	vmovdqu	0x00($inp),@X[0]
+	vmovdqu	0x10($inp),@X[1]
+	vmovdqu	0x20($inp),@X[2]
+	vmovdqu	0x30($inp),@X[3]
+	vpshufb	$t3,@X[0],@X[0]
+	lea	$TABLE(%rip),$Tbl
+	vpshufb	$t3,@X[1],@X[1]
+	vpshufb	$t3,@X[2],@X[2]
+	vpaddd	0x00($Tbl),@X[0],$t0
+	vpshufb	$t3,@X[3],@X[3]
+	vpaddd	0x20($Tbl),@X[1],$t1
+	vpaddd	0x40($Tbl),@X[2],$t2
+	vpaddd	0x60($Tbl),@X[3],$t3
+	vmovdqa	$t0,0x00(%rsp)
+	mov	$A,$a1
+	vmovdqa	$t1,0x10(%rsp)
+	mov	$B,$a3
+	vmovdqa	$t2,0x20(%rsp)
+	xor	$C,$a3			# magic
+	vmovdqa	$t3,0x30(%rsp)
+	mov	$E,$a0
+	jmp	.Lavx_00_47
+
+.align	16
+.Lavx_00_47:
+	sub	\$`-16*2*$SZ`,$Tbl	# size optimization
+___
+sub Xupdate_256_AVX () {
+	(
+	'&vpalignr	($t0,@X[1],@X[0],$SZ)',	# X[1..4]
+	 '&vpalignr	($t3,@X[3],@X[2],$SZ)',	# X[9..12]
+	'&vpsrld	($t2,$t0,$sigma0[0]);',
+	 '&vpaddd	(@X[0],@X[0],$t3)',	# X[0..3] += X[9..12]
+	'&vpsrld	($t3,$t0,$sigma0[2])',
+	'&vpslld	($t1,$t0,8*$SZ-$sigma0[1]);',
+	'&vpxor		($t0,$t3,$t2)',
+	 '&vpshufd	($t3,@X[3],0b11111010)',# X[14..15]
+	'&vpsrld	($t2,$t2,$sigma0[1]-$sigma0[0]);',
+	'&vpxor		($t0,$t0,$t1)',
+	'&vpslld	($t1,$t1,$sigma0[1]-$sigma0[0]);',
+	'&vpxor		($t0,$t0,$t2)',
+	 '&vpsrld	($t2,$t3,$sigma1[2]);',
+	'&vpxor		($t0,$t0,$t1)',		# sigma0(X[1..4])
+	 '&vpsrlq	($t3,$t3,$sigma1[0]);',
+	'&vpaddd	(@X[0],@X[0],$t0)',	# X[0..3] += sigma0(X[1..4])
+	 '&vpxor	($t2,$t2,$t3);',
+	 '&vpsrlq	($t3,$t3,$sigma1[1]-$sigma1[0])',
+	 '&vpxor	($t2,$t2,$t3)',
+	 '&vpshufb	($t2,$t2,$t4)',		# sigma1(X[14..15])
+	'&vpaddd	(@X[0],@X[0],$t2)',	# X[0..1] += sigma1(X[14..15])
+	 '&vpshufd	($t3,@X[0],0b01010000)',# X[16..17]
+	 '&vpsrld	($t2,$t3,$sigma1[2])',
+	 '&vpsrlq	($t3,$t3,$sigma1[0])',
+	 '&vpxor	($t2,$t2,$t3);',
+	 '&vpsrlq	($t3,$t3,$sigma1[1]-$sigma1[0])',
+	 '&vpxor	($t2,$t2,$t3)',
+	 '&vpshufb	($t2,$t2,$t5)',
+	'&vpaddd	(@X[0],@X[0],$t2)'	# X[2..3] += sigma1(X[16..17])
+	);
+}
+
+sub AVX_256_00_47 () {
+my $j = shift;
+my $body = shift;
+my @X = @_;
+my @insns = (&$body,&$body,&$body,&$body);	# 104 instructions
+
+	foreach (Xupdate_256_AVX()) {		# 29 instructions
+	    eval;
+	    eval(shift(@insns));
+	    eval(shift(@insns));
+	    eval(shift(@insns));
+	}
+	&vpaddd		($t2,@X[0],16*2*$j."($Tbl)");
+	  foreach (@insns) { eval; }		# remaining instructions
+	&vmovdqa	(16*$j."(%rsp)",$t2);
+}
+
+    for ($i=0,$j=0; $j<4; $j++) {
+	&AVX_256_00_47($j,\&body_00_15,@X);
+	push(@X,shift(@X));			# rotate(@X)
+    }
+	&cmpb	($SZ-1+16*2*$SZ."($Tbl)",0);
+	&jne	(".Lavx_00_47");
+
+    for ($i=0; $i<16; ) {
+	foreach(body_00_15()) { eval; }
+    }
+
+					} else {	# SHA512
+    my @X = map("%xmm$_",(0..7));
+    my ($t0,$t1,$t2,$t3) = map("%xmm$_",(8..11));
+
+$code.=<<___;
+	jmp	.Lloop_avx
+.align	16
+.Lloop_avx:
+	vmovdqa	$TABLE+`$SZ*2*$rounds`(%rip),$t3
+	vmovdqu	0x00($inp),@X[0]
+	lea	$TABLE+0x80(%rip),$Tbl	# size optimization
+	vmovdqu	0x10($inp),@X[1]
+	vmovdqu	0x20($inp),@X[2]
+	vpshufb	$t3,@X[0],@X[0]
+	vmovdqu	0x30($inp),@X[3]
+	vpshufb	$t3,@X[1],@X[1]
+	vmovdqu	0x40($inp),@X[4]
+	vpshufb	$t3,@X[2],@X[2]
+	vmovdqu	0x50($inp),@X[5]
+	vpshufb	$t3,@X[3],@X[3]
+	vmovdqu	0x60($inp),@X[6]
+	vpshufb	$t3,@X[4],@X[4]
+	vmovdqu	0x70($inp),@X[7]
+	vpshufb	$t3,@X[5],@X[5]
+	vpaddq	-0x80($Tbl),@X[0],$t0
+	vpshufb	$t3,@X[6],@X[6]
+	vpaddq	-0x60($Tbl),@X[1],$t1
+	vpshufb	$t3,@X[7],@X[7]
+	vpaddq	-0x40($Tbl),@X[2],$t2
+	vpaddq	-0x20($Tbl),@X[3],$t3
+	vmovdqa	$t0,0x00(%rsp)
+	vpaddq	0x00($Tbl),@X[4],$t0
+	vmovdqa	$t1,0x10(%rsp)
+	vpaddq	0x20($Tbl),@X[5],$t1
+	vmovdqa	$t2,0x20(%rsp)
+	vpaddq	0x40($Tbl),@X[6],$t2
+	vmovdqa	$t3,0x30(%rsp)
+	vpaddq	0x60($Tbl),@X[7],$t3
+	vmovdqa	$t0,0x40(%rsp)
+	mov	$A,$a1
+	vmovdqa	$t1,0x50(%rsp)
+	mov	$B,$a3
+	vmovdqa	$t2,0x60(%rsp)
+	xor	$C,$a3			# magic
+	vmovdqa	$t3,0x70(%rsp)
+	mov	$E,$a0
+	jmp	.Lavx_00_47
+
+.align	16
+.Lavx_00_47:
+	add	\$`16*2*$SZ`,$Tbl
+___
+sub Xupdate_512_AVX () {
+	(
+	'&vpalignr	($t0,@X[1],@X[0],$SZ)',	# X[1..2]
+	 '&vpalignr	($t3,@X[5],@X[4],$SZ)',	# X[9..10]
+	'&vpsrlq	($t2,$t0,$sigma0[0])',
+	 '&vpaddq	(@X[0],@X[0],$t3);',	# X[0..1] += X[9..10]
+	'&vpsrlq	($t3,$t0,$sigma0[2])',
+	'&vpsllq	($t1,$t0,8*$SZ-$sigma0[1]);',
+	 '&vpxor	($t0,$t3,$t2)',
+	'&vpsrlq	($t2,$t2,$sigma0[1]-$sigma0[0]);',
+	 '&vpxor	($t0,$t0,$t1)',
+	'&vpsllq	($t1,$t1,$sigma0[1]-$sigma0[0]);',
+	 '&vpxor	($t0,$t0,$t2)',
+	 '&vpsrlq	($t3,@X[7],$sigma1[2]);',
+	'&vpxor		($t0,$t0,$t1)',		# sigma0(X[1..2])
+	 '&vpsllq	($t2,@X[7],8*$SZ-$sigma1[1]);',
+	'&vpaddq	(@X[0],@X[0],$t0)',	# X[0..1] += sigma0(X[1..2])
+	 '&vpsrlq	($t1,@X[7],$sigma1[0]);',
+	 '&vpxor	($t3,$t3,$t2)',
+	 '&vpsllq	($t2,$t2,$sigma1[1]-$sigma1[0]);',
+	 '&vpxor	($t3,$t3,$t1)',
+	 '&vpsrlq	($t1,$t1,$sigma1[1]-$sigma1[0]);',
+	 '&vpxor	($t3,$t3,$t2)',
+	 '&vpxor	($t3,$t3,$t1)',		# sigma1(X[14..15])
+	'&vpaddq	(@X[0],@X[0],$t3)',	# X[0..1] += sigma1(X[14..15])
+	);
+}
+
+sub AVX_512_00_47 () {
+my $j = shift;
+my $body = shift;
+my @X = @_;
+my @insns = (&$body,&$body);			# 52 instructions
+
+	foreach (Xupdate_512_AVX()) {		# 23 instructions
+	    eval;
+	    eval(shift(@insns));
+	    eval(shift(@insns));
+	}
+	&vpaddq		($t2,@X[0],16*2*$j-0x80."($Tbl)");
+	  foreach (@insns) { eval; }		# remaining instructions
+	&vmovdqa	(16*$j."(%rsp)",$t2);
+}
+
+    for ($i=0,$j=0; $j<8; $j++) {
+	&AVX_512_00_47($j,\&body_00_15,@X);
+	push(@X,shift(@X));			# rotate(@X)
+    }
+	&cmpb	($SZ-1+16*2*$SZ-0x80."($Tbl)",0);
+	&jne	(".Lavx_00_47");
+
+    for ($i=0; $i<16; ) {
+	foreach(body_00_15()) { eval; }
+    }
+}
+$code.=<<___;
+	mov	$_ctx,$ctx
+	mov	$a1,$A
+
+	add	$SZ*0($ctx),$A
+	lea	16*$SZ($inp),$inp
+	add	$SZ*1($ctx),$B
+	add	$SZ*2($ctx),$C
+	add	$SZ*3($ctx),$D
+	add	$SZ*4($ctx),$E
+	add	$SZ*5($ctx),$F
+	add	$SZ*6($ctx),$G
+	add	$SZ*7($ctx),$H
+
+	cmp	$_end,$inp
+
+	mov	$A,$SZ*0($ctx)
+	mov	$B,$SZ*1($ctx)
+	mov	$C,$SZ*2($ctx)
+	mov	$D,$SZ*3($ctx)
+	mov	$E,$SZ*4($ctx)
+	mov	$F,$SZ*5($ctx)
+	mov	$G,$SZ*6($ctx)
+	mov	$H,$SZ*7($ctx)
+	jb	.Lloop_avx
+
+	mov	$_rsp,%rsi
+	vzeroupper
+___
+$code.=<<___ if ($win64);
+	movaps	16*$SZ+32(%rsp),%xmm6
+	movaps	16*$SZ+48(%rsp),%xmm7
+	movaps	16*$SZ+64(%rsp),%xmm8
+	movaps	16*$SZ+80(%rsp),%xmm9
+___
+$code.=<<___ if ($win64 && $SZ>4);
+	movaps	16*$SZ+96(%rsp),%xmm10
+	movaps	16*$SZ+112(%rsp),%xmm11
+___
+$code.=<<___;
+	mov	(%rsi),%r15
+	mov	8(%rsi),%r14
+	mov	16(%rsi),%r13
+	mov	24(%rsi),%r12
+	mov	32(%rsi),%rbp
+	mov	40(%rsi),%rbx
+	lea	48(%rsi),%rsp
+.Lepilogue_avx:
+	ret
+.size	${func}_avx,.-${func}_avx
+___
+
+if ($avx>1) {{
+######################################################################
+# AVX2+BMI code path
+#
+my $a5=$SZ==4?"%esi":"%rsi";	# zap $inp 
+my $PUSH8=8*2*$SZ;
+use integer;
+
+sub bodyx_00_15 () {
+	# at start $a1 should be zero, $a3 - $b^$c and $a4 copy of $f
+	(
+	'($a,$b,$c,$d,$e,$f,$g,$h)=@ROT;'.
+
+	'&add	($h,(32*($i/(16/$SZ))+$SZ*($i%(16/$SZ)))%$PUSH8.$base)',    # h+=X[i]+K[i]
+	'&and	($a4,$e)',		# f&e
+	'&rorx	($a0,$e,$Sigma1[2])',
+	'&rorx	($a2,$e,$Sigma1[1])',
+
+	'&lea	($a,"($a,$a1)")',	# h+=Sigma0(a) from the past
+	'&lea	($h,"($h,$a4)")',
+	'&andn	($a4,$e,$g)',		# ~e&g
+	'&xor	($a0,$a2)',
+
+	'&rorx	($a1,$e,$Sigma1[0])',
+	'&lea	($h,"($h,$a4)")',	# h+=Ch(e,f,g)=(e&f)+(~e&g)
+	'&xor	($a0,$a1)',		# Sigma1(e)
+	'&mov	($a2,$a)',
+
+	'&rorx	($a4,$a,$Sigma0[2])',
+	'&lea	($h,"($h,$a0)")',	# h+=Sigma1(e)
+	'&xor	($a2,$b)',		# a^b, b^c in next round
+	'&rorx	($a1,$a,$Sigma0[1])',
+
+	'&rorx	($a0,$a,$Sigma0[0])',
+	'&lea	($d,"($d,$h)")',	# d+=h
+	'&and	($a3,$a2)',		# (b^c)&(a^b)
+	'&xor	($a1,$a4)',
+
+	'&xor	($a3,$b)',		# Maj(a,b,c)=Ch(a^b,c,b)
+	'&xor	($a1,$a0)',		# Sigma0(a)
+	'&lea	($h,"($h,$a3)");'.	# h+=Maj(a,b,c)
+	'&mov	($a4,$e)',		# copy of f in future
+
+	'($a2,$a3) = ($a3,$a2); unshift(@ROT,pop(@ROT)); $i++;'
+	);
+	# and at the finish one has to $a+=$a1
+}
+
+$code.=<<___;
+.type	${func}_avx2,\@function,3
+.align	64
+${func}_avx2:
+.Lavx2_shortcut:
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+	mov	%rsp,%r11		# copy %rsp
+	sub	\$`2*$SZ*$rounds+4*8+$win64*16*($SZ==4?4:6)`,%rsp
+	shl	\$4,%rdx		# num*16
+	and	\$-256*$SZ,%rsp		# align stack frame
+	lea	($inp,%rdx,$SZ),%rdx	# inp+num*16*$SZ
+	add	\$`2*$SZ*($rounds-8)`,%rsp
+	mov	$ctx,$_ctx		# save ctx, 1st arg
+	mov	$inp,$_inp		# save inp, 2nd arh
+	mov	%rdx,$_end		# save end pointer, "3rd" arg
+	mov	%r11,$_rsp		# save copy of %rsp
+___
+$code.=<<___ if ($win64);
+	movaps	%xmm6,16*$SZ+32(%rsp)
+	movaps	%xmm7,16*$SZ+48(%rsp)
+	movaps	%xmm8,16*$SZ+64(%rsp)
+	movaps	%xmm9,16*$SZ+80(%rsp)
+___
+$code.=<<___ if ($win64 && $SZ>4);
+	movaps	%xmm10,16*$SZ+96(%rsp)
+	movaps	%xmm11,16*$SZ+112(%rsp)
+___
+$code.=<<___;
+.Lprologue_avx2:
+
+	vzeroupper
+	sub	\$-16*$SZ,$inp		# inp++, size optimization
+	mov	$SZ*0($ctx),$A
+	mov	$inp,%r12		# borrow $T1
+	mov	$SZ*1($ctx),$B
+	cmp	%rdx,$inp		# $_end
+	mov	$SZ*2($ctx),$C
+	cmove	%rsp,%r12		# next block or random data
+	mov	$SZ*3($ctx),$D
+	mov	$SZ*4($ctx),$E
+	mov	$SZ*5($ctx),$F
+	mov	$SZ*6($ctx),$G
+	mov	$SZ*7($ctx),$H
+___
+					if ($SZ==4) {	# SHA256
+    my @X = map("%ymm$_",(0..3));
+    my ($t0,$t1,$t2,$t3, $t4,$t5) = map("%ymm$_",(4..9));
+
+$code.=<<___;
+	vmovdqa	$TABLE+`$SZ*2*$rounds`+32(%rip),$t4
+	vmovdqa	$TABLE+`$SZ*2*$rounds`+64(%rip),$t5
+	jmp	.Loop_avx2
+.align	16
+.Loop_avx2:
+	vmovdqa	$TABLE+`$SZ*2*$rounds`(%rip),$t3
+	vmovdqu	-16*$SZ+0($inp),%xmm0
+	vmovdqu	-16*$SZ+16($inp),%xmm1
+	vmovdqu	-16*$SZ+32($inp),%xmm2
+	vmovdqu	-16*$SZ+48($inp),%xmm3
+	#mov		$inp,$_inp	# offload $inp
+	vinserti128	\$1,(%r12),@X[0],@X[0]
+	vinserti128	\$1,16(%r12),@X[1],@X[1]
+	vpshufb		$t3,@X[0],@X[0]
+	vinserti128	\$1,32(%r12),@X[2],@X[2]
+	vpshufb		$t3,@X[1],@X[1]
+	vinserti128	\$1,48(%r12),@X[3],@X[3]
+
+	lea	$TABLE(%rip),$Tbl
+	vpshufb	$t3,@X[2],@X[2]
+	vpaddd	0x00($Tbl),@X[0],$t0
+	vpshufb	$t3,@X[3],@X[3]
+	vpaddd	0x20($Tbl),@X[1],$t1
+	vpaddd	0x40($Tbl),@X[2],$t2
+	vpaddd	0x60($Tbl),@X[3],$t3
+	vmovdqa	$t0,0x00(%rsp)
+	xor	$a1,$a1
+	vmovdqa	$t1,0x20(%rsp)
+	lea	-$PUSH8(%rsp),%rsp
+	mov	$B,$a3
+	vmovdqa	$t2,0x00(%rsp)
+	xor	$C,$a3			# magic
+	vmovdqa	$t3,0x20(%rsp)
+	mov	$F,$a4
+	sub	\$-16*2*$SZ,$Tbl	# size optimization
+	jmp	.Lavx2_00_47
+
+.align	16
+.Lavx2_00_47:
+___
+
+sub AVX2_256_00_47 () {
+my $j = shift;
+my $body = shift;
+my @X = @_;
+my @insns = (&$body,&$body,&$body,&$body);	# 96 instructions
+my $base = "+2*$PUSH8(%rsp)";
+
+	&lea	("%rsp","-$PUSH8(%rsp)")	if (($j%2)==0);
+	foreach (Xupdate_256_AVX()) {		# 29 instructions
+	    eval;
+	    eval(shift(@insns));
+	    eval(shift(@insns));
+	    eval(shift(@insns));
+	}
+	&vpaddd		($t2,@X[0],16*2*$j."($Tbl)");
+	  foreach (@insns) { eval; }		# remaining instructions
+	&vmovdqa	((32*$j)%$PUSH8."(%rsp)",$t2);
+}
+
+    for ($i=0,$j=0; $j<4; $j++) {
+	&AVX2_256_00_47($j,\&bodyx_00_15,@X);
+	push(@X,shift(@X));			# rotate(@X)
+    }
+	&lea	($Tbl,16*2*$SZ."($Tbl)");
+	&cmpb	(($SZ-1)."($Tbl)",0);
+	&jne	(".Lavx2_00_47");
+
+    for ($i=0; $i<16; ) {
+	my $base=$i<8?"+$PUSH8(%rsp)":"(%rsp)";
+	foreach(bodyx_00_15()) { eval; }
+    }
+					} else {	# SHA512
+    my @X = map("%ymm$_",(0..7));
+    my ($t0,$t1,$t2,$t3) = map("%ymm$_",(8..11));
+
+$code.=<<___;
+	jmp	.Loop_avx2
+.align	16
+.Loop_avx2:
+	vmovdqu	-16*$SZ($inp),%xmm0
+	vmovdqu	-16*$SZ+16($inp),%xmm1
+	vmovdqu	-16*$SZ+32($inp),%xmm2
+	lea	$TABLE+0x80(%rip),$Tbl	# size optimization
+	vmovdqu	-16*$SZ+48($inp),%xmm3
+	vmovdqu	-16*$SZ+64($inp),%xmm4
+	vmovdqu	-16*$SZ+80($inp),%xmm5
+	vmovdqu	-16*$SZ+96($inp),%xmm6
+	vmovdqu	-16*$SZ+112($inp),%xmm7
+	#mov	$inp,$_inp	# offload $inp
+	vmovdqa	`$SZ*2*$rounds-0x80`($Tbl),$t2
+	vinserti128	\$1,(%r12),@X[0],@X[0]
+	vinserti128	\$1,16(%r12),@X[1],@X[1]
+	 vpshufb	$t2,@X[0],@X[0]
+	vinserti128	\$1,32(%r12),@X[2],@X[2]
+	 vpshufb	$t2,@X[1],@X[1]
+	vinserti128	\$1,48(%r12),@X[3],@X[3]
+	 vpshufb	$t2,@X[2],@X[2]
+	vinserti128	\$1,64(%r12),@X[4],@X[4]
+	 vpshufb	$t2,@X[3],@X[3]
+	vinserti128	\$1,80(%r12),@X[5],@X[5]
+	 vpshufb	$t2,@X[4],@X[4]
+	vinserti128	\$1,96(%r12),@X[6],@X[6]
+	 vpshufb	$t2,@X[5],@X[5]
+	vinserti128	\$1,112(%r12),@X[7],@X[7]
+
+	vpaddq	-0x80($Tbl),@X[0],$t0
+	vpshufb	$t2,@X[6],@X[6]
+	vpaddq	-0x60($Tbl),@X[1],$t1
+	vpshufb	$t2,@X[7],@X[7]
+	vpaddq	-0x40($Tbl),@X[2],$t2
+	vpaddq	-0x20($Tbl),@X[3],$t3
+	vmovdqa	$t0,0x00(%rsp)
+	vpaddq	0x00($Tbl),@X[4],$t0
+	vmovdqa	$t1,0x20(%rsp)
+	vpaddq	0x20($Tbl),@X[5],$t1
+	vmovdqa	$t2,0x40(%rsp)
+	vpaddq	0x40($Tbl),@X[6],$t2
+	vmovdqa	$t3,0x60(%rsp)
+	lea	-$PUSH8(%rsp),%rsp
+	vpaddq	0x60($Tbl),@X[7],$t3
+	vmovdqa	$t0,0x00(%rsp)
+	xor	$a1,$a1
+	vmovdqa	$t1,0x20(%rsp)
+	mov	$B,$a3
+	vmovdqa	$t2,0x40(%rsp)
+	xor	$C,$a3			# magic
+	vmovdqa	$t3,0x60(%rsp)
+	mov	$F,$a4
+	add	\$16*2*$SZ,$Tbl
+	jmp	.Lavx2_00_47
+
+.align	16
+.Lavx2_00_47:
+___
+
+sub AVX2_512_00_47 () {
+my $j = shift;
+my $body = shift;
+my @X = @_;
+my @insns = (&$body,&$body);			# 48 instructions
+my $base = "+2*$PUSH8(%rsp)";
+
+	&lea	("%rsp","-$PUSH8(%rsp)")	if (($j%4)==0);
+	foreach (Xupdate_512_AVX()) {		# 23 instructions
+	    eval;
+	    if ($_ !~ /\;$/) {
+		eval(shift(@insns));
+		eval(shift(@insns));
+		eval(shift(@insns));
+	    }
+	}
+	&vpaddq		($t2,@X[0],16*2*$j-0x80."($Tbl)");
+	  foreach (@insns) { eval; }		# remaining instructions
+	&vmovdqa	((32*$j)%$PUSH8."(%rsp)",$t2);
+}
+
+    for ($i=0,$j=0; $j<8; $j++) {
+	&AVX2_512_00_47($j,\&bodyx_00_15,@X);
+	push(@X,shift(@X));			# rotate(@X)
+    }
+	&lea	($Tbl,16*2*$SZ."($Tbl)");
+	&cmpb	(($SZ-1-0x80)."($Tbl)",0);
+	&jne	(".Lavx2_00_47");
+
+    for ($i=0; $i<16; ) {
+	my $base=$i<8?"+$PUSH8(%rsp)":"(%rsp)";
+	foreach(bodyx_00_15()) { eval; }
+    }
+}
+$code.=<<___;
+	mov	`2*$SZ*$rounds`(%rsp),$ctx	# $_ctx
+	add	$a1,$A
+	#mov	`2*$SZ*$rounds+8`(%rsp),$inp	# $_inp
+	lea	`2*$SZ*($rounds-8)`(%rsp),$Tbl
+
+	add	$SZ*0($ctx),$A
+	add	$SZ*1($ctx),$B
+	add	$SZ*2($ctx),$C
+	add	$SZ*3($ctx),$D
+	add	$SZ*4($ctx),$E
+	add	$SZ*5($ctx),$F
+	add	$SZ*6($ctx),$G
+	add	$SZ*7($ctx),$H
+
+	mov	$A,$SZ*0($ctx)
+	mov	$B,$SZ*1($ctx)
+	mov	$C,$SZ*2($ctx)
+	mov	$D,$SZ*3($ctx)
+	mov	$E,$SZ*4($ctx)
+	mov	$F,$SZ*5($ctx)
+	mov	$G,$SZ*6($ctx)
+	mov	$H,$SZ*7($ctx)
+
+	cmp	`$PUSH8+2*8`($Tbl),$inp	# $_end
+	je	.Ldone_avx2
+
+	xor	$a1,$a1
+	mov	$B,$a3
+	xor	$C,$a3			# magic
+	mov	$F,$a4
+	jmp	.Lower_avx2
+.align	16
+.Lower_avx2:
+___
+    for ($i=0; $i<8; ) {
+	my $base="+16($Tbl)";
+	foreach(bodyx_00_15()) { eval; }
+    }
+$code.=<<___;
+	lea	-$PUSH8($Tbl),$Tbl
+	cmp	%rsp,$Tbl
+	jae	.Lower_avx2
+
+	mov	`2*$SZ*$rounds`(%rsp),$ctx	# $_ctx
+	add	$a1,$A
+	#mov	`2*$SZ*$rounds+8`(%rsp),$inp	# $_inp
+	lea	`2*$SZ*($rounds-8)`(%rsp),%rsp
+
+	add	$SZ*0($ctx),$A
+	add	$SZ*1($ctx),$B
+	add	$SZ*2($ctx),$C
+	add	$SZ*3($ctx),$D
+	add	$SZ*4($ctx),$E
+	add	$SZ*5($ctx),$F
+	lea	`2*16*$SZ`($inp),$inp	# inp+=2
+	add	$SZ*6($ctx),$G
+	mov	$inp,%r12
+	add	$SZ*7($ctx),$H
+	cmp	$_end,$inp
+
+	mov	$A,$SZ*0($ctx)
+	cmove	%rsp,%r12		# next block or stale data
+	mov	$B,$SZ*1($ctx)
+	mov	$C,$SZ*2($ctx)
+	mov	$D,$SZ*3($ctx)
+	mov	$E,$SZ*4($ctx)
+	mov	$F,$SZ*5($ctx)
+	mov	$G,$SZ*6($ctx)
+	mov	$H,$SZ*7($ctx)
+
+	jbe	.Loop_avx2
+	lea	(%rsp),$Tbl
+
+.Ldone_avx2:
+	lea	($Tbl),%rsp
+	mov	$_rsp,%rsi
+	vzeroupper
+___
+$code.=<<___ if ($win64);
+	movaps	16*$SZ+32(%rsp),%xmm6
+	movaps	16*$SZ+48(%rsp),%xmm7
+	movaps	16*$SZ+64(%rsp),%xmm8
+	movaps	16*$SZ+80(%rsp),%xmm9
+___
+$code.=<<___ if ($win64 && $SZ>4);
+	movaps	16*$SZ+96(%rsp),%xmm10
+	movaps	16*$SZ+112(%rsp),%xmm11
+___
+$code.=<<___;
+	mov	(%rsi),%r15
+	mov	8(%rsi),%r14
+	mov	16(%rsi),%r13
+	mov	24(%rsi),%r12
+	mov	32(%rsi),%rbp
+	mov	40(%rsi),%rbx
+	lea	48(%rsi),%rsp
+.Lepilogue_avx2:
+	ret
+.size	${func}_avx2,.-${func}_avx2
+___
+}}
+}}}}}
+
+# EXCEPTION_DISPOSITION handler (EXCEPTION_RECORD *rec,ULONG64 frame,
+#		CONTEXT *context,DISPATCHER_CONTEXT *disp)
+if ($win64) {
+$rec="%rcx";
+$frame="%rdx";
+$context="%r8";
+$disp="%r9";
+
+$code.=<<___;
+.extern	__imp_RtlVirtualUnwind
+.type	se_handler,\@abi-omnipotent
+.align	16
+se_handler:
+	push	%rsi
+	push	%rdi
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+	pushfq
+	sub	\$64,%rsp
+
+	mov	120($context),%rax	# pull context->Rax
+	mov	248($context),%rbx	# pull context->Rip
+
+	mov	8($disp),%rsi		# disp->ImageBase
+	mov	56($disp),%r11		# disp->HanderlData
+
+	mov	0(%r11),%r10d		# HandlerData[0]
+	lea	(%rsi,%r10),%r10	# prologue label
+	cmp	%r10,%rbx		# context->RipRsp
+
+	mov	4(%r11),%r10d		# HandlerData[1]
+	lea	(%rsi,%r10),%r10	# epilogue label
+	cmp	%r10,%rbx		# context->Rip>=epilogue label
+	jae	.Lin_prologue
+___
+$code.=<<___ if ($avx>1);
+	lea	.Lavx2_shortcut(%rip),%r10
+	cmp	%r10,%rbx		# context->RipRbx
+	mov	%rbp,160($context)	# restore context->Rbp
+	mov	%r12,216($context)	# restore context->R12
+	mov	%r13,224($context)	# restore context->R13
+	mov	%r14,232($context)	# restore context->R14
+	mov	%r15,240($context)	# restore context->R15
+
+	lea	.Lepilogue(%rip),%r10
+	cmp	%r10,%rbx
+	jb	.Lin_prologue		# non-AVX code
+
+	lea	16*$SZ+4*8(%rsi),%rsi	# Xmm6- save area
+	lea	512($context),%rdi	# &context.Xmm6
+	mov	\$`$SZ==4?8:12`,%ecx
+	.long	0xa548f3fc		# cld; rep movsq
+
+.Lin_prologue:
+	mov	8(%rax),%rdi
+	mov	16(%rax),%rsi
+	mov	%rax,152($context)	# restore context->Rsp
+	mov	%rsi,168($context)	# restore context->Rsi
+	mov	%rdi,176($context)	# restore context->Rdi
+
+	mov	40($disp),%rdi		# disp->ContextRecord
+	mov	$context,%rsi		# context
+	mov	\$154,%ecx		# sizeof(CONTEXT)
+	.long	0xa548f3fc		# cld; rep movsq
+
+	mov	$disp,%rsi
+	xor	%rcx,%rcx		# arg1, UNW_FLAG_NHANDLER
+	mov	8(%rsi),%rdx		# arg2, disp->ImageBase
+	mov	0(%rsi),%r8		# arg3, disp->ControlPc
+	mov	16(%rsi),%r9		# arg4, disp->FunctionEntry
+	mov	40(%rsi),%r10		# disp->ContextRecord
+	lea	56(%rsi),%r11		# &disp->HandlerData
+	lea	24(%rsi),%r12		# &disp->EstablisherFrame
+	mov	%r10,32(%rsp)		# arg5
+	mov	%r11,40(%rsp)		# arg6
+	mov	%r12,48(%rsp)		# arg7
+	mov	%rcx,56(%rsp)		# arg8, (NULL)
+	call	*__imp_RtlVirtualUnwind(%rip)
+
+	mov	\$1,%eax		# ExceptionContinueSearch
+	add	\$64,%rsp
+	popfq
+	pop	%r15
+	pop	%r14
+	pop	%r13
+	pop	%r12
+	pop	%rbp
+	pop	%rbx
+	pop	%rdi
+	pop	%rsi
+	ret
+.size	se_handler,.-se_handler
+___
+
+$code.=<<___ if ($SZ==4 && $shaext);
+.type	shaext_handler,\@abi-omnipotent
+.align	16
+shaext_handler:
+	push	%rsi
+	push	%rdi
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+	pushfq
+	sub	\$64,%rsp
+
+	mov	120($context),%rax	# pull context->Rax
+	mov	248($context),%rbx	# pull context->Rip
+
+	lea	.Lprologue_shaext(%rip),%r10
+	cmp	%r10,%rbx		# context->Rip<.Lprologue
+	jb	.Lin_prologue
+
+	lea	.Lepilogue_shaext(%rip),%r10
+	cmp	%r10,%rbx		# context->Rip>=.Lepilogue
+	jae	.Lin_prologue
+
+	lea	-8-5*16(%rax),%rsi
+	lea	512($context),%rdi	# &context.Xmm6
+	mov	\$10,%ecx
+	.long	0xa548f3fc		# cld; rep movsq
+
+	jmp	.Lin_prologue
+.size	shaext_handler,.-shaext_handler
+___
+
+$code.=<<___;
+.section	.pdata
+.align	4
+	.rva	.LSEH_begin_$func
+	.rva	.LSEH_end_$func
+	.rva	.LSEH_info_$func
+___
+$code.=<<___ if ($SZ==4 && $shaext);
+	.rva	.LSEH_begin_${func}_shaext
+	.rva	.LSEH_end_${func}_shaext
+	.rva	.LSEH_info_${func}_shaext
+___
+$code.=<<___ if ($SZ==4);
+	.rva	.LSEH_begin_${func}_ssse3
+	.rva	.LSEH_end_${func}_ssse3
+	.rva	.LSEH_info_${func}_ssse3
+___
+$code.=<<___ if ($avx && $SZ==8);
+	.rva	.LSEH_begin_${func}_xop
+	.rva	.LSEH_end_${func}_xop
+	.rva	.LSEH_info_${func}_xop
+___
+$code.=<<___ if ($avx);
+	.rva	.LSEH_begin_${func}_avx
+	.rva	.LSEH_end_${func}_avx
+	.rva	.LSEH_info_${func}_avx
+___
+$code.=<<___ if ($avx>1);
+	.rva	.LSEH_begin_${func}_avx2
+	.rva	.LSEH_end_${func}_avx2
+	.rva	.LSEH_info_${func}_avx2
+___
+$code.=<<___;
+.section	.xdata
+.align	8
+.LSEH_info_$func:
+	.byte	9,0,0,0
+	.rva	se_handler
+	.rva	.Lprologue,.Lepilogue			# HandlerData[]
+___
+$code.=<<___ if ($SZ==4 && $shaext);
+.LSEH_info_${func}_shaext:
+	.byte	9,0,0,0
+	.rva	shaext_handler
+___
+$code.=<<___ if ($SZ==4);
+.LSEH_info_${func}_ssse3:
+	.byte	9,0,0,0
+	.rva	se_handler
+	.rva	.Lprologue_ssse3,.Lepilogue_ssse3	# HandlerData[]
+___
+$code.=<<___ if ($avx && $SZ==8);
+.LSEH_info_${func}_xop:
+	.byte	9,0,0,0
+	.rva	se_handler
+	.rva	.Lprologue_xop,.Lepilogue_xop		# HandlerData[]
+___
+$code.=<<___ if ($avx);
+.LSEH_info_${func}_avx:
+	.byte	9,0,0,0
+	.rva	se_handler
+	.rva	.Lprologue_avx,.Lepilogue_avx		# HandlerData[]
+___
+$code.=<<___ if ($avx>1);
+.LSEH_info_${func}_avx2:
+	.byte	9,0,0,0
+	.rva	se_handler
+	.rva	.Lprologue_avx2,.Lepilogue_avx2		# HandlerData[]
+___
+}
+
+sub sha256op38 {
+    my $instr = shift;
+    my %opcodelet = (
+		"sha256rnds2" => 0xcb,
+  		"sha256msg1"  => 0xcc,
+		"sha256msg2"  => 0xcd	);
+
+    if (defined($opcodelet{$instr}) && @_[0] =~ /%xmm([0-7]),\s*%xmm([0-7])/) {
+      my @opcode=(0x0f,0x38);
+	push @opcode,$opcodelet{$instr};
+	push @opcode,0xc0|($1&7)|(($2&7)<<3);		# ModR/M
+	return ".byte\t".join(',',@opcode);
+    } else {
+	return $instr."\t".@_[0];
+    }
+}
+
+foreach (split("\n",$code)) {
+	s/\`([^\`]*)\`/eval $1/geo;
+
+	s/\b(sha256[^\s]*)\s+(.*)/sha256op38($1,$2)/geo;
+
+	print $_,"\n";
+}
+close STDOUT;
diff --git a/crypto/sha/asm/sha512p8-ppc.pl b/crypto/sha/asm/sha512p8-ppc.pl
new file mode 100755
index 000000000..4d3d3b2f8
--- /dev/null
+++ b/crypto/sha/asm/sha512p8-ppc.pl
@@ -0,0 +1,431 @@
+#! /usr/bin/env perl
+# Copyright 2014-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+
+# ====================================================================
+# Written by Andy Polyakov  for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+# ====================================================================
+
+# SHA256/512 for PowerISA v2.07.
+#
+# Accurate performance measurements are problematic, because it's
+# always virtualized setup with possibly throttled processor.
+# Relative comparison is therefore more informative. This module is
+# ~60% faster than integer-only sha512-ppc.pl. To anchor to something
+# else, SHA256 is 24% slower than sha1-ppc.pl and 2.5x slower than
+# hardware-assisted aes-128-cbc encrypt. SHA512 is 20% faster than
+# sha1-ppc.pl and 1.6x slower than aes-128-cbc. Another interesting
+# result is degree of computational resources' utilization. POWER8 is
+# "massively multi-threaded chip" and difference between single- and
+# maximum multi-process benchmark results tells that utlization is
+# whooping 94%. For sha512-ppc.pl we get [not unimpressive] 84% and
+# for sha1-ppc.pl - 73%. 100% means that multi-process result equals
+# to single-process one, given that all threads end up on the same
+# physical core.
+
+$flavour=shift;
+$output =shift;
+
+if ($flavour =~ /64/) {
+	$SIZE_T=8;
+	$LRSAVE=2*$SIZE_T;
+	$STU="stdu";
+	$POP="ld";
+	$PUSH="std";
+} elsif ($flavour =~ /32/) {
+	$SIZE_T=4;
+	$LRSAVE=$SIZE_T;
+	$STU="stwu";
+	$POP="lwz";
+	$PUSH="stw";
+} else { die "nonsense $flavour"; }
+
+$LENDIAN=($flavour=~/le/);
+
+$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+( $xlate="${dir}ppc-xlate.pl" and -f $xlate ) or
+( $xlate="${dir}../../perlasm/ppc-xlate.pl" and -f $xlate) or
+die "can't locate ppc-xlate.pl";
+
+open STDOUT,"| $^X $xlate $flavour $output" || die "can't call $xlate: $!";
+
+if ($output =~ /512/) {
+	$bits=512;
+	$SZ=8;
+	$sz="d";
+	$rounds=80;
+} else {
+	$bits=256;
+	$SZ=4;
+	$sz="w";
+	$rounds=64;
+}
+
+$func="sha${bits}_block_p8";
+$FRAME=8*$SIZE_T;
+
+$sp ="r1";
+$toc="r2";
+$ctx="r3";
+$inp="r4";
+$num="r5";
+$Tbl="r6";
+$idx="r7";
+$lrsave="r8";
+$offload="r11";
+$vrsave="r12";
+($x00,$x10,$x20,$x30,$x40,$x50,$x60,$x70)=map("r$_",(0,10,26..31));
+ $x00=0 if ($flavour =~ /osx/);
+
+@V=($A,$B,$C,$D,$E,$F,$G,$H)=map("v$_",(0..7));
+@X=map("v$_",(8..23));
+($Ki,$Func,$S0,$S1,$s0,$s1,$lemask)=map("v$_",(24..31));
+
+sub ROUND {
+my ($i,$a,$b,$c,$d,$e,$f,$g,$h)=@_;
+my $j=($i+1)%16;
+
+$code.=<<___		if ($i<15 && ($i%(16/$SZ))==(16/$SZ-1));
+	lvx_u		@X[$i+1],0,$inp		; load X[i] in advance
+	addi		$inp,$inp,16
+___
+$code.=<<___		if ($i<16 && ($i%(16/$SZ)));
+	vsldoi		@X[$i],@X[$i-1],@X[$i-1],$SZ
+___
+$code.=<<___		if ($LENDIAN && $i<16 && ($i%(16/$SZ))==0);
+	vperm		@X[$i],@X[$i],@X[$i],$lemask
+___
+$code.=<<___;
+	`"vshasigma${sz}	$s0,@X[($j+1)%16],0,0"		if ($i>=15)`
+	vsel		$Func,$g,$f,$e		; Ch(e,f,g)
+	vshasigma${sz}	$S1,$e,1,15		; Sigma1(e)
+	vaddu${sz}m	$h,$h,@X[$i%16]		; h+=X[i]
+	vshasigma${sz}	$S0,$a,1,0		; Sigma0(a)
+	`"vshasigma${sz}	$s1,@X[($j+14)%16],0,15"	if ($i>=15)`
+	vaddu${sz}m	$h,$h,$Func		; h+=Ch(e,f,g)
+	vxor		$Func,$a,$b
+	`"vaddu${sz}m		@X[$j],@X[$j],@X[($j+9)%16]"	if ($i>=15)`
+	vaddu${sz}m	$h,$h,$S1		; h+=Sigma1(e)
+	vsel		$Func,$b,$c,$Func	; Maj(a,b,c)
+	vaddu${sz}m	$g,$g,$Ki		; future h+=K[i]
+	vaddu${sz}m	$d,$d,$h		; d+=h
+	vaddu${sz}m	$S0,$S0,$Func		; Sigma0(a)+Maj(a,b,c)
+	`"vaddu${sz}m		@X[$j],@X[$j],$s0"		if ($i>=15)`
+	lvx		$Ki,$idx,$Tbl		; load next K[i]
+	addi		$idx,$idx,16
+	vaddu${sz}m	$h,$h,$S0		; h+=Sigma0(a)+Maj(a,b,c)
+	`"vaddu${sz}m		@X[$j],@X[$j],$s1"		if ($i>=15)`
+___
+}
+
+$code=<<___;
+.machine	"any"
+.text
+
+.globl	$func
+.align	6
+$func:
+	$STU		$sp,-`($FRAME+21*16+6*$SIZE_T)`($sp)
+	mflr		$lrsave
+	li		r10,`$FRAME+8*16+15`
+	li		r11,`$FRAME+8*16+31`
+	stvx		v20,r10,$sp		# ABI says so
+	addi		r10,r10,32
+	mfspr		$vrsave,256
+	stvx		v21,r11,$sp
+	addi		r11,r11,32
+	stvx		v22,r10,$sp
+	addi		r10,r10,32
+	stvx		v23,r11,$sp
+	addi		r11,r11,32
+	stvx		v24,r10,$sp
+	addi		r10,r10,32
+	stvx		v25,r11,$sp
+	addi		r11,r11,32
+	stvx		v26,r10,$sp
+	addi		r10,r10,32
+	stvx		v27,r11,$sp
+	addi		r11,r11,32
+	stvx		v28,r10,$sp
+	addi		r10,r10,32
+	stvx		v29,r11,$sp
+	addi		r11,r11,32
+	stvx		v30,r10,$sp
+	stvx		v31,r11,$sp
+	li		r11,-1
+	stw		$vrsave,`$FRAME+21*16-4`($sp)	# save vrsave
+	li		$x10,0x10
+	$PUSH		r26,`$FRAME+21*16+0*$SIZE_T`($sp)
+	li		$x20,0x20
+	$PUSH		r27,`$FRAME+21*16+1*$SIZE_T`($sp)
+	li		$x30,0x30
+	$PUSH		r28,`$FRAME+21*16+2*$SIZE_T`($sp)
+	li		$x40,0x40
+	$PUSH		r29,`$FRAME+21*16+3*$SIZE_T`($sp)
+	li		$x50,0x50
+	$PUSH		r30,`$FRAME+21*16+4*$SIZE_T`($sp)
+	li		$x60,0x60
+	$PUSH		r31,`$FRAME+21*16+5*$SIZE_T`($sp)
+	li		$x70,0x70
+	$PUSH		$lrsave,`$FRAME+21*16+6*$SIZE_T+$LRSAVE`($sp)
+	mtspr		256,r11
+
+	bl		LPICmeup
+	addi		$offload,$sp,$FRAME+15
+___
+$code.=<<___		if ($LENDIAN);
+	li		$idx,8
+	lvsl		$lemask,0,$idx
+	vspltisb	$Ki,0x0f
+	vxor		$lemask,$lemask,$Ki
+___
+$code.=<<___		if ($SZ==4);
+	lvx_4w		$A,$x00,$ctx
+	lvx_4w		$E,$x10,$ctx
+	vsldoi		$B,$A,$A,4		# unpack
+	vsldoi		$C,$A,$A,8
+	vsldoi		$D,$A,$A,12
+	vsldoi		$F,$E,$E,4
+	vsldoi		$G,$E,$E,8
+	vsldoi		$H,$E,$E,12
+___
+$code.=<<___		if ($SZ==8);
+	lvx_u		$A,$x00,$ctx
+	lvx_u		$C,$x10,$ctx
+	lvx_u		$E,$x20,$ctx
+	vsldoi		$B,$A,$A,8		# unpack
+	lvx_u		$G,$x30,$ctx
+	vsldoi		$D,$C,$C,8
+	vsldoi		$F,$E,$E,8
+	vsldoi		$H,$G,$G,8
+___
+$code.=<<___;
+	li		r0,`($rounds-16)/16`	# inner loop counter
+	b		Loop
+.align	5
+Loop:
+	lvx		$Ki,$x00,$Tbl
+	li		$idx,16
+	lvx_u		@X[0],0,$inp
+	addi		$inp,$inp,16
+	stvx		$A,$x00,$offload	# offload $A-$H
+	stvx		$B,$x10,$offload
+	stvx		$C,$x20,$offload
+	stvx		$D,$x30,$offload
+	stvx		$E,$x40,$offload
+	stvx		$F,$x50,$offload
+	stvx		$G,$x60,$offload
+	stvx		$H,$x70,$offload
+	vaddu${sz}m	$H,$H,$Ki		# h+K[i]
+	lvx		$Ki,$idx,$Tbl
+	addi		$idx,$idx,16
+___
+for ($i=0;$i<16;$i++)	{ &ROUND($i,@V); unshift(@V,pop(@V)); }
+$code.=<<___;
+	mtctr		r0
+	b		L16_xx
+.align	5
+L16_xx:
+___
+for (;$i<32;$i++)	{ &ROUND($i,@V); unshift(@V,pop(@V)); }
+$code.=<<___;
+	bdnz		L16_xx
+
+	lvx		@X[2],$x00,$offload
+	subic.		$num,$num,1
+	lvx		@X[3],$x10,$offload
+	vaddu${sz}m	$A,$A,@X[2]
+	lvx		@X[4],$x20,$offload
+	vaddu${sz}m	$B,$B,@X[3]
+	lvx		@X[5],$x30,$offload
+	vaddu${sz}m	$C,$C,@X[4]
+	lvx		@X[6],$x40,$offload
+	vaddu${sz}m	$D,$D,@X[5]
+	lvx		@X[7],$x50,$offload
+	vaddu${sz}m	$E,$E,@X[6]
+	lvx		@X[8],$x60,$offload
+	vaddu${sz}m	$F,$F,@X[7]
+	lvx		@X[9],$x70,$offload
+	vaddu${sz}m	$G,$G,@X[8]
+	vaddu${sz}m	$H,$H,@X[9]
+	bne		Loop
+___
+$code.=<<___		if ($SZ==4);
+	lvx		@X[0],$idx,$Tbl
+	addi		$idx,$idx,16
+	vperm		$A,$A,$B,$Ki		# pack the answer
+	lvx		@X[1],$idx,$Tbl
+	vperm		$E,$E,$F,$Ki
+	vperm		$A,$A,$C,@X[0]
+	vperm		$E,$E,$G,@X[0]
+	vperm		$A,$A,$D,@X[1]
+	vperm		$E,$E,$H,@X[1]
+	stvx_4w		$A,$x00,$ctx
+	stvx_4w		$E,$x10,$ctx
+___
+$code.=<<___		if ($SZ==8);
+	vperm		$A,$A,$B,$Ki		# pack the answer
+	vperm		$C,$C,$D,$Ki
+	vperm		$E,$E,$F,$Ki
+	vperm		$G,$G,$H,$Ki
+	stvx_u		$A,$x00,$ctx
+	stvx_u		$C,$x10,$ctx
+	stvx_u		$E,$x20,$ctx
+	stvx_u		$G,$x30,$ctx
+___
+$code.=<<___;
+	li		r10,`$FRAME+8*16+15`
+	mtlr		$lrsave
+	li		r11,`$FRAME+8*16+31`
+	mtspr		256,$vrsave
+	lvx		v20,r10,$sp		# ABI says so
+	addi		r10,r10,32
+	lvx		v21,r11,$sp
+	addi		r11,r11,32
+	lvx		v22,r10,$sp
+	addi		r10,r10,32
+	lvx		v23,r11,$sp
+	addi		r11,r11,32
+	lvx		v24,r10,$sp
+	addi		r10,r10,32
+	lvx		v25,r11,$sp
+	addi		r11,r11,32
+	lvx		v26,r10,$sp
+	addi		r10,r10,32
+	lvx		v27,r11,$sp
+	addi		r11,r11,32
+	lvx		v28,r10,$sp
+	addi		r10,r10,32
+	lvx		v29,r11,$sp
+	addi		r11,r11,32
+	lvx		v30,r10,$sp
+	lvx		v31,r11,$sp
+	$POP		r26,`$FRAME+21*16+0*$SIZE_T`($sp)
+	$POP		r27,`$FRAME+21*16+1*$SIZE_T`($sp)
+	$POP		r28,`$FRAME+21*16+2*$SIZE_T`($sp)
+	$POP		r29,`$FRAME+21*16+3*$SIZE_T`($sp)
+	$POP		r30,`$FRAME+21*16+4*$SIZE_T`($sp)
+	$POP		r31,`$FRAME+21*16+5*$SIZE_T`($sp)
+	addi		$sp,$sp,`$FRAME+21*16+6*$SIZE_T`
+	blr
+	.long		0
+	.byte		0,12,4,1,0x80,6,3,0
+	.long		0
+.size	$func,.-$func
+___
+
+# Ugly hack here, because PPC assembler syntax seem to vary too
+# much from platforms to platform...
+$code.=<<___;
+.align	6
+LPICmeup:
+	mflr	r0
+	bcl	20,31,\$+4
+	mflr	$Tbl	; vvvvvv "distance" between . and 1st data entry
+	addi	$Tbl,$Tbl,`64-8`
+	mtlr	r0
+	blr
+	.long	0
+	.byte	0,12,0x14,0,0,0,0,0
+	.space	`64-9*4`
+___
+
+if ($SZ==8) {
+    local *table = sub {
+	foreach(@_) { $code.=".quad	$_,$_\n"; }
+    };
+    table(
+	"0x428a2f98d728ae22","0x7137449123ef65cd",
+	"0xb5c0fbcfec4d3b2f","0xe9b5dba58189dbbc",
+	"0x3956c25bf348b538","0x59f111f1b605d019",
+	"0x923f82a4af194f9b","0xab1c5ed5da6d8118",
+	"0xd807aa98a3030242","0x12835b0145706fbe",
+	"0x243185be4ee4b28c","0x550c7dc3d5ffb4e2",
+	"0x72be5d74f27b896f","0x80deb1fe3b1696b1",
+	"0x9bdc06a725c71235","0xc19bf174cf692694",
+	"0xe49b69c19ef14ad2","0xefbe4786384f25e3",
+	"0x0fc19dc68b8cd5b5","0x240ca1cc77ac9c65",
+	"0x2de92c6f592b0275","0x4a7484aa6ea6e483",
+	"0x5cb0a9dcbd41fbd4","0x76f988da831153b5",
+	"0x983e5152ee66dfab","0xa831c66d2db43210",
+	"0xb00327c898fb213f","0xbf597fc7beef0ee4",
+	"0xc6e00bf33da88fc2","0xd5a79147930aa725",
+	"0x06ca6351e003826f","0x142929670a0e6e70",
+	"0x27b70a8546d22ffc","0x2e1b21385c26c926",
+	"0x4d2c6dfc5ac42aed","0x53380d139d95b3df",
+	"0x650a73548baf63de","0x766a0abb3c77b2a8",
+	"0x81c2c92e47edaee6","0x92722c851482353b",
+	"0xa2bfe8a14cf10364","0xa81a664bbc423001",
+	"0xc24b8b70d0f89791","0xc76c51a30654be30",
+	"0xd192e819d6ef5218","0xd69906245565a910",
+	"0xf40e35855771202a","0x106aa07032bbd1b8",
+	"0x19a4c116b8d2d0c8","0x1e376c085141ab53",
+	"0x2748774cdf8eeb99","0x34b0bcb5e19b48a8",
+	"0x391c0cb3c5c95a63","0x4ed8aa4ae3418acb",
+	"0x5b9cca4f7763e373","0x682e6ff3d6b2b8a3",
+	"0x748f82ee5defb2fc","0x78a5636f43172f60",
+	"0x84c87814a1f0ab72","0x8cc702081a6439ec",
+	"0x90befffa23631e28","0xa4506cebde82bde9",
+	"0xbef9a3f7b2c67915","0xc67178f2e372532b",
+	"0xca273eceea26619c","0xd186b8c721c0c207",
+	"0xeada7dd6cde0eb1e","0xf57d4f7fee6ed178",
+	"0x06f067aa72176fba","0x0a637dc5a2c898a6",
+	"0x113f9804bef90dae","0x1b710b35131c471b",
+	"0x28db77f523047d84","0x32caab7b40c72493",
+	"0x3c9ebe0a15c9bebc","0x431d67c49c100d4c",
+	"0x4cc5d4becb3e42b6","0x597f299cfc657e2a",
+	"0x5fcb6fab3ad6faec","0x6c44198c4a475817","0");
+$code.=<<___	if (!$LENDIAN);
+.quad	0x0001020304050607,0x1011121314151617
+___
+$code.=<<___	if ($LENDIAN);	# quad-swapped
+.quad	0x1011121314151617,0x0001020304050607
+___
+} else {
+    local *table = sub {
+	foreach(@_) { $code.=".long	$_,$_,$_,$_\n"; }
+    };
+    table(
+	"0x428a2f98","0x71374491","0xb5c0fbcf","0xe9b5dba5",
+	"0x3956c25b","0x59f111f1","0x923f82a4","0xab1c5ed5",
+	"0xd807aa98","0x12835b01","0x243185be","0x550c7dc3",
+	"0x72be5d74","0x80deb1fe","0x9bdc06a7","0xc19bf174",
+	"0xe49b69c1","0xefbe4786","0x0fc19dc6","0x240ca1cc",
+	"0x2de92c6f","0x4a7484aa","0x5cb0a9dc","0x76f988da",
+	"0x983e5152","0xa831c66d","0xb00327c8","0xbf597fc7",
+	"0xc6e00bf3","0xd5a79147","0x06ca6351","0x14292967",
+	"0x27b70a85","0x2e1b2138","0x4d2c6dfc","0x53380d13",
+	"0x650a7354","0x766a0abb","0x81c2c92e","0x92722c85",
+	"0xa2bfe8a1","0xa81a664b","0xc24b8b70","0xc76c51a3",
+	"0xd192e819","0xd6990624","0xf40e3585","0x106aa070",
+	"0x19a4c116","0x1e376c08","0x2748774c","0x34b0bcb5",
+	"0x391c0cb3","0x4ed8aa4a","0x5b9cca4f","0x682e6ff3",
+	"0x748f82ee","0x78a5636f","0x84c87814","0x8cc70208",
+	"0x90befffa","0xa4506ceb","0xbef9a3f7","0xc67178f2","0");
+$code.=<<___	if (!$LENDIAN);
+.long	0x00010203,0x10111213,0x10111213,0x10111213
+.long	0x00010203,0x04050607,0x10111213,0x10111213
+.long	0x00010203,0x04050607,0x08090a0b,0x10111213
+___
+$code.=<<___	if ($LENDIAN);	# word-swapped
+.long	0x10111213,0x10111213,0x10111213,0x00010203
+.long	0x10111213,0x10111213,0x04050607,0x00010203
+.long	0x10111213,0x08090a0b,0x04050607,0x00010203
+___
+}
+$code.=<<___;
+.asciz	"SHA${bits} for PowerISA 2.07, CRYPTOGAMS by "
+.align	2
+___
+
+$code =~ s/\`([^\`]*)\`/eval $1/gem;
+print $code;
+close STDOUT;
diff --git a/crypto/sha/build.info b/crypto/sha/build.info
new file mode 100644
index 000000000..5843e5089
--- /dev/null
+++ b/crypto/sha/build.info
@@ -0,0 +1,69 @@
+LIBS=../../libcrypto
+SOURCE[../../libcrypto]=\
+        sha1dgst.c sha1_one.c sha256.c sha512.c {- $target{sha1_asm_src} -}
+
+GENERATE[sha1-586.s]=asm/sha1-586.pl $(PERLASM_SCHEME) $(CFLAGS) $(LIB_CFLAGS) $(PROCESSOR)
+DEPEND[sha1-586.s]=../perlasm/x86asm.pl
+GENERATE[sha256-586.s]=asm/sha256-586.pl $(PERLASM_SCHEME) $(CFLAGS) $(LIB_CFLAGS) $(PROCESSOR)
+DEPEND[sha256-586.s]=../perlasm/x86asm.pl
+GENERATE[sha512-586.s]=asm/sha512-586.pl $(PERLASM_SCHEME) $(CFLAGS) $(LIB_CFLAGS) $(PROCESSOR)
+DEPEND[sha512-586.s]=../perlasm/x86asm.pl
+
+GENERATE[sha1-ia64.s]=asm/sha1-ia64.pl $(CFLAGS) $(LIB_CFLAGS)
+GENERATE[sha256-ia64.s]=asm/sha512-ia64.pl $(CFLAGS) $(LIB_CFLAGS)
+GENERATE[sha512-ia64.s]=asm/sha512-ia64.pl $(CFLAGS) $(LIB_CFLAGS)
+
+GENERATE[sha1-alpha.S]=asm/sha1-alpha.pl $(PERLASM_SCHEME)
+
+GENERATE[sha1-x86_64.s]=asm/sha1-x86_64.pl $(PERLASM_SCHEME)
+GENERATE[sha1-mb-x86_64.s]=asm/sha1-mb-x86_64.pl $(PERLASM_SCHEME)
+GENERATE[sha256-x86_64.s]=asm/sha512-x86_64.pl $(PERLASM_SCHEME)
+GENERATE[sha256-mb-x86_64.s]=asm/sha256-mb-x86_64.pl $(PERLASM_SCHEME)
+GENERATE[sha512-x86_64.s]=asm/sha512-x86_64.pl $(PERLASM_SCHEME)
+
+GENERATE[sha1-sparcv9.S]=asm/sha1-sparcv9.pl $(PERLASM_SCHEME)
+INCLUDE[sha1-sparcv9.o]=..
+GENERATE[sha256-sparcv9.S]=asm/sha512-sparcv9.pl $(PERLASM_SCHEME)
+INCLUDE[sha256-sparcv9.o]=..
+GENERATE[sha512-sparcv9.S]=asm/sha512-sparcv9.pl $(PERLASM_SCHEME)
+INCLUDE[sha512-sparcv9.o]=..
+
+GENERATE[sha1-ppc.s]=asm/sha1-ppc.pl $(PERLASM_SCHEME)
+GENERATE[sha256-ppc.s]=asm/sha512-ppc.pl $(PERLASM_SCHEME)
+GENERATE[sha512-ppc.s]=asm/sha512-ppc.pl $(PERLASM_SCHEME)
+GENERATE[sha256p8-ppc.s]=asm/sha512p8-ppc.pl $(PERLASM_SCHEME)
+GENERATE[sha512p8-ppc.s]=asm/sha512p8-ppc.pl $(PERLASM_SCHEME)
+
+GENERATE[sha1-parisc.s]=asm/sha1-parisc.pl $(PERLASM_SCHEME)
+GENERATE[sha256-parisc.s]=asm/sha512-parisc.pl $(PERLASM_SCHEME)
+GENERATE[sha512-parisc.s]=asm/sha512-parisc.pl $(PERLASM_SCHEME)
+
+GENERATE[sha1-mips.S]=asm/sha1-mips.pl $(PERLASM_SCHEME)
+GENERATE[sha256-mips.S]=asm/sha512-mips.pl $(PERLASM_SCHEME)
+GENERATE[sha512-mips.S]=asm/sha512-mips.pl $(PERLASM_SCHEME)
+
+GENERATE[sha1-armv4-large.S]=asm/sha1-armv4-large.pl $(PERLASM_SCHEME)
+INCLUDE[sha1-armv4-large.o]=..
+GENERATE[sha256-armv4.S]=asm/sha256-armv4.pl $(PERLASM_SCHEME)
+INCLUDE[sha256-armv4.o]=..
+GENERATE[sha512-armv4.S]=asm/sha512-armv4.pl $(PERLASM_SCHEME)
+INCLUDE[sha512-armv4.o]=..
+
+GENERATE[sha1-armv8.S]=asm/sha1-armv8.pl $(PERLASM_SCHEME)
+INCLUDE[sha1-armv8.o]=..
+GENERATE[sha256-armv8.S]=asm/sha512-armv8.pl $(PERLASM_SCHEME)
+INCLUDE[sha256-armv8.o]=..
+GENERATE[sha512-armv8.S]=asm/sha512-armv8.pl $(PERLASM_SCHEME)
+INCLUDE[sha512-armv8.o]=..
+
+BEGINRAW[Makefile(unix)]
+##### SHA assembler implementations
+
+# GNU make "catch all"
+{- $builddir -}/sha1-%.S:	{- $sourcedir -}/asm/sha1-%.pl
+	CC="$(CC)" $(PERL) $< $(PERLASM_SCHEME) $@
+{- $builddir -}/sha256-%.S:	{- $sourcedir -}/asm/sha512-%.pl
+	CC="$(CC)" $(PERL) $< $(PERLASM_SCHEME) $@
+{- $builddir -}/sha512-%.S:	{- $sourcedir -}/asm/sha512-%.pl
+	CC="$(CC)" $(PERL) $< $(PERLASM_SCHEME) $@
+ENDRAW[Makefile(unix)]
diff --git a/crypto/sha/sha1_one.c b/crypto/sha/sha1_one.c
new file mode 100644
index 000000000..273ab08dc
--- /dev/null
+++ b/crypto/sha/sha1_one.c
@@ -0,0 +1,28 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include 
+#include 
+
+unsigned char *SHA1(const unsigned char *d, size_t n, unsigned char *md)
+{
+    SHA_CTX c;
+    static unsigned char m[SHA_DIGEST_LENGTH];
+
+    if (md == NULL)
+        md = m;
+    if (!SHA1_Init(&c))
+        return NULL;
+    SHA1_Update(&c, d, n);
+    SHA1_Final(md, &c);
+    OPENSSL_cleanse(&c, sizeof(c));
+    return (md);
+}
diff --git a/crypto/sha/sha1dgst.c b/crypto/sha/sha1dgst.c
new file mode 100644
index 000000000..819370e61
--- /dev/null
+++ b/crypto/sha/sha1dgst.c
@@ -0,0 +1,17 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+
+# include 
+
+/* The implementation is in ../md32_common.h */
+
+# include "sha_locl.h"
diff --git a/crypto/sha/sha256.c b/crypto/sha/sha256.c
new file mode 100644
index 000000000..5e7ba439f
--- /dev/null
+++ b/crypto/sha/sha256.c
@@ -0,0 +1,386 @@
+/*
+ * Copyright 2004-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+
+#include 
+#include 
+
+#include 
+#include 
+#include 
+
+int SHA224_Init(SHA256_CTX *c)
+{
+    memset(c, 0, sizeof(*c));
+    c->h[0] = 0xc1059ed8UL;
+    c->h[1] = 0x367cd507UL;
+    c->h[2] = 0x3070dd17UL;
+    c->h[3] = 0xf70e5939UL;
+    c->h[4] = 0xffc00b31UL;
+    c->h[5] = 0x68581511UL;
+    c->h[6] = 0x64f98fa7UL;
+    c->h[7] = 0xbefa4fa4UL;
+    c->md_len = SHA224_DIGEST_LENGTH;
+    return 1;
+}
+
+int SHA256_Init(SHA256_CTX *c)
+{
+    memset(c, 0, sizeof(*c));
+    c->h[0] = 0x6a09e667UL;
+    c->h[1] = 0xbb67ae85UL;
+    c->h[2] = 0x3c6ef372UL;
+    c->h[3] = 0xa54ff53aUL;
+    c->h[4] = 0x510e527fUL;
+    c->h[5] = 0x9b05688cUL;
+    c->h[6] = 0x1f83d9abUL;
+    c->h[7] = 0x5be0cd19UL;
+    c->md_len = SHA256_DIGEST_LENGTH;
+    return 1;
+}
+
+unsigned char *SHA224(const unsigned char *d, size_t n, unsigned char *md)
+{
+    SHA256_CTX c;
+    static unsigned char m[SHA224_DIGEST_LENGTH];
+
+    if (md == NULL)
+        md = m;
+    SHA224_Init(&c);
+    SHA256_Update(&c, d, n);
+    SHA256_Final(md, &c);
+    OPENSSL_cleanse(&c, sizeof(c));
+    return (md);
+}
+
+unsigned char *SHA256(const unsigned char *d, size_t n, unsigned char *md)
+{
+    SHA256_CTX c;
+    static unsigned char m[SHA256_DIGEST_LENGTH];
+
+    if (md == NULL)
+        md = m;
+    SHA256_Init(&c);
+    SHA256_Update(&c, d, n);
+    SHA256_Final(md, &c);
+    OPENSSL_cleanse(&c, sizeof(c));
+    return (md);
+}
+
+int SHA224_Update(SHA256_CTX *c, const void *data, size_t len)
+{
+    return SHA256_Update(c, data, len);
+}
+
+int SHA224_Final(unsigned char *md, SHA256_CTX *c)
+{
+    return SHA256_Final(md, c);
+}
+
+#define DATA_ORDER_IS_BIG_ENDIAN
+
+#define HASH_LONG               SHA_LONG
+#define HASH_CTX                SHA256_CTX
+#define HASH_CBLOCK             SHA_CBLOCK
+
+/*
+ * Note that FIPS180-2 discusses "Truncation of the Hash Function Output."
+ * default: case below covers for it. It's not clear however if it's
+ * permitted to truncate to amount of bytes not divisible by 4. I bet not,
+ * but if it is, then default: case shall be extended. For reference.
+ * Idea behind separate cases for pre-defined lengths is to let the
+ * compiler decide if it's appropriate to unroll small loops.
+ */
+#define HASH_MAKE_STRING(c,s)   do {    \
+        unsigned long ll;               \
+        unsigned int  nn;               \
+        switch ((c)->md_len)            \
+        {   case SHA224_DIGEST_LENGTH:  \
+                for (nn=0;nnh[nn]; (void)HOST_l2c(ll,(s));   }  \
+                break;                  \
+            case SHA256_DIGEST_LENGTH:  \
+                for (nn=0;nnh[nn]; (void)HOST_l2c(ll,(s));   }  \
+                break;                  \
+            default:                    \
+                if ((c)->md_len > SHA256_DIGEST_LENGTH) \
+                    return 0;                           \
+                for (nn=0;nn<(c)->md_len/4;nn++)                \
+                {   ll=(c)->h[nn]; (void)HOST_l2c(ll,(s));   }  \
+                break;                  \
+        }                               \
+        } while (0)
+
+#define HASH_UPDATE             SHA256_Update
+#define HASH_TRANSFORM          SHA256_Transform
+#define HASH_FINAL              SHA256_Final
+#define HASH_BLOCK_DATA_ORDER   sha256_block_data_order
+#ifndef SHA256_ASM
+static
+#endif
+void sha256_block_data_order(SHA256_CTX *ctx, const void *in, size_t num);
+
+#include "internal/md32_common.h"
+
+#ifndef SHA256_ASM
+static const SHA_LONG K256[64] = {
+    0x428a2f98UL, 0x71374491UL, 0xb5c0fbcfUL, 0xe9b5dba5UL,
+    0x3956c25bUL, 0x59f111f1UL, 0x923f82a4UL, 0xab1c5ed5UL,
+    0xd807aa98UL, 0x12835b01UL, 0x243185beUL, 0x550c7dc3UL,
+    0x72be5d74UL, 0x80deb1feUL, 0x9bdc06a7UL, 0xc19bf174UL,
+    0xe49b69c1UL, 0xefbe4786UL, 0x0fc19dc6UL, 0x240ca1ccUL,
+    0x2de92c6fUL, 0x4a7484aaUL, 0x5cb0a9dcUL, 0x76f988daUL,
+    0x983e5152UL, 0xa831c66dUL, 0xb00327c8UL, 0xbf597fc7UL,
+    0xc6e00bf3UL, 0xd5a79147UL, 0x06ca6351UL, 0x14292967UL,
+    0x27b70a85UL, 0x2e1b2138UL, 0x4d2c6dfcUL, 0x53380d13UL,
+    0x650a7354UL, 0x766a0abbUL, 0x81c2c92eUL, 0x92722c85UL,
+    0xa2bfe8a1UL, 0xa81a664bUL, 0xc24b8b70UL, 0xc76c51a3UL,
+    0xd192e819UL, 0xd6990624UL, 0xf40e3585UL, 0x106aa070UL,
+    0x19a4c116UL, 0x1e376c08UL, 0x2748774cUL, 0x34b0bcb5UL,
+    0x391c0cb3UL, 0x4ed8aa4aUL, 0x5b9cca4fUL, 0x682e6ff3UL,
+    0x748f82eeUL, 0x78a5636fUL, 0x84c87814UL, 0x8cc70208UL,
+    0x90befffaUL, 0xa4506cebUL, 0xbef9a3f7UL, 0xc67178f2UL
+};
+
+/*
+ * FIPS specification refers to right rotations, while our ROTATE macro
+ * is left one. This is why you might notice that rotation coefficients
+ * differ from those observed in FIPS document by 32-N...
+ */
+# define Sigma0(x)       (ROTATE((x),30) ^ ROTATE((x),19) ^ ROTATE((x),10))
+# define Sigma1(x)       (ROTATE((x),26) ^ ROTATE((x),21) ^ ROTATE((x),7))
+# define sigma0(x)       (ROTATE((x),25) ^ ROTATE((x),14) ^ ((x)>>3))
+# define sigma1(x)       (ROTATE((x),15) ^ ROTATE((x),13) ^ ((x)>>10))
+
+# define Ch(x,y,z)       (((x) & (y)) ^ ((~(x)) & (z)))
+# define Maj(x,y,z)      (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)))
+
+# ifdef OPENSSL_SMALL_FOOTPRINT
+
+static void sha256_block_data_order(SHA256_CTX *ctx, const void *in,
+                                    size_t num)
+{
+    unsigned MD32_REG_T a, b, c, d, e, f, g, h, s0, s1, T1, T2;
+    SHA_LONG X[16], l;
+    int i;
+    const unsigned char *data = in;
+
+    while (num--) {
+
+        a = ctx->h[0];
+        b = ctx->h[1];
+        c = ctx->h[2];
+        d = ctx->h[3];
+        e = ctx->h[4];
+        f = ctx->h[5];
+        g = ctx->h[6];
+        h = ctx->h[7];
+
+        for (i = 0; i < 16; i++) {
+            (void)HOST_c2l(data, l);
+            T1 = X[i] = l;
+            T1 += h + Sigma1(e) + Ch(e, f, g) + K256[i];
+            T2 = Sigma0(a) + Maj(a, b, c);
+            h = g;
+            g = f;
+            f = e;
+            e = d + T1;
+            d = c;
+            c = b;
+            b = a;
+            a = T1 + T2;
+        }
+
+        for (; i < 64; i++) {
+            s0 = X[(i + 1) & 0x0f];
+            s0 = sigma0(s0);
+            s1 = X[(i + 14) & 0x0f];
+            s1 = sigma1(s1);
+
+            T1 = X[i & 0xf] += s0 + s1 + X[(i + 9) & 0xf];
+            T1 += h + Sigma1(e) + Ch(e, f, g) + K256[i];
+            T2 = Sigma0(a) + Maj(a, b, c);
+            h = g;
+            g = f;
+            f = e;
+            e = d + T1;
+            d = c;
+            c = b;
+            b = a;
+            a = T1 + T2;
+        }
+
+        ctx->h[0] += a;
+        ctx->h[1] += b;
+        ctx->h[2] += c;
+        ctx->h[3] += d;
+        ctx->h[4] += e;
+        ctx->h[5] += f;
+        ctx->h[6] += g;
+        ctx->h[7] += h;
+
+    }
+}
+
+# else
+
+#  define ROUND_00_15(i,a,b,c,d,e,f,g,h)          do {    \
+        T1 += h + Sigma1(e) + Ch(e,f,g) + K256[i];      \
+        h = Sigma0(a) + Maj(a,b,c);                     \
+        d += T1;        h += T1;                } while (0)
+
+#  define ROUND_16_63(i,a,b,c,d,e,f,g,h,X)        do {    \
+        s0 = X[(i+1)&0x0f];     s0 = sigma0(s0);        \
+        s1 = X[(i+14)&0x0f];    s1 = sigma1(s1);        \
+        T1 = X[(i)&0x0f] += s0 + s1 + X[(i+9)&0x0f];    \
+        ROUND_00_15(i,a,b,c,d,e,f,g,h);         } while (0)
+
+static void sha256_block_data_order(SHA256_CTX *ctx, const void *in,
+                                    size_t num)
+{
+    unsigned MD32_REG_T a, b, c, d, e, f, g, h, s0, s1, T1;
+    SHA_LONG X[16];
+    int i;
+    const unsigned char *data = in;
+    const union {
+        long one;
+        char little;
+    } is_endian = {
+        1
+    };
+
+    while (num--) {
+
+        a = ctx->h[0];
+        b = ctx->h[1];
+        c = ctx->h[2];
+        d = ctx->h[3];
+        e = ctx->h[4];
+        f = ctx->h[5];
+        g = ctx->h[6];
+        h = ctx->h[7];
+
+        if (!is_endian.little && sizeof(SHA_LONG) == 4
+            && ((size_t)in % 4) == 0) {
+            const SHA_LONG *W = (const SHA_LONG *)data;
+
+            T1 = X[0] = W[0];
+            ROUND_00_15(0, a, b, c, d, e, f, g, h);
+            T1 = X[1] = W[1];
+            ROUND_00_15(1, h, a, b, c, d, e, f, g);
+            T1 = X[2] = W[2];
+            ROUND_00_15(2, g, h, a, b, c, d, e, f);
+            T1 = X[3] = W[3];
+            ROUND_00_15(3, f, g, h, a, b, c, d, e);
+            T1 = X[4] = W[4];
+            ROUND_00_15(4, e, f, g, h, a, b, c, d);
+            T1 = X[5] = W[5];
+            ROUND_00_15(5, d, e, f, g, h, a, b, c);
+            T1 = X[6] = W[6];
+            ROUND_00_15(6, c, d, e, f, g, h, a, b);
+            T1 = X[7] = W[7];
+            ROUND_00_15(7, b, c, d, e, f, g, h, a);
+            T1 = X[8] = W[8];
+            ROUND_00_15(8, a, b, c, d, e, f, g, h);
+            T1 = X[9] = W[9];
+            ROUND_00_15(9, h, a, b, c, d, e, f, g);
+            T1 = X[10] = W[10];
+            ROUND_00_15(10, g, h, a, b, c, d, e, f);
+            T1 = X[11] = W[11];
+            ROUND_00_15(11, f, g, h, a, b, c, d, e);
+            T1 = X[12] = W[12];
+            ROUND_00_15(12, e, f, g, h, a, b, c, d);
+            T1 = X[13] = W[13];
+            ROUND_00_15(13, d, e, f, g, h, a, b, c);
+            T1 = X[14] = W[14];
+            ROUND_00_15(14, c, d, e, f, g, h, a, b);
+            T1 = X[15] = W[15];
+            ROUND_00_15(15, b, c, d, e, f, g, h, a);
+
+            data += SHA256_CBLOCK;
+        } else {
+            SHA_LONG l;
+
+            (void)HOST_c2l(data, l);
+            T1 = X[0] = l;
+            ROUND_00_15(0, a, b, c, d, e, f, g, h);
+            (void)HOST_c2l(data, l);
+            T1 = X[1] = l;
+            ROUND_00_15(1, h, a, b, c, d, e, f, g);
+            (void)HOST_c2l(data, l);
+            T1 = X[2] = l;
+            ROUND_00_15(2, g, h, a, b, c, d, e, f);
+            (void)HOST_c2l(data, l);
+            T1 = X[3] = l;
+            ROUND_00_15(3, f, g, h, a, b, c, d, e);
+            (void)HOST_c2l(data, l);
+            T1 = X[4] = l;
+            ROUND_00_15(4, e, f, g, h, a, b, c, d);
+            (void)HOST_c2l(data, l);
+            T1 = X[5] = l;
+            ROUND_00_15(5, d, e, f, g, h, a, b, c);
+            (void)HOST_c2l(data, l);
+            T1 = X[6] = l;
+            ROUND_00_15(6, c, d, e, f, g, h, a, b);
+            (void)HOST_c2l(data, l);
+            T1 = X[7] = l;
+            ROUND_00_15(7, b, c, d, e, f, g, h, a);
+            (void)HOST_c2l(data, l);
+            T1 = X[8] = l;
+            ROUND_00_15(8, a, b, c, d, e, f, g, h);
+            (void)HOST_c2l(data, l);
+            T1 = X[9] = l;
+            ROUND_00_15(9, h, a, b, c, d, e, f, g);
+            (void)HOST_c2l(data, l);
+            T1 = X[10] = l;
+            ROUND_00_15(10, g, h, a, b, c, d, e, f);
+            (void)HOST_c2l(data, l);
+            T1 = X[11] = l;
+            ROUND_00_15(11, f, g, h, a, b, c, d, e);
+            (void)HOST_c2l(data, l);
+            T1 = X[12] = l;
+            ROUND_00_15(12, e, f, g, h, a, b, c, d);
+            (void)HOST_c2l(data, l);
+            T1 = X[13] = l;
+            ROUND_00_15(13, d, e, f, g, h, a, b, c);
+            (void)HOST_c2l(data, l);
+            T1 = X[14] = l;
+            ROUND_00_15(14, c, d, e, f, g, h, a, b);
+            (void)HOST_c2l(data, l);
+            T1 = X[15] = l;
+            ROUND_00_15(15, b, c, d, e, f, g, h, a);
+        }
+
+        for (i = 16; i < 64; i += 8) {
+            ROUND_16_63(i + 0, a, b, c, d, e, f, g, h, X);
+            ROUND_16_63(i + 1, h, a, b, c, d, e, f, g, X);
+            ROUND_16_63(i + 2, g, h, a, b, c, d, e, f, X);
+            ROUND_16_63(i + 3, f, g, h, a, b, c, d, e, X);
+            ROUND_16_63(i + 4, e, f, g, h, a, b, c, d, X);
+            ROUND_16_63(i + 5, d, e, f, g, h, a, b, c, X);
+            ROUND_16_63(i + 6, c, d, e, f, g, h, a, b, X);
+            ROUND_16_63(i + 7, b, c, d, e, f, g, h, a, X);
+        }
+
+        ctx->h[0] += a;
+        ctx->h[1] += b;
+        ctx->h[2] += c;
+        ctx->h[3] += d;
+        ctx->h[4] += e;
+        ctx->h[5] += f;
+        ctx->h[6] += g;
+        ctx->h[7] += h;
+
+    }
+}
+
+# endif
+#endif                         /* SHA256_ASM */
diff --git a/crypto/sha/sha512.c b/crypto/sha/sha512.c
new file mode 100644
index 000000000..d24d10343
--- /dev/null
+++ b/crypto/sha/sha512.c
@@ -0,0 +1,678 @@
+/*
+ * Copyright 2004-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+/*-
+ * IMPLEMENTATION NOTES.
+ *
+ * As you might have noticed 32-bit hash algorithms:
+ *
+ * - permit SHA_LONG to be wider than 32-bit
+ * - optimized versions implement two transform functions: one operating
+ *   on [aligned] data in host byte order and one - on data in input
+ *   stream byte order;
+ * - share common byte-order neutral collector and padding function
+ *   implementations, ../md32_common.h;
+ *
+ * Neither of the above applies to this SHA-512 implementations. Reasons
+ * [in reverse order] are:
+ *
+ * - it's the only 64-bit hash algorithm for the moment of this writing,
+ *   there is no need for common collector/padding implementation [yet];
+ * - by supporting only one transform function [which operates on
+ *   *aligned* data in input stream byte order, big-endian in this case]
+ *   we minimize burden of maintenance in two ways: a) collector/padding
+ *   function is simpler; b) only one transform function to stare at;
+ * - SHA_LONG64 is required to be exactly 64-bit in order to be able to
+ *   apply a number of optimizations to mitigate potential performance
+ *   penalties caused by previous design decision;
+ *
+ * Caveat lector.
+ *
+ * Implementation relies on the fact that "long long" is 64-bit on
+ * both 32- and 64-bit platforms. If some compiler vendor comes up
+ * with 128-bit long long, adjustment to sha.h would be required.
+ * As this implementation relies on 64-bit integer type, it's totally
+ * inappropriate for platforms which don't support it, most notably
+ * 16-bit platforms.
+ *                                      
+ */
+#include 
+#include 
+
+#include 
+#include 
+#include 
+
+#include "internal/cryptlib.h"
+
+#if defined(__i386) || defined(__i386__) || defined(_M_IX86) || \
+    defined(__x86_64) || defined(_M_AMD64) || defined(_M_X64) || \
+    defined(__s390__) || defined(__s390x__) || \
+    defined(__aarch64__) || \
+    defined(SHA512_ASM)
+# define SHA512_BLOCK_CAN_MANAGE_UNALIGNED_DATA
+#endif
+
+int SHA384_Init(SHA512_CTX *c)
+{
+    c->h[0] = U64(0xcbbb9d5dc1059ed8);
+    c->h[1] = U64(0x629a292a367cd507);
+    c->h[2] = U64(0x9159015a3070dd17);
+    c->h[3] = U64(0x152fecd8f70e5939);
+    c->h[4] = U64(0x67332667ffc00b31);
+    c->h[5] = U64(0x8eb44a8768581511);
+    c->h[6] = U64(0xdb0c2e0d64f98fa7);
+    c->h[7] = U64(0x47b5481dbefa4fa4);
+
+    c->Nl = 0;
+    c->Nh = 0;
+    c->num = 0;
+    c->md_len = SHA384_DIGEST_LENGTH;
+    return 1;
+}
+
+int SHA512_Init(SHA512_CTX *c)
+{
+    c->h[0] = U64(0x6a09e667f3bcc908);
+    c->h[1] = U64(0xbb67ae8584caa73b);
+    c->h[2] = U64(0x3c6ef372fe94f82b);
+    c->h[3] = U64(0xa54ff53a5f1d36f1);
+    c->h[4] = U64(0x510e527fade682d1);
+    c->h[5] = U64(0x9b05688c2b3e6c1f);
+    c->h[6] = U64(0x1f83d9abfb41bd6b);
+    c->h[7] = U64(0x5be0cd19137e2179);
+
+    c->Nl = 0;
+    c->Nh = 0;
+    c->num = 0;
+    c->md_len = SHA512_DIGEST_LENGTH;
+    return 1;
+}
+
+#ifndef SHA512_ASM
+static
+#endif
+void sha512_block_data_order(SHA512_CTX *ctx, const void *in, size_t num);
+
+int SHA512_Final(unsigned char *md, SHA512_CTX *c)
+{
+    unsigned char *p = (unsigned char *)c->u.p;
+    size_t n = c->num;
+
+    p[n] = 0x80;                /* There always is a room for one */
+    n++;
+    if (n > (sizeof(c->u) - 16)) {
+        memset(p + n, 0, sizeof(c->u) - n);
+        n = 0;
+        sha512_block_data_order(c, p, 1);
+    }
+
+    memset(p + n, 0, sizeof(c->u) - 16 - n);
+#ifdef  B_ENDIAN
+    c->u.d[SHA_LBLOCK - 2] = c->Nh;
+    c->u.d[SHA_LBLOCK - 1] = c->Nl;
+#else
+    p[sizeof(c->u) - 1] = (unsigned char)(c->Nl);
+    p[sizeof(c->u) - 2] = (unsigned char)(c->Nl >> 8);
+    p[sizeof(c->u) - 3] = (unsigned char)(c->Nl >> 16);
+    p[sizeof(c->u) - 4] = (unsigned char)(c->Nl >> 24);
+    p[sizeof(c->u) - 5] = (unsigned char)(c->Nl >> 32);
+    p[sizeof(c->u) - 6] = (unsigned char)(c->Nl >> 40);
+    p[sizeof(c->u) - 7] = (unsigned char)(c->Nl >> 48);
+    p[sizeof(c->u) - 8] = (unsigned char)(c->Nl >> 56);
+    p[sizeof(c->u) - 9] = (unsigned char)(c->Nh);
+    p[sizeof(c->u) - 10] = (unsigned char)(c->Nh >> 8);
+    p[sizeof(c->u) - 11] = (unsigned char)(c->Nh >> 16);
+    p[sizeof(c->u) - 12] = (unsigned char)(c->Nh >> 24);
+    p[sizeof(c->u) - 13] = (unsigned char)(c->Nh >> 32);
+    p[sizeof(c->u) - 14] = (unsigned char)(c->Nh >> 40);
+    p[sizeof(c->u) - 15] = (unsigned char)(c->Nh >> 48);
+    p[sizeof(c->u) - 16] = (unsigned char)(c->Nh >> 56);
+#endif
+
+    sha512_block_data_order(c, p, 1);
+
+    if (md == 0)
+        return 0;
+
+    switch (c->md_len) {
+        /* Let compiler decide if it's appropriate to unroll... */
+    case SHA384_DIGEST_LENGTH:
+        for (n = 0; n < SHA384_DIGEST_LENGTH / 8; n++) {
+            SHA_LONG64 t = c->h[n];
+
+            *(md++) = (unsigned char)(t >> 56);
+            *(md++) = (unsigned char)(t >> 48);
+            *(md++) = (unsigned char)(t >> 40);
+            *(md++) = (unsigned char)(t >> 32);
+            *(md++) = (unsigned char)(t >> 24);
+            *(md++) = (unsigned char)(t >> 16);
+            *(md++) = (unsigned char)(t >> 8);
+            *(md++) = (unsigned char)(t);
+        }
+        break;
+    case SHA512_DIGEST_LENGTH:
+        for (n = 0; n < SHA512_DIGEST_LENGTH / 8; n++) {
+            SHA_LONG64 t = c->h[n];
+
+            *(md++) = (unsigned char)(t >> 56);
+            *(md++) = (unsigned char)(t >> 48);
+            *(md++) = (unsigned char)(t >> 40);
+            *(md++) = (unsigned char)(t >> 32);
+            *(md++) = (unsigned char)(t >> 24);
+            *(md++) = (unsigned char)(t >> 16);
+            *(md++) = (unsigned char)(t >> 8);
+            *(md++) = (unsigned char)(t);
+        }
+        break;
+        /* ... as well as make sure md_len is not abused. */
+    default:
+        return 0;
+    }
+
+    return 1;
+}
+
+int SHA384_Final(unsigned char *md, SHA512_CTX *c)
+{
+    return SHA512_Final(md, c);
+}
+
+int SHA512_Update(SHA512_CTX *c, const void *_data, size_t len)
+{
+    SHA_LONG64 l;
+    unsigned char *p = c->u.p;
+    const unsigned char *data = (const unsigned char *)_data;
+
+    if (len == 0)
+        return 1;
+
+    l = (c->Nl + (((SHA_LONG64) len) << 3)) & U64(0xffffffffffffffff);
+    if (l < c->Nl)
+        c->Nh++;
+    if (sizeof(len) >= 8)
+        c->Nh += (((SHA_LONG64) len) >> 61);
+    c->Nl = l;
+
+    if (c->num != 0) {
+        size_t n = sizeof(c->u) - c->num;
+
+        if (len < n) {
+            memcpy(p + c->num, data, len), c->num += (unsigned int)len;
+            return 1;
+        } else {
+            memcpy(p + c->num, data, n), c->num = 0;
+            len -= n, data += n;
+            sha512_block_data_order(c, p, 1);
+        }
+    }
+
+    if (len >= sizeof(c->u)) {
+#ifndef SHA512_BLOCK_CAN_MANAGE_UNALIGNED_DATA
+        if ((size_t)data % sizeof(c->u.d[0]) != 0)
+            while (len >= sizeof(c->u))
+                memcpy(p, data, sizeof(c->u)),
+                    sha512_block_data_order(c, p, 1),
+                    len -= sizeof(c->u), data += sizeof(c->u);
+        else
+#endif
+            sha512_block_data_order(c, data, len / sizeof(c->u)),
+                data += len, len %= sizeof(c->u), data -= len;
+    }
+
+    if (len != 0)
+        memcpy(p, data, len), c->num = (int)len;
+
+    return 1;
+}
+
+int SHA384_Update(SHA512_CTX *c, const void *data, size_t len)
+{
+    return SHA512_Update(c, data, len);
+}
+
+void SHA512_Transform(SHA512_CTX *c, const unsigned char *data)
+{
+#ifndef SHA512_BLOCK_CAN_MANAGE_UNALIGNED_DATA
+    if ((size_t)data % sizeof(c->u.d[0]) != 0)
+        memcpy(c->u.p, data, sizeof(c->u.p)), data = c->u.p;
+#endif
+    sha512_block_data_order(c, data, 1);
+}
+
+unsigned char *SHA384(const unsigned char *d, size_t n, unsigned char *md)
+{
+    SHA512_CTX c;
+    static unsigned char m[SHA384_DIGEST_LENGTH];
+
+    if (md == NULL)
+        md = m;
+    SHA384_Init(&c);
+    SHA512_Update(&c, d, n);
+    SHA512_Final(md, &c);
+    OPENSSL_cleanse(&c, sizeof(c));
+    return (md);
+}
+
+unsigned char *SHA512(const unsigned char *d, size_t n, unsigned char *md)
+{
+    SHA512_CTX c;
+    static unsigned char m[SHA512_DIGEST_LENGTH];
+
+    if (md == NULL)
+        md = m;
+    SHA512_Init(&c);
+    SHA512_Update(&c, d, n);
+    SHA512_Final(md, &c);
+    OPENSSL_cleanse(&c, sizeof(c));
+    return (md);
+}
+
+#ifndef SHA512_ASM
+static const SHA_LONG64 K512[80] = {
+    U64(0x428a2f98d728ae22), U64(0x7137449123ef65cd),
+    U64(0xb5c0fbcfec4d3b2f), U64(0xe9b5dba58189dbbc),
+    U64(0x3956c25bf348b538), U64(0x59f111f1b605d019),
+    U64(0x923f82a4af194f9b), U64(0xab1c5ed5da6d8118),
+    U64(0xd807aa98a3030242), U64(0x12835b0145706fbe),
+    U64(0x243185be4ee4b28c), U64(0x550c7dc3d5ffb4e2),
+    U64(0x72be5d74f27b896f), U64(0x80deb1fe3b1696b1),
+    U64(0x9bdc06a725c71235), U64(0xc19bf174cf692694),
+    U64(0xe49b69c19ef14ad2), U64(0xefbe4786384f25e3),
+    U64(0x0fc19dc68b8cd5b5), U64(0x240ca1cc77ac9c65),
+    U64(0x2de92c6f592b0275), U64(0x4a7484aa6ea6e483),
+    U64(0x5cb0a9dcbd41fbd4), U64(0x76f988da831153b5),
+    U64(0x983e5152ee66dfab), U64(0xa831c66d2db43210),
+    U64(0xb00327c898fb213f), U64(0xbf597fc7beef0ee4),
+    U64(0xc6e00bf33da88fc2), U64(0xd5a79147930aa725),
+    U64(0x06ca6351e003826f), U64(0x142929670a0e6e70),
+    U64(0x27b70a8546d22ffc), U64(0x2e1b21385c26c926),
+    U64(0x4d2c6dfc5ac42aed), U64(0x53380d139d95b3df),
+    U64(0x650a73548baf63de), U64(0x766a0abb3c77b2a8),
+    U64(0x81c2c92e47edaee6), U64(0x92722c851482353b),
+    U64(0xa2bfe8a14cf10364), U64(0xa81a664bbc423001),
+    U64(0xc24b8b70d0f89791), U64(0xc76c51a30654be30),
+    U64(0xd192e819d6ef5218), U64(0xd69906245565a910),
+    U64(0xf40e35855771202a), U64(0x106aa07032bbd1b8),
+    U64(0x19a4c116b8d2d0c8), U64(0x1e376c085141ab53),
+    U64(0x2748774cdf8eeb99), U64(0x34b0bcb5e19b48a8),
+    U64(0x391c0cb3c5c95a63), U64(0x4ed8aa4ae3418acb),
+    U64(0x5b9cca4f7763e373), U64(0x682e6ff3d6b2b8a3),
+    U64(0x748f82ee5defb2fc), U64(0x78a5636f43172f60),
+    U64(0x84c87814a1f0ab72), U64(0x8cc702081a6439ec),
+    U64(0x90befffa23631e28), U64(0xa4506cebde82bde9),
+    U64(0xbef9a3f7b2c67915), U64(0xc67178f2e372532b),
+    U64(0xca273eceea26619c), U64(0xd186b8c721c0c207),
+    U64(0xeada7dd6cde0eb1e), U64(0xf57d4f7fee6ed178),
+    U64(0x06f067aa72176fba), U64(0x0a637dc5a2c898a6),
+    U64(0x113f9804bef90dae), U64(0x1b710b35131c471b),
+    U64(0x28db77f523047d84), U64(0x32caab7b40c72493),
+    U64(0x3c9ebe0a15c9bebc), U64(0x431d67c49c100d4c),
+    U64(0x4cc5d4becb3e42b6), U64(0x597f299cfc657e2a),
+    U64(0x5fcb6fab3ad6faec), U64(0x6c44198c4a475817)
+};
+
+# ifndef PEDANTIC
+#  if defined(__GNUC__) && __GNUC__>=2 && !defined(OPENSSL_NO_ASM) && !defined(OPENSSL_NO_INLINE_ASM)
+#   if defined(__x86_64) || defined(__x86_64__)
+#    define ROTR(a,n)    ({ SHA_LONG64 ret;              \
+                                asm ("rorq %1,%0"       \
+                                : "=r"(ret)             \
+                                : "J"(n),"0"(a)         \
+                                : "cc"); ret;           })
+#    if !defined(B_ENDIAN)
+#     define PULL64(x) ({ SHA_LONG64 ret=*((const SHA_LONG64 *)(&(x)));  \
+                                asm ("bswapq    %0"             \
+                                : "=r"(ret)                     \
+                                : "0"(ret)); ret;               })
+#    endif
+#   elif (defined(__i386) || defined(__i386__)) && !defined(B_ENDIAN)
+#    if defined(I386_ONLY)
+#     define PULL64(x) ({ const unsigned int *p=(const unsigned int *)(&(x));\
+                         unsigned int hi=p[0],lo=p[1];          \
+                                asm("xchgb %%ah,%%al;xchgb %%dh,%%dl;"\
+                                    "roll $16,%%eax; roll $16,%%edx; "\
+                                    "xchgb %%ah,%%al;xchgb %%dh,%%dl;" \
+                                : "=a"(lo),"=d"(hi)             \
+                                : "0"(lo),"1"(hi) : "cc");      \
+                                ((SHA_LONG64)hi)<<32|lo;        })
+#    else
+#     define PULL64(x) ({ const unsigned int *p=(const unsigned int *)(&(x));\
+                         unsigned int hi=p[0],lo=p[1];          \
+                                asm ("bswapl %0; bswapl %1;"    \
+                                : "=r"(lo),"=r"(hi)             \
+                                : "0"(lo),"1"(hi));             \
+                                ((SHA_LONG64)hi)<<32|lo;        })
+#    endif
+#   elif (defined(_ARCH_PPC) && defined(__64BIT__)) || defined(_ARCH_PPC64)
+#    define ROTR(a,n)    ({ SHA_LONG64 ret;              \
+                                asm ("rotrdi %0,%1,%2"  \
+                                : "=r"(ret)             \
+                                : "r"(a),"K"(n)); ret;  })
+#   elif defined(__aarch64__)
+#    define ROTR(a,n)    ({ SHA_LONG64 ret;              \
+                                asm ("ror %0,%1,%2"     \
+                                : "=r"(ret)             \
+                                : "r"(a),"I"(n)); ret;  })
+#    if  defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && \
+        __BYTE_ORDER__==__ORDER_LITTLE_ENDIAN__
+#     define PULL64(x)   ({ SHA_LONG64 ret;                      \
+                                asm ("rev       %0,%1"          \
+                                : "=r"(ret)                     \
+                                : "r"(*((const SHA_LONG64 *)(&(x))))); ret;             })
+#    endif
+#   endif
+#  elif defined(_MSC_VER)
+#   if defined(_WIN64)         /* applies to both IA-64 and AMD64 */
+#    pragma intrinsic(_rotr64)
+#    define ROTR(a,n)    _rotr64((a),n)
+#   endif
+#   if defined(_M_IX86) && !defined(OPENSSL_NO_ASM) && !defined(OPENSSL_NO_INLINE_ASM)
+#    if defined(I386_ONLY)
+static SHA_LONG64 __fastcall __pull64be(const void *x)
+{
+    _asm mov edx,[ecx + 0]
+    _asm mov eax,[ecx + 4]
+_asm xchg dh, dl
+        _asm xchg ah, al
+        _asm rol edx, 16 _asm rol eax, 16 _asm xchg dh, dl _asm xchg ah, al}
+#    else
+static SHA_LONG64 __fastcall __pull64be(const void *x)
+{
+    _asm mov edx,[ecx + 0]
+    _asm mov eax,[ecx + 4]
+_asm bswap edx _asm bswap eax}
+#    endif
+#    define PULL64(x) __pull64be(&(x))
+#    if _MSC_VER<=1200
+#     pragma inline_depth(0)
+#    endif
+#   endif
+#  endif
+# endif
+# ifndef PULL64
+#  define B(x,j)    (((SHA_LONG64)(*(((const unsigned char *)(&x))+j)))<<((7-j)*8))
+#  define PULL64(x) (B(x,0)|B(x,1)|B(x,2)|B(x,3)|B(x,4)|B(x,5)|B(x,6)|B(x,7))
+# endif
+# ifndef ROTR
+#  define ROTR(x,s)       (((x)>>s) | (x)<<(64-s))
+# endif
+# define Sigma0(x)       (ROTR((x),28) ^ ROTR((x),34) ^ ROTR((x),39))
+# define Sigma1(x)       (ROTR((x),14) ^ ROTR((x),18) ^ ROTR((x),41))
+# define sigma0(x)       (ROTR((x),1)  ^ ROTR((x),8)  ^ ((x)>>7))
+# define sigma1(x)       (ROTR((x),19) ^ ROTR((x),61) ^ ((x)>>6))
+# define Ch(x,y,z)       (((x) & (y)) ^ ((~(x)) & (z)))
+# define Maj(x,y,z)      (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)))
+# if defined(__i386) || defined(__i386__) || defined(_M_IX86)
+/*
+ * This code should give better results on 32-bit CPU with less than
+ * ~24 registers, both size and performance wise...
+ */ static void sha512_block_data_order(SHA512_CTX *ctx, const void *in,
+                                        size_t num)
+{
+    const SHA_LONG64 *W = in;
+    SHA_LONG64 A, E, T;
+    SHA_LONG64 X[9 + 80], *F;
+    int i;
+
+    while (num--) {
+
+        F = X + 80;
+        A = ctx->h[0];
+        F[1] = ctx->h[1];
+        F[2] = ctx->h[2];
+        F[3] = ctx->h[3];
+        E = ctx->h[4];
+        F[5] = ctx->h[5];
+        F[6] = ctx->h[6];
+        F[7] = ctx->h[7];
+
+        for (i = 0; i < 16; i++, F--) {
+#  ifdef B_ENDIAN
+            T = W[i];
+#  else
+            T = PULL64(W[i]);
+#  endif
+            F[0] = A;
+            F[4] = E;
+            F[8] = T;
+            T += F[7] + Sigma1(E) + Ch(E, F[5], F[6]) + K512[i];
+            E = F[3] + T;
+            A = T + Sigma0(A) + Maj(A, F[1], F[2]);
+        }
+
+        for (; i < 80; i++, F--) {
+            T = sigma0(F[8 + 16 - 1]);
+            T += sigma1(F[8 + 16 - 14]);
+            T += F[8 + 16] + F[8 + 16 - 9];
+
+            F[0] = A;
+            F[4] = E;
+            F[8] = T;
+            T += F[7] + Sigma1(E) + Ch(E, F[5], F[6]) + K512[i];
+            E = F[3] + T;
+            A = T + Sigma0(A) + Maj(A, F[1], F[2]);
+        }
+
+        ctx->h[0] += A;
+        ctx->h[1] += F[1];
+        ctx->h[2] += F[2];
+        ctx->h[3] += F[3];
+        ctx->h[4] += E;
+        ctx->h[5] += F[5];
+        ctx->h[6] += F[6];
+        ctx->h[7] += F[7];
+
+        W += SHA_LBLOCK;
+    }
+}
+
+# elif defined(OPENSSL_SMALL_FOOTPRINT)
+static void sha512_block_data_order(SHA512_CTX *ctx, const void *in,
+                                    size_t num)
+{
+    const SHA_LONG64 *W = in;
+    SHA_LONG64 a, b, c, d, e, f, g, h, s0, s1, T1, T2;
+    SHA_LONG64 X[16];
+    int i;
+
+    while (num--) {
+
+        a = ctx->h[0];
+        b = ctx->h[1];
+        c = ctx->h[2];
+        d = ctx->h[3];
+        e = ctx->h[4];
+        f = ctx->h[5];
+        g = ctx->h[6];
+        h = ctx->h[7];
+
+        for (i = 0; i < 16; i++) {
+#  ifdef B_ENDIAN
+            T1 = X[i] = W[i];
+#  else
+            T1 = X[i] = PULL64(W[i]);
+#  endif
+            T1 += h + Sigma1(e) + Ch(e, f, g) + K512[i];
+            T2 = Sigma0(a) + Maj(a, b, c);
+            h = g;
+            g = f;
+            f = e;
+            e = d + T1;
+            d = c;
+            c = b;
+            b = a;
+            a = T1 + T2;
+        }
+
+        for (; i < 80; i++) {
+            s0 = X[(i + 1) & 0x0f];
+            s0 = sigma0(s0);
+            s1 = X[(i + 14) & 0x0f];
+            s1 = sigma1(s1);
+
+            T1 = X[i & 0xf] += s0 + s1 + X[(i + 9) & 0xf];
+            T1 += h + Sigma1(e) + Ch(e, f, g) + K512[i];
+            T2 = Sigma0(a) + Maj(a, b, c);
+            h = g;
+            g = f;
+            f = e;
+            e = d + T1;
+            d = c;
+            c = b;
+            b = a;
+            a = T1 + T2;
+        }
+
+        ctx->h[0] += a;
+        ctx->h[1] += b;
+        ctx->h[2] += c;
+        ctx->h[3] += d;
+        ctx->h[4] += e;
+        ctx->h[5] += f;
+        ctx->h[6] += g;
+        ctx->h[7] += h;
+
+        W += SHA_LBLOCK;
+    }
+}
+
+# else
+#  define ROUND_00_15(i,a,b,c,d,e,f,g,h)          do {    \
+        T1 += h + Sigma1(e) + Ch(e,f,g) + K512[i];      \
+        h = Sigma0(a) + Maj(a,b,c);                     \
+        d += T1;        h += T1;                } while (0)
+#  define ROUND_16_80(i,j,a,b,c,d,e,f,g,h,X)      do {    \
+        s0 = X[(j+1)&0x0f];     s0 = sigma0(s0);        \
+        s1 = X[(j+14)&0x0f];    s1 = sigma1(s1);        \
+        T1 = X[(j)&0x0f] += s0 + s1 + X[(j+9)&0x0f];    \
+        ROUND_00_15(i+j,a,b,c,d,e,f,g,h);               } while (0)
+static void sha512_block_data_order(SHA512_CTX *ctx, const void *in,
+                                    size_t num)
+{
+    const SHA_LONG64 *W = in;
+    SHA_LONG64 a, b, c, d, e, f, g, h, s0, s1, T1;
+    SHA_LONG64 X[16];
+    int i;
+
+    while (num--) {
+
+        a = ctx->h[0];
+        b = ctx->h[1];
+        c = ctx->h[2];
+        d = ctx->h[3];
+        e = ctx->h[4];
+        f = ctx->h[5];
+        g = ctx->h[6];
+        h = ctx->h[7];
+
+#  ifdef B_ENDIAN
+        T1 = X[0] = W[0];
+        ROUND_00_15(0, a, b, c, d, e, f, g, h);
+        T1 = X[1] = W[1];
+        ROUND_00_15(1, h, a, b, c, d, e, f, g);
+        T1 = X[2] = W[2];
+        ROUND_00_15(2, g, h, a, b, c, d, e, f);
+        T1 = X[3] = W[3];
+        ROUND_00_15(3, f, g, h, a, b, c, d, e);
+        T1 = X[4] = W[4];
+        ROUND_00_15(4, e, f, g, h, a, b, c, d);
+        T1 = X[5] = W[5];
+        ROUND_00_15(5, d, e, f, g, h, a, b, c);
+        T1 = X[6] = W[6];
+        ROUND_00_15(6, c, d, e, f, g, h, a, b);
+        T1 = X[7] = W[7];
+        ROUND_00_15(7, b, c, d, e, f, g, h, a);
+        T1 = X[8] = W[8];
+        ROUND_00_15(8, a, b, c, d, e, f, g, h);
+        T1 = X[9] = W[9];
+        ROUND_00_15(9, h, a, b, c, d, e, f, g);
+        T1 = X[10] = W[10];
+        ROUND_00_15(10, g, h, a, b, c, d, e, f);
+        T1 = X[11] = W[11];
+        ROUND_00_15(11, f, g, h, a, b, c, d, e);
+        T1 = X[12] = W[12];
+        ROUND_00_15(12, e, f, g, h, a, b, c, d);
+        T1 = X[13] = W[13];
+        ROUND_00_15(13, d, e, f, g, h, a, b, c);
+        T1 = X[14] = W[14];
+        ROUND_00_15(14, c, d, e, f, g, h, a, b);
+        T1 = X[15] = W[15];
+        ROUND_00_15(15, b, c, d, e, f, g, h, a);
+#  else
+        T1 = X[0] = PULL64(W[0]);
+        ROUND_00_15(0, a, b, c, d, e, f, g, h);
+        T1 = X[1] = PULL64(W[1]);
+        ROUND_00_15(1, h, a, b, c, d, e, f, g);
+        T1 = X[2] = PULL64(W[2]);
+        ROUND_00_15(2, g, h, a, b, c, d, e, f);
+        T1 = X[3] = PULL64(W[3]);
+        ROUND_00_15(3, f, g, h, a, b, c, d, e);
+        T1 = X[4] = PULL64(W[4]);
+        ROUND_00_15(4, e, f, g, h, a, b, c, d);
+        T1 = X[5] = PULL64(W[5]);
+        ROUND_00_15(5, d, e, f, g, h, a, b, c);
+        T1 = X[6] = PULL64(W[6]);
+        ROUND_00_15(6, c, d, e, f, g, h, a, b);
+        T1 = X[7] = PULL64(W[7]);
+        ROUND_00_15(7, b, c, d, e, f, g, h, a);
+        T1 = X[8] = PULL64(W[8]);
+        ROUND_00_15(8, a, b, c, d, e, f, g, h);
+        T1 = X[9] = PULL64(W[9]);
+        ROUND_00_15(9, h, a, b, c, d, e, f, g);
+        T1 = X[10] = PULL64(W[10]);
+        ROUND_00_15(10, g, h, a, b, c, d, e, f);
+        T1 = X[11] = PULL64(W[11]);
+        ROUND_00_15(11, f, g, h, a, b, c, d, e);
+        T1 = X[12] = PULL64(W[12]);
+        ROUND_00_15(12, e, f, g, h, a, b, c, d);
+        T1 = X[13] = PULL64(W[13]);
+        ROUND_00_15(13, d, e, f, g, h, a, b, c);
+        T1 = X[14] = PULL64(W[14]);
+        ROUND_00_15(14, c, d, e, f, g, h, a, b);
+        T1 = X[15] = PULL64(W[15]);
+        ROUND_00_15(15, b, c, d, e, f, g, h, a);
+#  endif
+
+        for (i = 16; i < 80; i += 16) {
+            ROUND_16_80(i, 0, a, b, c, d, e, f, g, h, X);
+            ROUND_16_80(i, 1, h, a, b, c, d, e, f, g, X);
+            ROUND_16_80(i, 2, g, h, a, b, c, d, e, f, X);
+            ROUND_16_80(i, 3, f, g, h, a, b, c, d, e, X);
+            ROUND_16_80(i, 4, e, f, g, h, a, b, c, d, X);
+            ROUND_16_80(i, 5, d, e, f, g, h, a, b, c, X);
+            ROUND_16_80(i, 6, c, d, e, f, g, h, a, b, X);
+            ROUND_16_80(i, 7, b, c, d, e, f, g, h, a, X);
+            ROUND_16_80(i, 8, a, b, c, d, e, f, g, h, X);
+            ROUND_16_80(i, 9, h, a, b, c, d, e, f, g, X);
+            ROUND_16_80(i, 10, g, h, a, b, c, d, e, f, X);
+            ROUND_16_80(i, 11, f, g, h, a, b, c, d, e, X);
+            ROUND_16_80(i, 12, e, f, g, h, a, b, c, d, X);
+            ROUND_16_80(i, 13, d, e, f, g, h, a, b, c, X);
+            ROUND_16_80(i, 14, c, d, e, f, g, h, a, b, X);
+            ROUND_16_80(i, 15, b, c, d, e, f, g, h, a, X);
+        }
+
+        ctx->h[0] += a;
+        ctx->h[1] += b;
+        ctx->h[2] += c;
+        ctx->h[3] += d;
+        ctx->h[4] += e;
+        ctx->h[5] += f;
+        ctx->h[6] += g;
+        ctx->h[7] += h;
+
+        W += SHA_LBLOCK;
+    }
+}
+
+# endif
+
+#endif                         /* SHA512_ASM */
diff --git a/crypto/sha/sha_locl.h b/crypto/sha/sha_locl.h
new file mode 100644
index 000000000..918278a83
--- /dev/null
+++ b/crypto/sha/sha_locl.h
@@ -0,0 +1,424 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+
+#include 
+#include 
+
+#define DATA_ORDER_IS_BIG_ENDIAN
+
+#define HASH_LONG               SHA_LONG
+#define HASH_CTX                SHA_CTX
+#define HASH_CBLOCK             SHA_CBLOCK
+#define HASH_MAKE_STRING(c,s)   do {    \
+        unsigned long ll;               \
+        ll=(c)->h0; (void)HOST_l2c(ll,(s));     \
+        ll=(c)->h1; (void)HOST_l2c(ll,(s));     \
+        ll=(c)->h2; (void)HOST_l2c(ll,(s));     \
+        ll=(c)->h3; (void)HOST_l2c(ll,(s));     \
+        ll=(c)->h4; (void)HOST_l2c(ll,(s));     \
+        } while (0)
+
+#define HASH_UPDATE                     SHA1_Update
+#define HASH_TRANSFORM                  SHA1_Transform
+#define HASH_FINAL                      SHA1_Final
+#define HASH_INIT                       SHA1_Init
+#define HASH_BLOCK_DATA_ORDER           sha1_block_data_order
+#define Xupdate(a,ix,ia,ib,ic,id)       ( (a)=(ia^ib^ic^id),    \
+                                          ix=(a)=ROTATE((a),1)  \
+                                        )
+
+#ifndef SHA1_ASM
+static void sha1_block_data_order(SHA_CTX *c, const void *p, size_t num);
+#else
+void sha1_block_data_order(SHA_CTX *c, const void *p, size_t num);
+#endif
+
+#include "internal/md32_common.h"
+
+#define INIT_DATA_h0 0x67452301UL
+#define INIT_DATA_h1 0xefcdab89UL
+#define INIT_DATA_h2 0x98badcfeUL
+#define INIT_DATA_h3 0x10325476UL
+#define INIT_DATA_h4 0xc3d2e1f0UL
+
+int HASH_INIT(SHA_CTX *c)
+{
+    memset(c, 0, sizeof(*c));
+    c->h0 = INIT_DATA_h0;
+    c->h1 = INIT_DATA_h1;
+    c->h2 = INIT_DATA_h2;
+    c->h3 = INIT_DATA_h3;
+    c->h4 = INIT_DATA_h4;
+    return 1;
+}
+
+#define K_00_19 0x5a827999UL
+#define K_20_39 0x6ed9eba1UL
+#define K_40_59 0x8f1bbcdcUL
+#define K_60_79 0xca62c1d6UL
+
+/*
+ * As pointed out by Wei Dai , F() below can be simplified
+ * to the code in F_00_19.  Wei attributes these optimisations to Peter
+ * Gutmann's SHS code, and he attributes it to Rich Schroeppel. #define
+ * F(x,y,z) (((x) & (y)) | ((~(x)) & (z))) I've just become aware of another
+ * tweak to be made, again from Wei Dai, in F_40_59, (x&a)|(y&a) -> (x|y)&a
+ */
+#define F_00_19(b,c,d)  ((((c) ^ (d)) & (b)) ^ (d))
+#define F_20_39(b,c,d)  ((b) ^ (c) ^ (d))
+#define F_40_59(b,c,d)  (((b) & (c)) | (((b)|(c)) & (d)))
+#define F_60_79(b,c,d)  F_20_39(b,c,d)
+
+#ifndef OPENSSL_SMALL_FOOTPRINT
+
+# define BODY_00_15(i,a,b,c,d,e,f,xi) \
+        (f)=xi+(e)+K_00_19+ROTATE((a),5)+F_00_19((b),(c),(d)); \
+        (b)=ROTATE((b),30);
+
+# define BODY_16_19(i,a,b,c,d,e,f,xi,xa,xb,xc,xd) \
+        Xupdate(f,xi,xa,xb,xc,xd); \
+        (f)+=(e)+K_00_19+ROTATE((a),5)+F_00_19((b),(c),(d)); \
+        (b)=ROTATE((b),30);
+
+# define BODY_20_31(i,a,b,c,d,e,f,xi,xa,xb,xc,xd) \
+        Xupdate(f,xi,xa,xb,xc,xd); \
+        (f)+=(e)+K_20_39+ROTATE((a),5)+F_20_39((b),(c),(d)); \
+        (b)=ROTATE((b),30);
+
+# define BODY_32_39(i,a,b,c,d,e,f,xa,xb,xc,xd) \
+        Xupdate(f,xa,xa,xb,xc,xd); \
+        (f)+=(e)+K_20_39+ROTATE((a),5)+F_20_39((b),(c),(d)); \
+        (b)=ROTATE((b),30);
+
+# define BODY_40_59(i,a,b,c,d,e,f,xa,xb,xc,xd) \
+        Xupdate(f,xa,xa,xb,xc,xd); \
+        (f)+=(e)+K_40_59+ROTATE((a),5)+F_40_59((b),(c),(d)); \
+        (b)=ROTATE((b),30);
+
+# define BODY_60_79(i,a,b,c,d,e,f,xa,xb,xc,xd) \
+        Xupdate(f,xa,xa,xb,xc,xd); \
+        (f)=xa+(e)+K_60_79+ROTATE((a),5)+F_60_79((b),(c),(d)); \
+        (b)=ROTATE((b),30);
+
+# ifdef X
+#  undef X
+# endif
+# ifndef MD32_XARRAY
+  /*
+   * Originally X was an array. As it's automatic it's natural
+   * to expect RISC compiler to accommodate at least part of it in
+   * the register bank, isn't it? Unfortunately not all compilers
+   * "find" this expectation reasonable:-( On order to make such
+   * compilers generate better code I replace X[] with a bunch of
+   * X0, X1, etc. See the function body below...
+   *                                    
+   */
+#  define X(i)   XX##i
+# else
+  /*
+   * However! Some compilers (most notably HP C) get overwhelmed by
+   * that many local variables so that we have to have the way to
+   * fall down to the original behavior.
+   */
+#  define X(i)   XX[i]
+# endif
+
+# if !defined(SHA1_ASM)
+static void HASH_BLOCK_DATA_ORDER(SHA_CTX *c, const void *p, size_t num)
+{
+    const unsigned char *data = p;
+    register unsigned MD32_REG_T A, B, C, D, E, T, l;
+#  ifndef MD32_XARRAY
+    unsigned MD32_REG_T XX0, XX1, XX2, XX3, XX4, XX5, XX6, XX7,
+        XX8, XX9, XX10, XX11, XX12, XX13, XX14, XX15;
+#  else
+    SHA_LONG XX[16];
+#  endif
+
+    A = c->h0;
+    B = c->h1;
+    C = c->h2;
+    D = c->h3;
+    E = c->h4;
+
+    for (;;) {
+        const union {
+            long one;
+            char little;
+        } is_endian = {
+            1
+        };
+
+        if (!is_endian.little && sizeof(SHA_LONG) == 4
+            && ((size_t)p % 4) == 0) {
+            const SHA_LONG *W = (const SHA_LONG *)data;
+
+            X(0) = W[0];
+            X(1) = W[1];
+            BODY_00_15(0, A, B, C, D, E, T, X(0));
+            X(2) = W[2];
+            BODY_00_15(1, T, A, B, C, D, E, X(1));
+            X(3) = W[3];
+            BODY_00_15(2, E, T, A, B, C, D, X(2));
+            X(4) = W[4];
+            BODY_00_15(3, D, E, T, A, B, C, X(3));
+            X(5) = W[5];
+            BODY_00_15(4, C, D, E, T, A, B, X(4));
+            X(6) = W[6];
+            BODY_00_15(5, B, C, D, E, T, A, X(5));
+            X(7) = W[7];
+            BODY_00_15(6, A, B, C, D, E, T, X(6));
+            X(8) = W[8];
+            BODY_00_15(7, T, A, B, C, D, E, X(7));
+            X(9) = W[9];
+            BODY_00_15(8, E, T, A, B, C, D, X(8));
+            X(10) = W[10];
+            BODY_00_15(9, D, E, T, A, B, C, X(9));
+            X(11) = W[11];
+            BODY_00_15(10, C, D, E, T, A, B, X(10));
+            X(12) = W[12];
+            BODY_00_15(11, B, C, D, E, T, A, X(11));
+            X(13) = W[13];
+            BODY_00_15(12, A, B, C, D, E, T, X(12));
+            X(14) = W[14];
+            BODY_00_15(13, T, A, B, C, D, E, X(13));
+            X(15) = W[15];
+            BODY_00_15(14, E, T, A, B, C, D, X(14));
+            BODY_00_15(15, D, E, T, A, B, C, X(15));
+
+            data += SHA_CBLOCK;
+        } else {
+            (void)HOST_c2l(data, l);
+            X(0) = l;
+            (void)HOST_c2l(data, l);
+            X(1) = l;
+            BODY_00_15(0, A, B, C, D, E, T, X(0));
+            (void)HOST_c2l(data, l);
+            X(2) = l;
+            BODY_00_15(1, T, A, B, C, D, E, X(1));
+            (void)HOST_c2l(data, l);
+            X(3) = l;
+            BODY_00_15(2, E, T, A, B, C, D, X(2));
+            (void)HOST_c2l(data, l);
+            X(4) = l;
+            BODY_00_15(3, D, E, T, A, B, C, X(3));
+            (void)HOST_c2l(data, l);
+            X(5) = l;
+            BODY_00_15(4, C, D, E, T, A, B, X(4));
+            (void)HOST_c2l(data, l);
+            X(6) = l;
+            BODY_00_15(5, B, C, D, E, T, A, X(5));
+            (void)HOST_c2l(data, l);
+            X(7) = l;
+            BODY_00_15(6, A, B, C, D, E, T, X(6));
+            (void)HOST_c2l(data, l);
+            X(8) = l;
+            BODY_00_15(7, T, A, B, C, D, E, X(7));
+            (void)HOST_c2l(data, l);
+            X(9) = l;
+            BODY_00_15(8, E, T, A, B, C, D, X(8));
+            (void)HOST_c2l(data, l);
+            X(10) = l;
+            BODY_00_15(9, D, E, T, A, B, C, X(9));
+            (void)HOST_c2l(data, l);
+            X(11) = l;
+            BODY_00_15(10, C, D, E, T, A, B, X(10));
+            (void)HOST_c2l(data, l);
+            X(12) = l;
+            BODY_00_15(11, B, C, D, E, T, A, X(11));
+            (void)HOST_c2l(data, l);
+            X(13) = l;
+            BODY_00_15(12, A, B, C, D, E, T, X(12));
+            (void)HOST_c2l(data, l);
+            X(14) = l;
+            BODY_00_15(13, T, A, B, C, D, E, X(13));
+            (void)HOST_c2l(data, l);
+            X(15) = l;
+            BODY_00_15(14, E, T, A, B, C, D, X(14));
+            BODY_00_15(15, D, E, T, A, B, C, X(15));
+        }
+
+        BODY_16_19(16, C, D, E, T, A, B, X(0), X(0), X(2), X(8), X(13));
+        BODY_16_19(17, B, C, D, E, T, A, X(1), X(1), X(3), X(9), X(14));
+        BODY_16_19(18, A, B, C, D, E, T, X(2), X(2), X(4), X(10), X(15));
+        BODY_16_19(19, T, A, B, C, D, E, X(3), X(3), X(5), X(11), X(0));
+
+        BODY_20_31(20, E, T, A, B, C, D, X(4), X(4), X(6), X(12), X(1));
+        BODY_20_31(21, D, E, T, A, B, C, X(5), X(5), X(7), X(13), X(2));
+        BODY_20_31(22, C, D, E, T, A, B, X(6), X(6), X(8), X(14), X(3));
+        BODY_20_31(23, B, C, D, E, T, A, X(7), X(7), X(9), X(15), X(4));
+        BODY_20_31(24, A, B, C, D, E, T, X(8), X(8), X(10), X(0), X(5));
+        BODY_20_31(25, T, A, B, C, D, E, X(9), X(9), X(11), X(1), X(6));
+        BODY_20_31(26, E, T, A, B, C, D, X(10), X(10), X(12), X(2), X(7));
+        BODY_20_31(27, D, E, T, A, B, C, X(11), X(11), X(13), X(3), X(8));
+        BODY_20_31(28, C, D, E, T, A, B, X(12), X(12), X(14), X(4), X(9));
+        BODY_20_31(29, B, C, D, E, T, A, X(13), X(13), X(15), X(5), X(10));
+        BODY_20_31(30, A, B, C, D, E, T, X(14), X(14), X(0), X(6), X(11));
+        BODY_20_31(31, T, A, B, C, D, E, X(15), X(15), X(1), X(7), X(12));
+
+        BODY_32_39(32, E, T, A, B, C, D, X(0), X(2), X(8), X(13));
+        BODY_32_39(33, D, E, T, A, B, C, X(1), X(3), X(9), X(14));
+        BODY_32_39(34, C, D, E, T, A, B, X(2), X(4), X(10), X(15));
+        BODY_32_39(35, B, C, D, E, T, A, X(3), X(5), X(11), X(0));
+        BODY_32_39(36, A, B, C, D, E, T, X(4), X(6), X(12), X(1));
+        BODY_32_39(37, T, A, B, C, D, E, X(5), X(7), X(13), X(2));
+        BODY_32_39(38, E, T, A, B, C, D, X(6), X(8), X(14), X(3));
+        BODY_32_39(39, D, E, T, A, B, C, X(7), X(9), X(15), X(4));
+
+        BODY_40_59(40, C, D, E, T, A, B, X(8), X(10), X(0), X(5));
+        BODY_40_59(41, B, C, D, E, T, A, X(9), X(11), X(1), X(6));
+        BODY_40_59(42, A, B, C, D, E, T, X(10), X(12), X(2), X(7));
+        BODY_40_59(43, T, A, B, C, D, E, X(11), X(13), X(3), X(8));
+        BODY_40_59(44, E, T, A, B, C, D, X(12), X(14), X(4), X(9));
+        BODY_40_59(45, D, E, T, A, B, C, X(13), X(15), X(5), X(10));
+        BODY_40_59(46, C, D, E, T, A, B, X(14), X(0), X(6), X(11));
+        BODY_40_59(47, B, C, D, E, T, A, X(15), X(1), X(7), X(12));
+        BODY_40_59(48, A, B, C, D, E, T, X(0), X(2), X(8), X(13));
+        BODY_40_59(49, T, A, B, C, D, E, X(1), X(3), X(9), X(14));
+        BODY_40_59(50, E, T, A, B, C, D, X(2), X(4), X(10), X(15));
+        BODY_40_59(51, D, E, T, A, B, C, X(3), X(5), X(11), X(0));
+        BODY_40_59(52, C, D, E, T, A, B, X(4), X(6), X(12), X(1));
+        BODY_40_59(53, B, C, D, E, T, A, X(5), X(7), X(13), X(2));
+        BODY_40_59(54, A, B, C, D, E, T, X(6), X(8), X(14), X(3));
+        BODY_40_59(55, T, A, B, C, D, E, X(7), X(9), X(15), X(4));
+        BODY_40_59(56, E, T, A, B, C, D, X(8), X(10), X(0), X(5));
+        BODY_40_59(57, D, E, T, A, B, C, X(9), X(11), X(1), X(6));
+        BODY_40_59(58, C, D, E, T, A, B, X(10), X(12), X(2), X(7));
+        BODY_40_59(59, B, C, D, E, T, A, X(11), X(13), X(3), X(8));
+
+        BODY_60_79(60, A, B, C, D, E, T, X(12), X(14), X(4), X(9));
+        BODY_60_79(61, T, A, B, C, D, E, X(13), X(15), X(5), X(10));
+        BODY_60_79(62, E, T, A, B, C, D, X(14), X(0), X(6), X(11));
+        BODY_60_79(63, D, E, T, A, B, C, X(15), X(1), X(7), X(12));
+        BODY_60_79(64, C, D, E, T, A, B, X(0), X(2), X(8), X(13));
+        BODY_60_79(65, B, C, D, E, T, A, X(1), X(3), X(9), X(14));
+        BODY_60_79(66, A, B, C, D, E, T, X(2), X(4), X(10), X(15));
+        BODY_60_79(67, T, A, B, C, D, E, X(3), X(5), X(11), X(0));
+        BODY_60_79(68, E, T, A, B, C, D, X(4), X(6), X(12), X(1));
+        BODY_60_79(69, D, E, T, A, B, C, X(5), X(7), X(13), X(2));
+        BODY_60_79(70, C, D, E, T, A, B, X(6), X(8), X(14), X(3));
+        BODY_60_79(71, B, C, D, E, T, A, X(7), X(9), X(15), X(4));
+        BODY_60_79(72, A, B, C, D, E, T, X(8), X(10), X(0), X(5));
+        BODY_60_79(73, T, A, B, C, D, E, X(9), X(11), X(1), X(6));
+        BODY_60_79(74, E, T, A, B, C, D, X(10), X(12), X(2), X(7));
+        BODY_60_79(75, D, E, T, A, B, C, X(11), X(13), X(3), X(8));
+        BODY_60_79(76, C, D, E, T, A, B, X(12), X(14), X(4), X(9));
+        BODY_60_79(77, B, C, D, E, T, A, X(13), X(15), X(5), X(10));
+        BODY_60_79(78, A, B, C, D, E, T, X(14), X(0), X(6), X(11));
+        BODY_60_79(79, T, A, B, C, D, E, X(15), X(1), X(7), X(12));
+
+        c->h0 = (c->h0 + E) & 0xffffffffL;
+        c->h1 = (c->h1 + T) & 0xffffffffL;
+        c->h2 = (c->h2 + A) & 0xffffffffL;
+        c->h3 = (c->h3 + B) & 0xffffffffL;
+        c->h4 = (c->h4 + C) & 0xffffffffL;
+
+        if (--num == 0)
+            break;
+
+        A = c->h0;
+        B = c->h1;
+        C = c->h2;
+        D = c->h3;
+        E = c->h4;
+
+    }
+}
+# endif
+
+#else                           /* OPENSSL_SMALL_FOOTPRINT */
+
+# define BODY_00_15(xi)           do {   \
+        T=E+K_00_19+F_00_19(B,C,D);     \
+        E=D, D=C, C=ROTATE(B,30), B=A;  \
+        A=ROTATE(A,5)+T+xi;         } while(0)
+
+# define BODY_16_19(xa,xb,xc,xd)  do {   \
+        Xupdate(T,xa,xa,xb,xc,xd);      \
+        T+=E+K_00_19+F_00_19(B,C,D);    \
+        E=D, D=C, C=ROTATE(B,30), B=A;  \
+        A=ROTATE(A,5)+T;            } while(0)
+
+# define BODY_20_39(xa,xb,xc,xd)  do {   \
+        Xupdate(T,xa,xa,xb,xc,xd);      \
+        T+=E+K_20_39+F_20_39(B,C,D);    \
+        E=D, D=C, C=ROTATE(B,30), B=A;  \
+        A=ROTATE(A,5)+T;            } while(0)
+
+# define BODY_40_59(xa,xb,xc,xd)  do {   \
+        Xupdate(T,xa,xa,xb,xc,xd);      \
+        T+=E+K_40_59+F_40_59(B,C,D);    \
+        E=D, D=C, C=ROTATE(B,30), B=A;  \
+        A=ROTATE(A,5)+T;            } while(0)
+
+# define BODY_60_79(xa,xb,xc,xd)  do {   \
+        Xupdate(T,xa,xa,xb,xc,xd);      \
+        T=E+K_60_79+F_60_79(B,C,D);     \
+        E=D, D=C, C=ROTATE(B,30), B=A;  \
+        A=ROTATE(A,5)+T+xa;         } while(0)
+
+# if !defined(SHA1_ASM)
+static void HASH_BLOCK_DATA_ORDER(SHA_CTX *c, const void *p, size_t num)
+{
+    const unsigned char *data = p;
+    register unsigned MD32_REG_T A, B, C, D, E, T, l;
+    int i;
+    SHA_LONG X[16];
+
+    A = c->h0;
+    B = c->h1;
+    C = c->h2;
+    D = c->h3;
+    E = c->h4;
+
+    for (;;) {
+        for (i = 0; i < 16; i++) {
+            (void)HOST_c2l(data, l);
+            X[i] = l;
+            BODY_00_15(X[i]);
+        }
+        for (i = 0; i < 4; i++) {
+            BODY_16_19(X[i], X[i + 2], X[i + 8], X[(i + 13) & 15]);
+        }
+        for (; i < 24; i++) {
+            BODY_20_39(X[i & 15], X[(i + 2) & 15], X[(i + 8) & 15],
+                       X[(i + 13) & 15]);
+        }
+        for (i = 0; i < 20; i++) {
+            BODY_40_59(X[(i + 8) & 15], X[(i + 10) & 15], X[i & 15],
+                       X[(i + 5) & 15]);
+        }
+        for (i = 4; i < 24; i++) {
+            BODY_60_79(X[(i + 8) & 15], X[(i + 10) & 15], X[i & 15],
+                       X[(i + 5) & 15]);
+        }
+
+        c->h0 = (c->h0 + A) & 0xffffffffL;
+        c->h1 = (c->h1 + B) & 0xffffffffL;
+        c->h2 = (c->h2 + C) & 0xffffffffL;
+        c->h3 = (c->h3 + D) & 0xffffffffL;
+        c->h4 = (c->h4 + E) & 0xffffffffL;
+
+        if (--num == 0)
+            break;
+
+        A = c->h0;
+        B = c->h1;
+        C = c->h2;
+        D = c->h3;
+        E = c->h4;
+
+    }
+}
+# endif
+
+#endif
diff --git a/crypto/skf/build.info b/crypto/skf/build.info
new file mode 100644
index 000000000..369b09bfc
--- /dev/null
+++ b/crypto/skf/build.info
@@ -0,0 +1,2 @@
+LIBS=../../libcrypto
+SOURCE[../../libcrypto]=skf_err.c skf_lib.c skf_meth.c skf_ext.c skf_prn.c skf_wisec.c
diff --git a/crypto/skf/skf_err.c b/crypto/skf/skf_err.c
new file mode 100644
index 000000000..9d88bd336
--- /dev/null
+++ b/crypto/skf/skf_err.c
@@ -0,0 +1,223 @@
+/*
+ * Generated by util/mkerr.pl DO NOT EDIT
+ * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include 
+
+/* BEGIN ERROR CODES */
+#ifndef OPENSSL_NO_ERR
+
+# define ERR_FUNC(func) ERR_PACK(ERR_LIB_SKF,func,0)
+# define ERR_REASON(reason) ERR_PACK(ERR_LIB_SKF,0,reason)
+
+static ERR_STRING_DATA SKF_str_functs[] = {
+    {ERR_FUNC(SKF_F_SKF_CANCELWAITFORDEVEVENT), "SKF_CancelWaitForDevEvent"},
+    {ERR_FUNC(SKF_F_SKF_CHANGEDEVAUTHKEY), "SKF_ChangeDevAuthKey"},
+    {ERR_FUNC(SKF_F_SKF_CHANGEPIN), "SKF_ChangePIN"},
+    {ERR_FUNC(SKF_F_SKF_CLEARSECURESTATE), "SKF_ClearSecureState"},
+    {ERR_FUNC(SKF_F_SKF_CLOSEAPPLICATION), "SKF_CloseApplication"},
+    {ERR_FUNC(SKF_F_SKF_CLOSECONTAINER), "SKF_CloseContainer"},
+    {ERR_FUNC(SKF_F_SKF_CLOSEDEVICE), "SKF_CloseDevice"},
+    {ERR_FUNC(SKF_F_SKF_CLOSEHANDLE), "SKF_CloseHandle"},
+    {ERR_FUNC(SKF_F_SKF_CONNECTDEV), "SKF_ConnectDev"},
+    {ERR_FUNC(SKF_F_SKF_CREATEAPPLICATION), "SKF_CreateApplication"},
+    {ERR_FUNC(SKF_F_SKF_CREATECONTAINER), "SKF_CreateContainer"},
+    {ERR_FUNC(SKF_F_SKF_CREATEFILE), "SKF_CreateFile"},
+    {ERR_FUNC(SKF_F_SKF_DECRYPT), "SKF_Decrypt"},
+    {ERR_FUNC(SKF_F_SKF_DECRYPTFINAL), "SKF_DecryptFinal"},
+    {ERR_FUNC(SKF_F_SKF_DECRYPTINIT), "SKF_DecryptInit"},
+    {ERR_FUNC(SKF_F_SKF_DECRYPTUPDATE), "SKF_DecryptUpdate"},
+    {ERR_FUNC(SKF_F_SKF_DELETEAPPLICATION), "SKF_DeleteApplication"},
+    {ERR_FUNC(SKF_F_SKF_DELETECONTAINER), "SKF_DeleteContainer"},
+    {ERR_FUNC(SKF_F_SKF_DELETEFILE), "SKF_DeleteFile"},
+    {ERR_FUNC(SKF_F_SKF_DEVAUTH), "SKF_DevAuth"},
+    {ERR_FUNC(SKF_F_SKF_DIGEST), "SKF_Digest"},
+    {ERR_FUNC(SKF_F_SKF_DIGESTFINAL), "SKF_DigestFinal"},
+    {ERR_FUNC(SKF_F_SKF_DIGESTINIT), "SKF_DigestInit"},
+    {ERR_FUNC(SKF_F_SKF_DIGESTUPDATE), "SKF_DigestUpdate"},
+    {ERR_FUNC(SKF_F_SKF_DISCONNECTDEV), "SKF_DisConnectDev"},
+    {ERR_FUNC(SKF_F_SKF_ECCDECRYPT), "SKF_ECCDecrypt"},
+    {ERR_FUNC(SKF_F_SKF_ECCEXPORTSESSIONKEY), "SKF_ECCExportSessionKey"},
+    {ERR_FUNC(SKF_F_SKF_ECCSIGNDATA), "SKF_ECCSignData"},
+    {ERR_FUNC(SKF_F_SKF_ECCVERIFY), "SKF_ECCVerify"},
+    {ERR_FUNC(SKF_F_SKF_ENCRYPT), "SKF_Encrypt"},
+    {ERR_FUNC(SKF_F_SKF_ENCRYPTFINAL), "SKF_EncryptFinal"},
+    {ERR_FUNC(SKF_F_SKF_ENCRYPTINIT), "SKF_EncryptInit"},
+    {ERR_FUNC(SKF_F_SKF_ENCRYPTUPDATE), "SKF_EncryptUpdate"},
+    {ERR_FUNC(SKF_F_SKF_ENUMAPPLICATION), "SKF_EnumApplication"},
+    {ERR_FUNC(SKF_F_SKF_ENUMCONTAINER), "SKF_EnumContainer"},
+    {ERR_FUNC(SKF_F_SKF_ENUMDEV), "SKF_EnumDev"},
+    {ERR_FUNC(SKF_F_SKF_ENUMFILES), "SKF_EnumFiles"},
+    {ERR_FUNC(SKF_F_SKF_EXPORTCERTIFICATE), "SKF_ExportCertificate"},
+    {ERR_FUNC(SKF_F_SKF_EXPORTECCPUBLICKEY), "SKF_ExportECCPublicKey"},
+    {ERR_FUNC(SKF_F_SKF_EXPORTEVPPUBLICKEY), "SKF_ExportEVPPublicKey"},
+    {ERR_FUNC(SKF_F_SKF_EXPORTPUBLICKEY), "SKF_ExportPublicKey"},
+    {ERR_FUNC(SKF_F_SKF_EXPORTRSAPUBLICKEY), "SKF_ExportRSAPublicKey"},
+    {ERR_FUNC(SKF_F_SKF_EXPORTX509CERTIFICATE), "SKF_ExportX509Certificate"},
+    {ERR_FUNC(SKF_F_SKF_EXTECCDECRYPT), "SKF_ExtECCDecrypt"},
+    {ERR_FUNC(SKF_F_SKF_EXTECCENCRYPT), "SKF_ExtECCEncrypt"},
+    {ERR_FUNC(SKF_F_SKF_EXTECCSIGN), "SKF_ExtECCSign"},
+    {ERR_FUNC(SKF_F_SKF_EXTECCVERIFY), "SKF_ExtECCVerify"},
+    {ERR_FUNC(SKF_F_SKF_EXTRSAPRIKEYOPERATION), "SKF_ExtRSAPriKeyOperation"},
+    {ERR_FUNC(SKF_F_SKF_EXTRSAPUBKEYOPERATION), "SKF_ExtRSAPubKeyOperation"},
+    {ERR_FUNC(SKF_F_SKF_GENECCKEYPAIR), "SKF_GenECCKeyPair"},
+    {ERR_FUNC(SKF_F_SKF_GENERATEAGREEMENTDATAANDKEYWITHECC),
+     "SKF_GenerateAgreementDataAndKeyWithECC"},
+    {ERR_FUNC(SKF_F_SKF_GENERATEAGREEMENTDATAWITHECC),
+     "SKF_GenerateAgreementDataWithECC"},
+    {ERR_FUNC(SKF_F_SKF_GENERATEKEYWITHECC), "SKF_GenerateKeyWithECC"},
+    {ERR_FUNC(SKF_F_SKF_GENEXTRSAKEY), "SKF_GenExtRSAKey"},
+    {ERR_FUNC(SKF_F_SKF_GENRANDOM), "SKF_GenRandom"},
+    {ERR_FUNC(SKF_F_SKF_GENRSAKEYPAIR), "SKF_GenRSAKeyPair"},
+    {ERR_FUNC(SKF_F_SKF_GETCONTAINERTYPE), "SKF_GetContainerType"},
+    {ERR_FUNC(SKF_F_SKF_GETDEVINFO), "SKF_GetDevInfo"},
+    {ERR_FUNC(SKF_F_SKF_GETDEVSTATE), "SKF_GetDevState"},
+    {ERR_FUNC(SKF_F_SKF_GETFILEINFO), "SKF_GetFileInfo"},
+    {ERR_FUNC(SKF_F_SKF_GETPININFO), "SKF_GetPINInfo"},
+    {ERR_FUNC(SKF_F_SKF_IMPORTCERTIFICATE), "SKF_ImportCertificate"},
+    {ERR_FUNC(SKF_F_SKF_IMPORTECCKEYPAIR), "SKF_ImportECCKeyPair"},
+    {ERR_FUNC(SKF_F_SKF_IMPORTECCPRIVATEKEY), "SKF_ImportECCPrivateKey"},
+    {ERR_FUNC(SKF_F_SKF_IMPORTPRIVATEKEY), "SKF_ImportPrivateKey"},
+    {ERR_FUNC(SKF_F_SKF_IMPORTRSAKEYPAIR), "SKF_ImportRSAKeyPair"},
+    {ERR_FUNC(SKF_F_SKF_IMPORTRSAPRIVATEKEY), "SKF_ImportRSAPrivateKey"},
+    {ERR_FUNC(SKF_F_SKF_IMPORTSESSIONKEY), "SKF_ImportSessionKey"},
+    {ERR_FUNC(SKF_F_SKF_IMPORTX509CERTIFICATEBYKEYUSAGE),
+     "SKF_ImportX509CertificateByKeyUsage"},
+    {ERR_FUNC(SKF_F_SKF_LOADLIBRARY), "SKF_LoadLibrary"},
+    {ERR_FUNC(SKF_F_SKF_LOCKDEV), "SKF_LockDev"},
+    {ERR_FUNC(SKF_F_SKF_MAC), "SKF_Mac"},
+    {ERR_FUNC(SKF_F_SKF_MACFINAL), "SKF_MacFinal"},
+    {ERR_FUNC(SKF_F_SKF_MACINIT), "SKF_MacInit"},
+    {ERR_FUNC(SKF_F_SKF_MACUPDATE), "SKF_MacUpdate"},
+    {ERR_FUNC(SKF_F_SKF_METHOD_LOAD_LIBRARY), "SKF_METHOD_load_library"},
+    {ERR_FUNC(SKF_F_SKF_NEWECCCIPHER), "SKF_NewECCCipher"},
+    {ERR_FUNC(SKF_F_SKF_NEWENVELOPEDKEY), "SKF_NewEnvelopedKey"},
+    {ERR_FUNC(SKF_F_SKF_OPENAPPLICATION), "SKF_OpenApplication"},
+    {ERR_FUNC(SKF_F_SKF_OPENCONTAINER), "SKF_OpenContainer"},
+    {ERR_FUNC(SKF_F_SKF_OPENDEVICE), "SKF_OpenDevice"},
+    {ERR_FUNC(SKF_F_SKF_READFILE), "SKF_ReadFile"},
+    {ERR_FUNC(SKF_F_SKF_RSAEXPORTSESSIONKEY), "SKF_RSAExportSessionKey"},
+    {ERR_FUNC(SKF_F_SKF_RSASIGNDATA), "SKF_RSASignData"},
+    {ERR_FUNC(SKF_F_SKF_RSAVERIFY), "SKF_RSAVerify"},
+    {ERR_FUNC(SKF_F_SKF_SETLABEL), "SKF_SetLabel"},
+    {ERR_FUNC(SKF_F_SKF_SETSYMMKEY), "SKF_SetSymmKey"},
+    {ERR_FUNC(SKF_F_SKF_TRANSMIT), "SKF_Transmit"},
+    {ERR_FUNC(SKF_F_SKF_UNBLOCKPIN), "SKF_UnblockPIN"},
+    {ERR_FUNC(SKF_F_SKF_UNLOCKDEV), "SKF_UnlockDev"},
+    {ERR_FUNC(SKF_F_SKF_VERIFYPIN), "SKF_VerifyPIN"},
+    {ERR_FUNC(SKF_F_SKF_WAITFORDEVEVENT), "SKF_WaitForDevEvent"},
+    {ERR_FUNC(SKF_F_SKF_WRITEFILE), "SKF_WriteFile"},
+    {0, NULL}
+};
+
+static ERR_STRING_DATA SKF_str_reasons[] = {
+    {ERR_REASON(SKF_R_APPLICATION_ALREADY_EXIST),
+     "application already exist"},
+    {ERR_REASON(SKF_R_APPLICATION_NOT_EXIST), "application not exist"},
+    {ERR_REASON(SKF_R_BUFFER_TOO_SMALL), "buffer too small"},
+    {ERR_REASON(SKF_R_CERTIFICATE_NOT_FOUND), "certificate not found"},
+    {ERR_REASON(SKF_R_CONTAINER_TYPE_NOT_MATCH), "container type not match"},
+    {ERR_REASON(SKF_R_CSP_IMPORT_PUBLIC_KEY_ERROR),
+     "csp import public key error"},
+    {ERR_REASON(SKF_R_DECRYPT_INVALID_PADDING), "decrypt invalid padding"},
+    {ERR_REASON(SKF_R_DEVICE_REMOVED), "device removed"},
+    {ERR_REASON(SKF_R_DIGEST_ERROR), "digest error"},
+    {ERR_REASON(SKF_R_DSO_LOAD_FAILURE), "dso load failure"},
+    {ERR_REASON(SKF_R_EXPORT_FAILED), "export failed"},
+    {ERR_REASON(SKF_R_FAILURE), "failure"},
+    {ERR_REASON(SKF_R_FILE_ALREADY_EXIST), "file already exist"},
+    {ERR_REASON(SKF_R_FILE_ERROR), "file error"},
+    {ERR_REASON(SKF_R_FILE_NOT_EXIST), "file not exist"},
+    {ERR_REASON(SKF_R_FUNCTION_NOT_SUPPORTED), "function not supported"},
+    {ERR_REASON(SKF_R_HASH_NOT_EQUAL), "hash not equal"},
+    {ERR_REASON(SKF_R_INVALID_APPLICATION_NAME), "invalid application name"},
+    {ERR_REASON(SKF_R_INVALID_CONTAINER_TYPE), "invalid container type"},
+    {ERR_REASON(SKF_R_INVALID_DIGEST_HANDLE), "invalid digest handle"},
+    {ERR_REASON(SKF_R_INVALID_ECC_PUBLIC_KEY), "invalid ecc public key"},
+    {ERR_REASON(SKF_R_INVALID_HANDLE), "invalid handle"},
+    {ERR_REASON(SKF_R_INVALID_INPUT_LENGTH), "invalid input length"},
+    {ERR_REASON(SKF_R_INVALID_INPUT_VALUE), "invalid input value"},
+    {ERR_REASON(SKF_R_INVALID_KEY_INFO_TYPE), "invalid key info type"},
+    {ERR_REASON(SKF_R_INVALID_KEY_USAGE), "invalid key usage"},
+    {ERR_REASON(SKF_R_INVALID_MAC_LENGTH), "invalid mac length"},
+    {ERR_REASON(SKF_R_INVALID_MODULUS_LENGTH), "invalid modulus length"},
+    {ERR_REASON(SKF_R_INVALID_NAME_LENGTH), "invalid name length"},
+    {ERR_REASON(SKF_R_INVALID_OBJECT), "invalid object"},
+    {ERR_REASON(SKF_R_INVALID_PARAMETER), "invalid parameter"},
+    {ERR_REASON(SKF_R_INVALID_PIN), "invalid pin"},
+    {ERR_REASON(SKF_R_INVALID_PIN_LENGTH), "invalid pin length"},
+    {ERR_REASON(SKF_R_INVALID_RSA_MODULUS_LENGTH),
+     "invalid rsa modulus length"},
+    {ERR_REASON(SKF_R_INVALID_RSA_PUBLIC_KEY), "invalid rsa public key"},
+    {ERR_REASON(SKF_R_INVALID_USER_TYPE), "invalid user type"},
+    {ERR_REASON(SKF_R_KEY_NOT_FOUND), "key not found"},
+    {ERR_REASON(SKF_R_LOAD_LIBRARY_FAILURE), "load library failure"},
+    {ERR_REASON(SKF_R_MEMORY_ERROR), "memory error"},
+    {ERR_REASON(SKF_R_NOT_INITIALIZED), "not initialized"},
+    {ERR_REASON(SKF_R_NOT_SUPPORTED_CIPHER_ALGOR),
+     "not supported cipher algor"},
+    {ERR_REASON(SKF_R_NOT_SUPPORTED_DIGEST_ALGOR),
+     "not supported digest algor"},
+    {ERR_REASON(SKF_R_NOT_SUPPORTED_PKEY_ALGOR), "not supported pkey algor"},
+    {ERR_REASON(SKF_R_NO_EVENT), "no event"},
+    {ERR_REASON(SKF_R_NO_SPACE), "no space"},
+    {ERR_REASON(SKF_R_OPERATION_NOT_SUPPORTED), "operation not supported"},
+    {ERR_REASON(SKF_R_PARSE_CERTIFICATE_FAILURE),
+     "parse certificate failure"},
+    {ERR_REASON(SKF_R_PIN_INCORRECT), "pin incorrect"},
+    {ERR_REASON(SKF_R_PIN_LOCKED), "pin locked"},
+    {ERR_REASON(SKF_R_RANDOM_GENERATION_FAILED), "random generation failed"},
+    {ERR_REASON(SKF_R_READ_FILE_FAILURE), "read file failure"},
+    {ERR_REASON(SKF_R_RSA_DECRYPTION_FAILURE), "rsa decryption failure"},
+    {ERR_REASON(SKF_R_RSA_ENCRYPTION_FAILURE), "rsa encryption failure"},
+    {ERR_REASON(SKF_R_RSA_KEY_GENERATION_FAILURE),
+     "rsa key generation failure"},
+    {ERR_REASON(SKF_R_SKF_METHOD_NOT_INITIALIZED),
+     "skf method not initialized"},
+    {ERR_REASON(SKF_R_SUCCESS), "success"},
+    {ERR_REASON(SKF_R_TIMEOUT), "timeout"},
+    {ERR_REASON(SKF_R_UNKNOWN_CERTIFICATE_KEYUSAGE),
+     "unknown certificate keyusage"},
+    {ERR_REASON(SKF_R_UNKNOWN_ERROR), "unknown error"},
+    {ERR_REASON(SKF_R_UNKNOWN_VENDOR), "unknown vendor"},
+    {ERR_REASON(SKF_R_UNSUPPORTED_PRIVATE_KEY_TYPE),
+     "unsupported private key type"},
+    {ERR_REASON(SKF_R_USER_ALREADY_LOGGED_IN), "user already logged in"},
+    {ERR_REASON(SKF_R_USER_NOT_LOGGED_IN), "user not logged in"},
+    {ERR_REASON(SKF_R_USER_PIN_NOT_INITIALIZED), "user pin not initialized"},
+    {ERR_REASON(SKF_R_WISEC_AUTH_BLOCKED), "wisec auth blocked"},
+    {ERR_REASON(SKF_R_WISEC_CERTNOUSAGEERR), "wisec certnousageerr"},
+    {ERR_REASON(SKF_R_WISEC_CERTUSAGEERR), "wisec certusageerr"},
+    {ERR_REASON(SKF_R_WISEC_CONTAINER_EXISTS), "wisec container exists"},
+    {ERR_REASON(SKF_R_WISEC_CONTAINER_NOT_EXISTS),
+     "wisec container not exists"},
+    {ERR_REASON(SKF_R_WISEC_DEVNOAUTH), "wisec devnoauth"},
+    {ERR_REASON(SKF_R_WISEC_FILEATTRIBUTEERR), "wisec fileattributeerr"},
+    {ERR_REASON(SKF_R_WISEC_INVALIDCONTAINERERR),
+     "wisec invalidcontainererr"},
+    {ERR_REASON(SKF_R_WISEC_KEYNOUSAGEERR), "wisec keynousageerr"},
+    {ERR_REASON(SKF_R_WRITE_FILE_FAILURE), "write file failure"},
+    {0, NULL}
+};
+
+#endif
+
+int ERR_load_SKF_strings(void)
+{
+#ifndef OPENSSL_NO_ERR
+
+    if (ERR_func_error_string(SKF_str_functs[0].error) == NULL) {
+        ERR_load_strings(0, SKF_str_functs);
+        ERR_load_strings(0, SKF_str_reasons);
+    }
+#endif
+    return 1;
+}
diff --git a/src/skf/skf_ext.c b/crypto/skf/skf_ext.c
old mode 100755
new mode 100644
similarity index 83%
rename from src/skf/skf_ext.c
rename to crypto/skf/skf_ext.c
index c76dc38bf..fcf302135
--- a/src/skf/skf_ext.c
+++ b/crypto/skf/skf_ext.c
@@ -1,29 +1,70 @@
-/*
- *  Copyright 2014-2022 The GmSSL Project. All Rights Reserved.
+/* ====================================================================
+ * Copyright (c) 2014 - 2019 The GmSSL Project.  All rights reserved.
  *
- *  Licensed under the Apache License, Version 2.0 (the License); you may
- *  not use this file except in compliance with the License.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
  *
- *  http://www.apache.org/licenses/LICENSE-2.0
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the GmSSL Project.
+ *    (http://gmssl.org/)"
+ *
+ * 4. The name "GmSSL Project" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    guanzhi1980@gmail.com.
+ *
+ * 5. Products derived from this software may not be called "GmSSL"
+ *    nor may "GmSSL" appear in their names without prior written
+ *    permission of the GmSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the GmSSL Project
+ *    (http://gmssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE GmSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE GmSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
  */
 
-
 #include 
 #include 
 #include 
 #include 
-#include "skf_int.h"
-#include "skf_ext.h"
-#include "skf.h"
+#include 
+#include 
+#include 
+#include 
+#include 
+#include "internal/skf_int.h"
+#include "../../e_os.h"
 
 
-#define SKFerr(f,e)
-
 ULONG DEVAPI SKF_NewECCCipher(ULONG ulCipherLen, ECCCIPHERBLOB **cipherBlob)
 {
 	ECCCIPHERBLOB *ret = NULL;
 
-	if (!(ret = malloc(sizeof(ECCCIPHERBLOB) - 1 + ulCipherLen))) {
+	if (!(ret = OPENSSL_malloc(sizeof(ECCCIPHERBLOB) - 1 + ulCipherLen))) {
 		SKFerr(SKF_F_SKF_NEWECCCIPHER, ERR_R_MALLOC_FAILURE);
 		return SAR_MEMORYERR;
 	}
@@ -37,7 +78,7 @@ ULONG DEVAPI SKF_NewEnvelopedKey(ULONG ulCipherLen, ENVELOPEDKEYBLOB **enveloped
 {
 	ENVELOPEDKEYBLOB *ret = NULL;
 
-	if (!(ret = malloc(sizeof(ENVELOPEDKEYBLOB) - 1 + ulCipherLen))) {
+	if (!(ret = OPENSSL_zalloc(sizeof(ENVELOPEDKEYBLOB) - 1 + ulCipherLen))) {
 		SKFerr(SKF_F_SKF_NEWENVELOPEDKEY, ERR_R_MALLOC_FAILURE);
 		return SAR_MEMORYERR;
 	}
@@ -74,8 +115,8 @@ ULONG DEVAPI SKF_OpenDevice(LPSTR devName, BYTE authKey[16], DEVINFO *devInfo, D
 	hDev = NULL;
 
 end:
-	//OPENSSL_cleanse(authRand, sizeof(authRand));
-	//OPENSSL_cleanse(authData, sizeof(authData));
+	OPENSSL_cleanse(authRand, sizeof(authRand));
+	OPENSSL_cleanse(authData, sizeof(authData));
 	if (hKey && (rv = SKF_CloseHandle(hKey)) != SAR_OK) {
 		SKFerr(SKF_F_SKF_OPENDEVICE, ERR_R_SKF_LIB);
 	}
@@ -97,7 +138,7 @@ ULONG DEVAPI SKF_CloseDevice(DEVHANDLE hDev)
 	return rv;
 }
 
-#if 0
+
 ULONG DEVAPI SKF_ImportECCPrivateKey(DEVHANDLE hDev, HCONTAINER hContainer,
 	EC_KEY *ec_key, ULONG symmAlgId)
 {
@@ -137,7 +178,7 @@ ULONG DEVAPI SKF_ImportECCPrivateKey(DEVHANDLE hDev, HCONTAINER hContainer,
 	envelopedKeyBlob.ulBits = eccPriKeyBlob.BitLen;
 
 	/* encrypt private key with random generated symmkey */
-	if (!rand_bytes(symmKey, sizeof(symmKey))) {
+	if (!RAND_bytes(symmKey, sizeof(symmKey))) {
 		SKFerr(SKF_F_SKF_IMPORTECCPRIVATEKEY, ERR_R_SKF_LIB);
 		rv = SAR_FAIL;
 		goto end;
@@ -227,7 +268,7 @@ ULONG DEVAPI SKF_ImportRSAPrivateKey(DEVHANDLE hDev, HCONTAINER hContainer,
 
 	/* generate symmkey */
 	/* wrap symmkey with signing public key */
-	if (!rand_bytes(symmKey, sizeof(symmKey))) {
+	if (!RAND_bytes(symmKey, sizeof(symmKey))) {
 		SKFerr(SKF_F_SKF_IMPORTRSAPRIVATEKEY, ERR_R_SKF_LIB);
 		goto end;
 	}
@@ -435,9 +476,7 @@ ULONG DEVAPI SKF_ExportEVPPublicKey(HCONTAINER hContainer, BOOL bSign, EVP_PKEY
 	EVP_PKEY_free(pkey);
 	return rv;
 }
-#endif
 
-/*
 ULONG DEVAPI SKF_ImportX509Certificate(HCONTAINER hContainer, BOOL bSign, X509 *x509)
 {
 	int ret = 0;
@@ -483,7 +522,7 @@ ULONG DEVAPI SKF_ImportX509Certificate(HCONTAINER hContainer, BOOL bSign, X509 *
 	}
 
 	if ((len = i2d_X509(x509, NULL)) <= 0
-		|| !(p = cert = malloc(len))
+		|| !(p = cert = OPENSSL_malloc(len))
 		|| (len = i2d_X509(x509, &p)) <= 0) {
 		goto end;
 	}
@@ -533,7 +572,7 @@ ULONG DEVAPI SKF_ExportX509Certificate(HCONTAINER hContainer, BOOL bSign, X509 *
 	X509 *x509 = NULL;
 
 	ulCertLen = SKF_MAX_CERTIFICATE_SIZE;
-	if (!(pbCert = malloc(ulCertLen))) {
+	if (!(pbCert = OPENSSL_zalloc(ulCertLen))) {
 		SKFerr(SKF_F_SKF_EXPORTX509CERTIFICATE, ERR_R_MALLOC_FAILURE);
 		rv = SAR_MEMORYERR;
 		goto end;
@@ -565,4 +604,3 @@ ULONG DEVAPI SKF_ExportX509Certificate(HCONTAINER hContainer, BOOL bSign, X509 *
 	X509_free(x509);
 	return rv;
 }
-*/
diff --git a/src/skf/skf_lib.c b/crypto/skf/skf_lib.c
old mode 100755
new mode 100644
similarity index 95%
rename from src/skf/skf_lib.c
rename to crypto/skf/skf_lib.c
index 1656e574c..005f24417
--- a/src/skf/skf_lib.c
+++ b/crypto/skf/skf_lib.c
@@ -1,29 +1,67 @@
-/*
- *  Copyright 2014-2022 The GmSSL Project. All Rights Reserved.
+/* ====================================================================
+ * Copyright (c) 2014 - 2017 The GmSSL Project.  All rights reserved.
  *
- *  Licensed under the Apache License, Version 2.0 (the License); you may
- *  not use this file except in compliance with the License.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
  *
- *  http://www.apache.org/licenses/LICENSE-2.0
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the GmSSL Project.
+ *    (http://gmssl.org/)"
+ *
+ * 4. The name "GmSSL Project" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    guanzhi1980@gmail.com.
+ *
+ * 5. Products derived from this software may not be called "GmSSL"
+ *    nor may "GmSSL" appear in their names without prior written
+ *    permission of the GmSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the GmSSL Project
+ *    (http://gmssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE GmSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE GmSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
  */
 
-
 #include 
 #include 
 #include 
-#include "skf_int.h"
-
+#include 
+#include 
+#include "internal/dso.h"
+#include "internal/skf_int.h"
+#include "../../e_os.h"
 
 
 SKF_METHOD *skf_method = NULL;
 SKF_VENDOR *skf_vendor = NULL;
 extern SKF_VENDOR skf_wisec;
 
-
-#define SKFerr(f,e)
-
-
-ULONG DEVAPI SKF_LoadLibrary(LPSTR so_path, LPSTR vendor)
+ULONG SKF_LoadLibrary(LPSTR so_path, LPSTR vendor)
 {
 	if (skf_method) {
 		SKF_METHOD_free(skf_method);
@@ -47,7 +85,7 @@ ULONG DEVAPI SKF_LoadLibrary(LPSTR so_path, LPSTR vendor)
 	return SAR_OK;
 }
 
-ULONG DEVAPI SKF_UnloadLibrary(void)
+ULONG SKF_UnloadLibrary(void)
 {
 	SKF_METHOD_free(skf_method);
 	skf_method = NULL;
@@ -55,7 +93,6 @@ ULONG DEVAPI SKF_UnloadLibrary(void)
 	return SAR_OK;
 }
 
-/*
 static SKF_ERR_REASON skf_errors[] = {
 	{ SAR_OK,			SKF_R_SUCCESS },
 	{ SAR_FAIL,			SKF_R_FAILURE },
@@ -108,11 +145,9 @@ static SKF_ERR_REASON skf_errors[] = {
 	{ SAR_NO_ROOM,			SKF_R_NO_SPACE },
 	{ SAR_FILE_NOT_EXIST,		SKF_R_FILE_NOT_EXIST },
 };
-*/
 
 static unsigned long skf_get_error_reason(ULONG ulError)
 {
-/*
 	int i;
 	for (i = 0; i < OSSL_NELEM(skf_errors); i++) {
 		if (ulError == skf_errors[i].err) {
@@ -122,16 +157,15 @@ static unsigned long skf_get_error_reason(ULONG ulError)
 	if (skf_vendor) {
 		return skf_vendor->get_error_reason(ulError);
 	}
-*/
 	return 0;
 }
 
-ULONG DEVAPI SKF_GetErrorString(ULONG ulError, LPSTR *szErrorStr)
+ULONG SKF_GetErrorString(ULONG ulError, LPSTR *szErrorStr)
 {
 	unsigned long reason;
 
 	if ((reason = skf_get_error_reason(ulError)) != 0) {
-		//*szErrorStr = (LPSTR)ERR_reason_error_string(reason);
+		*szErrorStr = (LPSTR)ERR_reason_error_string(reason);
 	} else {
 		*szErrorStr = (LPSTR)"(unknown)";
 	}
diff --git a/crypto/skf/skf_meth.c b/crypto/skf/skf_meth.c
new file mode 100644
index 000000000..a5fad81f9
--- /dev/null
+++ b/crypto/skf/skf_meth.c
@@ -0,0 +1,172 @@
+/* ====================================================================
+ * Copyright (c) 2014 - 2017 The GmSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the GmSSL Project.
+ *    (http://gmssl.org/)"
+ *
+ * 4. The name "GmSSL Project" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    guanzhi1980@gmail.com.
+ *
+ * 5. Products derived from this software may not be called "GmSSL"
+ *    nor may "GmSSL" appear in their names without prior written
+ *    permission of the GmSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the GmSSL Project
+ *    (http://gmssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE GmSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE GmSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+
+#include 
+#include 
+#include "internal/dso.h"
+#include "internal/skf_int.h"
+
+
+#define SKF_METHOD_BIND_FUNCTION_EX(func,name) \
+	skf->func = (SKF_##func##_FuncPtr)DSO_bind_func(skf->dso, "SKF_"#name)
+
+#define SKF_METHOD_BIND_FUNCTION(func) \
+	SKF_METHOD_BIND_FUNCTION_EX(func,func)
+
+
+SKF_METHOD *SKF_METHOD_load_library(const char *so_path)
+{
+	SKF_METHOD *ret = NULL;
+	SKF_METHOD *skf = NULL;
+
+	if (!(skf = OPENSSL_zalloc(sizeof(*skf)))) {
+		SKFerr(SKF_F_SKF_METHOD_LOAD_LIBRARY, ERR_R_MALLOC_FAILURE);
+		goto end;
+	}
+	if (!(skf->dso = DSO_load(NULL, so_path, NULL, 0))) {
+		SKFerr(SKF_F_SKF_METHOD_LOAD_LIBRARY, SKF_R_DSO_LOAD_FAILURE);
+		goto end;
+	}
+
+	SKF_METHOD_BIND_FUNCTION(WaitForDevEvent);
+	SKF_METHOD_BIND_FUNCTION(CancelWaitForDevEvent);
+	SKF_METHOD_BIND_FUNCTION(EnumDev);
+	SKF_METHOD_BIND_FUNCTION(ConnectDev);
+	SKF_METHOD_BIND_FUNCTION(DisConnectDev);
+	SKF_METHOD_BIND_FUNCTION(GetDevState);
+	SKF_METHOD_BIND_FUNCTION(SetLabel);
+	SKF_METHOD_BIND_FUNCTION(GetDevInfo);
+	SKF_METHOD_BIND_FUNCTION(LockDev);
+	SKF_METHOD_BIND_FUNCTION(UnlockDev);
+	SKF_METHOD_BIND_FUNCTION(Transmit);
+	SKF_METHOD_BIND_FUNCTION(ChangeDevAuthKey);
+	SKF_METHOD_BIND_FUNCTION(DevAuth);
+	SKF_METHOD_BIND_FUNCTION(ChangePIN);
+	SKF_METHOD_BIND_FUNCTION(GetPINInfo);
+	SKF_METHOD_BIND_FUNCTION(VerifyPIN);
+	SKF_METHOD_BIND_FUNCTION(UnblockPIN);
+	SKF_METHOD_BIND_FUNCTION(ClearSecureState);
+	SKF_METHOD_BIND_FUNCTION(CreateApplication);
+	SKF_METHOD_BIND_FUNCTION(EnumApplication);
+	SKF_METHOD_BIND_FUNCTION(DeleteApplication);
+	SKF_METHOD_BIND_FUNCTION(OpenApplication);
+	SKF_METHOD_BIND_FUNCTION(CloseApplication);
+	SKF_METHOD_BIND_FUNCTION_EX(CreateObject,CreateFile);
+	SKF_METHOD_BIND_FUNCTION_EX(DeleteObject,DeleteFile);
+	SKF_METHOD_BIND_FUNCTION_EX(EnumObjects,EnumFiles);
+	SKF_METHOD_BIND_FUNCTION_EX(GetObjectInfo,GetFileInfo);
+	SKF_METHOD_BIND_FUNCTION_EX(ReadObject,ReadFile);
+	SKF_METHOD_BIND_FUNCTION_EX(WriteObject,WriteFile);
+	SKF_METHOD_BIND_FUNCTION(CreateContainer);
+	SKF_METHOD_BIND_FUNCTION(DeleteContainer);
+	SKF_METHOD_BIND_FUNCTION(EnumContainer);
+	SKF_METHOD_BIND_FUNCTION(OpenContainer);
+	SKF_METHOD_BIND_FUNCTION(CloseContainer);
+	SKF_METHOD_BIND_FUNCTION(GetContainerType);
+	SKF_METHOD_BIND_FUNCTION(ImportCertificate);
+	SKF_METHOD_BIND_FUNCTION(ExportCertificate);
+	SKF_METHOD_BIND_FUNCTION(ExportPublicKey);
+	SKF_METHOD_BIND_FUNCTION(GenRandom);
+	SKF_METHOD_BIND_FUNCTION(GenExtRSAKey);
+	SKF_METHOD_BIND_FUNCTION(GenRSAKeyPair);
+	SKF_METHOD_BIND_FUNCTION(ImportRSAKeyPair);
+	SKF_METHOD_BIND_FUNCTION(RSASignData);
+	SKF_METHOD_BIND_FUNCTION(RSAVerify);
+	SKF_METHOD_BIND_FUNCTION(RSAExportSessionKey);
+	SKF_METHOD_BIND_FUNCTION(ExtRSAPubKeyOperation);
+	SKF_METHOD_BIND_FUNCTION(ExtRSAPriKeyOperation);
+	SKF_METHOD_BIND_FUNCTION(GenECCKeyPair);
+	SKF_METHOD_BIND_FUNCTION(ImportECCKeyPair);
+	SKF_METHOD_BIND_FUNCTION(ECCSignData);
+	SKF_METHOD_BIND_FUNCTION(ECCVerify);
+	SKF_METHOD_BIND_FUNCTION(ECCExportSessionKey);
+	SKF_METHOD_BIND_FUNCTION(ExtECCEncrypt);
+	SKF_METHOD_BIND_FUNCTION(ExtECCDecrypt);
+	SKF_METHOD_BIND_FUNCTION(ExtECCSign);
+	SKF_METHOD_BIND_FUNCTION(ExtECCVerify);
+	SKF_METHOD_BIND_FUNCTION(GenerateAgreementDataWithECC);
+	SKF_METHOD_BIND_FUNCTION(GenerateAgreementDataAndKeyWithECC);
+	SKF_METHOD_BIND_FUNCTION(GenerateKeyWithECC);
+	SKF_METHOD_BIND_FUNCTION(ImportSessionKey);
+	SKF_METHOD_BIND_FUNCTION(SetSymmKey);
+	SKF_METHOD_BIND_FUNCTION(EncryptInit);
+	SKF_METHOD_BIND_FUNCTION(Encrypt);
+	SKF_METHOD_BIND_FUNCTION(EncryptUpdate);
+	SKF_METHOD_BIND_FUNCTION(EncryptFinal);
+	SKF_METHOD_BIND_FUNCTION(DecryptInit);
+	SKF_METHOD_BIND_FUNCTION(Decrypt);
+	SKF_METHOD_BIND_FUNCTION(DecryptUpdate);
+	SKF_METHOD_BIND_FUNCTION(DecryptFinal);
+	SKF_METHOD_BIND_FUNCTION(DigestInit);
+	SKF_METHOD_BIND_FUNCTION(Digest);
+	SKF_METHOD_BIND_FUNCTION(DigestUpdate);
+	SKF_METHOD_BIND_FUNCTION(DigestFinal);
+	SKF_METHOD_BIND_FUNCTION(MacInit);
+	SKF_METHOD_BIND_FUNCTION(Mac);
+	SKF_METHOD_BIND_FUNCTION(MacUpdate);
+	SKF_METHOD_BIND_FUNCTION(MacFinal);
+	SKF_METHOD_BIND_FUNCTION(CloseHandle);
+#ifdef SKF_HAS_ECCDECRYPT
+	SKF_METHOD_BIND_FUNCTION(ECCDecrypt);
+#endif
+
+	ret = skf;
+	skf = NULL;
+
+end:
+	SKF_METHOD_free(skf);
+	return ret;
+}
+
+void SKF_METHOD_free(SKF_METHOD *meth)
+{
+	if (meth)
+		DSO_free(meth->dso);
+	OPENSSL_free(meth);
+}
diff --git a/crypto/skf/skf_prn.c b/crypto/skf/skf_prn.c
new file mode 100644
index 000000000..f61bafa53
--- /dev/null
+++ b/crypto/skf/skf_prn.c
@@ -0,0 +1,373 @@
+/* ====================================================================
+ * Copyright (c) 2014 - 2019 The GmSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the GmSSL Project.
+ *    (http://gmssl.org/)"
+ *
+ * 4. The name "GmSSL Project" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    guanzhi1980@gmail.com.
+ *
+ * 5. Products derived from this software may not be called "GmSSL"
+ *    nor may "GmSSL" appear in their names without prior written
+ *    permission of the GmSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the GmSSL Project
+ *    (http://gmssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE GmSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE GmSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include "internal/skf_int.h"
+#include "../../e_os.h"
+
+
+static char *skf_algor_name(ULONG ulAlgID)
+{
+	switch (ulAlgID) {
+	case SGD_SM1_ECB: return "sm1-ecb";
+	case SGD_SM1_CBC: return "sm1-cbc";
+	case SGD_SM1_CFB: return "sm1-cfb";
+	case SGD_SM1_OFB: return "sm1-ofb128";
+	case SGD_SM1_MAC: return "sm1-mac";
+	case SGD_SM4_ECB: return "sms4-ecb";
+	case SGD_SM4_CBC: return "sms4-cbc";
+	case SGD_SM4_CFB: return "sms4-cfb";
+	case SGD_SM4_OFB: return "sms4-ofb128";
+	case SGD_SM4_MAC: return "sms4-mac";
+	case SGD_SSF33_ECB: return "ssf33-ecb";
+	case SGD_SSF33_CBC: return "ssf33-cbc";
+	case SGD_SSF33_CFB: return "ssf33-cfb";
+	case SGD_SSF33_OFB: return "ssf33-ofb128";
+	case SGD_SSF33_MAC: return "ssf33-mac";
+	case SGD_RSA: return "rsa";
+	case SGD_SM2_1: return "sm2sign";
+	case SGD_SM2_2: return "sm2encrypt";
+	case SGD_SM2_3: return "sm2keyagreement";
+	case SGD_SM3: return "sm3";
+	case SGD_SHA1: return "sha1";
+	case SGD_SHA256: return "sha256";
+	}
+	return NULL;
+}
+
+ULONG SKF_GetDevStateName(ULONG ulDevState, LPSTR *szDevStateName)
+{
+	if (!szDevStateName) {
+		return SAR_INDATALENERR;
+	}
+
+	switch (ulDevState) {
+	case SKF_DEV_STATE_ABSENT:
+		*szDevStateName = (LPSTR)"Absent";
+		break;
+	case SKF_DEV_STATE_PRESENT:
+		*szDevStateName = (LPSTR)"Present";
+		break;
+	case SKF_DEV_STATE_UNKNOW:
+		*szDevStateName = (LPSTR)"Unknown";
+		break;
+	default:
+		*szDevStateName = (LPSTR)"(Error)";
+		return SAR_INDATALENERR;
+	}
+
+	return SAR_OK;
+}
+
+ULONG SKF_GetContainerTypeName(ULONG ulContainerType, LPSTR *szName)
+{
+	switch (ulContainerType) {
+	case SKF_CONTAINER_TYPE_UNDEF:
+		*szName = (LPSTR)"(undef)";
+		break;
+	case SKF_CONTAINER_TYPE_RSA:
+		*szName = (LPSTR)"RSA";
+		break;
+	case SKF_CONTAINER_TYPE_ECC:
+		*szName = (LPSTR)"EC";
+		break;
+	default:
+		*szName = (LPSTR)"(unknown)";
+	}
+	/* always success for help functions */
+	return SAR_OK;
+}
+
+typedef struct {
+	ULONG id;
+	char *name;
+} table_item_t;
+
+static table_item_t skf_cipher_caps[] = {
+	{ SGD_SM1_ECB, "sm1-ecb" },
+	{ SGD_SM1_CBC, "sm1-cbc" },
+	{ SGD_SM1_CFB, "sm1-cfb" },
+	{ SGD_SM1_OFB, "sm1-ofb128" },
+	{ SGD_SM1_MAC, "cbcmac-sm1" },
+	{ SGD_SSF33_ECB, "ssf33-ecb" },
+	{ SGD_SSF33_CBC, "ssf33-cbc" },
+	{ SGD_SSF33_CFB, "ssf33-cfb" },
+	{ SGD_SSF33_OFB, "ssf33-ofb128" },
+	{ SGD_SSF33_MAC, "cbcmac-ssf33" },
+	{ SGD_SM4_ECB, "sms4-ecb" },
+	{ SGD_SM4_CBC, "sms4-cbc" },
+	{ SGD_SM4_CFB, "sms4-cfb" },
+	{ SGD_SM4_OFB, "sms4-ofb128" },
+	{ SGD_SM4_MAC, "cbcmac-sms4" },
+	{ SGD_ZUC_EEA3, "zuc_128eea3" },
+	{ SGD_ZUC_EIA3, "zuc_128eia3" }
+};
+
+static table_item_t skf_digest_caps[] = {
+	{ SGD_SM3,  "sm3" },
+	{ SGD_SHA1, "sha1" },
+	{ SGD_SHA256, "sha256" },
+};
+
+static table_item_t skf_pkey_caps[] = {
+	{ SGD_RSA_SIGN, "rsa" },
+	{ SGD_RSA_ENC, "rsaEncryption" },
+	{ SGD_SM2_1, "sm2sign" },
+	{ SGD_SM2_2, "sm2exchange" },
+	{ SGD_SM2_3, "sm2encrypt" }
+};
+
+ULONG SKF_PrintDevInfo(BIO *out, DEVINFO *devInfo)
+{
+	size_t i, n;
+	char *serial = OPENSSL_buf2hexstr(devInfo->SerialNumber, strlen((char *)devInfo->SerialNumber));
+
+	BIO_printf(out, "  %-16s : %d.%d\n", "Version", devInfo->Version.major, devInfo->Version.minor);
+	BIO_printf(out, "  %-16s : %s\n", "Manufacturer", devInfo->Manufacturer);
+	BIO_printf(out, "  %-16s : %s\n", "Issuer", devInfo->Issuer);
+	BIO_printf(out, "  %-16s : %s\n", "Label", devInfo->Label);
+	BIO_printf(out, "  %-16s : %s\n", "Serial Number", serial);
+	BIO_printf(out, "  %-16s : %d.%d\n", "Firmware Version", devInfo->HWVersion.major, devInfo->HWVersion.minor);
+
+	BIO_printf(out, "  %-16s : ", "Ciphers");
+	for (i = n = 0; i < OSSL_NELEM(skf_cipher_caps); i++) {
+		if ((devInfo->AlgSymCap & skf_cipher_caps[i].id) ==
+			skf_cipher_caps[i].id) {
+			BIO_printf(out, "%s%s", n ? "," : "", skf_cipher_caps[i].name);
+			n++;
+		}
+	}
+	BIO_puts(out, "\n");
+
+	BIO_printf(out, "  %-16s : ", "Public Keys");
+	for (i = n = 0; i < OSSL_NELEM(skf_pkey_caps); i++) {
+		if ((devInfo->AlgAsymCap & skf_pkey_caps[i].id) ==
+			skf_pkey_caps[i].id) {
+			BIO_printf(out, "%s%s", n ? "," : "", skf_pkey_caps[i].name);
+			n++;
+		}
+	}
+	BIO_puts(out, "\n");
+
+	BIO_printf(out, "  %-16s : ", "Digests");
+	for (i = n = 0; i < OSSL_NELEM(skf_digest_caps); i++) {
+		if ((devInfo->AlgHashCap & skf_digest_caps[i].id) ==
+			skf_digest_caps[i].id) {
+			BIO_printf(out, "%s%s", n ? "," : "", skf_digest_caps[i].name);
+			n++;
+		}
+	}
+	BIO_puts(out, "\n");
+
+	BIO_printf(out, "  %-16s : ", "Auth Cipher");
+	for (i = 0; i < OSSL_NELEM(skf_cipher_caps); i++) {
+		if (devInfo->DevAuthAlgId == skf_cipher_caps[i].id) {
+			BIO_printf(out, "%s\n", skf_cipher_caps[i].name);
+			break;
+		}
+	}
+	if (i == OSSL_NELEM(skf_cipher_caps)) {
+		BIO_puts(out, "(unknown)\n");
+	}
+
+	if (devInfo->TotalSpace == UINT_MAX)
+		BIO_printf(out, "  %-16s : %s\n", "Total Sapce", "(unlimited)");
+	else	BIO_printf(out, "  %-16s : %u\n", "Total Sapce", devInfo->TotalSpace);
+
+	if (devInfo->FreeSpace == UINT_MAX)
+		BIO_printf(out, "  %-16s : %s\n", "Free Space", "(unlimited)");
+	else	BIO_printf(out, "  %-16s : %u\n", "Free Space", devInfo->FreeSpace);
+
+	if (devInfo->MaxECCBufferSize == UINT_MAX)
+		BIO_printf(out, "  %-16s : %s\n", "MAX ECC Input", "(unlimited)");
+	else	BIO_printf(out, "  %-16s : %u\n", "MAX ECC Input", devInfo->MaxECCBufferSize);
+
+	if (devInfo->MaxBufferSize == UINT_MAX)
+		BIO_printf(out, "  %-16s : %s\n", "MAX Cipher Input", "(unlimited)");
+	else	BIO_printf(out, "  %-16s : %u\n", "MAX Cipher Input", devInfo->MaxBufferSize);
+
+	OPENSSL_free(serial);
+	return SAR_OK;
+}
+
+ULONG SKF_PrintRSAPublicKey(BIO *out, RSAPUBLICKEYBLOB *blob)
+{
+	BIO_printf(out, "AlgID : %s\n", skf_algor_name(blob->AlgID));
+	BIO_printf(out, "BitLen : %u\n", blob->BitLen);
+	BIO_puts(out, "Modulus:\n");
+	BIO_puts(out, "    ");
+	BIO_hex_string(out, 4, 16, blob->Modulus, MAX_RSA_MODULUS_LEN);
+	BIO_puts(out, "\n");
+	BIO_puts(out, "PublicExponent:\n");
+	BIO_puts(out, "    ");
+	BIO_hex_string(out, 4, 16, blob->PublicExponent, MAX_RSA_EXPONENT_LEN);
+	BIO_puts(out, "\n");
+	return SAR_OK;
+}
+
+ULONG SKF_PrintRSAPrivateKey(BIO *out, RSAPRIVATEKEYBLOB *blob)
+{
+	BIO_printf(out, "AlgID : %s\n", skf_algor_name(blob->AlgID));
+	BIO_printf(out, "BitLen : %u\n", blob->BitLen);
+	BIO_puts(out, "Modulus:\n");
+	BIO_puts(out, "    ");
+	BIO_hex_string(out, 4, 16, blob->Modulus, MAX_RSA_MODULUS_LEN);
+	BIO_puts(out, "\n");
+	BIO_puts(out, "PublicExponent:\n");
+	BIO_puts(out, "    ");
+	BIO_hex_string(out, 4, 16, blob->PublicExponent, MAX_RSA_EXPONENT_LEN);
+	BIO_puts(out, "\n");
+	BIO_puts(out, "PrivateExponent:\n");
+	BIO_puts(out, "    ");
+	BIO_hex_string(out, 4, 16, blob->PrivateExponent, MAX_RSA_MODULUS_LEN);
+	BIO_puts(out, "\n");
+	BIO_puts(out, "Prime1:\n");
+	BIO_puts(out, "    ");
+	BIO_hex_string(out, 4, 16, blob->Prime1, MAX_RSA_MODULUS_LEN/2);
+	BIO_puts(out, "\n");
+	BIO_puts(out, "Prime2:\n");
+	BIO_puts(out, "    ");
+	BIO_hex_string(out, 4, 16, blob->Prime2, MAX_RSA_MODULUS_LEN/2);
+	BIO_puts(out, "\n");
+	BIO_puts(out, "Prime1Exponent:\n");
+	BIO_hex_string(out, 4, 16, blob->Prime1Exponent, MAX_RSA_MODULUS_LEN/2);
+	BIO_puts(out, "\n");
+	BIO_puts(out, "    ");
+	BIO_puts(out, "Prime2Exponent:\n");
+	BIO_puts(out, "    ");
+	BIO_hex_string(out, 4, 16, blob->Prime2Exponent, MAX_RSA_MODULUS_LEN/2);
+	BIO_puts(out, "\n");
+	BIO_puts(out, "Coefficient:\n");
+	BIO_puts(out, "    ");
+	BIO_hex_string(out, 4, 16, blob->Coefficient, MAX_RSA_MODULUS_LEN/2);
+	BIO_puts(out, "\n");
+	return SAR_OK;
+}
+
+ULONG SKF_PrintECCPublicKey(BIO *out, ECCPUBLICKEYBLOB *blob)
+{
+	BIO_printf(out, "BitLen : %u\n", blob->BitLen);
+	BIO_puts(out, "XCoordinate:\n");
+	BIO_puts(out, "    ");
+	BIO_hex_string(out, 4, 16, blob->XCoordinate, ECC_MAX_XCOORDINATE_BITS_LEN/8);
+	BIO_puts(out, "\n");
+	BIO_puts(out, "YCoordinate:\n");
+	BIO_puts(out, "    ");
+	BIO_hex_string(out, 4, 16, blob->YCoordinate, ECC_MAX_XCOORDINATE_BITS_LEN/8);
+	BIO_puts(out, "\n");
+	return SAR_OK;
+}
+
+ULONG SKF_PrintECCPrivateKey(BIO *out, ECCPRIVATEKEYBLOB *blob)
+{
+	BIO_printf(out, "BitLen : %u\n", blob->BitLen);
+	BIO_puts(out, "PrivateKey:\n");
+	BIO_puts(out, "    ");
+	BIO_hex_string(out, 4, 16, blob->PrivateKey, ECC_MAX_MODULUS_BITS_LEN/8);
+	BIO_puts(out, "\n");
+	return SAR_OK;
+}
+
+ULONG SKF_PrintECCCipher(BIO *out, ECCCIPHERBLOB *blob)
+{
+	BIO_puts(out, "XCoordinate:\n");
+	BIO_puts(out, "    ");
+	BIO_hex_string(out, 4, 16, blob->XCoordinate, ECC_MAX_XCOORDINATE_BITS_LEN/8);
+	BIO_puts(out, "\n");
+	BIO_puts(out, "YCoordinate:\n");
+	BIO_puts(out, "    ");
+	BIO_hex_string(out, 4, 16, blob->YCoordinate, ECC_MAX_XCOORDINATE_BITS_LEN/8);
+	BIO_puts(out, "\n");
+	BIO_puts(out, "HASH:\n");
+	BIO_puts(out, "    ");
+	BIO_hex_string(out, 4, 16, blob->HASH, 32);
+	BIO_puts(out, "\n");
+	BIO_printf(out, "CipherLen: %u\n", blob->CipherLen);
+	BIO_puts(out, "Cipher:\n");
+	BIO_puts(out, "    ");
+	BIO_hex_string(out, 4, 16, blob->Cipher, blob->CipherLen);
+	BIO_puts(out, "\n");
+	return SAR_OK;
+}
+
+ULONG SKF_PrintECCSignature(BIO *out, ECCSIGNATUREBLOB *blob)
+{
+	BIO_puts(out, "r:\n");
+	BIO_puts(out, "    ");
+	BIO_hex_string(out, 4, 16, blob->r, ECC_MAX_XCOORDINATE_BITS_LEN/8);
+	BIO_puts(out, "\n");
+	BIO_puts(out, "s:\n");
+	BIO_puts(out, "    ");
+	BIO_hex_string(out, 4, 16, blob->s, ECC_MAX_XCOORDINATE_BITS_LEN/8);
+	BIO_puts(out, "\n");
+	return SAR_OK;
+}
+
+ULONG DEVAPI SKF_GetAlgorName(ULONG ulAlgID, LPSTR *szName)
+{
+	char *name;
+	if ((name = skf_algor_name(ulAlgID)) != NULL) {
+		*szName = (LPSTR)&name;
+		return SAR_OK;
+	}
+	return SAR_FAIL;
+}
+
+ULONG DEVAPI SKF_PrintErrorString(BIO *out, ULONG ulError)
+{
+	LPSTR str = NULL;
+	SKF_GetErrorString(ulError, &str);
+	BIO_printf(out, "SKF Error: %s\n", (char *)str);
+	return SAR_OK;
+}
diff --git a/crypto/skf/skf_wisec.c b/crypto/skf/skf_wisec.c
new file mode 100644
index 000000000..8796c3d6d
--- /dev/null
+++ b/crypto/skf/skf_wisec.c
@@ -0,0 +1,200 @@
+/* ====================================================================
+ * Copyright (c) 2016 - 2017 The GmSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the GmSSL Project.
+ *    (http://gmssl.org/)"
+ *
+ * 4. The name "GmSSL Project" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    guanzhi1980@gmail.com.
+ *
+ * 5. Products derived from this software may not be called "GmSSL"
+ *    nor may "GmSSL" appear in their names without prior written
+ *    permission of the GmSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the GmSSL Project
+ *    (http://gmssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE GmSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE GmSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+
+#include 
+#include 
+#include "internal/skf_int.h"
+#include "../../e_os.h"
+#include "skf_wisec.h"
+
+typedef struct {
+	ULONG std_id;
+	ULONG vendor_id;
+} SKF_ALGOR_PAIR;
+
+static SKF_ALGOR_PAIR wisec_ciphers[] = {
+	{ SGD_SM1, WISEC_SM1 },
+	{ SGD_SM1_ECB, WISEC_SM1_ECB },
+	{ SGD_SM1_CBC, WISEC_SM1_CBC },
+	{ SGD_SM1_CFB, WISEC_SM1_CFB },
+	{ SGD_SM1_OFB, WISEC_SM1_OFB },
+	{ SGD_SM1_MAC, WISEC_SM1_MAC },
+	{ SGD_SM4, WISEC_SM4 },
+	{ SGD_SM4_ECB, WISEC_SM4_ECB },
+	{ SGD_SM4_CBC, WISEC_SM4_CBC },
+	{ SGD_SM4_CFB, WISEC_SM4_CFB },
+	{ SGD_SM4_OFB, WISEC_SM4_OFB },
+	{ SGD_SM4_MAC, WISEC_SM4_MAC },
+	{ SGD_SSF33, WISEC_SSF33 },
+	{ SGD_SSF33_ECB, WISEC_SSF33_ECB },
+	{ SGD_SSF33_CBC, WISEC_SSF33_CBC },
+	{ SGD_SSF33_CFB, WISEC_SSF33_CFB },
+	{ SGD_SSF33_OFB, WISEC_SSF33_OFB },
+	{ SGD_SSF33_MAC, WISEC_SSF33_MAC },
+};
+
+static ULONG wisec_get_cipher_algor(ULONG vendor_id)
+{
+	size_t i;
+	for (i = 0; i < OSSL_NELEM(wisec_ciphers); i++) {
+		if (vendor_id == wisec_ciphers[i].vendor_id) {
+			return wisec_ciphers[i].std_id;
+		}
+	}
+	return 0;
+}
+
+static ULONG wisec_get_cipher_cap(ULONG vendor_cap)
+{
+	ULONG std_cap = 0;
+	size_t i;
+	for (i = 0; i < OSSL_NELEM(wisec_ciphers); i++) {
+		if (vendor_cap & wisec_ciphers[i].vendor_id) {
+			std_cap |= wisec_ciphers[i].std_id;
+		}
+	}
+	return std_cap;
+}
+
+static SKF_ALGOR_PAIR wisec_digests[] = {
+	{ SGD_SM3, WISEC_SM3 },
+	{ SGD_SHA1, WISEC_SHA1 },
+	{ SGD_SHA256, WISEC_SHA256 },
+};
+
+static ULONG wisec_get_digest_algor(ULONG vendor_id)
+{
+	size_t i;
+	for (i = 0; i < OSSL_NELEM(wisec_digests); i++) {
+		if (vendor_id == wisec_digests[i].vendor_id) {
+			return wisec_digests[i].std_id;
+		}
+	}
+	return 0;
+}
+
+static ULONG wisec_get_digest_cap(ULONG vendor_cap)
+{
+	ULONG std_cap = 0;
+	size_t i;
+	for (i = 0; i < OSSL_NELEM(wisec_digests); i++) {
+		if (vendor_cap & wisec_digests[i].vendor_id) {
+			std_cap |= wisec_digests[i].std_id;
+		}
+	}
+	return std_cap;
+}
+
+static SKF_ALGOR_PAIR wisec_pkeys[] = {
+	{ SGD_RSA, WISEC_RSA },
+	{ SGD_RSA_SIGN, WISEC_RSA_SIGN },
+	{ SGD_RSA_ENC, WISEC_RSA_ENC },
+	{ SGD_SM2, WISEC_SM2 },
+	{ SGD_SM2_1, WISEC_SM2_1 },
+	{ SGD_SM2_2, WISEC_SM2_2 },
+	{ SGD_SM2_3, WISEC_SM2_3 },
+};
+
+static ULONG wisec_get_pkey_algor(ULONG vendor_id)
+{
+	size_t i;
+	for (i = 0; i < OSSL_NELEM(wisec_pkeys); i++) {
+		if (vendor_id == wisec_pkeys[i].vendor_id) {
+			return wisec_pkeys[i].std_id;
+		}
+	}
+	return 0;
+}
+
+static ULONG wisec_get_pkey_cap(ULONG vendor_cap)
+{
+	ULONG std_cap = 0;
+	size_t i;
+	for (i = 0; i < OSSL_NELEM(wisec_pkeys); i++) {
+		if (vendor_cap & wisec_pkeys[i].vendor_id) {
+			std_cap |= wisec_pkeys[i].std_id;
+		}
+	}
+	return std_cap;
+}
+
+static SKF_ERR_REASON wisec_errors[] = {
+	{ WISEC_AUTH_BLOCKED, SKF_R_WISEC_AUTH_BLOCKED },
+	{ WISEC_CERTNOUSAGEERR, SKF_R_WISEC_CERTNOUSAGEERR },
+	{ WISEC_INVALIDCONTAINERERR, SKF_R_WISEC_INVALIDCONTAINERERR },
+	{ WISEC_CONTAINER_NOT_EXISTS, SKF_R_WISEC_CONTAINER_NOT_EXISTS },
+	{ WISEC_CONTAINER_EXISTS, SKF_R_WISEC_CONTAINER_EXISTS },
+	{ WISEC_CERTUSAGEERR, SKF_R_WISEC_CERTUSAGEERR },
+	{ WISEC_KEYNOUSAGEERR, SKF_R_WISEC_KEYNOUSAGEERR },
+	{ WISEC_FILEATTRIBUTEERR, SKF_R_WISEC_FILEATTRIBUTEERR },
+	{ WISEC_DEVNOAUTH, SKF_R_WISEC_DEVNOAUTH },
+};
+
+static unsigned long wisec_get_error_reason(ULONG err)
+{
+	size_t i = 0;
+	for (i = 0; i < OSSL_NELEM(wisec_errors); i++) {
+		if (err == wisec_errors[i].err) {
+			return wisec_errors[i].reason;
+		}
+	}
+	return 0;
+}
+
+SKF_VENDOR skf_wisec = {
+	"wisec",
+	16,
+	wisec_get_cipher_algor,
+	wisec_get_cipher_cap,
+	wisec_get_digest_algor,
+	wisec_get_digest_cap,
+	wisec_get_pkey_algor,
+	wisec_get_pkey_cap,
+	wisec_get_error_reason,
+};
diff --git a/crypto/skf/skf_wisec.h b/crypto/skf/skf_wisec.h
new file mode 100644
index 000000000..ddbcae616
--- /dev/null
+++ b/crypto/skf/skf_wisec.h
@@ -0,0 +1,157 @@
+/* ====================================================================
+ * Copyright (c) 2016 - 2017 The GmSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the GmSSL Project.
+ *    (http://gmssl.org/)"
+ *
+ * 4. The name "GmSSL Project" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    guanzhi1980@gmail.com.
+ *
+ * 5. Products derived from this software may not be called "GmSSL"
+ *    nor may "GmSSL" appear in their names without prior written
+ *    permission of the GmSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the GmSSL Project
+ *    (http://gmssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE GmSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE GmSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+
+#ifndef HEADER_SKF_WISEC_H
+#define HEADER_SKF_WISEC_H
+
+#include 
+#include 
+
+
+#define WISEC_SM1		(SGD_SM1)
+#define WISEC_SM1_ECB		(SGD_SM1_ECB)
+#define WISEC_SM1_CBC		(SGD_SM1_CBC)
+#define WISEC_SM1_CFB		(SGD_SM1_CFB)
+#define WISEC_SM1_OFB		(SGD_SM1_OFB)
+#define WISEC_SM1_MAC		(SGD_SM1_MAC)
+
+#define WISEC_SSF33		(SGD_SSF33)
+#define WISEC_SSF33_ECB		(SGD_SSF33_ECB)
+#define WISEC_SSF33_CBC		(SGD_SSF33_CBC)
+#define WISEC_SSF33_CFB		(SGD_SSF33_CFB)
+#define WISEC_SSF33_OFB		(SGD_SSF33_OFB)
+#define WISEC_SSF33_MAC		(SGD_SSF33_MAC)
+
+#define WISEC_SM4		(SGD_SM4)
+#define WISEC_SM4_ECB		(WISEC_SM4|SGD_ECB)
+#define WISEC_SM4_CBC		(WISEC_SM4|SGD_CBC)
+#define WISEC_SM4_CFB		(WISEC_SM4|SGD_CFB)
+#define WISEC_SM4_OFB		(WISEC_SM4|SGD_OFB)
+#define WISEC_SM4_MAC		(WISEC_SM4|SGD_MAC)
+
+#define WISEC_AES		0x00000800
+#define WISEC_128		0x00000000
+#define WISEC_192		0x00000010
+#define WISEC_256		0x00000020
+#define WISEC_AES128		(WISEC_AES|WISEC_128)
+#define WISEC_AES192		(WISEC_AES|WISEC_192)
+#define WISEC_AES256		(WISEC_AES|WISEC_256)
+#define WISEC_AES128_ECB	(WISEC_AES128|SGD_ECB)
+#define WISEC_AES128_CBC	(WISEC_AES128|SGD_CBC)
+#define WISEC_AES128_CFB	(WISEC_AES128|SGD_CFB)
+#define WISEC_AES128_OFB	(WISEC_AES128|SGD_OFB)
+#define WISEC_AES128_MAC	(WISEC_AES128|SGD_MAC)
+#define WISEC_AES192_ECB	(WISEC_AES192|SGD_ECB)
+#define WISEC_AES192_CBC	(WISEC_AES192|SGD_CBC)
+#define WISEC_AES192_CFB	(WISEC_AES192|SGD_CFB)
+#define WISEC_AES192_OFB	(WISEC_AES192|SGD_OFB)
+#define WISEC_AES192_MAC	(WISEC_AES192|SGD_MAC)
+#define WISEC_AES256_ECB	(WISEC_AES256|SGD_ECB)
+#define WISEC_AES256_CBC	(WISEC_AES256|SGD_CBC)
+#define WISEC_AES256_CFB	(WISEC_AES256|SGD_CFB)
+#define WISEC_AES256_OFB	(WISEC_AES256|SGD_OFB)
+#define WISEC_AES256_MAC	(WISEC_AES256|SGD_MAC)
+
+#define WISEC_DES		0x00001000
+#define WISEC_DES_ECB		(WISEC_DES|SGD_ECB)
+#define WISEC_DES_CBC		(WISEC_DES|SGD_CBC)
+#define WISEC_DES_CFB		(WISEC_DES|SGD_CFB)
+#define WISEC_DES_OFB		(WISEC_DES|SGD_OFB)
+#define WISEC_DES_MAC		(WISEC_DES|SGD_MAC)
+
+#define WISEC_D3DES		0x00001010
+#define WISEC_D3DES_ECB		(WISEC_D3DES|SGD_ECB)
+#define WISEC_D3DES_CBC		(WISEC_D3DES|SGD_CBC)
+#define WISEC_D3DES_CFB		(WISEC_D3DES|SGD_CFB)
+#define WISEC_D3DES_OFB		(WISEC_D3DES|SGD_OFB)
+#define WISEC_D3DES_MAC		(WISEC_D3DES|SGD_MAC)
+
+#define WISEC_T3DES		0x00001020
+#define WISEC_T3DES_ECB		(WISEC_T3DES|SGD_ECB)
+#define WISEC_T3DES_CBC		(WISEC_T3DES|SGD_CBC)
+#define WISEC_T3DES_CFB		(WISEC_T3DES|SGD_CFB)
+#define WISEC_T3DES_OFB		(WISEC_T3DES|SGD_OFB)
+#define WISEC_T3DES_MAC		(WISEC_T3DES|SGD_MAC)
+
+#define WISEC_SM3		(SGD_SM3)
+#define WISEC_SHA1		(SGD_SHA1)
+#define WISEC_SHA256		(SGD_SHA256)
+
+#define WISEC_RSA		(SGD_RSA)
+#define WISEC_RSA_SIGN		(SGD_RSA_SIGN)
+#define WISEC_RSA_ENC		(SGD_RSA_ENC)
+#define WISEC_SM2		(SGD_SM2)
+#define WISEC_SM2_1		(SGD_SM2_1)
+#define WISEC_SM2_2		(SGD_SM2_2)
+#define WISEC_SM2_3		(SGD_SM2_3)
+
+
+#define WISEC_AUTH_BLOCKED		0x0A000033
+#define WISEC_CERTNOUSAGEERR		0x0A000034
+#define WISEC_INVALIDCONTAINERERR	0x0A000035
+#define WISEC_CONTAINER_NOT_EXISTS	0x0A000036
+#define WISEC_CONTAINER_EXISTS		0x0A000037
+#define WISEC_CERTUSAGEERR		0x0A000038
+#define WISEC_KEYNOUSAGEERR		0x0A000039
+#define WISEC_FILEATTRIBUTEERR		0x0A00003A
+#define WISEC_DEVNOAUTH			0x0A00003B
+
+/*
+ULONG DEVAPI SKFE_SetSN(DEVHANDLE hDev, CHAR *SN, UINT SNLen);
+ULONG DEVAPI SKFE_GenExtECCKey(DEVHANDLE hDev, PECCPRIVATEKEYBLOB pPriBlob, PECCPUBLICKEYBLOB pPubBlob);
+ULONG DEVAPI SKF_ECCDecrypt(HCONTAINER hContainer, PECCCIPHERBLOB pCipherText, BYTE *pbPlainText,ULONG *pulPlainTextLen);
+ULONG DEVAPI SKF_GenerateKey(HCONTAINER hContainer, ULONG ulAlgId, HANDLE *phSessionKey) ;
+ULONG DEVAPI SKF_ECCExportSessionKeyByHandle(HANDLE phSessionKey, ECCPUBLICKEYBLOB *pPubKey,PECCCIPHERBLOB pData);
+ULONG DEVAPI SKF_RSAExportSessionKeyByHandle(HANDLE phSessionKey, RSAPUBLICKEYBLOB*pPubKey,BYTE *pbData, ULONG *pulDataLen);
+ULONG DEVAPI SKF_PrvKeyDecrypt(HCONTAINER hContainer, PECCCIPHERBLOB pCipherText, BYTE *pbData, ULONG *pbDataLen);
+ULONG DEVAPI SKF_PrvKeyDecrypt(HCONTAINER hContainer, ULONG ulType, PECCCIPHERBLOB pCipherText, BYTE *pbData, ULONG *pbDataLen);
+ULONG DEVAPI SKF_RSAPrvKeyDecrypt(HCONTAINER hContainer, BYTE *pCipherData, ULONG pCipherDataLen, BYTE *pbData, ULONG *pbDataLen);
+*/
+
+#endif
diff --git a/crypto/sm2/build.info b/crypto/sm2/build.info
new file mode 100644
index 000000000..6b650d735
--- /dev/null
+++ b/crypto/sm2/build.info
@@ -0,0 +1,3 @@
+LIBS=../../libcrypto
+SOURCE[../../libcrypto]=sm2_err.c sm2_asn1.c sm2_id.c sm2_sign.c sm2_enc.c \
+			sm2_oct.c sm2_exch.c sm2_kmeth.c
diff --git a/crypto/sm2/sm2_asn1.c b/crypto/sm2/sm2_asn1.c
new file mode 100644
index 000000000..7bb7721a1
--- /dev/null
+++ b/crypto/sm2/sm2_asn1.c
@@ -0,0 +1,144 @@
+/* ====================================================================
+ * Copyright (c) 2007 - 2016 The GmSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the GmSSL Project.
+ *    (http://gmssl.org/)"
+ *
+ * 4. The name "GmSSL Project" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    guanzhi1980@gmail.com.
+ *
+ * 5. Products derived from this software may not be called "GmSSL"
+ *    nor may "GmSSL" appear in their names without prior written
+ *    permission of the GmSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the GmSSL Project
+ *    (http://gmssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE GmSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE GmSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include "../ec/ec_lcl.h"
+#include "sm2_lcl.h"
+
+
+ASN1_SEQUENCE(SM2CiphertextValue) = {
+	ASN1_SIMPLE(SM2CiphertextValue, xCoordinate, BIGNUM),
+	ASN1_SIMPLE(SM2CiphertextValue, yCoordinate, BIGNUM),
+	ASN1_SIMPLE(SM2CiphertextValue, hash, ASN1_OCTET_STRING),
+	ASN1_SIMPLE(SM2CiphertextValue, ciphertext, ASN1_OCTET_STRING),
+} ASN1_SEQUENCE_END(SM2CiphertextValue)
+IMPLEMENT_ASN1_FUNCTIONS(SM2CiphertextValue)
+IMPLEMENT_ASN1_DUP_FUNCTION(SM2CiphertextValue)
+
+int SM2_ciphertext_size(const EC_KEY *ec_key, size_t inlen)
+{
+	int ret;
+	const EC_GROUP *group = NULL;
+	ASN1_OCTET_STRING s;
+	int len = 0, i;
+
+	if (inlen > SM2_MAX_PLAINTEXT_LENGTH) {
+		SM2err(SM2_F_SM2_CIPHERTEXT_SIZE, SM2_R_PLAINTEXT_TOO_LONG);
+		return 0;
+	}
+
+	if (ec_key) {
+		group = EC_KEY_get0_group(ec_key);
+	}
+
+	if (group) {
+		ASN1_INTEGER a;
+		unsigned char buf[4] = {0xff};
+
+		/* ASN1_INTEGER xCoordinate, yCoordinate */
+		if (!(i = EC_GROUP_order_bits(group))) {
+			SM2err(SM2_F_SM2_CIPHERTEXT_SIZE, ERR_R_EC_LIB);
+			return 0;
+		}
+		a.length = (i + 7)/8;
+		a.data = buf;
+		a.type = V_ASN1_INTEGER;
+		i = i2d_ASN1_INTEGER(&a, NULL);
+		len = i + i;
+
+		/* ASN1_OCTET_STRING hash 32 */
+		s.length = SM3_DIGEST_LENGTH;
+		s.data = NULL;
+		s.type = V_ASN1_OCTET_STRING;
+		i = i2d_ASN1_OCTET_STRING(&s, NULL);
+		len += i;
+
+	} else {
+		len += 104;
+	}
+
+	/* ASN1_OCTET_STRING ciphertext inlen */
+	s.length = inlen;
+	s.data = NULL;
+	s.type = V_ASN1_OCTET_STRING;
+	i = i2d_ASN1_OCTET_STRING(&s, NULL);
+	len += i;
+
+	ret = ASN1_object_size(1, len, V_ASN1_SEQUENCE);
+	return ret;
+}
+
+int i2d_SM2CiphertextValue_bio(BIO *bp, SM2CiphertextValue *a)
+{
+	return ASN1_item_i2d_bio(ASN1_ITEM_rptr(SM2CiphertextValue), bp, a);
+}
+
+SM2CiphertextValue *d2i_SM2CiphertextValue_bio(BIO *bp, SM2CiphertextValue **a)
+{
+	return ASN1_item_d2i_bio(ASN1_ITEM_rptr(SM2CiphertextValue), bp, a);
+}
+
+#ifndef OPENSSL_NO_STDIO
+SM2CiphertextValue *d2i_SM2CiphertextValue_fp(FILE *fp, SM2CiphertextValue **a)
+{
+	return ASN1_item_d2i_fp(ASN1_ITEM_rptr(SM2CiphertextValue), fp, a);
+}
+
+int i2d_SM2CiphertextValue_fp(FILE *fp, SM2CiphertextValue *a)
+{
+	return ASN1_item_i2d_fp(ASN1_ITEM_rptr(SM2CiphertextValue), fp, a);
+}
+#endif
diff --git a/crypto/sm2/sm2_enc.c b/crypto/sm2/sm2_enc.c
new file mode 100644
index 000000000..36bacb528
--- /dev/null
+++ b/crypto/sm2/sm2_enc.c
@@ -0,0 +1,499 @@
+/*
+ * Copyright (c) 2015 - 2017 The GmSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the GmSSL Project.
+ *    (http://gmssl.org/)"
+ *
+ * 4. The name "GmSSL Project" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    guanzhi1980@gmail.com.
+ *
+ * 5. Products derived from this software may not be called "GmSSL"
+ *    nor may "GmSSL" appear in their names without prior written
+ *    permission of the GmSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the GmSSL Project
+ *    (http://gmssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE GmSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE GmSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include "internal/o_str.h"
+#include "sm2_lcl.h"
+
+SM2CiphertextValue *SM2_do_encrypt(const EVP_MD *md,
+	const unsigned char *in, size_t inlen, EC_KEY *ec_key)
+{
+	SM2CiphertextValue *ret = NULL;
+	SM2CiphertextValue *cv = NULL;
+	const EC_GROUP *group;
+	const EC_POINT *pub_key;
+	KDF_FUNC kdf;
+	EC_POINT *ephem_point = NULL;
+	EC_POINT *share_point = NULL;
+	BIGNUM *n = NULL;
+	BIGNUM *h = NULL;
+	BIGNUM *k = NULL;
+	BN_CTX *bn_ctx = NULL;
+	EVP_MD_CTX *md_ctx = NULL;
+
+	unsigned char buf[(OPENSSL_ECC_MAX_FIELD_BITS + 7)/4 + 1];
+	int nbytes;
+	size_t len;
+	size_t i;
+	unsigned int hashlen;
+
+	/* check arguments */
+	if (!md || !in || !ec_key) {
+		SM2err(SM2_F_SM2_DO_ENCRYPT, ERR_R_PASSED_NULL_PARAMETER);
+		return 0;
+	}
+
+	if (inlen <= 0 || inlen > SM2_MAX_PLAINTEXT_LENGTH) {
+		SM2err(SM2_F_SM2_DO_ENCRYPT, SM2_R_INVALID_PLAINTEXT_LENGTH);
+		return 0;
+	}
+
+	if (!(kdf = KDF_get_x9_63(md))) {
+		SM2err(SM2_F_SM2_DO_ENCRYPT, SM2_R_INVALID_DIGEST_ALGOR);
+		return 0;
+	}
+
+	if (!(group = EC_KEY_get0_group(ec_key))
+		|| !(pub_key = EC_KEY_get0_public_key(ec_key))) {
+		SM2err(SM2_F_SM2_DO_ENCRYPT, SM2_R_INVALID_EC_KEY);
+		return 0;
+	}
+
+	/* malloc */
+	if (!(cv = SM2CiphertextValue_new())
+		|| !(ephem_point = EC_POINT_new(group))
+		|| !(share_point = EC_POINT_new(group))
+		|| !(n = BN_new())
+		|| !(h = BN_new())
+		|| !(k = BN_new())
+		|| !(bn_ctx = BN_CTX_new())
+		|| !(md_ctx = EVP_MD_CTX_new())) {
+		SM2err(SM2_F_SM2_DO_ENCRYPT, ERR_R_MALLOC_FAILURE);
+		goto end;
+	}
+
+	if (!ASN1_OCTET_STRING_set(cv->ciphertext, NULL, (int)inlen)
+		|| !ASN1_OCTET_STRING_set(cv->hash, NULL, EVP_MD_size(md))) {
+		SM2err(SM2_F_SM2_DO_ENCRYPT, ERR_R_ASN1_LIB);
+		goto end;
+	}
+
+	/* init ec domain parameters */
+	if (!EC_GROUP_get_order(group, n, bn_ctx)) {
+		ECerr(EC_F_SM2_DO_ENCRYPT, EC_R_ERROR);
+		goto end;
+	}
+
+	if (!EC_GROUP_get_cofactor(group, h, bn_ctx)) {
+		ECerr(EC_F_SM2_DO_ENCRYPT, EC_R_ERROR);
+		goto end;
+	}
+
+	nbytes = (EC_GROUP_get_degree(group) + 7) / 8;
+
+	/* check [h]P_B != O */
+	if (!EC_POINT_mul(group, share_point, NULL, pub_key, h, bn_ctx)) {
+		SM2err(SM2_F_SM2_DO_ENCRYPT, ERR_R_EC_LIB);
+		goto end;
+	}
+
+	if (EC_POINT_is_at_infinity(group, share_point)) {
+		SM2err(SM2_F_SM2_DO_ENCRYPT, SM2_R_INVALID_PUBLIC_KEY);
+		goto end;
+	}
+
+	do
+	{
+		size_t size;
+
+		/* rand k in [1, n-1] */
+		do {
+			BN_rand_range(k, n);
+		} while (BN_is_zero(k));
+
+		/* compute ephem_point [k]G = (x1, y1) */
+		if (!EC_POINT_mul(group, ephem_point, k, NULL, NULL, bn_ctx)) {
+			SM2err(SM2_F_SM2_DO_ENCRYPT, ERR_R_EC_LIB);
+			goto end;
+		}
+
+		/* compute ECDH share_point [k]P_B = (x2, y2) */
+		if (!EC_POINT_mul(group, share_point, NULL, pub_key, k, bn_ctx)) {
+			SM2err(SM2_F_SM2_DO_ENCRYPT, ERR_R_EC_LIB);
+			goto end;
+		}
+
+		/* compute t = KDF(x2 || y2, klen) */
+		if (!(len = EC_POINT_point2oct(group, share_point,
+			POINT_CONVERSION_UNCOMPRESSED, buf, sizeof(buf), bn_ctx))) {
+			SM2err(SM2_F_SM2_DO_ENCRYPT, ERR_R_EC_LIB);
+			goto end;
+		}
+
+		size = cv->ciphertext->length;
+		kdf(buf + 1, len - 1, cv->ciphertext->data, &size);
+		if (size != inlen) {
+			SM2err(SM2_F_SM2_DO_ENCRYPT, SM2_R_KDF_FAILURE);
+			goto end;
+		}
+
+		/* ASN1_OCTET_STRING_is_zero in asn1.h and a_octet.c */
+	} while (ASN1_OCTET_STRING_is_zero(cv->ciphertext));
+
+	/* set x/yCoordinates as (x1, y1) */
+	if (EC_METHOD_get_field_type(EC_GROUP_method_of(group)) == NID_X9_62_prime_field) {
+		if (!EC_POINT_get_affine_coordinates_GFp(group, ephem_point,
+			cv->xCoordinate, cv->yCoordinate, bn_ctx)) {
+			SM2err(SM2_F_SM2_DO_ENCRYPT, ERR_R_EC_LIB);
+			goto end;
+		}
+	} else {
+		if (!EC_POINT_get_affine_coordinates_GF2m(group, ephem_point,
+			cv->xCoordinate, cv->yCoordinate, bn_ctx)) {
+			SM2err(SM2_F_SM2_DO_ENCRYPT, ERR_R_EC_LIB);
+			goto end;
+		}
+	}
+
+	/* ciphertext = t xor in */
+	for (i = 0; i < inlen; i++) {
+		cv->ciphertext->data[i] ^= in[i];
+	}
+
+	/* generate hash = Hash(x2 || M || y2) */
+	hashlen = cv->hash->length;
+	if (!EVP_DigestInit_ex(md_ctx, md, NULL)
+		|| !EVP_DigestUpdate(md_ctx, buf + 1, nbytes)
+		|| !EVP_DigestUpdate(md_ctx, in, inlen)
+		|| !EVP_DigestUpdate(md_ctx, buf + 1 + nbytes, nbytes)
+		|| !EVP_DigestFinal_ex(md_ctx, cv->hash->data, &hashlen)) {
+		SM2err(SM2_F_SM2_DO_ENCRYPT, ERR_R_EVP_LIB);
+		goto end;
+	}
+
+	ret = cv;
+	cv = NULL;
+
+end:
+	SM2CiphertextValue_free(cv);
+	EC_POINT_free(share_point);
+	EC_POINT_free(ephem_point);
+	BN_free(n);
+	BN_free(h);
+	BN_clear_free(k);
+	BN_CTX_free(bn_ctx);
+	EVP_MD_CTX_free(md_ctx);
+	return ret;
+}
+
+int SM2_encrypt(int type, const unsigned char *in, size_t inlen,
+	unsigned char *out, size_t *outlen, EC_KEY *ec_key)
+{
+	int ret = 0;
+	const EVP_MD *md;
+	SM2CiphertextValue *cv = NULL;
+	int clen;
+
+	// check type 					
+	if (!(md = EVP_get_digestbynid(type))) {
+		SM2err(SM2_F_SM2_ENCRYPT, SM2_R_INVALID_DIGEST_ALGOR);
+		return 0;
+	}
+
+	if (!(clen = SM2_ciphertext_size(ec_key, inlen))) {
+		SM2err(SM2_F_SM2_ENCRYPT, ERR_R_SM2_LIB);
+		return 0;
+	}
+	if (!out) {
+		*outlen = clen;
+		return 1;
+	} else if (*outlen < (size_t)clen) {
+		SM2err(SM2_F_SM2_ENCRYPT, SM2_R_BUFFER_TOO_SMALL);
+		return 0;
+	}
+
+	RAND_seed(in, inlen);
+	if (!(cv = SM2_do_encrypt(md, in, inlen, ec_key))) {
+		SM2err(SM2_F_SM2_ENCRYPT, ERR_R_SM2_LIB);
+		*outlen = 0;
+		return 0;
+	}
+
+	if ((clen = i2d_SM2CiphertextValue(cv, &out)) <= 0) {
+		SM2err(SM2_F_SM2_ENCRYPT, ERR_R_SM2_LIB);
+		goto end;
+	}
+
+	*outlen = clen;
+	ret = 1;
+
+end:
+	SM2CiphertextValue_free(cv);
+	return ret;
+}
+
+int SM2_decrypt(int type, const unsigned char *in, size_t inlen,
+	unsigned char *out, size_t *outlen, EC_KEY *ec_key)
+{
+	int ret = 0;
+	const EVP_MD *md;
+	const unsigned char *p;
+	SM2CiphertextValue *cv = NULL;
+
+	/* check arguments */
+	if (!(md = EVP_get_digestbynid(type))) {
+		SM2err(SM2_F_SM2_DECRYPT, SM2_R_INVALID_DIGEST_ALGOR);
+		*outlen = 0;
+		return 0;
+	}
+
+	if (!in) {
+		SM2err(SM2_F_SM2_DECRYPT, ERR_R_PASSED_NULL_PARAMETER);
+		*outlen = 0;
+		return 0;
+	}
+	if (inlen <= 0 || inlen > INT_MAX) {
+		SM2err(SM2_F_SM2_DECRYPT, SM2_R_INVALID_INPUT_LENGTH);
+		*outlen = 0;
+		return 0;
+	}
+
+	/* decode asn.1 and check no data remaining */
+	p = in;
+	if (!(cv = d2i_SM2CiphertextValue(NULL, &p, (long)inlen))) {
+		SM2err(SM2_F_SM2_DECRYPT, SM2_R_INVALID_CIPHERTEXT);
+		return 0;
+	}
+	if (p != in + inlen) {
+		SM2err(SM2_F_SM2_DECRYPT, SM2_R_INVALID_CIPHERTEXT);
+		goto end;
+	}
+
+	/* return or check output length */
+	if (!out) {
+		*outlen = ASN1_STRING_length(cv->ciphertext);
+		ret = 1;
+		goto end;
+	}
+	/*
+	else if (*outlen < ASN1_STRING_length(cv->ciphertext)) {
+		SM2err(SM2_F_SM2_DECRYPT, SM2_R_BUFFER_TOO_SMALL);
+		ret = 0;
+		goto end;
+	}
+	*/
+
+	/* do decrypt */
+	if (!SM2_do_decrypt(md, cv, out, outlen, ec_key)) {
+		SM2err(SM2_F_SM2_DECRYPT, SM2_R_DECRYPT_FAILURE);
+		goto end;
+	}
+
+	ret = 1;
+
+end:
+	SM2CiphertextValue_free(cv);
+	return ret;
+}
+
+int SM2_do_decrypt(const EVP_MD *md, const SM2CiphertextValue *cv,
+	unsigned char *out, size_t *outlen, EC_KEY *ec_key)
+{
+	int ret = 0;
+	const EC_GROUP *group;
+	const BIGNUM *pri_key;
+	KDF_FUNC kdf;
+	EC_POINT *point = NULL;
+	EC_POINT *tmp_point = NULL;
+	BIGNUM *n = NULL;
+	BIGNUM *h = NULL;
+	BN_CTX *bn_ctx = NULL;
+	EVP_MD_CTX *md_ctx = NULL;
+	unsigned char buf[(OPENSSL_ECC_MAX_FIELD_BITS + 7)/4 + 1];
+	unsigned char mac[EVP_MAX_MD_SIZE];
+	unsigned int maclen = sizeof(mac);
+	int nbytes, len, i;
+
+	/* check arguments */
+	if (!md || !cv || !outlen || !ec_key) {
+		SM2err(SM2_F_SM2_DO_DECRYPT, ERR_R_PASSED_NULL_PARAMETER);
+		return 0;
+	}
+
+	if (!(kdf = KDF_get_x9_63(md))) {
+		SM2err(SM2_F_SM2_DO_DECRYPT, SM2_R_INVALID_DIGEST_ALGOR);
+		return 0;
+	}
+
+	if (!cv->xCoordinate || !cv->yCoordinate || !cv->hash || !cv->ciphertext) {
+		SM2err(SM2_F_SM2_DO_DECRYPT, SM2_R_INVALID_CIPHERTEXT);
+		return 0;
+	}
+
+	if (cv->hash->length != EVP_MD_size(md)) {
+		SM2err(SM2_F_SM2_DO_DECRYPT, SM2_R_INVALID_CIPHERTEXT);
+		return 0;
+	}
+
+	if (cv->ciphertext->length <= 0
+		|| cv->ciphertext->length > SM2_MAX_PLAINTEXT_LENGTH) {
+		SM2err(SM2_F_SM2_DO_DECRYPT, SM2_R_INVALID_CIPHERTEXT);
+		return 0;
+	}
+
+	if (!(group = EC_KEY_get0_group(ec_key))
+		|| !(pri_key = EC_KEY_get0_private_key(ec_key))) {
+		SM2err(SM2_F_SM2_DO_DECRYPT, SM2_R_INVALID_EC_KEY);
+		return 0;
+	}
+
+	if (!out) {
+		*outlen = cv->ciphertext->length;
+		return 1;
+	}
+	/*
+	if (*outlen < cv->ciphertext->length) {
+		SM2err(SM2_F_SM2_DO_DECRYPT, SM2_R_BUFFER_TOO_SMALL);
+		return 0;
+	}
+	*/
+
+	/* malloc */
+	point = EC_POINT_new(group);
+	tmp_point = EC_POINT_new(group);
+	n = BN_new();
+	h = BN_new();
+	bn_ctx = BN_CTX_new();
+	md_ctx = EVP_MD_CTX_new();
+	if (!point || !n || !h || !bn_ctx || !md_ctx) {
+		SM2err(SM2_F_SM2_DO_DECRYPT, ERR_R_MALLOC_FAILURE);
+		goto end;
+	}
+
+	/* init ec domain parameters */
+	if (!EC_GROUP_get_order(group, n, bn_ctx)) {
+		SM2err(SM2_F_SM2_DO_DECRYPT, ERR_R_EC_LIB);
+		goto end;
+	}
+
+	if (!EC_GROUP_get_cofactor(group, h, bn_ctx)) {
+		SM2err(SM2_F_SM2_DO_DECRYPT, ERR_R_EC_LIB);
+		goto end;
+	}
+
+	nbytes = (EC_GROUP_get_degree(group) + 7) / 8;
+
+	/* get x/yCoordinates as C1 = (x1, y1) */
+	if (EC_METHOD_get_field_type(EC_GROUP_method_of(group)) == NID_X9_62_prime_field) {
+		if (!EC_POINT_set_affine_coordinates_GFp(group, point,
+			cv->xCoordinate, cv->yCoordinate, bn_ctx)) {
+			SM2err(SM2_F_SM2_DO_DECRYPT, SM2_R_INVALID_CIPHERTEXT);
+			goto end;
+		}
+	} else {
+		if (!EC_POINT_set_affine_coordinates_GF2m(group, point,
+			cv->xCoordinate, cv->yCoordinate, bn_ctx)) {
+			SM2err(SM2_F_SM2_DO_DECRYPT, SM2_R_INVALID_CIPHERTEXT);
+			goto end;
+		}
+	}
+
+	/* check [h]C1 != O */
+	if (!EC_POINT_mul(group, tmp_point, NULL, point, h, bn_ctx)) {
+		SM2err(SM2_F_SM2_DO_DECRYPT, ERR_R_EC_LIB);
+		goto end;
+	}
+
+	if (EC_POINT_is_at_infinity(group, tmp_point)) {
+		SM2err(SM2_F_SM2_DO_DECRYPT, SM2_R_INVALID_CIPHERTEXT);
+		goto end;
+	}
+
+	/* compute ECDH [d]C1 = (x2, y2) */
+	if (!EC_POINT_mul(group, point, NULL, point, pri_key, bn_ctx)) {
+		SM2err(SM2_F_SM2_DO_DECRYPT, ERR_R_EC_LIB);
+		goto end;
+	}
+
+	if (!(len = EC_POINT_point2oct(group, point,
+		POINT_CONVERSION_UNCOMPRESSED, buf, sizeof(buf), bn_ctx))) {
+		SM2err(SM2_F_SM2_DO_DECRYPT, ERR_R_EC_LIB);
+		goto end;
+	}
+
+	/* compute t = KDF(x2 || y2, clen) */
+	*outlen = cv->ciphertext->length;
+	kdf(buf + 1, len - 1, out, outlen);
+
+
+	/* compute M = C2 xor t */
+	for (i = 0; i < cv->ciphertext->length; i++) {
+		out[i] ^= cv->ciphertext->data[i];
+	}
+
+	/* check hash == Hash(x2 || M || y2) */
+	if (!EVP_DigestInit_ex(md_ctx, md, NULL)
+		|| !EVP_DigestUpdate(md_ctx, buf + 1, nbytes)
+		|| !EVP_DigestUpdate(md_ctx, out, *outlen)
+		|| !EVP_DigestUpdate(md_ctx, buf + 1 + nbytes, nbytes)
+		|| !EVP_DigestFinal_ex(md_ctx, mac, &maclen)) {
+		SM2err(SM2_F_SM2_DO_DECRYPT, ERR_R_EVP_LIB);
+		goto end;
+	}
+
+	if (OPENSSL_memcmp(cv->hash->data, mac, maclen) != 0) {
+		SM2err(SM2_F_SM2_DO_DECRYPT, SM2_R_INVALID_CIPHERTEXT);
+		goto end;
+	}
+
+	ret = 1;
+end:
+	EC_POINT_free(point);
+	EC_POINT_free(tmp_point);
+	BN_free(n);
+	BN_free(h);
+	BN_CTX_free(bn_ctx);
+	EVP_MD_CTX_free(md_ctx);
+	return ret;
+}
diff --git a/crypto/sm2/sm2_err.c b/crypto/sm2/sm2_err.c
new file mode 100644
index 000000000..fa943f503
--- /dev/null
+++ b/crypto/sm2/sm2_err.c
@@ -0,0 +1,77 @@
+/*
+ * Generated by util/mkerr.pl DO NOT EDIT
+ * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include 
+
+/* BEGIN ERROR CODES */
+#ifndef OPENSSL_NO_ERR
+
+# define ERR_FUNC(func) ERR_PACK(ERR_LIB_SM2,func,0)
+# define ERR_REASON(reason) ERR_PACK(ERR_LIB_SM2,0,reason)
+
+static ERR_STRING_DATA SM2_str_functs[] = {
+    {ERR_FUNC(SM2_F_I2O_SM2CIPHERTEXTVALUE), "i2o_SM2CiphertextValue"},
+    {ERR_FUNC(SM2_F_O2I_SM2CIPHERTEXTVALUE), "o2i_SM2CiphertextValue"},
+    {ERR_FUNC(SM2_F_SM2CIPHERTEXTVALUE_SIZE), "SM2CiphertextValue_size"},
+    {ERR_FUNC(SM2_F_SM2_CIPHERTEXT_SIZE), "SM2_ciphertext_size"},
+    {ERR_FUNC(SM2_F_SM2_COSIGNER1_GENERATE_PROOF),
+     "SM2_cosigner1_generate_proof"},
+    {ERR_FUNC(SM2_F_SM2_COSIGNER1_GENERATE_SIGNATURE),
+     "SM2_cosigner1_generate_signature"},
+    {ERR_FUNC(SM2_F_SM2_COSIGNER1_SETUP), "SM2_cosigner1_setup"},
+    {ERR_FUNC(SM2_F_SM2_COSIGNER2_GENERATE_PROOF),
+     "SM2_cosigner2_generate_proof"},
+    {ERR_FUNC(SM2_F_SM2_COSIGNER2_SETUP), "SM2_cosigner2_setup"},
+    {ERR_FUNC(SM2_F_SM2_DECRYPT), "SM2_decrypt"},
+    {ERR_FUNC(SM2_F_SM2_DO_DECRYPT), "SM2_do_decrypt"},
+    {ERR_FUNC(SM2_F_SM2_DO_ENCRYPT), "SM2_do_encrypt"},
+    {ERR_FUNC(SM2_F_SM2_DO_SIGN), "SM2_do_sign"},
+    {ERR_FUNC(SM2_F_SM2_DO_VERIFY), "SM2_do_verify"},
+    {ERR_FUNC(SM2_F_SM2_ENCRYPT), "SM2_encrypt"},
+    {ERR_FUNC(SM2_F_SM2_SIGN_SETUP), "SM2_sign_setup"},
+    {0, NULL}
+};
+
+static ERR_STRING_DATA SM2_str_reasons[] = {
+    {ERR_REASON(SM2_R_BAD_SIGNATURE), "bad signature"},
+    {ERR_REASON(SM2_R_BUFFER_TOO_SMALL), "buffer too small"},
+    {ERR_REASON(SM2_R_DECRYPT_FAILURE), "decrypt failure"},
+    {ERR_REASON(SM2_R_ENCRYPT_FAILURE), "encrypt failure"},
+    {ERR_REASON(SM2_R_INVALID_CIPHERTEXT), "invalid ciphertext"},
+    {ERR_REASON(SM2_R_INVALID_DIGEST_ALGOR), "invalid digest algor"},
+    {ERR_REASON(SM2_R_INVALID_EC_KEY), "invalid ec key"},
+    {ERR_REASON(SM2_R_INVALID_INPUT_LENGTH), "invalid input length"},
+    {ERR_REASON(SM2_R_INVALID_PLAINTEXT_LENGTH), "invalid plaintext length"},
+    {ERR_REASON(SM2_R_INVALID_PUBLIC_KEY), "invalid public key"},
+    {ERR_REASON(SM2_R_KDF_FAILURE), "kdf failure"},
+    {ERR_REASON(SM2_R_MISSING_PARAMETERS), "missing parameters"},
+    {ERR_REASON(SM2_R_NEED_NEW_SETUP_VALUES), "need new setup values"},
+    {ERR_REASON(SM2_R_NOT_IMPLEMENTED), "not implemented"},
+    {ERR_REASON(SM2_R_PLAINTEXT_TOO_LONG), "plaintext too long"},
+    {ERR_REASON(SM2_R_RANDOM_NUMBER_GENERATION_FAILED),
+     "random number generation failed"},
+    {0, NULL}
+};
+
+#endif
+
+int ERR_load_SM2_strings(void)
+{
+#ifndef OPENSSL_NO_ERR
+
+    if (ERR_func_error_string(SM2_str_functs[0].error) == NULL) {
+        ERR_load_strings(0, SM2_str_functs);
+        ERR_load_strings(0, SM2_str_reasons);
+    }
+#endif
+    return 1;
+}
diff --git a/crypto/sm2/sm2_exch.c b/crypto/sm2/sm2_exch.c
new file mode 100644
index 000000000..b78f740a7
--- /dev/null
+++ b/crypto/sm2/sm2_exch.c
@@ -0,0 +1,598 @@
+/* ====================================================================
+ * Copyright (c) 2015 - 2016 The GmSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the GmSSL Project.
+ *    (http://gmssl.org/)"
+ *
+ * 4. The name "GmSSL Project" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    guanzhi1980@gmail.com.
+ *
+ * 5. Products derived from this software may not be called "GmSSL"
+ *    nor may "GmSSL" appear in their names without prior written
+ *    permission of the GmSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the GmSSL Project
+ *    (http://gmssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE GmSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE GmSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include "sm2_lcl.h"
+
+int SM2_KAP_CTX_init(SM2_KAP_CTX *ctx,
+	EC_KEY *ec_key, const char *id, size_t idlen,
+	EC_KEY *remote_pubkey, const char *rid, size_t ridlen,
+	int is_initiator, int do_checksum)
+{
+	int ret = 0;
+	int w;
+	size_t len;
+
+	if (!ctx || !ec_key || !remote_pubkey) {
+		ECerr(EC_F_SM2_KAP_CTX_INIT, ERR_R_PASSED_NULL_PARAMETER);
+		return 0;
+	}
+
+	memset(ctx, 0, sizeof(*ctx));
+	ctx->id_dgstlen = sizeof(ctx->id_dgst);
+	ctx->remote_id_dgstlen = sizeof(ctx->remote_id_dgst);
+
+	ctx->id_dgst_md = EVP_sm3();
+	ctx->kdf_md = EVP_sm3();
+	ctx->checksum_md = EVP_sm3();
+	ctx->point_form = SM2_DEFAULT_POINT_CONVERSION_FORM;
+
+	if (!(ctx->kdf = KDF_get_x9_63(ctx->kdf_md))) {
+		ECerr(EC_F_SM2_KAP_CTX_INIT, EC_R_INVALID_KDF_MD);
+		goto end;
+	}
+
+	ctx->is_initiator = is_initiator;
+	ctx->do_checksum = do_checksum;
+
+	if (EC_GROUP_cmp(EC_KEY_get0_group(ec_key),
+		EC_KEY_get0_group(remote_pubkey), NULL) != 0) {
+		ECerr(EC_F_SM2_KAP_CTX_INIT, 0);
+		goto end;
+	}
+
+	len = ctx->id_dgstlen;
+	if (!SM2_compute_id_digest(ctx->id_dgst_md, id, idlen,
+		ctx->id_dgst, &len, ec_key)) {
+		ECerr(EC_F_SM2_KAP_CTX_INIT, 0);
+		goto end;
+	}
+	ctx->id_dgstlen = len;
+
+	if (!(ctx->ec_key = EC_KEY_dup(ec_key))) {
+		ECerr(EC_F_SM2_KAP_CTX_INIT, ERR_R_EC_LIB);
+		goto end;
+	}
+
+	len = ctx->remote_id_dgstlen;
+	if (!SM2_compute_id_digest(ctx->id_dgst_md, rid, ridlen,
+		ctx->remote_id_dgst, &len, remote_pubkey)) {
+		ECerr(EC_F_SM2_KAP_CTX_INIT, 0);
+		goto end;
+	}
+	ctx->remote_id_dgstlen = len;
+
+	if (!(ctx->remote_pubkey = EC_KEY_dup(remote_pubkey))) {
+		ECerr(EC_F_SM2_KAP_CTX_INIT, 0);
+		goto end;
+	}
+
+	ctx->group = EC_KEY_get0_group(ec_key);
+	ctx->bn_ctx = BN_CTX_new();
+	ctx->order = BN_new();
+	ctx->two_pow_w = BN_new();
+	ctx->t = BN_new();
+
+	if (!ctx->bn_ctx || !ctx->order || !ctx->two_pow_w || !ctx->t) {
+		ECerr(EC_F_SM2_KAP_CTX_INIT, ERR_R_BN_LIB);
+		goto end;
+	}
+
+	if (!EC_GROUP_get_order(EC_KEY_get0_group(ec_key), ctx->order, ctx->bn_ctx)) {
+		ECerr(EC_F_SM2_KAP_CTX_INIT, ERR_R_EC_LIB);
+		goto end;
+	}
+
+	w = (BN_num_bits(ctx->order) + 1)/2 - 1;
+
+	if (!BN_one(ctx->two_pow_w)) {
+		ECerr(EC_F_SM2_KAP_CTX_INIT, ERR_R_BN_LIB);
+		goto end;
+	}
+
+	if (!BN_lshift(ctx->two_pow_w, ctx->two_pow_w, w)) {
+		ECerr(EC_F_SM2_KAP_CTX_INIT, ERR_R_BN_LIB);
+		goto end;
+	}
+
+	if (!(ctx->point = EC_POINT_new(ctx->group))) {
+		ECerr(EC_F_SM2_KAP_CTX_INIT, ERR_R_EC_LIB);
+		goto end;
+	}
+
+	ret = 1;
+
+end:
+	if (!ret) SM2_KAP_CTX_cleanup(ctx);
+	return ret;
+}
+
+void SM2_KAP_CTX_cleanup(SM2_KAP_CTX *ctx)
+{
+	if (ctx) {
+		EC_KEY_free(ctx->ec_key);
+		EC_KEY_free(ctx->remote_pubkey);
+		BN_CTX_free(ctx->bn_ctx);
+		BN_free(ctx->two_pow_w);
+		BN_free(ctx->order);
+		EC_POINT_free(ctx->point);
+		BN_free(ctx->t);
+		memset(ctx, 0, sizeof(*ctx));
+	}
+}
+
+/* FIXME: ephem_point_len should be both input and output */
+int SM2_KAP_prepare(SM2_KAP_CTX *ctx, unsigned char *ephem_point,
+	size_t *ephem_point_len)
+{
+	int ret = 0;
+	const BIGNUM *prikey;
+	BIGNUM *h = NULL;
+	BIGNUM *r = NULL;
+	BIGNUM *x = NULL;
+
+	if (!(prikey = EC_KEY_get0_private_key(ctx->ec_key))) {
+		ECerr(EC_F_SM2_KAP_PREPARE, EC_R_SM2_KAP_NOT_INITED);
+		return 0;
+	}
+
+	h = BN_new();
+	r = BN_new();
+	x = BN_new();
+
+	if (!h || !r || !x) {
+		ECerr(EC_F_SM2_KAP_PREPARE, 0);
+		goto end;
+	}
+
+	/*
+	 * r = rand(1, n)
+	 * R = rG = (x, y)
+	 */
+
+	do {
+		if (!BN_rand_range(r, ctx->order)) {
+			ECerr(EC_F_SM2_KAP_PREPARE, EC_R_RANDOM_NUMBER_GENERATION_FAILED);
+			goto end;
+		}
+
+	} while (BN_is_zero(r));
+
+
+	if (!EC_POINT_mul(ctx->group, ctx->point, r, NULL, NULL, ctx->bn_ctx)) {
+		ECerr(EC_F_SM2_KAP_PREPARE, ERR_R_EC_LIB);
+		goto end;
+	}
+
+
+	if (EC_METHOD_get_field_type(EC_GROUP_method_of(ctx->group)) == NID_X9_62_prime_field) {
+		if (!EC_POINT_get_affine_coordinates_GFp(ctx->group, ctx->point, x, NULL, ctx->bn_ctx)) {
+			ECerr(EC_F_SM2_KAP_PREPARE, ERR_R_EC_LIB);
+			goto end;
+		}
+	} else {
+		if (!EC_POINT_get_affine_coordinates_GF2m(ctx->group, ctx->point, x, NULL, ctx->bn_ctx)) {
+			ECerr(EC_F_SM2_KAP_PREPARE, ERR_R_EC_LIB);
+			goto end;
+		}
+	}
+
+	/*
+	 * w = ceil(keybits / 2) - 1
+	 * x = 2^w + (x and (2^w - 1)) = 2^w + (x mod 2^w)
+	 * t = (d + x * r) mod n
+	 * t = (h * t) mod n
+	 */
+
+	if (!ctx->t) {
+		ECerr(EC_F_SM2_KAP_PREPARE, EC_R_SM2_KAP_NOT_INITED);
+		goto end;
+	}
+
+	if (!BN_nnmod(x, x, ctx->two_pow_w, ctx->bn_ctx)) {
+		ECerr(EC_F_SM2_KAP_PREPARE, ERR_R_BN_LIB);
+		goto end;
+	}
+
+	if (!BN_add(x, x, ctx->two_pow_w)) {
+		ECerr(EC_F_SM2_KAP_PREPARE, ERR_R_BN_LIB);
+		goto end;
+	}
+
+	if (!BN_mod_mul(ctx->t, x, r, ctx->order, ctx->bn_ctx)) {
+		ECerr(EC_F_SM2_KAP_PREPARE, ERR_R_BN_LIB);
+		goto end;
+	}
+
+	if (!BN_mod_add(ctx->t, ctx->t, prikey, ctx->order, ctx->bn_ctx)) {
+		ECerr(EC_F_SM2_KAP_PREPARE, ERR_R_BN_LIB);
+		goto end;
+	}
+
+	if (!EC_GROUP_get_cofactor(ctx->group, h, ctx->bn_ctx)) {
+		ECerr(EC_F_SM2_KAP_PREPARE, ERR_R_EC_LIB);
+		goto end;
+	}
+
+	if (!BN_mul(ctx->t, ctx->t, h, ctx->bn_ctx)) {
+		ECerr(EC_F_SM2_KAP_PREPARE, ERR_R_BN_LIB);
+		goto end;
+	}
+
+	/* encode R = (x, y) for output and local buffer */
+
+	// FIXME: ret is size_t and ret is the output length
+	ret = EC_POINT_point2oct(ctx->group, ctx->point, ctx->point_form,
+		ephem_point, *ephem_point_len, ctx->bn_ctx);
+
+	memcpy(ctx->pt_buf, ephem_point, ret);
+	*ephem_point_len = ret;
+	ret = 1;
+
+end:
+	if (h) BN_free(h);
+	if (r) BN_free(r);
+	if (x) BN_free(x);
+
+	return ret;
+}
+
+int SM2_KAP_compute_key(SM2_KAP_CTX *ctx, const unsigned char *remote_point,
+	size_t remote_point_len, unsigned char *key, size_t keylen,
+	unsigned char *checksum, size_t *checksumlen)
+{
+	int ret = 0;
+
+	EVP_MD_CTX *md_ctx = NULL;
+	BIGNUM *x = NULL;
+	unsigned char share_pt_buf[1 + (OPENSSL_ECC_MAX_FIELD_BITS+7)/4 + EVP_MAX_MD_SIZE * 2 + 100];
+	unsigned char remote_pt_buf[1 + (OPENSSL_ECC_MAX_FIELD_BITS+7)/4 + 111];
+	unsigned char dgst[EVP_MAX_MD_SIZE];
+	unsigned int dgstlen;
+	unsigned int len, bnlen;
+	size_t klen = keylen;
+
+	md_ctx = EVP_MD_CTX_new();
+	x = BN_new();
+	if (!md_ctx || !x) {
+		ECerr(EC_F_SM2_KAP_COMPUTE_KEY, 0);
+		goto end;
+	}
+
+	/*
+	 * decode point R = (x, y), encode (x, y)
+	 * x = 2^w + (x and (2^w - 1)) = 2^w + (x mod 2^w), w = ceil(keybits / 2) - 1
+	 * U = ht * (P + x * R)
+	 * check U != O
+	 */
+
+	if (!EC_POINT_oct2point(ctx->group, ctx->point,
+		remote_point, remote_point_len, ctx->bn_ctx)) {
+		ECerr(EC_F_SM2_KAP_COMPUTE_KEY, 0);
+		goto end;
+	}
+
+	if (!(len = EC_POINT_point2oct(ctx->group, ctx->point, POINT_CONVERSION_UNCOMPRESSED,
+		remote_pt_buf, sizeof(remote_pt_buf), ctx->bn_ctx))) {
+		ECerr(EC_F_SM2_KAP_COMPUTE_KEY, 0);
+		goto end;
+	}
+
+	if (EC_METHOD_get_field_type(EC_GROUP_method_of(ctx->group)) == NID_X9_62_prime_field) {
+		if (!EC_POINT_get_affine_coordinates_GFp(ctx->group, ctx->point, x, NULL, ctx->bn_ctx)) {
+			ECerr(EC_F_SM2_KAP_COMPUTE_KEY, ERR_R_EC_LIB);
+			goto end;
+		}
+	} else {
+		if (!EC_POINT_get_affine_coordinates_GF2m(ctx->group, ctx->point, x, NULL, ctx->bn_ctx)) {
+			ECerr(EC_F_SM2_KAP_COMPUTE_KEY, ERR_R_EC_LIB);
+			goto end;
+		}
+	}
+
+	/* x = 2^w + (x and (2^w - 1)) = 2^w + (x mod 2^w) */
+
+	if (!BN_nnmod(x, x, ctx->two_pow_w, ctx->bn_ctx)) {
+		ECerr(EC_F_SM2_KAP_COMPUTE_KEY, ERR_R_BN_LIB);
+		goto end;
+	}
+
+	if (!BN_add(x, x, ctx->two_pow_w)) {
+		ECerr(EC_F_SM2_KAP_COMPUTE_KEY, ERR_R_BN_LIB);
+		goto end;
+	}
+
+	/*
+	if (!BN_mod_mul(x, x, ctx->t, ctx->order, ctx->bn_ctx)) {
+		ECerr(EC_F_SM2_KAP_COMPUTE_KEY, ERR_R_BN_LIB);
+		goto end;
+	}
+	*/
+
+	/* U = ht * (P + x * R), check U != O */
+
+	if (!EC_POINT_mul(ctx->group, ctx->point, NULL, ctx->point, x, ctx->bn_ctx)) {
+		ECerr(EC_F_SM2_KAP_COMPUTE_KEY, ERR_R_EC_LIB);
+		goto end;
+	}
+
+	if (!EC_POINT_add(ctx->group, ctx->point, ctx->point,
+		EC_KEY_get0_public_key(ctx->remote_pubkey), ctx->bn_ctx)) {
+		ECerr(EC_F_SM2_KAP_COMPUTE_KEY, ERR_R_EC_LIB);
+		goto end;
+	}
+
+	if (!EC_POINT_mul(ctx->group, ctx->point, NULL, ctx->point, ctx->t, ctx->bn_ctx)) {
+		ECerr(EC_F_SM2_KAP_COMPUTE_KEY, ERR_R_EC_LIB);
+		goto end;
+	}
+
+	if (EC_POINT_is_at_infinity(ctx->group, ctx->point)) {
+		ECerr(EC_F_SM2_KAP_COMPUTE_KEY, 0);
+		goto end;
+	}
+
+	/* encode U, append with ZA, ZB */
+
+	if (!(len = EC_POINT_point2oct(ctx->group, ctx->point, POINT_CONVERSION_UNCOMPRESSED,
+		share_pt_buf, sizeof(share_pt_buf), ctx->bn_ctx))) {
+		ECerr(EC_F_SM2_KAP_COMPUTE_KEY, 0);
+		goto end;
+	}
+
+	if (ctx->is_initiator) {
+		memcpy(share_pt_buf + len, ctx->id_dgst, ctx->id_dgstlen);
+		len += ctx->id_dgstlen;
+		memcpy(share_pt_buf + len, ctx->remote_id_dgst, ctx->remote_id_dgstlen);
+		len += ctx->remote_id_dgstlen;
+	} else {
+		memcpy(share_pt_buf + len, ctx->remote_id_dgst, ctx->remote_id_dgstlen);
+		len += ctx->remote_id_dgstlen;
+		memcpy(share_pt_buf + len, ctx->id_dgst, ctx->id_dgstlen);
+		len += ctx->id_dgstlen;
+	}
+
+	/* key = KDF(xu, yu, ZA, ZB) */
+
+
+	if (!ctx->kdf(share_pt_buf + 1, len - 1, key, &klen)) {
+		ECerr(EC_F_SM2_KAP_COMPUTE_KEY, 0);
+		goto end;
+	}
+
+	if (ctx->do_checksum) {
+
+		/* generate checksum S1 or SB start with 0x02
+		 * S1 = SB = Hash(0x02, yu, Hash(xu, ZA, ZB, x1, y1, x2, y2))
+		 */
+		if (!EVP_DigestInit_ex(md_ctx, ctx->checksum_md, NULL)) {
+			ECerr(EC_F_SM2_KAP_COMPUTE_KEY, ERR_R_EVP_LIB);
+			goto end;
+		}
+
+		bnlen = BN_num_bytes(ctx->order);
+
+		if (!EVP_DigestUpdate(md_ctx, share_pt_buf + 1, bnlen)) {
+			ECerr(EC_F_SM2_KAP_COMPUTE_KEY, ERR_R_EVP_LIB);
+			goto end;
+		}
+
+		if (ctx->is_initiator) {
+
+			/* update ZA,ZB,x1,y1,x2,y2 */
+			if (!EVP_DigestUpdate(md_ctx, ctx->id_dgst, ctx->id_dgstlen)) {
+				ECerr(EC_F_SM2_KAP_COMPUTE_KEY, ERR_R_EVP_LIB);
+				goto end;
+			}
+			if (!EVP_DigestUpdate(md_ctx, ctx->remote_id_dgst, ctx->remote_id_dgstlen)) {
+				ECerr(EC_F_SM2_KAP_COMPUTE_KEY, ERR_R_EVP_LIB);
+				goto end;
+			}
+			if (!EVP_DigestUpdate(md_ctx, ctx->pt_buf + 1, bnlen * 2)) {
+				ECerr(EC_F_SM2_KAP_COMPUTE_KEY, ERR_R_EVP_LIB);
+				goto end;
+			}
+			if (!EVP_DigestUpdate(md_ctx, remote_pt_buf + 1, bnlen * 2)) {
+				ECerr(EC_F_SM2_KAP_COMPUTE_KEY, ERR_R_EVP_LIB);
+				goto end;
+			}
+
+		} else {
+
+			if (!EVP_DigestUpdate(md_ctx, ctx->remote_id_dgst, ctx->remote_id_dgstlen)) {
+				ECerr(EC_F_SM2_KAP_COMPUTE_KEY, ERR_R_EVP_LIB);
+				goto end;
+			}
+			if (!EVP_DigestUpdate(md_ctx, ctx->id_dgst, ctx->id_dgstlen)) {
+				ECerr(EC_F_SM2_KAP_COMPUTE_KEY, ERR_R_EVP_LIB);
+				goto end;
+			}
+			if (!EVP_DigestUpdate(md_ctx, remote_pt_buf + 1, bnlen * 2)) {
+				ECerr(EC_F_SM2_KAP_COMPUTE_KEY, ERR_R_EVP_LIB);
+				goto end;
+			}
+			if (!EVP_DigestUpdate(md_ctx, ctx->pt_buf + 1, bnlen * 2)) {
+				ECerr(EC_F_SM2_KAP_COMPUTE_KEY, ERR_R_EVP_LIB);
+				goto end;
+			}
+		}
+
+		if (!EVP_DigestFinal_ex(md_ctx, dgst, &dgstlen)) {
+			ECerr(EC_F_SM2_KAP_COMPUTE_KEY, ERR_R_EVP_LIB);
+			goto end;
+		}
+		/* now dgst == H(xu,ZA,ZB,x1,y1,x2,y2)
+		 */
+
+		/* S1 = SB = Hash(0x02, yu, dgst) */
+
+		if (!EVP_DigestInit_ex(md_ctx, ctx->checksum_md, NULL)) {
+			ECerr(EC_F_SM2_KAP_COMPUTE_KEY, ERR_R_EVP_LIB);
+			goto end;
+		}
+
+		if (!EVP_DigestUpdate(md_ctx, "\x02", 1)) {
+			ECerr(EC_F_SM2_KAP_COMPUTE_KEY, ERR_R_EVP_LIB);
+			goto end;
+		}
+
+		if (!EVP_DigestUpdate(md_ctx, share_pt_buf + 1 + bnlen, bnlen)) {
+			ECerr(EC_F_SM2_KAP_COMPUTE_KEY, ERR_R_EVP_LIB);
+			goto end;
+		}
+
+		if (!EVP_DigestUpdate(md_ctx, dgst, dgstlen)) {
+			ECerr(EC_F_SM2_KAP_COMPUTE_KEY, ERR_R_EVP_LIB);
+			goto end;
+		}
+
+		/* output S1 to local buffer or SB to output */
+		if (ctx->is_initiator) {
+			if (!EVP_DigestFinal_ex(md_ctx, ctx->checksum, &len)) {
+				ECerr(EC_F_SM2_KAP_COMPUTE_KEY, ERR_R_EVP_LIB);
+				goto end;
+			}
+
+		} else {
+			if (!EVP_DigestFinal_ex(md_ctx, checksum, &len)) {
+				ECerr(EC_F_SM2_KAP_COMPUTE_KEY, ERR_R_EVP_LIB);
+				goto end;
+			}
+			*checksumlen = len;
+		}
+
+		/* generate checksum SA or S2 start with 0x03
+		 * SA = S2 = Hash(0x03, yu, dgst)
+		 */
+
+		if (!EVP_DigestInit_ex(md_ctx, ctx->checksum_md, NULL)) {
+			ECerr(EC_F_SM2_KAP_COMPUTE_KEY, ERR_R_EVP_LIB);
+			goto end;
+		}
+
+		if (!EVP_DigestUpdate(md_ctx, "\x03", 1)) {
+			ECerr(EC_F_SM2_KAP_COMPUTE_KEY, ERR_R_EVP_LIB);
+			goto end;
+		}
+
+		if (!EVP_DigestUpdate(md_ctx, share_pt_buf + 1 + bnlen, bnlen)) {
+			ECerr(EC_F_SM2_KAP_COMPUTE_KEY, ERR_R_EVP_LIB);
+			goto end;
+		}
+
+		if (!EVP_DigestUpdate(md_ctx, dgst, dgstlen)) {
+			ECerr(EC_F_SM2_KAP_COMPUTE_KEY, ERR_R_EVP_LIB);
+			goto end;
+		}
+
+		if (ctx->is_initiator) {
+			if (!EVP_DigestFinal_ex(md_ctx, checksum, &len)) {
+				ECerr(EC_F_SM2_KAP_COMPUTE_KEY, ERR_R_EVP_LIB);
+				goto end;
+			}
+			*checksumlen = len;
+
+		} else {
+			if (!EVP_DigestFinal_ex(md_ctx, ctx->checksum, &len)) {
+				ECerr(EC_F_SM2_KAP_COMPUTE_KEY, ERR_R_EVP_LIB);
+				goto end;
+			}
+		}
+
+
+	}
+
+	ret = 1;
+
+end:
+	EVP_MD_CTX_free(md_ctx);
+	BN_free(x);
+	return ret;
+}
+
+int SM2_KAP_final_check(SM2_KAP_CTX *ctx, const unsigned char *checksum,
+	size_t checksumlen)
+{
+	if (ctx->do_checksum) {
+		if (checksumlen != (size_t)EVP_MD_size(ctx->checksum_md)) {
+			ECerr(EC_F_SM2_KAP_FINAL_CHECK, EC_R_INVALID_SM2_KAP_CHECKSUM_LENGTH);
+			return 0;
+		}
+		if (memcmp(ctx->checksum, checksum, checksumlen)) {
+			ECerr(EC_F_SM2_KAP_FINAL_CHECK, EC_R_INVALID_SM2_KAP_CHECKSUM_VALUE);
+			return 0;
+		}
+	}
+
+	return 1;
+}
+
+int SM2_compute_share_key(unsigned char *out, size_t *outlen,
+	const EC_POINT *peer_ephem, EC_KEY *ephem,
+	const EC_POINT *peer_pk, const unsigned char *peer_z, size_t peer_zlen,
+	const unsigned char *z, size_t zlen, EC_KEY *sk, int initiator)
+{
+	(void)out;
+	(void)outlen;
+	(void)peer_ephem;
+	(void)ephem;
+	(void)peer_pk;
+	(void)peer_z;
+	(void)peer_zlen;
+	(void)z;
+	(void)zlen;
+	(void)sk;
+	(void)initiator;
+
+	ECerr(EC_F_SM2_COMPUTE_SHARE_KEY, EC_R_NOT_IMPLEMENTED);
+	return 0;
+}
diff --git a/crypto/sm2/sm2_id.c b/crypto/sm2/sm2_id.c
new file mode 100644
index 000000000..ec8edc79f
--- /dev/null
+++ b/crypto/sm2/sm2_id.c
@@ -0,0 +1,326 @@
+/* ====================================================================
+ * Copyright (c) 2015 - 2016 The GmSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the GmSSL Project.
+ *    (http://gmssl.org/)"
+ *
+ * 4. The name "GmSSL Project" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    guanzhi1980@gmail.com.
+ *
+ * 5. Products derived from this software may not be called "GmSSL"
+ *    nor may "GmSSL" appear in their names without prior written
+ *    permission of the GmSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the GmSSL Project
+ *    (http://gmssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE GmSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE GmSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include "sm2_lcl.h"
+
+#define EC_MAX_NBYTES  ((OPENSSL_ECC_MAX_FIELD_BITS + 7)/8)
+
+
+int SM2_get_public_key_data(EC_KEY *ec_key, unsigned char *out, size_t *outlen)
+{
+	int ret = 0;
+	const EC_GROUP *group;
+	BN_CTX *bn_ctx = NULL;
+	BIGNUM *p;
+	BIGNUM *x;
+	BIGNUM *y;
+	int nbytes;
+	size_t len;
+
+	if (!ec_key || !outlen || !(group = EC_KEY_get0_group(ec_key))) {
+		ECerr(EC_F_SM2_GET_PUBLIC_KEY_DATA, ERR_R_PASSED_NULL_PARAMETER);
+		return 0;
+	}
+
+	/* degree is the bit length of field element, not the order of subgroup */
+	nbytes = (EC_GROUP_get_degree(group) + 7)/8;
+	len = nbytes * 6;
+
+	if (!out) {
+		*outlen = len;
+		return 1;
+	}
+	if (*outlen < len) {
+		ECerr(EC_F_SM2_GET_PUBLIC_KEY_DATA, EC_R_BUFFER_TOO_SMALL);
+		return 0;
+	}
+
+	if (!(bn_ctx = BN_CTX_new())) {
+		ECerr(EC_F_SM2_GET_PUBLIC_KEY_DATA,  ERR_R_MALLOC_FAILURE);
+		goto  end;
+	}
+
+	BN_CTX_start(bn_ctx);
+	p = BN_CTX_get(bn_ctx);
+	x = BN_CTX_get(bn_ctx);
+	y = BN_CTX_get(bn_ctx);
+	if (!y) {
+		ECerr(EC_F_SM2_GET_PUBLIC_KEY_DATA,  ERR_R_MALLOC_FAILURE);
+		goto end;
+	}
+
+	memset(out, 0, len);
+
+	/* get curve coefficients */
+	if (EC_METHOD_get_field_type(EC_GROUP_method_of(group)) == NID_X9_62_prime_field) {
+		if (!EC_GROUP_get_curve_GFp(group, p, x, y, bn_ctx)) {
+			ECerr(EC_F_SM2_GET_PUBLIC_KEY_DATA, ERR_R_EC_LIB);
+			goto end;
+		}
+	} else {
+		if (!EC_GROUP_get_curve_GF2m(group, p, x, y, bn_ctx)) {
+			ECerr(EC_F_SM2_GET_PUBLIC_KEY_DATA, ERR_R_EC_LIB);
+			goto end;
+		}
+	}
+
+	/* when coeffiient a is zero, BN_bn2bin/BN_num_bytes return 0 */
+	BN_bn2bin(x, out + nbytes - BN_num_bytes(x));
+	out += nbytes;
+
+	if (!BN_bn2bin(y, out + nbytes - BN_num_bytes(y))) {
+		ECerr(EC_F_SM2_GET_PUBLIC_KEY_DATA, ERR_R_BN_LIB);
+		goto end;
+	}
+	out += nbytes;
+
+	/* get curve generator coordinates */
+	if (EC_METHOD_get_field_type(EC_GROUP_method_of(group)) == NID_X9_62_prime_field) {
+		if (!EC_POINT_get_affine_coordinates_GFp(group,
+			EC_GROUP_get0_generator(group), x, y, bn_ctx)) {
+			ECerr(EC_F_SM2_GET_PUBLIC_KEY_DATA, ERR_R_EC_LIB);
+			goto end;
+		}
+	} else {
+		if (!EC_POINT_get_affine_coordinates_GF2m(group,
+			EC_GROUP_get0_generator(group), x, y, bn_ctx)) {
+			ECerr(EC_F_SM2_GET_PUBLIC_KEY_DATA, ERR_R_EC_LIB);
+			goto end;
+		}
+	}
+
+	if (!BN_bn2bin(x, out + nbytes - BN_num_bytes(x))) {
+		ECerr(EC_F_SM2_GET_PUBLIC_KEY_DATA, ERR_R_BN_LIB);
+		goto end;
+	}
+	out += nbytes;
+
+	if (!BN_bn2bin(y, out + nbytes - BN_num_bytes(y))) {
+		ECerr(EC_F_SM2_GET_PUBLIC_KEY_DATA, ERR_R_BN_LIB);
+		goto end;
+	}
+	out += nbytes;
+
+	/* get pub_key coorindates */
+	if (EC_METHOD_get_field_type(EC_GROUP_method_of(group)) == NID_X9_62_prime_field) {
+		if (!EC_POINT_get_affine_coordinates_GFp(group,
+			EC_KEY_get0_public_key(ec_key), x, y, bn_ctx)) {
+			ECerr(EC_F_SM2_GET_PUBLIC_KEY_DATA, ERR_R_EC_LIB);
+			goto end;
+		}
+	} else {
+		if (!EC_POINT_get_affine_coordinates_GF2m(group,
+			EC_KEY_get0_public_key(ec_key), x, y, bn_ctx)) {
+			ECerr(EC_F_SM2_GET_PUBLIC_KEY_DATA, ERR_R_EC_LIB);
+			goto end;
+		}
+	}
+
+	if (!BN_bn2bin(x, out + nbytes - BN_num_bytes(x))) {
+		ECerr(EC_F_SM2_GET_PUBLIC_KEY_DATA, ERR_R_BN_LIB);
+		goto end;
+	}
+	out += nbytes;
+
+	if (!BN_bn2bin(y, out + nbytes - BN_num_bytes(y))) {
+		ECerr(EC_F_SM2_GET_PUBLIC_KEY_DATA, ERR_R_BN_LIB);
+		goto end;
+	}
+
+	*outlen = len;
+	ret = 1;
+
+end:
+	if (bn_ctx) {
+		BN_CTX_end(bn_ctx);
+	}
+	BN_CTX_free(bn_ctx);
+	return ret;
+}
+
+int SM2_compute_id_digest(const EVP_MD *md, const char *id, size_t idlen,
+	unsigned char *out, size_t *outlen, EC_KEY *ec_key)
+{
+	int ret = 0;
+	EVP_MD_CTX *md_ctx = NULL;
+	unsigned char idbits[2];
+	unsigned char pkdata[SM2_MAX_PKEY_DATA_LENGTH];
+	unsigned int len;
+	size_t size;
+
+	if (!md || !id || idlen <= 0 || !outlen || !ec_key) {
+		ECerr(EC_F_SM2_COMPUTE_ID_DIGEST, ERR_R_PASSED_NULL_PARAMETER);
+		return 0;
+	}
+
+#ifndef OPENSSL_NO_STRICT_GM
+	if (EVP_MD_size(md) != SM2_DEFAULT_ID_DIGEST_LENGTH) {
+		ECerr(EC_F_SM2_COMPUTE_ID_DIGEST, EC_R_INVALID_DIGEST_ALGOR);
+		return 0;
+	}
+#endif
+
+	if (strlen(id) != idlen) {
+		ECerr(EC_F_SM2_COMPUTE_ID_DIGEST, EC_R_INVALID_SM2_ID);
+		return 0;
+	}
+	if (idlen > SM2_MAX_ID_LENGTH || idlen <= 0) {
+		ECerr(EC_F_SM2_COMPUTE_ID_DIGEST, EC_R_INVALID_ID_LENGTH);
+		return 0;
+	}
+
+	if (!out) {
+		*outlen = EVP_MD_size(md);
+		return 1;
+	}
+	if (*outlen < (size_t)EVP_MD_size(md)) {
+		ECerr(EC_F_SM2_COMPUTE_ID_DIGEST, EC_R_BUFFER_TOO_SMALL);
+		return 0;
+	}
+
+
+	/* get public key data from ec_key */
+	size = sizeof(pkdata);
+	if (!SM2_get_public_key_data(ec_key, pkdata, &size)) {
+		ECerr(EC_F_SM2_COMPUTE_ID_DIGEST, EC_R_GET_PUBLIC_KEY_DATA_FAILURE);
+		goto end;
+	}
+
+	/* 2-byte id length in bits */
+	idbits[0] = ((idlen * 8) >> 8) % 256;
+	idbits[1] = (idlen * 8) % 256;
+
+	len = EVP_MD_size(md);
+
+	if (!(md_ctx = EVP_MD_CTX_new())
+		|| !EVP_DigestInit_ex(md_ctx, md, NULL)
+		|| !EVP_DigestUpdate(md_ctx, idbits, sizeof(idbits))
+		|| !EVP_DigestUpdate(md_ctx, id, idlen)
+		|| !EVP_DigestUpdate(md_ctx, pkdata, size)
+		|| !EVP_DigestFinal_ex(md_ctx, out, &len)) {
+		ECerr(EC_F_SM2_COMPUTE_ID_DIGEST, ERR_R_EVP_LIB);
+		goto end;
+	}
+
+	*outlen = len;
+	ret = 1;
+
+end:
+	EVP_MD_CTX_free(md_ctx);
+        return ret;
+}
+
+/*
+ * return msg_md( id_md(id, ec_key) || msg )
+ */
+int SM2_compute_message_digest(const EVP_MD *id_md, const EVP_MD *msg_md,
+	const unsigned char *msg, size_t msglen, const char *id, size_t idlen,
+	unsigned char *out, size_t *poutlen,
+	EC_KEY *ec_key)
+{
+	int ret = 0;
+	EVP_MD_CTX *md_ctx = NULL;
+	unsigned char za[EVP_MAX_MD_SIZE];
+	size_t zalen = sizeof(za);
+	unsigned int outlen;
+
+	if (!id_md || !msg_md || !msg || msglen <= 0 || msglen > INT_MAX ||
+		!id || idlen <= 0 || idlen > INT_MAX || !poutlen || !ec_key) {
+		ECerr(EC_F_SM2_COMPUTE_MESSAGE_DIGEST, ERR_R_PASSED_NULL_PARAMETER);
+		return 0;
+	}
+
+	if (EVP_MD_size(msg_md) <= 0) {
+		ECerr(EC_F_SM2_COMPUTE_MESSAGE_DIGEST, EC_R_INVALID_MD);
+		return 0;
+	}
+	outlen = EVP_MD_size(msg_md);
+
+	if (!out) {
+		*poutlen = outlen;
+		return 1;
+	} else if (*poutlen < outlen) {
+		ECerr(EC_F_SM2_COMPUTE_MESSAGE_DIGEST, EC_R_BUFFER_TOO_SMALL);
+		return 0;
+	}
+
+	if (!SM2_compute_id_digest(id_md, id, idlen, za, &zalen, ec_key)) {
+		ECerr(EC_F_SM2_COMPUTE_MESSAGE_DIGEST, ERR_R_EC_LIB);
+		goto end;
+	}
+
+	/* msg_md(za || msg) */
+	if (!(md_ctx = EVP_MD_CTX_new())
+		|| !EVP_DigestInit_ex(md_ctx, msg_md, NULL)
+		|| !EVP_DigestUpdate(md_ctx, za, zalen)
+		|| !EVP_DigestUpdate(md_ctx, msg, msglen)
+		|| !EVP_DigestFinal_ex(md_ctx, out, &outlen)) {
+		ECerr(EC_F_SM2_COMPUTE_MESSAGE_DIGEST, ERR_R_EVP_LIB);
+		goto end;
+	}
+
+	*poutlen = outlen;
+	ret = 1;
+
+end:
+	EVP_MD_CTX_free(md_ctx);
+	return ret;
+}
diff --git a/crypto/sm2/sm2_kmeth.c b/crypto/sm2/sm2_kmeth.c
new file mode 100644
index 000000000..1e6f391c5
--- /dev/null
+++ b/crypto/sm2/sm2_kmeth.c
@@ -0,0 +1,172 @@
+/*
+ * Copyright (c) 2016 The GmSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the GmSSL Project.
+ *    (http://gmssl.org/)"
+ *
+ * 4. The name "GmSSL Project" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    guanzhi1980@gmail.com.
+ *
+ * 5. Products derived from this software may not be called "GmSSL"
+ *    nor may "GmSSL" appear in their names without prior written
+ *    permission of the GmSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the GmSSL Project
+ *    (http://gmssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE GmSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE GmSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include "../ec/ec_lcl.h"
+#include "sm2_lcl.h"
+
+#define SM2_KMETH_FLAGS		0
+
+
+int EC_KEY_is_sm2p256v1(const EC_KEY *ec_key)
+{
+	const EC_GROUP *group = EC_KEY_get0_group(ec_key);
+	if (group)
+		return EC_GROUP_get_curve_name(group) == NID_sm2p256v1;
+	return 0;
+}
+
+static const EC_KEY_METHOD gmssl_ec_key_method = {
+	"SM2 method",		/* name */
+	EC_KEY_METHOD_SM2,	/* flags */
+	NULL,			/* init */
+	NULL,			/* finish */
+	0,			/* copy */
+	0,			/* set_group */
+	0,			/* set_private */
+	0,			/* set_public */
+	ossl_ec_key_gen,	/* keygen */
+	NULL,			/* compute_key */
+	SM2_sign_ex,		/* sign */
+	SM2_sign_setup,		/* sign_setup */
+	SM2_do_sign_ex,		/* sign_sig */
+	SM2_verify,		/* verify */
+	SM2_do_verify,		/* verify_sig */
+	SM2_encrypt,		/* encrypt */
+	NULL,			/* do_encrypt */
+	SM2_decrypt,		/* decrypt */
+	NULL,			/* do_decrypt */
+};
+
+const EC_KEY_METHOD *EC_KEY_GmSSL(void)
+{
+	return &gmssl_ec_key_method;
+}
+
+int EC_KEY_METHOD_type(const EC_KEY_METHOD *meth)
+{
+	if (meth->flags & EC_KEY_METHOD_SM2) {
+		return NID_sm_scheme;
+	} else {
+		return NID_secg_scheme;
+	}
+}
+
+void EC_KEY_METHOD_set_encrypt(EC_KEY_METHOD *meth,
+                               int (*encrypt)(int type,
+                                              const unsigned char *in,
+                                              size_t inlen,
+                                              unsigned char *out,
+                                              size_t *outlen,
+                                              EC_KEY *ec_key),
+                               ECIES_CIPHERTEXT_VALUE *(*do_encrypt)(int type,
+                                              const unsigned char *in,
+                                              size_t inlen,
+                                              EC_KEY *ec_key))
+{
+    meth->encrypt = encrypt;
+    meth->do_encrypt = do_encrypt;
+}
+
+void EC_KEY_METHOD_set_decrypt(EC_KEY_METHOD *meth,
+                               int (*decrypt)(int type,
+                                              const unsigned char *in,
+                                              size_t inlen,
+                                              unsigned char *out,
+                                              size_t *outlen,
+                                              EC_KEY *ec_key),
+                               int (do_decrypt)(int type,
+                                                const ECIES_CIPHERTEXT_VALUE *in,
+                                                unsigned char *out,
+                                                size_t *outlen,
+                                                EC_KEY *ec_key))
+{
+    meth->decrypt = decrypt;
+    meth->do_decrypt = do_decrypt;
+}
+
+void EC_KEY_METHOD_get_encrypt(EC_KEY_METHOD *meth,
+                               int (**pencrypt)(int type,
+                                                const unsigned char *in,
+                                                size_t inlen,
+                                                unsigned char *out,
+                                                size_t *outlen,
+                                                EC_KEY *ec_key),
+                               ECIES_CIPHERTEXT_VALUE *(**pdo_encrypt)(int type,
+                                                const unsigned char *in,
+                                                size_t inlen,
+                                                EC_KEY *ec_key))
+{
+    if (pencrypt != NULL)
+        *pencrypt = meth->encrypt;
+    if (pdo_encrypt != NULL)
+        *pdo_encrypt = meth->do_encrypt;
+}
+
+void EC_KEY_METHOD_get_decrypt(EC_KEY_METHOD *meth,
+                               int (**pdecrypt)(int type,
+                                                const unsigned char *in,
+                                                size_t inlen,
+                                                unsigned char *out,
+                                                size_t *outlen,
+                                                EC_KEY *ec_key),
+                               int (**pdo_decrypt)(int type,
+                                                   const ECIES_CIPHERTEXT_VALUE *in,
+                                                   unsigned char *out,
+                                                   size_t *outlen,
+                                                   EC_KEY *ec_key))
+{
+	if (pdecrypt != NULL)
+		*pdecrypt = meth->decrypt;
+	if (pdo_decrypt != NULL)
+		*pdo_decrypt = meth->do_decrypt;
+}
diff --git a/crypto/sm2/sm2_lcl.h b/crypto/sm2/sm2_lcl.h
new file mode 100644
index 000000000..c53744063
--- /dev/null
+++ b/crypto/sm2/sm2_lcl.h
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2015 - 2017 The GmSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the GmSSL Project.
+ *    (http://gmssl.org/)"
+ *
+ * 4. The name "GmSSL Project" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    guanzhi1980@gmail.com.
+ *
+ * 5. Products derived from this software may not be called "GmSSL"
+ *    nor may "GmSSL" appear in their names without prior written
+ *    permission of the GmSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the GmSSL Project
+ *    (http://gmssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE GmSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE GmSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define EC_KEY_METHOD_SM2	0x02
+
+#define SM2_DEFAULT_POINT_CONVERSION_FORM	POINT_CONVERSION_UNCOMPRESSED
+
+#define SM2_MAX_PKEY_DATA_LENGTH		((EC_MAX_NBYTES + 1) * 6)
+
+#define SM2_MAX_PLAINTEXT_LENGTH		65535
+#define SM2_MAX_CIPHERTEXT_LENGTH		(SM2_MAX_PLAINTEXT_LENGTH + 2048)
+
+
+int SM2_get_public_key_data(EC_KEY *ec_key, unsigned char *out, size_t *outlen);
+
+struct SM2CiphertextValue_st {
+	BIGNUM *xCoordinate;
+	BIGNUM *yCoordinate;
+	ASN1_OCTET_STRING *hash;
+	ASN1_OCTET_STRING *ciphertext;
+};
+
+struct sm2_kap_ctx_st {
+
+	const EVP_MD *id_dgst_md;
+	const EVP_MD *kdf_md;
+	const EVP_MD *checksum_md;
+	point_conversion_form_t point_form;
+	KDF_FUNC kdf;
+
+	int is_initiator;
+	int do_checksum;
+
+	EC_KEY *ec_key;
+	unsigned char id_dgst[EVP_MAX_MD_SIZE];
+	unsigned int id_dgstlen;
+
+	EC_KEY *remote_pubkey;
+	unsigned char remote_id_dgst[EVP_MAX_MD_SIZE];
+	unsigned int remote_id_dgstlen;
+
+	const EC_GROUP *group;
+	BN_CTX *bn_ctx;
+	BIGNUM *order;
+	BIGNUM *two_pow_w;
+
+	BIGNUM *t;
+	EC_POINT *point;
+	unsigned char pt_buf[1 + (OPENSSL_ECC_MAX_FIELD_BITS+7)/4];
+	unsigned char checksum[EVP_MAX_MD_SIZE];
+
+};
+
diff --git a/crypto/sm2/sm2_oct.c b/crypto/sm2/sm2_oct.c
new file mode 100755
index 000000000..0a006cce9
--- /dev/null
+++ b/crypto/sm2/sm2_oct.c
@@ -0,0 +1,284 @@
+/* ====================================================================
+ * Copyright (c) 2007 - 2016 The GmSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the GmSSL Project.
+ *    (http://gmssl.org/)"
+ *
+ * 4. The name "GmSSL Project" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    guanzhi1980@gmail.com.
+ *
+ * 5. Products derived from this software may not be called "GmSSL"
+ *    nor may "GmSSL" appear in their names without prior written
+ *    permission of the GmSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the GmSSL Project
+ *    (http://gmssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE GmSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE GmSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include "sm2_lcl.h"
+
+
+int i2o_SM2CiphertextValue(const EC_GROUP *group, const SM2CiphertextValue *cv,
+	unsigned char **pout)
+{
+	int ret = 0, outlen = 0, nbytes;
+	EC_POINT *point = NULL;
+	BN_CTX *bn_ctx = NULL;
+	unsigned char *buf;
+	unsigned char *p;
+	size_t siz;
+
+	if (!group || !cv || !pout) {
+		SM2err(SM2_F_I2O_SM2CIPHERTEXTVALUE,
+			ERR_R_PASSED_NULL_PARAMETER);
+		return 0;
+	}
+
+	nbytes = (EC_GROUP_get_degree(group) + 7)/8;
+
+	if (!cv->xCoordinate || BN_num_bytes(cv->xCoordinate) > nbytes
+		|| !cv->yCoordinate || BN_num_bytes(cv->yCoordinate) > nbytes
+		|| ASN1_STRING_length(cv->hash) <= 0
+		|| ASN1_STRING_length(cv->hash) > EVP_MAX_MD_SIZE
+		|| ASN1_STRING_length(cv->ciphertext) <= 0) {
+		SM2err(SM2_F_I2O_SM2CIPHERTEXTVALUE, SM2_R_INVALID_CIPHERTEXT);
+		return 0;
+	}
+
+	/* prepare buffer */
+	if (*pout) {
+		p = *pout;
+
+	} else {
+		size_t buflen = 1 + nbytes * 2
+			+ ASN1_STRING_length(cv->ciphertext)
+			+ ASN1_STRING_length(cv->hash);
+
+		if (!(buf = OPENSSL_malloc(buflen))) {
+			SM2err(SM2_F_I2O_SM2CIPHERTEXTVALUE,
+				ERR_R_MALLOC_FAILURE);
+			return 0;
+		}
+
+		p = buf;
+	}
+
+	/* encode x, y */
+	if (!(point = EC_POINT_new(group)) || !(bn_ctx = BN_CTX_new())) {
+		SM2err(SM2_F_I2O_SM2CIPHERTEXTVALUE, ERR_R_MALLOC_FAILURE);
+		goto end;
+	}
+
+	if (EC_METHOD_get_field_type(EC_GROUP_method_of(group)) == NID_X9_62_prime_field) {
+		if (!EC_POINT_set_affine_coordinates_GFp(group, point,
+			cv->xCoordinate, cv->yCoordinate, bn_ctx)) {
+			goto end;
+		}
+	} else {
+		if (!EC_POINT_set_affine_coordinates_GF2m(group, point,
+			cv->xCoordinate, cv->yCoordinate, bn_ctx)) {
+			goto end;
+		}
+	}
+
+	if (!(siz = EC_POINT_point2oct(group, point,
+		POINT_CONVERSION_UNCOMPRESSED, p, 1 + 2 * nbytes, bn_ctx))) {
+		SM2err(SM2_F_I2O_SM2CIPHERTEXTVALUE, ERR_R_EC_LIB);
+		goto end;
+	}
+	OPENSSL_assert(siz == 1 + 2 * nbytes);
+	p += siz;
+	outlen += siz;
+
+	/* encode ciphertext */
+	memcpy(p, ASN1_STRING_get0_data(cv->ciphertext),
+		ASN1_STRING_length(cv->ciphertext));
+	p += ASN1_STRING_length(cv->ciphertext);
+	outlen += ASN1_STRING_length(cv->ciphertext);
+
+	/* encode hash */
+	memcpy(p, ASN1_STRING_get0_data(cv->hash),
+		ASN1_STRING_length(cv->hash));
+	p += ASN1_STRING_length(cv->hash);
+	outlen += ASN1_STRING_length(cv->hash);
+
+	/* output */
+	if (*pout) {
+		*pout = p;
+	} else {
+		*pout = buf;
+		buf = NULL;
+	}
+	ret = outlen;
+
+end:
+	EC_POINT_free(point);
+	BN_CTX_free(bn_ctx);
+	return ret;
+}
+
+SM2CiphertextValue *o2i_SM2CiphertextValue(const EC_GROUP *group,
+	const EVP_MD *md, SM2CiphertextValue **pout,
+	const unsigned char **pin, long len)
+{
+	SM2CiphertextValue *ret = NULL;
+	SM2CiphertextValue *cv = NULL;
+	BN_CTX *bn_ctx = NULL;
+	EC_POINT *point = NULL;
+	const unsigned char *p;
+	int nbytes;
+
+	if (!group || !pin) {
+		SM2err(SM2_F_O2I_SM2CIPHERTEXTVALUE,
+			ERR_R_PASSED_NULL_PARAMETER);
+		return NULL;
+	}
+
+	nbytes = (EC_GROUP_get_degree(group) + 7)/8;
+
+	if (len <= 1 + nbytes * 2 + EVP_MD_size(md)) {
+		SM2err(SM2_F_O2I_SM2CIPHERTEXTVALUE,
+			SM2_R_INVALID_CIPHERTEXT);
+		return NULL;
+	}
+
+	if (pout && *pout) {
+		cv = *pout;
+	} else {
+		if (!(cv = SM2CiphertextValue_new())) {
+			SM2err(SM2_F_O2I_SM2CIPHERTEXTVALUE,
+				ERR_R_MALLOC_FAILURE);
+			goto end;
+		}
+	}
+
+	if (!(point = EC_POINT_new(group))
+		|| !(bn_ctx = BN_CTX_new())) {
+		SM2err(SM2_F_O2I_SM2CIPHERTEXTVALUE,
+			ERR_R_MALLOC_FAILURE);
+		goto end;
+	}
+
+	p = *pin;
+
+	/* set (x, y) */
+	if (!EC_POINT_oct2point(group, point, p, 1 + nbytes * 2, bn_ctx)) {
+		SM2err(SM2_F_O2I_SM2CIPHERTEXTVALUE,
+			SM2_R_INVALID_CIPHERTEXT);
+		goto end;
+	}
+	p += 1 + nbytes * 2;
+	len -= 1 + nbytes * 2;
+
+	if (!cv->xCoordinate) {
+		if (!(cv->xCoordinate = BN_new())) {
+			SM2err(SM2_F_O2I_SM2CIPHERTEXTVALUE,
+				ERR_R_MALLOC_FAILURE);
+			goto end;
+		}
+	}
+	if (!cv->yCoordinate) {
+		if (!(cv->yCoordinate = BN_new())) {
+			SM2err(SM2_F_O2I_SM2CIPHERTEXTVALUE,
+				ERR_R_MALLOC_FAILURE);
+			goto end;
+		}
+	}
+
+	if (EC_METHOD_get_field_type(EC_GROUP_method_of(group)) == NID_X9_62_prime_field) {
+		if (!EC_POINT_get_affine_coordinates_GFp(group, point,
+			cv->xCoordinate, cv->yCoordinate, bn_ctx)) {
+			SM2err(SM2_F_O2I_SM2CIPHERTEXTVALUE, ERR_R_EC_LIB);
+			goto end;
+		}
+	} else {
+		if (!EC_POINT_get_affine_coordinates_GF2m(group, point,
+			cv->xCoordinate, cv->yCoordinate, bn_ctx)) {
+			SM2err(SM2_F_O2I_SM2CIPHERTEXTVALUE, ERR_R_EC_LIB);
+			goto end;
+		}
+	}
+
+	/* set ciphertext */
+	if (!cv->ciphertext) {
+		if (!(cv->ciphertext = ASN1_OCTET_STRING_new())) {
+			SM2err(SM2_F_O2I_SM2CIPHERTEXTVALUE,
+				ERR_R_MALLOC_FAILURE);
+			goto end;
+		}
+	}
+
+	if (!ASN1_OCTET_STRING_set(cv->ciphertext, p, len - EVP_MD_size(md))) {
+		SM2err(SM2_F_O2I_SM2CIPHERTEXTVALUE, ERR_R_ASN1_LIB);
+		goto end;
+	}
+	p += len - EVP_MD_size(md);
+
+	/* set hash */
+	if (!cv->hash) {
+		if (!(cv->hash = ASN1_OCTET_STRING_new())) {
+			SM2err(SM2_F_O2I_SM2CIPHERTEXTVALUE,
+				ERR_R_MALLOC_FAILURE);
+			goto end;
+		}
+	}
+
+	if (!ASN1_OCTET_STRING_set(cv->hash, p, EVP_MD_size(md))) {
+		SM2err(SM2_F_O2I_SM2CIPHERTEXTVALUE, ERR_R_ASN1_LIB);
+		goto end;
+	}
+	p += EVP_MD_size(md);
+
+	/* set result */
+	*pin = p;
+	ret = cv;
+	cv = NULL;
+
+end:
+	if ((cv != *pout) && (!ret))
+		SM2CiphertextValue_free(cv);
+
+	EC_POINT_free(point);
+	BN_CTX_free(bn_ctx);
+	return ret;
+}
diff --git a/crypto/sm2/sm2_sign.c b/crypto/sm2/sm2_sign.c
new file mode 100644
index 000000000..5eca82b18
--- /dev/null
+++ b/crypto/sm2/sm2_sign.c
@@ -0,0 +1,608 @@
+/* ====================================================================
+ * Copyright (c) 2015 - 2018 The GmSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the GmSSL Project.
+ *    (http://gmssl.org/)"
+ *
+ * 4. The name "GmSSL Project" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    guanzhi1980@gmail.com.
+ *
+ * 5. Products derived from this software may not be called "GmSSL"
+ *    nor may "GmSSL" appear in their names without prior written
+ *    permission of the GmSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the GmSSL Project
+ *    (http://gmssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE GmSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE GmSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include "../ec/ec_lcl.h"
+
+static int sm2_sign_idx = -1;
+
+static void sm2_sign_free(void *parent, void *ptr, CRYPTO_EX_DATA *ad,
+	int idx, long argl, void *argp)
+{
+	BIGNUM *bn = (BIGNUM *)CRYPTO_get_ex_data(ad, sm2_sign_idx);
+	if (bn) {
+		BN_clear_free(bn);
+		CRYPTO_set_ex_data(ad, sm2_sign_idx, NULL);
+	}
+
+	(void)parent;
+	(void)ptr;
+	(void)idx;
+	(void)argl;
+	(void)argp;
+}
+
+static int sm2_sign_setup(EC_KEY *ec_key, BN_CTX *ctx_in, BIGNUM **kp, BIGNUM **xp)
+{
+	int ret = 0;
+	const EC_GROUP *ec_group;
+	BN_CTX *ctx = NULL;
+	BIGNUM *k = NULL;
+	BIGNUM *x = NULL;
+	BIGNUM *order = NULL;
+	EC_POINT *point = NULL;
+
+	if (ec_key == NULL || (ec_group = EC_KEY_get0_group(ec_key)) == NULL) {
+		SM2err(SM2_F_SM2_SIGN_SETUP, ERR_R_PASSED_NULL_PARAMETER);
+		return 0;
+	}
+
+	if (ctx_in == NULL)  {
+		if ((ctx = BN_CTX_new()) == NULL) {
+			SM2err(SM2_F_SM2_SIGN_SETUP, ERR_R_MALLOC_FAILURE);
+			return 0;
+		}
+	}
+	else {
+		ctx = ctx_in;
+	}
+
+
+	k = BN_new();
+	x = BN_new();
+	order = BN_new();
+	if (!k || !x || !order) {
+		SM2err(SM2_F_SM2_SIGN_SETUP, ERR_R_MALLOC_FAILURE);
+		goto end;
+	}
+
+	if (!EC_GROUP_get_order(ec_group, order, ctx)) {
+		SM2err(SM2_F_SM2_SIGN_SETUP, ERR_R_EC_LIB);
+		goto end;
+	}
+
+	if ((point = EC_POINT_new(ec_group)) == NULL) {
+		SM2err(SM2_F_SM2_SIGN_SETUP, ERR_R_EC_LIB);
+		goto end;
+	}
+
+	/* do pre compute (1 + d)^-1 */
+	if (sm2_sign_idx < 0) {
+		if ((sm2_sign_idx = EC_KEY_get_ex_new_index(0, NULL, NULL, NULL,
+			sm2_sign_free)) < 0) {
+			SM2err(SM2_F_SM2_SIGN_SETUP, ERR_R_EC_LIB);
+			goto end;
+		}
+	}
+
+	if (!EC_KEY_get_ex_data(ec_key, sm2_sign_idx)) {
+		BIGNUM *d = NULL;
+		if (!(d = BN_dup(EC_KEY_get0_private_key(ec_key)))) {
+			SM2err(SM2_F_SM2_SIGN_SETUP, ERR_R_MALLOC_FAILURE);
+			goto end;
+		}
+		if (!BN_add_word(d, 1)) {
+			SM2err(SM2_F_SM2_SIGN_SETUP, ERR_R_BN_LIB);
+			BN_clear_free(d);
+			goto end;
+		}
+		if (!BN_mod_inverse(d, d, order, ctx)) {
+			SM2err(SM2_F_SM2_SIGN_SETUP, ERR_R_BN_LIB);
+			BN_clear_free(d);
+			goto end;
+		}
+		if (!EC_KEY_set_ex_data(ec_key, sm2_sign_idx, d)) {
+			SM2err(SM2_F_SM2_SIGN_SETUP, ERR_R_EC_LIB);
+			goto end;
+		}
+	}
+
+	do {
+		/* get random k */
+		do {
+			if (!BN_rand_range(k, order)) {
+				SM2err(SM2_F_SM2_SIGN_SETUP,
+					SM2_R_RANDOM_NUMBER_GENERATION_FAILED);
+				goto end;
+			}
+
+		} while (BN_is_zero(k));
+
+		/* compute r the x-coordinate of generator * k */
+		if (!EC_POINT_mul(ec_group, point, k, NULL, NULL, ctx)) {
+			SM2err(SM2_F_SM2_SIGN_SETUP, ERR_R_EC_LIB);
+			goto end;
+		}
+
+		if (EC_METHOD_get_field_type(EC_GROUP_method_of(ec_group)) == NID_X9_62_prime_field) {
+			if (!EC_POINT_get_affine_coordinates_GFp(ec_group, point, x, NULL, ctx)) {
+				SM2err(SM2_F_SM2_SIGN_SETUP, ERR_R_EC_LIB);
+				goto end;
+			}
+		} else /* NID_X9_62_characteristic_two_field */ {
+			if (!EC_POINT_get_affine_coordinates_GF2m(ec_group, point, x, NULL, ctx)) {
+				SM2err(SM2_F_SM2_SIGN_SETUP, ERR_R_EC_LIB);
+				goto end;
+			}
+		}
+
+		if (!BN_nnmod(x, x, order, ctx)) {
+			SM2err(SM2_F_SM2_SIGN_SETUP, ERR_R_BN_LIB);
+			goto end;
+		}
+
+	} while (BN_is_zero(x));
+
+	/* clear old values if necessary */
+	BN_clear_free(*kp);
+	BN_clear_free(*xp);
+
+	/* save the pre-computed values  */
+	*kp = k;
+	*xp = x;
+	ret = 1;
+
+end:
+	if (!ret) {
+		BN_clear_free(k);
+		BN_clear_free(x);
+	}
+	if (!ctx_in) {
+		BN_CTX_free(ctx);
+	}
+	BN_free(order);
+	EC_POINT_free(point);
+	return(ret);
+}
+
+static ECDSA_SIG *sm2_do_sign(const unsigned char *dgst, int dgstlen,
+	const BIGNUM *in_k, const BIGNUM *in_x, EC_KEY *ec_key)
+{
+	int ok = 0;
+	ECDSA_SIG *ret = NULL;
+	const EC_GROUP *ec_group;
+	const BIGNUM *priv_key;
+	const BIGNUM *ck;
+	BIGNUM *k = NULL;
+	BN_CTX *ctx = NULL;
+	BIGNUM *order = NULL;
+	BIGNUM *e = NULL;
+	BIGNUM *bn = NULL;
+	int i;
+
+	ec_group = EC_KEY_get0_group(ec_key);
+	priv_key = EC_KEY_get0_private_key(ec_key);
+	if (!ec_group || !priv_key) {
+		SM2err(SM2_F_SM2_DO_SIGN, ERR_R_PASSED_NULL_PARAMETER);
+		return NULL;
+	}
+
+	if (!(ret = ECDSA_SIG_new())) {
+		SM2err(SM2_F_SM2_DO_SIGN, ERR_R_MALLOC_FAILURE);
+		return NULL;
+	}
+	ret->r = BN_new();
+	ret->s = BN_new();
+	ctx = BN_CTX_new();
+	order = BN_new();
+	e = BN_new();
+	bn = BN_new();
+	if (!ret->r || !ret->s || !ctx || !order || !e || !bn) {
+		SM2err(SM2_F_SM2_DO_SIGN, ERR_R_MALLOC_FAILURE);
+		goto end;
+	}
+	if (!EC_GROUP_get_order(ec_group, order, ctx)) {
+		SM2err(SM2_F_SM2_DO_SIGN, ERR_R_EC_LIB);
+		goto end;
+	}
+
+	/* convert dgst to e */
+	i = BN_num_bits(order);
+#if 0
+	if (8 * dgstlen > i) {
+		dgstlen = (i + 7)/8;
+	}
+#endif
+	if (!BN_bin2bn(dgst, dgstlen, e)) {
+		SM2err(SM2_F_SM2_DO_SIGN, ERR_R_BN_LIB);
+		goto end;
+	}
+
+#if 0
+	if ((8 * dgstlen > i) && !BN_rshift(e, e, 8 - (i & 0x7))) {
+		SM2err(SM2_F_SM2_DO_SIGN, ERR_R_BN_LIB);
+		goto end;
+	}
+#endif
+
+	do {
+		/* use or compute k and (kG).x */
+		if (!in_k || !in_x) {
+			if (!sm2_sign_setup(ec_key, ctx, &k, &ret->r)) {
+				SM2err(SM2_F_SM2_DO_SIGN, ERR_R_ECDSA_LIB);
+				goto end;
+			}
+			ck = k;
+		} else {
+			ck = in_k;
+			if (!BN_copy(ret->r, in_x)) {
+				SM2err(SM2_F_SM2_DO_SIGN, ERR_R_MALLOC_FAILURE);
+				goto end;
+			}
+		}
+
+		/* r = e + x (mod n) */
+		if (!BN_mod_add(ret->r, ret->r, e, order, ctx)) {
+			SM2err(SM2_F_SM2_DO_SIGN, ERR_R_BN_LIB);
+			goto end;
+		}
+
+		if (!BN_mod_add(bn, ret->r, ck, order, ctx)) {
+			SM2err(SM2_F_SM2_DO_SIGN, ERR_R_BN_LIB);
+			goto end;
+		}
+
+		/* check r != 0 && r + k != n */
+		if (BN_is_zero(ret->r) || BN_is_zero(bn)) {
+			if (in_k && in_x) {
+				SM2err(SM2_F_SM2_DO_SIGN, SM2_R_NEED_NEW_SETUP_VALUES);
+				goto end;
+			} else
+				continue;
+		}
+
+		/* s = ((1 + d)^-1 * (k - rd)) mod n */
+#if 0
+		if (!BN_one(bn)) {
+			SM2err(SM2_F_SM2_DO_SIGN, ERR_R_BN_LIB);
+			goto end;
+		}
+
+		if (!BN_mod_add(ret->s, priv_key, bn, order, ctx)) {
+			SM2err(SM2_F_SM2_DO_SIGN, ERR_R_BN_LIB);
+			goto end;
+		}
+		if (!BN_mod_inverse(ret->s, ret->s, order, ctx)) {
+			SM2err(SM2_F_SM2_DO_SIGN, ERR_R_BN_LIB);
+			goto end;
+		}
+
+		if (!BN_mod_mul(bn, ret->r, priv_key, order, ctx)) {
+			SM2err(SM2_F_SM2_DO_SIGN, ERR_R_BN_LIB);
+			goto end;
+		}
+		if (!BN_mod_sub(bn, ck, bn, order, ctx)) {
+			SM2err(SM2_F_SM2_DO_SIGN, ERR_R_BN_LIB);
+			goto end;
+		}
+		if (!BN_mod_mul(ret->s, ret->s, bn, order, ctx)) {
+			SM2err(SM2_F_SM2_DO_SIGN, ERR_R_BN_LIB);
+			goto end;
+		}
+#else
+		/* s = d'(k + r) - r mod n */
+		if (!BN_mod_mul(ret->s, EC_KEY_get_ex_data(ec_key, sm2_sign_idx),
+			bn, order, ctx)) {
+			SM2err(SM2_F_SM2_DO_SIGN, ERR_R_BN_LIB);
+			goto end;
+		}
+		if (!BN_mod_sub(ret->s, ret->s, ret->r, order, ctx)) {
+			SM2err(SM2_F_SM2_DO_SIGN, ERR_R_BN_LIB);
+			goto end;
+		}
+#endif
+
+		/* check s != 0 */
+		if (BN_is_zero(ret->s)) {
+			if (in_k && in_x) {
+				SM2err(SM2_F_SM2_DO_SIGN, SM2_R_NEED_NEW_SETUP_VALUES);
+				goto end;
+			}
+		} else {
+			break;
+		}
+
+	} while (1);
+
+#if 0
+	if (!BN_rshift1(bn, order)) {
+		SM2err(SM2_F_SM2_DO_SIGN, ERR_R_BN_LIB);
+		goto end;
+	}
+	if (BN_cmp(ret->r, bn) <= 0) {
+		if (!BN_sub(ret->r, order, ret->r)
+			|| !BN_sub(ret->s, order, ret->s)) {
+			SM2err(SM2_F_SM2_DO_SIGN, ERR_R_BN_LIB);
+			goto end;
+		}
+	}
+
+#endif
+
+	ok = 1;
+
+end:
+	if (!ok) {
+		ECDSA_SIG_free(ret);
+		ret = NULL;
+	}
+	BN_free(k);
+	BN_CTX_free(ctx);
+	BN_free(order);
+	BN_free(e);
+	BN_free(bn);
+
+	return ret;
+}
+
+int sm2_do_verify(const unsigned char *dgst, int dgstlen,
+	const ECDSA_SIG *sig, EC_KEY *ec_key)
+{
+	int ret = -1;
+	const EC_GROUP *ec_group;
+	const EC_POINT *pub_key;
+	EC_POINT *point = NULL;
+	BN_CTX *ctx = NULL;
+	BIGNUM *order = NULL;
+	BIGNUM *e = NULL;
+	BIGNUM *t = NULL;
+	int i;
+
+	if (!sig || !ec_key ||
+		!(ec_group = EC_KEY_get0_group(ec_key)) ||
+		!(pub_key  = EC_KEY_get0_public_key(ec_key))) {
+
+		SM2err(SM2_F_SM2_DO_VERIFY, SM2_R_MISSING_PARAMETERS);
+		return -1;
+	}
+
+	ctx = BN_CTX_new();
+	order = BN_new();
+	e = BN_new();
+	t = BN_new();
+	if (!ctx || !order || !e || !t) {
+		SM2err(SM2_F_SM2_DO_VERIFY, ERR_R_MALLOC_FAILURE);
+		goto end;
+	}
+	if (!EC_GROUP_get_order(ec_group, order, ctx)) {
+		SM2err(SM2_F_SM2_DO_VERIFY, ERR_R_EC_LIB);
+		goto end;
+	}
+
+#if 0
+	if (!BN_rshift1(t, order)) {
+		SM2err(SM2_F_SM2_DO_VERIFY, ERR_R_BN_LIB);
+		goto end;
+	}
+	if (BN_cmp(sig->r, t) <= 0) {
+		SM2err(SM2_F_SM2_DO_VERIFY, ERR_R_BN_LIB); //FIXME: error code
+		goto end;
+	}
+#endif
+
+	/* check r, s in [1, n-1] and r + s != 0 (mod n) */
+	if (BN_is_zero(sig->r) ||
+		BN_is_negative(sig->r) ||
+		BN_ucmp(sig->r, order) >= 0 ||
+		BN_is_zero(sig->s) ||
+		BN_is_negative(sig->s) ||
+		BN_ucmp(sig->s, order) >= 0) {
+
+		SM2err(SM2_F_SM2_DO_VERIFY, SM2_R_BAD_SIGNATURE);
+		ret = 0;
+		goto end;
+	}
+
+	/* check t = r + s != 0 */
+	if (!BN_mod_add(t, sig->r, sig->s, order, ctx)) {
+		SM2err(SM2_F_SM2_DO_VERIFY, ERR_R_BN_LIB);
+		goto end;
+	}
+	if (BN_is_zero(t)) {
+		ret = 0;
+		goto end;
+	}
+
+	/* convert digest to e */
+	i = BN_num_bits(order);
+#if 0
+	if (8 * dgstlen > i) {
+		dgstlen = (i + 7)/8;
+	}
+#endif
+	if (!BN_bin2bn(dgst, dgstlen, e)) {
+		SM2err(SM2_F_SM2_DO_VERIFY, ERR_R_BN_LIB);
+		goto end;
+	}
+#if 0
+	if ((8 * dgstlen > i) && !BN_rshift(e, e, 8 - (i & 0x7))) {
+		SM2err(SM2_F_SM2_DO_VERIFY, ERR_R_BN_LIB);
+		goto end;
+	}
+#endif
+
+	/* compute (x, y) = sG + tP, P is pub_key */
+	if (!(point = EC_POINT_new(ec_group))) {
+		SM2err(SM2_F_SM2_DO_VERIFY, ERR_R_MALLOC_FAILURE);
+		goto end;
+	}
+	if (!EC_POINT_mul(ec_group, point, sig->s, pub_key, t, ctx)) {
+		SM2err(SM2_F_SM2_DO_VERIFY, ERR_R_EC_LIB);
+		goto end;
+	}
+	if (EC_METHOD_get_field_type(EC_GROUP_method_of(ec_group)) == NID_X9_62_prime_field) {
+		if (!EC_POINT_get_affine_coordinates_GFp(ec_group, point, t, NULL, ctx)) {
+			SM2err(SM2_F_SM2_DO_VERIFY, ERR_R_EC_LIB);
+			goto end;
+		}
+	} else /* NID_X9_62_characteristic_two_field */ {
+		if (!EC_POINT_get_affine_coordinates_GF2m(ec_group, point, t, NULL, ctx)) {
+			SM2err(SM2_F_SM2_DO_VERIFY, ERR_R_EC_LIB);
+			goto end;
+		}
+	}
+	if (!BN_nnmod(t, t, order, ctx)) {
+		SM2err(SM2_F_SM2_DO_VERIFY, ERR_R_BN_LIB);
+		goto end;
+	}
+
+	/* check (sG + tP).x + e  == sig.r */
+	if (!BN_mod_add(t, t, e, order, ctx)) {
+		SM2err(SM2_F_SM2_DO_VERIFY, ERR_R_BN_LIB);
+		goto end;
+	}
+	if (BN_ucmp(t, sig->r) == 0) {
+		ret = 1;
+	} else {
+		printf("%s %d: %s\n", __FILE__, __LINE__, __FUNCTION__);
+		ret = 0;
+	}
+
+end:
+	EC_POINT_free(point);
+	BN_free(order);
+	BN_free(e);
+	BN_free(t);
+	BN_CTX_free(ctx);
+	return ret;
+}
+
+int SM2_sign_setup(EC_KEY *ec_key, BN_CTX *ctx_in, BIGNUM **kp, BIGNUM **xp)
+{
+	return sm2_sign_setup(ec_key, ctx_in, kp, xp);
+}
+
+ECDSA_SIG *SM2_do_sign_ex(const unsigned char *dgst, int dgstlen,
+	const BIGNUM *kp, const BIGNUM *xp, EC_KEY *ec_key)
+{
+	return sm2_do_sign(dgst, dgstlen, kp, xp, ec_key);
+}
+
+ECDSA_SIG *SM2_do_sign(const unsigned char *dgst, int dgstlen, EC_KEY *ec_key)
+{
+	return SM2_do_sign_ex(dgst, dgstlen, NULL, NULL, ec_key);
+}
+
+int SM2_do_verify(const unsigned char *dgst, int dgstlen,
+	const ECDSA_SIG *sig, EC_KEY *ec_key)
+{
+	return sm2_do_verify(dgst, dgstlen, sig, ec_key);
+}
+
+int SM2_sign_ex(int type, const unsigned char *dgst, int dgstlen,
+	unsigned char *sig, unsigned int *siglen,
+	const BIGNUM *k, const BIGNUM *x, EC_KEY *ec_key)
+{
+	ECDSA_SIG *s;
+
+	if (type != NID_undef) {
+		return 0;
+	}
+
+	RAND_seed(dgst, dgstlen);
+
+	if (!(s = SM2_do_sign_ex(dgst, dgstlen, k, x, ec_key))) {
+		*siglen = 0;
+		return 0;
+	}
+
+	*siglen = i2d_ECDSA_SIG(s, &sig);
+	ECDSA_SIG_free(s);
+
+	return 1;
+}
+
+int SM2_sign(int type, const unsigned char *dgst, int dgstlen,
+	unsigned char *sig, unsigned int *siglen, EC_KEY *ec_key)
+{
+	return SM2_sign_ex(type, dgst, dgstlen, sig, siglen, NULL, NULL, ec_key);
+}
+
+int SM2_verify(int type, const unsigned char *dgst, int dgstlen,
+	const unsigned char *sig, int siglen, EC_KEY *ec_key)
+{
+	ECDSA_SIG *s;
+	const unsigned char *p = sig;
+	unsigned char *der = NULL;
+	int derlen = -1;
+	int ret = -1;
+
+	if (type != NID_undef) {
+		return ret;
+	}
+
+	if (!(s = ECDSA_SIG_new())) {
+		return ret;
+	}
+	if (!d2i_ECDSA_SIG(&s, &p, siglen)) {
+		goto err;
+	}
+	derlen = i2d_ECDSA_SIG(s, &der);
+	if (derlen != siglen || memcmp(sig, der, derlen)) {
+		goto err;
+	}
+
+	ret = SM2_do_verify(dgst, dgstlen, s, ec_key);
+
+err:
+	if (derlen > 0) {
+		OPENSSL_cleanse(der, derlen);
+		OPENSSL_free(der);
+	}
+
+	ECDSA_SIG_free(s);
+	return ret;
+}
diff --git a/crypto/sm3/asm/sm3-x86_64.pl b/crypto/sm3/asm/sm3-x86_64.pl
new file mode 100755
index 000000000..f07fb8516
--- /dev/null
+++ b/crypto/sm3/asm/sm3-x86_64.pl
@@ -0,0 +1,289 @@
+#! /usr/bin/env perl
+
+$flavour = shift;
+$output  = shift;
+if ($flavour =~ /\./) { $output = $flavour; undef $flavour; }
+
+$win64=0; $win64=1 if ($flavour =~ /[nm]asm|mingw64/ || $output =~ /\.asm$/);
+
+$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+( $xlate="${dir}x86_64-xlate.pl" and -f $xlate ) or
+( $xlate="${dir}../../perlasm/x86_64-xlate.pl" and -f $xlate) or
+die "can't locate x86_64-xlate.pl";
+
+if (`$ENV{CC} -Wa,-v -c -o /dev/null -x assembler /dev/null 2>&1`
+		=~ /GNU assembler version ([2-9]\.[0-9]+)/) {
+	$avx = ($1>=2.19) + ($1>=2.22);
+}
+
+if (!$avx && $win64 && ($flavour =~ /nasm/ || $ENV{ASM} =~ /nasm/) &&
+	   `nasm -v 2>&1` =~ /NASM version ([2-9]\.[0-9]+)/) {
+	$avx = ($1>=2.09) + ($1>=2.10);
+}
+
+if (!$avx && $win64 && ($flavour =~ /masm/ || $ENV{ASM} =~ /ml64/) &&
+	   `ml64 2>&1` =~ /Version ([0-9]+)\./) {
+	$avx = ($1>=10) + ($1>=11);
+}
+
+if (!$avx && `$ENV{CC} -v 2>&1` =~ /((?:^clang|LLVM) version|.*based on LLVM) ([3-9]\.[0-9]+)/) {
+	$avx = ($2>=3.0) + ($2>3.0);
+}
+
+$avx=1		if ($avx);
+
+open OUT,"| \"$^X\" \"$xlate\" $flavour \"$output\"";
+*STDOUT=*OUT;
+
+	$func="sm3_block_data_order";
+	$TABLE="K256";
+	$SZ=4;
+	@ROT=($A,$B,$C,$D,$E,$F,$G,$H)=("%eax","%ebx","%ecx","%edx",
+					"%r8d","%r9d","%r10d","%r11d");
+	($T1,$a0,$a1,$a2,$a3)=("%r12d","%r13d","%r14d","%r15d","%edi");
+	@Sigma0=( 2,13,22);
+	@Sigma1=( 6,11,25);
+	@sigma0=( 7,18, 3);
+	@sigma1=(17,19,10);
+	$rounds=64;
+
+$ctx="%rdi";	# 1st arg, zapped by $a3
+$inp="%rsi";	# 2nd arg
+$Tbl="%rbp";
+
+$_ctx="16*$SZ+0*8(%rsp)";
+$_inp="16*$SZ+1*8(%rsp)";
+$_end="16*$SZ+2*8(%rsp)";
+$_rsp="16*$SZ+3*8(%rsp)";
+$framesz="16*$SZ+4*8";
+
+
+sub ROUND_00_15()
+{ my ($i,$a,$b,$c,$d,$e,$f,$g,$h) = @_;
+  my $STRIDE=$SZ;
+     $STRIDE += 16 if ($i%(16/$SZ)==(16/$SZ-1));
+
+$code.=<<___;
+	ror	\$`$Sigma1[2]-$Sigma1[1]`,$a0
+	mov	$f,$a2
+
+	xor	$e,$a0
+	ror	\$`$Sigma0[2]-$Sigma0[1]`,$a1
+	xor	$g,$a2			# f^g
+
+	mov	$T1,`$SZ*($i&0xf)`(%rsp)
+	xor	$a,$a1
+	and	$e,$a2			# (f^g)&e
+
+	ror	\$`$Sigma1[1]-$Sigma1[0]`,$a0
+	add	$h,$T1			# T1+=h
+	xor	$g,$a2			# Ch(e,f,g)=((f^g)&e)^g
+
+	ror	\$`$Sigma0[1]-$Sigma0[0]`,$a1
+	xor	$e,$a0
+	add	$a2,$T1			# T1+=Ch(e,f,g)
+
+	mov	$a,$a2
+	add	($Tbl),$T1		# T1+=K[round]
+	xor	$a,$a1
+
+	xor	$b,$a2			# a^b, b^c in next round
+	ror	\$$Sigma1[0],$a0	# Sigma1(e)
+	mov	$b,$h
+
+	and	$a2,$a3
+	ror	\$$Sigma0[0],$a1	# Sigma0(a)
+	add	$a0,$T1			# T1+=Sigma1(e)
+
+	xor	$a3,$h			# h=Maj(a,b,c)=Ch(a^b,c,b)
+	add	$T1,$d			# d+=T1
+	add	$T1,$h			# h+=T1
+
+	lea	$STRIDE($Tbl),$Tbl	# round++
+___
+$code.=<<___ if ($i<15);
+	add	$a1,$h			# h+=Sigma0(a)
+___
+	($a2,$a3) = ($a3,$a2);
+}
+
+sub ROUND_16_XX()
+{ my ($i,$a,$b,$c,$d,$e,$f,$g,$h) = @_;
+
+$code.=<<___;
+	mov	`$SZ*(($i+1)&0xf)`(%rsp),$a0
+	mov	`$SZ*(($i+14)&0xf)`(%rsp),$a2
+
+	mov	$a0,$T1
+	ror	\$`$sigma0[1]-$sigma0[0]`,$a0
+	add	$a1,$a			# modulo-scheduled h+=Sigma0(a)
+	mov	$a2,$a1
+	ror	\$`$sigma1[1]-$sigma1[0]`,$a2
+
+	xor	$T1,$a0
+	shr	\$$sigma0[2],$T1
+	ror	\$$sigma0[0],$a0
+	xor	$a1,$a2
+	shr	\$$sigma1[2],$a1
+
+	ror	\$$sigma1[0],$a2
+	xor	$a0,$T1			# sigma0(X[(i+1)&0xf])
+	xor	$a1,$a2			# sigma1(X[(i+14)&0xf])
+	add	`$SZ*(($i+9)&0xf)`(%rsp),$T1
+
+	add	`$SZ*($i&0xf)`(%rsp),$T1
+	mov	$e,$a0
+	add	$a2,$T1
+	mov	$a,$a1
+___
+	&ROUND_00_15(@_);
+}
+
+$code=<<___;
+.text
+
+.extern	OPENSSL_ia32cap_P
+.globl	$func
+.type	$func,\@function,3
+.align	16
+$func:
+___
+$code.=<<___;
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+	mov	%rsp,%r11		# copy %rsp
+	shl	\$4,%rdx		# num*16
+	sub	\$$framesz,%rsp
+	lea	($inp,%rdx,$SZ),%rdx	# inp+num*16*$SZ
+	and	\$-64,%rsp		# align stack frame
+	mov	$ctx,$_ctx		# save ctx, 1st arg
+	mov	$inp,$_inp		# save inp, 2nd arh
+	mov	%rdx,$_end		# save end pointer, "3rd" arg
+	mov	%r11,$_rsp		# save copy of %rsp
+.Lprologue:
+
+	mov	$SZ*0($ctx),$A
+	mov	$SZ*1($ctx),$B
+	mov	$SZ*2($ctx),$C
+	mov	$SZ*3($ctx),$D
+	mov	$SZ*4($ctx),$E
+	mov	$SZ*5($ctx),$F
+	mov	$SZ*6($ctx),$G
+	mov	$SZ*7($ctx),$H
+	jmp	.Lloop
+
+.align	16
+.Lloop:
+	mov	$B,$a3
+	lea	$TABLE(%rip),$Tbl
+	xor	$C,$a3			# magic
+___
+	for($i=0;$i<16;$i++) {
+		$code.="	mov	$SZ*$i($inp),$T1\n";
+		$code.="	mov	@ROT[4],$a0\n";
+		$code.="	mov	@ROT[0],$a1\n";
+		$code.="	bswap	$T1\n";
+		&ROUND_00_15($i,@ROT);
+		unshift(@ROT,pop(@ROT));
+	}
+$code.=<<___;
+	jmp	.Lrounds_16_xx
+.align	16
+.Lrounds_16_xx:
+___
+	for(;$i<32;$i++) {
+		&ROUND_16_XX($i,@ROT);
+		unshift(@ROT,pop(@ROT));
+	}
+
+$code.=<<___;
+	cmpb	\$0,`$SZ-1`($Tbl)
+	jnz	.Lrounds_16_xx
+
+	mov	$_ctx,$ctx
+	add	$a1,$A			# modulo-scheduled h+=Sigma0(a)
+	lea	16*$SZ($inp),$inp
+
+	add	$SZ*0($ctx),$A
+	add	$SZ*1($ctx),$B
+	add	$SZ*2($ctx),$C
+	add	$SZ*3($ctx),$D
+	add	$SZ*4($ctx),$E
+	add	$SZ*5($ctx),$F
+	add	$SZ*6($ctx),$G
+	add	$SZ*7($ctx),$H
+
+	cmp	$_end,$inp
+
+	mov	$A,$SZ*0($ctx)
+	mov	$B,$SZ*1($ctx)
+	mov	$C,$SZ*2($ctx)
+	mov	$D,$SZ*3($ctx)
+	mov	$E,$SZ*4($ctx)
+	mov	$F,$SZ*5($ctx)
+	mov	$G,$SZ*6($ctx)
+	mov	$H,$SZ*7($ctx)
+	jb	.Lloop
+
+	mov	$_rsp,%rsi
+	mov	(%rsi),%r15
+	mov	8(%rsi),%r14
+	mov	16(%rsi),%r13
+	mov	24(%rsi),%r12
+	mov	32(%rsi),%rbp
+	mov	40(%rsi),%rbx
+	lea	48(%rsi),%rsp
+.Lepilogue:
+	ret
+.size	$func,.-$func
+___
+
+$code.=<<___;
+.align	64
+.type	$TABLE,\@object
+$TABLE:
+	.long	0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5
+	.long	0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5
+	.long	0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5
+	.long	0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5
+	.long	0xd807aa98,0x12835b01,0x243185be,0x550c7dc3
+	.long	0xd807aa98,0x12835b01,0x243185be,0x550c7dc3
+	.long	0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174
+	.long	0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174
+	.long	0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc
+	.long	0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc
+	.long	0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da
+	.long	0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da
+	.long	0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7
+	.long	0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7
+	.long	0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967
+	.long	0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967
+	.long	0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13
+	.long	0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13
+	.long	0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85
+	.long	0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85
+	.long	0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3
+	.long	0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3
+	.long	0xd192e819,0xd6990624,0xf40e3585,0x106aa070
+	.long	0xd192e819,0xd6990624,0xf40e3585,0x106aa070
+	.long	0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5
+	.long	0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5
+	.long	0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3
+	.long	0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3
+	.long	0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208
+	.long	0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208
+	.long	0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2
+	.long	0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2
+
+	.long	0x00010203,0x04050607,0x08090a0b,0x0c0d0e0f
+	.long	0x00010203,0x04050607,0x08090a0b,0x0c0d0e0f
+	.long	0x03020100,0x0b0a0908,0xffffffff,0xffffffff
+	.long	0x03020100,0x0b0a0908,0xffffffff,0xffffffff
+	.long	0xffffffff,0xffffffff,0x03020100,0x0b0a0908
+	.long	0xffffffff,0xffffffff,0x03020100,0x0b0a0908
+___
+close STDOUT;
diff --git a/crypto/sm3/build.info b/crypto/sm3/build.info
new file mode 100644
index 000000000..794f7fa7c
--- /dev/null
+++ b/crypto/sm3/build.info
@@ -0,0 +1,3 @@
+LIBS=../../libcrypto
+SOURCE[../../libcrypto]=sm3.c sm3_hmac.c
+INCLUDE[sm3.o]=../modes
diff --git a/crypto/sm3/sm3.c b/crypto/sm3/sm3.c
new file mode 100755
index 000000000..0ac8eda14
--- /dev/null
+++ b/crypto/sm3/sm3.c
@@ -0,0 +1,509 @@
+/* ====================================================================
+ * Copyright (c) 2014 - 2017 The GmSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the GmSSL Project.
+ *    (http://gmssl.org/)"
+ *
+ * 4. The name "GmSSL Project" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    guanzhi1980@gmail.com.
+ *
+ * 5. Products derived from this software may not be called "GmSSL"
+ *    nor may "GmSSL" appear in their names without prior written
+ *    permission of the GmSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the GmSSL Project
+ *    (http://gmssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE GmSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE GmSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+
+#include 
+#include 
+#include "internal/rotate.h"
+#include "internal/byteorder.h"
+#include "modes_lcl.h"
+
+#ifdef SM3_SSE3
+# include 
+# include 
+
+# define _mm_rotl_epi32(X,i) \
+	_mm_xor_si128(_mm_slli_epi32((X),(i)), _mm_srli_epi32((X),32-(i)))
+#endif
+
+static void sm3_compress_blocks(uint32_t digest[8],
+	const unsigned char *data, size_t blocks);
+
+
+void sm3_init(sm3_ctx_t *ctx)
+{
+	memset(ctx, 0, sizeof(*ctx));
+	ctx->digest[0] = 0x7380166F;
+	ctx->digest[1] = 0x4914B2B9;
+	ctx->digest[2] = 0x172442D7;
+	ctx->digest[3] = 0xDA8A0600;
+	ctx->digest[4] = 0xA96F30BC;
+	ctx->digest[5] = 0x163138AA;
+	ctx->digest[6] = 0xE38DEE4D;
+	ctx->digest[7] = 0xB0FB0E4E;
+}
+
+void sm3_compute_id_digest(unsigned char z[32], const char *id,
+	const unsigned char x[32], const unsigned char y[32])
+{
+	unsigned char zin[] = {
+		0x00, 0x80,
+		0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38,
+		0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38,
+		0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF,
+		0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+		0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
+		0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC,
+		0x28, 0xE9, 0xFA, 0x9E, 0x9D, 0x9F, 0x5E, 0x34,
+		0x4D, 0x5A, 0x9E, 0x4B, 0xCF, 0x65, 0x09, 0xA7,
+		0xF3, 0x97, 0x89, 0xF5, 0x15, 0xAB, 0x8F, 0x92,
+		0xDD, 0xBC, 0xBD, 0x41, 0x4D, 0x94, 0x0E, 0x93,
+       		0x32, 0xC4, 0xAE, 0x2C, 0x1F, 0x19, 0x81, 0x19,
+		0x5F, 0x99, 0x04, 0x46, 0x6A, 0x39, 0xC9, 0x94,
+		0x8F, 0xE3, 0x0B, 0xBF, 0xF2, 0x66, 0x0B, 0xE1,
+		0x71, 0x5A, 0x45, 0x89, 0x33, 0x4C, 0x74, 0xC7,
+		0xBC, 0x37, 0x36, 0xA2, 0xF4, 0xF6, 0x77, 0x9C,
+		0x59, 0xBD, 0xCE, 0xE3, 0x6B, 0x69, 0x21, 0x53,
+		0xD0, 0xA9, 0x87, 0x7C, 0xC6, 0x2A, 0x47, 0x40,
+        	0x02, 0xDF, 0x32, 0xE5, 0x21, 0x39, 0xF0, 0xA0,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x06, 0x90,
+	};
+
+	if (!id || strcmp(id, "1234567812345678")) {
+		unsigned int digest[8] = {
+			0xadadedb5U, 0x0446043fU, 0x08a87aceU, 0xe86d2243U,
+			0x8e232383U, 0xbfc81fe2U, 0xcf9117c8U, 0x4707011dU,
+		};
+		memcpy(&zin[128], x, 32);
+		memcpy(&zin[160], y, 32);
+		sm3_compress_blocks(digest, zin, 2);
+		PUTU32(z     , digest[0]);
+		PUTU32(z +  4, digest[1]);
+		PUTU32(z +  8, digest[2]);
+		PUTU32(z + 12, digest[3]);
+		PUTU32(z + 16, digest[4]);
+		PUTU32(z + 20, digest[5]);
+		PUTU32(z + 24, digest[6]);
+		PUTU32(z + 28, digest[7]);
+
+	} else {
+		sm3_ctx_t ctx;
+		unsigned char idbits[2];
+		size_t len;
+
+		len = strlen(id);
+		idbits[0] = (unsigned char)(len >> 5);
+		idbits[1] = (unsigned char)(len << 3);
+
+		sm3_init(&ctx);
+		sm3_update(&ctx, idbits, 2);
+		sm3_update(&ctx, (unsigned char *)id, len);
+		sm3_update(&ctx, zin + 18, 128);
+		sm3_update(&ctx, x, 32);
+		sm3_update(&ctx, y, 32);
+		sm3_final(&ctx, z);
+	}
+}
+
+int sm3_sm2_init(sm3_ctx_t *ctx, const char *id,
+	const unsigned char *x, const unsigned char *y)
+{
+	unsigned char z[32];
+	if ((id && strlen(id) > 65535/8) || !x || !y) {
+		return 0;
+	}
+	sm3_compute_id_digest(z, id, x, y);
+	sm3_init(ctx);
+	sm3_update(ctx, z, 32);
+	return 1;
+}
+
+void sm3_update(sm3_ctx_t *ctx, const unsigned char *data, size_t data_len)
+{
+	size_t blocks;
+
+	if (ctx->num) {
+		unsigned int left = SM3_BLOCK_SIZE - ctx->num;
+		if (data_len < left) {
+			memcpy(ctx->block + ctx->num, data, data_len);
+			ctx->num += data_len;
+			return;
+		} else {
+			memcpy(ctx->block + ctx->num, data, left);
+			sm3_compress_blocks(ctx->digest, ctx->block, 1);
+			ctx->nblocks++;
+			data += left;
+			data_len -= left;
+		}
+	}
+
+	blocks = data_len / SM3_BLOCK_SIZE;
+	sm3_compress_blocks(ctx->digest, data, blocks);
+	ctx->nblocks += blocks;
+	data += SM3_BLOCK_SIZE * blocks;
+	data_len -= SM3_BLOCK_SIZE * blocks;
+
+	ctx->num = data_len;
+	if (data_len) {
+		memcpy(ctx->block, data, data_len);
+	}
+}
+
+void sm3_final(sm3_ctx_t *ctx, unsigned char *digest)
+{
+	int i;
+
+	ctx->block[ctx->num] = 0x80;
+
+	if (ctx->num + 9 <= SM3_BLOCK_SIZE) {
+		memset(ctx->block + ctx->num + 1, 0, SM3_BLOCK_SIZE - ctx->num - 9);
+	} else {
+		memset(ctx->block + ctx->num + 1, 0, SM3_BLOCK_SIZE - ctx->num - 1);
+		sm3_compress(ctx->digest, ctx->block);
+		memset(ctx->block, 0, SM3_BLOCK_SIZE - 8);
+	}
+	PUTU32(ctx->block + 56, ctx->nblocks >> 23);
+	PUTU32(ctx->block + 60, (ctx->nblocks << 9) + (ctx->num << 3));
+
+	sm3_compress(ctx->digest, ctx->block);
+	for (i = 0; i < 8; i++) {
+		PUTU32(digest + i*4, ctx->digest[i]);
+	}
+}
+
+#define ROTL(x,n)  (((x)<<(n)) | ((x)>>(32-(n))))
+#define P0(x) ((x) ^ ROL32((x), 9) ^ ROL32((x),17))
+#define P1(x) ((x) ^ ROL32((x),15) ^ ROL32((x),23))
+
+#define FF00(x,y,z)  ((x) ^ (y) ^ (z))
+#define FF16(x,y,z)  (((x)&(y)) | ((x)&(z)) | ((y)&(z)))
+#define GG00(x,y,z)  ((x) ^ (y) ^ (z))
+#define GG16(x,y,z)  ((((y)^(z)) & (x)) ^ (z))
+
+#define R(A, B, C, D, E, F, G, H, xx)				\
+	SS1 = ROL32((ROL32(A, 12) + E + K[j]), 7);		\
+	SS2 = SS1 ^ ROL32(A, 12);				\
+	TT1 = FF##xx(A, B, C) + D + SS2 + (W[j] ^ W[j + 4]);	\
+	TT2 = GG##xx(E, F, G) + H + SS1 + W[j];			\
+	B = ROL32(B, 9);					\
+	H = TT1;						\
+	F = ROL32(F, 19);					\
+	D = P0(TT2);						\
+	j++
+
+#define R8(A, B, C, D, E, F, G, H, xx)				\
+	R(A, B, C, D, E, F, G, H, xx);				\
+	R(H, A, B, C, D, E, F, G, xx);				\
+	R(G, H, A, B, C, D, E, F, xx);				\
+	R(F, G, H, A, B, C, D, E, xx);				\
+	R(E, F, G, H, A, B, C, D, xx);				\
+	R(D, E, F, G, H, A, B, C, xx);				\
+	R(C, D, E, F, G, H, A, B, xx);				\
+	R(B, C, D, E, F, G, H, A, xx)
+
+
+
+#define T00 0x79cc4519U
+#define T16 0x7a879d8aU
+
+#define K0	0x79cc4519U
+#define K1	0xf3988a32U
+#define K2	0xe7311465U
+#define K3	0xce6228cbU
+#define K4	0x9cc45197U
+#define K5	0x3988a32fU
+#define K6	0x7311465eU
+#define K7	0xe6228cbcU
+#define K8	0xcc451979U
+#define K9	0x988a32f3U
+#define K10	0x311465e7U
+#define K11	0x6228cbceU
+#define K12	0xc451979cU
+#define K13	0x88a32f39U
+#define K14	0x11465e73U
+#define K15	0x228cbce6U
+#define K16	0x9d8a7a87U
+#define K17	0x3b14f50fU
+#define K18	0x7629ea1eU
+#define K19	0xec53d43cU
+#define K20	0xd8a7a879U
+#define K21	0xb14f50f3U
+#define K22	0x629ea1e7U
+#define K23	0xc53d43ceU
+#define K24	0x8a7a879dU
+#define K25	0x14f50f3bU
+#define K26	0x29ea1e76U
+#define K27	0x53d43cecU
+#define K28	0xa7a879d8U
+#define K29	0x4f50f3b1U
+#define K30	0x9ea1e762U
+#define K31	0x3d43cec5U
+#define K32	0x7a879d8aU
+#define K33	0xf50f3b14U
+#define K34	0xea1e7629U
+#define K35	0xd43cec53U
+#define K36	0xa879d8a7U
+#define K37	0x50f3b14fU
+#define K38	0xa1e7629eU
+#define K39	0x43cec53dU
+#define K40	0x879d8a7aU
+#define K41	0x0f3b14f5U
+#define K42	0x1e7629eaU
+#define K43	0x3cec53d4U
+#define K44	0x79d8a7a8U
+#define K45	0xf3b14f50U
+#define K46	0xe7629ea1U
+#define K47	0xcec53d43U
+#define K48	0x9d8a7a87U
+#define K49	0x3b14f50fU
+#define K50	0x7629ea1eU
+#define K51	0xec53d43cU
+#define K52	0xd8a7a879U
+#define K53	0xb14f50f3U
+#define K54	0x629ea1e7U
+#define K55	0xc53d43ceU
+#define K56	0x8a7a879dU
+#define K57	0x14f50f3bU
+#define K58	0x29ea1e76U
+#define K59	0x53d43cecU
+#define K60	0xa7a879d8U
+#define K61	0x4f50f3b1U
+#define K62	0x9ea1e762U
+#define K63	0x3d43cec5U
+
+uint32_t K[64] = {
+	K0,  K1,  K2,  K3,  K4,  K5,  K6,  K7,
+	K8,  K9,  K10, K11, K12, K13, K14, K15,
+	K16, K17, K18, K19, K20, K21, K22, K23,
+	K24, K25, K26, K27, K28, K29, K30, K31,
+	K32, K33, K34, K35, K36, K37, K38, K39,
+	K40, K41, K42, K43, K44, K45, K46, K47,
+	K48, K49, K50, K51, K52, K53, K54, K55,
+	K56, K57, K58, K59, K60, K61, K62, K63,
+	/*
+	0x79cc4519U, 0xf3988a32U, 0xe7311465U, 0xce6228cbU,
+	0x9cc45197U, 0x3988a32fU, 0x7311465eU, 0xe6228cbcU,
+	0xcc451979U, 0x988a32f3U, 0x311465e7U, 0x6228cbceU,
+	0xc451979cU, 0x88a32f39U, 0x11465e73U, 0x228cbce6U,
+	0x9d8a7a87U, 0x3b14f50fU, 0x7629ea1eU, 0xec53d43cU,
+	0xd8a7a879U, 0xb14f50f3U, 0x629ea1e7U, 0xc53d43ceU,
+	0x8a7a879dU, 0x14f50f3bU, 0x29ea1e76U, 0x53d43cecU,
+	0xa7a879d8U, 0x4f50f3b1U, 0x9ea1e762U, 0x3d43cec5U,
+	0x7a879d8aU, 0xf50f3b14U, 0xea1e7629U, 0xd43cec53U,
+	0xa879d8a7U, 0x50f3b14fU, 0xa1e7629eU, 0x43cec53dU,
+	0x879d8a7aU, 0x0f3b14f5U, 0x1e7629eaU, 0x3cec53d4U,
+	0x79d8a7a8U, 0xf3b14f50U, 0xe7629ea1U, 0xcec53d43U,
+	0x9d8a7a87U, 0x3b14f50fU, 0x7629ea1eU, 0xec53d43cU,
+	0xd8a7a879U, 0xb14f50f3U, 0x629ea1e7U, 0xc53d43ceU,
+	0x8a7a879dU, 0x14f50f3bU, 0x29ea1e76U, 0x53d43cecU,
+	0xa7a879d8U, 0x4f50f3b1U, 0x9ea1e762U, 0x3d43cec5U,
+	*/
+};
+
+static void sm3_compress_blocks(uint32_t digest[8],
+	const unsigned char *data, size_t blocks)
+{
+	uint32_t A;
+	uint32_t B;
+	uint32_t C;
+	uint32_t D;
+	uint32_t E;
+	uint32_t F;
+	uint32_t G;
+	uint32_t H;
+	uint32_t W[68];
+	uint32_t SS1, SS2, TT1, TT2;
+	int j;
+
+#ifdef SM3_SSE3
+	__m128i X, T, R;
+	__m128i M = _mm_setr_epi32(0, 0, 0, 0xffffffff);
+	__m128i V = _mm_setr_epi8(3,2,1,0,7,6,5,4,11,10,9,8,15,14,13,12);
+#endif
+
+	while (blocks--) {
+
+		A = digest[0];
+		B = digest[1];
+		C = digest[2];
+		D = digest[3];
+		E = digest[4];
+		F = digest[5];
+		G = digest[6];
+		H = digest[7];
+
+
+#ifdef SM3_SSE3
+
+		for (j = 0; j < 16; j += 4) {
+			X = _mm_loadu_si128((__m128i *)(data + j * 4));
+			X = _mm_shuffle_epi8(X, V);
+			_mm_storeu_si128((__m128i *)(W + j), X);
+		}
+
+		for (j = 16; j < 68; j += 4) {
+			/* X = (W[j - 3], W[j - 2], W[j - 1], 0) */
+			X = _mm_loadu_si128((__m128i *)(W + j - 3));
+			X = _mm_andnot_si128(M, X);
+
+			X = _mm_rotl_epi32(X, 15);
+			T = _mm_loadu_si128((__m128i *)(W + j - 9));
+			X = _mm_xor_si128(X, T);
+			T = _mm_loadu_si128((__m128i *)(W + j - 16));
+			X = _mm_xor_si128(X, T);
+
+			/* P1() */
+			T = _mm_rotl_epi32(X, (23 - 15));
+			T = _mm_xor_si128(T, X);
+			T = _mm_rotl_epi32(T, 15);
+			X = _mm_xor_si128(X, T);
+
+			T = _mm_loadu_si128((__m128i *)(W + j - 13));
+			T = _mm_rotl_epi32(T, 7);
+			X = _mm_xor_si128(X, T);
+			T = _mm_loadu_si128((__m128i *)(W + j - 6));
+			X = _mm_xor_si128(X, T);
+
+			/* W[j + 3] ^= P1(ROL32(W[j + 1], 15)) */
+			R = _mm_shuffle_epi32(X, 0);
+			R = _mm_and_si128(R, M);
+			T = _mm_rotl_epi32(R, 15);
+			T = _mm_xor_si128(T, R);
+			T = _mm_rotl_epi32(T, 9);
+			R = _mm_xor_si128(R, T);
+			R = _mm_rotl_epi32(R, 6);
+			X = _mm_xor_si128(X, R);
+
+			_mm_storeu_si128((__m128i *)(W + j), X);
+		}
+#else
+		for (j = 0; j < 16; j++)
+			W[j] = GETU32(data + j*4);
+
+		for (; j < 68; j++)
+			W[j] = P1(W[j - 16] ^ W[j - 9] ^ ROL32(W[j - 3], 15))
+				^ ROL32(W[j - 13], 7) ^ W[j - 6];
+#endif
+
+
+		j = 0;
+
+#define FULL_UNROLL
+#ifdef FULL_UNROLL
+		R8(A, B, C, D, E, F, G, H, 00);
+		R8(A, B, C, D, E, F, G, H, 00);
+		R8(A, B, C, D, E, F, G, H, 16);
+		R8(A, B, C, D, E, F, G, H, 16);
+		R8(A, B, C, D, E, F, G, H, 16);
+		R8(A, B, C, D, E, F, G, H, 16);
+		R8(A, B, C, D, E, F, G, H, 16);
+		R8(A, B, C, D, E, F, G, H, 16);
+#else
+		for (; j < 16; j++) {
+			SS1 = ROL32((ROL32(A, 12) + E + K(j)), 7);
+			SS2 = SS1 ^ ROL32(A, 12);
+			TT1 = FF00(A, B, C) + D + SS2 + (W[j] ^ W[j + 4]);
+			TT2 = GG00(E, F, G) + H + SS1 + W[j];
+			D = C;
+			C = ROL32(B, 9);
+			B = A;
+			A = TT1;
+			H = G;
+			G = ROL32(F, 19);
+			F = E;
+			E = P0(TT2);
+		}
+
+		for (; j < 64; j++) {
+			SS1 = ROL32((ROL32(A, 12) + E + K(j)), 7);
+			SS2 = SS1 ^ ROL32(A, 12);
+			TT1 = FF16(A, B, C) + D + SS2 + (W[j] ^ W[j + 4]);
+			TT2 = GG16(E, F, G) + H + SS1 + W[j];
+			D = C;
+			C = ROL32(B, 9);
+			B = A;
+			A = TT1;
+			H = G;
+			G = ROL32(F, 19);
+			F = E;
+			E = P0(TT2);
+		}
+#endif
+
+		digest[0] ^= A;
+		digest[1] ^= B;
+		digest[2] ^= C;
+		digest[3] ^= D;
+		digest[4] ^= E;
+		digest[5] ^= F;
+		digest[6] ^= G;
+		digest[7] ^= H;
+
+		data += 64;
+	}
+}
+
+void sm3_compress(uint32_t digest[8], const unsigned char block[64])
+{
+	return sm3_compress_blocks(digest, block, 1);
+}
+
+void sm3(const unsigned char *msg, size_t msglen,
+	unsigned char dgst[SM3_DIGEST_LENGTH])
+{
+	sm3_ctx_t ctx;
+
+	sm3_init(&ctx);
+	sm3_update(&ctx, msg, msglen);
+	sm3_final(&ctx, dgst);
+
+	memset(&ctx, 0, sizeof(sm3_ctx_t));
+}
diff --git a/crypto/sm3/sm3_hmac.c b/crypto/sm3/sm3_hmac.c
new file mode 100644
index 000000000..1c134897b
--- /dev/null
+++ b/crypto/sm3/sm3_hmac.c
@@ -0,0 +1,124 @@
+/* ====================================================================
+ * Copyright (c) 2014 - 2017 The GmSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the GmSSL Project.
+ *    (http://gmssl.org/)"
+ *
+ * 4. The name "GmSSL Project" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    guanzhi1980@gmail.com.
+ *
+ * 5. Products derived from this software may not be called "GmSSL"
+ *    nor may "GmSSL" appear in their names without prior written
+ *    permission of the GmSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the GmSSL Project
+ *    (http://gmssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE GmSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE GmSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ */
+#include 
+#include 
+
+/**
+ * HMAC_k(m) = H((k ^ opad), H((k ^ ipad), m))
+ * pseudo-code:
+ * function hmac(key, message)
+ *	opad = [0x5c * blocksize]
+ *	ipad = [0x36 * blocksize]
+ *	if (length(key) > blocksize) then
+ *		key = hash(key)
+ *	end if
+ *	for i from 0 to length(key) - 1 step 1
+ *		ipad[i] = ipad[i] XOR key[i]
+ *		opad[i] = opad[i] XOR key[i]
+ *	end for
+ *	return hash(opad || hash(ipad || message))
+ * end function
+ */
+
+
+#define IPAD	0x36
+#define OPAD	0x5C
+
+void sm3_hmac_init(sm3_hmac_ctx_t *ctx, const unsigned char *key, size_t key_len)
+{
+	int i;
+
+	if (key_len <= SM3_BLOCK_SIZE) {
+		memcpy(ctx->key, key, key_len);
+		memset(ctx->key + key_len, 0, SM3_BLOCK_SIZE - key_len);
+	} else {
+		sm3_init(&ctx->sm3_ctx);
+		sm3_update(&ctx->sm3_ctx, key, key_len);
+		sm3_final(&ctx->sm3_ctx, ctx->key);
+		memset(ctx->key + SM3_DIGEST_LENGTH, 0,
+			SM3_BLOCK_SIZE - SM3_DIGEST_LENGTH);
+	}
+	for (i = 0; i < SM3_BLOCK_SIZE; i++) {
+		ctx->key[i] ^= IPAD;
+	}
+
+	sm3_init(&ctx->sm3_ctx);
+	sm3_update(&ctx->sm3_ctx, ctx->key, SM3_BLOCK_SIZE);
+}
+
+void sm3_hmac_update(sm3_hmac_ctx_t *ctx,
+	const unsigned char *data, size_t data_len)
+{
+	sm3_update(&ctx->sm3_ctx, data, data_len);
+}
+
+void sm3_hmac_final(sm3_hmac_ctx_t *ctx, unsigned char mac[SM3_HMAC_SIZE])
+{
+	int i;
+	for (i = 0; i < SM3_BLOCK_SIZE; i++) {
+		ctx->key[i] ^= (IPAD ^ OPAD);
+	}
+	sm3_final(&ctx->sm3_ctx, mac);
+	sm3_init(&ctx->sm3_ctx);
+	sm3_update(&ctx->sm3_ctx, ctx->key, SM3_BLOCK_SIZE);
+	sm3_update(&ctx->sm3_ctx, mac, SM3_DIGEST_LENGTH);
+	sm3_final(&ctx->sm3_ctx, mac);
+}
+
+void sm3_hmac(const unsigned char *data, size_t data_len,
+	const unsigned char *key, size_t key_len,
+	unsigned char mac[SM3_HMAC_SIZE])
+{
+	sm3_hmac_ctx_t ctx;
+	sm3_hmac_init(&ctx, key, key_len);
+	sm3_hmac_update(&ctx, data, data_len);
+	sm3_hmac_final(&ctx, mac);
+	memset(&ctx, 0, sizeof(ctx));
+}
diff --git a/crypto/sm3/sm3_mb.c b/crypto/sm3/sm3_mb.c
new file mode 100755
index 000000000..2ef94d15e
--- /dev/null
+++ b/crypto/sm3/sm3_mb.c
@@ -0,0 +1,356 @@
+/* ====================================================================
+ * Copyright (c) 2014 - 2017 The GmSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the GmSSL Project.
+ *    (http://gmssl.org/)"
+ *
+ * 4. The name "GmSSL Project" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    guanzhi1980@gmail.com.
+ *
+ * 5. Products derived from this software may not be called "GmSSL"
+ *    nor may "GmSSL" appear in their names without prior written
+ *    permission of the GmSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the GmSSL Project
+ *    (http://gmssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE GmSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE GmSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+
+#include 
+#include 
+#include "internal/rotate.h"
+#include "internal/byteorder.h"
+#include "modes_lcl.h"
+#include 
+
+
+typedef struct {
+	uint32_t A[8];
+	uint32_t B[8];
+	uint32_t C[8];
+	uint32_t D[8];
+	uint32_t E[8];
+	uint32_t F[8];
+	uint32_t G[8];
+	uint32_t H[8];
+} SM3_AVX2_CTX;
+
+
+void sm3_mb_init(sm3_mb_ctx_t *ctx)
+{
+	int i;
+	memset(ctx, 0, sizeof(*ctx));
+	for (i = 0; i < 8; i++) {
+		ctx->A[i] = 0x7380166F;
+		ctx->B[i] = 0x4914B2B9;
+		ctx->C[i] = 0x172442D7;
+		ctx->D[i] = 0xDA8A0600;
+		ctx->E[i] = 0xA96F30BC;
+		ctx->F[i] = 0x163138AA;
+		ctx->G[i] = 0xE38DEE4D;
+		ctx->H[i] = 0xB0FB0E4E;
+	}
+}
+
+void sm3_update(sm3_ctx_t *ctx, const unsigned char *data, size_t data_len)
+{
+	size_t blocks = data_len / SM3_BLOCK_SIZE;
+
+	if (ctx->num) {
+		unsigned int left = SM3_BLOCK_SIZE - ctx->num;
+		if (data_len < left) {
+			memcpy(ctx->block + ctx->num, data, data_len);
+			ctx->num += data_len;
+			return;
+		} else {
+			memcpy(ctx->block + ctx->num, data, left);
+			sm3_compress(ctx->digest, ctx->block);
+			ctx->nblocks++;
+			data += left;
+			data_len -= left;
+		}
+	}
+
+	sm3_compress_blocks(ctx->digest, data, blocks);
+	ctx->nblocks += blocks;
+	data += SM3_BLOCK_SIZE * blocks;
+	data_len -= SM3_BLOCK_SIZE * blocks;
+
+	ctx->num = data_len;
+	if (data_len) {
+		memcpy(ctx->block, data, data_len);
+	}
+}
+
+void sm3_final(sm3_ctx_t *ctx, unsigned char *digest)
+{
+	int i;
+
+	ctx->block[ctx->num] = 0x80;
+
+	if (ctx->num + 9 <= SM3_BLOCK_SIZE) {
+		memset(ctx->block + ctx->num + 1, 0, SM3_BLOCK_SIZE - ctx->num - 9);
+	} else {
+		memset(ctx->block + ctx->num + 1, 0, SM3_BLOCK_SIZE - ctx->num - 1);
+		sm3_compress(ctx->digest, ctx->block);
+		memset(ctx->block, 0, SM3_BLOCK_SIZE - 8);
+	}
+	PUTU32(ctx->block + 56, ctx->nblocks >> 23);
+	PUTU32(ctx->block + 60, (ctx->nblocks << 9) + (ctx->num << 3));
+
+	sm3_compress(ctx->digest, ctx->block);
+	for (i = 0; i < 8; i++) {
+		PUTU32(digest + i*4, ctx->digest[i]);
+	}
+}
+
+
+#define VPUT(addr, X)	_mm256_store_si256((__m256i *)(addr), _mm256_load_si256((__m256i *)(addr), X))
+# define _mm256_rotl_epi32(X, i)			\
+	_mm256_xor_si256(				\
+		_mm256_slli_epi32(a, i),		\
+		_mm256_srli_epi32(a, 32 - i))
+
+/* P0(X) = X ^ (X <<< 9) ^ (X <<< 17) */
+#define P0(X)						\
+	_mm256_xor_si256(				\
+		_mm256_xor_si256(X,			\
+			_mm256_rotl_epi32(X, 9)),	\
+		_mm256_rotl_epi32(X, 17))
+
+/* P1(X) = X ^ (X <<< 15) ^ (X <<< 23) */
+#define P1(X)						\
+	_mm256_xor_si256(				\
+		_mm256_xor_si256(X,			\
+			_mm256_rotl_epi32(X, 15)),	\
+		_mm256_rotl_epi32(X, 23))
+
+/* FF0(X, Y, Z) = X ^ Y ^ Z */
+#define FF00(X,Y,Z)					\
+	_mm256_xor_si256(X, _mm256_xor_si256(Y, Z))
+
+/* FF16(X, Y, Z) = (X and Y) or (X and Z) or (Y and Z) */
+#define FF16(X,Y,Z)					\
+	_mm256_or_si256(				\
+		_mm256_and_si256(X, Y),			\
+		_mm256_or_si256(			\
+			_mm256_and_si256(X, Z),		\
+			_mm256_and_si256(Y, Z)))
+
+/* GG00(X, Y, Z) = X ^ Y ^ Z */
+#define G00(X,Y,Z)	F00(X,Y,Z)
+
+/* #define GG16(x,y,z)  ((((y)^(z)) & (x)) ^ (z)) */
+#define GG16(X,Y,Z)					\
+	_mm256_xor_si256(Z,				\
+		_mm256_and_si256(X,			\
+			_mm256_xor_si256(Y, Z)))
+
+#define R(A, B, C, D, E, F, G, H, xx)				\
+	SS1 = ROL32((ROL32(A, 12) + E + K[j]), 7);		\
+	A12 = VROL(A, 12);					\
+	SS1 = VADD(A12, E);					\
+	SS1 = VADD(SS1, K(j));					\
+	SS1 = VROL(SS1, 7);					\
+	SS2 = VXOR(SS1, A12);					\
+	TT1 = FF##xx(A, B, C);
+	TT1 = VADD(TT1, D);
+	TT1 = VADD(TT1, SS2);
+	TT1 = VXOR(W(j), W(j+4));
+	TT2 = 
+
+	SS2 = SS1 ^ ROL32(A, 12);				\
+	TT1 = FF##xx(A, B, C) + D + SS2 + (W[j] ^ W[j + 4]);	\
+	TT2 = GG##xx(E, F, G) + H + SS1 + W[j];			\
+	B = ROL32(B, 9);					\
+	H = TT1;						\
+	F = ROL32(F, 19);					\
+	D = P0(TT2);						\
+	j++
+
+
+#define R8(A, B, C, D, E, F, G, H, xx)				\
+	R1(A, B, C, D, E, F, G, H, xx);				\
+	R1(H, A, B, C, D, E, F, G, xx);				\
+	R1(G, H, A, B, C, D, E, F, xx);				\
+	R1(F, G, H, A, B, C, D, E, xx);				\
+	R1(E, F, G, H, A, B, C, D, xx);				\
+	R1(D, E, F, G, H, A, B, C, xx);				\
+	R1(C, D, E, F, G, H, A, B, xx);				\
+	R1(B, C, D, E, F, G, H, A, xx)
+
+
+#define T00 0x79cc4519U
+#define T16 0x7a879d8aU
+
+#define K0	0x79cc4519U
+#define K1	0xf3988a32U
+#define K2	0xe7311465U
+#define K3	0xce6228cbU
+#define K4	0x9cc45197U
+#define K5	0x3988a32fU
+#define K6	0x7311465eU
+#define K7	0xe6228cbcU
+#define K8	0xcc451979U
+#define K9	0x988a32f3U
+#define K10	0x311465e7U
+#define K11	0x6228cbceU
+#define K12	0xc451979cU
+#define K13	0x88a32f39U
+#define K14	0x11465e73U
+#define K15	0x228cbce6U
+#define K16	0x9d8a7a87U
+#define K17	0x3b14f50fU
+#define K18	0x7629ea1eU
+#define K19	0xec53d43cU
+#define K20	0xd8a7a879U
+#define K21	0xb14f50f3U
+#define K22	0x629ea1e7U
+#define K23	0xc53d43ceU
+#define K24	0x8a7a879dU
+#define K25	0x14f50f3bU
+#define K26	0x29ea1e76U
+#define K27	0x53d43cecU
+#define K28	0xa7a879d8U
+#define K29	0x4f50f3b1U
+#define K30	0x9ea1e762U
+#define K31	0x3d43cec5U
+#define K32	0x7a879d8aU
+#define K33	0xf50f3b14U
+#define K34	0xea1e7629U
+#define K35	0xd43cec53U
+#define K36	0xa879d8a7U
+#define K37	0x50f3b14fU
+#define K38	0xa1e7629eU
+#define K39	0x43cec53dU
+#define K40	0x879d8a7aU
+#define K41	0x0f3b14f5U
+#define K42	0x1e7629eaU
+#define K43	0x3cec53d4U
+#define K44	0x79d8a7a8U
+#define K45	0xf3b14f50U
+#define K46	0xe7629ea1U
+#define K47	0xcec53d43U
+#define K48	0x9d8a7a87U
+#define K49	0x3b14f50fU
+#define K50	0x7629ea1eU
+#define K51	0xec53d43cU
+#define K52	0xd8a7a879U
+#define K53	0xb14f50f3U
+#define K54	0x629ea1e7U
+#define K55	0xc53d43ceU
+#define K56	0x8a7a879dU
+#define K57	0x14f50f3bU
+#define K58	0x29ea1e76U
+#define K59	0x53d43cecU
+#define K60	0xa7a879d8U
+#define K61	0x4f50f3b1U
+#define K62	0x9ea1e762U
+#define K63	0x3d43cec5U
+
+
+
+
+
+static void sm3_avx2_compress_blocks(uint32_t digest[8][8],
+	const unsigned char *data, size_t offset[8], size_t blocks)
+{
+	VREG A, B, C, D, E, F, G, H;
+	VREG SS1, SS2, TT1, TT2;
+
+	uint32_t W[68][8];
+	int j;
+
+	while (blocks--) {
+
+		A = _mm256_load_si256((__m256i *)ctx->A);
+		B = _mm256_load_si256((__m256i *)ctx->B);
+		C = _mm256_load_si256((__m256i *)ctx->C);
+		D = _mm256_load_si256((__m256i *)ctx->D);
+		E = _mm256_load_si256((__m256i *)ctx->E);
+		F = _mm256_load_si256((__m256i *)ctx->F);
+		G = _mm256_load_si256((__m256i *)ctx->G);
+		H = _mm256_load_si256((__m256i *)ctx->H);
+
+		for (j = 0; j < 16; j++) {
+			W = _mm256_i32gather_epi32((int *)data, I, 4);
+			W = _mm255_shuffle_epi8(W, vindex_swap);
+			_mm256_store_si256((__m256i *)(w + j * 8), W);
+		}
+
+		for (; j < 68; j++) {
+			PUTV32(W[j],
+				P1(GETV32(W[j - 16]) ^ GETV32(W[j - 9]) ^ ROL32(GETV32(W[j - 3]), 15))
+					^ ROL32(GETV32(W[j - 13]), 7) ^ W[j - 6];
+
+		}
+
+
+		j = 0;
+
+
+
+
+
+
+		VPUT(ctx->A, A);
+		VPUT(ctx->B, B);
+		VPUT(ctx->C, C);
+		VPUT(ctx->D, D);
+		VPUT(ctx->E, E);
+		VPUT(ctx->F, F);
+		VPUT(ctx->G, G);
+		VPUT(ctx->H, H);
+
+		data += 64;
+	}
+}
+
+void sm3_compress(uint32_t digest[8], const unsigned char block[64])
+{
+	return sm3_compress_blocks(digest, block, 1);
+}
+
+void sm3(const unsigned char *msg, size_t msglen,
+	unsigned char dgst[SM3_DIGEST_LENGTH])
+{
+	sm3_ctx_t ctx;
+
+	sm3_init(&ctx);
+	sm3_update(&ctx, msg, msglen);
+	sm3_final(&ctx, dgst);
+
+	memset(&ctx, 0, sizeof(sm3_ctx_t));
+}
+
diff --git a/crypto/sm3/sm3_simd.c b/crypto/sm3/sm3_simd.c
new file mode 100644
index 000000000..643aa8d65
--- /dev/null
+++ b/crypto/sm3/sm3_simd.c
@@ -0,0 +1,221 @@
+#include 
+#include 
+#include 
+#include 
+
+# define GETU32(p)			\
+	((unsigned int)(p)[0] << 24 |	\
+	 (unsigned int)(p)[1] << 16 |	\
+	 (unsigned int)(p)[2] <<  8 |	\
+	 (unsigned int)(p)[3])
+
+# define ROL32(X,n)			\
+	(((X) << (n)) | (((X) & 0xffffffff) >> (32-(n))))
+
+
+# define P1(X)	\
+	((X) ^ ROL32((X), 15) ^ ROL32((X), 23))
+
+# define _mm_rotl_epi32(X, i)	_mm_xor_si128(		\
+	_mm_slli_epi32((X), i), _mm_srli_epi32((X), 32 - i))
+
+
+# define P1_128(X)				\
+	T = _mm_rotl_epi32((X), (23-15));		\
+	T = _mm_xor_si128(T, (X));		\
+	T = _mm_rotl_epi32(T, 15);		\
+	X = _mm_xor_si128((X), T)
+
+/*
+# define GETU128(p)							\
+	_mm_setr_epi32(							\
+		*((unsigned int *)(((unsigned char *)(p)) + 0)),	\
+		*((unsigned int *)(((unsigned char *)(p)) + 4)),	\
+		*((unsigned int *)(((unsigned char *)(p)) + 8)),	\
+		*((unsigned int *)(((unsigned char *)(p)) + 12)))
+*/
+
+# define GETU128(p)			\
+	_mm_loadu_si128((__m128i *)(p))
+
+/*
+ * _mm_lddqu_si128	SSE3	 faster when data crosses a cache line boundary
+ * _mm_load_si128	SSE2	
+ * _mm_loadu_si128	SSE2	 no aligned mem
+ * _mm_loadu_si32	SSE2	 load u32 to lower 32-bit of xmm
+ * _mm_loadl_epi64	SSE2	 load u64 to lower 64-bit of xmm
+ * _mm_loadu_si64	SSE	 load unaligned u64 to lower 64-bit of xmm
+ */
+
+static void print_words(unsigned int W[68])
+{
+	int i;
+	for (i = 0; i < 68; i++) {
+		printf("%08x ", W[i]);
+		if (i % 4 == 3)
+			printf("\n");
+	}
+	printf("\n");
+}
+
+static void expand(unsigned int W[68], const unsigned char data[64])
+{
+	int j;
+	unsigned int X;
+	unsigned int T;
+
+	for (j = 0; j < 16; j++)
+		W[j] = GETU32(data + j*4);
+
+	for (; j < 68; j++) {
+		W[j] = P1(W[j - 16] ^ W[j - 9] ^ ROL32(W[j - 3], 15))
+			^ ROL32(W[j - 13], 7) ^ W[j - 6];
+	}
+}
+
+
+static void print_mm(const char *s, __m128i X) {
+	unsigned int buf[4];
+	int i;
+
+	_mm_store_si128((__m128i *)buf, X);
+
+	printf("%s = ", s);
+	for (i = 0; i < 4; i++) {
+		printf("%08x ", buf[i]);
+	}
+	printf("\n");
+}
+
+static __m128i V;
+static __m128i M;
+static __m128i M2;
+
+static void expand_simd(unsigned int W[68], const unsigned char data[64])
+{
+	__m128i X, T, R;
+	int j;
+	const uint32_t *p = (uint32_t *)data;
+
+	for (j = 0; j < 16; j += sizeof(X)/sizeof(W[0])) {
+		X = _mm_loadu_si128((__m128i *)(data + j * 4));
+		X = _mm_shuffle_epi8(X, V);
+		_mm_storeu_si128((__m128i *)(W + j), X);
+	}
+
+	for (j = 16; j < 68; j += 4) {
+		/* X = (W[j - 3], W[j - 2], W[j - 1], 0) */
+		X = _mm_loadu_si128((__m128i *)(W + j - 3));
+		X = _mm_andnot_si128(M, X);
+
+		X = _mm_rotl_epi32(X, 15);
+		T = _mm_loadu_si128((__m128i *)(W + j - 9));
+		X = _mm_xor_si128(X, T);
+		T = _mm_loadu_si128((__m128i *)(W + j - 16));
+		X = _mm_xor_si128(X, T);
+
+		/* P1() */
+		T = _mm_rotl_epi32(X, (23 - 15));
+		T = _mm_xor_si128(T, X);
+		T = _mm_rotl_epi32(T, 15);
+		X = _mm_xor_si128(X, T);
+
+		T = _mm_loadu_si128((__m128i *)(W + j - 13));
+		T = _mm_rotl_epi32(T, 7);
+		X = _mm_xor_si128(X, T);
+		T = _mm_loadu_si128((__m128i *)(W + j - 6));
+		X = _mm_xor_si128(X, T);
+
+		/* W[j + 3] ^= P1(ROL32(W[j + 1], 15)) */
+		R = _mm_shuffle_epi32(X, 0);
+		R = _mm_and_si128(R, M);
+		T = _mm_rotl_epi32(R, 15);
+		T = _mm_xor_si128(T, R);
+		T = _mm_rotl_epi32(T, 9);
+		R = _mm_xor_si128(R, T);
+		R = _mm_rotl_epi32(R, 6);
+		X = _mm_xor_si128(X, R);
+
+		_mm_storeu_si128((__m128i *)(W + j), X);
+	}
+}
+
+void test()
+{
+	unsigned char block[64] __attribute__((aligned(0x10))) = {0};
+	unsigned int W1[68] __attribute__((aligned(0x10))) = {0};
+	unsigned int W2[68] __attribute__((aligned(0x10))) = {0};
+	int i;
+
+	for (i = 0; i < 64; i++)
+		block[i] = i;
+
+
+	printf("W1 = %08x\n", &W1[0]);
+	printf("W2 = %08x\n", &W2[0]);
+
+
+
+	expand(W1, block);
+
+
+
+
+
+	expand_simd(W2, block);
+
+
+	for (i = 0; i < 68; i++) {
+		printf("%08x ", W1[i]);
+		if (i % 4 == 3) printf("\n");
+	}
+	printf("\n");
+
+
+	for (i = 0; i < 68; i++) {
+		printf("%08x ", W2[i]);
+		if (i % 4 == 3) printf("\n");
+	}
+	printf("\n");
+
+
+	for (i = 0; i < 68; i++) {
+		if (W1[i] != W2[i]) {
+			printf("failed on %d\n" , i);
+			printf("%08x\n", W1[i]);
+			printf("%08x\n", W2[i]);
+		}
+	}
+}
+
+int main(int argc, char **argv)
+{
+	unsigned char block[64] __attribute__((aligned(0x10))) = {0};
+	unsigned int W[68] __attribute__((aligned(0x10))) = {0};
+	int i;
+
+	V = _mm_setr_epi8(3,2,1,0,7,6,5,4,11,10,9,8,15,14,13,12);
+	M = _mm_setr_epi32(0, 0, 0, 0xffffffff);
+
+
+	for (i = 0; i < 64; i++)
+		block[i] = i;
+
+
+	/*
+	for (i = 0; i < 1000000; i++) {
+		expand(W, block);
+	}
+	*/
+
+	/*
+	for (i = 0; i < 1000000; i++) {
+	expand_simd(W, block);
+	}
+	*/
+
+
+	test();
+
+	return 0;
+}
diff --git a/crypto/sm9/build.info b/crypto/sm9/build.info
new file mode 100755
index 000000000..5f79c6ffa
--- /dev/null
+++ b/crypto/sm9/build.info
@@ -0,0 +1,4 @@
+LIBS=../../libcrypto
+SOURCE[../../libcrypto]=sm9_lib.c sm9_err.c sm9_asn1.c sm9_params.c \
+	sm9_setup.c sm9_keygen.c sm9_sign.c sm9_enc.c sm9_exch.c sm9_rate.c \
+	sm9_pmeth.c sm9_ameth.c
diff --git a/crypto/sm9/sm9_ameth.c b/crypto/sm9/sm9_ameth.c
new file mode 100755
index 000000000..c3d0ba37e
--- /dev/null
+++ b/crypto/sm9/sm9_ameth.c
@@ -0,0 +1,577 @@
+/* ====================================================================
+ * Copyright (c) 2015 - 2018 The GmSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the GmSSL Project.
+ *    (http://gmssl.org/)"
+ *
+ * 4. The name "GmSSL Project" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    guanzhi1980@gmail.com.
+ *
+ * 5. Products derived from this software may not be called "GmSSL"
+ *    nor may "GmSSL" appear in their names without prior written
+ *    permission of the GmSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the GmSSL Project
+ *    (http://gmssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE GmSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE GmSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include "internal/cryptlib.h"
+#include "internal/asn1_int.h"
+#include "internal/evp_int.h"
+#include "sm9_lcl.h"
+
+
+static int sm9_master_pub_encode(X509_PUBKEY *pubkey, const EVP_PKEY *pkey)
+{
+	unsigned char *penc = NULL;
+	int penclen;
+
+	if ((penclen = i2d_SM9PublicParameters(pkey->pkey.sm9_master, &penc)) <= 0) {
+		return 0;
+	}
+	OPENSSL_assert(pubkey);
+
+	if (X509_PUBKEY_set0_param(pubkey, OBJ_nid2obj(EVP_PKEY_SM9_MASTER),
+		V_ASN1_NULL, NULL, penc, penclen)) {
+		return 1;
+	}
+
+	OPENSSL_free(penc);
+	return 0;
+}
+
+static int sm9_master_pub_decode(EVP_PKEY *pkey, X509_PUBKEY *pubkey)
+{
+	const unsigned char *cp;
+	int len;
+	SM9PublicParameters *sm9_params = NULL;
+
+	if (!X509_PUBKEY_get0_param(NULL, &cp, &len, NULL, pubkey)) {
+		return 0;
+	}
+	if (!(sm9_params = d2i_SM9PublicParameters(NULL, &cp, len))) {
+		SM9err(SM9_F_SM9_MASTER_PUB_DECODE, ERR_R_SM9_LIB);
+		return 0;
+	}
+
+	EVP_PKEY_assign_SM9PublicParameters(pkey, sm9_params);
+	return 1;
+}
+
+static int sm9_master_pub_cmp(const EVP_PKEY *a, const EVP_PKEY *b)
+{
+	/* FIXME: implement this! */
+	(void)a;
+	(void)b;
+
+	/*
+	if (OBJ_cmp(a->pkey.sm9_master->pairing, b->pkey.sm9_master->pairing) != 0
+		|| OBJ_cmp(a->pkey.sm9_master->scheme, b->pkey.sm9_master->scheme) != 0
+		|| OBJ_cmp(a->pkey.sm9_master->hash1, b->pkey.sm9_master->hash1) != 0
+		|| ASN1_OCTET_STRING_cmp(a->pkey.sm9_master->pointPpub,
+			b->pkey.sm9_master->pointPpub) != 0) {
+		return 0;
+	}
+	*/
+	return 1;
+}
+
+static int do_sm9_master_key_print(BIO *bp, const SM9_MASTER_KEY *x, int off, int priv)
+{
+	int pairing;
+	int scheme;
+	int hash1;
+
+	if (!x) {
+		SM9err(SM9_F_DO_SM9_MASTER_KEY_PRINT, ERR_R_PASSED_NULL_PARAMETER);
+		return 0;
+	}
+
+	if (BIO_printf(bp, "%s: (256 bit)\n", priv ? "Master-Private-Key"
+		: "Master-Public-Key") <= 0)
+		return 0;
+
+	if (!BIO_indent(bp, off, 128))
+		return 0;
+
+	/* pairing */
+	pairing = OBJ_obj2nid(x->pairing);
+	if (!sm9_check_pairing(pairing)) {
+		SM9err(SM9_F_DO_SM9_MASTER_KEY_PRINT, SM9_R_INVALID_PAIRING);
+		return 0;
+	}
+	if (BIO_printf(bp, "pairing: %s\n", OBJ_nid2sn(pairing)) <= 0)
+		return 0;
+
+	/* scheme */
+	scheme = OBJ_obj2nid(x->scheme);
+	if (!sm9_check_scheme(scheme)) {
+		SM9err(SM9_F_DO_SM9_MASTER_KEY_PRINT, SM9_R_INVALID_SCHEME);
+		return 0;
+	}
+	if (BIO_printf(bp, "scheme: %s\n", OBJ_nid2sn(scheme)) <= 0)
+		return 0;
+
+	/* hash1 */
+	hash1 = OBJ_obj2nid(x->hash1);
+	if (!sm9_check_hash1(hash1)) {
+		SM9err(SM9_F_DO_SM9_MASTER_KEY_PRINT, SM9_R_INVALID_HASH1);
+		return 0;
+	}
+	if (BIO_printf(bp, "hash1: %s\n", OBJ_nid2sn(hash1)) <= 0)
+		return 0;
+
+	/* pointPpub */
+	if (BIO_printf(bp, "%*spointPpub:\n", off, "") <= 0)
+		return 0;
+	if (ASN1_buf_print(bp, ASN1_STRING_get0_data(x->pointPpub),
+		ASN1_STRING_length(x->pointPpub), off + 4) == 0)
+		return 0;
+
+	/* masterSecret */
+	if (priv) {
+		unsigned char master[32];
+		if (BIO_printf(bp, "%*smasterSecret:\n", off, "") <= 0)
+			return 0;
+		if (BN_bn2binpad(x->masterSecret, master, sizeof(master)) <= 0) {
+			SM9err(SM9_F_DO_SM9_MASTER_KEY_PRINT, ERR_R_BN_LIB);
+			OPENSSL_cleanse(master, sizeof(master));
+			return 0;
+		}
+		if (ASN1_buf_print(bp, master, sizeof(master), off + 4) == 0) {
+			OPENSSL_cleanse(master, sizeof(master));
+			return 0;
+		}
+		OPENSSL_cleanse(master, sizeof(master));
+	}
+
+	return 1;
+}
+
+static int sm9_master_pub_print(BIO *bp, const EVP_PKEY *pkey, int indent,
+	ASN1_PCTX *ctx)
+{
+	(void)ctx; /* FIXME: how to use ASN1_PCTX? */
+	return do_sm9_master_key_print(bp, pkey->pkey.sm9_master, indent, 0);
+}
+
+static int sm9_master_priv_print(BIO *bp, const EVP_PKEY *pkey, int indent,
+	ASN1_PCTX *ctx)
+{
+	(void)ctx; /* FIXME: how to use ASN1_PCTX? */
+	return do_sm9_master_key_print(bp, pkey->pkey.sm9_master, indent, 1);
+}
+
+static int sm9_master_priv_decode(EVP_PKEY *pkey, const PKCS8_PRIV_KEY_INFO *p8)
+{
+	const unsigned char *p;
+	int pklen;
+	SM9MasterSecret *sm9_master;
+
+	if (!PKCS8_pkey_get0(NULL, &p, &pklen, NULL, p8))
+		return 0;
+	if (!(sm9_master = d2i_SM9MasterSecret(NULL, &p, pklen))) {
+		SM9err(SM9_F_SM9_MASTER_PRIV_DECODE, ERR_R_SM9_LIB);
+		return 0;
+	}
+	EVP_PKEY_assign_SM9MasterSecret(pkey, sm9_master);
+	return 1;
+}
+
+static int sm9_master_priv_encode(PKCS8_PRIV_KEY_INFO *p8, const EVP_PKEY *pkey)
+{
+	unsigned char *rk = NULL;
+	int rklen;
+
+	if ((rklen = i2d_SM9MasterSecret(pkey->pkey.sm9_master, &rk)) <= 0) {
+		SM9err(SM9_F_SM9_MASTER_PRIV_ENCODE, ERR_R_MALLOC_FAILURE);
+		return 0;
+	}
+
+	if (!PKCS8_pkey_set0(p8, OBJ_nid2obj(EVP_PKEY_SM9_MASTER), 0,
+		V_ASN1_NULL, NULL, rk, rklen)) {
+		SM9err(SM9_F_SM9_MASTER_PRIV_ENCODE, ERR_R_MALLOC_FAILURE);
+		return 0;
+	}
+
+	return 1;
+}
+
+static int sm9_pkey_size(const EVP_PKEY *pkey)
+{
+	/* FIXME: wrap a SM9_xxx() */
+	(void)pkey;
+
+	return 32 * 12;
+}
+
+static int sm9_pkey_bits(const EVP_PKEY *pkey)
+{
+	/* FIXME: wrap a SM9_xxx() */
+	(void)pkey;
+
+	return 256 * 12;
+}
+
+static int sm9_pkey_security_bits(const EVP_PKEY *pkey)
+{
+	/* FIXME: wrap a SM9_xxx() */
+	(void)pkey;
+
+	return 256/2;
+}
+
+static void sm9_master_pkey_free(EVP_PKEY *pkey)
+{
+	SM9MasterSecret_free(pkey->pkey.sm9_master);
+}
+
+static int sm9_master_pkey_ctrl(EVP_PKEY *pkey, int op, long arg1, void *arg2)
+{
+	(void)pkey;
+	(void)op;
+	(void)arg1;
+	(void)arg2;
+
+	return -2;
+}
+
+static int sm9_master_old_priv_decode(EVP_PKEY *pkey,
+	const unsigned char **pder, int derlen)
+{
+	SM9_MASTER_KEY *sm9_master;
+	if ((sm9_master = d2i_SM9MasterSecret(NULL, pder, derlen)) == NULL) {
+		SM9err(SM9_F_SM9_MASTER_OLD_PRIV_DECODE, SM9_R_DECODE_ERROR);
+		return 0;
+	}
+	EVP_PKEY_assign_SM9_MASTER(pkey, sm9_master);
+	return 1;
+}
+
+static int sm9_master_old_priv_encode(const EVP_PKEY *pkey, unsigned char **pder)
+{
+	return i2d_SM9MasterSecret(pkey->pkey.sm9_master, pder);
+}
+
+const EVP_PKEY_ASN1_METHOD sm9_master_asn1_meth = {
+	EVP_PKEY_SM9_MASTER,		/* pkey_id */
+	EVP_PKEY_SM9_MASTER,		/* pkey_base_id */
+	0,				/* pkey_flags */
+	"SM9 MASTER",			/* pem_str */
+	"GmSSL SM9 system algorithm",	/* info */
+	sm9_master_pub_decode,		/* pub_decode */
+	sm9_master_pub_encode,		/* pub_encode */
+	sm9_master_pub_cmp,		/* pub_cmp */
+	sm9_master_pub_print,		/* pub_print */
+	sm9_master_priv_decode,		/* priv_decode */
+	sm9_master_priv_encode,		/* priv_encode */
+	sm9_master_priv_print,		/* priv_print */
+	sm9_pkey_size,			/* pkey_size */
+	sm9_pkey_bits,			/* pkey_bits */
+	sm9_pkey_security_bits,		/* pkey_security_bits */
+	NULL,				/* param_decode */
+	NULL,				/* param_encode */
+	NULL,				/* param_missing */
+	NULL,				/* param_copy */
+	NULL,				/* param_cmp */
+	NULL,				/* param_print */
+	NULL,				/* sig_print */
+	sm9_master_pkey_free,		/* pkey_free */
+	sm9_master_pkey_ctrl,		/* pkey_ctrl */
+	sm9_master_old_priv_decode,	/* old_priv_decode */
+	sm9_master_old_priv_encode,	/* old_priv_encode */
+	NULL,				/* item_verify */
+	NULL,				/* item_sign */
+};
+
+static int sm9_pub_encode(X509_PUBKEY *pubkey, const EVP_PKEY *pkey)
+{
+	unsigned char *penc = NULL;
+	int penclen;
+
+	if ((penclen = i2d_SM9PublicKey(pkey->pkey.sm9, &penc)) <= 0) {
+		return 0;
+	}
+	if (X509_PUBKEY_set0_param(pubkey, OBJ_nid2obj(EVP_PKEY_SM9),
+				   V_ASN1_NULL, NULL, penc, penclen)) {
+		return 1;
+	}
+
+	OPENSSL_free(penc);
+	return 0;
+}
+
+static int sm9_pub_decode(EVP_PKEY *pkey, X509_PUBKEY *pubkey)
+{
+	const unsigned char *cp;
+	int len;
+	SM9PublicKey *sm9 = NULL;
+
+	if (!X509_PUBKEY_get0_param(NULL, &cp, &len, NULL, pubkey)) {
+		return 0;
+	}
+	if (!(sm9 = d2i_SM9PublicKey(NULL, &cp, len))) {
+		SM9err(SM9_F_SM9_PUB_DECODE, ERR_R_SM9_LIB);
+		return 0;
+	}
+
+	EVP_PKEY_assign_SM9PublicKey(pkey, sm9);
+	return 1;
+}
+
+static int sm9_pub_cmp(const EVP_PKEY *a, const EVP_PKEY *b)
+{
+	const SM9PublicKey *ax = a->pkey.sm9;
+	const SM9PublicKey *bx = b->pkey.sm9;
+	if (OBJ_cmp(ax->pairing, bx->pairing)
+		|| OBJ_cmp(ax->scheme, bx->scheme)
+		|| OBJ_cmp(ax->hash1, bx->hash1)
+		/* FIXME: decode point then compare to support point compression */
+		|| ASN1_OCTET_STRING_cmp(ax->pointPpub, bx->pointPpub)
+		|| ASN1_OCTET_STRING_cmp(ax->identity, bx->identity)
+		|| ASN1_OCTET_STRING_cmp(ax->publicPoint, bx->publicPoint)) {
+		return 0;
+	}
+	return 1;
+}
+
+static int do_sm9_key_print(BIO *bp, const SM9PrivateKey *x, int off, int priv)
+{
+	int pairing;
+	int scheme;
+	int hash1;
+
+	if (!x) {
+		SM9err(SM9_F_DO_SM9_KEY_PRINT, ERR_R_PASSED_NULL_PARAMETER);
+		return 0;
+	}
+
+	if (BIO_printf(bp, "%s: (256 bit)\n", priv ? "Private-Key" : "Public-Key") <= 0)
+		return 0;
+
+	if (!BIO_indent(bp, off, 128))
+		return 0;
+
+	/* pairing */
+	pairing = OBJ_obj2nid(x->pairing);
+	if (!sm9_check_pairing(pairing)) {
+		SM9err(SM9_F_DO_SM9_KEY_PRINT, SM9_R_INVALID_PAIRING);
+		return 0;
+	}
+	if (BIO_printf(bp, "pairing: %s\n", OBJ_nid2sn(pairing)) <= 0)
+		return 0;
+
+	/* scheme */
+	scheme = OBJ_obj2nid(x->scheme);
+	if (!sm9_check_scheme(scheme)) {
+		SM9err(SM9_F_DO_SM9_KEY_PRINT, SM9_R_INVALID_SCHEME);
+		return 0;
+	}
+	if (BIO_printf(bp, "scheme: %s\n", OBJ_nid2sn(scheme)) <= 0)
+		return 0;
+
+	/* hash1 */
+	hash1 = OBJ_obj2nid(x->hash1);
+	if (!sm9_check_hash1(hash1)) {
+		SM9err(SM9_F_DO_SM9_KEY_PRINT, SM9_R_INVALID_HASH1);
+		return 0;
+	}
+	if (BIO_printf(bp, "hash1: %s\n", OBJ_nid2sn(hash1)) <= 0)
+		return 0;
+
+	/* pointPpub */
+	if (BIO_printf(bp, "%*spointPpub:\n", off, "") <= 0)
+		return 0;
+	if (ASN1_buf_print(bp, ASN1_STRING_get0_data(x->pointPpub),
+		ASN1_STRING_length(x->pointPpub), off + 4) == 0)
+		return 0;
+
+	/* identity */
+	if (BIO_printf(bp, "%*sidentity:\n", off, "") <= 0)
+		return 0;
+	if (ASN1_buf_print(bp, ASN1_STRING_get0_data(x->identity),
+		ASN1_STRING_length(x->identity), off + 4) == 0)
+		return 0;
+
+	/* publicPoint */
+	if (BIO_printf(bp, "%*spublicPoint:\n", off, "") <= 0)
+		return 0;
+	if (ASN1_buf_print(bp, ASN1_STRING_get0_data(x->publicPoint),
+		ASN1_STRING_length(x->publicPoint), off + 4) == 0)
+		return 0;
+
+	/* privatePoint */
+	if (priv) {
+		if (BIO_printf(bp, "%*sprivatePoint:\n", off, "") <= 0)
+			return 0;
+		if (ASN1_buf_print(bp, ASN1_STRING_get0_data(x->privatePoint),
+			ASN1_STRING_length(x->privatePoint), off + 4) == 0)
+			return 0;
+	}
+
+	return 1;
+}
+
+static int sm9_pub_print(BIO *bp, const EVP_PKEY *pkey, int indent,
+	ASN1_PCTX *ctx)
+{
+	(void)ctx; /* FIXME: how to use ASN1_PCTX? */
+	return do_sm9_key_print(bp, pkey->pkey.sm9, indent, 0);
+}
+
+static int sm9_priv_print(BIO *bp, const EVP_PKEY *pkey, int indent,
+	ASN1_PCTX *ctx)
+{
+	(void)ctx; /* FIXME: how to use ASN1_PCTX? */
+	return do_sm9_key_print(bp, pkey->pkey.sm9, indent, 1);
+}
+
+static int sm9_priv_decode(EVP_PKEY *pkey, const PKCS8_PRIV_KEY_INFO *p8)
+{
+	const unsigned char *p;
+	int pklen;
+	SM9PrivateKey *sm9;
+
+	if (!PKCS8_pkey_get0(NULL, &p, &pklen, NULL, p8))
+		return 0;
+	if (!(sm9 = d2i_SM9PrivateKey(NULL, &p, pklen))) {
+		SM9err(SM9_F_SM9_PRIV_DECODE, ERR_R_SM9_LIB);
+		return 0;
+	}
+	EVP_PKEY_assign_SM9PrivateKey(pkey, sm9);
+	return 1;
+}
+
+static int sm9_priv_encode(PKCS8_PRIV_KEY_INFO *p8, const EVP_PKEY *pkey)
+{
+	unsigned char *rk = NULL;
+	int rklen;
+
+	if ((rklen = i2d_SM9PrivateKey(pkey->pkey.sm9, &rk)) <= 0) {
+		SM9err(SM9_F_SM9_PRIV_ENCODE, ERR_R_MALLOC_FAILURE);
+		return 0;
+	}
+
+	if (!PKCS8_pkey_set0(p8, OBJ_nid2obj(EVP_PKEY_SM9), 0,
+			     V_ASN1_NULL, NULL, rk, rklen)) {
+		SM9err(SM9_F_SM9_PRIV_ENCODE, ERR_R_MALLOC_FAILURE);
+		return 0;
+	}
+
+	return 1;
+}
+
+static void sm9_pkey_free(EVP_PKEY *pkey)
+{
+	SM9PrivateKey_free(pkey->pkey.sm9);
+}
+
+static int sm9_pkey_ctrl(EVP_PKEY *pkey, int op, long arg1, void *arg2)
+{
+	/* FIXME */
+	(void)pkey;
+	(void)op;
+	(void)arg1;
+	(void)arg2;
+
+	return -2;
+}
+
+static int sm9_old_priv_decode(EVP_PKEY *pkey,
+	const unsigned char **pder, int derlen)
+{
+	SM9PrivateKey *sm9;
+	if ((sm9 = d2i_SM9PrivateKey(NULL, pder, derlen)) == NULL) {
+		SM9err(SM9_F_SM9_OLD_PRIV_DECODE, SM9_R_DECODE_ERROR);
+		return 0;
+	}
+	EVP_PKEY_assign_SM9PrivateKey(pkey, sm9);
+	return 1;
+}
+
+static int sm9_old_priv_encode(const EVP_PKEY *pkey, unsigned char **pder)
+{
+	return i2d_SM9PrivateKey(pkey->pkey.sm9, pder);
+}
+
+const EVP_PKEY_ASN1_METHOD sm9_asn1_meth = {
+	EVP_PKEY_SM9,		/* pkey_id */
+	EVP_PKEY_SM9,		/* pkey_base_id */
+	0,			/* pkey_flags */
+	"SM9",			/* pem_str */
+	"GmSSL SM9 algorithm",	/* info */
+	sm9_pub_decode,		/* pub_decode */
+	sm9_pub_encode,		/* pub_encode */
+	sm9_pub_cmp,		/* pub_cmp */
+	sm9_pub_print,		/* pub_print */
+	sm9_priv_decode,	/* priv_decode */
+	sm9_priv_encode,	/* priv_encode */
+	sm9_priv_print,		/* priv_print */
+	sm9_pkey_size,		/* pkey_size */
+	sm9_pkey_bits,		/* pkey_bits */
+	sm9_pkey_security_bits,	/* pkey_security_bits */
+	NULL,			/* param_decode */
+	NULL,			/* param_encode */
+	NULL,			/* param_missing */
+	NULL,			/* param_copy */
+	NULL,			/* param_cmp */
+	NULL,			/* param_print */
+	NULL,			/* sig_print */
+	sm9_pkey_free,		/* pkey_free */
+	sm9_pkey_ctrl,		/* pkey_ctrl */
+	sm9_old_priv_decode,	/* old_priv_decode */
+	sm9_old_priv_encode,	/* old_priv_encode */
+	NULL,			/* item_verify */
+	NULL,			/* item_sign */
+};
+
+int SM9_MASTER_KEY_print(BIO *bp, const SM9_MASTER_KEY *x, int off)
+{
+	int priv = (x->masterSecret != NULL);
+	return do_sm9_master_key_print(bp, x, off, priv);
+}
+
+int SM9_KEY_print(BIO *bp, const SM9_KEY *x, int off)
+{
+	int priv = (x->privatePoint != NULL);
+	return do_sm9_key_print(bp, x, off, priv);
+}
diff --git a/crypto/sm9/sm9_asn1.c b/crypto/sm9/sm9_asn1.c
new file mode 100755
index 000000000..181c794c2
--- /dev/null
+++ b/crypto/sm9/sm9_asn1.c
@@ -0,0 +1,326 @@
+/* ====================================================================
+ * Copyright (c) 2016 The GmSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the GmSSL Project.
+ *    (http://gmssl.org/)"
+ *
+ * 4. The name "GmSSL Project" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    guanzhi1980@gmail.com.
+ *
+ * 5. Products derived from this software may not be called "GmSSL"
+ *    nor may "GmSSL" appear in their names without prior written
+ *    permission of the GmSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the GmSSL Project
+ *    (http://gmssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE GmSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE GmSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include "sm9_lcl.h"
+
+static int sm9_master_key_cb(int operation, ASN1_VALUE **pval,
+	const ASN1_ITEM *it, void *exarg)
+{
+	/* FIXME: usage of it and exarg? */
+	(void)it;
+	(void)exarg;
+
+	if (operation == ASN1_OP_NEW_PRE) {
+		*pval = (ASN1_VALUE *)SM9_MASTER_KEY_new();
+		if (*pval)
+			return 2;
+		return 0;
+	} else if (operation == ASN1_OP_FREE_PRE) {
+		SM9_MASTER_KEY_free((SM9_MASTER_KEY *)*pval);
+		*pval = NULL;
+		return 2;
+	}
+	return 1;
+}
+
+ASN1_SEQUENCE_cb(SM9MasterSecret, sm9_master_key_cb) = {
+	ASN1_SIMPLE(SM9_MASTER_KEY, pairing, ASN1_OBJECT),
+	ASN1_SIMPLE(SM9_MASTER_KEY, scheme, ASN1_OBJECT),
+	ASN1_SIMPLE(SM9_MASTER_KEY, hash1, ASN1_OBJECT),
+	ASN1_SIMPLE(SM9_MASTER_KEY, pointPpub, ASN1_OCTET_STRING),
+	ASN1_SIMPLE(SM9_MASTER_KEY, masterSecret, BIGNUM)
+} ASN1_SEQUENCE_END_cb(SM9_MASTER_KEY, SM9MasterSecret)
+IMPLEMENT_ASN1_ENCODE_FUNCTIONS_const_fname(SM9_MASTER_KEY,SM9MasterSecret,SM9MasterSecret)
+
+ASN1_SEQUENCE_cb(SM9PublicParameters, sm9_master_key_cb) = {
+	ASN1_SIMPLE(SM9_MASTER_KEY, pairing, ASN1_OBJECT),
+	ASN1_SIMPLE(SM9_MASTER_KEY, scheme, ASN1_OBJECT),
+	ASN1_SIMPLE(SM9_MASTER_KEY, hash1, ASN1_OBJECT),
+	ASN1_SIMPLE(SM9_MASTER_KEY, pointPpub, ASN1_OCTET_STRING)
+} ASN1_SEQUENCE_END_cb(SM9_MASTER_KEY, SM9PublicParameters)
+IMPLEMENT_ASN1_ENCODE_FUNCTIONS_const_fname(SM9_MASTER_KEY,SM9PublicParameters,SM9PublicParameters)
+
+
+static int sm9_key_cb(int operation, ASN1_VALUE **pval,
+	const ASN1_ITEM *it, void *exarg)
+{
+	/* FIXME: usage of it and exarg? */
+	(void)it;
+	(void)exarg;
+
+	if (operation == ASN1_OP_NEW_PRE) {
+		*pval = (ASN1_VALUE *)SM9_KEY_new();
+		if (*pval)
+			return 2;
+		return 0;
+	} else if (operation == ASN1_OP_FREE_PRE) {
+		SM9_KEY_free((SM9_KEY *)*pval);
+		*pval = NULL;
+		return 2;
+	}
+	return 1;
+}
+
+ASN1_SEQUENCE_cb(SM9PrivateKey, sm9_key_cb) = {
+	ASN1_SIMPLE(SM9_KEY, pairing, ASN1_OBJECT),
+	ASN1_SIMPLE(SM9_KEY, scheme, ASN1_OBJECT),
+	ASN1_SIMPLE(SM9_KEY, hash1, ASN1_OBJECT),
+	ASN1_SIMPLE(SM9_KEY, pointPpub, ASN1_OCTET_STRING),
+	ASN1_SIMPLE(SM9_KEY, identity, ASN1_OCTET_STRING),
+	ASN1_SIMPLE(SM9_KEY, publicPoint, ASN1_OCTET_STRING),
+	ASN1_SIMPLE(SM9_KEY, privatePoint, ASN1_OCTET_STRING)
+} ASN1_SEQUENCE_END_cb(SM9_KEY, SM9PrivateKey)
+IMPLEMENT_ASN1_ENCODE_FUNCTIONS_const_fname(SM9_KEY,SM9PrivateKey,SM9PrivateKey)
+
+ASN1_SEQUENCE_cb(SM9PublicKey, sm9_key_cb) = {
+	ASN1_SIMPLE(SM9_KEY, pairing, ASN1_OBJECT),
+	ASN1_SIMPLE(SM9_KEY, scheme, ASN1_OBJECT),
+	ASN1_SIMPLE(SM9_KEY, hash1, ASN1_OBJECT),
+	ASN1_SIMPLE(SM9_KEY, pointPpub, ASN1_OCTET_STRING),
+	ASN1_SIMPLE(SM9_KEY, identity, ASN1_OCTET_STRING),
+	ASN1_SIMPLE(SM9_KEY, publicPoint, ASN1_OCTET_STRING)
+} ASN1_SEQUENCE_END_cb(SM9_KEY, SM9PublicKey)
+IMPLEMENT_ASN1_ENCODE_FUNCTIONS_const_fname(SM9_KEY,SM9PublicKey,SM9PublicKey)
+
+ASN1_SEQUENCE(SM9Ciphertext) = {
+	ASN1_SIMPLE(SM9Ciphertext, pointC1, ASN1_OCTET_STRING),
+	ASN1_SIMPLE(SM9Ciphertext, c2, ASN1_OCTET_STRING),
+	ASN1_SIMPLE(SM9Ciphertext, c3, ASN1_OCTET_STRING)
+} ASN1_SEQUENCE_END(SM9Ciphertext)
+IMPLEMENT_ASN1_FUNCTIONS(SM9Ciphertext)
+IMPLEMENT_ASN1_DUP_FUNCTION(SM9Ciphertext)
+
+ASN1_SEQUENCE(SM9Signature) = {
+	ASN1_SIMPLE(SM9Signature, h, BIGNUM),
+	ASN1_SIMPLE(SM9Signature, pointS, ASN1_OCTET_STRING)
+} ASN1_SEQUENCE_END(SM9Signature)
+IMPLEMENT_ASN1_FUNCTIONS(SM9Signature)
+IMPLEMENT_ASN1_DUP_FUNCTION(SM9Signature)
+
+int SM9PublicKey_gmtls_encode(SM9PublicKey *pk, unsigned char key[1024])
+{
+	/* FIMXE: implement this */
+	(void)pk;
+	(void)key;
+
+	return 0;
+}
+
+int i2d_SM9MasterSecret_bio(BIO *bp, SM9MasterSecret *a)
+{
+	return ASN1_item_i2d_bio(ASN1_ITEM_rptr(SM9MasterSecret), bp, a);
+}
+
+SM9MasterSecret *d2i_SM9MasterSecret_bio(BIO *bp, SM9MasterSecret **a)
+{
+	return ASN1_item_d2i_bio(ASN1_ITEM_rptr(SM9MasterSecret), bp, a);
+}
+
+int i2d_SM9PublicParameters_bio(BIO *bp, SM9PublicParameters *a)
+{
+	return ASN1_item_i2d_bio(ASN1_ITEM_rptr(SM9PublicParameters), bp, a);
+}
+
+SM9PublicParameters *d2i_SM9PublicParameters_bio(BIO *bp, SM9PublicParameters **a)
+{
+	return ASN1_item_d2i_bio(ASN1_ITEM_rptr(SM9PublicParameters), bp, a);
+}
+
+int i2d_SM9PrivateKey_bio(BIO *bp, SM9PrivateKey *a)
+{
+	return ASN1_item_i2d_bio(ASN1_ITEM_rptr(SM9PrivateKey), bp, a);
+}
+
+SM9PrivateKey *d2i_SM9PrivateKey_bio(BIO *bp, SM9PrivateKey **a)
+{
+	return ASN1_item_d2i_bio(ASN1_ITEM_rptr(SM9PrivateKey), bp, a);
+}
+
+int i2d_SM9Signature_bio(BIO *bp, SM9Signature *a)
+{
+	return ASN1_item_i2d_bio(ASN1_ITEM_rptr(SM9Signature), bp, a);
+}
+
+SM9Signature *d2i_SM9Signature_bio(BIO *bp, SM9Signature **a)
+{
+	return ASN1_item_d2i_bio(ASN1_ITEM_rptr(SM9Signature), bp, a);
+}
+
+int i2d_SM9Ciphertext_bio(BIO *bp, SM9Ciphertext *a)
+{
+	return ASN1_item_i2d_bio(ASN1_ITEM_rptr(SM9Ciphertext), bp, a);
+}
+
+SM9Ciphertext *d2i_SM9Ciphertext_bio(BIO *bp, SM9Ciphertext **a)
+{
+	return ASN1_item_d2i_bio(ASN1_ITEM_rptr(SM9Ciphertext), bp, a);
+}
+
+
+#ifndef OPENSSL_NO_STDIO
+SM9MasterSecret *d2i_SM9MasterSecret_fp(FILE *fp, SM9MasterSecret **msk)
+{
+	return ASN1_item_d2i_fp(ASN1_ITEM_rptr(SM9MasterSecret), fp, msk);
+}
+
+int i2d_SM9MasterSecret_fp(FILE *fp, SM9MasterSecret *msk)
+{
+	return ASN1_item_i2d_fp(ASN1_ITEM_rptr(SM9MasterSecret), fp, msk);
+}
+
+SM9PublicParameters *d2i_SM9PublicParameters_fp(FILE *fp, SM9PublicParameters **mpk)
+{
+	return ASN1_item_d2i_fp(ASN1_ITEM_rptr(SM9PublicParameters), fp, mpk);
+}
+
+int i2d_SM9PublicParameters_fp(FILE *fp, SM9PublicParameters *mpk)
+{
+	return ASN1_item_i2d_fp(ASN1_ITEM_rptr(SM9PublicParameters), fp, mpk);
+}
+
+SM9PrivateKey *d2i_SM9PrivateKey_fp(FILE *fp, SM9PrivateKey **sk)
+{
+	return ASN1_item_d2i_fp(ASN1_ITEM_rptr(SM9PrivateKey), fp, sk);
+}
+
+int i2d_SM9PrivateKey_fp(FILE *fp, SM9PrivateKey *sk)
+{
+	return ASN1_item_i2d_fp(ASN1_ITEM_rptr(SM9PrivateKey), fp, sk);
+}
+
+SM9Signature *d2i_SM9Signature_fp(FILE *fp, SM9Signature **sig)
+{
+	return ASN1_item_d2i_fp(ASN1_ITEM_rptr(SM9Signature), fp, sig);
+}
+
+int i2d_SM9Signature_fp(FILE *fp, SM9Signature *sig)
+{
+	return ASN1_item_i2d_fp(ASN1_ITEM_rptr(SM9Signature), fp, sig);
+}
+
+SM9Ciphertext *d2i_SM9Ciphertext_fp(FILE *fp, SM9Ciphertext **c)
+{
+	return ASN1_item_d2i_fp(ASN1_ITEM_rptr(SM9Ciphertext), fp, c);
+}
+
+int i2d_SM9Ciphertext_fp(FILE *fp, SM9Ciphertext *c)
+{
+	return ASN1_item_i2d_fp(ASN1_ITEM_rptr(SM9Ciphertext), fp, c);
+}
+#endif
+
+int SM9_signature_size(const SM9_MASTER_KEY *params)
+{
+	if (params) {
+		int ret;
+		ASN1_INTEGER h;
+		ASN1_OCTET_STRING s;
+		unsigned char buf[4] = {0xff};
+		int len = 0;
+
+		/* ASN1_INTEGER h convert from hash */
+		h.length = SM3_DIGEST_LENGTH;
+		h.data = buf;
+		h.type = V_ASN1_INTEGER;
+		len += i2d_ASN1_INTEGER(&h, NULL);
+
+		/* ASN1_OCTET_STRING pointS over E'(F_p^2) */
+		s.length = 129;
+		s.data = buf;
+		s.type = V_ASN1_OCTET_STRING;
+		len += i2d_ASN1_OCTET_STRING(&s, NULL);
+
+		ret = ASN1_object_size(1, len, V_ASN1_SEQUENCE);
+		return ret;
+	} else {
+		return 170;
+	}
+}
+
+int SM9_ciphertext_size(const SM9_MASTER_KEY *params, size_t inlen)
+{
+	int ret;
+	ASN1_OCTET_STRING s;
+	s.type = V_ASN1_OCTET_STRING;
+	s.data = NULL;
+	int len = 0;
+
+	if (inlen > SM9_MAX_PLAINTEXT_LENGTH) {
+		SM9err(SM9_F_SM9_CIPHERTEXT_SIZE, SM9_R_PLAINTEXT_TOO_LONG);
+		return 0;
+	}
+
+	if (params) {
+		/* ASN1_OCTET_STRING pointC1 over E(F_p) */
+		s.length = 65;
+		len += i2d_ASN1_OCTET_STRING(&s, NULL);
+
+		/* ASN1_OCTET_STRING c3 SM3-MAC */
+		s.length = SM3_DIGEST_LENGTH;
+		len += i2d_ASN1_OCTET_STRING(&s, NULL);
+	} else {
+		/* when no params given, if use point compression is unknown,
+		 * so the maximum uncompressed point length is used */
+		len += 101;
+	}
+
+	/* ASN1_OCTET_STRING c2 ciphertext */
+	s.length = inlen;
+	len += i2d_ASN1_OCTET_STRING(&s, NULL);
+
+	ret = ASN1_object_size(1, len, V_ASN1_SEQUENCE);
+	return ret;
+}
diff --git a/crypto/sm9/sm9_bn.c b/crypto/sm9/sm9_bn.c
new file mode 100644
index 000000000..c25dfefa8
--- /dev/null
+++ b/crypto/sm9/sm9_bn.c
@@ -0,0 +1,6 @@
+#include 
+#include 
+#include 
+
+
+
diff --git a/crypto/sm9/sm9_enc.c b/crypto/sm9/sm9_enc.c
new file mode 100755
index 000000000..925fefd69
--- /dev/null
+++ b/crypto/sm9/sm9_enc.c
@@ -0,0 +1,518 @@
+/* ====================================================================
+ * Copyright (c) 2016 - 2018 The GmSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the GmSSL Project.
+ *    (http://gmssl.org/)"
+ *
+ * 4. The name "GmSSL Project" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    guanzhi1980@gmail.com.
+ *
+ * 5. Products derived from this software may not be called "GmSSL"
+ *    nor may "GmSSL" appear in their names without prior written
+ *    permission of the GmSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the GmSSL Project
+ *    (http://gmssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE GmSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE GmSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include "sm9_lcl.h"
+
+
+int SM9_unwrap_key(int type,
+	unsigned char *key, size_t keylen,
+	const unsigned char *enced_key, size_t enced_len,
+	SM9PrivateKey *sk)
+{
+	int ret = 0;
+	EC_GROUP *group = NULL;
+	EC_POINT *C = NULL;
+	EVP_MD_CTX *md_ctx = NULL;
+	BN_CTX *bn_ctx = NULL;
+	point_t de;
+	fp12_t w;
+	const BIGNUM *p = SM9_get0_prime();
+	const EVP_MD *kdf_md;
+	unsigned char wbuf[384];
+	unsigned char *out = key;
+	size_t outlen = keylen;
+	unsigned char counter[4] = {0, 0, 0, 1};
+	unsigned char dgst[64];
+	unsigned int len;
+
+	switch (type) {
+	case NID_sm9kdf_with_sm3:
+		kdf_md = EVP_sm3();
+		break;
+	case NID_sm9kdf_with_sha256:
+		kdf_md = EVP_sha256();
+		break;
+	default:
+		return 0;
+	}
+
+	/* malloc */
+	if (!(group = EC_GROUP_new_by_curve_name(NID_sm9bn256v1))
+		|| !(C = EC_POINT_new(group))
+		|| !(md_ctx = EVP_MD_CTX_new())
+		|| !(bn_ctx = BN_CTX_new())) {
+		SM9err(SM9_F_SM9_UNWRAP_KEY, ERR_R_MALLOC_FAILURE);
+		goto end;
+	}
+	BN_CTX_start(bn_ctx);
+	if (!point_init(&de, bn_ctx) || !fp12_init(w, bn_ctx)) {
+		SM9err(SM9_F_SM9_UNWRAP_KEY, ERR_R_MALLOC_FAILURE);
+		goto end;
+	}
+
+	/* parse C on E(F_p) */
+	if (!EC_POINT_oct2point(group, C, enced_key, enced_len, bn_ctx)) {
+		SM9err(SM9_F_SM9_UNWRAP_KEY, ERR_R_MALLOC_FAILURE);
+		goto end;
+	}
+
+	/* parse de on E'(E_p^2) */
+	if (!point_from_octets(&de, ASN1_STRING_get0_data(sk->privatePoint), p, bn_ctx)) {
+		SM9err(SM9_F_SM9_UNWRAP_KEY, ERR_R_MALLOC_FAILURE);
+		goto end;
+	}
+
+	/* w = e(C, de) */
+	if (!rate_pairing(w, &de, C, bn_ctx)) {
+		SM9err(SM9_F_SM9_UNWRAP_KEY, ERR_R_MALLOC_FAILURE);
+		goto end;
+	}
+	if (!fp12_to_bin(w, wbuf)) {
+		SM9err(SM9_F_SM9_UNWRAP_KEY, ERR_R_MALLOC_FAILURE);
+		goto end;
+	}
+
+	/* K = KDF(C||w||ID_B, klen) */
+	while (outlen > 0) {
+		if (!EVP_DigestInit_ex(md_ctx, kdf_md, NULL)
+			|| !EVP_DigestUpdate(md_ctx, enced_key + 1, enced_len - 1)
+			|| !EVP_DigestUpdate(md_ctx, wbuf, sizeof(wbuf))
+			|| !EVP_DigestUpdate(md_ctx, ASN1_STRING_get0_data(sk->identity), ASN1_STRING_length(sk->identity))
+			|| !EVP_DigestUpdate(md_ctx, counter, sizeof(counter))
+			|| !EVP_DigestFinal_ex(md_ctx, dgst, &len)) {
+			SM9err(SM9_F_SM9_UNWRAP_KEY, ERR_R_EVP_LIB);
+			goto end;
+		}
+
+		if (len > outlen)
+			len = outlen;
+		memcpy(out, dgst, len);
+
+		out += len;
+		outlen -= len;
+		counter[3]++;
+	}
+
+	ret = 1;
+
+end:
+	EC_GROUP_free(group);
+	EC_POINT_free(C);
+	EVP_MD_CTX_free(md_ctx);
+	fp12_cleanup(w);
+	point_cleanup(&de);
+	if (bn_ctx) {
+		BN_CTX_end(bn_ctx);
+	}
+	BN_CTX_free(bn_ctx);
+	return ret;
+}
+
+int SM9_wrap_key(int type, /* NID_sm9kdf_with_sm3 */
+	unsigned char *key, size_t keylen,
+	unsigned char *enced_key, size_t *enced_len,
+	SM9PublicParameters *mpk, const char *id, size_t idlen)
+{
+	int ret = 0;
+	EC_GROUP *group = NULL;
+	EC_POINT *Ppube = NULL;
+	EC_POINT *C = NULL;
+	EVP_MD_CTX *md_ctx = NULL;
+	BN_CTX *bn_ctx = NULL;
+	BIGNUM *r = NULL;
+	BIGNUM *h = NULL;
+	fp12_t w;
+	const EVP_MD *kdf_md;
+	const EVP_MD *hash1_md;
+	const BIGNUM *p = SM9_get0_prime();
+	const BIGNUM *n = SM9_get0_order();
+	unsigned char cbuf[65];
+	unsigned char wbuf[384];
+	unsigned char dgst[64];
+	int all;
+
+	switch (type) {
+	case NID_sm9kdf_with_sm3:
+		kdf_md = EVP_sm3();
+		break;
+	case NID_sm9kdf_with_sha256:
+		kdf_md = EVP_sha256();
+		break;
+	default:
+		SM9err(SM9_F_SM9_WRAP_KEY, SM9_R_INVALID_DIGEST_TYPE);
+		return 0;
+	}
+
+	if (keylen > (size_t)EVP_MD_size(kdf_md) * 255) {
+		SM9err(SM9_F_SM9_WRAP_KEY, SM9_R_INVALID_KEM_KEY_LENGTH);
+		return 0;
+	}
+
+	if (!(group = EC_GROUP_new_by_curve_name(NID_sm9bn256v1))
+		|| !(Ppube = EC_POINT_new(group))
+		|| !(C = EC_POINT_new(group))
+		|| !(md_ctx = EVP_MD_CTX_new())
+		|| !(bn_ctx = BN_CTX_new())) {
+		SM9err(SM9_F_SM9_WRAP_KEY, ERR_R_MALLOC_FAILURE);
+		goto end;
+	}
+	BN_CTX_start(bn_ctx);
+	if (!(r = BN_CTX_get(bn_ctx)) || !fp12_init(w, bn_ctx)) {
+		SM9err(SM9_F_SM9_WRAP_KEY, ERR_R_MALLOC_FAILURE);
+		goto end;
+	}
+
+	/* parse Ppube */
+	if (!EC_POINT_oct2point(group, Ppube, ASN1_STRING_get0_data(mpk->pointPpub),
+		ASN1_STRING_length(mpk->pointPpub), bn_ctx)) {
+		SM9err(SM9_F_SM9_WRAP_KEY, SM9_R_INVALID_POINTPPUB);
+		goto end;
+	}
+
+	/* g = e(Ppube, P2) */
+	if (!rate_pairing(w, NULL, Ppube, bn_ctx)) {
+		SM9err(SM9_F_SM9_WRAP_KEY, SM9_R_RATE_PAIRING_ERROR);
+		goto end;
+	}
+
+	switch (OBJ_obj2nid(mpk->hash1)) {
+	case NID_sm9hash1_with_sm3:
+		hash1_md = EVP_sm3();
+		break;
+	case NID_sm9hash1_with_sha256:
+		hash1_md = EVP_sha256();
+		break;
+	default:
+		SM9err(SM9_F_SM9_WRAP_KEY, ERR_R_SM9_LIB);
+		goto end;
+	}
+
+	/* parse Q_B = H1(ID_B||hid) * P1 + Ppube */
+	// we should check mpk->hash1
+	if (!SM9_hash1(hash1_md, &h, id, idlen, SM9_HID_ENC, n, bn_ctx)
+		|| !EC_POINT_mul(group, C, h, NULL, NULL, bn_ctx)
+		|| !EC_POINT_add(group, C, C, Ppube, bn_ctx)) {
+		ERR_print_errors_fp(stderr);
+		SM9err(SM9_F_SM9_WRAP_KEY, ERR_R_EC_LIB);
+		goto end;
+	}
+
+	do {
+		unsigned char *out = key;
+		size_t outlen = keylen;
+		unsigned char counter[4] = {0, 0, 0, 1};
+		unsigned int len;
+
+		/* r = rand([1, n-1]) */
+		do {
+			if (!BN_rand_range(r, n)) {
+				goto end;
+			}
+		} while (BN_is_zero(r));
+
+		/* C = r * Q_B */
+		if (!EC_POINT_mul(group, C, NULL, C, r, bn_ctx)
+			|| EC_POINT_point2oct(group, C, POINT_CONVERSION_UNCOMPRESSED,
+				cbuf, sizeof(cbuf), bn_ctx) != sizeof(cbuf)) {
+			SM9err(SM9_F_SM9_WRAP_KEY, ERR_R_EC_LIB);
+			goto end;
+		}
+
+		/* w = g^r */
+		if (!fp12_pow(w, w, r, p, bn_ctx) || !fp12_to_bin(w, wbuf)) {
+			SM9err(SM9_F_SM9_WRAP_KEY, SM9_R_EXTENSION_FIELD_ERROR);
+			goto end;
+		}
+
+		/* K = KDF(C||w||ID_B, klen) */
+		while (outlen > 0) {
+			if (!EVP_DigestInit_ex(md_ctx, kdf_md, NULL)
+				|| !EVP_DigestUpdate(md_ctx, cbuf + 1, sizeof(cbuf) - 1)
+				|| !EVP_DigestUpdate(md_ctx, wbuf, sizeof(wbuf))
+				|| !EVP_DigestUpdate(md_ctx, id, idlen)
+				|| !EVP_DigestUpdate(md_ctx, counter, sizeof(counter))
+				|| !EVP_DigestFinal_ex(md_ctx, dgst, &len)) {
+				SM9err(SM9_F_SM9_WRAP_KEY, ERR_R_EVP_LIB);
+				goto end;
+			}
+
+			if (len > outlen)
+				len = outlen;
+			memcpy(out, dgst, len);
+
+			out += len;
+			outlen -= len;
+			counter[3]++;
+		}
+
+		all = 0;
+		for (len = 0; len < keylen; len++) {
+			all |= key[len];
+		}
+
+	} while (all == 0);
+
+	memcpy(enced_key, cbuf, sizeof(cbuf));
+	*enced_len = sizeof(cbuf);
+
+
+	ret = 1;
+
+end:
+	EC_GROUP_free(group);
+	EC_POINT_free(Ppube);
+	EC_POINT_free(C);
+	EVP_MD_CTX_free(md_ctx);
+	if (bn_ctx) {
+		BN_CTX_end(bn_ctx);
+	}
+	BN_free(r);
+	BN_free(h);
+	BN_CTX_free(bn_ctx);
+	OPENSSL_cleanse(cbuf, sizeof(cbuf));
+	OPENSSL_cleanse(wbuf, sizeof(wbuf));
+	OPENSSL_cleanse(dgst, sizeof(dgst));
+	return ret;
+}
+
+int SM9_encrypt(int type,
+	const unsigned char *in, size_t inlen,
+	unsigned char *out, size_t *outlen,
+	SM9PublicParameters *mpk, const char *id, size_t idlen)
+{
+	int ret = 0;
+	SM9Ciphertext *sm9cipher = NULL;
+	int kdf;
+	const EVP_MD *md;
+	unsigned char *key = NULL;
+	size_t keylen;
+	unsigned char C1[1 + 64];
+	size_t C1_len;
+	unsigned char mac[EVP_MAX_MD_SIZE];
+	unsigned int maclen = sizeof(mac);
+	int len;
+	size_t i;
+
+	/* parse type */
+	switch (type) {
+	case NID_sm9encrypt_with_sm3_xor:
+		kdf = NID_sm9kdf_with_sm3;
+		md = EVP_sm3();
+		break;
+	/*
+	case NID_sm9encrypt_with_sha256_xor:
+		kdf = NID_sm9kdf_with_sha256;
+		md = EVP_sha256();
+		break;
+	*/
+	case NID_sm9encrypt_with_sm3_sms4_cbc:
+	case NID_sm9encrypt_with_sm3_sms4_ctr:
+	default:
+		return 0;
+	}
+
+	keylen = inlen + EVP_MD_size(md);
+
+	/* malloc */
+	if (!(sm9cipher = SM9Ciphertext_new())
+		|| !(key = OPENSSL_malloc(keylen))) {
+		SM9err(SM9_F_SM9_ENCRYPT, ERR_R_MALLOC_FAILURE);
+		goto end;
+	}
+
+	/* C1 */
+	if (!SM9_wrap_key(kdf, key, keylen, C1, &C1_len, mpk, id, idlen)) {
+		SM9err(SM9_F_SM9_ENCRYPT, ERR_R_SM9_LIB);
+		goto end;
+	}
+
+	/* C2 = M xor K1 */
+	for (i = 0; i < inlen; i++) {
+		key[i] ^= in[i];
+	}
+
+	/* C3 = Hv(C2||K2) */
+	if (!EVP_Digest(key, keylen, mac, &maclen, md, NULL)) {
+		SM9err(SM9_F_SM9_ENCRYPT, ERR_R_EVP_LIB);
+		goto end;
+	}
+
+	/* compose SM9Ciphertext */
+	if (!ASN1_STRING_set(sm9cipher->pointC1, C1, C1_len)
+		|| !ASN1_STRING_set(sm9cipher->c2, key, inlen)
+		|| !ASN1_STRING_set(sm9cipher->c3, mac, maclen)) {
+		SM9err(SM9_F_SM9_ENCRYPT, ERR_R_SM9_LIB);
+		goto end;
+	}
+
+	/* encode sm9 ciphertext */
+	if ((len = i2d_SM9Ciphertext(sm9cipher, &out)) <= 0) {
+		SM9err(SM9_F_SM9_ENCRYPT, ERR_R_SM9_LIB);
+		goto end;
+	}
+	*outlen = len;
+
+	ret = 1;
+
+end:
+	OPENSSL_free(sm9cipher);
+	OPENSSL_clear_free(key, keylen);
+	return ret;
+}
+
+int SM9_decrypt(int type,
+	const unsigned char *in, size_t inlen,
+	unsigned char *out, size_t *outlen,
+	SM9PrivateKey *sk)
+{
+	int ret = 0;
+	SM9Ciphertext *sm9cipher = NULL;
+	unsigned char *key = NULL;
+	size_t keylen;
+	int kdf;
+	const EVP_MD *md;
+	const unsigned char *C2;
+	int C2_len;
+	unsigned char mac[EVP_MAX_MD_SIZE];
+	unsigned int maclen = sizeof(mac);
+	int i;
+
+	/* parse type */
+	switch (type) {
+	case NID_sm9encrypt_with_sm3_xor:
+		kdf = NID_sm9kdf_with_sm3;
+		md = EVP_sm3();
+		break;
+	/*
+	case NID_sm9encrypt_with_sha256_xor:
+		kdf = NID_sm9kdf_with_sha256;
+		md = EVP_sha256();
+		break;
+	*/
+	case NID_sm9encrypt_with_sm3_sms4_cbc:
+	case NID_sm9encrypt_with_sm3_sms4_ctr:
+	default:
+		return 0;
+	}
+
+	if (!in || !outlen || !sk) {
+		SM9err(SM9_F_SM9_DECRYPT, ERR_R_PASSED_NULL_PARAMETER);
+		goto end;
+	}
+
+	/* decode sm9 ciphertext */
+	if (!(sm9cipher = d2i_SM9Ciphertext(NULL, &in, inlen))) {
+		SM9err(SM9_F_SM9_DECRYPT, ERR_R_SM9_LIB);
+		goto end;
+	}
+	C2 = ASN1_STRING_get0_data(sm9cipher->c2);
+	C2_len = ASN1_STRING_length(sm9cipher->c2);
+
+	/* check/return output length */
+	if (!out) {
+		*outlen = C2_len;
+		ret = 1;
+		goto end;
+	} else if (*outlen < (size_t)C2_len) {
+		SM9err(SM9_F_SM9_DECRYPT, SM9_R_BUFFER_TOO_SMALL);
+		goto end;
+	}
+
+	/* unwrap key */
+	keylen = C2_len + EVP_MD_size(md);
+	if (!(key = OPENSSL_malloc(keylen))) {
+		SM9err(SM9_F_SM9_DECRYPT, ERR_R_MALLOC_FAILURE);
+		goto end;
+	}
+	if (!SM9_unwrap_key(kdf, key, keylen,
+		ASN1_STRING_get0_data(sm9cipher->pointC1),
+		ASN1_STRING_length(sm9cipher->pointC1), sk)) {
+		SM9err(SM9_F_SM9_DECRYPT, ERR_R_SM9_LIB);
+		goto end;
+	}
+
+	/* M = C2 xor key */
+	for (i = 0; i < C2_len; i++) {
+		out[i] = C2[i] ^ key[i];
+	}
+	*outlen = C2_len;
+
+	/* check mac length */
+	if (ASN1_STRING_length(sm9cipher->c3) != EVP_MD_size(md)) {
+		SM9err(SM9_F_SM9_DECRYPT, ERR_R_SM9_LIB);
+		goto end;
+	}
+
+	/* C3 = Hv(C2||K2) */
+	memcpy(key, C2, C2_len);
+	if (!EVP_Digest(key, keylen, mac, &maclen, md, NULL)) {
+		SM9err(SM9_F_SM9_DECRYPT, ERR_R_EVP_LIB);
+		goto end;
+	}
+
+	if (CRYPTO_memcmp(ASN1_STRING_get0_data(sm9cipher->c3), mac, maclen) != 0) {
+		SM9err(SM9_F_SM9_DECRYPT, ERR_R_EVP_LIB);
+		goto end;
+	}
+
+	ret = 1;
+
+end:
+	SM9Ciphertext_free(sm9cipher);
+	if (key)
+		OPENSSL_clear_free(key, keylen);
+	return ret;
+}
diff --git a/crypto/sm9/sm9_err.c b/crypto/sm9/sm9_err.c
new file mode 100755
index 000000000..a07dfaf97
--- /dev/null
+++ b/crypto/sm9/sm9_err.c
@@ -0,0 +1,120 @@
+/*
+ * Generated by util/mkerr.pl DO NOT EDIT
+ * Copyright 1995-2018 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include 
+
+/* BEGIN ERROR CODES */
+#ifndef OPENSSL_NO_ERR
+
+# define ERR_FUNC(func) ERR_PACK(ERR_LIB_SM9,func,0)
+# define ERR_REASON(reason) ERR_PACK(ERR_LIB_SM9,0,reason)
+
+static ERR_STRING_DATA SM9_str_functs[] = {
+    {ERR_FUNC(SM9_F_DO_SM9_KEY_PRINT), "do_sm9_key_print"},
+    {ERR_FUNC(SM9_F_DO_SM9_MASTER_KEY_PRINT), "do_sm9_master_key_print"},
+    {ERR_FUNC(SM9_F_PKEY_SM9_COPY), "pkey_sm9_copy"},
+    {ERR_FUNC(SM9_F_PKEY_SM9_CTRL), "pkey_sm9_ctrl"},
+    {ERR_FUNC(SM9_F_PKEY_SM9_CTRL_STR), "pkey_sm9_ctrl_str"},
+    {ERR_FUNC(SM9_F_PKEY_SM9_DECRYPT), "pkey_sm9_decrypt"},
+    {ERR_FUNC(SM9_F_PKEY_SM9_INIT), "pkey_sm9_init"},
+    {ERR_FUNC(SM9_F_PKEY_SM9_MASTER_COPY), "pkey_sm9_master_copy"},
+    {ERR_FUNC(SM9_F_PKEY_SM9_MASTER_CTRL), "pkey_sm9_master_ctrl"},
+    {ERR_FUNC(SM9_F_PKEY_SM9_MASTER_CTRL_STR), "pkey_sm9_master_ctrl_str"},
+    {ERR_FUNC(SM9_F_PKEY_SM9_MASTER_ENCRYPT), "pkey_sm9_master_encrypt"},
+    {ERR_FUNC(SM9_F_PKEY_SM9_MASTER_INIT), "pkey_sm9_master_init"},
+    {ERR_FUNC(SM9_F_PKEY_SM9_MASTER_KEYGEN), "pkey_sm9_master_keygen"},
+    {ERR_FUNC(SM9_F_PKEY_SM9_MASTER_VERIFY), "pkey_sm9_master_verify"},
+    {ERR_FUNC(SM9_F_PKEY_SM9_SIGN), "pkey_sm9_sign"},
+    {ERR_FUNC(SM9_F_SM9_CIPHERTEXT_SIZE), "SM9_ciphertext_size"},
+    {ERR_FUNC(SM9_F_SM9_COMPUTE_SHARE_KEY_A), "SM9_compute_share_key_A"},
+    {ERR_FUNC(SM9_F_SM9_COMPUTE_SHARE_KEY_B), "SM9_compute_share_key_B"},
+    {ERR_FUNC(SM9_F_SM9_DECRYPT), "SM9_decrypt"},
+    {ERR_FUNC(SM9_F_SM9_ENCRYPT), "SM9_encrypt"},
+    {ERR_FUNC(SM9_F_SM9_EXTRACT_PUBLIC_PARAMETERS),
+     "SM9_extract_public_parameters"},
+    {ERR_FUNC(SM9_F_SM9_GENERATE_KEY_EXCHANGE), "SM9_generate_key_exchange"},
+    {ERR_FUNC(SM9_F_SM9_GENERATE_MASTER_SECRET),
+     "SM9_generate_master_secret"},
+    {ERR_FUNC(SM9_F_SM9_KEY_NEW), "SM9_KEY_new"},
+    {ERR_FUNC(SM9_F_SM9_MASTER_KEY_EXTRACT_KEY),
+     "SM9_MASTER_KEY_extract_key"},
+    {ERR_FUNC(SM9_F_SM9_MASTER_KEY_NEW), "SM9_MASTER_KEY_new"},
+    {ERR_FUNC(SM9_F_SM9_MASTER_OLD_PRIV_DECODE),
+     "sm9_master_old_priv_decode"},
+    {ERR_FUNC(SM9_F_SM9_MASTER_PRIV_DECODE), "sm9_master_priv_decode"},
+    {ERR_FUNC(SM9_F_SM9_MASTER_PRIV_ENCODE), "sm9_master_priv_encode"},
+    {ERR_FUNC(SM9_F_SM9_MASTER_PUB_DECODE), "sm9_master_pub_decode"},
+    {ERR_FUNC(SM9_F_SM9_OLD_PRIV_DECODE), "sm9_old_priv_decode"},
+    {ERR_FUNC(SM9_F_SM9_PRIV_DECODE), "sm9_priv_decode"},
+    {ERR_FUNC(SM9_F_SM9_PRIV_ENCODE), "sm9_priv_encode"},
+    {ERR_FUNC(SM9_F_SM9_PUB_DECODE), "sm9_pub_decode"},
+    {ERR_FUNC(SM9_F_SM9_SIGN), "SM9_sign"},
+    {ERR_FUNC(SM9_F_SM9_SIGNFINAL), "SM9_SignFinal"},
+    {ERR_FUNC(SM9_F_SM9_SIGNINIT), "SM9_SignInit"},
+    {ERR_FUNC(SM9_F_SM9_UNWRAP_KEY), "SM9_unwrap_key"},
+    {ERR_FUNC(SM9_F_SM9_VERIFY), "SM9_verify"},
+    {ERR_FUNC(SM9_F_SM9_VERIFYFINAL), "SM9_VerifyFinal"},
+    {ERR_FUNC(SM9_F_SM9_VERIFYINIT), "SM9_VerifyInit"},
+    {ERR_FUNC(SM9_F_SM9_WRAP_KEY), "SM9_wrap_key"},
+    {0, NULL}
+};
+
+static ERR_STRING_DATA SM9_str_reasons[] = {
+    {ERR_REASON(SM9_R_BUFFER_TOO_SMALL), "buffer too small"},
+    {ERR_REASON(SM9_R_DECODE_ERROR), "decode error"},
+    {ERR_REASON(SM9_R_DIGEST_FAILURE), "digest failure"},
+    {ERR_REASON(SM9_R_EC_LIB), "ec lib"},
+    {ERR_REASON(SM9_R_EXTENSION_FIELD_ERROR), "extension field error"},
+    {ERR_REASON(SM9_R_IDENTITY_REQUIRED), "identity required"},
+    {ERR_REASON(SM9_R_INVALID_DIGEST_TYPE), "invalid digest type"},
+    {ERR_REASON(SM9_R_INVALID_ENCRYPT_SCHEME), "invalid encrypt scheme"},
+    {ERR_REASON(SM9_R_INVALID_HASH1), "invalid hash1"},
+    {ERR_REASON(SM9_R_INVALID_HASH2_DIGEST), "invalid hash2 digest"},
+    {ERR_REASON(SM9_R_INVALID_ID), "invalid id"},
+    {ERR_REASON(SM9_R_INVALID_KEM_KEY_LENGTH), "invalid kem key length"},
+    {ERR_REASON(SM9_R_INVALID_KEY_AGREEMENT_CHECKSUM),
+     "invalid key agreement checksum"},
+    {ERR_REASON(SM9_R_INVALID_KEY_USAGE), "invalid key usage"},
+    {ERR_REASON(SM9_R_INVALID_PAIRING), "invalid pairing"},
+    {ERR_REASON(SM9_R_INVALID_PAIRING_TYPE), "invalid pairing type"},
+    {ERR_REASON(SM9_R_INVALID_POINTPPUB), "invalid pointppub"},
+    {ERR_REASON(SM9_R_INVALID_PRIVATE_POINT), "invalid private point"},
+    {ERR_REASON(SM9_R_INVALID_SCHEME), "invalid scheme"},
+    {ERR_REASON(SM9_R_INVALID_SIGNATURE), "invalid signature"},
+    {ERR_REASON(SM9_R_INVALID_SIGNATURE_FORMAT), "invalid signature format"},
+    {ERR_REASON(SM9_R_INVALID_SIGN_MD), "invalid sign md"},
+    {ERR_REASON(SM9_R_INVALID_SIGN_SCHEME), "invalid sign scheme"},
+    {ERR_REASON(SM9_R_INVALID_SM9_SCHEME), "invalid sm9 scheme"},
+    {ERR_REASON(SM9_R_NO_MASTER_SECRET), "no master secret"},
+    {ERR_REASON(SM9_R_PAIRING_ERROR), "pairing error"},
+    {ERR_REASON(SM9_R_PLAINTEXT_TOO_LONG), "plaintext too long"},
+    {ERR_REASON(SM9_R_RATE_PAIRING_ERROR), "rate pairing error"},
+    {ERR_REASON(SM9_R_SIGNER_ID_REQUIRED), "signer id required"},
+    {ERR_REASON(SM9_R_TWIST_CURVE_ERROR), "twist curve error"},
+    {ERR_REASON(SM9_R_VERIFY_FAILURE), "verify failure"},
+    {ERR_REASON(SM9_R_ZERO_ID), "zero id"},
+    {0, NULL}
+};
+
+#endif
+
+int ERR_load_SM9_strings(void)
+{
+#ifndef OPENSSL_NO_ERR
+
+    if (ERR_func_error_string(SM9_str_functs[0].error) == NULL) {
+        ERR_load_strings(0, SM9_str_functs);
+        ERR_load_strings(0, SM9_str_reasons);
+    }
+#endif
+    return 1;
+}
diff --git a/crypto/sm9/sm9_exch.c b/crypto/sm9/sm9_exch.c
new file mode 100755
index 000000000..1cdface03
--- /dev/null
+++ b/crypto/sm9/sm9_exch.c
@@ -0,0 +1,500 @@
+/* ====================================================================
+ * Copyright (c) 2016 - 2018 The GmSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the GmSSL Project.
+ *    (http://gmssl.org/)"
+ *
+ * 4. The name "GmSSL Project" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    guanzhi1980@gmail.com.
+ *
+ * 5. Products derived from this software may not be called "GmSSL"
+ *    nor may "GmSSL" appear in their names without prior written
+ *    permission of the GmSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the GmSSL Project
+ *    (http://gmssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE GmSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE GmSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include "sm9_lcl.h"
+
+/*
+ * compute Q = H1(peer_id) * P1 + Ppube
+ * r = rand(1, n-1)
+ * R = r * Q
+ * g = e(Ppube, P2)
+ * g1' = g2 = g^r
+ * send R to peer
+ */
+int SM9_generate_key_exchange(unsigned char *R, size_t *Rlen,
+	BIGNUM *r, unsigned char *gr, size_t *grlen,
+	const char *peer_id, size_t peer_idlen,
+	SM9PrivateKey *sk, int initiator)
+{
+	int ret = 0;
+	EC_GROUP *group = NULL;
+	EC_POINT *Ppube = NULL;
+	EC_POINT *Q = NULL;
+	BN_CTX *bn_ctx = NULL;
+	BIGNUM *h = NULL;
+	const EVP_MD *md;
+	int point_form = POINT_CONVERSION_UNCOMPRESSED;
+	const BIGNUM *p = SM9_get0_prime();
+	const BIGNUM *n = SM9_get0_order();
+	fp12_t g;
+	int len;
+
+	if (!(group = EC_GROUP_new_by_curve_name(NID_sm9bn256v1))
+		|| !(Ppube = EC_POINT_new(group))
+		|| !(Q = EC_POINT_new(group))
+		|| !(bn_ctx = BN_CTX_new())) {
+		SM9err(SM9_F_SM9_GENERATE_KEY_EXCHANGE, ERR_R_MALLOC_FAILURE);
+		goto end;
+	}
+	BN_CTX_start(bn_ctx);
+	if (!fp12_init(g, bn_ctx)) {
+		SM9err(SM9_F_SM9_GENERATE_KEY_EXCHANGE, ERR_R_MALLOC_FAILURE);
+		goto end;
+	}
+
+	/* r = rand(1, n-1) */
+	do {
+		if (!BN_rand_range(r, n)) {
+			SM9err(SM9_F_SM9_GENERATE_KEY_EXCHANGE, ERR_R_BN_LIB);
+			goto end;
+		}
+	} while (BN_is_zero(r));
+
+	switch (OBJ_obj2nid(sk->hash1)) {
+	case NID_sm9hash1_with_sm3:
+		md = EVP_sm3();
+		break;
+	case NID_sm9hash1_with_sha256:
+		md = EVP_sha256();
+		break;
+	default:
+		SM9err(SM9_F_SM9_GENERATE_KEY_EXCHANGE, ERR_R_SM9_LIB);
+		goto end;
+	}
+
+	/* h = H1(peer_id) */
+	if (!SM9_hash1(md, &h, peer_id, peer_idlen, SM9_HID_EXCH, n, bn_ctx)) {
+		SM9err(SM9_F_SM9_GENERATE_KEY_EXCHANGE, ERR_R_SM9_LIB);
+		goto end;
+	}
+
+	/* get Ppube from sk */
+	if (!EC_POINT_oct2point(group, Ppube, ASN1_STRING_get0_data(sk->pointPpub),
+		ASN1_STRING_length(sk->pointPpub), bn_ctx)) {
+		SM9err(SM9_F_SM9_GENERATE_KEY_EXCHANGE, ERR_R_SM9_LIB);
+		goto end;
+	}
+
+	if (!EC_POINT_mul(group, Q, h, NULL, NULL, bn_ctx)
+		/* Q = H1(peer_id) * P1 + Ppube */
+		|| !EC_POINT_add(group, Q, Q, Ppube, bn_ctx)
+		/* R = r * Q */
+		|| !EC_POINT_mul(group, Q, NULL, Q, r, bn_ctx)
+		|| (len = EC_POINT_point2oct(group, Q, point_form, R, *Rlen, bn_ctx)) <= 0) {
+		SM9err(SM9_F_SM9_GENERATE_KEY_EXCHANGE, ERR_R_SM9_LIB);
+		goto end;
+	}
+	*Rlen = len;
+
+	/* g = e(Ppube, P2) */
+	if (!rate_pairing(g, NULL, Ppube, bn_ctx)) {
+		SM9err(SM9_F_SM9_GENERATE_KEY_EXCHANGE, ERR_R_SM9_LIB);
+		goto end;
+	}
+
+	/* g1' = g2 = g^r */
+	if (!fp12_pow(g, g, r, p, bn_ctx) || !fp12_to_bin(g, gr)) {
+		SM9err(SM9_F_SM9_GENERATE_KEY_EXCHANGE, ERR_R_SM9_LIB);
+		goto end;
+	}
+
+	ret = 1;
+
+end:
+	EC_GROUP_free(group);
+	EC_POINT_free(Ppube);
+	EC_POINT_free(Q);
+	BN_free(h);
+	fp12_cleanup(g);
+	if (bn_ctx) {
+		BN_CTX_end(bn_ctx);
+	}
+	BN_CTX_free(bn_ctx);
+	return ret;
+}
+
+int SM9_compute_share_key_A(int type,
+	unsigned char *SKA, size_t SKAlen,
+	unsigned char SA[32], /* optional, send to B */
+	const unsigned char SB[32], /* optional, recv from B */
+	const BIGNUM *rA,
+	const unsigned char RA[65],
+	const unsigned char RB[65],
+	const unsigned char g1[384],
+	const char *IDB, size_t IDBlen,
+	SM9PrivateKey *skA)
+{
+	int ret = 0;
+	const EVP_MD *md = EVP_sm3();
+	const char *IDA;
+	int IDAlen;
+	EC_GROUP *group = NULL;
+	EC_POINT *P = NULL;
+	EVP_MD_CTX *md_ctx = NULL;
+	BN_CTX *bn_ctx = NULL;
+	fp12_t g;
+	point_t deA;
+	const BIGNUM *p = SM9_get0_prime();
+	unsigned char x82[1] = {0x82};
+	unsigned char x83[1] = {0x83};
+	unsigned char buf[384 * 2];
+	unsigned char counter[4] = {0, 0, 0, 1};
+	unsigned char dgst[EVP_MAX_MD_SIZE];
+	unsigned int dgstlen;
+
+	switch (type) {
+	case NID_sm9kdf_with_sm3:
+		md = EVP_sm3();
+		break;
+	case NID_sm9kdf_with_sha256:
+		md = EVP_sha256();
+		break;
+	default:
+		goto end;
+	}
+
+	/* get IDA */
+	IDA = (char *)ASN1_STRING_get0_data(skA->identity);
+	IDAlen = ASN1_STRING_length(skA->identity);
+
+	/* malloc */
+	if (!(group = EC_GROUP_new_by_curve_name(NID_sm9bn256v1))
+		|| !(P = EC_POINT_new(group))
+		|| !(md_ctx = EVP_MD_CTX_new())
+		|| !(bn_ctx = BN_CTX_new())) {
+		SM9err(SM9_F_SM9_COMPUTE_SHARE_KEY_A, ERR_R_MALLOC_FAILURE);
+		goto end;
+	}
+	BN_CTX_start(bn_ctx);
+	if (!point_init(&deA, bn_ctx)
+		|| !fp12_init(g, bn_ctx)) {
+		SM9err(SM9_F_SM9_COMPUTE_SHARE_KEY_A, ERR_R_MALLOC_FAILURE);
+		goto end;
+	}
+
+	/* parse deA */
+	if (ASN1_STRING_length(skA->privatePoint) != 129
+		|| !point_from_octets(&deA, ASN1_STRING_get0_data(skA->privatePoint), p, bn_ctx)) {
+		SM9err(SM9_F_SM9_COMPUTE_SHARE_KEY_A, ERR_R_SM9_LIB);
+		goto end;
+	}
+
+	/* parse RB */
+	if (!EC_POINT_oct2point(group, P, RB, 65, bn_ctx)) {
+		SM9err(SM9_F_SM9_COMPUTE_SHARE_KEY_A, ERR_R_SM9_LIB);
+		goto end;
+	}
+
+	/* g2' = e(RB, deA) */
+	if (!rate_pairing(g, &deA, P, bn_ctx) || !fp12_to_bin(g, buf)) {
+		SM9err(SM9_F_SM9_COMPUTE_SHARE_KEY_A, ERR_R_SM9_LIB);
+		goto end;
+	}
+
+	/* g3' = (g2')^r_A */
+	if (!fp12_pow(g, g, rA, p, bn_ctx) || !fp12_to_bin(g, buf + 384)) {
+		SM9err(SM9_F_SM9_COMPUTE_SHARE_KEY_A, ERR_R_SM9_LIB);
+		goto end;
+	}
+
+	/* compute optional S1 */
+	if (SA) {
+		unsigned char S1[32];
+
+		/* dgst =  H(g2 || g3 || ID_A || ID_B || R_A || R_B) */
+		if (!EVP_DigestInit_ex(md_ctx, md, NULL)
+			|| !EVP_DigestUpdate(md_ctx, buf, sizeof(buf))
+			|| !EVP_DigestUpdate(md_ctx, IDA, IDAlen)
+			|| !EVP_DigestUpdate(md_ctx, IDB, IDBlen)
+			|| !EVP_DigestUpdate(md_ctx, RA + 1, 64)
+			|| !EVP_DigestUpdate(md_ctx, RB + 1, 64)
+			|| !EVP_DigestFinal_ex(md_ctx, dgst, &dgstlen)) {
+			SM9err(SM9_F_SM9_COMPUTE_SHARE_KEY_A, ERR_R_EVP_LIB);
+			goto end;
+		}
+
+		/* S1 = H(0x82 || g1' || dgst) */
+		if (!EVP_DigestInit_ex(md_ctx, md, NULL)
+			|| !EVP_DigestUpdate(md_ctx, x82, 1)
+			|| !EVP_DigestUpdate(md_ctx, g1, 384)
+			|| !EVP_DigestUpdate(md_ctx, dgst, dgstlen)
+			|| !EVP_DigestFinal_ex(md_ctx, S1, &dgstlen)) {
+			SM9err(SM9_F_SM9_COMPUTE_SHARE_KEY_A, ERR_R_EVP_LIB);
+			goto end;
+		}
+
+		/* given SB, check if S1 == SB */
+		if (SB) {
+			if (memcmp(S1, SB, dgstlen) != 0) {
+				SM9err(SM9_F_SM9_COMPUTE_SHARE_KEY_A, SM9_R_INVALID_KEY_AGREEMENT_CHECKSUM);
+				goto end;
+			}
+		}
+	}
+
+ 	/* SKA = KDF(ID_A || ID_B || R_A || R_B || g1 || g2 || g3, Klen) */
+	while (SKAlen > 0) {
+		unsigned char key[64];
+		unsigned int len;
+
+		if (!EVP_DigestInit_ex(md_ctx, md, NULL)
+			|| !EVP_DigestUpdate(md_ctx, IDA, IDAlen)
+			|| !EVP_DigestUpdate(md_ctx, IDB, IDBlen)
+			|| !EVP_DigestUpdate(md_ctx, RA + 1, 64)
+			|| !EVP_DigestUpdate(md_ctx, RB + 1, 64)
+			|| !EVP_DigestUpdate(md_ctx, g1, 384)
+			|| !EVP_DigestUpdate(md_ctx, buf, sizeof(buf))
+			|| !EVP_DigestUpdate(md_ctx, counter, 4)
+			|| !EVP_DigestFinal_ex(md_ctx, key, &len)) {
+			SM9err(SM9_F_SM9_COMPUTE_SHARE_KEY_A, ERR_R_EVP_LIB);
+			goto end;
+		}
+
+		if (len > SKAlen)
+			len = SKAlen;
+		memcpy(SKA, key, len);
+
+		SKA += len;
+		SKAlen -= len;
+		counter[3]++;
+	}
+
+	if (SA) {
+		/* SA = H(0x83 || g1 || dgst) */
+		if (!EVP_DigestInit_ex(md_ctx, md, NULL)
+			|| !EVP_DigestUpdate(md_ctx, x83, 1)
+			|| !EVP_DigestUpdate(md_ctx, g1, 384)
+			|| !EVP_DigestUpdate(md_ctx, dgst, dgstlen)
+			|| !EVP_DigestFinal_ex(md_ctx, SA, &dgstlen)) {
+			SM9err(SM9_F_SM9_COMPUTE_SHARE_KEY_A, ERR_R_EVP_LIB);
+			goto end;
+		}
+	}
+
+	ret = 1;
+
+end:
+	EC_GROUP_free(group);
+	EC_POINT_free(P);
+	EVP_MD_CTX_free(md_ctx);
+	fp12_cleanup(g);
+	point_cleanup(&deA);
+	if (bn_ctx) {
+		BN_CTX_end(bn_ctx);
+	}
+	BN_CTX_free(bn_ctx);
+	return ret;
+}
+
+int SM9_compute_share_key_B(int type,
+	unsigned char *SKB, size_t SKBlen,
+	unsigned char SB[32], /* optional, send to A */
+	unsigned char S2[32], /* optional, to be compared with recved SA */
+	const BIGNUM *rB,
+	const unsigned char RB[65],
+	const unsigned char RA[65],
+	const unsigned char g2[384],
+	const char *IDA, size_t IDAlen,
+	SM9PrivateKey *skB)
+{
+	int ret = 0;
+	const EVP_MD *md = EVP_sm3();
+	const char *IDB;
+	int IDBlen;
+	EC_GROUP *group = NULL;
+	EC_POINT *P = NULL;
+	EVP_MD_CTX *md_ctx = NULL;
+	BN_CTX *bn_ctx = NULL;
+	fp12_t g;
+	point_t deB;
+	const BIGNUM *p = SM9_get0_prime();
+	unsigned char x82[1] = {0x82};
+	unsigned char x83[1] = {0x83};
+	unsigned char g1[384];
+	unsigned char g3[384];
+	unsigned char counter[4] = {0, 0, 0, 1};
+	unsigned char key[EVP_MAX_MD_SIZE];
+	unsigned int len;
+
+	switch (type) {
+	case NID_sm9kdf_with_sm3:
+		md = EVP_sm3();
+		break;
+	case NID_sm9kdf_with_sha256:
+		md = EVP_sha256();
+		break;
+	default:
+		goto end;
+	}
+
+	/* get IDB */
+	IDB = (char *)ASN1_STRING_get0_data(skB->identity);
+	IDBlen = ASN1_STRING_length(skB->identity);
+
+	/* malloc */
+	if (!(group = EC_GROUP_new_by_curve_name(NID_sm9bn256v1))
+		|| !(P = EC_POINT_new(group))
+		|| !(md_ctx = EVP_MD_CTX_new())
+		|| !(bn_ctx = BN_CTX_new())) {
+		SM9err(SM9_F_SM9_COMPUTE_SHARE_KEY_B, ERR_R_MALLOC_FAILURE);
+		goto end;
+	}
+	BN_CTX_start(bn_ctx);
+	if (!point_init(&deB, bn_ctx)
+		|| !fp12_init(g, bn_ctx)) {
+		SM9err(SM9_F_SM9_COMPUTE_SHARE_KEY_B, ERR_R_MALLOC_FAILURE);
+		goto end;
+	}
+
+	/* parse deB */
+	if (ASN1_STRING_length(skB->privatePoint) != 129
+		|| !point_from_octets(&deB, ASN1_STRING_get0_data(skB->privatePoint), p, bn_ctx)) {
+		SM9err(SM9_F_SM9_COMPUTE_SHARE_KEY_B, ERR_R_SM9_LIB);
+		goto end;
+	}
+
+	/* parse RA */
+	if (!EC_POINT_oct2point(group, P, RA, 65, bn_ctx)) {
+		SM9err(SM9_F_SM9_COMPUTE_SHARE_KEY_B, ERR_R_SM9_LIB);
+		goto end;
+	}
+
+	/* g1 = e(RA, deB) */
+	if (!rate_pairing(g, &deB, P, bn_ctx) || !fp12_to_bin(g, g1)) {
+		SM9err(SM9_F_SM9_COMPUTE_SHARE_KEY_B, ERR_R_SM9_LIB);
+		goto end;
+	}
+
+	/* g3 = (g1)^r_B */
+	if (!fp12_pow(g, g, rB, p, bn_ctx) || !fp12_to_bin(g, g3)) {
+		SM9err(SM9_F_SM9_COMPUTE_SHARE_KEY_B, ERR_R_SM9_LIB);
+		goto end;
+	}
+
+ 	/* SKB = KDF(ID_A || ID_B || R_A || R_B || g1 || g2 || g3, Klen) */
+	while (SKBlen > 0) {
+		if (!EVP_DigestInit_ex(md_ctx, md, NULL)
+			|| !EVP_DigestUpdate(md_ctx, IDA, IDAlen)
+			|| !EVP_DigestUpdate(md_ctx, IDB, IDBlen)
+			|| !EVP_DigestUpdate(md_ctx, RA + 1, 64)
+			|| !EVP_DigestUpdate(md_ctx, RB + 1, 64)
+			|| !EVP_DigestUpdate(md_ctx, g1, 384)
+			|| !EVP_DigestUpdate(md_ctx, g2, 384)
+			|| !EVP_DigestUpdate(md_ctx, g3, 384)
+			|| !EVP_DigestUpdate(md_ctx, counter, 4)
+			|| !EVP_DigestFinal_ex(md_ctx, key, &len)) {
+			SM9err(SM9_F_SM9_COMPUTE_SHARE_KEY_B, ERR_R_EVP_LIB);
+			goto end;
+		}
+
+		if (len > SKBlen)
+			len = SKBlen;
+		memcpy(SKB, key, len);
+
+		SKB += len;
+		SKBlen -= len;
+		counter[3]++;
+	}
+
+	/* compute optional S1 */
+	if (S2 && SB) {
+		/* dgst =  H(g2 || g3 || ID_A || ID_B || R_A || R_B) */
+		if (!EVP_DigestInit_ex(md_ctx, md, NULL)
+			|| !EVP_DigestUpdate(md_ctx, g2, 384)
+			|| !EVP_DigestUpdate(md_ctx, g3, 384)
+			|| !EVP_DigestUpdate(md_ctx, IDA, IDAlen)
+			|| !EVP_DigestUpdate(md_ctx, IDB, IDBlen)
+			|| !EVP_DigestUpdate(md_ctx, RA + 1, 64)
+			|| !EVP_DigestUpdate(md_ctx, RB + 1, 64)
+			|| !EVP_DigestFinal_ex(md_ctx, key, &len)) {
+			SM9err(SM9_F_SM9_COMPUTE_SHARE_KEY_B, ERR_R_EVP_LIB);
+			goto end;
+		}
+
+		/* SB = H(0x82 || g1 || dgst) */
+		if (!EVP_DigestInit_ex(md_ctx, md, NULL)
+			|| !EVP_DigestUpdate(md_ctx, x82, 1)
+			|| !EVP_DigestUpdate(md_ctx, g1, 384)
+			|| !EVP_DigestUpdate(md_ctx, key, len)
+			|| !EVP_DigestFinal_ex(md_ctx, SB, &len)) {
+			SM9err(SM9_F_SM9_COMPUTE_SHARE_KEY_B, ERR_R_EVP_LIB);
+			goto end;
+		}
+
+		/* S2 = H(0x83 || g1 || dgst) */
+		if (!EVP_DigestInit_ex(md_ctx, md, NULL)
+			|| !EVP_DigestUpdate(md_ctx, x83, 1)
+			|| !EVP_DigestUpdate(md_ctx, g1, 384)
+			|| !EVP_DigestUpdate(md_ctx, key, len)
+			|| !EVP_DigestFinal_ex(md_ctx, S2, &len)) {
+			SM9err(SM9_F_SM9_COMPUTE_SHARE_KEY_B, ERR_R_EVP_LIB);
+			goto end;
+		}
+	}
+
+	ret = 1;
+
+end:
+	EC_GROUP_free(group);
+	EC_POINT_free(P);
+	EVP_MD_CTX_free(md_ctx);
+	fp12_cleanup(g);
+	point_cleanup(&deB);
+	if (bn_ctx) {
+		BN_CTX_end(bn_ctx);
+	}
+	BN_CTX_free(bn_ctx);
+	OPENSSL_cleanse(key, sizeof(key));
+	return ret;
+}
diff --git a/crypto/sm9/sm9_keygen.c b/crypto/sm9/sm9_keygen.c
new file mode 100755
index 000000000..d80dbc930
--- /dev/null
+++ b/crypto/sm9/sm9_keygen.c
@@ -0,0 +1,359 @@
+/* ====================================================================
+ * Copyright (c) 2016 The GmSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the GmSSL Project.
+ *    (http://gmssl.org/)"
+ *
+ * 4. The name "GmSSL Project" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    guanzhi1980@gmail.com.
+ *
+ * 5. Products derived from this software may not be called "GmSSL"
+ *    nor may "GmSSL" appear in their names without prior written
+ *    permission of the GmSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the GmSSL Project
+ *    (http://gmssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE GmSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE GmSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+
+#include 
+#include 
+#include 
+#include "sm9_lcl.h"
+
+
+int SM9_hash1(const EVP_MD *md, BIGNUM **r, const char *id, size_t idlen,
+	unsigned char hid, const BIGNUM *n, BN_CTX *ctx)
+{
+	int ret = 0;
+	BIGNUM *h = NULL;
+	BN_CTX *bn_ctx = NULL;
+	EVP_MD_CTX *ctx1 = NULL;
+	EVP_MD_CTX *ctx2 = NULL;
+	unsigned char prefix[1] = {0x01};
+	unsigned char ct1[4] = {0x00, 0x00, 0x00, 0x01};
+	unsigned char ct2[4] = {0x00, 0x00, 0x00, 0x02};
+	unsigned char buf[128];
+	unsigned int len;
+
+	if (!(ctx1 = EVP_MD_CTX_new())
+		|| !(ctx2 = EVP_MD_CTX_new())
+		|| !(bn_ctx = BN_CTX_new())
+		|| !(h = BN_new())) {
+		goto end;
+	}
+
+	if (!EVP_DigestInit_ex(ctx1, md, NULL)
+		|| !EVP_DigestUpdate(ctx1, prefix, sizeof(prefix))
+		|| !EVP_DigestUpdate(ctx1, id, idlen)
+		|| !EVP_DigestUpdate(ctx1, &hid, 1)
+		|| !EVP_MD_CTX_copy(ctx2, ctx1)
+		|| !EVP_DigestUpdate(ctx1, ct1, sizeof(ct1))
+		|| !EVP_DigestUpdate(ctx2, ct2, sizeof(ct2))
+		|| !EVP_DigestFinal_ex(ctx1, buf, &len)
+		|| !EVP_DigestFinal_ex(ctx2, buf + len, &len)) {
+		goto end;
+	}
+
+	if (!BN_bin2bn(buf, 40, h)
+		|| !BN_mod(h, h, SM9_get0_order_minus_one(), bn_ctx)
+		|| !BN_add_word(h, 1)) {
+		goto end;
+	}
+
+	*r = h;
+	h = NULL;
+	ret = 1;
+
+end:
+	BN_free(h);
+	BN_CTX_free(bn_ctx);
+	EVP_MD_CTX_free(ctx1);
+	EVP_MD_CTX_free(ctx2);
+	return ret;
+}
+
+SM9_KEY *SM9_MASTER_KEY_extract_key(SM9_MASTER_KEY *master,
+	const char *id, size_t idlen, int priv)
+{
+	SM9PrivateKey *ret = NULL;
+	SM9PrivateKey *sk = NULL;
+	EC_GROUP *group = NULL;
+	const BIGNUM *p = SM9_get0_prime();
+	const BIGNUM *n = SM9_get0_order();
+	int scheme;
+	unsigned char hid;
+	const EVP_MD *md;
+	BN_CTX *ctx = NULL;
+	BIGNUM *t = NULL;
+	int point_form = POINT_CONVERSION_UNCOMPRESSED;
+	unsigned char buf[129];
+	size_t len = sizeof(buf);
+
+	/* check args */
+	if (!master || !id) {
+		SM9err(SM9_F_SM9_MASTER_KEY_EXTRACT_KEY,
+			ERR_R_PASSED_NULL_PARAMETER);
+		return NULL;
+	}
+	if (strlen(id) != idlen || idlen <= 0 || idlen > SM9_MAX_ID_LENGTH) {
+		SM9err(SM9_F_SM9_MASTER_KEY_EXTRACT_KEY,
+			SM9_R_INVALID_ID);
+		return NULL;
+	}
+
+	/* check pairing */
+	if (OBJ_obj2nid(master->pairing) != NID_sm9bn256v1) {
+		return NULL;
+	}
+
+	/* check scheme */
+	scheme = OBJ_obj2nid(master->scheme);
+	switch (scheme) {
+	case NID_sm9sign:
+		hid = SM9_HID_SIGN;
+		break;
+	case NID_sm9keyagreement:
+		hid = SM9_HID_EXCH;
+		break;
+	case NID_sm9encrypt:
+		hid = SM9_HID_ENC;
+		break;
+	default:
+		return NULL;
+	}
+
+	/* check if master */
+	if (priv && master->masterSecret == NULL) {
+		SM9err(SM9_F_SM9_MASTER_KEY_EXTRACT_KEY, SM9_R_NO_MASTER_SECRET);
+		return NULL;
+	}
+
+	/* check hash1 and set hash1 md */
+	switch (OBJ_obj2nid(master->hash1)) {
+	case NID_sm9hash1_with_sm3:
+		md = EVP_sm3();
+		break;
+	case NID_sm9hash1_with_sha256:
+		md = EVP_sha256();
+		break;
+	default:
+		SM9err(SM9_F_SM9_MASTER_KEY_EXTRACT_KEY, SM9_R_INVALID_HASH1);
+		return NULL;
+	}
+
+	/* malloc */
+	if (!(sk = SM9PrivateKey_new())
+		|| !(group = EC_GROUP_new_by_curve_name(NID_sm9bn256v1))
+		|| !(ctx = BN_CTX_new())) {
+		SM9err(SM9_F_SM9_MASTER_KEY_EXTRACT_KEY,
+			ERR_R_MALLOC_FAILURE);
+		goto end;
+	}
+	BN_CTX_start(ctx);
+
+	if (!(sk->pairing = master->pairing)
+		|| !(sk->scheme = master->scheme)
+		|| !(sk->hash1 = master->hash1)
+		|| !(sk->pointPpub = ASN1_OCTET_STRING_dup(master->pointPpub))
+		|| !(sk->identity = ASN1_OCTET_STRING_new())
+		|| !ASN1_OCTET_STRING_set(sk->identity, (unsigned char *)id, idlen)
+		|| !(sk->publicPoint = ASN1_OCTET_STRING_new())
+		|| !(sk->privatePoint = ASN1_OCTET_STRING_new())) {
+		SM9err(SM9_F_SM9_MASTER_KEY_EXTRACT_KEY, ERR_R_ASN1_LIB);
+		goto end;
+	}
+
+	/* h1 = H1(id||HID) */
+	if (!SM9_hash1(md, &t, id, idlen, hid, n, ctx)) {
+		SM9err(SM9_F_SM9_MASTER_KEY_EXTRACT_KEY, ERR_R_SM9_LIB);
+		goto end;
+	}
+
+	/* generate (Ppubs, ds) or (Ppube, de) */
+	if (scheme == NID_sm9sign) {
+
+		/* publicPoint = h1 * P2 + Ppubs */
+		point_t Ppubs;
+		point_t point;
+		if (!point_init(&point, ctx)
+			|| !point_init(&Ppubs, ctx)
+			|| ASN1_STRING_length(master->pointPpub) != sizeof(buf)
+			|| !point_from_octets(&Ppubs, ASN1_STRING_get0_data(master->pointPpub), p, ctx)
+			|| !point_mul_generator(&point, t, p, ctx)
+			|| !point_add(&point, &point, &Ppubs, p, ctx)
+			|| !point_to_octets(&point, buf, ctx)
+			|| !ASN1_OCTET_STRING_set(sk->publicPoint, buf, sizeof(buf))) {
+			SM9err(SM9_F_SM9_MASTER_KEY_EXTRACT_KEY, ERR_R_SM9_LIB);
+			point_cleanup(&Ppubs);
+			point_cleanup(&point);
+			goto end;
+		}
+		point_cleanup(&Ppubs);
+		point_cleanup(&point);
+
+	} else {
+
+		/* publicPoint = h1 * P1 + Ppube */
+		EC_POINT *Ppube = NULL;
+		EC_POINT *point = NULL;
+		if (!(group = EC_GROUP_new_by_curve_name(NID_sm9bn256v1))
+			|| !(point = EC_POINT_new(group))
+			|| !(Ppube = EC_POINT_new(group))
+			|| !EC_POINT_oct2point(group, Ppube,
+				ASN1_STRING_get0_data(master->pointPpub),
+				ASN1_STRING_length(master->pointPpub), ctx)
+			|| !EC_POINT_mul(group, point, t, NULL, NULL, ctx)
+			|| !EC_POINT_add(group, point, point, Ppube, ctx)
+			|| !(len = EC_POINT_point2oct(group, point, point_form, buf, len, ctx))
+			|| !ASN1_OCTET_STRING_set(sk->publicPoint, buf, len)) {
+			SM9err(SM9_F_SM9_MASTER_KEY_EXTRACT_KEY, ERR_R_SM9_LIB);
+			EC_POINT_free(Ppube);
+			EC_POINT_free(point);
+			goto end;
+		}
+		EC_POINT_free(Ppube);
+		EC_POINT_free(point);
+	}
+
+	if (priv) {
+
+		/* t1 = H1(ID||hid) + master (mod n) */
+		if (!BN_mod_add(t, t, master->masterSecret, n, ctx)) {
+			SM9err(SM9_F_SM9_MASTER_KEY_EXTRACT_KEY, ERR_R_BN_LIB);
+			goto end;
+		}
+
+		/* if t1 is zero, return failed */
+		if (BN_is_zero(t)) {
+			SM9err(SM9_F_SM9_MASTER_KEY_EXTRACT_KEY, SM9_R_ZERO_ID);
+			goto end;
+		}
+
+		/* t2 = master * t1^-1 (mod n) */
+		if (!BN_mod_inverse(t, t, n, ctx)) {
+			SM9err(SM9_F_SM9_MASTER_KEY_EXTRACT_KEY, ERR_R_BN_LIB);
+			goto end;
+		}
+		if (!BN_mod_mul(t, master->masterSecret, t, n, ctx)) {
+			SM9err(SM9_F_SM9_MASTER_KEY_EXTRACT_KEY, ERR_R_BN_LIB);
+			goto end;
+		}
+
+		if (scheme == NID_sm9sign) {
+			/* ds = t2 * P1 */
+			EC_POINT *ds = NULL;
+			if (!(ds = EC_POINT_new(group))
+				|| !EC_POINT_mul(group, ds, t, NULL, NULL, ctx)
+				|| !(len = EC_POINT_point2oct(group, ds, point_form, buf, len, ctx))
+				|| !ASN1_OCTET_STRING_set(sk->privatePoint, buf, len)) {
+				SM9err(SM9_F_SM9_MASTER_KEY_EXTRACT_KEY, ERR_R_SM9_LIB);
+				EC_POINT_free(ds);
+				goto end;
+			}
+			EC_POINT_free(ds);
+		} else {
+			/* de = t2 * P2 */
+			point_t de;
+			if (!point_init(&de, ctx)
+				|| !point_mul_generator(&de, t, p, ctx)
+				|| !point_to_octets(&de, buf, ctx)
+				|| !ASN1_OCTET_STRING_set(sk->privatePoint, buf, sizeof(buf))) {
+				SM9err(SM9_F_SM9_MASTER_KEY_EXTRACT_KEY, ERR_R_SM9_LIB);
+				point_cleanup(&de);
+				goto end;
+			}
+			point_cleanup(&de);
+		}
+	}
+
+	ret = sk;
+	sk = NULL;
+
+end:
+	SM9PrivateKey_free(sk);
+	EC_GROUP_clear_free(group);
+	if (ctx) {
+		BN_CTX_end(ctx);
+	}
+	BN_CTX_free(ctx);
+	BN_clear_free(t);
+	OPENSSL_cleanse(buf, sizeof(buf));
+	return ret;
+}
+
+SM9PublicKey *SM9_extract_public_key(SM9PublicParameters *master,
+	const char *id, size_t idlen)
+{
+	return SM9_MASTER_KEY_extract_key(master, id, idlen, 0);
+}
+
+SM9PrivateKey *SM9_extract_private_key(SM9MasterSecret *master,
+	const char *id, size_t idlen)
+{
+	return SM9_MASTER_KEY_extract_key(master, id, idlen, 1);
+}
+
+SM9PublicKey *SM9PrivateKey_get_public_key(SM9PrivateKey *sk)
+{
+	SM9PublicKey *ret = NULL;
+	SM9PublicKey *pk = NULL;
+
+	if (!(pk = SM9PublicKey_new())) {
+		return NULL;
+	}
+
+	ASN1_OBJECT_free(pk->pairing);
+	ASN1_OBJECT_free(pk->scheme);
+	ASN1_OBJECT_free(pk->hash1);
+	pk->pairing = NULL;
+	pk->scheme = NULL;
+	pk->hash1 = NULL;
+
+	if (!(pk->pairing = OBJ_dup(sk->pairing))
+		|| !(pk->scheme = OBJ_dup(sk->scheme))
+		|| !(pk->hash1 = OBJ_dup(sk->hash1))
+		|| !ASN1_STRING_copy(pk->pointPpub, sk->pointPpub)
+		|| !ASN1_STRING_copy(pk->publicPoint, sk->publicPoint)
+		|| !ASN1_STRING_copy(pk->identity, sk->identity)) {
+		goto end;
+	}
+
+	ret = pk;
+	pk = NULL;
+
+end:
+	SM9PublicKey_free(pk);
+	return ret;
+}
diff --git a/crypto/sm9/sm9_lcl.h b/crypto/sm9/sm9_lcl.h
new file mode 100755
index 000000000..471e531a6
--- /dev/null
+++ b/crypto/sm9/sm9_lcl.h
@@ -0,0 +1,332 @@
+/* ====================================================================
+ * Copyright (c) 2016 The GmSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the GmSSL Project.
+ *    (http://gmssl.org/)"
+ *
+ * 4. The name "GmSSL Project" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    guanzhi1980@gmail.com.
+ *
+ * 5. Products derived from this software may not be called "GmSSL"
+ *    nor may "GmSSL" appear in their names without prior written
+ *    permission of the GmSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the GmSSL Project
+ *    (http://gmssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE GmSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE GmSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+
+#ifndef HEADER_SM9_LCL_H
+#define HEADER_SM9_LCL_H
+
+#include 
+#include 
+#include "../../e_os.h"
+
+/* private key extract algorithms */
+#define SM9_HID_SIGN		0x01
+#define SM9_HID_EXCH		0x02
+#define SM9_HID_ENC		0x03
+
+#define SM9_HASH1		0x01
+#define SM9_HASH2		0x02
+
+
+/* Curve ID */
+/* non-supersingular curve over Fp */
+#define SM9_CID_TYPE0CURVE	0x10
+/* supersingular curve over Fp */
+#define SM9_CID_TYPE1CURVE	0x11
+/* twist curve over Fp */
+#define SM9_CID_TYPE2CURVE	0x12
+
+/* Pairing Type */
+#define SM9_EID_TATE		0x01
+#define SM9_EID_WEIL		0x02
+#define SM9_EID_ATE		0x03
+#define SM9_EID_R_ATE		0x04
+
+/* phi() with different embedded degree */
+#define SM9_PHI_D2		0x02
+#define SM9_PHI_D4		0x04
+#define SM9_PHI_D6		0x06
+
+
+#define SM9_MAX_PLAINTEXT_LENGTH	12800
+#define SM9_MAX_CIPHERTEXT_LENGTH 	25600
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+struct SM9_MASTER_KEY_st {
+	/* public */
+	ASN1_OBJECT *pairing;
+	ASN1_OBJECT *scheme;
+	ASN1_OBJECT *hash1;
+	ASN1_OCTET_STRING *pointPpub;
+
+	/* private */
+	BIGNUM *masterSecret;
+
+	int references;
+	int flags;
+	CRYPTO_EX_DATA ex_data;
+	CRYPTO_RWLOCK *lock;
+};
+
+struct SM9_KEY_st {
+	/* public */
+	ASN1_OBJECT *pairing;
+	ASN1_OBJECT *scheme;
+	ASN1_OBJECT *hash1;
+	ASN1_OCTET_STRING *pointPpub;
+	ASN1_OCTET_STRING *identity;
+	ASN1_OCTET_STRING *publicPoint;
+
+	/* private */
+	ASN1_OCTET_STRING *privatePoint;
+
+	int references;
+	int flags;
+	CRYPTO_EX_DATA ex_data;
+	CRYPTO_RWLOCK *lock;
+};
+
+struct SM9Ciphertext_st {
+	ASN1_OCTET_STRING *pointC1; /* point over E(F_p) */
+	ASN1_OCTET_STRING *c2; /* ciphertext */
+	ASN1_OCTET_STRING *c3; /* mac-tag */
+};
+
+struct SM9Signature_st {
+	BIGNUM *h; /* hash */
+	ASN1_OCTET_STRING *pointS; /* point over E'(F_p^2) */
+};
+
+int SM9_hash1(const EVP_MD *md, BIGNUM **r,
+	const char *id, size_t idlen, unsigned char hid,
+	const BIGNUM *range, BN_CTX *ctx);
+
+int SM9_hash2(const EVP_MD *md, BIGNUM **r,
+	const unsigned char *data, size_t datalen,
+	const unsigned char *elem, size_t elemlen,
+	const BIGNUM *range, BN_CTX *ctx);
+
+const BIGNUM *SM9_get0_prime(void);
+const BIGNUM *SM9_get0_order(void);
+const BIGNUM *SM9_get0_order_minus_one(void);
+const BIGNUM *SM9_get0_loop_count(void);
+const BIGNUM *SM9_get0_final_exponent(void);
+const BIGNUM *SM9_get0_fast_final_exponent_p20(void);
+const BIGNUM *SM9_get0_fast_final_exponent_p21(void);
+const BIGNUM *SM9_get0_fast_final_exponent_p22(void);
+const BIGNUM *SM9_get0_fast_final_exponent_p23(void);
+const BIGNUM *SM9_get0_fast_final_exponent_p3(void);
+const BIGNUM *SM9_get0_generator2_x0(void);
+const BIGNUM *SM9_get0_generator2_x1(void);
+const BIGNUM *SM9_get0_generator2_y0(void);
+const BIGNUM *SM9_get0_generator2_y1(void);
+
+typedef BIGNUM *fp2_t[2];
+
+int fp2_init(fp2_t a, BN_CTX *ctx);
+void fp2_cleanup(fp2_t a);
+void fp2_clear_cleanup(fp2_t a);
+int fp2_is_zero(const fp2_t a);
+int fp2_is_zero(const fp2_t a);
+int fp2_print(const fp2_t a);
+int fp2_is_one(const fp2_t a);
+void fp2_set_zero(fp2_t r);
+int fp2_set_one(fp2_t r);
+int fp2_copy(fp2_t r, const fp2_t a);
+int fp2_set(fp2_t r, const BIGNUM *a0, const BIGNUM *a1);
+int fp2_set_hex(fp2_t r, const char *str[2]);
+int fp2_set_u(fp2_t r);
+int fp2_set_5u(fp2_t r);
+int fp2_set_bn(fp2_t r, const BIGNUM *a);
+int fp2_set_word(fp2_t r, unsigned long a);
+int fp2_equ(const fp2_t a, const fp2_t b);
+int fp2_equ_hex(const fp2_t a, const char *str[2], BN_CTX *ctx);
+int fp2_add_word(fp2_t r, const fp2_t a, unsigned long b, const BIGNUM *p, BN_CTX *ctx);
+int fp2_add(fp2_t r, const fp2_t a, const fp2_t b, const BIGNUM *p, BN_CTX *ctx);
+int fp2_dbl(fp2_t r, const fp2_t a, const BIGNUM *p, BN_CTX *ctx);
+int fp2_tri(fp2_t r, const fp2_t a, const BIGNUM *p, BN_CTX *ctx);
+int fp2_sub(fp2_t r, const fp2_t a, const fp2_t b, const BIGNUM *p, BN_CTX *ctx);
+int fp2_neg(fp2_t r, const fp2_t a, const BIGNUM *p, BN_CTX *ctx);
+int fp2_mul(fp2_t r, const fp2_t a, const fp2_t b, const BIGNUM *p, BN_CTX *ctx);
+int fp2_mul_u(fp2_t r, const fp2_t a, const fp2_t b, const BIGNUM *p, BN_CTX *ctx);
+int fp2_mul_num(fp2_t r, const fp2_t a, const BIGNUM *n, const BIGNUM *p, BN_CTX *ctx);
+int fp2_sqr(fp2_t r, const fp2_t a, const BIGNUM *p, BN_CTX *ctx);
+int fp2_sqr_u(fp2_t r, const fp2_t a, const BIGNUM *p, BN_CTX *ctx);
+int fp2_inv(fp2_t r, const fp2_t a, const BIGNUM *p, BN_CTX *ctx);
+int fp2_div(fp2_t r, const fp2_t a, const fp2_t b, const BIGNUM *p, BN_CTX *ctx);
+int fp2_to_bin(const fp2_t a, unsigned char to[64]);
+int fp2_from_bin(fp2_t a, const unsigned char from[64]);
+int fp2_test(const BIGNUM *p, BN_CTX *ctx);
+
+
+typedef fp2_t fp4_t[2];
+
+int fp4_init(fp4_t a, BN_CTX *ctx);
+void fp4_cleanup(fp4_t a);
+void fp4_clear_cleanup(fp4_t a);
+int fp4_print(const fp4_t a);
+int fp4_is_zero(const fp4_t a);
+int fp4_is_one(const fp4_t a);
+void fp4_set_zero(fp4_t r);
+int fp4_set_one(fp4_t r);
+int fp4_set_bn(fp4_t r, const BIGNUM *a);
+int fp4_set_word(fp4_t r, unsigned long a);
+int fp4_set_fp2(fp4_t r, const fp2_t a);
+int fp4_set(fp4_t r, const fp2_t a0, const fp2_t a1);
+int fp4_set_hex(fp4_t r, const char *str[4]);
+int fp4_copy(fp4_t r, const fp4_t a);
+int fp4_set_u(fp4_t r);
+int fp4_set_v(fp4_t r);
+int fp4_equ(const fp4_t a, const fp4_t b);
+int fp4_equ_hex(const fp4_t a, const char *str[4], BN_CTX *ctx);
+int fp4_to_bin(const fp4_t a, unsigned char to[128]);
+int fp4_from_bin(fp4_t a, const unsigned char from[128]);
+int fp4_add(fp4_t r, const fp4_t a, const fp4_t b, const BIGNUM *p, BN_CTX *ctx);
+int fp4_dbl(fp4_t r, const fp4_t a, const BIGNUM *p, BN_CTX *ctx);
+int fp4_sub(fp4_t r, const fp4_t a, const fp4_t b, const BIGNUM *p, BN_CTX *ctx);
+int fp4_neg(fp4_t r, const fp4_t a, const BIGNUM *p, BN_CTX *ctx);
+int fp4_mul(fp4_t r, const fp4_t a, const fp4_t b, const BIGNUM *p, BN_CTX *ctx);
+int fp4_mul_v(fp4_t r, const fp4_t a, const fp4_t b, const BIGNUM *p, BN_CTX *ctx);
+int fp4_sqr(fp4_t r, const fp4_t a, const BIGNUM *p, BN_CTX *ctx);
+int fp4_sqr_v(fp4_t r, const fp4_t a, const BIGNUM *p, BN_CTX *ctx);
+int fp4_inv(fp4_t r, const fp4_t a, const BIGNUM *p, BN_CTX *ctx);
+int fp4_test(const BIGNUM *p, BN_CTX *ctx);
+
+
+typedef fp4_t fp12_t[3];
+
+int fp12_init(fp12_t a, BN_CTX *ctx);
+void fp12_cleanup(fp12_t a);
+void fp12_clear_cleanup(fp12_t a);
+int fp12_print(const fp12_t a);
+int fp12_is_zero(const fp12_t a);
+int fp12_is_one(const fp12_t a);
+void fp12_set_zero(fp12_t r);
+int fp12_set_one(fp12_t r);
+int fp12_copy(fp12_t r, const fp12_t a);
+int fp12_set(fp12_t r, const fp4_t a0, const fp4_t a1, const fp4_t a2);
+int fp12_set_hex(fp12_t r, const char *str[12]);
+int fp12_set_fp4(fp12_t r, const fp4_t a);
+int fp12_set_fp2(fp12_t r, const fp2_t a);
+int fp12_set_bn(fp12_t r, const BIGNUM *a);
+int fp12_set_word(fp12_t r, unsigned long a);
+int fp12_set_u(fp12_t r);
+int fp12_set_v(fp12_t r);
+int fp12_set_w(fp12_t r);
+int fp12_set_w_sqr(fp12_t r);
+int fp12_equ(const fp12_t a, const fp12_t b);
+int fp12_equ_hex(const fp12_t a, const char *str[12], BN_CTX *ctx);
+int fp12_to_bin(const fp12_t a, unsigned char to[384]);
+int fp12_from_bin(fp4_t a, const unsigned char from[384]);
+int fp12_add(fp12_t r, const fp12_t a, const fp12_t b, const BIGNUM *p, BN_CTX *ctx);
+int fp12_dbl(fp12_t r, const fp12_t a, const BIGNUM *p, BN_CTX *ctx);
+int fp12_tri(fp12_t r, const fp12_t a, const BIGNUM *p, BN_CTX *ctx);
+int fp12_sub(fp12_t r, const fp12_t a, const fp12_t b, const BIGNUM *p, BN_CTX *ctx);
+int fp12_neg(fp12_t r, const fp12_t a, const BIGNUM *p, BN_CTX *ctx);
+int fp12_mul(fp12_t r, const fp12_t a, const fp12_t b, const BIGNUM *p, BN_CTX *ctx);
+int fp12_sqr(fp12_t r, const fp12_t a, const BIGNUM *p, BN_CTX *ctx);
+int fp12_inv(fp12_t r, const fp12_t a, const BIGNUM *p, BN_CTX *ctx);
+int fp12_div(fp12_t r, const fp12_t a, const fp12_t b, const BIGNUM *p, BN_CTX *ctx);
+int fp12_pow(fp12_t r, const fp12_t a, const BIGNUM *k, const BIGNUM *p, BN_CTX *ctx);
+int fp12_fast_expo_p1(fp12_t r, const fp12_t a, const BIGNUM *p, BN_CTX *ctx);
+int fp12_fast_expo_p2(fp12_t r, const fp12_t a, const BIGNUM *p, BN_CTX *ctx);
+int fp12_test(const BIGNUM *p, BN_CTX *ctx);
+
+
+typedef struct point_t {
+	fp2_t X;
+	fp2_t Y;
+	fp2_t Z;
+} point_t;
+
+int point_init(point_t *P, BN_CTX *ctx);
+void point_cleanup(point_t *P);
+void point_print(const point_t *P);
+int point_copy(point_t *R, const point_t *P);
+int point_equ(const point_t *P, const point_t *Q);
+int point_equ_hex(const point_t *P, const char *str[4], BN_CTX *ctx);
+int point_is_at_infinity(const point_t *P);
+int point_set_to_infinity(point_t *P);
+int point_set_affine_coordinates(point_t *P, const fp2_t x, const fp2_t y);
+int point_set_affine_coordinates_hex(point_t *P, const char *str[4]);
+int point_set_affine_coordinates_bignums(point_t *P,
+	const BIGNUM *x0, const BIGNUM *x1, const BIGNUM *y0, const BIGNUM *y1);
+int point_get_affine_coordinates(const point_t *P, fp2_t x, fp2_t y);
+int point_get_ext_affine_coordinates(const point_t *P, fp12_t x, fp12_t y, const BIGNUM *p, BN_CTX *ctx);
+int point_set_ext_affine_coordinates(point_t *P, const fp12_t x, const fp12_t y, const BIGNUM *p, BN_CTX *ctx);
+int point_is_on_curve(point_t *P, const BIGNUM *p, BN_CTX *ctx);
+int point_to_octets(const point_t *P, unsigned char to[129], BN_CTX *ctx);
+int point_from_octets(point_t *P, const unsigned char from[129], const BIGNUM *p, BN_CTX *ctx);
+int point_add(point_t *R, const point_t *A, const point_t *B, const BIGNUM *p, BN_CTX *ctx);
+int point_dbl(point_t *R, const point_t *P, const BIGNUM *p, BN_CTX *ctx);
+int point_neg(point_t *R, const point_t *P, const BIGNUM *p, BN_CTX *ctx);
+int point_sub(point_t *R, const point_t *P, const point_t *Q, const BIGNUM *p, BN_CTX *ctx);
+int point_mul(point_t *R, const BIGNUM *k, const point_t *P, const BIGNUM *p, BN_CTX *ctx);
+int point_mul_generator(point_t *R, const BIGNUM *k, const BIGNUM *p, BN_CTX *ctx);
+
+
+int eval_tangent(fp12_t r, const point_t *T, const BIGNUM *xP, const BIGNUM *yP,
+	const BIGNUM *p, BN_CTX *ctx);
+int eval_line(fp12_t r,  const point_t *T, const point_t *Q,
+	const BIGNUM *xP, const BIGNUM *yP,
+	const BIGNUM *p, BN_CTX *ctx);
+int frobenius(point_t *R, const point_t *P, const BIGNUM *p, BN_CTX *ctx);
+int final_expo(fp12_t r, const fp12_t a, const BIGNUM *k, const BIGNUM *p, BN_CTX *ctx);
+int fast_final_expo(fp12_t r, const fp12_t a, const BIGNUM *k, const BIGNUM *p, BN_CTX *ctx);
+int rate(fp12_t f, const point_t *Q, const BIGNUM *xP, const BIGNUM *yP,
+	const BIGNUM *a, const BIGNUM *k, const BIGNUM *p, BN_CTX *ctx);
+int rate_test(void);
+int rate_pairing(fp12_t r, const point_t *Q, const EC_POINT *P, BN_CTX *ctx);
+
+int params_test(void);
+
+int sm9_check_pairing(int nid);
+int sm9_check_scheme(int nid);
+int sm9_check_hash1(int nid);
+int sm9_check_encrypt_scheme(int nid);
+int sm9_check_sign_scheme(int nid);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/crypto/sm9/sm9_lib.c b/crypto/sm9/sm9_lib.c
new file mode 100755
index 000000000..dca400e1b
--- /dev/null
+++ b/crypto/sm9/sm9_lib.c
@@ -0,0 +1,342 @@
+/* ====================================================================
+ * Copyright (c) 2016 The GmSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the GmSSL Project.
+ *    (http://gmssl.org/)"
+ *
+ * 4. The name "GmSSL Project" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    guanzhi1980@gmail.com.
+ *
+ * 5. Products derived from this software may not be called "GmSSL"
+ *    nor may "GmSSL" appear in their names without prior written
+ *    permission of the GmSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the GmSSL Project
+ *    (http://gmssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE GmSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE GmSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include "../bn/bn_lcl.h"
+#include "sm9_lcl.h"
+
+#if 0
+typedef struct {
+	int nid;
+	int nid;
+} sm9_algor_table;
+
+
+static const sm9_algor_table sm9encrypt_scheme_table[] = {
+	{NID_sm9encrypt_with_sm3, NID_sm3},
+	{NID_sm9encrypt_with_sha256, NID_sha256},
+};
+
+static const sm9_algoro_table sm9sign_scheme_table[] = {
+	{NID_sm9sign_with_sm3, NID_sm3},
+	{NID_sm9sign_with_sha256, NID_sha256},
+};
+
+static const sm9_algor_table sm9_encrypt_table[] = {
+	{sm9encrypt-with-sm3-xor, NID_sm3, NID_undef},
+	{sm9encrypt-with-sm3-sms4-cbc, NID_sm3, NID_sms4_cbc},
+	{sm9encrypt-with-sm3-sms4-ctr, NID_sm3, NID_sms4_ctr},
+};
+
+static const sm9_algor sm9_hash1[] = {
+	{NID_sm9hash1_with_sm3, NID_sm3},
+	{NID_sm9hash1_with_sha256, NID_sha256},
+	{NID_sm9kdf_with_sm3, NID_sm3},
+	{NID_sm9kdf_with_sha256, NID_sha256},
+};
+#endif
+
+
+SM9_MASTER_KEY *SM9_MASTER_KEY_new(void)
+{
+	SM9_MASTER_KEY *ret = NULL;
+
+	if (!(ret = OPENSSL_zalloc(sizeof(*ret)))) {
+		SM9err(SM9_F_SM9_MASTER_KEY_NEW, ERR_R_MALLOC_FAILURE);
+		return NULL;
+	}
+
+	return ret;
+}
+
+void SM9_MASTER_KEY_free(SM9_MASTER_KEY *key)
+{
+	if (key) {
+		ASN1_OBJECT_free(key->pairing);
+		ASN1_OBJECT_free(key->scheme);
+		ASN1_OBJECT_free(key->hash1);
+		ASN1_OCTET_STRING_free(key->pointPpub);
+		BN_clear_free(key->masterSecret);
+	}
+	OPENSSL_clear_free(key, sizeof(*key));
+}
+
+SM9_KEY *SM9_KEY_new(void)
+{
+	SM9_KEY *ret = NULL;
+
+	if (!(ret = OPENSSL_zalloc(sizeof(*ret)))) {
+		SM9err(SM9_F_SM9_KEY_NEW, ERR_R_MALLOC_FAILURE);
+		return NULL;
+	}
+
+	return ret;
+}
+
+void SM9_KEY_free(SM9_KEY *key)
+{
+	if (key) {
+		ASN1_OBJECT_free(key->pairing);
+		ASN1_OBJECT_free(key->scheme);
+		ASN1_OBJECT_free(key->hash1);
+		ASN1_OCTET_STRING_free(key->pointPpub);
+		ASN1_OCTET_STRING_free(key->identity);
+		ASN1_OCTET_STRING_free(key->publicPoint);
+	}
+	OPENSSL_clear_free(key, sizeof(*key));
+}
+
+
+int SM9PrivateKey_get_gmtls_public_key(SM9PublicParameters *mpk,
+	SM9PrivateKey *sk, unsigned char pub_key[1024])
+{
+	/* FIXME */
+	(void)mpk;
+	(void)sk;
+	(void)pub_key;
+
+	return 0;
+}
+
+int SM9PublicKey_get_gmtls_encoded(SM9PublicParameters *mpk,
+	SM9PublicKey *pk, unsigned char encoded[1024])
+{
+	/* FIXME */
+	(void)mpk;
+	(void)pk;
+	(void)encoded;
+
+	return 0;
+}
+
+
+int SM9_DigestInit(EVP_MD_CTX *ctx, unsigned char prefix,
+	const EVP_MD *md, ENGINE *impl)
+{
+	if (!EVP_DigestInit_ex(ctx, md, impl)
+		|| !EVP_DigestUpdate(ctx, &prefix, 1)) {
+		ERR_print_errors_fp(stderr);
+		return 0;
+	}
+	return 1;
+}
+
+int SM9_MASTER_KEY_up_ref(SM9_MASTER_KEY *msk)
+{
+	int i;
+
+	if (CRYPTO_atomic_add(&msk->references, 1,  &i, msk->lock) <= 0)
+		return 0;
+
+	REF_PRINT_COUNT("SM9_MASTER_KEY", msk);
+	REF_ASSERT_ISNT(i < 2);
+	return ((i > 1) ? 1 : 0);
+}
+
+int SM9_KEY_up_ref(SM9_KEY *sk)
+{
+	int i;
+
+	if (CRYPTO_atomic_add(&sk->references, 1,  &i, sk->lock) <= 0)
+		return 0;
+
+	REF_PRINT_COUNT("SM9_KEY", sk);
+	REF_ASSERT_ISNT(i < 2);
+	return ((i > 1) ? 1 : 0);
+}
+
+int sm9_check_pairing(int nid)
+{
+	/* FIXME */
+	(void)nid;
+
+	return 1;
+}
+
+int sm9_check_scheme(int nid)
+{
+	/* FIXME */
+	(void)nid;
+
+	return 1;
+}
+
+int sm9_check_hash1(int nid)
+{
+	/* FIXME */
+	(void)nid;
+
+	return 1;
+}
+
+int sm9_check_encrypt_scheme(int nid)
+{
+	/* FIXME */
+	(void)nid;
+
+	return 1;
+}
+
+int sm9_check_sign_scheme(int nid)
+{
+	/* FIXME */
+	(void)nid;
+
+	return 1;
+}
+
+/* SM9_hash2() should be implemented as an EVP_MD module
+ * and refactor the SM9_SignInit/Update/Final API
+ */
+#if 0
+int BN_hash_to_range(const EVP_MD *md, BIGNUM **bn,
+	const void *s, size_t slen, const BIGNUM *range, BN_CTX *bn_ctx)
+{
+	int ret = 0;
+	BIGNUM *r = NULL;
+	BIGNUM *a = NULL;
+	unsigned char *buf = NULL;
+	size_t buflen, mdlen;
+	int nbytes, rounds, i;
+
+	if (!s || slen <= 0 || !md || !range) {
+		//BNerr(BN_F_BN_HASH_TO_RANGE, ERR_R_PASSED_NULL_PARAMETER);
+		return 0;
+	}
+
+	if (!(*bn)) {
+		if (!(r = BN_new())) {
+			//BNerr(BN_F_BN_HASH_TO_RANGE, ERR_R_MALLOC_FAILURE);
+			return 0;
+		}
+	} else {
+		r = *bn;
+		BN_zero(r);
+	}
+
+	mdlen = EVP_MD_size(md);
+	buflen = mdlen + slen;
+	if (!(buf = OPENSSL_malloc(buflen))) {
+		//BNerr(BN_F_BN_HASH_TO_RANGE, ERR_R_MALLOC_FAILURE);
+		goto end;
+	}
+	memset(buf, 0, mdlen);
+	memcpy(buf + mdlen, s, slen);
+
+	a = BN_new();
+	if (!a) {
+		//BNerr(BN_F_BN_HASH_TO_RANGE, ERR_R_MALLOC_FAILURE);
+		goto end;
+	}
+
+	nbytes = BN_num_bytes(range);
+	rounds = (nbytes + mdlen - 1)/mdlen;
+
+	if (!bn_expand(r, rounds * mdlen * 8)) {
+		//BNerr(BN_F_BN_HASH_TO_RANGE, ERR_R_BN_LIB);
+		goto end;
+	}
+
+	for (i = 0; i < rounds; i++) {
+		if (!EVP_Digest(buf, buflen, buf, (unsigned int *)&mdlen, md, NULL)) {
+			//BNerr(BN_F_BN_HASH_TO_RANGE, ERR_R_EVP_LIB);
+			goto end;
+		}
+		if (!BN_bin2bn(buf, mdlen, a)) {
+			//BNerr(BN_F_BN_HASH_TO_RANGE, ERR_R_BN_LIB);
+			goto end;
+		}
+		if (!BN_lshift(r, r, mdlen * 8)) {
+			//BNerr(BN_F_BN_HASH_TO_RANGE, ERR_R_BN_LIB);
+			goto end;
+		}
+		if (!BN_uadd(r, r, a)) {
+			goto end;
+		}
+	}
+
+	if (!BN_mod(r, r, range, bn_ctx)) {
+		//BNerr(BN_F_BN_HASH_TO_RANGE, ERR_R_BN_LIB);
+		goto end;
+	}
+
+	*bn = r;
+	ret = 1;
+end:
+	if (!ret && !(*bn)) {
+		BN_free(r);
+	}
+	BN_free(a);
+	OPENSSL_free(buf);
+	return ret;
+}
+
+int SM9_hash2(const EVP_MD *md, BIGNUM **r,
+	const unsigned char *data, size_t datalen,
+	const unsigned char *elem, size_t elemlen,
+	const BIGNUM *range, BN_CTX *ctx)
+{
+	EVP_MD_CTX *mctx = NULL;
+
+	if (!(mctx = EVP_MD_CTX_new())) {
+	}
+
+	if (!EVP_DigestInit_ex(mctx, md, NULL)
+		|| !EVP_DigestUpdate(mctx, data, datalen)
+		|| !EVP_DigestUpdate(mctx, elem, elemlen)
+		|| !EVP_DigestFinal_ex(mctx, buf, &buflen)) {
+	}
+
+	
+
+}
+#endif
diff --git a/crypto/sm9/sm9_params.c b/crypto/sm9/sm9_params.c
new file mode 100755
index 000000000..967edd508
--- /dev/null
+++ b/crypto/sm9/sm9_params.c
@@ -0,0 +1,410 @@
+/* ====================================================================
+ * Copyright (c) 2016 The GmSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the GmSSL Project.
+ *    (http://gmssl.org/)"
+ *
+ * 4. The name "GmSSL Project" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    guanzhi1980@gmail.com.
+ *
+ * 5. Products derived from this software may not be called "GmSSL"
+ *    nor may "GmSSL" appear in their names without prior written
+ *    permission of the GmSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the GmSSL Project
+ *    (http://gmssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE GmSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE GmSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+
+#include "../bn/bn_lcl.h"
+#include "internal/cryptlib.h"
+
+
+#define BN_SM9_BN256_TOP (256+BN_BITS2-1)/BN_BITS2
+#define BN_SM9_LOOP_TOP (66+BN_BITS2-1)/BN_BITS2
+#define BN_SM9_FINAL_EXPO_TOP (2816+BN_BITS2-1)/BN_BITS2
+#define BN_SM9_FAST_FINAL_EXPO_P2_TOP (256+BN_BITS2-1)/BN_BITS2
+#define BN_SM9_FAST_FINAL_EXPO_P3_TOP (768+BN_BITS2-1)/BN_BITS2
+
+#if BN_BITS2 == 64
+static const BN_ULONG _sm9bn256v1_prime[BN_SM9_BN256_TOP] = {
+	0xE56F9B27E351457DULL, 0x21F2934B1A7AEEDBULL,
+	0xD603AB4FF58EC745ULL, 0xB640000002A3A6F1ULL,
+};
+
+static const BN_ULONG _sm9bn256v1_order[BN_SM9_BN256_TOP] = {
+	0xE56EE19CD69ECF25ULL, 0x49F2934B18EA8BEEULL,
+	0xD603AB4FF58EC744ULL, 0xB640000002A3A6F1ULL,
+};
+
+static const BN_ULONG _sm9bn256v1_order_minus_one[BN_SM9_BN256_TOP] = {
+	0xE56EE19CD69ECF24ULL, 0x49F2934B18EA8BEEULL,
+	0xD603AB4FF58EC744ULL, 0xB640000002A3A6F1ULL,
+};
+
+static const BN_ULONG _sm9bn256v1_loop[BN_SM9_LOOP_TOP] = {
+	0x400000000215D93EULL, 0x02ULL,
+};
+
+static const BN_ULONG _sm9bn256v1_x2[][BN_SM9_BN256_TOP] = {
+	{0xF9B7213BAF82D65BULL, 0xEE265948D19C17ABULL,
+	 0xD2AAB97FD34EC120ULL, 0x3722755292130B08ULL},
+	{0x54806C11D8806141ULL, 0xF1DD2C190F5E93C4ULL,
+	 0x597B6027B441A01FULL, 0x85AEF3D078640C98ULL}
+};
+
+static const BN_ULONG _sm9bn256v1_y2[][BN_SM9_BN256_TOP] = {
+	{0x6215BBA5C999A7C7ULL, 0x47EFBA98A71A0811ULL,
+	 0x5F3170153D278FF2ULL, 0xA7CF28D519BE3DA6ULL},
+	{0x856DC76B84EBEB96ULL, 0x0736A96FA347C8BDULL,
+	 0x66BA0D262CBEE6EDULL, 0x17509B092E845C12ULL}
+};
+
+static const BN_ULONG _sm9bn256v1_final_expo[BN_SM9_FINAL_EXPO_TOP] = {
+	0x2FACDD0F0D042330ULL, 0xB19FE1764AC0B748ULL,
+	0xC6135FEC864E1676ULL, 0x8E4890AB7E824DC7ULL,
+	0x5B83E3D7E2D0B969ULL, 0xCBD806A8225955B0ULL,
+	0x6FE5E935A4B5F799ULL, 0x0B48EE8FD4C31B8FULL,
+	0x8478B1A3E843D1FAULL, 0xBB6793F9FE39F256ULL,
+	0xB760558913D719F2ULL, 0xEFE915CAB1B62D11ULL,
+	0x1D16182C1D978AD0ULL, 0x5BBD6C4AA78C62F3ULL,
+	0xF703B00C9E53535AULL, 0x0F4C6983D64089BDULL,
+	0xC3E3945F87BF2203ULL, 0xE804F361D729E88DULL,
+	0x6B948EBD4AA170BAULL, 0x4550E98DCA2042C3ULL,
+	0x5A54D51E82F7DD63ULL, 0x2FB14FC5412315ECULL,
+	0xE2C3601D19F32C69ULL, 0x335D59C358AAC66EULL,
+	0x96FD3135583D5AA1ULL, 0xB78BD480FF56FA06ULL,
+	0xA4DEFA394C04689AULL, 0xCB5A6B4B9EC19BA7ULL,
+	0x80357732739295EDULL, 0x3BED65F00632C9CCULL,
+	0x93DC562FA23AFA5AULL, 0xF1C8D7D0598EEE9BULL,
+	0xB97559B180EE9629ULL, 0x5ED57CD410455806ULL,
+	0x239E0CB2A1387366ULL, 0x96F691269CEB7907ULL,
+	0x93B5122D974B7BA1ULL, 0xBE8CB0476C5042CBULL,
+	0x9BF41BCB4067AA64ULL, 0xDD9D1D3019DBA153ULL,
+	0xC16BE7FBACA54D38ULL, 0xACEF6F4E86411255ULL,
+	0x1A09A6AE43ADE454ULL, 0x061835E8B1259499ULL,
+};
+
+static const BN_ULONG _sm9bn256v1_fast_final_expo_p2[][BN_SM9_FAST_FINAL_EXPO_P2_TOP] = {
+	{0xD5FC11967BE65334ULL, 0x780272354F8B78F4ULL,
+	 0xF300000002A3A6F2ULL},
+	{0x0F738991676AF249ULL, 0xA9F02115CAEF75E7ULL,
+	 0xE303AB4FF2EB2052ULL, 0xB640000002A3A6F0ULL},
+	{0xD5FC11967BE65333ULL, 0x780272354F8B78F4ULL,
+	 0xF300000002A3A6F2ULL},
+	{0x0F738991676AF24AULL, 0xA9F02115CAEF75E7ULL,
+	 0xE303AB4FF2EB2052ULL, 0xB640000002A3A6F0ULL}
+};
+
+static const BN_ULONG _sm9bn256v1_fast_final_expo_p3[BN_SM9_FAST_FINAL_EXPO_P3_TOP] = {
+	0xA9B2ADA593152855ULL, 0x44BF9D0FA74DDFB7ULL,
+	0x83687EE0C6D9188CULL, 0xE0D49DE3AA8A4748ULL,
+	0x0DA3D71BCDB13FE5ULL, 0xA5782C82FDB6B0A1ULL,
+	0x7C0CA02D9B0D8649ULL, 0xBA4CADE09029E471ULL,
+	0xDC53E586930846F1ULL, 0xD62CD8FB7B497A0AULL,
+	0xF12FCAD3B31FE2B0ULL, 0x5C5E452404034E2AULL,
+};
+
+#elif BN_BITS2 == 32
+static const BN_ULONG _sm9bn256v1_prime[BN_SM9_BN256_TOP] = {
+	0xE351457D, 0xE56F9B27, 0x1A7AEEDB, 0x21F2934B,
+	0xF58EC745, 0xD603AB4F, 0x02A3A6F1, 0xB6400000,
+};
+
+static const BN_ULONG _sm9bn256v1_order[BN_SM9_BN256_TOP] = {
+	0xD69ECF25, 0xE56EE19C, 0x18EA8BEE, 0x49F2934B,
+	0xF58EC744, 0xD603AB4F, 0x02A3A6F1, 0xB6400000,
+};
+
+static const BN_ULONG _sm9bn256v1_order_minus_one[BN_SM9_BN256_TOP] = {
+	0xD69ECF24, 0xE56EE19C, 0x18EA8BEE, 0x49F2934B,
+	0xF58EC744, 0xD603AB4F, 0x02A3A6F1, 0xB6400000,
+};
+
+static const BN_ULONG _sm9bn256v1_loop[BN_SM9_LOOP_TOP] = {
+	0x0215D93E, 0x40000000, 0x02,
+};
+
+static const BN_ULONG _sm9bn256v1_x2[][BN_SM9_BN256_TOP] = {
+	{0xAF82D65B, 0xF9B7213B, 0xD19C17AB, 0xEE265948,
+	 0xD34EC120, 0xD2AAB97F, 0x92130B08, 0x37227552},
+	{0xD8806141, 0x54806C11, 0x0F5E93C4, 0xF1DD2C19,
+	 0xB441A01F, 0x597B6027, 0x78640C98, 0x85AEF3D0}
+};
+
+static const BN_ULONG _sm9bn256v1_y2[][BN_SM9_BN256_TOP] = {
+	{0xC999A7C7, 0x6215BBA5, 0xA71A0811, 0x47EFBA98,
+	 0x3D278FF2, 0x5F317015, 0x19BE3DA6, 0xA7CF28D5},
+	{0x84EBEB96, 0x856DC76B, 0xA347C8BD, 0x0736A96F,
+	 0x2CBEE6ED, 0x66BA0D26, 0x2E845C12, 0x17509B09}
+};
+
+static const BN_ULONG _sm9bn256v1_final_expo[BN_SM9_FINAL_EXPO_TOP] = {
+	0x0D042330, 0x2FACDD0F, 0x4AC0B748, 0xB19FE176,
+	0x864E1676, 0xC6135FEC, 0x7E824DC7, 0x8E4890AB,
+	0xE2D0B969, 0x5B83E3D7, 0x225955B0, 0xCBD806A8,
+	0xA4B5F799, 0x6FE5E935, 0xD4C31B8F, 0x0B48EE8F,
+	0xE843D1FA, 0x8478B1A3, 0xFE39F256, 0xBB6793F9,
+	0x13D719F2, 0xB7605589, 0xB1B62D11, 0xEFE915CA,
+	0x1D978AD0, 0x1D16182C, 0xA78C62F3, 0x5BBD6C4A,
+	0x9E53535A, 0xF703B00C, 0xD64089BD, 0x0F4C6983,
+	0x87BF2203, 0xC3E3945F, 0xD729E88D, 0xE804F361,
+	0x4AA170BA, 0x6B948EBD, 0xCA2042C3, 0x4550E98D,
+	0x82F7DD63, 0x5A54D51E, 0x412315EC, 0x2FB14FC5,
+	0x19F32C69, 0xE2C3601D, 0x58AAC66E, 0x335D59C3,
+	0x583D5AA1, 0x96FD3135, 0xFF56FA06, 0xB78BD480,
+	0x4C04689A, 0xA4DEFA39, 0x9EC19BA7, 0xCB5A6B4B,
+	0x739295ED, 0x80357732, 0x0632C9CC, 0x3BED65F0,
+	0xA23AFA5A, 0x93DC562F, 0x598EEE9B, 0xF1C8D7D0,
+	0x80EE9629, 0xB97559B1, 0x10455806, 0x5ED57CD4,
+	0xA1387366, 0x239E0CB2, 0x9CEB7907, 0x96F69126,
+	0x974B7BA1, 0x93B5122D, 0x6C5042CB, 0xBE8CB047,
+	0x4067AA64, 0x9BF41BCB, 0x19DBA153, 0xDD9D1D30,
+	0xACA54D38, 0xC16BE7FB, 0x86411255, 0xACEF6F4E,
+	0x43ADE454, 0x1A09A6AE, 0xB1259499, 0x061835E8,
+};
+
+static const BN_ULONG _sm9bn256v1_fast_final_expo_p2[][BN_SM9_FAST_FINAL_EXPO_P2_TOP] = {
+	{0x7BE65334, 0xD5FC1196, 0x4F8B78F4, 0x78027235,
+	 0x02A3A6F2, 0xF3000000},
+	{0x676AF249, 0x0F738991, 0xCAEF75E7, 0xA9F02115,
+	 0xF2EB2052, 0xE303AB4F, 0x02A3A6F0, 0xB6400000},
+	{0x7BE65333, 0xD5FC1196, 0x4F8B78F4, 0x78027235,
+	 0x02A3A6F2, 0xF3000000},
+	{0x676AF24A, 0x0F738991, 0xCAEF75E7, 0xA9F02115,
+	 0xF2EB2052, 0xE303AB4F, 0x02A3A6F0, 0xB6400000}
+};
+
+static const BN_ULONG _sm9bn256v1_fast_final_expo_p3[BN_SM9_FAST_FINAL_EXPO_P3_TOP] = {
+	0x93152855, 0xA9B2ADA5, 0xA74DDFB7, 0x44BF9D0F,
+	0xC6D9188C, 0x83687EE0, 0xAA8A4748, 0xE0D49DE3,
+	0xCDB13FE5, 0x0DA3D71B, 0xFDB6B0A1, 0xA5782C82,
+	0x9B0D8649, 0x7C0CA02D, 0x9029E471, 0xBA4CADE0,
+	0x930846F1, 0xDC53E586, 0x7B497A0A, 0xD62CD8FB,
+	0xB31FE2B0, 0xF12FCAD3, 0x04034E2A, 0x5C5E4524,
+};
+
+#else
+# error "unsupported BN_BITS2"
+#endif
+
+static const BIGNUM _bignum_sm9bn256v1_prime = {
+	(BN_ULONG *)_sm9bn256v1_prime,
+	BN_SM9_BN256_TOP,
+	BN_SM9_BN256_TOP,
+	0,
+	BN_FLG_STATIC_DATA
+};
+
+static const BIGNUM _bignum_sm9bn256v1_order = {
+	(BN_ULONG *)_sm9bn256v1_order,
+	BN_SM9_BN256_TOP,
+	BN_SM9_BN256_TOP,
+	0,
+	BN_FLG_STATIC_DATA
+};
+
+static const BIGNUM _bignum_sm9bn256v1_order_minus_one = {
+	(BN_ULONG *)_sm9bn256v1_order_minus_one,
+	BN_SM9_BN256_TOP,
+	BN_SM9_BN256_TOP,
+	0,
+	BN_FLG_STATIC_DATA
+};
+
+static const BIGNUM _bignum_sm9bn256v1_loop = {
+	(BN_ULONG *)_sm9bn256v1_loop,
+	BN_SM9_LOOP_TOP,
+	BN_SM9_LOOP_TOP,
+	0,
+	BN_FLG_STATIC_DATA
+};
+
+static const BIGNUM _bignum_sm9bn256v1_final_expo = {
+	(BN_ULONG *)_sm9bn256v1_final_expo,
+	BN_SM9_FINAL_EXPO_TOP,
+	BN_SM9_FINAL_EXPO_TOP,
+	0,
+	BN_FLG_STATIC_DATA
+};
+
+static const BIGNUM _bignum_sm9bn256v1_fast_final_expo_p20 = {
+	(BN_ULONG *)_sm9bn256v1_fast_final_expo_p2[0],
+	BN_SM9_FAST_FINAL_EXPO_P2_TOP,
+	BN_SM9_FAST_FINAL_EXPO_P2_TOP,
+	0,
+	BN_FLG_STATIC_DATA
+};
+
+static const BIGNUM _bignum_sm9bn256v1_fast_final_expo_p21 = {
+	(BN_ULONG *)_sm9bn256v1_fast_final_expo_p2[1],
+	BN_SM9_FAST_FINAL_EXPO_P2_TOP,
+	BN_SM9_FAST_FINAL_EXPO_P2_TOP,
+	0,
+	BN_FLG_STATIC_DATA
+};
+
+static const BIGNUM _bignum_sm9bn256v1_fast_final_expo_p22 = {
+	(BN_ULONG *)_sm9bn256v1_fast_final_expo_p2[2],
+	BN_SM9_FAST_FINAL_EXPO_P2_TOP,
+	BN_SM9_FAST_FINAL_EXPO_P2_TOP,
+	0,
+	BN_FLG_STATIC_DATA
+};
+
+static const BIGNUM _bignum_sm9bn256v1_fast_final_expo_p23 = {
+	(BN_ULONG *)_sm9bn256v1_fast_final_expo_p2[3],
+	BN_SM9_FAST_FINAL_EXPO_P2_TOP,
+	BN_SM9_FAST_FINAL_EXPO_P2_TOP,
+	0,
+	BN_FLG_STATIC_DATA
+};
+
+static const BIGNUM _bignum_sm9bn256v1_fast_final_expo_p3 = {
+	(BN_ULONG *)_sm9bn256v1_fast_final_expo_p3,
+	BN_SM9_FAST_FINAL_EXPO_P3_TOP,
+	BN_SM9_FAST_FINAL_EXPO_P3_TOP,
+	0,
+	BN_FLG_STATIC_DATA
+};
+
+static const BIGNUM _bignum_sm9bn256v1_x20 = {
+	(BN_ULONG *)_sm9bn256v1_x2[0],
+	BN_SM9_BN256_TOP,
+	BN_SM9_BN256_TOP,
+	0,
+	BN_FLG_STATIC_DATA
+};
+
+static const BIGNUM _bignum_sm9bn256v1_x21 = {
+	(BN_ULONG *)_sm9bn256v1_x2[1],
+	BN_SM9_BN256_TOP,
+	BN_SM9_BN256_TOP,
+	0,
+	BN_FLG_STATIC_DATA
+};
+
+static const BIGNUM _bignum_sm9bn256v1_y20 = {
+	(BN_ULONG *)_sm9bn256v1_y2[0],
+	BN_SM9_BN256_TOP,
+	BN_SM9_BN256_TOP,
+	0,
+	BN_FLG_STATIC_DATA
+};
+
+static const BIGNUM _bignum_sm9bn256v1_y21 = {
+	(BN_ULONG *)_sm9bn256v1_y2[1],
+	BN_SM9_BN256_TOP,
+	BN_SM9_BN256_TOP,
+	0,
+	BN_FLG_STATIC_DATA
+};
+
+const BIGNUM *SM9_get0_generator2_x0(void)
+{
+	return &_bignum_sm9bn256v1_x20;
+}
+
+const BIGNUM *SM9_get0_generator2_x1(void)
+{
+	return &_bignum_sm9bn256v1_x21;
+}
+
+const BIGNUM *SM9_get0_generator2_y0(void)
+{
+	return &_bignum_sm9bn256v1_y20;
+}
+
+const BIGNUM *SM9_get0_generator2_y1(void)
+{
+	return &_bignum_sm9bn256v1_y21;
+}
+
+const BIGNUM *SM9_get0_prime(void)
+{
+	return &_bignum_sm9bn256v1_prime;
+}
+
+const BIGNUM *SM9_get0_order(void)
+{
+	return &_bignum_sm9bn256v1_order;
+}
+
+const BIGNUM *SM9_get0_order_minus_one(void)
+{
+	return &_bignum_sm9bn256v1_order_minus_one;
+}
+
+const BIGNUM *SM9_get0_loop_count(void)
+{
+	return &_bignum_sm9bn256v1_loop;
+}
+
+const BIGNUM *SM9_get0_final_exponent(void)
+{
+	return &_bignum_sm9bn256v1_final_expo;
+}
+
+const BIGNUM *SM9_get0_fast_final_exponent_p20(void)
+{
+	return &_bignum_sm9bn256v1_fast_final_expo_p20;
+}
+
+const BIGNUM *SM9_get0_fast_final_exponent_p21(void)
+{
+	return &_bignum_sm9bn256v1_fast_final_expo_p21;
+}
+
+const BIGNUM *SM9_get0_fast_final_exponent_p22(void)
+{
+	return &_bignum_sm9bn256v1_fast_final_expo_p22;
+}
+
+const BIGNUM *SM9_get0_fast_final_exponent_p23(void)
+{
+	return &_bignum_sm9bn256v1_fast_final_expo_p23;
+}
+
+const BIGNUM *SM9_get0_fast_final_exponent_p3(void)
+{
+	return &_bignum_sm9bn256v1_fast_final_expo_p3;
+}
diff --git a/crypto/sm9/sm9_pmeth.c b/crypto/sm9/sm9_pmeth.c
new file mode 100755
index 000000000..60ee2bc1f
--- /dev/null
+++ b/crypto/sm9/sm9_pmeth.c
@@ -0,0 +1,502 @@
+/* ====================================================================
+ * Copyright (c) 2015 - 2018 The GmSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the GmSSL Project.
+ *    (http://gmssl.org/)"
+ *
+ * 4. The name "GmSSL Project" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    guanzhi1980@gmail.com.
+ *
+ * 5. Products derived from this software may not be called "GmSSL"
+ *    nor may "GmSSL" appear in their names without prior written
+ *    permission of the GmSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the GmSSL Project
+ *    (http://gmssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE GmSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE GmSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include "internal/cryptlib.h"
+#include "internal/asn1_int.h"
+#include "internal/evp_int.h"
+#include "sm9_lcl.h"
+
+typedef struct {
+	int pairing; /* NID_sm9bn256v1 */
+	int scheme; /* NID_sm9[sign|encrypt|keyagreement] */
+	int hash1; /* NID_sm9hash1_with_[sm3|sha256] */
+	int sign_scheme; /* NID_sm9sign_with_[sm3|sha256] */
+	int encrypt_scheme; /*NID_sm9encrypt */
+	char *id;
+} SM9_MASTER_PKEY_CTX;
+
+static int pkey_sm9_master_init(EVP_PKEY_CTX *ctx)
+{
+	SM9_MASTER_PKEY_CTX *dctx;
+	if (!(dctx = OPENSSL_zalloc(sizeof(*dctx)))) {
+		SM9err(SM9_F_PKEY_SM9_MASTER_INIT, ERR_R_MALLOC_FAILURE);
+		return 0;
+	}
+
+	dctx->pairing = NID_sm9bn256v1;
+	dctx->scheme = NID_sm9encrypt;
+	dctx->hash1 = NID_sm9hash1_with_sm3;
+	dctx->sign_scheme = NID_sm3;
+	dctx->encrypt_scheme = NID_sm9encrypt_with_sm3_xor;
+	dctx->id = NULL;
+
+	ctx->data = dctx;
+	return 1;
+}
+
+static int pkey_sm9_master_copy(EVP_PKEY_CTX *dst, EVP_PKEY_CTX *src)
+{
+	SM9_MASTER_PKEY_CTX *dctx, *sctx;
+	if (!pkey_sm9_master_init(dst))
+		return 0;
+	sctx = src->data;
+	dctx = dst->data;
+	*dctx = *sctx;
+	if (!(dctx->id = OPENSSL_strdup(sctx->id))) {
+		SM9err(SM9_F_PKEY_SM9_MASTER_COPY, ERR_R_MALLOC_FAILURE);
+		return 0;
+	}
+	return 1;
+}
+
+static void pkey_sm9_master_cleanup(EVP_PKEY_CTX *ctx)
+{
+	SM9_MASTER_PKEY_CTX *dctx = EVP_PKEY_CTX_get_data(ctx);
+	if (dctx) {
+		OPENSSL_free(dctx->id);
+		OPENSSL_free(dctx);
+	}
+}
+
+static int pkey_sm9_master_keygen(EVP_PKEY_CTX *ctx, EVP_PKEY *pkey)
+{
+	SM9_MASTER_PKEY_CTX *dctx = EVP_PKEY_CTX_get_data(ctx);
+	SM9_MASTER_KEY *sm9_master;
+
+	if (!(sm9_master = SM9_generate_master_secret(dctx->pairing,
+		dctx->scheme, dctx->hash1))) {
+		SM9err(SM9_F_PKEY_SM9_MASTER_KEYGEN, ERR_R_SM9_LIB);
+		return 0;
+	}
+	if (!EVP_PKEY_assign_SM9_MASTER(pkey, sm9_master)) {
+		SM9err(SM9_F_PKEY_SM9_MASTER_KEYGEN, ERR_R_EVP_LIB);
+		SM9_MASTER_KEY_free(sm9_master);
+		return 0;
+	}
+	return 1;
+}
+
+static int pkey_sm9_master_verify(EVP_PKEY_CTX *ctx,
+	const unsigned char *sig, size_t siglen,
+	const unsigned char *tbs, size_t tbslen)
+{
+	int ret;
+	SM9_MASTER_PKEY_CTX *dctx = EVP_PKEY_CTX_get_data(ctx);
+	SM9_MASTER_KEY *sm9_master = EVP_PKEY_get0_SM9_MASTER(
+		EVP_PKEY_CTX_get0_pkey(ctx));
+
+	if (OBJ_obj2nid(sm9_master->scheme) != NID_sm9sign) {
+		SM9err(SM9_F_PKEY_SM9_MASTER_VERIFY, SM9_R_INVALID_KEY_USAGE);
+		return 0;
+	}
+	if (!dctx->id) {
+		SM9err(SM9_F_PKEY_SM9_MASTER_VERIFY, SM9_R_SIGNER_ID_REQUIRED);
+		return 0;
+	}
+
+	if ((ret = SM9_verify(dctx->sign_scheme, tbs, tbslen, sig, siglen,
+		sm9_master, dctx->id, strlen(dctx->id))) < 0) {
+		SM9err(SM9_F_PKEY_SM9_MASTER_VERIFY, ERR_R_SM9_LIB);
+	}
+	return ret;
+}
+
+static int pkey_sm9_master_encrypt(EVP_PKEY_CTX *ctx,
+	unsigned char *out, size_t *outlen,
+	const unsigned char *in, size_t inlen)
+{
+	SM9_MASTER_PKEY_CTX *dctx = EVP_PKEY_CTX_get_data(ctx);
+	SM9_MASTER_KEY *sm9_master = EVP_PKEY_get0_SM9_MASTER(
+		EVP_PKEY_CTX_get0_pkey(ctx));
+
+	if (OBJ_obj2nid(sm9_master->scheme) != NID_sm9encrypt) {
+		SM9err(SM9_F_PKEY_SM9_MASTER_ENCRYPT, SM9_R_INVALID_KEY_USAGE);
+		return 0;
+	}
+
+	if (!dctx->id) {
+		SM9err(SM9_F_PKEY_SM9_MASTER_ENCRYPT, SM9_R_IDENTITY_REQUIRED);
+		return 0;
+	}
+
+	if (!SM9_encrypt(dctx->encrypt_scheme, in, inlen, out, outlen,
+		sm9_master, dctx->id, strlen(dctx->id))) {
+		SM9err(SM9_F_PKEY_SM9_MASTER_ENCRYPT, ERR_R_SM9_LIB);
+		return 0;
+	}
+
+	return 1;
+}
+
+static int pkey_sm9_master_derive(EVP_PKEY_CTX *ctx, unsigned char *key, size_t *keylen)
+{
+	/* FIXME */
+	(void)ctx;
+	(void)key;
+	(void)keylen;
+
+	/*
+	SM9_MASTER_PKEY_CTX *dctx = EVP_PKEY_CTX_get_data(ctx);
+	SM9_MASTER_KEY *sm9_master = EVP_PKEY_get0_SM9_MASTER(
+		EVP_PKEY_CTX_get0_pkey(ctx));
+	*/
+
+	return -2;
+}
+
+static int pkey_sm9_master_ctrl(EVP_PKEY_CTX *ctx, int type, int p1, void *p2)
+{
+	SM9_MASTER_PKEY_CTX *dctx = EVP_PKEY_CTX_get_data(ctx);
+
+	switch (type) {
+	case EVP_PKEY_CTRL_SM9_PAIRING:
+		if (p1 == -2)
+			return dctx->pairing;
+		if (!sm9_check_pairing(p1)) {
+		}
+		dctx->pairing = p1;
+		return 1;
+
+	case EVP_PKEY_CTRL_SM9_SCHEME:
+		if (p1 == -2)
+			return dctx->scheme;
+		if (!sm9_check_scheme(p1)) {
+			SM9err(SM9_F_PKEY_SM9_MASTER_CTRL, SM9_R_INVALID_SCHEME);
+			return 0;
+		}
+		dctx->scheme = p1;
+		return 1;
+
+	case EVP_PKEY_CTRL_SM9_HASH1:
+		if (p1 == -2)
+			return dctx->hash1;
+		if (!sm9_check_hash1(p1)) {
+			SM9err(SM9_F_PKEY_SM9_MASTER_CTRL, SM9_R_INVALID_HASH1);
+			return 0;
+		}
+		dctx->hash1 = p1;
+		return 1;
+
+	case EVP_PKEY_CTRL_SM9_ID:
+		if (!p2 || !strlen((char *)p2) || strlen((char *)p2) > SM9_MAX_ID_LENGTH) {
+			SM9err(SM9_F_PKEY_SM9_MASTER_CTRL, SM9_R_INVALID_ID);
+			return 0;
+		} else {
+			char *id = NULL;
+			if (!(id = OPENSSL_strdup((char *)p2))) {
+				SM9err(SM9_F_PKEY_SM9_MASTER_CTRL, ERR_R_MALLOC_FAILURE);
+			}
+			if (dctx->id) {
+				OPENSSL_free(dctx->id);
+			}
+			dctx->id = id;
+		}
+		return 1;
+
+	case EVP_PKEY_CTRL_GET_SM9_ID:
+		*(const char **)p2 = dctx->id;
+		return 1;
+	}
+
+	return -2;
+}
+
+static int pkey_sm9_master_ctrl_str(EVP_PKEY_CTX *ctx, const char *type, const char *value)
+{
+	if (!strcmp(type, "pairing")) {
+		int nid = OBJ_txt2nid(value);
+		if (!sm9_check_pairing(nid)) {
+			SM9err(SM9_F_PKEY_SM9_MASTER_CTRL_STR, SM9_R_INVALID_PAIRING);
+			return 0;
+		}
+		return EVP_PKEY_CTX_set_sm9_pairing(ctx, nid);
+
+	} else if (!strcmp(type, "scheme")) {
+		int nid = OBJ_txt2nid(value);
+		if (!sm9_check_scheme(nid)) {
+			SM9err(SM9_F_PKEY_SM9_MASTER_CTRL_STR, SM9_R_INVALID_SM9_SCHEME);
+			return 0;
+		}
+		return EVP_PKEY_CTX_set_sm9_scheme(ctx, nid);
+
+	} else if (!strcmp(type, "hash1")) {
+		int nid = OBJ_txt2nid(value);
+		if (!sm9_check_hash1(nid)) {
+			SM9err(SM9_F_PKEY_SM9_MASTER_CTRL_STR, SM9_R_INVALID_SM9_SCHEME);
+			return 0;
+		}
+		return EVP_PKEY_CTX_set_sm9_hash1(ctx, nid);
+
+	} else if (!strcmp(type, "id")) {
+		return EVP_PKEY_CTX_set_sm9_id(ctx, value);
+	}
+
+	return -2;
+}
+
+const EVP_PKEY_METHOD sm9_master_pkey_meth = {
+	EVP_PKEY_SM9_MASTER,		/* pkey_id */
+	0,				/* flags */
+	pkey_sm9_master_init,		/* init */
+	pkey_sm9_master_copy,		/* copy */
+	pkey_sm9_master_cleanup,	/* cleanup */
+	NULL,				/* paramgen_init */
+	NULL,				/* paramgen */
+	NULL,				/* keygen_init */
+	pkey_sm9_master_keygen,		/* keygen */
+	NULL,				/* sign_init */
+	NULL,				/* sign */
+	NULL,				/* verify_init */
+	pkey_sm9_master_verify,		/* verify */
+	NULL,				/* verify_recover_init */
+	NULL,				/* verify_recover */
+	NULL,				/* signctx_init */
+	NULL,				/* signctx */
+	NULL,				/* verifyctx_init */
+	NULL,				/* verifyctx */
+	NULL,				/* encrypt_init */
+	pkey_sm9_master_encrypt,	/* encrypt */
+	NULL,				/* decrypt_init */
+	NULL,				/* decrypt */
+	NULL,				/* derive_init */
+	pkey_sm9_master_derive,		/* derive */
+	pkey_sm9_master_ctrl,		/* ctrl */
+	pkey_sm9_master_ctrl_str,	/* ctrl_str */
+};
+
+typedef struct {
+	int sign_scheme;
+	int encrypt_scheme;
+	char *id;
+} SM9_PKEY_CTX;
+
+static int pkey_sm9_init(EVP_PKEY_CTX *ctx)
+{
+	SM9_PKEY_CTX *dctx;
+	if (!(dctx = OPENSSL_zalloc(sizeof(*dctx)))) {
+		SM9err(SM9_F_PKEY_SM9_INIT, ERR_R_MALLOC_FAILURE);
+		return 0;
+	}
+	dctx->sign_scheme = NID_sm3; // FIXME: some like NID_sm9sign_sm3 			
+	dctx->encrypt_scheme = NID_sm9encrypt_with_sm3_xor;
+	dctx->id = NULL;
+	OPENSSL_assert(EVP_PKEY_CTX_get_data(ctx) == NULL);
+	(void)EVP_PKEY_CTX_set_data(ctx, dctx);
+	return 1;
+}
+
+static int pkey_sm9_copy(EVP_PKEY_CTX *dst, EVP_PKEY_CTX *src)
+{
+	SM9_PKEY_CTX *dctx, *sctx;
+	if (!pkey_sm9_init(dst)) {
+		SM9err(SM9_F_PKEY_SM9_COPY, ERR_R_SM9_LIB);
+		return 0;
+	}
+	sctx = EVP_PKEY_CTX_get_data(src);
+	dctx = EVP_PKEY_CTX_get_data(dst);
+	*dctx = *sctx;
+	if (!(dctx->id = OPENSSL_strdup(sctx->id))) {
+		return 0;
+	}
+	return 1;
+}
+
+static void pkey_sm9_cleanup(EVP_PKEY_CTX *ctx)
+{
+	SM9_PKEY_CTX *dctx = EVP_PKEY_CTX_get_data(ctx);
+	if (dctx) {
+		OPENSSL_free(dctx->id);
+		OPENSSL_free(dctx);
+	}
+}
+
+static int pkey_sm9_sign(EVP_PKEY_CTX *ctx,
+	unsigned char *sig, size_t *siglen,
+	const unsigned char *tbs, size_t tbslen)
+{
+	SM9_PKEY_CTX *dctx = EVP_PKEY_CTX_get_data(ctx);
+	SM9_KEY *sm9 = EVP_PKEY_get0_SM9(EVP_PKEY_CTX_get0_pkey(ctx));
+	if (!SM9_sign(dctx->sign_scheme, tbs, tbslen, sig, siglen, sm9)) {
+		SM9err(SM9_F_PKEY_SM9_SIGN, ERR_R_SM9_LIB);
+		return 0;
+	}
+	return 1;
+}
+
+static int pkey_sm9_decrypt(EVP_PKEY_CTX *ctx,
+	unsigned char *out, size_t *outlen,
+	const unsigned char *in, size_t inlen)
+{
+	SM9_PKEY_CTX *dctx = EVP_PKEY_CTX_get_data(ctx);
+	SM9_KEY *sm9 = EVP_PKEY_get0_SM9(EVP_PKEY_CTX_get0_pkey(ctx));
+	if (!SM9_decrypt(dctx->encrypt_scheme, in, inlen,
+		out, outlen, sm9)) {
+		SM9err(SM9_F_PKEY_SM9_DECRYPT, ERR_R_SM9_LIB);
+		return 0;
+	}
+	return 1;
+}
+
+static int pkey_sm9_derive(EVP_PKEY_CTX *ctx, unsigned char *key, size_t *keylen)
+{
+	/* FIXME */
+	(void)ctx;
+	(void)key;
+	(void)keylen;
+
+	return -2;
+}
+
+static int pkey_sm9_ctrl(EVP_PKEY_CTX *ctx, int type, int p1, void *p2)
+{
+	SM9_PKEY_CTX *dctx = EVP_PKEY_CTX_get_data(ctx);
+
+	switch (type) {
+	case EVP_PKEY_CTRL_SM9_SIGN_SCHEME:
+		if (p1 == -2)
+			return dctx->sign_scheme;
+		if (!sm9_check_sign_scheme(p1)) {
+			SM9err(SM9_F_PKEY_SM9_CTRL, SM9_R_INVALID_SIGN_SCHEME);
+			return 0;
+		}
+		dctx->sign_scheme = p1;
+		return 1;
+
+	case EVP_PKEY_CTRL_SM9_ENCRYPT_SCHEME:
+		if (p1 == -2)
+			return dctx->encrypt_scheme;
+		if (!sm9_check_encrypt_scheme(p1)) {
+			SM9err(SM9_F_PKEY_SM9_CTRL, SM9_R_INVALID_ENCRYPT_SCHEME);
+			return 0;
+		}
+		dctx->encrypt_scheme = p1;
+		return 1;
+
+	case EVP_PKEY_CTRL_SM9_ID:
+		if (!p2 || !strlen((char *)p2) || strlen((char *)p2) > SM9_MAX_ID_LENGTH) {
+			return 0;
+		} else {
+		}
+		return 1;
+
+	case EVP_PKEY_CTRL_GET_SM9_ID:
+		*(const char **)p2 = dctx->id;
+		return 1;
+
+	default:
+		return -2;
+	}
+
+	return -2;
+}
+
+static int pkey_sm9_ctrl_str(EVP_PKEY_CTX *ctx, const char *type, const char *value)
+{
+	if (!strcmp(type, "sign_scheme")) {
+		int nid = OBJ_txt2nid(value);
+		if (!sm9_check_sign_scheme(nid)) {
+			SM9err(SM9_F_PKEY_SM9_CTRL_STR, SM9_R_INVALID_SIGN_MD);
+			return 0;
+		}
+		return EVP_PKEY_CTX_set_sm9_sign_scheme(ctx, nid);
+
+	} else if (!strcmp(type, "encrypt_scheme")) {
+		int nid = OBJ_txt2nid(value);
+		if (!sm9_check_encrypt_scheme(nid)) {
+			SM9err(SM9_F_PKEY_SM9_CTRL_STR, SM9_R_INVALID_ENCRYPT_SCHEME);
+			return 0;
+		}
+		return EVP_PKEY_CTX_set_sm9_encrypt_scheme(ctx, nid);
+
+	} else if (!strcmp(type, "id")) {
+		return EVP_PKEY_CTX_set_sm9_id(ctx, value);
+	}
+
+	return -2;
+}
+
+/* TODO: currently data instead of dgst is signed.
+ * we need to support to ctrl which to sign.
+ */
+const EVP_PKEY_METHOD sm9_pkey_meth = {
+	EVP_PKEY_SM9,		/* pkey_id */
+	0,			/* flags */
+	pkey_sm9_init,		/* init */
+	pkey_sm9_copy,		/* copy */
+	pkey_sm9_cleanup,	/* cleanup */
+	NULL,			/* paramgen_init */
+	NULL,			/* paramgen */
+	NULL,			/* keygen_init */
+	NULL,			/* keygen */
+	NULL,			/* sign_init */
+	pkey_sm9_sign,		/* sign */
+	NULL, 			/* verify_init */
+	NULL,			/* verify */
+	NULL,			/* verify_recover_init */
+	NULL,			/* verify_recover */
+	NULL,			/* signctx_init */
+	NULL,			/* signctx */
+	NULL,			/* verifyctx_init */
+	NULL,			/* verifyctx */
+	NULL,			/* encrypt_init */
+	NULL,			/* encrypt */
+	NULL,			/* decrypt_init */
+	pkey_sm9_decrypt,	/* decrypt */
+	NULL,			/* derive_init */
+	pkey_sm9_derive,	/* derive */
+	pkey_sm9_ctrl,		/* ctrl */
+	pkey_sm9_ctrl_str,	/* ctrl_str */
+};
diff --git a/crypto/sm9/sm9_rate.c b/crypto/sm9/sm9_rate.c
new file mode 100644
index 000000000..02a43a9d9
--- /dev/null
+++ b/crypto/sm9/sm9_rate.c
@@ -0,0 +1,2729 @@
+/* ====================================================================
+ * Copyright (c) 2018 The GmSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the GmSSL Project.
+ *    (http://gmssl.org/)"
+ *
+ * 4. The name "GmSSL Project" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    guanzhi1980@gmail.com.
+ *
+ * 5. Products derived from this software may not be called "GmSSL"
+ *    nor may "GmSSL" appear in their names without prior written
+ *    permission of the GmSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the GmSSL Project
+ *    (http://gmssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE GmSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE GmSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include "sm9_lcl.h"
+
+
+#define NOSM9_FAST
+
+int fp2_init(fp2_t a, BN_CTX *ctx)
+{
+	a[0] = NULL;
+	a[1] = NULL;
+	a[0] = BN_CTX_get(ctx);
+	a[1] = BN_CTX_get(ctx);
+	/*
+	if (!a[1]) {
+		BN_free(a[0]);
+		a[0] = NULL;
+		return 0;
+	}
+	*/
+	return 1;
+}
+
+void fp2_cleanup(fp2_t a)
+{
+	BN_free(a[0]);
+	BN_free(a[1]);
+	a[0] = NULL;
+	a[1] = NULL;
+}
+
+void fp2_clear_cleanup(fp2_t a)
+{
+	BN_clear_free(a[0]);
+	BN_clear_free(a[1]);
+	a[0] = NULL;
+	a[1] = NULL;
+}
+
+int fp2_is_zero(const fp2_t a)
+{
+	return BN_is_zero(a[0])
+		&& BN_is_zero(a[1]);
+}
+
+int fp2_print(const fp2_t a)
+{
+	printf("%s\n", BN_bn2hex(a[0]));
+	printf("%s\n", BN_bn2hex(a[1]));
+	return 1;
+}
+
+int fp2_is_one(const fp2_t a)
+{
+	return BN_is_one(a[0])
+		&& BN_is_zero(a[1]);
+}
+
+void fp2_set_zero(fp2_t r)
+{
+	BN_zero(r[0]);
+	BN_zero(r[1]);
+}
+
+int fp2_set_one(fp2_t r)
+{
+	BN_zero(r[1]);
+	return BN_one(r[0]);
+}
+
+int fp2_copy(fp2_t r, const fp2_t a)
+{
+	return BN_copy(r[0], a[0])
+		&& BN_copy(r[1], a[1]);
+}
+
+int fp2_set(fp2_t r, const BIGNUM *a0, const BIGNUM *a1)
+{
+	return BN_copy(r[0], a0)
+		&& BN_copy(r[1], a1);
+}
+
+int fp2_set_hex(fp2_t r, const char *str[2])
+{
+	return BN_hex2bn(&r[0], str[0])
+		&& BN_hex2bn(&r[1], str[1]);
+}
+
+int fp2_set_u(fp2_t r)
+{
+	BN_zero(r[0]);
+	return BN_one(r[1]);
+}
+
+int fp2_set_5u(fp2_t r)
+{
+	BN_zero(r[0]);
+	return BN_set_word(r[1], 5);
+}
+
+int fp2_set_bn(fp2_t r, const BIGNUM *a)
+{
+	BN_zero(r[1]);
+	return BN_copy(r[0], a) != NULL;
+}
+
+int fp2_set_word(fp2_t r, unsigned long a)
+{
+	BN_zero(r[1]);
+	return BN_set_word(r[0], a);
+}
+
+int fp2_equ(const fp2_t a, const fp2_t b)
+{
+	return !BN_cmp(a[0], b[0]) && !BN_cmp(a[1], b[1]);
+}
+
+int fp2_equ_hex(const fp2_t a, const char *str[2], BN_CTX *ctx)
+{
+	fp2_t t;
+	fp2_init(t, ctx);
+	fp2_set_hex(t, str);
+	return fp2_equ(a, t);
+}
+
+int fp2_add_word(fp2_t r, const fp2_t a, unsigned long b, const BIGNUM *p, BN_CTX *ctx)
+{
+	BIGNUM *w = NULL;
+	if (!(w = BN_CTX_get(ctx))
+		|| !BN_set_word(w, b)
+		|| !BN_mod_add(r[0], a[0], w, p, ctx)
+		|| !BN_copy(r[1], a[1])) {
+		BN_free(w);
+		return 0;
+	}
+	BN_free(w);
+	return 1;
+}
+
+int fp2_add(fp2_t r, const fp2_t a, const fp2_t b, const BIGNUM *p, BN_CTX *ctx)
+{
+	return BN_mod_add(r[0], a[0], b[0], p, ctx)
+		&& BN_mod_add(r[1], a[1], b[1], p, ctx);
+}
+
+int fp2_dbl(fp2_t r, const fp2_t a, const BIGNUM *p, BN_CTX *ctx)
+{
+	return BN_mod_add(r[0], a[0], a[0], p, ctx)
+		&& BN_mod_add(r[1], a[1], a[1], p, ctx);
+}
+
+int fp2_tri(fp2_t r, const fp2_t a, const BIGNUM *p, BN_CTX *ctx)
+{
+	fp2_t t;
+	if (!fp2_init(t, ctx)
+		|| !fp2_dbl(t, a, p, ctx)
+		|| !fp2_add(r, t, a, p, ctx)) {
+		fp2_cleanup(t);
+		return 0;
+	}
+	fp2_cleanup(t);
+	return 1;
+}
+
+int fp2_sub(fp2_t r, const fp2_t a, const fp2_t b, const BIGNUM *p, BN_CTX *ctx)
+{
+	return BN_mod_sub(r[0], a[0], b[0], p, ctx)
+		&& BN_mod_sub(r[1], a[1], b[1], p, ctx);
+}
+
+int fp2_neg(fp2_t r, const fp2_t a, const BIGNUM *p, BN_CTX *ctx)
+{
+	return BN_mod_sub(r[0], p, a[0], p, ctx)
+		&& BN_mod_sub(r[1], p, a[1], p, ctx);
+}
+
+int fp2_mul(fp2_t r, const fp2_t a, const fp2_t b, const BIGNUM *p, BN_CTX *ctx)
+{
+	BIGNUM *t = NULL;
+	BIGNUM *r0 = NULL;
+	BIGNUM *r1 = NULL;
+	if (!(t = BN_CTX_get(ctx))
+		|| !(r0 = BN_CTX_get(ctx))
+		|| !(r1 = BN_CTX_get(ctx))
+
+		/* r0 = a0 * b0 - 2 * a1 * b1 */
+		|| !BN_mod_mul(r0, a[0], b[0], p, ctx)
+		|| !BN_mod_mul(t, a[1], b[1], p, ctx)
+		|| !BN_mod_add(t, t, t, p, ctx)
+		|| !BN_mod_sub(r0, r0, t, p, ctx)
+
+		/* r1 = a0 * b1 + a1 * b0 */
+		|| !BN_mod_mul(r1, a[0], b[1], p, ctx)
+		|| !BN_mod_mul(t, a[1], b[0], p, ctx)
+		|| !BN_mod_add(r1, r1, t, p, ctx)
+
+		|| !BN_copy(r[0], r0)
+		|| !BN_copy(r[1], r1)) {
+		BN_free(t);
+		BN_free(r0);
+		BN_free(r1);
+		return 0;
+	}
+	BN_free(t);
+	BN_free(r0);
+	BN_free(r1);
+	return 1;
+}
+
+int fp2_mul_u(fp2_t r, const fp2_t a, const fp2_t b, const BIGNUM *p, BN_CTX *ctx)
+{
+	BIGNUM *r0 = NULL;
+	BIGNUM *r1 = NULL;
+	BIGNUM *t = NULL;
+	if (!(r0 = BN_CTX_get(ctx))
+		|| !(r1 = BN_CTX_get(ctx))
+		|| !(t = BN_CTX_get(ctx))
+
+		/* r0 = -2 * (a0 * b1 + a1 * b0) */
+		|| !BN_mod_mul(r0, a[0], b[1], p, ctx)
+		|| !BN_mod_mul(t, a[1], b[0], p, ctx)
+		|| !BN_mod_add(r0, r0, t, p, ctx)
+		|| !BN_mod_add(r0, r0, r0, p, ctx)
+		|| !BN_mod_sub(r0, p, r0, p, ctx)
+
+		/* r1 = a0 * b0 - 2 * a1 * b1 */
+		|| !BN_mod_mul(r1, a[0], b[0], p, ctx)
+		|| !BN_mod_mul(t, a[1], b[1], p, ctx)
+		|| !BN_mod_add(t, t, t, p, ctx)
+		|| !BN_mod_sub(r1, r1, t, p, ctx)
+
+		|| !BN_copy(r[0], r0)
+		|| !BN_copy(r[1], r1)) {
+		BN_free(r0);
+		BN_free(r1);
+		BN_free(t);
+		return 0;
+	}
+	BN_free(r0);
+	BN_free(r1);
+	BN_free(t);
+	return 1;
+}
+
+int fp2_mul_num(fp2_t r, const fp2_t a, const BIGNUM *n, const BIGNUM *p, BN_CTX *ctx)
+{
+	BIGNUM *r0 = NULL;
+	BIGNUM *r1 = NULL;
+	if (!(r0 = BN_CTX_get(ctx))
+		|| !(r1 = BN_CTX_get(ctx))
+
+		|| !BN_mod_mul(r0, a[0], n, p, ctx)
+		|| !BN_mod_mul(r1, a[1], n, p, ctx)
+
+		|| !BN_copy(r[0], r0)
+		|| !BN_copy(r[1], r1)) {
+		BN_free(r0);
+		BN_free(r1);
+		return 0;
+	}
+	BN_free(r0);
+	BN_free(r1);
+	return 1;
+}
+
+int fp2_sqr(fp2_t r, const fp2_t a, const BIGNUM *p, BN_CTX *ctx)
+{
+	BIGNUM *r0 = NULL;
+	BIGNUM *r1 = NULL;
+	BIGNUM *t = NULL;
+	if (!(r0 = BN_CTX_get(ctx))
+		|| !(r1 = BN_CTX_get(ctx))
+		||!(t = BN_CTX_get(ctx))
+		/* r0 = a0^2 - 2 * a1^2 */
+		|| !BN_mod_sqr(r0, a[0], p, ctx)
+		|| !BN_mod_sqr(t, a[1], p, ctx)
+		|| !BN_mod_add(t, t, t, p, ctx)
+		|| !BN_mod_sub(r0, r0, t, p, ctx)
+
+		/* r1 = 2 * a0 * a1 */
+		|| !BN_mod_mul(r1, a[0], a[1], p, ctx)
+		|| !BN_mod_add(r1, r1, r1, p, ctx)
+		|| !BN_copy(r[0], r0)
+		|| !BN_copy(r[1], r1)) {
+		BN_free(r0);
+		BN_free(r1);
+		BN_free(t);
+		return 0;
+	}
+	BN_free(r0);
+	BN_free(r1);
+	BN_free(t);
+	return 1;
+}
+
+int fp2_sqr_u(fp2_t r, const fp2_t a, const BIGNUM *p, BN_CTX *ctx)
+{
+	BIGNUM *r0 = NULL;
+	BIGNUM *r1 = NULL;
+	BIGNUM *t = NULL;
+	if (!(r0 = BN_CTX_get(ctx))
+		|| !(r1 = BN_CTX_get(ctx))
+		|| !(t = BN_CTX_get(ctx))
+		/* r0 = -4 * a0 * a1 */
+		|| !BN_mod_mul(r0, a[0], a[1], p, ctx)
+		|| !BN_mod_add(r0, r0, r0, p, ctx)
+		|| !BN_mod_add(r0, r0, r0, p, ctx)
+		|| !BN_mod_sub(r0, p, r0, p, ctx)
+
+		/* r1 = a0^2 - 2 * a1^2 */
+		|| !BN_mod_sqr(r1, a[0], p, ctx)
+		|| !BN_mod_sqr(t, a[1], p, ctx)
+		|| !BN_mod_add(t, t, t, p, ctx)
+		|| !BN_mod_sub(r1, r1, t, p, ctx)
+
+		|| !BN_copy(r[0], r0)
+		|| !BN_copy(r[1], r1)) {
+		BN_free(r0);
+		BN_free(r1);
+		BN_free(t);
+		return 0;
+	}
+	BN_free(r0);
+	BN_free(r1);
+	BN_free(t);
+	return 1;
+}
+
+int fp2_inv(fp2_t r, const fp2_t a, const BIGNUM *p, BN_CTX *ctx)
+{
+	if (BN_is_zero(a[0])) {
+		/* r0 = 0 */
+		BN_zero(r[0]);
+		/* r1 = -(2 * a1)^-1 */
+		if (!BN_mod_add(r[1], a[1], a[1], p, ctx)
+			|| !BN_mod_inverse(r[1], r[1], p, ctx)
+			|| !BN_mod_sub(r[1], p, r[1], p, ctx)) {
+			return 0;
+		}
+
+	} else if (BN_is_zero(a[1])) {
+		/* r1 = 0 */
+		BN_zero(r[1]);
+		/* r0 = a0^-1 */
+		if (!BN_mod_inverse(r[0], a[0], p, ctx)) {
+			return 0;
+		}
+
+	} else {
+		BIGNUM *k = NULL;
+		BIGNUM *t = NULL;
+		if (!(k = BN_CTX_get(ctx))
+			|| !(t = BN_CTX_get(ctx))
+
+			/* k = (a[0]^2 + 2 * a[1]^2)^-1 */
+			|| !BN_mod_sqr(k, a[0], p, ctx)
+			|| !BN_mod_sqr(t, a[1], p, ctx)
+			|| !BN_mod_add(t, t, t, p, ctx)
+			|| !BN_mod_add(k, k, t, p, ctx)
+			|| !BN_mod_inverse(k, k, p, ctx)
+
+			/* r[0] = a[0] * k, r[1] = -a[1] * k */
+			|| !BN_mod_mul(r[0], a[0], k, p, ctx)
+			|| !BN_mod_mul(r[1], a[1], k, p, ctx)
+			|| !BN_mod_sub(r[1], p, r[1], p, ctx)) {
+
+			BN_free(k);
+			BN_free(t);
+			return 0;
+		}
+		BN_free(k);
+		BN_free(t);
+	}
+
+	return 1;
+}
+
+int fp2_div(fp2_t r, const fp2_t a, const fp2_t b, const BIGNUM *p, BN_CTX *ctx)
+{
+	return fp2_inv(r, b, p, ctx)
+		&& fp2_mul(r, a, r, p, ctx);
+}
+
+int fp2_to_bin(const fp2_t a, unsigned char to[64])
+{
+	memset(to, 0, 64);
+	BN_bn2bin(a[1], to + 32 - BN_num_bytes(a[1]));
+	BN_bn2bin(a[0], to + 64 - BN_num_bytes(a[0]));
+	return 1;
+}
+
+int fp2_from_bin(fp2_t a, const unsigned char from[64])
+{
+	return BN_bin2bn(from, 32, a[1])
+		&& BN_bin2bn(from + 32, 32, a[0]);
+}
+
+int fp2_test(const BIGNUM *p, BN_CTX *ctx)
+{
+	const char *_a[] = {
+		"5f25ce2083fc970a6b9fdcd819fb1966d300af2afd58d480c59e02b320852183",
+		"9acddfef770bcdce452d72461f9d1482a8eff7662e1d591c70a7ce35f2f5710c"};
+	const char *_b[] = {
+		"7114f0b7f50ebb85c124558f76f10bd277f71c27384deb67f229e582befde3ee",
+		"aa2714a30d7b8ae08b987fae8818881fb1952a1f53cda30a35c72841b174d7d"};
+	const char *add_a_b[] = {
+		"19fabed87667ab9e56c087179b5d5df4290538071b2bd10cd2584d0dfc31bff4",
+		"a5705139a7e3867c4de6fa41081e9d04a4094a08235a334d140440ba0e0cbe89"};
+	const char *dbl_a[] = {
+		"80b9c4105558723013c0e603e676b88840ecb0ae036ba25a5cc6a3e5db8fd89",
+		"7f5bbfdeeb73f4aab457393c49ab61c02fed5b8141bfc35cfbe0014402999c9b"};
+	const char *tri_a[] = {
+		"67316a6189521e2d6cdbeb38586284ef570f7a35dd8f8ea66b6a6cf17e3e1f0c",
+		"63e99fce5fdc1b872381003273b9aefdb6eabf9c55622d9d87183452123dc82a"};
+	const char *sub_a_b[] = {
+		"a450dd6891918276807f32989898d4d97cfc264edf85d7f4b8e3b85844d88312",
+		"902b6ea5463415203c73ea4b371b8c00add6a4c438e07eebcd4b5bb1d7de238f"};
+	const char *neg_a[] = {
+		"571a31df7ea70fe76a63ce77db93adde4ef1e4201d221a5b1fd19874c2cc23fa",
+		"1b7220108b97d92390d63909d5f1b2c279029be4ec5d95bf74c7ccf1f05bd471"};
+	const char *mul_a_b[] = {
+		"87c654e6949d4f119504b210360cc580fc5365c6d34c1321aec27fcf5a1f15b1",
+		"36e98d79d712f6a3d1bc6d5ac5a055ee0fcbf088c8744e6e943369538e863eb7"};
+	const char *mulu_a_b[] = {
+		"486ce50c547db9aa328ad09a6a4e1b69025ab239899251febd08c880c644c80f",
+		"87c654e6949d4f119504b210360cc580fc5365c6d34c1321aec27fcf5a1f15b1"};
+	const char *sqr_a[] = {
+		"b6011ba4cb9f083149a717c7829eb292c31a0800caf88db28bc71f5a084a0405",
+		"3a16d18809fca540de3543bdfa3317b45bd8117ce6e6a6be5837fbe70350b684"};
+	const char *sqru_a[] = {
+		"42125cefeeaa5c70199923d4012897dc6a4270514cada15f34ffa359dcafd875",
+		"b6011ba4cb9f083149a717c7829eb292c31a0800caf88db28bc71f5a084a0405"};
+	const char *inv_a[] = {
+		"5d25a81c4b20b093804befda14731ad2dfa436a450e8b40cf91939ba94fe84be",
+		"b317556351e184acb06cf1171069fcf3e9a36b60ca8cd718f55ff0c5769ad325"};
+	const char *div_a_b[] = {
+		"107048b3442ccc880691cbeb5005bbeb3e71fbe1ec3899971b6caecd224b7dd9",
+		"7716a52c4911f52b519d9ec928b56561008469bf3403508d40a324bcd17f4a17"};
+	const char *inv_1[] = {
+		"1",
+		"0"};
+	const char *inv_u[] = {
+		"0",
+		"5b2000000151d378eb01d5a7fac763a290f949a58d3d776df2b7cd93f1a8a2be"};
+
+
+	fp2_t a, b, r;
+	int ok;
+
+	fp2_init(a, ctx);
+	fp2_init(b, ctx);
+	fp2_init(r, ctx);
+
+	fp2_set_hex(a, _a);
+	fp2_set_hex(b, _b);
+
+	fp2_add(r, a, b, p, ctx);
+	ok = fp2_equ_hex(r, add_a_b, ctx);
+	printf("fp2 test %d: %s\n", __LINE__, ok ? "ok" : "error");
+
+	fp2_dbl(r, a, p, ctx);
+	ok = fp2_equ_hex(r, dbl_a, ctx);
+	printf("fp2 test %d: %s\n", __LINE__, ok ? "ok" : "error");
+
+	fp2_tri(r, a, p, ctx);
+	ok = fp2_equ_hex(r, tri_a, ctx);
+	printf("fp2 test %d: %s\n", __LINE__, ok ? "ok" : "error");
+
+	fp2_sub(r, a, b, p, ctx);
+	ok = fp2_equ_hex(r, sub_a_b, ctx);
+	printf("fp2 test %d: %s\n", __LINE__, ok ? "ok" : "error");
+
+	fp2_neg(r, a, p, ctx);
+	ok = fp2_equ_hex(r, neg_a, ctx);
+	printf("fp2 test %d: %s\n", __LINE__, ok ? "ok" : "error");
+
+	fp2_mul(r, a, b, p, ctx);
+	ok = fp2_equ_hex(r, mul_a_b, ctx);
+	printf("fp2 test %d: %s\n", __LINE__, ok ? "ok" : "error");
+
+	fp2_mul_u(r, a, b, p, ctx);
+	ok = fp2_equ_hex(r, mulu_a_b, ctx);
+	printf("fp2 test %d: %s\n", __LINE__, ok ? "ok" : "error");
+
+	fp2_sqr(r, a, p, ctx);
+	ok = fp2_equ_hex(r, sqr_a, ctx);
+	printf("fp2 test %d: %s\n", __LINE__, ok ? "ok" : "error");
+
+	fp2_sqr_u(r, a, p, ctx);
+	ok = fp2_equ_hex(r, sqru_a, ctx);
+	printf("fp2 test %d: %s\n", __LINE__, ok ? "ok" : "error");
+
+	fp2_inv(r, a, p, ctx);
+	ok = fp2_equ_hex(r, inv_a, ctx);
+	printf("fp2 test %d: %s\n", __LINE__, ok ? "ok" : "error");
+
+	fp2_div(r, a, b, p, ctx);
+	ok = fp2_equ_hex(r, div_a_b, ctx);
+	printf("fp2 test %d: %s\n", __LINE__, ok ? "ok" : "error");
+
+	fp2_set_one(r);
+	fp2_inv(r, r, p, ctx);
+	ok = fp2_equ_hex(r, inv_1, ctx);
+	printf("fp2 test %d: %s\n", __LINE__, ok ? "ok" : "error");
+
+	fp2_set_u(r);
+	fp2_inv(r, r, p, ctx);
+	ok = fp2_equ_hex(r, inv_u, ctx);
+	printf("fp2 test %d: %s\n", __LINE__, ok ? "ok" : "error");
+
+	return 1;
+}
+
+int fp4_init(fp4_t a, BN_CTX *ctx)
+{
+	int r;
+	r = fp2_init(a[0], ctx);
+	r &= fp2_init(a[1], ctx);
+	if (!r) {
+		fp2_cleanup(a[0]);
+		fp2_cleanup(a[1]);
+	}
+	return r;
+}
+
+void fp4_cleanup(fp4_t a)
+{
+	fp2_cleanup(a[0]);
+	fp2_cleanup(a[1]);
+}
+
+void fp4_clear_cleanup(fp4_t a)
+{
+	fp2_clear_cleanup(a[0]);
+	fp2_clear_cleanup(a[1]);
+}
+
+int fp4_print(const fp4_t a)
+{
+	fp2_print(a[0]);
+	fp2_print(a[1]);
+	printf("\n");
+	return 1;
+}
+
+int fp4_is_zero(const fp4_t a)
+{
+	return fp2_is_zero(a[0])
+		&& fp2_is_zero(a[1]);
+}
+
+int fp4_is_one(const fp4_t a)
+{
+	return fp2_is_one(a[0])
+		&& fp2_is_zero(a[1]);
+}
+
+void fp4_set_zero(fp4_t r)
+{
+	fp2_set_zero(r[0]);
+	fp2_set_zero(r[1]);
+}
+
+int fp4_set_one(fp4_t r)
+{
+	fp2_set_zero(r[1]);
+	return fp2_set_one(r[0]);
+}
+
+int fp4_set_bn(fp4_t r, const BIGNUM *a)
+{
+	fp2_set_zero(r[1]);
+	return fp2_set_bn(r[0], a);
+}
+
+int fp4_set_word(fp4_t r, unsigned long a)
+{
+	fp2_set_zero(r[1]);
+	return fp2_set_word(r[0], a);
+}
+
+int fp4_set_fp2(fp4_t r, const fp2_t a)
+{
+	fp2_set_zero(r[1]);
+	return fp2_copy(r[0], a);
+}
+
+int fp4_set(fp4_t r, const fp2_t a0, const fp2_t a1)
+{
+	return fp2_copy(r[0], a0)
+		&& fp2_copy(r[1], a1);
+}
+
+int fp4_set_hex(fp4_t r, const char *str[4])
+{
+	return fp2_set_hex(r[0], str)
+		&& fp2_set_hex(r[1], str+2);
+}
+
+int fp4_copy(fp4_t r, const fp4_t a)
+{
+	return fp2_copy(r[0], a[0])
+		&& fp2_copy(r[1], a[1]);
+}
+
+int fp4_set_u(fp4_t r)
+{
+	fp2_set_zero(r[1]);
+	return fp2_set_u(r[0]);
+}
+
+int fp4_set_v(fp4_t r)
+{
+	fp2_set_zero(r[0]);
+	return fp2_set_one(r[1]);
+}
+
+int fp4_equ(const fp4_t a, const fp4_t b)
+{
+	return fp2_equ(a[0], b[0])
+		&& fp2_equ(a[1], b[1]);
+}
+
+int fp4_equ_hex(const fp4_t a, const char *str[4], BN_CTX *ctx)
+{
+	fp4_t t;
+	fp4_init(t, ctx);
+	fp4_set_hex(t, str);
+	return fp4_equ(a, t);
+}
+
+int fp4_to_bin(const fp4_t a, unsigned char to[128])
+{
+	return fp2_to_bin(a[1], to)
+		&& fp2_to_bin(a[0], to + 64);
+}
+
+int fp4_from_bin(fp4_t a, const unsigned char from[128])
+{
+	return fp2_from_bin(a[1], from)
+		&& fp2_from_bin(a[0], from + 64);
+}
+
+int fp4_add(fp4_t r, const fp4_t a, const fp4_t b, const BIGNUM *p, BN_CTX *ctx)
+{
+	return fp2_add(r[0], a[0], b[0], p, ctx)
+		&& fp2_add(r[1], a[1], b[1], p, ctx);
+}
+
+int fp4_dbl(fp4_t r, const fp4_t a, const BIGNUM *p, BN_CTX *ctx)
+{
+	return fp2_dbl(r[0], a[0], p, ctx)
+		&& fp2_dbl(r[1], a[1], p, ctx);
+}
+
+int fp4_sub(fp4_t r, const fp4_t a, const fp4_t b, const BIGNUM *p, BN_CTX *ctx)
+{
+	return fp2_sub(r[0], a[0], b[0], p, ctx)
+		&& fp2_sub(r[1], a[1], b[1], p, ctx);
+}
+
+int fp4_neg(fp4_t r, const fp4_t a, const BIGNUM *p, BN_CTX *ctx)
+{
+	return fp2_neg(r[0], a[0], p, ctx)
+		&&fp2_neg(r[1], a[1], p, ctx);
+}
+
+int fp4_mul(fp4_t r, const fp4_t a, const fp4_t b, const BIGNUM *p, BN_CTX *ctx)
+{
+	fp2_t r0, r1, t;
+
+	fp2_init(r0, ctx);
+	fp2_init(r1, ctx);
+
+	if (!fp2_init(t, ctx)
+		/* r0 = a0 * b0 + a1 * b1 * u */
+		|| !fp2_mul(r0, a[0], b[0], p, ctx)
+		|| !fp2_mul_u(t, a[1], b[1], p, ctx)
+		|| !fp2_add(r0, r0, t, p, ctx)
+
+		/* r[1] = a[0] * b[1] + a[1] * b[0] */
+		|| !fp2_mul(r1, a[0], b[1], p, ctx)
+		|| !fp2_mul(t, a[1], b[0], p, ctx)
+		|| !fp2_add(r1, r1, t, p, ctx)
+
+		|| !fp2_copy(r[0], r0)
+		|| !fp2_copy(r[1], r1)) {
+		fp2_cleanup(r0);
+		fp2_cleanup(r1);
+		fp2_cleanup(t);
+		return 0;
+	}
+	fp2_cleanup(r0);
+	fp2_cleanup(r1);
+	fp2_cleanup(t);
+	return 1;
+}
+
+int fp4_mul_v(fp4_t r, const fp4_t a, const fp4_t b, const BIGNUM *p, BN_CTX *ctx)
+{
+	fp2_t r0, r1, t;
+	fp2_init(r0, ctx);
+	fp2_init(r1, ctx);
+	if (!fp2_init(t, ctx)
+		/* r0 = a0 * b1 * u + a1 * b0 * u */
+		|| !fp2_mul_u(r0, a[0], b[1], p, ctx)
+		|| !fp2_mul_u(t, a[1], b[0], p, ctx)
+		|| !fp2_add(r0, r0, t, p, ctx)
+
+		/* r1 = a0 * b0 + a1 * b1 * u */
+		|| !fp2_mul(r1, a[0], b[0], p, ctx)
+		|| !fp2_mul_u(t, a[1], b[1], p, ctx)
+		|| !fp2_add(r1, r1, t, p, ctx)
+
+		|| !fp2_copy(r[0], r0)
+		|| !fp2_copy(r[1], r1)) {
+		fp2_cleanup(r0);
+		fp2_cleanup(r1);
+		fp2_cleanup(t);
+		return 0;
+	}
+	fp2_cleanup(r0);
+	fp2_cleanup(r1);
+	fp2_cleanup(t);
+	return 1;
+}
+
+int fp4_sqr(fp4_t r, const fp4_t a, const BIGNUM *p, BN_CTX *ctx)
+{
+	fp2_t r0, r1, t;
+	fp2_init(r0, ctx);
+	fp2_init(r1, ctx);
+	if (!fp2_init(t, ctx)
+		/* r0 = a0^2 + a1^2 * u */
+		|| !fp2_sqr(r0, a[0], p, ctx)
+		|| !fp2_sqr_u(t, a[1], p, ctx)
+		|| !fp2_add(r0, r0, t, p, ctx)
+		/* r1 = 2 * (a0 * a1) */
+		|| !fp2_mul(r1, a[0], a[1], p, ctx)
+		|| !fp2_dbl(r1, r1, p, ctx)
+		|| !fp2_copy(r[0], r0)
+		|| !fp2_copy(r[1], r1)) {
+		fp2_cleanup(r0);
+		fp2_cleanup(r1);
+		fp2_cleanup(t);
+		return 0;
+	}
+	fp2_cleanup(r0);
+	fp2_cleanup(r1);
+	fp2_cleanup(t);
+	return 1;
+}
+
+int fp4_sqr_v(fp4_t r, const fp4_t a, const BIGNUM *p, BN_CTX *ctx)
+{
+	fp2_t r0, r1, t;
+	fp2_init(r0, ctx);
+	fp2_init(r1, ctx);
+	if (!fp2_init(t, ctx)
+		/* r0 = 2 * (a0 * a1) */
+		|| !fp2_mul_u(t, a[0], a[1], p, ctx)
+		|| !fp2_dbl(r0, t, p, ctx)
+
+		/* r1 = a0^2 + a1^2 * u */
+		|| !fp2_sqr(r1, a[0], p, ctx)
+		|| !fp2_sqr_u(t, a[1], p, ctx)
+		|| !fp2_add(r1, r1, t, p, ctx)
+		|| !fp2_copy(r[0], r0)
+		|| !fp2_copy(r[1], r1)) {
+		fp2_cleanup(r0);
+		fp2_cleanup(r1);
+		fp2_cleanup(t);
+		return 0;
+	}
+	fp2_cleanup(r0);
+	fp2_cleanup(r1);
+	fp2_cleanup(t);
+	return 1;
+}
+
+int fp4_inv(fp4_t r, const fp4_t a, const BIGNUM *p, BN_CTX *ctx)
+{
+	fp2_t r0, r1, k;
+	fp2_init(r0, ctx);
+	fp2_init(r1, ctx);
+
+
+	if (!fp2_init(k, ctx)
+		/* k = (a1^2 * u - a0^2)^-1 */
+		|| !fp2_sqr_u(k, a[1], p, ctx)
+		|| !fp2_sqr(r0, a[0], p, ctx)
+		|| !fp2_sub(k, k, r0, p, ctx)
+		|| !fp2_inv(k, k, p, ctx)
+
+		/* r0 = -(a0 * k) */
+		|| !fp2_mul(r0, a[0], k, p, ctx)
+		|| !fp2_neg(r0, r0, p, ctx)
+
+		/* r1 = a1 * k */
+		|| !fp2_mul(r1, a[1], k, p, ctx)
+
+		|| !fp2_copy(r[0], r0)
+		|| !fp2_copy(r[1], r1)) {
+		fp2_cleanup(r0);
+		fp2_cleanup(r1);
+		fp2_cleanup(k);
+		return 0;
+	}
+	fp2_cleanup(r0);
+	fp2_cleanup(r1);
+	fp2_cleanup(k);
+	return 1;
+}
+
+int fp4_test(const BIGNUM *p, BN_CTX *ctx)
+{
+	const char *_a[] = {
+		"bec057c34cec656c05f236d9399cd00c64319632885d200f964e4591dd7ca77",
+		"55a10432b9095a12c106019c97fa1ed2a484d84bbb750bcf6a378c3f85ba9d09",
+		"9eb75c7b34e0259a59385602bd2210b844e6b9f6396443eed06dbd701b48a26c",
+		"76f63f8fb8272b173eaf93cb79e57444c816ef099b3fb11057977d1f3f50eb8"};
+	const char *_b[] = {
+		"1dd8569e8b7d7a53a362334330ff5b4e3beeb180466cf7d268c157ff724c2de7",
+		"48619106bcf6f34107318044223fa5ae3ec74573829f9873e4f06b41d0210762",
+		"79fdcb2d33f115ef5405c62b509be15adc14cc82abbe6f89978ed0de987377c6",
+		"71a8d1fd3d68cd689b9ed04872690c41858d98065b2535e70d1a6a8f2547f07e"};
+	const char *add_a_b[] = {
+		"29c45c1ac04c40aa63c156b0c499284f0231cae36ef2c9d362263c589023f85e",
+		"9e02953976004d53c83781e0ba39c480e34c1dbf3e14a4434f27f78155dba46b",
+		"627527a8662d9497d73a70de182f2acdff08f32dcaa7c49c828cf326d06ad4b5",
+		"791835f638eb401a0f89c9852a076385d20f06f6f4d930f81293e261193cff36"};
+	const char *dbl_a[] = {
+		"17d80af8699d8cad80be46db27339a018c8632c6510ba401f2c9c8b23baf94ee",
+		"ab4208657212b425820c03392ff43da54909b09776ea179ed46f187f0b753a12",
+		"872eb8f6671ca442dc6d00b584b55a2b67dae0a1584d9901bb6bdfb8533fff5b",
+		"edec7f1f704e562e7d5f2796f3cae889902dde13367f6220af2efa3e7ea1d70"};
+	const char *sub_a_b[] = {
+		"a453aeddabf4f2f4f3009b7a582938f7ac46fb2dfc93c90a761327818edce20d",
+		"d3f732bfc1266d1b9d4815875ba792465bd92d838d5735b854720fdb59995a7",
+		"24b9914e00ef0fab05328fd76c862f5d68d1ed738da5d46538deec9182d52aa6",
+		"4c0691fbc0bd4c3aae4fd4443ac41247e8e66a355909b405ddcea86ab1fe63b7"};
+	const char *neg_a[] = {
+		"aa53fa83cdd4e09b15a487e261f4fa445baf79e7f1f51cdaec0ab6cec5797b06",
+		"609efbcd499a4cdf14fda9b35d94a8727d6dbaff5f05e30c7b380ee85d96a874",
+		"1788a384cdc381577ccb554d386cb68cdd0bd954e116aaed1501ddb7c808a311",
+		"aed09c07072134406218b2133df07000d571245a80c6f3cadff62355ef5c36c5"};
+	const char *mul_a_b[] = {
+		"8e897a274c44e47c7db00d58bf08c020472e75f1e008a8a34975a6c947587f80",
+		"e8b79955f768f30ab48aa1b12b305a71fd12e252f34345d7692d58adf908739",
+		"a647307d347637d0525d62f9148d9bd7aabfb9c93ec03a7575404e5d4fa64310",
+		"65cbf741cdf37a3459727a9fcd84b10cc8b1d4c1a3641556de11434b330daf04"};
+	const char *mulv_a_b[] = {
+		"a0e8117c6960597af922616050142c70b2817d12ee2db30a0ebcafb960872cf2",
+		"a647307d347637d0525d62f9148d9bd7aabfb9c93ec03a7575404e5d4fa64310",
+		"8e897a274c44e47c7db00d58bf08c020472e75f1e008a8a34975a6c947587f80",
+		"e8b79955f768f30ab48aa1b12b305a71fd12e252f34345d7692d58adf908739"};
+	const char *sqr_a[] = {
+		"fb487bb1bee1c8d21956f8b5b7b1d93c5e7087b02666fc475f63b65cf5a2198",
+		"3a4deaf2a26a4f42fdb3bd34ae1c866a2d1ae5f5d9739d66ec758a38661d7639",
+		"a089b0d9a76cc56a2db2b56ab0df6e15f7a76ba8ad15e1f3b20accb2245bd827",
+		"8ad9618cfbada9f4cb296b5f219267785bc4d9b4d3070048e5301972005bb37f"};
+	const char *sqrv_a[] = {
+		"56cd3ce60debf9fa15b47fe1a7f8bf998c5b732c8ee7dd26007f036bc5eb23fc",
+		"a089b0d9a76cc56a2db2b56ab0df6e15f7a76ba8ad15e1f3b20accb2245bd827",
+		"fb487bb1bee1c8d21956f8b5b7b1d93c5e7087b02666fc475f63b65cf5a2198",
+		"3a4deaf2a26a4f42fdb3bd34ae1c866a2d1ae5f5d9739d66ec758a38661d7639"};
+	const char *inv_a[] = {
+		"7aa3d284401216d78e171627742b5a5dc3af41c15e112ceba1eb9e12ea3780cf",
+		"99711ed85be3e353d43f87600a9f416b64e1778d92e6b3fc374bc94f59772f70",
+		"8be97927776cbf6b7a162a5268df1d6a184ecd4ee56cc36273a7127ceabbebd4",
+		"7b4b924e6c5e548d2c5467e6db40bf35858f690d312d35066821af199a81ff67"};
+	const char *inv_1[] = {
+		"1",
+		"0",
+		"0",
+		"0"};
+	const char *inv_u[] = {
+		"0",
+		"5b2000000151d378eb01d5a7fac763a290f949a58d3d776df2b7cd93f1a8a2be",
+		"0",
+		"0"};
+	const char *inv_v[] = {
+		"0",
+		"0",
+		"0",
+		"5b2000000151d378eb01d5a7fac763a290f949a58d3d776df2b7cd93f1a8a2be"};
+
+	fp4_t r, a, b;
+	int ok;
+
+	fp4_init(r, ctx);
+	fp4_init(a, ctx);
+	fp4_init(b, ctx);
+
+	fp4_set_hex(a, _a);
+	fp4_set_hex(b, _b);
+
+	fp4_add(r, a, b, p, ctx);
+	ok = fp4_equ_hex(r, add_a_b, ctx);
+	printf("fp4 test %d: %s\n", __LINE__, ok ? "ok" : "error");
+
+	fp4_dbl(r, a, p, ctx);
+	ok = fp4_equ_hex(r, dbl_a, ctx);
+	printf("fp4 test %d: %s\n", __LINE__, ok ? "ok" : "error");
+
+	fp4_sub(r, a, b, p, ctx);
+	ok = fp4_equ_hex(r, sub_a_b, ctx);
+	printf("fp4 test %d: %s\n", __LINE__, ok ? "ok" : "error");
+
+	fp4_neg(r, a, p, ctx);
+	ok = fp4_equ_hex(r, neg_a, ctx);
+	printf("fp4 test %d: %s\n", __LINE__, ok ? "ok" : "error");
+
+	fp4_mul(r, a, b, p, ctx);
+	ok = fp4_equ_hex(r, mul_a_b, ctx);
+	printf("fp4 test %d: %s\n", __LINE__, ok ? "ok" : "error");
+
+	fp4_mul_v(r, a, b, p, ctx);
+	ok = fp4_equ_hex(r, mulv_a_b, ctx);
+	printf("fp4 test %d: %s\n", __LINE__, ok ? "ok" : "error");
+
+	fp4_sqr(r, a, p, ctx);
+	ok = fp4_equ_hex(r, sqr_a, ctx);
+	printf("fp4 test %d: %s\n", __LINE__, ok ? "ok" : "error");
+
+	fp4_sqr_v(r, a, p, ctx);
+	ok = fp4_equ_hex(r, sqrv_a, ctx);
+	printf("fp4 test %d: %s\n", __LINE__, ok ? "ok" : "error");
+
+	fp4_inv(r, a, p, ctx);
+	ok = fp4_equ_hex(r, inv_a, ctx);
+	printf("fp4 test %d: %s\n", __LINE__, ok ? "ok" : "error");
+
+	fp4_set_one(r);
+	fp4_inv(r, r, p, ctx);
+	ok = fp4_equ_hex(r, inv_1, ctx);
+	printf("fp4 test %d: %s\n", __LINE__, ok ? "ok" : "error");
+
+	fp4_set_u(r);
+	fp4_inv(r, r, p, ctx);
+	ok = fp4_equ_hex(r, inv_u, ctx);
+	printf("fp4 test %d: %s\n", __LINE__, ok ? "ok" : "error");
+
+	fp4_set_v(r);
+	fp4_inv(r, r, p, ctx);
+	ok = fp4_equ_hex(r, inv_v, ctx);
+	printf("fp4 test %d: %s\n", __LINE__, ok ? "ok" : "error");
+
+	return 0;
+}
+
+int fp12_init(fp12_t a, BN_CTX *ctx)
+{
+	int r;
+	r = fp4_init(a[0], ctx);
+	r &= fp4_init(a[1], ctx);
+	r &= fp4_init(a[2], ctx);
+	if (!r) {
+		fp4_cleanup(a[0]);
+		fp4_cleanup(a[1]);
+		fp4_cleanup(a[2]);
+	}
+	return r;
+}
+
+void fp12_cleanup(fp12_t a)
+{
+	fp4_cleanup(a[0]);
+	fp4_cleanup(a[1]);
+	fp4_cleanup(a[2]);
+}
+
+void fp12_clear_cleanup(fp12_t a)
+{
+	fp4_clear_cleanup(a[0]);
+	fp4_clear_cleanup(a[1]);
+	fp4_clear_cleanup(a[2]);
+}
+
+int fp12_print(const fp12_t a)
+{
+	fp4_print(a[0]);
+	fp4_print(a[1]);
+	fp4_print(a[2]);
+	return 1;
+}
+
+int fp12_is_zero(const fp12_t a)
+{
+	return fp4_is_zero(a[0])
+		&& fp4_is_zero(a[1])
+		&& fp4_is_zero(a[2]);
+}
+
+int fp12_is_one(const fp12_t a)
+{
+	return fp4_is_one(a[0])
+		&& fp4_is_zero(a[1])
+		&& fp4_is_zero(a[2]);
+}
+
+void fp12_set_zero(fp12_t r)
+{
+	fp4_set_zero(r[0]);
+	fp4_set_zero(r[1]);
+	fp4_set_zero(r[2]);
+}
+
+int fp12_set_one(fp12_t r)
+{
+	fp4_set_zero(r[1]);
+	fp4_set_zero(r[2]);
+	return fp4_set_one(r[0]);
+}
+
+int fp12_copy(fp12_t r, const fp12_t a)
+{
+	return fp4_copy(r[0], a[0])
+		&& fp4_copy(r[1], a[1])
+		&& fp4_copy(r[2], a[2]);
+}
+
+int fp12_set(fp12_t r, const fp4_t a0, const fp4_t a1, const fp4_t a2)
+{
+	return fp4_copy(r[0], a0)
+		&& fp4_copy(r[1], a1)
+		&& fp4_copy(r[2], a2);
+}
+
+int fp12_set_hex(fp12_t r, const char *str[12])
+{
+	return fp4_set_hex(r[0], str)
+		&& fp4_set_hex(r[1], str + 4)
+		&& fp4_set_hex(r[2], str + 8);
+}
+
+int fp12_set_fp4(fp12_t r, const fp4_t a)
+{
+	fp4_set_zero(r[1]);
+	fp4_set_zero(r[2]);
+	return fp4_copy(r[0], a);
+}
+
+int fp12_set_fp2(fp12_t r, const fp2_t a)
+{
+	fp4_set_zero(r[1]);
+	fp4_set_zero(r[2]);
+	return fp4_set_fp2(r[0], a);
+}
+
+int fp12_set_bn(fp12_t r, const BIGNUM *a)
+{
+	fp4_set_zero(r[1]);
+	fp4_set_zero(r[2]);
+	return fp4_set_bn(r[0], a);
+}
+
+int fp12_set_word(fp12_t r, unsigned long a)
+{
+	fp4_set_zero(r[1]);
+	fp4_set_zero(r[2]);
+	return fp4_set_word(r[0], a);
+}
+
+int fp12_set_u(fp12_t r)
+{
+	fp4_set_zero(r[1]);
+	fp4_set_zero(r[2]);
+	return fp4_set_u(r[0]);
+}
+
+int fp12_set_v(fp12_t r)
+{
+	fp4_set_zero(r[1]);
+	fp4_set_zero(r[2]);
+	return fp4_set_v(r[0]);
+}
+
+int fp12_set_w(fp12_t r)
+{
+	fp4_set_zero(r[0]);
+	fp4_set_zero(r[2]);
+	return fp4_set_one(r[1]);
+}
+
+int fp12_set_w_sqr(fp12_t r)
+{
+	fp4_set_zero(r[0]);
+	fp4_set_zero(r[1]);
+	return fp4_set_one(r[2]);
+}
+
+int fp12_equ(const fp12_t a, const fp12_t b)
+{
+	return fp4_equ(a[0], b[0])
+		&& fp4_equ(a[1], b[1])
+		&& fp4_equ(a[2], b[2]);
+}
+
+int fp12_equ_hex(const fp12_t a, const char *str[12], BN_CTX *ctx)
+{
+	fp12_t t;
+	fp12_init(t, ctx);
+	fp12_set_hex(t, str);
+	return fp12_equ(a, t);
+}
+
+int fp12_to_bin(const fp12_t a, unsigned char to[384])
+{
+	return fp4_to_bin(a[2], to)
+		&& fp4_to_bin(a[1], to + 128)
+		&& fp4_to_bin(a[0], to + 256);
+}
+
+int fp12_from_bin(fp4_t a, const unsigned char from[384])
+{
+	return fp4_from_bin(&a[2], from)
+		&& fp4_from_bin(&a[1], from + 128)
+		&& fp4_from_bin(&a[0], from + 256);
+}
+
+int fp12_add(fp12_t r, const fp12_t a, const fp12_t b, const BIGNUM *p, BN_CTX *ctx)
+{
+	return fp4_add(r[0], a[0], b[0], p, ctx)
+		&& fp4_add(r[1], a[1], b[1], p, ctx)
+		&& fp4_add(r[2], a[2], b[2], p, ctx);
+}
+
+int fp12_dbl(fp12_t r, const fp12_t a, const BIGNUM *p, BN_CTX *ctx)
+{
+	return fp4_dbl(r[0], a[0], p, ctx)
+		&& fp4_dbl(r[1], a[1], p, ctx)
+		&& fp4_dbl(r[2], a[2], p, ctx);
+}
+
+int fp12_tri(fp12_t r, const fp12_t a, const BIGNUM *p, BN_CTX *ctx)
+{
+	fp12_t t;
+	fp12_init(t, ctx);
+
+	if (!fp12_dbl(t, a, p, ctx)
+		|| !fp12_add(r, t, a, p, ctx)) {
+		fp12_cleanup(t);
+		return 0;
+	}
+	fp12_cleanup(t);
+	return 1;
+}
+
+int fp12_sub(fp12_t r, const fp12_t a, const fp12_t b, const BIGNUM *p, BN_CTX *ctx)
+{
+	return fp4_sub(r[0], a[0], b[0], p, ctx)
+		&& fp4_sub(r[1], a[1], b[1], p, ctx)
+		&& fp4_sub(r[2], a[2], b[2], p, ctx);
+}
+
+int fp12_neg(fp12_t r, const fp12_t a, const BIGNUM *p, BN_CTX *ctx)
+{
+	return fp4_neg(r[0], a[0], p, ctx)
+		&& fp4_neg(r[1], a[1], p, ctx)
+		&& fp4_neg(r[2], a[2], p, ctx);
+}
+
+int fp12_mul(fp12_t r, const fp12_t a, const fp12_t b, const BIGNUM *p, BN_CTX *ctx)
+{
+	fp4_t r0, r1, r2, t;
+	fp4_init(r0, ctx);
+	fp4_init(r1, ctx);
+	fp4_init(r2, ctx);
+
+	if (!fp4_init(t, ctx)
+		/* r0 = a0 * b0 + a1 * b2 * v + a2 * b1 * v */
+		|| !fp4_mul(r0, a[0], b[0], p, ctx)
+		|| !fp4_mul_v(t, a[1], b[2], p, ctx)
+		|| !fp4_add(r0, r0, t, p, ctx)
+		|| !fp4_mul_v(t, a[2], b[1], p, ctx)
+		|| !fp4_add(r0, r0, t, p, ctx)
+
+		/* r1 = a0*b1 + a1*b0 + a2*b2*v */
+		|| !fp4_mul(r1, a[0], b[1], p, ctx)
+		|| !fp4_mul(t, a[1], b[0], p, ctx)
+		|| !fp4_add(r1, r1, t, p, ctx)
+		|| !fp4_mul_v(t, a[2], b[2], p, ctx)
+		|| !fp4_add(r1, r1, t, p, ctx)
+
+		/* r2 = a0*b2 + a1*b1 + a2*b0 */
+		|| !fp4_mul(r2, a[0], b[2], p, ctx)
+		|| !fp4_mul(t, a[1], b[1], p, ctx)
+		|| !fp4_add(r2, r2, t, p, ctx)
+		|| !fp4_mul(t, a[2], b[0], p, ctx)
+		|| !fp4_add(r2, r2, t, p, ctx)
+
+		|| !fp4_copy(r[0], r0)
+		|| !fp4_copy(r[1], r1)
+		|| !fp4_copy(r[2], r2)) {
+
+		fp4_cleanup(r0);
+		fp4_cleanup(r1);
+		fp4_cleanup(r2);
+		fp4_cleanup(t);
+		return 0;
+	}
+	fp4_cleanup(r0);
+	fp4_cleanup(r1);
+	fp4_cleanup(r2);
+	fp4_cleanup(t);
+	return 1;
+}
+
+int fp12_sqr(fp12_t r, const fp12_t a, const BIGNUM *p, BN_CTX *ctx)
+{
+	fp4_t r0, r1, r2, t;
+	fp4_init(r0, ctx);
+	fp4_init(r1, ctx);
+	fp4_init(r2, ctx);
+	if (!(fp4_init(t, ctx))
+		/* r0 = a0^2 + 2*a1*a2*v */
+		|| !fp4_sqr(r0, a[0], p, ctx)
+		|| !fp4_mul_v(t, a[1], a[2], p, ctx)
+		|| !fp4_dbl(t, t, p, ctx)
+		|| !fp4_add(r0, r0, t, p, ctx)
+
+		/* r1 = 2*a0*a1 + a^2 * v */
+		|| !fp4_mul(r1, a[0], a[1], p, ctx)
+		|| !fp4_dbl(r1, r1, p, ctx)
+		|| !fp4_sqr_v(t, a[2], p, ctx)
+		|| !fp4_add(r1, r1, t, p, ctx)
+
+		/* r2 = 2*a0*a2 + a1^2*/
+		|| !fp4_mul(r2, a[0], a[2], p, ctx)
+		|| !fp4_dbl(r2, r2, p, ctx)
+		|| !fp4_sqr(t, a[1], p, ctx)
+		|| !fp4_add(r2, r2, t, p, ctx)
+
+		|| !fp4_copy(r[0], r0)
+		|| !fp4_copy(r[1], r1)
+		|| !fp4_copy(r[2], r2)) {
+
+		fp4_cleanup(r0);
+		fp4_cleanup(r1);
+		fp4_cleanup(r2);
+		fp4_cleanup(t);
+		return 0;
+	}
+	fp4_cleanup(r0);
+	fp4_cleanup(r1);
+	fp4_cleanup(r2);
+	fp4_cleanup(t);
+	return 1;
+}
+
+int fp12_inv(fp12_t r, const fp12_t a, const BIGNUM *p, BN_CTX *ctx)
+{
+	if (fp4_is_zero(a[2])) {
+		fp4_t k;
+		fp4_t t;
+		if (!fp4_init(t, ctx)) {
+			return 0;
+		}
+
+		fp4_t r0, r1, r2;
+		fp4_init(r0, ctx); // FIXME: r0, r1, r2 never used
+		fp4_init(r1, ctx);
+		fp4_init(r2, ctx);
+
+		if (!(fp4_init(k, ctx))
+			/* k = (a0^3 + a1^3 * v)^-1 */
+			|| !fp4_sqr(k, a[0], p, ctx)
+			|| !fp4_mul(k, k, a[0], p, ctx)
+			|| !fp4_sqr_v(t, a[1], p, ctx)
+			|| !fp4_mul(t, t, a[1], p, ctx)
+			|| !fp4_add(k, k, t, p, ctx)
+			|| !fp4_inv(k, k, p, ctx)
+
+			/* r2 = a1^2 * k */
+			|| !fp4_sqr(r[2], a[1], p, ctx)
+			|| !fp4_mul(r[2], r[2], k, p, ctx)
+
+			/* r1 = -(a0 * a1 * k) */
+			|| !fp4_mul(r[1], a[0], a[1], p, ctx)
+			|| !fp4_mul(r[1], r[1], k, p, ctx)
+			|| !fp4_neg(r[1], r[1], p, ctx)
+
+			/* r0 = a0^2 * k */
+			|| !fp4_sqr(r[0], a[0], p, ctx)
+			|| !fp4_mul(r[0], r[0], k, p, ctx)
+
+			) {
+
+			fp4_cleanup(k);
+			fp4_cleanup(t);
+			return 0;
+		}
+		fp4_cleanup(k);
+		fp4_cleanup(t);
+		return 1;
+
+	} else {
+
+		fp4_t t0, t1, t2, t3;
+
+		if (!(fp4_init(t0, ctx))
+			|| !(fp4_init(t1, ctx)) //FIXME
+			|| !(fp4_init(t2, ctx))
+			|| !(fp4_init(t3, ctx))
+
+			/* t0 = a1^2 - a0 * a2 */
+			|| !fp4_sqr(t0, a[1], p, ctx)
+			|| !fp4_mul(t1, a[0], a[2], p, ctx)
+			|| !fp4_sub(t0, t0, t1, p, ctx)
+
+			/* t1 = a0 * a1 - a2^2 * v */
+			|| !fp4_mul(t1, a[0], a[1], p, ctx)
+			|| !fp4_sqr_v(t2, a[2], p, ctx)
+			|| !fp4_sub(t1, t1, t2, p, ctx)
+
+			/* t2 = a0^2 - a1 * a2 * v */
+			|| !fp4_sqr(t2, a[0], p, ctx)
+			|| !fp4_mul_v(t3, a[1], a[2], p, ctx)
+			|| !fp4_sub(t2, t2, t3, p, ctx)
+
+			/* t3 = a2 * (t1^2 - t0 * t2)^-1 */
+			|| !fp4_sqr(t3, t1, p, ctx)
+			|| !fp4_mul(r[0], t0, t2, p, ctx)
+			|| !fp4_sub(t3, t3, r[0], p, ctx)
+			|| !fp4_inv(t3, t3, p, ctx)
+			|| !fp4_mul(t3, a[2], t3, p, ctx)
+
+			/* r0 = t2 * t3 */
+			|| !fp4_mul(r[0], t2, t3, p, ctx)
+
+			/* r1 = -(t1 * t3) */
+			|| !fp4_mul(r[1], t1, t3, p, ctx)
+			|| !fp4_neg(r[1], r[1], p, ctx)
+
+			/* r2 = t0 * t3 */
+			|| !fp4_mul(r[2], t0, t3, p, ctx)
+			) {
+			fp4_cleanup(t0);
+			fp4_cleanup(t1);
+			fp4_cleanup(t2);
+			fp4_cleanup(t3);
+			return 0;
+		}
+
+		fp4_cleanup(t0);
+		fp4_cleanup(t1);
+		fp4_cleanup(t2);
+		fp4_cleanup(t3);
+		return 1;
+	}
+
+	return 1;
+}
+
+//TODO: check this!
+int fp12_div(fp12_t r, const fp12_t a, const fp12_t b, const BIGNUM *p, BN_CTX *ctx)
+{
+	return fp12_inv(r, b, p, ctx)
+		&& fp12_mul(r, a, r, p, ctx);
+}
+
+int fp12_pow(fp12_t r, const fp12_t a, const BIGNUM *k, const BIGNUM *p, BN_CTX *ctx)
+{
+	int n, i;
+	fp12_t t;
+
+	fp12_init(t, ctx);
+
+	if (BN_is_zero(k)) {
+		return fp12_set_one(r);
+	}
+
+	n = BN_num_bits(k);
+	if (n < 1 || n > 256 * 12) {
+		return 0;
+	}
+
+	if (!fp12_copy(t, a)) {
+		return 0;
+	}
+	for (i = n - 2; i >= 0; i--) {
+		if (!fp12_sqr(t, t, p, ctx)) {
+			return 0;
+		}
+		if (BN_is_bit_set(k, i)) {
+			if (!fp12_mul(t, t, a, p, ctx)) {
+				return 0;
+			}
+		}
+	}
+
+	fp12_copy(r, t);
+
+	fp12_cleanup(t);
+	return 1;
+}
+
+int fp12_fast_expo_p1(fp12_t r, const fp12_t a, const BIGNUM *p, BN_CTX *ctx)
+{
+	return fp2_copy(r[0][0], a[0][0])
+		&& fp2_neg (r[0][1], a[0][1], p, ctx)
+		&& fp2_neg (r[1][0], a[1][0], p, ctx)
+		&& fp2_copy(r[1][1], a[1][1])
+		&& fp2_copy(r[2][0], a[2][0])
+		&& fp2_neg (r[2][1], a[2][1], p, ctx);
+}
+
+int fp12_fast_expo_p2(fp12_t r, const fp12_t a, const BIGNUM *p, BN_CTX *ctx)
+{
+	const BIGNUM *pw20;
+	const BIGNUM *pw21;
+	const BIGNUM *pw22;
+	const BIGNUM *pw23;
+	pw20 = SM9_get0_fast_final_exponent_p20();
+	pw21 = SM9_get0_fast_final_exponent_p21();
+	pw22 = SM9_get0_fast_final_exponent_p22();
+	pw23 = SM9_get0_fast_final_exponent_p23();
+
+	if(!fp2_copy(r[0][0], a[0][0])
+		|| !fp2_neg (r[0][1], a[0][1], p, ctx)
+		|| !fp2_mul_num(r[1][0], a[1][0], pw20, p, ctx)
+		|| !fp2_mul_num(r[1][1], a[1][1], pw21, p, ctx)
+		|| !fp2_mul_num(r[2][0], a[2][0], pw22, p, ctx)
+		|| !fp2_mul_num(r[2][1], a[2][1], pw23, p, ctx)) {
+
+		return 0;
+	}
+	return 1;
+}
+
+int fp12_test(const BIGNUM *p, BN_CTX *ctx)
+{
+	const char *_a[] = {
+		"3a4b2fdf33cfe01aab98d17aefc8d38b0508061c3117685839bd0dfdeb5783a8",
+		"88a9043bdc9abb43d241e7f62b0182d2c9f8de39d77d154a57e126d871e7bcc0",
+		"cd2a13d8e31bc262757be16f34fab3632bfdf4c5be36e86799037305a73210f",
+		"c407de563b8393c590e35b4df002bc9c79e3098558412a7d48bd62ca8723f3e",
+		"66cbdec2300eebf35b0ab8637a93e0174a4182957b853b227c2a1612adbac39d",
+		"481431cd7d6b54175b2b95e0036821ed9d757e383ae1a8d4b4ee95614271d328",
+		"4b1d12f271aa058193adc626fa8dd7c510678cd9f6a330c69652deaf6287948d",
+		"ab075f5760464947bdb5a644a1292776d5b6cfd735df54c3b4f1948f2cc1ac7",
+		"b16b0b0bd0c14d693f2bcb13c738669ed806e67b7b18d6f0bb62a2e8d94aeff6",
+		"7ca6249c1b6d5793aca0549ffaf1debe372a3c72129599afeae445865d0f53cd",
+		"16390ad0d7dd96377a198a2c0736278a453e39f006275b64d2027ea1570eac51",
+		"ab12e62fcda3b9e62074960a8b036f564b6d45bab4b183be000827a3183f2878"};
+	const char *_b[] = {
+		"7d09b50545b09312f786b5e0486de52aa79578b18961ede71e8e2e0b8a3aebb8",
+		"280070ecb08554a8bb799271eb3214f2b582c69858e2771aec594d72cd067f66",
+		"18ef945e265cc1a1b77a2c60db66a97b5365f939bd0dcf1cce578a822ce2fd74",
+		"9b0690c98e2c054afc4ddb3cf9e6e45f2ed8dbbf1edfd8eae8950454dd2f5033",
+		"540a59f0db96dba0c8efc44d50dc3b55b0b1a421ba8964b76759f5fa2db4604f",
+		"2a17506aeb7ce73497db53143dedda0429d1430453ee17d743a7d1425c19b79e",
+		"1ca32ffef87f1aeda06046ca9d345445424f00300daf0fd66d37b2620572db62",
+		"18c11cc3b61d709d7ed976e3da5ba630bc49d17bda470c3aace50d4db3e8ae5d",
+		"4b91ac95d741011137c66858ca98dbbcd5744b77c51894e1fda0c5b80959cfd3",
+		"1d8e5cae7d4463ee8f37b73037066455b284cca92ca0255eef8b0733b1d7b7f",
+		"839ea9892563527b6e653cd9ba665d6284c7696d5aedc884e469cb90e352a91c",
+		"ad699cb305b6d98aaac6fdb684e59c0d194db0213214a7df4bbed0240545a520"};
+	const char *add_a_b[] = {
+		"114e4e476dccc3bcd1bdc0b42a7f1708aaaeb829ffe676372dba0e1924129e3",
+		"b0a975288d200fec8dbb7a68163397c57f7ba4d2305f8c65443a744b3eee3c26",
+		"25c2359bb48e7dc7ded1ea77ceb654b18625d88618f13da347e7c1b287561e83",
+		"a7470eaef1e43e87555c10f1d8e71028f6770c577463eb92bd20da8185a18f71",
+		"49638b3090220a24df6d160d5e15427d900936c1b93b0fdfe1470e4f81dde6f",
+		"722b823868e83b4bf306e8f44155fbf1c746c13c8ecfc0abf89666a39e8b8ac6",
+		"67c042f16a29206f340e0cf197c22c0a52b68d0a0452409d038a911167fa6fef",
+		"237192b92c21d531fab4d148246e38a829a53e794da50186e8342696a6b4c924",
+		"46bcb7a1a55ea788a0ee881c9c427b168b889ea825b67cf6d393cd78ff537a4c",
+		"7e7f0a6703419dd29593d012fe6245039252893ca55f9c05d9dcf5f9982ccf4c",
+		"99d7b459fd40e8b2e87ec705c19c84ecca05a35d611523e9b66c4a323a61556d",
+		"a23c82e2d0b6ec7ef537e8711a5a441e42c86290cc4b3cc166575c9f3a33881b"};
+	const char *dbl_a[] = {
+		"74965fbe679fc0355731a2f5df91a7160a100c38622ed0b0737a1bfbd6af0750",
+		"5b120877b691cf95ce80249c60743e6071ff2928947f3bb8ca52b289007e3403",
+		"19a5427b1c63784c4eaf7c2de69f566c657fbe98b7c6dd0cf3206e60b4e6421e",
+		"1880fbcac7707278b21c6b69be0057938f3c6130ab08254fa917ac5950e47e7c",
+		"1757bd845d7a30f4e011c576ff98f8e9729071dfdc8f876912e490fd782441bd",
+		"9028639afad6a82eb6572bc006d043db3aeafc7075c351a969dd2ac284e3a650",
+		"963a25e4e3540b03275b8c4df51baf8a20cf19b3ed46618d2ca5bd5ec50f291a",
+		"1560ebeaec08c928f7b6b4c8942524eedab6d9fae6bbea98769e3291e598358e",
+		"ac9616179edef3e0a853ead798e205f88e1b39abdbb6bf059155aaa9cf449a6f",
+		"430c493834370835833cfdf00054f6374c61e5990ab04483f058efe4d6cd621d",
+		"2c7215a1afbb2c6ef43314580e6c4f148a7c73e00c4eb6c9a404fd42ae1d58a2",
+		"9fe5cc5f98a3ccda6ae580c52078176774e7f82a4ee818a01aa0b41e4d2d0b73"};
+	const char *tri_a[] = {
+		"aee18f9d9b6fa05002ca7470cf5a7aa10f18125493463908ad3729f9c2068af8",
+		"2d7b0cb39088e3e7cabe614295e6f9ee1a057417518162273cc43e398f14ab46",
+		"2677e3b8aa95347276073a44d9ef01a2983f9de513aa4b936cb0a5910f59632d",
+		"24c179b02b28abb50b2aa11e9d00835d56da91c9008c37f77da38285f956bdba",
+		"7e239c468d891ce83b1c7dda7a2cd900bcd1f4755814c28b8f0ea71025df055a",
+		"21fc9568759e55543b7f165014a99e83b66de75d962a0ba2395c24fbe40433fb",
+		"2b1738d7525a6992e505a724fa1ac00a0f441342c96ea377dd8900e64445782a",
+		"201161e0620d2dbd73920f2cde37b766481246f85a19dfe4b1ed4bdad8645055",
+		"a7c121236cfc9a58117c0a9b6a8ba552442f8cdc3c54a71a6748b26ac53e44e8",
+		"9726dd44d00b8d759d9a74005b80db061998ec002caef57f5cd9a43508b706d",
+		"42ab20728798c2a66e4c9e8415a2769ecfbaadd01276122e76077be4052c04f3",
+		"94b8b28f63a3dfceb5566b7fb5ecbf789e62aa99e91ead8235394099821aee6e"};
+	const char *sub_a_b[] = {
+		"73817ad9f0c2f3f98a15c6ea9ce9b5a57f6520b5c230694d009e7b1a446ddd6d",
+		"60a8934f2c15669b16c855843fcf6de0147617a17e9a9e2f6b87d965a4e13d5a",
+		"aa230cdf6a78a17645e13d060d77c900014c795db9508e4590a847d610e16918",
+		"2779ed1bd82fdae332c405c7daa80eafbab7e824511f2898d1666cffae943488",
+		"12c184d154781052921af41629b7a4c1998fde73c0fbd66b14d020188006634e",
+		"1dfce16291ee6ce2c35042cbc57a47e973a43b33e6f390fd7146c41ee6581b8a",
+		"2e79e2f3792aea93f34d7f5c5d59837fce188ca9e8f420f0291b2c4d5d14b92b",
+		"a82f5931c28a9ae8d3058ed06545b38bd3042eccb391d7ed73d9a7232234b1e7",
+		"65d95e75f9804c58076562bafc9f8ae202929b03b600420ebdc1dd30cff12023",
+		"7acd3ed133991154c3acd92cf7817878dc01efa77fcb9759fbeb951321f1d84e",
+		"48da6147b51deaade1b7f8a2425e916ce26963cdc5b481bbd3084e38570d48b2",
+		"b3e9497cca90874d4bb143a3fbac9a8e541228e49d17caba99b8f2a6f64ac8d5"};
+	const char *neg_a[] = {
+		"7bf4d020ced3c6d72a6ad9d505c5f3ba1cea8d2ee9638683abb28d29f7f9c1d5",
+		"2d96fbc42608ebae03c1c359ca8d447257f9b51142fdd9918d8e744f716988bd",
+		"a96d5ec27471eacbaeabed39023f1c0eef32b3febe9780556bdf63f788de246e",
+		"a9ff821a9eeb6db57cf5759b168e9b7b5a5462b2c4f6dc3410e3c4fb3adf063f",
+		"4f74213dd294bafe7af8f2ec7afae72dd7b110b59ef5b3b969458515359681e0",
+		"6e2bce32853852da7ad8156ff226a557847d1512df994607308105c6a0df7255",
+		"6b22ed0d90f9a1704255e528fb00ef80118b067123d7be154f1cbc7880c9b0f0",
+		"ab8f8a0a8c9f425d5a2850ebab7c34cdb497264da71cf98faa2081def0852ab6",
+		"4d4f4f431e2598896d7e03c2e5660a649ebaccf9f6217eb2a0cf83f0a065587",
+		"3999db63e7364f5e296356affa9ce886eac856d907e5552bfa8b55a18641f1b0",
+		"a006f52f2ac610ba5bea2123ee589fbadcb4595b14539377136d1c868c42992c",
+		"b2d19d034ffed0bb58f15456a8b57eed6854d9065c96b1de5677384cb121d05"};
+	const char *mul_a_b[] = {
+		"b053bed5afdc274ecf4d5ed22d464f121545c877192cb8bc2ee213fbd18c6a6d",
+		"b5b637e1176fdc19fe6eca269ae7653766f4583d9cef7c4afdf479f9f90253d2",
+		"b4f0bdbcf61a49b3d05760978b3f81ae2e14df914d39db2f9250d66a05dc6925",
+		"acb2108b6694c3123885af6df5b9b3ab9b68930ec54b7fccfd7228bcb78e9fd3",
+		"606eb0f669124e2afa06f4ed23bee5ca9445a1a9cc6f08202564ed69aab0dd2",
+		"3f9ca64637e2ff3453e54a450a2f5a05c1d3254cd785d841cb16b6bad0879de5",
+		"4c8165e6d96c598acee552bf0a98833af5e44dc867ca8fe7f48aa158bac619e0",
+		"5d8c852ab5b21eec17dd9919a60e6d1ba24f9a8064af5675b6173ec6279dc35e",
+		"3dca5d804536983f2d46853d58dd994a9212f18ef3e8df749342d71a9a92989c",
+		"64af38a985084da07ccbae1a91d2c7f09e7779d0285a1b9b753860e9937c4a47",
+		"77fe4dbaca161d0f7f99b4f186d8d12449201e7ba574ebbbb45bab99406b119c",
+		"e9f3cfc4c197176ad588c1d3fa965172ff9f1046831a92490885af616c3edb4"};
+	const char *sqr_a[] = {
+		"1e92886bddb591a8f6ba96d288bdfd7f2e50f6c7151c58e3682628f1827a0a19",
+		"a939f4ca345e7007c25484bb222cbac1877b3bc19fde66051c3b4ad1afcd3be3",
+		"a911367ed1020dc600b229e755fd0e892035a7833feb5d1ec9735b0606bde3f7",
+		"5064e0d6b9aa4c5d0b1a345f7e51de0789bd91753fbc92bd041733a0b5618b58",
+		"426fddbc40017ef829487b9644308737cde22889931cd3c25d6d6b9c5a80eacc",
+		"b2b8d0264942b6f2ba2fce697ffd663e1f7d2337d964a6a1f1d3a8f274293074",
+		"ee7c6cd61ae3316b70d47c6e0ffffff18e7f9a11a1b1a3d4faee189c42946af",
+		"4cd26db84bbad4051abfe8e65071eb45ae0cb9edad42fbaebc624a4ab1624c06",
+		"59168d22a22f0e835aafb8e47f9e3ca3ac0787d75472f5bb2a3200d3d8fe8901",
+		"916ad4377c2939b7fa7c9e4ca76c7191a60db21db13e028b411af8cac9305355",
+		"816985d7bc47c4ebb55b186b8eefd2609d01623b7a9f034f31a2434d0d022e49",
+		"b1f0bc64e20661ce1067112a7306ff9dcf2b2394b038062cfcb79652f92e4678"};
+	const char *inv_a[] = {
+		"76003429929c9970eb611b8579900f9806b1acb8955a8effea9c28a1156fe2f1",
+		"90682a0a7b278173bb8aed9bd9fed33e1c36af5dc968f8f4e01c8133ce552e5f",
+		"a29d8805baa933bd50ff7ba55ea3feb3df8811d773c7fa4f846bbbddbb9a5acf",
+		"20b64affa212b98e4ea552f87be84ea52183bac9df7c4bdbe07212cc8b9654da",
+		"addbc46339a3c49e7e856d73255974d0a5fd65da56a36f536a298a4d8082069e",
+		"8c43a01e79cef927fc9b2ec58d63ecfdc720d47e3b9cfaf7edf1ec30fd9764ef",
+		"46da195353db6d51fb72c9134f14db01552e84452b70109f1aa9c5bdca80d9b6",
+		"8a72fe0032b82fab1e83b5cd8945fd6c9cbe14486a5b0b4b3ee0680d2ac38f15",
+		"5270e560c6f81f183614c77e37bb9dba009b6769c91a856505ac3954ef244aa3",
+		"8640186e2f43c293f19f3470645fbafcfe132b5eefc60b7e3004702f2e41b906",
+		"2a46b3cb4d9484e9a20aa399e2a1a3895646f5784849abe5f251b6bb1394d135",
+		"99c14f695f65df2b583f7669f30b60ce9b7f2b01036d89917f095792fd072788"};
+	char *r_str =
+		"033C8616B06704813203DFD00965022ED15975C662337AED648835DC4B1CBE";
+	char *w_str[] = { /* w = g^r */
+		"1F96B08E97997363911314705BFB9A9DBB97F75553EC90FBB2DDAE53C8F68E42",
+		"6A814AAF475F128AEF43A128E37F80154AE6CB92CAD7D1501BAE30F750B3A9BD",
+		"898D60848026B7EFB8FCC1B2442ECF0795F8A81CEE99A6248F294C82C90D26BD",
+		"44643CEAD40F0965F28E1CD2895C3D118E4F65C9A0E3E741B6DD52C0EE2D25F5",
+		"0656FCB663D24731E80292188A2471B8B68AA993899268499D23C89755A1A897",
+		"4F8624EB435B838CCA77B2D0347E65D5E46964412A096F4150D8C5EDE5440DDF",
+		"3F012DB04BA59FE88DB889321CC2373D4C0C35E84F7AB1FF33679BCA575D6765",
+		"A543D25609AE943920679194ED30328BB33FD15660BDE485C6B79A7B32B01398",
+		"8EAF5D179A1836B359A9D1D9BFC19F2EFCDB829328620962BD3FDF15F2567F58",
+		"30DADC5CD9E207AEE32209F6C3CA3EC0D800A1A42D33C73153DED47C70A39D2E",
+		"815AEBA217AD502DA0F48704CC73CABB3C06209BD87142E14CBD99E8BCA1680F",
+		"81377B8FDBC2839B4FA2D0E0F8AA6853BBBE9E9C4099608F8612C6078ACD7563"};
+	const char *inv_1[] = {
+		"1",
+		"0",
+		"0",
+		"0",
+		"0",
+		"0",
+		"0",
+		"0",
+		"0",
+		"0",
+		"0",
+		"0"};
+	const char *inv_u[] = {
+		"0",
+		"5b2000000151d378eb01d5a7fac763a290f949a58d3d776df2b7cd93f1a8a2be",
+		"0",
+		"0",
+		"0",
+		"0",
+		"0",
+		"0",
+		"0",
+		"0",
+		"0",
+		"0"};
+	const char *inv_v[] = {
+		"0",
+		"0",
+		"0",
+		"5b2000000151d378eb01d5a7fac763a290f949a58d3d776df2b7cd93f1a8a2be",
+		"0",
+		"0",
+		"0",
+		"0",
+		"0",
+		"0",
+		"0",
+		"0"};
+	const char *inv_w[] = {
+		"0",
+		"0",
+		"0",
+		"0",
+		"0",
+		"0",
+		"0",
+		"0",
+		"0",
+		"0",
+		"0",
+		"5b2000000151d378eb01d5a7fac763a290f949a58d3d776df2b7cd93f1a8a2be"};
+	const char *inv_w2[] = {
+		"0",
+		"0",
+		"0",
+		"0",
+		"0",
+		"0",
+		"0",
+		"5b2000000151d378eb01d5a7fac763a290f949a58d3d776df2b7cd93f1a8a2be",
+		"0",
+		"0",
+		"0",
+		"0"};
+
+
+	fp12_t r, a, b;
+	int ok;
+
+	fp12_init(r, ctx);
+	fp12_init(a, ctx);
+	fp12_init(b, ctx);
+
+	fp12_set_hex(a, _a);
+	fp12_set_hex(b, _b);
+
+	fp12_add(r, a, b, p, ctx);
+	ok = fp12_equ_hex(r, add_a_b, ctx);
+	printf("fp12 test %d: %s\n", __LINE__, ok ? "ok" : "error");
+
+	fp12_dbl(r, a, p, ctx);
+	ok = fp12_equ_hex(r, dbl_a, ctx);
+	printf("fp12 test %d: %s\n", __LINE__, ok ? "ok" : "error");
+
+	fp12_tri(r, a, p, ctx);
+	ok = fp12_equ_hex(r, tri_a, ctx);
+	printf("fp12 test %d: %s\n", __LINE__, ok ? "ok" : "error");
+
+	fp12_sub(r, a, b, p, ctx);
+	ok = fp12_equ_hex(r, sub_a_b, ctx);
+	printf("fp12 test %d: %s\n", __LINE__, ok ? "ok" : "error");
+
+	fp12_neg(r, a, p, ctx);
+	ok = fp12_equ_hex(r, neg_a, ctx);
+	printf("fp12 test %d: %s\n", __LINE__, ok ? "ok" : "error");
+
+	fp12_mul(r, a, b, p, ctx);
+	ok = fp12_equ_hex(r, mul_a_b, ctx);
+	printf("fp12 test %d: %s\n", __LINE__, ok ? "ok" : "error");
+
+	fp12_sqr(r, a, p, ctx);
+	ok = fp12_equ_hex(r, sqr_a, ctx);
+	printf("fp12 test %d: %s\n", __LINE__, ok ? "ok" : "error");
+
+	fp12_inv(r, a, p, ctx);
+	ok = fp12_equ_hex(r, inv_a, ctx);
+	printf("fp12 test %d: %s\n", __LINE__, ok ? "ok" : "error");
+
+	fp12_set_one(r);
+	fp12_inv(r, r, p, ctx);
+	ok = fp12_equ_hex(r, inv_1, ctx);
+	printf("fp12 test %d: %s\n", __LINE__, ok ? "ok" : "error");
+
+	fp12_set_u(r);
+	fp12_inv(r, r, p, ctx);
+	ok = fp12_equ_hex(r, inv_u, ctx);
+	printf("fp12 test %d: %s\n", __LINE__, ok ? "ok" : "error");
+
+	fp12_set_v(r);
+	fp12_inv(r, r, p, ctx);
+	ok = fp12_equ_hex(r, inv_v, ctx);
+	printf("fp12 test %d: %s\n", __LINE__, ok ? "ok" : "error");
+
+	fp12_set_w(r);
+	fp12_inv(r, r, p, ctx);
+	ok = fp12_equ_hex(r, inv_w, ctx);
+	printf("fp12 test %d: %s\n", __LINE__, ok ? "ok" : "error");
+
+	fp12_set_w_sqr(r);
+	fp12_inv(r, r, p, ctx);
+	ok = fp12_equ_hex(r, inv_w2, ctx);
+	printf("fp12 test %d: %s\n", __LINE__, ok ? "ok" : "error");
+
+	return 0;
+}
+
+int point_init(point_t *P, BN_CTX *ctx)
+{
+	int r;
+	r = fp2_init(P->X, ctx);
+	r &= fp2_init(P->Y, ctx);
+	r &= fp2_init(P->Z, ctx);
+	r &= fp2_set_one(P->Y);
+	if (!r) {
+		fp2_cleanup(P->X);
+		fp2_cleanup(P->Y);
+		fp2_cleanup(P->Z);
+		return 0;
+	}
+	fp2_set_zero(P->X);
+	fp2_set_zero(P->Z);
+	return 1;
+}
+
+void point_cleanup(point_t *P)
+{
+	fp2_cleanup(P->X);
+	fp2_cleanup(P->Y);
+	fp2_cleanup(P->Z);
+}
+
+void point_print(const point_t *P)
+{
+	printf(" X1: %s\n", BN_bn2hex((P->X)[1]));
+	printf(" X0: %s\n", BN_bn2hex((P->X)[0]));
+	printf(" Y1: %s\n", BN_bn2hex((P->Y)[1]));
+	printf(" Y0: %s\n", BN_bn2hex((P->Y)[0]));
+	printf(" Z1: %s\n", BN_bn2hex((P->Z)[1]));
+	printf(" Z0: %s\n", BN_bn2hex((P->Z)[0]));
+	printf("\n");
+}
+
+int point_copy(point_t *R, const point_t *P)
+{
+	return fp2_copy(R->X, P->X)
+		&& fp2_copy(R->Y, P->Y)
+		&& fp2_copy(R->Z, P->Z);
+}
+
+int point_set_to_infinity(point_t *P)
+{
+	fp2_set_zero(P->X);
+	fp2_set_zero(P->Z);
+	return fp2_set_one(P->Y);
+}
+
+int point_is_at_infinity(const point_t *P)
+{
+	return fp2_is_zero(P->X)
+		&& fp2_is_one(P->Y)
+		&& fp2_is_zero(P->Z);
+}
+
+int point_equ(const point_t *P, const point_t *Q)
+{
+	return fp2_equ(P->X, Q->X)
+		&& fp2_equ(P->Y, Q->Y)
+		&& fp2_equ(P->Z, Q->Z);
+}
+
+int point_set_affine_coordinates(point_t *P, const fp2_t x, const fp2_t y)
+{
+	return fp2_copy(P->X, x)
+		&& fp2_copy(P->Y, y)
+		&& fp2_set_one(P->Z);
+}
+
+int point_set_affine_coordinates_hex(point_t *P, const char *str[4])
+{
+	fp2_set_hex(P->X, str);
+	fp2_set_hex(P->Y, str + 2);
+	fp2_set_one(P->Z);
+	return 1;
+}
+
+int point_equ_hex(const point_t *P, const char *str[4], BN_CTX *ctx)
+{
+	point_t T;
+	point_init(&T, ctx);
+	point_set_affine_coordinates_hex(&T, str);
+	return point_equ(P, &T);
+}
+
+int point_set_affine_coordinates_bignums(point_t *P,
+	const BIGNUM *x0, const BIGNUM *x1, const BIGNUM *y0, const BIGNUM *y1)
+{
+	return fp2_set(P->X, x0, x1)
+		&& fp2_set(P->Y, y0, y1)
+		&& fp2_set_one(P->Z);
+}
+
+int point_get_affine_coordinates(const point_t *P, fp2_t x, fp2_t y)
+{
+	return fp2_copy(x, P->X)
+		&& fp2_copy(y, P->Y)
+		&& fp2_is_one(P->Z);
+}
+
+int point_get_ext_affine_coordinates(const point_t *P, fp12_t x, fp12_t y, const BIGNUM *p, BN_CTX *ctx)
+{
+	int r;
+	fp2_t xP;
+	fp2_t yP;
+	fp12_t wem2;
+	fp12_t wem3;
+
+	r = 1;
+	r &= fp2_init(xP, ctx);
+	r &= fp2_init(yP, ctx);
+	r &= fp12_init(wem2, ctx);
+	r &= fp12_init(wem3, ctx);
+	if (!r) {
+		goto end;
+	}
+
+	r = 0;
+	if (!point_get_affine_coordinates(P, xP, yP)
+		|| !fp12_set_fp2(x, xP)
+		|| !fp12_set_fp2(y, yP)
+
+		/* x = x * w^-2 */
+		|| !fp12_set_w_sqr(wem2)
+		|| !fp12_inv(wem2, wem2, p, ctx)
+		|| !fp12_mul(x, x, wem2, p, ctx)
+
+		/* y = y * w^-3 */
+		|| !fp12_set_v(wem3)
+		|| !fp12_inv(wem3, wem3, p, ctx)
+		|| !fp12_mul(y, y, wem3, p, ctx)) {
+		//goto end;
+	}
+	r = 1;
+
+end:
+	fp2_cleanup(xP);
+	fp2_cleanup(yP);
+	fp12_cleanup(wem2);
+	fp12_cleanup(wem3);
+	return r;
+}
+
+int point_set_ext_affine_coordinates(point_t *P, const fp12_t x, const fp12_t y, const BIGNUM *p, BN_CTX *ctx)
+{
+	fp12_t tx;
+	fp12_t ty;
+
+	fp12_init(tx, ctx);
+	fp12_init(ty, ctx);
+
+	fp12_set_w_sqr(tx);
+	fp12_set_v(ty);
+	fp12_mul(tx, x, tx, p, ctx);
+	fp12_mul(ty, y, ty, p, ctx);
+
+	point_set_affine_coordinates(P, tx[0][0], ty[0][0]);
+
+	fp12_cleanup(tx);
+	fp12_cleanup(ty);
+	return 1;
+}
+
+int point_is_on_curve(point_t *P, const BIGNUM *p, BN_CTX *ctx)
+{
+	int r;
+	fp2_t x, y, b, t;
+
+	r = fp2_init(x, ctx);
+	r &= fp2_init(y, ctx);
+	r &= fp2_init(b, ctx);
+	r &= fp2_init(t, ctx);
+	if (!r) {
+		goto end;
+	}
+
+	fp2_set_5u(b);
+
+	if (!point_get_affine_coordinates(P, x, y)
+		/* x^3 + 5 * u */
+		|| !fp2_sqr(t, x, p, ctx)
+		|| !fp2_mul(x, x, t, p, ctx)
+		|| !fp2_add(x, x, b, p, ctx)
+		/* y^2 */
+		|| !fp2_sqr(y, y, p, ctx)) {
+		r = 0;
+		goto end;
+	}
+	r = fp2_equ(x, y);
+
+end:
+	fp2_cleanup(x);
+	fp2_cleanup(y);
+	fp2_cleanup(t);
+	return r;
+}
+
+int point_to_octets(const point_t *P, unsigned char to[129], BN_CTX *ctx)
+{
+	to[0] = 0x04;
+
+	if (fp2_is_one(P->Z)) {
+		fp2_to_bin(P->X, to + 1);
+		fp2_to_bin(P->Y, to + 65);
+	} else {
+		fp2_t x, y;
+		fp2_init(x, ctx);
+		fp2_init(y, ctx);
+		point_get_affine_coordinates(P, x, y);
+
+		fp2_to_bin(x, to + 1);
+		fp2_to_bin(y, to + 65);
+		fp2_cleanup(x);
+		fp2_cleanup(y);
+	}
+	return 1;
+}
+
+int point_from_octets(point_t *P, const unsigned char from[129], const BIGNUM *p, BN_CTX *ctx)
+{
+	if (from[0] != 0x04) {
+		return 0;
+	}
+	fp2_from_bin(P->X, from + 1);
+	fp2_from_bin(P->Y, from + 65);
+	fp2_set_one(P->Z);
+	return point_is_on_curve(P, p, ctx);
+}
+
+int point_dbl(point_t *R, const point_t *P, const BIGNUM *p, BN_CTX *ctx)
+{
+	int r;
+	fp2_t x3, y3, x1, y1, lambda, t;
+
+	r = 1;
+	r &= fp2_init(x1, ctx);
+	r &= fp2_init(y1, ctx);
+	r &= fp2_init(x3, ctx);
+	r &= fp2_init(y3, ctx);
+	r &= fp2_init(lambda, ctx);
+	r &= fp2_init(t, ctx);
+	if (!r) {
+		goto end;
+	}
+
+	if (point_is_at_infinity(P)) {
+		r = point_set_to_infinity(R);
+		goto end;
+	}
+
+	if (!point_get_affine_coordinates(P, x1, y1)
+		/* lambda = 3 * x1^2 / 2 * y1 */
+		|| !fp2_sqr(lambda, x1, p, ctx)
+		|| !fp2_tri(lambda, lambda, p, ctx)
+		|| !fp2_dbl(t, y1, p, ctx)
+		|| !fp2_inv(t, t, p, ctx)
+		|| !fp2_mul(lambda, lambda, t, p, ctx)
+
+		/* x3 = lambda^2 - 2 * x1 */
+		|| !fp2_sqr(x3, lambda, p, ctx)
+		|| !fp2_dbl(t, x1, p, ctx)
+		|| !fp2_sub(x3, x3, t, p, ctx)
+
+		/* y3 = lambda * (x1 - x3) - y1 */
+		|| !fp2_sub(y3, x1, x3, p, ctx)
+		|| !fp2_mul(y3, lambda, y3, p, ctx)
+		|| !fp2_sub(y3, y3, y1, p, ctx)) {
+		r = 0;
+		goto end;
+	}
+
+	r = point_set_affine_coordinates(R, x3, y3);
+
+end:
+	fp2_cleanup(x1);
+	fp2_cleanup(y1);
+	fp2_cleanup(x3);
+	fp2_cleanup(y3);
+	fp2_cleanup(lambda);
+	fp2_cleanup(t);
+	return r;
+}
+
+int point_add(point_t *R, const point_t *P, const point_t *Q, const BIGNUM *p, BN_CTX *ctx)
+{
+	int r;
+	fp2_t x1;
+	fp2_t y1;
+	fp2_t x2;
+	fp2_t y2;
+	fp2_t x3;
+	fp2_t y3;
+	fp2_t lambda;
+	fp2_t t;
+
+	if (point_is_at_infinity(P)) {
+		return point_copy(R, Q);
+
+	}
+
+	if (point_is_at_infinity(Q)) {
+		return point_copy(R, P);
+	}
+
+	if (point_equ(P, Q)) {
+		return point_dbl(R, P, p, ctx);
+	}
+
+	r = 1;
+	r &= fp2_init(x1, ctx);
+	r &= fp2_init(y1, ctx);
+	r &= fp2_init(x2, ctx);
+	r &= fp2_init(y2, ctx);
+	r &= fp2_init(x3, ctx);
+	r &= fp2_init(y3, ctx);
+	r &= fp2_init(lambda, ctx);
+	r &= fp2_init(t, ctx);
+	if (!r) {
+		goto end;
+	}
+
+	r = 0;
+
+	if (!point_get_affine_coordinates(P, x1, y1)
+		|| !point_get_affine_coordinates(Q, x2, y2)
+		|| !fp2_add(t, y1, y2, p, ctx)) {
+		goto end;
+	}
+
+	if (fp2_equ(x1, x2) && fp2_is_zero(t)) {
+		r = point_set_to_infinity(R);
+		goto end;
+	}
+
+	/* lambda = (y2 - y1)/(x2 - x1) */
+	if (!fp2_sub(lambda, y2, y1, p, ctx)
+		|| !fp2_sub(t, x2, x1, p, ctx)
+		|| !fp2_inv(t, t, p, ctx)
+		|| !fp2_mul(lambda, lambda, t, p, ctx)
+
+		/* x3 = lambda^2 - x1 - x2 */
+		|| !fp2_sqr(x3, lambda, p, ctx)
+		|| !fp2_sub(x3, x3, x1, p, ctx)
+		|| !fp2_sub(x3, x3, x2, p, ctx)
+
+		/* y3 = lambda * (x1 - x3) - y1 */
+		|| !fp2_sub(y3, x1, x3, p, ctx)
+		|| !fp2_mul(y3, lambda, y3, p, ctx)
+		|| !fp2_sub(y3, y3, y1, p, ctx)) {
+		goto end;
+	}
+
+	r = point_set_affine_coordinates(R, x3, y3);
+
+end:
+	fp2_cleanup(x1);
+	fp2_cleanup(y1);
+	fp2_cleanup(x2);
+	fp2_cleanup(y2);
+	fp2_cleanup(x3);
+	fp2_cleanup(y3);
+	fp2_cleanup(lambda);
+	fp2_cleanup(t);
+	return r;
+}
+
+int point_neg(point_t *R, const point_t *P, const BIGNUM *p, BN_CTX *ctx)
+{
+	return fp2_copy(R->X, P->X)
+		&& fp2_neg(R->Y, P->Y, p, ctx)
+		&& fp2_copy(R->Z, P->Z);
+}
+
+int point_sub(point_t *R, const point_t *P, const point_t *Q, const BIGNUM *p, BN_CTX *ctx)
+{
+	point_t T;
+
+	memset(&T, 0, sizeof(T));
+	if (!point_init(&T, ctx)
+		|| !point_neg(&T, Q, p, ctx)
+		|| !point_add(R, P, &T, p, ctx)) {
+		point_cleanup(&T);
+		return 0;
+	}
+	point_cleanup(&T);
+	return 1;
+}
+
+int point_mul(point_t *R, const BIGNUM *k, const point_t *P, const BIGNUM *p, BN_CTX *ctx)
+{
+	int i, n;
+
+	if (BN_is_zero(k)) {
+		return point_set_to_infinity(R);
+	}
+
+	if (!point_copy(R, P)) {
+		return 0;
+	}
+	n = BN_num_bits(k);
+	for (i = n - 2; i >= 0; i--) {
+
+		if (!point_dbl(R, R, p, ctx)) {
+			return 0;
+		}
+		if (BN_is_bit_set(k, i)) {
+			if (!point_add(R, R, P, p, ctx)) {
+				return 0;
+			}
+		}
+	}
+
+	return 1;
+}
+
+int point_mul_generator(point_t *R, const BIGNUM *k, const BIGNUM *p, BN_CTX *ctx)
+{
+	point_t G;
+
+	memset(&G, 0, sizeof(G));
+	point_init(&G, ctx);
+	point_set_affine_coordinates_bignums(&G,
+		SM9_get0_generator2_x0(),
+		SM9_get0_generator2_x1(),
+		SM9_get0_generator2_y0(),
+		SM9_get0_generator2_y1());
+
+	return point_mul(R, k, &G, p, ctx);
+}
+
+int point_test(const BIGNUM *p, BN_CTX *ctx)
+{
+	const char *_G[] = {
+		"3722755292130b08d2aab97fd34ec120ee265948d19c17abf9b7213baf82d65b",
+		"85aef3d078640c98597b6027b441a01ff1dd2c190f5e93c454806c11d8806141",
+		"a7cf28d519be3da65f3170153d278ff247efba98a71a08116215bba5c999a7c7",
+		"17509b092e845c1266ba0d262cbee6ed0736a96fa347c8bd856dc76b84ebeb96"};
+	const char *dbl_G[] = {
+		"2a74f8561b91993205eb512576ad56221ea5963f3da078240d55594fb051ea86",
+		"513f149ab53e94bb3a0367c61ff87670e025db30c57f84594e4ba4d7b3c656cf",
+		"8e3d9ec4e63d5b9f83081fb97b715430c8bfc6f1a1321a89627b9a4e8961c7bd",
+		"776de41db0511b8976d69c982dd4757d641487c68d13cbee7069396c20cd3459"};
+	const char *tri_G[] = {
+		"9e5437ea263653ea0617ca82c5ce5db4937dece2f762a6fbdae7fb3032f9b154",
+		"4dd9b503b00f0e8334e5cbdc9ff80deb4b207a1b1fda2382f3812bd5687937c0",
+		"3d491f4ffb2a4ab249e396fe8e58b6e8cb23ef935309e576bc5a9a3b4fd97090",
+		"b1174c2d2b36cee03e1a7081eb71f60c35fac603f2b550218ec935c1e00bdd5b"};
+	const char *neg_G[] = {
+		"3722755292130b08d2aab97fd34ec120ee265948d19c17abf9b7213baf82d65b",
+		"85aef3d078640c98597b6027b441a01ff1dd2c190f5e93c454806c11d8806141",
+		"e70d72ae8e5694b76d23b3ab8673752da02d8b27360e6ca8359df8219b79db6",
+		"9eef64f6d41f4adf6f499e29c8cfe0581abbe9db7733261e6001d3bc5e6559e7"};
+	const char *sub_3G_G[] = {
+		"2a74f8561b91993205eb512576ad56221ea5963f3da078240d55594fb051ea86",
+		"513f149ab53e94bb3a0367c61ff87670e025db30c57f84594e4ba4d7b3c656cf",
+		"8e3d9ec4e63d5b9f83081fb97b715430c8bfc6f1a1321a89627b9a4e8961c7bd",
+		"776de41db0511b8976d69c982dd4757d641487c68d13cbee7069396c20cd3459"};
+	const char *ten_G[] = {
+		"1e3188d71ed78f5541bc77b3bdc75df1c93d9811e26793bba71a3f30de6ee9be",
+		"b0a3030bffb5a431f593a2375865d04c8a83516c0af56c7c63fb17aacb96c44c",
+		"8d7e1a49cddc2eccd0b757967e3fb669f66397ee4ba232562ed5a72850606471",
+		"1d2a27fd716f0b2ab9c9a40191c5c7ac00c48f554b1e976dc4d25324f29a8531"};
+	const char *ks =
+		"0130E78459D78545CB54C587E02CF480CE0B66340F319F348A1D5B1F2DC5F4";
+	const char *Ppubs[] = {
+		"29DBA116152D1F786CE843ED24A3B573414D2177386A92DD8F14D65696EA5E32",
+		"9F64080B3084F733E48AFF4B41B565011CE0711C5E392CFB0AB1B6791B94C408",
+		"41E00A53DDA532DA1A7CE027B7A46F741006E85F5CDFF0730E75C05FB4E3216D",
+		"69850938ABEA0112B57329F447E3A0CBAD3E2FDB1A77F335E89E1408D0EF1C25"};
+
+	point_t G, P;
+	BIGNUM *k = BN_new();
+	int ok;
+
+	point_init(&G, ctx);
+	point_init(&P, ctx);
+
+	point_set_affine_coordinates_bignums(&G,
+		SM9_get0_generator2_x0(),
+		SM9_get0_generator2_x1(),
+		SM9_get0_generator2_y0(),
+		SM9_get0_generator2_y1());
+
+	ok = point_equ_hex(&G, _G, ctx);
+	printf("point test %d: %s\n", __LINE__, ok ? "ok" : "error");
+
+	ok = point_is_on_curve(&G, p, ctx);
+	printf("point test %d: %s\n", __LINE__, ok ? "ok" : "error");
+
+	point_dbl(&P, &G, p, ctx);
+	ok = point_equ_hex(&P, dbl_G, ctx);
+	printf("point test %d: %s\n", __LINE__, ok ? "ok" : "error");
+
+	point_add(&P, &P, &G, p, ctx);
+	ok = point_equ_hex(&P, tri_G, ctx);
+	printf("point test %d: %s\n", __LINE__, ok ? "ok" : "error");
+
+	point_sub(&P, &P, &G, p, ctx);
+	ok = point_equ_hex(&P, sub_3G_G, ctx);
+	printf("point test %d: %s\n", __LINE__, ok ? "ok" : "error");
+
+	point_neg(&P, &G, p, ctx);
+	ok = point_equ_hex(&P, neg_G, ctx);
+	printf("point test %d: %s\n", __LINE__, ok ? "ok" : "error");
+
+	BN_set_word(k, 10);
+	point_mul(&P, k, &G, p, ctx);
+	ok = point_equ_hex(&P, ten_G, ctx);
+	printf("point test %d: %s\n", __LINE__, ok ? "ok" : "error");
+
+	BN_hex2bn(&k, ks);
+	point_mul_generator(&P, k, p, ctx);
+	ok = point_equ_hex(&P, Ppubs, ctx);
+	printf("point test %d: %s\n", __LINE__, ok ? "ok" : "error");
+
+	fp12_t x, y;
+
+	fp12_init(x, ctx);
+	fp12_init(y, ctx);
+
+	point_get_ext_affine_coordinates(&G, x, y, p, ctx);
+	point_set_ext_affine_coordinates(&P, x, y, p, ctx);
+
+	ok = point_equ(&P, &G);
+	printf("point test %d: %s\n", __LINE__, ok ? "ok" : "error");
+
+	//fp12_cleanup(x);
+	//fp12_cleanup(y);
+	return 1;
+}
+
+int eval_tangent(fp12_t r, const point_t *T, const BIGNUM *xP, const BIGNUM *yP,
+	const BIGNUM *p, BN_CTX *ctx)
+{
+	int ret;
+	fp12_t x, y, lambda, t;
+	fp12_t xT, yT;
+
+	ret = 1;
+	ret &= fp12_init(x, ctx);
+	ret &= fp12_init(y, ctx);
+	ret &= fp12_init(lambda, ctx);
+	ret &= fp12_init(t, ctx);
+	ret &= fp12_init(xT, ctx);
+	ret &= fp12_init(yT, ctx);
+	if (!ret) {
+		goto end;
+	}
+
+	point_get_ext_affine_coordinates(T, xT, yT, p, ctx);
+
+	ret = 0;
+	if (!fp12_set_bn(x, xP)
+		|| !fp12_set_bn(y, yP)
+		/* lambda = (3 * xT^2)/(2 * yT) */
+		|| !fp12_sqr(lambda, xT, p, ctx)
+		|| !fp12_tri(lambda, lambda, p, ctx)
+		|| !fp12_dbl(t, yT, p, ctx)
+		|| !fp12_inv(t, t, p, ctx)
+		|| !fp12_mul(lambda, lambda, t, p, ctx)
+
+		/* r = lambda * (x - xT) - y + yT */
+		|| !fp12_sub(r, x, xT, p, ctx)
+		|| !fp12_mul(r, lambda, r, p, ctx)
+		|| !fp12_sub(r, r, y, p, ctx)
+		|| !fp12_add(r, r, yT, p, ctx)) {
+		goto end;
+	}
+	ret = 1;
+
+end:
+	fp12_cleanup(x);
+	fp12_cleanup(y);
+	fp12_cleanup(lambda);
+	fp12_cleanup(t);
+	return ret;
+}
+
+int eval_line(fp12_t r,  const point_t *T, const point_t *Q,
+	const BIGNUM *xP, const BIGNUM *yP,
+	const BIGNUM *p, BN_CTX *ctx)
+{
+	int ret;
+	fp12_t x, y, lambda, t;
+	fp12_t xT, yT, xQ, yQ;
+
+	ret = 1;
+	ret &= fp12_init(x, ctx);
+	ret &= fp12_init(y, ctx);
+	ret &= fp12_init(lambda, ctx);
+	ret &= fp12_init(t, ctx);
+	ret &= fp12_init(xT, ctx);
+	ret &= fp12_init(yT, ctx);
+	ret &= fp12_init(xQ, ctx);
+	ret &= fp12_init(yQ, ctx);
+	if (!ret) {
+		goto end;
+	}
+
+	point_get_ext_affine_coordinates(T, xT, yT, p, ctx);
+	point_get_ext_affine_coordinates(Q, xQ, yQ, p, ctx);
+
+	ret = 0;
+	if (!fp12_set_bn(x, xP)
+		|| !fp12_set_bn(y, yP)
+		/* lambda = (yT - yQ)/(xT - xQ) */
+		|| !fp12_sub(lambda, yT, yQ, p, ctx)
+		|| !fp12_sub(t, xT, xQ, p, ctx)
+		|| !fp12_inv(t, t, p, ctx)
+		|| !fp12_mul(lambda, lambda, t, p, ctx)
+
+		/* r = lambda * (x - xQ) - y + yQ */
+		|| !fp12_sub(r, x, xQ, p, ctx)
+		|| !fp12_mul(r, lambda, r, p, ctx)
+		|| !fp12_sub(r, r, y, p, ctx)
+		|| !fp12_add(r, r, yQ, p, ctx)) {
+		goto end;
+	}
+	ret = 1;
+
+end:
+	fp12_cleanup(x);
+	fp12_cleanup(y);
+	fp12_cleanup(lambda);
+	fp12_cleanup(t);
+	return ret;
+}
+
+int frobenius(point_t *R, const point_t *P, const BIGNUM *p, BN_CTX *ctx)
+{
+	fp12_t x, y;
+
+	fp12_init(x, ctx);
+	fp12_init(y, ctx);
+
+
+
+	point_get_ext_affine_coordinates(P, x, y, p, ctx);
+
+	fp12_pow(x, x, p, p, ctx);
+	fp12_pow(y, y, p, p, ctx);
+
+	point_set_ext_affine_coordinates(R, x, y, p, ctx);
+
+	fp12_cleanup(x);
+	fp12_cleanup(y);
+	return 1;
+}
+
+int frobenius_twice(point_t *R, const point_t *P, const BIGNUM *p, BN_CTX *ctx)
+{
+	frobenius(R, P, p, ctx);
+	frobenius(R, R, p, ctx);
+	return 1;
+}
+
+
+int final_expo(fp12_t r, const fp12_t a, const BIGNUM *k, const BIGNUM *p, BN_CTX *ctx)
+{
+	int i, n;
+	fp12_t t;
+
+	fp12_init(t, ctx);
+
+	if (!fp12_copy(t, a)) {
+		return 0;
+	}
+
+	n = BN_num_bits(k);
+	for (i = n - 2; i >= 0; i--) {
+		if (!fp12_sqr(t, t, p, ctx)) {
+			return 0;
+		}
+		if (BN_is_bit_set(k, i)) {
+			if (!fp12_mul(t, t, a, p, ctx)) {
+				return 0;
+			}
+		}
+	}
+	fp12_copy(r, t);
+
+	fp12_cleanup(t);
+	return 1;
+}
+
+
+int fast_final_expo(fp12_t r, const fp12_t a, const BIGNUM *k, const BIGNUM *p, BN_CTX *ctx)
+{
+	int i, n;
+	fp12_t t;
+	fp12_t t0;
+
+	fp12_init(t, ctx);
+	fp12_init(t0, ctx);
+
+	if (!fp12_copy(t, a)) {
+		return 0;
+	}
+	if (!fp12_copy(t0, a)) {
+		return 0;
+	}
+
+	if (!fp12_inv(t0, t, p, ctx)) {
+		return 0;
+	}
+	if (!fp12_fast_expo_p1(t, t, p, ctx)) {
+		return 0;
+	}
+	if (!fp12_mul(t, t0, t, p, ctx)) {
+		return 0;
+	}
+
+	if (!fp12_copy(t0, t)) {
+		return 0;
+	}
+
+	if(!fp12_fast_expo_p2(t, t, p, ctx)){
+		return 0;
+	}
+
+	if (!fp12_mul(t, t0, t, p, ctx)) {
+		return 0;
+	}
+
+	if (!fp12_copy(t0, t)) {
+		return 0;
+	}
+
+	n = BN_num_bits(k);
+	for (i = n - 2; i >= 0; i--) {
+		if (!fp12_sqr(t, t, p, ctx)) {
+			return 0;
+		}
+		if (BN_is_bit_set(k, i)) {
+			if (!fp12_mul(t, t, t0, p, ctx)) {
+				return 0;
+			}
+		}
+	}
+	fp12_copy(r, t);
+
+	fp12_cleanup(t);
+	fp12_cleanup(t0);
+	return 1;
+}
+
+int rate(fp12_t f, const point_t *Q, const BIGNUM *xP, const BIGNUM *yP,
+	const BIGNUM *a, const BIGNUM *k, const BIGNUM *p, BN_CTX *ctx)
+{
+	int ret = 0;
+	int i, n;
+	point_t T, Q1, Q2;
+	fp12_t g;
+
+	memset(&T, 0, sizeof(T));
+	memset(&Q1, 0, sizeof(Q1));
+	memset(&Q2, 0, sizeof(Q2));
+
+	point_init(&T, ctx);
+	point_init(&Q1, ctx);
+	point_init(&Q2, ctx);
+	fp12_init(g, ctx);
+
+	fp12_set_one(f);
+	point_copy(&T, Q);
+
+	n = BN_num_bits(a);
+	for (i = n - 2; i >= 0; i--) {
+		//printf("miller loop %d\n", i);
+
+		/* f = f^2 * g_{T,T}(P) */
+		eval_tangent(g, &T, xP, yP, p, ctx);
+
+		//printf("g\n");
+		//fp12_print(g);
+
+		fp12_sqr(f, f, p, ctx);
+		fp12_mul(f, f, g, p, ctx);
+
+		//printf("f\n");
+		//fp12_print(f);
+
+		/* T = 2 * T */
+		point_dbl(&T, &T, p, ctx);
+
+		if (BN_is_bit_set(a, i)) {
+			/* f = f * g_{T,Q}(P) */
+			eval_line(g, &T, Q, xP, yP, p, ctx);
+
+			//printf("g\n");
+			//fp12_print(g);
+
+
+			fp12_mul(f, f, g, p, ctx);
+
+			//printf("f\n");
+			//fp12_print(f);
+
+			/* T = T + Q */
+			point_add(&T, &T, Q, p, ctx);
+		}
+
+	}
+
+	/* Q1 = (x^p, y^p) */
+	frobenius(&Q1, Q, p, ctx);
+
+	/* Q2 = (x^(p^2), y^(p^2)) */
+	frobenius_twice(&Q2, Q, p, ctx);
+
+	/* f = f * g_{T, Q1}(P) */
+	eval_line(g, &T, &Q1, xP, yP, p, ctx);
+	fp12_mul(f, f, g, p, ctx);
+
+	/* T = T + Q1 */
+	point_add(&T, &T, &Q1, p, ctx);
+
+	/* f = f * g_{T, -Q2}(P) */
+	point_neg(&Q2, &Q2, p, ctx);
+	eval_line(g, &T, &Q2, xP, yP, p, ctx);
+	fp12_mul(f, f, g, p, ctx);
+
+	/* T = T - Q2 */
+	point_add(&T, &T, &Q2, p, ctx);
+
+#ifdef NOSM9_FAST
+	/* f = f^((p^12 - 1)/n) */
+	final_expo(f, f, k, p, ctx);
+#else
+	/* f = ((f ^ (p^6-1)) ^ (p^2+1)) ^ [(p^4-p^2+1)/n] */
+	fast_final_expo(f, f, k, p, ctx);
+#endif
+
+	point_cleanup(&T);
+	point_cleanup(&Q1);
+	point_cleanup(&Q2);
+	fp12_cleanup(g);
+	return ret;
+}
+
+int params_test(void)
+{
+	const BIGNUM *p = SM9_get0_prime();
+	const BIGNUM *a = SM9_get0_loop_count();
+	const BIGNUM *k = SM9_get0_final_exponent();
+
+	printf("p = %s\n", BN_bn2dec(p));
+	printf("a = %s\n", BN_bn2dec(a));
+	printf("k = %s\n", BN_bn2dec(k));
+
+	return 1;
+}
+
+int rate_pairing(fp12_t r, const point_t *Q, const EC_POINT *P, BN_CTX *ctx)
+{
+	int ret = 1;
+	const EC_GROUP *group;
+	const BIGNUM *p;
+	const BIGNUM *a;
+	const BIGNUM *k;
+	BIGNUM *xP = NULL;
+	BIGNUM *yP = NULL;
+
+	group = EC_GROUP_new_by_curve_name(NID_sm9bn256v1);
+	p = SM9_get0_prime();
+	a = SM9_get0_loop_count();
+#ifdef NOSM9_FAST
+	k = SM9_get0_final_exponent();
+#else
+	k = SM9_get0_fast_final_exponent_p3();
+#endif
+	xP = BN_CTX_get(ctx);
+	yP = BN_CTX_get(ctx);
+
+	if (!P) {
+		EC_POINT_get_affine_coordinates_GFp(group,
+			EC_GROUP_get0_generator(group), xP, yP, ctx);
+	} else {
+		EC_POINT_get_affine_coordinates_GFp(group, P, xP, yP, ctx);
+	}
+
+	if (!Q) {
+		point_t P2;
+		point_init(&P2, ctx);
+		point_set_affine_coordinates_bignums(&P2,
+			SM9_get0_generator2_x0(),
+			SM9_get0_generator2_x1(),
+			SM9_get0_generator2_y0(),
+			SM9_get0_generator2_y1());
+
+		rate(r, &P2, xP, yP, a, k, p, ctx);
+
+		point_cleanup(&P2);
+	} else {
+		rate(r, Q, xP, yP, a, k, p, ctx);
+	}
+
+	BN_free(xP);
+	BN_free(yP);
+	EC_GROUP_clear_free(group);
+	return ret;
+}
+
+int rate_test(void)
+{
+	const char *Ppubs_str[] = {
+		"29DBA116152D1F786CE843ED24A3B573414D2177386A92DD8F14D65696EA5E32",
+		"9F64080B3084F733E48AFF4B41B565011CE0711C5E392CFB0AB1B6791B94C408",
+		"41E00A53DDA532DA1A7CE027B7A46F741006E85F5CDFF0730E75C05FB4E3216D",
+		"69850938ABEA0112B57329F447E3A0CBAD3E2FDB1A77F335E89E1408D0EF1C25"};
+	const char *g_str[] = {
+		"AAB9F06A4EEBA4323A7833DB202E4E35639D93FA3305AF73F0F071D7D284FCFB",
+		"84B87422330D7936EABA1109FA5A7A7181EE16F2438B0AEB2F38FD5F7554E57A",
+		"4C744E69C4A2E1C8ED72F796D151A17CE2325B943260FC460B9F73CB57C9014B",
+		"B3129A75D31D17194675A1BC56947920898FBF390A5BF5D931CE6CBB3340F66D",
+		"93634F44FA13AF76169F3CC8FBEA880ADAFF8475D5FD28A75DEB83C44362B439",
+		"1604A3FCFA9783E667CE9FCB1062C2A5C6685C316DDA62DE0548BAA6BA30038B",
+		"5A1AE172102EFD95DF7338DBC577C66D8D6C15E0A0158C7507228EFB078F42A6",
+		"67E0E0C2EED7A6993DCE28FE9AA2EF56834307860839677F96685F2B44D0911F",
+		"A01F2C8BEE81769609462C69C96AA923FD863E209D3CE26DD889B55E2E3873DB",
+		"38BFFE40A22D529A0C66124B2C308DAC9229912656F62B4FACFCED408E02380F",
+		"28B3404A61908F5D6198815C99AF1990C8AF38655930058C28C21BB539CE0000",
+		"4E378FB5561CD0668F906B731AC58FEE25738EDF09CADC7A29C0ABC0177AEA6D"};
+
+	BN_CTX *ctx = NULL;
+	EC_GROUP *group = NULL;
+	const EC_POINT *P1;
+	point_t Ppubs;
+	fp12_t g;
+	int ok;
+
+	ctx = BN_CTX_new();
+	BN_CTX_start(ctx);
+	group = EC_GROUP_new_by_curve_name(NID_sm9bn256v1);
+	P1 = EC_GROUP_get0_generator(group);
+
+	point_init(&Ppubs, ctx);
+	point_set_affine_coordinates_hex(&Ppubs, Ppubs_str);
+
+	fp12_init(g, ctx);
+	rate_pairing(g, &Ppubs, P1, ctx);
+
+	ok = fp12_equ_hex(g, g_str, ctx);
+
+	fp12_cleanup(g);
+	point_cleanup(&Ppubs);
+	EC_GROUP_free(group);
+	BN_CTX_free(ctx);
+
+	return ok;
+}
+
+/* for SM9 sign, the (xP, yP) is the fixed generator of E(Fp)
+ */
+int SM9_rate_pairing(BIGNUM *r[12], const BIGNUM *xQ[2], const BIGNUM *yQ[2],
+	const BIGNUM *xP, const BIGNUM *yP, BN_CTX *ctx)
+{
+	return 0;
+}
diff --git a/crypto/sm9/sm9_setup.c b/crypto/sm9/sm9_setup.c
new file mode 100755
index 000000000..199a4dc69
--- /dev/null
+++ b/crypto/sm9/sm9_setup.c
@@ -0,0 +1,240 @@
+/* ====================================================================
+ * Copyright (c) 2016 The GmSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the GmSSL Project.
+ *    (http://gmssl.org/)"
+ *
+ * 4. The name "GmSSL Project" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    guanzhi1980@gmail.com.
+ *
+ * 5. Products derived from this software may not be called "GmSSL"
+ *    nor may "GmSSL" appear in their names without prior written
+ *    permission of the GmSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the GmSSL Project
+ *    (http://gmssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE GmSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE GmSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+
+#include 
+#include 
+#include 
+#include "sm9_lcl.h"
+
+SM9MasterSecret *SM9_generate_master_secret(int pairing, int scheme, int hash1)
+{
+	SM9MasterSecret *ret = NULL;
+	SM9MasterSecret *msk = NULL;
+	BN_CTX *ctx = NULL;
+	const BIGNUM *n = SM9_get0_order();
+	const BIGNUM *p = SM9_get0_prime();
+	int point_form = POINT_CONVERSION_UNCOMPRESSED;
+	unsigned char buf[129];
+	size_t len = sizeof(buf);
+
+	if (!(msk = SM9MasterSecret_new())
+		|| !(ctx = BN_CTX_new())) {
+		SM9err(SM9_F_SM9_GENERATE_MASTER_SECRET, ERR_R_MALLOC_FAILURE);
+		goto end;
+	}
+	BN_CTX_start(ctx);
+
+	/* set pairing type */
+	switch (pairing) {
+	case NID_sm9bn256v1:
+		if (!(msk->pairing = OBJ_nid2obj(pairing))) {
+			SM9err(SM9_F_SM9_GENERATE_MASTER_SECRET, ERR_R_OBJ_LIB);
+			goto end;
+		}
+		break;
+	default:
+		SM9err(SM9_F_SM9_GENERATE_MASTER_SECRET, SM9_R_INVALID_PAIRING_TYPE);
+		goto end;
+	}
+
+	/* set helper functions */
+	switch (scheme) {
+	case NID_sm9sign:
+	case NID_sm9encrypt:
+	case NID_sm9keyagreement:
+		if (!(msk->scheme = OBJ_nid2obj(scheme))) {
+			SM9err(SM9_F_SM9_GENERATE_MASTER_SECRET, ERR_R_OBJ_LIB);
+			goto end;
+		}
+		break;
+	default:
+		SM9err(SM9_F_SM9_GENERATE_MASTER_SECRET, SM9_R_INVALID_SCHEME);
+		goto end;
+	}
+
+	/* set hash1 */
+	switch (hash1) {
+	case NID_sm9hash1_with_sm3:
+	case NID_sm9hash1_with_sha256:
+		if (!(msk->hash1 = OBJ_nid2obj(hash1))) {
+			SM9err(SM9_F_SM9_GENERATE_MASTER_SECRET, ERR_R_OBJ_LIB);
+			goto end;
+		}
+		break;
+	default:
+		SM9err(SM9_F_SM9_GENERATE_MASTER_SECRET, SM9_R_INVALID_HASH1);
+		goto end;
+	}
+
+	/* generate master secret k = rand(1, n - 1) */
+	do {
+
+		if (!(msk->masterSecret = BN_new())) {
+			SM9err(SM9_F_SM9_GENERATE_MASTER_SECRET, ERR_R_MALLOC_FAILURE);
+			goto end;
+		}
+		if (!BN_rand_range(msk->masterSecret, n)) {
+			SM9err(SM9_F_SM9_GENERATE_MASTER_SECRET, ERR_R_BN_LIB);
+			goto end;
+		}
+	} while (BN_is_zero(msk->masterSecret));
+
+	/* generate master public point */
+	if (scheme == NID_sm9sign) {
+
+		/* Ppubs = k * P2 in E'(F_p^2) */
+		point_t Ppubs;
+
+		if (!point_init(&Ppubs, ctx)
+			|| !point_mul_generator(&Ppubs, msk->masterSecret, p, ctx)
+			|| !point_to_octets(&Ppubs, buf, ctx)) {
+			SM9err(SM9_F_SM9_GENERATE_MASTER_SECRET, SM9_R_TWIST_CURVE_ERROR);
+			point_cleanup(&Ppubs);
+			goto end;
+		}
+
+		len = 129;
+		point_cleanup(&Ppubs);
+
+	} else if (scheme == NID_sm9keyagreement
+		|| scheme == NID_sm9encrypt) {
+
+		/* Ppube = k * P1 in E(F_p) */
+		EC_GROUP *group = NULL;
+		EC_POINT *Ppube = NULL;
+
+		if (!(group = EC_GROUP_new_by_curve_name(NID_sm9bn256v1))
+			|| !(Ppube = EC_POINT_new(group))
+			|| !EC_POINT_mul(group, Ppube, msk->masterSecret, NULL, NULL, ctx)
+			|| !(len = EC_POINT_point2oct(group, Ppube, point_form, buf, len, ctx))) {
+			SM9err(SM9_F_SM9_GENERATE_MASTER_SECRET, SM9_R_EC_LIB);
+			EC_GROUP_free(group);
+			EC_POINT_free(Ppube);
+			goto end;
+		}
+
+		EC_GROUP_free(group);
+		EC_POINT_free(Ppube);
+
+	} else {
+		SM9err(SM9_F_SM9_GENERATE_MASTER_SECRET, SM9_R_INVALID_SCHEME);
+		goto end;
+	}
+
+	if (!(msk->pointPpub = ASN1_OCTET_STRING_new())) {
+		SM9err(SM9_F_SM9_GENERATE_MASTER_SECRET, ERR_R_MALLOC_FAILURE);
+		goto end;
+	}
+	if (!ASN1_OCTET_STRING_set(msk->pointPpub, buf, (int)len)) {
+		ERR_print_errors_fp(stderr);
+		goto end;
+	}
+
+	ret = msk;
+	msk = NULL;
+
+end:
+	SM9MasterSecret_free(msk);
+	if (ctx) {
+		BN_CTX_end(ctx);
+	}
+	BN_CTX_free(ctx);
+	OPENSSL_cleanse(buf, sizeof(buf));
+	return ret;
+}
+
+SM9PublicParameters *SM9_extract_public_parameters(SM9MasterSecret *msk)
+{
+	SM9PublicParameters *ret = NULL;
+	SM9PublicParameters *mpk = NULL;
+
+	if (!(mpk = SM9PublicParameters_new())) {
+		SM9err(SM9_F_SM9_EXTRACT_PUBLIC_PARAMETERS, ERR_R_MALLOC_FAILURE);
+		return NULL;
+	}
+
+	if (!(mpk->pairing = OBJ_dup(msk->pairing))
+		|| !(mpk->scheme = OBJ_dup(msk->scheme))
+		|| !(mpk->hash1 = OBJ_dup(msk->hash1))
+		|| !(mpk->pointPpub = ASN1_OCTET_STRING_dup(msk->pointPpub))) {
+		SM9err(SM9_F_SM9_EXTRACT_PUBLIC_PARAMETERS, ERR_R_MALLOC_FAILURE);
+		goto end;
+	}
+
+	ret = mpk;
+	mpk = NULL;
+
+end:
+	SM9PublicParameters_free(mpk);
+	return ret;
+}
+
+int SM9_setup(int pairing, int scheme, int hash1,
+	SM9PublicParameters **pmpk, SM9MasterSecret **pmsk)
+{
+	int ret = 0;
+	SM9MasterSecret *msk = NULL;
+	SM9PublicParameters *mpk = NULL;
+
+	if (!(msk = SM9_generate_master_secret(pairing, scheme, hash1))
+		|| !(mpk = SM9_extract_public_parameters(msk))) {
+		goto end;
+	}
+
+	*pmsk = msk;
+	*pmpk = mpk;
+	msk = NULL;
+	mpk = NULL;
+	ret = 1;
+
+end:
+	SM9MasterSecret_free(msk);
+	SM9PublicParameters_free(mpk);
+	return ret;
+}
diff --git a/crypto/sm9/sm9_sign.c b/crypto/sm9/sm9_sign.c
new file mode 100755
index 000000000..c4666d7a7
--- /dev/null
+++ b/crypto/sm9/sm9_sign.c
@@ -0,0 +1,450 @@
+/* ====================================================================
+ * Copyright (c) 2016 - 2018 The GmSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the GmSSL Project.
+ *    (http://gmssl.org/)"
+ *
+ * 4. The name "GmSSL Project" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    guanzhi1980@gmail.com.
+ *
+ * 5. Products derived from this software may not be called "GmSSL"
+ *    nor may "GmSSL" appear in their names without prior written
+ *    permission of the GmSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the GmSSL Project
+ *    (http://gmssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE GmSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE GmSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include "sm9_lcl.h"
+
+
+SM9Signature *SM9_do_sign(const unsigned char *dgst, int dgstlen, SM9_KEY *sm9)
+{
+	return NULL;
+}
+
+int SM9_do_verify(const unsigned char *dgst, int dgstlen,
+	const SM9Signature *sig, SM9_KEY *sm9)
+{
+	return -1;
+}
+
+int SM9_SignInit(EVP_MD_CTX *ctx, const EVP_MD *md, ENGINE *eng)
+{
+	unsigned char prefix[1] = {0x02};
+
+	if (!EVP_DigestInit_ex(ctx, md, eng)) {
+		SM9err(SM9_F_SM9_SIGNINIT, ERR_R_EVP_LIB);
+		return 0;
+	}
+	if (!EVP_DigestUpdate(ctx, prefix, sizeof(prefix))) {
+		SM9err(SM9_F_SM9_SIGNINIT, ERR_R_EVP_LIB);
+		return 0;
+	}
+
+	return 1;
+}
+
+SM9Signature *SM9_SignFinal(EVP_MD_CTX *ctx1, SM9PrivateKey *sk)
+{
+	SM9Signature *ret = NULL;
+	SM9Signature *sig = NULL;
+	const BIGNUM *p = SM9_get0_prime();
+	const BIGNUM *n = SM9_get0_order();
+	int point_form = POINT_CONVERSION_COMPRESSED;
+	/* buf for w and prefix zeros of ct1/2 */
+	unsigned char buf[384] = {0};
+	unsigned int len;
+	const unsigned char ct1[4] = {0x00, 0x00, 0x00, 0x01};
+	const unsigned char ct2[4] = {0x00, 0x00, 0x00, 0x02};
+	EVP_MD_CTX *ctx2 = NULL;
+	EC_GROUP *group = NULL;
+	EC_POINT *S = NULL;
+	BN_CTX *bn_ctx = NULL;
+	BIGNUM *r = NULL;
+	point_t Ppubs;
+	fp12_t w;
+
+	if (!(sig = SM9Signature_new())
+		|| !(ctx2 = EVP_MD_CTX_new())
+		|| !(group = EC_GROUP_new_by_curve_name(NID_sm9bn256v1))
+		|| !(S = EC_POINT_new(group))
+		|| !(bn_ctx = BN_CTX_new())) {
+		SM9err(SM9_F_SM9_SIGNFINAL, ERR_R_MALLOC_FAILURE);
+		goto end;
+	}
+	BN_CTX_start(bn_ctx);
+	if (!(r = BN_CTX_get(bn_ctx))
+		|| !fp12_init(w, bn_ctx)
+		|| !point_init(&Ppubs, bn_ctx)) {
+		SM9err(SM9_F_SM9_SIGNFINAL, ERR_R_MALLOC_FAILURE);
+		goto end;
+	}
+
+
+	/* get Ppubs */
+	if (ASN1_STRING_length(sk->pointPpub) != 129
+		|| !point_from_octets(&Ppubs, ASN1_STRING_get0_data(sk->pointPpub), p, bn_ctx)) {
+		SM9err(SM9_F_SM9_SIGNFINAL, SM9_R_INVALID_POINTPPUB);
+		goto end;
+	}
+	/* g = e(P1, Ppubs) */
+	if (!rate_pairing(w, &Ppubs, EC_GROUP_get0_generator(group), bn_ctx)) {
+		SM9err(SM9_F_SM9_SIGNFINAL, SM9_R_PAIRING_ERROR);
+		goto end;
+	}
+
+	do {
+		/* r = rand(1, n - 1) */
+		do {
+			if (!BN_rand_range(r, n)) {
+				SM9err(SM9_F_SM9_SIGNFINAL, ERR_R_BN_LIB);
+				goto end;
+			}
+		} while (BN_is_zero(r));
+
+		/* w = g^r */
+		if (!fp12_pow(w, w, r, p, bn_ctx)
+			|| !fp12_to_bin(w, buf)) {
+			SM9err(SM9_F_SM9_SIGNFINAL, SM9_R_EXTENSION_FIELD_ERROR);
+			goto end;
+		}
+
+		if (!EVP_DigestUpdate(ctx1, buf, sizeof(buf))
+			|| !EVP_MD_CTX_copy(ctx2, ctx1)
+			/* Ha1 = Hv(0x02||M||w||0x00000001) */
+			|| !EVP_DigestUpdate(ctx1, ct1, sizeof(ct1))
+		 	/* Ha2 = Hv(0x02||M||w||0x00000002) */
+			|| !EVP_DigestUpdate(ctx2, ct2, sizeof(ct2))
+			|| !EVP_DigestFinal_ex(ctx1, buf, &len)
+			|| !EVP_DigestFinal_ex(ctx2, buf + len, &len)) {
+			SM9err(SM9_F_SM9_SIGNFINAL, SM9_R_DIGEST_FAILURE);
+			goto end;
+		}
+
+		/* Ha = Ha1||Ha2[0..7] */
+		if (!BN_bin2bn(buf, 40, sig->h)
+			/* h = (Ha mod (n - 1)) + 1 */
+			|| !BN_mod(sig->h, sig->h, SM9_get0_order_minus_one(), bn_ctx)
+			|| !BN_add_word(sig->h, 1)
+			/* l = r - h (mod n) */
+			|| !BN_mod_sub(r, r, sig->h, n, bn_ctx)) {
+			SM9err(SM9_F_SM9_SIGNFINAL, ERR_R_BN_LIB);
+			goto end;
+		}
+	} while (BN_is_zero(r));
+
+	/* get sk */
+	if (!EC_POINT_oct2point(group, S, ASN1_STRING_get0_data(sk->privatePoint),
+		ASN1_STRING_length(sk->privatePoint), bn_ctx)) {
+		SM9err(SM9_F_SM9_SIGNFINAL, SM9_R_INVALID_PRIVATE_POINT);
+		goto end;
+	}
+	/* S = l * sk */
+	len = sizeof(buf);
+	if (!EC_POINT_mul(group, S, NULL, S, r, bn_ctx)
+		|| !(len = EC_POINT_point2oct(group, S, point_form, buf, len, bn_ctx))
+		|| !ASN1_OCTET_STRING_set(sig->pointS, buf, len)) {
+		SM9err(SM9_F_SM9_SIGNFINAL, ERR_R_EC_LIB);
+		goto end;
+	}
+
+	ret = sig;
+	sig = NULL;
+
+end:
+	SM9Signature_free(sig);
+	EVP_MD_CTX_free(ctx2);
+	EC_GROUP_free(group);
+	EC_POINT_free(S);
+	BN_free(r);
+	point_cleanup(&Ppubs);
+	fp12_cleanup(w);
+	BN_CTX_end(bn_ctx);
+	BN_CTX_free(bn_ctx);
+	return ret;
+}
+
+int SM9_VerifyInit(EVP_MD_CTX *ctx, const EVP_MD *md, ENGINE *eng)
+{
+	unsigned char prefix[1] = {0x02};
+
+	if (!EVP_DigestInit_ex(ctx, md, eng)) {
+		SM9err(SM9_F_SM9_VERIFYINIT, ERR_R_EVP_LIB);
+		return 0;
+	}
+	if (!EVP_DigestUpdate(ctx, prefix, sizeof(prefix))) {
+		SM9err(SM9_F_SM9_VERIFYINIT, ERR_R_EVP_LIB);
+		return 0;
+	}
+
+	return 1;
+}
+
+static const EVP_MD *sm9hash1_to_md(const ASN1_OBJECT *hash1obj)
+{
+	switch (OBJ_obj2nid(hash1obj)) {
+	case NID_sm9hash1_with_sm3:
+		return EVP_sm3();
+	case NID_sm9hash1_with_sha256:
+		return EVP_sha256();
+	}
+	return NULL;
+}
+
+int SM9_VerifyFinal(EVP_MD_CTX *ctx1, const SM9Signature *sig, SM9PublicKey *pk)
+{
+	int ret = -1;
+	const BIGNUM *p = SM9_get0_prime();
+	const BIGNUM *n = SM9_get0_order();
+	const EVP_MD *md;
+	unsigned char buf[384] = {0};
+	unsigned int len;
+	const unsigned char ct1[4] = {0x00, 0x00, 0x00, 0x01};
+	const unsigned char ct2[4] = {0x00, 0x00, 0x00, 0x02};
+	EVP_MD_CTX *ctx2 = NULL;
+	EC_GROUP *group = NULL;
+	EC_POINT *S = NULL;
+	BN_CTX *bn_ctx = NULL;
+	BIGNUM *h = NULL;
+	point_t Ppubs;
+	point_t P;
+	fp12_t w;
+	fp12_t u;
+
+	if (!(ctx2 = EVP_MD_CTX_new())
+		|| !(group = EC_GROUP_new_by_curve_name(NID_sm9bn256v1))
+		|| !(S = EC_POINT_new(group))
+		|| !(bn_ctx = BN_CTX_new())) {
+		SM9err(SM9_F_SM9_VERIFYFINAL, ERR_R_MALLOC_FAILURE);
+		goto end;
+	}
+	BN_CTX_start(bn_ctx);
+	if (!(h = BN_CTX_get(bn_ctx))
+		|| !point_init(&Ppubs, bn_ctx)
+		|| !point_init(&P, bn_ctx)
+		|| !fp12_init(w, bn_ctx)
+		|| !fp12_init(u, bn_ctx)) {
+		SM9err(SM9_F_SM9_VERIFYFINAL, ERR_R_MALLOC_FAILURE);
+		goto end;
+	}
+
+	/* check signature (h, S) */
+	if (BN_is_zero(sig->h) || BN_cmp(sig->h, SM9_get0_order()) >= 0) {
+		SM9err(SM9_F_SM9_VERIFYFINAL, SM9_R_INVALID_SIGNATURE);
+		goto end;
+	}
+	if (!EC_POINT_oct2point(group, S, ASN1_STRING_get0_data(sig->pointS),
+		ASN1_STRING_length(sig->pointS), bn_ctx)) {
+		SM9err(SM9_F_SM9_VERIFYFINAL, SM9_R_INVALID_SIGNATURE);
+		goto end;
+	}
+
+	/* g = e(P1, Ppubs) */
+	if (ASN1_STRING_length(pk->pointPpub) != 129
+		|| !point_from_octets(&Ppubs, ASN1_STRING_get0_data(pk->pointPpub), p, bn_ctx)) {
+		SM9err(SM9_F_SM9_VERIFYFINAL, SM9_R_INVALID_POINTPPUB);
+		goto end;
+	}
+	if (!rate_pairing(w, &Ppubs, EC_GROUP_get0_generator(group), bn_ctx)) {
+		SM9err(SM9_F_SM9_VERIFYFINAL, SM9_R_PAIRING_ERROR);
+		goto end;
+	}
+
+	/* t = g^(sig->h) */
+	if (!fp12_pow(w, w, sig->h, p, bn_ctx)) {
+		SM9err(SM9_F_SM9_VERIFYFINAL, SM9_R_EXTENSION_FIELD_ERROR);
+		goto end;
+	}
+
+	/* h1 = H1(ID||hid, N) */
+	if (!(md = sm9hash1_to_md(pk->hash1))) {
+		SM9err(SM9_F_SM9_VERIFYFINAL, SM9_R_INVALID_HASH1);
+		goto end;
+	}
+	if (!SM9_hash1(md, &h, (const char *)ASN1_STRING_get0_data(pk->identity),
+		ASN1_STRING_length(pk->identity), SM9_HID_SIGN, n, bn_ctx)) {
+		SM9err(SM9_F_SM9_VERIFYFINAL, ERR_R_SM9_LIB);
+		goto end;
+	}
+
+	/* P = h1 * P2 + Ppubs */
+	if (!point_mul_generator(&P, h, p, bn_ctx)
+		|| !point_add(&P, &P, &Ppubs, p, bn_ctx)
+		/* u = e(sig->S, P) */
+		|| !rate_pairing(u, &P, S, bn_ctx)
+		/* w = u * t */
+		|| !fp12_mul(w, u, w, p, bn_ctx)
+		|| !fp12_to_bin(w, buf)) {
+		SM9err(SM9_F_SM9_VERIFYFINAL, SM9_R_EXTENSION_FIELD_ERROR);
+		goto end;
+	}
+
+	/* h2 = H2(M||w) mod n */
+	if (!EVP_DigestUpdate(ctx1, buf, sizeof(buf))
+		|| !EVP_MD_CTX_copy(ctx2, ctx1)
+		/* Ha1 = Hv(0x02||M||w||0x00000001) */
+		|| !EVP_DigestUpdate(ctx1, ct1, sizeof(ct1))
+	 	/* Ha2 = Hv(0x02||M||w||0x00000002) */
+		|| !EVP_DigestUpdate(ctx2, ct2, sizeof(ct2))
+		|| !EVP_DigestFinal_ex(ctx1, buf, &len)
+		|| !EVP_DigestFinal_ex(ctx2, buf + len, &len)) {
+		SM9err(SM9_F_SM9_VERIFYFINAL, SM9_R_DIGEST_FAILURE);
+		goto end;
+	}
+	/* Ha = Ha1||Ha2[0..7] */
+	if (!BN_bin2bn(buf, 40, h)
+		/* h2 = (Ha mod (n - 1)) + 1 */
+		|| !BN_mod(h, h, SM9_get0_order_minus_one(), bn_ctx)
+		|| !BN_add_word(h, 1)) {
+		SM9err(SM9_F_SM9_VERIFYFINAL, ERR_R_BN_LIB);
+		goto end;
+	}
+
+	/* check if h2 == sig->h */
+	if (BN_cmp(h, sig->h) != 0) {
+		SM9err(SM9_F_SM9_VERIFYFINAL, SM9_R_VERIFY_FAILURE);
+		ret = 0;
+		goto end;
+	}
+
+	ret = 1;
+
+end:
+	EVP_MD_CTX_free(ctx2);
+	EC_GROUP_free(group);
+	EC_POINT_free(S);
+	BN_free(h);
+	point_cleanup(&Ppubs);
+	point_cleanup(&P);
+	fp12_cleanup(w);
+	fp12_cleanup(u);
+	BN_CTX_end(bn_ctx);
+	BN_CTX_free(bn_ctx);
+	return ret;
+}
+
+int SM9_sign(int type, /* NID_[sm3 | sha256] */
+	const unsigned char *data, size_t datalen,
+	unsigned char *sig, size_t *siglen,
+	SM9PrivateKey *sk)
+{
+	int ret = 0;
+	EVP_MD_CTX *ctx = NULL;
+	SM9Signature *sm9sig = NULL;
+	const EVP_MD *md;
+	int len;
+
+	if (!(md = EVP_get_digestbynid(type))
+		|| EVP_MD_size(md) != EVP_MD_size(EVP_sm3())) {
+		SM9err(SM9_F_SM9_SIGN, SM9_R_INVALID_HASH2_DIGEST);
+		return 0;
+	}
+
+	if (!(ctx = EVP_MD_CTX_new())) {
+		SM9err(SM9_F_SM9_SIGN, ERR_R_MALLOC_FAILURE);
+		return 0;
+	}
+
+	if (!SM9_SignInit(ctx, md, NULL)
+		|| !SM9_SignUpdate(ctx, data, datalen)
+		|| !(sm9sig = SM9_SignFinal(ctx, sk))) {
+		SM9err(SM9_F_SM9_SIGN, ERR_R_SM9_LIB);
+		goto end;
+	}
+
+	if ((len = i2d_SM9Signature(sm9sig, &sig)) <= 0) {
+		SM9err(SM9_F_SM9_SIGN, ERR_R_SM9_LIB);
+		goto end;
+	}
+
+	*siglen = len;
+	ret = 1;
+
+end:
+	EVP_MD_CTX_free(ctx);
+	SM9Signature_free(sm9sig);
+	return ret;
+}
+
+int SM9_verify(int type, /* NID_[sm3 | sha256] */
+	const unsigned char *data, size_t datalen,
+	const unsigned char *sig, size_t siglen,
+	SM9PublicParameters *mpk, const char *id, size_t idlen)
+{
+	int ret = -1;
+	EVP_MD_CTX *ctx = NULL;
+	SM9Signature *sm9sig = NULL;
+	SM9PublicKey *pk = NULL;
+	const EVP_MD *md;
+
+	if (!(md = EVP_get_digestbynid(type))
+		|| EVP_MD_size(md) != EVP_MD_size(EVP_sm3())) {
+		SM9err(SM9_F_SM9_VERIFY, SM9_R_INVALID_HASH2_DIGEST);
+		return -1;
+	}
+
+	if (!(sm9sig = d2i_SM9Signature(NULL, &sig, siglen))
+		|| i2d_SM9Signature(sm9sig, NULL) != siglen) {
+		SM9err(SM9_F_SM9_VERIFY, SM9_R_INVALID_SIGNATURE_FORMAT);
+		goto end;
+	}
+
+	if (!(pk = SM9_extract_public_key(mpk, id, idlen))) {
+		SM9err(SM9_F_SM9_VERIFY, ERR_R_SM9_LIB);
+		goto end;
+	}
+
+	if (!(ctx = EVP_MD_CTX_new())) {
+		SM9err(SM9_F_SM9_VERIFY, ERR_R_MALLOC_FAILURE);
+		goto end;
+	}
+	if (!SM9_VerifyInit(ctx, md, NULL)
+		|| !SM9_VerifyUpdate(ctx, data, datalen)
+		|| (ret = SM9_VerifyFinal(ctx, sm9sig, pk)) < 0) {
+		SM9err(SM9_F_SM9_VERIFY, ERR_R_SM9_LIB);
+		goto end;
+	}
+
+end:
+	EVP_MD_CTX_free(ctx);
+	SM9Signature_free(sm9sig);
+	SM9PublicKey_free(pk);
+	return ret;
+}
diff --git a/crypto/sms4/build.info b/crypto/sms4/build.info
new file mode 100644
index 000000000..3f1ebc53a
--- /dev/null
+++ b/crypto/sms4/build.info
@@ -0,0 +1,9 @@
+LIBS=../../libcrypto
+SOURCE[../../libcrypto]=\
+	sms4_common.c sms4_setkey.c sms4_enc.c sms4_enc_avx2.c sms4_ede.c \
+	sms4_ecb.c sms4_cbc.c sms4_cfb.c sms4_ctr.c sms4_ofb.c sms4_wrap.c
+
+
+INCLUDE[sms4_setkey.o]=../modes
+INCLUDE[sms4_enc.o]=../modes
+INCLUDE[sms4_enc_avx2.o]=../modes
diff --git a/crypto/sms4/sm4_bs.c b/crypto/sms4/sm4_bs.c
new file mode 100644
index 000000000..621a7e3a3
--- /dev/null
+++ b/crypto/sms4/sm4_bs.c
@@ -0,0 +1,893 @@
+#include 
+#include "sm4_bs.h"
+
+#define GF4_inver(a) ((a)^(((a)>>1)&0x1111111111111111))
+#define bit_4bit(a) ((((bit_2bit(a)<<1)^(a))<<1)^(a))
+#define bit_2bit(a) (((a)<<1)^(a))
+#define GF4X2(a,b) (((b)&(bit_2bit((a)&0x1111111111111111)))^(((b)<<1)&(bit_4bit(((a)>>1)&0x1111111111111111)))^(0x7777777777777777&(bit_4bit((((a)&(b))>>1)&0x1111111111111111))))
+#define GF16X2(a,b)  (JZ2_4(GF4_2_X((JZ4_2((a))),(JZ4_2((b))))))
+#define GB(a,b) ((a)>>(b))&0x1111111111111111
+
+
+unsigned __int64 CKH[2][16][4]=
+{
+	{
+		{0x0000000000000000,0x0000000000000000,0x0000000000000000,0x1111111111111111},
+		{0x1111111111111111,0x2222222222222222,0x2222222222222222,0x3333333333333333},
+		{0x3333333333333333,0x3333333333333333,0x4444444444444444,0x4444444444444444},
+		{0x5555555555555555,0x5555555555555555,0x6666666666666666,0x6666666666666666},
+		{0x7777777777777777,0x7777777777777777,0x7777777777777777,0x8888888888888888},
+		{0x8888888888888888,0x9999999999999999,0x9999999999999999,0xaaaaaaaaaaaaaaaa},
+		{0xaaaaaaaaaaaaaaaa,0xaaaaaaaaaaaaaaaa,0xbbbbbbbbbbbbbbbb,0xbbbbbbbbbbbbbbbb},
+		{0xcccccccccccccccc,0xcccccccccccccccc,0xdddddddddddddddd,0xdddddddddddddddd},
+		{0xeeeeeeeeeeeeeeee,0xeeeeeeeeeeeeeeee,0xeeeeeeeeeeeeeeee,0xffffffffffffffff},
+		{0xffffffffffffffff,0x0000000000000000,0x0000000000000000,0x1111111111111111},
+		{0x1111111111111111,0x1111111111111111,0x2222222222222222,0x2222222222222222},
+		{0x3333333333333333,0x3333333333333333,0x4444444444444444,0x4444444444444444},
+		{0x5555555555555555,0x5555555555555555,0x5555555555555555,0x6666666666666666},
+		{0x6666666666666666,0x7777777777777777,0x7777777777777777,0x8888888888888888},
+		{0x8888888888888888,0x8888888888888888,0x9999999999999999,0x9999999999999999},
+		{0xaaaaaaaaaaaaaaaa,0xaaaaaaaaaaaaaaaa,0xbbbbbbbbbbbbbbbb,0xbbbbbbbbbbbbbbbb}
+	},
+	{
+		{0xcccccccccccccccc,0xcccccccccccccccc,0xcccccccccccccccc,0xdddddddddddddddd},
+		{0xdddddddddddddddd,0xeeeeeeeeeeeeeeee,0xeeeeeeeeeeeeeeee,0xffffffffffffffff},
+		{0xffffffffffffffff,0xffffffffffffffff,0x0000000000000000,0x0000000000000000},
+		{0x1111111111111111,0x1111111111111111,0x2222222222222222,0x2222222222222222},
+		{0x3333333333333333,0x3333333333333333,0x3333333333333333,0x4444444444444444},
+		{0x4444444444444444,0x5555555555555555,0x5555555555555555,0x6666666666666666},
+		{0x6666666666666666,0x6666666666666666,0x7777777777777777,0x7777777777777777},
+		{0x8888888888888888,0x8888888888888888,0x9999999999999999,0x9999999999999999},
+		{0xaaaaaaaaaaaaaaaa,0xaaaaaaaaaaaaaaaa,0xaaaaaaaaaaaaaaaa,0xbbbbbbbbbbbbbbbb},
+		{0xbbbbbbbbbbbbbbbb,0xcccccccccccccccc,0xcccccccccccccccc,0xdddddddddddddddd},
+		{0xdddddddddddddddd,0xdddddddddddddddd,0xeeeeeeeeeeeeeeee,0xeeeeeeeeeeeeeeee},
+		{0xffffffffffffffff,0xffffffffffffffff,0x0000000000000000,0x0000000000000000},
+		{0x1111111111111111,0x1111111111111111,0x1111111111111111,0x2222222222222222},
+		{0x2222222222222222,0x3333333333333333,0x3333333333333333,0x4444444444444444},
+		{0x4444444444444444,0x4444444444444444,0x5555555555555555,0x5555555555555555},
+		{0x6666666666666666,0x6666666666666666,0x7777777777777777,0x7777777777777777}
+	}
+};
+unsigned __int64 CKL[2][16][4]=
+{
+	{
+		{0x0000000000000000,0x7777777777777777,0xeeeeeeeeeeeeeeee,0x5555555555555555},
+		{0xcccccccccccccccc,0x3333333333333333,0xaaaaaaaaaaaaaaaa,0x1111111111111111},
+		{0x8888888888888888,0xffffffffffffffff,0x6666666666666666,0xdddddddddddddddd},
+		{0x4444444444444444,0xbbbbbbbbbbbbbbbb,0x2222222222222222,0x9999999999999999},
+		{0x0000000000000000,0x7777777777777777,0xeeeeeeeeeeeeeeee,0x5555555555555555},
+		{0xcccccccccccccccc,0x3333333333333333,0xaaaaaaaaaaaaaaaa,0x1111111111111111},
+		{0x8888888888888888,0xffffffffffffffff,0x6666666666666666,0xdddddddddddddddd},
+		{0x4444444444444444,0xbbbbbbbbbbbbbbbb,0x2222222222222222,0x9999999999999999},
+		{0x0000000000000000,0x7777777777777777,0xeeeeeeeeeeeeeeee,0x5555555555555555},
+		{0xcccccccccccccccc,0x3333333333333333,0xaaaaaaaaaaaaaaaa,0x1111111111111111},
+		{0x8888888888888888,0xffffffffffffffff,0x6666666666666666,0xdddddddddddddddd},
+		{0x4444444444444444,0xbbbbbbbbbbbbbbbb,0x2222222222222222,0x9999999999999999},
+		{0x0000000000000000,0x7777777777777777,0xeeeeeeeeeeeeeeee,0x5555555555555555},
+		{0xcccccccccccccccc,0x3333333333333333,0xaaaaaaaaaaaaaaaa,0x1111111111111111},
+		{0x8888888888888888,0xffffffffffffffff,0x6666666666666666,0xdddddddddddddddd},
+		{0x4444444444444444,0xbbbbbbbbbbbbbbbb,0x2222222222222222,0x9999999999999999}
+},
+	{
+		{0x0000000000000000,0x7777777777777777,0xeeeeeeeeeeeeeeee,0x5555555555555555},
+		{0xcccccccccccccccc,0x3333333333333333,0xaaaaaaaaaaaaaaaa,0x1111111111111111},
+		{0x8888888888888888,0xffffffffffffffff,0x6666666666666666,0xdddddddddddddddd},
+		{0x4444444444444444,0xbbbbbbbbbbbbbbbb,0x2222222222222222,0x9999999999999999},
+		{0x0000000000000000,0x7777777777777777,0xeeeeeeeeeeeeeeee,0x5555555555555555},
+		{0xcccccccccccccccc,0x3333333333333333,0xaaaaaaaaaaaaaaaa,0x1111111111111111},
+		{0x8888888888888888,0xffffffffffffffff,0x6666666666666666,0xdddddddddddddddd},
+		{0x4444444444444444,0xbbbbbbbbbbbbbbbb,0x2222222222222222,0x9999999999999999},
+		{0x0000000000000000,0x7777777777777777,0xeeeeeeeeeeeeeeee,0x5555555555555555},
+		{0xcccccccccccccccc,0x3333333333333333,0xaaaaaaaaaaaaaaaa,0x1111111111111111},
+		{0x8888888888888888,0xffffffffffffffff,0x6666666666666666,0xdddddddddddddddd},
+		{0x4444444444444444,0xbbbbbbbbbbbbbbbb,0x2222222222222222,0x9999999999999999},
+		{0x0000000000000000,0x7777777777777777,0xeeeeeeeeeeeeeeee,0x5555555555555555},
+		{0xcccccccccccccccc,0x3333333333333333,0xaaaaaaaaaaaaaaaa,0x1111111111111111},
+		{0x8888888888888888,0xffffffffffffffff,0x6666666666666666,0xdddddddddddddddd},
+		{0x4444444444444444,0xbbbbbbbbbbbbbbbb,0x2222222222222222,0x9999999999999999}
+	}
+};
+
+
+unsigned __int64 JZ4_2(unsigned __int64 cin)//4 bit silce
+{
+	unsigned __int64 t;
+	t=0;
+	t=t^((bit_4bit((cin>>(3))&0x1111111111111111))&(0xeeeeeeeeeeeeeeee));
+	t=t^((bit_4bit((cin>>(2))&0x1111111111111111))&(0x6666666666666666));
+	t=t^((bit_4bit((cin>>(1))&0x1111111111111111))&(0x4444444444444444));
+	t=t^((bit_4bit((cin>>(0))&0x1111111111111111))&(0x1111111111111111));
+	return t;
+}
+unsigned __int64 JZ2_4(unsigned __int64 cin)//4 bit silce
+{
+	unsigned __int64 t;
+	t=0;
+	t=t^((bit_4bit((cin>>(3))&0x1111111111111111))&(0xcccccccccccccccc));
+	t=t^((bit_4bit((cin>>(2))&0x1111111111111111))&(0x2222222222222222));
+	t=t^((bit_4bit((cin>>(1))&0x1111111111111111))&(0x6666666666666666));
+	t=t^((bit_4bit((cin>>(0))&0x1111111111111111))&(0x1111111111111111));
+	return t;
+}
+unsigned __int64 GF4_2_X(unsigned __int64 a,unsigned __int64 b)//(4+4)*(4+4),//4 bit silce
+{
+	unsigned __int64 a1,a0,b1,b0;
+
+	a1=(a>>2)&0x3333333333333333;
+	a0=a&0x3333333333333333;
+	b1=(b>>2)&0x3333333333333333;
+	b0=b&0x3333333333333333;
+
+	a1=((GF4X2(a0,b1)^GF4X2(a1,b0)^GF4X2(a1,b1))<<2)|(GF4X2(a0,b0)^GF4X2(0x2222222222222222,(GF4X2(a1,b1))));
+	return a1;//·½³Ìx^2+x+2
+}
+unsigned __int64 GF4X2_inver(unsigned __int64 in)//4+4bit invers//4 bit silce
+{
+	unsigned __int64 a1,a0,dt,rdt;
+	a1=(in>>2)&0x3333333333333333;
+	a0=in&0x3333333333333333;
+	dt=GF4X2((a1^a0),a0)^GF4X2(GF4X2(0x2222222222222222,a1),a1);
+	dt=GF4_inver(dt);
+	return (GF4X2(a1,dt)<<2)|GF4X2(a1^a0,dt);
+}
+void bit256_16X2_bs(unsigned __int64 cinh,unsigned __int64 cinl,unsigned __int64 &th,unsigned __int64 &tl)//8 -> 4+4
+{	
+	th=0;
+	th=th^((bit_4bit((cinh>>(3))&0x1111111111111111))&(0x2222222222222222));
+	th=th^((bit_4bit((cinh>>(2))&0x1111111111111111))&(0xffffffffffffffff));
+	th=th^((bit_4bit((cinh>>(1))&0x1111111111111111))&(0x4444444444444444));
+	th=th^((bit_4bit((cinh>>(0))&0x1111111111111111))&(0xffffffffffffffff));
+
+	th=th^((bit_4bit((cinl>>(3))&0x1111111111111111))&(0xcccccccccccccccc));
+	th=th^((bit_4bit((cinl>>(2))&0x1111111111111111))&(0xcccccccccccccccc));
+	th=th^((bit_4bit((cinl>>(1))&0x1111111111111111))&(0x8888888888888888));
+	th=th^((bit_4bit((cinl>>(0))&0x1111111111111111))&(0x0000000000000000));
+
+	tl=0;
+	tl=tl^((bit_4bit((cinh>>(3))&0x1111111111111111))&(0x6666666666666666));
+	tl=tl^((bit_4bit((cinh>>(2))&0x1111111111111111))&(0xbbbbbbbbbbbbbbbb));
+	tl=tl^((bit_4bit((cinh>>(1))&0x1111111111111111))&(0xdddddddddddddddd));
+	tl=tl^((bit_4bit((cinh>>(0))&0x1111111111111111))&(0xffffffffffffffff));
+
+	tl=tl^((bit_4bit((cinl>>(3))&0x1111111111111111))&(0x3333333333333333));
+	tl=tl^((bit_4bit((cinl>>(2))&0x1111111111111111))&(0x1111111111111111));
+	tl=tl^((bit_4bit((cinl>>(1))&0x1111111111111111))&(0x6666666666666666));
+	tl=tl^((bit_4bit((cinl>>(0))&0x1111111111111111))&(0x1111111111111111));
+}
+
+void bit16X2_256_bs(unsigned __int64 cinh,unsigned __int64 cinl,unsigned __int64 &th,unsigned __int64 &tl)//4+4 -> 8
+{
+	th=0;
+	th=th^((bit_4bit((cinh>>(3))&0x1111111111111111))&(0x5555555555555555));
+	th=th^((bit_4bit((cinh>>(2))&0x1111111111111111))&(0x5555555555555555));
+	th=th^((bit_4bit((cinh>>(1))&0x1111111111111111))&(0xdddddddddddddddd));
+	th=th^((bit_4bit((cinh>>(0))&0x1111111111111111))&(0xbbbbbbbbbbbbbbbb));
+
+	th=th^((bit_4bit((cinl>>(3))&0x1111111111111111))&(0x2222222222222222));
+	th=th^((bit_4bit((cinl>>(2))&0x1111111111111111))&(0x5555555555555555));
+	th=th^((bit_4bit((cinl>>(1))&0x1111111111111111))&(0x0000000000000000));
+	th=th^((bit_4bit((cinl>>(0))&0x1111111111111111))&(0x0000000000000000));
+
+	tl=0;
+	tl=tl^((bit_4bit((cinh>>(3))&0x1111111111111111))&(0xeeeeeeeeeeeeeeee));
+	tl=tl^((bit_4bit((cinh>>(2))&0x1111111111111111))&(0xbbbbbbbbbbbbbbbb));
+	tl=tl^((bit_4bit((cinh>>(1))&0x1111111111111111))&(0xcccccccccccccccc));
+	tl=tl^((bit_4bit((cinh>>(0))&0x1111111111111111))&(0xeeeeeeeeeeeeeeee));
+
+	tl=tl^((bit_4bit((cinl>>(3))&0x1111111111111111))&(0xaaaaaaaaaaaaaaaa));
+	tl=tl^((bit_4bit((cinl>>(2))&0x1111111111111111))&(0x0000000000000000));
+	tl=tl^((bit_4bit((cinl>>(1))&0x1111111111111111))&(0xcccccccccccccccc));
+	tl=tl^((bit_4bit((cinl>>(0))&0x1111111111111111))&(0x1111111111111111));
+}
+void GF16_2_inver_bs(unsigned __int64 a1,unsigned __int64 a0,unsigned __int64 &th,unsigned __int64 &tl)//2+2 invers
+{
+	unsigned __int64 dt;
+	unsigned __int64 rdt,t;
+
+	dt=GF16X2((a1^a0),a0)^GF16X2(GF16X2(0x9999999999999999,a1),a1);
+
+	dt=JZ2_4(GF4X2_inver(JZ4_2(dt)));//
+	th=GF16X2(a1,dt);
+	tl=GF16X2(a1^a0,dt);
+}
+
+void S_BOX_bs(unsigned __int64 cinh,unsigned __int64 cinl,unsigned __int64 &th,unsigned __int64 &tl)
+{
+	unsigned char temp;
+	th=0;
+	th=th^((bit_4bit((cinh>>(3))&0x1111111111111111))&0xeeeeeeeeeeeeeeee);
+	th=th^((bit_4bit((cinh>>(2))&0x1111111111111111))&0xffffffffffffffff);
+	th=th^((bit_4bit((cinh>>(1))&0x1111111111111111))&0x7777777777777777);
+	th=th^((bit_4bit((cinh>>(0))&0x1111111111111111))&0xbbbbbbbbbbbbbbbb);
+
+	th=th^((bit_4bit((cinl>>(3))&0x1111111111111111))&0x5555555555555555);
+	th=th^((bit_4bit((cinl>>(2))&0x1111111111111111))&0x2222222222222222);
+	th=th^((bit_4bit((cinl>>(1))&0x1111111111111111))&0x9999999999999999);
+	th=th^((bit_4bit((cinl>>(0))&0x1111111111111111))&0xcccccccccccccccc);
+
+	tl=0;
+	tl=tl^((bit_4bit((cinh>>(3))&0x1111111111111111))&0x5555555555555555);
+	tl=tl^((bit_4bit((cinh>>(2))&0x1111111111111111))&0x2222222222222222);
+	tl=tl^((bit_4bit((cinh>>(1))&0x1111111111111111))&0x9999999999999999);
+	tl=tl^((bit_4bit((cinh>>(0))&0x1111111111111111))&0xcccccccccccccccc);
+
+	tl=tl^((bit_4bit((cinl>>(3))&0x1111111111111111))&0xeeeeeeeeeeeeeeee);
+	tl=tl^((bit_4bit((cinl>>(2))&0x1111111111111111))&0xffffffffffffffff);
+	tl=tl^((bit_4bit((cinl>>(1))&0x1111111111111111))&0x7777777777777777);
+	tl=tl^((bit_4bit((cinl>>(0))&0x1111111111111111))&0xbbbbbbbbbbbbbbbb);
+
+	th=th^0xdddddddddddddddd;
+	tl=tl^0x3333333333333333;
+
+	bit256_16X2_bs(th,tl,cinh,cinl);
+	GF16_2_inver_bs(cinh,cinl,th,tl);
+	bit16X2_256_bs(th,tl,cinh,cinl);
+
+	th=0;
+	th=th^((bit_4bit((cinh>>(3))&0x1111111111111111))&0xeeeeeeeeeeeeeeee);
+	th=th^((bit_4bit((cinh>>(2))&0x1111111111111111))&0xffffffffffffffff);
+	th=th^((bit_4bit((cinh>>(1))&0x1111111111111111))&0x7777777777777777);
+	th=th^((bit_4bit((cinh>>(0))&0x1111111111111111))&0xbbbbbbbbbbbbbbbb);
+
+	th=th^((bit_4bit((cinl>>(3))&0x1111111111111111))&0x5555555555555555);
+	th=th^((bit_4bit((cinl>>(2))&0x1111111111111111))&0x2222222222222222);
+	th=th^((bit_4bit((cinl>>(1))&0x1111111111111111))&0x9999999999999999);
+	th=th^((bit_4bit((cinl>>(0))&0x1111111111111111))&0xcccccccccccccccc);
+
+	tl=0;
+	tl=tl^((bit_4bit((cinh>>(3))&0x1111111111111111))&0x5555555555555555);
+	tl=tl^((bit_4bit((cinh>>(2))&0x1111111111111111))&0x2222222222222222);
+	tl=tl^((bit_4bit((cinh>>(1))&0x1111111111111111))&0x9999999999999999);
+	tl=tl^((bit_4bit((cinh>>(0))&0x1111111111111111))&0xcccccccccccccccc);
+
+	tl=tl^((bit_4bit((cinl>>(3))&0x1111111111111111))&0xeeeeeeeeeeeeeeee);
+	tl=tl^((bit_4bit((cinl>>(2))&0x1111111111111111))&0xffffffffffffffff);
+	tl=tl^((bit_4bit((cinl>>(1))&0x1111111111111111))&0x7777777777777777);
+	tl=tl^((bit_4bit((cinl>>(0))&0x1111111111111111))&0xbbbbbbbbbbbbbbbb);
+
+	th=th^0xdddddddddddddddd;
+	tl=tl^0x3333333333333333;
+}
+
+void print_int(unsigned __int64 PH[4],unsigned __int64 PL[4],__int64 id)
+{
+	__int64 i,j;
+	//printf("0x");
+	//for(i=0;i<4;i++)printf("%x%x",(PH[i]>>(id*4))&0xf,(PL[i]>>(id*4))&0xf);
+	//printf("\n");
+
+	for(i=0;i<4;i++)
+	{
+		for(j=3;j>=0;j--)
+		{
+			printf("%d",(((PH[i]>>(id*4))&0xf)>>j)&1);
+		}
+		printf(" ");
+		for(j=3;j>=0;j--)
+		{
+			printf("%d",(((PL[i]>>(id*4))&0xf)>>j)&1);
+		}
+		printf(" ");
+	}printf("\n");
+}
+void ENC_ROUND_BS0(unsigned __int64 Xh[5][4],unsigned __int64 Xl[5][4],unsigned __int64 RKH[4],unsigned __int64 RKL[4],unsigned __int64 i,unsigned __int64 ii)	
+{
+	Xh[4][0]=Xh[1][0]^Xh[2][0]^Xh[3][0]^CKH[i][ii][0];
+	Xh[4][1]=Xh[1][1]^Xh[2][1]^Xh[3][1]^CKH[i][ii][1];
+	Xh[4][2]=Xh[1][2]^Xh[2][2]^Xh[3][2]^CKH[i][ii][2];
+	Xh[4][3]=Xh[1][3]^Xh[2][3]^Xh[3][3]^CKH[i][ii][3];
+	Xl[4][0]=Xl[1][0]^Xl[2][0]^Xl[3][0]^CKL[i][ii][0];
+	Xl[4][1]=Xl[1][1]^Xl[2][1]^Xl[3][1]^CKL[i][ii][1];
+	Xl[4][2]=Xl[1][2]^Xl[2][2]^Xl[3][2]^CKL[i][ii][2];
+	Xl[4][3]=Xl[1][3]^Xl[2][3]^Xl[3][3]^CKL[i][ii][3];
+
+	S_BOX_bs(Xh[4][0],Xl[4][0],Xh[4][0],Xl[4][0]);
+	S_BOX_bs(Xh[4][1],Xl[4][1],Xh[4][1],Xl[4][1]);
+	S_BOX_bs(Xh[4][2],Xl[4][2],Xh[4][2],Xl[4][2]);
+	S_BOX_bs(Xh[4][3],Xl[4][3],Xh[4][3],Xl[4][3]);
+
+	RKH[0]=Xh[0][0]^Xh[4][0]^((Xl[4][1]&0x7777777777777777)<<1)^((Xh[4][2]>>3)&0x1111111111111111)^((Xl[4][2]&0x1111111111111111)<<3)^((Xh[4][3]>>1)&0x7777777777777777);
+	RKH[1]=Xh[0][1]^Xh[4][1]^((Xl[4][2]&0x7777777777777777)<<1)^((Xh[4][3]>>3)&0x1111111111111111)^((Xl[4][3]&0x1111111111111111)<<3)^((Xh[4][0]>>1)&0x7777777777777777);
+	RKH[2]=Xh[0][2]^Xh[4][2]^((Xl[4][3]&0x7777777777777777)<<1)^((Xh[4][0]>>3)&0x1111111111111111)^((Xl[4][0]&0x1111111111111111)<<3)^((Xh[4][1]>>1)&0x7777777777777777);
+	RKH[3]=Xh[0][3]^Xh[4][3]^((Xl[4][0]&0x7777777777777777)<<1)^((Xh[4][1]>>3)&0x1111111111111111)^((Xl[4][1]&0x1111111111111111)<<3)^((Xh[4][2]>>1)&0x7777777777777777);
+	RKL[0]=Xl[0][0]^Xl[4][0]^((Xh[4][2]&0x7777777777777777)<<1)^((Xl[4][2]>>3)&0x1111111111111111)^((Xh[4][3]&0x1111111111111111)<<3)^((Xl[4][3]>>1)&0x7777777777777777);
+	RKL[1]=Xl[0][1]^Xl[4][1]^((Xh[4][3]&0x7777777777777777)<<1)^((Xl[4][3]>>3)&0x1111111111111111)^((Xh[4][0]&0x1111111111111111)<<3)^((Xl[4][0]>>1)&0x7777777777777777);
+	RKL[2]=Xl[0][2]^Xl[4][2]^((Xh[4][0]&0x7777777777777777)<<1)^((Xl[4][0]>>3)&0x1111111111111111)^((Xh[4][1]&0x1111111111111111)<<3)^((Xl[4][1]>>1)&0x7777777777777777);
+	RKL[3]=Xl[0][3]^Xl[4][3]^((Xh[4][1]&0x7777777777777777)<<1)^((Xl[4][1]>>3)&0x1111111111111111)^((Xh[4][2]&0x1111111111111111)<<3)^((Xl[4][2]>>1)&0x7777777777777777); 
+
+	Xh[4][0]=RKH[0];
+ 	Xh[4][1]=RKH[1];
+ 	Xh[4][2]=RKH[2];
+ 	Xh[4][3]=RKH[3];
+ 	Xl[4][0]=RKL[0];
+ 	Xl[4][1]=RKL[1];
+ 	Xl[4][2]=RKL[2];
+ 	Xl[4][3]=RKL[3];
+
+}
+void ENC_ROUND_BS1(unsigned __int64 Xh[5][4],unsigned __int64 Xl[5][4],unsigned __int64 RKH[4],unsigned __int64 RKL[4],unsigned __int64 i,unsigned __int64 ii)	
+{
+
+	Xh[0][0]=Xh[2][0]^Xh[3][0]^Xh[4][0]^CKH[i][ii][0];
+	Xh[0][1]=Xh[2][1]^Xh[3][1]^Xh[4][1]^CKH[i][ii][1];
+	Xh[0][2]=Xh[2][2]^Xh[3][2]^Xh[4][2]^CKH[i][ii][2];
+	Xh[0][3]=Xh[2][3]^Xh[3][3]^Xh[4][3]^CKH[i][ii][3];
+	Xl[0][0]=Xl[2][0]^Xl[3][0]^Xl[4][0]^CKL[i][ii][0];
+	Xl[0][1]=Xl[2][1]^Xl[3][1]^Xl[4][1]^CKL[i][ii][1];
+	Xl[0][2]=Xl[2][2]^Xl[3][2]^Xl[4][2]^CKL[i][ii][2];
+	Xl[0][3]=Xl[2][3]^Xl[3][3]^Xl[4][3]^CKL[i][ii][3];
+
+	S_BOX_bs(Xh[0][0],Xl[0][0],Xh[0][0],Xl[0][0]);
+	S_BOX_bs(Xh[0][1],Xl[0][1],Xh[0][1],Xl[0][1]);
+	S_BOX_bs(Xh[0][2],Xl[0][2],Xh[0][2],Xl[0][2]);
+	S_BOX_bs(Xh[0][3],Xl[0][3],Xh[0][3],Xl[0][3]);
+
+	RKH[0]=Xh[1][0]^Xh[0][0]^((Xl[0][1]&0x7777777777777777)<<1)^((Xh[0][2]>>3)&0x1111111111111111)^((Xl[0][2]&0x1111111111111111)<<3)^((Xh[0][3]>>1)&0x7777777777777777);
+	RKH[1]=Xh[1][1]^Xh[0][1]^((Xl[0][2]&0x7777777777777777)<<1)^((Xh[0][3]>>3)&0x1111111111111111)^((Xl[0][3]&0x1111111111111111)<<3)^((Xh[0][0]>>1)&0x7777777777777777);
+	RKH[2]=Xh[1][2]^Xh[0][2]^((Xl[0][3]&0x7777777777777777)<<1)^((Xh[0][0]>>3)&0x1111111111111111)^((Xl[0][0]&0x1111111111111111)<<3)^((Xh[0][1]>>1)&0x7777777777777777);
+	RKH[3]=Xh[1][3]^Xh[0][3]^((Xl[0][0]&0x7777777777777777)<<1)^((Xh[0][1]>>3)&0x1111111111111111)^((Xl[0][1]&0x1111111111111111)<<3)^((Xh[0][2]>>1)&0x7777777777777777);
+	RKL[0]=Xl[1][0]^Xl[0][0]^((Xh[0][2]&0x7777777777777777)<<1)^((Xl[0][2]>>3)&0x1111111111111111)^((Xh[0][3]&0x1111111111111111)<<3)^((Xl[0][3]>>1)&0x7777777777777777);
+	RKL[1]=Xl[1][1]^Xl[0][1]^((Xh[0][3]&0x7777777777777777)<<1)^((Xl[0][3]>>3)&0x1111111111111111)^((Xh[0][0]&0x1111111111111111)<<3)^((Xl[0][0]>>1)&0x7777777777777777);
+	RKL[2]=Xl[1][2]^Xl[0][2]^((Xh[0][0]&0x7777777777777777)<<1)^((Xl[0][0]>>3)&0x1111111111111111)^((Xh[0][1]&0x1111111111111111)<<3)^((Xl[0][1]>>1)&0x7777777777777777);
+	RKL[3]=Xl[1][3]^Xl[0][3]^((Xh[0][1]&0x7777777777777777)<<1)^((Xl[0][1]>>3)&0x1111111111111111)^((Xh[0][2]&0x1111111111111111)<<3)^((Xl[0][2]>>1)&0x7777777777777777); 
+
+	Xh[0][0]=RKH[0];
+ 	Xh[0][1]=RKH[1];
+ 	Xh[0][2]=RKH[2];
+ 	Xh[0][3]=RKH[3];
+ 	Xl[0][0]=RKL[0];
+ 	Xl[0][1]=RKL[1];
+ 	Xl[0][2]=RKL[2];
+ 	Xl[0][3]=RKL[3];
+}
+void ENC_ROUND_BS2(unsigned __int64 Xh[5][4],unsigned __int64 Xl[5][4],unsigned __int64 RKH[4],unsigned __int64 RKL[4],unsigned __int64 i,unsigned __int64 ii)	
+{
+
+	Xh[1][0]=Xh[3][0]^Xh[4][0]^Xh[0][0]^CKH[i][ii][0];
+	Xh[1][1]=Xh[3][1]^Xh[4][1]^Xh[0][1]^CKH[i][ii][1];
+	Xh[1][2]=Xh[3][2]^Xh[4][2]^Xh[0][2]^CKH[i][ii][2];
+	Xh[1][3]=Xh[3][3]^Xh[4][3]^Xh[0][3]^CKH[i][ii][3];
+	Xl[1][0]=Xl[3][0]^Xl[4][0]^Xl[0][0]^CKL[i][ii][0];
+	Xl[1][1]=Xl[3][1]^Xl[4][1]^Xl[0][1]^CKL[i][ii][1];
+	Xl[1][2]=Xl[3][2]^Xl[4][2]^Xl[0][2]^CKL[i][ii][2];
+	Xl[1][3]=Xl[3][3]^Xl[4][3]^Xl[0][3]^CKL[i][ii][3];
+
+	S_BOX_bs(Xh[1][0],Xl[1][0],Xh[1][0],Xl[1][0]);
+	S_BOX_bs(Xh[1][1],Xl[1][1],Xh[1][1],Xl[1][1]);
+	S_BOX_bs(Xh[1][2],Xl[1][2],Xh[1][2],Xl[1][2]);
+	S_BOX_bs(Xh[1][3],Xl[1][3],Xh[1][3],Xl[1][3]);
+
+	RKH[0]=Xh[2][0]^Xh[1][0]^((Xl[1][1]&0x7777777777777777)<<1)^((Xh[1][2]>>3)&0x1111111111111111)^((Xl[1][2]&0x1111111111111111)<<3)^((Xh[1][3]>>1)&0x7777777777777777);
+	RKH[1]=Xh[2][1]^Xh[1][1]^((Xl[1][2]&0x7777777777777777)<<1)^((Xh[1][3]>>3)&0x1111111111111111)^((Xl[1][3]&0x1111111111111111)<<3)^((Xh[1][0]>>1)&0x7777777777777777);
+	RKH[2]=Xh[2][2]^Xh[1][2]^((Xl[1][3]&0x7777777777777777)<<1)^((Xh[1][0]>>3)&0x1111111111111111)^((Xl[1][0]&0x1111111111111111)<<3)^((Xh[1][1]>>1)&0x7777777777777777);
+	RKH[3]=Xh[2][3]^Xh[1][3]^((Xl[1][0]&0x7777777777777777)<<1)^((Xh[1][1]>>3)&0x1111111111111111)^((Xl[1][1]&0x1111111111111111)<<3)^((Xh[1][2]>>1)&0x7777777777777777);
+	RKL[0]=Xl[2][0]^Xl[1][0]^((Xh[1][2]&0x7777777777777777)<<1)^((Xl[1][2]>>3)&0x1111111111111111)^((Xh[1][3]&0x1111111111111111)<<3)^((Xl[1][3]>>1)&0x7777777777777777);
+	RKL[1]=Xl[2][1]^Xl[1][1]^((Xh[1][3]&0x7777777777777777)<<1)^((Xl[1][3]>>3)&0x1111111111111111)^((Xh[1][0]&0x1111111111111111)<<3)^((Xl[1][0]>>1)&0x7777777777777777);
+	RKL[2]=Xl[2][2]^Xl[1][2]^((Xh[1][0]&0x7777777777777777)<<1)^((Xl[1][0]>>3)&0x1111111111111111)^((Xh[1][1]&0x1111111111111111)<<3)^((Xl[1][1]>>1)&0x7777777777777777);
+	RKL[3]=Xl[2][3]^Xl[1][3]^((Xh[1][1]&0x7777777777777777)<<1)^((Xl[1][1]>>3)&0x1111111111111111)^((Xh[1][2]&0x1111111111111111)<<3)^((Xl[1][2]>>1)&0x7777777777777777); 
+
+	Xh[1][0]=RKH[0];
+ 	Xh[1][1]=RKH[1];
+ 	Xh[1][2]=RKH[2];
+ 	Xh[1][3]=RKH[3];
+ 	Xl[1][0]=RKL[0];
+ 	Xl[1][1]=RKL[1];
+ 	Xl[1][2]=RKL[2];
+ 	Xl[1][3]=RKL[3];
+}
+void ENC_ROUND_BS3(unsigned __int64 Xh[5][4],unsigned __int64 Xl[5][4],unsigned __int64 RKH[4],unsigned __int64 RKL[4],unsigned __int64 i,unsigned __int64 ii)	
+{
+
+	Xh[2][0]=Xh[4][0]^Xh[0][0]^Xh[1][0]^CKH[i][ii][0];
+	Xh[2][1]=Xh[4][1]^Xh[0][1]^Xh[1][1]^CKH[i][ii][1];
+	Xh[2][2]=Xh[4][2]^Xh[0][2]^Xh[1][2]^CKH[i][ii][2];
+	Xh[2][3]=Xh[4][3]^Xh[0][3]^Xh[1][3]^CKH[i][ii][3];
+	Xl[2][0]=Xl[4][0]^Xl[0][0]^Xl[1][0]^CKL[i][ii][0];
+	Xl[2][1]=Xl[4][1]^Xl[0][1]^Xl[1][1]^CKL[i][ii][1];
+	Xl[2][2]=Xl[4][2]^Xl[0][2]^Xl[1][2]^CKL[i][ii][2];
+	Xl[2][3]=Xl[4][3]^Xl[0][3]^Xl[1][3]^CKL[i][ii][3];
+
+	S_BOX_bs(Xh[2][0],Xl[2][0],Xh[2][0],Xl[2][0]);
+	S_BOX_bs(Xh[2][1],Xl[2][1],Xh[2][1],Xl[2][1]);
+	S_BOX_bs(Xh[2][2],Xl[2][2],Xh[2][2],Xl[2][2]);
+	S_BOX_bs(Xh[2][3],Xl[2][3],Xh[2][3],Xl[2][3]);
+
+	RKH[0]=Xh[3][0]^Xh[2][0]^((Xl[2][1]&0x7777777777777777)<<1)^((Xh[2][2]>>3)&0x1111111111111111)^((Xl[2][2]&0x1111111111111111)<<3)^((Xh[2][3]>>1)&0x7777777777777777);
+	RKH[1]=Xh[3][1]^Xh[2][1]^((Xl[2][2]&0x7777777777777777)<<1)^((Xh[2][3]>>3)&0x1111111111111111)^((Xl[2][3]&0x1111111111111111)<<3)^((Xh[2][0]>>1)&0x7777777777777777);
+	RKH[2]=Xh[3][2]^Xh[2][2]^((Xl[2][3]&0x7777777777777777)<<1)^((Xh[2][0]>>3)&0x1111111111111111)^((Xl[2][0]&0x1111111111111111)<<3)^((Xh[2][1]>>1)&0x7777777777777777);
+	RKH[3]=Xh[3][3]^Xh[2][3]^((Xl[2][0]&0x7777777777777777)<<1)^((Xh[2][1]>>3)&0x1111111111111111)^((Xl[2][1]&0x1111111111111111)<<3)^((Xh[2][2]>>1)&0x7777777777777777);
+	RKL[0]=Xl[3][0]^Xl[2][0]^((Xh[2][2]&0x7777777777777777)<<1)^((Xl[2][2]>>3)&0x1111111111111111)^((Xh[2][3]&0x1111111111111111)<<3)^((Xl[2][3]>>1)&0x7777777777777777);
+	RKL[1]=Xl[3][1]^Xl[2][1]^((Xh[2][3]&0x7777777777777777)<<1)^((Xl[2][3]>>3)&0x1111111111111111)^((Xh[2][0]&0x1111111111111111)<<3)^((Xl[2][0]>>1)&0x7777777777777777);
+	RKL[2]=Xl[3][2]^Xl[2][2]^((Xh[2][0]&0x7777777777777777)<<1)^((Xl[2][0]>>3)&0x1111111111111111)^((Xh[2][1]&0x1111111111111111)<<3)^((Xl[2][1]>>1)&0x7777777777777777);
+	RKL[3]=Xl[3][3]^Xl[2][3]^((Xh[2][1]&0x7777777777777777)<<1)^((Xl[2][1]>>3)&0x1111111111111111)^((Xh[2][2]&0x1111111111111111)<<3)^((Xl[2][2]>>1)&0x7777777777777777); 
+
+	Xh[2][0]=RKH[0];
+ 	Xh[2][1]=RKH[1];
+ 	Xh[2][2]=RKH[2];
+ 	Xh[2][3]=RKH[3];
+ 	Xl[2][0]=RKL[0];
+ 	Xl[2][1]=RKL[1];
+ 	Xl[2][2]=RKL[2];
+ 	Xl[2][3]=RKL[3];
+}
+void ENC_ROUND_BS4(unsigned __int64 Xh[5][4],unsigned __int64 Xl[5][4],unsigned __int64 RKH[4],unsigned __int64 RKL[4],unsigned __int64 i,unsigned __int64 ii)	
+{
+
+	Xh[3][0]=Xh[0][0]^Xh[1][0]^Xh[2][0]^CKH[i][ii][0];
+	Xh[3][1]=Xh[0][1]^Xh[1][1]^Xh[2][1]^CKH[i][ii][1];
+	Xh[3][2]=Xh[0][2]^Xh[1][2]^Xh[2][2]^CKH[i][ii][2];
+	Xh[3][3]=Xh[0][3]^Xh[1][3]^Xh[2][3]^CKH[i][ii][3];
+	Xl[3][0]=Xl[0][0]^Xl[1][0]^Xl[2][0]^CKL[i][ii][0];
+	Xl[3][1]=Xl[0][1]^Xl[1][1]^Xl[2][1]^CKL[i][ii][1];
+	Xl[3][2]=Xl[0][2]^Xl[1][2]^Xl[2][2]^CKL[i][ii][2];
+	Xl[3][3]=Xl[0][3]^Xl[1][3]^Xl[2][3]^CKL[i][ii][3];
+
+	S_BOX_bs(Xh[3][0],Xl[3][0],Xh[3][0],Xl[3][0]);
+	S_BOX_bs(Xh[3][1],Xl[3][1],Xh[3][1],Xl[3][1]);
+	S_BOX_bs(Xh[3][2],Xl[3][2],Xh[3][2],Xl[3][2]);
+	S_BOX_bs(Xh[3][3],Xl[3][3],Xh[3][3],Xl[3][3]);
+	RKH[0]=Xh[4][0]^Xh[3][0]^((Xl[3][1]&0x7777777777777777)<<1)^((Xh[3][2]>>3)&0x1111111111111111)^((Xl[3][2]&0x1111111111111111)<<3)^((Xh[3][3]>>1)&0x7777777777777777);
+	RKH[1]=Xh[4][1]^Xh[3][1]^((Xl[3][2]&0x7777777777777777)<<1)^((Xh[3][3]>>3)&0x1111111111111111)^((Xl[3][3]&0x1111111111111111)<<3)^((Xh[3][0]>>1)&0x7777777777777777);
+	RKH[2]=Xh[4][2]^Xh[3][2]^((Xl[3][3]&0x7777777777777777)<<1)^((Xh[3][0]>>3)&0x1111111111111111)^((Xl[3][0]&0x1111111111111111)<<3)^((Xh[3][1]>>1)&0x7777777777777777);
+	RKH[3]=Xh[4][3]^Xh[3][3]^((Xl[3][0]&0x7777777777777777)<<1)^((Xh[3][1]>>3)&0x1111111111111111)^((Xl[3][1]&0x1111111111111111)<<3)^((Xh[3][2]>>1)&0x7777777777777777);
+	RKL[0]=Xl[4][0]^Xl[3][0]^((Xh[3][2]&0x7777777777777777)<<1)^((Xl[3][2]>>3)&0x1111111111111111)^((Xh[3][3]&0x1111111111111111)<<3)^((Xl[3][3]>>1)&0x7777777777777777);
+	RKL[1]=Xl[4][1]^Xl[3][1]^((Xh[3][3]&0x7777777777777777)<<1)^((Xl[3][3]>>3)&0x1111111111111111)^((Xh[3][0]&0x1111111111111111)<<3)^((Xl[3][0]>>1)&0x7777777777777777);
+	RKL[2]=Xl[4][2]^Xl[3][2]^((Xh[3][0]&0x7777777777777777)<<1)^((Xl[3][0]>>3)&0x1111111111111111)^((Xh[3][1]&0x1111111111111111)<<3)^((Xl[3][1]>>1)&0x7777777777777777);
+	RKL[3]=Xl[4][3]^Xl[3][3]^((Xh[3][1]&0x7777777777777777)<<1)^((Xl[3][1]>>3)&0x1111111111111111)^((Xh[3][2]&0x1111111111111111)<<3)^((Xl[3][2]>>1)&0x7777777777777777); 
+	
+	Xh[3][0]=RKH[0];
+ 	Xh[3][1]=RKH[1];
+ 	Xh[3][2]=RKH[2];
+ 	Xh[3][3]=RKH[3];
+ 	Xl[3][0]=RKL[0];
+ 	Xl[3][1]=RKL[1];
+ 	Xl[3][2]=RKL[2];
+ 	Xl[3][3]=RKL[3];
+}
+void ROUND_BS0(unsigned __int64 Xh[5][4],unsigned __int64 Xl[5][4],unsigned __int64 RKH[4],unsigned __int64 RKL[4])	
+{
+	unsigned __int64 TH[4];
+	unsigned __int64 TL[4];
+	Xh[4][0]=Xh[1][0]^Xh[2][0]^Xh[3][0]^RKH[0];
+	Xh[4][1]=Xh[1][1]^Xh[2][1]^Xh[3][1]^RKH[1];
+	Xh[4][2]=Xh[1][2]^Xh[2][2]^Xh[3][2]^RKH[2];
+	Xh[4][3]=Xh[1][3]^Xh[2][3]^Xh[3][3]^RKH[3];
+	Xl[4][0]=Xl[1][0]^Xl[2][0]^Xl[3][0]^RKL[0];
+	Xl[4][1]=Xl[1][1]^Xl[2][1]^Xl[3][1]^RKL[1];
+	Xl[4][2]=Xl[1][2]^Xl[2][2]^Xl[3][2]^RKL[2];
+	Xl[4][3]=Xl[1][3]^Xl[2][3]^Xl[3][3]^RKL[3];
+
+	S_BOX_bs(Xh[4][0],Xl[4][0],Xh[4][0],Xl[4][0]);
+	S_BOX_bs(Xh[4][1],Xl[4][1],Xh[4][1],Xl[4][1]);
+	S_BOX_bs(Xh[4][2],Xl[4][2],Xh[4][2],Xl[4][2]);
+	S_BOX_bs(Xh[4][3],Xl[4][3],Xh[4][3],Xl[4][3]);
+
+	TH[0]=Xh[0][0]^Xh[4][0]^((Xh[4][0]&0x3333333333333333)<<2)^((Xl[4][0]>>2)&0x3333333333333333)^((Xh[4][1]&0x3333333333333333)<<2)^((Xl[4][1]>>2)&0x3333333333333333)^((Xh[4][2]&0x3333333333333333)<<2)^((Xl[4][2]>>2)&0x3333333333333333)^((Xh[4][3]&0xffffffffffffffff)<<0)^((Xl[4][3]>>4)&0x0000000000000000);
+	TH[1]=Xh[0][1]^Xh[4][1]^((Xh[4][1]&0x3333333333333333)<<2)^((Xl[4][1]>>2)&0x3333333333333333)^((Xh[4][2]&0x3333333333333333)<<2)^((Xl[4][2]>>2)&0x3333333333333333)^((Xh[4][3]&0x3333333333333333)<<2)^((Xl[4][3]>>2)&0x3333333333333333)^((Xh[4][0]&0xffffffffffffffff)<<0)^((Xl[4][0]>>4)&0x0000000000000000);
+	TH[2]=Xh[0][2]^Xh[4][2]^((Xh[4][2]&0x3333333333333333)<<2)^((Xl[4][2]>>2)&0x3333333333333333)^((Xh[4][3]&0x3333333333333333)<<2)^((Xl[4][3]>>2)&0x3333333333333333)^((Xh[4][0]&0x3333333333333333)<<2)^((Xl[4][0]>>2)&0x3333333333333333)^((Xh[4][1]&0xffffffffffffffff)<<0)^((Xl[4][1]>>4)&0x0000000000000000);
+	TH[3]=Xh[0][3]^Xh[4][3]^((Xh[4][3]&0x3333333333333333)<<2)^((Xl[4][3]>>2)&0x3333333333333333)^((Xh[4][0]&0x3333333333333333)<<2)^((Xl[4][0]>>2)&0x3333333333333333)^((Xh[4][1]&0x3333333333333333)<<2)^((Xl[4][1]>>2)&0x3333333333333333)^((Xh[4][2]&0xffffffffffffffff)<<0)^((Xl[4][2]>>4)&0x0000000000000000);
+	TL[0]=Xl[0][0]^Xl[4][0]^((Xl[4][0]&0x3333333333333333)<<2)^((Xh[4][1]>>2)&0x3333333333333333)^((Xl[4][1]&0x3333333333333333)<<2)^((Xh[4][2]>>2)&0x3333333333333333)^((Xl[4][2]&0x3333333333333333)<<2)^((Xh[4][3]>>2)&0x3333333333333333)^((Xl[4][3]&0xffffffffffffffff)<<0)^((Xh[4][3]>>4)&0x0000000000000000);
+	TL[1]=Xl[0][1]^Xl[4][1]^((Xl[4][1]&0x3333333333333333)<<2)^((Xh[4][2]>>2)&0x3333333333333333)^((Xl[4][2]&0x3333333333333333)<<2)^((Xh[4][3]>>2)&0x3333333333333333)^((Xl[4][3]&0x3333333333333333)<<2)^((Xh[4][0]>>2)&0x3333333333333333)^((Xl[4][0]&0xffffffffffffffff)<<0)^((Xh[4][0]>>4)&0x0000000000000000);
+	TL[2]=Xl[0][2]^Xl[4][2]^((Xl[4][2]&0x3333333333333333)<<2)^((Xh[4][3]>>2)&0x3333333333333333)^((Xl[4][3]&0x3333333333333333)<<2)^((Xh[4][0]>>2)&0x3333333333333333)^((Xl[4][0]&0x3333333333333333)<<2)^((Xh[4][1]>>2)&0x3333333333333333)^((Xl[4][1]&0xffffffffffffffff)<<0)^((Xh[4][1]>>4)&0x0000000000000000);
+	TL[3]=Xl[0][3]^Xl[4][3]^((Xl[4][3]&0x3333333333333333)<<2)^((Xh[4][0]>>2)&0x3333333333333333)^((Xl[4][0]&0x3333333333333333)<<2)^((Xh[4][1]>>2)&0x3333333333333333)^((Xl[4][1]&0x3333333333333333)<<2)^((Xh[4][2]>>2)&0x3333333333333333)^((Xl[4][2]&0xffffffffffffffff)<<0)^((Xh[4][2]>>4)&0x0000000000000000);
+
+	Xh[4][0]=TH[0];
+	Xh[4][1]=TH[1];
+	Xh[4][2]=TH[2];
+	Xh[4][3]=TH[3];
+	Xl[4][0]=TL[0];
+	Xl[4][1]=TL[1];
+	Xl[4][2]=TL[2];
+	Xl[4][3]=TL[3];
+}
+void ROUND_BS1(unsigned __int64 Xh[5][4],unsigned __int64 Xl[5][4],unsigned __int64 RKH[4],unsigned __int64 RKL[4])	
+{
+	unsigned __int64 TH[4];
+	unsigned __int64 TL[4];
+	Xh[0][0]=Xh[2][0]^Xh[3][0]^Xh[4][0]^RKH[0];
+	Xh[0][1]=Xh[2][1]^Xh[3][1]^Xh[4][1]^RKH[1];
+	Xh[0][2]=Xh[2][2]^Xh[3][2]^Xh[4][2]^RKH[2];
+	Xh[0][3]=Xh[2][3]^Xh[3][3]^Xh[4][3]^RKH[3];
+	Xl[0][0]=Xl[2][0]^Xl[3][0]^Xl[4][0]^RKL[0];
+	Xl[0][1]=Xl[2][1]^Xl[3][1]^Xl[4][1]^RKL[1];
+	Xl[0][2]=Xl[2][2]^Xl[3][2]^Xl[4][2]^RKL[2];
+	Xl[0][3]=Xl[2][3]^Xl[3][3]^Xl[4][3]^RKL[3];
+
+
+	S_BOX_bs(Xh[0][0],Xl[0][0],Xh[0][0],Xl[0][0]);
+	S_BOX_bs(Xh[0][1],Xl[0][1],Xh[0][1],Xl[0][1]);
+	S_BOX_bs(Xh[0][2],Xl[0][2],Xh[0][2],Xl[0][2]);
+	S_BOX_bs(Xh[0][3],Xl[0][3],Xh[0][3],Xl[0][3]);
+
+	TH[0]=Xh[1][0]^Xh[0][0]^((Xh[0][0]&0x3333333333333333)<<2)^((Xl[0][0]>>2)&0x3333333333333333)^((Xh[0][1]&0x3333333333333333)<<2)^((Xl[0][1]>>2)&0x3333333333333333)^((Xh[0][2]&0x3333333333333333)<<2)^((Xl[0][2]>>2)&0x3333333333333333)^((Xh[0][3]&0xffffffffffffffff)<<0)^((Xl[0][3]>>4)&0x0000000000000000);
+	TH[1]=Xh[1][1]^Xh[0][1]^((Xh[0][1]&0x3333333333333333)<<2)^((Xl[0][1]>>2)&0x3333333333333333)^((Xh[0][2]&0x3333333333333333)<<2)^((Xl[0][2]>>2)&0x3333333333333333)^((Xh[0][3]&0x3333333333333333)<<2)^((Xl[0][3]>>2)&0x3333333333333333)^((Xh[0][0]&0xffffffffffffffff)<<0)^((Xl[0][0]>>4)&0x0000000000000000);
+	TH[2]=Xh[1][2]^Xh[0][2]^((Xh[0][2]&0x3333333333333333)<<2)^((Xl[0][2]>>2)&0x3333333333333333)^((Xh[0][3]&0x3333333333333333)<<2)^((Xl[0][3]>>2)&0x3333333333333333)^((Xh[0][0]&0x3333333333333333)<<2)^((Xl[0][0]>>2)&0x3333333333333333)^((Xh[0][1]&0xffffffffffffffff)<<0)^((Xl[0][1]>>4)&0x0000000000000000);
+	TH[3]=Xh[1][3]^Xh[0][3]^((Xh[0][3]&0x3333333333333333)<<2)^((Xl[0][3]>>2)&0x3333333333333333)^((Xh[0][0]&0x3333333333333333)<<2)^((Xl[0][0]>>2)&0x3333333333333333)^((Xh[0][1]&0x3333333333333333)<<2)^((Xl[0][1]>>2)&0x3333333333333333)^((Xh[0][2]&0xffffffffffffffff)<<0)^((Xl[0][2]>>4)&0x0000000000000000);
+	TL[0]=Xl[1][0]^Xl[0][0]^((Xl[0][0]&0x3333333333333333)<<2)^((Xh[0][1]>>2)&0x3333333333333333)^((Xl[0][1]&0x3333333333333333)<<2)^((Xh[0][2]>>2)&0x3333333333333333)^((Xl[0][2]&0x3333333333333333)<<2)^((Xh[0][3]>>2)&0x3333333333333333)^((Xl[0][3]&0xffffffffffffffff)<<0)^((Xh[0][3]>>4)&0x0000000000000000);
+	TL[1]=Xl[1][1]^Xl[0][1]^((Xl[0][1]&0x3333333333333333)<<2)^((Xh[0][2]>>2)&0x3333333333333333)^((Xl[0][2]&0x3333333333333333)<<2)^((Xh[0][3]>>2)&0x3333333333333333)^((Xl[0][3]&0x3333333333333333)<<2)^((Xh[0][0]>>2)&0x3333333333333333)^((Xl[0][0]&0xffffffffffffffff)<<0)^((Xh[0][0]>>4)&0x0000000000000000);
+	TL[2]=Xl[1][2]^Xl[0][2]^((Xl[0][2]&0x3333333333333333)<<2)^((Xh[0][3]>>2)&0x3333333333333333)^((Xl[0][3]&0x3333333333333333)<<2)^((Xh[0][0]>>2)&0x3333333333333333)^((Xl[0][0]&0x3333333333333333)<<2)^((Xh[0][1]>>2)&0x3333333333333333)^((Xl[0][1]&0xffffffffffffffff)<<0)^((Xh[0][1]>>4)&0x0000000000000000);
+	TL[3]=Xl[1][3]^Xl[0][3]^((Xl[0][3]&0x3333333333333333)<<2)^((Xh[0][0]>>2)&0x3333333333333333)^((Xl[0][0]&0x3333333333333333)<<2)^((Xh[0][1]>>2)&0x3333333333333333)^((Xl[0][1]&0x3333333333333333)<<2)^((Xh[0][2]>>2)&0x3333333333333333)^((Xl[0][2]&0xffffffffffffffff)<<0)^((Xh[0][2]>>4)&0x0000000000000000);
+
+	Xh[0][0]=TH[0];
+	Xh[0][1]=TH[1];
+	Xh[0][2]=TH[2];
+	Xh[0][3]=TH[3];
+	Xl[0][0]=TL[0];
+	Xl[0][1]=TL[1];
+	Xl[0][2]=TL[2];
+	Xl[0][3]=TL[3];
+
+}
+void ROUND_BS2(unsigned __int64 Xh[5][4],unsigned __int64 Xl[5][4],unsigned __int64 RKH[4],unsigned __int64 RKL[4])	
+{
+	unsigned __int64 TH[4];
+	unsigned __int64 TL[4];
+	Xh[1][0]=Xh[3][0]^Xh[4][0]^Xh[0][0]^RKH[0];
+	Xh[1][1]=Xh[3][1]^Xh[4][1]^Xh[0][1]^RKH[1];
+	Xh[1][2]=Xh[3][2]^Xh[4][2]^Xh[0][2]^RKH[2];
+	Xh[1][3]=Xh[3][3]^Xh[4][3]^Xh[0][3]^RKH[3];
+	Xl[1][0]=Xl[3][0]^Xl[4][0]^Xl[0][0]^RKL[0];
+	Xl[1][1]=Xl[3][1]^Xl[4][1]^Xl[0][1]^RKL[1];
+	Xl[1][2]=Xl[3][2]^Xl[4][2]^Xl[0][2]^RKL[2];
+	Xl[1][3]=Xl[3][3]^Xl[4][3]^Xl[0][3]^RKL[3];
+
+	S_BOX_bs(Xh[1][0],Xl[1][0],Xh[1][0],Xl[1][0]);
+	S_BOX_bs(Xh[1][1],Xl[1][1],Xh[1][1],Xl[1][1]);
+	S_BOX_bs(Xh[1][2],Xl[1][2],Xh[1][2],Xl[1][2]);
+	S_BOX_bs(Xh[1][3],Xl[1][3],Xh[1][3],Xl[1][3]);
+
+	TH[0]=Xh[2][0]^Xh[1][0]^((Xh[1][0]&0x3333333333333333)<<2)^((Xl[1][0]>>2)&0x3333333333333333)^((Xh[1][1]&0x3333333333333333)<<2)^((Xl[1][1]>>2)&0x3333333333333333)^((Xh[1][2]&0x3333333333333333)<<2)^((Xl[1][2]>>2)&0x3333333333333333)^((Xh[1][3]&0xffffffffffffffff)<<0)^((Xl[1][3]>>4)&0x0000000000000000);
+	TH[1]=Xh[2][1]^Xh[1][1]^((Xh[1][1]&0x3333333333333333)<<2)^((Xl[1][1]>>2)&0x3333333333333333)^((Xh[1][2]&0x3333333333333333)<<2)^((Xl[1][2]>>2)&0x3333333333333333)^((Xh[1][3]&0x3333333333333333)<<2)^((Xl[1][3]>>2)&0x3333333333333333)^((Xh[1][0]&0xffffffffffffffff)<<0)^((Xl[1][0]>>4)&0x0000000000000000);
+	TH[2]=Xh[2][2]^Xh[1][2]^((Xh[1][2]&0x3333333333333333)<<2)^((Xl[1][2]>>2)&0x3333333333333333)^((Xh[1][3]&0x3333333333333333)<<2)^((Xl[1][3]>>2)&0x3333333333333333)^((Xh[1][0]&0x3333333333333333)<<2)^((Xl[1][0]>>2)&0x3333333333333333)^((Xh[1][1]&0xffffffffffffffff)<<0)^((Xl[1][1]>>4)&0x0000000000000000);
+	TH[3]=Xh[2][3]^Xh[1][3]^((Xh[1][3]&0x3333333333333333)<<2)^((Xl[1][3]>>2)&0x3333333333333333)^((Xh[1][0]&0x3333333333333333)<<2)^((Xl[1][0]>>2)&0x3333333333333333)^((Xh[1][1]&0x3333333333333333)<<2)^((Xl[1][1]>>2)&0x3333333333333333)^((Xh[1][2]&0xffffffffffffffff)<<0)^((Xl[1][2]>>4)&0x0000000000000000);
+	TL[0]=Xl[2][0]^Xl[1][0]^((Xl[1][0]&0x3333333333333333)<<2)^((Xh[1][1]>>2)&0x3333333333333333)^((Xl[1][1]&0x3333333333333333)<<2)^((Xh[1][2]>>2)&0x3333333333333333)^((Xl[1][2]&0x3333333333333333)<<2)^((Xh[1][3]>>2)&0x3333333333333333)^((Xl[1][3]&0xffffffffffffffff)<<0)^((Xh[1][3]>>4)&0x0000000000000000);
+	TL[1]=Xl[2][1]^Xl[1][1]^((Xl[1][1]&0x3333333333333333)<<2)^((Xh[1][2]>>2)&0x3333333333333333)^((Xl[1][2]&0x3333333333333333)<<2)^((Xh[1][3]>>2)&0x3333333333333333)^((Xl[1][3]&0x3333333333333333)<<2)^((Xh[1][0]>>2)&0x3333333333333333)^((Xl[1][0]&0xffffffffffffffff)<<0)^((Xh[1][0]>>4)&0x0000000000000000);
+	TL[2]=Xl[2][2]^Xl[1][2]^((Xl[1][2]&0x3333333333333333)<<2)^((Xh[1][3]>>2)&0x3333333333333333)^((Xl[1][3]&0x3333333333333333)<<2)^((Xh[1][0]>>2)&0x3333333333333333)^((Xl[1][0]&0x3333333333333333)<<2)^((Xh[1][1]>>2)&0x3333333333333333)^((Xl[1][1]&0xffffffffffffffff)<<0)^((Xh[1][1]>>4)&0x0000000000000000);
+	TL[3]=Xl[2][3]^Xl[1][3]^((Xl[1][3]&0x3333333333333333)<<2)^((Xh[1][0]>>2)&0x3333333333333333)^((Xl[1][0]&0x3333333333333333)<<2)^((Xh[1][1]>>2)&0x3333333333333333)^((Xl[1][1]&0x3333333333333333)<<2)^((Xh[1][2]>>2)&0x3333333333333333)^((Xl[1][2]&0xffffffffffffffff)<<0)^((Xh[1][2]>>4)&0x0000000000000000);
+
+	Xh[1][0]=TH[0];
+	Xh[1][1]=TH[1];
+	Xh[1][2]=TH[2];
+	Xh[1][3]=TH[3];
+	Xl[1][0]=TL[0];
+	Xl[1][1]=TL[1];
+	Xl[1][2]=TL[2];
+	Xl[1][3]=TL[3];
+
+}
+void ROUND_BS3(unsigned __int64 Xh[5][4],unsigned __int64 Xl[5][4],unsigned __int64 RKH[4],unsigned __int64 RKL[4])	
+{
+	unsigned __int64 TH[4];
+	unsigned __int64 TL[4];
+	Xh[2][0]=Xh[4][0]^Xh[0][0]^Xh[1][0]^RKH[0];
+	Xh[2][1]=Xh[4][1]^Xh[0][1]^Xh[1][1]^RKH[1];
+	Xh[2][2]=Xh[4][2]^Xh[0][2]^Xh[1][2]^RKH[2];
+	Xh[2][3]=Xh[4][3]^Xh[0][3]^Xh[1][3]^RKH[3];
+	Xl[2][0]=Xl[4][0]^Xl[0][0]^Xl[1][0]^RKL[0];
+	Xl[2][1]=Xl[4][1]^Xl[0][1]^Xl[1][1]^RKL[1];
+	Xl[2][2]=Xl[4][2]^Xl[0][2]^Xl[1][2]^RKL[2];
+	Xl[2][3]=Xl[4][3]^Xl[0][3]^Xl[1][3]^RKL[3];
+
+	S_BOX_bs(Xh[2][0],Xl[2][0],Xh[2][0],Xl[2][0]);
+	S_BOX_bs(Xh[2][1],Xl[2][1],Xh[2][1],Xl[2][1]);
+	S_BOX_bs(Xh[2][2],Xl[2][2],Xh[2][2],Xl[2][2]);
+	S_BOX_bs(Xh[2][3],Xl[2][3],Xh[2][3],Xl[2][3]);
+
+	TH[0]=Xh[3][0]^Xh[2][0]^((Xh[2][0]&0x3333333333333333)<<2)^((Xl[2][0]>>2)&0x3333333333333333)^((Xh[2][1]&0x3333333333333333)<<2)^((Xl[2][1]>>2)&0x3333333333333333)^((Xh[2][2]&0x3333333333333333)<<2)^((Xl[2][2]>>2)&0x3333333333333333)^((Xh[2][3]&0xffffffffffffffff)<<0)^((Xl[2][3]>>4)&0x0000000000000000);
+	TH[1]=Xh[3][1]^Xh[2][1]^((Xh[2][1]&0x3333333333333333)<<2)^((Xl[2][1]>>2)&0x3333333333333333)^((Xh[2][2]&0x3333333333333333)<<2)^((Xl[2][2]>>2)&0x3333333333333333)^((Xh[2][3]&0x3333333333333333)<<2)^((Xl[2][3]>>2)&0x3333333333333333)^((Xh[2][0]&0xffffffffffffffff)<<0)^((Xl[2][0]>>4)&0x0000000000000000);
+	TH[2]=Xh[3][2]^Xh[2][2]^((Xh[2][2]&0x3333333333333333)<<2)^((Xl[2][2]>>2)&0x3333333333333333)^((Xh[2][3]&0x3333333333333333)<<2)^((Xl[2][3]>>2)&0x3333333333333333)^((Xh[2][0]&0x3333333333333333)<<2)^((Xl[2][0]>>2)&0x3333333333333333)^((Xh[2][1]&0xffffffffffffffff)<<0)^((Xl[2][1]>>4)&0x0000000000000000);
+	TH[3]=Xh[3][3]^Xh[2][3]^((Xh[2][3]&0x3333333333333333)<<2)^((Xl[2][3]>>2)&0x3333333333333333)^((Xh[2][0]&0x3333333333333333)<<2)^((Xl[2][0]>>2)&0x3333333333333333)^((Xh[2][1]&0x3333333333333333)<<2)^((Xl[2][1]>>2)&0x3333333333333333)^((Xh[2][2]&0xffffffffffffffff)<<0)^((Xl[2][2]>>4)&0x0000000000000000);
+	TL[0]=Xl[3][0]^Xl[2][0]^((Xl[2][0]&0x3333333333333333)<<2)^((Xh[2][1]>>2)&0x3333333333333333)^((Xl[2][1]&0x3333333333333333)<<2)^((Xh[2][2]>>2)&0x3333333333333333)^((Xl[2][2]&0x3333333333333333)<<2)^((Xh[2][3]>>2)&0x3333333333333333)^((Xl[2][3]&0xffffffffffffffff)<<0)^((Xh[2][3]>>4)&0x0000000000000000);
+	TL[1]=Xl[3][1]^Xl[2][1]^((Xl[2][1]&0x3333333333333333)<<2)^((Xh[2][2]>>2)&0x3333333333333333)^((Xl[2][2]&0x3333333333333333)<<2)^((Xh[2][3]>>2)&0x3333333333333333)^((Xl[2][3]&0x3333333333333333)<<2)^((Xh[2][0]>>2)&0x3333333333333333)^((Xl[2][0]&0xffffffffffffffff)<<0)^((Xh[2][0]>>4)&0x0000000000000000);
+	TL[2]=Xl[3][2]^Xl[2][2]^((Xl[2][2]&0x3333333333333333)<<2)^((Xh[2][3]>>2)&0x3333333333333333)^((Xl[2][3]&0x3333333333333333)<<2)^((Xh[2][0]>>2)&0x3333333333333333)^((Xl[2][0]&0x3333333333333333)<<2)^((Xh[2][1]>>2)&0x3333333333333333)^((Xl[2][1]&0xffffffffffffffff)<<0)^((Xh[2][1]>>4)&0x0000000000000000);
+	TL[3]=Xl[3][3]^Xl[2][3]^((Xl[2][3]&0x3333333333333333)<<2)^((Xh[2][0]>>2)&0x3333333333333333)^((Xl[2][0]&0x3333333333333333)<<2)^((Xh[2][1]>>2)&0x3333333333333333)^((Xl[2][1]&0x3333333333333333)<<2)^((Xh[2][2]>>2)&0x3333333333333333)^((Xl[2][2]&0xffffffffffffffff)<<0)^((Xh[2][2]>>4)&0x0000000000000000);
+
+	Xh[2][0]=TH[0];
+	Xh[2][1]=TH[1];
+	Xh[2][2]=TH[2];
+	Xh[2][3]=TH[3];
+	Xl[2][0]=TL[0];
+	Xl[2][1]=TL[1];
+	Xl[2][2]=TL[2];
+	Xl[2][3]=TL[3];
+
+}
+void ROUND_BS4(unsigned __int64 Xh[5][4],unsigned __int64 Xl[5][4],unsigned __int64 RKH[4],unsigned __int64 RKL[4])	
+{
+	unsigned __int64 TH[4];
+	unsigned __int64 TL[4];
+	Xh[3][0]=Xh[0][0]^Xh[1][0]^Xh[2][0]^RKH[0];
+	Xh[3][1]=Xh[0][1]^Xh[1][1]^Xh[2][1]^RKH[1];
+	Xh[3][2]=Xh[0][2]^Xh[1][2]^Xh[2][2]^RKH[2];
+	Xh[3][3]=Xh[0][3]^Xh[1][3]^Xh[2][3]^RKH[3];
+	Xl[3][0]=Xl[0][0]^Xl[1][0]^Xl[2][0]^RKL[0];
+	Xl[3][1]=Xl[0][1]^Xl[1][1]^Xl[2][1]^RKL[1];
+	Xl[3][2]=Xl[0][2]^Xl[1][2]^Xl[2][2]^RKL[2];
+	Xl[3][3]=Xl[0][3]^Xl[1][3]^Xl[2][3]^RKL[3];
+
+	S_BOX_bs(Xh[3][0],Xl[3][0],Xh[3][0],Xl[3][0]);
+	S_BOX_bs(Xh[3][1],Xl[3][1],Xh[3][1],Xl[3][1]);
+	S_BOX_bs(Xh[3][2],Xl[3][2],Xh[3][2],Xl[3][2]);
+	S_BOX_bs(Xh[3][3],Xl[3][3],Xh[3][3],Xl[3][3]);
+
+	TH[0]=Xh[4][0]^Xh[3][0]^((Xh[3][0]&0x3333333333333333)<<2)^((Xl[3][0]>>2)&0x3333333333333333)^((Xh[3][1]&0x3333333333333333)<<2)^((Xl[3][1]>>2)&0x3333333333333333)^((Xh[3][2]&0x3333333333333333)<<2)^((Xl[3][2]>>2)&0x3333333333333333)^((Xh[3][3]&0xffffffffffffffff)<<0)^((Xl[3][3]>>4)&0x0000000000000000);
+	TH[1]=Xh[4][1]^Xh[3][1]^((Xh[3][1]&0x3333333333333333)<<2)^((Xl[3][1]>>2)&0x3333333333333333)^((Xh[3][2]&0x3333333333333333)<<2)^((Xl[3][2]>>2)&0x3333333333333333)^((Xh[3][3]&0x3333333333333333)<<2)^((Xl[3][3]>>2)&0x3333333333333333)^((Xh[3][0]&0xffffffffffffffff)<<0)^((Xl[3][0]>>4)&0x0000000000000000);
+	TH[2]=Xh[4][2]^Xh[3][2]^((Xh[3][2]&0x3333333333333333)<<2)^((Xl[3][2]>>2)&0x3333333333333333)^((Xh[3][3]&0x3333333333333333)<<2)^((Xl[3][3]>>2)&0x3333333333333333)^((Xh[3][0]&0x3333333333333333)<<2)^((Xl[3][0]>>2)&0x3333333333333333)^((Xh[3][1]&0xffffffffffffffff)<<0)^((Xl[3][1]>>4)&0x0000000000000000);
+	TH[3]=Xh[4][3]^Xh[3][3]^((Xh[3][3]&0x3333333333333333)<<2)^((Xl[3][3]>>2)&0x3333333333333333)^((Xh[3][0]&0x3333333333333333)<<2)^((Xl[3][0]>>2)&0x3333333333333333)^((Xh[3][1]&0x3333333333333333)<<2)^((Xl[3][1]>>2)&0x3333333333333333)^((Xh[3][2]&0xffffffffffffffff)<<0)^((Xl[3][2]>>4)&0x0000000000000000);
+	TL[0]=Xl[4][0]^Xl[3][0]^((Xl[3][0]&0x3333333333333333)<<2)^((Xh[3][1]>>2)&0x3333333333333333)^((Xl[3][1]&0x3333333333333333)<<2)^((Xh[3][2]>>2)&0x3333333333333333)^((Xl[3][2]&0x3333333333333333)<<2)^((Xh[3][3]>>2)&0x3333333333333333)^((Xl[3][3]&0xffffffffffffffff)<<0)^((Xh[3][3]>>4)&0x0000000000000000);
+	TL[1]=Xl[4][1]^Xl[3][1]^((Xl[3][1]&0x3333333333333333)<<2)^((Xh[3][2]>>2)&0x3333333333333333)^((Xl[3][2]&0x3333333333333333)<<2)^((Xh[3][3]>>2)&0x3333333333333333)^((Xl[3][3]&0x3333333333333333)<<2)^((Xh[3][0]>>2)&0x3333333333333333)^((Xl[3][0]&0xffffffffffffffff)<<0)^((Xh[3][0]>>4)&0x0000000000000000);
+	TL[2]=Xl[4][2]^Xl[3][2]^((Xl[3][2]&0x3333333333333333)<<2)^((Xh[3][3]>>2)&0x3333333333333333)^((Xl[3][3]&0x3333333333333333)<<2)^((Xh[3][0]>>2)&0x3333333333333333)^((Xl[3][0]&0x3333333333333333)<<2)^((Xh[3][1]>>2)&0x3333333333333333)^((Xl[3][1]&0xffffffffffffffff)<<0)^((Xh[3][1]>>4)&0x0000000000000000);
+	TL[3]=Xl[4][3]^Xl[3][3]^((Xl[3][3]&0x3333333333333333)<<2)^((Xh[3][0]>>2)&0x3333333333333333)^((Xl[3][0]&0x3333333333333333)<<2)^((Xh[3][1]>>2)&0x3333333333333333)^((Xl[3][1]&0x3333333333333333)<<2)^((Xh[3][2]>>2)&0x3333333333333333)^((Xl[3][2]&0xffffffffffffffff)<<0)^((Xh[3][2]>>4)&0x0000000000000000);
+
+	Xh[3][0]=TH[0];
+	Xh[3][1]=TH[1];
+	Xh[3][2]=TH[2];
+	Xh[3][3]=TH[3];
+	Xl[3][0]=TL[0];
+	Xl[3][1]=TL[1];
+	Xl[3][2]=TL[2];
+	Xl[3][3]=TL[3];
+
+}
+
+
+void print_bit(unsigned __int64 x)
+{
+	__int64 i;
+	for(i=0;i<32;i++){printf("%d",(x>>(31-i))&1);if(i%4==3)printf(" ");}printf("\n");
+}
+
+void sms4_set_encrypt_key_bs(sms4_key_t_bs *key,unsigned __int64 *user_keyh,unsigned __int64 *user_keyl)
+{
+	unsigned __int64 Xh[5][4],Xl[5][4],ckh[4],ckl[4];
+	unsigned __int64 *RKh,*RKl;
+
+	RKh=&(key->rkh[0][0]);
+	RKl=&(key->rkl[0][0]);
+
+	Xh[0][0]=user_keyh[0]^FK_BS00h;
+	Xh[0][1]=user_keyh[1]^FK_BS01h;
+	Xh[0][2]=user_keyh[2]^FK_BS02h;
+	Xh[0][3]=user_keyh[3]^FK_BS03h;
+	Xl[0][0]=user_keyl[0]^FK_BS00l;
+	Xl[0][1]=user_keyl[1]^FK_BS01l;
+	Xl[0][2]=user_keyl[2]^FK_BS02l;
+	Xl[0][3]=user_keyl[3]^FK_BS03l;
+
+	Xh[1][0]=user_keyh[4]^FK_BS10h;
+	Xh[1][1]=user_keyh[5]^FK_BS11h;
+	Xh[1][2]=user_keyh[6]^FK_BS12h;
+	Xh[1][3]=user_keyh[7]^FK_BS13h;
+	Xl[1][0]=user_keyl[4]^FK_BS10l;
+	Xl[1][1]=user_keyl[5]^FK_BS11l;
+	Xl[1][2]=user_keyl[6]^FK_BS12l;
+	Xl[1][3]=user_keyl[7]^FK_BS13l;
+
+	Xh[2][0]=user_keyh[8 ]^FK_BS20h;
+	Xh[2][1]=user_keyh[9 ]^FK_BS21h;
+	Xh[2][2]=user_keyh[10]^FK_BS22h;
+	Xh[2][3]=user_keyh[11]^FK_BS23h;
+	Xl[2][0]=user_keyl[8 ]^FK_BS20l;
+	Xl[2][1]=user_keyl[9 ]^FK_BS21l;
+	Xl[2][2]=user_keyl[10]^FK_BS22l;
+	Xl[2][3]=user_keyl[11]^FK_BS23l;
+
+	Xh[3][0]=user_keyh[12]^FK_BS30h;
+	Xh[3][1]=user_keyh[13]^FK_BS31h;
+	Xh[3][2]=user_keyh[14]^FK_BS32h;
+	Xh[3][3]=user_keyh[15]^FK_BS33h;
+	Xl[3][0]=user_keyl[12]^FK_BS30l;
+	Xl[3][1]=user_keyl[13]^FK_BS31l;
+	Xl[3][2]=user_keyl[14]^FK_BS32l;
+	Xl[3][3]=user_keyl[15]^FK_BS33l;
+
+	ENC_ROUND_BS0(Xh,Xl,RKh,RKl,0,0);
+	RKh+=4;RKl+=4;
+	ENC_ROUND_BS1(Xh,Xl,RKh,RKl,0,1);
+	RKh+=4;RKl+=4;
+	ENC_ROUND_BS2(Xh,Xl,RKh,RKl,0,2);
+	RKh+=4;RKl+=4;
+	ENC_ROUND_BS3(Xh,Xl,RKh,RKl,0,3);
+	RKh+=4;RKl+=4;
+	ENC_ROUND_BS4(Xh,Xl,RKh,RKl,0,4);
+	RKh+=4;RKl+=4;
+	ENC_ROUND_BS0(Xh,Xl,RKh,RKl,0,5);
+	RKh+=4;RKl+=4;
+	ENC_ROUND_BS1(Xh,Xl,RKh,RKl,0,6);
+	RKh+=4;RKl+=4;
+	ENC_ROUND_BS2(Xh,Xl,RKh,RKl,0,7);
+	RKh+=4;RKl+=4;
+	ENC_ROUND_BS3(Xh,Xl,RKh,RKl,0,8);
+	RKh+=4;RKl+=4;
+	ENC_ROUND_BS4(Xh,Xl,RKh,RKl,0,9);
+	RKh+=4;RKl+=4;
+	ENC_ROUND_BS0(Xh,Xl,RKh,RKl,0,10);
+	RKh+=4;RKl+=4;
+	ENC_ROUND_BS1(Xh,Xl,RKh,RKl,0,11);
+	RKh+=4;RKl+=4;
+	ENC_ROUND_BS2(Xh,Xl,RKh,RKl,0,12);
+	RKh+=4;RKl+=4;
+	ENC_ROUND_BS3(Xh,Xl,RKh,RKl,0,13);
+	RKh+=4;RKl+=4;
+	ENC_ROUND_BS4(Xh,Xl,RKh,RKl,0,14);
+	RKh+=4;RKl+=4;
+	ENC_ROUND_BS0(Xh,Xl,RKh,RKl,0,15);
+	RKh+=4;RKl+=4;
+	ENC_ROUND_BS1(Xh,Xl,RKh,RKl,1,0);
+	RKh+=4;RKl+=4;
+	ENC_ROUND_BS2(Xh,Xl,RKh,RKl,1,1);
+	RKh+=4;RKl+=4;
+	ENC_ROUND_BS3(Xh,Xl,RKh,RKl,1,2);
+	RKh+=4;RKl+=4;
+	ENC_ROUND_BS4(Xh,Xl,RKh,RKl,1,3);
+	RKh+=4;RKl+=4;
+	ENC_ROUND_BS0(Xh,Xl,RKh,RKl,1,4);
+	RKh+=4;RKl+=4;
+	ENC_ROUND_BS1(Xh,Xl,RKh,RKl,1,5);
+	RKh+=4;RKl+=4;
+	ENC_ROUND_BS2(Xh,Xl,RKh,RKl,1,6);
+	RKh+=4;RKl+=4;
+	ENC_ROUND_BS3(Xh,Xl,RKh,RKl,1,7);
+	RKh+=4;RKl+=4;
+	ENC_ROUND_BS4(Xh,Xl,RKh,RKl,1,8);	
+	RKh+=4;RKl+=4;
+	ENC_ROUND_BS0(Xh,Xl,RKh,RKl,1,9);
+	RKh+=4;RKl+=4;
+	ENC_ROUND_BS1(Xh,Xl,RKh,RKl,1,10);
+	RKh+=4;RKl+=4;
+	ENC_ROUND_BS2(Xh,Xl,RKh,RKl,1,11);
+	RKh+=4;RKl+=4;
+	ENC_ROUND_BS3(Xh,Xl,RKh,RKl,1,12);
+	RKh+=4;RKl+=4;
+	ENC_ROUND_BS4(Xh,Xl,RKh,RKl,1,13);	
+	RKh+=4;RKl+=4;
+	ENC_ROUND_BS0(Xh,Xl,RKh,RKl,1,14);
+	RKh+=4;RKl+=4;
+	ENC_ROUND_BS1(Xh,Xl,RKh,RKl,1,15);
+
+}
+void SMS4_encrypt_bs(sms4_key_t_bs *key,unsigned __int64 *INh,unsigned __int64 *INl,unsigned __int64 *OUTh,unsigned __int64 *OUTl)
+{
+	unsigned __int64 Xh[5][4],Xl[5][4],ckh[4],ckl[4];
+	unsigned __int64 *RKh,*RKl;
+
+	RKh=&(key->rkh[0][0]);
+	RKl=&(key->rkl[0][0]);
+
+	Xh[0][0]=INh[0];
+	Xh[0][1]=INh[1];
+	Xh[0][2]=INh[2];
+	Xh[0][3]=INh[3];
+	Xl[0][0]=INl[0];
+	Xl[0][1]=INl[1];
+	Xl[0][2]=INl[2];
+	Xl[0][3]=INl[3];
+
+	Xh[1][0]=INh[4];
+	Xh[1][1]=INh[5];
+	Xh[1][2]=INh[6];
+	Xh[1][3]=INh[7];
+	Xl[1][0]=INl[4];
+	Xl[1][1]=INl[5];
+	Xl[1][2]=INl[6];
+	Xl[1][3]=INl[7];
+
+	Xh[2][0]=INh[8 ];
+	Xh[2][1]=INh[9 ];
+	Xh[2][2]=INh[10];
+	Xh[2][3]=INh[11];
+	Xl[2][0]=INl[8 ];
+	Xl[2][1]=INl[9 ];
+	Xl[2][2]=INl[10];
+	Xl[2][3]=INl[11];
+
+	Xh[3][0]=INh[12];
+	Xh[3][1]=INh[13];
+	Xh[3][2]=INh[14];
+	Xh[3][3]=INh[15];
+	Xl[3][0]=INl[12];
+	Xl[3][1]=INl[13];
+	Xl[3][2]=INl[14];
+	Xl[3][3]=INl[15];
+
+	ROUND_BS0(Xh,Xl,RKh,RKl);
+	RKh+=4;RKl+=4;
+	ROUND_BS1(Xh,Xl,RKh,RKl);
+	RKh+=4;RKl+=4;
+	ROUND_BS2(Xh,Xl,RKh,RKl);
+	RKh+=4;RKl+=4;
+	ROUND_BS3(Xh,Xl,RKh,RKl);
+	RKh+=4;RKl+=4;
+	ROUND_BS4(Xh,Xl,RKh,RKl);
+	RKh+=4;RKl+=4;
+	ROUND_BS0(Xh,Xl,RKh,RKl);
+	RKh+=4;RKl+=4;
+	ROUND_BS1(Xh,Xl,RKh,RKl);
+	RKh+=4;RKl+=4;
+	ROUND_BS2(Xh,Xl,RKh,RKl);
+	RKh+=4;RKl+=4;
+	ROUND_BS3(Xh,Xl,RKh,RKl);
+	RKh+=4;RKl+=4;
+	ROUND_BS4(Xh,Xl,RKh,RKl);
+	RKh+=4;RKl+=4;
+	ROUND_BS0(Xh,Xl,RKh,RKl);
+	RKh+=4;RKl+=4;
+	ROUND_BS1(Xh,Xl,RKh,RKl);
+	RKh+=4;RKl+=4;
+	ROUND_BS2(Xh,Xl,RKh,RKl);
+	RKh+=4;RKl+=4;
+	ROUND_BS3(Xh,Xl,RKh,RKl);
+	RKh+=4;RKl+=4;
+	ROUND_BS4(Xh,Xl,RKh,RKl);
+	RKh+=4;RKl+=4;
+	ROUND_BS0(Xh,Xl,RKh,RKl);
+	RKh+=4;RKl+=4;
+	ROUND_BS1(Xh,Xl,RKh,RKl);
+	RKh+=4;RKl+=4;
+	ROUND_BS2(Xh,Xl,RKh,RKl);
+	RKh+=4;RKl+=4;
+	ROUND_BS3(Xh,Xl,RKh,RKl);
+	RKh+=4;RKl+=4;
+	ROUND_BS4(Xh,Xl,RKh,RKl);
+	RKh+=4;RKl+=4;
+	ROUND_BS0(Xh,Xl,RKh,RKl);
+	RKh+=4;RKl+=4;
+	ROUND_BS1(Xh,Xl,RKh,RKl);
+	RKh+=4;RKl+=4;
+	ROUND_BS2(Xh,Xl,RKh,RKl);
+	RKh+=4;RKl+=4;
+	ROUND_BS3(Xh,Xl,RKh,RKl);
+	RKh+=4;RKl+=4;
+	ROUND_BS4(Xh,Xl,RKh,RKl);	
+	RKh+=4;RKl+=4;
+	ROUND_BS0(Xh,Xl,RKh,RKl);
+	RKh+=4;RKl+=4;
+	ROUND_BS1(Xh,Xl,RKh,RKl);
+	RKh+=4;RKl+=4;
+	ROUND_BS2(Xh,Xl,RKh,RKl);
+	RKh+=4;RKl+=4;
+	ROUND_BS3(Xh,Xl,RKh,RKl);
+	RKh+=4;RKl+=4;
+	ROUND_BS4(Xh,Xl,RKh,RKl);	
+	RKh+=4;RKl+=4;
+	ROUND_BS0(Xh,Xl,RKh,RKl);
+	RKh+=4;RKl+=4;
+	ROUND_BS1(Xh,Xl,RKh,RKl);
+
+	OUTh[0]=Xh[0][0];
+	OUTh[1]=Xh[0][1];
+	OUTh[2]=Xh[0][2];
+	OUTh[3]=Xh[0][3];
+	OUTl[0]=Xl[0][0];
+	OUTl[1]=Xl[0][1];
+	OUTl[2]=Xl[0][2];
+	OUTl[3]=Xl[0][3];
+               
+	OUTh[4]=Xh[4][0];
+	OUTh[5]=Xh[4][1];
+	OUTh[6]=Xh[4][2];
+	OUTh[7]=Xh[4][3];
+	OUTl[4]=Xl[4][0];
+	OUTl[5]=Xl[4][1];
+	OUTl[6]=Xl[4][2];
+	OUTl[7]=Xl[4][3];
+
+	OUTh[8 ]=Xh[3][0];
+	OUTh[9 ]=Xh[3][1];
+	OUTh[10]=Xh[3][2];
+	OUTh[11]=Xh[3][3];
+	OUTl[8 ]=Xl[3][0];
+	OUTl[9 ]=Xl[3][1];
+	OUTl[10]=Xl[3][2];
+	OUTl[11]=Xl[3][3];
+
+	OUTh[12]=Xh[2][0];
+	OUTh[13]=Xh[2][1];
+	OUTh[14]=Xh[2][2];
+	OUTh[15]=Xh[2][3];
+	OUTl[12]=Xl[2][0];
+	OUTl[13]=Xl[2][1];
+	OUTl[14]=Xl[2][2];
+	OUTl[15]=Xl[2][3];
+
+}
\ No newline at end of file
diff --git a/crypto/sms4/sm4_bs.h b/crypto/sms4/sm4_bs.h
new file mode 100644
index 000000000..58e24c799
--- /dev/null
+++ b/crypto/sms4/sm4_bs.h
@@ -0,0 +1,55 @@
+
+#define SMS4_NUM_ROUNDS_BS		32
+typedef struct 
+{
+	unsigned __int64 rkh[SMS4_NUM_ROUNDS_BS][4];
+	unsigned __int64 rkl[SMS4_NUM_ROUNDS_BS][4];
+} sms4_key_t_bs;
+
+
+#define FK_BS00h (0xaaaaaaaaaaaaaaaa)
+#define FK_BS00l (0x3333333333333333)
+#define FK_BS01h (0xbbbbbbbbbbbbbbbb)
+#define FK_BS01l (0x1111111111111111)
+#define FK_BS02h (0xbbbbbbbbbbbbbbbb)
+#define FK_BS02l (0xaaaaaaaaaaaaaaaa)
+#define FK_BS03h (0xcccccccccccccccc)
+#define FK_BS03l (0x6666666666666666)
+
+#define FK_BS10h (0x5555555555555555)
+#define FK_BS10l (0x6666666666666666)
+#define FK_BS11h (0xaaaaaaaaaaaaaaaa)
+#define FK_BS11l (0xaaaaaaaaaaaaaaaa)
+#define FK_BS12h (0x3333333333333333)
+#define FK_BS12l (0x3333333333333333)
+#define FK_BS13h (0x5555555555555555)
+#define FK_BS13l (0x0000000000000000)
+//0x677d9197, 0xb27022dc,
+#define FK_BS20h (0x6666666666666666)
+#define FK_BS20l (0x7777777777777777)
+#define FK_BS21h (0x7777777777777777)
+#define FK_BS21l (0xdddddddddddddddd)
+#define FK_BS22h (0x9999999999999999)
+#define FK_BS22l (0x1111111111111111)
+#define FK_BS23h (0x9999999999999999)
+#define FK_BS23l (0x7777777777777777)
+
+#define FK_BS30h (0xbbbbbbbbbbbbbbbb)
+#define FK_BS30l (0x2222222222222222)
+#define FK_BS31h (0x7777777777777777)
+#define FK_BS31l (0x0000000000000000)
+#define FK_BS32h (0x2222222222222222)
+#define FK_BS32l (0x2222222222222222)
+#define FK_BS33h (0xdddddddddddddddd)
+#define FK_BS33l (0xcccccccccccccccc)
+
+void sms4_set_encrypt_key_bs(sms4_key_t_bs *key,unsigned __int64 *user_keyh,unsigned __int64 *user_keyl);
+void SMS4_encrypt_bs(sms4_key_t_bs *key,unsigned __int64 *INh,unsigned __int64 *INl,unsigned __int64 *OUTh,unsigned __int64 *OUTl);
+
+
+
+
+
+
+
+
diff --git a/crypto/sms4/sm4_bs_test.c b/crypto/sms4/sm4_bs_test.c
new file mode 100644
index 000000000..9ae872c99
--- /dev/null
+++ b/crypto/sms4/sm4_bs_test.c
@@ -0,0 +1,29 @@
+#include 
+#include 
+#include "sm4_bs.h"
+
+unsigned __int64 ukeyh[16]={0x0000000000000000,0x2222222222222222,0x4444444444444444,0x6666666666666666,0x8888888888888888,0xAAAAAAAAAAAAAAAA,0xCCCCCCCCCCCCCCCC,0xEEEEEEEEEEEEEEEE,0xFFFFFFFFFFFFFFFF,0xDDDDDDDDDDDDDDDD,0xBBBBBBBBBBBBBBBB,0x9999999999999999,0x7777777777777777,0x5555555555555555,0x3333333333333333,0x1111111111111111};
+unsigned __int64 ukeyl[16]={0x1111111111111111,0x3333333333333333,0x5555555555555555,0x7777777777777777,0x9999999999999999,0xBBBBBBBBBBBBBBBB,0xDDDDDDDDDDDDDDDD,0xFFFFFFFFFFFFFFFF,0xEEEEEEEEEEEEEEEE,0xCCCCCCCCCCCCCCCC,0xAAAAAAAAAAAAAAAA,0x8888888888888888,0x6666666666666666,0x4444444444444444,0x2222222222222222,0x0000000000000000};
+unsigned __int64 inh[16]={0x0000000000000000,0x2222222222222222,0x4444444444444444,0x6666666666666666,0x8888888888888888,0xAAAAAAAAAAAAAAAA,0xCCCCCCCCCCCCCCCC,0xEEEEEEEEEEEEEEEE,0xFFFFFFFFFFFFFFFF,0xDDDDDDDDDDDDDDDD,0xBBBBBBBBBBBBBBBB,0x9999999999999999,0x7777777777777777,0x5555555555555555,0x3333333333333333,0x1111111111111111};
+unsigned __int64 inl[16]={0x1111111111111111,0x3333333333333333,0x5555555555555555,0x7777777777777777,0x9999999999999999,0xBBBBBBBBBBBBBBBB,0xDDDDDDDDDDDDDDDD,0xFFFFFFFFFFFFFFFF,0xEEEEEEEEEEEEEEEE,0xCCCCCCCCCCCCCCCC,0xAAAAAAAAAAAAAAAA,0x8888888888888888,0x6666666666666666,0x4444444444444444,0x2222222222222222,0x0000000000000000};
+
+void main()
+{
+	clock_t t1,t2;
+	unsigned int p; 
+	int i,j;
+	sms4_key_t_bs keybs;
+
+	printf("pai:\n");
+	scanf("%d",&p);
+	sms4_set_encrypt_key_bs(&keybs,ukeyh,ukeyl);
+	t1 = clock();
+	for(i=0;i>j*4)&0xf,(unsigned char)(inl[i]>>j*4)&0xf);}printf("\n");}
+	//68,1e,df,34,d2,06,96,5e,86,b3,e9,4f,53,6e,42,46,
+}
\ No newline at end of file
diff --git a/crypto/sms4/sms4_cbc.c b/crypto/sms4/sms4_cbc.c
new file mode 100644
index 000000000..48efa4587
--- /dev/null
+++ b/crypto/sms4/sms4_cbc.c
@@ -0,0 +1,59 @@
+/* ====================================================================
+ * Copyright (c) 2014 - 2017 The GmSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the GmSSL Project.
+ *    (http://gmssl.org/)"
+ *
+ * 4. The name "GmSSL Project" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    guanzhi1980@gmail.com.
+ *
+ * 5. Products derived from this software may not be called "GmSSL"
+ *    nor may "GmSSL" appear in their names without prior written
+ *    permission of the GmSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the GmSSL Project
+ *    (http://gmssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE GmSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE GmSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+
+#include 
+#include 
+
+void sms4_cbc_encrypt(const unsigned char *in, unsigned char *out,
+	size_t len, const sms4_key_t *key, unsigned char *iv, int enc)
+{
+	if (enc)
+		CRYPTO_cbc128_encrypt(in, out, len, key, iv, (block128_f)sms4_encrypt);
+	else	CRYPTO_cbc128_decrypt(in, out, len, key, iv, (block128_f)sms4_encrypt);
+}
diff --git a/crypto/sms4/sms4_cfb.c b/crypto/sms4/sms4_cfb.c
new file mode 100644
index 000000000..499891d2f
--- /dev/null
+++ b/crypto/sms4/sms4_cfb.c
@@ -0,0 +1,57 @@
+/* ====================================================================
+ * Copyright (c) 2014 - 2017 The GmSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the GmSSL Project.
+ *    (http://gmssl.org/)"
+ *
+ * 4. The name "GmSSL Project" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    guanzhi1980@gmail.com.
+ *
+ * 5. Products derived from this software may not be called "GmSSL"
+ *    nor may "GmSSL" appear in their names without prior written
+ *    permission of the GmSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the GmSSL Project
+ *    (http://gmssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE GmSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE GmSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+
+#include 
+#include 
+
+void sms4_cfb128_encrypt(const unsigned char *in, unsigned char *out,
+	size_t len, const sms4_key_t *key, unsigned char *iv, int *num, int enc)
+{
+	CRYPTO_cfb128_encrypt(in, out, len, key, iv, num, enc, (block128_f)sms4_encrypt);
+}
diff --git a/crypto/sms4/sms4_common.c b/crypto/sms4/sms4_common.c
new file mode 100644
index 000000000..3203ea2f8
--- /dev/null
+++ b/crypto/sms4/sms4_common.c
@@ -0,0 +1,16540 @@
+/* ====================================================================
+ * Copyright (c) 2014 - 2017 The GmSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the GmSSL Project.
+ *    (http://gmssl.org/)"
+ *
+ * 4. The name "GmSSL Project" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    guanzhi1980@gmail.com.
+ *
+ * 5. Products derived from this software may not be called "GmSSL"
+ *    nor may "GmSSL" appear in their names without prior written
+ *    permission of the GmSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the GmSSL Project
+ *    (http://gmssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE GmSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE GmSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+
+#include "sms4_lcl.h"
+
+
+const uint8_t SMS4_S[256] = {
+	0xd6, 0x90, 0xe9, 0xfe, 0xcc, 0xe1, 0x3d, 0xb7,
+	0x16, 0xb6, 0x14, 0xc2, 0x28, 0xfb, 0x2c, 0x05,
+	0x2b, 0x67, 0x9a, 0x76, 0x2a, 0xbe, 0x04, 0xc3,
+	0xaa, 0x44, 0x13, 0x26, 0x49, 0x86, 0x06, 0x99,
+	0x9c, 0x42, 0x50, 0xf4, 0x91, 0xef, 0x98, 0x7a,
+	0x33, 0x54, 0x0b, 0x43, 0xed, 0xcf, 0xac, 0x62,
+	0xe4, 0xb3, 0x1c, 0xa9, 0xc9, 0x08, 0xe8, 0x95,
+	0x80, 0xdf, 0x94, 0xfa, 0x75, 0x8f, 0x3f, 0xa6,
+	0x47, 0x07, 0xa7, 0xfc, 0xf3, 0x73, 0x17, 0xba,
+	0x83, 0x59, 0x3c, 0x19, 0xe6, 0x85, 0x4f, 0xa8,
+	0x68, 0x6b, 0x81, 0xb2, 0x71, 0x64, 0xda, 0x8b,
+	0xf8, 0xeb, 0x0f, 0x4b, 0x70, 0x56, 0x9d, 0x35,
+	0x1e, 0x24, 0x0e, 0x5e, 0x63, 0x58, 0xd1, 0xa2,
+	0x25, 0x22, 0x7c, 0x3b, 0x01, 0x21, 0x78, 0x87,
+	0xd4, 0x00, 0x46, 0x57, 0x9f, 0xd3, 0x27, 0x52,
+	0x4c, 0x36, 0x02, 0xe7, 0xa0, 0xc4, 0xc8, 0x9e,
+	0xea, 0xbf, 0x8a, 0xd2, 0x40, 0xc7, 0x38, 0xb5,
+	0xa3, 0xf7, 0xf2, 0xce, 0xf9, 0x61, 0x15, 0xa1,
+	0xe0, 0xae, 0x5d, 0xa4, 0x9b, 0x34, 0x1a, 0x55,
+	0xad, 0x93, 0x32, 0x30, 0xf5, 0x8c, 0xb1, 0xe3,
+	0x1d, 0xf6, 0xe2, 0x2e, 0x82, 0x66, 0xca, 0x60,
+	0xc0, 0x29, 0x23, 0xab, 0x0d, 0x53, 0x4e, 0x6f,
+	0xd5, 0xdb, 0x37, 0x45, 0xde, 0xfd, 0x8e, 0x2f,
+	0x03, 0xff, 0x6a, 0x72, 0x6d, 0x6c, 0x5b, 0x51,
+	0x8d, 0x1b, 0xaf, 0x92, 0xbb, 0xdd, 0xbc, 0x7f,
+	0x11, 0xd9, 0x5c, 0x41, 0x1f, 0x10, 0x5a, 0xd8,
+	0x0a, 0xc1, 0x31, 0x88, 0xa5, 0xcd, 0x7b, 0xbd,
+	0x2d, 0x74, 0xd0, 0x12, 0xb8, 0xe5, 0xb4, 0xb0,
+	0x89, 0x69, 0x97, 0x4a, 0x0c, 0x96, 0x77, 0x7e,
+	0x65, 0xb9, 0xf1, 0x09, 0xc5, 0x6e, 0xc6, 0x84,
+	0x18, 0xf0, 0x7d, 0xec, 0x3a, 0xdc, 0x4d, 0x20,
+	0x79, 0xee, 0x5f, 0x3e, 0xd7, 0xcb, 0x39, 0x48,
+};
+
+const uint32_t SMS4_T[256] = {
+	0x8ed55b5bU, 0xd0924242U, 0x4deaa7a7U, 0x06fdfbfbU,
+	0xfccf3333U, 0x65e28787U, 0xc93df4f4U, 0x6bb5dedeU,
+	0x4e165858U, 0x6eb4dadaU, 0x44145050U, 0xcac10b0bU,
+	0x8828a0a0U, 0x17f8efefU, 0x9c2cb0b0U, 0x11051414U,
+	0x872bacacU, 0xfb669d9dU, 0xf2986a6aU, 0xae77d9d9U,
+	0x822aa8a8U, 0x46bcfafaU, 0x14041010U, 0xcfc00f0fU,
+	0x02a8aaaaU, 0x54451111U, 0x5f134c4cU, 0xbe269898U,
+	0x6d482525U, 0x9e841a1aU, 0x1e061818U, 0xfd9b6666U,
+	0xec9e7272U, 0x4a430909U, 0x10514141U, 0x24f7d3d3U,
+	0xd5934646U, 0x53ecbfbfU, 0xf89a6262U, 0x927be9e9U,
+	0xff33ccccU, 0x04555151U, 0x270b2c2cU, 0x4f420d0dU,
+	0x59eeb7b7U, 0xf3cc3f3fU, 0x1caeb2b2U, 0xea638989U,
+	0x74e79393U, 0x7fb1ceceU, 0x6c1c7070U, 0x0daba6a6U,
+	0xedca2727U, 0x28082020U, 0x48eba3a3U, 0xc1975656U,
+	0x80820202U, 0xa3dc7f7fU, 0xc4965252U, 0x12f9ebebU,
+	0xa174d5d5U, 0xb38d3e3eU, 0xc33ffcfcU, 0x3ea49a9aU,
+	0x5b461d1dU, 0x1b071c1cU, 0x3ba59e9eU, 0x0cfff3f3U,
+	0x3ff0cfcfU, 0xbf72cdcdU, 0x4b175c5cU, 0x52b8eaeaU,
+	0x8f810e0eU, 0x3d586565U, 0xcc3cf0f0U, 0x7d196464U,
+	0x7ee59b9bU, 0x91871616U, 0x734e3d3dU, 0x08aaa2a2U,
+	0xc869a1a1U, 0xc76aadadU, 0x85830606U, 0x7ab0cacaU,
+	0xb570c5c5U, 0xf4659191U, 0xb2d96b6bU, 0xa7892e2eU,
+	0x18fbe3e3U, 0x47e8afafU, 0x330f3c3cU, 0x674a2d2dU,
+	0xb071c1c1U, 0x0e575959U, 0xe99f7676U, 0xe135d4d4U,
+	0x661e7878U, 0xb4249090U, 0x360e3838U, 0x265f7979U,
+	0xef628d8dU, 0x38596161U, 0x95d24747U, 0x2aa08a8aU,
+	0xb1259494U, 0xaa228888U, 0x8c7df1f1U, 0xd73bececU,
+	0x05010404U, 0xa5218484U, 0x9879e1e1U, 0x9b851e1eU,
+	0x84d75353U, 0x00000000U, 0x5e471919U, 0x0b565d5dU,
+	0xe39d7e7eU, 0x9fd04f4fU, 0xbb279c9cU, 0x1a534949U,
+	0x7c4d3131U, 0xee36d8d8U, 0x0a020808U, 0x7be49f9fU,
+	0x20a28282U, 0xd4c71313U, 0xe8cb2323U, 0xe69c7a7aU,
+	0x42e9ababU, 0x43bdfefeU, 0xa2882a2aU, 0x9ad14b4bU,
+	0x40410101U, 0xdbc41f1fU, 0xd838e0e0U, 0x61b7d6d6U,
+	0x2fa18e8eU, 0x2bf4dfdfU, 0x3af1cbcbU, 0xf6cd3b3bU,
+	0x1dfae7e7U, 0xe5608585U, 0x41155454U, 0x25a38686U,
+	0x60e38383U, 0x16acbabaU, 0x295c7575U, 0x34a69292U,
+	0xf7996e6eU, 0xe434d0d0U, 0x721a6868U, 0x01545555U,
+	0x19afb6b6U, 0xdf914e4eU, 0xfa32c8c8U, 0xf030c0c0U,
+	0x21f6d7d7U, 0xbc8e3232U, 0x75b3c6c6U, 0x6fe08f8fU,
+	0x691d7474U, 0x2ef5dbdbU, 0x6ae18b8bU, 0x962eb8b8U,
+	0x8a800a0aU, 0xfe679999U, 0xe2c92b2bU, 0xe0618181U,
+	0xc0c30303U, 0x8d29a4a4U, 0xaf238c8cU, 0x07a9aeaeU,
+	0x390d3434U, 0x1f524d4dU, 0x764f3939U, 0xd36ebdbdU,
+	0x81d65757U, 0xb7d86f6fU, 0xeb37dcdcU, 0x51441515U,
+	0xa6dd7b7bU, 0x09fef7f7U, 0xb68c3a3aU, 0x932fbcbcU,
+	0x0f030c0cU, 0x03fcffffU, 0xc26ba9a9U, 0xba73c9c9U,
+	0xd96cb5b5U, 0xdc6db1b1U, 0x375a6d6dU, 0x15504545U,
+	0xb98f3636U, 0x771b6c6cU, 0x13adbebeU, 0xda904a4aU,
+	0x57b9eeeeU, 0xa9de7777U, 0x4cbef2f2U, 0x837efdfdU,
+	0x55114444U, 0xbdda6767U, 0x2c5d7171U, 0x45400505U,
+	0x631f7c7cU, 0x50104040U, 0x325b6969U, 0xb8db6363U,
+	0x220a2828U, 0xc5c20707U, 0xf531c4c4U, 0xa88a2222U,
+	0x31a79696U, 0xf9ce3737U, 0x977aededU, 0x49bff6f6U,
+	0x992db4b4U, 0xa475d1d1U, 0x90d34343U, 0x5a124848U,
+	0x58bae2e2U, 0x71e69797U, 0x64b6d2d2U, 0x70b2c2c2U,
+	0xad8b2626U, 0xcd68a5a5U, 0xcb955e5eU, 0x624b2929U,
+	0x3c0c3030U, 0xce945a5aU, 0xab76ddddU, 0x867ff9f9U,
+	0xf1649595U, 0x5dbbe6e6U, 0x35f2c7c7U, 0x2d092424U,
+	0xd1c61717U, 0xd66fb9b9U, 0xdec51b1bU, 0x94861212U,
+	0x78186060U, 0x30f3c3c3U, 0x897cf5f5U, 0x5cefb3b3U,
+	0xd23ae8e8U, 0xacdf7373U, 0x794c3535U, 0xa0208080U,
+	0x9d78e5e5U, 0x56edbbbbU, 0x235e7d7dU, 0xc63ef8f8U,
+	0x8bd45f5fU, 0xe7c82f2fU, 0xdd39e4e4U, 0x68492121U,
+};
+
+const uint32_t SMS4_D[65536] = {
+	0xd55b8e00U, 0xcc05c919U, 0x2998b1fcU, 0x75d3a6a0U,
+	0xbd299468U, 0x09b0b9dcU, 0x7a1c66afU, 0x50beee85U,
+	0xd69b4d03U, 0x54bbef81U, 0xde914f0bU, 0x851f9a50U,
+	0x2e5d73fbU, 0x61c2a3b4U, 0x3e4977ebU, 0x9ac45e4fU,
+	0x225270f7U, 0x132e3dc6U, 0xe427c331U, 0x577b2c82U,
+	0x265771f3U, 0x7493e7a1U, 0x9ec15f4bU, 0x811a9b54U,
+	0x24d7f3f1U, 0x9f811e4aU, 0xc28a4817U, 0x166b7dc3U,
+	0xabb8137eU, 0x944bdf41U, 0x96cb5d43U, 0xe828c03dU,
+	0xfc39c529U, 0x879f1852U, 0xcfc50a1aU, 0x5df1ac88U,
+	0xc800c81dU, 0x3186b7e4U, 0xec2dc139U, 0x674720b2U,
+	0x422a6897U, 0xdfd10e0aU, 0xa2f25077U, 0x839a1956U,
+	0x398cb5ecU, 0xb1269764U, 0x3cc9f5e9U, 0x073f38d2U,
+	0x1da1bcc8U, 0x40aaea95U, 0xfeb9472bU, 0x28d8f0fdU,
+	0xa938917cU, 0xaefd537bU, 0x2d9db0f8U, 0xd814cc0dU,
+	0x8c55d959U, 0xf1768724U, 0xdc11cd09U, 0x65c7a2b0U,
+	0x5b742f8eU, 0xb066d665U, 0x721664a7U, 0x14ebffc1U,
+	0x938e1d46U, 0x92ce5c47U, 0x10eefec5U, 0x7dd9a4a8U,
+	0x41eaab94U, 0x436a2996U, 0xd29e4c07U, 0x6487e3b1U,
+	0x805ada55U, 0xebe8033eU, 0x7e1967abU, 0xeaa8423fU,
+	0x15abbec0U, 0x9844dc4dU, 0xb3a61566U, 0x2cddf1f9U,
+	0x2f1d32faU, 0x231231f6U, 0x8850d85dU, 0x44afeb91U,
+	0x4b602b9eU, 0x1f213ecaU, 0xe5678230U, 0xa072d275U,
+	0x6dcda0b8U, 0x2192b3f4U, 0xb2e65467U, 0xa3b21176U,
+	0x4f652a9aU, 0xd7db0c02U, 0xf83cc42dU, 0x5a346e8fU,
+	0xf6b34523U, 0x1e617fcbU, 0xb6e35563U, 0xf7f30422U,
+	0x033a39d6U, 0xefed023aU, 0xc940891cU, 0x04fffbd1U,
+	0x1a647ecfU, 0x067f79d3U, 0x7f5926aaU, 0x620260b7U,
+	0x8ad05a5fU, 0x0a707adfU, 0x6f4d22baU, 0x904ede45U,
+	0xdd518c08U, 0x8ed55b5bU, 0x978b1c42U, 0xd3de0d06U,
+	0xf036c625U, 0xc14a8b14U, 0x126e7cc7U, 0xc7cf0812U,
+	0xbfa9166aU, 0x563b6d83U, 0x86df5953U, 0x11aebfc4U,
+	0x0cf5f9d9U, 0x9d019c48U, 0xad3d9078U, 0xf433c721U,
+	0x2597b2f0U, 0x7096e6a5U, 0xa477d371U, 0xc54f8a10U,
+	0x8f951a5aU, 0x910e9f44U, 0x6e0d63bbU, 0x58b4ec8dU,
+	0x00fafad5U, 0x51feaf84U, 0x45efaa90U, 0xb5239660U,
+	0x69c8a1bcU, 0x0b303bdeU, 0xda944e0fU, 0x08f0f8ddU,
+	0x0db5b8d8U, 0x34c3f7e1U, 0xfbfc072eU, 0x1ce1fdc9U,
+	0xe022c235U, 0x5e316f8bU, 0xe6a74133U, 0xdbd40f0eU,
+	0x38ccf4edU, 0xc00aca15U, 0x462f6993U, 0x4e256b9bU,
+	0x59f4ad8cU, 0xbc69d569U, 0x48a0e89dU, 0x01babbd4U,
+	0xfabc462fU, 0x55fbae80U, 0x05bfbad0U, 0x364375e3U,
+	0x845fdb51U, 0x172b3cc2U, 0xa5379270U, 0x0f353adaU,
+	0x8d159858U, 0x2a5872ffU, 0x027a78d7U, 0x20d2f2f5U,
+	0xbaec566fU, 0xc3ca0916U, 0xb7a31462U, 0x330635e6U,
+	0xd9548d0cU, 0xe1628334U, 0x523e6c87U, 0x9b841f4eU,
+	0xf5738620U, 0x79dca5acU, 0xb463d761U, 0x324674e7U,
+	0x82da5857U, 0x71d6a7a4U, 0x271730f2U, 0x476f2892U,
+	0x3b0c37eeU, 0x3f0936eaU, 0xe3e20136U, 0xcbc00b1eU,
+	0xb86cd46dU, 0xe2a24037U, 0x30c6f6e5U, 0xc40fcb11U,
+	0x6082e2b5U, 0xf97c852cU, 0x7c99e5a9U, 0x735625a6U,
+	0xca804a1fU, 0xe968813cU, 0xfff9062aU, 0x8b901b5eU,
+	0xf2b64427U, 0xce854b1bU, 0xe7e70032U, 0xed6d8038U,
+	0xa6f75173U, 0x8910995cU, 0x4a206a9fU, 0xac7dd179U,
+	0x18e4fccdU, 0xb92c956cU, 0x634221b6U, 0x789ce4adU,
+	0x3a4c76efU, 0x5f712e8aU, 0xcd458818U, 0xc68f4913U,
+	0x6c8de1b9U, 0x19a4bdccU, 0x5cb1ed89U, 0x4ca5e999U,
+	0xa878d07dU, 0x2b1833feU, 0xd01ece05U, 0xa7b71072U,
+	0xbee9576bU, 0xd41bcf01U, 0x537e2d86U, 0x775324a2U,
+	0x1b243fceU, 0x6888e0bdU, 0x49e0a99cU, 0xaaf8527fU,
+	0x99049d4cU, 0x370334e2U, 0x950b9e40U, 0x9c41dd49U,
+	0xeead433bU, 0x4de5a898U, 0x7b5c27aeU, 0x3d89b4e8U,
+	0x660761b3U, 0xfd798428U, 0xbbac176eU, 0x0e757bdbU,
+	0x6b4823beU, 0x3583b6e0U, 0xf3f60526U, 0x761365a3U,
+	0xd15e8f04U, 0xa1329374U, 0x6a0862bfU, 0xafbd127aU,
+	0x8b1c9719U, 0x9242d000U, 0x77dfa8e5U, 0x2b94bfb9U,
+	0xe36e8d71U, 0x57f7a0c5U, 0x245b7fb6U, 0x0ef9f79cU,
+	0x88dc541aU, 0x0afcf698U, 0x80d65612U, 0xdb588349U,
+	0x701a6ae2U, 0x3f85baadU, 0x600e6ef2U, 0xc4834756U,
+	0x7c1569eeU, 0x4d6924dfU, 0xba60da28U, 0x093c359bU,
+	0x781068eaU, 0x2ad4feb8U, 0xc0864652U, 0xdf5d824dU,
+	0x7a90eae8U, 0xc1c60753U, 0x9ccd510eU, 0x482c64daU,
+	0xf5ff0a67U, 0xca0cc658U, 0xc88c445aU, 0xb66fd924U,
+	0xa27edc30U, 0xd9d8014bU, 0x91821303U, 0x03b6b591U,
+	0x9647d104U, 0x6fc1aefdU, 0xb26ad820U, 0x390039abU,
+	0x1c6d718eU, 0x81961713U, 0xfcb5496eU, 0xdddd004fU,
+	0x67cbacf5U, 0xef618e7dU, 0x628eecf0U, 0x597821cbU,
+	0x43e6a5d1U, 0x1eedf38cU, 0xa0fe5e32U, 0x769fe9e4U,
+	0xf77f8865U, 0xf0ba4a62U, 0x73daa9e1U, 0x8653d514U,
+	0xd212c040U, 0xaf319e3dU, 0x8256d410U, 0x3b80bba9U,
+	0x05333697U, 0xee21cf7cU, 0x2c517dbeU, 0x4aace6d8U,
+	0xcdc9045fU, 0xcc89455eU, 0x4ea9e7dcU, 0x239ebdb1U,
+	0x1fadb28dU, 0x1d2d308fU, 0x8cd9551eU, 0x3ac0faa8U,
+	0xde1dc34cU, 0xb5af1a27U, 0x205e7eb2U, 0xb4ef5b26U,
+	0x4beca7d9U, 0xc603c554U, 0xede10c7fU, 0x729ae8e0U,
+	0x715a2be3U, 0x7d5528efU, 0xd617c144U, 0x1ae8f288U,
+	0x15273287U, 0x416627d3U, 0xbb209b29U, 0xfe35cb6cU,
+	0x338ab9a1U, 0x7fd5aaedU, 0xeca14d7eU, 0xfdf5086fU,
+	0x11223383U, 0x899c151bU, 0xa67bdd34U, 0x04737796U,
+	0xa8f45c3aU, 0x402666d2U, 0xe8a44c7aU, 0xa9b41d3bU,
+	0x5d7d20cfU, 0xb1aa1b23U, 0x97079005U, 0x5ab8e2c8U,
+	0x442367d6U, 0x583860caU, 0x211e3fb3U, 0x3c4579aeU,
+	0xd4974346U, 0x543763c6U, 0x310a3ba3U, 0xce09c75cU,
+	0x83169511U, 0xd0924242U, 0xc9cc055bU, 0x8d99141fU,
+	0xae71df3cU, 0x9f0d920dU, 0x4c2965deU, 0x9988110bU,
+	0xe1ee0f73U, 0x087c749aU, 0xd898404aU, 0x4fe9a6ddU,
+	0x52b2e0c0U, 0xc3468551U, 0xf37a8961U, 0xaa74de38U,
+	0x7bd0abe9U, 0x2ed1ffbcU, 0xfa30ca68U, 0x9b089309U,
+	0xd1d20343U, 0xcf49865dU, 0x304a7aa2U, 0x06f3f594U,
+	0x5ebde3ccU, 0x0fb9b69dU, 0x1ba8b389U, 0xeb648f79U,
+	0x378fb8a5U, 0x557722c7U, 0x84d35716U, 0x56b7e1c4U,
+	0x53f2a1c1U, 0x6a84eef8U, 0xa5bb1e37U, 0x42a6e4d0U,
+	0xbe65db2cU, 0x00767692U, 0xb8e0582aU, 0x85931617U,
+	0x668bedf4U, 0x9e4dd30cU, 0x1868708aU, 0x10627282U,
+	0x07b3b495U, 0xe22ecc70U, 0x16e7f184U, 0x5ffda2cdU,
+	0xa4fb5f36U, 0x0bbcb799U, 0x5bf8a3c9U, 0x68046cfaU,
+	0xda18c248U, 0x496c25dbU, 0xfb708b69U, 0x517223c3U,
+	0xd3528141U, 0x741f6be6U, 0x5c3d61ceU, 0x7e95ebecU,
+	0xe4ab4f76U, 0x9d8d100fU, 0xe9e40d7bU, 0x6d412cffU,
+	0x87139415U, 0xbf259a2dU, 0x0c79759eU, 0xc5c30657U,
+	0xab349f39U, 0x279bbcb5U, 0xea24ce78U, 0x6c016dfeU,
+	0xdc9d414eU, 0x2f91bebdU, 0x795029ebU, 0x1928318bU,
+	0x654b2ef7U, 0x614e2ff3U, 0xbda5182fU, 0x95871207U,
+	0xe62bcd74U, 0xbce5592eU, 0x6e81effcU, 0x9a48d208U,
+	0x3ec5fbacU, 0xa73b9c35U, 0x22defcb0U, 0x2d113cbfU,
+	0x94c75306U, 0xb72f9825U, 0xa1be1f33U, 0xd5d70247U,
+	0xacf15d3eU, 0x90c25202U, 0xb9a0192bU, 0xb32a9921U,
+	0xf8b0486aU, 0xd7578045U, 0x14677386U, 0xf23ac860U,
+	0x46a3e5d4U, 0xe76b8c75U, 0x3d0538afU, 0x26dbfdb4U,
+	0x640b6ff6U, 0x01363793U, 0x93029101U, 0x98c8500aU,
+	0x32caf8a0U, 0x47e3a4d5U, 0x02f6f490U, 0x12e2f080U,
+	0xf63fc964U, 0x755f2ae7U, 0x8e59d71cU, 0xf9f0096bU,
+	0xe0ae4e72U, 0x8a5cd618U, 0x0d39349fU, 0x29143dbbU,
+	0x456326d7U, 0x36cff9a4U, 0x17a7b085U, 0xf4bf4b66U,
+	0xc7438455U, 0x69442dfbU, 0xcb4c8759U, 0xc206c450U,
+	0xb0ea5a22U, 0x13a2b181U, 0x251b3eb7U, 0x63ceadf1U,
+	0x384078aaU, 0xa33e9d31U, 0xe5eb0e77U, 0x503262c2U,
+	0x350f3aa7U, 0x6bc4aff9U, 0xadb11c3fU, 0x28547cbaU,
+	0x8f19961dU, 0xff758a6dU, 0x344f7ba6U, 0xf1fa0b63U,
+	0x166472fcU, 0x0f3a35e5U, 0xeaa74d00U, 0xb6ec5a5cU,
+	0x7e166894U, 0xca8f4520U, 0xb9239a53U, 0x93811279U,
+	0x15a4b1ffU, 0x9784137dU, 0x1daeb3f7U, 0x462066acU,
+	0xed628f07U, 0xa2fd5f48U, 0xfd768b17U, 0x59fba2b3U,
+	0xe16d8c0bU, 0xd011c13aU, 0x27183fcdU, 0x9444d07eU,
+	0xe5688d0fU, 0xb7ac1b5dU, 0x5dfea3b7U, 0x422567a8U,
+	0xe7e80f0dU, 0x5cbee2b6U, 0x01b5b4ebU, 0xd554813fU,
+	0x6887ef82U, 0x577423bdU, 0x55f4a1bfU, 0x2b173cc1U,
+	0x3f0639d5U, 0x44a0e4aeU, 0x0cfaf6e6U, 0x9ece5074U,
+	0x0b3f34e1U, 0xf2b94b18U, 0x2f123dc5U, 0xa478dc4eU,
+	0x8115946bU, 0x1ceef2f6U, 0x61cdac8bU, 0x40a5e5aaU,
+	0xfab34910U, 0x72196b98U, 0xfff60915U, 0xc400c42eU,
+	0xde9e4034U, 0x83951669U, 0x3d86bbd7U, 0xebe70c01U,
+	0x6a076d80U, 0x6dc2af87U, 0xeea24c04U, 0x1b2b30f1U,
+	0x4f6a25a5U, 0x32497bd8U, 0x1f2e31f5U, 0xa6f85e4cU,
+	0x984bd372U, 0x73592a99U, 0xb129985bU, 0xd7d4033dU,
+	0x50b1e1baU, 0x51f1a0bbU, 0xd3d10239U, 0xbee65854U,
+	0x82d55768U, 0x8055d56aU, 0x11a1b0fbU, 0xa7b81f4dU,
+	0x436526a9U, 0x28d7ffc2U, 0xbd269b57U, 0x2997bec3U,
+	0xd694423cU, 0x5b7b20b1U, 0x7099e99aU, 0xefe20d05U,
+	0xec22ce06U, 0xe02dcd0aU, 0x4b6f24a1U, 0x8790176dU,
+	0x885fd762U, 0xdc1ec236U, 0x26587eccU, 0x634d2e89U,
+	0xaef25c44U, 0xe2ad4f08U, 0x71d9a89bU, 0x608ded8aU,
+	0x8c5ad666U, 0x14e4f0feU, 0x3b0338d1U, 0x990b9273U,
+	0x358cb9dfU, 0xdd5e8337U, 0x75dca99fU, 0x34ccf8deU,
+	0xc005c52aU, 0x2cd2fec6U, 0x0a7f75e0U, 0xc7c0072dU,
+	0xd95b8233U, 0xc540852fU, 0xbc66da56U, 0xa13d9c4bU,
+	0x49efa6a3U, 0xc94f8623U, 0xac72de46U, 0x537122b9U,
+	0x1e6e70f4U, 0x4deaa7a7U, 0x54b4e0beU, 0x10e1f1faU,
+	0x33093ad9U, 0x027577e8U, 0xd151803bU, 0x04f0f4eeU,
+	0x7c96ea96U, 0x9504917fU, 0x45e0a5afU, 0xd2914338U,
+	0xcfca0525U, 0x5e3e60b4U, 0x6e026c84U, 0x370c3bddU,
+	0xe6a84e0cU, 0xb3a91a59U, 0x67482f8dU, 0x067076ecU,
+	0x4caae6a6U, 0x523163b8U, 0xad329f47U, 0x9b8b1071U,
+	0xc3c50629U, 0x92c15378U, 0x86d0566cU, 0x761c6a9cU,
+	0xaaf75d40U, 0xc80fc722U, 0x19abb2f3U, 0xcbcf0421U,
+	0xce8a4424U, 0xf7fc0b1dU, 0x38c3fbd2U, 0xdfde0135U,
+	0x231d3ec9U, 0x9d0e9377U, 0x2598bdcfU, 0x18ebf3f2U,
+	0xfbf30811U, 0x033536e9U, 0x8510956fU, 0x8d1a9767U,
+	0x9acb5170U, 0x7f562995U, 0x8b9f1461U, 0xc2854728U,
+	0x3983bad3U, 0x96c4527cU, 0xc680462cU, 0xf57c891fU,
+	0x476027adU, 0xd414c03eU, 0x66086e8cU, 0xcc0ac626U,
+	0x4e2a64a4U, 0xe9678e03U, 0xc145842bU, 0xe3ed0e09U,
+	0x79d3aa93U, 0x00f5f5eaU, 0x749ce89eU, 0xf039c91aU,
+	0x1a6b71f0U, 0x225d7fc8U, 0x9101907bU, 0x58bbe3b2U,
+	0x364c7adcU, 0xbae35950U, 0x775c2b9dU, 0xf179881bU,
+	0x41e5a4abU, 0xb2e95b58U, 0xe428cc0eU, 0x8450d46eU,
+	0xf833cb12U, 0xfc36ca16U, 0x20ddfdcaU, 0x08fff7e2U,
+	0x7b532891U, 0x219dbccbU, 0xf3f90a19U, 0x073037edU,
+	0xa3bd1e49U, 0x3a4379d0U, 0xbfa61955U, 0xb069d95aU,
+	0x09bfb6e3U, 0x2a577dc0U, 0x3cc6fad6U, 0x48afe7a2U,
+	0x3189b8dbU, 0x0dbab7e7U, 0x24d8fcceU, 0x2e527cc4U,
+	0x65c8ad8fU, 0x4a2f65a0U, 0x891f9663U, 0x6f422d85U,
+	0xdbdb0031U, 0x7a136990U, 0xa07ddd4aU, 0xbba31851U,
+	0xf9738a13U, 0x9c4ed276U, 0x0e7a74e4U, 0x05b0b5efU,
+	0xafb21d45U, 0xda9b4130U, 0x9f8e1175U, 0x8f9a1565U,
+	0x6b472c81U, 0xe827cf02U, 0x132132f9U, 0x6488ec8eU,
+	0x7dd6ab97U, 0x172433fdU, 0x9041d17aU, 0xb46cd85eU,
+	0xd81bc332U, 0xabb71c41U, 0x8adf5560U, 0x69c7ae83U,
+	0x5a3b61b0U, 0xf43cc81eU, 0x563462bcU, 0x5f7e21b5U,
+	0x2d92bfc7U, 0x8eda5464U, 0xb863db52U, 0xfeb64814U,
+	0xa5389d4fU, 0x3e4678d4U, 0x7893eb92U, 0xcd4a8727U,
+	0xa877df42U, 0xf6bc4a1cU, 0x30c9f9daU, 0xb52c995fU,
+	0x126173f8U, 0x620d6f88U, 0xa9379e43U, 0x6c82ee86U,
+	0x5d732ea0U, 0x442d69b9U, 0xa1b0115cU, 0xfdfb0600U,
+	0x350134c8U, 0x8198197cU, 0xf234c60fU, 0xd8964e25U,
+	0x5eb3eda3U, 0xdc934f21U, 0x56b9efabU, 0x0d373af0U,
+	0xa675d35bU, 0xe9ea0314U, 0xb661d74bU, 0x12ecfeefU,
+	0xaa7ad057U, 0x9b069d66U, 0x6c0f6391U, 0xdf538c22U,
+	0xae7fd153U, 0xfcbb4701U, 0x16e9ffebU, 0x09323bf4U,
+	0xacff5351U, 0x17a9beeaU, 0x4aa2e8b7U, 0x9e43dd63U,
+	0x2390b3deU, 0x1c637fe1U, 0x1ee3fde3U, 0x6000609dU,
+	0x74116589U, 0x0fb7b8f2U, 0x47edaabaU, 0xd5d90c28U,
+	0x402868bdU, 0xb9ae1744U, 0x64056199U, 0xef6f8012U,
+	0xca02c837U, 0x57f9aeaaU, 0x2adaf0d7U, 0x0bb2b9f6U,
+	0xb1a4154cU, 0x390e37c4U, 0xb4e15549U, 0x8f179872U,
+	0x95891c68U, 0xc8824a35U, 0x7691e78bU, 0xa0f0505dU,
+	0x211031dcU, 0x26d5f3dbU, 0xa5b51058U, 0x503c6cadU,
+	0x047d79f9U, 0x795e2784U, 0x54396da9U, 0xedef0210U,
+	0xd35c8f2eU, 0x384e76c5U, 0xfa3ec407U, 0x9cc35f61U,
+	0x1ba6bde6U, 0x1ae6fce7U, 0x98c65e65U, 0xf5f10408U,
+	0xc9c20b34U, 0xcb428936U, 0x5ab6eca7U, 0xecaf4311U,
+	0x08727af5U, 0x63c0a39eU, 0xf631c70bU, 0x6280e29fU,
+	0x9d831e60U, 0x106c7cedU, 0x3b8eb5c6U, 0xa4f55159U,
+	0xa735925aU, 0xab3a9156U, 0x007878fdU, 0xcc874b31U,
+	0xc3488b3eU, 0x97099e6aU, 0x6d4f2290U, 0x285a72d5U,
+	0xe5e50018U, 0xa9ba1354U, 0x3acef4c7U, 0x2b9ab1d6U,
+	0xc74d8a3aU, 0x5ff3aca2U, 0x7014648dU, 0xd21cce2fU,
+	0x7e9be583U, 0x9649df6bU, 0x3ecbf5c3U, 0x7fdba482U,
+	0x8b129976U, 0x67c5a29aU, 0x416829bcU, 0x8cd75b71U,
+	0x924cde6fU, 0x8e57d973U, 0xf771860aU, 0xea2ac017U,
+	0x02f8faffU, 0x8258da7fU, 0xe765821aU, 0x18667ee5U,
+	0x55792ca8U, 0x06fdfbfbU, 0x1fa3bce2U, 0x5bf6ada6U,
+	0x781e6685U, 0x49622bb4U, 0x9a46dc67U, 0x4fe7a8b2U,
+	0x3781b6caU, 0xde13cd23U, 0x0ef7f9f3U, 0x99861f64U,
+	0x84dd5979U, 0x15293ce8U, 0x251530d8U, 0x7c1b6781U,
+	0xadbf1250U, 0xf8be4605U, 0x2c5f73d1U, 0x4d672ab0U,
+	0x07bdbafaU, 0x19263fe4U, 0xe625c31bU, 0xd09c4c2dU,
+	0x88d25a75U, 0xd9d60f24U, 0xcdc70a30U, 0x3d0b36c0U,
+	0xe1e0011cU, 0x83189b7eU, 0x52bceeafU, 0x80d8587dU,
+	0x859d1878U, 0xbceb5741U, 0x73d4a78eU, 0x94c95d69U,
+	0x680a6295U, 0xd619cf2bU, 0x6e8fe193U, 0x53fcafaeU,
+	0xb0e4544dU, 0x48226ab5U, 0xce07c933U, 0xc60dcb3bU,
+	0xd1dc0d2cU, 0x344175c9U, 0xc088483dU, 0x89921b74U,
+	0x7294e68fU, 0xddd30e20U, 0x8d971a70U, 0xbe6bd543U,
+	0x0c777bf1U, 0x9f039c62U, 0x2d1f32d0U, 0x871d9a7aU,
+	0x053d38f8U, 0xa270d25fU, 0x8a52d877U, 0xa8fa5255U,
+	0x32c4f6cfU, 0x4be2a9b6U, 0x3f8bb4c2U, 0xbb2e9546U,
+	0x517c2dacU, 0x694a2394U, 0xda16cc27U, 0x13acbfeeU,
+	0x7d5b2680U, 0xf1f4050cU, 0x3c4b77c1U, 0xba6ed447U,
+	0x0af2f8f7U, 0xf9fe0704U, 0xaf3f9052U, 0xcf478832U,
+	0xb324974eU, 0xb721964aU, 0x6bcaa196U, 0x43e8abbeU,
+	0x304474cdU, 0x6a8ae097U, 0xb8ee5645U, 0x4c276bb1U,
+	0xe8aa4215U, 0x7154258cU, 0xf4b14509U, 0xfb7e8506U,
+	0x42a8eabfU, 0x6140219cU, 0x77d1a68aU, 0x03b8bbfeU,
+	0x7a9ee487U, 0x46adebbbU, 0x6fcfa092U, 0x65452098U,
+	0x2edff1d3U, 0x013839fcU, 0xc208ca3fU, 0x245571d9U,
+	0x90cc5c6dU, 0x310435ccU, 0xeb6a8116U, 0xf0b4440dU,
+	0xb264d64fU, 0xd7598e2aU, 0x456d28b8U, 0x4ea7e9b3U,
+	0xe4a54119U, 0x918c1d6cU, 0xd4994d29U, 0xc48d4939U,
+	0x205070ddU, 0xa330935eU, 0x58366ea5U, 0x2f9fb0d2U,
+	0x36c1f7cbU, 0x5c336fa1U, 0xdb568d26U, 0xff7b8402U,
+	0x930c9f6eU, 0xe0a0401dU, 0xc1c8093cU, 0x22d0f2dfU,
+	0x112c3decU, 0xbf2b9442U, 0x1d233ee0U, 0x14697de9U,
+	0x6685e39bU, 0xc5cd0838U, 0xf374870eU, 0xb5a11448U,
+	0xee2fc113U, 0x75512488U, 0x3384b7ceU, 0x865ddb7bU,
+	0xe360831eU, 0xbdab1640U, 0x7bdea586U, 0xfe3bc503U,
+	0x59762fa4U, 0x291a33d4U, 0xe220c21fU, 0x2795b2daU,
+	0xa741e668U, 0xbe1fa171U, 0x5b82d994U, 0x07c9cec8U,
+	0xcf33fc00U, 0x7baad1b4U, 0x08060ec7U, 0x22a486edU,
+	0xa481256bU, 0x26a187e9U, 0xac8b2763U, 0xf705f238U,
+	0x5c471b93U, 0x13d8cbdcU, 0x4c531f83U, 0xe8de3627U,
+	0x5048189fU, 0x613455aeU, 0x963dab59U, 0x256144eaU,
+	0x544d199bU, 0x06898fc9U, 0xecdb3723U, 0xf300f33cU,
+	0x56cd9b99U, 0xed9b7622U, 0xb090207fU, 0x647115abU,
+	0xd9a27b16U, 0xe651b729U, 0xe4d1352bU, 0x9a32a855U,
+	0x8e23ad41U, 0xf585703aU, 0xbddf6272U, 0x2febc4e0U,
+	0xba1aa075U, 0x439cdf8cU, 0x9e37a951U, 0x155d48daU,
+	0x303000ffU, 0xadcb6662U, 0xd0e8381fU, 0xf180713eU,
+	0x4b96dd84U, 0xc33cff0cU, 0x4ed39d81U, 0x752550baU,
+	0x6fbbd4a0U, 0x32b082fdU, 0x8ca32f43U, 0x5ac29895U,
+	0xdb22f914U, 0xdce73b13U, 0x5f87d890U, 0xaa0ea465U,
+	0xfe4fb131U, 0x836cef4cU, 0xae0ba561U, 0x17ddcad8U,
+	0x296e47e6U, 0xc27cbe0dU, 0x000c0ccfU, 0x66f197a9U,
+	0xe194752eU, 0xe0d4342fU, 0x62f496adU, 0x0fc3ccc0U,
+	0x33f0c3fcU, 0x317041feU, 0xa084246fU, 0x169d8bd9U,
+	0xf240b23dU, 0x99f26b56U, 0x0c030fc3U, 0x98b22a57U,
+	0x67b1d6a8U, 0xea5eb425U, 0xc1bc7d0eU, 0x5ec79991U,
+	0x5d075a92U, 0x5108599eU, 0xfa4ab035U, 0x36b583f9U,
+	0x397a43f6U, 0x6d3b56a2U, 0x977dea58U, 0xd268ba1dU,
+	0x1fd7c8d0U, 0x5388db9cU, 0xc0fc3c0fU, 0xd1a8791eU,
+	0x3d7f42f2U, 0xa5c1646aU, 0x8a26ac45U, 0x282e06e7U,
+	0x84a92d4bU, 0x6c7b17a3U, 0xc4f93d0bU, 0x85e96c4aU,
+	0x712051beU, 0x9df76a52U, 0xbb5ae174U, 0x76e593b9U,
+	0x687e16a7U, 0x746511bbU, 0x0d434ec2U, 0x101808dfU,
+	0xf8ca3237U, 0x786a12b7U, 0x1d574ad2U, 0xe254b62dU,
+	0xaf4be460U, 0xfccf3333U, 0xe591742aU, 0xa1c4656eU,
+	0x822cae4dU, 0xb350e37cU, 0x607414afU, 0xb5d5607aU,
+	0xcdb37e02U, 0x242105ebU, 0xf4c5313bU, 0x63b4d7acU,
+	0x7eef91b1U, 0xef1bf420U, 0xdf27f810U, 0x8629af49U,
+	0x578dda98U, 0x028c8ecdU, 0xd66dbb19U, 0xb755e278U,
+	0xfd8f7232U, 0xe314f72cU, 0x1c170bd3U, 0x2aae84e5U,
+	0x72e092bdU, 0x23e4c7ecU, 0x37f5c2f8U, 0xc739fe08U,
+	0x1bd2c9d4U, 0x792a53b6U, 0xa88e2667U, 0x7aea90b5U,
+	0x7fafd0b0U, 0x46d99f89U, 0x89e66f46U, 0x6efb95a1U,
+	0x9238aa5dU, 0x2c2b07e3U, 0x94bd295bU, 0xa9ce6766U,
+	0x4ad69c85U, 0xb210a27dU, 0x343501fbU, 0x3c3f03f3U,
+	0x2beec5e4U, 0xce73bd01U, 0x3aba80f5U, 0x73a0d3bcU,
+	0x88a62e47U, 0x27e1c6e8U, 0x77a5d2b8U, 0x44591d8bU,
+	0xf645b339U, 0x653154aaU, 0xd72dfa18U, 0x7d2f52b2U,
+	0xff0ff030U, 0x58421a97U, 0x706010bfU, 0x52c89a9dU,
+	0xc8f63e07U, 0xb1d0617eU, 0xc5b97c0aU, 0x411c5d8eU,
+	0xab4ee564U, 0x9378eb5cU, 0x202404efU, 0xe99e7726U,
+	0x8769ee48U, 0x0bc6cdc4U, 0xc679bf09U, 0x405c1c8fU,
+	0xf0c0303fU, 0x03cccfccU, 0x550d589aU, 0x357540faU,
+	0x49165f86U, 0x4d135e82U, 0x91f8695eU, 0xb9da6376U,
+	0xca76bc05U, 0x90b8285fU, 0x42dc9e8dU, 0xb615a379U,
+	0x12988addU, 0x8b66ed44U, 0x0e838dc1U, 0x014c4dceU,
+	0xb89a2277U, 0x9b72e954U, 0x8de36e42U, 0xf98a7336U,
+	0x80ac2c4fU, 0xbc9f2373U, 0x95fd685aU, 0x9f77e850U,
+	0xd4ed391bU, 0xfb0af134U, 0x383a02f7U, 0xde67b911U,
+	0x6afe94a5U, 0xcb36fd04U, 0x115849deU, 0x0a868cc5U,
+	0x48561e87U, 0x2d6b46e2U, 0xbf5fe070U, 0xb495217bU,
+	0x1e9789d1U, 0x6bbed5a4U, 0x2eab85e1U, 0x3ebf81f1U,
+	0xda62b815U, 0x59025b96U, 0xa204a66dU, 0xd5ad781aU,
+	0xccf33f03U, 0xa601a769U, 0x216445eeU, 0x05494ccaU,
+	0x693e57a6U, 0x1a9288d5U, 0x3bfac1f4U, 0xd8e23a17U,
+	0xeb1ef524U, 0x45195c8aU, 0xe711f628U, 0xee5bb521U,
+	0x9cb72b53U, 0x3fffc0f0U, 0x09464fc6U, 0x4f93dc80U,
+	0x141d09dbU, 0x8f63ec40U, 0xc9b67f06U, 0x7c6f13b3U,
+	0x19524bd6U, 0x4799de88U, 0x81ec6d4eU, 0x04090dcbU,
+	0xa344e76cU, 0xd328fb1cU, 0x18120ad7U, 0xdda77a12U,
+	0x3e6c52dcU, 0x273215c5U, 0xc2af6d20U, 0x9ee47a7cU,
+	0x561e48b4U, 0xe2876500U, 0x912bba73U, 0xbb893259U,
+	0x3dac91dfU, 0xbf8c335dU, 0x35a693d7U, 0x6e28468cU,
+	0xc56aaf27U, 0x8af57f68U, 0xd57eab37U, 0x71f38293U,
+	0xc965ac2bU, 0xf819e11aU, 0x0f101fedU, 0xbc4cf05eU,
+	0xcd60ad2fU, 0x9fa43b7dU, 0x75f68397U, 0x6a2d4788U,
+	0xcfe02f2dU, 0x74b6c296U, 0x29bd94cbU, 0xfd5ca11fU,
+	0x408fcfa2U, 0x7f7c039dU, 0x7dfc819fU, 0x031f1ce1U,
+	0x170e19f5U, 0x6ca8c48eU, 0x24f2d6c6U, 0xb6c67054U,
+	0x233714c1U, 0xdab16b38U, 0x071a1de5U, 0x8c70fc6eU,
+	0xa91db44bU, 0x34e6d2d6U, 0x49c58cabU, 0x68adc58aU,
+	0xd2bb6930U, 0x5a114bb8U, 0xd7fe2935U, 0xec08e40eU,
+	0xf6966014U, 0xab9d3649U, 0x158e9bf7U, 0xc3ef2c21U,
+	0x420f4da0U, 0x45ca8fa7U, 0xc6aa6c24U, 0x332310d1U,
+	0x67620585U, 0x1a415bf8U, 0x372611d5U, 0x8ef07e6cU,
+	0xb043f352U, 0x5b510ab9U, 0x9921b87bU, 0xffdc231dU,
+	0x78b9c19aU, 0x79f9809bU, 0xfbd92219U, 0x96ee7874U,
+	0xaadd7748U, 0xa85df54aU, 0x39a990dbU, 0x8fb03f6dU,
+	0x6b6d0689U, 0x00dfdfe2U, 0x952ebb77U, 0x019f9ee3U,
+	0xfe9c621cU, 0x73730091U, 0x5891c9baU, 0xc7ea2d25U,
+	0xc42aee26U, 0xc825ed2aU, 0x63670481U, 0xaf98374dU,
+	0xa057f742U, 0xf416e216U, 0x0e505eecU, 0x4b450ea9U,
+	0x86fa7c64U, 0xcaa56f28U, 0x59d188bbU, 0x4885cdaaU,
+	0xa452f646U, 0x3cecd0deU, 0x130b18f1U, 0xb103b253U,
+	0x1d8499ffU, 0xf556a317U, 0x5dd489bfU, 0x1cc4d8feU,
+	0xe80de50aU, 0x04dadee6U, 0x227755c0U, 0xefc8270dU,
+	0xf153a213U, 0xed48a50fU, 0x946efa76U, 0x8935bc6bU,
+	0x61e78683U, 0xe147a603U, 0x847afe66U, 0x7b790299U,
+	0x366650d4U, 0x65e28787U, 0x7cbcc09eU, 0x38e9d1daU,
+	0x1b011af9U, 0x2a7d57c8U, 0xf959a01bU, 0x2cf8d4ceU,
+	0x549ecab6U, 0xbd0cb15fU, 0x6de8858fU, 0xfa996318U,
+	0xe7c22505U, 0x76364094U, 0x460a4ca4U, 0x1f041bfdU,
+	0xcea06e2cU, 0x9ba13a79U, 0x4f400fadU, 0x2e7856ccU,
+	0x64a2c686U, 0x7a394398U, 0x853abf67U, 0xb3833051U,
+	0xebcd2609U, 0xbac97358U, 0xaed8764cU, 0x5e144abcU,
+	0x82ff7d60U, 0xe007e702U, 0x31a392d3U, 0xe3c72401U,
+	0xe6826404U, 0xdff42b3dU, 0x10cbdbf2U, 0xf7d62115U,
+	0x0b151ee9U, 0xb506b357U, 0x0d909defU, 0x30e3d3d2U,
+	0xd3fb2831U, 0x2b3d16c9U, 0xad18b54fU, 0xa512b747U,
+	0xb2c37150U, 0x575e09b5U, 0xa3973441U, 0xea8d6708U,
+	0x118b9af3U, 0xbecc725cU, 0xee88660cU, 0xdd74a93fU,
+	0x6f68078dU, 0xfc1ce01eU, 0x4e004eacU, 0xe402e606U,
+	0x66224484U, 0xc16fae23U, 0xe94da40bU, 0xcbe52e29U,
+	0x51db8ab3U, 0x28fdd5caU, 0x5c94c8beU, 0xd831e93aU,
+	0x326351d0U, 0x0a555fe8U, 0xb909b05bU, 0x70b3c392U,
+	0x1e445afcU, 0x92eb7970U, 0x5f540bbdU, 0xd971a83bU,
+	0x69ed848bU, 0x9ae17b78U, 0xcc20ec2eU, 0xac58f44eU,
+	0xd03beb32U, 0xd43eea36U, 0x08d5ddeaU, 0x20f7d7c2U,
+	0x535b08b1U, 0x09959cebU, 0xdbf12a39U, 0x2f3817cdU,
+	0x8bb53e69U, 0x124b59f0U, 0x97ae3975U, 0x9861f97aU,
+	0x21b796c3U, 0x025f5de0U, 0x14cedaf6U, 0x60a7c782U,
+	0x198198fbU, 0x25b297c7U, 0x0cd0dceeU, 0x065a5ce4U,
+	0x4dc08dafU, 0x62274580U, 0xa117b643U, 0x474a0da5U,
+	0xf3d32011U, 0x521b49b0U, 0x8875fd6aU, 0x93ab3871U,
+	0xd17baa33U, 0xb446f256U, 0x267254c4U, 0x2db895cfU,
+	0x87ba3d65U, 0xf2936110U, 0xb7863155U, 0xa7923545U,
+	0x434f0ca1U, 0xc02fef22U, 0x3b2912d9U, 0x4c80ccaeU,
+	0x55de8bb7U, 0x3f2c13ddU, 0xb849f15aU, 0x9c64f87eU,
+	0xf013e312U, 0x83bf3c61U, 0xa2d77540U, 0x41cf8ea3U,
+	0x72334190U, 0xdc34e83eU, 0x7e3c429cU, 0x77760195U,
+	0x059a9fe7U, 0xa6d27444U, 0x906bfb72U, 0xd6be6834U,
+	0x8d30bd6fU, 0x164e58f4U, 0x509bcbb2U, 0xe542a707U,
+	0x807fff62U, 0xdeb46a3cU, 0x18c1d9faU, 0x9d24b97fU,
+	0x3a6953d8U, 0x4a054fa8U, 0x813fbe63U, 0x448acea6U,
+	0x92b321afU, 0x8bed66b6U, 0x6e701e53U, 0x323b090fU,
+	0xfac13bc7U, 0x4e581673U, 0x3df4c900U, 0x1756412aU,
+	0x9173e2acU, 0x1353402eU, 0x9979e0a4U, 0xc2f735ffU,
+	0x69b5dc54U, 0x262a0c1bU, 0x79a1d844U, 0xdd2cf1e0U,
+	0x65badf58U, 0x54c69269U, 0xa3cf6c9eU, 0x1093832dU,
+	0x61bfde5cU, 0x337b480eU, 0xd929f0e4U, 0xc6f234fbU,
+	0x633f5c5eU, 0xd869b1e5U, 0x8562e7b8U, 0x5183d26cU,
+	0xec50bcd1U, 0xd3a370eeU, 0xd123f2ecU, 0xafc06f92U,
+	0xbbd16a86U, 0xc077b7fdU, 0x882da5b5U, 0x1a190327U,
+	0x8fe867b2U, 0x766e184bU, 0xabc56e96U, 0x20af8f1dU,
+	0x05c2c738U, 0x9839a1a5U, 0xe51affd8U, 0xc472b6f9U,
+	0x7e641a43U, 0xf6ce38cbU, 0x7b215a46U, 0x40d7977dU,
+	0x5a491367U, 0x0742453aU, 0xb951e884U, 0x6f305f52U,
+	0xeed03ed3U, 0xe915fcd4U, 0x6a751f57U, 0x9ffc63a2U,
+	0xcbbd76f6U, 0xb69e288bU, 0x9bf962a6U, 0x222f0d1fU,
+	0x1c9c8021U, 0xf78e79caU, 0x35fecb08U, 0x5303506eU,
+	0xd466b2e9U, 0xd526f3e8U, 0x5706516aU, 0x3a310b07U,
+	0x0602043bU, 0x04828639U, 0x9576e3a8U, 0x236f4c1eU,
+	0xc7b275faU, 0xac00ac91U, 0x39f1c804U, 0xad40ed90U,
+	0x5243116fU, 0xdfac73e2U, 0xf44ebac9U, 0x6b355e56U,
+	0x68f59d55U, 0x64fa9e59U, 0xcfb877f2U, 0x0347443eU,
+	0x0c888431U, 0x58c99165U, 0xa28f2d9fU, 0xe79a7ddaU,
+	0x2a250f17U, 0x667a1c5bU, 0xf50efbc8U, 0xe45abed9U,
+	0x088d8535U, 0x9033a3adU, 0xbfd46b82U, 0x1ddcc120U,
+	0xb15bea8cU, 0x5989d064U, 0xf10bfaccU, 0xb01bab8dU,
+	0x44d29679U, 0xa805ad95U, 0x8ea826b3U, 0x4317547eU,
+	0x5d8cd160U, 0x4197d67cU, 0x38b18905U, 0x25eacf18U,
+	0xcd38f5f0U, 0x4d98d570U, 0x28a58d15U, 0xd7a671eaU,
+	0x9ab923a7U, 0xc93df4f4U, 0xd063b3edU, 0x9436a2a9U,
+	0xb7de698aU, 0x86a224bbU, 0x5586d368U, 0x8027a7bdU,
+	0xf841b9c5U, 0x11d3c22cU, 0xc137f6fcU, 0x5646106bU,
+	0x4b1d5676U, 0xdae933e7U, 0xead53fd7U, 0xb3db688eU,
+	0x627f1d5fU, 0x377e490aU, 0xe39f7cdeU, 0x82a725bfU,
+	0xc87db5f5U, 0xd6e630ebU, 0x29e5cc14U, 0x1f5c4322U,
+	0x4712557aU, 0x1616002bU, 0x0207053fU, 0xf2cb39cfU,
+	0x2e200e13U, 0x4cd89471U, 0x9d7ce1a0U, 0x4f185772U,
+	0x4a5d1777U, 0x732b584eU, 0xbc14a881U, 0x5b095266U,
+	0xa7ca6d9aU, 0x19d9c024U, 0xa14fee9cU, 0x9c3ca0a1U,
+	0x7f245b42U, 0x87e265baU, 0x01c7c63cU, 0x09cdc434U,
+	0x1e1c0223U, 0xfb817ac6U, 0x0f484732U, 0x4652147bU,
+	0xbd54e980U, 0x1213012fU, 0x4257157fU, 0x71abda4cU,
+	0xc3b774feU, 0x50c3936dU, 0xe2df3ddfU, 0x48dd9575U,
+	0xcafd37f7U, 0x6db0dd50U, 0x4592d778U, 0x673a5d5aU,
+	0xfd04f9c0U, 0x8422a6b9U, 0xf04bbbcdU, 0x74ee9a49U,
+	0x9ebc22a3U, 0xa68a2c9bU, 0x15d6c328U, 0xdc6cb0e1U,
+	0xb29b298fU, 0x3e340a03U, 0xf38b78ceU, 0x75aedb48U,
+	0xc532f7f8U, 0x363e080bU, 0x60ff9f5dU, 0x0087873dU,
+	0x7ce49841U, 0x78e19945U, 0xa40aae99U, 0x8c28a4b1U,
+	0xff847bc2U, 0xa54aef98U, 0x772e594aU, 0x83e764beU,
+	0x276a4d1aU, 0xbe942a83U, 0x3b714a06U, 0x34be8a09U,
+	0x8d68e5b0U, 0xae802e93U, 0xb811a985U, 0xcc78b4f1U,
+	0xb55eeb88U, 0x896de4b4U, 0xa00faf9dU, 0xaa852f97U,
+	0xe11ffedcU, 0xcef836f3U, 0x0dc8c530U, 0xeb957ed6U,
+	0x5f0c5362U, 0xfec43ac3U, 0x24aa8e19U, 0x3f744b02U,
+	0x7da4d940U, 0x18998125U, 0x8aad27b7U, 0x8167e6bcU,
+	0x2b654e16U, 0x5e4c1263U, 0x1b594226U, 0x0b4d4636U,
+	0xef907fd2U, 0x6cf09c51U, 0x97f661aaU, 0xe05fbfddU,
+	0xf901f8c4U, 0x93f360aeU, 0x14968229U, 0x30bb8b0dU,
+	0x5ccc9061U, 0x2f604f12U, 0x0e080633U, 0xed10fdd0U,
+	0xdeec32e3U, 0x70eb9b4dU, 0xd2e331efU, 0xdba972e6U,
+	0xa945ec94U, 0x0a0d0737U, 0x3cb48801U, 0x7a611b47U,
+	0x21efce1cU, 0xba912b87U, 0xfc44b8c1U, 0x499dd474U,
+	0x2ca08c11U, 0x726b194fU, 0xb41eaa89U, 0x31fbca0cU,
+	0x96b620abU, 0xe6da3cdbU, 0x2de0cd10U, 0xe855bdd5U,
+	0x303b0b85U, 0x29654c9cU, 0xccf83479U, 0x90b32325U,
+	0x584911edU, 0xecd03c59U, 0x9f7ce32aU, 0xb5de6b00U,
+	0x33fbc886U, 0xb1db6a04U, 0x3bf1ca8eU, 0x607f1fd5U,
+	0xcb3df67eU, 0x84a22631U, 0xdb29f26eU, 0x7fa4dbcaU,
+	0xc732f572U, 0xf64eb843U, 0x014746b4U, 0xb21ba907U,
+	0xc337f476U, 0x91f36224U, 0x7ba1daceU, 0x647a1ed1U,
+	0xc1b77674U, 0x7ae19bcfU, 0x27eacd92U, 0xf30bf846U,
+	0x4ed896fbU, 0x712b5ac4U, 0x73abd8c6U, 0x0d4845b8U,
+	0x195940acU, 0x62ff9dd7U, 0x2aa58f9fU, 0xb891290dU,
+	0x2d604d98U, 0xd4e63261U, 0x094d44bcU, 0x8227a537U,
+	0xa74aed12U, 0x3ab18b8fU, 0x4792d5f2U, 0x66fa9cd3U,
+	0xdcec3069U, 0x544612e1U, 0xd9a9706cU, 0xe25fbd57U,
+	0xf8c1394dU, 0xa5ca6f10U, 0x1bd9c2aeU, 0xcdb87578U,
+	0x4c5814f9U, 0x4b9dd6feU, 0xc8fd357dU, 0x3d744988U,
+	0x69355cdcU, 0x141602a1U, 0x3971488cU, 0x80a72735U,
+	0xbe14aa0bU, 0x550653e0U, 0x9776e122U, 0xf18b7a44U,
+	0x76ee98c3U, 0x77aed9c2U, 0xf58e7b40U, 0x98b9212dU,
+	0xa48a2e11U, 0xa60aac13U, 0x37fec982U, 0x81e76634U,
+	0x653a5fd0U, 0x0e8886bbU, 0x9b79e22eU, 0x0fc8c7baU,
+	0xf0cb3b45U, 0x7d2459c8U, 0x56c690e3U, 0xc9bd747cU,
+	0xca7db77fU, 0xc672b473U, 0x6d305dd8U, 0xa1cf6e14U,
+	0xae00ae1bU, 0xfa41bb4fU, 0x000707b5U, 0x451257f0U,
+	0x88ad253dU, 0xc4f23671U, 0x5786d1e2U, 0x46d294f3U,
+	0xaa05af1fU, 0x32bb8987U, 0x1d5c41a8U, 0xbf54eb0aU,
+	0x13d3c0a6U, 0xfb01fa4eU, 0x5383d0e6U, 0x129381a7U,
+	0xe65abc53U, 0x0a8d87bfU, 0x2c200c99U, 0xe19f7e54U,
+	0xff04fb4aU, 0xe31ffc56U, 0x9a39a32fU, 0x8762e532U,
+	0x6fb0dfdaU, 0xef10ff5aU, 0x8a2da73fU, 0x752e5bc0U,
+	0x3831098dU, 0x6bb5dedeU, 0x72eb99c7U, 0x36be8883U,
+	0x155643a0U, 0x242a0e91U, 0xf70ef942U, 0x22af8d97U,
+	0x5ac993efU, 0xb35be806U, 0x63bfdcd6U, 0xf4ce3a41U,
+	0xe9957c5cU, 0x786119cdU, 0x485d15fdU, 0x115342a4U,
+	0xc0f73775U, 0x95f66320U, 0x411756f4U, 0x202f0f95U,
+	0x6af59fdfU, 0x746e1ac1U, 0x8b6de63eU, 0xbdd46908U,
+	0xe59a7f50U, 0xb49e2a01U, 0xa08f2f15U, 0x504313e5U,
+	0x8ca82439U, 0xee50be5bU, 0x3ff4cb8aU, 0xed907d58U,
+	0xe8d53d5dU, 0xd1a37264U, 0x1e9c82abU, 0xf981784cU,
+	0x054247b0U, 0xbb51ea0eU, 0x03c7c4b6U, 0x3eb48a8bU,
+	0xddac7168U, 0x256a4f90U, 0xa34fec16U, 0xab45ee1eU,
+	0xbc942809U, 0x590950ecU, 0xadc06d18U, 0xe4da3e51U,
+	0x1fdcc3aaU, 0xb09b2b05U, 0xe0df3f55U, 0xd323f066U,
+	0x613f5ed4U, 0xf24bb947U, 0x405717f5U, 0xea55bf5fU,
+	0x68751dddU, 0xcf38f77aU, 0xe71afd52U, 0xc5b27770U,
+	0x5f8cd3eaU, 0x26aa8c93U, 0x52c391e7U, 0xd666b063U,
+	0x3c340889U, 0x040206b1U, 0xb75ee902U, 0x7ee49acbU,
+	0x101303a5U, 0x9cbc2029U, 0x510352e4U, 0xd726f162U,
+	0x67baddd2U, 0x94b62221U, 0xc277b577U, 0xa20fad17U,
+	0xde6cb26bU, 0xda69b36fU, 0x068284b3U, 0x2ea08e9bU,
+	0x5d0c51e8U, 0x07c2c5b2U, 0xd5a67360U, 0x216f4e94U,
+	0x85e26730U, 0x1c1c00a9U, 0x99f9602cU, 0x9636a023U,
+	0x2fe0cf9aU, 0x0c0804b9U, 0x1a9983afU, 0x6ef09edbU,
+	0x17d6c1a2U, 0x2be5ce9eU, 0x028785b7U, 0x080d05bdU,
+	0x4397d4f6U, 0x6c701cd9U, 0xaf40ef1aU, 0x491d54fcU,
+	0xfd847948U, 0x5c4c10e9U, 0x8622a433U, 0x9dfc6128U,
+	0xdf2cf36aU, 0xba11ab0fU, 0x28250d9dU, 0x23efcc96U,
+	0x89ed643cU, 0xfcc43849U, 0xb9d1680cU, 0xa9c56c1cU,
+	0x4d1855f8U, 0xce78b67bU, 0x357e4b80U, 0x42d795f7U,
+	0x5b89d2eeU, 0x317b4a84U, 0xb61ea803U, 0x9233a127U,
+	0xfe44ba4bU, 0x8de86538U, 0xac802c19U, 0x4f98d7faU,
+	0x7c6418c9U, 0xd263b167U, 0x706b1bc5U, 0x792158ccU,
+	0x0bcdc6beU, 0xa8852d1dU, 0x9e3ca22bU, 0xd8e9316dU,
+	0x8367e436U, 0x181901adU, 0x5ecc92ebU, 0xeb15fe5eU,
+	0x8e28a63bU, 0xd0e33365U, 0x169680a3U, 0x9373e026U,
+	0x343e0a81U, 0x445216f1U, 0x8f68e73aU, 0x4add97ffU,
+	0x15988d03U, 0x0cc6ca1aU, 0xe95bb2ffU, 0xb510a5a3U,
+	0x7dea976bU, 0xc973badfU, 0xbadf65acU, 0x907ded86U,
+	0x16584e00U, 0x9478ec82U, 0x1e524c08U, 0x45dc9953U,
+	0xee9e70f8U, 0xa101a0b7U, 0xfe8a74e8U, 0x5a075d4cU,
+	0xe29173f4U, 0xd3ed3ec5U, 0x24e4c032U, 0x97b82f81U,
+	0xe69472f0U, 0xb450e4a2U, 0x5e025c48U, 0x41d99857U,
+	0xe414f0f2U, 0x5f421d49U, 0x02494b14U, 0xd6a87ec0U,
+	0x6b7b107dU, 0x5488dc42U, 0x56085e40U, 0x28ebc33eU,
+	0x3cfac62aU, 0x475c1b51U, 0x0f060919U, 0x9d32af8bU,
+	0x08c3cb1eU, 0xf145b4e7U, 0x2ceec23aU, 0xa78423b1U,
+	0x82e96b94U, 0x1f120d09U, 0x62315374U, 0x43591a55U,
+	0xf94fb6efU, 0x71e59467U, 0xfc0af6eaU, 0xc7fc3bd1U,
+	0xdd62bfcbU, 0x8069e996U, 0x3e7a4428U, 0xe81bf3feU,
+	0x69fb927fU, 0x6e3e5078U, 0xed5eb3fbU, 0x18d7cf0eU,
+	0x4c96da5aU, 0x31b58427U, 0x1cd2ce0aU, 0xa504a1b3U,
+	0x9bb72c8dU, 0x70a5d566U, 0xb2d567a4U, 0xd428fcc2U,
+	0x534d1e45U, 0x520d5f44U, 0xd02dfdc6U, 0xbd1aa7abU,
+	0x8129a897U, 0x83a92a95U, 0x125d4f04U, 0xa444e0b2U,
+	0x4099d956U, 0x2b2b003dU, 0xbeda64a8U, 0x2a6b413cU,
+	0xd568bdc3U, 0x5887df4eU, 0x73651665U, 0xec1ef2faU,
+	0xefde31f9U, 0xe3d132f5U, 0x4893db5eU, 0x846ce892U,
+	0x8ba3289dU, 0xdfe23dc9U, 0x25a48133U, 0x60b1d176U,
+	0xad0ea3bbU, 0xe151b0f7U, 0x72255764U, 0x63711275U,
+	0x8fa62999U, 0x17180f01U, 0x38ffc72eU, 0x9af76d8cU,
+	0x36704620U, 0xdea27cc8U, 0x76205660U, 0x37300721U,
+	0xc3f93ad5U, 0x2f2e0139U, 0x09838a1fU, 0xc43cf8d2U,
+	0xdaa77dccU, 0xc6bc7ad0U, 0xbf9a25a9U, 0xa2c163b4U,
+	0x4a13595cU, 0xcab379dcU, 0xaf8e21b9U, 0x508ddd46U,
+	0x1d928f0bU, 0x4e165858U, 0x57481f41U, 0x131d0e05U,
+	0x30f5c526U, 0x01898817U, 0xd2ad7fc4U, 0x070c0b11U,
+	0x7f6a1569U, 0x96f86e80U, 0x461c5a50U, 0xd16dbcc7U,
+	0xcc36fadaU, 0x5dc29f4bU, 0x6dfe937bU, 0x34f0c422U,
+	0xe554b1f3U, 0xb055e5a6U, 0x64b4d072U, 0x058c8913U,
+	0x4f561959U, 0x51cd9c47U, 0xaece60b8U, 0x9877ef8eU,
+	0xc039f9d6U, 0x913dac87U, 0x852ca993U, 0x75e09563U,
+	0xa90ba2bfU, 0xcbf338ddU, 0x1a574d0cU, 0xc833fbdeU,
+	0xcd76bbdbU, 0xf400f4e2U, 0x3b3f042dU, 0xdc22fecaU,
+	0x20e1c136U, 0x9ef26c88U, 0x26644230U, 0x1b170c0dU,
+	0xf80ff7eeU, 0x00c9c916U, 0x86ec6a90U, 0x8ee66898U,
+	0x9937ae8fU, 0x7caad66aU, 0x8863eb9eU, 0xc179b8d7U,
+	0x3a7f452cU, 0x9538ad83U, 0xc57cb9d3U, 0xf68076e0U,
+	0x449cd852U, 0xd7e83fc1U, 0x65f49173U, 0xcff639d9U,
+	0x4dd69b5bU, 0xea9b71fcU, 0xc2b97bd4U, 0xe011f1f6U,
+	0x7a2f556cU, 0x03090a15U, 0x77601761U, 0xf3c536e5U,
+	0x19978e0fU, 0x21a18037U, 0x92fd6f84U, 0x5b471c4dU,
+	0x35b08523U, 0xb91fa6afU, 0x74a0d462U, 0xf28577e4U,
+	0x42195b54U, 0xb115a4a7U, 0xe7d433f1U, 0x87ac2b91U,
+	0xfbcf34edU, 0xffca35e9U, 0x23210235U, 0x0b03081dU,
+	0x78afd76eU, 0x22614334U, 0xf005f5e6U, 0x04ccc812U,
+	0xa041e1b6U, 0x39bf862fU, 0xbc5ae6aaU, 0xb39526a5U,
+	0x0a43491cU, 0x29ab823fU, 0x3f3a0529U, 0x4b53185dU,
+	0x32754724U, 0x0e464818U, 0x27240331U, 0x2dae833bU,
+	0x66345270U, 0x49d39a5fU, 0x8ae3699cU, 0x6cbed27aU,
+	0xd827ffceU, 0x79ef966fU, 0xa38122b5U, 0xb85fe7aeU,
+	0xfa8f75ecU, 0x9fb22d89U, 0x0d868b1bU, 0x064c4a10U,
+	0xac4ee2baU, 0xd967becfU, 0x9c72ee8aU, 0x8c66ea9aU,
+	0x68bbd37eU, 0xebdb30fdU, 0x10ddcd06U, 0x67741371U,
+	0x7e2a5468U, 0x14d8cc02U, 0x93bd2e85U, 0xb79027a1U,
+	0xdbe73ccdU, 0xa84be3beU, 0x8923aa9fU, 0x6a3b517cU,
+	0x59c79e4fU, 0xf7c037e1U, 0x55c89d43U, 0x5c82de4aU,
+	0x2e6e4038U, 0x8d26ab9bU, 0xbb9f24adU, 0xfd4ab7ebU,
+	0xa6c462b0U, 0x3dba872bU, 0x7b6f146dU, 0xceb678d8U,
+	0xab8b20bdU, 0xf540b5e3U, 0x33350625U, 0xb6d066a0U,
+	0x119d8c07U, 0x61f19077U, 0xaacb61bcU, 0x6f7e1179U,
+	0x353a0f81U, 0x2c644898U, 0xc9f9307dU, 0x95b22721U,
+	0x5d4815e9U, 0xe9d1385dU, 0x9a7de72eU, 0xb0df6f04U,
+	0x36facc82U, 0xb4da6e00U, 0x3ef0ce8aU, 0x657e1bd1U,
+	0xce3cf27aU, 0x81a32235U, 0xde28f66aU, 0x7aa5dfceU,
+	0xc233f176U, 0xf34fbc47U, 0x044642b0U, 0xb71aad03U,
+	0xc636f072U, 0x94f26620U, 0x7ea0decaU, 0x617b1ad5U,
+	0xc4b67270U, 0x7fe09fcbU, 0x22ebc996U, 0xf60afc42U,
+	0x4bd992ffU, 0x742a5ec0U, 0x76aadcc2U, 0x084941bcU,
+	0x1c5844a8U, 0x67fe99d3U, 0x2fa48b9bU, 0xbd902d09U,
+	0x2861499cU, 0xd1e73665U, 0x0c4c40b8U, 0x8726a133U,
+	0xa24be916U, 0x3fb08f8bU, 0x4293d1f6U, 0x63fb98d7U,
+	0xd9ed346dU, 0x514716e5U, 0xdca87468U, 0xe75eb953U,
+	0xfdc03d49U, 0xa0cb6b14U, 0x1ed8c6aaU, 0xc8b9717cU,
+	0x495910fdU, 0x4e9cd2faU, 0xcdfc3179U, 0x38754d8cU,
+	0x6c3458d8U, 0x111706a5U, 0x3c704c88U, 0x85a62331U,
+	0xbb15ae0fU, 0x500757e4U, 0x9277e526U, 0xf48a7e40U,
+	0x73ef9cc7U, 0x72afddc6U, 0xf08f7f44U, 0x9db82529U,
+	0xa18b2a15U, 0xa30ba817U, 0x32ffcd86U, 0x84e66230U,
+	0x603b5bd4U, 0x0b8982bfU, 0x9e78e62aU, 0x0ac9c3beU,
+	0xf5ca3f41U, 0x78255dccU, 0x53c794e7U, 0xccbc7078U,
+	0xcf7cb37bU, 0xc373b077U, 0x683159dcU, 0xa4ce6a10U,
+	0xab01aa1fU, 0xff40bf4bU, 0x050603b1U, 0x401353f4U,
+	0x8dac2139U, 0xc1f33275U, 0x5287d5e6U, 0x43d390f7U,
+	0xaf04ab1bU, 0x37ba8d83U, 0x185d45acU, 0xba55ef0eU,
+	0x16d2c4a2U, 0xfe00fe4aU, 0x5682d4e2U, 0x179285a3U,
+	0xe35bb857U, 0x0f8c83bbU, 0x2921089dU, 0xe49e7a50U,
+	0xfa05ff4eU, 0xe61ef852U, 0x9f38a72bU, 0x8263e136U,
+	0x6ab1dbdeU, 0xea11fb5eU, 0x8f2ca33bU, 0x702f5fc4U,
+	0x3d300d89U, 0x6eb4dadaU, 0x77ea9dc3U, 0x33bf8c87U,
+	0x105747a4U, 0x212b0a95U, 0xf20ffd46U, 0x27ae8993U,
+	0x5fc897ebU, 0xb65aec02U, 0x66bed8d2U, 0xf1cf3e45U,
+	0xec947858U, 0x7d601dc9U, 0x4d5c11f9U, 0x145246a0U,
+	0xc5f63371U, 0x90f76724U, 0x441652f0U, 0x252e0b91U,
+	0x6ff49bdbU, 0x716f1ec5U, 0x8e6ce23aU, 0xb8d56d0cU,
+	0xe09b7b54U, 0xb19f2e05U, 0xa58e2b11U, 0x554217e1U,
+	0x89a9203dU, 0xeb51ba5fU, 0x3af5cf8eU, 0xe891795cU,
+	0xedd43959U, 0xd4a27660U, 0x1b9d86afU, 0xfc807c48U,
+	0x004343b4U, 0xbe50ee0aU, 0x06c6c0b2U, 0x3bb58e8fU,
+	0xd8ad756cU, 0x206b4b94U, 0xa64ee812U, 0xae44ea1aU,
+	0xb9952c0dU, 0x5c0854e8U, 0xa8c1691cU, 0xe1db3a55U,
+	0x1addc7aeU, 0xb59a2f01U, 0xe5de3b51U, 0xd622f462U,
+	0x643e5ad0U, 0xf74abd43U, 0x455613f1U, 0xef54bb5bU,
+	0x6d7419d9U, 0xca39f37eU, 0xe21bf956U, 0xc0b37374U,
+	0x5a8dd7eeU, 0x23ab8897U, 0x57c295e3U, 0xd367b467U,
+	0x39350c8dU, 0x010302b5U, 0xb25fed06U, 0x7be59ecfU,
+	0x151207a1U, 0x99bd242dU, 0x540256e0U, 0xd227f566U,
+	0x62bbd9d6U, 0x91b72625U, 0xc776b173U, 0xa70ea913U,
+	0xdb6db66fU, 0xdf68b76bU, 0x038380b7U, 0x2ba18a9fU,
+	0x580d55ecU, 0x02c3c1b6U, 0xd0a77764U, 0x246e4a90U,
+	0x80e36334U, 0x191d04adU, 0x9cf86428U, 0x9337a427U,
+	0x2ae1cb9eU, 0x090900bdU, 0x1f9887abU, 0x6bf19adfU,
+	0x12d7c5a6U, 0x2ee4ca9aU, 0x078681b3U, 0x0d0c01b9U,
+	0x4696d0f2U, 0x697118ddU, 0xaa41eb1eU, 0x4c1c50f8U,
+	0xf8857d4cU, 0x594d14edU, 0x8323a037U, 0x98fd652cU,
+	0xda2df76eU, 0xbf10af0bU, 0x2d240999U, 0x26eec892U,
+	0x8cec6038U, 0xf9c53c4dU, 0xbcd06c08U, 0xacc46818U,
+	0x481951fcU, 0xcb79b27fU, 0x307f4f84U, 0x47d691f3U,
+	0x5e88d6eaU, 0x347a4e80U, 0xb31fac07U, 0x9732a523U,
+	0xfb45be4fU, 0x88e9613cU, 0xa981281dU, 0x4a99d3feU,
+	0x79651ccdU, 0xd762b563U, 0x756a1fc1U, 0x7c205cc8U,
+	0x0eccc2baU, 0xad842919U, 0x9b3da62fU, 0xdde83569U,
+	0x8666e032U, 0x1d1805a9U, 0x5bcd96efU, 0xee14fa5aU,
+	0x8b29a23fU, 0xd5e23761U, 0x139784a7U, 0x9672e422U,
+	0x313f0e85U, 0x415312f5U, 0x8a69e33eU, 0x4fdc93fbU,
+	0x1f9a850bU, 0x06c4c212U, 0xe359baf7U, 0xbf12adabU,
+	0x77e89f63U, 0xc371b2d7U, 0xb0dd6da4U, 0x9a7fe58eU,
+	0x1c5a4608U, 0x9e7ae48aU, 0x14504400U, 0x4fde915bU,
+	0xe49c78f0U, 0xab03a8bfU, 0xf4887ce0U, 0x50055544U,
+	0xe8937bfcU, 0xd9ef36cdU, 0x2ee6c83aU, 0x9dba2789U,
+	0xec967af8U, 0xbe52ecaaU, 0x54005440U, 0x4bdb905fU,
+	0xee16f8faU, 0x55401541U, 0x084b431cU, 0xdcaa76c8U,
+	0x61791875U, 0x5e8ad44aU, 0x5c0a5648U, 0x22e9cb36U,
+	0x36f8ce22U, 0x4d5e1359U, 0x05040111U, 0x9730a783U,
+	0x02c1c316U, 0xfb47bcefU, 0x26ecca32U, 0xad862bb9U,
+	0x88eb639cU, 0x15100501U, 0x68335b7cU, 0x495b125dU,
+	0xf34dbee7U, 0x7be79c6fU, 0xf608fee2U, 0xcdfe33d9U,
+	0xd760b7c3U, 0x8a6be19eU, 0x34784c20U, 0xe219fbf6U,
+	0x63f99a77U, 0x643c5870U, 0xe75cbbf3U, 0x12d5c706U,
+	0x4694d252U, 0x3bb78c2fU, 0x16d0c602U, 0xaf06a9bbU,
+	0x91b52485U, 0x7aa7dd6eU, 0xb8d76facU, 0xde2af4caU,
+	0x594f164dU, 0x580f574cU, 0xda2ff5ceU, 0xb718afa3U,
+	0x8b2ba09fU, 0x89ab229dU, 0x185f470cU, 0xae46e8baU,
+	0x4a9bd15eU, 0x21290835U, 0xb4d86ca0U, 0x20694934U,
+	0xdf6ab5cbU, 0x5285d746U, 0x79671e6dU, 0xe61cfaf2U,
+	0xe5dc39f1U, 0xe9d33afdU, 0x4291d356U, 0x8e6ee09aU,
+	0x81a12095U, 0xd5e035c1U, 0x2fa6893bU, 0x6ab3d97eU,
+	0xa70cabb3U, 0xeb53b8ffU, 0x78275f6cU, 0x69731a7dU,
+	0x85a42191U, 0x1d1a0709U, 0x32fdcf26U, 0x90f56584U,
+	0x3c724e28U, 0xd4a074c0U, 0x7c225e68U, 0x3d320f29U,
+	0xc9fb32ddU, 0x252c0931U, 0x03818217U, 0xce3ef0daU,
+	0xd0a575c4U, 0xccbe72d8U, 0xb5982da1U, 0xa8c36bbcU,
+	0x40115154U, 0xc0b171d4U, 0xa58c29b1U, 0x5a8fd54eU,
+	0x17908703U, 0x44145050U, 0x5d4a1749U, 0x191f060dU,
+	0x3af7cd2eU, 0x0b8b801fU, 0xd8af77ccU, 0x0d0e0319U,
+	0x75681d61U, 0x9cfa6688U, 0x4c1e5258U, 0xdb6fb4cfU,
+	0xc634f2d2U, 0x57c09743U, 0x67fc9b73U, 0x3ef2cc2aU,
+	0xef56b9fbU, 0xba57edaeU, 0x6eb6d87aU, 0x0f8e811bU,
+	0x45541151U, 0x5bcf944fU, 0xa4cc68b0U, 0x9275e786U,
+	0xca3bf1deU, 0x9b3fa48fU, 0x8f2ea19bU, 0x7fe29d6bU,
+	0xa309aab7U, 0xc1f130d5U, 0x10554504U, 0xc231f3d6U,
+	0xc774b3d3U, 0xfe02fceaU, 0x313d0c25U, 0xd620f6c2U,
+	0x2ae3c93eU, 0x94f06480U, 0x2c664a38U, 0x11150405U,
+	0xf20dffe6U, 0x0acbc11eU, 0x8cee6298U, 0x84e46090U,
+	0x9335a687U, 0x76a8de62U, 0x8261e396U, 0xcb7bb0dfU,
+	0x307d4d24U, 0x9f3aa58bU, 0xcf7eb1dbU, 0xfc827ee8U,
+	0x4e9ed05aU, 0xddea37c9U, 0x6ff6997bU, 0xc5f431d1U,
+	0x47d49353U, 0xe09979f4U, 0xc8bb73dcU, 0xea13f9feU,
+	0x702d5d64U, 0x090b021dU, 0x7d621f69U, 0xf9c73eedU,
+	0x13958607U, 0x2ba3883fU, 0x98ff678cU, 0x51451445U,
+	0x3fb28d2bU, 0xb31daea7U, 0x7ea2dc6aU, 0xf8877fecU,
+	0x481b535cU, 0xbb17acafU, 0xedd63bf9U, 0x8dae2399U,
+	0xf1cd3ce5U, 0xf5c83de1U, 0x29230a3dU, 0x01010015U,
+	0x72addf66U, 0x28634b3cU, 0xfa07fdeeU, 0x0ecec01aU,
+	0xaa43e9beU, 0x33bd8e27U, 0xb658eea2U, 0xb9972eadU,
+	0x00414114U, 0x23a98a37U, 0x35380d21U, 0x41511055U,
+	0x38774f2cU, 0x04444010U, 0x2d260b39U, 0x27ac8b33U,
+	0x6c365a78U, 0x43d19257U, 0x80e16194U, 0x66bcda72U,
+	0xd225f7c6U, 0x73ed9e67U, 0xa9832abdU, 0xb25defa6U,
+	0xf08d7de4U, 0x95b02581U, 0x07848313U, 0x0c4e4218U,
+	0xa64ceab2U, 0xd365b6c7U, 0x9670e682U, 0x8664e292U,
+	0x62b9db76U, 0xe1d938f5U, 0x1adfc50eU, 0x6d761b79U,
+	0x74285c60U, 0x1edac40aU, 0x99bf268dU, 0xbd922fa9U,
+	0xd1e534c5U, 0xa249ebb6U, 0x8321a297U, 0x60395974U,
+	0x53c59647U, 0xfdc23fe9U, 0x5fca954bU, 0x5680d642U,
+	0x246c4830U, 0x8724a393U, 0xb19d2ca5U, 0xf748bfe3U,
+	0xacc66ab8U, 0x37b88f23U, 0x716d1c65U, 0xc4b470d0U,
+	0xa18928b5U, 0xff42bdebU, 0x39370e2dU, 0xbcd26ea8U,
+	0x1b9f840fU, 0x6bf3987fU, 0xa0c969b4U, 0x657c1971U,
+	0x914fde50U, 0x88119949U, 0x6d8ce1acU, 0x31c7f6f0U,
+	0xf93dc438U, 0x4da4e98cU, 0x3e0836ffU, 0x14aabed5U,
+	0x928f1d53U, 0x10afbfd1U, 0x9a851f5bU, 0xc10bca00U,
+	0x6a4923abU, 0x25d6f3e4U, 0x7a5d27bbU, 0xded00e1fU,
+	0x664620a7U, 0x573a6d96U, 0xa0339361U, 0x136f7cd2U,
+	0x624321a3U, 0x3087b7f1U, 0xdad50f1bU, 0xc50ecb04U,
+	0x60c3a3a1U, 0xdb954e1aU, 0x869e1847U, 0x527f2d93U,
+	0xefac432eU, 0xd05f8f11U, 0xd2df0d13U, 0xac3c906dU,
+	0xb82d9579U, 0xc38b4802U, 0x8bd15a4aU, 0x19e5fcd8U,
+	0x8c14984dU, 0x7592e7b4U, 0xa8399169U, 0x235370e2U,
+	0x063e38c7U, 0x9bc55e5aU, 0xe6e60027U, 0xc78e4906U,
+	0x7d98e5bcU, 0xf532c734U, 0x78dda5b9U, 0x432b6882U,
+	0x59b5ec98U, 0x04bebac5U, 0xbaad177bU, 0x6ccca0adU,
+	0xed2cc12cU, 0xeae9032bU, 0x6989e0a8U, 0x9c009c5dU,
+	0xc8418909U, 0xb562d774U, 0x98059d59U, 0x21d3f2e0U,
+	0x1f607fdeU, 0xf4728635U, 0x360234f7U, 0x50ffaf91U,
+	0xd79a4d16U, 0xd6da0c17U, 0x54faae95U, 0x39cdf4f8U,
+	0x05fefbc4U, 0x077e79c6U, 0x968a1c57U, 0x2093b3e1U,
+	0xc44e8a05U, 0xaffc536eU, 0x3a0d37fbU, 0xaebc126fU,
+	0x51bfee90U, 0xdc508c1dU, 0xf7b24536U, 0x68c9a1a9U,
+	0x6b0962aaU, 0x670661a6U, 0xcc44880dU, 0x00bbbbc1U,
+	0x0f747bceU, 0x5b356e9aU, 0xa173d260U, 0xe4668225U,
+	0x29d9f0e8U, 0x6586e3a4U, 0xf6f20437U, 0xe7a64126U,
+	0x0b717acaU, 0x93cf5c52U, 0xbc28947dU, 0x1e203edfU,
+	0xb2a71573U, 0x5a752f9bU, 0xf2f70533U, 0xb3e75472U,
+	0x472e6986U, 0xabf9526aU, 0x8d54d94cU, 0x40ebab81U,
+	0x5e702e9fU, 0x426b2983U, 0x3b4d76faU, 0x261630e7U,
+	0xcec40a0fU, 0x4e642a8fU, 0x2b5972eaU, 0xd45a8e15U,
+	0x9945dc58U, 0xcac10b0bU, 0xd39f4c12U, 0x97ca5d56U,
+	0xb4229675U, 0x855edb44U, 0x567a2c97U, 0x83db5842U,
+	0xfbbd463aU, 0x122f3dd3U, 0xc2cb0903U, 0x55baef94U,
+	0x48e1a989U, 0xd915cc18U, 0xe929c028U, 0xb0279771U,
+	0x6183e2a0U, 0x3482b6f5U, 0xe0638321U, 0x815bda40U,
+	0xcb814a0aU, 0xd51acf14U, 0x2a1933ebU, 0x1ca0bcddU,
+	0x44eeaa85U, 0x15eaffd4U, 0x01fbfac0U, 0xf137c630U,
+	0x2ddcf1ecU, 0x4f246b8eU, 0x9e801e5fU, 0x4ce4a88dU,
+	0x49a1e888U, 0x70d7a7b1U, 0xbfe8577eU, 0x58f5ad99U,
+	0xa4369265U, 0x1a253fdbU, 0xa2b31163U, 0x9fc05f5eU,
+	0x7cd8a4bdU, 0x841e9a45U, 0x023b39c3U, 0x0a313bcbU,
+	0x1de0fddcU, 0xf87d8539U, 0x0cb4b8cdU, 0x45aeeb84U,
+	0xbea8167fU, 0x11effed0U, 0x41abea80U, 0x725725b3U,
+	0xc04b8b01U, 0x533f6c92U, 0xe123c220U, 0x4b216a8aU,
+	0xc901c808U, 0x6e4c22afU, 0x466e2887U, 0x64c6a2a5U,
+	0xfef8063fU, 0x87de5946U, 0xf3b74432U, 0x771265b6U,
+	0x9d40dd5cU, 0xa576d364U, 0x162a3cd7U, 0xdf904f1eU,
+	0xb167d670U, 0x3dc8f5fcU, 0xf0778731U, 0x765224b7U,
+	0xc6ce0807U, 0x35c2f7f4U, 0x630360a2U, 0x037b78c2U,
+	0x7f1867beU, 0x7b1d66baU, 0xa7f65166U, 0x8fd45b4eU,
+	0xfc78843dU, 0xa6b61067U, 0x74d2a6b5U, 0x801b9b41U,
+	0x2496b2e5U, 0xbd68d57cU, 0x388db5f9U, 0x374275f6U,
+	0x8e941a4fU, 0xad7cd16cU, 0xbbed567aU, 0xcf844b0eU,
+	0xb6a21477U, 0x8a911b4bU, 0xa3f35062U, 0xa979d068U,
+	0xe2e30123U, 0xcd04c90cU, 0x0e343acfU, 0xe8698129U,
+	0x5cf0ac9dU, 0xfd38c53cU, 0x275671e6U, 0x3c88b4fdU,
+	0x7e5826bfU, 0x1b657edaU, 0x8951d848U, 0x829b1943U,
+	0x2899b1e9U, 0x5db0ed9cU, 0x18a5bdd9U, 0x08b1b9c9U,
+	0xec6c802dU, 0x6f0c63aeU, 0x940a9e55U, 0xe3a34022U,
+	0xfafd073bU, 0x900f9f51U, 0x176a7dd6U, 0x334774f2U,
+	0x5f306f9eU, 0x2c9cb0edU, 0x0df4f9ccU, 0xeeec022fU,
+	0xdd10cd1cU, 0x731764b2U, 0xd11fce10U, 0xd8558d19U,
+	0xaab9136bU, 0x09f1f8c8U, 0x3f4877feU, 0x799de4b8U,
+	0x221331e3U, 0xb96dd478U, 0xffb8473eU, 0x4a612b8bU,
+	0x2f5c73eeU, 0x7197e6b0U, 0xb7e25576U, 0x320735f3U,
+	0x954adf54U, 0xe526c324U, 0x2e1c32efU, 0xeba9422aU,
+	0xd3a675fbU, 0xcaf832e2U, 0x2f654a07U, 0x732e5d5bU,
+	0xbbd46f93U, 0x0f4d4227U, 0x7ce19d54U, 0x5643157eU,
+	0xd066b6f8U, 0x5246147aU, 0xd86cb4f0U, 0x83e261abU,
+	0x28a08800U, 0x673f584fU, 0x38b48c10U, 0x9c39a5b4U,
+	0x24af8b0cU, 0x15d3c63dU, 0xe2da38caU, 0x5186d779U,
+	0x20aa8a08U, 0x726e1c5aU, 0x983ca4b0U, 0x87e760afU,
+	0x222a080aU, 0x997ce5b1U, 0xc477b3ecU, 0x10968638U,
+	0xad45e885U, 0x92b624baU, 0x9036a6b8U, 0xeed53bc6U,
+	0xfac43ed2U, 0x8162e3a9U, 0xc938f1e1U, 0x5b0c5773U,
+	0xcefd33e6U, 0x377b4c1fU, 0xead03ac2U, 0x61badb49U,
+	0x44d7936cU, 0xd92cf5f1U, 0xa40fab8cU, 0x8567e2adU,
+	0x3f714e17U, 0xb7db6c9fU, 0x3a340e12U, 0x01c2c329U,
+	0x1b5c4733U, 0x4657116eU, 0xf844bcd0U, 0x2e250b06U,
+	0xafc56a87U, 0xa800a880U, 0x2b604b03U, 0xdee937f6U,
+	0x8aa822a2U, 0xf78b7cdfU, 0xdaec36f2U, 0x633a594bU,
+	0x5d89d475U, 0xb69b2d9eU, 0x74eb9f5cU, 0x1216043aU,
+	0x9573e6bdU, 0x9433a7bcU, 0x1613053eU, 0x7b245f53U,
+	0x4717506fU, 0x4597d26dU, 0xd463b7fcU, 0x627a184aU,
+	0x86a721aeU, 0xed15f8c5U, 0x78e49c50U, 0xec55b9c4U,
+	0x1356453bU, 0x9eb927b6U, 0xb55bee9dU, 0x2a200a02U,
+	0x29e0c901U, 0x25efca0dU, 0x8ead23a6U, 0x4252106aU,
+	0x4d9dd065U, 0x19dcc531U, 0xe39a79cbU, 0xa68f298eU,
+	0x6b305b43U, 0x276f480fU, 0xb41baf9cU, 0xa54fea8dU,
+	0x4998d161U, 0xd126f7f9U, 0xfec13fd6U, 0x5cc99574U,
+	0xf04ebed8U, 0x189c8430U, 0xb01eae98U, 0xf10effd9U,
+	0x05c7c22dU, 0xe910f9c1U, 0xcfbd72e7U, 0x0202002aU,
+	0x1c998534U, 0x00828228U, 0x79a4dd51U, 0x64ff9b4cU,
+	0x8c2da1a4U, 0x0c8d8124U, 0x69b0d941U, 0x96b325beU,
+	0xdbac77f3U, 0x8828a0a0U, 0x9176e7b9U, 0xd523f6fdU,
+	0xf6cb3ddeU, 0xc7b770efU, 0x1493873cU, 0xc132f3e9U,
+	0xb954ed91U, 0x50c69678U, 0x8022a2a8U, 0x1753443fU,
+	0x0a080222U, 0x9bfc67b3U, 0xabc06b83U, 0xf2ce3cdaU,
+	0x236a490bU, 0x766b1d5eU, 0xa28a288aU, 0xc3b271ebU,
+	0x8968e1a1U, 0x97f364bfU, 0x68f09840U, 0x5e491776U,
+	0x0607012eU, 0x5703547fU, 0x4312516bU, 0xb3de6d9bU,
+	0x6f355a47U, 0x0dcdc025U, 0xdc69b5f4U, 0x0e0d0326U,
+	0x0b484323U, 0x323e0c1aU, 0xfd01fcd5U, 0x1a1c0632U,
+	0xe6df39ceU, 0x58cc9470U, 0xe05abac8U, 0xdd29f4f5U,
+	0x3e310f16U, 0xc6f731eeU, 0x40d29268U, 0x48d89060U,
+	0x5f095677U, 0xba942e92U, 0x4e5d1366U, 0x0747402fU,
+	0xfc41bdd4U, 0x5306557bU, 0x0342412bU, 0x30be8e18U,
+	0x82a220aaU, 0x11d6c739U, 0xa3ca698bU, 0x09c8c121U,
+	0x8be863a3U, 0x2ca58904U, 0x0487832cU, 0x262f090eU,
+	0xbc11ad94U, 0xc537f2edU, 0xb15eef99U, 0x35fbce1dU,
+	0xdfa976f7U, 0xe79f78cfU, 0x54c3977cU, 0x9d79e4b5U,
+	0xf38e7ddbU, 0x7f215e57U, 0xb29e2c9aU, 0x34bb8f1cU,
+	0x8427a3acU, 0x772b5c5fU, 0x21eacb09U, 0x4192d369U,
+	0x3df1cc15U, 0x39f4cd11U, 0xe51ffacdU, 0xcd3df0e5U,
+	0xbe912f96U, 0xe45fbbccU, 0x363b0d1eU, 0xc2f230eaU,
+	0x667f194eU, 0xff817ed7U, 0x7a641e52U, 0x75abde5dU,
+	0xcc7db1e4U, 0xef957ac7U, 0xf904fdd1U, 0x8d6de0a5U,
+	0xf44bbfdcU, 0xc878b0e0U, 0xe11afbc9U, 0xeb907bc3U,
+	0xa00aaa88U, 0x8fed62a7U, 0x4cdd9164U, 0xaa802a82U,
+	0x1e190736U, 0xbfd16e97U, 0x65bfda4dU, 0x7e611f56U,
+	0x3cb18d14U, 0x598cd571U, 0xcbb873e3U, 0xc072b2e8U,
+	0x6a701a42U, 0x1f594637U, 0x5a4c1672U, 0x4a581262U,
+	0xae852b86U, 0x2de5c805U, 0xd6e335feU, 0xa14aeb89U,
+	0xb814ac90U, 0xd2e634faU, 0x5583d67dU, 0x71aedf59U,
+	0x1dd9c435U, 0x6e751b46U, 0x4f1d5267U, 0xac05a984U,
+	0x9ff966b7U, 0x31fecf19U, 0x93f665bbU, 0x9abc26b2U,
+	0xe850b8c0U, 0x4b185363U, 0x7da1dc55U, 0x3b744f13U,
+	0x60fa9a48U, 0xfb847fd3U, 0xbd51ec95U, 0x08888020U,
+	0x6db5d845U, 0x337e4d1bU, 0xf50bfeddU, 0x70ee9e58U,
+	0xd7a374ffU, 0xa7cf688fU, 0x6cf59944U, 0xa940e981U,
+	0x4c763ab4U, 0x55287dadU, 0xb0b50548U, 0xecfe1214U,
+	0x240420dcU, 0x909d0d68U, 0xe331d21bU, 0xc9935a31U,
+	0x4fb6f9b7U, 0xcd965b35U, 0x47bcfbbfU, 0x1c322ee4U,
+	0xb770c74fU, 0xf8ef1700U, 0xa764c35fU, 0x03e9eafbU,
+	0xbb7fc443U, 0x8a038972U, 0x7d0a7785U, 0xce569836U,
+	0xbf7ac547U, 0xedbe5315U, 0x07ecebffU, 0x18372fe0U,
+	0xbdfa4745U, 0x06acaafeU, 0x5ba7fca3U, 0x8f46c977U,
+	0x3295a7caU, 0x0d666bf5U, 0x0fe6e9f7U, 0x71057489U,
+	0x6514719dU, 0x1eb2ace6U, 0x56e8beaeU, 0xc4dc183cU,
+	0x512d7ca9U, 0xa8ab0350U, 0x7500758dU, 0xfe6a9406U,
+	0xdb07dc23U, 0x46fcbabeU, 0x3bdfe4c3U, 0x1ab7ade2U,
+	0xa0a10158U, 0x280b23d0U, 0xa5e4415dU, 0x9e128c66U,
+	0x848c087cU, 0xd9875e21U, 0x6794f39fU, 0xb1f54449U,
+	0x301525c8U, 0x37d0e7cfU, 0xb4b0044cU, 0x413978b9U,
+	0x15786dedU, 0x685b3390U, 0x453c79bdU, 0xfcea1604U,
+	0xc2599b3aU, 0x294b62d1U, 0xeb3bd013U, 0x8dc64b75U,
+	0x0aa3a9f2U, 0x0be3e8f3U, 0x89c34a71U, 0xe4f4101cU,
+	0xd8c71f20U, 0xda479d22U, 0x4bb3f8b3U, 0xfdaa5705U,
+	0x19776ee1U, 0x72c5b78aU, 0xe734d31fU, 0x7385f68bU,
+	0x8c860a74U, 0x016968f9U, 0x2a8ba1d2U, 0xb5f0454dU,
+	0xb630864eU, 0xba3f8542U, 0x117d6ce9U, 0xdd825f25U,
+	0xd24d9f2aU, 0x860c8a7eU, 0x7c4a3684U, 0x395f66c1U,
+	0xf4e0140cU, 0xb8bf0740U, 0x2bcbe0d3U, 0x3a9fa5c2U,
+	0xd6489e2eU, 0x4ef6b8b6U, 0x61117099U, 0xc319da3bU,
+	0x6f9ef197U, 0x874ccb7fU, 0x2fcee1d7U, 0x6edeb096U,
+	0x9a178d62U, 0x76c0b68eU, 0x506d3da8U, 0x9dd24f65U,
+	0x8349ca7bU, 0x9f52cd67U, 0xe674921eU, 0xfb2fd403U,
+	0x13fdeeebU, 0x935dce6bU, 0xf660960eU, 0x09636af1U,
+	0x447c38bcU, 0x17f8efefU, 0x0ea6a8f6U, 0x4af3b9b2U,
+	0x691b7291U, 0x58673fa0U, 0x8b43c873U, 0x5ee2bca6U,
+	0x2684a2deU, 0xcf16d937U, 0x1ff2ede7U, 0x88830b70U,
+	0x95d84d6dU, 0x042c28fcU, 0x341024ccU, 0x6d1e7395U,
+	0xbcba0644U, 0xe9bb5211U, 0x3d5a67c5U, 0x5c623ea4U,
+	0x16b8aeeeU, 0x08232bf0U, 0xf720d70fU, 0xc1995839U,
+	0x99d74e61U, 0xc8d31b30U, 0xdcc21e24U, 0x2c0e22d4U,
+	0xf0e51508U, 0x921d8f6aU, 0x43b9fabbU, 0x91dd4c69U,
+	0x94980c6cU, 0xadee4355U, 0x62d1b39aU, 0x85cc497dU,
+	0x790f7681U, 0xc71cdb3fU, 0x7f8af587U, 0x42f9bbbaU,
+	0xa1e14059U, 0x59277ea1U, 0xdf02dd27U, 0xd708df2fU,
+	0xc0d91938U, 0x254461ddU, 0xd18d5c29U, 0x98970f60U,
+	0x6391f29bU, 0xccd61a34U, 0x9c920e64U, 0xaf6ec157U,
+	0x1d726fe5U, 0x8e068876U, 0x3c1a26c4U, 0x96188e6eU,
+	0x14382cecU, 0xb375c64bU, 0x9b57cc63U, 0xb9ff4641U,
+	0x23c1e2dbU, 0x5ae7bda2U, 0x2e8ea0d6U, 0xaa2b8152U,
+	0x407939b8U, 0x784f3780U, 0xcb13d833U, 0x02a9abfaU,
+	0x6c5e3294U, 0xe0f11118U, 0x2d4e63d5U, 0xab6bc053U,
+	0x1bf7ece3U, 0xe8fb1310U, 0xbe3a8446U, 0xde429c26U,
+	0xa221835aU, 0xa624825eU, 0x7acfb582U, 0x52edbfaaU,
+	0x214160d9U, 0x7b8ff483U, 0xa9eb4251U, 0x5d227fa5U,
+	0xf9af5601U, 0x60513198U, 0xe5b4511dU, 0xea7b9112U,
+	0x53adfeabU, 0x70453588U, 0x66d4b29eU, 0x12bdafeaU,
+	0x6b9bf093U, 0x57a8ffafU, 0x7ecab486U, 0x7440348cU,
+	0x3fdae5c7U, 0x103d2de8U, 0xd30dde2bU, 0x355065cdU,
+	0x81c94879U, 0x200121d8U, 0xfa6f9502U, 0xe1b15019U,
+	0xa361c25bU, 0xc65c9a3eU, 0x54683cacU, 0x5fa2fda7U,
+	0xf5a0550dU, 0x80890978U, 0xc59c593dU, 0xd5885d2dU,
+	0x315564c9U, 0xb235874aU, 0x49337ab1U, 0x3e9aa4c6U,
+	0x27c4e3dfU, 0x4d367bb5U, 0xca539932U, 0xee7e9016U,
+	0x82098b7aU, 0xf1a55409U, 0xd0cd1d28U, 0x33d5e6cbU,
+	0x002929f8U, 0xae2e8056U, 0x0c262af4U, 0x056c69fdU,
+	0x7780f78fU, 0xd4c81c2cU, 0xe271931aU, 0xa4a4005cU,
+	0xff2ad507U, 0x6454309cU, 0x2281a3daU, 0x9758cf6fU,
+	0xf265970aU, 0xacae0254U, 0x6adbb192U, 0xef3ed117U,
+	0x48733bb0U, 0x381f27c0U, 0xf325d60bU, 0x3690a6ceU,
+	0xc7a265ebU, 0xdefc22f2U, 0x3b615a17U, 0x672a4d4bU,
+	0xafd07f83U, 0x1b495237U, 0x68e58d44U, 0x4247056eU,
+	0xc462a6e8U, 0x4642046aU, 0xcc68a4e0U, 0x97e671bbU,
+	0x3ca49810U, 0x733b485fU, 0x2cb09c00U, 0x883db5a4U,
+	0x30ab9b1cU, 0x01d7d62dU, 0xf6de28daU, 0x4582c769U,
+	0x34ae9a18U, 0x666a0c4aU, 0x8c38b4a0U, 0x93e370bfU,
+	0x362e181aU, 0x8d78f5a1U, 0xd073a3fcU, 0x04929628U,
+	0xb941f895U, 0x86b234aaU, 0x8432b6a8U, 0xfad12bd6U,
+	0xeec02ec2U, 0x9566f3b9U, 0xdd3ce1f1U, 0x4f084763U,
+	0xdaf923f6U, 0x237f5c0fU, 0xfed42ad2U, 0x75becb59U,
+	0x50d3837cU, 0xcd28e5e1U, 0xb00bbb9cU, 0x9163f2bdU,
+	0x2b755e07U, 0xa3df7c8fU, 0x2e301e02U, 0x15c6d339U,
+	0x0f585723U, 0x5253017eU, 0xec40acc0U, 0x3a211b16U,
+	0xbbc17a97U, 0xbc04b890U, 0x3f645b13U, 0xcaed27e6U,
+	0x9eac32b2U, 0xe38f6ccfU, 0xcee826e2U, 0x773e495bU,
+	0x498dc465U, 0xa29f3d8eU, 0x60ef8f4cU, 0x0612142aU,
+	0x8177f6adU, 0x8037b7acU, 0x0217152eU, 0x6f204f43U,
+	0x5313407fU, 0x5193c27dU, 0xc067a7ecU, 0x767e085aU,
+	0x92a331beU, 0xf911e8d5U, 0x6ce08c40U, 0xf851a9d4U,
+	0x0752552bU, 0x8abd37a6U, 0xa15ffe8dU, 0x3e241a12U,
+	0x3de4d911U, 0x31ebda1dU, 0x9aa933b6U, 0x5656007aU,
+	0x5999c075U, 0x0dd8d521U, 0xf79e69dbU, 0xb28b399eU,
+	0x7f344b53U, 0x336b581fU, 0xa01fbf8cU, 0xb14bfa9dU,
+	0x5d9cc171U, 0xc522e7e9U, 0xeac52fc6U, 0x48cd8564U,
+	0xe44aaec8U, 0x0c989420U, 0xa41abe88U, 0xe50aefc9U,
+	0x11c3d23dU, 0xfd14e9d1U, 0xdbb962f7U, 0x1606103aU,
+	0x089d9524U, 0x14869238U, 0x6da0cd41U, 0x70fb8b5cU,
+	0x9829b1b4U, 0x18899134U, 0x7db4c951U, 0x82b735aeU,
+	0xcfa867e3U, 0x9c2cb0b0U, 0x8572f7a9U, 0xc127e6edU,
+	0xe2cf2dceU, 0xd3b360ffU, 0x0097972cU, 0xd536e3f9U,
+	0xad50fd81U, 0x44c28668U, 0x9426b2b8U, 0x0357542fU,
+	0x1e0c1232U, 0x8ff877a3U, 0xbfc47b93U, 0xe6ca2ccaU,
+	0x376e591bU, 0x626f0d4eU, 0xb68e389aU, 0xd7b661fbU,
+	0x9d6cf1b1U, 0x83f774afU, 0x7cf48850U, 0x4a4d0766U,
+	0x1203113eU, 0x4307446fU, 0x5716417bU, 0xa7da7d8bU,
+	0x7b314a57U, 0x19c9d035U, 0xc86da5e4U, 0x1a091336U,
+	0x1f4c5333U, 0x263a1c0aU, 0xe905ecc5U, 0x0e181622U,
+	0xf2db29deU, 0x4cc88460U, 0xf45eaad8U, 0xc92de4e5U,
+	0x2a351f06U, 0xd2f321feU, 0x54d68278U, 0x5cdc8070U,
+	0x4b0d4667U, 0xae903e82U, 0x5a590376U, 0x1343503fU,
+	0xe845adc4U, 0x4702456bU, 0x1746513bU, 0x24ba9e08U,
+	0x96a630baU, 0x05d2d729U, 0xb7ce799bU, 0x1dccd131U,
+	0x9fec73b3U, 0x38a19914U, 0x1083933cU, 0x322b191eU,
+	0xa815bd84U, 0xd133e2fdU, 0xa55aff89U, 0x21ffde0dU,
+	0xcbad66e7U, 0xf39b68dfU, 0x40c7876cU, 0x897df4a5U,
+	0xe78a6dcbU, 0x6b254e47U, 0xa69a3c8aU, 0x20bf9f0cU,
+	0x9023b3bcU, 0x632f4c4fU, 0x35eedb19U, 0x5596c379U,
+	0x29f5dc05U, 0x2df0dd01U, 0xf11beaddU, 0xd939e0f5U,
+	0xaa953f86U, 0xf05babdcU, 0x223f1d0eU, 0xd6f620faU,
+	0x727b095eU, 0xeb856ec7U, 0x6e600e42U, 0x61afce4dU,
+	0xd879a1f4U, 0xfb916ad7U, 0xed00edc1U, 0x9969f0b5U,
+	0xe04fafccU, 0xdc7ca0f0U, 0xf51eebd9U, 0xff946bd3U,
+	0xb40eba98U, 0x9be972b7U, 0x58d98174U, 0xbe843a92U,
+	0x0a1d1726U, 0xabd57e87U, 0x71bbca5dU, 0x6a650f46U,
+	0x28b59d04U, 0x4d88c561U, 0xdfbc63f3U, 0xd476a2f8U,
+	0x7e740a52U, 0x0b5d5627U, 0x4e480662U, 0x5e5c0272U,
+	0xba813b96U, 0x39e1d815U, 0xc2e725eeU, 0xb54efb99U,
+	0xac10bc80U, 0xc6e224eaU, 0x4187c66dU, 0x65aacf49U,
+	0x09ddd425U, 0x7a710b56U, 0x5b194277U, 0xb801b994U,
+	0x8bfd76a7U, 0x25fadf09U, 0x87f275abU, 0x8eb836a2U,
+	0xfc54a8d0U, 0x5f1c4373U, 0x69a5cc45U, 0x2f705f03U,
+	0x74fe8a58U, 0xef806fc3U, 0xa955fc85U, 0x1c8c9030U,
+	0x79b1c855U, 0x277a5d0bU, 0xe10feecdU, 0x64ea8e48U,
+	0xc3a764efU, 0xb3cb789fU, 0x78f18954U, 0xbd44f991U,
+	0x4a8bc14fU, 0x53d58656U, 0xb648feb3U, 0xea03e9efU,
+	0x22f9db27U, 0x9660f693U, 0xe5cc29e0U, 0xcf6ea1caU,
+	0x494b024cU, 0xcb6ba0ceU, 0x41410044U, 0x1acfd51fU,
+	0xb18d3cb4U, 0xfe12ecfbU, 0xa19938a4U, 0x05141100U,
+	0xbd823fb8U, 0x8cfe7289U, 0x7bf78c7eU, 0xc8ab63cdU,
+	0xb9873ebcU, 0xeb43a8eeU, 0x01111004U, 0x1ecad41bU,
+	0xbb07bcbeU, 0x00515105U, 0x5d5a0758U, 0x89bb328cU,
+	0x34685c31U, 0x0b9b900eU, 0x091b120cU, 0x77f88f72U,
+	0x63e98a66U, 0x184f571dU, 0x50154555U, 0xc221e3c7U,
+	0x57d08752U, 0xae56f8abU, 0x73fd8e76U, 0xf8976ffdU,
+	0xddfa27d8U, 0x40014145U, 0x3d221f38U, 0x1c4a5619U,
+	0xa65cfaa3U, 0x2ef6d82bU, 0xa319baa6U, 0x98ef779dU,
+	0x8271f387U, 0xdf7aa5daU, 0x61690864U, 0xb708bfb2U,
+	0x36e8de33U, 0x312d1c34U, 0xb24dffb7U, 0x47c48342U,
+	0x13859616U, 0x6ea6c86bU, 0x43c18246U, 0xfa17edffU,
+	0xc4a460c1U, 0x2fb6992aU, 0xedc62be8U, 0x8b3bb08eU,
+	0x0c5e5209U, 0x0d1e1308U, 0x8f3eb18aU, 0xe209ebe7U,
+	0xde3ae4dbU, 0xdcba66d9U, 0x4d4e0348U, 0xfb57acfeU,
+	0x1f8a951aU, 0x74384c71U, 0xe1c928e4U, 0x75780d70U,
+	0x8a7bf18fU, 0x07949302U, 0x2c765a29U, 0xb30dbeb6U,
+	0xb0cd7db5U, 0xbcc27eb9U, 0x17809712U, 0xdb7fa4deU,
+	0xd4b064d1U, 0x80f17185U, 0x7ab7cd7fU, 0x3fa29d3aU,
+	0xf21deff7U, 0xbe42fcbbU, 0x2d361b28U, 0x3c625e39U,
+	0xd0b565d5U, 0x480b434dU, 0x67ec8b62U, 0xc5e421c0U,
+	0x69630a6cU, 0x81b13084U, 0x29331a2cU, 0x68234b6dU,
+	0x9cea7699U, 0x703d4d75U, 0x5690c653U, 0x9b2fb49eU,
+	0x85b43180U, 0x99af369cU, 0xe08969e5U, 0xfdd22ff8U,
+	0x15001510U, 0x95a03590U, 0xf09d6df5U, 0x0f9e910aU,
+	0x4281c347U, 0x11051414U, 0x085b530dU, 0x4c0e4249U,
+	0x6fe6896aU, 0x5e9ac45bU, 0x8dbe3388U, 0x581f475dU,
+	0x20795925U, 0xc9eb22ccU, 0x190f161cU, 0x8e7ef08bU,
+	0x9325b696U, 0x02d1d307U, 0x32eddf37U, 0x6be3886eU,
+	0xba47fdbfU, 0xef46a9eaU, 0x3ba79c3eU, 0x5a9fc55fU,
+	0x10455515U, 0x0eded00bU, 0xf1dd2cf4U, 0xc764a3c2U,
+	0x9f2ab59aU, 0xce2ee0cbU, 0xda3fe5dfU, 0x2af3d92fU,
+	0xf618eef3U, 0x94e07491U, 0x45440140U, 0x9720b792U,
+	0x9265f797U, 0xab13b8aeU, 0x642c4861U, 0x8331b286U,
+	0x7ff28d7aU, 0xc1e120c4U, 0x79770e7cU, 0x44044041U,
+	0xa71cbba2U, 0x5fda855aU, 0xd9ff26dcU, 0xd1f524d4U,
+	0xc624e2c3U, 0x23b99a26U, 0xd770a7d2U, 0x9e6af49bU,
+	0x656c0960U, 0xca2be1cfU, 0x9a6ff59fU, 0xa9933aacU,
+	0x1b8f941eU, 0x88fb738dU, 0x3ae7dd3fU, 0x90e57595U,
+	0x12c5d717U, 0xb5883db0U, 0x9daa3798U, 0xbf02bdbaU,
+	0x253c1920U, 0x5c1a4659U, 0x28735b2dU, 0xacd67aa9U,
+	0x4684c243U, 0x7eb2cc7bU, 0xcdee23c8U, 0x04545001U,
+	0x6aa3c96fU, 0xe60ceae3U, 0x2bb3982eU, 0xad963ba8U,
+	0x1d0a1718U, 0xee06e8ebU, 0xb8c77fbdU, 0xd8bf67ddU,
+	0xa4dc78a1U, 0xa0d979a5U, 0x7c324e79U, 0x54104451U,
+	0x27bc9b22U, 0x7d720f78U, 0xaf16b9aaU, 0x5bdf845eU,
+	0xff52adfaU, 0x66acca63U, 0xe349aae6U, 0xec866ae9U,
+	0x55500550U, 0x76b8ce73U, 0x60294965U, 0x14405411U,
+	0x6d660b68U, 0x51550454U, 0x78374f7dU, 0x72bdcf77U,
+	0x39271e3cU, 0x16c0d613U, 0xd5f025d0U, 0x33ad9e36U,
+	0x8734b382U, 0x26fcda23U, 0xfc926ef9U, 0xe74cabe2U,
+	0xa59c39a0U, 0xc0a161c5U, 0x5295c757U, 0x595f065cU,
+	0xf35daef6U, 0x8674f283U, 0xc361a2c6U, 0xd375a6d6U,
+	0x37a89f32U, 0xb4c87cb1U, 0x4fce814aU, 0x38675f3dU,
+	0x21391824U, 0x4bcb804eU, 0xccae62c9U, 0xe8836bedU,
+	0x84f47081U, 0xf758aff2U, 0xd630e6d3U, 0x35281d30U,
+	0x06d4d203U, 0xa8d37badU, 0x0adbd10fU, 0x03919206U,
+	0x717d0c74U, 0xd235e7d7U, 0xe48c68e1U, 0xa259fba7U,
+	0xf9d72efcU, 0x62a9cb67U, 0x247c5821U, 0x91a53494U,
+	0xf4986cf1U, 0xaa53f9afU, 0x6c264a69U, 0xe9c32aecU,
+	0x4e8ec04bU, 0x3ee2dc3bU, 0xf5d82df0U, 0x306d5d35U,
+	0xdca579f7U, 0xc5fb3eeeU, 0x2066460bU, 0x7c2d5157U,
+	0xb4d7639fU, 0x004e4e2bU, 0x73e29158U, 0x59401972U,
+	0xdf65baf4U, 0x5d451876U, 0xd76fb8fcU, 0x8ce16da7U,
+	0x27a3840cU, 0x683c5443U, 0x37b7801cU, 0x933aa9b8U,
+	0x2bac8700U, 0x1ad0ca31U, 0xedd934c6U, 0x5e85db75U,
+	0x2fa98604U, 0x7d6d1056U, 0x973fa8bcU, 0x88e46ca3U,
+	0x2d290406U, 0x967fe9bdU, 0xcb74bfe0U, 0x1f958a34U,
+	0xa246e489U, 0x9db528b6U, 0x9f35aab4U, 0xe1d637caU,
+	0xf5c732deU, 0x8e61efa5U, 0xc63bfdedU, 0x540f5b7fU,
+	0xc1fe3feaU, 0x38784013U, 0xe5d336ceU, 0x6eb9d745U,
+	0x4bd49f60U, 0xd62ff9fdU, 0xab0ca780U, 0x8a64eea1U,
+	0x3072421bU, 0xb8d86093U, 0x3537021eU, 0x0ec1cf25U,
+	0x145f4b3fU, 0x49541d62U, 0xf747b0dcU, 0x2126070aU,
+	0xa0c6668bU, 0xa703a48cU, 0x2463470fU, 0xd1ea3bfaU,
+	0x85ab2eaeU, 0xf88870d3U, 0xd5ef3afeU, 0x6c395547U,
+	0x528ad879U, 0xb9982192U, 0x7be89350U, 0x1d150836U,
+	0x9a70eab1U, 0x9b30abb0U, 0x19100932U, 0x7427535fU,
+	0x48145c63U, 0x4a94de61U, 0xdb60bbf0U, 0x6d791446U,
+	0x89a42da2U, 0xe216f4c9U, 0x77e7905cU, 0xe356b5c8U,
+	0x1c554937U, 0x91ba2bbaU, 0xba58e291U, 0x2523060eU,
+	0x26e3c50dU, 0x2aecc601U, 0x81ae2faaU, 0x4d511c66U,
+	0x429edc69U, 0x16dfc93dU, 0xec9975c7U, 0xa98c2582U,
+	0x6433574fU, 0x286c4403U, 0xbb18a390U, 0xaa4ce681U,
+	0x469bdd6dU, 0xde25fbf5U, 0xf1c233daU, 0x53ca9978U,
+	0xff4db2d4U, 0x179f883cU, 0xbf1da294U, 0xfe0df3d5U,
+	0x0ac4ce21U, 0xe613f5cdU, 0xc0be7eebU, 0x0d010c26U,
+	0x139a8938U, 0x0f818e24U, 0x76a7d15dU, 0x6bfc9740U,
+	0x832eada8U, 0x038e8d28U, 0x66b3d54dU, 0x99b029b2U,
+	0xd4af7bffU, 0x872bacacU, 0x9e75ebb5U, 0xda20faf1U,
+	0xf9c831d2U, 0xc8b47ce3U, 0x1b908b30U, 0xce31ffe5U,
+	0xb657e19dU, 0x5fc59a74U, 0x8f21aea4U, 0x18504833U,
+	0x050b0e2eU, 0x94ff6bbfU, 0xa4c3678fU, 0xfdcd30d6U,
+	0x2c694507U, 0x79681152U, 0xad892486U, 0xccb17de7U,
+	0x866bedadU, 0x98f068b3U, 0x67f3944cU, 0x514a1b7aU,
+	0x09040d22U, 0x58005873U, 0x4c115d67U, 0xbcdd6197U,
+	0x6036564bU, 0x02cecc29U, 0xd36ab9f8U, 0x010e0f2aU,
+	0x044b4f2fU, 0x3d3d0016U, 0xf202f0d9U, 0x151f0a3eU,
+	0xe9dc35c2U, 0x57cf987cU, 0xef59b6c4U, 0xd22af8f9U,
+	0x3132031aU, 0xc9f43de2U, 0x4fd19e64U, 0x47db9c6cU,
+	0x500a5a7bU, 0xb597229eU, 0x415e1f6aU, 0x08444c23U,
+	0xf342b1d8U, 0x5c055977U, 0x0c414d27U, 0x3fbd8214U,
+	0x8da12ca6U, 0x1ed5cb35U, 0xacc96587U, 0x06cbcd2dU,
+	0x84eb6fafU, 0x23a68508U, 0x0b848f20U, 0x292c0502U,
+	0xb312a198U, 0xca34fee1U, 0xbe5de395U, 0x3af8c211U,
+	0xd0aa7afbU, 0xe89c74c3U, 0x5bc09b70U, 0x927ae8b9U,
+	0xfc8d71d7U, 0x7022525bU, 0xbd9d2096U, 0x3bb88310U,
+	0x8b24afa0U, 0x78285053U, 0x2ee9c705U, 0x4e91df65U,
+	0x32f2c019U, 0x36f7c11dU, 0xea1cf6c1U, 0xc23efce9U,
+	0xb192239aU, 0xeb5cb7c0U, 0x39380112U, 0xcdf13ce6U,
+	0x697c1542U, 0xf08272dbU, 0x7567125eU, 0x7aa8d251U,
+	0xc37ebde8U, 0xe09676cbU, 0xf607f1ddU, 0x826eeca9U,
+	0xfb48b3d0U, 0xc77bbcecU, 0xee19f7c5U, 0xe49377cfU,
+	0xaf09a684U, 0x80ee6eabU, 0x43de9d68U, 0xa583268eU,
+	0x111a0b3aU, 0xb0d2629bU, 0x6abcd641U, 0x7162135aU,
+	0x33b28118U, 0x568fd97dU, 0xc4bb7fefU, 0xcf71bee4U,
+	0x6573164eU, 0x105a4a3bU, 0x554f1a7eU, 0x455b1e6eU,
+	0xa186278aU, 0x22e6c409U, 0xd9e039f2U, 0xae49e785U,
+	0xb717a09cU, 0xdde538f6U, 0x5a80da71U, 0x7eadd355U,
+	0x12dac839U, 0x6176174aU, 0x401e5e6bU, 0xa306a588U,
+	0x90fa6abbU, 0x3efdc315U, 0x9cf569b7U, 0x95bf2abeU,
+	0xe753b4ccU, 0x441b5f6fU, 0x72a2d059U, 0x3477431fU,
+	0x6ff99644U, 0xf48773dfU, 0xb252e099U, 0x078b8c2cU,
+	0x62b6d449U, 0x3c7d4117U, 0xfa08f2d1U, 0x7fed9254U,
+	0xd8a078f3U, 0xa8cc6483U, 0x63f69548U, 0xa643e58dU,
+	0xa0e848c6U, 0xb9b60fdfU, 0x5c2b773aU, 0x00606066U,
+	0xc89a52aeU, 0x7c037f1aU, 0x0fafa069U, 0x250d2843U,
+	0xa3288bc5U, 0x21082947U, 0xab2289cdU, 0xf0ac5c96U,
+	0x5beeb53dU, 0x14716572U, 0x4bfab12dU, 0xef779889U,
+	0x57e1b631U, 0x669dfb00U, 0x919405f7U, 0x22c8ea44U,
+	0x53e4b735U, 0x01202167U, 0xeb72998dU, 0xf4a95d92U,
+	0x51643537U, 0xea32d88cU, 0xb7398ed1U, 0x63d8bb05U,
+	0xde0bd5b8U, 0xe1f81987U, 0xe3789b85U, 0x9d9b06fbU,
+	0x898a03efU, 0xf22cde94U, 0xba76ccdcU, 0x28426a4eU,
+	0xbdb30edbU, 0x44357122U, 0x999e07ffU, 0x12f4e674U,
+	0x3799ae51U, 0xaa62c8ccU, 0xd74196b1U, 0xf629df90U,
+	0x4c3f732aU, 0xc49551a2U, 0x497a332fU, 0x728cfe14U,
+	0x68127a0eU, 0x35192c53U, 0x8b0a81edU, 0x5d6b363bU,
+	0xdc8b57baU, 0xdb4e95bdU, 0x582e763eU, 0xada70acbU,
+	0xf9e61f9fU, 0x84c541e2U, 0xa9a20bcfU, 0x10746476U,
+	0x2ec7e948U, 0xc5d510a3U, 0x07a5a261U, 0x61583907U,
+	0xe63ddb80U, 0xe77d9a81U, 0x655d3803U, 0x086a626eU,
+	0x34596d52U, 0x36d9ef50U, 0xa72d8ac1U, 0x11342577U,
+	0xf5e91c93U, 0x9e5bc5f8U, 0x0baaa16dU, 0x9f1b84f9U,
+	0x60187806U, 0xedf71a8bU, 0xc615d3a0U, 0x596e373fU,
+	0x5aaef43cU, 0x56a1f730U, 0xfde31e9bU, 0x311c2d57U,
+	0x3ed3ed58U, 0x6a92f80cU, 0x90d444f6U, 0xd5c114b3U,
+	0x187e667eU, 0x54217532U, 0xc75592a1U, 0xd601d7b0U,
+	0x3ad6ec5cU, 0xa268cac4U, 0x8d8f02ebU, 0x2f87a849U,
+	0x830083e5U, 0x6bd2b90dU, 0xc35093a5U, 0x8240c2e4U,
+	0x7689ff10U, 0x9a5ec4fcU, 0xbcf34fdaU, 0x714c3d17U,
+	0x6fd7b809U, 0x73ccbf15U, 0x0aeae06cU, 0x17b1a671U,
+	0xff639c99U, 0x7fc3bc19U, 0x1afee47cU, 0xe5fd1883U,
+	0xa8e24aceU, 0xfb669d9dU, 0xe238da84U, 0xa66dcbc0U,
+	0x858500e3U, 0xb4f94dd2U, 0x67ddba01U, 0xb27cced4U,
+	0xca1ad0acU, 0x2388ab45U, 0xf36c9f95U, 0x641d7902U,
+	0x79463f1fU, 0xe8b25a8eU, 0xd88e56beU, 0x818001e7U,
+	0x50247436U, 0x05252063U, 0xd1c415b7U, 0xb0fc4cd6U,
+	0xfa26dc9cU, 0xe4bd5982U, 0x1bbea57dU, 0x2d072a4bU,
+	0x75493c13U, 0x244d6942U, 0x305c6c56U, 0xc09050a6U,
+	0x1c7b677aU, 0x7e83fd18U, 0xaf2788c9U, 0x7d433e1bU,
+	0x78067e1eU, 0x41703127U, 0x8e4fc1e8U, 0x69523b0fU,
+	0x959104f3U, 0x2b82a94dU, 0x931487f5U, 0xae67c9c8U,
+	0x4d7f322bU, 0xb5b90cd3U, 0x339caf55U, 0x3b96ad5dU,
+	0x2c476b4aU, 0xc9da13afU, 0x3d132e5bU, 0x74097d12U,
+	0x8f0f80e9U, 0x20486846U, 0x700c7c16U, 0x43f0b325U,
+	0xf1ec1d97U, 0x6298fa04U, 0xd08454b6U, 0x7a86fc1cU,
+	0xf8a65e9eU, 0x5febb439U, 0x77c9be11U, 0x55613433U,
+	0xcf5f90a9U, 0xb679cfd0U, 0xc210d2a4U, 0x46b5f320U,
+	0xace74bcaU, 0x94d145f2U, 0x278daa41U, 0xee37d988U,
+	0x80c040e6U, 0x0c6f636aU, 0xc1d011a7U, 0x47f5b221U,
+	0xf7699e91U, 0x04656162U, 0x52a4f634U, 0x32dcee54U,
+	0x4ebff128U, 0x4abaf02cU, 0x9651c7f0U, 0xbe73cdd8U,
+	0xcddf12abU, 0x971186f1U, 0x45753023U, 0xb1bc0dd7U,
+	0x15312473U, 0x8ccf43eaU, 0x092a236fU, 0x06e5e360U,
+	0xbf338cd9U, 0x9cdb47faU, 0x8a4ac0ecU, 0xfe23dd98U,
+	0x870582e1U, 0xbb368dddU, 0x9254c6f4U, 0x98de46feU,
+	0xd34497b5U, 0xfca35f9aU, 0x3f93ac59U, 0xd9ce17bfU,
+	0x6d573a0bU, 0xcc9f53aaU, 0x16f1e770U, 0x0d2f226bU,
+	0x4fffb029U, 0x2ac2e84cU, 0xb8f64edeU, 0xb33c8fd5U,
+	0x193e277fU, 0x6c177b0aU, 0x29022b4fU, 0x39162f5fU,
+	0xddcb16bbU, 0x5eabf538U, 0xa5ad08c3U, 0xd204d6b4U,
+	0xcb5a91adU, 0xa1a809c7U, 0x26cdeb40U, 0x02e0e264U,
+	0x6e97f908U, 0x1d3b267bU, 0x3c536f5aU, 0xdf4b94b9U,
+	0xecb75b8aU, 0x42b0f224U, 0xe0b85886U, 0xe9f21b8fU,
+	0x9b1e85fdU, 0x38566e5eU, 0x0eefe168U, 0x483a722eU,
+	0x13b4a775U, 0x88ca42eeU, 0xce1fd1a8U, 0x7bc6bd1dU,
+	0x1efbe578U, 0x40307026U, 0x8645c3e0U, 0x03a0a365U,
+	0xa4ed49c2U, 0xd48155b2U, 0x1fbba479U, 0xda0ed4bcU,
+	0xa916bf31U, 0xb048f828U, 0x55d580cdU, 0x099e9791U,
+	0xc164a559U, 0x75fd88edU, 0x0651579eU, 0x2cf3dfb4U,
+	0xaad67c32U, 0x28f6deb0U, 0xa2dc7e3aU, 0xf952ab61U,
+	0x521042caU, 0x1d8f9285U, 0x420446daU, 0xe6896f7eU,
+	0x5e1f41c6U, 0x6f630cf7U, 0x986af200U, 0x2b361db3U,
+	0x5a1a40c2U, 0x08ded690U, 0xe28c6e7aU, 0xfd57aa65U,
+	0x589ac2c0U, 0xe3cc2f7bU, 0xbec77926U, 0x6a264cf2U,
+	0xd7f5224fU, 0xe806ee70U, 0xea866c72U, 0x9465f10cU,
+	0x8074f418U, 0xfbd22963U, 0xb3883b2bU, 0x21bc9db9U,
+	0xb44df92cU, 0x4dcb86d5U, 0x9060f008U, 0x1b0a1183U,
+	0x3e6759a6U, 0xa39c3f3bU, 0xdebf6146U, 0xffd72867U,
+	0x45c184ddU, 0xcd6ba655U, 0x4084c4d8U, 0x7b7209e3U,
+	0x61ec8df9U, 0x3ce7dba4U, 0x82f4761aU, 0x5495c1ccU,
+	0xd575a04dU, 0xd2b0624aU, 0x51d081c9U, 0xa459fd3cU,
+	0xf018e868U, 0x8d3bb615U, 0xa05cfc38U, 0x198a9381U,
+	0x27391ebfU, 0xcc2be754U, 0x0e5b5596U, 0x68a6cef0U,
+	0xefc32c77U, 0xee836d76U, 0x6ca3cff4U, 0x01949599U,
+	0x3da79aa5U, 0x3f2718a7U, 0xaed37d36U, 0x18cad280U,
+	0xfc17eb64U, 0x97a5320fU, 0x0254569aU, 0x96e5730eU,
+	0x69e68ff1U, 0xe409ed7cU, 0xcfeb2457U, 0x5090c0c8U,
+	0x535003cbU, 0x5f5f00c7U, 0xf41de96cU, 0x38e2daa0U,
+	0x372d1aafU, 0x636c0ffbU, 0x992ab301U, 0xdc3fe344U,
+	0x11809189U, 0x5ddf82c5U, 0xceab6556U, 0xdfff2047U,
+	0x33281babU, 0xab963d33U, 0x8471f51cU, 0x26795fbeU,
+	0x8afe7412U, 0x622c4efaU, 0xcaae6452U, 0x8bbe3513U,
+	0x7f7708e7U, 0x93a0330bU, 0xb50db82dU, 0x78b2cae0U,
+	0x66294ffeU, 0x7a3248e2U, 0x0314179bU, 0x1e4f5186U,
+	0xf69d6b6eU, 0x763d4beeU, 0x1300138bU, 0xec03ef74U,
+	0xa11cbd39U, 0xf2986a6aU, 0xebc62d73U, 0xaf933c37U,
+	0x8c7bf714U, 0xbd07ba25U, 0x6e234df6U, 0xbb823923U,
+	0xc3e4275bU, 0x2a765cb2U, 0xfa926862U, 0x6de38ef5U,
+	0x70b8c8e8U, 0xe14cad79U, 0xd170a149U, 0x887ef610U,
+	0x59da83c1U, 0x0cdbd794U, 0xd83ae240U, 0xb902bb21U,
+	0xf3d82b6bU, 0xed43ae75U, 0x1240528aU, 0x24f9ddbcU,
+	0x7cb7cbe4U, 0x2db39eb5U, 0x39a29ba1U, 0xc96ea751U,
+	0x1585908dU, 0x777d0aefU, 0xa6d97f3eU, 0x74bdc9ecU,
+	0x71f889e9U, 0x488ec6d0U, 0x87b1361fU, 0x60acccf8U,
+	0x9c6ff304U, 0x227c5ebaU, 0x9aea7002U, 0xa7993e3fU,
+	0x4481c5dcU, 0xbc47fb24U, 0x3a6258a2U, 0x32685aaaU,
+	0x25b99cbdU, 0xc024e458U, 0x34edd9acU, 0x7df78ae5U,
+	0x86f1771eU, 0x29b69fb1U, 0x79f28be1U, 0x4a0e44d2U,
+	0xf812ea60U, 0x6b660df3U, 0xd97aa341U, 0x73780bebU,
+	0xf158a969U, 0x561543ceU, 0x7e3749e6U, 0x5c9fc3c4U,
+	0xc6a1675eU, 0xbf873827U, 0xcbee2553U, 0x4f4b04d7U,
+	0xa519bc3dU, 0x9d2fb205U, 0x2e735db6U, 0xe7c92e7fU,
+	0x893eb711U, 0x0591949dU, 0xc82ee650U, 0x4e0b45d6U,
+	0xfe976966U, 0x0d9b9695U, 0x5b5a01c3U, 0x3b2219a3U,
+	0x474106dfU, 0x434407dbU, 0x9faf3007U, 0xb78d3a2fU,
+	0xc421e55cU, 0x9eef7106U, 0x4c8bc7d4U, 0xb842fa20U,
+	0x1ccfd384U, 0x8531b41dU, 0x00d4d498U, 0x0f1b1497U,
+	0xb6cd7b2eU, 0x9525b00dU, 0x83b4371bU, 0xf7dd2a6fU,
+	0x8efb7516U, 0xb2c87a2aU, 0x9baa3103U, 0x9120b109U,
+	0xdaba6042U, 0xf55da86dU, 0x366d5baeU, 0xd030e048U,
+	0x64a9cdfcU, 0xc561a45dU, 0x1f0f1087U, 0x04d1d59cU,
+	0x460147deU, 0x233c1fbbU, 0xb108b929U, 0xbac27822U,
+	0x10c0d088U, 0x65e98cfdU, 0x20fcdcb8U, 0x30e8d8a8U,
+	0xd435e14cU, 0x575502cfU, 0xac53ff34U, 0xdbfa2143U,
+	0xc2a4665aU, 0xa856fe30U, 0x2f331cb7U, 0x0b1e1593U,
+	0x67690effU, 0x14c5d18cU, 0x35ad98adU, 0xd6b5634eU,
+	0xe549ac7dU, 0x4b4e05d3U, 0xe946af71U, 0xe00cec78U,
+	0x92e0720aU, 0x31a899a9U, 0x0711169fU, 0x41c485d9U,
+	0x1a4a5082U, 0x8134b519U, 0xc7e1265fU, 0x72384aeaU,
+	0x1705128fU, 0x49ce87d1U, 0x8fbb3417U, 0x0a5e5492U,
+	0xad13be35U, 0xdd7fa245U, 0x1645538eU, 0xd3f0234bU,
+	0xf5f90c82U, 0xeca74b9bU, 0x093a337eU, 0x55712422U,
+	0x9d8b16eaU, 0x29123b5eU, 0x5abee42dU, 0x701c6c07U,
+	0xf639cf81U, 0x74196d03U, 0xfe33cd89U, 0xa5bd18d2U,
+	0x0efff179U, 0x41602136U, 0x1eebf569U, 0xba66dccdU,
+	0x02f0f275U, 0x338cbf44U, 0xc48541b3U, 0x77d9ae00U,
+	0x06f5f371U, 0x54316523U, 0xbe63ddc9U, 0xa1b819d6U,
+	0x04757173U, 0xbf239cc8U, 0xe228ca95U, 0x36c9ff41U,
+	0x8b1a91fcU, 0xb4e95dc3U, 0xb669dfc1U, 0xc88a42bfU,
+	0xdc9b47abU, 0xa73d9ad0U, 0xef678898U, 0x7d532e0aU,
+	0xe8a24a9fU, 0x11243566U, 0xcc8f43bbU, 0x47e5a230U,
+	0x6288ea15U, 0xff738c88U, 0x8250d2f5U, 0xa3389bd4U,
+	0x192e376eU, 0x918415e6U, 0x1c6b776bU, 0x279dba50U,
+	0x3d033e4aU, 0x60086817U, 0xde1bc5a9U, 0x087a727fU,
+	0x899a13feU, 0x8e5fd1f9U, 0x0d3f327aU, 0xf8b64e8fU,
+	0xacf75bdbU, 0xd1d405a6U, 0xfcb34f8bU, 0x45652032U,
+	0x7bd6ad0cU, 0x90c454e7U, 0x52b4e625U, 0x34497d43U,
+	0xb32c9fc4U, 0xb26cdec5U, 0x304c7c47U, 0x5d7b262aU,
+	0x61482916U, 0x63c8ab14U, 0xf23cce85U, 0x44256133U,
+	0xa0f858d7U, 0xcb4a81bcU, 0x5ebbe529U, 0xca0ac0bdU,
+	0x35093c42U, 0xb8e65ecfU, 0x930497e4U, 0x0c7f737bU,
+	0x0fbfb078U, 0x03b0b374U, 0xa8f25adfU, 0x640d6913U,
+	0x6bc2a91cU, 0x3f83bc48U, 0xc5c500b2U, 0x80d050f7U,
+	0x4d6f223aU, 0x01303176U, 0x9244d6e5U, 0x831093f4U,
+	0x6fc7a818U, 0xf7798e80U, 0xd89e46afU, 0x7a96ec0dU,
+	0xd611c7a1U, 0x3ec3fd49U, 0x9641d7e1U, 0xd75186a0U,
+	0x2398bb54U, 0xcf4f80b8U, 0xe9e20b9eU, 0x245d7953U,
+	0x3ac6fc4dU, 0x26ddfb51U, 0x5ffba428U, 0x42a0e235U,
+	0xaa72d8ddU, 0x2ad2f85dU, 0x4fefa038U, 0xb0ec5cc7U,
+	0xfdf30e8aU, 0xae77d9d9U, 0xb7299ec0U, 0xf37c8f84U,
+	0xd09444a7U, 0xe1e80996U, 0x32ccfe45U, 0xe76d8a90U,
+	0x9f0b94e8U, 0x7699ef01U, 0xa67ddbd1U, 0x310c3d46U,
+	0x2c577b5bU, 0xbda31ecaU, 0x8d9f12faU, 0xd49145a3U,
+	0x05353072U, 0x50346427U, 0x84d551f3U, 0xe5ed0892U,
+	0xaf3798d8U, 0xb1ac1dc6U, 0x4eafe139U, 0x78166e0fU,
+	0x20587857U, 0x715c2d06U, 0x654d2812U, 0x958114e2U,
+	0x496a233eU, 0x2b92b95cU, 0xfa36cc8dU, 0x28527a5fU,
+	0x2d173a5aU, 0x14617563U, 0xdb5e85acU, 0x3c437f4bU,
+	0xc08040b7U, 0x7e93ed09U, 0xc605c3b1U, 0xfb768d8cU,
+	0x186e766fU, 0xe0a84897U, 0x668deb11U, 0x6e87e919U,
+	0x79562f0eU, 0x9ccb57ebU, 0x68026a1fU, 0x21183956U,
+	0xda1ec4adU, 0x75592c02U, 0x251d3852U, 0x16e1f761U,
+	0xa4fd59d3U, 0x3789be40U, 0x859510f2U, 0x2f97b858U,
+	0xadb71adaU, 0x0afaf07dU, 0x22d8fa55U, 0x00707077U,
+	0x9a4ed4edU, 0xe3688b94U, 0x970196e0U, 0x13a4b764U,
+	0xf9f60f8eU, 0xc1c001b6U, 0x729cee05U, 0xbb269dccU,
+	0xd5d104a2U, 0x597e272eU, 0x94c155e3U, 0x12e4f665U,
+	0xa278dad5U, 0x51742526U, 0x07b5b270U, 0x67cdaa10U,
+	0x1baeb56cU, 0x1fabb468U, 0xc34083b4U, 0xeb62899cU,
+	0x98ce56efU, 0xc200c2b5U, 0x10647467U, 0xe4ad4993U,
+	0x40206037U, 0xd9de07aeU, 0x5c3b672bU, 0x53f4a724U,
+	0xea22c89dU, 0xc9ca03beU, 0xdf5b84a8U, 0xab3299dcU,
+	0xd214c6a5U, 0xee27c999U, 0xc74582b0U, 0xcdcf02baU,
+	0x8655d3f1U, 0xa9b21bdeU, 0x6a82e81dU, 0x8cdf53fbU,
+	0x38467e4fU, 0x998e17eeU, 0x43e0a334U, 0x583e662fU,
+	0x1aeef46dU, 0x7fd3ac08U, 0xede70a9aU, 0xe62dcb91U,
+	0x4c2f633bU, 0x39063f4eU, 0x7c136f0bU, 0x6c076b1bU,
+	0x88da52ffU, 0x0bbab17cU, 0xf0bc4c87U, 0x871592f0U,
+	0x9e4bd5e9U, 0xf4b94d83U, 0x73dcaf04U, 0x57f1a620U,
+	0x3b86bd4cU, 0x482a623fU, 0x69422b1eU, 0x8a5ad0fdU,
+	0xb9a61fceU, 0x17a1b660U, 0xb5a91cc2U, 0xbce35fcbU,
+	0xce0fc1b9U, 0x6d472a1aU, 0x5bfea52cU, 0x1d2b366aU,
+	0x46a5e331U, 0xdddb06aaU, 0x9b0e95ecU, 0x2ed7f959U,
+	0x4beaa13cU, 0x15213462U, 0xd35487a4U, 0x56b1e721U,
+	0xf1fc0d86U, 0x819011f6U, 0x4aaae03dU, 0x8f1f90f8U,
+	0xd9a47df3U, 0xc0fa3aeaU, 0x2567420fU, 0x792c5553U,
+	0xb1d6679bU, 0x054f4a2fU, 0x76e3955cU, 0x5c411d76U,
+	0xda64bef0U, 0x58441c72U, 0xd26ebcf8U, 0x89e069a3U,
+	0x22a28008U, 0x6d3d5047U, 0x32b68418U, 0x963badbcU,
+	0x2ead8304U, 0x1fd1ce35U, 0xe8d830c2U, 0x5b84df71U,
+	0x2aa88200U, 0x786c1452U, 0x923eacb8U, 0x8de568a7U,
+	0x28280002U, 0x937eedb9U, 0xce75bbe4U, 0x1a948e30U,
+	0xa747e08dU, 0x98b42cb2U, 0x9a34aeb0U, 0xe4d733ceU,
+	0xf0c636daU, 0x8b60eba1U, 0xc33af9e9U, 0x510e5f7bU,
+	0xc4ff3beeU, 0x3d794417U, 0xe0d232caU, 0x6bb8d341U,
+	0x4ed59b64U, 0xd32efdf9U, 0xae0da384U, 0x8f65eaa5U,
+	0x3573461fU, 0xbdd96497U, 0x3036061aU, 0x0bc0cb21U,
+	0x115e4f3bU, 0x4c551966U, 0xf246b4d8U, 0x2427030eU,
+	0xa5c7628fU, 0xa202a088U, 0x2162430bU, 0xd4eb3ffeU,
+	0x80aa2aaaU, 0xfd8974d7U, 0xd0ee3efaU, 0x69385143U,
+	0x578bdc7dU, 0xbc992596U, 0x7ee99754U, 0x18140c32U,
+	0x9f71eeb5U, 0x9e31afb4U, 0x1c110d36U, 0x7126575bU,
+	0x4d155867U, 0x4f95da65U, 0xde61bff4U, 0x68781042U,
+	0x8ca529a6U, 0xe717f0cdU, 0x72e69458U, 0xe657b1ccU,
+	0x19544d33U, 0x94bb2fbeU, 0xbf59e695U, 0x2022020aU,
+	0x23e2c109U, 0x2fedc205U, 0x84af2baeU, 0x48501862U,
+	0x479fd86dU, 0x13decd39U, 0xe99871c3U, 0xac8d2186U,
+	0x6132534bU, 0x2d6d4007U, 0xbe19a794U, 0xaf4de285U,
+	0x439ad969U, 0xdb24fff1U, 0xf4c337deU, 0x56cb9d7cU,
+	0xfa4cb6d0U, 0x129e8c38U, 0xba1ca690U, 0xfb0cf7d1U,
+	0x0fc5ca25U, 0xe312f1c9U, 0xc5bf7aefU, 0x08000822U,
+	0x169b8d3cU, 0x0a808a20U, 0x73a6d559U, 0x6efd9344U,
+	0x862fa9acU, 0x068f892cU, 0x63b2d149U, 0x9cb12db6U,
+	0xd1ae7ffbU, 0x822aa8a8U, 0x9b74efb1U, 0xdf21fef5U,
+	0xfcc935d6U, 0xcdb578e7U, 0x1e918f34U, 0xcb30fbe1U,
+	0xb356e599U, 0x5ac49e70U, 0x8a20aaa0U, 0x1d514c37U,
+	0x000a0a2aU, 0x91fe6fbbU, 0xa1c2638bU, 0xf8cc34d2U,
+	0x29684103U, 0x7c691556U, 0xa8882082U, 0xc9b079e3U,
+	0x836ae9a9U, 0x9df16cb7U, 0x62f29048U, 0x544b1f7eU,
+	0x0c050926U, 0x5d015c77U, 0x49105963U, 0xb9dc6593U,
+	0x6537524fU, 0x07cfc82dU, 0xd66bbdfcU, 0x040f0b2eU,
+	0x014a4b2bU, 0x383c0412U, 0xf703f4ddU, 0x101e0e3aU,
+	0xecdd31c6U, 0x52ce9c78U, 0xea58b2c0U, 0xd72bfcfdU,
+	0x3433071eU, 0xccf539e6U, 0x4ad09a60U, 0x42da9868U,
+	0x550b5e7fU, 0xb096269aU, 0x445f1b6eU, 0x0d454827U,
+	0xf643b5dcU, 0x59045d73U, 0x09404923U, 0x3abc8610U,
+	0x88a028a2U, 0x1bd4cf31U, 0xa9c86183U, 0x03cac929U,
+	0x81ea6babU, 0x26a7810cU, 0x0e858b24U, 0x2c2d0106U,
+	0xb613a59cU, 0xcf35fae5U, 0xbb5ce791U, 0x3ff9c615U,
+	0xd5ab7effU, 0xed9d70c7U, 0x5ec19f74U, 0x977becbdU,
+	0xf98c75d3U, 0x7523565fU, 0xb89c2492U, 0x3eb98714U,
+	0x8e25aba4U, 0x7d295457U, 0x2be8c301U, 0x4b90db61U,
+	0x37f3c41dU, 0x33f6c519U, 0xef1df2c5U, 0xc73ff8edU,
+	0xb493279eU, 0xee5db3c4U, 0x3c390516U, 0xc8f038e2U,
+	0x6c7d1146U, 0xf58376dfU, 0x7066165aU, 0x7fa9d655U,
+	0xc67fb9ecU, 0xe59772cfU, 0xf306f5d9U, 0x876fe8adU,
+	0xfe49b7d4U, 0xc27ab8e8U, 0xeb18f3c1U, 0xe19273cbU,
+	0xaa08a280U, 0x85ef6aafU, 0x46df996cU, 0xa082228aU,
+	0x141b0f3eU, 0xb5d3669fU, 0x6fbdd245U, 0x7463175eU,
+	0x36b3851cU, 0x538edd79U, 0xc1ba7bebU, 0xca70bae0U,
+	0x6072124aU, 0x155b4e3fU, 0x504e1e7aU, 0x405a1a6aU,
+	0xa487238eU, 0x27e7c00dU, 0xdce13df6U, 0xab48e381U,
+	0xb216a498U, 0xd8e43cf2U, 0x5f81de75U, 0x7bacd751U,
+	0x17dbcc3dU, 0x6477134eU, 0x451f5a6fU, 0xa607a18cU,
+	0x95fb6ebfU, 0x3bfcc711U, 0x99f46db3U, 0x90be2ebaU,
+	0xe252b0c8U, 0x411a5b6bU, 0x77a3d45dU, 0x3176471bU,
+	0x6af89240U, 0xf18677dbU, 0xb753e49dU, 0x028a8828U,
+	0x67b7d04dU, 0x397c4513U, 0xff09f6d5U, 0x7aec9650U,
+	0xdda17cf7U, 0xadcd6087U, 0x66f7914cU, 0xa342e189U,
+	0x1d322fa1U, 0x046c68b8U, 0xe1f1105dU, 0xbdba0701U,
+	0x754035c9U, 0xc1d9187dU, 0xb275c70eU, 0x98d74f24U,
+	0x1ef2eca2U, 0x9cd24e20U, 0x16f8eeaaU, 0x4d763bf1U,
+	0xe634d25aU, 0xa9ab0215U, 0xf620d64aU, 0x52adffeeU,
+	0xea3bd156U, 0xdb479c67U, 0x2c4e6290U, 0x9f128d23U,
+	0xee3ed052U, 0xbcfa4600U, 0x56a8feeaU, 0x49733af5U,
+	0xecbe5250U, 0x57e8bfebU, 0x0ae3e9b6U, 0xde02dc62U,
+	0x63d1b2dfU, 0x5c227ee0U, 0x5ea2fce2U, 0x2041619cU,
+	0x34506488U, 0x4ff6b9f3U, 0x07acabbbU, 0x95980d29U,
+	0x006969bcU, 0xf9ef1645U, 0x24446098U, 0xaf2e8113U,
+	0x8a43c936U, 0x17b8afabU, 0x6a9bf1d6U, 0x4bf3b8f7U,
+	0xf1e5144dU, 0x794f36c5U, 0xf4a05448U, 0xcf569973U,
+	0xd5c81d69U, 0x88c34b34U, 0x36d0e68aU, 0xe0b1515cU,
+	0x615130ddU, 0x6694f2daU, 0xe5f41159U, 0x107d6dacU,
+	0x443c78f8U, 0x391f2685U, 0x14786ca8U, 0xadae0311U,
+	0x931d8e2fU, 0x780f77c4U, 0xba7fc506U, 0xdc825e60U,
+	0x5be7bce7U, 0x5aa7fde6U, 0xd8875f64U, 0xb5b00509U,
+	0x89830a35U, 0x8b038837U, 0x1af7eda6U, 0xacee4210U,
+	0x48337bf4U, 0x2381a29fU, 0xb670c60aU, 0x22c1e39eU,
+	0xddc21f61U, 0x502d7decU, 0x7bcfb4c7U, 0xe4b45058U,
+	0xe774935bU, 0xeb7b9057U, 0x403979fcU, 0x8cc64a30U,
+	0x83098a3fU, 0xd7489f6bU, 0x2d0e2391U, 0x681b73d4U,
+	0xa5a40119U, 0xe9fb1255U, 0x7a8ff5c6U, 0x6bdbb0d7U,
+	0x870c8b3bU, 0x1fb2ada3U, 0x3055658cU, 0x925dcf2eU,
+	0x3edae482U, 0xd608de6aU, 0x7e8af4c2U, 0x3f9aa583U,
+	0xcb539877U, 0x2784a39bU, 0x012928bdU, 0xcc965a70U,
+	0xd20ddf6eU, 0xce16d872U, 0xb730870bU, 0xaa6bc116U,
+	0x42b9fbfeU, 0xc219db7eU, 0xa724831bU, 0x58277fe4U,
+	0x15382da9U, 0x46bcfafaU, 0x5fe2bde3U, 0x1bb7aca7U,
+	0x385f6784U, 0x09232ab5U, 0xda07dd66U, 0x0fa6a9b3U,
+	0x77c0b7cbU, 0x9e52cc22U, 0x4eb6f8f2U, 0xd9c71e65U,
+	0xc49c5878U, 0x55683de9U, 0x655431d9U, 0x3c5a6680U,
+	0xedfe1351U, 0xb8ff4704U, 0x6c1e72d0U, 0x0d262bb1U,
+	0x47fcbbfbU, 0x59673ee5U, 0xa664c21aU, 0x90dd4d2cU,
+	0xc8935b74U, 0x99970e25U, 0x8d860b31U, 0x7d4a37c1U,
+	0xa1a1001dU, 0xc3599a7fU, 0x12fdefaeU, 0xc099597cU,
+	0xc5dc1979U, 0xfcaa5640U, 0x3395a68fU, 0xd4885c68U,
+	0x284b6394U, 0x9658ce2aU, 0x2ecee092U, 0x13bdaeafU,
+	0xf0a5554cU, 0x08636bb4U, 0x8e46c832U, 0x864cca3aU,
+	0x919d0c2dU, 0x740074c8U, 0x80c9493cU, 0xc9d31a75U,
+	0x32d5e78eU, 0x9d920f21U, 0xcdd61b71U, 0xfe2ad442U,
+	0x4c367af0U, 0xdf429d63U, 0x6d5e33d1U, 0xc75c9b7bU,
+	0x457c39f9U, 0xe231d35eU, 0xca13d976U, 0xe8bb5354U,
+	0x7285f7ceU, 0x0ba3a8b7U, 0x7fcab5c3U, 0xfb6f9447U,
+	0x113d2cadU, 0x290b2295U, 0x9a57cd26U, 0x53edbeefU,
+	0x3d1a2781U, 0xb1b5040dU, 0x7c0a76c0U, 0xfa2fd546U,
+	0x4ab3f9f6U, 0xb9bf0605U, 0xef7e9153U, 0x8f068933U,
+	0xf365964fU, 0xf760974bU, 0x2b8ba097U, 0x03a9aabfU,
+	0x700575ccU, 0x2acbe196U, 0xf8af5744U, 0x0c666ab0U,
+	0xa8eb4314U, 0x3115248dU, 0xb4f04408U, 0xbb3f8407U,
+	0x02e9ebbeU, 0x2101209dU, 0x3790a78bU, 0x43f9baffU,
+	0x3adfe586U, 0x06eceabaU, 0x2f8ea193U, 0x25042199U,
+	0x6e9ef0d2U, 0x417938fdU, 0x8249cb3eU, 0x641470d8U,
+	0xd08d5d6cU, 0x714534cdU, 0xab2b8017U, 0xb0f5450cU,
+	0xf225d74eU, 0x97188f2bU, 0x052c29b9U, 0x0ee6e8b2U,
+	0xa4e44018U, 0xd1cd1c6dU, 0x94d84c28U, 0x84cc4838U,
+	0x601171dcU, 0xe371925fU, 0x18776fa4U, 0x6fdeb1d3U,
+	0x7680f6caU, 0x1c726ea0U, 0x9b178c27U, 0xbf3a8503U,
+	0xd34d9e6fU, 0xa0e1411cU, 0x8189083dU, 0x6291f3deU,
+	0x516d3cedU, 0xff6a9543U, 0x5d623fe1U, 0x54287ce8U,
+	0x26c4e29aU, 0x858c0939U, 0xb335860fU, 0xf5e01549U,
+	0xae6ec012U, 0x35102589U, 0x73c5b6cfU, 0xc61cda7aU,
+	0xa321821fU, 0xfdea1741U, 0x3b9fa487U, 0xbe7ac402U,
+	0x19372ea5U, 0x695b32d5U, 0xa261c31eU, 0x67d4b3dbU,
+	0x4f8ac54bU, 0x56d48252U, 0xb349fab7U, 0xef02edebU,
+	0x27f8df23U, 0x9361f297U, 0xe0cd2de4U, 0xca6fa5ceU,
+	0x4c4a0648U, 0xce6aa4caU, 0x44400440U, 0x1fced11bU,
+	0xb48c38b0U, 0xfb13e8ffU, 0xa4983ca0U, 0x00151504U,
+	0xb8833bbcU, 0x89ff768dU, 0x7ef6887aU, 0xcdaa67c9U,
+	0xbc863ab8U, 0xee42aceaU, 0x04101400U, 0x1bcbd01fU,
+	0xbe06b8baU, 0x05505501U, 0x585b035cU, 0x8cba3688U,
+	0x31695835U, 0x0e9a940aU, 0x0c1a1608U, 0x72f98b76U,
+	0x66e88e62U, 0x1d4e5319U, 0x55144151U, 0xc720e7c3U,
+	0x52d18356U, 0xab57fcafU, 0x76fc8a72U, 0xfd966bf9U,
+	0xd8fb23dcU, 0x45004541U, 0x38231b3cU, 0x194b521dU,
+	0xa35dfea7U, 0x2bf7dc2fU, 0xa618bea2U, 0x9dee7399U,
+	0x8770f783U, 0xda7ba1deU, 0x64680c60U, 0xb209bbb6U,
+	0x33e9da37U, 0x342c1830U, 0xb74cfbb3U, 0x42c58746U,
+	0x16849212U, 0x6ba7cc6fU, 0x46c08642U, 0xff16e9fbU,
+	0xc1a564c5U, 0x2ab79d2eU, 0xe8c72fecU, 0x8e3ab48aU,
+	0x095f560dU, 0x081f170cU, 0x8a3fb58eU, 0xe708efe3U,
+	0xdb3be0dfU, 0xd9bb62ddU, 0x484f074cU, 0xfe56a8faU,
+	0x1a8b911eU, 0x71394875U, 0xe4c82ce0U, 0x70790974U,
+	0x8f7af58bU, 0x02959706U, 0x29775e2dU, 0xb60cbab2U,
+	0xb5cc79b1U, 0xb9c37abdU, 0x12819316U, 0xde7ea0daU,
+	0xd1b160d5U, 0x85f07581U, 0x7fb6c97bU, 0x3aa3993eU,
+	0xf71cebf3U, 0xbb43f8bfU, 0x28371f2cU, 0x39635a3dU,
+	0xd5b461d1U, 0x4d0a4749U, 0x62ed8f66U, 0xc0e525c4U,
+	0x6c620e68U, 0x84b03480U, 0x2c321e28U, 0x6d224f69U,
+	0x99eb729dU, 0x753c4971U, 0x5391c257U, 0x9e2eb09aU,
+	0x80b53584U, 0x9cae3298U, 0xe5886de1U, 0xf8d32bfcU,
+	0x10011114U, 0x90a13194U, 0xf59c69f1U, 0x0a9f950eU,
+	0x4780c743U, 0x14041010U, 0x0d5a5709U, 0x490f464dU,
+	0x6ae78d6eU, 0x5b9bc05fU, 0x88bf378cU, 0x5d1e4359U,
+	0x25785d21U, 0xccea26c8U, 0x1c0e1218U, 0x8b7ff48fU,
+	0x9624b292U, 0x07d0d703U, 0x37ecdb33U, 0x6ee28c6aU,
+	0xbf46f9bbU, 0xea47adeeU, 0x3ea6983aU, 0x5f9ec15bU,
+	0x15445111U, 0x0bdfd40fU, 0xf4dc28f0U, 0xc265a7c6U,
+	0x9a2bb19eU, 0xcb2fe4cfU, 0xdf3ee1dbU, 0x2ff2dd2bU,
+	0xf319eaf7U, 0x91e17095U, 0x40450544U, 0x9221b396U,
+	0x9764f393U, 0xae12bcaaU, 0x612d4c65U, 0x8630b682U,
+	0x7af3897eU, 0xc4e024c0U, 0x7c760a78U, 0x41054445U,
+	0xa21dbfa6U, 0x5adb815eU, 0xdcfe22d8U, 0xd4f420d0U,
+	0xc325e6c7U, 0x26b89e22U, 0xd271a3d6U, 0x9b6bf09fU,
+	0x606d0d64U, 0xcf2ae5cbU, 0x9f6ef19bU, 0xac923ea8U,
+	0x1e8e901aU, 0x8dfa7789U, 0x3fe6d93bU, 0x95e47191U,
+	0x17c4d313U, 0xb08939b4U, 0x98ab339cU, 0xba03b9beU,
+	0x203d1d24U, 0x591b425dU, 0x2d725f29U, 0xa9d77eadU,
+	0x4385c647U, 0x7bb3c87fU, 0xc8ef27ccU, 0x01555405U,
+	0x6fa2cd6bU, 0xe30deee7U, 0x2eb29c2aU, 0xa8973facU,
+	0x180b131cU, 0xeb07ecefU, 0xbdc67bb9U, 0xddbe63d9U,
+	0xa1dd7ca5U, 0xa5d87da1U, 0x79334a7dU, 0x51114055U,
+	0x22bd9f26U, 0x78730b7cU, 0xaa17bdaeU, 0x5ede805aU,
+	0xfa53a9feU, 0x63adce67U, 0xe648aee2U, 0xe9876eedU,
+	0x50510154U, 0x73b9ca77U, 0x65284d61U, 0x11415015U,
+	0x68670f6cU, 0x54540050U, 0x7d364b79U, 0x77bccb73U,
+	0x3c261a38U, 0x13c1d217U, 0xd0f121d4U, 0x36ac9a32U,
+	0x8235b786U, 0x23fdde27U, 0xf9936afdU, 0xe24dafe6U,
+	0xa09d3da4U, 0xc5a065c1U, 0x5794c353U, 0x5c5e0258U,
+	0xf65caaf2U, 0x8375f687U, 0xc660a6c2U, 0xd674a2d2U,
+	0x32a99b36U, 0xb1c978b5U, 0x4acf854eU, 0x3d665b39U,
+	0x24381c20U, 0x4eca844aU, 0xc9af66cdU, 0xed826fe9U,
+	0x81f57485U, 0xf259abf6U, 0xd331e2d7U, 0x30291934U,
+	0x03d5d607U, 0xadd27fa9U, 0x0fdad50bU, 0x06909602U,
+	0x747c0870U, 0xd734e3d3U, 0xe18d6ce5U, 0xa758ffa3U,
+	0xfcd62af8U, 0x67a8cf63U, 0x217d5c25U, 0x94a43090U,
+	0xf19968f5U, 0xaf52fdabU, 0x69274e6dU, 0xecc22ee8U,
+	0x4b8fc44fU, 0x3be3d83fU, 0xf0d929f4U, 0x356c5931U,
+	0x944eda54U, 0x8d109d4dU, 0x688de5a8U, 0x34c6f2f4U,
+	0xfc3cc03cU, 0x48a5ed88U, 0x3b0932fbU, 0x11abbad1U,
+	0x978e1957U, 0x15aebbd5U, 0x9f841b5fU, 0xc40ace04U,
+	0x6f4827afU, 0x20d7f7e0U, 0x7f5c23bfU, 0xdbd10a1bU,
+	0x634724a3U, 0x523b6992U, 0xa5329765U, 0x166e78d6U,
+	0x674225a7U, 0x3586b3f5U, 0xdfd40b1fU, 0xc00fcf00U,
+	0x65c2a7a5U, 0xde944a1eU, 0x839f1c43U, 0x577e2997U,
+	0xeaad472aU, 0xd55e8b15U, 0xd7de0917U, 0xa93d9469U,
+	0xbd2c917dU, 0xc68a4c06U, 0x8ed05e4eU, 0x1ce4f8dcU,
+	0x89159c49U, 0x7093e3b0U, 0xad38956dU, 0x265274e6U,
+	0x033f3cc3U, 0x9ec45a5eU, 0xe3e70423U, 0xc28f4d02U,
+	0x7899e1b8U, 0xf033c330U, 0x7ddca1bdU, 0x462a6c86U,
+	0x5cb4e89cU, 0x01bfbec1U, 0xbfac137fU, 0x69cda4a9U,
+	0xe82dc528U, 0xefe8072fU, 0x6c88e4acU, 0x99019859U,
+	0xcd408d0dU, 0xb063d370U, 0x9d04995dU, 0x24d2f6e4U,
+	0x1a617bdaU, 0xf1738231U, 0x330330f3U, 0x55feab95U,
+	0xd29b4912U, 0xd3db0813U, 0x51fbaa91U, 0x3cccf0fcU,
+	0x00ffffc0U, 0x027f7dc2U, 0x938b1853U, 0x2592b7e5U,
+	0xc14f8e01U, 0xaafd576aU, 0x3f0c33ffU, 0xabbd166bU,
+	0x54beea94U, 0xd9518819U, 0xf2b34132U, 0x6dc8a5adU,
+	0x6e0866aeU, 0x620765a2U, 0xc9458c09U, 0x05babfc5U,
+	0x0a757fcaU, 0x5e346a9eU, 0xa472d664U, 0xe1678621U,
+	0x2cd8f4ecU, 0x6087e7a0U, 0xf3f30033U, 0xe2a74522U,
+	0x0e707eceU, 0x96ce5856U, 0xb9299079U, 0x1b213adbU,
+	0xb7a61177U, 0x5f742b9fU, 0xf7f60137U, 0xb6e65076U,
+	0x422f6d82U, 0xaef8566eU, 0x8855dd48U, 0x45eaaf85U,
+	0x5b712a9bU, 0x476a2d87U, 0x3e4c72feU, 0x231734e3U,
+	0xcbc50e0bU, 0x4b652e8bU, 0x2e5876eeU, 0xd15b8a11U,
+	0x9c44d85cU, 0xcfc00f0fU, 0xd69e4816U, 0x92cb5952U,
+	0xb1239271U, 0x805fdf40U, 0x537b2893U, 0x86da5c46U,
+	0xfebc423eU, 0x172e39d7U, 0xc7ca0d07U, 0x50bbeb90U,
+	0x4de0ad8dU, 0xdc14c81cU, 0xec28c42cU, 0xb5269375U,
+	0x6482e6a4U, 0x3183b2f1U, 0xe5628725U, 0x845ade44U,
+	0xce804e0eU, 0xd01bcb10U, 0x2f1837efU, 0x19a1b8d9U,
+	0x41efae81U, 0x10ebfbd0U, 0x04fafec4U, 0xf436c234U,
+	0x28ddf5e8U, 0x4a256f8aU, 0x9b811a5bU, 0x49e5ac89U,
+	0x4ca0ec8cU, 0x75d6a3b5U, 0xbae9537aU, 0x5df4a99dU,
+	0xa1379661U, 0x1f243bdfU, 0xa7b21567U, 0x9ac15b5aU,
+	0x79d9a0b9U, 0x811f9e41U, 0x073a3dc7U, 0x0f303fcfU,
+	0x18e1f9d8U, 0xfd7c813dU, 0x09b5bcc9U, 0x40afef80U,
+	0xbba9127bU, 0x14eefad4U, 0x44aaee84U, 0x775621b7U,
+	0xc54a8f05U, 0x563e6896U, 0xe422c624U, 0x4e206e8eU,
+	0xcc00cc0cU, 0x6b4d26abU, 0x436f2c83U, 0x61c7a6a1U,
+	0xfbf9023bU, 0x82df5d42U, 0xf6b64036U, 0x721361b2U,
+	0x9841d958U, 0xa077d760U, 0x132b38d3U, 0xda914b1aU,
+	0xb466d274U, 0x38c9f1f8U, 0xf5768335U, 0x735320b3U,
+	0xc3cf0c03U, 0x30c3f3f0U, 0x660264a6U, 0x067a7cc6U,
+	0x7a1963baU, 0x7e1c62beU, 0xa2f75562U, 0x8ad55f4aU,
+	0xf9798039U, 0xa3b71463U, 0x71d3a2b1U, 0x851a9f45U,
+	0x2197b6e1U, 0xb869d178U, 0x3d8cb1fdU, 0x324371f2U,
+	0x8b951e4bU, 0xa87dd568U, 0xbeec527eU, 0xca854f0aU,
+	0xb3a31073U, 0x8f901f4fU, 0xa6f25466U, 0xac78d46cU,
+	0xe7e20527U, 0xc805cd08U, 0x0b353ecbU, 0xed68852dU,
+	0x59f1a899U, 0xf839c138U, 0x225775e2U, 0x3989b0f9U,
+	0x7b5922bbU, 0x1e647adeU, 0x8c50dc4cU, 0x879a1d47U,
+	0x2d98b5edU, 0x58b1e998U, 0x1da4b9ddU, 0x0db0bdcdU,
+	0xe96d8429U, 0x6a0d67aaU, 0x910b9a51U, 0xe6a24426U,
+	0xfffc033fU, 0x950e9b55U, 0x126b79d2U, 0x364670f6U,
+	0x5a316b9aU, 0x299db4e9U, 0x08f5fdc8U, 0xebed062bU,
+	0xd811c918U, 0x761660b6U, 0xd41eca14U, 0xdd54891dU,
+	0xafb8176fU, 0x0cf0fcccU, 0x3a4973faU, 0x7c9ce0bcU,
+	0x271235e7U, 0xbc6cd07cU, 0xfab9433aU, 0x4f602f8fU,
+	0x2a5d77eaU, 0x7496e2b4U, 0xb2e35172U, 0x370631f7U,
+	0x904bdb50U, 0xe027c720U, 0x2b1d36ebU, 0xeea8462eU,
+	0x59267ff1U, 0x407838e8U, 0xa5e5400dU, 0xf9ae5751U,
+	0x31546599U, 0x85cd482dU, 0xf661975eU, 0xdcc31f74U,
+	0x5ae6bcf2U, 0xd8c61e70U, 0x52ecbefaU, 0x09626ba1U,
+	0xa220820aU, 0xedbf5245U, 0xb234861aU, 0x16b9afbeU,
+	0xae2f8106U, 0x9f53cc37U, 0x685a32c0U, 0xdb06dd73U,
+	0xaa2a8002U, 0xf8ee1650U, 0x12bcaebaU, 0x0d676aa5U,
+	0xa8aa0200U, 0x13fcefbbU, 0x4ef7b9e6U, 0x9a168c32U,
+	0x27c5e28fU, 0x18362eb0U, 0x1ab6acb2U, 0x645531ccU,
+	0x704434d8U, 0x0be2e9a3U, 0x43b8fbebU, 0xd18c5d79U,
+	0x447d39ecU, 0xbdfb4615U, 0x605030c8U, 0xeb3ad143U,
+	0xce579966U, 0x53acfffbU, 0x2e8fa186U, 0x0fe7e8a7U,
+	0xb5f1441dU, 0x3d5b6695U, 0xb0b40418U, 0x8b42c923U,
+	0x91dc4d39U, 0xccd71b64U, 0x72c4b6daU, 0xa4a5010cU,
+	0x2545608dU, 0x2280a28aU, 0xa1e04109U, 0x54693dfcU,
+	0x002828a8U, 0x7d0b76d5U, 0x506c3cf8U, 0xe9ba5341U,
+	0xd709de7fU, 0x3c1b2794U, 0xfe6b9556U, 0x98960e30U,
+	0x1ff3ecb7U, 0x1eb3adb6U, 0x9c930f34U, 0xf1a45559U,
+	0xcd975a65U, 0xcf17d867U, 0x5ee3bdf6U, 0xe8fa1240U,
+	0x0c272ba4U, 0x6795f2cfU, 0xf264965aU, 0x66d5b3ceU,
+	0x99d64f31U, 0x14392dbcU, 0x3fdbe497U, 0xa0a00008U,
+	0xa360c30bU, 0xaf6fc007U, 0x042d29acU, 0xc8d21a60U,
+	0xc71dda6fU, 0x935ccf3bU, 0x691a73c1U, 0x2c0f2384U,
+	0xe1b05149U, 0xadef4205U, 0x3e9ba596U, 0x2fcfe087U,
+	0xc318db6bU, 0x5ba6fdf3U, 0x744135dcU, 0xd6499f7eU,
+	0x7aceb4d2U, 0x921c8e3aU, 0x3a9ea492U, 0x7b8ef5d3U,
+	0x8f47c827U, 0x6390f3cbU, 0x453d78edU, 0x88820a20U,
+	0x96198f3eU, 0x8a028822U, 0xf324d75bU, 0xee7f9146U,
+	0x06adabaeU, 0x860d8b2eU, 0xe330d34bU, 0x1c332fb4U,
+	0x512c7df9U, 0x02a8aaaaU, 0x1bf6edb3U, 0x5fa3fcf7U,
+	0x7c4b37d4U, 0x4d377ae5U, 0x9e138d36U, 0x4bb2f9e3U,
+	0x33d4e79bU, 0xda469c72U, 0x0aa2a8a2U, 0x9dd34e35U,
+	0x80880828U, 0x117c6db9U, 0x21406189U, 0x784e36d0U,
+	0xa9ea4301U, 0xfceb1754U, 0x280a2280U, 0x49327be1U,
+	0x03e8ebabU, 0x1d736eb5U, 0xe270924aU, 0xd4c91d7cU,
+	0x8c870b24U, 0xdd835e75U, 0xc9925b61U, 0x395e6791U,
+	0xe5b5504dU, 0x874dca2fU, 0x56e9bffeU, 0x848d092cU,
+	0x81c84929U, 0xb8be0610U, 0x7781f6dfU, 0x909c0c38U,
+	0x6c5f33c4U, 0xd24c9e7aU, 0x6adab0c2U, 0x57a9feffU,
+	0xb4b1051cU, 0x4c773be4U, 0xca529862U, 0xc2589a6aU,
+	0xd5895c7dU, 0x30142498U, 0xc4dd196cU, 0x8dc74a25U,
+	0x76c1b7deU, 0xd9865f71U, 0x89c24b21U, 0xba3e8412U,
+	0x08222aa0U, 0x9b56cd33U, 0x294a6381U, 0x8348cb2bU,
+	0x016869a9U, 0xa625830eU, 0x8e078926U, 0xacaf0304U,
+	0x3691a79eU, 0x4fb7f8e7U, 0x3bdee593U, 0xbf7bc417U,
+	0x55297cfdU, 0x6d1f72c5U, 0xde439d76U, 0x17f9eebfU,
+	0x790e77d1U, 0xf5a1545dU, 0x381e2690U, 0xbe3b8516U,
+	0x0ea7a9a6U, 0xfdab5655U, 0xab6ac103U, 0xcb12d963U,
+	0xb771c61fU, 0xb374c71bU, 0x6f9ff0c7U, 0x47bdfaefU,
+	0x3411259cU, 0x6edfb1c6U, 0xbcbb0714U, 0x48723ae0U,
+	0xecff1344U, 0x750174ddU, 0xf0e41458U, 0xff2bd457U,
+	0x46fdbbeeU, 0x651570cdU, 0x7384f7dbU, 0x07edeaafU,
+	0x7ecbb5d6U, 0x42f8baeaU, 0x6b9af1c3U, 0x611071c9U,
+	0x2a8aa082U, 0x056d68adU, 0xc65d9b6eU, 0x20002088U,
+	0x94990d3cU, 0x3551649dU, 0xef3fd047U, 0xf4e1155cU,
+	0xb631871eU, 0xd30cdf7bU, 0x413879e9U, 0x4af2b8e2U,
+	0xe0f01048U, 0x95d94c3dU, 0xd0cc1c78U, 0xc0d81868U,
+	0x2405218cU, 0xa765c20fU, 0x5c633ff4U, 0x2bcae183U,
+	0x3294a69aU, 0x58663ef0U, 0xdf03dc77U, 0xfb2ed553U,
+	0x9759ce3fU, 0xe4f5114cU, 0xc59d586dU, 0x2685a38eU,
+	0x15796cbdU, 0xbb7ec513U, 0x19766fb1U, 0x103c2cb8U,
+	0x62d0b2caU, 0xc1985969U, 0xf721d65fU, 0xb1f44519U,
+	0xea7a9042U, 0x710475d9U, 0x37d1e69fU, 0x82088a2aU,
+	0xe735d24fU, 0xb9fe4711U, 0x7f8bf4d7U, 0xfa6e9452U,
+	0x5d237ef5U, 0x2d4f6285U, 0xe675934eU, 0x23c0e38bU,
+	0x0fcbc44aU, 0x16958353U, 0xf308fbb6U, 0xaf43eceaU,
+	0x67b9de22U, 0xd320f396U, 0xa08c2ce5U, 0x8a2ea4cfU,
+	0x0c0b0749U, 0x8e2ba5cbU, 0x04010541U, 0x5f8fd01aU,
+	0xf4cd39b1U, 0xbb52e9feU, 0xe4d93da1U, 0x40541405U,
+	0xf8c23abdU, 0xc9be778cU, 0x3eb7897bU, 0x8deb66c8U,
+	0xfcc73bb9U, 0xae03adebU, 0x44511501U, 0x5b8ad11eU,
+	0xfe47b9bbU, 0x45115400U, 0x181a025dU, 0xccfb3789U,
+	0x71285934U, 0x4edb950bU, 0x4c5b1709U, 0x32b88a77U,
+	0x26a98f63U, 0x5d0f5218U, 0x15554050U, 0x8761e6c2U,
+	0x12908257U, 0xeb16fdaeU, 0x36bd8b73U, 0xbdd76af8U,
+	0x98ba22ddU, 0x05414440U, 0x78621a3dU, 0x590a531cU,
+	0xe31cffa6U, 0x6bb6dd2eU, 0xe659bfa3U, 0xddaf7298U,
+	0xc731f682U, 0x9a3aa0dfU, 0x24290d61U, 0xf248bab7U,
+	0x73a8db36U, 0x746d1931U, 0xf70dfab2U, 0x02848647U,
+	0x56c59313U, 0x2be6cd6eU, 0x06818743U, 0xbf57e8faU,
+	0x81e465c4U, 0x6af69c2fU, 0xa8862eedU, 0xce7bb58bU,
+	0x491e570cU, 0x485e160dU, 0xca7eb48fU, 0xa749eee2U,
+	0x9b7ae1deU, 0x99fa63dcU, 0x080e064dU, 0xbe17a9fbU,
+	0x5aca901fU, 0x31784974U, 0xa4892de1U, 0x30380875U,
+	0xcf3bf48aU, 0x42d49607U, 0x69365f2cU, 0xf64dbbb3U,
+	0xf58d78b0U, 0xf9827bbcU, 0x52c09217U, 0x9e3fa1dbU,
+	0x91f061d4U, 0xc5b17480U, 0x3ff7c87aU, 0x7ae2983fU,
+	0xb75deaf2U, 0xfb02f9beU, 0x68761e2dU, 0x79225b3cU,
+	0x95f560d0U, 0x0d4b4648U, 0x22ac8e67U, 0x80a424c5U,
+	0x2c230f69U, 0xc4f13581U, 0x6c731f29U, 0x2d634e68U,
+	0xd9aa739cU, 0x357d4870U, 0x13d0c356U, 0xde6fb19bU,
+	0xc0f43485U, 0xdcef3399U, 0xa5c96ce0U, 0xb8922afdU,
+	0x50401015U, 0xd0e03095U, 0xb5dd68f0U, 0x4ade940fU,
+	0x07c1c642U, 0x54451111U, 0x4d1b5608U, 0x094e474cU,
+	0x2aa68c6fU, 0x1bdac15eU, 0xc8fe368dU, 0x1d5f4258U,
+	0x65395c20U, 0x8cab27c9U, 0x5c4f1319U, 0xcb3ef58eU,
+	0xd665b393U, 0x4791d602U, 0x77adda32U, 0x2ea38d6bU,
+	0xff07f8baU, 0xaa06acefU, 0x7ee7993bU, 0x1fdfc05aU,
+	0x55055010U, 0x4b9ed50eU, 0xb49d29f1U, 0x8224a6c7U,
+	0xda6ab09fU, 0x8b6ee5ceU, 0x9f7fe0daU, 0x6fb3dc2aU,
+	0xb358ebf6U, 0xd1a07194U, 0x00040445U, 0xd260b297U,
+	0xd725f292U, 0xee53bdabU, 0x216c4d64U, 0xc671b783U,
+	0x3ab2887fU, 0x84a125c1U, 0x3c370b79U, 0x01444544U,
+	0xe25cbea7U, 0x1a9a805fU, 0x9cbf23d9U, 0x94b521d1U,
+	0x8364e7c6U, 0x66f99f23U, 0x9230a2d7U, 0xdb2af19eU,
+	0x202c0c65U, 0x8f6be4caU, 0xdf2ff09aU, 0xecd33fa9U,
+	0x5ecf911bU, 0xcdbb7688U, 0x7fa7d83aU, 0xd5a57090U,
+	0x5785d212U, 0xf0c838b5U, 0xd8ea329dU, 0xfa42b8bfU,
+	0x607c1c25U, 0x195a435cU, 0x6d335e28U, 0xe9967facU,
+	0x03c4c746U, 0x3bf2c97eU, 0x88ae26cdU, 0x41145504U,
+	0x2fe3cc6aU, 0xa34cefe6U, 0x6ef39d2bU, 0xe8d63eadU,
+	0x584a121dU, 0xab46edeeU, 0xfd877ab8U, 0x9dff62d8U,
+	0xe19c7da4U, 0xe5997ca0U, 0x39724b7cU, 0x11504154U,
+	0x62fc9e27U, 0x38320a7dU, 0xea56bcafU, 0x1e9f815bU,
+	0xba12a8ffU, 0x23eccf66U, 0xa609afe3U, 0xa9c66fecU,
+	0x10100055U, 0x33f8cb76U, 0x25694c60U, 0x51005114U,
+	0x28260e6dU, 0x14150151U, 0x3d774a78U, 0x37fdca72U,
+	0x7c671b39U, 0x5380d316U, 0x90b020d5U, 0x76ed9b33U,
+	0xc274b687U, 0x63bcdf26U, 0xb9d26bfcU, 0xa20caee7U,
+	0xe0dc3ca5U, 0x85e164c0U, 0x17d5c252U, 0x1c1f0359U,
+	0xb61dabf3U, 0xc334f786U, 0x8621a7c3U, 0x9635a3d3U,
+	0x72e89a37U, 0xf18879b4U, 0x0a8e844fU, 0x7d275a38U,
+	0x64791d21U, 0x0e8b854bU, 0x89ee67ccU, 0xadc36ee8U,
+	0xc1b47584U, 0xb218aaf7U, 0x9370e3d6U, 0x70681835U,
+	0x4394d706U, 0xed937ea8U, 0x4f9bd40aU, 0x46d19703U,
+	0x343d0971U, 0x9775e2d2U, 0xa1cc6de4U, 0xe719fea2U,
+	0xbc972bf9U, 0x27e9ce62U, 0x613c5d24U, 0xd4e53191U,
+	0xb1d869f4U, 0xef13fcaaU, 0x29664f6cU, 0xac832fe9U,
+	0x0bcec54eU, 0x7ba2d93eU, 0xb09828f5U, 0x752d5830U,
+	0x049d9917U, 0x1dc3de0eU, 0xf85ea6ebU, 0xa415b1b7U,
+	0x6cef837fU, 0xd876aecbU, 0xabda71b8U, 0x8178f992U,
+	0x075d5a14U, 0x857df896U, 0x0f57581cU, 0x54d98d47U,
+	0xff9b64ecU, 0xb004b4a3U, 0xef8f60fcU, 0x4b024958U,
+	0xf39467e0U, 0xc2e82ad1U, 0x35e1d426U, 0x86bd3b95U,
+	0xf79166e4U, 0xa555f0b6U, 0x4f07485cU, 0x50dc8c43U,
+	0xf511e4e6U, 0x4e47095dU, 0x134c5f00U, 0xc7ad6ad4U,
+	0x7a7e0469U, 0x458dc856U, 0x470d4a54U, 0x39eed72aU,
+	0x2dffd23eU, 0x56590f45U, 0x1e031d0dU, 0x8c37bb9fU,
+	0x19c6df0aU, 0xe040a0f3U, 0x3debd62eU, 0xb68137a5U,
+	0x93ec7f80U, 0x0e17191dU, 0x73344760U, 0x525c0e41U,
+	0xe84aa2fbU, 0x60e08073U, 0xed0fe2feU, 0xd6f92fc5U,
+	0xcc67abdfU, 0x916cfd82U, 0x2f7f503cU, 0xf91ee7eaU,
+	0x78fe866bU, 0x7f3b446cU, 0xfc5ba7efU, 0x09d2db1aU,
+	0x5d93ce4eU, 0x20b09033U, 0x0dd7da1eU, 0xb401b5a7U,
+	0x8ab23899U, 0x61a0c172U, 0xa3d073b0U, 0xc52de8d6U,
+	0x42480a51U, 0x43084b50U, 0xc128e9d2U, 0xac1fb3bfU,
+	0x902cbc83U, 0x92ac3e81U, 0x03585b10U, 0xb541f4a6U,
+	0x519ccd42U, 0x3a2e1429U, 0xafdf70bcU, 0x3b6e5528U,
+	0xc46da9d7U, 0x4982cb5aU, 0x62600271U, 0xfd1be6eeU,
+	0xfedb25edU, 0xf2d426e1U, 0x5996cf4aU, 0x9569fc86U,
+	0x9aa63c89U, 0xcee729ddU, 0x34a19527U, 0x71b4c562U,
+	0xbc0bb7afU, 0xf054a4e3U, 0x63204370U, 0x72740661U,
+	0x9ea33d8dU, 0x061d1b15U, 0x29fad33aU, 0x8bf27998U,
+	0x27755234U, 0xcfa768dcU, 0x67254274U, 0x26351335U,
+	0xd2fc2ec1U, 0x3e2b152dU, 0x18869e0bU, 0xd539ecc6U,
+	0xcba269d8U, 0xd7b96ec4U, 0xae9f31bdU, 0xb3c477a0U,
+	0x5b164d48U, 0xdbb66dc8U, 0xbe8b35adU, 0x4188c952U,
+	0x0c979b1fU, 0x5f134c4cU, 0x464d0b55U, 0x02181a11U,
+	0x21f0d132U, 0x108c9c03U, 0xc3a86bd0U, 0x16091f05U,
+	0x6e6f017dU, 0x87fd7a94U, 0x57194e44U, 0xc068a8d3U,
+	0xdd33eeceU, 0x4cc78b5fU, 0x7cfb876fU, 0x25f5d036U,
+	0xf451a5e7U, 0xa150f1b2U, 0x75b1c466U, 0x14899d07U,
+	0x5e530d4dU, 0x40c88853U, 0xbfcb74acU, 0x8972fb9aU,
+	0xd13cedc2U, 0x8038b893U, 0x9429bd87U, 0x64e58177U,
+	0xb80eb6abU, 0xdaf62cc9U, 0x0b525918U, 0xd936efcaU,
+	0xdc73afcfU, 0xe505e0f6U, 0x2a3a1039U, 0xcd27eadeU,
+	0x31e4d522U, 0x8ff7789cU, 0x37615624U, 0x0a121819U,
+	0xe90ae3faU, 0x11ccdd02U, 0x97e97e84U, 0x9fe37c8cU,
+	0x8832ba9bU, 0x6dafc27eU, 0x9966ff8aU, 0xd07cacc3U,
+	0x2b7a5138U, 0x843db997U, 0xd479adc7U, 0xe78562f4U,
+	0x5599cc46U, 0xc6ed2bd5U, 0x74f18567U, 0xdef32dcdU,
+	0x5cd38f4fU, 0xfb9e65e8U, 0xd3bc6fc0U, 0xf114e5e2U,
+	0x6b2a4178U, 0x120c1e01U, 0x66650375U, 0xe2c022f1U,
+	0x08929a1bU, 0x30a49423U, 0x83f87b90U, 0x4a420859U,
+	0x24b59137U, 0xa81ab2bbU, 0x65a5c076U, 0xe38063f0U,
+	0x531c4f40U, 0xa010b0b3U, 0xf6d127e5U, 0x96a93f85U,
+	0xeaca20f9U, 0xeecf21fdU, 0x32241621U, 0x1a061c09U,
+	0x69aac37aU, 0x33645720U, 0xe100e1f2U, 0x15c9dc06U,
+	0xb144f5a2U, 0x28ba923bU, 0xad5ff2beU, 0xa29032b1U,
+	0x1b465d08U, 0x38ae962bU, 0x2e3f113dU, 0x5a560c49U,
+	0x23705330U, 0x1f435c0cU, 0x36211725U, 0x3cab972fU,
+	0x77314664U, 0x58d68e4bU, 0x9be67d88U, 0x7dbbc66eU,
+	0xc922ebdaU, 0x68ea827bU, 0xb28436a1U, 0xa95af3baU,
+	0xeb8a61f8U, 0x8eb7399dU, 0x1c839f0fU, 0x17495e04U,
+	0xbd4bf6aeU, 0xc862aadbU, 0x8d77fa9eU, 0x9d63fe8eU,
+	0x79bec76aU, 0xfade24e9U, 0x01d8d912U, 0x76710765U,
+	0x6f2f407cU, 0x05ddd816U, 0x82b83a91U, 0xa69533b5U,
+	0xcae228d9U, 0xb94ef7aaU, 0x9826be8bU, 0x7b3e4568U,
+	0x48c28a5bU, 0xe6c523f5U, 0x44cd8957U, 0x4d87ca5eU,
+	0x3f6b542cU, 0x9c23bf8fU, 0xaa9a30b9U, 0xec4fa3ffU,
+	0xb7c176a4U, 0x2cbf933fU, 0x6a6a0079U, 0xdfb36cccU,
+	0xba8e34a9U, 0xe445a1f7U, 0x22301231U, 0xa7d572b4U,
+	0x00989813U, 0x70f48463U, 0xbbce75a8U, 0x7e7b056dU,
+	0xe5a84dc3U, 0xfcf60adaU, 0x196b723fU, 0x45206563U,
+	0x8dda57abU, 0x39437a1fU, 0x4aefa56cU, 0x604d2d46U,
+	0xe6688ec0U, 0x64482c42U, 0xee628cc8U, 0xb5ec5993U,
+	0x1eaeb038U, 0x51316077U, 0x0ebab428U, 0xaa379d8cU,
+	0x12a1b334U, 0x23ddfe05U, 0xd4d400f2U, 0x6788ef41U,
+	0x16a4b230U, 0x44602462U, 0xae329c88U, 0xb1e95897U,
+	0x14243032U, 0xaf72dd89U, 0xf2798bd4U, 0x2698be00U,
+	0x9b4bd0bdU, 0xa4b81c82U, 0xa6389e80U, 0xd8db03feU,
+	0xccca06eaU, 0xb76cdb91U, 0xff36c9d9U, 0x6d026f4bU,
+	0xf8f30bdeU, 0x01757427U, 0xdcde02faU, 0x57b4e371U,
+	0x72d9ab54U, 0xef22cdc9U, 0x920193b4U, 0xb369da95U,
+	0x097f762fU, 0x81d554a7U, 0x0c3a362aU, 0x37ccfb11U,
+	0x2d527f0bU, 0x70592956U, 0xce4a84e8U, 0x182b333eU,
+	0x99cb52bfU, 0x9e0e90b8U, 0x1d6e733bU, 0xe8e70fceU,
+	0xbca61a9aU, 0xc18544e7U, 0xece20ecaU, 0x55346173U,
+	0x6b87ec4dU, 0x809515a6U, 0x42e5a764U, 0x24183c02U,
+	0xa37dde85U, 0xa23d9f84U, 0x201d3d06U, 0x4d2a676bU,
+	0x71196857U, 0x7399ea55U, 0xe26d8fc4U, 0x54742072U,
+	0xb0a91996U, 0xdb1bc0fdU, 0x4eeaa468U, 0xda5b81fcU,
+	0x25587d03U, 0xa8b71f8eU, 0x8355d6a5U, 0x1c2e323aU,
+	0x1feef139U, 0x13e1f235U, 0xb8a31b9eU, 0x745c2852U,
+	0x7b93e85dU, 0x2fd2fd09U, 0xd59441f3U, 0x908111b6U,
+	0x5d3e637bU, 0x11617037U, 0x821597a4U, 0x9341d2b5U,
+	0x7f96e959U, 0xe728cfc1U, 0xc8cf07eeU, 0x6ac7ad4cU,
+	0xc64086e0U, 0x2e92bc08U, 0x861096a0U, 0xc700c7e1U,
+	0x33c9fa15U, 0xdf1ec1f9U, 0xf9b34adfU, 0x340c3812U,
+	0x2a97bd0cU, 0x368cba10U, 0x4faae569U, 0x52f1a374U,
+	0xba23999cU, 0x3a83b91cU, 0x5fbee179U, 0xa0bd1d86U,
+	0xeda24fcbU, 0xbe269898U, 0xa778df81U, 0xe32dcec5U,
+	0xc0c505e6U, 0xf1b948d7U, 0x229dbf04U, 0xf73ccbd1U,
+	0x8f5ad5a9U, 0x66c8ae40U, 0xb62c9a90U, 0x215d7c07U,
+	0x3c063a1aU, 0xadf25f8bU, 0x9dce53bbU, 0xc4c004e2U,
+	0x15647133U, 0x40652566U, 0x948410b2U, 0xf5bc49d3U,
+	0xbf66d999U, 0xa1fd5c87U, 0x5efea078U, 0x68472f4eU,
+	0x30093916U, 0x610d6c47U, 0x751c6953U, 0x85d055a3U,
+	0x593b627fU, 0x3bc3f81dU, 0xea678dccU, 0x38033b1eU,
+	0x3d467b1bU, 0x04303422U, 0xcb0fc4edU, 0x2c123e0aU,
+	0xd0d101f6U, 0x6ec2ac48U, 0xd65482f0U, 0xeb27cccdU,
+	0x083f372eU, 0xf0f909d6U, 0x76dcaa50U, 0x7ed6a858U,
+	0x69076e4fU, 0x8c9a16aaU, 0x78532b5eU, 0x31497817U,
+	0xca4f85ecU, 0x65086d43U, 0x354c7913U, 0x06b0b620U,
+	0xb4ac1892U, 0x27d8ff01U, 0x95c451b3U, 0x3fc6f919U,
+	0xbde65b9bU, 0x1aabb13cU, 0x3289bb14U, 0x10213136U,
+	0x8a1f95acU, 0xf339cad5U, 0x8750d7a1U, 0x03f5f625U,
+	0xe9a74ecfU, 0xd19140f7U, 0x62cdaf44U, 0xab77dc8dU,
+	0xc58045e3U, 0x492f666fU, 0x849014a2U, 0x02b5b724U,
+	0xb2299b94U, 0x41256467U, 0x17e4f331U, 0x779ceb51U,
+	0x0bfff42dU, 0x0ffaf529U, 0xd311c2f5U, 0xfb33c8ddU,
+	0x889f17aeU, 0xd25183f4U, 0x00353526U, 0xf4fc08d2U,
+	0x50712176U, 0xc98f46efU, 0x4c6a266aU, 0x43a5e665U,
+	0xfa7389dcU, 0xd99b42ffU, 0xcf0ac5e9U, 0xbb63d89dU,
+	0xc24587e4U, 0xfe7688d8U, 0xd714c3f1U, 0xdd9e43fbU,
+	0x960492b0U, 0xb9e35a9fU, 0x7ad3a95cU, 0x9c8e12baU,
+	0x28173f0eU, 0x89df56afU, 0x53b1e275U, 0x486f276eU,
+	0x0abfb52cU, 0x6f82ed49U, 0xfdb64bdbU, 0xf67c8ad0U,
+	0x5c7e227aU, 0x29577e0fU, 0x6c422e4aU, 0x7c562a5aU,
+	0x988b13beU, 0x1bebf03dU, 0xe0ed0dc6U, 0x9744d3b1U,
+	0x8e1a94a8U, 0xe4e80cc2U, 0x638dee45U, 0x47a0e761U,
+	0x2bd7fc0dU, 0x587b237eU, 0x79136a5fU, 0x9a0b91bcU,
+	0xa9f75e8fU, 0x07f0f721U, 0xa5f85d83U, 0xacb21e8aU,
+	0xde5e80f8U, 0x7d166b5bU, 0x4bafe46dU, 0x0d7a772bU,
+	0x56f4a270U, 0xcd8a47ebU, 0x8b5fd4adU, 0x3e86b818U,
+	0x5bbbe07dU, 0x05707523U, 0xc305c6e5U, 0x46e0a660U,
+	0xe1ad4cc7U, 0x91c150b7U, 0x5afba17cU, 0x9f4ed1b9U,
+	0x36c6f07eU, 0x2f98b767U, 0xca05cf82U, 0x964ed8deU,
+	0x5eb4ea16U, 0xea2dc7a2U, 0x998118d1U, 0xb32390fbU,
+	0x3506337dU, 0xb72691ffU, 0x3d0c3175U, 0x6682e42eU,
+	0xcdc00d85U, 0x825fddcaU, 0xddd40995U, 0x79592031U,
+	0xc1cf0e89U, 0xf0b343b8U, 0x07babd4fU, 0xb4e652fcU,
+	0xc5ca0f8dU, 0x970e99dfU, 0x7d5c2135U, 0x6287e52aU,
+	0xc74a8d8fU, 0x7c1c6034U, 0x21173669U, 0xf5f603bdU,
+	0x48256d00U, 0x77d6a13fU, 0x7556233dU, 0x0bb5be43U,
+	0x1fa4bb57U, 0x6402662cU, 0x2c587464U, 0xbe6cd2f6U,
+	0x2b9db663U, 0xd21bc99aU, 0x0fb0bf47U, 0x84da5eccU,
+	0xa1b716e9U, 0x3c4c7074U, 0x416f2e09U, 0x60076728U,
+	0xda11cb92U, 0x52bbe91aU, 0xdf548b97U, 0xe4a246acU,
+	0xfe3cc2b6U, 0xa33794ebU, 0x1d243955U, 0xcb458e83U,
+	0x4aa5ef02U, 0x4d602d05U, 0xce00ce86U, 0x3b89b273U,
+	0x6fc8a727U, 0x12ebf95aU, 0x3f8cb377U, 0x865adcceU,
+	0xb8e951f0U, 0x53fba81bU, 0x918b1ad9U, 0xf77681bfU,
+	0x70136338U, 0x71532239U, 0xf37380bbU, 0x9e44dad6U,
+	0xa277d5eaU, 0xa0f757e8U, 0x31033279U, 0x871a9dcfU,
+	0x63c7a42bU, 0x08757d40U, 0x9d8419d5U, 0x09353c41U,
+	0xf636c0beU, 0x7bd9a233U, 0x503b6b18U, 0xcf408f87U,
+	0xcc804c84U, 0xc08f4f88U, 0x6bcda623U, 0xa73295efU,
+	0xa8fd55e0U, 0xfcbc40b4U, 0x06fafc4eU, 0x43efac0bU,
+	0x8e50dec6U, 0xc20fcd8aU, 0x517b2a19U, 0x402f6f08U,
+	0xacf854e4U, 0x3446727cU, 0x1ba1ba53U, 0xb9a910f1U,
+	0x152e3b5dU, 0xfdfc01b5U, 0x557e2b1dU, 0x146e7a5cU,
+	0xe0a747a8U, 0x0c707c44U, 0x2addf762U, 0xe76285afU,
+	0xf9f900b1U, 0xe5e207adU, 0x9cc458d4U, 0x819f1ec9U,
+	0x694d2421U, 0xe9ed04a1U, 0x8cd05cc4U, 0x73d3a03bU,
+	0x3eccf276U, 0x6d482525U, 0x7416623cU, 0x30437378U,
+	0x13abb85bU, 0x22d7f56aU, 0xf1f302b9U, 0x2452766cU,
+	0x5c346814U, 0xb5a613fdU, 0x6542272dU, 0xf233c1baU,
+	0xef6887a7U, 0x7e9ce236U, 0x4ea0ee06U, 0x17aeb95fU,
+	0xc60acc8eU, 0x930b98dbU, 0x47eaad0fU, 0x26d2f46eU,
+	0x6c086424U, 0x7293e13aU, 0x8d901dc5U, 0xbb2992f3U,
+	0xe36784abU, 0xb263d1faU, 0xa672d4eeU, 0x56bee81eU,
+	0x8a55dfc2U, 0xe8ad45a0U, 0x39093071U, 0xeb6d86a3U,
+	0xee28c6a6U, 0xd75e899fU, 0x18617950U, 0xff7c83b7U,
+	0x03bfbc4bU, 0xbdac11f5U, 0x053a3f4dU, 0x38497170U,
+	0xdb518a93U, 0x2397b46bU, 0xa5b217edU, 0xadb815e5U,
+	0xba69d3f2U, 0x5ff4ab17U, 0xab3d96e3U, 0xe227c5aaU,
+	0x19213851U, 0xb666d0feU, 0xe622c4aeU, 0xd5de0b9dU,
+	0x67c2a52fU, 0xf4b642bcU, 0x46aaec0eU, 0xeca844a4U,
+	0x6e88e626U, 0xc9c50c81U, 0xe1e706a9U, 0xc34f8c8bU,
+	0x59712811U, 0x20577768U, 0x543e6a1cU, 0xd09b4b98U,
+	0x3ac9f372U, 0x02fffd4aU, 0xb1a312f9U, 0x78196130U,
+	0x16eef85eU, 0x9a41dbd2U, 0x57fea91fU, 0xd1db0a99U,
+	0x61472629U, 0x924bd9daU, 0xc48a4e8cU, 0xa4f256ecU,
+	0xd8914990U, 0xdc944894U, 0x007f7f48U, 0x285d7560U,
+	0x5bf1aa13U, 0x013f3e49U, 0xd35b889bU, 0x2792b56fU,
+	0x831f9ccbU, 0x1ae1fb52U, 0x9f049bd7U, 0x90cb5bd8U,
+	0x291d3461U, 0x0af5ff42U, 0x1c647854U, 0x680d6520U,
+	0x112b3a59U, 0x2d183565U, 0x047a7e4cU, 0x0ef0fe46U,
+	0x456a2f0dU, 0x6a8de722U, 0xa9bd14e1U, 0x4fe0af07U,
+	0xfb7982b3U, 0x5ab1eb12U, 0x80df5fc8U, 0x9b019ad3U,
+	0xd9d10891U, 0xbcec50f4U, 0x2ed8f666U, 0x2512376dU,
+	0x8f109fc7U, 0xfa39c3b2U, 0xbf2c93f7U, 0xaf3897e7U,
+	0x4be5ae03U, 0xc8854d80U, 0x3383b07bU, 0x442a6e0cU,
+	0x5d742915U, 0x3786b17fU, 0xb0e353f8U, 0x94ce5adcU,
+	0xf8b941b0U, 0x8b159ec3U, 0xaa7dd7e2U, 0x49652c01U,
+	0x7a99e332U, 0xd49e4a9cU, 0x7696e03eU, 0x7fdca337U,
+	0x0d303d45U, 0xae78d6e6U, 0x98c159d0U, 0xde14ca96U,
+	0x859a1fcdU, 0x1ee4fa56U, 0x58316910U, 0xede805a5U,
+	0x88d55dc0U, 0xd61ec89eU, 0x106b7b58U, 0x958e1bddU,
+	0x32c3f17aU, 0x42afed0aU, 0x89951cc1U, 0x4c206c04U,
+	0xc50acf41U, 0xdc548858U, 0x39c9f0bdU, 0x6582e7e1U,
+	0xad78d529U, 0x19e1f89dU, 0x6a4d27eeU, 0x40efafc4U,
+	0xc6ca0c42U, 0x44eaaec0U, 0xcec00e4aU, 0x954edb11U,
+	0x3e0c32baU, 0x7193e2f5U, 0x2e1836aaU, 0x8a951f0eU,
+	0x320331b6U, 0x037f7c87U, 0xf4768270U, 0x472a6dc3U,
+	0x360630b2U, 0x64c2a6e0U, 0x8e901e0aU, 0x914bda15U,
+	0x3486b2b0U, 0x8fd05f0bU, 0xd2db0956U, 0x063a3c82U,
+	0xbbe9523fU, 0x841a9e00U, 0x869a1c02U, 0xf879817cU,
+	0xec688468U, 0x97ce5913U, 0xdf944b5bU, 0x4da0edc9U,
+	0xd851895cU, 0x21d7f6a5U, 0xfc7c8078U, 0x771661f3U,
+	0x527b29d6U, 0xcf804f4bU, 0xb2a31136U, 0x93cb5817U,
+	0x29ddf4adU, 0xa177d625U, 0x2c98b4a8U, 0x176e7993U,
+	0x0df0fd89U, 0x50fbabd4U, 0xeee8066aU, 0x3889b1bcU,
+	0xb969d03dU, 0xbeac123aU, 0x3dccf1b9U, 0xc8458d4cU,
+	0x9c049818U, 0xe127c665U, 0xcc408c48U, 0x7596e3f1U,
+	0x4b256ecfU, 0xa0379724U, 0x624725e6U, 0x04babe80U,
+	0x83df5c07U, 0x829f1d06U, 0x00bfbf84U, 0x6d88e5e9U,
+	0x51bbead5U, 0x533b68d7U, 0xc2cf0d46U, 0x74d6a2f0U,
+	0x900b9b14U, 0xfbb9427fU, 0x6e4826eaU, 0xfaf9037eU,
+	0x05faff81U, 0x88159d0cU, 0xa3f75427U, 0x3c8cb0b8U,
+	0x3f4c73bbU, 0x334370b7U, 0x9801991cU, 0x54feaad0U,
+	0x5b316adfU, 0x0f707f8bU, 0xf536c371U, 0xb0239334U,
+	0x7d9ce1f9U, 0x31c3f2b5U, 0xa2b71526U, 0xb3e35037U,
+	0x5f346bdbU, 0xc78a4d43U, 0xe86d856cU, 0x4a652fceU,
+	0xe6e20462U, 0x0e303e8aU, 0xa6b21422U, 0xe7a24563U,
+	0x136b7897U, 0xffbc437bU, 0xd911c85dU, 0x14aeba90U,
+	0x0a353f8eU, 0x162e3892U, 0x6f0867ebU, 0x725321f6U,
+	0x9a811b1eU, 0x1a213b9eU, 0x7f1c63fbU, 0x801f9f04U,
+	0xcd00cd49U, 0x9e841a1aU, 0x87da5d03U, 0xc38f4c47U,
+	0xe0678764U, 0xd11bca55U, 0x023f3d86U, 0xd79e4953U,
+	0xaff8572bU, 0x466a2cc2U, 0x968e1812U, 0x01fffe85U,
+	0x1ca4b898U, 0x8d50dd09U, 0xbd6cd139U, 0xe4628660U,
+	0x35c6f3b1U, 0x60c7a7e4U, 0xb4269230U, 0xd51ecb51U,
+	0x9fc45b1bU, 0x815fde05U, 0x7e5c22faU, 0x48e5adccU,
+	0x10abbb94U, 0x41afeec5U, 0x55beebd1U, 0xa572d721U,
+	0x7999e0fdU, 0x1b617a9fU, 0xcac50f4eU, 0x18a1b99cU,
+	0x1de4f999U, 0x2492b6a0U, 0xebad466fU, 0x0cb0bc88U,
+	0xf0738374U, 0x4e602ecaU, 0xf6f60072U, 0xcb854e4fU,
+	0x289db5acU, 0xd05b8b54U, 0x567e28d2U, 0x5e742adaU,
+	0x49a5eccdU, 0xac389428U, 0x58f1a9dcU, 0x11ebfa95U,
+	0xeaed076eU, 0x45aaefc1U, 0x15eefb91U, 0x261234a2U,
+	0x940e9a10U, 0x077a7d83U, 0xb566d331U, 0x1f647b9bU,
+	0x9d44d919U, 0x3a0933beU, 0x122b3996U, 0x3083b3b4U,
+	0xaabd172eU, 0xd39b4857U, 0xa7f25523U, 0x235774a7U,
+	0xc905cc4dU, 0xf133c275U, 0x426f2dc6U, 0x8bd55e0fU,
+	0xe522c761U, 0x698de4edU, 0xa4329620U, 0x221735a6U,
+	0x928b1916U, 0x6187e6e5U, 0x374671b3U, 0x573e69d3U,
+	0x2b5d76afU, 0x2f5877abU, 0xf3b34077U, 0xdb914a5fU,
+	0xa83d952cU, 0xf2f30176U, 0x2097b7a4U, 0xd45e8a50U,
+	0x70d3a3f4U, 0xe92dc46dU, 0x6cc8a4e8U, 0x630764e7U,
+	0xdad10b5eU, 0xf939c07dU, 0xefa8476bU, 0x9bc15a1fU,
+	0xe2e70566U, 0xded40a5aU, 0xf7b64173U, 0xfd3cc179U,
+	0xb6a61032U, 0x9941d81dU, 0x5a712bdeU, 0xbc2c9038U,
+	0x08b5bd8cU, 0xa97dd42dU, 0x731360f7U, 0x68cda5ecU,
+	0x2a1d37aeU, 0x4f206fcbU, 0xdd14c959U, 0xd6de0852U,
+	0x7cdca0f8U, 0x09f5fc8dU, 0x4ce0acc8U, 0x5cf4a8d8U,
+	0xb829913cU, 0x3b4972bfU, 0xc04f8f44U, 0xb7e65133U,
+	0xaeb8162aU, 0xc44a8e40U, 0x432f6cc7U, 0x670265e3U,
+	0x0b757e8fU, 0x78d9a1fcU, 0x59b1e8ddU, 0xbaa9133eU,
+	0x8955dc0dU, 0x275275a3U, 0x855adf01U, 0x8c109c08U,
+	0xfefc027aU, 0x5db4e9d9U, 0x6b0d66efU, 0x2dd8f5a9U,
+	0x765620f2U, 0xed28c569U, 0xabfd562fU, 0x1e243a9aU,
+	0x7b1962ffU, 0x25d2f7a1U, 0xe3a74467U, 0x664224e2U,
+	0xc10fce45U, 0xb163d235U, 0x7a5923feU, 0xbfec533bU,
+	0x4588cd43U, 0x5cd68a5aU, 0xb94bf2bfU, 0xe500e5e3U,
+	0x2dfad72bU, 0x9963fa9fU, 0xeacf25ecU, 0xc06dadc6U,
+	0x46480e40U, 0xc468acc2U, 0x4e420c48U, 0x15ccd913U,
+	0xbe8e30b8U, 0xf111e0f7U, 0xae9a34a8U, 0x0a171d0cU,
+	0xb28133b4U, 0x83fd7e85U, 0x74f48072U, 0xc7a86fc1U,
+	0xb68432b0U, 0xe440a4e2U, 0x0e121c08U, 0x11c9d817U,
+	0xb404b0b2U, 0x0f525d09U, 0x52590b54U, 0x86b83e80U,
+	0x3b6b503dU, 0x04989c02U, 0x06181e00U, 0x78fb837eU,
+	0x6cea866aU, 0x174c5b11U, 0x5f164959U, 0xcd22efcbU,
+	0x58d38b5eU, 0xa155f4a7U, 0x7cfe827aU, 0xf79463f1U,
+	0xd2f92bd4U, 0x4f024d49U, 0x32211334U, 0x13495a15U,
+	0xa95ff6afU, 0x21f5d427U, 0xac1ab6aaU, 0x97ec7b91U,
+	0x8d72ff8bU, 0xd079a9d6U, 0x6e6a0468U, 0xb80bb3beU,
+	0x39ebd23fU, 0x3e2e1038U, 0xbd4ef3bbU, 0x48c78f4eU,
+	0x1c869a1aU, 0x61a5c467U, 0x4cc28e4aU, 0xf514e1f3U,
+	0xcba76ccdU, 0x20b59526U, 0xe2c527e4U, 0x8438bc82U,
+	0x035d5e05U, 0x021d1f04U, 0x803dbd86U, 0xed0ae7ebU,
+	0xd139e8d7U, 0xd3b96ad5U, 0x424d0f44U, 0xf454a0f2U,
+	0x10899916U, 0x7b3b407dU, 0xeeca24e8U, 0x7a7b017cU,
+	0x8578fd83U, 0x08979f0eU, 0x23755625U, 0xbc0eb2baU,
+	0xbfce71b9U, 0xb3c172b5U, 0x18839b1eU, 0xd47ca8d2U,
+	0xdbb368ddU, 0x8ff27d89U, 0x75b4c173U, 0x30a19136U,
+	0xfd1ee3fbU, 0xb141f0b7U, 0x22351724U, 0x33615235U,
+	0xdfb669d9U, 0x47084f41U, 0x68ef876eU, 0xcae72dccU,
+	0x66600660U, 0x8eb23c88U, 0x26301620U, 0x67204761U,
+	0x93e97a95U, 0x7f3e4179U, 0x5993ca5fU, 0x942cb892U,
+	0x8ab73d8cU, 0x96ac3a90U, 0xef8a65e9U, 0xf2d123f4U,
+	0x1a03191cU, 0x9aa3399cU, 0xff9e61f9U, 0x009d9d06U,
+	0x4d82cf4bU, 0x1e061818U, 0x07585f01U, 0x430d4e45U,
+	0x60e58566U, 0x5199c857U, 0x82bd3f84U, 0x571c4b51U,
+	0x2f7a5529U, 0xc6e82ec0U, 0x160c1a10U, 0x817dfc87U,
+	0x9c26ba9aU, 0x0dd2df0bU, 0x3deed33bU, 0x64e08462U,
+	0xb544f1b3U, 0xe045a5e6U, 0x34a49032U, 0x559cc953U,
+	0x1f465919U, 0x01dddc07U, 0xfede20f8U, 0xc867afceU,
+	0x9029b996U, 0xc12decc7U, 0xd53ce9d3U, 0x25f0d523U,
+	0xf91be2ffU, 0x9be3789dU, 0x4a470d4cU, 0x9823bb9eU,
+	0x9d66fb9bU, 0xa410b4a2U, 0x6b2f446dU, 0x8c32be8aU,
+	0x70f18176U, 0xcee22cc8U, 0x76740270U, 0x4b074c4dU,
+	0xa81fb7aeU, 0x50d98956U, 0xd6fc2ad0U, 0xdef628d8U,
+	0xc927eecfU, 0x2cba962aU, 0xd873abdeU, 0x9169f897U,
+	0x6a6f056cU, 0xc528edc3U, 0x956cf993U, 0xa69036a0U,
+	0x148c9812U, 0x87f87f81U, 0x35e4d133U, 0x9fe67999U,
+	0x1dc6db1bU, 0xba8b31bcU, 0x92a93b94U, 0xb001b1b6U,
+	0x2a3f152cU, 0x53194a55U, 0x27705721U, 0xa3d576a5U,
+	0x4987ce4fU, 0x71b1c077U, 0xc2ed2fc4U, 0x0b575c0dU,
+	0x65a0c563U, 0xe90fe6efU, 0x24b09422U, 0xa29537a4U,
+	0x12091b14U, 0xe105e4e7U, 0xb7c473b1U, 0xd7bc6bd1U,
+	0xabdf74adU, 0xafda75a9U, 0x73314275U, 0x5b13485dU,
+	0x28bf972eU, 0x72710374U, 0xa015b5a6U, 0x54dc8852U,
+	0xf051a1f6U, 0x69afc66fU, 0xec4aa6eaU, 0xe38566e5U,
+	0x5a53095cU, 0x79bbc27fU, 0x6f2a4569U, 0x1b43581dU,
+	0x62650764U, 0x5e560858U, 0x77344371U, 0x7dbec37bU,
+	0x36241230U, 0x19c3da1fU, 0xdaf329dcU, 0x3cae923aU,
+	0x8837bf8eU, 0x29ffd62fU, 0xf39162f5U, 0xe84fa7eeU,
+	0xaa9f35acU, 0xcfa26dc9U, 0x5d96cb5bU, 0x565c0a50U,
+	0xfc5ea2faU, 0x8977fe8fU, 0xcc62aecaU, 0xdc76aadaU,
+	0x38ab933eU, 0xbbcb70bdU, 0x40cd8d46U, 0x37645331U,
+	0x2e3a1428U, 0x44c88c42U, 0xc3ad6ec5U, 0xe78067e1U,
+	0x8bf77c8dU, 0xf85ba3feU, 0xd933eadfU, 0x3a2b113cU,
+	0x09d7de0fU, 0xa7d077a1U, 0x05d8dd03U, 0x0c929e0aU,
+	0x7e7e0078U, 0xdd36ebdbU, 0xeb8f64edU, 0xad5af7abU,
+	0xf6d422f0U, 0x6daac76bU, 0x2b7f542dU, 0x9ea63898U,
+	0xfb9b60fdU, 0xa550f5a3U, 0x63254665U, 0xe6c026e0U,
+	0x418dcc47U, 0x31e1d037U, 0xfadb21fcU, 0x3f6e5139U,
+	0xa615b33dU, 0xbf4bf424U, 0x5ad68cc1U, 0x069d9b9dU,
+	0xce67a955U, 0x7afe84e1U, 0x09525b92U, 0x23f0d3b8U,
+	0xa5d5703eU, 0x27f5d2bcU, 0xaddf7236U, 0xf651a76dU,
+	0x5d134ec6U, 0x128c9e89U, 0x4d074ad6U, 0xe98a6372U,
+	0x511c4dcaU, 0x606000fbU, 0x9769fe0cU, 0x243511bfU,
+	0x55194cceU, 0x07ddda9cU, 0xed8f6276U, 0xf254a669U,
+	0x5799ceccU, 0xeccf2377U, 0xb1c4752aU, 0x652540feU,
+	0xd8f62e43U, 0xe705e27cU, 0xe585607eU, 0x9b66fd00U,
+	0x8f77f814U, 0xf4d1256fU, 0xbc8b3727U, 0x2ebf91b5U,
+	0xbb4ef520U, 0x42c88ad9U, 0x9f63fc04U, 0x14091d8fU,
+	0x316455aaU, 0xac9f3337U, 0xd1bc6d4aU, 0xf0d4246bU,
+	0x4ac288d1U, 0xc268aa59U, 0x4f87c8d4U, 0x747105efU,
+	0x6eef81f5U, 0x33e4d7a8U, 0x8df77a16U, 0x5b96cdc0U,
+	0xda76ac41U, 0xddb36e46U, 0x5ed38dc5U, 0xab5af130U,
+	0xff1be464U, 0x8238ba19U, 0xaf5ff034U, 0x16899f8dU,
+	0x283a12b3U, 0xc328eb58U, 0x0158599aU, 0x67a5c2fcU,
+	0xe0c0207bU, 0xe180617aU, 0x63a0c3f8U, 0x0e979995U,
+	0x32a496a9U, 0x302414abU, 0xa1d0713aU, 0x17c9de8cU,
+	0xf314e768U, 0x98a63e03U, 0x0d575a96U, 0x99e67f02U,
+	0x66e583fdU, 0xeb0ae170U, 0xc0e8285bU, 0x5f93ccc4U,
+	0x5c530fc7U, 0x505c0ccbU, 0xfb1ee560U, 0x37e1d6acU,
+	0x382e16a3U, 0x6c6f03f7U, 0x9629bf0dU, 0xd33cef48U,
+	0x1e839d85U, 0x52dc8ec9U, 0xc1a8695aU, 0xd0fc2c4bU,
+	0x3c2b17a7U, 0xa495313fU, 0x8b72f910U, 0x297a53b2U,
+	0x85fd781eU, 0x6d2f42f6U, 0xc5ad685eU, 0x84bd391fU,
+	0x707404ebU, 0x9ca33f07U, 0xba0eb421U, 0x77b1c6ecU,
+	0x692a43f2U, 0x753144eeU, 0x0c171b97U, 0x114c5d8aU,
+	0xf99e6762U, 0x793e47e2U, 0x1c031f87U, 0xe300e378U,
+	0xae1fb135U, 0xfd9b6666U, 0xe4c5217fU, 0xa090303bU,
+	0x8378fb18U, 0xb204b629U, 0x612041faU, 0xb481352fU,
+	0xcce72b57U, 0x257550beU, 0xf591646eU, 0x62e082f9U,
+	0x7fbbc4e4U, 0xee4fa175U, 0xde73ad45U, 0x877dfa1cU,
+	0x56d98fcdU, 0x03d8db98U, 0xd739ee4cU, 0xb601b72dU,
+	0xfcdb2767U, 0xe240a279U, 0x1d435e86U, 0x2bfad1b0U,
+	0x73b4c7e8U, 0x22b092b9U, 0x36a197adU, 0xc66dab5dU,
+	0x1a869c81U, 0x787e06e3U, 0xa9da7332U, 0x7bbec5e0U,
+	0x7efb85e5U, 0x478dcadcU, 0x88b23a13U, 0x6fafc0f4U,
+	0x936cff08U, 0x2d7f52b6U, 0x95e97c0eU, 0xa89a3233U,
+	0x4b82c9d0U, 0xb344f728U, 0x356154aeU, 0x3d6b56a6U,
+	0x2aba90b1U, 0xcf27e854U, 0x3beed5a0U, 0x72f486e9U,
+	0x89f27b12U, 0x26b593bdU, 0x76f187edU, 0x450d48deU,
+	0xf711e66cU, 0x646501ffU, 0xd679af4dU, 0x7c7b07e7U,
+	0xfe5ba565U, 0x59164fc2U, 0x713445eaU, 0x539ccfc8U,
+	0xc9a26b52U, 0xb084342bU, 0xc4ed295fU, 0x404808dbU,
+	0xaa1ab031U, 0x922cbe09U, 0x217051baU, 0xe8ca2273U,
+	0x863dbb1dU, 0x0a929891U, 0xc72dea5cU, 0x410849daU,
+	0xf194656aU, 0x02989a99U, 0x54590dcfU, 0x342115afU,
+	0x48420ad3U, 0x4c470bd7U, 0x90ac3c0bU, 0xb88e3623U,
+	0xcb22e950U, 0x91ec7d0aU, 0x4388cbd8U, 0xb741f62cU,
+	0x13ccdf88U, 0x8a32b811U, 0x0fd7d894U, 0x0018189bU,
+	0xb9ce7722U, 0x9a26bc01U, 0x8cb73b17U, 0xf8de2663U,
+	0x81f8791aU, 0xbdcb7626U, 0x94a93d0fU, 0x9e23bd05U,
+	0xd5b96c4eU, 0xfa5ea461U, 0x396e57a2U, 0xdf33ec44U,
+	0x6baac1f0U, 0xca62a851U, 0x100c1c8bU, 0x0bd2d990U,
+	0x49024bd2U, 0x2c3f13b7U, 0xbe0bb525U, 0xb5c1742eU,
+	0x1fc3dc84U, 0x6aea80f1U, 0x2fffd0b4U, 0x3febd4a4U,
+	0xdb36ed40U, 0x58560ec3U, 0xa350f338U, 0xd4f92d4fU,
+	0xcda76a56U, 0xa755f23cU, 0x203010bbU, 0x041d199fU,
+	0x686a02f3U, 0x1bc6dd80U, 0x3aae94a1U, 0xd9b66f42U,
+	0xea4aa071U, 0x444d09dfU, 0xe645a37dU, 0xef0fe074U,
+	0x9de37e06U, 0x3eab95a5U, 0x08121a93U, 0x4ec789d5U,
+	0x15495c8eU, 0x8e37b915U, 0xc8e22a53U, 0x7d3b46e6U,
+	0x18061e83U, 0x46cd8bddU, 0x80b8381bU, 0x055d589eU,
+	0xa210b239U, 0xd27cae49U, 0x19465f82U, 0xdcf32f47U,
+	0xb710a729U, 0xae4ee030U, 0x4bd398d5U, 0x17988f89U,
+	0xdf62bd41U, 0x6bfb90f5U, 0x18574f86U, 0x32f5c7acU,
+	0xb4d0642aU, 0x36f0c6a8U, 0xbcda6622U, 0xe754b379U,
+	0x4c165ad2U, 0x03898a9dU, 0x5c025ec2U, 0xf88f7766U,
+	0x401959deU, 0x716514efU, 0x866cea18U, 0x353005abU,
+	0x441c58daU, 0x16d8ce88U, 0xfc8a7662U, 0xe351b27dU,
+	0x469cdad8U, 0xfdca3763U, 0xa0c1613eU, 0x742054eaU,
+	0xc9f33a57U, 0xf600f668U, 0xf480746aU, 0x8a63e914U,
+	0x9e72ec00U, 0xe5d4317bU, 0xad8e2333U, 0x3fba85a1U,
+	0xaa4be134U, 0x53cd9ecdU, 0x8e66e810U, 0x050c099bU,
+	0x206141beU, 0xbd9a2723U, 0xc0b9795eU, 0xe1d1307fU,
+	0x5bc79cc5U, 0xd36dbe4dU, 0x5e82dcc0U, 0x657411fbU,
+	0x7fea95e1U, 0x22e1c3bcU, 0x9cf26e02U, 0x4a93d9d4U,
+	0xcb73b855U, 0xccb67a52U, 0x4fd699d1U, 0xba5fe524U,
+	0xee1ef070U, 0x933dae0dU, 0xbe5ae420U, 0x078c8b99U,
+	0x393f06a7U, 0xd22dff4cU, 0x105d4d8eU, 0x76a0d6e8U,
+	0xf1c5346fU, 0xf085756eU, 0x72a5d7ecU, 0x1f928d81U,
+	0x23a182bdU, 0x212100bfU, 0xb0d5652eU, 0x06ccca98U,
+	0xe211f37cU, 0x89a32a17U, 0x1c524e82U, 0x88e36b16U,
+	0x77e097e9U, 0xfa0ff564U, 0xd1ed3c4fU, 0x4e96d8d0U,
+	0x4d561bd3U, 0x415918dfU, 0xea1bf174U, 0x26e4c2b8U,
+	0x292b02b7U, 0x7d6a17e3U, 0x872cab19U, 0xc239fb5cU,
+	0x0f868991U, 0x43d99addU, 0xd0ad7d4eU, 0xc1f9385fU,
+	0x2d2e03b3U, 0xb590252bU, 0x9a77ed04U, 0x387f47a6U,
+	0x94f86c0aU, 0x7c2a56e2U, 0xd4a87c4aU, 0x95b82d0bU,
+	0x617110ffU, 0x8da62b13U, 0xab0ba035U, 0x66b4d2f8U,
+	0x782f57e6U, 0x643450faU, 0x1d120f83U, 0x0049499eU,
+	0xe89b7376U, 0x683b53f6U, 0x0d060b93U, 0xf205f76cU,
+	0xbf1aa521U, 0xec9e7272U, 0xf5c0356bU, 0xb195242fU,
+	0x927def0cU, 0xa301a23dU, 0x702555eeU, 0xa584213bU,
+	0xdde23f43U, 0x347044aaU, 0xe494707aU, 0x73e596edU,
+	0x6ebed0f0U, 0xff4ab561U, 0xcf76b951U, 0x9678ee08U,
+	0x47dc9bd9U, 0x12ddcf8cU, 0xc63cfa58U, 0xa704a339U,
+	0xedde3373U, 0xf345b66dU, 0x0c464a92U, 0x3affc5a4U,
+	0x62b1d3fcU, 0x33b586adU, 0x27a483b9U, 0xd768bf49U,
+	0x0b838895U, 0x697b12f7U, 0xb8df6726U, 0x6abbd1f4U,
+	0x6ffe91f1U, 0x5688dec8U, 0x99b72e07U, 0x7eaad4e0U,
+	0x8269eb1cU, 0x3c7a46a2U, 0x84ec681aU, 0xb99f2627U,
+	0x5a87ddc4U, 0xa241e33cU, 0x246440baU, 0x2c6e42b2U,
+	0x3bbf84a5U, 0xde22fc40U, 0x2aebc1b4U, 0x63f192fdU,
+	0x98f76f06U, 0x37b087a9U, 0x67f493f9U, 0x54085ccaU,
+	0xe614f278U, 0x756015ebU, 0xc77cbb59U, 0x6d7e13f3U,
+	0xef5eb171U, 0x48135bd6U, 0x603151feU, 0x4299dbdcU,
+	0xd8a77f46U, 0xa181203fU, 0xd5e83d4bU, 0x514d1ccfU,
+	0xbb1fa425U, 0x8329aa1dU, 0x307545aeU, 0xf9cf3667U,
+	0x9738af09U, 0x1b978c85U, 0xd628fe48U, 0x500d5dceU,
+	0xe091717eU, 0x139d8e8dU, 0x455c19dbU, 0x252401bbU,
+	0x59471ec7U, 0x5d421fc3U, 0x81a9281fU, 0xa98b2237U,
+	0xda27fd44U, 0x80e9691eU, 0x528ddfccU, 0xa644e238U,
+	0x02c9cb9cU, 0x9b37ac05U, 0x1ed2cc80U, 0x111d0c8fU,
+	0xa8cb6336U, 0x8b23a815U, 0x9db22f03U, 0xe9db3277U,
+	0x90fd6d0eU, 0xacce6232U, 0x85ac291bU, 0x8f26a911U,
+	0xc4bc785aU, 0xeb5bb075U, 0x286b43b6U, 0xce36f850U,
+	0x7aafd5e4U, 0xdb67bc45U, 0x0109089fU, 0x1ad7cd84U,
+	0x58075fc6U, 0x3d3a07a3U, 0xaf0ea131U, 0xa4c4603aU,
+	0x0ec6c890U, 0x7bef94e5U, 0x3efac4a0U, 0x2eeec0b0U,
+	0xca33f954U, 0x49531ad7U, 0xb255e72cU, 0xc5fc395bU,
+	0xdca27e42U, 0xb650e628U, 0x313504afU, 0x15180d8bU,
+	0x796f16e7U, 0x0ac3c994U, 0x2bab80b5U, 0xc8b37b56U,
+	0xfb4fb465U, 0x55481dcbU, 0xf740b769U, 0xfe0af460U,
+	0x8ce66a12U, 0x2fae81b1U, 0x19170e87U, 0x5fc29dc1U,
+	0x044c489aU, 0x9f32ad01U, 0xd9e73e47U, 0x6c3e52f2U,
+	0x09030a97U, 0x57c89fc9U, 0x91bd2c0fU, 0x14584c8aU,
+	0xb315a62dU, 0xc379ba5dU, 0x08434b96U, 0xcdf63b53U,
+	0x11cddc52U, 0x08939b4bU, 0xed0ee3aeU, 0xb145f4f2U,
+	0x79bfc63aU, 0xcd26eb8eU, 0xbe8a34fdU, 0x9428bcd7U,
+	0x120d1f51U, 0x902dbdd3U, 0x1a071d59U, 0x4189c802U,
+	0xeacb21a9U, 0xa554f1e6U, 0xfadf25b9U, 0x5e520c1dU,
+	0xe6c422a5U, 0xd7b86f94U, 0x20b19163U, 0x93ed7ed0U,
+	0xe2c123a1U, 0xb005b5f3U, 0x5a570d19U, 0x458cc906U,
+	0xe041a1a3U, 0x5b174c18U, 0x061c1a45U, 0xd2fd2f91U,
+	0x6f2e412cU, 0x50dd8d13U, 0x525d0f11U, 0x2cbe926fU,
+	0x38af977bU, 0x43094a00U, 0x0b535848U, 0x9967fedaU,
+	0x0c969a4fU, 0xf510e5b6U, 0x28bb936bU, 0xa3d172e0U,
+	0x86bc3ac5U, 0x1b475c58U, 0x66640225U, 0x470c4b04U,
+	0xfd1ae7beU, 0x75b0c536U, 0xf85fa7bbU, 0xc3a96a80U,
+	0xd937ee9aU, 0x843cb8c7U, 0x3a2f1579U, 0xec4ea2afU,
+	0x6daec32eU, 0x6a6b0129U, 0xe90be2aaU, 0x1c829e5fU,
+	0x48c38b0bU, 0x35e0d576U, 0x18879f5bU, 0xa151f0e2U,
+	0x9fe27ddcU, 0x74f08437U, 0xb68036f5U, 0xd07dad93U,
+	0x57184f14U, 0x56580e15U, 0xd478ac97U, 0xb94ff6faU,
+	0x857cf9c6U, 0x87fc7bc4U, 0x16081e55U, 0xa011b1e3U,
+	0x44cc8807U, 0x2f7e516cU, 0xba8f35f9U, 0x2e3e106dU,
+	0xd13dec92U, 0x5cd28e1fU, 0x77304734U, 0xe84ba3abU,
+	0xeb8b60a8U, 0xe78463a4U, 0x4cc68a0fU, 0x8039b9c3U,
+	0x8ff679ccU, 0xdbb76c98U, 0x21f1d062U, 0x64e48027U,
+	0xa95bf2eaU, 0xe504e1a6U, 0x76700635U, 0x67244324U,
+	0x8bf378c8U, 0x134d5e50U, 0x3caa967fU, 0x9ea23cddU,
+	0x32251771U, 0xdaf72d99U, 0x72750731U, 0x33655670U,
+	0xc7ac6b84U, 0x2b7b5068U, 0x0dd6db4eU, 0xc069a983U,
+	0xdef22c9dU, 0xc2e92b81U, 0xbbcf74f8U, 0xa69432e5U,
+	0x4e46080dU, 0xcee6288dU, 0xabdb70e8U, 0x54d88c17U,
+	0x19c7de5aU, 0x4a430909U, 0x531d4e10U, 0x17485f54U,
+	0x34a09477U, 0x05dcd946U, 0xd6f82e95U, 0x03595a40U,
+	0x7b3f4438U, 0x92ad3fd1U, 0x42490b01U, 0xd538ed96U,
+	0xc863ab8bU, 0x5997ce1aU, 0x69abc22aU, 0x30a59573U,
+	0xe101e0a2U, 0xb400b4f7U, 0x60e18123U, 0x01d9d842U,
+	0x4b034808U, 0x5598cd16U, 0xaa9b31e9U, 0x9c22bedfU,
+	0xc46ca887U, 0x9568fdd6U, 0x8179f8c2U, 0x71b5c432U,
+	0xad5ef3eeU, 0xcfa6698cU, 0x1e021c5dU, 0xcc66aa8fU,
+	0xc923ea8aU, 0xf055a5b3U, 0x3f6a557cU, 0xd877af9bU,
+	0x24b49067U, 0x9aa73dd9U, 0x22311361U, 0x1f425d5cU,
+	0xfc5aa6bfU, 0x049c9847U, 0x82b93bc1U, 0x8ab339c9U,
+	0x9d62ffdeU, 0x78ff873bU, 0x8c36bacfU, 0xc52ce986U,
+	0x3e2a147dU, 0x916dfcd2U, 0xc129e882U, 0xf2d527b1U,
+	0x40c98903U, 0xd3bd6e90U, 0x61a1c022U, 0xcba36888U,
+	0x4983ca0aU, 0xeece20adU, 0xc6ec2a85U, 0xe444a0a7U,
+	0x7e7a043dU, 0x075c5b44U, 0x73354630U, 0xf79067b4U,
+	0x1dc2df5eU, 0x25f4d166U, 0x96a83ed5U, 0x5f124d1cU,
+	0x31e5d472U, 0xbd4af7feU, 0x70f58533U, 0xf6d026b5U,
+	0x464c0a05U, 0xb540f5f6U, 0xe38162a0U, 0x83f97ac0U,
+	0xff9a65bcU, 0xfb9f64b8U, 0x27745364U, 0x0f56594cU,
+	0x7cfa863fU, 0x26341265U, 0xf450a4b7U, 0x00999943U,
+	0xa414b0e7U, 0x3dead77eU, 0xb80fb7fbU, 0xb7c077f4U,
+	0x0e16184dU, 0x2dfed36eU, 0x3b6f5478U, 0x4f06490cU,
+	0x36201675U, 0x0a131949U, 0x23715260U, 0x29fbd26aU,
+	0x62610321U, 0x4d86cb0eU, 0x8eb638cdU, 0x68eb832bU,
+	0xdc72ae9fU, 0x7dbac73eU, 0xa7d473e4U, 0xbc0ab6ffU,
+	0xfeda24bdU, 0x9be77cd8U, 0x09d3da4aU, 0x02191b41U,
+	0xa81bb3ebU, 0xdd32ef9eU, 0x9827bfdbU, 0x8833bbcbU,
+	0x6cee822fU, 0xef8e61acU, 0x14889c57U, 0x63214220U,
+	0x7a7f0539U, 0x108d9d53U, 0x97e87fd4U, 0xb3c576f0U,
+	0xdfb26d9cU, 0xac1eb2efU, 0x8d76fbceU, 0x6e6e002dU,
+	0x5d92cf1eU, 0xf39566b0U, 0x519dcc12U, 0x58d78f1bU,
+	0x2a3b1169U, 0x8973facaU, 0xbfca75fcU, 0xf91fe6baU,
+	0xa29133e1U, 0x39efd67aU, 0x7f3a453cU, 0xcae32989U,
+	0xafde71ecU, 0xf115e4b2U, 0x37605774U, 0xb28537f1U,
+	0x15c8dd56U, 0x65a4c126U, 0xae9e30edU, 0x6b2b4028U,
+	0x4bdf941aU, 0x5281d303U, 0xb71cabe6U, 0xeb57bcbaU,
+	0x23ad8e72U, 0x9734a3c6U, 0xe4987cb5U, 0xce3af49fU,
+	0x481f5719U, 0xca3ff59bU, 0x40155511U, 0x1b9b804aU,
+	0xb0d969e1U, 0xff46b9aeU, 0xa0cd6df1U, 0x04404455U,
+	0xbcd66aedU, 0x8daa27dcU, 0x7aa3d92bU, 0xc9ff3698U,
+	0xb8d36be9U, 0xea17fdbbU, 0x00454551U, 0x1f9e814eU,
+	0xba53e9ebU, 0x01050450U, 0x5c0e520dU, 0x88ef67d9U,
+	0x353c0964U, 0x0acfc55bU, 0x084f4759U, 0x76acda27U,
+	0x62bddf33U, 0x191b0248U, 0x51411000U, 0xc375b692U,
+	0x5684d207U, 0xaf02adfeU, 0x72a9db23U, 0xf9c33aa8U,
+	0xdcae728dU, 0x41551410U, 0x3c764a6dU, 0x1d1e034cU,
+	0xa708aff6U, 0x2fa28d7eU, 0xa24deff3U, 0x99bb22c8U,
+	0x8325a6d2U, 0xde2ef08fU, 0x603d5d31U, 0xb65ceae7U,
+	0x37bc8b66U, 0x30794961U, 0xb319aae2U, 0x4690d617U,
+	0x12d1c343U, 0x6ff29d3eU, 0x4295d713U, 0xfb43b8aaU,
+	0xc5f03594U, 0x2ee2cc7fU, 0xec927ebdU, 0x8a6fe5dbU,
+	0x0d0a075cU, 0x0c4a465dU, 0x8e6ae4dfU, 0xe35dbeb2U,
+	0xdf6eb18eU, 0xddee338cU, 0x4c1a561dU, 0xfa03f9abU,
+	0x1edec04fU, 0x756c1924U, 0xe09d7db1U, 0x742c5825U,
+	0x8b2fa4daU, 0x06c0c657U, 0x2d220f7cU, 0xb259ebe3U,
+	0xb19928e0U, 0xbd962becU, 0x16d4c247U, 0xda2bf18bU,
+	0xd5e43184U, 0x81a524d0U, 0x7be3982aU, 0x3ef6c86fU,
+	0xf349baa2U, 0xbf16a9eeU, 0x2c624e7dU, 0x3d360b6cU,
+	0xd1e13080U, 0x495f1618U, 0x66b8de37U, 0xc4b07495U,
+	0x68375f39U, 0x80e565d1U, 0x28674f79U, 0x69771e38U,
+	0x9dbe23ccU, 0x71691820U, 0x57c49306U, 0x9a7be1cbU,
+	0x84e064d5U, 0x98fb63c9U, 0xe1dd3cb0U, 0xfc867aadU,
+	0x14544045U, 0x94f460c5U, 0xf1c938a0U, 0x0ecac45fU,
+	0x43d59612U, 0x10514141U, 0x090f0658U, 0x4d5a171cU,
+	0x6eb2dc3fU, 0x5fce910eU, 0x8cea66ddU, 0x594b1208U,
+	0x212d0c70U, 0xc8bf7799U, 0x185b4349U, 0x8f2aa5deU,
+	0x9271e3c3U, 0x03858652U, 0x33b98a62U, 0x6ab7dd3bU,
+	0xbb13a8eaU, 0xee12fcbfU, 0x3af3c96bU, 0x5bcb900aU,
+	0x11110040U, 0x0f8a855eU, 0xf08979a1U, 0xc630f697U,
+	0x9e7ee0cfU, 0xcf7ab59eU, 0xdb6bb08aU, 0x2ba78c7aU,
+	0xf74cbba6U, 0x95b421c4U, 0x44105415U, 0x9674e2c7U,
+	0x9331a2c2U, 0xaa47edfbU, 0x65781d34U, 0x8265e7d3U,
+	0x7ea6d82fU, 0xc0b57591U, 0x78235b29U, 0x45501514U,
+	0xa648eef7U, 0x5e8ed00fU, 0xd8ab7389U, 0xd0a17181U,
+	0xc770b796U, 0x22edcf73U, 0xd624f287U, 0x9f3ea1ceU,
+	0x64385c35U, 0xcb7fb49aU, 0x9b3ba0caU, 0xa8c76ff9U,
+	0x1adbc14bU, 0x89af26d8U, 0x3bb3886aU, 0x91b120c0U,
+	0x13918242U, 0xb4dc68e5U, 0x9cfe62cdU, 0xbe56e8efU,
+	0x24684c75U, 0x5d4e130cU, 0x29270e78U, 0xad822ffcU,
+	0x47d09716U, 0x7fe6992eU, 0xccba769dU, 0x05000554U,
+	0x6bf79c3aU, 0xe758bfb6U, 0x2ae7cd7bU, 0xacc26efdU,
+	0x1c5e424dU, 0xef52bdbeU, 0xb9932ae8U, 0xd9eb3288U,
+	0xa5882df4U, 0xa18d2cf0U, 0x7d661b2cU, 0x55441104U,
+	0x26e8ce77U, 0x7c265a2dU, 0xae42ecffU, 0x5a8bd10bU,
+	0xfe06f8afU, 0x67f89f36U, 0xe21dffb3U, 0xedd23fbcU,
+	0x54045005U, 0x77ec9b26U, 0x617d1c30U, 0x15140144U,
+	0x6c325e3dU, 0x50015101U, 0x79631a28U, 0x73e99a22U,
+	0x38734b69U, 0x17948346U, 0xd4a47085U, 0x32f9cb63U,
+	0x8660e6d7U, 0x27a88f76U, 0xfdc63bacU, 0xe618feb7U,
+	0xa4c86cf5U, 0xc1f53490U, 0x53c19202U, 0x580b5309U,
+	0xf209fba3U, 0x8720a7d6U, 0xc235f793U, 0xd221f383U,
+	0x36fcca67U, 0xb59c29e4U, 0x4e9ad41fU, 0x39330a68U,
+	0x206d4d71U, 0x4a9fd51bU, 0xcdfa379cU, 0xe9d73eb8U,
+	0x85a025d4U, 0xf60cfaa7U, 0xd764b386U, 0x347c4865U,
+	0x07808756U, 0xa9872ef8U, 0x0b8f845aU, 0x02c5c753U,
+	0x70295921U, 0xd361b282U, 0xe5d83db4U, 0xa30daef2U,
+	0xf8837ba9U, 0x63fd9e32U, 0x25280d74U, 0x90f161c1U,
+	0xf5cc39a4U, 0xab07acfaU, 0x6d721f3cU, 0xe8977fb9U,
+	0x4fda951eU, 0x3fb6896eU, 0xf48c78a5U, 0x31390860U,
+	0x7f790688U, 0x66274191U, 0x83ba3974U, 0xdff12e28U,
+	0x170b1ce0U, 0xa3923154U, 0xd03eee27U, 0xfa9c660dU,
+	0x7cb9c58bU, 0xfe996709U, 0x74b3c783U, 0x2f3d12d8U,
+	0x847ffb73U, 0xcbe02b3cU, 0x946bff63U, 0x30e6d6c7U,
+	0x8870f87fU, 0xb90cb54eU, 0x4e054bb9U, 0xfd59a40aU,
+	0x8c75f97bU, 0xdeb16f29U, 0x34e3d7c3U, 0x2b3813dcU,
+	0x8ef57b79U, 0x35a396c2U, 0x68a8c09fU, 0xbc49f54bU,
+	0x019a9bf6U, 0x3e6957c9U, 0x3ce9d5cbU, 0x420a48b5U,
+	0x561b4da1U, 0x2dbd90daU, 0x65e78292U, 0xf7d32400U,
+	0x62224095U, 0x9ba43f6cU, 0x460f49b1U, 0xcd65a83aU,
+	0xe808e01fU, 0x75f38682U, 0x08d0d8ffU, 0x29b891deU,
+	0x93ae3d64U, 0x1b041fecU, 0x96eb7d61U, 0xad1db05aU,
+	0xb7833440U, 0xea88621dU, 0x549bcfa3U, 0x82fa7875U,
+	0x031a19f4U, 0x04dfdbf3U, 0x87bf3870U, 0x72364485U,
+	0x267751d1U, 0x5b540facU, 0x76334581U, 0xcfe52a38U,
+	0xf156a706U, 0x1a445eedU, 0xd834ec2fU, 0xbec97749U,
+	0x39ac95ceU, 0x38ecd4cfU, 0xbacc764dU, 0xd7fb2c20U,
+	0xebc8231cU, 0xe948a11eU, 0x78bcc48fU, 0xcea56b39U,
+	0x2a7852ddU, 0x41ca8bb6U, 0xd43bef23U, 0x408acab7U,
+	0xbf893648U, 0x326654c5U, 0x19849deeU, 0x86ff7971U,
+	0x853fba72U, 0x8930b97eU, 0x227250d5U, 0xee8d6319U,
+	0xe142a316U, 0xb503b642U, 0x4f450ab8U, 0x0a505afdU,
+	0xc7ef2830U, 0x8bb03b7cU, 0x18c4dcefU, 0x099099feU,
+	0xe547a212U, 0x7df9848aU, 0x521e4ca5U, 0xf016e607U,
+	0x5c91cdabU, 0xb443f743U, 0x1cc1ddebU, 0x5dd18caaU,
+	0xa918b15eU, 0x45cf8ab2U, 0x63620194U, 0xaedd7359U,
+	0xb046f647U, 0xac5df15bU, 0xd57bae22U, 0xc820e83fU,
+	0x20f2d2d7U, 0xa052f257U, 0xc56faa32U, 0x3a6c56cdU,
+	0x77730480U, 0x24f7d3d3U, 0x3da994caU, 0x79fc858eU,
+	0x5a144eadU, 0x6b68039cU, 0xb84cf44fU, 0x6ded809aU,
+	0x158b9ee2U, 0xfc19e50bU, 0x2cfdd1dbU, 0xbb8c374cU,
+	0xa6d77151U, 0x372314c0U, 0x071f18f0U, 0x5e114fa9U,
+	0x8fb53a78U, 0xdab46e2dU, 0x0e555bf9U, 0x6f6d0298U,
+	0x25b792d2U, 0x3b2c17ccU, 0xc42feb33U, 0xf2966405U,
+	0xaad8725dU, 0xfbdc270cU, 0xefcd2218U, 0x1f011ee8U,
+	0xc3ea2934U, 0xa112b356U, 0x70b6c687U, 0xa2d27055U,
+	0xa7973050U, 0x9ee17f69U, 0x51de8fa6U, 0xb6c37541U,
+	0x4a004abdU, 0xf413e703U, 0x4c85c9bbU, 0x71f68786U,
+	0x92ee7c65U, 0x6a28429dU, 0xec0de11bU, 0xe407e313U,
+	0xf3d62504U, 0x164b5de1U, 0xe2826015U, 0xab98335cU,
+	0x509ecea7U, 0xffd92608U, 0xaf9d3258U, 0x9c61fd6bU,
+	0x2e7d53d9U, 0xbd09b44aU, 0x0f151af8U, 0xa517b252U,
+	0x273710d0U, 0x807afa77U, 0xa858f05fU, 0x8af07a7dU,
+	0x10cedee7U, 0x69e8819eU, 0x1d819ceaU, 0x9924bd6eU,
+	0x73760584U, 0x4b400bbcU, 0xf81ce40fU, 0x31a697c6U,
+	0x5f510ea8U, 0xd3fe2d24U, 0x1e415fe9U, 0x9864fc6fU,
+	0x28f8d0dfU, 0xdbf42f2cU, 0x8d35b87aU, 0xed4da01aU,
+	0x912ebf66U, 0x952bbe62U, 0x49c089beU, 0x61e28396U,
+	0x124e5ce5U, 0x4880c8bfU, 0x9ae47e6dU, 0x6e2d4399U,
+	0xcaa06a3dU, 0x535e0da4U, 0xd6bb6d21U, 0xd974ad2eU,
+	0x60a2c297U, 0x434a09b4U, 0x55db8ea2U, 0x21b293d6U,
+	0x5894ccafU, 0x64a7c393U, 0x4dc588baU, 0x474f08b0U,
+	0x0cd5d9fbU, 0x233211d4U, 0xe002e217U, 0x065f59f1U,
+	0xb2c67445U, 0x130e1de4U, 0xc960a93eU, 0xd2be6c25U,
+	0x906efe67U, 0xf553a602U, 0x67670090U, 0x6cadc19bU,
+	0xc6af6931U, 0xb3863544U, 0xf6936501U, 0xe6876111U,
+	0x025a58f5U, 0x813abb76U, 0x7a3c468dU, 0x0d9598faU,
+	0x14cbdfe3U, 0x7e394789U, 0xf95ca50eU, 0xdd71ac2aU,
+	0xb106b746U, 0xc2aa6835U, 0xe3c22114U, 0x00dadaf7U,
+	0x332615c4U, 0x9d21bc6aU, 0x3f2916c8U, 0x366355c1U,
+	0x448fcbb3U, 0xe7c72010U, 0xd17eaf26U, 0x97ab3c60U,
+	0xcc25e93bU, 0x575b0ca0U, 0x118e9fe6U, 0xa457f353U,
+	0xc16aab36U, 0x9fa13e68U, 0x59d48daeU, 0xdc31ed2bU,
+	0x7b7c078cU, 0x0b101bfcU, 0xc02aea37U, 0x059f9af2U,
+	0x8e1d931dU, 0x9743d404U, 0x72deace1U, 0x2e95bbbdU,
+	0xe66f8975U, 0x52f6a4c1U, 0x215a7bb2U, 0x0bf8f398U,
+	0x8ddd501eU, 0x0ffdf29cU, 0x85d75216U, 0xde59874dU,
+	0x751b6ee6U, 0x3a84bea9U, 0x650f6af6U, 0xc1824352U,
+	0x79146deaU, 0x486820dbU, 0xbf61de2cU, 0x0c3d319fU,
+	0x7d116ceeU, 0x2fd5fabcU, 0xc5874256U, 0xda5c8649U,
+	0x7f91eeecU, 0xc4c70357U, 0x99cc550aU, 0x4d2d60deU,
+	0xf0fe0e63U, 0xcf0dc25cU, 0xcd8d405eU, 0xb36edd20U,
+	0xa77fd834U, 0xdcd9054fU, 0x94831707U, 0x06b7b195U,
+	0x9346d500U, 0x6ac0aaf9U, 0xb76bdc24U, 0x3c013dafU,
+	0x196c758aU, 0x84971317U, 0xf9b44d6aU, 0xd8dc044bU,
+	0x62caa8f1U, 0xea608a79U, 0x678fe8f4U, 0x5c7925cfU,
+	0x46e7a1d5U, 0x1becf788U, 0xa5ff5a36U, 0x739eede0U,
+	0xf27e8c61U, 0xf5bb4e66U, 0x76dbade5U, 0x8352d110U,
+	0xd713c444U, 0xaa309a39U, 0x8757d014U, 0x3e81bfadU,
+	0x00323293U, 0xeb20cb78U, 0x295079baU, 0x4fade2dcU,
+	0xc8c8005bU, 0xc988415aU, 0x4ba8e3d8U, 0x269fb9b5U,
+	0x1aacb689U, 0x182c348bU, 0x89d8511aU, 0x3fc1feacU,
+	0xdb1cc748U, 0xb0ae1e23U, 0x255f7ab6U, 0xb1ee5f22U,
+	0x4eeda3ddU, 0xc302c150U, 0xe8e0087bU, 0x779bece4U,
+	0x745b2fe7U, 0x78542cebU, 0xd316c540U, 0x1fe9f68cU,
+	0x10263683U, 0x446723d7U, 0xbe219f2dU, 0xfb34cf68U,
+	0x368bbda5U, 0x7ad4aee9U, 0xe9a0497aU, 0xf8f40c6bU,
+	0x14233787U, 0x8c9d111fU, 0xa37ad930U, 0x01727392U,
+	0xadf5583eU, 0x452762d6U, 0xeda5487eU, 0xacb5193fU,
+	0x587c24cbU, 0xb4ab1f27U, 0x92069401U, 0x5fb9e6ccU,
+	0x412263d2U, 0x5d3964ceU, 0x241f3bb7U, 0x39447daaU,
+	0xd1964742U, 0x513667c2U, 0x340b3fa7U, 0xcb08c358U,
+	0x86179115U, 0xd5934646U, 0xcccd015fU, 0x8898101bU,
+	0xab70db38U, 0x9a0c9609U, 0x492861daU, 0x9c89150fU,
+	0xe4ef0b77U, 0x0d7d709eU, 0xdd99444eU, 0x4ae8a2d9U,
+	0x57b3e4c4U, 0xc6478155U, 0xf67b8d65U, 0xaf75da3cU,
+	0x7ed1afedU, 0x2bd0fbb8U, 0xff31ce6cU, 0x9e09970dU,
+	0xd4d30747U, 0xca488259U, 0x354b7ea6U, 0x03f2f190U,
+	0x5bbce7c8U, 0x0ab8b299U, 0x1ea9b78dU, 0xee658b7dU,
+	0x328ebca1U, 0x507626c3U, 0x81d25312U, 0x53b6e5c0U,
+	0x56f3a5c5U, 0x6f85eafcU, 0xa0ba1a33U, 0x47a7e0d4U,
+	0xbb64df28U, 0x05777296U, 0xbde15c2eU, 0x80921213U,
+	0x638ae9f0U, 0x9b4cd708U, 0x1d69748eU, 0x15637686U,
+	0x02b2b091U, 0xe72fc874U, 0x13e6f580U, 0x5afca6c9U,
+	0xa1fa5b32U, 0x0ebdb39dU, 0x5ef9a7cdU, 0x6d0568feU,
+	0xdf19c64cU, 0x4c6d21dfU, 0xfe718f6dU, 0x547327c7U,
+	0xd6538545U, 0x711e6fe2U, 0x593c65caU, 0x7b94efe8U,
+	0xe1aa4b72U, 0x988c140bU, 0xece5097fU, 0x684028fbU,
+	0x82129011U, 0xba249e29U, 0x0978719aU, 0xc0c20253U,
+	0xae359b3dU, 0x229ab8b1U, 0xef25ca7cU, 0x690069faU,
+	0xd99c454aU, 0x2a90bab9U, 0x7c512defU, 0x1c29358fU,
+	0x604a2af3U, 0x644f2bf7U, 0xb8a41c2bU, 0x90861603U,
+	0xe32ac970U, 0xb9e45d2aU, 0x6b80ebf8U, 0x9f49d60cU,
+	0x3bc4ffa8U, 0xa23a9831U, 0x27dff8b4U, 0x281038bbU,
+	0x91c65702U, 0xb22e9c21U, 0xa4bf1b37U, 0xd0d60643U,
+	0xa9f0593aU, 0x95c35606U, 0xbca11d2fU, 0xb62b9d25U,
+	0xfdb14c6eU, 0xd2568441U, 0x11667782U, 0xf73bcc64U,
+	0x43a2e1d0U, 0xe26a8871U, 0x38043cabU, 0x23daf9b0U,
+	0x610a6bf2U, 0x04373397U, 0x96039505U, 0x9dc9540eU,
+	0x37cbfca4U, 0x42e2a0d1U, 0x07f7f094U, 0x17e3f484U,
+	0xf33ecd60U, 0x705e2ee3U, 0x8b58d318U, 0xfcf10d6fU,
+	0xe5af4a76U, 0x8f5dd21cU, 0x0838309bU, 0x2c1539bfU,
+	0x406222d3U, 0x33cefda0U, 0x12a6b481U, 0xf1be4f62U,
+	0xc2428051U, 0x6c4529ffU, 0xce4d835dU, 0xc707c054U,
+	0xb5eb5e26U, 0x16a3b585U, 0x201a3ab3U, 0x66cfa9f5U,
+	0x3d417caeU, 0xa63f9935U, 0xe0ea0a73U, 0x553366c6U,
+	0x300e3ea3U, 0x6ec5abfdU, 0xa8b0183bU, 0x2d5578beU,
+	0x8a189219U, 0xfa748e69U, 0x314e7fa2U, 0xf4fb0f67U,
+	0x08626ae4U, 0x113c2dfdU, 0xf4a15518U, 0xa8ea4244U,
+	0x6010708cU, 0xd4895d38U, 0xa725824bU, 0x8d870a61U,
+	0x0ba2a9e7U, 0x89820b65U, 0x03a8abefU, 0x58267eb4U,
+	0xf364971fU, 0xbcfb4750U, 0xe370930fU, 0x47fdbaabU,
+	0xff6b9413U, 0xce17d922U, 0x391e27d5U, 0x8a42c866U,
+	0xfb6e9517U, 0xa9aa0345U, 0x43f8bbafU, 0x5c237fb0U,
+	0xf9ee1715U, 0x42b8faaeU, 0x1fb3acf3U, 0xcb529927U,
+	0x7681f79aU, 0x49723ba5U, 0x4bf2b9a7U, 0x351124d9U,
+	0x210021cdU, 0x5aa6fcb6U, 0x12fceefeU, 0x80c8486cU,
+	0x15392cf9U, 0xecbf5300U, 0x311425ddU, 0xba7ec456U,
+	0x9f138c73U, 0x02e8eaeeU, 0x7fcbb493U, 0x5ea3fdb2U,
+	0xe4b55108U, 0x6c1f7380U, 0xe1f0110dU, 0xda06dc36U,
+	0xc098582cU, 0x9d930e71U, 0x2380a3cfU, 0xf5e11419U,
+	0x74017598U, 0x73c4b79fU, 0xf0a4541cU, 0x052d28e9U,
+	0x516c3dbdU, 0x2c4f63c0U, 0x012829edU, 0xb8fe4654U,
+	0x864dcb6aU, 0x6d5f3281U, 0xaf2f8043U, 0xc9d21b25U,
+	0x4eb7f9a2U, 0x4ff7b8a3U, 0xcdd71a21U, 0xa0e0404cU,
+	0x9cd34f70U, 0x9e53cd72U, 0x0fa7a8e3U, 0xb9be0755U,
+	0x5d633eb1U, 0x36d1e7daU, 0xa320834fU, 0x3791a6dbU,
+	0xc8925a24U, 0x457d38a9U, 0x6e9ff182U, 0xf1e4151dU,
+	0xf224d61eU, 0xfe2bd512U, 0x55693cb9U, 0x99960f75U,
+	0x9659cf7aU, 0xc218da2eU, 0x385e66d4U, 0x7d4b3691U,
+	0xb0f4445cU, 0xfcab5710U, 0x6fdfb083U, 0x7e8bf592U,
+	0x925cce7eU, 0x0ae2e8e6U, 0x250520c9U, 0x870d8a6bU,
+	0x2b8aa1c7U, 0xc3589b2fU, 0x6bdab187U, 0x2acae0c6U,
+	0xde03dd32U, 0x32d4e6deU, 0x14796df8U, 0xd9c61f35U,
+	0xc75d9a2bU, 0xdb469d37U, 0xa260c24eU, 0xbf3b8453U,
+	0x57e9bebbU, 0xd7499e3bU, 0xb274c65eU, 0x4d773aa1U,
+	0x006868ecU, 0x53ecbfbfU, 0x4ab2f8a6U, 0x0ee7e9e2U,
+	0x2d0f22c1U, 0x1c736ff0U, 0xcf579823U, 0x1af6ecf6U,
+	0x6290f28eU, 0x8b028967U, 0x5be6bdb7U, 0xcc975b20U,
+	0xd1cc1d3dU, 0x403878acU, 0x7004749cU, 0x290a23c5U,
+	0xf8ae5614U, 0xadaf0241U, 0x794e3795U, 0x18766ef4U,
+	0x52acfebeU, 0x4c377ba0U, 0xb334875fU, 0x858d0869U,
+	0xddc31e31U, 0x8cc74b60U, 0x98d64e74U, 0x681a7284U,
+	0xb4f14558U, 0xd609df3aU, 0x07adaaebU, 0xd5c91c39U,
+	0xd08c5c3cU, 0xe9fa1305U, 0x26c5e3caU, 0xc1d8192dU,
+	0x3d1b26d1U, 0x83088b6fU, 0x3b9ea5d7U, 0x06edebeaU,
+	0xe5f51009U, 0x1d332ef1U, 0x9b168d77U, 0x931c8f7fU,
+	0x84cd4968U, 0x6150318dU, 0x95990c79U, 0xdc835f30U,
+	0x2785a2cbU, 0x88c24a64U, 0xd8865e34U, 0xeb7a9107U,
+	0x59663fb5U, 0xca12d826U, 0x780e7694U, 0xd20cde3eU,
+	0x502c7cbcU, 0xf761961bU, 0xdf439c33U, 0xfdeb1611U,
+	0x67d5b28bU, 0x1ef3edf2U, 0x6a9af086U, 0xee3fd102U,
+	0x046d69e8U, 0x3c5b67d0U, 0x8f078863U, 0x46bdfbaaU,
+	0x284a62c4U, 0xa4e54148U, 0x695a3385U, 0xef7f9003U,
+	0x5fe3bcb3U, 0xacef4340U, 0xfa2ed416U, 0x9a56cc76U,
+	0xe635d30aU, 0xe230d20eU, 0x3edbe5d2U, 0x16f9effaU,
+	0x65553089U, 0x3f9ba4d3U, 0xedff1201U, 0x19362ff5U,
+	0xbdbb0651U, 0x244561c8U, 0xa1a0014dU, 0xae6fc142U,
+	0x17b9aefbU, 0x345165d8U, 0x22c0e2ceU, 0x56a9ffbaU,
+	0x2f8fa0c3U, 0x13bcafffU, 0x3adee4d6U, 0x305464dcU,
+	0x7bceb597U, 0x54297db8U, 0x97198e7bU, 0x7144359dU,
+	0xc5dd1829U, 0x64157188U, 0xbe7bc552U, 0xa5a50049U,
+	0xe775920bU, 0x8248ca6eU, 0x107c6cfcU, 0x1bb6adf7U,
+	0xb1b4055dU, 0xc49d5928U, 0x8188096dU, 0x919c0d7dU,
+	0x75413499U, 0xf621d71aU, 0x0d272ae1U, 0x7a8ef496U,
+	0x63d0b38fU, 0x09222be5U, 0x8e47c962U, 0xaa6ac046U,
+	0xc61ddb2aU, 0xb5b10459U, 0x94d94d78U, 0x77c1b69bU,
+	0x443d79a8U, 0xea3ad006U, 0x48327aa4U, 0x417839adU,
+	0x3394a7dfU, 0x90dc4c7cU, 0xa665c34aU, 0xe0b0500cU,
+	0xbb3e8557U, 0x204060ccU, 0x6695f38aU, 0xd34c9f3fU,
+	0xb671c75aU, 0xe8ba5204U, 0x2ecfe1c2U, 0xab2a8147U,
+	0x0c676be0U, 0x7c0b7790U, 0xb731865bU, 0x7284f69eU,
+	0xa314b739U, 0xba4af020U, 0x5fd788c5U, 0x039c9f99U,
+	0xcb66ad51U, 0x7fff80e5U, 0x0c535f96U, 0x26f1d7bcU,
+	0xa0d4743aU, 0x22f4d6b8U, 0xa8de7632U, 0xf350a369U,
+	0x58124ac2U, 0x178d9a8dU, 0x48064ed2U, 0xec8b6776U,
+	0x541d49ceU, 0x656104ffU, 0x9268fa08U, 0x213415bbU,
+	0x501848caU, 0x02dcde98U, 0xe88e6672U, 0xf755a26dU,
+	0x5298cac8U, 0xe9ce2773U, 0xb4c5712eU, 0x602444faU,
+	0xddf72a47U, 0xe204e678U, 0xe084647aU, 0x9e67f904U,
+	0x8a76fc10U, 0xf1d0216bU, 0xb98a3323U, 0x2bbe95b1U,
+	0xbe4ff124U, 0x47c98eddU, 0x9a62f800U, 0x1108198bU,
+	0x346551aeU, 0xa99e3733U, 0xd4bd694eU, 0xf5d5206fU,
+	0x4fc38cd5U, 0xc769ae5dU, 0x4a86ccd0U, 0x717001ebU,
+	0x6bee85f1U, 0x36e5d3acU, 0x88f67e12U, 0x5e97c9c4U,
+	0xdf77a845U, 0xd8b26a42U, 0x5bd289c1U, 0xae5bf534U,
+	0xfa1ae060U, 0x8739be1dU, 0xaa5ef430U, 0x13889b89U,
+	0x2d3b16b7U, 0xc629ef5cU, 0x04595d9eU, 0x62a4c6f8U,
+	0xe5c1247fU, 0xe481657eU, 0x66a1c7fcU, 0x0b969d91U,
+	0x37a592adU, 0x352510afU, 0xa4d1753eU, 0x12c8da88U,
+	0xf615e36cU, 0x9da73a07U, 0x08565e92U, 0x9ce77b06U,
+	0x63e487f9U, 0xee0be574U, 0xc5e92c5fU, 0x5a92c8c0U,
+	0x59520bc3U, 0x555d08cfU, 0xfe1fe164U, 0x32e0d2a8U,
+	0x3d2f12a7U, 0x696e07f3U, 0x9328bb09U, 0xd63deb4cU,
+	0x1b829981U, 0x57dd8acdU, 0xc4a96d5eU, 0xd5fd284fU,
+	0x392a13a3U, 0xa194353bU, 0x8e73fd14U, 0x2c7b57b6U,
+	0x80fc7c1aU, 0x682e46f2U, 0xc0ac6c5aU, 0x81bc3d1bU,
+	0x757500efU, 0x99a23b03U, 0xbf0fb025U, 0x72b0c2e8U,
+	0x6c2b47f6U, 0x703040eaU, 0x09161f93U, 0x144d598eU,
+	0xfc9f6366U, 0x7c3f43e6U, 0x19021b83U, 0xe601e77cU,
+	0xab1eb531U, 0xf89a6262U, 0xe1c4257bU, 0xa591343fU,
+	0x8679ff1cU, 0xb705b22dU, 0x642145feU, 0xb180312bU,
+	0xc9e62f53U, 0x207454baU, 0xf090606aU, 0x67e186fdU,
+	0x7abac0e0U, 0xeb4ea571U, 0xdb72a941U, 0x827cfe18U,
+	0x53d88bc9U, 0x06d9df9cU, 0xd238ea48U, 0xb300b329U,
+	0xf9da2363U, 0xe741a67dU, 0x18425a82U, 0x2efbd5b4U,
+	0x76b5c3ecU, 0x27b196bdU, 0x33a093a9U, 0xc36caf59U,
+	0x1f879885U, 0x7d7f02e7U, 0xacdb7736U, 0x7ebfc1e4U,
+	0x7bfa81e1U, 0x428cced8U, 0x8db33e17U, 0x6aaec4f0U,
+	0x966dfb0cU, 0x287e56b2U, 0x90e8780aU, 0xad9b3637U,
+	0x4e83cdd4U, 0xb645f32cU, 0x306050aaU, 0x386a52a2U,
+	0x2fbb94b5U, 0xca26ec50U, 0x3eefd1a4U, 0x77f582edU,
+	0x8cf37f16U, 0x23b497b9U, 0x73f083e9U, 0x400c4cdaU,
+	0xf210e268U, 0x616405fbU, 0xd378ab49U, 0x797a03e3U,
+	0xfb5aa161U, 0x5c174bc6U, 0x743541eeU, 0x569dcbccU,
+	0xcca36f56U, 0xb585302fU, 0xc1ec2d5bU, 0x45490cdfU,
+	0xaf1bb435U, 0x972dba0dU, 0x247155beU, 0xedcb2677U,
+	0x833cbf19U, 0x0f939c95U, 0xc22cee58U, 0x44094ddeU,
+	0xf495616eU, 0x07999e9dU, 0x515809cbU, 0x312011abU,
+	0x4d430ed7U, 0x49460fd3U, 0x95ad380fU, 0xbd8f3227U,
+	0xce23ed54U, 0x94ed790eU, 0x4689cfdcU, 0xb240f228U,
+	0x16cddb8cU, 0x8f33bc15U, 0x0ad6dc90U, 0x05191c9fU,
+	0xbccf7326U, 0x9f27b805U, 0x89b63f13U, 0xfddf2267U,
+	0x84f97d1eU, 0xb8ca7222U, 0x91a8390bU, 0x9b22b901U,
+	0xd0b8684aU, 0xff5fa065U, 0x3c6f53a6U, 0xda32e840U,
+	0x6eabc5f4U, 0xcf63ac55U, 0x150d188fU, 0x0ed3dd94U,
+	0x4c034fd6U, 0x293e17b3U, 0xbb0ab121U, 0xb0c0702aU,
+	0x1ac2d880U, 0x6feb84f5U, 0x2afed4b0U, 0x3aead0a0U,
+	0xde37e944U, 0x5d570ac7U, 0xa651f73cU, 0xd1f8294bU,
+	0xc8a66e52U, 0xa254f638U, 0x253114bfU, 0x011c1d9bU,
+	0x6d6b06f7U, 0x1ec7d984U, 0x3faf90a5U, 0xdcb76b46U,
+	0xef4ba475U, 0x414c0ddbU, 0xe344a779U, 0xea0ee470U,
+	0x98e27a02U, 0x3baa91a1U, 0x0d131e97U, 0x4bc68dd1U,
+	0x1048588aU, 0x8b36bd11U, 0xcde32e57U, 0x783a42e2U,
+	0x1d071a87U, 0x43cc8fd9U, 0x85b93c1fU, 0x005c5c9aU,
+	0xa711b63dU, 0xd77daa4dU, 0x1c475b86U, 0xd9f22b43U,
+	0xc9f53cb2U, 0xd0ab7babU, 0x3536034eU, 0x697d1412U,
+	0xa18726daU, 0x151e0b6eU, 0x66b2d41dU, 0x4c105c37U,
+	0xca35ffb1U, 0x48155d33U, 0xc23ffdb9U, 0x99b128e2U,
+	0x32f3c149U, 0x7d6c1106U, 0x22e7c559U, 0x866aecfdU,
+	0x3efcc245U, 0x0f808f74U, 0xf8897183U, 0x4bd59e30U,
+	0x3af9c341U, 0x683d5513U, 0x826fedf9U, 0x9db429e6U,
+	0x38794143U, 0x832facf8U, 0xde24faa5U, 0x0ac5cf71U,
+	0xb716a1ccU, 0x88e56df3U, 0x8a65eff1U, 0xf486728fU,
+	0xe097779bU, 0x9b31aae0U, 0xd36bb8a8U, 0x415f1e3aU,
+	0xd4ae7aafU, 0x2d280556U, 0xf083738bU, 0x7be99200U,
+	0x5e84da25U, 0xc37fbcb8U, 0xbe5ce2c5U, 0x9f34abe4U,
+	0x2522075eU, 0xad8825d6U, 0x2067475bU, 0x1b918a60U,
+	0x010f0e7aU, 0x5c045827U, 0xe217f599U, 0x3476424fU,
+	0xb59623ceU, 0xb253e1c9U, 0x3133024aU, 0xc4ba7ebfU,
+	0x90fb6bebU, 0xedd83596U, 0xc0bf7fbbU, 0x79691002U,
+	0x47da9d3cU, 0xacc864d7U, 0x6eb8d615U, 0x08454d73U,
+	0x8f20aff4U, 0x8e60eef5U, 0x0c404c77U, 0x6177161aU,
+	0x5d441926U, 0x5fc49b24U, 0xce30feb5U, 0x78295103U,
+	0x9cf468e7U, 0xf746b18cU, 0x62b7d519U, 0xf606f08dU,
+	0x09050c72U, 0x84ea6effU, 0xaf08a7d4U, 0x3073434bU,
+	0x33b38048U, 0x3fbc8344U, 0x94fe6aefU, 0x58015923U,
+	0x57ce992cU, 0x038f8c78U, 0xf9c93082U, 0xbcdc60c7U,
+	0x7163120aU, 0x3d3c0146U, 0xae48e6d5U, 0xbf1ca3c4U,
+	0x53cb9828U, 0xcb75beb0U, 0xe492769fU, 0x469adc3dU,
+	0xea1df791U, 0x02cfcd79U, 0xaa4de7d1U, 0xeb5db690U,
+	0x1f948b64U, 0xf343b088U, 0xd5ee3baeU, 0x18514963U,
+	0x06cacc7dU, 0x1ad1cb61U, 0x63f79418U, 0x7eacd205U,
+	0x967ee8edU, 0x16dec86dU, 0x73e39008U, 0x8ce06cf7U,
+	0xc1ff3ebaU, 0x927be9e9U, 0x8b25aef0U, 0xcf70bfb4U,
+	0xec987497U, 0xdde439a6U, 0x0ec0ce75U, 0xdb61baa0U,
+	0xa307a4d8U, 0x4a95df31U, 0x9a71ebe1U, 0x0d000d76U,
+	0x105b4b6bU, 0x81af2efaU, 0xb19322caU, 0xe89d7593U,
+	0x39390042U, 0x6c385417U, 0xb8d961c3U, 0xd9e138a2U,
+	0x933ba8e8U, 0x8da02df6U, 0x72a3d109U, 0x441a5e3fU,
+	0x1c544867U, 0x4d501d36U, 0x59411822U, 0xa98d24d2U,
+	0x7566130eU, 0x179e896cU, 0xc63afcbdU, 0x145e4a6fU,
+	0x111b0a6aU, 0x286d4553U, 0xe752b59cU, 0x004f4f7bU,
+	0xfc8c7087U, 0x429fdd39U, 0xfa09f381U, 0xc77abdbcU,
+	0x2462465fU, 0xdca478a7U, 0x5a81db21U, 0x528bd929U,
+	0x455a1f3eU, 0xa0c767dbU, 0x540e5a2fU, 0x1d140966U,
+	0xe612f49dU, 0x49551c32U, 0x19110862U, 0x2aedc751U,
+	0x98f169e3U, 0x0b858e70U, 0xb99920c2U, 0x139b8868U,
+	0x91bb2aeaU, 0x36f6c04dU, 0x1ed4ca65U, 0x3c7c4047U,
+	0xa642e4ddU, 0xdf64bba4U, 0xab0da6d0U, 0x2fa88754U,
+	0xc5fa3fbeU, 0xfdcc3186U, 0x4e90de35U, 0x872aadfcU,
+	0xe9dd3492U, 0x6572171eU, 0xa8cd65d3U, 0x2ee8c655U,
+	0x9e74eae5U, 0x6d781516U, 0x3bb98240U, 0x5bc19a20U,
+	0x27a2855cU, 0x23a78458U, 0xff4cb384U, 0xd76eb9acU,
+	0xa4c266dfU, 0xfe0cf285U, 0x2c684457U, 0xd8a179a3U,
+	0x7c2c5007U, 0xe5d2379eU, 0x6037571bU, 0x6ff89714U,
+	0xd62ef8adU, 0xf5c6338eU, 0xe357b498U, 0x973ea9ecU,
+	0xee18f695U, 0xd22bf9a9U, 0xfb49b280U, 0xf1c3328aU,
+	0xba59e3c1U, 0x95be2beeU, 0x568ed82dU, 0xb0d363cbU,
+	0x044a4e7fU, 0xa58227deU, 0x7fec9304U, 0x6432561fU,
+	0x26e2c45dU, 0x43df9c38U, 0xd1eb3aaaU, 0xda21fba1U,
+	0x7023530bU, 0x050a0f7eU, 0x401f5f3bU, 0x500b5b2bU,
+	0xb4d662cfU, 0x37b6814cU, 0xccb07cb7U, 0xbb19a2c0U,
+	0xa247e5d9U, 0xc8b57db3U, 0x4fd09f34U, 0x6bfd9610U,
+	0x078a8d7cU, 0x7426520fU, 0x554e1b2eU, 0xb656e0cdU,
+	0x85aa2ffeU, 0x2bad8650U, 0x89a52cf2U, 0x80ef6ffbU,
+	0xf203f189U, 0x514b1a2aU, 0x67f2951cU, 0x2127065aU,
+	0x7aa9d301U, 0xe1d7369aU, 0xa702a5dcU, 0x12dbc969U,
+	0x77e6910cU, 0x292d0452U, 0xef58b794U, 0x6abdd711U,
+	0xcdf03db6U, 0xbd9c21c6U, 0x76a6d00dU, 0xb313a0c8U,
+	0xa4bd1997U, 0xbde35e8eU, 0x587e266bU, 0x04353137U,
+	0xcccf03ffU, 0x78562e4bU, 0x0bfaf138U, 0x21587912U,
+	0xa77dda94U, 0x255d7816U, 0xaf77d89cU, 0xf4f90dc7U,
+	0x5fbbe46cU, 0x10243423U, 0x4fafe07cU, 0xeb22c9d8U,
+	0x53b4e760U, 0x62c8aa51U, 0x95c154a6U, 0x269dbb15U,
+	0x57b1e664U, 0x05757036U, 0xef27c8dcU, 0xf0fc0cc3U,
+	0x55316466U, 0xee6789ddU, 0xb36cdf80U, 0x678dea54U,
+	0xda5e84e9U, 0xe5ad48d6U, 0xe72dcad4U, 0x99ce57aaU,
+	0x8ddf52beU, 0xf6798fc5U, 0xbe239d8dU, 0x2c173b1fU,
+	0xb9e65f8aU, 0x40602073U, 0x9dcb56aeU, 0x16a1b725U,
+	0x33ccff00U, 0xae37999dU, 0xd314c7e0U, 0xf27c8ec1U,
+	0x486a227bU, 0xc0c000f3U, 0x4d2f627eU, 0x76d9af45U,
+	0x6c472b5fU, 0x314c7d02U, 0x8f5fd0bcU, 0x593e676aU,
+	0xd8de06ebU, 0xdf1bc4ecU, 0x5c7b276fU, 0xa9f25b9aU,
+	0xfdb34eceU, 0x809010b3U, 0xadf75a9eU, 0x14213527U,
+	0x2a92b819U, 0xc18041f2U, 0x03f0f330U, 0x650d6856U,
+	0xe2688ad1U, 0xe328cbd0U, 0x61086952U, 0x0c3f333fU,
+	0x300c3c03U, 0x328cbe01U, 0xa378db90U, 0x15617426U,
+	0xf1bc4dc2U, 0x9a0e94a9U, 0x0ffff03cU, 0x9b4ed5a8U,
+	0x644d2957U, 0xe9a24bdaU, 0xc24082f1U, 0x5d3b666eU,
+	0x5efba56dU, 0x52f4a661U, 0xf9b64fcaU, 0x35497c06U,
+	0x3a86bc09U, 0x6ec7a95dU, 0x948115a7U, 0xd19445e2U,
+	0x1c2b372fU, 0x50742463U, 0xc300c3f0U, 0xd25486e1U,
+	0x3e83bd0dU, 0xa63d9b95U, 0x89da53baU, 0x2bd2f918U,
+	0x8755d2b4U, 0x6f87e85cU, 0xc705c2f4U, 0x861593b5U,
+	0x72dcae41U, 0x9e0b95adU, 0xb8a61e8bU, 0x75196c46U,
+	0x6b82e958U, 0x7799ee44U, 0x0ebfb13dU, 0x13e4f720U,
+	0xfb36cdc8U, 0x7b96ed48U, 0x1eabb52dU, 0xe1a849d2U,
+	0xacb71b9fU, 0xff33ccccU, 0xe66d8bd5U, 0xa2389a91U,
+	0x81d051b2U, 0xb0ac1c83U, 0x6388eb50U, 0xb6299f85U,
+	0xce4f81fdU, 0x27ddfa14U, 0xf739cec4U, 0x60482853U,
+	0x7d136e4eU, 0xece70bdfU, 0xdcdb07efU, 0x85d550b6U,
+	0x54712567U, 0x01707132U, 0xd59144e6U, 0xb4a91d87U,
+	0xfe738dcdU, 0xe0e808d3U, 0x1febf42cU, 0x29527b1aU,
+	0x711c6d42U, 0x20183813U, 0x34093d07U, 0xc4c501f7U,
+	0x182e362bU, 0x7ad6ac49U, 0xab72d998U, 0x79166f4aU,
+	0x7c532f4fU, 0x45256076U, 0x8a1a90b9U, 0x6d076a5eU,
+	0x91c455a2U, 0x2fd7f81cU, 0x9741d6a4U, 0xaa329899U,
+	0x492a637aU, 0xb1ec5d82U, 0x37c9fe04U, 0x3fc3fc0cU,
+	0x28123a1bU, 0xcd8f42feU, 0x39467f0aU, 0x705c2c43U,
+	0x8b5ad1b8U, 0x241d3917U, 0x74592d47U, 0x47a5e274U,
+	0xf5b94cc6U, 0x66cdab55U, 0xd4d105e7U, 0x7ed3ad4dU,
+	0xfcf30fcfU, 0x5bbee568U, 0x739cef40U, 0x51346562U,
+	0xcb0ac1f8U, 0xb22c9e81U, 0xc64583f5U, 0x42e0a271U,
+	0xa8b21a9bU, 0x908414a3U, 0x23d8fb10U, 0xea6288d9U,
+	0x849511b7U, 0x083a323bU, 0xc58540f6U, 0x43a0e370U,
+	0xf33ccfc0U, 0x00303033U, 0x56f1a765U, 0x3689bf05U,
+	0x4aeaa079U, 0x4eefa17dU, 0x920496a1U, 0xba269c89U,
+	0xc98a43faU, 0x9344d7a0U, 0x41206172U, 0xb5e95c86U,
+	0x11647522U, 0x889a12bbU, 0x0d7f723eU, 0x02b0b231U,
+	0xbb66dd88U, 0x988e16abU, 0x8e1f91bdU, 0xfa768cc9U,
+	0x8350d3b0U, 0xbf63dc8cU, 0x960197a5U, 0x9c8b17afU,
+	0xd711c6e4U, 0xf8f60ecbU, 0x3bc6fd08U, 0xdd9b46eeU,
+	0x69026b5aU, 0xc8ca02fbU, 0x12a4b621U, 0x097a733aU,
+	0x4baae178U, 0x2e97b91dU, 0xbca31f8fU, 0xb769de84U,
+	0x1d6b762eU, 0x68422a5bU, 0x2d577a1eU, 0x3d437e0eU,
+	0xd99e47eaU, 0x5afea469U, 0xa1f85992U, 0xd65187e5U,
+	0xcf0fc0fcU, 0xa5fd5896U, 0x2298ba11U, 0x06b5b335U,
+	0x6ac2a859U, 0x196e772aU, 0x38063e0bU, 0xdb1ec5e8U,
+	0xe8e20adbU, 0x46e5a375U, 0xe4ed09d7U, 0xeda74adeU,
+	0x9f4bd4acU, 0x3c033f0fU, 0x0abab039U, 0x4c6f237fU,
+	0x17e1f624U, 0x8c9f13bfU, 0xca4a80f9U, 0x7f93ec4cU,
+	0x1aaeb429U, 0x44652177U, 0x821092b1U, 0x07f5f234U,
+	0xa0b81893U, 0xd0d404e3U, 0x1beef528U, 0xde5b85edU,
+	0x5fdb840aU, 0x4685c313U, 0xa318bbf6U, 0xff53acaaU,
+	0x37a99e62U, 0x8330b3d6U, 0xf09c6ca5U, 0xda3ee48fU,
+	0x5c1b4709U, 0xde3be58bU, 0x54114501U, 0x0f9f905aU,
+	0xa4dd79f1U, 0xeb42a9beU, 0xb4c97de1U, 0x10445445U,
+	0xa8d27afdU, 0x99ae37ccU, 0x6ea7c93bU, 0xddfb2688U,
+	0xacd77bf9U, 0xfe13edabU, 0x14415541U, 0x0b9a915eU,
+	0xae57f9fbU, 0x15011440U, 0x480a421dU, 0x9ceb77c9U,
+	0x21381974U, 0x1ecbd54bU, 0x1c4b5749U, 0x62a8ca37U,
+	0x76b9cf23U, 0x0d1f1258U, 0x45450010U, 0xd771a682U,
+	0x4280c217U, 0xbb06bdeeU, 0x66adcb33U, 0xedc72ab8U,
+	0xc8aa629dU, 0x55510400U, 0x28725a7dU, 0x091a135cU,
+	0xb30cbfe6U, 0x3ba69d6eU, 0xb649ffe3U, 0x8dbf32d8U,
+	0x9721b6c2U, 0xca2ae09fU, 0x74394d21U, 0xa258faf7U,
+	0x23b89b76U, 0x247d5971U, 0xa71dbaf2U, 0x5294c607U,
+	0x06d5d353U, 0x7bf68d2eU, 0x5691c703U, 0xef47a8baU,
+	0xd1f42584U, 0x3ae6dc6fU, 0xf8966eadU, 0x9e6bf5cbU,
+	0x190e174cU, 0x184e564dU, 0x9a6ef4cfU, 0xf759aea2U,
+	0xcb6aa19eU, 0xc9ea239cU, 0x581e460dU, 0xee07e9bbU,
+	0x0adad05fU, 0x61680934U, 0xf4996da1U, 0x60284835U,
+	0x9f2bb4caU, 0x12c4d647U, 0x39261f6cU, 0xa65dfbf3U,
+	0xa59d38f0U, 0xa9923bfcU, 0x02d0d257U, 0xce2fe19bU,
+	0xc1e02194U, 0x95a134c0U, 0x6fe7883aU, 0x2af2d87fU,
+	0xe74daab2U, 0xab12b9feU, 0x38665e6dU, 0x29321b7cU,
+	0xc5e52090U, 0x5d5b0608U, 0x72bcce27U, 0xd0b46485U,
+	0x7c334f29U, 0x94e175c1U, 0x3c635f69U, 0x7d730e28U,
+	0x89ba33dcU, 0x656d0830U, 0x43c08316U, 0x8e7ff1dbU,
+	0x90e474c5U, 0x8cff73d9U, 0xf5d92ca0U, 0xe8826abdU,
+	0x00505055U, 0x80f070d5U, 0xe5cd28b0U, 0x1aced44fU,
+	0x57d18602U, 0x04555151U, 0x1d0b1648U, 0x595e070cU,
+	0x7ab6cc2fU, 0x4bca811eU, 0x98ee76cdU, 0x4d4f0218U,
+	0x35291c60U, 0xdcbb6789U, 0x0c5f5359U, 0x9b2eb5ceU,
+	0x8675f3d3U, 0x17819642U, 0x27bd9a72U, 0x7eb3cd2bU,
+	0xaf17b8faU, 0xfa16ecafU, 0x2ef7d97bU, 0x4fcf801aU,
+	0x05151050U, 0x1b8e954eU, 0xe48d69b1U, 0xd234e687U,
+	0x8a7af0dfU, 0xdb7ea58eU, 0xcf6fa09aU, 0x3fa39c6aU,
+	0xe348abb6U, 0x81b031d4U, 0x50144405U, 0x8270f2d7U,
+	0x8735b2d2U, 0xbe43fdebU, 0x717c0d24U, 0x9661f7c3U,
+	0x6aa2c83fU, 0xd4b16581U, 0x6c274b39U, 0x51540504U,
+	0xb24cfee7U, 0x4a8ac01fU, 0xccaf6399U, 0xc4a56191U,
+	0xd374a786U, 0x36e9df63U, 0xc220e297U, 0x8b3ab1deU,
+	0x703c4c25U, 0xdf7ba48aU, 0x8f3fb0daU, 0xbcc37fe9U,
+	0x0edfd15bU, 0x9dab36c8U, 0x2fb7987aU, 0x85b530d0U,
+	0x07959252U, 0xa0d878f5U, 0x88fa72ddU, 0xaa52f8ffU,
+	0x306c5c65U, 0x494a031cU, 0x3d231e68U, 0xb9863fecU,
+	0x53d48706U, 0x6be2893eU, 0xd8be668dU, 0x11041544U,
+	0x7ff38c2aU, 0xf35cafa6U, 0x3ee3dd6bU, 0xb8c67eedU,
+	0x085a525dU, 0xfb56adaeU, 0xad973af8U, 0xcdef2298U,
+	0xb18c3de4U, 0xb5893ce0U, 0x69620b3cU, 0x41400114U,
+	0x32ecde67U, 0x68224a3dU, 0xba46fcefU, 0x4e8fc11bU,
+	0xea02e8bfU, 0x73fc8f26U, 0xf619efa3U, 0xf9d62facU,
+	0x40004015U, 0x63e88b36U, 0x75790c20U, 0x01101154U,
+	0x78364e2dU, 0x44054111U, 0x6d670a38U, 0x67ed8a32U,
+	0x2c775b79U, 0x03909356U, 0xc0a06095U, 0x26fddb73U,
+	0x9264f6c7U, 0x33ac9f66U, 0xe9c22bbcU, 0xf21ceea7U,
+	0xb0cc7ce5U, 0xd5f12480U, 0x47c58212U, 0x4c0f4319U,
+	0xe60debb3U, 0x9324b7c6U, 0xd631e783U, 0xc625e393U,
+	0x22f8da77U, 0xa19839f4U, 0x5a9ec40fU, 0x2d371a78U,
+	0x34695d61U, 0x5e9bc50bU, 0xd9fe278cU, 0xfdd32ea8U,
+	0x91a435c4U, 0xe208eab7U, 0xc360a396U, 0x20785875U,
+	0x13849746U, 0xbd833ee8U, 0x1f8b944aU, 0x16c1d743U,
+	0x642d4931U, 0xc765a292U, 0xf1dc2da4U, 0xb709bee2U,
+	0xec876bb9U, 0x77f98e22U, 0x312c1d64U, 0x84f571d1U,
+	0xe1c829b4U, 0xbf03bceaU, 0x79760f2cU, 0xfc936fa9U,
+	0x5bde850eU, 0x2bb2997eU, 0xe08868b5U, 0x253d1870U,
+	0x7c85f977U, 0x65dbbe6eU, 0x8046c68bU, 0xdc0dd1d7U,
+	0x14f7e31fU, 0xa06eceabU, 0xd3c211d8U, 0xf96099f2U,
+	0x7f453a74U, 0xfd6598f6U, 0x774f387cU, 0x2cc1ed27U,
+	0x8783048cU, 0xc81cd4c3U, 0x9797009cU, 0x331a2938U,
+	0x8b8c0780U, 0xbaf04ab1U, 0x4df9b446U, 0xfea55bf5U,
+	0x8f890684U, 0xdd4d90d6U, 0x371f283cU, 0x28c4ec23U,
+	0x8d098486U, 0x365f693dU, 0x6b543f60U, 0xbfb50ab4U,
+	0x02666409U, 0x3d95a836U, 0x3f152a34U, 0x41f6b74aU,
+	0x55e7b25eU, 0x2e416f25U, 0x661b7d6dU, 0xf42fdbffU,
+	0x61debf6aU, 0x9858c093U, 0x45f3b64eU, 0xce9957c5U,
+	0xebf41fe0U, 0x760f797dU, 0x0b2c2700U, 0x2a446e21U,
+	0x9052c29bU, 0x18f8e013U, 0x9517829eU, 0xaee14fa5U,
+	0xb47fcbbfU, 0xe9749de2U, 0x5767305cU, 0x8106878aU,
+	0x00e6e60bU, 0x0723240cU, 0x8443c78fU, 0x71cabb7aU,
+	0x258bae2eU, 0x58a8f053U, 0x75cfba7eU, 0xcc19d5c7U,
+	0xf2aa58f9U, 0x19b8a112U, 0xdbc813d0U, 0xbd3588b6U,
+	0x3a506a31U, 0x3b102b30U, 0xb93089b2U, 0xd407d3dfU,
+	0xe834dce3U, 0xeab45ee1U, 0x7b403b70U, 0xcd5994c6U,
+	0x2984ad22U, 0x42367449U, 0xd7c710dcU, 0x43763548U,
+	0xbc75c9b7U, 0x319aab3aU, 0x1a786211U, 0x8503868eU,
+	0x86c3458dU, 0x8acc4681U, 0x218eaf2aU, 0xed719ce6U,
+	0xe2be5ce9U, 0xb6ff49bdU, 0x4cb9f547U, 0x09aca502U,
+	0xc413d7cfU, 0x884cc483U, 0x1b382310U, 0x0a6c6601U,
+	0xe6bb5dedU, 0x7e057b75U, 0x51e2b35aU, 0xf3ea19f8U,
+	0x5f6d3254U, 0xb7bf08bcU, 0x1f3d2214U, 0x5e2d7355U,
+	0xaae44ea1U, 0x4633754dU, 0x609efe6bU, 0xad218ca6U,
+	0xb3ba09b8U, 0xafa10ea4U, 0xd68751ddU, 0xcbdc17c0U,
+	0x230e2d28U, 0xa3ae0da8U, 0xc69355cdU, 0x3990a932U,
+	0x748ffb7fU, 0x270b2c2cU, 0x3e556b35U, 0x7a007a71U,
+	0x59e8b152U, 0x6894fc63U, 0xbbb00bb0U, 0x6e117f65U,
+	0x1677611dU, 0xffe51af4U, 0x2f012e24U, 0xb870c8b3U,
+	0xa52b8eaeU, 0x34dfeb3fU, 0x04e3e70fU, 0x5dedb056U,
+	0x8c49c587U, 0xd94891d2U, 0x0da9a406U, 0x6c91fd67U,
+	0x264b6d2dU, 0x38d0e833U, 0xc7d314ccU, 0xf16a9bfaU,
+	0xa9248da2U, 0xf820d8f3U, 0xec31dde7U, 0x1cfde117U,
+	0xc016d6cbU, 0xa2ee4ca9U, 0x734a3978U, 0xa12e8faaU,
+	0xa46bcfafU, 0x9d1d8096U, 0x52227059U, 0xb53f8abeU,
+	0x49fcb542U, 0xf7ef18fcU, 0x4f793644U, 0x720a7879U,
+	0x9112839aU, 0x69d4bd62U, 0xeff11ee4U, 0xe7fb1cecU,
+	0xf02adafbU, 0x15b7a21eU, 0xe17e9feaU, 0xa864cca3U,
+	0x53623158U, 0xfc25d9f7U, 0xac61cda7U, 0x9f9d0294U,
+	0x2d81ac26U, 0xbef54bb5U, 0x0ce9e507U, 0xa6eb4dadU,
+	0x24cbef2fU, 0x83860588U, 0xaba40fa0U, 0x890c8582U,
+	0x13322118U, 0x6a147e61U, 0x1e7d6315U, 0x9ad84291U,
+	0x708afa7bU, 0x48bcf443U, 0xfbe01bf0U, 0x325a6839U,
+	0x5cadf157U, 0xd002d2dbU, 0x1dbda016U, 0x9b980390U,
+	0x2b042f20U, 0xd808d0d3U, 0x8ec94785U, 0xeeb15fe5U,
+	0x92d24099U, 0x96d7419dU, 0x4a3c7641U, 0x621e7c69U,
+	0x11b2a31aU, 0x4b7c3740U, 0x99188192U, 0x6dd1bc66U,
+	0xc95c95c2U, 0x50a2f25bU, 0xd54792deU, 0xda8852d1U,
+	0x635e3d68U, 0x40b6f64bU, 0x5627715dU, 0x224e6c29U,
+	0x5b683350U, 0x675b3c6cU, 0x4e397745U, 0x44b3f74fU,
+	0x0f292604U, 0x20ceee2bU, 0xe3fe1de8U, 0x05a3a60eU,
+	0xb13a8bbaU, 0x10f2e21bU, 0xca9c56c1U, 0xd14293daU,
+	0x93920198U, 0xf6af59fdU, 0x649bff6fU, 0x6f513e64U,
+	0xc55396ceU, 0xb07acabbU, 0xf56f9afeU, 0xe57b9eeeU,
+	0x01a6a70aU, 0x82c64489U, 0x79c0b972U, 0x0e696705U,
+	0x1737201cU, 0x7dc5b876U, 0xfaa05af1U, 0xde8d53d5U,
+	0xb2fa48b9U, 0xc15697caU, 0xe03edeebU, 0x03262508U,
+	0x30daea3bU, 0x9edd4395U, 0x3cd5e937U, 0x359faa3eU,
+	0x4773344cU, 0xe43bdfefU, 0xd28250d9U, 0x9457c39fU,
+	0xcfd916c4U, 0x54a7f35fU, 0x12726019U, 0xa7ab0cacU,
+	0xc29654c9U, 0x9c5dc197U, 0x5a287251U, 0xdfcd12d4U,
+	0x7880f873U, 0x08ece403U, 0xc3d615c8U, 0x0663650dU,
+	0x14ccd856U, 0x0d929f4fU, 0xe80fe7aaU, 0xb444f0f6U,
+	0x7cbec23eU, 0xc827ef8aU, 0xbb8b30f9U, 0x9129b8d3U,
+	0x170c1b55U, 0x952cb9d7U, 0x1f06195dU, 0x4488cc06U,
+	0xefca25adU, 0xa055f5e2U, 0xffde21bdU, 0x5b530819U,
+	0xe3c526a1U, 0xd2b96b90U, 0x25b09567U, 0x96ec7ad4U,
+	0xe7c027a5U, 0xb504b1f7U, 0x5f56091dU, 0x408dcd02U,
+	0xe540a5a7U, 0x5e16481cU, 0x031d1e41U, 0xd7fc2b95U,
+	0x6a2f4528U, 0x55dc8917U, 0x575c0b15U, 0x29bf966bU,
+	0x3dae937fU, 0x46084e04U, 0x0e525c4cU, 0x9c66fadeU,
+	0x09979e4bU, 0xf011e1b2U, 0x2dba976fU, 0xa6d076e4U,
+	0x83bd3ec1U, 0x1e46585cU, 0x63650621U, 0x420d4f00U,
+	0xf81be3baU, 0x70b1c132U, 0xfd5ea3bfU, 0xc6a86e84U,
+	0xdc36ea9eU, 0x813dbcc3U, 0x3f2e117dU, 0xe94fa6abU,
+	0x68afc72aU, 0x6f6a052dU, 0xec0ae6aeU, 0x19839a5bU,
+	0x4dc28f0fU, 0x30e1d172U, 0x1d869b5fU, 0xa450f4e6U,
+	0x9ae379d8U, 0x71f18033U, 0xb38132f1U, 0xd57ca997U,
+	0x52194b10U, 0x53590a11U, 0xd179a893U, 0xbc4ef2feU,
+	0x807dfdc2U, 0x82fd7fc0U, 0x13091a51U, 0xa510b5e7U,
+	0x41cd8c03U, 0x2a7f5568U, 0xbf8e31fdU, 0x2b3f1469U,
+	0xd43ce896U, 0x59d38a1bU, 0x72314330U, 0xed4aa7afU,
+	0xee8a64acU, 0xe28567a0U, 0x49c78e0bU, 0x8538bdc7U,
+	0x8af77dc8U, 0xdeb6689cU, 0x24f0d466U, 0x61e58423U,
+	0xac5af6eeU, 0xe005e5a2U, 0x73710231U, 0x62254720U,
+	0x8ef27cccU, 0x164c5a54U, 0x39ab927bU, 0x9ba338d9U,
+	0x37241375U, 0xdff6299dU, 0x77740335U, 0x36645274U,
+	0xc2ad6f80U, 0x2e7a546cU, 0x08d7df4aU, 0xc568ad87U,
+	0xdbf32899U, 0xc7e82f85U, 0xbece70fcU, 0xa39536e1U,
+	0x4b470c09U, 0xcbe72c89U, 0xaeda74ecU, 0x51d98813U,
+	0x1cc6da5eU, 0x4f420d0dU, 0x561c4a14U, 0x12495b50U,
+	0x31a19073U, 0x00dddd42U, 0xd3f92a91U, 0x06585e44U,
+	0x7e3e403cU, 0x97ac3bd5U, 0x47480f05U, 0xd039e992U,
+	0xcd62af8fU, 0x5c96ca1eU, 0x6caac62eU, 0x35a49177U,
+	0xe400e4a6U, 0xb101b0f3U, 0x65e08527U, 0x04d8dc46U,
+	0x4e024c0cU, 0x5099c912U, 0xaf9a35edU, 0x9923badbU,
+	0xc16dac83U, 0x9069f9d2U, 0x8478fcc6U, 0x74b4c036U,
+	0xa85ff7eaU, 0xcaa76d88U, 0x1b031859U, 0xc967ae8bU,
+	0xcc22ee8eU, 0xf554a1b7U, 0x3a6b5178U, 0xdd76ab9fU,
+	0x21b59463U, 0x9fa639ddU, 0x27301765U, 0x1a435958U,
+	0xf95ba2bbU, 0x019d9c43U, 0x87b83fc5U, 0x8fb23dcdU,
+	0x9863fbdaU, 0x7dfe833fU, 0x8937becbU, 0xc02ded82U,
+	0x3b2b1079U, 0x946cf8d6U, 0xc428ec86U, 0xf7d423b5U,
+	0x45c88d07U, 0xd6bc6a94U, 0x64a0c426U, 0xcea26c8cU,
+	0x4c82ce0eU, 0xebcf24a9U, 0xc3ed2e81U, 0xe145a4a3U,
+	0x7b7b0039U, 0x025d5f40U, 0x76344234U, 0xf29163b0U,
+	0x18c3db5aU, 0x20f5d562U, 0x93a93ad1U, 0x5a134918U,
+	0x34e4d076U, 0xb84bf3faU, 0x75f48137U, 0xf3d122b1U,
+	0x434d0e01U, 0xb041f1f2U, 0xe68066a4U, 0x86f87ec4U,
+	0xfa9b61b8U, 0xfe9e60bcU, 0x22755760U, 0x0a575d48U,
+	0x79fb823bU, 0x23351661U, 0xf151a0b3U, 0x05989d47U,
+	0xa115b4e3U, 0x38ebd37aU, 0xbd0eb3ffU, 0xb2c173f0U,
+	0x0b171c49U, 0x28ffd76aU, 0x3e6e507cU, 0x4a074d08U,
+	0x33211271U, 0x0f121d4dU, 0x26705664U, 0x2cfad66eU,
+	0x67600725U, 0x4887cf0aU, 0x8bb73cc9U, 0x6dea872fU,
+	0xd973aa9bU, 0x78bbc33aU, 0xa2d577e0U, 0xb90bb2fbU,
+	0xfbdb20b9U, 0x9ee678dcU, 0x0cd2de4eU, 0x07181f45U,
+	0xad1ab7efU, 0xd833eb9aU, 0x9d26bbdfU, 0x8d32bfcfU,
+	0x69ef862bU, 0xea8f65a8U, 0x11899853U, 0x66204624U,
+	0x7f7e013dU, 0x158c9957U, 0x92e97bd0U, 0xb6c472f4U,
+	0xdab36998U, 0xa91fb6ebU, 0x8877ffcaU, 0x6b6f0429U,
+	0x5893cb1aU, 0xf69462b4U, 0x549cc816U, 0x5dd68b1fU,
+	0x2f3a156dU, 0x8c72feceU, 0xbacb71f8U, 0xfc1ee2beU,
+	0xa79037e5U, 0x3ceed27eU, 0x7a3b4138U, 0xcfe22d8dU,
+	0xaadf75e8U, 0xf414e0b6U, 0x32615370U, 0xb78433f5U,
+	0x10c9d952U, 0x60a5c522U, 0xab9f34e9U, 0x6e2a442cU,
+	0x026062ecU, 0x1b3e25f5U, 0xfea35d10U, 0xa2e84a4cU,
+	0x6a127884U, 0xde8b5530U, 0xad278a43U, 0x87850269U,
+	0x01a0a1efU, 0x8380036dU, 0x09aaa3e7U, 0x522476bcU,
+	0xf9669f17U, 0xb6f94f58U, 0xe9729b07U, 0x4dffb2a3U,
+	0xf5699c1bU, 0xc415d12aU, 0x331c2fddU, 0x8040c06eU,
+	0xf16c9d1fU, 0xa3a80b4dU, 0x49fab3a7U, 0x562177b8U,
+	0xf3ec1f1dU, 0x48baf2a6U, 0x15b1a4fbU, 0xc150912fU,
+	0x7c83ff92U, 0x437033adU, 0x41f0b1afU, 0x3f132cd1U,
+	0x2b0229c5U, 0x50a4f4beU, 0x18fee6f6U, 0x8aca4064U,
+	0x1f3b24f1U, 0xe6bd5b08U, 0x3b162dd5U, 0xb07ccc5eU,
+	0x9511847bU, 0x08eae2e6U, 0x75c9bc9bU, 0x54a1f5baU,
+	0xeeb75900U, 0x661d7b88U, 0xebf21905U, 0xd004d43eU,
+	0xca9a5024U, 0x97910679U, 0x2982abc7U, 0xffe31c11U,
+	0x7e037d90U, 0x79c6bf97U, 0xfaa65c14U, 0x0f2f20e1U,
+	0x5b6e35b5U, 0x264d6bc8U, 0x0b2a21e5U, 0xb2fc4e5cU,
+	0x8c4fc362U, 0x675d3a89U, 0xa52d884bU, 0xc3d0132dU,
+	0x44b5f1aaU, 0x45f5b0abU, 0xc7d51229U, 0xaae24844U,
+	0x96d14778U, 0x9451c57aU, 0x05a5a0ebU, 0xb3bc0f5dU,
+	0x576136b9U, 0x3cd3efd2U, 0xa9228b47U, 0x3d93aed3U,
+	0xc290522cU, 0x4f7f30a1U, 0x649df98aU, 0xfbe61d15U,
+	0xf826de16U, 0xf429dd1aU, 0x5f6b34b1U, 0x9394077dU,
+	0x9c5bc772U, 0xc81ad226U, 0x325c6edcU, 0x77493e99U,
+	0xbaf64c54U, 0xf6a95f18U, 0x65ddb88bU, 0x7489fd9aU,
+	0x985ec676U, 0x00e0e0eeU, 0x2f0728c1U, 0x8d0f8263U,
+	0x2188a9cfU, 0xc95a9327U, 0x61d8b98fU, 0x20c8e8ceU,
+	0xd401d53aU, 0x38d6eed6U, 0x1e7b65f0U, 0xd3c4173dU,
+	0xcd5f9223U, 0xd144953fU, 0xa862ca46U, 0xb5398c5bU,
+	0x5debb6b3U, 0xdd4b9633U, 0xb876ce56U, 0x477532a9U,
+	0x0a6a60e4U, 0x59eeb7b7U, 0x40b0f0aeU, 0x04e5e1eaU,
+	0x270d2ac9U, 0x167167f8U, 0xc555902bU, 0x10f4e4feU,
+	0x6892fa86U, 0x8100816fU, 0x51e4b5bfU, 0xc6955328U,
+	0xdbce1535U, 0x4a3a70a4U, 0x7a067c94U, 0x23082bcdU,
+	0xf2ac5e1cU, 0xa7ad0a49U, 0x734c3f9dU, 0x127466fcU,
+	0x58aef6b6U, 0x463573a8U, 0xb9368f57U, 0x8f8f0061U,
+	0xd7c11639U, 0x86c54368U, 0x92d4467cU, 0x62187a8cU,
+	0xbef34d50U, 0xdc0bd732U, 0x0dafa2e3U, 0xdfcb1431U,
+	0xda8e5434U, 0xe3f81b0dU, 0x2cc7ebc2U, 0xcbda1125U,
+	0x37192ed9U, 0x890a8367U, 0x319caddfU, 0x0cefe3e2U,
+	0xeff71801U, 0x173126f9U, 0x9114857fU, 0x991e8777U,
+	0x8ecf4160U, 0x6b523985U, 0x9f9b0471U, 0xd6815738U,
+	0x2d87aac3U, 0x82c0426cU, 0xd284563cU, 0xe178990fU,
+	0x536437bdU, 0xc010d02eU, 0x720c7e9cU, 0xd80ed636U,
+	0x5a2e74b4U, 0xfd639e13U, 0xd541943bU, 0xf7e91e19U,
+	0x6dd7ba83U, 0x14f1e5faU, 0x6098f88eU, 0xe43dd90aU,
+	0x0e6f61e0U, 0x36596fd8U, 0x8505806bU, 0x4cbff3a2U,
+	0x22486accU, 0xaee74940U, 0x63583b8dU, 0xe57d980bU,
+	0x55e1b4bbU, 0xa6ed4b48U, 0xf02cdc1eU, 0x9054c47eU,
+	0xec37db02U, 0xe832da06U, 0x34d9eddaU, 0x1cfbe7f2U,
+	0x6f573881U, 0x3599acdbU, 0xe7fd1a09U, 0x133427fdU,
+	0xb7b90e59U, 0x2e4769c0U, 0xaba20945U, 0xa46dc94aU,
+	0x1dbba6f3U, 0x3e536dd0U, 0x28c2eac6U, 0x5cabf7b2U,
+	0x258da8cbU, 0x19bea7f7U, 0x30dcecdeU, 0x3a566cd4U,
+	0x71ccbd9fU, 0x5e2b75b0U, 0x9d1b8673U, 0x7b463d95U,
+	0xcfdf1021U, 0x6e177980U, 0xb479cd5aU, 0xafa70841U,
+	0xed779a03U, 0x884ac266U, 0x1a7e64f4U, 0x11b4a5ffU,
+	0xbbb60d55U, 0xce9f5120U, 0x8b8a0165U, 0x9b9e0575U,
+	0x7f433c91U, 0xfc23df12U, 0x072522e9U, 0x708cfc9eU,
+	0x69d2bb87U, 0x032023edU, 0x8445c16aU, 0xa068c84eU,
+	0xcc1fd322U, 0xbfb30c51U, 0x9edb4570U, 0x7dc3be93U,
+	0x4e3f71a0U, 0xe038d80eU, 0x423072acU, 0x4b7a31a5U,
+	0x3996afd7U, 0x9ade4474U, 0xac67cb42U, 0xeab25804U,
+	0xb13c8d5fU, 0x2a4268c4U, 0x6c97fb82U, 0xd94e9737U,
+	0xbc73cf52U, 0xe2b85a0cU, 0x24cde9caU, 0xa128894fU,
+	0x066563e8U, 0x76097f98U, 0xbd338e53U, 0x7886fe96U,
+	0xa842ea64U, 0xb11cad7dU, 0x5481d598U, 0x08cac2c4U,
+	0xc030f00cU, 0x74a9ddb8U, 0x070502cbU, 0x2da78ae1U,
+	0xab822967U, 0x29a28be5U, 0xa3882b6fU, 0xf806fe34U,
+	0x5344179fU, 0x1cdbc7d0U, 0x4350138fU, 0xe7dd3a2bU,
+	0x5f4b1493U, 0x6e3759a2U, 0x993ea755U, 0x2a6248e6U,
+	0x5b4e1597U, 0x098a83c5U, 0xe3d83b2fU, 0xfc03ff30U,
+	0x59ce9795U, 0xe2987a2eU, 0xbf932c73U, 0x6b7219a7U,
+	0xd6a1771aU, 0xe952bb25U, 0xebd23927U, 0x9531a459U,
+	0x8120a14dU, 0xfa867c36U, 0xb2dc6e7eU, 0x20e8c8ecU,
+	0xb519ac79U, 0x4c9fd380U, 0x9134a55dU, 0x1a5e44d6U,
+	0x3f330cf3U, 0xa2c86a6eU, 0xdfeb3413U, 0xfe837d32U,
+	0x4495d188U, 0xcc3ff300U, 0x41d0918dU, 0x7a265cb6U,
+	0x60b8d8acU, 0x3db38ef1U, 0x83a0234fU, 0x55c19499U,
+	0xd421f518U, 0xd3e4371fU, 0x5084d49cU, 0xa50da869U,
+	0xf14cbd3dU, 0x8c6fe340U, 0xa108a96dU, 0x18dec6d4U,
+	0x266d4beaU, 0xcd7fb201U, 0x0f0f00c3U, 0x69f29ba5U,
+	0xee977922U, 0xefd73823U, 0x6df79aa1U, 0x00c0c0ccU,
+	0x3cf3cff0U, 0x3e734df2U, 0xaf872863U, 0x199e87d5U,
+	0xfd43be31U, 0x96f1675aU, 0x030003cfU, 0x97b1265bU,
+	0x68b2daa4U, 0xe55db829U, 0xcebf7102U, 0x51c4959dU,
+	0x5204569eU, 0x5e0b5592U, 0xf549bc39U, 0x39b68ff5U,
+	0x36794ffaU, 0x62385aaeU, 0x987ee654U, 0xdd6bb611U,
+	0x10d4c4dcU, 0x5c8bd790U, 0xcfff3003U, 0xdeab7512U,
+	0x327c4efeU, 0xaac26866U, 0x8525a049U, 0x272d0aebU,
+	0x8baa2147U, 0x63781bafU, 0xcbfa3107U, 0x8aea6046U,
+	0x7e235db2U, 0x92f4665eU, 0xb459ed78U, 0x79e69fb5U,
+	0x677d1aabU, 0x7b661db7U, 0x024042ceU, 0x1f1b04d3U,
+	0xf7c93e3bU, 0x77691ebbU, 0x125446deU, 0xed57ba21U,
+	0xa048e86cU, 0xf3cc3f3fU, 0xea927826U, 0xaec76962U,
+	0x8d2fa241U, 0xbc53ef70U, 0x6f7718a3U, 0xbad66c76U,
+	0xc2b0720eU, 0x2b2209e7U, 0xfbc63d37U, 0x6cb7dba0U,
+	0x71ec9dbdU, 0xe018f82cU, 0xd024f41cU, 0x892aa345U,
+	0x588ed694U, 0x0d8f82c1U, 0xd96eb715U, 0xb856ee74U,
+	0xf28c7e3eU, 0xec17fb20U, 0x131407dfU, 0x25ad88e9U,
+	0x7de39eb1U, 0x2ce7cbe0U, 0x38f6cef4U, 0xc83af204U,
+	0x14d1c5d8U, 0x76295fbaU, 0xa78d2a6bU, 0x75e99cb9U,
+	0x70acdcbcU, 0x49da9385U, 0x86e5634aU, 0x61f899adU,
+	0x9d3ba651U, 0x23280befU, 0x9bbe2557U, 0xa6cd6b6aU,
+	0x45d59089U, 0xbd13ae71U, 0x3b360df7U, 0x333c0fffU,
+	0x24edc9e8U, 0xc170b10dU, 0x35b98cf9U, 0x7ca3dfb0U,
+	0x87a5224bU, 0x28e2cae4U, 0x78a6deb4U, 0x4b5a1187U,
+	0xf946bf35U, 0x6a3258a6U, 0xd82ef614U, 0x722c5ebeU,
+	0xf00cfc3cU, 0x5741169bU, 0x7f631cb3U, 0x5dcb9691U,
+	0xc7f5320bU, 0xbed36d72U, 0xcaba7006U, 0x4e1f5182U,
+	0xa44de968U, 0x9c7be750U, 0x2f2708e3U, 0xe69d7b2aU,
+	0x886ae244U, 0x04c5c1c8U, 0xc97ab305U, 0x4f5f1083U,
+	0xffc33c33U, 0x0ccfc3c0U, 0x5a0e5496U, 0x3a764cf6U,
+	0x4615538aU, 0x4210528eU, 0x9efb6552U, 0xb6d96f7aU,
+	0xc575b009U, 0x9fbb2453U, 0x4ddf9281U, 0xb916af75U,
+	0x1d9b86d1U, 0x8465e148U, 0x018081cdU, 0x0e4f41c2U,
+	0xb7992e7bU, 0x9471e558U, 0x82e0624eU, 0xf6897f3aU,
+	0x8faf2043U, 0xb39c2f7fU, 0x9afe6456U, 0x9074e45cU,
+	0xdbee3517U, 0xf409fd38U, 0x37390efbU, 0xd164b51dU,
+	0x65fd98a9U, 0xc435f108U, 0x1e5b45d2U, 0x058580c9U,
+	0x4755128bU, 0x22684aeeU, 0xb05cec7cU, 0xbb962d77U,
+	0x119485ddU, 0x64bdd9a8U, 0x21a889edU, 0x31bc8dfdU,
+	0xd561b419U, 0x5601579aU, 0xad07aa61U, 0xdaae7416U,
+	0xc3f0330fU, 0xa902ab65U, 0x2e6749e2U, 0x0a4a40c6U,
+	0x663d5baaU, 0x159184d9U, 0x34f9cdf8U, 0xd7e1361bU,
+	0xe41df928U, 0x4a1a5086U, 0xe812fa24U, 0xe158b92dU,
+	0x93b4275fU, 0x30fcccfcU, 0x064543caU, 0x4090d08cU,
+	0x1b1e05d7U, 0x8060e04cU, 0xc6b5730aU, 0x736c1fbfU,
+	0x165147daU, 0x489ad284U, 0x8eef6142U, 0x0b0a01c7U,
+	0xac47eb60U, 0xdc2bf710U, 0x171106dbU, 0xd2a4761eU,
+	0x472067e9U, 0x5e7e20f0U, 0xbbe35815U, 0xe7a84f49U,
+	0x2f527d81U, 0x9bcb5035U, 0xe8678f46U, 0xc2c5076cU,
+	0x44e0a4eaU, 0xc6c00668U, 0x4ceaa6e2U, 0x176473b9U,
+	0xbc269a12U, 0xf3b94a5dU, 0xac329e02U, 0x08bfb7a6U,
+	0xb029991eU, 0x8155d42fU, 0x765c2ad8U, 0xc500c56bU,
+	0xb42c981aU, 0xe6e80e48U, 0x0cbab6a2U, 0x136172bdU,
+	0xb6ac1a18U, 0x0dfaf7a3U, 0x50f1a1feU, 0x8410942aU,
+	0x39c3fa97U, 0x063036a8U, 0x04b0b4aaU, 0x7a5329d4U,
+	0x6e422cc0U, 0x15e4f1bbU, 0x5dbee3f3U, 0xcf8a4561U,
+	0x5a7b21f4U, 0xa3fd5e0dU, 0x7e5628d0U, 0xf53cc95bU,
+	0xd051817eU, 0x4daae7e3U, 0x3089b99eU, 0x11e1f0bfU,
+	0xabf75c05U, 0x235d7e8dU, 0xaeb21c00U, 0x9544d13bU,
+	0x8fda5521U, 0xd2d1037cU, 0x6cc2aec2U, 0xbaa31914U,
+	0x3b437895U, 0x3c86ba92U, 0xbfe65911U, 0x4a6f25e4U,
+	0x1e2e30b0U, 0x630d6ecdU, 0x4e6a24e0U, 0xf7bc4b59U,
+	0xc90fc667U, 0x221d3f8cU, 0xe06d8d4eU, 0x86901628U,
+	0x01f5f4afU, 0x00b5b5aeU, 0x8295172cU, 0xefa24d41U,
+	0xd391427dU, 0xd111c07fU, 0x40e5a5eeU, 0xf6fc0a58U,
+	0x122133bcU, 0x7993ead7U, 0xec628e42U, 0x78d3abd6U,
+	0x87d05729U, 0x0a3f35a4U, 0x21ddfc8fU, 0xbea61810U,
+	0xbd66db13U, 0xb169d81fU, 0x1a2b31b4U, 0xd6d40278U,
+	0xd91bc277U, 0x8d5ad723U, 0x771c6bd9U, 0x32093b9cU,
+	0xffb64951U, 0xb3e95a1dU, 0x209dbd8eU, 0x31c9f89fU,
+	0xdd1ec373U, 0x45a0e5ebU, 0x6a472dc4U, 0xc84f8766U,
+	0x64c8accaU, 0x8c1a9622U, 0x2498bc8aU, 0x6588edcbU,
+	0x9141d03fU, 0x7d96ebd3U, 0x5b3b60f5U, 0x96841238U,
+	0x881f9726U, 0x9404903aU, 0xed22cf43U, 0xf079895eU,
+	0x18abb3b6U, 0x980b9336U, 0xfd36cb53U, 0x023537acU,
+	0x4f2a65e1U, 0x1caeb2b2U, 0x05f0f5abU, 0x41a5e4efU,
+	0x624d2fccU, 0x533162fdU, 0x8015952eU, 0x55b4e1fbU,
+	0x2dd2ff83U, 0xc440846aU, 0x14a4b0baU, 0x83d5562dU,
+	0x9e8e1030U, 0x0f7a75a1U, 0x3f467991U, 0x66482ec8U,
+	0xb7ec5b19U, 0xe2ed0f4cU, 0x360c3a98U, 0x573463f9U,
+	0x1deef3b3U, 0x037576adU, 0xfc768a52U, 0xcacf0564U,
+	0x9281133cU, 0xc385466dU, 0xd7944379U, 0x27587f89U,
+	0xfbb34855U, 0x994bd237U, 0x48efa7e6U, 0x9a8b1134U,
+	0x9fce5131U, 0xa6b81e08U, 0x6987eec7U, 0x8e9a1420U,
+	0x72592bdcU, 0xcc4a8662U, 0x74dca8daU, 0x49afe6e7U,
+	0xaab71d04U, 0x527123fcU, 0xd454807aU, 0xdc5e8272U,
+	0xcb8f4465U, 0x2e123c80U, 0xdadb0174U, 0x93c1523dU,
+	0x68c7afc6U, 0xc7804769U, 0x97c45339U, 0xa4389c0aU,
+	0x162432b8U, 0x8550d52bU, 0x374c7b99U, 0x9d4ed333U,
+	0x1f6e71b1U, 0xb8239b16U, 0x9001913eU, 0xb2a91b1cU,
+	0x2897bf86U, 0x51b1e0ffU, 0x25d8fd8bU, 0xa17ddc0fU,
+	0x4b2f64e5U, 0x73196addU, 0xc045856eU, 0x09fff6a7U,
+	0x67086fc9U, 0xeba74c45U, 0x26183e88U, 0xa03d9d0eU,
+	0x10a1b1beU, 0xe3ad4e4dU, 0xb56cd91bU, 0xd514c17bU,
+	0xa977de07U, 0xad72df03U, 0x7199e8dfU, 0x59bbe2f7U,
+	0x2a173d84U, 0x70d9a9deU, 0xa2bd1f0cU, 0x567422f8U,
+	0xf2f90b5cU, 0x6b076cc5U, 0xeee20c40U, 0xe12dcc4fU,
+	0x58fba3f6U, 0x7b1368d5U, 0x6d82efc3U, 0x19ebf2b7U,
+	0x60cdadceU, 0x5cfea2f2U, 0x759ce9dbU, 0x7f1669d1U,
+	0x348cb89aU, 0x1b6b70b5U, 0xd85b8376U, 0x3e063890U,
+	0x8a9f1524U, 0x2b577c85U, 0xf139c85fU, 0xeae70d44U,
+	0xa8379f06U, 0xcd0ac763U, 0x5f3e61f1U, 0x54f4a0faU,
+	0xfef60850U, 0x8bdf5425U, 0xceca0460U, 0xdede0070U,
+	0x3a033994U, 0xb963da17U, 0x426527ecU, 0x35ccf99bU,
+	0x2c92be82U, 0x466026e8U, 0xc105c46fU, 0xe528cd4bU,
+	0x895fd627U, 0xfaf30954U, 0xdb9b4075U, 0x3883bb96U,
+	0x0b7f74a5U, 0xa578dd0bU, 0x077077a9U, 0x0e3a34a0U,
+	0x7cd6aad2U, 0xdf9e4171U, 0xe927ce47U, 0xaff25d01U,
+	0xf47c885aU, 0x6f026dc1U, 0x29d7fe87U, 0x9c0e9232U,
+	0xf933ca57U, 0xa7f85f09U, 0x618deccfU, 0xe4688c4aU,
+	0x432566edU, 0x33497a9dU, 0xf8738b56U, 0x3dc6fb93U,
+	0xb1ed5cd2U, 0xa8b31bcbU, 0x4d2e632eU, 0x11657472U,
+	0xd99f46baU, 0x6d066b0eU, 0x1eaab47dU, 0x34083c57U,
+	0xb22d9fd1U, 0x300d3d53U, 0xba279dd9U, 0xe1a94882U,
+	0x4aeba129U, 0x05747166U, 0x5affa539U, 0xfe728c9dU,
+	0x46e4a225U, 0x7798ef14U, 0x809111e3U, 0x33cdfe50U,
+	0x42e1a321U, 0x10253573U, 0xfa778d99U, 0xe5ac4986U,
+	0x40612123U, 0xfb37cc98U, 0xa63c9ac5U, 0x72ddaf11U,
+	0xcf0ec1acU, 0xf0fd0d93U, 0xf27d8f91U, 0x8c9e12efU,
+	0x988f17fbU, 0xe329ca80U, 0xab73d8c8U, 0x39477e5aU,
+	0xacb61acfU, 0x55306536U, 0x889b13ebU, 0x03f1f260U,
+	0x269cba45U, 0xbb67dcd8U, 0xc64482a5U, 0xe72ccb84U,
+	0x5d3a673eU, 0xd59045b6U, 0x587f273bU, 0x6389ea00U,
+	0x79176e1aU, 0x241c3847U, 0x9a0f95f9U, 0x4c6e222fU,
+	0xcd8e43aeU, 0xca4b81a9U, 0x492b622aU, 0xbca21edfU,
+	0xe8e30b8bU, 0x95c055f6U, 0xb8a71fdbU, 0x01717062U,
+	0x3fc2fd5cU, 0xd4d004b7U, 0x16a0b675U, 0x705d2d13U,
+	0xf738cf94U, 0xf6788e95U, 0x74582c17U, 0x196f767aU,
+	0x255c7946U, 0x27dcfb44U, 0xb6289ed5U, 0x00313163U,
+	0xe4ec0887U, 0x8f5ed1ecU, 0x1aafb579U, 0x8e1e90edU,
+	0x711d6c12U, 0xfcf20e9fU, 0xd710c7b4U, 0x486b232bU,
+	0x4babe028U, 0x47a4e324U, 0xece60a8fU, 0x20193943U,
+	0x2fd6f94cU, 0x7b97ec18U, 0x81d150e2U, 0xc4c400a7U,
+	0x097b726aU, 0x45246126U, 0xd65086b5U, 0xc704c3a4U,
+	0x2bd3f848U, 0xb36dded0U, 0x9c8a16ffU, 0x3e82bc5dU,
+	0x920597f1U, 0x7ad7ad19U, 0xd25587b1U, 0x9345d6f0U,
+	0x678ceb04U, 0x8b5bd0e8U, 0xadf65bceU, 0x60492903U,
+	0x7ed2ac1dU, 0x62c9ab01U, 0x1beff478U, 0x06b4b265U,
+	0xee66888dU, 0x6ec6a80dU, 0x0bfbf068U, 0xf4f80c97U,
+	0xb9e75edaU, 0xea638989U, 0xf33dce90U, 0xb768dfd4U,
+	0x948014f7U, 0xa5fc59c6U, 0x76d8ae15U, 0xa379dac0U,
+	0xdb1fc4b8U, 0x328dbf51U, 0xe2698b81U, 0x75186d16U,
+	0x68432b0bU, 0xf9b74e9aU, 0xc98b42aaU, 0x908515f3U,
+	0x41216022U, 0x14203477U, 0xc0c101a3U, 0xa1f958c2U,
+	0xeb23c888U, 0xf5b84d96U, 0x0abbb169U, 0x3c023e5fU,
+	0x644c2807U, 0x35487d56U, 0x21597842U, 0xd19544b2U,
+	0x0d7e736eU, 0x6f86e90cU, 0xbe229cddU, 0x6c462a0fU,
+	0x69036a0aU, 0x50752533U, 0x9f4ad5fcU, 0x78572f1bU,
+	0x849410e7U, 0x3a87bd59U, 0x821193e1U, 0xbf62dddcU,
+	0x5c7a263fU, 0xa4bc18c7U, 0x2299bb41U, 0x2a93b949U,
+	0x3d427f5eU, 0xd8df07bbU, 0x2c163a4fU, 0x650c6906U,
+	0x9e0a94fdU, 0x314d7c52U, 0x61096802U, 0x52f5a731U,
+	0xe0e90983U, 0x739dee10U, 0xc18140a2U, 0x6b83e808U,
+	0xe9a34a8aU, 0x4eeea02dU, 0x66ccaa05U, 0x44642027U,
+	0xde5a84bdU, 0xa77cdbc4U, 0xd315c6b0U, 0x57b0e734U,
+	0xbde25fdeU, 0x85d451e6U, 0x3688be55U, 0xff32cd9cU,
+	0x91c554f2U, 0x1d6a777eU, 0xd0d505b3U, 0x56f0a635U,
+	0xe66c8a85U, 0x15607576U, 0x43a1e220U, 0x23d9fa40U,
+	0x5fbae53cU, 0x5bbfe438U, 0x8754d3e4U, 0xaf76d9ccU,
+	0xdcda06bfU, 0x861492e5U, 0x54702437U, 0xa0b919c3U,
+	0x04343067U, 0x9dca57feU, 0x182f377bU, 0x17e0f774U,
+	0xae3698cdU, 0x8dde53eeU, 0x9b4fd4f8U, 0xef26c98cU,
+	0x960096f5U, 0xaa3399c9U, 0x8351d2e0U, 0x89db52eaU,
+	0xc24183a1U, 0xeda64b8eU, 0x2e96b84dU, 0xc8cb03abU,
+	0x7c522e1fU, 0xdd9a47beU, 0x07f4f364U, 0x1c2a367fU,
+	0x5efaa43dU, 0x3bc7fc58U, 0xa9f35acaU, 0xa2399bc1U,
+	0x083b336bU, 0x7d126f1eU, 0x38073f5bU, 0x28133b4bU,
+	0xccce02afU, 0x4faee12cU, 0xb4a81cd7U, 0xc301c2a0U,
+	0xda5f85b9U, 0xb0ad1dd3U, 0x37c8ff54U, 0x13e5f670U,
+	0x7f92ed1cU, 0x0c3e326fU, 0x2d567b4eU, 0xce4e80adU,
+	0xfdb24f9eU, 0x53b5e630U, 0xf1bd4c92U, 0xf8f70f9bU,
+	0x8a1b91e9U, 0x29537a4aU, 0x1feaf57cU, 0x593f663aU,
+	0x02b1b361U, 0x99cf56faU, 0xdf1ac5bcU, 0x6ac3a909U,
+	0x0ffef16cU, 0x51356432U, 0x9740d7f4U, 0x12a5b771U,
+	0xb5e85dd6U, 0xc58441a6U, 0x0ebeb06dU, 0xcb0bc0a8U,
+	0x2f6946c8U, 0x363701d1U, 0xd3aa7934U, 0x8fe16e68U,
+	0x471b5ca0U, 0xf3827114U, 0x802eae67U, 0xaa8c264dU,
+	0x2ca985cbU, 0xae892749U, 0x24a387c3U, 0x7f2d5298U,
+	0xd46fbb33U, 0x9bf06b7cU, 0xc47bbf23U, 0x60f69687U,
+	0xd860b83fU, 0xe91cf50eU, 0x1e150bf9U, 0xad49e44aU,
+	0xdc65b93bU, 0x8ea12f69U, 0x64f39783U, 0x7b28539cU,
+	0xdee53b39U, 0x65b3d682U, 0x38b880dfU, 0xec59b50bU,
+	0x518adbb6U, 0x6e791789U, 0x6cf9958bU, 0x121a08f5U,
+	0x060b0de1U, 0x7dadd09aU, 0x35f7c2d2U, 0xa7c36440U,
+	0x323200d5U, 0xcbb47f2cU, 0x161f09f1U, 0x9d75e87aU,
+	0xb818a05fU, 0x25e3c6c2U, 0x58c098bfU, 0x79a8d19eU,
+	0xc3be7d24U, 0x4b145facU, 0xc6fb3d21U, 0xfd0df01aU,
+	0xe7937400U, 0xba98225dU, 0x048b8fe3U, 0xd2ea3835U,
+	0x530a59b4U, 0x54cf9bb3U, 0xd7af7830U, 0x222604c5U,
+	0x76671191U, 0x0b444fecU, 0x262305c1U, 0x9ff56a78U,
+	0xa146e746U, 0x4a541eadU, 0x8824ac6fU, 0xeed93709U,
+	0x69bcd58eU, 0x68fc948fU, 0xeadc360dU, 0x87eb6c60U,
+	0xbbd8635cU, 0xb958e15eU, 0x28ac84cfU, 0x9eb52b79U,
+	0x7a68129dU, 0x11dacbf6U, 0x842baf63U, 0x109a8af7U,
+	0xef997608U, 0x62761485U, 0x4994ddaeU, 0xd6ef3931U,
+	0xd52ffa32U, 0xd920f93eU, 0x72621095U, 0xbe9d2359U,
+	0xb152e356U, 0xe513f602U, 0x1f554af8U, 0x5a401abdU,
+	0x97ff6870U, 0xdba07b3cU, 0x48d49cafU, 0x5980d9beU,
+	0xb557e252U, 0x2de9c4caU, 0x020e0ce5U, 0xa006a647U,
+	0x0c818debU, 0xe453b703U, 0x4cd19dabU, 0x0dc1cceaU,
+	0xf908f11eU, 0x15dfcaf2U, 0x337241d4U, 0xfecd3319U,
+	0xe056b607U, 0xfc4db11bU, 0x856bee62U, 0x9830a87fU,
+	0x70e29297U, 0xf042b217U, 0x957fea72U, 0x6a7c168dU,
+	0x276344c0U, 0x74e79393U, 0x6db9d48aU, 0x29ecc5ceU,
+	0x0a040eedU, 0x3b7843dcU, 0xe85cb40fU, 0x3dfdc0daU,
+	0x459bdea2U, 0xac09a54bU, 0x7ced919bU, 0xeb9c770cU,
+	0xf6c73111U, 0x67335480U, 0x570f58b0U, 0x0e010fe9U,
+	0xdfa57a38U, 0x8aa42e6dU, 0x5e451bb9U, 0x3f7d42d8U,
+	0x75a7d292U, 0x6b3c578cU, 0x943fab73U, 0xa2862445U,
+	0xfac8321dU, 0xabcc674cU, 0xbfdd6258U, 0x4f115ea8U,
+	0x93fa6974U, 0xf102f316U, 0x20a686c7U, 0xf2c23015U,
+	0xf7877010U, 0xcef13f29U, 0x01cecfe6U, 0xe6d33501U,
+	0x1a100afdU, 0xa403a743U, 0x1c9589fbU, 0x21e6c7c6U,
+	0xc2fe3c25U, 0x3a3802ddU, 0xbc1da15bU, 0xb417a353U,
+	0xa3c66544U, 0x465b1da1U, 0xb2922055U, 0xfb88731cU,
+	0x008e8ee7U, 0xafc96648U, 0xff8d7218U, 0xcc71bd2bU,
+	0x7e6d1399U, 0xed19f40aU, 0x5f055ab8U, 0xf507f212U,
+	0x77275090U, 0xd06aba37U, 0xf848b01fU, 0xdae03a3dU,
+	0x40de9ea7U, 0x39f8c1deU, 0x4d91dcaaU, 0xc934fd2eU,
+	0x236645c4U, 0x1b504bfcU, 0xa80ca44fU, 0x61b6d786U,
+	0x0f414ee8U, 0x83ee6d64U, 0x4e511fa9U, 0xc874bc2fU,
+	0x78e8909fU, 0x8be46f6cU, 0xdd25f83aU, 0xbd5de05aU,
+	0xc13eff26U, 0xc53bfe22U, 0x19d0c9feU, 0x31f2c3d6U,
+	0x425e1ca5U, 0x189088ffU, 0xcaf43e2dU, 0x3e3d03d9U,
+	0x9ab02a7dU, 0x034e4de4U, 0x86ab2d61U, 0x8964ed6eU,
+	0x30b282d7U, 0x135a49f4U, 0x05cbcee2U, 0x71a2d396U,
+	0x08848cefU, 0x34b783d3U, 0x1dd5c8faU, 0x175f48f0U,
+	0x5cc599bbU, 0x73225194U, 0xb012a257U, 0x564f19b1U,
+	0xe2d63405U, 0x431e5da4U, 0x9970e97eU, 0x82ae2c65U,
+	0xc07ebe27U, 0xa543e642U, 0x377740d0U, 0x3cbd81dbU,
+	0x96bf2971U, 0xe3967504U, 0xa6832541U, 0xb6972151U,
+	0x524a18b5U, 0xd12afb36U, 0x2a2c06cdU, 0x5d85d8baU,
+	0x44db9fa3U, 0x2e2907c9U, 0xa94ce54eU, 0x8d61ec6aU,
+	0xe116f706U, 0x92ba2875U, 0xb3d26154U, 0x50ca9ab7U,
+	0x63365584U, 0xcd31fc2aU, 0x6f395688U, 0x66731581U,
+	0x149f8bf3U, 0xb7d76050U, 0x816eef66U, 0xc7bb7c20U,
+	0x9c35a97bU, 0x074b4ce0U, 0x419edfa6U, 0xf447b313U,
+	0x917aeb76U, 0xcfb17e28U, 0x09c4cdeeU, 0x8c21ad6bU,
+	0x2b6c47ccU, 0x5b005bbcU, 0x903aaa77U, 0x558fdab2U,
+	0x243f1b95U, 0x3d615c8cU, 0xd8fc2469U, 0x84b73335U,
+	0x4c4d01fdU, 0xf8d42c49U, 0x8b78f33aU, 0xa1da7b10U,
+	0x27ffd896U, 0xa5df7a14U, 0x2ff5da9eU, 0x747b0fc5U,
+	0xdf39e66eU, 0x90a63621U, 0xcf2de27eU, 0x6ba0cbdaU,
+	0xd336e562U, 0xe24aa853U, 0x154356a4U, 0xa61fb917U,
+	0xd733e466U, 0x85f77234U, 0x6fa5cadeU, 0x707e0ec1U,
+	0xd5b36664U, 0x6ee58bdfU, 0x33eedd82U, 0xe70fe856U,
+	0x5adc86ebU, 0x652f4ad4U, 0x67afc8d6U, 0x194c55a8U,
+	0x0d5d50bcU, 0x76fb8dc7U, 0x3ea19f8fU, 0xac95391dU,
+	0x39645d88U, 0xc0e22271U, 0x1d4954acU, 0x9623b527U,
+	0xb34efd02U, 0x2eb59b9fU, 0x5396c5e2U, 0x72fe8cc3U,
+	0xc8e82079U, 0x404202f1U, 0xcdad607cU, 0xf65bad47U,
+	0xecc5295dU, 0xb1ce7f00U, 0x0fddd2beU, 0xd9bc6568U,
+	0x585c04e9U, 0x5f99c6eeU, 0xdcf9256dU, 0x29705998U,
+	0x7d314cccU, 0x001212b1U, 0x2d75589cU, 0x94a33725U,
+	0xaa10ba1bU, 0x410243f0U, 0x8372f132U, 0xe58f6a54U,
+	0x62ea88d3U, 0x63aac9d2U, 0xe18a6b50U, 0x8cbd313dU,
+	0xb08e3e01U, 0xb20ebc03U, 0x23fad992U, 0x95e37624U,
+	0x713e4fc0U, 0x1a8c96abU, 0x8f7df23eU, 0x1bccd7aaU,
+	0xe4cf2b55U, 0x692049d8U, 0x42c280f3U, 0xddb9646cU,
+	0xde79a76fU, 0xd276a463U, 0x79344dc8U, 0xb5cb7e04U,
+	0xba04be0bU, 0xee45ab5fU, 0x140317a5U, 0x511647e0U,
+	0x9ca9352dU, 0xd0f62661U, 0x4382c1f2U, 0x52d684e3U,
+	0xbe01bf0fU, 0x26bf9997U, 0x095851b8U, 0xab50fb1aU,
+	0x07d7d0b6U, 0xef05ea5eU, 0x4787c0f6U, 0x069791b7U,
+	0xf25eac43U, 0x1e8997afU, 0x38241c89U, 0xf59b6e44U,
+	0xeb00eb5aU, 0xf71bec46U, 0x8e3db33fU, 0x9366f522U,
+	0x7bb4cfcaU, 0xfb14ef4aU, 0x9e29b72fU, 0x612a4bd0U,
+	0x2c35199dU, 0x7fb1ceceU, 0x66ef89d7U, 0x22ba9893U,
+	0x015253b0U, 0x302e1e81U, 0xe30ae952U, 0x36ab9d87U,
+	0x4ecd83ffU, 0xa75ff816U, 0x77bbccc6U, 0xe0ca2a51U,
+	0xfd916c4cU, 0x6c6509ddU, 0x5c5905edU, 0x055752b4U,
+	0xd4f32765U, 0x81f27330U, 0x551346e4U, 0x342b1f85U,
+	0x7ef18fcfU, 0x606a0ad1U, 0x9f69f62eU, 0xa9d07918U,
+	0xf19e6f40U, 0xa09a3a11U, 0xb48b3f05U, 0x444703f5U,
+	0x98ac3429U, 0xfa54ae4bU, 0x2bf0db9aU, 0xf9946d48U,
+	0xfcd12d4dU, 0xc5a76274U, 0x0a9892bbU, 0xed85685cU,
+	0x114657a0U, 0xaf55fa1eU, 0x17c3d4a6U, 0x2ab09a9bU,
+	0xc9a86178U, 0x316e5f80U, 0xb74bfc06U, 0xbf41fe0eU,
+	0xa8903819U, 0x4d0d40fcU, 0xb9c47d08U, 0xf0de2e41U,
+	0x0bd8d3baU, 0xa49f3b15U, 0xf4db2f45U, 0xc727e076U,
+	0x753b4ec4U, 0xe64fa957U, 0x545307e5U, 0xfe51af4fU,
+	0x7c710dcdU, 0xdb3ce76aU, 0xf31eed42U, 0xd1b66760U,
+	0x4b88c3faU, 0x32ae9c83U, 0x46c781f7U, 0xc262a073U,
+	0x28301899U, 0x100616a1U, 0xa35af912U, 0x6ae08adbU,
+	0x041713b5U, 0x88b83039U, 0x450742f4U, 0xc322e172U,
+	0x73becdc2U, 0x80b23231U, 0xd673a567U, 0xb60bbd07U,
+	0xca68a27bU, 0xce6da37fU, 0x128694a3U, 0x3aa49e8bU,
+	0x490841f8U, 0x13c6d5a2U, 0xc1a26370U, 0x356b5e84U,
+	0x91e67720U, 0x081810b9U, 0x8dfd703cU, 0x8232b033U,
+	0x3be4df8aU, 0x180c14a9U, 0x0e9d93bfU, 0x7af48ecbU,
+	0x03d2d1b2U, 0x3fe1de8eU, 0x168395a7U, 0x1c0915adU,
+	0x5793c4e6U, 0x78740cc9U, 0xbb44ff0aU, 0x5d1944ecU,
+	0xe9806958U, 0x484800f9U, 0x9226b423U, 0x89f87138U,
+	0xcb28e37aU, 0xae15bb1fU, 0x3c211d8dU, 0x37ebdc86U,
+	0x9de9742cU, 0xe8c02859U, 0xadd5781cU, 0xbdc17c0cU,
+	0x591c45e8U, 0xda7ca66bU, 0x217a5b90U, 0x56d385e7U,
+	0x4f8dc2feU, 0x257f5a94U, 0xa21ab813U, 0x8637b137U,
+	0xea40aa5bU, 0x99ec7528U, 0xb8843c09U, 0x5b9cc7eaU,
+	0x686008d9U, 0xc667a177U, 0x646f0bd5U, 0x6d2548dcU,
+	0x1fc9d6aeU, 0xbc813d0dU, 0x8a38b23bU, 0xcced217dU,
+	0x9763f426U, 0x0c1d11bdU, 0x4ac882fbU, 0xff11ee4eU,
+	0x9a2cb62bU, 0xc4e72375U, 0x029290b3U, 0x8777f036U,
+	0x203a1a91U, 0x505606e1U, 0x9b6cf72aU, 0x5ed987efU,
+	0x3792a52bU, 0x2ecce232U, 0xcb519ad7U, 0x971a8d8bU,
+	0x5fe0bf43U, 0xeb7992f7U, 0x98d54d84U, 0xb277c5aeU,
+	0x34526628U, 0xb672c4aaU, 0x3c586420U, 0x67d6b17bU,
+	0xcc9458d0U, 0x830b889fU, 0xdc805cc0U, 0x780d7564U,
+	0xc09b5bdcU, 0xf1e716edU, 0x06eee81aU, 0xb5b207a9U,
+	0xc49e5ad8U, 0x965acc8aU, 0x7c087460U, 0x63d3b07fU,
+	0xc61ed8daU, 0x7d483561U, 0x2043633cU, 0xf4a256e8U,
+	0x49713855U, 0x7682f46aU, 0x74027668U, 0x0ae1eb16U,
+	0x1ef0ee02U, 0x65563379U, 0x2d0c2131U, 0xbf3887a3U,
+	0x2ac9e336U, 0xd34f9ccfU, 0x0ee4ea12U, 0x858e0b99U,
+	0xa0e343bcU, 0x3d182521U, 0x403b7b5cU, 0x6153327dU,
+	0xdb459ec7U, 0x53efbc4fU, 0xde00dec2U, 0xe5f613f9U,
+	0xff6897e3U, 0xa263c1beU, 0x1c706c00U, 0xca11dbd6U,
+	0x4bf1ba57U, 0x4c347850U, 0xcf549bd3U, 0x3adde726U,
+	0x6e9cf272U, 0x13bfac0fU, 0x3ed8e622U, 0x870e899bU,
+	0xb9bd04a5U, 0x52affd4eU, 0x90df4f8cU, 0xf622d4eaU,
+	0x7147366dU, 0x7007776cU, 0xf227d5eeU, 0x9f108f83U,
+	0xa32380bfU, 0xa1a302bdU, 0x3057672cU, 0x864ec89aU,
+	0x6293f17eU, 0x09212815U, 0x9cd04c80U, 0x08616914U,
+	0xf76295ebU, 0x7a8df766U, 0x516f3e4dU, 0xce14dad2U,
+	0xcdd419d1U, 0xc1db1addU, 0x6a99f376U, 0xa666c0baU,
+	0xa9a900b5U, 0xfde815e1U, 0x07aea91bU, 0x42bbf95eU,
+	0x8f048b93U, 0xc35b98dfU, 0x502f7f4cU, 0x417b3a5dU,
+	0xadac01b1U, 0x35122729U, 0x1af5ef06U, 0xb8fd45a4U,
+	0x147a6e08U, 0xfca854e0U, 0x542a7e48U, 0x153a2f09U,
+	0xe1f312fdU, 0x0d242911U, 0x2b89a237U, 0xe636d0faU,
+	0xf8ad55e4U, 0xe4b652f8U, 0x9d900d81U, 0x80cb4b9cU,
+	0x68197174U, 0xe8b951f4U, 0x8d840991U, 0x7287f56eU,
+	0x3f98a723U, 0x6c1c7070U, 0x75423769U, 0x3117262dU,
+	0x12ffed0eU, 0x2383a03fU, 0xf0a757ecU, 0x25062339U,
+	0x5d603d41U, 0xb4f246a8U, 0x64167278U, 0xf36794efU,
+	0xee3cd2f2U, 0x7fc8b763U, 0x4ff4bb53U, 0x16faec0aU,
+	0xc75e99dbU, 0x925fcd8eU, 0x46bef85aU, 0x2786a13bU,
+	0x6d5c3171U, 0x73c7b46fU, 0x8cc44890U, 0xba7dc7a6U,
+	0xe233d1feU, 0xb33784afU, 0xa72681bbU, 0x57eabd4bU,
+	0x8b018a97U, 0xe9f910f5U, 0x385d6524U, 0xea39d3f6U,
+	0xef7c93f3U, 0xd60adccaU, 0x19352c05U, 0xfe28d6e2U,
+	0x02ebe91eU, 0xbcf844a0U, 0x046e6a18U, 0x391d2425U,
+	0xda05dfc6U, 0x22c3e13eU, 0xa4e642b8U, 0xacec40b0U,
+	0xbb3d86a7U, 0x5ea0fe42U, 0xaa69c3b6U, 0xe37390ffU,
+	0x18756d04U, 0xb73285abU, 0xe77691fbU, 0xd48a5ec8U,
+	0x6696f07aU, 0xf5e217e9U, 0x47feb95bU, 0xedfc11f1U,
+	0x6fdcb373U, 0xc89159d4U, 0xe0b353fcU, 0xc21bd9deU,
+	0x58257d44U, 0x2103223dU, 0x556a3f49U, 0xd1cf1ecdU,
+	0x3b9da627U, 0x03aba81fU, 0xb0f747acU, 0x794d3465U,
+	0x17baad0bU, 0x9b158e87U, 0x56aafc4aU, 0xd08f5fccU,
+	0x6013737cU, 0x931f8c8fU, 0xc5de1bd9U, 0xa5a603b9U,
+	0xd9c51cc5U, 0xddc01dc1U, 0x012b2a1dU, 0x29092035U,
+	0x5aa5ff46U, 0x006b6b1cU, 0xd20fddceU, 0x26c6e03aU,
+	0x824bc99eU, 0x1bb5ae07U, 0x9e50ce82U, 0x919f0e8dU,
+	0x28496134U, 0x0ba1aa17U, 0x1d302d01U, 0x69593075U,
+	0x107f6f0cU, 0x2c4c6030U, 0x052e2b19U, 0x0fa4ab13U,
+	0x443e7a58U, 0x6bd9b277U, 0xa8e941b4U, 0x4eb4fa52U,
+	0xfa2dd7e6U, 0x5be5be47U, 0x818b0a9dU, 0x9a55cf86U,
+	0xd8855dc4U, 0xbdb805a1U, 0x2f8ca333U, 0x24466238U,
+	0x8e44ca92U, 0xfb6d96e7U, 0xbe78c6a2U, 0xae6cc2b2U,
+	0x4ab1fb56U, 0xc9d118d5U, 0x32d7e52eU, 0x457e3b59U,
+	0x5c207c40U, 0x36d2e42aU, 0xb1b706adU, 0x959a0f89U,
+	0xf9ed14e5U, 0x8a41cb96U, 0xab2982b7U, 0x48317954U,
+	0x7bcdb667U, 0xd5ca1fc9U, 0x77c2b56bU, 0x7e88f662U,
+	0x0c646810U, 0xaf2c83b3U, 0x99950c85U, 0xdf409fc3U,
+	0x84ce4a98U, 0x1fb0af03U, 0x59653c45U, 0xecbc50f0U,
+	0x89810895U, 0xd74a9dcbU, 0x113f2e0dU, 0x94da4e88U,
+	0x3397a42fU, 0x43fbb85fU, 0x88c14994U, 0x4d743951U,
+	0x562573fdU, 0x4f7b34e4U, 0xaae64c01U, 0xf6ad5b5dU,
+	0x3e576995U, 0x8ace4421U, 0xf9629b52U, 0xd3c01378U,
+	0x55e5b0feU, 0xd7c5127cU, 0x5defb2f6U, 0x066167adU,
+	0xad238e06U, 0xe2bc5e49U, 0xbd378a16U, 0x19baa3b2U,
+	0xa12c8d0aU, 0x9050c03bU, 0x67593eccU, 0xd405d17fU,
+	0xa5298c0eU, 0xf7ed1a5cU, 0x1dbfa2b6U, 0x026466a9U,
+	0xa7a90e0cU, 0x1cffe3b7U, 0x41f4b5eaU, 0x9515803eU,
+	0x28c6ee83U, 0x173522bcU, 0x15b5a0beU, 0x6b563dc0U,
+	0x7f4738d4U, 0x04e1e5afU, 0x4cbbf7e7U, 0xde8f5175U,
+	0x4b7e35e0U, 0xb2f84a19U, 0x6f533cc4U, 0xe439dd4fU,
+	0xc154956aU, 0x5caff3f7U, 0x218cad8aU, 0x00e4e4abU,
+	0xbaf24811U, 0x32586a99U, 0xbfb70814U, 0x8441c52fU,
+	0x9edf4135U, 0xc3d41768U, 0x7dc7bad6U, 0xaba60d00U,
+	0x2a466c81U, 0x2d83ae86U, 0xaee34d05U, 0x5b6a31f0U,
+	0x0f2b24a4U, 0x72087ad9U, 0x5f6f30f4U, 0xe6b95f4dU,
+	0xd80ad273U, 0x33182b98U, 0xf168995aU, 0x9795023cU,
+	0x10f0e0bbU, 0x11b0a1baU, 0x93900338U, 0xfea75955U,
+	0xc2945669U, 0xc014d46bU, 0x51e0b1faU, 0xe7f91e4cU,
+	0x032427a8U, 0x6896fec3U, 0xfd679a56U, 0x69d6bfc2U,
+	0x96d5433dU, 0x1b3a21b0U, 0x30d8e89bU, 0xafa30c04U,
+	0xac63cf07U, 0xa06ccc0bU, 0x0b2e25a0U, 0xc7d1166cU,
+	0xc81ed663U, 0x9c5fc337U, 0x66197fcdU, 0x230c2f88U,
+	0xeeb35d45U, 0xa2ec4e09U, 0x3198a99aU, 0x20ccec8bU,
+	0xcc1bd767U, 0x54a5f1ffU, 0x7b4239d0U, 0xd94a9372U,
+	0x75cdb8deU, 0x9d1f8236U, 0x359da89eU, 0x748df9dfU,
+	0x8044c42bU, 0x6c93ffc7U, 0x4a3e74e1U, 0x8781062cU,
+	0x991a8332U, 0x8501842eU, 0xfc27db57U, 0xe17c9d4aU,
+	0x09aea7a2U, 0x890e8722U, 0xec33df47U, 0x133023b8U,
+	0x5e2f71f5U, 0x0daba6a6U, 0x14f5e1bfU, 0x50a0f0fbU,
+	0x73483bd8U, 0x423476e9U, 0x9110813aU, 0x44b1f5efU,
+	0x3cd7eb97U, 0xd545907eU, 0x05a1a4aeU, 0x92d04239U,
+	0x8f8b0424U, 0x1e7f61b5U, 0x2e436d85U, 0x774d3adcU,
+	0xa6e94f0dU, 0xf3e81b58U, 0x27092e8cU, 0x463177edU,
+	0x0cebe7a7U, 0x127062b9U, 0xed739e46U, 0xdbca1170U,
+	0x83840728U, 0xd2805279U, 0xc691576dU, 0x365d6b9dU,
+	0xeab65c41U, 0x884ec623U, 0x59eab3f2U, 0x8b8e0520U,
+	0x8ecb4525U, 0xb7bd0a1cU, 0x7882fad3U, 0x9f9f0034U,
+	0x635c3fc8U, 0xdd4f9276U, 0x65d9bcceU, 0x58aaf2f3U,
+	0xbbb20910U, 0x437437e8U, 0xc551946eU, 0xcd5b9666U,
+	0xda8a5071U, 0x3f172894U, 0xcbde1560U, 0x82c44629U,
+	0x79c2bbd2U, 0xd685537dU, 0x86c1472dU, 0xb53d881eU,
+	0x072126acU, 0x9455c13fU, 0x26496f8dU, 0x8c4bc727U,
+	0x0e6b65a5U, 0xa9268f02U, 0x8104852aU, 0xa3ac0f08U,
+	0x3992ab92U, 0x40b4f4ebU, 0x34dde99fU, 0xb078c81bU,
+	0x5a2a70f1U, 0x621c7ec9U, 0xd140917aU, 0x18fae2b3U,
+	0x760d7bddU, 0xfaa25851U, 0x371d2a9cU, 0xb138891aU,
+	0x01a4a5aaU, 0xf2a85a59U, 0xa469cd0fU, 0xc411d56fU,
+	0xb872ca13U, 0xbc77cb17U, 0x609cfccbU, 0x48bef6e3U,
+	0x3b122990U, 0x61dcbdcaU, 0xb3b80b18U, 0x477136ecU,
+	0xe3fc1f48U, 0x7a0278d1U, 0xffe71854U, 0xf028d85bU,
+	0x49feb7e2U, 0x6a167cc1U, 0x7c87fbd7U, 0x08eee6a3U,
+	0x71c8b9daU, 0x4dfbb6e6U, 0x6499fdcfU, 0x6e137dc5U,
+	0x2589ac8eU, 0x0a6e64a1U, 0xc95e9762U, 0x2f032c84U,
+	0x9b9a0130U, 0x3a526891U, 0xe03cdc4bU, 0xfbe21950U,
+	0xb9328b12U, 0xdc0fd377U, 0x4e3b75e5U, 0x45f1b4eeU,
+	0xeff31c44U, 0x9ada4031U, 0xdfcf1074U, 0xcfdb1464U,
+	0x2b062d80U, 0xa866ce03U, 0x536033f8U, 0x24c9ed8fU,
+	0x3d97aa96U, 0x576532fcU, 0xd000d07bU, 0xf42dd95fU,
+	0x985ac233U, 0xebf61d40U, 0xca9e5461U, 0x2986af82U,
+	0x1a7a60b1U, 0xb47dc91fU, 0x167563bdU, 0x1f3f20b4U,
+	0x6dd3bec6U, 0xce9b5565U, 0xf822da53U, 0xbef74915U,
+	0xe5799c4eU, 0x7e0779d5U, 0x38d2ea93U, 0x8d0b8626U,
+	0xe836de43U, 0xb6fd4b1dU, 0x7088f8dbU, 0xf56d985eU,
+	0x522072f9U, 0x224c6e89U, 0xe9769f42U, 0x2cc3ef87U,
+	0xb644f27cU, 0xaf1ab565U, 0x4a87cd80U, 0x16ccdadcU,
+	0xde36e814U, 0x6aafc5a0U, 0x19031ad3U, 0x33a192f9U,
+	0xb584317fU, 0x37a493fdU, 0xbd8e3377U, 0xe600e62cU,
+	0x4d420f87U, 0x02dddfc8U, 0x5d560b97U, 0xf9db2233U,
+	0x414d0c8bU, 0x703141baU, 0x8738bf4dU, 0x346450feU,
+	0x45480d8fU, 0x178c9bddU, 0xfdde2337U, 0xe205e728U,
+	0x47c88f8dU, 0xfc9e6236U, 0xa195346bU, 0x757401bfU,
+	0xc8a76f02U, 0xf754a33dU, 0xf5d4213fU, 0x8b37bc41U,
+	0x9f26b955U, 0xe480642eU, 0xacda7666U, 0x3eeed0f4U,
+	0xab1fb461U, 0x5299cb98U, 0x8f32bd45U, 0x04585cceU,
+	0x213514ebU, 0xbcce7276U, 0xc1ed2c0bU, 0xe085652aU,
+	0x5a93c990U, 0xd239eb18U, 0x5fd68995U, 0x642044aeU,
+	0x7ebec0b4U, 0x23b596e9U, 0x9da63b57U, 0x4bc78c81U,
+	0xca27ed00U, 0xcde22f07U, 0x4e82cc84U, 0xbb0bb071U,
+	0xef4aa525U, 0x9269fb58U, 0xbf0eb175U, 0x06d8deccU,
+	0x386b53f2U, 0xd379aa19U, 0x110918dbU, 0x77f483bdU,
+	0xf091613aU, 0xf1d1203bU, 0x73f182b9U, 0x1ec6d8d4U,
+	0x22f5d7e8U, 0x207555eaU, 0xb181307bU, 0x07989fcdU,
+	0xe345a629U, 0x88f77f42U, 0x1d061bd7U, 0x89b73e43U,
+	0x76b4c2bcU, 0xfb5ba031U, 0xd0b9691aU, 0x4fc28d85U,
+	0x4c024e86U, 0x400d4d8aU, 0xeb4fa421U, 0x27b097edU,
+	0x287f57e2U, 0x7c3e42b6U, 0x8678fe4cU, 0xc36dae09U,
+	0x0ed2dcc4U, 0x428dcf88U, 0xd1f9281bU, 0xc0ad6d0aU,
+	0x2c7a56e6U, 0xb4c4707eU, 0x9b23b851U, 0x392b12f3U,
+	0x95ac395fU, 0x7d7e03b7U, 0xd5fc291fU, 0x94ec785eU,
+	0x602545aaU, 0x8cf27e46U, 0xaa5ff560U, 0x67e087adU,
+	0x797b02b3U, 0x656005afU, 0x1c465ad6U, 0x011d1ccbU,
+	0xe9cf2623U, 0x696f06a3U, 0x0c525ec6U, 0xf351a239U,
+	0xbe4ef074U, 0xedca2727U, 0xf494603eU, 0xb0c1717aU,
+	0x9329ba59U, 0xa255f768U, 0x717100bbU, 0xa4d0746eU,
+	0xdcb66a16U, 0x352411ffU, 0xe5c0252fU, 0x72b1c3b8U,
+	0x6fea85a5U, 0xfe1ee034U, 0xce22ec04U, 0x972cbb5dU,
+	0x4688ce8cU, 0x13899ad9U, 0xc768af0dU, 0xa650f66cU,
+	0xec8a6626U, 0xf211e338U, 0x0d121fc7U, 0x3bab90f1U,
+	0x63e586a9U, 0x32e1d3f8U, 0x26f0d6ecU, 0xd63cea1cU,
+	0x0ad7ddc0U, 0x682f47a2U, 0xb98b3273U, 0x6bef84a1U,
+	0x6eaac4a4U, 0x57dc8b9dU, 0x98e37b52U, 0x7ffe81b5U,
+	0x833dbe49U, 0x3d2e13f7U, 0x85b83d4fU, 0xb8cb7372U,
+	0x5bd38891U, 0xa315b669U, 0x253015efU, 0x2d3a17e7U,
+	0x3aebd1f0U, 0xdf76a915U, 0x2bbf94e1U, 0x62a5c7a8U,
+	0x99a33a53U, 0x36e4d2fcU, 0x66a0c6acU, 0x555c099fU,
+	0xe740a72dU, 0x743440beU, 0xc628ee0cU, 0x6c2a46a6U,
+	0xee0ae424U, 0x49470e83U, 0x616504abU, 0x43cd8e89U,
+	0xd9f32a13U, 0xa0d5756aU, 0xd4bc681eU, 0x5019499aU,
+	0xba4bf170U, 0x827dff48U, 0x312110fbU, 0xf89b6332U,
+	0x966cfa5cU, 0x1ac3d9d0U, 0xd77cab1dU, 0x5159089bU,
+	0xe1c5242bU, 0x12c9dbd8U, 0x44084c8eU, 0x247054eeU,
+	0x58134b92U, 0x5c164a96U, 0x80fd7d4aU, 0xa8df7762U,
+	0xdb73a811U, 0x81bd3c4bU, 0x53d98a99U, 0xa710b76dU,
+	0x039d9ec9U, 0x9a63f950U, 0x1f8699d5U, 0x104959daU,
+	0xa99f3663U, 0x8a77fd40U, 0x9ce67a56U, 0xe88f6722U,
+	0x91a9385bU, 0xad9a3767U, 0x84f87c4eU, 0x8e72fc44U,
+	0xc5e82d0fU, 0xea0fe520U, 0x293f16e3U, 0xcf62ad05U,
+	0x7bfb80b1U, 0xda33e910U, 0x005d5dcaU, 0x1b8398d1U,
+	0x59530a93U, 0x3c6e52f6U, 0xae5af464U, 0xa590356fU,
+	0x0f929dc5U, 0x7abbc1b0U, 0x3fae91f5U, 0x2fba95e5U,
+	0xcb67ac01U, 0x48074f82U, 0xb301b279U, 0xc4a86c0eU,
+	0xddf62b17U, 0xb704b37dU, 0x306151faU, 0x144c58deU,
+	0x783b43b2U, 0x0b979cc1U, 0x2affd5e0U, 0xc9e72e03U,
+	0xfa1be130U, 0x541c489eU, 0xf614e23cU, 0xff5ea135U,
+	0x8db23f47U, 0x2efad4e4U, 0x18435bd2U, 0x5e96c894U,
+	0x05181dcfU, 0x9e66f854U, 0xd8b36b12U, 0x6d6a07a7U,
+	0x08575fc2U, 0x569cca9cU, 0x90e9795aU, 0x150c19dfU,
+	0xb241f378U, 0xc22def08U, 0x09171ec3U, 0xcca26e06U,
+	0x7386f57bU, 0x6ad8b262U, 0x8f45ca87U, 0xd30edddbU,
+	0x1bf4ef13U, 0xaf6dc2a7U, 0xdcc11dd4U, 0xf66395feU,
+	0x70463678U, 0xf26694faU, 0x784c3470U, 0x23c2e12bU,
+	0x88800880U, 0xc71fd8cfU, 0x98940c90U, 0x3c192534U,
+	0x848f0b8cU, 0xb5f346bdU, 0x42fab84aU, 0xf1a657f9U,
+	0x808a0a88U, 0xd24e9cdaU, 0x381c2430U, 0x27c7e02fU,
+	0x820a888aU, 0x395c6531U, 0x6457336cU, 0xb0b606b8U,
+	0x0d656805U, 0x3296a43aU, 0x30162638U, 0x4ef5bb46U,
+	0x5ae4be52U, 0x21426329U, 0x69187161U, 0xfb2cd7f3U,
+	0x6eddb366U, 0x975bcc9fU, 0x4af0ba42U, 0xc19a5bc9U,
+	0xe4f713ecU, 0x790c7571U, 0x042f2b0cU, 0x2547622dU,
+	0x9f51ce97U, 0x17fbec1fU, 0x9a148e92U, 0xa1e243a9U,
+	0xbb7cc7b3U, 0xe67791eeU, 0x58643c50U, 0x8e058b86U,
+	0x0fe5ea07U, 0x08202800U, 0x8b40cb83U, 0x7ec9b776U,
+	0x2a88a222U, 0x57abfc5fU, 0x7accb672U, 0xc31ad9cbU,
+	0xfda954f5U, 0x16bbad1eU, 0xd4cb1fdcU, 0xb23684baU,
+	0x3553663dU, 0x3413273cU, 0xb63385beU, 0xdb04dfd3U,
+	0xe737d0efU, 0xe5b752edU, 0x7443377cU, 0xc25a98caU,
+	0x2687a12eU, 0x4d357845U, 0xd8c41cd0U, 0x4c753944U,
+	0xb376c5bbU, 0x3e99a736U, 0x157b6e1dU, 0x8a008a82U,
+	0x89c04981U, 0x85cf4a8dU, 0x2e8da326U, 0xe27290eaU,
+	0xedbd50e5U, 0xb9fc45b1U, 0x43baf94bU, 0x06afa90eU,
+	0xcb10dbc3U, 0x874fc88fU, 0x143b2f1cU, 0x056f6a0dU,
+	0xe9b851e1U, 0x71067779U, 0x5ee1bf56U, 0xfce915f4U,
+	0x506e3e58U, 0xb8bc04b0U, 0x103e2e18U, 0x512e7f59U,
+	0xa5e742adU, 0x49307941U, 0x6f9df267U, 0xa22280aaU,
+	0xbcb905b4U, 0xa0a202a8U, 0xd9845dd1U, 0xc4df1bccU,
+	0x2c0d2124U, 0xacad01a4U, 0xc99059c1U, 0x3693a53eU,
+	0x7b8cf773U, 0x28082020U, 0x31566739U, 0x7503767dU,
+	0x56ebbd5eU, 0x6797f06fU, 0xb4b307bcU, 0x61127369U,
+	0x19746d11U, 0xf0e616f8U, 0x20022228U, 0xb773c4bfU,
+	0xaa2882a2U, 0x3bdce733U, 0x0be0eb03U, 0x52eebc5aU,
+	0x834ac98bU, 0xd64b9ddeU, 0x02aaa80aU, 0x6392f16bU,
+	0x29486121U, 0x37d3e43fU, 0xc8d018c0U, 0xfe6997f6U,
+	0xa62781aeU, 0xf723d4ffU, 0xe332d1ebU, 0x13feed1bU,
+	0xcf15dac7U, 0xaded40a5U, 0x7c493574U, 0xae2d83a6U,
+	0xab68c3a3U, 0x921e8c9aU, 0x5d217c55U, 0xba3c86b2U,
+	0x46ffb94eU, 0xf8ec14f0U, 0x407a3a48U, 0x7d097475U,
+	0x9e118f96U, 0x66d7b16eU, 0xe0f212e8U, 0xe8f810e0U,
+	0xff29d6f7U, 0x1ab4ae12U, 0xee7d93e6U, 0xa767c0afU,
+	0x5c613d54U, 0xf326d5fbU, 0xa362c1abU, 0x909e0e98U,
+	0x2282a02aU, 0xb1f647b9U, 0x03eae90bU, 0xa9e841a1U,
+	0x2bc8e323U, 0x8c850984U, 0xa4a703acU, 0x860f898eU,
+	0x1c312d14U, 0x6517726dU, 0x117e6f19U, 0x95db4e9dU,
+	0x7f89f677U, 0x47bff84fU, 0xf4e317fcU, 0x3d596435U,
+	0x53aefd5bU, 0xdf01ded7U, 0x12beac1aU, 0x949b0f9cU,
+	0x2407232cU, 0xd70bdcdfU, 0x81ca4b89U, 0xe1b253e9U,
+	0x9dd14c95U, 0x99d44d91U, 0x453f7a4dU, 0x6d1d7065U,
+	0x1eb1af16U, 0x447f3b4cU, 0x961b8d9eU, 0x62d2b06aU,
+	0xc65f99ceU, 0x5fa1fe57U, 0xda449ed2U, 0xd58b5eddU,
+	0x6c5d3164U, 0x4fb5fa47U, 0x59247d51U, 0x2d4d6025U,
+	0x546b3f5cU, 0x68583060U, 0x413a7b49U, 0x4bb0fb43U,
+	0x002a2a08U, 0x2fcde227U, 0xecfd11e4U, 0x0aa0aa02U,
+	0xbe3987b6U, 0x1ff1ee17U, 0xc59f5acdU, 0xde419fd6U,
+	0x9c910d94U, 0xf9ac55f1U, 0x6b98f363U, 0x60523268U,
+	0xca509ac2U, 0xbf79c6b7U, 0xfa6c96f2U, 0xea7892e2U,
+	0x0ea5ab06U, 0x8dc54885U, 0x76c3b57eU, 0x016a6b09U,
+	0x18342c10U, 0x72c6b47aU, 0xf5a356fdU, 0xd18e5fd9U,
+	0xbdf944b5U, 0xce559bc6U, 0xef3dd2e7U, 0x0c252904U,
+	0x3fd9e637U, 0x91de4f99U, 0x33d6e53bU, 0x3a9ca632U,
+	0x48703840U, 0xeb38d3e3U, 0xdd815cd5U, 0x9b54cf93U,
+	0xc0da1ac8U, 0x5ba4ff53U, 0x1d716c15U, 0xa8a800a0U,
+	0xcd9558c5U, 0x935ecd9bU, 0x552b7e5dU, 0xd0ce1ed8U,
+	0x7783f47fU, 0x07efe80fU, 0xccd519c4U, 0x09606901U,
+	0x136576f8U, 0x0a3b31e1U, 0xefa64904U, 0xb3ed5e58U,
+	0x7b176c90U, 0xcf8e4124U, 0xbc229e57U, 0x9680167dU,
+	0x10a5b5fbU, 0x92851779U, 0x18afb7f3U, 0x432162a8U,
+	0xe8638b03U, 0xa7fc5b4cU, 0xf8778f13U, 0x5cfaa6b7U,
+	0xe46c880fU, 0xd510c53eU, 0x22193bc9U, 0x9145d47aU,
+	0xe069890bU, 0xb2ad1f59U, 0x58ffa7b3U, 0x472463acU,
+	0xe2e90b09U, 0x59bfe6b2U, 0x04b4b0efU, 0xd055853bU,
+	0x6d86eb86U, 0x527527b9U, 0x50f5a5bbU, 0x2e1638c5U,
+	0x3a073dd1U, 0x41a1e0aaU, 0x09fbf2e2U, 0x9bcf5470U,
+	0x0e3e30e5U, 0xf7b84f1cU, 0x2a1339c1U, 0xa179d84aU,
+	0x8414906fU, 0x19eff6f2U, 0x64cca88fU, 0x45a4e1aeU,
+	0xffb24d14U, 0x77186f9cU, 0xfaf70d11U, 0xc101c02aU,
+	0xdb9f4430U, 0x8694126dU, 0x3887bfd3U, 0xeee60805U,
+	0x6f066984U, 0x68c3ab83U, 0xeba34800U, 0x1e2a34f5U,
+	0x4a6b21a1U, 0x37487fdcU, 0x1a2f35f1U, 0xa3f95a48U,
+	0x9d4ad776U, 0x76582e9dU, 0xb4289c5fU, 0xd2d50739U,
+	0x55b0e5beU, 0x54f0a4bfU, 0xd6d0063dU, 0xbbe75c50U,
+	0x87d4536cU, 0x8554d16eU, 0x14a0b4ffU, 0xa2b91b49U,
+	0x466422adU, 0x2dd6fbc6U, 0xb8279f53U, 0x2c96bac7U,
+	0xd3954638U, 0x5e7a24b5U, 0x7598ed9eU, 0xeae30901U,
+	0xe923ca02U, 0xe52cc90eU, 0x4e6e20a5U, 0x82911369U,
+	0x8d5ed366U, 0xd91fc632U, 0x23597ac8U, 0x664c2a8dU,
+	0xabf35840U, 0xe7ac4b0cU, 0x74d8ac9fU, 0x658ce98eU,
+	0x895bd262U, 0x11e5f4faU, 0x3e023cd5U, 0x9c0a9677U,
+	0x308dbddbU, 0xd85f8733U, 0x70ddad9bU, 0x31cdfcdaU,
+	0xc504c12eU, 0x29d3fac2U, 0x0f7e71e4U, 0xc2c10329U,
+	0xdc5a8637U, 0xc041812bU, 0xb967de52U, 0xa43c984fU,
+	0x4ceea2a7U, 0xcc4e8227U, 0xa973da42U, 0x567026bdU,
+	0x1b6f74f0U, 0x48eba3a3U, 0x51b5e4baU, 0x15e0f5feU,
+	0x36083eddU, 0x077473ecU, 0xd450843fU, 0x01f1f0eaU,
+	0x7997ee92U, 0x9005957bU, 0x40e1a1abU, 0xd790473cU,
+	0xcacb0121U, 0x5b3f64b0U, 0x6b036880U, 0x320d3fd9U,
+	0xe3a94a08U, 0xb6a81e5dU, 0x62492b89U, 0x037172e8U,
+	0x49abe2a2U, 0x573067bcU, 0xa8339b43U, 0x9e8a1475U,
+	0xc6c4022dU, 0x97c0577cU, 0x83d15268U, 0x731d6e98U,
+	0xaff65944U, 0xcd0ec326U, 0x1caab6f7U, 0xcece0025U,
+	0xcb8b4020U, 0xf2fd0f19U, 0x3dc2ffd6U, 0xdadf0531U,
+	0x261c3acdU, 0x980f9773U, 0x2099b9cbU, 0x1deaf7f6U,
+	0xfef20c15U, 0x063432edU, 0x8011916bU, 0x881b9363U,
+	0x9fca5574U, 0x7a572d91U, 0x8e9e1065U, 0xc784432cU,
+	0x3c82bed7U, 0x93c55678U, 0xc3814228U, 0xf07d8d1bU,
+	0x426123a9U, 0xd115c43aU, 0x63096a88U, 0xc90bc222U,
+	0x4b2b60a0U, 0xec668a07U, 0xc444802fU, 0xe6ec0a0dU,
+	0x7cd2ae97U, 0x05f4f1eeU, 0x719dec9aU, 0xf538cd1eU,
+	0x1f6a75f4U, 0x275c7bccU, 0x9400947fU, 0x5dbae7b6U,
+	0x334d7ed8U, 0xbfe25d54U, 0x725d2f99U, 0xf4788c1fU,
+	0x44e4a0afU, 0xb7e85f5cU, 0xe129c80aU, 0x8151d06aU,
+	0xfd32cf16U, 0xf937ce12U, 0x25dcf9ceU, 0x0dfef3e6U,
+	0x7e522c95U, 0x249cb8cfU, 0xf6f80e1dU, 0x023133e9U,
+	0xa6bc1a4dU, 0x3f427dd4U, 0xbaa71d51U, 0xb568dd5eU,
+	0x0cbeb2e7U, 0x2f5679c4U, 0x39c7fed2U, 0x4daee3a6U,
+	0x3488bcdfU, 0x08bbb3e3U, 0x21d9f8caU, 0x2b5378c0U,
+	0x60c9a98bU, 0x4f2e61a4U, 0x8c1e9267U, 0x6a432981U,
+	0xdeda0435U, 0x7f126d94U, 0xa57cd94eU, 0xbea21c55U,
+	0xfc728e17U, 0x994fd672U, 0x0b7b70e0U, 0x00b1b1ebU,
+	0xaab31941U, 0xdf9a4534U, 0x9a8f1571U, 0x8a9b1161U,
+	0x6e462885U, 0xed26cb06U, 0x162036fdU, 0x6189e88aU,
+	0x78d7af93U, 0x122537f9U, 0x9540d57eU, 0xb16ddc5aU,
+	0xdd1ac736U, 0xaeb61845U, 0x8fde5164U, 0x6cc6aa87U,
+	0x5f3a65b4U, 0xf13dcc1aU, 0x533566b8U, 0x5a7f25b1U,
+	0x2893bbc3U, 0x8bdb5060U, 0xbd62df56U, 0xfbb74c10U,
+	0xa039994bU, 0x3b477cd0U, 0x7d92ef96U, 0xc84b8323U,
+	0xad76db46U, 0xf3bd4e18U, 0x35c8fddeU, 0xb02d9d5bU,
+	0x176077fcU, 0x670c6b8cU, 0xac369a47U, 0x6983ea82U,
+	0x9a19830dU, 0x8347c414U, 0x66dabcf1U, 0x3a91abadU,
+	0xf26b9965U, 0x46f2b4d1U, 0x355e6ba2U, 0x1ffce388U,
+	0x99d9400eU, 0x1bf9e28cU, 0x91d34206U, 0xca5d975dU,
+	0x611f7ef6U, 0x2e80aeb9U, 0x710b7ae6U, 0xd5865342U,
+	0x6d107dfaU, 0x5c6c30cbU, 0xab65ce3cU, 0x1839218fU,
+	0x69157cfeU, 0x3bd1eaacU, 0xd1835246U, 0xce589659U,
+	0x6b95fefcU, 0xd0c31347U, 0x8dc8451aU, 0x592970ceU,
+	0xe4fa1e73U, 0xdb09d24cU, 0xd989504eU, 0xa76acd30U,
+	0xb37bc824U, 0xc8dd155fU, 0x80870717U, 0x12b3a185U,
+	0x8742c510U, 0x7ec4bae9U, 0xa36fcc34U, 0x28052dbfU,
+	0x0d68659aU, 0x90930307U, 0xedb05d7aU, 0xccd8145bU,
+	0x76ceb8e1U, 0xfe649a69U, 0x738bf8e4U, 0x487d35dfU,
+	0x52e3b1c5U, 0x0fe8e798U, 0xb1fb4a26U, 0x679afdf0U,
+	0xe67a9c71U, 0xe1bf5e76U, 0x62dfbdf5U, 0x9756c100U,
+	0xc317d454U, 0xbe348a29U, 0x9353c004U, 0x2a85afbdU,
+	0x14362283U, 0xff24db68U, 0x3d5469aaU, 0x5ba9f2ccU,
+	0xdccc104bU, 0xdd8c514aU, 0x5facf3c8U, 0x329ba9a5U,
+	0x0ea8a699U, 0x0c28249bU, 0x9ddc410aU, 0x2bc5eebcU,
+	0xcf18d758U, 0xa4aa0e33U, 0x315b6aa6U, 0xa5ea4f32U,
+	0x5ae9b3cdU, 0xd706d140U, 0xfce4186bU, 0x639ffcf4U,
+	0x605f3ff7U, 0x6c503cfbU, 0xc712d550U, 0x0bede69cU,
+	0x04222693U, 0x506333c7U, 0xaa258f3dU, 0xef30df78U,
+	0x228fadb5U, 0x6ed0bef9U, 0xfda4596aU, 0xecf01c7bU,
+	0x00272797U, 0x9899010fU, 0xb77ec920U, 0x15766382U,
+	0xb9f1482eU, 0x512372c6U, 0xf9a1586eU, 0xb8b1092fU,
+	0x4c7834dbU, 0xa0af0f37U, 0x86028411U, 0x4bbdf6dcU,
+	0x552673c2U, 0x493d74deU, 0x301b2ba7U, 0x2d406dbaU,
+	0xc5925752U, 0x453277d2U, 0x200f2fb7U, 0xdf0cd348U,
+	0x92138105U, 0xc1975656U, 0xd8c9114fU, 0x9c9c000bU,
+	0xbf74cb28U, 0x8e088619U, 0x5d2c71caU, 0x888d051fU,
+	0xf0eb1b67U, 0x1979608eU, 0xc99d545eU, 0x5eecb2c9U,
+	0x43b7f4d4U, 0xd2439145U, 0xe27f9d75U, 0xbb71ca2cU,
+	0x6ad5bffdU, 0x3fd4eba8U, 0xeb35de7cU, 0x8a0d871dU,
+	0xc0d71757U, 0xde4c9249U, 0x214f6eb6U, 0x17f6e180U,
+	0x4fb8f7d8U, 0x1ebca289U, 0x0aada79dU, 0xfa619b6dU,
+	0x268aacb1U, 0x447236d3U, 0x95d64302U, 0x47b2f5d0U,
+	0x42f7b5d5U, 0x7b81faecU, 0xb4be0a23U, 0x53a3f0c4U,
+	0xaf60cf38U, 0x11736286U, 0xa9e54c3eU, 0x94960203U,
+	0x778ef9e0U, 0x8f48c718U, 0x096d649eU, 0x01676696U,
+	0x16b6a081U, 0xf32bd864U, 0x07e2e590U, 0x4ef8b6d9U,
+	0xb5fe4b22U, 0x1ab9a38dU, 0x4afdb7ddU, 0x790178eeU,
+	0xcb1dd65cU, 0x586931cfU, 0xea759f7dU, 0x407737d7U,
+	0xc2579555U, 0x651a7ff2U, 0x4d3875daU, 0x6f90fff8U,
+	0xf5ae5b62U, 0x8c88041bU, 0xf8e1196fU, 0x7c4438ebU,
+	0x96168001U, 0xae208e39U, 0x1d7c618aU, 0xd4c61243U,
+	0xba318b2dU, 0x369ea8a1U, 0xfb21da6cU, 0x7d0479eaU,
+	0xcd98555aU, 0x3e94aaa9U, 0x68553dffU, 0x082d259fU,
+	0x744e3ae3U, 0x704b3be7U, 0xaca00c3bU, 0x84820613U,
+	0xf72ed960U, 0xade04d3aU, 0x7f84fbe8U, 0x8b4dc61cU,
+	0x2fc0efb8U, 0xb63e8821U, 0x33dbe8a4U, 0x3c1428abU,
+	0x85c24712U, 0xa62a8c31U, 0xb0bb0b27U, 0xc4d21653U,
+	0xbdf4492aU, 0x81c74616U, 0xa8a50d3fU, 0xa22f8d35U,
+	0xe9b55c7eU, 0xc6529451U, 0x05626792U, 0xe33fdc74U,
+	0x57a6f1c0U, 0xf66e9861U, 0x2c002cbbU, 0x37dee9a0U,
+	0x750e7be2U, 0x10332387U, 0x82078515U, 0x89cd441eU,
+	0x23cfecb4U, 0x56e6b0c1U, 0x13f3e084U, 0x03e7e494U,
+	0xe73add70U, 0x645a3ef3U, 0x9f5cc308U, 0xe8f51d7fU,
+	0xf1ab5a66U, 0x9b59c20cU, 0x1c3c208bU, 0x381129afU,
+	0x546632c3U, 0x27caedb0U, 0x06a2a491U, 0xe5ba5f72U,
+	0xd6469041U, 0x784139efU, 0xda49934dU, 0xd303d044U,
+	0xa1ef4e36U, 0x02a7a595U, 0x341e2aa3U, 0x72cbb9e5U,
+	0x29456cbeU, 0xb23b8925U, 0xf4ee1a63U, 0x413776d6U,
+	0x240a2eb3U, 0x7ac1bbedU, 0xbcb4082bU, 0x395168aeU,
+	0x9e1c8209U, 0xee709e79U, 0x254a6fb2U, 0xe0ff1f77U,
+	0xdb0cd759U, 0xc2529040U, 0x27cfe8a5U, 0x7b84fff9U,
+	0xb37ecd31U, 0x07e7e085U, 0x744b3ff6U, 0x5ee9b7dcU,
+	0xd8cc145aU, 0x5aecb6d8U, 0xd0c61652U, 0x8b48c309U,
+	0x200a2aa2U, 0x6f95faedU, 0x301e2eb2U, 0x94930716U,
+	0x2c0529aeU, 0x1d79649fU, 0xea709a68U, 0x592c75dbU,
+	0x280028aaU, 0x7ac4bef8U, 0x90960612U, 0x8f4dc20dU,
+	0x2a80aaa8U, 0x91d64713U, 0xccdd114eU, 0x183c249aU,
+	0xa5ef4a27U, 0x9a1c8618U, 0x989c041aU, 0xe67f9964U,
+	0xf26e9c70U, 0x89c8410bU, 0xc1925343U, 0x53a6f5d1U,
+	0xc6579144U, 0x3fd1eebdU, 0xe27a9860U, 0x691079ebU,
+	0x4c7d31ceU, 0xd1865753U, 0xaca5092eU, 0x8dcd400fU,
+	0x37dbecb5U, 0xbf71ce3dU, 0x329eacb0U, 0x0968618bU,
+	0x13f6e591U, 0x4efdb3ccU, 0xf0ee1e72U, 0x268fa9a4U,
+	0xa76fc825U, 0xa0aa0a22U, 0x23cae9a1U, 0xd6439554U,
+	0x82028000U, 0xff21de7dU, 0xd2469450U, 0x6b90fbe9U,
+	0x552376d7U, 0xbe318f3cU, 0x7c413dfeU, 0x1abca698U,
+	0x9dd9441fU, 0x9c99051eU, 0x1eb9a79cU, 0x738efdf1U,
+	0x4fbdf2cdU, 0x4d3d70cfU, 0xdcc9155eU, 0x6ad0bae8U,
+	0x8e0d830cU, 0xe5bf5a67U, 0x704e3ef2U, 0xe4ff1b66U,
+	0x1bfce799U, 0x96138514U, 0xbdf14c3fU, 0x228aa8a0U,
+	0x214a6ba3U, 0x2d4568afU, 0x86078104U, 0x4af8b2c8U,
+	0x453772c7U, 0x11766793U, 0xeb30db69U, 0xae258b2cU,
+	0x639af9e1U, 0x2fc5eaadU, 0xbcb10d3eU, 0xade5482fU,
+	0x413273c3U, 0xd98c555bU, 0xf66b9d74U, 0x546337d6U,
+	0xf8e41c7aU, 0x10362692U, 0xb8b40c3aU, 0xf9a45d7bU,
+	0x0d6d608fU, 0xe1ba5b63U, 0xc717d045U, 0x0aa8a288U,
+	0x14332796U, 0x0828208aU, 0x710e7ff3U, 0x6c5539eeU,
+	0x84870306U, 0x04272386U, 0x611a7be3U, 0x9e19871cU,
+	0xd306d551U, 0x80820202U, 0x99dc451bU, 0xdd89545fU,
+	0xfe619f7cU, 0xcf1dd24dU, 0x1c39259eU, 0xc998514bU,
+	0xb1fe4f33U, 0x586c34daU, 0x8888000aU, 0x1ff9e69dU,
+	0x02a2a080U, 0x9356c511U, 0xa36ac921U, 0xfa649e78U,
+	0x2bc0eba9U, 0x7ec1bffcU, 0xaa208a28U, 0xcb18d349U,
+	0x81c24303U, 0x9f59c61dU, 0x605a3ae2U, 0x56e3b5d4U,
+	0x0eada38cU, 0x5fa9f6ddU, 0x4bb8f3c9U, 0xbb74cf39U,
+	0x679ff8e5U, 0x05676287U, 0xd4c31756U, 0x06a7a184U,
+	0x03e2e181U, 0x3a94aeb8U, 0xf5ab5e77U, 0x12b6a490U,
+	0xee759b6cU, 0x506636d2U, 0xe8f0186aU, 0xd5835657U,
+	0x369badb4U, 0xce5d934cU, 0x487830caU, 0x407232c2U,
+	0x57a3f4d5U, 0xb23e8c30U, 0x46f7b1c4U, 0x0fede28dU,
+	0xf4eb1f76U, 0x5bacf7d9U, 0x0be8e389U, 0x38142cbaU,
+	0x8a088208U, 0x197c659bU, 0xab60cb29U, 0x01626383U,
+	0x8342c101U, 0x240f2ba6U, 0x0c2d218eU, 0x2e85abacU,
+	0xb4bb0f36U, 0xcd9d504fU, 0xb9f44d3bU, 0x3d516cbfU,
+	0xd703d455U, 0xef35da6dU, 0x5c6935deU, 0x95d34617U,
+	0xfb24df79U, 0x778bfcf5U, 0xba348e38U, 0x3c112dbeU,
+	0x8c8d010eU, 0x7f81fefdU, 0x294069abU, 0x493871cbU,
+	0x355b6eb7U, 0x315e6fb3U, 0xedb5586fU, 0xc5975247U,
+	0xb63b8d34U, 0xecf5196eU, 0x3e91afbcU, 0xca589248U,
+	0x6ed5bbecU, 0xf72bdc75U, 0x72cebcf0U, 0x7d017cffU,
+	0xc4d71346U, 0xe73fd865U, 0xf1ae5f73U, 0x85c74207U,
+	0xfce11d7eU, 0xc0d21242U, 0xe9b0596bU, 0xe33ad961U,
+	0xa8a0082aU, 0x8747c005U, 0x447733c6U, 0xa22a8820U,
+	0x16b3a594U, 0xb77bcc35U, 0x6d1578efU, 0x76cbbdf4U,
+	0x341b2fb6U, 0x512677d3U, 0xc312d141U, 0xc8d8104aU,
+	0x62dab8e0U, 0x17f3e495U, 0x52e6b4d0U, 0x42f2b0c0U,
+	0xa62f8924U, 0x254f6aa7U, 0xde49975cU, 0xa9e0492bU,
+	0xb0be0e32U, 0xda4c9658U, 0x5d2974dfU, 0x79047dfbU,
+	0x15736697U, 0x66dfb9e4U, 0x47b7f0c5U, 0xa4af0b26U,
+	0x9753c415U, 0x39546dbbU, 0x9b5cc719U, 0x92168410U,
+	0xe0fa1a62U, 0x43b2f1c1U, 0x750b7ef7U, 0x33deedb1U,
+	0x685038eaU, 0xf32edd71U, 0xb5fb4e37U, 0x00222282U,
+	0x651f7ae7U, 0x3bd4efb9U, 0xfda15c7fU, 0x78443cfaU,
+	0xdf09d65dU, 0xaf65ca2dU, 0x645f3be6U, 0xa1ea4b23U,
+	0xf852aa24U, 0xe10ced3dU, 0x049195d8U, 0x58da8284U,
+	0x9020b04cU, 0x24b99df8U, 0x5715428bU, 0x7db7caa1U,
+	0xfb926927U, 0x79b2cba5U, 0xf3986b2fU, 0xa816be74U,
+	0x035457dfU, 0x4ccb8790U, 0x134053cfU, 0xb7cd7a6bU,
+	0x0f5b54d3U, 0x3e2719e2U, 0xc92ee715U, 0x7a7208a6U,
+	0x0b5e55d7U, 0x599ac385U, 0xb3c87b6fU, 0xac13bf70U,
+	0x09ded7d5U, 0xb2883a6eU, 0xef836c33U, 0x3b6259e7U,
+	0x86b1375aU, 0xb942fb65U, 0xbbc27967U, 0xc521e419U,
+	0xd130e10dU, 0xaa963c76U, 0xe2cc2e3eU, 0x70f888acU,
+	0xe509ec39U, 0x1c8f93c0U, 0xc124e51dU, 0x4a4e0496U,
+	0x6f234cb3U, 0xf2d82a2eU, 0x8ffb7453U, 0xae933d72U,
+	0x148591c8U, 0x9c2fb340U, 0x11c0d1cdU, 0x2a361cf6U,
+	0x30a898ecU, 0x6da3ceb1U, 0xd3b0630fU, 0x05d1d4d9U,
+	0x8431b558U, 0x83f4775fU, 0x009494dcU, 0xf51de829U,
+	0xa15cfd7dU, 0xdc7fa300U, 0xf118e92dU, 0x48ce8694U,
+	0x767d0baaU, 0x9d6ff241U, 0x5f1f4083U, 0x39e2dbe5U,
+	0xbe873962U, 0xbfc77863U, 0x3de7dae1U, 0x50d0808cU,
+	0x6ce38fb0U, 0x6e630db2U, 0xff976823U, 0x498ec795U,
+	0xad53fe71U, 0xc6e1271aU, 0x5310438fU, 0xc7a1661bU,
+	0x38a29ae4U, 0xb54df869U, 0x9eaf3142U, 0x01d4d5ddU,
+	0x021416deU, 0x0e1b15d2U, 0xa559fc79U, 0x69a6cfb5U,
+	0x66690fbaU, 0x32281aeeU, 0xc86ea614U, 0x8d7bf651U,
+	0x40c4849cU, 0x0c9b97d0U, 0x9fef7043U, 0x8ebb3552U,
+	0x626c0ebeU, 0xfad22826U, 0xd535e009U, 0x773d4aabU,
+	0xdbba6107U, 0x33685befU, 0x9bea7147U, 0xdafa2006U,
+	0x2e331df2U, 0xc2e4261eU, 0xe449ad38U, 0x29f6dff5U,
+	0x376d5aebU, 0x2b765df7U, 0x5250028eU, 0x4f0b4493U,
+	0xa7d97e7bU, 0x27795efbU, 0x4244069eU, 0xbd47fa61U,
+	0xf058a82cU, 0xa3dc7f7fU, 0xba823866U, 0xfed72922U,
+	0xdd3fe201U, 0xec43af30U, 0x3f6758e3U, 0xeac62c36U,
+	0x92a0324eU, 0x7b3249a7U, 0xabd67d77U, 0x3ca79be0U,
+	0x21fcddfdU, 0xb008b86cU, 0x8034b45cU, 0xd93ae305U,
+	0x089e96d4U, 0x5d9fc281U, 0x897ef755U, 0xe846ae34U,
+	0xa29c3e7eU, 0xbc07bb60U, 0x4304479fU, 0x75bdc8a9U,
+	0x2df3def1U, 0x7cf78ba0U, 0x68e68eb4U, 0x982ab244U,
+	0x44c18598U, 0x26391ffaU, 0xf79d6a2bU, 0x25f9dcf9U,
+	0x20bc9cfcU, 0x19cad3c5U, 0xd6f5230aU, 0x31e8d9edU,
+	0xcd2be611U, 0x73384bafU, 0xcbae6517U, 0xf6dd2b2aU,
+	0x15c5d0c9U, 0xed03ee31U, 0x6b264db7U, 0x632c4fbfU,
+	0x74fd89a8U, 0x9160f14dU, 0x65a9ccb9U, 0x2cb39ff0U,
+	0xd7b5620bU, 0x78f28aa4U, 0x28b69ef4U, 0x1b4a51c7U,
+	0xa956ff75U, 0x3a2218e6U, 0x883eb654U, 0x223c1efeU,
+	0xa01cbc7cU, 0x075156dbU, 0x2f735cf3U, 0x0ddbd6d1U,
+	0x97e5724bU, 0xeec32d32U, 0x9aaa3046U, 0x1e0f11c2U,
+	0xf45da928U, 0xcc6ba710U, 0x7f3748a3U, 0xb68d3b6aU,
+	0xd87aa204U, 0x54d58188U, 0x996af345U, 0x1f4f50c3U,
+	0xafd37c73U, 0x5cdf8380U, 0x0a1e14d6U, 0x6a660cb6U,
+	0x160513caU, 0x120012ceU, 0xceeb2512U, 0xe6c92f3aU,
+	0x9565f049U, 0xcfab6413U, 0x1dcfd2c1U, 0xe906ef35U,
+	0x4d8bc691U, 0xd475a108U, 0x5190c18dU, 0x5e5f0182U,
+	0xe7896e3bU, 0xc461a518U, 0xd2f0220eU, 0xa6993f7aU,
+	0xdfbf6003U, 0xe38c6f3fU, 0xcaee2416U, 0xc064a41cU,
+	0x8bfe7557U, 0xa419bd78U, 0x67294ebbU, 0x8174f55dU,
+	0x35edd8e9U, 0x9425b148U, 0x4e4b0592U, 0x5595c089U,
+	0x174552cbU, 0x72780aaeU, 0xe04cac3cU, 0xeb866d37U,
+	0x4184c59dU, 0x34ad99e8U, 0x71b8c9adU, 0x61accdbdU,
+	0x8571f459U, 0x061117daU, 0xfd17ea21U, 0x8abe3456U,
+	0x93e0734fU, 0xf912eb25U, 0x7e7709a2U, 0x5a5a0086U,
+	0x362d1beaU, 0x4581c499U, 0x64e98db8U, 0x87f1765bU,
+	0xb40db968U, 0x1a0a10c6U, 0xb802ba64U, 0xb148f96dU,
+	0xc3a4671fU, 0x60ec8cbcU, 0x5655038aU, 0x108090ccU,
+	0x4b0e4597U, 0xd070a00cU, 0x96a5334aU, 0x237c5fffU,
+	0x4641079aU, 0x188a92c4U, 0xdeff2102U, 0x5b1a4187U,
+	0xfc57ab20U, 0x8c3bb750U, 0x4701469bU, 0x82b4365eU,
+	0x9f188709U, 0x8646c010U, 0x63dbb8f5U, 0x3f90afa9U,
+	0xf76a9d61U, 0x43f3b0d5U, 0x305f6fa6U, 0x1afde78cU,
+	0x9cd8440aU, 0x1ef8e688U, 0x94d24602U, 0xcf5c9359U,
+	0x641e7af2U, 0x2b81aabdU, 0x740a7ee2U, 0xd0875746U,
+	0x681179feU, 0x596d34cfU, 0xae64ca38U, 0x1d38258bU,
+	0x6c1478faU, 0x3ed0eea8U, 0xd4825642U, 0xcb59925dU,
+	0x6e94faf8U, 0xd5c21743U, 0x88c9411eU, 0x5c2874caU,
+	0xe1fb1a77U, 0xde08d648U, 0xdc88544aU, 0xa26bc934U,
+	0xb67acc20U, 0xcddc115bU, 0x85860313U, 0x17b2a581U,
+	0x8243c114U, 0x7bc5beedU, 0xa66ec830U, 0x2d0429bbU,
+	0x0869619eU, 0x95920703U, 0xe8b1597eU, 0xc9d9105fU,
+	0x73cfbce5U, 0xfb659e6dU, 0x768afce0U, 0x4d7c31dbU,
+	0x57e2b5c1U, 0x0ae9e39cU, 0xb4fa4e22U, 0x629bf9f4U,
+	0xe37b9875U, 0xe4be5a72U, 0x67deb9f1U, 0x9257c504U,
+	0xc616d050U, 0xbb358e2dU, 0x9652c400U, 0x2f84abb9U,
+	0x11372687U, 0xfa25df6cU, 0x38556daeU, 0x5ea8f6c8U,
+	0xd9cd144fU, 0xd88d554eU, 0x5aadf7ccU, 0x379aada1U,
+	0x0ba9a29dU, 0x0929209fU, 0x98dd450eU, 0x2ec4eab8U,
+	0xca19d35cU, 0xa1ab0a37U, 0x345a6ea2U, 0xa0eb4b36U,
+	0x5fe8b7c9U, 0xd207d544U, 0xf9e51c6fU, 0x669ef8f0U,
+	0x655e3bf3U, 0x695138ffU, 0xc213d154U, 0x0eece298U,
+	0x01232297U, 0x556237c3U, 0xaf248b39U, 0xea31db7cU,
+	0x278ea9b1U, 0x6bd1bafdU, 0xf8a55d6eU, 0xe9f1187fU,
+	0x05262393U, 0x9d98050bU, 0xb27fcd24U, 0x10776786U,
+	0xbcf04c2aU, 0x542276c2U, 0xfca05c6aU, 0xbdb00d2bU,
+	0x497930dfU, 0xa5ae0b33U, 0x83038015U, 0x4ebcf2d8U,
+	0x502777c6U, 0x4c3c70daU, 0x351a2fa3U, 0x284169beU,
+	0xc0935356U, 0x403373d6U, 0x250e2bb3U, 0xda0dd74cU,
+	0x97128501U, 0xc4965252U, 0xddc8154bU, 0x999d040fU,
+	0xba75cf2cU, 0x8b09821dU, 0x582d75ceU, 0x8d8c011bU,
+	0xf5ea1f63U, 0x1c78648aU, 0xcc9c505aU, 0x5bedb6cdU,
+	0x46b6f0d0U, 0xd7429541U, 0xe77e9971U, 0xbe70ce28U,
+	0x6fd4bbf9U, 0x3ad5efacU, 0xee34da78U, 0x8f0c8319U,
+	0xc5d61353U, 0xdb4d964dU, 0x244e6ab2U, 0x12f7e584U,
+	0x4ab9f3dcU, 0x1bbda68dU, 0x0faca399U, 0xff609f69U,
+	0x238ba8b5U, 0x417332d7U, 0x90d74706U, 0x42b3f1d4U,
+	0x47f6b1d1U, 0x7e80fee8U, 0xb1bf0e27U, 0x56a2f4c0U,
+	0xaa61cb3cU, 0x14726682U, 0xace4483aU, 0x91970607U,
+	0x728ffde4U, 0x8a49c31cU, 0x0c6c609aU, 0x04666292U,
+	0x13b7a485U, 0xf62adc60U, 0x02e3e194U, 0x4bf9b2ddU,
+	0xb0ff4f26U, 0x1fb8a789U, 0x4ffcb3d9U, 0x7c007ceaU,
+	0xce1cd258U, 0x5d6835cbU, 0xef749b79U, 0x457633d3U,
+	0xc7569151U, 0x601b7bf6U, 0x483971deU, 0x6a91fbfcU,
+	0xf0af5f66U, 0x8989001fU, 0xfde01d6bU, 0x79453cefU,
+	0x93178405U, 0xab218a3dU, 0x187d658eU, 0xd1c71647U,
+	0xbf308f29U, 0x339faca5U, 0xfe20de68U, 0x78057deeU,
+	0xc899515eU, 0x3b95aeadU, 0x6d5439fbU, 0x0d2c219bU,
+	0x714f3ee7U, 0x754a3fe3U, 0xa9a1083fU, 0x81830217U,
+	0xf22fdd64U, 0xa8e1493eU, 0x7a85ffecU, 0x8e4cc218U,
+	0x2ac1ebbcU, 0xb33f8c25U, 0x36daeca0U, 0x39152cafU,
+	0x80c34316U, 0xa32b8835U, 0xb5ba0f23U, 0xc1d31257U,
+	0xb8f54d2eU, 0x84c64212U, 0xada4093bU, 0xa72e8931U,
+	0xecb4587aU, 0xc3539055U, 0x00636396U, 0xe63ed870U,
+	0x52a7f5c4U, 0xf36f9c65U, 0x290128bfU, 0x32dfeda4U,
+	0x700f7fe6U, 0x15322783U, 0x87068111U, 0x8ccc401aU,
+	0x26cee8b0U, 0x53e7b4c5U, 0x16f2e480U, 0x06e6e090U,
+	0xe23bd974U, 0x615b3af7U, 0x9a5dc70cU, 0xedf4197bU,
+	0xf4aa5e62U, 0x9e58c608U, 0x193d248fU, 0x3d102dabU,
+	0x516736c7U, 0x22cbe9b4U, 0x03a3a095U, 0xe0bb5b76U,
+	0xd3479445U, 0x7d403debU, 0xdf489749U, 0xd602d440U,
+	0xa4ee4a32U, 0x07a6a191U, 0x311f2ea7U, 0x77cabde1U,
+	0x2c4468baU, 0xb73a8d21U, 0xf1ef1e67U, 0x443672d2U,
+	0x210b2ab7U, 0x7fc0bfe9U, 0xb9b50c2fU, 0x3c506caaU,
+	0x9b1d860dU, 0xeb719a7dU, 0x204b6bb6U, 0xe5fe1b73U,
+	0x49773eb0U, 0x502979a9U, 0xb5b4014cU, 0xe9ff1610U,
+	0x210524d8U, 0x959c096cU, 0xe630d61fU, 0xcc925e35U,
+	0x4ab7fdb3U, 0xc8975f31U, 0x42bdffbbU, 0x19332ae0U,
+	0xb271c34bU, 0xfdee1304U, 0xa265c75bU, 0x06e8eeffU,
+	0xbe7ec047U, 0x8f028d76U, 0x780b7381U, 0xcb579c32U,
+	0xba7bc143U, 0xe8bf5711U, 0x02edeffbU, 0x1d362be4U,
+	0xb8fb4341U, 0x03adaefaU, 0x5ea6f8a7U, 0x8a47cd73U,
+	0x3794a3ceU, 0x08676ff1U, 0x0ae7edf3U, 0x7404708dU,
+	0x60157599U, 0x1bb3a8e2U, 0x53e9baaaU, 0xc1dd1c38U,
+	0x542c78adU, 0xadaa0754U, 0x70017189U, 0xfb6b9002U,
+	0xde06d827U, 0x43fdbebaU, 0x3edee0c7U, 0x1fb6a9e6U,
+	0xa5a0055cU, 0x2d0a27d4U, 0xa0e54559U, 0x9b138862U,
+	0x818d0c78U, 0xdc865a25U, 0x6295f79bU, 0xb4f4404dU,
+	0x351421ccU, 0x32d1e3cbU, 0xb1b10048U, 0x44387cbdU,
+	0x107969e9U, 0x6d5a3794U, 0x403d7db9U, 0xf9eb1200U,
+	0xc7589f3eU, 0x2c4a66d5U, 0xee3ad417U, 0x88c74f71U,
+	0x0fa2adf6U, 0x0ee2ecf7U, 0x8cc24e75U, 0xe1f51418U,
+	0xddc61b24U, 0xdf469926U, 0x4eb2fcb7U, 0xf8ab5301U,
+	0x1c766ae5U, 0x77c4b38eU, 0xe235d71bU, 0x7684f28fU,
+	0x89870e70U, 0x04686cfdU, 0x2f8aa5d6U, 0xb0f14149U,
+	0xb331824aU, 0xbf3e8146U, 0x147c68edU, 0xd8835b21U,
+	0xd74c9b2eU, 0x830d8e7aU, 0x794b3280U, 0x3c5e62c5U,
+	0xf1e11008U, 0xbdbe0344U, 0x2ecae4d7U, 0x3f9ea1c6U,
+	0xd3499a2aU, 0x4bf7bcb2U, 0x6410749dU, 0xc618de3fU,
+	0x6a9ff593U, 0x824dcf7bU, 0x2acfe5d3U, 0x6bdfb492U,
+	0x9f168966U, 0x73c1b28aU, 0x556c39acU, 0x98d34b61U,
+	0x8648ce7fU, 0x9a53c963U, 0xe375961aU, 0xfe2ed007U,
+	0x16fceaefU, 0x965cca6fU, 0xf361920aU, 0x0c626ef5U,
+	0x417d3cb8U, 0x12f9ebebU, 0x0ba7acf2U, 0x4ff2bdb6U,
+	0x6c1a7695U, 0x5d663ba4U, 0x8e42cc77U, 0x5be3b8a2U,
+	0x2385a6daU, 0xca17dd33U, 0x1af3e9e3U, 0x8d820f74U,
+	0x90d94969U, 0x012d2cf8U, 0x311120c8U, 0x681f7791U,
+	0xb9bb0240U, 0xecba5615U, 0x385b63c1U, 0x59633aa0U,
+	0x13b9aaeaU, 0x0d222ff4U, 0xf221d30bU, 0xc4985c3dU,
+	0x9cd64a65U, 0xcdd21f34U, 0xd9c31a20U, 0x290f26d0U,
+	0xf5e4110cU, 0x971c8b6eU, 0x46b8febfU, 0x94dc486dU,
+	0x91990868U, 0xa8ef4751U, 0x67d0b79eU, 0x80cd4d79U,
+	0x7c0e7285U, 0xc21ddf3bU, 0x7a8bf183U, 0x47f8bfbeU,
+	0xa4e0445dU, 0x5c267aa5U, 0xda03d923U, 0xd209db2bU,
+	0xc5d81d3cU, 0x204565d9U, 0xd48c582dU, 0x9d960b64U,
+	0x6690f69fU, 0xc9d71e30U, 0x99930a60U, 0xaa6fc553U,
+	0x18736be1U, 0x8b078c72U, 0x391b22c0U, 0x93198a6aU,
+	0x113928e8U, 0xb674c24fU, 0x9e56c867U, 0xbcfe4245U,
+	0x26c0e6dfU, 0x5fe6b9a6U, 0x2b8fa4d2U, 0xaf2a8556U,
+	0x45783dbcU, 0x7d4e3384U, 0xce12dc37U, 0x07a8affeU,
+	0x695f3690U, 0xe5f0151cU, 0x284f67d1U, 0xae6ac457U,
+	0x1ef6e8e7U, 0xedfa1714U, 0xbb3b8042U, 0xdb439822U,
+	0xa720875eU, 0xa325865aU, 0x7fceb186U, 0x57ecbbaeU,
+	0x244064ddU, 0x7e8ef087U, 0xacea4655U, 0x58237ba1U,
+	0xfcae5205U, 0x6550359cU, 0xe0b55519U, 0xef7a9516U,
+	0x56acfaafU, 0x7544318cU, 0x63d5b69aU, 0x17bcabeeU,
+	0x6e9af497U, 0x52a9fbabU, 0x7bcbb082U, 0x71413088U,
+	0x3adbe1c3U, 0x153c29ecU, 0xd60cda2fU, 0x305161c9U,
+	0x84c84c7dU, 0x250025dcU, 0xff6e9106U, 0xe4b0541dU,
+	0xa660c65fU, 0xc35d9e3aU, 0x516938a8U, 0x5aa3f9a3U,
+	0xf0a15109U, 0x85880d7cU, 0xc09d5d39U, 0xd0895929U,
+	0x345460cdU, 0xb734834eU, 0x4c327eb5U, 0x3b9ba0c2U,
+	0x22c5e7dbU, 0x48377fb1U, 0xcf529d36U, 0xeb7f9412U,
+	0x87088f7eU, 0xf4a4500dU, 0xd5cc192cU, 0x36d4e2cfU,
+	0x05282dfcU, 0xab2f8452U, 0x09272ef0U, 0x006d6df9U,
+	0x7281f38bU, 0xd1c91828U, 0xe770971eU, 0xa1a50458U,
+	0xfa2bd103U, 0x61553498U, 0x2780a7deU, 0x9259cb6bU,
+	0xf764930eU, 0xa9af0650U, 0x6fdab596U, 0xea3fd513U,
+	0x4d723fb4U, 0x3d1e23c4U, 0xf624d20fU, 0x3391a2caU,
+	0xfafa008eU, 0xe3a44797U, 0x06393f72U, 0x5a72282eU,
+	0x92881ae6U, 0x26113752U, 0x55bde821U, 0x7f1f600bU,
+	0xf93ac38dU, 0x7b1a610fU, 0xf130c185U, 0xaabe14deU,
+	0x01fcfd75U, 0x4e632d3aU, 0x11e8f965U, 0xb565d0c1U,
+	0x0df3fe79U, 0x3c8fb348U, 0xcb864dbfU, 0x78daa20cU,
+	0x09f6ff7dU, 0x5b32692fU, 0xb160d1c5U, 0xaebb15daU,
+	0x0b767d7fU, 0xb02090c4U, 0xed2bc699U, 0x39caf34dU,
+	0x84199df0U, 0xbbea51cfU, 0xb96ad3cdU, 0xc7894eb3U,
+	0xd3984ba7U, 0xa83e96dcU, 0xe0648494U, 0x72502206U,
+	0xe7a14693U, 0x1e27396aU, 0xc38c4fb7U, 0x48e6ae3cU,
+	0x6d8be619U, 0xf0708084U, 0x8d53def9U, 0xac3b97d8U,
+	0x162d3b62U, 0x9e8719eaU, 0x13687b67U, 0x289eb65cU,
+	0x32003246U, 0x6f0b641bU, 0xd118c9a5U, 0x07797e73U,
+	0x86991ff2U, 0x815cddf5U, 0x023c3e76U, 0xf7b54283U,
+	0xa3f457d7U, 0xded709aaU, 0xf3b04387U, 0x4a662c3eU,
+	0x74d5a100U, 0x9fc758ebU, 0x5db7ea29U, 0x3b4a714fU,
+	0xbc2f93c8U, 0xbd6fd2c9U, 0x3f4f704bU, 0x52782a26U,
+	0x6e4b251aU, 0x6ccba718U, 0xfd3fc289U, 0x4b266d3fU,
+	0xaffb54dbU, 0xc4498db0U, 0x51b8e925U, 0xc509ccb1U,
+	0x3a0a304eU, 0xb7e552c3U, 0x9c079be8U, 0x037c7f77U,
+	0x00bcbc74U, 0x0cb3bf78U, 0xa7f156d3U, 0x6b0e651fU,
+	0x64c1a510U, 0x3080b044U, 0xcac60cbeU, 0x8fd35cfbU,
+	0x426c2e36U, 0x0e333d7aU, 0x9d47dae9U, 0x8c139ff8U,
+	0x60c4a414U, 0xf87a828cU, 0xd79d4aa3U, 0x7595e001U,
+	0xd912cbadU, 0x31c0f145U, 0x9942dbedU, 0xd8528aacU,
+	0x2c9bb758U, 0xc04c8cb4U, 0xe6e10792U, 0x2b5e755fU,
+	0x35c5f041U, 0x29def75dU, 0x50f8a824U, 0x4da3ee39U,
+	0xa571d4d1U, 0x25d1f451U, 0x40ecac34U, 0xbfef50cbU,
+	0xf2f00286U, 0xa174d5d5U, 0xb82a92ccU, 0xfc7f8388U,
+	0xdf9748abU, 0xeeeb059aU, 0x3dcff249U, 0xe86e869cU,
+	0x900898e4U, 0x799ae30dU, 0xa97ed7ddU, 0x3e0f314aU,
+	0x23547757U, 0xb2a012c6U, 0x829c1ef6U, 0xdb9249afU,
+	0x0a363c7eU, 0x5f37682bU, 0x8bd65dffU, 0xeaee049eU,
+	0xa03494d4U, 0xbeaf11caU, 0x41aced35U, 0x77156203U,
+	0x2f5b745bU, 0x7e5f210aU, 0x6a4e241eU, 0x9a8218eeU,
+	0x46692f32U, 0x2491b550U, 0xf535c081U, 0x27517653U,
+	0x22143656U, 0x1b62796fU, 0xd45d89a0U, 0x33407347U,
+	0xcf834cbbU, 0x7190e105U, 0xc906cfbdU, 0xf4758180U,
+	0x176d7a63U, 0xefab449bU, 0x698ee71dU, 0x6184e515U,
+	0x76552302U, 0x93c85be7U, 0x67016613U, 0x2e1b355aU,
+	0xd51dc8a1U, 0x7a5a200eU, 0x2a1e345eU, 0x19e2fb6dU,
+	0xabfe55dfU, 0x388ab24cU, 0x8a961cfeU, 0x2094b454U,
+	0xa2b416d6U, 0x05f9fc71U, 0x2ddbf659U, 0x0f737c7bU,
+	0x954dd8e1U, 0xec6b8798U, 0x98029aecU, 0x1ca7bb68U,
+	0xf6f50382U, 0xcec30dbaU, 0x7d9fe209U, 0xb42591c0U,
+	0xdad208aeU, 0x567d2b22U, 0x9bc259efU, 0x1de7fa69U,
+	0xad7bd6d9U, 0x5e77292aU, 0x08b6be7cU, 0x68cea61cU,
+	0x14adb960U, 0x10a8b864U, 0xcc438fb8U, 0xe4618590U,
+	0x97cd5ae3U, 0xcd03ceb9U, 0x1f67786bU, 0xebae459fU,
+	0x4f236c3bU, 0xd6dd0ba2U, 0x53386b27U, 0x5cf7ab28U,
+	0xe521c491U, 0xc6c90fb2U, 0xd05888a4U, 0xa43195d0U,
+	0xdd17caa9U, 0xe124c595U, 0xc8468ebcU, 0xc2cc0eb6U,
+	0x8956dffdU, 0xa6b117d2U, 0x6581e411U, 0x83dc5ff7U,
+	0x37457243U, 0x968d1be2U, 0x4ce3af38U, 0x573d6a23U,
+	0x15edf861U, 0x70d0a004U, 0xe2e40696U, 0xe92ec79dU,
+	0x432c6f37U, 0x36053342U, 0x73106307U, 0x63046717U,
+	0x87d95ef3U, 0x04b9bd70U, 0xffbf408bU, 0x88169efcU,
+	0x9148d9e5U, 0xfbba418fU, 0x7cdfa308U, 0x58f2aa2cU,
+	0x3485b140U, 0x47296e33U, 0x66412712U, 0x8559dcf1U,
+	0xb6a513c2U, 0x18a2ba6cU, 0xbaaa10ceU, 0xb3e053c7U,
+	0xc10ccdb5U, 0x62442616U, 0x54fda920U, 0x12283a66U,
+	0x49a6ef3dU, 0xd2d80aa6U, 0x940d99e0U, 0x21d4f555U,
+	0x44e9ad30U, 0x1a22386eU, 0xdc578ba8U, 0x59b2eb2dU,
+	0xfeff018aU, 0x8e931dfaU, 0x45a9ec31U, 0x801c9cf4U,
+	0xe803eb65U, 0xf15dac7cU, 0x14c0d499U, 0x488bc3c5U,
+	0x8071f10dU, 0x34e8dcb9U, 0x474403caU, 0x6de68be0U,
+	0xebc32866U, 0x69e38ae4U, 0xe3c92a6eU, 0xb847ff35U,
+	0x1305169eU, 0x5c9ac6d1U, 0x0311128eU, 0xa79c3b2aU,
+	0x1f0a1592U, 0x2e7658a3U, 0xd97fa654U, 0x6a2349e7U,
+	0x1b0f1496U, 0x49cb82c4U, 0xa3993a2eU, 0xbc42fe31U,
+	0x198f9694U, 0xa2d97b2fU, 0xffd22d72U, 0x2b3318a6U,
+	0x96e0761bU, 0xa913ba24U, 0xab933826U, 0xd570a558U,
+	0xc161a04cU, 0xbac77d37U, 0xf29d6f7fU, 0x60a9c9edU,
+	0xf558ad78U, 0x0cded281U, 0xd175a45cU, 0x5a1f45d7U,
+	0x7f720df2U, 0xe2896b6fU, 0x9faa3512U, 0xbec27c33U,
+	0x04d4d089U, 0x8c7ef201U, 0x0191908cU, 0x3a675db7U,
+	0x20f9d9adU, 0x7df28ff0U, 0xc3e1224eU, 0x15809598U,
+	0x9460f419U, 0x93a5361eU, 0x10c5d59dU, 0xe54ca968U,
+	0xb10dbc3cU, 0xcc2ee241U, 0xe149a86cU, 0x589fc7d5U,
+	0x662c4aebU, 0x8d3eb300U, 0x4f4e01c2U, 0x29b39aa4U,
+	0xaed67823U, 0xaf963922U, 0x2db69ba0U, 0x4081c1cdU,
+	0x7cb2cef1U, 0x7e324cf3U, 0xefc62962U, 0x59df86d4U,
+	0xbd02bf30U, 0xd6b0665bU, 0x434102ceU, 0xd7f0275aU,
+	0x28f3dba5U, 0xa51cb928U, 0x8efe7003U, 0x1185949cU,
+	0x1245579fU, 0x1e4a5493U, 0xb508bd38U, 0x79f78ef4U,
+	0x76384efbU, 0x22795bafU, 0xd83fe755U, 0x9d2ab710U,
+	0x5095c5ddU, 0x1ccad691U, 0x8fbe3102U, 0x9eea7413U,
+	0x723d4fffU, 0xea836967U, 0xc564a148U, 0x676c0beaU,
+	0xcbeb2046U, 0x23391aaeU, 0x8bbb3006U, 0xcaab6147U,
+	0x3e625cb3U, 0xd2b5675fU, 0xf418ec79U, 0x39a79eb4U,
+	0x273c1baaU, 0x3b271cb6U, 0x420143cfU, 0x5f5a05d2U,
+	0xb7883f3aU, 0x37281fbaU, 0x521547dfU, 0xad16bb20U,
+	0xe009e96dU, 0xb38d3e3eU, 0xaad37927U, 0xee866863U,
+	0xcd6ea340U, 0xfc12ee71U, 0x2f3619a2U, 0xfa976d77U,
+	0x82f1730fU, 0x6b6308e6U, 0xbb873c36U, 0x2cf6daa1U,
+	0x31ad9cbcU, 0xa059f92dU, 0x9065f51dU, 0xc96ba244U,
+	0x18cfd795U, 0x4dce83c0U, 0x992fb614U, 0xf817ef75U,
+	0xb2cd7f3fU, 0xac56fa21U, 0x535506deU, 0x65ec89e8U,
+	0x3da29fb0U, 0x6ca6cae1U, 0x78b7cff5U, 0x887bf305U,
+	0x5490c4d9U, 0x36685ebbU, 0xe7cc2b6aU, 0x35a89db8U,
+	0x30edddbdU, 0x099b9284U, 0xc6a4624bU, 0x21b998acU,
+	0xdd7aa750U, 0x63690aeeU, 0xdbff2456U, 0xe68c6a6bU,
+	0x05949188U, 0xfd52af70U, 0x7b770cf6U, 0x737d0efeU,
+	0x64acc8e9U, 0x8131b00cU, 0x75f88df8U, 0x3ce2deb1U,
+	0xc7e4234aU, 0x68a3cbe5U, 0x38e7dfb5U, 0x0b1b1086U,
+	0xb907be34U, 0x2a7359a7U, 0x986ff715U, 0x326d5fbfU,
+	0xb04dfd3dU, 0x1700179aU, 0x3f221db2U, 0x1d8a9790U,
+	0x87b4330aU, 0xfe926c73U, 0x8afb7107U, 0x0e5e5083U,
+	0xe40ce869U, 0xdc3ae651U, 0x6f6609e2U, 0xa6dc7a2bU,
+	0xc82be345U, 0x4484c0c9U, 0x893bb204U, 0x0f1e1182U,
+	0xbf823d32U, 0x4c8ec2c1U, 0x1a4f5597U, 0x7a374df7U,
+	0x0654528bU, 0x0251538fU, 0xdeba6453U, 0xf6986e7bU,
+	0x8534b108U, 0xdffa2552U, 0x0d9e9380U, 0xf957ae74U,
+	0x5dda87d0U, 0xc424e049U, 0x41c180ccU, 0x4e0e40c3U,
+	0xf7d82f7aU, 0xd430e459U, 0xc2a1634fU, 0xb6c87e3bU,
+	0xcfee2142U, 0xf3dd2e7eU, 0xdabf6557U, 0xd035e55dU,
+	0x9baf3416U, 0xb448fc39U, 0x77780ffaU, 0x9125b41cU,
+	0x25bc99a8U, 0x8474f009U, 0x5e1a44d3U, 0x45c481c8U,
+	0x0714138aU, 0x62294befU, 0xf01ded7dU, 0xfbd72c76U,
+	0x51d584dcU, 0x24fcd8a9U, 0x61e988ecU, 0x71fd8cfcU,
+	0x9520b518U, 0x1640569bU, 0xed46ab60U, 0x9aef7517U,
+	0x83b1320eU, 0xe943aa64U, 0x6e2648e3U, 0x4a0b41c7U,
+	0x267c5aabU, 0x55d085d8U, 0x74b8ccf9U, 0x97a0371aU,
+	0xa45cf829U, 0x0a5b5187U, 0xa853fb25U, 0xa119b82cU,
+	0xd3f5265eU, 0x70bdcdfdU, 0x460442cbU, 0x00d1d18dU,
+	0x5b5f04d6U, 0xc021e14dU, 0x86f4720bU, 0x332d1ebeU,
+	0x561046dbU, 0x08dbd385U, 0xceae6043U, 0x4b4b00c6U,
+	0xec06ea61U, 0x9c6af611U, 0x575007daU, 0x92e5771fU,
+	0x98b129a7U, 0x81ef6ebeU, 0x6472165bU, 0x38390107U,
+	0xf0c333cfU, 0x445a1e7bU, 0x37f6c108U, 0x1d544922U,
+	0x9b71eaa4U, 0x19514826U, 0x937be8acU, 0xc8f53df7U,
+	0x63b7d45cU, 0x2c280413U, 0x73a3d04cU, 0xd72ef9e8U,
+	0x6fb8d750U, 0x5ec49a61U, 0xa9cd6496U, 0x1a918b25U,
+	0x6bbdd654U, 0x39794006U, 0xd32bf8ecU, 0xccf03cf3U,
+	0x693d5456U, 0xd26bb9edU, 0x8f60efb0U, 0x5b81da64U,
+	0xe652b4d9U, 0xd9a178e6U, 0xdb21fae4U, 0xa5c2679aU,
+	0xb1d3628eU, 0xca75bff5U, 0x822fadbdU, 0x101b0b2fU,
+	0x85ea6fbaU, 0x7c6c1043U, 0xa1c7669eU, 0x2aad8715U,
+	0x0fc0cf30U, 0x923ba9adU, 0xef18f7d0U, 0xce70bef1U,
+	0x7466124bU, 0xfccc30c3U, 0x7123524eU, 0x4ad59f75U,
+	0x504b1b6fU, 0x0d404d32U, 0xb353e08cU, 0x6532575aU,
+	0xe4d236dbU, 0xe317f4dcU, 0x6077175fU, 0x95fe6baaU,
+	0xc1bf7efeU, 0xbc9c2083U, 0x91fb6aaeU, 0x282d0517U,
+	0x169e8829U, 0xfd8c71c2U, 0x3ffcc300U, 0x59015866U,
+	0xde64bae1U, 0xdf24fbe0U, 0x5d045962U, 0x3033030fU,
+	0x0c000c33U, 0x0e808e31U, 0x9f74eba0U, 0x296d4416U,
+	0xcdb07df2U, 0xa602a499U, 0x33f3c00cU, 0xa742e598U,
+	0x58411967U, 0xd5ae7beaU, 0xfe4cb2c1U, 0x6137565eU,
+	0x62f7955dU, 0x6ef89651U, 0xc5ba7ffaU, 0x09454c36U,
+	0x068a8c39U, 0x52cb996dU, 0xa88d2597U, 0xed9875d2U,
+	0x2027071fU, 0x6c781453U, 0xff0cf3c0U, 0xee58b6d1U,
+	0x028f8d3dU, 0x9a31aba5U, 0xb5d6638aU, 0x17dec928U,
+	0xbb59e284U, 0x538bd86cU, 0xfb09f2c4U, 0xba19a385U,
+	0x4ed09e71U, 0xa207a59dU, 0x84aa2ebbU, 0x49155c76U,
+	0x578ed968U, 0x4b95de74U, 0x32b3810dU, 0x2fe8c710U,
+	0xc73afdf8U, 0x479add78U, 0x22a7851dU, 0xdda479e2U,
+	0x90bb2bafU, 0xc33ffcfcU, 0xda61bbe5U, 0x9e34aaa1U,
+	0xbddc6182U, 0x8ca02cb3U, 0x5f84db60U, 0x8a25afb5U,
+	0xf243b1cdU, 0x1bd1ca24U, 0xcb35fef4U, 0x5c441863U,
+	0x411f5e7eU, 0xd0eb3befU, 0xe0d737dfU, 0xb9d96086U,
+	0x687d1557U, 0x3d7c4102U, 0xe99d74d6U, 0x88a52db7U,
+	0xc27fbdfdU, 0xdce438e3U, 0x23e7c41cU, 0x155e4b2aU,
+	0x4d105d72U, 0x1c140823U, 0x08050d37U, 0xf8c931c7U,
+	0x2422061bU, 0x46da9c79U, 0x977ee9a8U, 0x451a5f7aU,
+	0x405f1f7fU, 0x79295046U, 0xb616a089U, 0x510b5a6eU,
+	0xadc86592U, 0x13dbc82cU, 0xab4de694U, 0x963ea8a9U,
+	0x7526534aU, 0x8de06db2U, 0x0bc5ce34U, 0x03cfcc3cU,
+	0x141e0a2bU, 0xf18372ceU, 0x054a4f3aU, 0x4c501c73U,
+	0xb756e188U, 0x18110927U, 0x48551d77U, 0x7ba9d244U,
+	0xc9b57cf6U, 0x5ac19b65U, 0xe8dd35d7U, 0x42df9d7dU,
+	0xc0ff3fffU, 0x67b2d558U, 0x4f90df70U, 0x6d385552U,
+	0xf706f1c8U, 0x8e20aeb1U, 0xfa49b3c5U, 0x7eec9241U,
+	0x94be2aabU, 0xac882493U, 0x1fd4cb20U, 0xd66eb8e9U,
+	0xb8992187U, 0x3436020bU, 0xf98970c6U, 0x7facd340U,
+	0xcf30fff0U, 0x3c3c0003U, 0x6afd9755U, 0x0a858f35U,
+	0x76e69049U, 0x72e3914dU, 0xae08a691U, 0x862aacb9U,
+	0xf58673caU, 0xaf48e790U, 0x7d2c5142U, 0x89e56cb6U,
+	0x2d684512U, 0xb496228bU, 0x3173420eU, 0x3ebc8201U,
+	0x876aedb8U, 0xa482269bU, 0xb213a18dU, 0xc67abcf9U,
+	0xbf5ce380U, 0x836fecbcU, 0xaa0da795U, 0xa087279fU,
+	0xeb1df6d4U, 0xc4fa3efbU, 0x07cacd38U, 0xe19776deU,
+	0x550e5b6aU, 0xf4c632cbU, 0x2ea88611U, 0x3576430aU,
+	0x77a6d148U, 0x129b892dU, 0x80af2fbfU, 0x8b65eeb4U,
+	0x2167461eU, 0x544e1a6bU, 0x115b4a2eU, 0x014f4e3eU,
+	0xe59277daU, 0x66f29459U, 0x9df469a2U, 0xea5db7d5U,
+	0xf303f0ccU, 0x99f168a6U, 0x1e948a21U, 0x3ab98305U,
+	0x56ce9869U, 0x2562471aU, 0x040a0e3bU, 0xe712f5d8U,
+	0xd4ee3aebU, 0x7ae99345U, 0xd8e139e7U, 0xd1ab7aeeU,
+	0xa347e49cU, 0x000f0f3fU, 0x36b68009U, 0x7063134fU,
+	0x2bedc614U, 0xb093238fU, 0xf646b0c9U, 0x439fdc7cU,
+	0x26a28419U, 0x78691147U, 0xbe1ca281U, 0x3bf9c204U,
+	0x9cb428a3U, 0xecd834d3U, 0x27e2c518U, 0xe257b5ddU,
+	0x652a4fc1U, 0x7c7408d8U, 0x99e9703dU, 0xc5a26761U,
+	0x0d5855a9U, 0xb9c1781dU, 0xca6da76eU, 0xe0cf2f44U,
+	0x66ea8cc2U, 0xe4ca2e40U, 0x6ee08ecaU, 0x356e5b91U,
+	0x9e2cb23aU, 0xd1b36275U, 0x8e38b62aU, 0x2ab59f8eU,
+	0x9223b136U, 0xa35ffc07U, 0x545602f0U, 0xe70aed43U,
+	0x9626b032U, 0xc4e22660U, 0x2eb09e8aU, 0x316b5a95U,
+	0x94a63230U, 0x2ff0df8bU, 0x72fb89d6U, 0xa61abc02U,
+	0x1bc9d2bfU, 0x243a1e80U, 0x26ba9c82U, 0x585901fcU,
+	0x4c4804e8U, 0x37eed993U, 0x7fb4cbdbU, 0xed806d49U,
+	0x787109dcU, 0x81f77625U, 0x5c5c00f8U, 0xd736e173U,
+	0xf25ba956U, 0x6fa0cfcbU, 0x128391b6U, 0x33ebd897U,
+	0x89fd742dU, 0x015756a5U, 0x8cb83428U, 0xb74ef913U,
+	0xadd07d09U, 0xf0db2b54U, 0x4ec886eaU, 0x98a9313cU,
+	0x194950bdU, 0x1e8c92baU, 0x9dec7139U, 0x68650dccU,
+	0x3c241898U, 0x410746e5U, 0x6c600cc8U, 0xd5b66371U,
+	0xeb05ee4fU, 0x001717a4U, 0xc267a566U, 0xa49a3e00U,
+	0x23ffdc87U, 0x22bf9d86U, 0xa09f3f04U, 0xcda86569U,
+	0xf19b6a55U, 0xf31be857U, 0x62ef8dc6U, 0xd4f62270U,
+	0x302b1b94U, 0x5b99c2ffU, 0xce68a66aU, 0x5ad983feU,
+	0xa5da7f01U, 0x28351d8cU, 0x03d7d4a7U, 0x9cac3038U,
+	0x9f6cf33bU, 0x9363f037U, 0x3821199cU, 0xf4de2a50U,
+	0xfb11ea5fU, 0xaf50ff0bU, 0x551643f1U, 0x100313b4U,
+	0xddbc6179U, 0x91e37235U, 0x029795a6U, 0x13c3d0b7U,
+	0xff14eb5bU, 0x67aacdc3U, 0x484d05ecU, 0xea45af4eU,
+	0x46c284e2U, 0xae10be0aU, 0x069294a2U, 0x4782c5e3U,
+	0xb34bf817U, 0x5f9cc3fbU, 0x793148ddU, 0xb48e3a10U,
+	0xaa15bf0eU, 0xb60eb812U, 0xcf28e76bU, 0xd273a176U,
+	0x3aa19b9eU, 0xba01bb1eU, 0xdf3ce37bU, 0x203f1f84U,
+	0x6d204dc9U, 0x3ea49a9aU, 0x27fadd83U, 0x63afccc7U,
+	0x404707e4U, 0x713b4ad5U, 0xa21fbd06U, 0x77bec9d3U,
+	0x0fd8d7abU, 0xe64aac42U, 0x36ae9892U, 0xa1df7e05U,
+	0xbc843818U, 0x2d705d89U, 0x1d4c51b9U, 0x444206e0U,
+	0x95e67331U, 0xc0e72764U, 0x140612b0U, 0x753e4bd1U,
+	0x3fe4db9bU, 0x217f5e85U, 0xde7ca27aU, 0xe8c52d4cU,
+	0xb08b3b14U, 0xe18f6e45U, 0xf59e6b51U, 0x055257a1U,
+	0xd9b9607dU, 0xbb41fa1fU, 0x6ae58fceU, 0xb881391cU,
+	0xbdc47919U, 0x84b23620U, 0x4b8dc6efU, 0xac903c08U,
+	0x505303f4U, 0xee40ae4aU, 0x56d680f2U, 0x6ba5cecfU,
+	0x88bd352cU, 0x707b0bd4U, 0xf65ea852U, 0xfe54aa5aU,
+	0xe9856c4dU, 0x0c1814a8U, 0xf8d1295cU, 0xb1cb7a15U,
+	0x4acd87eeU, 0xe58a6f41U, 0xb5ce7b11U, 0x8632b422U,
+	0x342e1a90U, 0xa75afd03U, 0x154653b1U, 0xbf44fb1bU,
+	0x3d645999U, 0x9a29b33eU, 0xb20bb916U, 0x90a33334U,
+	0x0a9d97aeU, 0x73bbc8d7U, 0x07d2d5a3U, 0x8377f427U,
+	0x69254ccdU, 0x511342f5U, 0xe24fad46U, 0x2bf5de8fU,
+	0x450247e1U, 0xc9ad646dU, 0x041216a0U, 0x8237b526U,
+	0x32ab9996U, 0xc1a76665U, 0x9766f133U, 0xf71ee953U,
+	0x8b7df62fU, 0x8f78f72bU, 0x5393c0f7U, 0x7bb1cadfU,
+	0x081d15acU, 0x52d381f6U, 0x80b73724U, 0x747e0ad0U,
+	0xd0f32374U, 0x490d44edU, 0xcce82468U, 0xc327e467U,
+	0x7af18bdeU, 0x591940fdU, 0x4f88c7ebU, 0x3be1da9fU,
+	0x42c785e6U, 0x7ef48adaU, 0x5796c1f3U, 0x5d1c41f9U,
+	0x168690b2U, 0x3961589dU, 0xfa51ab5eU, 0x1c0c10b8U,
+	0xa8953d0cU, 0x095d54adU, 0xd333e077U, 0xc8ed256cU,
+	0x8a3db72eU, 0xef00ef4bU, 0x7d3449d9U, 0x76fe88d2U,
+	0xdcfc2078U, 0xa9d57c0dU, 0xecc02c48U, 0xfcd42858U,
+	0x180911bcU, 0x9b69f23fU, 0x606f0fc4U, 0x17c6d1b3U,
+	0x0e9896aaU, 0x646a0ec0U, 0xe30fec47U, 0xc722e563U,
+	0xab55fe0fU, 0xd8f9217cU, 0xf991685dU, 0x1a8993beU,
+	0x29755c8dU, 0x8772f523U, 0x257a5f81U, 0x2c301c88U,
+	0x5edc82faU, 0xfd946959U, 0xcb2de66fU, 0x8df87529U,
+	0xd676a072U, 0x4d0845e9U, 0x0bddd6afU, 0xbe04ba1aU,
+	0xdb39e27fU, 0x85f27721U, 0x4387c4e7U, 0xc662a462U,
+	0x612f4ec5U, 0x114352b5U, 0xda79a37eU, 0x1fccd3bbU,
+	0x00c8c846U, 0x19968f5fU, 0xfc0bf7baU, 0xa040e0e6U,
+	0x68bad22eU, 0xdc23ff9aU, 0xaf8f20e9U, 0x852da8c3U,
+	0x03080b45U, 0x8128a9c7U, 0x0b02094dU, 0x508cdc16U,
+	0xfbce35bdU, 0xb451e5f2U, 0xebda31adU, 0x4f571809U,
+	0xf7c136b1U, 0xc6bd7b80U, 0x31b48577U, 0x82e86ac4U,
+	0xf3c437b5U, 0xa100a1e7U, 0x4b52190dU, 0x5489dd12U,
+	0xf144b5b7U, 0x4a12580cU, 0x17190e51U, 0xc3f83b85U,
+	0x7e2b5538U, 0x41d89907U, 0x43581b05U, 0x3dbb867bU,
+	0x29aa836fU, 0x520c5e14U, 0x1a564c5cU, 0x8862eaceU,
+	0x1d938e5bU, 0xe415f1a2U, 0x39be877fU, 0xb2d466f4U,
+	0x97b92ed1U, 0x0a42484cU, 0x77611631U, 0x56095f10U,
+	0xec1ff3aaU, 0x64b5d122U, 0xe95ab3afU, 0xd2ac7e94U,
+	0xc832fa8eU, 0x9539acd3U, 0x2b2a016dU, 0xfd4bb6bbU,
+	0x7cabd73aU, 0x7b6e153dU, 0xf80ef6beU, 0x0d878a4bU,
+	0x59c69f1fU, 0x24e5c162U, 0x09828b4fU, 0xb054e4f6U,
+	0x8ee769c8U, 0x65f59023U, 0xa78522e1U, 0xc178b987U,
+	0x461d5b00U, 0x475d1a01U, 0xc57db883U, 0xa84ae2eeU,
+	0x9479edd2U, 0x96f96fd0U, 0x070d0a41U, 0xb114a5f7U,
+	0x55c99c13U, 0x3e7b4578U, 0xab8a21edU, 0x3f3b0479U,
+	0xc038f886U, 0x4dd79a0bU, 0x66355320U, 0xf94eb7bfU,
+	0xfa8e74bcU, 0xf68177b0U, 0x5dc39e1bU, 0x913cadd7U,
+	0x9ef36dd8U, 0xcab2788cU, 0x30f4c476U, 0x75e19433U,
+	0xb85ee6feU, 0xf401f5b2U, 0x67751221U, 0x76215730U,
+	0x9af66cdcU, 0x02484a44U, 0x2daf826bU, 0x8fa728c9U,
+	0x23200365U, 0xcbf2398dU, 0x63701325U, 0x22604264U,
+	0xd6a97f90U, 0x3a7e447cU, 0x1cd3cf5aU, 0xd16cbd97U,
+	0xcff73889U, 0xd3ec3f95U, 0xaaca60ecU, 0xb79126f1U,
+	0x5f431c19U, 0xdfe33c99U, 0xbade64fcU, 0x45dd9803U,
+	0x08c2ca4eU, 0x5b461d1dU, 0x42185a04U, 0x064d4b40U,
+	0x25a58063U, 0x14d9cd52U, 0xc7fd3a81U, 0x125c4e54U,
+	0x6a3a502cU, 0x83a82bc5U, 0x534c1f15U, 0xc43df982U,
+	0xd966bf9fU, 0x4892da0eU, 0x78aed63eU, 0x21a08167U,
+	0xf004f4b6U, 0xa505a0e3U, 0x71e49537U, 0x10dccc56U,
+	0x5a065c1cU, 0x449dd902U, 0xbb9e25fdU, 0x8d27aacbU,
+	0xd569bc93U, 0x846de9c2U, 0x907cecd6U, 0x60b0d026U,
+	0xbc5be7faU, 0xdea37d98U, 0x0f070849U, 0xdd63be9bU,
+	0xd826fe9eU, 0xe150b1a7U, 0x2e6f4168U, 0xc972bb8fU,
+	0x35b18473U, 0x8ba229cdU, 0x33340775U, 0x0e474948U,
+	0xed5fb2abU, 0x15998c53U, 0x93bc2fd5U, 0x9bb62dddU,
+	0x8c67ebcaU, 0x69fa932fU, 0x9d33aedbU, 0xd429fd92U,
+	0x2f2f0069U, 0x8068e8c6U, 0xd02cfc96U, 0xe3d033a5U,
+	0x51cc9d17U, 0xc2b87a84U, 0x70a4d436U, 0xdaa67c9cU,
+	0x5886de1eU, 0xffcb34b9U, 0xd7e93e91U, 0xf541b4b3U,
+	0x6f7f1029U, 0x16594f50U, 0x62305224U, 0xe69573a0U,
+	0x0cc7cb4aU, 0x34f1c572U, 0x87ad2ac1U, 0x4e175908U,
+	0x20e0c066U, 0xac4fe3eaU, 0x61f09127U, 0xe7d532a1U,
+	0x57491e11U, 0xa445e1e2U, 0xf28476b4U, 0x92fc6ed4U,
+	0xee9f71a8U, 0xea9a70acU, 0x36714770U, 0x1e534d58U,
+	0x6dff922bU, 0x37310671U, 0xe555b0a3U, 0x119c8d57U,
+	0xb511a4f3U, 0x2cefc36aU, 0xa90aa3efU, 0xa6c563e0U,
+	0x1f130c59U, 0x3cfbc77aU, 0x2a6a406cU, 0x5e035d18U,
+	0x27250261U, 0x1b160d5dU, 0x32744674U, 0x38fec67eU,
+	0x73641735U, 0x5c83df1aU, 0x9fb32cd9U, 0x79ee973fU,
+	0xcd77ba8bU, 0x6cbfd32aU, 0xb6d167f0U, 0xad0fa2ebU,
+	0xefdf30a9U, 0x8ae268ccU, 0x18d6ce5eU, 0x131c0f55U,
+	0xb91ea7ffU, 0xcc37fb8aU, 0x8922abcfU, 0x9936afdfU,
+	0x7deb963bU, 0xfe8b75b8U, 0x058d8843U, 0x72245634U,
+	0x6b7a112dU, 0x01888947U, 0x86ed6bc0U, 0xa2c062e4U,
+	0xceb77988U, 0xbd1ba6fbU, 0x9c73efdaU, 0x7f6b1439U,
+	0x4c97db0aU, 0xe29072a4U, 0x4098d806U, 0x49d29b0fU,
+	0x3b3e057dU, 0x9876eedeU, 0xaecf61e8U, 0xe81af2aeU,
+	0xb39427f5U, 0x28eac26eU, 0x6e3f5128U, 0xdbe63d9dU,
+	0xbedb65f8U, 0xe010f0a6U, 0x26654360U, 0xa38023e5U,
+	0x04cdc942U, 0x74a1d532U, 0xbf9b24f9U, 0x7a2e543cU,
+	0x4089c947U, 0x59d78e5eU, 0xbc4af6bbU, 0xe001e1e7U,
+	0x28fbd32fU, 0x9c62fe9bU, 0xefce21e8U, 0xc56ca9c2U,
+	0x43490a44U, 0xc169a8c6U, 0x4b43084cU, 0x10cddd17U,
+	0xbb8f34bcU, 0xf410e4f3U, 0xab9b30acU, 0x0f161908U,
+	0xb78037b0U, 0x86fc7a81U, 0x71f58476U, 0xc2a96bc5U,
+	0xb38536b4U, 0xe141a0e6U, 0x0b13180cU, 0x14c8dc13U,
+	0xb105b4b6U, 0x0a53590dU, 0x57580f50U, 0x83b93a84U,
+	0x3e6a5439U, 0x01999806U, 0x03191a04U, 0x7dfa877aU,
+	0x69eb826eU, 0x124d5f15U, 0x5a174d5dU, 0xc823ebcfU,
+	0x5dd28f5aU, 0xa454f0a3U, 0x79ff867eU, 0xf29567f5U,
+	0xd7f82fd0U, 0x4a03494dU, 0x37201730U, 0x16485e11U,
+	0xac5ef2abU, 0x24f4d023U, 0xa91bb2aeU, 0x92ed7f95U,
+	0x8873fb8fU, 0xd578add2U, 0x6b6b006cU, 0xbd0ab7baU,
+	0x3cead63bU, 0x3b2f143cU, 0xb84ff7bfU, 0x4dc68b4aU,
+	0x19879e1eU, 0x64a4c063U, 0x49c38a4eU, 0xf015e5f7U,
+	0xcea668c9U, 0x25b49122U, 0xe7c423e0U, 0x8139b886U,
+	0x065c5a01U, 0x071c1b00U, 0x853cb982U, 0xe80be3efU,
+	0xd438ecd3U, 0xd6b86ed1U, 0x474c0b40U, 0xf155a4f6U,
+	0x15889d12U, 0x7e3a4479U, 0xebcb20ecU, 0x7f7a0578U,
+	0x8079f987U, 0x0d969b0aU, 0x26745221U, 0xb90fb6beU,
+	0xbacf75bdU, 0xb6c076b1U, 0x1d829f1aU, 0xd17dacd6U,
+	0xdeb26cd9U, 0x8af3798dU, 0x70b5c577U, 0x35a09532U,
+	0xf81fe7ffU, 0xb440f4b3U, 0x27341320U, 0x36605631U,
+	0xdab76dddU, 0x42094b45U, 0x6dee836aU, 0xcfe629c8U,
+	0x63610264U, 0x8bb3388cU, 0x23311224U, 0x62214365U,
+	0x96e87e91U, 0x7a3f457dU, 0x5c92ce5bU, 0x912dbc96U,
+	0x8fb63988U, 0x93ad3e94U, 0xea8b61edU, 0xf7d027f0U,
+	0x1f021d18U, 0x9fa23d98U, 0xfa9f65fdU, 0x059c9902U,
+	0x4883cb4fU, 0x1b071c1cU, 0x02595b05U, 0x460c4a41U,
+	0x65e48162U, 0x5498cc53U, 0x87bc3b80U, 0x521d4f55U,
+	0x2a7b512dU, 0xc3e92ac4U, 0x130d1e14U, 0x847cf883U,
+	0x9927be9eU, 0x08d3db0fU, 0x38efd73fU, 0x61e18066U,
+	0xb045f5b7U, 0xe544a1e2U, 0x31a59436U, 0x509dcd57U,
+	0x1a475d1dU, 0x04dcd803U, 0xfbdf24fcU, 0xcd66abcaU,
+	0x9528bd92U, 0xc42ce8c3U, 0xd03dedd7U, 0x20f1d127U,
+	0xfc1ae6fbU, 0x9ee27c99U, 0x4f460948U, 0x9d22bf9aU,
+	0x9867ff9fU, 0xa111b0a6U, 0x6e2e4069U, 0x8933ba8eU,
+	0x75f08572U, 0xcbe328ccU, 0x73750674U, 0x4e064849U,
+	0xad1eb3aaU, 0x55d88d52U, 0xd3fd2ed4U, 0xdbf72cdcU,
+	0xcc26eacbU, 0x29bb922eU, 0xdd72afdaU, 0x9468fc93U,
+	0x6f6e0168U, 0xc029e9c7U, 0x906dfd97U, 0xa39132a4U,
+	0x118d9c16U, 0x82f97b85U, 0x30e5d537U, 0x9ae77d9dU,
+	0x18c7df1fU, 0xbf8a35b8U, 0x97a83f90U, 0xb500b5b2U,
+	0x2f3e1128U, 0x56184e51U, 0x22715325U, 0xa6d472a1U,
+	0x4c86ca4bU, 0x74b0c473U, 0xc7ec2bc0U, 0x0e565809U,
+	0x60a1c167U, 0xec0ee2ebU, 0x21b19026U, 0xa79433a0U,
+	0x17081f10U, 0xe404e0e3U, 0xb2c577b5U, 0xd2bd6fd5U,
+	0xaede70a9U, 0xaadb71adU, 0x76304671U, 0x5e124c59U,
+	0x2dbe932aU, 0x77700770U, 0xa514b1a2U, 0x51dd8c56U,
+	0xf550a5f2U, 0x6caec26bU, 0xe94ba2eeU, 0xe68462e1U,
+	0x5f520d58U, 0x7cbac67bU, 0x6a2b416dU, 0x1e425c19U,
+	0x67640360U, 0x5b570c5cU, 0x72354775U, 0x78bfc77fU,
+	0x33251634U, 0x1cc2de1bU, 0xdff22dd8U, 0x39af963eU,
+	0x8d36bb8aU, 0x2cfed22bU, 0xf69066f1U, 0xed4ea3eaU,
+	0xaf9e31a8U, 0xcaa369cdU, 0x5897cf5fU, 0x535d0e54U,
+	0xf95fa6feU, 0x8c76fa8bU, 0xc963aaceU, 0xd977aedeU,
+	0x3daa973aU, 0xbeca74b9U, 0x45cc8942U, 0x32655735U,
+	0x2b3b102cU, 0x41c98846U, 0xc6ac6ac1U, 0xe28163e5U,
+	0x8ef67889U, 0xfd5aa7faU, 0xdc32eedbU, 0x3f2a1538U,
+	0x0cd6da0bU, 0xa2d173a5U, 0x00d9d907U, 0x09939a0eU,
+	0x7b7f047cU, 0xd837efdfU, 0xee8e60e9U, 0xa85bf3afU,
+	0xf3d526f4U, 0x68abc36fU, 0x2e7e5029U, 0x9ba73c9cU,
+	0xfe9a64f9U, 0xa051f1a7U, 0x66244261U, 0xe3c122e4U,
+	0x448cc843U, 0x34e0d433U, 0xffda25f8U, 0x3a6f553dU,
+	0x602b4bc5U, 0x79750cdcU, 0x9ce87439U, 0xc0a36365U,
+	0x085951adU, 0xbcc07c19U, 0xcf6ca36aU, 0xe5ce2b40U,
+	0x63eb88c6U, 0xe1cb2a44U, 0x6be18aceU, 0x306f5f95U,
+	0x9b2db63eU, 0xd4b26671U, 0x8b39b22eU, 0x2fb49b8aU,
+	0x9722b532U, 0xa65ef803U, 0x515706f4U, 0xe20be947U,
+	0x9327b436U, 0xc1e32264U, 0x2bb19a8eU, 0x346a5e91U,
+	0x91a73634U, 0x2af1db8fU, 0x77fa8dd2U, 0xa31bb806U,
+	0x1ec8d6bbU, 0x213b1a84U, 0x23bb9886U, 0x5d5805f8U,
+	0x494900ecU, 0x32efdd97U, 0x7ab5cfdfU, 0xe881694dU,
+	0x7d700dd8U, 0x84f67221U, 0x595d04fcU, 0xd237e577U,
+	0xf75aad52U, 0x6aa1cbcfU, 0x178295b2U, 0x36eadc93U,
+	0x8cfc7029U, 0x045652a1U, 0x89b9302cU, 0xb24ffd17U,
+	0xa8d1790dU, 0xf5da2f50U, 0x4bc982eeU, 0x9da83538U,
+	0x1c4854b9U, 0x1b8d96beU, 0x98ed753dU, 0x6d6409c8U,
+	0x39251c9cU, 0x440642e1U, 0x696108ccU, 0xd0b76775U,
+	0xee04ea4bU, 0x051613a0U, 0xc766a162U, 0xa19b3a04U,
+	0x26fed883U, 0x27be9982U, 0xa59e3b00U, 0xc8a9616dU,
+	0xf49a6e51U, 0xf61aec53U, 0x67ee89c2U, 0xd1f72674U,
+	0x352a1f90U, 0x5e98c6fbU, 0xcb69a26eU, 0x5fd887faU,
+	0xa0db7b05U, 0x2d341988U, 0x06d6d0a3U, 0x99ad343cU,
+	0x9a6df73fU, 0x9662f433U, 0x3d201d98U, 0xf1df2e54U,
+	0xfe10ee5bU, 0xaa51fb0fU, 0x501747f5U, 0x150217b0U,
+	0xd8bd657dU, 0x94e27631U, 0x079691a2U, 0x16c2d4b3U,
+	0xfa15ef5fU, 0x62abc9c7U, 0x4d4c01e8U, 0xef44ab4aU,
+	0x43c380e6U, 0xab11ba0eU, 0x039390a6U, 0x4283c1e7U,
+	0xb64afc13U, 0x5a9dc7ffU, 0x7c304cd9U, 0xb18f3e14U,
+	0xaf14bb0aU, 0xb30fbc16U, 0xca29e36fU, 0xd772a572U,
+	0x3fa09f9aU, 0xbf00bf1aU, 0xda3de77fU, 0x253e1b80U,
+	0x682149cdU, 0x3ba59e9eU, 0x22fbd987U, 0x66aec8c3U,
+	0x454603e0U, 0x743a4ed1U, 0xa71eb902U, 0x72bfcdd7U,
+	0x0ad9d3afU, 0xe34ba846U, 0x33af9c96U, 0xa4de7a01U,
+	0xb9853c1cU, 0x2871598dU, 0x184d55bdU, 0x414302e4U,
+	0x90e77735U, 0xc5e62360U, 0x110716b4U, 0x703f4fd5U,
+	0x3ae5df9fU, 0x247e5a81U, 0xdb7da67eU, 0xedc42948U,
+	0xb58a3f10U, 0xe48e6a41U, 0xf09f6f55U, 0x005353a5U,
+	0xdcb86479U, 0xbe40fe1bU, 0x6fe48bcaU, 0xbd803d18U,
+	0xb8c57d1dU, 0x81b33224U, 0x4e8cc2ebU, 0xa991380cU,
+	0x555207f0U, 0xeb41aa4eU, 0x53d784f6U, 0x6ea4cacbU,
+	0x8dbc3128U, 0x757a0fd0U, 0xf35fac56U, 0xfb55ae5eU,
+	0xec846849U, 0x091910acU, 0xfdd02d58U, 0xb4ca7e11U,
+	0x4fcc83eaU, 0xe08b6b45U, 0xb0cf7f15U, 0x8333b026U,
+	0x312f1e94U, 0xa25bf907U, 0x104757b5U, 0xba45ff1fU,
+	0x38655d9dU, 0x9f28b73aU, 0xb70abd12U, 0x95a23730U,
+	0x0f9c93aaU, 0x76baccd3U, 0x02d3d1a7U, 0x8676f023U,
+	0x6c2448c9U, 0x541246f1U, 0xe74ea942U, 0x2ef4da8bU,
+	0x400343e5U, 0xccac6069U, 0x011312a4U, 0x8736b122U,
+	0x37aa9d92U, 0xc4a66261U, 0x9267f537U, 0xf21fed57U,
+	0x8e7cf22bU, 0x8a79f32fU, 0x5692c4f3U, 0x7eb0cedbU,
+	0x0d1c11a8U, 0x57d285f2U, 0x85b63320U, 0x717f0ed4U,
+	0xd5f22770U, 0x4c0c40e9U, 0xc9e9206cU, 0xc626e063U,
+	0x7ff08fdaU, 0x5c1844f9U, 0x4a89c3efU, 0x3ee0de9bU,
+	0x47c681e2U, 0x7bf58edeU, 0x5297c5f7U, 0x581d45fdU,
+	0x138794b6U, 0x3c605c99U, 0xff50af5aU, 0x190d14bcU,
+	0xad943908U, 0x0c5c50a9U, 0xd632e473U, 0xcdec2168U,
+	0x8f3cb32aU, 0xea01eb4fU, 0x78354dddU, 0x73ff8cd6U,
+	0xd9fd247cU, 0xacd47809U, 0xe9c1284cU, 0xf9d52c5cU,
+	0x1d0815b8U, 0x9e68f63bU, 0x656e0bc0U, 0x12c7d5b7U,
+	0x0b9992aeU, 0x616b0ac4U, 0xe60ee843U, 0xc223e167U,
+	0xae54fa0bU, 0xddf82578U, 0xfc906c59U, 0x1f8897baU,
+	0x2c745889U, 0x8273f127U, 0x207b5b85U, 0x2931188cU,
+	0x5bdd86feU, 0xf8956d5dU, 0xce2ce26bU, 0x88f9712dU,
+	0xd377a476U, 0x480941edU, 0x0edcd2abU, 0xbb05be1eU,
+	0xde38e67bU, 0x80f37325U, 0x4686c0e3U, 0xc363a066U,
+	0x642e4ac1U, 0x144256b1U, 0xdf78a77aU, 0x1acdd7bfU,
+	0x577126a8U, 0x4e2f61b1U, 0xabb21954U, 0xf7f90e08U,
+	0x3f033cc0U, 0x8b9a1174U, 0xf836ce07U, 0xd294462dU,
+	0x54b1e5abU, 0xd6914729U, 0x5cbbe7a3U, 0x073532f8U,
+	0xac77db53U, 0xe3e80b1cU, 0xbc63df43U, 0x18eef6e7U,
+	0xa078d85fU, 0x9104956eU, 0x660d6b99U, 0xd551842aU,
+	0xa47dd95bU, 0xf6b94f09U, 0x1cebf7e3U, 0x033033fcU,
+	0xa6fd5b59U, 0x1dabb6e2U, 0x40a0e0bfU, 0x9441d56bU,
+	0x2992bbd6U, 0x166177e9U, 0x14e1f5ebU, 0x6a026895U,
+	0x7e136d81U, 0x05b5b0faU, 0x4defa2b2U, 0xdfdb0420U,
+	0x4a2a60b5U, 0xb3ac1f4cU, 0x6e076991U, 0xe56d881aU,
+	0xc000c03fU, 0x5dfba6a2U, 0x20d8f8dfU, 0x01b0b1feU,
+	0xbba61d44U, 0x330c3fccU, 0xbee35d41U, 0x8515907aU,
+	0x9f8b1460U, 0xc280423dU, 0x7c93ef83U, 0xaaf25855U,
+	0x2b1239d4U, 0x2cd7fbd3U, 0xafb71850U, 0x5a3e64a5U,
+	0x0e7f71f1U, 0x735c2f8cU, 0x5e3b65a1U, 0xe7ed0a18U,
+	0xd95e8726U, 0x324c7ecdU, 0xf03ccc0fU, 0x96c15769U,
+	0x11a4b5eeU, 0x10e4f4efU, 0x92c4566dU, 0xfff30c00U,
+	0xc3c0033cU, 0xc140813eU, 0x50b4e4afU, 0xe6ad4b19U,
+	0x027072fdU, 0x69c2ab96U, 0xfc33cf03U, 0x6882ea97U,
+	0x97811668U, 0x1a6e74e5U, 0x318cbdceU, 0xaef75951U,
+	0xad379a52U, 0xa138995eU, 0x0a7a70f5U, 0xc6854339U,
+	0xc94a8336U, 0x9d0b9662U, 0x674d2a98U, 0x22587addU,
+	0xefe70810U, 0xa3b81b5cU, 0x30ccfccfU, 0x2198b9deU,
+	0xcd4f8232U, 0x55f1a4aaU, 0x7a166c85U, 0xd81ec627U,
+	0x7499ed8bU, 0x9c4bd763U, 0x34c9fdcbU, 0x75d9ac8aU,
+	0x8110917eU, 0x6dc7aa92U, 0x4b6a21b4U, 0x86d55379U,
+	0x984ed667U, 0x8455d17bU, 0xfd738e02U, 0xe028c81fU,
+	0x08faf2f7U, 0x885ad277U, 0xed678a12U, 0x126476edU,
+	0x5f7b24a0U, 0x0cfff3f3U, 0x15a1b4eaU, 0x51f4a5aeU,
+	0x721c6e8dU, 0x436023bcU, 0x9044d46fU, 0x45e5a0baU,
+	0x3d83bec2U, 0xd411c52bU, 0x04f5f1fbU, 0x9384176cU,
+	0x8edf5171U, 0x1f2b34e0U, 0x2f1738d0U, 0x76196f89U,
+	0xa7bd1a58U, 0xf2bc4e0dU, 0x265d7bd9U, 0x476522b8U,
+	0x0dbfb2f2U, 0x132437ecU, 0xec27cb13U, 0xda9e4425U,
+	0x82d0527dU, 0xd3d4072cU, 0xc7c50238U, 0x37093ec8U,
+	0xebe20914U, 0x891a9376U, 0x58bee6a7U, 0x8ada5075U,
+	0x8f9f1070U, 0xb6e95f49U, 0x79d6af86U, 0x9ecb5561U,
+	0x62086a9dU, 0xdc1bc723U, 0x648de99bU, 0x59fea7a6U,
+	0xbae65c45U, 0x422062bdU, 0xc405c13bU, 0xcc0fc333U,
+	0xdbde0524U, 0x3e437dc1U, 0xca8a4035U, 0x8390137cU,
+	0x7896ee87U, 0xd7d10628U, 0x87951278U, 0xb469dd4bU,
+	0x067573f9U, 0x9501946aU, 0x271d3ad8U, 0x8d1f9272U,
+	0x0f3f30f0U, 0xa872da57U, 0x8050d07fU, 0xa2f85a5dU,
+	0x38c6fec7U, 0x41e0a1beU, 0x3589bccaU, 0xb12c9d4eU,
+	0x5b7e25a4U, 0x63482b9cU, 0xd014c42fU, 0x19aeb7e6U,
+	0x77592e88U, 0xfbf60d04U, 0x36497fc9U, 0xb06cdc4fU,
+	0x00f0f0ffU, 0xf3fc0f0cU, 0xa53d985aU, 0xc545803aU,
+	0xb9269f46U, 0xbd239e42U, 0x61c8a99eU, 0x49eaa3b6U,
+	0x3a467cc5U, 0x6088e89fU, 0xb2ec5e4dU, 0x462563b9U,
+	0xe2a84a1dU, 0x7b562d84U, 0xfeb34d01U, 0xf17c8d0eU,
+	0x48aae2b7U, 0x6b422994U, 0x7dd3ae82U, 0x09bab3f6U,
+	0x709cec8fU, 0x4cafe3b3U, 0x65cda89aU, 0x6f472890U,
+	0x24ddf9dbU, 0x0b3a31f4U, 0xc80ac237U, 0x2e5779d1U,
+	0x9ace5465U, 0x3b063dc4U, 0xe168891eU, 0xfab64c05U,
+	0xb866de47U, 0xdd5b8622U, 0x4f6f20b0U, 0x44a5e1bbU,
+	0xeea74911U, 0x9b8e1564U, 0xde9b4521U, 0xce8f4131U,
+	0x2a5278d5U, 0xa9329b56U, 0x523466adU, 0x259db8daU,
+	0x3cc3ffc3U, 0x563167a9U, 0xd154852eU, 0xf5798c0aU,
+	0x990e9766U, 0xeaa24815U, 0xcbca0134U, 0x28d2fad7U,
+	0x1b2e35e4U, 0xb5299c4aU, 0x172136e8U, 0x1e6b75e1U,
+	0x6c87eb93U, 0xcfcf0030U, 0xf9768f06U, 0xbfa31c40U,
+	0xe42dc91bU, 0x7f532c80U, 0x3986bfc6U, 0x8c5fd373U,
+	0xe9628b16U, 0xb7a91e48U, 0x71dcad8eU, 0xf439cd0bU,
+	0x537427acU, 0x23183bdcU, 0xe822ca17U, 0x2d97bad2U,
+	0x647e1a94U, 0x7d205d8dU, 0x98bd2568U, 0xc4f63234U,
+	0x0c0c00fcU, 0xb8952d48U, 0xcb39f23bU, 0xe19b7a11U,
+	0x67bed997U, 0xe59e7b15U, 0x6fb4db9fU, 0x343a0ec4U,
+	0x9f78e76fU, 0xd0e73720U, 0x8f6ce37fU, 0x2be1cadbU,
+	0x9377e463U, 0xa20ba952U, 0x550257a5U, 0xe65eb816U,
+	0x9772e567U, 0xc5b67335U, 0x2fe4cbdfU, 0x303f0fc0U,
+	0x95f26765U, 0x2ea48adeU, 0x73afdc83U, 0xa74ee957U,
+	0x1a9d87eaU, 0x256e4bd5U, 0x27eec9d7U, 0x590d54a9U,
+	0x4d1c51bdU, 0x36ba8cc6U, 0x7ee09e8eU, 0xecd4381cU,
+	0x79255c89U, 0x80a32370U, 0x5d0855adU, 0xd662b426U,
+	0xf30ffc03U, 0x6ef49a9eU, 0x13d7c4e3U, 0x32bf8dc2U,
+	0x88a92178U, 0x000303f0U, 0x8dec617dU, 0xb61aac46U,
+	0xac84285cU, 0xf18f7e01U, 0x4f9cd3bfU, 0x99fd6469U,
+	0x181d05e8U, 0x1fd8c7efU, 0x9cb8246cU, 0x69315899U,
+	0x3d704dcdU, 0x405313b0U, 0x6d34599dU, 0xd4e23624U,
+	0xea51bb1aU, 0x014342f1U, 0xc333f033U, 0xa5ce6b55U,
+	0x22ab89d2U, 0x23ebc8d3U, 0xa1cb6a51U, 0xccfc303cU,
+	0xf0cf3f00U, 0xf24fbd02U, 0x63bbd893U, 0xd5a27725U,
+	0x317f4ec1U, 0x5acd97aaU, 0xcf3cf33fU, 0x5b8dd6abU,
+	0xa48e2a54U, 0x296148d9U, 0x028381f2U, 0x9df8656dU,
+	0x9e38a66eU, 0x9237a562U, 0x39754cc9U, 0xf58a7f05U,
+	0xfa45bf0aU, 0xae04aa5eU, 0x544216a4U, 0x115746e1U,
+	0xdce8342cU, 0x90b72760U, 0x03c3c0f3U, 0x129785e2U,
+	0xfe40be0eU, 0x66fe9896U, 0x491950b9U, 0xeb11fa1bU,
+	0x4796d1b7U, 0xaf44eb5fU, 0x07c6c1f7U, 0x46d690b6U,
+	0xb21fad42U, 0x5ec896aeU, 0x78651d88U, 0xb5da6f45U,
+	0xab41ea5bU, 0xb75aed47U, 0xce7cb23eU, 0xd327f423U,
+	0x3bf5cecbU, 0xbb55ee4bU, 0xde68b62eU, 0x216b4ad1U,
+	0x6c74189cU, 0x3ff0cfcfU, 0x26ae88d6U, 0x62fb9992U,
+	0x411352b1U, 0x706f1f80U, 0xa34be853U, 0x76ea9c86U,
+	0x0e8c82feU, 0xe71ef917U, 0x37facdc7U, 0xa08b2b50U,
+	0xbdd06d4dU, 0x2c2408dcU, 0x1c1804ecU, 0x451653b5U,
+	0x94b22664U, 0xc1b37231U, 0x155247e5U, 0x746a1e84U,
+	0x3eb08eceU, 0x202b0bd0U, 0xdf28f72fU, 0xe9917819U,
+	0xb1df6e41U, 0xe0db3b10U, 0xf4ca3e04U, 0x040602f4U,
+	0xd8ed3528U, 0xba15af4aU, 0x6bb1da9bU, 0xb9d56c49U,
+	0xbc902c4cU, 0x85e66375U, 0x4ad993baU, 0xadc4695dU,
+	0x510756a1U, 0xef14fb1fU, 0x5782d5a7U, 0x6af19b9aU,
+	0x89e96079U, 0x712f5e81U, 0xf70afd07U, 0xff00ff0fU,
+	0xe8d13918U, 0x0d4c41fdU, 0xf9857c09U, 0xb09f2f40U,
+	0x4b99d2bbU, 0xe4de3a14U, 0xb49a2e44U, 0x8766e177U,
+	0x357a4fc5U, 0xa60ea856U, 0x141206e4U, 0xbe10ae4eU,
+	0x3c300cccU, 0x9b7de66bU, 0xb35fec43U, 0x91f76661U,
+	0x0bc9c2fbU, 0x72ef9d82U, 0x068680f6U, 0x8223a172U,
+	0x68711998U, 0x504717a0U, 0xe31bf813U, 0x2aa18bdaU,
+	0x445612b4U, 0xc8f93138U, 0x054643f5U, 0x8363e073U,
+	0x33ffccc3U, 0xc0f33330U, 0x9632a466U, 0xf64abc06U,
+	0x8a29a37aU, 0x8e2ca27eU, 0x52c795a2U, 0x7ae59f8aU,
+	0x094940f9U, 0x5387d4a3U, 0x81e36271U, 0x752a5f85U,
+	0xd1a77621U, 0x485911b8U, 0xcdbc713dU, 0xc273b132U,
+	0x7ba5de8bU, 0x584d15a8U, 0x4edc92beU, 0x3ab58fcaU,
+	0x4393d0b3U, 0x7fa0df8fU, 0x56c294a6U, 0x5c4814acU,
+	0x17d2c5e7U, 0x38350dc8U, 0xfb05fe0bU, 0x1d5845edU,
+	0xa9c16859U, 0x080901f8U, 0xd267b522U, 0xc9b97039U,
+	0x8b69e27bU, 0xee54ba1eU, 0x7c601c8cU, 0x77aadd87U,
+	0xdda8752dU, 0xa8812958U, 0xed94791dU, 0xfd807d0dU,
+	0x195d44e9U, 0x9a3da76aU, 0x613b5a91U, 0x169284e6U,
+	0x0fccc3ffU, 0x653e5b95U, 0xe25bb912U, 0xc676b036U,
+	0xaa01ab5aU, 0xd9ad7429U, 0xf8c53d08U, 0x1bddc6ebU,
+	0x282109d8U, 0x8626a076U, 0x242e0ad4U, 0x2d6449ddU,
+	0x5f88d7afU, 0xfcc03c0cU, 0xca79b33aU, 0x8cac207cU,
+	0xd722f527U, 0x4c5c10bcU, 0x0a8983faU, 0xbf50ef4fU,
+	0xda6db72aU, 0x84a62274U, 0x42d391b2U, 0xc736f137U,
+	0x607b1b90U, 0x101707e0U, 0xdb2df62bU, 0x1e9886eeU,
+	0xe4fc1896U, 0xfda25f8fU, 0x183f276aU, 0x44743036U,
+	0x8c8e02feU, 0x38172f4aU, 0x4bbbf039U, 0x61197813U,
+	0xe73cdb95U, 0x651c7917U, 0xef36d99dU, 0xb4b80cc6U,
+	0x1ffae56dU, 0x50653522U, 0x0feee17dU, 0xab63c8d9U,
+	0x13f5e661U, 0x2289ab50U, 0xd58055a7U, 0x66dcba14U,
+	0x17f0e765U, 0x45347137U, 0xaf66c9ddU, 0xb0bd0dc2U,
+	0x15706567U, 0xae2688dcU, 0xf32dde81U, 0x27cceb55U,
+	0x9a1f85e8U, 0xa5ec49d7U, 0xa76ccbd5U, 0xd98f56abU,
+	0xcd9e53bfU, 0xb6388ec4U, 0xfe629c8cU, 0x6c563a1eU,
+	0xf9a75e8bU, 0x00212172U, 0xdd8a57afU, 0x56e0b624U,
+	0x738dfe01U, 0xee76989cU, 0x9355c6e1U, 0xb23d8fc0U,
+	0x082b237aU, 0x808101f2U, 0x0d6e637fU, 0x3698ae44U,
+	0x2c062a5eU, 0x710d7c03U, 0xcf1ed1bdU, 0x197f666bU,
+	0x989f07eaU, 0x9f5ac5edU, 0x1c3a266eU, 0xe9b35a9bU,
+	0xbdf24fcfU, 0xc0d111b2U, 0xedb65b9fU, 0x54603426U,
+	0x6ad3b918U, 0x81c140f3U, 0x43b1f231U, 0x254c6957U,
+	0xa2298bd0U, 0xa369cad1U, 0x21496853U, 0x4c7e323eU,
+	0x704d3d02U, 0x72cdbf00U, 0xe339da91U, 0x55207527U,
+	0xb1fd4cc3U, 0xda4f95a8U, 0x4fbef13dU, 0xdb0fd4a9U,
+	0x240c2856U, 0xa9e34adbU, 0x820183f0U, 0x1d7a676fU,
+	0x1ebaa46cU, 0x12b5a760U, 0xb9f74ecbU, 0x75087d07U,
+	0x7ac7bd08U, 0x2e86a85cU, 0xd4c014a6U, 0x91d544e3U,
+	0x5c6a362eU, 0x10352562U, 0x8341c2f1U, 0x921587e0U,
+	0x7ec2bc0cU, 0xe67c9a94U, 0xc99b52bbU, 0x6b93f819U,
+	0xc714d3b5U, 0x2fc6e95dU, 0x8744c3f5U, 0xc65492b4U,
+	0x329daf40U, 0xde4a94acU, 0xf8e71f8aU, 0x35586d47U,
+	0x2bc3e859U, 0x37d8ef45U, 0x4efeb03cU, 0x53a5f621U,
+	0xbb77ccc9U, 0x3bd7ec49U, 0x5eeab42cU, 0xa1e948d3U,
+	0xecf61a9eU, 0xbf72cdcdU, 0xa62c8ad4U, 0xe2799b90U,
+	0xc19150b3U, 0xf0ed1d82U, 0x23c9ea51U, 0xf6689e84U,
+	0x8e0e80fcU, 0x679cfb15U, 0xb778cfc5U, 0x20092952U,
+	0x3d526f4fU, 0xaca60adeU, 0x9c9a06eeU, 0xc59451b7U,
+	0x14302466U, 0x41317033U, 0x95d045e7U, 0xf4e81c86U,
+	0xbe328cccU, 0xa0a909d2U, 0x5faaf52dU, 0x69137a1bU,
+	0x315d6c43U, 0x60593912U, 0x74483c06U, 0x848400f6U,
+	0x586f372aU, 0x3a97ad48U, 0xeb33d899U, 0x39576e4bU,
+	0x3c122e4eU, 0x05646177U, 0xca5b91b8U, 0x2d466b5fU,
+	0xd18554a3U, 0x6f96f91dU, 0xd700d7a5U, 0xea739998U,
+	0x096b627bU, 0xf1ad5c83U, 0x7788ff05U, 0x7f82fd0dU,
+	0x68533b1aU, 0x8dce43ffU, 0x79077e0bU, 0x301d2d42U,
+	0xcb1bd0b9U, 0x645c3816U, 0x34182c46U, 0x07e4e375U,
+	0xb5f84dc7U, 0x268caa54U, 0x949004e6U, 0x3e92ac4cU,
+	0xbcb20eceU, 0x1bffe469U, 0x33ddee41U, 0x11756463U,
+	0x8b4bc0f9U, 0xf26d9f80U, 0x860482f4U, 0x02a1a370U,
+	0xe8f31b9aU, 0xd0c515a2U, 0x6399fa11U, 0xaa2389d8U,
+	0xc4d410b6U, 0x487b333aU, 0x85c441f7U, 0x03e1e271U,
+	0xb37dcec1U, 0x40713132U, 0x16b0a664U, 0x76c8be04U,
+	0x0aaba178U, 0x0eaea07cU, 0xd24597a0U, 0xfa679d88U,
+	0x89cb42fbU, 0xd305d6a1U, 0x01616073U, 0xf5a85d87U,
+	0x51257423U, 0xc8db13baU, 0x4d3e733fU, 0x42f1b330U,
+	0xfb27dc89U, 0xd8cf17aaU, 0xce5e90bcU, 0xba378dc8U,
+	0xc311d2b1U, 0xff22dd8dU, 0xd64096a4U, 0xdcca16aeU,
+	0x9750c7e5U, 0xb8b70fcaU, 0x7b87fc09U, 0x9dda47efU,
+	0x29436a5bU, 0x888b03faU, 0x52e5b720U, 0x493b723bU,
+	0x0bebe079U, 0x6ed6b81cU, 0xfce21e8eU, 0xf728df85U,
+	0x5d2a772fU, 0x28032b5aU, 0x6d167b1fU, 0x7d027f0fU,
+	0x99df46ebU, 0x1abfa568U, 0xe1b95893U, 0x961086e4U,
+	0x8f4ec1fdU, 0xe5bc5997U, 0x62d9bb10U, 0x46f4b234U,
+	0x2a83a958U, 0x592f762bU, 0x78473f0aU, 0x9b5fc4e9U,
+	0xa8a30bdaU, 0x06a4a274U, 0xa4ac08d6U, 0xade64bdfU,
+	0xdf0ad5adU, 0x7c423e0eU, 0x4afbb138U, 0x0c2e227eU,
+	0x57a0f725U, 0xccde12beU, 0x8a0b81f8U, 0x3fd2ed4dU,
+	0x5aefb528U, 0x04242076U, 0xc25193b0U, 0x47b4f335U,
+	0xe0f91992U, 0x909505e2U, 0x5baff429U, 0x9e1a84ecU,
+	0x10998907U, 0x09c7ce1eU, 0xec5ab6fbU, 0xb011a1a7U,
+	0x78eb936fU, 0xcc72bedbU, 0xbfde61a8U, 0x957ce982U,
+	0x13594a04U, 0x9179e886U, 0x1b53480cU, 0x40dd9d57U,
+	0xeb9f74fcU, 0xa400a4b3U, 0xfb8b70ecU, 0x5f065948U,
+	0xe79077f0U, 0xd6ec3ac1U, 0x21e5c436U, 0x92b92b85U,
+	0xe39576f4U, 0xb151e0a6U, 0x5b03584cU, 0x44d89c53U,
+	0xe115f4f6U, 0x5a43194dU, 0x07484f10U, 0xd3a97ac4U,
+	0x6e7a1479U, 0x5189d846U, 0x53095a44U, 0x2deac73aU,
+	0x39fbc22eU, 0x425d1f55U, 0x0a070d1dU, 0x9833ab8fU,
+	0x0dc2cf1aU, 0xf444b0e3U, 0x29efc63eU, 0xa28527b5U,
+	0x87e86f90U, 0x1a13090dU, 0x67305770U, 0x46581e51U,
+	0xfc4eb2ebU, 0x74e49063U, 0xf90bf2eeU, 0xc2fd3fd5U,
+	0xd863bbcfU, 0x8568ed92U, 0x3b7b402cU, 0xed1af7faU,
+	0x6cfa967bU, 0x6b3f547cU, 0xe85fb7ffU, 0x1dd6cb0aU,
+	0x4997de5eU, 0x34b48023U, 0x19d3ca0eU, 0xa005a5b7U,
+	0x9eb62889U, 0x75a4d162U, 0xb7d463a0U, 0xd129f8c6U,
+	0x564c1a41U, 0x570c5b40U, 0xd52cf9c2U, 0xb81ba3afU,
+	0x8428ac93U, 0x86a82e91U, 0x175c4b00U, 0xa145e4b6U,
+	0x4598dd52U, 0x2e2a0439U, 0xbbdb60acU, 0x2f6a4538U,
+	0xd069b9c7U, 0x5d86db4aU, 0x76641261U, 0xe91ff6feU,
+	0xeadf35fdU, 0xe6d036f1U, 0x4d92df5aU, 0x816dec96U,
+	0x8ea22c99U, 0xdae339cdU, 0x20a58537U, 0x65b0d572U,
+	0xa80fa7bfU, 0xe450b4f3U, 0x77245360U, 0x66701671U,
+	0x8aa72d9dU, 0x12190b05U, 0x3dfec32aU, 0x9ff66988U,
+	0x33714224U, 0xdba378ccU, 0x73215264U, 0x32310325U,
+	0xc6f83ed1U, 0x2a2f053dU, 0x0c828e1bU, 0xc13dfcd6U,
+	0xdfa679c8U, 0xc3bd7ed4U, 0xba9b21adU, 0xa7c067b0U,
+	0x4f125d58U, 0xcfb27dd8U, 0xaa8f25bdU, 0x558cd942U,
+	0x18938b0fU, 0x4b175c5cU, 0x52491b45U, 0x161c0a01U,
+	0x35f4c122U, 0x04888c13U, 0xd7ac7bc0U, 0x020d0f15U,
+	0x7a6b116dU, 0x93f96a84U, 0x431d5e54U, 0xd46cb8c3U,
+	0xc937fedeU, 0x58c39b4fU, 0x68ff977fU, 0x31f1c026U,
+	0xe055b5f7U, 0xb554e1a2U, 0x61b5d476U, 0x008d8d17U,
+	0x4a571d5dU, 0x54cc9843U, 0xabcf64bcU, 0x9d76eb8aU,
+	0xc538fdd2U, 0x943ca883U, 0x802dad97U, 0x70e19167U,
+	0xac0aa6bbU, 0xcef23cd9U, 0x1f564908U, 0xcd32ffdaU,
+	0xc877bfdfU, 0xf101f0e6U, 0x3e3e0029U, 0xd923faceU,
+	0x25e0c532U, 0x9bf3688cU, 0x23654634U, 0x1e160809U,
+	0xfd0ef3eaU, 0x05c8cd12U, 0x83ed6e94U, 0x8be76c9cU,
+	0x9c36aa8bU, 0x79abd26eU, 0x8d62ef9aU, 0xc478bcd3U,
+	0x3f7e4128U, 0x9039a987U, 0xc07dbdd7U, 0xf38172e4U,
+	0x419ddc56U, 0xd2e93bc5U, 0x60f59577U, 0xcaf73dddU,
+	0x48d79f5fU, 0xef9a75f8U, 0xc7b87fd0U, 0xe510f5f2U,
+	0x7f2e5168U, 0x06080e11U, 0x72611365U, 0xf6c432e1U,
+	0x1c968a0bU, 0x24a08433U, 0x97fc6b80U, 0x5e461849U,
+	0x30b18127U, 0xbc1ea2abU, 0x71a1d066U, 0xf78473e0U,
+	0x47185f50U, 0xb414a0a3U, 0xe2d537f5U, 0x82ad2f95U,
+	0xfece30e9U, 0xfacb31edU, 0x26200631U, 0x0e020c19U,
+	0x7daed36aU, 0x27604730U, 0xf504f1e2U, 0x01cdcc16U,
+	0xa540e5b2U, 0x3cbe822bU, 0xb95be2aeU, 0xb69422a1U,
+	0x0f424d18U, 0x2caa863bU, 0x3a3b012dU, 0x4e521c59U,
+	0x37744320U, 0x0b474c1cU, 0x22250735U, 0x28af873fU,
+	0x63355674U, 0x4cd29e5bU, 0x8fe26d98U, 0x69bfd67eU,
+	0xdd26fbcaU, 0x7cee926bU, 0xa68026b1U, 0xbd5ee3aaU,
+	0xff8e71e8U, 0x9ab3298dU, 0x08878f1fU, 0x034d4e14U,
+	0xa94fe6beU, 0xdc66bacbU, 0x9973ea8eU, 0x8967ee9eU,
+	0x6dbad77aU, 0xeeda34f9U, 0x15dcc902U, 0x62751775U,
+	0x7b2b506cU, 0x11d9c806U, 0x96bc2a81U, 0xb29123a5U,
+	0xdee638c9U, 0xad4ae7baU, 0x8c22ae9bU, 0x6f3a5578U,
+	0x5cc69a4bU, 0xf2c133e5U, 0x50c99947U, 0x5983da4eU,
+	0x2b6f443cU, 0x8827af9fU, 0xbe9e20a9U, 0xf84bb3efU,
+	0xa3c566b4U, 0x38bb832fU, 0x7e6e1069U, 0xcbb77cdcU,
+	0xae8a24b9U, 0xf041b1e7U, 0x36340221U, 0xb3d162a4U,
+	0x149c8803U, 0x64f09473U, 0xafca65b8U, 0x6a7f157dU,
+	0x09363fb1U, 0x106878a8U, 0xf5f5004dU, 0xa9be1711U,
+	0x614425d9U, 0xd5dd086dU, 0xa671d71eU, 0x8cd35f34U,
+	0x0af6fcb2U, 0x88d65e30U, 0x02fcfebaU, 0x59722be1U,
+	0xf230c24aU, 0xbdaf1205U, 0xe224c65aU, 0x46a9effeU,
+	0xfe3fc146U, 0xcf438c77U, 0x384a7280U, 0x8b169d33U,
+	0xfa3ac042U, 0xa8fe5610U, 0x42aceefaU, 0x5d772ae5U,
+	0xf8ba4240U, 0x43ecaffbU, 0x1ee7f9a6U, 0xca06cc72U,
+	0x77d5a2cfU, 0x48266ef0U, 0x4aa6ecf2U, 0x3445718cU,
+	0x20547498U, 0x5bf2a9e3U, 0x13a8bbabU, 0x819c1d39U,
+	0x146d79acU, 0xedeb0655U, 0x30407088U, 0xbb2a9103U,
+	0x9e47d926U, 0x03bcbfbbU, 0x7e9fe1c6U, 0x5ff7a8e7U,
+	0xe5e1045dU, 0x6d4b26d5U, 0xe0a44458U, 0xdb528963U,
+	0xc1cc0d79U, 0x9cc75b24U, 0x22d4f69aU, 0xf4b5414cU,
+	0x755520cdU, 0x7290e2caU, 0xf1f00149U, 0x04797dbcU,
+	0x503868e8U, 0x2d1b3695U, 0x007c7cb8U, 0xb9aa1301U,
+	0x87199e3fU, 0x6c0b67d4U, 0xae7bd516U, 0xc8864e70U,
+	0x4fe3acf7U, 0x4ea3edf6U, 0xcc834f74U, 0xa1b41519U,
+	0x9d871a25U, 0x9f079827U, 0x0ef3fdb6U, 0xb8ea5200U,
+	0x5c376be4U, 0x3785b28fU, 0xa274d61aU, 0x36c5f38eU,
+	0xc9c60f71U, 0x44296dfcU, 0x6fcba4d7U, 0xf0b04048U,
+	0xf370834bU, 0xff7f8047U, 0x543d69ecU, 0x98c25a20U,
+	0x970d9a2fU, 0xc34c8f7bU, 0x390a3381U, 0x7c1f63c4U,
+	0xb1a01109U, 0xfdff0245U, 0x6e8be5d6U, 0x7fdfa0c7U,
+	0x93089b2bU, 0x0bb6bdb3U, 0x2451759cU, 0x8659df3eU,
+	0x2adef492U, 0xc20cce7aU, 0x6a8ee4d2U, 0x2b9eb593U,
+	0xdf578867U, 0x3380b38bU, 0x152d38adU, 0xd8924a60U,
+	0xc609cf7eU, 0xda12c862U, 0xa334971bU, 0xbe6fd106U,
+	0x56bdebeeU, 0xd61dcb6eU, 0xb320930bU, 0x4c236ff4U,
+	0x013c3db9U, 0x52b8eaeaU, 0x4be6adf3U, 0x0fb3bcb7U,
+	0x2c5b7794U, 0x1d273aa5U, 0xce03cd76U, 0x1ba2b9a3U,
+	0x63c4a7dbU, 0x8a56dc32U, 0x5ab2e8e2U, 0xcdc30e75U,
+	0xd0984868U, 0x416c2df9U, 0x715021c9U, 0x285e7690U,
+	0xf9fa0341U, 0xacfb5714U, 0x781a62c0U, 0x19223ba1U,
+	0x53f8abebU, 0x4d632ef5U, 0xb260d20aU, 0x84d95d3cU,
+	0xdc974b64U, 0x8d931e35U, 0x99821b21U, 0x694e27d1U,
+	0xb5a5100dU, 0xd75d8a6fU, 0x06f9ffbeU, 0xd49d496cU,
+	0xd1d80969U, 0xe8ae4650U, 0x2791b69fU, 0xc08c4c78U,
+	0x3c4f7384U, 0x825cde3aU, 0x3acaf082U, 0x07b9bebfU,
+	0xe4a1455cU, 0x1c677ba4U, 0x9a42d822U, 0x9248da2aU,
+	0x85991c3dU, 0x600464d8U, 0x94cd592cU, 0xddd70a65U,
+	0x26d1f79eU, 0x89961f31U, 0xd9d20b61U, 0xea2ec452U,
+	0x58326ae0U, 0xcb468d73U, 0x795a23c1U, 0xd3588b6bU,
+	0x517829e9U, 0xf635c34eU, 0xde17c966U, 0xfcbf4344U,
+	0x6681e7deU, 0x1fa7b8a7U, 0x6bcea5d3U, 0xef6b8457U,
+	0x05393cbdU, 0x3d0f3285U, 0x8e53dd36U, 0x47e9aeffU,
+	0x291e3791U, 0xa5b1141dU, 0x680e66d0U, 0xee2bc556U,
+	0x5eb7e9e6U, 0xadbb1615U, 0xfb7a8143U, 0x9b029923U,
+	0xe761865fU, 0xe364875bU, 0x3f8fb087U, 0x17adbaafU,
+	0x640165dcU, 0x3ecff186U, 0xecab4754U, 0x18627aa0U,
+	0xbcef5304U, 0x2511349dU, 0xa0f45418U, 0xaf3b9417U,
+	0x16edfbaeU, 0x3505308dU, 0x2394b79bU, 0x57fdaaefU,
+	0x2edbf596U, 0x12e8faaaU, 0x3b8ab183U, 0x31003189U,
+	0x7a9ae0c2U, 0x557d28edU, 0x964ddb2eU, 0x701060c8U,
+	0xc4894d7cU, 0x654124ddU, 0xbf2f9007U, 0xa4f1551cU,
+	0xe621c75eU, 0x831c9f3bU, 0x112839a9U, 0x1ae2f8a2U,
+	0xb0e05008U, 0xc5c90c7dU, 0x80dc5c38U, 0x90c85828U,
+	0x741561ccU, 0xf775824fU, 0x0c737fb4U, 0x7bdaa1c3U,
+	0x6284e6daU, 0x08767eb0U, 0x8f139c37U, 0xab3e9513U,
+	0xc7498e7fU, 0xb4e5510cU, 0x958d182dU, 0x7695e3ceU,
+	0x45692cfdU, 0xeb6e8553U, 0x49662ff1U, 0x402c6cf8U,
+	0x32c0f28aU, 0x91881929U, 0xa731961fU, 0xe1e40559U,
+	0xba6ad002U, 0x21143599U, 0x67c1a6dfU, 0xd218ca6aU,
+	0xb725920fU, 0xe9ee0751U, 0x2f9bb497U, 0xaa7ed412U,
+	0x0d333eb5U, 0x7d5f22c5U, 0xb665d30eU, 0x73d0a3cbU,
+	0xd40fdb55U, 0xcd519c4cU, 0x28cce4a9U, 0x7487f3f5U,
+	0xbc7dc13dU, 0x08e4ec89U, 0x7b4833faU, 0x51eabbd0U,
+	0xd7cf1856U, 0x55efbad4U, 0xdfc51a5eU, 0x844bcf05U,
+	0x2f0926aeU, 0x6096f6e1U, 0x3f1d22beU, 0x9b900b1aU,
+	0x230625a2U, 0x127a6893U, 0xe5739664U, 0x562f79d7U,
+	0x270324a6U, 0x75c7b2f4U, 0x9f950a1eU, 0x804ece01U,
+	0x2583a6a4U, 0x9ed54b1fU, 0xc3de1d42U, 0x173f2896U,
+	0xaaec462bU, 0x951f8a14U, 0x979f0816U, 0xe97c9568U,
+	0xfd6d907cU, 0x86cb4d07U, 0xce915f4fU, 0x5ca5f9ddU,
+	0xc9549d48U, 0x30d2e2b1U, 0xed79946cU, 0x661375e7U,
+	0x437e3dc2U, 0xde855b5fU, 0xa3a60522U, 0x82ce4c03U,
+	0x38d8e0b9U, 0xb072c231U, 0x3d9da0bcU, 0x066b6d87U,
+	0x1cf5e99dU, 0x41febfc0U, 0xffed127eU, 0x298ca5a8U,
+	0xa86cc429U, 0xafa9062eU, 0x2cc9e5adU, 0xd9409958U,
+	0x8d018c0cU, 0xf022d271U, 0xdd45985cU, 0x6493f7e5U,
+	0x5a207adbU, 0xb1328330U, 0x734231f2U, 0x15bfaa94U,
+	0x92da4813U, 0x939a0912U, 0x11baab90U, 0x7c8df1fdU,
+	0x40befec1U, 0x423e7cc3U, 0xd3ca1952U, 0x65d3b6e4U,
+	0x810e8f00U, 0xeabc566bU, 0x7f4d32feU, 0xebfc176aU,
+	0x14ffeb95U, 0x99108918U, 0xb2f24033U, 0x2d89a4acU,
+	0x2e4967afU, 0x224664a3U, 0x89048d08U, 0x45fbbec4U,
+	0x4a347ecbU, 0x1e756b9fU, 0xe433d765U, 0xa1268720U,
+	0x6c99f5edU, 0x20c6e6a1U, 0xb3b20132U, 0xa2e64423U,
+	0x4e317fcfU, 0xd68f5957U, 0xf9689178U, 0x5b603bdaU,
+	0xf7e71076U, 0x1f352a9eU, 0xb7b70036U, 0xf6a75177U,
+	0x026e6c83U, 0xeeb9576fU, 0xc814dc49U, 0x05abae84U,
+	0x1b302b9aU, 0x072b2c86U, 0x7e0d73ffU, 0x635635e2U,
+	0x8b840f0aU, 0x0b242f8aU, 0x6e1977efU, 0x911a8b10U,
+	0xdc05d95dU, 0x8f810e0eU, 0x96df4917U, 0xd28a5853U,
+	0xf1629370U, 0xc01ede41U, 0x133a2992U, 0xc69b5d47U,
+	0xbefd433fU, 0x576f38d6U, 0x878b0c06U, 0x10faea91U,
+	0x0da1ac8cU, 0x9c55c91dU, 0xac69c52dU, 0xf5679274U,
+	0x24c3e7a5U, 0x71c2b3f0U, 0xa5238624U, 0xc41bdf45U,
+	0x8ec14f0fU, 0x905aca11U, 0x6f5936eeU, 0x59e0b9d8U,
+	0x01aeaf80U, 0x50aafad1U, 0x44bbffc5U, 0xb477c335U,
+	0x689cf4e9U, 0x0a646e8bU, 0xdbc01b5aU, 0x09a4ad88U,
+	0x0ce1ed8dU, 0x3597a2b4U, 0xfaa8527bU, 0x1db5a89cU,
+	0xe1769760U, 0x5f653adeU, 0xe7f31466U, 0xda805a5bU,
+	0x3998a1b8U, 0xc15e9f40U, 0x477b3cc6U, 0x4f713eceU,
+	0x58a0f8d9U, 0xbd3d803cU, 0x49f4bdc8U, 0x00eeee81U,
+	0xfbe8137aU, 0x54affbd5U, 0x04ebef85U, 0x371720b6U,
+	0x850b8e04U, 0x167f6997U, 0xa463c725U, 0x0e616f8fU,
+	0x8c41cd0dU, 0x2b0c27aaU, 0x032e2d82U, 0x2186a7a0U,
+	0xbbb8033aU, 0xc29e5c43U, 0xb6f74137U, 0x325260b3U,
+	0xd800d859U, 0xe036d661U, 0x536a39d2U, 0x9ad04a1bU,
+	0xf427d375U, 0x7888f0f9U, 0xb5378234U, 0x331221b2U,
+	0x838e0d02U, 0x7082f2f1U, 0x264365a7U, 0x463b7dc7U,
+	0x3a5862bbU, 0x3e5d63bfU, 0xe2b65463U, 0xca945e4bU,
+	0xb9388138U, 0xe3f61562U, 0x3192a3b0U, 0xc55b9e44U,
+	0x61d6b7e0U, 0xf828d079U, 0x7dcdb0fcU, 0x720270f3U,
+	0xcbd41f4aU, 0xe83cd469U, 0xfead537fU, 0x8ac44e0bU,
+	0xf3e21172U, 0xcfd11e4eU, 0xe6b35567U, 0xec39d56dU,
+	0xa7a30426U, 0x8844cc09U, 0x4b743fcaU, 0xad29842cU,
+	0x19b0a998U, 0xb878c039U, 0x621674e3U, 0x79c8b1f8U,
+	0x3b1823baU, 0x5e257bdfU, 0xcc11dd4dU, 0xc7db1c46U,
+	0x6dd9b4ecU, 0x18f0e899U, 0x5de5b8dcU, 0x4df1bcccU,
+	0xa92c8528U, 0x2a4c66abU, 0xd14a9b50U, 0xa6e34527U,
+	0xbfbd023eU, 0xd54f9a54U, 0x522a78d3U, 0x760771f7U,
+	0x1a706a9bU, 0x69dcb5e8U, 0x48b4fcc9U, 0xabac072aU,
+	0x9850c819U, 0x365761b7U, 0x945fcb15U, 0x9d15881cU,
+	0xeff9166eU, 0x4cb1fdcdU, 0x7a0872fbU, 0x3cdde1bdU,
+	0x675334e6U, 0xfc2dd17dU, 0xbaf8423bU, 0x0f212e8eU,
+	0x6a1c76ebU, 0x34d7e3b5U, 0xf2a25073U, 0x774730f6U,
+	0xd00ada51U, 0xa066c621U, 0x6b5c37eaU, 0xaee9472fU,
+	0x66d6b03eU, 0x7f88f727U, 0x9a158fc2U, 0xc65e989eU,
+	0x0ea4aa56U, 0xba3d87e2U, 0xc9915891U, 0xe333d0bbU,
+	0x6516733dU, 0xe736d1bfU, 0x6d1c7135U, 0x3692a46eU,
+	0x9dd04dc5U, 0xd24f9d8aU, 0x8dc449d5U, 0x29496071U,
+	0x91df4ec9U, 0xa0a303f8U, 0x57aafd0fU, 0xe4f612bcU,
+	0x95da4fcdU, 0xc71ed99fU, 0x2d4c6175U, 0x3297a56aU,
+	0x975acdcfU, 0x2c0c2074U, 0x71077629U, 0xa5e643fdU,
+	0x18352d40U, 0x27c6e17fU, 0x2546637dU, 0x5ba5fe03U,
+	0x4fb4fb17U, 0x3412266cU, 0x7c483424U, 0xee7c92b6U,
+	0x7b8df623U, 0x820b89daU, 0x5fa0ff07U, 0xd4ca1e8cU,
+	0xf1a756a9U, 0x6c5c3034U, 0x117f6e49U, 0x30172768U,
+	0x8a018bd2U, 0x02aba95aU, 0x8f44cbd7U, 0xb4b206ecU,
+	0xae2c82f6U, 0xf327d4abU, 0x4d347915U, 0x9b55cec3U,
+	0x1ab5af42U, 0x1d706d45U, 0x9e108ec6U, 0x6b99f233U,
+	0x3fd8e767U, 0x42fbb91aU, 0x6f9cf337U, 0xd64a9c8eU,
+	0xe8f911b0U, 0x03ebe85bU, 0xc19b5a99U, 0xa766c1ffU,
+	0x20032378U, 0x21436279U, 0xa363c0fbU, 0xce549a96U,
+	0xf26795aaU, 0xf0e717a8U, 0x61137239U, 0xd70add8fU,
+	0x33d7e46bU, 0x58653d00U, 0xcd945995U, 0x59257c01U,
+	0xa62680feU, 0x2bc9e273U, 0x002b2b58U, 0x9f50cfc7U,
+	0x9c900cc4U, 0x909f0fc8U, 0x3bdde663U, 0xf722d5afU,
+	0xf8ed15a0U, 0xacac00f4U, 0x56eabc0eU, 0x13ffec4bU,
+	0xde409e86U, 0x921f8dcaU, 0x016b6a59U, 0x103f2f48U,
+	0xfce814a4U, 0x6456323cU, 0x4bb1fa13U, 0xe9b950b1U,
+	0x453e7b1dU, 0xadec41f5U, 0x056e6b5dU, 0x447e3a1cU,
+	0xb0b707e8U, 0x5c603c04U, 0x7acdb722U, 0xb772c5efU,
+	0xa9e940f1U, 0xb5f247edU, 0xccd41894U, 0xd18f5e89U,
+	0x395d6461U, 0xb9fd44e1U, 0xdcc01c84U, 0x23c3e07bU,
+	0x6edcb236U, 0x3d586565U, 0x2406227cU, 0x60533338U,
+	0x43bbf81bU, 0x72c7b52aU, 0xa1e342f9U, 0x7442362cU,
+	0x0c242854U, 0xe5b653bdU, 0x3552676dU, 0xa22381faU,
+	0xbf78c7e7U, 0x2e8ca276U, 0x1eb0ae46U, 0x47bef91fU,
+	0x961a8cceU, 0xc31bd89bU, 0x17faed4fU, 0x76c2b42eU,
+	0x3c182464U, 0x2283a17aU, 0xdd805d85U, 0xeb39d2b3U,
+	0xb377c4ebU, 0xe27391baU, 0xf66294aeU, 0x06aea85eU,
+	0xda459f82U, 0xb8bd05e0U, 0x69197031U, 0xbb7dc6e3U,
+	0xbe3886e6U, 0x874ec9dfU, 0x48713910U, 0xaf6cc3f7U,
+	0x53affc0bU, 0xedbc51b5U, 0x552a7f0dU, 0x68593130U,
+	0x8b41cad3U, 0x7387f42bU, 0xf5a257adU, 0xfda855a5U,
+	0xea7993b2U, 0x0fe4eb57U, 0xfb2dd6a3U, 0xb23785eaU,
+	0x49317811U, 0xe67690beU, 0xb63284eeU, 0x85ce4bddU,
+	0x37d2e56fU, 0xa4a602fcU, 0x16baac4eU, 0xbcb804e4U,
+	0x3e98a666U, 0x99d54cc1U, 0xb1f746e9U, 0x935fcccbU,
+	0x09616851U, 0x70473728U, 0x042e2a5cU, 0x808b0bd8U,
+	0x6ad9b332U, 0x52efbd0aU, 0xe1b352b9U, 0x28092170U,
+	0x46feb81eU, 0xca519b92U, 0x07eee95fU, 0x81cb4ad9U,
+	0x31576669U, 0xc25b999aU, 0x949a0eccU, 0xf4e216acU,
+	0x888109d0U, 0x8c8408d4U, 0x506f3f08U, 0x784d3520U,
+	0x0be1ea53U, 0x512f7e09U, 0x834bc8dbU, 0x7782f52fU,
+	0xd30fdc8bU, 0x4af1bb12U, 0xcf14db97U, 0xc0db1b98U,
+	0x790d7421U, 0x5ae5bf02U, 0x4c743814U, 0x381d2560U,
+	0x413b7a19U, 0x7d087525U, 0x546a3e0cU, 0x5ee0be06U,
+	0x157a6f4dU, 0x3a9da762U, 0xf9ad54a1U, 0x1ff0ef47U,
+	0xab69c2f3U, 0x0aa1ab52U, 0xd0cf1f88U, 0xcb11da93U,
+	0x89c148d1U, 0xecfc10b4U, 0x7ec8b626U, 0x7502772dU,
+	0xdf00df87U, 0xaa2983f2U, 0xef3cd3b7U, 0xff28d7a7U,
+	0x1bf5ee43U, 0x98950dc0U, 0x6393f03bU, 0x143a2e4cU,
+	0x0d646955U, 0x6796f13fU, 0xe0f313b8U, 0xc4de1a9cU,
+	0xa8a901f0U, 0xdb05de83U, 0xfa6d97a2U, 0x19756c41U,
+	0x2a89a372U, 0x848e0adcU, 0x2686a07eU, 0x2fcce377U,
+	0x5d207d05U, 0xfe6896a6U, 0xc8d11990U, 0x8e048ad6U,
+	0xd58a5f8dU, 0x4ef4ba16U, 0x08212950U, 0xbdf845e5U,
+	0xd8c51d80U, 0x860e88deU, 0x407b3b18U, 0xc59e5b9dU,
+	0x62d3b13aU, 0x12bfad4aU, 0xd9855c81U, 0x1c302c44U,
+	0x97b225abU, 0x8eec62b2U, 0x6b711a57U, 0x373a0d0bU,
+	0xffc03fc3U, 0x4b591277U, 0x38f5cd04U, 0x1257452eU,
+	0x9472e6a8U, 0x1652442aU, 0x9c78e4a0U, 0xc7f631fbU,
+	0x6cb4d850U, 0x232b081fU, 0x7ca0dc40U, 0xd82df5e4U,
+	0x60bbdb5cU, 0x51c7966dU, 0xa6ce689aU, 0x15928729U,
+	0x64beda58U, 0x367a4c0aU, 0xdc28f4e0U, 0xc3f330ffU,
+	0x663e585aU, 0xdd68b5e1U, 0x8063e3bcU, 0x5482d668U,
+	0xe951b8d5U, 0xd6a274eaU, 0xd422f6e8U, 0xaac16b96U,
+	0xbed06e82U, 0xc576b3f9U, 0x8d2ca1b1U, 0x1f180723U,
+	0x8ae963b6U, 0x736f1c4fU, 0xaec46a92U, 0x25ae8b19U,
+	0x00c3c33cU, 0x9d38a5a1U, 0xe01bfbdcU, 0xc173b2fdU,
+	0x7b651e47U, 0xf3cf3ccfU, 0x7e205e42U, 0x45d69379U,
+	0x5f481763U, 0x0243413eU, 0xbc50ec80U, 0x6a315b56U,
+	0xebd13ad7U, 0xec14f8d0U, 0x6f741b53U, 0x9afd67a6U,
+	0xcebc72f2U, 0xb39f2c8fU, 0x9ef866a2U, 0x272e091bU,
+	0x199d8425U, 0xf28f7dceU, 0x30ffcf0cU, 0x5602546aU,
+	0xd167b6edU, 0xd027f7ecU, 0x5207556eU, 0x3f300f03U,
+	0x0303003fU, 0x0183823dU, 0x9077e7acU, 0x266e481aU,
+	0xc2b371feU, 0xa901a895U, 0x3cf0cc00U, 0xa841e994U,
+	0x5742156bU, 0xdaad77e6U, 0xf14fbecdU, 0x6e345a52U,
+	0x6df49951U, 0x61fb9a5dU, 0xcab973f6U, 0x0646403aU,
+	0x09898035U, 0x5dc89561U, 0xa78e299bU, 0xe29b79deU,
+	0x2f240b13U, 0x637b185fU, 0xf00fffccU, 0xe15bbaddU,
+	0x0d8c8131U, 0x9532a7a9U, 0xbad56f86U, 0x18ddc524U,
+	0xb45aee88U, 0x5c88d460U, 0xf40afec8U, 0xb51aaf89U,
+	0x41d3927dU, 0xad04a991U, 0x8ba922b7U, 0x4616507aU,
+	0x588dd564U, 0x4496d278U, 0x3db08d01U, 0x20ebcb1cU,
+	0xc839f1f4U, 0x4899d174U, 0x2da48911U, 0xd2a775eeU,
+	0x9fb827a3U, 0xcc3cf0f0U, 0xd562b7e9U, 0x9137a6adU,
+	0xb2df6d8eU, 0x83a320bfU, 0x5087d76cU, 0x8526a3b9U,
+	0xfd40bdc1U, 0x14d2c628U, 0xc436f2f8U, 0x5347146fU,
+	0x4e1c5272U, 0xdfe837e3U, 0xefd43bd3U, 0xb6da6c8aU,
+	0x677e195bU, 0x327f4d0eU, 0xe69e78daU, 0x87a621bbU,
+	0xcd7cb1f1U, 0xd3e734efU, 0x2ce4c810U, 0x1a5d4726U,
+	0x4213517eU, 0x1317042fU, 0x0706013bU, 0xf7ca3dcbU,
+	0x2b210a17U, 0x49d99075U, 0x987de5a4U, 0x4a195376U,
+	0x4f5c1373U, 0x762a5c4aU, 0xb915ac85U, 0x5e085662U,
+	0xa2cb699eU, 0x1cd8c420U, 0xa44eea98U, 0x993da4a5U,
+	0x7a255f46U, 0x82e361beU, 0x04c6c238U, 0x0cccc030U,
+	0x1b1d0627U, 0xfe807ec2U, 0x0a494336U, 0x4353107fU,
+	0xb855ed84U, 0x1712052bU, 0x4756117bU, 0x74aade48U,
+	0xc6b670faU, 0x55c29769U, 0xe7de39dbU, 0x4ddc9171U,
+	0xcffc33f3U, 0x68b1d954U, 0x4093d37cU, 0x623b595eU,
+	0xf805fdc4U, 0x8123a2bdU, 0xf54abfc9U, 0x71ef9e4dU,
+	0x9bbd26a7U, 0xa38b289fU, 0x10d7c72cU, 0xd96db4e5U,
+	0xb79a2d8bU, 0x3b350e07U, 0xf68a7ccaU, 0x70afdf4cU,
+	0xc033f3fcU, 0x333f0c0fU, 0x65fe9b59U, 0x05868339U,
+	0x79e59c45U, 0x7de09d41U, 0xa10baa9dU, 0x8929a0b5U,
+	0xfa857fc6U, 0xa04beb9cU, 0x722f5d4eU, 0x86e660baU,
+	0x226b491eU, 0xbb952e87U, 0x3e704e02U, 0x31bf8e0dU,
+	0x8869e1b4U, 0xab812a97U, 0xbd10ad81U, 0xc979b0f5U,
+	0xb05fef8cU, 0x8c6ce0b0U, 0xa50eab99U, 0xaf842b93U,
+	0xe41efad8U, 0xcbf932f7U, 0x08c9c134U, 0xee947ad2U,
+	0x5a0d5766U, 0xfbc53ec7U, 0x21ab8a1dU, 0x3a754f06U,
+	0x78a5dd44U, 0x1d988521U, 0x8fac23b3U, 0x8466e2b8U,
+	0x2e644a12U, 0x5b4d1667U, 0x1e584622U, 0x0e4c4232U,
+	0xea917bd6U, 0x69f19855U, 0x92f765aeU, 0xe55ebbd9U,
+	0xfc00fcc0U, 0x96f264aaU, 0x1197862dU, 0x35ba8f09U,
+	0x59cd9465U, 0x2a614b16U, 0x0b090237U, 0xe811f9d4U,
+	0xdbed36e7U, 0x75ea9f49U, 0xd7e235ebU, 0xdea876e2U,
+	0xac44e890U, 0x0f0c0333U, 0x39b58c05U, 0x7f601f43U,
+	0x24eeca18U, 0xbf902f83U, 0xf945bcc5U, 0x4c9cd070U,
+	0x29a18815U, 0x776a1d4bU, 0xb11fae8dU, 0x34face08U,
+	0x93b724afU, 0xe3db38dfU, 0x28e1c914U, 0xed54b9d1U,
+	0x2697b13fU, 0x3fc9f626U, 0xda548ec3U, 0x861f999fU,
+	0x4ee5ab57U, 0xfa7c86e3U, 0x89d05990U, 0xa372d1baU,
+	0x2557723cU, 0xa777d0beU, 0x2d5d7034U, 0x76d3a56fU,
+	0xdd914cc4U, 0x920e9c8bU, 0xcd8548d4U, 0x69086170U,
+	0xd19e4fc8U, 0xe0e202f9U, 0x17ebfc0eU, 0xa4b713bdU,
+	0xd59b4eccU, 0x875fd89eU, 0x6d0d6074U, 0x72d6a46bU,
+	0xd71bccceU, 0x6c4d2175U, 0x31467728U, 0xe5a742fcU,
+	0x58742c41U, 0x6787e07eU, 0x6507627cU, 0x1be4ff02U,
+	0x0ff5fa16U, 0x7453276dU, 0x3c093525U, 0xae3d93b7U,
+	0x3bccf722U, 0xc24a88dbU, 0x1fe1fe06U, 0x948b1f8dU,
+	0xb1e657a8U, 0x2c1d3135U, 0x513e6f48U, 0x70562669U,
+	0xca408ad3U, 0x42eaa85bU, 0xcf05cad6U, 0xf4f307edU,
+	0xee6d83f7U, 0xb366d5aaU, 0x0d757814U, 0xdb14cfc2U,
+	0x5af4ae43U, 0x5d316c44U, 0xde518fc7U, 0x2bd8f332U,
+	0x7f99e666U, 0x02bab81bU, 0x2fddf236U, 0x960b9d8fU,
+	0xa8b810b1U, 0x43aae95aU, 0x81da5b98U, 0xe727c0feU,
+	0x60422279U, 0x61026378U, 0xe322c1faU, 0x8e159b97U,
+	0xb22694abU, 0xb0a616a9U, 0x21527338U, 0x974bdc8eU,
+	0x7396e56aU, 0x18243c01U, 0x8dd55894U, 0x19647d00U,
+	0xe66781ffU, 0x6b88e372U, 0x406a2a59U, 0xdf11cec6U,
+	0xdcd10dc5U, 0xd0de0ec9U, 0x7b9ce762U, 0xb763d4aeU,
+	0xb8ac14a1U, 0xeced01f5U, 0x16abbd0fU, 0x53beed4aU,
+	0x9e019f87U, 0xd25e8ccbU, 0x412a6b58U, 0x507e2e49U,
+	0xbca915a5U, 0x2417333dU, 0x0bf0fb12U, 0xa9f851b0U,
+	0x057f7a1cU, 0xedad40f4U, 0x452f6a5cU, 0x043f3b1dU,
+	0xf0f606e9U, 0x1c213d05U, 0x3a8cb623U, 0xf733c4eeU,
+	0xe9a841f0U, 0xf5b346ecU, 0x8c951995U, 0x91ce5f88U,
+	0x791c6560U, 0xf9bc45e0U, 0x9c811d85U, 0x6382e17aU,
+	0x2e9db337U, 0x7d196464U, 0x6447237dU, 0x20123239U,
+	0x03faf91aU, 0x3286b42bU, 0xe1a243f8U, 0x3403372dU,
+	0x4c652955U, 0xa5f752bcU, 0x7513666cU, 0xe26280fbU,
+	0xff39c6e6U, 0x6ecda377U, 0x5ef1af47U, 0x07fff81eU,
+	0xd65b8dcfU, 0x835ad99aU, 0x57bbec4eU, 0x3683b52fU,
+	0x7c592565U, 0x62c2a07bU, 0x9dc15c84U, 0xab78d3b2U,
+	0xf336c5eaU, 0xa23290bbU, 0xb62395afU, 0x46efa95fU,
+	0x9a049e83U, 0xf8fc04e1U, 0x29587130U, 0xfb3cc7e2U,
+	0xfe7987e7U, 0xc70fc8deU, 0x08303811U, 0xef2dc2f6U,
+	0x13eefd0aU, 0xadfd50b4U, 0x156b7e0cU, 0x28183031U,
+	0xcb00cbd2U, 0x33c6f52aU, 0xb5e356acU, 0xbde954a4U,
+	0xaa3892b3U, 0x4fa5ea56U, 0xbb6cd7a2U, 0xf27684ebU,
+	0x09707910U, 0xa63791bfU, 0xf67385efU, 0xc58f4adcU,
+	0x7793e46eU, 0xe4e703fdU, 0x56fbad4fU, 0xfcf905e5U,
+	0x7ed9a767U, 0xd9944dc0U, 0xf1b647e8U, 0xd31ecdcaU,
+	0x49206950U, 0x30063629U, 0x446f2b5dU, 0xc0ca0ad9U,
+	0x2a98b233U, 0x12aebc0bU, 0xa1f253b8U, 0x68482071U,
+	0x06bfb91fU, 0x8a109a93U, 0x47afe85eU, 0xc18a4bd8U,
+	0x71166768U, 0x821a989bU, 0xd4db0fcdU, 0xb4a317adU,
+	0xc8c008d1U, 0xccc509d5U, 0x102e3e09U, 0x380c3421U,
+	0x4ba0eb52U, 0x116e7f08U, 0xc30ac9daU, 0x37c3f42eU,
+	0x934edd8aU, 0x0ab0ba13U, 0x8f55da96U, 0x809a1a99U,
+	0x394c7520U, 0x1aa4be03U, 0x0c353915U, 0x785c2461U,
+	0x017a7b18U, 0x3d497424U, 0x142b3f0dU, 0x1ea1bf07U,
+	0x553b6e4cU, 0x7adca663U, 0xb9ec55a0U, 0x5fb1ee46U,
+	0xeb28c3f2U, 0x4ae0aa53U, 0x908e1e89U, 0x8b50db92U,
+	0xc98049d0U, 0xacbd11b5U, 0x3e89b727U, 0x3543762cU,
+	0x9f41de86U, 0xea6882f3U, 0xaf7dd2b6U, 0xbf69d6a6U,
+	0x5bb4ef42U, 0xd8d40cc1U, 0x23d2f13aU, 0x547b2f4dU,
+	0x4d256854U, 0x27d7f03eU, 0xa0b212b9U, 0x849f1b9dU,
+	0xe8e800f1U, 0x9b44df82U, 0xba2c96a3U, 0x59346d40U,
+	0x6ac8a273U, 0xc4cf0bddU, 0x66c7a17fU, 0x6f8de276U,
+	0x1d617c04U, 0xbe2997a7U, 0x88901891U, 0xce458bd7U,
+	0x95cb5e8cU, 0x0eb5bb17U, 0x48602851U, 0xfdb944e4U,
+	0x98841c81U, 0xc64f89dfU, 0x003a3a19U, 0x85df5a9cU,
+	0x2292b03bU, 0x52feac4bU, 0x99c45d80U, 0x5c712d45U,
+	0x256b4ec0U, 0x3c3509d9U, 0xd9a8713cU, 0x85e36660U,
+	0x4d1954a8U, 0xf980791cU, 0x8a2ca66fU, 0xa08e2e45U,
+	0x26ab8dc3U, 0xa48b2f41U, 0x2ea18fcbU, 0x752f5a90U,
+	0xde6db33bU, 0x91f26374U, 0xce79b72bU, 0x6af49e8fU,
+	0xd262b037U, 0xe31efd06U, 0x141703f1U, 0xa74bec42U,
+	0xd667b133U, 0x84a32761U, 0x6ef19f8bU, 0x712a5b94U,
+	0xd4e73331U, 0x6fb1de8aU, 0x32ba88d7U, 0xe65bbd03U,
+	0x5b88d3beU, 0x647b1f81U, 0x66fb9d83U, 0x181800fdU,
+	0x0c0905e9U, 0x77afd892U, 0x3ff5cadaU, 0xadc16c48U,
+	0x383008ddU, 0xc1b67724U, 0x1c1d01f9U, 0x9777e072U,
+	0xb21aa857U, 0x2fe1cecaU, 0x52c290b7U, 0x73aad996U,
+	0xc9bc752cU, 0x411657a4U, 0xccf93529U, 0xf70ff812U,
+	0xed917c08U, 0xb09a2a55U, 0x0e8987ebU, 0xd8e8303dU,
+	0x590851bcU, 0x5ecd93bbU, 0xddad7038U, 0x28240ccdU,
+	0x7c651999U, 0x014647e4U, 0x2c210dc9U, 0x95f76270U,
+	0xab44ef4eU, 0x405616a5U, 0x8226a467U, 0xe4db3f01U,
+	0x63bedd86U, 0x62fe9c87U, 0xe0de3e05U, 0x8de96468U,
+	0xb1da6b54U, 0xb35ae956U, 0x22ae8cc7U, 0x94b72371U,
+	0x706a1a95U, 0x1bd8c3feU, 0x8e29a76bU, 0x1a9882ffU,
+	0xe59b7e00U, 0x68741c8dU, 0x4396d5a6U, 0xdced3139U,
+	0xdf2df23aU, 0xd322f136U, 0x7860189dU, 0xb49f2b51U,
+	0xbb50eb5eU, 0xef11fe0aU, 0x155742f0U, 0x504212b5U,
+	0x9dfd6078U, 0xd1a27334U, 0x42d694a7U, 0x5382d1b6U,
+	0xbf55ea5aU, 0x27ebccc2U, 0x080c04edU, 0xaa04ae4fU,
+	0x068385e3U, 0xee51bf0bU, 0x46d395a3U, 0x07c3c4e2U,
+	0xf30af916U, 0x1fddc2faU, 0x397049dcU, 0xf4cf3b11U,
+	0xea54be0fU, 0xf64fb913U, 0x8f69e66aU, 0x9232a077U,
+	0x7ae09a9fU, 0xfa40ba1fU, 0x9f7de27aU, 0x607e1e85U,
+	0x2d614cc8U, 0x7ee59b9bU, 0x67bbdc82U, 0x23eecdc6U,
+	0x000606e5U, 0x317a4bd4U, 0xe25ebc07U, 0x37ffc8d2U,
+	0x4f99d6aaU, 0xa60bad43U, 0x76ef9993U, 0xe19e7f04U,
+	0xfcc53919U, 0x6d315c88U, 0x5d0d50b8U, 0x040307e1U,
+	0xd5a77230U, 0x80a62665U, 0x544713b1U, 0x357f4ad0U,
+	0x7fa5da9aU, 0x613e5f84U, 0x9e3da37bU, 0xa8842c4dU,
+	0xf0ca3a15U, 0xa1ce6f44U, 0xb5df6a50U, 0x451356a0U,
+	0x99f8617cU, 0xfb00fb1eU, 0x2aa48ecfU, 0xf8c0381dU,
+	0xfd857818U, 0xc4f33721U, 0x0bccc7eeU, 0xecd13d09U,
+	0x101202f5U, 0xae01af4bU, 0x169781f3U, 0x2be4cfceU,
+	0xc8fc342dU, 0x303a0ad5U, 0xb61fa953U, 0xbe15ab5bU,
+	0xa9c46d4cU, 0x4c5915a9U, 0xb890285dU, 0xf18a7b14U,
+	0x0a8c86efU, 0xa5cb6e40U, 0xf58f7a10U, 0xc673b523U,
+	0x746f1b91U, 0xe71bfc02U, 0x550752b0U, 0xff05fa1aU,
+	0x7d255898U, 0xda68b23fU, 0xf24ab817U, 0xd0e23235U,
+	0x4adc96afU, 0x33fac9d6U, 0x4793d4a2U, 0xc336f526U,
+	0x29644dccU, 0x115243f4U, 0xa20eac47U, 0x6bb4df8eU,
+	0x054346e0U, 0x89ec656cU, 0x445317a1U, 0xc276b427U,
+	0x72ea9897U, 0x81e66764U, 0xd727f032U, 0xb75fe852U,
+	0xcb3cf72eU, 0xcf39f62aU, 0x13d2c1f6U, 0x3bf0cbdeU,
+	0x485c14adU, 0x129280f7U, 0xc0f63625U, 0x343f0bd1U,
+	0x90b22275U, 0x094c45ecU, 0x8ca92569U, 0x8366e566U,
+	0x3ab08adfU, 0x195841fcU, 0x0fc9c6eaU, 0x7ba0db9eU,
+	0x028684e7U, 0x3eb58bdbU, 0x17d7c0f2U, 0x1d5d40f8U,
+	0x56c791b3U, 0x7920599cU, 0xba10aa5fU, 0x5c4d11b9U,
+	0xe8d43c0dU, 0x491c55acU, 0x9372e176U, 0x88ac246dU,
+	0xca7cb62fU, 0xaf41ee4aU, 0x3d7548d8U, 0x36bf89d3U,
+	0x9cbd2179U, 0xe9947d0cU, 0xac812d49U, 0xbc952959U,
+	0x584810bdU, 0xdb28f33eU, 0x202e0ec5U, 0x5787d0b2U,
+	0x4ed997abU, 0x242b0fc1U, 0xa34eed46U, 0x8763e462U,
+	0xeb14ff0eU, 0x98b8207dU, 0xb9d0695cU, 0x5ac892bfU,
+	0x69345d8cU, 0xc733f422U, 0x653b5e80U, 0x6c711d89U,
+	0x1e9d83fbU, 0xbdd56858U, 0x8b6ce76eU, 0xcdb97428U,
+	0x9637a173U, 0x0d4944e8U, 0x4b9cd7aeU, 0xfe45bb1bU,
+	0x9b78e37eU, 0xc5b37620U, 0x03c6c5e6U, 0x8623a563U,
+	0x216e4fc4U, 0x510253b4U, 0x9a38a27fU, 0x5f8dd2baU,
+	0xca09c34dU, 0xd3578454U, 0x36cafcb1U, 0x6a81ebedU,
+	0xa27bd925U, 0x16e2f491U, 0x654e2be2U, 0x4feca3c8U,
+	0xc9c9004eU, 0x4be9a2ccU, 0xc1c30246U, 0x9a4dd71dU,
+	0x310f3eb6U, 0x7e90eef9U, 0x211b3aa6U, 0x85961302U,
+	0x3d003dbaU, 0x0c7c708bU, 0xfb758e7cU, 0x482961cfU,
+	0x39053cbeU, 0x6bc1aaecU, 0x81931206U, 0x9e48d619U,
+	0x3b85bebcU, 0x80d35307U, 0xddd8055aU, 0x0939308eU,
+	0xb4ea5e33U, 0x8b19920cU, 0x8999100eU, 0xf77a8d70U,
+	0xe36b8864U, 0x98cd551fU, 0xd0974757U, 0x42a3e1c5U,
+	0xd7528550U, 0x2ed4faa9U, 0xf37f8c74U, 0x78156dffU,
+	0x5d7825daU, 0xc0834347U, 0xbda01d3aU, 0x9cc8541bU,
+	0x26def8a1U, 0xae74da29U, 0x239bb8a4U, 0x186d759fU,
+	0x02f3f185U, 0x5ff8a7d8U, 0xe1eb0a66U, 0x378abdb0U,
+	0xb66adc31U, 0xb1af1e36U, 0x32cffdb5U, 0xc7468140U,
+	0x93079414U, 0xee24ca69U, 0xc3438044U, 0x7a95effdU,
+	0x442662c3U, 0xaf349b28U, 0x6d4429eaU, 0x0bb9b28cU,
+	0x8cdc500bU, 0x8d9c110aU, 0x0fbcb388U, 0x628be9e5U,
+	0x5eb8e6d9U, 0x5c3864dbU, 0xcdcc014aU, 0x7bd5aefcU,
+	0x9f089718U, 0xf4ba4e73U, 0x614b2ae6U, 0xf5fa0f72U,
+	0x0af9f38dU, 0x87169100U, 0xacf4582bU, 0x338fbcb4U,
+	0x304f7fb7U, 0x3c407cbbU, 0x97029510U, 0x5bfda6dcU,
+	0x543266d3U, 0x00737387U, 0xfa35cf7dU, 0xbf209f38U,
+	0x729fedf5U, 0x3ec0feb9U, 0xadb4192aU, 0xbce05c3bU,
+	0x503767d7U, 0xc889414fU, 0xe76e8960U, 0x456623c2U,
+	0xe9e1086eU, 0x01333286U, 0xa9b1182eU, 0xe8a1496fU,
+	0x1c68749bU, 0xf0bf4f77U, 0xd612c451U, 0x1badb69cU,
+	0x05363382U, 0x192d349eU, 0x600b6be7U, 0x7d502dfaU,
+	0x95821712U, 0x15223792U, 0x701f6ff7U, 0x8f1c9308U,
+	0xc203c145U, 0x91871616U, 0x88d9510fU, 0xcc8c404bU,
+	0xef648b68U, 0xde18c659U, 0x0d3c318aU, 0xd89d455fU,
+	0xa0fb5b27U, 0x496920ceU, 0x998d141eU, 0x0efcf289U,
+	0x13a7b494U, 0x8253d105U, 0xb26fdd35U, 0xeb618a6cU,
+	0x3ac5ffbdU, 0x6fc4abe8U, 0xbb259e3cU, 0xda1dc75dU,
+	0x90c75717U, 0x8e5cd209U, 0x715f2ef6U, 0x47e6a1c0U,
+	0x1fa8b798U, 0x4eace2c9U, 0x5abde7ddU, 0xaa71db2dU,
+	0x769aecf1U, 0x14627693U, 0xc5c60342U, 0x17a2b590U,
+	0x12e7f595U, 0x2b91baacU, 0xe4ae4a63U, 0x03b3b084U,
+	0xff708f78U, 0x416322c6U, 0xf9f50c7eU, 0xc4864243U,
+	0x279eb9a0U, 0xdf588758U, 0x597d24deU, 0x517726d6U,
+	0x46a6e0c1U, 0xa33b9824U, 0x57f2a5d0U, 0x1ee8f699U,
+	0xe5ee0b62U, 0x4aa9e3cdU, 0x1aedf79dU, 0x291138aeU,
+	0x9b0d961cU, 0x0879718fU, 0xba65df3dU, 0x10677797U,
+	0x9247d515U, 0x350a3fb2U, 0x1d28359aU, 0x3f80bfb8U,
+	0xa5be1b22U, 0xdc98445bU, 0xa8f1592fU, 0x2c5478abU,
+	0xc606c041U, 0xfe30ce79U, 0x4d6c21caU, 0x84d65203U,
+	0xea21cb6dU, 0x668ee8e1U, 0xab319a2cU, 0x2d1439aaU,
+	0x9d88151aU, 0x6e84eae9U, 0x38457dbfU, 0x583d65dfU,
+	0x245e7aa3U, 0x205b7ba7U, 0xfcb04c7bU, 0xd4924653U,
+	0xa73e9920U, 0xfdf00d7aU, 0x2f94bba8U, 0xdb5d865cU,
+	0x7fd0aff8U, 0xe62ec861U, 0x63cba8e4U, 0x6c0468ebU,
+	0xd5d20752U, 0xf63acc71U, 0xe0ab4b67U, 0x94c25613U,
+	0xede4096aU, 0xd1d70656U, 0xf8b54d7fU, 0xf23fcd75U,
+	0xb9a51c3eU, 0x9642d411U, 0x557227d2U, 0xb32f9c34U,
+	0x07b6b180U, 0xa67ed821U, 0x7c106cfbU, 0x67cea9e0U,
+	0x251e3ba2U, 0x402363c7U, 0xd217c555U, 0xd9dd045eU,
+	0x73dfacf4U, 0x06f6f081U, 0x43e3a0c4U, 0x53f7a4d4U,
+	0xb72a9d30U, 0x344a7eb3U, 0xcf4c8348U, 0xb8e55d3fU,
+	0xa1bb1a26U, 0xcb49824cU, 0x4c2c60cbU, 0x680169efU,
+	0x04767283U, 0x77daadf0U, 0x56b2e4d1U, 0xb5aa1f32U,
+	0x8656d001U, 0x285179afU, 0x8a59d30dU, 0x83139004U,
+	0xf1ff0e76U, 0x52b7e5d5U, 0x640e6ae3U, 0x22dbf9a5U,
+	0x79552cfeU, 0xe22bc965U, 0xa4fe5a23U, 0x11273696U,
+	0x741a6ef3U, 0x2ad1fbadU, 0xeca4486bU, 0x694128eeU,
+	0xce0cc249U, 0xbe60de39U, 0x755a2ff2U, 0xb0ef5f37U,
+	0x28c0e866U, 0x319eaf7fU, 0xd403d79aU, 0x8848c0c6U,
+	0x40b2f20eU, 0xf42bdfbaU, 0x878700c9U, 0xad2588e3U,
+	0x2b002b65U, 0xa92089e7U, 0x230a296dU, 0x7884fc36U,
+	0xd3c6159dU, 0x9c59c5d2U, 0xc3d2118dU, 0x675f3829U,
+	0xdfc91691U, 0xeeb55ba0U, 0x19bca557U, 0xaae04ae4U,
+	0xdbcc1795U, 0x890881c7U, 0x635a392dU, 0x7c81fd32U,
+	0xd94c9597U, 0x621a782cU, 0x3f112e71U, 0xebf01ba5U,
+	0x56237518U, 0x69d0b927U, 0x6b503b25U, 0x15b3a65bU,
+	0x01a2a34fU, 0x7a047e34U, 0x325e6c7cU, 0xa06acaeeU,
+	0x359bae7bU, 0xcc1dd182U, 0x11b6a75fU, 0x9adc46d4U,
+	0xbfb10ef1U, 0x224a686cU, 0x5f693611U, 0x7e017f30U,
+	0xc417d38aU, 0x4cbdf102U, 0xc152938fU, 0xfaa45eb4U,
+	0xe03adaaeU, 0xbd318cf3U, 0x0322214dU, 0xd543969bU,
+	0x54a3f71aU, 0x5366351dU, 0xd006d69eU, 0x258faa6bU,
+	0x71cebf3fU, 0x0cede142U, 0x218aab6fU, 0x985cc4d6U,
+	0xa6ef49e8U, 0x4dfdb003U, 0x8f8d02c1U, 0xe97099a7U,
+	0x6e157b20U, 0x6f553a21U, 0xed7598a3U, 0x8042c2ceU,
+	0xbc71cdf2U, 0xbef14ff0U, 0x2f052a61U, 0x991c85d7U,
+	0x7dc1bc33U, 0x16736558U, 0x838201cdU, 0x17332459U,
+	0xe830d8a6U, 0x65dfba2bU, 0x4e3d7300U, 0xd146979fU,
+	0xd286549cU, 0xde895790U, 0x75cbbe3bU, 0xb9348df7U,
+	0xb6fb4df8U, 0xe2ba58acU, 0x18fce456U, 0x5de9b413U,
+	0x9056c6deU, 0xdc09d592U, 0x4f7d3201U, 0x5e297710U,
+	0xb2fe4cfcU, 0x2a406a64U, 0x05a7a24bU, 0xa7af08e9U,
+	0x0b282345U, 0xe3fa19adU, 0x4b783305U, 0x0a686244U,
+	0xfea15fb0U, 0x1276645cU, 0x34dbef7aU, 0xf9649db7U,
+	0xe7ff18a9U, 0xfbe41fb5U, 0x82c240ccU, 0x9f9906d1U,
+	0x774b3c39U, 0xf7eb1cb9U, 0x92d644dcU, 0x6dd5b823U,
+	0x20caea6eU, 0x734e3d3dU, 0x6a107a24U, 0x2e456b60U,
+	0x0dada043U, 0x3cd1ed72U, 0xeff51aa1U, 0x3a546e74U,
+	0x4232700cU, 0xaba00be5U, 0x7b443f35U, 0xec35d9a2U,
+	0xf16e9fbfU, 0x609afa2eU, 0x50a6f61eU, 0x09a8a147U,
+	0xd80cd496U, 0x8d0d80c3U, 0x59ecb517U, 0x38d4ec76U,
+	0x720e7c3cU, 0x6c95f922U, 0x939605ddU, 0xa52f8aebU,
+	0xfd619cb3U, 0xac65c9e2U, 0xb874ccf6U, 0x48b8f006U,
+	0x9453c7daU, 0xf6ab5db8U, 0x270f2869U, 0xf56b9ebbU,
+	0xf02edebeU, 0xc9589187U, 0x06676148U, 0xe17a9bafU,
+	0x1db9a453U, 0xa3aa09edU, 0x1b3c2755U, 0x264f6968U,
+	0xc557928bU, 0x3d91ac73U, 0xbbb40ff5U, 0xb3be0dfdU,
+	0xa46fcbeaU, 0x41f2b30fU, 0xb53b8efbU, 0xfc21ddb2U,
+	0x07272049U, 0xa860c8e6U, 0xf824dcb6U, 0xcbd81385U,
+	0x79c4bd37U, 0xeab05aa4U, 0x58acf416U, 0xf2ae5cbcU,
+	0x708efe3eU, 0xd7c31499U, 0xffe11eb1U, 0xdd499493U,
+	0x47773009U, 0x3e516f70U, 0x4a387204U, 0xce9d5380U,
+	0x24cfeb6aU, 0x1cf9e552U, 0xafa50ae1U, 0x661f7928U,
+	0x08e8e046U, 0x8447c3caU, 0x49f8b107U, 0xcfdd1281U,
+	0x7f413e31U, 0x8c4dc1c2U, 0xda8c5694U, 0xbaf44ef4U,
+	0xc6975188U, 0xc292508cU, 0x1e796750U, 0x365b6d78U,
+	0x45f7b20bU, 0x1f392651U, 0xcd5d9083U, 0x3994ad77U,
+	0x9d1984d3U, 0x04e7e34aU, 0x810283cfU, 0x8ecd43c0U,
+	0x371b2c79U, 0x14f3e75aU, 0x0262604cU, 0x760b7d38U,
+	0x0f2d2241U, 0x331e2d7dU, 0x1a7c6654U, 0x10f6e65eU,
+	0x5b6c3715U, 0x748bff3aU, 0xb7bb0cf9U, 0x51e6b71fU,
+	0xe57f9aabU, 0x44b7f30aU, 0x9ed947d0U, 0x850782cbU,
+	0xc7d71089U, 0xa2ea48ecU, 0x30deee7eU, 0x3b142f75U,
+	0x911687dfU, 0xe43fdbaaU, 0xa12a8befU, 0xb13e8fffU,
+	0x55e3b61bU, 0xd6835598U, 0x2d85a863U, 0x5a2c7614U,
+	0x4372310dU, 0x2980a967U, 0xaee54be0U, 0x8ac842c4U,
+	0xe6bf59a8U, 0x951386dbU, 0xb47bcffaU, 0x57633419U,
+	0x649ffb2aU, 0xca985284U, 0x6890f826U, 0x61dabb2fU,
+	0x1336255dU, 0xb07ecefeU, 0x86c741c8U, 0xc012d28eU,
+	0x9b9c07d5U, 0x00e2e24eU, 0x46377108U, 0xf3ee1dbdU,
+	0x96d345d8U, 0xc818d086U, 0x0e6d6340U, 0x8b8803c5U,
+	0x2cc5e962U, 0x5ca9f512U, 0x979304d9U, 0x5226741cU,
+	0x532477f9U, 0x4a7a30e0U, 0xafe74805U, 0xf3ac5f59U,
+	0x3b566d91U, 0x8fcf4025U, 0xfc639f56U, 0xd6c1177cU,
+	0x50e4b4faU, 0xd2c41678U, 0x58eeb6f2U, 0x036063a9U,
+	0xa8228a02U, 0xe7bd5a4dU, 0xb8368e12U, 0x1cbba7b6U,
+	0xa42d890eU, 0x9551c43fU, 0x62583ac8U, 0xd104d57bU,
+	0xa028880aU, 0xf2ec1e58U, 0x18bea6b2U, 0x076562adU,
+	0xa2a80a08U, 0x19fee7b3U, 0x44f5b1eeU, 0x9014843aU,
+	0x2dc7ea87U, 0x123426b8U, 0x10b4a4baU, 0x6e5739c4U,
+	0x7a463cd0U, 0x01e0e1abU, 0x49baf3e3U, 0xdb8e5571U,
+	0x4e7f31e4U, 0xb7f94e1dU, 0x6a5238c0U, 0xe138d94bU,
+	0xc455916eU, 0x59aef7f3U, 0x248da98eU, 0x05e5e0afU,
+	0xbff34c15U, 0x37596e9dU, 0xbab60c10U, 0x8140c12bU,
+	0x9bde4531U, 0xc6d5136cU, 0x78c6bed2U, 0xaea70904U,
+	0x2f476885U, 0x2882aa82U, 0xabe24901U, 0x5e6b35f4U,
+	0x0a2a20a0U, 0x77097eddU, 0x5a6e34f0U, 0xe3b85b49U,
+	0xdd0bd677U, 0x36192f9cU, 0xf4699d5eU, 0x92940638U,
+	0x15f1e4bfU, 0x14b1a5beU, 0x9691073cU, 0xfba65d51U,
+	0xc795526dU, 0xc515d06fU, 0x54e1b5feU, 0xe2f81a48U,
+	0x062523acU, 0x6d97fac7U, 0xf8669e52U, 0x6cd7bbc6U,
+	0x93d44739U, 0x1e3b25b4U, 0x35d9ec9fU, 0xaaa20800U,
+	0xa962cb03U, 0xa56dc80fU, 0x0e2f21a4U, 0xc2d01268U,
+	0xcd1fd267U, 0x995ec733U, 0x63187bc9U, 0x260d2b8cU,
+	0xebb25941U, 0xa7ed4a0dU, 0x3499ad9eU, 0x25cde88fU,
+	0xc91ad363U, 0x51a4f5fbU, 0x7e433dd4U, 0xdc4b9776U,
+	0x70ccbcdaU, 0x981e8632U, 0x309cac9aU, 0x718cfddbU,
+	0x8545c02fU, 0x6992fbc3U, 0x4f3f70e5U, 0x82800228U,
+	0x9c1b8736U, 0x8000802aU, 0xf926df53U, 0xe47d994eU,
+	0x0cafa3a6U, 0x8c0f8326U, 0xe932db43U, 0x163127bcU,
+	0x5b2e75f1U, 0x08aaa2a2U, 0x11f4e5bbU, 0x55a1f4ffU,
+	0x76493fdcU, 0x473572edU, 0x9411853eU, 0x41b0f1ebU,
+	0x39d6ef93U, 0xd044947aU, 0x00a0a0aaU, 0x97d1463dU,
+	0x8a8a0020U, 0x1b7e65b1U, 0x2b426981U, 0x724c3ed8U,
+	0xa3e84b09U, 0xf6e91f5cU, 0x22082a88U, 0x433073e9U,
+	0x09eae3a3U, 0x177166bdU, 0xe8729a42U, 0xdecb1574U,
+	0x8685032cU, 0xd781567dU, 0xc3905369U, 0x335c6f99U,
+	0xefb75845U, 0x8d4fc227U, 0x5cebb7f6U, 0x8e8f0124U,
+	0x8bca4121U, 0xb2bc0e18U, 0x7d83fed7U, 0x9a9e0430U,
+	0x665d3bccU, 0xd84e9672U, 0x60d8b8caU, 0x5dabf6f7U,
+	0xbeb30d14U, 0x467533ecU, 0xc050906aU, 0xc85a9262U,
+	0xdf8b5475U, 0x3a162c90U, 0xcedf1164U, 0x87c5422dU,
+	0x7cc3bfd6U, 0xd3845779U, 0x83c04329U, 0xb03c8c1aU,
+	0x022022a8U, 0x9154c53bU, 0x23486b89U, 0x894ac323U,
+	0x0b6a61a1U, 0xac278b06U, 0x8405812eU, 0xa6ad0b0cU,
+	0x3c93af96U, 0x45b5f0efU, 0x31dced9bU, 0xb579cc1fU,
+	0x5f2b74f5U, 0x671d7acdU, 0xd441957eU, 0x1dfbe6b7U,
+	0x730c7fd9U, 0xffa35c55U, 0x321c2e98U, 0xb4398d1eU,
+	0x04a5a1aeU, 0xf7a95e5dU, 0xa168c90bU, 0xc110d16bU,
+	0xbd73ce17U, 0xb976cf13U, 0x659df8cfU, 0x4dbff2e7U,
+	0x3e132d94U, 0x64ddb9ceU, 0xb6b90f1cU, 0x427032e8U,
+	0xe6fd1b4cU, 0x7f037cd5U, 0xfae61c50U, 0xf529dc5fU,
+	0x4cffb3e6U, 0x6f1778c5U, 0x7986ffd3U, 0x0defe2a7U,
+	0x74c9bddeU, 0x48fab2e2U, 0x6198f9cbU, 0x6b1279c1U,
+	0x2088a88aU, 0x0f6f60a5U, 0xcc5f9366U, 0x2a022880U,
+	0x9e9b0534U, 0x3f536c95U, 0xe53dd84fU, 0xfee31d54U,
+	0xbc338f16U, 0xd90ed773U, 0x4b3a71e1U, 0x40f0b0eaU,
+	0xeaf21840U, 0x9fdb4435U, 0xdace1470U, 0xcada1060U,
+	0x2e072984U, 0xad67ca07U, 0x566137fcU, 0x21c8e98bU,
+	0x3896ae92U, 0x526436f8U, 0xd501d47fU, 0xf12cdd5bU,
+	0x9d5bc637U, 0xeef71944U, 0xcf9f5065U, 0x2c87ab86U,
+	0x1f7b64b5U, 0xb17ccd1bU, 0x137467b9U, 0x1a3e24b0U,
+	0x68d2bac2U, 0xcb9a5161U, 0xfd23de57U, 0xbbf64d11U,
+	0xe078984aU, 0x7b067dd1U, 0x3dd3ee97U, 0x880a8222U,
+	0xed37da47U, 0xb3fc4f19U, 0x7589fcdfU, 0xf06c9c5aU,
+	0x572176fdU, 0x274d6a8dU, 0xec779b46U, 0x29c2eb83U,
+	0x93e774faU, 0x8ab933e3U, 0x6f244b06U, 0x336f5c5aU,
+	0xfb956e92U, 0x4f0c4326U, 0x3ca09c55U, 0x1602147fU,
+	0x9027b7f9U, 0x1207157bU, 0x982db5f1U, 0xc3a360aaU,
+	0x68e18901U, 0x277e594eU, 0x78f58d11U, 0xdc78a4b5U,
+	0x64ee8a0dU, 0x5592c73cU, 0xa29b39cbU, 0x11c7d678U,
+	0x60eb8b09U, 0x322f1d5bU, 0xd87da5b1U, 0xc7a661aeU,
+	0x626b090bU, 0xd93de4b0U, 0x8436b2edU, 0x50d78739U,
+	0xed04e984U, 0xd2f725bbU, 0xd077a7b9U, 0xae943ac7U,
+	0xba853fd3U, 0xc123e2a8U, 0x8979f0e0U, 0x1b4d5672U,
+	0x8ebc32e7U, 0x773a4d1eU, 0xaa913bc3U, 0x21fbda48U,
+	0x0496926dU, 0x996df4f0U, 0xe44eaa8dU, 0xc526e3acU,
+	0x7f304f16U, 0xf79a6d9eU, 0x7a750f13U, 0x4183c228U,
+	0x5b1d4632U, 0x0616106fU, 0xb805bdd1U, 0x6e640a07U,
+	0xef846b86U, 0xe841a981U, 0x6b214a02U, 0x9ea836f7U,
+	0xcae923a3U, 0xb7ca7ddeU, 0x9aad37f3U, 0x237b584aU,
+	0x1dc8d574U, 0xf6da2c9fU, 0x34aa9e5dU, 0x5257053bU,
+	0xd532e7bcU, 0xd472a6bdU, 0x5652043fU, 0x3b655e52U,
+	0x0756516eU, 0x05d6d36cU, 0x9422b6fdU, 0x223b194bU,
+	0xc6e620afU, 0xad54f9c4U, 0x38a59d51U, 0xac14b8c5U,
+	0x5317443aU, 0xdef826b7U, 0xf51aef9cU, 0x6a610b03U,
+	0x69a1c800U, 0x65aecb0cU, 0xceec22a7U, 0x0213116bU,
+	0x0ddcd164U, 0x599dc430U, 0xa3db78caU, 0xe6ce288fU,
+	0x2b715a42U, 0x672e490eU, 0xf45aae9dU, 0xe50eeb8cU,
+	0x09d9d060U, 0x9167f6f8U, 0xbe803ed7U, 0x1c889475U,
+	0xb00fbfd9U, 0x58dd8531U, 0xf05faf99U, 0xb14ffed8U,
+	0x4586c32cU, 0xa951f8c0U, 0x8ffc73e6U, 0x4243012bU,
+	0x5cd88435U, 0x40c38329U, 0x39e5dc50U, 0x24be9a4dU,
+	0xcc6ca0a5U, 0x4ccc8025U, 0x29f1d840U, 0xd6f224bfU,
+	0x9bed76f2U, 0xc869a1a1U, 0xd137e6b8U, 0x9562f7fcU,
+	0xb68a3cdfU, 0x87f671eeU, 0x54d2863dU, 0x8173f2e8U,
+	0xf915ec90U, 0x10879779U, 0xc063a3a9U, 0x5712453eU,
+	0x4a490323U, 0xdbbd66b2U, 0xeb816a82U, 0xb28f3ddbU,
+	0x632b480aU, 0x362a1c5fU, 0xe2cb298bU, 0x83f370eaU,
+	0xc929e0a0U, 0xd7b265beU, 0x28b19941U, 0x1e081677U,
+	0x4646002fU, 0x1742557eU, 0x0353506aU, 0xf39f6c9aU,
+	0x2f745b46U, 0x4d8cc124U, 0x9c28b4f5U, 0x4e4c0227U,
+	0x4b094222U, 0x727f0d1bU, 0xbd40fdd4U, 0x5a5d0733U,
+	0xa69e38cfU, 0x188d9571U, 0xa01bbbc9U, 0x9d68f5f4U,
+	0x7e700e17U, 0x86b630efU, 0x00939369U, 0x08999161U,
+	0x1f485776U, 0xfad52f93U, 0x0e1c1267U, 0x4706412eU,
+	0xbc00bcd5U, 0x1347547aU, 0x4303402aU, 0x70ff8f19U,
+	0xc2e321abU, 0x5197c638U, 0xe38b688aU, 0x4989c020U,
+	0xcba962a2U, 0x6ce48805U, 0x44c6822dU, 0x666e080fU,
+	0xfc50ac95U, 0x8576f3ecU, 0xf11fee98U, 0x75bacf1cU,
+	0x9fe877f6U, 0xa7de79ceU, 0x1482967dU, 0xdd38e5b4U,
+	0xb3cf7cdaU, 0x3f605f56U, 0xf2df2d9bU, 0x74fa8e1dU,
+	0xc466a2adU, 0x376a5d5eU, 0x61abca08U, 0x01d3d268U,
+	0x7db0cd14U, 0x79b5cc10U, 0xa55efbccU, 0x8d7cf1e4U,
+	0xfed02e97U, 0xa41ebacdU, 0x767a0c1fU, 0x82b331ebU,
+	0x263e184fU, 0xbfc07fd6U, 0x3a251f53U, 0x35eadf5cU,
+	0x8c3cb0e5U, 0xafd47bc6U, 0xb945fcd0U, 0xcd2ce1a4U,
+	0xb40abeddU, 0x8839b1e1U, 0xa15bfac8U, 0xabd17ac2U,
+	0xe04bab89U, 0xcfac63a6U, 0x0c9c9065U, 0xeac12b83U,
+	0x5e580637U, 0xff906f96U, 0x25fedb4cU, 0x3e201e57U,
+	0x7cf08c15U, 0x19cdd470U, 0x8bf972e2U, 0x8033b3e9U,
+	0x2a311b43U, 0x5f184736U, 0x1a0d1773U, 0x0a191363U,
+	0xeec42a87U, 0x6da4c904U, 0x96a234ffU, 0xe10bea88U,
+	0xf855ad91U, 0x92a735fbU, 0x15c2d77cU, 0x31efde58U,
+	0x5d98c534U, 0x2e341a47U, 0x0f5c5366U, 0xec44a885U,
+	0xdfb867b6U, 0x71bfce18U, 0xd3b764baU, 0xdafd27b3U,
+	0xa811b9c1U, 0x0b595262U, 0x3de0dd54U, 0x7b354e12U,
+	0x20bb9b49U, 0xbbc57ed2U, 0xfd10ed94U, 0x48c98121U,
+	0x2df4d944U, 0x733f4c1aU, 0xb54affdcU, 0x30af9f59U,
+	0x97e275feU, 0xe78e698eU, 0x2cb49845U, 0xe901e880U,
+	0x9ce478f6U, 0x85ba3fefU, 0x6027470aU, 0x3c6c5056U,
+	0xf496629eU, 0x400f4f2aU, 0x33a39059U, 0x19011873U,
+	0x9f24bbf5U, 0x1d041977U, 0x972eb9fdU, 0xcca06ca6U,
+	0x67e2850dU, 0x287d5542U, 0x77f6811dU, 0xd37ba8b9U,
+	0x6bed8601U, 0x5a91cb30U, 0xad9835c7U, 0x1ec4da74U,
+	0x6fe88705U, 0x3d2c1157U, 0xd77ea9bdU, 0xc8a56da2U,
+	0x6d680507U, 0xd63ee8bcU, 0x8b35bee1U, 0x5fd48b35U,
+	0xe207e588U, 0xddf429b7U, 0xdf74abb5U, 0xa19736cbU,
+	0xb58633dfU, 0xce20eea4U, 0x867afcecU, 0x144e5a7eU,
+	0x81bf3eebU, 0x78394112U, 0xa59237cfU, 0x2ef8d644U,
+	0x0b959e61U, 0x966ef8fcU, 0xeb4da681U, 0xca25efa0U,
+	0x7033431aU, 0xf8996192U, 0x7576031fU, 0x4e80ce24U,
+	0x541e4a3eU, 0x09151c63U, 0xb706b1ddU, 0x6167060bU,
+	0xe087678aU, 0xe742a58dU, 0x6422460eU, 0x91ab3afbU,
+	0xc5ea2fafU, 0xb8c971d2U, 0x95ae3bffU, 0x2c785446U,
+	0x12cbd978U, 0xf9d92093U, 0x3ba99251U, 0x5d540937U,
+	0xda31ebb0U, 0xdb71aab1U, 0x59510833U, 0x3466525eU,
+	0x08555d62U, 0x0ad5df60U, 0x9b21baf1U, 0x2d381547U,
+	0xc9e52ca3U, 0xa257f5c8U, 0x37a6915dU, 0xa317b4c9U,
+	0x5c144836U, 0xd1fb2abbU, 0xfa19e390U, 0x6562070fU,
+	0x66a2c40cU, 0x6aadc700U, 0xc1ef2eabU, 0x0d101d67U,
+	0x02dfdd68U, 0x569ec83cU, 0xacd874c6U, 0xe9cd2483U,
+	0x2472564eU, 0x682d4502U, 0xfb59a291U, 0xea0de780U,
+	0x06dadc6cU, 0x9e64faf4U, 0xb18332dbU, 0x138b9879U,
+	0xbf0cb3d5U, 0x57de893dU, 0xff5ca395U, 0xbe4cf2d4U,
+	0x4a85cf20U, 0xa652f4ccU, 0x80ff7feaU, 0x4d400d27U,
+	0x53db8839U, 0x4fc08f25U, 0x36e6d05cU, 0x2bbd9641U,
+	0xc36faca9U, 0x43cf8c29U, 0x26f2d44cU, 0xd9f128b3U,
+	0x94ee7afeU, 0xc76aadadU, 0xde34eab4U, 0x9a61fbf0U,
+	0xb98930d3U, 0x88f57de2U, 0x5bd18a31U, 0x8e70fee4U,
+	0xf616e09cU, 0x1f849b75U, 0xcf60afa5U, 0x58114932U,
+	0x454a0f2fU, 0xd4be6abeU, 0xe482668eU, 0xbd8c31d7U,
+	0x6c284406U, 0x39291053U, 0xedc82587U, 0x8cf07ce6U,
+	0xc62aecacU, 0xd8b169b2U, 0x27b2954dU, 0x110b1a7bU,
+	0x49450c23U, 0x18415972U, 0x0c505c66U, 0xfc9c6096U,
+	0x2077574aU, 0x428fcd28U, 0x932bb8f9U, 0x414f0e2bU,
+	0x440a4e2eU, 0x7d7c0117U, 0xb243f1d8U, 0x555e0b3fU,
+	0xa99d34c3U, 0x178e997dU, 0xaf18b7c5U, 0x926bf9f8U,
+	0x7173021bU, 0x89b53ce3U, 0x0f909f65U, 0x079a9d6dU,
+	0x104b5b7aU, 0xf5d6239fU, 0x011f1e6bU, 0x48054d22U,
+	0xb303b0d9U, 0x1c445876U, 0x4c004c26U, 0x7ffc8315U,
+	0xcde02da7U, 0x5e94ca34U, 0xec886486U, 0x468acc2cU,
+	0xc4aa6eaeU, 0x63e78409U, 0x4bc58e21U, 0x696d0403U,
+	0xf353a099U, 0x8a75ffe0U, 0xfe1ce294U, 0x7ab9c310U,
+	0x90eb7bfaU, 0xa8dd75c2U, 0x1b819a71U, 0xd23be9b8U,
+	0xbccc70d6U, 0x3063535aU, 0xfddc2197U, 0x7bf98211U,
+	0xcb65aea1U, 0x38695152U, 0x6ea8c604U, 0x0ed0de64U,
+	0x72b3c118U, 0x76b6c01cU, 0xaa5df7c0U, 0x827ffde8U,
+	0xf1d3229bU, 0xab1db6c1U, 0x79790013U, 0x8db03de7U,
+	0x293d1443U, 0xb0c373daU, 0x3526135fU, 0x3ae9d350U,
+	0x833fbce9U, 0xa0d777caU, 0xb646f0dcU, 0xc22feda8U,
+	0xbb09b2d1U, 0x873abdedU, 0xae58f6c4U, 0xa4d276ceU,
+	0xef48a785U, 0xc0af6faaU, 0x039f9c69U, 0xe5c2278fU,
+	0x515b0a3bU, 0xf093639aU, 0x2afdd740U, 0x3123125bU,
+	0x73f38019U, 0x16ced87cU, 0x84fa7eeeU, 0x8f30bfe5U,
+	0x2532174fU, 0x501b4b3aU, 0x150e1b7fU, 0x051a1f6fU,
+	0xe1c7268bU, 0x62a7c508U, 0x99a138f3U, 0xee08e684U,
+	0xf756a19dU, 0x9da439f7U, 0x1ac1db70U, 0x3eecd254U,
+	0x529bc938U, 0x2137164bU, 0x005f5f6aU, 0xe347a489U,
+	0xd0bb6bbaU, 0x7ebcc214U, 0xdcb468b6U, 0xd5fe2bbfU,
+	0xa712b5cdU, 0x045a5e6eU, 0x32e3d158U, 0x7436421eU,
+	0x2fb89745U, 0xb4c672deU, 0xf213e198U, 0x47ca8d2dU,
+	0x22f7d548U, 0x7c3c4016U, 0xba49f3d0U, 0x3fac9355U,
+	0x98e179f2U, 0xe88d6582U, 0x23b79449U, 0xe602e48cU,
+	0xde0dd35dU, 0xc7539444U, 0x22ceeca1U, 0x7e85fbfdU,
+	0xb67fc935U, 0x02e6e481U, 0x714a3bf2U, 0x5be8b3d8U,
+	0xddcd105eU, 0x5fedb2dcU, 0xd5c71256U, 0x8e49c70dU,
+	0x250b2ea6U, 0x6a94fee9U, 0x351f2ab6U, 0x91920312U,
+	0x29042daaU, 0x1878609bU, 0xef719e6cU, 0x5c2d71dfU,
+	0x2d012caeU, 0x7fc5bafcU, 0x95970216U, 0x8a4cc609U,
+	0x2f81aeacU, 0x94d74317U, 0xc9dc154aU, 0x1d3d209eU,
+	0xa0ee4e23U, 0x9f1d821cU, 0x9d9d001eU, 0xe37e9d60U,
+	0xf76f9874U, 0x8cc9450fU, 0xc4935747U, 0x56a7f1d5U,
+	0xc3569540U, 0x3ad0eab9U, 0xe77b9c64U, 0x6c117defU,
+	0x497c35caU, 0xd4875357U, 0xa9a40d2aU, 0x88cc440bU,
+	0x32dae8b1U, 0xba70ca39U, 0x379fa8b4U, 0x0c69658fU,
+	0x16f7e195U, 0x4bfcb7c8U, 0xf5ef1a76U, 0x238eada0U,
+	0xa26ecc21U, 0xa5ab0e26U, 0x26cbeda5U, 0xd3429150U,
+	0x87038404U, 0xfa20da79U, 0xd7479054U, 0x6e91ffedU,
+	0x502272d3U, 0xbb308b38U, 0x794039faU, 0x1fbda29cU,
+	0x98d8401bU, 0x9998011aU, 0x1bb8a398U, 0x768ff9f5U,
+	0x4abcf6c9U, 0x483c74cbU, 0xd9c8115aU, 0x6fd1beecU,
+	0x8b0c8708U, 0xe0be5e63U, 0x754f3af6U, 0xe1fe1f62U,
+	0x1efde39dU, 0x93128110U, 0xb8f0483bU, 0x278baca4U,
+	0x244b6fa7U, 0x28446cabU, 0x83068500U, 0x4ff9b6ccU,
+	0x403676c3U, 0x14776397U, 0xee31df6dU, 0xab248f28U,
+	0x669bfde5U, 0x2ac4eea9U, 0xb9b0093aU, 0xa8e44c2bU,
+	0x443377c7U, 0xdc8d515fU, 0xf36a9970U, 0x516233d2U,
+	0xfde5187eU, 0x15372296U, 0xbdb5083eU, 0xfca5597fU,
+	0x086c648bU, 0xe4bb5f67U, 0xc216d441U, 0x0fa9a68cU,
+	0x11322392U, 0x0d29248eU, 0x740f7bf7U, 0x69543deaU,
+	0x81860702U, 0x01262782U, 0x641b7fe7U, 0x9b188318U,
+	0xd607d155U, 0x85830606U, 0x9cdd411fU, 0xd888505bU,
+	0xfb609b78U, 0xca1cd649U, 0x1938219aU, 0xcc99554fU,
+	0xb4ff4b37U, 0x5d6d30deU, 0x8d89040eU, 0x1af8e299U,
+	0x07a3a484U, 0x9657c115U, 0xa66bcd25U, 0xff659a7cU,
+	0x2ec1efadU, 0x7bc0bbf8U, 0xaf218e2cU, 0xce19d74dU,
+	0x84c34707U, 0x9a58c219U, 0x655b3ee6U, 0x53e2b1d0U,
+	0x0baca788U, 0x5aa8f2d9U, 0x4eb9f7cdU, 0xbe75cb3dU,
+	0x629efce1U, 0x00666683U, 0xd1c21352U, 0x03a6a580U,
+	0x06e3e585U, 0x3f95aabcU, 0xf0aa5a73U, 0x17b7a094U,
+	0xeb749f68U, 0x556732d6U, 0xedf11c6eU, 0xd0825253U,
+	0x339aa9b0U, 0xcb5c9748U, 0x4d7934ceU, 0x457336c6U,
+	0x52a2f0d1U, 0xb73f8834U, 0x43f6b5c0U, 0x0aece689U,
+	0xf1ea1b72U, 0x5eadf3ddU, 0x0ee9e78dU, 0x3d1528beU,
+	0x8f09860cU, 0x1c7d619fU, 0xae61cf2dU, 0x04636787U,
+	0x8643c505U, 0x210e2fa2U, 0x092c258aU, 0x2b84afa8U,
+	0xb1ba0b32U, 0xc89c544bU, 0xbcf5493fU, 0x385068bbU,
+	0xd202d051U, 0xea34de69U, 0x596831daU, 0x90d24213U,
+	0xfe25db7dU, 0x728af8f1U, 0xbf358a3cU, 0x391029baU,
+	0x898c050aU, 0x7a80faf9U, 0x2c416dafU, 0x4c3975cfU,
+	0x305a6ab3U, 0x345f6bb7U, 0xe8b45c6bU, 0xc0965643U,
+	0xb33a8930U, 0xe9f41d6aU, 0x3b90abb8U, 0xcf59964cU,
+	0x6bd4bfe8U, 0xf22ad871U, 0x77cfb8f4U, 0x780078fbU,
+	0xc1d61742U, 0xe23edc61U, 0xf4af5b77U, 0x80c64603U,
+	0xf9e0197aU, 0xc5d31646U, 0xecb15d6fU, 0xe63bdd65U,
+	0xada10c2eU, 0x8246c401U, 0x417637c2U, 0xa72b8c24U,
+	0x13b2a190U, 0xb27ac831U, 0x68147cebU, 0x73cab9f0U,
+	0x311a2bb2U, 0x542773d7U, 0xc613d545U, 0xcdd9144eU,
+	0x67dbbce4U, 0x12f2e091U, 0x57e7b0d4U, 0x47f3b4c4U,
+	0xa32e8d20U, 0x204e6ea3U, 0xdb489358U, 0xace14d2fU,
+	0xb5bf0a36U, 0xdf4d925cU, 0x582870dbU, 0x7c0579ffU,
+	0x10726293U, 0x63debde0U, 0x42b6f4c1U, 0xa1ae0f22U,
+	0x9252c011U, 0x3c5569bfU, 0x9e5dc31dU, 0x97178014U,
+	0xe5fb1e66U, 0x46b3f5c5U, 0x700a7af3U, 0x36dfe9b5U,
+	0x6d513ceeU, 0xf62fd975U, 0xb0fa4a33U, 0x05232686U,
+	0x601e7ee3U, 0x3ed5ebbdU, 0xf8a0587bU, 0x7d4538feU,
+	0xda08d259U, 0xaa64ce29U, 0x615e3fe2U, 0xa4eb4f27U,
+	0x213e1f91U, 0x38605888U, 0xddfd206dU, 0x81b63731U,
+	0x494c05f9U, 0xfdd5284dU, 0x8e79f73eU, 0xa4db7f14U,
+	0x22fedc92U, 0xa0de7e10U, 0x2af4de9aU, 0x717a0bc1U,
+	0xda38e26aU, 0x95a73225U, 0xca2ce67aU, 0x6ea1cfdeU,
+	0xd637e166U, 0xe74bac57U, 0x104252a0U, 0xa31ebd13U,
+	0xd232e062U, 0x80f67630U, 0x6aa4cedaU, 0x757f0ac5U,
+	0xd0b26260U, 0x6be48fdbU, 0x36efd986U, 0xe20eec52U,
+	0x5fdd82efU, 0x602e4ed0U, 0x62aeccd2U, 0x1c4d51acU,
+	0x085c54b8U, 0x73fa89c3U, 0x3ba09b8bU, 0xa9943d19U,
+	0x3c65598cU, 0xc5e32675U, 0x184850a8U, 0x9322b123U,
+	0xb64ff906U, 0x2bb49f9bU, 0x5697c1e6U, 0x77ff88c7U,
+	0xcde9247dU, 0x454306f5U, 0xc8ac6478U, 0xf35aa943U,
+	0xe9c42d59U, 0xb4cf7b04U, 0x0adcd6baU, 0xdcbd616cU,
+	0x5d5d00edU, 0x5a98c2eaU, 0xd9f82169U, 0x2c715d9cU,
+	0x783048c8U, 0x051316b5U, 0x28745c98U, 0x91a23321U,
+	0xaf11be1fU, 0x440347f4U, 0x8673f536U, 0xe08e6e50U,
+	0x67eb8cd7U, 0x66abcdd6U, 0xe48b6f54U, 0x89bc3539U,
+	0xb58f3a05U, 0xb70fb807U, 0x26fbdd96U, 0x90e27220U,
+	0x743f4bc4U, 0x1f8d92afU, 0x8a7cf63aU, 0x1ecdd3aeU,
+	0xe1ce2f51U, 0x6c214ddcU, 0x47c384f7U, 0xd8b86068U,
+	0xdb78a36bU, 0xd777a067U, 0x7c3549ccU, 0xb0ca7a00U,
+	0xbf05ba0fU, 0xeb44af5bU, 0x110213a1U, 0x541743e4U,
+	0x99a83129U, 0xd5f72265U, 0x4683c5f6U, 0x57d780e7U,
+	0xbb00bb0bU, 0x23be9d93U, 0x0c5955bcU, 0xae51ff1eU,
+	0x02d6d4b2U, 0xea04ee5aU, 0x4286c4f2U, 0x039695b3U,
+	0xf75fa847U, 0x1b8893abU, 0x3d25188dU, 0xf09a6a40U,
+	0xee01ef5eU, 0xf21ae842U, 0x8b3cb73bU, 0x9667f126U,
+	0x7eb5cbceU, 0xfe15eb4eU, 0x9b28b32bU, 0x642b4fd4U,
+	0x29341d99U, 0x7ab0cacaU, 0x63ee8dd3U, 0x27bb9c97U,
+	0x045357b4U, 0x352f1a85U, 0xe60bed56U, 0x33aa9983U,
+	0x4bcc87fbU, 0xa25efc12U, 0x72bac8c2U, 0xe5cb2e55U,
+	0xf8906848U, 0x69640dd9U, 0x595801e9U, 0x005656b0U,
+	0xd1f22361U, 0x84f37734U, 0x501242e0U, 0x312a1b81U,
+	0x7bf08bcbU, 0x656b0ed5U, 0x9a68f22aU, 0xacd17d1cU,
+	0xf49f6b44U, 0xa59b3e15U, 0xb18a3b01U, 0x414607f1U,
+	0x9dad302dU, 0xff55aa4fU, 0x2ef1df9eU, 0xfc95694cU,
+	0xf9d02949U, 0xc0a66670U, 0x0f9996bfU, 0xe8846c58U,
+	0x144753a4U, 0xaa54fe1aU, 0x12c2d0a2U, 0x2fb19e9fU,
+	0xcca9657cU, 0x346f5b84U, 0xb24af802U, 0xba40fa0aU,
+	0xad913c1dU, 0x480c44f8U, 0xbcc5790cU, 0xf5df2a45U,
+	0x0ed9d7beU, 0xa19e3f11U, 0xf1da2b41U, 0xc226e472U,
+	0x703a4ac0U, 0xe34ead53U, 0x515203e1U, 0xfb50ab4bU,
+	0x797009c9U, 0xde3de36eU, 0xf61fe946U, 0xd4b76364U,
+	0x4e89c7feU, 0x37af9887U, 0x43c685f3U, 0xc763a477U,
+	0x2d311c9dU, 0x150712a5U, 0xa65bfd16U, 0x6fe18edfU,
+	0x011617b1U, 0x8db9343dU, 0x400646f0U, 0xc623e576U,
+	0x76bfc9c6U, 0x85b33635U, 0xd372a163U, 0xb30ab903U,
+	0xcf69a67fU, 0xcb6ca77bU, 0x178790a7U, 0x3fa59a8fU,
+	0x4c0945fcU, 0x16c7d1a6U, 0xc4a36774U, 0x306a5a80U,
+	0x94e77324U, 0x0d1914bdU, 0x88fc7438U, 0x8733b437U,
+	0x3ee5db8eU, 0x1d0d10adU, 0x0b9c97bbU, 0x7ff58acfU,
+	0x06d3d5b6U, 0x3ae0da8aU, 0x138291a3U, 0x190811a9U,
+	0x5292c0e2U, 0x7d7508cdU, 0xbe45fb0eU, 0x581840e8U,
+	0xec816d5cU, 0x4d4904fdU, 0x9727b027U, 0x8cf9753cU,
+	0xce29e77eU, 0xab14bf1bU, 0x39201989U, 0x32ead882U,
+	0x98e87028U, 0xedc12c5dU, 0xa8d47c18U, 0xb8c07808U,
+	0x5c1d41ecU, 0xdf7da26fU, 0x247b5f94U, 0x53d281e3U,
+	0x4a8cc6faU, 0x207e5e90U, 0xa71bbc17U, 0x8336b533U,
+	0xef41ae5fU, 0x9ced712cU, 0xbd85380dU, 0x5e9dc3eeU,
+	0x6d610cddU, 0xc366a573U, 0x616e0fd1U, 0x68244cd8U,
+	0x1ac8d2aaU, 0xb9803909U, 0x8f39b63fU, 0xc9ec2579U,
+	0x9262f022U, 0x091c15b9U, 0x4fc986ffU, 0xfa10ea4aU,
+	0x9f2db22fU, 0xc1e62771U, 0x079394b7U, 0x8276f432U,
+	0x253b1e95U, 0x555702e5U, 0x9e6df32eU, 0x5bd883ebU,
+	0xeefe109eU, 0xf7a05787U, 0x123d2f62U, 0x4e76383eU,
+	0x868c0af6U, 0x32152742U, 0x41b9f831U, 0x6b1b701bU,
+	0xed3ed39dU, 0x6f1e711fU, 0xe534d195U, 0xbeba04ceU,
+	0x15f8ed65U, 0x5a673d2aU, 0x05ece975U, 0xa161c0d1U,
+	0x19f7ee69U, 0x288ba358U, 0xdf825dafU, 0x6cdeb21cU,
+	0x1df2ef6dU, 0x4f36793fU, 0xa564c1d5U, 0xbabf05caU,
+	0x1f726d6fU, 0xa42480d4U, 0xf92fd689U, 0x2dcee35dU,
+	0x901d8de0U, 0xafee41dfU, 0xad6ec3ddU, 0xd38d5ea3U,
+	0xc79c5bb7U, 0xbc3a86ccU, 0xf4609484U, 0x66543216U,
+	0xf3a55683U, 0x0a23297aU, 0xd7885fa7U, 0x5ce2be2cU,
+	0x798ff609U, 0xe4749094U, 0x9957cee9U, 0xb83f87c8U,
+	0x02292b72U, 0x8a8309faU, 0x076c6b77U, 0x3c9aa64cU,
+	0x26042256U, 0x7b0f740bU, 0xc51cd9b5U, 0x137d6e63U,
+	0x929d0fe2U, 0x9558cde5U, 0x16382e66U, 0xe3b15293U,
+	0xb7f047c7U, 0xcad319baU, 0xe7b45397U, 0x5e623c2eU,
+	0x60d1b110U, 0x8bc348fbU, 0x49b3fa39U, 0x2f4e615fU,
+	0xa82b83d8U, 0xa96bc2d9U, 0x2b4b605bU, 0x467c3a36U,
+	0x7a4f350aU, 0x78cfb708U, 0xe93bd299U, 0x5f227d2fU,
+	0xbbff44cbU, 0xd04d9da0U, 0x45bcf935U, 0xd10ddca1U,
+	0x2e0e205eU, 0xa3e142d3U, 0x88038bf8U, 0x17786f67U,
+	0x14b8ac64U, 0x18b7af68U, 0xb3f546c3U, 0x7f0a750fU,
+	0x70c5b500U, 0x2484a054U, 0xdec21caeU, 0x9bd74cebU,
+	0x56683e26U, 0x1a372d6aU, 0x8943caf9U, 0x98178fe8U,
+	0x74c0b404U, 0xec7e929cU, 0xc3995ab3U, 0x6191f011U,
+	0xcd16dbbdU, 0x25c4e155U, 0x8d46cbfdU, 0xcc569abcU,
+	0x389fa748U, 0xd4489ca4U, 0xf2e51782U, 0x3f5a654fU,
+	0x21c1e051U, 0x3ddae74dU, 0x44fcb834U, 0x59a7fe29U,
+	0xb175c4c1U, 0x31d5e441U, 0x54e8bc24U, 0xabeb40dbU,
+	0xe6f41296U, 0xb570c5c5U, 0xac2e82dcU, 0xe87b9398U,
+	0xcb9358bbU, 0xfaef158aU, 0x29cbe259U, 0xfc6a968cU,
+	0x840c88f4U, 0x6d9ef31dU, 0xbd7ac7cdU, 0x2a0b215aU,
+	0x37506747U, 0xa6a402d6U, 0x96980ee6U, 0xcf9659bfU,
+	0x1e322c6eU, 0x4b33783bU, 0x9fd24defU, 0xfeea148eU,
+	0xb43084c4U, 0xaaab01daU, 0x55a8fd25U, 0x63117213U,
+	0x3b5f644bU, 0x6a5b311aU, 0x7e4a340eU, 0x8e8608feU,
+	0x526d3f22U, 0x3095a540U, 0xe131d091U, 0x33556643U,
+	0x36102646U, 0x0f66697fU, 0xc05999b0U, 0x27446357U,
+	0xdb875cabU, 0x6594f115U, 0xdd02dfadU, 0xe0719190U,
+	0x03696a73U, 0xfbaf548bU, 0x7d8af70dU, 0x7580f505U,
+	0x62513312U, 0x87cc4bf7U, 0x73057603U, 0x3a1f254aU,
+	0xc119d8b1U, 0x6e5e301eU, 0x3e1a244eU, 0x0de6eb7dU,
+	0xbffa45cfU, 0x2c8ea25cU, 0x9e920ceeU, 0x3490a444U,
+	0xb6b006c6U, 0x11fdec61U, 0x39dfe649U, 0x1b776c6bU,
+	0x8149c8f1U, 0xf86f9788U, 0x8c068afcU, 0x08a3ab78U,
+	0xe2f11392U, 0xdac71daaU, 0x699bf219U, 0xa02181d0U,
+	0xced618beU, 0x42793b32U, 0x8fc649ffU, 0x09e3ea79U,
+	0xb97fc6c9U, 0x4a73393aU, 0x1cb2ae6cU, 0x7ccab60cU,
+	0x00a9a970U, 0x04aca874U, 0xd8479fa8U, 0xf0659580U,
+	0x83c94af3U, 0xd907dea9U, 0x0b63687bU, 0xffaa558fU,
+	0x5b277c2bU, 0xc2d91bb2U, 0x473c7b37U, 0x48f3bb38U,
+	0xf125d481U, 0xd2cd1fa2U, 0xc45c98b4U, 0xb03585c0U,
+	0xc913dab9U, 0xf520d585U, 0xdc429eacU, 0xd6c81ea6U,
+	0x9d52cfedU, 0xb2b507c2U, 0x7185f401U, 0x97d84fe7U,
+	0x23416253U, 0x82890bf2U, 0x58e7bf28U, 0x43397a33U,
+	0x01e9e871U, 0x64d4b014U, 0xf6e01686U, 0xfd2ad78dU,
+	0x57287f27U, 0x22012352U, 0x67147317U, 0x77007707U,
+	0x93dd4ee3U, 0x10bdad60U, 0xebbb509bU, 0x9c128eecU,
+	0x854cc9f5U, 0xefbe519fU, 0x68dbb318U, 0x4cf6ba3cU,
+	0x2081a150U, 0x532d7e23U, 0x72453702U, 0x915dcce1U,
+	0xa2a103d2U, 0x0ca6aa7cU, 0xaeae00deU, 0xa7e443d7U,
+	0xd508dda5U, 0x76403606U, 0x40f9b930U, 0x062c2a76U,
+	0x5da2ff2dU, 0xc6dc1ab6U, 0x800989f0U, 0x35d0e545U,
+	0x50edbd20U, 0x0e26287eU, 0xc8539bb8U, 0x4db6fb3dU,
+	0xeafb119aU, 0x9a970deaU, 0x51adfc21U, 0x94188ce4U,
+	0xafeb44caU, 0xb6b503d3U, 0x53287b36U, 0x0f636c6aU,
+	0xc7995ea2U, 0x73007316U, 0x00acac65U, 0x2a0e244fU,
+	0xac2b87c9U, 0x2e0b254bU, 0xa42185c1U, 0xffaf509aU,
+	0x54edb931U, 0x1b72697eU, 0x44f9bd21U, 0xe0749485U,
+	0x58e2ba3dU, 0x699ef70cU, 0x9e9709fbU, 0x2dcbe648U,
+	0x5ce7bb39U, 0x0e232d6bU, 0xe4719581U, 0xfbaa519eU,
+	0x5e67393bU, 0xe531d480U, 0xb83a82ddU, 0x6cdbb709U,
+	0xd108d9b4U, 0xeefb158bU, 0xec7b9789U, 0x92980af7U,
+	0x86890fe3U, 0xfd2fd298U, 0xb575c0d0U, 0x27416642U,
+	0xb2b002d7U, 0x4b367d2eU, 0x969d0bf3U, 0x1df7ea78U,
+	0x389aa25dU, 0xa561c4c0U, 0xd8429abdU, 0xf92ad39cU,
+	0x433c7f26U, 0xcb965daeU, 0x46793f23U, 0x7d8ff218U,
+	0x67117602U, 0x3a1a205fU, 0x84098de1U, 0x52683a37U,
+	0xd3885bb6U, 0xd44d99b1U, 0x572d7a32U, 0xa2a406c7U,
+	0xf6e51393U, 0x8bc64deeU, 0xa6a107c3U, 0x1f77687aU,
+	0x21c4e544U, 0xcad61cafU, 0x08a6ae6dU, 0x6e5b350bU,
+	0xe93ed78cU, 0xe87e968dU, 0x6a5e340fU, 0x07696e62U,
+	0x3b5a615eU, 0x39dae35cU, 0xa82e86cdU, 0x1e37297bU,
+	0xfaea109fU, 0x9158c9f4U, 0x04a9ad61U, 0x901888f5U,
+	0x6f1b740aU, 0xe2f41687U, 0xc916dfacU, 0x566d3b33U,
+	0x55adf830U, 0x59a2fb3cU, 0xf2e01297U, 0x3e1f215bU,
+	0x31d0e154U, 0x6591f400U, 0x9fd748faU, 0xdac218bfU,
+	0x177d6a72U, 0x5b22793eU, 0xc8569eadU, 0xd902dbbcU,
+	0x35d5e050U, 0xad6bc6c8U, 0x828c0ee7U, 0x2084a445U,
+	0x8c038fe9U, 0x64d1b501U, 0xcc539fa9U, 0x8d43cee8U,
+	0x798af31cU, 0x955dc8f0U, 0xb3f043d6U, 0x7e4f311bU,
+	0x60d4b405U, 0x7ccfb319U, 0x05e9ec60U, 0x18b2aa7dU,
+	0xf0609095U, 0x70c0b015U, 0x15fde870U, 0xeafe148fU,
+	0xa7e146c2U, 0xf4659191U, 0xed3bd688U, 0xa96ec7ccU,
+	0x8a860cefU, 0xbbfa41deU, 0x68deb60dU, 0xbd7fc2d8U,
+	0xc519dca0U, 0x2c8ba749U, 0xfc6f9399U, 0x6b1e750eU,
+	0x76453313U, 0xe7b15682U, 0xd78d5ab2U, 0x8e830debU,
+	0x5f27783aU, 0x0a262c6fU, 0xdec719bbU, 0xbfff40daU,
+	0xf525d090U, 0xebbe558eU, 0x14bda971U, 0x22042647U,
+	0x7a4a301fU, 0x2b4e654eU, 0x3f5f605aU, 0xcf935caaU,
+	0x13786b76U, 0x7180f114U, 0xa02484c5U, 0x72403217U,
+	0x77057212U, 0x4e733d2bU, 0x814ccde4U, 0x66513703U,
+	0x9a9208ffU, 0x2481a541U, 0x9c178bf9U, 0xa164c5c4U,
+	0x427c3e27U, 0xbaba00dfU, 0x3c9fa359U, 0x3495a151U,
+	0x23446746U, 0xc6d91fa3U, 0x32102257U, 0x7b0a711eU,
+	0x800c8ce5U, 0x2f4b644aU, 0x7f0f701aU, 0x4cf3bf29U,
+	0xfeef119bU, 0x6d9bf608U, 0xdf8758baU, 0x7585f010U,
+	0xf7a55292U, 0x50e8b835U, 0x78cab21dU, 0x5a62383fU,
+	0xc05c9ca5U, 0xb97ac3dcU, 0xcd13dea8U, 0x49b6ff2cU,
+	0xa3e447c6U, 0x9bd249feU, 0x288ea64dU, 0xe134d584U,
+	0x8fc34ceaU, 0x036c6f66U, 0xced31dabU, 0x48f6be2dU,
+	0xf86a929dU, 0x0b666d6eU, 0x5da7fa38U, 0x3ddfe258U,
+	0x41bcfd24U, 0x45b9fc20U, 0x9952cbfcU, 0xb170c1d4U,
+	0xc2dc1ea7U, 0x98128afdU, 0x4a763c2fU, 0xbebf01dbU,
+	0x1a32287fU, 0x83cc4fe6U, 0x06292f63U, 0x09e6ef6cU,
+	0xb03080d5U, 0x93d84bf6U, 0x8549cce0U, 0xf120d194U,
+	0x88068eedU, 0xb43581d1U, 0x9d57caf8U, 0x97dd4af2U,
+	0xdc479bb9U, 0xf3a05396U, 0x3090a055U, 0xd6cd1bb3U,
+	0x62543607U, 0xc39c5fa6U, 0x19f2eb7cU, 0x022c2e67U,
+	0x40fcbc25U, 0x25c1e440U, 0xb7f542d2U, 0xbc3f83d9U,
+	0x163d2b73U, 0x63147706U, 0x26012743U, 0x36152353U,
+	0xd2c81ab7U, 0x51a8f934U, 0xaaae04cfU, 0xdd07dab8U,
+	0xc4599da1U, 0xaeab05cbU, 0x29cee74cU, 0x0de3ee68U,
+	0x6194f504U, 0x12382a77U, 0x33506356U, 0xd04898b5U,
+	0xe3b45786U, 0x4db3fe28U, 0xefbb548aU, 0xe6f11783U,
+	0x941d89f1U, 0x37556252U, 0x01eced64U, 0x47397e22U,
+	0x1cb7ab79U, 0x87c94ee2U, 0xc11cdda4U, 0x74c5b111U,
+	0x11f8e974U, 0x4f337c2aU, 0x8946cfecU, 0x0ca3af69U,
+	0xabee45ceU, 0xdb8259beU, 0x10b8a875U, 0xd50dd8b0U,
+	0xe957be30U, 0xf009f929U, 0x159481ccU, 0x49df9690U,
+	0x8125a458U, 0x35bc89ecU, 0x4610569fU, 0x6cb2deb5U,
+	0xea977d33U, 0x68b7dfb1U, 0xe29d7f3bU, 0xb913aa60U,
+	0x125143cbU, 0x5dce9384U, 0x024547dbU, 0xa6c86e7fU,
+	0x1e5e40c7U, 0x2f220df6U, 0xd82bf301U, 0x6b771cb2U,
+	0x1a5b41c3U, 0x489fd791U, 0xa2cd6f7bU, 0xbd16ab64U,
+	0x18dbc3c1U, 0xa38d2e7aU, 0xfe867827U, 0x2a674df3U,
+	0x97b4234eU, 0xa847ef71U, 0xaac76d73U, 0xd424f00dU,
+	0xc035f519U, 0xbb932862U, 0xf3c93a2aU, 0x61fd9cb8U,
+	0xf40cf82dU, 0x0d8a87d4U, 0xd021f109U, 0x5b4b1082U,
+	0x7e2658a7U, 0xe3dd3e3aU, 0x9efe6047U, 0xbf962966U,
+	0x058085dcU, 0x8d2aa754U, 0x00c5c5d9U, 0x3b3308e2U,
+	0x21ad8cf8U, 0x7ca6daa5U, 0xc2b5771bU, 0x14d4c0cdU,
+	0x9534a14cU, 0x92f1634bU, 0x119180c8U, 0xe418fc3dU,
+	0xb059e969U, 0xcd7ab714U, 0xe01dfd39U, 0x59cb9280U,
+	0x67781fbeU, 0x8c6ae655U, 0x4e1a5497U, 0x28e7cff1U,
+	0xaf822d76U, 0xaec26c77U, 0x2ce2cef5U, 0x41d59498U,
+	0x7de69ba4U, 0x7f6619a6U, 0xee927c37U, 0x588bd381U,
+	0xbc56ea65U, 0xd7e4330eU, 0x4215579bU, 0xd6a4720fU,
+	0x29a78ef0U, 0xa448ec7dU, 0x8faa2556U, 0x10d1c1c9U,
+	0x131102caU, 0x1f1e01c6U, 0xb45ce86dU, 0x78a3dba1U,
+	0x776c1baeU, 0x232d0efaU, 0xd96bb200U, 0x9c7ee245U,
+	0x51c19088U, 0x1d9e83c4U, 0x8eea6457U, 0x9fbe2146U,
+	0x73691aaaU, 0xebd73c32U, 0xc430f41dU, 0x66385ebfU,
+	0xcabf7513U, 0x226d4ffbU, 0x8aef6553U, 0xcbff3412U,
+	0x3f3609e6U, 0xd3e1320aU, 0xf54cb92cU, 0x38f3cbe1U,
+	0x26684effU, 0x3a7349e3U, 0x4355169aU, 0x5e0e5087U,
+	0xb6dc6a6fU, 0x367c4aefU, 0x5341128aU, 0xac42ee75U,
+	0xe15dbc38U, 0xb2d96b6bU, 0xab872c72U, 0xefd23d36U,
+	0xcc3af615U, 0xfd46bb24U, 0x2e624cf7U, 0xfbc33822U,
+	0x83a5265aU, 0x6a375db3U, 0xbad36963U, 0x2da28ff4U,
+	0x30f9c9e9U, 0xa10dac78U, 0x9131a048U, 0xc83ff711U,
+	0x199b82c0U, 0x4c9ad695U, 0x987be341U, 0xf943ba20U,
+	0xb3992a6aU, 0xad02af74U, 0x5201538bU, 0x64b8dcbdU,
+	0x3cf6cae5U, 0x6df29fb4U, 0x79e39aa0U, 0x892fa650U,
+	0x55c4918cU, 0x373c0beeU, 0xe6987e3fU, 0x34fcc8edU,
+	0x31b988e8U, 0x08cfc7d1U, 0xc7f0371eU, 0x20edcdf9U,
+	0xdc2ef205U, 0x623d5fbbU, 0xdaab7103U, 0xe7d83f3eU,
+	0x04c0c4ddU, 0xfc06fa25U, 0x7a2359a3U, 0x72295babU,
+	0x65f89dbcU, 0x8065e559U, 0x74acd8adU, 0x3db68be4U,
+	0xc6b0761fU, 0x69f79eb0U, 0x39b38ae0U, 0x0a4f45d3U,
+	0xb853eb61U, 0x2b270cf2U, 0x993ba240U, 0x33390aeaU,
+	0xb119a868U, 0x165442cfU, 0x3e7648e7U, 0x1cdec2c5U,
+	0x86e0665fU, 0xffc63926U, 0x8baf2452U, 0x0f0a05d6U,
+	0xe558bd3cU, 0xdd6eb304U, 0x6e325cb7U, 0xa7882f7eU,
+	0xc97fb610U, 0x45d0959cU, 0x886fe751U, 0x0e4a44d7U,
+	0xbed66867U, 0x4dda9794U, 0x1b1b00c2U, 0x7b6318a2U,
+	0x070007deU, 0x030506daU, 0xdfee3106U, 0xf7cc3b2eU,
+	0x8460e45dU, 0xdeae7007U, 0x0ccac6d5U, 0xf803fb21U,
+	0x5c8ed285U, 0xc570b51cU, 0x4095d599U, 0x4f5a1596U,
+	0xf68c7a2fU, 0xd564b10cU, 0xc3f5361aU, 0xb79c2b6eU,
+	0xceba7417U, 0xf2897b2bU, 0xdbeb3002U, 0xd161b008U,
+	0x9afb6143U, 0xb51ca96cU, 0x762c5aafU, 0x9071e149U,
+	0x24e8ccfdU, 0x8520a55cU, 0x5f4e1186U, 0x4490d49dU,
+	0x064046dfU, 0x637d1ebaU, 0xf149b828U, 0xfa837923U,
+	0x5081d189U, 0x25a88dfcU, 0x60bdddb9U, 0x70a9d9a9U,
+	0x9474e04dU, 0x171403ceU, 0xec12fe35U, 0x9bbb2042U,
+	0x82e5675bU, 0xe817ff31U, 0x6f721db6U, 0x4b5f1492U,
+	0x27280ffeU, 0x5484d08dU, 0x75ec99acU, 0x96f4624fU,
+	0xa508ad7cU, 0x0b0f04d2U, 0xa907ae70U, 0xa04ded79U,
+	0xd2a1730bU, 0x71e998a8U, 0x4750179eU, 0x018584d8U,
+	0x5a0b5183U, 0xc175b418U, 0x87a0275eU, 0x32794bebU,
+	0x5744138eU, 0x098f86d0U, 0xcffa3516U, 0x4a1f5593U,
+	0xed52bf34U, 0x9d3ea344U, 0x5604528fU, 0x93b1224aU,
+	0xfc07fb75U, 0xe559bc6cU, 0x00c4c489U, 0x5c8fd3d5U,
+	0x9475e11dU, 0x20eccca9U, 0x534013daU, 0x79e29bf0U,
+	0xffc73876U, 0x7de79af4U, 0xf7cd3a7eU, 0xac43ef25U,
+	0x0701068eU, 0x489ed6c1U, 0x1715029eU, 0xb3982b3aU,
+	0x0b0e0582U, 0x3a7248b3U, 0xcd7bb644U, 0x7e2759f7U,
+	0x0f0b0486U, 0x5dcf92d4U, 0xb79d2a3eU, 0xa846ee21U,
+	0x0d8b8684U, 0xb6dd6b3fU, 0xebd63d62U, 0x3f3708b6U,
+	0x82e4660bU, 0xbd17aa34U, 0xbf972836U, 0xc174b548U,
+	0xd565b05cU, 0xaec36d27U, 0xe6997f6fU, 0x74add9fdU,
+	0xe15cbd68U, 0x18dac291U, 0xc571b44cU, 0x4e1b55c7U,
+	0x6b761de2U, 0xf68d7b7fU, 0x8bae2502U, 0xaac66c23U,
+	0x10d0c099U, 0x987ae211U, 0x1595809cU, 0x2e634da7U,
+	0x34fdc9bdU, 0x69f69fe0U, 0xd7e5325eU, 0x01848588U,
+	0x8064e409U, 0x87a1260eU, 0x04c1c58dU, 0xf148b978U,
+	0xa509ac2cU, 0xd82af251U, 0xf54db87cU, 0x4c9bd7c5U,
+	0x72285afbU, 0x993aa310U, 0x5b4a11d2U, 0x3db78ab4U,
+	0xbad26833U, 0xbb922932U, 0x39b28bb0U, 0x5485d1ddU,
+	0x68b6dee1U, 0x6a365ce3U, 0xfbc23972U, 0x4ddb96c4U,
+	0xa906af20U, 0xc2b4764bU, 0x574512deU, 0xc3f4374aU,
+	0x3cf7cbb5U, 0xb118a938U, 0x9afa6013U, 0x0581848cU,
+	0x0641478fU, 0x0a4e4483U, 0xa10cad28U, 0x6df39ee4U,
+	0x623c5eebU, 0x367d4bbfU, 0xcc3bf745U, 0x892ea700U,
+	0x4491d5cdU, 0x08cec681U, 0x9bba2112U, 0x8aee6403U,
+	0x66395fefU, 0xfe877977U, 0xd160b158U, 0x73681bfaU,
+	0xdfef3056U, 0x373d0abeU, 0x9fbf2016U, 0xdeaf7157U,
+	0x2a664ca3U, 0xc6b1774fU, 0xe01cfc69U, 0x2da38ea4U,
+	0x33380bbaU, 0x2f230ca6U, 0x560553dfU, 0x4b5e15c2U,
+	0xa38c2f2aU, 0x232c0faaU, 0x461157cfU, 0xb912ab30U,
+	0xf40df97dU, 0xa7892e2eU, 0xbed76937U, 0xfa827873U,
+	0xd96ab350U, 0xe816fe61U, 0x3b3209b2U, 0xee937d67U,
+	0x96f5631fU, 0x7f6718f6U, 0xaf832c26U, 0x38f2cab1U,
+	0x25a98cacU, 0xb45de93dU, 0x8461e50dU, 0xdd6fb254U,
+	0x0ccbc785U, 0x59ca93d0U, 0x8d2ba604U, 0xec13ff65U,
+	0xa6c96f2fU, 0xb852ea31U, 0x475116ceU, 0x71e899f8U,
+	0x29a68fa0U, 0x78a2daf1U, 0x6cb3dfe5U, 0x9c7fe315U,
+	0x4094d4c9U, 0x226c4eabU, 0xf3c83b7aU, 0x21ac8da8U,
+	0x24e9cdadU, 0x1d9f8294U, 0xd2a0725bU, 0x35bd88bcU,
+	0xc97eb740U, 0x776d1afeU, 0xcffb3446U, 0xf2887a7bU,
+	0x11908198U, 0xe956bf60U, 0x6f731ce6U, 0x67791eeeU,
+	0x70a8d8f9U, 0x9535a01cU, 0x61fc9de8U, 0x28e6cea1U,
+	0xd3e0335aU, 0x7ca7dbf5U, 0x2ce3cfa5U, 0x1f1f0096U,
+	0xad03ae24U, 0x3e7749b7U, 0x8c6be705U, 0x26694fafU,
+	0xa449ed2dU, 0x0304078aU, 0x2b260da2U, 0x098e8780U,
+	0x93b0231aU, 0xea967c63U, 0x9eff6117U, 0x1a5a4093U,
+	0xf008f879U, 0xc83ef641U, 0x7b6219f2U, 0xb2d86a3bU,
+	0xdc2ff355U, 0x5080d0d9U, 0x9d3fa214U, 0x1b1a0192U,
+	0xab862d22U, 0x588ad2d1U, 0x0e4b4587U, 0x6e335de7U,
+	0x1250429bU, 0x1655439fU, 0xcabe7443U, 0xe29c7e6bU,
+	0x9130a118U, 0xcbfe3542U, 0x199a8390U, 0xed53be64U,
+	0x49de97c0U, 0xd020f059U, 0x55c590dcU, 0x5a0a50d3U,
+	0xe3dc3f6aU, 0xc034f449U, 0xd6a5735fU, 0xa2cc6e2bU,
+	0xdbea3152U, 0xe7d93e6eU, 0xcebb7547U, 0xc431f54dU,
+	0x8fab2406U, 0xa04cec29U, 0x637c1feaU, 0x8521a40cU,
+	0x31b889b8U, 0x9070e019U, 0x4a1e54c3U, 0x51c091d8U,
+	0x1310039aU, 0x762d5bffU, 0xe419fd6dU, 0xefd33c66U,
+	0x45d194ccU, 0x30f8c8b9U, 0x75ed98fcU, 0x65f99cecU,
+	0x8124a508U, 0x0244468bU, 0xf942bb70U, 0x8eeb6507U,
+	0x97b5221eU, 0xfd47ba74U, 0x7a2258f3U, 0x5e0f51d7U,
+	0x32784abbU, 0x41d495c8U, 0x60bcdce9U, 0x83a4270aU,
+	0xb058e839U, 0x1e5f4197U, 0xbc57eb35U, 0xb51da83cU,
+	0xc7f1364eU, 0x64b9ddedU, 0x520052dbU, 0x14d5c19dU,
+	0x4f5b14c6U, 0xd425f15dU, 0x92f0621bU, 0x27290eaeU,
+	0x421456cbU, 0x1cdfc395U, 0xdaaa7053U, 0x5f4f10d6U,
+	0xf802fa71U, 0x886ee601U, 0x435417caU, 0x86e1670fU,
+	0x437536b8U, 0x5a2b71a1U, 0xbfb60944U, 0xe3fd1e18U,
+	0x2b072cd0U, 0x9f9e0164U, 0xec32de17U, 0xc690563dU,
+	0x40b5f5bbU, 0xc2955739U, 0x48bff7b3U, 0x133122e8U,
+	0xb873cb43U, 0xf7ec1b0cU, 0xa867cf53U, 0x0ceae6f7U,
+	0xb47cc84fU, 0x8500857eU, 0x72097b89U, 0xc155943aU,
+	0xb079c94bU, 0xe2bd5f19U, 0x08efe7f3U, 0x173423ecU,
+	0xb2f94b49U, 0x09afa6f2U, 0x54a4f0afU, 0x8045c57bU,
+	0x3d96abc6U, 0x026567f9U, 0x00e5e5fbU, 0x7e067885U,
+	0x6a177d91U, 0x11b1a0eaU, 0x59ebb2a2U, 0xcbdf1430U,
+	0x5e2e70a5U, 0xa7a80f5cU, 0x7a037981U, 0xf169980aU,
+	0xd404d02fU, 0x49ffb6b2U, 0x34dce8cfU, 0x15b4a1eeU,
+	0xafa20d54U, 0x27082fdcU, 0xaae74d51U, 0x9111806aU,
+	0x8b8f0470U, 0xd684522dU, 0x6897ff93U, 0xbef64845U,
+	0x3f1629c4U, 0x38d3ebc3U, 0xbbb30840U, 0x4e3a74b5U,
+	0x1a7b61e1U, 0x67583f9cU, 0x4a3f75b1U, 0xf3e91a08U,
+	0xcd5a9736U, 0x26486eddU, 0xe438dc1fU, 0x82c54779U,
+	0x05a0a5feU, 0x04e0e4ffU, 0x86c0467dU, 0xebf71c10U,
+	0xd7c4132cU, 0xd544912eU, 0x44b0f4bfU, 0xf2a95b09U,
+	0x167462edU, 0x7dc6bb86U, 0xe837df13U, 0x7c86fa87U,
+	0x83850678U, 0x0e6a64f5U, 0x2588addeU, 0xbaf34941U,
+	0xb9338a42U, 0xb53c894eU, 0x1e7e60e5U, 0xd2815329U,
+	0xdd4e9326U, 0x890f8672U, 0x73493a88U, 0x365c6acdU,
+	0xfbe31800U, 0xb7bc0b4cU, 0x24c8ecdfU, 0x359ca9ceU,
+	0xd94b9222U, 0x41f5b4baU, 0x6e127c95U, 0xcc1ad637U,
+	0x609dfd9bU, 0x884fc773U, 0x20cdeddbU, 0x61ddbc9aU,
+	0x9514816eU, 0x79c3ba82U, 0x5f6e31a4U, 0x92d14369U,
+	0x8c4ac677U, 0x9051c16bU, 0xe9779e12U, 0xf42cd80fU,
+	0x1cfee2e7U, 0x9c5ec267U, 0xf9639a02U, 0x066066fdU,
+	0x4b7f34b0U, 0x18fbe3e3U, 0x01a5a4faU, 0x45f0b5beU,
+	0x66187e9dU, 0x576433acU, 0x8440c47fU, 0x51e1b0aaU,
+	0x2987aed2U, 0xc015d53bU, 0x10f1e1ebU, 0x8780077cU,
+	0x9adb4161U, 0x0b2f24f0U, 0x3b1328c0U, 0x621d7f99U,
+	0xb3b90a48U, 0xe6b85e1dU, 0x32596bc9U, 0x536132a8U,
+	0x19bba2e2U, 0x072027fcU, 0xf823db03U, 0xce9a5435U,
+	0x96d4426dU, 0xc7d0173cU, 0xd3c11228U, 0x230d2ed8U,
+	0xffe61904U, 0x9d1e8366U, 0x4cbaf6b7U, 0x9ede4065U,
+	0x9b9b0060U, 0xa2ed4f59U, 0x6dd2bf96U, 0x8acf4571U,
+	0x760c7a8dU, 0xc81fd733U, 0x7089f98bU, 0x4dfab7b6U,
+	0xaee24c55U, 0x562472adU, 0xd001d12bU, 0xd80bd323U,
+	0xcfda1534U, 0x2a476dd1U, 0xde8e5025U, 0x9794036cU,
+	0x6c92fe97U, 0xc3d51638U, 0x93910268U, 0xa06dcd5bU,
+	0x127163e9U, 0x8105847aU, 0x33192ac8U, 0x991b8262U,
+	0x1b3b20e0U, 0xbc76ca47U, 0x9454c06fU, 0xb6fc4a4dU,
+	0x2cc2eed7U, 0x55e4b1aeU, 0x218dacdaU, 0xa5288d5eU,
+	0x4f7a35b4U, 0x774c3b8cU, 0xc410d43fU, 0x0daaa7f6U,
+	0x635d3e98U, 0xeff21d14U, 0x224d6fd9U, 0xa468cc5fU,
+	0x14f4e0efU, 0xe7f81f1cU, 0xb139884aU, 0xd141902aU,
+	0xad228f56U, 0xa9278e52U, 0x75ccb98eU, 0x5deeb3a6U,
+	0x2e426cd5U, 0x748cf88fU, 0xa6e84e5dU, 0x522173a9U,
+	0xf6ac5a0dU, 0x6f523d94U, 0xeab75d11U, 0xe5789d1eU,
+	0x5caef2a7U, 0x7f463984U, 0x69d7be92U, 0x1dbea3e6U,
+	0x6498fc9fU, 0x58abf3a3U, 0x71c9b88aU, 0x7b433880U,
+	0x30d9e9cbU, 0x1f3e21e4U, 0xdc0ed227U, 0x3a5369c1U,
+	0x8eca4475U, 0x2f022dd4U, 0xf56c990eU, 0xeeb25c15U,
+	0xac62ce57U, 0xc95f9632U, 0x5b6b30a0U, 0x50a1f1abU,
+	0xfaa35901U, 0x8f8a0574U, 0xca9f5531U, 0xda8b5121U,
+	0x3e5668c5U, 0xbd368b46U, 0x463076bdU, 0x3199a8caU,
+	0x28c7efd3U, 0x423577b9U, 0xc550953eU, 0xe17d9c1aU,
+	0x8d0a8776U, 0xfea65805U, 0xdfce1124U, 0x3cd6eac7U,
+	0x0f2a25f4U, 0xa12d8c5aU, 0x032526f8U, 0x0a6f65f1U,
+	0x7883fb83U, 0xdbcb1020U, 0xed729f16U, 0xaba70c50U,
+	0xf029d90bU, 0x6b573c90U, 0x2d82afd6U, 0x985bc363U,
+	0xfd669b06U, 0xa3ad0e58U, 0x65d8bd9eU, 0xe03ddd1bU,
+	0x477037bcU, 0x371c2bccU, 0xfc26da07U, 0x3993aac2U,
+	0x1c667af4U, 0x05383dedU, 0xe0a54508U, 0xbcee5254U,
+	0x7414609cU, 0xc08d4d28U, 0xb321925bU, 0x99831a71U,
+	0x1fa6b9f7U, 0x9d861b75U, 0x17acbbffU, 0x4c226ea4U,
+	0xe760870fU, 0xa8ff5740U, 0xf774831fU, 0x53f9aabbU,
+	0xeb6f8403U, 0xda13c932U, 0x2d1a37c5U, 0x9e46d876U,
+	0xef6a8507U, 0xbdae1355U, 0x57fcabbfU, 0x48276fa0U,
+	0xedea0705U, 0x56bceabeU, 0x0bb7bce3U, 0xdf568937U,
+	0x6285e78aU, 0x5d762bb5U, 0x5ff6a9b7U, 0x211534c9U,
+	0x350431ddU, 0x4ea2eca6U, 0x06f8feeeU, 0x94cc587cU,
+	0x013d3ce9U, 0xf8bb4310U, 0x251035cdU, 0xae7ad446U,
+	0x8b179c63U, 0x16ecfafeU, 0x6bcfa483U, 0x4aa7eda2U,
+	0xf0b14118U, 0x781b6390U, 0xf5f4011dU, 0xce02cc26U,
+	0xd49c483cU, 0x89971e61U, 0x3784b3dfU, 0xe1e50409U,
+	0x60056588U, 0x67c0a78fU, 0xe4a0440cU, 0x112938f9U,
+	0x45682dadU, 0x384b73d0U, 0x152c39fdU, 0xacfa5644U,
+	0x9249db7aU, 0x795b2291U, 0xbb2b9053U, 0xddd60b35U,
+	0x5ab3e9b2U, 0x5bf3a8b3U, 0xd9d30a31U, 0xb4e4505cU,
+	0x88d75f60U, 0x8a57dd62U, 0x1ba3b8f3U, 0xadba1745U,
+	0x49672ea1U, 0x22d5f7caU, 0xb724935fU, 0x2395b6cbU,
+	0xdc964a34U, 0x517928b9U, 0x7a9be192U, 0xe5e0050dU,
+	0xe620c60eU, 0xea2fc502U, 0x416d2ca9U, 0x8d921f65U,
+	0x825ddf6aU, 0xd61cca3eU, 0x2c5a76c4U, 0x694f2681U,
+	0xa4f0544cU, 0xe8af4700U, 0x7bdba093U, 0x6a8fe582U,
+	0x8658de6eU, 0x1ee6f8f6U, 0x310130d9U, 0x93099a7bU,
+	0x3f8eb1d7U, 0xd75c8b3fU, 0x7fdea197U, 0x3ecef0d6U,
+	0xca07cd22U, 0x26d0f6ceU, 0x007d7de8U, 0xcdc20f25U,
+	0xd3598a3bU, 0xcf428d27U, 0xb664d25eU, 0xab3f9443U,
+	0x43edaeabU, 0xc34d8e2bU, 0xa670d64eU, 0x59732ab1U,
+	0x146c78fcU, 0x47e8afafU, 0x5eb6e8b6U, 0x1ae3f9f2U,
+	0x390b32d1U, 0x08777fe0U, 0xdb538833U, 0x0ef2fce6U,
+	0x7694e29eU, 0x9f069977U, 0x4fe2ada7U, 0xd8934b30U,
+	0xc5c80d2dU, 0x543c68bcU, 0x6400648cU, 0x3d0e33d5U,
+	0xecaa4604U, 0xb9ab1251U, 0x6d4a2785U, 0x0c727ee4U,
+	0x46a8eeaeU, 0x58336bb0U, 0xa730974fU, 0x91891879U,
+	0xc9c70e21U, 0x98c35b70U, 0x8cd25e64U, 0x7c1e6294U,
+	0xa0f55548U, 0xc20dcf2aU, 0x13a9bafbU, 0xc1cd0c29U,
+	0xc4884c2cU, 0xfdfe0315U, 0x32c1f3daU, 0xd5dc093dU,
+	0x291f36c1U, 0x970c9b7fU, 0x2f9ab5c7U, 0x12e9fbfaU,
+	0xf1f10019U, 0x09373ee1U, 0x8f129d67U, 0x87189f6fU,
+	0x90c95978U, 0x7554219dU, 0x819d1c69U, 0xc8874f20U,
+	0x3381b2dbU, 0x9cc65a74U, 0xcc824e24U, 0xff7e8117U,
+	0x4d622fa5U, 0xde16c836U, 0x6c0a6684U, 0xc608ce2eU,
+	0x44286cacU, 0xe365860bU, 0xcb478c23U, 0xe9ef0601U,
+	0x73d1a29bU, 0x0af7fde2U, 0x7e9ee096U, 0xfa3bc112U,
+	0x106979f8U, 0x285f77c0U, 0x9b039873U, 0x52b9ebbaU,
+	0x3c4e72d4U, 0xb0e15158U, 0x7d5e2395U, 0xfb7b8013U,
+	0x4be7aca3U, 0xb8eb5350U, 0xee2ac406U, 0x8e52dc66U,
+	0xf231c31aU, 0xf634c21eU, 0x2adff5c2U, 0x02fdffeaU,
+	0x71512099U, 0x2b9fb4c3U, 0xf9fb0211U, 0x0d323fe5U,
+	0xa9bf1641U, 0x304171d8U, 0xb5a4115dU, 0xba6bd152U,
+	0x03bdbeebU, 0x205575c8U, 0x36c4f2deU, 0x42adefaaU,
+	0x3b8bb0d3U, 0x07b8bfefU, 0x2edaf4c6U, 0x245074ccU,
+	0x6fcaa587U, 0x402d6da8U, 0x831d9e6bU, 0x6540258dU,
+	0xd1d90839U, 0x70116198U, 0xaa7fd542U, 0xb1a11059U,
+	0xf371821bU, 0x964cda7eU, 0x04787cecU, 0x0fb2bde7U,
+	0xa5b0154dU, 0xd0994938U, 0x958c197dU, 0x85981d6dU,
+	0x61452489U, 0xe225c70aU, 0x19233af1U, 0x6e8ae486U,
+	0x77d4a39fU, 0x1d263bf5U, 0x9a43d972U, 0xbe6ed056U,
+	0xd219cb3aU, 0xa1b51449U, 0x80dd5d68U, 0x63c5a68bU,
+	0x503969b8U, 0xfe3ec016U, 0x5c366ab4U, 0x557c29bdU,
+	0x2790b7cfU, 0x84d85c6cU, 0xb261d35aU, 0xf4b4401cU,
+	0xaf3a9547U, 0x344470dcU, 0x7291e39aU, 0xc7488f2fU,
+	0xa275d74aU, 0xfcbe4214U, 0x3acbf1d2U, 0xbf2e9157U,
+	0x18637bf0U, 0x680f6780U, 0xa335964bU, 0x6680e68eU,
+	0x6881e967U, 0x71dfae7eU, 0x9442d69bU, 0xc809c1c7U,
+	0x00f3f30fU, 0xb46adebbU, 0xc7c601c8U, 0xed6489e2U,
+	0x6b412a64U, 0xe96188e6U, 0x634b286cU, 0x38c5fd37U,
+	0x9387149cU, 0xdc18c4d3U, 0x8393108cU, 0x271e3928U,
+	0x9f881790U, 0xaef45aa1U, 0x59fda456U, 0xeaa14be5U,
+	0x9b8d1694U, 0xc94980c6U, 0x231b382cU, 0x3cc0fc33U,
+	0x990d9496U, 0x225b792dU, 0x7f502f70U, 0xabb11aa4U,
+	0x16627419U, 0x2991b826U, 0x2b113a24U, 0x55f2a75aU,
+	0x41e3a24eU, 0x3a457f35U, 0x721f6d7dU, 0xe02bcbefU,
+	0x75daaf7aU, 0x8c5cd083U, 0x51f7a65eU, 0xda9d47d5U,
+	0xfff00ff0U, 0x620b696dU, 0x1f283710U, 0x3e407e31U,
+	0x8456d28bU, 0x0cfcf003U, 0x8113928eU, 0xbae55fb5U,
+	0xa07bdbafU, 0xfd708df2U, 0x4363204cU, 0x9502979aU,
+	0x14e2f61bU, 0x1327341cU, 0x9047d79fU, 0x65ceab6aU,
+	0x318fbe3eU, 0x4cace043U, 0x61cbaa6eU, 0xd81dc5d7U,
+	0xe6ae48e9U, 0x0dbcb102U, 0xcfcc03c0U, 0xa93198a6U,
+	0x2e547a21U, 0x2f143b20U, 0xad3499a2U, 0xc003c3cfU,
+	0xfc30ccf3U, 0xfeb04ef1U, 0x6f442b60U, 0xd95d84d6U,
+	0x3d80bd32U, 0x56326459U, 0xc3c300ccU, 0x57722558U,
+	0xa871d9a7U, 0x259ebb2aU, 0x0e7c7201U, 0x9107969eU,
+	0x92c7559dU, 0x9ec85691U, 0x358abf3aU, 0xf9758cf6U,
+	0xf6ba4cf9U, 0xa2fb59adU, 0x58bde557U, 0x1da8b512U,
+	0xd017c7dfU, 0x9c48d493U, 0x0f3c3300U, 0x1e687611U,
+	0xf2bf4dfdU, 0x6a016b65U, 0x45e6a34aU, 0xe7ee09e8U,
+	0x4b692244U, 0xa3bb18acU, 0x0b393204U, 0x4a296345U,
+	0xbee05eb1U, 0x5237655dU, 0x749aee7bU, 0xb9259cb6U,
+	0xa7be19a8U, 0xbba51eb4U, 0xc28341cdU, 0xdfd807d0U,
+	0x370a3d38U, 0xb7aa1db8U, 0xd29745ddU, 0x2d94b922U,
+	0x608beb6fU, 0x330f3c3cU, 0x2a517b25U, 0x6e046a61U,
+	0x4deca142U, 0x7c90ec73U, 0xafb41ba0U, 0x7a156f75U,
+	0x0273710dU, 0xebe10ae4U, 0x3b053e34U, 0xac74d8a3U,
+	0xb12f9ebeU, 0x20dbfb2fU, 0x10e7f71fU, 0x49e9a046U,
+	0x984dd597U, 0xcd4c81c2U, 0x19adb416U, 0x7895ed77U,
+	0x324f7d3dU, 0x2cd4f823U, 0xd3d704dcU, 0xe56e8beaU,
+	0xbd209db2U, 0xec24c8e3U, 0xf835cdf7U, 0x08f9f107U,
+	0xd412c6dbU, 0xb6ea5cb9U, 0x674e2968U, 0xb52a9fbaU,
+	0xb06fdfbfU, 0x89199086U, 0x46266049U, 0xa13b9aaeU,
+	0x5df8a552U, 0xe3eb08ecU, 0x5b7d2654U, 0x660e6869U,
+	0x8516938aU, 0x7dd0ad72U, 0xfbf50ef4U, 0xf3ff0cfcU,
+	0xe42ecaebU, 0x01b3b20eU, 0xf57a8ffaU, 0xbc60dcb3U,
+	0x47662148U, 0xe821c9e7U, 0xb865ddb7U, 0x8b991284U,
+	0x3985bc36U, 0xaaf15ba5U, 0x18edf517U, 0xb2ef5dbdU,
+	0x30cfff3fU, 0x97821598U, 0xbfa01fb0U, 0x9d089592U,
+	0x07363108U, 0x7e106e71U, 0x0a797305U, 0x8edc5281U,
+	0x648eea6bU, 0x5cb8e453U, 0xefe40be0U, 0x265e7829U,
+	0x48a9e147U, 0xc406c2cbU, 0x09b9b006U, 0x8f9c1380U,
+	0x3f003f30U, 0xcc0cc0c3U, 0x9acd5795U, 0xfab54ff5U,
+	0x86d65089U, 0x82d3518dU, 0x5e386651U, 0x761a6c79U,
+	0x05b6b30aU, 0x5f782750U, 0x8d1c9182U, 0x79d5ac76U,
+	0xdd5885d2U, 0x44a6e24bU, 0xc14382ceU, 0xce8c42c1U,
+	0x775a2d78U, 0x54b2e65bU, 0x4223614dU, 0x364a7c39U,
+	0x4f6c2340U, 0x735f2c7cU, 0x5a3d6755U, 0x50b7e75fU,
+	0x1b2d3614U, 0x34cafe3bU, 0xf7fa0df8U, 0x11a7b61eU,
+	0xa53e9baaU, 0x04f6f20bU, 0xde9846d1U, 0xc54683caU,
+	0x87961188U, 0xe2ab49edU, 0x709fef7fU, 0x7b552e74U,
+	0xd15786deU, 0xa47edaabU, 0xe16b8aeeU, 0xf17f8efeU,
+	0x15a2b71aU, 0x96c25499U, 0x6dc4a962U, 0x1a6d7715U,
+	0x0333300cU, 0x69c1a866U, 0xeea44ae1U, 0xca8943c5U,
+	0xa6fe58a9U, 0xd55287daU, 0xf43acefbU, 0x17223518U,
+	0x24defa2bU, 0x8ad95385U, 0x28d1f927U, 0x219bba2eU,
+	0x5377245cU, 0xf03fcfffU, 0xc68640c9U, 0x8053d38fU,
+	0xdbdd06d4U, 0x40a3e34fU, 0x06767009U, 0xb3af1cbcU,
+	0xd69244d9U, 0x8859d187U, 0x4e2c6241U, 0xcbc902c4U,
+	0x6c84e863U, 0x1ce8f413U, 0xd7d205d8U, 0x1267751dU,
+	0x3cc4f876U, 0x259abf6fU, 0xc007c78aU, 0x9c4cd0d6U,
+	0x54b6e21eU, 0xe02fcfaaU, 0x938310d9U, 0xb92198f3U,
+	0x3f043b75U, 0xbd2499f7U, 0x370e397dU, 0x6c80ec26U,
+	0xc7c2058dU, 0x885dd5c2U, 0xd7d6019dU, 0x735b2839U,
+	0xcbcd0681U, 0xfab14bb0U, 0x0db8b547U, 0xbee45af4U,
+	0xcfc80785U, 0x9d0c91d7U, 0x775e293dU, 0x6885ed22U,
+	0xcd488587U, 0x761e683cU, 0x2b153e61U, 0xfff40bb5U,
+	0x42276508U, 0x7dd4a937U, 0x7f542b35U, 0x01b7b64bU,
+	0x15a6b35fU, 0x6e006e24U, 0x265a7c6cU, 0xb46edafeU,
+	0x219fbe6bU, 0xd819c192U, 0x05b2b74fU, 0x8ed856c4U,
+	0xabb51ee1U, 0x364e787cU, 0x4b6d2601U, 0x6a056f20U,
+	0xd013c39aU, 0x58b9e112U, 0xd556839fU, 0xeea04ea4U,
+	0xf43ecabeU, 0xa9359ce3U, 0x1726315dU, 0xc147868bU,
+	0x40a7e70aU, 0x4762250dU, 0xc402c68eU, 0x318bba7bU,
+	0x65caaf2fU, 0x18e9f152U, 0x358ebb7fU, 0x8c58d4c6U,
+	0xb2eb59f8U, 0x59f9a013U, 0x9b8912d1U, 0xfd7489b7U,
+	0x7a116b30U, 0x7b512a31U, 0xf97188b3U, 0x9446d2deU,
+	0xa875dde2U, 0xaaf55fe0U, 0x3b013a71U, 0x8d1895c7U,
+	0x69c5ac23U, 0x02777548U, 0x978611ddU, 0x03373449U,
+	0xfc34c8b6U, 0x71dbaa3bU, 0x5a396310U, 0xc542878fU,
+	0xc682448cU, 0xca8d4780U, 0x61cfae2bU, 0xad309de7U,
+	0xa2ff5de8U, 0xf6be48bcU, 0x0cf8f446U, 0x49eda403U,
+	0x8452d6ceU, 0xc80dc582U, 0x5b792211U, 0x4a2d6700U,
+	0xa6fa5cecU, 0x3e447a74U, 0x11a3b25bU, 0xb3ab18f9U,
+	0x1f2c3355U, 0xf7fe09bdU, 0x5f7c2315U, 0x1e6c7254U,
+	0xeaa54fa0U, 0x0672744cU, 0x20dfff6aU, 0xed608da7U,
+	0xf3fb08b9U, 0xefe00fa5U, 0x96c650dcU, 0x8b9d16c1U,
+	0x634f2c29U, 0xe3ef0ca9U, 0x86d254ccU, 0x79d1a833U,
+	0x34cefa7eU, 0x674a2d2dU, 0x7e146a34U, 0x3a417b70U,
+	0x19a9b053U, 0x28d5fd62U, 0xfbf10ab1U, 0x2e507e64U,
+	0x5636601cU, 0xbfa41bf5U, 0x6f402f25U, 0xf831c9b2U,
+	0xe56a8fafU, 0x749eea3eU, 0x44a2e60eU, 0x1dacb157U,
+	0xcc08c486U, 0x990990d3U, 0x4de8a507U, 0x2cd0fc66U,
+	0x660a6c2cU, 0x7891e932U, 0x879215cdU, 0xb12b9afbU,
+	0xe9658ca3U, 0xb861d9f2U, 0xac70dce6U, 0x5cbce016U,
+	0x8057d7caU, 0xe2af4da8U, 0x330b3879U, 0xe16f8eabU,
+	0xe42aceaeU, 0xdd5c8197U, 0x12637158U, 0xf57e8bbfU,
+	0x09bdb443U, 0xb7ae19fdU, 0x0f383745U, 0x324b7978U,
+	0xd153829bU, 0x2995bc63U, 0xafb01fe5U, 0xa7ba1dedU,
+	0xb06bdbfaU, 0x55f6a31fU, 0xa13f9eebU, 0xe825cda2U,
+	0x13233059U, 0xbc64d8f6U, 0xec20cca6U, 0xdfdc0395U,
+	0x6dc0ad27U, 0xfeb44ab4U, 0x4ca8e406U, 0xe6aa4cacU,
+	0x648aee2eU, 0xc3c70489U, 0xebe50ea1U, 0xc94d8483U,
+	0x53732019U, 0x2a557f60U, 0x5e3c6214U, 0xda994390U,
+	0x30cbfb7aU, 0x08fdf542U, 0xbba11af1U, 0x721b6938U,
+	0x1cecf056U, 0x9043d3daU, 0x5dfca117U, 0xdbd90291U,
+	0x6b452e21U, 0x9849d1d2U, 0xce884684U, 0xaef05ee4U,
+	0xd2934198U, 0xd696409cU, 0x0a7d7740U, 0x225f7d68U,
+	0x51f3a21bU, 0x0b3d3641U, 0xd9598093U, 0x2d90bd67U,
+	0x891d94c3U, 0x10e3f35aU, 0x950693dfU, 0x9ac953d0U,
+	0x231f3c69U, 0x00f7f74aU, 0x1666705cU, 0x620f6d28U,
+	0x1b293251U, 0x271a3d6dU, 0x0e787644U, 0x04f2f64eU,
+	0x4f682705U, 0x608fef2aU, 0xa3bf1ce9U, 0x45e2a70fU,
+	0xf17b8abbU, 0x50b3e31aU, 0x8add57c0U, 0x910392dbU,
+	0xd3d30099U, 0xb6ee58fcU, 0x24dafe6eU, 0x2f103f65U,
+	0x851297cfU, 0xf03bcbbaU, 0xb52e9bffU, 0xa53a9fefU,
+	0x41e7a60bU, 0xc2874588U, 0x3981b873U, 0x4e286604U,
+	0x5776211dU, 0x3d84b977U, 0xbae15bf0U, 0x9ecc52d4U,
+	0xf2bb49b8U, 0x811796cbU, 0xa07fdfeaU, 0x43672409U,
+	0x709beb3aU, 0xde9c4294U, 0x7c94e836U, 0x75deab3fU,
+	0x0732354dU, 0xa47adeeeU, 0x92c351d8U, 0xd416c29eU,
+	0x8f9817c5U, 0x14e6f25eU, 0x52336118U, 0xe7ea0dadU,
+	0x82d755c8U, 0xdc1cc096U, 0x1a697350U, 0x9f8c13d5U,
+	0x38c1f972U, 0x48ade502U, 0x839714c9U, 0x4622640cU,
+	0xebff149aU, 0xf2a15383U, 0x173c2b66U, 0x4b773c3aU,
+	0x838d0ef2U, 0x37142346U, 0x44b8fc35U, 0x6e1a741fU,
+	0xe83fd799U, 0x6a1f751bU, 0xe035d591U, 0xbbbb00caU,
+	0x10f9e961U, 0x5f66392eU, 0x00eded71U, 0xa460c4d5U,
+	0x1cf6ea6dU, 0x2d8aa75cU, 0xda8359abU, 0x69dfb618U,
+	0x18f3eb69U, 0x4a377d3bU, 0xa065c5d1U, 0xbfbe01ceU,
+	0x1a73696bU, 0xa12584d0U, 0xfc2ed28dU, 0x28cfe759U,
+	0x951c89e4U, 0xaaef45dbU, 0xa86fc7d9U, 0xd68c5aa7U,
+	0xc29d5fb3U, 0xb93b82c8U, 0xf1619080U, 0x63553612U,
+	0xf6a45287U, 0x0f222d7eU, 0xd2895ba3U, 0x59e3ba28U,
+	0x7c8ef20dU, 0xe1759490U, 0x9c56caedU, 0xbd3e83ccU,
+	0x07282f76U, 0x8f820dfeU, 0x026d6f73U, 0x399ba248U,
+	0x23052652U, 0x7e0e700fU, 0xc01dddb1U, 0x167c6a67U,
+	0x979c0be6U, 0x9059c9e1U, 0x13392a62U, 0xe6b05697U,
+	0xb2f143c3U, 0xcfd21dbeU, 0xe2b55793U, 0x5b63382aU,
+	0x65d0b514U, 0x8ec24cffU, 0x4cb2fe3dU, 0x2a4f655bU,
+	0xad2a87dcU, 0xac6ac6ddU, 0x2e4a645fU, 0x437d3e32U,
+	0x7f4e310eU, 0x7dceb30cU, 0xec3ad69dU, 0x5a23792bU,
+	0xbefe40cfU, 0xd54c99a4U, 0x40bdfd31U, 0xd40cd8a5U,
+	0x2b0f245aU, 0xa6e046d7U, 0x8d028ffcU, 0x12796b63U,
+	0x11b9a860U, 0x1db6ab6cU, 0xb6f442c7U, 0x7a0b710bU,
+	0x75c4b104U, 0x2185a450U, 0xdbc318aaU, 0x9ed648efU,
+	0x53693a22U, 0x1f36296eU, 0x8c42cefdU, 0x9d168becU,
+	0x71c1b000U, 0xe97f9698U, 0xc6985eb7U, 0x6490f415U,
+	0xc817dfb9U, 0x20c5e551U, 0x8847cff9U, 0xc9579eb8U,
+	0x3d9ea34cU, 0xd14998a0U, 0xf7e41386U, 0x3a5b614bU,
+	0x24c0e455U, 0x38dbe349U, 0x41fdbc30U, 0x5ca6fa2dU,
+	0xb474c0c5U, 0x34d4e045U, 0x51e9b820U, 0xaeea44dfU,
+	0xe3f51692U, 0xb071c1c1U, 0xa92f86d8U, 0xed7a979cU,
+	0xce925cbfU, 0xffee118eU, 0x2ccae65dU, 0xf96b9288U,
+	0x810d8cf0U, 0x689ff719U, 0xb87bc3c9U, 0x2f0a255eU,
+	0x32516343U, 0xa3a506d2U, 0x93990ae2U, 0xca975dbbU,
+	0x1b33286aU, 0x4e327c3fU, 0x9ad349ebU, 0xfbeb108aU,
+	0xb13180c0U, 0xafaa05deU, 0x50a9f921U, 0x66107617U,
+	0x3e5e604fU, 0x6f5a351eU, 0x7b4b300aU, 0x8b870cfaU,
+	0x576c3b26U, 0x3594a144U, 0xe430d495U, 0x36546247U,
+	0x33112242U, 0x0a676d7bU, 0xc5589db4U, 0x22456753U,
+	0xde8658afU, 0x6095f511U, 0xd803dba9U, 0xe5709594U,
+	0x06686e77U, 0xfeae508fU, 0x788bf309U, 0x7081f101U,
+	0x67503716U, 0x82cd4ff3U, 0x76047207U, 0x3f1e214eU,
+	0xc418dcb5U, 0x6b5f341aU, 0x3b1b204aU, 0x08e7ef79U,
+	0xbafb41cbU, 0x298fa658U, 0x9b9308eaU, 0x3191a040U,
+	0xb3b102c2U, 0x14fce865U, 0x3cdee24dU, 0x1e76686fU,
+	0x8448ccf5U, 0xfd6e938cU, 0x89078ef8U, 0x0da2af7cU,
+	0xe7f01796U, 0xdfc619aeU, 0x6c9af61dU, 0xa52085d4U,
+	0xcbd71cbaU, 0x47783f36U, 0x8ac74dfbU, 0x0ce2ee7dU,
+	0xbc7ec2cdU, 0x4f723d3eU, 0x19b3aa68U, 0x79cbb208U,
+	0x05a8ad74U, 0x01adac70U, 0xdd469bacU, 0xf5649184U,
+	0x86c84ef7U, 0xdc06daadU, 0x0e626c7fU, 0xfaab518bU,
+	0x5e26782fU, 0xc7d81fb6U, 0x423d7f33U, 0x4df2bf3cU,
+	0xf424d085U, 0xd7cc1ba6U, 0xc15d9cb0U, 0xb53481c4U,
+	0xcc12debdU, 0xf021d181U, 0xd9439aa8U, 0xd3c91aa2U,
+	0x9853cbe9U, 0xb7b403c6U, 0x7484f005U, 0x92d94be3U,
+	0x26406657U, 0x87880ff6U, 0x5de6bb2cU, 0x46387e37U,
+	0x04e8ec75U, 0x61d5b410U, 0xf3e11282U, 0xf82bd389U,
+	0x52297b23U, 0x27002756U, 0x62157713U, 0x72017303U,
+	0x96dc4ae7U, 0x15bca964U, 0xeeba549fU, 0x99138ae8U,
+	0x804dcdf1U, 0xeabf559bU, 0x6ddab71cU, 0x49f7be38U,
+	0x2580a554U, 0x562c7a27U, 0x77443306U, 0x945cc8e5U,
+	0xa7a007d6U, 0x09a7ae78U, 0xabaf04daU, 0xa2e547d3U,
+	0xd009d9a1U, 0x73413202U, 0x45f8bd34U, 0x032d2e72U,
+	0x58a3fb29U, 0xc3dd1eb2U, 0x85088df4U, 0x30d1e141U,
+	0x55ecb924U, 0x0b272c7aU, 0xcd529fbcU, 0x48b7ff39U,
+	0xeffa159eU, 0x9f9609eeU, 0x54acf825U, 0x911988e0U,
+	0x55d98c02U, 0x4c87cb1bU, 0xa91ab3feU, 0xf551a4a2U,
+	0x3dab966aU, 0x8932bbdeU, 0xfa9e64adU, 0xd03cec87U,
+	0x56194f01U, 0xd439ed83U, 0x5e134d09U, 0x059d9852U,
+	0xaedf71f9U, 0xe140a1b6U, 0xbecb75e9U, 0x1a465c4dU,
+	0xa2d072f5U, 0x93ac3fc4U, 0x64a5c133U, 0xd7f92e80U,
+	0xa6d573f1U, 0xf411e5a3U, 0x1e435d49U, 0x01989956U,
+	0xa455f1f3U, 0x1f031c48U, 0x42084a15U, 0x96e97fc1U,
+	0x2b3a117cU, 0x14c9dd43U, 0x16495f41U, 0x68aac23fU,
+	0x7cbbc72bU, 0x071d1a50U, 0x4f470818U, 0xdd73ae8aU,
+	0x4882ca1fU, 0xb104b5e6U, 0x6cafc33bU, 0xe7c522b0U,
+	0xc2a86a95U, 0x5f530c08U, 0x22705275U, 0x03181b54U,
+	0xb90eb7eeU, 0x31a49566U, 0xbc4bf7ebU, 0x87bd3ad0U,
+	0x9d23becaU, 0xc028e897U, 0x7e3b4529U, 0xa85af2ffU,
+	0x29ba937eU, 0x2e7f5179U, 0xad1fb2faU, 0x5896ce0fU,
+	0x0cd7db5bU, 0x71f48526U, 0x5c93cf0bU, 0xe545a0b2U,
+	0xdbf62d8cU, 0x30e4d467U, 0xf29466a5U, 0x9469fdc3U,
+	0x130c1f44U, 0x124c5e45U, 0x906cfcc7U, 0xfd5ba6aaU,
+	0xc168a996U, 0xc3e82b94U, 0x521c4e05U, 0xe405e1b3U,
+	0x00d8d857U, 0x6b6a013cU, 0xfe9b65a9U, 0x6a2a403dU,
+	0x9529bcc2U, 0x18c6de4fU, 0x33241764U, 0xac5ff3fbU,
+	0xaf9f30f8U, 0xa39033f4U, 0x08d2da5fU, 0xc42de993U,
+	0xcbe2299cU, 0x9fa33cc8U, 0x65e58032U, 0x20f0d077U,
+	0xed4fa2baU, 0xa110b1f6U, 0x32645665U, 0x23301374U,
+	0xcfe72898U, 0x57590e00U, 0x78bec62fU, 0xdab66c8dU,
+	0x76314721U, 0x9ee37dc9U, 0x36615761U, 0x77710620U,
+	0x83b83bd4U, 0x6f6f0038U, 0x49c28b1eU, 0x847df9d3U,
+	0x9ae67ccdU, 0x86fd7bd1U, 0xffdb24a8U, 0xe28062b5U,
+	0x0a52585dU, 0x8af278ddU, 0xefcf20b8U, 0x10ccdc47U,
+	0x5dd38e0aU, 0x0e575959U, 0x17091e40U, 0x535c0f04U,
+	0x70b4c427U, 0x41c88916U, 0x92ec7ec5U, 0x474d0a10U,
+	0x3f2b1468U, 0xd6b96f81U, 0x065d5b51U, 0x912cbdc6U,
+	0x8c77fbdbU, 0x1d839e4aU, 0x2dbf927aU, 0x74b1c523U,
+	0xa515b0f2U, 0xf014e4a7U, 0x24f5d173U, 0x45cd8812U,
+	0x0f171858U, 0x118c9d46U, 0xee8f61b9U, 0xd836ee8fU,
+	0x8078f8d7U, 0xd17cad86U, 0xc56da892U, 0x35a19462U,
+	0xe94aa3beU, 0x8bb239dcU, 0x5a164c0dU, 0x8872fadfU,
+	0x8d37badaU, 0xb441f5e3U, 0x7b7e052cU, 0x9c63ffcbU,
+	0x60a0c037U, 0xdeb36d89U, 0x66254331U, 0x5b560d0cU,
+	0xb84ef6efU, 0x4088c817U, 0xc6ad6b91U, 0xcea76999U,
+	0xd976af8eU, 0x3cebd76bU, 0xc822ea9fU, 0x8138b9d6U,
+	0x7a3e442dU, 0xd579ac82U, 0x853db8d2U, 0xb6c177e1U,
+	0x04ddd953U, 0x97a93ec0U, 0x25b59072U, 0x8fb738d8U,
+	0x0d979a5aU, 0xaada70fdU, 0x82f87ad5U, 0xa050f0f7U,
+	0x3a6e546dU, 0x43480b14U, 0x37211660U, 0xb38437e4U,
+	0x59d68f0eU, 0x61e08136U, 0xd2bc6e85U, 0x1b061d4cU,
+	0x75f18422U, 0xf95ea7aeU, 0x34e1d563U, 0xb2c476e5U,
+	0x02585a55U, 0xf154a5a6U, 0xa79532f0U, 0xc7ed2a90U,
+	0xbb8e35ecU, 0xbf8b34e8U, 0x63600334U, 0x4b42091cU,
+	0x38eed66fU, 0x62204235U, 0xb044f4e7U, 0x448dc913U,
+	0xe000e0b7U, 0x79fe872eU, 0xfc1be7abU, 0xf3d427a4U,
+	0x4a02481dU, 0x69ea833eU, 0x7f7b0428U, 0x0b12195cU,
+	0x72344625U, 0x4e074919U, 0x67650230U, 0x6def823aU,
+	0x26755371U, 0x09929b5eU, 0xcaa2689dU, 0x2cffd37bU,
+	0x9866fecfU, 0x39ae976eU, 0xe3c023b4U, 0xf81ee6afU,
+	0xbace74edU, 0xdff32c88U, 0x4dc78a1aU, 0x460d4b11U,
+	0xec0fe3bbU, 0x9926bfceU, 0xdc33ef8bU, 0xcc27eb9bU,
+	0x28fad27fU, 0xab9a31fcU, 0x509ccc07U, 0x27351270U,
+	0x3e6b5569U, 0x5499cd03U, 0xd3fc2f84U, 0xf7d126a0U,
+	0x9ba63dccU, 0xe80ae2bfU, 0xc962ab9eU, 0x2a7a507dU,
+	0x19869f4eU, 0xb78136e0U, 0x15899c42U, 0x1cc3df4bU,
+	0x6e2f4139U, 0xcd67aa9aU, 0xfbde25acU, 0xbd0bb6eaU,
+	0xe68563b1U, 0x7dfb862aU, 0x3b2e156cU, 0x8ef779d9U,
+	0xebca21bcU, 0xb501b4e2U, 0x73740724U, 0xf69167a1U,
+	0x51dc8d06U, 0x21b09176U, 0xea8a60bdU, 0x2f3f1078U,
+	0xb211a32dU, 0xab4fe434U, 0x4ed29cd1U, 0x12998b8dU,
+	0xda63b945U, 0x6efa94f1U, 0x1d564b82U, 0x37f4c3a8U,
+	0xb1d1602eU, 0x33f1c2acU, 0xb9db6226U, 0xe255b77dU,
+	0x49175ed6U, 0x06888e99U, 0x59035ac6U, 0xfd8e7362U,
+	0x45185ddaU, 0x746410ebU, 0x836dee1cU, 0x303101afU,
+	0x411d5cdeU, 0x13d9ca8cU, 0xf98b7266U, 0xe650b679U,
+	0x439ddedcU, 0xf8cb3367U, 0xa5c0653aU, 0x712150eeU,
+	0xccf23e53U, 0xf301f26cU, 0xf181706eU, 0x8f62ed10U,
+	0x9b73e804U, 0xe0d5357fU, 0xa88f2737U, 0x3abb81a5U,
+	0xaf4ae530U, 0x56cc9ac9U, 0x8b67ec14U, 0x000d0d9fU,
+	0x256045baU, 0xb89b2327U, 0xc5b87d5aU, 0xe4d0347bU,
+	0x5ec698c1U, 0xd66cba49U, 0x5b83d8c4U, 0x607515ffU,
+	0x7aeb91e5U, 0x27e0c7b8U, 0x99f36a06U, 0x4f92ddd0U,
+	0xce72bc51U, 0xc9b77e56U, 0x4ad79dd5U, 0xbf5ee120U,
+	0xeb1ff474U, 0x963caa09U, 0xbb5be024U, 0x028d8f9dU,
+	0x3c3e02a3U, 0xd72cfb48U, 0x155c498aU, 0x73a1d2ecU,
+	0xf4c4306bU, 0xf584716aU, 0x77a4d3e8U, 0x1a938985U,
+	0x26a086b9U, 0x242004bbU, 0xb5d4612aU, 0x03cdce9cU,
+	0xe710f778U, 0x8ca22e13U, 0x19534a86U, 0x8de26f12U,
+	0x72e193edU, 0xff0ef160U, 0xd4ec384bU, 0x4b97dcd4U,
+	0x48571fd7U, 0x44581cdbU, 0xef1af570U, 0x23e5c6bcU,
+	0x2c2a06b3U, 0x786b13e7U, 0x822daf1dU, 0xc738ff58U,
+	0x0a878d95U, 0x46d89ed9U, 0xd5ac794aU, 0xc4f83c5bU,
+	0x282f07b7U, 0xb091212fU, 0x9f76e900U, 0x3d7e43a2U,
+	0x91f9680eU, 0x792b52e6U, 0xd1a9784eU, 0x90b9290fU,
+	0x647014fbU, 0x88a72f17U, 0xae0aa431U, 0x63b5d6fcU,
+	0x7d2e53e2U, 0x613554feU, 0x18130b87U, 0x05484d9aU,
+	0xed9a7772U, 0x6d3a57f2U, 0x08070f97U, 0xf704f368U,
+	0xba1ba125U, 0xe99f7676U, 0xf0c1316fU, 0xb494202bU,
+	0x977ceb08U, 0xa600a639U, 0x752451eaU, 0xa085253fU,
+	0xd8e33b47U, 0x317140aeU, 0xe195747eU, 0x76e492e9U,
+	0x6bbfd4f4U, 0xfa4bb165U, 0xca77bd55U, 0x9379ea0cU,
+	0x42dd9fddU, 0x17dccb88U, 0xc33dfe5cU, 0xa205a73dU,
+	0xe8df3777U, 0xf644b269U, 0x09474e96U, 0x3ffec1a0U,
+	0x67b0d7f8U, 0x36b482a9U, 0x22a587bdU, 0xd269bb4dU,
+	0x0e828c91U, 0x6c7a16f3U, 0xbdde6322U, 0x6fbad5f0U,
+	0x6aff95f5U, 0x5389daccU, 0x9cb62a03U, 0x7babd0e4U,
+	0x8768ef18U, 0x397b42a6U, 0x81ed6c1eU, 0xbc9e2223U,
+	0x5f86d9c0U, 0xa740e738U, 0x216544beU, 0x296f46b6U,
+	0x3ebe80a1U, 0xdb23f844U, 0x2feac5b0U, 0x66f096f9U,
+	0x9df66b02U, 0x32b183adU, 0x62f597fdU, 0x510958ceU,
+	0xe315f67cU, 0x706111efU, 0xc27dbf5dU, 0x687f17f7U,
+	0xea5fb575U, 0x4d125fd2U, 0x653055faU, 0x4798dfd8U,
+	0xdda67b42U, 0xa480243bU, 0xd0e9394fU, 0x544c18cbU,
+	0xbe1ea021U, 0x8628ae19U, 0x357441aaU, 0xfcce3263U,
+	0x9239ab0dU, 0x1e968881U, 0xd329fa4cU, 0x550c59caU,
+	0xe590757aU, 0x169c8a89U, 0x405d1ddfU, 0x202505bfU,
+	0x5c461ac3U, 0x58431bc7U, 0x84a82c1bU, 0xac8a2633U,
+	0xdf26f940U, 0x85e86d1aU, 0x578cdbc8U, 0xa345e63cU,
+	0x07c8cf98U, 0x9e36a801U, 0x1bd3c884U, 0x141c088bU,
+	0xadca6732U, 0x8e22ac11U, 0x98b32b07U, 0xecda3673U,
+	0x95fc690aU, 0xa9cf6636U, 0x80ad2d1fU, 0x8a27ad15U,
+	0xc1bd7c5eU, 0xee5ab471U, 0x2d6a47b2U, 0xcb37fc54U,
+	0x7faed1e0U, 0xde66b841U, 0x04080c9bU, 0x1fd6c980U,
+	0x5d065bc2U, 0x383b03a7U, 0xaa0fa535U, 0xa1c5643eU,
+	0x0bc7cc94U, 0x7eee90e1U, 0x3bfbc0a4U, 0x2befc4b4U,
+	0xcf32fd50U, 0x4c521ed3U, 0xb754e328U, 0xc0fd3d5fU,
+	0xd9a37a46U, 0xb351e22cU, 0x343400abU, 0x1019098fU,
+	0x7c6e12e3U, 0x0fc2cd90U, 0x2eaa84b1U, 0xcdb27f52U,
+	0xfe4eb061U, 0x504919cfU, 0xf241b36dU, 0xfb0bf064U,
+	0x89e76e16U, 0x2aaf85b5U, 0x1c160a83U, 0x5ac399c5U,
+	0x014d4c9eU, 0x9a33a905U, 0xdce63a43U, 0x693f56f6U,
+	0x0c020e93U, 0x52c99bcdU, 0x94bc280bU, 0x1159488eU,
+	0xb614a229U, 0xc678be59U, 0x0d424f92U, 0xc8f73f57U,
+	0xbabb018fU, 0xa3e54696U, 0x46783e73U, 0x1a33292fU,
+	0xd2c91be7U, 0x66503653U, 0x15fce920U, 0x3f5e610aU,
+	0xb97bc28cU, 0x3b5b600eU, 0xb171c084U, 0xeaff15dfU,
+	0x41bdfc74U, 0x0e222c3bU, 0x51a9f864U, 0xf524d1c0U,
+	0x4db2ff78U, 0x7cceb249U, 0x8bc74cbeU, 0x389ba30dU,
+	0x49b7fe7cU, 0x1b73682eU, 0xf121d0c4U, 0xeefa14dbU,
+	0x4b377c7eU, 0xf06191c5U, 0xad6ac798U, 0x798bf24cU,
+	0xc4589cf1U, 0xfbab50ceU, 0xf92bd2ccU, 0x87c84fb2U,
+	0x93d94aa6U, 0xe87f97ddU, 0xa0258595U, 0x32112307U,
+	0xa7e04792U, 0x5e66386bU, 0x83cd4eb6U, 0x08a7af3dU,
+	0x2dcae718U, 0xb0318185U, 0xcd12dff8U, 0xec7a96d9U,
+	0x566c3a63U, 0xdec618ebU, 0x53297a66U, 0x68dfb75dU,
+	0x72413347U, 0x2f4a651aU, 0x9159c8a4U, 0x47387f72U,
+	0xc6d81ef3U, 0xc11ddcf4U, 0x427d3f77U, 0xb7f44382U,
+	0xe3b556d6U, 0x9e9608abU, 0xb3f14286U, 0x0a272d3fU,
+	0x3494a001U, 0xdf8659eaU, 0x1df6eb28U, 0x7b0b704eU,
+	0xfc6e92c9U, 0xfd2ed3c8U, 0x7f0e714aU, 0x12392b27U,
+	0x2e0a241bU, 0x2c8aa619U, 0xbd7ec388U, 0x0b676c3eU,
+	0xefba55daU, 0x84088cb1U, 0x11f9e824U, 0x8548cdb0U,
+	0x7a4b314fU, 0xf7a453c2U, 0xdc469ae9U, 0x433d7e76U,
+	0x40fdbd75U, 0x4cf2be79U, 0xe7b057d2U, 0x2b4f641eU,
+	0x2480a411U, 0x70c1b145U, 0x8a870dbfU, 0xcf925dfaU,
+	0x022d2f37U, 0x4e723c7bU, 0xdd06dbe8U, 0xcc529ef9U,
+	0x2085a515U, 0xb83b838dU, 0x97dc4ba2U, 0x35d4e100U,
+	0x9953caacU, 0x7181f044U, 0xd903daecU, 0x98138badU,
+	0x6cdab659U, 0x800d8db5U, 0xa6a00693U, 0x6b1f745eU,
+	0x7584f140U, 0x699ff65cU, 0x10b9a925U, 0x0de2ef38U,
+	0xe530d5d0U, 0x6590f550U, 0x00adad35U, 0xffae51caU,
+	0xb2b10387U, 0xe135d4d4U, 0xf86b93cdU, 0xbc3e8289U,
+	0x9fd649aaU, 0xaeaa049bU, 0x7d8ef348U, 0xa82f879dU,
+	0xd04999e5U, 0x39dbe20cU, 0xe93fd6dcU, 0x7e4e304bU,
+	0x63157656U, 0xf2e113c7U, 0xc2dd1ff7U, 0x9bd348aeU,
+	0x4a773d7fU, 0x1f76692aU, 0xcb975cfeU, 0xaaaf059fU,
+	0xe07595d5U, 0xfeee10cbU, 0x01edec34U, 0x37546302U,
+	0x6f1a755aU, 0x3e1e200bU, 0x2a0f251fU, 0xdac319efU,
+	0x06282e33U, 0x64d0b451U, 0xb574c180U, 0x67107752U,
+	0x62553757U, 0x5b23786eU, 0x941c88a1U, 0x73017246U,
+	0x8fc24dbaU, 0x31d1e004U, 0x8947cebcU, 0xb4348081U,
+	0x572c7b62U, 0xafea459aU, 0x29cfe61cU, 0x21c5e414U,
+	0x36142203U, 0xd3895ae6U, 0x27406712U, 0x6e5a345bU,
+	0x955cc9a0U, 0x3a1b210fU, 0x6a5f355fU, 0x59a3fa6cU,
+	0xebbf54deU, 0x78cbb34dU, 0xcad71dffU, 0x60d5b555U,
+	0xe2f517d7U, 0x45b8fd70U, 0x6d9af758U, 0x4f327d7aU,
+	0xd50cd9e0U, 0xac2a8699U, 0xd8439bedU, 0x5ce6ba69U,
+	0xb6b40283U, 0x8e820cbbU, 0x3ddee308U, 0xf46490c1U,
+	0x9a9309afU, 0x163c2a23U, 0xdb8358eeU, 0x5da6fb68U,
+	0xed3ad7d8U, 0x1e36282bU, 0x48f7bf7dU, 0x288fa71dU,
+	0x54ecb861U, 0x50e9b965U, 0x8c028eb9U, 0xa4208491U,
+	0xd78c5be2U, 0x8d42cfb8U, 0x5f26796aU, 0xabef449eU,
+	0x0f626d3aU, 0x969c0aa3U, 0x13796a26U, 0x1cb6aa29U,
+	0xa560c590U, 0x86880eb3U, 0x901989a5U, 0xe47094d1U,
+	0x9d56cba8U, 0xa165c494U, 0x88078fbdU, 0x828d0fb7U,
+	0xc917defcU, 0xe6f016d3U, 0x25c0e510U, 0xc39d5ef6U,
+	0x77047342U, 0xd6cc1ae3U, 0x0ca2ae39U, 0x177c6b22U,
+	0x55acf960U, 0x3091a105U, 0xa2a50797U, 0xa96fc69cU,
+	0x036d6e36U, 0x76443243U, 0x33516206U, 0x23456616U,
+	0xc7985ff2U, 0x44f8bc71U, 0xbffe418aU, 0xc8579ffdU,
+	0xd109d8e4U, 0xbbfb408eU, 0x3c9ea209U, 0x18b3ab2dU,
+	0x74c4b041U, 0x07686f32U, 0x26002613U, 0xc518ddf0U,
+	0xf6e412c3U, 0x58e3bb6dU, 0xfaeb11cfU, 0xf3a152c6U,
+	0x814dccb4U, 0x22052717U, 0x14bca821U, 0x52693b67U,
+	0x09e7ee3cU, 0x92990ba7U, 0xd44c98e1U, 0x6195f454U,
+	0x04a8ac31U, 0x5a63396fU, 0x9c168aa9U, 0x19f3ea2cU,
+	0xbebe008bU, 0xced21cfbU, 0x05e8ed30U, 0xc05d9df5U,
+	0x3d90ad23U, 0x24ceea3aU, 0xc15392dfU, 0x9d188583U,
+	0x55e2b74bU, 0xe17b9affU, 0x92d7458cU, 0xb875cda6U,
+	0x3e506e20U, 0xbc70cca2U, 0x365a6c28U, 0x6dd4b973U,
+	0xc69650d8U, 0x89098097U, 0xd68254c8U, 0x720f7d6cU,
+	0xca9953d4U, 0xfbe51ee5U, 0x0cece012U, 0xbfb00fa1U,
+	0xce9c52d0U, 0x9c58c482U, 0x760a7c68U, 0x69d1b877U,
+	0xcc1cd0d2U, 0x774a3d69U, 0x2a416b34U, 0xfea05ee0U,
+	0x4373305dU, 0x7c80fc62U, 0x7e007e60U, 0x00e3e31eU,
+	0x14f2e60aU, 0x6f543b71U, 0x270e2939U, 0xb53a8fabU,
+	0x20cbeb3eU, 0xd94d94c7U, 0x04e6e21aU, 0x8f8c0391U,
+	0xaae14bb4U, 0x371a2d29U, 0x4a397354U, 0x6b513a75U,
+	0xd14796cfU, 0x59edb447U, 0xd402d6caU, 0xeff41bf1U,
+	0xf56a9febU, 0xa861c9b6U, 0x16726408U, 0xc013d3deU,
+	0x41f3b25fU, 0x46367058U, 0xc55693dbU, 0x30dfef2eU,
+	0x649efa7aU, 0x19bda407U, 0x34daee2aU, 0x8d0c8193U,
+	0xb3bf0cadU, 0x58adf546U, 0x9add4784U, 0xfc20dce2U,
+	0x7b453e65U, 0x7a057f64U, 0xf825dde6U, 0x9512878bU,
+	0xa92188b7U, 0xaba10ab5U, 0x3a556f24U, 0x8c4cc092U,
+	0x6891f976U, 0x0323201dU, 0x96d24488U, 0x0263611cU,
+	0xfd609de3U, 0x708fff6eU, 0x5b6d3645U, 0xc416d2daU,
+	0xc7d611d9U, 0xcbd912d5U, 0x609bfb7eU, 0xac64c8b2U,
+	0xa3ab08bdU, 0xf7ea1de9U, 0x0daca113U, 0x48b9f156U,
+	0x8506839bU, 0xc95990d7U, 0x5a2d7744U, 0x4b793255U,
+	0xa7ae09b9U, 0x3f102f21U, 0x10f7e70eU, 0xb2ff4dacU,
+	0x1e786600U, 0xf6aa5ce8U, 0x5e287640U, 0x1f382701U,
+	0xebf11af5U, 0x07262119U, 0x218baa3fU, 0xec34d8f2U,
+	0xf2af5decU, 0xeeb45af0U, 0x97920589U, 0x8ac94394U,
+	0x621b797cU, 0xe2bb59fcU, 0x87860199U, 0x7885fd66U,
+	0x359aaf2bU, 0x661e7878U, 0x7f403f61U, 0x3b152e25U,
+	0x18fde506U, 0x2981a837U, 0xfaa55fe4U, 0x2f042b31U,
+	0x57623549U, 0xbef04ea0U, 0x6e147a70U, 0xf9659ce7U,
+	0xe43edafaU, 0x75cabf6bU, 0x45f6b35bU, 0x1cf8e402U,
+	0xcd5c91d3U, 0x985dc586U, 0x4cbcf052U, 0x2d84a933U,
+	0x675e3979U, 0x79c5bc67U, 0x86c64098U, 0xb07fcfaeU,
+	0xe831d9f6U, 0xb9358ca7U, 0xad2489b3U, 0x5de8b543U,
+	0x8103829fU, 0xe3fb18fdU, 0x325f6d2cU, 0xe03bdbfeU,
+	0xe57e9bfbU, 0xdc08d4c2U, 0x1337240dU, 0xf42adeeaU,
+	0x08e9e116U, 0xb6fa4ca8U, 0x0e6c6210U, 0x331f2c2dU,
+	0xd007d7ceU, 0x28c1e936U, 0xaee44ab0U, 0xa6ee48b8U,
+	0xb13f8eafU, 0x54a2f64aU, 0xa06bcbbeU, 0xe97198f7U,
+	0x1277650cU, 0xbd308da3U, 0xed7499f3U, 0xde8856c0U,
+	0x6c94f872U, 0xffe01fe1U, 0x4dfcb153U, 0xe7fe19f9U,
+	0x65debb7bU, 0xc29351dcU, 0xeab15bf4U, 0xc819d1d6U,
+	0x5227754cU, 0x2b012a35U, 0x5f683741U, 0xdbcd16c5U,
+	0x319fae2fU, 0x09a9a017U, 0xbaf54fa4U, 0x734f3c6dU,
+	0x1db8a503U, 0x9117868fU, 0x5ca8f442U, 0xda8d57c4U,
+	0x6a117b74U, 0x991d8487U, 0xcfdc13d1U, 0xafa40bb1U,
+	0xd3c714cdU, 0xd7c215c9U, 0x0b292215U, 0x230b283dU,
+	0x50a7f74eU, 0x0a696314U, 0xd80dd5c6U, 0x2cc4e832U,
+	0x8849c196U, 0x11b7a60fU, 0x9452c68aU, 0x9b9d0685U,
+	0x224b693cU, 0x01a3a21fU, 0x17322509U, 0x635b387dU,
+	0x1a7d6704U, 0x264e6838U, 0x0f2c2311U, 0x05a6a31bU,
+	0x4e3c7250U, 0x61dbba7fU, 0xa2eb49bcU, 0x44b6f25aU,
+	0xf02fdfeeU, 0x51e7b64fU, 0x8b890295U, 0x9057c78eU,
+	0xd28755ccU, 0xb7ba0da9U, 0x258eab3bU, 0x2e446a30U,
+	0x8446c29aU, 0xf16f9eefU, 0xb47aceaaU, 0xa46ecabaU,
+	0x40b3f35eU, 0xc3d310ddU, 0x38d5ed26U, 0x4f7c3351U,
+	0x56227448U, 0x3cd0ec22U, 0xbbb50ea5U, 0x9f980781U,
+	0xf3ef1cedU, 0x8043c39eU, 0xa12b8abfU, 0x4233715cU,
+	0x71cfbe6fU, 0xdfc817c1U, 0x7dc0bd63U, 0x748afe6aU,
+	0x06666018U, 0xa52e8bbbU, 0x9397048dU, 0xd54297cbU,
+	0x8ecc4290U, 0x15b2a70bU, 0x5367344dU, 0xe6be58f8U,
+	0x8383009dU, 0xdd4895c3U, 0x1b3d2605U, 0x9ed84680U,
+	0x3995ac27U, 0x49f9b057U, 0x82c3419cU, 0x47763159U,
+	0xefaa45cbU, 0xf6f402d2U, 0x13697a37U, 0x4f226d6bU,
+	0x87d85fa3U, 0x33417217U, 0x40edad64U, 0x6a4f254eU,
+	0xec6a86c8U, 0x6e4a244aU, 0xe46084c0U, 0xbfee519bU,
+	0x14acb830U, 0x5b33687fU, 0x04b8bc20U, 0xa0359584U,
+	0x18a3bb3cU, 0x29dff60dU, 0xded608faU, 0x6d8ae749U,
+	0x1ca6ba38U, 0x4e622c6aU, 0xa4309480U, 0xbbeb509fU,
+	0x1e26383aU, 0xa570d581U, 0xf87b83dcU, 0x2c9ab608U,
+	0x9149d8b5U, 0xaeba148aU, 0xac3a9688U, 0xd2d90bf6U,
+	0xc6c80ee2U, 0xbd6ed399U, 0xf534c1d1U, 0x67006743U,
+	0xf2f103d6U, 0x0b777c2fU, 0xd6dc0af2U, 0x5db6eb79U,
+	0x78dba35cU, 0xe520c5c1U, 0x98039bbcU, 0xb96bd29dU,
+	0x037d7e27U, 0x8bd75cafU, 0x06383e22U, 0x3dcef319U,
+	0x27507703U, 0x7a5b215eU, 0xc4488ce0U, 0x12293b36U,
+	0x93c95ab7U, 0x940c98b0U, 0x176c7b33U, 0xe2e507c6U,
+	0xb6a41292U, 0xcb874cefU, 0xe6e006c2U, 0x5f36697bU,
+	0x6185e445U, 0x8a971daeU, 0x48e7af6cU, 0x2e1a340aU,
+	0xa97fd68dU, 0xa83f978cU, 0x2a1f350eU, 0x47286f63U,
+	0x7b1b605fU, 0x799be25dU, 0xe86f87ccU, 0x5e76287aU,
+	0xbaab119eU, 0xd119c8f5U, 0x44e8ac60U, 0xd05989f4U,
+	0x2f5a750bU, 0xa2b51786U, 0x8957deadU, 0x162c3a32U,
+	0x15ecf931U, 0x19e3fa3dU, 0xb2a11396U, 0x7e5e205aU,
+	0x7191e055U, 0x25d0f501U, 0xdf9649fbU, 0x9a8319beU,
+	0x573c6b73U, 0x1b63783fU, 0x88179facU, 0x9943dabdU,
+	0x7594e151U, 0xed2ac7c9U, 0xc2cd0fe6U, 0x60c5a544U,
+	0xcc428ee8U, 0x2490b400U, 0x8c129ea8U, 0xcd02cfe9U,
+	0x39cbf21dU, 0xd51cc9f1U, 0xf3b142d7U, 0x3e0e301aU,
+	0x2095b504U, 0x3c8eb218U, 0x45a8ed61U, 0x58f3ab7cU,
+	0xb0219194U, 0x3081b114U, 0x55bce971U, 0xaabf158eU,
+	0xe7a047c3U, 0xb4249090U, 0xad7ad789U, 0xe92fc6cdU,
+	0xcac70deeU, 0xfbbb40dfU, 0x289fb70cU, 0xfd3ec3d9U,
+	0x8558dda1U, 0x6ccaa648U, 0xbc2e9298U, 0x2b5f740fU,
+	0x36043212U, 0xa7f05783U, 0x97cc5bb3U, 0xcec20ceaU,
+	0x1f66793bU, 0x4a672d6eU, 0x9e8618baU, 0xffbe41dbU,
+	0xb564d191U, 0xabff548fU, 0x54fca870U, 0x62452746U,
+	0x3a0b311eU, 0x6b0f644fU, 0x7f1e615bU, 0x8fd25dabU,
+	0x53396a77U, 0x31c1f015U, 0xe06585c4U, 0x32013316U,
+	0x37447313U, 0x0e323c2aU, 0xc10dcce5U, 0x26103602U,
+	0xdad309feU, 0x64c0a440U, 0xdc568af8U, 0xe125c4c5U,
+	0x023d3f26U, 0xfafb01deU, 0x7cdea258U, 0x74d4a050U,
+	0x63056647U, 0x86981ea2U, 0x72512356U, 0x3b4b701fU,
+	0xc04d8de4U, 0x6f0a654bU, 0x3f4e711bU, 0x0cb2be28U,
+	0xbeae109aU, 0x2ddaf709U, 0x9fc659bbU, 0x35c4f111U,
+	0xb7e45393U, 0x10a9b934U, 0x388bb31cU, 0x1a23393eU,
+	0x801d9da4U, 0xf93bc2ddU, 0x8d52dfa9U, 0x09f7fe2dU,
+	0xe3a546c7U, 0xdb9348ffU, 0x68cfa74cU, 0xa175d485U,
+	0xcf824debU, 0x432d6e67U, 0x8e921caaU, 0x08b7bf2cU,
+	0xb82b939cU, 0x4b276c6fU, 0x1de6fb39U, 0x7d9ee359U,
+	0x01fdfc25U, 0x05f8fd21U, 0xd913cafdU, 0xf131c0d5U,
+	0x829d1fa6U, 0xd8538bfcU, 0x0a373d2eU, 0xfefe00daU,
+	0x5a73297eU, 0xc38d4ee7U, 0x46682e62U, 0x49a7ee6dU,
+	0xf07181d4U, 0xd3994af7U, 0xc508cde1U, 0xb161d095U,
+	0xc8478fecU, 0xf47480d0U, 0xdd16cbf9U, 0xd79c4bf3U,
+	0x9c069ab8U, 0xb3e15297U, 0x70d1a154U, 0x968c1ab2U,
+	0x22153706U, 0x83dd5ea7U, 0x59b3ea7dU, 0x426d2f66U,
+	0x00bdbd24U, 0x6580e541U, 0xf7b443d3U, 0xfc7e82d8U,
+	0x567c2a72U, 0x23557607U, 0x66402642U, 0x76542252U,
+	0x92891bb6U, 0x11e9f835U, 0xeaef05ceU, 0x9d46dbb9U,
+	0x84189ca0U, 0xeeea04caU, 0x698fe64dU, 0x4da2ef69U,
+	0x21d5f405U, 0x52792b76U, 0x73116257U, 0x900999b4U,
+	0xa3f55687U, 0x0df2ff29U, 0xaffa558bU, 0xa6b01682U,
+	0xd45c88f0U, 0x77146353U, 0x41adec65U, 0x07787f23U,
+	0x5cf6aa78U, 0xc7884fe3U, 0x815ddca5U, 0x3484b010U,
+	0x51b9e875U, 0x0f727d2bU, 0xc907ceedU, 0x4ce2ae68U,
+	0xebaf44cfU, 0x9bc358bfU, 0x50f9a974U, 0x954cd9b1U,
+	0x6d80ed63U, 0x74deaa7aU, 0x9143d29fU, 0xcd08c5c3U,
+	0x05f2f70bU, 0xb16bdabfU, 0xc2c705ccU, 0xe8658de6U,
+	0x6e402e60U, 0xec608ce2U, 0x664a2c68U, 0x3dc4f933U,
+	0x96861098U, 0xd919c0d7U, 0x86921488U, 0x221f3d2cU,
+	0x9a891394U, 0xabf55ea5U, 0x5cfca052U, 0xefa04fe1U,
+	0x9e8c1290U, 0xcc4884c2U, 0x261a3c28U, 0x39c1f837U,
+	0x9c0c9092U, 0x275a7d29U, 0x7a512b74U, 0xaeb01ea0U,
+	0x1363701dU, 0x2c90bc22U, 0x2e103e20U, 0x50f3a35eU,
+	0x44e2a64aU, 0x3f447b31U, 0x771e6979U, 0xe52acfebU,
+	0x70dbab7eU, 0x895dd487U, 0x54f6a25aU, 0xdf9c43d1U,
+	0xfaf10bf4U, 0x670a6d69U, 0x1a293314U, 0x3b417a35U,
+	0x8157d68fU, 0x09fdf407U, 0x8412968aU, 0xbfe45bb1U,
+	0xa57adfabU, 0xf87189f6U, 0x46622448U, 0x9003939eU,
+	0x11e3f21fU, 0x16263018U, 0x9546d39bU, 0x60cfaf6eU,
+	0x348eba3aU, 0x49ade447U, 0x64caae6aU, 0xdd1cc1d3U,
+	0xe3af4cedU, 0x08bdb506U, 0xcacd07c4U, 0xac309ca2U,
+	0x2b557e25U, 0x2a153f24U, 0xa8359da6U, 0xc502c7cbU,
+	0xf931c8f7U, 0xfbb14af5U, 0x6a452f64U, 0xdc5c80d2U,
+	0x3881b936U, 0x5333605dU, 0xc6c204c8U, 0x5273215cU,
+	0xad70dda3U, 0x209fbf2eU, 0x0b7d7605U, 0x9406929aU,
+	0x97c65199U, 0x9bc95295U, 0x308bbb3eU, 0xfc7488f2U,
+	0xf3bb48fdU, 0xa7fa5da9U, 0x5dbce153U, 0x18a9b116U,
+	0xd516c3dbU, 0x9949d097U, 0x0a3d3704U, 0x1b697215U,
+	0xf7be49f9U, 0x6f006f61U, 0x40e7a74eU, 0xe2ef0decU,
+	0x4e682640U, 0xa6ba1ca8U, 0x0e383600U, 0x4f286741U,
+	0xbbe15ab5U, 0x57366159U, 0x719bea7fU, 0xbc2498b2U,
+	0xa2bf1dacU, 0xbea41ab0U, 0xc78245c9U, 0xdad903d4U,
+	0x320b393cU, 0xb2ab19bcU, 0xd79641d9U, 0x2895bd26U,
+	0x658aef6bU, 0x360e3838U, 0x2f507f21U, 0x6b056e65U,
+	0x48eda546U, 0x7991e877U, 0xaab51fa4U, 0x7f146b71U,
+	0x07727509U, 0xeee00ee0U, 0x3e043a30U, 0xa975dca7U,
+	0xb42e9abaU, 0x25daff2bU, 0x15e6f31bU, 0x4ce8a442U,
+	0x9d4cd193U, 0xc84d85c6U, 0x1cacb012U, 0x7d94e973U,
+	0x374e7939U, 0x29d5fc27U, 0xd6d600d8U, 0xe06f8feeU,
+	0xb82199b6U, 0xe925cce7U, 0xfd34c9f3U, 0x0df8f503U,
+	0xd113c2dfU, 0xb3eb58bdU, 0x624f2d6cU, 0xb02b9bbeU,
+	0xb56edbbbU, 0x8c189482U, 0x4327644dU, 0xa43a9eaaU,
+	0x58f9a156U, 0xe6ea0ce8U, 0x5e7c2250U, 0x630f6c6dU,
+	0x8017978eU, 0x78d1a976U, 0xfef40af0U, 0xf6fe08f8U,
+	0xe12fceefU, 0x04b2b60aU, 0xf07b8bfeU, 0xb961d8b7U,
+	0x4267254cU, 0xed20cde3U, 0xbd64d9b3U, 0x8e981680U,
+	0x3c84b832U, 0xaff05fa1U, 0x1decf113U, 0xb7ee59b9U,
+	0x35cefb3bU, 0x9283119cU, 0xbaa11bb4U, 0x98099196U,
+	0x0237350cU, 0x7b116a75U, 0x0f787701U, 0x8bdd5685U,
+	0x618fee6fU, 0x59b9e057U, 0xeae50fe4U, 0x235f7c2dU,
+	0x4da8e543U, 0xc107c6cfU, 0x0cb8b402U, 0x8a9d1784U,
+	0x3a013b34U, 0xc90dc4c7U, 0x9fcc5391U, 0xffb44bf1U,
+	0x83d7548dU, 0x87d25589U, 0x5b396255U, 0x731b687dU,
+	0x00b7b70eU, 0x5a792354U, 0x881d9586U, 0x7cd4a872U,
+	0xd85981d6U, 0x41a7e64fU, 0xc44286caU, 0xcb8d46c5U,
+	0x725b297cU, 0x51b3e25fU, 0x47226549U, 0x334b783dU,
+	0x4a6d2744U, 0x765e2878U, 0x5f3c6351U, 0x55b6e35bU,
+	0x1e2c3210U, 0x31cbfa3fU, 0xf2fb09fcU, 0x14a6b21aU,
+	0xa03f9faeU, 0x01f7f60fU, 0xdb9942d5U, 0xc04787ceU,
+	0x8297158cU, 0xe7aa4de9U, 0x759eeb7bU, 0x7e542a70U,
+	0xd45682daU, 0xa17fdeafU, 0xe46a8eeaU, 0xf47e8afaU,
+	0x10a3b31eU, 0x93c3509dU, 0x68c5ad66U, 0x1f6c7311U,
+	0x06323408U, 0x6cc0ac62U, 0xeba54ee5U, 0xcf8847c1U,
+	0xa3ff5cadU, 0xd05383deU, 0xf13bcaffU, 0x1223311cU,
+	0x21dffe2fU, 0x8fd85781U, 0x2dd0fd23U, 0x249abe2aU,
+	0x56762058U, 0xf53ecbfbU, 0xc38744cdU, 0x8552d78bU,
+	0xdedc02d0U, 0x45a2e74bU, 0x0377740dU, 0xb6ae18b8U,
+	0xd39340ddU, 0x8d58d583U, 0x4b2d6645U, 0xcec806c0U,
+	0x6985ec67U, 0x19e9f017U, 0xd2d301dcU, 0x17667119U,
+	0x7dd1ac22U, 0x648feb3bU, 0x811293deU, 0xdd598482U,
+	0x15a3b64aU, 0xa13a9bfeU, 0xd296448dU, 0xf834cca7U,
+	0x7e116f21U, 0xfc31cda3U, 0x761b6d29U, 0x2d95b872U,
+	0x86d751d9U, 0xc9488196U, 0x96c355c9U, 0x324e7c6dU,
+	0x8ad852d5U, 0xbba41fe4U, 0x4cade113U, 0xfff10ea0U,
+	0x8edd53d1U, 0xdc19c583U, 0x364b7d69U, 0x2990b976U,
+	0x8c5dd1d3U, 0x370b3c68U, 0x6a006a35U, 0xbee15fe1U,
+	0x0332315cU, 0x3cc1fd63U, 0x3e417f61U, 0x40a2e21fU,
+	0x54b3e70bU, 0x2f153a70U, 0x674f2838U, 0xf57b8eaaU,
+	0x608aea3fU, 0x990c95c6U, 0x44a7e31bU, 0xcfcd0290U,
+	0xeaa04ab5U, 0x775b2c28U, 0x0a787255U, 0x2b103b74U,
+	0x910697ceU, 0x19acb546U, 0x9443d7cbU, 0xafb51af0U,
+	0xb52b9eeaU, 0xe820c8b7U, 0x56336509U, 0x8052d2dfU,
+	0x01b2b35eU, 0x06777159U, 0x851792daU, 0x709eee2fU,
+	0x24dffb7bU, 0x59fca506U, 0x749bef2bU, 0xcd4d8092U,
+	0xf3fe0dacU, 0x18ecf447U, 0xda9c4685U, 0xbc61dde3U,
+	0x3b043f64U, 0x3a447e65U, 0xb864dce7U, 0xd553868aU,
+	0xe96089b6U, 0xebe00bb4U, 0x7a146e25U, 0xcc0dc193U,
+	0x28d0f877U, 0x4362211cU, 0xd6934589U, 0x4222601dU,
+	0xbd219ce2U, 0x30cefe6fU, 0x1b2c3744U, 0x8457d3dbU,
+	0x879710d8U, 0x8b9813d4U, 0x20dafa7fU, 0xec25c9b3U,
+	0xe3ea09bcU, 0xb7ab1ce8U, 0x4deda012U, 0x08f8f057U,
+	0xc547829aU, 0x891891d6U, 0x1a6c7645U, 0x0b383354U,
+	0xe7ef08b8U, 0x7f512e20U, 0x50b6e60fU, 0xf2be4cadU,
+	0x5e396701U, 0xb6eb5de9U, 0x1e697741U, 0x5f792600U,
+	0xabb01bf4U, 0x47672018U, 0x61caab3eU, 0xac75d9f3U,
+	0xb2ee5cedU, 0xaef55bf1U, 0xd7d30488U, 0xca884295U,
+	0x225a787dU, 0xa2fa58fdU, 0xc7c70098U, 0x38c4fc67U,
+	0x75dbae2aU, 0x265f7979U, 0x3f013e60U, 0x7b542f24U,
+	0x58bce407U, 0x69c0a936U, 0xbae45ee5U, 0x6f452a30U,
+	0x17233448U, 0xfeb14fa1U, 0x2e557b71U, 0xb9249de6U,
+	0xa47fdbfbU, 0x358bbe6aU, 0x05b7b25aU, 0x5cb9e503U,
+	0x8d1d90d2U, 0xd81cc487U, 0x0cfdf153U, 0x6dc5a832U,
+	0x271f3878U, 0x3984bd66U, 0xc6874199U, 0xf03eceafU,
+	0xa870d8f7U, 0xf9748da6U, 0xed6588b2U, 0x1da9b442U,
+	0xc142839eU, 0xa3ba19fcU, 0x721e6c2dU, 0xa07adaffU,
+	0xa53f9afaU, 0x9c49d5c3U, 0x5376250cU, 0xb46bdfebU,
+	0x48a8e017U, 0xf6bb4da9U, 0x4e2d6311U, 0x735e2d2cU,
+	0x9046d6cfU, 0x6880e837U, 0xeea54bb1U, 0xe6af49b9U,
+	0xf17e8faeU, 0x14e3f74bU, 0xe02acabfU, 0xa93099f6U,
+	0x5236640dU, 0xfd718ca2U, 0xad3598f2U, 0x9ec957c1U,
+	0x2cd5f973U, 0xbfa11ee0U, 0x0dbdb052U, 0xa7bf18f8U,
+	0x259fba7aU, 0x82d250ddU, 0xaaf05af5U, 0x8858d0d7U,
+	0x1266744dU, 0x6b402b34U, 0x1f293640U, 0x9b8c17c4U,
+	0x71deaf2eU, 0x49e8a116U, 0xfab44ea5U, 0x330e3d6cU,
+	0x5df9a402U, 0xd156878eU, 0x1ce9f543U, 0x9acc56c5U,
+	0x2a507a75U, 0xd95c8586U, 0x8f9d12d0U, 0xefe50ab0U,
+	0x938615ccU, 0x978314c8U, 0x4b682314U, 0x634a293cU,
+	0x10e6f64fU, 0x4a286215U, 0x984cd4c7U, 0x6c85e933U,
+	0xc808c097U, 0x51f6a70eU, 0xd413c78bU, 0xdbdc0784U,
+	0x620a683dU, 0x41e2a31eU, 0x57732408U, 0x231a397cU,
+	0x5a3c6605U, 0x660f6939U, 0x4f6d2210U, 0x45e7a21aU,
+	0x0e7d7351U, 0x219abb7eU, 0xe2aa48bdU, 0x04f7f35bU,
+	0xb06edeefU, 0x11a6b74eU, 0xcbc80394U, 0xd016c68fU,
+	0x92c654cdU, 0xf7fb0ca8U, 0x65cfaa3aU, 0x6e056b31U,
+	0xc407c39bU, 0xb12e9feeU, 0xf43bcfabU, 0xe42fcbbbU,
+	0x00f2f25fU, 0x839211dcU, 0x7894ec27U, 0x0f3d3250U,
+	0x16637549U, 0x7c91ed23U, 0xfbf40fa4U, 0xdfd90680U,
+	0xb3ae1decU, 0xc002c29fU, 0xe16a8bbeU, 0x0272705dU,
+	0x318ebf6eU, 0x9f8916c0U, 0x3d81bc62U, 0x34cbff6bU,
+	0x46276119U, 0xe56f8abaU, 0xd3d6058cU, 0x950396caU,
+	0xce8d4391U, 0x55f3a60aU, 0x1326354cU, 0xa6ff59f9U,
+	0xc3c2019cU, 0x9d0994c2U, 0x5b7c2704U, 0xde994781U,
+	0x79d4ad26U, 0x09b8b156U, 0xc282409dU, 0x07373058U,
+	0xb4ec58d6U, 0xadb21fcfU, 0x482f672aU, 0x14647076U,
+	0xdc9e42beU, 0x68076f0aU, 0x1babb079U, 0x31093853U,
+	0xb72c9bd5U, 0x350c3957U, 0xbf2699ddU, 0xe4a84c86U,
+	0x4feaa52dU, 0x00757562U, 0x5ffea13dU, 0xfb738899U,
+	0x43e5a621U, 0x7299eb10U, 0x859015e7U, 0x36ccfa54U,
+	0x47e0a725U, 0x15243177U, 0xff76899dU, 0xe0ad4d82U,
+	0x45602527U, 0xfe36c89cU, 0xa33d9ec1U, 0x77dcab15U,
+	0xca0fc5a8U, 0xf5fc0997U, 0xf77c8b95U, 0x899f16ebU,
+	0x9d8e13ffU, 0xe628ce84U, 0xae72dcccU, 0x3c467a5eU,
+	0xa9b71ecbU, 0x50316132U, 0x8d9a17efU, 0x06f0f664U,
+	0x239dbe41U, 0xbe66d8dcU, 0xc34586a1U, 0xe22dcf80U,
+	0x583b633aU, 0xd09141b2U, 0x5d7e233fU, 0x6688ee04U,
+	0x7c166a1eU, 0x211d3c43U, 0x9f0e91fdU, 0x496f262bU,
+	0xc88f47aaU, 0xcf4a85adU, 0x4c2a662eU, 0xb9a31adbU,
+	0xede20f8fU, 0x90c151f2U, 0xbda61bdfU, 0x04707466U,
+	0x3ac3f958U, 0xd1d100b3U, 0x13a1b271U, 0x755c2917U,
+	0xf239cb90U, 0xf3798a91U, 0x71592813U, 0x1c6e727eU,
+	0x205d7d42U, 0x22ddff40U, 0xb3299ad1U, 0x05303567U,
+	0xe1ed0c83U, 0x8a5fd5e8U, 0x1faeb17dU, 0x8b1f94e9U,
+	0x741c6816U, 0xf9f30a9bU, 0xd211c3b0U, 0x4d6a272fU,
+	0x4eaae42cU, 0x42a5e720U, 0xe9e70e8bU, 0x25183d47U,
+	0x2ad7fd48U, 0x7e96e81cU, 0x84d054e6U, 0xc1c504a3U,
+	0x0c7a766eU, 0x40256522U, 0xd35182b1U, 0xc205c7a0U,
+	0x2ed2fc4cU, 0xb66cdad4U, 0x998b12fbU, 0x3b83b859U,
+	0x970493f5U, 0x7fd6a91dU, 0xd75483b5U, 0x9644d2f4U,
+	0x628def00U, 0x8e5ad4ecU, 0xa8f75fcaU, 0x65482d07U,
+	0x7bd3a819U, 0x67c8af05U, 0x1eeef07cU, 0x03b5b661U,
+	0xeb678c89U, 0x6bc7ac09U, 0x0efaf46cU, 0xf1f90893U,
+	0xbce65adeU, 0xef628d8dU, 0xf63cca94U, 0xb269dbd0U,
+	0x918110f3U, 0xa0fd5dc2U, 0x73d9aa11U, 0xa678dec4U,
+	0xde1ec0bcU, 0x378cbb55U, 0xe7688f85U, 0x70196912U,
+	0x6d422f0fU, 0xfcb64a9eU, 0xcc8a46aeU, 0x958411f7U,
+	0x44206426U, 0x11213073U, 0xc5c005a7U, 0xa4f85cc6U,
+	0xee22cc8cU, 0xf0b94992U, 0x0fbab56dU, 0x39033a5bU,
+	0x614d2c03U, 0x30497952U, 0x24587c46U, 0xd49440b6U,
+	0x087f776aU, 0x6a87ed08U, 0xbb2398d9U, 0x69472e0bU,
+	0x6c026e0eU, 0x55742137U, 0x9a4bd1f8U, 0x7d562b1fU,
+	0x819514e3U, 0x3f86b95dU, 0x871097e5U, 0xba63d9d8U,
+	0x597b223bU, 0xa1bd1cc3U, 0x2798bf45U, 0x2f92bd4dU,
+	0x38437b5aU, 0xddde03bfU, 0x29173e4bU, 0x600d6d02U,
+	0x9b0b90f9U, 0x344c7856U, 0x64086c06U, 0x57f4a335U,
+	0xe5e80d87U, 0x769cea14U, 0xc48044a6U, 0x6e82ec0cU,
+	0xeca24e8eU, 0x4befa429U, 0x63cdae01U, 0x41652423U,
+	0xdb5b80b9U, 0xa27ddfc0U, 0xd614c2b4U, 0x52b1e330U,
+	0xb8e35bdaU, 0x80d555e2U, 0x3389ba51U, 0xfa33c998U,
+	0x94c450f6U, 0x186b737aU, 0xd5d401b7U, 0x53f1a231U,
+	0xe36d8e81U, 0x10617172U, 0x46a0e624U, 0x26d8fe44U,
+	0x5abbe138U, 0x5ebee03cU, 0x8255d7e0U, 0xaa77ddc8U,
+	0xd9db02bbU, 0x831596e1U, 0x51712033U, 0xa5b81dc7U,
+	0x01353463U, 0x98cb53faU, 0x1d2e337fU, 0x12e1f370U,
+	0xab379cc9U, 0x88df57eaU, 0x9e4ed0fcU, 0xea27cd88U,
+	0x930192f1U, 0xaf329dcdU, 0x8650d6e4U, 0x8cda56eeU,
+	0xc74087a5U, 0xe8a74f8aU, 0x2b97bc49U, 0xcdca07afU,
+	0x79532a1bU, 0xd89b43baU, 0x02f5f760U, 0x192b327bU,
+	0x5bfba039U, 0x3ec6f85cU, 0xacf25eceU, 0xa7389fc5U,
+	0x0d3a376fU, 0x78136b1aU, 0x3d063b5fU, 0x2d123f4fU,
+	0xc9cf06abU, 0x4aafe528U, 0xb1a918d3U, 0xc600c6a4U,
+	0xdf5e81bdU, 0xb5ac19d7U, 0x32c9fb50U, 0x16e4f274U,
+	0x7a93e918U, 0x093f366bU, 0x28577f4aU, 0xcb4f84a9U,
+	0xf8b34b9aU, 0x56b4e234U, 0xf4bc4896U, 0xfdf60b9fU,
+	0x8f1a95edU, 0x2c527e4eU, 0x1aebf178U, 0x5c3e623eU,
+	0x07b0b765U, 0x9cce52feU, 0xda1bc1b8U, 0x6fc2ad0dU,
+	0x0afff568U, 0x54346036U, 0x9241d3f0U, 0x17a4b375U,
+	0xb0e959d2U, 0xc08545a2U, 0x0bbfb469U, 0xce0ac4acU,
+	0x63d7b43aU, 0x7a89f323U, 0x9f148bc6U, 0xc35f9c9aU,
+	0x0ba5ae52U, 0xbf3c83e6U, 0xcc905c95U, 0xe632d4bfU,
+	0x60177739U, 0xe237d5bbU, 0x681d7531U, 0x3393a06aU,
+	0x98d149c1U, 0xd74e998eU, 0x88c54dd1U, 0x2c486475U,
+	0x94de4acdU, 0xa5a207fcU, 0x52abf90bU, 0xe1f716b8U,
+	0x90db4bc9U, 0xc21fdd9bU, 0x284d6571U, 0x3796a16eU,
+	0x925bc9cbU, 0x290d2470U, 0x7406722dU, 0xa0e747f9U,
+	0x1d342944U, 0x22c7e57bU, 0x20476779U, 0x5ea4fa07U,
+	0x4ab5ff13U, 0x31132268U, 0x79493020U, 0xeb7d96b2U,
+	0x7e8cf227U, 0x870a8ddeU, 0x5aa1fb03U, 0xd1cb1a88U,
+	0xf4a652adU, 0x695d3430U, 0x147e6a4dU, 0x3516236cU,
+	0x8f008fd6U, 0x07aaad5eU, 0x8a45cfd3U, 0xb1b302e8U,
+	0xab2d86f2U, 0xf626d0afU, 0x48357d11U, 0x9e54cac7U,
+	0x1fb4ab46U, 0x18716941U, 0x9b118ac2U, 0x6e98f637U,
+	0x3ad9e363U, 0x47fabd1eU, 0x6a9df733U, 0xd34b988aU,
+	0xedf815b4U, 0x06eaec5fU, 0xc49a5e9dU, 0xa267c5fbU,
+	0x2502277cU, 0x2442667dU, 0xa662c4ffU, 0xcb559e92U,
+	0xf76691aeU, 0xf5e613acU, 0x6412763dU, 0xd20bd98bU,
+	0x36d6e06fU, 0x5d643904U, 0xc8955d91U, 0x5c247805U,
+	0xa32784faU, 0x2ec8e677U, 0x052a2f5cU, 0x9a51cbc3U,
+	0x999108c0U, 0x959e0bccU, 0x3edce267U, 0xf223d1abU,
+	0xfdec11a4U, 0xa9ad04f0U, 0x53ebb80aU, 0x16fee84fU,
+	0xdb419a82U, 0x971e89ceU, 0x046a6e5dU, 0x153e2b4cU,
+	0xf9e910a0U, 0x61573638U, 0x4eb0fe17U, 0xecb854b5U,
+	0x403f7f19U, 0xa8ed45f1U, 0x006f6f59U, 0x417f3e18U,
+	0xb5b603ecU, 0x59613800U, 0x7fccb326U, 0xb273c1ebU,
+	0xace844f5U, 0xb0f343e9U, 0xc9d51c90U, 0xd48e5a8dU,
+	0x3c5c6065U, 0xbcfc40e5U, 0xd9c11880U, 0x26c2e47fU,
+	0x6bddb632U, 0x38596161U, 0x21072678U, 0x6552373cU,
+	0x46bafc1fU, 0x77c6b12eU, 0xa4e246fdU, 0x71433228U,
+	0x09252c50U, 0xe0b757b9U, 0x30536369U, 0xa72285feU,
+	0xba79c3e3U, 0x2b8da672U, 0x1bb1aa42U, 0x42bffd1bU,
+	0x931b88caU, 0xc61adc9fU, 0x12fbe94bU, 0x73c3b02aU,
+	0x39192060U, 0x2782a57eU, 0xd8815981U, 0xee38d6b7U,
+	0xb676c0efU, 0xe77295beU, 0xf36390aaU, 0x03afac5aU,
+	0xdf449b86U, 0xbdbc01e4U, 0x6c187435U, 0xbe7cc2e7U,
+	0xbb3982e2U, 0x824fcddbU, 0x4d703d14U, 0xaa6dc7f3U,
+	0x56aef80fU, 0xe8bd55b1U, 0x502b7b09U, 0x6d583534U,
+	0x8e40ced7U, 0x7686f02fU, 0xf0a353a9U, 0xf8a951a1U,
+	0xef7897b6U, 0x0ae5ef53U, 0xfe2cd2a7U, 0xb73681eeU,
+	0x4c307c15U, 0xe37794baU, 0xb33380eaU, 0x80cf4fd9U,
+	0x32d3e16bU, 0xa1a706f8U, 0x13bba84aU, 0xb9b900e0U,
+	0x3b99a262U, 0x9cd448c5U, 0xb4f642edU, 0x965ec8cfU,
+	0x0c606c55U, 0x7546332cU, 0x012f2e58U, 0x858a0fdcU,
+	0x6fd8b736U, 0x57eeb90eU, 0xe4b256bdU, 0x2d082574U,
+	0x43ffbc1aU, 0xcf509f96U, 0x02efed5bU, 0x84ca4eddU,
+	0x3456626dU, 0xc75a9d9eU, 0x919b0ac8U, 0xf1e312a8U,
+	0x8d800dd4U, 0x89850cd0U, 0x556e3b0cU, 0x7d4c3124U,
+	0x0ee0ee57U, 0x542e7a0dU, 0x864accdfU, 0x7283f12bU,
+	0xd60ed88fU, 0x4ff0bf16U, 0xca15df93U, 0xc5da1f9cU,
+	0x7c0c7025U, 0x5fe4bb06U, 0x49753c10U, 0x3d1c2164U,
+	0x443a7e1dU, 0x78097121U, 0x516b3a08U, 0x5be1ba02U,
+	0x107b6b49U, 0x3f9ca366U, 0xfcac50a5U, 0x1af1eb43U,
+	0xae68c6f7U, 0x0fa0af56U, 0xd5ce1b8cU, 0xce10de97U,
+	0x8cc04cd5U, 0xe9fd14b0U, 0x7bc9b222U, 0x70037329U,
+	0xda01db83U, 0xaf2887f6U, 0xea3dd7b3U, 0xfa29d3a3U,
+	0x1ef4ea47U, 0x9d9409c4U, 0x6692f43fU, 0x113b2a48U,
+	0x08656d51U, 0x6297f53bU, 0xe5f217bcU, 0xc1df1e98U,
+	0xada805f4U, 0xde04da87U, 0xff6c93a6U, 0x1c746845U,
+	0x2f88a776U, 0x818f0ed8U, 0x2387a47aU, 0x2acde773U,
+	0x58217901U, 0xfb6992a2U, 0xcdd01d94U, 0x8b058ed2U,
+	0xd08b5b89U, 0x4bf5be12U, 0x0d202d54U, 0xb8f941e1U,
+	0xddc41984U, 0x830f8cdaU, 0x457a3f1cU, 0xc09f5f99U,
+	0x67d2b53eU, 0x17bea94eU, 0xdc845885U, 0x19312840U,
+	0xce5c921cU, 0xd702d505U, 0x329fade0U, 0x6ed4babcU,
+	0xa62e8874U, 0x12b7a5c0U, 0x611b7ab3U, 0x4bb9f299U,
+	0xcd9c511fU, 0x4fbcf39dU, 0xc5965317U, 0x9e18864cU,
+	0x355a6fe7U, 0x7ac5bfa8U, 0x254e6bf7U, 0x81c34253U,
+	0x39556cebU, 0x082921daU, 0xff20df2dU, 0x4c7c309eU,
+	0x3d506defU, 0x6f94fbbdU, 0x85c64357U, 0x9a1d8748U,
+	0x3fd0efedU, 0x84860256U, 0xd98d540bU, 0x0d6c61dfU,
+	0xb0bf0f62U, 0x8f4cc35dU, 0x8dcc415fU, 0xf32fdc21U,
+	0xe73ed935U, 0x9c98044eU, 0xd4c21606U, 0x46f6b094U,
+	0xd307d401U, 0x2a81abf8U, 0xf72add25U, 0x7c403caeU,
+	0x592d748bU, 0xc4d61216U, 0xb9f54c6bU, 0x989d054aU,
+	0x228ba9f0U, 0xaa218b78U, 0x27cee9f5U, 0x1c3824ceU,
+	0x06a6a0d4U, 0x5badf689U, 0xe5be5b37U, 0x33dfece1U,
+	0xb23f8d60U, 0xb5fa4f67U, 0x369aace4U, 0xc313d011U,
+	0x9752c545U, 0xea719b38U, 0xc716d115U, 0x7ec0beacU,
+	0x40733392U, 0xab61ca79U, 0x691178bbU, 0x0fece3ddU,
+	0x8889015aU, 0x89c9405bU, 0x0be9e2d9U, 0x66deb8b4U,
+	0x5aedb788U, 0x586d358aU, 0xc999501bU, 0x7f80ffadU,
+	0x9b5dc649U, 0xf0ef1f22U, 0x651e7bb7U, 0xf1af5e23U,
+	0x0eaca2dcU, 0x8343c051U, 0xa8a1097aU, 0x37daede5U,
+	0x341a2ee6U, 0x38152deaU, 0x9357c441U, 0x5fa8f78dU,
+	0x50673782U, 0x042622d6U, 0xfe609e2cU, 0xbb75ce69U,
+	0x76cabca4U, 0x3a95afe8U, 0xa9e1487bU, 0xb8b50d6aU,
+	0x54623686U, 0xccdc101eU, 0xe33bd831U, 0x41337293U,
+	0xedb4593fU, 0x056663d7U, 0xade4497fU, 0xecf4183eU,
+	0x183d25caU, 0xf4ea1e26U, 0xd2479500U, 0x1ff8e7cdU,
+	0x016362d3U, 0x1d7865cfU, 0x645e3ab6U, 0x79057cabU,
+	0x91d74643U, 0x117766c3U, 0x744a3ea6U, 0x8b49c259U,
+	0xc6569014U, 0x95d24747U, 0x8c8c005eU, 0xc8d9111aU,
+	0xeb31da39U, 0xda4d9708U, 0x096960dbU, 0xdcc8140eU,
+	0xa4ae0a76U, 0x4d3c719fU, 0x9dd8454fU, 0x0aa9a3d8U,
+	0x17f2e5c5U, 0x86068054U, 0xb63a8c64U, 0xef34db3dU,
+	0x3e90aeecU, 0x6b91fab9U, 0xbf70cf6dU, 0xde48960cU,
+	0x94920646U, 0x8a098358U, 0x750a7fa7U, 0x43b3f091U,
+	0x1bfde6c9U, 0x4af9b398U, 0x5ee8b68cU, 0xae248a7cU,
+	0x72cfbda0U, 0x103727c2U, 0xc1935213U, 0x13f7e4c1U,
+	0x16b2a4c4U, 0x2fc4ebfdU, 0xe0fb1b32U, 0x07e6e1d5U,
+	0xfb25de29U, 0x45367397U, 0xfda05d2fU, 0xc0d31312U,
+	0x23cbe8f1U, 0xdb0dd609U, 0x5d28758fU, 0x55227787U,
+	0x42f3b190U, 0xa76ec975U, 0x53a7f481U, 0x1abda7c8U,
+	0xe1bb5a33U, 0x4efcb29cU, 0x1eb8a6ccU, 0x2d4469ffU,
+	0x9f58c74dU, 0x0c2c20deU, 0xbe308e6cU, 0x143226c6U,
+	0x96128444U, 0x315f6ee3U, 0x197d64cbU, 0x3bd5eee9U,
+	0xa1eb4a73U, 0xd8cd150aU, 0xaca4087eU, 0x280129faU,
+	0xc2539110U, 0xfa659f28U, 0x4939709bU, 0x80830352U,
+	0xee749a3cU, 0x62dbb9b0U, 0xaf64cb7dU, 0x294168fbU,
+	0x99dd444bU, 0x6ad1bbb8U, 0x3c102ceeU, 0x5c68348eU,
+	0x200b2bf2U, 0x240e2af6U, 0xf8e51d2aU, 0xd0c71702U,
+	0xa36bc871U, 0xf9a55c2bU, 0x2bc1eaf9U, 0xdf08d70dU,
+	0x7b85fea9U, 0xe27b9930U, 0x679ef9b5U, 0x685139baU,
+	0xd1875603U, 0xf26f9d20U, 0xe4fe1a36U, 0x90970742U,
+	0xe9b1583bU, 0xd5825707U, 0xfce01c2eU, 0xf66a9c24U,
+	0xbdf04d6fU, 0x92178540U, 0x51277683U, 0xb77acd65U,
+	0x03e3e0d1U, 0xa22b8970U, 0x78453daaU, 0x639bf8b1U,
+	0x214b6af3U, 0x44763296U, 0xd6429404U, 0xdd88550fU,
+	0x778afda5U, 0x02a3a1d0U, 0x47b6f195U, 0x57a2f585U,
+	0xb37fcc61U, 0x301f2fe2U, 0xcb19d219U, 0xbcb00c6eU,
+	0xa5ee4b77U, 0xcf1cd31dU, 0x4879319aU, 0x6c5438beU,
+	0x002323d2U, 0x738ffca1U, 0x52e7b580U, 0xb1ff4e63U,
+	0x82038150U, 0x2c0428feU, 0x8e0c825cU, 0x8746c155U,
+	0xf5aa5f27U, 0x56e2b484U, 0x605b3bb2U, 0x268ea8f4U,
+	0x7d007dafU, 0xe67e9834U, 0xa0ab0b72U, 0x157267c7U,
+	0x704f3fa2U, 0x2e84aafcU, 0xe8f1193aU, 0x6d1479bfU,
+	0xca599318U, 0xba358f68U, 0x710f7ea3U, 0xb4ba0e66U,
+	0x712e5fd1U, 0x687018c8U, 0x8ded602dU, 0xd1a67771U,
+	0x195c45b9U, 0xadc5680dU, 0xde69b77eU, 0xf4cb3f54U,
+	0x72ee9cd2U, 0xf0ce3e50U, 0x7ae49edaU, 0x216a4b81U,
+	0x8a28a22aU, 0xc5b77265U, 0x9a3ca63aU, 0x3eb18f9eU,
+	0x8627a126U, 0xb75bec17U, 0x405212e0U, 0xf30efd53U,
+	0x8222a022U, 0xd0e63670U, 0x3ab48e9aU, 0x256f4a85U,
+	0x80a22220U, 0x3bf4cf9bU, 0x66ff99c6U, 0xb21eac12U,
+	0x0fcdc2afU, 0x303e0e90U, 0x32be8c92U, 0x4c5d11ecU,
+	0x584c14f8U, 0x23eac983U, 0x6bb0dbcbU, 0xf9847d59U,
+	0x6c7519ccU, 0x95f36635U, 0x485810e8U, 0xc332f163U,
+	0xe65fb946U, 0x7ba4dfdbU, 0x068781a6U, 0x27efc887U,
+	0x9df9643dU, 0x155346b5U, 0x98bc2438U, 0xa34ae903U,
+	0xb9d46d19U, 0xe4df3b44U, 0x5acc96faU, 0x8cad212cU,
+	0x0d4d40adU, 0x0a8882aaU, 0x89e86129U, 0x7c611ddcU,
+	0x28200888U, 0x550356f5U, 0x78641cd8U, 0xc1b27361U,
+	0xff01fe5fU, 0x141307b4U, 0xd663b576U, 0xb09e2e10U,
+	0x37fbcc97U, 0x36bb8d96U, 0xb49b2f14U, 0xd9ac7579U,
+	0xe59f7a45U, 0xe71ff847U, 0x76eb9dd6U, 0xc0f23260U,
+	0x242f0b84U, 0x4f9dd2efU, 0xda6cb67aU, 0x4edd93eeU,
+	0xb1de6f11U, 0x3c310d9cU, 0x17d3c4b7U, 0x88a82028U,
+	0x8b68e32bU, 0x8767e027U, 0x2c25098cU, 0xe0da3a40U,
+	0xef15fa4fU, 0xbb54ef1bU, 0x411253e1U, 0x040703a4U,
+	0xc9b87169U, 0x85e76225U, 0x169385b6U, 0x07c7c0a7U,
+	0xeb10fb4bU, 0x73aeddd3U, 0x5c4915fcU, 0xfe41bf5eU,
+	0x52c694f2U, 0xba14ae1aU, 0x129684b2U, 0x5386d5f3U,
+	0xa74fe807U, 0x4b98d3ebU, 0x6d3558cdU, 0xa08a2a00U,
+	0xbe11af1eU, 0xa20aa802U, 0xdb2cf77bU, 0xc677b166U,
+	0x2ea58b8eU, 0xae05ab0eU, 0xcb38f36bU, 0x343b0f94U,
+	0x79245dd9U, 0x2aa08a8aU, 0x33fecd93U, 0x77abdcd7U,
+	0x544317f4U, 0x653f5ac5U, 0xb61bad16U, 0x63bad9c3U,
+	0x1bdcc7bbU, 0xf24ebc52U, 0x22aa8882U, 0xb5db6e15U,
+	0xa8802808U, 0x39744d99U, 0x094841a9U, 0x504616f0U,
+	0x81e26321U, 0xd4e33774U, 0x000202a0U, 0x613a5bc1U,
+	0x2be0cb8bU, 0x357b4e95U, 0xca78b26aU, 0xfcc13d5cU,
+	0xa48f2b04U, 0xf58b7e55U, 0xe19a7b41U, 0x115647b1U,
+	0xcdbd706dU, 0xaf45ea0fU, 0x7ee19fdeU, 0xac85290cU,
+	0xa9c06909U, 0x90b62630U, 0x5f89d6ffU, 0xb8942c18U,
+	0x445713e4U, 0xfa44be5aU, 0x42d290e2U, 0x7fa1dedfU,
+	0x9cb9253cU, 0x647f1bc4U, 0xe25ab842U, 0xea50ba4aU,
+	0xfd817c5dU, 0x181c04b8U, 0xecd5394cU, 0xa5cf6a05U,
+	0x5ec997feU, 0xf18e7f51U, 0xa1ca6b01U, 0x9236a432U,
+	0x202a0a80U, 0xb35eed13U, 0x014243a1U, 0xab40eb0bU,
+	0x29604989U, 0x8e2da32eU, 0xa60fa906U, 0x84a72324U,
+	0x1e9987beU, 0x67bfd8c7U, 0x13d6c5b3U, 0x9773e437U,
+	0x7d215cddU, 0x451752e5U, 0xf64bbd56U, 0x3ff1ce9fU,
+	0x510657f1U, 0xdda9747dU, 0x101606b0U, 0x9633a536U,
+	0x26af8986U, 0xd5a37675U, 0x8362e123U, 0xe31af943U,
+	0x9f79e63fU, 0x9b7ce73bU, 0x4797d0e7U, 0x6fb5dacfU,
+	0x1c1905bcU, 0x46d791e6U, 0x94b32734U, 0x607a1ac0U,
+	0xc4f73364U, 0x5d0954fdU, 0xd8ec3478U, 0xd723f477U,
+	0x6ef59bceU, 0x4d1d50edU, 0x5b8cd7fbU, 0x2fe5ca8fU,
+	0x56c395f6U, 0x6af09acaU, 0x4392d1e3U, 0x491851e9U,
+	0x028280a2U, 0x2d65488dU, 0xee55bb4eU, 0x080800a8U,
+	0xbc912d1cU, 0x1d5944bdU, 0xc737f067U, 0xdce9357cU,
+	0x9e39a73eU, 0xfb04ff5bU, 0x693059c9U, 0x62fa98c2U,
+	0xc8f83068U, 0xbdd16c1dU, 0xf8c43c58U, 0xe8d03848U,
+	0x0c0d01acU, 0x8f6de22fU, 0x746b1fd4U, 0x03c2c1a3U,
+	0x1a9c86baU, 0x706e1ed0U, 0xf70bfc57U, 0xd326f573U,
+	0xbf51ee1fU, 0xccfd316cU, 0xed95784dU, 0x0e8d83aeU,
+	0x3d714c9dU, 0x9376e533U, 0x317e4f91U, 0x38340c98U,
+	0x4ad892eaU, 0xe9907949U, 0xdf29f67fU, 0x99fc6539U,
+	0xc272b062U, 0x590c55f9U, 0x1fd9c6bfU, 0xaa00aa0aU,
+	0xcf3df26fU, 0x91f66731U, 0x5783d4f7U, 0xd266b472U,
+	0x752b5ed5U, 0x054742a5U, 0xce7db36eU, 0x0bc8c3abU,
+	0xeaab41cfU, 0xf3f506d6U, 0x16687e33U, 0x4a23696fU,
+	0x82d95ba7U, 0x36407613U, 0x45eca960U, 0x6f4e214aU,
+	0xe96b82ccU, 0x6b4b204eU, 0xe16180c4U, 0xbaef559fU,
+	0x11adbc34U, 0x5e326c7bU, 0x01b9b824U, 0xa5349180U,
+	0x1da2bf38U, 0x2cdef209U, 0xdbd70cfeU, 0x688be34dU,
+	0x19a7be3cU, 0x4b63286eU, 0xa1319084U, 0xbeea549bU,
+	0x1b273c3eU, 0xa071d185U, 0xfd7a87d8U, 0x299bb20cU,
+	0x9448dcb1U, 0xabbb108eU, 0xa93b928cU, 0xd7d80ff2U,
+	0xc3c90ae6U, 0xb86fd79dU, 0xf035c5d5U, 0x62016347U,
+	0xf7f007d2U, 0x0e76782bU, 0xd3dd0ef6U, 0x58b7ef7dU,
+	0x7ddaa758U, 0xe021c1c5U, 0x9d029fb8U, 0xbc6ad699U,
+	0x067c7a23U, 0x8ed658abU, 0x03393a26U, 0x38cff71dU,
+	0x22517307U, 0x7f5a255aU, 0xc14988e4U, 0x17283f32U,
+	0x96c85eb3U, 0x910d9cb4U, 0x126d7f37U, 0xe7e403c2U,
+	0xb3a51696U, 0xce8648ebU, 0xe3e102c6U, 0x5a376d7fU,
+	0x6484e041U, 0x8f9619aaU, 0x4de6ab68U, 0x2b1b300eU,
+	0xac7ed289U, 0xad3e9388U, 0x2f1e310aU, 0x42296b67U,
+	0x7e1a645bU, 0x7c9ae659U, 0xed6e83c8U, 0x5b772c7eU,
+	0xbfaa159aU, 0xd418ccf1U, 0x41e9a864U, 0xd5588df0U,
+	0x2a5b710fU, 0xa7b41382U, 0x8c56daa9U, 0x132d3e36U,
+	0x10edfd35U, 0x1ce2fe39U, 0xb7a01792U, 0x7b5f245eU,
+	0x7490e451U, 0x20d1f105U, 0xda974dffU, 0x9f821dbaU,
+	0x523d6f77U, 0x1e627c3bU, 0x8d169ba8U, 0x9c42deb9U,
+	0x7095e555U, 0xe82bc3cdU, 0xc7cc0be2U, 0x65c4a140U,
+	0xc9438aecU, 0x2191b004U, 0x89139aacU, 0xc803cbedU,
+	0x3ccaf619U, 0xd01dcdf5U, 0xf6b046d3U, 0x3b0f341eU,
+	0x2594b100U, 0x398fb61cU, 0x40a9e965U, 0x5df2af78U,
+	0xb5209590U, 0x3580b510U, 0x50bded75U, 0xafbe118aU,
+	0xe2a143c7U, 0xb1259494U, 0xa87bd38dU, 0xec2ec2c9U,
+	0xcfc609eaU, 0xfeba44dbU, 0x2d9eb308U, 0xf83fc7ddU,
+	0x8059d9a5U, 0x69cba24cU, 0xb92f969cU, 0x2e5e700bU,
+	0x33053616U, 0xa2f15387U, 0x92cd5fb7U, 0xcbc308eeU,
+	0x1a677d3fU, 0x4f66296aU, 0x9b871cbeU, 0xfabf45dfU,
+	0xb065d595U, 0xaefe508bU, 0x51fdac74U, 0x67442342U,
+	0x3f0a351aU, 0x6e0e604bU, 0x7a1f655fU, 0x8ad359afU,
+	0x56386e73U, 0x34c0f411U, 0xe56481c0U, 0x37003712U,
+	0x32457717U, 0x0b33382eU, 0xc40cc8e1U, 0x23113206U,
+	0xdfd20dfaU, 0x61c1a044U, 0xd9578efcU, 0xe424c0c1U,
+	0x073c3b22U, 0xfffa05daU, 0x79dfa65cU, 0x71d5a454U,
+	0x66046243U, 0x83991aa6U, 0x77502752U, 0x3e4a741bU,
+	0xc54c89e0U, 0x6a0b614fU, 0x3a4f751fU, 0x09b3ba2cU,
+	0xbbaf149eU, 0x28dbf30dU, 0x9ac75dbfU, 0x30c5f515U,
+	0xb2e55797U, 0x15a8bd30U, 0x3d8ab718U, 0x1f223d3aU,
+	0x851c99a0U, 0xfc3ac6d9U, 0x8853dbadU, 0x0cf6fa29U,
+	0xe6a442c3U, 0xde924cfbU, 0x6dcea348U, 0xa474d081U,
+	0xca8349efU, 0x462c6a63U, 0x8b9318aeU, 0x0db6bb28U,
+	0xbd2a9798U, 0x4e26686bU, 0x18e7ff3dU, 0x789fe75dU,
+	0x04fcf821U, 0x00f9f925U, 0xdc12cef9U, 0xf430c4d1U,
+	0x879c1ba2U, 0xdd528ff8U, 0x0f36392aU, 0xfbff04deU,
+	0x5f722d7aU, 0xc68c4ae3U, 0x43692a66U, 0x4ca6ea69U,
+	0xf57085d0U, 0xd6984ef3U, 0xc009c9e5U, 0xb460d491U,
+	0xcd468be8U, 0xf17584d4U, 0xd817cffdU, 0xd29d4ff7U,
+	0x99079ebcU, 0xb6e05693U, 0x75d0a550U, 0x938d1eb6U,
+	0x27143302U, 0x86dc5aa3U, 0x5cb2ee79U, 0x476c2b62U,
+	0x05bcb920U, 0x6081e145U, 0xf2b547d7U, 0xf97f86dcU,
+	0x537d2e76U, 0x26547203U, 0x63412246U, 0x73552656U,
+	0x97881fb2U, 0x14e8fc31U, 0xefee01caU, 0x9847dfbdU,
+	0x811998a4U, 0xebeb00ceU, 0x6c8ee249U, 0x48a3eb6dU,
+	0x24d4f001U, 0x57782f72U, 0x76106653U, 0x95089db0U,
+	0xa6f45283U, 0x08f3fb2dU, 0xaafb518fU, 0xa3b11286U,
+	0xd15d8cf4U, 0x72156757U, 0x44ace861U, 0x02797b27U,
+	0x59f7ae7cU, 0xc2894be7U, 0x845cd8a1U, 0x3185b414U,
+	0x54b8ec71U, 0x0a73792fU, 0xcc06cae9U, 0x49e3aa6cU,
+	0xeeae40cbU, 0x9ec25cbbU, 0x55f8ad70U, 0x904dddb5U,
+	0xf1ac5dd3U, 0xe8f21acaU, 0x0d6f622fU, 0x51247573U,
+	0x99de47bbU, 0x2d476a0fU, 0x5eebb57cU, 0x74493d56U,
+	0xf26c9ed0U, 0x704c3c52U, 0xfa669cd8U, 0xa1e84983U,
+	0x0aaaa028U, 0x45357067U, 0x1abea438U, 0xbe338d9cU,
+	0x06a5a324U, 0x37d9ee15U, 0xc0d010e2U, 0x738cff51U,
+	0x02a0a220U, 0x50643472U, 0xba368c98U, 0xa5ed4887U,
+	0x00202022U, 0xbb76cd99U, 0xe67d9bc4U, 0x329cae10U,
+	0x8f4fc0adU, 0xb0bc0c92U, 0xb23c8e90U, 0xccdf13eeU,
+	0xd8ce16faU, 0xa368cb81U, 0xeb32d9c9U, 0x79067f5bU,
+	0xecf71bceU, 0x15716437U, 0xc8da12eaU, 0x43b0f361U,
+	0x66ddbb44U, 0xfb26ddd9U, 0x860583a4U, 0xa76dca85U,
+	0x1d7b663fU, 0x95d144b7U, 0x183e263aU, 0x23c8eb01U,
+	0x39566f1bU, 0x645d3946U, 0xda4e94f8U, 0x0c2f232eU,
+	0x8dcf42afU, 0x8a0a80a8U, 0x096a632bU, 0xfce31fdeU,
+	0xa8a20a8aU, 0xd58154f7U, 0xf8e61edaU, 0x41307163U,
+	0x7f83fc5dU, 0x949105b6U, 0x56e1b774U, 0x301c2c12U,
+	0xb779ce95U, 0xb6398f94U, 0x34192d16U, 0x592e777bU,
+	0x651d7847U, 0x679dfa45U, 0xf6699fd4U, 0x40703062U,
+	0xa4ad0986U, 0xcf1fd0edU, 0x5aeeb478U, 0xce5f91ecU,
+	0x315c6d13U, 0xbcb30f9eU, 0x9751c6b5U, 0x082a222aU,
+	0x0beae129U, 0x07e5e225U, 0xaca70b8eU, 0x60583842U,
+	0x6f97f84dU, 0x3bd6ed19U, 0xc19051e3U, 0x848501a6U,
+	0x493a736bU, 0x05656027U, 0x961187b4U, 0x8745c2a5U,
+	0x6b92f949U, 0xf32cdfd1U, 0xdccb17feU, 0x7ec3bd5cU,
+	0xd24496f0U, 0x3a96ac18U, 0x921486b0U, 0xd304d7f1U,
+	0x27cdea05U, 0xcb1ad1e9U, 0xedb75acfU, 0x20082802U,
+	0x3e93ad1cU, 0x2288aa00U, 0x5baef579U, 0x46f5b364U,
+	0xae27898cU, 0x2e87a90cU, 0x4bbaf169U, 0xb4b90d96U,
+	0xf9a65fdbU, 0xaa228888U, 0xb37ccf91U, 0xf729ded5U,
+	0xd4c115f6U, 0xe5bd58c7U, 0x3699af14U, 0xe338dbc1U,
+	0x9b5ec5b9U, 0x72ccbe50U, 0xa2288a80U, 0x35596c17U,
+	0x28022a0aU, 0xb9f64f9bU, 0x89ca43abU, 0xd0c414f2U,
+	0x01606123U, 0x54613576U, 0x808000a2U, 0xe1b859c3U,
+	0xab62c989U, 0xb5f94c97U, 0x4afab068U, 0x7c433f5eU,
+	0x240d2906U, 0x75097c57U, 0x61187943U, 0x91d445b3U,
+	0x4d3f726fU, 0x2fc7e80dU, 0xfe639ddcU, 0x2c072b0eU,
+	0x29426b0bU, 0x10342432U, 0xdf0bd4fdU, 0x38162e1aU,
+	0xc4d511e6U, 0x7ac6bc58U, 0xc25092e0U, 0xff23dcddU,
+	0x1c3b273eU, 0xe4fd19c6U, 0x62d8ba40U, 0x6ad2b848U,
+	0x7d037e5fU, 0x989e06baU, 0x6c573b4eU, 0x254d6807U,
+	0xde4b95fcU, 0x710c7d53U, 0x21486903U, 0x12b4a630U,
+	0xa0a80882U, 0x33dcef11U, 0x81c041a3U, 0x2bc2e909U,
+	0xa9e24b8bU, 0x0eafa12cU, 0x268dab04U, 0x04252126U,
+	0x9e1b85bcU, 0xe73ddac5U, 0x9354c7b1U, 0x17f1e635U,
+	0xfda35edfU, 0xc59550e7U, 0x76c9bf54U, 0xbf73cc9dU,
+	0xd18455f3U, 0x5d2b767fU, 0x909404b2U, 0x16b1a734U,
+	0xa62d8b84U, 0x55217477U, 0x03e0e321U, 0x6398fb41U,
+	0x1ffbe43dU, 0x1bfee539U, 0xc715d2e5U, 0xef37d8cdU,
+	0x9c9b07beU, 0xc65593e4U, 0x14312536U, 0xe0f818c2U,
+	0x44753166U, 0xdd8b56ffU, 0x586e367aU, 0x57a1f675U,
+	0xee7799ccU, 0xcd9f52efU, 0xdb0ed5f9U, 0xaf67c88dU,
+	0xd64197f4U, 0xea7298c8U, 0xc310d3e1U, 0xc99a53ebU,
+	0x820082a0U, 0xade74a8fU, 0x6ed7b94cU, 0x888a02aaU,
+	0x3c132f1eU, 0x9ddb46bfU, 0x47b5f265U, 0x5c6b377eU,
+	0x1ebba53cU, 0x7b86fd59U, 0xe9b25bcbU, 0xe2789ac0U,
+	0x487a326aU, 0x3d536e1fU, 0x78463e5aU, 0x68523a4aU,
+	0x8c8f03aeU, 0x0fefe02dU, 0xf4e91dd6U, 0x8340c3a1U,
+	0x9a1e84b8U, 0xf0ec1cd2U, 0x7789fe55U, 0x53a4f771U,
+	0x3fd3ec1dU, 0x4c7f336eU, 0x6d177a4fU, 0x8e0f81acU,
+	0xbdf34e9fU, 0x13f4e731U, 0xb1fc4d93U, 0xb8b60e9aU,
+	0xca5a90e8U, 0x69127b4bU, 0x5fabf47dU, 0x197e673bU,
+	0x42f0b260U, 0xd98e57fbU, 0x9f5bc4bdU, 0x2a82a808U,
+	0x4fbff06dU, 0x11746533U, 0xd701d6f5U, 0x52e4b670U,
+	0xf5a95cd7U, 0x85c540a7U, 0x4effb16cU, 0x8b4ac1a9U,
+	0xd7f324aaU, 0xcead63b3U, 0x2b301b56U, 0x777b0c0aU,
+	0xbf813ec2U, 0x0b181376U, 0x78b4cc05U, 0x5216442fU,
+	0xd433e7a9U, 0x5613452bU, 0xdc39e5a1U, 0x87b730faU,
+	0x2cf5d951U, 0x636a091eU, 0x3ce1dd41U, 0x986cf4e5U,
+	0x20fada5dU, 0x1186976cU, 0xe68f699bU, 0x55d38628U,
+	0x24ffdb59U, 0x763b4d0bU, 0x9c69f5e1U, 0x83b231feU,
+	0x267f595bU, 0x9d29b4e0U, 0xc022e2bdU, 0x14c3d769U,
+	0xa910b9d4U, 0x96e375ebU, 0x9463f7e9U, 0xea806a97U,
+	0xfe916f83U, 0x8537b2f8U, 0xcd6da0b0U, 0x5f590622U,
+	0xcaa862b7U, 0x332e1d4eU, 0xee856b93U, 0x65ef8a18U,
+	0x4082c23dU, 0xdd79a4a0U, 0xa05afaddU, 0x8132b3fcU,
+	0x3b241f46U, 0xb38e3dceU, 0x3e615f43U, 0x05979278U,
+	0x1f091662U, 0x4202403fU, 0xfc11ed81U, 0x2a705a57U,
+	0xab903bd6U, 0xac55f9d1U, 0x2f351a52U, 0xdabc66a7U,
+	0x8efd73f3U, 0xf3de2d8eU, 0xdeb967a3U, 0x676f081aU,
+	0x59dc8524U, 0xb2ce7ccfU, 0x70bece0dU, 0x1643556bU,
+	0x9126b7ecU, 0x9066f6edU, 0x1246546fU, 0x7f710e02U,
+	0x4342013eU, 0x41c2833cU, 0xd036e6adU, 0x662f491bU,
+	0x82f270ffU, 0xe940a994U, 0x7cb1cd01U, 0xe800e895U,
+	0x1703146aU, 0x9aec76e7U, 0xb10ebfccU, 0x2e755b53U,
+	0x2db59850U, 0x21ba9b5cU, 0x8af872f7U, 0x4607413bU,
+	0x49c88134U, 0x1d899460U, 0xe7cf289aU, 0xa2da78dfU,
+	0x6f650a12U, 0x233a195eU, 0xb04efecdU, 0xa11abbdcU,
+	0x4dcd8030U, 0xd573a6a8U, 0xfa946e87U, 0x589cc425U,
+	0xf41bef89U, 0x1cc9d561U, 0xb44bffc9U, 0xf55bae88U,
+	0x0192937cU, 0xed45a890U, 0xcbe823b6U, 0x0657517bU,
+	0x18ccd465U, 0x04d7d379U, 0x7df18c00U, 0x60aaca1dU,
+	0x8878f0f5U, 0x08d8d075U, 0x6de58810U, 0x92e674efU,
+	0xdff926a2U, 0x8c7df1f1U, 0x9523b6e8U, 0xd176a7acU,
+	0xf29e6c8fU, 0xc3e221beU, 0x10c6d66dU, 0xc567a2b8U,
+	0xbd01bcc0U, 0x5493c729U, 0x8477f3f9U, 0x1306156eU,
+	0x0e5d5373U, 0x9fa936e2U, 0xaf953ad2U, 0xf69b6d8bU,
+	0x273f185aU, 0x723e4c0fU, 0xa6df79dbU, 0xc7e720baU,
+	0x8d3db0f0U, 0x93a635eeU, 0x6ca5c911U, 0x5a1c4627U,
+	0x0252507fU, 0x5356052eU, 0x4747003aU, 0xb78b3ccaU,
+	0x6b600b16U, 0x09989174U, 0xd83ce4a5U, 0x0a585277U,
+	0x0f1d1272U, 0x366b5d4bU, 0xf954ad84U, 0x1e495763U,
+	0xe28a689fU, 0x5c99c521U, 0xe40feb99U, 0xd97ca5a4U,
+	0x3a645e47U, 0xc2a260bfU, 0x4487c339U, 0x4c8dc131U,
+	0x5b5c0726U, 0xbec17fc3U, 0x4a084237U, 0x0312117eU,
+	0xf814ec85U, 0x5753042aU, 0x0717107aU, 0x34ebdf49U,
+	0x86f771fbU, 0x15839668U, 0xa79f38daU, 0x0d9d9070U,
+	0x8fbd32f2U, 0x28f0d855U, 0x00d2d27dU, 0x227a585fU,
+	0xb844fcc5U, 0xc162a3bcU, 0xb50bbec8U, 0x31ae9f4cU,
+	0xdbfc27a6U, 0xe3ca299eU, 0x5096c62dU, 0x992cb5e4U,
+	0xf7db2c8aU, 0x7b740f06U, 0xb6cb7dcbU, 0x30eede4dU,
+	0x8072f2fdU, 0x737e0d0eU, 0x25bf9a58U, 0x45c78238U,
+	0x39a49d44U, 0x3da19c40U, 0xe14aab9cU, 0xc968a1b4U,
+	0xbac47ec7U, 0xe00aea9dU, 0x326e5c4fU, 0xc6a761bbU,
+	0x622a481fU, 0xfbd42f86U, 0x7e314f03U, 0x71fe8f0cU,
+	0xc828e0b5U, 0xebc02b96U, 0xfd51ac80U, 0x8938b1f4U,
+	0xf01eee8dU, 0xcc2de1b1U, 0xe54faa98U, 0xefc52a92U,
+	0xa45ffbd9U, 0x8bb833f6U, 0x4888c035U, 0xaed57bd3U,
+	0x1a4c5667U, 0xbb843fc6U, 0x61ea8b1cU, 0x7a344e07U,
+	0x38e4dc45U, 0x5dd98420U, 0xcfed22b2U, 0xc427e3b9U,
+	0x6e254b13U, 0x1b0c1766U, 0x5e194723U, 0x4e0d4333U,
+	0xaad07ad7U, 0x29b09954U, 0xd2b664afU, 0xa51fbad8U,
+	0xbc41fdc1U, 0xd6b365abU, 0x51d6872cU, 0x75fb8e08U,
+	0x198c9564U, 0x6a204a17U, 0x4b480336U, 0xa850f8d5U,
+	0x9bac37e6U, 0x35ab9e48U, 0x97a334eaU, 0x9ee977e3U,
+	0xec05e991U, 0x4f4d0232U, 0x79f48d04U, 0x3f211e42U,
+	0x64afcb19U, 0xffd12e82U, 0xb904bdc4U, 0x0cddd171U,
+	0x69e08914U, 0x372b1c4aU, 0xf15eaf8cU, 0x74bbcf09U,
+	0xd3f625aeU, 0xa39a39deU, 0x68a0c815U, 0xad15b8d0U,
+	0x8cb539b7U, 0x95eb7eaeU, 0x7076064bU, 0x2c3d1117U,
+	0xe4c723dfU, 0x505e0e6bU, 0x23f2d118U, 0x09505932U,
+	0x8f75fab4U, 0x0d555836U, 0x877ff8bcU, 0xdcf12de7U,
+	0x77b3c44cU, 0x382c1403U, 0x67a7c05cU, 0xc32ae9f8U,
+	0x7bbcc740U, 0x4ac08a71U, 0xbdc97486U, 0x0e959b35U,
+	0x7fb9c644U, 0x2d7d5016U, 0xc72fe8fcU, 0xd8f42ce3U,
+	0x7d394446U, 0xc66fa9fdU, 0x9b64ffa0U, 0x4f85ca74U,
+	0xf256a4c9U, 0xcda568f6U, 0xcf25eaf4U, 0xb1c6778aU,
+	0xa5d7729eU, 0xde71afe5U, 0x962bbdadU, 0x041f1b3fU,
+	0x91ee7faaU, 0x68680053U, 0xb5c3768eU, 0x3ea99705U,
+	0x1bc4df20U, 0x863fb9bdU, 0xfb1ce7c0U, 0xda74aee1U,
+	0x6062025bU, 0xe8c820d3U, 0x6527425eU, 0x5ed18f65U,
+	0x444f0b7fU, 0x19445d22U, 0xa757f09cU, 0x7136474aU,
+	0xf0d626cbU, 0xf713e4ccU, 0x7473074fU, 0x81fa7bbaU,
+	0xd5bb6eeeU, 0xa8983093U, 0x85ff7abeU, 0x3c291507U,
+	0x029a9839U, 0xe98861d2U, 0x2bf8d310U, 0x4d054876U,
+	0xca60aaf1U, 0xcb20ebf0U, 0x49004972U, 0x2437131fU,
+	0x18041c23U, 0x1a849e21U, 0x8b70fbb0U, 0x3d695406U,
+	0xd9b46de2U, 0xb206b489U, 0x27f7d01cU, 0xb346f588U,
+	0x4c450977U, 0xc1aa6bfaU, 0xea48a2d1U, 0x7533464eU,
+	0x76f3854dU, 0x7afc8641U, 0xd1be6feaU, 0x1d415c26U,
+	0x128e9c29U, 0x46cf897dU, 0xbc893587U, 0xf99c65c2U,
+	0x3423170fU, 0x787c0443U, 0xeb08e3d0U, 0xfa5ca6c1U,
+	0x168b9d2dU, 0x8e35bbb5U, 0xa1d2739aU, 0x03dad938U,
+	0xaf5df294U, 0x478fc87cU, 0xef0de2d4U, 0xae1db395U,
+	0x5ad48e61U, 0xb603b58dU, 0x90ae3eabU, 0x5d114c66U,
+	0x438ac978U, 0x5f91ce64U, 0x26b7911dU, 0x3becd700U,
+	0xd33eede8U, 0x539ecd68U, 0x36a3950dU, 0xc9a069f2U,
+	0x84bf3bbfU, 0xd73bececU, 0xce65abf5U, 0x8a30bab1U,
+	0xa9d87192U, 0x98a43ca3U, 0x4b80cb70U, 0x9e21bfa5U,
+	0xe647a1ddU, 0x0fd5da34U, 0xdf31eee4U, 0x48400873U,
+	0x551b4e6eU, 0xc4ef2bffU, 0xf4d327cfU, 0xaddd7096U,
+	0x7c790547U, 0x29785112U, 0xfd9964c6U, 0x9ca13da7U,
+	0xd67badedU, 0xc8e028f3U, 0x37e3d40cU, 0x015a5b3aU,
+	0x59144d62U, 0x08101833U, 0x1c011d27U, 0xeccd21d7U,
+	0x3026160bU, 0x52de8c69U, 0x837af9b8U, 0x511e4f6aU,
+	0x545b0f6fU, 0x6d2d4056U, 0xa212b099U, 0x450f4a7eU,
+	0xb9cc7582U, 0x07dfd83cU, 0xbf49f684U, 0x823ab8b9U,
+	0x6122435aU, 0x99e47da2U, 0x1fc1de24U, 0x17cbdc2cU,
+	0x001a1a3bU, 0xe58762deU, 0x114e5f2aU, 0x58540c63U,
+	0xa352f198U, 0x0c151937U, 0x5c510d67U, 0x6fadc254U,
+	0xddb16ce6U, 0x4ec58b75U, 0xfcd925c7U, 0x56db8d6dU,
+	0xd4fb2fefU, 0x73b6c548U, 0x5b94cf60U, 0x793c4542U,
+	0xe302e1d8U, 0x9a24bea1U, 0xee4da3d5U, 0x6ae88251U,
+	0x80ba3abbU, 0xb88c3483U, 0x0bd0db30U, 0xc26aa8f9U,
+	0xac9d3197U, 0x2032121bU, 0xed8d60d6U, 0x6ba8c350U,
+	0xdb34efe0U, 0x28381013U, 0x7ef98745U, 0x1e819f25U,
+	0x62e28059U, 0x66e7815dU, 0xba0cb681U, 0x922ebca9U,
+	0xe18263daU, 0xbb4cf780U, 0x69284152U, 0x9de17ca6U,
+	0x396c5502U, 0xa092329bU, 0x2577521eU, 0x2ab89211U,
+	0x936efda8U, 0xb086368bU, 0xa617b19dU, 0xd27eace9U,
+	0xab58f390U, 0x976bfcacU, 0xbe09b785U, 0xb483378fU,
+	0xff19e6c4U, 0xd0fe2eebU, 0x13cedd28U, 0xf59366ceU,
+	0x410a4b7aU, 0xe0c222dbU, 0x3aac9601U, 0x2172531aU,
+	0x63a2c158U, 0x069f993dU, 0x94ab3fafU, 0x9f61fea4U,
+	0x3563560eU, 0x404a0a7bU, 0x055f5a3eU, 0x154b5e2eU,
+	0xf19667caU, 0x72f68449U, 0x89f079b2U, 0xfe59a7c5U,
+	0xe707e0dcU, 0x8df578b6U, 0x0a909a31U, 0x2ebd9315U,
+	0x42ca8879U, 0x3166570aU, 0x100e1e2bU, 0xf316e5c8U,
+	0xc0ea2afbU, 0x6eed8355U, 0xcce529f7U, 0xc5af6afeU,
+	0xb743f48cU, 0x140b1f2fU, 0x22b29019U, 0x6467035fU,
+	0x3fe9d604U, 0xa497339fU, 0xe242a0d9U, 0x579bcc6cU,
+	0x32a69409U, 0x6c6d0157U, 0xaa18b291U, 0x2ffdd214U,
+	0x88b038b3U, 0xf8dc24c3U, 0x33e6d508U, 0xf653a5cdU,
+	0x5e8fd15fU, 0x47d19646U, 0xa24ceea3U, 0xfe07f9ffU,
+	0x36fdcb37U, 0x8264e683U, 0xf1c839f0U, 0xdb6ab1daU,
+	0x5d4f125cU, 0xdf6fb0deU, 0x55451054U, 0x0ecbc50fU,
+	0xa5892ca4U, 0xea16fcebU, 0xb59d28b4U, 0x11100110U,
+	0xa9862fa8U, 0x98fa6299U, 0x6ff39c6eU, 0xdcaf73ddU,
+	0xad832eacU, 0xff47b8feU, 0x15150014U, 0x0acec40bU,
+	0xaf03acaeU, 0x14554115U, 0x495e1748U, 0x9dbf229cU,
+	0x206c4c21U, 0x1f9f801eU, 0x1d1f021cU, 0x63fc9f62U,
+	0x77ed9a76U, 0x0c4b470dU, 0x44115545U, 0xd625f3d7U,
+	0x43d49742U, 0xba52e8bbU, 0x67f99e66U, 0xec937fedU,
+	0xc9fe37c8U, 0x54055155U, 0x29260f28U, 0x084e4609U,
+	0xb258eab3U, 0x3af2c83bU, 0xb71daab6U, 0x8ceb678dU,
+	0x9675e397U, 0xcb7eb5caU, 0x756d1874U, 0xa30cafa2U,
+	0x22ecce23U, 0x25290c24U, 0xa649efa7U, 0x53c09352U,
+	0x07818606U, 0x7aa2d87bU, 0x57c59256U, 0xee13fdefU,
+	0xd0a070d1U, 0x3bb2893aU, 0xf9c23bf8U, 0x9f3fa09eU,
+	0x185a4219U, 0x191a0318U, 0x9b3aa19aU, 0xf60dfbf7U,
+	0xca3ef4cbU, 0xc8be76c9U, 0x594a1358U, 0xef53bceeU,
+	0x0b8e850aU, 0x603c5c61U, 0xf5cd38f4U, 0x617c1d60U,
+	0x9e7fe19fU, 0x13908312U, 0x38724a39U, 0xa709aea6U,
+	0xa4c96da5U, 0xa8c66ea9U, 0x03848702U, 0xcf7bb4ceU,
+	0xc0b474c1U, 0x94f56195U, 0x6eb3dd6fU, 0x2ba68d2aU,
+	0xe619ffe7U, 0xaa46ecabU, 0x39320b38U, 0x28664e29U,
+	0xc4b175c5U, 0x5c0f535dU, 0x73e89b72U, 0xd1e031d0U,
+	0x7d671a7cU, 0x95b52094U, 0x3d370a3cU, 0x7c275b7dU,
+	0x88ee6689U, 0x64395d65U, 0x4294d643U, 0x8f2ba48eU,
+	0x91b02190U, 0x8dab268cU, 0xf48d79f5U, 0xe9d63fe8U,
+	0x01040500U, 0x81a42580U, 0xe4997de5U, 0x1b9a811aU,
+	0x5685d357U, 0x05010404U, 0x1c5f431dU, 0x580a5259U,
+	0x7be2997aU, 0x4a9ed44bU, 0x99ba2398U, 0x4c1b574dU,
+	0x347d4935U, 0xddef32dcU, 0x0d0b060cU, 0x9a7ae09bU,
+	0x8721a686U, 0x16d5c317U, 0x26e9cf27U, 0x7fe7987eU,
+	0xae43edafU, 0xfb42b9faU, 0x2fa38c2eU, 0x4e9bd54fU,
+	0x04414505U, 0x1adac01bU, 0xe5d93ce4U, 0xd360b3d2U,
+	0x8b2ea58aU, 0xda2af0dbU, 0xce3bf5cfU, 0x3ef7c93fU,
+	0xe21cfee3U, 0x80e46481U, 0x51401150U, 0x8324a782U,
+	0x8661e787U, 0xbf17a8beU, 0x70285871U, 0x9735a296U,
+	0x6bf69d6aU, 0xd5e530d4U, 0x6d731e6cU, 0x50005051U,
+	0xb318abb2U, 0x4bde954aU, 0xcdfb36ccU, 0xc5f134c4U,
+	0xd220f2d3U, 0x37bd8a36U, 0xc374b7c2U, 0x8a6ee48bU,
+	0x71681970U, 0xde2ff1dfU, 0x8e6be58fU, 0xbd972abcU,
+	0x0f8b840eU, 0x9cff639dU, 0x2ee3cd2fU, 0x84e16585U,
+	0x06c1c707U, 0xa18c2da0U, 0x89ae2788U, 0xab06adaaU,
+	0x31380930U, 0x481e5649U, 0x3c774b3dU, 0xb8d26ab9U,
+	0x5280d253U, 0x6ab6dc6bU, 0xd9ea33d8U, 0x10504011U,
+	0x7ea7d97fU, 0xf208faf3U, 0x3fb7883eU, 0xb9922bb8U,
+	0x090e0708U, 0xfa02f8fbU, 0xacc36fadU, 0xccbb77cdU,
+	0xb0d868b1U, 0xb4dd69b5U, 0x68365e69U, 0x40145441U,
+	0x33b88b32U, 0x69761f68U, 0xbb12a9baU, 0x4fdb944eU,
+	0xeb56bdeaU, 0x72a8da73U, 0xf74dbaf6U, 0xf8827af9U,
+	0x41541540U, 0x62bcde63U, 0x742d5975U, 0x00444401U,
+	0x79621b78U, 0x45511444U, 0x6c335f6dU, 0x66b9df67U,
+	0x2d230e2cU, 0x02c4c603U, 0xc1f435c0U, 0x27a98e26U,
+	0x9330a392U, 0x32f8ca33U, 0xe8967ee9U, 0xf348bbf2U,
+	0xb19829b0U, 0xd4a571d5U, 0x4691d747U, 0x4d5b164cU,
+	0xe759bee6U, 0x9270e293U, 0xd765b2d6U, 0xc771b6c6U,
+	0x23ac8f22U, 0xa0cc6ca1U, 0x5bca915aU, 0x2c634f2dU,
+	0x353d0834U, 0x5fcf905eU, 0xd8aa72d9U, 0xfc877bfdU,
+	0x90f06091U, 0xe35cbfe2U, 0xc234f6c3U, 0x212c0d20U,
+	0x12d0c213U, 0xbcd76bbdU, 0x1edfc11fU, 0x17958216U,
+	0x65791c64U, 0xc631f7c7U, 0xf08878f1U, 0xb65debb7U,
+	0xedd33eecU, 0x76addb77U, 0x30784831U, 0x85a12484U,
+	0xe09c7ce1U, 0xbe57e9bfU, 0x78225a79U, 0xfdc73afcU,
+	0x5a8ad05bU, 0x2ae6cc2bU, 0xe1dc3de0U, 0x24694d25U,
+	0xfeaf51dfU, 0xe7f116c6U, 0x026c6e23U, 0x5e27797fU,
+	0x96dd4bb7U, 0x22446603U, 0x51e8b970U, 0x7b4a315aU,
+	0xfd6f92dcU, 0x7f4f305eU, 0xf56590d4U, 0xaeeb458fU,
+	0x05a9ac24U, 0x4a367c6bU, 0x15bda834U, 0xb1308190U,
+	0x09a6af28U, 0x38dae219U, 0xcfd31ceeU, 0x7c8ff35dU,
+	0x0da3ae2cU, 0x5f67387eU, 0xb5358094U, 0xaaee448bU,
+	0x0f232c2eU, 0xb475c195U, 0xe97e97c8U, 0x3d9fa21cU,
+	0x804ccca1U, 0xbfbf009eU, 0xbd3f829cU, 0xc3dc1fe2U,
+	0xd7cd1af6U, 0xac6bc78dU, 0xe431d5c5U, 0x76057357U,
+	0xe3f417c2U, 0x1a72683bU, 0xc7d91ee6U, 0x4cb3ff6dU,
+	0x69deb748U, 0xf425d1d5U, 0x89068fa8U, 0xa86ec689U,
+	0x12786a33U, 0x9ad248bbU, 0x173d2a36U, 0x2ccbe70dU,
+	0x36556317U, 0x6b5e354aU, 0xd54d98f4U, 0x032c2f22U,
+	0x82cc4ea3U, 0x85098ca4U, 0x06696f27U, 0xf3e013d2U,
+	0xa7a10686U, 0xda8258fbU, 0xf7e512d6U, 0x4e337d6fU,
+	0x7080f051U, 0x9b9209baU, 0x59e2bb78U, 0x3f1f201eU,
+	0xb87ac299U, 0xb93a8398U, 0x3b1a211aU, 0x562d7b77U,
+	0x6a1e744bU, 0x689ef649U, 0xf96a93d8U, 0x4f733c6eU,
+	0xabae058aU, 0xc01cdce1U, 0x55edb874U, 0xc15c9de0U,
+	0x3e5f611fU, 0xb3b00392U, 0x9852cab9U, 0x07292e26U,
+	0x04e9ed25U, 0x08e6ee29U, 0xa3a40782U, 0x6f5b344eU,
+	0x6094f441U, 0x34d5e115U, 0xce935defU, 0x8b860daaU,
+	0x46397f67U, 0x0a666c2bU, 0x99128bb8U, 0x8846cea9U,
+	0x6491f545U, 0xfc2fd3ddU, 0xd3c81bf2U, 0x71c0b150U,
+	0xdd479afcU, 0x3595a014U, 0x9d178abcU, 0xdc07dbfdU,
+	0x28cee609U, 0xc419dde5U, 0xe2b456c3U, 0x2f0b240eU,
+	0x3190a110U, 0x2d8ba60cU, 0x54adf975U, 0x49f6bf68U,
+	0xa1248580U, 0x2184a500U, 0x44b9fd65U, 0xbbba019aU,
+	0xf6a553d7U, 0xa5218484U, 0xbc7fc39dU, 0xf82ad2d9U,
+	0xdbc219faU, 0xeabe54cbU, 0x399aa318U, 0xec3bd7cdU,
+	0x945dc9b5U, 0x7dcfb25cU, 0xad2b868cU, 0x3a5a601bU,
+	0x27012606U, 0xb6f54397U, 0x86c94fa7U, 0xdfc718feU,
+	0x0e636d2fU, 0x5b62397aU, 0x8f830caeU, 0xeebb55cfU,
+	0xa461c585U, 0xbafa409bU, 0x45f9bc64U, 0x73403352U,
+	0x2b0e250aU, 0x7a0a705bU, 0x6e1b754fU, 0x9ed749bfU,
+	0x423c7e63U, 0x20c4e401U, 0xf16091d0U, 0x23042702U,
+	0x26416707U, 0x1f37283eU, 0xd008d8f1U, 0x37152216U,
+	0xcbd61deaU, 0x75c5b054U, 0xcd539eecU, 0xf020d0d1U,
+	0x13382b32U, 0xebfe15caU, 0x6ddbb64cU, 0x65d1b444U,
+	0x72007253U, 0x979d0ab6U, 0x63543742U, 0x2a4e640bU,
+	0xd14899f0U, 0x7e0f715fU, 0x2e4b650fU, 0x1db7aa3cU,
+	0xafab048eU, 0x3cdfe31dU, 0x8ec34dafU, 0x24c1e505U,
+	0xa6e14787U, 0x01acad20U, 0x298ea708U, 0x0b262d2aU,
+	0x911889b0U, 0xe83ed6c9U, 0x9c57cbbdU, 0x18f2ea39U,
+	0xf2a052d3U, 0xca965cebU, 0x79cab358U, 0xb070c091U,
+	0xde8759ffU, 0x52287a73U, 0x9f9708beU, 0x19b2ab38U,
+	0xa92e8788U, 0x5a22787bU, 0x0ce3ef2dU, 0x6c9bf74dU,
+	0x10f8e831U, 0x14fde935U, 0xc816dee9U, 0xe034d4c1U,
+	0x93980bb2U, 0xc9569fe8U, 0x1b32293aU, 0xeffb14ceU,
+	0x4b763d6aU, 0xd2885af3U, 0x576d3a76U, 0x58a2fa79U,
+	0xe17495c0U, 0xc29c5ee3U, 0xd40dd9f5U, 0xa064c481U,
+	0xd9429bf8U, 0xe57194c4U, 0xcc13dfedU, 0xc6995fe7U,
+	0x8d038eacU, 0xa2e44683U, 0x61d4b540U, 0x87890ea6U,
+	0x33102312U, 0x92d84ab3U, 0x48b6fe69U, 0x53683b72U,
+	0x11b8a930U, 0x7485f155U, 0xe6b157c7U, 0xed7b96ccU,
+	0x47793e66U, 0x32506213U, 0x77453256U, 0x67513646U,
+	0x838c0fa2U, 0x00ecec21U, 0xfbea11daU, 0x8c43cfadU,
+	0x951d88b4U, 0xffef10deU, 0x788af259U, 0x5ca7fb7dU,
+	0x30d0e011U, 0x437c3f62U, 0x62147643U, 0x810c8da0U,
+	0xb2f04293U, 0x1cf7eb3dU, 0xbeff419fU, 0xb7b50296U,
+	0xc5599ce4U, 0x66117747U, 0x50a8f871U, 0x167d6b37U,
+	0x4df3be6cU, 0xd68d5bf7U, 0x9058c8b1U, 0x2581a404U,
+	0x40bcfc61U, 0x1e77693fU, 0xd802daf9U, 0x5de7ba7cU,
+	0xfaaa50dbU, 0x8ac64cabU, 0x41fcbd60U, 0x8449cda5U,
+	0xc3f734baU, 0xdaa973a3U, 0x3f340b46U, 0x637f1c1aU,
+	0xab852ed2U, 0x1f1c0366U, 0x6cb0dc15U, 0x4612543fU,
+	0xc037f7b9U, 0x4217553bU, 0xc83df5b1U, 0x93b320eaU,
+	0x38f1c941U, 0x776e190eU, 0x28e5cd51U, 0x8c68e4f5U,
+	0x34feca4dU, 0x0582877cU, 0xf28b798bU, 0x41d79638U,
+	0x30fbcb49U, 0x623f5d1bU, 0x886de5f1U, 0x97b621eeU,
+	0x327b494bU, 0x892da4f0U, 0xd426f2adU, 0x00c7c779U,
+	0xbd14a9c4U, 0x82e765fbU, 0x8067e7f9U, 0xfe847a87U,
+	0xea957f93U, 0x9133a2e8U, 0xd969b0a0U, 0x4b5d1632U,
+	0xdeac72a7U, 0x272a0d5eU, 0xfa817b83U, 0x71eb9a08U,
+	0x5486d22dU, 0xc97db4b0U, 0xb45eeacdU, 0x9536a3ecU,
+	0x2f200f56U, 0xa78a2ddeU, 0x2a654f53U, 0x11938268U,
+	0x0b0d0672U, 0x5606502fU, 0xe815fd91U, 0x3e744a47U,
+	0xbf942bc6U, 0xb851e9c1U, 0x3b310a42U, 0xceb876b7U,
+	0x9af963e3U, 0xe7da3d9eU, 0xcabd77b3U, 0x736b180aU,
+	0x4dd89534U, 0xa6ca6cdfU, 0x64bade1dU, 0x0247457bU,
+	0x8522a7fcU, 0x8462e6fdU, 0x0642447fU, 0x6b751e12U,
+	0x5746112eU, 0x55c6932cU, 0xc432f6bdU, 0x722b590bU,
+	0x96f660efU, 0xfd44b984U, 0x68b5dd11U, 0xfc04f885U,
+	0x0307047aU, 0x8ee866f7U, 0xa50aafdcU, 0x3a714b43U,
+	0x39b18840U, 0x35be8b4cU, 0x9efc62e7U, 0x5203512bU,
+	0x5dcc9124U, 0x098d8470U, 0xf3cb388aU, 0xb6de68cfU,
+	0x7b611a02U, 0x373e094eU, 0xa44aeeddU, 0xb51eabccU,
+	0x59c99020U, 0xc177b6b8U, 0xee907e97U, 0x4c98d435U,
+	0xe01fff99U, 0x08cdc571U, 0xa04fefd9U, 0xe15fbe98U,
+	0x1596836cU, 0xf941b880U, 0xdfec33a6U, 0x1253416bU,
+	0x0cc8c475U, 0x10d3c369U, 0x69f59c10U, 0x74aeda0dU,
+	0x9c7ce0e5U, 0x1cdcc065U, 0x79e19800U, 0x86e264ffU,
+	0xcbfd36b2U, 0x9879e1e1U, 0x8127a6f8U, 0xc572b7bcU,
+	0xe69a7c9fU, 0xd7e631aeU, 0x04c2c67dU, 0xd163b2a8U,
+	0xa905acd0U, 0x4097d739U, 0x9073e3e9U, 0x0702057eU,
+	0x1a594363U, 0x8bad26f2U, 0xbb912ac2U, 0xe29f7d9bU,
+	0x333b084aU, 0x663a5c1fU, 0xb2db69cbU, 0xd3e330aaU,
+	0x9939a0e0U, 0x87a225feU, 0x78a1d901U, 0x4e185637U,
+	0x1656406fU, 0x4752153eU, 0x5343102aU, 0xa38f2cdaU,
+	0x7f641b06U, 0x1d9c8164U, 0xcc38f4b5U, 0x1e5c4267U,
+	0x1b190262U, 0x226f4d5bU, 0xed50bd94U, 0x0a4d4773U,
+	0xf68e788fU, 0x489dd531U, 0xf00bfb89U, 0xcd78b5b4U,
+	0x2e604e57U, 0xd6a670afU, 0x5083d329U, 0x5889d121U,
+	0x4f581736U, 0xaac56fd3U, 0x5e0c5227U, 0x1716016eU,
+	0xec10fc95U, 0x4357143aU, 0x1313006aU, 0x20efcf59U,
+	0x92f361ebU, 0x01878678U, 0xb39b28caU, 0x19998060U,
+	0x9bb922e2U, 0x3cf4c845U, 0x14d6c26dU, 0x367e484fU,
+	0xac40ecd5U, 0xd566b3acU, 0xa10faed8U, 0x25aa8f5cU,
+	0xcff837b6U, 0xf7ce398eU, 0x4492d63dU, 0x8d28a5f4U,
+	0xe3df3c9aU, 0x6f701f16U, 0xa2cf6ddbU, 0x24eace5dU,
+	0x9476e2edU, 0x677a1d1eU, 0x31bb8a48U, 0x51c39228U,
+	0x2da08d54U, 0x29a58c50U, 0xf54ebb8cU, 0xdd6cb1a4U,
+	0xaec06ed7U, 0xf40efa8dU, 0x266a4c5fU, 0xd2a371abU,
+	0x762e580fU, 0xefd03f96U, 0x6a355f13U, 0x65fa9f1cU,
+	0xdc2cf0a5U, 0xffc43b86U, 0xe955bc90U, 0x9d3ca1e4U,
+	0xe41afe9dU, 0xd829f1a1U, 0xf14bba88U, 0xfbc13a82U,
+	0xb05bebc9U, 0x9fbc23e6U, 0x5c8cd025U, 0xbad16bc3U,
+	0x0e484677U, 0xaf802fd6U, 0x75ee9b0cU, 0x6e305e17U,
+	0x2ce0cc55U, 0x49dd9430U, 0xdbe932a2U, 0xd023f3a9U,
+	0x7a215b03U, 0x0f080776U, 0x4a1d5733U, 0x5a095323U,
+	0xbed46ac7U, 0x3db48944U, 0xc6b274bfU, 0xb11baac8U,
+	0xa845edd1U, 0xc2b775bbU, 0x45d2973cU, 0x61ff9e18U,
+	0x0d888574U, 0x7e245a07U, 0x5f4c1326U, 0xbc54e8c5U,
+	0x8fa827f6U, 0x21af8e58U, 0x83a724faU, 0x8aed67f3U,
+	0xf801f981U, 0x5b491222U, 0x6df09d14U, 0x2b250e52U,
+	0x70abdb09U, 0xebd53e92U, 0xad00add4U, 0x18d9c161U,
+	0x7de49904U, 0x232f0c5aU, 0xe55abf9cU, 0x60bfdf19U,
+	0xc7f235beU, 0xb79e29ceU, 0x7ca4d805U, 0xb911a8c0U,
+	0xc00bcb45U, 0xd9558c5cU, 0x3cc8f4b9U, 0x6083e3e5U,
+	0xa879d12dU, 0x1ce0fc99U, 0x6f4c23eaU, 0x45eeabc0U,
+	0xc3cb0846U, 0x41ebaac4U, 0xcbc10a4eU, 0x904fdf15U,
+	0x3b0d36beU, 0x7492e6f1U, 0x2b1932aeU, 0x8f941b0aU,
+	0x370235b2U, 0x067e7883U, 0xf1778674U, 0x422b69c7U,
+	0x330734b6U, 0x61c3a2e4U, 0x8b911a0eU, 0x944ade11U,
+	0x3187b6b4U, 0x8ad15b0fU, 0xd7da0d52U, 0x033b3886U,
+	0xbee8563bU, 0x811b9a04U, 0x839b1806U, 0xfd788578U,
+	0xe969806cU, 0x92cf5d17U, 0xda954f5fU, 0x48a1e9cdU,
+	0xdd508d58U, 0x24d6f2a1U, 0xf97d847cU, 0x721765f7U,
+	0x577a2dd2U, 0xca814b4fU, 0xb7a21532U, 0x96ca5c13U,
+	0x2cdcf0a9U, 0xa476d221U, 0x2999b0acU, 0x126f7d97U,
+	0x08f1f98dU, 0x55faafd0U, 0xebe9026eU, 0x3d88b5b8U,
+	0xbc68d439U, 0xbbad163eU, 0x38cdf5bdU, 0xcd448948U,
+	0x99059c1cU, 0xe426c261U, 0xc941884cU, 0x7097e7f5U,
+	0x4e246acbU, 0xa5369320U, 0x674621e2U, 0x01bbba84U,
+	0x86de5803U, 0x879e1902U, 0x05bebb80U, 0x6889e1edU,
+	0x54baeed1U, 0x563a6cd3U, 0xc7ce0942U, 0x71d7a6f4U,
+	0x950a9f10U, 0xfeb8467bU, 0x6b4922eeU, 0xfff8077aU,
+	0x00fbfb85U, 0x8d149908U, 0xa6f65023U, 0x398db4bcU,
+	0x3a4d77bfU, 0x364274b3U, 0x9d009d18U, 0x51ffaed4U,
+	0x5e306edbU, 0x0a717b8fU, 0xf037c775U, 0xb5229730U,
+	0x789de5fdU, 0x34c2f6b1U, 0xa7b61122U, 0xb6e25433U,
+	0x5a356fdfU, 0xc28b4947U, 0xed6c8168U, 0x4f642bcaU,
+	0xe3e30066U, 0x0b313a8eU, 0xa3b31026U, 0xe2a34167U,
+	0x166a7c93U, 0xfabd477fU, 0xdc10cc59U, 0x11afbe94U,
+	0x0f343b8aU, 0x132f3c96U, 0x6a0963efU, 0x775225f2U,
+	0x9f801f1aU, 0x1f203f9aU, 0x7a1d67ffU, 0x851e9b00U,
+	0xc801c94dU, 0x9b851e1eU, 0x82db5907U, 0xc68e4843U,
+	0xe5668360U, 0xd41ace51U, 0x073e3982U, 0xd29f4d57U,
+	0xaaf9532fU, 0x436b28c6U, 0x938f1c16U, 0x04fefa81U,
+	0x19a5bc9cU, 0x8851d90dU, 0xb86dd53dU, 0xe1638264U,
+	0x30c7f7b5U, 0x65c6a3e0U, 0xb1279634U, 0xd01fcf55U,
+	0x9ac55f1fU, 0x845eda01U, 0x7b5d26feU, 0x4de4a9c8U,
+	0x15aabf90U, 0x44aeeac1U, 0x50bfefd5U, 0xa073d325U,
+	0x7c98e4f9U, 0x1e607e9bU, 0xcfc40b4aU, 0x1da0bd98U,
+	0x18e5fd9dU, 0x2193b2a4U, 0xeeac426bU, 0x09b1b88cU,
+	0xf5728770U, 0x4b612aceU, 0xf3f70476U, 0xce844a4bU,
+	0x2d9cb1a8U, 0xd55a8f50U, 0x537f2cd6U, 0x5b752edeU,
+	0x4ca4e8c9U, 0xa939902cU, 0x5df0add8U, 0x14eafe91U,
+	0xefec036aU, 0x40abebc5U, 0x10efff95U, 0x231330a6U,
+	0x910f9e14U, 0x027b7987U, 0xb067d735U, 0x1a657f9fU,
+	0x9845dd1dU, 0x3f0837baU, 0x172a3d92U, 0x3582b7b0U,
+	0xafbc132aU, 0xd69a4c53U, 0xa2f35127U, 0x265670a3U,
+	0xcc04c849U, 0xf432c671U, 0x476e29c2U, 0x8ed45a0bU,
+	0xe023c365U, 0x6c8ce0e9U, 0xa1339224U, 0x271631a2U,
+	0x978a1d12U, 0x6486e2e1U, 0x324775b7U, 0x523f6dd7U,
+	0x2e5c72abU, 0x2a5973afU, 0xf6b24473U, 0xde904e5bU,
+	0xad3c9128U, 0xf7f20572U, 0x2596b3a0U, 0xd15f8e54U,
+	0x75d2a7f0U, 0xec2cc069U, 0x69c9a0ecU, 0x660660e3U,
+	0xdfd00f5aU, 0xfc38c479U, 0xeaa9436fU, 0x9ec05e1bU,
+	0xe7e60162U, 0xdbd50e5eU, 0xf2b74577U, 0xf83dc57dU,
+	0xb3a71436U, 0x9c40dc19U, 0x5f702fdaU, 0xb92d943cU,
+	0x0db4b988U, 0xac7cd029U, 0x761264f3U, 0x6dcca1e8U,
+	0x2f1c33aaU, 0x4a216bcfU, 0xd815cd5dU, 0xd3df0c56U,
+	0x79dda4fcU, 0x0cf4f889U, 0x49e1a8ccU, 0x59f5acdcU,
+	0xbd289538U, 0x3e4876bbU, 0xc54e8b40U, 0xb2e75537U,
+	0xabb9122eU, 0xc14b8a44U, 0x462e68c3U, 0x620361e7U,
+	0x0e747a8bU, 0x7dd8a5f8U, 0x5cb0ecd9U, 0xbfa8173aU,
+	0x8c54d809U, 0x225371a7U, 0x805bdb05U, 0x8911980cU,
+	0xfbfd067eU, 0x58b5edddU, 0x6e0c62ebU, 0x28d9f1adU,
+	0x735724f6U, 0xe829c16dU, 0xaefc522bU, 0x1b253e9eU,
+	0x7e1866fbU, 0x20d3f3a5U, 0xe6a64063U, 0x634320e6U,
+	0xc40eca41U, 0xb462d631U, 0x7f5827faU, 0xbaed573fU,
+	0xdf598608U, 0xc607c111U, 0x239ab9f4U, 0x7fd1aea8U,
+	0xb72b9c60U, 0x03b2b1d4U, 0x701e6ea7U, 0x5abce68dU,
+	0xdc99450bU, 0x5eb9e789U, 0xd4934703U, 0x8f1d9258U,
+	0x245f7bf3U, 0x6bc0abbcU, 0x344b7fe3U, 0x90c65647U,
+	0x285078ffU, 0x192c35ceU, 0xee25cb39U, 0x5d79248aU,
+	0x2c5579fbU, 0x7e91efa9U, 0x94c35743U, 0x8b18935cU,
+	0x2ed5fbf9U, 0x95831642U, 0xc888401fU, 0x1c6975cbU,
+	0xa1ba1b76U, 0x9e49d749U, 0x9cc9554bU, 0xe22ac835U,
+	0xf63bcd21U, 0x8d9d105aU, 0xc5c70212U, 0x57f3a480U,
+	0xc202c015U, 0x3b84bfecU, 0xe62fc931U, 0x6d4528baU,
+	0x4828609fU, 0xd5d30602U, 0xa8f0587fU, 0x8998115eU,
+	0x338ebde4U, 0xbb249f6cU, 0x36cbfde1U, 0x0d3d30daU,
+	0x17a3b4c0U, 0x4aa8e29dU, 0xf4bb4f23U, 0x22daf8f5U,
+	0xa33a9974U, 0xa4ff5b73U, 0x279fb8f0U, 0xd216c405U,
+	0x8657d151U, 0xfb748f2cU, 0xd613c501U, 0x6fc5aab8U,
+	0x51762786U, 0xba64de6dU, 0x78146cafU, 0x1ee9f7c9U,
+	0x998c154eU, 0x98cc544fU, 0x1aecf6cdU, 0x77dbaca0U,
+	0x4be8a39cU, 0x4968219eU, 0xd89c440fU, 0x6e85ebb9U,
+	0x8a58d25dU, 0xe1ea0b36U, 0x741b6fa3U, 0xe0aa4a37U,
+	0x1fa9b6c8U, 0x9246d445U, 0xb9a41d6eU, 0x26dff9f1U,
+	0x251f3af2U, 0x291039feU, 0x8252d055U, 0x4eade399U,
+	0x41622396U, 0x152336c2U, 0xef658a38U, 0xaa70da7dU,
+	0x67cfa8b0U, 0x2b90bbfcU, 0xb8e45c6fU, 0xa9b0197eU,
+	0x45672292U, 0xddd9040aU, 0xf23ecc25U, 0x50366687U,
+	0xfcb14d2bU, 0x146377c3U, 0xbce15d6bU, 0xfdf10c2aU,
+	0x093831deU, 0xe5ef0a32U, 0xc3428114U, 0x0efdf3d9U,
+	0x106676c7U, 0x0c7d71dbU, 0x755b2ea2U, 0x680068bfU,
+	0x80d25257U, 0x007272d7U, 0x654f2ab2U, 0x9a4cd64dU,
+	0xd7538400U, 0x84d75353U, 0x9d89144aU, 0xd9dc050eU,
+	0xfa34ce2dU, 0xcb48831cU, 0x186c74cfU, 0xcdcd001aU,
+	0xb5ab1e62U, 0x5c39658bU, 0x8cdd515bU, 0x1bacb7ccU,
+	0x06f7f1d1U, 0x97039440U, 0xa73f9870U, 0xfe31cf29U,
+	0x2f95baf8U, 0x7a94eeadU, 0xae75db79U, 0xcf4d8218U,
+	0x85971252U, 0x9b0c974cU, 0x640f6bb3U, 0x52b6e485U,
+	0x0af8f2ddU, 0x5bfca78cU, 0x4feda298U, 0xbf219e68U,
+	0x63caa9b4U, 0x013233d6U, 0xd0964607U, 0x02f2f0d5U,
+	0x07b7b0d0U, 0x3ec1ffe9U, 0xf1fe0f26U, 0x16e3f5c1U,
+	0xea20ca3dU, 0x54336783U, 0xeca5493bU, 0xd1d60706U,
+	0x32cefce5U, 0xca08c21dU, 0x4c2d619bU, 0x44276393U,
+	0x53f6a584U, 0xb66bdd61U, 0x42a2e095U, 0x0bb8b3dcU,
+	0xf0be4e27U, 0x5ff9a688U, 0x0fbdb2d8U, 0x3c417debU,
+	0x8e5dd359U, 0x1d2934caU, 0xaf359a78U, 0x053732d2U,
+	0x87179050U, 0x205a7af7U, 0x087870dfU, 0x2ad0fafdU,
+	0xb0ee5e67U, 0xc9c8011eU, 0xbda11c6aU, 0x39043deeU,
+	0xd3568504U, 0xeb608b3cU, 0x583c648fU, 0x91861746U,
+	0xff718e28U, 0x73deada4U, 0xbe61df69U, 0x38447cefU,
+	0x88d8505fU, 0x7bd4afacU, 0x2d1538faU, 0x4d6d209aU,
+	0x310e3fe6U, 0x350b3ee2U, 0xe9e0093eU, 0xc1c20316U,
+	0xb26edc65U, 0xe8a0483fU, 0x3ac4feedU, 0xce0dc319U,
+	0x6a80eabdU, 0xf37e8d24U, 0x769beda1U, 0x79542daeU,
+	0xc0824217U, 0xe36a8934U, 0xf5fb0e22U, 0x81921356U,
+	0xf8b44c2fU, 0xc4874313U, 0xede5083aU, 0xe76f8830U,
+	0xacf5597bU, 0x83129154U, 0x40226297U, 0xa67fd971U,
+	0x12e6f4c5U, 0xb32e9d64U, 0x694029beU, 0x729eeca5U,
+	0x304e7ee7U, 0x55732682U, 0xc7478010U, 0xcc8d411bU,
+	0x668fe9b1U, 0x13a6b5c4U, 0x56b3e581U, 0x46a7e191U,
+	0xa27ad875U, 0x211a3bf6U, 0xda1cc60dU, 0xadb5187aU,
+	0xb4eb5f63U, 0xde19c709U, 0x597c258eU, 0x7d512caaU,
+	0x112637c6U, 0x628ae8b5U, 0x43e2a194U, 0xa0fa5a77U,
+	0x93069544U, 0x3d013ceaU, 0x9f099648U, 0x9643d541U,
+	0xe4af4b33U, 0x47e7a090U, 0x715e2fa6U, 0x378bbce0U,
+	0x6c0569bbU, 0xf77b8c20U, 0xb1ae1f66U, 0x047773d3U,
+	0x614a2bb6U, 0x3f81bee8U, 0xf9f40d2eU, 0x7c116dabU,
+	0xdb5c870cU, 0xab309b7cU, 0x600a6ab7U, 0xa5bf1a72U,
+	0x5b8ed55bU, 0x42d09242U, 0xa74deaa7U, 0xfb06fdfbU,
+	0x33fccf33U, 0x8765e287U, 0xf4c93df4U, 0xde6bb5deU,
+	0x584e1658U, 0xda6eb4daU, 0x50441450U, 0x0bcac10bU,
+	0xa08828a0U, 0xef17f8efU, 0xb09c2cb0U, 0x14110514U,
+	0xac872bacU, 0x9dfb669dU, 0x6af2986aU, 0xd9ae77d9U,
+	0xa8822aa8U, 0xfa46bcfaU, 0x10140410U, 0x0fcfc00fU,
+	0xaa02a8aaU, 0x11544511U, 0x4c5f134cU, 0x98be2698U,
+	0x256d4825U, 0x1a9e841aU, 0x181e0618U, 0x66fd9b66U,
+	0x72ec9e72U, 0x094a4309U, 0x41105141U, 0xd324f7d3U,
+	0x46d59346U, 0xbf53ecbfU, 0x62f89a62U, 0xe9927be9U,
+	0xccff33ccU, 0x51045551U, 0x2c270b2cU, 0x0d4f420dU,
+	0xb759eeb7U, 0x3ff3cc3fU, 0xb21caeb2U, 0x89ea6389U,
+	0x9374e793U, 0xce7fb1ceU, 0x706c1c70U, 0xa60daba6U,
+	0x27edca27U, 0x20280820U, 0xa348eba3U, 0x56c19756U,
+	0x02808202U, 0x7fa3dc7fU, 0x52c49652U, 0xeb12f9ebU,
+	0xd5a174d5U, 0x3eb38d3eU, 0xfcc33ffcU, 0x9a3ea49aU,
+	0x1d5b461dU, 0x1c1b071cU, 0x9e3ba59eU, 0xf30cfff3U,
+	0xcf3ff0cfU, 0xcdbf72cdU, 0x5c4b175cU, 0xea52b8eaU,
+	0x0e8f810eU, 0x653d5865U, 0xf0cc3cf0U, 0x647d1964U,
+	0x9b7ee59bU, 0x16918716U, 0x3d734e3dU, 0xa208aaa2U,
+	0xa1c869a1U, 0xadc76aadU, 0x06858306U, 0xca7ab0caU,
+	0xc5b570c5U, 0x91f46591U, 0x6bb2d96bU, 0x2ea7892eU,
+	0xe318fbe3U, 0xaf47e8afU, 0x3c330f3cU, 0x2d674a2dU,
+	0xc1b071c1U, 0x590e5759U, 0x76e99f76U, 0xd4e135d4U,
+	0x78661e78U, 0x90b42490U, 0x38360e38U, 0x79265f79U,
+	0x8def628dU, 0x61385961U, 0x4795d247U, 0x8a2aa08aU,
+	0x94b12594U, 0x88aa2288U, 0xf18c7df1U, 0xecd73becU,
+	0x04050104U, 0x84a52184U, 0xe19879e1U, 0x1e9b851eU,
+	0x5384d753U, 0x00000000U, 0x195e4719U, 0x5d0b565dU,
+	0x7ee39d7eU, 0x4f9fd04fU, 0x9cbb279cU, 0x491a5349U,
+	0x317c4d31U, 0xd8ee36d8U, 0x080a0208U, 0x9f7be49fU,
+	0x8220a282U, 0x13d4c713U, 0x23e8cb23U, 0x7ae69c7aU,
+	0xab42e9abU, 0xfe43bdfeU, 0x2aa2882aU, 0x4b9ad14bU,
+	0x01404101U, 0x1fdbc41fU, 0xe0d838e0U, 0xd661b7d6U,
+	0x8e2fa18eU, 0xdf2bf4dfU, 0xcb3af1cbU, 0x3bf6cd3bU,
+	0xe71dfae7U, 0x85e56085U, 0x54411554U, 0x8625a386U,
+	0x8360e383U, 0xba16acbaU, 0x75295c75U, 0x9234a692U,
+	0x6ef7996eU, 0xd0e434d0U, 0x68721a68U, 0x55015455U,
+	0xb619afb6U, 0x4edf914eU, 0xc8fa32c8U, 0xc0f030c0U,
+	0xd721f6d7U, 0x32bc8e32U, 0xc675b3c6U, 0x8f6fe08fU,
+	0x74691d74U, 0xdb2ef5dbU, 0x8b6ae18bU, 0xb8962eb8U,
+	0x0a8a800aU, 0x99fe6799U, 0x2be2c92bU, 0x81e06181U,
+	0x03c0c303U, 0xa48d29a4U, 0x8caf238cU, 0xae07a9aeU,
+	0x34390d34U, 0x4d1f524dU, 0x39764f39U, 0xbdd36ebdU,
+	0x5781d657U, 0x6fb7d86fU, 0xdceb37dcU, 0x15514415U,
+	0x7ba6dd7bU, 0xf709fef7U, 0x3ab68c3aU, 0xbc932fbcU,
+	0x0c0f030cU, 0xff03fcffU, 0xa9c26ba9U, 0xc9ba73c9U,
+	0xb5d96cb5U, 0xb1dc6db1U, 0x6d375a6dU, 0x45155045U,
+	0x36b98f36U, 0x6c771b6cU, 0xbe13adbeU, 0x4ada904aU,
+	0xee57b9eeU, 0x77a9de77U, 0xf24cbef2U, 0xfd837efdU,
+	0x44551144U, 0x67bdda67U, 0x712c5d71U, 0x05454005U,
+	0x7c631f7cU, 0x40501040U, 0x69325b69U, 0x63b8db63U,
+	0x28220a28U, 0x07c5c207U, 0xc4f531c4U, 0x22a88a22U,
+	0x9631a796U, 0x37f9ce37U, 0xed977aedU, 0xf649bff6U,
+	0xb4992db4U, 0xd1a475d1U, 0x4390d343U, 0x485a1248U,
+	0xe258bae2U, 0x9771e697U, 0xd264b6d2U, 0xc270b2c2U,
+	0x26ad8b26U, 0xa5cd68a5U, 0x5ecb955eU, 0x29624b29U,
+	0x303c0c30U, 0x5ace945aU, 0xddab76ddU, 0xf9867ff9U,
+	0x95f16495U, 0xe65dbbe6U, 0xc735f2c7U, 0x242d0924U,
+	0x17d1c617U, 0xb9d66fb9U, 0x1bdec51bU, 0x12948612U,
+	0x60781860U, 0xc330f3c3U, 0xf5897cf5U, 0xb35cefb3U,
+	0xe8d23ae8U, 0x73acdf73U, 0x35794c35U, 0x80a02080U,
+	0xe59d78e5U, 0xbb56edbbU, 0x7d235e7dU, 0xf8c63ef8U,
+	0x5f8bd45fU, 0x2fe7c82fU, 0xe4dd39e4U, 0x21684921U,
+	0x05c9cc42U, 0x1c978b5bU, 0xf90af3beU, 0xa541e4e2U,
+	0x6dbbd62aU, 0xd922fb9eU, 0xaa8e24edU, 0x802cacc7U,
+	0x06090f41U, 0x8429adc3U, 0x0e030d49U, 0x558dd812U,
+	0xfecf31b9U, 0xb150e1f6U, 0xeedb35a9U, 0x4a561c0dU,
+	0xf2c032b5U, 0xc3bc7f84U, 0x34b58173U, 0x87e96ec0U,
+	0xf6c533b1U, 0xa401a5e3U, 0x4e531d09U, 0x5188d916U,
+	0xf445b1b3U, 0x4f135c08U, 0x12180a55U, 0xc6f93f81U,
+	0x7b2a513cU, 0x44d99d03U, 0x46591f01U, 0x38ba827fU,
+	0x2cab876bU, 0x570d5a10U, 0x1f574858U, 0x8d63eecaU,
+	0x18928a5fU, 0xe114f5a6U, 0x3cbf837bU, 0xb7d562f0U,
+	0x92b82ad5U, 0x0f434c48U, 0x72601235U, 0x53085b14U,
+	0xe91ef7aeU, 0x61b4d526U, 0xec5bb7abU, 0xd7ad7a90U,
+	0xcd33fe8aU, 0x9038a8d7U, 0x2e2b0569U, 0xf84ab2bfU,
+	0x79aad33eU, 0x7e6f1139U, 0xfd0ff2baU, 0x08868e4fU,
+	0x5cc79b1bU, 0x21e4c566U, 0x0c838f4bU, 0xb555e0f2U,
+	0x8be66dccU, 0x60f49427U, 0xa28426e5U, 0xc479bd83U,
+	0x431c5f04U, 0x425c1e05U, 0xc07cbc87U, 0xad4be6eaU,
+	0x9178e9d6U, 0x93f86bd4U, 0x020c0e45U, 0xb415a1f3U,
+	0x50c89817U, 0x3b7a417cU, 0xae8b25e9U, 0x3a3a007dU,
+	0xc539fc82U, 0x48d69e0fU, 0x63345724U, 0xfc4fb3bbU,
+	0xff8f70b8U, 0xf38073b4U, 0x58c29a1fU, 0x943da9d3U,
+	0x9bf269dcU, 0xcfb37c88U, 0x35f5c072U, 0x70e09037U,
+	0xbd5fe2faU, 0xf100f1b6U, 0x62741625U, 0x73205334U,
+	0x9ff768d8U, 0x07494e40U, 0x28ae866fU, 0x8aa62ccdU,
+	0x26210761U, 0xcef33d89U, 0x66711721U, 0x27614660U,
+	0xd3a87b94U, 0x3f7f4078U, 0x19d2cb5eU, 0xd46db993U,
+	0xcaf63c8dU, 0xd6ed3b91U, 0xafcb64e8U, 0xb29022f5U,
+	0x5a42181dU, 0xdae2389dU, 0xbfdf60f8U, 0x40dc9c07U,
+	0x0dc3ce4aU, 0x5e471919U, 0x47195e00U, 0x034c4f44U,
+	0x20a48467U, 0x11d8c956U, 0xc2fc3e85U, 0x175d4a50U,
+	0x6f3b5428U, 0x86a92fc1U, 0x564d1b11U, 0xc13cfd86U,
+	0xdc67bb9bU, 0x4d93de0aU, 0x7dafd23aU, 0x24a18563U,
+	0xf505f0b2U, 0xa004a4e7U, 0x74e59133U, 0x15ddc852U,
+	0x5f075818U, 0x419cdd06U, 0xbe9f21f9U, 0x8826aecfU,
+	0xd068b897U, 0x816cedc6U, 0x957de8d2U, 0x65b1d422U,
+	0xb95ae3feU, 0xdba2799cU, 0x0a060c4dU, 0xd862ba9fU,
+	0xdd27fa9aU, 0xe451b5a3U, 0x2b6e456cU, 0xcc73bf8bU,
+	0x30b08077U, 0x8ea32dc9U, 0x36350371U, 0x0b464d4cU,
+	0xe85eb6afU, 0x10988857U, 0x96bd2bd1U, 0x9eb729d9U,
+	0x8966efceU, 0x6cfb972bU, 0x9832aadfU, 0xd128f996U,
+	0x2a2e046dU, 0x8569ecc2U, 0xd52df892U, 0xe6d137a1U,
+	0x54cd9913U, 0xc7b97e80U, 0x75a5d032U, 0xdfa77898U,
+	0x5d87da1aU, 0xfaca30bdU, 0xd2e83a95U, 0xf040b0b7U,
+	0x6a7e142dU, 0x13584b54U, 0x67315620U, 0xe39477a4U,
+	0x09c6cf4eU, 0x31f0c176U, 0x82ac2ec5U, 0x4b165d0cU,
+	0x25e1c462U, 0xa94ee7eeU, 0x64f19523U, 0xe2d436a5U,
+	0x52481a15U, 0xa144e5e6U, 0xf78572b0U, 0x97fd6ad0U,
+	0xeb9e75acU, 0xef9b74a8U, 0x33704374U, 0x1b52495cU,
+	0x68fe962fU, 0x32300275U, 0xe054b4a7U, 0x149d8953U,
+	0xb010a0f7U, 0x29eec76eU, 0xac0ba7ebU, 0xa3c467e4U,
+	0x1a12085dU, 0x39fac37eU, 0x2f6b4468U, 0x5b02591cU,
+	0x22240665U, 0x1e170959U, 0x37754270U, 0x3dffc27aU,
+	0x76651331U, 0x5982db1eU, 0x9ab228ddU, 0x7cef933bU,
+	0xc876be8fU, 0x69bed72eU, 0xb3d063f4U, 0xa80ea6efU,
+	0xeade34adU, 0x8fe36cc8U, 0x1dd7ca5aU, 0x161d0b51U,
+	0xbc1fa3fbU, 0xc936ff8eU, 0x8c23afcbU, 0x9c37abdbU,
+	0x78ea923fU, 0xfb8a71bcU, 0x008c8c47U, 0x77255230U,
+	0x6e7b1529U, 0x04898d43U, 0x83ec6fc4U, 0xa7c166e0U,
+	0xcbb67d8cU, 0xb81aa2ffU, 0x9972ebdeU, 0x7a6a103dU,
+	0x4996df0eU, 0xe79176a0U, 0x4599dc02U, 0x4cd39f0bU,
+	0x3e3f0179U, 0x9d77eadaU, 0xabce65ecU, 0xed1bf6aaU,
+	0xb69523f1U, 0x2debc66aU, 0x6b3e552cU, 0xdee73999U,
+	0xbbda61fcU, 0xe511f4a2U, 0x23644764U, 0xa68127e1U,
+	0x01cccd46U, 0x71a0d136U, 0xba9a20fdU, 0x7f2f5038U,
+	0x50d88806U, 0x4986cf1fU, 0xac1bb7faU, 0xf050a0a6U,
+	0x38aa926eU, 0x8c33bfdaU, 0xff9f60a9U, 0xd53de883U,
+	0x53184b05U, 0xd138e987U, 0x5b12490dU, 0x009c9c56U,
+	0xabde75fdU, 0xe441a5b2U, 0xbbca71edU, 0x1f475849U,
+	0xa7d176f1U, 0x96ad3bc0U, 0x61a4c537U, 0xd2f82a84U,
+	0xa3d477f5U, 0xf110e1a7U, 0x1b42594dU, 0x04999d52U,
+	0xa154f5f7U, 0x1a02184cU, 0x47094e11U, 0x93e87bc5U,
+	0x2e3b1578U, 0x11c8d947U, 0x13485b45U, 0x6dabc63bU,
+	0x79bac32fU, 0x021c1e54U, 0x4a460c1cU, 0xd872aa8eU,
+	0x4d83ce1bU, 0xb405b1e2U, 0x69aec73fU, 0xe2c426b4U,
+	0xc7a96e91U, 0x5a52080cU, 0x27715671U, 0x06191f50U,
+	0xbc0fb3eaU, 0x34a59162U, 0xb94af3efU, 0x82bc3ed4U,
+	0x9822baceU, 0xc529ec93U, 0x7b3a412dU, 0xad5bf6fbU,
+	0x2cbb977aU, 0x2b7e557dU, 0xa81eb6feU, 0x5d97ca0bU,
+	0x09d6df5fU, 0x74f58122U, 0x5992cb0fU, 0xe044a4b6U,
+	0xdef72988U, 0x35e5d063U, 0xf79562a1U, 0x9168f9c7U,
+	0x160d1b40U, 0x174d5a41U, 0x956df8c3U, 0xf85aa2aeU,
+	0xc469ad92U, 0xc6e92f90U, 0x571d4a01U, 0xe104e5b7U,
+	0x05d9dc53U, 0x6e6b0538U, 0xfb9a61adU, 0x6f2b4439U,
+	0x9028b8c6U, 0x1dc7da4bU, 0x36251360U, 0xa95ef7ffU,
+	0xaa9e34fcU, 0xa69137f0U, 0x0dd3de5bU, 0xc12ced97U,
+	0xcee32d98U, 0x9aa238ccU, 0x60e48436U, 0x25f1d473U,
+	0xe84ea6beU, 0xa411b5f2U, 0x37655261U, 0x26311770U,
+	0xcae62c9cU, 0x52580a04U, 0x7dbfc22bU, 0xdfb76889U,
+	0x73304325U, 0x9be279cdU, 0x33605365U, 0x72700224U,
+	0x86b93fd0U, 0x6a6e043cU, 0x4cc38f1aU, 0x817cfdd7U,
+	0x9fe778c9U, 0x83fc7fd5U, 0xfada20acU, 0xe78166b1U,
+	0x0f535c59U, 0x8ff37cd9U, 0xeace24bcU, 0x15cdd843U,
+	0x58d28a0eU, 0x0b565d5dU, 0x12081a44U, 0x565d0b00U,
+	0x75b5c023U, 0x44c98d12U, 0x97ed7ac1U, 0x424c0e14U,
+	0x3a2a106cU, 0xd3b86b85U, 0x035c5f55U, 0x942db9c2U,
+	0x8976ffdfU, 0x18829a4eU, 0x28be967eU, 0x71b0c127U,
+	0xa014b4f6U, 0xf515e0a3U, 0x21f4d577U, 0x40cc8c16U,
+	0x0a161c5cU, 0x148d9942U, 0xeb8e65bdU, 0xdd37ea8bU,
+	0x8579fcd3U, 0xd47da982U, 0xc06cac96U, 0x30a09066U,
+	0xec4ba7baU, 0x8eb33dd8U, 0x5f174809U, 0x8d73fedbU,
+	0x8836bedeU, 0xb140f1e7U, 0x7e7f0128U, 0x9962fbcfU,
+	0x65a1c433U, 0xdbb2698dU, 0x63244735U, 0x5e570908U,
+	0xbd4ff2ebU, 0x4589cc13U, 0xc3ac6f95U, 0xcba66d9dU,
+	0xdc77ab8aU, 0x39ead36fU, 0xcd23ee9bU, 0x8439bdd2U,
+	0x7f3f4029U, 0xd078a886U, 0x803cbcd6U, 0xb3c073e5U,
+	0x01dcdd57U, 0x92a83ac4U, 0x20b49476U, 0x8ab63cdcU,
+	0x08969e5eU, 0xafdb74f9U, 0x87f97ed1U, 0xa551f4f3U,
+	0x3f6f5069U, 0x46490f10U, 0x32201264U, 0xb68533e0U,
+	0x5cd78b0aU, 0x64e18532U, 0xd7bd6a81U, 0x1e071948U,
+	0x70f08026U, 0xfc5fa3aaU, 0x31e0d167U, 0xb7c572e1U,
+	0x07595e51U, 0xf455a1a2U, 0xa29436f4U, 0xc2ec2e94U,
+	0xbe8f31e8U, 0xba8a30ecU, 0x66610730U, 0x4e430d18U,
+	0x3defd26bU, 0x67214631U, 0xb545f0e3U, 0x418ccd17U,
+	0xe501e4b3U, 0x7cff832aU, 0xf91ae3afU, 0xf6d523a0U,
+	0x4f034c19U, 0x6ceb873aU, 0x7a7a002cU, 0x0e131d58U,
+	0x77354221U, 0x4b064d1dU, 0x62640634U, 0x68ee863eU,
+	0x23745775U, 0x0c939f5aU, 0xcfa36c99U, 0x29fed77fU,
+	0x9d67facbU, 0x3caf936aU, 0xe6c127b0U, 0xfd1fe2abU,
+	0xbfcf70e9U, 0xdaf2288cU, 0x48c68e1eU, 0x430c4f15U,
+	0xe90ee7bfU, 0x9c27bbcaU, 0xd932eb8fU, 0xc926ef9fU,
+	0x2dfbd67bU, 0xae9b35f8U, 0x559dc803U, 0x22341674U,
+	0x3b6a516dU, 0x5198c907U, 0xd6fd2b80U, 0xf2d022a4U,
+	0x9ea739c8U, 0xed0be6bbU, 0xcc63af9aU, 0x2f7b5479U,
+	0x1c879b4aU, 0xb28032e4U, 0x10889846U, 0x19c2db4fU,
+	0x6b2e453dU, 0xc866ae9eU, 0xfedf21a8U, 0xb80ab2eeU,
+	0xe38467b5U, 0x78fa822eU, 0x3e2f1168U, 0x8bf67dddU,
+	0xeecb25b8U, 0xb000b0e6U, 0x76750320U, 0xf39063a5U,
+	0x54dd8902U, 0x24b19572U, 0xef8b64b9U, 0x2a3e147cU,
+	0xb813ab25U, 0xa14dec3cU, 0x44d094d9U, 0x189b8385U,
+	0xd061b14dU, 0x64f89cf9U, 0x1754438aU, 0x3df6cba0U,
+	0xbbd36826U, 0x39f3caa4U, 0xb3d96a2eU, 0xe857bf75U,
+	0x431556deU, 0x0c8a8691U, 0x530152ceU, 0xf78c7b6aU,
+	0x4f1a55d2U, 0x7e6618e3U, 0x896fe614U, 0x3a3309a7U,
+	0x4b1f54d6U, 0x19dbc284U, 0xf3897a6eU, 0xec52be71U,
+	0x499fd6d4U, 0xf2c93b6fU, 0xafc26d32U, 0x7b2358e6U,
+	0xc6f0365bU, 0xf903fa64U, 0xfb837866U, 0x8560e518U,
+	0x9171e00cU, 0xead73d77U, 0xa28d2f3fU, 0x30b989adU,
+	0xa548ed38U, 0x5cce92c1U, 0x8165e41cU, 0x0a0f0597U,
+	0x2f624db2U, 0xb2992b2fU, 0xcfba7552U, 0xeed23c73U,
+	0x54c490c9U, 0xdc6eb241U, 0x5181d0ccU, 0x6a771df7U,
+	0x70e999edU, 0x2de2cfb0U, 0x93f1620eU, 0x4590d5d8U,
+	0xc470b459U, 0xc3b5765eU, 0x40d595ddU, 0xb55ce928U,
+	0xe11dfc7cU, 0x9c3ea201U, 0xb159e82cU, 0x088f8795U,
+	0x363c0aabU, 0xdd2ef340U, 0x1f5e4182U, 0x79a3dae4U,
+	0xfec63863U, 0xff867962U, 0x7da6dbe0U, 0x1091818dU,
+	0x2ca28eb1U, 0x2e220cb3U, 0xbfd66922U, 0x09cfc694U,
+	0xed12ff70U, 0x86a0261bU, 0x1351428eU, 0x87e0671aU,
+	0x78e39be5U, 0xf50cf968U, 0xdeee3043U, 0x4195d4dcU,
+	0x425517dfU, 0x4e5a14d3U, 0xe518fd78U, 0x29e7ceb4U,
+	0x26280ebbU, 0x72691befU, 0x882fa715U, 0xcd3af750U,
+	0x0085859dU, 0x4cda96d1U, 0xdfae7142U, 0xcefa3453U,
+	0x222d0fbfU, 0xba932927U, 0x9574e108U, 0x377c4baaU,
+	0x9bfb6006U, 0x73295aeeU, 0xdbab7046U, 0x9abb2107U,
+	0x6e721cf3U, 0x82a5271fU, 0xa408ac39U, 0x69b7def4U,
+	0x772c5beaU, 0x6b375cf6U, 0x1211038fU, 0x0f4a4592U,
+	0xe7987f7aU, 0x67385ffaU, 0x0205079fU, 0xfd06fb60U,
+	0xb019a92dU, 0xe39d7e7eU, 0xfac33967U, 0xbe962823U,
+	0x9d7ee300U, 0xac02ae31U, 0x7f2659e2U, 0xaa872d37U,
+	0xd2e1334fU, 0x3b7348a6U, 0xeb977c76U, 0x7ce69ae1U,
+	0x61bddcfcU, 0xf049b96dU, 0xc075b55dU, 0x997be204U,
+	0x48df97d5U, 0x1ddec380U, 0xc93ff654U, 0xa807af35U,
+	0xe2dd3f7fU, 0xfc46ba61U, 0x0345469eU, 0x35fcc9a8U,
+	0x6db2dff0U, 0x3cb68aa1U, 0x28a78fb5U, 0xd86bb345U,
+	0x04808499U, 0x66781efbU, 0xb7dc6b2aU, 0x65b8ddf8U,
+	0x60fd9dfdU, 0x598bd2c4U, 0x96b4220bU, 0x71a9d8ecU,
+	0x8d6ae710U, 0x33794aaeU, 0x8bef6416U, 0xb69c2a2bU,
+	0x5584d1c8U, 0xad42ef30U, 0x2b674cb6U, 0x236d4ebeU,
+	0x34bc88a9U, 0xd121f04cU, 0x25e8cdb8U, 0x6cf29ef1U,
+	0x97f4630aU, 0x38b38ba5U, 0x68f79ff5U, 0x5b0b50c6U,
+	0xe917fe74U, 0x7a6319e7U, 0xc87fb755U, 0x627d1fffU,
+	0xe05dbd7dU, 0x471057daU, 0x6f325df2U, 0x4d9ad7d0U,
+	0xd7a4734aU, 0xae822c33U, 0xdaeb3147U, 0x5e4e10c3U,
+	0xb41ca829U, 0x8c2aa611U, 0x3f7649a2U, 0xf6cc3a6bU,
+	0x983ba305U, 0x14948089U, 0xd92bf244U, 0x5f0e51c2U,
+	0xef927d72U, 0x1c9e8281U, 0x4a5f15d7U, 0x2a270db7U,
+	0x564412cbU, 0x524113cfU, 0x8eaa2413U, 0xa6882e3bU,
+	0xd524f148U, 0x8fea6512U, 0x5d8ed3c0U, 0xa947ee34U,
+	0x0dcac790U, 0x9434a009U, 0x11d1c08cU, 0x1e1e0083U,
+	0xa7c86f3aU, 0x8420a419U, 0x92b1230fU, 0xe6d83e7bU,
+	0x9ffe6102U, 0xa3cd6e3eU, 0x8aaf2517U, 0x8025a51dU,
+	0xcbbf7456U, 0xe458bc79U, 0x27684fbaU, 0xc135f45cU,
+	0x75acd9e8U, 0xd464b049U, 0x0e0a0493U, 0x15d4c188U,
+	0x570453caU, 0x32390bafU, 0xa00dad3dU, 0xabc76c36U,
+	0x01c5c49cU, 0x74ec98e9U, 0x31f9c8acU, 0x21edccbcU,
+	0xc530f558U, 0x465016dbU, 0xbd56eb20U, 0xcaff3557U,
+	0xd3a1724eU, 0xb953ea24U, 0x3e3608a3U, 0x1a1b0187U,
+	0x766c1aebU, 0x05c0c598U, 0x24a88cb9U, 0xc7b0775aU,
+	0xf44cb869U, 0x5a4b11c7U, 0xf843bb65U, 0xf109f86cU,
+	0x83e5661eU, 0x20ad8dbdU, 0x1614028bU, 0x50c191cdU,
+	0x0b4f4496U, 0x9031a10dU, 0xd6e4324bU, 0x633d5efeU,
+	0x0600069bU, 0x58cb93c5U, 0x9ebe2003U, 0x1b5b4086U,
+	0xbc16aa21U, 0xcc7ab651U, 0x0740479aU, 0xc2f5375fU,
+	0xc45e9a14U, 0xdd00dd0dU, 0x389da5e8U, 0x64d6b2b4U,
+	0xac2c807cU, 0x18b5adc8U, 0x6b1972bbU, 0x41bbfa91U,
+	0xc79e5917U, 0x45befb95U, 0xcf945b1fU, 0x941a8e44U,
+	0x3f5867efU, 0x70c7b7a0U, 0x2f4c63ffU, 0x8bc14a5bU,
+	0x335764e3U, 0x022b29d2U, 0xf522d725U, 0x467e3896U,
+	0x375265e7U, 0x6596f3b5U, 0x8fc44b5fU, 0x901f8f40U,
+	0x35d2e7e5U, 0x8e840a5eU, 0xd38f5c03U, 0x076e69d7U,
+	0xbabd076aU, 0x854ecb55U, 0x87ce4957U, 0xf92dd429U,
+	0xed3cd13dU, 0x969a0c46U, 0xdec01e0eU, 0x4cf4b89cU,
+	0xd905dc09U, 0x2083a3f0U, 0xfd28d52dU, 0x764234a6U,
+	0x532f7c83U, 0xced41a1eU, 0xb3f74463U, 0x929f0d42U,
+	0x2889a1f8U, 0xa0238370U, 0x2dcce1fdU, 0x163a2cc6U,
+	0x0ca4a8dcU, 0x51affe81U, 0xefbc533fU, 0x39dde4e9U,
+	0xb83d8568U, 0xbff8476fU, 0x3c98a4ecU, 0xc911d819U,
+	0x9d50cd4dU, 0xe0739330U, 0xcd14d91dU, 0x74c2b6a4U,
+	0x4a713b9aU, 0xa163c271U, 0x631370b3U, 0x05eeebd5U,
+	0x828b0952U, 0x83cb4853U, 0x01ebead1U, 0x6cdcb0bcU,
+	0x50efbf80U, 0x526f3d82U, 0xc39b5813U, 0x7582f7a5U,
+	0x915fce41U, 0xfaed172aU, 0x6f1c73bfU, 0xfbad562bU,
+	0x04aeaad4U, 0x8941c859U, 0xa2a30172U, 0x3dd8e5edU,
+	0x3e1826eeU, 0x321725e2U, 0x9955cc49U, 0x55aaff85U,
+	0x5a653f8aU, 0x0e242adeU, 0xf4629624U, 0xb177c661U,
+	0x7cc8b4acU, 0x3097a7e0U, 0xa3e34073U, 0xb2b70562U,
+	0x5e603e8eU, 0xc6de1816U, 0xe939d039U, 0x4b317a9bU,
+	0xe7b65137U, 0x0f646bdfU, 0xa7e64177U, 0xe6f61036U,
+	0x123f2dc2U, 0xfee8162eU, 0xd8459d08U, 0x15faefc5U,
+	0x0b616adbU, 0x177a6dc7U, 0x6e5c32beU, 0x730774a3U,
+	0x9bd54e4bU, 0x1b756ecbU, 0x7e4836aeU, 0x814bca51U,
+	0xcc54981cU, 0x9fd04f4fU, 0x868e0856U, 0xc2db1912U,
+	0xe133d231U, 0xd04f9f00U, 0x036b68d3U, 0xd6ca1c06U,
+	0xaeac027eU, 0x473e7997U, 0x97da4d47U, 0x00ababd0U,
+	0x1df0edcdU, 0x8c04885cU, 0xbc38846cU, 0xe536d335U,
+	0x3492a6e4U, 0x6193f2b1U, 0xb572c765U, 0xd44a9e04U,
+	0x9e900e4eU, 0x800b8b50U, 0x7f0877afU, 0x49b1f899U,
+	0x11ffeec1U, 0x40fbbb90U, 0x54eabe84U, 0xa4268274U,
+	0x78cdb5a8U, 0x1a352fcaU, 0xcb915a1bU, 0x19f5ecc9U,
+	0x1cb0acccU, 0x25c6e3f5U, 0xeaf9133aU, 0x0de4e9ddU,
+	0xf127d621U, 0x4f347b9fU, 0xf7a25527U, 0xcad11b1aU,
+	0x29c9e0f9U, 0xd10fde01U, 0x572a7d87U, 0x5f207f8fU,
+	0x48f1b998U, 0xad6cc17dU, 0x59a5fc89U, 0x10bfafc0U,
+	0xebb9523bU, 0x44feba94U, 0x14baaec4U, 0x274661f7U,
+	0x955acf45U, 0x062e28d6U, 0xb4328664U, 0x1e302eceU,
+	0x9c108c4cU, 0x3b5d66ebU, 0x137f6cc3U, 0x31d7e6e1U,
+	0xabe9427bU, 0xd2cf1d02U, 0xa6a60076U, 0x220321f2U,
+	0xc8519918U, 0xf0679720U, 0x433b7893U, 0x8a810b5aU,
+	0xe4769234U, 0x68d9b1b8U, 0xa566c375U, 0x234360f3U,
+	0x93df4c43U, 0x60d3b3b0U, 0x361224e6U, 0x566a3c86U,
+	0x2a0923faU, 0x2e0c22feU, 0xf2e71522U, 0xdac51f0aU,
+	0xa969c079U, 0xf3a75423U, 0x21c3e2f1U, 0xd50adf05U,
+	0x7187f6a1U, 0xe8799138U, 0x6d9cf1bdU, 0x625331b2U,
+	0xdb855e0bU, 0xf86d9528U, 0xeefc123eU, 0x9a950f4aU,
+	0xe3b35033U, 0xdf805f0fU, 0xf6e21426U, 0xfc68942cU,
+	0xb7f24567U, 0x98158d48U, 0x5b257e8bU, 0xbd78c56dU,
+	0x09e1e8d9U, 0xa8298178U, 0x724735a2U, 0x6999f0b9U,
+	0x2b4962fbU, 0x4e743a9eU, 0xdc409c0cU, 0xd78a5d07U,
+	0x7d88f5adU, 0x08a1a9d8U, 0x4db4f99dU, 0x5da0fd8dU,
+	0xb97dc469U, 0x3a1d27eaU, 0xc11bda11U, 0xb6b20466U,
+	0xafec437fU, 0xc51edb15U, 0x427b3992U, 0x665630b6U,
+	0x0a212bdaU, 0x798df4a9U, 0x58e5bd88U, 0xbbfd466bU,
+	0x88018958U, 0x260620f6U, 0x840e8a54U, 0x8d44c95dU,
+	0xffa8572fU, 0x5ce0bc8cU, 0x6a5933baU, 0x2c8ca0fcU,
+	0x770275a7U, 0xec7c903cU, 0xaaa9037aU, 0x1f706fcfU,
+	0x7a4d37aaU, 0x2486a2f4U, 0xe2f31132U, 0x671671b7U,
+	0xc05b9b10U, 0xb0378760U, 0x7b0d76abU, 0xbeb8066eU,
+	0xe0a949c7U, 0xf9f70edeU, 0x1c6a763bU, 0x40216167U,
+	0x88db53afU, 0x3c427e1bU, 0x4feea168U, 0x654c2942U,
+	0xe3698ac4U, 0x61492846U, 0xeb6388ccU, 0xb0ed5d97U,
+	0x1bafb43cU, 0x54306473U, 0x0bbbb02cU, 0xaf369988U,
+	0x17a0b730U, 0x26dcfa01U, 0xd1d504f6U, 0x6289eb45U,
+	0x13a5b634U, 0x41612066U, 0xab33988cU, 0xb4e85c93U,
+	0x11253436U, 0xaa73d98dU, 0xf7788fd0U, 0x2399ba04U,
+	0x9e4ad4b9U, 0xa1b91886U, 0xa3399a84U, 0xddda07faU,
+	0xc9cb02eeU, 0xb26ddf95U, 0xfa37cdddU, 0x68036b4fU,
+	0xfdf20fdaU, 0x04747023U, 0xd9df06feU, 0x52b5e775U,
+	0x77d8af50U, 0xea23c9cdU, 0x970097b0U, 0xb668de91U,
+	0x0c7e722bU, 0x84d450a3U, 0x093b322eU, 0x32cdff15U,
+	0x28537b0fU, 0x75582d52U, 0xcb4b80ecU, 0x1d2a373aU,
+	0x9cca56bbU, 0x9b0f94bcU, 0x186f773fU, 0xede60bcaU,
+	0xb9a71e9eU, 0xc48440e3U, 0xe9e30aceU, 0x50356577U,
+	0x6e86e849U, 0x859411a2U, 0x47e4a360U, 0x21193806U,
+	0xa67cda81U, 0xa73c9b80U, 0x251c3902U, 0x482b636fU,
+	0x74186c53U, 0x7698ee51U, 0xe76c8bc0U, 0x51752476U,
+	0xb5a81d92U, 0xde1ac4f9U, 0x4beba06cU, 0xdf5a85f8U,
+	0x20597907U, 0xadb61b8aU, 0x8654d2a1U, 0x192f363eU,
+	0x1aeff53dU, 0x16e0f631U, 0xbda21f9aU, 0x715d2c56U,
+	0x7e92ec59U, 0x2ad3f90dU, 0xd09545f7U, 0x958015b2U,
+	0x583f677fU, 0x14607433U, 0x871493a0U, 0x9640d6b1U,
+	0x7a97ed5dU, 0xe229cbc5U, 0xcdce03eaU, 0x6fc6a948U,
+	0xc34182e4U, 0x2b93b80cU, 0x831192a4U, 0xc201c3e5U,
+	0x36c8fe11U, 0xda1fc5fdU, 0xfcb24edbU, 0x310d3c16U,
+	0x2f96b908U, 0x338dbe14U, 0x4aabe16dU, 0x57f0a770U,
+	0xbf229d98U, 0x3f82bd18U, 0x5abfe57dU, 0xa5bc1982U,
+	0xe8a34bcfU, 0xbb279c9cU, 0xa279db85U, 0xe62ccac1U,
+	0xc5c401e2U, 0xf4b84cd3U, 0x279cbb00U, 0xf23dcfd5U,
+	0x8a5bd1adU, 0x63c9aa44U, 0xb32d9e94U, 0x245c7803U,
+	0x39073e1eU, 0xa8f35b8fU, 0x98cf57bfU, 0xc1c100e6U,
+	0x10657537U, 0x45642162U, 0x918514b6U, 0xf0bd4dd7U,
+	0xba67dd9dU, 0xa4fc5883U, 0x5bffa47cU, 0x6d462b4aU,
+	0x35083d12U, 0x640c6843U, 0x701d6d57U, 0x80d151a7U,
+	0x5c3a667bU, 0x3ec2fc19U, 0xef6689c8U, 0x3d023f1aU,
+	0x38477f1fU, 0x01313026U, 0xce0ec0e9U, 0x29133a0eU,
+	0xd5d005f2U, 0x6bc3a84cU, 0xd35586f4U, 0xee26c8c9U,
+	0x0d3e332aU, 0xf5f80dd2U, 0x73ddae54U, 0x7bd7ac5cU,
+	0x6c066a4bU, 0x899b12aeU, 0x7d522f5aU, 0x34487c13U,
+	0xcf4e81e8U, 0x60096947U, 0x304d7d17U, 0x03b1b224U,
+	0xb1ad1c96U, 0x22d9fb05U, 0x90c555b7U, 0x3ac7fd1dU,
+	0xb8e75f9fU, 0x1faab538U, 0x3788bf10U, 0x15203532U,
+	0x8f1e91a8U, 0xf638ced1U, 0x8251d3a5U, 0x06f4f221U,
+	0xeca64acbU, 0xd49044f3U, 0x67ccab40U, 0xae76d889U,
+	0xc08141e7U, 0x4c2e626bU, 0x819110a6U, 0x07b4b320U,
+	0xb7289f90U, 0x44246063U, 0x12e5f735U, 0x729def55U,
+	0x0efef029U, 0x0afbf12dU, 0xd610c6f1U, 0xfe32ccd9U,
+	0x8d9e13aaU, 0xd75087f0U, 0x05343122U, 0xf1fd0cd6U,
+	0x55702572U, 0xcc8e42ebU, 0x496b226eU, 0x46a4e261U,
+	0xff728dd8U, 0xdc9a46fbU, 0xca0bc1edU, 0xbe62dc99U,
+	0xc74483e0U, 0xfb778cdcU, 0xd215c7f5U, 0xd89f47ffU,
+	0x930596b4U, 0xbce25e9bU, 0x7fd2ad58U, 0x998f16beU,
+	0x2d163b0aU, 0x8cde52abU, 0x56b0e671U, 0x4d6e236aU,
+	0x0fbeb128U, 0x6a83e94dU, 0xf8b74fdfU, 0xf37d8ed4U,
+	0x597f267eU, 0x2c567a0bU, 0x69432a4eU, 0x79572e5eU,
+	0x9d8a17baU, 0x1eeaf439U, 0xe5ec09c2U, 0x9245d7b5U,
+	0x8b1b90acU, 0xe1e908c6U, 0x668cea41U, 0x42a1e365U,
+	0x2ed6f809U, 0x5d7a277aU, 0x7c126e5bU, 0x9f0a95b8U,
+	0xacf65a8bU, 0x02f1f325U, 0xa0f95987U, 0xa9b31a8eU,
+	0xdb5f84fcU, 0x78176f5fU, 0x4eaee069U, 0x087b732fU,
+	0x53f5a674U, 0xc88b43efU, 0x8e5ed0a9U, 0x3b87bc1cU,
+	0x5ebae479U, 0x00717127U, 0xc604c2e1U, 0x43e1a264U,
+	0xe4ac48c3U, 0x94c054b3U, 0x5ffaa578U, 0x9a4fd5bdU,
+	0x41dd9c12U, 0x5883db0bU, 0xbd1ea3eeU, 0xe155b4b2U,
+	0x29af867aU, 0x9d36abceU, 0xee9a74bdU, 0xc438fc97U,
+	0x421d5f11U, 0xc03dfd93U, 0x4a175d19U, 0x11998842U,
+	0xbadb61e9U, 0xf544b1a6U, 0xaacf65f9U, 0x0e424c5dU,
+	0xb6d462e5U, 0x87a82fd4U, 0x70a1d123U, 0xc3fd3e90U,
+	0xb2d163e1U, 0xe015f5b3U, 0x0a474d59U, 0x159c8946U,
+	0xb051e1e3U, 0x0b070c58U, 0x560c5a05U, 0x82ed6fd1U,
+	0x3f3e016cU, 0x00cdcd53U, 0x024d4f51U, 0x7caed22fU,
+	0x68bfd73bU, 0x13190a40U, 0x5b431808U, 0xc977be9aU,
+	0x5c86da0fU, 0xa500a5f6U, 0x78abd32bU, 0xf3c132a0U,
+	0xd6ac7a85U, 0x4b571c18U, 0x36744265U, 0x171c0b44U,
+	0xad0aa7feU, 0x25a08576U, 0xa84fe7fbU, 0x93b92ac0U,
+	0x8927aedaU, 0xd42cf887U, 0x6a3f5539U, 0xbc5ee2efU,
+	0x3dbe836eU, 0x3a7b4169U, 0xb91ba2eaU, 0x4c92de1fU,
+	0x18d3cb4bU, 0x65f09536U, 0x4897df1bU, 0xf141b0a2U,
+	0xcff23d9cU, 0x24e0c477U, 0xe69076b5U, 0x806dedd3U,
+	0x07080f54U, 0x06484e55U, 0x8468ecd7U, 0xe95fb6baU,
+	0xd56cb986U, 0xd7ec3b84U, 0x46185e15U, 0xf001f1a3U,
+	0x14dcc847U, 0x7f6e112cU, 0xea9f75b9U, 0x7e2e502dU,
+	0x812dacd2U, 0x0cc2ce5fU, 0x27200774U, 0xb85be3ebU,
+	0xbb9b20e8U, 0xb79423e4U, 0x1cd6ca4fU, 0xd029f983U,
+	0xdfe6398cU, 0x8ba72cd8U, 0x71e19022U, 0x34f4c067U,
+	0xf94bb2aaU, 0xb514a1e6U, 0x26604675U, 0x37340364U,
+	0xdbe33888U, 0x435d1e10U, 0x6cbad63fU, 0xceb27c9dU,
+	0x62355731U, 0x8ae76dd9U, 0x22654771U, 0x63751630U,
+	0x97bc2bc4U, 0x7b6b1028U, 0x5dc69b0eU, 0x9079e9c3U,
+	0x8ee26cddU, 0x92f96bc1U, 0xebdf34b8U, 0xf68472a5U,
+	0x1e56484dU, 0x9ef668cdU, 0xfbcb30a8U, 0x04c8cc57U,
+	0x49d79e1aU, 0x1a534949U, 0x030d0e50U, 0x47581f14U,
+	0x64b0d437U, 0x55cc9906U, 0x86e86ed5U, 0x53491a00U,
+	0x2b2f0478U, 0xc2bd7f91U, 0x12594b41U, 0x8528add6U,
+	0x9873ebcbU, 0x09878e5aU, 0x39bb826aU, 0x60b5d533U,
+	0xb111a0e2U, 0xe410f4b7U, 0x30f1c163U, 0x51c99802U,
+	0x1b130848U, 0x05888d56U, 0xfa8b71a9U, 0xcc32fe9fU,
+	0x947ce8c7U, 0xc578bd96U, 0xd169b882U, 0x21a58472U,
+	0xfd4eb3aeU, 0x9fb629ccU, 0x4e125c1dU, 0x9c76eacfU,
+	0x9933aacaU, 0xa045e5f3U, 0x6f7a153cU, 0x8867efdbU,
+	0x74a4d027U, 0xcab77d99U, 0x72215321U, 0x4f521d1cU,
+	0xac4ae6ffU, 0x548cd807U, 0xd2a97b81U, 0xdaa37989U,
+	0xcd72bf9eU, 0x28efc77bU, 0xdc26fa8fU, 0x953ca9c6U,
+	0x6e3a543dU, 0xc17dbc92U, 0x9139a8c2U, 0xa2c567f1U,
+	0x10d9c943U, 0x83ad2ed0U, 0x31b18062U, 0x9bb328c8U,
+	0x19938a4aU, 0xbede60edU, 0x96fc6ac5U, 0xb454e0e7U,
+	0x2e6a447dU, 0x574c1b04U, 0x23250670U, 0xa78027f4U,
+	0x4dd29f1eU, 0x75e49126U, 0xc6b87e95U, 0x0f020d5cU,
+	0x61f59432U, 0xed5ab7beU, 0x20e5c573U, 0xa6c066f5U,
+	0x165c4a45U, 0xe550b5b6U, 0xb39122e0U, 0xd3e93a80U,
+	0xaf8a25fcU, 0xab8f24f8U, 0x77641324U, 0x5f46190cU,
+	0x2ceac67fU, 0x76245225U, 0xa440e4f7U, 0x5089d903U,
+	0xf404f0a7U, 0x6dfa973eU, 0xe81ff7bbU, 0xe7d037b4U,
+	0x5e06580dU, 0x7dee932eU, 0x6b7f1438U, 0x1f16094cU,
+	0x66305635U, 0x5a035909U, 0x73611220U, 0x79eb922aU,
+	0x32714361U, 0x1d968b4eU, 0xdea6788dU, 0x38fbc36bU,
+	0x8c62eedfU, 0x2daa877eU, 0xf7c433a4U, 0xec1af6bfU,
+	0xaeca64fdU, 0xcbf73c98U, 0x59c39a0aU, 0x52095b01U,
+	0xf80bf3abU, 0x8d22afdeU, 0xc837ff9bU, 0xd823fb8bU,
+	0x3cfec26fU, 0xbf9e21ecU, 0x4498dc17U, 0x33310260U,
+	0x2a6f4579U, 0x409ddd13U, 0xc7f83f94U, 0xe3d536b0U,
+	0x8fa22ddcU, 0xfc0ef2afU, 0xdd66bb8eU, 0x3e7e406dU,
+	0x0d828f5eU, 0xa38526f0U, 0x018d8c52U, 0x08c7cf5bU,
+	0x7a2b5129U, 0xd963ba8aU, 0xefda35bcU, 0xa90fa6faU,
+	0xf28173a1U, 0x69ff963aU, 0x2f2a057cU, 0x9af369c9U,
+	0xffce31acU, 0xa105a4f2U, 0x67701734U, 0xe29577b1U,
+	0x45d89d16U, 0x35b48166U, 0xfe8e70adU, 0x3b3b0068U,
+	0x27c3e46aU, 0x3e9da373U, 0xdb00db96U, 0x874bcccaU,
+	0x4fb1fe02U, 0xfb28d3b6U, 0x88840cc5U, 0xa22684efU,
+	0x24032769U, 0xa62385ebU, 0x2c092561U, 0x7787f03aU,
+	0xdcc51991U, 0x935ac9deU, 0xccd11d81U, 0x685c3425U,
+	0xd0ca1a9dU, 0xe1b657acU, 0x16bfa95bU, 0xa5e346e8U,
+	0xd4cf1b99U, 0x860b8dcbU, 0x6c593521U, 0x7382f13eU,
+	0xd64f999bU, 0x6d197420U, 0x3012227dU, 0xe4f317a9U,
+	0x59207914U, 0x66d3b52bU, 0x64533729U, 0x1ab0aa57U,
+	0x0ea1af43U, 0x75077238U, 0x3d5d6070U, 0xaf69c6e2U,
+	0x3a98a277U, 0xc31edd8eU, 0x1eb5ab53U, 0x95df4ad8U,
+	0xb0b202fdU, 0x2d496460U, 0x506a3a1dU, 0x7102733cU,
+	0xcb14df86U, 0x43befd0eU, 0xce519f83U, 0xf5a752b8U,
+	0xef39d6a2U, 0xb23280ffU, 0x0c212d41U, 0xda409a97U,
+	0x5ba0fb16U, 0x5c653911U, 0xdf05da92U, 0x2a8ca667U,
+	0x7ecdb333U, 0x03eeed4eU, 0x2e89a763U, 0x975fc8daU,
+	0xa9ec45e4U, 0x42febc0fU, 0x808e0ecdU, 0xe67395abU,
+	0x6116772cU, 0x6056362dU, 0xe27694afU, 0x8f41cec2U,
+	0xb372c1feU, 0xb1f243fcU, 0x2006266dU, 0x961f89dbU,
+	0x72c2b03fU, 0x19706954U, 0x8c810dc1U, 0x18302855U,
+	0xe733d4aaU, 0x6adcb627U, 0x413e7f0cU, 0xde459b93U,
+	0xdd855890U, 0xd18a5b9cU, 0x7ac8b237U, 0xb63781fbU,
+	0xb9f841f4U, 0xedb954a0U, 0x17ffe85aU, 0x52eab81fU,
+	0x9f55cad2U, 0xd30ad99eU, 0x407e3e0dU, 0x512a7b1cU,
+	0xbdfd40f0U, 0x25436668U, 0x0aa4ae47U, 0xa8ac04e5U,
+	0x042b2f49U, 0xecf915a1U, 0x447b3f09U, 0x056b6e48U,
+	0xf1a253bcU, 0x1d756850U, 0x3bd8e376U, 0xf66791bbU,
+	0xe8fc14a5U, 0xf4e713b9U, 0x8dc14cc0U, 0x909a0addU,
+	0x78483035U, 0xf8e810b5U, 0x9dd548d0U, 0x62d6b42fU,
+	0x2fc9e662U, 0x7c4d3131U, 0x65137628U, 0x2146676cU,
+	0x02aeac4fU, 0x33d2e17eU, 0xe0f616adU, 0x35576278U,
+	0x4d317c00U, 0xa4a307e9U, 0x74473339U, 0xe336d5aeU,
+	0xfe6d93b3U, 0x6f99f622U, 0x5fa5fa12U, 0x06abad4bU,
+	0xd70fd89aU, 0x820e8ccfU, 0x56efb91bU, 0x37d7e07aU,
+	0x7d0d7030U, 0x6396f52eU, 0x9c9509d1U, 0xaa2c86e7U,
+	0xf26290bfU, 0xa366c5eeU, 0xb777c0faU, 0x47bbfc0aU,
+	0x9b50cbd6U, 0xf9a851b4U, 0x280c2465U, 0xfa6892b7U,
+	0xff2dd2b2U, 0xc65b9d8bU, 0x09646d44U, 0xee7997a3U,
+	0x12baa85fU, 0xaca905e1U, 0x143f2b59U, 0x294c6564U,
+	0xca549e87U, 0x3292a07fU, 0xb4b703f9U, 0xbcbd01f1U,
+	0xab6cc7e6U, 0x4ef1bf03U, 0xba3882f7U, 0xf322d1beU,
+	0x08242c45U, 0xa763c4eaU, 0xf727d0baU, 0xc4db1f89U,
+	0x76c7b13bU, 0xe5b356a8U, 0x57aff81aU, 0xfdad50b0U,
+	0x7f8df232U, 0xd8c01895U, 0xf0e212bdU, 0xd24a989fU,
+	0x48743c05U, 0x3152637cU, 0x453b7e08U, 0xc19e5f8cU,
+	0x2bcce766U, 0x13fae95eU, 0xa0a606edU, 0x691c7524U,
+	0x07ebec4aU, 0x8b44cfc6U, 0x46fbbd0bU, 0xc0de1e8dU,
+	0x7042323dU, 0x834ecdceU, 0xd58f5a98U, 0xb5f742f8U,
+	0xc9945d84U, 0xcd915c80U, 0x117a6b5cU, 0x39586174U,
+	0x4af4be07U, 0x103a2a5dU, 0xc25e9c8fU, 0x3697a17bU,
+	0x921a88dfU, 0x0be4ef46U, 0x8e018fc3U, 0x81ce4fccU,
+	0x38182075U, 0x1bf0eb56U, 0x0d616c40U, 0x79087134U,
+	0x002e2e4dU, 0x3c1d2171U, 0x157f6a58U, 0x1ff5ea52U,
+	0x546f3b19U, 0x7b88f336U, 0xb8b800f5U, 0x5ee5bb13U,
+	0xea7c96a7U, 0x4bb4ff06U, 0x91da4bdcU, 0x8a048ec7U,
+	0xc8d41c85U, 0xade944e0U, 0x3fdde272U, 0x34172379U,
+	0x9e158bd3U, 0xeb3cd7a6U, 0xae2987e3U, 0xbe3d83f3U,
+	0x5ae0ba17U, 0xd9805994U, 0x2286a46fU, 0x552f7a18U,
+	0x4c713d01U, 0x2683a56bU, 0xa1e647ecU, 0x85cb4ec8U,
+	0xe9bc55a4U, 0x9a108ad7U, 0xbb78c3f6U, 0x58603815U,
+	0x6b9cf726U, 0xc59b5e88U, 0x6793f42aU, 0x6ed9b723U,
+	0x1c352951U, 0xbf7dc2f2U, 0x89c44dc4U, 0xcf11de82U,
+	0x949f0bd9U, 0x0fe1ee42U, 0x49347d04U, 0xfced11b1U,
+	0x99d049d4U, 0xc71bdc8aU, 0x016e6f4cU, 0x848b0fc9U,
+	0x23c6e56eU, 0x53aaf91eU, 0x989008d5U, 0x5d257810U,
+	0xb5b80d83U, 0xace64a9aU, 0x497b327fU, 0x15302523U,
+	0xddca17ebU, 0x69533a5fU, 0x1affe52cU, 0x305d6d06U,
+	0xb678ce80U, 0x34586c02U, 0xbe72cc88U, 0xe5fc19d3U,
+	0x4ebef078U, 0x01212037U, 0x5eaaf468U, 0xfa27ddccU,
+	0x42b1f374U, 0x73cdbe45U, 0x84c440b2U, 0x3798af01U,
+	0x46b4f270U, 0x14706422U, 0xfe22dcc8U, 0xe1f918d7U,
+	0x44347072U, 0xff629dc9U, 0xa269cb94U, 0x7688fe40U,
+	0xcb5b90fdU, 0xf4a85cc2U, 0xf628dec0U, 0x88cb43beU,
+	0x9cda46aaU, 0xe77c9bd1U, 0xaf268999U, 0x3d122f0bU,
+	0xa8e34b9eU, 0x51653467U, 0x8cce42baU, 0x07a4a331U,
+	0x22c9eb14U, 0xbf328d89U, 0xc211d3f4U, 0xe3799ad5U,
+	0x596f366fU, 0xd1c514e7U, 0x5c2a766aU, 0x67dcbb51U,
+	0x7d423f4bU, 0x20496916U, 0x9e5ac4a8U, 0x483b737eU,
+	0xc9db12ffU, 0xce1ed0f8U, 0x4d7e337bU, 0xb8f74f8eU,
+	0xecb65adaU, 0x919504a7U, 0xbcf24e8aU, 0x05242133U,
+	0x3b97ac0dU, 0xd08555e6U, 0x12f5e724U, 0x74087c42U,
+	0xf36d9ec5U, 0xf22ddfc4U, 0x700d7d46U, 0x1d3a272bU,
+	0x21092817U, 0x2389aa15U, 0xb27dcf84U, 0x04646032U,
+	0xe0b959d6U, 0x8b0b80bdU, 0x1efae428U, 0x8a4bc1bcU,
+	0x75483d43U, 0xf8a75fceU, 0xd34596e5U, 0x4c3e727aU,
+	0x4ffeb179U, 0x43f1b275U, 0xe8b35bdeU, 0x244c6812U,
+	0x2b83a81dU, 0x7fc2bd49U, 0x858401b3U, 0xc09151f6U,
+	0x0d2e233bU, 0x41713077U, 0xd205d7e4U, 0xc35192f5U,
+	0x2f86a919U, 0xb7388f81U, 0x98df47aeU, 0x3ad7ed0cU,
+	0x9650c6a0U, 0x7e82fc48U, 0xd600d6e0U, 0x971087a1U,
+	0x63d9ba55U, 0x8f0e81b9U, 0xa9a30a9fU, 0x641c7852U,
+	0x7a87fd4cU, 0x669cfa50U, 0x1fbaa529U, 0x02e1e334U,
+	0xea33d9dcU, 0x6a93f95cU, 0x0faea139U, 0xf0ad5dc6U,
+	0xbdb20f8bU, 0xee36d8d8U, 0xf7689fc1U, 0xb33d8e85U,
+	0x90d545a6U, 0xa1a90897U, 0x728dff44U, 0xa72c8b91U,
+	0xdf4a95e9U, 0x36d8ee00U, 0xe63cdad0U, 0x714d3c47U,
+	0x6c167a5aU, 0xfde21fcbU, 0xcdde13fbU, 0x94d044a2U,
+	0x45743173U, 0x10756526U, 0xc49450f2U, 0xa5ac0993U,
+	0xef7699d9U, 0xf1ed1cc7U, 0x0eeee038U, 0x38576f0eU,
+	0x60197956U, 0x311d2c07U, 0x250c2913U, 0xd5c015e3U,
+	0x092b223fU, 0x6bd3b85dU, 0xba77cd8cU, 0x68137b5eU,
+	0x6d563b5bU, 0x54207462U, 0x9b1f84adU, 0x7c027e4aU,
+	0x80c141b6U, 0x3ed2ec08U, 0x8644c2b0U, 0xbb378c8dU,
+	0x582f776eU, 0xa0e94996U, 0x26ccea10U, 0x2ec6e818U,
+	0x39172e0fU, 0xdc8a56eaU, 0x28436b1eU, 0x61593857U,
+	0x9a5fc5acU, 0x35182d03U, 0x655c3953U, 0x56a0f660U,
+	0xe4bc58d2U, 0x77c8bf41U, 0xc5d411f3U, 0x6fd6b959U,
+	0xedf61bdbU, 0x4abbf17cU, 0x6299fb54U, 0x40317176U,
+	0xda0fd5ecU, 0xa3298a95U, 0xd74097e1U, 0x53e5b665U,
+	0xb9b70e8fU, 0x818100b7U, 0x32ddef04U, 0xfb679ccdU,
+	0x959005a3U, 0x193f262fU, 0xd48054e2U, 0x52a5f764U,
+	0xe239dbd4U, 0x11352427U, 0x47f4b371U, 0x278cab11U,
+	0x5befb46dU, 0x5feab569U, 0x830182b5U, 0xab23889dU,
+	0xd88f57eeU, 0x8241c3b4U, 0x50257566U, 0xa4ec4892U,
+	0x00616136U, 0x999f06afU, 0x1c7a662aU, 0x13b5a625U,
+	0xaa63c99cU, 0x898b02bfU, 0x9f1a85a9U, 0xeb7398ddU,
+	0x9255c7a4U, 0xae66c898U, 0x870483b1U, 0x8d8e03bbU,
+	0xc614d2f0U, 0xe9f31adfU, 0x2ac3e91cU, 0xcc9e52faU,
+	0x78077f4eU, 0xd9cf16efU, 0x03a1a235U, 0x187f672eU,
+	0x5aaff56cU, 0x3f92ad09U, 0xada60b9bU, 0xa66cca90U,
+	0x0c6e623aU, 0x79473e4fU, 0x3c526e0aU, 0x2c466a1aU,
+	0xc89b53feU, 0x4bfbb07dU, 0xb0fd4d86U, 0xc75493f1U,
+	0xde0ad4e8U, 0xb4f84c82U, 0x339dae05U, 0x17b0a721U,
+	0x7bc7bc4dU, 0x086b633eU, 0x29032a1fU, 0xca1bd1fcU,
+	0xf9e71ecfU, 0x57e0b761U, 0xf5e81dc3U, 0xfca25ecaU,
+	0x8e4ec0b8U, 0x2d062b1bU, 0x1bbfa42dU, 0x5d6a376bU,
+	0x06e4e230U, 0x9d9a07abU, 0xdb4f94edU, 0x6e96f858U,
+	0x0baba03dU, 0x55603563U, 0x931586a5U, 0x16f0e620U,
+	0xb1bd0c87U, 0xc1d110f7U, 0x0aebe13cU, 0xcf5e91f9U,
+	0x518cdd53U, 0x48d29a4aU, 0xad4fe2afU, 0xf104f5f3U,
+	0x39fec73bU, 0x8d67ea8fU, 0xfecb35fcU, 0xd469bdd6U,
+	0x524c1e50U, 0xd06cbcd2U, 0x5a461c58U, 0x01c8c903U,
+	0xaa8a20a8U, 0xe515f0e7U, 0xba9e24b8U, 0x1e130d1cU,
+	0xa68523a4U, 0x97f96e95U, 0x60f09062U, 0xd3ac7fd1U,
+	0xa28022a0U, 0xf044b4f2U, 0x1a160c18U, 0x05cdc807U,
+	0xa000a0a2U, 0x1b564d19U, 0x465d1b44U, 0x92bc2e90U,
+	0x2f6f402dU, 0x109c8c12U, 0x121c0e10U, 0x6cff936eU,
+	0x78ee967aU, 0x03484b01U, 0x4b125949U, 0xd926ffdbU,
+	0x4cd79b4eU, 0xb551e4b7U, 0x68fa926aU, 0xe39073e1U,
+	0xc6fd3bc4U, 0x5b065d59U, 0x26250324U, 0x074d4a05U,
+	0xbd5be6bfU, 0x35f1c437U, 0xb81ea6baU, 0x83e86b81U,
+	0x9976ef9bU, 0xc47db9c6U, 0x7a6e1478U, 0xac0fa3aeU,
+	0x2defc22fU, 0x2a2a0028U, 0xa94ae3abU, 0x5cc39f5eU,
+	0x08828a0aU, 0x75a1d477U, 0x58c69e5aU, 0xe110f1e3U,
+	0xdfa37cddU, 0x34b18536U, 0xf6c137f4U, 0x903cac92U,
+	0x17594e15U, 0x16190f14U, 0x9439ad96U, 0xf90ef7fbU,
+	0xc53df8c7U, 0xc7bd7ac5U, 0x56491f54U, 0xe050b0e2U,
+	0x048d8906U, 0x6f3f506dU, 0xface34f8U, 0x6e7f116cU,
+	0x917ced93U, 0x1c938f1eU, 0x37714635U, 0xa80aa2aaU,
+	0xabca61a9U, 0xa7c562a5U, 0x0c878b0eU, 0xc078b8c2U,
+	0xcfb778cdU, 0x9bf66d99U, 0x61b0d163U, 0x24a58126U,
+	0xe91af3ebU, 0xa545e0a7U, 0x36310734U, 0x27654225U,
+	0xcbb279c9U, 0x530c5f51U, 0x7ceb977eU, 0xdee33ddcU,
+	0x72641670U, 0x9ab62c98U, 0x32340630U, 0x73245771U,
+	0x87ed6a85U, 0x6b3a5169U, 0x4d97da4fU, 0x8028a882U,
+	0x9eb32d9cU, 0x82a82a80U, 0xfb8e75f9U, 0xe6d533e4U,
+	0x0e07090cU, 0x8ea7298cU, 0xeb9a71e9U, 0x14998d16U,
+	0x5986df5bU, 0x0a020808U, 0x135c4f11U, 0x57095e55U,
+	0x74e19576U, 0x459dd847U, 0x96b92f94U, 0x43185b41U,
+	0x3b7e4539U, 0xd2ec3ed0U, 0x02080a00U, 0x9579ec97U,
+	0x8822aa8aU, 0x19d6cf1bU, 0x29eac32bU, 0x70e49472U,
+	0xa140e1a3U, 0xf441b5f6U, 0x20a08022U, 0x4198d943U,
+	0x0b424909U, 0x15d9cc17U, 0xeada30e8U, 0xdc63bfdeU,
+	0x842da986U, 0xd529fcd7U, 0xc138f9c3U, 0x31f4c533U,
+	0xed1ff2efU, 0x8fe7688dU, 0x5e431d5cU, 0x8c27ab8eU,
+	0x8962eb8bU, 0xb014a4b2U, 0x7f2b547dU, 0x9836ae9aU,
+	0x64f59166U, 0xdae63cd8U, 0x62701260U, 0x5f035c5dU,
+	0xbc1ba7beU, 0x44dd9946U, 0xc2f83ac0U, 0xcaf238c8U,
+	0xdd23fedfU, 0x38be863aU, 0xcc77bbceU, 0x856de887U,
+	0x7e6b157cU, 0xd12cfdd3U, 0x8168e983U, 0xb29426b0U,
+	0x00888802U, 0x93fc6f91U, 0x21e0c123U, 0x8be26989U,
+	0x09c2cb0bU, 0xae8f21acU, 0x86ad2b84U, 0xa405a1a6U,
+	0x3e3b053cU, 0x471d5a45U, 0x33744731U, 0xb7d166b5U,
+	0x5d83de5fU, 0x65b5d067U, 0xd6e93fd4U, 0x1f534c1dU,
+	0x71a4d573U, 0xfd0bf6ffU, 0x30b48432U, 0xb69127b4U,
+	0x060d0b04U, 0xf501f4f7U, 0xa3c063a1U, 0xc3b87bc1U,
+	0xbfdb64bdU, 0xbbde65b9U, 0x67355265U, 0x4f17584dU,
+	0x3cbb873eU, 0x66751364U, 0xb411a5b6U, 0x40d89842U,
+	0xe455b1e6U, 0x7dabd67fU, 0xf84eb6faU, 0xf78176f5U,
+	0x4e57194cU, 0x6dbfd26fU, 0x7b2e5579U, 0x0f47480dU,
+	0x76611774U, 0x4a521848U, 0x63305361U, 0x69bad36bU,
+	0x22200220U, 0x0dc7ca0fU, 0xcef739ccU, 0x28aa822aU,
+	0x9c33af9eU, 0x3dfbc63fU, 0xe79572e5U, 0xfc4bb7feU,
+	0xbe9b25bcU, 0xdba67dd9U, 0x4992db4bU, 0x42581a40U,
+	0xe85ab2eaU, 0x9d73ee9fU, 0xd866bedaU, 0xc872bacaU,
+	0x2caf832eU, 0xafcf60adU, 0x54c99d56U, 0x23604321U,
+	0x3a3e0438U, 0x50cc9c52U, 0xd7a97ed5U, 0xf38477f1U,
+	0x9ff36c9dU, 0xec5fb3eeU, 0xcd37facfU, 0x2e2f012cU,
+	0x1dd3ce1fU, 0xb3d467b1U, 0x11dccd13U, 0x18968e1aU,
+	0x6a7a1068U, 0xc932fbcbU, 0xff8b74fdU, 0xb95ee7bbU,
+	0xe2d032e0U, 0x79aed77bU, 0x3f7b443dU, 0x8aa22888U,
+	0xef9f70edU, 0xb154e5b3U, 0x77215675U, 0xf2c436f0U,
+	0x5589dc57U, 0x25e5c027U, 0xeedf31ecU, 0x2b6a4129U,
+	0x206a4ac4U, 0x39340dddU, 0xdca97538U, 0x80e26264U,
+	0x481850acU, 0xfc817d18U, 0x8f2da26bU, 0xa58f2a41U,
+	0x23aa89c7U, 0xa18a2b45U, 0x2ba08bcfU, 0x702e5e94U,
+	0xdb6cb73fU, 0x94f36770U, 0xcb78b32fU, 0x6ff59a8bU,
+	0xd763b433U, 0xe61ff902U, 0x111607f5U, 0xa24ae846U,
+	0xd366b537U, 0x81a22365U, 0x6bf09b8fU, 0x742b5f90U,
+	0xd1e63735U, 0x6ab0da8eU, 0x37bb8cd3U, 0xe35ab907U,
+	0x5e89d7baU, 0x617a1b85U, 0x63fa9987U, 0x1d1904f9U,
+	0x090801edU, 0x72aedc96U, 0x3af4cedeU, 0xa8c0684cU,
+	0x3d310cd9U, 0xc4b77320U, 0x191c05fdU, 0x9276e476U,
+	0xb71bac53U, 0x2ae0caceU, 0x57c394b3U, 0x76abdd92U,
+	0xccbd7128U, 0x441753a0U, 0xc9f8312dU, 0xf20efc16U,
+	0xe890780cU, 0xb59b2e51U, 0x0b8883efU, 0xdde93439U,
+	0x5c0955b8U, 0x5bcc97bfU, 0xd8ac743cU, 0x2d2508c9U,
+	0x79641d9dU, 0x044743e0U, 0x292009cdU, 0x90f66674U,
+	0xae45eb4aU, 0x455712a1U, 0x8727a063U, 0xe1da3b05U,
+	0x66bfd982U, 0x67ff9883U, 0xe5df3a01U, 0x88e8606cU,
+	0xb4db6f50U, 0xb65bed52U, 0x27af88c3U, 0x91b62775U,
+	0x756b1e91U, 0x1ed9c7faU, 0x8b28a36fU, 0x1f9986fbU,
+	0xe09a7a04U, 0x6d751889U, 0x4697d1a2U, 0xd9ec353dU,
+	0xda2cf63eU, 0xd623f532U, 0x7d611c99U, 0xb19e2f55U,
+	0xbe51ef5aU, 0xea10fa0eU, 0x105646f4U, 0x554316b1U,
+	0x98fc647cU, 0xd4a37730U, 0x47d790a3U, 0x5683d5b2U,
+	0xba54ee5eU, 0x22eac8c6U, 0x0d0d00e9U, 0xaf05aa4bU,
+	0x038281e7U, 0xeb50bb0fU, 0x43d291a7U, 0x02c2c0e6U,
+	0xf60bfd12U, 0x1adcc6feU, 0x3c714dd8U, 0xf1ce3f15U,
+	0xef55ba0bU, 0xf34ebd17U, 0x8a68e26eU, 0x9733a473U,
+	0x7fe19e9bU, 0xff41be1bU, 0x9a7ce67eU, 0x657f1a81U,
+	0x286048ccU, 0x7be49f9fU, 0x62bad886U, 0x26efc9c2U,
+	0x050702e1U, 0x347b4fd0U, 0xe75fb803U, 0x32feccd6U,
+	0x4a98d2aeU, 0xa30aa947U, 0x73ee9d97U, 0xe49f7b00U,
+	0xf9c43d1dU, 0x6830588cU, 0x580c54bcU, 0x010203e5U,
+	0xd0a67634U, 0x85a72261U, 0x514617b5U, 0x307e4ed4U,
+	0x7aa4de9eU, 0x643f5b80U, 0x9b3ca77fU, 0xad852849U,
+	0xf5cb3e11U, 0xa4cf6b40U, 0xb0de6e54U, 0x401252a4U,
+	0x9cf96578U, 0xfe01ff1aU, 0x2fa58acbU, 0xfdc13c19U,
+	0xf8847c1cU, 0xc1f23325U, 0x0ecdc3eaU, 0xe9d0390dU,
+	0x151306f1U, 0xab00ab4fU, 0x139685f7U, 0x2ee5cbcaU,
+	0xcdfd3029U, 0x353b0ed1U, 0xb31ead57U, 0xbb14af5fU,
+	0xacc56948U, 0x495811adU, 0xbd912c59U, 0xf48b7f10U,
+	0x0f8d82ebU, 0xa0ca6a44U, 0xf08e7e14U, 0xc372b127U,
+	0x716e1f95U, 0xe21af806U, 0x500656b4U, 0xfa04fe1eU,
+	0x78245c9cU, 0xdf69b63bU, 0xf74bbc13U, 0xd5e33631U,
+	0x4fdd92abU, 0x36fbcdd2U, 0x4292d0a6U, 0xc637f122U,
+	0x2c6549c8U, 0x145347f0U, 0xa70fa843U, 0x6eb5db8aU,
+	0x004242e4U, 0x8ced6168U, 0x415213a5U, 0xc777b023U,
+	0x77eb9c93U, 0x84e76360U, 0xd226f436U, 0xb25eec56U,
+	0xce3df32aU, 0xca38f22eU, 0x16d3c5f2U, 0x3ef1cfdaU,
+	0x4d5d10a9U, 0x179384f3U, 0xc5f73221U, 0x313e0fd5U,
+	0x95b32671U, 0x0c4d41e8U, 0x89a8216dU, 0x8667e162U,
+	0x3fb18edbU, 0x1c5945f8U, 0x0ac8c2eeU, 0x7ea1df9aU,
+	0x078780e3U, 0x3bb48fdfU, 0x12d6c4f6U, 0x185c44fcU,
+	0x53c695b7U, 0x7c215d98U, 0xbf11ae5bU, 0x594c15bdU,
+	0xedd53809U, 0x4c1d51a8U, 0x9673e572U, 0x8dad2069U,
+	0xcf7db22bU, 0xaa40ea4eU, 0x38744cdcU, 0x33be8dd7U,
+	0x99bc257dU, 0xec957908U, 0xa980294dU, 0xb9942d5dU,
+	0x5d4914b9U, 0xde29f73aU, 0x252f0ac1U, 0x5286d4b6U,
+	0x4bd893afU, 0x212a0bc5U, 0xa64fe942U, 0x8262e066U,
+	0xee15fb0aU, 0x9db92479U, 0xbcd16d58U, 0x5fc996bbU,
+	0x6c355988U, 0xc232f026U, 0x603a5a84U, 0x6970198dU,
+	0x1b9c87ffU, 0xb8d46c5cU, 0x8e6de36aU, 0xc8b8702cU,
+	0x9336a577U, 0x084840ecU, 0x4e9dd3aaU, 0xfb44bf1fU,
+	0x9e79e77aU, 0xc0b27224U, 0x06c7c1e2U, 0x8322a167U,
+	0x246f4bc0U, 0x540357b0U, 0x9f39a67bU, 0x5a8cd6beU,
+	0x7b2c57d9U, 0x627210c0U, 0x87ef6825U, 0xdba47f79U,
+	0x135e4db1U, 0xa7c76005U, 0xd46bbf76U, 0xfec9375cU,
+	0x78ec94daU, 0xfacc3658U, 0x70e696d2U, 0x2b684389U,
+	0x802aaa22U, 0xcfb57a6dU, 0x903eae32U, 0x34b38796U,
+	0x8c25a92eU, 0xbd59e41fU, 0x4a501ae8U, 0xf90cf55bU,
+	0x8820a82aU, 0xdae43e78U, 0x30b68692U, 0x2f6d428dU,
+	0x8aa02a28U, 0x31f6c793U, 0x6cfd91ceU, 0xb81ca41aU,
+	0x05cfcaa7U, 0x3a3c0698U, 0x38bc849aU, 0x465f19e4U,
+	0x524e1cf0U, 0x29e8c18bU, 0x61b2d3c3U, 0xf3867551U,
+	0x667711c4U, 0x9ff16e3dU, 0x425a18e0U, 0xc930f96bU,
+	0xec5db14eU, 0x71a6d7d3U, 0x0c8589aeU, 0x2dedc08fU,
+	0x97fb6c35U, 0x1f514ebdU, 0x92be2c30U, 0xa948e10bU,
+	0xb3d66511U, 0xeedd334cU, 0x50ce9ef2U, 0x86af2924U,
+	0x074f48a5U, 0x008a8aa2U, 0x83ea6921U, 0x766315d4U,
+	0x22220080U, 0x5f015efdU, 0x726614d0U, 0xcbb07b69U,
+	0xf503f657U, 0x1e110fbcU, 0xdc61bd7eU, 0xba9c2618U,
+	0x3df9c49fU, 0x3cb9859eU, 0xbe99271cU, 0xd3ae7d71U,
+	0xef9d724dU, 0xed1df04fU, 0x7ce995deU, 0xcaf03a68U,
+	0x2e2d038cU, 0x459fdae7U, 0xd06ebe72U, 0x44df9be6U,
+	0xbbdc6719U, 0x36330594U, 0x1dd1ccbfU, 0x82aa2820U,
+	0x816aeb23U, 0x8d65e82fU, 0x26270184U, 0xead83248U,
+	0xe517f247U, 0xb156e713U, 0x4b105be9U, 0x0e050bacU,
+	0xc3ba7961U, 0x8fe56a2dU, 0x1c918dbeU, 0x0dc5c8afU,
+	0xe112f343U, 0x79acd5dbU, 0x564b1df4U, 0xf443b756U,
+	0x58c49cfaU, 0xb016a612U, 0x18948cbaU, 0x5984ddfbU,
+	0xad4de00fU, 0x419adbe3U, 0x673750c5U, 0xaa882208U,
+	0xb413a716U, 0xa808a00aU, 0xd12eff73U, 0xcc75b96eU,
+	0x24a78386U, 0xa407a306U, 0xc13afb63U, 0x3e39079cU,
+	0x732655d1U, 0x20a28282U, 0x39fcc59bU, 0x7da9d4dfU,
+	0x5e411ffcU, 0x6f3d52cdU, 0xbc19a51eU, 0x69b8d1cbU,
+	0x11decfb3U, 0xf84cb45aU, 0x28a8808aU, 0xbfd9661dU,
+	0xa2822000U, 0x33764591U, 0x034a49a1U, 0x5a441ef8U,
+	0x8be06b29U, 0xdee13f7cU, 0x0a000aa8U, 0x6b3853c9U,
+	0x21e2c383U, 0x3f79469dU, 0xc07aba62U, 0xf6c33554U,
+	0xae8d230cU, 0xff89765dU, 0xeb987349U, 0x1b544fb9U,
+	0xc7bf7865U, 0xa547e207U, 0x74e397d6U, 0xa6872104U,
+	0xa3c26101U, 0x9ab42e38U, 0x558bdef7U, 0xb2962410U,
+	0x4e551becU, 0xf046b652U, 0x48d098eaU, 0x75a3d6d7U,
+	0x96bb2d34U, 0x6e7d13ccU, 0xe858b04aU, 0xe052b242U,
+	0xf7837455U, 0x121e0cb0U, 0xe6d73144U, 0xafcd620dU,
+	0x54cb9ff6U, 0xfb8c7759U, 0xabc86309U, 0x9834ac3aU,
+	0x2a280288U, 0xb95ce51bU, 0x0b404ba9U, 0xa142e303U,
+	0x23624181U, 0x842fab26U, 0xac0da10eU, 0x8ea52b2cU,
+	0x149b8fb6U, 0x6dbdd0cfU, 0x19d4cdbbU, 0x9d71ec3fU,
+	0x772354d5U, 0x4f155aedU, 0xfc49b55eU, 0x35f3c697U,
+	0x5b045ff9U, 0xd7ab7c75U, 0x1a140eb8U, 0x9c31ad3eU,
+	0x2cad818eU, 0xdfa17e7dU, 0x8960e92bU, 0xe918f14bU,
+	0x957bee37U, 0x917eef33U, 0x4d95d8efU, 0x65b7d2c7U,
+	0x161b0db4U, 0x4cd599eeU, 0x9eb12f3cU, 0x6a7812c8U,
+	0xcef53b6cU, 0x570b5cf5U, 0xd2ee3c70U, 0xdd21fc7fU,
+	0x64f793c6U, 0x471f58e5U, 0x518edff3U, 0x25e7c287U,
+	0x5cc19dfeU, 0x60f292c2U, 0x4990d9ebU, 0x431a59e1U,
+	0x088088aaU, 0x27674085U, 0xe457b346U, 0x020a08a0U,
+	0xb6932514U, 0x175b4cb5U, 0xcd35f86fU, 0xd6eb3d74U,
+	0x943baf36U, 0xf106f753U, 0x633251c1U, 0x68f890caU,
+	0xc2fa3860U, 0xb7d36415U, 0xf2c63450U, 0xe2d23040U,
+	0x060f09a4U, 0x856fea27U, 0x7e6917dcU, 0x09c0c9abU,
+	0x109e8eb2U, 0x7a6c16d8U, 0xfd09f45fU, 0xd924fd7bU,
+	0xb553e617U, 0xc6ff3964U, 0xe7977045U, 0x048f8ba6U,
+	0x37734495U, 0x9974ed3bU, 0x3b7c4799U, 0x32360490U,
+	0x40da9ae2U, 0xe3927141U, 0xd52bfe77U, 0x93fe6d31U,
+	0xc870b86aU, 0x530e5df1U, 0x15dbceb7U, 0xa002a202U,
+	0xc53ffa67U, 0x9bf46f39U, 0x5d81dcffU, 0xd864bc7aU,
+	0x7f2956ddU, 0x0f454aadU, 0xc47fbb66U, 0x01cacba3U,
+	0x8f49c648U, 0x96178151U, 0x738af9b4U, 0x2fc1eee8U,
+	0xe73bdc20U, 0x53a2f194U, 0x200e2ee7U, 0x0aaca6cdU,
+	0x8c89054bU, 0x0ea9a7c9U, 0x84830743U, 0xdf0dd218U,
+	0x744f3bb3U, 0x3bd0ebfcU, 0x645b3fa3U, 0xc0d61607U,
+	0x784038bfU, 0x493c758eU, 0xbe358b79U, 0x0d6964caU,
+	0x7c4539bbU, 0x2e81afe9U, 0xc4d31703U, 0xdb08d31cU,
+	0x7ec5bbb9U, 0xc5935602U, 0x9898005fU, 0x4c79358bU,
+	0xf1aa5b36U, 0xce599709U, 0xccd9150bU, 0xb23a8875U,
+	0xa62b8d61U, 0xdd8d501aU, 0x95d74252U, 0x07e3e4c0U,
+	0x92128055U, 0x6b94ffacU, 0xb63f8971U, 0x3d5568faU,
+	0x183820dfU, 0x85c34642U, 0xf8e0183fU, 0xd988511eU,
+	0x639efda4U, 0xeb34df2cU, 0x66dbbda1U, 0x5d2d709aU,
+	0x47b3f480U, 0x1ab8a2ddU, 0xa4ab0f63U, 0x72cab8b5U,
+	0xf32ad934U, 0xf4ef1b33U, 0x778ff8b0U, 0x82068445U,
+	0xd6479111U, 0xab64cf6cU, 0x86038541U, 0x3fd5eaf8U,
+	0x016667c6U, 0xea749e2dU, 0x28042cefU, 0x4ef9b789U,
+	0xc99c550eU, 0xc8dc140fU, 0x4afcb68dU, 0x27cbece0U,
+	0x1bf8e3dcU, 0x197861deU, 0x888c044fU, 0x3e95abf9U,
+	0xda48921dU, 0xb1fa4b76U, 0x240b2fe3U, 0xb0ba0a77U,
+	0x4fb9f688U, 0xc2569405U, 0xe9b45d2eU, 0x76cfb9b1U,
+	0x750f7ab2U, 0x790079beU, 0xd2429015U, 0x1ebda3d9U,
+	0x117263d6U, 0x45337682U, 0xbf75ca78U, 0xfa609a3dU,
+	0x37dfe8f0U, 0x7b80fbbcU, 0xe8f41c2fU, 0xf9a0593eU,
+	0x157762d2U, 0x8dc9444aU, 0xa22e8c65U, 0x002626c7U,
+	0xaca10d6bU, 0x44733783U, 0xecf11d2bU, 0xade14c6aU,
+	0x5928719eU, 0xb5ff4a72U, 0x9352c154U, 0x5eedb399U,
+	0x40763687U, 0x5c6d319bU, 0x254b6ee2U, 0x381028ffU,
+	0xd0c21217U, 0x50623297U, 0x355f6af2U, 0xca5c960dU,
+	0x8743c440U, 0xd4c71313U, 0xcd99540aU, 0x89cc454eU,
+	0xaa248e6dU, 0x9b58c35cU, 0x487c348fU, 0x9ddd405aU,
+	0xe5bb5e22U, 0x0c2925cbU, 0xdccd111bU, 0x4bbcf78cU,
+	0x56e7b191U, 0xc713d400U, 0xf72fd830U, 0xae218f69U,
+	0x7f85fab8U, 0x2a84aeedU, 0xfe659b39U, 0x9f5dc258U,
+	0xd5875212U, 0xcb1cd70cU, 0x341f2bf3U, 0x02a6a4c5U,
+	0x5ae8b29dU, 0x0bece7ccU, 0x1ffde2d8U, 0xef31de28U,
+	0x33dae9f4U, 0x51227396U, 0x80860647U, 0x52e2b095U,
+	0x57a7f090U, 0x6ed1bfa9U, 0xa1ee4f66U, 0x46f3b581U,
+	0xba308a7dU, 0x042327c3U, 0xbcb5097bU, 0x81c64746U,
+	0x62debca5U, 0x9a18825dU, 0x1c3d21dbU, 0x143723d3U,
+	0x03e6e5c4U, 0xe67b9d21U, 0x12b2a0d5U, 0x5ba8f39cU,
+	0xa0ae0e67U, 0x0fe9e6c8U, 0x5fadf298U, 0x6c513dabU,
+	0xde4d9319U, 0x4d39748aU, 0xff25da38U, 0x55277292U,
+	0xd707d010U, 0x704a3ab7U, 0x5868309fU, 0x7ac0babdU,
+	0xe0fe1e27U, 0x99d8415eU, 0xedb15c2aU, 0x69147daeU,
+	0x8346c544U, 0xbb70cb7cU, 0x082c24cfU, 0xc1965706U,
+	0xaf61ce68U, 0x23ceede4U, 0xee719f29U, 0x68543cafU,
+	0xd8c8101fU, 0x2bc4efecU, 0x7d0578baU, 0x1d7d60daU,
+	0x611e7fa6U, 0x651b7ea2U, 0xb9f0497eU, 0x91d24356U,
+	0xe27e9c25U, 0xb8b0087fU, 0x6ad4beadU, 0x9e1d8359U,
+	0x3a90aafdU, 0xa36ecd64U, 0x268bade1U, 0x29446deeU,
+	0x90920257U, 0xb37ac974U, 0xa5eb4e62U, 0xd1825316U,
+	0xa8a40c6fU, 0x94970353U, 0xbdf5487aU, 0xb77fc870U,
+	0xfce5193bU, 0xd302d114U, 0x103222d7U, 0xf66f9931U,
+	0x42f6b485U, 0xe33edd24U, 0x395069feU, 0x228eace5U,
+	0x605e3ea7U, 0x056366c2U, 0x9757c050U, 0x9c9d015bU,
+	0x369fa9f1U, 0x43b6f584U, 0x06a3a5c1U, 0x16b7a1d1U,
+	0xf26a9835U, 0x710a7bb6U, 0x8a0c864dU, 0xfda5583aU,
+	0xe4fb1f23U, 0x8e098749U, 0x096c65ceU, 0x2d416ceaU,
+	0x41367786U, 0x329aa8f5U, 0x13f2e1d4U, 0xf0ea1a37U,
+	0xc316d504U, 0x6d117caaU, 0xcf19d608U, 0xc6539501U,
+	0xb4bf0b73U, 0x17f7e0d0U, 0x214e6fe6U, 0x679bfca0U,
+	0x3c1529fbU, 0xa76bcc60U, 0xe1be5f26U, 0x54673393U,
+	0x315a6bf6U, 0x6f91fea8U, 0xa9e44d6eU, 0x2c012debU,
+	0x8b4cc74cU, 0xfb20db3cU, 0x301a2af7U, 0xf5af5a32U,
+	0xb345f678U, 0xaa1bb161U, 0x4f86c984U, 0x13cdded8U,
+	0xdb37ec10U, 0x6faec1a4U, 0x1c021ed7U, 0x36a096fdU,
+	0xb085357bU, 0x32a597f9U, 0xb88f3773U, 0xe301e228U,
+	0x48430b83U, 0x07dcdbccU, 0x58570f93U, 0xfcda2637U,
+	0x444c088fU, 0x753045beU, 0x8239bb49U, 0x316554faU,
+	0x4049098bU, 0x128d9fd9U, 0xf8df2733U, 0xe704e32cU,
+	0x42c98b89U, 0xf99f6632U, 0xa494306fU, 0x707505bbU,
+	0xcda66b06U, 0xf255a739U, 0xf0d5253bU, 0x8e36b845U,
+	0x9a27bd51U, 0xe181602aU, 0xa9db7262U, 0x3befd4f0U,
+	0xae1eb065U, 0x5798cf9cU, 0x8a33b941U, 0x015958caU,
+	0x243410efU, 0xb9cf7672U, 0xc4ec280fU, 0xe584612eU,
+	0x5f92cd94U, 0xd738ef1cU, 0x5ad78d91U, 0x612140aaU,
+	0x7bbfc4b0U, 0x26b492edU, 0x98a73f53U, 0x4ec68885U,
+	0xcf26e904U, 0xc8e32b03U, 0x4b83c880U, 0xbe0ab475U,
+	0xea4ba121U, 0x9768ff5cU, 0xba0fb571U, 0x03d9dac8U,
+	0x3d6a57f6U, 0xd678ae1dU, 0x14081cdfU, 0x72f587b9U,
+	0xf590653eU, 0xf4d0243fU, 0x76f086bdU, 0x1bc7dcd0U,
+	0x27f4d3ecU, 0x257451eeU, 0xb480347fU, 0x02999bc9U,
+	0xe644a22dU, 0x8df67b46U, 0x18071fd3U, 0x8cb63a47U,
+	0x73b5c6b8U, 0xfe5aa435U, 0xd5b86d1eU, 0x4ac38981U,
+	0x49034a82U, 0x450c498eU, 0xee4ea025U, 0x22b193e9U,
+	0x2d7e53e6U, 0x793f46b2U, 0x8379fa48U, 0xc66caa0dU,
+	0x0bd3d8c0U, 0x478ccb8cU, 0xd4f82c1fU, 0xc5ac690eU,
+	0x297b52e2U, 0xb1c5747aU, 0x9e22bc55U, 0x3c2a16f7U,
+	0x90ad3d5bU, 0x787f07b3U, 0xd0fd2d1bU, 0x91ed7c5aU,
+	0x652441aeU, 0x89f37a42U, 0xaf5ef164U, 0x62e183a9U,
+	0x7c7a06b7U, 0x606101abU, 0x19475ed2U, 0x041c18cfU,
+	0xecce2227U, 0x6c6e02a7U, 0x09535ac2U, 0xf650a63dU,
+	0xbb4ff470U, 0xe8cb2323U, 0xf195643aU, 0xb5c0757eU,
+	0x9628be5dU, 0xa754f36cU, 0x747004bfU, 0xa1d1706aU,
+	0xd9b76e12U, 0x302515fbU, 0xe0c1212bU, 0x77b0c7bcU,
+	0x6aeb81a1U, 0xfb1fe430U, 0xcb23e800U, 0x922dbf59U,
+	0x4389ca88U, 0x16889eddU, 0xc269ab09U, 0xa351f268U,
+	0xe98b6222U, 0xf710e73cU, 0x08131bc3U, 0x3eaa94f5U,
+	0x66e482adU, 0x37e0d7fcU, 0x23f1d2e8U, 0xd33dee18U,
+	0x0fd6d9c4U, 0x6d2e43a6U, 0xbc8a3677U, 0x6eee80a5U,
+	0x6babc0a0U, 0x52dd8f99U, 0x9de27f56U, 0x7aff85b1U,
+	0x863cba4dU, 0x382f17f3U, 0x80b9394bU, 0xbdca7776U,
+	0x5ed28c95U, 0xa614b26dU, 0x203111ebU, 0x283b13e3U,
+	0x3fead5f4U, 0xda77ad11U, 0x2ebe90e5U, 0x67a4c3acU,
+	0x9ca23e57U, 0x33e5d6f8U, 0x63a1c2a8U, 0x505d0d9bU,
+	0xe241a329U, 0x713544baU, 0xc329ea08U, 0x692b42a2U,
+	0xeb0be020U, 0x4c460a87U, 0x646400afU, 0x46cc8a8dU,
+	0xdcf22e17U, 0xa5d4716eU, 0xd1bd6c1aU, 0x55184d9eU,
+	0xbf4af574U, 0x877cfb4cU, 0x342014ffU, 0xfd9a6736U,
+	0x936dfe58U, 0x1fc2ddd4U, 0xd27daf19U, 0x54580c9fU,
+	0xe4c4202fU, 0x17c8dfdcU, 0x4109488aU, 0x217150eaU,
+	0x5d124f96U, 0x59174e92U, 0x85fc794eU, 0xadde7366U,
+	0xde72ac15U, 0x84bc384fU, 0x56d88e9dU, 0xa211b369U,
+	0x069c9acdU, 0x9f62fd54U, 0x1a879dd1U, 0x15485ddeU,
+	0xac9e3267U, 0x8f76f944U, 0x99e77e52U, 0xed8e6326U,
+	0x94a83c5fU, 0xa89b3363U, 0x81f9784aU, 0x8b73f840U,
+	0xc0e9290bU, 0xef0ee124U, 0x2c3e12e7U, 0xca63a901U,
+	0x7efa84b5U, 0xdf32ed14U, 0x055c59ceU, 0x1e829cd5U,
+	0x5c520e97U, 0x396f56f2U, 0xab5bf060U, 0xa091316bU,
+	0x0a9399c1U, 0x7fbac5b4U, 0x3aaf95f1U, 0x2abb91e1U,
+	0xce66a805U, 0x4d064b86U, 0xb600b67dU, 0xc1a9680aU,
+	0xd8f72f13U, 0xb205b779U, 0x356055feU, 0x114d5cdaU,
+	0x7d3a47b6U, 0x0e9698c5U, 0x2ffed1e4U, 0xcce62a07U,
+	0xff1ae534U, 0x511d4c9aU, 0xf315e638U, 0xfa5fa531U,
+	0x88b33b43U, 0x2bfbd0e0U, 0x1d425fd6U, 0x5b97cc90U,
+	0x001919cbU, 0x9b67fc50U, 0xddb26f16U, 0x686b03a3U,
+	0x0d565bc6U, 0x539dce98U, 0x95e87d5eU, 0x100d1ddbU,
+	0xb740f77cU, 0xc72ceb0cU, 0x0c161ac7U, 0xc9a36a02U,
+	0xbd12af21U, 0xa44ce838U, 0x41d190ddU, 0x1d9a8781U,
+	0xd560b549U, 0x61f998fdU, 0x1255478eU, 0x38f7cfa4U,
+	0xbed26c22U, 0x3cf2cea0U, 0xb6d86e2aU, 0xed56bb71U,
+	0x461452daU, 0x098b8295U, 0x560056caU, 0xf28d7f6eU,
+	0x4a1b51d6U, 0x7b671ce7U, 0x8c6ee210U, 0x3f320da3U,
+	0x4e1e50d2U, 0x1cdac680U, 0xf6887e6aU, 0xe953ba75U,
+	0x4c9ed2d0U, 0xf7c83f6bU, 0xaac36936U, 0x7e225ce2U,
+	0xc3f1325fU, 0xfc02fe60U, 0xfe827c62U, 0x8061e11cU,
+	0x9470e408U, 0xefd63973U, 0xa78c2b3bU, 0x35b88da9U,
+	0xa049e93cU, 0x59cf96c5U, 0x8464e018U, 0x0f0e0193U,
+	0x2a6349b6U, 0xb7982f2bU, 0xcabb7156U, 0xebd33877U,
+	0x51c594cdU, 0xd96fb645U, 0x5480d4c8U, 0x6f7619f3U,
+	0x75e89de9U, 0x28e3cbb4U, 0x96f0660aU, 0x4091d1dcU,
+	0xc171b05dU, 0xc6b4725aU, 0x45d491d9U, 0xb05ded2cU,
+	0xe41cf878U, 0x993fa605U, 0xb458ec28U, 0x0d8e8391U,
+	0x333d0eafU, 0xd82ff744U, 0x1a5f4586U, 0x7ca2dee0U,
+	0xfbc73c67U, 0xfa877d66U, 0x78a7dfe4U, 0x15908589U,
+	0x29a38ab5U, 0x2b2308b7U, 0xbad76d26U, 0x0ccec290U,
+	0xe813fb74U, 0x83a1221fU, 0x1650468aU, 0x82e1631eU,
+	0x7de29fe1U, 0xf00dfd6cU, 0xdbef3447U, 0x4494d0d8U,
+	0x475413dbU, 0x4b5b10d7U, 0xe019f97cU, 0x2ce6cab0U,
+	0x23290abfU, 0x77681febU, 0x8d2ea311U, 0xc83bf354U,
+	0x05848199U, 0x49db92d5U, 0xdaaf7546U, 0xcbfb3057U,
+	0x272c0bbbU, 0xbf922d23U, 0x9075e50cU, 0x327d4faeU,
+	0x9efa6402U, 0x76285eeaU, 0xdeaa7442U, 0x9fba2503U,
+	0x6b7318f7U, 0x87a4231bU, 0xa109a83dU, 0x6cb6daf0U,
+	0x722d5feeU, 0x6e3658f2U, 0x1710078bU, 0x0a4b4196U,
+	0xe2997b7eU, 0x62395bfeU, 0x0704039bU, 0xf807ff64U,
+	0xb518ad29U, 0xe69c7a7aU, 0xffc23d63U, 0xbb972c27U,
+	0x987fe704U, 0xa903aa35U, 0x7a275de6U, 0xaf862933U,
+	0xd7e0374bU, 0x3e724ca2U, 0xee967872U, 0x79e79ee5U,
+	0x64bcd8f8U, 0xf548bd69U, 0xc574b159U, 0x9c7ae600U,
+	0x4dde93d1U, 0x18dfc784U, 0xcc3ef250U, 0xad06ab31U,
+	0xe7dc3b7bU, 0xf947be65U, 0x0644429aU, 0x30fdcdacU,
+	0x68b3dbf4U, 0x39b78ea5U, 0x2da68bb1U, 0xdd6ab741U,
+	0x0181809dU, 0x63791affU, 0xb2dd6f2eU, 0x60b9d9fcU,
+	0x65fc99f9U, 0x5c8ad6c0U, 0x93b5260fU, 0x74a8dce8U,
+	0x886be314U, 0x36784eaaU, 0x8eee6012U, 0xb39d2e2fU,
+	0x5085d5ccU, 0xa843eb34U, 0x2e6648b2U, 0x266c4abaU,
+	0x31bd8cadU, 0xd420f448U, 0x20e9c9bcU, 0x69f39af5U,
+	0x92f5670eU, 0x3db28fa1U, 0x6df69bf1U, 0x5e0a54c2U,
+	0xec16fa70U, 0x7f621de3U, 0xcd7eb351U, 0x677c1bfbU,
+	0xe55cb979U, 0x421153deU, 0x6a3359f6U, 0x489bd3d4U,
+	0xd2a5774eU, 0xab832837U, 0xdfea3543U, 0x5b4f14c7U,
+	0xb11dac2dU, 0x892ba215U, 0x3a774da6U, 0xf3cd3e6fU,
+	0x9d3aa701U, 0x1195848dU, 0xdc2af640U, 0x5a0f55c6U,
+	0xea937976U, 0x199f8685U, 0x4f5e11d3U, 0x2f2609b3U,
+	0x534516cfU, 0x574017cbU, 0x8bab2017U, 0xa3892a3fU,
+	0xd025f54cU, 0x8aeb6116U, 0x588fd7c4U, 0xac46ea30U,
+	0x08cbc394U, 0x9135a40dU, 0x14d0c488U, 0x1b1f0487U,
+	0xa2c96b3eU, 0x8121a01dU, 0x97b0270bU, 0xe3d93a7fU,
+	0x9aff6506U, 0xa6cc6a3aU, 0x8fae2113U, 0x8524a119U,
+	0xcebe7052U, 0xe159b87dU, 0x22694bbeU, 0xc434f058U,
+	0x70adddecU, 0xd165b44dU, 0x0b0b0097U, 0x10d5c58cU,
+	0x520557ceU, 0x37380fabU, 0xa50ca939U, 0xaec66832U,
+	0x04c4c098U, 0x71ed9cedU, 0x34f8cca8U, 0x24ecc8b8U,
+	0xc031f15cU, 0x435112dfU, 0xb857ef24U, 0xcffe3153U,
+	0xd6a0764aU, 0xbc52ee20U, 0x3b370ca7U, 0x1f1a0583U,
+	0x736d1eefU, 0x00c1c19cU, 0x21a988bdU, 0xc2b1735eU,
+	0xf14dbc6dU, 0x5f4a15c3U, 0xfd42bf61U, 0xf408fc68U,
+	0x86e4621aU, 0x25ac89b9U, 0x1315068fU, 0x55c095c9U,
+	0x0e4e4092U, 0x9530a509U, 0xd3e5364fU, 0x663c5afaU,
+	0x0301029fU, 0x5dca97c1U, 0x9bbf2407U, 0x1e5a4482U,
+	0xb917ae25U, 0xc97bb255U, 0x0241439eU, 0xc7f4335bU,
+	0x19677ef0U, 0x003939e9U, 0xe5a4410cU, 0xb9ef5650U,
+	0x71156498U, 0xc58c492cU, 0xb620965fU, 0x9c821e75U,
+	0x1aa7bdf3U, 0x98871f71U, 0x12adbffbU, 0x49236aa0U,
+	0xe261830bU, 0xadfe5344U, 0xf275871bU, 0x56f8aebfU,
+	0xee6e8007U, 0xdf12cd36U, 0x281b33c1U, 0x9b47dc72U,
+	0xea6b8103U, 0xb8af1751U, 0x52fdafbbU, 0x4d266ba4U,
+	0xe8eb0301U, 0x53bdeebaU, 0x0eb6b8e7U, 0xda578d33U,
+	0x6784e38eU, 0x58772fb1U, 0x5af7adb3U, 0x241430cdU,
+	0x300535d9U, 0x4ba3e8a2U, 0x03f9faeaU, 0x91cd5c78U,
+	0x043c38edU, 0xfdba4714U, 0x201131c9U, 0xab7bd042U,
+	0x8e169867U, 0x13edfefaU, 0x6ecea087U, 0x4fa6e9a6U,
+	0xf5b0451cU, 0x7d1a6794U, 0xf0f50519U, 0xcb03c822U,
+	0xd19d4c38U, 0x8c961a65U, 0x3285b7dbU, 0xe4e4000dU,
+	0x6504618cU, 0x62c1a38bU, 0xe1a14008U, 0x14283cfdU,
+	0x406929a9U, 0x3d4a77d4U, 0x102d3df9U, 0xa9fb5240U,
+	0x9748df7eU, 0x7c5a2695U, 0xbe2a9457U, 0xd8d70f31U,
+	0x5fb2edb6U, 0x5ef2acb7U, 0xdcd20e35U, 0xb1e55458U,
+	0x8dd65b64U, 0x8f56d966U, 0x1ea2bcf7U, 0xa8bb1341U,
+	0x4c662aa5U, 0x27d4f3ceU, 0xb225975bU, 0x2694b2cfU,
+	0xd9974e30U, 0x54782cbdU, 0x7f9ae596U, 0xe0e10109U,
+	0xe321c20aU, 0xef2ec106U, 0x446c28adU, 0x88931b61U,
+	0x875cdb6eU, 0xd31dce3aU, 0x295b72c0U, 0x6c4e2285U,
+	0xa1f15048U, 0xedae4304U, 0x7edaa497U, 0x6f8ee186U,
+	0x8359da6aU, 0x1be7fcf2U, 0x340034ddU, 0x96089e7fU,
+	0x3a8fb5d3U, 0xd25d8f3bU, 0x7adfa593U, 0x3bcff4d2U,
+	0xcf06c926U, 0x23d1f2caU, 0x057c79ecU, 0xc8c30b21U,
+	0xd6588e3fU, 0xca438923U, 0xb365d65aU, 0xae3e9047U,
+	0x46ecaaafU, 0xc64c8a2fU, 0xa371d24aU, 0x5c722eb5U,
+	0x116d7cf8U, 0x42e9ababU, 0x5bb7ecb2U, 0x1fe2fdf6U,
+	0x3c0a36d5U, 0x0d767be4U, 0xde528c37U, 0x0bf3f8e2U,
+	0x7395e69aU, 0x9a079d73U, 0x4ae3a9a3U, 0xdd924f34U,
+	0xc0c90929U, 0x513d6cb8U, 0x61016088U, 0x380f37d1U,
+	0xe9ab4200U, 0xbcaa1655U, 0x684b2381U, 0x09737ae0U,
+	0x43a9eaaaU, 0x5d326fb4U, 0xa231934bU, 0x94881c7dU,
+	0xccc60a25U, 0x9dc25f74U, 0x89d35a60U, 0x791f6690U,
+	0xa5f4514cU, 0xc70ccb2eU, 0x16a8beffU, 0xc4cc082dU,
+	0xc1894828U, 0xf8ff0711U, 0x37c0f7deU, 0xd0dd0d39U,
+	0x2c1e32c5U, 0x920d9f7bU, 0x2a9bb1c3U, 0x17e8fffeU,
+	0xf4f0041dU, 0x0c363ae5U, 0x8a139963U, 0x82199b6bU,
+	0x95c85d7cU, 0x70552599U, 0x849c186dU, 0xcd864b24U,
+	0x3680b6dfU, 0x99c75e70U, 0xc9834a20U, 0xfa7f8513U,
+	0x48632ba1U, 0xdb17cc32U, 0x690b6280U, 0xc309ca2aU,
+	0x412968a8U, 0xe664820fU, 0xce468827U, 0xecee0205U,
+	0x76d0a69fU, 0x0ff6f9e6U, 0x7b9fe492U, 0xff3ac516U,
+	0x15687dfcU, 0x2d5e73c4U, 0x9e029c77U, 0x57b8efbeU,
+	0x394f76d0U, 0xb5e0555cU, 0x785f2791U, 0xfe7a8417U,
+	0x4ee6a8a7U, 0xbdea5754U, 0xeb2bc002U, 0x8b53d862U,
+	0xf730c71eU, 0xf335c61aU, 0x2fdef1c6U, 0x07fcfbeeU,
+	0x7450249dU, 0x2e9eb0c7U, 0xfcfa0615U, 0x08333be1U,
+	0xacbe1245U, 0x354075dcU, 0xb0a51559U, 0xbf6ad556U,
+	0x06bcbaefU, 0x255471ccU, 0x33c5f6daU, 0x47acebaeU,
+	0x3e8ab4d7U, 0x02b9bbebU, 0x2bdbf0c2U, 0x215170c8U,
+	0x6acba183U, 0x452c69acU, 0x861c9a6fU, 0x60412189U,
+	0xd4d80c3dU, 0x7510659cU, 0xaf7ed146U, 0xb4a0145dU,
+	0xf670861fU, 0x934dde7aU, 0x017978e8U, 0x0ab3b9e3U,
+	0xa0b11149U, 0xd5984d3cU, 0x908d1d79U, 0x80991969U,
+	0x6444208dU, 0xe724c30eU, 0x1c223ef5U, 0x6b8be082U,
+	0x72d5a79bU, 0x18273ff1U, 0x9f42dd76U, 0xbb6fd452U,
+	0xd718cf3eU, 0xa4b4104dU, 0x85dc596cU, 0x66c4a28fU,
+	0x55386dbcU, 0xfb3fc412U, 0x59376eb0U, 0x507d2db9U,
+	0x2291b3cbU, 0x81d95868U, 0xb760d75eU, 0xf1b54418U,
+	0xaa3b9143U, 0x314574d8U, 0x7790e79eU, 0xc2498b2bU,
+	0xa774d34eU, 0xf9bf4610U, 0x3fcaf5d6U, 0xba2f9553U,
+	0x1d627ff4U, 0x6d0e6384U, 0xa634924fU, 0x6381e28aU,
+	0x18332ba5U, 0x016d6cbcU, 0xe4f01459U, 0xb8bb0305U,
+	0x704131cdU, 0xc4d81c79U, 0xb774c30aU, 0x9dd64b20U,
+	0x1bf3e8a6U, 0x99d34a24U, 0x13f9eaaeU, 0x48773ff5U,
+	0xe335d65eU, 0xacaa0611U, 0xf321d24eU, 0x57acfbeaU,
+	0xef3ad552U, 0xde469863U, 0x294f6694U, 0x9a138927U,
+	0xeb3fd456U, 0xb9fb4204U, 0x53a9faeeU, 0x4c723ef1U,
+	0xe9bf5654U, 0x52e9bbefU, 0x0fe2edb2U, 0xdb03d866U,
+	0x66d0b6dbU, 0x59237ae4U, 0x5ba3f8e6U, 0x25406598U,
+	0x3151608cU, 0x4af7bdf7U, 0x02adafbfU, 0x9099092dU,
+	0x05686db8U, 0xfcee1241U, 0x2145649cU, 0xaa2f8517U,
+	0x8f42cd32U, 0x12b9abafU, 0x6f9af5d2U, 0x4ef2bcf3U,
+	0xf4e41049U, 0x7c4e32c1U, 0xf1a1504cU, 0xca579d77U,
+	0xd0c9196dU, 0x8dc24f30U, 0x33d1e28eU, 0xe5b05558U,
+	0x645034d9U, 0x6395f6deU, 0xe0f5155dU, 0x157c69a8U,
+	0x413d7cfcU, 0x3c1e2281U, 0x117968acU, 0xa8af0715U,
+	0x961c8a2bU, 0x7d0e73c0U, 0xbf7ec102U, 0xd9835a64U,
+	0x5ee6b8e3U, 0x5fa6f9e2U, 0xdd865b60U, 0xb0b1010dU,
+	0x8c820e31U, 0x8e028c33U, 0x1ff6e9a2U, 0xa9ef4614U,
+	0x4d327ff0U, 0x2680a69bU, 0xb371c20eU, 0x27c0e79aU,
+	0xd8c31b65U, 0x552c79e8U, 0x7eceb0c3U, 0xe1b5545cU,
+	0xe275975fU, 0xee7a9453U, 0x45387df8U, 0x89c74e34U,
+	0x86088e3bU, 0xd2499b6fU, 0x280f2795U, 0x6d1a77d0U,
+	0xa0a5051dU, 0xecfa1651U, 0x7f8ef1c2U, 0x6edab4d3U,
+	0x820d8f3fU, 0x1ab3a9a7U, 0x35546188U, 0x975ccb2aU,
+	0x3bdbe086U, 0xd309da6eU, 0x7b8bf0c6U, 0x3a9ba187U,
+	0xce529c73U, 0x2285a79fU, 0x04282cb9U, 0xc9975e74U,
+	0xd70cdb6aU, 0xcb17dc76U, 0xb231830fU, 0xaf6ac512U,
+	0x47b8fffaU, 0xc718df7aU, 0xa225871fU, 0x5d267be0U,
+	0x103929adU, 0x43bdfefeU, 0x5ae3b9e7U, 0x1eb6a8a3U,
+	0x3d5e6380U, 0x0c222eb1U, 0xdf06d962U, 0x0aa7adb7U,
+	0x72c1b3cfU, 0x9b53c826U, 0x4bb7fcf6U, 0xdcc61a61U,
+	0xc19d5c7cU, 0x506939edU, 0x605535ddU, 0x395b6284U,
+	0xe8ff1755U, 0xbdfe4300U, 0x691f76d4U, 0x08272fb5U,
+	0x42fdbfffU, 0x5c663ae1U, 0xa365c61eU, 0x95dc4928U,
+	0xcd925f70U, 0x9c960a21U, 0x88870f35U, 0x784b33c5U,
+	0xa4a00419U, 0xc6589e7bU, 0x17fcebaaU, 0xc5985d78U,
+	0xc0dd1d7dU, 0xf9ab5244U, 0x3694a28bU, 0xd189586cU,
+	0x2d4a6790U, 0x9359ca2eU, 0x2bcfe496U, 0x16bcaaabU,
+	0xf5a45148U, 0x0d626fb0U, 0x8b47cc36U, 0x834dce3eU,
+	0x949c0829U, 0x710170ccU, 0x85c84d38U, 0xccd21e71U,
+	0x37d4e38aU, 0x98930b25U, 0xc8d71f75U, 0xfb2bd046U,
+	0x49377ef4U, 0xda439967U, 0x685f37d5U, 0xc25d9f7fU,
+	0x407d3dfdU, 0xe730d75aU, 0xcf12dd72U, 0xedba5750U,
+	0x7784f3caU, 0x0ea2acb3U, 0x7acbb1c7U, 0xfe6e9043U,
+	0x143c28a9U, 0x2c0a2691U, 0x9f56c922U, 0x56ecbaebU,
+	0x381b2385U, 0xb4b40009U, 0x790b72c4U, 0xff2ed142U,
+	0x4fb2fdf2U, 0xbcbe0201U, 0xea7f9557U, 0x8a078d37U,
+	0xf664924bU, 0xf261934fU, 0x2e8aa493U, 0x06a8aebbU,
+	0x750471c8U, 0x2fcae592U, 0xfdae5340U, 0x09676eb4U,
+	0xadea4710U, 0x34142089U, 0xb1f1400cU, 0xbe3e8003U,
+	0x07e8efbaU, 0x24002499U, 0x3291a38fU, 0x46f8befbU,
+	0x3fdee182U, 0x03edeebeU, 0x2a8fa597U, 0x2005259dU,
+	0x6b9ff4d6U, 0x44783cf9U, 0x8748cf3aU, 0x611574dcU,
+	0xd58c5968U, 0x744430c9U, 0xae2a8413U, 0xb5f44108U,
+	0xf724d34aU, 0x92198b2fU, 0x002d2dbdU, 0x0be7ecb6U,
+	0xa1e5441cU, 0xd4cc1869U, 0x91d9482cU, 0x81cd4c3cU,
+	0x651075d8U, 0xe670965bU, 0x1d766ba0U, 0x6adfb5d7U,
+	0x7381f2ceU, 0x19736aa4U, 0x9e168823U, 0xba3b8107U,
+	0xd64c9a6bU, 0xa5e04518U, 0x84880c39U, 0x6790f7daU,
+	0x546c38e9U, 0xfa6b9147U, 0x58633be5U, 0x512978ecU,
+	0x23c5e69eU, 0x808d0d3dU, 0xb634820bU, 0xf0e1114dU,
+	0xab6fc416U, 0x3011218dU, 0x76c4b2cbU, 0xc31dde7eU,
+	0xa620861bU, 0xf8eb1345U, 0x3e9ea083U, 0xbb7bc006U,
+	0x1c362aa1U, 0x6c5a36d1U, 0xa760c71aU, 0x62d5b7dfU,
+	0xf906ff71U, 0xe058b868U, 0x05c5c08dU, 0x598ed7d1U,
+	0x9174e519U, 0x25edc8adU, 0x564117deU, 0x7ce39ff4U,
+	0xfac63c72U, 0x78e69ef0U, 0xf2cc3e7aU, 0xa942eb21U,
+	0x0200028aU, 0x4d9fd2c5U, 0x1214069aU, 0xb6992f3eU,
+	0x0e0f0186U, 0x3f734cb7U, 0xc87ab240U, 0x7b265df3U,
+	0x0a0a0082U, 0x58ce96d0U, 0xb29c2e3aU, 0xad47ea25U,
+	0x088a8280U, 0xb3dc6f3bU, 0xeed73966U, 0x3a360cb2U,
+	0x87e5620fU, 0xb816ae30U, 0xba962c32U, 0xc475b14cU,
+	0xd064b458U, 0xabc26923U, 0xe3987b6bU, 0x71acddf9U,
+	0xe45db96cU, 0x1ddbc695U, 0xc070b048U, 0x4b1a51c3U,
+	0x6e7719e6U, 0xf38c7f7bU, 0x8eaf2106U, 0xafc76827U,
+	0x15d1c49dU, 0x9d7be615U, 0x10948498U, 0x2b6249a3U,
+	0x31fccdb9U, 0x6cf79be4U, 0xd2e4365aU, 0x0485818cU,
+	0x8565e00dU, 0x82a0220aU, 0x01c0c189U, 0xf449bd7cU,
+	0xa008a828U, 0xdd2bf655U, 0xf04cbc78U, 0x499ad3c1U,
+	0x77295effU, 0x9c3ba714U, 0x5e4b15d6U, 0x38b68eb0U,
+	0xbfd36c37U, 0xbe932d36U, 0x3cb38fb4U, 0x5184d5d9U,
+	0x6db7dae5U, 0x6f3758e7U, 0xfec33d76U, 0x48da92c0U,
+	0xac07ab24U, 0xc7b5724fU, 0x524416daU, 0xc6f5334eU,
+	0x39f6cfb1U, 0xb419ad3cU, 0x9ffb6417U, 0x00808088U,
+	0x0340438bU, 0x0f4f4087U, 0xa40da92cU, 0x68f29ae0U,
+	0x673d5aefU, 0x337c4fbbU, 0xc93af341U, 0x8c2fa304U,
+	0x4190d1c9U, 0x0dcfc285U, 0x9ebb2516U, 0x8fef6007U,
+	0x63385bebU, 0xfb867d73U, 0xd461b55cU, 0x76691ffeU,
+	0xdaee3452U, 0x323c0ebaU, 0x9abe2412U, 0xdbae7553U,
+	0x2f6748a7U, 0xc3b0734bU, 0xe51df86dU, 0x28a28aa0U,
+	0x36390fbeU, 0x2a2208a2U, 0x530457dbU, 0x4e5f11c6U,
+	0xa68d2b2eU, 0x262d0baeU, 0x431053cbU, 0xbc13af34U,
+	0xf10cfd79U, 0xa2882a2aU, 0xbbd66d33U, 0xff837c77U,
+	0xdc6bb754U, 0xed17fa65U, 0x3e330db6U, 0xeb927963U,
+	0x93f4671bU, 0x7a661cf2U, 0xaa822822U, 0x3df3ceb5U,
+	0x20a888a8U, 0xb15ced39U, 0x8160e109U, 0xd86eb650U,
+	0x09cac381U, 0x5ccb97d4U, 0x882aa200U, 0xe912fb61U,
+	0xa3c86b2bU, 0xbd53ee35U, 0x425012caU, 0x74e99dfcU,
+	0x2ca78ba4U, 0x7da3def5U, 0x69b2dbe1U, 0x997ee711U,
+	0x4595d0cdU, 0x276d4aafU, 0xf6c93f7eU, 0x24ad89acU,
+	0x21e8c9a9U, 0x189e8690U, 0xd7a1765fU, 0x30bc8cb8U,
+	0xcc7fb344U, 0x726c1efaU, 0xcafa3042U, 0xf7897e7fU,
+	0x1491859cU, 0xec57bb64U, 0x6a7218e2U, 0x62781aeaU,
+	0x75a9dcfdU, 0x9034a418U, 0x64fd99ecU, 0x2de7caa5U,
+	0xd6e1375eU, 0x79a6dff1U, 0x29e2cba1U, 0x1a1e0492U,
+	0xa802aa20U, 0x3b764db3U, 0x896ae301U, 0x23684babU,
+	0xa148e929U, 0x0605038eU, 0x2e2709a6U, 0x0c8f8384U,
+	0x96b1271eU, 0xef977867U, 0x9bfe6513U, 0x1f5b4497U,
+	0xf509fc7dU, 0xcd3ff245U, 0x7e631df6U, 0xb7d96e3fU,
+	0xd92ef751U, 0x5581d4ddU, 0x983ea610U, 0x1e1b0596U,
+	0xae872926U, 0x5d8bd6d5U, 0x0b4a4183U, 0x6b3259e3U,
+	0x1751469fU, 0x1354479bU, 0xcfbf7047U, 0xe79d7a6fU,
+	0x9431a51cU, 0xceff3146U, 0x1c9b8794U, 0xe852ba60U,
+	0x4cdf93c4U, 0xd521f45dU, 0x50c494d8U, 0x5f0b54d7U,
+	0xe6dd3b6eU, 0xc535f04dU, 0xd3a4775bU, 0xa7cd6a2fU,
+	0xdeeb3556U, 0xe2d83a6aU, 0xcbba7143U, 0xc130f149U,
+	0x8aaa2002U, 0xa54de82dU, 0x667d1beeU, 0x8020a008U,
+	0x34b98dbcU, 0x9571e41dU, 0x4f1f50c7U, 0x54c195dcU,
+	0x1611079eU, 0x732c5ffbU, 0xe118f969U, 0xead23862U,
+	0x40d090c8U, 0x35f9ccbdU, 0x70ec9cf8U, 0x60f898e8U,
+	0x8425a10cU, 0x0745428fU, 0xfc43bf74U, 0x8bea6103U,
+	0x92b4261aU, 0xf846be70U, 0x7f235cf7U, 0x5b0e55d3U,
+	0x37794ebfU, 0x44d591ccU, 0x65bdd8edU, 0x86a5230eU,
+	0xb559ec3dU, 0x1b5e4593U, 0xb956ef31U, 0xb01cac38U,
+	0xc2f0324aU, 0x61b8d9e9U, 0x570156dfU, 0x11d4c599U,
+	0x4a5a10c2U, 0xd124f559U, 0x97f1661fU, 0x22280aaaU,
+	0x471552cfU, 0x19dec791U, 0xdfab7457U, 0x5a4e14d2U,
+	0xfd03fe75U, 0x8d6fe205U, 0x465513ceU, 0x83e0630bU,
+	0xc15f9e10U, 0xd801d909U, 0x3d9ca1ecU, 0x61d7b6b0U,
+	0xa92d8478U, 0x1db4a9ccU, 0x6e1876bfU, 0x44bafe95U,
+	0xc29f5d13U, 0x40bfff91U, 0xca955f1bU, 0x911b8a40U,
+	0x3a5963ebU, 0x75c6b3a4U, 0x2a4d67fbU, 0x8ec04e5fU,
+	0x365660e7U, 0x072a2dd6U, 0xf023d321U, 0x437f3c92U,
+	0x325361e3U, 0x6097f7b1U, 0x8ac54f5bU, 0x951e8b44U,
+	0x30d3e3e1U, 0x8b850e5aU, 0xd68e5807U, 0x026f6dd3U,
+	0xbfbc036eU, 0x804fcf51U, 0x82cf4d53U, 0xfc2cd02dU,
+	0xe83dd539U, 0x939b0842U, 0xdbc11a0aU, 0x49f5bc98U,
+	0xdc04d80dU, 0x2582a7f4U, 0xf829d129U, 0x734330a2U,
+	0x562e7887U, 0xcbd51e1aU, 0xb6f64067U, 0x979e0946U,
+	0x2d88a5fcU, 0xa5228774U, 0x28cde5f9U, 0x133b28c2U,
+	0x09a5acd8U, 0x54aefa85U, 0xeabd573bU, 0x3cdce0edU,
+	0xbd3c816cU, 0xbaf9436bU, 0x3999a0e8U, 0xcc10dc1dU,
+	0x9851c949U, 0xe5729734U, 0xc815dd19U, 0x71c3b2a0U,
+	0x4f703f9eU, 0xa462c675U, 0x661274b7U, 0x00efefd1U,
+	0x878a0d56U, 0x86ca4c57U, 0x04eaeed5U, 0x69ddb4b8U,
+	0x55eebb84U, 0x576e3986U, 0xc69a5c17U, 0x7083f3a1U,
+	0x945eca45U, 0xffec132eU, 0x6a1d77bbU, 0xfeac522fU,
+	0x01afaed0U, 0x8c40cc5dU, 0xa7a20576U, 0x38d9e1e9U,
+	0x3b1922eaU, 0x371621e6U, 0x9c54c84dU, 0x50abfb81U,
+	0x5f643b8eU, 0x0b252edaU, 0xf1639220U, 0xb476c265U,
+	0x79c9b0a8U, 0x3596a3e4U, 0xa6e24477U, 0xb7b60166U,
+	0x5b613a8aU, 0xc3df1c12U, 0xec38d43dU, 0x4e307e9fU,
+	0xe2b75533U, 0x0a656fdbU, 0xa2e74573U, 0xe3f71432U,
+	0x173e29c6U, 0xfbe9122aU, 0xdd44990cU, 0x10fbebc1U,
+	0x0e606edfU, 0x127b69c3U, 0x6b5d36baU, 0x760670a7U,
+	0x9ed44a4fU, 0x1e746acfU, 0x7b4932aaU, 0x844ace55U,
+	0xc9559c18U, 0x9ad14b4bU, 0x838f0c52U, 0xc7da1d16U,
+	0xe432d635U, 0xd54e9b04U, 0x066a6cd7U, 0xd3cb1802U,
+	0xabad067aU, 0x423f7d93U, 0x92db4943U, 0x05aaafd4U,
+	0x18f1e9c9U, 0x89058c58U, 0xb9398068U, 0xe037d731U,
+	0x3193a2e0U, 0x6492f6b5U, 0xb073c361U, 0xd14b9a00U,
+	0x9b910a4aU, 0x850a8f54U, 0x7a0973abU, 0x4cb0fc9dU,
+	0x14feeac5U, 0x45fabf94U, 0x51ebba80U, 0xa1278670U,
+	0x7dccb1acU, 0x1f342bceU, 0xce905e1fU, 0x1cf4e8cdU,
+	0x19b1a8c8U, 0x20c7e7f1U, 0xeff8173eU, 0x08e5edd9U,
+	0xf426d225U, 0x4a357f9bU, 0xf2a35123U, 0xcfd01f1eU,
+	0x2cc8e4fdU, 0xd40eda05U, 0x522b7983U, 0x5a217b8bU,
+	0x4df0bd9cU, 0xa86dc579U, 0x5ca4f88dU, 0x15beabc4U,
+	0xeeb8563fU, 0x41ffbe90U, 0x11bbaac0U, 0x224765f3U,
+	0x905bcb41U, 0x032f2cd2U, 0xb1338260U, 0x1b312acaU,
+	0x99118848U, 0x3e5c62efU, 0x167e68c7U, 0x34d6e2e5U,
+	0xaee8467fU, 0xd7ce1906U, 0xa3a70472U, 0x270225f6U,
+	0xcd509d1cU, 0xf5669324U, 0x463a7c97U, 0x8f800f5eU,
+	0xe1779630U, 0x6dd8b5bcU, 0xa067c771U, 0x264264f7U,
+	0x96de4847U, 0x65d2b7b4U, 0x331320e2U, 0x536b3882U,
+	0x2f0827feU, 0x2b0d26faU, 0xf7e61126U, 0xdfc41b0eU,
+	0xac68c47dU, 0xf6a65027U, 0x24c2e6f5U, 0xd00bdb01U,
+	0x7486f2a5U, 0xed78953cU, 0x689df5b9U, 0x675235b6U,
+	0xde845a0fU, 0xfd6c912cU, 0xebfd163aU, 0x9f940b4eU,
+	0xe6b25437U, 0xda815b0bU, 0xf3e31022U, 0xf9699028U,
+	0xb2f34163U, 0x9d14894cU, 0x5e247a8fU, 0xb879c169U,
+	0x0ce0ecddU, 0xad28857cU, 0x774631a6U, 0x6c98f4bdU,
+	0x2e4866ffU, 0x4b753e9aU, 0xd9419808U, 0xd28b5903U,
+	0x7889f1a9U, 0x0da0addcU, 0x48b5fd99U, 0x58a1f989U,
+	0xbc7cc06dU, 0x3f1c23eeU, 0xc41ade15U, 0xb3b30062U,
+	0xaaed477bU, 0xc01fdf11U, 0x477a3d96U, 0x635734b2U,
+	0x0f202fdeU, 0x7c8cf0adU, 0x5de4b98cU, 0xbefc426fU,
+	0x8d008d5cU, 0x230724f2U, 0x810f8e50U, 0x8845cd59U,
+	0xfaa9532bU, 0x59e1b888U, 0x6f5837beU, 0x298da4f8U,
+	0x720371a3U, 0xe97d9438U, 0xafa8077eU, 0x1a716bcbU,
+	0x7f4c33aeU, 0x2187a6f0U, 0xe7f21536U, 0x621775b3U,
+	0xc55a9f14U, 0xb5368364U, 0x7e0c72afU, 0xbbb9026aU,
+	0x1bcfd45aU, 0x02919343U, 0xe70ceba6U, 0xbb47fcfaU,
+	0x73bdce32U, 0xc724e386U, 0xb4883cf5U, 0x9e2ab4dfU,
+	0x180f1759U, 0x9a2fb5dbU, 0x10051551U, 0x4b8bc00aU,
+	0xe0c929a1U, 0xaf56f9eeU, 0xf0dd2db1U, 0x54500415U,
+	0xecc62aadU, 0xddba679cU, 0x2ab3996bU, 0x99ef76d8U,
+	0xe8c32ba9U, 0xba07bdfbU, 0x50550511U, 0x4f8ec10eU,
+	0xea43a9abU, 0x51154410U, 0x0c1e124dU, 0xd8ff2799U,
+	0x652c4924U, 0x5adf851bU, 0x585f0719U, 0x26bc9a67U,
+	0x32ad9f73U, 0x490b4208U, 0x01515040U, 0x9365f6d2U,
+	0x06949247U, 0xff12edbeU, 0x22b99b63U, 0xa9d37ae8U,
+	0x8cbe32cdU, 0x11455450U, 0x6c660a2dU, 0x4d0e430cU,
+	0xf718efb6U, 0x7fb2cd3eU, 0xf25dafb3U, 0xc9ab6288U,
+	0xd335e692U, 0x8e3eb0cfU, 0x302d1d71U, 0xe64caaa7U,
+	0x67accb26U, 0x60690921U, 0xe309eaa2U, 0x16809657U,
+	0x42c18303U, 0x3fe2dd7eU, 0x12859753U, 0xab53f8eaU,
+	0x95e075d4U, 0x7ef28c3fU, 0xbc823efdU, 0xda7fa59bU,
+	0x5d1a471cU, 0x5c5a061dU, 0xde7aa49fU, 0xb34dfef2U,
+	0x8f7ef1ceU, 0x8dfe73ccU, 0x1c0a165dU, 0xaa13b9ebU,
+	0x4ece800fU, 0x257c5964U, 0xb08d3df1U, 0x243c1865U,
+	0xdb3fe49aU, 0x56d08617U, 0x7d324f3cU, 0xe249aba3U,
+	0xe18968a0U, 0xed866bacU, 0x46c48207U, 0x8a3bb1cbU,
+	0x85f471c4U, 0xd1b56490U, 0x2bf3d86aU, 0x6ee6882fU,
+	0xa359fae2U, 0xef06e9aeU, 0x7c720e3dU, 0x6d264b2cU,
+	0x81f170c0U, 0x194f5658U, 0x36a89e77U, 0x94a034d5U,
+	0x38271f79U, 0xd0f52591U, 0x78770f39U, 0x39675e78U,
+	0xcdae638cU, 0x21795860U, 0x07d4d346U, 0xca6ba18bU,
+	0xd4f02495U, 0xc8eb2389U, 0xb1cd7cf0U, 0xac963aedU,
+	0x44440005U, 0xc4e42085U, 0xa1d978e0U, 0x5eda841fU,
+	0x13c5d652U, 0x40410101U, 0x591f4618U, 0x1d4a575cU,
+	0x3ea29c7fU, 0x0fded14eU, 0xdcfa269dU, 0x095b5248U,
+	0x713d4c30U, 0x98af37d9U, 0x484b0309U, 0xdf3ae59eU,
+	0xc261a383U, 0x5395c612U, 0x63a9ca22U, 0x3aa79d7bU,
+	0xeb03e8aaU, 0xbe02bcffU, 0x6ae3892bU, 0x0bdbd04aU,
+	0x41014000U, 0x5f9ac51eU, 0xa09939e1U, 0x9620b6d7U,
+	0xce6ea08fU, 0x9f6af5deU, 0x8b7bf0caU, 0x7bb7cc3aU,
+	0xa75cfbe6U, 0xc5a46184U, 0x14001455U, 0xc664a287U,
+	0xc321e282U, 0xfa57adbbU, 0x35685d74U, 0xd275a793U,
+	0x2eb6986fU, 0x90a535d1U, 0x28331b69U, 0x15405554U,
+	0xf658aeb7U, 0x0e9e904fU, 0x88bb33c9U, 0x80b131c1U,
+	0x9760f7d6U, 0x72fd8f33U, 0x8634b2c7U, 0xcf2ee18eU,
+	0x34281c75U, 0x9b6ff4daU, 0xcb2be08aU, 0xf8d72fb9U,
+	0x4acb810bU, 0xd9bf6698U, 0x6ba3c82aU, 0xc1a16080U,
+	0x4381c202U, 0xe4cc28a5U, 0xccee228dU, 0xee46a8afU,
+	0x74780c35U, 0x0d5e534cU, 0x79374e38U, 0xfd926fbcU,
+	0x17c0d756U, 0x2ff6d96eU, 0x9caa36ddU, 0x55104514U,
+	0x3be7dc7aU, 0xb748fff6U, 0x7af78d3bU, 0xfcd22ebdU,
+	0x4c4e020dU, 0xbf42fdfeU, 0xe9836aa8U, 0x89fb72c8U,
+	0xf5986db4U, 0xf19d6cb0U, 0x2d765b6cU, 0x05545144U,
+	0x76f88e37U, 0x2c361a6dU, 0xfe52acbfU, 0x0a9b914bU,
+	0xae16b8efU, 0x37e8df76U, 0xb20dbff3U, 0xbdc27ffcU,
+	0x04141045U, 0x27fcdb66U, 0x316d5c70U, 0x45044104U,
+	0x3c221e7dU, 0x00111141U, 0x29735a68U, 0x23f9da62U,
+	0x68630b29U, 0x4784c306U, 0x84b430c5U, 0x62e98b23U,
+	0xd670a697U, 0x77b8cf36U, 0xadd67becU, 0xb608bef7U,
+	0xf4d82cb5U, 0x91e574d0U, 0x03d1d242U, 0x081b1349U,
+	0xa219bbe3U, 0xd730e796U, 0x9225b7d3U, 0x8231b3c3U,
+	0x66ec8a27U, 0xe58c69a4U, 0x1e8a945fU, 0x69234a28U,
+	0x707d0d31U, 0x1a8f955bU, 0x9dea77dcU, 0xb9c77ef8U,
+	0xd5b06594U, 0xa61cbae7U, 0x8774f3c6U, 0x646c0825U,
+	0x5790c716U, 0xf9976eb8U, 0x5b9fc41aU, 0x52d58713U,
+	0x20391961U, 0x8371f2c2U, 0xb5c87df4U, 0xf31deeb2U,
+	0xa8933be9U, 0x33edde72U, 0x75384d34U, 0xc0e12181U,
+	0xa5dc79e4U, 0xfb17ecbaU, 0x3d625f7cU, 0xb8873ff9U,
+	0x1fcad55eU, 0x6fa6c92eU, 0xa49c38e5U, 0x61294820U,
+	0x804aca44U, 0x99148d5dU, 0x7c89f5b8U, 0x20c2e2e4U,
+	0xe838d02cU, 0x5ca1fd98U, 0x2f0d22ebU, 0x05afaac1U,
+	0x838a0947U, 0x01aaabc5U, 0x8b800b4fU, 0xd00ede14U,
+	0x7b4c37bfU, 0x34d3e7f0U, 0x6b5833afU, 0xcfd51a0bU,
+	0x774334b3U, 0x463f7982U, 0xb1368775U, 0x026a68c6U,
+	0x734635b7U, 0x2182a3e5U, 0xcbd01b0fU, 0xd40bdf10U,
+	0x71c6b7b5U, 0xca905a0eU, 0x979b0c53U, 0x437a3987U,
+	0xfea9573aU, 0xc15a9b05U, 0xc3da1907U, 0xbd398479U,
+	0xa928816dU, 0xd28e5c16U, 0x9ad44e5eU, 0x08e0e8ccU,
+	0x9d118c59U, 0x6497f3a0U, 0xb93c857dU, 0x325664f6U,
+	0x173b2cd3U, 0x8ac04a4eU, 0xf7e31433U, 0xd68b5d12U,
+	0x6c9df1a8U, 0xe437d320U, 0x69d8b1adU, 0x522e7c96U,
+	0x48b0f88cU, 0x15bbaed1U, 0xaba8036fU, 0x7dc9b4b9U,
+	0xfc29d538U, 0xfbec173fU, 0x788cf4bcU, 0x8d058849U,
+	0xd9449d1dU, 0xa467c360U, 0x8900894dU, 0x30d6e6f4U,
+	0x0e656bcaU, 0xe5779221U, 0x270720e3U, 0x41fabb85U,
+	0xc69f5902U, 0xc7df1803U, 0x45ffba81U, 0x28c8e0ecU,
+	0x14fbefd0U, 0x167b6dd2U, 0x878f0843U, 0x3196a7f5U,
+	0xd54b9e11U, 0xbef9477aU, 0x2b0823efU, 0xbfb9067bU,
+	0x40bafa84U, 0xcd559809U, 0xe6b75122U, 0x79ccb5bdU,
+	0x7a0c76beU, 0x760375b2U, 0xdd419c19U, 0x11beafd5U,
+	0x1e716fdaU, 0x4a307a8eU, 0xb076c674U, 0xf5639631U,
+	0x38dce4fcU, 0x7483f7b0U, 0xe7f71023U, 0xf6a35532U,
+	0x1a746edeU, 0x82ca4846U, 0xad2d8069U, 0x0f252acbU,
+	0xa3a20167U, 0x4b703b8fU, 0xe3f21127U, 0xa2e24066U,
+	0x562b7d92U, 0xbafc467eU, 0x9c51cd58U, 0x51eebf95U,
+	0x4f753a8bU, 0x536e3d97U, 0x2a4862eeU, 0x371324f3U,
+	0xdfc11e1bU, 0x5f613e9bU, 0x3a5c66feU, 0xc55f9a01U,
+	0x8840c84cU, 0xdbc41f1fU, 0xc29a5806U, 0x86cf4942U,
+	0xa5278261U, 0x945bcf50U, 0x477f3883U, 0x92de4c56U,
+	0xeab8522eU, 0x032a29c7U, 0xd3ce1d17U, 0x44bffb80U,
+	0x59e4bd9dU, 0xc810d80cU, 0xf82cd43cU, 0xa1228365U,
+	0x7086f6b4U, 0x2587a2e1U, 0xf1669735U, 0x905ece54U,
+	0xda845e1eU, 0xc41fdb00U, 0x3b1c27ffU, 0x0da5a8c9U,
+	0x55ebbe91U, 0x04efebc0U, 0x10feeed4U, 0xe032d224U,
+	0x3cd9e5f8U, 0x5e217f9aU, 0x8f850a4bU, 0x5de1bc99U,
+	0x58a4fc9cU, 0x61d2b3a5U, 0xaeed436aU, 0x49f0b98dU,
+	0xb5338671U, 0x0b202bcfU, 0xb3b60577U, 0x8ec54b4aU,
+	0x6dddb0a9U, 0x951b8e51U, 0x133e2dd7U, 0x1b342fdfU,
+	0x0ce5e9c8U, 0xe978912dU, 0x1db1acd9U, 0x54abff90U,
+	0xafad026bU, 0x00eaeac4U, 0x50aefe94U, 0x635231a7U,
+	0xd14e9f15U, 0x423a7886U, 0xf026d634U, 0x5a247e9eU,
+	0xd804dc1cU, 0x7f4936bbU, 0x576b3c93U, 0x75c3b6b1U,
+	0xeffd122bU, 0x96db4d52U, 0xe2b25026U, 0x661771a2U,
+	0x8c45c948U, 0xb473c770U, 0x072f28c3U, 0xce955b0aU,
+	0xa062c264U, 0x2ccde1e8U, 0xe1729325U, 0x675730a3U,
+	0xd7cb1c13U, 0x24c7e3e0U, 0x720674b6U, 0x127e6cd6U,
+	0x6e1d73aaU, 0x6a1872aeU, 0xb6f34572U, 0x9ed14f5aU,
+	0xed7d9029U, 0xb7b30473U, 0x65d7b2a1U, 0x911e8f55U,
+	0x3593a6f1U, 0xac6dc168U, 0x2988a1edU, 0x264761e2U,
+	0x9f910e5bU, 0xbc79c578U, 0xaae8426eU, 0xde815f1aU,
+	0xa7a70063U, 0x9b940f5fU, 0xb2f64476U, 0xb87cc47cU,
+	0xf3e61537U, 0xdc01dd18U, 0x1f312edbU, 0xf96c953dU,
+	0x4df5b889U, 0xec3dd128U, 0x365365f2U, 0x2d8da0e9U,
+	0x6f5d32abU, 0x0a606aceU, 0x9854cc5cU, 0x939e0d57U,
+	0x399ca5fdU, 0x4cb5f988U, 0x09a0a9cdU, 0x19b4adddU,
+	0xfd699439U, 0x7e0977baU, 0x850f8a41U, 0xf2a65436U,
+	0xebf8132fU, 0x810a8b45U, 0x066f69c2U, 0x224260e6U,
+	0x4e357b8aU, 0x3d99a4f9U, 0x1cf1edd8U, 0xffe9163bU,
+	0xcc15d908U, 0x621270a6U, 0xc01ada04U, 0xc950990dU,
+	0xbbbc077fU, 0x18f4ecdcU, 0x2e4d63eaU, 0x6898f0acU,
+	0x331625f7U, 0xa868c06cU, 0xeebd532aU, 0x5b643f9fU,
+	0x3e5967faU, 0x6092f2a4U, 0xa6e74162U, 0x230221e7U,
+	0x844fcb40U, 0xf423d730U, 0x3f1926fbU, 0xfaac563eU,
+	0x83b635bbU, 0x9ae872a2U, 0x7f750a47U, 0x233e1d1bU,
+	0xebc42fd3U, 0x5f5d0267U, 0x2cf1dd14U, 0x0653553eU,
+	0x8076f6b8U, 0x0256543aU, 0x887cf4b0U, 0xd3f221ebU,
+	0x78b0c840U, 0x372f180fU, 0x68a4cc50U, 0xcc29e5f4U,
+	0x74bfcb4cU, 0x45c3867dU, 0xb2ca788aU, 0x01969739U,
+	0x70baca48U, 0x227e5c1aU, 0xc82ce4f0U, 0xd7f720efU,
+	0x723a484aU, 0xc96ca5f1U, 0x9467f3acU, 0x4086c678U,
+	0xfd55a8c5U, 0xc2a664faU, 0xc026e6f8U, 0xbec57b86U,
+	0xaad47e92U, 0xd172a3e9U, 0x9928b1a1U, 0x0b1c1733U,
+	0x9eed73a6U, 0x676b0c5fU, 0xbac07a82U, 0x31aa9b09U,
+	0x14c7d32cU, 0x893cb5b1U, 0xf41febccU, 0xd577a2edU,
+	0x6f610e57U, 0xe7cb2cdfU, 0x6a244e52U, 0x51d28369U,
+	0x4b4c0773U, 0x1647512eU, 0xa854fc90U, 0x7e354b46U,
+	0xffd52ac7U, 0xf810e8c0U, 0x7b700b43U, 0x8ef977b6U,
+	0xdab862e2U, 0xa79b3c9fU, 0x8afc76b2U, 0x332a190bU,
+	0x0d999435U, 0xe68b6ddeU, 0x24fbdf1cU, 0x4206447aU,
+	0xc563a6fdU, 0xc423e7fcU, 0x4603457eU, 0x2b341f13U,
+	0x1707102fU, 0x1587922dU, 0x8473f7bcU, 0x326a580aU,
+	0xd6b761eeU, 0xbd05b885U, 0x28f4dc10U, 0xbc45f984U,
+	0x4346057bU, 0xcea967f6U, 0xe54baeddU, 0x7a304a42U,
+	0x79f08941U, 0x75ff8a4dU, 0xdebd63e6U, 0x1242502aU,
+	0x1d8d9025U, 0x49cc8571U, 0xb38a398bU, 0xf69f69ceU,
+	0x3b201b03U, 0x777f084fU, 0xe40befdcU, 0xf55faacdU,
+	0x19889121U, 0x8136b7b9U, 0xaed17f96U, 0x0cd9d534U,
+	0xa05efe98U, 0x488cc470U, 0xe00eeed8U, 0xa11ebf99U,
+	0x55d7826dU, 0xb900b981U, 0x9fad32a7U, 0x5212406aU,
+	0x4c89c574U, 0x5092c268U, 0x29b49d11U, 0x34efdb0cU,
+	0xdc3de1e4U, 0x5c9dc164U, 0x39a09901U, 0xc6a365feU,
+	0x8bbc37b3U, 0xd838e0e0U, 0xc166a7f9U, 0x8533b6bdU,
+	0xa6db7d9eU, 0x97a730afU, 0x4483c77cU, 0x9122b3a9U,
+	0xe944add1U, 0x00d6d638U, 0xd032e2e8U, 0x4743047fU,
+	0x5a184262U, 0xcbec27f3U, 0xfbd02bc3U, 0xa2de7c9aU,
+	0x737a094bU, 0x267b5d1eU, 0xf29a68caU, 0x93a231abU,
+	0xd978a1e1U, 0xc7e324ffU, 0x38e0d800U, 0x0e595736U,
+	0x5617416eU, 0x0713143fU, 0x1302112bU, 0xe3ce2ddbU,
+	0x3f251a07U, 0x5ddd8065U, 0x8c79f5b4U, 0x5e1d4366U,
+	0x5b580363U, 0x622e4c5aU, 0xad11bc95U, 0x4a0c4672U,
+	0xb6cf798eU, 0x08dcd430U, 0xb04afa88U, 0x8d39b4b5U,
+	0x6e214f56U, 0x96e771aeU, 0x10c2d228U, 0x18c8d020U,
+	0x0f191637U, 0xea846ed2U, 0x1e4d5326U, 0x5757006fU,
+	0xac51fd94U, 0x0316153bU, 0x5352016bU, 0x60aece58U,
+	0xd2b260eaU, 0x41c68779U, 0xf3da29cbU, 0x59d88161U,
+	0xdbf823e3U, 0x7cb5c944U, 0x5497c36cU, 0x763f494eU,
+	0xec01edd4U, 0x9527b2adU, 0xe14eafd9U, 0x65eb8e5dU,
+	0x8fb936b7U, 0xb78f388fU, 0x04d3d73cU, 0xcd69a4f5U,
+	0xa39e3d9bU, 0x2f311e17U, 0xe28e6cdaU, 0x64abcf5cU,
+	0xd437e3ecU, 0x273b1c1fU, 0x71fa8b49U, 0x11829329U,
+	0x6de18c55U, 0x69e48d51U, 0xb50fba8dU, 0x9d2db0a5U,
+	0xee816fd6U, 0xb44ffb8cU, 0x662b4d5eU, 0x92e270aaU,
+	0x366f590eU, 0xaf913e97U, 0x2a745e12U, 0x25bb9e1dU,
+	0x9c6df1a4U, 0xbf853a87U, 0xa914bd91U, 0xdd7da0e5U,
+	0xa45bff9cU, 0x9868f0a0U, 0xb10abb89U, 0xbb803b83U,
+	0xf01aeac8U, 0xdffd22e7U, 0x1ccdd124U, 0xfa906ac2U,
+	0x4e094776U, 0xefc12ed7U, 0x35af9a0dU, 0x2e715f16U,
+	0x6ca1cd54U, 0x099c9531U, 0x9ba833a3U, 0x9062f2a8U,
+	0x3a605a02U, 0x4f490677U, 0x0a5c5632U, 0x1a485222U,
+	0xfe956bc6U, 0x7df58845U, 0x86f375beU, 0xf15aabc9U,
+	0xe804ecd0U, 0x82f674baU, 0x0593963dU, 0x21be9f19U,
+	0x4dc98475U, 0x3e655b06U, 0x1f0d1227U, 0xfc15e9c4U,
+	0xcfe926f7U, 0x61ee8f59U, 0xc3e625fbU, 0xcaac66f2U,
+	0xb840f880U, 0x1b081323U, 0x2db19c15U, 0x6b640f53U,
+	0x30eada08U, 0xab943f93U, 0xed41acd5U, 0x5898c060U,
+	0x3da59805U, 0x636e0d5bU, 0xa51bbe9dU, 0x20fede18U,
+	0x87b334bfU, 0xf7df28cfU, 0x3ce5d904U, 0xf950a9c1U,
+	0x3a39038dU, 0x23674494U, 0xc6fa3c71U, 0x9ab12b2dU,
+	0x524b19e5U, 0xe6d23451U, 0x957eeb22U, 0xbfdc6308U,
+	0x39f9c08eU, 0xbbd9620cU, 0x31f3c286U, 0x6a7d17ddU,
+	0xc13ffe76U, 0x8ea02e39U, 0xd12bfa66U, 0x75a6d3c2U,
+	0xcd30fd7aU, 0xfc4cb04bU, 0x0b454ebcU, 0xb819a10fU,
+	0xc935fc7eU, 0x9bf16a2cU, 0x71a3d2c6U, 0x6e7816d9U,
+	0xcbb57e7cU, 0x70e393c7U, 0x2de8c59aU, 0xf909f04eU,
+	0x44da9ef3U, 0x7b2952ccU, 0x79a9d0ceU, 0x074a4db0U,
+	0x135b48a4U, 0x68fd95dfU, 0x20a78797U, 0xb2932105U,
+	0x27624590U, 0xdee43a69U, 0x034f4cb4U, 0x8825ad3fU,
+	0xad48e51aU, 0x30b38387U, 0x4d90ddfaU, 0x6cf894dbU,
+	0xd6ee3861U, 0x5e441ae9U, 0xd3ab7864U, 0xe85db55fU,
+	0xf2c33145U, 0xafc86718U, 0x11dbcaa6U, 0xc7ba7d70U,
+	0x465a1cf1U, 0x419fdef6U, 0xc2ff3d75U, 0x37764180U,
+	0x633754d4U, 0x1e140aa9U, 0x33734084U, 0x8aa52f3dU,
+	0xb416a203U, 0x5f045be8U, 0x9d74e92aU, 0xfb89724cU,
+	0x7cec90cbU, 0x7dacd1caU, 0xff8c7348U, 0x92bb2925U,
+	0xae882619U, 0xac08a41bU, 0x3dfcc18aU, 0x8be56e3cU,
+	0x6f3857d8U, 0x048a8eb3U, 0x917bea26U, 0x05cacfb2U,
+	0xfac9334dU, 0x772651c0U, 0x5cc498ebU, 0xc3bf7c74U,
+	0xc07fbf77U, 0xcc70bc7bU, 0x673255d0U, 0xabcd661cU,
+	0xa402a613U, 0xf043b347U, 0x0a050fbdU, 0x4f105ff8U,
+	0x82af2d35U, 0xcef03e79U, 0x5d84d9eaU, 0x4cd09cfbU,
+	0xa007a717U, 0x38b9818fU, 0x175e49a0U, 0xb556e302U,
+	0x19d1c8aeU, 0xf103f246U, 0x5981d8eeU, 0x189189afU,
+	0xec58b45bU, 0x008f8fb7U, 0x26220491U, 0xeb9d765cU,
+	0xf506f342U, 0xe91df45eU, 0x903bab27U, 0x8d60ed3aU,
+	0x65b2d7d2U, 0xe512f752U, 0x802faf37U, 0x7f2c53c8U,
+	0x32330185U, 0x61b7d6d6U, 0x78e991cfU, 0x3cbc808bU,
+	0x1f544ba8U, 0x2e280699U, 0xfd0cf14aU, 0x28ad859fU,
+	0x50cb9be7U, 0xb959e00eU, 0x69bdd4deU, 0xfecc3249U,
+	0xe3977454U, 0x726311c5U, 0x425f1df5U, 0x1b514aacU,
+	0xcaf53f7dU, 0x9ff46b28U, 0x4b155efcU, 0x2a2d079dU,
+	0x60f797d7U, 0x7e6c12c9U, 0x816fee36U, 0xb7d66100U,
+	0xef987758U, 0xbe9c2209U, 0xaa8d271dU, 0x5a411bedU,
+	0x86aa2c31U, 0xe452b653U, 0x35f6c382U, 0xe7927550U,
+	0xe2d73555U, 0xdba17a6cU, 0x149e8aa3U, 0xf3837044U,
+	0x0f404fb8U, 0xb153e206U, 0x09c5ccbeU, 0x34b68283U,
+	0xd7ae7960U, 0x2f684798U, 0xa94de41eU, 0xa147e616U,
+	0xb6962001U, 0x530b58e4U, 0xa7c26510U, 0xeed83659U,
+	0x15decba2U, 0xba99230dU, 0xeadd375dU, 0xd921f86eU,
+	0x6b3d56dcU, 0xf849b14fU, 0x4a551ffdU, 0xe057b757U,
+	0x627715d5U, 0xc53aff72U, 0xed18f55aU, 0xcfb07f78U,
+	0x558edbe2U, 0x2ca8849bU, 0x58c199efU, 0xdc64b86bU,
+	0x36360081U, 0x0e000eb9U, 0xbd5ce10aU, 0x74e692c3U,
+	0x1a110badU, 0x96be2821U, 0x5b015aecU, 0xdd24f96aU,
+	0x6db8d5daU, 0x9eb42a29U, 0xc875bd7fU, 0xa80da51fU,
+	0xd46eba63U, 0xd06bbb67U, 0x0c808cbbU, 0x24a28693U,
+	0x570e59e0U, 0x0dc0cdbaU, 0xdfa47b68U, 0x2b6d469cU,
+	0x8fe06f38U, 0x161e08a1U, 0x93fb6824U, 0x9c34a82bU,
+	0x25e2c792U, 0x060a0cb1U, 0x109b8ba7U, 0x64f296d3U,
+	0x1dd4c9aaU, 0x21e7c696U, 0x08858dbfU, 0x020f0db5U,
+	0x4995dcfeU, 0x667214d1U, 0xa542e712U, 0x431f5cf4U,
+	0xf7867140U, 0x564e18e1U, 0x8c20ac3bU, 0x97fe6920U,
+	0xd52efb62U, 0xb013a307U, 0x22270595U, 0x29edc49eU,
+	0x83ef6c34U, 0xf6c63041U, 0xb3d36004U, 0xa3c76414U,
+	0x471a5df0U, 0xc47abe73U, 0x3f7c4388U, 0x48d59dffU,
+	0x518bdae6U, 0x3b79428cU, 0xbc1ca00bU, 0x9831a92fU,
+	0xf446b243U, 0x87ea6d30U, 0xa6822411U, 0x459adff2U,
+	0x766610c1U, 0xd861b96fU, 0x7a6913cdU, 0x732350c4U,
+	0x01cfceb6U, 0xa2872515U, 0x943eaa23U, 0xd2eb3965U,
+	0x8965ec3eU, 0x121b09a5U, 0x54ce9ae3U, 0xe117f656U,
+	0x842aae33U, 0xdae13b6dU, 0x1c9488abU, 0x9971e82eU,
+	0x3e3c0289U, 0x4e501ef9U, 0x856aef32U, 0x40df9ff7U,
+	0x742f5bd5U, 0x6d711cccU, 0x88ec6429U, 0xd4a77375U,
+	0x1c5d41bdU, 0xa8c46c09U, 0xdb68b37aU, 0xf1ca3b50U,
+	0x77ef98d6U, 0xf5cf3a54U, 0x7fe59adeU, 0x246b4f85U,
+	0x8f29a62eU, 0xc0b67661U, 0x9f3da23eU, 0x3bb08b9aU,
+	0x8326a522U, 0xb25ae813U, 0x455316e4U, 0xf60ff957U,
+	0x8723a426U, 0xd5e73274U, 0x3fb58a9eU, 0x206e4e81U,
+	0x85a32624U, 0x3ef5cb9fU, 0x63fe9dc2U, 0xb71fa816U,
+	0x0accc6abU, 0x353f0a94U, 0x37bf8896U, 0x495c15e8U,
+	0x5d4d10fcU, 0x26ebcd87U, 0x6eb1dfcfU, 0xfc85795dU,
+	0x69741dc8U, 0x90f26231U, 0x4d5914ecU, 0xc633f567U,
+	0xe35ebd42U, 0x7ea5dbdfU, 0x038685a2U, 0x22eecc83U,
+	0x98f86039U, 0x105242b1U, 0x9dbd203cU, 0xa64bed07U,
+	0xbcd5691dU, 0xe1de3f40U, 0x5fcd92feU, 0x89ac2528U,
+	0x084c44a9U, 0x0f8986aeU, 0x8ce9652dU, 0x796019d8U,
+	0x2d210c8cU, 0x500252f1U, 0x7d6518dcU, 0xc4b37765U,
+	0xfa00fa5bU, 0x111203b0U, 0xd362b172U, 0xb59f2a14U,
+	0x32fac893U, 0x33ba8992U, 0xb19a2b10U, 0xdcad717dU,
+	0xe09e7e41U, 0xe21efc43U, 0x73ea99d2U, 0xc5f33664U,
+	0x212e0f80U, 0x4a9cd6ebU, 0xdf6db27eU, 0x4bdc97eaU,
+	0xb4df6b15U, 0x39300998U, 0x12d2c0b3U, 0x8da9242cU,
+	0x8e69e72fU, 0x8266e423U, 0x29240d88U, 0xe5db3e44U,
+	0xea14fe4bU, 0xbe55eb1fU, 0x441357e5U, 0x010607a0U,
+	0xccb9756dU, 0x80e66621U, 0x139281b2U, 0x02c6c4a3U,
+	0xee11ff4fU, 0x76afd9d7U, 0x594811f8U, 0xfb40bb5aU,
+	0x57c790f6U, 0xbf15aa1eU, 0x179780b6U, 0x5687d1f7U,
+	0xa24eec03U, 0x4e99d7efU, 0x68345cc9U, 0xa58b2e04U,
+	0xbb10ab1aU, 0xa70bac06U, 0xde2df37fU, 0xc376b562U,
+	0x2ba48f8aU, 0xab04af0aU, 0xce39f76fU, 0x313a0b90U,
+	0x7c2559ddU, 0x2fa18e8eU, 0x36ffc997U, 0x72aad8d3U,
+	0x514213f0U, 0x603e5ec1U, 0xb31aa912U, 0x66bbddc7U,
+	0x1eddc3bfU, 0xf74fb856U, 0x27ab8c86U, 0xb0da6a11U,
+	0xad812c0cU, 0x3c75499dU, 0x0c4945adU, 0x554712f4U,
+	0x84e36725U, 0xd1e23370U, 0x050306a4U, 0x643b5fc5U,
+	0x2ee1cf8fU, 0x307a4a91U, 0xcf79b66eU, 0xf9c03958U,
+	0xa18e2f00U, 0xf08a7a51U, 0xe49b7f45U, 0x145743b5U,
+	0xc8bc7469U, 0xaa44ee0bU, 0x7be09bdaU, 0xa9842d08U,
+	0xacc16d0dU, 0x95b72234U, 0x5a88d2fbU, 0xbd95281cU,
+	0x415617e0U, 0xff45ba5eU, 0x47d394e6U, 0x7aa0dadbU,
+	0x99b82138U, 0x617e1fc0U, 0xe75bbc46U, 0xef51be4eU,
+	0xf8807859U, 0x1d1d00bcU, 0xe9d43d48U, 0xa0ce6e01U,
+	0x5bc893faU, 0xf48f7b55U, 0xa4cb6f05U, 0x9737a036U,
+	0x252b0e84U, 0xb65fe917U, 0x044347a5U, 0xae41ef0fU,
+	0x2c614d8dU, 0x8b2ca72aU, 0xa30ead02U, 0x81a62720U,
+	0x1b9883baU, 0x62bedcc3U, 0x16d7c1b7U, 0x9272e033U,
+	0x782058d9U, 0x401656e1U, 0xf34ab952U, 0x3af0ca9bU,
+	0x540753f5U, 0xd8a87079U, 0x151702b4U, 0x9332a132U,
+	0x23ae8d82U, 0xd0a27271U, 0x8663e527U, 0xe61bfd47U,
+	0x9a78e23bU, 0x9e7de33fU, 0x4296d4e3U, 0x6ab4decbU,
+	0x191801b8U, 0x43d695e2U, 0x91b22330U, 0x657b1ec4U,
+	0xc1f63760U, 0x580850f9U, 0xdded307cU, 0xd222f073U,
+	0x6bf49fcaU, 0x481c54e9U, 0x5e8dd3ffU, 0x2ae4ce8bU,
+	0x53c291f2U, 0x6ff19eceU, 0x4693d5e7U, 0x4c1955edU,
+	0x078384a6U, 0x28644c89U, 0xeb54bf4aU, 0x0d0904acU,
+	0xb9902918U, 0x185840b9U, 0xc236f463U, 0xd9e83178U,
+	0x9b38a33aU, 0xfe05fb5fU, 0x6c315dcdU, 0x67fb9cc6U,
+	0xcdf9346cU, 0xb8d06819U, 0xfdc5385cU, 0xedd13c4cU,
+	0x090c05a8U, 0x8a6ce62bU, 0x716a1bd0U, 0x06c3c5a7U,
+	0x1f9d82beU, 0x756f1ad4U, 0xf20af853U, 0xd627f177U,
+	0xba50ea1bU, 0xc9fc3568U, 0xe8947c49U, 0x0b8c87aaU,
+	0x38704899U, 0x9677e137U, 0x347f4b95U, 0x3d35089cU,
+	0x4fd996eeU, 0xec917d4dU, 0xda28f27bU, 0x9cfd613dU,
+	0xc773b466U, 0x5c0d51fdU, 0x1ad8c2bbU, 0xaf01ae0eU,
+	0xca3cf66bU, 0x94f76335U, 0x5282d0f3U, 0xd767b076U,
+	0x702a5ad1U, 0x004646a1U, 0xcb7cb76aU, 0x0ec9c7afU,
+	0x707a0a84U, 0x69244d9dU, 0x8cb93578U, 0xd0f22224U,
+	0x180810ecU, 0xac913d58U, 0xdf3de22bU, 0xf59f6a01U,
+	0x73bac987U, 0xf19a6b05U, 0x7bb0cb8fU, 0x203e1ed4U,
+	0x8b7cf77fU, 0xc4e32730U, 0x9b68f36fU, 0x3fe5dacbU,
+	0x8773f473U, 0xb60fb942U, 0x410647b5U, 0xf25aa806U,
+	0x8376f577U, 0xd1b26325U, 0x3be0dbcfU, 0x243b1fd0U,
+	0x81f67775U, 0x3aa09aceU, 0x67abcc93U, 0xb34af947U,
+	0x0e9997faU, 0x316a5bc5U, 0x33ead9c7U, 0x4d0944b9U,
+	0x591841adU, 0x22be9cd6U, 0x6ae48e9eU, 0xf8d0280cU,
+	0x6d214c99U, 0x94a73360U, 0x490c45bdU, 0xc266a436U,
+	0xe70bec13U, 0x7af08a8eU, 0x07d3d4f3U, 0x26bb9dd2U,
+	0x9cad3168U, 0x140713e0U, 0x99e8716dU, 0xa21ebc56U,
+	0xb880384cU, 0xe58b6e11U, 0x5b98c3afU, 0x8df97479U,
+	0x0c1915f8U, 0x0bdcd7ffU, 0x88bc347cU, 0x7d354889U,
+	0x29745dddU, 0x545703a0U, 0x7930498dU, 0xc0e62634U,
+	0xfe55ab0aU, 0x154752e1U, 0xd737e023U, 0xb1ca7b45U,
+	0x36af99c2U, 0x37efd8c3U, 0xb5cf7a41U, 0xd8f8202cU,
+	0xe4cb2f10U, 0xe64bad12U, 0x77bfc883U, 0xc1a66735U,
+	0x257b5ed1U, 0x4ec987baU, 0xdb38e32fU, 0x4f89c6bbU,
+	0xb08a3a44U, 0x3d6558c9U, 0x168791e2U, 0x89fc757dU,
+	0x8a3cb67eU, 0x8633b572U, 0x2d715cd9U, 0xe18e6f15U,
+	0xee41af1aU, 0xba00ba4eU, 0x404606b4U, 0x055356f1U,
+	0xc8ec243cU, 0x84b33770U, 0x17c7d0e3U, 0x069395f2U,
+	0xea44ae1eU, 0x72fa8886U, 0x5d1d40a9U, 0xff15ea0bU,
+	0x5392c1a7U, 0xbb40fb4fU, 0x13c2d1e7U, 0x52d280a6U,
+	0xa61bbd52U, 0x4acc86beU, 0x6c610d98U, 0xa1de7f55U,
+	0xbf45fa4bU, 0xa35efd57U, 0xda78a22eU, 0xc723e433U,
+	0x2ff1dedbU, 0xaf51fe5bU, 0xca6ca63eU, 0x356f5ac1U,
+	0x7870088cU, 0x2bf4dfdfU, 0x32aa98c6U, 0x76ff8982U,
+	0x551742a1U, 0x646b0f90U, 0xb74ff843U, 0x62ee8c96U,
+	0x1a8892eeU, 0xf31ae907U, 0x23feddd7U, 0xb48f3b40U,
+	0xa9d47d5dU, 0x382018ccU, 0x081c14fcU, 0x511243a5U,
+	0x80b63674U, 0xd5b76221U, 0x015657f5U, 0x606e0e94U,
+	0x2ab49edeU, 0x342f1bc0U, 0xcb2ce73fU, 0xfd956809U,
+	0xa5db7e51U, 0xf4df2b00U, 0xe0ce2e14U, 0x100212e4U,
+	0xcce92538U, 0xae11bf5aU, 0x7fb5ca8bU, 0xadd17c59U,
+	0xa8943c5cU, 0x91e27365U, 0x5edd83aaU, 0xb9c0794dU,
+	0x450346b1U, 0xfb10eb0fU, 0x4386c5b7U, 0x7ef58b8aU,
+	0x9ded7069U, 0x652b4e91U, 0xe30eed17U, 0xeb04ef1fU,
+	0xfcd52908U, 0x194851edU, 0xed816c19U, 0xa49b3f50U,
+	0x5f9dc2abU, 0xf0da2a04U, 0xa09e3e54U, 0x9362f167U,
+	0x217e5fd5U, 0xb20ab846U, 0x001616f4U, 0xaa14be5eU,
+	0x28341cdcU, 0x8f79f67bU, 0xa75bfc53U, 0x85f37671U,
+	0x1fcdd2ebU, 0x66eb8d92U, 0x128290e6U, 0x9627b162U,
+	0x7c750988U, 0x444307b0U, 0xf71fe803U, 0x3ea59bcaU,
+	0x505202a4U, 0xdcfd2128U, 0x114253e5U, 0x9767f063U,
+	0x27fbdcd3U, 0xd4f72320U, 0x8236b476U, 0xe24eac16U,
+	0x9e2db36aU, 0x9a28b26eU, 0x46c385b2U, 0x6ee18f9aU,
+	0x1d4d50e9U, 0x4783c4b3U, 0x95e77261U, 0x612e4f95U,
+	0xc5a36631U, 0x5c5d01a8U, 0xd9b8612dU, 0xd677a122U,
+	0x6fa1ce9bU, 0x4c4905b8U, 0x5ad882aeU, 0x2eb19fdaU,
+	0x5797c0a3U, 0x6ba4cf9fU, 0x42c684b6U, 0x484c04bcU,
+	0x03d6d5f7U, 0x2c311dd8U, 0xef01ee1bU, 0x095c55fdU,
+	0xbdc57849U, 0x1c0d11e8U, 0xc663a532U, 0xddbd6029U,
+	0x9f6df26bU, 0xfa50aa0eU, 0x68640c9cU, 0x63aecd97U,
+	0xc9ac653dU, 0xbc853948U, 0xf990690dU, 0xe9846d1dU,
+	0x0d5954f9U, 0x8e39b77aU, 0x753f4a81U, 0x029694f6U,
+	0x1bc8d3efU, 0x713a4b85U, 0xf65fa902U, 0xd272a026U,
+	0xbe05bb4aU, 0xcda96439U, 0xecc12d18U, 0x0fd9d6fbU,
+	0x3c2519c8U, 0x9222b066U, 0x302a1ac4U, 0x396059cdU,
+	0x4b8cc7bfU, 0xe8c42c1cU, 0xde7da32aU, 0x98a8306cU,
+	0xc326e537U, 0x585800acU, 0x1e8d93eaU, 0xab54ff5fU,
+	0xce69a73aU, 0x90a23264U, 0x56d781a2U, 0xd332e127U,
+	0x747f0b80U, 0x041317f0U, 0xcf29e63bU, 0x0a9c96feU,
+	0x617f1e90U, 0x78215989U, 0x9dbc216cU, 0xc1f73630U,
+	0x090d04f8U, 0xbd94294cU, 0xce38f63fU, 0xe49a7e15U,
+	0x62bfdd93U, 0xe09f7f11U, 0x6ab5df9bU, 0x313b0ac0U,
+	0x9a79e36bU, 0xd5e63324U, 0x8a6de77bU, 0x2ee0cedfU,
+	0x9676e067U, 0xa70aad56U, 0x500353a1U, 0xe35fbc12U,
+	0x9273e163U, 0xc0b77731U, 0x2ae5cfdbU, 0x353e0bc4U,
+	0x90f36361U, 0x2ba58edaU, 0x76aed887U, 0xa24fed53U,
+	0x1f9c83eeU, 0x206f4fd1U, 0x22efcdd3U, 0x5c0c50adU,
+	0x481d55b9U, 0x33bb88c2U, 0x7be19a8aU, 0xe9d53c18U,
+	0x7c24588dU, 0x85a22774U, 0x580951a9U, 0xd363b022U,
+	0xf60ef807U, 0x6bf59e9aU, 0x16d6c0e7U, 0x37be89c6U,
+	0x8da8257cU, 0x050207f4U, 0x88ed6579U, 0xb31ba842U,
+	0xa9852c58U, 0xf48e7a05U, 0x4a9dd7bbU, 0x9cfc606dU,
+	0x1d1c01ecU, 0x1ad9c3ebU, 0x99b92068U, 0x6c305c9dU,
+	0x387149c9U, 0x455217b4U, 0x68355d99U, 0xd1e33220U,
+	0xef50bf1eU, 0x044246f5U, 0xc632f437U, 0xa0cf6f51U,
+	0x27aa8dd6U, 0x26eaccd7U, 0xa4ca6e55U, 0xc9fd3438U,
+	0xf5ce3b04U, 0xf74eb906U, 0x66badc97U, 0xd0a37321U,
+	0x347e4ac5U, 0x5fcc93aeU, 0xca3df73bU, 0x5e8cd2afU,
+	0xa18f2e50U, 0x2c604cddU, 0x078285f6U, 0x98f96169U,
+	0x9b39a26aU, 0x9736a166U, 0x3c7448cdU, 0xf08b7b01U,
+	0xff44bb0eU, 0xab05ae5aU, 0x514312a0U, 0x145642e5U,
+	0xd9e93028U, 0x95b62364U, 0x06c2c4f7U, 0x179681e6U,
+	0xfb41ba0aU, 0x63ff9c92U, 0x4c1854bdU, 0xee10fe1fU,
+	0x4297d5b3U, 0xaa45ef5bU, 0x02c7c5f3U, 0x43d794b2U,
+	0xb71ea946U, 0x5bc992aaU, 0x7d64198cU, 0xb0db6b41U,
+	0xae40ee5fU, 0xb25be943U, 0xcb7db63aU, 0xd626f027U,
+	0x3ef4cacfU, 0xbe54ea4fU, 0xdb69b22aU, 0x246a4ed5U,
+	0x69751c98U, 0x3af1cbcbU, 0x23af8cd2U, 0x67fa9d96U,
+	0x441256b5U, 0x756e1b84U, 0xa64aec57U, 0x73eb9882U,
+	0x0b8d86faU, 0xe21ffd13U, 0x32fbc9c3U, 0xa58a2f54U,
+	0xb8d16949U, 0x29250cd8U, 0x191900e8U, 0x401757b1U,
+	0x91b32260U, 0xc4b27635U, 0x105343e1U, 0x716b1a80U,
+	0x3bb18acaU, 0x252a0fd4U, 0xda29f32bU, 0xec907c1dU,
+	0xb4de6a45U, 0xe5da3f14U, 0xf1cb3a00U, 0x010706f0U,
+	0xddec312cU, 0xbf14ab4eU, 0x6eb0de9fU, 0xbcd4684dU,
+	0xb9912848U, 0x80e76771U, 0x4fd897beU, 0xa8c56d59U,
+	0x540652a5U, 0xea15ff1bU, 0x5283d1a3U, 0x6ff09f9eU,
+	0x8ce8647dU, 0x742e5a85U, 0xf20bf903U, 0xfa01fb0bU,
+	0xedd03d1cU, 0x084d45f9U, 0xfc84780dU, 0xb59e2b44U,
+	0x4e98d6bfU, 0xe1df3e10U, 0xb19b2a40U, 0x8267e573U,
+	0x307b4bc1U, 0xa30fac52U, 0x111302e0U, 0xbb11aa4aU,
+	0x393108c8U, 0x9e7ce26fU, 0xb65ee847U, 0x94f66265U,
+	0x0ec8c6ffU, 0x77ee9986U, 0x038784f2U, 0x8722a576U,
+	0x6d701d9cU, 0x554613a4U, 0xe61afc17U, 0x2fa08fdeU,
+	0x415716b0U, 0xcdf8353cU, 0x004747f1U, 0x8662e477U,
+	0x36fec8c7U, 0xc5f23734U, 0x9333a062U, 0xf34bb802U,
+	0x8f28a77eU, 0x8b2da67aU, 0x57c691a6U, 0x7fe49b8eU,
+	0x0c4844fdU, 0x5686d0a7U, 0x84e26675U, 0x702b5b81U,
+	0xd4a67225U, 0x4d5815bcU, 0xc8bd7539U, 0xc772b536U,
+	0x7ea4da8fU, 0x5d4c11acU, 0x4bdd96baU, 0x3fb48bceU,
+	0x4692d4b7U, 0x7aa1db8bU, 0x53c390a2U, 0x594910a8U,
+	0x12d3c1e3U, 0x3d3409ccU, 0xfe04fa0fU, 0x185941e9U,
+	0xacc06c5dU, 0x0d0805fcU, 0xd766b126U, 0xccb8743dU,
+	0x8e68e67fU, 0xeb55be1aU, 0x79611888U, 0x72abd983U,
+	0xd8a97129U, 0xad802d5cU, 0xe8957d19U, 0xf8817909U,
+	0x1c5c40edU, 0x9f3ca36eU, 0x643a5e95U, 0x139380e2U,
+	0x0acdc7fbU, 0x603f5f91U, 0xe75abd16U, 0xc377b432U,
+	0xaf00af5eU, 0xdcac702dU, 0xfdc4390cU, 0x1edcc2efU,
+	0x2d200ddcU, 0x8327a472U, 0x212f0ed0U, 0x28654dd9U,
+	0x5a89d3abU, 0xf9c13808U, 0xcf78b73eU, 0x89ad2478U,
+	0xd223f123U, 0x495d14b8U, 0x0f8887feU, 0xba51eb4bU,
+	0xdf6cb32eU, 0x81a72670U, 0x47d295b6U, 0xc237f533U,
+	0x657a1f94U, 0x151603e4U, 0xde2cf22fU, 0x1b9982eaU,
+	0xad43ee60U, 0xb41da979U, 0x5180d19cU, 0x0dcbc6c0U,
+	0xc531f408U, 0x71a8d9bcU, 0x020406cfU, 0x28a68ee5U,
+	0xae832d63U, 0x2ca38fe1U, 0xa6892f6bU, 0xfd07fa30U,
+	0x5645139bU, 0x19dac3d4U, 0x4651178bU, 0xe2dc3e2fU,
+	0x5a4a1097U, 0x6b365da6U, 0x9c3fa351U, 0x2f634ce2U,
+	0x5e4f1193U, 0x0c8b87c1U, 0xe6d93f2bU, 0xf902fb34U,
+	0x5ccf9391U, 0xe7997e2aU, 0xba922877U, 0x6e731da3U,
+	0xd3a0731eU, 0xec53bf21U, 0xeed33d23U, 0x9030a05dU,
+	0x8421a549U, 0xff877832U, 0xb7dd6a7aU, 0x25e9cce8U,
+	0xb018a87dU, 0x499ed784U, 0x9435a159U, 0x1f5f40d2U,
+	0x3a3208f7U, 0xa7c96e6aU, 0xdaea3017U, 0xfb827936U,
+	0x4194d58cU, 0xc93ef704U, 0x44d19589U, 0x7f2758b2U,
+	0x65b9dca8U, 0x38b28af5U, 0x86a1274bU, 0x50c0909dU,
+	0xd120f11cU, 0xd6e5331bU, 0x5585d098U, 0xa00cac6dU,
+	0xf44db939U, 0x896ee744U, 0xa409ad69U, 0x1ddfc2d0U,
+	0x236c4feeU, 0xc87eb605U, 0x0a0e04c7U, 0x6cf39fa1U,
+	0xeb967d26U, 0xead63c27U, 0x68f69ea5U, 0x05c1c4c8U,
+	0x39f2cbf4U, 0x3b7249f6U, 0xaa862c67U, 0x1c9f83d1U,
+	0xf842ba35U, 0x93f0635eU, 0x060107cbU, 0x92b0225fU,
+	0x6db3dea0U, 0xe05cbc2dU, 0xcbbe7506U, 0x54c59199U,
+	0x5705529aU, 0x5b0a5196U, 0xf048b83dU, 0x3cb78bf1U,
+	0x33784bfeU, 0x67395eaaU, 0x9d7fe250U, 0xd86ab215U,
+	0x15d5c0d8U, 0x598ad394U, 0xcafe3407U, 0xdbaa7116U,
+	0x377d4afaU, 0xafc36c62U, 0x8024a44dU, 0x222c0eefU,
+	0x8eab2543U, 0x66791fabU, 0xcefb3503U, 0x8feb6442U,
+	0x7b2259b6U, 0x97f5625aU, 0xb158e97cU, 0x7ce79bb1U,
+	0x627c1eafU, 0x7e6719b3U, 0x074146caU, 0x1a1a00d7U,
+	0xf2c83a3fU, 0x72681abfU, 0x175542daU, 0xe856be25U,
+	0xa549ec68U, 0xf6cd3b3bU, 0xef937c22U, 0xabc66d66U,
+	0x882ea645U, 0xb952eb74U, 0x6a761ca7U, 0xbfd76872U,
+	0xc7b1760aU, 0x2e230de3U, 0xfec73933U, 0x69b6dfa4U,
+	0x74ed99b9U, 0xe519fc28U, 0xd525f018U, 0x8c2ba741U,
+	0x5d8fd290U, 0x088e86c5U, 0xdc6fb311U, 0xbd57ea70U,
+	0xf78d7a3aU, 0xe916ff24U, 0x161503dbU, 0x20ac8cedU,
+	0x78e29ab5U, 0x29e6cfe4U, 0x3df7caf0U, 0xcd3bf600U,
+	0x11d0c1dcU, 0x73285bbeU, 0xa28c2e6fU, 0x70e898bdU,
+	0x75add8b8U, 0x4cdb9781U, 0x83e4674eU, 0x64f99da9U,
+	0x983aa255U, 0x26290febU, 0x9ebf2153U, 0xa3cc6f6eU,
+	0x40d4948dU, 0xb812aa75U, 0x3e3709f3U, 0x363d0bfbU,
+	0x21eccdecU, 0xc471b509U, 0x30b888fdU, 0x79a2dbb4U,
+	0x82a4264fU, 0x2de3cee0U, 0x7da7dab0U, 0x4e5b1583U,
+	0xfc47bb31U, 0x6f335ca2U, 0xdd2ff210U, 0x772d5abaU,
+	0xf50df838U, 0x5240129fU, 0x7a6218b7U, 0x58ca9295U,
+	0xc2f4360fU, 0xbbd26976U, 0xcfbb7402U, 0x4b1e5586U,
+	0xa14ced6cU, 0x997ae354U, 0x2a260ce7U, 0xe39c7f2eU,
+	0x8d6be640U, 0x01c4c5ccU, 0xcc7bb701U, 0x4a5e1487U,
+	0xfac23837U, 0x09cec7c4U, 0x5f0f5092U, 0x3f7748f2U,
+	0x4314578eU, 0x4711568aU, 0x9bfa6156U, 0xb3d86b7eU,
+	0xc074b40dU, 0x9aba2057U, 0x48de9685U, 0xbc17ab71U,
+	0x189a82d5U, 0x8164e54cU, 0x048185c9U, 0x0b4e45c6U,
+	0xb2982a7fU, 0x9170e15cU, 0x87e1664aU, 0xf3887b3eU,
+	0x8aae2447U, 0xb69d2b7bU, 0x9fff6052U, 0x9575e058U,
+	0xdeef3113U, 0xf108f93cU, 0x32380affU, 0xd465b119U,
+	0x60fc9cadU, 0xc134f50cU, 0x1b5a41d6U, 0x008484cdU,
+	0x4254168fU, 0x27694eeaU, 0xb55de878U, 0xbe972973U,
+	0x149581d9U, 0x61bcddacU, 0x24a98de9U, 0x34bd89f9U,
+	0xd060b01dU, 0x5300539eU, 0xa806ae65U, 0xdfaf7012U,
+	0xc6f1370bU, 0xac03af61U, 0x2b664de6U, 0x0f4b44c2U,
+	0x633c5faeU, 0x109080ddU, 0x31f8c9fcU, 0xd2e0321fU,
+	0xe11cfd2cU, 0x4f1b5482U, 0xed13fe20U, 0xe459bd29U,
+	0x96b5235bU, 0x35fdc8f8U, 0x034447ceU, 0x4591d488U,
+	0x1e1f01d3U, 0x8561e448U, 0xc3b4770eU, 0x766d1bbbU,
+	0x135043deU, 0x4d9bd680U, 0x8bee6546U, 0x0e0b05c3U,
+	0xa946ef64U, 0xd92af314U, 0x121002dfU, 0xd7a5721aU,
+	0x467432bcU, 0x5f2a75a5U, 0xbab70d40U, 0xe6fc1a1cU,
+	0x2e0628d4U, 0x9a9f0560U, 0xe933da13U, 0xc3915239U,
+	0x45b4f1bfU, 0xc794533dU, 0x4dbef3b7U, 0x163026ecU,
+	0xbd72cf47U, 0xf2ed1f08U, 0xad66cb57U, 0x09ebe2f3U,
+	0xb17dcc4bU, 0x8001817aU, 0x77087f8dU, 0xc454903eU,
+	0xb578cd4fU, 0xe7bc5b1dU, 0x0deee3f7U, 0x123527e8U,
+	0xb7f84f4dU, 0x0caea2f6U, 0x51a5f4abU, 0x8544c17fU,
+	0x3897afc2U, 0x076463fdU, 0x05e4e1ffU, 0x7b077c81U,
+	0x6f167995U, 0x14b0a4eeU, 0x5ceab6a6U, 0xcede1034U,
+	0x5b2f74a1U, 0xa2a90b58U, 0x7f027d85U, 0xf4689c0eU,
+	0xd105d42bU, 0x4cfeb2b6U, 0x31ddeccbU, 0x10b5a5eaU,
+	0xaaa30950U, 0x22092bd8U, 0xafe64955U, 0x9410846eU,
+	0x8e8e0074U, 0xd3855629U, 0x6d96fb97U, 0xbbf74c41U,
+	0x3a172dc0U, 0x3dd2efc7U, 0xbeb20c44U, 0x4b3b70b1U,
+	0x1f7a65e5U, 0x62593b98U, 0x4f3e71b5U, 0xf6e81e0cU,
+	0xc85b9332U, 0x23496ad9U, 0xe139d81bU, 0x87c4437dU,
+	0x00a1a1faU, 0x01e1e0fbU, 0x83c14279U, 0xeef61814U,
+	0xd2c51728U, 0xd045952aU, 0x41b1f0bbU, 0xf7a85f0dU,
+	0x137566e9U, 0x78c7bf82U, 0xed36db17U, 0x7987fe83U,
+	0x8684027cU, 0x0b6b60f1U, 0x2089a9daU, 0xbff24d45U,
+	0xbc328e46U, 0xb03d8d4aU, 0x1b7f64e1U, 0xd780572dU,
+	0xd84f9722U, 0x8c0e8276U, 0x76483e8cU, 0x335d6ec9U,
+	0xfee21c04U, 0xb2bd0f48U, 0x21c9e8dbU, 0x309dadcaU,
+	0xdc4a9626U, 0x44f4b0beU, 0x6b137891U, 0xc91bd233U,
+	0x659cf99fU, 0x8d4ec377U, 0x25cce9dfU, 0x64dcb89eU,
+	0x9015856aU, 0x7cc2be86U, 0x5a6f35a0U, 0x97d0476dU,
+	0x894bc273U, 0x9550c56fU, 0xec769a16U, 0xf12ddc0bU,
+	0x19ffe6e3U, 0x995fc663U, 0xfc629e06U, 0x036162f9U,
+	0x4e7e30b4U, 0x1dfae7e7U, 0x04a4a0feU, 0x40f1b1baU,
+	0x63197a99U, 0x526537a8U, 0x8141c07bU, 0x54e0b4aeU,
+	0x2c86aad6U, 0xc514d13fU, 0x15f0e5efU, 0x82810378U,
+	0x9fda4565U, 0x0e2e20f4U, 0x3e122cc4U, 0x671c7b9dU,
+	0xb6b80e4cU, 0xe3b95a19U, 0x37586fcdU, 0x566036acU,
+	0x1cbaa6e6U, 0x022123f8U, 0xfd22df07U, 0xcb9b5031U,
+	0x93d54669U, 0xc2d11338U, 0xd6c0162cU, 0x260c2adcU,
+	0xfae71d00U, 0x981f8762U, 0x49bbf2b3U, 0x9bdf4461U,
+	0x9e9a0464U, 0xa7ec4b5dU, 0x68d3bb92U, 0x8fce4175U,
+	0x730d7e89U, 0xcd1ed337U, 0x7588fd8fU, 0x48fbb3b2U,
+	0xabe34851U, 0x532576a9U, 0xd500d52fU, 0xdd0ad727U,
+	0xcadb1130U, 0x2f4669d5U, 0xdb8f5421U, 0x92950768U,
+	0x6993fa93U, 0xc6d4123cU, 0x9690066cU, 0xa56cc95fU,
+	0x177067edU, 0x8404807eU, 0x36182eccU, 0x9c1a8666U,
+	0x1e3a24e4U, 0xb977ce43U, 0x9155c46bU, 0xb3fd4e49U,
+	0x29c3ead3U, 0x50e5b5aaU, 0x248ca8deU, 0xa029895aU,
+	0x4a7b31b0U, 0x724d3f88U, 0xc111d03bU, 0x08aba3f2U,
+	0x665c3a9cU, 0xeaf31910U, 0x274c6bddU, 0xa169c85bU,
+	0x11f5e4ebU, 0xe2f91b18U, 0xb4388c4eU, 0xd440942eU,
+	0xa8238b52U, 0xac268a56U, 0x70cdbd8aU, 0x58efb7a2U,
+	0x2b4368d1U, 0x718dfc8bU, 0xa3e94a59U, 0x572077adU,
+	0xf3ad5e09U, 0x6a533990U, 0xefb65915U, 0xe079991aU,
+	0x59aff6a3U, 0x7a473d80U, 0x6cd6ba96U, 0x18bfa7e2U,
+	0x6199f89bU, 0x5daaf7a7U, 0x74c8bc8eU, 0x7e423c84U,
+	0x35d8edcfU, 0x1a3f25e0U, 0xd90fd623U, 0x3f526dc5U,
+	0x8bcb4071U, 0x2a0329d0U, 0xf06d9d0aU, 0xebb35811U,
+	0xa963ca53U, 0xcc5e9236U, 0x5e6a34a4U, 0x55a0f5afU,
+	0xffa25d05U, 0x8a8b0170U, 0xcf9e5135U, 0xdf8a5525U,
+	0x3b576cc1U, 0xb8378f42U, 0x433172b9U, 0x3498acceU,
+	0x2dc6ebd7U, 0x473473bdU, 0xc051913aU, 0xe47c981eU,
+	0x880b8372U, 0xfba75c01U, 0xdacf1520U, 0x39d7eec3U,
+	0x0a2b21f0U, 0xa42c885eU, 0x062422fcU, 0x0f6e61f5U,
+	0x7d82ff87U, 0xdeca1424U, 0xe8739b12U, 0xaea60854U,
+	0xf528dd0fU, 0x6e563894U, 0x2883abd2U, 0x9d5ac767U,
+	0xf8679f02U, 0xa6ac0a5cU, 0x60d9b99aU, 0xe53cd91fU,
+	0x427133b8U, 0x321d2fc8U, 0xf927de03U, 0x3c92aec6U,
+	0xbeee50deU, 0xa7b017c7U, 0x422d6f22U, 0x1e66787eU,
+	0xd69c4ab6U, 0x62056702U, 0x11a9b871U, 0x3b0b305bU,
+	0xbd2e93ddU, 0x3f0e315fU, 0xb52491d5U, 0xeeaa448eU,
+	0x45e8ad25U, 0x0a777d6aU, 0x55fca935U, 0xf1718091U,
+	0x49e7ae29U, 0x789be318U, 0x8f921defU, 0x3ccef25cU,
+	0x4de2af2dU, 0x1f26397fU, 0xf5748195U, 0xeaaf458aU,
+	0x4f622d2fU, 0xf434c094U, 0xa93f96c9U, 0x7ddea31dU,
+	0xc00dcda0U, 0xfffe019fU, 0xfd7e839dU, 0x839d1ee3U,
+	0x978c1bf7U, 0xec2ac68cU, 0xa470d4c4U, 0x36447256U,
+	0xa3b516c3U, 0x5a33693aU, 0x87981fe7U, 0x0cf2fe6cU,
+	0x299fb649U, 0xb464d0d4U, 0xc9478ea9U, 0xe82fc788U,
+	0x52396b32U, 0xda9349baU, 0x577c2b37U, 0x6c8ae60cU,
+	0x76146216U, 0x2b1f344bU, 0x950c99f5U, 0x436d2e23U,
+	0xc28d4fa2U, 0xc5488da5U, 0x46286e26U, 0xb3a112d3U,
+	0xe7e00787U, 0x9ac359faU, 0xb7a413d7U, 0x0e727c6eU,
+	0x30c1f150U, 0xdbd308bbU, 0x19a3ba79U, 0x7f5e211fU,
+	0xf83bc398U, 0xf97b8299U, 0x7b5b201bU, 0x166c7a76U,
+	0x2a5f754aU, 0x28dff748U, 0xb92b92d9U, 0x0f323d6fU,
+	0xebef048bU, 0x805ddde0U, 0x15acb975U, 0x811d9ce1U,
+	0x7e1e601eU, 0xf3f10293U, 0xd813cbb8U, 0x47682f27U,
+	0x44a8ec24U, 0x48a7ef28U, 0xe3e50683U, 0x2f1a354fU,
+	0x20d5f540U, 0x7494e014U, 0x8ed25ceeU, 0xcbc70cabU,
+	0x06787e66U, 0x4a276d2aU, 0xd9538ab9U, 0xc807cfa8U,
+	0x24d0f444U, 0xbc6ed2dcU, 0x93891af3U, 0x3181b051U,
+	0x9d069bfdU, 0x75d4a115U, 0xdd568bbdU, 0x9c46dafcU,
+	0x688fe708U, 0x8458dce4U, 0xa2f557c2U, 0x6f4a250fU,
+	0x71d1a011U, 0x6dcaa70dU, 0x14ecf874U, 0x09b7be69U,
+	0xe1658481U, 0x61c5a401U, 0x04f8fc64U, 0xfbfb009bU,
+	0xb6e452d6U, 0xe5608585U, 0xfc3ec29cU, 0xb86bd3d8U,
+	0x9b8318fbU, 0xaaff55caU, 0x79dba219U, 0xac7ad6ccU,
+	0xd41cc8b4U, 0x3d8eb35dU, 0xed6a878dU, 0x7a1b611aU,
+	0x67402707U, 0xf6b44296U, 0xc6884ea6U, 0x9f8619ffU,
+	0x4e226c2eU, 0x1b23387bU, 0xcfc20dafU, 0xaefa54ceU,
+	0xe420c484U, 0xfabb419aU, 0x05b8bd65U, 0x33013253U,
+	0x6b4f240bU, 0x3a4b715aU, 0x2e5a744eU, 0xde9648beU,
+	0x027d7f62U, 0x6085e500U, 0xb12190d1U, 0x63452603U,
+	0x66006606U, 0x5f76293fU, 0x9049d9f0U, 0x77542317U,
+	0x8b971cebU, 0x3584b155U, 0x8d129fedU, 0xb061d1d0U,
+	0x53792a33U, 0xabbf14cbU, 0x2d9ab74dU, 0x2590b545U,
+	0x32417352U, 0xd7dc0bb7U, 0x23153643U, 0x6a0f650aU,
+	0x910998f1U, 0x3e4e705eU, 0x6e0a640eU, 0x5df6ab3dU,
+	0xefea058fU, 0x7c9ee21cU, 0xce824caeU, 0x6480e404U,
+	0xe6a04686U, 0x41edac21U, 0x69cfa609U, 0x4b672c2bU,
+	0xd15988b1U, 0xa87fd7c8U, 0xdc16cabcU, 0x58b3eb38U,
+	0xb2e153d2U, 0x8ad75deaU, 0x398bb259U, 0xf031c190U,
+	0x9ec658feU, 0x12697b72U, 0xdfd609bfU, 0x59f3aa39U,
+	0xe96f8689U, 0x1a63797aU, 0x4ca2ee2cU, 0x2cdaf64cU,
+	0x50b9e930U, 0x54bce834U, 0x8857dfe8U, 0xa075d5c0U,
+	0xd3d90ab3U, 0x89179ee9U, 0x5b73283bU, 0xafba15cfU,
+	0x0b373c6bU, 0x92c95bf2U, 0x172c3b77U, 0x18e3fb78U,
+	0xa13594c1U, 0x82dd5fe2U, 0x944cd8f4U, 0xe025c580U,
+	0x99039af9U, 0xa53095c5U, 0x8c52deecU, 0x86d85ee6U,
+	0xcd428fadU, 0xe2a54782U, 0x2195b441U, 0xc7c80fa7U,
+	0x73512213U, 0xd2994bb2U, 0x08f7ff68U, 0x13293a73U,
+	0x51f9a831U, 0x34c4f054U, 0xa6f056c6U, 0xad3a97cdU,
+	0x07383f67U, 0x72116312U, 0x37043357U, 0x27103747U,
+	0xc3cd0ea3U, 0x40aded20U, 0xbbab10dbU, 0xcc02ceacU,
+	0xd55c89b5U, 0xbfae11dfU, 0x38cbf358U, 0x1ce6fa7cU,
+	0x7091e110U, 0x033d3e63U, 0x22557742U, 0xc14d8ca1U,
+	0xf2b14392U, 0x5cb6ea3cU, 0xfebe409eU, 0xf7f40397U,
+	0x85189de5U, 0x26507646U, 0x10e9f970U, 0x563c6a36U,
+	0x0db2bf6dU, 0x96cc5af6U, 0xd019c9b0U, 0x65c0a505U,
+	0x00fdfd60U, 0x5e36683eU, 0x9843dbf8U, 0x1da6bb7dU,
+	0xbaeb51daU, 0xca874daaU, 0x01bdbc61U, 0xc408cca4U,
+	0x1a9b810fU, 0x03c5c616U, 0xe658bef3U, 0xba13a9afU,
+	0x72e99b67U, 0xc670b6d3U, 0xb5dc69a0U, 0x9f7ee18aU,
+	0x195b420cU, 0x9b7be08eU, 0x11514004U, 0x4adf955fU,
+	0xe19d7cf4U, 0xae02acbbU, 0xf18978e4U, 0x55045140U,
+	0xed927ff8U, 0xdcee32c9U, 0x2be7cc3eU, 0x98bb238dU,
+	0xe9977efcU, 0xbb53e8aeU, 0x51015044U, 0x4eda945bU,
+	0xeb17fcfeU, 0x50411145U, 0x0d4a4718U, 0xd9ab72ccU,
+	0x64781c71U, 0x5b8bd04eU, 0x590b524cU, 0x27e8cf32U,
+	0x33f9ca26U, 0x485f175dU, 0x00050515U, 0x9231a387U,
+	0x07c0c712U, 0xfe46b8ebU, 0x23edce36U, 0xa8872fbdU,
+	0x8dea6798U, 0x10110105U, 0x6d325f78U, 0x4c5a1659U,
+	0xf64cbae3U, 0x7ee6986bU, 0xf309fae6U, 0xc8ff37ddU,
+	0xd261b3c7U, 0x8f6ae59aU, 0x31794824U, 0xe718fff2U,
+	0x66f89e73U, 0x613d5c74U, 0xe25dbff7U, 0x17d4c302U,
+	0x4395d656U, 0x3eb6882bU, 0x13d1c206U, 0xaa07adbfU,
+	0x94b42081U, 0x7fa6d96aU, 0xbdd66ba8U, 0xdb2bf0ceU,
+	0x5c4e1249U, 0x5d0e5348U, 0xdf2ef1caU, 0xb219aba7U,
+	0x8e2aa49bU, 0x8caa2699U, 0x1d5e4308U, 0xab47ecbeU,
+	0x4f9ad55aU, 0x24280c31U, 0xb1d968a4U, 0x25684d30U,
+	0xda6bb1cfU, 0x5784d342U, 0x7c661a69U, 0xe31dfef6U,
+	0xe0dd3df5U, 0xecd23ef9U, 0x4790d752U, 0x8b6fe49eU,
+	0x84a02491U, 0xd0e131c5U, 0x2aa78d3fU, 0x6fb2dd7aU,
+	0xa20dafb7U, 0xee52bcfbU, 0x7d265b68U, 0x6c721e79U,
+	0x80a52595U, 0x181b030dU, 0x37fccb22U, 0x95f46180U,
+	0x39734a2cU, 0xd1a170c4U, 0x79235a6cU, 0x38330b2dU,
+	0xccfa36d9U, 0x202d0d35U, 0x06808613U, 0xcb3ff4deU,
+	0xd5a471c0U, 0xc9bf76dcU, 0xb09929a5U, 0xadc26fb8U,
+	0x45105550U, 0xc5b075d0U, 0xa08d2db5U, 0x5f8ed14aU,
+	0x12918307U, 0x41155454U, 0x584b134dU, 0x1c1e0209U,
+	0x3ff6c92aU, 0x0e8a841bU, 0xddae73c8U, 0x080f071dU,
+	0x70691965U, 0x99fb628cU, 0x491f565cU, 0xde6eb0cbU,
+	0xc335f6d6U, 0x52c19347U, 0x62fd9f77U, 0x3bf3c82eU,
+	0xea57bdffU, 0xbf56e9aaU, 0x6bb7dc7eU, 0x0a8f851fU,
+	0x40551555U, 0x5ece904bU, 0xa1cd6cb4U, 0x9774e382U,
+	0xcf3af5daU, 0x9e3ea08bU, 0x8a2fa59fU, 0x7ae3996fU,
+	0xa608aeb3U, 0xc4f034d1U, 0x15544100U, 0xc730f7d2U,
+	0xc275b7d7U, 0xfb03f8eeU, 0x343c0821U, 0xd321f2c6U,
+	0x2fe2cd3aU, 0x91f16084U, 0x29674e3cU, 0x14140001U,
+	0xf70cfbe2U, 0x0fcac51aU, 0x89ef669cU, 0x81e56494U,
+	0x9634a283U, 0x73a9da66U, 0x8760e792U, 0xce7ab4dbU,
+	0x357c4920U, 0x9a3ba18fU, 0xca7fb5dfU, 0xf9837aecU,
+	0x4b9fd45eU, 0xd8eb33cdU, 0x6af79d7fU, 0xc0f535d5U,
+	0x42d59757U, 0xe5987df0U, 0xcdba77d8U, 0xef12fdfaU,
+	0x752c5960U, 0x0c0a0619U, 0x78631b6dU, 0xfcc63ae9U,
+	0x16948203U, 0x2ea28c3bU, 0x9dfe6388U, 0x54441041U,
+	0x3ab3892fU, 0xb61caaa3U, 0x7ba3d86eU, 0xfd867be8U,
+	0x4d1a5758U, 0xbe16a8abU, 0xe8d73ffdU, 0x88af279dU,
+	0xf4cc38e1U, 0xf0c939e5U, 0x2c220e39U, 0x04000411U,
+	0x77acdb62U, 0x2d624f38U, 0xff06f9eaU, 0x0bcfc41eU,
+	0xaf42edbaU, 0x36bc8a23U, 0xb359eaa6U, 0xbc962aa9U,
+	0x05404510U, 0x26a88e33U, 0x30390925U, 0x44501451U,
+	0x3d764b28U, 0x01454414U, 0x28270f3dU, 0x22ad8f37U,
+	0x69375e7cU, 0x46d09653U, 0x85e06590U, 0x63bdde76U,
+	0xd724f3c2U, 0x76ec9a63U, 0xac822eb9U, 0xb75ceba2U,
+	0xf58c79e0U, 0x90b12185U, 0x02858717U, 0x094f461cU,
+	0xa34deeb6U, 0xd664b2c3U, 0x9371e286U, 0x8365e696U,
+	0x67b8df72U, 0xe4d83cf1U, 0x1fdec10aU, 0x68771f7dU,
+	0x71295864U, 0x1bdbc00eU, 0x9cbe2289U, 0xb8932badU,
+	0xd4e430c1U, 0xa748efb2U, 0x8620a693U, 0x65385d70U,
+	0x56c49243U, 0xf8c33bedU, 0x5acb914fU, 0x5381d246U,
+	0x216d4c34U, 0x8225a797U, 0xb49c28a1U, 0xf249bbe7U,
+	0xa9c76ebcU, 0x32b98b27U, 0x746c1861U, 0xc1b574d4U,
+	0xa4882cb1U, 0xfa43b9efU, 0x3c360a29U, 0xb9d36aacU,
+	0x1e9e800bU, 0x6ef29c7bU, 0xa5c86db0U, 0x607d1d75U,
+	0x7e2d53ddU, 0x677314c4U, 0x82ee6c21U, 0xdea57b7dU,
+	0x165f49b5U, 0xa2c66401U, 0xd16abb72U, 0xfbc83358U,
+	0x7ded90deU, 0xffcd325cU, 0x75e792d6U, 0x2e69478dU,
+	0x852bae26U, 0xcab47e69U, 0x953faa36U, 0x31b28392U,
+	0x8924ad2aU, 0xb858e01bU, 0x4f511eecU, 0xfc0df15fU,
+	0x8d21ac2eU, 0xdfe53a7cU, 0x35b78296U, 0x2a6c4689U,
+	0x8fa12e2cU, 0x34f7c397U, 0x69fc95caU, 0xbd1da01eU,
+	0x00cecea3U, 0x3f3d029cU, 0x3dbd809eU, 0x435e1de0U,
+	0x574f18f4U, 0x2ce9c58fU, 0x64b3d7c7U, 0xf6877155U,
+	0x637615c0U, 0x9af06a39U, 0x475b1ce4U, 0xcc31fd6fU,
+	0xe95cb54aU, 0x74a7d3d7U, 0x09848daaU, 0x28ecc48bU,
+	0x92fa6831U, 0x1a504ab9U, 0x97bf2834U, 0xac49e50fU,
+	0xb6d76115U, 0xebdc3748U, 0x55cf9af6U, 0x83ae2d20U,
+	0x024e4ca1U, 0x058b8ea6U, 0x86eb6d25U, 0x736211d0U,
+	0x27230484U, 0x5a005af9U, 0x776710d4U, 0xceb17f6dU,
+	0xf002f253U, 0x1b100bb8U, 0xd960b97aU, 0xbf9d221cU,
+	0x38f8c09bU, 0x39b8819aU, 0xbb982318U, 0xd6af7975U,
+	0xea9c7649U, 0xe81cf44bU, 0x79e891daU, 0xcff13e6cU,
+	0x2b2c0788U, 0x409edee3U, 0xd56fba76U, 0x41de9fe2U,
+	0xbedd631dU, 0x33320190U, 0x18d0c8bbU, 0x87ab2c24U,
+	0x846bef27U, 0x8864ec2bU, 0x23260580U, 0xefd9364cU,
+	0xe016f643U, 0xb457e317U, 0x4e115fedU, 0x0b040fa8U,
+	0xc6bb7d65U, 0x8ae46e29U, 0x199089baU, 0x08c4ccabU,
+	0xe413f747U, 0x7cadd1dfU, 0x534a19f0U, 0xf142b352U,
+	0x5dc598feU, 0xb517a216U, 0x1d9588beU, 0x5c85d9ffU,
+	0xa84ce40bU, 0x449bdfe7U, 0x623654c1U, 0xaf89260cU,
+	0xb112a312U, 0xad09a40eU, 0xd42ffb77U, 0xc974bd6aU,
+	0x21a68782U, 0xa106a702U, 0xc43bff67U, 0x3b380398U,
+	0x762751d5U, 0x25a38686U, 0x3cfdc19fU, 0x78a8d0dbU,
+	0x5b401bf8U, 0x6a3c56c9U, 0xb918a11aU, 0x6cb9d5cfU,
+	0x14dfcbb7U, 0xfd4db05eU, 0x2da9848eU, 0xbad86219U,
+	0xa7832404U, 0x36774195U, 0x064b4da5U, 0x5f451afcU,
+	0x8ee16f2dU, 0xdbe03b78U, 0x0f010eacU, 0x6e3957cdU,
+	0x24e3c787U, 0x3a784299U, 0xc57bbe66U, 0xf3c23150U,
+	0xab8c2708U, 0xfa887259U, 0xee99774dU, 0x1e554bbdU,
+	0xc2be7c61U, 0xa046e603U, 0x71e293d2U, 0xa3862500U,
+	0xa6c36505U, 0x9fb52a3cU, 0x508adaf3U, 0xb7972014U,
+	0x4b541fe8U, 0xf547b256U, 0x4dd19ceeU, 0x70a2d2d3U,
+	0x93ba2930U, 0x6b7c17c8U, 0xed59b44eU, 0xe553b646U,
+	0xf2827051U, 0x171f08b4U, 0xe3d63540U, 0xaacc6609U,
+	0x51ca9bf2U, 0xfe8d735dU, 0xaec9670dU, 0x9d35a83eU,
+	0x2f29068cU, 0xbc5de11fU, 0x0e414fadU, 0xa443e707U,
+	0x26634585U, 0x812eaf22U, 0xa90ca50aU, 0x8ba42f28U,
+	0x119a8bb2U, 0x68bcd4cbU, 0x1cd5c9bfU, 0x9870e83bU,
+	0x722250d1U, 0x4a145ee9U, 0xf948b15aU, 0x30f2c293U,
+	0x5e055bfdU, 0xd2aa7871U, 0x1f150abcU, 0x9930a93aU,
+	0x29ac858aU, 0xdaa07a79U, 0x8c61ed2fU, 0xec19f54fU,
+	0x907aea33U, 0x947feb37U, 0x4894dcebU, 0x60b6d6c3U,
+	0x131a09b0U, 0x49d49deaU, 0x9bb02b38U, 0x6f7916ccU,
+	0xcbf43f68U, 0x520a58f1U, 0xd7ef3874U, 0xd820f87bU,
+	0x61f697c2U, 0x421e5ce1U, 0x548fdbf7U, 0x20e6c683U,
+	0x59c099faU, 0x65f396c6U, 0x4c91ddefU, 0x461b5de5U,
+	0x0d818caeU, 0x22664481U, 0xe156b742U, 0x070b0ca4U,
+	0xb3922110U, 0x125a48b1U, 0xc834fc6bU, 0xd3ea3970U,
+	0x913aab32U, 0xf407f357U, 0x663355c5U, 0x6df994ceU,
+	0xc7fb3c64U, 0xb2d26011U, 0xf7c73054U, 0xe7d33444U,
+	0x030e0da0U, 0x806eee23U, 0x7b6813d8U, 0x0cc1cdafU,
+	0x159f8ab6U, 0x7f6d12dcU, 0xf808f05bU, 0xdc25f97fU,
+	0xb052e213U, 0xc3fe3d60U, 0xe2967441U, 0x018e8fa2U,
+	0x32724091U, 0x9c75e93fU, 0x3e7d439dU, 0x37370094U,
+	0x45db9ee6U, 0xe6937545U, 0xd02afa73U, 0x96ff6935U,
+	0xcd71bc6eU, 0x560f59f5U, 0x10dacab3U, 0xa503a606U,
+	0xc03efe63U, 0x9ef56b3dU, 0x5880d8fbU, 0xdd65b87eU,
+	0x7a2852d9U, 0x0a444ea9U, 0xc17ebf62U, 0x04cbcfa7U,
+	0x3b6d56d8U, 0x223311c1U, 0xc7ae6924U, 0x9be57e78U,
+	0x531f4cb0U, 0xe7866104U, 0x942abe77U, 0xbe88365dU,
+	0x38ad95dbU, 0xba8d3759U, 0x30a797d3U, 0x6b294288U,
+	0xc06bab23U, 0x8ff47b6cU, 0xd07faf33U, 0x74f28697U,
+	0xcc64a82fU, 0xfd18e51eU, 0x0a111be9U, 0xb94df45aU,
+	0xc861a92bU, 0x9aa53f79U, 0x70f78793U, 0x6f2c438cU,
+	0xcae12b29U, 0x71b7c692U, 0x2cbc90cfU, 0xf85da51bU,
+	0x458ecba6U, 0x7a7d0799U, 0x78fd859bU, 0x061e18e5U,
+	0x120f1df1U, 0x69a9c08aU, 0x21f3d2c2U, 0xb3c77450U,
+	0x263610c5U, 0xdfb06f3cU, 0x021b19e1U, 0x8971f86aU,
+	0xac1cb04fU, 0x31e7d6d2U, 0x4cc488afU, 0x6dacc18eU,
+	0xd7ba6d34U, 0x5f104fbcU, 0xd2ff2d31U, 0xe909e00aU,
+	0xf3976410U, 0xae9c324dU, 0x108f9ff3U, 0xc6ee2825U,
+	0x470e49a4U, 0x40cb8ba3U, 0xc3ab6820U, 0x362214d5U,
+	0x62630181U, 0x1f405ffcU, 0x322715d1U, 0x8bf17a68U,
+	0xb542f756U, 0x5e500ebdU, 0x9c20bc7fU, 0xfadd2719U,
+	0x7db8c59eU, 0x7cf8849fU, 0xfed8261dU, 0x93ef7c70U,
+	0xafdc734cU, 0xad5cf14eU, 0x3ca894dfU, 0x8ab13b69U,
+	0x6e6c028dU, 0x05dedbe6U, 0x902fbf73U, 0x049e9ae7U,
+	0xfb9d6618U, 0x76720495U, 0x5d90cdbeU, 0xc2eb2921U,
+	0xc12bea22U, 0xcd24e92eU, 0x66660085U, 0xaa993349U,
+	0xa556f346U, 0xf117e612U, 0x0b515ae8U, 0x4e440aadU,
+	0x83fb7860U, 0xcfa46b2cU, 0x5cd08cbfU, 0x4d84c9aeU,
+	0xa153f242U, 0x39edd4daU, 0x160a1cf5U, 0xb402b657U,
+	0x18859dfbU, 0xf057a713U, 0x58d58dbbU, 0x19c5dcfaU,
+	0xed0ce10eU, 0x01dbdae2U, 0x277651c4U, 0xeac92309U,
+	0xf452a617U, 0xe849a10bU, 0x916ffe72U, 0x8c34b86fU,
+	0x64e68287U, 0xe446a207U, 0x817bfa62U, 0x7e78069dU,
+	0x336754d0U, 0x60e38383U, 0x79bdc49aU, 0x3de8d5deU,
+	0x1e001efdU, 0x2f7c53ccU, 0xfc58a41fU, 0x29f9d0caU,
+	0x519fceb2U, 0xb80db55bU, 0x68e9818bU, 0xff98671cU,
+	0xe2c32101U, 0x73374490U, 0x430b48a0U, 0x1a051ff9U,
+	0xcba16a28U, 0x9ea03e7dU, 0x4a410ba9U, 0x2b7952c8U,
+	0x61a3c282U, 0x7f38479cU, 0x803bbb63U, 0xb6823455U,
+	0xeecc220dU, 0xbfc8775cU, 0xabd97248U, 0x5b154eb8U,
+	0x87fe7964U, 0xe506e306U, 0x34a296d7U, 0xe6c62005U,
+	0xe3836000U, 0xdaf52f39U, 0x15cadff6U, 0xf2d72511U,
+	0x0e141aedU, 0xb007b753U, 0x089199ebU, 0x35e2d7d6U,
+	0xd6fa2c35U, 0x2e3c12cdU, 0xa819b14bU, 0xa013b343U,
+	0xb7c27554U, 0x525f0db1U, 0xa6963045U, 0xef8c630cU,
+	0x148a9ef7U, 0xbbcd7658U, 0xeb896208U, 0xd875ad3bU,
+	0x6a690389U, 0xf91de41aU, 0x4b014aa8U, 0xe103e202U,
+	0x63234080U, 0xc46eaa27U, 0xec4ca00fU, 0xcee42a2dU,
+	0x54da8eb7U, 0x2dfcd1ceU, 0x5995ccbaU, 0xdd30ed3eU,
+	0x376255d4U, 0x0f545becU, 0xbc08b45fU, 0x75b2c796U,
+	0x1b455ef8U, 0x97ea7d74U, 0x5a550fb9U, 0xdc70ac3fU,
+	0x6cec808fU, 0x9fe07f7cU, 0xc921e82aU, 0xa959f04aU,
+	0xd53aef36U, 0xd13fee32U, 0x0dd4d9eeU, 0x25f6d3c6U,
+	0x565a0cb5U, 0x0c9498efU, 0xdef02e3dU, 0x2a3913c9U,
+	0x8eb43a6dU, 0x174a5df4U, 0x92af3d71U, 0x9d60fd7eU,
+	0x24b692c7U, 0x075e59e4U, 0x11cfdef2U, 0x65a6c386U,
+	0x1c809cffU, 0x20b393c3U, 0x09d1d8eaU, 0x035b58e0U,
+	0x48c189abU, 0x67264184U, 0xa416b247U, 0x424b09a1U,
+	0xf6d22415U, 0x571a4db4U, 0x8d74f96eU, 0x96aa3c75U,
+	0xd47aae37U, 0xb147f652U, 0x237350c0U, 0x28b991cbU,
+	0x82bb3961U, 0xf7926514U, 0xb2873551U, 0xa2933141U,
+	0x464e08a5U, 0xc52eeb26U, 0x3e2816ddU, 0x4981c8aaU,
+	0x50df8fb3U, 0x3a2d17d9U, 0xbd48f55eU, 0x9965fc7aU,
+	0xf512e716U, 0x86be3865U, 0xa7d67144U, 0x44ce8aa7U,
+	0x77324594U, 0xd935ec3aU, 0x7b3d4698U, 0x72770591U,
+	0x009b9be3U, 0xa3d37040U, 0x956aff76U, 0xd3bf6c30U,
+	0x8831b96bU, 0x134f5cf0U, 0x559acfb6U, 0xe043a303U,
+	0x857efb66U, 0xdbb56e38U, 0x1dc0ddfeU, 0x9825bd7bU,
+	0x3f6857dcU, 0x4f044bacU, 0x843eba67U, 0x418bcaa2U,
+	0x4d226fe1U, 0x547c28f8U, 0xb1e1501dU, 0xedaa4741U,
+	0x25507589U, 0x91c9583dU, 0xe265874eU, 0xc8c70f64U,
+	0x4ee2ace2U, 0xccc20e60U, 0x46e8aeeaU, 0x1d667bb1U,
+	0xb624921aU, 0xf9bb4255U, 0xa630960aU, 0x02bdbfaeU,
+	0xba2b9116U, 0x8b57dc27U, 0x7c5e22d0U, 0xcf02cd63U,
+	0xbe2e9012U, 0xecea0640U, 0x06b8beaaU, 0x19637ab5U,
+	0xbcae1210U, 0x07f8ffabU, 0x5af3a9f6U, 0x8e129c22U,
+	0x33c1f29fU, 0x0c323ea0U, 0x0eb2bca2U, 0x705121dcU,
+	0x644024c8U, 0x1fe6f9b3U, 0x57bcebfbU, 0xc5884d69U,
+	0x507929fcU, 0xa9ff5605U, 0x745420d8U, 0xff3ec153U,
+	0xda538976U, 0x47a8efebU, 0x3a8bb196U, 0x1be3f8b7U,
+	0xa1f5540dU, 0x295f7685U, 0xa4b01408U, 0x9f46d933U,
+	0x85d85d29U, 0xd8d30b74U, 0x66c0a6caU, 0xb0a1111cU,
+	0x3141709dU, 0x3684b29aU, 0xb5e45119U, 0x406d2decU,
+	0x142c38b8U, 0x690f66c5U, 0x44682ce8U, 0xfdbe4351U,
+	0xc30dce6fU, 0x281f3784U, 0xea6f8546U, 0x8c921e20U,
+	0x0bf7fca7U, 0x0ab7bda6U, 0x88971f24U, 0xe5a04549U,
+	0xd9934a75U, 0xdb13c877U, 0x4ae7ade6U, 0xfcfe0250U,
+	0x18233bb4U, 0x7391e2dfU, 0xe660864aU, 0x72d1a3deU,
+	0x8dd25f21U, 0x003d3dacU, 0x2bdff487U, 0xb4a41018U,
+	0xb764d31bU, 0xbb6bd017U, 0x102939bcU, 0xdcd60a70U,
+	0xd319ca7fU, 0x8758df2bU, 0x7d1e63d1U, 0x380b3394U,
+	0xf5b44159U, 0xb9eb5215U, 0x2a9fb586U, 0x3bcbf097U,
+	0xd71ccb7bU, 0x4fa2ede3U, 0x604525ccU, 0xc24d8f6eU,
+	0x6ecaa4c2U, 0x86189e2aU, 0x2e9ab482U, 0x6f8ae5c3U,
+	0x9b43d837U, 0x7794e3dbU, 0x513968fdU, 0x9c861a30U,
+	0x821d9f2eU, 0x9e069832U, 0xe720c74bU, 0xfa7b8156U,
+	0x12a9bbbeU, 0x92099b3eU, 0xf734c35bU, 0x08373fa4U,
+	0x45286de9U, 0x16acbabaU, 0x0ff2fda3U, 0x4ba7ece7U,
+	0x684f27c4U, 0x59336af5U, 0x8a179d26U, 0x5fb6e9f3U,
+	0x27d0f78bU, 0xce428c62U, 0x1ea6b8b2U, 0x89d75e25U,
+	0x948c1838U, 0x05787da9U, 0x35447199U, 0x6c4a26c0U,
+	0xbdee5311U, 0xe8ef0744U, 0x3c0e3290U, 0x5d366bf1U,
+	0x17ecfbbbU, 0x09777ea5U, 0xf674825aU, 0xc0cd0d6cU,
+	0x98831b34U, 0xc9874e65U, 0xdd964b71U, 0x2d5a7781U,
+	0xf1b1405dU, 0x9349da3fU, 0x42edafeeU, 0x9089193cU,
+	0x95cc5939U, 0xacba1600U, 0x6385e6cfU, 0x84981c28U,
+	0x785b23d4U, 0xc6488e6aU, 0x7edea0d2U, 0x43adeeefU,
+	0xa0b5150cU, 0x58732bf4U, 0xde568872U, 0xd65c8a7aU,
+	0xc18d4c6dU, 0x24103488U, 0xd0d9097cU, 0x99c35a35U,
+	0x62c5a7ceU, 0xcd824f61U, 0x9dc65b31U, 0xae3a9402U,
+	0x1c263ab0U, 0x8f52dd23U, 0x3d4e7391U, 0x974cdb3bU,
+	0x156c79b9U, 0xb221931eU, 0x9a039936U, 0xb8ab1314U,
+	0x2295b78eU, 0x5bb3e8f7U, 0x2fdaf583U, 0xab7fd407U,
+	0x412d6cedU, 0x791b62d5U, 0xca478d66U, 0x03fdfeafU,
+	0x6d0a67c1U, 0xe1a5444dU, 0x2c1a3680U, 0xaa3f9506U,
+	0x1aa3b9b6U, 0xe9af4645U, 0xbf6ed113U, 0xdf16c973U,
+	0xa375d60fU, 0xa770d70bU, 0x7b9be0d7U, 0x53b9eaffU,
+	0x2015358cU, 0x7adba1d6U, 0xa8bf1704U, 0x5c762af0U,
+	0xf8fb0354U, 0x610564cdU, 0xe4e00448U, 0xeb2fc447U,
+	0x52f9abfeU, 0x711160ddU, 0x6780e7cbU, 0x13e9fabfU,
+	0x6acfa5c6U, 0x56fcaafaU, 0x7f9ee1d3U, 0x751461d9U,
+	0x3e8eb092U, 0x116978bdU, 0xd2598b7eU, 0x34043098U,
+	0x809d1d2cU, 0x2155748dU, 0xfb3bc057U, 0xe0e5054cU,
+	0xa235970eU, 0xc708cf6bU, 0x553c69f9U, 0x5ef6a8f2U,
+	0xf4f40058U, 0x81dd5c2dU, 0xc4c80c68U, 0xd4dc0878U,
+	0x3001319cU, 0xb361d21fU, 0x48672fe4U, 0x3fcef193U,
+	0x2690b68aU, 0x4c622ee0U, 0xcb07cc67U, 0xef2ac543U,
+	0x835dde2fU, 0xf0f1015cU, 0xd199487dU, 0x3281b39eU,
+	0x017d7cadU, 0xaf7ad503U, 0x0d727fa1U, 0x04383ca8U,
+	0x76d4a2daU, 0xd59c4979U, 0xe325c64fU, 0xa5f05509U,
+	0xfe7e8052U, 0x650065c9U, 0x23d5f68fU, 0x960c9a3aU,
+	0xf331c25fU, 0xadfa5701U, 0x6b8fe4c7U, 0xee6a8442U,
+	0x49276ee5U, 0x394b7295U, 0xf271835eU, 0x37c4f39bU,
+	0x72d2a02eU, 0x6b8ce737U, 0x8e119fd2U, 0xd25a888eU,
+	0x1aa0ba46U, 0xae3997f2U, 0xdd954881U, 0xf737c0abU,
+	0x7112632dU, 0xf332c1afU, 0x79186125U, 0x2296b47eU,
+	0x89d45dd5U, 0xc64b8d9aU, 0x99c059c5U, 0x3d4d7061U,
+	0x85db5ed9U, 0xb4a713e8U, 0x43aeed1fU, 0xf0f202acU,
+	0x81de5fddU, 0xd31ac98fU, 0x39487165U, 0x2693b57aU,
+	0x835edddfU, 0x38083064U, 0x65036639U, 0xb1e253edU,
+	0x0c313d50U, 0x33c2f16fU, 0x3142736dU, 0x4fa1ee13U,
+	0x5bb0eb07U, 0x2016367cU, 0x684c2434U, 0xfa7882a6U,
+	0x6f89e633U, 0x960f99caU, 0x4ba4ef17U, 0xc0ce0e9cU,
+	0xe5a346b9U, 0x78582024U, 0x057b7e59U, 0x24133778U,
+	0x9e059bc2U, 0x16afb94aU, 0x9b40dbc7U, 0xa0b616fcU,
+	0xba2892e6U, 0xe723c4bbU, 0x59306905U, 0x8f51ded3U,
+	0x0eb1bf52U, 0x09747d55U, 0x8a149ed6U, 0x7f9de223U,
+	0x2bdcf777U, 0x56ffa90aU, 0x7b98e327U, 0xc24e8c9eU,
+	0xfcfd01a0U, 0x17eff84bU, 0xd59f4a89U, 0xb362d1efU,
+	0x34073368U, 0x35477269U, 0xb767d0ebU, 0xda508a86U,
+	0xe66385baU, 0xe4e307b8U, 0x75176229U, 0xc30ecd9fU,
+	0x27d3f47bU, 0x4c612d10U, 0xd9904985U, 0x4d216c11U,
+	0xb22290eeU, 0x3fcdf263U, 0x142f3b48U, 0x8b54dfd7U,
+	0x88941cd4U, 0x849b1fd8U, 0x2fd9f673U, 0xe326c5bfU,
+	0xece905b0U, 0xb8a810e4U, 0x42eeac1eU, 0x07fbfc5bU,
+	0xca448e96U, 0x861b9ddaU, 0x156f7a49U, 0x043b3f58U,
+	0xe8ec04b4U, 0x7052222cU, 0x5fb5ea03U, 0xfdbd40a1U,
+	0x513a6b0dU, 0xb9e851e5U, 0x116a7b4dU, 0x507a2a0cU,
+	0xa4b317f8U, 0x48642c14U, 0x6ec9a732U, 0xa376d5ffU,
+	0xbded50e1U, 0xa1f657fdU, 0xd8d00884U, 0xc58b4e99U,
+	0x2d597471U, 0xadf954f1U, 0xc8c40c94U, 0x37c7f06bU,
+	0x7ad8a226U, 0x295c7575U, 0x3002326cU, 0x74572328U,
+	0x57bfe80bU, 0x66c3a53aU, 0xb5e752e9U, 0x6046263cU,
+	0x18203844U, 0xf1b243adU, 0x2156777dU, 0xb62791eaU,
+	0xab7cd7f7U, 0x3a88b266U, 0x0ab4be56U, 0x53bae90fU,
+	0x821e9cdeU, 0xd71fc88bU, 0x03fefd5fU, 0x62c6a43eU,
+	0x281c3474U, 0x3687b16aU, 0xc9844d95U, 0xff3dc2a3U,
+	0xa773d4fbU, 0xf67781aaU, 0xe26684beU, 0x12aab84eU,
+	0xce418f92U, 0xacb915f0U, 0x7d1d6021U, 0xaf79d6f3U,
+	0xaa3c96f6U, 0x934ad9cfU, 0x5c752900U, 0xbb68d3e7U,
+	0x47abec1bU, 0xf9b841a5U, 0x412e6f1dU, 0x7c5d2120U,
+	0x9f45dac3U, 0x6783e43bU, 0xe1a647bdU, 0xe9ac45b5U,
+	0xfe7d83a2U, 0x1be0fb47U, 0xef29c6b3U, 0xa63395faU,
+	0x5d356801U, 0xf27280aeU, 0xa23694feU, 0x91ca5bcdU,
+	0x23d6f57fU, 0xb0a212ecU, 0x02bebc5eU, 0xa8bc14f4U,
+	0x2a9cb676U, 0x8dd15cd1U, 0xa5f356f9U, 0x875bdcdbU,
+	0x1d657841U, 0x64432738U, 0x102a3a4cU, 0x948f1bc8U,
+	0x7edda322U, 0x46ebad1aU, 0xf5b742a9U, 0x3c0d3160U,
+	0x52faa80eU, 0xde558b82U, 0x13eaf94fU, 0x95cf5ac9U,
+	0x25537679U, 0xd65f898aU, 0x809e1edcU, 0xe0e606bcU,
+	0x9c8519c0U, 0x988018c4U, 0x446b2f18U, 0x6c492530U,
+	0x1fe5fa43U, 0x452b6e19U, 0x974fd8cbU, 0x6386e53fU,
+	0xc70bcc9bU, 0x5ef5ab02U, 0xdb10cb87U, 0xd4df0b88U,
+	0x6d096431U, 0x4ee1af12U, 0x58702804U, 0x2c193570U,
+	0x553f6a09U, 0x690c6535U, 0x406e2e1cU, 0x4ae4ae16U,
+	0x017e7f5dU, 0x2e99b772U, 0xeda944b1U, 0x0bf4ff57U,
+	0xbf6dd2e3U, 0x1ea5bb42U, 0xc4cb0f98U, 0xdf15ca83U,
+	0x9dc558c1U, 0xf8f800a4U, 0x6acca636U, 0x6106673dU,
+	0xcb04cf97U, 0xbe2d93e2U, 0xfb38c3a7U, 0xeb2cc7b7U,
+	0x0ff1fe53U, 0x8c911dd0U, 0x7797e02bU, 0x003e3e5cU,
+	0x19607945U, 0x7392e12fU, 0xf4f703a8U, 0xd0da0a8cU,
+	0xbcad11e0U, 0xcf01ce93U, 0xee6987b2U, 0x0d717c51U,
+	0x3e8db362U, 0x908a1accU, 0x3282b06eU, 0x3bc8f367U,
+	0x49246d15U, 0xea6c86b6U, 0xdcd50980U, 0x9a009ac6U,
+	0xc18e4f9dU, 0x5af0aa06U, 0x1c253940U, 0xa9fc55f5U,
+	0xccc10d90U, 0x920a98ceU, 0x547f2b08U, 0xd19a4b8dU,
+	0x76d7a12aU, 0x06bbbd5aU, 0xcd814c91U, 0x08343c54U,
+	0x6f2847c9U, 0x767600d0U, 0x93eb7835U, 0xcfa06f69U,
+	0x075a5da1U, 0xb3c37015U, 0xc06faf66U, 0xeacd274cU,
+	0x6ce884caU, 0xeec82648U, 0x64e286c2U, 0x3f6c5399U,
+	0x942eba32U, 0xdbb16a7dU, 0x843abe22U, 0x20b79786U,
+	0x9821b93eU, 0xa95df40fU, 0x5e540af8U, 0xed08e54bU,
+	0x9c24b83aU, 0xcee02e68U, 0x24b29682U, 0x3b69529dU,
+	0x9ea43a38U, 0x25f2d783U, 0x78f981deU, 0xac18b40aU,
+	0x11cbdab7U, 0x2e381688U, 0x2cb8948aU, 0x525b09f4U,
+	0x464a0ce0U, 0x3decd19bU, 0x75b6c3d3U, 0xe7826541U,
+	0x727301d4U, 0x8bf57e2dU, 0x565e08f0U, 0xdd34e97bU,
+	0xf859a15eU, 0x65a2c7c3U, 0x188199beU, 0x39e9d09fU,
+	0x83ff7c25U, 0x0b555eadU, 0x86ba3c20U, 0xbd4cf11bU,
+	0xa7d27501U, 0xfad9235cU, 0x44ca8ee2U, 0x92ab3934U,
+	0x134b58b5U, 0x148e9ab2U, 0x97ee7931U, 0x626705c4U,
+	0x36261090U, 0x4b054eedU, 0x666204c0U, 0xdfb46b79U,
+	0xe107e647U, 0x0a151facU, 0xc865ad6eU, 0xae983608U,
+	0x29fdd48fU, 0x28bd958eU, 0xaa9d370cU, 0xc7aa6d61U,
+	0xfb99625dU, 0xf919e05fU, 0x68ed85ceU, 0xdef42a78U,
+	0x3a29139cU, 0x519bcaf7U, 0xc46aae62U, 0x50db8bf6U,
+	0xafd87709U, 0x22371584U, 0x09d5dcafU, 0x96ae3830U,
+	0x956efb33U, 0x9961f83fU, 0x32231194U, 0xfedc2258U,
+	0xf113e257U, 0xa552f703U, 0x5f144bf9U, 0x1a011bbcU,
+	0xd7be6971U, 0x9be17a3dU, 0x08959daeU, 0x19c1d8bfU,
+	0xf516e353U, 0x6da8c5cbU, 0x424f0de4U, 0xe047a746U,
+	0x4cc08ceaU, 0xa412b602U, 0x0c909caaU, 0x4d80cdebU,
+	0xb949f01fU, 0x559ecbf3U, 0x733340d5U, 0xbe8c3218U,
+	0xa017b706U, 0xbc0cb01aU, 0xc52aef63U, 0xd871a97eU,
+	0x30a39396U, 0xb003b316U, 0xd53eeb73U, 0x2a3d178cU,
+	0x672245c1U, 0x34a69292U, 0x2df8d58bU, 0x69adc4cfU,
+	0x4a450fecU, 0x7b3942ddU, 0xa81db50eU, 0x7dbcc1dbU,
+	0x05dadfa3U, 0xec48a44aU, 0x3cac909aU, 0xabdd760dU,
+	0xb6863010U, 0x27725581U, 0x174e59b1U, 0x4e400ee8U,
+	0x9fe47b39U, 0xcae52f6cU, 0x1e041ab8U, 0x7f3c43d9U,
+	0x35e6d393U, 0x2b7d568dU, 0xd47eaa72U, 0xe2c72544U,
+	0xba89331cU, 0xeb8d664dU, 0xff9c6359U, 0x0f505fa9U,
+	0xd3bb6875U, 0xb143f217U, 0x60e787c6U, 0xb2833114U,
+	0xb7c67111U, 0x8eb03e28U, 0x418fcee7U, 0xa6923400U,
+	0x5a510bfcU, 0xe442a642U, 0x5cd488faU, 0x61a7c6c7U,
+	0x82bf3d24U, 0x7a7903dcU, 0xfc5ca05aU, 0xf456a252U,
+	0xe3876445U, 0x061a1ca0U, 0xf2d32154U, 0xbbc9721dU,
+	0x40cf8fe6U, 0xef886749U, 0xbfcc7319U, 0x8c30bc2aU,
+	0x3e2c1298U, 0xad58f50bU, 0x1f445bb9U, 0xb546f313U,
+	0x37665191U, 0x902bbb36U, 0xb809b11eU, 0x9aa13b3cU,
+	0x009f9fa6U, 0x79b9c0dfU, 0x0dd0ddabU, 0x8975fc2fU,
+	0x632744c5U, 0x5b114afdU, 0xe84da54eU, 0x21f7d687U,
+	0x4f004fe9U, 0xc3af6c65U, 0x0e101ea8U, 0x8835bd2eU,
+	0x38a9919eU, 0xcba56e6dU, 0x9d64f93bU, 0xfd1ce15bU,
+	0x817ffe27U, 0x857aff23U, 0x5991c8ffU, 0x71b3c2d7U,
+	0x021f1da4U, 0x58d189feU, 0x8ab53f2cU, 0x7e7c02d8U,
+	0xdaf12b7cU, 0x430f4ce5U, 0xc6ea2c60U, 0xc925ec6fU,
+	0x70f383d6U, 0x531b48f5U, 0x458acfe3U, 0x31e3d297U,
+	0x48c58deeU, 0x74f682d2U, 0x5d94c9fbU, 0x571e49f1U,
+	0x1c8498baU, 0x33635095U, 0xf053a356U, 0x160e18b0U,
+	0xa2973504U, 0x035f5ca5U, 0xd931e87fU, 0xc2ef2d64U,
+	0x803fbf26U, 0xe502e743U, 0x773641d1U, 0x7cfc80daU,
+	0xd6fe2870U, 0xa3d77405U, 0xe6c22440U, 0xf6d62050U,
+	0x120b19b4U, 0x916bfa37U, 0x6a6d07ccU, 0x1dc4d9bbU,
+	0x049a9ea2U, 0x6e6806c8U, 0xe90de44fU, 0xcd20ed6bU,
+	0xa157f607U, 0xd2fb2974U, 0xf3936055U, 0x108b9bb6U,
+	0x23775485U, 0x8d70fd2bU, 0x2f785789U, 0x26321480U,
+	0x54de8af2U, 0xf7966151U, 0xc12fee67U, 0x87fa7d21U,
+	0xdc74a87aU, 0x470a4de1U, 0x01dfdea7U, 0xb406b212U,
+	0xd13bea77U, 0x8ff07f29U, 0x4985ccefU, 0xcc60ac6aU,
+	0x6b2d46cdU, 0x1b415abdU, 0xd07bab76U, 0x15cedbb3U,
+	0xac17bb35U, 0xb549fc2cU, 0x50d484c9U, 0x0c9f9395U,
+	0xc465a15dU, 0x70fc8ce9U, 0x0350539aU, 0x29f2dbb0U,
+	0xafd77836U, 0x2df7dab4U, 0xa7dd7a3eU, 0xfc53af65U,
+	0x571146ceU, 0x188e9681U, 0x470542deU, 0xe3886b7aU,
+	0x5b1e45c2U, 0x6a6208f3U, 0x9d6bf604U, 0x2e3719b7U,
+	0x5f1b44c6U, 0x0ddfd294U, 0xe78d6a7eU, 0xf856ae61U,
+	0x5d9bc6c4U, 0xe6cd2b7fU, 0xbbc67d22U, 0x6f2748f6U,
+	0xd2f4264bU, 0xed07ea74U, 0xef876876U, 0x9164f508U,
+	0x8575f01cU, 0xfed32d67U, 0xb6893f2fU, 0x24bd99bdU,
+	0xb14cfd28U, 0x48ca82d1U, 0x9561f40cU, 0x1e0b1587U,
+	0x3b665da2U, 0xa69d3b3fU, 0xdbbe6542U, 0xfad62c63U,
+	0x40c080d9U, 0xc86aa251U, 0x4585c0dcU, 0x7e730de7U,
+	0x64ed89fdU, 0x39e6dfa0U, 0x87f5721eU, 0x5194c5c8U,
+	0xd074a449U, 0xd7b1664eU, 0x54d185cdU, 0xa158f938U,
+	0xf519ec6cU, 0x883ab211U, 0xa55df83cU, 0x1c8b9785U,
+	0x22381abbU, 0xc92ae350U, 0x0b5a5192U, 0x6da7caf4U,
+	0xeac22873U, 0xeb826972U, 0x69a2cbf0U, 0x0495919dU,
+	0x38a69ea1U, 0x3a261ca3U, 0xabd27932U, 0x1dcbd684U,
+	0xf916ef60U, 0x92a4360bU, 0x0755529eU, 0x93e4770aU,
+	0x6ce78bf5U, 0xe108e978U, 0xcaea2053U, 0x5591c4ccU,
+	0x565107cfU, 0x5a5e04c3U, 0xf11ced68U, 0x3de3dea4U,
+	0x322c1eabU, 0x666d0bffU, 0x9c2bb705U, 0xd93ee740U,
+	0x1481958dU, 0x58de86c1U, 0xcbaa6152U, 0xdafe2443U,
+	0x36291fafU, 0xae973937U, 0x8170f118U, 0x23785bbaU,
+	0x8fff7016U, 0x672d4afeU, 0xcfaf6056U, 0x8ebf3117U,
+	0x7a760ce3U, 0x96a1370fU, 0xb00cbc29U, 0x7db3cee4U,
+	0x63284bfaU, 0x7f334ce6U, 0x0615139fU, 0x1b4e5582U,
+	0xf39c6f6aU, 0x733c4feaU, 0x1601178fU, 0xe902eb70U,
+	0xa41db93dU, 0xf7996e6eU, 0xeec72977U, 0xaa923833U,
+	0x897af310U, 0xb806be21U, 0x6b2249f2U, 0xbe833d27U,
+	0xc6e5235fU, 0x2f7758b6U, 0xff936c66U, 0x68e28af1U,
+	0x75b9ccecU, 0xe44da97dU, 0xd471a54dU, 0x8d7ff214U,
+	0x5cdb87c5U, 0x09dad390U, 0xdd3be644U, 0xbc03bf25U,
+	0xf6d92f6fU, 0xe842aa71U, 0x1741568eU, 0x21f8d9b8U,
+	0x79b6cfe0U, 0x28b29ab1U, 0x3ca39fa5U, 0xcc6fa355U,
+	0x10849489U, 0x727c0eebU, 0xa3d87b3aU, 0x71bccde8U,
+	0x74f98dedU, 0x4d8fc2d4U, 0x82b0321bU, 0x65adc8fcU,
+	0x996ef700U, 0x277d5abeU, 0x9feb7406U, 0xa2983a3bU,
+	0x4180c1d8U, 0xb946ff20U, 0x3f635ca6U, 0x37695eaeU,
+	0x20b898b9U, 0xc525e05cU, 0x31ecdda8U, 0x78f68ee1U,
+	0x83f0731aU, 0x2cb79bb5U, 0x7cf38fe5U, 0x4f0f40d6U,
+	0xfd13ee64U, 0x6e6709f7U, 0xdc7ba745U, 0x76790fefU,
+	0xf459ad6dU, 0x531447caU, 0x7b364de2U, 0x599ec7c0U,
+	0xc3a0635aU, 0xba863c23U, 0xceef2157U, 0x4a4a00d3U,
+	0xa018b839U, 0x982eb601U, 0x2b7259b2U, 0xe2c82a7bU,
+	0x8c3fb315U, 0x00909099U, 0xcd2fe254U, 0x4b0a41d2U,
+	0xfb966d62U, 0x089a9291U, 0x5e5b05c7U, 0x3e231da7U,
+	0x424002dbU, 0x464503dfU, 0x9aae3403U, 0xb28c3e2bU,
+	0xc120e158U, 0x9bee7502U, 0x498ac3d0U, 0xbd43fe24U,
+	0x19ced780U, 0x8030b019U, 0x05d5d09cU, 0x0a1a1093U,
+	0xb3cc7f2aU, 0x9024b409U, 0x86b5331fU, 0xf2dc2e6bU,
+	0x8bfa7112U, 0xb7c97e2eU, 0x9eab3507U, 0x9421b50dU,
+	0xdfbb6446U, 0xf05cac69U, 0x336c5faaU, 0xd531e44cU,
+	0x61a8c9f8U, 0xc060a059U, 0x1a0e1483U, 0x01d0d198U,
+	0x430043daU, 0x263d1bbfU, 0xb409bd2dU, 0xbfc37c26U,
+	0x15c1d48cU, 0x60e888f9U, 0x25fdd8bcU, 0x35e9dcacU,
+	0xd134e548U, 0x525406cbU, 0xa952fb30U, 0xdefb2547U,
+	0xc7a5625eU, 0xad57fa34U, 0x2a3218b3U, 0x0e1f1197U,
+	0x62680afbU, 0x11c4d588U, 0x30ac9ca9U, 0xd3b4674aU,
+	0xe048a879U, 0x4e4f01d7U, 0xec47ab75U, 0xe50de87cU,
+	0x97e1760eU, 0x34a99dadU, 0x0210129bU, 0x44c581ddU,
+	0x1f4b5486U, 0x8435b11dU, 0xc2e0225bU, 0x77394eeeU,
+	0x1204168bU, 0x4ccf83d5U, 0x8aba3013U, 0x0f5f5096U,
+	0xa812ba31U, 0xd87ea641U, 0x1344578aU, 0xd6f1274fU,
+	0xbfba058bU, 0xa6e44292U, 0x43793a77U, 0x1f322d2bU,
+	0xd7c81fe3U, 0x63513257U, 0x10fded24U, 0x3a5f650eU,
+	0xbc7ac688U, 0x3e5a640aU, 0xb470c480U, 0xeffe11dbU,
+	0x44bcf870U, 0x0b23283fU, 0x54a8fc60U, 0xf025d5c4U,
+	0x48b3fb7cU, 0x79cfb64dU, 0x8ec648baU, 0x3d9aa709U,
+	0x4cb6fa78U, 0x1e726c2aU, 0xf420d4c0U, 0xebfb10dfU,
+	0x4e36787aU, 0xf56095c1U, 0xa86bc39cU, 0x7c8af648U,
+	0xc15998f5U, 0xfeaa54caU, 0xfc2ad6c8U, 0x82c94bb6U,
+	0x96d84ea2U, 0xed7e93d9U, 0xa5248191U, 0x37102703U,
+	0xa2e14396U, 0x5b673c6fU, 0x86cc4ab2U, 0x0da6ab39U,
+	0x28cbe31cU, 0xb5308581U, 0xc813dbfcU, 0xe97b92ddU,
+	0x536d3e67U, 0xdbc71cefU, 0x56287e62U, 0x6ddeb359U,
+	0x77403743U, 0x2a4b611eU, 0x9458cca0U, 0x42397b76U,
+	0xc3d91af7U, 0xc41cd8f0U, 0x477c3b73U, 0xb2f54786U,
+	0xe6b452d2U, 0x9b970cafU, 0xb6f04682U, 0x0f26293bU,
+	0x3195a405U, 0xda875deeU, 0x18f7ef2cU, 0x7e0a744aU,
+	0xf96f96cdU, 0xf82fd7ccU, 0x7a0f754eU, 0x17382f23U,
+	0x2b0b201fU, 0x298ba21dU, 0xb87fc78cU, 0x0e66683aU,
+	0xeabb51deU, 0x810988b5U, 0x14f8ec20U, 0x8049c9b4U,
+	0x7f4a354bU, 0xf2a557c6U, 0xd9479eedU, 0x463c7a72U,
+	0x45fcb971U, 0x49f3ba7dU, 0xe2b153d6U, 0x2e4e601aU,
+	0x2181a015U, 0x75c0b541U, 0x8f8609bbU, 0xca9359feU,
+	0x072c2b33U, 0x4b73387fU, 0xd807dfecU, 0xc9539afdU,
+	0x2584a111U, 0xbd3a8789U, 0x92dd4fa6U, 0x30d5e504U,
+	0x9c52cea8U, 0x7480f440U, 0xdc02dee8U, 0x9d128fa9U,
+	0x69dbb25dU, 0x850c89b1U, 0xa3a10297U, 0x6e1e705aU,
+	0x7085f544U, 0x6c9ef258U, 0x15b8ad21U, 0x08e3eb3cU,
+	0xe031d1d4U, 0x6091f154U, 0x05aca931U, 0xfaaf55ceU,
+	0xb7b00783U, 0xe434d0d0U, 0xfd6a97c9U, 0xb93f868dU,
+	0x9ad74daeU, 0xabab009fU, 0x788ff74cU, 0xad2e8399U,
+	0xd5489de1U, 0x3cdae608U, 0xec3ed2d8U, 0x7b4f344fU,
+	0x66147252U, 0xf7e017c3U, 0xc7dc1bf3U, 0x9ed24caaU,
+	0x4f76397bU, 0x1a776d2eU, 0xce9658faU, 0xafae019bU,
+	0xe57491d1U, 0xfbef14cfU, 0x04ece830U, 0x32556706U,
+	0x6a1b715eU, 0x3b1f240fU, 0x2f0e211bU, 0xdfc21debU,
+	0x03292a37U, 0x61d1b055U, 0xb075c584U, 0x62117356U,
+	0x67543353U, 0x5e227c6aU, 0x911d8ca5U, 0x76007642U,
+	0x8ac349beU, 0x34d0e400U, 0x8c46cab8U, 0xb1358485U,
+	0x522d7f66U, 0xaaeb419eU, 0x2ccee218U, 0x24c4e010U,
+	0x33152607U, 0xd6885ee2U, 0x22416316U, 0x6b5b305fU,
+	0x905dcda4U, 0x3f1a250bU, 0x6f5e315bU, 0x5ca2fe68U,
+	0xeebe50daU, 0x7dcab749U, 0xcfd619fbU, 0x65d4b151U,
+	0xe7f413d3U, 0x40b9f974U, 0x689bf35cU, 0x4a33797eU,
+	0xd00ddde4U, 0xa92b829dU, 0xdd429fe9U, 0x59e7be6dU,
+	0xb3b50687U, 0x8b8308bfU, 0x38dfe70cU, 0xf16594c5U,
+	0x9f920dabU, 0x133d2e27U, 0xde825ceaU, 0x58a7ff6cU,
+	0xe83bd3dcU, 0x1b372c2fU, 0x4df6bb79U, 0x2d8ea319U,
+	0x51edbc65U, 0x55e8bd61U, 0x89038abdU, 0xa1218095U,
+	0xd28d5fe6U, 0x8843cbbcU, 0x5a277d6eU, 0xaeee409aU,
+	0x0a63693eU, 0x939d0ea7U, 0x16786e22U, 0x19b7ae2dU,
+	0xa061c194U, 0x83890ab7U, 0x95188da1U, 0xe17190d5U,
+	0x9857cfacU, 0xa464c090U, 0x8d068bb9U, 0x878c0bb3U,
+	0xcc16daf8U, 0xe3f112d7U, 0x20c1e114U, 0xc69c5af2U,
+	0x72057746U, 0xd3cd1ee7U, 0x09a3aa3dU, 0x127d6f26U,
+	0x50adfd64U, 0x3590a501U, 0xa7a40393U, 0xac6ec298U,
+	0x066c6a32U, 0x73453647U, 0x36506602U, 0x26446212U,
+	0xc2995bf6U, 0x41f9b875U, 0xbaff458eU, 0xcd569bf9U,
+	0xd408dce0U, 0xbefa448aU, 0x399fa60dU, 0x1db2af29U,
+	0x71c5b445U, 0x02696b36U, 0x23012217U, 0xc019d9f4U,
+	0xf3e516c7U, 0x5de2bf69U, 0xffea15cbU, 0xf6a056c2U,
+	0x844cc8b0U, 0x27042313U, 0x11bdac25U, 0x57683f63U,
+	0x0ce6ea38U, 0x97980fa3U, 0xd14d9ce5U, 0x6494f050U,
+	0x01a9a835U, 0x5f623d6bU, 0x99178eadU, 0x1cf2ee28U,
+	0xbbbf048fU, 0xcbd318ffU, 0x00e9e934U, 0xc55c99f1U,
+	0x2994bd33U, 0x30cafa2aU, 0xd55782cfU, 0x891c9593U,
+	0x41e6a75bU, 0xf57f8aefU, 0x86d3559cU, 0xac71ddb6U,
+	0x2a547e30U, 0xa874dcb2U, 0x225e7c38U, 0x79d0a963U,
+	0xd29240c8U, 0x9d0d9087U, 0xc28644d8U, 0x660b6d7cU,
+	0xde9d43c4U, 0xefe10ef5U, 0x18e8f002U, 0xabb41fb1U,
+	0xda9842c0U, 0x885cd492U, 0x620e6c78U, 0x7dd5a867U,
+	0xd818c0c2U, 0x634e2d79U, 0x3e457b24U, 0xeaa44ef0U,
+	0x5777204dU, 0x6884ec72U, 0x6a046e70U, 0x14e7f30eU,
+	0x00f6f61aU, 0x7b502b61U, 0x330a3929U, 0xa13e9fbbU,
+	0x34cffb2eU, 0xcd4984d7U, 0x10e2f20aU, 0x9b881381U,
+	0xbee55ba4U, 0x231e3d39U, 0x5e3d6344U, 0x7f552a65U,
+	0xc54386dfU, 0x4de9a457U, 0xc006c6daU, 0xfbf00be1U,
+	0xe16e8ffbU, 0xbc65d9a6U, 0x02767418U, 0xd417c3ceU,
+	0x55f7a24fU, 0x52326048U, 0xd15283cbU, 0x24dbff3eU,
+	0x709aea6aU, 0x0db9b417U, 0x20defe3aU, 0x99089183U,
+	0xa7bb1cbdU, 0x4ca9e556U, 0x8ed95794U, 0xe824ccf2U,
+	0x6f412e75U, 0x6e016f74U, 0xec21cdf6U, 0x8116979bU,
+	0xbd2598a7U, 0xbfa51aa5U, 0x2e517f34U, 0x9848d082U,
+	0x7c95e966U, 0x1727300dU, 0x82d65498U, 0x1667710cU,
+	0xe9648df3U, 0x648bef7eU, 0x4f692655U, 0xd012c2caU,
+	0xd3d201c9U, 0xdfdd02c5U, 0x749feb6eU, 0xb860d8a2U,
+	0xb7af18adU, 0xe3ee0df9U, 0x19a8b103U, 0x5cbde146U,
+	0x9102938bU, 0xdd5d80c7U, 0x4e296754U, 0x5f7d2245U,
+	0xb3aa19a9U, 0x2b143f31U, 0x04f3f71eU, 0xa6fb5dbcU,
+	0x0a7c7610U, 0xe2ae4cf8U, 0x4a2c6650U, 0x0b3c3711U,
+	0xfff50ae5U, 0x13223109U, 0x358fba2fU, 0xf830c8e2U,
+	0xe6ab4dfcU, 0xfab04ae0U, 0x83961599U, 0x9ecd5384U,
+	0x761f696cU, 0xf6bf49ecU, 0x93821189U, 0x6c81ed76U,
+	0x219ebf3bU, 0x721a6868U, 0x6b442f71U, 0x2f113e35U,
+	0x0cf9f516U, 0x3d85b827U, 0xeea14ff4U, 0x3b003b21U,
+	0x43662559U, 0xaaf45eb0U, 0x7a106a60U, 0xed618cf7U,
+	0xf03acaeaU, 0x61ceaf7bU, 0x51f2a34bU, 0x08fcf412U,
+	0xd95881c3U, 0x8c59d596U, 0x58b8e042U, 0x3980b923U,
+	0x735a2969U, 0x6dc1ac77U, 0x92c25088U, 0xa47bdfbeU,
+	0xfc35c9e6U, 0xad319cb7U, 0xb92099a3U, 0x49eca553U,
+	0x9507928fU, 0xf7ff08edU, 0x265b7d3cU, 0xf43fcbeeU,
+	0xf17a8bebU, 0xc80cc4d2U, 0x0733341dU, 0xe02ecefaU,
+	0x1cedf106U, 0xa2fe5cb8U, 0x1a687200U, 0x271b3c3dU,
+	0xc403c7deU, 0x3cc5f926U, 0xbae05aa0U, 0xb2ea58a8U,
+	0xa53b9ebfU, 0x40a6e65aU, 0xb46fdbaeU, 0xfd7588e7U,
+	0x0673751cU, 0xa9349db3U, 0xf97089e3U, 0xca8c46d0U,
+	0x7890e862U, 0xebe40ff1U, 0x59f8a143U, 0xf3fa09e9U,
+	0x71daab6bU, 0xd69741ccU, 0xfeb54be4U, 0xdc1dc1c6U,
+	0x4623655cU, 0x3f053a25U, 0x4b6c2751U, 0xcfc906d5U,
+	0x259bbe3fU, 0x1dadb007U, 0xaef15fb4U, 0x674b2c7dU,
+	0x09bcb513U, 0x8513969fU, 0x48ace452U, 0xce8947d4U,
+	0x7e156b64U, 0x8d199497U, 0xdbd803c1U, 0xbba01ba1U,
+	0xc7c304ddU, 0xc3c605d9U, 0x1f2d3205U, 0x370f382dU,
+	0x44a3e75eU, 0x1e6d7304U, 0xcc09c5d6U, 0x38c0f822U,
+	0x9c4dd186U, 0x05b3b61fU, 0x8056d69aU, 0x8f991695U,
+	0x364f792cU, 0x15a7b20fU, 0x03363519U, 0x775f286dU,
+	0x0e797714U, 0x324a7828U, 0x1b283301U, 0x11a2b30bU,
+	0x5a386240U, 0x75dfaa6fU, 0xb6ef59acU, 0x50b2e24aU,
+	0xe42bcffeU, 0x45e3a65fU, 0x9f8d1285U, 0x8453d79eU,
+	0xc68345dcU, 0xa3be1db9U, 0x318abb2bU, 0x3a407a20U,
+	0x9042d28aU, 0xe56b8effU, 0xa07edebaU, 0xb06adaaaU,
+	0x54b7e34eU, 0xd7d700cdU, 0x2cd1fd36U, 0x5b782341U,
+	0x42266458U, 0x28d4fc32U, 0xafb11eb5U, 0x8b9c1791U,
+	0xe7eb0cfdU, 0x9447d38eU, 0xb52f9aafU, 0x5637614cU,
+	0x65cbae7fU, 0xcbcc07d1U, 0x69c4ad73U, 0x608eee7aU,
+	0x12627008U, 0xb12a9babU, 0x8793149dU, 0xc14687dbU,
+	0x9ac85280U, 0x01b6b71bU, 0x4763245dU, 0xf2ba48e8U,
+	0x9787108dU, 0xc94c85d3U, 0x0f393615U, 0x8adc5690U,
+	0x2d91bc37U, 0x5dfda047U, 0x96c7518cU, 0x53722149U,
+	0x5ada800eU, 0x4384c717U, 0xa619bff2U, 0xfa52a8aeU,
+	0x32a89a66U, 0x8631b7d2U, 0xf59d68a1U, 0xdf3fe08bU,
+	0x591a430dU, 0xdb3ae18fU, 0x51104105U, 0x0a9e945eU,
+	0xa1dc7df5U, 0xee43adbaU, 0xb1c879e5U, 0x15455041U,
+	0xadd37ef9U, 0x9caf33c8U, 0x6ba6cd3fU, 0xd8fa228cU,
+	0xa9d67ffdU, 0xfb12e9afU, 0x11405145U, 0x0e9b955aU,
+	0xab56fdffU, 0x10001044U, 0x4d0b4619U, 0x99ea73cdU,
+	0x24391d70U, 0x1bcad14fU, 0x194a534dU, 0x67a9ce33U,
+	0x73b8cb27U, 0x081e165cU, 0x40440414U, 0xd270a286U,
+	0x4781c613U, 0xbe07b9eaU, 0x63accf37U, 0xe8c62ebcU,
+	0xcdab6699U, 0x50500004U, 0x2d735e79U, 0x0c1b1758U,
+	0xb60dbbe2U, 0x3ea7996aU, 0xb348fbe7U, 0x88be36dcU,
+	0x9220b2c6U, 0xcf2be49bU, 0x71384925U, 0xa759fef3U,
+	0x26b99f72U, 0x217c5d75U, 0xa21cbef6U, 0x5795c203U,
+	0x03d4d757U, 0x7ef7892aU, 0x5390c307U, 0xea46acbeU,
+	0xd4f52180U, 0x3fe7d86bU, 0xfd976aa9U, 0x9b6af1cfU,
+	0x1c0f1348U, 0x1d4f5249U, 0x9f6ff0cbU, 0xf258aaa6U,
+	0xce6ba59aU, 0xcceb2798U, 0x5d1f4209U, 0xeb06edbfU,
+	0x0fdbd45bU, 0x64690d30U, 0xf19869a5U, 0x65294c31U,
+	0x9a2ab0ceU, 0x17c5d243U, 0x3c271b68U, 0xa35cfff7U,
+	0xa09c3cf4U, 0xac933ff8U, 0x07d1d653U, 0xcb2ee59fU,
+	0xc4e12590U, 0x90a030c4U, 0x6ae68c3eU, 0x2ff3dc7bU,
+	0xe24caeb6U, 0xae13bdfaU, 0x3d675a69U, 0x2c331f78U,
+	0xc0e42494U, 0x585a020cU, 0x77bdca23U, 0xd5b56081U,
+	0x79324b2dU, 0x91e071c5U, 0x39625b6dU, 0x78720a2cU,
+	0x8cbb37d8U, 0x606c0c34U, 0x46c18712U, 0x8b7ef5dfU,
+	0x95e570c1U, 0x89fe77ddU, 0xf0d828a4U, 0xed836eb9U,
+	0x05515451U, 0x85f174d1U, 0xe0cc2cb4U, 0x1fcfd04bU,
+	0x52d08206U, 0x01545555U, 0x180a124cU, 0x5c5f0308U,
+	0x7fb7c82bU, 0x4ecb851aU, 0x9def72c9U, 0x484e061cU,
+	0x30281864U, 0xd9ba638dU, 0x095e575dU, 0x9e2fb1caU,
+	0x8374f7d7U, 0x12809246U, 0x22bc9e76U, 0x7bb2c92fU,
+	0xaa16bcfeU, 0xff17e8abU, 0x2bf6dd7fU, 0x4ace841eU,
+	0x00141454U, 0x1e8f914aU, 0xe18c6db5U, 0xd735e283U,
+	0x8f7bf4dbU, 0xde7fa18aU, 0xca6ea49eU, 0x3aa2986eU,
+	0xe649afb2U, 0x84b135d0U, 0x55154001U, 0x8771f6d3U,
+	0x8234b6d6U, 0xbb42f9efU, 0x747d0920U, 0x9360f3c7U,
+	0x6fa3cc3bU, 0xd1b06185U, 0x69264f3dU, 0x54550100U,
+	0xb74dfae3U, 0x4f8bc41bU, 0xc9ae679dU, 0xc1a46595U,
+	0xd675a382U, 0x33e8db67U, 0xc721e693U, 0x8e3bb5daU,
+	0x753d4821U, 0xda7aa08eU, 0x8a3eb4deU, 0xb9c27bedU,
+	0x0bded55fU, 0x98aa32ccU, 0x2ab69c7eU, 0x80b434d4U,
+	0x02949656U, 0xa5d97cf1U, 0x8dfb76d9U, 0xaf53fcfbU,
+	0x356d5861U, 0x4c4b0718U, 0x38221a6cU, 0xbc873be8U,
+	0x56d58302U, 0x6ee38d3aU, 0xddbf6289U, 0x14051140U,
+	0x7af2882eU, 0xf65daba2U, 0x3be2d96fU, 0xbdc77ae9U,
+	0x0d5b5659U, 0xfe57a9aaU, 0xa8963efcU, 0xc8ee269cU,
+	0xb48d39e0U, 0xb08838e4U, 0x6c630f38U, 0x44410510U,
+	0x37edda63U, 0x6d234e39U, 0xbf47f8ebU, 0x4b8ec51fU,
+	0xef03ecbbU, 0x76fd8b22U, 0xf318eba7U, 0xfcd72ba8U,
+	0x45014411U, 0x66e98f32U, 0x70780824U, 0x04111550U,
+	0x7d374a29U, 0x41044515U, 0x68660e3cU, 0x62ec8e36U,
+	0x29765f7dU, 0x06919752U, 0xc5a16491U, 0x23fcdf77U,
+	0x9765f2c3U, 0x36ad9b62U, 0xecc32fb8U, 0xf71deaa3U,
+	0xb5cd78e1U, 0xd0f02084U, 0x42c48616U, 0x490e471dU,
+	0xe30cefb7U, 0x9625b3c2U, 0xd330e387U, 0xc324e797U,
+	0x27f9de73U, 0xa4993df0U, 0x5f9fc00bU, 0x28361e7cU,
+	0x31685965U, 0x5b9ac10fU, 0xdcff2388U, 0xf8d22aacU,
+	0x94a531c0U, 0xe709eeb3U, 0xc661a792U, 0x25795c71U,
+	0x16859342U, 0xb8823aecU, 0x1a8a904eU, 0x13c0d347U,
+	0x612c4d35U, 0xc264a696U, 0xf4dd29a0U, 0xb208bae6U,
+	0xe9866fbdU, 0x72f88a26U, 0x342d1960U, 0x81f475d5U,
+	0xe4c92db0U, 0xba02b8eeU, 0x7c770b28U, 0xf9926badU,
+	0x5edf810aU, 0x2eb39d7aU, 0xe5896cb1U, 0x203c1c74U,
+	0x422163edU, 0x5b7f24f4U, 0xbee25c11U, 0xe2a94b4dU,
+	0x2a537985U, 0x9eca5431U, 0xed668b42U, 0xc7c40368U,
+	0x41e1a0eeU, 0xc3c1026cU, 0x49eba2e6U, 0x126577bdU,
+	0xb9279e16U, 0xf6b84e59U, 0xa9339a06U, 0x0dbeb3a2U,
+	0xb5289d1aU, 0x8454d02bU, 0x735d2edcU, 0xc001c16fU,
+	0xb12d9c1eU, 0xe3e90a4cU, 0x09bbb2a6U, 0x166076b9U,
+	0xb3ad1e1cU, 0x08fbf3a7U, 0x55f0a5faU, 0x8111902eU,
+	0x3cc2fe93U, 0x033132acU, 0x01b1b0aeU, 0x7f522dd0U,
+	0x6b4328c4U, 0x10e5f5bfU, 0x58bfe7f7U, 0xca8b4165U,
+	0x5f7a25f0U, 0xa6fc5a09U, 0x7b572cd4U, 0xf03dcd5fU,
+	0xd550857aU, 0x48abe3e7U, 0x3588bd9aU, 0x14e0f4bbU,
+	0xaef65801U, 0x265c7a89U, 0xabb31804U, 0x9045d53fU,
+	0x8adb5125U, 0xd7d00778U, 0x69c3aac6U, 0xbfa21d10U,
+	0x3e427c91U, 0x3987be96U, 0xbae75d15U, 0x4f6e21e0U,
+	0x1b2f34b4U, 0x660c6ac9U, 0x4b6b20e4U, 0xf2bd4f5dU,
+	0xcc0ec263U, 0x271c3b88U, 0xe56c894aU, 0x8391122cU,
+	0x04f4f0abU, 0x05b4b1aaU, 0x87941328U, 0xeaa34945U,
+	0xd6904679U, 0xd410c47bU, 0x45e4a1eaU, 0xf3fd0e5cU,
+	0x172037b8U, 0x7c92eed3U, 0xe9638a46U, 0x7dd2afd2U,
+	0x82d1532dU, 0x0f3e31a0U, 0x24dcf88bU, 0xbba71c14U,
+	0xb867df17U, 0xb468dc1bU, 0x1f2a35b0U, 0xd3d5067cU,
+	0xdc1ac673U, 0x885bd327U, 0x721d6fddU, 0x37083f98U,
+	0xfab74d55U, 0xb6e85e19U, 0x259cb98aU, 0x34c8fc9bU,
+	0xd81fc777U, 0x40a1e1efU, 0x6f4629c0U, 0xcd4e8362U,
+	0x61c9a8ceU, 0x891b9226U, 0x2199b88eU, 0x6089e9cfU,
+	0x9440d43bU, 0x7897efd7U, 0x5e3a64f1U, 0x9385163cU,
+	0x8d1e9322U, 0x9105943eU, 0xe823cb47U, 0xf5788d5aU,
+	0x1daab7b2U, 0x9d0a9732U, 0xf837cf57U, 0x073433a8U,
+	0x4a2b61e5U, 0x19afb6b6U, 0x00f1f1afU, 0x44a4e0ebU,
+	0x674c2bc8U, 0x563066f9U, 0x8514912aU, 0x50b5e5ffU,
+	0x28d3fb87U, 0xc141806eU, 0x11a5b4beU, 0x86d45229U,
+	0x9b8f1434U, 0x0a7b71a5U, 0x3a477d95U, 0x63492accU,
+	0xb2ed5f1dU, 0xe7ec0b48U, 0x330d3e9cU, 0x523567fdU,
+	0x18eff7b7U, 0x067472a9U, 0xf9778e56U, 0xcfce0160U,
+	0x97801738U, 0xc6844269U, 0xd295477dU, 0x22597b8dU,
+	0xfeb24c51U, 0x9c4ad633U, 0x4deea3e2U, 0x9f8a1530U,
+	0x9acf5535U, 0xa3b91a0cU, 0x6c86eac3U, 0x8b9b1024U,
+	0x77582fd8U, 0xc94b8266U, 0x71ddacdeU, 0x4caee2e3U,
+	0xafb61900U, 0x577027f8U, 0xd155847eU, 0xd95f8676U,
+	0xce8e4061U, 0x2b133884U, 0xdfda0570U, 0x96c05639U,
+	0x6dc6abc2U, 0xc281436dU, 0x92c5573dU, 0xa139980eU,
+	0x132536bcU, 0x8051d12fU, 0x324d7f9dU, 0x984fd737U,
+	0x1a6f75b5U, 0xbd229f12U, 0x9500953aU, 0xb7a81f18U,
+	0x2d96bb82U, 0x54b0e4fbU, 0x20d9f98fU, 0xa47cd80bU,
+	0x4e2e60e1U, 0x76186ed9U, 0xc544816aU, 0x0cfef2a3U,
+	0x62096bcdU, 0xeea64841U, 0x23193a8cU, 0xa53c990aU,
+	0x15a0b5baU, 0xe6ac4a49U, 0xb06ddd1fU, 0xd015c57fU,
+	0xac76da03U, 0xa873db07U, 0x7498ecdbU, 0x5cbae6f3U,
+	0x2f163980U, 0x75d8addaU, 0xa7bc1b08U, 0x537526fcU,
+	0xf7f80f58U, 0x6e0668c1U, 0xebe30844U, 0xe42cc84bU,
+	0x5dfaa7f2U, 0x7e126cd1U, 0x6883ebc7U, 0x1ceaf6b3U,
+	0x65cca9caU, 0x59ffa6f6U, 0x709deddfU, 0x7a176dd5U,
+	0x318dbc9eU, 0x1e6a74b1U, 0xdd5a8772U, 0x3b073c94U,
+	0x8f9e1120U, 0x2e567881U, 0xf438cc5bU, 0xefe60940U,
+	0xad369b02U, 0xc80bc367U, 0x5a3f65f5U, 0x51f5a4feU,
+	0xfbf70c54U, 0x8ede5021U, 0xcbcb0064U, 0xdbdf0474U,
+	0x3f023d90U, 0xbc62de13U, 0x476423e8U, 0x30cdfd9fU,
+	0x2993ba86U, 0x436122ecU, 0xc404c06bU, 0xe029c94fU,
+	0x8c5ed223U, 0xfff20d50U, 0xde9a4471U, 0x3d82bf92U,
+	0x0e7e70a1U, 0xa079d90fU, 0x027173adU, 0x0b3b30a4U,
+	0x79d7aed6U, 0xda9f4575U, 0xec26ca43U, 0xaaf35905U,
+	0xf17d8c5eU, 0x6a0369c5U, 0x2cd6fa83U, 0x990f9636U,
+	0xfc32ce53U, 0xa2f95b0dU, 0x648ce8cbU, 0xe169884eU,
+	0x462462e9U, 0x36487e99U, 0xfd728f52U, 0x38c7ff97U,
+	0x841f9b15U, 0x9d41dc0cU, 0x78dca4e9U, 0x2497b3b5U,
+	0xec6d817dU, 0x58f4acc9U, 0x2b5873baU, 0x01fafb90U,
+	0x87df5816U, 0x05fffa94U, 0x8fd55a1eU, 0xd45b8f45U,
+	0x7f1966eeU, 0x3086b6a1U, 0x6f0d62feU, 0xcb804b5aU,
+	0x731665e2U, 0x426a28d3U, 0xb563d624U, 0x063f3997U,
+	0x771364e6U, 0x25d7f2b4U, 0xcf854a5eU, 0xd05e8e41U,
+	0x7593e6e4U, 0xcec50b5fU, 0x93ce5d02U, 0x472f68d6U,
+	0xfafc066bU, 0xc50fca54U, 0xc78f4856U, 0xb96cd528U,
+	0xad7dd03cU, 0xd6db0d47U, 0x9e811f0fU, 0x0cb5b99dU,
+	0x9944dd08U, 0x60c2a2f1U, 0xbd69d42cU, 0x360335a7U,
+	0x136e7d82U, 0x8e951b1fU, 0xf3b64562U, 0xd2de0c43U,
+	0x68c8a0f9U, 0xe0628271U, 0x6d8de0fcU, 0x567b2dc7U,
+	0x4ce5a9ddU, 0x11eeff80U, 0xaffd523eU, 0x799ce5e8U,
+	0xf87c8469U, 0xffb9466eU, 0x7cd9a5edU, 0x8950d918U,
+	0xdd11cc4cU, 0xa0329231U, 0x8d55d81cU, 0x3483b7a5U,
+	0x0a303a9bU, 0xe122c370U, 0x235271b2U, 0x45afead4U,
+	0xc2ca0853U, 0xc38a4952U, 0x41aaebd0U, 0x2c9db1bdU,
+	0x10aebe81U, 0x122e3c83U, 0x83da5912U, 0x35c3f6a4U,
+	0xd11ecf40U, 0xbaac162bU, 0x2f5d72beU, 0xbbec572aU,
+	0x44efabd5U, 0xc900c958U, 0xe2e20073U, 0x7d99e4ecU,
+	0x7e5927efU, 0x725624e3U, 0xd914cd48U, 0x15ebfe84U,
+	0x1a243e8bU, 0x4e652bdfU, 0xb4239725U, 0xf136c760U,
+	0x3c89b5adU, 0x70d6a6e1U, 0xe3a24172U, 0xf2f60463U,
+	0x1e213f8fU, 0x869f1917U, 0xa978d138U, 0x0b707b9aU,
+	0xa7f75036U, 0x4f256adeU, 0xe7a74076U, 0xa6b71137U,
+	0x527e2cc3U, 0xbea9172fU, 0x98049c09U, 0x55bbeec4U,
+	0x4b206bdaU, 0x573b6cc6U, 0x2e1d33bfU, 0x334675a2U,
+	0xdb944f4aU, 0x5b346fcaU, 0x3e0937afU, 0xc10acb50U,
+	0x8c15991dU, 0xdf914e4eU, 0xc6cf0957U, 0x829a1813U,
+	0xa172d330U, 0x900e9e01U, 0x432a69d2U, 0x968b1d07U,
+	0xeeed037fU, 0x077f7896U, 0xd79b4c46U, 0x40eaaad1U,
+	0x5db1ecccU, 0xcc45895dU, 0xfc79856dU, 0xa577d234U,
+	0x74d3a7e5U, 0x21d2f3b0U, 0xf533c664U, 0x940b9f05U,
+	0xded10f4fU, 0xc04a8a51U, 0x3f4976aeU, 0x09f0f998U,
+	0x51beefc0U, 0x00baba91U, 0x14abbf85U, 0xe4678375U,
+	0x388cb4a9U, 0x5a742ecbU, 0x8bd05b1aU, 0x59b4edc8U,
+	0x5cf1adcdU, 0x6587e2f4U, 0xaab8123bU, 0x4da5e8dcU,
+	0xb166d720U, 0x0f757a9eU, 0xb7e35426U, 0x8a901a1bU,
+	0x6988e1f8U, 0x914edf00U, 0x176b7c86U, 0x1f617e8eU,
+	0x08b0b899U, 0xed2dc07cU, 0x19e4fd88U, 0x50feaec1U,
+	0xabf8533aU, 0x04bfbb95U, 0x54fbafc5U, 0x670760f6U,
+	0xd51bce44U, 0x466f29d7U, 0xf4738765U, 0x5e712fcfU,
+	0xdc518d4dU, 0x7b1c67eaU, 0x533e6dc2U, 0x7196e7e0U,
+	0xeba8437aU, 0x928e1c03U, 0xe6e70177U, 0x624220f3U,
+	0x88109819U, 0xb0269621U, 0x037a7992U, 0xcac00a5bU,
+	0xa4379335U, 0x2898b0b9U, 0xe527c274U, 0x630261f2U,
+	0xd39e4d42U, 0x2092b2b1U, 0x765325e7U, 0x162b3d87U,
+	0x6a4822fbU, 0x6e4d23ffU, 0xb2a61423U, 0x9a841e0bU,
+	0xe928c178U, 0xb3e65522U, 0x6182e3f0U, 0x954bde04U,
+	0x31c6f7a0U, 0xa8389039U, 0x2dddf0bcU, 0x221230b3U,
+	0x9bc45f0aU, 0xb82c9429U, 0xaebd133fU, 0xdad40e4bU,
+	0xa3f25132U, 0x9fc15e0eU, 0xb6a31527U, 0xbc29952dU,
+	0xf7b34466U, 0xd8548c49U, 0x1b647f8aU, 0xfd39c46cU,
+	0x49a0e9d8U, 0xe8688079U, 0x320634a3U, 0x29d8f1b8U,
+	0x6b0863faU, 0x0e353b9fU, 0x9c019d0dU, 0x97cb5c06U,
+	0x3dc9f4acU, 0x48e0a8d9U, 0x0df5f89cU, 0x1de1fc8cU,
+	0xf93cc568U, 0x7a5c26ebU, 0x815adb10U, 0xf6f30567U,
+	0xefad427eU, 0x855fda14U, 0x023a3893U, 0x261731b7U,
+	0x4a602adbU, 0x39ccf5a8U, 0x18a4bc89U, 0xfbbc476aU,
+	0xc8408859U, 0x664721f7U, 0xc44f8b55U, 0xcd05c85cU,
+	0xbfe9562eU, 0x1ca1bd8dU, 0x2a1832bbU, 0x6ccda1fdU,
+	0x374374a6U, 0xac3d913dU, 0xeae8027bU, 0x5f316eceU,
+	0x3a0c36abU, 0x64c7a3f5U, 0xa2b21033U, 0x275770b6U,
+	0x801a9a11U, 0xf0768661U, 0x3b4c77aaU, 0xfef9076fU,
+	0xa1bc1d93U, 0xb8e25a8aU, 0x5d7f226fU, 0x01343533U,
+	0xc9ce07fbU, 0x7d572a4fU, 0x0efbf53cU, 0x24597d16U,
+	0xa27cde90U, 0x205c7c12U, 0xaa76dc98U, 0xf1f809c3U,
+	0x5abae068U, 0x15253027U, 0x4aaee478U, 0xee23cddcU,
+	0x56b5e364U, 0x67c9ae55U, 0x90c050a2U, 0x239cbf11U,
+	0x52b0e260U, 0x00747432U, 0xea26ccd8U, 0xf5fd08c7U,
+	0x50306062U, 0xeb668dd9U, 0xb66ddb84U, 0x628cee50U,
+	0xdf5f80edU, 0xe0ac4cd2U, 0xe22cced0U, 0x9ccf53aeU,
+	0x88de56baU, 0xf3788bc1U, 0xbb229989U, 0x29163f1bU,
+	0xbce75b8eU, 0x45612477U, 0x98ca52aaU, 0x13a0b321U,
+	0x36cdfb04U, 0xab369d99U, 0xd615c3e4U, 0xf77d8ac5U,
+	0x4d6b267fU, 0xc5c104f7U, 0x482e667aU, 0x73d8ab41U,
+	0x69462f5bU, 0x344d7906U, 0x8a5ed4b8U, 0x5c3f636eU,
+	0xdddf02efU, 0xda1ac0e8U, 0x597a236bU, 0xacf35f9eU,
+	0xf8b24acaU, 0x859114b7U, 0xa8f65e9aU, 0x11203123U,
+	0x2f93bc1dU, 0xc48145f6U, 0x06f1f734U, 0x600c6c52U,
+	0xe7698ed5U, 0xe629cfd4U, 0x64096d56U, 0x093e373bU,
+	0x350d3807U, 0x378dba05U, 0xa679df94U, 0x10607022U,
+	0xf4bd49c6U, 0x9f0f90adU, 0x0afef438U, 0x9e4fd1acU,
+	0x614c2d53U, 0xeca34fdeU, 0xc74186f5U, 0x583a626aU,
+	0x5bfaa169U, 0x57f5a265U, 0xfcb74bceU, 0x30487802U,
+	0x3f87b80dU, 0x6bc6ad59U, 0x918011a3U, 0xd49541e6U,
+	0x192a332bU, 0x55752067U, 0xc601c7f4U, 0xd75582e5U,
+	0x3b82b909U, 0xa33c9f91U, 0x8cdb57beU, 0x2ed3fd1cU,
+	0x8254d6b0U, 0x6a86ec58U, 0xc204c6f0U, 0x831497b1U,
+	0x77ddaa45U, 0x9b0a91a9U, 0xbda71a8fU, 0x70186842U,
+	0x6e83ed5cU, 0x7298ea40U, 0x0bbeb539U, 0x16e5f324U,
+	0xfe37c9ccU, 0x7e97e94cU, 0x1baab129U, 0xe4a94dd6U,
+	0xa9b61f9bU, 0xfa32c8c8U, 0xe36c8fd1U, 0xa7399e95U,
+	0x84d155b6U, 0xb5ad1887U, 0x6689ef54U, 0xb3289b81U,
+	0xcb4e85f9U, 0x22dcfe10U, 0xf238cac0U, 0x65492c57U,
+	0x78126a4aU, 0xe9e60fdbU, 0xd9da03ebU, 0x80d454b2U,
+	0x51702163U, 0x04717536U, 0xd09040e2U, 0xb1a81983U,
+	0xfb7289c9U, 0xe5e90cd7U, 0x1aeaf028U, 0x2c537f1eU,
+	0x741d6946U, 0x25193c17U, 0x31083903U, 0xc1c405f3U,
+	0x1d2f322fU, 0x7fd7a84dU, 0xae73dd9cU, 0x7c176b4eU,
+	0x79522b4bU, 0x40246472U, 0x8f1b94bdU, 0x68066e5aU,
+	0x94c551a6U, 0x2ad6fc18U, 0x9240d2a0U, 0xaf339c9dU,
+	0x4c2b677eU, 0xb4ed5986U, 0x32c8fa00U, 0x3ac2f808U,
+	0x2d133e1fU, 0xc88e46faU, 0x3c477b0eU, 0x755d2847U,
+	0x8e5bd5bcU, 0x211c3d13U, 0x71582943U, 0x42a4e670U,
+	0xf0b848c2U, 0x63ccaf51U, 0xd1d001e3U, 0x7bd2a949U,
+	0xf9f20bcbU, 0x5ebfe16cU, 0x769deb44U, 0x54356166U,
+	0xce0bc5fcU, 0xb72d9a85U, 0xc34487f1U, 0x47e1a675U,
+	0xadb31e9fU, 0x958510a7U, 0x26d9ff14U, 0xef638cddU,
+	0x819415b3U, 0x0d3b363fU, 0xc08444f2U, 0x46a1e774U,
+	0xf63dcbc4U, 0x05313437U, 0x53f0a361U, 0x3388bb01U,
+	0x4feba47dU, 0x4beea579U, 0x970592a5U, 0xbf27988dU,
+	0xcc8b47feU, 0x9645d3a4U, 0x44216576U, 0xb0e85882U,
+	0x14657126U, 0x8d9b16bfU, 0x087e763aU, 0x07b1b635U,
+	0xbe67d98cU, 0x9d8f12afU, 0x8b1e95b9U, 0xff7788cdU,
+	0x8651d7b4U, 0xba62d888U, 0x930093a1U, 0x998a13abU,
+	0xd210c2e0U, 0xfdf70acfU, 0x3ec7f90cU, 0xd89a42eaU,
+	0x6c036f5eU, 0xcdcb06ffU, 0x17a5b225U, 0x0c7b773eU,
+	0x4eabe57cU, 0x2b96bd19U, 0xb9a21b8bU, 0xb268da80U,
+	0x186a722aU, 0x6d432e5fU, 0x28567e1aU, 0x38427a0aU,
+	0xdc9f43eeU, 0x5fffa06dU, 0xa4f95d96U, 0xd35083e1U,
+	0xca0ec4f8U, 0xa0fc5c92U, 0x2799be15U, 0x03b4b731U,
+	0x6fc3ac5dU, 0x1c6f732eU, 0x3d073a0fU, 0xde1fc1ecU,
+	0xede30edfU, 0x43e4a771U, 0xe1ec0dd3U, 0xe8a64edaU,
+	0x9a4ad0a8U, 0x39023b0bU, 0x0fbbb43dU, 0x496e277bU,
+	0x12e0f220U, 0x899e17bbU, 0xcf4b84fdU, 0x7a92e848U,
+	0x1fafb02dU, 0x41642573U, 0x871196b5U, 0x02f4f630U,
+	0xa5b91c97U, 0xd5d500e7U, 0x1eeff12cU, 0xdb5a81e9U,
+	0xabbe159bU, 0xb2e05282U, 0x577d2a67U, 0x0b363d3bU,
+	0xc3cc0ff3U, 0x77552247U, 0x04f9fd34U, 0x2e5b751eU,
+	0xa87ed698U, 0x2a5e741aU, 0xa074d490U, 0xfbfa01cbU,
+	0x50b8e860U, 0x1f27382fU, 0x40acec70U, 0xe421c5d4U,
+	0x5cb7eb6cU, 0x6dcba65dU, 0x9ac258aaU, 0x299eb719U,
+	0x58b2ea68U, 0x0a767c3aU, 0xe024c4d0U, 0xffff00cfU,
+	0x5a32686aU, 0xe16485d1U, 0xbc6fd38cU, 0x688ee658U,
+	0xd55d88e5U, 0xeaae44daU, 0xe82ec6d8U, 0x96cd5ba6U,
+	0x82dc5eb2U, 0xf97a83c9U, 0xb1209181U, 0x23143713U,
+	0xb6e55386U, 0x4f632c7fU, 0x92c85aa2U, 0x19a2bb29U,
+	0x3ccff30cU, 0xa1349591U, 0xdc17cbecU, 0xfd7f82cdU,
+	0x47692e77U, 0xcfc30cffU, 0x422c6e72U, 0x79daa349U,
+	0x63442753U, 0x3e4f710eU, 0x805cdcb0U, 0x563d6b66U,
+	0xd7dd0ae7U, 0xd018c8e0U, 0x53782b63U, 0xa6f15796U,
+	0xf2b042c2U, 0x8f931cbfU, 0xa2f45692U, 0x1b22392bU,
+	0x2591b415U, 0xce834dfeU, 0x0cf3ff3cU, 0x6a0e645aU,
+	0xed6b86ddU, 0xec2bc7dcU, 0x6e0b655eU, 0x033c3f33U,
+	0x3f0f300fU, 0x3d8fb20dU, 0xac7bd79cU, 0x1a62782aU,
+	0xfebf41ceU, 0x950d98a5U, 0x00fcfc30U, 0x944dd9a4U,
+	0x6b4e255bU, 0xe6a147d6U, 0xcd438efdU, 0x52386a62U,
+	0x51f8a961U, 0x5df7aa6dU, 0xf6b543c6U, 0x3a4a700aU,
+	0x3585b005U, 0x61c4a551U, 0x9b8219abU, 0xde9749eeU,
+	0x13283b23U, 0x5f77286fU, 0xcc03cffcU, 0xdd578aedU,
+	0x3180b101U, 0xa93e9799U, 0x86d95fb6U, 0x24d1f514U,
+	0x8856deb8U, 0x6084e450U, 0xc806cef8U, 0x89169fb9U,
+	0x7ddfa24dU, 0x910899a1U, 0xb7a51287U, 0x7a1a604aU,
+	0x6481e554U, 0x789ae248U, 0x01bcbd31U, 0x1ce7fb2cU,
+	0xf435c1c4U, 0x7495e144U, 0x11a8b921U, 0xeeab45deU,
+	0xa3b41793U, 0xf030c0c0U, 0xe96e87d9U, 0xad3b969dU,
+	0x8ed35dbeU, 0xbfaf108fU, 0x6c8be75cU, 0xb92a9389U,
+	0xc14c8df1U, 0x28def618U, 0xf83ac2c8U, 0x6f4b245fU,
+	0x72106242U, 0xe3e407d3U, 0xd3d80be3U, 0x8ad65cbaU,
+	0x5b72296bU, 0x0e737d3eU, 0xda9248eaU, 0xbbaa118bU,
+	0xf17081c1U, 0xefeb04dfU, 0x10e8f820U, 0x26517716U,
+	0x7e1f614eU, 0x2f1b341fU, 0x3b0a310bU, 0xcbc60dfbU,
+	0x172d3a27U, 0x75d5a045U, 0xa471d594U, 0x76156346U,
+	0x73502343U, 0x4a266c7aU, 0x85199cb5U, 0x62046652U,
+	0x9ec759aeU, 0x20d4f410U, 0x9842daa8U, 0xa5319495U,
+	0x46296f76U, 0xbeef518eU, 0x38caf208U, 0x30c0f000U,
+	0x27113617U, 0xc28c4ef2U, 0x36457306U, 0x7f5f204fU,
+	0x8459ddb4U, 0x2b1e351bU, 0x7b5a214bU, 0x48a6ee78U,
+	0xfaba40caU, 0x69cea759U, 0xdbd209ebU, 0x71d0a141U,
+	0xf3f003c3U, 0x54bde964U, 0x7c9fe34cU, 0x5e37696eU,
+	0xc409cdf4U, 0xbd2f928dU, 0xc9468ff9U, 0x4de3ae7dU,
+	0xa7b11697U, 0x9f8718afU, 0x2cdbf71cU, 0xe56184d5U,
+	0x8b961dbbU, 0x07393e37U, 0xca864cfaU, 0x4ca3ef7cU,
+	0xfc3fc3ccU, 0x0f333c3fU, 0x59f2ab69U, 0x398ab309U,
+	0x45e9ac75U, 0x41ecad71U, 0x9d079aadU, 0xb5259085U,
+	0xc6894ff6U, 0x9c47dbacU, 0x4e236d7eU, 0xbaea508aU,
+	0x1e67792eU, 0x87991eb7U, 0x027c7e32U, 0x0db3be3dU,
+	0xb465d184U, 0x978d1aa7U, 0x811c9db1U, 0xf57580c5U,
+	0x8c53dfbcU, 0xb060d080U, 0x99029ba9U, 0x93881ba3U,
+	0xd812cae8U, 0xf7f502c7U, 0x34c5f104U, 0xd2984ae2U,
+	0x66016756U, 0xc7c90ef7U, 0x1da7ba2dU, 0x06797f36U,
+	0x44a9ed74U, 0x2194b511U, 0xb3a01383U, 0xb86ad288U,
+	0x12687a22U, 0x67412657U, 0x22547612U, 0x32407202U,
+	0xd69d4be6U, 0x55fda865U, 0xaefb559eU, 0xd9528be9U,
+	0xc00cccf0U, 0xaafe549aU, 0x2d9bb61dU, 0x09b6bf39U,
+	0x65c1a455U, 0x166d7b26U, 0x37053207U, 0xd41dc9e4U,
+	0xe7e106d7U, 0x49e6af79U, 0xebee05dbU, 0xe2a446d2U,
+	0x9048d8a0U, 0x33003303U, 0x05b9bc35U, 0x436c2f73U,
+	0x18e2fa28U, 0x839c1fb3U, 0xc5498cf5U, 0x7090e040U,
+	0x15adb825U, 0x4b662d7bU, 0x8d139ebdU, 0x08f6fe38U,
+	0xafbb149fU, 0xdfd708efU, 0x14edf924U, 0xd15889e1U,
+	0x7a78028cU, 0x63264595U, 0x86bb3d70U, 0xdaf02a2cU,
+	0x120a18e4U, 0xa6933550U, 0xd53fea23U, 0xff9d6209U,
+	0x79b8c18fU, 0xfb98630dU, 0x71b2c387U, 0x2a3c16dcU,
+	0x817eff77U, 0xcee12f38U, 0x916afb67U, 0x35e7d2c3U,
+	0x8d71fc7bU, 0xbc0db14aU, 0x4b044fbdU, 0xf858a00eU,
+	0x8974fd7fU, 0xdbb06b2dU, 0x31e2d3c7U, 0x2e3917d8U,
+	0x8bf47f7dU, 0x30a292c6U, 0x6da9c49bU, 0xb948f14fU,
+	0x049b9ff2U, 0x3b6853cdU, 0x39e8d1cfU, 0x470b4cb1U,
+	0x531a49a5U, 0x28bc94deU, 0x60e68696U, 0xf2d22004U,
+	0x67234491U, 0x9ea53b68U, 0x430e4db5U, 0xc864ac3eU,
+	0xed09e41bU, 0x70f28286U, 0x0dd1dcfbU, 0x2cb995daU,
+	0x96af3960U, 0x1e051be8U, 0x93ea7965U, 0xa81cb45eU,
+	0xb2823044U, 0xef896619U, 0x519acba7U, 0x87fb7c71U,
+	0x061b1df0U, 0x01dedff7U, 0x82be3c74U, 0x77374081U,
+	0x237655d5U, 0x5e550ba8U, 0x73324185U, 0xcae42e3cU,
+	0xf457a302U, 0x1f455ae9U, 0xdd35e82bU, 0xbbc8734dU,
+	0x3cad91caU, 0x3dedd0cbU, 0xbfcd7249U, 0xd2fa2824U,
+	0xeec92718U, 0xec49a51aU, 0x7dbdc08bU, 0xcba46f3dU,
+	0x2f7956d9U, 0x44cb8fb2U, 0xd13aeb27U, 0x458bceb3U,
+	0xba88324cU, 0x376750c1U, 0x1c8599eaU, 0x83fe7d75U,
+	0x803ebe76U, 0x8c31bd7aU, 0x277354d1U, 0xeb8c671dU,
+	0xe443a712U, 0xb002b246U, 0x4a440ebcU, 0x0f515ef9U,
+	0xc2ee2c34U, 0x8eb13f78U, 0x1dc5d8ebU, 0x0c919dfaU,
+	0xe046a616U, 0x78f8808eU, 0x571f48a1U, 0xf517e203U,
+	0x5990c9afU, 0xb142f347U, 0x19c0d9efU, 0x58d088aeU,
+	0xac19b55aU, 0x40ce8eb6U, 0x66630590U, 0xabdc775dU,
+	0xb547f243U, 0xa95cf55fU, 0xd07aaa26U, 0xcd21ec3bU,
+	0x25f3d6d3U, 0xa553f653U, 0xc06eae36U, 0x3f6d52c9U,
+	0x72720084U, 0x21f6d7d7U, 0x38a890ceU, 0x7cfd818aU,
+	0x5f154aa9U, 0x6e690798U, 0xbd4df04bU, 0x68ec849eU,
+	0x108a9ae6U, 0xf918e10fU, 0x29fcd5dfU, 0xbe8d3348U,
+	0xa3d67555U, 0x322210c4U, 0x021e1cf4U, 0x5b104badU,
+	0x8ab43e7cU, 0xdfb56a29U, 0x0b545ffdU, 0x6a6c069cU,
+	0x20b696d6U, 0x3e2d13c8U, 0xc12eef37U, 0xf7976001U,
+	0xafd97659U, 0xfedd2308U, 0xeacc261cU, 0x1a001aecU,
+	0xc6eb2d30U, 0xa413b752U, 0x75b7c283U, 0xa7d37451U,
+	0xa2963454U, 0x9be07b6dU, 0x54df8ba2U, 0xb3c27145U,
+	0x4f014eb9U, 0xf112e307U, 0x4984cdbfU, 0x74f78382U,
+	0x97ef7861U, 0x6f294699U, 0xe90ce51fU, 0xe106e717U,
+	0xf6d72100U, 0x134a59e5U, 0xe7836411U, 0xae993758U,
+	0x559fcaa3U, 0xfad8220cU, 0xaa9c365cU, 0x9960f96fU,
+	0x2b7c57ddU, 0xb808b04eU, 0x0a141efcU, 0xa016b656U,
+	0x223614d4U, 0x857bfe73U, 0xad59f45bU, 0x8ff17e79U,
+	0x15cfdae3U, 0x6ce9859aU, 0x188098eeU, 0x9c25b96aU,
+	0x76770180U, 0x4e410fb8U, 0xfd1de00bU, 0x34a793c2U,
+	0x5a500aacU, 0xd6ff2920U, 0x1b405bedU, 0x9d65f86bU,
+	0x2df9d4dbU, 0xdef52b28U, 0x8834bc7eU, 0xe84ca41eU,
+	0x942fbb62U, 0x902aba66U, 0x4cc18dbaU, 0x64e38792U,
+	0x174f58e1U, 0x4d81ccbbU, 0x9fe57a69U, 0x6b2c479dU,
+	0xcfa16e39U, 0x565f09a0U, 0xd3ba6925U, 0xdc75a92aU,
+	0x65a3c693U, 0x464b0db0U, 0x50da8aa6U, 0x24b397d2U,
+	0x5d95c8abU, 0x61a6c797U, 0x48c48cbeU, 0x424e0cb4U,
+	0x09d4ddffU, 0x263315d0U, 0xe503e613U, 0x035e5df5U,
+	0xb7c77041U, 0x160f19e0U, 0xcc61ad3aU, 0xd7bf6821U,
+	0x956ffa63U, 0xf052a206U, 0x62660494U, 0x69acc59fU,
+	0xc3ae6d35U, 0xb6873140U, 0xf3926105U, 0xe3866515U,
+	0x075b5cf1U, 0x843bbf72U, 0x7f3d4289U, 0x08949cfeU,
+	0x11cadbe7U, 0x7b38438dU, 0xfc5da10aU, 0xd870a82eU,
+	0xb407b342U, 0xc7ab6c31U, 0xe6c32510U, 0x05dbdef3U,
+	0x362711c0U, 0x9820b86eU, 0x3a2812ccU, 0x336251c5U,
+	0x418ecfb7U, 0xe2c62414U, 0xd47fab22U, 0x92aa3864U,
+	0xc924ed3fU, 0x525a08a4U, 0x148f9be2U, 0xa156f757U,
+	0xc46baf32U, 0x9aa03a6cU, 0x5cd589aaU, 0xd930e92fU,
+	0x7e7d0388U, 0x0e111ff8U, 0xc52bee33U, 0x009e9ef6U,
+	0xe700e769U, 0xfe5ea070U, 0x1bc3d895U, 0x4788cfc9U,
+	0x8f72fd01U, 0x3bebd0b5U, 0x48470fc6U, 0x62e587ecU,
+	0xe4c0246aU, 0x66e086e8U, 0xecca2662U, 0xb744f339U,
+	0x1c061a92U, 0x5399caddU, 0x0c121e82U, 0xa89f3726U,
+	0x1009199eU, 0x217554afU, 0xd67caa58U, 0x652045ebU,
+	0x140c189aU, 0x46c88ec8U, 0xac9a3622U, 0xb341f23dU,
+	0x168c9a98U, 0xadda7723U, 0xf0d1217eU, 0x243014aaU,
+	0x99e37a17U, 0xa610b628U, 0xa490342aU, 0xda73a954U,
+	0xce62ac40U, 0xb5c4713bU, 0xfd9e6373U, 0x6faac5e1U,
+	0xfa5ba174U, 0x03ddde8dU, 0xde76a850U, 0x551c49dbU,
+	0x707101feU, 0xed8a6763U, 0x90a9391eU, 0xb1c1703fU,
+	0x0bd7dc85U, 0x837dfe0dU, 0x0e929c80U, 0x356451bbU,
+	0x2ffad5a1U, 0x72f183fcU, 0xcce22e42U, 0x1a839994U,
+	0x9b63f815U, 0x9ca63a12U, 0x1fc6d991U, 0xea4fa564U,
+	0xbe0eb030U, 0xc32dee4dU, 0xee4aa460U, 0x579ccbd9U,
+	0x692f46e7U, 0x823dbf0cU, 0x404d0dceU, 0x26b096a8U,
+	0xa1d5742fU, 0xa095352eU, 0x22b597acU, 0x4f82cdc1U,
+	0x73b1c2fdU, 0x713140ffU, 0xe0c5256eU, 0x56dc8ad8U,
+	0xb201b33cU, 0xd9b36a57U, 0x4c420ec2U, 0xd8f32b56U,
+	0x27f0d7a9U, 0xaa1fb524U, 0x81fd7c0fU, 0x1e869890U,
+	0x1d465b93U, 0x1149589fU, 0xba0bb134U, 0x76f482f8U,
+	0x793b42f7U, 0x2d7a57a3U, 0xd73ceb59U, 0x9229bb1cU,
+	0x5f96c9d1U, 0x13c9da9dU, 0x80bd3d0eU, 0x91e9781fU,
+	0x7d3e43f3U, 0xe580656bU, 0xca67ad44U, 0x686f07e6U,
+	0xc4e82c4aU, 0x2c3a16a2U, 0x84b83c0aU, 0xc5a86d4bU,
+	0x316150bfU, 0xddb66b53U, 0xfb1be075U, 0x36a492b8U,
+	0x283f17a6U, 0x342410baU, 0x4d024fc3U, 0x505909deU,
+	0xb88b3336U, 0x382b13b6U, 0x5d164bd3U, 0xa215b72cU,
+	0xef0ae561U, 0xbc8e3232U, 0xa5d0752bU, 0xe185646fU,
+	0xc26daf4cU, 0xf311e27dU, 0x203515aeU, 0xf594617bU,
+	0x8df27f03U, 0x646004eaU, 0xb484303aU, 0x23f5d6adU,
+	0x3eae90b0U, 0xaf5af521U, 0x9f66f911U, 0xc668ae48U,
+	0x17ccdb99U, 0x42cd8fccU, 0x962cba18U, 0xf714e379U,
+	0xbdce7333U, 0xa355f62dU, 0x5c560ad2U, 0x6aef85e4U,
+	0x32a193bcU, 0x63a5c6edU, 0x77b4c3f9U, 0x8778ff09U,
+	0x5b93c8d5U, 0x396b52b7U, 0xe8cf2766U, 0x3aab91b4U,
+	0x3feed1b1U, 0x06989e88U, 0xc9a76e47U, 0x2eba94a0U,
+	0xd279ab5cU, 0x6c6a06e2U, 0xd4fc285aU, 0xe98f6667U,
+	0x0a979d84U, 0xf251a37cU, 0x747400faU, 0x7c7e02f2U,
+	0x6bafc4e5U, 0x8e32bc00U, 0x7afb81f4U, 0x33e1d2bdU,
+	0xc8e72f46U, 0x67a0c7e9U, 0x37e4d3b9U, 0x04181c8aU,
+	0xb604b238U, 0x257055abU, 0x976cfb19U, 0x3d6e53b3U,
+	0xbf4ef131U, 0x18031b96U, 0x302111beU, 0x12899b9cU,
+	0x88b73f06U, 0xf191607fU, 0x85f87d0bU, 0x015d5c8fU,
+	0xeb0fe465U, 0xd339ea5dU, 0x606505eeU, 0xa9df7627U,
+	0xc728ef49U, 0x4b87ccc5U, 0x8638be08U, 0x001d1d8eU,
+	0xb081313eU, 0x438dcecdU, 0x154c599bU, 0x753441fbU,
+	0x09575e87U, 0x0d525f83U, 0xd1b9685fU, 0xf99b6277U,
+	0x8a37bd04U, 0xd0f9295eU, 0x029d9f8cU, 0xf654a278U,
+	0x52d98bdcU, 0xcb27ec45U, 0x4ec28cc0U, 0x410d4ccfU,
+	0xf8db2376U, 0xdb33e855U, 0xcda26f43U, 0xb9cb7237U,
+	0xc0ed2d4eU, 0xfcde2272U, 0xd5bc695bU, 0xdf36e951U,
+	0x94ac381aU, 0xbb4bf035U, 0x787b03f6U, 0x9e26b810U,
+	0x2abf95a4U, 0x8b77fc05U, 0x511948dfU, 0x4ac78dc4U,
+	0x08171f86U, 0x6d2a47e3U, 0xff1ee171U, 0xf4d4207aU,
+	0x5ed688d0U, 0x2bffd4a5U, 0x6eea84e0U, 0x7efe80f0U,
+	0x9a23b914U, 0x19435a97U, 0xe245a76cU, 0x95ec791bU,
+	0x8cb23e02U, 0xe640a668U, 0x612544efU, 0x45084dcbU,
+	0x297f56a7U, 0x5ad389d4U, 0x7bbbc0f5U, 0x98a33b16U,
+	0xab5ff425U, 0x05585d8bU, 0xa750f729U, 0xae1ab420U,
+	0xdcf62a52U, 0x7fbec1f1U, 0x49074ec7U, 0x0fd2dd81U,
+	0x545c08daU, 0xcf22ed41U, 0x89f77e07U, 0x3c2e12b2U,
+	0x59134ad7U, 0x07d8df89U, 0xc1ad6c4fU, 0x44480ccaU,
+	0xe305e66dU, 0x9369fa1dU, 0x58530bd6U, 0x9de67b13U,
+	0x2e3d139dU, 0x37635484U, 0xd2fe2c61U, 0x8eb53b3dU,
+	0x464f09f5U, 0xf2d62441U, 0x817afb32U, 0xabd87318U,
+	0x2dfdd09eU, 0xafdd721cU, 0x25f7d296U, 0x7e7907cdU,
+	0xd53bee66U, 0x9aa43e29U, 0xc52fea76U, 0x61a2c3d2U,
+	0xd934ed6aU, 0xe848a05bU, 0x1f415eacU, 0xac1db11fU,
+	0xdd31ec6eU, 0x8ff57a3cU, 0x65a7c2d6U, 0x7a7c06c9U,
+	0xdfb16e6cU, 0x64e783d7U, 0x39ecd58aU, 0xed0de05eU,
+	0x50de8ee3U, 0x6f2d42dcU, 0x6dadc0deU, 0x134e5da0U,
+	0x075f58b4U, 0x7cf985cfU, 0x34a39787U, 0xa6973115U,
+	0x33665580U, 0xcae02a79U, 0x174b5ca4U, 0x9c21bd2fU,
+	0xb94cf50aU, 0x24b79397U, 0x5994cdeaU, 0x78fc84cbU,
+	0xc2ea2871U, 0x4a400af9U, 0xc7af6874U, 0xfc59a54fU,
+	0xe6c72155U, 0xbbcc7708U, 0x05dfdab6U, 0xd3be6d60U,
+	0x525e0ce1U, 0x559bcee6U, 0xd6fb2d65U, 0x23725190U,
+	0x773344c4U, 0x0a101ab9U, 0x27775094U, 0x9ea13f2dU,
+	0xa012b213U, 0x4b004bf8U, 0x8970f93aU, 0xef8d625cU,
+	0x68e880dbU, 0x69a8c1daU, 0xeb886358U, 0x86bf3935U,
+	0xba8c3609U, 0xb80cb40bU, 0x29f8d19aU, 0x9fe17e2cU,
+	0x7b3c47c8U, 0x108e9ea3U, 0x857ffa36U, 0x11cedfa2U,
+	0xeecd235dU, 0x632241d0U, 0x48c088fbU, 0xd7bb6c64U,
+	0xd47baf67U, 0xd874ac6bU, 0x733645c0U, 0xbfc9760cU,
+	0xb006b603U, 0xe447a357U, 0x1e011fadU, 0x5b144fe8U,
+	0x96ab3d25U, 0xdaf42e69U, 0x4980c9faU, 0x58d48cebU,
+	0xb403b707U, 0x2cbd919fU, 0x035a59b0U, 0xa152f312U,
+	0x0dd5d8beU, 0xe507e256U, 0x4d85c8feU, 0x0c9599bfU,
+	0xf85ca44bU, 0x148b9fa7U, 0x32261481U, 0xff99664cU,
+	0xe102e352U, 0xfd19e44eU, 0x843fbb37U, 0x9964fd2aU,
+	0x71b6c7c2U, 0xf116e742U, 0x942bbf27U, 0x6b2843d8U,
+	0x26371195U, 0x75b3c6c6U, 0x6ced81dfU, 0x28b8909bU,
+	0x0b505bb8U, 0x3a2c1689U, 0xe908e15aU, 0x3ca9958fU,
+	0x44cf8bf7U, 0xad5df01eU, 0x7db9c4ceU, 0xeac82259U,
+	0xf7936444U, 0x666701d5U, 0x565b0de5U, 0x0f555abcU,
+	0xdef12f6dU, 0x8bf07b38U, 0x5f114eecU, 0x3e29178dU,
+	0x74f387c7U, 0x6a6802d9U, 0x956bfe26U, 0xa3d27110U,
+	0xfb9c6748U, 0xaa983219U, 0xbe89370dU, 0x4e450bfdU,
+	0x92ae3c21U, 0xf056a643U, 0x21f2d392U, 0xf3966540U,
+	0xf6d32545U, 0xcfa56a7cU, 0x009a9ab3U, 0xe7876054U,
+	0x1b445fa8U, 0xa557f216U, 0x1dc1dcaeU, 0x20b29293U,
+	0xc3aa6970U, 0x3b6c5788U, 0xbd49f40eU, 0xb543f606U,
+	0xa2923011U, 0x470f48f4U, 0xb3c67500U, 0xfadc2649U,
+	0x01dadbb2U, 0xae9d331dU, 0xfed9274dU, 0xcd25e87eU,
+	0x7f3946ccU, 0xec4da15fU, 0x5e510fedU, 0xf453a747U,
+	0x767305c5U, 0xd13eef62U, 0xf91ce54aU, 0xdbb46f68U,
+	0x418acbf2U, 0x38ac948bU, 0x4cc589ffU, 0xc860a87bU,
+	0x22321091U, 0x1a041ea9U, 0xa958f11aU, 0x60e282d3U,
+	0x0e151bbdU, 0x82ba3831U, 0x4f054afcU, 0xc920e97aU,
+	0x79bcc5caU, 0x8ab03a39U, 0xdc71ad6fU, 0xbc09b50fU,
+	0xc06aaa73U, 0xc46fab77U, 0x18849cabU, 0x30a69683U,
+	0x430a49f0U, 0x19c4ddaaU, 0xcba06b78U, 0x3f69568cU,
+	0x9be47f28U, 0x021a18b1U, 0x87ff7834U, 0x8830b83bU,
+	0x31e6d782U, 0x120e1ca1U, 0x049f9bb7U, 0x70f686c3U,
+	0x09d0d9baU, 0x35e3d686U, 0x1c819dafU, 0x160b1da5U,
+	0x5d91cceeU, 0x727604c1U, 0xb146f702U, 0x571b4ce4U,
+	0xe3826150U, 0x424a08f1U, 0x9824bc2bU, 0x83fa7930U,
+	0xc12aeb72U, 0xa417b317U, 0x36231585U, 0x3de9d48eU,
+	0x97eb7c24U, 0xe2c22051U, 0xa7d77014U, 0xb7c37404U,
+	0x531e4de0U, 0xd07eae63U, 0x2b785398U, 0x5cd18defU,
+	0x458fcaf6U, 0x2f7d529cU, 0xa818b01bU, 0x8c35b93fU,
+	0xe042a253U, 0x93ee7d20U, 0xb2863401U, 0x519ecfe2U,
+	0x626200d1U, 0xcc65a97fU, 0x6e6d03ddU, 0x672740d4U,
+	0x15cbdea6U, 0xb6833505U, 0x803aba33U, 0xc6ef2975U,
+	0x9d61fc2eU, 0x061f19b5U, 0x40ca8af3U, 0xf513e646U,
+	0x902ebe23U, 0xcee52b7dU, 0x089098bbU, 0x8d75f83eU,
+	0x2a381299U, 0x5a540ee9U, 0x916eff22U, 0x54db8fe7U,
+	0x346e5ad4U, 0x2d301dcdU, 0xc8ad6528U, 0x94e67274U,
+	0x5c1c40bcU, 0xe8856d08U, 0x9b29b27bU, 0xb18b3a51U,
+	0x37ae99d7U, 0xb58e3b55U, 0x3fa49bdfU, 0x642a4e84U,
+	0xcf68a72fU, 0x80f77760U, 0xdf7ca33fU, 0x7bf18a9bU,
+	0xc367a423U, 0xf21be912U, 0x051217e5U, 0xb64ef856U,
+	0xc762a527U, 0x95a63375U, 0x7ff48b9fU, 0x602f4f80U,
+	0xc5e22725U, 0x7eb4ca9eU, 0x23bf9cc3U, 0xf75ea917U,
+	0x4a8dc7aaU, 0x757e0b95U, 0x77fe8997U, 0x091d14e9U,
+	0x1d0c11fdU, 0x66aacc86U, 0x2ef0deceU, 0xbcc4785cU,
+	0x29351cc9U, 0xd0b36330U, 0x0d1815edU, 0x8672f466U,
+	0xa31fbc43U, 0x3ee4dadeU, 0x43c784a3U, 0x62afcd82U,
+	0xd8b96138U, 0x501343b0U, 0xddfc213dU, 0xe60aec06U,
+	0xfc94681cU, 0xa19f3e41U, 0x1f8c93ffU, 0xc9ed2429U,
+	0x480d45a8U, 0x4fc887afU, 0xcca8642cU, 0x392118d9U,
+	0x6d600d8dU, 0x104353f0U, 0x3d2419ddU, 0x84f27664U,
+	0xba41fb5aU, 0x515302b1U, 0x9323b073U, 0xf5de2b15U,
+	0x72bbc992U, 0x73fb8893U, 0xf1db2a11U, 0x9cec707cU,
+	0xa0df7f40U, 0xa25ffd42U, 0x33ab98d3U, 0x85b23765U,
+	0x616f0e81U, 0x0addd7eaU, 0x9f2cb37fU, 0x0b9d96ebU,
+	0xf49e6a14U, 0x79710899U, 0x5293c1b2U, 0xcde8252dU,
+	0xce28e62eU, 0xc227e522U, 0x69650c89U, 0xa59a3f45U,
+	0xaa55ff4aU, 0xfe14ea1eU, 0x045256e4U, 0x414706a1U,
+	0x8cf8746cU, 0xc0a76720U, 0x53d380b3U, 0x4287c5a2U,
+	0xae50fe4eU, 0x36eed8d6U, 0x190910f9U, 0xbb01ba5bU,
+	0x178691f7U, 0xff54ab1fU, 0x57d681b7U, 0x16c6d0f6U,
+	0xe20fed02U, 0x0ed8d6eeU, 0x28755dc8U, 0xe5ca2f05U,
+	0xfb51aa1bU, 0xe74aad07U, 0x9e6cf27eU, 0x8337b463U,
+	0x6be58e8bU, 0xeb45ae0bU, 0x8e78f66eU, 0x717b0a91U,
+	0x3c6458dcU, 0x6fe08f8fU, 0x76bec896U, 0x32ebd9d2U,
+	0x110312f1U, 0x207f5fc0U, 0xf35ba813U, 0x26fadcc6U,
+	0x5e9cc2beU, 0xb70eb957U, 0x67ea8d87U, 0xf09b6b10U,
+	0xedc02d0dU, 0x7c34489cU, 0x4c0844acU, 0x150613f5U,
+	0xc4a26624U, 0x91a33271U, 0x454207a5U, 0x247a5ec4U,
+	0x6ea0ce8eU, 0x703b4b90U, 0x8f38b76fU, 0xb9813859U,
+	0xe1cf2e01U, 0xb0cb7b50U, 0xa4da7e44U, 0x541642b4U,
+	0x88fd7568U, 0xea05ef0aU, 0x3ba19adbU, 0xe9c52c09U,
+	0xec806c0cU, 0xd5f62335U, 0x1ac9d3faU, 0xfdd4291dU,
+	0x011716e1U, 0xbf04bb5fU, 0x079295e7U, 0x3ae1dbdaU,
+	0xd9f92039U, 0x213f1ec1U, 0xa71abd47U, 0xaf10bf4fU,
+	0xb8c17958U, 0x5d5c01bdU, 0xa9953c49U, 0xe08f6f00U,
+	0x1b8992fbU, 0xb4ce7a54U, 0xe48a6e04U, 0xd776a137U,
+	0x656a0f85U, 0xf61ee816U, 0x440246a4U, 0xee00ee0eU,
+	0x6c204c8cU, 0xcb6da62bU, 0xe34fac03U, 0xc1e72621U,
+	0x5bd982bbU, 0x22ffddc2U, 0x5696c0b6U, 0xd233e132U,
+	0x386159d8U, 0x005757e0U, 0xb30bb853U, 0x7ab1cb9aU,
+	0x144652f4U, 0x98e97178U, 0x555603b5U, 0xd373a033U,
+	0x63ef8c83U, 0x90e37370U, 0xc622e426U, 0xa65afc46U,
+	0xda39e33aU, 0xde3ce23eU, 0x02d7d5e2U, 0x2af5dfcaU,
+	0x595900b9U, 0x039794e3U, 0xd1f32231U, 0x253a1fc5U,
+	0x81b73661U, 0x184951f8U, 0x9dac317dU, 0x9263f172U,
+	0x2bb59ecbU, 0x085d55e8U, 0x1eccd2feU, 0x6aa5cf8aU,
+	0x138390f3U, 0x2fb09fcfU, 0x06d2d4e6U, 0x0c5854ecU,
+	0x47c285a7U, 0x68254d88U, 0xab15be4bU, 0x4d4805adU,
+	0xf9d12819U, 0x581941b8U, 0x8277f562U, 0x99a93079U,
+	0xdb79a23bU, 0xbe44fa5eU, 0x2c705cccU, 0x27ba9dc7U,
+	0x8db8356dU, 0xf8916918U, 0xbd84395dU, 0xad903d4dU,
+	0x494d04a9U, 0xca2de72aU, 0x312b1ad1U, 0x4682c4a6U,
+	0x5fdc83bfU, 0x352e1bd5U, 0xb24bf952U, 0x9666f076U,
+	0xfa11eb1aU, 0x89bd3469U, 0xa8d57d48U, 0x4bcd86abU,
+	0x78314998U, 0xd636e036U, 0x743e4a94U, 0x7d74099dU,
+	0x0f9897efU, 0xacd07c4cU, 0x9a69f37aU, 0xdcbc603cU,
+	0x8732b567U, 0x1c4c50fcU, 0x5a99c3baU, 0xef40af0fU,
+	0x8a7df76aU, 0xd4b66234U, 0x12c3d1f2U, 0x9726b177U,
+	0x306b5bd0U, 0x400747a0U, 0x8b3db66bU, 0x4e88c6aeU,
+	0x3293a12fU, 0x2bcde636U, 0xce509ed3U, 0x921b898fU,
+	0x5ae1bb47U, 0xee7896f3U, 0x9dd44980U, 0xb776c1aaU,
+	0x3153622cU, 0xb373c0aeU, 0x39596024U, 0x62d7b57fU,
+	0xc9955cd4U, 0x860a8c9bU, 0xd98158c4U, 0x7d0c7160U,
+	0xc59a5fd8U, 0xf4e612e9U, 0x03efec1eU, 0xb0b303adU,
+	0xc19f5edcU, 0x935bc88eU, 0x79097064U, 0x66d2b47bU,
+	0xc31fdcdeU, 0x78493165U, 0x25426738U, 0xf1a352ecU,
+	0x4c703c51U, 0x7383f06eU, 0x7103726cU, 0x0fe0ef12U,
+	0x1bf1ea06U, 0x6057377dU, 0x280d2535U, 0xba3983a7U,
+	0x2fc8e732U, 0xd64e98cbU, 0x0be5ee16U, 0x808f0f9dU,
+	0xa5e247b8U, 0x38192125U, 0x453a7f58U, 0x64523679U,
+	0xde449ac3U, 0x56eeb84bU, 0xdb01dac6U, 0xe0f717fdU,
+	0xfa6993e7U, 0xa762c5baU, 0x19716804U, 0xcf10dfd2U,
+	0x4ef0be53U, 0x49357c54U, 0xca559fd7U, 0x3fdce322U,
+	0x6b9df676U, 0x16bea80bU, 0x3bd9e226U, 0x820f8d9fU,
+	0xbcbc00a1U, 0x57aef94aU, 0x95de4b88U, 0xf323d0eeU,
+	0x74463269U, 0x75067368U, 0xf726d1eaU, 0x9a118b87U,
+	0xa62284bbU, 0xa4a206b9U, 0x35566328U, 0x834fcc9eU,
+	0x6792f57aU, 0x0c202c11U, 0x99d14884U, 0x0d606d10U,
+	0xf26391efU, 0x7f8cf362U, 0x546e3a49U, 0xcb15ded6U,
+	0xc8d51dd5U, 0xc4da1ed9U, 0x6f98f772U, 0xa367c4beU,
+	0xaca804b1U, 0xf8e911e5U, 0x02afad1fU, 0x47bafd5aU,
+	0x8a058f97U, 0xc65a9cdbU, 0x552e7b48U, 0x447a3e59U,
+	0xa8ad05b5U, 0x3013232dU, 0x1ff4eb02U, 0xbdfc41a0U,
+	0x117b6a0cU, 0xf9a950e4U, 0x512b7a4cU, 0x103b2b0dU,
+	0xe4f216f9U, 0x08252d15U, 0x2e88a633U, 0xe337d4feU,
+	0xfdac51e0U, 0xe1b756fcU, 0x98910985U, 0x85ca4f98U,
+	0x6d187570U, 0xedb855f0U, 0x88850d95U, 0x7786f16aU,
+	0x3a99a327U, 0x691d7474U, 0x7043336dU, 0x34162229U,
+	0x17fee90aU, 0x2682a43bU, 0xf5a653e8U, 0x2007273dU,
+	0x58613945U, 0xb1f342acU, 0x6117767cU, 0xf66690ebU,
+	0xeb3dd6f6U, 0x7ac9b367U, 0x4af5bf57U, 0x13fbe80eU,
+	0xc25f9ddfU, 0x975ec98aU, 0x43bffc5eU, 0x2287a53fU,
+	0x685d3575U, 0x76c6b06bU, 0x89c54c94U, 0xbf7cc3a2U,
+	0xe732d5faU, 0xb63680abU, 0xa22785bfU, 0x52ebb94fU,
+	0x8e008e93U, 0xecf814f1U, 0x3d5c6120U, 0xef38d7f2U,
+	0xea7d97f7U, 0xd30bd8ceU, 0x1c342801U, 0xfb29d2e6U,
+	0x07eaed1aU, 0xb9f940a4U, 0x016f6e1cU, 0x3c1c2021U,
+	0xdf04dbc2U, 0x27c2e53aU, 0xa1e746bcU, 0xa9ed44b4U,
+	0xbe3c82a3U, 0x5ba1fa46U, 0xaf68c7b2U, 0xe67294fbU,
+	0x1d746900U, 0xb23381afU, 0xe27795ffU, 0xd18b5accU,
+	0x6397f47eU, 0xf0e313edU, 0x42ffbd5fU, 0xe8fd15f5U,
+	0x6addb777U, 0xcd905dd0U, 0xe5b257f8U, 0xc71adddaU,
+	0x5d247940U, 0x24022639U, 0x506b3b4dU, 0xd4ce1ac9U,
+	0x3e9ca223U, 0x06aaac1bU, 0xb5f643a8U, 0x7c4c3061U,
+	0x12bba90fU, 0x9e148a83U, 0x53abf84eU, 0xd58e5bc8U,
+	0x65127778U, 0x961e888bU, 0xc0df1fddU, 0xa0a707bdU,
+	0xdcc418c1U, 0xd8c119c5U, 0x042a2e19U, 0x2c082431U,
+	0x5fa4fb42U, 0x056a6f18U, 0xd70ed9caU, 0x23c7e43eU,
+	0x874acd9aU, 0x1eb4aa03U, 0x9b51ca86U, 0x949e0a89U,
+	0x2d486530U, 0x0ea0ae13U, 0x18312905U, 0x6c583471U,
+	0x157e6b08U, 0x294d6434U, 0x002f2f1dU, 0x0aa5af17U,
+	0x413f7e5cU, 0x6ed8b673U, 0xade845b0U, 0x4bb5fe56U,
+	0xff2cd3e2U, 0x5ee4ba43U, 0x848a0e99U, 0x9f54cb82U,
+	0xdd8459c0U, 0xb8b901a5U, 0x2a8da737U, 0x2147663cU,
+	0x8b45ce96U, 0xfe6c92e3U, 0xbb79c2a6U, 0xab6dc6b6U,
+	0x4fb0ff52U, 0xccd01cd1U, 0x37d6e12aU, 0x407f3f5dU,
+	0x59217844U, 0x33d3e02eU, 0xb4b602a9U, 0x909b0b8dU,
+	0xfcec10e1U, 0x8f40cf92U, 0xae2886b3U, 0x4d307d50U,
+	0x7eccb263U, 0xd0cb1bcdU, 0x72c3b16fU, 0x7b89f266U,
+	0x09656c14U, 0xaa2d87b7U, 0x9c940881U, 0xda419bc7U,
+	0x81cf4e9cU, 0x1ab1ab07U, 0x5c643841U, 0xe9bd54f4U,
+	0x8c800c91U, 0xd24b99cfU, 0x143e2a09U, 0x91db4a8cU,
+	0x3696a02bU, 0x46fabc5bU, 0x8dc04d90U, 0x48753d55U,
+	0x757b0e80U, 0x6c254999U, 0x89b8317cU, 0xd5f32620U,
+	0x1d0914e8U, 0xa990395cU, 0xda3ce62fU, 0xf09e6e05U,
+	0x76bbcd83U, 0xf49b6f01U, 0x7eb1cf8bU, 0x253f1ad0U,
+	0x8e7df37bU, 0xc1e22334U, 0x9e69f76bU, 0x3ae4decfU,
+	0x8272f077U, 0xb30ebd46U, 0x440743b1U, 0xf75bac02U,
+	0x8677f173U, 0xd4b36721U, 0x3ee1dfcbU, 0x213a1bd4U,
+	0x84f77371U, 0x3fa19ecaU, 0x62aac897U, 0xb64bfd43U,
+	0x0b9893feU, 0x346b5fc1U, 0x36ebddc3U, 0x480840bdU,
+	0x5c1945a9U, 0x27bf98d2U, 0x6fe58a9aU, 0xfdd12c08U,
+	0x6820489dU, 0x91a63764U, 0x4c0d41b9U, 0xc767a032U,
+	0xe20ae817U, 0x7ff18e8aU, 0x02d2d0f7U, 0x23ba99d6U,
+	0x99ac356cU, 0x110617e4U, 0x9ce97569U, 0xa71fb852U,
+	0xbd813c48U, 0xe08a6a15U, 0x5e99c7abU, 0x88f8707dU,
+	0x091811fcU, 0x0eddd3fbU, 0x8dbd3078U, 0x78344c8dU,
+	0x2c7559d9U, 0x515607a4U, 0x7c314d89U, 0xc5e72230U,
+	0xfb54af0eU, 0x104656e5U, 0xd236e427U, 0xb4cb7f41U,
+	0x33ae9dc6U, 0x32eedcc7U, 0xb0ce7e45U, 0xddf92428U,
+	0xe1ca2b14U, 0xe34aa916U, 0x72becc87U, 0xc4a76331U,
+	0x207a5ad5U, 0x4bc883beU, 0xde39e72bU, 0x4a88c2bfU,
+	0xb58b3e40U, 0x38645ccdU, 0x138695e6U, 0x8cfd7179U,
+	0x8f3db27aU, 0x8332b176U, 0x287058ddU, 0xe48f6b11U,
+	0xeb40ab1eU, 0xbf01be4aU, 0x454702b0U, 0x005252f5U,
+	0xcded2038U, 0x81b23374U, 0x12c6d4e7U, 0x039291f6U,
+	0xef45aa1aU, 0x77fb8c82U, 0x581c44adU, 0xfa14ee0fU,
+	0x5693c5a3U, 0xbe41ff4bU, 0x16c3d5e3U, 0x57d384a2U,
+	0xa31ab956U, 0x4fcd82baU, 0x6960099cU, 0xa4df7b51U,
+	0xba44fe4fU, 0xa65ff953U, 0xdf79a62aU, 0xc222e037U,
+	0x2af0dadfU, 0xaa50fa5fU, 0xcf6da23aU, 0x306e5ec5U,
+	0x7d710c88U, 0x2ef5dbdbU, 0x37ab9cc2U, 0x73fe8d86U,
+	0x501646a5U, 0x616a0b94U, 0xb24efc47U, 0x67ef8892U,
+	0x1f8996eaU, 0xf61bed03U, 0x26ffd9d3U, 0xb18e3f44U,
+	0xacd57959U, 0x3d211cc8U, 0x0d1d10f8U, 0x541347a1U,
+	0x85b73270U, 0xd0b66625U, 0x045753f1U, 0x656f0a90U,
+	0x2fb59adaU, 0x312e1fc4U, 0xce2de33bU, 0xf8946c0dU,
+	0xa0da7a55U, 0xf1de2f04U, 0xe5cf2a10U, 0x150316e0U,
+	0xc9e8213cU, 0xab10bb5eU, 0x7ab4ce8fU, 0xa8d0785dU,
+	0xad953858U, 0x94e37761U, 0x5bdc87aeU, 0xbcc17d49U,
+	0x400242b5U, 0xfe11ef0bU, 0x4687c1b3U, 0x7bf48f8eU,
+	0x98ec746dU, 0x602a4a95U, 0xe60fe913U, 0xee05eb1bU,
+	0xf9d42d0cU, 0x1c4955e9U, 0xe880681dU, 0xa19a3b54U,
+	0x5a9cc6afU, 0xf5db2e00U, 0xa59f3a50U, 0x9663f563U,
+	0x247f5bd1U, 0xb70bbc42U, 0x051712f0U, 0xaf15ba5aU,
+	0x2d3518d8U, 0x8a78f27fU, 0xa25af857U, 0x80f27275U,
+	0x1accd6efU, 0x63ea8996U, 0x178394e2U, 0x9326b566U,
+	0x79740d8cU, 0x414203b4U, 0xf21eec07U, 0x3ba49fceU,
+	0x555306a0U, 0xd9fc252cU, 0x144357e1U, 0x9266f467U,
+	0x22fad8d7U, 0xd1f62724U, 0x8737b072U, 0xe74fa812U,
+	0x9b2cb76eU, 0x9f29b66aU, 0x43c281b6U, 0x6be08b9eU,
+	0x184c54edU, 0x4282c0b7U, 0x90e67665U, 0x642f4b91U,
+	0xc0a26235U, 0x595c05acU, 0xdcb96529U, 0xd376a526U,
+	0x6aa0ca9fU, 0x494801bcU, 0x5fd986aaU, 0x2bb09bdeU,
+	0x5296c4a7U, 0x6ea5cb9bU, 0x47c780b2U, 0x4d4d00b8U,
+	0x06d7d1f3U, 0x293019dcU, 0xea00ea1fU, 0x0c5d51f9U,
+	0xb8c47c4dU, 0x190c15ecU, 0xc362a136U, 0xd8bc642dU,
+	0x9a6cf66fU, 0xff51ae0aU, 0x6d650898U, 0x66afc993U,
+	0xccad6139U, 0xb9843d4cU, 0xfc916d09U, 0xec856919U,
+	0x085850fdU, 0x8b38b37eU, 0x703e4e85U, 0x079790f2U,
+	0x1ec9d7ebU, 0x743b4f81U, 0xf35ead06U, 0xd773a422U,
+	0xbb04bf4eU, 0xc8a8603dU, 0xe9c0291cU, 0x0ad8d2ffU,
+	0x39241dccU, 0x9723b462U, 0x352b1ec0U, 0x3c615dc9U,
+	0x4e8dc3bbU, 0xedc52818U, 0xdb7ca72eU, 0x9da93468U,
+	0xc627e133U, 0x5d5904a8U, 0x1b8c97eeU, 0xae55fb5bU,
+	0xcb68a33eU, 0x95a33660U, 0x53d685a6U, 0xd633e523U,
+	0x717e0f84U, 0x011213f4U, 0xca28e23fU, 0x0f9d92faU,
+	0x316f5ed0U, 0x283119c9U, 0xcdac612cU, 0x91e77670U,
+	0x591d44b8U, 0xed84690cU, 0x9e28b67fU, 0xb48a3e55U,
+	0x32af9dd3U, 0xb08f3f51U, 0x3aa59fdbU, 0x612b4a80U,
+	0xca69a32bU, 0x85f67364U, 0xda7da73bU, 0x7ef08e9fU,
+	0xc666a027U, 0xf71aed16U, 0x001313e1U, 0xb34ffc52U,
+	0xc263a123U, 0x90a73771U, 0x7af58f9bU, 0x652e4b84U,
+	0xc0e32321U, 0x7bb5ce9aU, 0x26be98c7U, 0xf25fad13U,
+	0x4f8cc3aeU, 0x707f0f91U, 0x72ff8d93U, 0x0c1c10edU,
+	0x180d15f9U, 0x63abc882U, 0x2bf1dacaU, 0xb9c57c58U,
+	0x2c3418cdU, 0xd5b26734U, 0x081911e9U, 0x8373f062U,
+	0xa61eb847U, 0x3be5dedaU, 0x46c680a7U, 0x67aec986U,
+	0xddb8653cU, 0x551247b4U, 0xd8fd2539U, 0xe30be802U,
+	0xf9956c18U, 0xa49e3a45U, 0x1a8d97fbU, 0xccec202dU,
+	0x4d0c41acU, 0x4ac983abU, 0xc9a96028U, 0x3c201cddU,
+	0x68610989U, 0x154257f4U, 0x38251dd9U, 0x81f37260U,
+	0xbf40ff5eU, 0x545206b5U, 0x9622b477U, 0xf0df2f11U,
+	0x77bacd96U, 0x76fa8c97U, 0xf4da2e15U, 0x99ed7478U,
+	0xa5de7b44U, 0xa75ef946U, 0x36aa9cd7U, 0x80b33361U,
+	0x646e0a85U, 0x0fdcd3eeU, 0x9a2db77bU, 0x0e9c92efU,
+	0xf19f6e10U, 0x7c700c9dU, 0x5792c5b6U, 0xc8e92129U,
+	0xcb29e22aU, 0xc726e126U, 0x6c64088dU, 0xa09b3b41U,
+	0xaf54fb4eU, 0xfb15ee1aU, 0x015352e0U, 0x444602a5U,
+	0x89f97068U, 0xc5a66324U, 0x56d284b7U, 0x4786c1a6U,
+	0xab51fa4aU, 0x33efdcd2U, 0x1c0814fdU, 0xbe00be5fU,
+	0x128795f3U, 0xfa55af1bU, 0x52d785b3U, 0x13c7d4f2U,
+	0xe70ee906U, 0x0bd9d2eaU, 0x2d7459ccU, 0xe0cb2b01U,
+	0xfe50ae1fU, 0xe24ba903U, 0x9b6df67aU, 0x8636b067U,
+	0x6ee48a8fU, 0xee44aa0fU, 0x8b79f26aU, 0x747a0e95U,
+	0x39655cd8U, 0x6ae18b8bU, 0x73bfcc92U, 0x37eaddd6U,
+	0x140216f5U, 0x257e5bc4U, 0xf65aac17U, 0x23fbd8c2U,
+	0x5b9dc6baU, 0xb20fbd53U, 0x62eb8983U, 0xf59a6f14U,
+	0xe8c12909U, 0x79354c98U, 0x490940a8U, 0x100717f1U,
+	0xc1a36220U, 0x94a23675U, 0x404303a1U, 0x217b5ac0U,
+	0x6ba1ca8aU, 0x753a4f94U, 0x8a39b36bU, 0xbc803c5dU,
+	0xe4ce2a05U, 0xb5ca7f54U, 0xa1db7a40U, 0x511746b0U,
+	0x8dfc716cU, 0xef04eb0eU, 0x3ea09edfU, 0xecc4280dU,
+	0xe9816808U, 0xd0f72731U, 0x1fc8d7feU, 0xf8d52d19U,
+	0x041612e5U, 0xba05bf5bU, 0x029391e3U, 0x3fe0dfdeU,
+	0xdcf8243dU, 0x243e1ac5U, 0xa21bb943U, 0xaa11bb4bU,
+	0xbdc07d5cU, 0x585d05b9U, 0xac94384dU, 0xe58e6b04U,
+	0x1e8896ffU, 0xb1cf7e50U, 0xe18b6a00U, 0xd277a533U,
+	0x606b0b81U, 0xf31fec12U, 0x410342a0U, 0xeb01ea0aU,
+	0x69214888U, 0xce6ca22fU, 0xe64ea807U, 0xc4e62225U,
+	0x5ed886bfU, 0x27fed9c6U, 0x5397c4b2U, 0xd732e536U,
+	0x3d605ddcU, 0x055653e4U, 0xb60abc57U, 0x7fb0cf9eU,
+	0x114756f0U, 0x9de8757cU, 0x505707b1U, 0xd672a437U,
+	0x66ee8887U, 0x95e27774U, 0xc323e022U, 0xa35bf842U,
+	0xdf38e73eU, 0xdb3de63aU, 0x07d6d1e6U, 0x2ff4dbceU,
+	0x5c5804bdU, 0x069690e7U, 0xd4f22635U, 0x203b1bc1U,
+	0x84b63265U, 0x1d4855fcU, 0x98ad3579U, 0x9762f576U,
+	0x2eb49acfU, 0x0d5c51ecU, 0x1bcdd6faU, 0x6fa4cb8eU,
+	0x168294f7U, 0x2ab19bcbU, 0x03d3d0e2U, 0x095950e8U,
+	0x42c381a3U, 0x6d24498cU, 0xae14ba4fU, 0x484901a9U,
+	0xfcd02c1dU, 0x5d1845bcU, 0x8776f166U, 0x9ca8347dU,
+	0xde78a63fU, 0xbb45fe5aU, 0x297158c8U, 0x22bb99c3U,
+	0x88b93169U, 0xfd906d1cU, 0xb8853d59U, 0xa8913949U,
+	0x4c4c00adU, 0xcf2ce32eU, 0x342a1ed5U, 0x4383c0a2U,
+	0x5add87bbU, 0x302f1fd1U, 0xb74afd56U, 0x9367f472U,
+	0xff10ef1eU, 0x8cbc306dU, 0xadd4794cU, 0x4ecc82afU,
+	0x7d304d9cU, 0xd337e432U, 0x713f4e90U, 0x78750d99U,
+	0x0a9993ebU, 0xa9d17848U, 0x9f68f77eU, 0xd9bd6438U,
+	0x8233b163U, 0x194d54f8U, 0x5f98c7beU, 0xea41ab0bU,
+	0x8f7cf36eU, 0xd1b76630U, 0x17c2d5f6U, 0x9227b573U,
+	0x356a5fd4U, 0x450643a4U, 0x8e3cb26fU, 0x4b89c2aaU,
+	0xcda06de3U, 0xd4fe2afaU, 0x3163521fU, 0x6d284543U,
+	0xa5d2778bU, 0x114b5a3fU, 0x62e7854cU, 0x48450d66U,
+	0xce60aee0U, 0x4c400c62U, 0xc66aace8U, 0x9de479b3U,
+	0x36a69018U, 0x79394057U, 0x26b29408U, 0x823fbdacU,
+	0x3aa99314U, 0x0bd5de25U, 0xfcdc20d2U, 0x4f80cf61U,
+	0x3eac9210U, 0x6c680442U, 0x863abca8U, 0x99e178b7U,
+	0x3c2c1012U, 0x877afda9U, 0xda71abf4U, 0x0e909e20U,
+	0xb343f09dU, 0x8cb03ca2U, 0x8e30bea0U, 0xf0d323deU,
+	0xe4c226caU, 0x9f64fbb1U, 0xd73ee9f9U, 0x450a4f6bU,
+	0xd0fb2bfeU, 0x297d5407U, 0xf4d622daU, 0x7fbcc351U,
+	0x5ad18b74U, 0xc72aede9U, 0xba09b394U, 0x9b61fab5U,
+	0x2177560fU, 0xa9dd7487U, 0x2432160aU, 0x1fc4db31U,
+	0x055a5f2bU, 0x58510976U, 0xe642a4c8U, 0x3023131eU,
+	0xb1c3729fU, 0xb606b098U, 0x3566531bU, 0xc0ef2feeU,
+	0x94ae3abaU, 0xe98d64c7U, 0xc4ea2eeaU, 0x7d3c4153U,
+	0x438fcc6dU, 0xa89d3586U, 0x6aed8744U, 0x0c101c22U,
+	0x8b75fea5U, 0x8a35bfa4U, 0x08151d26U, 0x6522474bU,
+	0x59114877U, 0x5b91ca75U, 0xca65afe4U, 0x7c7c0052U,
+	0x98a139b6U, 0xf313e0ddU, 0x66e28448U, 0xf253a1dcU,
+	0x0d505d23U, 0x80bf3faeU, 0xab5df685U, 0x3426121aU,
+	0x37e6d119U, 0x3be9d215U, 0x90ab3bbeU, 0x5c540872U,
+	0x539bc87dU, 0x07dadd29U, 0xfd9c61d3U, 0xb8893196U,
+	0x7536435bU, 0x39695017U, 0xaa1db784U, 0xbb49f295U,
+	0x579ec979U, 0xcf20efe1U, 0xe0c727ceU, 0x42cf8d6cU,
+	0xee48a6c0U, 0x069a9c28U, 0xae18b680U, 0xef08e7c1U,
+	0x1bc1da35U, 0xf716e1d9U, 0xd1bb6affU, 0x1c041832U,
+	0x029f9d2cU, 0x1e849a30U, 0x67a2c549U, 0x7af98354U,
+	0x922bb9bcU, 0x128b993cU, 0x77b6c159U, 0x88b53da6U,
+	0xc5aa6febU, 0x962eb8b8U, 0x8f70ffa1U, 0xcb25eee5U,
+	0xe8cd25c6U, 0xd9b168f7U, 0x0a959f24U, 0xdf34ebf1U,
+	0xa752f589U, 0x4ec08e60U, 0x9e24bab0U, 0x09555c27U,
+	0x140e1a3aU, 0x85fa7fabU, 0xb5c6739bU, 0xecc824c2U,
+	0x3d6c5113U, 0x686d0546U, 0xbc8c3092U, 0xddb469f3U,
+	0x976ef9b9U, 0x89f57ca7U, 0x76f68058U, 0x404f0f6eU,
+	0x18011936U, 0x49054c67U, 0x5d144973U, 0xadd87583U,
+	0x7133425fU, 0x13cbd83dU, 0xc26fadecU, 0x100b1b3eU,
+	0x154e5b3bU, 0x2c381402U, 0xe307e4cdU, 0x041a1e2aU,
+	0xf8d921d6U, 0x46ca8c68U, 0xfe5ca2d0U, 0xc32fecedU,
+	0x2037170eU, 0xd8f129f6U, 0x5ed48a70U, 0x56de8878U,
+	0x410f4e6fU, 0xa492368aU, 0x505b0b7eU, 0x19415837U,
+	0xe247a5ccU, 0x4d004d63U, 0x1d445933U, 0x2eb89600U,
+	0x9ca438b2U, 0x0fd0df21U, 0xbdcc7193U, 0x17ced939U,
+	0x95ee7bbbU, 0x32a3911cU, 0x1a819b34U, 0x38291116U,
+	0xa217b58cU, 0xdb31eaf5U, 0xaf58f781U, 0x2bfdd605U,
+	0xc1af6eefU, 0xf99960d7U, 0x4ac58f64U, 0x837ffcadU,
+	0xed8865c3U, 0x6127464fU, 0xac983482U, 0x2abd9704U,
+	0x9a21bbb4U, 0x692d4447U, 0x3fecd311U, 0x5f94cb71U,
+	0x23f7d40dU, 0x27f2d509U, 0xfb19e2d5U, 0xd33be8fdU,
+	0xa097378eU, 0xfa59a3d4U, 0x283d1506U, 0xdcf428f2U,
+	0x78790156U, 0xe18766cfU, 0x6462064aU, 0x6badc645U,
+	0xd27ba9fcU, 0xf19362dfU, 0xe702e5c9U, 0x936bf8bdU,
+	0xea4da7c4U, 0xd67ea8f8U, 0xff1ce3d1U, 0xf59663dbU,
+	0xbe0cb290U, 0x91eb7abfU, 0x52db897cU, 0xb486329aU,
+	0x001f1f2eU, 0xa1d7768fU, 0x7bb9c255U, 0x6067074eU,
+	0x22b7950cU, 0x478acd69U, 0xd5be6bfbU, 0xde74aaf0U,
+	0x7476025aU, 0x015f5e2fU, 0x444a0e6aU, 0x545e0a7aU,
+	0xb083339eU, 0x33e3d01dU, 0xc8e52de6U, 0xbf4cf391U,
+	0xa612b488U, 0xcce02ce2U, 0x4b85ce65U, 0x6fa8c741U,
+	0x03dfdc2dU, 0x7073035eU, 0x511b4a7fU, 0xb203b19cU,
+	0x81ff7eafU, 0x2ff8d701U, 0x8df07da3U, 0x84ba3eaaU,
+	0xf656a0d8U, 0x551e4b7bU, 0x63a7c44dU, 0x2572570bU,
+	0x7efc8250U, 0xe58267cbU, 0xa357f48dU, 0x168e9838U,
+	0x73b3c05dU, 0x2d785503U, 0xeb0de6c5U, 0x6ee88640U,
+	0xc9a56ce7U, 0xb9c97097U, 0x72f3815cU, 0xb746f199U,
+	0xd10edf51U, 0xc8509848U, 0x2dcde0adU, 0x7186f7f1U,
+	0xb97cc539U, 0x0de5e88dU, 0x7e4937feU, 0x54ebbfd4U,
+	0xd2ce1c52U, 0x50eebed0U, 0xdac41e5aU, 0x814acb01U,
+	0x2a0822aaU, 0x6597f2e5U, 0x3a1c26baU, 0x9e910f1eU,
+	0x260721a6U, 0x177b6c97U, 0xe0729260U, 0x532e7dd3U,
+	0x220220a2U, 0x70c6b6f0U, 0x9a940e1aU, 0x854fca05U,
+	0x2082a2a0U, 0x9bd44f1bU, 0xc6df1946U, 0x123e2c92U,
+	0xafed422fU, 0x901e8e10U, 0x929e0c12U, 0xec7d916cU,
+	0xf86c9478U, 0x83ca4903U, 0xcb905b4bU, 0x59a4fdd9U,
+	0xcc55994cU, 0x35d3e6b5U, 0xe8789068U, 0x631271e3U,
+	0x467f39c6U, 0xdb845f5bU, 0xa6a70126U, 0x87cf4807U,
+	0x3dd9e4bdU, 0xb573c635U, 0x389ca4b8U, 0x036a6983U,
+	0x19f4ed99U, 0x44ffbbc4U, 0xfaec167aU, 0x2c8da1acU,
+	0xad6dc02dU, 0xaaa8022aU, 0x29c8e1a9U, 0xdc419d5cU,
+	0x88008808U, 0xf523d675U, 0xd8449c58U, 0x6192f3e1U,
+	0x5f217edfU, 0xb4338734U, 0x764335f6U, 0x10beae90U,
+	0x97db4c17U, 0x969b0d16U, 0x14bbaf94U, 0x798cf5f9U,
+	0x45bffac5U, 0x473f78c7U, 0xd6cb1d56U, 0x60d2b2e0U,
+	0x840f8b04U, 0xefbd526fU, 0x7a4c36faU, 0xeefd136eU,
+	0x11feef91U, 0x9c118d1cU, 0xb7f34437U, 0x2888a0a8U,
+	0x2b4863abU, 0x274760a7U, 0x8c05890cU, 0x40fabac0U,
+	0x4f357acfU, 0x1b746f9bU, 0xe132d361U, 0xa4278324U,
+	0x6998f1e9U, 0x25c7e2a5U, 0xb6b30536U, 0xa7e74027U,
+	0x4b307bcbU, 0xd38e5d53U, 0xfc69957cU, 0x5e613fdeU,
+	0xf2e61472U, 0x1a342e9aU, 0xb2b60432U, 0xf3a65573U,
+	0x076f6887U, 0xebb8536bU, 0xcd15d84dU, 0x00aaaa80U,
+	0x1e312f9eU, 0x022a2882U, 0x7b0c77fbU, 0x665731e6U,
+	0x8e850b0eU, 0x0e252b8eU, 0x6b1873ebU, 0x941b8f14U,
+	0xd904dd59U, 0x8a800a0aU, 0x93de4d13U, 0xd78b5c57U,
+	0xf4639774U, 0xc51fda45U, 0x163b2d96U, 0xc39a5943U,
+	0xbbfc473bU, 0x526e3cd2U, 0x828a0802U, 0x15fbee95U,
+	0x08a0a888U, 0x9954cd19U, 0xa968c129U, 0xf0669670U,
+	0x21c2e3a1U, 0x74c3b7f4U, 0xa0228220U, 0xc11adb41U,
+	0x8bc04b0bU, 0x955bce15U, 0x6a5832eaU, 0x5ce1bddcU,
+	0x04afab84U, 0x55abfed5U, 0x41bafbc1U, 0xb176c731U,
+	0x6d9df0edU, 0x0f656a8fU, 0xdec11f5eU, 0x0ca5a98cU,
+	0x09e0e989U, 0x3096a6b0U, 0xffa9567fU, 0x18b4ac98U,
+	0xe4779364U, 0x5a643edaU, 0xe2f21062U, 0xdf815e5fU,
+	0x3c99a5bcU, 0xc45f9b44U, 0x427a38c2U, 0x4a703acaU,
+	0x5da1fcddU, 0xb83c8438U, 0x4cf5b9ccU, 0x05efea85U,
+	0xfee9177eU, 0x51aeffd1U, 0x01eaeb81U, 0x321624b2U,
+	0x800a8a00U, 0x137e6d93U, 0xa162c321U, 0x0b606b8bU,
+	0x8940c909U, 0x2e0d23aeU, 0x062f2986U, 0x2487a3a4U,
+	0xbeb9073eU, 0xc79f5847U, 0xb3f64533U, 0x375364b7U,
+	0xdd01dc5dU, 0xe537d265U, 0x566b3dd6U, 0x9fd14e1fU,
+	0xf126d771U, 0x7d89f4fdU, 0xb0368630U, 0x361325b6U,
+	0x868f0906U, 0x7583f6f5U, 0x234261a3U, 0x433a79c3U,
+	0x3f5966bfU, 0x3b5c67bbU, 0xe7b75067U, 0xcf955a4fU,
+	0xbc39853cU, 0xe6f71166U, 0x3493a7b4U, 0xc05a9a40U,
+	0x64d7b3e4U, 0xfd29d47dU, 0x78ccb4f8U, 0x770374f7U,
+	0xced51b4eU, 0xed3dd06dU, 0xfbac577bU, 0x8fc54a0fU,
+	0xf6e31576U, 0xcad01a4aU, 0xe3b25163U, 0xe938d169U,
+	0xa2a20022U, 0x8d45c80dU, 0x4e753bceU, 0xa8288028U,
+	0x1cb1ad9cU, 0xbd79c43dU, 0x671770e7U, 0x7cc9b5fcU,
+	0x3e1927beU, 0x5b247fdbU, 0xc910d949U, 0xc2da1842U,
+	0x68d8b0e8U, 0x1df1ec9dU, 0x58e4bcd8U, 0x48f0b8c8U,
+	0xac2d812cU, 0x2f4d62afU, 0xd44b9f54U, 0xa3e24123U,
+	0xbabc063aU, 0xd04e9e50U, 0x572b7cd7U, 0x730675f3U,
+	0x1f716e9fU, 0x6cddb1ecU, 0x4db5f8cdU, 0xaead032eU,
+	0x9d51cc1dU, 0x335665b3U, 0x915ecf11U, 0x98148c18U,
+	0xeaf8126aU, 0x49b0f9c9U, 0x7f0976ffU, 0x39dce5b9U,
+	0x625230e2U, 0xf92cd579U, 0xbff9463fU, 0x0a202a8aU,
+	0x6f1d72efU, 0x31d6e7b1U, 0xf7a35477U, 0x724634f2U,
+	0xd50bde55U, 0xa567c225U, 0x6e5d33eeU, 0xabe8432bU,
+	0xa5e94cc2U, 0xbcb70bdbU, 0x592a733eU, 0x05616462U,
+	0xcd9b56aaU, 0x79027b1eU, 0x0aaea46dU, 0x200c2c47U,
+	0xa6298fc1U, 0x24092d43U, 0xae238dc9U, 0xf5ad5892U,
+	0x5eefb139U, 0x11706176U, 0x4efbb529U, 0xea769c8dU,
+	0x52e0b235U, 0x639cff04U, 0x949501f3U, 0x27c9ee40U,
+	0x56e5b331U, 0x04212563U, 0xee739d89U, 0xf1a85996U,
+	0x54653133U, 0xef33dc88U, 0xb2388ad5U, 0x66d9bf01U,
+	0xdb0ad1bcU, 0xe4f91d83U, 0xe6799f81U, 0x989a02ffU,
+	0x8c8b07ebU, 0xf72dda90U, 0xbf77c8d8U, 0x2d436e4aU,
+	0xb8b20adfU, 0x41347526U, 0x9c9f03fbU, 0x17f5e270U,
+	0x3298aa55U, 0xaf63ccc8U, 0xd24092b5U, 0xf328db94U,
+	0x493e772eU, 0xc19455a6U, 0x4c7b372bU, 0x778dfa10U,
+	0x6d137e0aU, 0x30182857U, 0x8e0b85e9U, 0x586a323fU,
+	0xd98a53beU, 0xde4f91b9U, 0x5d2f723aU, 0xa8a60ecfU,
+	0xfce71b9bU, 0x81c445e6U, 0xaca30fcbU, 0x15756072U,
+	0x2bc6ed4cU, 0xc0d414a7U, 0x02a4a665U, 0x64593d03U,
+	0xe33cdf84U, 0xe27c9e85U, 0x605c3c07U, 0x0d6b666aU,
+	0x31586956U, 0x33d8eb54U, 0xa22c8ec5U, 0x14352173U,
+	0xf0e81897U, 0x9b5ac1fcU, 0x0eaba569U, 0x9a1a80fdU,
+	0x65197c02U, 0xe8f61e8fU, 0xc314d7a4U, 0x5c6f333bU,
+	0x5faff038U, 0x53a0f334U, 0xf8e21a9fU, 0x341d2953U,
+	0x3bd2e95cU, 0x6f93fc08U, 0x95d540f2U, 0xd0c010b7U,
+	0x1d7f627aU, 0x51207136U, 0xc25496a5U, 0xd300d3b4U,
+	0x3fd7e858U, 0xa769cec0U, 0x888e06efU, 0x2a86ac4dU,
+	0x860187e1U, 0x6ed3bd09U, 0xc65197a1U, 0x8741c6e0U,
+	0x7388fb14U, 0x9f5fc0f8U, 0xb9f24bdeU, 0x744d3913U,
+	0x6ad6bc0dU, 0x76cdbb11U, 0x0febe468U, 0x12b0a275U,
+	0xfa62989dU, 0x7ac2b81dU, 0x1fffe078U, 0xe0fc1c87U,
+	0xade34ecaU, 0xfe679999U, 0xe739de80U, 0xa36ccfc4U,
+	0x808404e7U, 0xb1f849d6U, 0x62dcbe05U, 0xb77dcad0U,
+	0xcf1bd4a8U, 0x2689af41U, 0xf66d9b91U, 0x611c7d06U,
+	0x7c473b1bU, 0xedb35e8aU, 0xdd8f52baU, 0x848105e3U,
+	0x55257032U, 0x00242467U, 0xd4c511b3U, 0xb5fd48d2U,
+	0xff27d898U, 0xe1bc5d86U, 0x1ebfa179U, 0x28062e4fU,
+	0x70483817U, 0x214c6d46U, 0x355d6852U, 0xc59154a2U,
+	0x197a637eU, 0x7b82f91cU, 0xaa268ccdU, 0x78423a1fU,
+	0x7d077a1aU, 0x44713523U, 0x8b4ec5ecU, 0x6c533f0bU,
+	0x909000f7U, 0x2e83ad49U, 0x961583f1U, 0xab66cdccU,
+	0x487e362fU, 0xb0b808d7U, 0x369dab51U, 0x3e97a959U,
+	0x29466f4eU, 0xccdb17abU, 0x38122a5fU, 0x71087916U,
+	0x8a0e84edU, 0x25496c42U, 0x750d7812U, 0x46f1b721U,
+	0xf4ed1993U, 0x6799fe00U, 0xd58550b2U, 0x7f87f818U,
+	0xfda75a9aU, 0x5aeab03dU, 0x72c8ba15U, 0x50603037U,
+	0xca5e94adU, 0xb378cbd4U, 0xc711d6a0U, 0x43b4f724U,
+	0xa9e64fceU, 0x91d041f6U, 0x228cae45U, 0xeb36dd8cU,
+	0x85c144e2U, 0x096e676eU, 0xc4d115a3U, 0x42f4b625U,
+	0xf2689a95U, 0x01646566U, 0x57a5f230U, 0x37ddea50U,
+	0x4bbef52cU, 0x4fbbf428U, 0x9350c3f4U, 0xbb72c9dcU,
+	0xc8de16afU, 0x921082f5U, 0x40743427U, 0xb4bd09d3U,
+	0x10302077U, 0x89ce47eeU, 0x0c2b276bU, 0x03e4e764U,
+	0xba3288ddU, 0x99da43feU, 0x8f4bc4e8U, 0xfb22d99cU,
+	0x820486e5U, 0xbe3789d9U, 0x9755c2f0U, 0x9ddf42faU,
+	0xd64593b1U, 0xf9a25b9eU, 0x3a92a85dU, 0xdccf13bbU,
+	0x68563e0fU, 0xc99e57aeU, 0x13f0e374U, 0x082e266fU,
+	0x4afeb42dU, 0x2fc3ec48U, 0xbdf74adaU, 0xb63d8bd1U,
+	0x1c3f237bU, 0x69167f0eU, 0x2c032f4bU, 0x3c172b5bU,
+	0xd8ca12bfU, 0x5baaf13cU, 0xa0ac0cc7U, 0xd705d2b0U,
+	0xce5b95a9U, 0xa4a90dc3U, 0x23ccef44U, 0x07e1e660U,
+	0x6b96fd0cU, 0x183a227fU, 0x39526b5eU, 0xda4a90bdU,
+	0xe9b65f8eU, 0x47b1f620U, 0xe5b95c82U, 0xecf31f8bU,
+	0x9e1f81f9U, 0x3d576a5aU, 0x0beee56cU, 0x4d3b762aU,
+	0x16b5a371U, 0x8dcb46eaU, 0xcb1ed5acU, 0x7ec7b919U,
+	0x1bfae17cU, 0x45317422U, 0x8344c7e4U, 0x06a1a761U,
+	0xa1ec4dc6U, 0xd18051b6U, 0x1abaa07dU, 0xdf0fd0b8U,
+	0xb947fe70U, 0xa019b969U, 0x4584c18cU, 0x19cfd6d0U,
+	0xd135e418U, 0x65acc9acU, 0x160016dfU, 0x3ca29ef5U,
+	0xba873d73U, 0x38a79ff1U, 0xb28d3f7bU, 0xe903ea20U,
+	0x4241038bU, 0x0dded3c4U, 0x5255079bU, 0xf6d82e3fU,
+	0x4e4e0087U, 0x7f324db6U, 0x883bb341U, 0x3b675cf2U,
+	0x4a4b0183U, 0x188f97d1U, 0xf2dd2f3bU, 0xed06eb24U,
+	0x48cb8381U, 0xf39d6e3aU, 0xae963867U, 0x7a770db3U,
+	0xc7a4630eU, 0xf857af31U, 0xfad72d33U, 0x8434b04dU,
+	0x9025b559U, 0xeb836822U, 0xa3d97a6aU, 0x31eddcf8U,
+	0xa41cb86dU, 0x5d9ac794U, 0x8031b149U, 0x0b5b50c2U,
+	0x2e3618e7U, 0xb3cd7e7aU, 0xceee2007U, 0xef866926U,
+	0x5590c59cU, 0xdd3ae714U, 0x50d58599U, 0x6b2348a2U,
+	0x71bdccb8U, 0x2cb69ae5U, 0x92a5375bU, 0x44c4808dU,
+	0xc524e10cU, 0xc2e1230bU, 0x4181c088U, 0xb408bc7dU,
+	0xe049a929U, 0x9d6af754U, 0xb00dbd79U, 0x09dbd2c0U,
+	0x37685ffeU, 0xdc7aa615U, 0x1e0a14d7U, 0x78f78fb1U,
+	0xff926d36U, 0xfed22c37U, 0x7cf28eb5U, 0x11c5d4d8U,
+	0x2df6dbe4U, 0x2f7659e6U, 0xbe823c77U, 0x089b93c1U,
+	0xec46aa25U, 0x87f4734eU, 0x120517dbU, 0x86b4324fU,
+	0x79b7ceb0U, 0xf458ac3dU, 0xdfba6516U, 0x40c18189U,
+	0x4301428aU, 0x4f0e4186U, 0xe44ca82dU, 0x28b39be1U,
+	0x277c5beeU, 0x733d4ebaU, 0x897bf240U, 0xcc6ea205U,
+	0x01d1d0c8U, 0x4d8ec384U, 0xdefa2417U, 0xcfae6106U,
+	0x23795aeaU, 0xbbc77c72U, 0x9420b45dU, 0x36281effU,
+	0x9aaf3553U, 0x727d0fbbU, 0xdaff2513U, 0x9bef7452U,
+	0x6f2649a6U, 0x83f1724aU, 0xa55cf96cU, 0x68e38ba1U,
+	0x76780ebfU, 0x6a6309a3U, 0x134556daU, 0x0e1e10c7U,
+	0xe6cc2a2fU, 0x666c0aafU, 0x035152caU, 0xfc52ae35U,
+	0xb14dfc78U, 0xe2c92b2bU, 0xfb976c32U, 0xbfc27d76U,
+	0x9c2ab655U, 0xad56fb64U, 0x7e720cb7U, 0xabd37862U,
+	0xd3b5661aU, 0x3a271df3U, 0xeac32923U, 0x7db2cfb4U,
+	0x60e989a9U, 0xf11dec38U, 0xc121e008U, 0x982fb751U,
+	0x498bc280U, 0x1c8a96d5U, 0xc86ba301U, 0xa953fa60U,
+	0xe3896a2aU, 0xfd12ef34U, 0x021113cbU, 0x34a89cfdU,
+	0x6ce68aa5U, 0x3de2dff4U, 0x29f3dae0U, 0xd93fe610U,
+	0x05d4d1ccU, 0x672c4baeU, 0xb6883e7fU, 0x64ec88adU,
+	0x61a9c8a8U, 0x58df8791U, 0x97e0775eU, 0x70fd8db9U,
+	0x8c3eb245U, 0x322d1ffbU, 0x8abb3143U, 0xb7c87f7eU,
+	0x54d0849dU, 0xac16ba65U, 0x2a3319e3U, 0x22391bebU,
+	0x35e8ddfcU, 0xd075a519U, 0x24bc98edU, 0x6da6cba4U,
+	0x96a0365fU, 0x39e7def0U, 0x69a3caa0U, 0x5a5f0593U,
+	0xe843ab21U, 0x7b374cb2U, 0xc92be200U, 0x63294aaaU,
+	0xe109e828U, 0x4644028fU, 0x6e6608a7U, 0x4cce8285U,
+	0xd6f0261fU, 0xafd67966U, 0xdbbf6412U, 0x5f1a4596U,
+	0xb548fd7cU, 0x8d7ef344U, 0x3e221cf7U, 0xf7986f3eU,
+	0x996ff650U, 0x15c0d5dcU, 0xd87fa711U, 0x5e5a0497U,
+	0xeec62827U, 0x1dcad7d4U, 0x4b0b4082U, 0x2b7358e2U,
+	0x5710479eU, 0x5315469aU, 0x8ffe7146U, 0xa7dc7b6eU,
+	0xd470a41dU, 0x8ebe3047U, 0x5cda8695U, 0xa813bb61U,
+	0x0c9e92c5U, 0x9560f55cU, 0x108595d9U, 0x1f4a55d6U,
+	0xa69c3a6fU, 0x8574f14cU, 0x93e5765aU, 0xe78c6b2eU,
+	0x9eaa3457U, 0xa2993b6bU, 0x8bfb7042U, 0x8171f048U,
+	0xcaeb2103U, 0xe50ce92cU, 0x263c1aefU, 0xc061a109U,
+	0x74f88cbdU, 0xd530e51cU, 0x0f5e51c6U, 0x148094ddU,
+	0x5650069fU, 0x336d5efaU, 0xa159f868U, 0xaa933963U,
+	0x009191c9U, 0x75b8cdbcU, 0x30ad9df9U, 0x20b999e9U,
+	0xc464a00dU, 0x4704438eU, 0xbc02be75U, 0xcbab6002U,
+	0xd2f5271bU, 0xb807bf71U, 0x3f625df6U, 0x1b4f54d2U,
+	0x77384fbeU, 0x049490cdU, 0x25fcd9ecU, 0xc6e4220fU,
+	0xf518ed3cU, 0x5b1f4492U, 0xf917ee30U, 0xf05dad39U,
+	0x82b1334bU, 0x21f9d8e8U, 0x174057deU, 0x5195c498U,
+	0x0a1b11c3U, 0x9165f458U, 0xd7b0671eU, 0x62690babU,
+	0x075453ceU, 0x599fc690U, 0x9fea7556U, 0x1a0f15d3U,
+	0xbd42ff74U, 0xcd2ee304U, 0x061412cfU, 0xc3a1620aU,
+	0xbbef54daU, 0xa2b113c3U, 0x472c6b26U, 0x1b677c7aU,
+	0xd39d4eb2U, 0x67046306U, 0x14a8bc75U, 0x3e0a345fU,
+	0xb82f97d9U, 0x3a0f355bU, 0xb02595d1U, 0xebab408aU,
+	0x40e9a921U, 0x0f76796eU, 0x50fdad31U, 0xf4708495U,
+	0x4ce6aa2dU, 0x7d9ae71cU, 0x8a9319ebU, 0x39cff658U,
+	0x48e3ab29U, 0x1a273d7bU, 0xf0758591U, 0xefae418eU,
+	0x4a63292bU, 0xf135c490U, 0xac3e92cdU, 0x78dfa719U,
+	0xc50cc9a4U, 0xfaff059bU, 0xf87f8799U, 0x869c1ae7U,
+	0x928d1ff3U, 0xe92bc288U, 0xa171d0c0U, 0x33457652U,
+	0xa6b412c7U, 0x5f326d3eU, 0x82991be3U, 0x09f3fa68U,
+	0x2c9eb24dU, 0xb165d4d0U, 0xcc468aadU, 0xed2ec38cU,
+	0x57386f36U, 0xdf924dbeU, 0x527d2f33U, 0x698be208U,
+	0x73156612U, 0x2e1e304fU, 0x900d9df1U, 0x466c2a27U,
+	0xc78c4ba6U, 0xc04989a1U, 0x43296a22U, 0xb6a016d7U,
+	0xe2e10383U, 0x9fc25dfeU, 0xb2a517d3U, 0x0b73786aU,
+	0x35c0f554U, 0xded20cbfU, 0x1ca2be7dU, 0x7a5f251bU,
+	0xfd3ac79cU, 0xfc7a869dU, 0x7e5a241fU, 0x136d7e72U,
+	0x2f5e714eU, 0x2ddef34cU, 0xbc2a96ddU, 0x0a33396bU,
+	0xeeee008fU, 0x855cd9e4U, 0x10adbd71U, 0x841c98e5U,
+	0x7b1f641aU, 0xf6f00697U, 0xdd12cfbcU, 0x42692b23U,
+	0x41a9e820U, 0x4da6eb2cU, 0xe6e40287U, 0x2a1b314bU,
+	0x25d4f144U, 0x7195e410U, 0x8bd358eaU, 0xcec608afU,
+	0x03797a62U, 0x4f26692eU, 0xdc528ebdU, 0xcd06cbacU,
+	0x21d1f040U, 0xb96fd6d8U, 0x96881ef7U, 0x3480b455U,
+	0x98079ff9U, 0x70d5a511U, 0xd8578fb9U, 0x9947def8U,
+	0x6d8ee30cU, 0x8159d8e0U, 0xa7f453c6U, 0x6a4b210bU,
+	0x74d0a415U, 0x68cba309U, 0x11edfc70U, 0x0cb6ba6dU,
+	0xe4648085U, 0x64c4a005U, 0x01f9f860U, 0xfefa049fU,
+	0xb3e556d2U, 0xe0618181U, 0xf93fc698U, 0xbd6ad7dcU,
+	0x9e821cffU, 0xaffe51ceU, 0x7cdaa61dU, 0xa97bd2c8U,
+	0xd11dccb0U, 0x388fb759U, 0xe86b8389U, 0x7f1a651eU,
+	0x62412303U, 0xf3b54692U, 0xc3894aa2U, 0x9a871dfbU,
+	0x4b23682aU, 0x1e223c7fU, 0xcac309abU, 0xabfb50caU,
+	0xe121c080U, 0xffba459eU, 0x00b9b961U, 0x36003657U,
+	0x6e4e200fU, 0x3f4a755eU, 0x2b5b704aU, 0xdb974cbaU,
+	0x077c7b66U, 0x6584e104U, 0xb42094d5U, 0x66442207U,
+	0x63016202U, 0x5a772d3bU, 0x9548ddf4U, 0x72552713U,
+	0x8e9618efU, 0x3085b551U, 0x88139be9U, 0xb560d5d4U,
+	0x56782e37U, 0xaebe10cfU, 0x289bb349U, 0x2091b141U,
+	0x37407756U, 0xd2dd0fb3U, 0x26143247U, 0x6f0e610eU,
+	0x94089cf5U, 0x3b4f745aU, 0x6b0b600aU, 0x58f7af39U,
+	0xeaeb018bU, 0x799fe618U, 0xcb8348aaU, 0x6181e000U,
+	0xe3a14282U, 0x44eca825U, 0x6ccea20dU, 0x4e66282fU,
+	0xd4588cb5U, 0xad7ed3ccU, 0xd917ceb8U, 0x5db2ef3cU,
+	0xb7e057d6U, 0x8fd659eeU, 0x3c8ab65dU, 0xf530c594U,
+	0x9bc75cfaU, 0x17687f76U, 0xdad70dbbU, 0x5cf2ae3dU,
+	0xec6e828dU, 0x1f627d7eU, 0x49a3ea28U, 0x29dbf248U,
+	0x55b8ed34U, 0x51bdec30U, 0x8d56dbecU, 0xa574d1c4U,
+	0xd6d80eb7U, 0x8c169aedU, 0x5e722c3fU, 0xaabb11cbU,
+	0x0e36386fU, 0x97c85ff6U, 0x122d3f73U, 0x1de2ff7cU,
+	0xa43490c5U, 0x87dc5be6U, 0x914ddcf0U, 0xe524c184U,
+	0x9c029efdU, 0xa03191c1U, 0x8953dae8U, 0x83d95ae2U,
+	0xc8438ba9U, 0xe7a44386U, 0x2494b045U, 0xc2c90ba3U,
+	0x76502617U, 0xd7984fb6U, 0x0df6fb6cU, 0x16283e77U,
+	0x54f8ac35U, 0x31c5f450U, 0xa3f152c2U, 0xa83b93c9U,
+	0x02393b63U, 0x77106716U, 0x32053753U, 0x22113343U,
+	0xc6cc0aa7U, 0x45ace924U, 0xbeaa14dfU, 0xc903caa8U,
+	0xd05d8db1U, 0xbaaf15dbU, 0x3dcaf75cU, 0x19e7fe78U,
+	0x7590e514U, 0x063c3a67U, 0x27547346U, 0xc44c88a5U,
+	0xf7b04796U, 0x59b7ee38U, 0xfbbf449aU, 0xf2f50793U,
+	0x801999e1U, 0x23517242U, 0x15e8fd74U, 0x533d6e32U,
+	0x08b3bb69U, 0x93cd5ef2U, 0xd518cdb4U, 0x60c1a101U,
+	0x05fcf964U, 0x5b376c3aU, 0x9d42dffcU, 0x18a7bf79U,
+	0xbfea55deU, 0xcf8649aeU, 0x04bcb865U, 0xc109c8a0U,
+	0x9b4dd658U, 0x82139141U, 0x678ee9a4U, 0x3bc5fef8U,
+	0xf33fcc30U, 0x47a6e184U, 0x340a3ef7U, 0x1ea8b6ddU,
+	0x988d155bU, 0x1aadb7d9U, 0x90871753U, 0xcb09c208U,
+	0x604b2ba3U, 0x2fd4fbecU, 0x705f2fb3U, 0xd4d20617U,
+	0x6c4428afU, 0x5d38659eU, 0xaa319b69U, 0x196d74daU,
+	0x684129abU, 0x3a85bff9U, 0xd0d70713U, 0xcf0cc30cU,
+	0x6ac1aba9U, 0xd1974612U, 0x8c9c104fU, 0x587d259bU,
+	0xe5ae4b26U, 0xda5d8719U, 0xd8dd051bU, 0xa63e9865U,
+	0xb22f9d71U, 0xc989400aU, 0x81d35242U, 0x13e7f4d0U,
+	0x86169045U, 0x7f90efbcU, 0xa23b9961U, 0x295178eaU,
+	0x0c3c30cfU, 0x91c75652U, 0xece4082fU, 0xcd8c410eU,
+	0x779aedb4U, 0xff30cf3cU, 0x72dfadb1U, 0x4929608aU,
+	0x53b7e490U, 0x0ebcb2cdU, 0xb0af1f73U, 0x66cea8a5U,
+	0xe72ec924U, 0xe0eb0b23U, 0x638be8a0U, 0x96029455U,
+	0xc2438101U, 0xbf60df7cU, 0x92079551U, 0x2bd1fae8U,
+	0x156277d6U, 0xfe708e3dU, 0x3c003cffU, 0x5afda799U,
+	0xdd98451eU, 0xdcd8041fU, 0x5ef8a69dU, 0x33cffcf0U,
+	0x0ffcf3ccU, 0x0d7c71ceU, 0x9c88145fU, 0x2a91bbe9U,
+	0xce4c820dU, 0xa5fe5b66U, 0x300f3ff3U, 0xa4be1a67U,
+	0x5bbde698U, 0xd6528415U, 0xfdb04d3eU, 0x62cba9a1U,
+	0x610b6aa2U, 0x6d0469aeU, 0xc6468005U, 0x0ab9b3c9U,
+	0x057673c6U, 0x51376692U, 0xab71da68U, 0xee648a2dU,
+	0x23dbf8e0U, 0x6f84ebacU, 0xfcf00c3fU, 0xeda4492eU,
+	0x017372c2U, 0x99cd545aU, 0xb62a9c75U, 0x142236d7U,
+	0xb8a51d7bU, 0x50772793U, 0xf8f50d3bU, 0xb9e55c7aU,
+	0x4d2c618eU, 0xa1fb5a62U, 0x8756d144U, 0x4ae9a389U,
+	0x54722697U, 0x4869218bU, 0x314f7ef2U, 0x2c1438efU,
+	0xc4c60207U, 0x44662287U, 0x215b7ae2U, 0xde58861dU,
+	0x9347d450U, 0xc0c30303U, 0xd99d441aU, 0x9dc8555eU,
+	0xbe209e7dU, 0x8f5cd34cU, 0x5c78249fU, 0x89d9504aU,
+	0xf1bf4e32U, 0x182d35dbU, 0xc8c9010bU, 0x5fb8e79cU,
+	0x42e3a181U, 0xd317c410U, 0xe32bc820U, 0xba259f79U,
+	0x6b81eaa8U, 0x3e80befdU, 0xea618b29U, 0x8b59d248U,
+	0xc1834202U, 0xdf18c71cU, 0x201b3be3U, 0x16a2b4d5U,
+	0x4eeca28dU, 0x1fe8f7dcU, 0x0bf9f2c8U, 0xfb35ce38U,
+	0x27def9e4U, 0x45266386U, 0x94821657U, 0x46e6a085U,
+	0x43a3e080U, 0x7ad5afb9U, 0xb5ea5f76U, 0x52f7a591U,
+	0xae349a6dU, 0x102737d3U, 0xa8b1196bU, 0x95c25756U,
+	0x76daacb5U, 0x8e1c924dU, 0x083931cbU, 0x003333c3U,
+	0x17e2f5d4U, 0xf27f8d31U, 0x06b6b0c5U, 0x4face38cU,
+	0xb4aa1e77U, 0x1bedf6d8U, 0x4ba9e288U, 0x78552dbbU,
+	0xca498309U, 0x593d649aU, 0xeb21ca28U, 0x41236282U,
+	0xc303c000U, 0x644e2aa7U, 0x4c6c208fU, 0x6ec4aaadU,
+	0xf4fa0e37U, 0x8ddc514eU, 0xf9b54c3aU, 0x7d106dbeU,
+	0x9742d554U, 0xaf74db6cU, 0x1c2834dfU, 0xd5924716U,
+	0xbb65de78U, 0x37cafdf4U, 0xfa758f39U, 0x7c502cbfU,
+	0xcccc000fU, 0x3fc0fffcU, 0x690168aaU, 0x097970caU,
+	0x751a6fb6U, 0x711f6eb2U, 0xadf4596eU, 0x85d65346U,
+	0xf67a8c35U, 0xacb4186fU, 0x7ed0aebdU, 0x8a199349U,
+	0x2e94baedU, 0xb76add74U, 0x328fbdf1U, 0x3d407dfeU,
+	0x84961247U, 0xa77ed964U, 0xb1ef5e72U, 0xc5864306U,
+	0xbca01c7fU, 0x80931343U, 0xa9f1586aU, 0xa37bd860U,
+	0xe8e1092bU, 0xc706c104U, 0x043632c7U, 0xe26b8921U,
+	0x56f2a495U, 0xf73acd34U, 0x2d5479eeU, 0x368abcf5U,
+	0x745a2eb7U, 0x116776d2U, 0x8353d040U, 0x8899114bU,
+	0x229bb9e1U, 0x57b2e594U, 0x12a7b5d1U, 0x02b3b1c1U,
+	0xe66e8825U, 0x650e6ba6U, 0x9e08965dU, 0xe9a1482aU,
+	0xf0ff0f33U, 0x9a0d9759U, 0x1d6875deU, 0x39457cfaU,
+	0x55326796U, 0x269eb8e5U, 0x07f6f1c4U, 0xe4ee0a27U,
+	0xd712c514U, 0x79156cbaU, 0xdb1dc618U, 0xd2578511U,
+	0xa0bb1b63U, 0x03f3f0c0U, 0x354a7ff6U, 0x739fecb0U,
+	0x281139ebU, 0xb36fdc70U, 0xf5ba4f36U, 0x40632383U,
+	0x255e7be6U, 0x7b95eeb8U, 0xbde05d7eU, 0x38053dfbU,
+	0x9f48d75cU, 0xef24cb2cU, 0x241e3ae7U, 0xe1ab4a22U,
+	0xd6a771ffU, 0xcff936e6U, 0x2a644e03U, 0x762f595fU,
+	0xbed56b97U, 0x0a4c4623U, 0x79e09950U, 0x5342117aU,
+	0xd567b2fcU, 0x5747107eU, 0xdd6db0f4U, 0x86e365afU,
+	0x2da18c04U, 0x623e5c4bU, 0x3db58814U, 0x9938a1b0U,
+	0x21ae8f08U, 0x10d2c239U, 0xe7db3cceU, 0x5487d37dU,
+	0x25ab8e0cU, 0x776f185eU, 0x9d3da0b4U, 0x82e664abU,
+	0x272b0c0eU, 0x9c7de1b5U, 0xc176b7e8U, 0x1597823cU,
+	0xa844ec81U, 0x97b720beU, 0x9537a2bcU, 0xebd43fc2U,
+	0xffc53ad6U, 0x8463e7adU, 0xcc39f5e5U, 0x5e0d5377U,
+	0xcbfc37e2U, 0x327a481bU, 0xefd13ec6U, 0x64bbdf4dU,
+	0x41d69768U, 0xdc2df1f5U, 0xa10eaf88U, 0x8066e6a9U,
+	0x3a704a13U, 0xb2da689bU, 0x3f350a16U, 0x04c3c72dU,
+	0x1e5d4337U, 0x4356156aU, 0xfd45b8d4U, 0x2b240f02U,
+	0xaac46e83U, 0xad01ac84U, 0x2e614f07U, 0xdbe833f2U,
+	0x8fa926a6U, 0xf28a78dbU, 0xdfed32f6U, 0x663b5d4fU,
+	0x5888d071U, 0xb39a299aU, 0x71ea9b58U, 0x1717003eU,
+	0x9072e2b9U, 0x9132a3b8U, 0x1312013aU, 0x7e255b57U,
+	0x4216546bU, 0x4096d669U, 0xd162b3f8U, 0x677b1c4eU,
+	0x83a625aaU, 0xe814fcc1U, 0x7de59854U, 0xe954bdc0U,
+	0x1657413fU, 0x9bb823b2U, 0xb05aea99U, 0x2f210e06U,
+	0x2ce1cd05U, 0x20eece09U, 0x8bac27a2U, 0x4753146eU,
+	0x489cd461U, 0x1cddc135U, 0xe69b7dcfU, 0xa38e2d8aU,
+	0x6e315f47U, 0x226e4c0bU, 0xb11aab98U, 0xa04eee89U,
+	0x4c99d565U, 0xd427f3fdU, 0xfbc03bd2U, 0x59c89170U,
+	0xf54fbadcU, 0x1d9d8034U, 0xb51faa9cU, 0xf40ffbddU,
+	0x00c6c629U, 0xec11fdc5U, 0xcabc76e3U, 0x0703042eU,
+	0x19988130U, 0x0583862cU, 0x7ca5d955U, 0x61fe9f48U,
+	0x892ca5a0U, 0x098c8520U, 0x6cb1dd45U, 0x93b221baU,
+	0xdead73f7U, 0x8d29a4a4U, 0x9477e3bdU, 0xd022f2f9U,
+	0xf3ca39daU, 0xc2b674ebU, 0x11928338U, 0xc433f7edU,
+	0xbc55e995U, 0x55c7927cU, 0x8523a6acU, 0x1252403bU,
+	0x0f090626U, 0x9efd63b7U, 0xaec16f87U, 0xf7cf38deU,
+	0x266b4d0fU, 0x736a195aU, 0xa78b2c8eU, 0xc6b375efU,
+	0x8c69e5a5U, 0x92f260bbU, 0x6df19c44U, 0x5b481372U,
+	0x0306052aU, 0x5202507bU, 0x4613556fU, 0xb6df699fU,
+	0x6a345e43U, 0x08ccc421U, 0xd968b1f0U, 0x0b0c0722U,
+	0x0e494727U, 0x373f081eU, 0xf800f8d1U, 0x1f1d0236U,
+	0xe3de3dcaU, 0x5dcd9074U, 0xe55bbeccU, 0xd828f0f1U,
+	0x3b300b12U, 0xc3f635eaU, 0x45d3966cU, 0x4dd99464U,
+	0x5a085273U, 0xbf952a96U, 0x4b5c1762U, 0x0246442bU,
+	0xf940b9d0U, 0x5607517fU, 0x0643452fU, 0x35bf8a1cU,
+	0x87a324aeU, 0x14d7c33dU, 0xa6cb6d8fU, 0x0cc9c525U,
+	0x8ee967a7U, 0x29a48d00U, 0x01868728U, 0x232e0d0aU,
+	0xb910a990U, 0xc036f6e9U, 0xb45feb9dU, 0x30faca19U,
+	0xdaa872f3U, 0xe29e7ccbU, 0x51c29378U, 0x9878e0b1U,
+	0xf68f79dfU, 0x7a205a53U, 0xb79f289eU, 0x31ba8b18U,
+	0x8126a7a8U, 0x722a585bU, 0x24ebcf0dU, 0x4493d76dU,
+	0x38f0c811U, 0x3cf5c915U, 0xe01efec9U, 0xc83cf4e1U,
+	0xbb902b92U, 0xe15ebfc8U, 0x333a091aU, 0xc7f334eeU,
+	0x637e1d4aU, 0xfa807ad3U, 0x7f651a56U, 0x70aada59U,
+	0xc97cb5e0U, 0xea947ec3U, 0xfc05f9d5U, 0x886ce4a1U,
+	0xf14abbd8U, 0xcd79b4e4U, 0xe41bffcdU, 0xee917fc7U,
+	0xa50bae8cU, 0x8aec66a3U, 0x49dc9560U, 0xaf812e86U,
+	0x1b180332U, 0xbad06a93U, 0x60bede49U, 0x7b601b52U,
+	0x39b08910U, 0x5c8dd175U, 0xceb977e7U, 0xc573b6ecU,
+	0x6f711e46U, 0x1a584233U, 0x5f4d1276U, 0x4f591666U,
+	0xab842f82U, 0x28e4cc01U, 0xd3e231faU, 0xa44bef8dU,
+	0xbd15a894U, 0xd7e730feU, 0x5082d279U, 0x74afdb5dU,
+	0x18d8c031U, 0x6b741f42U, 0x4a1c5663U, 0xa904ad80U,
+	0x9af862b3U, 0x34ffcb1dU, 0x96f761bfU, 0x9fbd22b6U,
+	0xed51bcc4U, 0x4e195767U, 0x78a0d851U, 0x3e754b17U,
+	0x65fb9e4cU, 0xfe857bd7U, 0xb850e891U, 0x0d898424U,
+	0x68b4dc41U, 0x367f491fU, 0xf00afad9U, 0x75ef9a5cU,
+	0xd2a270fbU, 0xa2ce6c8bU, 0x69f49d40U, 0xac41ed85U,
+	0xf4ad59d7U, 0xedf31eceU, 0x086e662bU, 0x54257177U,
+	0x9cdf43bfU, 0x28466e0bU, 0x5beab178U, 0x71483952U,
+	0xf76d9ad4U, 0x754d3856U, 0xff6798dcU, 0xa4e94d87U,
+	0x0faba42cU, 0x40347463U, 0x1fbfa03cU, 0xbb328998U,
+	0x03a4a720U, 0x32d8ea11U, 0xc5d114e6U, 0x768dfb55U,
+	0x07a1a624U, 0x55653076U, 0xbf37889cU, 0xa0ec4c83U,
+	0x05212426U, 0xbe77c99dU, 0xe37c9fc0U, 0x379daa14U,
+	0x8a4ec4a9U, 0xb5bd0896U, 0xb73d8a94U, 0xc9de17eaU,
+	0xddcf12feU, 0xa669cf85U, 0xee33ddcdU, 0x7c077b5fU,
+	0xe9f61fcaU, 0x10706033U, 0xcddb16eeU, 0x46b1f765U,
+	0x63dcbf40U, 0xfe27d9ddU, 0x830487a0U, 0xa26cce81U,
+	0x187a623bU, 0x90d040b3U, 0x1d3f223eU, 0x26c9ef05U,
+	0x3c576b1fU, 0x615c3d42U, 0xdf4f90fcU, 0x092e272aU,
+	0x88ce46abU, 0x8f0b84acU, 0x0c6b672fU, 0xf9e21bdaU,
+	0xada30e8eU, 0xd08050f3U, 0xfde71adeU, 0x44317567U,
+	0x7a82f859U, 0x919001b2U, 0x53e0b370U, 0x351d2816U,
+	0xb278ca91U, 0xb3388b90U, 0x31182912U, 0x5c2f737fU,
+	0x601c7c43U, 0x629cfe41U, 0xf3689bd0U, 0x45713466U,
+	0xa1ac0d82U, 0xca1ed4e9U, 0x5fefb07cU, 0xcb5e95e8U,
+	0x345d6917U, 0xb9b20b9aU, 0x9250c2b1U, 0x0d2b262eU,
+	0x0eebe52dU, 0x02e4e621U, 0xa9a60f8aU, 0x65593c46U,
+	0x6a96fc49U, 0x3ed7e91dU, 0xc49155e7U, 0x818405a2U,
+	0x4c3b776fU, 0x00646423U, 0x931083b0U, 0x8244c6a1U,
+	0x6e93fd4dU, 0xf62ddbd5U, 0xd9ca13faU, 0x7bc2b958U,
+	0xd74592f4U, 0x3f97a81cU, 0x971582b4U, 0xd605d3f5U,
+	0x22ccee01U, 0xce1bd5edU, 0xe8b65ecbU, 0x25092c06U,
+	0x3b92a918U, 0x2789ae04U, 0x5eaff17dU, 0x43f4b760U,
+	0xab268d88U, 0x2b86ad08U, 0x4ebbf56dU, 0xb1b80992U,
+	0xfca75bdfU, 0xaf238c8cU, 0xb67dcb95U, 0xf228dad1U,
+	0xd1c011f2U, 0xe0bc5cc3U, 0x3398ab10U, 0xe639dfc5U,
+	0x9e5fc1bdU, 0x77cdba54U, 0xa7298e84U, 0x30586813U,
+	0x2d032e0eU, 0xbcf74b9fU, 0x8ccb47afU, 0xd5c510f6U,
+	0x04616527U, 0x51603172U, 0x858104a6U, 0xe4b95dc7U,
+	0xae63cd8dU, 0xb0f84893U, 0x4ffbb46cU, 0x79423b5aU,
+	0x210c2d02U, 0x70087853U, 0x64197d47U, 0x94d541b7U,
+	0x483e766bU, 0x2ac6ec09U, 0xfb6299d8U, 0x29062f0aU,
+	0x2c436f0fU, 0x15352036U, 0xda0ad0f9U, 0x3d172a1eU,
+	0xc1d415e2U, 0x7fc7b85cU, 0xc75196e4U, 0xfa22d8d9U,
+	0x193a233aU, 0xe1fc1dc2U, 0x67d9be44U, 0x6fd3bc4cU,
+	0x78027a5bU, 0x9d9f02beU, 0x69563f4aU, 0x204c6c03U,
+	0xdb4a91f8U, 0x740d7957U, 0x24496d07U, 0x17b5a234U,
+	0xa5a90c86U, 0x36ddeb15U, 0x84c145a7U, 0x2ec3ed0dU,
+	0xace34f8fU, 0x0baea528U, 0x238caf00U, 0x01242522U,
+	0x9b1a81b8U, 0xe23cdec1U, 0x9655c3b5U, 0x12f0e231U,
+	0xf8a25adbU, 0xc09454e3U, 0x73c8bb50U, 0xba72c899U,
+	0xd48551f7U, 0x582a727bU, 0x959500b6U, 0x13b0a330U,
+	0xa32c8f80U, 0x50207073U, 0x06e1e725U, 0x6699ff45U,
+	0x1afae039U, 0x1effe13dU, 0xc214d6e1U, 0xea36dcc9U,
+	0x999a03baU, 0xc35497e0U, 0x11302132U, 0xe5f91cc6U,
+	0x41743562U, 0xd88a52fbU, 0x5d6f327eU, 0x52a0f271U,
+	0xeb769dc8U, 0xc89e56ebU, 0xde0fd1fdU, 0xaa66cc89U,
+	0xd34093f0U, 0xef739cccU, 0xc611d7e5U, 0xcc9b57efU,
+	0x870186a4U, 0xa8e64e8bU, 0x6bd6bd48U, 0x8d8b06aeU,
+	0x39122b1aU, 0x98da42bbU, 0x42b4f661U, 0x596a337aU,
+	0x1bbaa138U, 0x7e87f95dU, 0xecb35fcfU, 0xe7799ec4U,
+	0x4d7b366eU, 0x38526a1bU, 0x7d473a5eU, 0x6d533e4eU,
+	0x898e07aaU, 0x0aeee429U, 0xf1e819d2U, 0x8641c7a5U,
+	0x9f1f80bcU, 0xf5ed18d6U, 0x7288fa51U, 0x56a5f375U,
+	0x3ad2e819U, 0x497e376aU, 0x68167e4bU, 0x8b0e85a8U,
+	0xb8f24a9bU, 0x16f5e335U, 0xb4fd4997U, 0xbdb70a9eU,
+	0xcf5b94ecU, 0x6c137f4fU, 0x5aaaf079U, 0x1c7f633fU,
+	0x47f1b664U, 0xdc8f53ffU, 0x9a5ac0b9U, 0x2f83ac0cU,
+	0x4abef469U, 0x14756137U, 0xd200d2f1U, 0x57e5b274U,
+	0xf0a858d3U, 0x80c444a3U, 0x4bfeb568U, 0x8e4bc5adU,
+	0x5c277bf5U, 0x45793cecU, 0xa0e44409U, 0xfcaf5355U,
+	0x3455619dU, 0x80cc4c29U, 0xf360935aU, 0xd9c21b70U,
+	0x5fe7b8f6U, 0xddc71a74U, 0x57edbafeU, 0x0c636fa5U,
+	0xa721860eU, 0xe8be5641U, 0xb735821eU, 0x13b8abbaU,
+	0xab2e8502U, 0x9a52c833U, 0x6d5b36c4U, 0xde07d977U,
+	0xaf2b8406U, 0xfdef1254U, 0x17bdaabeU, 0x08666ea1U,
+	0xadab0604U, 0x16fdebbfU, 0x4bf6bde2U, 0x9f178836U,
+	0x22c4e68bU, 0x1d372ab4U, 0x1fb7a8b6U, 0x615435c8U,
+	0x754530dcU, 0x0ee3eda7U, 0x46b9ffefU, 0xd48d597dU,
+	0x417c3de8U, 0xb8fa4211U, 0x655134ccU, 0xee3bd547U,
+	0xcb569d62U, 0x56adfbffU, 0x2b8ea582U, 0x0ae6eca3U,
+	0xb0f04019U, 0x385a6291U, 0xb5b5001cU, 0x8e43cd27U,
+	0x94dd493dU, 0xc9d61f60U, 0x77c5b2deU, 0xa1a40508U,
+	0x20446489U, 0x2781a68eU, 0xa4e1450dU, 0x516839f8U,
+	0x05292cacU, 0x780a72d1U, 0x556d38fcU, 0xecbb5745U,
+	0xd208da7bU, 0x391a2390U, 0xfb6a9152U, 0x9d970a34U,
+	0x1af2e8b3U, 0x1bb2a9b2U, 0x99920b30U, 0xf4a5515dU,
+	0xc8965e61U, 0xca16dc63U, 0x5be2b9f2U, 0xedfb1644U,
+	0x09262fa0U, 0x6294f6cbU, 0xf765925eU, 0x63d4b7caU,
+	0x9cd74b35U, 0x113829b8U, 0x3adae093U, 0xa5a1040cU,
+	0xa661c70fU, 0xaa6ec403U, 0x012c2da8U, 0xcdd31e64U,
+	0xc21cde6bU, 0x965dcb3fU, 0x6c1b77c5U, 0x290e2780U,
+	0xe4b1554dU, 0xa8ee4601U, 0x3b9aa192U, 0x2acee483U,
+	0xc619df6fU, 0x5ea7f9f7U, 0x714031d8U, 0xd3489b7aU,
+	0x7fcfb0d6U, 0x971d8a3eU, 0x3f9fa096U, 0x7e8ff1d7U,
+	0x8a46cc23U, 0x6691f7cfU, 0x403c7ce9U, 0x8d830e24U,
+	0x93188b3aU, 0x8f038c26U, 0xf625d35fU, 0xeb7e9542U,
+	0x03acafaaU, 0x830c8f2aU, 0xe631d74fU, 0x19322bb0U,
+	0x542d79fdU, 0x07a9aeaeU, 0x1ef7e9b7U, 0x5aa2f8f3U,
+	0x794a33d0U, 0x48367ee1U, 0x9b128932U, 0x4eb3fde7U,
+	0x36d5e39fU, 0xdf479876U, 0x0fa3aca6U, 0x98d24a31U,
+	0x85890c2cU, 0x147d69bdU, 0x2441658dU, 0x7d4f32d4U,
+	0xaceb4705U, 0xf9ea1350U, 0x2d0b2684U, 0x4c337fe5U,
+	0x06e9efafU, 0x18726ab1U, 0xe771964eU, 0xd1c81978U,
+	0x89860f20U, 0xd8825a71U, 0xcc935f65U, 0x3c5f6395U,
+	0xe0b45449U, 0x824cce2bU, 0x53e8bbfaU, 0x818c0d28U,
+	0x84c94d2dU, 0xbdbf0214U, 0x7280f2dbU, 0x959d083cU,
+	0x695e37c0U, 0xd74d9a7eU, 0x6fdbb4c6U, 0x52a8fafbU,
+	0xb1b00118U, 0x49763fe0U, 0xcf539c66U, 0xc7599e6eU,
+	0xd0885879U, 0x3515209cU, 0xc1dc1d68U, 0x88c64e21U,
+	0x73c0b3daU, 0xdc875b75U, 0x8cc34f25U, 0xbf3f8016U,
+	0x0d232ea4U, 0x9e57c937U, 0x2c4b6785U, 0x8649cf2fU,
+	0x04696dadU, 0xa324870aU, 0x8b068d22U, 0xa9ae0700U,
+	0x3390a39aU, 0x4ab6fce3U, 0x3edfe197U, 0xba7ac013U,
+	0x502878f9U, 0x681e76c1U, 0xdb429972U, 0x12f8eabbU,
+	0x7c0f73d5U, 0xf0a05059U, 0x3d1f2294U, 0xbb3a8112U,
+	0x0ba6ada2U, 0xf8aa5251U, 0xae6bc507U, 0xce13dd67U,
+	0xb270c21bU, 0xb675c31fU, 0x6a9ef4c3U, 0x42bcfeebU,
+	0x31102198U, 0x6bdeb5c2U, 0xb9ba0310U, 0x4d733ee4U,
+	0xe9fe1740U, 0x700070d9U, 0xf5e5105cU, 0xfa2ad053U,
+	0x43fcbfeaU, 0x601474c9U, 0x7685f3dfU, 0x02eceeabU,
+	0x7bcab1d2U, 0x47f9beeeU, 0x6e9bf5c7U, 0x641175cdU,
+	0x2f8ba486U, 0x006c6ca9U, 0xc35c9f6aU, 0x2501248cU,
+	0x91980938U, 0x30506099U, 0xea3ed443U, 0xf1e01158U,
+	0xb330831aU, 0xd60ddb7fU, 0x44397dedU, 0x4ff3bce6U,
+	0xe5f1144cU, 0x90d84839U, 0xd5cd187cU, 0xc5d91c6cU,
+	0x21042588U, 0xa264c60bU, 0x59623bf0U, 0x2ecbe587U,
+	0x3795a29eU, 0x5d673af4U, 0xda02d873U, 0xfe2fd157U,
+	0x9258ca3bU, 0xe1f41548U, 0xc09c5c69U, 0x2384a78aU,
+	0x107868b9U, 0xbe7fc117U, 0x1c776bb5U, 0x153d28bcU,
+	0x67d1b6ceU, 0xc4995d6dU, 0xf220d25bU, 0xb4f5411dU,
+	0xef7b9446U, 0x740571ddU, 0x32d0e29bU, 0x87098e2eU,
+	0xe234d64bU, 0xbcff4315U, 0x7a8af0d3U, 0xff6f9056U,
+	0x58227af1U, 0x284e6681U, 0xe374974aU, 0x26c1e78fU,
+	0x6283e16fU, 0x7bdda676U, 0x9e40de93U, 0xc20bc9cfU,
+	0x0af1fb07U, 0xbe68d6b3U, 0xcdc409c0U, 0xe76681eaU,
+	0x6143226cU, 0xe36380eeU, 0x69492064U, 0x32c7f53fU,
+	0x99851c94U, 0xd61accdbU, 0x89911884U, 0x2d1c3120U,
+	0x958a1f98U, 0xa4f652a9U, 0x53ffac5eU, 0xe0a343edU,
+	0x918f1e9cU, 0xc34b88ceU, 0x29193024U, 0x36c2f43bU,
+	0x930f9c9eU, 0x28597125U, 0x75522778U, 0xa1b312acU,
+	0x1c607c11U, 0x2393b02eU, 0x2113322cU, 0x5ff0af52U,
+	0x4be1aa46U, 0x3047773dU, 0x781d6575U, 0xea29c3e7U,
+	0x7fd8a772U, 0x865ed88bU, 0x5bf5ae56U, 0xd09f4fddU,
+	0xf5f207f8U, 0x68096165U, 0x152a3f18U, 0x34427639U,
+	0x8e54da83U, 0x06fef80bU, 0x8b119a86U, 0xb0e757bdU,
+	0xaa79d3a7U, 0xf77285faU, 0x49612844U, 0x9f009f92U,
+	0x1ee0fe13U, 0x19253c14U, 0x9a45df97U, 0x6fcca362U,
+	0x3b8db636U, 0x46aee84bU, 0x6bc9a266U, 0xd21fcddfU,
+	0xecac40e1U, 0x07beb90aU, 0xc5ce0bc8U, 0xa33390aeU,
+	0x24567229U, 0x25163328U, 0xa73691aaU, 0xca01cbc7U,
+	0xf632c4fbU, 0xf4b246f9U, 0x65462368U, 0xd35f8cdeU,
+	0x3782b53aU, 0x5c306c51U, 0xc9c108c4U, 0x5d702d50U,
+	0xa273d1afU, 0x2f9cb322U, 0x047e7a09U, 0x9b059e96U,
+	0x98c55d95U, 0x94ca5e99U, 0x3f88b732U, 0xf37784feU,
+	0xfcb844f1U, 0xa8f951a5U, 0x52bfed5fU, 0x17aabd1aU,
+	0xda15cfd7U, 0x964adc9bU, 0x053e3b08U, 0x146a7e19U,
+	0xf8bd45f5U, 0x6003636dU, 0x4fe4ab42U, 0xedec01e0U,
+	0x416b2a4cU, 0xa9b910a4U, 0x013b3a0cU, 0x402b6b4dU,
+	0xb4e256b9U, 0x58356d55U, 0x7e98e673U, 0xb32794beU,
+	0xadbc11a0U, 0xb1a716bcU, 0xc88149c5U, 0xd5da0fd8U,
+	0x3d083530U, 0xbda815b0U, 0xd8954dd5U, 0x2796b12aU,
+	0x6a89e367U, 0x390d3434U, 0x2053732dU, 0x64066269U,
+	0x47eea94aU, 0x7692e47bU, 0xa5b613a8U, 0x7017677dU,
+	0x08717905U, 0xe1e302ecU, 0x3107363cU, 0xa676d0abU,
+	0xbb2d96b6U, 0x2ad9f327U, 0x1ae5ff17U, 0x43eba84eU,
+	0x924fdd9fU, 0xc74e89caU, 0x13afbc1eU, 0x7297e57fU,
+	0x384d7535U, 0x26d6f02bU, 0xd9d50cd4U, 0xef6c83e2U,
+	0xb72295baU, 0xe626c0ebU, 0xf237c5ffU, 0x02fbf90fU,
+	0xde10ced3U, 0xbce854b1U, 0x6d4c2160U, 0xbf2897b2U,
+	0xba6dd7b7U, 0x831b988eU, 0x4c246841U, 0xab3992a6U,
+	0x57faad5aU, 0xe9e900e4U, 0x517f2e5cU, 0x6c0c6061U,
+	0x8f149b82U, 0x77d2a57aU, 0xf1f706fcU, 0xf9fd04f4U,
+	0xee2cc2e3U, 0x0bb1ba06U, 0xff7887f2U, 0xb662d4bbU,
+	0x4d642940U, 0xe223c1efU, 0xb267d5bfU, 0x819b1a8cU,
+	0x3387b43eU, 0xa0f353adU, 0x12effd1fU, 0xb8ed55b5U,
+	0x3acdf737U, 0x9d801d90U, 0xb5a217b8U, 0x970a9d9aU,
+	0x0d343900U, 0x74126679U, 0x007b7b0dU, 0x84de5a89U,
+	0x6e8ce263U, 0x56baec5bU, 0xe5e603e8U, 0x2c5c7021U,
+	0x42abe94fU, 0xce04cac3U, 0x03bbb80eU, 0x859e1b88U,
+	0x35023738U, 0xc60ec8cbU, 0x90cf5f9dU, 0xf0b747fdU,
+	0x8cd45881U, 0x88d15985U, 0x543a6e59U, 0x7c186471U,
+	0x0fb4bb02U, 0x557a2f58U, 0x871e998aU, 0x73d7a47eU,
+	0xd75a8ddaU, 0x4ea4ea43U, 0xcb418ac6U, 0xc48e4ac9U,
+	0x7d582570U, 0x5eb0ee53U, 0x48216945U, 0x3c487431U,
+	0x456e2b48U, 0x795d2474U, 0x503f6f5dU, 0x5ab5ef57U,
+	0x112f3e1cU, 0x3ec8f633U, 0xfdf805f0U, 0x1ba5be16U,
+	0xaf3c93a2U, 0x0ef4fa03U, 0xd49a4ed9U, 0xcf448bc2U,
+	0x8d941980U, 0xe8a941e5U, 0x7a9de777U, 0x7157267cU,
+	0xdb558ed6U, 0xae7cd2a3U, 0xeb6982e6U, 0xfb7d86f6U,
+	0x1fa0bf12U, 0x9cc05c91U, 0x67c6a16aU, 0x106f7f1dU,
+	0x09313804U, 0x63c3a06eU, 0xe4a642e9U, 0xc08b4bcdU,
+	0xacfc50a1U, 0xdf508fd2U, 0xfe38c6f3U, 0x1d203d10U,
+	0x2edcf223U, 0x80db5b8dU, 0x22d3f12fU, 0x2b99b226U,
+	0x59752c54U, 0xfa3dc7f7U, 0xcc8448c1U, 0x8a51db87U,
+	0xd1df0edcU, 0x4aa1eb47U, 0x0c747801U, 0xb9ad14b4U,
+	0xdc904cd1U, 0x825bd98fU, 0x442e6a49U, 0xc1cb0accU,
+	0x6686e06bU, 0x16eafc1bU, 0xddd00dd0U, 0x18657d15U,
+	0x44dc9816U, 0x5d82df0fU, 0xb81fa7eaU, 0xe454b0b6U,
+	0x2cae827eU, 0x9837afcaU, 0xeb9b70b9U, 0xc139f893U,
+	0x471c5b15U, 0xc53cf997U, 0x4f16591dU, 0x14988c46U,
+	0xbfda65edU, 0xf045b5a2U, 0xafce61fdU, 0x0b434859U,
+	0xb3d566e1U, 0x82a92bd0U, 0x75a0d527U, 0xc6fc3a94U,
+	0xb7d067e5U, 0xe514f1b7U, 0x0f46495dU, 0x109d8d42U,
+	0xb550e5e7U, 0x0e06085cU, 0x530d5e01U, 0x87ec6bd5U,
+	0x3a3f0568U, 0x05ccc957U, 0x074c4b55U, 0x79afd62bU,
+	0x6dbed33fU, 0x16180e44U, 0x5e421c0cU, 0xcc76ba9eU,
+	0x5987de0bU, 0xa001a1f2U, 0x7daad72fU, 0xf6c036a4U,
+	0xd3ad7e81U, 0x4e56181cU, 0x33754661U, 0x121d0f40U,
+	0xa80ba3faU, 0x20a18172U, 0xad4ee3ffU, 0x96b82ec4U,
+	0x8c26aadeU, 0xd12dfc83U, 0x6f3e513dU, 0xb95fe6ebU,
+	0x38bf876aU, 0x3f7a456dU, 0xbc1aa6eeU, 0x4993da1bU,
+	0x1dd2cf4fU, 0x60f19132U, 0x4d96db1fU, 0xf440b4a6U,
+	0xcaf33998U, 0x21e1c073U, 0xe39172b1U, 0x856ce9d7U,
+	0x02090b50U, 0x03494a51U, 0x8169e8d3U, 0xec5eb2beU,
+	0xd06dbd82U, 0xd2ed3f80U, 0x43195a11U, 0xf500f5a7U,
+	0x11ddcc43U, 0x7a6f1528U, 0xef9e71bdU, 0x7b2f5429U,
+	0x842ca8d6U, 0x09c3ca5bU, 0x22210370U, 0xbd5ae7efU,
+	0xbe9a24ecU, 0xb29527e0U, 0x19d7ce4bU, 0xd528fd87U,
+	0xdae73d88U, 0x8ea628dcU, 0x74e09426U, 0x31f5c463U,
+	0xfc4ab6aeU, 0xb015a5e2U, 0x23614271U, 0x32350760U,
+	0xdee23c8cU, 0x465c1a14U, 0x69bbd23bU, 0xcbb37899U,
+	0x67345335U, 0x8fe669ddU, 0x27644375U, 0x66741234U,
+	0x92bd2fc0U, 0x7e6a142cU, 0x58c79f0aU, 0x9578edc7U,
+	0x8be368d9U, 0x97f86fc5U, 0xeede30bcU, 0xf38576a1U,
+	0x1b574c49U, 0x9bf76cc9U, 0xfeca34acU, 0x01c9c853U,
+	0x4cd69a1eU, 0x1f524d4dU, 0x060c0a54U, 0x42591b10U,
+	0x61b1d033U, 0x50cd9d02U, 0x83e96ad1U, 0x56481e04U,
+	0x2e2e007cU, 0xc7bc7b95U, 0x17584f45U, 0x8029a9d2U,
+	0x9d72efcfU, 0x0c868a5eU, 0x3cba866eU, 0x65b4d137U,
+	0xb410a4e6U, 0xe111f0b3U, 0x35f0c567U, 0x54c89c06U,
+	0x1e120c4cU, 0x00898952U, 0xff8a75adU, 0xc933fa9bU,
+	0x917decc3U, 0xc079b992U, 0xd468bc86U, 0x24a48076U,
+	0xf84fb7aaU, 0x9ab72dc8U, 0x4b135819U, 0x9977eecbU,
+	0x9c32aeceU, 0xa544e1f7U, 0x6a7b1138U, 0x8d66ebdfU,
+	0x71a5d423U, 0xcfb6799dU, 0x77205725U, 0x4a531918U,
+	0xa94be2fbU, 0x518ddc03U, 0xd7a87f85U, 0xdfa27d8dU,
+	0xc873bb9aU, 0x2deec37fU, 0xd927fe8bU, 0x903dadc2U,
+	0x6b3b5039U, 0xc47cb896U, 0x9438acc6U, 0xa7c463f5U,
+	0x15d8cd47U, 0x86ac2ad4U, 0x34b08466U, 0x9eb22cccU,
+	0x1c928e4eU, 0xbbdf64e9U, 0x93fd6ec1U, 0xb155e4e3U,
+	0x2b6b4079U, 0x524d1f00U, 0x26240274U, 0xa28123f0U,
+	0x48d39b1aU, 0x70e59522U, 0xc3b97a91U, 0x0a030958U,
+	0x64f49036U, 0xe85bb3baU, 0x25e4c177U, 0xa3c162f1U,
+	0x135d4e41U, 0xe051b1b2U, 0xb69026e4U, 0xd6e83e84U,
+	0xaa8b21f8U, 0xae8e20fcU, 0x72651720U, 0x5a471d08U,
+	0x29ebc27bU, 0x73255621U, 0xa141e0f3U, 0x5588dd07U,
+	0xf105f4a3U, 0x68fb933aU, 0xed1ef3bfU, 0xe2d133b0U,
+	0x5b075c09U, 0x78ef972aU, 0x6e7e103cU, 0x1a170d48U,
+	0x63315231U, 0x5f025d0dU, 0x76601624U, 0x7cea962eU,
+	0x37704765U, 0x18978f4aU, 0xdba77c89U, 0x3dfac76fU,
+	0x8963eadbU, 0x28ab837aU, 0xf2c537a0U, 0xe91bf2bbU,
+	0xabcb60f9U, 0xcef6389cU, 0x5cc29e0eU, 0x57085f05U,
+	0xfd0af7afU, 0x8823abdaU, 0xcd36fb9fU, 0xdd22ff8fU,
+	0x39ffc66bU, 0xba9f25e8U, 0x4199d813U, 0x36300664U,
+	0x2f6e417dU, 0x459cd917U, 0xc2f93b90U, 0xe6d432b4U,
+	0x8aa329d8U, 0xf90ff6abU, 0xd867bf8aU, 0x3b7f4469U,
+	0x08838b5aU, 0xa68422f4U, 0x048c8856U, 0x0dc6cb5fU,
+	0x7f2a552dU, 0xdc62be8eU, 0xeadb31b8U, 0xac0ea2feU,
+	0xf78077a5U, 0x6cfe923eU, 0x2a2b0178U, 0x9ff26dcdU,
+	0xfacf35a8U, 0xa404a0f6U, 0x62711330U, 0xe79473b5U,
+	0x40d99912U, 0x30b58562U, 0xfb8f74a9U, 0x3e3a046cU,
+	0x2dc1ec62U, 0x349fab7bU, 0xd102d39eU, 0x8d49c4c2U,
+	0x45b3f60aU, 0xf12adbbeU, 0x828604cdU, 0xa8248ce7U,
+	0x2e012f61U, 0xac218de3U, 0x260b2d69U, 0x7d85f832U,
+	0xd6c71199U, 0x9958c1d6U, 0xc6d31589U, 0x625e3c2dU,
+	0xdac81295U, 0xebb45fa4U, 0x1cbda153U, 0xafe14ee0U,
+	0xdecd1391U, 0x8c0985c3U, 0x665b3d29U, 0x7980f936U,
+	0xdc4d9193U, 0x671b7c28U, 0x3a102a75U, 0xeef11fa1U,
+	0x5322711cU, 0x6cd1bd23U, 0x6e513f21U, 0x10b2a25fU,
+	0x04a3a74bU, 0x7f057a30U, 0x375f6878U, 0xa56bceeaU,
+	0x309aaa7fU, 0xc91cd586U, 0x14b7a35bU, 0x9fdd42d0U,
+	0xbab00af5U, 0x274b6c68U, 0x5a683215U, 0x7b007b34U,
+	0xc116d78eU, 0x49bcf506U, 0xc453978bU, 0xffa55ab0U,
+	0xe53bdeaaU, 0xb83088f7U, 0x06232549U, 0xd042929fU,
+	0x51a2f31eU, 0x56673119U, 0xd507d29aU, 0x208eae6fU,
+	0x74cfbb3bU, 0x09ece546U, 0x248baf6bU, 0x9d5dc0d2U,
+	0xa3ee4decU, 0x48fcb407U, 0x8a8c06c5U, 0xec719da3U,
+	0x6b147f24U, 0x6a543e25U, 0xe8749ca7U, 0x8543c6caU,
+	0xb970c9f6U, 0xbbf04bf4U, 0x2a042e65U, 0x9c1d81d3U,
+	0x78c0b837U, 0x1372615cU, 0x868305c9U, 0x1232205dU,
+	0xed31dca2U, 0x60debe2fU, 0x4b3c7704U, 0xd447939bU,
+	0xd7875098U, 0xdb885394U, 0x70caba3fU, 0xbc3589f3U,
+	0xb3fa49fcU, 0xe7bb5ca8U, 0x1dfde052U, 0x58e8b017U,
+	0x9557c2daU, 0xd908d196U, 0x4a7c3605U, 0x5b287314U,
+	0xb7ff48f8U, 0x2f416e60U, 0x00a6a64fU, 0xa2ae0cedU,
+	0x0e292741U, 0xe6fb1da9U, 0x4e793701U, 0x0f696640U,
+	0xfba05bb4U, 0x17776058U, 0x31daeb7eU, 0xfc6599b3U,
+	0xe2fe1cadU, 0xfee51bb1U, 0x87c344c8U, 0x9a9802d5U,
+	0x724a383dU, 0xf2ea18bdU, 0x97d740d8U, 0x68d4bc27U,
+	0x25cbee6aU, 0x764f3939U, 0x6f117e20U, 0x2b446f64U,
+	0x08aca447U, 0x39d0e976U, 0xeaf41ea5U, 0x3f556a70U,
+	0x47337408U, 0xaea10fe1U, 0x7e453b31U, 0xe934dda6U,
+	0xf46f9bbbU, 0x659bfe2aU, 0x55a7f21aU, 0x0ca9a543U,
+	0xdd0dd092U, 0x880c84c7U, 0x5cedb113U, 0x3dd5e872U,
+	0x770f7838U, 0x6994fd26U, 0x969701d9U, 0xa02e8eefU,
+	0xf86098b7U, 0xa964cde6U, 0xbd75c8f2U, 0x4db9f402U,
+	0x9152c3deU, 0xf3aa59bcU, 0x220e2c6dU, 0xf06a9abfU,
+	0xf52fdabaU, 0xcc599583U, 0x0366654cU, 0xe47b9fabU,
+	0x18b8a057U, 0xa6ab0de9U, 0x1e3d2351U, 0x234e6d6cU,
+	0xc056968fU, 0x3890a877U, 0xbeb50bf1U, 0xb6bf09f9U,
+	0xa16ecfeeU, 0x44f3b70bU, 0xb03a8affU, 0xf920d9b6U,
+	0x0226244dU, 0xad61cce2U, 0xfd25d8b2U, 0xced91781U,
+	0x7cc5b933U, 0xefb15ea0U, 0x5dadf012U, 0xf7af58b8U,
+	0x758ffa3aU, 0xd2c2109dU, 0xfae01ab5U, 0xd8489097U,
+	0x4276340dU, 0x3b506b74U, 0x4f397600U, 0xcb9c5784U,
+	0x21ceef6eU, 0x19f8e156U, 0xaaa40ee5U, 0x631e7d2cU,
+	0x0de9e442U, 0x8146c7ceU, 0x4cf9b503U, 0xcadc1685U,
+	0x7a403a35U, 0x894cc5c6U, 0xdf8d5290U, 0xbff54af0U,
+	0xc396558cU, 0xc7935488U, 0x1b786354U, 0x335a697cU,
+	0x40f6b60fU, 0x1a382255U, 0xc85c9487U, 0x3c95a973U,
+	0x981880d7U, 0x01e6e74eU, 0x840387cbU, 0x8bcc47c4U,
+	0x321a287dU, 0x11f2e35eU, 0x07636448U, 0x730a793cU,
+	0x0a2c2645U, 0x361f2979U, 0x1f7d6250U, 0x15f7e25aU,
+	0x5e6d3311U, 0x718afb3eU, 0xb2ba08fdU, 0x54e7b31bU,
+	0xe07e9eafU, 0x41b6f70eU, 0x9bd843d4U, 0x800686cfU,
+	0xc2d6148dU, 0xa7eb4ce8U, 0x35dfea7aU, 0x3e152b71U,
+	0x941783dbU, 0xe13edfaeU, 0xa42b8febU, 0xb43f8bfbU,
+	0x50e2b21fU, 0xd382519cU, 0x2884ac67U, 0x5f2d7210U,
+	0x46733509U, 0x2c81ad63U, 0xabe44fe4U, 0x8fc946c0U,
+	0xe3be5dacU, 0x901282dfU, 0xb17acbfeU, 0x5262301dU,
+	0x619eff2eU, 0xcf995680U, 0x6d91fc22U, 0x64dbbf2bU,
+	0x16372159U, 0xb57fcafaU, 0x83c645ccU, 0xc513d68aU,
+	0x9e9d03d1U, 0x05e3e64aU, 0x4336750cU, 0xf6ef19b9U,
+	0x93d241dcU, 0xcd19d482U, 0x0b6c6744U, 0x8e8907c1U,
+	0x29c4ed66U, 0x59a8f116U, 0x929200ddU, 0x57277018U,
+	0x88e068e6U, 0x91be2fffU, 0x7423571aU, 0x28684046U,
+	0xe092728eU, 0x540b5f3aU, 0x27a78049U, 0x0d050863U,
+	0x8b20abe5U, 0x09000967U, 0x832aa9edU, 0xd8a47cb6U,
+	0x73e6951dU, 0x3c794552U, 0x63f2910dU, 0xc77fb8a9U,
+	0x7fe99611U, 0x4e95db20U, 0xb99c25d7U, 0x0ac0ca64U,
+	0x7bec9715U, 0x29280147U, 0xc37ab9adU, 0xdca17db2U,
+	0x796c1517U, 0xc23af8acU, 0x9f31aef1U, 0x4bd09b25U,
+	0xf603f598U, 0xc9f039a7U, 0xcb70bba5U, 0xb59326dbU,
+	0xa18223cfU, 0xda24feb4U, 0x927eecfcU, 0x004a4a6eU,
+	0x95bb2efbU, 0x6c3d5102U, 0xb19627dfU, 0x3afcc654U,
+	0x1f918e71U, 0x826ae8ecU, 0xff49b691U, 0xde21ffb0U,
+	0x6437530aU, 0xec9d7182U, 0x6172130fU, 0x5a84de34U,
+	0x401a5a2eU, 0x1d110c73U, 0xa302a1cdU, 0x7563161bU,
+	0xf483779aU, 0xf346b59dU, 0x7026561eU, 0x85af2aebU,
+	0xd1ee3fbfU, 0xaccd61c2U, 0x81aa2befU, 0x387c4456U,
+	0x06cfc968U, 0xeddd3083U, 0x2fad8241U, 0x49501927U,
+	0xce35fba0U, 0xcf75baa1U, 0x4d551823U, 0x2062424eU,
+	0x1c514d72U, 0x1ed1cf70U, 0x8f25aae1U, 0x393c0557U,
+	0xdde13cb3U, 0xb653e5d8U, 0x23a2814dU, 0xb713a4d9U,
+	0x48105826U, 0xc5ff3aabU, 0xee1df380U, 0x7166171fU,
+	0x72a6d41cU, 0x7ea9d710U, 0xd5eb3ebbU, 0x19140d77U,
+	0x16dbcd78U, 0x429ad82cU, 0xb8dc64d6U, 0xfdc93493U,
+	0x3076465eU, 0x7c295512U, 0xef5db281U, 0xfe09f790U,
+	0x12decc7cU, 0x8a60eae4U, 0xa58722cbU, 0x078f8869U,
+	0xab08a3c5U, 0x43da992dU, 0xeb58b385U, 0xaa48e2c4U,
+	0x5e81df30U, 0xb256e4dcU, 0x94fb6ffaU, 0x59441d37U,
+	0x47df9829U, 0x5bc49f35U, 0x22e2c04cU, 0x3fb98651U,
+	0xd76bbcb9U, 0x57cb9c39U, 0x32f6c45cU, 0xcdf538a3U,
+	0x80ea6aeeU, 0xd36ebdbdU, 0xca30faa4U, 0x8e65ebe0U,
+	0xad8d20c3U, 0x9cf16df2U, 0x4fd59a21U, 0x9a74eef4U,
+	0xe212f08cU, 0x0b808b65U, 0xdb64bfb5U, 0x4c155922U,
+	0x514e1f3fU, 0xc0ba7aaeU, 0xf086769eU, 0xa98821c7U,
+	0x782c5416U, 0x2d2d0043U, 0xf9cc3597U, 0x98f46cf6U,
+	0xd22efcbcU, 0xccb579a2U, 0x33b6855dU, 0x050f0a6bU,
+	0x5d411c33U, 0x0c454962U, 0x18544c76U, 0xe8987086U,
+	0x3473475aU, 0x568bdd38U, 0x872fa8e9U, 0x554b1e3bU,
+	0x500e5e3eU, 0x69781107U, 0xa647e1c8U, 0x415a1b2fU,
+	0xbd9924d3U, 0x038a896dU, 0xbb1ca7d5U, 0x866fe9e8U,
+	0x6577120bU, 0x9db12cf3U, 0x1b948f75U, 0x139e8d7dU,
+	0x044f4b6aU, 0xe1d2338fU, 0x151b0e7bU, 0x5c015d32U,
+	0xa707a0c9U, 0x08404866U, 0x58045c36U, 0x6bf89305U,
+	0xd9e43db7U, 0x4a90da24U, 0xf88c7496U, 0x528edc3cU,
+	0xd0ae7ebeU, 0x77e39419U, 0x5fc19e31U, 0x7d691413U,
+	0xe757b089U, 0x9e71eff0U, 0xea18f284U, 0x6ebdd300U,
+	0x84ef6beaU, 0xbcd965d2U, 0x0f858a61U, 0xc63ff9a8U,
+	0xa8c860c6U, 0x2467434aU, 0xe9d83187U, 0x6ffd9201U,
+	0xdf61beb1U, 0x2c6d4142U, 0x7aacd614U, 0x1ad4ce74U,
+	0x66b7d108U, 0x62b2d00cU, 0xbe59e7d0U, 0x967bedf8U,
+	0xe5d7328bU, 0xbf19a6d1U, 0x6d7d1003U, 0x99b42df7U,
+	0x3d390453U, 0xa4c763caU, 0x2122034fU, 0x2eedc340U,
+	0x973bacf9U, 0xb4d367daU, 0xa242e0ccU, 0xd62bfdb8U,
+	0xaf0da2c1U, 0x933eadfdU, 0xba5ce6d4U, 0xb0d666deU,
+	0xfb4cb795U, 0xd4ab7fbaU, 0x179b8c79U, 0xf1c6379fU,
+	0x455f1a2bU, 0xe497738aU, 0x3ef9c750U, 0x2527024bU,
+	0x67f79009U, 0x02cac86cU, 0x90fe6efeU, 0x9b34aff5U,
+	0x3136075fU, 0x441f5b2aU, 0x010a0b6fU, 0x111e0f7fU,
+	0xf5c3369bU, 0x76a3d518U, 0x8da528e3U, 0xfa0cf694U,
+	0xe352b18dU, 0x89a029e7U, 0x0ec5cb60U, 0x2ae8c244U,
+	0x469fd928U, 0x3533065bU, 0x145b4f7aU, 0xf743b499U,
+	0xc4bf7baaU, 0x6ab8d204U, 0xc8b078a6U, 0xc1fa3bafU,
+	0xb316a5ddU, 0x105e4e7eU, 0x26e7c148U, 0x6032520eU,
+	0x3bbc8755U, 0xa0c262ceU, 0xe617f188U, 0x53ce9d3dU,
+	0x36f3c558U, 0x68385006U, 0xae4de3c0U, 0x2ba88345U,
+	0x8ce569e2U, 0xfc897592U, 0x37b38459U, 0xf206f49cU,
+	0xda58820cU, 0xc306c515U, 0x269bbdf0U, 0x7ad0aaacU,
+	0xb22a9864U, 0x06b3b5d0U, 0x751f6aa3U, 0x5fbde289U,
+	0xd998410fU, 0x5bb8e38dU, 0xd1924307U, 0x8a1c965cU,
+	0x215e7ff7U, 0x6ec1afb8U, 0x314a7be7U, 0x95c75243U,
+	0x2d517cfbU, 0x1c2d31caU, 0xeb24cf3dU, 0x5878208eU,
+	0x29547dffU, 0x7b90ebadU, 0x91c25347U, 0x8e199758U,
+	0x2bd4fffdU, 0x90821246U, 0xcd89441bU, 0x196871cfU,
+	0xa4bb1f72U, 0x9b48d34dU, 0x99c8514fU, 0xe72bcc31U,
+	0xf33ac925U, 0x889c145eU, 0xc0c60616U, 0x52f2a084U,
+	0xc703c411U, 0x3e85bbe8U, 0xe32ecd35U, 0x68442cbeU,
+	0x4d29649bU, 0xd0d20206U, 0xadf15c7bU, 0x8c99155aU,
+	0x368fb9e0U, 0xbe259b68U, 0x33caf9e5U, 0x083c34deU,
+	0x12a2b0c4U, 0x4fa9e699U, 0xf1ba4b27U, 0x27dbfcf1U,
+	0xa63b9d70U, 0xa1fe5f77U, 0x229ebcf4U, 0xd717c001U,
+	0x8356d555U, 0xfe758b28U, 0xd312c105U, 0x6ac4aebcU,
+	0x54772382U, 0xbf65da69U, 0x7d1568abU, 0x1be8f3cdU,
+	0x9c8d114aU, 0x9dcd504bU, 0x1fedf2c9U, 0x72daa8a4U,
+	0x4ee9a798U, 0x4c69259aU, 0xdd9d400bU, 0x6b84efbdU,
+	0x8f59d659U, 0xe4eb0f32U, 0x711a6ba7U, 0xe5ab4e33U,
+	0x1aa8b2ccU, 0x9747d041U, 0xbca5196aU, 0x23defdf5U,
+	0x201e3ef6U, 0x2c113dfaU, 0x8753d451U, 0x4bace79dU,
+	0x44632792U, 0x102232c6U, 0xea648e3cU, 0xaf71de79U,
+	0x62ceacb4U, 0x2e91bff8U, 0xbde5586bU, 0xacb11d7aU,
+	0x40662696U, 0xd8d8000eU, 0xf73fc821U, 0x55376283U,
+	0xf9b0492fU, 0x116273c7U, 0xb9e0596fU, 0xf8f0082eU,
+	0x0c3935daU, 0xe0ee0e36U, 0xc6438510U, 0x0bfcf7ddU,
+	0x156772c3U, 0x097c75dfU, 0x705a2aa6U, 0x6d016cbbU,
+	0x85d35653U, 0x057376d3U, 0x604e2eb6U, 0x9f4dd249U,
+	0xd2528004U, 0x81d65757U, 0x9888104eU, 0xdcdd010aU,
+	0xff35ca29U, 0xce498718U, 0x1d6d70cbU, 0xc8cc041eU,
+	0xb0aa1a66U, 0x5938618fU, 0x89dc555fU, 0x1eadb3c8U,
+	0x03f6f5d5U, 0x92029044U, 0xa23e9c74U, 0xfb30cb2dU,
+	0x2a94befcU, 0x7f95eaa9U, 0xab74df7dU, 0xca4c861cU,
+	0x80961656U, 0x9e0d9348U, 0x610e6fb7U, 0x57b7e081U,
+	0x0ff9f6d9U, 0x5efda388U, 0x4aeca69cU, 0xba209a6cU,
+	0x66cbadb0U, 0x043337d2U, 0xd5974203U, 0x07f3f4d1U,
+	0x02b6b4d4U, 0x3bc0fbedU, 0xf4ff0b22U, 0x13e2f1c5U,
+	0xef21ce39U, 0x51326387U, 0xe9a44d3fU, 0xd4d70302U,
+	0x37cff8e1U, 0xcf09c619U, 0x492c659fU, 0x41266797U,
+	0x56f7a180U, 0xb36ad965U, 0x47a3e491U, 0x0eb9b7d8U,
+	0xf5bf4a23U, 0x5af8a28cU, 0x0abcb6dcU, 0x394079efU,
+	0x8b5cd75dU, 0x182830ceU, 0xaa349e7cU, 0x003636d6U,
+	0x82169454U, 0x255b7ef3U, 0x0d7974dbU, 0x2fd1fef9U,
+	0xb5ef5a63U, 0xccc9051aU, 0xb8a0186eU, 0x3c0539eaU,
+	0xd6578100U, 0xee618f38U, 0x5d3d608bU, 0x94871342U,
+	0xfa708a2cU, 0x76dfa9a0U, 0xbb60db6dU, 0x3d4578ebU,
+	0x8dd9545bU, 0x7ed5aba8U, 0x28143cfeU, 0x486c249eU,
+	0x340f3be2U, 0x300a3ae6U, 0xece10d3aU, 0xc4c30712U,
+	0xb76fd861U, 0xeda14c3bU, 0x3fc5fae9U, 0xcb0cc71dU,
+	0x6f81eeb9U, 0xf67f8920U, 0x739ae9a5U, 0x7c5529aaU,
+	0xc5834613U, 0xe66b8d30U, 0xf0fa0a26U, 0x84931752U,
+	0xfdb5482bU, 0xc1864717U, 0xe8e40c3eU, 0xe26e8c34U,
+	0xa9f45d7fU, 0x86139550U, 0x45236693U, 0xa37edd75U,
+	0x17e7f0c1U, 0xb62f9960U, 0x6c412dbaU, 0x779fe8a1U,
+	0x354f7ae3U, 0x50722286U, 0xc2468414U, 0xc98c451fU,
+	0x638eedb5U, 0x16a7b1c0U, 0x53b2e185U, 0x43a6e595U,
+	0xa77bdc71U, 0x241b3ff2U, 0xdf1dc209U, 0xa8b41c7eU,
+	0xb1ea5b67U, 0xdb18c30dU, 0x5c7d218aU, 0x785028aeU,
+	0x142733c2U, 0x678becb1U, 0x46e3a590U, 0xa5fb5e73U,
+	0x96079140U, 0x380038eeU, 0x9a08924cU, 0x9342d145U,
+	0xe1ae4f37U, 0x42e6a494U, 0x745f2ba2U, 0x328ab8e4U,
+	0x69046dbfU, 0xf27a8824U, 0xb4af1b62U, 0x017677d7U,
+	0x644b2fb2U, 0x3a80baecU, 0xfcf5092aU, 0x791069afU,
+	0xde5d8308U, 0xae319f78U, 0x650b6eb3U, 0xa0be1e76U,
+	0xec56ba34U, 0xf508fd2dU, 0x109585c8U, 0x4cde9294U,
+	0x8424a05cU, 0x30bd8de8U, 0x4311529bU, 0x69b3dab1U,
+	0xef967937U, 0x6db6dbb5U, 0xe79c7b3fU, 0xbc12ae64U,
+	0x175047cfU, 0x58cf9780U, 0x074443dfU, 0xa3c96a7bU,
+	0x1b5f44c3U, 0x2a2309f2U, 0xdd2af705U, 0x6e7618b6U,
+	0x1f5a45c7U, 0x4d9ed395U, 0xa7cc6b7fU, 0xb817af60U,
+	0x1ddac7c5U, 0xa68c2a7eU, 0xfb877c23U, 0x2f6649f7U,
+	0x92b5274aU, 0xad46eb75U, 0xafc66977U, 0xd125f409U,
+	0xc534f11dU, 0xbe922c66U, 0xf6c83e2eU, 0x64fc98bcU,
+	0xf10dfc29U, 0x088b83d0U, 0xd520f50dU, 0x5e4a1486U,
+	0x7b275ca3U, 0xe6dc3a3eU, 0x9bff6443U, 0xba972d62U,
+	0x008181d8U, 0x882ba350U, 0x05c4c1ddU, 0x3e320ce6U,
+	0x24ac88fcU, 0x79a7dea1U, 0xc7b4731fU, 0x11d5c4c9U,
+	0x9035a548U, 0x97f0674fU, 0x149084ccU, 0xe119f839U,
+	0xb558ed6dU, 0xc87bb310U, 0xe51cf93dU, 0x5cca9684U,
+	0x62791bbaU, 0x896be251U, 0x4b1b5093U, 0x2de6cbf5U,
+	0xaa832972U, 0xabc36873U, 0x29e3caf1U, 0x44d4909cU,
+	0x78e79fa0U, 0x7a671da2U, 0xeb937833U, 0x5d8ad785U,
+	0xb957ee61U, 0xd2e5370aU, 0x4714539fU, 0xd3a5760bU,
+	0x2ca68af4U, 0xa149e879U, 0x8aab2152U, 0x15d0c5cdU,
+	0x161006ceU, 0x1a1f05c2U, 0xb15dec69U, 0x7da2dfa5U,
+	0x726d1faaU, 0x262c0afeU, 0xdc6ab604U, 0x997fe641U,
+	0x54c0948cU, 0x189f87c0U, 0x8beb6053U, 0x9abf2542U,
+	0x76681eaeU, 0xeed63836U, 0xc131f019U, 0x63395abbU,
+	0xcfbe7117U, 0x276c4bffU, 0x8fee6157U, 0xcefe3016U,
+	0x3a370de2U, 0xd6e0360eU, 0xf04dbd28U, 0x3df2cfe5U,
+	0x23694afbU, 0x3f724de7U, 0x4654129eU, 0x5b0f5483U,
+	0xb3dd6e6bU, 0x337d4eebU, 0x5640168eU, 0xa943ea71U,
+	0xe45cb83cU, 0xb7d86f6fU, 0xae862876U, 0xead33932U,
+	0xc93bf211U, 0xf847bf20U, 0x2b6348f3U, 0xfec23c26U,
+	0x86a4225eU, 0x6f3659b7U, 0xbfd26d67U, 0x28a38bf0U,
+	0x35f8cdedU, 0xa40ca87cU, 0x9430a44cU, 0xcd3ef315U,
+	0x1c9a86c4U, 0x499bd291U, 0x9d7ae745U, 0xfc42be24U,
+	0xb6982e6eU, 0xa803ab70U, 0x5700578fU, 0x61b9d8b9U,
+	0x39f7cee1U, 0x68f39bb0U, 0x7ce29ea4U, 0x8c2ea254U,
+	0x50c59588U, 0x323d0feaU, 0xe3997a3bU, 0x31fdcce9U,
+	0x34b88cecU, 0x0dcec3d5U, 0xc2f1331aU, 0x25ecc9fdU,
+	0xd92ff601U, 0x673c5bbfU, 0xdfaa7507U, 0xe2d93b3aU,
+	0x01c1c0d9U, 0xf907fe21U, 0x7f225da7U, 0x77285fafU,
+	0x60f999b8U, 0x8564e15dU, 0x71addca9U, 0x38b78fe0U,
+	0xc3b1721bU, 0x6cf69ab4U, 0x3cb28ee4U, 0x0f4e41d7U,
+	0xbd52ef65U, 0x2e2608f6U, 0x9c3aa644U, 0x36380eeeU,
+	0xb418ac6cU, 0x135546cbU, 0x3b774ce3U, 0x19dfc6c1U,
+	0x83e1625bU, 0xfac73d22U, 0x8eae2056U, 0x0a0b01d2U,
+	0xe059b938U, 0xd86fb700U, 0x6b3358b3U, 0xa2892b7aU,
+	0xcc7eb214U, 0x40d19198U, 0x8d6ee355U, 0x0b4b40d3U,
+	0xbbd76c63U, 0x48db9390U, 0x1e1a04c6U, 0x7e621ca6U,
+	0x020103daU, 0x060402deU, 0xdaef3502U, 0xf2cd3f2aU,
+	0x8161e059U, 0xdbaf7403U, 0x09cbc2d1U, 0xfd02ff25U,
+	0x598fd681U, 0xc071b118U, 0x4594d19dU, 0x4a5b1192U,
+	0xf38d7e2bU, 0xd065b508U, 0xc6f4321eU, 0xb29d2f6aU,
+	0xcbbb7013U, 0xf7887f2fU, 0xdeea3406U, 0xd460b40cU,
+	0x9ffa6547U, 0xb01dad68U, 0x732d5eabU, 0x9570e54dU,
+	0x21e9c8f9U, 0x8021a158U, 0x5a4f1582U, 0x4191d099U,
+	0x034142dbU, 0x667c1abeU, 0xf448bc2cU, 0xff827d27U,
+	0x5580d58dU, 0x20a989f8U, 0x65bcd9bdU, 0x75a8ddadU,
+	0x9175e449U, 0x121507caU, 0xe913fa31U, 0x9eba2446U,
+	0x87e4635fU, 0xed16fb35U, 0x6a7319b2U, 0x4e5e1096U,
+	0x22290bfaU, 0x5185d489U, 0x70ed9da8U, 0x93f5664bU,
+	0xa009a978U, 0x0e0e00d6U, 0xac06aa74U, 0xa54ce97dU,
+	0xd7a0770fU, 0x74e89cacU, 0x4251139aU, 0x048480dcU,
+	0x5f0a5587U, 0xc474b01cU, 0x82a1235aU, 0x37784fefU,
+	0x5245178aU, 0x0c8e82d4U, 0xcafb3112U, 0x4f1e5197U,
+	0xe853bb30U, 0x983fa740U, 0x5305568bU, 0x96b0264eU,
+	0xb0b90987U, 0xa9e74e9eU, 0x4c7a367bU, 0x10312127U,
+	0xd8cb13efU, 0x6c523e5bU, 0x1ffee128U, 0x355c6902U,
+	0xb379ca84U, 0x31596806U, 0xbb73c88cU, 0xe0fd1dd7U,
+	0x4bbff47cU, 0x04202433U, 0x5babf06cU, 0xff26d9c8U,
+	0x47b0f770U, 0x76ccba41U, 0x81c544b6U, 0x3299ab05U,
+	0x43b5f674U, 0x11716026U, 0xfb23d8ccU, 0xe4f81cd3U,
+	0x41357476U, 0xfa6399cdU, 0xa768cf90U, 0x7389fa44U,
+	0xce5a94f9U, 0xf1a958c6U, 0xf329dac4U, 0x8dca47baU,
+	0x99db42aeU, 0xe27d9fd5U, 0xaa278d9dU, 0x38132b0fU,
+	0xade24f9aU, 0x54643063U, 0x89cf46beU, 0x02a5a735U,
+	0x27c8ef10U, 0xba33898dU, 0xc710d7f0U, 0xe6789ed1U,
+	0x5c6e326bU, 0xd4c410e3U, 0x592b726eU, 0x62ddbf55U,
+	0x78433b4fU, 0x25486d12U, 0x9b5bc0acU, 0x4d3a777aU,
+	0xccda16fbU, 0xcb1fd4fcU, 0x487f377fU, 0xbdf64b8aU,
+	0xe9b75edeU, 0x949400a3U, 0xb9f34a8eU, 0x00252537U,
+	0x3e96a809U, 0xd58451e2U, 0x17f4e320U, 0x71097846U,
+	0xf66c9ac1U, 0xf72cdbc0U, 0x750c7942U, 0x183b232fU,
+	0x24082c13U, 0x2688ae11U, 0xb77ccb80U, 0x01656436U,
+	0xe5b85dd2U, 0x8e0a84b9U, 0x1bfbe02cU, 0x8f4ac5b8U,
+	0x70493947U, 0xfda65bcaU, 0xd64492e1U, 0x493f767eU,
+	0x4affb57dU, 0x46f0b671U, 0xedb25fdaU, 0x214d6c16U,
+	0x2e82ac19U, 0x7ac3b94dU, 0x808505b7U, 0xc59055f2U,
+	0x082f273fU, 0x44703473U, 0xd704d3e0U, 0xc65096f1U,
+	0x2a87ad1dU, 0xb2398b85U, 0x9dde43aaU, 0x3fd6e908U,
+	0x9351c2a4U, 0x7b83f84cU, 0xd301d2e4U, 0x921183a5U,
+	0x66d8be51U, 0x8a0f85bdU, 0xaca20e9bU, 0x611d7c56U,
+	0x7f86f948U, 0x639dfe54U, 0x1abba12dU, 0x07e0e730U,
+	0xef32ddd8U, 0x6f92fd58U, 0x0aafa53dU, 0xf5ac59c2U,
+	0xb8b30b8fU, 0xeb37dcdcU, 0xf2699bc5U, 0xb63c8a81U,
+	0x95d441a2U, 0xa4a80c93U, 0x778cfb40U, 0xa22d8f95U,
+	0xda4b91edU, 0x33d9ea04U, 0xe33dded4U, 0x744c3843U,
+	0x69177e5eU, 0xf8e31bcfU, 0xc8df17ffU, 0x91d140a6U,
+	0x40753577U, 0x15746122U, 0xc19554f6U, 0xa0ad0d97U,
+	0xea779dddU, 0xf4ec18c3U, 0x0befe43cU, 0x3d566b0aU,
+	0x65187d52U, 0x341c2803U, 0x200d2d17U, 0xd0c111e7U,
+	0x0c2a263bU, 0x6ed2bc59U, 0xbf76c988U, 0x6d127f5aU,
+	0x68573f5fU, 0x51217066U, 0x9e1e80a9U, 0x79037a4eU,
+	0x85c045b2U, 0x3bd3e80cU, 0x8345c6b4U, 0xbe368889U,
+	0x5d2e736aU, 0xa5e84d92U, 0x23cdee14U, 0x2bc7ec1cU,
+	0x3c162a0bU, 0xd98b52eeU, 0x2d426f1aU, 0x64583c53U,
+	0x9f5ec1a8U, 0x30192907U, 0x605d3d57U, 0x53a1f264U,
+	0xe1bd5cd6U, 0x72c9bb45U, 0xc0d515f7U, 0x6ad7bd5dU,
+	0xe8f71fdfU, 0x4fbaf578U, 0x6798ff50U, 0x45307572U,
+	0xdf0ed1e8U, 0xa6288e91U, 0xd24193e5U, 0x56e4b261U,
+	0xbcb60a8bU, 0x848004b3U, 0x37dceb00U, 0xfe6698c9U,
+	0x909101a7U, 0x1c3e222bU, 0xd18150e6U, 0x57a4f360U,
+	0xe738dfd0U, 0x14342023U, 0x42f5b775U, 0x228daf15U,
+	0x5eeeb069U, 0x5aebb16dU, 0x860086b1U, 0xae228c99U,
+	0xdd8e53eaU, 0x8740c7b0U, 0x55247162U, 0xa1ed4c96U,
+	0x05606532U, 0x9c9e02abU, 0x197b622eU, 0x16b4a221U,
+	0xaf62cd98U, 0x8c8a06bbU, 0x9a1b81adU, 0xee729cd9U,
+	0x9754c3a0U, 0xab67cc9cU, 0x820587b5U, 0x888f07bfU,
+	0xc315d6f4U, 0xecf21edbU, 0x2fc2ed18U, 0xc99f56feU,
+	0x7d067b4aU, 0xdcce12ebU, 0x06a0a631U, 0x1d7e632aU,
+	0x5faef168U, 0x3a93a90dU, 0xa8a70f9fU, 0xa36dce94U,
+	0x096f663eU, 0x7c463a4bU, 0x39536a0eU, 0x29476e1eU,
+	0xcd9a57faU, 0x4efab479U, 0xb5fc4982U, 0xc25597f5U,
+	0xdb0bd0ecU, 0xb1f94886U, 0x369caa01U, 0x12b1a325U,
+	0x7ec6b849U, 0x0d6a673aU, 0x2c022e1bU, 0xcf1ad5f8U,
+	0xfce61acbU, 0x52e1b365U, 0xf0e919c7U, 0xf9a35aceU,
+	0x8b4fc4bcU, 0x28072f1fU, 0x1ebea029U, 0x586b336fU,
+	0x03e5e634U, 0x989b03afU, 0xde4e90e9U, 0x6b97fc5cU,
+	0x0eaaa439U, 0x50613167U, 0x961482a1U, 0x13f1e224U,
+	0xb4bc0883U, 0xc4d014f3U, 0x0feae538U, 0xca5f95fdU,
+	0x0acac04eU, 0x13948757U, 0xf609ffb2U, 0xaa42e8eeU,
+	0x62b8da26U, 0xd621f792U, 0xa58d28e1U, 0x8f2fa0cbU,
+	0x090a034dU, 0x8b2aa1cfU, 0x01000145U, 0x5a8ed41eU,
+	0xf1cc3db5U, 0xbe53edfaU, 0xe1d839a5U, 0x45551001U,
+	0xfdc33eb9U, 0xccbf7388U, 0x3bb68d7fU, 0x88ea62ccU,
+	0xf9c63fbdU, 0xab02a9efU, 0x41501105U, 0x5e8bd51aU,
+	0xfb46bdbfU, 0x40105004U, 0x1d1b0659U, 0xc9fa338dU,
+	0x74295d30U, 0x4bda910fU, 0x495a130dU, 0x37b98e73U,
+	0x23a88b67U, 0x580e561cU, 0x10544454U, 0x8260e2c6U,
+	0x17918653U, 0xee17f9aaU, 0x33bc8f77U, 0xb8d66efcU,
+	0x9dbb26d9U, 0x00404044U, 0x7d631e39U, 0x5c0b5718U,
+	0xe61dfba2U, 0x6eb7d92aU, 0xe358bba7U, 0xd8ae769cU,
+	0xc230f286U, 0x9f3ba4dbU, 0x21280965U, 0xf749beb3U,
+	0x76a9df32U, 0x716c1d35U, 0xf20cfeb6U, 0x07858243U,
+	0x53c49717U, 0x2ee7c96aU, 0x03808347U, 0xba56ecfeU,
+	0x84e561c0U, 0x6ff7982bU, 0xad872ae9U, 0xcb7ab18fU,
+	0x4c1f5308U, 0x4d5f1209U, 0xcf7fb08bU, 0xa248eae6U,
+	0x9e7be5daU, 0x9cfb67d8U, 0x0d0f0249U, 0xbb16adffU,
+	0x5fcb941bU, 0x34794d70U, 0xa18829e5U, 0x35390c71U,
+	0xca3af08eU, 0x47d59203U, 0x6c375b28U, 0xf34cbfb7U,
+	0xf08c7cb4U, 0xfc837fb8U, 0x57c19613U, 0x9b3ea5dfU,
+	0x94f165d0U, 0xc0b07084U, 0x3af6cc7eU, 0x7fe39c3bU,
+	0xb25ceef6U, 0xfe03fdbaU, 0x6d771a29U, 0x7c235f38U,
+	0x90f464d4U, 0x084a424cU, 0x27ad8a63U, 0x85a520c1U,
+	0x29220b6dU, 0xc1f03185U, 0x69721b2dU, 0x28624a6cU,
+	0xdcab7798U, 0x307c4c74U, 0x16d1c752U, 0xdb6eb59fU,
+	0xc5f53081U, 0xd9ee379dU, 0xa0c868e4U, 0xbd932ef9U,
+	0x55411411U, 0xd5e13491U, 0xb0dc6cf4U, 0x4fdf900bU,
+	0x02c0c246U, 0x51441515U, 0x481a520cU, 0x0c4f4348U,
+	0x2fa7886bU, 0x1edbc55aU, 0xcdff3289U, 0x185e465cU,
+	0x60385824U, 0x89aa23cdU, 0x594e171dU, 0xce3ff18aU,
+	0xd364b797U, 0x4290d206U, 0x72acde36U, 0x2ba2896fU,
+	0xfa06fcbeU, 0xaf07a8ebU, 0x7be69d3fU, 0x1adec45eU,
+	0x50045414U, 0x4e9fd10aU, 0xb19c2df5U, 0x8725a2c3U,
+	0xdf6bb49bU, 0x8e6fe1caU, 0x9a7ee4deU, 0x6ab2d82eU,
+	0xb659eff2U, 0xd4a17590U, 0x05050041U, 0xd761b693U,
+	0xd224f696U, 0xeb52b9afU, 0x246d4960U, 0xc370b387U,
+	0x3fb38c7bU, 0x81a021c5U, 0x39360f7dU, 0x04454140U,
+	0xe75dbaa3U, 0x1f9b845bU, 0x99be27ddU, 0x91b425d5U,
+	0x8665e3c2U, 0x63f89b27U, 0x9731a6d3U, 0xde2bf59aU,
+	0x252d0861U, 0x8a6ae0ceU, 0xda2ef49eU, 0xe9d23badU,
+	0x5bce951fU, 0xc8ba728cU, 0x7aa6dc3eU, 0xd0a47494U,
+	0x5284d616U, 0xf5c93cb1U, 0xddeb3699U, 0xff43bcbbU,
+	0x657d1821U, 0x1c5b4758U, 0x68325a2cU, 0xec977ba8U,
+	0x06c5c342U, 0x3ef3cd7aU, 0x8daf22c9U, 0x44155100U,
+	0x2ae2c86eU, 0xa64debe2U, 0x6bf2992fU, 0xedd73aa9U,
+	0x5d4b1619U, 0xae47e9eaU, 0xf8867ebcU, 0x98fe66dcU,
+	0xe49d79a0U, 0xe09878a4U, 0x3c734f78U, 0x14514550U,
+	0x67fd9a23U, 0x3d330e79U, 0xef57b8abU, 0x1b9e855fU,
+	0xbf13acfbU, 0x26edcb62U, 0xa308abe7U, 0xacc76be8U,
+	0x15110451U, 0x36f9cf72U, 0x20684864U, 0x54015510U,
+	0x2d270a69U, 0x11140555U, 0x38764e7cU, 0x32fcce76U,
+	0x79661f3dU, 0x5681d712U, 0x95b124d1U, 0x73ec9f37U,
+	0xc775b283U, 0x66bddb22U, 0xbcd36ff8U, 0xa70daae3U,
+	0xe5dd38a1U, 0x80e060c4U, 0x12d4c656U, 0x191e075dU,
+	0xb31caff7U, 0xc635f382U, 0x8320a3c7U, 0x9334a7d7U,
+	0x77e99e33U, 0xf4897db0U, 0x0f8f804bU, 0x78265e3cU,
+	0x61781925U, 0x0b8a814fU, 0x8cef63c8U, 0xa8c26aecU,
+	0xc4b57180U, 0xb719aef3U, 0x9671e7d2U, 0x75691c31U,
+	0x4695d302U, 0xe8927aacU, 0x4a9ad00eU, 0x43d09307U,
+	0x313c0d75U, 0x9274e6d6U, 0xa4cd69e0U, 0xe218faa6U,
+	0xb9962ffdU, 0x22e8ca66U, 0x643d5920U, 0xd1e43595U,
+	0xb4d96df0U, 0xea12f8aeU, 0x2c674b68U, 0xa9822bedU,
+	0x0ecfc14aU, 0x7ea3dd3aU, 0xb5992cf1U, 0x702c5c34U,
+	0xfd53ae20U, 0xe40de939U, 0x019091dcU, 0x5ddb8680U,
+	0x9521b448U, 0x21b899fcU, 0x5214468fU, 0x78b6cea5U,
+	0xfe936d23U, 0x7cb3cfa1U, 0xf6996f2bU, 0xad17ba70U,
+	0x065553dbU, 0x49ca8394U, 0x164157cbU, 0xb2cc7e6fU,
+	0x0a5a50d7U, 0x3b261de6U, 0xcc2fe311U, 0x7f730ca2U,
+	0x0e5f51d3U, 0x5c9bc781U, 0xb6c97f6bU, 0xa912bb74U,
+	0x0cdfd3d1U, 0xb7893e6aU, 0xea826837U, 0x3e635de3U,
+	0x83b0335eU, 0xbc43ff61U, 0xbec37d63U, 0xc020e01dU,
+	0xd431e509U, 0xaf973872U, 0xe7cd2a3aU, 0x75f98ca8U,
+	0xe008e83dU, 0x198e97c4U, 0xc425e119U, 0x4f4f0092U,
+	0x6a2248b7U, 0xf7d92e2aU, 0x8afa7057U, 0xab923976U,
+	0x118495ccU, 0x992eb744U, 0x14c1d5c9U, 0x2f3718f2U,
+	0x35a99ce8U, 0x68a2cab5U, 0xd6b1670bU, 0x00d0d0ddU,
+	0x8130b15cU, 0x86f5735bU, 0x059590d8U, 0xf01cec2dU,
+	0xa45df979U, 0xd97ea704U, 0xf419ed29U, 0x4dcf8290U,
+	0x737c0faeU, 0x986ef645U, 0x5a1e4487U, 0x3ce3dfe1U,
+	0xbb863d66U, 0xbac67c67U, 0x38e6dee5U, 0x55d18488U,
+	0x69e28bb4U, 0x6b6209b6U, 0xfa966c27U, 0x4c8fc391U,
+	0xa852fa75U, 0xc3e0231eU, 0x5611478bU, 0xc2a0621fU,
+	0x3da39ee0U, 0xb04cfc6dU, 0x9bae3546U, 0x04d5d1d9U,
+	0x071512daU, 0x0b1a11d6U, 0xa058f87dU, 0x6ca7cbb1U,
+	0x63680bbeU, 0x37291eeaU, 0xcd6fa210U, 0x887af255U,
+	0x45c58098U, 0x099a93d4U, 0x9aee7447U, 0x8bba3156U,
+	0x676d0abaU, 0xffd32c22U, 0xd034e40dU, 0x723c4eafU,
+	0xdebb6503U, 0x36695febU, 0x9eeb7543U, 0xdffb2402U,
+	0x2b3219f6U, 0xc7e5221aU, 0xe148a93cU, 0x2cf7dbf1U,
+	0x326c5eefU, 0x2e7759f3U, 0x5751068aU, 0x4a0a4097U,
+	0xa2d87a7fU, 0x22785affU, 0x4745029aU, 0xb846fe65U,
+	0xf559ac28U, 0xa6dd7b7bU, 0xbf833c62U, 0xfbd62d26U,
+	0xd83ee605U, 0xe942ab34U, 0x3a665ce7U, 0xefc72832U,
+	0x97a1364aU, 0x7e334da3U, 0xaed77973U, 0x39a69fe4U,
+	0x24fdd9f9U, 0xb509bc68U, 0x8535b058U, 0xdc3be701U,
+	0x0d9f92d0U, 0x589ec685U, 0x8c7ff351U, 0xed47aa30U,
+	0xa79d3a7aU, 0xb906bf64U, 0x4605439bU, 0x70bcccadU,
+	0x28f2daf5U, 0x79f68fa4U, 0x6de78ab0U, 0x9d2bb640U,
+	0x41c0819cU, 0x23381bfeU, 0xf29c6e2fU, 0x20f8d8fdU,
+	0x25bd98f8U, 0x1ccbd7c1U, 0xd3f4270eU, 0x34e9dde9U,
+	0xc82ae215U, 0x76394fabU, 0xceaf6113U, 0xf3dc2f2eU,
+	0x10c4d4cdU, 0xe802ea35U, 0x6e2749b3U, 0x662d4bbbU,
+	0x71fc8dacU, 0x9461f549U, 0x60a8c8bdU, 0x29b29bf4U,
+	0xd2b4660fU, 0x7df38ea0U, 0x2db79af0U, 0x1e4b55c3U,
+	0xac57fb71U, 0x3f231ce2U, 0x8d3fb250U, 0x273d1afaU,
+	0xa51db878U, 0x025052dfU, 0x2a7258f7U, 0x08dad2d5U,
+	0x92e4764fU, 0xebc22936U, 0x9fab3442U, 0x1b0e15c6U,
+	0xf15cad2cU, 0xc96aa314U, 0x7a364ca7U, 0xb38c3f6eU,
+	0xdd7ba600U, 0x51d4858cU, 0x9c6bf741U, 0x1a4e54c7U,
+	0xaad27877U, 0x59de8784U, 0x0f1f10d2U, 0x6f6708b2U,
+	0x130417ceU, 0x170116caU, 0xcbea2116U, 0xe3c82b3eU,
+	0x9064f44dU, 0xcaaa6017U, 0x18ced6c5U, 0xec07eb31U,
+	0x488ac295U, 0xd174a50cU, 0x5491c589U, 0x5b5e0586U,
+	0xe2886a3fU, 0xc160a11cU, 0xd7f1260aU, 0xa3983b7eU,
+	0xdabe6407U, 0xe68d6b3bU, 0xcfef2012U, 0xc565a018U,
+	0x8eff7153U, 0xa118b97cU, 0x62284abfU, 0x8475f159U,
+	0x30ecdcedU, 0x9124b54cU, 0x4b4a0196U, 0x5094c48dU,
+	0x124456cfU, 0x77790eaaU, 0xe54da838U, 0xee876933U,
+	0x4485c199U, 0x31ac9decU, 0x74b9cda9U, 0x64adc9b9U,
+	0x8070f05dU, 0x031013deU, 0xf816ee25U, 0x8fbf3052U,
+	0x96e1774bU, 0xfc13ef21U, 0x7b760da6U, 0x5f5b0482U,
+	0x332c1feeU, 0x4080c09dU, 0x61e889bcU, 0x82f0725fU,
+	0xb10cbd6cU, 0x1f0b14c2U, 0xbd03be60U, 0xb449fd69U,
+	0xc6a5631bU, 0x65ed88b8U, 0x5354078eU, 0x158194c8U,
+	0x4e0f4193U, 0xd571a408U, 0x93a4374eU, 0x267d5bfbU,
+	0x4340039eU, 0x1d8b96c0U, 0xdbfe2506U, 0x5e1b4583U,
+	0xf956af24U, 0x893ab354U, 0x4200429fU, 0x87b5325aU,
+	0x527022acU, 0x4b2e65b5U, 0xaeb31d50U, 0xf2f80a0cU,
+	0x3a0238c4U, 0x8e9b1570U, 0xfd37ca03U, 0xd7954229U,
+	0x51b0e1afU, 0xd390432dU, 0x59bae3a7U, 0x023436fcU,
+	0xa976df57U, 0xe6e90f18U, 0xb962db47U, 0x1deff2e3U,
+	0xa579dc5bU, 0x9405916aU, 0x630c6f9dU, 0xd050802eU,
+	0xa17cdd5fU, 0xf3b84b0dU, 0x19eaf3e7U, 0x063137f8U,
+	0xa3fc5f5dU, 0x18aab2e6U, 0x45a1e4bbU, 0x9140d16fU,
+	0x2c93bfd2U, 0x136073edU, 0x11e0f1efU, 0x6f036c91U,
+	0x7b126985U, 0x00b4b4feU, 0x48eea6b6U, 0xdada0024U,
+	0x4f2b64b1U, 0xb6ad1b48U, 0x6b066d95U, 0xe06c8c1eU,
+	0xc501c43bU, 0x58faa2a6U, 0x25d9fcdbU, 0x04b1b5faU,
+	0xbea71940U, 0x360d3bc8U, 0xbbe25945U, 0x8014947eU,
+	0x9a8a1064U, 0xc7814639U, 0x7992eb87U, 0xaff35c51U,
+	0x2e133dd0U, 0x29d6ffd7U, 0xaab61c54U, 0x5f3f60a1U,
+	0x0b7e75f5U, 0x765d2b88U, 0x5b3a61a5U, 0xe2ec0e1cU,
+	0xdc5f8322U, 0x374d7ac9U, 0xf53dc80bU, 0x93c0536dU,
+	0x14a5b1eaU, 0x15e5f0ebU, 0x97c55269U, 0xfaf20804U,
+	0xc6c10738U, 0xc441853aU, 0x55b5e0abU, 0xe3ac4f1dU,
+	0x077176f9U, 0x6cc3af92U, 0xf932cb07U, 0x6d83ee93U,
+	0x9280126cU, 0x1f6f70e1U, 0x348db9caU, 0xabf65d55U,
+	0xa8369e56U, 0xa4399d5aU, 0x0f7b74f1U, 0xc384473dU,
+	0xcc4b8732U, 0x980a9266U, 0x624c2e9cU, 0x27597ed9U,
+	0xeae60c14U, 0xa6b91f58U, 0x35cdf8cbU, 0x2499bddaU,
+	0xc84e8636U, 0x50f0a0aeU, 0x7f176881U, 0xdd1fc223U,
+	0x7198e98fU, 0x994ad367U, 0x31c8f9cfU, 0x70d8a88eU,
+	0x8411957aU, 0x68c6ae96U, 0x4e6b25b0U, 0x83d4577dU,
+	0x9d4fd263U, 0x8154d57fU, 0xf8728a06U, 0xe529cc1bU,
+	0x0dfbf6f3U, 0x8d5bd673U, 0xe8668e16U, 0x176572e9U,
+	0x5a7a20a4U, 0x09fef7f7U, 0x10a0b0eeU, 0x54f5a1aaU,
+	0x771d6a89U, 0x466127b8U, 0x9545d06bU, 0x40e4a4beU,
+	0x3882bac6U, 0xd110c12fU, 0x01f4f5ffU, 0x96851368U,
+	0x8bde5575U, 0x1a2a30e4U, 0x2a163cd4U, 0x73186b8dU,
+	0xa2bc1e5cU, 0xf7bd4a09U, 0x235c7fddU, 0x426426bcU,
+	0x08beb6f6U, 0x162533e8U, 0xe926cf17U, 0xdf9f4021U,
+	0x87d15679U, 0xd6d50328U, 0xc2c4063cU, 0x32083accU,
+	0xeee30d10U, 0x8c1b9772U, 0x5dbfe2a3U, 0x8fdb5471U,
+	0x8a9e1474U, 0xb3e85b4dU, 0x7cd7ab82U, 0x9bca5165U,
+	0x67096e99U, 0xd91ac327U, 0x618ced9fU, 0x5cffa3a2U,
+	0xbfe75841U, 0x472166b9U, 0xc104c53fU, 0xc90ec737U,
+	0xdedf0120U, 0x3b4279c5U, 0xcf8b4431U, 0x86911778U,
+	0x7d97ea83U, 0xd2d0022cU, 0x8294167cU, 0xb168d94fU,
+	0x037477fdU, 0x9000906eU, 0x221c3edcU, 0x881e9676U,
+	0x0a3e34f4U, 0xad73de53U, 0x8551d47bU, 0xa7f95e59U,
+	0x3dc7fac3U, 0x44e1a5baU, 0x3088b8ceU, 0xb42d994aU,
+	0x5e7f21a0U, 0x66492f98U, 0xd515c02bU, 0x1cafb3e2U,
+	0x72582a8cU, 0xfef70900U, 0x33487bcdU, 0xb56dd84bU,
+	0x05f1f4fbU, 0xf6fd0b08U, 0xa03c9c5eU, 0xc044843eU,
+	0xbc279b42U, 0xb8229a46U, 0x64c9ad9aU, 0x4ceba7b2U,
+	0x3f4778c1U, 0x6589ec9bU, 0xb7ed5a49U, 0x432467bdU,
+	0xe7a94e19U, 0x7e572980U, 0xfbb24905U, 0xf47d890aU,
+	0x4dabe6b3U, 0x6e432d90U, 0x78d2aa86U, 0x0cbbb7f2U,
+	0x759de88bU, 0x49aee7b7U, 0x60ccac9eU, 0x6a462c94U,
+	0x21dcfddfU, 0x0e3b35f0U, 0xcd0bc633U, 0x2b567dd5U,
+	0x9fcf5061U, 0x3e0739c0U, 0xe4698d1aU, 0xffb74801U,
+	0xbd67da43U, 0xd85a8226U, 0x4a6e24b4U, 0x41a4e5bfU,
+	0xeba64d15U, 0x9e8f1160U, 0xdb9a4125U, 0xcb8e4535U,
+	0x2f537cd1U, 0xac339f52U, 0x573562a9U, 0x209cbcdeU,
+	0x39c2fbc7U, 0x533063adU, 0xd455812aU, 0xf078880eU,
+	0x9c0f9362U, 0xefa34c11U, 0xcecb0530U, 0x2dd3fed3U,
+	0x1e2f31e0U, 0xb028984eU, 0x122032ecU, 0x1b6a71e5U,
+	0x6986ef97U, 0xcace0434U, 0xfc778b02U, 0xbaa21844U,
+	0xe12ccd1fU, 0x7a522884U, 0x3c87bbc2U, 0x895ed777U,
+	0xec638f12U, 0xb2a81a4cU, 0x74dda98aU, 0xf138c90fU,
+	0x567523a8U, 0x26193fd8U, 0xed23ce13U, 0x2896bed6U,
+	0xed02ef61U, 0xf45ca878U, 0x11c1d09dU, 0x4d8ac7c1U,
+	0x8570f509U, 0x31e9d8bdU, 0x424507ceU, 0x68e78fe4U,
+	0xeec22c62U, 0x6ce28ee0U, 0xe6c82e6aU, 0xbd46fb31U,
+	0x1604129aU, 0x599bc2d5U, 0x0610168aU, 0xa29d3f2eU,
+	0x1a0b1196U, 0x2b775ca7U, 0xdc7ea250U, 0x6f224de3U,
+	0x1e0e1092U, 0x4cca86c0U, 0xa6983e2aU, 0xb943fa35U,
+	0x1c8e9290U, 0xa7d87f2bU, 0xfad32976U, 0x2e321ca2U,
+	0x93e1721fU, 0xac12be20U, 0xae923c22U, 0xd071a15cU,
+	0xc460a448U, 0xbfc67933U, 0xf79c6b7bU, 0x65a8cde9U,
+	0xf059a97cU, 0x09dfd685U, 0xd474a058U, 0x5f1e41d3U,
+	0x7a7309f6U, 0xe7886f6bU, 0x9aab3116U, 0xbbc37837U,
+	0x01d5d48dU, 0x897ff605U, 0x04909488U, 0x3f6659b3U,
+	0x25f8dda9U, 0x78f38bf4U, 0xc6e0264aU, 0x1081919cU,
+	0x9161f01dU, 0x96a4321aU, 0x15c4d199U, 0xe04dad6cU,
+	0xb40cb838U, 0xc92fe645U, 0xe448ac68U, 0x5d9ec3d1U,
+	0x632d4eefU, 0x883fb704U, 0x4a4f05c6U, 0x2cb29ea0U,
+	0xabd77c27U, 0xaa973d26U, 0x28b79fa4U, 0x4580c5c9U,
+	0x79b3caf5U, 0x7b3348f7U, 0xeac72d66U, 0x5cde82d0U,
+	0xb803bb34U, 0xd3b1625fU, 0x464006caU, 0xd2f1235eU,
+	0x2df2dfa1U, 0xa01dbd2cU, 0x8bff7407U, 0x14849098U,
+	0x1744539bU, 0x1b4b5097U, 0xb009b93cU, 0x7cf68af0U,
+	0x73394affU, 0x27785fabU, 0xdd3ee351U, 0x982bb314U,
+	0x5594c1d9U, 0x19cbd295U, 0x8abf3506U, 0x9beb7017U,
+	0x773c4bfbU, 0xef826d63U, 0xc065a54cU, 0x626d0feeU,
+	0xceea2442U, 0x26381eaaU, 0x8eba3402U, 0xcfaa6543U,
+	0x3b6358b7U, 0xd7b4635bU, 0xf119e87dU, 0x3ca69ab0U,
+	0x223d1faeU, 0x3e2618b2U, 0x470047cbU, 0x5a5b01d6U,
+	0xb2893b3eU, 0x32291bbeU, 0x571443dbU, 0xa817bf24U,
+	0xe508ed69U, 0xb68c3a3aU, 0xafd27d23U, 0xeb876c67U,
+	0xc86fa744U, 0xf913ea75U, 0x2a371da6U, 0xff966973U,
+	0x87f0770bU, 0x6e620ce2U, 0xbe863832U, 0x29f7dea5U,
+	0x34ac98b8U, 0xa558fd29U, 0x9564f119U, 0xcc6aa640U,
+	0x1dced391U, 0x48cf87c4U, 0x9c2eb210U, 0xfd16eb71U,
+	0xb7cc7b3bU, 0xa957fe25U, 0x565402daU, 0x60ed8decU,
+	0x38a39bb4U, 0x69a7cee5U, 0x7db6cbf1U, 0x8d7af701U,
+	0x5191c0ddU, 0x33695abfU, 0xe2cd2f6eU, 0x30a999bcU,
+	0x35ecd9b9U, 0x0c9a9680U, 0xc3a5664fU, 0x24b89ca8U,
+	0xd87ba354U, 0x66680eeaU, 0xdefe2052U, 0xe38d6e6fU,
+	0x0095958cU, 0xf853ab74U, 0x7e7608f2U, 0x767c0afaU,
+	0x61adccedU, 0x8430b408U, 0x70f989fcU, 0x39e3dab5U,
+	0xc2e5274eU, 0x6da2cfe1U, 0x3de6dbb1U, 0x0e1a1482U,
+	0xbc06ba30U, 0x2f725da3U, 0x9d6ef311U, 0x376c5bbbU,
+	0xb54cf939U, 0x1201139eU, 0x3a2319b6U, 0x188b9394U,
+	0x82b5370eU, 0xfb936877U, 0x8ffa7503U, 0x0b5f5487U,
+	0xe10dec6dU, 0xd93be255U, 0x6a670de6U, 0xa3dd7e2fU,
+	0xcd2ae741U, 0x4185c4cdU, 0x8c3ab600U, 0x0a1f1586U,
+	0xba833936U, 0x498fc6c5U, 0x1f4e5193U, 0x7f3649f3U,
+	0x0355568fU, 0x0750578bU, 0xdbbb6057U, 0xf3996a7fU,
+	0x8035b50cU, 0xdafb2156U, 0x089f9784U, 0xfc56aa70U,
+	0x58db83d4U, 0xc125e44dU, 0x44c084c8U, 0x4b0f44c7U,
+	0xf2d92b7eU, 0xd131e05dU, 0xc7a0674bU, 0xb3c97a3fU,
+	0xcaef2546U, 0xf6dc2a7aU, 0xdfbe6153U, 0xd534e159U,
+	0x9eae3012U, 0xb149f83dU, 0x72790bfeU, 0x9424b018U,
+	0x20bd9dacU, 0x8175f40dU, 0x5b1b40d7U, 0x40c585ccU,
+	0x0215178eU, 0x67284febU, 0xf51ce979U, 0xfed62872U,
+	0x54d480d8U, 0x21fddcadU, 0x64e88ce8U, 0x74fc88f8U,
+	0x9021b11cU, 0x1341529fU, 0xe847af64U, 0x9fee7113U,
+	0x86b0360aU, 0xec42ae60U, 0x6b274ce7U, 0x4f0a45c3U,
+	0x237d5eafU, 0x50d181dcU, 0x71b9c8fdU, 0x92a1331eU,
+	0xa15dfc2dU, 0x0f5a5583U, 0xad52ff21U, 0xa418bc28U,
+	0xd6f4225aU, 0x75bcc9f9U, 0x430546cfU, 0x05d0d589U,
+	0x5e5e00d2U, 0xc520e549U, 0x83f5760fU, 0x362c1abaU,
+	0x531142dfU, 0x0ddad781U, 0xcbaf6447U, 0x4e4a04c2U,
+	0xe907ee65U, 0x996bf215U, 0x525103deU, 0x97e4731bU,
+	0xc8a169e7U, 0xd1ff2efeU, 0x3462561bU, 0x68294147U,
+	0xa0d3738fU, 0x144a5e3bU, 0x67e68148U, 0x4d440962U,
+	0xcb61aae4U, 0x49410866U, 0xc36ba8ecU, 0x98e57db7U,
+	0x33a7941cU, 0x7c384453U, 0x23b3900cU, 0x873eb9a8U,
+	0x3fa89710U, 0x0ed4da21U, 0xf9dd24d6U, 0x4a81cb65U,
+	0x3bad9614U, 0x69690046U, 0x833bb8acU, 0x9ce07cb3U,
+	0x392d1416U, 0x827bf9adU, 0xdf70aff0U, 0x0b919a24U,
+	0xb642f499U, 0x89b138a6U, 0x8b31baa4U, 0xf5d227daU,
+	0xe1c322ceU, 0x9a65ffb5U, 0xd23fedfdU, 0x400b4b6fU,
+	0xd5fa2ffaU, 0x2c7c5003U, 0xf1d726deU, 0x7abdc755U,
+	0x5fd08f70U, 0xc22be9edU, 0xbf08b790U, 0x9e60feb1U,
+	0x2476520bU, 0xacdc7083U, 0x2133120eU, 0x1ac5df35U,
+	0x005b5b2fU, 0x5d500d72U, 0xe343a0ccU, 0x3522171aU,
+	0xb4c2769bU, 0xb307b49cU, 0x3067571fU, 0xc5ee2beaU,
+	0x91af3ebeU, 0xec8c60c3U, 0xc1eb2aeeU, 0x783d4557U,
+	0x468ec869U, 0xad9c3182U, 0x6fec8340U, 0x09111826U,
+	0x8e74faa1U, 0x8f34bba0U, 0x0d141922U, 0x6023434fU,
+	0x5c104c73U, 0x5e90ce71U, 0xcf64abe0U, 0x797d0456U,
+	0x9da03db2U, 0xf612e4d9U, 0x63e3804cU, 0xf752a5d8U,
+	0x08515927U, 0x85be3baaU, 0xae5cf281U, 0x3127161eU,
+	0x32e7d51dU, 0x3ee8d611U, 0x95aa3fbaU, 0x59550c76U,
+	0x569acc79U, 0x02dbd92dU, 0xf89d65d7U, 0xbd883592U,
+	0x7037475fU, 0x3c685413U, 0xaf1cb380U, 0xbe48f691U,
+	0x529fcd7dU, 0xca21ebe5U, 0xe5c623caU, 0x47ce8968U,
+	0xeb49a2c4U, 0x039b982cU, 0xab19b284U, 0xea09e3c5U,
+	0x1ec0de31U, 0xf217e5ddU, 0xd4ba6efbU, 0x19051c36U,
+	0x079e9928U, 0x1b859e34U, 0x62a3c14dU, 0x7ff88750U,
+	0x972abdb8U, 0x178a9d38U, 0x72b7c55dU, 0x8db439a2U,
+	0xc0ab6befU, 0x932fbcbcU, 0x8a71fba5U, 0xce24eae1U,
+	0xedcc21c2U, 0xdcb06cf3U, 0x0f949b20U, 0xda35eff5U,
+	0xa253f18dU, 0x4bc18a64U, 0x9b25beb4U, 0x0c545823U,
+	0x110f1e3eU, 0x80fb7bafU, 0xb0c7779fU, 0xe9c920c6U,
+	0x386d5517U, 0x6d6c0142U, 0xb98d3496U, 0xd8b56df7U,
+	0x926ffdbdU, 0x8cf478a3U, 0x73f7845cU, 0x454e0b6aU,
+	0x1d001d32U, 0x4c044863U, 0x58154d77U, 0xa8d97187U,
+	0x7432465bU, 0x16cadc39U, 0xc76ea9e8U, 0x150a1f3aU,
+	0x104f5f3fU, 0x29391006U, 0xe606e0c9U, 0x011b1a2eU,
+	0xfdd825d2U, 0x43cb886cU, 0xfb5da6d4U, 0xc62ee8e9U,
+	0x2536130aU, 0xddf02df2U, 0x5bd58e74U, 0x53df8c7cU,
+	0x440e4a6bU, 0xa193328eU, 0x555a0f7aU, 0x1c405c33U,
+	0xe746a1c8U, 0x48014967U, 0x18455d37U, 0x2bb99204U,
+	0x99a53cb6U, 0x0ad1db25U, 0xb8cd7597U, 0x12cfdd3dU,
+	0x90ef7fbfU, 0x37a29518U, 0x1f809f30U, 0x3d281512U,
+	0xa716b188U, 0xde30eef1U, 0xaa59f385U, 0x2efcd201U,
+	0xc4ae6aebU, 0xfc9864d3U, 0x4fc48b60U, 0x867ef8a9U,
+	0xe88961c7U, 0x6426424bU, 0xa9993086U, 0x2fbc9300U,
+	0x9f20bfb0U, 0x6c2c4043U, 0x3aedd715U, 0x5a95cf75U,
+	0x26f6d009U, 0x22f3d10dU, 0xfe18e6d1U, 0xd63aecf9U,
+	0xa596338aU, 0xff58a7d0U, 0x2d3c1102U, 0xd9f52cf6U,
+	0x7d780552U, 0xe48662cbU, 0x6163024eU, 0x6eacc241U,
+	0xd77aadf8U, 0xf49266dbU, 0xe203e1cdU, 0x966afcb9U,
+	0xef4ca3c0U, 0xd37facfcU, 0xfa1de7d5U, 0xf09767dfU,
+	0xbb0db694U, 0x94ea7ebbU, 0x57da8d78U, 0xb187369eU,
+	0x051e1b2aU, 0xa4d6728bU, 0x7eb8c651U, 0x6566034aU,
+	0x27b69108U, 0x428bc96dU, 0xd0bf6fffU, 0xdb75aef4U,
+	0x7177065eU, 0x045e5a2bU, 0x414b0a6eU, 0x515f0e7eU,
+	0xb582379aU, 0x36e2d419U, 0xcde429e2U, 0xba4df795U,
+	0xa313b08cU, 0xc9e128e6U, 0x4e84ca61U, 0x6aa9c345U,
+	0x06ded829U, 0x7572075aU, 0x541a4e7bU, 0xb702b598U,
+	0x84fe7aabU, 0x2af9d305U, 0x88f179a7U, 0x81bb3aaeU,
+	0xf357a4dcU, 0x501f4f7fU, 0x66a6c049U, 0x2073530fU,
+	0x7bfd8654U, 0xe08363cfU, 0xa656f089U, 0x138f9c3cU,
+	0x76b2c459U, 0x28795107U, 0xee0ce2c1U, 0x6be98244U,
+	0xcca468e3U, 0xbcc87493U, 0x77f28558U, 0xb247f59dU,
+	0x548dd957U, 0x4dd39e4eU, 0xa84ee6abU, 0xf405f1f7U,
+	0x3cffc33fU, 0x8866ee8bU, 0xfbca31f8U, 0xd168b9d2U,
+	0x574d1a54U, 0xd56db8d6U, 0x5f47185cU, 0x04c9cd07U,
+	0xaf8b24acU, 0xe014f4e3U, 0xbf9f20bcU, 0x1b120918U,
+	0xa38427a0U, 0x92f86a91U, 0x65f19466U, 0xd6ad7bd5U,
+	0xa78126a4U, 0xf545b0f6U, 0x1f17081cU, 0x00cccc03U,
+	0xa501a4a6U, 0x1e57491dU, 0x435c1f40U, 0x97bd2a94U,
+	0x2a6e4429U, 0x159d8816U, 0x171d0a14U, 0x69fe976aU,
+	0x7def927eU, 0x06494f05U, 0x4e135d4dU, 0xdc27fbdfU,
+	0x49d69f4aU, 0xb050e0b3U, 0x6dfb966eU, 0xe69177e5U,
+	0xc3fc3fc0U, 0x5e07595dU, 0x23240720U, 0x024c4e01U,
+	0xb85ae2bbU, 0x30f0c033U, 0xbd1fa2beU, 0x86e96f85U,
+	0x9c77eb9fU, 0xc17cbdc2U, 0x7f6f107cU, 0xa90ea7aaU,
+	0x28eec62bU, 0x2f2b042cU, 0xac4be7afU, 0x59c29b5aU,
+	0x0d838e0eU, 0x70a0d073U, 0x5dc79a5eU, 0xe411f5e7U,
+	0xdaa278d9U, 0x31b08132U, 0xf3c033f0U, 0x953da896U,
+	0x12584a11U, 0x13180b10U, 0x9138a992U, 0xfc0ff3ffU,
+	0xc03cfcc3U, 0xc2bc7ec1U, 0x53481b50U, 0xe551b4e6U,
+	0x018c8d02U, 0x6a3e5469U, 0xffcf30fcU, 0x6b7e1568U,
+	0x947de997U, 0x19928b1aU, 0x32704231U, 0xad0ba6aeU,
+	0xaecb65adU, 0xa2c466a1U, 0x09868f0aU, 0xc579bcc6U,
+	0xcab67cc9U, 0x9ef7699dU, 0x64b1d567U, 0x21a48522U,
+	0xec1bf7efU, 0xa044e4a3U, 0x33300330U, 0x22644621U,
+	0xceb37dcdU, 0x560d5b55U, 0x79ea937aU, 0xdbe239d8U,
+	0x77651274U, 0x9fb7289cU, 0x37350234U, 0x76255375U,
+	0x82ec6e81U, 0x6e3b556dU, 0x4896de4bU, 0x8529ac86U,
+	0x9bb22998U, 0x87a92e84U, 0xfe8f71fdU, 0xe3d437e0U,
+	0x0b060d08U, 0x8ba62d88U, 0xee9b75edU, 0x11988912U,
+	0x5c87db5fU, 0x0f030c0cU, 0x165d4b15U, 0x52085a51U,
+	0x71e09172U, 0x409cdc43U, 0x93b82b90U, 0x46195f45U,
+	0x3e7f413dU, 0xd7ed3ad4U, 0x07090e04U, 0x9078e893U,
+	0x8d23ae8eU, 0x1cd7cb1fU, 0x2cebc72fU, 0x75e59076U,
+	0xa441e5a7U, 0xf140b1f2U, 0x25a18426U, 0x4499dd47U,
+	0x0e434d0dU, 0x10d8c813U, 0xefdb34ecU, 0xd962bbdaU,
+	0x812cad82U, 0xd028f8d3U, 0xc439fdc7U, 0x34f5c137U,
+	0xe81ef6ebU, 0x8ae66c89U, 0x5b421958U, 0x8926af8aU,
+	0x8c63ef8fU, 0xb515a0b6U, 0x7a2a5079U, 0x9d37aa9eU,
+	0x61f49562U, 0xdfe738dcU, 0x67711664U, 0x5a025859U,
+	0xb91aa3baU, 0x41dc9d42U, 0xc7f93ec4U, 0xcff33cccU,
+	0xd822fadbU, 0x3dbf823eU, 0xc976bfcaU, 0x806cec83U,
+	0x7b6a1178U, 0xd42df9d7U, 0x8469ed87U, 0xb79522b4U,
+	0x05898c06U, 0x96fd6b95U, 0x24e1c527U, 0x8ee36d8dU,
+	0x0cc3cf0fU, 0xab8e25a8U, 0x83ac2f80U, 0xa104a5a2U,
+	0x3b3a0138U, 0x421c5e41U, 0x36754335U, 0xb2d062b1U,
+	0x5882da5bU, 0x60b4d463U, 0xd3e83bd0U, 0x1a524819U,
+	0x74a5d177U, 0xf80af2fbU, 0x35b58036U, 0xb39023b0U,
+	0x030c0f00U, 0xf000f0f3U, 0xa6c167a5U, 0xc6b97fc5U,
+	0xbada60b9U, 0xbedf61bdU, 0x62345661U, 0x4a165c49U,
+	0x39ba833aU, 0x63741760U, 0xb110a1b2U, 0x45d99c46U,
+	0xe154b5e2U, 0x78aad27bU, 0xfd4fb2feU, 0xf28072f1U,
+	0x4b561d48U, 0x68bed66bU, 0x7e2f517dU, 0x0a464c09U,
+	0x73601370U, 0x4f531c4cU, 0x66315765U, 0x6cbbd76fU,
+	0x27210624U, 0x08c6ce0bU, 0xcbf63dc8U, 0x2dab862eU,
+	0x9932ab9aU, 0x38fac23bU, 0xe29476e1U, 0xf94ab3faU,
+	0xbb9a21b8U, 0xdea779ddU, 0x4c93df4fU, 0x47591e44U,
+	0xed5bb6eeU, 0x9872ea9bU, 0xdd67badeU, 0xcd73beceU,
+	0x29ae872aU, 0xaace64a9U, 0x51c89952U, 0x26614725U,
+	0x3f3f003cU, 0x55cd9856U, 0xd2a87ad1U, 0xf68573f5U,
+	0x9af26899U, 0xe95eb7eaU, 0xc836fecbU, 0x2b2e0528U,
+	0x18d2ca1bU, 0xb6d563b5U, 0x14ddc917U, 0x1d978a1eU,
+	0x6f7b146cU, 0xcc33ffcfU, 0xfa8a70f9U, 0xbc5fe3bfU,
+	0xe7d136e4U, 0x7cafd37fU, 0x3a7a4039U, 0x8fa32c8cU,
+	0xea9e74e9U, 0xb455e1b7U, 0x72205271U, 0xf7c532f4U,
+	0x5088d853U, 0x20e4c423U, 0xebde35e8U, 0x2e6b452dU,
+	0x58722aa4U, 0x412c6dbdU, 0xa4b11558U, 0xf8fa0204U,
+	0x300030ccU, 0x84991d78U, 0xf735c20bU, 0xdd974a21U,
+	0x5bb2e9a7U, 0xd9924b25U, 0x53b8ebafU, 0x08363ef4U,
+	0xa374d75fU, 0xeceb0710U, 0xb360d34fU, 0x17edfaebU,
+	0xaf7bd453U, 0x9e079962U, 0x690e6795U, 0xda528826U,
+	0xab7ed557U, 0xf9ba4305U, 0x13e8fbefU, 0x0c333ff0U,
+	0xa9fe5755U, 0x12a8baeeU, 0x4fa3ecb3U, 0x9b42d967U,
+	0x2691b7daU, 0x19627be5U, 0x1be2f9e7U, 0x65016499U,
+	0x7110618dU, 0x0ab6bcf6U, 0x42ecaebeU, 0xd0d8082cU,
+	0x45296cb9U, 0xbcaf1340U, 0x6104659dU, 0xea6e8416U,
+	0xcf03cc33U, 0x52f8aaaeU, 0x2fdbf4d3U, 0x0eb3bdf2U,
+	0xb4a51148U, 0x3c0f33c0U, 0xb1e0514dU, 0x8a169c76U,
+	0x9088186cU, 0xcd834e31U, 0x7390e38fU, 0xa5f15459U,
+	0x241135d8U, 0x23d4f7dfU, 0xa0b4145cU, 0x553d68a9U,
+	0x017c7dfdU, 0x7c5f2380U, 0x513869adU, 0xe8ee0614U,
+	0xd65d8b2aU, 0x3d4f72c1U, 0xff3fc003U, 0x99c25b65U,
+	0x1ea7b9e2U, 0x1fe7f8e3U, 0x9dc75a61U, 0xf0f0000cU,
+	0xccc30f30U, 0xce438d32U, 0x5fb7e8a3U, 0xe9ae4715U,
+	0x0d737ef1U, 0x66c1a79aU, 0xf330c30fU, 0x6781e69bU,
+	0x98821a64U, 0x156d78e9U, 0x3e8fb1c2U, 0xa1f4555dU,
+	0xa234965eU, 0xae3b9552U, 0x05797cf9U, 0xc9864f35U,
+	0xc6498f3aU, 0x92089a6eU, 0x684e2694U, 0x2d5b76d1U,
+	0xe0e4041cU, 0xacbb1750U, 0x3fcff0c3U, 0x2e9bb5d2U,
+	0xc24c8e3eU, 0x5af2a8a6U, 0x75156089U, 0xd71dca2bU,
+	0x7b9ae187U, 0x9348db6fU, 0x3bcaf1c7U, 0x7adaa086U,
+	0x8e139d72U, 0x62c4a69eU, 0x44692db8U, 0x89d65f75U,
+	0x974dda6bU, 0x8b56dd77U, 0xf270820eU, 0xef2bc413U,
+	0x07f9fefbU, 0x8759de7bU, 0xe264861eU, 0x1d677ae1U,
+	0x507828acU, 0x03fcffffU, 0x1aa2b8e6U, 0x5ef7a9a2U,
+	0x7d1f6281U, 0x4c632fb0U, 0x9f47d863U, 0x4ae6acb6U,
+	0x3280b2ceU, 0xdb12c927U, 0x0bf6fdf7U, 0x9c871b60U,
+	0x81dc5d7dU, 0x102838ecU, 0x201434dcU, 0x791a6385U,
+	0xa8be1654U, 0xfdbf4201U, 0x295e77d5U, 0x48662eb4U,
+	0x02bcbefeU, 0x1c273be0U, 0xe324c71fU, 0xd59d4829U,
+	0x8dd35e71U, 0xdcd70b20U, 0xc8c60e34U, 0x380a32c4U,
+	0xe4e10518U, 0x86199f7aU, 0x57bdeaabU, 0x85d95c79U,
+	0x809c1c7cU, 0xb9ea5345U, 0x76d5a38aU, 0x91c8596dU,
+	0x6d0b6691U, 0xd318cb2fU, 0x6b8ee597U, 0x56fdabaaU,
+	0xb5e55049U, 0x4d236eb1U, 0xcb06cd37U, 0xc30ccf3fU,
+	0xd4dd0928U, 0x314071cdU, 0xc5894c39U, 0x8c931f70U,
+	0x7795e28bU, 0xd8d20a24U, 0x88961e74U, 0xbb6ad147U,
+	0x09767ff5U, 0x9a029866U, 0x281e36d4U, 0x821c9e7eU,
+	0x003c3cfcU, 0xa771d65bU, 0x8f53dc73U, 0xadfb5651U,
+	0x37c5f2cbU, 0x4ee3adb2U, 0x3a8ab0c6U, 0xbe2f9142U,
+	0x547d29a8U, 0x6c4b2790U, 0xdf17c823U, 0x16adbbeaU,
+	0x785a2284U, 0xf4f50108U, 0x394a73c5U, 0xbf6fd043U,
+	0x0ff3fcf3U, 0xfcff0300U, 0xaa3e9456U, 0xca468c36U,
+	0xb625934aU, 0xb220924eU, 0x6ecba592U, 0x46e9afbaU,
+	0x354570c9U, 0x6f8be493U, 0xbdef5241U, 0x49266fb5U,
+	0xedab4611U, 0x74552188U, 0xf1b0410dU, 0xfe7f8102U,
+	0x47a9eebbU, 0x64412598U, 0x72d0a28eU, 0x06b9bffaU,
+	0x7f9fe083U, 0x43acefbfU, 0x6acea496U, 0x6044249cU,
+	0x2bdef5d7U, 0x04393df8U, 0xc709ce3bU, 0x215475ddU,
+	0x95cd5869U, 0x340531c8U, 0xee6b8512U, 0xf5b54009U,
+	0xb765d24bU, 0xd2588a2eU, 0x406c2cbcU, 0x4ba6edb7U,
+	0xe1a4451dU, 0x948d1968U, 0xd198492dU, 0xc18c4d3dU,
+	0x255174d9U, 0xa631975aU, 0x5d376aa1U, 0x2a9eb4d6U,
+	0x33c0f3cfU, 0x59326ba5U, 0xde578922U, 0xfa7a8006U,
+	0x960d9b6aU, 0xe5a14419U, 0xc4c90d38U, 0x27d1f6dbU,
+	0x142d39e8U, 0xba2a9046U, 0x18223ae4U, 0x116879edU,
+	0x6384e79fU, 0xc0cc0c3cU, 0xf675830aU, 0xb0a0104cU,
+	0xeb2ec517U, 0x7050208cU, 0x3685b3caU, 0x835cdf7fU,
+	0xe661871aU, 0xb8aa1244U, 0x7edfa182U, 0xfb3ac107U,
+	0x5c772ba0U, 0x2c1b37d0U, 0xe721c61bU, 0x2294b6deU,
+	0x99e57cf2U, 0x80bb3bebU, 0x6526430eU, 0x396d5452U,
+	0xf197669aU, 0x450e4b2eU, 0x36a2945dU, 0x1c001c77U,
+	0x9a25bff1U, 0x18051d73U, 0x922fbdf9U, 0xc9a168a2U,
+	0x62e38109U, 0x2d7c5146U, 0x72f78519U, 0xd67aacbdU,
+	0x6eec8205U, 0x5f90cf34U, 0xa89931c3U, 0x1bc5de70U,
+	0x6ae98301U, 0x382d1553U, 0xd27fadb9U, 0xcda469a6U,
+	0x68690103U, 0xd33fecb8U, 0x8e34bae5U, 0x5ad58f31U,
+	0xe706e18cU, 0xd8f52db3U, 0xda75afb1U, 0xa49632cfU,
+	0xb08737dbU, 0xcb21eaa0U, 0x837bf8e8U, 0x114f5e7aU,
+	0x84be3aefU, 0x7d384516U, 0xa09333cbU, 0x2bf9d240U,
+	0x0e949a65U, 0x936ffcf8U, 0xee4ca285U, 0xcf24eba4U,
+	0x7532471eU, 0xfd986596U, 0x7077071bU, 0x4b81ca20U,
+	0x511f4e3aU, 0x0c141867U, 0xb207b5d9U, 0x6466020fU,
+	0xe586638eU, 0xe243a189U, 0x6123420aU, 0x94aa3effU,
+	0xc0eb2babU, 0xbdc875d6U, 0x90af3ffbU, 0x29795042U,
+	0x17cadd7cU, 0xfcd82497U, 0x3ea89655U, 0x58550d33U,
+	0xdf30efb4U, 0xde70aeb5U, 0x5c500c37U, 0x3167565aU,
+	0x0d545966U, 0x0fd4db64U, 0x9e20bef5U, 0x28391143U,
+	0xcce428a7U, 0xa756f1ccU, 0x32a79559U, 0xa616b0cdU,
+	0x59154c32U, 0xd4fa2ebfU, 0xff18e794U, 0x6063030bU,
+	0x63a3c008U, 0x6facc304U, 0xc4ee2aafU, 0x08111963U,
+	0x07ded96cU, 0x539fcc38U, 0xa9d970c2U, 0xeccc2087U,
+	0x2173524aU, 0x6d2c4106U, 0xfe58a695U, 0xef0ce384U,
+	0x03dbd868U, 0x9b65fef0U, 0xb48236dfU, 0x168a9c7dU,
+	0xba0db7d1U, 0x52df8d39U, 0xfa5da791U, 0xbb4df6d0U,
+	0x4f84cb24U, 0xa353f0c8U, 0x85fe7beeU, 0x48410923U,
+	0x56da8c3dU, 0x4ac18b21U, 0x33e7d458U, 0x2ebc9245U,
+	0xc66ea8adU, 0x46ce882dU, 0x23f3d048U, 0xdcf02cb7U,
+	0x91ef7efaU, 0xc26ba9a9U, 0xdb35eeb0U, 0x9f60fff4U,
+	0xbc8834d7U, 0x8df479e6U, 0x5ed08e35U, 0x8b71fae0U,
+	0xf317e498U, 0x1a859f71U, 0xca61aba1U, 0x5d104d36U,
+	0x404b0b2bU, 0xd1bf6ebaU, 0xe183628aU, 0xb88d35d3U,
+	0x69294002U, 0x3c281457U, 0xe8c92183U, 0x89f178e2U,
+	0xc32be8a8U, 0xddb06db6U, 0x22b39149U, 0x140a1e7fU,
+	0x4c440827U, 0x1d405d76U, 0x09515862U, 0xf99d6492U,
+	0x2576534eU, 0x478ec92cU, 0x962abcfdU, 0x444e0a2fU,
+	0x410b4a2aU, 0x787d0513U, 0xb742f5dcU, 0x505f0f3bU,
+	0xac9c30c7U, 0x128f9d79U, 0xaa19b3c1U, 0x976afdfcU,
+	0x7472061fU, 0x8cb438e7U, 0x0a919b61U, 0x029b9969U,
+	0x154a5f7eU, 0xf0d7279bU, 0x041e1a6fU, 0x4d044926U,
+	0xb602b4ddU, 0x19455c72U, 0x49014822U, 0x7afd8711U,
+	0xc8e129a3U, 0x5b95ce30U, 0xe9896082U, 0x438bc828U,
+	0xc1ab6aaaU, 0x66e6800dU, 0x4ec48a25U, 0x6c6c0007U,
+	0xf652a49dU, 0x8f74fbe4U, 0xfb1de690U, 0x7fb8c714U,
+	0x95ea7ffeU, 0xaddc71c6U, 0x1e809e75U, 0xd73aedbcU,
+	0xb9cd74d2U, 0x3562575eU, 0xf8dd2593U, 0x7ef88615U,
+	0xce64aaa5U, 0x3d685556U, 0x6ba9c200U, 0x0bd1da60U,
+	0x77b2c51cU, 0x73b7c418U, 0xaf5cf3c4U, 0x877ef9ecU,
+	0xf4d2269fU, 0xae1cb2c5U, 0x7c780417U, 0x88b139e3U,
+	0x2c3c1047U, 0xb5c277deU, 0x3027175bU, 0x3fe8d754U,
+	0x863eb8edU, 0xa5d673ceU, 0xb347f4d8U, 0xc72ee9acU,
+	0xbe08b6d5U, 0x823bb9e9U, 0xab59f2c0U, 0xa1d372caU,
+	0xea49a381U, 0xc5ae6baeU, 0x069e986dU, 0xe0c3238bU,
+	0x545a0e3fU, 0xf592679eU, 0x2ffcd344U, 0x3422165fU,
+	0x76f2841dU, 0x13cfdc78U, 0x81fb7aeaU, 0x8a31bbe1U,
+	0x2033134bU, 0x551a4f3eU, 0x100f1f7bU, 0x001b1b6bU,
+	0xe4c6228fU, 0x67a6c10cU, 0x9ca03cf7U, 0xeb09e280U,
+	0xf257a599U, 0x98a53df3U, 0x1fc0df74U, 0x3bedd650U,
+	0x579acd3cU, 0x2436124fU, 0x055e5b6eU, 0xe646a08dU,
+	0xd5ba6fbeU, 0x7bbdc610U, 0xd9b56cb2U, 0xd0ff2fbbU,
+	0xa213b1c9U, 0x015b5a6aU, 0x37e2d55cU, 0x7137461aU,
+	0x2ab99341U, 0xb1c776daU, 0xf712e59cU, 0x42cb8929U,
+	0x27f6d14cU, 0x793d4412U, 0xbf48f7d4U, 0x3aad9751U,
+	0x9de07df6U, 0xed8c6186U, 0x26b6904dU, 0xe303e088U,
+	0xe1fd1c92U, 0xf8a35b8bU, 0x1d3e236eU, 0x41753432U,
+	0x898f06faU, 0x3d162b4eU, 0x4ebaf43dU, 0x64187c17U,
+	0xe23ddf91U, 0x601d7d13U, 0xea37dd99U, 0xb1b908c2U,
+	0x1afbe169U, 0x55643126U, 0x0aefe579U, 0xae62ccddU,
+	0x16f4e265U, 0x2788af54U, 0xd08151a3U, 0x63ddbe10U,
+	0x12f1e361U, 0x40357533U, 0xaa67cdd9U, 0xb5bc09c6U,
+	0x10716163U, 0xab278cd8U, 0xf62cda85U, 0x22cdef51U,
+	0x9f1e81ecU, 0xa0ed4dd3U, 0xa26dcfd1U, 0xdc8e52afU,
+	0xc89f57bbU, 0xb3398ac0U, 0xfb639888U, 0x69573e1aU,
+	0xfca65a8fU, 0x05202576U, 0xd88b53abU, 0x53e1b220U,
+	0x768cfa05U, 0xeb779c98U, 0x9654c2e5U, 0xb73c8bc4U,
+	0x0d2a277eU, 0x858005f6U, 0x086f677bU, 0x3399aa40U,
+	0x29072e5aU, 0x740c7807U, 0xca1fd5b9U, 0x1c7e626fU,
+	0x9d9e03eeU, 0x9a5bc1e9U, 0x193b226aU, 0xecb25e9fU,
+	0xb8f34bcbU, 0xc5d015b6U, 0xe8b75f9bU, 0x51613022U,
+	0x6fd2bd1cU, 0x84c044f7U, 0x46b0f635U, 0x204d6d53U,
+	0xa7288fd4U, 0xa668ced5U, 0x24486c57U, 0x497f363aU,
+	0x754c3906U, 0x77ccbb04U, 0xe638de95U, 0x50217123U,
+	0xb4fc48c7U, 0xdf4e91acU, 0x4abff539U, 0xde0ed0adU,
+	0x210d2c52U, 0xace24edfU, 0x870087f4U, 0x187b636bU,
+	0x1bbba068U, 0x17b4a364U, 0xbcf64acfU, 0x70097903U,
+	0x7fc6b90cU, 0x2b87ac58U, 0xd1c110a2U, 0x94d440e7U,
+	0x596b322aU, 0x15342166U, 0x8640c6f5U, 0x971483e4U,
+	0x7bc3b808U, 0xe37d9e90U, 0xcc9a56bfU, 0x6e92fc1dU,
+	0xc215d7b1U, 0x2ac7ed59U, 0x8245c7f1U, 0xc35596b0U,
+	0x379cab44U, 0xdb4b90a8U, 0xfde61b8eU, 0x30596943U,
+	0x2ec2ec5dU, 0x32d9eb41U, 0x4bffb438U, 0x56a4f225U,
+	0xbe76c8cdU, 0x3ed6e84dU, 0x5bebb028U, 0xa4e84cd7U,
+	0xe9f71e9aU, 0xba73c9c9U, 0xa32d8ed0U, 0xe7789f94U,
+	0xc49054b7U, 0xf5ec1986U, 0x26c8ee55U, 0xf3699a80U,
+	0x8b0f84f8U, 0x629dff11U, 0xb279cbc1U, 0x25082d56U,
+	0x38536b4bU, 0xa9a70edaU, 0x999b02eaU, 0xc09555b3U,
+	0x11312062U, 0x44307437U, 0x90d141e3U, 0xf1e91882U,
+	0xbb3388c8U, 0xa5a80dd6U, 0x5aabf129U, 0x6c127e1fU,
+	0x345c6847U, 0x65583d16U, 0x71493802U, 0x818504f2U,
+	0x5d6e332eU, 0x3f96a94cU, 0xee32dc9dU, 0x3c566a4fU,
+	0x39132a4aU, 0x00656573U, 0xcf5a95bcU, 0x28476f5bU,
+	0xd48450a7U, 0x6a97fd19U, 0xd201d3a1U, 0xef729d9cU,
+	0x0c6a667fU, 0xf4ac5887U, 0x7289fb01U, 0x7a83f909U,
+	0x6d523f1eU, 0x88cf47fbU, 0x7c067a0fU, 0x351c2946U,
+	0xce1ad4bdU, 0x615d3c12U, 0x31192842U, 0x02e5e771U,
+	0xb0f949c3U, 0x238dae50U, 0x919100e2U, 0x3b93a848U,
+	0xb9b30acaU, 0x1efee06dU, 0x36dcea45U, 0x14746067U,
+	0x8e4ac4fdU, 0xf76c9b84U, 0x830586f0U, 0x07a0a774U,
+	0xedf21f9eU, 0xd5c411a6U, 0x6698fe15U, 0xaf228ddcU,
+	0xc1d514b2U, 0x4d7a373eU, 0x80c545f3U, 0x06e0e675U,
+	0xb67ccac5U, 0x45703536U, 0x13b1a260U, 0x73c9ba00U,
+	0x0faaa57cU, 0x0bafa478U, 0xd74493a4U, 0xff66998cU,
+	0x8cca46ffU, 0xd604d2a5U, 0x04606477U, 0xf0a95983U,
+	0x54247027U, 0xcdda17beU, 0x483f773bU, 0x47f0b734U,
+	0xfe26d88dU, 0xddce13aeU, 0xcb5f94b8U, 0xbf3689ccU,
+	0xc610d6b5U, 0xfa23d989U, 0xd34192a0U, 0xd9cb12aaU,
+	0x9251c3e1U, 0xbdb60bceU, 0x7e86f80dU, 0x98db43ebU,
+	0x2c426e5fU, 0x8d8a07feU, 0x57e4b324U, 0x4c3a763fU,
+	0x0eeae47dU, 0x6bd7bc18U, 0xf9e31a8aU, 0xf229db81U,
+	0x582b732bU, 0x2d022f5eU, 0x68177f1bU, 0x78037b0bU,
+	0x9cde42efU, 0x1fbea16cU, 0xe4b85c97U, 0x931182e0U,
+	0x8a4fc5f9U, 0xe0bd5d93U, 0x67d8bf14U, 0x43f5b630U,
+	0x2f82ad5cU, 0x5c2e722fU, 0x7d463b0eU, 0x9e5ec0edU,
+	0xada20fdeU, 0x03a5a670U, 0xa1ad0cd2U, 0xa8e74fdbU,
+	0xda0bd1a9U, 0x79433a0aU, 0x4ffab53cU, 0x092f267aU,
+	0x52a1f321U, 0xc9df16baU, 0x8f0a85fcU, 0x3ad3e949U,
+	0x5feeb12cU, 0x01252472U, 0xc75097b4U, 0x42b5f731U,
+	0xe5f81d96U, 0x959401e6U, 0x5eaef02dU, 0x9b1b80e8U,
+	0x82e260eeU, 0x9bbc27f7U, 0x7e215f12U, 0x226a484eU,
+	0xea907a86U, 0x5e095732U, 0x2da58841U, 0x0707006bU,
+	0x8122a3edU, 0x0302016fU, 0x8928a1e5U, 0xd2a674beU,
+	0x79e49d15U, 0x367b4d5aU, 0x69f09905U, 0xcd7db0a1U,
+	0x75eb9e19U, 0x4497d328U, 0xb39e2ddfU, 0x00c2c26cU,
+	0x71ee9f1dU, 0x232a094fU, 0xc978b1a5U, 0xd6a375baU,
+	0x736e1d1fU, 0xc838f0a4U, 0x9533a6f9U, 0x41d2932dU,
+	0xfc01fd90U, 0xc3f231afU, 0xc172b3adU, 0xbf912ed3U,
+	0xab802bc7U, 0xd026f6bcU, 0x987ce4f4U, 0x0a484266U,
+	0x9fb926f3U, 0x663f590aU, 0xbb942fd7U, 0x30fece5cU,
+	0x15938679U, 0x8868e0e4U, 0xf54bbe99U, 0xd423f7b8U,
+	0x6e355b02U, 0xe69f798aU, 0x6b701b07U, 0x5086d63cU,
+	0x4a185226U, 0x1713047bU, 0xa900a9c5U, 0x7f611e13U,
+	0xfe817f92U, 0xf944bd95U, 0x7a245e16U, 0x8fad22e3U,
+	0xdbec37b7U, 0xa6cf69caU, 0x8ba823e7U, 0x327e4c5eU,
+	0x0ccdc160U, 0xe7df388bU, 0x25af8a49U, 0x4352112fU,
+	0xc437f3a8U, 0xc577b2a9U, 0x4757102bU, 0x2a604a46U,
+	0x1653457aU, 0x14d3c778U, 0x8527a2e9U, 0x333e0d5fU,
+	0xd7e334bbU, 0xbc51edd0U, 0x29a08945U, 0xbd11acd1U,
+	0x4212502eU, 0xcffd32a3U, 0xe41ffb88U, 0x7b641f17U,
+	0x78a4dc14U, 0x74abdf18U, 0xdfe936b3U, 0x1316057fU,
+	0x1cd9c570U, 0x4898d024U, 0xb2de6cdeU, 0xf7cb3c9bU,
+	0x3a744e56U, 0x762b5d1aU, 0xe55fba89U, 0xf40bff98U,
+	0x18dcc474U, 0x8062e2ecU, 0xaf852ac3U, 0x0d8d8061U,
+	0xa10aabcdU, 0x49d89125U, 0xe15abb8dU, 0xa04aeaccU,
+	0x5483d738U, 0xb854ecd4U, 0x9ef967f2U, 0x5346153fU,
+	0x4ddd9021U, 0x51c6973dU, 0x28e0c844U, 0x35bb8e59U,
+	0xdd69b4b1U, 0x5dc99431U, 0x38f4cc54U, 0xc7f730abU,
+	0x8ae862e6U, 0xd96cb5b5U, 0xc032f2acU, 0x8467e3e8U,
+	0xa78f28cbU, 0x96f365faU, 0x45d79229U, 0x9076e6fcU,
+	0xe810f884U, 0x0182836dU, 0xd166b7bdU, 0x4617512aU,
+	0x5b4c1737U, 0xcab872a6U, 0xfa847e96U, 0xa38a29cfU,
+	0x722e5c1eU, 0x272f084bU, 0xf3ce3d9fU, 0x92f664feU,
+	0xd82cf4b4U, 0xc6b771aaU, 0x39b48d55U, 0x0f0d0263U,
+	0x5743143bU, 0x0647416aU, 0x1256447eU, 0xe29a788eU,
+	0x3e714f52U, 0x5c89d530U, 0x8d2da0e1U, 0x5f491633U,
+	0x5a0c5636U, 0x637a190fU, 0xac45e9c0U, 0x4b581327U,
+	0xb79b2cdbU, 0x09888165U, 0xb11eafddU, 0x8c6de1e0U,
+	0x6f751a03U, 0x97b324fbU, 0x1196877dU, 0x199c8575U,
+	0x0e4d4362U, 0xebd03b87U, 0x1f190673U, 0x5603553aU,
+	0xad05a8c1U, 0x0242406eU, 0x5206543eU, 0x61fa9b0dU,
+	0xd3e635bfU, 0x4092d22cU, 0xf28e7c9eU, 0x588cd434U,
+	0xdaac76b6U, 0x7de19c11U, 0x55c39639U, 0x776b1c1bU,
+	0xed55b881U, 0x9473e7f8U, 0xe01afa8cU, 0x64bfdb08U,
+	0x8eed63e2U, 0xb6db6ddaU, 0x05878269U, 0xcc3df1a0U,
+	0xa2ca68ceU, 0x2e654b42U, 0xe3da398fU, 0x65ff9a09U,
+	0xd563b6b9U, 0x266f494aU, 0x70aede1cU, 0x10d6c67cU,
+	0x6cb5d900U, 0x68b0d804U, 0xb45befd8U, 0x9c79e5f0U,
+	0xefd53a83U, 0xb51baed9U, 0x677f180bU, 0x93b625ffU,
+	0x373b0c5bU, 0xaec56bc2U, 0x2b200b47U, 0x24efcb48U,
+	0x9d39a4f1U, 0xbed16fd2U, 0xa840e8c4U, 0xdc29f5b0U,
+	0xa50faac9U, 0x993ca5f5U, 0xb05eeedcU, 0xbad46ed6U,
+	0xf14ebf9dU, 0xdea977b2U, 0x1d998471U, 0xfbc43f97U,
+	0x4f5d1223U, 0xee957b82U, 0x34fbcf58U, 0x2f250a43U,
+	0x6df59801U, 0x08c8c064U, 0x9afc66f6U, 0x9136a7fdU,
+	0x3b340f57U, 0x4e1d5322U, 0x0b080367U, 0x1b1c0777U,
+	0xffc13e93U, 0x7ca1dd10U, 0x87a720ebU, 0xf00efe9cU,
+	0xe950b985U, 0x83a221efU, 0x04c7c368U, 0x20eaca4cU,
+	0x4c9dd120U, 0x3f310e53U, 0x1e594772U, 0xfd41bc91U,
+	0xcebd73a2U, 0x60bada0cU, 0xc2b270aeU, 0xcbf833a7U,
+	0xb914add5U, 0x1a5c4676U, 0x2ce5c940U, 0x6a305a06U,
+	0x31be8f5dU, 0xaac06ac6U, 0xec15f980U, 0x59cc9535U,
+	0x3cf1cd50U, 0x623a580eU, 0xa44febc8U, 0x21aa8b4dU,
+	0x86e761eaU, 0xf68b7d9aU, 0x3db18c51U, 0xf804fc94U,
+	0x87e364eaU, 0x9ebd23f3U, 0x7b205b16U, 0x276b4c4aU,
+	0xef917e82U, 0x5b085336U, 0x28a48c45U, 0x0206046fU,
+	0x8423a7e9U, 0x0603056bU, 0x8c29a5e1U, 0xd7a770baU,
+	0x7ce59911U, 0x337a495eU, 0x6cf19d01U, 0xc87cb4a5U,
+	0x70ea9a1dU, 0x4196d72cU, 0xb69f29dbU, 0x05c3c668U,
+	0x74ef9b19U, 0x262b0d4bU, 0xcc79b5a1U, 0xd3a271beU,
+	0x766f191bU, 0xcd39f4a0U, 0x9032a2fdU, 0x44d39729U,
+	0xf900f994U, 0xc6f335abU, 0xc473b7a9U, 0xba902ad7U,
+	0xae812fc3U, 0xd527f2b8U, 0x9d7de0f0U, 0x0f494662U,
+	0x9ab822f7U, 0x633e5d0eU, 0xbe952bd3U, 0x35ffca58U,
+	0x1092827dU, 0x8d69e4e0U, 0xf04aba9dU, 0xd122f3bcU,
+	0x6b345f06U, 0xe39e7d8eU, 0x6e711f03U, 0x5587d238U,
+	0x4f195622U, 0x1212007fU, 0xac01adc1U, 0x7a601a17U,
+	0xfb807b96U, 0xfc45b991U, 0x7f255a12U, 0x8aac26e7U,
+	0xdeed33b3U, 0xa3ce6dceU, 0x8ea927e3U, 0x377f485aU,
+	0x09ccc564U, 0xe2de3c8fU, 0x20ae8e4dU, 0x4653152bU,
+	0xc136f7acU, 0xc076b6adU, 0x4256142fU, 0x2f614e42U,
+	0x1352417eU, 0x11d2c37cU, 0x8026a6edU, 0x363f095bU,
+	0xd2e230bfU, 0xb950e9d4U, 0x2ca18d41U, 0xb810a8d5U,
+	0x4713542aU, 0xcafc36a7U, 0xe11eff8cU, 0x7e651b13U,
+	0x7da5d810U, 0x71aadb1cU, 0xdae832b7U, 0x1617017bU,
+	0x19d8c174U, 0x4d99d420U, 0xb7df68daU, 0xf2ca389fU,
+	0x3f754a52U, 0x732a591eU, 0xe05ebe8dU, 0xf10afb9cU,
+	0x1dddc070U, 0x8563e6e8U, 0xaa842ec7U, 0x088c8465U,
+	0xa40bafc9U, 0x4cd99521U, 0xe45bbf89U, 0xa54beec8U,
+	0x5182d33cU, 0xbd55e8d0U, 0x9bf863f6U, 0x5647113bU,
+	0x48dc9425U, 0x54c79339U, 0x2de1cc40U, 0x30ba8a5dU,
+	0xd868b0b5U, 0x58c89035U, 0x3df5c850U, 0xc2f634afU,
+	0x8fe966e2U, 0xdc6db1b1U, 0xc533f6a8U, 0x8166e7ecU,
+	0xa28e2ccfU, 0x93f261feU, 0x40d6962dU, 0x9577e2f8U,
+	0xed11fc80U, 0x04838769U, 0xd467b3b9U, 0x4316552eU,
+	0x5e4d1333U, 0xcfb976a2U, 0xff857a92U, 0xa68b2dcbU,
+	0x772f581aU, 0x222e0c4fU, 0xf6cf399bU, 0x97f760faU,
+	0xdd2df0b0U, 0xc3b675aeU, 0x3cb58951U, 0x0a0c0667U,
+	0x5242103fU, 0x0346456eU, 0x1757407aU, 0xe79b7c8aU,
+	0x3b704b56U, 0x5988d134U, 0x882ca4e5U, 0x5a481237U,
+	0x5f0d5232U, 0x667b1d0bU, 0xa944edc4U, 0x4e591723U,
+	0xb29a28dfU, 0x0c898561U, 0xb41fabd9U, 0x896ce5e4U,
+	0x6a741e07U, 0x92b220ffU, 0x14978379U, 0x1c9d8171U,
+	0x0b4c4766U, 0xeed13f83U, 0x1a180277U, 0x5302513eU,
+	0xa804acc5U, 0x0743446aU, 0x5707503aU, 0x64fb9f09U,
+	0xd6e731bbU, 0x4593d628U, 0xf78f789aU, 0x5d8dd030U,
+	0xdfad72b2U, 0x78e09815U, 0x50c2923dU, 0x726a181fU,
+	0xe854bc85U, 0x9172e3fcU, 0xe51bfe88U, 0x61bedf0cU,
+	0x8bec67e6U, 0xb3da69deU, 0x0086866dU, 0xc93cf5a4U,
+	0xa7cb6ccaU, 0x2b644f46U, 0xe6db3d8bU, 0x60fe9e0dU,
+	0xd062b2bdU, 0x236e4d4eU, 0x75afda18U, 0x15d7c278U,
+	0x69b4dd04U, 0x6db1dc00U, 0xb15aebdcU, 0x9978e1f4U,
+	0xead43e87U, 0xb01aaaddU, 0x627e1c0fU, 0x96b721fbU,
+	0x323a085fU, 0xabc46fc6U, 0x2e210f43U, 0x21eecf4cU,
+	0x9838a0f5U, 0xbbd06bd6U, 0xad41ecc0U, 0xd928f1b4U,
+	0xa00eaecdU, 0x9c3da1f1U, 0xb55fead8U, 0xbfd56ad2U,
+	0xf44fbb99U, 0xdba873b6U, 0x18988075U, 0xfec53b93U,
+	0x4a5c1627U, 0xeb947f86U, 0x31facb5cU, 0x2a240e47U,
+	0x68f49c05U, 0x0dc9c460U, 0x9ffd62f2U, 0x9437a3f9U,
+	0x3e350b53U, 0x4b1c5726U, 0x0e090763U, 0x1e1d0373U,
+	0xfac03a97U, 0x79a0d914U, 0x82a624efU, 0xf50ffa98U,
+	0xec51bd81U, 0x86a325ebU, 0x01c6c76cU, 0x25ebce48U,
+	0x499cd524U, 0x3a300a57U, 0x1b584376U, 0xf840b895U,
+	0xcbbc77a6U, 0x65bbde08U, 0xc7b374aaU, 0xcef937a3U,
+	0xbc15a9d1U, 0x1f5d4272U, 0x29e4cd44U, 0x6f315e02U,
+	0x34bf8b59U, 0xafc16ec2U, 0xe914fd84U, 0x5ccd9131U,
+	0x39f0c954U, 0x673b5c0aU, 0xa14eefccU, 0x24ab8f49U,
+	0x83e665eeU, 0xf38a799eU, 0x38b08855U, 0xfd05f890U,
+	0x6cd4b836U, 0x758aff2fU, 0x901787caU, 0xcc5c9096U,
+	0x04a6a25eU, 0xb03f8feaU, 0xc3935099U, 0xe931d8b3U,
+	0x6f147b35U, 0xed34d9b7U, 0x671e793dU, 0x3c90ac66U,
+	0x97d245cdU, 0xd84d9582U, 0x87c641ddU, 0x234b6879U,
+	0x9bdd46c1U, 0xaaa10bf0U, 0x5da8f507U, 0xeef41ab4U,
+	0x9fd847c5U, 0xcd1cd197U, 0x274e697dU, 0x3895ad62U,
+	0x9d58c5c7U, 0x260e287cU, 0x7b057e21U, 0xafe44bf5U,
+	0x12372548U, 0x2dc4e977U, 0x2f446b75U, 0x51a7f60bU,
+	0x45b6f31fU, 0x3e102e64U, 0x764a3c2cU, 0xe47e9abeU,
+	0x718ffe2bU, 0x880981d2U, 0x55a2f70fU, 0xdec81684U,
+	0xfba55ea1U, 0x665e383cU, 0x1b7d6641U, 0x3a152f60U,
+	0x800383daU, 0x08a9a152U, 0x8546c3dfU, 0xbeb00ee4U,
+	0xa42e8afeU, 0xf925dca3U, 0x4736711dU, 0x9157c6cbU,
+	0x10b7a74aU, 0x1772654dU, 0x941286ceU, 0x619bfa3bU,
+	0x35daef6fU, 0x48f9b112U, 0x659efb3fU, 0xdc489486U,
+	0xe2fb19b8U, 0x09e9e053U, 0xcb995291U, 0xad64c9f7U,
+	0x2a012b70U, 0x2b416a71U, 0xa961c8f3U, 0xc456929eU,
+	0xf8659da2U, 0xfae51fa0U, 0x6b117a31U, 0xdd08d587U,
+	0x39d5ec63U, 0x52673508U, 0xc796519dU, 0x53277409U,
+	0xac2488f6U, 0x21cbea7bU, 0x0a292350U, 0x9552c7cfU,
+	0x969204ccU, 0x9a9d07c0U, 0x31dfee6bU, 0xfd20dda7U,
+	0xf2ef1da8U, 0xa6ae08fcU, 0x5ce8b406U, 0x19fde443U,
+	0xd442968eU, 0x981d85c2U, 0x0b696251U, 0x1a3d2740U,
+	0xf6ea1cacU, 0x6e543a34U, 0x41b3f21bU, 0xe3bb58b9U,
+	0x4f3c7315U, 0xa7ee49fdU, 0x0f6c6355U, 0x4e7c3214U,
+	0xbab50fe0U, 0x5662340cU, 0x70cfbf2aU, 0xbd70cde7U,
+	0xa3eb48f9U, 0xbff04fe5U, 0xc6d6109cU, 0xdb8d5681U,
+	0x335f6c69U, 0xb3ff4ce9U, 0xd6c2148cU, 0x29c1e873U,
+	0x64deba3eU, 0x375a6d6dU, 0x2e042a74U, 0x6a513b30U,
+	0x49b9f013U, 0x78c5bd22U, 0xabe14af1U, 0x7e403e24U,
+	0x0626205cU, 0xefb45bb5U, 0x3f506f65U, 0xa82189f2U,
+	0xb57acfefU, 0x248eaa7eU, 0x14b2a64eU, 0x4dbcf117U,
+	0x9c1884c6U, 0xc919d093U, 0x1df8e547U, 0x7cc0bc26U,
+	0x361a2c6cU, 0x2881a972U, 0xd782558dU, 0xe13bdabbU,
+	0xb975cce3U, 0xe87199b2U, 0xfc609ca6U, 0x0caca056U,
+	0xd047978aU, 0xb2bf0de8U, 0x631b7839U, 0xb17fceebU,
+	0xb43a8eeeU, 0x8d4cc1d7U, 0x42733118U, 0xa56ecbffU,
+	0x59adf403U, 0xe7be59bdU, 0x5f287705U, 0x625b3938U,
+	0x8143c2dbU, 0x7985fc23U, 0xffa05fa5U, 0xf7aa5dadU,
+	0xe07b9bbaU, 0x05e6e35fU, 0xf12fdeabU, 0xb8358de2U,
+	0x43337019U, 0xec7498b6U, 0xbc308ce6U, 0x8fcc43d5U,
+	0x3dd0ed67U, 0xaea40af4U, 0x1cb8a446U, 0xb6ba0cecU,
+	0x349aae6eU, 0x93d744c9U, 0xbbf54ee1U, 0x995dc4c3U,
+	0x03636059U, 0x7a453f20U, 0x0e2c2254U, 0x8a8903d0U,
+	0x60dbbb3aU, 0x58edb502U, 0xebb15ab1U, 0x220b2978U,
+	0x4cfcb016U, 0xc053939aU, 0x0dece157U, 0x8bc942d1U,
+	0x3b556e61U, 0xc8599192U, 0x9e9806c4U, 0xfee01ea4U,
+	0x828301d8U, 0x868600dcU, 0x5a6d3700U, 0x724f3d28U,
+	0x01e3e25bU, 0x5b2d7601U, 0x8949c0d3U, 0x7d80fd27U,
+	0xd90dd483U, 0x40f3b31aU, 0xc516d39fU, 0xcad91390U,
+	0x730f7c29U, 0x50e7b70aU, 0x4676301cU, 0x321f2d68U,
+	0x4b397211U, 0x770a7d2dU, 0x5e683604U, 0x54e2b60eU,
+	0x1f786745U, 0x309faf6aU, 0xf3af5ca9U, 0x15f2e74fU,
+	0xa16bcafbU, 0x00a3a35aU, 0xdacd1780U, 0xc113d29bU,
+	0x83c340d9U, 0xe6fe18bcU, 0x74cabe2eU, 0x7f007f25U,
+	0xd502d78fU, 0xa02b8bfaU, 0xe53edbbfU, 0xf52adfafU,
+	0x11f7e64bU, 0x929705c8U, 0x6991f833U, 0x1e382644U,
+	0x0766615dU, 0x6d94f937U, 0xeaf11bb0U, 0xcedc1294U,
+	0xa2ab09f8U, 0xd107d68bU, 0xf06f9faaU, 0x13776449U,
+	0x208bab7aU, 0x8e8c02d4U, 0x2c84a876U, 0x25ceeb7fU,
+	0x5722750dU, 0xf46a9eaeU, 0xc2d31198U, 0x840682deU,
+	0xdf885785U, 0x44f6b21eU, 0x02232158U, 0xb7fa4dedU,
+	0xd2c71588U, 0x8c0c80d6U, 0x4a793310U, 0xcf9c5395U,
+	0x68d1b932U, 0x18bda542U, 0xd3875489U, 0x1632244cU,
+	0x4ede901eU, 0x5780d707U, 0xb21dafe2U, 0xee56b8beU,
+	0x26ac8a76U, 0x9235a7c2U, 0xe19978b1U, 0xcb3bf09bU,
+	0x4d1e531dU, 0xcf3ef19fU, 0x45145115U, 0x1e9a844eU,
+	0xb5d86de5U, 0xfa47bdaaU, 0xa5cc69f5U, 0x01414051U,
+	0xb9d76ee9U, 0x88ab23d8U, 0x7fa2dd2fU, 0xccfe329cU,
+	0xbdd26fedU, 0xef16f9bfU, 0x05444155U, 0x1a9f854aU,
+	0xbf52edefU, 0x04040054U, 0x590f5609U, 0x8dee63ddU,
+	0x303d0d60U, 0x0fcec15fU, 0x0d4e435dU, 0x73adde23U,
+	0x67bcdb37U, 0x1c1a064cU, 0x54401404U, 0xc674b296U,
+	0x5385d603U, 0xaa03a9faU, 0x77a8df27U, 0xfcc23eacU,
+	0xd9af7689U, 0x44541014U, 0x39774e69U, 0x181f0748U,
+	0xa209abf2U, 0x2aa3897aU, 0xa74cebf7U, 0x9cba26ccU,
+	0x8624a2d6U, 0xdb2ff48bU, 0x653c5935U, 0xb35deee3U,
+	0x32bd8f62U, 0x35784d65U, 0xb618aee6U, 0x4391d213U,
+	0x17d0c747U, 0x6af3993aU, 0x4794d317U, 0xfe42bcaeU,
+	0xc0f13190U, 0x2be3c87bU, 0xe9937ab9U, 0x8f6ee1dfU,
+	0x080b0358U, 0x094b4259U, 0x8b6be0dbU, 0xe65cbab6U,
+	0xda6fb58aU, 0xd8ef3788U, 0x491b5219U, 0xff02fdafU,
+	0x1bdfc44bU, 0x706d1d20U, 0xe59c79b5U, 0x712d5c21U,
+	0x8e2ea0deU, 0x03c1c253U, 0x28230b78U, 0xb758efe7U,
+	0xb4982ce4U, 0xb8972fe8U, 0x13d5c643U, 0xdf2af58fU,
+	0xd0e53580U, 0x84a420d4U, 0x7ee29c2eU, 0x3bf7cc6bU,
+	0xf648bea6U, 0xba17adeaU, 0x29634a79U, 0x38370f68U,
+	0xd4e03484U, 0x4c5e121cU, 0x63b9da33U, 0xc1b17091U,
+	0x6d365b3dU, 0x85e461d5U, 0x2d664b7dU, 0x6c761a3cU,
+	0x98bf27c8U, 0x74681c24U, 0x52c59702U, 0x9f7ae5cfU,
+	0x81e160d1U, 0x9dfa67cdU, 0xe4dc38b4U, 0xf9877ea9U,
+	0x11554441U, 0x91f564c1U, 0xf4c83ca4U, 0x0bcbc05bU,
+	0x46d49216U, 0x15504545U, 0x0c0e025cU, 0x485b1318U,
+	0x6bb3d83bU, 0x5acf950aU, 0x89eb62d9U, 0x5c4a160cU,
+	0x242c0874U, 0xcdbe739dU, 0x1d5a474dU, 0x8a2ba1daU,
+	0x9770e7c7U, 0x06848256U, 0x36b88e66U, 0x6fb6d93fU,
+	0xbe12aceeU, 0xeb13f8bbU, 0x3ff2cd6fU, 0x5eca940eU,
+	0x14100444U, 0x0a8b815aU, 0xf5887da5U, 0xc331f293U,
+	0x9b7fe4cbU, 0xca7bb19aU, 0xde6ab48eU, 0x2ea6887eU,
+	0xf24dbfa2U, 0x90b525c0U, 0x41115011U, 0x9375e6c3U,
+	0x9630a6c6U, 0xaf46e9ffU, 0x60791930U, 0x8764e3d7U,
+	0x7ba7dc2bU, 0xc5b47195U, 0x7d225f2dU, 0x40511110U,
+	0xa349eaf3U, 0x5b8fd40bU, 0xddaa778dU, 0xd5a07585U,
+	0xc271b392U, 0x27eccb77U, 0xd325f683U, 0x9a3fa5caU,
+	0x61395831U, 0xce7eb09eU, 0x9e3aa4ceU, 0xadc66bfdU,
+	0x1fdac54fU, 0x8cae22dcU, 0x3eb28c6eU, 0x94b024c4U,
+	0x16908646U, 0xb1dd6ce1U, 0x99ff66c9U, 0xbb57ecebU,
+	0x21694871U, 0x584f1708U, 0x2c260a7cU, 0xa8832bf8U,
+	0x42d19312U, 0x7ae79d2aU, 0xc9bb7299U, 0x00010150U,
+	0x6ef6983eU, 0xe259bbb2U, 0x2fe6c97fU, 0xa9c36af9U,
+	0x195f4649U, 0xea53b9baU, 0xbc922eecU, 0xdcea368cU,
+	0xa08929f0U, 0xa48c28f4U, 0x78671f28U, 0x50451500U,
+	0x23e9ca73U, 0x79275e29U, 0xab43e8fbU, 0x5f8ad50fU,
+	0xfb07fcabU, 0x62f99b32U, 0xe71cfbb7U, 0xe8d33bb8U,
+	0x51055401U, 0x72ed9f22U, 0x647c1834U, 0x10150540U,
+	0x69335a39U, 0x55005505U, 0x7c621e2cU, 0x76e89e26U,
+	0x3d724f6dU, 0x12958742U, 0xd1a57481U, 0x37f8cf67U,
+	0x8361e2d3U, 0x22a98b72U, 0xf8c73fa8U, 0xe319fab3U,
+	0xa1c968f1U, 0xc4f43094U, 0x56c09606U, 0x5d0a570dU,
+	0xf708ffa7U, 0x8221a3d2U, 0xc734f397U, 0xd720f787U,
+	0x33fdce63U, 0xb09d2de0U, 0x4b9bd01bU, 0x3c320e6cU,
+	0x256c4975U, 0x4f9ed11fU, 0xc8fb3398U, 0xecd63abcU,
+	0x80a121d0U, 0xf30dfea3U, 0xd265b782U, 0x317d4c61U,
+	0x02818352U, 0xac862afcU, 0x0e8e805eU, 0x07c4c357U,
+	0x75285d25U, 0xd660b686U, 0xe0d939b0U, 0xa60caaf6U,
+	0xfd827fadU, 0x66fc9a36U, 0x20290970U, 0x95f065c5U,
+	0xf0cd3da0U, 0xae06a8feU, 0x68731b38U, 0xed967bbdU,
+	0x4adb911aU, 0x3ab78d6aU, 0xf18d7ca1U, 0x34380c64U,
+	0xe201e36dU, 0xfb5fa474U, 0x1ec2dc91U, 0x4289cbcdU,
+	0x8a73f905U, 0x3eead4b1U, 0x4d460bc2U, 0x67e483e8U,
+	0xe1c1206eU, 0x63e182ecU, 0xe9cb2266U, 0xb245f73dU,
+	0x19071e96U, 0x5698ced9U, 0x09131a86U, 0xad9e3322U,
+	0x15081d9aU, 0x247450abU, 0xd37dae5cU, 0x602141efU,
+	0x110d1c9eU, 0x43c98accU, 0xa99b3226U, 0xb640f639U,
+	0x138d9e9cU, 0xa8db7327U, 0xf5d0257aU, 0x213110aeU,
+	0x9ce27e13U, 0xa311b22cU, 0xa191302eU, 0xdf72ad50U,
+	0xcb63a844U, 0xb0c5753fU, 0xf89f6777U, 0x6aabc1e5U,
+	0xff5aa570U, 0x06dcda89U, 0xdb77ac54U, 0x501d4ddfU,
+	0x757005faU, 0xe88b6367U, 0x95a83d1aU, 0xb4c0743bU,
+	0x0ed6d881U, 0x867cfa09U, 0x0b939884U, 0x306555bfU,
+	0x2afbd1a5U, 0x77f087f8U, 0xc9e32a46U, 0x1f829d90U,
+	0x9e62fc11U, 0x99a73e16U, 0x1ac7dd95U, 0xef4ea160U,
+	0xbb0fb434U, 0xc62cea49U, 0xeb4ba064U, 0x529dcfddU,
+	0x6c2e42e3U, 0x873cbb08U, 0x454c09caU, 0x23b192acU,
+	0xa4d4702bU, 0xa594312aU, 0x27b493a8U, 0x4a83c9c5U,
+	0x76b0c6f9U, 0x743044fbU, 0xe5c4216aU, 0x53dd8edcU,
+	0xb700b738U, 0xdcb26e53U, 0x49430ac6U, 0xddf22f52U,
+	0x22f1d3adU, 0xaf1eb120U, 0x84fc780bU, 0x1b879c94U,
+	0x18475f97U, 0x14485c9bU, 0xbf0ab530U, 0x73f586fcU,
+	0x7c3a46f3U, 0x287b53a7U, 0xd23def5dU, 0x9728bf18U,
+	0x5a97cdd5U, 0x16c8de99U, 0x85bc390aU, 0x94e87c1bU,
+	0x783f47f7U, 0xe081616fU, 0xcf66a940U, 0x6d6e03e2U,
+	0xc1e9284eU, 0x293b12a6U, 0x81b9380eU, 0xc0a9694fU,
+	0x346054bbU, 0xd8b76f57U, 0xfe1ae471U, 0x33a596bcU,
+	0x2d3e13a2U, 0x312514beU, 0x48034bc7U, 0x55580ddaU,
+	0xbd8a3732U, 0x3d2a17b2U, 0x58174fd7U, 0xa714b328U,
+	0xea0be165U, 0xb98f3636U, 0xa0d1712fU, 0xe484606bU,
+	0xc76cab48U, 0xf610e679U, 0x253411aaU, 0xf095657fU,
+	0x88f37b07U, 0x616100eeU, 0xb185343eU, 0x26f4d2a9U,
+	0x3baf94b4U, 0xaa5bf125U, 0x9a67fd15U, 0xc369aa4cU,
+	0x12cddf9dU, 0x47cc8bc8U, 0x932dbe1cU, 0xf215e77dU,
+	0xb8cf7737U, 0xa654f229U, 0x59570ed6U, 0x6fee81e0U,
+	0x37a097b8U, 0x66a4c2e9U, 0x72b5c7fdU, 0x8279fb0dU,
+	0x5e92ccd1U, 0x3c6a56b3U, 0xedce2362U, 0x3faa95b0U,
+	0x3aefd5b5U, 0x03999a8cU, 0xcca66a43U, 0x2bbb90a4U,
+	0xd778af58U, 0x696b02e6U, 0xd1fd2c5eU, 0xec8e6263U,
+	0x0f969980U, 0xf750a778U, 0x717504feU, 0x797f06f6U,
+	0x6eaec0e1U, 0x8b33b804U, 0x7ffa85f0U, 0x36e0d6b9U,
+	0xcde62b42U, 0x62a1c3edU, 0x32e5d7bdU, 0x0119188eU,
+	0xb305b63cU, 0x207151afU, 0x926dff1dU, 0x386f57b7U,
+	0xba4ff535U, 0x1d021f92U, 0x352015baU, 0x17889f98U,
+	0x8db63b02U, 0xf490647bU, 0x80f9790fU, 0x045c588bU,
+	0xee0ee061U, 0xd638ee59U, 0x656401eaU, 0xacde7223U,
+	0xc229eb4dU, 0x4e86c8c1U, 0x8339ba0cU, 0x051c198aU,
+	0xb580353aU, 0x468ccac9U, 0x104d5d9fU, 0x703545ffU,
+	0x0c565a83U, 0x08535b87U, 0xd4b86c5bU, 0xfc9a6673U,
+	0x8f36b900U, 0xd5f82d5aU, 0x079c9b88U, 0xf355a67cU,
+	0x57d88fd8U, 0xce26e841U, 0x4bc388c4U, 0x440c48cbU,
+	0xfdda2772U, 0xde32ec51U, 0xc8a36b47U, 0xbcca7633U,
+	0xc5ec294aU, 0xf9df2676U, 0xd0bd6d5fU, 0xda37ed55U,
+	0x91ad3c1eU, 0xbe4af431U, 0x7d7a07f2U, 0x9b27bc14U,
+	0x2fbe91a0U, 0x8e76f801U, 0x54184cdbU, 0x4fc689c0U,
+	0x0d161b82U, 0x682b43e7U, 0xfa1fe575U, 0xf1d5247eU,
+	0x5bd78cd4U, 0x2efed0a1U, 0x6beb80e4U, 0x7bff84f4U,
+	0x9f22bd10U, 0x1c425e93U, 0xe744a368U, 0x90ed7d1fU,
+	0x89b33a06U, 0xe341a26cU, 0x642440ebU, 0x400949cfU,
+	0x2c7e52a3U, 0x5fd28dd0U, 0x7ebac4f1U, 0x9da23f12U,
+	0xae5ef021U, 0x0059598fU, 0xa251f32dU, 0xab1bb024U,
+	0xd9f72e56U, 0x7abfc5f5U, 0x4c064ac3U, 0x0ad3d985U,
+	0x515d0cdeU, 0xca23e945U, 0x8cf67a03U, 0x392f16b6U,
+	0x5c124ed3U, 0x02d9db8dU, 0xc4ac684bU, 0x414908ceU,
+	0xe604e269U, 0x9668fe19U, 0x5d520fd2U, 0x98e77f17U,
+	0x2c95b937U, 0x35cbfe2eU, 0xd05686cbU, 0x8c1d9197U,
+	0x44e7a35fU, 0xf07e8eebU, 0x83d25198U, 0xa970d9b2U,
+	0x2f557a34U, 0xad75d8b6U, 0x275f783cU, 0x7cd1ad67U,
+	0xd79344ccU, 0x980c9483U, 0xc78740dcU, 0x630a6978U,
+	0xdb9c47c0U, 0xeae00af1U, 0x1de9f406U, 0xaeb51bb5U,
+	0xdf9946c4U, 0x8d5dd096U, 0x670f687cU, 0x78d4ac63U,
+	0xdd19c4c6U, 0x664f297dU, 0x3b447f20U, 0xefa54af4U,
+	0x52762449U, 0x6d85e876U, 0x6f056a74U, 0x11e6f70aU,
+	0x05f7f21eU, 0x7e512f65U, 0x360b3d2dU, 0xa43f9bbfU,
+	0x31ceff2aU, 0xc84880d3U, 0x15e3f60eU, 0x9e891785U,
+	0xbbe45fa0U, 0x261f393dU, 0x5b3c6740U, 0x7a542e61U,
+	0xc04282dbU, 0x48e8a053U, 0xc507c2deU, 0xfef10fe5U,
+	0xe46f8bffU, 0xb964dda2U, 0x0777701cU, 0xd116c7caU,
+	0x50f6a64bU, 0x5733644cU, 0xd45387cfU, 0x21dafb3aU,
+	0x759bee6eU, 0x08b8b013U, 0x25dffa3eU, 0x9c099587U,
+	0xa2ba18b9U, 0x49a8e152U, 0x8bd85390U, 0xed25c8f6U,
+	0x6a402a71U, 0x6b006b70U, 0xe920c9f2U, 0x8417939fU,
+	0xb8249ca3U, 0xbaa41ea1U, 0x2b507b30U, 0x9d49d486U,
+	0x7994ed62U, 0x12263409U, 0x87d7509cU, 0x13667508U,
+	0xec6589f7U, 0x618aeb7aU, 0x4a682251U, 0xd513c6ceU,
+	0xd6d305cdU, 0xdadc06c1U, 0x719eef6aU, 0xbd61dca6U,
+	0xb2ae1ca9U, 0xe6ef09fdU, 0x1ca9b507U, 0x59bce542U,
+	0x9403978fU, 0xd85c84c3U, 0x4b286350U, 0x5a7c2641U,
+	0xb6ab1dadU, 0x2e153b35U, 0x01f2f31aU, 0xa3fa59b8U,
+	0x0f7d7214U, 0xe7af48fcU, 0x4f2d6254U, 0x0e3d3315U,
+	0xfaf40ee1U, 0x1623350dU, 0x308ebe2bU, 0xfd31cce6U,
+	0xe3aa49f8U, 0xffb14ee4U, 0x8697119dU, 0x9bcc5780U,
+	0x731e6d68U, 0xf3be4de8U, 0x9683158dU, 0x6980e972U,
+	0x249fbb3fU, 0x771b6c6cU, 0x6e452b75U, 0x2a103a31U,
+	0x09f8f112U, 0x3884bc23U, 0xeba04bf0U, 0x3e013f25U,
+	0x4667215dU, 0xaff55ab4U, 0x7f116e64U, 0xe86088f3U,
+	0xf53bceeeU, 0x64cfab7fU, 0x54f3a74fU, 0x0dfdf016U,
+	0xdc5985c7U, 0x8958d192U, 0x5db9e446U, 0x3c81bd27U,
+	0x765b2d6dU, 0x68c0a873U, 0x97c3548cU, 0xa17adbbaU,
+	0xf934cde2U, 0xa83098b3U, 0xbc219da7U, 0x4ceda157U,
+	0x9006968bU, 0xf2fe0ce9U, 0x235a7938U, 0xf13ecfeaU,
+	0xf47b8fefU, 0xcd0dc0d6U, 0x02323019U, 0xe52fcafeU,
+	0x19ecf502U, 0xa7ff58bcU, 0x1f697604U, 0x221a3839U,
+	0xc102c3daU, 0x39c4fd22U, 0xbfe15ea4U, 0xb7eb5cacU,
+	0xa03a9abbU, 0x45a7e25eU, 0xb16edfaaU, 0xf8748ce3U,
+	0x03727118U, 0xac3599b7U, 0xfc718de7U, 0xcf8d42d4U,
+	0x7d91ec66U, 0xeee50bf5U, 0x5cf9a547U, 0xf6fb0dedU,
+	0x74dbaf6fU, 0xd39645c8U, 0xfbb44fe0U, 0xd91cc5c2U,
+	0x43226158U, 0x3a043e21U, 0x4e6d2355U, 0xcac802d1U,
+	0x209aba3bU, 0x18acb403U, 0xabf05bb0U, 0x624a2879U,
+	0x0cbdb117U, 0x8012929bU, 0x4dade056U, 0xcb8843d0U,
+	0x7b146f60U, 0x88189093U, 0xded907c5U, 0xbea11fa5U,
+	0xc2c200d9U, 0xc6c701ddU, 0x1a2c3601U, 0x320e3c29U,
+	0x41a2e35aU, 0x1b6c7700U, 0xc908c1d2U, 0x3dc1fc26U,
+	0x994cd582U, 0x00b2b21bU, 0x8557d29eU, 0x8a981291U,
+	0x334e7d28U, 0x10a6b60bU, 0x0637311dU, 0x725e2c69U,
+	0x0b787310U, 0x374b7c2cU, 0x1e293705U, 0x14a3b70fU,
+	0x5f396644U, 0x70deae6bU, 0xb3ee5da8U, 0x55b3e64eU,
+	0xe12acbfaU, 0x40e2a25bU, 0x9a8c1681U, 0x8152d39aU,
+	0xc38241d8U, 0xa6bf19bdU, 0x348bbf2fU, 0x3f417e24U,
+	0x9543d68eU, 0xe06a8afbU, 0xa57fdabeU, 0xb56bdeaeU,
+	0x51b6e74aU, 0xd2d604c9U, 0x29d0f932U, 0x5e792745U,
+	0x4727605cU, 0x2dd5f836U, 0xaab01ab1U, 0x8e9d1395U,
+	0xe2ea08f9U, 0x9146d78aU, 0xb02e9eabU, 0x53366548U,
+	0x60caaa7bU, 0xcecd03d5U, 0x6cc5a977U, 0x658fea7eU,
+	0x1763740cU, 0xb42b9fafU, 0x82921099U, 0xc44783dfU,
+	0x9fc95684U, 0x04b7b31fU, 0x42622059U, 0xf7bb4cecU,
+	0x92861489U, 0xcc4d81d7U, 0x0a383211U, 0x8fdd5294U,
+	0x2890b833U, 0x58fca443U, 0x93c65588U, 0x5673254dU,
+	0x48236be5U, 0x517d2cfcU, 0xb4e05419U, 0xe8ab4345U,
+	0x2051718dU, 0x94c85c39U, 0xe764834aU, 0xcdc60b60U,
+	0x4be3a8e6U, 0xc9c30a64U, 0x43e9aaeeU, 0x18677fb5U,
+	0xb325961eU, 0xfcba4651U, 0xa331920eU, 0x07bcbbaaU,
+	0xbf2a9512U, 0x8e56d823U, 0x795f26d4U, 0xca03c967U,
+	0xbb2f9416U, 0xe9eb0244U, 0x03b9baaeU, 0x1c627eb1U,
+	0xb9af1614U, 0x02f9fbafU, 0x5ff2adf2U, 0x8b139826U,
+	0x36c0f69bU, 0x09333aa4U, 0x0bb3b8a6U, 0x755025d8U,
+	0x614120ccU, 0x1ae7fdb7U, 0x52bdefffU, 0xc089496dU,
+	0x55782df8U, 0xacfe5201U, 0x715524dcU, 0xfa3fc557U,
+	0xdf528d72U, 0x42a9ebefU, 0x3f8ab592U, 0x1ee2fcb3U,
+	0xa4f45009U, 0x2c5e7281U, 0xa1b1100cU, 0x9a47dd37U,
+	0x80d9592dU, 0xddd20f70U, 0x63c1a2ceU, 0xb5a01518U,
+	0x34407499U, 0x3385b69eU, 0xb0e5551dU, 0x456c29e8U,
+	0x112d3cbcU, 0x6c0e62c1U, 0x416928ecU, 0xf8bf4755U,
+	0xc60cca6bU, 0x2d1e3380U, 0xef6e8142U, 0x89931a24U,
+	0x0ef6f8a3U, 0x0fb6b9a2U, 0x8d961b20U, 0xe0a1414dU,
+	0xdc924e71U, 0xde12cc73U, 0x4fe6a9e2U, 0xf9ff0654U,
+	0x1d223fb0U, 0x7690e6dbU, 0xe361824eU, 0x77d0a7daU,
+	0x88d35b25U, 0x053c39a8U, 0x2edef083U, 0xb1a5141cU,
+	0xb265d71fU, 0xbe6ad413U, 0x15283db8U, 0xd9d70e74U,
+	0xd618ce7bU, 0x8259db2fU, 0x781f67d5U, 0x3d0a3790U,
+	0xf0b5455dU, 0xbcea5611U, 0x2f9eb182U, 0x3ecaf493U,
+	0xd21dcf7fU, 0x4aa3e9e7U, 0x654421c8U, 0xc74c8b6aU,
+	0x6bcba0c6U, 0x83199a2eU, 0x2b9bb086U, 0x6a8be1c7U,
+	0x9e42dc33U, 0x7295e7dfU, 0x54386cf9U, 0x99871e34U,
+	0x871c9b2aU, 0x9b079c36U, 0xe221c34fU, 0xff7a8552U,
+	0x17a8bfbaU, 0x97089f3aU, 0xf235c75fU, 0x0d363ba0U,
+	0x402969edU, 0x13adbebeU, 0x0af3f9a7U, 0x4ea6e8e3U,
+	0x6d4e23c0U, 0x5c326ef1U, 0x8f169922U, 0x5ab7edf7U,
+	0x22d1f38fU, 0xcb438866U, 0x1ba7bcb6U, 0x8cd65a21U,
+	0x918d1c3cU, 0x007979adU, 0x3045759dU, 0x694b22c4U,
+	0xb8ef5715U, 0xedee0340U, 0x390f3694U, 0x58376ff5U,
+	0x12edffbfU, 0x0c767aa1U, 0xf375865eU, 0xc5cc0968U,
+	0x9d821f30U, 0xcc864a61U, 0xd8974f75U, 0x285b7385U,
+	0xf4b04459U, 0x9648de3bU, 0x47ecabeaU, 0x95881d38U,
+	0x90cd5d3dU, 0xa9bb1204U, 0x6684e2cbU, 0x8199182cU,
+	0x7d5a27d0U, 0xc3498a6eU, 0x7bdfa4d6U, 0x46aceaebU,
+	0xa5b41108U, 0x5d722ff0U, 0xdb578c76U, 0xd35d8e7eU,
+	0xc48c4869U, 0x2111308cU, 0xd5d80d78U, 0x9cc25e31U,
+	0x67c4a3caU, 0xc8834b65U, 0x98c75f35U, 0xab3b9006U,
+	0x19273eb4U, 0x8a53d927U, 0x384f7795U, 0x924ddf3fU,
+	0x106d7dbdU, 0xb720971aU, 0x9f029d32U, 0xbdaa1710U,
+	0x2794b38aU, 0x5eb2ecf3U, 0x2adbf187U, 0xae7ed003U,
+	0x442c68e9U, 0x7c1a66d1U, 0xcf468962U, 0x06fcfaabU,
+	0x680b63c5U, 0xe4a44049U, 0x291b3284U, 0xaf3e9102U,
+	0x1fa2bdb2U, 0xecae4241U, 0xba6fd517U, 0xda17cd77U,
+	0xa674d20bU, 0xa271d30fU, 0x7e9ae4d3U, 0x56b8eefbU,
+	0x25143188U, 0x7fdaa5d2U, 0xadbe1300U, 0x59772ef4U,
+	0xfdfa0750U, 0x640460c9U, 0xe1e1004cU, 0xee2ec043U,
+	0x57f8affaU, 0x741064d9U, 0x6281e3cfU, 0x16e8febbU,
+	0x6fcea1c2U, 0x53fdaefeU, 0x7a9fe5d7U, 0x701565ddU,
+	0x3b8fb496U, 0x14687cb9U, 0xd7588f7aU, 0x3105349cU,
+	0x859c1928U, 0x24547089U, 0xfe3ac453U, 0xe5e40148U,
+	0xa734930aU, 0xc209cb6fU, 0x503d6dfdU, 0x5bf7acf6U,
+	0xf1f5045cU, 0x84dc5829U, 0xc1c9086cU, 0xd1dd0c7cU,
+	0x35003598U, 0xb660d61bU, 0x4d662be0U, 0x3acff597U,
+	0x2391b28eU, 0x49632ae4U, 0xce06c863U, 0xea2bc147U,
+	0x865cda2bU, 0xf5f00558U, 0xd4984c79U, 0x3780b79aU,
+	0x047c78a9U, 0xaa7bd107U, 0x08737ba5U, 0x013938acU,
+	0x73d5a6deU, 0xd09d4d7dU, 0xe624c24bU, 0xa0f1510dU,
+	0xfb7f8456U, 0x600161cdU, 0x26d4f28bU, 0x930d9e3eU,
+	0xf630c65bU, 0xa8fb5305U, 0x6e8ee0c3U, 0xeb6b8046U,
+	0x4c266ae1U, 0x3c4a7691U, 0xf770875aU, 0x32c5f79fU,
+	0x811e9f11U, 0x9840d808U, 0x7ddda0edU, 0x2196b7b1U,
+	0xe96c8579U, 0x5df5a8cdU, 0x2e5977beU, 0x04fbff94U,
+	0x82de5c12U, 0x00fefe90U, 0x8ad45e1aU, 0xd15a8b41U,
+	0x7a1862eaU, 0x3587b2a5U, 0x6a0c66faU, 0xce814f5eU,
+	0x761761e6U, 0x476b2cd7U, 0xb062d220U, 0x033e3d93U,
+	0x721260e2U, 0x20d6f6b0U, 0xca844e5aU, 0xd55f8a45U,
+	0x7092e2e0U, 0xcbc40f5bU, 0x96cf5906U, 0x422e6cd2U,
+	0xfffd026fU, 0xc00ece50U, 0xc28e4c52U, 0xbc6dd12cU,
+	0xa87cd438U, 0xd3da0943U, 0x9b801b0bU, 0x09b4bd99U,
+	0x9c45d90cU, 0x65c3a6f5U, 0xb868d028U, 0x330231a3U,
+	0x166f7986U, 0x8b941f1bU, 0xf6b74166U, 0xd7df0847U,
+	0x6dc9a4fdU, 0xe5638675U, 0x688ce4f8U, 0x537a29c3U,
+	0x49e4add9U, 0x14effb84U, 0xaafc563aU, 0x7c9de1ecU,
+	0xfd7d806dU, 0xfab8426aU, 0x79d8a1e9U, 0x8c51dd1cU,
+	0xd810c848U, 0xa5339635U, 0x8854dc18U, 0x3182b3a1U,
+	0x0f313e9fU, 0xe423c774U, 0x265375b6U, 0x40aeeed0U,
+	0xc7cb0c57U, 0xc68b4d56U, 0x44abefd4U, 0x299cb5b9U,
+	0x15afba85U, 0x172f3887U, 0x86db5d16U, 0x30c2f2a0U,
+	0xd41fcb44U, 0xbfad122fU, 0x2a5c76baU, 0xbeed532eU,
+	0x41eeafd1U, 0xcc01cd5cU, 0xe7e30477U, 0x7898e0e8U,
+	0x7b5823ebU, 0x775720e7U, 0xdc15c94cU, 0x10eafa80U,
+	0x1f253a8fU, 0x4b642fdbU, 0xb1229321U, 0xf437c364U,
+	0x3988b1a9U, 0x75d7a2e5U, 0xe6a34576U, 0xf7f70067U,
+	0x1b203b8bU, 0x839e1d13U, 0xac79d53cU, 0x0e717f9eU,
+	0xa2f65432U, 0x4a246edaU, 0xe2a64472U, 0xa3b61533U,
+	0x577f28c7U, 0xbba8132bU, 0x9d05980dU, 0x50baeac0U,
+	0x4e216fdeU, 0x523a68c2U, 0x2b1c37bbU, 0x364771a6U,
+	0xde954b4eU, 0x5e356bceU, 0x3b0833abU, 0xc40bcf54U,
+	0x89149d19U, 0xda904a4aU, 0xc3ce0d53U, 0x879b1c17U,
+	0xa473d734U, 0x950f9a05U, 0x462b6dd6U, 0x938a1903U,
+	0xebec077bU, 0x027e7c92U, 0xd29a4842U, 0x45ebaed5U,
+	0x58b0e8c8U, 0xc9448d59U, 0xf9788169U, 0xa076d630U,
+	0x71d2a3e1U, 0x24d3f7b4U, 0xf032c260U, 0x910a9b01U,
+	0xdbd00b4bU, 0xc54b8e55U, 0x3a4872aaU, 0x0cf1fd9cU,
+	0x54bfebc4U, 0x05bbbe95U, 0x11aabb81U, 0xe1668771U,
+	0x3d8db0adU, 0x5f752acfU, 0x8ed15f1eU, 0x5cb5e9ccU,
+	0x59f0a9c9U, 0x6086e6f0U, 0xafb9163fU, 0x48a4ecd8U,
+	0xb467d324U, 0x0a747e9aU, 0xb2e25022U, 0x8f911e1fU,
+	0x6c89e5fcU, 0x944fdb04U, 0x126a7882U, 0x1a607a8aU,
+	0x0db1bc9dU, 0xe82cc478U, 0x1ce5f98cU, 0x55ffaac5U,
+	0xaef9573eU, 0x01bebf91U, 0x51faabc1U, 0x620664f2U,
+	0xd01aca40U, 0x436e2dd3U, 0xf1728361U, 0x5b702bcbU,
+	0xd9508949U, 0x7e1d63eeU, 0x563f69c6U, 0x7497e3e4U,
+	0xeea9477eU, 0x978f1807U, 0xe3e60573U, 0x674324f7U,
+	0x8d119c1dU, 0xb5279225U, 0x067b7d96U, 0xcfc10e5fU,
+	0xa1369731U, 0x2d99b4bdU, 0xe026c670U, 0x660365f6U,
+	0xd69f4946U, 0x2593b6b5U, 0x735221e3U, 0x132a3983U,
+	0x6f4926ffU, 0x6b4c27fbU, 0xb7a71027U, 0x9f851a0fU,
+	0xec29c57cU, 0xb6e75126U, 0x6483e7f4U, 0x904ada00U,
+	0x34c7f3a4U, 0xad39943dU, 0x28dcf4b8U, 0x271334b7U,
+	0x9ec55b0eU, 0xbd2d902dU, 0xabbc173bU, 0xdfd50a4fU,
+	0xa6f35536U, 0x9ac05a0aU, 0xb3a21123U, 0xb9289129U,
+	0xf2b24062U, 0xdd55884dU, 0x1e657b8eU, 0xf838c068U,
+	0x4ca1eddcU, 0xed69847dU, 0x370730a7U, 0x2cd9f5bcU,
+	0x6e0967feU, 0x0b343f9bU, 0x99009909U, 0x92ca5802U,
+	0x38c8f0a8U, 0x4de1acddU, 0x08f4fc98U, 0x18e0f888U,
+	0xfc3dc16cU, 0x7f5d22efU, 0x845bdf14U, 0xf3f20163U,
+	0xeaac467aU, 0x805ede10U, 0x073b3c97U, 0x231635b3U,
+	0x4f612edfU, 0x3ccdf1acU, 0x1da5b88dU, 0xfebd436eU,
+	0xcd418c5dU, 0x634625f3U, 0xc14e8f51U, 0xc804cc58U,
+	0xbae8522aU, 0x19a0b989U, 0x2f1936bfU, 0x69cca5f9U,
+	0x324270a2U, 0xa93c9539U, 0xefe9067fU, 0x5a306acaU,
+	0x3f0d32afU, 0x61c6a7f1U, 0xa7b31437U, 0x225674b2U,
+	0x851b9e15U, 0xf5778265U, 0x3e4d73aeU, 0xfbf8036bU,
+	0x0c373bb5U, 0x15697cacU, 0xf0f40449U, 0xacbf1315U,
+	0x644521ddU, 0xd0dc0c69U, 0xa370d31aU, 0x89d25b30U,
+	0x0ff7f8b6U, 0x8dd75a34U, 0x07fdfabeU, 0x5c732fe5U,
+	0xf731c64eU, 0xb8ae1601U, 0xe725c25eU, 0x43a8ebfaU,
+	0xfb3ec542U, 0xca428873U, 0x3d4b7684U, 0x8e179937U,
+	0xff3bc446U, 0xadff5214U, 0x47adeafeU, 0x58762ee1U,
+	0xfdbb4644U, 0x46edabffU, 0x1be6fda2U, 0xcf07c876U,
+	0x72d4a6cbU, 0x4d276af4U, 0x4fa7e8f6U, 0x31447588U,
+	0x2555709cU, 0x5ef3ade7U, 0x16a9bfafU, 0x849d193dU,
+	0x116c7da8U, 0xe8ea0251U, 0x3541748cU, 0xbe2b9507U,
+	0x9b46dd22U, 0x06bdbbbfU, 0x7b9ee5c2U, 0x5af6ace3U,
+	0xe0e00059U, 0x684a22d1U, 0xe5a5405cU, 0xde538d67U,
+	0xc4cd097dU, 0x99c65f20U, 0x27d5f29eU, 0xf1b44548U,
+	0x705424c9U, 0x7791e6ceU, 0xf4f1054dU, 0x017879b8U,
+	0x55396cecU, 0x281a3291U, 0x057d78bcU, 0xbcab1705U,
+	0x82189a3bU, 0x690a63d0U, 0xab7ad112U, 0xcd874a74U,
+	0x4ae2a8f3U, 0x4ba2e9f2U, 0xc9824b70U, 0xa4b5111dU,
+	0x98861e21U, 0x9a069c23U, 0x0bf2f9b2U, 0xbdeb5604U,
+	0x59366fe0U, 0x3284b68bU, 0xa775d21eU, 0x33c4f78aU,
+	0xccc70b75U, 0x412869f8U, 0x6acaa0d3U, 0xf5b1444cU,
+	0xf671874fU, 0xfa7e8443U, 0x513c6de8U, 0x9dc35e24U,
+	0x920c9e2bU, 0xc64d8b7fU, 0x3c0b3785U, 0x791e67c0U,
+	0xb4a1150dU, 0xf8fe0641U, 0x6b8ae1d2U, 0x7adea4c3U,
+	0x96099f2fU, 0x0eb7b9b7U, 0x21507198U, 0x8358db3aU,
+	0x2fdff096U, 0xc70dca7eU, 0x6f8fe0d6U, 0x2e9fb197U,
+	0xda568c63U, 0x3681b78fU, 0x102c3ca9U, 0xdd934e64U,
+	0xc308cb7aU, 0xdf13cc66U, 0xa635931fU, 0xbb6ed502U,
+	0x53bcefeaU, 0xd31ccf6aU, 0xb621970fU, 0x49226bf0U,
+	0x043d39bdU, 0x57b9eeeeU, 0x4ee7a9f7U, 0x0ab2b8b3U,
+	0x295a7390U, 0x18263ea1U, 0xcb02c972U, 0x1ea3bda7U,
+	0x66c5a3dfU, 0x8f57d836U, 0x5fb3ece6U, 0xc8c20a71U,
+	0xd5994c6cU, 0x446d29fdU, 0x745125cdU, 0x2d5f7294U,
+	0xfcfb0745U, 0xa9fa5310U, 0x7d1b66c4U, 0x1c233fa5U,
+	0x56f9afefU, 0x48622af1U, 0xb761d60eU, 0x81d85938U,
+	0xd9964f60U, 0x88921a31U, 0x9c831f25U, 0x6c4f23d5U,
+	0xb0a41409U, 0xd25c8e6bU, 0x03f8fbbaU, 0xd19c4d68U,
+	0xd4d90d6dU, 0xedaf4254U, 0x2290b29bU, 0xc58d487cU,
+	0x394e7780U, 0x875dda3eU, 0x3fcbf486U, 0x02b8babbU,
+	0xe1a04158U, 0x19667fa0U, 0x9f43dc26U, 0x9749de2eU,
+	0x80981839U, 0x650560dcU, 0x91cc5d28U, 0xd8d60e61U,
+	0x23d0f39aU, 0x8c971b35U, 0xdcd30f65U, 0xef2fc056U,
+	0x5d336ee4U, 0xce478977U, 0x7c5b27c5U, 0xd6598f6fU,
+	0x54792dedU, 0xf334c74aU, 0xdb16cd62U, 0xf9be4740U,
+	0x6380e3daU, 0x1aa6bca3U, 0x6ecfa1d7U, 0xea6a8053U,
+	0x003838b9U, 0x380e3681U, 0x8b52d932U, 0x42e8aafbU,
+	0x2c1f3395U, 0xa0b01019U, 0x6d0f62d4U, 0xeb2ac152U,
+	0x5bb6ede2U, 0xa8ba1211U, 0xfe7b8547U, 0x9e039d27U,
+	0xe260825bU, 0xe665835fU, 0x3a8eb483U, 0x12acbeabU,
+	0x610061d8U, 0x3bcef582U, 0xe9aa4350U, 0x1d637ea4U,
+	0xb9ee5700U, 0x20103099U, 0xa5f5501cU, 0xaa3a9013U,
+	0x13ecffaaU, 0x30043489U, 0x2695b39fU, 0x52fcaeebU,
+	0x2bdaf192U, 0x17e9feaeU, 0x3e8bb587U, 0x3401358dU,
+	0x7f9be4c6U, 0x507c2ce9U, 0x934cdf2aU, 0x751164ccU,
+	0xc1884978U, 0x604020d9U, 0xba2e9403U, 0xa1f05118U,
+	0xe320c35aU, 0x861d9b3fU, 0x14293dadU, 0x1fe3fca6U,
+	0xb5e1540cU, 0xc0c80879U, 0x85dd583cU, 0x95c95c2cU,
+	0x711465c8U, 0xf274864bU, 0x09727bb0U, 0x7edba5c7U,
+	0x6785e2deU, 0x0d777ab4U, 0x8a129833U, 0xae3f9117U,
+	0xc2488a7bU, 0xb1e45508U, 0x908c1c29U, 0x7394e7caU,
+	0x406828f9U, 0xee6f8157U, 0x4c672bf5U, 0x452d68fcU,
+	0x37c1f68eU, 0x94891d2dU, 0xa230921bU, 0xe4e5015dU,
+	0xbf6bd406U, 0x2415319dU, 0x62c0a2dbU, 0xd719ce6eU,
+	0xb224960bU, 0xecef0355U, 0x2a9ab093U, 0xaf7fd016U,
+	0x08323ab1U, 0x785e26c1U, 0xb364d70aU, 0x76d1a7cfU,
+	0xf250a22cU, 0xeb0ee535U, 0x0e939dd0U, 0x52d88a8cU,
+	0x9a22b844U, 0x2ebb95f0U, 0x5d174a83U, 0x77b5c2a9U,
+	0xf190612fU, 0x73b0c3adU, 0xf99a6327U, 0xa214b67cU,
+	0x09565fd7U, 0x46c98f98U, 0x19425bc7U, 0xbdcf7263U,
+	0x05595cdbU, 0x342511eaU, 0xc32cef1dU, 0x707000aeU,
+	0x015c5ddfU, 0x5398cb8dU, 0xb9ca7367U, 0xa611b778U,
+	0x03dcdfddU, 0xb88a3266U, 0xe581643bU, 0x316051efU,
+	0x8cb33f52U, 0xb340f36dU, 0xb1c0716fU, 0xcf23ec11U,
+	0xdb32e905U, 0xa094347eU, 0xe8ce2636U, 0x7afa80a4U,
+	0xef0be431U, 0x168d9bc8U, 0xcb26ed15U, 0x404c0c9eU,
+	0x652144bbU, 0xf8da2226U, 0x85f97c5bU, 0xa491357aU,
+	0x1e8799c0U, 0x962dbb48U, 0x1bc2d9c5U, 0x203414feU,
+	0x3aaa90e4U, 0x67a1c6b9U, 0xd9b26b07U, 0x0fd3dcd1U,
+	0x8e33bd50U, 0x89f67f57U, 0x0a969cd4U, 0xff1fe021U,
+	0xab5ef575U, 0xd67dab08U, 0xfb1ae125U, 0x42cc8e9cU,
+	0x7c7f03a2U, 0x976dfa49U, 0x551d488bU, 0x33e0d3edU,
+	0xb485316aU, 0xb5c5706bU, 0x37e5d2e9U, 0x5ad28884U,
+	0x66e187b8U, 0x646105baU, 0xf595602bU, 0x438ccf9dU,
+	0xa751f679U, 0xcce32f12U, 0x59124b87U, 0xcda36e13U,
+	0x32a092ecU, 0xbf4ff061U, 0x94ad394aU, 0x0bd6ddd5U,
+	0x08161ed6U, 0x04191ddaU, 0xaf5bf471U, 0x63a4c7bdU,
+	0x6c6b07b2U, 0x382a12e6U, 0xc26cae1cU, 0x8779fe59U,
+	0x4ac68c94U, 0x06999fd8U, 0x95ed784bU, 0x84b93d5aU,
+	0x686e06b6U, 0xf0d0202eU, 0xdf37e801U, 0x7d3f42a3U,
+	0xd1b8690fU, 0x396a53e7U, 0x91e8794fU, 0xd0f8280eU,
+	0x243115faU, 0xc8e62e16U, 0xee4ba530U, 0x23f4d7fdU,
+	0x3d6f52e3U, 0x217455ffU, 0x58520a86U, 0x45094c9bU,
+	0xaddb7673U, 0x2d7b56f3U, 0x48460e96U, 0xb745f269U,
+	0xfa5aa024U, 0xa9de7777U, 0xb080306eU, 0xf4d5212aU,
+	0xd73dea09U, 0xe641a738U, 0x356550ebU, 0xe0c4243eU,
+	0x98a23a46U, 0x713041afU, 0xa1d4757fU, 0x36a593e8U,
+	0x2bfed5f5U, 0xba0ab064U, 0x8a36bc54U, 0xd338eb0dU,
+	0x029c9edcU, 0x579dca89U, 0x837cff5dU, 0xe244a63cU,
+	0xa89e3676U, 0xb605b368U, 0x49064f97U, 0x7fbfc0a1U,
+	0x27f1d6f9U, 0x76f583a8U, 0x62e486bcU, 0x9228ba4cU,
+	0x4ec38d90U, 0x2c3b17f2U, 0xfd9f6223U, 0x2ffbd4f1U,
+	0x2abe94f4U, 0x13c8dbcdU, 0xdcf72b02U, 0x3bead1e5U,
+	0xc729ee19U, 0x793a43a7U, 0xc1ac6d1fU, 0xfcdf2322U,
+	0x1fc7d8c1U, 0xe701e639U, 0x612445bfU, 0x692e47b7U,
+	0x7eff81a0U, 0x9b62f945U, 0x6fabc4b1U, 0x26b197f8U,
+	0xddb76a03U, 0x72f082acU, 0x22b496fcU, 0x114859cfU,
+	0xa354f77dU, 0x302010eeU, 0x823cbe5cU, 0x283e16f6U,
+	0xaa1eb474U, 0x0d535ed3U, 0x257154fbU, 0x07d9ded9U,
+	0x9de77a43U, 0xe4c1253aU, 0x90a8384eU, 0x140d19caU,
+	0xfe5fa120U, 0xc669af18U, 0x753540abU, 0xbc8f3362U,
+	0xd278aa0cU, 0x5ed78980U, 0x9368fb4dU, 0x154d58cbU,
+	0xa5d1747bU, 0x56dd8b88U, 0x001c1cdeU, 0x606404beU,
+	0x1c071bc2U, 0x18021ac6U, 0xc4e92d1aU, 0xeccb2732U,
+	0x9f67f841U, 0xc5a96c1bU, 0x17cddac9U, 0xe304e73dU,
+	0x4789ce99U, 0xde77a900U, 0x5b92c985U, 0x545d098aU,
+	0xed8b6633U, 0xce63ad10U, 0xd8f22a06U, 0xac9b3772U,
+	0xd5bd680bU, 0xe98e6737U, 0xc0ec2c1eU, 0xca66ac14U,
+	0x81fc7d5fU, 0xae1bb570U, 0x6d2b46b3U, 0x8b76fd55U,
+	0x3fefd0e1U, 0x9e27b940U, 0x44490d9aU, 0x5f97c881U,
+	0x1d475ac3U, 0x787a02a6U, 0xea4ea434U, 0xe184653fU,
+	0x4b86cd95U, 0x3eaf91e0U, 0x7bbac1a5U, 0x6baec5b5U,
+	0x8f73fc51U, 0x0c131fd2U, 0xf715e229U, 0x80bc3c5eU,
+	0x99e27b47U, 0xf310e32dU, 0x747501aaU, 0x5058088eU,
+	0x3c2f13e2U, 0x4f83cc91U, 0x6eeb85b0U, 0x8df37e53U,
+	0xbe0fb160U, 0x100818ceU, 0xb200b26cU, 0xbb4af165U,
+	0xc9a66f17U, 0x6aee84b4U, 0x5c570b82U, 0x1a8298c4U,
+	0x410c4d9fU, 0xda72a804U, 0x9ca73b42U, 0x297e57f7U,
+	0x4c430f92U, 0x12889accU, 0xd4fd290aU, 0x5118498fU,
+	0xf655a328U, 0x8639bf58U, 0x4d034e93U, 0x88b63e56U,
+	0x173027a9U, 0x0e6e60b0U, 0xebf31855U, 0xb7b80f09U,
+	0x7f423dc1U, 0xcbdb1075U, 0xb877cf06U, 0x92d5472cU,
+	0x14f0e4aaU, 0x96d04628U, 0x1cfae6a2U, 0x477433f9U,
+	0xec36da52U, 0xa3a90a1dU, 0xfc22de42U, 0x58aff7e6U,
+	0xe039d95eU, 0xd145946fU, 0x264c6a98U, 0x9510852bU,
+	0xe43cd85aU, 0xb6f84e08U, 0x5caaf6e2U, 0x437132fdU,
+	0xe6bc5a58U, 0x5deab7e3U, 0x00e1e1beU, 0xd400d46aU,
+	0x69d3bad7U, 0x562076e8U, 0x54a0f4eaU, 0x2a436994U,
+	0x3e526c80U, 0x45f4b1fbU, 0x0daea3b3U, 0x9f9a0521U,
+	0x0a6b61b4U, 0xf3ed1e4dU, 0x2e466890U, 0xa52c891bU,
+	0x8041c13eU, 0x1dbaa7a3U, 0x6099f9deU, 0x41f1b0ffU,
+	0xfbe71c45U, 0x734d3ecdU, 0xfea25c40U, 0xc554917bU,
+	0xdfca1561U, 0x82c1433cU, 0x3cd2ee82U, 0xeab35954U,
+	0x6b5338d5U, 0x6c96fad2U, 0xeff61951U, 0x1a7f65a4U,
+	0x4e3e70f0U, 0x331d2e8dU, 0x1e7a64a0U, 0xa7ac0b19U,
+	0x991f8627U, 0x720d7fccU, 0xb07dcd0eU, 0xd6805668U,
+	0x51e5b4efU, 0x50a5f5eeU, 0xd285576cU, 0xbfb20d01U,
+	0x8381023dU, 0x8101803fU, 0x10f5e5aeU, 0xa6ec4a18U,
+	0x423173fcU, 0x2983aa97U, 0xbc72ce02U, 0x28c3eb96U,
+	0xd7c01769U, 0x5a2f75e4U, 0x71cdbccfU, 0xeeb65850U,
+	0xed769b53U, 0xe179985fU, 0x4a3b71f4U, 0x86c44238U,
+	0x890b8237U, 0xdd4a9763U, 0x270c2b99U, 0x62197bdcU,
+	0xafa60911U, 0xe3f91a5dU, 0x708dfdceU, 0x61d9b8dfU,
+	0x8d0e8333U, 0x15b0a5abU, 0x3a576d84U, 0x985fc726U,
+	0x34d8ec8aU, 0xdc0ad662U, 0x7488fccaU, 0x3598ad8bU,
+	0xc151907fU, 0x2d86ab93U, 0x0b2b20b5U, 0xc6945278U,
+	0xd80fd766U, 0xc414d07aU, 0xbd328f03U, 0xa069c91eU,
+	0x48bbf3f6U, 0xc81bd376U, 0xad268b13U, 0x522577ecU,
+	0x1f3a25a1U, 0x4cbef2f2U, 0x55e0b5ebU, 0x11b5a4afU,
+	0x325d6f8cU, 0x032122bdU, 0xd005d56eU, 0x05a4a1bbU,
+	0x7dc2bfc3U, 0x9450c42aU, 0x44b4f0faU, 0xd3c5166dU,
+	0xce9e5070U, 0x5f6a35e1U, 0x6f5639d1U, 0x36586e88U,
+	0xe7fc1b59U, 0xb2fd4f0cU, 0x661c7ad8U, 0x072423b9U,
+	0x4dfeb3f3U, 0x536536edU, 0xac66ca12U, 0x9adf4524U,
+	0xc291537cU, 0x9395062dU, 0x87840339U, 0x77483fc9U,
+	0xaba30815U, 0xc95b9277U, 0x18ffe7a6U, 0xca9b5174U,
+	0xcfde1171U, 0xf6a85e48U, 0x3997ae87U, 0xde8a5460U,
+	0x22496b9cU, 0x9c5ac622U, 0x24cce89aU, 0x19bfa6a7U,
+	0xfaa75d44U, 0x026163bcU, 0x8444c03aU, 0x8c4ec232U,
+	0x9b9f0425U, 0x7e027cc0U, 0x8acb4134U, 0xc3d1127dU,
+	0x38d7ef86U, 0x97900729U, 0xc7d41379U, 0xf428dc4aU,
+	0x463472f8U, 0xd540956bU, 0x675c3bd9U, 0xcd5e9373U,
+	0x4f7e31f1U, 0xe833db56U, 0xc011d17eU, 0xe2b95b5cU,
+	0x7887ffc6U, 0x01a1a0bfU, 0x75c8bdcbU, 0xf16d9c4fU,
+	0x1b3f24a5U, 0x23092a9dU, 0x9055c52eU, 0x59efb6e7U,
+	0x37182f89U, 0xbbb70c05U, 0x76087ec8U, 0xf02ddd4eU,
+	0x40b1f1feU, 0xb3bd0e0dU, 0xe57c995bU, 0x8504813bU,
+	0xf9679e47U, 0xfd629f43U, 0x2189a89fU, 0x09aba2b7U,
+	0x7a077dc4U, 0x20c9e99eU, 0xf2ad5f4cU, 0x066462b8U,
+	0xa2e94b1cU, 0x3b172c85U, 0xbef24c00U, 0xb13d8c0fU,
+	0x08ebe3b6U, 0x2b032895U, 0x3d92af83U, 0x49fbb2f7U,
+	0x30dded8eU, 0x0ceee2b2U, 0x258ca99bU, 0x2f062991U,
+	0x649cf8daU, 0x4b7b30f5U, 0x884bc336U, 0x6e1678d0U,
+	0xda8f5564U, 0x7b473cc5U, 0xa129881fU, 0xbaf74d04U,
+	0xf827df46U, 0x9d1a8723U, 0x0f2e21b1U, 0x04e4e0baU,
+	0xaee64810U, 0xdbcf1465U, 0x9eda4420U, 0x8ece4030U,
+	0x6a1379d4U, 0xe9739a57U, 0x127567acU, 0x65dcb9dbU,
+	0x7c82fec2U, 0x167066a8U, 0x9115842fU, 0xb5388d0bU,
+	0xd94f9667U, 0xaae34914U, 0x8b8b0035U, 0x6893fbd6U,
+	0x5b6f34e5U, 0xf5689d4bU, 0x576037e9U, 0x5e2a74e0U,
+	0x2cc6ea92U, 0x8f8e0131U, 0xb9378e07U, 0xffe21d41U,
+	0xa46cc81aU, 0x3f122d81U, 0x79c7bec7U, 0xcc1ed272U,
+	0xa9238a17U, 0xf7e81f49U, 0x319dac8fU, 0xb478cc0aU,
+	0x133526adU, 0x63593addU, 0xa863cb16U, 0x6dd6bbd3U,
+	0xd8f028a6U, 0xc1ae6fbfU, 0x2433175aU, 0x78780006U,
+	0xb08232ceU, 0x041b1f7aU, 0x77b7c009U, 0x5d154823U,
+	0xdb30eba5U, 0x59104927U, 0xd33ae9adU, 0x88b43cf6U,
+	0x23f6d55dU, 0x6c690512U, 0x33e2d14dU, 0x976ff8e9U,
+	0x2ff9d651U, 0x1e859b60U, 0xe98c6597U, 0x5ad08a24U,
+	0x2bfcd755U, 0x79384107U, 0x936af9edU, 0x8cb13df2U,
+	0x297c5557U, 0x922ab8ecU, 0xcf21eeb1U, 0x1bc0db65U,
+	0xa613b5d8U, 0x99e079e7U, 0x9b60fbe5U, 0xe583669bU,
+	0xf192638fU, 0x8a34bef4U, 0xc26eacbcU, 0x505a0a2eU,
+	0xc5ab6ebbU, 0x3c2d1142U, 0xe186679fU, 0x6aec8614U,
+	0x4f81ce31U, 0xd27aa8acU, 0xaf59f6d1U, 0x8e31bff0U,
+	0x3427134aU, 0xbc8d31c2U, 0x3162534fU, 0x0a949e74U,
+	0x100a1a6eU, 0x4d014c33U, 0xf312e18dU, 0x2573565bU,
+	0xa49337daU, 0xa356f5ddU, 0x2036165eU, 0xd5bf6aabU,
+	0x81fe7fffU, 0xfcdd2182U, 0xd1ba6bafU, 0x686c0416U,
+	0x56df8928U, 0xbdcd70c3U, 0x7fbdc201U, 0x19405967U,
+	0x9e25bbe0U, 0x9f65fae1U, 0x1d455863U, 0x7072020eU,
+	0x4c410d32U, 0x4ec18f30U, 0xdf35eaa1U, 0x692c4517U,
+	0x8df17cf3U, 0xe643a598U, 0x73b2c10dU, 0xe703e499U,
+	0x18001866U, 0x95ef7aebU, 0xbe0db3c0U, 0x2176575fU,
+	0x22b6945cU, 0x2eb99750U, 0x85fb7efbU, 0x49044d37U,
+	0x46cb8d38U, 0x128a986cU, 0xe8cc2496U, 0xadd974d3U,
+	0x6066061eU, 0x2c391552U, 0xbf4df2c1U, 0xae19b7d0U,
+	0x42ce8c3cU, 0xda70aaa4U, 0xf597628bU, 0x579fc829U,
+	0xfb18e385U, 0x13cad96dU, 0xbb48f3c5U, 0xfa58a284U,
+	0x0e919f70U, 0xe246a49cU, 0xc4eb2fbaU, 0x09545d77U,
+	0x17cfd869U, 0x0bd4df75U, 0x72f2800cU, 0x6fa9c611U,
+	0x877bfcf9U, 0x07dbdc79U, 0x62e6841cU, 0x9de578e3U,
+	0xd0fa2aaeU, 0x837efdfdU, 0x9a20bae4U, 0xde75aba0U,
+	0xfd9d6083U, 0xcce12db2U, 0x1fc5da61U, 0xca64aeb4U,
+	0xb202b0ccU, 0x5b90cb25U, 0x8b74fff5U, 0x1c051962U,
+	0x015e5f7fU, 0x90aa3aeeU, 0xa09636deU, 0xf9986187U,
+	0x283c1456U, 0x7d3d4003U, 0xa9dc75d7U, 0xc8e42cb6U,
+	0x823ebcfcU, 0x9ca539e2U, 0x63a6c51dU, 0x551f4a2bU,
+	0x0d515c73U, 0x5c550922U, 0x48440c36U, 0xb88830c6U,
+	0x6463071aU, 0x069b9d78U, 0xd73fe8a9U, 0x055b5e7bU,
+	0x001e1e7eU, 0x39685147U, 0xf657a188U, 0x114a5b6fU,
+	0xed896493U, 0x539ac92dU, 0xeb0ce795U, 0xd67fa9a8U,
+	0x3567524bU, 0xcda16cb3U, 0x4b84cf35U, 0x438ecd3dU,
+	0x545f0b2aU, 0xb1c273cfU, 0x450b4e3bU, 0x0c111d72U,
+	0xf717e089U, 0x58500826U, 0x08141c76U, 0x3be8d345U,
+	0x89f47df7U, 0x1a809a64U, 0xa89c34d6U, 0x029e9c7cU,
+	0x80be3efeU, 0x27f3d459U, 0x0fd1de71U, 0x2d795453U,
+	0xb747f0c9U, 0xce61afb0U, 0xba08b2c4U, 0x3ead9340U,
+	0xd4ff2baaU, 0xecc92592U, 0x5f95ca21U, 0x962fb9e8U,
+	0xf8d82086U, 0x7477030aU, 0xb9c871c7U, 0x3fedd241U,
+	0x8f71fef1U, 0x7c7d0102U, 0x2abc9654U, 0x4ac48e34U,
+	0x36a79148U, 0x32a2904cU, 0xee49a790U, 0xc66badb8U,
+	0xb5c772cbU, 0xef09e691U, 0x3d6d5043U, 0xc9a46db7U,
+	0x6d294413U, 0xf4d7238aU, 0x7132430fU, 0x7efd8300U,
+	0xc72becb9U, 0xe4c3279aU, 0xf252a08cU, 0x863bbdf8U,
+	0xff1de281U, 0xc32eedbdU, 0xea4ca694U, 0xe0c6269eU,
+	0xab5cf7d5U, 0x84bb3ffaU, 0x478bcc39U, 0xa1d677dfU,
+	0x154f5a6bU, 0xb48733caU, 0x6ee98710U, 0x7537420bU,
+	0x37e7d049U, 0x52da882cU, 0xc0ee2ebeU, 0xcb24efb5U,
+	0x6126471fU, 0x140f1b6aU, 0x511a4b2fU, 0x410e4f3fU,
+	0xa5d376dbU, 0x26b39558U, 0xddb568a3U, 0xaa1cb6d4U,
+	0xb342f1cdU, 0xd9b069a7U, 0x5ed58b20U, 0x7af88204U,
+	0x168f9968U, 0x6523461bU, 0x444b0f3aU, 0xa753f4d9U,
+	0x94af3beaU, 0x3aa89244U, 0x98a038e6U, 0x91ea7befU,
+	0xe306e59dU, 0x404e0e3eU, 0x76f78108U, 0x3022124eU,
+	0x6bacc715U, 0xf0d2228eU, 0xb607b1c8U, 0x03dedd7dU,
+	0x66e38518U, 0x38281046U, 0xfe5da380U, 0x7bb8c305U,
+	0xdcf529a2U, 0xac9935d2U, 0x67a3c419U, 0xa216b4dcU,
+	0x0e9f911fU, 0x17c1d606U, 0xf25caee3U, 0xae17b9bfU,
+	0x66ed8b77U, 0xd274a6c3U, 0xa1d879b0U, 0x8b7af19aU,
+	0x0d5f521cU, 0x8f7ff09eU, 0x05555014U, 0x5edb854fU,
+	0xf5996ce4U, 0xba06bcabU, 0xe58d68f4U, 0x41004150U,
+	0xf9966fe8U, 0xc8ea22d9U, 0x3fe3dc2eU, 0x8cbf339dU,
+	0xfd936eecU, 0xaf57f8beU, 0x45054054U, 0x5ade844bU,
+	0xff13eceeU, 0x44450155U, 0x194e5708U, 0xcdaf62dcU,
+	0x707c0c61U, 0x4f8fc05eU, 0x4d0f425cU, 0x33ecdf22U,
+	0x27fdda36U, 0x5c5b074dU, 0x14011505U, 0x8635b397U,
+	0x13c4d702U, 0xea42a8fbU, 0x37e9de26U, 0xbc833fadU,
+	0x99ee7788U, 0x04151115U, 0x79364f68U, 0x585e0649U,
+	0xe248aaf3U, 0x6ae2887bU, 0xe70deaf6U, 0xdcfb27cdU,
+	0xc665a3d7U, 0x9b6ef58aU, 0x257d5834U, 0xf31cefe2U,
+	0x72fc8e63U, 0x75394c64U, 0xf659afe7U, 0x03d0d312U,
+	0x5791c646U, 0x2ab2983bU, 0x07d5d216U, 0xbe03bdafU,
+	0x80b03091U, 0x6ba2c97aU, 0xa9d27bb8U, 0xcf2fe0deU,
+	0x484a0259U, 0x490a4358U, 0xcb2ae1daU, 0xa61dbbb7U,
+	0x9a2eb48bU, 0x98ae3689U, 0x095a5318U, 0xbf43fcaeU,
+	0x5b9ec54aU, 0x302c1c21U, 0xa5dd78b4U, 0x316c5d20U,
+	0xce6fa1dfU, 0x4380c352U, 0x68620a79U, 0xf719eee6U,
+	0xf4d92de5U, 0xf8d62ee9U, 0x5394c742U, 0x9f6bf48eU,
+	0x90a43481U, 0xc4e521d5U, 0x3ea39d2fU, 0x7bb6cd6aU,
+	0xb609bfa7U, 0xfa56acebU, 0x69224b78U, 0x78760e69U,
+	0x94a13585U, 0x0c1f131dU, 0x23f8db32U, 0x81f07190U,
+	0x2d775a3cU, 0xc5a560d4U, 0x6d274a7cU, 0x2c371b3dU,
+	0xd8fe26c9U, 0x34291d25U, 0x12849603U, 0xdf3be4ceU,
+	0xc1a061d0U, 0xddbb66ccU, 0xa49d39b5U, 0xb9c67fa8U,
+	0x51144540U, 0xd1b465c0U, 0xb4893da5U, 0x4b8ac15aU,
+	0x06959317U, 0x55114444U, 0x4c4f035dU, 0x081a1219U,
+	0x2bf2d93aU, 0x1a8e940bU, 0xc9aa63d8U, 0x1c0b170dU,
+	0x646d0975U, 0x8dff729cU, 0x5d1b464cU, 0xca6aa0dbU,
+	0xd731e6c6U, 0x46c58357U, 0x76f98f67U, 0x2ff7d83eU,
+	0xfe53adefU, 0xab52f9baU, 0x7fb3cc6eU, 0x1e8b950fU,
+	0x54510545U, 0x4aca805bU, 0xb5c97ca4U, 0x8370f392U,
+	0xdb3ee5caU, 0x8a3ab09bU, 0x9e2bb58fU, 0x6ee7897fU,
+	0xb20cbea3U, 0xd0f424c1U, 0x01505110U, 0xd334e7c2U,
+	0xd671a7c7U, 0xef07e8feU, 0x20381831U, 0xc725e2d6U,
+	0x3be6dd2aU, 0x85f57094U, 0x3d635e2cU, 0x00101011U,
+	0xe308ebf2U, 0x1bced50aU, 0x9deb768cU, 0x95e17484U,
+	0x8230b293U, 0x67adca76U, 0x9364f782U, 0xda7ea4cbU,
+	0x21785930U, 0x8e3fb19fU, 0xde7ba5cfU, 0xed876afcU,
+	0x5f9bc44eU, 0xccef23ddU, 0x7ef38d6fU, 0xd4f125c5U,
+	0x56d18747U, 0xf19c6de0U, 0xd9be67c8U, 0xfb16edeaU,
+	0x61284970U, 0x180e1609U, 0x6c670b7dU, 0xe8c22af9U,
+	0x02909213U, 0x3aa69c2bU, 0x89fa7398U, 0x40400051U,
+	0x2eb7993fU, 0xa218bab3U, 0x6fa7c87eU, 0xe9826bf8U,
+	0x591e4748U, 0xaa12b8bbU, 0xfcd32fedU, 0x9cab378dU,
+	0xe0c828f1U, 0xe4cd29f5U, 0x38261e29U, 0x10041401U,
+	0x63a8cb72U, 0x39665f28U, 0xeb02e9faU, 0x1fcbd40eU,
+	0xbb46fdaaU, 0x22b89a33U, 0xa75dfab6U, 0xa8923ab9U,
+	0x11445500U, 0x32ac9e23U, 0x243d1935U, 0x50540441U,
+	0x29725b38U, 0x15415404U, 0x3c231f2dU, 0x36a99f27U,
+	0x7d334e6cU, 0x52d48643U, 0x91e47580U, 0x77b9ce66U,
+	0xc320e3d2U, 0x62e88a73U, 0xb8863ea9U, 0xa358fbb2U,
+	0xe18869f0U, 0x84b53195U, 0x16819707U, 0x1d4b560cU,
+	0xb749fea6U, 0xc260a2d3U, 0x8775f296U, 0x9761f686U,
+	0x73bccf62U, 0xf0dc2ce1U, 0x0bdad11aU, 0x7c730f6dU,
+	0x652d4874U, 0x0fdfd01eU, 0x88ba3299U, 0xac973bbdU,
+	0xc0e020d1U, 0xb34cffa2U, 0x9224b683U, 0x713c4d60U,
+	0x42c08253U, 0xecc72bfdU, 0x4ecf815fU, 0x4785c256U,
+	0x35695c24U, 0x9621b787U, 0xa09838b1U, 0xe64dabf7U,
+	0xbdc37eacU, 0x26bd9b37U, 0x60680871U, 0xd5b164c4U,
+	0xb08c3ca1U, 0xee47a9ffU, 0x28321a39U, 0xadd77abcU,
+	0x0a9a901bU, 0x7af68c6bU, 0xb1cc7da0U, 0x74790d65U,
+	0xe654b23cU, 0xff0af525U, 0x1a978dc0U, 0x46dc9a9cU,
+	0x8e26a854U, 0x3abf85e0U, 0x49135a93U, 0x63b1d2b9U,
+	0xe594713fU, 0x67b4d3bdU, 0xed9e7337U, 0xb610a66cU,
+	0x1d524fc7U, 0x52cd9f88U, 0x0d464bd7U, 0xa9cb6273U,
+	0x115d4ccbU, 0x202101faU, 0xd728ff0dU, 0x647410beU,
+	0x15584dcfU, 0x479cdb9dU, 0xadce6377U, 0xb215a768U,
+	0x17d8cfcdU, 0xac8e2276U, 0xf185742bU, 0x256441ffU,
+	0x98b72f42U, 0xa744e37dU, 0xa5c4617fU, 0xdb27fc01U,
+	0xcf36f915U, 0xb490246eU, 0xfcca3626U, 0x6efe90b4U,
+	0xfb0ff421U, 0x02898bd8U, 0xdf22fd05U, 0x54481c8eU,
+	0x712554abU, 0xecde3236U, 0x91fd6c4bU, 0xb095256aU,
+	0x0a8389d0U, 0x8229ab58U, 0x0fc6c9d5U, 0x343004eeU,
+	0x2eae80f4U, 0x73a5d6a9U, 0xcdb67b17U, 0x1bd7ccc1U,
+	0x9a37ad40U, 0x9df26f47U, 0x1e928cc4U, 0xeb1bf031U,
+	0xbf5ae565U, 0xc279bb18U, 0xef1ef135U, 0x56c89e8cU,
+	0x687b13b2U, 0x8369ea59U, 0x4119589bU, 0x27e4c3fdU,
+	0xa081217aU, 0xa1c1607bU, 0x23e1c2f9U, 0x4ed69894U,
+	0x72e597a8U, 0x706515aaU, 0xe191703bU, 0x5788df8dU,
+	0xb355e669U, 0xd8e73f02U, 0x4d165b97U, 0xd9a77e03U,
+	0x26a482fcU, 0xab4be071U, 0x80a9295aU, 0x1fd2cdc5U,
+	0x1c120ec6U, 0x101d0dcaU, 0xbb5fe461U, 0x77a0d7adU,
+	0x786f17a2U, 0x2c2e02f6U, 0xd668be0cU, 0x937dee49U,
+	0x5ec29c84U, 0x129d8fc8U, 0x81e9685bU, 0x90bd2d4aU,
+	0x7c6a16a6U, 0xe4d4303eU, 0xcb33f811U, 0x693b52b3U,
+	0xc5bc791fU, 0x2d6e43f7U, 0x85ec695fU, 0xc4fc381eU,
+	0x303505eaU, 0xdce23e06U, 0xfa4fb520U, 0x37f0c7edU,
+	0x296b42f3U, 0x357045efU, 0x4c561a96U, 0x510d5c8bU,
+	0xb9df6663U, 0x397f46e3U, 0x5c421e86U, 0xa341e279U,
+	0xee5eb034U, 0xbdda6767U, 0xa484207eU, 0xe0d1313aU,
+	0xc339fa19U, 0xf245b728U, 0x216140fbU, 0xf4c0342eU,
+	0x8ca62a56U, 0x653451bfU, 0xb5d0656fU, 0x22a183f8U,
+	0x3ffac5e5U, 0xae0ea074U, 0x9e32ac44U, 0xc73cfb1dU,
+	0x16988eccU, 0x4399da99U, 0x9778ef4dU, 0xf640b62cU,
+	0xbc9a2666U, 0xa201a378U, 0x5d025f87U, 0x6bbbd0b1U,
+	0x33f5c6e9U, 0x62f193b8U, 0x76e096acU, 0x862caa5cU,
+	0x5ac79d80U, 0x383f07e2U, 0xe99b7233U, 0x3bffc4e1U,
+	0x3eba84e4U, 0x07cccbddU, 0xc8f33b12U, 0x2feec1f5U,
+	0xd32dfe09U, 0x6d3e53b7U, 0xd5a87d0fU, 0xe8db3332U,
+	0x0bc3c8d1U, 0xf305f629U, 0x752055afU, 0x7d2a57a7U,
+	0x6afb91b0U, 0x8f66e955U, 0x7bafd4a1U, 0x32b587e8U,
+	0xc9b37a13U, 0x66f492bcU, 0x36b086ecU, 0x054c49dfU,
+	0xb750e76dU, 0x242400feU, 0x9638ae4cU, 0x3c3a06e6U,
+	0xbe1aa464U, 0x19574ec3U, 0x317544ebU, 0x13ddcec9U,
+	0x89e36a53U, 0xf0c5352aU, 0x84ac285eU, 0x000909daU,
+	0xea5bb130U, 0xd26dbf08U, 0x613150bbU, 0xa88b2372U,
+	0xc67cba1cU, 0x4ad39990U, 0x876ceb5dU, 0x014948dbU,
+	0xb1d5646bU, 0x42d99b98U, 0x14180cceU, 0x746014aeU,
+	0x08030bd2U, 0x0c060ad6U, 0xd0ed3d0aU, 0xf8cf3722U,
+	0x8b63e851U, 0xd1ad7c0bU, 0x03c9cad9U, 0xf700f72dU,
+	0x538dde89U, 0xca73b910U, 0x4f96d995U, 0x4059199aU,
+	0xf98f7623U, 0xda67bd00U, 0xccf63a16U, 0xb89f2762U,
+	0xc1b9781bU, 0xfd8a7727U, 0xd4e83c0eU, 0xde62bc04U,
+	0x95f86d4fU, 0xba1fa560U, 0x792f56a3U, 0x9f72ed45U,
+	0x2bebc0f1U, 0x8a23a950U, 0x504d1d8aU, 0x4b93d891U,
+	0x09434ad3U, 0x6c7e12b6U, 0xfe4ab424U, 0xf580752fU,
+	0x5f82dd85U, 0x2aab81f0U, 0x6fbed1b5U, 0x7faad5a5U,
+	0x9b77ec41U, 0x18170fc2U, 0xe311f239U, 0x94b82c4eU,
+	0x8de66b57U, 0xe714f33dU, 0x607111baU, 0x445c189eU,
+	0x282b03f2U, 0x5b87dc81U, 0x7aef95a0U, 0x99f76e43U,
+	0xaa0ba170U, 0x040c08deU, 0xa604a27cU, 0xaf4ee175U,
+	0xdda27f07U, 0x7eea94a4U, 0x48531b92U, 0x0e8688d4U,
+	0x55085d8fU, 0xce76b814U, 0x88a32b52U, 0x3d7a47e7U,
+	0x58471f82U, 0x068c8adcU, 0xc0f9391aU, 0x451c599fU,
+	0xe251b338U, 0x923daf48U, 0x59075e83U, 0x9cb22e46U,
+	0x77d3a42aU, 0x6e8de333U, 0x8b109bd6U, 0xd75b8c8aU,
+	0x1fa1be42U, 0xab3893f6U, 0xd8944c85U, 0xf236c4afU,
+	0x74136729U, 0xf633c5abU, 0x7c196521U, 0x2797b07aU,
+	0x8cd559d1U, 0xc34a899eU, 0x9cc15dc1U, 0x384c7465U,
+	0x80da5addU, 0xb1a617ecU, 0x46afe91bU, 0xf5f306a8U,
+	0x84df5bd9U, 0xd61bcd8bU, 0x3c497561U, 0x2392b17eU,
+	0x865fd9dbU, 0x3d093460U, 0x6002623dU, 0xb4e357e9U,
+	0x09303954U, 0x36c3f56bU, 0x34437769U, 0x4aa0ea17U,
+	0x5eb1ef03U, 0x25173278U, 0x6d4d2030U, 0xff7986a2U,
+	0x6a88e237U, 0x930e9dceU, 0x4ea5eb13U, 0xc5cf0a98U,
+	0xe0a242bdU, 0x7d592420U, 0x007a7a5dU, 0x2112337cU,
+	0x9b049fc6U, 0x13aebd4eU, 0x9e41dfc3U, 0xa5b712f8U,
+	0xbf2996e2U, 0xe222c0bfU, 0x5c316d01U, 0x8a50dad7U,
+	0x0bb0bb56U, 0x0c757951U, 0x8f159ad2U, 0x7a9ce627U,
+	0x2eddf373U, 0x53fead0eU, 0x7e99e723U, 0xc74f889aU,
+	0xf9fc05a4U, 0x12eefc4fU, 0xd09e4e8dU, 0xb663d5ebU,
+	0x3106376cU, 0x3046766dU, 0xb266d4efU, 0xdf518e82U,
+	0xe36281beU, 0xe1e203bcU, 0x7016662dU, 0xc60fc99bU,
+	0x22d2f07fU, 0x49602914U, 0xdc914d81U, 0x48206815U,
+	0xb72394eaU, 0x3accf667U, 0x112e3f4cU, 0x8e55dbd3U,
+	0x8d9518d0U, 0x819a1bdcU, 0x2ad8f277U, 0xe627c1bbU,
+	0xe9e801b4U, 0xbda914e0U, 0x47efa81aU, 0x02faf85fU,
+	0xcf458a92U, 0x831a99deU, 0x106e7e4dU, 0x013a3b5cU,
+	0xeded00b0U, 0x75532628U, 0x5ab4ee07U, 0xf8bc44a5U,
+	0x543b6f09U, 0xbce955e1U, 0x146b7f49U, 0x557b2e08U,
+	0xa1b213fcU, 0x4d652810U, 0x6bc8a336U, 0xa677d1fbU,
+	0xb8ec54e5U, 0xa4f753f9U, 0xddd10c80U, 0xc08a4a9dU,
+	0x28587075U, 0xa8f850f5U, 0xcdc50890U, 0x32c6f46fU,
+	0x7fd9a622U, 0x2c5d7171U, 0x35033668U, 0x7156272cU,
+	0x52beec0fU, 0x63c2a13eU, 0xb0e656edU, 0x65472238U,
+	0x1d213c40U, 0xf4b347a9U, 0x24577379U, 0xb32695eeU,
+	0xae7dd3f3U, 0x3f89b662U, 0x0fb5ba52U, 0x56bbed0bU,
+	0x871f98daU, 0xd21ecc8fU, 0x06fff95bU, 0x67c7a03aU,
+	0x2d1d3070U, 0x3386b56eU, 0xcc854991U, 0xfa3cc6a7U,
+	0xa272d0ffU, 0xf37685aeU, 0xe76780baU, 0x17abbc4aU,
+	0xcb408b96U, 0xa9b811f4U, 0x781c6425U, 0xaa78d2f7U,
+	0xaf3d92f2U, 0x964bddcbU, 0x59742d04U, 0xbe69d7e3U,
+	0x42aae81fU, 0xfcb945a1U, 0x442f6b19U, 0x795c2524U,
+	0x9a44dec7U, 0x6282e03fU, 0xe4a743b9U, 0xecad41b1U,
+	0xfb7c87a6U, 0x1ee1ff43U, 0xea28c2b7U, 0xa33291feU,
+	0x58346c05U, 0xf77384aaU, 0xa73790faU, 0x94cb5fc9U,
+	0x26d7f17bU, 0xb5a316e8U, 0x07bfb85aU, 0xadbd10f0U,
+	0x2f9db272U, 0x88d058d5U, 0xa0f252fdU, 0x825ad8dfU,
+	0x18647c45U, 0x6142233cU, 0x152b3e48U, 0x918e1fccU,
+	0x7bdca726U, 0x43eaa91eU, 0xf0b646adU, 0x390c3564U,
+	0x57fbac0aU, 0xdb548f86U, 0x16ebfd4bU, 0x90ce5ecdU,
+	0x2052727dU, 0xd35e8d8eU, 0x859f1ad8U, 0xe5e702b8U,
+	0x99841dc4U, 0x9d811cc0U, 0x416a2b1cU, 0x69482134U,
+	0x1ae4fe47U, 0x402a6a1dU, 0x924edccfU, 0x6687e13bU,
+	0xc20ac89fU, 0x5bf4af06U, 0xde11cf83U, 0xd1de0f8cU,
+	0x68086035U, 0x4be0ab16U, 0x5d712c00U, 0x29183174U,
+	0x503e6e0dU, 0x6c0d6131U, 0x456f2a18U, 0x4fe5aa12U,
+	0x047f7b59U, 0x2b98b376U, 0xe8a840b5U, 0x0ef5fb53U,
+	0xba6cd6e7U, 0x1ba4bf46U, 0xc1ca0b9cU, 0xda14ce87U,
+	0x98c45cc5U, 0xfdf904a0U, 0x6fcda232U, 0x64076339U,
+	0xce05cb93U, 0xbb2c97e6U, 0xfe39c7a3U, 0xee2dc3b3U,
+	0x0af0fa57U, 0x899019d4U, 0x7296e42fU, 0x053f3a58U,
+	0x1c617d41U, 0x7693e52bU, 0xf1f607acU, 0xd5db0e88U,
+	0xb9ac15e4U, 0xca00ca97U, 0xeb6883b6U, 0x08707855U,
+	0x3b8cb766U, 0x958b1ec8U, 0x3783b46aU, 0x3ec9f763U,
+	0x4c256911U, 0xef6d82b2U, 0xd9d40d84U, 0x9f019ec2U,
+	0xc48f4b99U, 0x5ff1ae02U, 0x19243d44U, 0xacfd51f1U,
+	0xc9c00994U, 0x970b9ccaU, 0x517e2f0cU, 0xd49b4f89U,
+	0x73d6a52eU, 0x03bab95eU, 0xc8804895U, 0x0d353850U,
+	0x1eced05eU, 0x07909747U, 0xe20defa2U, 0xbe46f8feU,
+	0x76bcca36U, 0xc225e782U, 0xb18938f1U, 0x9b2bb0dbU,
+	0x1d0e135dU, 0x9f2eb1dfU, 0x15041155U, 0x4e8ac40eU,
+	0xe5c82da5U, 0xaa57fdeaU, 0xf5dc29b5U, 0x51510011U,
+	0xe9c72ea9U, 0xd8bb6398U, 0x2fb29d6fU, 0x9cee72dcU,
+	0xedc22fadU, 0xbf06b9ffU, 0x55540115U, 0x4a8fc50aU,
+	0xef42adafU, 0x54144014U, 0x091f1649U, 0xddfe239dU,
+	0x602d4d20U, 0x5fde811fU, 0x5d5e031dU, 0x23bd9e63U,
+	0x37ac9b77U, 0x4c0a460cU, 0x04505444U, 0x9664f2d6U,
+	0x03959643U, 0xfa13e9baU, 0x27b89f67U, 0xacd27eecU,
+	0x89bf36c9U, 0x14445054U, 0x69670e29U, 0x480f4708U,
+	0xf219ebb2U, 0x7ab3c93aU, 0xf75cabb7U, 0xccaa668cU,
+	0xd634e296U, 0x8b3fb4cbU, 0x352c1975U, 0xe34daea3U,
+	0x62adcf22U, 0x65680d25U, 0xe608eea6U, 0x13819253U,
+	0x47c08707U, 0x3ae3d97aU, 0x17849357U, 0xae52fceeU,
+	0x90e171d0U, 0x7bf3883bU, 0xb9833af9U, 0xdf7ea19fU,
+	0x581b4318U, 0x595b0219U, 0xdb7ba09bU, 0xb64cfaf6U,
+	0x8a7ff5caU, 0x88ff77c8U, 0x190b1259U, 0xaf12bdefU,
+	0x4bcf840bU, 0x207d5d60U, 0xb58c39f5U, 0x213d1c61U,
+	0xde3ee09eU, 0x53d18213U, 0x78334b38U, 0xe748afa7U,
+	0xe4886ca4U, 0xe8876fa8U, 0x43c58603U, 0x8f3ab5cfU,
+	0x80f575c0U, 0xd4b46094U, 0x2ef2dc6eU, 0x6be78c2bU,
+	0xa658fee6U, 0xea07edaaU, 0x79730a39U, 0x68274f28U,
+	0x84f074c4U, 0x1c4e525cU, 0x33a99a73U, 0x91a130d1U,
+	0x3d261b7dU, 0xd5f42195U, 0x7d760b3dU, 0x3c665a7cU,
+	0xc8af6788U, 0x24785c64U, 0x02d5d742U, 0xcf6aa58fU,
+	0xd1f12091U, 0xcdea278dU, 0xb4cc78f4U, 0xa9973ee9U,
+	0x41450401U, 0xc1e52481U, 0xa4d87ce4U, 0x5bdb801bU,
+	0x16c4d256U, 0x45400505U, 0x5c1e421cU, 0x184b5358U,
+	0x3ba3987bU, 0x0adfd54aU, 0xd9fb2299U, 0x0c5a564cU,
+	0x743c4834U, 0x9dae33ddU, 0x4d4a070dU, 0xda3be19aU,
+	0xc760a787U, 0x5694c216U, 0x66a8ce26U, 0x3fa6997fU,
+	0xee02ecaeU, 0xbb03b8fbU, 0x6fe28d2fU, 0x0edad44eU,
+	0x44004404U, 0x5a9bc11aU, 0xa5983de5U, 0x9321b2d3U,
+	0xcb6fa48bU, 0x9a6bf1daU, 0x8e7af4ceU, 0x7eb6c83eU,
+	0xa25dffe2U, 0xc0a56580U, 0x11011051U, 0xc365a683U,
+	0xc620e686U, 0xff56a9bfU, 0x30695970U, 0xd774a397U,
+	0x2bb79c6bU, 0x95a431d5U, 0x2d321f6dU, 0x10415150U,
+	0xf359aab3U, 0x0b9f944bU, 0x8dba37cdU, 0x85b035c5U,
+	0x9261f3d2U, 0x77fc8b37U, 0x8335b6c3U, 0xca2fe58aU,
+	0x31291871U, 0x9e6ef0deU, 0xce2ae48eU, 0xfdd62bbdU,
+	0x4fca850fU, 0xdcbe629cU, 0x6ea2cc2eU, 0xc4a06484U,
+	0x4680c606U, 0xe1cd2ca1U, 0xc9ef2689U, 0xeb47acabU,
+	0x71790831U, 0x085f5748U, 0x7c364a3cU, 0xf8936bb8U,
+	0x12c1d352U, 0x2af7dd6aU, 0x99ab32d9U, 0x50114110U,
+	0x3ee6d87eU, 0xb249fbf2U, 0x7ff6893fU, 0xf9d32ab9U,
+	0x494f0609U, 0xba43f9faU, 0xec826eacU, 0x8cfa76ccU,
+	0xf09969b0U, 0xf49c68b4U, 0x28775f68U, 0x00555540U,
+	0x73f98a33U, 0x29371e69U, 0xfb53a8bbU, 0x0f9a954fU,
+	0xab17bcebU, 0x32e9db72U, 0xb70cbbf7U, 0xb8c37bf8U,
+	0x01151441U, 0x22fddf62U, 0x346c5874U, 0x40054500U,
+	0x39231a79U, 0x05101545U, 0x2c725e6cU, 0x26f8de66U,
+	0x6d620f2dU, 0x4285c702U, 0x81b534c1U, 0x67e88f27U,
+	0xd371a293U, 0x72b9cb32U, 0xa8d77fe8U, 0xb309baf3U,
+	0xf1d928b1U, 0x94e470d4U, 0x06d0d646U, 0x0d1a174dU,
+	0xa718bfe7U, 0xd231e392U, 0x9724b3d7U, 0x8730b7c7U,
+	0x63ed8e23U, 0xe08d6da0U, 0x1b8b905bU, 0x6c224e2cU,
+	0x757c0935U, 0x1f8e915fU, 0x98eb73d8U, 0xbcc67afcU,
+	0xd0b16190U, 0xa31dbee3U, 0x8275f7c2U, 0x616d0c21U,
+	0x5291c312U, 0xfc966abcU, 0x5e9ec01eU, 0x57d48317U,
+	0x25381d65U, 0x8670f6c6U, 0xb0c979f0U, 0xf61ceab6U,
+	0xad923fedU, 0x36ecda76U, 0x70394930U, 0xc5e02585U,
+	0xa0dd7de0U, 0xfe16e8beU, 0x38635b78U, 0xbd863bfdU,
+	0x1acbd15aU, 0x6aa7cd2aU, 0xa19d3ce1U, 0x64284c24U,
+	0x3891a927U, 0x21cfee3eU, 0xc45296dbU, 0x98198187U,
+	0x50e3b34fU, 0xe47a9efbU, 0x97d64188U, 0xbd74c9a2U,
+	0x3b516a24U, 0xb971c8a6U, 0x335b682cU, 0x68d5bd77U,
+	0xc39754dcU, 0x8c088493U, 0xd38350ccU, 0x770e7968U,
+	0xcf9857d0U, 0xfee41ae1U, 0x09ede416U, 0xbab10ba5U,
+	0xcb9d56d4U, 0x9959c086U, 0x730b786cU, 0x6cd0bc73U,
+	0xc91dd4d6U, 0x724b396dU, 0x2f406f30U, 0xfba15ae4U,
+	0x46723459U, 0x7981f866U, 0x7b017a64U, 0x05e2e71aU,
+	0x11f3e20eU, 0x6a553f75U, 0x220f2d3dU, 0xb03b8bafU,
+	0x25caef3aU, 0xdc4c90c3U, 0x01e7e61eU, 0x8a8d0795U,
+	0xafe04fb0U, 0x321b292dU, 0x4f387750U, 0x6e503e71U,
+	0xd44692cbU, 0x5cecb043U, 0xd103d2ceU, 0xeaf51ff5U,
+	0xf06b9befU, 0xad60cdb2U, 0x1373600cU, 0xc512d7daU,
+	0x44f2b65bU, 0x4337745cU, 0xc05797dfU, 0x35deeb2aU,
+	0x619ffe7eU, 0x1cbca003U, 0x31dbea2eU, 0x880d8597U,
+	0xb6be08a9U, 0x5dacf142U, 0x9fdc4380U, 0xf921d8e6U,
+	0x7e443a61U, 0x7f047b60U, 0xfd24d9e2U, 0x9013838fU,
+	0xac208cb3U, 0xaea00eb1U, 0x3f546b20U, 0x894dc496U,
+	0x6d90fd72U, 0x06222419U, 0x93d3408cU, 0x07626518U,
+	0xf86199e7U, 0x758efb6aU, 0x5e6c3241U, 0xc117d6deU,
+	0xc2d715ddU, 0xced816d1U, 0x659aff7aU, 0xa965ccb6U,
+	0xa6aa0cb9U, 0xf2eb19edU, 0x08ada517U, 0x4db8f552U,
+	0x8007879fU, 0xcc5894d3U, 0x5f2c7340U, 0x4e783651U,
+	0xa2af0dbdU, 0x3a112b25U, 0x15f6e30aU, 0xb7fe49a8U,
+	0x1b796204U, 0xf3ab58ecU, 0x5b297244U, 0x1a392305U,
+	0xeef01ef1U, 0x0227251dU, 0x248aae3bU, 0xe935dcf6U,
+	0xf7ae59e8U, 0xebb55ef4U, 0x9293018dU, 0x8fc84790U,
+	0x671a7d78U, 0xe7ba5df8U, 0x8287059dU, 0x7d84f962U,
+	0x309bab2fU, 0x631f7c7cU, 0x7a413b65U, 0x3e142a21U,
+	0x1dfce102U, 0x2c80ac33U, 0xffa45be0U, 0x2a052f35U,
+	0x5263314dU, 0xbbf14aa4U, 0x6b157e74U, 0xfc6498e3U,
+	0xe13fdefeU, 0x70cbbb6fU, 0x40f7b75fU, 0x19f9e006U,
+	0xc85d95d7U, 0x9d5cc182U, 0x49bdf456U, 0x2885ad37U,
+	0x625f3d7dU, 0x7cc4b863U, 0x83c7449cU, 0xb57ecbaaU,
+	0xed30ddf2U, 0xbc3488a3U, 0xa8258db7U, 0x58e9b147U,
+	0x8402869bU, 0xe6fa1cf9U, 0x375e6928U, 0xe53adffaU,
+	0xe07f9fffU, 0xd909d0c6U, 0x16362009U, 0xf12bdaeeU,
+	0x0de8e512U, 0xb3fb48acU, 0x0b6d6614U, 0x361e2829U,
+	0xd506d3caU, 0x2dc0ed32U, 0xabe54eb4U, 0xa3ef4cbcU,
+	0xb43e8aabU, 0x51a3f24eU, 0xa56acfbaU, 0xec709cf3U,
+	0x17766108U, 0xb83189a7U, 0xe8759df7U, 0xdb8952c4U,
+	0x6995fc76U, 0xfae11be5U, 0x48fdb557U, 0xe2ff1dfdU,
+	0x60dfbf7fU, 0xc79255d8U, 0xefb05ff0U, 0xcd18d5d2U,
+	0x57267148U, 0x2e002e31U, 0x5a693345U, 0xdecc12c1U,
+	0x349eaa2bU, 0x0ca8a413U, 0xbff44ba0U, 0x764e3869U,
+	0x18b9a107U, 0x9416828bU, 0x59a9f046U, 0xdf8c53c0U,
+	0x6f107f70U, 0x9c1c8083U, 0xcadd17d5U, 0xaaa50fb5U,
+	0xd6c610c9U, 0xd2c311cdU, 0x0e282611U, 0x260a2c39U,
+	0x55a6f34aU, 0x0f686710U, 0xdd0cd1c2U, 0x29c5ec36U,
+	0x8d48c592U, 0x14b6a20bU, 0x9153c28eU, 0x9e9c0281U,
+	0x274a6d38U, 0x04a2a61bU, 0x1233210dU, 0x665a3c79U,
+	0x1f7c6300U, 0x234f6c3cU, 0x0a2d2715U, 0x00a7a71fU,
+	0x4b3d7654U, 0x64dabe7bU, 0xa7ea4db8U, 0x41b7f65eU,
+	0xf52edbeaU, 0x54e6b24bU, 0x8e880691U, 0x9556c38aU,
+	0xd78651c8U, 0xb2bb09adU, 0x208faf3fU, 0x2b456e34U,
+	0x8147c69eU, 0xf46e9aebU, 0xb17bcaaeU, 0xa16fcebeU,
+	0x45b2f75aU, 0xc6d214d9U, 0x3dd4e922U, 0x4a7d3755U,
+	0x5323704cU, 0x39d1e826U, 0xbeb40aa1U, 0x9a990385U,
+	0xf6ee18e9U, 0x8542c79aU, 0xa42a8ebbU, 0x47327558U,
+	0x74ceba6bU, 0xdac913c5U, 0x78c1b967U, 0x718bfa6eU,
+	0x0367641cU, 0xa02f8fbfU, 0x96960089U, 0xd04393cfU,
+	0x8bcd4694U, 0x10b3a30fU, 0x56663049U, 0xe3bf5cfcU,
+	0x86820499U, 0xd84991c7U, 0x1e3c2201U, 0x9bd94284U,
+	0x3c94a823U, 0x4cf8b453U, 0x87c24598U, 0x4277355dU,
+	0x0b9e951bU, 0x12c0d202U, 0xf75daae7U, 0xab16bdbbU,
+	0x63ec8f73U, 0xd775a2c7U, 0xa4d97db4U, 0x8e7bf59eU,
+	0x085e5618U, 0x8a7ef49aU, 0x00545410U, 0x5bda814bU,
+	0xf09868e0U, 0xbf07b8afU, 0xe08c6cf0U, 0x44014554U,
+	0xfc976becU, 0xcdeb26ddU, 0x3ae2d82aU, 0x89be3799U,
+	0xf8926ae8U, 0xaa56fcbaU, 0x40044450U, 0x5fdf804fU,
+	0xfa12e8eaU, 0x41440551U, 0x1c4f530cU, 0xc8ae66d8U,
+	0x757d0865U, 0x4a8ec45aU, 0x480e4658U, 0x36eddb26U,
+	0x22fcde32U, 0x595a0349U, 0x11001101U, 0x8334b793U,
+	0x16c5d306U, 0xef43acffU, 0x32e8da22U, 0xb9823ba9U,
+	0x9cef738cU, 0x01141511U, 0x7c374b6cU, 0x5d5f024dU,
+	0xe749aef7U, 0x6fe38c7fU, 0xe20ceef2U, 0xd9fa23c9U,
+	0xc364a7d3U, 0x9e6ff18eU, 0x207c5c30U, 0xf61debe6U,
+	0x77fd8a67U, 0x70384860U, 0xf358abe3U, 0x06d1d716U,
+	0x5290c242U, 0x2fb39c3fU, 0x02d4d612U, 0xbb02b9abU,
+	0x85b13495U, 0x6ea3cd7eU, 0xacd37fbcU, 0xca2ee4daU,
+	0x4d4b065dU, 0x4c0b475cU, 0xce2be5deU, 0xa31cbfb3U,
+	0x9f2fb08fU, 0x9daf328dU, 0x0c5b571cU, 0xba42f8aaU,
+	0x5e9fc14eU, 0x352d1825U, 0xa0dc7cb0U, 0x346d5924U,
+	0xcb6ea5dbU, 0x4681c756U, 0x6d630e7dU, 0xf218eae2U,
+	0xf1d829e1U, 0xfdd72aedU, 0x5695c346U, 0x9a6af08aU,
+	0x95a53085U, 0xc1e425d1U, 0x3ba2992bU, 0x7eb7c96eU,
+	0xb308bba3U, 0xff57a8efU, 0x6c234f7cU, 0x7d770a6dU,
+	0x91a03181U, 0x091e1719U, 0x26f9df36U, 0x84f17594U,
+	0x28765e38U, 0xc0a464d0U, 0x68264e78U, 0x29361f39U,
+	0xddff22cdU, 0x31281921U, 0x17859207U, 0xda3ae0caU,
+	0xc4a165d4U, 0xd8ba62c8U, 0xa19c3db1U, 0xbcc77bacU,
+	0x54154144U, 0xd4b561c4U, 0xb18839a1U, 0x4e8bc55eU,
+	0x03949713U, 0x50104040U, 0x494e0759U, 0x0d1b161dU,
+	0x2ef3dd3eU, 0x1f8f900fU, 0xccab67dcU, 0x190a1309U,
+	0x616c0d71U, 0x88fe7698U, 0x581a4248U, 0xcf6ba4dfU,
+	0xd230e2c2U, 0x43c48753U, 0x73f88b63U, 0x2af6dc3aU,
+	0xfb52a9ebU, 0xae53fdbeU, 0x7ab2c86aU, 0x1b8a910bU,
+	0x51500141U, 0x4fcb845fU, 0xb0c878a0U, 0x8671f796U,
+	0xde3fe1ceU, 0x8f3bb49fU, 0x9b2ab18bU, 0x6be68d7bU,
+	0xb70dbaa7U, 0xd5f520c5U, 0x04515514U, 0xd635e3c6U,
+	0xd370a3c3U, 0xea06ecfaU, 0x25391c35U, 0xc224e6d2U,
+	0x3ee7d92eU, 0x80f47490U, 0x38625a28U, 0x05111415U,
+	0xe609eff6U, 0x1ecfd10eU, 0x98ea7288U, 0x90e07080U,
+	0x8731b697U, 0x62acce72U, 0x9665f386U, 0xdf7fa0cfU,
+	0x24795d34U, 0x8b3eb59bU, 0xdb7aa1cbU, 0xe8866ef8U,
+	0x5a9ac04aU, 0xc9ee27d9U, 0x7bf2896bU, 0xd1f021c1U,
+	0x53d08343U, 0xf49d69e4U, 0xdcbf63ccU, 0xfe17e9eeU,
+	0x64294d74U, 0x1d0f120dU, 0x69660f79U, 0xedc32efdU,
+	0x07919617U, 0x3fa7982fU, 0x8cfb779cU, 0x45410455U,
+	0x2bb69d3bU, 0xa719beb7U, 0x6aa6cc7aU, 0xec836ffcU,
+	0x5c1f434cU, 0xaf13bcbfU, 0xf9d22be9U, 0x99aa3389U,
+	0xe5c92cf5U, 0xe1cc2df1U, 0x3d271a2dU, 0x15051005U,
+	0x66a9cf76U, 0x3c675b2cU, 0xee03edfeU, 0x1acad00aU,
+	0xbe47f9aeU, 0x27b99e37U, 0xa25cfeb2U, 0xad933ebdU,
+	0x14455104U, 0x37ad9a27U, 0x213c1d31U, 0x55550045U,
+	0x2c735f3cU, 0x10405000U, 0x39221b29U, 0x33a89b23U,
+	0x78324a68U, 0x57d58247U, 0x94e57184U, 0x72b8ca62U,
+	0xc621e7d6U, 0x67e98e77U, 0xbd873aadU, 0xa659ffb6U,
+	0xe4896df4U, 0x81b43591U, 0x13809303U, 0x184a5208U,
+	0xb248faa2U, 0xc761a6d7U, 0x8274f692U, 0x9260f282U,
+	0x76bdcb66U, 0xf5dd28e5U, 0x0edbd51eU, 0x79720b69U,
+	0x602c4c70U, 0x0aded41aU, 0x8dbb369dU, 0xa9963fb9U,
+	0xc5e124d5U, 0xb64dfba6U, 0x9725b287U, 0x743d4964U,
+	0x47c18657U, 0xe9c62ff9U, 0x4bce855bU, 0x4284c652U,
+	0x30685820U, 0x9320b383U, 0xa5993cb5U, 0xe34caff3U,
+	0xb8c27aa8U, 0x23bc9f33U, 0x65690c75U, 0xd0b060c0U,
+	0xb58d38a5U, 0xeb46adfbU, 0x2d331e3dU, 0xa8d67eb8U,
+	0x0f9b941fU, 0x7ff7886fU, 0xb4cd79a4U, 0x71780961U,
+	0x69d5bc32U, 0x708bfb2bU, 0x951683ceU, 0xc95d9492U,
+	0x01a7a65aU, 0xb53e8beeU, 0xc692549dU, 0xec30dcb7U,
+	0x6a157f31U, 0xe835ddb3U, 0x621f7d39U, 0x3991a862U,
+	0x92d341c9U, 0xdd4c9186U, 0x82c745d9U, 0x264a6c7dU,
+	0x9edc42c5U, 0xafa00ff4U, 0x58a9f103U, 0xebf51eb0U,
+	0x9ad943c1U, 0xc81dd593U, 0x224f6d79U, 0x3d94a966U,
+	0x9859c1c3U, 0x230f2c78U, 0x7e047a25U, 0xaae54ff1U,
+	0x1736214cU, 0x28c5ed73U, 0x2a456f71U, 0x54a6f20fU,
+	0x40b7f71bU, 0x3b112a60U, 0x734b3828U, 0xe17f9ebaU,
+	0x748efa2fU, 0x8d0885d6U, 0x50a3f30bU, 0xdbc91280U,
+	0xfea45aa5U, 0x635f3c38U, 0x1e7c6245U, 0x3f142b64U,
+	0x850287deU, 0x0da8a556U, 0x8047c7dbU, 0xbbb10ae0U,
+	0xa12f8efaU, 0xfc24d8a7U, 0x42377519U, 0x9456c2cfU,
+	0x15b6a34eU, 0x12736149U, 0x911382caU, 0x649afe3fU,
+	0x30dbeb6bU, 0x4df8b516U, 0x609fff3bU, 0xd9499082U,
+	0xe7fa1dbcU, 0x0ce8e457U, 0xce985695U, 0xa865cdf3U,
+	0x2f002f74U, 0x2e406e75U, 0xac60ccf7U, 0xc157969aU,
+	0xfd6499a6U, 0xffe41ba4U, 0x6e107e35U, 0xd809d183U,
+	0x3cd4e867U, 0x5766310cU, 0xc2975599U, 0x5626700dU,
+	0xa9258cf2U, 0x24caee7fU, 0x0f282754U, 0x9053c3cbU,
+	0x939300c8U, 0x9f9c03c4U, 0x34deea6fU, 0xf821d9a3U,
+	0xf7ee19acU, 0xa3af0cf8U, 0x59e9b002U, 0x1cfce047U,
+	0xd143928aU, 0x9d1c81c6U, 0x0e686655U, 0x1f3c2344U,
+	0xf3eb18a8U, 0x6b553e30U, 0x44b2f61fU, 0xe6ba5cbdU,
+	0x4a3d7711U, 0xa2ef4df9U, 0x0a6d6751U, 0x4b7d3610U,
+	0xbfb40be4U, 0x53633008U, 0x75cebb2eU, 0xb871c9e3U,
+	0xa6ea4cfdU, 0xbaf14be1U, 0xc3d71498U, 0xde8c5285U,
+	0x365e686dU, 0xb6fe48edU, 0xd3c31088U, 0x2cc0ec77U,
+	0x61dfbe3aU, 0x325b6969U, 0x2b052e70U, 0x6f503f34U,
+	0x4cb8f417U, 0x7dc4b926U, 0xaee04ef5U, 0x7b413a20U,
+	0x03272458U, 0xeab55fb1U, 0x3a516b61U, 0xad208df6U,
+	0xb07bcbebU, 0x218fae7aU, 0x11b3a24aU, 0x48bdf513U,
+	0x991980c2U, 0xcc18d497U, 0x18f9e143U, 0x79c1b822U,
+	0x331b2868U, 0x2d80ad76U, 0xd2835189U, 0xe43adebfU,
+	0xbc74c8e7U, 0xed709db6U, 0xf96198a2U, 0x09ada452U,
+	0xd546938eU, 0xb7be09ecU, 0x661a7c3dU, 0xb47ecaefU,
+	0xb13b8aeaU, 0x884dc5d3U, 0x4772351cU, 0xa06fcffbU,
+	0x5cacf007U, 0xe2bf5db9U, 0x5a297301U, 0x675a3d3cU,
+	0x8442c6dfU, 0x7c84f827U, 0xfaa15ba1U, 0xf2ab59a9U,
+	0xe57a9fbeU, 0x00e7e75bU, 0xf42edaafU, 0xbd3489e6U,
+	0x4632741dU, 0xe9759cb2U, 0xb93188e2U, 0x8acd47d1U,
+	0x38d1e963U, 0xaba50ef0U, 0x19b9a042U, 0xb3bb08e8U,
+	0x319baa6aU, 0x96d640cdU, 0xbef44ae5U, 0x9c5cc0c7U,
+	0x0662645dU, 0x7f443b24U, 0x0b2d2650U, 0x8f8807d4U,
+	0x65dabf3eU, 0x5decb106U, 0xeeb05eb5U, 0x270a2d7cU,
+	0x49fdb412U, 0xc552979eU, 0x08ede553U, 0x8ec846d5U,
+	0x3e546a65U, 0xcd589596U, 0x9b9902c0U, 0xfbe11aa0U,
+	0x878205dcU, 0x838704d8U, 0x5f6c3304U, 0x774e392cU,
+	0x04e2e65fU, 0x5e2c7205U, 0x8c48c4d7U, 0x7881f923U,
+	0xdc0cd087U, 0x45f2b71eU, 0xc017d79bU, 0xcfd81794U,
+	0x760e782dU, 0x55e6b30eU, 0x43773418U, 0x371e296cU,
+	0x4e387615U, 0x720b7929U, 0x5b693200U, 0x51e3b20aU,
+	0x1a796341U, 0x359eab6eU, 0xf6ae58adU, 0x10f3e34bU,
+	0xa46aceffU, 0x05a2a75eU, 0xdfcc1384U, 0xc412d69fU,
+	0x86c244ddU, 0xe3ff1cb8U, 0x71cbba2aU, 0x7a017b21U,
+	0xd003d38bU, 0xa52a8ffeU, 0xe03fdfbbU, 0xf02bdbabU,
+	0x14f6e24fU, 0x979601ccU, 0x6c90fc37U, 0x1b392240U,
+	0x02676559U, 0x6895fd33U, 0xeff01fb4U, 0xcbdd1690U,
+	0xa7aa0dfcU, 0xd406d28fU, 0xf56e9baeU, 0x1676604dU,
+	0x258aaf7eU, 0x8b8d06d0U, 0x2985ac72U, 0x20cfef7bU,
+	0x52237109U, 0xf16b9aaaU, 0xc7d2159cU, 0x810786daU,
+	0xda895381U, 0x41f7b61aU, 0x0722255cU, 0xb2fb49e9U,
+	0xd7c6118cU, 0x890d84d2U, 0x4f783714U, 0xca9d5791U,
+	0x6dd0bd36U, 0x1dbca146U, 0xd686508dU, 0x13332048U,
+	0xe355b638U, 0xfa0bf121U, 0x1f9689c4U, 0x43dd9e98U,
+	0x8b27ac50U, 0x3fbe81e4U, 0x4c125e97U, 0x66b0d6bdU,
+	0xe095753bU, 0x62b5d7b9U, 0xe89f7733U, 0xb311a268U,
+	0x18534bc3U, 0x57cc9b8cU, 0x08474fd3U, 0xacca6677U,
+	0x145c48cfU, 0x252005feU, 0xd229fb09U, 0x617514baU,
+	0x105949cbU, 0x429ddf99U, 0xa8cf6773U, 0xb714a36cU,
+	0x12d9cbc9U, 0xa98f2672U, 0xf484702fU, 0x206545fbU,
+	0x9db62b46U, 0xa245e779U, 0xa0c5657bU, 0xde26f805U,
+	0xca37fd11U, 0xb191206aU, 0xf9cb3222U, 0x6bff94b0U,
+	0xfe0ef025U, 0x07888fdcU, 0xda23f901U, 0x5149188aU,
+	0x742450afU, 0xe9df3632U, 0x94fc684fU, 0xb594216eU,
+	0x0f828dd4U, 0x8728af5cU, 0x0ac7cdd1U, 0x313100eaU,
+	0x2baf84f0U, 0x76a4d2adU, 0xc8b77f13U, 0x1ed6c8c5U,
+	0x9f36a944U, 0x98f36b43U, 0x1b9388c0U, 0xee1af435U,
+	0xba5be161U, 0xc778bf1cU, 0xea1ff531U, 0x53c99a88U,
+	0x6d7a17b6U, 0x8668ee5dU, 0x44185c9fU, 0x22e5c7f9U,
+	0xa580257eU, 0xa4c0647fU, 0x26e0c6fdU, 0x4bd79c90U,
+	0x77e493acU, 0x756411aeU, 0xe490743fU, 0x5289db89U,
+	0xb654e26dU, 0xdde63b06U, 0x48175f93U, 0xdca67a07U,
+	0x23a586f8U, 0xae4ae475U, 0x85a82d5eU, 0x1ad3c9c1U,
+	0x19130ac2U, 0x151c09ceU, 0xbe5ee065U, 0x72a1d3a9U,
+	0x7d6e13a6U, 0x292f06f2U, 0xd369ba08U, 0x967cea4dU,
+	0x5bc39880U, 0x179c8bccU, 0x84e86c5fU, 0x95bc294eU,
+	0x796b12a2U, 0xe1d5343aU, 0xce32fc15U, 0x6c3a56b7U,
+	0xc0bd7d1bU, 0x286f47f3U, 0x80ed6d5bU, 0xc1fd3c1aU,
+	0x353401eeU, 0xd9e33a02U, 0xff4eb124U, 0x32f1c3e9U,
+	0x2c6a46f7U, 0x307141ebU, 0x49571e92U, 0x540c588fU,
+	0xbcde6267U, 0x3c7e42e7U, 0x59431a82U, 0xa640e67dU,
+	0xeb5fb430U, 0xb8db6363U, 0xa185247aU, 0xe5d0353eU,
+	0xc638fe1dU, 0xf744b32cU, 0x246044ffU, 0xf1c1302aU,
+	0x89a72e52U, 0x603555bbU, 0xb0d1616bU, 0x27a087fcU,
+	0x3afbc1e1U, 0xab0fa470U, 0x9b33a840U, 0xc23dff19U,
+	0x13998ac8U, 0x4698de9dU, 0x9279eb49U, 0xf341b228U,
+	0xb99b2262U, 0xa700a77cU, 0x58035b83U, 0x6ebad4b5U,
+	0x36f4c2edU, 0x67f097bcU, 0x73e192a8U, 0x832dae58U,
+	0x5fc69984U, 0x3d3e03e6U, 0xec9a7637U, 0x3efec0e5U,
+	0x3bbb80e0U, 0x02cdcfd9U, 0xcdf23f16U, 0x2aefc5f1U,
+	0xd62cfa0dU, 0x683f57b3U, 0xd0a9790bU, 0xedda3736U,
+	0x0ec2ccd5U, 0xf604f22dU, 0x702151abU, 0x782b53a3U,
+	0x6ffa95b4U, 0x8a67ed51U, 0x7eaed0a5U, 0x37b483ecU,
+	0xccb27e17U, 0x63f596b8U, 0x33b182e8U, 0x004d4ddbU,
+	0xb251e369U, 0x212504faU, 0x9339aa48U, 0x393b02e2U,
+	0xbb1ba060U, 0x1c564ac7U, 0x347440efU, 0x16dccacdU,
+	0x8ce26e57U, 0xf5c4312eU, 0x81ad2c5aU, 0x05080ddeU,
+	0xef5ab534U, 0xd76cbb0cU, 0x643054bfU, 0xad8a2776U,
+	0xc37dbe18U, 0x4fd29d94U, 0x826def59U, 0x04484cdfU,
+	0xb4d4606fU, 0x47d89f9cU, 0x111908caU, 0x716110aaU,
+	0x0d020fd6U, 0x09070ed2U, 0xd5ec390eU, 0xfdce3326U,
+	0x8e62ec55U, 0xd4ac780fU, 0x06c8ceddU, 0xf201f329U,
+	0x568cda8dU, 0xcf72bd14U, 0x4a97dd91U, 0x45581d9eU,
+	0xfc8e7227U, 0xdf66b904U, 0xc9f73e12U, 0xbd9e2366U,
+	0xc4b87c1fU, 0xf88b7323U, 0xd1e9380aU, 0xdb63b800U,
+	0x90f9694bU, 0xbf1ea164U, 0x7c2e52a7U, 0x9a73e941U,
+	0x2eeac4f5U, 0x8f22ad54U, 0x554c198eU, 0x4e92dc95U,
+	0x0c424ed7U, 0x697f16b2U, 0xfb4bb020U, 0xf081712bU,
+	0x5a83d981U, 0x2faa85f4U, 0x6abfd5b1U, 0x7aabd1a1U,
+	0x9e76e845U, 0x1d160bc6U, 0xe610f63dU, 0x91b9284aU,
+	0x88e76f53U, 0xe215f739U, 0x657015beU, 0x415d1c9aU,
+	0x2d2a07f6U, 0x5e86d885U, 0x7fee91a4U, 0x9cf66a47U,
+	0xaf0aa574U, 0x010d0cdaU, 0xa305a678U, 0xaa4fe571U,
+	0xd8a37b03U, 0x7beb90a0U, 0x4d521f96U, 0x0b878cd0U,
+	0x5009598bU, 0xcb77bc10U, 0x8da22f56U, 0x387b43e3U,
+	0x5d461b86U, 0x038d8ed8U, 0xc5f83d1eU, 0x401d5d9bU,
+	0xe750b73cU, 0x973cab4cU, 0x5c065a87U, 0x99b32a42U,
+	0x7984fd73U, 0x60daba6aU, 0x8547c28fU, 0xd90cd5d3U,
+	0x11f6e71bU, 0xa56fcaafU, 0xd6c315dcU, 0xfc619df6U,
+	0x7a443e70U, 0xf8649cf2U, 0x724e3c78U, 0x29c0e923U,
+	0x82820088U, 0xcd1dd0c7U, 0x92960498U, 0x361b2d3cU,
+	0x8e8d0384U, 0xbff14eb5U, 0x48f8b042U, 0xfba45ff1U,
+	0x8a880280U, 0xd84c94d2U, 0x321e2c38U, 0x2dc5e827U,
+	0x88088082U, 0x335e6d39U, 0x6e553b64U, 0xbab40eb0U,
+	0x0767600dU, 0x3894ac32U, 0x3a142e30U, 0x44f7b34eU,
+	0x50e6b65aU, 0x2b406b21U, 0x631a7969U, 0xf12edffbU,
+	0x64dfbb6eU, 0x9d59c497U, 0x40f2b24aU, 0xcb9853c1U,
+	0xeef51be4U, 0x730e7d79U, 0x0e2d2304U, 0x2f456a25U,
+	0x9553c69fU, 0x1df9e417U, 0x9016869aU, 0xabe04ba1U,
+	0xb17ecfbbU, 0xec7599e6U, 0x52663458U, 0x8407838eU,
+	0x05e7e20fU, 0x02222008U, 0x8142c38bU, 0x74cbbf7eU,
+	0x208aaa2aU, 0x5da9f457U, 0x70cebe7aU, 0xc918d1c3U,
+	0xf7ab5cfdU, 0x1cb9a516U, 0xdec917d4U, 0xb8348cb2U,
+	0x3f516e35U, 0x3e112f34U, 0xbc318db6U, 0xd106d7dbU,
+	0xed35d8e7U, 0xefb55ae5U, 0x7e413f74U, 0xc85890c2U,
+	0x2c85a926U, 0x4737704dU, 0xd2c614d8U, 0x4677314cU,
+	0xb974cdb3U, 0x349baf3eU, 0x1f796615U, 0x8002828aU,
+	0x83c24189U, 0x8fcd4285U, 0x248fab2eU, 0xe87098e2U,
+	0xe7bf58edU, 0xb3fe4db9U, 0x49b8f143U, 0x0cada106U,
+	0xc112d3cbU, 0x8d4dc087U, 0x1e392714U, 0x0f6d6205U,
+	0xe3ba59e9U, 0x7b047f71U, 0x54e3b75eU, 0xf6eb1dfcU,
+	0x5a6c3650U, 0xb2be0cb8U, 0x1a3c2610U, 0x5b2c7751U,
+	0xafe54aa5U, 0x43327149U, 0x659ffa6fU, 0xa82088a2U,
+	0xb6bb0dbcU, 0xaaa00aa0U, 0xd38655d9U, 0xcedd13c4U,
+	0x260f292cU, 0xa6af09acU, 0xc39251c9U, 0x3c91ad36U,
+	0x718eff7bU, 0x220a2828U, 0x3b546f31U, 0x7f017e75U,
+	0x5ce9b556U, 0x6d95f867U, 0xbeb10fb4U, 0x6b107b61U,
+	0x13766519U, 0xfae41ef0U, 0x2a002a20U, 0xbd71ccb7U,
+	0xa02a8aaaU, 0x31deef3bU, 0x01e2e30bU, 0x58ecb452U,
+	0x8948c183U, 0xdc4995d6U, 0x08a8a002U, 0x6990f963U,
+	0x234a6929U, 0x3dd1ec37U, 0xc2d210c8U, 0xf46b9ffeU,
+	0xac2589a6U, 0xfd21dcf7U, 0xe930d9e3U, 0x19fce513U,
+	0xc517d2cfU, 0xa7ef48adU, 0x764b3d7cU, 0xa42f8baeU,
+	0xa16acbabU, 0x981c8492U, 0x5723745dU, 0xb03e8ebaU,
+	0x4cfdb146U, 0xf2ee1cf8U, 0x4a783240U, 0x770b7c7dU,
+	0x9413879eU, 0x6cd5b966U, 0xeaf01ae0U, 0xe2fa18e8U,
+	0xf52bdeffU, 0x10b6a61aU, 0xe47f9beeU, 0xad65c8a7U,
+	0x5663355cU, 0xf924ddf3U, 0xa960c9a3U, 0x9a9c0690U,
+	0x2880a822U, 0xbbf44fb1U, 0x09e8e103U, 0xa3ea49a9U,
+	0x21caeb2bU, 0x8687018cU, 0xaea50ba4U, 0x8c0d8186U,
+	0x1633251cU, 0x6f157a65U, 0x1b7c6711U, 0x9fd94695U,
+	0x758bfe7fU, 0x4dbdf047U, 0xfee11ff4U, 0x375b6c3dU,
+	0x59acf553U, 0xd503d6dfU, 0x18bca412U, 0x9e990794U,
+	0x2e052b24U, 0xdd09d4d7U, 0x8bc84381U, 0xebb05be1U,
+	0x97d3449dU, 0x93d64599U, 0x4f3d7245U, 0x671f786dU,
+	0x14b3a71eU, 0x4e7d3344U, 0x9c198596U, 0x68d0b862U,
+	0xcc5d91c6U, 0x55a3f65fU, 0xd04696daU, 0xdf8956d5U,
+	0x665f396cU, 0x45b7f24fU, 0x53267559U, 0x274f682dU,
+	0x5e693754U, 0x625a3868U, 0x4b387341U, 0x41b2f34bU,
+	0x0a282200U, 0x25cfea2fU, 0xe6ff19ecU, 0x00a2a20aU,
+	0xb43b8fbeU, 0x15f3e61fU, 0xcf9d52c5U, 0xd44397deU,
+	0x9693059cU, 0xf3ae5df9U, 0x619afb6bU, 0x6a503a60U,
+	0xc05292caU, 0xb57bcebfU, 0xf06e9efaU, 0xe07a9aeaU,
+	0x04a7a30eU, 0x87c7408dU, 0x7cc1bd76U, 0x0b686301U,
+	0x12362418U, 0x78c4bc72U, 0xffa15ef5U, 0xdb8c57d1U,
+	0xb7fb4cbdU, 0xc45793ceU, 0xe53fdaefU, 0x0627210cU,
+	0x35dbee3fU, 0x9bdc4791U, 0x39d4ed33U, 0x309eae3aU,
+	0x42723048U, 0xe13adbebU, 0xd78354ddU, 0x9156c79bU,
+	0xcad812c0U, 0x51a6f75bU, 0x1773641dU, 0xa2aa08a8U,
+	0xc79750cdU, 0x995cc593U, 0x5f297655U, 0xdacc16d0U,
+	0x7d81fc77U, 0x0dede007U, 0xc6d711ccU, 0x03626109U,
+	0x9e4cd25cU, 0x87129545U, 0x628feda0U, 0x3ec4fafcU,
+	0xf63ec834U, 0x42a7e580U, 0x310b3af3U, 0x1ba9b2d9U,
+	0x9d8c115fU, 0x1facb3ddU, 0x95861357U, 0xce08c60cU,
+	0x654a2fa7U, 0x2ad5ffe8U, 0x755e2bb7U, 0xd1d30213U,
+	0x69452cabU, 0x5839619aU, 0xaf309f6dU, 0x1c6c70deU,
+	0x6d402dafU, 0x3f84bbfdU, 0xd5d60317U, 0xca0dc708U,
+	0x6fc0afadU, 0xd4964216U, 0x899d144bU, 0x5d7c219fU,
+	0xe0af4f22U, 0xdf5c831dU, 0xdddc011fU, 0xa33f9c61U,
+	0xb72e9975U, 0xcc88440eU, 0x84d25646U, 0x16e6f0d4U,
+	0x83179441U, 0x7a91ebb8U, 0xa73a9d65U, 0x2c507ceeU,
+	0x093d34cbU, 0x94c65256U, 0xe9e50c2bU, 0xc88d450aU,
+	0x729be9b0U, 0xfa31cb38U, 0x77dea9b5U, 0x4c28648eU,
+	0x56b6e094U, 0x0bbdb6c9U, 0xb5ae1b77U, 0x63cfaca1U,
+	0xe22fcd20U, 0xe5ea0f27U, 0x668aeca4U, 0x93039051U,
+	0xc7428505U, 0xba61db78U, 0x97069155U, 0x2ed0feecU,
+	0x106373d2U, 0xfb718a39U, 0x390138fbU, 0x5ffca39dU,
+	0xd899411aU, 0xd9d9001bU, 0x5bf9a299U, 0x36cef8f4U,
+	0x0afdf7c8U, 0x087d75caU, 0x9989105bU, 0x2f90bfedU,
+	0xcb4d8609U, 0xa0ff5f62U, 0x350e3bf7U, 0xa1bf1e63U,
+	0x5ebce29cU, 0xd3538011U, 0xf8b1493aU, 0x67caada5U,
+	0x640a6ea6U, 0x68056daaU, 0xc3478401U, 0x0fb8b7cdU,
+	0x007777c2U, 0x54366296U, 0xae70de6cU, 0xeb658e29U,
+	0x26dafce4U, 0x6a85efa8U, 0xf9f1083bU, 0xe8a54d2aU,
+	0x047276c6U, 0x9ccc505eU, 0xb32b9871U, 0x112332d3U,
+	0xbda4197fU, 0x55762397U, 0xfdf4093fU, 0xbce4587eU,
+	0x482d658aU, 0xa4fa5e66U, 0x8257d540U, 0x4fe8a78dU,
+	0x51732293U, 0x4d68258fU, 0x344e7af6U, 0x29153cebU,
+	0xc1c70603U, 0x41672683U, 0x245a7ee6U, 0xdb598219U,
+	0x9646d054U, 0xc5c20707U, 0xdc9c401eU, 0x98c9515aU,
+	0xbb219a79U, 0x8a5dd748U, 0x5979209bU, 0x8cd8544eU,
+	0xf4be4a36U, 0x1d2c31dfU, 0xcdc8050fU, 0x5ab9e398U,
+	0x47e2a585U, 0xd616c014U, 0xe62acc24U, 0xbf249b7dU,
+	0x6e80eeacU, 0x3b81baf9U, 0xef608f2dU, 0x8e58d64cU,
+	0xc4824606U, 0xda19c318U, 0x251a3fe7U, 0x13a3b0d1U,
+	0x4beda689U, 0x1ae9f3d8U, 0x0ef8f6ccU, 0xfe34ca3cU,
+	0x22dffde0U, 0x40276782U, 0x91831253U, 0x43e7a481U,
+	0x46a2e484U, 0x7fd4abbdU, 0xb0eb5b72U, 0x57f6a195U,
+	0xab359e69U, 0x152633d7U, 0xadb01d6fU, 0x90c35352U,
+	0x73dba8b1U, 0x8b1d9649U, 0x0d3835cfU, 0x053237c7U,
+	0x12e3f1d0U, 0xf77e8935U, 0x03b7b4c1U, 0x4aade788U,
+	0xb1ab1a73U, 0x1eecf2dcU, 0x4ea8e68cU, 0x7d5429bfU,
+	0xcf48870dU, 0x5c3c609eU, 0xee20ce2cU, 0x44226686U,
+	0xc602c404U, 0x614f2ea3U, 0x496d248bU, 0x6bc5aea9U,
+	0xf1fb0a33U, 0x88dd554aU, 0xfcb4483eU, 0x781169baU,
+	0x9243d150U, 0xaa75df68U, 0x192930dbU, 0xd0934312U,
+	0xbe64da7cU, 0x32cbf9f0U, 0xff748b3dU, 0x795128bbU,
+	0xc9cd040bU, 0x3ac1fbf8U, 0x6c006caeU, 0x0c7874ceU,
+	0x701b6bb2U, 0x741e6ab6U, 0xa8f55d6aU, 0x80d75742U,
+	0xf37b8831U, 0xa9b51c6bU, 0x7bd1aab9U, 0x8f18974dU,
+	0x2b95bee9U, 0xb26bd970U, 0x378eb9f5U, 0x384179faU,
+	0x81971643U, 0xa27fdd60U, 0xb4ee5a76U, 0xc0874702U,
+	0xb9a1187bU, 0x85921747U, 0xacf05c6eU, 0xa67adc64U,
+	0xede00d2fU, 0xc207c500U, 0x013736c3U, 0xe76a8d25U,
+	0x53f3a091U, 0xf23bc930U, 0x28557deaU, 0x338bb8f1U,
+	0x715b2ab3U, 0x146672d6U, 0x8652d444U, 0x8d98154fU,
+	0x279abde5U, 0x52b3e190U, 0x17a6b1d5U, 0x07b2b5c5U,
+	0xe36f8c21U, 0x600f6fa2U, 0x9b099259U, 0xeca04c2eU,
+	0xf5fe0b37U, 0x9f0c935dU, 0x186971daU, 0x3c4478feU,
+	0x50336392U, 0x239fbce1U, 0x02f7f5c0U, 0xe1ef0e23U,
+	0xd213c110U, 0x7c1468beU, 0xde1cc21cU, 0xd7568115U,
+	0xa5ba1f67U, 0x06f2f4c4U, 0x304b7bf2U, 0x769ee8b4U,
+	0x2d103defU, 0xb66ed874U, 0xf0bb4b32U, 0x45622787U,
+	0x205f7fe2U, 0x7e94eabcU, 0xb8e1597aU, 0x3d0439ffU,
+	0x9a49d358U, 0xea25cf28U, 0x211f3ee3U, 0xe4aa4e26U,
+	0xaebf119fU, 0xb7e15686U, 0x527c2e63U, 0x0e37393fU,
+	0xc6cd0bf7U, 0x72542643U, 0x01f8f930U, 0x2b5a711aU,
+	0xad7fd29cU, 0x2f5f701eU, 0xa575d094U, 0xfefb05cfU,
+	0x55b9ec64U, 0x1a263c2bU, 0x45ade874U, 0xe120c1d0U,
+	0x59b6ef68U, 0x68caa259U, 0x9fc35caeU, 0x2c9fb31dU,
+	0x5db3ee6cU, 0x0f77783eU, 0xe525c0d4U, 0xfafe04cbU,
+	0x5f336c6eU, 0xe46581d5U, 0xb96ed788U, 0x6d8fe25cU,
+	0xd05c8ce1U, 0xefaf40deU, 0xed2fc2dcU, 0x93cc5fa2U,
+	0x87dd5ab6U, 0xfc7b87cdU, 0xb4219585U, 0x26153317U,
+	0xb3e45782U, 0x4a62287bU, 0x97c95ea6U, 0x1ca3bf2dU,
+	0x39cef708U, 0xa4359195U, 0xd916cfe8U, 0xf87e86c9U,
+	0x42682a73U, 0xcac208fbU, 0x472d6a76U, 0x7cdba74dU,
+	0x66452357U, 0x3b4e750aU, 0x855dd8b4U, 0x533c6f62U,
+	0xd2dc0ee3U, 0xd519cce4U, 0x56792f67U, 0xa3f05392U,
+	0xf7b146c6U, 0x8a9218bbU, 0xa7f55296U, 0x1e233d2fU,
+	0x2090b011U, 0xcb8249faU, 0x09f2fb38U, 0x6f0f605eU,
+	0xe86a82d9U, 0xe92ac3d8U, 0x6b0a615aU, 0x063d3b37U,
+	0x3a0e340bU, 0x388eb609U, 0xa97ad398U, 0x1f637c2eU,
+	0xfbbe45caU, 0x900c9ca1U, 0x05fdf834U, 0x914cdda0U,
+	0x6e4f215fU, 0xe3a043d2U, 0xc8428af9U, 0x57396e66U,
+	0x54f9ad65U, 0x58f6ae69U, 0xf3b447c2U, 0x3f4b740eU,
+	0x3084b401U, 0x64c5a155U, 0x9e831dafU, 0xdb964deaU,
+	0x16293f27U, 0x5a762c6bU, 0xc902cbf8U, 0xd8568ee9U,
+	0x3481b505U, 0xac3f939dU, 0x83d85bb2U, 0x21d0f110U,
+	0x8d57dabcU, 0x6585e054U, 0xcd07cafcU, 0x8c179bbdU,
+	0x78dea649U, 0x94099da5U, 0xb2a41683U, 0x7f1b644eU,
+	0x6180e150U, 0x7d9be64cU, 0x04bdb935U, 0x19e6ff28U,
+	0xf134c5c0U, 0x7194e540U, 0x14a9bd25U, 0xebaa41daU,
+	0xa6b51397U, 0xf531c4c4U, 0xec6f83ddU, 0xa83a9299U,
+	0x8bd259baU, 0xbaae148bU, 0x698ae358U, 0xbc2b978dU,
+	0xc44d89f5U, 0x2ddff21cU, 0xfd3bc6ccU, 0x6a4a205bU,
+	0x77116646U, 0xe6e503d7U, 0xd6d90fe7U, 0x8fd758beU,
+	0x5e732d6fU, 0x0b72793aU, 0xdf934ceeU, 0xbeab158fU,
+	0xf47185c5U, 0xeaea00dbU, 0x15e9fc24U, 0x23507312U,
+	0x7b1e654aU, 0x2a1a301bU, 0x3e0b350fU, 0xcec709ffU,
+	0x122c3e23U, 0x70d4a441U, 0xa170d190U, 0x73146742U,
+	0x76512747U, 0x4f27687eU, 0x801898b1U, 0x67056256U,
+	0x9bc65daaU, 0x25d5f014U, 0x9d43deacU, 0xa0309091U,
+	0x43286b72U, 0xbbee558aU, 0x3dcbf60cU, 0x35c1f404U,
+	0x22103213U, 0xc78d4af6U, 0x33447702U, 0x7a5e244bU,
+	0x8158d9b0U, 0x2e1f311fU, 0x7e5b254fU, 0x4da7ea7cU,
+	0xffbb44ceU, 0x6ccfa35dU, 0xded30defU, 0x74d1a545U,
+	0xf6f107c7U, 0x51bced60U, 0x799ee748U, 0x5b366d6aU,
+	0xc108c9f0U, 0xb82e9689U, 0xcc478bfdU, 0x48e2aa79U,
+	0xa2b01293U, 0x9a861cabU, 0x29daf318U, 0xe06080d1U,
+	0x8e9719bfU, 0x02383a33U, 0xcf8748feU, 0x49a2eb78U,
+	0xf93ec7c8U, 0x0a32383bU, 0x5cf3af6dU, 0x3c8bb70dU,
+	0x40e8a871U, 0x44eda975U, 0x98069ea9U, 0xb0249481U,
+	0xc3884bf2U, 0x9946dfa8U, 0x4b22697aU, 0xbfeb548eU,
+	0x1b667d2aU, 0x82981ab3U, 0x077d7a36U, 0x08b2ba39U,
+	0xb164d580U, 0x928c1ea3U, 0x841d99b5U, 0xf07484c1U,
+	0x8952dbb8U, 0xb561d484U, 0x9c039fadU, 0x96891fa7U,
+	0xdd13ceecU, 0xf2f406c3U, 0x31c4f500U, 0xd7994ee6U,
+	0x63006352U, 0xc2c80af3U, 0x18a6be29U, 0x03787b32U,
+	0x41a8e970U, 0x2495b115U, 0xb6a11787U, 0xbd6bd68cU,
+	0x17697e26U, 0x62402253U, 0x27557216U, 0x37417606U,
+	0xd39c4fe2U, 0x50fcac61U, 0xabfa519aU, 0xdc538fedU,
+	0xc50dc8f4U, 0xafff509eU, 0x289ab219U, 0x0cb7bb3dU,
+	0x60c0a051U, 0x136c7f22U, 0x32043603U, 0xd11ccde0U,
+	0xe2e002d3U, 0x4ce7ab7dU, 0xeeef01dfU, 0xe7a542d6U,
+	0x9549dca4U, 0x36013707U, 0x00b8b831U, 0x466d2b77U,
+	0x1de3fe2cU, 0x869d1bb7U, 0xc04888f1U, 0x7591e444U,
+	0x10acbc21U, 0x4e67297fU, 0x88129ab9U, 0x0df7fa3cU,
+	0xaaba109bU, 0xdad60cebU, 0x11ecfd20U, 0xd4598de5U,
+	0xf304f779U, 0xea5ab060U, 0x0fc7c885U, 0x538cdfd9U,
+	0x9b76ed11U, 0x2fefc0a5U, 0x5c431fd6U, 0x76e197fcU,
+	0xf0c4347aU, 0x72e496f8U, 0xf8ce3672U, 0xa340e329U,
+	0x08020a82U, 0x479ddacdU, 0x18160e92U, 0xbc9b2736U,
+	0x040d098eU, 0x357144bfU, 0xc278ba48U, 0x712455fbU,
+	0x0008088aU, 0x52cc9ed8U, 0xb89e2632U, 0xa745e22dU,
+	0x02888a88U, 0xb9de6733U, 0xe4d5316eU, 0x303404baU,
+	0x8de76a07U, 0xb214a638U, 0xb094243aU, 0xce77b944U,
+	0xda66bc50U, 0xa1c0612bU, 0xe99a7363U, 0x7baed5f1U,
+	0xee5fb164U, 0x17d9ce9dU, 0xca72b840U, 0x411859cbU,
+	0x647511eeU, 0xf98e7773U, 0x84ad290eU, 0xa5c5602fU,
+	0x1fd3cc95U, 0x9779ee1dU, 0x1a968c90U, 0x216041abU,
+	0x3bfec5b1U, 0x66f593ecU, 0xd8e63e52U, 0x0e878984U,
+	0x8f67e805U, 0x88a22a02U, 0x0bc2c981U, 0xfe4bb574U,
+	0xaa0aa020U, 0xd729fe5dU, 0xfa4eb470U, 0x4398dbc9U,
+	0x7d2b56f7U, 0x9639af1cU, 0x54491ddeU, 0x32b486b8U,
+	0xb5d1643fU, 0xb491253eU, 0x36b187bcU, 0x5b86ddd1U,
+	0x67b5d2edU, 0x653550efU, 0xf4c1357eU, 0x42d89ac8U,
+	0xa605a32cU, 0xcdb77a47U, 0x58461ed2U, 0xccf73b46U,
+	0x33f4c7b9U, 0xbe1ba534U, 0x95f96c1fU, 0x0a828880U,
+	0x09424b83U, 0x054d488fU, 0xae0fa124U, 0x62f092e8U,
+	0x6d3f52e7U, 0x397e47b3U, 0xc338fb49U, 0x862dab0cU,
+	0x4b92d9c1U, 0x07cdca8dU, 0x94b92d1eU, 0x85ed680fU,
+	0x693a53e3U, 0xf184757bU, 0xde63bd54U, 0x7c6b17f6U,
+	0xd0ec3c5aU, 0x383e06b2U, 0x90bc2c1aU, 0xd1ac7d5bU,
+	0x256540afU, 0xc9b27b43U, 0xef1ff065U, 0x22a082a8U,
+	0x3c3b07b6U, 0x202000aaU, 0x59065fd3U, 0x445d19ceU,
+	0xac8f2326U, 0x2c2f03a6U, 0x49125bc3U, 0xb611a73cU,
+	0xfb0ef571U, 0xa88a2222U, 0xb1d4653bU, 0xf581747fU,
+	0xd669bf5cU, 0xe715f26dU, 0x343105beU, 0xe190716bU,
+	0x99f66f13U, 0x706414faU, 0xa080202aU, 0x37f1c6bdU,
+	0x2aaa80a0U, 0xbb5ee531U, 0x8b62e901U, 0xd26cbe58U,
+	0x03c8cb89U, 0x56c99fdcU, 0x8228aa08U, 0xe310f369U,
+	0xa9ca6323U, 0xb751e63dU, 0x48521ac2U, 0x7eeb95f4U,
+	0x26a583acU, 0x77a1d6fdU, 0x63b0d3e9U, 0x937cef19U,
+	0x4f97d8c5U, 0x2d6f42a7U, 0xfccb3776U, 0x2eaf81a4U,
+	0x2beac1a1U, 0x129c8e98U, 0xdda37e57U, 0x3abe84b0U,
+	0xc67dbb4cU, 0x786e16f2U, 0xc0f8384aU, 0xfd8b7677U,
+	0x1e938d94U, 0xe655b36cU, 0x607010eaU, 0x687a12e2U,
+	0x7fabd4f5U, 0x9a36ac10U, 0x6eff91e4U, 0x27e5c2adU,
+	0xdce33f56U, 0x73a4d7f9U, 0x23e0c3a9U, 0x101c0c9aU,
+	0xa200a228U, 0x317445bbU, 0x8368eb09U, 0x296a43a3U,
+	0xab4ae121U, 0x0c070b86U, 0x242501aeU, 0x068d8b8cU,
+	0x9cb32f16U, 0xe595706fU, 0x91fc6d1bU, 0x15594c9fU,
+	0xff0bf475U, 0xc73dfa4dU, 0x746115feU, 0xbddb6637U,
+	0xd32cff59U, 0x5f83dcd5U, 0x923cae18U, 0x14190d9eU,
+	0xa485212eU, 0x5789deddU, 0x0148498bU, 0x613051ebU,
+	0x1d534e97U, 0x19564f93U, 0xc5bd784fU, 0xed9f7267U,
+	0x9e33ad14U, 0xc4fd394eU, 0x16998f9cU, 0xe250b268U,
+	0x46dd9bccU, 0xdf23fc55U, 0x5ac69cd0U, 0x55095cdfU,
+	0xecdf3366U, 0xcf37f845U, 0xd9a67f53U, 0xadcf6227U,
+	0xd4e93d5eU, 0xe8da3262U, 0xc1b8794bU, 0xcb32f941U,
+	0x80a8280aU, 0xaf4fe025U, 0x6c7f13e6U, 0x8a22a800U,
+	0x3ebb85b4U, 0x9f73ec15U, 0x451d58cfU, 0x5ec39dd4U,
+	0x1c130f96U, 0x792e57f3U, 0xeb1af161U, 0xe0d0306aU,
+	0x4ad298c0U, 0x3ffbc4b5U, 0x7aee94f0U, 0x6afa90e0U,
+	0x8e27a904U, 0x0d474a87U, 0xf641b77cU, 0x81e8690bU,
+	0x98b62e12U, 0xf244b678U, 0x752154ffU, 0x510c5ddbU,
+	0x3d7b46b7U, 0x4ed799c4U, 0x6fbfd0e5U, 0x8ca72b06U,
+	0xbf5be435U, 0x115c4d9bU, 0xb354e739U, 0xba1ea430U,
+	0xc8f23a42U, 0x6bbad1e1U, 0x5d035ed7U, 0x1bd6cd91U,
+	0x405818caU, 0xdb26fd51U, 0x9df36e17U, 0x282a02a2U,
+	0x4d175ac7U, 0x13dccf99U, 0xd5a97c5fU, 0x504c1cdaU,
+	0xf701f67dU, 0x876dea0dU, 0x4c571bc6U, 0x89e26b03U,
+	0x6a2943cdU, 0x737704d4U, 0x96ea7c31U, 0xcaa16b6dU,
+	0x025b59a5U, 0xb6c27411U, 0xc56eab62U, 0xefcc2348U,
+	0x69e980ceU, 0xebc9224cU, 0x61e382c6U, 0x3a6d579dU,
+	0x912fbe36U, 0xdeb06e79U, 0x813bba26U, 0x25b69382U,
+	0x9d20bd3aU, 0xac5cf00bU, 0x5b550efcU, 0xe809e14fU,
+	0x9925bc3eU, 0xcbe12a6cU, 0x21b39286U, 0x3e685699U,
+	0x9ba53e3cU, 0x20f3d387U, 0x7df885daU, 0xa919b00eU,
+	0x14cadeb3U, 0x2b39128cU, 0x29b9908eU, 0x575a0df0U,
+	0x434b08e4U, 0x38edd59fU, 0x70b7c7d7U, 0xe2836145U,
+	0x777205d0U, 0x8ef47a29U, 0x535f0cf4U, 0xd835ed7fU,
+	0xfd58a55aU, 0x60a3c3c7U, 0x1d809dbaU, 0x3ce8d49bU,
+	0x86fe7821U, 0x0e545aa9U, 0x83bb3824U, 0xb84df51fU,
+	0xa2d37105U, 0xffd82758U, 0x41cb8ae6U, 0x97aa3d30U,
+	0x164a5cb1U, 0x118f9eb6U, 0x92ef7d35U, 0x676601c0U,
+	0x33271494U, 0x4e044ae9U, 0x636300c4U, 0xdab56f7dU,
+	0xe406e243U, 0x0f141ba8U, 0xcd64a96aU, 0xab99320cU,
+	0x2cfcd08bU, 0x2dbc918aU, 0xaf9c3308U, 0xc2ab6965U,
+	0xfe986659U, 0xfc18e45bU, 0x6dec81caU, 0xdbf52e7cU,
+	0x3f281798U, 0x549acef3U, 0xc16baa66U, 0x55da8ff2U,
+	0xaad9730dU, 0x27361180U, 0x0cd4d8abU, 0x93af3c34U,
+	0x906fff37U, 0x9c60fc3bU, 0x37221590U, 0xfbdd265cU,
+	0xf412e653U, 0xa053f307U, 0x5a154ffdU, 0x1f001fb8U,
+	0xd2bf6d75U, 0x9ee07e39U, 0x0d9499aaU, 0x1cc0dcbbU,
+	0xf017e757U, 0x68a9c1cfU, 0x474e09e0U, 0xe546a342U,
+	0x49c188eeU, 0xa113b206U, 0x099198aeU, 0x4881c9efU,
+	0xbc48f41bU, 0x509fcff7U, 0x763244d1U, 0xbb8d361cU,
+	0xa516b302U, 0xb90db41eU, 0xc02beb67U, 0xdd70ad7aU,
+	0x35a29792U, 0xb502b712U, 0xd03fef77U, 0x2f3c1388U,
+	0x622341c5U, 0x31a79696U, 0x28f9d18fU, 0x6cacc0cbU,
+	0x4f440be8U, 0x7e3846d9U, 0xad1cb10aU, 0x78bdc5dfU,
+	0x00dbdba7U, 0xe949a04eU, 0x39ad949eU, 0xaedc7209U,
+	0xb3873414U, 0x22735185U, 0x124f5db5U, 0x4b410aecU,
+	0x9ae57f3dU, 0xcfe42b68U, 0x1b051ebcU, 0x7a3d47ddU,
+	0x30e7d797U, 0x2e7c5289U, 0xd17fae76U, 0xe7c62140U,
+	0xbf883718U, 0xee8c6249U, 0xfa9d675dU, 0x0a515badU,
+	0xd6ba6c71U, 0xb442f613U, 0x65e683c2U, 0xb7823510U,
+	0xb2c77515U, 0x8bb13a2cU, 0x448ecae3U, 0xa3933004U,
+	0x5f500ff8U, 0xe143a246U, 0x59d58cfeU, 0x64a6c2c3U,
+	0x87be3920U, 0x7f7807d8U, 0xf95da45eU, 0xf157a656U,
+	0xe6866041U, 0x031b18a4U, 0xf7d22550U, 0xbec87619U,
+	0x45ce8be2U, 0xea89634dU, 0xbacd771dU, 0x8931b82eU,
+	0x3b2d169cU, 0xa859f10fU, 0x1a455fbdU, 0xb047f717U,
+	0x32675595U, 0x952abf32U, 0xbd08b51aU, 0x9fa03f38U,
+	0x059e9ba2U, 0x7cb8c4dbU, 0x08d1d9afU, 0x8c74f82bU,
+	0x662640c1U, 0x5e104ef9U, 0xed4ca14aU, 0x24f6d283U,
+	0x4a014bedU, 0xc6ae6861U, 0x0b111aacU, 0x8d34b92aU,
+	0x3da8959aU, 0xcea46a69U, 0x9865fd3fU, 0xf81de55fU,
+	0x847efa23U, 0x807bfb27U, 0x5c90ccfbU, 0x74b2c6d3U,
+	0x071e19a0U, 0x5dd08dfaU, 0x8fb43b28U, 0x7b7d06dcU,
+	0xdff02f78U, 0x460e48e1U, 0xc3eb2864U, 0xcc24e86bU,
+	0x75f287d2U, 0x561a4cf1U, 0x408bcbe7U, 0x34e2d693U,
+	0x4dc489eaU, 0x71f786d6U, 0x5895cdffU, 0x521f4df5U,
+	0x19859cbeU, 0x36625491U, 0xf552a752U, 0x130f1cb4U,
+	0xa7963100U, 0x065e58a1U, 0xdc30ec7bU, 0xc7ee2960U,
+	0x853ebb22U, 0xe003e347U, 0x723745d5U, 0x79fd84deU,
+	0xd3ff2c74U, 0xa6d67001U, 0xe3c32044U, 0xf3d72454U,
+	0x170a1db0U, 0x946afe33U, 0x6f6c03c8U, 0x18c5ddbfU,
+	0x019b9aa6U, 0x6b6902ccU, 0xec0ce04bU, 0xc821e96fU,
+	0xa456f203U, 0xd7fa2d70U, 0xf6926451U, 0x158a9fb2U,
+	0x26765081U, 0x8871f92fU, 0x2a79538dU, 0x23331084U,
+	0x51df8ef6U, 0xf2976555U, 0xc42eea63U, 0x82fb7925U,
+	0xd975ac7eU, 0x420b49e5U, 0x04dedaa3U, 0xb107b616U,
+	0xd43aee73U, 0x8af17b2dU, 0x4c84c8ebU, 0xc961a86eU,
+	0x6e2c42c9U, 0x1e405eb9U, 0xd57aaf72U, 0x10cfdfb7U,
+	0xa240e26cU, 0xbb1ea575U, 0x5e83dd90U, 0x02c8caccU,
+	0xca32f804U, 0x7eabd5b0U, 0x0d070ac3U, 0x27a582e9U,
+	0xa180216fU, 0x23a083edU, 0xa98a2367U, 0xf204f63cU,
+	0x59461f97U, 0x16d9cfd8U, 0x49521b87U, 0xeddf3223U,
+	0x55491c9bU, 0x643551aaU, 0x933caf5dU, 0x206040eeU,
+	0x514c1d9fU, 0x03888bcdU, 0xe9da3327U, 0xf601f738U,
+	0x53cc9f9dU, 0xe89a7226U, 0xb591247bU, 0x617011afU,
+	0xdca37f12U, 0xe350b32dU, 0xe1d0312fU, 0x9f33ac51U,
+	0x8b22a945U, 0xf084743eU, 0xb8de6676U, 0x2aeac0e4U,
+	0xbf1ba471U, 0x469ddb88U, 0x9b36ad55U, 0x105c4cdeU,
+	0x353104fbU, 0xa8ca6266U, 0xd5e93c1bU, 0xf481753aU,
+	0x4e97d980U, 0xc63dfb08U, 0x4bd29985U, 0x702454beU,
+	0x6abad0a4U, 0x37b186f9U, 0x89a22b47U, 0x5fc39c91U,
+	0xde23fd10U, 0xd9e63f17U, 0x5a86dc94U, 0xaf0fa061U,
+	0xfb4eb535U, 0x866deb48U, 0xab0aa165U, 0x12dccedcU,
+	0x2c6f43e2U, 0xc77dba09U, 0x050d08cbU, 0x63f093adU,
+	0xe495712aU, 0xe5d5302bU, 0x67f592a9U, 0x0ac2c8c4U,
+	0x36f1c7f8U, 0x347145faU, 0xa585206bU, 0x139c8fddU,
+	0xf741b639U, 0x9cf36f52U, 0x09020bc7U, 0x9db32e53U,
+	0x62b0d2acU, 0xef5fb021U, 0xc4bd790aU, 0x5bc69d95U,
+	0x58065e96U, 0x54095d9aU, 0xff4bb431U, 0x33b487fdU,
+	0x3c7b47f2U, 0x683a52a6U, 0x927cee5cU, 0xd769be19U,
+	0x1ad6ccd4U, 0x5689df98U, 0xc5fd380bU, 0xd4a97d1aU,
+	0x387e46f6U, 0xa0c0606eU, 0x8f27a841U, 0x2d2f02e3U,
+	0x81a8294fU, 0x697a13a7U, 0xc1f8390fU, 0x80e8684eU,
+	0x742155baU, 0x98f66e56U, 0xbe5be570U, 0x73e497bdU,
+	0x6d7f12a3U, 0x716415bfU, 0x08424ac6U, 0x15190cdbU,
+	0xfdcb3633U, 0x7d6b16b3U, 0x18564ed6U, 0xe755b229U,
+	0xaa4ae064U, 0xf9ce3737U, 0xe090702eU, 0xa4c5616aU,
+	0x872daa49U, 0xb651e778U, 0x657510abU, 0xb0d4647eU,
+	0xc8b27a06U, 0x212001efU, 0xf1c4353fU, 0x66b5d3a8U,
+	0x7bee95b5U, 0xea1af024U, 0xda26fc14U, 0x8328ab4dU,
+	0x528cde9cU, 0x078d8ac9U, 0xd36cbf1dU, 0xb254e67cU,
+	0xf88e7636U, 0xe615f328U, 0x19160fd7U, 0x2faf80e1U,
+	0x77e196b9U, 0x26e5c3e8U, 0x32f4c6fcU, 0xc238fa0cU,
+	0x1ed3cdd0U, 0x7c2b57b2U, 0xad8f2263U, 0x7feb94b1U,
+	0x7aaed4b4U, 0x43d89b8dU, 0x8ce76b42U, 0x6bfa91a5U,
+	0x9739ae59U, 0x292a03e7U, 0x91bc2d5fU, 0xaccf6362U,
+	0x4fd79881U, 0xb711a679U, 0x313405ffU, 0x393e07f7U,
+	0x2eefc1e0U, 0xcb72b905U, 0x3fbb84f1U, 0x76a1d7b8U,
+	0x8da72a43U, 0x22e0c2ecU, 0x72a4d6bcU, 0x4158198fU,
+	0xf344b73dU, 0x603050aeU, 0xd22cfe1cU, 0x782e56b6U,
+	0xfa0ef434U, 0x5d431e93U, 0x756114bbU, 0x57c99e99U,
+	0xcdf73a03U, 0xb4d1657aU, 0xc0b8780eU, 0x441d598aU,
+	0xae4fe160U, 0x9679ef58U, 0x252500ebU, 0xec9f7322U,
+	0x8268ea4cU, 0x0ec7c9c0U, 0xc378bb0dU, 0x455d188bU,
+	0xf5c1343bU, 0x06cdcbc8U, 0x500c5c9eU, 0x307444feU,
+	0x4c175b82U, 0x48125a86U, 0x94f96d5aU, 0xbcdb6772U,
+	0xcf77b801U, 0x95b92c5bU, 0x47dd9a89U, 0xb314a77dU,
+	0x17998ed9U, 0x8e67e940U, 0x0b8289c5U, 0x044d49caU,
+	0xbd9b2673U, 0x9e73ed50U, 0x88e26a46U, 0xfc8b7732U,
+	0x85ad284bU, 0xb99e2777U, 0x90fc6c5eU, 0x9a76ec54U,
+	0xd1ec3d1fU, 0xfe0bf530U, 0x3d3b06f3U, 0xdb66bd15U,
+	0x6fff90a1U, 0xce37f900U, 0x14594ddaU, 0x0f8788c1U,
+	0x4d571a83U, 0x286a42e6U, 0xba5ee474U, 0xb194257fU,
+	0x1b968dd5U, 0x6ebfd1a0U, 0x2baa81e5U, 0x3bbe85f5U,
+	0xdf63bc11U, 0x5c035f92U, 0xa705a269U, 0xd0ac7c1eU,
+	0xc9f23b07U, 0xa300a36dU, 0x246541eaU, 0x004848ceU,
+	0x6c3f53a2U, 0x1f938cd1U, 0x3efbc5f0U, 0xdde33e13U,
+	0xee1ff120U, 0x4018588eU, 0xe210f22cU, 0xeb5ab125U,
+	0x99b62f57U, 0x3afec4f4U, 0x0c474bc2U, 0x4a92d884U,
+	0x111c0ddfU, 0x8a62e844U, 0xccb77b02U, 0x796e17b7U,
+	0x1c534fd2U, 0x4298da8cU, 0x84ed694aU, 0x010809cfU,
+	0xa645e368U, 0xd629ff18U, 0x1d130ed3U, 0xd8a67e16U,
+	0xccf438b6U, 0xd5aa7fafU, 0x3037074aU, 0x6c7c1016U,
+	0xa48622deU, 0x101f0f6aU, 0x63b3d019U, 0x49115833U,
+	0xcf34fbb5U, 0x4d145937U, 0xc73ef9bdU, 0x9cb02ce6U,
+	0x37f2c54dU, 0x786d1502U, 0x27e6c15dU, 0x836be8f9U,
+	0x3bfdc641U, 0x0a818b70U, 0xfd887587U, 0x4ed49a34U,
+	0x3ff8c745U, 0x6d3c5117U, 0x876ee9fdU, 0x98b52de2U,
+	0x3d784547U, 0x862ea8fcU, 0xdb25fea1U, 0x0fc4cb75U,
+	0xb217a5c8U, 0x8de469f7U, 0x8f64ebf5U, 0xf187768bU,
+	0xe596739fU, 0x9e30aee4U, 0xd66abcacU, 0x445e1a3eU,
+	0xd1af7eabU, 0x28290152U, 0xf582778fU, 0x7ee89604U,
+	0x5b85de21U, 0xc67eb8bcU, 0xbb5de6c1U, 0x9a35afe0U,
+	0x2023035aU, 0xa88921d2U, 0x2566435fU, 0x1e908e64U,
+	0x040e0a7eU, 0x59055c23U, 0xe716f19dU, 0x3177464bU,
+	0xb09727caU, 0xb752e5cdU, 0x3432064eU, 0xc1bb7abbU,
+	0x95fa6fefU, 0xe8d93192U, 0xc5be7bbfU, 0x7c681406U,
+	0x42db9938U, 0xa9c960d3U, 0x6bb9d211U, 0x0d444977U,
+	0x8a21abf0U, 0x8b61eaf1U, 0x09414873U, 0x6476121eU,
+	0x58451d22U, 0x5ac59f20U, 0xcb31fab1U, 0x7d285507U,
+	0x99f56ce3U, 0xf247b588U, 0x67b6d11dU, 0xf307f489U,
+	0x0c040876U, 0x81eb6afbU, 0xaa09a3d0U, 0x3572474fU,
+	0x36b2844cU, 0x3abd8740U, 0x91ff6eebU, 0x5d005d27U,
+	0x52cf9d28U, 0x068e887cU, 0xfcc83486U, 0xb9dd64c3U,
+	0x7462160eU, 0x383d0542U, 0xab49e2d1U, 0xba1da7c0U,
+	0x56ca9c2cU, 0xce74bab4U, 0xe193729bU, 0x439bd839U,
+	0xef1cf395U, 0x07cec97dU, 0xaf4ce3d5U, 0xee5cb294U,
+	0x1a958f60U, 0xf642b48cU, 0xd0ef3faaU, 0x1d504d67U,
+	0x03cbc879U, 0x1fd0cf65U, 0x66f6901cU, 0x7badd601U,
+	0x937fece9U, 0x13dfcc69U, 0x76e2940cU, 0x89e168f3U,
+	0xc4fe3abeU, 0x977aededU, 0x8e24aaf4U, 0xca71bbb0U,
+	0xe9997093U, 0xd8e53da2U, 0x0bc1ca71U, 0xde60bea4U,
+	0xa606a0dcU, 0x4f94db35U, 0x9f70efe5U, 0x08010972U,
+	0x155a4f6fU, 0x84ae2afeU, 0xb49226ceU, 0xed9c7197U,
+	0x3c380446U, 0x69395013U, 0xbdd865c7U, 0xdce03ca6U,
+	0x963aacecU, 0x88a129f2U, 0x77a2d50dU, 0x411b5a3bU,
+	0x19554c63U, 0x48511932U, 0x5c401c26U, 0xac8c20d6U,
+	0x7067170aU, 0x129f8d68U, 0xc33bf8b9U, 0x115f4e6bU,
+	0x141a0e6eU, 0x2d6c4157U, 0xe253b198U, 0x054e4b7fU,
+	0xf98d7483U, 0x479ed93dU, 0xff08f785U, 0xc27bb9b8U,
+	0x2163425bU, 0xd9a57ca3U, 0x5f80df25U, 0x578add2dU,
+	0x405b1b3aU, 0xa5c663dfU, 0x510f5e2bU, 0x18150d62U,
+	0xe313f099U, 0x4c541836U, 0x1c100c66U, 0x2fecc355U,
+	0x9df06de7U, 0x0e848a74U, 0xbc9824c6U, 0x169a8c6cU,
+	0x94ba2eeeU, 0x33f7c449U, 0x1bd5ce61U, 0x397d4443U,
+	0xa343e0d9U, 0xda65bfa0U, 0xae0ca2d4U, 0x2aa98350U,
+	0xc0fb3bbaU, 0xf8cd3582U, 0x4b91da31U, 0x822ba9f8U,
+	0xecdc3096U, 0x6073131aU, 0xadcc61d7U, 0x2be9c251U,
+	0x9b75eee1U, 0x68791112U, 0x3eb88644U, 0x5ec09e24U,
+	0x22a38158U, 0x26a6805cU, 0xfa4db780U, 0xd26fbda8U,
+	0xa1c362dbU, 0xfb0df681U, 0x29694053U, 0xdda07da7U,
+	0x792d5403U, 0xe0d3339aU, 0x6536531fU, 0x6af99310U,
+	0xd32ffca9U, 0xf0c7378aU, 0xe656b09cU, 0x923fade8U,
+	0xeb19f291U, 0xd72afdadU, 0xfe48b684U, 0xf4c2368eU,
+	0xbf58e7c5U, 0x90bf2feaU, 0x538fdc29U, 0xb5d267cfU,
+	0x014b4a7bU, 0xa08323daU, 0x7aed9700U, 0x6133521bU,
+	0x23e3c059U, 0x46de983cU, 0xd4ea3eaeU, 0xdf20ffa5U,
+	0x7522570fU, 0x000b0b7aU, 0x451e5b3fU, 0x550a5f2fU,
+	0xb1d766cbU, 0x32b78548U, 0xc9b178b3U, 0xbe18a6c4U,
+	0xa746e1ddU, 0xcdb479b7U, 0x4ad19b30U, 0x6efc9214U,
+	0x028b8978U, 0x7127560bU, 0x504f1f2aU, 0xb357e4c9U,
+	0x80ab2bfaU, 0x2eac8254U, 0x8ca428f6U, 0x85ee6bffU,
+	0xf702f58dU, 0x544a1e2eU, 0x62f39118U, 0x2426025eU,
+	0x7fa8d705U, 0xe4d6329eU, 0xa203a1d8U, 0x17dacd6dU,
+	0x72e79508U, 0x2c2c0056U, 0xea59b390U, 0x6fbcd315U,
+	0xc8f139b2U, 0xb89d25c2U, 0x73a7d409U, 0xb612a4ccU,
+	0x123123adU, 0x0b6f64b4U, 0xeef21c51U, 0xb2b90b0dU,
+	0x7a4339c5U, 0xceda1471U, 0xbd76cb02U, 0x97d44328U,
+	0x11f1e0aeU, 0x93d1422cU, 0x19fbe2a6U, 0x427537fdU,
+	0xe937de56U, 0xa6a80e19U, 0xf923da46U, 0x5daef3e2U,
+	0xe538dd5aU, 0xd444906bU, 0x234d6e9cU, 0x9011812fU,
+	0xe13ddc5eU, 0xb3f94a0cU, 0x59abf2e6U, 0x467036f9U,
+	0xe3bd5e5cU, 0x58ebb3e7U, 0x05e0e5baU, 0xd101d06eU,
+	0x6cd2bed3U, 0x532172ecU, 0x51a1f0eeU, 0x2f426d90U,
+	0x3b536884U, 0x40f5b5ffU, 0x08afa7b7U, 0x9a9b0125U,
+	0x0f6a65b0U, 0xf6ec1a49U, 0x2b476c94U, 0xa02d8d1fU,
+	0x8540c53aU, 0x18bba3a7U, 0x6598fddaU, 0x44f0b4fbU,
+	0xfee61841U, 0x764c3ac9U, 0xfba35844U, 0xc055957fU,
+	0xdacb1165U, 0x87c04738U, 0x39d3ea86U, 0xefb25d50U,
+	0x6e523cd1U, 0x6997fed6U, 0xeaf71d55U, 0x1f7e61a0U,
+	0x4b3f74f4U, 0x361c2a89U, 0x1b7b60a4U, 0xa2ad0f1dU,
+	0x9c1e8223U, 0x770c7bc8U, 0xb57cc90aU, 0xd381526cU,
+	0x54e4b0ebU, 0x55a4f1eaU, 0xd7845368U, 0xbab30905U,
+	0x86800639U, 0x8400843bU, 0x15f4e1aaU, 0xa3ed4e1cU,
+	0x473077f8U, 0x2c82ae93U, 0xb973ca06U, 0x2dc2ef92U,
+	0xd2c1136dU, 0x5f2e71e0U, 0x74ccb8cbU, 0xebb75c54U,
+	0xe8779f57U, 0xe4789c5bU, 0x4f3a75f0U, 0x83c5463cU,
+	0x8c0a8633U, 0xd84b9367U, 0x220d2f9dU, 0x67187fd8U,
+	0xaaa70d15U, 0xe6f81e59U, 0x758cf9caU, 0x64d8bcdbU,
+	0x880f8737U, 0x10b1a1afU, 0x3f566980U, 0x9d5ec322U,
+	0x31d9e88eU, 0xd90bd266U, 0x7189f8ceU, 0x3099a98fU,
+	0xc450947bU, 0x2887af97U, 0x0e2a24b1U, 0xc395567cU,
+	0xdd0ed362U, 0xc115d47eU, 0xb8338b07U, 0xa568cd1aU,
+	0x4dbaf7f2U, 0xcd1ad772U, 0xa8278f17U, 0x572473e8U,
+	0x1a3b21a5U, 0x49bff6f6U, 0x50e1b1efU, 0x14b4a0abU,
+	0x375c6b88U, 0x062026b9U, 0xd504d16aU, 0x00a5a5bfU,
+	0x78c3bbc7U, 0x9151c02eU, 0x41b5f4feU, 0xd6c41269U,
+	0xcb9f5474U, 0x5a6b31e5U, 0x6a573dd5U, 0x33596a8cU,
+	0xe2fd1f5dU, 0xb7fc4b08U, 0x631d7edcU, 0x022527bdU,
+	0x48ffb7f7U, 0x566432e9U, 0xa967ce16U, 0x9fde4120U,
+	0xc7905778U, 0x96940229U, 0x8285073dU, 0x72493bcdU,
+	0xaea20c11U, 0xcc5a9673U, 0x1dfee3a2U, 0xcf9a5570U,
+	0xcadf1575U, 0xf3a95a4cU, 0x3c96aa83U, 0xdb8b5064U,
+	0x27486f98U, 0x995bc226U, 0x21cdec9eU, 0x1cbea2a3U,
+	0xffa65940U, 0x076067b8U, 0x8145c43eU, 0x894fc636U,
+	0x9e9e0021U, 0x7b0378c4U, 0x8fca4530U, 0xc6d01679U,
+	0x3dd6eb82U, 0x9291032dU, 0xc2d5177dU, 0xf129d84eU,
+	0x433576fcU, 0xd041916fU, 0x625d3fddU, 0xc85f9777U,
+	0x4a7f35f5U, 0xed32df52U, 0xc510d57aU, 0xe7b85f58U,
+	0x7d86fbc2U, 0x04a0a4bbU, 0x70c9b9cfU, 0xf46c984bU,
+	0x1e3e20a1U, 0x26082e99U, 0x9554c12aU, 0x5ceeb2e3U,
+	0x32192b8dU, 0xbeb60801U, 0x73097accU, 0xf52cd94aU,
+	0x45b0f5faU, 0xb6bc0a09U, 0xe07d9d5fU, 0x8005853fU,
+	0xfc669a43U, 0xf8639b47U, 0x2488ac9bU, 0x0caaa6b3U,
+	0x7f0679c0U, 0x25c8ed9aU, 0xf7ac5b48U, 0x036566bcU,
+	0xa7e84f18U, 0x3e162881U, 0xbbf34804U, 0xb43c880bU,
+	0x0deae7b2U, 0x2e022c91U, 0x3893ab87U, 0x4cfab6f3U,
+	0x35dce98aU, 0x09efe6b6U, 0x208dad9fU, 0x2a072d95U,
+	0x619dfcdeU, 0x4e7a34f1U, 0x8d4ac732U, 0x6b177cd4U,
+	0xdf8e5160U, 0x7e4638c1U, 0xa4288c1bU, 0xbff64900U,
+	0xfd26db42U, 0x981b8327U, 0x0a2f25b5U, 0x01e5e4beU,
+	0xabe74c14U, 0xdece1061U, 0x9bdb4024U, 0x8bcf4434U,
+	0x6f127dd0U, 0xec729e53U, 0x177463a8U, 0x60ddbddfU,
+	0x7983fac6U, 0x137162acU, 0x9414802bU, 0xb039890fU,
+	0xdc4e9263U, 0xafe24d10U, 0x8e8a0431U, 0x6d92ffd2U,
+	0x5e6e30e1U, 0xf069994fU, 0x526133edU, 0x5b2b70e4U,
+	0x29c7ee96U, 0x8a8f0535U, 0xbc368a03U, 0xfae31945U,
+	0xa16dcc1eU, 0x3a132985U, 0x7cc6bac3U, 0xc91fd676U,
+	0xac228e13U, 0xf2e91b4dU, 0x349ca88bU, 0xb179c80eU,
+	0x163422a9U, 0x66583ed9U, 0xad62cf12U, 0x68d7bfd7U,
+	0xc2a361efU, 0xdbfd26f6U, 0x3e605e13U, 0x622b494fU,
+	0xaad17b87U, 0x1e485633U, 0x6de48940U, 0x4746016aU,
+	0xc163a2ecU, 0x4343006eU, 0xc969a0e4U, 0x92e775bfU,
+	0x39a59c14U, 0x763a4c5bU, 0x29b19804U, 0x8d3cb1a0U,
+	0x35aa9f18U, 0x04d6d229U, 0xf3df2cdeU, 0x4083c36dU,
+	0x31af9e1cU, 0x636b084eU, 0x8939b0a4U, 0x96e274bbU,
+	0x332f1c1eU, 0x8879f1a5U, 0xd572a7f8U, 0x0193922cU,
+	0xbc40fc91U, 0x83b330aeU, 0x8133b2acU, 0xffd02fd2U,
+	0xebc12ac6U, 0x9067f7bdU, 0xd83de5f5U, 0x4a094367U,
+	0xdff827f2U, 0x267e580bU, 0xfbd52ed6U, 0x70bfcf5dU,
+	0x55d28778U, 0xc829e1e5U, 0xb50abf98U, 0x9462f6b9U,
+	0x2e745a03U, 0xa6de788bU, 0x2b311a06U, 0x10c7d73dU,
+	0x0a595327U, 0x5752057aU, 0xe941a8c4U, 0x3f201f12U,
+	0xbec07e93U, 0xb905bc94U, 0x3a655f17U, 0xcfec23e2U,
+	0x9bad36b6U, 0xe68e68cbU, 0xcbe922e6U, 0x723f4d5fU,
+	0x4c8cc061U, 0xa79e398aU, 0x65ee8b48U, 0x0313102eU,
+	0x8476f2a9U, 0x8536b3a8U, 0x0716112aU, 0x6a214b47U,
+	0x5612447bU, 0x5492c679U, 0xc566a3e8U, 0x737f0c5eU,
+	0x97a235baU, 0xfc10ecd1U, 0x69e18844U, 0xfd50add0U,
+	0x0253512fU, 0x8fbc33a2U, 0xa45efa89U, 0x3b251e16U,
+	0x38e5dd15U, 0x34eade19U, 0x9fa837b2U, 0x5357047eU,
+	0x5c98c471U, 0x08d9d125U, 0xf29f6ddfU, 0xb78a3d9aU,
+	0x7a354f57U, 0x366a5c1bU, 0xa51ebb88U, 0xb44afe99U,
+	0x589dc575U, 0xc023e3edU, 0xefc42bc2U, 0x4dcc8160U,
+	0xe14baaccU, 0x09999024U, 0xa11bba8cU, 0xe00bebcdU,
+	0x14c2d639U, 0xf815edd5U, 0xdeb866f3U, 0x1307143eU,
+	0x0d9c9120U, 0x1187963cU, 0x68a1c945U, 0x75fa8f58U,
+	0x9d28b5b0U, 0x1d889530U, 0x78b5cd55U, 0x87b631aaU,
+	0xcaa963e7U, 0x992db4b4U, 0x8073f3adU, 0xc426e2e9U,
+	0xe7ce29caU, 0xd6b264fbU, 0x05969328U, 0xd037e7fdU,
+	0xa851f985U, 0x41c3826cU, 0x9127b6bcU, 0x0656502bU,
+	0x1b0d1636U, 0x8af973a7U, 0xbac57f97U, 0xe3cb28ceU,
+	0x326f5d1fU, 0x676e094aU, 0xb38f3c9eU, 0xd2b765ffU,
+	0x986df5b5U, 0x86f670abU, 0x79f58c54U, 0x4f4c0362U,
+	0x1702153aU, 0x4606406bU, 0x5217457fU, 0xa2db798fU,
+	0x7e304e53U, 0x1cc8d431U, 0xcd6ca1e0U, 0x1f081732U,
+	0x1a4d5737U, 0x233b180eU, 0xec04e8c1U, 0x0b191226U,
+	0xf7da2ddaU, 0x49c98064U, 0xf15faedcU, 0xcc2ce0e1U,
+	0x2f341b02U, 0xd7f225faU, 0x51d7867cU, 0x59dd8474U,
+	0x4e0c4263U, 0xab913a86U, 0x5f580772U, 0x1642543bU,
+	0xed44a9c0U, 0x4203416fU, 0x1247553fU, 0x21bb9a0cU,
+	0x93a734beU, 0x00d3d32dU, 0xb2cf7d9fU, 0x18cdd535U,
+	0x9aed77b7U, 0x3da09d10U, 0x15829738U, 0x372a1d1aU,
+	0xad14b980U, 0xd432e6f9U, 0xa05bfb8dU, 0x24feda09U,
+	0xceac62e3U, 0xf69a6cdbU, 0x45c68368U, 0x8c7cf0a1U,
+	0xe28b69cfU, 0x6e244a43U, 0xa39b388eU, 0x25be9b08U,
+	0x9522b7b8U, 0x662e484bU, 0x30efdf1dU, 0x5097c77dU,
+	0x2cf4d801U, 0x28f1d905U, 0xf41aeed9U, 0xdc38e4f1U,
+	0xaf943b82U, 0xf55aafd8U, 0x273e190aU, 0xd3f724feU,
+	0x777a0d5aU, 0xee846ac3U, 0x6b610a46U, 0x64aeca49U,
+	0xdd78a5f0U, 0xfe906ed3U, 0xe801e9c5U, 0x9c68f4b1U,
+	0xe54eabc8U, 0xd97da4f4U, 0xf01fefddU, 0xfa956fd7U,
+	0xb10fbe9cU, 0x9ee876b3U, 0x5dd88570U, 0xbb853e96U,
+	0x0f1c1322U, 0xaed47a83U, 0x74bace59U, 0x6f640b42U,
+	0x2db49900U, 0x4889c165U, 0xdabd67f7U, 0xd177a6fcU,
+	0x7b750e56U, 0x0e5c5223U, 0x4b490266U, 0x5b5d0676U,
+	0xbf803f92U, 0x3ce0dc11U, 0xc7e621eaU, 0xb04fff9dU,
+	0xa911b884U, 0xc3e320eeU, 0x4486c269U, 0x60abcb4dU,
+	0x0cdcd021U, 0x7f700f52U, 0x5e184673U, 0xbd00bd90U,
+	0x8efc72a3U, 0x20fbdb0dU, 0x82f371afU, 0x8bb932a6U,
+	0xf955acd4U, 0x5a1d4777U, 0x6ca4c841U, 0x2a715b07U,
+	0x71ff8e5cU, 0xea816bc7U, 0xac54f881U, 0x198d9434U,
+	0x7cb0cc51U, 0x227b590fU, 0xe40eeac9U, 0x61eb8a4cU,
+	0xc6a660ebU, 0xb6ca7c9bU, 0x7df08d50U, 0xb845fd95U,
+	0xfffb048aU, 0xe6a54393U, 0x03383b76U, 0x5f732c2aU,
+	0x97891ee2U, 0x23103356U, 0x50bcec25U, 0x7a1e640fU,
+	0xfc3bc789U, 0x7e1b650bU, 0xf431c581U, 0xafbf10daU,
+	0x04fdf971U, 0x4b62293eU, 0x14e9fd61U, 0xb064d4c5U,
+	0x08f2fa7dU, 0x398eb74cU, 0xce8749bbU, 0x7ddba608U,
+	0x0cf7fb79U, 0x5e336d2bU, 0xb461d5c1U, 0xabba11deU,
+	0x0e77797bU, 0xb52194c0U, 0xe82ac29dU, 0x3ccbf749U,
+	0x811899f4U, 0xbeeb55cbU, 0xbc6bd7c9U, 0xc2884ab7U,
+	0xd6994fa3U, 0xad3f92d8U, 0xe5658090U, 0x77512602U,
+	0xe2a04297U, 0x1b263d6eU, 0xc68d4bb3U, 0x4de7aa38U,
+	0x688ae21dU, 0xf5718480U, 0x8852dafdU, 0xa93a93dcU,
+	0x132c3f66U, 0x9b861deeU, 0x16697f63U, 0x2d9fb258U,
+	0x37013642U, 0x6a0a601fU, 0xd419cda1U, 0x02787a77U,
+	0x83981bf6U, 0x845dd9f1U, 0x073d3a72U, 0xf2b44687U,
+	0xa6f553d3U, 0xdbd60daeU, 0xf6b14783U, 0x4f67283aU,
+	0x71d4a504U, 0x9ac65cefU, 0x58b6ee2dU, 0x3e4b754bU,
+	0xb92e97ccU, 0xb86ed6cdU, 0x3a4e744fU, 0x57792e22U,
+	0x6b4a211eU, 0x69caa31cU, 0xf83ec68dU, 0x4e27693bU,
+	0xaafa50dfU, 0xc14889b4U, 0x54b9ed21U, 0xc008c8b5U,
+	0x3f0b344aU, 0xb2e456c7U, 0x99069fecU, 0x067d7b73U,
+	0x05bdb870U, 0x09b2bb7cU, 0xa2f052d7U, 0x6e0f611bU,
+	0x61c0a114U, 0x3581b440U, 0xcfc708baU, 0x8ad258ffU,
+	0x476d2a32U, 0x0b32397eU, 0x9846deedU, 0x89129bfcU,
+	0x65c5a010U, 0xfd7b8688U, 0xd29c4ea7U, 0x7094e405U,
+	0xdc13cfa9U, 0x34c1f541U, 0x9c43dfe9U, 0xdd538ea8U,
+	0x299ab35cU, 0xc54d88b0U, 0xe3e00396U, 0x2e5f715bU,
+	0x30c4f445U, 0x2cdff359U, 0x55f9ac20U, 0x48a2ea3dU,
+	0xa070d0d5U, 0x20d0f055U, 0x45eda830U, 0xbaee54cfU,
+	0xf7f10682U, 0xa475d1d1U, 0xbd2b96c8U, 0xf97e878cU,
+	0xda964cafU, 0xebea019eU, 0x38cef64dU, 0xed6f8298U,
+	0x95099ce0U, 0x7c9be709U, 0xac7fd3d9U, 0x3b0e354eU,
+	0x26557353U, 0xb7a116c2U, 0x879d1af2U, 0xde934dabU,
+	0x0f37387aU, 0x5a366c2fU, 0x8ed759fbU, 0xefef009aU,
+	0xa53590d0U, 0xbbae15ceU, 0x44ade931U, 0x72146607U,
+	0x2a5a705fU, 0x7b5e250eU, 0x6f4f201aU, 0x9f831ceaU,
+	0x43682b36U, 0x2190b154U, 0xf034c485U, 0x22507257U,
+	0x27153252U, 0x1e637d6bU, 0xd15c8da4U, 0x36417743U,
+	0xca8248bfU, 0x7491e501U, 0xcc07cbb9U, 0xf1748584U,
+	0x126c7e67U, 0xeaaa409fU, 0x6c8fe319U, 0x6485e111U,
+	0x73542706U, 0x96c95fe3U, 0x62006217U, 0x2b1a315eU,
+	0xd01ccca5U, 0x7f5b240aU, 0x2f1f305aU, 0x1ce3ff69U,
+	0xaeff51dbU, 0x3d8bb648U, 0x8f9718faU, 0x2595b050U,
+	0xa7b512d2U, 0x00f8f875U, 0x28daf25dU, 0x0a72787fU,
+	0x904cdce5U, 0xe96a839cU, 0x9d039ee8U, 0x19a6bf6cU,
+	0xf3f40786U, 0xcbc209beU, 0x789ee60dU, 0xb12495c4U,
+	0xdfd30caaU, 0x537c2f26U, 0x9ec35debU, 0x18e6fe6dU,
+	0xa87ad2ddU, 0x5b762d2eU, 0x0db7ba78U, 0x6dcfa218U,
+	0x11acbd64U, 0x15a9bc60U, 0xc9428bbcU, 0xe1608194U,
+	0x92cc5ee7U, 0xc802cabdU, 0x1a667c6fU, 0xeeaf419bU,
+	0x4a22683fU, 0xd3dc0fa6U, 0x56396f23U, 0x59f6af2cU,
+	0xe020c095U, 0xc3c80bb6U, 0xd5598ca0U, 0xa13091d4U,
+	0xd816ceadU, 0xe425c191U, 0xcd478ab8U, 0xc7cd0ab2U,
+	0x8c57dbf9U, 0xa3b013d6U, 0x6080e015U, 0x86dd5bf3U,
+	0x32447647U, 0x938c1fe6U, 0x49e2ab3cU, 0x523c6e27U,
+	0x10ecfc65U, 0x75d1a400U, 0xe7e50292U, 0xec2fc399U,
+	0x462d6b33U, 0x33043746U, 0x76116703U, 0x66056313U,
+	0x82d85af7U, 0x01b8b974U, 0xfabe448fU, 0x8d179af8U,
+	0x9449dde1U, 0xfebb458bU, 0x79dea70cU, 0x5df3ae28U,
+	0x3184b544U, 0x42286a37U, 0x63402316U, 0x8058d8f5U,
+	0xb3a417c6U, 0x1da3be68U, 0xbfab14caU, 0xb6e157c3U,
+	0xc40dc9b1U, 0x67452212U, 0x51fcad24U, 0x17293e62U,
+	0x4ca7eb39U, 0xd7d90ea2U, 0x910c9de4U, 0x24d5f151U,
+	0x41e8a934U, 0x1f233c6aU, 0xd9568facU, 0x5cb3ef29U,
+	0xfbfe058eU, 0x8b9219feU, 0x40a8e835U, 0x851d98f0U,
+	0xcb5d9618U, 0xd203d101U, 0x379ea9e4U, 0x6bd5beb8U,
+	0xa32f8c70U, 0x17b6a1c4U, 0x641a7eb7U, 0x4eb8f69dU,
+	0xc89d551bU, 0x4abdf799U, 0xc0975713U, 0x9b198248U,
+	0x305b6be3U, 0x7fc4bbacU, 0x204f6ff3U, 0x84c24657U,
+	0x3c5468efU, 0x0d2825deU, 0xfa21db29U, 0x497d349aU,
+	0x385169ebU, 0x6a95ffb9U, 0x80c74753U, 0x9f1c834cU,
+	0x3ad1ebe9U, 0x81870652U, 0xdc8c500fU, 0x086d65dbU,
+	0xb5be0b66U, 0x8a4dc759U, 0x88cd455bU, 0xf62ed825U,
+	0xe23fdd31U, 0x9999004aU, 0xd1c31202U, 0x43f7b490U,
+	0xd606d005U, 0x2f80affcU, 0xf22bd921U, 0x794138aaU,
+	0x5c2c708fU, 0xc1d71612U, 0xbcf4486fU, 0x9d9c014eU,
+	0x278aadf4U, 0xaf208f7cU, 0x22cfedf1U, 0x193920caU,
+	0x03a7a4d0U, 0x5eacf28dU, 0xe0bf5f33U, 0x36dee8e5U,
+	0xb73e8964U, 0xb0fb4b63U, 0x339ba8e0U, 0xc612d415U,
+	0x9253c141U, 0xef709f3cU, 0xc217d511U, 0x7bc1baa8U,
+	0x45723796U, 0xae60ce7dU, 0x6c107cbfU, 0x0aede7d9U,
+	0x8d88055eU, 0x8cc8445fU, 0x0ee8e6ddU, 0x63dfbcb0U,
+	0x5fecb38cU, 0x5d6c318eU, 0xcc98541fU, 0x7a81fba9U,
+	0x9e5cc24dU, 0xf5ee1b26U, 0x601f7fb3U, 0xf4ae5a27U,
+	0x0bada6d8U, 0x8642c455U, 0xada00d7eU, 0x32dbe9e1U,
+	0x311b2ae2U, 0x3d1429eeU, 0x9656c045U, 0x5aa9f389U,
+	0x55663386U, 0x012726d2U, 0xfb619a28U, 0xbe74ca6dU,
+	0x73cbb8a0U, 0x3f94abecU, 0xace04c7fU, 0xbdb4096eU,
+	0x51633282U, 0xc9dd141aU, 0xe63adc35U, 0x44327697U,
+	0xe8b55d3bU, 0x006767d3U, 0xa8e54d7bU, 0xe9f51c3aU,
+	0x1d3c21ceU, 0xf1eb1a22U, 0xd7469104U, 0x1af9e3c9U,
+	0x046266d7U, 0x187961cbU, 0x615f3eb2U, 0x7c0478afU,
+	0x94d64247U, 0x147662c7U, 0x714b3aa2U, 0x8e48c65dU,
+	0xc3579410U, 0x90d34343U, 0x898d045aU, 0xcdd8151eU,
+	0xee30de3dU, 0xdf4c930cU, 0x0c6864dfU, 0xd9c9100aU,
+	0xa1af0e72U, 0x483d759bU, 0x98d9414bU, 0x0fa8a7dcU,
+	0x12f3e1c1U, 0x83078450U, 0xb33b8860U, 0xea35df39U,
+	0x3b91aae8U, 0x6e90febdU, 0xba71cb69U, 0xdb499208U,
+	0x91930242U, 0x8f08875cU, 0x700b7ba3U, 0x46b2f495U,
+	0x1efce2cdU, 0x4ff8b79cU, 0x5be9b288U, 0xab258e78U,
+	0x77ceb9a4U, 0x153623c6U, 0xc4925617U, 0x16f6e0c5U,
+	0x13b3a0c0U, 0x2ac5eff9U, 0xe5fa1f36U, 0x02e7e5d1U,
+	0xfe24da2dU, 0x40377793U, 0xf8a1592bU, 0xc5d21716U,
+	0x26caecf5U, 0xde0cd20dU, 0x5829718bU, 0x50237383U,
+	0x47f2b594U, 0xa26fcd71U, 0x56a6f085U, 0x1fbca3ccU,
+	0xe4ba5e37U, 0x4bfdb698U, 0x1bb9a2c8U, 0x28456dfbU,
+	0x9a59c349U, 0x092d24daU, 0xbb318a68U, 0x113322c2U,
+	0x93138040U, 0x345e6ae7U, 0x1c7c60cfU, 0x3ed4eaedU,
+	0xa4ea4e77U, 0xddcc110eU, 0xa9a50c7aU, 0x2d002dfeU,
+	0xc7529514U, 0xff649b2cU, 0x4c38749fU, 0x85820756U,
+	0xeb759e38U, 0x67dabdb4U, 0xaa65cf79U, 0x2c406cffU,
+	0x9cdc404fU, 0x6fd0bfbcU, 0x391128eaU, 0x5969308aU,
+	0x250a2ff6U, 0x210f2ef2U, 0xfde4192eU, 0xd5c61306U,
+	0xa66acc75U, 0xfca4582fU, 0x2ec0eefdU, 0xda09d309U,
+	0x7e84faadU, 0xe77a9d34U, 0x629ffdb1U, 0x6d503dbeU,
+	0xd4865207U, 0xf76e9924U, 0xe1ff1e32U, 0x95960346U,
+	0xecb05c3fU, 0xd0835303U, 0xf9e1182aU, 0xf36b9820U,
+	0xb8f1496bU, 0x97168144U, 0x54267287U, 0xb27bc961U,
+	0x06e2e4d5U, 0xa72a8d74U, 0x7d4439aeU, 0x669afcb5U,
+	0x244a6ef7U, 0x41773692U, 0xd3439000U, 0xd889510bU,
+	0x728bf9a1U, 0x07a2a5d4U, 0x42b7f591U, 0x52a3f181U,
+	0xb67ec865U, 0x351e2be6U, 0xce18d61dU, 0xb9b1086aU,
+	0xa0ef4f73U, 0xca1dd719U, 0x4d78359eU, 0x69553cbaU,
+	0x052227d6U, 0x768ef8a5U, 0x57e6b184U, 0xb4fe4a67U,
+	0x87028554U, 0x29052cfaU, 0x8b0d8658U, 0x8247c551U,
+	0xf0ab5b23U, 0x53e3b080U, 0x655a3fb6U, 0x238facf0U,
+	0x780179abU, 0xe37f9c30U, 0xa5aa0f76U, 0x107363c3U,
+	0x754e3ba6U, 0x2b85aef8U, 0xedf01d3eU, 0x68157dbbU,
+	0xcf58971cU, 0xbf348b6cU, 0x740e7aa7U, 0xb1bb0a62U,
+	0x019c9d13U, 0x18c2da0aU, 0xfd5fa2efU, 0xa114b5b3U,
+	0x69ee877bU, 0xdd77aacfU, 0xaedb75bcU, 0x8479fd96U,
+	0x025c5e10U, 0x807cfc92U, 0x0a565c18U, 0x51d88943U,
+	0xfa9a60e8U, 0xb505b0a7U, 0xea8e64f8U, 0x4e034d5cU,
+	0xf69563e4U, 0xc7e92ed5U, 0x30e0d022U, 0x83bc3f91U,
+	0xf29062e0U, 0xa054f4b2U, 0x4a064c58U, 0x55dd8847U,
+	0xf010e0e2U, 0x4b460d59U, 0x164d5b04U, 0xc2ac6ed0U,
+	0x7f7f006dU, 0x408ccc52U, 0x420c4e50U, 0x3cefd32eU,
+	0x28fed63aU, 0x53580b41U, 0x1b021909U, 0x8936bf9bU,
+	0x1cc7db0eU, 0xe541a4f7U, 0x38ead22aU, 0xb38033a1U,
+	0x96ed7b84U, 0x0b161d19U, 0x76354364U, 0x575d0a45U,
+	0xed4ba6ffU, 0x65e18477U, 0xe80ee6faU, 0xd3f82bc1U,
+	0xc966afdbU, 0x946df986U, 0x2a7e5438U, 0xfc1fe3eeU,
+	0x7dff826fU, 0x7a3a4068U, 0xf95aa3ebU, 0x0cd3df1eU,
+	0x5892ca4aU, 0x25b19437U, 0x08d6de1aU, 0xb100b1a3U,
+	0x8fb33c9dU, 0x64a1c576U, 0xa6d177b4U, 0xc02cecd2U,
+	0x47490e55U, 0x46094f54U, 0xc429edd6U, 0xa91eb7bbU,
+	0x952db887U, 0x97ad3a85U, 0x06595f14U, 0xb040f0a2U,
+	0x549dc946U, 0x3f2f102dU, 0xaade74b8U, 0x3e6f512cU,
+	0xc16cadd3U, 0x4c83cf5eU, 0x67610675U, 0xf81ae2eaU,
+	0xfbda21e9U, 0xf7d522e5U, 0x5c97cb4eU, 0x9068f882U,
+	0x9fa7388dU, 0xcbe62dd9U, 0x31a09123U, 0x74b5c166U,
+	0xb90ab3abU, 0xf555a0e7U, 0x66214774U, 0x77750265U,
+	0x9ba23989U, 0x031c1f11U, 0x2cfbd73eU, 0x8ef37d9cU,
+	0x22745630U, 0xcaa66cd8U, 0x62244670U, 0x23341731U,
+	0xd7fd2ac5U, 0x3b2a1129U, 0x1d879a0fU, 0xd038e8c2U,
+	0xcea36ddcU, 0xd2b86ac0U, 0xab9e35b9U, 0xb6c573a4U,
+	0x5e17494cU, 0xdeb769ccU, 0xbb8a31a9U, 0x4489cd56U,
+	0x09969f1bU, 0x5a124848U, 0x434c0f51U, 0x07191e15U,
+	0x24f1d536U, 0x158d9807U, 0xc6a96fd4U, 0x13081b01U,
+	0x6b6e0579U, 0x82fc7e90U, 0x52184a40U, 0xc569acd7U,
+	0xd832eacaU, 0x49c68f5bU, 0x79fa836bU, 0x20f4d432U,
+	0xf150a1e3U, 0xa451f5b6U, 0x70b0c062U, 0x11889903U,
+	0x5b520949U, 0x45c98c57U, 0xbaca70a8U, 0x8c73ff9eU,
+	0xd43de9c6U, 0x8539bc97U, 0x9128b983U, 0x61e48573U,
+	0xbd0fb2afU, 0xdff728cdU, 0x0e535d1cU, 0xdc37ebceU,
+	0xd972abcbU, 0xe004e4f2U, 0x2f3b143dU, 0xc826eedaU,
+	0x34e5d126U, 0x8af67c98U, 0x32605220U, 0x0f131c1dU,
+	0xec0be7feU, 0x14cdd906U, 0x92e87a80U, 0x9ae27888U,
+	0x8d33be9fU, 0x68aec67aU, 0x9c67fb8eU, 0xd57da8c7U,
+	0x2e7b553cU, 0x813cbd93U, 0xd178a9c3U, 0xe28466f0U,
+	0x5098c842U, 0xc3ec2fd1U, 0x71f08163U, 0xdbf229c9U,
+	0x59d28b4bU, 0xfe9f61ecU, 0xd6bd6bc4U, 0xf415e1e6U,
+	0x6e2b457cU, 0x170d1a05U, 0x63640771U, 0xe7c126f5U,
+	0x0d939e1fU, 0x35a59027U, 0x86f97f94U, 0x4f430c5dU,
+	0x21b49533U, 0xad1bb6bfU, 0x60a4c472U, 0xe68167f4U,
+	0x561d4b44U, 0xa511b4b7U, 0xf3d023e1U, 0x93a83b81U,
+	0xefcb24fdU, 0xebce25f9U, 0x37251225U, 0x1f07180dU,
+	0x6cabc77eU, 0x36655324U, 0xe401e5f6U, 0x10c8d802U,
+	0xb445f1a6U, 0x2dbb963fU, 0xa85ef6baU, 0xa79136b5U,
+	0x1e47590cU, 0x3daf922fU, 0x2b3e1539U, 0x5f57084dU,
+	0x26715734U, 0x1a425808U, 0x33201321U, 0x39aa932bU,
+	0x72304260U, 0x5dd78a4fU, 0x9ee7798cU, 0x78bac26aU,
+	0xcc23efdeU, 0x6deb867fU, 0xb78532a5U, 0xac5bf7beU,
+	0xee8b65fcU, 0x8bb63d99U, 0x19829b0bU, 0x12485a00U,
+	0xb84af2aaU, 0xcd63aedfU, 0x8876fe9aU, 0x9862fa8aU,
+	0x7cbfc36eU, 0xffdf20edU, 0x04d9dd16U, 0x73700361U,
+	0x6a2e4478U, 0x00dcdc12U, 0x87b93e95U, 0xa39437b1U,
+	0xcfe32cddU, 0xbc4ff3aeU, 0x9d27ba8fU, 0x7e3f416cU,
+	0x4dc38e5fU, 0xe3c427f1U, 0x41cc8d53U, 0x4886ce5aU,
+	0x3a6a5028U, 0x9922bb8bU, 0xaf9b34bdU, 0xe94ea7fbU,
+	0xb2c072a0U, 0x29be973bU, 0x6f6b047dU, 0xdab268c8U,
+	0xbf8f30adU, 0xe144a5f3U, 0x27311635U, 0xa2d476b0U,
+	0x05999c17U, 0x75f58067U, 0xbecf71acU, 0x7b7a0169U,
+	0x033437b9U, 0x1a6a70a0U, 0xfff70845U, 0xa3bc1f19U,
+	0x6b462dd1U, 0xdfdf0065U, 0xac73df16U, 0x86d1573cU,
+	0x00f4f4baU, 0x82d45638U, 0x08fef6b2U, 0x537023e9U,
+	0xf832ca42U, 0xb7ad1a0dU, 0xe826ce52U, 0x4cabe7f6U,
+	0xf43dc94eU, 0xc541847fU, 0x32487a88U, 0x8114953bU,
+	0xf038c84aU, 0xa2fc5e18U, 0x48aee6f2U, 0x577522edU,
+	0xf2b84a48U, 0x49eea7f3U, 0x14e5f1aeU, 0xc004c47aU,
+	0x7dd7aac7U, 0x422466f8U, 0x40a4e4faU, 0x3e477984U,
+	0x2a567c90U, 0x51f0a1ebU, 0x19aab3a3U, 0x8b9e1531U,
+	0x1e6f71a4U, 0xe7e90e5dU, 0x3a427880U, 0xb128990bU,
+	0x9445d12eU, 0x09beb7b3U, 0x749de9ceU, 0x55f5a0efU,
+	0xefe30c55U, 0x67492eddU, 0xeaa64c50U, 0xd150816bU,
+	0xcbce0571U, 0x96c5532cU, 0x28d6fe92U, 0xfeb74944U,
+	0x7f5728c5U, 0x7892eac2U, 0xfbf20941U, 0x0e7b75b4U,
+	0x5a3a60e0U, 0x27193e9dU, 0x0a7e74b0U, 0xb3a81b09U,
+	0x8d1b9637U, 0x66096fdcU, 0xa479dd1eU, 0xc2844678U,
+	0x45e1a4ffU, 0x44a1e5feU, 0xc681477cU, 0xabb61d11U,
+	0x9785122dU, 0x9505902fU, 0x04f1f5beU, 0xb2e85a08U,
+	0x563563ecU, 0x3d87ba87U, 0xa876de12U, 0x3cc7fb86U,
+	0xc3c40779U, 0x4e2b65f4U, 0x65c9acdfU, 0xfab24840U,
+	0xf9728b43U, 0xf57d884fU, 0x5e3f61e4U, 0x92c05228U,
+	0x9d0f9227U, 0xc94e8773U, 0x33083b89U, 0x761d6bccU,
+	0xbba21901U, 0xf7fd0a4dU, 0x6489eddeU, 0x75dda8cfU,
+	0x990a9323U, 0x01b4b5bbU, 0x2e537d94U, 0x8c5bd736U,
+	0x20dcfc9aU, 0xc80ec672U, 0x608cecdaU, 0x219cbd9bU,
+	0xd555806fU, 0x3982bb83U, 0x1f2f30a5U, 0xd2904268U,
+	0xcc0bc776U, 0xd010c06aU, 0xa9369f13U, 0xb46dd90eU,
+	0x5cbfe3e6U, 0xdc1fc366U, 0xb9229b03U, 0x462167fcU,
+	0x0b3e35b1U, 0x58bae2e2U, 0x41e4a5fbU, 0x05b1b4bfU,
+	0x26597f9cU, 0x172532adU, 0xc401c57eU, 0x11a0b1abU,
+	0x69c6afd3U, 0x8054d43aU, 0x50b0e0eaU, 0xc7c1067dU,
+	0xda9a4060U, 0x4b6e25f1U, 0x7b5229c1U, 0x225c7e98U,
+	0xf3f80b49U, 0xa6f95f1cU, 0x72186ac8U, 0x132033a9U,
+	0x59faa3e3U, 0x476126fdU, 0xb862da02U, 0x8edb5534U,
+	0xd695436cU, 0x8791163dU, 0x93801329U, 0x634c2fd9U,
+	0xbfa71805U, 0xdd5f8267U, 0x0cfbf7b6U, 0xde9f4164U,
+	0xdbda0161U, 0xe2ac4e58U, 0x2d93be97U, 0xca8e4470U,
+	0x364d7b8cU, 0x885ed632U, 0x30c8f88aU, 0x0dbbb6b7U,
+	0xeea34d54U, 0x166573acU, 0x9040d02aU, 0x984ad222U,
+	0x8f9b1435U, 0x6a066cd0U, 0x9ecf5124U, 0xd7d5026dU,
+	0x2cd3ff96U, 0x83941739U, 0xd3d00369U, 0xe02ccc5aU,
+	0x523062e8U, 0xc144857bU, 0x73582bc9U, 0xd95a8363U,
+	0x5b7a21e1U, 0xfc37cb46U, 0xd415c16eU, 0xf6bd4b4cU,
+	0x6c83efd6U, 0x15a5b0afU, 0x61ccaddbU, 0xe5698c5fU,
+	0x0f3b34b5U, 0x370d3a8dU, 0x8451d53eU, 0x4deba6f7U,
+	0x231c3f99U, 0xafb31c15U, 0x620c6ed8U, 0xe429cd5eU,
+	0x54b5e1eeU, 0xa7b91e1dU, 0xf178894bU, 0x9100912bU,
+	0xed638e57U, 0xe9668f53U, 0x358db88fU, 0x1dafb2a7U,
+	0x6e036dd4U, 0x34cdf98eU, 0xe6a94f5cU, 0x126072a8U,
+	0xb6ed5b0cU, 0x2f133c95U, 0xaaf65c10U, 0xa5399c1fU,
+	0x1ceff3a6U, 0x3f073885U, 0x2996bf93U, 0x5dffa2e7U,
+	0x24d9fd9eU, 0x18eaf2a2U, 0x3188b98bU, 0x3b023981U,
+	0x7098e8caU, 0x5f7f20e5U, 0x9c4fd326U, 0x7a1268c0U,
+	0xce8b4574U, 0x6f432cd5U, 0xb52d980fU, 0xaef35d14U,
+	0xec23cf56U, 0x891e9733U, 0x1b2a31a1U, 0x10e0f0aaU,
+	0xbae25800U, 0xcfcb0475U, 0x8ade5430U, 0x9aca5020U,
+	0x7e1769c4U, 0xfd778a47U, 0x067177bcU, 0x71d8a9cbU,
+	0x6886eed2U, 0x027476b8U, 0x8511943fU, 0xa13c9d1bU,
+	0xcd4b8677U, 0xbee75904U, 0x9f8f1025U, 0x7c97ebc6U,
+	0x4f6b24f5U, 0xe16c8d5bU, 0x436427f9U, 0x4a2e64f0U,
+	0x38c2fa82U, 0x9b8a1121U, 0xad339e17U, 0xebe60d51U,
+	0xb068d80aU, 0x2b163d91U, 0x6dc3aed7U, 0xd81ac262U,
+	0xbd279a07U, 0xe3ec0f59U, 0x2599bc9fU, 0xa07cdc1aU,
+	0x073136bdU, 0x775d2acdU, 0xbc67db06U, 0x79d2abc3U,
+	0x2a6842ccU, 0x333605d5U, 0xd6ab7d30U, 0x8ae06a6cU,
+	0x421a58a4U, 0xf6837510U, 0x852faa63U, 0xaf8d2249U,
+	0x29a881cfU, 0xab88234dU, 0x21a283c7U, 0x7a2c569cU,
+	0xd16ebf37U, 0x9ef16f78U, 0xc17abb27U, 0x65f79283U,
+	0xdd61bc3bU, 0xec1df10aU, 0x1b140ffdU, 0xa848e04eU,
+	0xd964bd3fU, 0x8ba02b6dU, 0x61f29387U, 0x7e295798U,
+	0xdbe43f3dU, 0x60b2d286U, 0x3db984dbU, 0xe958b10fU,
+	0x548bdfb2U, 0x6b78138dU, 0x69f8918fU, 0x171b0cf1U,
+	0x030a09e5U, 0x78acd49eU, 0x30f6c6d6U, 0xa2c26044U,
+	0x373304d1U, 0xceb57b28U, 0x131e0df5U, 0x9874ec7eU,
+	0xbd19a45bU, 0x20e2c2c6U, 0x5dc19cbbU, 0x7ca9d59aU,
+	0xc6bf7920U, 0x4e155ba8U, 0xc3fa3925U, 0xf80cf41eU,
+	0xe2927004U, 0xbf992659U, 0x018a8be7U, 0xd7eb3c31U,
+	0x560b5db0U, 0x51ce9fb7U, 0xd2ae7c34U, 0x272700c1U,
+	0x73661595U, 0x0e454be8U, 0x232201c5U, 0x9af46e7cU,
+	0xa447e342U, 0x4f551aa9U, 0x8d25a86bU, 0xebd8330dU,
+	0x6cbdd18aU, 0x6dfd908bU, 0xefdd3209U, 0x82ea6864U,
+	0xbed96758U, 0xbc59e55aU, 0x2dad80cbU, 0x9bb42f7dU,
+	0x7f691699U, 0x14dbcff2U, 0x812aab67U, 0x159b8ef3U,
+	0xea98720cU, 0x67771081U, 0x4c95d9aaU, 0xd3ee3d35U,
+	0xd02efe36U, 0xdc21fd3aU, 0x77631491U, 0xbb9c275dU,
+	0xb453e752U, 0xe012f206U, 0x1a544efcU, 0x5f411eb9U,
+	0x92fe6c74U, 0xdea17f38U, 0x4dd598abU, 0x5c81ddbaU,
+	0xb056e656U, 0x28e8c0ceU, 0x070f08e1U, 0xa507a243U,
+	0x098089efU, 0xe152b307U, 0x49d099afU, 0x08c0c8eeU,
+	0xfc09f51aU, 0x10decef6U, 0x367345d0U, 0xfbcc371dU,
+	0xe557b203U, 0xf94cb51fU, 0x806aea66U, 0x9d31ac7bU,
+	0x75e39693U, 0xf543b613U, 0x907eee76U, 0x6f7d1289U,
+	0x226240c4U, 0x71e69797U, 0x68b8d08eU, 0x2cedc1caU,
+	0x0f050ae9U, 0x3e7947d8U, 0xed5db00bU, 0x38fcc4deU,
+	0x409adaa6U, 0xa908a14fU, 0x79ec959fU, 0xee9d7308U,
+	0xf3c63515U, 0x62325084U, 0x520e5cb4U, 0x0b000bedU,
+	0xdaa47e3cU, 0x8fa52a69U, 0x5b441fbdU, 0x3a7c46dcU,
+	0x70a6d696U, 0x6e3d5388U, 0x913eaf77U, 0xa7872041U,
+	0xffc93619U, 0xaecd6348U, 0xbadc665cU, 0x4a105aacU,
+	0x96fb6d70U, 0xf403f712U, 0x25a782c3U, 0xf7c33411U,
+	0xf2867414U, 0xcbf03b2dU, 0x04cfcbe2U, 0xe3d23105U,
+	0x1f110ef9U, 0xa102a347U, 0x19948dffU, 0x24e7c3c2U,
+	0xc7ff3821U, 0x3f3906d9U, 0xb91ca55fU, 0xb116a757U,
+	0xa6c76140U, 0x435a19a5U, 0xb7932451U, 0xfe897718U,
+	0x058f8ae3U, 0xaac8624cU, 0xfa8c761cU, 0xc970b92fU,
+	0x7b6c179dU, 0xe818f00eU, 0x5a045ebcU, 0xf006f616U,
+	0x72265494U, 0xd56bbe33U, 0xfd49b41bU, 0xdfe13e39U,
+	0x45df9aa3U, 0x3cf9c5daU, 0x4890d8aeU, 0xcc35f92aU,
+	0x266741c0U, 0x1e514ff8U, 0xad0da04bU, 0x64b7d382U,
+	0x0a404aecU, 0x86ef6960U, 0x4b501badU, 0xcd75b82bU,
+	0x7de9949bU, 0x8ee56b68U, 0xd824fc3eU, 0xb85ce45eU,
+	0xc43ffb22U, 0xc03afa26U, 0x1cd1cdfaU, 0x34f3c7d2U,
+	0x475f18a1U, 0x1d918cfbU, 0xcff53a29U, 0x3b3c07ddU,
+	0x9fb12e79U, 0x064f49e0U, 0x83aa2965U, 0x8c65e96aU,
+	0x35b386d3U, 0x165b4df0U, 0x00cacae6U, 0x74a3d792U,
+	0x0d8588ebU, 0x31b687d7U, 0x18d4ccfeU, 0x125e4cf4U,
+	0x59c49dbfU, 0x76235590U, 0xb513a653U, 0x534e1db5U,
+	0xe7d73001U, 0x461f59a0U, 0x9c71ed7aU, 0x87af2861U,
+	0xc57fba23U, 0xa042e246U, 0x327644d4U, 0x39bc85dfU,
+	0x93be2d75U, 0xe6977100U, 0xa3822145U, 0xb3962555U,
+	0x574b1cb1U, 0xd42bff32U, 0x2f2d02c9U, 0x5884dcbeU,
+	0x41da9ba7U, 0x2b2803cdU, 0xac4de14aU, 0x8860e86eU,
+	0xe417f302U, 0x97bb2c71U, 0xb6d36550U, 0x55cb9eb3U,
+	0x66375180U, 0xc830f82eU, 0x6a38528cU, 0x63721185U,
+	0x119e8ff7U, 0xb2d66454U, 0x846feb62U, 0xc2ba7824U,
+	0x9934ad7fU, 0x024a48e4U, 0x449fdba2U, 0xf146b717U,
+	0x947bef72U, 0xcab07a2cU, 0x0cc5c9eaU, 0x8920a96fU,
+	0x2e6d43c8U, 0x5e015fb8U, 0x953bae73U, 0x508edeb6U,
+	0x3f380789U, 0x26664090U, 0xc3fb3875U, 0x9fb02f29U,
+	0x574a1de1U, 0xe3d33055U, 0x907fef26U, 0xbadd670cU,
+	0x3cf8c48aU, 0xbed86608U, 0x34f2c682U, 0x6f7c13d9U,
+	0xc43efa72U, 0x8ba12a3dU, 0xd42afe62U, 0x70a7d7c6U,
+	0xc831f97eU, 0xf94db44fU, 0x0e444ab8U, 0xbd18a50bU,
+	0xcc34f87aU, 0x9ef06e28U, 0x74a2d6c2U, 0x6b7912ddU,
+	0xceb47a78U, 0x75e297c3U, 0x28e9c19eU, 0xfc08f44aU,
+	0x41db9af7U, 0x7e2856c8U, 0x7ca8d4caU, 0x024b49b4U,
+	0x165a4ca0U, 0x6dfc91dbU, 0x25a68393U, 0xb7922501U,
+	0x22634194U, 0xdbe53e6dU, 0x064e48b0U, 0x8d24a93bU,
+	0xa849e11eU, 0x35b28783U, 0x4891d9feU, 0x69f990dfU,
+	0xd3ef3c65U, 0x5b451eedU, 0xd6aa7c60U, 0xed5cb15bU,
+	0xf7c23541U, 0xaac9631cU, 0x14dacea2U, 0xc2bb7974U,
+	0x435b18f5U, 0x449edaf2U, 0xc7fe3971U, 0x32774584U,
+	0x663650d0U, 0x1b150eadU, 0x36724480U, 0x8fa42b39U,
+	0xb117a607U, 0x5a055fecU, 0x9875ed2eU, 0xfe887648U,
+	0x79ed94cfU, 0x78add5ceU, 0xfa8d774cU, 0x97ba2d21U,
+	0xab89221dU, 0xa909a01fU, 0x38fdc58eU, 0x8ee46a38U,
+	0x6a3953dcU, 0x018b8ab7U, 0x947aee22U, 0x00cbcbb6U,
+	0xffc83749U, 0x722755c4U, 0x59c59cefU, 0xc6be7870U,
+	0xc57ebb73U, 0xc971b87fU, 0x623351d4U, 0xaecc6218U,
+	0xa103a217U, 0xf542b743U, 0x0f040bb9U, 0x4a115bfcU,
+	0x87ae2931U, 0xcbf13a7dU, 0x5885ddeeU, 0x49d198ffU,
+	0xa506a313U, 0x3db8858bU, 0x125f4da4U, 0xb057e706U,
+	0x1cd0ccaaU, 0xf402f642U, 0x5c80dceaU, 0x1d908dabU,
+	0xe959b05fU, 0x058e8bb3U, 0x23230095U, 0xee9c7258U,
+	0xf007f746U, 0xec1cf05aU, 0x953aaf23U, 0x8861e93eU,
+	0x60b3d3d6U, 0xe013f356U, 0x852eab33U, 0x7a2d57ccU,
+	0x37320581U, 0x64b6d2d2U, 0x7de895cbU, 0x39bd848fU,
+	0x1a554facU, 0x2b29029dU, 0xf80df54eU, 0x2dac819bU,
+	0x55ca9fe3U, 0xbc58e40aU, 0x6cbcd0daU, 0xfbcd364dU,
+	0xe6967050U, 0x776215c1U, 0x475e19f1U, 0x1e504ea8U,
+	0xcff43b79U, 0x9af56f2cU, 0x4e145af8U, 0x2f2c0399U,
+	0x65f693d3U, 0x7b6d16cdU, 0x846eea32U, 0xb2d76504U,
+	0xea99735cU, 0xbb9d260dU, 0xaf8c2319U, 0x5f401fe9U,
+	0x83ab2835U, 0xe153b257U, 0x30f7c786U, 0xe2937154U,
+	0xe7d63151U, 0xdea07e68U, 0x119f8ea7U, 0xf6827440U,
+	0x0a414bbcU, 0xb452e602U, 0x0cc4c8baU, 0x31b78687U,
+	0xd2af7d64U, 0x2a69439cU, 0xac4ce01aU, 0xa446e212U,
+	0xb3972405U, 0x560a5ce0U, 0xa2c36114U, 0xebd9325dU,
+	0x10dfcfa6U, 0xbf982709U, 0xefdc3359U, 0xdc20fc6aU,
+	0x6e3c52d8U, 0xfd48b54bU, 0x4f541bf9U, 0xe556b353U,
+	0x677611d1U, 0xc03bfb76U, 0xe819f15eU, 0xcab17b7cU,
+	0x508fdfe6U, 0x29a9809fU, 0x5dc09debU, 0xd965bc6fU,
+	0x33370485U, 0x0b010abdU, 0xb85de50eU, 0x71e796c7U,
+	0x1f100fa9U, 0x93bf2c25U, 0x5e005ee8U, 0xd825fd6eU,
+	0x68b9d1deU, 0x9bb52e2dU, 0xcd74b97bU, 0xad0ca11bU,
+	0xd16fbe67U, 0xd56abf63U, 0x098188bfU, 0x21a38297U,
+	0x520f5de4U, 0x08c1c9beU, 0xdaa57f6cU, 0x2e6c4298U,
+	0x8ae16b3cU, 0x131f0ca5U, 0x96fa6c20U, 0x9935ac2fU,
+	0x20e3c396U, 0x030b08b5U, 0x159a8fa3U, 0x61f392d7U,
+	0x18d5cdaeU, 0x24e6c292U, 0x0d8489bbU, 0x070e09b1U,
+	0x4c94d8faU, 0x637310d5U, 0xa043e316U, 0x461e58f0U,
+	0xf2877544U, 0x534f1ce5U, 0x8921a83fU, 0x92ff6d24U,
+	0xd02fff66U, 0xb512a703U, 0x27260191U, 0x2cecc09aU,
+	0x86ee6830U, 0xf3c73445U, 0xb6d26400U, 0xa6c66010U,
+	0x421b59f4U, 0xc17bba77U, 0x3a7d478cU, 0x4dd499fbU,
+	0x548adee2U, 0x3e784688U, 0xb91da40fU, 0x9d30ad2bU,
+	0xf147b647U, 0x82eb6934U, 0xa3832015U, 0x409bdbf6U,
+	0x736714c5U, 0xdd60bd6bU, 0x7f6817c9U, 0x762254c0U,
+	0x04cecab2U, 0xa7862111U, 0x913fae27U, 0xd7ea3d61U,
+	0x8c64e83aU, 0x171a0da1U, 0x51cf9ee7U, 0xe416f252U,
+	0x812baa37U, 0xdfe03f69U, 0x19958cafU, 0x9c70ec2aU,
+	0x3b3d068dU, 0x4b511afdU, 0x806beb36U, 0x45de9bf3U,
+	0x2b3c1799U, 0x32625080U, 0xd7ff2865U, 0x8bb43f39U,
+	0x434e0df1U, 0xf7d72045U, 0x847bff36U, 0xaed9771cU,
+	0x28fcd49aU, 0xaadc7618U, 0x20f6d692U, 0x7b7803c9U,
+	0xd03aea62U, 0x9fa53a2dU, 0xc02eee72U, 0x64a3c7d6U,
+	0xdc35e96eU, 0xed49a45fU, 0x1a405aa8U, 0xa91cb51bU,
+	0xd830e86aU, 0x8af47e38U, 0x60a6c6d2U, 0x7f7d02cdU,
+	0xdab06a68U, 0x61e687d3U, 0x3cedd18eU, 0xe80ce45aU,
+	0x55df8ae7U, 0x6a2c46d8U, 0x68acc4daU, 0x164f59a4U,
+	0x025e5cb0U, 0x79f881cbU, 0x31a29383U, 0xa3963511U,
+	0x36675184U, 0xcfe12e7dU, 0x124a58a0U, 0x9920b92bU,
+	0xbc4df10eU, 0x21b69793U, 0x5c95c9eeU, 0x7dfd80cfU,
+	0xc7eb2c75U, 0x4f410efdU, 0xc2ae6c70U, 0xf958a14bU,
+	0xe3c62551U, 0xbecd730cU, 0x00dedeb2U, 0xd6bf6964U,
+	0x575f08e5U, 0x509acae2U, 0xd3fa2961U, 0x26735594U,
+	0x723240c0U, 0x0f111ebdU, 0x22765490U, 0x9ba03b29U,
+	0xa513b617U, 0x4e014ffcU, 0x8c71fd3eU, 0xea8c6658U,
+	0x6de984dfU, 0x6ca9c5deU, 0xee89675cU, 0x83be3d31U,
+	0xbf8d320dU, 0xbd0db00fU, 0x2cf9d59eU, 0x9ae07a28U,
+	0x7e3d43ccU, 0x158f9aa7U, 0x807efe32U, 0x14cfdba6U,
+	0xebcc2759U, 0x662345d4U, 0x4dc18cffU, 0xd2ba6860U,
+	0xd17aab63U, 0xdd75a86fU, 0x763741c4U, 0xbac87208U,
+	0xb507b207U, 0xe146a753U, 0x1b001ba9U, 0x5e154becU,
+	0x93aa3921U, 0xdff52a6dU, 0x4c81cdfeU, 0x5dd588efU,
+	0xb102b303U, 0x29bc959bU, 0x065b5db4U, 0xa453f716U,
+	0x08d4dcbaU, 0xe006e652U, 0x4884ccfaU, 0x09949dbbU,
+	0xfd5da04fU, 0x118a9ba3U, 0x37271085U, 0xfa986248U,
+	0xe403e756U, 0xf818e04aU, 0x813ebf33U, 0x9c65f92eU,
+	0x74b7c3c6U, 0xf417e346U, 0x912abb23U, 0x6e2947dcU,
+	0x23361591U, 0x70b2c2c2U, 0x69ec85dbU, 0x2db9949fU,
+	0x0e515fbcU, 0x3f2d128dU, 0xec09e55eU, 0x39a8918bU,
+	0x41ce8ff3U, 0xa85cf41aU, 0x78b8c0caU, 0xefc9265dU,
+	0xf2926040U, 0x636605d1U, 0x535a09e1U, 0x0a545eb8U,
+	0xdbf02b69U, 0x8ef17f3cU, 0x5a104ae8U, 0x3b281389U,
+	0x71f283c3U, 0x6f6906ddU, 0x906afa22U, 0xa6d37514U,
+	0xfe9d634cU, 0xaf99361dU, 0xbb883309U, 0x4b440ff9U,
+	0x97af3825U, 0xf557a247U, 0x24f3d796U, 0xf6976144U,
+	0xf3d22141U, 0xcaa46e78U, 0x059b9eb7U, 0xe2866450U,
+	0x1e455bacU, 0xa056f612U, 0x18c0d8aaU, 0x25b39697U,
+	0xc6ab6d74U, 0x3e6d538cU, 0xb848f00aU, 0xb042f202U,
+	0xa7933415U, 0x420e4cf0U, 0xb6c77104U, 0xffdd224dU,
+	0x04dbdfb6U, 0xab9c3719U, 0xfbd82349U, 0xc824ec7aU,
+	0x7a3842c8U, 0xe94ca55bU, 0x5b500be9U, 0xf152a343U,
+	0x737201c1U, 0xd43feb66U, 0xfc1de14eU, 0xdeb56b6cU,
+	0x448bcff6U, 0x3dad908fU, 0x49c48dfbU, 0xcd61ac7fU,
+	0x27331495U, 0x1f051aadU, 0xac59f51eU, 0x65e386d7U,
+	0x0b141fb9U, 0x87bb3c35U, 0x4a044ef8U, 0xcc21ed7eU,
+	0x7cbdc1ceU, 0x8fb13e3dU, 0xd970a96bU, 0xb908b10bU,
+	0xc56bae77U, 0xc16eaf73U, 0x1d8598afU, 0x35a79287U,
+	0x460b4df4U, 0x1cc5d9aeU, 0xcea16f7cU, 0x3a685288U,
+	0x9ee57b2cU, 0x071b1cb5U, 0x82fe7c30U, 0x8d31bc3fU,
+	0x34e7d386U, 0x170f18a5U, 0x019e9fb3U, 0x75f782c7U,
+	0x0cd1ddbeU, 0x30e2d282U, 0x198099abU, 0x130a19a1U,
+	0x5890c8eaU, 0x777700c5U, 0xb447f306U, 0x521a48e0U,
+	0xe6836554U, 0x474b0cf5U, 0x9d25b82fU, 0x86fb7d34U,
+	0xc42bef76U, 0xa116b713U, 0x33221181U, 0x38e8d08aU,
+	0x92ea7820U, 0xe7c32455U, 0xa2d67410U, 0xb2c27000U,
+	0x561f49e4U, 0xd57faa67U, 0x2e79579cU, 0x59d089ebU,
+	0x408ecef2U, 0x2a7c5698U, 0xad19b41fU, 0x8934bd3bU,
+	0xe543a657U, 0x96ef7924U, 0xb7873005U, 0x549fcbe6U,
+	0x676304d5U, 0xc964ad7bU, 0x6b6c07d9U, 0x622644d0U,
+	0x10cadaa2U, 0xb3823101U, 0x853bbe37U, 0xc3ee2d71U,
+	0x9860f82aU, 0x031e1db1U, 0x45cb8ef7U, 0xf012e242U,
+	0x952fba27U, 0xcbe42f79U, 0x0d919cbfU, 0x8874fc3aU,
+	0x2f39169dU, 0x5f550aedU, 0x946ffb26U, 0x51da8be3U,
+	0xf605f37dU, 0xef5bb464U, 0x0ac6cc81U, 0x568ddbddU,
+	0x9e77e915U, 0x2aeec4a1U, 0x59421bd2U, 0x73e093f8U,
+	0xf5c5307eU, 0x77e592fcU, 0xfdcf3276U, 0xa641e72dU,
+	0x0d030e86U, 0x429cdec9U, 0x1d170a96U, 0xb99a2332U,
+	0x010c0d8aU, 0x307040bbU, 0xc779be4cU, 0x742551ffU,
+	0x05090c8eU, 0x57cd9adcU, 0xbd9f2236U, 0xa244e629U,
+	0x07898e8cU, 0xbcdf6337U, 0xe1d4356aU, 0x353500beU,
+	0x88e66e03U, 0xb715a23cU, 0xb595203eU, 0xcb76bd40U,
+	0xdf67b854U, 0xa4c1652fU, 0xec9b7767U, 0x7eafd1f5U,
+	0xeb5eb560U, 0x12d8ca99U, 0xcf73bc44U, 0x44195dcfU,
+	0x617415eaU, 0xfc8f7377U, 0x81ac2d0aU, 0xa0c4642bU,
+	0x1ad2c891U, 0x9278ea19U, 0x1f978894U, 0x246145afU,
+	0x3effc1b5U, 0x63f497e8U, 0xdde73a56U, 0x0b868d80U,
+	0x8a66ec01U, 0x8da32e06U, 0x0ec3cd85U, 0xfb4ab170U,
+	0xaf0ba424U, 0xd228fa59U, 0xff4fb074U, 0x4699dfcdU,
+	0x782a52f3U, 0x9338ab18U, 0x514819daU, 0x37b582bcU,
+	0xb0d0603bU, 0xb190213aU, 0x33b083b8U, 0x5e87d9d5U,
+	0x62b4d6e9U, 0x603454ebU, 0xf1c0317aU, 0x47d99eccU,
+	0xa304a728U, 0xc8b67e43U, 0x5d471ad6U, 0xc9f63f42U,
+	0x36f5c3bdU, 0xbb1aa130U, 0x90f8681bU, 0x0f838c84U,
+	0x0c434f87U, 0x004c4c8bU, 0xab0ea520U, 0x67f196ecU,
+	0x683e56e3U, 0x3c7f43b7U, 0xc639ff4dU, 0x832caf08U,
+	0x4e93ddc5U, 0x02ccce89U, 0x91b8291aU, 0x80ec6c0bU,
+	0x6c3b57e7U, 0xf485717fU, 0xdb62b950U, 0x796a13f2U,
+	0xd5ed385eU, 0x3d3f02b6U, 0x95bd281eU, 0xd4ad795fU,
+	0x206444abU, 0xccb37f47U, 0xea1ef461U, 0x27a186acU,
+	0x393a03b2U, 0x252104aeU, 0x5c075bd7U, 0x415c1dcaU,
+	0xa98e2722U, 0x292e07a2U, 0x4c135fc7U, 0xb310a338U,
+	0xfe0ff175U, 0xad8b2626U, 0xb4d5613fU, 0xf080707bU,
+	0xd368bb58U, 0xe214f669U, 0x313001baU, 0xe491756fU,
+	0x9cf76b17U, 0x756510feU, 0xa581242eU, 0x32f0c2b9U,
+	0x2fab84a4U, 0xbe5fe135U, 0x8e63ed05U, 0xd76dba5cU,
+	0x06c9cf8dU, 0x53c89bd8U, 0x8729ae0cU, 0xe611f76dU,
+	0xaccb6727U, 0xb250e239U, 0x4d531ec6U, 0x7bea91f0U,
+	0x23a487a8U, 0x72a0d2f9U, 0x66b1d7edU, 0x967deb1dU,
+	0x4a96dcc1U, 0x286e46a3U, 0xf9ca3372U, 0x2bae85a0U,
+	0x2eebc5a5U, 0x179d8a9cU, 0xd8a27a53U, 0x3fbf80b4U,
+	0xc37cbf48U, 0x7d6f12f6U, 0xc5f93c4eU, 0xf88a7273U,
+	0x1b928990U, 0xe354b768U, 0x657114eeU, 0x6d7b16e6U,
+	0x7aaad0f1U, 0x9f37a814U, 0x6bfe95e0U, 0x22e4c6a9U,
+	0xd9e23b52U, 0x76a5d3fdU, 0x26e1c7adU, 0x151d089eU,
+	0xa701a62cU, 0x347541bfU, 0x8669ef0dU, 0x2c6b47a7U,
+	0xae4be525U, 0x09060f82U, 0x212405aaU, 0x038c8f88U,
+	0x99b22b12U, 0xe094746bU, 0x94fd691fU, 0x1058489bU,
+	0xfa0af071U, 0xc23cfe49U, 0x716011faU, 0xb8da6233U,
+	0xd62dfb5dU, 0x5a82d8d1U, 0x973daa1cU, 0x1118099aU,
+	0xa184252aU, 0x5288dad9U, 0x04494d8fU, 0x643155efU,
+	0x18524a93U, 0x1c574b97U, 0xc0bc7c4bU, 0xe89e7663U,
+	0x9b32a910U, 0xc1fc3d4aU, 0x13988b98U, 0xe751b66cU,
+	0x43dc9fc8U, 0xda22f851U, 0x5fc798d4U, 0x500858dbU,
+	0xe9de3762U, 0xca36fc41U, 0xdca77b57U, 0xa8ce6623U,
+	0xd1e8395aU, 0xeddb3666U, 0xc4b97d4fU, 0xce33fd45U,
+	0x85a92c0eU, 0xaa4ee421U, 0x697e17e2U, 0x8f23ac04U,
+	0x3bba81b0U, 0x9a72e811U, 0x401c5ccbU, 0x5bc299d0U,
+	0x19120b92U, 0x7c2f53f7U, 0xee1bf565U, 0xe5d1346eU,
+	0x4fd39cc4U, 0x3afac0b1U, 0x7fef90f4U, 0x6ffb94e4U,
+	0x8b26ad00U, 0x08464e83U, 0xf340b378U, 0x84e96d0fU,
+	0x9db72a16U, 0xf745b27cU, 0x702050fbU, 0x540d59dfU,
+	0x387a42b3U, 0x4bd69dc0U, 0x6abed4e1U, 0x89a62f02U,
+	0xba5ae031U, 0x145d499fU, 0xb655e33dU, 0xbf1fa034U,
+	0xcdf33e46U, 0x6ebbd5e5U, 0x58025ad3U, 0x1ed7c995U,
+	0x45591cceU, 0xde27f955U, 0x98f26a13U, 0x2d2b06a6U,
+	0x48165ec3U, 0x16ddcb9dU, 0xd0a8785bU, 0x554d18deU,
+	0xf200f279U, 0x826cee09U, 0x49561fc2U, 0x8ce36f07U,
+	0x96e670feU, 0x8fb837e7U, 0x6a254f02U, 0x366e585eU,
+	0xfe946a96U, 0x4a0d4722U, 0x39a19851U, 0x1303107bU,
+	0x9526b3fdU, 0x1706117fU, 0x9d2cb1f5U, 0xc6a264aeU,
+	0x6de08d05U, 0x227f5d4aU, 0x7df48915U, 0xd979a0b1U,
+	0x61ef8e09U, 0x5093c338U, 0xa79a3dcfU, 0x14c6d27cU,
+	0x65ea8f0dU, 0x372e195fU, 0xdd7ca1b5U, 0xc2a765aaU,
+	0x676a0d0fU, 0xdc3ce0b4U, 0x8137b6e9U, 0x55d6833dU,
+	0xe805ed80U, 0xd7f621bfU, 0xd576a3bdU, 0xab953ec3U,
+	0xbf843bd7U, 0xc422e6acU, 0x8c78f4e4U, 0x1e4c5276U,
+	0x8bbd36e3U, 0x723b491aU, 0xaf903fc7U, 0x24fade4cU,
+	0x01979669U, 0x9c6cf0f4U, 0xe14fae89U, 0xc027e7a8U,
+	0x7a314b12U, 0xf29b699aU, 0x7f740b17U, 0x4482c62cU,
+	0x5e1c4236U, 0x0317146bU, 0xbd04b9d5U, 0x6b650e03U,
+	0xea856f82U, 0xed40ad85U, 0x6e204e06U, 0x9ba932f3U,
+	0xcfe827a7U, 0xb2cb79daU, 0x9fac33f7U, 0x267a5c4eU,
+	0x18c9d170U, 0xf3db289bU, 0x31ab9a59U, 0x5756013fU,
+	0xd033e3b8U, 0xd173a2b9U, 0x5353003bU, 0x3e645a56U,
+	0x0257556aU, 0x00d7d768U, 0x9123b2f9U, 0x273a1d4fU,
+	0xc3e724abU, 0xa855fdc0U, 0x3da49955U, 0xa915bcc1U,
+	0x5616403eU, 0xdbf922b3U, 0xf01beb98U, 0x6f600f07U,
+	0x6ca0cc04U, 0x60afcf08U, 0xcbed26a3U, 0x0712156fU,
+	0x08ddd560U, 0x5c9cc034U, 0xa6da7cceU, 0xe3cf2c8bU,
+	0x2e705e46U, 0x622f4d0aU, 0xf15baa99U, 0xe00fef88U,
+	0x0cd8d464U, 0x9466f2fcU, 0xbb813ad3U, 0x19899071U,
+	0xb50ebbddU, 0x5ddc8135U, 0xf55eab9dU, 0xb44efadcU,
+	0x4087c728U, 0xac50fcc4U, 0x8afd77e2U, 0x4742052fU,
+	0x59d98031U, 0x45c2872dU, 0x3ce4d854U, 0x21bf9e49U,
+	0xc96da4a1U, 0x49cd8421U, 0x2cf0dc44U, 0xd3f320bbU,
+	0x9eec72f6U, 0xcd68a5a5U, 0xd436e2bcU, 0x9063f3f8U,
+	0xb38b38dbU, 0x82f775eaU, 0x51d38239U, 0x8472f6ecU,
+	0xfc14e894U, 0x1586937dU, 0xc562a7adU, 0x5213413aU,
+	0x4f480727U, 0xdebc62b6U, 0xee806e86U, 0xb78e39dfU,
+	0x662a4c0eU, 0x332b185bU, 0xe7ca2d8fU, 0x86f274eeU,
+	0xcc28e4a4U, 0xd2b361baU, 0x2db09d45U, 0x1b091273U,
+	0x4347042bU, 0x1243517aU, 0x0652546eU, 0xf69e689eU,
+	0x2a755f42U, 0x488dc520U, 0x9929b0f1U, 0x4b4d0623U,
+	0x4e084626U, 0x777e091fU, 0xb841f9d0U, 0x5f5c0337U,
+	0xa39f3ccbU, 0x1d8c9175U, 0xa51abfcdU, 0x9869f1f0U,
+	0x7b710a13U, 0x83b734ebU, 0x0592976dU, 0x0d989565U,
+	0x1a495372U, 0xffd42b97U, 0x0b1d1663U, 0x4207452aU,
+	0xb901b8d1U, 0x1646507eU, 0x4602442eU, 0x75fe8b1dU,
+	0xc7e225afU, 0x5496c23cU, 0xe68a6c8eU, 0x4c88c424U,
+	0xcea866a6U, 0x69e58c01U, 0x41c78629U, 0x636f0c0bU,
+	0xf951a891U, 0x8077f7e8U, 0xf41eea9cU, 0x70bbcb18U,
+	0x9ae973f2U, 0xa2df7dcaU, 0x11839279U, 0xd839e1b0U,
+	0xb6ce78deU, 0x3a615b52U, 0xf7de299fU, 0x71fb8a19U,
+	0xc167a6a9U, 0x326b595aU, 0x64aace0cU, 0x04d2d66cU,
+	0x78b1c910U, 0x7cb4c814U, 0xa05fffc8U, 0x887df5e0U,
+	0xfbd12a93U, 0xa11fbec9U, 0x737b081bU, 0x87b235efU,
+	0x233f1c4bU, 0xbac17bd2U, 0x3f241b57U, 0x30ebdb58U,
+	0x893db4e1U, 0xaad57fc2U, 0xbc44f8d4U, 0xc82de5a0U,
+	0xb10bbad9U, 0x8d38b5e5U, 0xa45afeccU, 0xaed07ec6U,
+	0xe54aaf8dU, 0xcaad67a2U, 0x099d9461U, 0xefc02f87U,
+	0x5b590233U, 0xfa916b92U, 0x20ffdf48U, 0x3b211a53U,
+	0x79f18811U, 0x1cccd074U, 0x8ef876e6U, 0x8532b7edU,
+	0x2f301f47U, 0x5a194332U, 0x1f0c1377U, 0x0f181767U,
+	0xebc52e83U, 0x68a5cd00U, 0x93a330fbU, 0xe40aee8cU,
+	0xfd54a995U, 0x97a631ffU, 0x10c3d378U, 0x34eeda5cU,
+	0x5899c130U, 0x2b351e43U, 0x0a5d5762U, 0xe945ac81U,
+	0xdab963b2U, 0x74beca1cU, 0xd6b660beU, 0xdffc23b7U,
+	0xad10bdc5U, 0x0e585666U, 0x38e1d950U, 0x7e344a16U,
+	0x25ba9f4dU, 0xbec47ad6U, 0xf811e990U, 0x4dc88525U,
+	0x28f5dd40U, 0x763e481eU, 0xb04bfbd8U, 0x35ae9b5dU,
+	0x92e371faU, 0xe28f6d8aU, 0x29b59c41U, 0xec00ec84U,
+	0x901b8b05U, 0x8945cc1cU, 0x6cd8b4f9U, 0x3093a3a5U,
+	0xf869916dU, 0x4cf0bcd9U, 0x3f5c63aaU, 0x15feeb80U,
+	0x93db4806U, 0x11fbea84U, 0x9bd14a0eU, 0xc05f9f55U,
+	0x6b1d76feU, 0x2482a6b1U, 0x7b0972eeU, 0xdf845b4aU,
+	0x671275f2U, 0x566e38c3U, 0xa167c634U, 0x123b2987U,
+	0x631774f6U, 0x31d3e2a4U, 0xdb815a4eU, 0xc45a9e51U,
+	0x6197f6f4U, 0xdac11b4fU, 0x87ca4d12U, 0x532b78c6U,
+	0xeef8167bU, 0xd10bda44U, 0xd38b5846U, 0xad68c538U,
+	0xb979c02cU, 0xc2df1d57U, 0x8a850f1fU, 0x18b1a98dU,
+	0x8d40cd18U, 0x74c6b2e1U, 0xa96dc43cU, 0x220725b7U,
+	0x076a6d92U, 0x9a910b0fU, 0xe7b25572U, 0xc6da1c53U,
+	0x7cccb0e9U, 0xf4669261U, 0x7989f0ecU, 0x427f3dd7U,
+	0x58e1b9cdU, 0x05eaef90U, 0xbbf9422eU, 0x6d98f5f8U,
+	0xec789479U, 0xebbd567eU, 0x68ddb5fdU, 0x9d54c908U,
+	0xc915dc5cU, 0xb4368221U, 0x9951c80cU, 0x2087a7b5U,
+	0x1e342a8bU, 0xf526d360U, 0x375661a2U, 0x51abfac4U,
+	0xd6ce1843U, 0xd78e5942U, 0x55aefbc0U, 0x3899a1adU,
+	0x04aaae91U, 0x062a2c93U, 0x97de4902U, 0x21c7e6b4U,
+	0xc51adf50U, 0xaea8063bU, 0x3b5962aeU, 0xafe8473aU,
+	0x50ebbbc5U, 0xdd04d948U, 0xf6e61063U, 0x699df4fcU,
+	0x6a5d37ffU, 0x665234f3U, 0xcd10dd58U, 0x01efee94U,
+	0x0e202e9bU, 0x5a613bcfU, 0xa0278735U, 0xe532d770U,
+	0x288da5bdU, 0x64d2b6f1U, 0xf7a65162U, 0xe6f21473U,
+	0x0a252f9fU, 0x929b0907U, 0xbd7cc128U, 0x1f746b8aU,
+	0xb3f34026U, 0x5b217aceU, 0xf3a35066U, 0xb2b30127U,
+	0x467a3cd3U, 0xaaad073fU, 0x8c008c19U, 0x41bffed4U,
+	0x5f247bcaU, 0x433f7cd6U, 0x3a1923afU, 0x274265b2U,
+	0xcf905f5aU, 0x4f307fdaU, 0x2a0d27bfU, 0xd50edb40U,
+	0x9811890dU, 0xcb955e5eU, 0xd2cb1947U, 0x969e0803U,
+	0xb576c320U, 0x840a8e11U, 0x572e79c2U, 0x828f0d17U,
+	0xfae9136fU, 0x137b6886U, 0xc39f5c56U, 0x54eebac1U,
+	0x49b5fcdcU, 0xd841994dU, 0xe87d957dU, 0xb173c224U,
+	0x60d7b7f5U, 0x35d6e3a0U, 0xe137d674U, 0x800f8f15U,
+	0xcad51f5fU, 0xd44e9a41U, 0x2b4d66beU, 0x1df4e988U,
+	0x45baffd0U, 0x14beaa81U, 0x00afaf95U, 0xf0639365U,
+	0x2c88a4b9U, 0x4e703edbU, 0x9fd44b0aU, 0x4db0fdd8U,
+	0x48f5bdddU, 0x7183f2e4U, 0xbebc022bU, 0x59a1f8ccU,
+	0xa562c730U, 0x1b716a8eU, 0xa3e74436U, 0x9e940a0bU,
+	0x7d8cf1e8U, 0x854acf10U, 0x036f6c96U, 0x0b656e9eU,
+	0x1cb4a889U, 0xf929d06cU, 0x0de0ed98U, 0x44fabed1U,
+	0xbffc432aU, 0x10bbab85U, 0x40ffbfd5U, 0x730370e6U,
+	0xc11fde54U, 0x526b39c7U, 0xe0779775U, 0x4a753fdfU,
+	0xc8559d5dU, 0x6f1877faU, 0x473a7dd2U, 0x6592f7f0U,
+	0xffac536aU, 0x868a0c13U, 0xf2e31167U, 0x764630e3U,
+	0x9c148809U, 0xa4228631U, 0x177e6982U, 0xdec41a4bU,
+	0xb0338325U, 0x3c9ca0a9U, 0xf123d264U, 0x770671e2U,
+	0xc79a5d52U, 0x3496a2a1U, 0x625735f7U, 0x022f2d97U,
+	0x7e4c32ebU, 0x7a4933efU, 0xa6a20433U, 0x8e800e1bU,
+	0xfd2cd168U, 0xa7e24532U, 0x7586f3e0U, 0x814fce14U,
+	0x25c2e7b0U, 0xbc3c8029U, 0x39d9e0acU, 0x361620a3U,
+	0x8fc04f1aU, 0xac288439U, 0xbab9032fU, 0xced01e5bU,
+	0xb7f64122U, 0x8bc54e1eU, 0xa2a70537U, 0xa82d853dU,
+	0xe3b75476U, 0xcc509c59U, 0x0f606f9aU, 0xe93dd47cU,
+	0x5da4f9c8U, 0xfc6c9069U, 0x260224b3U, 0x3ddce1a8U,
+	0x7f0c73eaU, 0x1a312b8fU, 0x88058d1dU, 0x83cf4c16U,
+	0x29cde4bcU, 0x5ce4b8c9U, 0x19f1e88cU, 0x09e5ec9cU,
+	0xed38d578U, 0x6e5836fbU, 0x955ecb00U, 0xe2f71577U,
+	0xfba9526eU, 0x915bca04U, 0x163e2883U, 0x321321a7U,
+	0x5e643acbU, 0x2dc8e5b8U, 0x0ca0ac99U, 0xefb8577aU,
+	0xdc449849U, 0x724331e7U, 0xd04b9b45U, 0xd901d84cU,
+	0xabed463eU, 0x08a5ad9dU, 0x3e1c22abU, 0x78c9b1edU,
+	0x234764b6U, 0xb839812dU, 0xfeec126bU, 0x4b357edeU,
+	0x2e0826bbU, 0x70c3b3e5U, 0xb6b60023U, 0x335360a6U,
+	0x941e8a01U, 0xe4729671U, 0x2f4867baU, 0xeafd177fU,
+	0x39c5fc72U, 0x209bbb6bU, 0xc506c38eU, 0x994dd4d2U,
+	0x51b7e61aU, 0xe52ecbaeU, 0x968214ddU, 0xbc209cf7U,
+	0x3a053f71U, 0xb8259df3U, 0x320f3d79U, 0x6981e822U,
+	0xc2c30189U, 0x8d5cd1c6U, 0xd2d70599U, 0x765a2c3dU,
+	0xcecc0285U, 0xffb04fb4U, 0x08b9b143U, 0xbbe55ef0U,
+	0xcac90381U, 0x980d95d3U, 0x725f2d39U, 0x6d84e926U,
+	0xc8498183U, 0x731f6c38U, 0x2e143a65U, 0xfaf50fb1U,
+	0x4726610cU, 0x78d5ad33U, 0x7a552f31U, 0x04b6b24fU,
+	0x10a7b75bU, 0x6b016a20U, 0x235b7868U, 0xb16fdefaU,
+	0x249eba6fU, 0xdd18c596U, 0x00b3b34bU, 0x8bd952c0U,
+	0xaeb41ae5U, 0x334f7c78U, 0x4e6c2205U, 0x6f046b24U,
+	0xd512c79eU, 0x5db8e516U, 0xd057879bU, 0xeba14aa0U,
+	0xf13fcebaU, 0xac3498e7U, 0x12273559U, 0xc446828fU,
+	0x45a6e30eU, 0x42632109U, 0xc103c28aU, 0x348abe7fU,
+	0x60cbab2bU, 0x1de8f556U, 0x308fbf7bU, 0x8959d0c2U,
+	0xb7ea5dfcU, 0x5cf8a417U, 0x9e8816d5U, 0xf8758db3U,
+	0x7f106f34U, 0x7e502e35U, 0xfc708cb7U, 0x9147d6daU,
+	0xad74d9e6U, 0xaff45be4U, 0x3e003e75U, 0x881991c3U,
+	0x6cc4a827U, 0x0776714cU, 0x928715d9U, 0x0636304dU,
+	0xf935ccb2U, 0x74daae3fU, 0x5f386714U, 0xc043838bU,
+	0xc3834088U, 0xcf8c4384U, 0x64ceaa2fU, 0xa83199e3U,
+	0xa7fe59ecU, 0xf3bf4cb8U, 0x09f9f042U, 0x4ceca007U,
+	0x8153d2caU, 0xcd0cc186U, 0x5e782615U, 0x4f2c6304U,
+	0xa3fb58e8U, 0x3b457e70U, 0x14a2b65fU, 0xb6aa1cfdU,
+	0x1a2d3751U, 0xf2ff0db9U, 0x5a7d2711U, 0x1b6d7650U,
+	0xefa44ba4U, 0x03737048U, 0x25defb6eU, 0xe86189a3U,
+	0xf6fa0cbdU, 0xeae10ba1U, 0x93c754d8U, 0x8e9c12c5U,
+	0x664e282dU, 0xe6ee08adU, 0x83d350c8U, 0x7cd0ac37U,
+	0x31cffe7aU, 0x624b2929U, 0x7b156e30U, 0x3f407f74U,
+	0x1ca8b457U, 0x2dd4f966U, 0xfef00eb5U, 0x2b517a60U,
+	0x53376418U, 0xbaa51ff1U, 0x6a412b21U, 0xfd30cdb6U,
+	0xe06b8babU, 0x719fee3aU, 0x41a3e20aU, 0x18adb553U,
+	0xc909c082U, 0x9c0894d7U, 0x48e9a103U, 0x29d1f862U,
+	0x630b6828U, 0x7d90ed36U, 0x829311c9U, 0xb42a9effU,
+	0xec6488a7U, 0xbd60ddf6U, 0xa971d8e2U, 0x59bde412U,
+	0x8556d3ceU, 0xe7ae49acU, 0x360a3c7dU, 0xe46e8aafU,
+	0xe12bcaaaU, 0xd85d8593U, 0x1762755cU, 0xf07f8fbbU,
+	0x0cbcb047U, 0xb2af1df9U, 0x0a393341U, 0x374a7d7cU,
+	0xd452869fU, 0x2c94b867U, 0xaab11be1U, 0xa2bb19e9U,
+	0xb56adffeU, 0x50f7a71bU, 0xa43e9aefU, 0xed24c9a6U,
+	0x1622345dU, 0xb965dcf2U, 0xe921c8a2U, 0xdadd0791U,
+	0x68c1a923U, 0xfbb54eb0U, 0x49a9e002U, 0xe3ab48a8U,
+	0x618bea2aU, 0xc6c6008dU, 0xeee40aa5U, 0xcc4c8087U,
+	0x5672241dU, 0x2f547b64U, 0x5b3d6610U, 0xdf984794U,
+	0x35caff7eU, 0x0dfcf146U, 0xbea01ef5U, 0x771a6d3cU,
+	0x19edf452U, 0x9542d7deU, 0x58fda513U, 0xded80695U,
+	0x6e442a25U, 0x9d48d5d6U, 0xcb894280U, 0xabf15ae0U,
+	0xd792459cU, 0xd3974498U, 0x0f7c7344U, 0x275e796cU,
+	0x54f2a61fU, 0x0e3c3245U, 0xdc588497U, 0x2891b963U,
+	0x8c1c90c7U, 0x15e2f75eU, 0x900797dbU, 0x9fc857d4U,
+	0x261e386dU, 0x05f6f34eU, 0x13677458U, 0x670e692cU,
+	0x1e283655U, 0x221b3969U, 0x0b797240U, 0x01f3f24aU,
+	0x4a692301U, 0x658eeb2eU, 0xa6be18edU, 0x40e3a30bU,
+	0xf47a8ebfU, 0x55b2e71eU, 0x8fdc53c4U, 0x940296dfU,
+	0xd6d2049dU, 0xb3ef5cf8U, 0x21dbfa6aU, 0x2a113b61U,
+	0x801393cbU, 0xf53acfbeU, 0xb02f9ffbU, 0xa03b9bebU,
+	0x44e6a20fU, 0xc786418cU, 0x3c80bc77U, 0x4b296200U,
+	0x52772519U, 0x3885bd73U, 0xbfe05ff4U, 0x9bcd56d0U,
+	0xf7ba4dbcU, 0x841692cfU, 0xa57edbeeU, 0x4666200dU,
+	0x759aef3eU, 0xdb9d4690U, 0x7995ec32U, 0x70dfaf3bU,
+	0x02333149U, 0xa17bdaeaU, 0x97c255dcU, 0xd117c69aU,
+	0x8a9913c1U, 0x11e7f65aU, 0x5732651cU, 0xe2eb09a9U,
+	0x87d651ccU, 0xd91dc492U, 0x1f687754U, 0x9a8d17d1U,
+	0x3dc0fd76U, 0x4dace106U, 0x869610cdU, 0x43236008U,
+	0x6782e56bU, 0x7edca272U, 0x9b41da97U, 0xc70acdcbU,
+	0x0ff0ff03U, 0xbb69d2b7U, 0xc8c50dc4U, 0xe26785eeU,
+	0x64422668U, 0xe66284eaU, 0x6c482460U, 0x37c6f13bU,
+	0x9c841890U, 0xd31bc8dfU, 0x8c901c80U, 0x281d3524U,
+	0x908b1b9cU, 0xa1f756adU, 0x56fea85aU, 0xe5a247e9U,
+	0x948e1a98U, 0xc64a8ccaU, 0x2c183420U, 0x33c3f03fU,
+	0x960e989aU, 0x2d587521U, 0x7053237cU, 0xa4b216a8U,
+	0x19617815U, 0x2692b42aU, 0x24123628U, 0x5af1ab56U,
+	0x4ee0ae42U, 0x35467339U, 0x7d1c6171U, 0xef28c7e3U,
+	0x7ad9a376U, 0x835fdc8fU, 0x5ef4aa52U, 0xd59e4bd9U,
+	0xf0f303fcU, 0x6d086561U, 0x102b3b1cU, 0x3143723dU,
+	0x8b55de87U, 0x03fffc0fU, 0x8e109e82U, 0xb5e653b9U,
+	0xaf78d7a3U, 0xf27381feU, 0x4c602c40U, 0x9a019b96U,
+	0x1be1fa17U, 0x1c243810U, 0x9f44db93U, 0x6acda766U,
+	0x3e8cb232U, 0x43afec4fU, 0x6ec8a662U, 0xd71ec9dbU,
+	0xe9ad44e5U, 0x02bfbd0eU, 0xc0cf0fccU, 0xa63294aaU,
+	0x2157762dU, 0x2017372cU, 0xa23795aeU, 0xcf00cfc3U,
+	0xf333c0ffU, 0xf1b342fdU, 0x6047276cU, 0xd65e88daU,
+	0x3283b13eU, 0x59316855U, 0xccc00cc0U, 0x58712954U,
+	0xa772d5abU, 0x2a9db726U, 0x017f7e0dU, 0x9e049a92U,
+	0x9dc45991U, 0x91cb5a9dU, 0x3a89b336U, 0xf67680faU,
+	0xf9b940f5U, 0xadf855a1U, 0x57bee95bU, 0x12abb91eU,
+	0xdf14cbd3U, 0x934bd89fU, 0x003f3f0cU, 0x116b7a1dU,
+	0xfdbc41f1U, 0x65026769U, 0x4ae5af46U, 0xe8ed05e4U,
+	0x446a2e48U, 0xacb814a0U, 0x043a3e08U, 0x452a6f49U,
+	0xb1e352bdU, 0x5d346951U, 0x7b99e277U, 0xb62690baU,
+	0xa8bd15a4U, 0xb4a612b8U, 0xcd804dc1U, 0xd0db0bdcU,
+	0x38093134U, 0xb8a911b4U, 0xdd9449d1U, 0x2297b52eU,
+	0x6f88e763U, 0x3c0c3030U, 0x25527729U, 0x6107666dU,
+	0x42efad4eU, 0x7393e07fU, 0xa0b717acU, 0x75166379U,
+	0x0d707d01U, 0xe4e206e8U, 0x34063238U, 0xa377d4afU,
+	0xbe2c92b2U, 0x2fd8f723U, 0x1fe4fb13U, 0x46eaac4aU,
+	0x974ed99bU, 0xc24f8dceU, 0x16aeb81aU, 0x7796e17bU,
+	0x3d4c7131U, 0x23d7f42fU, 0xdcd408d0U, 0xea6d87e6U,
+	0xb22391beU, 0xe327c4efU, 0xf736c1fbU, 0x07fafd0bU,
+	0xdb11cad7U, 0xb9e950b5U, 0x684d2564U, 0xba2993b6U,
+	0xbf6cd3b3U, 0x861a9c8aU, 0x49256c45U, 0xae3896a2U,
+	0x52fba95eU, 0xece804e0U, 0x547e2a58U, 0x690d6465U,
+	0x8a159f86U, 0x72d3a17eU, 0xf4f602f8U, 0xfcfc00f0U,
+	0xeb2dc6e7U, 0x0eb0be02U, 0xfa7983f6U, 0xb363d0bfU,
+	0x48652d44U, 0xe722c5ebU, 0xb766d1bbU, 0x849a1e88U,
+	0x3686b03aU, 0xa5f257a9U, 0x17eef91bU, 0xbdec51b1U,
+	0x3fccf333U, 0x98811994U, 0xb0a313bcU, 0x920b999eU,
+	0x08353d04U, 0x7113627dU, 0x057a7f09U, 0x81df5e8dU,
+	0x6b8de667U, 0x53bbe85fU, 0xe0e707ecU, 0x295d7425U,
+	0x47aaed4bU, 0xcb05cec7U, 0x06babc0aU, 0x809f1f8cU,
+	0x3003333cU, 0xc30fcccfU, 0x95ce5b99U, 0xf5b643f9U,
+	0x89d55c85U, 0x8dd05d81U, 0x513b6a5dU, 0x79196075U,
+	0x0ab5bf06U, 0x507b2b5cU, 0x821f9d8eU, 0x76d6a07aU,
+	0xd25b89deU, 0x4ba5ee47U, 0xce408ec2U, 0xc18f4ecdU,
+	0x78592174U, 0x5bb1ea57U, 0x4d206d41U, 0x39497035U,
+	0x406f2f4cU, 0x7c5c2070U, 0x553e6b59U, 0x5fb4eb53U,
+	0x142e3a18U, 0x3bc9f237U, 0xf8f901f4U, 0x1ea4ba12U,
+	0xaa3d97a6U, 0x0bf5fe07U, 0xd19b4addU, 0xca458fc6U,
+	0x88951d84U, 0xeda845e1U, 0x7f9ce373U, 0x74562278U,
+	0xde548ad2U, 0xab7dd6a7U, 0xee6886e2U, 0xfe7c82f2U,
+	0x1aa1bb16U, 0x99c15895U, 0x62c7a56eU, 0x156e7b19U,
+	0x0c303c00U, 0x66c2a46aU, 0xe1a746edU, 0xc58a4fc9U,
+	0xa9fd54a5U, 0xda518bd6U, 0xfb39c2f7U, 0x18213914U,
+	0x2bddf627U, 0x85da5f89U, 0x27d2f52bU, 0x2e98b622U,
+	0x5c742850U, 0xff3cc3f3U, 0xc9854cc5U, 0x8f50df83U,
+	0xd4de0ad8U, 0x4fa0ef43U, 0x09757c05U, 0xbcac10b0U,
+	0xd99148d5U, 0x875add8bU, 0x412f6e4dU, 0xc4ca0ec8U,
+	0x6387e46fU, 0x13ebf81fU, 0xd8d109d4U, 0x1d647911U,
+	0x951a8f01U, 0x8c44c818U, 0x69d9b0fdU, 0x3592a7a1U,
+	0xfd689569U, 0x49f1b8ddU, 0x3a5d67aeU, 0x10ffef84U,
+	0x96da4c02U, 0x14faee80U, 0x9ed04e0aU, 0xc55e9b51U,
+	0x6e1c72faU, 0x2183a2b5U, 0x7e0876eaU, 0xda855f4eU,
+	0x621371f6U, 0x536f3cc7U, 0xa466c230U, 0x173a2d83U,
+	0x661670f2U, 0x34d2e6a0U, 0xde805e4aU, 0xc15b9a55U,
+	0x6496f2f0U, 0xdfc01f4bU, 0x82cb4916U, 0x562a7cc2U,
+	0xebf9127fU, 0xd40ade40U, 0xd68a5c42U, 0xa869c13cU,
+	0xbc78c428U, 0xc7de1953U, 0x8f840b1bU, 0x1db0ad89U,
+	0x8841c91cU, 0x71c7b6e5U, 0xac6cc038U, 0x270621b3U,
+	0x026b6996U, 0x9f900f0bU, 0xe2b35176U, 0xc3db1857U,
+	0x79cdb4edU, 0xf1679665U, 0x7c88f4e8U, 0x477e39d3U,
+	0x5de0bdc9U, 0x00ebeb94U, 0xbef8462aU, 0x6899f1fcU,
+	0xe979907dU, 0xeebc527aU, 0x6ddcb1f9U, 0x9855cd0cU,
+	0xcc14d858U, 0xb1378625U, 0x9c50cc08U, 0x2586a3b1U,
+	0x1b352e8fU, 0xf027d764U, 0x325765a6U, 0x54aafec0U,
+	0xd3cf1c47U, 0xd28f5d46U, 0x50afffc4U, 0x3d98a5a9U,
+	0x01abaa95U, 0x032b2897U, 0x92df4d06U, 0x24c6e2b0U,
+	0xc01bdb54U, 0xaba9023fU, 0x3e5866aaU, 0xaae9433eU,
+	0x55eabfc1U, 0xd805dd4cU, 0xf3e71467U, 0x6c9cf0f8U,
+	0x6f5c33fbU, 0x635330f7U, 0xc811d95cU, 0x04eeea90U,
+	0x0b212a9fU, 0x5f603fcbU, 0xa5268331U, 0xe033d374U,
+	0x2d8ca1b9U, 0x61d3b2f5U, 0xf2a75566U, 0xe3f31077U,
+	0x0f242b9bU, 0x979a0d03U, 0xb87dc52cU, 0x1a756f8eU,
+	0xb6f24422U, 0x5e207ecaU, 0xf6a25462U, 0xb7b20523U,
+	0x437b38d7U, 0xafac033bU, 0x8901881dU, 0x44befad0U,
+	0x5a257fceU, 0x463e78d2U, 0x3f1827abU, 0x224361b6U,
+	0xca915b5eU, 0x4a317bdeU, 0x2f0c23bbU, 0xd00fdf44U,
+	0x9d108d09U, 0xce945a5aU, 0xd7ca1d43U, 0x939f0c07U,
+	0xb077c724U, 0x810b8a15U, 0x522f7dc6U, 0x878e0913U,
+	0xffe8176bU, 0x167a6c82U, 0xc69e5852U, 0x51efbec5U,
+	0x4cb4f8d8U, 0xdd409d49U, 0xed7c9179U, 0xb472c620U,
+	0x65d6b3f1U, 0x30d7e7a4U, 0xe436d270U, 0x850e8b11U,
+	0xcfd41b5bU, 0xd14f9e45U, 0x2e4c62baU, 0x18f5ed8cU,
+	0x40bbfbd4U, 0x11bfae85U, 0x05aeab91U, 0xf5629761U,
+	0x2989a0bdU, 0x4b713adfU, 0x9ad54f0eU, 0x48b1f9dcU,
+	0x4df4b9d9U, 0x7482f6e0U, 0xbbbd062fU, 0x5ca0fcc8U,
+	0xa063c334U, 0x1e706e8aU, 0xa6e64032U, 0x9b950e0fU,
+	0x788df5ecU, 0x804bcb14U, 0x066e6892U, 0x0e646a9aU,
+	0x19b5ac8dU, 0xfc28d468U, 0x08e1e99cU, 0x41fbbad5U,
+	0xbafd472eU, 0x15baaf81U, 0x45febbd1U, 0x760274e2U,
+	0xc41eda50U, 0x576a3dc3U, 0xe5769371U, 0x4f743bdbU,
+	0xcd549959U, 0x6a1973feU, 0x423b79d6U, 0x6093f3f4U,
+	0xfaad576eU, 0x838b0817U, 0xf7e21563U, 0x734734e7U,
+	0x99158c0dU, 0xa1238235U, 0x127f6d86U, 0xdbc51e4fU,
+	0xb5328721U, 0x399da4adU, 0xf422d660U, 0x720775e6U,
+	0xc29b5956U, 0x3197a6a5U, 0x675631f3U, 0x072e2993U,
+	0x7b4d36efU, 0x7f4837ebU, 0xa3a30037U, 0x8b810a1fU,
+	0xf82dd56cU, 0xa2e34136U, 0x7087f7e4U, 0x844eca10U,
+	0x20c3e3b4U, 0xb93d842dU, 0x3cd8e4a8U, 0x331724a7U,
+	0x8ac14b1eU, 0xa929803dU, 0xbfb8072bU, 0xcbd11a5fU,
+	0xb2f74526U, 0x8ec44a1aU, 0xa7a60133U, 0xad2c8139U,
+	0xe6b65072U, 0xc951985dU, 0x0a616b9eU, 0xec3cd078U,
+	0x58a5fdccU, 0xf96d946dU, 0x230320b7U, 0x38dde5acU,
+	0x7a0d77eeU, 0x1f302f8bU, 0x8d048919U, 0x86ce4812U,
+	0x2ccce0b8U, 0x59e5bccdU, 0x1cf0ec88U, 0x0ce4e898U,
+	0xe839d17cU, 0x6b5932ffU, 0x905fcf04U, 0xe7f61173U,
+	0xfea8566aU, 0x945ace00U, 0x133f2c87U, 0x371225a3U,
+	0x5b653ecfU, 0x28c9e1bcU, 0x09a1a89dU, 0xeab9537eU,
+	0xd9459c4dU, 0x774235e3U, 0xd54a9f41U, 0xdc00dc48U,
+	0xaeec423aU, 0x0da4a999U, 0x3b1d26afU, 0x7dc8b5e9U,
+	0x264660b2U, 0xbd388529U, 0xfbed166fU, 0x4e347adaU,
+	0x2b0922bfU, 0x75c2b7e1U, 0xb3b70427U, 0x365264a2U,
+	0x911f8e05U, 0xe1739275U, 0x2a4963beU, 0xeffc137bU,
+	0xf0f80886U, 0xe9a64f9fU, 0x0c3b377aU, 0x50702026U,
+	0x988a12eeU, 0x2c133f5aU, 0x5fbfe029U, 0x751d6803U,
+	0xf338cb85U, 0x71186907U, 0xfb32c98dU, 0xa0bc1cd6U,
+	0x0bfef57dU, 0x44612532U, 0x1beaf16dU, 0xbf67d8c9U,
+	0x07f1f671U, 0x368dbb40U, 0xc18445b7U, 0x72d8aa04U,
+	0x03f4f775U, 0x51306127U, 0xbb62d9cdU, 0xa4b91dd2U,
+	0x01747577U, 0xba2298ccU, 0xe729ce91U, 0x33c8fb45U,
+	0x8e1b95f8U, 0xb1e859c7U, 0xb368dbc5U, 0xcd8b46bbU,
+	0xd99a43afU, 0xa23c9ed4U, 0xea668c9cU, 0x78522a0eU,
+	0xeda34e9bU, 0x14253162U, 0xc98e47bfU, 0x42e4a634U,
+	0x6789ee11U, 0xfa72888cU, 0x8751d6f1U, 0xa6399fd0U,
+	0x1c2f336aU, 0x948511e2U, 0x196a736fU, 0x229cbe54U,
+	0x38023a4eU, 0x65096c13U, 0xdb1ac1adU, 0x0d7b767bU,
+	0x8c9b17faU, 0x8b5ed5fdU, 0x083e367eU, 0xfdb74a8bU,
+	0xa9f65fdfU, 0xd4d501a2U, 0xf9b24b8fU, 0x40642436U,
+	0x7ed7a908U, 0x95c550e3U, 0x57b5e221U, 0x31487947U,
+	0xb62d9bc0U, 0xb76ddac1U, 0x354d7843U, 0x587a222eU,
+	0x64492d12U, 0x66c9af10U, 0xf73dca81U, 0x41246537U,
+	0xa5f95cd3U, 0xce4b85b8U, 0x5bbae12dU, 0xcf0bc4b9U,
+	0x30083846U, 0xbde75acbU, 0x960593e0U, 0x097e777fU,
+	0x0abeb47cU, 0x06b1b770U, 0xadf35edbU, 0x610c6d17U,
+	0x6ec3ad18U, 0x3a82b84cU, 0xc0c404b6U, 0x85d154f3U,
+	0x486e263eU, 0x04313572U, 0x9745d2e1U, 0x861197f0U,
+	0x6ac6ac1cU, 0xf2788a84U, 0xdd9f42abU, 0x7f97e809U,
+	0xd310c3a5U, 0x3bc2f94dU, 0x9340d3e5U, 0xd25082a4U,
+	0x2699bf50U, 0xca4e84bcU, 0xece30f9aU, 0x215c7d57U,
+	0x3fc7f849U, 0x23dcff55U, 0x5afaa02cU, 0x47a1e631U,
+	0xaf73dcd9U, 0x2fd3fc59U, 0x4aeea43cU, 0xb5ed58c3U,
+	0xf8f20a8eU, 0xab76ddddU, 0xb2289ac4U, 0xf67d8b80U,
+	0xd59540a3U, 0xe4e90d92U, 0x37cdfa41U, 0xe26c8e94U,
+	0x9a0a90ecU, 0x7398eb05U, 0xa37cdfd5U, 0x340d3942U,
+	0x29567f5fU, 0xb8a21aceU, 0x889e16feU, 0xd19041a7U,
+	0x00343476U, 0x55356023U, 0x81d455f7U, 0xe0ec0c96U,
+	0xaa369cdcU, 0xb4ad19c2U, 0x4baee53dU, 0x7d176a0bU,
+	0x25597c53U, 0x745d2902U, 0x604c2c16U, 0x908010e6U,
+	0x4c6b273aU, 0x2e93bd58U, 0xff37c889U, 0x2d537e5bU,
+	0x28163e5eU, 0x11607167U, 0xde5f81a8U, 0x39427b4fU,
+	0xc58144b3U, 0x7b92e90dU, 0xc304c7b5U, 0xfe778988U,
+	0x1d6f726bU, 0xe5a94c93U, 0x638cef15U, 0x6b86ed1dU,
+	0x7c572b0aU, 0x99ca53efU, 0x6d036e1bU, 0x24193d52U,
+	0xdf1fc0a9U, 0x70582806U, 0x201c3c56U, 0x13e0f365U,
+	0xa1fc5dd7U, 0x3288ba44U, 0x809414f6U, 0x2a96bc5cU,
+	0xa8b61edeU, 0x0ffbf479U, 0x27d9fe51U, 0x05717473U,
+	0x9f4fd0e9U, 0xe6698f90U, 0x920092e4U, 0x16a5b360U,
+	0xfcf70b8aU, 0xc4c105b2U, 0x779dea01U, 0xbe2799c8U,
+	0xd0d000a6U, 0x5c7f232aU, 0x91c051e7U, 0x17e5f261U,
+	0xa779ded1U, 0x54752122U, 0x02b4b674U, 0x62ccae14U,
+	0x1eafb168U, 0x1aaab06cU, 0xc64187b0U, 0xee638d98U,
+	0x9dcf52ebU, 0xc701c6b1U, 0x15657063U, 0xe1ac4d97U,
+	0x45216433U, 0xdcdf03aaU, 0x593a632fU, 0x56f5a320U,
+	0xef23cc99U, 0xcccb07baU, 0xda5a80acU, 0xae339dd8U,
+	0xd715c2a1U, 0xeb26cd9dU, 0xc24486b4U, 0xc8ce06beU,
+	0x8354d7f5U, 0xacb31fdaU, 0x6f83ec19U, 0x89de57ffU,
+	0x3d477a4bU, 0x9c8f13eaU, 0x46e1a730U, 0x5d3f622bU,
+	0x1feff069U, 0x7ad2a80cU, 0xe8e60e9eU, 0xe32ccf95U,
+	0x492e673fU, 0x3c073b4aU, 0x79126b0fU, 0x69066f1fU,
+	0x8ddb56fbU, 0x0ebbb578U, 0xf5bd4883U, 0x821496f4U,
+	0x9b4ad1edU, 0xf1b84987U, 0x76ddab00U, 0x52f0a224U,
+	0x3e87b948U, 0x4d2b663bU, 0x6c432f1aU, 0x8f5bd4f9U,
+	0xbca71bcaU, 0x12a0b264U, 0xb0a818c6U, 0xb9e25bcfU,
+	0xcb0ec5bdU, 0x68462e1eU, 0x5effa128U, 0x182a326eU,
+	0x43a4e735U, 0xd8da02aeU, 0x9e0f91e8U, 0x2bd6fd5dU,
+	0x4eeba538U, 0x10203066U, 0xd65583a0U, 0x53b0e325U,
+	0xf4fd0982U, 0x849115f2U, 0x4fabe439U, 0x8a1e94fcU,
+	0xddf12ca2U, 0xc4af6bbbU, 0x2132135eU, 0x7d790402U,
+	0xb58336caU, 0x011a1b7eU, 0x72b6c40dU, 0x58144c27U,
+	0xde31efa1U, 0x5c114d23U, 0xd63beda9U, 0x8db538f2U,
+	0x26f7d159U, 0x69680116U, 0x36e3d549U, 0x926efcedU,
+	0x2af8d255U, 0x1b849f64U, 0xec8d6193U, 0x5fd18e20U,
+	0x2efdd351U, 0x7c394503U, 0x966bfde9U, 0x89b039f6U,
+	0x2c7d5153U, 0x972bbce8U, 0xca20eab5U, 0x1ec1df61U,
+	0xa312b1dcU, 0x9ce17de3U, 0x9e61ffe1U, 0xe082629fU,
+	0xf493678bU, 0x8f35baf0U, 0xc76fa8b8U, 0x555b0e2aU,
+	0xc0aa6abfU, 0x392c1546U, 0xe487639bU, 0x6fed8210U,
+	0x4a80ca35U, 0xd77baca8U, 0xaa58f2d5U, 0x8b30bbf4U,
+	0x3126174eU, 0xb98c35c6U, 0x3463574bU, 0x0f959a70U,
+	0x150b1e6aU, 0x48004837U, 0xf613e589U, 0x2072525fU,
+	0xa19233deU, 0xa657f1d9U, 0x2537125aU, 0xd0be6eafU,
+	0x84ff7bfbU, 0xf9dc2586U, 0xd4bb6fabU, 0x6d6d0012U,
+	0x53de8d2cU, 0xb8cc74c7U, 0x7abcc605U, 0x1c415d63U,
+	0x9b24bfe4U, 0x9a64fee5U, 0x18445c67U, 0x7573060aU,
+	0x49400936U, 0x4bc08b34U, 0xda34eea5U, 0x6c2d4113U,
+	0x88f078f7U, 0xe342a19cU, 0x76b3c509U, 0xe202e09dU,
+	0x1d011c62U, 0x90ee7eefU, 0xbb0cb7c4U, 0x2477535bU,
+	0x27b79058U, 0x2bb89354U, 0x80fa7affU, 0x4c054933U,
+	0x43ca893cU, 0x178b9c68U, 0xedcd2092U, 0xa8d870d7U,
+	0x6567021aU, 0x29381156U, 0xba4cf6c5U, 0xab18b3d4U,
+	0x47cf8838U, 0xdf71aea0U, 0xf096668fU, 0x529ecc2dU,
+	0xfe19e781U, 0x16cbdd69U, 0xbe49f7c1U, 0xff59a680U,
+	0x0b909b74U, 0xe747a098U, 0xc1ea2bbeU, 0x0c555973U,
+	0x12cedc6dU, 0x0ed5db71U, 0x77f38408U, 0x6aa8c215U,
+	0x827af8fdU, 0x02dad87dU, 0x67e78018U, 0x98e47ce7U,
+	0xd5fb2eaaU, 0x867ff9f9U, 0x9f21bee0U, 0xdb74afa4U,
+	0xf89c6487U, 0xc9e029b6U, 0x1ac4de65U, 0xcf65aab0U,
+	0xb703b4c8U, 0x5e91cf21U, 0x8e75fbf1U, 0x19041d66U,
+	0x045f5b7bU, 0x95ab3eeaU, 0xa59732daU, 0xfc996583U,
+	0x2d3d1052U, 0x783c4407U, 0xacdd71d3U, 0xcde528b2U,
+	0x873fb8f8U, 0x99a43de6U, 0x66a7c119U, 0x501e4e2fU,
+	0x08505877U, 0x59540d26U, 0x4d450832U, 0xbd8934c2U,
+	0x6162031eU, 0x039a997cU, 0xd23eecadU, 0x005a5a7fU,
+	0x051f1a7aU, 0x3c695543U, 0xf356a58cU, 0x144b5f6bU,
+	0xe8886097U, 0x569bcd29U, 0xee0de391U, 0xd37eadacU,
+	0x3066564fU, 0xc8a068b7U, 0x4e85cb31U, 0x468fc939U,
+	0x515e0f2eU, 0xb4c377cbU, 0x400a4a3fU, 0x09101976U,
+	0xf216e48dU, 0x5d510c22U, 0x0d151872U, 0x3ee9d741U,
+	0x8cf579f3U, 0x1f819e60U, 0xad9d30d2U, 0x079f9878U,
+	0x85bf3afaU, 0x22f2d05dU, 0x0ad0da75U, 0x28785057U,
+	0xb246f4cdU, 0xcb60abb4U, 0xbf09b6c0U, 0x3bac9744U,
+	0xd1fe2faeU, 0xe9c82196U, 0x5a94ce25U, 0x932ebdecU,
+	0xfdd92482U, 0x7176070eU, 0xbcc975c3U, 0x3aecd645U,
+	0x8a70faf5U, 0x797c0506U, 0x2fbd9250U, 0x4fc58a30U,
+	0x33a6954cU, 0x37a39448U, 0xeb48a394U, 0xc36aa9bcU,
+	0xb0c676cfU, 0xea08e295U, 0x386c5447U, 0xcca569b3U,
+	0x68284017U, 0xf1d6278eU, 0x7433470bU, 0x7bfc8704U,
+	0xc22ae8bdU, 0xe1c2239eU, 0xf753a488U, 0x833ab9fcU,
+	0xfa1ce685U, 0xc62fe9b9U, 0xef4da290U, 0xe5c7229aU,
+	0xae5df3d1U, 0x81ba3bfeU, 0x428ac83dU, 0xa4d773dbU,
+	0x104e5e6fU, 0xb18637ceU, 0x6be88314U, 0x7036460fU,
+	0x32e6d44dU, 0x57db8c28U, 0xc5ef2abaU, 0xce25ebb1U,
+	0x6427431bU, 0x110e1f6eU, 0x541b4f2bU, 0x440f4b3bU,
+	0xa0d272dfU, 0x23b2915cU, 0xd8b46ca7U, 0xaf1db2d0U,
+	0xb643f5c9U, 0xdcb16da3U, 0x5bd48f24U, 0x7ff98600U,
+	0x138e9d6cU, 0x6022421fU, 0x414a0b3eU, 0xa252f0ddU,
+	0x91ae3feeU, 0x3fa99640U, 0x9da13ce2U, 0x94eb7febU,
+	0xe607e199U, 0x454f0a3aU, 0x73f6850cU, 0x3523164aU,
+	0x6eadc311U, 0xf5d3268aU, 0xb306b5ccU, 0x06dfd979U,
+	0x63e2811cU, 0x3d291442U, 0xfb5ca784U, 0x7eb9c701U,
+	0xd9f42da6U, 0xa99831d6U, 0x62a2c01dU, 0xa717b0d8U,
+	0xaaea40ceU, 0xb3b407d7U, 0x56297f32U, 0x0a62686eU,
+	0xc2985aa6U, 0x76017712U, 0x05ada861U, 0x2f0f204bU,
+	0xa92a83cdU, 0x2b0a214fU, 0xa12081c5U, 0xfaae549eU,
+	0x51ecbd35U, 0x1e736d7aU, 0x41f8b925U, 0xe5759081U,
+	0x5de3be39U, 0x6c9ff308U, 0x9b960dffU, 0x28cae24cU,
+	0x59e6bf3dU, 0x0b22296fU, 0xe1709185U, 0xfeab559aU,
+	0x5b663d3fU, 0xe030d084U, 0xbd3b86d9U, 0x69dab30dU,
+	0xd409ddb0U, 0xebfa118fU, 0xe97a938dU, 0x97990ef3U,
+	0x83880be7U, 0xf82ed69cU, 0xb074c4d4U, 0x22406246U,
+	0xb7b106d3U, 0x4e37792aU, 0x939c0ff7U, 0x18f6ee7cU,
+	0x3d9ba659U, 0xa060c0c4U, 0xdd439eb9U, 0xfc2bd798U,
+	0x463d7b22U, 0xce9759aaU, 0x43783b27U, 0x788ef61cU,
+	0x62107206U, 0x3f1b245bU, 0x810889e5U, 0x57693e33U,
+	0xd6895fb2U, 0xd14c9db5U, 0x522c7e36U, 0xa7a502c3U,
+	0xf3e41797U, 0x8ec749eaU, 0xa3a003c7U, 0x1a766c7eU,
+	0x24c5e140U, 0xcfd718abU, 0x0da7aa69U, 0x6b5a310fU,
+	0xec3fd388U, 0xed7f9289U, 0x6f5f300bU, 0x02686a66U,
+	0x3e5b655aU, 0x3cdbe758U, 0xad2f82c9U, 0x1b362d7fU,
+	0xffeb149bU, 0x9459cdf0U, 0x01a8a965U, 0x95198cf1U,
+	0x6a1a700eU, 0xe7f51283U, 0xcc17dba8U, 0x536c3f37U,
+	0x50acfc34U, 0x5ca3ff38U, 0xf7e11693U, 0x3b1e255fU,
+	0x34d1e550U, 0x6090f004U, 0x9ad64cfeU, 0xdfc31cbbU,
+	0x127c6e76U, 0x5e237d3aU, 0xcd579aa9U, 0xdc03dfb8U,
+	0x30d4e454U, 0xa86ac2ccU, 0x878d0ae3U, 0x2585a041U,
+	0x89028bedU, 0x61d0b105U, 0xc9529badU, 0x8842caecU,
+	0x7c8bf718U, 0x905cccf4U, 0xb6f147d2U, 0x7b4e351fU,
+	0x65d5b001U, 0x79ceb71dU, 0x00e8e864U, 0x1db3ae79U,
+	0xf5619491U, 0x75c1b411U, 0x10fcec74U, 0xefff108bU,
+	0xa2e042c6U, 0xf1649595U, 0xe83ad28cU, 0xac6fc3c8U,
+	0x8f8708ebU, 0xbefb45daU, 0x6ddfb209U, 0xb87ec6dcU,
+	0xc018d8a4U, 0x298aa34dU, 0xf96e979dU, 0x6e1f710aU,
+	0x73443717U, 0xe2b05286U, 0xd28c5eb6U, 0x8b8209efU,
+	0x5a267c3eU, 0x0f27286bU, 0xdbc61dbfU, 0xbafe44deU,
+	0xf024d494U, 0xeebf518aU, 0x11bcad75U, 0x27052243U,
+	0x7f4b341bU, 0x2e4f614aU, 0x3a5e645eU, 0xca9258aeU,
+	0x16796f72U, 0x7481f510U, 0xa52580c1U, 0x77413613U,
+	0x72047616U, 0x4b72392fU, 0x844dc9e0U, 0x63503307U,
+	0x9f930cfbU, 0x2180a145U, 0x99168ffdU, 0xa465c1c0U,
+	0x477d3a23U, 0xbfbb04dbU, 0x399ea75dU, 0x3194a555U,
+	0x26456342U, 0xc3d81ba7U, 0x37112653U, 0x7e0b751aU,
+	0x850d88e1U, 0x2a4a604eU, 0x7a0e741eU, 0x49f2bb2dU,
+	0xfbee159fU, 0x689af20cU, 0xda865cbeU, 0x7084f414U,
+	0xf2a45696U, 0x55e9bc31U, 0x7dcbb619U, 0x5f633c3bU,
+	0xc55d98a1U, 0xbc7bc7d8U, 0xc812daacU, 0x4cb7fb28U,
+	0xa6e543c2U, 0x9ed34dfaU, 0x2d8fa249U, 0xe435d180U,
+	0x8ac248eeU, 0x066d6b62U, 0xcbd219afU, 0x4df7ba29U,
+	0xfd6b9699U, 0x0e67696aU, 0x58a6fe3cU, 0x38dee65cU,
+	0x44bdf920U, 0x40b8f824U, 0x9c53cff8U, 0xb471c5d0U,
+	0xc7dd1aa3U, 0x9d138ef9U, 0x4f77382bU, 0xbbbe05dfU,
+	0x1f332c7bU, 0x86cd4be2U, 0x03282b67U, 0x0ce7eb68U,
+	0xb53184d1U, 0x96d94ff2U, 0x8048c8e4U, 0xf421d590U,
+	0x8d078ae9U, 0xb13485d5U, 0x9856cefcU, 0x92dc4ef6U,
+	0xd9469fbdU, 0xf6a15792U, 0x3591a451U, 0xd3cc1fb7U,
+	0x67553203U, 0xc69d5ba2U, 0x1cf3ef78U, 0x072d2a63U,
+	0x45fdb821U, 0x20c0e044U, 0xb2f446d6U, 0xb93e87ddU,
+	0x133c2f77U, 0x66157302U, 0x23002347U, 0x33142757U,
+	0xd7c91eb3U, 0x54a9fd30U, 0xafaf00cbU, 0xd806debcU,
+	0xc15899a5U, 0xabaa01cfU, 0x2ccfe348U, 0x08e2ea6cU,
+	0x6495f100U, 0x17392e73U, 0x36516752U, 0xd5499cb1U,
+	0xe6b55382U, 0x48b2fa2cU, 0xeaba508eU, 0xe3f01387U,
+	0x911c8df5U, 0x32546656U, 0x04ede960U, 0x42387a26U,
+	0x19b6af7dU, 0x82c84ae6U, 0xc41dd9a0U, 0x71c4b515U,
+	0x14f9ed70U, 0x4a32782eU, 0x8c47cbe8U, 0x09a2ab6dU,
+	0xaeef41caU, 0xde835dbaU, 0x15b9ac71U, 0xd00cdcb4U,
+	0x063533bdU, 0x1f6b74a4U, 0xfaf60c41U, 0xa6bd1b1dU,
+	0x6e4729d5U, 0xdade0461U, 0xa972db12U, 0x83d05338U,
+	0x05f5f0beU, 0x87d5523cU, 0x0dfff2b6U, 0x567127edU,
+	0xfd33ce46U, 0xb2ac1e09U, 0xed27ca56U, 0x49aae3f2U,
+	0xf13ccd4aU, 0xc040807bU, 0x37497e8cU, 0x8415913fU,
+	0xf539cc4eU, 0xa7fd5a1cU, 0x4dafe2f6U, 0x527426e9U,
+	0xf7b94e4cU, 0x4cefa3f7U, 0x11e4f5aaU, 0xc505c07eU,
+	0x78d6aec3U, 0x472562fcU, 0x45a5e0feU, 0x3b467d80U,
+	0x2f577894U, 0x54f1a5efU, 0x1cabb7a7U, 0x8e9f1135U,
+	0x1b6e75a0U, 0xe2e80a59U, 0x3f437c84U, 0xb4299d0fU,
+	0x9144d52aU, 0x0cbfb3b7U, 0x719cedcaU, 0x50f4a4ebU,
+	0xeae20851U, 0x62482ad9U, 0xefa74854U, 0xd451856fU,
+	0xcecf0175U, 0x93c45728U, 0x2dd7fa96U, 0xfbb64d40U,
+	0x7a562cc1U, 0x7d93eec6U, 0xfef30d45U, 0x0b7a71b0U,
+	0x5f3b64e4U, 0x22183a99U, 0x0f7f70b4U, 0xb6a91f0dU,
+	0x881a9233U, 0x63086bd8U, 0xa178d91aU, 0xc785427cU,
+	0x40e0a0fbU, 0x41a0e1faU, 0xc3804378U, 0xaeb71915U,
+	0x92841629U, 0x9004942bU, 0x01f0f1baU, 0xb7e95e0cU,
+	0x533467e8U, 0x3886be83U, 0xad77da16U, 0x39c6ff82U,
+	0xc6c5037dU, 0x4b2a61f0U, 0x60c8a8dbU, 0xffb34c44U,
+	0xfc738f47U, 0xf07c8c4bU, 0x5b3e65e0U, 0x97c1562cU,
+	0x980e9623U, 0xcc4f8377U, 0x36093f8dU, 0x731c6fc8U,
+	0xbea31d05U, 0xf2fc0e49U, 0x6188e9daU, 0x70dcaccbU,
+	0x9c0b9727U, 0x04b5b1bfU, 0x2b527990U, 0x895ad332U,
+	0x25ddf89eU, 0xcd0fc276U, 0x658de8deU, 0x249db99fU,
+	0xd054846bU, 0x3c83bf87U, 0x1a2e34a1U, 0xd791466cU,
+	0xc90ac372U, 0xd511c46eU, 0xac379b17U, 0xb16cdd0aU,
+	0x59bee7e2U, 0xd91ec762U, 0xbc239f07U, 0x432063f8U,
+	0x0e3f31b5U, 0x5dbbe6e6U, 0x44e5a1ffU, 0x00b0b0bbU,
+	0x23587b98U, 0x122436a9U, 0xc100c17aU, 0x14a1b5afU,
+	0x6cc7abd7U, 0x8555d03eU, 0x55b1e4eeU, 0xc2c00279U,
+	0xdf9b4464U, 0x4e6f21f5U, 0x7e532dc5U, 0x275d7a9cU,
+	0xf6f90f4dU, 0xa3f85b18U, 0x77196eccU, 0x162137adU,
+	0x5cfba7e7U, 0x426022f9U, 0xbd63de06U, 0x8bda5130U,
+	0xd3944768U, 0x82901239U, 0x9681172dU, 0x664d2bddU,
+	0xbaa61c01U, 0xd85e8663U, 0x09faf3b2U, 0xdb9e4560U,
+	0xdedb0565U, 0xe7ad4a5cU, 0x2892ba93U, 0xcf8f4074U,
+	0x334c7f88U, 0x8d5fd236U, 0x35c9fc8eU, 0x08bab2b3U,
+	0xeba24950U, 0x136477a8U, 0x9541d42eU, 0x9d4bd626U,
+	0x8a9a1031U, 0x6f0768d4U, 0x9bce5520U, 0xd2d40669U,
+	0x29d2fb92U, 0x8695133dU, 0xd6d1076dU, 0xe52dc85eU,
+	0x573166ecU, 0xc445817fU, 0x76592fcdU, 0xdc5b8767U,
+	0x5e7b25e5U, 0xf936cf42U, 0xd114c56aU, 0xf3bc4f48U,
+	0x6982ebd2U, 0x10a4b4abU, 0x64cda9dfU, 0xe068885bU,
+	0x0a3a30b1U, 0x320c3e89U, 0x8150d13aU, 0x48eaa2f3U,
+	0x261d3b9dU, 0xaab21811U, 0x670d6adcU, 0xe128c95aU,
+	0x51b4e5eaU, 0xa2b81a19U, 0xf4798d4fU, 0x9401952fU,
+	0xe8628a53U, 0xec678b57U, 0x308cbc8bU, 0x18aeb6a3U,
+	0x6b0269d0U, 0x31ccfd8aU, 0xe3a84b58U, 0x176176acU,
+	0xb3ec5f08U, 0x2a123891U, 0xaff75814U, 0xa038981bU,
+	0x19eef7a2U, 0x3a063c81U, 0x2c97bb97U, 0x58fea6e3U,
+	0x21d8f99aU, 0x1debf6a6U, 0x3489bd8fU, 0x3e033d85U,
+	0x7599ecceU, 0x5a7e24e1U, 0x994ed722U, 0x7f136cc4U,
+	0xcb8a4170U, 0x6a4228d1U, 0xb02c9c0bU, 0xabf25910U,
+	0xe922cb52U, 0x8c1f9337U, 0x1e2b35a5U, 0x15e1f4aeU,
+	0xbfe35c04U, 0xcaca0071U, 0x8fdf5034U, 0x9fcb5424U,
+	0x7b166dc0U, 0xf8768e43U, 0x037073b8U, 0x74d9adcfU,
+	0x6d87ead6U, 0x077572bcU, 0x8010903bU, 0xa43d991fU,
+	0xc84a8273U, 0xbbe65d00U, 0x9a8e1421U, 0x7996efc2U,
+	0x4a6a20f1U, 0xe46d895fU, 0x466523fdU, 0x4f2f60f4U,
+	0x3dc3fe86U, 0x9e8b1525U, 0xa8329a13U, 0xeee70955U,
+	0xb569dc0eU, 0x2e173995U, 0x68c2aad3U, 0xdd1bc666U,
+	0xb8269e03U, 0xe6ed0b5dU, 0x2098b89bU, 0xa57dd81eU,
+	0x023032b9U, 0x725c2ec9U, 0xb966df02U, 0x7cd3afc7U,
+	0x6e7c129cU, 0x77225585U, 0x92bf2d60U, 0xcef43a3cU,
+	0x060e08f4U, 0xb2972540U, 0xc13bfa33U, 0xeb997219U,
+	0x6dbcd19fU, 0xef9c731dU, 0x65b6d397U, 0x3e3806ccU,
+	0x957aef67U, 0xdae53f28U, 0x856eeb77U, 0x21e3c2d3U,
+	0x9975ec6bU, 0xa809a15aU, 0x5f005fadU, 0xec5cb01eU,
+	0x9d70ed6fU, 0xcfb47b3dU, 0x25e6c3d7U, 0x3a3d07c8U,
+	0x9ff06f6dU, 0x24a682d6U, 0x79add48bU, 0xad4ce15fU,
+	0x109f8fe2U, 0x2f6c43ddU, 0x2decc1dfU, 0x530f5ca1U,
+	0x471e59b5U, 0x3cb884ceU, 0x74e29686U, 0xe6d63014U,
+	0x73275481U, 0x8aa12b78U, 0x570a5da5U, 0xdc60bc2eU,
+	0xf90df40bU, 0x64f69296U, 0x19d5ccebU, 0x38bd85caU,
+	0x82ab2970U, 0x0a010bf8U, 0x87ee6975U, 0xbc18a44eU,
+	0xa6862054U, 0xfb8d7609U, 0x459edbb7U, 0x93ff6c61U,
+	0x121f0de0U, 0x15dacfe7U, 0x96ba2c64U, 0x63335091U,
+	0x377245c5U, 0x4a511bb8U, 0x67365195U, 0xdee03e2cU,
+	0xe053b312U, 0x0b414af9U, 0xc931f83bU, 0xafcc635dU,
+	0x28a981daU, 0x29e9c0dbU, 0xabc96259U, 0xc6fe3834U,
+	0xfacd3708U, 0xf84db50aU, 0x69b9d09bU, 0xdfa07f2dU,
+	0x3b7d46c9U, 0x50cf9fa2U, 0xc53efb37U, 0x518fdea3U,
+	0xae8c225cU, 0x236340d1U, 0x088189faU, 0x97fa6d65U,
+	0x943aae66U, 0x9835ad6aU, 0x337744c1U, 0xff88770dU,
+	0xf047b702U, 0xa406a256U, 0x5e401eacU, 0x1b554ee9U,
+	0xd6ea3c24U, 0x9ab52f68U, 0x09c1c8fbU, 0x18958deaU,
+	0xf442b606U, 0x6cfc909eU, 0x431b58b1U, 0xe113f213U,
+	0x4d94d9bfU, 0xa546e357U, 0x0dc4c9ffU, 0x4cd498beU,
+	0xb81da54aU, 0x54ca9ea6U, 0x72671580U, 0xbfd8674dU,
+	0xa143e253U, 0xbd58e54fU, 0xc47eba36U, 0xd925fc2bU,
+	0x31f7c6c3U, 0xb157e643U, 0xd46abe26U, 0x2b6942d9U,
+	0x66761094U, 0x35f2c7c7U, 0x2cac80deU, 0x68f9919aU,
+	0x4b115ab9U, 0x7a6d1788U, 0xa949e05bU, 0x7ce8948eU,
+	0x048e8af6U, 0xed1cf11fU, 0x3df8c5cfU, 0xaa892358U,
+	0xb7d26545U, 0x262600d4U, 0x161a0ce4U, 0x4f145bbdU,
+	0x9eb02e6cU, 0xcbb17a39U, 0x1f504fedU, 0x7e68168cU,
+	0x34b286c6U, 0x2a2903d8U, 0xd52aff27U, 0xe3937011U,
+	0xbbdd6649U, 0xead93318U, 0xfec8360cU, 0x0e040afcU,
+	0xd2ef3d20U, 0xb017a742U, 0x61b3d293U, 0xb3d76441U,
+	0xb6922444U, 0x8fe46b7dU, 0x40db9bb2U, 0xa7c66155U,
+	0x5b055ea9U, 0xe516f317U, 0x5d80ddafU, 0x60f39392U,
+	0x83eb6871U, 0x7b2d5689U, 0xfd08f50fU, 0xf502f707U,
+	0xe2d33110U, 0x074e49f5U, 0xf3877401U, 0xba9d2748U,
+	0x419bdab3U, 0xeedc321cU, 0xbe98264cU, 0x8d64e97fU,
+	0x3f7847cdU, 0xac0ca05eU, 0x1e100eecU, 0xb412a646U,
+	0x363204c4U, 0x917fee63U, 0xb95de44bU, 0x9bf56e69U,
+	0x01cbcaf3U, 0x78ed958aU, 0x0c8488feU, 0x8821a97aU,
+	0x62731190U, 0x5a451fa8U, 0xe919f01bU, 0x20a383d2U,
+	0x4e541abcU, 0xc2fb3930U, 0x0f444bfdU, 0x8961e87bU,
+	0x39fdc4cbU, 0xcaf13b38U, 0x9c30ac6eU, 0xfc48b40eU,
+	0x802bab72U, 0x842eaa76U, 0x58c59daaU, 0x70e79782U,
+	0x034b48f1U, 0x5985dcabU, 0x8be16a79U, 0x7f28578dU,
+	0xdba57e29U, 0x425b19b0U, 0xc7be7935U, 0xc871b93aU,
+	0x71a7d683U, 0x524f1da0U, 0x44de9ab6U, 0x30b787c2U,
+	0x4991d8bbU, 0x75a2d787U, 0x5cc09caeU, 0x564a1ca4U,
+	0x1dd0cdefU, 0x323705c0U, 0xf107f603U, 0x175a4de5U,
+	0xa3c36051U, 0x020b09f0U, 0xd865bd2aU, 0xc3bb7831U,
+	0x816bea73U, 0xe456b216U, 0x76621484U, 0x7da8d58fU,
+	0xd7aa7d25U, 0xa2832150U, 0xe7967115U, 0xf7827505U,
+	0x135f4ce1U, 0x903faf62U, 0x6b395299U, 0x1c908ceeU,
+	0x05cecbf7U, 0x6f3c539dU, 0xe859b11aU, 0xcc74b83eU,
+	0xa003a352U, 0xd3af7c21U, 0xf2c73500U, 0x11dfcee3U,
+	0x222301d0U, 0x8c24a87eU, 0x2e2c02dcU, 0x276641d5U,
+	0x558adfa7U, 0xf6c23404U, 0xc07bbb32U, 0x86ae2874U,
+	0xdd20fd2fU, 0x465e18b4U, 0x008b8bf2U, 0xb552e747U,
+	0xd06fbf22U, 0x8ea42a7cU, 0x48d199baU, 0xcd34f93fU,
+	0x6a791398U, 0x1a150fe8U, 0xd12ffe23U, 0x149a8ee6U,
+	0x7687f17fU, 0x6fd9b666U, 0x8a44ce83U, 0xd60fd9dfU,
+	0x1ef5eb17U, 0xaa6cc6a3U, 0xd9c019d0U, 0xf36291faU,
+	0x7547327cU, 0xf76790feU, 0x7d4d3074U, 0x26c3e52fU,
+	0x8d810c84U, 0xc21edccbU, 0x9d950894U, 0x39182130U,
+	0x818e0f88U, 0xb0f242b9U, 0x47fbbc4eU, 0xf4a753fdU,
+	0x858b0e8cU, 0xd74f98deU, 0x3d1d2034U, 0x22c6e42bU,
+	0x870b8c8eU, 0x3c5d6135U, 0x61563768U, 0xb5b702bcU,
+	0x08646c01U, 0x3797a03eU, 0x3517223cU, 0x4bf4bf42U,
+	0x5fe5ba56U, 0x2443672dU, 0x6c197565U, 0xfe2dd3f7U,
+	0x6bdcb762U, 0x925ac89bU, 0x4ff1be46U, 0xc49b5fcdU,
+	0xe1f617e8U, 0x7c0d7175U, 0x012e2f08U, 0x20466629U,
+	0x9a50ca93U, 0x12fae81bU, 0x9f158a96U, 0xa4e347adU,
+	0xbe7dc3b7U, 0xe37695eaU, 0x5d653854U, 0x8b048f82U,
+	0x0ae4ee03U, 0x0d212c04U, 0x8e41cf87U, 0x7bc8b372U,
+	0x2f89a626U, 0x52aaf85bU, 0x7fcdb276U, 0xc61bddcfU,
+	0xf8a850f1U, 0x13baa91aU, 0xd1ca1bd8U, 0xb73780beU,
+	0x30526239U, 0x31122338U, 0xb33281baU, 0xde05dbd7U,
+	0xe236d4ebU, 0xe0b656e9U, 0x71423378U, 0xc75b9cceU,
+	0x2386a52aU, 0x48347c41U, 0xddc518d4U, 0x49743d40U,
+	0xb677c1bfU, 0x3b98a332U, 0x107a6a19U, 0x8f018e86U,
+	0x8cc14d85U, 0x80ce4e89U, 0x2b8ca722U, 0xe77394eeU,
+	0xe8bc54e1U, 0xbcfd41b5U, 0x46bbfd4fU, 0x03aead0aU,
+	0xce11dfc7U, 0x824ecc8bU, 0x113a2b18U, 0x006e6e09U,
+	0xecb955e5U, 0x7407737dU, 0x5be0bb52U, 0xf9e811f0U,
+	0x556f3a5cU, 0xbdbd00b4U, 0x153f2a1cU, 0x542f7b5dU,
+	0xa0e646a9U, 0x4c317d45U, 0x6a9cf663U, 0xa72384aeU,
+	0xb9b801b0U, 0xa5a306acU, 0xdc8559d5U, 0xc1de1fc8U,
+	0x290c2520U, 0xa9ac05a0U, 0xcc915dc5U, 0x3392a13aU,
+	0x7e8df377U, 0x2d092424U, 0x3457633dU, 0x70027279U,
+	0x53eab95aU, 0x6296f46bU, 0xb1b203b8U, 0x6413776dU,
+	0x1c756915U, 0xf5e712fcU, 0x2503262cU, 0xb272c0bbU,
+	0xaf2986a6U, 0x3edde337U, 0x0ee1ef07U, 0x57efb85eU,
+	0x864bcd8fU, 0xd34a99daU, 0x07abac0eU, 0x6693f56fU,
+	0x2c496525U, 0x32d2e03bU, 0xcdd11cc4U, 0xfb6893f2U,
+	0xa32685aaU, 0xf222d0fbU, 0xe633d5efU, 0x16ffe91fU,
+	0xca14dec3U, 0xa8ec44a1U, 0x79483170U, 0xab2c87a2U,
+	0xae69c7a7U, 0x971f889eU, 0x58207851U, 0xbf3d82b6U,
+	0x43febd4aU, 0xfded10f4U, 0x457b3e4cU, 0x78087071U,
+	0x9b108b92U, 0x63d6b56aU, 0xe5f316ecU, 0xedf914e4U,
+	0xfa28d2f3U, 0x1fb5aa16U, 0xeb7c97e2U, 0xa266c4abU,
+	0x59603950U, 0xf627d1ffU, 0xa663c5afU, 0x959f0a9cU,
+	0x2783a42eU, 0xb4f743bdU, 0x06ebed0fU, 0xace945a5U,
+	0x2ec9e727U, 0x89840d80U, 0xa1a607a8U, 0x830e8d8aU,
+	0x19302910U, 0x60167669U, 0x147f6b1dU, 0x90da4a99U,
+	0x7a88f273U, 0x42befc4bU, 0xf1e213f8U, 0x38586031U,
+	0x56aff95fU, 0xda00dad3U, 0x17bfa81eU, 0x919a0b98U,
+	0x21062728U, 0xd20ad8dbU, 0x84cb4f8dU, 0xe4b357edU,
+	0x98d04891U, 0x9cd54995U, 0x403e7e49U, 0x681c7461U,
+	0x1bb0ab12U, 0x417e3f48U, 0x931a899aU, 0x67d3b46eU,
+	0xc35e9dcaU, 0x5aa0fa53U, 0xdf459ad6U, 0xd08a5ad9U,
+	0x695c3560U, 0x4ab4fe43U, 0x5c257955U, 0x284c6421U,
+	0x516a3b58U, 0x6d593464U, 0x443b7f4dU, 0x4eb1ff47U,
+	0x052b2e0cU, 0x2acce623U, 0xe9fc15e0U, 0x0fa1ae06U,
+	0xbb3883b2U, 0x1af0ea13U, 0xc09e5ec9U, 0xdb409bd2U,
+	0x99900990U, 0xfcad51f5U, 0x6e99f767U, 0x6553366cU,
+	0xcf519ec6U, 0xba78c2b3U, 0xff6d92f6U, 0xef7996e6U,
+	0x0ba4af02U, 0x88c44c81U, 0x73c2b17aU, 0x046b6f0dU,
+	0x1d352814U, 0x77c7b07eU, 0xf0a252f9U, 0xd48f5bddU,
+	0xb8f840b1U, 0xcb549fc2U, 0xea3cd6e3U, 0x09242d00U,
+	0x3ad8e233U, 0x94df4b9dU, 0x36d7e13fU, 0x3f9da236U,
+	0x4d713c44U, 0xee39d7e7U, 0xd88058d1U, 0x9e55cb97U,
+	0xc5db1eccU, 0x5ea5fb57U, 0x18706811U, 0xada904a4U,
+	0xc8945cc1U, 0x965fc99fU, 0x502a7a59U, 0xd5cf1adcU,
+	0x7282f07bU, 0x02eeec0bU, 0xc9d41dc0U, 0x0c616d05U,
+	0x8a48c24cU, 0x93168555U, 0x768bfdb0U, 0x2ac0eaecU,
+	0xe23ad824U, 0x56a3f590U, 0x250f2ae3U, 0x0fada2c9U,
+	0x8988014fU, 0x0ba8a3cdU, 0x81820347U, 0xda0cd61cU,
+	0x714e3fb7U, 0x3ed1eff8U, 0x615a3ba7U, 0xc5d71203U,
+	0x7d413cbbU, 0x4c3d718aU, 0xbb348f7dU, 0x086860ceU,
+	0x79443dbfU, 0x2b80abedU, 0xc1d21307U, 0xde09d718U,
+	0x7bc4bfbdU, 0xc0925206U, 0x9d99045bU, 0x4978318fU,
+	0xf4ab5f32U, 0xcb58930dU, 0xc9d8110fU, 0xb73b8c71U,
+	0xa32a8965U, 0xd88c541eU, 0x90d64656U, 0x02e2e0c4U,
+	0x97138451U, 0x6e95fba8U, 0xb33e8d75U, 0x38546cfeU,
+	0x1d3924dbU, 0x80c24246U, 0xfde11c3bU, 0xdc89551aU,
+	0x669ff9a0U, 0xee35db28U, 0x63dab9a5U, 0x582c749eU,
+	0x42b2f084U, 0x1fb9a6d9U, 0xa1aa0b67U, 0x77cbbcb1U,
+	0xf62bdd30U, 0xf1ee1f37U, 0x728efcb4U, 0x87078041U,
+	0xd3469515U, 0xae65cb68U, 0x83028145U, 0x3ad4eefcU,
+	0x046763c2U, 0xef759a29U, 0x2d0528ebU, 0x4bf8b38dU,
+	0xcc9d510aU, 0xcddd100bU, 0x4ffdb289U, 0x22cae8e4U,
+	0x1ef9e7d8U, 0x1c7965daU, 0x8d8d004bU, 0x3b94affdU,
+	0xdf499619U, 0xb4fb4f72U, 0x210a2be7U, 0xb5bb0e73U,
+	0x4ab8f28cU, 0xc7579001U, 0xecb5592aU, 0x73cebdb5U,
+	0x700e7eb6U, 0x7c017dbaU, 0xd7439411U, 0x1bbca7ddU,
+	0x147367d2U, 0x40327286U, 0xba74ce7cU, 0xff619e39U,
+	0x32deecf4U, 0x7e81ffb8U, 0xedf5182bU, 0xfca15d3aU,
+	0x107666d6U, 0x88c8404eU, 0xa72f8861U, 0x052722c3U,
+	0xa9a0096fU, 0x41723387U, 0xe9f0192fU, 0xa8e0486eU,
+	0x5c29759aU, 0xb0fe4e76U, 0x9653c550U, 0x5becb79dU,
+	0x45773283U, 0x596c359fU, 0x204a6ae6U, 0x3d112cfbU,
+	0xd5c31613U, 0x55633693U, 0x305e6ef6U, 0xcf5d9209U,
+	0x8242c044U, 0xd1c61717U, 0xc898500eU, 0x8ccd414aU,
+	0xaf258a69U, 0x9e59c758U, 0x4d7d308bU, 0x98dc445eU,
+	0xe0ba5a26U, 0x092821cfU, 0xd9cc151fU, 0x4ebdf388U,
+	0x53e6b595U, 0xc212d004U, 0xf22edc34U, 0xab208b6dU,
+	0x7a84febcU, 0x2f85aae9U, 0xfb649f3dU, 0x9a5cc65cU,
+	0xd0865616U, 0xce1dd308U, 0x311e2ff7U, 0x07a7a0c1U,
+	0x5fe9b699U, 0x0eede3c8U, 0x1afce6dcU, 0xea30da2cU,
+	0x36dbedf0U, 0x54237792U, 0x85870243U, 0x57e3b491U,
+	0x52a6f494U, 0x6bd0bbadU, 0xa4ef4b62U, 0x43f2b185U,
+	0xbf318e79U, 0x012223c7U, 0xb9b40d7fU, 0x84c74342U,
+	0x67dfb8a1U, 0x9f198659U, 0x193c25dfU, 0x113627d7U,
+	0x06e7e1c0U, 0xe37a9925U, 0x17b3a4d1U, 0x5ea9f798U,
+	0xa5af0a63U, 0x0ae8e2ccU, 0x5aacf69cU, 0x695039afU,
+	0xdb4c971dU, 0x4838708eU, 0xfa24de3cU, 0x50267696U,
+	0xd206d414U, 0x754b3eb3U, 0x5d69349bU, 0x7fc1beb9U,
+	0xe5ff1a23U, 0x9cd9455aU, 0xe8b0582eU, 0x6c1579aaU,
+	0x8647c140U, 0xbe71cf78U, 0x0d2d20cbU, 0xc4975302U,
+	0xaa60ca6cU, 0x26cfe9e0U, 0xeb709b2dU, 0x6d5538abU,
+	0xddc9141bU, 0x2ec5ebe8U, 0x78047cbeU, 0x187c64deU,
+	0x641f7ba2U, 0x601a7aa6U, 0xbcf14d7aU, 0x94d34752U,
+	0xe77f9821U, 0xbdb10c7bU, 0x6fd5baa9U, 0x9b1c875dU,
+	0x3f91aef9U, 0xa66fc960U, 0x238aa9e5U, 0x2c4569eaU,
+	0x95930653U, 0xb67bcd70U, 0xa0ea4a66U, 0xd4835712U,
+	0xada5086bU, 0x91960757U, 0xb8f44c7eU, 0xb27ecc74U,
+	0xf9e41d3fU, 0xd603d510U, 0x153326d3U, 0xf36e9d35U,
+	0x47f7b081U, 0xe63fd920U, 0x3c516dfaU, 0x278fa8e1U,
+	0x655f3aa3U, 0x006262c6U, 0x9256c454U, 0x999c055fU,
+	0x339eadf5U, 0x46b7f180U, 0x03a2a1c5U, 0x13b6a5d5U,
+	0xf76b9c31U, 0x740b7fb2U, 0x8f0d8249U, 0xf8a45c3eU,
+	0xe1fa1b27U, 0x8b08834dU, 0x0c6d61caU, 0x284068eeU,
+	0x44377382U, 0x379bacf1U, 0x16f3e5d0U, 0xf5eb1e33U,
+	0xc617d100U, 0x681078aeU, 0xca18d20cU, 0xc3529105U,
+	0xb1be0f77U, 0x12f6e4d4U, 0x244f6be2U, 0x629af8a4U,
+	0x39142dffU, 0xa26ac864U, 0xe4bf5b22U, 0x51663797U,
+	0x345b6ff2U, 0x6a90faacU, 0xace5496aU, 0x290029efU,
+	0x8e4dc348U, 0xfe21df38U, 0x351b2ef3U, 0xf0ae5e36U,
+	0x8de16ce2U, 0x94bf2bfbU, 0x7122531eU, 0x2d694442U,
+	0xe593768aU, 0x510a5b3eU, 0x22a6844dU, 0x08040c67U,
+	0x8e21afe1U, 0x0c010d63U, 0x862bade9U, 0xdda578b2U,
+	0x76e79119U, 0x39784156U, 0x66f39509U, 0xc27ebcadU,
+	0x7ae89215U, 0x4b94df24U, 0xbc9d21d3U, 0x0fc1ce60U,
+	0x7eed9311U, 0x2c290543U, 0xc67bbda9U, 0xd9a079b6U,
+	0x7c6d1113U, 0xc73bfca8U, 0x9a30aaf5U, 0x4ed19f21U,
+	0xf302f19cU, 0xccf13da3U, 0xce71bfa1U, 0xb09222dfU,
+	0xa48327cbU, 0xdf25fab0U, 0x977fe8f8U, 0x054b4e6aU,
+	0x90ba2affU, 0x693c5506U, 0xb49723dbU, 0x3ffdc250U,
+	0x1a908a75U, 0x876bece8U, 0xfa48b295U, 0xdb20fbb4U,
+	0x6136570eU, 0xe99c7586U, 0x6473170bU, 0x5f85da30U,
+	0x451b5e2aU, 0x18100877U, 0xa603a5c9U, 0x7062121fU,
+	0xf182739eU, 0xf647b199U, 0x7527521aU, 0x80ae2eefU,
+	0xd4ef3bbbU, 0xa9cc65c6U, 0x84ab2febU, 0x3d7d4052U,
+	0x03cecd6cU, 0xe8dc3487U, 0x2aac8645U, 0x4c511d23U,
+	0xcb34ffa4U, 0xca74bea5U, 0x48541c27U, 0x2563464aU,
+	0x19504976U, 0x1bd0cb74U, 0x8a24aee5U, 0x3c3d0153U,
+	0xd8e038b7U, 0xb352e1dcU, 0x26a38549U, 0xb212a0ddU,
+	0x4d115c22U, 0xc0fe3eafU, 0xeb1cf784U, 0x7467131bU,
+	0x77a7d018U, 0x7ba8d314U, 0xd0ea3abfU, 0x1c150973U,
+	0x13dac97cU, 0x479bdc28U, 0xbddd60d2U, 0xf8c83097U,
+	0x3577425aU, 0x79285116U, 0xea5cb685U, 0xfb08f394U,
+	0x17dfc878U, 0x8f61eee0U, 0xa08626cfU, 0x028e8c6dU,
+	0xae09a7c1U, 0x46db9d29U, 0xee59b781U, 0xaf49e6c0U,
+	0x5b80db34U, 0xb757e0d8U, 0x91fa6bfeU, 0x5c451933U,
+	0x42de9c2dU, 0x5ec59b31U, 0x27e3c448U, 0x3ab88255U,
+	0xd26ab8bdU, 0x52ca983dU, 0x37f7c058U, 0xc8f43ca7U,
+	0x85eb6eeaU, 0xd66fb9b9U, 0xcf31fea0U, 0x8b64efe4U,
+	0xa88c24c7U, 0x99f069f6U, 0x4ad49e25U, 0x9f75eaf0U,
+	0xe713f488U, 0x0e818f61U, 0xde65bbb1U, 0x49145d26U,
+	0x544f1b3bU, 0xc5bb7eaaU, 0xf587729aU, 0xac8925c3U,
+	0x7d2d5012U, 0x282c0447U, 0xfccd3193U, 0x9df568f2U,
+	0xd72ff8b8U, 0xc9b47da6U, 0x36b78159U, 0x000e0e6fU,
+	0x58401837U, 0x09444d66U, 0x1d554872U, 0xed997482U,
+	0x3172435eU, 0x538ad93cU, 0x822eacedU, 0x504a1a3fU,
+	0x550f5a3aU, 0x6c791503U, 0xa346e5ccU, 0x445b1f2bU,
+	0xb89820d7U, 0x068b8d69U, 0xbe1da3d1U, 0x836eedecU,
+	0x6076160fU, 0x98b028f7U, 0x1e958b71U, 0x169f8979U,
+	0x014e4f6eU, 0xe4d3378bU, 0x101a0a7fU, 0x59005936U,
+	0xa206a4cdU, 0x0d414c62U, 0x5d055832U, 0x6ef99701U,
+	0xdce539b3U, 0x4f91de20U, 0xfd8d7092U, 0x578fd838U,
+	0xd5af7abaU, 0x72e2901dU, 0x5ac09a35U, 0x78681017U,
+	0xe256b48dU, 0x9b70ebf4U, 0xef19f680U, 0x6bbcd704U,
+	0x81ee6feeU, 0xb9d861d6U, 0x0a848e65U, 0xc33efdacU,
+	0xadc964c2U, 0x2166474eU, 0xecd93583U, 0x6afc9605U,
+	0xda60bab5U, 0x296c4546U, 0x7fadd210U, 0x1fd5ca70U,
+	0x63b6d50cU, 0x67b3d408U, 0xbb58e3d4U, 0x937ae9fcU,
+	0xe0d6368fU, 0xba18a2d5U, 0x687c1407U, 0x9cb529f3U,
+	0x38380057U, 0xa1c667ceU, 0x2423074bU, 0x2becc744U,
+	0x923aa8fdU, 0xb1d263deU, 0xa743e4c8U, 0xd32af9bcU,
+	0xaa0ca6c5U, 0x963fa9f9U, 0xbf5de2d0U, 0xb5d762daU,
+	0xfe4db391U, 0xd1aa7bbeU, 0x129a887dU, 0xf4c7339bU,
+	0x405e1e2fU, 0xe196778eU, 0x3bf8c354U, 0x2026064fU,
+	0x62f6940dU, 0x07cbcc68U, 0x95ff6afaU, 0x9e35abf1U,
+	0x3437035bU, 0x411e5f2eU, 0x040b0f6bU, 0x141f0b7bU,
+	0xf0c2329fU, 0x73a2d11cU, 0x88a42ce7U, 0xff0df290U,
+	0xe653b589U, 0x8ca12de3U, 0x0bc4cf64U, 0x2fe9c640U,
+	0x439edd2cU, 0x3032025fU, 0x115a4b7eU, 0xf242b09dU,
+	0xc1be7faeU, 0x6fb9d600U, 0xcdb17ca2U, 0xc4fb3fabU,
+	0xb617a1d9U, 0x155f4a7aU, 0x23e6c54cU, 0x6533560aU,
+	0x3ebd8351U, 0xa5c366caU, 0xe316f58cU, 0x56cf9939U,
+	0x33f2c15cU, 0x6d395402U, 0xab4ce7c4U, 0x2ea98741U,
+	0x89e46de6U, 0xf9887196U, 0x32b2805dU, 0xf707f098U,
+	0x854bce40U, 0x9c158959U, 0x7988f1bcU, 0x25c3e6e0U,
+	0xed39d428U, 0x59a0f99cU, 0x2a0c26efU, 0x00aeaec5U,
+	0x868b0d43U, 0x04abafc1U, 0x8e810f4bU, 0xd50fda10U,
+	0x7e4d33bbU, 0x31d2e3f4U, 0x6e5937abU, 0xcad41e0fU,
+	0x724230b7U, 0x433e7d86U, 0xb4378371U, 0x076b6cc2U,
+	0x764731b3U, 0x2483a7e1U, 0xced11f0bU, 0xd10adb14U,
+	0x74c7b3b1U, 0xcf915e0aU, 0x929a0857U, 0x467b3d83U,
+	0xfba8533eU, 0xc45b9f01U, 0xc6db1d03U, 0xb838807dU,
+	0xac298569U, 0xd78f5812U, 0x9fd54a5aU, 0x0de1ecc8U,
+	0x9810885dU, 0x6196f7a4U, 0xbc3d8179U, 0x375760f2U,
+	0x123a28d7U, 0x8fc14e4aU, 0xf2e21037U, 0xd38a5916U,
+	0x699cf5acU, 0xe136d724U, 0x6cd9b5a9U, 0x572f7892U,
+	0x4db1fc88U, 0x10baaad5U, 0xaea9076bU, 0x78c8b0bdU,
+	0xf928d13cU, 0xfeed133bU, 0x7d8df0b8U, 0x88048c4dU,
+	0xdc459919U, 0xa166c764U, 0x8c018d49U, 0x35d7e2f0U,
+	0x0b646fceU, 0xe0769625U, 0x220624e7U, 0x44fbbf81U,
+	0xc39e5d06U, 0xc2de1c07U, 0x40febe85U, 0x2dc9e4e8U,
+	0x11faebd4U, 0x137a69d6U, 0x828e0c47U, 0x3497a3f1U,
+	0xd04a9a15U, 0xbbf8437eU, 0x2e0927ebU, 0xbab8027fU,
+	0x45bbfe80U, 0xc8549c0dU, 0xe3b65526U, 0x7ccdb1b9U,
+	0x7f0d72baU, 0x730271b6U, 0xd840981dU, 0x14bfabd1U,
+	0x1b706bdeU, 0x4f317e8aU, 0xb577c270U, 0xf0629235U,
+	0x3ddde0f8U, 0x7182f3b4U, 0xe2f61427U, 0xf3a25136U,
+	0x1f756adaU, 0x87cb4c42U, 0xa82c846dU, 0x0a242ecfU,
+	0xa6a30563U, 0x4e713f8bU, 0xe6f31523U, 0xa7e34462U,
+	0x532a7996U, 0xbffd427aU, 0x9950c95cU, 0x54efbb91U,
+	0x4a743e8fU, 0x566f3993U, 0x2f4966eaU, 0x321220f7U,
+	0xdac01a1fU, 0x5a603a9fU, 0x3f5d62faU, 0xc05e9e05U,
+	0x8d41cc48U, 0xdec51b1bU, 0xc79b5c02U, 0x83ce4d46U,
+	0xa0268665U, 0x915acb54U, 0x427e3c87U, 0x97df4852U,
+	0xefb9562aU, 0x062b2dc3U, 0xd6cf1913U, 0x41beff84U,
+	0x5ce5b999U, 0xcd11dc08U, 0xfd2dd038U, 0xa4238761U,
+	0x7587f2b0U, 0x2086a6e5U, 0xf4679331U, 0x955fca50U,
+	0xdf855a1aU, 0xc11edf04U, 0x3e1d23fbU, 0x08a4accdU,
+	0x50eaba95U, 0x01eeefc4U, 0x15ffead0U, 0xe533d620U,
+	0x39d8e1fcU, 0x5b207b9eU, 0x8a840e4fU, 0x58e0b89dU,
+	0x5da5f898U, 0x64d3b7a1U, 0xabec476eU, 0x4cf1bd89U,
+	0xb0328275U, 0x0e212fcbU, 0xb6b70173U, 0x8bc44f4eU,
+	0x68dcb4adU, 0x901a8a55U, 0x163f29d3U, 0x1e352bdbU,
+	0x09e4edccU, 0xec799529U, 0x18b0a8ddU, 0x51aafb94U,
+	0xaaac066fU, 0x05ebeec0U, 0x55affa90U, 0x665335a3U,
+	0xd44f9b11U, 0x473b7c82U, 0xf527d230U, 0x5f257a9aU,
+	0xdd05d818U, 0x7a4832bfU, 0x526a3897U, 0x70c2b2b5U,
+	0xeafc162fU, 0x93da4956U, 0xe7b35422U, 0x631675a6U,
+	0x8944cd4cU, 0xb172c374U, 0x022e2cc7U, 0xcb945f0eU,
+	0xa563c660U, 0x29cce5ecU, 0xe4739721U, 0x625634a7U,
+	0xd2ca1817U, 0x21c6e7e4U, 0x770770b2U, 0x177f68d2U,
+	0x6b1c77aeU, 0x6f1976aaU, 0xb3f24176U, 0x9bd04b5eU,
+	0xe87c942dU, 0xb2b20077U, 0x60d6b6a5U, 0x941f8b51U,
+	0x3092a2f5U, 0xa96cc56cU, 0x2c89a5e9U, 0x234665e6U,
+	0x9a900a5fU, 0xb978c17cU, 0xafe9466aU, 0xdb805b1eU,
+	0xa2a60467U, 0x9e950b5bU, 0xb7f74072U, 0xbd7dc078U,
+	0xf6e71133U, 0xd900d91cU, 0x1a302adfU, 0xfc6d9139U,
+	0x48f4bc8dU, 0xe93cd52cU, 0x335261f6U, 0x288ca4edU,
+	0x6a5c36afU, 0x0f616ecaU, 0x9d55c858U, 0x969f0953U,
+	0x3c9da1f9U, 0x49b4fd8cU, 0x0ca1adc9U, 0x1cb5a9d9U,
+	0xf868903dU, 0x7b0873beU, 0x800e8e45U, 0xf7a75032U,
+	0xeef9172bU, 0x840b8f41U, 0x036e6dc6U, 0x274364e2U,
+	0x4b347f8eU, 0x3898a0fdU, 0x19f0e9dcU, 0xfae8123fU,
+	0xc914dd0cU, 0x671374a2U, 0xc51bde00U, 0xcc519d09U,
+	0xbebd037bU, 0x1df5e8d8U, 0x2b4c67eeU, 0x6d99f4a8U,
+	0x361721f3U, 0xad69c468U, 0xebbc572eU, 0x5e653b9bU,
+	0x3b5863feU, 0x6593f6a0U, 0xa3e64566U, 0x260325e3U,
+	0x814ecf44U, 0xf122d334U, 0x3a1822ffU, 0xffad523aU,
+	0xcf08c749U, 0xd6568050U, 0x33cbf8b5U, 0x6f80efe9U,
+	0xa77add21U, 0x13e3f095U, 0x604f2fe6U, 0x4aeda7ccU,
+	0xccc8044aU, 0x4ee8a6c8U, 0xc4c20642U, 0x9f4cd319U,
+	0x340e3ab2U, 0x7b91eafdU, 0x241a3ea2U, 0x80971706U,
+	0x380139beU, 0x097d748fU, 0xfe748a78U, 0x4d2865cbU,
+	0x3c0438baU, 0x6ec0aee8U, 0x84921602U, 0x9b49d21dU,
+	0x3e84bab8U, 0x85d25703U, 0xd8d9015eU, 0x0c38348aU,
+	0xb1eb5a37U, 0x8e189608U, 0x8c98140aU, 0xf27b8974U,
+	0xe66a8c60U, 0x9dcc511bU, 0xd5964353U, 0x47a2e5c1U,
+	0xd2538154U, 0x2bd5feadU, 0xf67e8870U, 0x7d1469fbU,
+	0x587921deU, 0xc5824743U, 0xb8a1193eU, 0x99c9501fU,
+	0x23dffca5U, 0xab75de2dU, 0x269abca0U, 0x1d6c719bU,
+	0x07f2f581U, 0x5af9a3dcU, 0xe4ea0e62U, 0x328bb9b4U,
+	0xb36bd835U, 0xb4ae1a32U, 0x37cef9b1U, 0xc2478544U,
+	0x96069010U, 0xeb25ce6dU, 0xc6428440U, 0x7f94ebf9U,
+	0x412766c7U, 0xaa359f2cU, 0x68452deeU, 0x0eb8b688U,
+	0x89dd540fU, 0x889d150eU, 0x0abdb78cU, 0x678aede1U,
+	0x5bb9e2ddU, 0x593960dfU, 0xc8cd054eU, 0x7ed4aaf8U,
+	0x9a09931cU, 0xf1bb4a77U, 0x644a2ee2U, 0xf0fb0b76U,
+	0x0ff8f789U, 0x82179504U, 0xa9f55c2fU, 0x368eb8b0U,
+	0x354e7bb3U, 0x394178bfU, 0x92039114U, 0x5efca2d8U,
+	0x513362d7U, 0x05727783U, 0xff34cb79U, 0xba219b3cU,
+	0x779ee9f1U, 0x3bc1fabdU, 0xa8b51d2eU, 0xb9e1583fU,
+	0x553663d3U, 0xcd88454bU, 0xe26f8d64U, 0x406727c6U,
+	0xece00c6aU, 0x04323682U, 0xacb01c2aU, 0xeda04d6bU,
+	0x1969709fU, 0xf5be4b73U, 0xd313c055U, 0x1eacb298U,
+	0x00373786U, 0x1c2c309aU, 0x650a6fe3U, 0x785129feU,
+	0x90831316U, 0x10233396U, 0x751e6bf3U, 0x8a1d970cU,
+	0xc702c541U, 0x94861212U, 0x8dd8550bU, 0xc98d444fU,
+	0xea658f6cU, 0xdb19c25dU, 0x083d358eU, 0xdd9c415bU,
+	0xa5fa5f23U, 0x4c6824caU, 0x9c8c101aU, 0x0bfdf68dU,
+	0x16a6b090U, 0x8752d501U, 0xb76ed931U, 0xee608e68U,
+	0x3fc4fbb9U, 0x6ac5afecU, 0xbe249a38U, 0xdf1cc359U,
+	0x95c65313U, 0x8b5dd60dU, 0x745e2af2U, 0x42e7a5c4U,
+	0x1aa9b39cU, 0x4bade6cdU, 0x5fbce3d9U, 0xaf70df29U,
+	0x739be8f5U, 0x11637297U, 0xc0c70746U, 0x12a3b194U,
+	0x17e6f191U, 0x2e90bea8U, 0xe1af4e67U, 0x06b2b480U,
+	0xfa718b7cU, 0x446226c2U, 0xfcf4087aU, 0xc1874647U,
+	0x229fbda4U, 0xda59835cU, 0x5c7c20daU, 0x547622d2U,
+	0x43a7e4c5U, 0xa63a9c20U, 0x52f3a1d4U, 0x1be9f29dU,
+	0xe0ef0f66U, 0x4fa8e7c9U, 0x1fecf399U, 0x2c103caaU,
+	0x9e0c9218U, 0x0d78758bU, 0xbf64db39U, 0x15667393U,
+	0x9746d111U, 0x300b3bb6U, 0x1829319eU, 0x3a81bbbcU,
+	0xa0bf1f26U, 0xd999405fU, 0xadf05d2bU, 0x29557cafU,
+	0xc307c445U, 0xfb31ca7dU, 0x486d25ceU, 0x81d75607U,
+	0xef20cf69U, 0x638fece5U, 0xae309e28U, 0x28153daeU,
+	0x9889111eU, 0x6b85eeedU, 0x3d4479bbU, 0x5d3c61dbU,
+	0x215f7ea7U, 0x255a7fa3U, 0xf9b1487fU, 0xd1934257U,
+	0xa23f9d24U, 0xf8f1097eU, 0x2a95bfacU, 0xde5c8258U,
+	0x7ad1abfcU, 0xe32fcc65U, 0x66caace0U, 0x69056cefU,
+	0xd0d30356U, 0xf33bc875U, 0xe5aa4f63U, 0x91c35217U,
+	0xe8e50d6eU, 0xd4d60252U, 0xfdb4497bU, 0xf73ec971U,
+	0xbca4183aU, 0x9343d015U, 0x507323d6U, 0xb62e9830U,
+	0x02b7b584U, 0xa37fdc25U, 0x791168ffU, 0x62cfade4U,
+	0x201f3fa6U, 0x452267c3U, 0xd716c151U, 0xdcdc005aU,
+	0x76dea8f0U, 0x03f7f485U, 0x46e2a4c0U, 0x56f6a0d0U,
+	0xb22b9934U, 0x314b7ab7U, 0xca4d874cU, 0xbde4593bU,
+	0xa4ba1e22U, 0xce488648U, 0x492d64cfU, 0x6d006debU,
+	0x01777687U, 0x72dba9f4U, 0x53b3e0d5U, 0xb0ab1b36U,
+	0x8357d405U, 0x2d507dabU, 0x8f58d709U, 0x86129400U,
+	0xf4fe0a72U, 0x57b6e1d1U, 0x610f6ee7U, 0x27dafda1U,
+	0x7c5428faU, 0xe72acd61U, 0xa1ff5e27U, 0x14263292U,
+	0x711b6af7U, 0x2fd0ffa9U, 0xe9a54c6fU, 0x6c402ceaU,
+	0xcb0dc64dU, 0xbb61da3dU, 0x705b2bf6U, 0xb5ee5b33U,
+	0x2396b53bU, 0x3ac8f222U, 0xdf558ac7U, 0x831e9d9bU,
+	0x4be4af53U, 0xff7d82e7U, 0x8cd15d94U, 0xa673d5beU,
+	0x20567638U, 0xa276d4baU, 0x285c7430U, 0x73d2a16bU,
+	0xd89048c0U, 0x970f988fU, 0xc8844cd0U, 0x6c096574U,
+	0xd49f4bccU, 0xe5e306fdU, 0x12eaf80aU, 0xa1b617b9U,
+	0xd09a4ac8U, 0x825edc9aU, 0x680c6470U, 0x77d7a06fU,
+	0xd21ac8caU, 0x694c2571U, 0x3447732cU, 0xe0a646f8U,
+	0x5d752845U, 0x6286e47aU, 0x60066678U, 0x1ee5fb06U,
+	0x0af4fe12U, 0x71522369U, 0x39083121U, 0xab3c97b3U,
+	0x3ecdf326U, 0xc74b8cdfU, 0x1ae0fa02U, 0x918a1b89U,
+	0xb4e753acU, 0x291c3531U, 0x543f6b4cU, 0x7557226dU,
+	0xcf418ed7U, 0x47ebac5fU, 0xca04ced2U, 0xf1f203e9U,
+	0xeb6c87f3U, 0xb667d1aeU, 0x08747c10U, 0xde15cbc6U,
+	0x5ff5aa47U, 0x58306840U, 0xdb508bc3U, 0x2ed9f736U,
+	0x7a98e262U, 0x07bbbc1fU, 0x2adcf632U, 0x930a998bU,
+	0xadb914b5U, 0x46abed5eU, 0x84db5f9cU, 0xe226c4faU,
+	0x6543267dU, 0x6403677cU, 0xe623c5feU, 0x8b149f93U,
+	0xb72790afU, 0xb5a712adU, 0x2453773cU, 0x924ad88aU,
+	0x7697e16eU, 0x1d253805U, 0x88d45c90U, 0x1c657904U,
+	0xe36685fbU, 0x6e89e776U, 0x456b2e5dU, 0xda10cac2U,
+	0xd9d009c1U, 0xd5df0acdU, 0x7e9de366U, 0xb262d0aaU,
+	0xbdad10a5U, 0xe9ec05f1U, 0x13aab90bU, 0x56bfe94eU,
+	0x9b009b83U, 0xd75f88cfU, 0x442b6f5cU, 0x557f2a4dU,
+	0xb9a811a1U, 0x21163739U, 0x0ef1ff16U, 0xacf955b4U,
+	0x007e7e18U, 0xe8ac44f0U, 0x402e6e58U, 0x013e3f19U,
+	0xf5f702edU, 0x19203901U, 0x3f8db227U, 0xf232c0eaU,
+	0xeca945f4U, 0xf0b242e8U, 0x89941d91U, 0x94cf5b8cU,
+	0x7c1d6164U, 0xfcbd41e4U, 0x99801981U, 0x6683e57eU,
+	0x2b9cb733U, 0x78186060U, 0x61462779U, 0x2513363dU,
+	0x06fbfd1eU, 0x3787b02fU, 0xe4a347fcU, 0x31023329U,
+	0x49642d51U, 0xa0f656b8U, 0x70126268U, 0xe76384ffU,
+	0xfa38c2e2U, 0x6bcca773U, 0x5bf0ab43U, 0x02fefc1aU,
+	0xd35a89cbU, 0x865bdd9eU, 0x52bae84aU, 0x3382b12bU,
+	0x79582161U, 0x67c3a47fU, 0x98c05880U, 0xae79d7b6U,
+	0xf637c1eeU, 0xa73394bfU, 0xb32291abU, 0x43eead5bU,
+	0x9f059a87U, 0xfdfd00e5U, 0x2c597534U, 0xfe3dc3e6U,
+	0xfb7883e3U, 0xc20eccdaU, 0x0d313c15U, 0xea2cc6f2U,
+	0x16eff90eU, 0xa8fc54b0U, 0x106a7a08U, 0x2d193435U,
+	0xce01cfd6U, 0x36c7f12eU, 0xb0e252a8U, 0xb8e850a0U,
+	0xaf3996b7U, 0x4aa4ee52U, 0xbe6dd3a6U, 0xf77780efU,
+	0x0c717d14U, 0xa33695bbU, 0xf37281ebU, 0xc08e4ed8U,
+	0x7292e06aU, 0xe1e607f9U, 0x53faa94bU, 0xf9f801e1U,
+	0x7bd8a363U, 0xdc9549c4U, 0xf4b743ecU, 0xd61fc9ceU,
+	0x4c216d54U, 0x3507322dU, 0x416e2f59U, 0xc5cb0eddU,
+	0x2f99b637U, 0x17afb80fU, 0xa4f357bcU, 0x6d492475U,
+	0x03bebd1bU, 0x8f119e97U, 0x42aeec5aU, 0xc48b4fdcU,
+	0x7417636cU, 0x871b9c9fU, 0xd1da0bc9U, 0xb1a213a9U,
+	0xcdc10cd5U, 0xc9c40dd1U, 0x152f3a0dU, 0x3d0d3025U,
+	0x4ea1ef56U, 0x146f7b0cU, 0xc60bcddeU, 0x32c2f02aU,
+	0x964fd98eU, 0x0fb1be17U, 0x8a54de92U, 0x859b1e9dU,
+	0x3c4d7124U, 0x1fa5ba07U, 0x09343d11U, 0x7d5d2065U,
+	0x047b7f1cU, 0x38487020U, 0x112a3b09U, 0x1ba0bb03U,
+	0x503a6a48U, 0x7fdda267U, 0xbced51a4U, 0x5ab0ea42U,
+	0xee29c7f6U, 0x4fe1ae57U, 0x958f1a8dU, 0x8e51df96U,
+	0xcc814dd4U, 0xa9bc15b1U, 0x3b88b323U, 0x30427228U,
+	0x9a40da82U, 0xef6986f7U, 0xaa7cd6b2U, 0xba68d2a2U,
+	0x5eb5eb46U, 0xddd508c5U, 0x26d3f53eU, 0x517a2b49U,
+	0x48246c50U, 0x22d6f43aU, 0xa5b316bdU, 0x819e1f99U,
+	0xede904f5U, 0x9e45db86U, 0xbf2d92a7U, 0x5c356944U,
+	0x6fc9a677U, 0xc1ce0fd9U, 0x63c6a57bU, 0x6a8ce672U,
+	0x18607800U, 0xbb2893a3U, 0x8d911c95U, 0xcb448fd3U,
+	0x90ca5a88U, 0x0bb4bf13U, 0x4d612c55U, 0xf8b840e0U,
+	0x9d851885U, 0xc34e8ddbU, 0x053b3e1dU, 0x80de5e98U,
+	0x2793b43fU, 0x57ffa84fU, 0x9cc55984U, 0x59702941U,
+	0x6b7d1698U, 0x72235181U, 0x97be2964U, 0xcbf53e38U,
+	0x030f0cf0U, 0xb7962144U, 0xc43afe37U, 0xee98761dU,
+	0x68bdd59bU, 0xea9d7719U, 0x60b7d793U, 0x3b3902c8U,
+	0x907beb63U, 0xdfe43b2cU, 0x806fef73U, 0x24e2c6d7U,
+	0x9c74e86fU, 0xad08a55eU, 0x5a015ba9U, 0xe95db41aU,
+	0x9871e96bU, 0xcab57f39U, 0x20e7c7d3U, 0x3f3c03ccU,
+	0x9af16b69U, 0x21a786d2U, 0x7cacd08fU, 0xa84de55bU,
+	0x159e8be6U, 0x2a6d47d9U, 0x28edc5dbU, 0x560e58a5U,
+	0x421f5db1U, 0x39b980caU, 0x71e39282U, 0xe3d73410U,
+	0x76265085U, 0x8fa02f7cU, 0x520b59a1U, 0xd961b82aU,
+	0xfc0cf00fU, 0x61f79692U, 0x1cd4c8efU, 0x3dbc81ceU,
+	0x87aa2d74U, 0x0f000ffcU, 0x82ef6d71U, 0xb919a04aU,
+	0xa3872450U, 0xfe8c720dU, 0x409fdfb3U, 0x96fe6865U,
+	0x171e09e4U, 0x10dbcbe3U, 0x93bb2860U, 0x66325495U,
+	0x327341c1U, 0x4f501fbcU, 0x62375591U, 0xdbe13a28U,
+	0xe552b716U, 0x0e404efdU, 0xcc30fc3fU, 0xaacd6759U,
+	0x2da885deU, 0x2ce8c4dfU, 0xaec8665dU, 0xc3ff3c30U,
+	0xffcc330cU, 0xfd4cb10eU, 0x6cb8d49fU, 0xdaa17b29U,
+	0x3e7c42cdU, 0x55ce9ba6U, 0xc03fff33U, 0x548edaa7U,
+	0xab8d2658U, 0x266244d5U, 0x0d808dfeU, 0x92fb6961U,
+	0x913baa62U, 0x9d34a96eU, 0x367640c5U, 0xfa897309U,
+	0xf546b306U, 0xa107a652U, 0x5b411aa8U, 0x1e544aedU,
+	0xd3eb3820U, 0x9fb42b6cU, 0x0cc0ccffU, 0x1d9489eeU,
+	0xf143b202U, 0x69fd949aU, 0x461a5cb5U, 0xe412f617U,
+	0x4895ddbbU, 0xa047e753U, 0x08c5cdfbU, 0x49d59cbaU,
+	0xbd1ca14eU, 0x51cb9aa2U, 0x77661184U, 0xbad96349U,
+	0xa442e657U, 0xb859e14bU, 0xc17fbe32U, 0xdc24f82fU,
+	0x34f6c2c7U, 0xb456e247U, 0xd16bba22U, 0x2e6846ddU,
+	0x63771490U, 0x30f3c3c3U, 0x29ad84daU, 0x6df8959eU,
+	0x4e105ebdU, 0x7f6c138cU, 0xac48e45fU, 0x79e9908aU,
+	0x018f8ef2U, 0xe81df51bU, 0x38f9c1cbU, 0xaf88275cU,
+	0xb2d36141U, 0x232704d0U, 0x131b08e0U, 0x4a155fb9U,
+	0x9bb12a68U, 0xceb07e3dU, 0x1a514be9U, 0x7b691288U,
+	0x31b382c2U, 0x2f2807dcU, 0xd02bfb23U, 0xe6927415U,
+	0xbedc624dU, 0xefd8371cU, 0xfbc93208U, 0x0b050ef8U,
+	0xd7ee3924U, 0xb516a346U, 0x64b2d697U, 0xb6d66045U,
+	0xb3932040U, 0x8ae56f79U, 0x45da9fb6U, 0xa2c76551U,
+	0x5e045aadU, 0xe017f713U, 0x5881d9abU, 0x65f29796U,
+	0x86ea6c75U, 0x7e2c528dU, 0xf809f10bU, 0xf003f303U,
+	0xe7d23514U, 0x024f4df1U, 0xf6867005U, 0xbf9c234cU,
+	0x449adeb7U, 0xebdd3618U, 0xbb992248U, 0x8865ed7bU,
+	0x3a7943c9U, 0xa90da45aU, 0x1b110ae8U, 0xb113a242U,
+	0x333300c0U, 0x947eea67U, 0xbc5ce04fU, 0x9ef46a6dU,
+	0x04cacef7U, 0x7dec918eU, 0x09858cfaU, 0x8d20ad7eU,
+	0x67721594U, 0x5f441bacU, 0xec18f41fU, 0x25a287d6U,
+	0x4b551eb8U, 0xc7fa3d34U, 0x0a454ff9U, 0x8c60ec7fU,
+	0x3cfcc0cfU, 0xcff03f3cU, 0x9931a86aU, 0xf949b00aU,
+	0x852aaf76U, 0x812fae72U, 0x5dc499aeU, 0x75e69386U,
+	0x064a4cf5U, 0x5c84d8afU, 0x8ee06e7dU, 0x7a295389U,
+	0xdea47a2dU, 0x475a1db4U, 0xc2bf7d31U, 0xcd70bd3eU,
+	0x74a6d287U, 0x574e19a4U, 0x41df9eb2U, 0x35b683c6U,
+	0x4c90dcbfU, 0x70a3d383U, 0x59c198aaU, 0x534b18a0U,
+	0x18d1c9ebU, 0x373601c4U, 0xf406f207U, 0x125b49e1U,
+	0xa6c26455U, 0x070a0df4U, 0xdd64b92eU, 0xc6ba7c35U,
+	0x846aee77U, 0xe157b612U, 0x73631080U, 0x78a9d18bU,
+	0xd2ab7921U, 0xa7822554U, 0xe2977511U, 0xf2837101U,
+	0x165e48e5U, 0x953eab66U, 0x6e38569dU, 0x199188eaU,
+	0x00cfcff3U, 0x6a3d5799U, 0xed58b51eU, 0xc975bc3aU,
+	0xa502a756U, 0xd6ae7825U, 0xf7c63104U, 0x14decae7U,
+	0x272205d4U, 0x8925ac7aU, 0x2b2d06d8U, 0x226745d1U,
+	0x508bdba3U, 0xf3c33000U, 0xc57abf36U, 0x83af2c70U,
+	0xd821f92bU, 0x435f1cb0U, 0x058a8ff6U, 0xb053e343U,
+	0xd56ebb26U, 0x8ba52e78U, 0x4dd09dbeU, 0xc835fd3bU,
+	0x6f78179cU, 0x1f140becU, 0xd42efa27U, 0x119b8ae2U,
+	0xd2f220aeU, 0xcbac67b7U, 0x2e311f52U, 0x727a080eU,
+	0xba803ac6U, 0x0e191772U, 0x7db5c801U, 0x5717402bU,
+	0xd132e3adU, 0x5312412fU, 0xd938e1a5U, 0x82b634feU,
+	0x29f4dd55U, 0x666b0d1aU, 0x39e0d945U, 0x9d6df0e1U,
+	0x25fbde59U, 0x14879368U, 0xe38e6d9fU, 0x50d2822cU,
+	0x21fedf5dU, 0x733a490fU, 0x9968f1e5U, 0x86b335faU,
+	0x237e5d5fU, 0x9828b0e4U, 0xc523e6b9U, 0x11c2d36dU,
+	0xac11bdd0U, 0x93e271efU, 0x9162f3edU, 0xef816e93U,
+	0xfb906b87U, 0x8036b6fcU, 0xc86ca4b4U, 0x5a580226U,
+	0xcfa966b3U, 0x362f194aU, 0xeb846f97U, 0x60ee8e1cU,
+	0x4583c639U, 0xd878a0a4U, 0xa55bfed9U, 0x8433b7f8U,
+	0x3e251b42U, 0xb68f39caU, 0x3b605b47U, 0x0096967cU,
+	0x1a081266U, 0x4703443bU, 0xf910e985U, 0x2f715e53U,
+	0xae913fd2U, 0xa954fdd5U, 0x2a341e56U, 0xdfbd62a3U,
+	0x8bfc77f7U, 0xf6df298aU, 0xdbb863a7U, 0x626e0c1eU,
+	0x5cdd8120U, 0xb7cf78cbU, 0x75bfca09U, 0x1342516fU,
+	0x9427b3e8U, 0x9567f2e9U, 0x1747506bU, 0x7a700a06U,
+	0x4643053aU, 0x44c38738U, 0xd537e2a9U, 0x632e4d1fU,
+	0x87f374fbU, 0xec41ad90U, 0x79b0c905U, 0xed01ec91U,
+	0x1202106eU, 0x9fed72e3U, 0xb40fbbc8U, 0x2b745f57U,
+	0x28b49c54U, 0x24bb9f58U, 0x8ff976f3U, 0x4306453fU,
+	0x4cc98530U, 0x18889064U, 0xe2ce2c9eU, 0xa7db7cdbU,
+	0x6a640e16U, 0x263b1d5aU, 0xb54ffac9U, 0xa41bbfd8U,
+	0x48cc8434U, 0xd072a2acU, 0xff956a83U, 0x5d9dc021U,
+	0xf11aeb8dU, 0x19c8d165U, 0xb14afbcdU, 0xf05aaa8cU,
+	0x04939778U, 0xe844ac94U, 0xcee927b2U, 0x0356557fU,
+	0x1dcdd061U, 0x01d6d77dU, 0x78f08804U, 0x65abce19U,
+	0x8d79f4f1U, 0x0dd9d471U, 0x68e48c14U, 0x97e770ebU,
+	0xdaf822a6U, 0x897cf5f5U, 0x9022b2ecU, 0xd477a3a8U,
+	0xf79f688bU, 0xc6e325baU, 0x15c7d269U, 0xc066a6bcU,
+	0xb800b8c4U, 0x5192c32dU, 0x8176f7fdU, 0x1607116aU,
+	0x0b5c5777U, 0x9aa832e6U, 0xaa943ed6U, 0xf39a698fU,
+	0x223e1c5eU, 0x773f480bU, 0xa3de7ddfU, 0xc2e624beU,
+	0x883cb4f4U, 0x96a731eaU, 0x69a4cd15U, 0x5f1d4223U,
+	0x0753547bU, 0x5657012aU, 0x4246043eU, 0xb28a38ceU,
+	0x6e610f12U, 0x0c999570U, 0xdd3de0a1U, 0x0f595673U,
+	0x0a1c1676U, 0x336a594fU, 0xfc55a980U, 0x1b485367U,
+	0xe78b6c9bU, 0x5998c125U, 0xe10eef9dU, 0xdc7da1a0U,
+	0x3f655a43U, 0xc7a364bbU, 0x4186c73dU, 0x498cc535U,
+	0x5e5d0322U, 0xbbc07bc7U, 0x4f094633U, 0x0613157aU,
+	0xfd15e881U, 0x5252002eU, 0x0216147eU, 0x31eadb4dU,
+	0x83f675ffU, 0x1082926cU, 0xa29e3cdeU, 0x089c9474U,
+	0x8abc36f6U, 0x2df1dc51U, 0x05d3d679U, 0x277b5c5bU,
+	0xbd45f8c1U, 0xc463a7b8U, 0xb00abaccU, 0x34af9b48U,
+	0xdefd23a2U, 0xe6cb2d9aU, 0x5597c229U, 0x9c2db1e0U,
+	0xf2da288eU, 0x7e750b02U, 0xb3ca79cfU, 0x35efda49U,
+	0x8573f6f9U, 0x767f090aU, 0x20be9e5cU, 0x40c6863cU,
+	0x3ca59940U, 0x38a09844U, 0xe44baf98U, 0xcc69a5b0U,
+	0xbfc57ac3U, 0xe50bee99U, 0x376f584bU, 0xc3a665bfU,
+	0x672b4c1bU, 0xfed52b82U, 0x7b304b07U, 0x74ff8b08U,
+	0xcd29e4b1U, 0xeec12f92U, 0xf850a884U, 0x8c39b5f0U,
+	0xf51fea89U, 0xc92ce5b5U, 0xe04eae9cU, 0xeac42e96U,
+	0xa15effddU, 0x8eb937f2U, 0x4d89c431U, 0xabd47fd7U,
+	0x1f4d5263U, 0xbe853bc2U, 0x64eb8f18U, 0x7f354a03U,
+	0x3de5d841U, 0x58d88024U, 0xcaec26b6U, 0xc126e7bdU,
+	0x6b244f17U, 0x1e0d1362U, 0x5b184327U, 0x4b0c4737U,
+	0xafd17ed3U, 0x2cb19d50U, 0xd7b760abU, 0xa01ebedcU,
+	0xb940f9c5U, 0xd3b261afU, 0x54d78328U, 0x70fa8a0cU,
+	0x1c8d9160U, 0x6f214e13U, 0x4e490732U, 0xad51fcd1U,
+	0x9ead33e2U, 0x30aa9a4cU, 0x92a230eeU, 0x9be873e7U,
+	0xe904ed95U, 0x4a4c0636U, 0x7cf58900U, 0x3a201a46U,
+	0x61aecf1dU, 0xfad02a86U, 0xbc05b9c0U, 0x09dcd575U,
+	0x6ce18d10U, 0x322a184eU, 0xf45fab88U, 0x71bacb0dU,
+	0xd6f721aaU, 0xa69b3ddaU, 0x6da1cc11U, 0xa814bcd4U,
+	0x076166e8U, 0x1e3f21f1U, 0xfba25914U, 0xa7e94e48U,
+	0x6f137c80U, 0xdb8a5134U, 0xa8268e47U, 0x8284066dU,
+	0x04a1a5ebU, 0x86810769U, 0x0caba7e3U, 0x572572b8U,
+	0xfc679b13U, 0xb3f84b5cU, 0xec739f03U, 0x48feb6a7U,
+	0xf068981fU, 0xc114d52eU, 0x361d2bd9U, 0x8541c46aU,
+	0xf46d991bU, 0xa6a90f49U, 0x4cfbb7a3U, 0x532073bcU,
+	0xf6ed1b19U, 0x4dbbf6a2U, 0x10b0a0ffU, 0xc451952bU,
+	0x7982fb96U, 0x467137a9U, 0x44f1b5abU, 0x3a1228d5U,
+	0x2e032dc1U, 0x55a5f0baU, 0x1dffe2f2U, 0x8fcb4460U,
+	0x1a3a20f5U, 0xe3bc5f0cU, 0x3e1729d1U, 0xb57dc85aU,
+	0x9010807fU, 0x0debe6e2U, 0x70c8b89fU, 0x51a0f1beU,
+	0xebb65d04U, 0x631c7f8cU, 0xeef31d01U, 0xd505d03aU,
+	0xcf9b5420U, 0x9290027dU, 0x2c83afc3U, 0xfae21815U,
+	0x7b027994U, 0x7cc7bb93U, 0xffa75810U, 0x0a2e24e5U,
+	0x5e6f31b1U, 0x234c6fccU, 0x0e2b25e1U, 0xb7fd4a58U,
+	0x894ec766U, 0x625c3e8dU, 0xa02c8c4fU, 0xc6d11729U,
+	0x41b4f5aeU, 0x40f4b4afU, 0xc2d4162dU, 0xafe34c40U,
+	0x93d0437cU, 0x9150c17eU, 0x00a4a4efU, 0xb6bd0b59U,
+	0x526032bdU, 0x39d2ebd6U, 0xac238f43U, 0x3892aad7U,
+	0xc7915628U, 0x4a7e34a5U, 0x619cfd8eU, 0xfee71911U,
+	0xfd27da12U, 0xf128d91eU, 0x5a6a30b5U, 0x96950379U,
+	0x995ac376U, 0xcd1bd622U, 0x375d6ad8U, 0x72483a9dU,
+	0xbff74850U, 0xf3a85b1cU, 0x60dcbc8fU, 0x7188f99eU,
+	0x9d5fc272U, 0x05e1e4eaU, 0x2a062cc5U, 0x880e8667U,
+	0x2489adcbU, 0xcc5b9723U, 0x64d9bd8bU, 0x25c9eccaU,
+	0xd100d13eU, 0x3dd7ead2U, 0x1b7a61f4U, 0xd6c51339U,
+	0xc85e9627U, 0xd445913bU, 0xad63ce42U, 0xb038885fU,
+	0x58eab2b7U, 0xd84a9237U, 0xbd77ca52U, 0x427436adU,
+	0x0f6b64e0U, 0x5cefb3b3U, 0x45b1f4aaU, 0x01e4e5eeU,
+	0x220c2ecdU, 0x137063fcU, 0xc054942fU, 0x15f5e0faU,
+	0x6d93fe82U, 0x8401856bU, 0x54e5b1bbU, 0xc394572cU,
+	0xdecf1131U, 0x4f3b74a0U, 0x7f077890U, 0x26092fc9U,
+	0xf7ad5a18U, 0xa2ac0e4dU, 0x764d3b99U, 0x177562f8U,
+	0x5daff2b2U, 0x433477acU, 0xbc378b53U, 0x8a8e0465U,
+	0xd2c0123dU, 0x83c4476cU, 0x97d54278U, 0x67197e88U,
+	0xbbf24954U, 0xd90ad336U, 0x08aea6e7U, 0xdaca1035U,
+	0xdf8f5030U, 0xe6f91f09U, 0x29c6efc6U, 0xcedb1521U,
+	0x32182addU, 0x8c0b8763U, 0x349da9dbU, 0x09eee7e6U,
+	0xeaf61c05U, 0x123022fdU, 0x9415817bU, 0x9c1f8373U,
+	0x8bce4564U, 0x6e533d81U, 0x9a9a0075U, 0xd380533cU,
+	0x2886aec7U, 0x87c14668U, 0xd7855238U, 0xe4799d0bU,
+	0x566533b9U, 0xc511d42aU, 0x770d7a98U, 0xdd0fd232U,
+	0x5f2f70b0U, 0xf8629a17U, 0xd040903fU, 0xf2e81a1dU,
+	0x68d6be87U, 0x11f0e1feU, 0x6599fc8aU, 0xe13cdd0eU,
+	0x0b6e65e4U, 0x33586bdcU, 0x8004846fU, 0x49bef7a6U,
+	0x27496ec8U, 0xabe64d44U, 0x66593f89U, 0xe07c9c0fU,
+	0x50e0b0bfU, 0xa3ec4f4cU, 0xf52dd81aU, 0x9555c07aU,
+	0xe936df06U, 0xed33de02U, 0x31d8e9deU, 0x19fae3f6U,
+	0x6a563c85U, 0x3098a8dfU, 0xe2fc1e0dU, 0x163523f9U,
+	0xb2b80a5dU, 0x2b466dc4U, 0xaea30d41U, 0xa16ccd4eU,
+	0x18baa2f7U, 0x3b5269d4U, 0x2dc3eec2U, 0x59aaf3b6U,
+	0x208caccfU, 0x1cbfa3f3U, 0x35dde8daU, 0x3f5768d0U,
+	0x74cdb99bU, 0x5b2a71b4U, 0x981a8277U, 0x7e473991U,
+	0xcade1425U, 0x6b167d84U, 0xb178c95eU, 0xaaa60c45U,
+	0xe8769e07U, 0x8d4bc662U, 0x1f7f60f0U, 0x14b5a1fbU,
+	0xbeb70951U, 0xcb9e5524U, 0x8e8b0561U, 0x9e9f0171U,
+	0x7a423895U, 0xf922db16U, 0x022426edU, 0x758df89aU,
+	0x6cd3bf83U, 0x062127e9U, 0x8144c56eU, 0xa569cc4aU,
+	0xc91ed726U, 0xbab20855U, 0x9bda4174U, 0x78c2ba97U,
+	0x4b3e75a4U, 0xe539dc0aU, 0x473176a8U, 0x4e7b35a1U,
+	0x3c97abd3U, 0x9fdf4070U, 0xa966cf46U, 0xefb35c00U,
+	0xb43d895bU, 0x2f436cc0U, 0x6996ff86U, 0xdc4f9333U,
+	0xb972cb56U, 0xe7b95e08U, 0x21ccedceU, 0xa4298d4bU,
+	0x036467ecU, 0x73087b9cU, 0xb8328a57U, 0x7d87fa92U,
+	0x89b43db3U, 0x90ea7aaaU, 0x7577024fU, 0x293c1513U,
+	0xe1c627dbU, 0x555f0a6fU, 0x26f3d51cU, 0x0c515d36U,
+	0x8a74feb0U, 0x08545c32U, 0x827efcb8U, 0xd9f029e3U,
+	0x72b2c048U, 0x3d2d1007U, 0x62a6c458U, 0xc62bedfcU,
+	0x7ebdc344U, 0x4fc18e75U, 0xb8c87082U, 0x0b949f31U,
+	0x7ab8c240U, 0x287c5412U, 0xc22eecf8U, 0xddf528e7U,
+	0x78384042U, 0xc36eadf9U, 0x9e65fba4U, 0x4a84ce70U,
+	0xf757a0cdU, 0xc8a46cf2U, 0xca24eef0U, 0xb4c7738eU,
+	0xa0d6769aU, 0xdb70abe1U, 0x932ab9a9U, 0x011e1f3bU,
+	0x94ef7baeU, 0x6d690457U, 0xb0c2728aU, 0x3ba89301U,
+	0x1ec5db24U, 0x833ebdb9U, 0xfe1de3c4U, 0xdf75aae5U,
+	0x6563065fU, 0xedc924d7U, 0x6026465aU, 0x5bd08b61U,
+	0x414e0f7bU, 0x1c455926U, 0xa256f498U, 0x7437434eU,
+	0xf5d722cfU, 0xf212e0c8U, 0x7172034bU, 0x84fb7fbeU,
+	0xd0ba6aeaU, 0xad993497U, 0x80fe7ebaU, 0x39281103U,
+	0x079b9c3dU, 0xec8965d6U, 0x2ef9d714U, 0x48044c72U,
+	0xcf61aef5U, 0xce21eff4U, 0x4c014d76U, 0x2136171bU,
+	0x1d051827U, 0x1f859a25U, 0x8e71ffb4U, 0x38685002U,
+	0xdcb569e6U, 0xb707b08dU, 0x22f6d418U, 0xb647f18cU,
+	0x49440d73U, 0xc4ab6ffeU, 0xef49a6d5U, 0x7032424aU,
+	0x73f28149U, 0x7ffd8245U, 0xd4bf6beeU, 0x18405822U,
+	0x178f982dU, 0x43ce8d79U, 0xb9883183U, 0xfc9d61c6U,
+	0x3122130bU, 0x7d7d0047U, 0xee09e7d4U, 0xff5da2c5U,
+	0x138a9929U, 0x8b34bfb1U, 0xa4d3779eU, 0x06dbdd3cU,
+	0xaa5cf690U, 0x428ecc78U, 0xea0ce6d0U, 0xab1cb791U,
+	0x5fd58a65U, 0xb302b189U, 0x95af3aafU, 0x58104862U,
+	0x468bcd7cU, 0x5a90ca60U, 0x23b69519U, 0x3eedd304U,
+	0xd63fe9ecU, 0x569fc96cU, 0x33a29109U, 0xcca16df6U,
+	0x81be3fbbU, 0xd23ae8e8U, 0xcb64aff1U, 0x8f31beb5U,
+	0xacd97596U, 0x9da538a7U, 0x4e81cf74U, 0x9b20bba1U,
+	0xe346a5d9U, 0x0ad4de30U, 0xda30eae0U, 0x4d410c77U,
+	0x501a4a6aU, 0xc1ee2ffbU, 0xf1d223cbU, 0xa8dc7492U,
+	0x79780143U, 0x2c795516U, 0xf89860c2U, 0x99a039a3U,
+	0xd37aa9e9U, 0xcde12cf7U, 0x32e2d008U, 0x045b5f3eU,
+	0x5c154966U, 0x0d111c37U, 0x19001923U, 0xe9cc25d3U,
+	0x3527120fU, 0x57df886dU, 0x867bfdbcU, 0x541f4b6eU,
+	0x515a0b6bU, 0x682c4452U, 0xa713b49dU, 0x400e4e7aU,
+	0xbccd7186U, 0x02dedc38U, 0xba48f280U, 0x873bbcbdU,
+	0x6423475eU, 0x9ce579a6U, 0x1ac0da20U, 0x12cad828U,
+	0x051b1e3fU, 0xe08666daU, 0x144f5b2eU, 0x5d550867U,
+	0xa653f59cU, 0x09141d33U, 0x59500963U, 0x6aacc650U,
+	0xd8b068e2U, 0x4bc48f71U, 0xf9d821c3U, 0x53da8969U,
+	0xd1fa2bebU, 0x76b7c14cU, 0x5e95cb64U, 0x7c3d4146U,
+	0xe603e5dcU, 0x9f25baa5U, 0xeb4ca7d1U, 0x6fe98655U,
+	0x85bb3ebfU, 0xbd8d3087U, 0x0ed1df34U, 0xc76bacfdU,
+	0xa99c3593U, 0x2533161fU, 0xe88c64d2U, 0x6ea9c754U,
+	0xde35ebe4U, 0x2d391417U, 0x7bf88341U, 0x1b809b21U,
+	0x67e3845dU, 0x63e68559U, 0xbf0db285U, 0x972fb8adU,
+	0xe48367deU, 0xbe4df384U, 0x6c294556U, 0x98e078a2U,
+	0x3c6d5106U, 0xa593369fU, 0x2076561aU, 0x2fb99615U,
+	0x966ff9acU, 0xb587328fU, 0xa316b599U, 0xd77fa8edU,
+	0xae59f794U, 0x926af8a8U, 0xbb08b381U, 0xb182338bU,
+	0xfa18e2c0U, 0xd5ff2aefU, 0x16cfd92cU, 0xf09262caU,
+	0x440b4f7eU, 0xe5c326dfU, 0x3fad9205U, 0x2473571eU,
+	0x66a3c55cU, 0x039e9d39U, 0x91aa3babU, 0x9a60faa0U,
+	0x3062520aU, 0x454b0e7fU, 0x005e5e3aU, 0x104a5a2aU,
+	0xf49763ceU, 0x77f7804dU, 0x8cf17db6U, 0xfb58a3c1U,
+	0xe206e4d8U, 0x88f47cb2U, 0x0f919e35U, 0x2bbc9711U,
+	0x47cb8c7dU, 0x3467530eU, 0x150f1a2fU, 0xf617e1ccU,
+	0xc5eb2effU, 0x6bec8751U, 0xc9e42df3U, 0xc0ae6efaU,
+	0xb242f088U, 0x110a1b2bU, 0x27b3941dU, 0x6166075bU,
+	0x3ae8d200U, 0xa196379bU, 0xe743a4ddU, 0x529ac868U,
+	0x37a7900dU, 0x696c0553U, 0xaf19b695U, 0x2afcd610U,
+	0x8db13cb7U, 0xfddd20c7U, 0x36e7d10cU, 0xf352a1c9U,
+	0xf751a628U, 0xee0fe131U, 0x0b9299d4U, 0x57d98e88U,
+	0x9f23bc40U, 0x2bba91f4U, 0x58164e87U, 0x72b4c6adU,
+	0xf491652bU, 0x76b1c7a9U, 0xfc9b6723U, 0xa715b278U,
+	0x0c575bd3U, 0x43c88b9cU, 0x1c435fc3U, 0xb8ce7667U,
+	0x005858dfU, 0x312415eeU, 0xc62deb19U, 0x757104aaU,
+	0x045d59dbU, 0x5699cf89U, 0xbccb7763U, 0xa310b37cU,
+	0x06dddbd9U, 0xbd8b3662U, 0xe080603fU, 0x346155ebU,
+	0x89b23b56U, 0xb641f769U, 0xb4c1756bU, 0xca22e815U,
+	0xde33ed01U, 0xa595307aU, 0xedcf2232U, 0x7ffb84a0U,
+	0xea0ae035U, 0x138c9fccU, 0xce27e911U, 0x454d089aU,
+	0x602040bfU, 0xfddb2622U, 0x80f8785fU, 0xa190317eU,
+	0x1b869dc4U, 0x932cbf4cU, 0x1ec3ddc1U, 0x253510faU,
+	0x3fab94e0U, 0x62a0c2bdU, 0xdcb36f03U, 0x0ad2d8d5U,
+	0x8b32b954U, 0x8cf77b53U, 0x0f9798d0U, 0xfa1ee425U,
+	0xae5ff171U, 0xd37caf0cU, 0xfe1be521U, 0x47cd8a98U,
+	0x797e07a6U, 0x926cfe4dU, 0x501c4c8fU, 0x36e1d7e9U,
+	0xb184356eU, 0xb0c4746fU, 0x32e4d6edU, 0x5fd38c80U,
+	0x63e083bcU, 0x616001beU, 0xf094642fU, 0x468dcb99U,
+	0xa250f27dU, 0xc9e22b16U, 0x5c134f83U, 0xc8a26a17U,
+	0x37a196e8U, 0xba4ef465U, 0x91ac3d4eU, 0x0ed7d9d1U,
+	0x0d171ad2U, 0x011819deU, 0xaa5af075U, 0x66a5c3b9U,
+	0x696a03b6U, 0x3d2b16e2U, 0xc76daa18U, 0x8278fa5dU,
+	0x4fc78890U, 0x03989bdcU, 0x90ec7c4fU, 0x81b8395eU,
+	0x6d6f02b2U, 0xf5d1242aU, 0xda36ec05U, 0x783e46a7U,
+	0xd4b96d0bU, 0x3c6b57e3U, 0x94e97d4bU, 0xd5f92c0aU,
+	0x213011feU, 0xcde72a12U, 0xeb4aa134U, 0x26f5d3f9U,
+	0x386e56e7U, 0x247551fbU, 0x5d530e82U, 0x4008489fU,
+	0xa8da7277U, 0x287a52f7U, 0x4d470a92U, 0xb244f66dU,
+	0xff5ba420U, 0xacdf7373U, 0xb581346aU, 0xf1d4252eU,
+	0xd23cee0dU, 0xe340a33cU, 0x306454efU, 0xe5c5203aU,
+	0x9da33e42U, 0x743145abU, 0xa4d5717bU, 0x33a497ecU,
+	0x2effd1f1U, 0xbf0bb460U, 0x8f37b850U, 0xd639ef09U,
+	0x079d9ad8U, 0x529cce8dU, 0x867dfb59U, 0xe745a238U,
+	0xad9f3272U, 0xb304b76cU, 0x4c074b93U, 0x7abec4a5U,
+	0x22f0d2fdU, 0x73f487acU, 0x67e582b8U, 0x9729be48U,
+	0x4bc28994U, 0x293a13f6U, 0xf89e6627U, 0x2afad0f5U,
+	0x2fbf90f0U, 0x16c9dfc9U, 0xd9f62f06U, 0x3eebd5e1U,
+	0xc228ea1dU, 0x7c3b47a3U, 0xc4ad691bU, 0xf9de2726U,
+	0x1ac6dcc5U, 0xe200e23dU, 0x642541bbU, 0x6c2f43b3U,
+	0x7bfe85a4U, 0x9e63fd41U, 0x6aaac0b5U, 0x23b093fcU,
+	0xd8b66e07U, 0x77f186a8U, 0x27b592f8U, 0x14495dcbU,
+	0xa655f379U, 0x352114eaU, 0x873dba58U, 0x2d3f12f2U,
+	0xaf1fb070U, 0x08525ad7U, 0x207050ffU, 0x02d8daddU,
+	0x98e67e47U, 0xe1c0213eU, 0x95a93c4aU, 0x110c1dceU,
+	0xfb5ea524U, 0xc368ab1cU, 0x703444afU, 0xb98e3766U,
+	0xd779ae08U, 0x5bd68d84U, 0x9669ff49U, 0x104c5ccfU,
+	0xa0d0707fU, 0x53dc8f8cU, 0x051d18daU, 0x656500baU,
+	0x19061fc6U, 0x1d031ec2U, 0xc1e8291eU, 0xe9ca2336U,
+	0x9a66fc45U, 0xc0a8681fU, 0x12ccdecdU, 0xe605e339U,
+	0x4288ca9dU, 0xdb76ad04U, 0x5e93cd81U, 0x515c0d8eU,
+	0xe88a6237U, 0xcb62a914U, 0xddf32e02U, 0xa99a3376U,
+	0xd0bc6c0fU, 0xec8f6333U, 0xc5ed281aU, 0xcf67a810U,
+	0x84fd795bU, 0xab1ab174U, 0x682a42b7U, 0x8e77f951U,
+	0x3aeed4e5U, 0x9b26bd44U, 0x4148099eU, 0x5a96cc85U,
+	0x18465ec7U, 0x7d7b06a2U, 0xef4fa030U, 0xe485613bU,
+	0x4e87c991U, 0x3bae95e4U, 0x7ebbc5a1U, 0x6eafc1b1U,
+	0x8a72f855U, 0x09121bd6U, 0xf214e62dU, 0x85bd385aU,
+	0x9ce37f43U, 0xf611e729U, 0x717405aeU, 0x55590c8aU,
+	0x392e17e6U, 0x4a82c895U, 0x6bea81b4U, 0x88f27a57U,
+	0xbb0eb564U, 0x15091ccaU, 0xb701b668U, 0xbe4bf561U,
+	0xcca76b13U, 0x6fef80b0U, 0x59560f86U, 0x1f839cc0U,
+	0x440d499bU, 0xdf73ac00U, 0x99a63f46U, 0x2c7f53f3U,
+	0x49420b96U, 0x17899ec8U, 0xd1fc2d0eU, 0x54194d8bU,
+	0xf354a72cU, 0x8338bb5cU, 0x48024a97U, 0x8db73a52U,
+	0x22c2e06eU, 0x3b9ca777U, 0xde01df92U, 0x824ac8ceU,
+	0x4ab0fa06U, 0xfe29d7b2U, 0x8d8508c1U, 0xa72780ebU,
+	0x2102236dU, 0xa32281efU, 0x29082165U, 0x7286f43eU,
+	0xd9c41d95U, 0x965bcddaU, 0xc9d01985U, 0x6d5d3021U,
+	0xd5cb1e99U, 0xe4b753a8U, 0x13bead5fU, 0xa0e242ecU,
+	0xd1ce1f9dU, 0x830a89cfU, 0x69583125U, 0x7683f53aU,
+	0xd34e9d9fU, 0x68187024U, 0x35132679U, 0xe1f213adU,
+	0x5c217d10U, 0x63d2b12fU, 0x6152332dU, 0x1fb1ae53U,
+	0x0ba0ab47U, 0x7006763cU, 0x385c6474U, 0xaa68c2e6U,
+	0x3f99a673U, 0xc61fd98aU, 0x1bb4af57U, 0x90de4edcU,
+	0xb5b306f9U, 0x28486064U, 0x556b3e19U, 0x74037738U,
+	0xce15db82U, 0x46bff90aU, 0xcb509b87U, 0xf0a656bcU,
+	0xea38d2a6U, 0xb73384fbU, 0x09202945U, 0xdf419e93U,
+	0x5ea1ff12U, 0x59643d15U, 0xda04de96U, 0x2f8da263U,
+	0x7bccb737U, 0x06efe94aU, 0x2b88a367U, 0x925eccdeU,
+	0xaced41e0U, 0x47ffb80bU, 0x858f0ac9U, 0xe37291afU,
+	0x64177328U, 0x65573229U, 0xe77790abU, 0x8a40cac6U,
+	0xb673c5faU, 0xb4f347f8U, 0x25072269U, 0x931e8ddfU,
+	0x77c3b43bU, 0x1c716d50U, 0x898009c5U, 0x1d312c51U,
+	0xe232d0aeU, 0x6fddb223U, 0x443f7b08U, 0xdb449f97U,
+	0xd8845c94U, 0xd48b5f98U, 0x7fc9b633U, 0xb33685ffU,
+	0xbcf945f0U, 0xe8b850a4U, 0x12feec5eU, 0x57ebbc1bU,
+	0x9a54ced6U, 0xd60bdd9aU, 0x457f3a09U, 0x542b7f18U,
+	0xb8fc44f4U, 0x2042626cU, 0x0fa5aa43U, 0xadad00e1U,
+	0x012a2b4dU, 0xe9f811a5U, 0x417a3b0dU, 0x006a6a4cU,
+	0xf4a357b8U, 0x18746c54U, 0x3ed9e772U, 0xf36695bfU,
+	0xedfd10a1U, 0xf1e617bdU, 0x88c048c4U, 0x959b0ed9U,
+	0x7d493431U, 0xfde914b1U, 0x98d44cd4U, 0x67d7b02bU,
+	0x2ac8e266U, 0x794c3535U, 0x6012722cU, 0x24476368U,
+	0x07afa84bU, 0x36d3e57aU, 0xe5f712a9U, 0x3056667cU,
+	0x48307804U, 0xa1a203edU, 0x7146373dU, 0xe637d1aaU,
+	0xfb6c97b7U, 0x6a98f226U, 0x5aa4fe16U, 0x03aaa94fU,
+	0xd20edc9eU, 0x870f88cbU, 0x53eebd1fU, 0x32d6e47eU,
+	0x780c7434U, 0x6697f12aU, 0x99940dd5U, 0xaf2d82e3U,
+	0xf76394bbU, 0xa667c1eaU, 0xb276c4feU, 0x42baf80eU,
+	0x9e51cfd2U, 0xfca955b0U, 0x2d0d2061U, 0xff6996b3U,
+	0xfa2cd6b6U, 0xc35a998fU, 0x0c656940U, 0xeb7893a7U,
+	0x17bbac5bU, 0xa9a801e5U, 0x113e2f5dU, 0x2c4d6160U,
+	0xcf559a83U, 0x3793a47bU, 0xb1b607fdU, 0xb9bc05f5U,
+	0xae6dc3e2U, 0x4bf0bb07U, 0xbf3986f3U, 0xf623d5baU,
+	0x0d252841U, 0xa262c0eeU, 0xf226d4beU, 0xc1da1b8dU,
+	0x73c6b53fU, 0xe0b252acU, 0x52aefc1eU, 0xf8ac54b4U,
+	0x7a8cf636U, 0xddc11c91U, 0xf5e316b9U, 0xd74b9c9bU,
+	0x4d753801U, 0x34536778U, 0x403a7a0cU, 0xc49f5b88U,
+	0x2ecde362U, 0x16fbed5aU, 0xa5a702e9U, 0x6c1d7120U,
+	0x02eae84eU, 0x8e45cbc2U, 0x43fab90fU, 0xc5df1a89U,
+	0x75433639U, 0x864fc9caU, 0xd08e5e9cU, 0xb0f646fcU,
+	0xcc955980U, 0xc8905884U, 0x147b6f58U, 0x3c596570U,
+	0x4ff5ba03U, 0x153b2e59U, 0xc75f988bU, 0x3396a57fU,
+	0x971b8cdbU, 0x0ee5eb42U, 0x8b008bc7U, 0x84cf4bc8U,
+	0x3d192471U, 0x1ef1ef52U, 0x08606844U, 0x7c097530U,
+	0x052f2a49U, 0x391c2575U, 0x107e6e5cU, 0x1af4ee56U,
+	0x516e3f1dU, 0x7e89f732U, 0xbdb904f1U, 0x5be4bf17U,
+	0xef7d92a3U, 0x4eb5fb02U, 0x94db4fd8U, 0x8f058ac3U,
+	0xcdd51881U, 0xa8e840e4U, 0x3adce676U, 0x3116277dU,
+	0x9b148fd7U, 0xee3dd3a2U, 0xab2883e7U, 0xbb3c87f7U,
+	0x5fe1be13U, 0xdc815d90U, 0x2787a06bU, 0x502e7e1cU,
+	0x49703905U, 0x2382a16fU, 0xa4e743e8U, 0x80ca4accU,
+	0xecbd51a0U, 0x9f118ed3U, 0xbe79c7f2U, 0x5d613c11U,
+	0x6e9df322U, 0xc09a5a8cU, 0x6292f02eU, 0x6bd8b327U,
+	0x19342d55U, 0xba7cc6f6U, 0x8cc549c0U, 0xca10da86U,
+	0x919e0fddU, 0x0ae0ea46U, 0x4c357900U, 0xf9ec15b5U,
+	0x9cd14dd0U, 0xc21ad88eU, 0x046f6b48U, 0x818a0bcdU,
+	0x26c7e16aU, 0x56abfd1aU, 0x9d910cd1U, 0x58247c14U,
+	0xfbae55dbU, 0xe2f012c2U, 0x076d6a27U, 0x5b267d7bU,
+	0x93dc4fb3U, 0x27456207U, 0x54e9bd74U, 0x7e4b355eU,
+	0xf86e96d8U, 0x7a4e345aU, 0xf06494d0U, 0xabea418bU,
+	0x00a8a820U, 0x4f37786fU, 0x10bcac30U, 0xb4318594U,
+	0x0ca7ab2cU, 0x3ddbe61dU, 0xcad218eaU, 0x798ef759U,
+	0x08a2aa28U, 0x5a663c7aU, 0xb0348490U, 0xafef408fU,
+	0x0a22282aU, 0xb174c591U, 0xec7f93ccU, 0x389ea618U,
+	0x854dc8a5U, 0xbabe049aU, 0xb83e8698U, 0xc6dd1be6U,
+	0xd2cc1ef2U, 0xa96ac389U, 0xe130d1c1U, 0x73047753U,
+	0xe6f513c6U, 0x1f736c3fU, 0xc2d81ae2U, 0x49b2fb69U,
+	0x6cdfb34cU, 0xf124d5d1U, 0x8c078bacU, 0xad6fc28dU,
+	0x17796e37U, 0x9fd34cbfU, 0x123c2e32U, 0x29cae309U,
+	0x33546713U, 0x6e5f314eU, 0xd04c9cf0U, 0x062d2b26U,
+	0x87cd4aa7U, 0x800888a0U, 0x03686b23U, 0xf6e117d6U,
+	0xa2a00282U, 0xdf835cffU, 0xf2e416d2U, 0x4b32796bU,
+	0x7581f455U, 0x9e930dbeU, 0x5ce3bf7cU, 0x3a1e241aU,
+	0xbd7bc69dU, 0xbc3b879cU, 0x3e1b251eU, 0x532c7f73U,
+	0x6f1f704fU, 0x6d9ff24dU, 0xfc6b97dcU, 0x4a72386aU,
+	0xaeaf018eU, 0xc51dd8e5U, 0x50ecbc70U, 0xc45d99e4U,
+	0x3b5e651bU, 0xb6b10796U, 0x9d53cebdU, 0x02282a22U,
+	0x01e8e921U, 0x0de7ea2dU, 0xa6a50386U, 0x6a5a304aU,
+	0x6595f045U, 0x31d4e511U, 0xcb9259ebU, 0x8e8709aeU,
+	0x43387b63U, 0x0f67682fU, 0x9c138fbcU, 0x8d47caadU,
+	0x6190f141U, 0xf92ed7d9U, 0xd6c91ff6U, 0x74c1b554U,
+	0xd8469ef8U, 0x3094a410U, 0x98168eb8U, 0xd906dff9U,
+	0x2dcfe20dU, 0xc118d9e1U, 0xe7b552c7U, 0x2a0a200aU,
+	0x3491a514U, 0x288aa208U, 0x51acfd71U, 0x4cf7bb6cU,
+	0xa4258184U, 0x2485a104U, 0x41b8f961U, 0xbebb059eU,
+	0xf3a457d3U, 0xa0208080U, 0xb97ec799U, 0xfd2bd6ddU,
+	0xdec31dfeU, 0xefbf50cfU, 0x3c9ba71cU, 0xe93ad3c9U,
+	0x915ccdb1U, 0x78ceb658U, 0xa82a8288U, 0x3f5b641fU,
+	0x22002202U, 0xb3f44793U, 0x83c84ba3U, 0xdac61cfaU,
+	0x0b62692bU, 0x5e633d7eU, 0x8a8208aaU, 0xebba51cbU,
+	0xa160c181U, 0xbffb449fU, 0x40f8b860U, 0x76413756U,
+	0x2e0f210eU, 0x7f0b745fU, 0x6b1a714bU, 0x9bd64dbbU,
+	0x473d7a67U, 0x25c5e005U, 0xf46195d4U, 0x26052306U,
+	0x23406303U, 0x1a362c3aU, 0xd509dcf5U, 0x32142612U,
+	0xced719eeU, 0x70c4b450U, 0xc8529ae8U, 0xf521d4d5U,
+	0x16392f36U, 0xeeff11ceU, 0x68dab248U, 0x60d0b040U,
+	0x77017657U, 0x929c0eb2U, 0x66553346U, 0x2f4f600fU,
+	0xd4499df4U, 0x7b0e755bU, 0x2b4a610bU, 0x18b6ae38U,
+	0xaaaa008aU, 0x39dee719U, 0x8bc249abU, 0x21c0e101U,
+	0xa3e04383U, 0x04ada924U, 0x2c8fa30cU, 0x0e27292eU,
+	0x94198db4U, 0xed3fd2cdU, 0x9956cfb9U, 0x1df3ee3dU,
+	0xf7a156d7U, 0xcf9758efU, 0x7ccbb75cU, 0xb571c495U,
+	0xdb865dfbU, 0x57297e77U, 0x9a960cbaU, 0x1cb3af3cU,
+	0xac2f838cU, 0x5f237c7fU, 0x09e2eb29U, 0x699af349U,
+	0x15f9ec35U, 0x11fced31U, 0xcd17daedU, 0xe535d0c5U,
+	0x96990fb6U, 0xcc579becU, 0x1e332d3eU, 0xeafa10caU,
+	0x4e77396eU, 0xd7895ef7U, 0x526c3e72U, 0x5da3fe7dU,
+	0xe47591c4U, 0xc79d5ae7U, 0xd10cddf1U, 0xa565c085U,
+	0xdc439ffcU, 0xe07090c0U, 0xc912dbe9U, 0xc3985be3U,
+	0x88028aa8U, 0xa7e54287U, 0x64d5b144U, 0x82880aa2U,
+	0x36112716U, 0x97d94eb7U, 0x4db7fa6dU, 0x56693f76U,
+	0x14b9ad34U, 0x7184f551U, 0xe3b053c3U, 0xe87a92c8U,
+	0x42783a62U, 0x37516617U, 0x72443652U, 0x62503242U,
+	0x868d0ba6U, 0x05ede825U, 0xfeeb15deU, 0x8942cba9U,
+	0x901c8cb0U, 0xfaee14daU, 0x7d8bf65dU, 0x59a6ff79U,
+	0x35d1e415U, 0x467d3b66U, 0x67157247U, 0x840d89a4U,
+	0xb7f14697U, 0x19f6ef39U, 0xbbfe459bU, 0xb2b40692U,
+	0xc05898e0U, 0x63107343U, 0x55a9fc75U, 0x137c6f33U,
+	0x48f2ba68U, 0xd38c5ff3U, 0x9559ccb5U, 0x2080a000U,
+	0x45bdf865U, 0x1b766d3bU, 0xdd03defdU, 0x58e6be78U,
+	0xffab54dfU, 0x8fc748afU, 0x44fdb964U, 0x8148c9a1U,
+	0xc6f630beU, 0xdfa877a7U, 0x3a350f42U, 0x667e181eU,
+	0xae842ad6U, 0x1a1d0762U, 0x69b1d811U, 0x4313503bU,
+	0xc536f3bdU, 0x4716513fU, 0xcd3cf1b5U, 0x96b224eeU,
+	0x3df0cd45U, 0x726f1d0aU, 0x2de4c955U, 0x8969e0f1U,
+	0x31ffce49U, 0x00838378U, 0xf78a7d8fU, 0x44d6923cU,
+	0x35facf4dU, 0x673e591fU, 0x8d6ce1f5U, 0x92b725eaU,
+	0x377a4d4fU, 0x8c2ca0f4U, 0xd127f6a9U, 0x05c6c37dU,
+	0xb815adc0U, 0x87e661ffU, 0x8566e3fdU, 0xfb857e83U,
+	0xef947b97U, 0x9432a6ecU, 0xdc68b4a4U, 0x4e5c1236U,
+	0xdbad76a3U, 0x222b095aU, 0xff807f87U, 0x74ea9e0cU,
+	0x5187d629U, 0xcc7cb0b4U, 0xb15feec9U, 0x9037a7e8U,
+	0x2a210b52U, 0xa28b29daU, 0x2f644b57U, 0x1492866cU,
+	0x0e0c0276U, 0x5307542bU, 0xed14f995U, 0x3b754e43U,
+	0xba952fc2U, 0xbd50edc5U, 0x3e300e46U, 0xcbb972b3U,
+	0x9ff867e7U, 0xe2db399aU, 0xcfbc73b7U, 0x766a1c0eU,
+	0x48d99130U, 0xa3cb68dbU, 0x61bbda19U, 0x0746417fU,
+	0x8023a3f8U, 0x8163e2f9U, 0x0343407bU, 0x6e741a16U,
+	0x5247152aU, 0x50c79728U, 0xc133f2b9U, 0x772a5d0fU,
+	0x93f764ebU, 0xf845bd80U, 0x6db4d915U, 0xf905fc81U,
+	0x0606007eU, 0x8be962f3U, 0xa00babd8U, 0x3f704f47U,
+	0x3cb08c44U, 0x30bf8f48U, 0x9bfd66e3U, 0x5702552fU,
+	0x58cd9520U, 0x0c8c8074U, 0xf6ca3c8eU, 0xb3df6ccbU,
+	0x7e601e06U, 0x323f0d4aU, 0xa14bead9U, 0xb01fafc8U,
+	0x5cc89424U, 0xc476b2bcU, 0xeb917a93U, 0x4999d031U,
+	0xe51efb9dU, 0x0dccc175U, 0xa54eebddU, 0xe45eba9cU,
+	0x10978768U, 0xfc40bc84U, 0xdaed37a2U, 0x1752456fU,
+	0x09c9c071U, 0x15d2c76dU, 0x6cf49814U, 0x71afde09U,
+	0x997de4e1U, 0x19ddc461U, 0x7ce09c04U, 0x83e360fbU,
+	0xcefc32b6U, 0x9d78e5e5U, 0x8426a2fcU, 0xc073b3b8U,
+	0xe39b789bU, 0xd2e735aaU, 0x01c3c279U, 0xd462b6acU,
+	0xac04a8d4U, 0x4596d33dU, 0x9572e7edU, 0x0203017aU,
+	0x1f584767U, 0x8eac22f6U, 0xbe902ec6U, 0xe79e799fU,
+	0x363a0c4eU, 0x633b581bU, 0xb7da6dcfU, 0xd6e234aeU,
+	0x9c38a4e4U, 0x82a321faU, 0x7da0dd05U, 0x4b195233U,
+	0x1357446bU, 0x4253113aU, 0x5642142eU, 0xa68e28deU,
+	0x7a651f02U, 0x189d8560U, 0xc939f0b1U, 0x1b5d4663U,
+	0x1e180666U, 0x276e495fU, 0xe851b990U, 0x0f4c4377U,
+	0xf38f7c8bU, 0x4d9cd135U, 0xf50aff8dU, 0xc879b1b0U,
+	0x2b614a53U, 0xd3a774abU, 0x5582d72dU, 0x5d88d525U,
+	0x4a591332U, 0xafc46bd7U, 0x5b0d5623U, 0x1217056aU,
+	0xe911f891U, 0x4656103eU, 0x1612046eU, 0x25eecb5dU,
+	0x97f265efU, 0x0486827cU, 0xb69a2cceU, 0x1c988464U,
+	0x9eb826e6U, 0x39f5cc41U, 0x11d7c669U, 0x337f4c4bU,
+	0xa941e8d1U, 0xd067b7a8U, 0xa40eaadcU, 0x20ab8b58U,
+	0xcaf933b2U, 0xf2cf3d8aU, 0x4193d239U, 0x8829a1f0U,
+	0xe6de389eU, 0x6a711b12U, 0xa7ce69dfU, 0x21ebca59U,
+	0x9177e6e9U, 0x627b191aU, 0x34ba8e4cU, 0x54c2962cU,
+	0x28a18950U, 0x2ca48854U, 0xf04fbf88U, 0xd86db5a0U,
+	0xabc16ad3U, 0xf10ffe89U, 0x236b485bU, 0xd7a275afU,
+	0x732f5c0bU, 0xead13b92U, 0x6f345b17U, 0x60fb9b18U,
+	0xd92df4a1U, 0xfac53f82U, 0xec54b894U, 0x983da5e0U,
+	0xe11bfa99U, 0xdd28f5a5U, 0xf44abe8cU, 0xfec03e86U,
+	0xb55aefcdU, 0x9abd27e2U, 0x598dd421U, 0xbfd06fc7U,
+	0x0b494273U, 0xaa812bd2U, 0x70ef9f08U, 0x6b315a13U,
+	0x29e1c851U, 0x4cdc9034U, 0xdee836a6U, 0xd522f7adU,
+	0x7f205f07U, 0x0a090372U, 0x4f1c5337U, 0x5f085727U,
+	0xbbd56ec3U, 0x38b58d40U, 0xc3b370bbU, 0xb41aaeccU,
+	0xad44e9d5U, 0xc7b671bfU, 0x40d39338U, 0x64fe9a1cU,
+	0x08898170U, 0x7b255e03U, 0x5a4d1722U, 0xb955ecc1U,
+	0x8aa923f2U, 0x24ae8a5cU, 0x86a620feU, 0x8fec63f7U,
+	0xfd00fd85U, 0x5e481626U, 0x68f19910U, 0x2e240a56U,
+	0x75aadf0dU, 0xeed43a96U, 0xa801a9d0U, 0x1dd8c565U,
+	0x78e59d00U, 0x262e085eU, 0xe05bbb98U, 0x65bedb1dU,
+	0xc2f331baU, 0xb29f2dcaU, 0x79a5dc01U, 0xbc10acc4U,
+	0x0d636ee0U, 0x143d29f9U, 0xf1a0511cU, 0xadeb4640U,
+	0x65117488U, 0xd188593cU, 0xa224864fU, 0x88860e65U,
+	0x0ea3ade3U, 0x8c830f61U, 0x06a9afebU, 0x5d277ab0U,
+	0xf665931bU, 0xb9fa4354U, 0xe671970bU, 0x42fcbeafU,
+	0xfa6a9017U, 0xcb16dd26U, 0x3c1f23d1U, 0x8f43cc62U,
+	0xfe6f9113U, 0xacab0741U, 0x46f9bfabU, 0x59227bb4U,
+	0xfcef1311U, 0x47b9feaaU, 0x1ab2a8f7U, 0xce539d23U,
+	0x7380f39eU, 0x4c733fa1U, 0x4ef3bda3U, 0x301020ddU,
+	0x240125c9U, 0x5fa7f8b2U, 0x17fdeafaU, 0x85c94c68U,
+	0x103828fdU, 0xe9be5704U, 0x341521d9U, 0xbf7fc052U,
+	0x9a128877U, 0x07e9eeeaU, 0x7acab097U, 0x5ba2f9b6U,
+	0xe1b4550cU, 0x691e7784U, 0xe4f11509U, 0xdf07d832U,
+	0xc5995c28U, 0x98920a75U, 0x2681a7cbU, 0xf0e0101dU,
+	0x7100719cU, 0x76c5b39bU, 0xf5a55018U, 0x002c2cedU,
+	0x546d39b9U, 0x294e67c4U, 0x04292de9U, 0xbdff4250U,
+	0x834ccf6eU, 0x685e3685U, 0xaa2e8447U, 0xccd31f21U,
+	0x4bb6fda6U, 0x4af6bca7U, 0xc8d61e25U, 0xa5e14448U,
+	0x99d24b74U, 0x9b52c976U, 0x0aa6ace7U, 0xbcbf0351U,
+	0x58623ab5U, 0x33d0e3deU, 0xa621874bU, 0x3290a2dfU,
+	0xcd935e20U, 0x407c3cadU, 0x6b9ef586U, 0xf4e51119U,
+	0xf725d21aU, 0xfb2ad116U, 0x506838bdU, 0x9c970b71U,
+	0x9358cb7eU, 0xc719de2aU, 0x3d5f62d0U, 0x784a3295U,
+	0xb5f54058U, 0xf9aa5314U, 0x6adeb487U, 0x7b8af196U,
+	0x975dca7aU, 0x0fe3ece2U, 0x200424cdU, 0x820c8e6fU,
+	0x2e8ba5c3U, 0xc6599f2bU, 0x6edbb583U, 0x2fcbe4c2U,
+	0xdb02d936U, 0x37d5e2daU, 0x117869fcU, 0xdcc71b31U,
+	0xc25c9e2fU, 0xde479933U, 0xa761c64aU, 0xba3a8057U,
+	0x52e8babfU, 0xd2489a3fU, 0xb775c25aU, 0x48763ea5U,
+	0x05696ce8U, 0x56edbbbbU, 0x4fb3fca2U, 0x0be6ede6U,
+	0x280e26c5U, 0x19726bf4U, 0xca569c27U, 0x1ff7e8f2U,
+	0x6791f68aU, 0x8e038d63U, 0x5ee7b9b3U, 0xc9965f24U,
+	0xd4cd1939U, 0x45397ca8U, 0x75057098U, 0x2c0b27c1U,
+	0xfdaf5210U, 0xa8ae0645U, 0x7c4f3391U, 0x1d776af0U,
+	0x57adfabaU, 0x49367fa4U, 0xb635835bU, 0x808c0c6dU,
+	0xd8c21a35U, 0x89c64f64U, 0x9dd74a70U, 0x6d1b7680U,
+	0xb1f0415cU, 0xd308db3eU, 0x02acaeefU, 0xd0c8183dU,
+	0xd58d5838U, 0xecfb1701U, 0x23c4e7ceU, 0xc4d91d29U,
+	0x381a22d5U, 0x86098f6bU, 0x3e9fa1d3U, 0x03ecefeeU,
+	0xe0f4140dU, 0x18322af5U, 0x9e178973U, 0x961d8b7bU,
+	0x81cc4d6cU, 0x64513589U, 0x9098087dU, 0xd9825b34U,
+	0x2284a6cfU, 0x8dc34e60U, 0xdd875a30U, 0xee7b9503U,
+	0x5c673bb1U, 0xcf13dc22U, 0x7d0f7290U, 0xd70dda3aU,
+	0x552d78b8U, 0xf260921fU, 0xda429837U, 0xf8ea1215U,
+	0x62d4b68fU, 0x1bf2e9f6U, 0x6f9bf482U, 0xeb3ed506U,
+	0x016c6decU, 0x395a63d4U, 0x8a068c67U, 0x43bcffaeU,
+	0x2d4b66c0U, 0xa1e4454cU, 0x6c5b3781U, 0xea7e9407U,
+	0x5ae2b8b7U, 0xa9ee4744U, 0xff2fd012U, 0x9f57c872U,
+	0xe334d70eU, 0xe731d60aU, 0x3bdae1d6U, 0x13f8ebfeU,
+	0x6054348dU, 0x3a9aa0d7U, 0xe8fe1605U, 0x1c372bf1U,
+	0xb8ba0255U, 0x214465ccU, 0xa4a10549U, 0xab6ec546U,
+	0x12b8aaffU, 0x315061dcU, 0x27c1e6caU, 0x53a8fbbeU,
+	0x2a8ea4c7U, 0x16bdabfbU, 0x3fdfe0d2U, 0x355560d8U,
+	0x7ecfb193U, 0x512879bcU, 0x92188a7fU, 0x74453199U,
+	0xc0dc1c2dU, 0x6114758cU, 0xbb7ac156U, 0xa0a4044dU,
+	0xe274960fU, 0x8749ce6aU, 0x157d68f8U, 0x1eb7a9f3U,
+	0xb4b50159U, 0xc19c5d2cU, 0x84890d69U, 0x949d0979U,
+	0x7040309dU, 0xf320d31eU, 0x08262ee5U, 0x7f8ff092U,
+	0x66d1b78bU, 0x0c232fe1U, 0x8b46cd66U, 0xaf6bc442U,
+	0xc31cdf2eU, 0xb0b0005dU, 0x91d8497cU, 0x72c0b29fU,
+	0x413c7dacU, 0xef3bd402U, 0x4d337ea0U, 0x44793da9U,
+	0x3695a3dbU, 0x95dd4878U, 0xa364c74eU, 0xe5b15408U,
+	0xbe3f8153U, 0x254164c8U, 0x6394f78eU, 0xd64d9b3bU,
+	0xb370c35eU, 0xedbb5600U, 0x2bcee5c6U, 0xae2b8543U,
+	0x09666fe4U, 0x790a7394U, 0xb230825fU, 0x7785f29aU,
+	0x78d0a826U, 0x618eef3fU, 0x841397daU, 0xd8588086U,
+	0x10a2b24eU, 0xa43b9ffaU, 0xd7974089U, 0xfd35c8a3U,
+	0x7b106b25U, 0xf930c9a7U, 0x731a692dU, 0x2894bc76U,
+	0x83d655ddU, 0xcc498592U, 0x93c251cdU, 0x374f7869U,
+	0x8fd956d1U, 0xbea51be0U, 0x49ace517U, 0xfaf00aa4U,
+	0x8bdc57d5U, 0xd918c187U, 0x334a796dU, 0x2c91bd72U,
+	0x895cd5d7U, 0x320a386cU, 0x6f016e31U, 0xbbe05be5U,
+	0x06333558U, 0x39c0f967U, 0x3b407b65U, 0x45a3e61bU,
+	0x51b2e30fU, 0x2a143e74U, 0x624e2c3cU, 0xf07a8aaeU,
+	0x658bee3bU, 0x9c0d91c2U, 0x41a6e71fU, 0xcacc0694U,
+	0xefa14eb1U, 0x725a282cU, 0x0f797651U, 0x2e113f70U,
+	0x940793caU, 0x1cadb142U, 0x9142d3cfU, 0xaab41ef4U,
+	0xb02a9aeeU, 0xed21ccb3U, 0x5332610dU, 0x8553d6dbU,
+	0x04b3b75aU, 0x0376755dU, 0x801696deU, 0x759fea2bU,
+	0x21deff7fU, 0x5cfda102U, 0x719aeb2fU, 0xc84c8496U,
+	0xf6ff09a8U, 0x1dedf043U, 0xdf9d4281U, 0xb960d9e7U,
+	0x3e053b60U, 0x3f457a61U, 0xbd65d8e3U, 0xd052828eU,
+	0xec618db2U, 0xeee10fb0U, 0x7f156a21U, 0xc90cc597U,
+	0x2dd1fc73U, 0x46632518U, 0xd392418dU, 0x47236419U,
+	0xb82098e6U, 0x35cffa6bU, 0x1e2d3340U, 0x8156d7dfU,
+	0x829614dcU, 0x8e9917d0U, 0x25dbfe7bU, 0xe924cdb7U,
+	0xe6eb0db8U, 0xb2aa18ecU, 0x48eca416U, 0x0df9f453U,
+	0xc046869eU, 0x8c1995d2U, 0x1f6d7241U, 0x0e393750U,
+	0xe2ee0cbcU, 0x7a502a24U, 0x55b7e20bU, 0xf7bf48a9U,
+	0x5b386305U, 0xb3ea59edU, 0x1b687345U, 0x5a782204U,
+	0xaeb11ff0U, 0x4266241cU, 0x64cbaf3aU, 0xa974ddf7U,
+	0xb7ef58e9U, 0xabf45ff5U, 0xd2d2008cU, 0xcf894691U,
+	0x275b7c79U, 0xa7fb5cf9U, 0xc2c6049cU, 0x3dc5f863U,
+	0x70daaa2eU, 0x235e7d7dU, 0x3a003a64U, 0x7e552b20U,
+	0x5dbde003U, 0x6cc1ad32U, 0xbfe55ae1U, 0x6a442e34U,
+	0x1222304cU, 0xfbb04ba5U, 0x2b547f75U, 0xbc2599e2U,
+	0xa17edfffU, 0x308aba6eU, 0x00b6b65eU, 0x59b8e107U,
+	0x881c94d6U, 0xdd1dc083U, 0x09fcf557U, 0x68c4ac36U,
+	0x221e3c7cU, 0x3c85b962U, 0xc386459dU, 0xf53fcaabU,
+	0xad71dcf3U, 0xfc7589a2U, 0xe8648cb6U, 0x18a8b046U,
+	0xc443879aU, 0xa6bb1df8U, 0x771f6829U, 0xa57bdefbU,
+	0xa03e9efeU, 0x9948d1c7U, 0x56772108U, 0xb16adbefU,
+	0x4da9e413U, 0xf3ba49adU, 0x4b2c6715U, 0x765f2928U,
+	0x9547d2cbU, 0x6d81ec33U, 0xeba44fb5U, 0xe3ae4dbdU,
+	0xf47f8baaU, 0x11e2f34fU, 0xe52bcebbU, 0xac319df2U,
+	0x57376009U, 0xf87088a6U, 0xa8349cf6U, 0x9bc853c5U,
+	0x29d4fd77U, 0xbaa01ae4U, 0x08bcb456U, 0xa2be1cfcU,
+	0x209ebe7eU, 0x87d354d9U, 0xaff15ef1U, 0x8d59d4d3U,
+	0x17677049U, 0x6e412f30U, 0x1a283244U, 0x9e8d13c0U,
+	0x74dfab2aU, 0x4ce9a512U, 0xffb54aa1U, 0x360f3968U,
+	0x58f8a006U, 0xd457838aU, 0x19e8f147U, 0x9fcd52c1U,
+	0x2f517e71U, 0xdc5d8182U, 0x8a9c16d4U, 0xeae40eb4U,
+	0x968711c8U, 0x928210ccU, 0x4e692710U, 0x664b2d38U,
+	0x15e7f24bU, 0x4f296611U, 0x9d4dd0c3U, 0x6984ed37U,
+	0xcd09c493U, 0x54f7a30aU, 0xd112c38fU, 0xdedd0380U,
+	0x670b6c39U, 0x44e3a71aU, 0x5272200cU, 0x261b3d78U,
+	0x5f3d6201U, 0x630e6d3dU, 0x4a6c2614U, 0x40e6a61eU,
+	0x0b7c7755U, 0x249bbf7aU, 0xe7ab4cb9U, 0x01f6f75fU,
+	0xb56fdaebU, 0x14a7b34aU, 0xcec90790U, 0xd517c28bU,
+	0x97c750c9U, 0xf2fa08acU, 0x60ceae3eU, 0x6b046f35U,
+	0xc106c79fU, 0xb42f9beaU, 0xf13acbafU, 0xe12ecfbfU,
+	0x05f3f65bU, 0x869315d8U, 0x7d95e823U, 0x0a3c3654U,
+	0x1362714dU, 0x7990e927U, 0xfef50ba0U, 0xdad80284U,
+	0xb6af19e8U, 0xc503c69bU, 0xe46b8fbaU, 0x07737459U,
+	0x348fbb6aU, 0x9a8812c4U, 0x3880b866U, 0x31cafb6fU,
+	0x4326651dU, 0xe06e8ebeU, 0xd6d70188U, 0x900292ceU,
+	0xcb8c4795U, 0x50f2a20eU, 0x16273148U, 0xa3fe5dfdU,
+	0xc6c30598U, 0x980890c6U, 0x5e7d2300U, 0xdb984385U,
+	0x7cd5a922U, 0x0cb9b552U, 0xc7834499U, 0x0236345cU,
+	0x9db02da3U, 0x84ee6abaU, 0x6173125fU, 0x3d380503U,
+	0xf5c237cbU, 0x415b1a7fU, 0x32f7c50cU, 0x18554d26U,
+	0x9e70eea0U, 0x1c504c22U, 0x967aeca8U, 0xcdf439f3U,
+	0x66b6d058U, 0x29290017U, 0x76a2d448U, 0xd22ffdecU,
+	0x6ab9d354U, 0x5bc59e65U, 0xaccc6092U, 0x1f908f21U,
+	0x6ebcd250U, 0x3c784402U, 0xd62afce8U, 0xc9f138f7U,
+	0x6c3c5052U, 0xd76abde9U, 0x8a61ebb4U, 0x5e80de60U,
+	0xe353b0ddU, 0xdca07ce2U, 0xde20fee0U, 0xa0c3639eU,
+	0xb4d2668aU, 0xcf74bbf1U, 0x872ea9b9U, 0x151a0f2bU,
+	0x80eb6bbeU, 0x796d1447U, 0xa4c6629aU, 0x2fac8311U,
+	0x0ac1cb34U, 0x973aada9U, 0xea19f3d4U, 0xcb71baf5U,
+	0x7167164fU, 0xf9cd34c7U, 0x7422564aU, 0x4fd49b71U,
+	0x554a1f6bU, 0x08414936U, 0xb652e488U, 0x6033535eU,
+	0xe1d332dfU, 0xe616f0d8U, 0x6576135bU, 0x90ff6faeU,
+	0xc4be7afaU, 0xb99d2487U, 0x94fa6eaaU, 0x2d2c0113U,
+	0x139f8c2dU, 0xf88d75c6U, 0x3afdc704U, 0x5c005c62U,
+	0xdb65bee5U, 0xda25ffe4U, 0x58055d66U, 0x3532070bU,
+	0x09010837U, 0x0b818a35U, 0x9a75efa4U, 0x2c6c4012U,
+	0xc8b179f6U, 0xa303a09dU, 0x36f2c408U, 0xa243e19cU,
+	0x5d401d63U, 0xd0af7feeU, 0xfb4db6c5U, 0x6436525aU,
+	0x67f69159U, 0x6bf99255U, 0xc0bb7bfeU, 0x0c444832U,
+	0x038b883dU, 0x57ca9d69U, 0xad8c2193U, 0xe89971d6U,
+	0x2526031bU, 0x69791057U, 0xfa0df7c4U, 0xeb59b2d5U,
+	0x078e8939U, 0x9f30afa1U, 0xb0d7678eU, 0x12dfcd2cU,
+	0xbe58e680U, 0x568adc68U, 0xfe08f6c0U, 0xbf18a781U,
+	0x4bd19a75U, 0xa706a199U, 0x81ab2abfU, 0x4c145872U,
+	0x528fdd6cU, 0x4e94da70U, 0x37b28509U, 0x2ae9c314U,
+	0xc23bf9fcU, 0x429bd97cU, 0x27a68119U, 0xd8a57de6U,
+	0x95ba2fabU, 0xc63ef8f8U, 0xdf60bfe1U, 0x9b35aea5U,
+	0xb8dd6586U, 0x89a128b7U, 0x5a85df64U, 0x8f24abb1U,
+	0xf742b5c9U, 0x1ed0ce20U, 0xce34faf0U, 0x59451c67U,
+	0x441e5a7aU, 0xd5ea3febU, 0xe5d633dbU, 0xbcd86482U,
+	0x6d7c1153U, 0x387d4506U, 0xec9c70d2U, 0x8da429b3U,
+	0xc77eb9f9U, 0xd9e53ce7U, 0x26e6c018U, 0x105f4f2eU,
+	0x48115976U, 0x19150c27U, 0x0d040933U, 0xfdc835c3U,
+	0x2123021fU, 0x43db987dU, 0x927fedacU, 0x401b5b7eU,
+	0x455e1b7bU, 0x7c285442U, 0xb317a48dU, 0x540a5e6aU,
+	0xa8c96196U, 0x16dacc28U, 0xae4ce290U, 0x933facadU,
+	0x7027574eU, 0x88e169b6U, 0x0ec4ca30U, 0x06cec838U,
+	0x111f0e2fU, 0xf48276caU, 0x004b4b3eU, 0x49511877U,
+	0xb257e58cU, 0x1d100d23U, 0x4d541973U, 0x7ea8d640U,
+	0xccb478f2U, 0x5fc09f61U, 0xeddc31d3U, 0x47de9979U,
+	0xc5fe3bfbU, 0x62b3d15cU, 0x4a91db74U, 0x68395156U,
+	0xf207f5ccU, 0x8b21aab5U, 0xff48b7c1U, 0x7bed9645U,
+	0x91bf2eafU, 0xa9892097U, 0x1ad5cf24U, 0xd36fbcedU,
+	0xbd982583U, 0x3137060fU, 0xfc8874c2U, 0x7aadd744U,
+	0xca31fbf4U, 0x393d0407U, 0x6ffc9351U, 0x0f848b31U,
+	0x73e7944dU, 0x77e29549U, 0xab09a295U, 0x832ba8bdU,
+	0xf08777ceU, 0xaa49e394U, 0x782d5546U, 0x8ce468b2U,
+	0x28694116U, 0xb197268fU, 0x3472460aU, 0x3bbd8605U,
+	0x826be9bcU, 0xa183229fU, 0xb712a589U, 0xc37bb8fdU,
+	0xba5de784U, 0x866ee8b8U, 0xaf0ca391U, 0xa586239bU,
+	0xee1cf2d0U, 0xc1fb3affU, 0x02cbc93cU, 0xe49672daU,
+	0x500f5f6eU, 0xf1c736cfU, 0x2ba98215U, 0x3077470eU,
+	0x72a7d54cU, 0x179a8d29U, 0x85ae2bbbU, 0x8e64eab0U,
+	0x2466421aU, 0x514f1e6fU, 0x145a4e2aU, 0x044e4a3aU,
+	0xe09373deU, 0x63f3905dU, 0x98f56da6U, 0xef5cb3d1U,
+	0xf602f4c8U, 0x9cf06ca2U, 0x1b958e25U, 0x3fb88701U,
+	0x53cf9c6dU, 0x2063431eU, 0x010b0a3fU, 0xe213f1dcU,
+	0xd1ef3eefU, 0x7fe89741U, 0xdde03de3U, 0xd4aa7eeaU,
+	0xa646e098U, 0x050e0b3bU, 0x33b7840dU, 0x7562174bU,
+	0x2eecc210U, 0xb592278bU, 0xf347b4cdU, 0x469ed878U,
+	0x23a3801dU, 0x7d681543U, 0xbb1da685U, 0x3ef8c600U,
+	0x99b52ca7U, 0xe9d930d7U, 0x22e3c11cU, 0xe756b1d9U,
+	0xd05a8a04U, 0xc904cd1dU, 0x2c99b5f8U, 0x70d2a2a4U,
+	0xb828906cU, 0x0cb1bdd8U, 0x7f1d62abU, 0x55bfea81U,
+	0xd39a4907U, 0x51baeb85U, 0xdb904b0fU, 0x801e9e54U,
+	0x2b5c77ffU, 0x64c3a7b0U, 0x3b4873efU, 0x9fc55a4bU,
+	0x275374f3U, 0x162f39c2U, 0xe126c735U, 0x527a2886U,
+	0x235675f7U, 0x7192e3a5U, 0x9bc05b4fU, 0x841b9f50U,
+	0x21d6f7f5U, 0x9a801a4eU, 0xc78b4c13U, 0x136a79c7U,
+	0xaeb9177aU, 0x914adb45U, 0x93ca5947U, 0xed29c439U,
+	0xf938c12dU, 0x829e1c56U, 0xcac40e1eU, 0x58f0a88cU,
+	0xcd01cc19U, 0x3487b3e0U, 0xe92cc53dU, 0x624624b6U,
+	0x472b6c93U, 0xdad00a0eU, 0xa7f35473U, 0x869b1d52U,
+	0x3c8db1e8U, 0xb4279360U, 0x39c8f1edU, 0x023e3cd6U,
+	0x18a0b8ccU, 0x45abee91U, 0xfbb8432fU, 0x2dd9f4f9U,
+	0xac399578U, 0xabfc577fU, 0x289cb4fcU, 0xdd15c809U,
+	0x8954dd5dU, 0xf4778320U, 0xd910c90dU, 0x60c6a6b4U,
+	0x5e752b8aU, 0xb567d261U, 0x771760a3U, 0x11eafbc5U,
+	0x968f1942U, 0x97cf5843U, 0x15effac1U, 0x78d8a0acU,
+	0x44ebaf90U, 0x466b2d92U, 0xd79f4803U, 0x6186e7b5U,
+	0x855bde51U, 0xeee9073aU, 0x7b1863afU, 0xefa9463bU,
+	0x10aabac4U, 0x9d45d849U, 0xb6a71162U, 0x29dcf5fdU,
+	0x2a1c36feU, 0x261335f2U, 0x8d51dc59U, 0x41aeef95U,
+	0x4e612f9aU, 0x1a203aceU, 0xe0668634U, 0xa573d671U,
+	0x68cca4bcU, 0x2493b7f0U, 0xb7e75063U, 0xa6b31572U,
+	0x4a642e9eU, 0xd2da0806U, 0xfd3dc029U, 0x5f356a8bU,
+	0xf3b24127U, 0x1b607bcfU, 0xb3e25167U, 0xf2f20026U,
+	0x063b3dd2U, 0xeaec063eU, 0xcc418d18U, 0x01feffd5U,
+	0x1f657acbU, 0x037e7dd7U, 0x7a5822aeU, 0x670364b3U,
+	0x8fd15e5bU, 0x0f717edbU, 0x6a4c26beU, 0x954fda41U,
+	0xd850880cU, 0x8bd45f5fU, 0x928a1846U, 0xd6df0902U,
+	0xf537c221U, 0xc44b8f10U, 0x176f78c3U, 0xc2ce0c16U,
+	0xbaa8126eU, 0x533a6987U, 0x83de5d57U, 0x14afbbc0U,
+	0x09f4fdddU, 0x9800984cU, 0xa83c947cU, 0xf132c325U,
+	0x2096b6f4U, 0x7597e2a1U, 0xa176d775U, 0xc04e8e14U,
+	0x8a941e5eU, 0x940f9b40U, 0x6b0c67bfU, 0x5db5e889U,
+	0x05fbfed1U, 0x54ffab80U, 0x40eeae94U, 0xb0229264U,
+	0x6cc9a5b8U, 0x0e313fdaU, 0xdf954a0bU, 0x0df1fcd9U,
+	0x08b4bcdcU, 0x31c2f3e5U, 0xfefd032aU, 0x19e0f9cdU,
+	0xe523c631U, 0x5b306b8fU, 0xe3a64537U, 0xded50b0aU,
+	0x3dcdf0e9U, 0xc50bce11U, 0x432e6d97U, 0x4b246f9fU,
+	0x5cf5a988U, 0xb968d16dU, 0x4da1ec99U, 0x04bbbfd0U,
+	0xffbd422bU, 0x50faaa84U, 0x00bebed4U, 0x334271e7U,
+	0x815edf55U, 0x122a38c6U, 0xa0369674U, 0x0a343edeU,
+	0x88149c5cU, 0x2f5976fbU, 0x077b7cd3U, 0x25d3f6f1U,
+	0xbfed526bU, 0xc6cb0d12U, 0xb2a21066U, 0x360731e2U,
+	0xdc558908U, 0xe4638730U, 0x573f6883U, 0x9e851b4aU,
+	0xf0728224U, 0x7cdda1a8U, 0xb162d365U, 0x374770e3U,
+	0x87db5c53U, 0x74d7a3a0U, 0x221634f6U, 0x426e2c96U,
+	0x3e0d33eaU, 0x3a0832eeU, 0xe6e30532U, 0xcec10f1aU,
+	0xbd6dd069U, 0xe7a34433U, 0x35c7f2e1U, 0xc10ecf15U,
+	0x6583e6b1U, 0xfc7d8128U, 0x7998e1adU, 0x765721a2U,
+	0xcf814e1bU, 0xec698538U, 0xfaf8022eU, 0x8e911f5aU,
+	0xf7b74023U, 0xcb844f1fU, 0xe2e60436U, 0xe86c843cU,
+	0xa3f65577U, 0x8c119d58U, 0x4f216e9bU, 0xa97cd57dU,
+	0x1de5f8c9U, 0xbc2d9168U, 0x664325b2U, 0x7d9de0a9U,
+	0x3f4d72ebU, 0x5a702a8eU, 0xc8448c1cU, 0xc38e4d17U,
+	0x698ce5bdU, 0x1ca5b9c8U, 0x59b0e98dU, 0x49a4ed9dU,
+	0xad79d479U, 0x2e1937faU, 0xd51fca01U, 0xa2b61476U,
+	0xbbe8536fU, 0xd11acb05U, 0x567f2982U, 0x725220a6U,
+	0x1e253bcaU, 0x6d89e4b9U, 0x4ce1ad98U, 0xaff9567bU,
+	0x9c059948U, 0x320230e6U, 0x900a9a44U, 0x9940d94dU,
+	0xebac473fU, 0x48e4ac9cU, 0x7e5d23aaU, 0x3888b0ecU,
+	0x630665b7U, 0xf878802cU, 0xbead136aU, 0x0b747fdfU,
+	0x6e4927baU, 0x3082b2e4U, 0xf6f70122U, 0x731261a7U,
+	0xd45f8b00U, 0xa4339770U, 0x6f0966bbU, 0xaabc167eU,
+	0xbc46fa74U, 0xa518bd6dU, 0x4085c588U, 0x1cced2d4U,
+	0xd434e01cU, 0x60adcda8U, 0x130112dbU, 0x39a39af1U,
+	0xbf863977U, 0x3da69bf5U, 0xb78c3b7fU, 0xec02ee24U,
+	0x4740078fU, 0x08dfd7c0U, 0x5754039fU, 0xf3d92a3bU,
+	0x4b4f0483U, 0x7a3349b2U, 0x8d3ab745U, 0x3e6658f6U,
+	0x4f4a0587U, 0x1d8e93d5U, 0xf7dc2b3fU, 0xe807ef20U,
+	0x4dca8785U, 0xf69c6a3eU, 0xab973c63U, 0x7f7609b7U,
+	0xc2a5670aU, 0xfd56ab35U, 0xffd62937U, 0x8135b449U,
+	0x9524b15dU, 0xee826c26U, 0xa6d87e6eU, 0x34ecd8fcU,
+	0xa11dbc69U, 0x589bc390U, 0x8530b54dU, 0x0e5a54c6U,
+	0x2b371ce3U, 0xb6cc7a7eU, 0xcbef2403U, 0xea876d22U,
+	0x5091c198U, 0xd83be310U, 0x55d4819dU, 0x6e224ca6U,
+	0x74bcc8bcU, 0x29b79ee1U, 0x97a4335fU, 0x41c58489U,
+	0xc025e508U, 0xc7e0270fU, 0x4480c48cU, 0xb109b879U,
+	0xe548ad2dU, 0x986bf350U, 0xb50cb97dU, 0x0cdad6c4U,
+	0x32695bfaU, 0xd97ba211U, 0x1b0b10d3U, 0x7df68bb5U,
+	0xfa936932U, 0xfbd32833U, 0x79f38ab1U, 0x14c4d0dcU,
+	0x28f7dfe0U, 0x2a775de2U, 0xbb833873U, 0x0d9a97c5U,
+	0xe947ae21U, 0x82f5774aU, 0x170413dfU, 0x83b5364bU,
+	0x7cb6cab4U, 0xf159a839U, 0xdabb6112U, 0x45c0858dU,
+	0x4600468eU, 0x4a0f4582U, 0xe14dac29U, 0x2db29fe5U,
+	0x227d5feaU, 0x763c4abeU, 0x8c7af644U, 0xc96fa601U,
+	0x04d0d4ccU, 0x488fc780U, 0xdbfb2013U, 0xcaaf6502U,
+	0x26785eeeU, 0xbec67876U, 0x9121b059U, 0x33291afbU,
+	0x9fae3157U, 0x777c0bbfU, 0xdffe2117U, 0x9eee7056U,
+	0x6a274da2U, 0x86f0764eU, 0xa05dfd68U, 0x6de28fa5U,
+	0x73790abbU, 0x6f620da7U, 0x164452deU, 0x0b1f14c3U,
+	0xe3cd2e2bU, 0x636d0eabU, 0x065056ceU, 0xf953aa31U,
+	0xb44cf87cU, 0xe7c82f2fU, 0xfe966836U, 0xbac37972U,
+	0x992bb251U, 0xa857ff60U, 0x7b7308b3U, 0xaed27c66U,
+	0xd6b4621eU, 0x3f2619f7U, 0xefc22d27U, 0x78b3cbb0U,
+	0x65e88dadU, 0xf41ce83cU, 0xc420e40cU, 0x9d2eb355U,
+	0x4c8ac684U, 0x198b92d1U, 0xcd6aa705U, 0xac52fe64U,
+	0xe6886e2eU, 0xf813eb30U, 0x071017cfU, 0x31a998f9U,
+	0x69e78ea1U, 0x38e3dbf0U, 0x2cf2dee4U, 0xdc3ee214U,
+	0x00d5d5c8U, 0x622d4faaU, 0xb3893a7bU, 0x61ed8ca9U,
+	0x64a8ccacU, 0x5dde8395U, 0x92e1735aU, 0x75fc89bdU,
+	0x893fb641U, 0x372c1bffU, 0x8fba3547U, 0xb2c97b7aU,
+	0x51d18099U, 0xa917be61U, 0x2f321de7U, 0x27381fefU,
+	0x30e9d9f8U, 0xd574a11dU, 0x21bd9ce9U, 0x68a7cfa0U,
+	0x93a1325bU, 0x3ce6daf4U, 0x6ca2cea4U, 0x5f5e0197U,
+	0xed42af25U, 0x7e3648b6U, 0xcc2ae604U, 0x66284eaeU,
+	0xe408ec2cU, 0x4345068bU, 0x6b670ca3U, 0x49cf8681U,
+	0xd3f1221bU, 0xaad77d62U, 0xdebe6016U, 0x5a1b4192U,
+	0xb049f978U, 0x887ff740U, 0x3b2318f3U, 0xf2996b3aU,
+	0x9c6ef254U, 0x10c1d1d8U, 0xdd7ea315U, 0x5b5b0093U,
+	0xebc72c23U, 0x18cbd3d0U, 0x4e0a4486U, 0x2e725ce6U,
+	0x5211439aU, 0x5614429eU, 0x8aff7542U, 0xa2dd7f6aU,
+	0xd171a019U, 0x8bbf3443U, 0x59db8291U, 0xad12bf65U,
+	0x099f96c1U, 0x9061f158U, 0x158491ddU, 0x1a4b51d2U,
+	0xa39d3e6bU, 0x8075f548U, 0x96e4725eU, 0xe28d6f2aU,
+	0x9bab3053U, 0xa7983f6fU, 0x8efa7446U, 0x8470f44cU,
+	0xcfea2507U, 0xe00ded28U, 0x233d1eebU, 0xc560a50dU,
+	0x71f988b9U, 0xd031e118U, 0x0a5f55c2U, 0x118190d9U,
+	0x5351029bU, 0x366c5afeU, 0xa458fc6cU, 0xaf923d67U,
+	0x059095cdU, 0x70b9c9b8U, 0x35ac99fdU, 0x25b89dedU,
+	0xc165a409U, 0x4205478aU, 0xb903ba71U, 0xceaa6406U,
+	0xd7f4231fU, 0xbd06bb75U, 0x3a6359f2U, 0x1e4e50d6U,
+	0x72394bbaU, 0x019594c9U, 0x20fddde8U, 0xc3e5260bU,
+	0xf019e938U, 0x5e1e4096U, 0xfc16ea34U, 0xf55ca93dU,
+	0x87b0374fU, 0x24f8dcecU, 0x124153daU, 0x5494c09cU,
+	0x0f1a15c7U, 0x9464f05cU, 0xd2b1631aU, 0x67680fafU,
+	0x025557caU, 0x5c9ec294U, 0x9aeb7152U, 0x1f0e11d7U,
+	0xb843fb70U, 0xc82fe700U, 0x031516cbU, 0xc6a0660eU,
+	0x86b731bfU, 0x9fe976a6U, 0x7a740e43U, 0x263f191fU,
+	0xeec52bd7U, 0x5a5c0663U, 0x29f0d910U, 0x0352513aU,
+	0x8577f2bcU, 0x0757503eU, 0x8d7df0b4U, 0xd6f325efU,
+	0x7db1cc44U, 0x322e1c0bU, 0x6da5c854U, 0xc928e1f0U,
+	0x71becf48U, 0x40c28279U, 0xb7cb7c8eU, 0x0497933dU,
+	0x75bbce4cU, 0x277f581eU, 0xcd2de0f4U, 0xd2f624ebU,
+	0x773b4c4eU, 0xcc6da1f5U, 0x9166f7a8U, 0x4587c27cU,
+	0xf854acc1U, 0xc7a760feU, 0xc527e2fcU, 0xbbc47f82U,
+	0xafd57a96U, 0xd473a7edU, 0x9c29b5a5U, 0x0e1d1337U,
+	0x9bec77a2U, 0x626a085bU, 0xbfc17e86U, 0x34ab9f0dU,
+	0x11c6d728U, 0x8c3db1b5U, 0xf11eefc8U, 0xd076a6e9U,
+	0x6a600a53U, 0xe2ca28dbU, 0x6f254a56U, 0x54d3876dU,
+	0x4e4d0377U, 0x1346552aU, 0xad55f894U, 0x7b344f42U,
+	0xfad42ec3U, 0xfd11ecc4U, 0x7e710f47U, 0x8bf873b2U,
+	0xdfb966e6U, 0xa29a389bU, 0x8ffd72b6U, 0x362b1d0fU,
+	0x08989031U, 0xe38a69daU, 0x21fadb18U, 0x4707407eU,
+	0xc062a2f9U, 0xc122e3f8U, 0x4302417aU, 0x2e351b17U,
+	0x1206142bU, 0x10869629U, 0x8172f3b8U, 0x376b5c0eU,
+	0xd3b665eaU, 0xb804bc81U, 0x2df5d814U, 0xb944fd80U,
+	0x4647017fU, 0xcba863f2U, 0xe04aaad9U, 0x7f314e46U,
+	0x7cf18d45U, 0x70fe8e49U, 0xdbbc67e2U, 0x1743542eU,
+	0x188c9421U, 0x4ccd8175U, 0xb68b3d8fU, 0xf39e6dcaU,
+	0x3e211f07U, 0x727e0c4bU, 0xe10aebd8U, 0xf05eaec9U,
+	0x1c899525U, 0x8437b3bdU, 0xabd07b92U, 0x09d8d130U,
+	0xa55ffa9cU, 0x4d8dc074U, 0xe50feadcU, 0xa41fbb9dU,
+	0x50d68669U, 0xbc01bd85U, 0x9aac36a3U, 0x5713446eU,
+	0x4988c170U, 0x5593c66cU, 0x2cb59915U, 0x31eedf08U,
+	0xd93ce5e0U, 0x599cc560U, 0x3ca19d05U, 0xc3a261faU,
+	0x8ebd33b7U, 0xdd39e4e4U, 0xc467a3fdU, 0x8032b2b9U,
+	0xa3da799aU, 0x92a634abU, 0x4182c378U, 0x9423b7adU,
+	0xec45a9d5U, 0x05d7d23cU, 0xd533e6ecU, 0x4242007bU,
+	0x5f194666U, 0xceed23f7U, 0xfed12fc7U, 0xa7df789eU,
+	0x767b0d4fU, 0x237a591aU, 0xf79b6cceU, 0x96a335afU,
+	0xdc79a5e5U, 0xc2e220fbU, 0x3de1dc04U, 0x0b585332U,
+	0x5316456aU, 0x0212103bU, 0x1603152fU, 0xe6cf29dfU,
+	0x3a241e03U, 0x58dc8461U, 0x8978f1b0U, 0x5b1c4762U,
+	0x5e590767U, 0x672f485eU, 0xa810b891U, 0x4f0d4276U,
+	0xb3ce7d8aU, 0x0dddd034U, 0xb54bfe8cU, 0x8838b0b1U,
+	0x6b204b52U, 0x93e675aaU, 0x15c3d62cU, 0x1dc9d424U,
+	0x0a181233U, 0xef856ad6U, 0x1b4c5722U, 0x5256046bU,
+	0xa950f990U, 0x0617113fU, 0x5653056fU, 0x65afca5cU,
+	0xd7b364eeU, 0x44c7837dU, 0xf6db2dcfU, 0x5cd98565U,
+	0xdef927e7U, 0x79b4cd40U, 0x5196c768U, 0x733e4d4aU,
+	0xe900e9d0U, 0x9026b6a9U, 0xe44fabddU, 0x60ea8a59U,
+	0x8ab832b3U, 0xb28e3c8bU, 0x01d2d338U, 0xc868a0f1U,
+	0xa69f399fU, 0x2a301a13U, 0xe78f68deU, 0x61aacb58U,
+	0xd136e7e8U, 0x223a181bU, 0x74fb8f4dU, 0x1483972dU,
+	0x68e08851U, 0x6ce58955U, 0xb00ebe89U, 0x982cb4a1U,
+	0xeb806bd2U, 0xb14eff88U, 0x632a495aU, 0x97e374aeU,
+	0x336e5d0aU, 0xaa903a93U, 0x2f755a16U, 0x20ba9a19U,
+	0x996cf5a0U, 0xba843e83U, 0xac15b995U, 0xd87ca4e1U,
+	0xa15afb98U, 0x9d69f4a4U, 0xb40bbf8dU, 0xbe813f87U,
+	0xf51beeccU, 0xdafc26e3U, 0x19ccd520U, 0xff916ec6U,
+	0x4b084372U, 0xeac02ad3U, 0x30ae9e09U, 0x2b705b12U,
+	0x69a0c950U, 0x0c9d9135U, 0x9ea937a7U, 0x9563f6acU,
+	0x3f615e06U, 0x4a480273U, 0x0f5d5236U, 0x1f495626U,
+	0xfb946fc2U, 0x78f48c41U, 0x83f271baU, 0xf45bafcdU,
+	0xed05e8d4U, 0x87f770beU, 0x00929239U, 0x24bf9b1dU,
+	0x48c88071U, 0x3b645f02U, 0x1a0c1623U, 0xf914edc0U,
+	0xcae822f3U, 0x64ef8b5dU, 0xc6e721ffU, 0xcfad62f6U,
+	0xbd41fc84U, 0x1e091727U, 0x28b09811U, 0x6e650b57U,
+	0x35ebde0cU, 0xae953b97U, 0xe840a8d1U, 0x5d99c464U,
+	0x38a49c01U, 0x666f095fU, 0xa01aba99U, 0x25ffda1cU,
+	0x82b230bbU, 0xf2de2ccbU, 0x39e4dd00U, 0xfc51adc5U,
+	0x33c7f47aU, 0x2a99b363U, 0xcf04cb86U, 0x934fdcdaU,
+	0x5bb5ee12U, 0xef2cc3a6U, 0x9c801cd5U, 0xb62294ffU,
+	0x30073779U, 0xb22795fbU, 0x380d3571U, 0x6383e02aU,
+	0xc8c10981U, 0x875ed9ceU, 0xd8d50d91U, 0x7c582435U,
+	0xc4ce0a8dU, 0xf5b247bcU, 0x02bbb94bU, 0xb1e756f8U,
+	0xc0cb0b89U, 0x920f9ddbU, 0x785d2531U, 0x6786e12eU,
+	0xc24b898bU, 0x791d6430U, 0x2416326dU, 0xf0f707b9U,
+	0x4d246904U, 0x72d7a53bU, 0x70572739U, 0x0eb4ba47U,
+	0x1aa5bf53U, 0x61036228U, 0x29597060U, 0xbb6dd6f2U,
+	0x2e9cb267U, 0xd71acd9eU, 0x0ab1bb43U, 0x81db5ac8U,
+	0xa4b612edU, 0x394d7470U, 0x446e2a0dU, 0x6506632cU,
+	0xdf10cf96U, 0x57baed1eU, 0xda558f93U, 0xe1a342a8U,
+	0xfb3dc6b2U, 0xa63690efU, 0x18253d51U, 0xce448a87U,
+	0x4fa4eb06U, 0x48612901U, 0xcb01ca82U, 0x3e88b677U,
+	0x6ac9a323U, 0x17eafd5eU, 0x3a8db773U, 0x835bd8caU,
+	0xbde855f4U, 0x56faac1fU, 0x948a1eddU, 0xf27785bbU,
+	0x7512673cU, 0x7452263dU, 0xf67284bfU, 0x9b45ded2U,
+	0xa776d1eeU, 0xa5f653ecU, 0x3402367dU, 0x821b99cbU,
+	0x66c6a02fU, 0x0d747944U, 0x98851dd1U, 0x0c343845U,
+	0xf337c4baU, 0x7ed8a637U, 0x553a6f1cU, 0xca418b83U,
+	0xc9814880U, 0xc58e4b8cU, 0x6ecca227U, 0xa23391ebU,
+	0xadfc51e4U, 0xf9bd44b0U, 0x03fbf84aU, 0x46eea80fU,
+	0x8b51dac2U, 0xc70ec98eU, 0x547a2e1dU, 0x452e6b0cU,
+	0xa9f950e0U, 0x31477678U, 0x1ea0be57U, 0xbca814f5U,
+	0x102f3f59U, 0xf8fd05b1U, 0x507f2f19U, 0x116f7e58U,
+	0xe5a643acU, 0x09717840U, 0x2fdcf366U, 0xe26381abU,
+	0xfcf804b5U, 0xe0e303a9U, 0x99c55cd0U, 0x849e1acdU,
+	0x6c4c2025U, 0xecec00a5U, 0x89d158c0U, 0x76d2a43fU,
+	0x3bcdf672U, 0x68492121U, 0x71176638U, 0x3542777cU,
+	0x16aabc5fU, 0x27d6f16eU, 0xf4f206bdU, 0x21537268U,
+	0x59356c10U, 0xb0a717f9U, 0x60432329U, 0xf732c5beU,
+	0xea6983a3U, 0x7b9de632U, 0x4ba1ea02U, 0x12afbd5bU,
+	0xc30bc88aU, 0x960a9cdfU, 0x42eba90bU, 0x23d3f06aU,
+	0x69096020U, 0x7792e53eU, 0x889119c1U, 0xbe2896f7U,
+	0xe66680afU, 0xb762d5feU, 0xa373d0eaU, 0x53bfec1aU,
+	0x8f54dbc6U, 0xedac41a4U, 0x3c083475U, 0xee6c82a7U,
+	0xeb29c2a2U, 0xd25f8d9bU, 0x1d607d54U, 0xfa7d87b3U,
+	0x06beb84fU, 0xb8ad15f1U, 0x003b3b49U, 0x3d487574U,
+	0xde508e97U, 0x2696b06fU, 0xa0b313e9U, 0xa8b911e1U,
+	0xbf68d7f6U, 0x5af5af13U, 0xae3c92e7U, 0xe726c1aeU,
+	0x1c203c55U, 0xb367d4faU, 0xe323c0aaU, 0xd0df0f99U,
+	0x62c3a12bU, 0xf1b746b8U, 0x43abe80aU, 0xe9a940a0U,
+	0x6b89e222U, 0xccc40885U, 0xe4e602adU, 0xc64e888fU,
+	0x5c702c15U, 0x2556736cU, 0x513f6e18U, 0xd59a4f9cU,
+	0x3fc8f776U, 0x07fef94eU, 0xb4a216fdU, 0x7d186534U,
+	0x13effc5aU, 0x9f40dfd6U, 0x52ffad1bU, 0xd4da0e9dU,
+	0x6446222dU, 0x974adddeU, 0xc18b4a88U, 0xa1f352e8U,
+	0xdd904d94U, 0xd9954c90U, 0x057e7b4cU, 0x2d5c7164U,
+	0x5ef0ae17U, 0x043e3a4dU, 0xd65a8c9fU, 0x2293b16bU,
+	0x861e98cfU, 0x1fe0ff56U, 0x9a059fd3U, 0x95ca5fdcU,
+	0x2c1c3065U, 0x0ff4fb46U, 0x19657c50U, 0x6d0c6124U,
+	0x142a3e5dU, 0x28193161U, 0x017b7a48U, 0x0bf1fa42U,
+	0x406b2b09U, 0x6f8ce326U, 0xacbc10e5U, 0x4ae1ab03U,
+	0xfe7886b7U, 0x5fb0ef16U, 0x85de5bccU, 0x9e009ed7U,
+	0xdcd00c95U, 0xb9ed54f0U, 0x2bd9f262U, 0x20133369U,
+	0x8a119bc3U, 0xff38c7b6U, 0xba2d97f3U, 0xaa3993e3U,
+	0x4ee4aa07U, 0xcd844984U, 0x3682b47fU, 0x412b6a08U,
+	0x58752d11U, 0x3287b57bU, 0xb5e257fcU, 0x91cf5ed8U,
+	0xfdb845b4U, 0x8e149ac7U, 0xaf7cd3e6U, 0x4c642805U,
+	0x7f98e736U, 0xd19f4e98U, 0x7397e43aU, 0x7adda733U,
+	0x08313941U, 0xab79d2e2U, 0x9dc05dd4U, 0xdb15ce92U,
+	0x809b1bc9U, 0x1be5fe52U, 0x5d306d14U, 0xe8e901a1U,
+	0x8dd459c4U, 0xd31fcc9aU, 0x156a7f5cU, 0x908f1fd9U,
+	0x37c2f57eU, 0x47aee90eU, 0x8c9418c5U, 0x49216800U,
+};
diff --git a/crypto/sms4/sms4_ctr.c b/crypto/sms4/sms4_ctr.c
new file mode 100644
index 000000000..1de3d4ad5
--- /dev/null
+++ b/crypto/sms4/sms4_ctr.c
@@ -0,0 +1,59 @@
+/* ====================================================================
+ * Copyright (c) 2014 - 2017 The GmSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the GmSSL Project.
+ *    (http://gmssl.org/)"
+ *
+ * 4. The name "GmSSL Project" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    guanzhi1980@gmail.com.
+ *
+ * 5. Products derived from this software may not be called "GmSSL"
+ *    nor may "GmSSL" appear in their names without prior written
+ *    permission of the GmSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the GmSSL Project
+ *    (http://gmssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE GmSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE GmSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+
+#include 
+#include 
+
+void sms4_ctr128_encrypt(const unsigned char *in, unsigned char *out,
+	size_t len, const sms4_key_t *key, unsigned char *iv,
+	unsigned char ecount_buf[SMS4_BLOCK_SIZE], unsigned int *num)
+{
+	CRYPTO_ctr128_encrypt(in, out, len, key, iv, ecount_buf, num,
+		(block128_f)sms4_encrypt);
+}
diff --git a/crypto/sms4/sms4_ecb.c b/crypto/sms4/sms4_ecb.c
new file mode 100644
index 000000000..e00816e8c
--- /dev/null
+++ b/crypto/sms4/sms4_ecb.c
@@ -0,0 +1,59 @@
+/* ====================================================================
+ * Copyright (c) 2014 - 2017 The GmSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the GmSSL Project.
+ *    (http://gmssl.org/)"
+ *
+ * 4. The name "GmSSL Project" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    guanzhi1980@gmail.com.
+ *
+ * 5. Products derived from this software may not be called "GmSSL"
+ *    nor may "GmSSL" appear in their names without prior written
+ *    permission of the GmSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the GmSSL Project
+ *    (http://gmssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE GmSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE GmSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+
+#include 
+#include 
+
+void sms4_ecb_encrypt(const unsigned char *in, unsigned char *out,
+	const sms4_key_t *key, int enc)
+{
+	if (enc)
+		sms4_encrypt(in, out, key);
+	else	sms4_decrypt(in, out, key);
+}
diff --git a/crypto/sms4/sms4_ede.c b/crypto/sms4/sms4_ede.c
new file mode 100644
index 000000000..ab1977f51
--- /dev/null
+++ b/crypto/sms4/sms4_ede.c
@@ -0,0 +1,130 @@
+/* ====================================================================
+ * Copyright (c) 2014 - 2019 The GmSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the GmSSL Project.
+ *    (http://gmssl.org/)"
+ *
+ * 4. The name "GmSSL Project" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    guanzhi1980@gmail.com.
+ *
+ * 5. Products derived from this software may not be called "GmSSL"
+ *    nor may "GmSSL" appear in their names without prior written
+ *    permission of the GmSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the GmSSL Project
+ *    (http://gmssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE GmSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE GmSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+
+#include 
+#include 
+
+
+void sms4_ede_set_encrypt_key(sms4_ede_key_t *key,
+	const unsigned char user_key[48])
+{
+	sms4_set_encrypt_key(&key->k1, user_key);
+	sms4_set_decrypt_key(&key->k2, user_key + 16);
+	sms4_set_encrypt_key(&key->k3, user_key + 32);
+}
+
+void sms4_ede_set_decrypt_key(sms4_ede_key_t *key,
+	const unsigned char user_key[48])
+{
+	sms4_set_decrypt_key(&key->k1, user_key + 32);
+	sms4_set_encrypt_key(&key->k2, user_key + 16);
+	sms4_set_decrypt_key(&key->k3, user_key);
+}
+
+void sms4_ede_encrypt(const unsigned char in[16], unsigned char out[16],
+	const sms4_ede_key_t *key)
+{
+	sms4_encrypt(in, out, &key->k1);
+	sms4_encrypt(out, out, &key->k2);
+	sms4_encrypt(out, out, &key->k3);
+}
+
+void sms4_ede_ecb_encrypt(const unsigned char *in, unsigned char *out,
+	const sms4_ede_key_t *key, int enc)
+{
+	(void)enc;
+	sms4_ede_encrypt(in, out, key);
+}
+
+void sms4_ede_cbc_encrypt(const unsigned char *in, unsigned char *out,
+	size_t len, const sms4_ede_key_t *key, unsigned char *iv, int enc)
+{
+	if (enc)
+		CRYPTO_cbc128_encrypt(in, out, len, key, iv,
+			(block128_f)sms4_ede_encrypt);
+	else	CRYPTO_cbc128_decrypt(in, out, len, key, iv,
+			(block128_f)sms4_ede_encrypt);
+}
+
+void sms4_ede_cfb128_encrypt(const unsigned char *in, unsigned char *out,
+	size_t len, const sms4_ede_key_t *key, unsigned char *iv, int *num,
+	int enc)
+{
+	CRYPTO_cfb128_encrypt(in, out, len, key, iv, num, enc,
+		(block128_f)sms4_ede_encrypt);
+}
+
+void sms4_ede_ofb128_encrypt(const unsigned char *in, unsigned char *out,
+	size_t len, const sms4_ede_key_t *key, unsigned char *iv, int *num)
+{
+	CRYPTO_ofb128_encrypt(in, out, len, key, iv, num,
+		(block128_f)sms4_ede_encrypt);
+}
+
+void sms4_ede_ctr128_encrypt(const unsigned char *in, unsigned char *out,
+	size_t len, const sms4_ede_key_t *key, unsigned char *iv,
+	unsigned char ecount_buf[SMS4_BLOCK_SIZE], unsigned int *num)
+{
+	CRYPTO_ctr128_encrypt(in, out, len, key, iv, ecount_buf, num,
+		(block128_f)sms4_ede_encrypt);
+}
+
+int sms4_ede_wrap_key(sms4_ede_key_t *key, const unsigned char *iv,
+	unsigned char *out, const unsigned char *in, unsigned int inlen)
+{
+	return CRYPTO_128_wrap(key, iv, out, in, inlen,
+		(block128_f)sms4_ede_encrypt);
+}
+
+int sms4_ede_unwrap_key(sms4_ede_key_t *key, const unsigned char *iv,
+	unsigned char *out, const unsigned char *in, unsigned int inlen)
+{
+	return CRYPTO_128_unwrap(key, iv, out, in, inlen,
+		(block128_f)sms4_ede_encrypt);
+}
diff --git a/crypto/sms4/sms4_enc.c b/crypto/sms4/sms4_enc.c
new file mode 100644
index 000000000..5106de98c
--- /dev/null
+++ b/crypto/sms4/sms4_enc.c
@@ -0,0 +1,133 @@
+/* ====================================================================
+ * Copyright (c) 2014 - 2017 The GmSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the GmSSL Project.
+ *    (http://gmssl.org/)"
+ *
+ * 4. The name "GmSSL Project" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    guanzhi1980@gmail.com.
+ *
+ * 5. Products derived from this software may not be called "GmSSL"
+ *    nor may "GmSSL" appear in their names without prior written
+ *    permission of the GmSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the GmSSL Project
+ *    (http://gmssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE GmSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE GmSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+
+#include 
+#include "internal/rotate.h"
+#include "modes_lcl.h"
+#include "sms4_lcl.h"
+
+
+#define L32(x)							\
+	((x) ^							\
+	ROL32((x),  2) ^					\
+	ROL32((x), 10) ^					\
+	ROL32((x), 18) ^					\
+	ROL32((x), 24))
+
+#define ROUND_SBOX(x0, x1, x2, x3, x4, i)			\
+	x4 = x1 ^ x2 ^ x3 ^ *(rk + i);				\
+	x4 = S32(x4);						\
+	x4 = x0 ^ L32(x4)
+
+#define ROUND_TBOX(x0, x1, x2, x3, x4, i)			\
+	x4 = x1 ^ x2 ^ x3 ^ *(rk + i);				\
+	t0 = ROL32(SMS4_T[(uint8_t)x4], 8);			\
+	x4 >>= 8;						\
+	x0 ^= t0;						\
+	t0 = ROL32(SMS4_T[(uint8_t)x4], 16);			\
+	x4 >>= 8;						\
+	x0 ^= t0;						\
+	t0 = ROL32(SMS4_T[(uint8_t)x4], 24);			\
+	x4 >>= 8;						\
+	x0 ^= t0;						\
+	t1 = SMS4_T[x4];					\
+	x4 = x0 ^ t1
+
+#define ROUND_DBOX(x0, x1, x2, x3, x4, i)			\
+	x4 = x1 ^ x2 ^ x3 ^ *(rk + i);				\
+	x4 = x0 ^ SMS4_D[(uint16_t)(x4 >> 16)] ^		\
+		ROL32(SMS4_D[(uint16_t)x4], 16)
+
+#define ROUND ROUND_TBOX
+
+
+void sms4_encrypt(const unsigned char in[16], unsigned char out[16], const sms4_key_t *key)
+{
+	const uint32_t *rk = key->rk;
+	uint32_t x0, x1, x2, x3, x4;
+	uint32_t t0, t1;
+
+	x0 = GETU32(in     );
+	x1 = GETU32(in +  4);
+	x2 = GETU32(in +  8);
+	x3 = GETU32(in + 12);
+	ROUNDS(x0, x1, x2, x3, x4);
+	PUTU32(out     , x0);
+	PUTU32(out +  4, x4);
+	PUTU32(out +  8, x3);
+	PUTU32(out + 12, x2);
+}
+
+/* caller make sure counter not overflow */
+void sms4_ctr32_encrypt_blocks(const unsigned char *in, unsigned char *out,
+	size_t blocks, const sms4_key_t *key, const unsigned char iv[16])
+{
+	const uint32_t *rk = key->rk;
+	unsigned int c0 = GETU32(iv     );
+	unsigned int c1 = GETU32(iv +  4);
+	unsigned int c2 = GETU32(iv +  8);
+	unsigned int c3 = GETU32(iv + 12);
+	uint32_t x0, x1, x2, x3, x4;
+	uint32_t t0, t1;
+
+	while (blocks--) {
+		x0 = c0;
+		x1 = c1;
+		x2 = c2;
+		x3 = c3;
+		ROUNDS(x0, x1, x2, x3, x4);
+		PUTU32(out     , GETU32(in     ) ^ x0);
+		PUTU32(out +  4, GETU32(in +  4) ^ x4);
+		PUTU32(out +  8, GETU32(in +  8) ^ x3);
+		PUTU32(out + 12, GETU32(in + 12) ^ x2);
+		in += 16;
+		out += 16;
+		c3++;
+	}
+}
diff --git a/crypto/sms4/sms4_enc_avx2.c b/crypto/sms4/sms4_enc_avx2.c
new file mode 100644
index 000000000..91a2a8387
--- /dev/null
+++ b/crypto/sms4/sms4_enc_avx2.c
@@ -0,0 +1,208 @@
+/* ====================================================================
+ * Copyright (c) 2014 - 2019 The GmSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the GmSSL Project.
+ *    (http://gmssl.org/)"
+ *
+ * 4. The name "GmSSL Project" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    guanzhi1980@gmail.com.
+ *
+ * 5. Products derived from this software may not be called "GmSSL"
+ *    nor may "GmSSL" appear in their names without prior written
+ *    permission of the GmSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the GmSSL Project
+ *    (http://gmssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE GmSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE GmSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+
+#include 
+#include "internal/rotate.h"
+#include "modes_lcl.h"
+#include "sms4_lcl.h"
+
+#ifdef SMS4_AVX2
+# include 
+
+# define GET_BLKS(x0, x1, x2, x3, in)					\
+	t0 = _mm256_i32gather_epi32((int *)(in+4*0), vindex_4i, 4);	\
+	t1 = _mm256_i32gather_epi32((int *)(in+4*1), vindex_4i, 4);	\
+	t2 = _mm256_i32gather_epi32((int *)(in+4*2), vindex_4i, 4);	\
+	t3 = _mm256_i32gather_epi32((int *)(in+4*3), vindex_4i, 4);	\
+	x0 = _mm256_shuffle_epi8(t0, vindex_swap);			\
+	x1 = _mm256_shuffle_epi8(t1, vindex_swap);			\
+	x2 = _mm256_shuffle_epi8(t2, vindex_swap);			\
+	x3 = _mm256_shuffle_epi8(t3, vindex_swap)
+
+# define PUT_BLKS(out, x0, x1, x2, x3)					\
+	t0 = _mm256_shuffle_epi8(x0, vindex_swap);			\
+	t1 = _mm256_shuffle_epi8(x1, vindex_swap);			\
+	t2 = _mm256_shuffle_epi8(x2, vindex_swap);			\
+	t3 = _mm256_shuffle_epi8(x3, vindex_swap);			\
+	_mm256_storeu_si256((__m256i *)(out+32*0), t0);			\
+	_mm256_storeu_si256((__m256i *)(out+32*1), t1);			\
+	_mm256_storeu_si256((__m256i *)(out+32*2), t2);			\
+	_mm256_storeu_si256((__m256i *)(out+32*3), t3);			\
+	x0 = _mm256_i32gather_epi32((int *)(out+8*0), vindex_read, 4);	\
+	x1 = _mm256_i32gather_epi32((int *)(out+8*1), vindex_read, 4);	\
+	x2 = _mm256_i32gather_epi32((int *)(out+8*2), vindex_read, 4);	\
+	x3 = _mm256_i32gather_epi32((int *)(out+8*3), vindex_read, 4);	\
+	_mm256_storeu_si256((__m256i *)(out+32*0), x0);			\
+	_mm256_storeu_si256((__m256i *)(out+32*1), x1);			\
+	_mm256_storeu_si256((__m256i *)(out+32*2), x2);			\
+	_mm256_storeu_si256((__m256i *)(out+32*3), x3)
+
+# define _mm256_rotl_epi32(a, i)	_mm256_xor_si256(			\
+	_mm256_slli_epi32(a, i), _mm256_srli_epi32(a, 32 - i))
+
+# define INDEX_MASK_TBOX 0xff
+
+# define ROUND_TBOX(x0, x1, x2, x3, x4, i)				\
+	t0 = _mm256_set1_epi32(*(rk + i));				\
+	t1 = _mm256_xor_si256(x1, x2);					\
+	t2 = _mm256_xor_si256(x3, t0);					\
+	x4 = _mm256_xor_si256(t1, t2);					\
+	t0 = _mm256_and_si256(x4, vindex_mask);				\
+	t0 = _mm256_i32gather_epi32((int *)SMS4_T, t0, 4);		\
+	t0 = _mm256_rotl_epi32(t0, 8);					\
+	x4 = _mm256_srli_epi32(x4, 8);					\
+	x0 = _mm256_xor_si256(x0, t0);					\
+	t0 = _mm256_and_si256(x4, vindex_mask);				\
+	t0 = _mm256_i32gather_epi32((int *)SMS4_T, t0, 4);		\
+	t0 = _mm256_rotl_epi32(t0, 16);					\
+	x4 = _mm256_srli_epi32(x4, 8);					\
+	x0 = _mm256_xor_si256(x0, t0);					\
+	t0 = _mm256_and_si256(x4, vindex_mask);				\
+	t0 = _mm256_i32gather_epi32((int *)SMS4_T, t0, 4);		\
+	t0 = _mm256_rotl_epi32(t0, 24);					\
+	x4 = _mm256_srli_epi32(x4, 8);					\
+	x0 = _mm256_xor_si256(x0, t0);					\
+	t1 = _mm256_i32gather_epi32((int *)SMS4_T, x4, 4);		\
+	x4 = _mm256_xor_si256(x0, t1)
+
+# define INDEX_MASK_DBOX 0xffff
+
+# define ROUND_DBOX(x0, x1, x2, x3, x4, i)				\
+	t0 = _mm256_set1_epi32(*(rk + i));				\
+	t1 = _mm256_xor_si256(x1, x2);					\
+	t2 = _mm256_xor_si256(x3, t0);					\
+	x4 = _mm256_xor_si256(t1, t2);					\
+	t0 = _mm256_srli_epi32(x4, 16);					\
+	t1 = _mm256_i32gather_epi32((int *)SMS4_D, t0, 4);		\
+	t2 = _mm256_and_si256(x4, vindex_mask);				\
+	t3 = _mm256_i32gather_epi32((int *)SMS4_D, t2, 4);		\
+	t0 = _mm256_rotl_epi32(t3, 16);					\
+	x4 = _mm256_xor_si256(x0, t1);					\
+	x4 = _mm256_xor_si256(x4, t0)
+
+# define ROUND ROUND_TBOX
+# define INDEX_MASK INDEX_MASK_TBOX
+
+
+void sms4_avx2_ecb_encrypt_blocks(const unsigned char *in, unsigned char *out,
+	size_t blocks, const sms4_key_t *key)
+{
+	const int *rk = (int *)key->rk;
+	__m256i x0, x1, x2, x3, x4;
+	__m256i t0, t1, t2, t3;
+	__m256i vindex_4i = _mm256_setr_epi32(0,4,8,12,16,20,24,28);
+	__m256i vindex_mask = _mm256_set1_epi32(INDEX_MASK);
+	__m256i vindex_read = _mm256_setr_epi32(0,8,16,24,1,9,17,25);
+	__m256i vindex_swap = _mm256_setr_epi8(
+		3,2,1,0,7,6,5,4,11,10,9,8,15,14,13,12,
+		3,2,1,0,7,6,5,4,11,10,9,8,15,14,13,12
+	);
+
+	while (blocks >= 8) {
+		GET_BLKS(x0, x1, x2, x3, in);
+		ROUNDS(x0, x1, x2, x3, x4);
+		PUT_BLKS(out, x0, x4, x3, x2);
+		in += 128;
+		out += 128;
+		blocks -= 8;
+	}
+
+	while (blocks--) {
+		sms4_encrypt(in, out, key);
+		in += 16;
+		out += 16;
+	}
+}
+
+void sms4_avx2_ctr32_encrypt_blocks(const unsigned char *in, unsigned char *out,
+	size_t blocks, const sms4_key_t *key, const unsigned char iv[16])
+{
+	const int *rk = (int *)key->rk;
+	__m256i x0, x1, x2, x3, x4;
+	__m256i t0, t1, t2, t3;
+	__m256i vindex_4i = _mm256_setr_epi32(0,4,8,12,16,20,24,28);
+	__m256i vindex_mask = _mm256_set1_epi32(INDEX_MASK);
+	__m256i vindex_read = _mm256_setr_epi32(0,8,16,24,1,9,17,25);
+	__m256i vindex_swap = _mm256_setr_epi8(
+		3,2,1,0,7,6,5,4,11,10,9,8,15,14,13,12,
+		3,2,1,0,7,6,5,4,11,10,9,8,15,14,13,12
+	);
+	__m256i incr = _mm256_setr_epi32(0, 1, 2, 3, 4, 5, 6, 7);
+	int c0 = (int)GETU32(iv     );
+	int c1 = (int)GETU32(iv +  4);
+	int c2 = (int)GETU32(iv +  8);
+	int c3 = (int)GETU32(iv + 12);
+
+	while (blocks >= 8) {
+		x0 = _mm256_set1_epi32(c0);
+		x1 = _mm256_set1_epi32(c1);
+		x2 = _mm256_set1_epi32(c2);
+		x3 = _mm256_set1_epi32(c3);
+		x3 = _mm256_add_epi32(x3, incr);
+		ROUNDS(x0, x1, x2, x3, x4);
+		GET_BLKS(t0, t1, t2, t3, in);
+		x0 = _mm256_xor_si256(x0, t0);
+		x4 = _mm256_xor_si256(x4, t1);
+		x3 = _mm256_xor_si256(x3, t2);
+		x2 = _mm256_xor_si256(x2, t3);
+		PUT_BLKS(out, x0, x4, x3, x2);
+		c3 += 8;
+		in += 128;
+		out += 128;
+		blocks -= 8;
+	}
+
+	if (blocks) {
+		unsigned char ctr[16];
+		memcpy(ctr, iv, 12);
+		PUTU32(ctr + 12, c3);
+		sms4_ctr32_encrypt_blocks(in, out, blocks, key, ctr);
+	}
+}
+#endif
diff --git a/crypto/sms4/sms4_enc_knc.c b/crypto/sms4/sms4_enc_knc.c
new file mode 100644
index 000000000..7b206a0f2
--- /dev/null
+++ b/crypto/sms4/sms4_enc_knc.c
@@ -0,0 +1,158 @@
+/* ====================================================================
+ * Copyright (c) 2014 - 2016 The GmSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the GmSSL Project.
+ *    (http://gmssl.org/)"
+ *
+ * 4. The name "GmSSL Project" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    guanzhi1980@gmail.com.
+ *
+ * 5. Products derived from this software may not be called "GmSSL"
+ *    nor may "GmSSL" appear in their names without prior written
+ *    permission of the GmSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the GmSSL Project
+ *    (http://gmssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE GmSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE GmSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+
+
+#include 
+#include 
+#include "sms4_lcl.h"
+
+
+static __m512i mask_ff00;
+static __m512i mask_ffff;
+static __m512i mask_ff0000;
+static __m512i vindex_0s;
+static __m512i vindex_4i;
+
+
+void sms4_knc_encrypt_init(sms4_key_t *key)
+{
+	uint64_t value[sizeof(__m512i)/sizeof(uint64_t)];
+	int *p = (int *)value;
+
+	for (i = 0; i < 16; i++)
+		p[i] = 0xff00;
+	mask_ff00 = _mm512_load_epi32(value);
+
+	for (i = 0; i < 16; i++)
+		p[i] = 0xffff;
+	mask_ffff = _mm512_load_epi32(value);
+
+	for (i = 0; i < 16; i++)
+		p[i] = 0xff0000;
+	mask_ff0000 = _mm512_load_epi32(value);
+
+	for (i = 0; i < 16; i++)
+		p[i] = 0;
+	vindex_0s = _mm512_load_epi32(value);
+
+	for (i = 0; i < 16; i++)
+		p[i] = 4 * i;
+	vindex_4i = _mm512_load_epi32(value);
+
+	sms4_init_sbox32();
+}
+
+#define SWAP32(x)						\
+	t0 = _mm512_slli_epi32(x, 24);				\
+	t1 = _mm512_srli_epi32(x, 24);				\
+	t2 = _mm512_or_epi32(t0, t1);				\
+	t0 = _mm512_slli_epi32(x, 8);				\
+	t1 = _mm512_and_epi32(t0, mask_ff0000);			\
+	t0 = _mm512_or_epi32(t2, t1);				\
+	t1 = _mm512_srli_epi32(x, 8);				\
+	t2 = _mm512_and_epi32(t1, mask_ff00);			\
+	x  = _mm512_or_epi32(t0, t2)
+
+#define GET_BLKS(x0, x1, x2, x3, in)				\
+	x0 = _mm512_i32gather_epi32(vindex_4i, in+4*0, 4);	\
+	x1 = _mm512_i32gather_epi32(vindex_4i, in+4*1, 4);	\
+	x2 = _mm512_i32gather_epi32(vindex_4i, in+4*2, 4);	\
+	x3 = _mm512_i32gather_epi32(vindex_4i, in+4*3, 4);	\
+	SWAP32(x0); SWAP32(x1); SWAP32(x2); SWAP32(x3)
+
+#define PUT_BLKS(out, x0, x1, x2, x3)				\
+	SWAP32(x0); SWAP32(x1); SWAP32(x2); SWAP32(x3);		\
+	_mm512_i32scatter_epi32(out+4*0, vindex_4i, x0, 4);	\
+	_mm512_i32scatter_epi32(out+4*1, vindex_4i, x1, 4);	\
+	_mm512_i32scatter_epi32(out+4*2, vindex_4i, x2, 4);	\
+	_mm512_i32scatter_epi32(out+4*3, vindex_4i, x3, 4)
+
+#define S(x0, t0, t1, t2)					\
+	t0 = _mm512_and_epi32(x0, mask_ffff);			\
+	t1 = _mm512_i32gather_epi32(t0, SBOX32L, 4);		\
+	t0 = _mm512_srli_epi32(x0, 16);				\
+	t2 = _mm512_i32gather_epi32(t0, SBOX32H, 4);		\
+	x0 = _mm512_xor_epi32(t1, t2)
+
+#define ROT(r0, x0, i, t0, t1)					\
+	t0 = _mm512_slli_epi32(x0, i);				\
+	t1 = _mm512_srli_epi32(x0, 32-i);			\
+	r0 = _mm512_xor_epi32(t0, t1)
+
+#define L(x0, t0, t1, t2, t3, t4)				\
+	ROT(t0, x0,  2, t2, t3);				\
+	ROT(t1, x0, 10, t2, t3);				\
+	t4 = _mm512_xor_epi32(t0, t1);				\
+	ROT(t0, x0, 18, t2, t3);				\
+	ROT(t1, x0, 24, t2, t3);				\
+	t3 = _mm512_xor_epi32(t0, t1);				\
+	t2 = _mm512_xor_epi32(x0, t3);				\
+	x0 = _mm512_xor_epi32(t2, t4)
+
+#define ROUND(x0, x1, x2, x3, x4, i)				\
+	t0 = _mm512_i32gather_epi32(vindex_0s, rk+i*4, 4);	\
+	t1 = _mm512_xor_epi32(x1, x2);				\
+	t2 = _mm512_xor_epi32(x3, t0);				\
+	t0 = _mm512_xor_epi32(t1, t2);				\
+	S(t0, x4, t1, t2);					\
+	L(t0, x4, t1, t2, t3, t4);				\
+	x4 = _mm512_xor_epi32(x0, t0)
+
+
+void sms4_knc_encrypt_16blocks(sms4_key_t *key, const unsigned char *in, unsigned char *out)
+{
+	int *rk = (int *)key->rk;
+	__m512i x0, x1, x2, x3, x4;
+	__m512i t0, t1, t2, t3, t4;
+
+	GET_BLKS(x0, x1, x2, x3, in);
+	ROUNDS(x0, x1, x2, x3, x4, ROUND);
+	PUT_BLKS(out, x2, x3, x4, x0);
+}
+
diff --git a/crypto/sms4/sms4_lcl.h b/crypto/sms4/sms4_lcl.h
new file mode 100644
index 000000000..4985bcc71
--- /dev/null
+++ b/crypto/sms4/sms4_lcl.h
@@ -0,0 +1,100 @@
+/* ====================================================================
+ * Copyright (c) 2014 - 2019 The GmSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the GmSSL Project.
+ *    (http://gmssl.org/)"
+ *
+ * 4. The name "GmSSL Project" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    guanzhi1980@gmail.com.
+ *
+ * 5. Products derived from this software may not be called "GmSSL"
+ *    nor may "GmSSL" appear in their names without prior written
+ *    permission of the GmSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the GmSSL Project
+ *    (http://gmssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE GmSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE GmSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+
+#ifndef HEADER_SMS4_LCL_H
+#define HEADER_SMS4_LCL_H
+
+#include 
+#include 
+
+extern const uint8_t SMS4_S[256];
+extern const uint32_t SMS4_T[256];
+extern const uint32_t SMS4_D[65536];
+
+#define S32(A)					\
+	((SMS4_S[((A) >> 24)       ] << 24) ^	\
+	 (SMS4_S[((A) >> 16) & 0xff] << 16) ^	\
+	 (SMS4_S[((A) >>  8) & 0xff] <<  8) ^	\
+	 (SMS4_S[((A))       & 0xff]))
+
+#define ROUNDS(x0, x1, x2, x3, x4)		\
+	ROUND(x0, x1, x2, x3, x4, 0);		\
+	ROUND(x1, x2, x3, x4, x0, 1);		\
+	ROUND(x2, x3, x4, x0, x1, 2);		\
+	ROUND(x3, x4, x0, x1, x2, 3);		\
+	ROUND(x4, x0, x1, x2, x3, 4);		\
+	ROUND(x0, x1, x2, x3, x4, 5);		\
+	ROUND(x1, x2, x3, x4, x0, 6);		\
+	ROUND(x2, x3, x4, x0, x1, 7);		\
+	ROUND(x3, x4, x0, x1, x2, 8);		\
+	ROUND(x4, x0, x1, x2, x3, 9);		\
+	ROUND(x0, x1, x2, x3, x4, 10);		\
+	ROUND(x1, x2, x3, x4, x0, 11);		\
+	ROUND(x2, x3, x4, x0, x1, 12);		\
+	ROUND(x3, x4, x0, x1, x2, 13);		\
+	ROUND(x4, x0, x1, x2, x3, 14);		\
+	ROUND(x0, x1, x2, x3, x4, 15);		\
+	ROUND(x1, x2, x3, x4, x0, 16);		\
+	ROUND(x2, x3, x4, x0, x1, 17);		\
+	ROUND(x3, x4, x0, x1, x2, 18);		\
+	ROUND(x4, x0, x1, x2, x3, 19);		\
+	ROUND(x0, x1, x2, x3, x4, 20);		\
+	ROUND(x1, x2, x3, x4, x0, 21);		\
+	ROUND(x2, x3, x4, x0, x1, 22);		\
+	ROUND(x3, x4, x0, x1, x2, 23);		\
+	ROUND(x4, x0, x1, x2, x3, 24);		\
+	ROUND(x0, x1, x2, x3, x4, 25);		\
+	ROUND(x1, x2, x3, x4, x0, 26);		\
+	ROUND(x2, x3, x4, x0, x1, 27);		\
+	ROUND(x3, x4, x0, x1, x2, 28);		\
+	ROUND(x4, x0, x1, x2, x3, 29);		\
+	ROUND(x0, x1, x2, x3, x4, 30);		\
+	ROUND(x1, x2, x3, x4, x0, 31)
+
+#endif
diff --git a/crypto/sms4/sms4_ofb.c b/crypto/sms4/sms4_ofb.c
new file mode 100644
index 000000000..408d0b5a9
--- /dev/null
+++ b/crypto/sms4/sms4_ofb.c
@@ -0,0 +1,58 @@
+/* ====================================================================
+ * Copyright (c) 2014 - 2017 The GmSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the GmSSL Project.
+ *    (http://gmssl.org/)"
+ *
+ * 4. The name "GmSSL Project" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    guanzhi1980@gmail.com.
+ *
+ * 5. Products derived from this software may not be called "GmSSL"
+ *    nor may "GmSSL" appear in their names without prior written
+ *    permission of the GmSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the GmSSL Project
+ *    (http://gmssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE GmSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE GmSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+
+#include 
+#include 
+
+
+void sms4_ofb128_encrypt(const unsigned char *in, unsigned char *out,
+	size_t len, const sms4_key_t *key, unsigned char *iv, int *num)
+{
+	CRYPTO_ofb128_encrypt(in, out, len, key, iv, num, (block128_f)sms4_encrypt);
+}
diff --git a/crypto/sms4/sms4_setkey.c b/crypto/sms4/sms4_setkey.c
new file mode 100644
index 000000000..8dfd64557
--- /dev/null
+++ b/crypto/sms4/sms4_setkey.c
@@ -0,0 +1,119 @@
+/* ====================================================================
+ * Copyright (c) 2014 - 2019 The GmSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the GmSSL Project.
+ *    (http://gmssl.org/)"
+ *
+ * 4. The name "GmSSL Project" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    guanzhi1980@gmail.com.
+ *
+ * 5. Products derived from this software may not be called "GmSSL"
+ *    nor may "GmSSL" appear in their names without prior written
+ *    permission of the GmSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the GmSSL Project
+ *    (http://gmssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE GmSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE GmSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+
+#include 
+#include "internal/rotate.h"
+#include "modes_lcl.h"
+#include "sms4_lcl.h"
+
+static uint32_t FK[4] = {
+	0xa3b1bac6, 0x56aa3350, 0x677d9197, 0xb27022dc,
+};
+
+static uint32_t CK[32] = {
+	0x00070e15, 0x1c232a31, 0x383f464d, 0x545b6269,
+	0x70777e85, 0x8c939aa1, 0xa8afb6bd, 0xc4cbd2d9,
+	0xe0e7eef5, 0xfc030a11, 0x181f262d, 0x343b4249,
+	0x50575e65, 0x6c737a81, 0x888f969d, 0xa4abb2b9,
+	0xc0c7ced5, 0xdce3eaf1, 0xf8ff060d, 0x141b2229,
+	0x30373e45, 0x4c535a61, 0x686f767d, 0x848b9299,
+	0xa0a7aeb5, 0xbcc3cad1, 0xd8dfe6ed, 0xf4fb0209,
+	0x10171e25, 0x2c333a41, 0x484f565d, 0x646b7279,
+};
+
+#define L32_(x)					\
+	((x) ^ 					\
+	ROL32((x), 13) ^			\
+	ROL32((x), 23))
+
+#define ENC_ROUND(x0, x1, x2, x3, x4, i)	\
+	x4 = x1 ^ x2 ^ x3 ^ *(CK + i);		\
+	x4 = S32(x4);				\
+	x4 = x0 ^ L32_(x4);			\
+	*(rk + i) = x4
+
+#define DEC_ROUND(x0, x1, x2, x3, x4, i)	\
+	x4 = x1 ^ x2 ^ x3 ^ *(CK + i);		\
+	x4 = S32(x4);				\
+	x4 = x0 ^ L32_(x4);			\
+	*(rk + 31 - i) = x4
+
+void sms4_set_encrypt_key(sms4_key_t *key, const unsigned char user_key[16])
+{
+	uint32_t *rk = key->rk;
+	uint32_t x0, x1, x2, x3, x4;
+
+	x0 = GETU32(user_key     ) ^ FK[0];
+	x1 = GETU32(user_key  + 4) ^ FK[1];
+	x2 = GETU32(user_key  + 8) ^ FK[2];
+	x3 = GETU32(user_key + 12) ^ FK[3];
+
+#define ROUND ENC_ROUND
+	ROUNDS(x0, x1, x2, x3, x4);
+#undef ROUND
+
+	x0 = x1 = x2 = x3 = x4 = 0;
+}
+
+void sms4_set_decrypt_key(sms4_key_t *key, const unsigned char user_key[16])
+{
+	uint32_t *rk = key->rk;
+	uint32_t x0, x1, x2, x3, x4;
+
+	x0 = GETU32(user_key     ) ^ FK[0];
+	x1 = GETU32(user_key  + 4) ^ FK[1];
+	x2 = GETU32(user_key  + 8) ^ FK[2];
+	x3 = GETU32(user_key + 12) ^ FK[3];
+
+#define ROUND DEC_ROUND
+	ROUNDS(x0, x1, x2, x3, x4);
+#undef ROUND
+
+	x0 = x1 = x2 = x3 = x4 = 0;
+}
diff --git a/crypto/sms4/sms4_wrap.c b/crypto/sms4/sms4_wrap.c
new file mode 100644
index 000000000..95233a578
--- /dev/null
+++ b/crypto/sms4/sms4_wrap.c
@@ -0,0 +1,64 @@
+/* ====================================================================
+ * Copyright (c) 2014 - 2017 The GmSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the GmSSL Project.
+ *    (http://gmssl.org/)"
+ *
+ * 4. The name "GmSSL Project" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    guanzhi1980@gmail.com.
+ *
+ * 5. Products derived from this software may not be called "GmSSL"
+ *    nor may "GmSSL" appear in their names without prior written
+ *    permission of the GmSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the GmSSL Project
+ *    (http://gmssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE GmSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE GmSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+
+#include 
+#include 
+
+
+int sms4_wrap_key(sms4_key_t *key, const unsigned char *iv,
+	unsigned char *out, const unsigned char *in, unsigned int inlen)
+{
+	return CRYPTO_128_wrap(key, iv, out, in, inlen, (block128_f)sms4_encrypt);
+}
+
+int sms4_unwrap_key(sms4_key_t *key, const unsigned char *iv,
+	unsigned char *out, const unsigned char *in, unsigned int inlen)
+{
+	return CRYPTO_128_unwrap(key, iv, out, in, inlen, (block128_f)sms4_encrypt);
+}
diff --git a/crypto/sms4/sms4speed.c b/crypto/sms4/sms4speed.c
new file mode 100644
index 000000000..f5793d2f1
--- /dev/null
+++ b/crypto/sms4/sms4speed.c
@@ -0,0 +1,86 @@
+/* ====================================================================
+ * Copyright (c) 2014 - 2017 The GmSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the GmSSL Project.
+ *    (http://gmssl.org/)"
+ *
+ * 4. The name "GmSSL Project" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    guanzhi1980@gmail.com.
+ *
+ * 5. Products derived from this software may not be called "GmSSL"
+ *    nor may "GmSSL" appear in their names without prior written
+ *    permission of the GmSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the GmSSL Project
+ *    (http://gmssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE GmSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE GmSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+
+int main(int argc, char **argv)
+{
+	sms4_key_t sms4_key;
+	unsigned char user_key[16] = {
+		0x11,  0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88,
+		0x11,  0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88,
+	};
+	size_t buflen = SMS4_BLOCK_SIZE * 8 * 3 * 1000 * 1000;
+	unsigned char *buf = NULL;
+	unsigned char *p;
+	int i;
+
+
+	if (!(buf = (unsigned char *)malloc(buflen))) {
+		fprintf(stderr, "malloc failed\n");
+		return -1;
+	}
+
+	sms4_set_encrypt_key(&sms4_key, user_key);
+
+	#pragma omp parallel for
+	for (i = 0, p = buf; i < buflen/(SMS4_BLOCK_SIZE * 16); i++, p += SMS4_BLOCK_SIZE * 16) {
+		sms4_encrypt_16blocks(&sms4_key, p, p);
+	}
+
+	return 0;
+}
+
diff --git a/crypto/sparc_arch.h b/crypto/sparc_arch.h
new file mode 100644
index 000000000..99eafb331
--- /dev/null
+++ b/crypto/sparc_arch.h
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2012-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#ifndef __SPARC_ARCH_H__
+# define __SPARC_ARCH_H__
+
+# define SPARCV9_TICK_PRIVILEGED (1<<0)
+# define SPARCV9_PREFER_FPU      (1<<1)
+# define SPARCV9_VIS1            (1<<2)
+# define SPARCV9_VIS2            (1<<3)/* reserved */
+# define SPARCV9_FMADD           (1<<4)
+# define SPARCV9_BLK             (1<<5)/* VIS1 block copy */
+# define SPARCV9_VIS3            (1<<6)
+# define SPARCV9_RANDOM          (1<<7)
+# define SPARCV9_64BIT_STACK     (1<<8)
+# define SPARCV9_FJAESX          (1<<9)/* Fujitsu SPARC64 X AES */
+# define SPARCV9_FJDESX          (1<<10)/* Fujitsu SPARC64 X DES, reserved */
+# define SPARCV9_FJHPCACE        (1<<11)/* Fujitsu HPC-ACE, reserved */
+# define SPARCV9_IMA             (1<<13)/* reserved */
+# define SPARCV9_VIS4            (1<<14)/* reserved */
+
+/*
+ * OPENSSL_sparcv9cap_P[1] is copy of Compatibility Feature Register,
+ * %asr26, SPARC-T4 and later. There is no SPARCV9_CFR bit in
+ * OPENSSL_sparcv9cap_P[0], as %cfr copy is sufficient...
+ */
+# define CFR_AES         0x00000001/* Supports AES opcodes */
+# define CFR_DES         0x00000002/* Supports DES opcodes */
+# define CFR_KASUMI      0x00000004/* Supports KASUMI opcodes */
+# define CFR_CAMELLIA    0x00000008/* Supports CAMELLIA opcodes */
+# define CFR_MD5         0x00000010/* Supports MD5 opcodes */
+# define CFR_SHA1        0x00000020/* Supports SHA1 opcodes */
+# define CFR_SHA256      0x00000040/* Supports SHA256 opcodes */
+# define CFR_SHA512      0x00000080/* Supports SHA512 opcodes */
+# define CFR_MPMUL       0x00000100/* Supports MPMUL opcodes */
+# define CFR_MONTMUL     0x00000200/* Supports MONTMUL opcodes */
+# define CFR_MONTSQR     0x00000400/* Supports MONTSQR opcodes */
+# define CFR_CRC32C      0x00000800/* Supports CRC32C opcodes */
+# define CFR_XMPMUL      0x00001000/* Supports XMPMUL opcodes */
+# define CFR_XMONTMUL    0x00002000/* Supports XMONTMUL opcodes */
+# define CFR_XMONTSQR    0x00004000/* Supports XMONTSQR opcodes */
+
+# if defined(OPENSSL_PIC) && !defined(__PIC__)
+#  define __PIC__
+# endif
+
+# if defined(__SUNPRO_C) && defined(__sparcv9) && !defined(__arch64__)
+#  define __arch64__
+# endif
+
+# define SPARC_PIC_THUNK(reg)    \
+        .align  32;             \
+.Lpic_thunk:                    \
+        jmp     %o7 + 8;        \
+         add    %o7, reg, reg;
+
+# define SPARC_PIC_THUNK_CALL(reg)                       \
+        sethi   %hi(_GLOBAL_OFFSET_TABLE_-4), reg;      \
+        call    .Lpic_thunk;                            \
+         or     reg, %lo(_GLOBAL_OFFSET_TABLE_+4), reg;
+
+# if 1
+#  define SPARC_SETUP_GOT_REG(reg)       SPARC_PIC_THUNK_CALL(reg)
+# else
+#  define SPARC_SETUP_GOT_REG(reg)       \
+        sethi   %hi(_GLOBAL_OFFSET_TABLE_-4), reg;      \
+        call    .+8;                                    \
+        or      reg,%lo(_GLOBAL_OFFSET_TABLE_+4), reg;  \
+        add     %o7, reg, reg
+# endif
+
+# if defined(__arch64__)
+
+#  define SPARC_LOAD_ADDRESS(SYM, reg)   \
+        setx    SYM, %o7, reg;
+#  define LDPTR          ldx
+#  define SIZE_T_CC      %xcc
+#  define STACK_FRAME    192
+#  define STACK_BIAS     2047
+#  define STACK_7thARG   (STACK_BIAS+176)
+
+# else
+
+#  define SPARC_LOAD_ADDRESS(SYM, reg)   \
+        set     SYM, reg;
+#  define LDPTR          ld
+#  define SIZE_T_CC      %icc
+#  define STACK_FRAME    112
+#  define STACK_BIAS     0
+#  define STACK_7thARG   92
+#  define SPARC_LOAD_ADDRESS_LEAF(SYM,reg,tmp) SPARC_LOAD_ADDRESS(SYM,reg)
+
+# endif
+
+# ifdef __PIC__
+#  undef SPARC_LOAD_ADDRESS
+#  undef SPARC_LOAD_ADDRESS_LEAF
+#  define SPARC_LOAD_ADDRESS(SYM, reg)   \
+        SPARC_SETUP_GOT_REG(reg);       \
+        sethi   %hi(SYM), %o7;          \
+        or      %o7, %lo(SYM), %o7;     \
+        LDPTR   [reg + %o7], reg;
+# endif
+
+# ifndef SPARC_LOAD_ADDRESS_LEAF
+#  define SPARC_LOAD_ADDRESS_LEAF(SYM, reg, tmp) \
+        mov     %o7, tmp;                       \
+        SPARC_LOAD_ADDRESS(SYM, reg)            \
+        mov     tmp, %o7;
+# endif
+
+#endif                          /* __SPARC_ARCH_H__ */
diff --git a/crypto/sparccpuid.S b/crypto/sparccpuid.S
new file mode 100644
index 000000000..c6ca22473
--- /dev/null
+++ b/crypto/sparccpuid.S
@@ -0,0 +1,582 @@
+! Copyright 2005-2016 The OpenSSL Project Authors. All Rights Reserved.
+!
+! Licensed under the OpenSSL license (the "License").  You may not use
+! this file except in compliance with the License.  You can obtain a copy
+! in the file LICENSE in the source distribution or at
+! https://www.openssl.org/source/license.html
+
+#ifdef OPENSSL_FIPSCANISTER
+#include 
+#endif
+
+#if defined(__SUNPRO_C) && defined(__sparcv9)
+# define ABI64  /* They've said -xarch=v9 at command line */
+#elif defined(__GNUC__) && defined(__arch64__)
+# define ABI64  /* They've said -m64 at command line */
+#endif
+
+#ifdef ABI64
+  .register	%g2,#scratch
+  .register	%g3,#scratch
+# define	FRAME	-192
+# define	BIAS	2047
+#else
+# define	FRAME	-96
+# define	BIAS	0
+#endif
+
+.text
+.align	32
+.global	OPENSSL_wipe_cpu
+.type	OPENSSL_wipe_cpu,#function
+! Keep in mind that this does not excuse us from wiping the stack!
+! This routine wipes registers, but not the backing store [which
+! resides on the stack, toward lower addresses]. To facilitate for
+! stack wiping I return pointer to the top of stack of the *caller*.
+OPENSSL_wipe_cpu:
+	save	%sp,FRAME,%sp
+	nop
+#ifdef __sun
+#include 
+	ta	ST_CLEAN_WINDOWS
+#else
+	call	.walk.reg.wins
+#endif
+	nop
+	call	.PIC.zero.up
+	mov	.zero-(.-4),%o0
+	ld	[%o0],%f0
+	ld	[%o0],%f1
+
+	subcc	%g0,1,%o0
+	! Following is V9 "rd %ccr,%o0" instruction. However! V8
+	! specification says that it ("rd %asr2,%o0" in V8 terms) does
+	! not cause illegal_instruction trap. It therefore can be used
+	! to determine if the CPU the code is executing on is V8- or
+	! V9-compliant, as V9 returns a distinct value of 0x99,
+	! "negative" and "borrow" bits set in both %icc and %xcc.
+	.word	0x91408000	!rd	%ccr,%o0
+	cmp	%o0,0x99
+	bne	.v8
+	nop
+			! Even though we do not use %fp register bank,
+			! we wipe it as memcpy might have used it...
+			.word	0xbfa00040	!fmovd	%f0,%f62
+			.word	0xbba00040	!...
+			.word	0xb7a00040
+			.word	0xb3a00040
+			.word	0xafa00040
+			.word	0xaba00040
+			.word	0xa7a00040
+			.word	0xa3a00040
+			.word	0x9fa00040
+			.word	0x9ba00040
+			.word	0x97a00040
+			.word	0x93a00040
+			.word	0x8fa00040
+			.word	0x8ba00040
+			.word	0x87a00040
+			.word	0x83a00040	!fmovd	%f0,%f32
+.v8:			fmovs	%f1,%f31
+	clr	%o0
+			fmovs	%f0,%f30
+	clr	%o1
+			fmovs	%f1,%f29
+	clr	%o2
+			fmovs	%f0,%f28
+	clr	%o3
+			fmovs	%f1,%f27
+	clr	%o4
+			fmovs	%f0,%f26
+	clr	%o5
+			fmovs	%f1,%f25
+	clr	%o7
+			fmovs	%f0,%f24
+	clr	%l0
+			fmovs	%f1,%f23
+	clr	%l1
+			fmovs	%f0,%f22
+	clr	%l2
+			fmovs	%f1,%f21
+	clr	%l3
+			fmovs	%f0,%f20
+	clr	%l4
+			fmovs	%f1,%f19
+	clr	%l5
+			fmovs	%f0,%f18
+	clr	%l6
+			fmovs	%f1,%f17
+	clr	%l7
+			fmovs	%f0,%f16
+	clr	%i0
+			fmovs	%f1,%f15
+	clr	%i1
+			fmovs	%f0,%f14
+	clr	%i2
+			fmovs	%f1,%f13
+	clr	%i3
+			fmovs	%f0,%f12
+	clr	%i4
+			fmovs	%f1,%f11
+	clr	%i5
+			fmovs	%f0,%f10
+	clr	%g1
+			fmovs	%f1,%f9
+	clr	%g2
+			fmovs	%f0,%f8
+	clr	%g3
+			fmovs	%f1,%f7
+	clr	%g4
+			fmovs	%f0,%f6
+	clr	%g5
+			fmovs	%f1,%f5
+			fmovs	%f0,%f4
+			fmovs	%f1,%f3
+			fmovs	%f0,%f2
+
+	add	%fp,BIAS,%i0	! return pointer to caller´s top of stack
+
+	ret
+	restore
+
+.zero:	.long	0x0,0x0
+.PIC.zero.up:
+	retl
+	add	%o0,%o7,%o0
+#ifdef DEBUG
+.global	walk_reg_wins
+.type	walk_reg_wins,#function
+walk_reg_wins:
+#endif
+.walk.reg.wins:
+	save	%sp,FRAME,%sp
+	cmp	%i7,%o7
+	be	2f
+	clr	%o0
+	cmp	%o7,0	! compiler never cleans %o7...
+	be	1f	! could have been a leaf function...
+	clr	%o1
+	call	.walk.reg.wins
+	nop
+1:	clr	%o2
+	clr	%o3
+	clr	%o4
+	clr	%o5
+	clr	%o7
+	clr	%l0
+	clr	%l1
+	clr	%l2
+	clr	%l3
+	clr	%l4
+	clr	%l5
+	clr	%l6
+	clr	%l7
+	add	%o0,1,%i0	! used for debugging
+2:	ret
+	restore
+.size	OPENSSL_wipe_cpu,.-OPENSSL_wipe_cpu
+
+.global	OPENSSL_atomic_add
+.type	OPENSSL_atomic_add,#function
+.align	32
+OPENSSL_atomic_add:
+#ifndef ABI64
+	subcc	%g0,1,%o2
+	.word	0x95408000	!rd	%ccr,%o2, see comment above
+	cmp	%o2,0x99
+	be	.v9
+	nop
+	save	%sp,FRAME,%sp
+	ba	.enter
+	nop
+#ifdef __sun
+! Note that you do not have to link with libthread to call thr_yield,
+! as libc provides a stub, which is overloaded the moment you link
+! with *either* libpthread or libthread...
+#define	YIELD_CPU	thr_yield
+#else
+! applies at least to Linux and FreeBSD... Feedback expected...
+#define	YIELD_CPU	sched_yield
+#endif
+.spin:	call	YIELD_CPU
+	nop
+.enter:	ld	[%i0],%i2
+	cmp	%i2,-4096
+	be	.spin
+	mov	-1,%i2
+	swap	[%i0],%i2
+	cmp	%i2,-1
+	be	.spin
+	add	%i2,%i1,%i2
+	stbar
+	st	%i2,[%i0]
+	sra	%i2,%g0,%i0
+	ret
+	restore
+.v9:
+#endif
+	ld	[%o0],%o2
+1:	add	%o1,%o2,%o3
+	.word	0xd7e2100a	!cas [%o0],%o2,%o3, compare [%o0] with %o2 and swap %o3
+	cmp	%o2,%o3
+	bne	1b
+	mov	%o3,%o2		! cas is always fetching to dest. register
+	add	%o1,%o2,%o0	! OpenSSL expects the new value
+	retl
+	sra	%o0,%g0,%o0	! we return signed int, remember?
+.size	OPENSSL_atomic_add,.-OPENSSL_atomic_add
+
+.global	_sparcv9_rdtick
+.align	32
+_sparcv9_rdtick:
+	subcc	%g0,1,%o0
+	.word	0x91408000	!rd	%ccr,%o0
+	cmp	%o0,0x99
+	bne	.notick
+	xor	%o0,%o0,%o0
+	.word	0x91410000	!rd	%tick,%o0
+	retl
+	.word	0x93323020	!srlx	%o0,32,%o1
+.notick:
+	retl
+	xor	%o1,%o1,%o1
+.type	_sparcv9_rdtick,#function
+.size	_sparcv9_rdtick,.-_sparcv9_rdtick
+
+.global	_sparcv9_vis1_probe
+.align	8
+_sparcv9_vis1_probe:
+	add	%sp,BIAS+2,%o1
+	.word	0xc19a5a40	!ldda	[%o1]ASI_FP16_P,%f0
+	retl
+	.word	0x81b00d80	!fxor	%f0,%f0,%f0
+.type	_sparcv9_vis1_probe,#function
+.size	_sparcv9_vis1_probe,.-_sparcv9_vis1_probe
+
+! Probe and instrument VIS1 instruction. Output is number of cycles it
+! takes to execute rdtick and pair of VIS1 instructions. US-Tx VIS unit
+! is slow (documented to be 6 cycles on T2) and the core is in-order
+! single-issue, it should be possible to distinguish Tx reliably...
+! Observed return values are:
+!
+!	UltraSPARC IIe		7
+!	UltraSPARC III		7
+!	UltraSPARC T1		24
+!	SPARC T4		65(*)
+!
+! (*)	result has lesser to do with VIS instruction latencies, rdtick
+!	appears that slow, but it does the trick in sense that FP and
+!	VIS code paths are still slower than integer-only ones.
+!
+! Numbers for T2 and SPARC64 V-VII are more than welcomed.
+!
+! It would be possible to detect specifically US-T1 by instrumenting
+! fmul8ulx16, which is emulated on T1 and as such accounts for quite
+! a lot of %tick-s, couple of thousand on Linux...
+.global	_sparcv9_vis1_instrument
+.align	8
+_sparcv9_vis1_instrument:
+	.word	0x81b00d80	!fxor	%f0,%f0,%f0
+	.word	0x85b08d82	!fxor	%f2,%f2,%f2
+	.word	0x91410000	!rd	%tick,%o0
+	.word	0x81b00d80	!fxor	%f0,%f0,%f0
+	.word	0x85b08d82	!fxor	%f2,%f2,%f2
+	.word	0x93410000	!rd	%tick,%o1
+	.word	0x81b00d80	!fxor	%f0,%f0,%f0
+	.word	0x85b08d82	!fxor	%f2,%f2,%f2
+	.word	0x95410000	!rd	%tick,%o2
+	.word	0x81b00d80	!fxor	%f0,%f0,%f0
+	.word	0x85b08d82	!fxor	%f2,%f2,%f2
+	.word	0x97410000	!rd	%tick,%o3
+	.word	0x81b00d80	!fxor	%f0,%f0,%f0
+	.word	0x85b08d82	!fxor	%f2,%f2,%f2
+	.word	0x99410000	!rd	%tick,%o4
+
+	! calculate intervals
+	sub	%o1,%o0,%o0
+	sub	%o2,%o1,%o1
+	sub	%o3,%o2,%o2
+	sub	%o4,%o3,%o3
+
+	! find minimum value
+	cmp	%o0,%o1
+	.word	0x38680002	!bgu,a	%xcc,.+8
+	mov	%o1,%o0
+	cmp	%o0,%o2
+	.word	0x38680002	!bgu,a	%xcc,.+8
+	mov	%o2,%o0
+	cmp	%o0,%o3
+	.word	0x38680002	!bgu,a	%xcc,.+8
+	mov	%o3,%o0
+
+	retl
+	nop
+.type	_sparcv9_vis1_instrument,#function
+.size	_sparcv9_vis1_instrument,.-_sparcv9_vis1_instrument
+
+.global	_sparcv9_vis2_probe
+.align	8
+_sparcv9_vis2_probe:
+	retl
+	.word	0x81b00980	!bshuffle	%f0,%f0,%f0
+.type	_sparcv9_vis2_probe,#function
+.size	_sparcv9_vis2_probe,.-_sparcv9_vis2_probe
+
+.global	_sparcv9_fmadd_probe
+.align	8
+_sparcv9_fmadd_probe:
+	.word	0x81b00d80	!fxor	%f0,%f0,%f0
+	.word	0x85b08d82	!fxor	%f2,%f2,%f2
+	retl
+	.word	0x81b80440	!fmaddd	%f0,%f0,%f2,%f0
+.type	_sparcv9_fmadd_probe,#function
+.size	_sparcv9_fmadd_probe,.-_sparcv9_fmadd_probe
+
+.global	_sparcv9_rdcfr
+.align	8
+_sparcv9_rdcfr:
+	retl
+	.word	0x91468000	!rd	%asr26,%o0
+.type	_sparcv9_rdcfr,#function
+.size	_sparcv9_rdcfr,.-_sparcv9_rdcfr
+
+.global	_sparcv9_vis3_probe
+.align	8
+_sparcv9_vis3_probe:
+	retl
+	.word	0x81b022a0	!xmulx	%g0,%g0,%g0
+.type	_sparcv9_vis3_probe,#function
+.size	_sparcv9_vis3_probe,.-_sparcv9_vis3_probe
+
+.global	_sparcv9_random
+.align	8
+_sparcv9_random:
+	retl
+	.word	0x91b002a0	!random	%o0
+.type	_sparcv9_random,#function
+.size	_sparcv9_random,.-_sparcv9_vis3_probe
+
+.global	_sparcv9_fjaesx_probe
+.align	8
+_sparcv9_fjaesx_probe:
+	.word	0x81b09206	!faesencx %f2,%f6,%f0
+	retl
+	nop
+.size	_sparcv9_fjaesx_probe,.-_sparcv9_fjaesx_probe
+
+.global	OPENSSL_cleanse
+.align	32
+OPENSSL_cleanse:
+	cmp	%o1,14
+	nop
+#ifdef ABI64
+	bgu	%xcc,.Lot
+#else
+	bgu	.Lot
+#endif
+	cmp	%o1,0
+	bne	.Little
+	nop
+	retl
+	nop
+
+.Little:
+	stb	%g0,[%o0]
+	subcc	%o1,1,%o1
+	bnz	.Little
+	add	%o0,1,%o0
+	retl
+	nop
+.align	32
+.Lot:
+#ifndef ABI64
+	subcc	%g0,1,%g1
+	! see above for explanation
+	.word	0x83408000	!rd	%ccr,%g1
+	cmp	%g1,0x99
+	bne	.v8lot
+	nop
+#endif
+
+.v9lot:	andcc	%o0,7,%g0
+	bz	.v9aligned
+	nop
+	stb	%g0,[%o0]
+	sub	%o1,1,%o1
+	ba	.v9lot
+	add	%o0,1,%o0
+.align	16,0x01000000
+.v9aligned:
+	.word	0xc0720000	!stx	%g0,[%o0]
+	sub	%o1,8,%o1
+	andcc	%o1,-8,%g0
+#ifdef ABI64
+	.word	0x126ffffd	!bnz	%xcc,.v9aligned
+#else
+	.word	0x124ffffd	!bnz	%icc,.v9aligned
+#endif
+	add	%o0,8,%o0
+
+	cmp	%o1,0
+	bne	.Little
+	nop
+	retl
+	nop
+#ifndef ABI64
+.v8lot:	andcc	%o0,3,%g0
+	bz	.v8aligned
+	nop
+	stb	%g0,[%o0]
+	sub	%o1,1,%o1
+	ba	.v8lot
+	add	%o0,1,%o0
+	nop
+.v8aligned:
+	st	%g0,[%o0]
+	sub	%o1,4,%o1
+	andcc	%o1,-4,%g0
+	bnz	.v8aligned
+	add	%o0,4,%o0
+
+	cmp	%o1,0
+	bne	.Little
+	nop
+	retl
+	nop
+#endif
+.type	OPENSSL_cleanse,#function
+.size	OPENSSL_cleanse,.-OPENSSL_cleanse
+
+.global	CRYPTO_memcmp
+.align	16
+CRYPTO_memcmp:
+	cmp	%o2,0
+#ifdef ABI64
+	beq,pn	%xcc,.Lno_data
+#else
+	beq	.Lno_data
+#endif
+	xor	%g1,%g1,%g1
+	nop
+
+.Loop_cmp:
+	ldub	[%o0],%o3
+	add	%o0,1,%o0
+	ldub	[%o1],%o4
+	add	%o1,1,%o1
+	subcc	%o2,1,%o2
+	xor	%o3,%o4,%o4
+#ifdef ABI64
+	bnz	%xcc,.Loop_cmp
+#else
+	bnz	.Loop_cmp
+#endif
+	or	%o4,%g1,%g1
+
+	sub	%g0,%g1,%g1
+	srl	%g1,31,%g1
+.Lno_data:
+	retl
+	mov	%g1,%o0
+.type	CRYPTO_memcmp,#function
+.size	CRYPTO_memcmp,.-CRYPTO_memcmp
+
+.global	_sparcv9_vis1_instrument_bus
+.align	8
+_sparcv9_vis1_instrument_bus:
+	mov	%o1,%o3					! save cnt
+	.word	0x99410000	!rd	%tick,%o4	! tick
+	mov	%o4,%o5					! lasttick = tick
+	set	0,%g4					! diff
+
+	andn	%o0,63,%g1
+	.word	0xc1985e00	!ldda	[%g1]0xf0,%f0	! block load
+	.word	0x8143e040	!membar	#Sync
+	.word	0xc1b85c00	!stda	%f0,[%g1]0xe0	! block store and commit
+	.word	0x8143e040	!membar	#Sync
+	ld	[%o0],%o4
+	add	%o4,%g4,%g4
+	.word	0xc9e2100c	!cas	[%o0],%o4,%g4
+
+.Loop:	.word	0x99410000	!rd	%tick,%o4
+	sub	%o4,%o5,%g4				! diff=tick-lasttick
+	mov	%o4,%o5					! lasttick=tick
+
+	andn	%o0,63,%g1
+	.word	0xc1985e00	!ldda	[%g1]0xf0,%f0	! block load
+	.word	0x8143e040	!membar	#Sync
+	.word	0xc1b85c00	!stda	%f0,[%g1]0xe0	! block store and commit
+	.word	0x8143e040	!membar	#Sync
+	ld	[%o0],%o4
+	add	%o4,%g4,%g4
+	.word	0xc9e2100c	!cas	[%o0],%o4,%g4
+	subcc	%o1,1,%o1				! --$cnt
+	bnz	.Loop
+	add	%o0,4,%o0				! ++$out
+
+	retl
+	mov	%o3,%o0
+.type	_sparcv9_vis1_instrument_bus,#function
+.size	_sparcv9_vis1_instrument_bus,.-_sparcv9_vis1_instrument_bus
+
+.global	_sparcv9_vis1_instrument_bus2
+.align	8
+_sparcv9_vis1_instrument_bus2:
+	mov	%o1,%o3					! save cnt
+	sll	%o1,2,%o1				! cnt*=4
+
+	.word	0x99410000	!rd	%tick,%o4	! tick
+	mov	%o4,%o5					! lasttick = tick
+	set	0,%g4					! diff
+
+	andn	%o0,63,%g1
+	.word	0xc1985e00	!ldda	[%g1]0xf0,%f0	! block load
+	.word	0x8143e040	!membar	#Sync
+	.word	0xc1b85c00	!stda	%f0,[%g1]0xe0	! block store and commit
+	.word	0x8143e040	!membar	#Sync
+	ld	[%o0],%o4
+	add	%o4,%g4,%g4
+	.word	0xc9e2100c	!cas	[%o0],%o4,%g4
+
+	.word	0x99410000	!rd	%tick,%o4	! tick
+	sub	%o4,%o5,%g4				! diff=tick-lasttick
+	mov	%o4,%o5					! lasttick=tick
+	mov	%g4,%g5					! lastdiff=diff
+.Loop2:
+	andn	%o0,63,%g1
+	.word	0xc1985e00	!ldda	[%g1]0xf0,%f0	! block load
+	.word	0x8143e040	!membar	#Sync
+	.word	0xc1b85c00	!stda	%f0,[%g1]0xe0	! block store and commit
+	.word	0x8143e040	!membar	#Sync
+	ld	[%o0],%o4
+	add	%o4,%g4,%g4
+	.word	0xc9e2100c	!cas	[%o0],%o4,%g4
+
+	subcc	%o2,1,%o2				! --max
+	bz	.Ldone2
+	nop
+
+	.word	0x99410000	!rd	%tick,%o4	! tick
+	sub	%o4,%o5,%g4				! diff=tick-lasttick
+	mov	%o4,%o5					! lasttick=tick
+	cmp	%g4,%g5
+	mov	%g4,%g5					! lastdiff=diff
+
+	.word	0x83408000	!rd	%ccr,%g1
+	and	%g1,4,%g1				! isolate zero flag
+	xor	%g1,4,%g1				! flip zero flag
+
+	subcc	%o1,%g1,%o1				! conditional --$cnt
+	bnz	.Loop2
+	add	%o0,%g1,%o0				! conditional ++$out
+
+.Ldone2:
+	srl	%o1,2,%o1
+	retl
+	sub	%o3,%o1,%o0
+.type	_sparcv9_vis1_instrument_bus2,#function
+.size	_sparcv9_vis1_instrument_bus2,.-_sparcv9_vis1_instrument_bus2
+
+.section	".init",#alloc,#execinstr
+	call	OPENSSL_cpuid_setup
+	nop
diff --git a/crypto/sparcv9cap.c b/crypto/sparcv9cap.c
new file mode 100644
index 000000000..61d0334ee
--- /dev/null
+++ b/crypto/sparcv9cap.c
@@ -0,0 +1,294 @@
+/*
+ * Copyright 2005-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#include "sparc_arch.h"
+
+#if defined(__GNUC__) && defined(__linux)
+__attribute__ ((visibility("hidden")))
+#endif
+unsigned int OPENSSL_sparcv9cap_P[2] = { SPARCV9_TICK_PRIVILEGED, 0 };
+
+int bn_mul_mont(BN_ULONG *rp, const BN_ULONG *ap, const BN_ULONG *bp,
+                const BN_ULONG *np, const BN_ULONG *n0, int num)
+{
+    int bn_mul_mont_vis3(BN_ULONG *rp, const BN_ULONG *ap, const BN_ULONG *bp,
+                         const BN_ULONG *np, const BN_ULONG *n0, int num);
+    int bn_mul_mont_fpu(BN_ULONG *rp, const BN_ULONG *ap, const BN_ULONG *bp,
+                        const BN_ULONG *np, const BN_ULONG *n0, int num);
+    int bn_mul_mont_int(BN_ULONG *rp, const BN_ULONG *ap, const BN_ULONG *bp,
+                        const BN_ULONG *np, const BN_ULONG *n0, int num);
+
+    if (!(num & 1) && num >= 6) {
+        if ((num & 15) == 0 && num <= 64 &&
+            (OPENSSL_sparcv9cap_P[1] & (CFR_MONTMUL | CFR_MONTSQR)) ==
+            (CFR_MONTMUL | CFR_MONTSQR)) {
+            typedef int (*bn_mul_mont_f) (BN_ULONG *rp, const BN_ULONG *ap,
+                                          const BN_ULONG *bp,
+                                          const BN_ULONG *np,
+                                          const BN_ULONG *n0);
+            int bn_mul_mont_t4_8(BN_ULONG *rp, const BN_ULONG *ap,
+                                 const BN_ULONG *bp, const BN_ULONG *np,
+                                 const BN_ULONG *n0);
+            int bn_mul_mont_t4_16(BN_ULONG *rp, const BN_ULONG *ap,
+                                  const BN_ULONG *bp, const BN_ULONG *np,
+                                  const BN_ULONG *n0);
+            int bn_mul_mont_t4_24(BN_ULONG *rp, const BN_ULONG *ap,
+                                  const BN_ULONG *bp, const BN_ULONG *np,
+                                  const BN_ULONG *n0);
+            int bn_mul_mont_t4_32(BN_ULONG *rp, const BN_ULONG *ap,
+                                  const BN_ULONG *bp, const BN_ULONG *np,
+                                  const BN_ULONG *n0);
+            static const bn_mul_mont_f funcs[4] = {
+                bn_mul_mont_t4_8, bn_mul_mont_t4_16,
+                bn_mul_mont_t4_24, bn_mul_mont_t4_32
+            };
+            bn_mul_mont_f worker = funcs[num / 16 - 1];
+
+            if ((*worker) (rp, ap, bp, np, n0))
+                return 1;
+            /* retry once and fall back */
+            if ((*worker) (rp, ap, bp, np, n0))
+                return 1;
+            return bn_mul_mont_vis3(rp, ap, bp, np, n0, num);
+        }
+        if ((OPENSSL_sparcv9cap_P[0] & SPARCV9_VIS3))
+            return bn_mul_mont_vis3(rp, ap, bp, np, n0, num);
+        else if (num >= 8 &&
+                 /*
+                  * bn_mul_mont_fpu doesn't use FMADD, we just use the
+                  * flag to detect when FPU path is preferable in cases
+                  * when current heuristics is unreliable. [it works
+                  * out because FMADD-capable processors where FPU
+                  * code path is undesirable are also VIS3-capable and
+                  * VIS3 code path takes precedence.]
+                  */
+                 ( (OPENSSL_sparcv9cap_P[0] & SPARCV9_FMADD) ||
+                   (OPENSSL_sparcv9cap_P[0] &
+                    (SPARCV9_PREFER_FPU | SPARCV9_VIS1)) ==
+                   (SPARCV9_PREFER_FPU | SPARCV9_VIS1) ))
+            return bn_mul_mont_fpu(rp, ap, bp, np, n0, num);
+    }
+    return bn_mul_mont_int(rp, ap, bp, np, n0, num);
+}
+
+unsigned long _sparcv9_rdtick(void);
+void _sparcv9_vis1_probe(void);
+unsigned long _sparcv9_vis1_instrument(void);
+void _sparcv9_vis2_probe(void);
+void _sparcv9_fmadd_probe(void);
+unsigned long _sparcv9_rdcfr(void);
+void _sparcv9_vis3_probe(void);
+void _sparcv9_fjaesx_probe(void);
+unsigned long _sparcv9_random(void);
+size_t _sparcv9_vis1_instrument_bus(unsigned int *, size_t);
+size_t _sparcv9_vis1_instrument_bus2(unsigned int *, size_t, size_t);
+
+unsigned long OPENSSL_rdtsc(void)
+{
+    if (OPENSSL_sparcv9cap_P[0] & SPARCV9_TICK_PRIVILEGED)
+#if defined(__sun) && defined(__SVR4)
+        return gethrtime();
+#else
+        return 0;
+#endif
+    else
+        return _sparcv9_rdtick();
+}
+
+size_t OPENSSL_instrument_bus(unsigned int *out, size_t cnt)
+{
+    if ((OPENSSL_sparcv9cap_P[0] & (SPARCV9_TICK_PRIVILEGED | SPARCV9_BLK)) ==
+        SPARCV9_BLK)
+        return _sparcv9_vis1_instrument_bus(out, cnt);
+    else
+        return 0;
+}
+
+size_t OPENSSL_instrument_bus2(unsigned int *out, size_t cnt, size_t max)
+{
+    if ((OPENSSL_sparcv9cap_P[0] & (SPARCV9_TICK_PRIVILEGED | SPARCV9_BLK)) ==
+        SPARCV9_BLK)
+        return _sparcv9_vis1_instrument_bus2(out, cnt, max);
+    else
+        return 0;
+}
+
+static sigjmp_buf common_jmp;
+static void common_handler(int sig)
+{
+    siglongjmp(common_jmp, sig);
+}
+
+#if defined(__sun) && defined(__SVR4)
+# if defined(__GNUC__) && __GNUC__>=2
+extern unsigned int getisax(unsigned int vec[], unsigned int sz) __attribute__ ((weak));
+# elif defined(__SUNPRO_C)
+#pragma weak getisax
+extern unsigned int getisax(unsigned int vec[], unsigned int sz);
+# else
+static unsigned int (*getisax) (unsigned int vec[], unsigned int sz) = NULL;
+# endif
+#endif
+
+void OPENSSL_cpuid_setup(void)
+{
+    char *e;
+    struct sigaction common_act, ill_oact, bus_oact;
+    sigset_t all_masked, oset;
+    static int trigger = 0;
+
+    if (trigger)
+        return;
+    trigger = 1;
+
+    if ((e = getenv("OPENSSL_sparcv9cap"))) {
+        OPENSSL_sparcv9cap_P[0] = strtoul(e, NULL, 0);
+        if ((e = strchr(e, ':')))
+            OPENSSL_sparcv9cap_P[1] = strtoul(e + 1, NULL, 0);
+        return;
+    }
+
+#if defined(__sun) && defined(__SVR4)
+    if (getisax != NULL) {
+        unsigned int vec[2] = { 0, 0 };
+
+        if (getisax (vec,2)) {
+            if (vec[0]&0x00020) OPENSSL_sparcv9cap_P[0] |= SPARCV9_VIS1;
+            if (vec[0]&0x00040) OPENSSL_sparcv9cap_P[0] |= SPARCV9_VIS2;
+            if (vec[0]&0x00080) OPENSSL_sparcv9cap_P[0] |= SPARCV9_BLK;
+            if (vec[0]&0x00100) OPENSSL_sparcv9cap_P[0] |= SPARCV9_FMADD;
+            if (vec[0]&0x00400) OPENSSL_sparcv9cap_P[0] |= SPARCV9_VIS3;
+            if (vec[0]&0x01000) OPENSSL_sparcv9cap_P[0] |= SPARCV9_FJHPCACE;
+            if (vec[0]&0x02000) OPENSSL_sparcv9cap_P[0] |= SPARCV9_FJDESX;
+            if (vec[0]&0x08000) OPENSSL_sparcv9cap_P[0] |= SPARCV9_IMA;
+            if (vec[0]&0x10000) OPENSSL_sparcv9cap_P[0] |= SPARCV9_FJAESX;
+            if (vec[1]&0x00008) OPENSSL_sparcv9cap_P[0] |= SPARCV9_VIS4;
+
+            /* reconstruct %cfr copy */
+            OPENSSL_sparcv9cap_P[1] = (vec[0]>>17)&0x3ff;
+            OPENSSL_sparcv9cap_P[1] |= (OPENSSL_sparcv9cap_P[1]&CFR_MONTMUL)<<1;
+            if (vec[0]&0x20000000) OPENSSL_sparcv9cap_P[1] |= CFR_CRC32C;
+            if (vec[1]&0x00000020) OPENSSL_sparcv9cap_P[1] |= CFR_XMPMUL;
+            if (vec[1]&0x00000040)
+                OPENSSL_sparcv9cap_P[1] |= CFR_XMONTMUL|CFR_XMONTSQR;
+
+            /* Some heuristics */
+            /* all known VIS2-capable CPUs have unprivileged tick counter */
+            if (OPENSSL_sparcv9cap_P[0]&SPARCV9_VIS2)
+                OPENSSL_sparcv9cap_P[0] &= ~SPARCV9_TICK_PRIVILEGED;
+
+            OPENSSL_sparcv9cap_P[0] |= SPARCV9_PREFER_FPU;
+
+            /* detect UltraSPARC-Tx, see sparccpud.S for details... */
+            if ((OPENSSL_sparcv9cap_P[0]&SPARCV9_VIS1) &&
+                _sparcv9_vis1_instrument() >= 12)
+                OPENSSL_sparcv9cap_P[0] &= ~(SPARCV9_VIS1 | SPARCV9_PREFER_FPU);
+        }
+
+        if (sizeof(size_t) == 8)
+            OPENSSL_sparcv9cap_P[0] |= SPARCV9_64BIT_STACK;
+
+        return;
+    }
+#endif
+
+    /* Initial value, fits UltraSPARC-I&II... */
+    OPENSSL_sparcv9cap_P[0] = SPARCV9_PREFER_FPU | SPARCV9_TICK_PRIVILEGED;
+
+    sigfillset(&all_masked);
+    sigdelset(&all_masked, SIGILL);
+    sigdelset(&all_masked, SIGTRAP);
+# ifdef SIGEMT
+    sigdelset(&all_masked, SIGEMT);
+# endif
+    sigdelset(&all_masked, SIGFPE);
+    sigdelset(&all_masked, SIGBUS);
+    sigdelset(&all_masked, SIGSEGV);
+    sigprocmask(SIG_SETMASK, &all_masked, &oset);
+
+    memset(&common_act, 0, sizeof(common_act));
+    common_act.sa_handler = common_handler;
+    common_act.sa_mask = all_masked;
+
+    sigaction(SIGILL, &common_act, &ill_oact);
+    sigaction(SIGBUS, &common_act, &bus_oact); /* T1 fails 16-bit ldda [on
+                                                * Linux] */
+
+    if (sigsetjmp(common_jmp, 1) == 0) {
+        _sparcv9_rdtick();
+        OPENSSL_sparcv9cap_P[0] &= ~SPARCV9_TICK_PRIVILEGED;
+    }
+
+    if (sigsetjmp(common_jmp, 1) == 0) {
+        _sparcv9_vis1_probe();
+        OPENSSL_sparcv9cap_P[0] |= SPARCV9_VIS1 | SPARCV9_BLK;
+        /* detect UltraSPARC-Tx, see sparccpud.S for details... */
+        if (_sparcv9_vis1_instrument() >= 12)
+            OPENSSL_sparcv9cap_P[0] &= ~(SPARCV9_VIS1 | SPARCV9_PREFER_FPU);
+        else {
+            _sparcv9_vis2_probe();
+            OPENSSL_sparcv9cap_P[0] |= SPARCV9_VIS2;
+        }
+    }
+
+    if (sigsetjmp(common_jmp, 1) == 0) {
+        _sparcv9_fmadd_probe();
+        OPENSSL_sparcv9cap_P[0] |= SPARCV9_FMADD;
+    }
+
+    /*
+     * VIS3 flag is tested independently from VIS1, unlike VIS2 that is,
+     * because VIS3 defines even integer instructions.
+     */
+    if (sigsetjmp(common_jmp, 1) == 0) {
+        _sparcv9_vis3_probe();
+        OPENSSL_sparcv9cap_P[0] |= SPARCV9_VIS3;
+    }
+
+    if (sigsetjmp(common_jmp, 1) == 0) {
+        _sparcv9_fjaesx_probe();
+        OPENSSL_sparcv9cap_P[0] |= SPARCV9_FJAESX;
+    }
+
+    /*
+     * In wait for better solution _sparcv9_rdcfr is masked by
+     * VIS3 flag, because it goes to uninterruptable endless
+     * loop on UltraSPARC II running Solaris. Things might be
+     * different on Linux...
+     */
+    if ((OPENSSL_sparcv9cap_P[0] & SPARCV9_VIS3) &&
+        sigsetjmp(common_jmp, 1) == 0) {
+        OPENSSL_sparcv9cap_P[1] = (unsigned int)_sparcv9_rdcfr();
+    }
+
+    sigaction(SIGBUS, &bus_oact, NULL);
+    sigaction(SIGILL, &ill_oact, NULL);
+
+    sigprocmask(SIG_SETMASK, &oset, NULL);
+
+    if (sizeof(size_t) == 8)
+        OPENSSL_sparcv9cap_P[0] |= SPARCV9_64BIT_STACK;
+# ifdef __linux
+    else {
+        int ret = syscall(340);
+
+        if (ret >= 0 && ret & 1)
+            OPENSSL_sparcv9cap_P[0] |= SPARCV9_64BIT_STACK;
+    }
+# endif
+}
diff --git a/crypto/srp/build.info b/crypto/srp/build.info
new file mode 100644
index 000000000..b6c7fe7ad
--- /dev/null
+++ b/crypto/srp/build.info
@@ -0,0 +1,2 @@
+LIBS=../../libcrypto
+SOURCE[../../libcrypto]=srp_lib.c srp_vfy.c
diff --git a/crypto/srp/srp_lib.c b/crypto/srp/srp_lib.c
new file mode 100644
index 000000000..e79352cb2
--- /dev/null
+++ b/crypto/srp/srp_lib.c
@@ -0,0 +1,280 @@
+/*
+ * Copyright 2011-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#ifndef OPENSSL_NO_SRP
+# include "internal/cryptlib.h"
+# include 
+# include 
+# include 
+# include "internal/bn_srp.h"
+
+/* calculate = SHA1(PAD(x) || PAD(y)) */
+
+static BIGNUM *srp_Calc_xy(const BIGNUM *x, const BIGNUM *y, const BIGNUM *N)
+{
+    unsigned char digest[SHA_DIGEST_LENGTH];
+    unsigned char *tmp = NULL;
+    int numN = BN_num_bytes(N);
+    BIGNUM *res = NULL;
+    if (x != N && BN_ucmp(x, N) >= 0)
+        return NULL;
+    if (y != N && BN_ucmp(y, N) >= 0)
+        return NULL;
+    if ((tmp = OPENSSL_malloc(numN * 2)) == NULL)
+        goto err;
+    if (BN_bn2binpad(x, tmp, numN) < 0
+        || BN_bn2binpad(y, tmp + numN, numN) < 0
+        || !EVP_Digest(tmp, numN * 2, digest, NULL, EVP_sha1(), NULL))
+        goto err;
+    res = BN_bin2bn(digest, sizeof(digest), NULL);
+ err:
+    OPENSSL_free(tmp);
+    return res;
+}
+
+static BIGNUM *srp_Calc_k(const BIGNUM *N, const BIGNUM *g)
+{
+    /* k = SHA1(N | PAD(g)) -- tls-srp draft 8 */
+    return srp_Calc_xy(N, g, N);
+}
+
+BIGNUM *SRP_Calc_u(const BIGNUM *A, const BIGNUM *B, const BIGNUM *N)
+{
+    /* k = SHA1(PAD(A) || PAD(B) ) -- tls-srp draft 8 */
+    return srp_Calc_xy(A, B, N);
+}
+
+BIGNUM *SRP_Calc_server_key(const BIGNUM *A, const BIGNUM *v, const BIGNUM *u,
+                            const BIGNUM *b, const BIGNUM *N)
+{
+    BIGNUM *tmp = NULL, *S = NULL;
+    BN_CTX *bn_ctx;
+
+    if (u == NULL || A == NULL || v == NULL || b == NULL || N == NULL)
+        return NULL;
+
+    if ((bn_ctx = BN_CTX_new()) == NULL || (tmp = BN_new()) == NULL)
+        goto err;
+
+    /* S = (A*v**u) ** b */
+
+    if (!BN_mod_exp(tmp, v, u, N, bn_ctx))
+        goto err;
+    if (!BN_mod_mul(tmp, A, tmp, N, bn_ctx))
+        goto err;
+
+    S = BN_new();
+    if (S != NULL && !BN_mod_exp(S, tmp, b, N, bn_ctx)) {
+        BN_free(S);
+        S = NULL;
+    }
+ err:
+    BN_CTX_free(bn_ctx);
+    BN_clear_free(tmp);
+    return S;
+}
+
+BIGNUM *SRP_Calc_B(const BIGNUM *b, const BIGNUM *N, const BIGNUM *g,
+                   const BIGNUM *v)
+{
+    BIGNUM *kv = NULL, *gb = NULL;
+    BIGNUM *B = NULL, *k = NULL;
+    BN_CTX *bn_ctx;
+
+    if (b == NULL || N == NULL || g == NULL || v == NULL ||
+        (bn_ctx = BN_CTX_new()) == NULL)
+        return NULL;
+
+    if ((kv = BN_new()) == NULL ||
+        (gb = BN_new()) == NULL || (B = BN_new()) == NULL)
+        goto err;
+
+    /* B = g**b + k*v */
+
+    if (!BN_mod_exp(gb, g, b, N, bn_ctx)
+        || (k = srp_Calc_k(N, g)) == NULL
+        || !BN_mod_mul(kv, v, k, N, bn_ctx)
+        || !BN_mod_add(B, gb, kv, N, bn_ctx)) {
+        BN_free(B);
+        B = NULL;
+    }
+ err:
+    BN_CTX_free(bn_ctx);
+    BN_clear_free(kv);
+    BN_clear_free(gb);
+    BN_free(k);
+    return B;
+}
+
+BIGNUM *SRP_Calc_x(const BIGNUM *s, const char *user, const char *pass)
+{
+    unsigned char dig[SHA_DIGEST_LENGTH];
+    EVP_MD_CTX *ctxt;
+    unsigned char *cs = NULL;
+    BIGNUM *res = NULL;
+
+    if ((s == NULL) || (user == NULL) || (pass == NULL))
+        return NULL;
+
+    ctxt = EVP_MD_CTX_new();
+    if (ctxt == NULL)
+        return NULL;
+    if ((cs = OPENSSL_malloc(BN_num_bytes(s))) == NULL)
+        goto err;
+
+    if (!EVP_DigestInit_ex(ctxt, EVP_sha1(), NULL)
+        || !EVP_DigestUpdate(ctxt, user, strlen(user))
+        || !EVP_DigestUpdate(ctxt, ":", 1)
+        || !EVP_DigestUpdate(ctxt, pass, strlen(pass))
+        || !EVP_DigestFinal_ex(ctxt, dig, NULL)
+        || !EVP_DigestInit_ex(ctxt, EVP_sha1(), NULL))
+        goto err;
+    BN_bn2bin(s, cs);
+    if (!EVP_DigestUpdate(ctxt, cs, BN_num_bytes(s)))
+        goto err;
+
+    if (!EVP_DigestUpdate(ctxt, dig, sizeof(dig))
+        || !EVP_DigestFinal_ex(ctxt, dig, NULL))
+        goto err;
+
+    res = BN_bin2bn(dig, sizeof(dig), NULL);
+
+ err:
+    OPENSSL_free(cs);
+    EVP_MD_CTX_free(ctxt);
+    return res;
+}
+
+BIGNUM *SRP_Calc_A(const BIGNUM *a, const BIGNUM *N, const BIGNUM *g)
+{
+    BN_CTX *bn_ctx;
+    BIGNUM *A = NULL;
+
+    if (a == NULL || N == NULL || g == NULL || (bn_ctx = BN_CTX_new()) == NULL)
+        return NULL;
+
+    if ((A = BN_new()) != NULL && !BN_mod_exp(A, g, a, N, bn_ctx)) {
+        BN_free(A);
+        A = NULL;
+    }
+    BN_CTX_free(bn_ctx);
+    return A;
+}
+
+BIGNUM *SRP_Calc_client_key(const BIGNUM *N, const BIGNUM *B, const BIGNUM *g,
+                            const BIGNUM *x, const BIGNUM *a, const BIGNUM *u)
+{
+    BIGNUM *tmp = NULL, *tmp2 = NULL, *tmp3 = NULL, *k = NULL, *K = NULL;
+    BN_CTX *bn_ctx;
+
+    if (u == NULL || B == NULL || N == NULL || g == NULL || x == NULL
+        || a == NULL || (bn_ctx = BN_CTX_new()) == NULL)
+        return NULL;
+
+    if ((tmp = BN_new()) == NULL ||
+        (tmp2 = BN_new()) == NULL ||
+        (tmp3 = BN_new()) == NULL)
+        goto err;
+
+    if (!BN_mod_exp(tmp, g, x, N, bn_ctx))
+        goto err;
+    if ((k = srp_Calc_k(N, g)) == NULL)
+        goto err;
+    if (!BN_mod_mul(tmp2, tmp, k, N, bn_ctx))
+        goto err;
+    if (!BN_mod_sub(tmp, B, tmp2, N, bn_ctx))
+        goto err;
+    if (!BN_mul(tmp3, u, x, bn_ctx))
+        goto err;
+    if (!BN_add(tmp2, a, tmp3))
+        goto err;
+    K = BN_new();
+    if (K != NULL && !BN_mod_exp(K, tmp, tmp2, N, bn_ctx)) {
+        BN_free(K);
+        K = NULL;
+    }
+
+ err:
+    BN_CTX_free(bn_ctx);
+    BN_clear_free(tmp);
+    BN_clear_free(tmp2);
+    BN_clear_free(tmp3);
+    BN_free(k);
+    return K;
+}
+
+int SRP_Verify_B_mod_N(const BIGNUM *B, const BIGNUM *N)
+{
+    BIGNUM *r;
+    BN_CTX *bn_ctx;
+    int ret = 0;
+
+    if (B == NULL || N == NULL || (bn_ctx = BN_CTX_new()) == NULL)
+        return 0;
+
+    if ((r = BN_new()) == NULL)
+        goto err;
+    /* Checks if B % N == 0 */
+    if (!BN_nnmod(r, B, N, bn_ctx))
+        goto err;
+    ret = !BN_is_zero(r);
+ err:
+    BN_CTX_free(bn_ctx);
+    BN_free(r);
+    return ret;
+}
+
+int SRP_Verify_A_mod_N(const BIGNUM *A, const BIGNUM *N)
+{
+    /* Checks if A % N == 0 */
+    return SRP_Verify_B_mod_N(A, N);
+}
+
+static SRP_gN knowngN[] = {
+    {"8192", &bn_generator_19, &bn_group_8192},
+    {"6144", &bn_generator_5, &bn_group_6144},
+    {"4096", &bn_generator_5, &bn_group_4096},
+    {"3072", &bn_generator_5, &bn_group_3072},
+    {"2048", &bn_generator_2, &bn_group_2048},
+    {"1536", &bn_generator_2, &bn_group_1536},
+    {"1024", &bn_generator_2, &bn_group_1024},
+};
+
+# define KNOWN_GN_NUMBER sizeof(knowngN) / sizeof(SRP_gN)
+
+/*
+ * Check if G and N are known parameters. The values have been generated
+ * from the ietf-tls-srp draft version 8
+ */
+char *SRP_check_known_gN_param(const BIGNUM *g, const BIGNUM *N)
+{
+    size_t i;
+    if ((g == NULL) || (N == NULL))
+        return 0;
+
+    for (i = 0; i < KNOWN_GN_NUMBER; i++) {
+        if (BN_cmp(knowngN[i].g, g) == 0 && BN_cmp(knowngN[i].N, N) == 0)
+            return knowngN[i].id;
+    }
+    return NULL;
+}
+
+SRP_gN *SRP_get_default_gN(const char *id)
+{
+    size_t i;
+
+    if (id == NULL)
+        return knowngN;
+    for (i = 0; i < KNOWN_GN_NUMBER; i++) {
+        if (strcmp(knowngN[i].id, id) == 0)
+            return knowngN + i;
+    }
+    return NULL;
+}
+#endif
diff --git a/crypto/srp/srp_vfy.c b/crypto/srp/srp_vfy.c
new file mode 100644
index 000000000..188fad27e
--- /dev/null
+++ b/crypto/srp/srp_vfy.c
@@ -0,0 +1,677 @@
+/*
+ * Copyright 2011-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#ifndef OPENSSL_NO_SRP
+# include "internal/cryptlib.h"
+# include 
+# include 
+# include 
+# include 
+# include 
+# include 
+
+# define SRP_RANDOM_SALT_LEN 20
+# define MAX_LEN 2500
+
+static char b64table[] =
+    "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz./";
+
+/*
+ * the following two conversion routines have been inspired by code from
+ * Stanford
+ */
+
+/*
+ * Convert a base64 string into raw byte array representation.
+ */
+static int t_fromb64(unsigned char *a, size_t alen, const char *src)
+{
+    char *loc;
+    int i, j;
+    int size;
+
+    while (*src && (*src == ' ' || *src == '\t' || *src == '\n'))
+        ++src;
+    size = strlen(src);
+    if (alen > INT_MAX || size > (int)alen)
+        return -1;
+
+    i = 0;
+    while (i < size) {
+        loc = strchr(b64table, src[i]);
+        if (loc == (char *)0)
+            break;
+        else
+            a[i] = loc - b64table;
+        ++i;
+    }
+    /* if nothing valid to process we have a zero length response */
+    if (i == 0)
+        return 0;
+    size = i;
+    i = size - 1;
+    j = size;
+    while (1) {
+        a[j] = a[i];
+        if (--i < 0)
+            break;
+        a[j] |= (a[i] & 3) << 6;
+        --j;
+        a[j] = (unsigned char)((a[i] & 0x3c) >> 2);
+        if (--i < 0)
+            break;
+        a[j] |= (a[i] & 0xf) << 4;
+        --j;
+        a[j] = (unsigned char)((a[i] & 0x30) >> 4);
+        if (--i < 0)
+            break;
+        a[j] |= (a[i] << 2);
+
+        a[--j] = 0;
+        if (--i < 0)
+            break;
+    }
+    while (a[j] == 0 && j <= size)
+        ++j;
+    i = 0;
+    while (j <= size)
+        a[i++] = a[j++];
+    return i;
+}
+
+/*
+ * Convert a raw byte string into a null-terminated base64 ASCII string.
+ */
+static char *t_tob64(char *dst, const unsigned char *src, int size)
+{
+    int c, pos = size % 3;
+    unsigned char b0 = 0, b1 = 0, b2 = 0, notleading = 0;
+    char *olddst = dst;
+
+    switch (pos) {
+    case 1:
+        b2 = src[0];
+        break;
+    case 2:
+        b1 = src[0];
+        b2 = src[1];
+        break;
+    }
+
+    while (1) {
+        c = (b0 & 0xfc) >> 2;
+        if (notleading || c != 0) {
+            *dst++ = b64table[c];
+            notleading = 1;
+        }
+        c = ((b0 & 3) << 4) | ((b1 & 0xf0) >> 4);
+        if (notleading || c != 0) {
+            *dst++ = b64table[c];
+            notleading = 1;
+        }
+        c = ((b1 & 0xf) << 2) | ((b2 & 0xc0) >> 6);
+        if (notleading || c != 0) {
+            *dst++ = b64table[c];
+            notleading = 1;
+        }
+        c = b2 & 0x3f;
+        if (notleading || c != 0) {
+            *dst++ = b64table[c];
+            notleading = 1;
+        }
+        if (pos >= size)
+            break;
+        else {
+            b0 = src[pos++];
+            b1 = src[pos++];
+            b2 = src[pos++];
+        }
+    }
+
+    *dst++ = '\0';
+    return olddst;
+}
+
+void SRP_user_pwd_free(SRP_user_pwd *user_pwd)
+{
+    if (user_pwd == NULL)
+        return;
+    BN_free(user_pwd->s);
+    BN_clear_free(user_pwd->v);
+    OPENSSL_free(user_pwd->id);
+    OPENSSL_free(user_pwd->info);
+    OPENSSL_free(user_pwd);
+}
+
+static SRP_user_pwd *SRP_user_pwd_new(void)
+{
+    SRP_user_pwd *ret = OPENSSL_malloc(sizeof(*ret));
+    if (ret == NULL)
+        return NULL;
+    ret->N = NULL;
+    ret->g = NULL;
+    ret->s = NULL;
+    ret->v = NULL;
+    ret->id = NULL;
+    ret->info = NULL;
+    return ret;
+}
+
+static void SRP_user_pwd_set_gN(SRP_user_pwd *vinfo, const BIGNUM *g,
+                                const BIGNUM *N)
+{
+    vinfo->N = N;
+    vinfo->g = g;
+}
+
+static int SRP_user_pwd_set_ids(SRP_user_pwd *vinfo, const char *id,
+                                const char *info)
+{
+    if (id != NULL && NULL == (vinfo->id = OPENSSL_strdup(id)))
+        return 0;
+    return (info == NULL || NULL != (vinfo->info = OPENSSL_strdup(info)));
+}
+
+static int SRP_user_pwd_set_sv(SRP_user_pwd *vinfo, const char *s,
+                               const char *v)
+{
+    unsigned char tmp[MAX_LEN];
+    int len;
+
+    vinfo->v = NULL;
+    vinfo->s = NULL;
+
+    len = t_fromb64(tmp, sizeof(tmp), v);
+    if (len < 0)
+        return 0;
+    if (NULL == (vinfo->v = BN_bin2bn(tmp, len, NULL)))
+        return 0;
+    len = t_fromb64(tmp, sizeof(tmp), s);
+    if (len < 0)
+        goto err;
+    vinfo->s = BN_bin2bn(tmp, len, NULL);
+    if (vinfo->s == NULL)
+        goto err;
+    return 1;
+ err:
+    BN_free(vinfo->v);
+    vinfo->v = NULL;
+    return 0;
+}
+
+static int SRP_user_pwd_set_sv_BN(SRP_user_pwd *vinfo, BIGNUM *s, BIGNUM *v)
+{
+    vinfo->v = v;
+    vinfo->s = s;
+    return (vinfo->s != NULL && vinfo->v != NULL);
+}
+
+static SRP_user_pwd *srp_user_pwd_dup(SRP_user_pwd *src)
+{
+    SRP_user_pwd *ret;
+
+    if (src == NULL)
+        return NULL;
+    if ((ret = SRP_user_pwd_new()) == NULL)
+        return NULL;
+
+    SRP_user_pwd_set_gN(ret, src->g, src->N);
+    if (!SRP_user_pwd_set_ids(ret, src->id, src->info)
+        || !SRP_user_pwd_set_sv_BN(ret, BN_dup(src->s), BN_dup(src->v))) {
+            SRP_user_pwd_free(ret);
+            return NULL;
+    }
+    return ret;
+}
+
+SRP_VBASE *SRP_VBASE_new(char *seed_key)
+{
+    SRP_VBASE *vb = OPENSSL_malloc(sizeof(*vb));
+
+    if (vb == NULL)
+        return NULL;
+    if ((vb->users_pwd = sk_SRP_user_pwd_new_null()) == NULL
+        || (vb->gN_cache = sk_SRP_gN_cache_new_null()) == NULL) {
+        OPENSSL_free(vb);
+        return NULL;
+    }
+    vb->default_g = NULL;
+    vb->default_N = NULL;
+    vb->seed_key = NULL;
+    if ((seed_key != NULL) && (vb->seed_key = OPENSSL_strdup(seed_key)) == NULL) {
+        sk_SRP_user_pwd_free(vb->users_pwd);
+        sk_SRP_gN_cache_free(vb->gN_cache);
+        OPENSSL_free(vb);
+        return NULL;
+    }
+    return vb;
+}
+
+void SRP_VBASE_free(SRP_VBASE *vb)
+{
+    if (!vb)
+        return;
+    sk_SRP_user_pwd_pop_free(vb->users_pwd, SRP_user_pwd_free);
+    sk_SRP_gN_cache_free(vb->gN_cache);
+    OPENSSL_free(vb->seed_key);
+    OPENSSL_free(vb);
+}
+
+static SRP_gN_cache *SRP_gN_new_init(const char *ch)
+{
+    unsigned char tmp[MAX_LEN];
+    int len;
+    SRP_gN_cache *newgN = OPENSSL_malloc(sizeof(*newgN));
+
+    if (newgN == NULL)
+        return NULL;
+
+    len = t_fromb64(tmp, sizeof(tmp), ch);
+    if (len < 0)
+        goto err;
+
+    if ((newgN->b64_bn = OPENSSL_strdup(ch)) == NULL)
+        goto err;
+
+    if ((newgN->bn = BN_bin2bn(tmp, len, NULL)))
+        return newgN;
+
+    OPENSSL_free(newgN->b64_bn);
+ err:
+    OPENSSL_free(newgN);
+    return NULL;
+}
+
+static void SRP_gN_free(SRP_gN_cache *gN_cache)
+{
+    if (gN_cache == NULL)
+        return;
+    OPENSSL_free(gN_cache->b64_bn);
+    BN_free(gN_cache->bn);
+    OPENSSL_free(gN_cache);
+}
+
+static SRP_gN *SRP_get_gN_by_id(const char *id, STACK_OF(SRP_gN) *gN_tab)
+{
+    int i;
+
+    SRP_gN *gN;
+    if (gN_tab != NULL)
+        for (i = 0; i < sk_SRP_gN_num(gN_tab); i++) {
+            gN = sk_SRP_gN_value(gN_tab, i);
+            if (gN && (id == NULL || strcmp(gN->id, id) == 0))
+                return gN;
+        }
+
+    return SRP_get_default_gN(id);
+}
+
+static BIGNUM *SRP_gN_place_bn(STACK_OF(SRP_gN_cache) *gN_cache, char *ch)
+{
+    int i;
+    if (gN_cache == NULL)
+        return NULL;
+
+    /* search if we have already one... */
+    for (i = 0; i < sk_SRP_gN_cache_num(gN_cache); i++) {
+        SRP_gN_cache *cache = sk_SRP_gN_cache_value(gN_cache, i);
+        if (strcmp(cache->b64_bn, ch) == 0)
+            return cache->bn;
+    }
+    {                           /* it is the first time that we find it */
+        SRP_gN_cache *newgN = SRP_gN_new_init(ch);
+        if (newgN) {
+            if (sk_SRP_gN_cache_insert(gN_cache, newgN, 0) > 0)
+                return newgN->bn;
+            SRP_gN_free(newgN);
+        }
+    }
+    return NULL;
+}
+
+/*
+ * this function parses verifier file. Format is:
+ * string(index):base64(N):base64(g):0
+ * string(username):base64(v):base64(salt):int(index)
+ */
+
+int SRP_VBASE_init(SRP_VBASE *vb, char *verifier_file)
+{
+    int error_code;
+    STACK_OF(SRP_gN) *SRP_gN_tab = sk_SRP_gN_new_null();
+    char *last_index = NULL;
+    int i;
+    char **pp;
+
+    SRP_gN *gN = NULL;
+    SRP_user_pwd *user_pwd = NULL;
+
+    TXT_DB *tmpdb = NULL;
+    BIO *in = BIO_new(BIO_s_file());
+
+    error_code = SRP_ERR_OPEN_FILE;
+
+    if (in == NULL || BIO_read_filename(in, verifier_file) <= 0)
+        goto err;
+
+    error_code = SRP_ERR_VBASE_INCOMPLETE_FILE;
+
+    if ((tmpdb = TXT_DB_read(in, DB_NUMBER)) == NULL)
+        goto err;
+
+    error_code = SRP_ERR_MEMORY;
+
+    if (vb->seed_key) {
+        last_index = SRP_get_default_gN(NULL)->id;
+    }
+    for (i = 0; i < sk_OPENSSL_PSTRING_num(tmpdb->data); i++) {
+        pp = sk_OPENSSL_PSTRING_value(tmpdb->data, i);
+        if (pp[DB_srptype][0] == DB_SRP_INDEX) {
+            /*
+             * we add this couple in the internal Stack
+             */
+
+            if ((gN = OPENSSL_malloc(sizeof(*gN))) == NULL)
+                goto err;
+
+            if ((gN->id = OPENSSL_strdup(pp[DB_srpid])) == NULL
+                || (gN->N = SRP_gN_place_bn(vb->gN_cache, pp[DB_srpverifier]))
+                        == NULL
+                || (gN->g = SRP_gN_place_bn(vb->gN_cache, pp[DB_srpsalt]))
+                        == NULL
+                || sk_SRP_gN_insert(SRP_gN_tab, gN, 0) == 0)
+                goto err;
+
+            gN = NULL;
+
+            if (vb->seed_key != NULL) {
+                last_index = pp[DB_srpid];
+            }
+        } else if (pp[DB_srptype][0] == DB_SRP_VALID) {
+            /* it is a user .... */
+            const SRP_gN *lgN;
+
+            if ((lgN = SRP_get_gN_by_id(pp[DB_srpgN], SRP_gN_tab)) != NULL) {
+                error_code = SRP_ERR_MEMORY;
+                if ((user_pwd = SRP_user_pwd_new()) == NULL)
+                    goto err;
+
+                SRP_user_pwd_set_gN(user_pwd, lgN->g, lgN->N);
+                if (!SRP_user_pwd_set_ids
+                    (user_pwd, pp[DB_srpid], pp[DB_srpinfo]))
+                    goto err;
+
+                error_code = SRP_ERR_VBASE_BN_LIB;
+                if (!SRP_user_pwd_set_sv
+                    (user_pwd, pp[DB_srpsalt], pp[DB_srpverifier]))
+                    goto err;
+
+                if (sk_SRP_user_pwd_insert(vb->users_pwd, user_pwd, 0) == 0)
+                    goto err;
+                user_pwd = NULL; /* abandon responsibility */
+            }
+        }
+    }
+
+    if (last_index != NULL) {
+        /* this means that we want to simulate a default user */
+
+        if (((gN = SRP_get_gN_by_id(last_index, SRP_gN_tab)) == NULL)) {
+            error_code = SRP_ERR_VBASE_BN_LIB;
+            goto err;
+        }
+        vb->default_g = gN->g;
+        vb->default_N = gN->N;
+        gN = NULL;
+    }
+    error_code = SRP_NO_ERROR;
+
+ err:
+    /*
+     * there may be still some leaks to fix, if this fails, the application
+     * terminates most likely
+     */
+
+    if (gN != NULL) {
+        OPENSSL_free(gN->id);
+        OPENSSL_free(gN);
+    }
+
+    SRP_user_pwd_free(user_pwd);
+
+    TXT_DB_free(tmpdb);
+    BIO_free_all(in);
+
+    sk_SRP_gN_free(SRP_gN_tab);
+
+    return error_code;
+
+}
+
+static SRP_user_pwd *find_user(SRP_VBASE *vb, char *username)
+{
+    int i;
+    SRP_user_pwd *user;
+
+    if (vb == NULL)
+        return NULL;
+
+    for (i = 0; i < sk_SRP_user_pwd_num(vb->users_pwd); i++) {
+        user = sk_SRP_user_pwd_value(vb->users_pwd, i);
+        if (strcmp(user->id, username) == 0)
+            return user;
+    }
+
+    return NULL;
+}
+
+ #if OPENSSL_API_COMPAT < 0x10100000L
+/*
+ * DEPRECATED: use SRP_VBASE_get1_by_user instead.
+ * This method ignores the configured seed and fails for an unknown user.
+ * Ownership of the returned pointer is not released to the caller.
+ * In other words, caller must not free the result.
+ */
+SRP_user_pwd *SRP_VBASE_get_by_user(SRP_VBASE *vb, char *username)
+{
+    return find_user(vb, username);
+}
+#endif
+
+/*
+ * Ownership of the returned pointer is released to the caller.
+ * In other words, caller must free the result once done.
+ */
+SRP_user_pwd *SRP_VBASE_get1_by_user(SRP_VBASE *vb, char *username)
+{
+    SRP_user_pwd *user;
+    unsigned char digv[SHA_DIGEST_LENGTH];
+    unsigned char digs[SHA_DIGEST_LENGTH];
+    EVP_MD_CTX *ctxt = NULL;
+
+    if (vb == NULL)
+        return NULL;
+
+    if ((user = find_user(vb, username)) != NULL)
+        return srp_user_pwd_dup(user);
+
+    if ((vb->seed_key == NULL) ||
+        (vb->default_g == NULL) || (vb->default_N == NULL))
+        return NULL;
+
+/* if the user is unknown we set parameters as well if we have a seed_key */
+
+    if ((user = SRP_user_pwd_new()) == NULL)
+        return NULL;
+
+    SRP_user_pwd_set_gN(user, vb->default_g, vb->default_N);
+
+    if (!SRP_user_pwd_set_ids(user, username, NULL))
+        goto err;
+
+    if (RAND_bytes(digv, SHA_DIGEST_LENGTH) <= 0)
+        goto err;
+    ctxt = EVP_MD_CTX_new();
+    if (ctxt == NULL
+        || !EVP_DigestInit_ex(ctxt, EVP_sha1(), NULL)
+        || !EVP_DigestUpdate(ctxt, vb->seed_key, strlen(vb->seed_key))
+        || !EVP_DigestUpdate(ctxt, username, strlen(username))
+        || !EVP_DigestFinal_ex(ctxt, digs, NULL))
+        goto err;
+    EVP_MD_CTX_free(ctxt);
+    ctxt = NULL;
+    if (SRP_user_pwd_set_sv_BN(user,
+                               BN_bin2bn(digs, SHA_DIGEST_LENGTH, NULL),
+                               BN_bin2bn(digv, SHA_DIGEST_LENGTH, NULL)))
+        return user;
+
+ err:
+    EVP_MD_CTX_free(ctxt);
+    SRP_user_pwd_free(user);
+    return NULL;
+}
+
+/*
+ * create a verifier (*salt,*verifier,g and N are in base64)
+ */
+char *SRP_create_verifier(const char *user, const char *pass, char **salt,
+                          char **verifier, const char *N, const char *g)
+{
+    int len;
+    char *result = NULL, *vf = NULL;
+    const BIGNUM *N_bn = NULL, *g_bn = NULL;
+    BIGNUM *N_bn_alloc = NULL, *g_bn_alloc = NULL, *s = NULL, *v = NULL;
+    unsigned char tmp[MAX_LEN];
+    unsigned char tmp2[MAX_LEN];
+    char *defgNid = NULL;
+    int vfsize = 0;
+
+    if ((user == NULL) ||
+        (pass == NULL) || (salt == NULL) || (verifier == NULL))
+        goto err;
+
+    if (N) {
+        if ((len = t_fromb64(tmp, sizeof(tmp), N)) <= 0)
+            goto err;
+        N_bn_alloc = BN_bin2bn(tmp, len, NULL);
+        N_bn = N_bn_alloc;
+        if ((len = t_fromb64(tmp, sizeof(tmp) ,g)) <= 0)
+            goto err;
+        g_bn_alloc = BN_bin2bn(tmp, len, NULL);
+        g_bn = g_bn_alloc;
+        defgNid = "*";
+    } else {
+        SRP_gN *gN = SRP_get_gN_by_id(g, NULL);
+        if (gN == NULL)
+            goto err;
+        N_bn = gN->N;
+        g_bn = gN->g;
+        defgNid = gN->id;
+    }
+
+    if (*salt == NULL) {
+        if (RAND_bytes(tmp2, SRP_RANDOM_SALT_LEN) <= 0)
+            goto err;
+
+        s = BN_bin2bn(tmp2, SRP_RANDOM_SALT_LEN, NULL);
+    } else {
+        if ((len = t_fromb64(tmp2, sizeof(tmp2), *salt)) <= 0)
+            goto err;
+        s = BN_bin2bn(tmp2, len, NULL);
+    }
+
+    if (!SRP_create_verifier_BN(user, pass, &s, &v, N_bn, g_bn))
+        goto err;
+
+    BN_bn2bin(v, tmp);
+    vfsize = BN_num_bytes(v) * 2;
+    if (((vf = OPENSSL_malloc(vfsize)) == NULL))
+        goto err;
+    t_tob64(vf, tmp, BN_num_bytes(v));
+
+    if (*salt == NULL) {
+        char *tmp_salt;
+
+        if ((tmp_salt = OPENSSL_malloc(SRP_RANDOM_SALT_LEN * 2)) == NULL) {
+            goto err;
+        }
+        t_tob64(tmp_salt, tmp2, SRP_RANDOM_SALT_LEN);
+        *salt = tmp_salt;
+    }
+
+    *verifier = vf;
+    vf = NULL;
+    result = defgNid;
+
+ err:
+    BN_free(N_bn_alloc);
+    BN_free(g_bn_alloc);
+    OPENSSL_clear_free(vf, vfsize);
+    BN_clear_free(s);
+    BN_clear_free(v);
+    return result;
+}
+
+/*
+ * create a verifier (*salt,*verifier,g and N are BIGNUMs). If *salt != NULL
+ * then the provided salt will be used. On successful exit *verifier will point
+ * to a newly allocated BIGNUM containing the verifier and (if a salt was not
+ * provided) *salt will be populated with a newly allocated BIGNUM containing a
+ * random salt.
+ * The caller is responsible for freeing the allocated *salt and *verifier
+ * BIGNUMS.
+ */
+int SRP_create_verifier_BN(const char *user, const char *pass, BIGNUM **salt,
+                           BIGNUM **verifier, const BIGNUM *N,
+                           const BIGNUM *g)
+{
+    int result = 0;
+    BIGNUM *x = NULL;
+    BN_CTX *bn_ctx = BN_CTX_new();
+    unsigned char tmp2[MAX_LEN];
+    BIGNUM *salttmp = NULL;
+
+    if ((user == NULL) ||
+        (pass == NULL) ||
+        (salt == NULL) ||
+        (verifier == NULL) || (N == NULL) || (g == NULL) || (bn_ctx == NULL))
+        goto err;
+
+    if (*salt == NULL) {
+        if (RAND_bytes(tmp2, SRP_RANDOM_SALT_LEN) <= 0)
+            goto err;
+
+        salttmp = BN_bin2bn(tmp2, SRP_RANDOM_SALT_LEN, NULL);
+    } else {
+        salttmp = *salt;
+    }
+
+    x = SRP_Calc_x(salttmp, user, pass);
+
+    *verifier = BN_new();
+    if (*verifier == NULL)
+        goto err;
+
+    if (!BN_mod_exp(*verifier, g, x, N, bn_ctx)) {
+        BN_clear_free(*verifier);
+        goto err;
+    }
+
+    result = 1;
+    *salt = salttmp;
+
+ err:
+    if (salt != NULL && *salt != salttmp)
+        BN_clear_free(salttmp);
+    BN_clear_free(x);
+    BN_CTX_free(bn_ctx);
+    return result;
+}
+
+#endif
diff --git a/crypto/stack/build.info b/crypto/stack/build.info
new file mode 100644
index 000000000..e5870210a
--- /dev/null
+++ b/crypto/stack/build.info
@@ -0,0 +1,2 @@
+LIBS=../../libcrypto
+SOURCE[../../libcrypto]=stack.c
diff --git a/crypto/stack/stack.c b/crypto/stack/stack.c
new file mode 100644
index 000000000..43ddf30ac
--- /dev/null
+++ b/crypto/stack/stack.c
@@ -0,0 +1,312 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include "internal/numbers.h"
+#include 
+#include 
+
+struct stack_st {
+    int num;
+    const char **data;
+    int sorted;
+    size_t num_alloc;
+    OPENSSL_sk_compfunc comp;
+};
+
+#undef MIN_NODES
+#define MIN_NODES       4
+
+#include 
+
+OPENSSL_sk_compfunc OPENSSL_sk_set_cmp_func(OPENSSL_STACK *sk, OPENSSL_sk_compfunc c)
+{
+    OPENSSL_sk_compfunc old = sk->comp;
+
+    if (sk->comp != c)
+        sk->sorted = 0;
+    sk->comp = c;
+
+    return old;
+}
+
+OPENSSL_STACK *OPENSSL_sk_dup(const OPENSSL_STACK *sk)
+{
+    OPENSSL_STACK *ret;
+
+    if (sk->num < 0)
+        return NULL;
+
+    if ((ret = OPENSSL_malloc(sizeof(*ret))) == NULL)
+        return NULL;
+
+    /* direct structure assignment */
+    *ret = *sk;
+
+    if ((ret->data = OPENSSL_malloc(sizeof(*ret->data) * sk->num_alloc)) == NULL)
+        goto err;
+    memcpy(ret->data, sk->data, sizeof(char *) * sk->num);
+    return ret;
+ err:
+    OPENSSL_sk_free(ret);
+    return NULL;
+}
+
+OPENSSL_STACK *OPENSSL_sk_deep_copy(const OPENSSL_STACK *sk,
+                             OPENSSL_sk_copyfunc copy_func,
+                             OPENSSL_sk_freefunc free_func)
+{
+    OPENSSL_STACK *ret;
+    int i;
+
+    if (sk->num < 0)
+        return NULL;
+
+    if ((ret = OPENSSL_malloc(sizeof(*ret))) == NULL)
+        return NULL;
+
+    /* direct structure assignment */
+    *ret = *sk;
+
+    ret->num_alloc = sk->num > MIN_NODES ? (size_t)sk->num : MIN_NODES;
+    ret->data = OPENSSL_zalloc(sizeof(*ret->data) * ret->num_alloc);
+    if (ret->data == NULL) {
+        OPENSSL_free(ret);
+        return NULL;
+    }
+
+    for (i = 0; i < ret->num; ++i) {
+        if (sk->data[i] == NULL)
+            continue;
+        if ((ret->data[i] = copy_func(sk->data[i])) == NULL) {
+            while (--i >= 0)
+                if (ret->data[i] != NULL)
+                    free_func((void *)ret->data[i]);
+            OPENSSL_sk_free(ret);
+            return NULL;
+        }
+    }
+    return ret;
+}
+
+OPENSSL_STACK *OPENSSL_sk_new_null(void)
+{
+    return OPENSSL_sk_new((OPENSSL_sk_compfunc)NULL);
+}
+
+OPENSSL_STACK *OPENSSL_sk_new(OPENSSL_sk_compfunc c)
+{
+    OPENSSL_STACK *ret;
+
+    if ((ret = OPENSSL_zalloc(sizeof(*ret))) == NULL)
+        goto err;
+    if ((ret->data = OPENSSL_zalloc(sizeof(*ret->data) * MIN_NODES)) == NULL)
+        goto err;
+    ret->comp = c;
+    ret->num_alloc = MIN_NODES;
+    return (ret);
+
+ err:
+    OPENSSL_free(ret);
+    return (NULL);
+}
+
+int OPENSSL_sk_insert(OPENSSL_STACK *st, const void *data, int loc)
+{
+    if (st == NULL || st->num < 0 || st->num == INT_MAX) {
+        return 0;
+    }
+
+    if (st->num_alloc <= (size_t)(st->num + 1)) {
+        size_t doub_num_alloc = st->num_alloc * 2;
+        const char **tmpdata;
+
+        /* Overflow checks */
+        if (doub_num_alloc < st->num_alloc)
+            return 0;
+
+        /* Avoid overflow due to multiplication by sizeof(char *) */
+        if (doub_num_alloc > SIZE_MAX / sizeof(char *))
+            return 0;
+
+        tmpdata = OPENSSL_realloc((char *)st->data,
+                                  sizeof(char *) * doub_num_alloc);
+        if (tmpdata == NULL)
+            return 0;
+
+        st->data = tmpdata;
+        st->num_alloc = doub_num_alloc;
+    }
+    if ((loc >= st->num) || (loc < 0)) {
+        st->data[st->num] = data;
+    } else {
+        memmove(&st->data[loc + 1], &st->data[loc],
+                sizeof(st->data[0]) * (st->num - loc));
+        st->data[loc] = data;
+    }
+    st->num++;
+    st->sorted = 0;
+    return st->num;
+}
+
+void *OPENSSL_sk_delete_ptr(OPENSSL_STACK *st, const void *p)
+{
+    int i;
+
+    for (i = 0; i < st->num; i++)
+        if (st->data[i] == p)
+            return OPENSSL_sk_delete(st, i);
+    return NULL;
+}
+
+void *OPENSSL_sk_delete(OPENSSL_STACK *st, int loc)
+{
+    const char *ret;
+
+    if (st == NULL || loc < 0 || loc >= st->num)
+        return NULL;
+
+    ret = st->data[loc];
+    if (loc != st->num - 1)
+         memmove(&st->data[loc], &st->data[loc + 1],
+                 sizeof(st->data[0]) * (st->num - loc - 1));
+    st->num--;
+    return (void *)ret;
+}
+
+static int internal_find(OPENSSL_STACK *st, const void *data,
+                         int ret_val_options)
+{
+    const void *r;
+    int i;
+
+    if (st == NULL)
+        return -1;
+
+    if (st->comp == NULL) {
+        for (i = 0; i < st->num; i++)
+            if (st->data[i] == data)
+                return (i);
+        return (-1);
+    }
+    OPENSSL_sk_sort(st);
+    if (data == NULL)
+        return (-1);
+    r = OBJ_bsearch_ex_(&data, st->data, st->num, sizeof(void *), st->comp,
+                        ret_val_options);
+    if (r == NULL)
+        return (-1);
+    return (int)((const char **)r - st->data);
+}
+
+int OPENSSL_sk_find(OPENSSL_STACK *st, const void *data)
+{
+    return internal_find(st, data, OBJ_BSEARCH_FIRST_VALUE_ON_MATCH);
+}
+
+int OPENSSL_sk_find_ex(OPENSSL_STACK *st, const void *data)
+{
+    return internal_find(st, data, OBJ_BSEARCH_VALUE_ON_NOMATCH);
+}
+
+int OPENSSL_sk_push(OPENSSL_STACK *st, const void *data)
+{
+    return (OPENSSL_sk_insert(st, data, st->num));
+}
+
+int OPENSSL_sk_unshift(OPENSSL_STACK *st, const void *data)
+{
+    return (OPENSSL_sk_insert(st, data, 0));
+}
+
+void *OPENSSL_sk_shift(OPENSSL_STACK *st)
+{
+    if (st == NULL)
+        return (NULL);
+    if (st->num <= 0)
+        return (NULL);
+    return (OPENSSL_sk_delete(st, 0));
+}
+
+void *OPENSSL_sk_pop(OPENSSL_STACK *st)
+{
+    if (st == NULL)
+        return (NULL);
+    if (st->num <= 0)
+        return (NULL);
+    return (OPENSSL_sk_delete(st, st->num - 1));
+}
+
+void OPENSSL_sk_zero(OPENSSL_STACK *st)
+{
+    if (st == NULL)
+        return;
+    if (st->num <= 0)
+        return;
+    memset(st->data, 0, sizeof(*st->data) * st->num);
+    st->num = 0;
+}
+
+void OPENSSL_sk_pop_free(OPENSSL_STACK *st, OPENSSL_sk_freefunc func)
+{
+    int i;
+
+    if (st == NULL)
+        return;
+    for (i = 0; i < st->num; i++)
+        if (st->data[i] != NULL)
+            func((char *)st->data[i]);
+    OPENSSL_sk_free(st);
+}
+
+void OPENSSL_sk_free(OPENSSL_STACK *st)
+{
+    if (st == NULL)
+        return;
+    OPENSSL_free(st->data);
+    OPENSSL_free(st);
+}
+
+int OPENSSL_sk_num(const OPENSSL_STACK *st)
+{
+    if (st == NULL)
+        return -1;
+    return st->num;
+}
+
+void *OPENSSL_sk_value(const OPENSSL_STACK *st, int i)
+{
+    if (st == NULL || i < 0 || i >= st->num)
+        return NULL;
+    return (void *)st->data[i];
+}
+
+void *OPENSSL_sk_set(OPENSSL_STACK *st, int i, const void *data)
+{
+    if (st == NULL || i < 0 || i >= st->num)
+        return NULL;
+    st->data[i] = data;
+    return (void *)st->data[i];
+}
+
+void OPENSSL_sk_sort(OPENSSL_STACK *st)
+{
+    if (st && !st->sorted && st->comp != NULL) {
+        qsort(st->data, st->num, sizeof(char *), st->comp);
+        st->sorted = 1;
+    }
+}
+
+int OPENSSL_sk_is_sorted(const OPENSSL_STACK *st)
+{
+    if (st == NULL)
+        return 1;
+    return st->sorted;
+}
diff --git a/crypto/threads_none.c b/crypto/threads_none.c
new file mode 100644
index 000000000..72bf25b0d
--- /dev/null
+++ b/crypto/threads_none.c
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+
+#if !defined(OPENSSL_THREADS) || defined(CRYPTO_TDEBUG)
+
+CRYPTO_RWLOCK *CRYPTO_THREAD_lock_new(void)
+{
+    CRYPTO_RWLOCK *lock = OPENSSL_zalloc(sizeof(unsigned int));
+    if (lock == NULL)
+        return NULL;
+
+    *(unsigned int *)lock = 1;
+
+    return lock;
+}
+
+int CRYPTO_THREAD_read_lock(CRYPTO_RWLOCK *lock)
+{
+    OPENSSL_assert(*(unsigned int *)lock == 1);
+    return 1;
+}
+
+int CRYPTO_THREAD_write_lock(CRYPTO_RWLOCK *lock)
+{
+    OPENSSL_assert(*(unsigned int *)lock == 1);
+    return 1;
+}
+
+int CRYPTO_THREAD_unlock(CRYPTO_RWLOCK *lock)
+{
+    OPENSSL_assert(*(unsigned int *)lock == 1);
+    return 1;
+}
+
+void CRYPTO_THREAD_lock_free(CRYPTO_RWLOCK *lock) {
+    if (lock == NULL)
+        return;
+
+    *(unsigned int *)lock = 0;
+    OPENSSL_free(lock);
+
+    return;
+}
+
+int CRYPTO_THREAD_run_once(CRYPTO_ONCE *once, void (*init)(void))
+{
+    if (*once != 0)
+        return 1;
+
+    init();
+    *once = 1;
+
+    return 1;
+}
+
+#define OPENSSL_CRYPTO_THREAD_LOCAL_KEY_MAX 256
+
+static void *thread_local_storage[OPENSSL_CRYPTO_THREAD_LOCAL_KEY_MAX];
+
+int CRYPTO_THREAD_init_local(CRYPTO_THREAD_LOCAL *key, void (*cleanup)(void *))
+{
+    static unsigned int thread_local_key = 0;
+
+    if (thread_local_key >= OPENSSL_CRYPTO_THREAD_LOCAL_KEY_MAX)
+        return 0;
+
+    *key = thread_local_key++;
+
+    thread_local_storage[*key] = NULL;
+
+    return 1;
+}
+
+void *CRYPTO_THREAD_get_local(CRYPTO_THREAD_LOCAL *key)
+{
+    if (*key >= OPENSSL_CRYPTO_THREAD_LOCAL_KEY_MAX)
+        return NULL;
+
+    return thread_local_storage[*key];
+}
+
+int CRYPTO_THREAD_set_local(CRYPTO_THREAD_LOCAL *key, void *val)
+{
+    if (*key >= OPENSSL_CRYPTO_THREAD_LOCAL_KEY_MAX)
+        return 0;
+
+    thread_local_storage[*key] = val;
+
+    return 1;
+}
+
+int CRYPTO_THREAD_cleanup_local(CRYPTO_THREAD_LOCAL *key)
+{
+    *key = OPENSSL_CRYPTO_THREAD_LOCAL_KEY_MAX + 1;
+    return 1;
+}
+
+CRYPTO_THREAD_ID CRYPTO_THREAD_get_current_id(void)
+{
+    return 0;
+}
+
+int CRYPTO_THREAD_compare_id(CRYPTO_THREAD_ID a, CRYPTO_THREAD_ID b)
+{
+    return (a == b);
+}
+
+int CRYPTO_atomic_add(int *val, int amount, int *ret, CRYPTO_RWLOCK *lock)
+{
+    *val += amount;
+    *ret  = *val;
+
+    return 1;
+}
+
+#endif
diff --git a/crypto/threads_pthread.c b/crypto/threads_pthread.c
new file mode 100644
index 000000000..5cc48afb1
--- /dev/null
+++ b/crypto/threads_pthread.c
@@ -0,0 +1,130 @@
+/*
+ * Copyright 2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+
+#if defined(OPENSSL_THREADS) && !defined(CRYPTO_TDEBUG) && !defined(OPENSSL_SYS_WINDOWS)
+
+CRYPTO_RWLOCK *CRYPTO_THREAD_lock_new(void)
+{
+    CRYPTO_RWLOCK *lock = OPENSSL_zalloc(sizeof(pthread_rwlock_t));
+    if (lock == NULL)
+        return NULL;
+
+    if (pthread_rwlock_init(lock, NULL) != 0) {
+        OPENSSL_free(lock);
+        return NULL;
+    }
+
+    return lock;
+}
+
+int CRYPTO_THREAD_read_lock(CRYPTO_RWLOCK *lock)
+{
+    if (pthread_rwlock_rdlock(lock) != 0)
+        return 0;
+
+    return 1;
+}
+
+int CRYPTO_THREAD_write_lock(CRYPTO_RWLOCK *lock)
+{
+    if (pthread_rwlock_wrlock(lock) != 0)
+        return 0;
+
+    return 1;
+}
+
+int CRYPTO_THREAD_unlock(CRYPTO_RWLOCK *lock)
+{
+    if (pthread_rwlock_unlock(lock) != 0)
+        return 0;
+
+    return 1;
+}
+
+void CRYPTO_THREAD_lock_free(CRYPTO_RWLOCK *lock)
+{
+    if (lock == NULL)
+        return;
+
+    pthread_rwlock_destroy(lock);
+    OPENSSL_free(lock);
+
+    return;
+}
+
+int CRYPTO_THREAD_run_once(CRYPTO_ONCE *once, void (*init)(void))
+{
+    if (pthread_once(once, init) != 0)
+        return 0;
+
+    return 1;
+}
+
+int CRYPTO_THREAD_init_local(CRYPTO_THREAD_LOCAL *key, void (*cleanup)(void *))
+{
+    if (pthread_key_create(key, cleanup) != 0)
+        return 0;
+
+    return 1;
+}
+
+void *CRYPTO_THREAD_get_local(CRYPTO_THREAD_LOCAL *key)
+{
+    return pthread_getspecific(*key);
+}
+
+int CRYPTO_THREAD_set_local(CRYPTO_THREAD_LOCAL *key, void *val)
+{
+    if (pthread_setspecific(*key, val) != 0)
+        return 0;
+
+    return 1;
+}
+
+int CRYPTO_THREAD_cleanup_local(CRYPTO_THREAD_LOCAL *key)
+{
+    if (pthread_key_delete(*key) != 0)
+        return 0;
+
+    return 1;
+}
+
+CRYPTO_THREAD_ID CRYPTO_THREAD_get_current_id(void)
+{
+    return pthread_self();
+}
+
+int CRYPTO_THREAD_compare_id(CRYPTO_THREAD_ID a, CRYPTO_THREAD_ID b)
+{
+    return pthread_equal(a, b);
+}
+
+int CRYPTO_atomic_add(int *val, int amount, int *ret, CRYPTO_RWLOCK *lock)
+{
+# if defined(__GNUC__) && defined(__ATOMIC_ACQ_REL)
+    if (__atomic_is_lock_free(sizeof(*val), val)) {
+        *ret = __atomic_add_fetch(val, amount, __ATOMIC_ACQ_REL);
+        return 1;
+    }
+# endif
+    if (!CRYPTO_THREAD_write_lock(lock))
+        return 0;
+
+    *val += amount;
+    *ret  = *val;
+
+    if (!CRYPTO_THREAD_unlock(lock))
+        return 0;
+
+    return 1;
+}
+
+#endif
diff --git a/crypto/threads_win.c b/crypto/threads_win.c
new file mode 100644
index 000000000..4e0de908e
--- /dev/null
+++ b/crypto/threads_win.c
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#if defined(_WIN32)
+# include 
+#endif
+
+#include 
+
+#if defined(OPENSSL_THREADS) && !defined(CRYPTO_TDEBUG) && defined(OPENSSL_SYS_WINDOWS)
+
+CRYPTO_RWLOCK *CRYPTO_THREAD_lock_new(void)
+{
+    CRYPTO_RWLOCK *lock = OPENSSL_zalloc(sizeof(CRITICAL_SECTION));
+    if (lock == NULL)
+        return NULL;
+
+    /* 0x400 is the spin count value suggested in the documentation */
+    if (!InitializeCriticalSectionAndSpinCount(lock, 0x400)) {
+        OPENSSL_free(lock);
+        return NULL;
+    }
+
+    return lock;
+}
+
+int CRYPTO_THREAD_read_lock(CRYPTO_RWLOCK *lock)
+{
+    EnterCriticalSection(lock);
+    return 1;
+}
+
+int CRYPTO_THREAD_write_lock(CRYPTO_RWLOCK *lock)
+{
+    EnterCriticalSection(lock);
+    return 1;
+}
+
+int CRYPTO_THREAD_unlock(CRYPTO_RWLOCK *lock)
+{
+    LeaveCriticalSection(lock);
+    return 1;
+}
+
+void CRYPTO_THREAD_lock_free(CRYPTO_RWLOCK *lock)
+{
+    if (lock == NULL)
+        return;
+
+    DeleteCriticalSection(lock);
+    OPENSSL_free(lock);
+
+    return;
+}
+
+#  define ONCE_UNINITED     0
+#  define ONCE_ININIT       1
+#  define ONCE_DONE         2
+
+/*
+ * We don't use InitOnceExecuteOnce because that isn't available in WinXP which
+ * we still have to support.
+ */
+int CRYPTO_THREAD_run_once(CRYPTO_ONCE *once, void (*init)(void))
+{
+    LONG volatile *lock = (LONG *)once;
+    LONG result;
+
+    if (*lock == ONCE_DONE)
+        return 1;
+
+    do {
+        result = InterlockedCompareExchange(lock, ONCE_ININIT, ONCE_UNINITED);
+        if (result == ONCE_UNINITED) {
+            init();
+            *lock = ONCE_DONE;
+            return 1;
+        }
+    } while (result == ONCE_ININIT);
+
+    return (*lock == ONCE_DONE);
+}
+
+int CRYPTO_THREAD_init_local(CRYPTO_THREAD_LOCAL *key, void (*cleanup)(void *))
+{
+    *key = TlsAlloc();
+    if (*key == TLS_OUT_OF_INDEXES)
+        return 0;
+
+    return 1;
+}
+
+void *CRYPTO_THREAD_get_local(CRYPTO_THREAD_LOCAL *key)
+{
+    return TlsGetValue(*key);
+}
+
+int CRYPTO_THREAD_set_local(CRYPTO_THREAD_LOCAL *key, void *val)
+{
+    if (TlsSetValue(*key, val) == 0)
+        return 0;
+
+    return 1;
+}
+
+int CRYPTO_THREAD_cleanup_local(CRYPTO_THREAD_LOCAL *key)
+{
+    if (TlsFree(*key) == 0)
+        return 0;
+
+    return 1;
+}
+
+CRYPTO_THREAD_ID CRYPTO_THREAD_get_current_id(void)
+{
+    return GetCurrentThreadId();
+}
+
+int CRYPTO_THREAD_compare_id(CRYPTO_THREAD_ID a, CRYPTO_THREAD_ID b)
+{
+    return (a == b);
+}
+
+int CRYPTO_atomic_add(int *val, int amount, int *ret, CRYPTO_RWLOCK *lock)
+{
+    *ret = InterlockedExchangeAdd(val, amount) + amount;
+    return 1;
+}
+
+#endif
diff --git a/crypto/ts/build.info b/crypto/ts/build.info
new file mode 100644
index 000000000..98e633d57
--- /dev/null
+++ b/crypto/ts/build.info
@@ -0,0 +1,5 @@
+LIBS=../../libcrypto
+SOURCE[../../libcrypto]=\
+        ts_err.c ts_req_utils.c ts_req_print.c ts_rsp_utils.c ts_rsp_print.c \
+        ts_rsp_sign.c ts_rsp_verify.c ts_verify_ctx.c ts_lib.c ts_conf.c \
+        ts_asn1.c
diff --git a/crypto/ts/ts_asn1.c b/crypto/ts/ts_asn1.c
new file mode 100644
index 000000000..e60675ab7
--- /dev/null
+++ b/crypto/ts/ts_asn1.c
@@ -0,0 +1,259 @@
+/*
+ * Copyright 2006-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include 
+#include "ts_lcl.h"
+
+ASN1_SEQUENCE(TS_MSG_IMPRINT) = {
+        ASN1_SIMPLE(TS_MSG_IMPRINT, hash_algo, X509_ALGOR),
+        ASN1_SIMPLE(TS_MSG_IMPRINT, hashed_msg, ASN1_OCTET_STRING)
+} static_ASN1_SEQUENCE_END(TS_MSG_IMPRINT)
+
+IMPLEMENT_ASN1_FUNCTIONS_const(TS_MSG_IMPRINT)
+IMPLEMENT_ASN1_DUP_FUNCTION(TS_MSG_IMPRINT)
+TS_MSG_IMPRINT *d2i_TS_MSG_IMPRINT_bio(BIO *bp, TS_MSG_IMPRINT **a)
+{
+    return ASN1_d2i_bio_of(TS_MSG_IMPRINT, TS_MSG_IMPRINT_new,
+                           d2i_TS_MSG_IMPRINT, bp, a);
+}
+
+int i2d_TS_MSG_IMPRINT_bio(BIO *bp, TS_MSG_IMPRINT *a)
+{
+    return ASN1_i2d_bio_of_const(TS_MSG_IMPRINT, i2d_TS_MSG_IMPRINT, bp, a);
+}
+#ifndef OPENSSL_NO_STDIO
+TS_MSG_IMPRINT *d2i_TS_MSG_IMPRINT_fp(FILE *fp, TS_MSG_IMPRINT **a)
+{
+    return ASN1_d2i_fp_of(TS_MSG_IMPRINT, TS_MSG_IMPRINT_new,
+                          d2i_TS_MSG_IMPRINT, fp, a);
+}
+
+int i2d_TS_MSG_IMPRINT_fp(FILE *fp, TS_MSG_IMPRINT *a)
+{
+    return ASN1_i2d_fp_of_const(TS_MSG_IMPRINT, i2d_TS_MSG_IMPRINT, fp, a);
+}
+#endif
+
+ASN1_SEQUENCE(TS_REQ) = {
+        ASN1_SIMPLE(TS_REQ, version, ASN1_INTEGER),
+        ASN1_SIMPLE(TS_REQ, msg_imprint, TS_MSG_IMPRINT),
+        ASN1_OPT(TS_REQ, policy_id, ASN1_OBJECT),
+        ASN1_OPT(TS_REQ, nonce, ASN1_INTEGER),
+        ASN1_OPT(TS_REQ, cert_req, ASN1_FBOOLEAN),
+        ASN1_IMP_SEQUENCE_OF_OPT(TS_REQ, extensions, X509_EXTENSION, 0)
+} static_ASN1_SEQUENCE_END(TS_REQ)
+
+IMPLEMENT_ASN1_FUNCTIONS_const(TS_REQ)
+IMPLEMENT_ASN1_DUP_FUNCTION(TS_REQ)
+TS_REQ *d2i_TS_REQ_bio(BIO *bp, TS_REQ **a)
+{
+    return ASN1_d2i_bio_of(TS_REQ, TS_REQ_new, d2i_TS_REQ, bp, a);
+}
+
+int i2d_TS_REQ_bio(BIO *bp, TS_REQ *a)
+{
+    return ASN1_i2d_bio_of_const(TS_REQ, i2d_TS_REQ, bp, a);
+}
+#ifndef OPENSSL_NO_STDIO
+TS_REQ *d2i_TS_REQ_fp(FILE *fp, TS_REQ **a)
+{
+    return ASN1_d2i_fp_of(TS_REQ, TS_REQ_new, d2i_TS_REQ, fp, a);
+}
+
+int i2d_TS_REQ_fp(FILE *fp, TS_REQ *a)
+{
+    return ASN1_i2d_fp_of_const(TS_REQ, i2d_TS_REQ, fp, a);
+}
+#endif
+
+ASN1_SEQUENCE(TS_ACCURACY) = {
+        ASN1_OPT(TS_ACCURACY, seconds, ASN1_INTEGER),
+        ASN1_IMP_OPT(TS_ACCURACY, millis, ASN1_INTEGER, 0),
+        ASN1_IMP_OPT(TS_ACCURACY, micros, ASN1_INTEGER, 1)
+} static_ASN1_SEQUENCE_END(TS_ACCURACY)
+
+IMPLEMENT_ASN1_FUNCTIONS_const(TS_ACCURACY)
+IMPLEMENT_ASN1_DUP_FUNCTION(TS_ACCURACY)
+
+ASN1_SEQUENCE(TS_TST_INFO) = {
+        ASN1_SIMPLE(TS_TST_INFO, version, ASN1_INTEGER),
+        ASN1_SIMPLE(TS_TST_INFO, policy_id, ASN1_OBJECT),
+        ASN1_SIMPLE(TS_TST_INFO, msg_imprint, TS_MSG_IMPRINT),
+        ASN1_SIMPLE(TS_TST_INFO, serial, ASN1_INTEGER),
+        ASN1_SIMPLE(TS_TST_INFO, time, ASN1_GENERALIZEDTIME),
+        ASN1_OPT(TS_TST_INFO, accuracy, TS_ACCURACY),
+        ASN1_OPT(TS_TST_INFO, ordering, ASN1_FBOOLEAN),
+        ASN1_OPT(TS_TST_INFO, nonce, ASN1_INTEGER),
+        ASN1_EXP_OPT(TS_TST_INFO, tsa, GENERAL_NAME, 0),
+        ASN1_IMP_SEQUENCE_OF_OPT(TS_TST_INFO, extensions, X509_EXTENSION, 1)
+} static_ASN1_SEQUENCE_END(TS_TST_INFO)
+
+IMPLEMENT_ASN1_FUNCTIONS_const(TS_TST_INFO)
+IMPLEMENT_ASN1_DUP_FUNCTION(TS_TST_INFO)
+TS_TST_INFO *d2i_TS_TST_INFO_bio(BIO *bp, TS_TST_INFO **a)
+{
+    return ASN1_d2i_bio_of(TS_TST_INFO, TS_TST_INFO_new, d2i_TS_TST_INFO, bp,
+                           a);
+}
+
+int i2d_TS_TST_INFO_bio(BIO *bp, TS_TST_INFO *a)
+{
+    return ASN1_i2d_bio_of_const(TS_TST_INFO, i2d_TS_TST_INFO, bp, a);
+}
+#ifndef OPENSSL_NO_STDIO
+TS_TST_INFO *d2i_TS_TST_INFO_fp(FILE *fp, TS_TST_INFO **a)
+{
+    return ASN1_d2i_fp_of(TS_TST_INFO, TS_TST_INFO_new, d2i_TS_TST_INFO, fp,
+                          a);
+}
+
+int i2d_TS_TST_INFO_fp(FILE *fp, TS_TST_INFO *a)
+{
+    return ASN1_i2d_fp_of_const(TS_TST_INFO, i2d_TS_TST_INFO, fp, a);
+}
+#endif
+
+ASN1_SEQUENCE(TS_STATUS_INFO) = {
+        ASN1_SIMPLE(TS_STATUS_INFO, status, ASN1_INTEGER),
+        ASN1_SEQUENCE_OF_OPT(TS_STATUS_INFO, text, ASN1_UTF8STRING),
+        ASN1_OPT(TS_STATUS_INFO, failure_info, ASN1_BIT_STRING)
+} static_ASN1_SEQUENCE_END(TS_STATUS_INFO)
+
+IMPLEMENT_ASN1_FUNCTIONS_const(TS_STATUS_INFO)
+IMPLEMENT_ASN1_DUP_FUNCTION(TS_STATUS_INFO)
+
+static int ts_resp_set_tst_info(TS_RESP *a)
+{
+    long status;
+
+    status = ASN1_INTEGER_get(a->status_info->status);
+
+    if (a->token) {
+        if (status != 0 && status != 1) {
+            TSerr(TS_F_TS_RESP_SET_TST_INFO, TS_R_TOKEN_PRESENT);
+            return 0;
+        }
+        TS_TST_INFO_free(a->tst_info);
+        a->tst_info = PKCS7_to_TS_TST_INFO(a->token);
+        if (!a->tst_info) {
+            TSerr(TS_F_TS_RESP_SET_TST_INFO,
+                  TS_R_PKCS7_TO_TS_TST_INFO_FAILED);
+            return 0;
+        }
+    } else if (status == 0 || status == 1) {
+        TSerr(TS_F_TS_RESP_SET_TST_INFO, TS_R_TOKEN_NOT_PRESENT);
+        return 0;
+    }
+
+    return 1;
+}
+
+static int ts_resp_cb(int op, ASN1_VALUE **pval, const ASN1_ITEM *it,
+                      void *exarg)
+{
+    TS_RESP *ts_resp = (TS_RESP *)*pval;
+    if (op == ASN1_OP_NEW_POST) {
+        ts_resp->tst_info = NULL;
+    } else if (op == ASN1_OP_FREE_POST) {
+        TS_TST_INFO_free(ts_resp->tst_info);
+    } else if (op == ASN1_OP_D2I_POST) {
+        if (ts_resp_set_tst_info(ts_resp) == 0)
+            return 0;
+    }
+    return 1;
+}
+
+ASN1_SEQUENCE_cb(TS_RESP, ts_resp_cb) = {
+        ASN1_SIMPLE(TS_RESP, status_info, TS_STATUS_INFO),
+        ASN1_OPT(TS_RESP, token, PKCS7),
+} static_ASN1_SEQUENCE_END_cb(TS_RESP, TS_RESP)
+
+IMPLEMENT_ASN1_FUNCTIONS_const(TS_RESP)
+
+IMPLEMENT_ASN1_DUP_FUNCTION(TS_RESP)
+
+TS_RESP *d2i_TS_RESP_bio(BIO *bp, TS_RESP **a)
+{
+    return ASN1_d2i_bio_of(TS_RESP, TS_RESP_new, d2i_TS_RESP, bp, a);
+}
+
+int i2d_TS_RESP_bio(BIO *bp, TS_RESP *a)
+{
+    return ASN1_i2d_bio_of_const(TS_RESP, i2d_TS_RESP, bp, a);
+}
+#ifndef OPENSSL_NO_STDIO
+TS_RESP *d2i_TS_RESP_fp(FILE *fp, TS_RESP **a)
+{
+    return ASN1_d2i_fp_of(TS_RESP, TS_RESP_new, d2i_TS_RESP, fp, a);
+}
+
+int i2d_TS_RESP_fp(FILE *fp, TS_RESP *a)
+{
+    return ASN1_i2d_fp_of_const(TS_RESP, i2d_TS_RESP, fp, a);
+}
+#endif
+
+ASN1_SEQUENCE(ESS_ISSUER_SERIAL) = {
+        ASN1_SEQUENCE_OF(ESS_ISSUER_SERIAL, issuer, GENERAL_NAME),
+        ASN1_SIMPLE(ESS_ISSUER_SERIAL, serial, ASN1_INTEGER)
+} static_ASN1_SEQUENCE_END(ESS_ISSUER_SERIAL)
+
+IMPLEMENT_ASN1_FUNCTIONS_const(ESS_ISSUER_SERIAL)
+IMPLEMENT_ASN1_DUP_FUNCTION(ESS_ISSUER_SERIAL)
+
+ASN1_SEQUENCE(ESS_CERT_ID) = {
+        ASN1_SIMPLE(ESS_CERT_ID, hash, ASN1_OCTET_STRING),
+        ASN1_OPT(ESS_CERT_ID, issuer_serial, ESS_ISSUER_SERIAL)
+} static_ASN1_SEQUENCE_END(ESS_CERT_ID)
+
+IMPLEMENT_ASN1_FUNCTIONS_const(ESS_CERT_ID)
+IMPLEMENT_ASN1_DUP_FUNCTION(ESS_CERT_ID)
+
+ASN1_SEQUENCE(ESS_SIGNING_CERT) = {
+        ASN1_SEQUENCE_OF(ESS_SIGNING_CERT, cert_ids, ESS_CERT_ID),
+        ASN1_SEQUENCE_OF_OPT(ESS_SIGNING_CERT, policy_info, POLICYINFO)
+} static_ASN1_SEQUENCE_END(ESS_SIGNING_CERT)
+
+IMPLEMENT_ASN1_FUNCTIONS_const(ESS_SIGNING_CERT)
+IMPLEMENT_ASN1_DUP_FUNCTION(ESS_SIGNING_CERT)
+
+/* Getting encapsulated TS_TST_INFO object from PKCS7. */
+TS_TST_INFO *PKCS7_to_TS_TST_INFO(PKCS7 *token)
+{
+    PKCS7_SIGNED *pkcs7_signed;
+    PKCS7 *enveloped;
+    ASN1_TYPE *tst_info_wrapper;
+    ASN1_OCTET_STRING *tst_info_der;
+    const unsigned char *p;
+
+    if (!PKCS7_type_is_signed(token)) {
+        TSerr(TS_F_PKCS7_TO_TS_TST_INFO, TS_R_BAD_PKCS7_TYPE);
+        return NULL;
+    }
+    if (PKCS7_get_detached(token)) {
+        TSerr(TS_F_PKCS7_TO_TS_TST_INFO, TS_R_DETACHED_CONTENT);
+        return NULL;
+    }
+    pkcs7_signed = token->d.sign;
+    enveloped = pkcs7_signed->contents;
+    if (OBJ_obj2nid(enveloped->type) != NID_id_smime_ct_TSTInfo) {
+        TSerr(TS_F_PKCS7_TO_TS_TST_INFO, TS_R_BAD_PKCS7_TYPE);
+        return NULL;
+    }
+    tst_info_wrapper = enveloped->d.other;
+    if (tst_info_wrapper->type != V_ASN1_OCTET_STRING) {
+        TSerr(TS_F_PKCS7_TO_TS_TST_INFO, TS_R_BAD_TYPE);
+        return NULL;
+    }
+    tst_info_der = tst_info_wrapper->value.octet_string;
+    p = tst_info_der->data;
+    return d2i_TS_TST_INFO(NULL, &p, tst_info_der->length);
+}
diff --git a/crypto/ts/ts_conf.c b/crypto/ts/ts_conf.c
new file mode 100644
index 000000000..f5f3934df
--- /dev/null
+++ b/crypto/ts/ts_conf.c
@@ -0,0 +1,468 @@
+/*
+ * Copyright 2006-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+#include 
+
+/* Macro definitions for the configuration file. */
+#define BASE_SECTION                    "tsa"
+#define ENV_DEFAULT_TSA                 "default_tsa"
+#define ENV_SERIAL                      "serial"
+#define ENV_CRYPTO_DEVICE               "crypto_device"
+#define ENV_SIGNER_CERT                 "signer_cert"
+#define ENV_CERTS                       "certs"
+#define ENV_SIGNER_KEY                  "signer_key"
+#define ENV_SIGNER_DIGEST               "signer_digest"
+#define ENV_DEFAULT_POLICY              "default_policy"
+#define ENV_OTHER_POLICIES              "other_policies"
+#define ENV_DIGESTS                     "digests"
+#define ENV_ACCURACY                    "accuracy"
+#define ENV_ORDERING                    "ordering"
+#define ENV_TSA_NAME                    "tsa_name"
+#define ENV_ESS_CERT_ID_CHAIN           "ess_cert_id_chain"
+#define ENV_VALUE_SECS                  "secs"
+#define ENV_VALUE_MILLISECS             "millisecs"
+#define ENV_VALUE_MICROSECS             "microsecs"
+#define ENV_CLOCK_PRECISION_DIGITS      "clock_precision_digits"
+#define ENV_VALUE_YES                   "yes"
+#define ENV_VALUE_NO                    "no"
+
+/* Function definitions for certificate and key loading. */
+
+X509 *TS_CONF_load_cert(const char *file)
+{
+    BIO *cert = NULL;
+    X509 *x = NULL;
+
+    if ((cert = BIO_new_file(file, "r")) == NULL)
+        goto end;
+    x = PEM_read_bio_X509_AUX(cert, NULL, NULL, NULL);
+ end:
+    if (x == NULL)
+        TSerr(TS_F_TS_CONF_LOAD_CERT, TS_R_CANNOT_LOAD_CERT);
+    BIO_free(cert);
+    return x;
+}
+
+STACK_OF(X509) *TS_CONF_load_certs(const char *file)
+{
+    BIO *certs = NULL;
+    STACK_OF(X509) *othercerts = NULL;
+    STACK_OF(X509_INFO) *allcerts = NULL;
+    int i;
+
+    if ((certs = BIO_new_file(file, "r")) == NULL)
+        goto end;
+    if ((othercerts = sk_X509_new_null()) == NULL)
+        goto end;
+
+    allcerts = PEM_X509_INFO_read_bio(certs, NULL, NULL, NULL);
+    for (i = 0; i < sk_X509_INFO_num(allcerts); i++) {
+        X509_INFO *xi = sk_X509_INFO_value(allcerts, i);
+        if (xi->x509) {
+            sk_X509_push(othercerts, xi->x509);
+            xi->x509 = NULL;
+        }
+    }
+ end:
+    if (othercerts == NULL)
+        TSerr(TS_F_TS_CONF_LOAD_CERTS, TS_R_CANNOT_LOAD_CERT);
+    sk_X509_INFO_pop_free(allcerts, X509_INFO_free);
+    BIO_free(certs);
+    return othercerts;
+}
+
+EVP_PKEY *TS_CONF_load_key(const char *file, const char *pass)
+{
+    BIO *key = NULL;
+    EVP_PKEY *pkey = NULL;
+
+    if ((key = BIO_new_file(file, "r")) == NULL)
+        goto end;
+    pkey = PEM_read_bio_PrivateKey(key, NULL, NULL, (char *)pass);
+ end:
+    if (pkey == NULL)
+        TSerr(TS_F_TS_CONF_LOAD_KEY, TS_R_CANNOT_LOAD_KEY);
+    BIO_free(key);
+    return pkey;
+}
+
+/* Function definitions for handling configuration options. */
+
+static void ts_CONF_lookup_fail(const char *name, const char *tag)
+{
+    TSerr(TS_F_TS_CONF_LOOKUP_FAIL, TS_R_VAR_LOOKUP_FAILURE);
+    ERR_add_error_data(3, name, "::", tag);
+}
+
+static void ts_CONF_invalid(const char *name, const char *tag)
+{
+    TSerr(TS_F_TS_CONF_INVALID, TS_R_VAR_BAD_VALUE);
+    ERR_add_error_data(3, name, "::", tag);
+}
+
+const char *TS_CONF_get_tsa_section(CONF *conf, const char *section)
+{
+    if (!section) {
+        section = NCONF_get_string(conf, BASE_SECTION, ENV_DEFAULT_TSA);
+        if (!section)
+            ts_CONF_lookup_fail(BASE_SECTION, ENV_DEFAULT_TSA);
+    }
+    return section;
+}
+
+int TS_CONF_set_serial(CONF *conf, const char *section, TS_serial_cb cb,
+                       TS_RESP_CTX *ctx)
+{
+    int ret = 0;
+    char *serial = NCONF_get_string(conf, section, ENV_SERIAL);
+    if (!serial) {
+        ts_CONF_lookup_fail(section, ENV_SERIAL);
+        goto err;
+    }
+    TS_RESP_CTX_set_serial_cb(ctx, cb, serial);
+
+    ret = 1;
+ err:
+    return ret;
+}
+
+#ifndef OPENSSL_NO_ENGINE
+
+int TS_CONF_set_crypto_device(CONF *conf, const char *section,
+                              const char *device)
+{
+    int ret = 0;
+
+    if (device == NULL)
+        device = NCONF_get_string(conf, section, ENV_CRYPTO_DEVICE);
+
+    if (device && !TS_CONF_set_default_engine(device)) {
+        ts_CONF_invalid(section, ENV_CRYPTO_DEVICE);
+        goto err;
+    }
+    ret = 1;
+ err:
+    return ret;
+}
+
+int TS_CONF_set_default_engine(const char *name)
+{
+    ENGINE *e = NULL;
+    int ret = 0;
+
+    if (strcmp(name, "builtin") == 0)
+        return 1;
+
+    if ((e = ENGINE_by_id(name)) == NULL)
+        goto err;
+    if (strcmp(name, "chil") == 0)
+        ENGINE_ctrl(e, ENGINE_CTRL_CHIL_SET_FORKCHECK, 1, 0, 0);
+    if (!ENGINE_set_default(e, ENGINE_METHOD_ALL))
+        goto err;
+    ret = 1;
+
+ err:
+    if (!ret) {
+        TSerr(TS_F_TS_CONF_SET_DEFAULT_ENGINE, TS_R_COULD_NOT_SET_ENGINE);
+        ERR_add_error_data(2, "engine:", name);
+    }
+    ENGINE_free(e);
+    return ret;
+}
+
+#endif
+
+int TS_CONF_set_signer_cert(CONF *conf, const char *section,
+                            const char *cert, TS_RESP_CTX *ctx)
+{
+    int ret = 0;
+    X509 *cert_obj = NULL;
+
+    if (cert == NULL) {
+        cert = NCONF_get_string(conf, section, ENV_SIGNER_CERT);
+        if (cert == NULL) {
+            ts_CONF_lookup_fail(section, ENV_SIGNER_CERT);
+            goto err;
+        }
+    }
+    if ((cert_obj = TS_CONF_load_cert(cert)) == NULL)
+        goto err;
+    if (!TS_RESP_CTX_set_signer_cert(ctx, cert_obj))
+        goto err;
+
+    ret = 1;
+ err:
+    X509_free(cert_obj);
+    return ret;
+}
+
+int TS_CONF_set_certs(CONF *conf, const char *section, const char *certs,
+                      TS_RESP_CTX *ctx)
+{
+    int ret = 0;
+    STACK_OF(X509) *certs_obj = NULL;
+
+    if (certs == NULL) {
+        /* Certificate chain is optional. */
+        if ((certs = NCONF_get_string(conf, section, ENV_CERTS)) == NULL)
+            goto end;
+    }
+    if ((certs_obj = TS_CONF_load_certs(certs)) == NULL)
+        goto err;
+    if (!TS_RESP_CTX_set_certs(ctx, certs_obj))
+        goto err;
+ end:
+    ret = 1;
+ err:
+    sk_X509_pop_free(certs_obj, X509_free);
+    return ret;
+}
+
+int TS_CONF_set_signer_key(CONF *conf, const char *section,
+                           const char *key, const char *pass,
+                           TS_RESP_CTX *ctx)
+{
+    int ret = 0;
+    EVP_PKEY *key_obj = NULL;
+    if (!key)
+        key = NCONF_get_string(conf, section, ENV_SIGNER_KEY);
+    if (!key) {
+        ts_CONF_lookup_fail(section, ENV_SIGNER_KEY);
+        goto err;
+    }
+    if ((key_obj = TS_CONF_load_key(key, pass)) == NULL)
+        goto err;
+    if (!TS_RESP_CTX_set_signer_key(ctx, key_obj))
+        goto err;
+
+    ret = 1;
+ err:
+    EVP_PKEY_free(key_obj);
+    return ret;
+}
+
+int TS_CONF_set_signer_digest(CONF *conf, const char *section,
+                              const char *md, TS_RESP_CTX *ctx)
+{
+    int ret = 0;
+    const EVP_MD *sign_md = NULL;
+    if (md == NULL)
+        md = NCONF_get_string(conf, section, ENV_SIGNER_DIGEST);
+    if (md == NULL) {
+        ts_CONF_lookup_fail(section, ENV_SIGNER_DIGEST);
+        goto err;
+    }
+    sign_md = EVP_get_digestbyname(md);
+    if (sign_md == NULL) {
+        ts_CONF_invalid(section, ENV_SIGNER_DIGEST);
+        goto err;
+    }
+    if (!TS_RESP_CTX_set_signer_digest(ctx, sign_md))
+        goto err;
+
+    ret = 1;
+ err:
+    return ret;
+}
+
+int TS_CONF_set_def_policy(CONF *conf, const char *section,
+                           const char *policy, TS_RESP_CTX *ctx)
+{
+    int ret = 0;
+    ASN1_OBJECT *policy_obj = NULL;
+    if (!policy)
+        policy = NCONF_get_string(conf, section, ENV_DEFAULT_POLICY);
+    if (!policy) {
+        ts_CONF_lookup_fail(section, ENV_DEFAULT_POLICY);
+        goto err;
+    }
+    if ((policy_obj = OBJ_txt2obj(policy, 0)) == NULL) {
+        ts_CONF_invalid(section, ENV_DEFAULT_POLICY);
+        goto err;
+    }
+    if (!TS_RESP_CTX_set_def_policy(ctx, policy_obj))
+        goto err;
+
+    ret = 1;
+ err:
+    ASN1_OBJECT_free(policy_obj);
+    return ret;
+}
+
+int TS_CONF_set_policies(CONF *conf, const char *section, TS_RESP_CTX *ctx)
+{
+    int ret = 0;
+    int i;
+    STACK_OF(CONF_VALUE) *list = NULL;
+    char *policies = NCONF_get_string(conf, section, ENV_OTHER_POLICIES);
+
+    /* If no other policy is specified, that's fine. */
+    if (policies && (list = X509V3_parse_list(policies)) == NULL) {
+        ts_CONF_invalid(section, ENV_OTHER_POLICIES);
+        goto err;
+    }
+    for (i = 0; i < sk_CONF_VALUE_num(list); ++i) {
+        CONF_VALUE *val = sk_CONF_VALUE_value(list, i);
+        const char *extval = val->value ? val->value : val->name;
+        ASN1_OBJECT *objtmp;
+
+        if ((objtmp = OBJ_txt2obj(extval, 0)) == NULL) {
+            ts_CONF_invalid(section, ENV_OTHER_POLICIES);
+            goto err;
+        }
+        if (!TS_RESP_CTX_add_policy(ctx, objtmp))
+            goto err;
+        ASN1_OBJECT_free(objtmp);
+    }
+
+    ret = 1;
+ err:
+    sk_CONF_VALUE_pop_free(list, X509V3_conf_free);
+    return ret;
+}
+
+int TS_CONF_set_digests(CONF *conf, const char *section, TS_RESP_CTX *ctx)
+{
+    int ret = 0;
+    int i;
+    STACK_OF(CONF_VALUE) *list = NULL;
+    char *digests = NCONF_get_string(conf, section, ENV_DIGESTS);
+
+    if (digests == NULL) {
+        ts_CONF_lookup_fail(section, ENV_DIGESTS);
+        goto err;
+    }
+    if ((list = X509V3_parse_list(digests)) == NULL) {
+        ts_CONF_invalid(section, ENV_DIGESTS);
+        goto err;
+    }
+    if (sk_CONF_VALUE_num(list) == 0) {
+        ts_CONF_invalid(section, ENV_DIGESTS);
+        goto err;
+    }
+    for (i = 0; i < sk_CONF_VALUE_num(list); ++i) {
+        CONF_VALUE *val = sk_CONF_VALUE_value(list, i);
+        const char *extval = val->value ? val->value : val->name;
+        const EVP_MD *md;
+
+        if ((md = EVP_get_digestbyname(extval)) == NULL) {
+            ts_CONF_invalid(section, ENV_DIGESTS);
+            goto err;
+        }
+        if (!TS_RESP_CTX_add_md(ctx, md))
+            goto err;
+    }
+
+    ret = 1;
+ err:
+    sk_CONF_VALUE_pop_free(list, X509V3_conf_free);
+    return ret;
+}
+
+int TS_CONF_set_accuracy(CONF *conf, const char *section, TS_RESP_CTX *ctx)
+{
+    int ret = 0;
+    int i;
+    int secs = 0, millis = 0, micros = 0;
+    STACK_OF(CONF_VALUE) *list = NULL;
+    char *accuracy = NCONF_get_string(conf, section, ENV_ACCURACY);
+
+    if (accuracy && (list = X509V3_parse_list(accuracy)) == NULL) {
+        ts_CONF_invalid(section, ENV_ACCURACY);
+        goto err;
+    }
+    for (i = 0; i < sk_CONF_VALUE_num(list); ++i) {
+        CONF_VALUE *val = sk_CONF_VALUE_value(list, i);
+        if (strcmp(val->name, ENV_VALUE_SECS) == 0) {
+            if (val->value)
+                secs = atoi(val->value);
+        } else if (strcmp(val->name, ENV_VALUE_MILLISECS) == 0) {
+            if (val->value)
+                millis = atoi(val->value);
+        } else if (strcmp(val->name, ENV_VALUE_MICROSECS) == 0) {
+            if (val->value)
+                micros = atoi(val->value);
+        } else {
+            ts_CONF_invalid(section, ENV_ACCURACY);
+            goto err;
+        }
+    }
+    if (!TS_RESP_CTX_set_accuracy(ctx, secs, millis, micros))
+        goto err;
+
+    ret = 1;
+ err:
+    sk_CONF_VALUE_pop_free(list, X509V3_conf_free);
+    return ret;
+}
+
+int TS_CONF_set_clock_precision_digits(CONF *conf, const char *section,
+                                       TS_RESP_CTX *ctx)
+{
+    int ret = 0;
+    long digits = 0;
+
+    /*
+     * If not specified, set the default value to 0, i.e. sec precision
+     */
+    if (!NCONF_get_number_e(conf, section, ENV_CLOCK_PRECISION_DIGITS,
+                            &digits))
+        digits = 0;
+    if (digits < 0 || digits > TS_MAX_CLOCK_PRECISION_DIGITS) {
+        ts_CONF_invalid(section, ENV_CLOCK_PRECISION_DIGITS);
+        goto err;
+    }
+
+    if (!TS_RESP_CTX_set_clock_precision_digits(ctx, digits))
+        goto err;
+
+    return 1;
+ err:
+    return ret;
+}
+
+static int ts_CONF_add_flag(CONF *conf, const char *section,
+                            const char *field, int flag, TS_RESP_CTX *ctx)
+{
+    const char *value = NCONF_get_string(conf, section, field);
+
+    if (value) {
+        if (strcmp(value, ENV_VALUE_YES) == 0)
+            TS_RESP_CTX_add_flags(ctx, flag);
+        else if (strcmp(value, ENV_VALUE_NO) != 0) {
+            ts_CONF_invalid(section, field);
+            return 0;
+        }
+    }
+
+    return 1;
+}
+
+int TS_CONF_set_ordering(CONF *conf, const char *section, TS_RESP_CTX *ctx)
+{
+    return ts_CONF_add_flag(conf, section, ENV_ORDERING, TS_ORDERING, ctx);
+}
+
+int TS_CONF_set_tsa_name(CONF *conf, const char *section, TS_RESP_CTX *ctx)
+{
+    return ts_CONF_add_flag(conf, section, ENV_TSA_NAME, TS_TSA_NAME, ctx);
+}
+
+int TS_CONF_set_ess_cert_id_chain(CONF *conf, const char *section,
+                                  TS_RESP_CTX *ctx)
+{
+    return ts_CONF_add_flag(conf, section, ENV_ESS_CERT_ID_CHAIN,
+                            TS_ESS_CERT_ID_CHAIN, ctx);
+}
diff --git a/crypto/ts/ts_err.c b/crypto/ts/ts_err.c
new file mode 100644
index 000000000..a6d73a174
--- /dev/null
+++ b/crypto/ts/ts_err.c
@@ -0,0 +1,144 @@
+/*
+ * Generated by util/mkerr.pl DO NOT EDIT
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include 
+
+/* BEGIN ERROR CODES */
+#ifndef OPENSSL_NO_ERR
+
+# define ERR_FUNC(func) ERR_PACK(ERR_LIB_TS,func,0)
+# define ERR_REASON(reason) ERR_PACK(ERR_LIB_TS,0,reason)
+
+static ERR_STRING_DATA TS_str_functs[] = {
+    {ERR_FUNC(TS_F_DEF_SERIAL_CB), "def_serial_cb"},
+    {ERR_FUNC(TS_F_DEF_TIME_CB), "def_time_cb"},
+    {ERR_FUNC(TS_F_ESS_ADD_SIGNING_CERT), "ESS_add_signing_cert"},
+    {ERR_FUNC(TS_F_ESS_CERT_ID_NEW_INIT), "ess_CERT_ID_new_init"},
+    {ERR_FUNC(TS_F_ESS_SIGNING_CERT_NEW_INIT), "ess_SIGNING_CERT_new_init"},
+    {ERR_FUNC(TS_F_INT_TS_RESP_VERIFY_TOKEN), "int_ts_RESP_verify_token"},
+    {ERR_FUNC(TS_F_PKCS7_TO_TS_TST_INFO), "PKCS7_to_TS_TST_INFO"},
+    {ERR_FUNC(TS_F_TS_ACCURACY_SET_MICROS), "TS_ACCURACY_set_micros"},
+    {ERR_FUNC(TS_F_TS_ACCURACY_SET_MILLIS), "TS_ACCURACY_set_millis"},
+    {ERR_FUNC(TS_F_TS_ACCURACY_SET_SECONDS), "TS_ACCURACY_set_seconds"},
+    {ERR_FUNC(TS_F_TS_CHECK_IMPRINTS), "ts_check_imprints"},
+    {ERR_FUNC(TS_F_TS_CHECK_NONCES), "ts_check_nonces"},
+    {ERR_FUNC(TS_F_TS_CHECK_POLICY), "ts_check_policy"},
+    {ERR_FUNC(TS_F_TS_CHECK_SIGNING_CERTS), "ts_check_signing_certs"},
+    {ERR_FUNC(TS_F_TS_CHECK_STATUS_INFO), "ts_check_status_info"},
+    {ERR_FUNC(TS_F_TS_COMPUTE_IMPRINT), "ts_compute_imprint"},
+    {ERR_FUNC(TS_F_TS_CONF_INVALID), "ts_CONF_invalid"},
+    {ERR_FUNC(TS_F_TS_CONF_LOAD_CERT), "TS_CONF_load_cert"},
+    {ERR_FUNC(TS_F_TS_CONF_LOAD_CERTS), "TS_CONF_load_certs"},
+    {ERR_FUNC(TS_F_TS_CONF_LOAD_KEY), "TS_CONF_load_key"},
+    {ERR_FUNC(TS_F_TS_CONF_LOOKUP_FAIL), "ts_CONF_lookup_fail"},
+    {ERR_FUNC(TS_F_TS_CONF_SET_DEFAULT_ENGINE), "TS_CONF_set_default_engine"},
+    {ERR_FUNC(TS_F_TS_GET_STATUS_TEXT), "ts_get_status_text"},
+    {ERR_FUNC(TS_F_TS_MSG_IMPRINT_SET_ALGO), "TS_MSG_IMPRINT_set_algo"},
+    {ERR_FUNC(TS_F_TS_REQ_SET_MSG_IMPRINT), "TS_REQ_set_msg_imprint"},
+    {ERR_FUNC(TS_F_TS_REQ_SET_NONCE), "TS_REQ_set_nonce"},
+    {ERR_FUNC(TS_F_TS_REQ_SET_POLICY_ID), "TS_REQ_set_policy_id"},
+    {ERR_FUNC(TS_F_TS_RESP_CREATE_RESPONSE), "TS_RESP_create_response"},
+    {ERR_FUNC(TS_F_TS_RESP_CREATE_TST_INFO), "ts_RESP_create_tst_info"},
+    {ERR_FUNC(TS_F_TS_RESP_CTX_ADD_FAILURE_INFO),
+     "TS_RESP_CTX_add_failure_info"},
+    {ERR_FUNC(TS_F_TS_RESP_CTX_ADD_MD), "TS_RESP_CTX_add_md"},
+    {ERR_FUNC(TS_F_TS_RESP_CTX_ADD_POLICY), "TS_RESP_CTX_add_policy"},
+    {ERR_FUNC(TS_F_TS_RESP_CTX_NEW), "TS_RESP_CTX_new"},
+    {ERR_FUNC(TS_F_TS_RESP_CTX_SET_ACCURACY), "TS_RESP_CTX_set_accuracy"},
+    {ERR_FUNC(TS_F_TS_RESP_CTX_SET_CERTS), "TS_RESP_CTX_set_certs"},
+    {ERR_FUNC(TS_F_TS_RESP_CTX_SET_DEF_POLICY), "TS_RESP_CTX_set_def_policy"},
+    {ERR_FUNC(TS_F_TS_RESP_CTX_SET_SIGNER_CERT),
+     "TS_RESP_CTX_set_signer_cert"},
+    {ERR_FUNC(TS_F_TS_RESP_CTX_SET_STATUS_INFO),
+     "TS_RESP_CTX_set_status_info"},
+    {ERR_FUNC(TS_F_TS_RESP_GET_POLICY), "ts_RESP_get_policy"},
+    {ERR_FUNC(TS_F_TS_RESP_SET_GENTIME_WITH_PRECISION),
+     "TS_RESP_set_genTime_with_precision"},
+    {ERR_FUNC(TS_F_TS_RESP_SET_STATUS_INFO), "TS_RESP_set_status_info"},
+    {ERR_FUNC(TS_F_TS_RESP_SET_TST_INFO), "TS_RESP_set_tst_info"},
+    {ERR_FUNC(TS_F_TS_RESP_SIGN), "ts_RESP_sign"},
+    {ERR_FUNC(TS_F_TS_RESP_VERIFY_SIGNATURE), "TS_RESP_verify_signature"},
+    {ERR_FUNC(TS_F_TS_TST_INFO_SET_ACCURACY), "TS_TST_INFO_set_accuracy"},
+    {ERR_FUNC(TS_F_TS_TST_INFO_SET_MSG_IMPRINT),
+     "TS_TST_INFO_set_msg_imprint"},
+    {ERR_FUNC(TS_F_TS_TST_INFO_SET_NONCE), "TS_TST_INFO_set_nonce"},
+    {ERR_FUNC(TS_F_TS_TST_INFO_SET_POLICY_ID), "TS_TST_INFO_set_policy_id"},
+    {ERR_FUNC(TS_F_TS_TST_INFO_SET_SERIAL), "TS_TST_INFO_set_serial"},
+    {ERR_FUNC(TS_F_TS_TST_INFO_SET_TIME), "TS_TST_INFO_set_time"},
+    {ERR_FUNC(TS_F_TS_TST_INFO_SET_TSA), "TS_TST_INFO_set_tsa"},
+    {ERR_FUNC(TS_F_TS_VERIFY), "TS_VERIFY"},
+    {ERR_FUNC(TS_F_TS_VERIFY_CERT), "ts_verify_cert"},
+    {ERR_FUNC(TS_F_TS_VERIFY_CTX_NEW), "TS_VERIFY_CTX_new"},
+    {0, NULL}
+};
+
+static ERR_STRING_DATA TS_str_reasons[] = {
+    {ERR_REASON(TS_R_BAD_PKCS7_TYPE), "bad pkcs7 type"},
+    {ERR_REASON(TS_R_BAD_TYPE), "bad type"},
+    {ERR_REASON(TS_R_CANNOT_LOAD_CERT), "cannot load certificate"},
+    {ERR_REASON(TS_R_CANNOT_LOAD_KEY), "cannot load private key"},
+    {ERR_REASON(TS_R_CERTIFICATE_VERIFY_ERROR), "certificate verify error"},
+    {ERR_REASON(TS_R_COULD_NOT_SET_ENGINE), "could not set engine"},
+    {ERR_REASON(TS_R_COULD_NOT_SET_TIME), "could not set time"},
+    {ERR_REASON(TS_R_DETACHED_CONTENT), "detached content"},
+    {ERR_REASON(TS_R_ESS_ADD_SIGNING_CERT_ERROR),
+     "ess add signing cert error"},
+    {ERR_REASON(TS_R_ESS_SIGNING_CERTIFICATE_ERROR),
+     "ess signing certificate error"},
+    {ERR_REASON(TS_R_INVALID_NULL_POINTER), "invalid null pointer"},
+    {ERR_REASON(TS_R_INVALID_SIGNER_CERTIFICATE_PURPOSE),
+     "invalid signer certificate purpose"},
+    {ERR_REASON(TS_R_MESSAGE_IMPRINT_MISMATCH), "message imprint mismatch"},
+    {ERR_REASON(TS_R_NONCE_MISMATCH), "nonce mismatch"},
+    {ERR_REASON(TS_R_NONCE_NOT_RETURNED), "nonce not returned"},
+    {ERR_REASON(TS_R_NO_CONTENT), "no content"},
+    {ERR_REASON(TS_R_NO_TIME_STAMP_TOKEN), "no time stamp token"},
+    {ERR_REASON(TS_R_PKCS7_ADD_SIGNATURE_ERROR), "pkcs7 add signature error"},
+    {ERR_REASON(TS_R_PKCS7_ADD_SIGNED_ATTR_ERROR),
+     "pkcs7 add signed attr error"},
+    {ERR_REASON(TS_R_PKCS7_TO_TS_TST_INFO_FAILED),
+     "pkcs7 to ts tst info failed"},
+    {ERR_REASON(TS_R_POLICY_MISMATCH), "policy mismatch"},
+    {ERR_REASON(TS_R_PRIVATE_KEY_DOES_NOT_MATCH_CERTIFICATE),
+     "private key does not match certificate"},
+    {ERR_REASON(TS_R_RESPONSE_SETUP_ERROR), "response setup error"},
+    {ERR_REASON(TS_R_SIGNATURE_FAILURE), "signature failure"},
+    {ERR_REASON(TS_R_THERE_MUST_BE_ONE_SIGNER), "there must be one signer"},
+    {ERR_REASON(TS_R_TIME_SYSCALL_ERROR), "time syscall error"},
+    {ERR_REASON(TS_R_TOKEN_NOT_PRESENT), "token not present"},
+    {ERR_REASON(TS_R_TOKEN_PRESENT), "token present"},
+    {ERR_REASON(TS_R_TSA_NAME_MISMATCH), "tsa name mismatch"},
+    {ERR_REASON(TS_R_TSA_UNTRUSTED), "tsa untrusted"},
+    {ERR_REASON(TS_R_TST_INFO_SETUP_ERROR), "tst info setup error"},
+    {ERR_REASON(TS_R_TS_DATASIGN), "ts datasign"},
+    {ERR_REASON(TS_R_UNACCEPTABLE_POLICY), "unacceptable policy"},
+    {ERR_REASON(TS_R_UNSUPPORTED_MD_ALGORITHM), "unsupported md algorithm"},
+    {ERR_REASON(TS_R_UNSUPPORTED_VERSION), "unsupported version"},
+    {ERR_REASON(TS_R_VAR_BAD_VALUE), "var bad value"},
+    {ERR_REASON(TS_R_VAR_LOOKUP_FAILURE), "cannot find config variable"},
+    {ERR_REASON(TS_R_WRONG_CONTENT_TYPE), "wrong content type"},
+    {0, NULL}
+};
+
+#endif
+
+int ERR_load_TS_strings(void)
+{
+#ifndef OPENSSL_NO_ERR
+
+    if (ERR_func_error_string(TS_str_functs[0].error) == NULL) {
+        ERR_load_strings(0, TS_str_functs);
+        ERR_load_strings(0, TS_str_reasons);
+    }
+#endif
+    return 1;
+}
diff --git a/crypto/ts/ts_lcl.h b/crypto/ts/ts_lcl.h
new file mode 100644
index 000000000..d0c3cf816
--- /dev/null
+++ b/crypto/ts/ts_lcl.h
@@ -0,0 +1,183 @@
+/*
+ * Copyright 2015-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*-
+ * MessageImprint ::= SEQUENCE  {
+ *      hashAlgorithm                AlgorithmIdentifier,
+ *      hashedMessage                OCTET STRING  }
+ */
+struct TS_msg_imprint_st {
+    X509_ALGOR *hash_algo;
+    ASN1_OCTET_STRING *hashed_msg;
+};
+
+/*-
+ * TimeStampResp ::= SEQUENCE  {
+ *     status                  PKIStatusInfo,
+ *     timeStampToken          TimeStampToken     OPTIONAL }
+ */
+struct TS_resp_st {
+    TS_STATUS_INFO *status_info;
+    PKCS7 *token;
+    TS_TST_INFO *tst_info;
+};
+
+/*-
+ * TimeStampReq ::= SEQUENCE  {
+ *    version                  INTEGER  { v1(1) },
+ *    messageImprint           MessageImprint,
+ *      --a hash algorithm OID and the hash value of the data to be
+ *      --time-stamped
+ *    reqPolicy                TSAPolicyId                OPTIONAL,
+ *    nonce                    INTEGER                    OPTIONAL,
+ *    certReq                  BOOLEAN                    DEFAULT FALSE,
+ *    extensions               [0] IMPLICIT Extensions    OPTIONAL  }
+ */
+struct TS_req_st {
+    ASN1_INTEGER *version;
+    TS_MSG_IMPRINT *msg_imprint;
+    ASN1_OBJECT *policy_id;
+    ASN1_INTEGER *nonce;
+    ASN1_BOOLEAN cert_req;
+    STACK_OF(X509_EXTENSION) *extensions;
+};
+
+/*-
+ * Accuracy ::= SEQUENCE {
+ *                 seconds        INTEGER           OPTIONAL,
+ *                 millis     [0] INTEGER  (1..999) OPTIONAL,
+ *                 micros     [1] INTEGER  (1..999) OPTIONAL  }
+ */
+struct TS_accuracy_st {
+    ASN1_INTEGER *seconds;
+    ASN1_INTEGER *millis;
+    ASN1_INTEGER *micros;
+};
+
+/*-
+ * TSTInfo ::= SEQUENCE  {
+ *     version                      INTEGER  { v1(1) },
+ *     policy                       TSAPolicyId,
+ *     messageImprint               MessageImprint,
+ *       -- MUST have the same value as the similar field in
+ *       -- TimeStampReq
+ *     serialNumber                 INTEGER,
+ *      -- Time-Stamping users MUST be ready to accommodate integers
+ *      -- up to 160 bits.
+ *     genTime                      GeneralizedTime,
+ *     accuracy                     Accuracy                 OPTIONAL,
+ *     ordering                     BOOLEAN             DEFAULT FALSE,
+ *     nonce                        INTEGER                  OPTIONAL,
+ *       -- MUST be present if the similar field was present
+ *       -- in TimeStampReq.  In that case it MUST have the same value.
+ *     tsa                          [0] GeneralName          OPTIONAL,
+ *     extensions                   [1] IMPLICIT Extensions  OPTIONAL   }
+ */
+struct TS_tst_info_st {
+    ASN1_INTEGER *version;
+    ASN1_OBJECT *policy_id;
+    TS_MSG_IMPRINT *msg_imprint;
+    ASN1_INTEGER *serial;
+    ASN1_GENERALIZEDTIME *time;
+    TS_ACCURACY *accuracy;
+    ASN1_BOOLEAN ordering;
+    ASN1_INTEGER *nonce;
+    GENERAL_NAME *tsa;
+    STACK_OF(X509_EXTENSION) *extensions;
+};
+
+struct TS_status_info_st {
+    ASN1_INTEGER *status;
+    STACK_OF(ASN1_UTF8STRING) *text;
+    ASN1_BIT_STRING *failure_info;
+};
+
+/*-
+ * IssuerSerial ::= SEQUENCE {
+ *         issuer                   GeneralNames,
+ *         serialNumber             CertificateSerialNumber
+ *         }
+ */
+struct ESS_issuer_serial {
+    STACK_OF(GENERAL_NAME) *issuer;
+    ASN1_INTEGER *serial;
+};
+
+/*-
+ * ESSCertID ::=  SEQUENCE {
+ *         certHash                 Hash,
+ *         issuerSerial             IssuerSerial OPTIONAL
+ * }
+ */
+struct ESS_cert_id {
+    ASN1_OCTET_STRING *hash;    /* Always SHA-1 digest. */
+    ESS_ISSUER_SERIAL *issuer_serial;
+};
+
+/*-
+ * SigningCertificate ::=  SEQUENCE {
+ *        certs        SEQUENCE OF ESSCertID,
+ *        policies     SEQUENCE OF PolicyInformation OPTIONAL
+ * }
+ */
+struct ESS_signing_cert {
+    STACK_OF(ESS_CERT_ID) *cert_ids;
+    STACK_OF(POLICYINFO) *policy_info;
+};
+
+
+struct TS_resp_ctx {
+    X509 *signer_cert;
+    EVP_PKEY *signer_key;
+    const EVP_MD *signer_md;
+    STACK_OF(X509) *certs;      /* Certs to include in signed data. */
+    STACK_OF(ASN1_OBJECT) *policies; /* Acceptable policies. */
+    ASN1_OBJECT *default_policy; /* It may appear in policies, too. */
+    STACK_OF(EVP_MD) *mds;      /* Acceptable message digests. */
+    ASN1_INTEGER *seconds;      /* accuracy, 0 means not specified. */
+    ASN1_INTEGER *millis;       /* accuracy, 0 means not specified. */
+    ASN1_INTEGER *micros;       /* accuracy, 0 means not specified. */
+    unsigned clock_precision_digits; /* fraction of seconds in time stamp
+                                      * token. */
+    unsigned flags;             /* Optional info, see values above. */
+    /* Callback functions. */
+    TS_serial_cb serial_cb;
+    void *serial_cb_data;       /* User data for serial_cb. */
+    TS_time_cb time_cb;
+    void *time_cb_data;         /* User data for time_cb. */
+    TS_extension_cb extension_cb;
+    void *extension_cb_data;    /* User data for extension_cb. */
+    /* These members are used only while creating the response. */
+    TS_REQ *request;
+    TS_RESP *response;
+    TS_TST_INFO *tst_info;
+};
+
+struct TS_verify_ctx {
+    /* Set this to the union of TS_VFY_... flags you want to carry out. */
+    unsigned flags;
+    /* Must be set only with TS_VFY_SIGNATURE. certs is optional. */
+    X509_STORE *store;
+    STACK_OF(X509) *certs;
+    /* Must be set only with TS_VFY_POLICY. */
+    ASN1_OBJECT *policy;
+    /*
+     * Must be set only with TS_VFY_IMPRINT. If md_alg is NULL, the
+     * algorithm from the response is used.
+     */
+    X509_ALGOR *md_alg;
+    unsigned char *imprint;
+    unsigned imprint_len;
+    /* Must be set only with TS_VFY_DATA. */
+    BIO *data;
+    /* Must be set only with TS_VFY_TSA_NAME. */
+    ASN1_INTEGER *nonce;
+    /* Must be set only with TS_VFY_TSA_NAME. */
+    GENERAL_NAME *tsa_name;
+};
diff --git a/crypto/ts/ts_lib.c b/crypto/ts/ts_lib.c
new file mode 100644
index 000000000..de36e0e08
--- /dev/null
+++ b/crypto/ts/ts_lib.c
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2006-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+#include 
+#include 
+#include 
+#include "ts_lcl.h"
+
+int TS_ASN1_INTEGER_print_bio(BIO *bio, const ASN1_INTEGER *num)
+{
+    BIGNUM *num_bn;
+    int result = 0;
+    char *hex;
+
+    num_bn = BN_new();
+    if (num_bn == NULL)
+        return -1;
+    ASN1_INTEGER_to_BN(num, num_bn);
+    if ((hex = BN_bn2hex(num_bn))) {
+        result = BIO_write(bio, "0x", 2) > 0;
+        result = result && BIO_write(bio, hex, strlen(hex)) > 0;
+        OPENSSL_free(hex);
+    }
+    BN_free(num_bn);
+
+    return result;
+}
+
+int TS_OBJ_print_bio(BIO *bio, const ASN1_OBJECT *obj)
+{
+    char obj_txt[128];
+
+    OBJ_obj2txt(obj_txt, sizeof(obj_txt), obj, 0);
+    BIO_printf(bio, "%s\n", obj_txt);
+
+    return 1;
+}
+
+int TS_ext_print_bio(BIO *bio, const STACK_OF(X509_EXTENSION) *extensions)
+{
+    int i, critical, n;
+    X509_EXTENSION *ex;
+    ASN1_OBJECT *obj;
+
+    BIO_printf(bio, "Extensions:\n");
+    n = X509v3_get_ext_count(extensions);
+    for (i = 0; i < n; i++) {
+        ex = X509v3_get_ext(extensions, i);
+        obj = X509_EXTENSION_get_object(ex);
+        if (i2a_ASN1_OBJECT(bio, obj) < 0)
+            return 0;
+        critical = X509_EXTENSION_get_critical(ex);
+        BIO_printf(bio, ":%s\n", critical ? " critical" : "");
+        if (!X509V3_EXT_print(bio, ex, 0, 4)) {
+            BIO_printf(bio, "%4s", "");
+            ASN1_STRING_print(bio, X509_EXTENSION_get_data(ex));
+        }
+        BIO_write(bio, "\n", 1);
+    }
+
+    return 1;
+}
+
+int TS_X509_ALGOR_print_bio(BIO *bio, const X509_ALGOR *alg)
+{
+    int i = OBJ_obj2nid(alg->algorithm);
+    return BIO_printf(bio, "Hash Algorithm: %s\n",
+                      (i == NID_undef) ? "UNKNOWN" : OBJ_nid2ln(i));
+}
+
+int TS_MSG_IMPRINT_print_bio(BIO *bio, TS_MSG_IMPRINT *a)
+{
+    ASN1_OCTET_STRING *msg;
+
+    TS_X509_ALGOR_print_bio(bio, a->hash_algo);
+
+    BIO_printf(bio, "Message data:\n");
+    msg = a->hashed_msg;
+    BIO_dump_indent(bio, (const char *)ASN1_STRING_get0_data(msg),
+                    ASN1_STRING_length(msg), 4);
+
+    return 1;
+}
diff --git a/crypto/ts/ts_req_print.c b/crypto/ts/ts_req_print.c
new file mode 100644
index 000000000..0dedf47d9
--- /dev/null
+++ b/crypto/ts/ts_req_print.c
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2006-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+#include 
+#include 
+#include "ts_lcl.h"
+
+int TS_REQ_print_bio(BIO *bio, TS_REQ *a)
+{
+    int v;
+    ASN1_OBJECT *policy_id;
+
+    if (a == NULL)
+        return 0;
+
+    v = TS_REQ_get_version(a);
+    BIO_printf(bio, "Version: %d\n", v);
+
+    TS_MSG_IMPRINT_print_bio(bio, a->msg_imprint);
+
+    BIO_printf(bio, "Policy OID: ");
+    policy_id = TS_REQ_get_policy_id(a);
+    if (policy_id == NULL)
+        BIO_printf(bio, "unspecified\n");
+    else
+        TS_OBJ_print_bio(bio, policy_id);
+
+    BIO_printf(bio, "Nonce: ");
+    if (a->nonce == NULL)
+        BIO_printf(bio, "unspecified");
+    else
+        TS_ASN1_INTEGER_print_bio(bio, a->nonce);
+    BIO_write(bio, "\n", 1);
+
+    BIO_printf(bio, "Certificate required: %s\n",
+               a->cert_req ? "yes" : "no");
+
+    TS_ext_print_bio(bio, a->extensions);
+
+    return 1;
+}
diff --git a/crypto/ts/ts_req_utils.c b/crypto/ts/ts_req_utils.c
new file mode 100644
index 000000000..2073d3395
--- /dev/null
+++ b/crypto/ts/ts_req_utils.c
@@ -0,0 +1,183 @@
+/*
+ * Copyright 2006-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+#include 
+#include "ts_lcl.h"
+
+int TS_REQ_set_version(TS_REQ *a, long version)
+{
+    return ASN1_INTEGER_set(a->version, version);
+}
+
+long TS_REQ_get_version(const TS_REQ *a)
+{
+    return ASN1_INTEGER_get(a->version);
+}
+
+int TS_REQ_set_msg_imprint(TS_REQ *a, TS_MSG_IMPRINT *msg_imprint)
+{
+    TS_MSG_IMPRINT *new_msg_imprint;
+
+    if (a->msg_imprint == msg_imprint)
+        return 1;
+    new_msg_imprint = TS_MSG_IMPRINT_dup(msg_imprint);
+    if (new_msg_imprint == NULL) {
+        TSerr(TS_F_TS_REQ_SET_MSG_IMPRINT, ERR_R_MALLOC_FAILURE);
+        return 0;
+    }
+    TS_MSG_IMPRINT_free(a->msg_imprint);
+    a->msg_imprint = new_msg_imprint;
+    return 1;
+}
+
+TS_MSG_IMPRINT *TS_REQ_get_msg_imprint(TS_REQ *a)
+{
+    return a->msg_imprint;
+}
+
+int TS_MSG_IMPRINT_set_algo(TS_MSG_IMPRINT *a, X509_ALGOR *alg)
+{
+    X509_ALGOR *new_alg;
+
+    if (a->hash_algo == alg)
+        return 1;
+    new_alg = X509_ALGOR_dup(alg);
+    if (new_alg == NULL) {
+        TSerr(TS_F_TS_MSG_IMPRINT_SET_ALGO, ERR_R_MALLOC_FAILURE);
+        return 0;
+    }
+    X509_ALGOR_free(a->hash_algo);
+    a->hash_algo = new_alg;
+    return 1;
+}
+
+X509_ALGOR *TS_MSG_IMPRINT_get_algo(TS_MSG_IMPRINT *a)
+{
+    return a->hash_algo;
+}
+
+int TS_MSG_IMPRINT_set_msg(TS_MSG_IMPRINT *a, unsigned char *d, int len)
+{
+    return ASN1_OCTET_STRING_set(a->hashed_msg, d, len);
+}
+
+ASN1_OCTET_STRING *TS_MSG_IMPRINT_get_msg(TS_MSG_IMPRINT *a)
+{
+    return a->hashed_msg;
+}
+
+int TS_REQ_set_policy_id(TS_REQ *a, const ASN1_OBJECT *policy)
+{
+    ASN1_OBJECT *new_policy;
+
+    if (a->policy_id == policy)
+        return 1;
+    new_policy = OBJ_dup(policy);
+    if (new_policy == NULL) {
+        TSerr(TS_F_TS_REQ_SET_POLICY_ID, ERR_R_MALLOC_FAILURE);
+        return 0;
+    }
+    ASN1_OBJECT_free(a->policy_id);
+    a->policy_id = new_policy;
+    return 1;
+}
+
+ASN1_OBJECT *TS_REQ_get_policy_id(TS_REQ *a)
+{
+    return a->policy_id;
+}
+
+int TS_REQ_set_nonce(TS_REQ *a, const ASN1_INTEGER *nonce)
+{
+    ASN1_INTEGER *new_nonce;
+
+    if (a->nonce == nonce)
+        return 1;
+    new_nonce = ASN1_INTEGER_dup(nonce);
+    if (new_nonce == NULL) {
+        TSerr(TS_F_TS_REQ_SET_NONCE, ERR_R_MALLOC_FAILURE);
+        return 0;
+    }
+    ASN1_INTEGER_free(a->nonce);
+    a->nonce = new_nonce;
+    return 1;
+}
+
+const ASN1_INTEGER *TS_REQ_get_nonce(const TS_REQ *a)
+{
+    return a->nonce;
+}
+
+int TS_REQ_set_cert_req(TS_REQ *a, int cert_req)
+{
+    a->cert_req = cert_req ? 0xFF : 0x00;
+    return 1;
+}
+
+int TS_REQ_get_cert_req(const TS_REQ *a)
+{
+    return a->cert_req ? 1 : 0;
+}
+
+STACK_OF(X509_EXTENSION) *TS_REQ_get_exts(TS_REQ *a)
+{
+    return a->extensions;
+}
+
+void TS_REQ_ext_free(TS_REQ *a)
+{
+    if (!a)
+        return;
+    sk_X509_EXTENSION_pop_free(a->extensions, X509_EXTENSION_free);
+    a->extensions = NULL;
+}
+
+int TS_REQ_get_ext_count(TS_REQ *a)
+{
+    return X509v3_get_ext_count(a->extensions);
+}
+
+int TS_REQ_get_ext_by_NID(TS_REQ *a, int nid, int lastpos)
+{
+    return X509v3_get_ext_by_NID(a->extensions, nid, lastpos);
+}
+
+int TS_REQ_get_ext_by_OBJ(TS_REQ *a, const ASN1_OBJECT *obj, int lastpos)
+{
+    return X509v3_get_ext_by_OBJ(a->extensions, obj, lastpos);
+}
+
+int TS_REQ_get_ext_by_critical(TS_REQ *a, int crit, int lastpos)
+{
+    return X509v3_get_ext_by_critical(a->extensions, crit, lastpos);
+}
+
+X509_EXTENSION *TS_REQ_get_ext(TS_REQ *a, int loc)
+{
+    return X509v3_get_ext(a->extensions, loc);
+}
+
+X509_EXTENSION *TS_REQ_delete_ext(TS_REQ *a, int loc)
+{
+    return X509v3_delete_ext(a->extensions, loc);
+}
+
+int TS_REQ_add_ext(TS_REQ *a, X509_EXTENSION *ex, int loc)
+{
+    return X509v3_add_ext(&a->extensions, ex, loc) != NULL;
+}
+
+void *TS_REQ_get_ext_d2i(TS_REQ *a, int nid, int *crit, int *idx)
+{
+    return X509V3_get_d2i(a->extensions, nid, crit, idx);
+}
diff --git a/crypto/ts/ts_rsp_print.c b/crypto/ts/ts_rsp_print.c
new file mode 100644
index 000000000..6eb0ec8d7
--- /dev/null
+++ b/crypto/ts/ts_rsp_print.c
@@ -0,0 +1,195 @@
+/*
+ * Copyright 2006-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+#include 
+#include 
+#include "ts_lcl.h"
+
+struct status_map_st {
+    int bit;
+    const char *text;
+};
+
+static int ts_status_map_print(BIO *bio, const struct status_map_st *a,
+                               const ASN1_BIT_STRING *v);
+static int ts_ACCURACY_print_bio(BIO *bio, const TS_ACCURACY *accuracy);
+
+
+int TS_RESP_print_bio(BIO *bio, TS_RESP *a)
+{
+    BIO_printf(bio, "Status info:\n");
+    TS_STATUS_INFO_print_bio(bio, a->status_info);
+
+    BIO_printf(bio, "\nTST info:\n");
+    if (a->tst_info != NULL)
+        TS_TST_INFO_print_bio(bio, a->tst_info);
+    else
+        BIO_printf(bio, "Not included.\n");
+
+    return 1;
+}
+
+int TS_STATUS_INFO_print_bio(BIO *bio, TS_STATUS_INFO *a)
+{
+    static const char *status_map[] = {
+        "Granted.",
+        "Granted with modifications.",
+        "Rejected.",
+        "Waiting.",
+        "Revocation warning.",
+        "Revoked."
+    };
+    static const struct status_map_st failure_map[] = {
+        {TS_INFO_BAD_ALG,
+         "unrecognized or unsupported algorithm identifier"},
+        {TS_INFO_BAD_REQUEST,
+         "transaction not permitted or supported"},
+        {TS_INFO_BAD_DATA_FORMAT,
+         "the data submitted has the wrong format"},
+        {TS_INFO_TIME_NOT_AVAILABLE,
+         "the TSA's time source is not available"},
+        {TS_INFO_UNACCEPTED_POLICY,
+         "the requested TSA policy is not supported by the TSA"},
+        {TS_INFO_UNACCEPTED_EXTENSION,
+         "the requested extension is not supported by the TSA"},
+        {TS_INFO_ADD_INFO_NOT_AVAILABLE,
+         "the additional information requested could not be understood "
+         "or is not available"},
+        {TS_INFO_SYSTEM_FAILURE,
+         "the request cannot be handled due to system failure"},
+        {-1, NULL}
+    };
+    long status;
+    int i, lines = 0;
+
+    BIO_printf(bio, "Status: ");
+    status = ASN1_INTEGER_get(a->status);
+    if (0 <= status && status < (long)OSSL_NELEM(status_map))
+        BIO_printf(bio, "%s\n", status_map[status]);
+    else
+        BIO_printf(bio, "out of bounds\n");
+
+    BIO_printf(bio, "Status description: ");
+    for (i = 0; i < sk_ASN1_UTF8STRING_num(a->text); ++i) {
+        if (i > 0)
+            BIO_puts(bio, "\t");
+        ASN1_STRING_print_ex(bio, sk_ASN1_UTF8STRING_value(a->text, i), 0);
+        BIO_puts(bio, "\n");
+    }
+    if (i == 0)
+        BIO_printf(bio, "unspecified\n");
+
+    BIO_printf(bio, "Failure info: ");
+    if (a->failure_info != NULL)
+        lines = ts_status_map_print(bio, failure_map, a->failure_info);
+    if (lines == 0)
+        BIO_printf(bio, "unspecified");
+    BIO_printf(bio, "\n");
+
+    return 1;
+}
+
+static int ts_status_map_print(BIO *bio, const struct status_map_st *a,
+                               const ASN1_BIT_STRING *v)
+{
+    int lines = 0;
+
+    for (; a->bit >= 0; ++a) {
+        if (ASN1_BIT_STRING_get_bit(v, a->bit)) {
+            if (++lines > 1)
+                BIO_printf(bio, ", ");
+            BIO_printf(bio, "%s", a->text);
+        }
+    }
+
+    return lines;
+}
+
+int TS_TST_INFO_print_bio(BIO *bio, TS_TST_INFO *a)
+{
+    int v;
+
+    if (a == NULL)
+        return 0;
+
+    v = ASN1_INTEGER_get(a->version);
+    BIO_printf(bio, "Version: %d\n", v);
+
+    BIO_printf(bio, "Policy OID: ");
+    TS_OBJ_print_bio(bio, a->policy_id);
+
+    TS_MSG_IMPRINT_print_bio(bio, a->msg_imprint);
+
+    BIO_printf(bio, "Serial number: ");
+    if (a->serial == NULL)
+        BIO_printf(bio, "unspecified");
+    else
+        TS_ASN1_INTEGER_print_bio(bio, a->serial);
+    BIO_write(bio, "\n", 1);
+
+    BIO_printf(bio, "Time stamp: ");
+    ASN1_GENERALIZEDTIME_print(bio, a->time);
+    BIO_write(bio, "\n", 1);
+
+    BIO_printf(bio, "Accuracy: ");
+    if (a->accuracy == NULL)
+        BIO_printf(bio, "unspecified");
+    else
+        ts_ACCURACY_print_bio(bio, a->accuracy);
+    BIO_write(bio, "\n", 1);
+
+    BIO_printf(bio, "Ordering: %s\n", a->ordering ? "yes" : "no");
+
+    BIO_printf(bio, "Nonce: ");
+    if (a->nonce == NULL)
+        BIO_printf(bio, "unspecified");
+    else
+        TS_ASN1_INTEGER_print_bio(bio, a->nonce);
+    BIO_write(bio, "\n", 1);
+
+    BIO_printf(bio, "TSA: ");
+    if (a->tsa == NULL)
+        BIO_printf(bio, "unspecified");
+    else {
+        STACK_OF(CONF_VALUE) *nval;
+        if ((nval = i2v_GENERAL_NAME(NULL, a->tsa, NULL)))
+            X509V3_EXT_val_prn(bio, nval, 0, 0);
+        sk_CONF_VALUE_pop_free(nval, X509V3_conf_free);
+    }
+    BIO_write(bio, "\n", 1);
+
+    TS_ext_print_bio(bio, a->extensions);
+
+    return 1;
+}
+
+static int ts_ACCURACY_print_bio(BIO *bio, const TS_ACCURACY *a)
+{
+    if (a->seconds != NULL)
+        TS_ASN1_INTEGER_print_bio(bio, a->seconds);
+    else
+        BIO_printf(bio, "unspecified");
+    BIO_printf(bio, " seconds, ");
+    if (a->millis != NULL)
+        TS_ASN1_INTEGER_print_bio(bio, a->millis);
+    else
+        BIO_printf(bio, "unspecified");
+    BIO_printf(bio, " millis, ");
+    if (a->micros != NULL)
+        TS_ASN1_INTEGER_print_bio(bio, a->micros);
+    else
+        BIO_printf(bio, "unspecified");
+    BIO_printf(bio, " micros");
+
+    return 1;
+}
diff --git a/crypto/ts/ts_rsp_sign.c b/crypto/ts/ts_rsp_sign.c
new file mode 100644
index 000000000..aea7b922a
--- /dev/null
+++ b/crypto/ts/ts_rsp_sign.c
@@ -0,0 +1,904 @@
+/*
+ * Copyright 2006-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "internal/cryptlib.h"
+
+#if defined(OPENSSL_SYS_UNIX)
+# include 
+#endif
+
+#include 
+#include 
+#include 
+#include "ts_lcl.h"
+
+static ASN1_INTEGER *def_serial_cb(struct TS_resp_ctx *, void *);
+static int def_time_cb(struct TS_resp_ctx *, void *, long *sec, long *usec);
+static int def_extension_cb(struct TS_resp_ctx *, X509_EXTENSION *, void *);
+
+static void ts_RESP_CTX_init(TS_RESP_CTX *ctx);
+static void ts_RESP_CTX_cleanup(TS_RESP_CTX *ctx);
+static int ts_RESP_check_request(TS_RESP_CTX *ctx);
+static ASN1_OBJECT *ts_RESP_get_policy(TS_RESP_CTX *ctx);
+static TS_TST_INFO *ts_RESP_create_tst_info(TS_RESP_CTX *ctx,
+                                            ASN1_OBJECT *policy);
+static int ts_RESP_process_extensions(TS_RESP_CTX *ctx);
+static int ts_RESP_sign(TS_RESP_CTX *ctx);
+
+static ESS_SIGNING_CERT *ess_SIGNING_CERT_new_init(X509 *signcert,
+                                                   STACK_OF(X509) *certs);
+static ESS_CERT_ID *ess_CERT_ID_new_init(X509 *cert, int issuer_needed);
+static int ts_TST_INFO_content_new(PKCS7 *p7);
+static int ESS_add_signing_cert(PKCS7_SIGNER_INFO *si, ESS_SIGNING_CERT *sc);
+
+static ASN1_GENERALIZEDTIME
+*TS_RESP_set_genTime_with_precision(ASN1_GENERALIZEDTIME *, long, long,
+                                    unsigned);
+
+/* Default callback for response generation. */
+static ASN1_INTEGER *def_serial_cb(struct TS_resp_ctx *ctx, void *data)
+{
+    ASN1_INTEGER *serial = ASN1_INTEGER_new();
+
+    if (serial == NULL)
+        goto err;
+    if (!ASN1_INTEGER_set(serial, 1))
+        goto err;
+    return serial;
+
+ err:
+    TSerr(TS_F_DEF_SERIAL_CB, ERR_R_MALLOC_FAILURE);
+    TS_RESP_CTX_set_status_info(ctx, TS_STATUS_REJECTION,
+                                "Error during serial number generation.");
+    return NULL;
+}
+
+#if defined(OPENSSL_SYS_UNIX)
+
+static int def_time_cb(struct TS_resp_ctx *ctx, void *data,
+                       long *sec, long *usec)
+{
+    struct timeval tv;
+    if (gettimeofday(&tv, NULL) != 0) {
+        TSerr(TS_F_DEF_TIME_CB, TS_R_TIME_SYSCALL_ERROR);
+        TS_RESP_CTX_set_status_info(ctx, TS_STATUS_REJECTION,
+                                    "Time is not available.");
+        TS_RESP_CTX_add_failure_info(ctx, TS_INFO_TIME_NOT_AVAILABLE);
+        return 0;
+    }
+    *sec = tv.tv_sec;
+    *usec = tv.tv_usec;
+
+    return 1;
+}
+
+#else
+
+static int def_time_cb(struct TS_resp_ctx *ctx, void *data,
+                       long *sec, long *usec)
+{
+    time_t t;
+    if (time(&t) == (time_t)-1) {
+        TSerr(TS_F_DEF_TIME_CB, TS_R_TIME_SYSCALL_ERROR);
+        TS_RESP_CTX_set_status_info(ctx, TS_STATUS_REJECTION,
+                                    "Time is not available.");
+        TS_RESP_CTX_add_failure_info(ctx, TS_INFO_TIME_NOT_AVAILABLE);
+        return 0;
+    }
+    *sec = (long)t;
+    *usec = 0;
+
+    return 1;
+}
+
+#endif
+
+static int def_extension_cb(struct TS_resp_ctx *ctx, X509_EXTENSION *ext,
+                            void *data)
+{
+    TS_RESP_CTX_set_status_info(ctx, TS_STATUS_REJECTION,
+                                "Unsupported extension.");
+    TS_RESP_CTX_add_failure_info(ctx, TS_INFO_UNACCEPTED_EXTENSION);
+    return 0;
+}
+
+/* TS_RESP_CTX management functions. */
+
+TS_RESP_CTX *TS_RESP_CTX_new()
+{
+    TS_RESP_CTX *ctx;
+
+    if ((ctx = OPENSSL_zalloc(sizeof(*ctx))) == NULL) {
+        TSerr(TS_F_TS_RESP_CTX_NEW, ERR_R_MALLOC_FAILURE);
+        return NULL;
+    }
+
+    ctx->signer_md = EVP_sha256();
+
+    ctx->serial_cb = def_serial_cb;
+    ctx->time_cb = def_time_cb;
+    ctx->extension_cb = def_extension_cb;
+
+    return ctx;
+}
+
+void TS_RESP_CTX_free(TS_RESP_CTX *ctx)
+{
+    if (!ctx)
+        return;
+
+    X509_free(ctx->signer_cert);
+    EVP_PKEY_free(ctx->signer_key);
+    sk_X509_pop_free(ctx->certs, X509_free);
+    sk_ASN1_OBJECT_pop_free(ctx->policies, ASN1_OBJECT_free);
+    ASN1_OBJECT_free(ctx->default_policy);
+    sk_EVP_MD_free(ctx->mds);   /* No EVP_MD_free method exists. */
+    ASN1_INTEGER_free(ctx->seconds);
+    ASN1_INTEGER_free(ctx->millis);
+    ASN1_INTEGER_free(ctx->micros);
+    OPENSSL_free(ctx);
+}
+
+int TS_RESP_CTX_set_signer_cert(TS_RESP_CTX *ctx, X509 *signer)
+{
+    if (X509_check_purpose(signer, X509_PURPOSE_TIMESTAMP_SIGN, 0) != 1) {
+        TSerr(TS_F_TS_RESP_CTX_SET_SIGNER_CERT,
+              TS_R_INVALID_SIGNER_CERTIFICATE_PURPOSE);
+        return 0;
+    }
+    X509_free(ctx->signer_cert);
+    ctx->signer_cert = signer;
+    X509_up_ref(ctx->signer_cert);
+    return 1;
+}
+
+int TS_RESP_CTX_set_signer_key(TS_RESP_CTX *ctx, EVP_PKEY *key)
+{
+    EVP_PKEY_free(ctx->signer_key);
+    ctx->signer_key = key;
+    EVP_PKEY_up_ref(ctx->signer_key);
+
+    return 1;
+}
+
+int TS_RESP_CTX_set_signer_digest(TS_RESP_CTX *ctx, const EVP_MD *md)
+{
+    ctx->signer_md = md;
+    return 1;
+}
+
+int TS_RESP_CTX_set_def_policy(TS_RESP_CTX *ctx, const ASN1_OBJECT *def_policy)
+{
+    ASN1_OBJECT_free(ctx->default_policy);
+    if ((ctx->default_policy = OBJ_dup(def_policy)) == NULL)
+        goto err;
+    return 1;
+ err:
+    TSerr(TS_F_TS_RESP_CTX_SET_DEF_POLICY, ERR_R_MALLOC_FAILURE);
+    return 0;
+}
+
+int TS_RESP_CTX_set_certs(TS_RESP_CTX *ctx, STACK_OF(X509) *certs)
+{
+
+    sk_X509_pop_free(ctx->certs, X509_free);
+    ctx->certs = NULL;
+    if (!certs)
+        return 1;
+    if ((ctx->certs = X509_chain_up_ref(certs)) == NULL) {
+        TSerr(TS_F_TS_RESP_CTX_SET_CERTS, ERR_R_MALLOC_FAILURE);
+        return 0;
+    }
+
+    return 1;
+}
+
+int TS_RESP_CTX_add_policy(TS_RESP_CTX *ctx, const ASN1_OBJECT *policy)
+{
+    ASN1_OBJECT *copy = NULL;
+
+    if (ctx->policies == NULL
+        && (ctx->policies = sk_ASN1_OBJECT_new_null()) == NULL)
+        goto err;
+    if ((copy = OBJ_dup(policy)) == NULL)
+        goto err;
+    if (!sk_ASN1_OBJECT_push(ctx->policies, copy))
+        goto err;
+
+    return 1;
+ err:
+    TSerr(TS_F_TS_RESP_CTX_ADD_POLICY, ERR_R_MALLOC_FAILURE);
+    ASN1_OBJECT_free(copy);
+    return 0;
+}
+
+int TS_RESP_CTX_add_md(TS_RESP_CTX *ctx, const EVP_MD *md)
+{
+    if (ctx->mds == NULL
+        && (ctx->mds = sk_EVP_MD_new_null()) == NULL)
+        goto err;
+    if (!sk_EVP_MD_push(ctx->mds, md))
+        goto err;
+
+    return 1;
+ err:
+    TSerr(TS_F_TS_RESP_CTX_ADD_MD, ERR_R_MALLOC_FAILURE);
+    return 0;
+}
+
+#define TS_RESP_CTX_accuracy_free(ctx)          \
+        ASN1_INTEGER_free(ctx->seconds);        \
+        ctx->seconds = NULL;                    \
+        ASN1_INTEGER_free(ctx->millis);         \
+        ctx->millis = NULL;                     \
+        ASN1_INTEGER_free(ctx->micros);         \
+        ctx->micros = NULL;
+
+int TS_RESP_CTX_set_accuracy(TS_RESP_CTX *ctx,
+                             int secs, int millis, int micros)
+{
+
+    TS_RESP_CTX_accuracy_free(ctx);
+    if (secs
+        && ((ctx->seconds = ASN1_INTEGER_new()) == NULL
+            || !ASN1_INTEGER_set(ctx->seconds, secs)))
+        goto err;
+    if (millis
+        && ((ctx->millis = ASN1_INTEGER_new()) == NULL
+            || !ASN1_INTEGER_set(ctx->millis, millis)))
+        goto err;
+    if (micros
+        && ((ctx->micros = ASN1_INTEGER_new()) == NULL
+            || !ASN1_INTEGER_set(ctx->micros, micros)))
+        goto err;
+
+    return 1;
+ err:
+    TS_RESP_CTX_accuracy_free(ctx);
+    TSerr(TS_F_TS_RESP_CTX_SET_ACCURACY, ERR_R_MALLOC_FAILURE);
+    return 0;
+}
+
+void TS_RESP_CTX_add_flags(TS_RESP_CTX *ctx, int flags)
+{
+    ctx->flags |= flags;
+}
+
+void TS_RESP_CTX_set_serial_cb(TS_RESP_CTX *ctx, TS_serial_cb cb, void *data)
+{
+    ctx->serial_cb = cb;
+    ctx->serial_cb_data = data;
+}
+
+void TS_RESP_CTX_set_time_cb(TS_RESP_CTX *ctx, TS_time_cb cb, void *data)
+{
+    ctx->time_cb = cb;
+    ctx->time_cb_data = data;
+}
+
+void TS_RESP_CTX_set_extension_cb(TS_RESP_CTX *ctx,
+                                  TS_extension_cb cb, void *data)
+{
+    ctx->extension_cb = cb;
+    ctx->extension_cb_data = data;
+}
+
+int TS_RESP_CTX_set_status_info(TS_RESP_CTX *ctx,
+                                int status, const char *text)
+{
+    TS_STATUS_INFO *si = NULL;
+    ASN1_UTF8STRING *utf8_text = NULL;
+    int ret = 0;
+
+    if ((si = TS_STATUS_INFO_new()) == NULL)
+        goto err;
+    if (!ASN1_INTEGER_set(si->status, status))
+        goto err;
+    if (text) {
+        if ((utf8_text = ASN1_UTF8STRING_new()) == NULL
+            || !ASN1_STRING_set(utf8_text, text, strlen(text)))
+            goto err;
+        if (si->text == NULL
+            && (si->text = sk_ASN1_UTF8STRING_new_null()) == NULL)
+            goto err;
+        if (!sk_ASN1_UTF8STRING_push(si->text, utf8_text))
+            goto err;
+        utf8_text = NULL;       /* Ownership is lost. */
+    }
+    if (!TS_RESP_set_status_info(ctx->response, si))
+        goto err;
+    ret = 1;
+ err:
+    if (!ret)
+        TSerr(TS_F_TS_RESP_CTX_SET_STATUS_INFO, ERR_R_MALLOC_FAILURE);
+    TS_STATUS_INFO_free(si);
+    ASN1_UTF8STRING_free(utf8_text);
+    return ret;
+}
+
+int TS_RESP_CTX_set_status_info_cond(TS_RESP_CTX *ctx,
+                                     int status, const char *text)
+{
+    int ret = 1;
+    TS_STATUS_INFO *si = ctx->response->status_info;
+
+    if (ASN1_INTEGER_get(si->status) == TS_STATUS_GRANTED) {
+        ret = TS_RESP_CTX_set_status_info(ctx, status, text);
+    }
+    return ret;
+}
+
+int TS_RESP_CTX_add_failure_info(TS_RESP_CTX *ctx, int failure)
+{
+    TS_STATUS_INFO *si = ctx->response->status_info;
+    if (si->failure_info == NULL
+        && (si->failure_info = ASN1_BIT_STRING_new()) == NULL)
+        goto err;
+    if (!ASN1_BIT_STRING_set_bit(si->failure_info, failure, 1))
+        goto err;
+    return 1;
+ err:
+    TSerr(TS_F_TS_RESP_CTX_ADD_FAILURE_INFO, ERR_R_MALLOC_FAILURE);
+    return 0;
+}
+
+TS_REQ *TS_RESP_CTX_get_request(TS_RESP_CTX *ctx)
+{
+    return ctx->request;
+}
+
+TS_TST_INFO *TS_RESP_CTX_get_tst_info(TS_RESP_CTX *ctx)
+{
+    return ctx->tst_info;
+}
+
+int TS_RESP_CTX_set_clock_precision_digits(TS_RESP_CTX *ctx,
+                                           unsigned precision)
+{
+    if (precision > TS_MAX_CLOCK_PRECISION_DIGITS)
+        return 0;
+    ctx->clock_precision_digits = precision;
+    return 1;
+}
+
+/* Main entry method of the response generation. */
+TS_RESP *TS_RESP_create_response(TS_RESP_CTX *ctx, BIO *req_bio)
+{
+    ASN1_OBJECT *policy;
+    TS_RESP *response;
+    int result = 0;
+
+    ts_RESP_CTX_init(ctx);
+
+    if ((ctx->response = TS_RESP_new()) == NULL) {
+        TSerr(TS_F_TS_RESP_CREATE_RESPONSE, ERR_R_MALLOC_FAILURE);
+        goto end;
+    }
+    if ((ctx->request = d2i_TS_REQ_bio(req_bio, NULL)) == NULL) {
+        TS_RESP_CTX_set_status_info(ctx, TS_STATUS_REJECTION,
+                                    "Bad request format or system error.");
+        TS_RESP_CTX_add_failure_info(ctx, TS_INFO_BAD_DATA_FORMAT);
+        goto end;
+    }
+    if (!TS_RESP_CTX_set_status_info(ctx, TS_STATUS_GRANTED, NULL))
+        goto end;
+    if (!ts_RESP_check_request(ctx))
+        goto end;
+    if ((policy = ts_RESP_get_policy(ctx)) == NULL)
+        goto end;
+    if ((ctx->tst_info = ts_RESP_create_tst_info(ctx, policy)) == NULL)
+        goto end;
+    if (!ts_RESP_process_extensions(ctx))
+        goto end;
+    if (!ts_RESP_sign(ctx))
+        goto end;
+    result = 1;
+
+ end:
+    if (!result) {
+        TSerr(TS_F_TS_RESP_CREATE_RESPONSE, TS_R_RESPONSE_SETUP_ERROR);
+        if (ctx->response != NULL) {
+            if (TS_RESP_CTX_set_status_info_cond(ctx,
+                                                 TS_STATUS_REJECTION,
+                                                 "Error during response "
+                                                 "generation.") == 0) {
+                TS_RESP_free(ctx->response);
+                ctx->response = NULL;
+            }
+        }
+    }
+    response = ctx->response;
+    ctx->response = NULL;       /* Ownership will be returned to caller. */
+    ts_RESP_CTX_cleanup(ctx);
+    return response;
+}
+
+/* Initializes the variable part of the context. */
+static void ts_RESP_CTX_init(TS_RESP_CTX *ctx)
+{
+    ctx->request = NULL;
+    ctx->response = NULL;
+    ctx->tst_info = NULL;
+}
+
+/* Cleans up the variable part of the context. */
+static void ts_RESP_CTX_cleanup(TS_RESP_CTX *ctx)
+{
+    TS_REQ_free(ctx->request);
+    ctx->request = NULL;
+    TS_RESP_free(ctx->response);
+    ctx->response = NULL;
+    TS_TST_INFO_free(ctx->tst_info);
+    ctx->tst_info = NULL;
+}
+
+/* Checks the format and content of the request. */
+static int ts_RESP_check_request(TS_RESP_CTX *ctx)
+{
+    TS_REQ *request = ctx->request;
+    TS_MSG_IMPRINT *msg_imprint;
+    X509_ALGOR *md_alg;
+    int md_alg_id;
+    const ASN1_OCTET_STRING *digest;
+    const EVP_MD *md = NULL;
+    int i;
+
+    if (TS_REQ_get_version(request) != 1) {
+        TS_RESP_CTX_set_status_info(ctx, TS_STATUS_REJECTION,
+                                    "Bad request version.");
+        TS_RESP_CTX_add_failure_info(ctx, TS_INFO_BAD_REQUEST);
+        return 0;
+    }
+
+    msg_imprint = request->msg_imprint;
+    md_alg = msg_imprint->hash_algo;
+    md_alg_id = OBJ_obj2nid(md_alg->algorithm);
+    for (i = 0; !md && i < sk_EVP_MD_num(ctx->mds); ++i) {
+        const EVP_MD *current_md = sk_EVP_MD_value(ctx->mds, i);
+        if (md_alg_id == EVP_MD_type(current_md))
+            md = current_md;
+    }
+    if (!md) {
+        TS_RESP_CTX_set_status_info(ctx, TS_STATUS_REJECTION,
+                                    "Message digest algorithm is "
+                                    "not supported.");
+        TS_RESP_CTX_add_failure_info(ctx, TS_INFO_BAD_ALG);
+        return 0;
+    }
+
+    if (md_alg->parameter && ASN1_TYPE_get(md_alg->parameter) != V_ASN1_NULL) {
+        TS_RESP_CTX_set_status_info(ctx, TS_STATUS_REJECTION,
+                                    "Superfluous message digest "
+                                    "parameter.");
+        TS_RESP_CTX_add_failure_info(ctx, TS_INFO_BAD_ALG);
+        return 0;
+    }
+    digest = msg_imprint->hashed_msg;
+    if (digest->length != EVP_MD_size(md)) {
+        TS_RESP_CTX_set_status_info(ctx, TS_STATUS_REJECTION,
+                                    "Bad message digest.");
+        TS_RESP_CTX_add_failure_info(ctx, TS_INFO_BAD_DATA_FORMAT);
+        return 0;
+    }
+
+    return 1;
+}
+
+/* Returns the TSA policy based on the requested and acceptable policies. */
+static ASN1_OBJECT *ts_RESP_get_policy(TS_RESP_CTX *ctx)
+{
+    ASN1_OBJECT *requested = ctx->request->policy_id;
+    ASN1_OBJECT *policy = NULL;
+    int i;
+
+    if (ctx->default_policy == NULL) {
+        TSerr(TS_F_TS_RESP_GET_POLICY, TS_R_INVALID_NULL_POINTER);
+        return NULL;
+    }
+    if (!requested || !OBJ_cmp(requested, ctx->default_policy))
+        policy = ctx->default_policy;
+
+    /* Check if the policy is acceptable. */
+    for (i = 0; !policy && i < sk_ASN1_OBJECT_num(ctx->policies); ++i) {
+        ASN1_OBJECT *current = sk_ASN1_OBJECT_value(ctx->policies, i);
+        if (!OBJ_cmp(requested, current))
+            policy = current;
+    }
+    if (!policy) {
+        TSerr(TS_F_TS_RESP_GET_POLICY, TS_R_UNACCEPTABLE_POLICY);
+        TS_RESP_CTX_set_status_info(ctx, TS_STATUS_REJECTION,
+                                    "Requested policy is not " "supported.");
+        TS_RESP_CTX_add_failure_info(ctx, TS_INFO_UNACCEPTED_POLICY);
+    }
+    return policy;
+}
+
+/* Creates the TS_TST_INFO object based on the settings of the context. */
+static TS_TST_INFO *ts_RESP_create_tst_info(TS_RESP_CTX *ctx,
+                                            ASN1_OBJECT *policy)
+{
+    int result = 0;
+    TS_TST_INFO *tst_info = NULL;
+    ASN1_INTEGER *serial = NULL;
+    ASN1_GENERALIZEDTIME *asn1_time = NULL;
+    long sec, usec;
+    TS_ACCURACY *accuracy = NULL;
+    const ASN1_INTEGER *nonce;
+    GENERAL_NAME *tsa_name = NULL;
+
+    if ((tst_info = TS_TST_INFO_new()) == NULL)
+        goto end;
+    if (!TS_TST_INFO_set_version(tst_info, 1))
+        goto end;
+    if (!TS_TST_INFO_set_policy_id(tst_info, policy))
+        goto end;
+    if (!TS_TST_INFO_set_msg_imprint(tst_info, ctx->request->msg_imprint))
+        goto end;
+    if ((serial = ctx->serial_cb(ctx, ctx->serial_cb_data)) == NULL
+        || !TS_TST_INFO_set_serial(tst_info, serial))
+        goto end;
+    if (!ctx->time_cb(ctx, ctx->time_cb_data, &sec, &usec)
+        || (asn1_time =
+            TS_RESP_set_genTime_with_precision(NULL, sec, usec,
+                                        ctx->clock_precision_digits)) == NULL
+        || !TS_TST_INFO_set_time(tst_info, asn1_time))
+        goto end;
+
+    if ((ctx->seconds || ctx->millis || ctx->micros)
+        && (accuracy = TS_ACCURACY_new()) == NULL)
+        goto end;
+    if (ctx->seconds && !TS_ACCURACY_set_seconds(accuracy, ctx->seconds))
+        goto end;
+    if (ctx->millis && !TS_ACCURACY_set_millis(accuracy, ctx->millis))
+        goto end;
+    if (ctx->micros && !TS_ACCURACY_set_micros(accuracy, ctx->micros))
+        goto end;
+    if (accuracy && !TS_TST_INFO_set_accuracy(tst_info, accuracy))
+        goto end;
+
+    if ((ctx->flags & TS_ORDERING)
+        && !TS_TST_INFO_set_ordering(tst_info, 1))
+        goto end;
+
+    if ((nonce = ctx->request->nonce) != NULL
+        && !TS_TST_INFO_set_nonce(tst_info, nonce))
+        goto end;
+
+    if (ctx->flags & TS_TSA_NAME) {
+        if ((tsa_name = GENERAL_NAME_new()) == NULL)
+            goto end;
+        tsa_name->type = GEN_DIRNAME;
+        tsa_name->d.dirn =
+            X509_NAME_dup(X509_get_subject_name(ctx->signer_cert));
+        if (!tsa_name->d.dirn)
+            goto end;
+        if (!TS_TST_INFO_set_tsa(tst_info, tsa_name))
+            goto end;
+    }
+
+    result = 1;
+ end:
+    if (!result) {
+        TS_TST_INFO_free(tst_info);
+        tst_info = NULL;
+        TSerr(TS_F_TS_RESP_CREATE_TST_INFO, TS_R_TST_INFO_SETUP_ERROR);
+        TS_RESP_CTX_set_status_info_cond(ctx, TS_STATUS_REJECTION,
+                                         "Error during TSTInfo "
+                                         "generation.");
+    }
+    GENERAL_NAME_free(tsa_name);
+    TS_ACCURACY_free(accuracy);
+    ASN1_GENERALIZEDTIME_free(asn1_time);
+    ASN1_INTEGER_free(serial);
+
+    return tst_info;
+}
+
+/* Processing the extensions of the request. */
+static int ts_RESP_process_extensions(TS_RESP_CTX *ctx)
+{
+    STACK_OF(X509_EXTENSION) *exts = ctx->request->extensions;
+    int i;
+    int ok = 1;
+
+    for (i = 0; ok && i < sk_X509_EXTENSION_num(exts); ++i) {
+        X509_EXTENSION *ext = sk_X509_EXTENSION_value(exts, i);
+        /*
+         * The last argument was previously (void *)ctx->extension_cb,
+         * but ISO C doesn't permit converting a function pointer to void *.
+         * For lack of better information, I'm placing a NULL there instead.
+         * The callback can pick its own address out from the ctx anyway...
+         */
+        ok = (*ctx->extension_cb) (ctx, ext, NULL);
+    }
+
+    return ok;
+}
+
+/* Functions for signing the TS_TST_INFO structure of the context. */
+static int ts_RESP_sign(TS_RESP_CTX *ctx)
+{
+    int ret = 0;
+    PKCS7 *p7 = NULL;
+    PKCS7_SIGNER_INFO *si;
+    STACK_OF(X509) *certs;      /* Certificates to include in sc. */
+    ESS_SIGNING_CERT *sc = NULL;
+    ASN1_OBJECT *oid;
+    BIO *p7bio = NULL;
+    int i;
+
+    if (!X509_check_private_key(ctx->signer_cert, ctx->signer_key)) {
+        TSerr(TS_F_TS_RESP_SIGN, TS_R_PRIVATE_KEY_DOES_NOT_MATCH_CERTIFICATE);
+        goto err;
+    }
+
+    if ((p7 = PKCS7_new()) == NULL) {
+        TSerr(TS_F_TS_RESP_SIGN, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+    if (!PKCS7_set_type(p7, NID_pkcs7_signed))
+        goto err;
+    if (!ASN1_INTEGER_set(p7->d.sign->version, 3))
+        goto err;
+
+    if (ctx->request->cert_req) {
+        PKCS7_add_certificate(p7, ctx->signer_cert);
+        if (ctx->certs) {
+            for (i = 0; i < sk_X509_num(ctx->certs); ++i) {
+                X509 *cert = sk_X509_value(ctx->certs, i);
+                PKCS7_add_certificate(p7, cert);
+            }
+        }
+    }
+
+    if ((si = PKCS7_add_signature(p7, ctx->signer_cert,
+                                  ctx->signer_key, ctx->signer_md)) == NULL) {
+        TSerr(TS_F_TS_RESP_SIGN, TS_R_PKCS7_ADD_SIGNATURE_ERROR);
+        goto err;
+    }
+
+    oid = OBJ_nid2obj(NID_id_smime_ct_TSTInfo);
+    if (!PKCS7_add_signed_attribute(si, NID_pkcs9_contentType,
+                                    V_ASN1_OBJECT, oid)) {
+        TSerr(TS_F_TS_RESP_SIGN, TS_R_PKCS7_ADD_SIGNED_ATTR_ERROR);
+        goto err;
+    }
+
+    certs = ctx->flags & TS_ESS_CERT_ID_CHAIN ? ctx->certs : NULL;
+    if ((sc = ess_SIGNING_CERT_new_init(ctx->signer_cert, certs)) == NULL)
+        goto err;
+    if (!ESS_add_signing_cert(si, sc)) {
+        TSerr(TS_F_TS_RESP_SIGN, TS_R_ESS_ADD_SIGNING_CERT_ERROR);
+        goto err;
+    }
+
+    if (!ts_TST_INFO_content_new(p7))
+        goto err;
+    if ((p7bio = PKCS7_dataInit(p7, NULL)) == NULL) {
+        TSerr(TS_F_TS_RESP_SIGN, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+    if (!i2d_TS_TST_INFO_bio(p7bio, ctx->tst_info)) {
+        TSerr(TS_F_TS_RESP_SIGN, TS_R_TS_DATASIGN);
+        goto err;
+    }
+    if (!PKCS7_dataFinal(p7, p7bio)) {
+        TSerr(TS_F_TS_RESP_SIGN, TS_R_TS_DATASIGN);
+        goto err;
+    }
+    TS_RESP_set_tst_info(ctx->response, p7, ctx->tst_info);
+    p7 = NULL;                  /* Ownership is lost. */
+    ctx->tst_info = NULL;       /* Ownership is lost. */
+
+    ret = 1;
+ err:
+    if (!ret)
+        TS_RESP_CTX_set_status_info_cond(ctx, TS_STATUS_REJECTION,
+                                         "Error during signature "
+                                         "generation.");
+    BIO_free_all(p7bio);
+    ESS_SIGNING_CERT_free(sc);
+    PKCS7_free(p7);
+    return ret;
+}
+
+static ESS_SIGNING_CERT *ess_SIGNING_CERT_new_init(X509 *signcert,
+                                                   STACK_OF(X509) *certs)
+{
+    ESS_CERT_ID *cid;
+    ESS_SIGNING_CERT *sc = NULL;
+    int i;
+
+    if ((sc = ESS_SIGNING_CERT_new()) == NULL)
+        goto err;
+    if (sc->cert_ids == NULL
+        && (sc->cert_ids = sk_ESS_CERT_ID_new_null()) == NULL)
+        goto err;
+
+    if ((cid = ess_CERT_ID_new_init(signcert, 0)) == NULL
+        || !sk_ESS_CERT_ID_push(sc->cert_ids, cid))
+        goto err;
+    for (i = 0; i < sk_X509_num(certs); ++i) {
+        X509 *cert = sk_X509_value(certs, i);
+        if ((cid = ess_CERT_ID_new_init(cert, 1)) == NULL
+            || !sk_ESS_CERT_ID_push(sc->cert_ids, cid))
+            goto err;
+    }
+
+    return sc;
+ err:
+    ESS_SIGNING_CERT_free(sc);
+    TSerr(TS_F_ESS_SIGNING_CERT_NEW_INIT, ERR_R_MALLOC_FAILURE);
+    return NULL;
+}
+
+static ESS_CERT_ID *ess_CERT_ID_new_init(X509 *cert, int issuer_needed)
+{
+    ESS_CERT_ID *cid = NULL;
+    GENERAL_NAME *name = NULL;
+    unsigned char cert_sha1[SHA_DIGEST_LENGTH];
+
+    /* Call for side-effect of computing hash and caching extensions */
+    X509_check_purpose(cert, -1, 0);
+    if ((cid = ESS_CERT_ID_new()) == NULL)
+        goto err;
+    X509_digest(cert, EVP_sha1(), cert_sha1, NULL);
+    if (!ASN1_OCTET_STRING_set(cid->hash, cert_sha1, SHA_DIGEST_LENGTH))
+        goto err;
+
+    /* Setting the issuer/serial if requested. */
+    if (issuer_needed) {
+        if (cid->issuer_serial == NULL
+            && (cid->issuer_serial = ESS_ISSUER_SERIAL_new()) == NULL)
+            goto err;
+        if ((name = GENERAL_NAME_new()) == NULL)
+            goto err;
+        name->type = GEN_DIRNAME;
+        if ((name->d.dirn = X509_NAME_dup(X509_get_issuer_name(cert))) == NULL)
+            goto err;
+        if (!sk_GENERAL_NAME_push(cid->issuer_serial->issuer, name))
+            goto err;
+        name = NULL;            /* Ownership is lost. */
+        ASN1_INTEGER_free(cid->issuer_serial->serial);
+        if (!(cid->issuer_serial->serial =
+              ASN1_INTEGER_dup(X509_get_serialNumber(cert))))
+            goto err;
+    }
+
+    return cid;
+ err:
+    GENERAL_NAME_free(name);
+    ESS_CERT_ID_free(cid);
+    TSerr(TS_F_ESS_CERT_ID_NEW_INIT, ERR_R_MALLOC_FAILURE);
+    return NULL;
+}
+
+static int ts_TST_INFO_content_new(PKCS7 *p7)
+{
+    PKCS7 *ret = NULL;
+    ASN1_OCTET_STRING *octet_string = NULL;
+
+    /* Create new encapsulated NID_id_smime_ct_TSTInfo content. */
+    if ((ret = PKCS7_new()) == NULL)
+        goto err;
+    if ((ret->d.other = ASN1_TYPE_new()) == NULL)
+        goto err;
+    ret->type = OBJ_nid2obj(NID_id_smime_ct_TSTInfo);
+    if ((octet_string = ASN1_OCTET_STRING_new()) == NULL)
+        goto err;
+    ASN1_TYPE_set(ret->d.other, V_ASN1_OCTET_STRING, octet_string);
+    octet_string = NULL;
+
+    /* Add encapsulated content to signed PKCS7 structure. */
+    if (!PKCS7_set_content(p7, ret))
+        goto err;
+
+    return 1;
+ err:
+    ASN1_OCTET_STRING_free(octet_string);
+    PKCS7_free(ret);
+    return 0;
+}
+
+static int ESS_add_signing_cert(PKCS7_SIGNER_INFO *si, ESS_SIGNING_CERT *sc)
+{
+    ASN1_STRING *seq = NULL;
+    unsigned char *p, *pp = NULL;
+    int len;
+
+    len = i2d_ESS_SIGNING_CERT(sc, NULL);
+    if ((pp = OPENSSL_malloc(len)) == NULL) {
+        TSerr(TS_F_ESS_ADD_SIGNING_CERT, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+    p = pp;
+    i2d_ESS_SIGNING_CERT(sc, &p);
+    if ((seq = ASN1_STRING_new()) == NULL || !ASN1_STRING_set(seq, pp, len)) {
+        TSerr(TS_F_ESS_ADD_SIGNING_CERT, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+    OPENSSL_free(pp);
+    pp = NULL;
+    return PKCS7_add_signed_attribute(si,
+                                      NID_id_smime_aa_signingCertificate,
+                                      V_ASN1_SEQUENCE, seq);
+ err:
+    ASN1_STRING_free(seq);
+    OPENSSL_free(pp);
+
+    return 0;
+}
+
+static ASN1_GENERALIZEDTIME
+*TS_RESP_set_genTime_with_precision(ASN1_GENERALIZEDTIME *asn1_time,
+                                    long sec, long usec, unsigned precision)
+{
+    time_t time_sec = (time_t)sec;
+    struct tm *tm = NULL;
+    char genTime_str[17 + TS_MAX_CLOCK_PRECISION_DIGITS];
+    char *p = genTime_str;
+    char *p_end = genTime_str + sizeof(genTime_str);
+
+    if (precision > TS_MAX_CLOCK_PRECISION_DIGITS)
+        goto err;
+
+    if ((tm = gmtime(&time_sec)) == NULL)
+        goto err;
+
+    /*
+     * Put "genTime_str" in GeneralizedTime format.  We work around the
+     * restrictions imposed by rfc3280 (i.e. "GeneralizedTime values MUST
+     * NOT include fractional seconds") and OpenSSL related functions to
+     * meet the rfc3161 requirement: "GeneralizedTime syntax can include
+     * fraction-of-second details".
+     */
+    p += BIO_snprintf(p, p_end - p,
+                      "%04d%02d%02d%02d%02d%02d",
+                      tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
+                      tm->tm_hour, tm->tm_min, tm->tm_sec);
+    if (precision > 0) {
+        BIO_snprintf(p, 2 + precision, ".%06ld", usec);
+        p += strlen(p);
+
+        /*
+         * To make things a bit harder, X.690 | ISO/IEC 8825-1 provides the
+         * following restrictions for a DER-encoding, which OpenSSL
+         * (specifically ASN1_GENERALIZEDTIME_check() function) doesn't
+         * support: "The encoding MUST terminate with a "Z" (which means
+         * "Zulu" time). The decimal point element, if present, MUST be the
+         * point option ".". The fractional-seconds elements, if present,
+         * MUST omit all trailing 0's; if the elements correspond to 0, they
+         * MUST be wholly omitted, and the decimal point element also MUST be
+         * omitted."
+         */
+        /*
+         * Remove trailing zeros. The dot guarantees the exit condition of
+         * this loop even if all the digits are zero.
+         */
+        while (*--p == '0')
+             continue;
+        if (*p != '.')
+            ++p;
+    }
+    *p++ = 'Z';
+    *p++ = '\0';
+
+    if (asn1_time == NULL
+        && (asn1_time = ASN1_GENERALIZEDTIME_new()) == NULL)
+        goto err;
+    if (!ASN1_GENERALIZEDTIME_set_string(asn1_time, genTime_str)) {
+        ASN1_GENERALIZEDTIME_free(asn1_time);
+        goto err;
+    }
+    return asn1_time;
+
+ err:
+    TSerr(TS_F_TS_RESP_SET_GENTIME_WITH_PRECISION, TS_R_COULD_NOT_SET_TIME);
+    return NULL;
+}
diff --git a/crypto/ts/ts_rsp_utils.c b/crypto/ts/ts_rsp_utils.c
new file mode 100644
index 000000000..3ecee39a2
--- /dev/null
+++ b/crypto/ts/ts_rsp_utils.c
@@ -0,0 +1,365 @@
+/*
+ * Copyright 2006-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+#include 
+#include "ts_lcl.h"
+
+int TS_RESP_set_status_info(TS_RESP *a, TS_STATUS_INFO *status_info)
+{
+    TS_STATUS_INFO *new_status_info;
+
+    if (a->status_info == status_info)
+        return 1;
+    new_status_info = TS_STATUS_INFO_dup(status_info);
+    if (new_status_info == NULL) {
+        TSerr(TS_F_TS_RESP_SET_STATUS_INFO, ERR_R_MALLOC_FAILURE);
+        return 0;
+    }
+    TS_STATUS_INFO_free(a->status_info);
+    a->status_info = new_status_info;
+
+    return 1;
+}
+
+TS_STATUS_INFO *TS_RESP_get_status_info(TS_RESP *a)
+{
+    return a->status_info;
+}
+
+/* Caller loses ownership of PKCS7 and TS_TST_INFO objects. */
+void TS_RESP_set_tst_info(TS_RESP *a, PKCS7 *p7, TS_TST_INFO *tst_info)
+{
+    PKCS7_free(a->token);
+    a->token = p7;
+    TS_TST_INFO_free(a->tst_info);
+    a->tst_info = tst_info;
+}
+
+PKCS7 *TS_RESP_get_token(TS_RESP *a)
+{
+    return a->token;
+}
+
+TS_TST_INFO *TS_RESP_get_tst_info(TS_RESP *a)
+{
+    return a->tst_info;
+}
+
+int TS_TST_INFO_set_version(TS_TST_INFO *a, long version)
+{
+    return ASN1_INTEGER_set(a->version, version);
+}
+
+long TS_TST_INFO_get_version(const TS_TST_INFO *a)
+{
+    return ASN1_INTEGER_get(a->version);
+}
+
+int TS_TST_INFO_set_policy_id(TS_TST_INFO *a, ASN1_OBJECT *policy)
+{
+    ASN1_OBJECT *new_policy;
+
+    if (a->policy_id == policy)
+        return 1;
+    new_policy = OBJ_dup(policy);
+    if (new_policy == NULL) {
+        TSerr(TS_F_TS_TST_INFO_SET_POLICY_ID, ERR_R_MALLOC_FAILURE);
+        return 0;
+    }
+    ASN1_OBJECT_free(a->policy_id);
+    a->policy_id = new_policy;
+    return 1;
+}
+
+ASN1_OBJECT *TS_TST_INFO_get_policy_id(TS_TST_INFO *a)
+{
+    return a->policy_id;
+}
+
+int TS_TST_INFO_set_msg_imprint(TS_TST_INFO *a, TS_MSG_IMPRINT *msg_imprint)
+{
+    TS_MSG_IMPRINT *new_msg_imprint;
+
+    if (a->msg_imprint == msg_imprint)
+        return 1;
+    new_msg_imprint = TS_MSG_IMPRINT_dup(msg_imprint);
+    if (new_msg_imprint == NULL) {
+        TSerr(TS_F_TS_TST_INFO_SET_MSG_IMPRINT, ERR_R_MALLOC_FAILURE);
+        return 0;
+    }
+    TS_MSG_IMPRINT_free(a->msg_imprint);
+    a->msg_imprint = new_msg_imprint;
+    return 1;
+}
+
+TS_MSG_IMPRINT *TS_TST_INFO_get_msg_imprint(TS_TST_INFO *a)
+{
+    return a->msg_imprint;
+}
+
+int TS_TST_INFO_set_serial(TS_TST_INFO *a, const ASN1_INTEGER *serial)
+{
+    ASN1_INTEGER *new_serial;
+
+    if (a->serial == serial)
+        return 1;
+    new_serial = ASN1_INTEGER_dup(serial);
+    if (new_serial == NULL) {
+        TSerr(TS_F_TS_TST_INFO_SET_SERIAL, ERR_R_MALLOC_FAILURE);
+        return 0;
+    }
+    ASN1_INTEGER_free(a->serial);
+    a->serial = new_serial;
+    return 1;
+}
+
+const ASN1_INTEGER *TS_TST_INFO_get_serial(const TS_TST_INFO *a)
+{
+    return a->serial;
+}
+
+int TS_TST_INFO_set_time(TS_TST_INFO *a, const ASN1_GENERALIZEDTIME *gtime)
+{
+    ASN1_GENERALIZEDTIME *new_time;
+
+    if (a->time == gtime)
+        return 1;
+    new_time = ASN1_STRING_dup(gtime);
+    if (new_time == NULL) {
+        TSerr(TS_F_TS_TST_INFO_SET_TIME, ERR_R_MALLOC_FAILURE);
+        return 0;
+    }
+    ASN1_GENERALIZEDTIME_free(a->time);
+    a->time = new_time;
+    return 1;
+}
+
+const ASN1_GENERALIZEDTIME *TS_TST_INFO_get_time(const TS_TST_INFO *a)
+{
+    return a->time;
+}
+
+int TS_TST_INFO_set_accuracy(TS_TST_INFO *a, TS_ACCURACY *accuracy)
+{
+    TS_ACCURACY *new_accuracy;
+
+    if (a->accuracy == accuracy)
+        return 1;
+    new_accuracy = TS_ACCURACY_dup(accuracy);
+    if (new_accuracy == NULL) {
+        TSerr(TS_F_TS_TST_INFO_SET_ACCURACY, ERR_R_MALLOC_FAILURE);
+        return 0;
+    }
+    TS_ACCURACY_free(a->accuracy);
+    a->accuracy = new_accuracy;
+    return 1;
+}
+
+TS_ACCURACY *TS_TST_INFO_get_accuracy(TS_TST_INFO *a)
+{
+    return a->accuracy;
+}
+
+int TS_ACCURACY_set_seconds(TS_ACCURACY *a, const ASN1_INTEGER *seconds)
+{
+    ASN1_INTEGER *new_seconds;
+
+    if (a->seconds == seconds)
+        return 1;
+    new_seconds = ASN1_INTEGER_dup(seconds);
+    if (new_seconds == NULL) {
+        TSerr(TS_F_TS_ACCURACY_SET_SECONDS, ERR_R_MALLOC_FAILURE);
+        return 0;
+    }
+    ASN1_INTEGER_free(a->seconds);
+    a->seconds = new_seconds;
+    return 1;
+}
+
+const ASN1_INTEGER *TS_ACCURACY_get_seconds(const TS_ACCURACY *a)
+{
+    return a->seconds;
+}
+
+int TS_ACCURACY_set_millis(TS_ACCURACY *a, const ASN1_INTEGER *millis)
+{
+    ASN1_INTEGER *new_millis = NULL;
+
+    if (a->millis == millis)
+        return 1;
+    if (millis != NULL) {
+        new_millis = ASN1_INTEGER_dup(millis);
+        if (new_millis == NULL) {
+            TSerr(TS_F_TS_ACCURACY_SET_MILLIS, ERR_R_MALLOC_FAILURE);
+            return 0;
+        }
+    }
+    ASN1_INTEGER_free(a->millis);
+    a->millis = new_millis;
+    return 1;
+}
+
+const ASN1_INTEGER *TS_ACCURACY_get_millis(const TS_ACCURACY *a)
+{
+    return a->millis;
+}
+
+int TS_ACCURACY_set_micros(TS_ACCURACY *a, const ASN1_INTEGER *micros)
+{
+    ASN1_INTEGER *new_micros = NULL;
+
+    if (a->micros == micros)
+        return 1;
+    if (micros != NULL) {
+        new_micros = ASN1_INTEGER_dup(micros);
+        if (new_micros == NULL) {
+            TSerr(TS_F_TS_ACCURACY_SET_MICROS, ERR_R_MALLOC_FAILURE);
+            return 0;
+        }
+    }
+    ASN1_INTEGER_free(a->micros);
+    a->micros = new_micros;
+    return 1;
+}
+
+const ASN1_INTEGER *TS_ACCURACY_get_micros(const TS_ACCURACY *a)
+{
+    return a->micros;
+}
+
+int TS_TST_INFO_set_ordering(TS_TST_INFO *a, int ordering)
+{
+    a->ordering = ordering ? 0xFF : 0x00;
+    return 1;
+}
+
+int TS_TST_INFO_get_ordering(const TS_TST_INFO *a)
+{
+    return a->ordering ? 1 : 0;
+}
+
+int TS_TST_INFO_set_nonce(TS_TST_INFO *a, const ASN1_INTEGER *nonce)
+{
+    ASN1_INTEGER *new_nonce;
+
+    if (a->nonce == nonce)
+        return 1;
+    new_nonce = ASN1_INTEGER_dup(nonce);
+    if (new_nonce == NULL) {
+        TSerr(TS_F_TS_TST_INFO_SET_NONCE, ERR_R_MALLOC_FAILURE);
+        return 0;
+    }
+    ASN1_INTEGER_free(a->nonce);
+    a->nonce = new_nonce;
+    return 1;
+}
+
+const ASN1_INTEGER *TS_TST_INFO_get_nonce(const TS_TST_INFO *a)
+{
+    return a->nonce;
+}
+
+int TS_TST_INFO_set_tsa(TS_TST_INFO *a, GENERAL_NAME *tsa)
+{
+    GENERAL_NAME *new_tsa;
+
+    if (a->tsa == tsa)
+        return 1;
+    new_tsa = GENERAL_NAME_dup(tsa);
+    if (new_tsa == NULL) {
+        TSerr(TS_F_TS_TST_INFO_SET_TSA, ERR_R_MALLOC_FAILURE);
+        return 0;
+    }
+    GENERAL_NAME_free(a->tsa);
+    a->tsa = new_tsa;
+    return 1;
+}
+
+GENERAL_NAME *TS_TST_INFO_get_tsa(TS_TST_INFO *a)
+{
+    return a->tsa;
+}
+
+STACK_OF(X509_EXTENSION) *TS_TST_INFO_get_exts(TS_TST_INFO *a)
+{
+    return a->extensions;
+}
+
+void TS_TST_INFO_ext_free(TS_TST_INFO *a)
+{
+    if (!a)
+        return;
+    sk_X509_EXTENSION_pop_free(a->extensions, X509_EXTENSION_free);
+    a->extensions = NULL;
+}
+
+int TS_TST_INFO_get_ext_count(TS_TST_INFO *a)
+{
+    return X509v3_get_ext_count(a->extensions);
+}
+
+int TS_TST_INFO_get_ext_by_NID(TS_TST_INFO *a, int nid, int lastpos)
+{
+    return X509v3_get_ext_by_NID(a->extensions, nid, lastpos);
+}
+
+int TS_TST_INFO_get_ext_by_OBJ(TS_TST_INFO *a, const ASN1_OBJECT *obj, int lastpos)
+{
+    return X509v3_get_ext_by_OBJ(a->extensions, obj, lastpos);
+}
+
+int TS_TST_INFO_get_ext_by_critical(TS_TST_INFO *a, int crit, int lastpos)
+{
+    return X509v3_get_ext_by_critical(a->extensions, crit, lastpos);
+}
+
+X509_EXTENSION *TS_TST_INFO_get_ext(TS_TST_INFO *a, int loc)
+{
+    return X509v3_get_ext(a->extensions, loc);
+}
+
+X509_EXTENSION *TS_TST_INFO_delete_ext(TS_TST_INFO *a, int loc)
+{
+    return X509v3_delete_ext(a->extensions, loc);
+}
+
+int TS_TST_INFO_add_ext(TS_TST_INFO *a, X509_EXTENSION *ex, int loc)
+{
+    return X509v3_add_ext(&a->extensions, ex, loc) != NULL;
+}
+
+void *TS_TST_INFO_get_ext_d2i(TS_TST_INFO *a, int nid, int *crit, int *idx)
+{
+    return X509V3_get_d2i(a->extensions, nid, crit, idx);
+}
+
+int TS_STATUS_INFO_set_status(TS_STATUS_INFO *a, int i)
+{
+    return ASN1_INTEGER_set(a->status, i);
+}
+
+const ASN1_INTEGER *TS_STATUS_INFO_get0_status(const TS_STATUS_INFO *a)
+{
+    return a->status;
+}
+
+const STACK_OF(ASN1_UTF8STRING) *
+TS_STATUS_INFO_get0_text(const TS_STATUS_INFO *a)
+{
+    return a->text;
+}
+
+const ASN1_BIT_STRING *TS_STATUS_INFO_get0_failure_info(const TS_STATUS_INFO *a)
+{
+    return a->failure_info;
+}
diff --git a/crypto/ts/ts_rsp_verify.c b/crypto/ts/ts_rsp_verify.c
new file mode 100644
index 000000000..2755dd0ef
--- /dev/null
+++ b/crypto/ts/ts_rsp_verify.c
@@ -0,0 +1,635 @@
+/*
+ * Copyright 2006-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+#include 
+#include "ts_lcl.h"
+
+static int ts_verify_cert(X509_STORE *store, STACK_OF(X509) *untrusted,
+                          X509 *signer, STACK_OF(X509) **chain);
+static int ts_check_signing_certs(PKCS7_SIGNER_INFO *si,
+                                  STACK_OF(X509) *chain);
+static ESS_SIGNING_CERT *ess_get_signing_cert(PKCS7_SIGNER_INFO *si);
+static int ts_find_cert(STACK_OF(ESS_CERT_ID) *cert_ids, X509 *cert);
+static int ts_issuer_serial_cmp(ESS_ISSUER_SERIAL *is, X509 *cert);
+static int int_ts_RESP_verify_token(TS_VERIFY_CTX *ctx,
+                                    PKCS7 *token, TS_TST_INFO *tst_info);
+static int ts_check_status_info(TS_RESP *response);
+static char *ts_get_status_text(STACK_OF(ASN1_UTF8STRING) *text);
+static int ts_check_policy(const ASN1_OBJECT *req_oid,
+                           const TS_TST_INFO *tst_info);
+static int ts_compute_imprint(BIO *data, TS_TST_INFO *tst_info,
+                              X509_ALGOR **md_alg,
+                              unsigned char **imprint, unsigned *imprint_len);
+static int ts_check_imprints(X509_ALGOR *algor_a,
+                             const unsigned char *imprint_a, unsigned len_a,
+                             TS_TST_INFO *tst_info);
+static int ts_check_nonces(const ASN1_INTEGER *a, TS_TST_INFO *tst_info);
+static int ts_check_signer_name(GENERAL_NAME *tsa_name, X509 *signer);
+static int ts_find_name(STACK_OF(GENERAL_NAME) *gen_names,
+                        GENERAL_NAME *name);
+
+/*
+ * This must be large enough to hold all values in ts_status_text (with
+ * comma separator) or all text fields in ts_failure_info (also with comma).
+ */
+#define TS_STATUS_BUF_SIZE      256
+
+/*
+ * Local mapping between response codes and descriptions.
+ */
+static const char *ts_status_text[] = {
+    "granted",
+    "grantedWithMods",
+    "rejection",
+    "waiting",
+    "revocationWarning",
+    "revocationNotification"
+};
+
+#define TS_STATUS_TEXT_SIZE     OSSL_NELEM(ts_status_text)
+
+static struct {
+    int code;
+    const char *text;
+} ts_failure_info[] = {
+    {TS_INFO_BAD_ALG, "badAlg"},
+    {TS_INFO_BAD_REQUEST, "badRequest"},
+    {TS_INFO_BAD_DATA_FORMAT, "badDataFormat"},
+    {TS_INFO_TIME_NOT_AVAILABLE, "timeNotAvailable"},
+    {TS_INFO_UNACCEPTED_POLICY, "unacceptedPolicy"},
+    {TS_INFO_UNACCEPTED_EXTENSION, "unacceptedExtension"},
+    {TS_INFO_ADD_INFO_NOT_AVAILABLE, "addInfoNotAvailable"},
+    {TS_INFO_SYSTEM_FAILURE, "systemFailure"}
+};
+
+
+/*-
+ * This function carries out the following tasks:
+ *      - Checks if there is one and only one signer.
+ *      - Search for the signing certificate in 'certs' and in the response.
+ *      - Check the extended key usage and key usage fields of the signer
+ *      certificate (done by the path validation).
+ *      - Build and validate the certificate path.
+ *      - Check if the certificate path meets the requirements of the
+ *      SigningCertificate ESS signed attribute.
+ *      - Verify the signature value.
+ *      - Returns the signer certificate in 'signer', if 'signer' is not NULL.
+ */
+int TS_RESP_verify_signature(PKCS7 *token, STACK_OF(X509) *certs,
+                             X509_STORE *store, X509 **signer_out)
+{
+    STACK_OF(PKCS7_SIGNER_INFO) *sinfos = NULL;
+    PKCS7_SIGNER_INFO *si;
+    STACK_OF(X509) *signers = NULL;
+    X509 *signer;
+    STACK_OF(X509) *chain = NULL;
+    char buf[4096];
+    int i, j = 0, ret = 0;
+    BIO *p7bio = NULL;
+
+    /* Some sanity checks first. */
+    if (!token) {
+        TSerr(TS_F_TS_RESP_VERIFY_SIGNATURE, TS_R_INVALID_NULL_POINTER);
+        goto err;
+    }
+    if (!PKCS7_type_is_signed(token)) {
+        TSerr(TS_F_TS_RESP_VERIFY_SIGNATURE, TS_R_WRONG_CONTENT_TYPE);
+        goto err;
+    }
+    sinfos = PKCS7_get_signer_info(token);
+    if (!sinfos || sk_PKCS7_SIGNER_INFO_num(sinfos) != 1) {
+        TSerr(TS_F_TS_RESP_VERIFY_SIGNATURE, TS_R_THERE_MUST_BE_ONE_SIGNER);
+        goto err;
+    }
+    si = sk_PKCS7_SIGNER_INFO_value(sinfos, 0);
+    if (PKCS7_get_detached(token)) {
+        TSerr(TS_F_TS_RESP_VERIFY_SIGNATURE, TS_R_NO_CONTENT);
+        goto err;
+    }
+
+    /*
+     * Get hold of the signer certificate, search only internal certificates
+     * if it was requested.
+     */
+    signers = PKCS7_get0_signers(token, certs, 0);
+    if (!signers || sk_X509_num(signers) != 1)
+        goto err;
+    signer = sk_X509_value(signers, 0);
+
+    if (!ts_verify_cert(store, certs, signer, &chain))
+        goto err;
+    if (!ts_check_signing_certs(si, chain))
+        goto err;
+    p7bio = PKCS7_dataInit(token, NULL);
+
+    /* We now have to 'read' from p7bio to calculate digests etc. */
+    while ((i = BIO_read(p7bio, buf, sizeof(buf))) > 0)
+        continue;
+
+    j = PKCS7_signatureVerify(p7bio, token, si, signer);
+    if (j <= 0) {
+        TSerr(TS_F_TS_RESP_VERIFY_SIGNATURE, TS_R_SIGNATURE_FAILURE);
+        goto err;
+    }
+
+    if (signer_out) {
+        *signer_out = signer;
+        X509_up_ref(signer);
+    }
+    ret = 1;
+
+ err:
+    BIO_free_all(p7bio);
+    sk_X509_pop_free(chain, X509_free);
+    sk_X509_free(signers);
+
+    return ret;
+}
+
+/*
+ * The certificate chain is returned in chain. Caller is responsible for
+ * freeing the vector.
+ */
+static int ts_verify_cert(X509_STORE *store, STACK_OF(X509) *untrusted,
+                          X509 *signer, STACK_OF(X509) **chain)
+{
+    X509_STORE_CTX *cert_ctx = NULL;
+    int i;
+    int ret = 0;
+
+    *chain = NULL;
+    cert_ctx = X509_STORE_CTX_new();
+    if (cert_ctx == NULL) {
+        TSerr(TS_F_TS_VERIFY_CERT, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+    if (!X509_STORE_CTX_init(cert_ctx, store, signer, untrusted))
+        goto end;
+    X509_STORE_CTX_set_purpose(cert_ctx, X509_PURPOSE_TIMESTAMP_SIGN);
+    i = X509_verify_cert(cert_ctx);
+    if (i <= 0) {
+        int j = X509_STORE_CTX_get_error(cert_ctx);
+        TSerr(TS_F_TS_VERIFY_CERT, TS_R_CERTIFICATE_VERIFY_ERROR);
+        ERR_add_error_data(2, "Verify error:",
+                           X509_verify_cert_error_string(j));
+        goto err;
+    }
+    *chain = X509_STORE_CTX_get1_chain(cert_ctx);
+    ret = 1;
+    goto end;
+
+err:
+    ret = 0;
+
+end:
+    X509_STORE_CTX_free(cert_ctx);
+    return ret;
+}
+
+static int ts_check_signing_certs(PKCS7_SIGNER_INFO *si,
+                                  STACK_OF(X509) *chain)
+{
+    ESS_SIGNING_CERT *ss = ess_get_signing_cert(si);
+    STACK_OF(ESS_CERT_ID) *cert_ids = NULL;
+    X509 *cert;
+    int i = 0;
+    int ret = 0;
+
+    if (!ss)
+        goto err;
+    cert_ids = ss->cert_ids;
+    cert = sk_X509_value(chain, 0);
+    if (ts_find_cert(cert_ids, cert) != 0)
+        goto err;
+
+    /*
+     * Check the other certificates of the chain if there are more than one
+     * certificate ids in cert_ids.
+     */
+    if (sk_ESS_CERT_ID_num(cert_ids) > 1) {
+        for (i = 1; i < sk_X509_num(chain); ++i) {
+            cert = sk_X509_value(chain, i);
+            if (ts_find_cert(cert_ids, cert) < 0)
+                goto err;
+        }
+    }
+    ret = 1;
+ err:
+    if (!ret)
+        TSerr(TS_F_TS_CHECK_SIGNING_CERTS,
+              TS_R_ESS_SIGNING_CERTIFICATE_ERROR);
+    ESS_SIGNING_CERT_free(ss);
+    return ret;
+}
+
+static ESS_SIGNING_CERT *ess_get_signing_cert(PKCS7_SIGNER_INFO *si)
+{
+    ASN1_TYPE *attr;
+    const unsigned char *p;
+    attr = PKCS7_get_signed_attribute(si, NID_id_smime_aa_signingCertificate);
+    if (!attr)
+        return NULL;
+    p = attr->value.sequence->data;
+    return d2i_ESS_SIGNING_CERT(NULL, &p, attr->value.sequence->length);
+}
+
+/* Returns < 0 if certificate is not found, certificate index otherwise. */
+static int ts_find_cert(STACK_OF(ESS_CERT_ID) *cert_ids, X509 *cert)
+{
+    int i;
+    unsigned char cert_sha1[SHA_DIGEST_LENGTH];
+
+    if (!cert_ids || !cert)
+        return -1;
+
+    X509_digest(cert, EVP_sha1(), cert_sha1, NULL);
+
+    /* Recompute SHA1 hash of certificate if necessary (side effect). */
+    X509_check_purpose(cert, -1, 0);
+
+    /* Look for cert in the cert_ids vector. */
+    for (i = 0; i < sk_ESS_CERT_ID_num(cert_ids); ++i) {
+        ESS_CERT_ID *cid = sk_ESS_CERT_ID_value(cert_ids, i);
+
+        if (cid->hash->length == SHA_DIGEST_LENGTH
+            && memcmp(cid->hash->data, cert_sha1, SHA_DIGEST_LENGTH) == 0) {
+            ESS_ISSUER_SERIAL *is = cid->issuer_serial;
+            if (!is || !ts_issuer_serial_cmp(is, cert))
+                return i;
+        }
+    }
+
+    return -1;
+}
+
+static int ts_issuer_serial_cmp(ESS_ISSUER_SERIAL *is, X509 *cert)
+{
+    GENERAL_NAME *issuer;
+
+    if (!is || !cert || sk_GENERAL_NAME_num(is->issuer) != 1)
+        return -1;
+
+    issuer = sk_GENERAL_NAME_value(is->issuer, 0);
+    if (issuer->type != GEN_DIRNAME
+        || X509_NAME_cmp(issuer->d.dirn, X509_get_issuer_name(cert)))
+        return -1;
+
+    if (ASN1_INTEGER_cmp(is->serial, X509_get_serialNumber(cert)))
+        return -1;
+
+    return 0;
+}
+
+/*-
+ * Verifies whether 'response' contains a valid response with regards
+ * to the settings of the context:
+ *      - Gives an error message if the TS_TST_INFO is not present.
+ *      - Calls _TS_RESP_verify_token to verify the token content.
+ */
+int TS_RESP_verify_response(TS_VERIFY_CTX *ctx, TS_RESP *response)
+{
+    PKCS7 *token = response->token;
+    TS_TST_INFO *tst_info = response->tst_info;
+    int ret = 0;
+
+    if (!ts_check_status_info(response))
+        goto err;
+    if (!int_ts_RESP_verify_token(ctx, token, tst_info))
+        goto err;
+    ret = 1;
+
+ err:
+    return ret;
+}
+
+/*
+ * Tries to extract a TS_TST_INFO structure from the PKCS7 token and
+ * calls the internal int_TS_RESP_verify_token function for verifying it.
+ */
+int TS_RESP_verify_token(TS_VERIFY_CTX *ctx, PKCS7 *token)
+{
+    TS_TST_INFO *tst_info = PKCS7_to_TS_TST_INFO(token);
+    int ret = 0;
+    if (tst_info) {
+        ret = int_ts_RESP_verify_token(ctx, token, tst_info);
+        TS_TST_INFO_free(tst_info);
+    }
+    return ret;
+}
+
+/*-
+ * Verifies whether the 'token' contains a valid time stamp token
+ * with regards to the settings of the context. Only those checks are
+ * carried out that are specified in the context:
+ *      - Verifies the signature of the TS_TST_INFO.
+ *      - Checks the version number of the response.
+ *      - Check if the requested and returned policies math.
+ *      - Check if the message imprints are the same.
+ *      - Check if the nonces are the same.
+ *      - Check if the TSA name matches the signer.
+ *      - Check if the TSA name is the expected TSA.
+ */
+static int int_ts_RESP_verify_token(TS_VERIFY_CTX *ctx,
+                                    PKCS7 *token, TS_TST_INFO *tst_info)
+{
+    X509 *signer = NULL;
+    GENERAL_NAME *tsa_name = tst_info->tsa;
+    X509_ALGOR *md_alg = NULL;
+    unsigned char *imprint = NULL;
+    unsigned imprint_len = 0;
+    int ret = 0;
+    int flags = ctx->flags;
+
+    /* Some options require us to also check the signature */
+    if (((flags & TS_VFY_SIGNER) && tsa_name != NULL)
+            || (flags & TS_VFY_TSA_NAME)) {
+        flags |= TS_VFY_SIGNATURE;
+    }
+
+    if ((flags & TS_VFY_SIGNATURE)
+        && !TS_RESP_verify_signature(token, ctx->certs, ctx->store, &signer))
+        goto err;
+    if ((flags & TS_VFY_VERSION)
+        && TS_TST_INFO_get_version(tst_info) != 1) {
+        TSerr(TS_F_INT_TS_RESP_VERIFY_TOKEN, TS_R_UNSUPPORTED_VERSION);
+        goto err;
+    }
+    if ((flags & TS_VFY_POLICY)
+        && !ts_check_policy(ctx->policy, tst_info))
+        goto err;
+    if ((flags & TS_VFY_IMPRINT)
+        && !ts_check_imprints(ctx->md_alg, ctx->imprint, ctx->imprint_len,
+                              tst_info))
+        goto err;
+    if ((flags & TS_VFY_DATA)
+        && (!ts_compute_imprint(ctx->data, tst_info,
+                                &md_alg, &imprint, &imprint_len)
+            || !ts_check_imprints(md_alg, imprint, imprint_len, tst_info)))
+        goto err;
+    if ((flags & TS_VFY_NONCE)
+        && !ts_check_nonces(ctx->nonce, tst_info))
+        goto err;
+    if ((flags & TS_VFY_SIGNER)
+        && tsa_name && !ts_check_signer_name(tsa_name, signer)) {
+        TSerr(TS_F_INT_TS_RESP_VERIFY_TOKEN, TS_R_TSA_NAME_MISMATCH);
+        goto err;
+    }
+    if ((flags & TS_VFY_TSA_NAME)
+        && !ts_check_signer_name(ctx->tsa_name, signer)) {
+        TSerr(TS_F_INT_TS_RESP_VERIFY_TOKEN, TS_R_TSA_UNTRUSTED);
+        goto err;
+    }
+    ret = 1;
+
+ err:
+    X509_free(signer);
+    X509_ALGOR_free(md_alg);
+    OPENSSL_free(imprint);
+    return ret;
+}
+
+static int ts_check_status_info(TS_RESP *response)
+{
+    TS_STATUS_INFO *info = response->status_info;
+    long status = ASN1_INTEGER_get(info->status);
+    const char *status_text = NULL;
+    char *embedded_status_text = NULL;
+    char failure_text[TS_STATUS_BUF_SIZE] = "";
+
+    if (status == 0 || status == 1)
+        return 1;
+
+    /* There was an error, get the description in status_text. */
+    if (0 <= status && status < (long) OSSL_NELEM(ts_status_text))
+        status_text = ts_status_text[status];
+    else
+        status_text = "unknown code";
+
+    if (sk_ASN1_UTF8STRING_num(info->text) > 0
+        && (embedded_status_text = ts_get_status_text(info->text)) == NULL)
+        return 0;
+
+    /* Fill in failure_text with the failure information. */
+    if (info->failure_info) {
+        int i;
+        int first = 1;
+        for (i = 0; i < (int)OSSL_NELEM(ts_failure_info); ++i) {
+            if (ASN1_BIT_STRING_get_bit(info->failure_info,
+                                        ts_failure_info[i].code)) {
+                if (!first)
+                    strcat(failure_text, ",");
+                else
+                    first = 0;
+                strcat(failure_text, ts_failure_info[i].text);
+            }
+        }
+    }
+    if (failure_text[0] == '\0')
+        strcpy(failure_text, "unspecified");
+
+    TSerr(TS_F_TS_CHECK_STATUS_INFO, TS_R_NO_TIME_STAMP_TOKEN);
+    ERR_add_error_data(6,
+                       "status code: ", status_text,
+                       ", status text: ", embedded_status_text ?
+                       embedded_status_text : "unspecified",
+                       ", failure codes: ", failure_text);
+    OPENSSL_free(embedded_status_text);
+
+    return 0;
+}
+
+static char *ts_get_status_text(STACK_OF(ASN1_UTF8STRING) *text)
+{
+    int i;
+    int length = 0;
+    char *result = NULL;
+    char *p;
+
+    for (i = 0; i < sk_ASN1_UTF8STRING_num(text); ++i) {
+        ASN1_UTF8STRING *current = sk_ASN1_UTF8STRING_value(text, i);
+        if (ASN1_STRING_length(current) > TS_MAX_STATUS_LENGTH - length - 1)
+            return NULL;
+        length += ASN1_STRING_length(current);
+        length += 1;            /* separator character */
+    }
+    if ((result = OPENSSL_malloc(length)) == NULL) {
+        TSerr(TS_F_TS_GET_STATUS_TEXT, ERR_R_MALLOC_FAILURE);
+        return NULL;
+    }
+
+    for (i = 0, p = result; i < sk_ASN1_UTF8STRING_num(text); ++i) {
+        ASN1_UTF8STRING *current = sk_ASN1_UTF8STRING_value(text, i);
+        length = ASN1_STRING_length(current);
+        if (i > 0)
+            *p++ = '/';
+        strncpy(p, (const char *)ASN1_STRING_get0_data(current), length);
+        p += length;
+    }
+    *p = '\0';
+
+    return result;
+}
+
+static int ts_check_policy(const ASN1_OBJECT *req_oid, 
+                           const TS_TST_INFO *tst_info)
+{
+    const ASN1_OBJECT *resp_oid = tst_info->policy_id;
+
+    if (OBJ_cmp(req_oid, resp_oid) != 0) {
+        TSerr(TS_F_TS_CHECK_POLICY, TS_R_POLICY_MISMATCH);
+        return 0;
+    }
+
+    return 1;
+}
+
+static int ts_compute_imprint(BIO *data, TS_TST_INFO *tst_info,
+                              X509_ALGOR **md_alg,
+                              unsigned char **imprint, unsigned *imprint_len)
+{
+    TS_MSG_IMPRINT *msg_imprint = tst_info->msg_imprint;
+    X509_ALGOR *md_alg_resp = msg_imprint->hash_algo;
+    const EVP_MD *md;
+    EVP_MD_CTX *md_ctx = NULL;
+    unsigned char buffer[4096];
+    int length;
+
+    *md_alg = NULL;
+    *imprint = NULL;
+
+    if ((*md_alg = X509_ALGOR_dup(md_alg_resp)) == NULL)
+        goto err;
+    if ((md = EVP_get_digestbyobj((*md_alg)->algorithm)) == NULL) {
+        TSerr(TS_F_TS_COMPUTE_IMPRINT, TS_R_UNSUPPORTED_MD_ALGORITHM);
+        goto err;
+    }
+    length = EVP_MD_size(md);
+    if (length < 0)
+        goto err;
+    *imprint_len = length;
+    if ((*imprint = OPENSSL_malloc(*imprint_len)) == NULL) {
+        TSerr(TS_F_TS_COMPUTE_IMPRINT, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+
+    md_ctx = EVP_MD_CTX_new();
+    if (md_ctx == NULL) {
+        TSerr(TS_F_TS_COMPUTE_IMPRINT, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+    if (!EVP_DigestInit(md_ctx, md))
+        goto err;
+    while ((length = BIO_read(data, buffer, sizeof(buffer))) > 0) {
+        if (!EVP_DigestUpdate(md_ctx, buffer, length))
+            goto err;
+    }
+    if (!EVP_DigestFinal(md_ctx, *imprint, NULL))
+        goto err;
+    EVP_MD_CTX_free(md_ctx);
+
+    return 1;
+ err:
+    EVP_MD_CTX_free(md_ctx);
+    X509_ALGOR_free(*md_alg);
+    OPENSSL_free(*imprint);
+    *imprint_len = 0;
+    *imprint = 0;
+    return 0;
+}
+
+static int ts_check_imprints(X509_ALGOR *algor_a,
+                             const unsigned char *imprint_a, unsigned len_a,
+                             TS_TST_INFO *tst_info)
+{
+    TS_MSG_IMPRINT *b = tst_info->msg_imprint;
+    X509_ALGOR *algor_b = b->hash_algo;
+    int ret = 0;
+
+    if (algor_a) {
+        if (OBJ_cmp(algor_a->algorithm, algor_b->algorithm))
+            goto err;
+
+        /* The parameter must be NULL in both. */
+        if ((algor_a->parameter
+             && ASN1_TYPE_get(algor_a->parameter) != V_ASN1_NULL)
+            || (algor_b->parameter
+                && ASN1_TYPE_get(algor_b->parameter) != V_ASN1_NULL))
+            goto err;
+    }
+
+    ret = len_a == (unsigned)ASN1_STRING_length(b->hashed_msg) &&
+        memcmp(imprint_a, ASN1_STRING_get0_data(b->hashed_msg), len_a) == 0;
+ err:
+    if (!ret)
+        TSerr(TS_F_TS_CHECK_IMPRINTS, TS_R_MESSAGE_IMPRINT_MISMATCH);
+    return ret;
+}
+
+static int ts_check_nonces(const ASN1_INTEGER *a, TS_TST_INFO *tst_info)
+{
+    const ASN1_INTEGER *b = tst_info->nonce;
+
+    if (!b) {
+        TSerr(TS_F_TS_CHECK_NONCES, TS_R_NONCE_NOT_RETURNED);
+        return 0;
+    }
+
+    /* No error if a nonce is returned without being requested. */
+    if (ASN1_INTEGER_cmp(a, b) != 0) {
+        TSerr(TS_F_TS_CHECK_NONCES, TS_R_NONCE_MISMATCH);
+        return 0;
+    }
+
+    return 1;
+}
+
+/*
+ * Check if the specified TSA name matches either the subject or one of the
+ * subject alternative names of the TSA certificate.
+ */
+static int ts_check_signer_name(GENERAL_NAME *tsa_name, X509 *signer)
+{
+    STACK_OF(GENERAL_NAME) *gen_names = NULL;
+    int idx = -1;
+    int found = 0;
+
+    if (tsa_name->type == GEN_DIRNAME
+        && X509_name_cmp(tsa_name->d.dirn, X509_get_subject_name(signer)) == 0)
+        return 1;
+    gen_names = X509_get_ext_d2i(signer, NID_subject_alt_name, NULL, &idx);
+    while (gen_names != NULL) {
+        found = ts_find_name(gen_names, tsa_name) >= 0;
+        if (found)
+            break;
+        /*
+         * Get the next subject alternative name, although there should be no
+         * more than one.
+         */
+        GENERAL_NAMES_free(gen_names);
+        gen_names = X509_get_ext_d2i(signer, NID_subject_alt_name, NULL, &idx);
+    }
+    GENERAL_NAMES_free(gen_names);
+
+    return found;
+}
+
+/* Returns 1 if name is in gen_names, 0 otherwise. */
+static int ts_find_name(STACK_OF(GENERAL_NAME) *gen_names, GENERAL_NAME *name)
+{
+    int i, found;
+    for (i = 0, found = 0; !found && i < sk_GENERAL_NAME_num(gen_names); ++i) {
+        GENERAL_NAME *current = sk_GENERAL_NAME_value(gen_names, i);
+        found = GENERAL_NAME_cmp(current, name) == 0;
+    }
+    return found ? i - 1 : -1;
+}
diff --git a/crypto/ts/ts_verify_ctx.c b/crypto/ts/ts_verify_ctx.c
new file mode 100644
index 000000000..d4792ee04
--- /dev/null
+++ b/crypto/ts/ts_verify_ctx.c
@@ -0,0 +1,146 @@
+/*
+ * Copyright 2006-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "internal/cryptlib.h"
+#include 
+#include 
+#include "ts_lcl.h"
+
+TS_VERIFY_CTX *TS_VERIFY_CTX_new(void)
+{
+    TS_VERIFY_CTX *ctx = OPENSSL_zalloc(sizeof(*ctx));
+
+    if (ctx == NULL)
+        TSerr(TS_F_TS_VERIFY_CTX_NEW, ERR_R_MALLOC_FAILURE);
+    return ctx;
+}
+
+void TS_VERIFY_CTX_init(TS_VERIFY_CTX *ctx)
+{
+    OPENSSL_assert(ctx != NULL);
+    memset(ctx, 0, sizeof(*ctx));
+}
+
+void TS_VERIFY_CTX_free(TS_VERIFY_CTX *ctx)
+{
+    if (!ctx)
+        return;
+
+    TS_VERIFY_CTX_cleanup(ctx);
+    OPENSSL_free(ctx);
+}
+
+int TS_VERIFY_CTX_add_flags(TS_VERIFY_CTX *ctx, int f)
+{
+    ctx->flags |= f;
+    return ctx->flags;
+}
+
+int TS_VERIFY_CTX_set_flags(TS_VERIFY_CTX *ctx, int f)
+{
+    ctx->flags = f;
+    return ctx->flags;
+}
+
+BIO *TS_VERIFY_CTX_set_data(TS_VERIFY_CTX *ctx, BIO *b)
+{
+    ctx->data = b;
+    return ctx->data;
+}
+
+X509_STORE *TS_VERIFY_CTX_set_store(TS_VERIFY_CTX *ctx, X509_STORE *s)
+{
+    ctx->store = s;
+    return ctx->store;
+}
+
+STACK_OF(X509) *TS_VERIFY_CTS_set_certs(TS_VERIFY_CTX *ctx,
+                                        STACK_OF(X509) *certs)
+{
+    ctx->certs = certs;
+    return ctx->certs;
+}
+
+unsigned char *TS_VERIFY_CTX_set_imprint(TS_VERIFY_CTX *ctx,
+                                         unsigned char *hexstr, long len)
+{
+    ctx->imprint = hexstr;
+    ctx->imprint_len = len;
+    return ctx->imprint;
+}
+
+void TS_VERIFY_CTX_cleanup(TS_VERIFY_CTX *ctx)
+{
+    if (!ctx)
+        return;
+
+    X509_STORE_free(ctx->store);
+    sk_X509_pop_free(ctx->certs, X509_free);
+
+    ASN1_OBJECT_free(ctx->policy);
+
+    X509_ALGOR_free(ctx->md_alg);
+    OPENSSL_free(ctx->imprint);
+
+    BIO_free_all(ctx->data);
+
+    ASN1_INTEGER_free(ctx->nonce);
+
+    GENERAL_NAME_free(ctx->tsa_name);
+
+    TS_VERIFY_CTX_init(ctx);
+}
+
+TS_VERIFY_CTX *TS_REQ_to_TS_VERIFY_CTX(TS_REQ *req, TS_VERIFY_CTX *ctx)
+{
+    TS_VERIFY_CTX *ret = ctx;
+    ASN1_OBJECT *policy;
+    TS_MSG_IMPRINT *imprint;
+    X509_ALGOR *md_alg;
+    ASN1_OCTET_STRING *msg;
+    const ASN1_INTEGER *nonce;
+
+    OPENSSL_assert(req != NULL);
+    if (ret)
+        TS_VERIFY_CTX_cleanup(ret);
+    else if ((ret = TS_VERIFY_CTX_new()) == NULL)
+        return NULL;
+
+    ret->flags = TS_VFY_ALL_IMPRINT & ~(TS_VFY_TSA_NAME | TS_VFY_SIGNATURE);
+
+    if ((policy = req->policy_id) != NULL) {
+        if ((ret->policy = OBJ_dup(policy)) == NULL)
+            goto err;
+    } else
+        ret->flags &= ~TS_VFY_POLICY;
+
+    imprint = req->msg_imprint;
+    md_alg = imprint->hash_algo;
+    if ((ret->md_alg = X509_ALGOR_dup(md_alg)) == NULL)
+        goto err;
+    msg = imprint->hashed_msg;
+    ret->imprint_len = ASN1_STRING_length(msg);
+    if ((ret->imprint = OPENSSL_malloc(ret->imprint_len)) == NULL)
+        goto err;
+    memcpy(ret->imprint, ASN1_STRING_get0_data(msg), ret->imprint_len);
+
+    if ((nonce = req->nonce) != NULL) {
+        if ((ret->nonce = ASN1_INTEGER_dup(nonce)) == NULL)
+            goto err;
+    } else
+        ret->flags &= ~TS_VFY_NONCE;
+
+    return ret;
+ err:
+    if (ctx)
+        TS_VERIFY_CTX_cleanup(ctx);
+    else
+        TS_VERIFY_CTX_free(ret);
+    return NULL;
+}
diff --git a/crypto/txt_db/build.info b/crypto/txt_db/build.info
new file mode 100644
index 000000000..4379d5f1b
--- /dev/null
+++ b/crypto/txt_db/build.info
@@ -0,0 +1,2 @@
+LIBS=../../libcrypto
+SOURCE[../../libcrypto]=txt_db.c
diff --git a/crypto/txt_db/txt_db.c b/crypto/txt_db/txt_db.c
new file mode 100644
index 000000000..14322309a
--- /dev/null
+++ b/crypto/txt_db/txt_db.c
@@ -0,0 +1,301 @@
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+
+#undef BUFSIZE
+#define BUFSIZE 512
+
+TXT_DB *TXT_DB_read(BIO *in, int num)
+{
+    TXT_DB *ret = NULL;
+    int esc = 0;
+    long ln = 0;
+    int i, add, n;
+    int size = BUFSIZE;
+    int offset = 0;
+    char *p, *f;
+    OPENSSL_STRING *pp;
+    BUF_MEM *buf = NULL;
+
+    if ((buf = BUF_MEM_new()) == NULL)
+        goto err;
+    if (!BUF_MEM_grow(buf, size))
+        goto err;
+
+    if ((ret = OPENSSL_malloc(sizeof(*ret))) == NULL)
+        goto err;
+    ret->num_fields = num;
+    ret->index = NULL;
+    ret->qual = NULL;
+    if ((ret->data = sk_OPENSSL_PSTRING_new_null()) == NULL)
+        goto err;
+    if ((ret->index = OPENSSL_malloc(sizeof(*ret->index) * num)) == NULL)
+        goto err;
+    if ((ret->qual = OPENSSL_malloc(sizeof(*(ret->qual)) * num)) == NULL)
+        goto err;
+    for (i = 0; i < num; i++) {
+        ret->index[i] = NULL;
+        ret->qual[i] = NULL;
+    }
+
+    add = (num + 1) * sizeof(char *);
+    buf->data[size - 1] = '\0';
+    offset = 0;
+    for (;;) {
+        if (offset != 0) {
+            size += BUFSIZE;
+            if (!BUF_MEM_grow_clean(buf, size))
+                goto err;
+        }
+        buf->data[offset] = '\0';
+        BIO_gets(in, &(buf->data[offset]), size - offset);
+        ln++;
+        if (buf->data[offset] == '\0')
+            break;
+        if ((offset == 0) && (buf->data[0] == '#'))
+            continue;
+        i = strlen(&(buf->data[offset]));
+        offset += i;
+        if (buf->data[offset - 1] != '\n')
+            continue;
+        else {
+            buf->data[offset - 1] = '\0'; /* blat the '\n' */
+            if ((p = OPENSSL_malloc(add + offset)) == NULL)
+                goto err;
+            offset = 0;
+        }
+        pp = (char **)p;
+        p += add;
+        n = 0;
+        pp[n++] = p;
+        i = 0;
+        f = buf->data;
+
+        esc = 0;
+        for (;;) {
+            if (*f == '\0')
+                break;
+            if (*f == '\t') {
+                if (esc)
+                    p--;
+                else {
+                    *(p++) = '\0';
+                    f++;
+                    if (n >= num)
+                        break;
+                    pp[n++] = p;
+                    continue;
+                }
+            }
+            esc = (*f == '\\');
+            *(p++) = *(f++);
+        }
+        *(p++) = '\0';
+        if ((n != num) || (*f != '\0')) {
+            ret->error = DB_ERROR_WRONG_NUM_FIELDS;
+            goto err;
+        }
+        pp[n] = p;
+        if (!sk_OPENSSL_PSTRING_push(ret->data, pp))
+            goto err;
+    }
+    BUF_MEM_free(buf);
+    return ret;
+ err:
+    BUF_MEM_free(buf);
+    if (ret != NULL) {
+        sk_OPENSSL_PSTRING_free(ret->data);
+        OPENSSL_free(ret->index);
+        OPENSSL_free(ret->qual);
+        OPENSSL_free(ret);
+    }
+    return (NULL);
+}
+
+OPENSSL_STRING *TXT_DB_get_by_index(TXT_DB *db, int idx,
+                                    OPENSSL_STRING *value)
+{
+    OPENSSL_STRING *ret;
+    LHASH_OF(OPENSSL_STRING) *lh;
+
+    if (idx >= db->num_fields) {
+        db->error = DB_ERROR_INDEX_OUT_OF_RANGE;
+        return (NULL);
+    }
+    lh = db->index[idx];
+    if (lh == NULL) {
+        db->error = DB_ERROR_NO_INDEX;
+        return (NULL);
+    }
+    ret = lh_OPENSSL_STRING_retrieve(lh, value);
+    db->error = DB_ERROR_OK;
+    return (ret);
+}
+
+int TXT_DB_create_index(TXT_DB *db, int field, int (*qual) (OPENSSL_STRING *),
+                        OPENSSL_LH_HASHFUNC hash, OPENSSL_LH_COMPFUNC cmp)
+{
+    LHASH_OF(OPENSSL_STRING) *idx;
+    OPENSSL_STRING *r;
+    int i, n;
+
+    if (field >= db->num_fields) {
+        db->error = DB_ERROR_INDEX_OUT_OF_RANGE;
+        return (0);
+    }
+    /* FIXME: we lose type checking at this point */
+    if ((idx = (LHASH_OF(OPENSSL_STRING) *)OPENSSL_LH_new(hash, cmp)) == NULL) {
+        db->error = DB_ERROR_MALLOC;
+        return (0);
+    }
+    n = sk_OPENSSL_PSTRING_num(db->data);
+    for (i = 0; i < n; i++) {
+        r = sk_OPENSSL_PSTRING_value(db->data, i);
+        if ((qual != NULL) && (qual(r) == 0))
+            continue;
+        if ((r = lh_OPENSSL_STRING_insert(idx, r)) != NULL) {
+            db->error = DB_ERROR_INDEX_CLASH;
+            db->arg1 = sk_OPENSSL_PSTRING_find(db->data, r);
+            db->arg2 = i;
+            lh_OPENSSL_STRING_free(idx);
+            return (0);
+        }
+    }
+    lh_OPENSSL_STRING_free(db->index[field]);
+    db->index[field] = idx;
+    db->qual[field] = qual;
+    return (1);
+}
+
+long TXT_DB_write(BIO *out, TXT_DB *db)
+{
+    long i, j, n, nn, l, tot = 0;
+    char *p, **pp, *f;
+    BUF_MEM *buf = NULL;
+    long ret = -1;
+
+    if ((buf = BUF_MEM_new()) == NULL)
+        goto err;
+    n = sk_OPENSSL_PSTRING_num(db->data);
+    nn = db->num_fields;
+    for (i = 0; i < n; i++) {
+        pp = sk_OPENSSL_PSTRING_value(db->data, i);
+
+        l = 0;
+        for (j = 0; j < nn; j++) {
+            if (pp[j] != NULL)
+                l += strlen(pp[j]);
+        }
+        if (!BUF_MEM_grow_clean(buf, (int)(l * 2 + nn)))
+            goto err;
+
+        p = buf->data;
+        for (j = 0; j < nn; j++) {
+            f = pp[j];
+            if (f != NULL)
+                for (;;) {
+                    if (*f == '\0')
+                        break;
+                    if (*f == '\t')
+                        *(p++) = '\\';
+                    *(p++) = *(f++);
+                }
+            *(p++) = '\t';
+        }
+        p[-1] = '\n';
+        j = p - buf->data;
+        if (BIO_write(out, buf->data, (int)j) != j)
+            goto err;
+        tot += j;
+    }
+    ret = tot;
+ err:
+    BUF_MEM_free(buf);
+    return (ret);
+}
+
+int TXT_DB_insert(TXT_DB *db, OPENSSL_STRING *row)
+{
+    int i;
+    OPENSSL_STRING *r;
+
+    for (i = 0; i < db->num_fields; i++) {
+        if (db->index[i] != NULL) {
+            if ((db->qual[i] != NULL) && (db->qual[i] (row) == 0))
+                continue;
+            r = lh_OPENSSL_STRING_retrieve(db->index[i], row);
+            if (r != NULL) {
+                db->error = DB_ERROR_INDEX_CLASH;
+                db->arg1 = i;
+                db->arg_row = r;
+                goto err;
+            }
+        }
+    }
+    /* We have passed the index checks, now just append and insert */
+    if (!sk_OPENSSL_PSTRING_push(db->data, row)) {
+        db->error = DB_ERROR_MALLOC;
+        goto err;
+    }
+
+    for (i = 0; i < db->num_fields; i++) {
+        if (db->index[i] != NULL) {
+            if ((db->qual[i] != NULL) && (db->qual[i] (row) == 0))
+                continue;
+            (void)lh_OPENSSL_STRING_insert(db->index[i], row);
+        }
+    }
+    return (1);
+ err:
+    return (0);
+}
+
+void TXT_DB_free(TXT_DB *db)
+{
+    int i, n;
+    char **p, *max;
+
+    if (db == NULL)
+        return;
+
+    if (db->index != NULL) {
+        for (i = db->num_fields - 1; i >= 0; i--)
+            lh_OPENSSL_STRING_free(db->index[i]);
+        OPENSSL_free(db->index);
+    }
+    OPENSSL_free(db->qual);
+    if (db->data != NULL) {
+        for (i = sk_OPENSSL_PSTRING_num(db->data) - 1; i >= 0; i--) {
+            /*
+             * check if any 'fields' have been allocated from outside of the
+             * initial block
+             */
+            p = sk_OPENSSL_PSTRING_value(db->data, i);
+            max = p[db->num_fields]; /* last address */
+            if (max == NULL) {  /* new row */
+                for (n = 0; n < db->num_fields; n++)
+                    OPENSSL_free(p[n]);
+            } else {
+                for (n = 0; n < db->num_fields; n++) {
+                    if (((p[n] < (char *)p) || (p[n] > max)))
+                        OPENSSL_free(p[n]);
+                }
+            }
+            OPENSSL_free(sk_OPENSSL_PSTRING_value(db->data, i));
+        }
+        sk_OPENSSL_PSTRING_free(db->data);
+    }
+    OPENSSL_free(db);
+}
diff --git a/crypto/ui/build.info b/crypto/ui/build.info
new file mode 100644
index 000000000..fcb45af7e
--- /dev/null
+++ b/crypto/ui/build.info
@@ -0,0 +1,3 @@
+LIBS=../../libcrypto
+SOURCE[../../libcrypto]=\
+        ui_err.c ui_lib.c ui_openssl.c ui_util.c
diff --git a/crypto/ui/ui_err.c b/crypto/ui/ui_err.c
new file mode 100644
index 000000000..c8640feaf
--- /dev/null
+++ b/crypto/ui/ui_err.c
@@ -0,0 +1,72 @@
+/*
+ * Generated by util/mkerr.pl DO NOT EDIT
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+#include 
+
+/* BEGIN ERROR CODES */
+#ifndef OPENSSL_NO_ERR
+
+# define ERR_FUNC(func) ERR_PACK(ERR_LIB_UI,func,0)
+# define ERR_REASON(reason) ERR_PACK(ERR_LIB_UI,0,reason)
+
+static ERR_STRING_DATA UI_str_functs[] = {
+    {ERR_FUNC(UI_F_CLOSE_CONSOLE), "close_console"},
+    {ERR_FUNC(UI_F_ECHO_CONSOLE), "echo_console"},
+    {ERR_FUNC(UI_F_GENERAL_ALLOCATE_BOOLEAN), "general_allocate_boolean"},
+    {ERR_FUNC(UI_F_GENERAL_ALLOCATE_PROMPT), "general_allocate_prompt"},
+    {ERR_FUNC(UI_F_NOECHO_CONSOLE), "noecho_console"},
+    {ERR_FUNC(UI_F_OPEN_CONSOLE), "open_console"},
+    {ERR_FUNC(UI_F_UI_CREATE_METHOD), "UI_create_method"},
+    {ERR_FUNC(UI_F_UI_CTRL), "UI_ctrl"},
+    {ERR_FUNC(UI_F_UI_DUP_ERROR_STRING), "UI_dup_error_string"},
+    {ERR_FUNC(UI_F_UI_DUP_INFO_STRING), "UI_dup_info_string"},
+    {ERR_FUNC(UI_F_UI_DUP_INPUT_BOOLEAN), "UI_dup_input_boolean"},
+    {ERR_FUNC(UI_F_UI_DUP_INPUT_STRING), "UI_dup_input_string"},
+    {ERR_FUNC(UI_F_UI_DUP_VERIFY_STRING), "UI_dup_verify_string"},
+    {ERR_FUNC(UI_F_UI_GET0_RESULT), "UI_get0_result"},
+    {ERR_FUNC(UI_F_UI_NEW_METHOD), "UI_new_method"},
+    {ERR_FUNC(UI_F_UI_PROCESS), "UI_process"},
+    {ERR_FUNC(UI_F_UI_SET_RESULT), "UI_set_result"},
+    {0, NULL}
+};
+
+static ERR_STRING_DATA UI_str_reasons[] = {
+    {ERR_REASON(UI_R_COMMON_OK_AND_CANCEL_CHARACTERS),
+     "common ok and cancel characters"},
+    {ERR_REASON(UI_R_INDEX_TOO_LARGE), "index too large"},
+    {ERR_REASON(UI_R_INDEX_TOO_SMALL), "index too small"},
+    {ERR_REASON(UI_R_NO_RESULT_BUFFER), "no result buffer"},
+    {ERR_REASON(UI_R_PROCESSING_ERROR), "processing error"},
+    {ERR_REASON(UI_R_RESULT_TOO_LARGE), "result too large"},
+    {ERR_REASON(UI_R_RESULT_TOO_SMALL), "result too small"},
+    {ERR_REASON(UI_R_SYSASSIGN_ERROR), "sys$assign error"},
+    {ERR_REASON(UI_R_SYSDASSGN_ERROR), "sys$dassgn error"},
+    {ERR_REASON(UI_R_SYSQIOW_ERROR), "sys$qiow error"},
+    {ERR_REASON(UI_R_UNKNOWN_CONTROL_COMMAND), "unknown control command"},
+    {ERR_REASON(UI_R_UNKNOWN_TTYGET_ERRNO_VALUE),
+     "unknown ttyget errno value"},
+    {0, NULL}
+};
+
+#endif
+
+int ERR_load_UI_strings(void)
+{
+#ifndef OPENSSL_NO_ERR
+
+    if (ERR_func_error_string(UI_str_functs[0].error) == NULL) {
+        ERR_load_strings(0, UI_str_functs);
+        ERR_load_strings(0, UI_str_reasons);
+    }
+#endif
+    return 1;
+}
diff --git a/crypto/ui/ui_lib.c b/crypto/ui/ui_lib.c
new file mode 100644
index 000000000..12d62d86d
--- /dev/null
+++ b/crypto/ui/ui_lib.c
@@ -0,0 +1,826 @@
+/*
+ * Copyright 2001-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "internal/cryptlib.h"
+#include 
+#include 
+#include 
+#include 
+#include "ui_locl.h"
+
+static const UI_METHOD *default_UI_meth = NULL;
+
+UI *UI_new(void)
+{
+    return (UI_new_method(NULL));
+}
+
+UI *UI_new_method(const UI_METHOD *method)
+{
+    UI *ret = OPENSSL_zalloc(sizeof(*ret));
+
+    if (ret == NULL) {
+        UIerr(UI_F_UI_NEW_METHOD, ERR_R_MALLOC_FAILURE);
+        return NULL;
+    }
+
+    ret->lock = CRYPTO_THREAD_lock_new();
+    if (ret->lock == NULL) {
+        UIerr(UI_F_UI_NEW_METHOD, ERR_R_MALLOC_FAILURE);
+        OPENSSL_free(ret);
+        return NULL;
+    }
+
+    if (method == NULL)
+        ret->meth = UI_get_default_method();
+    else
+        ret->meth = method;
+
+    if (!CRYPTO_new_ex_data(CRYPTO_EX_INDEX_UI, ret, &ret->ex_data)) {
+        OPENSSL_free(ret);
+        return NULL;
+    }
+    return ret;
+}
+
+static void free_string(UI_STRING *uis)
+{
+    if (uis->flags & OUT_STRING_FREEABLE) {
+        OPENSSL_free((char *)uis->out_string);
+        switch (uis->type) {
+        case UIT_BOOLEAN:
+            OPENSSL_free((char *)uis->_.boolean_data.action_desc);
+            OPENSSL_free((char *)uis->_.boolean_data.ok_chars);
+            OPENSSL_free((char *)uis->_.boolean_data.cancel_chars);
+            break;
+        default:
+            break;
+        }
+    }
+    OPENSSL_free(uis);
+}
+
+void UI_free(UI *ui)
+{
+    if (ui == NULL)
+        return;
+    sk_UI_STRING_pop_free(ui->strings, free_string);
+    CRYPTO_free_ex_data(CRYPTO_EX_INDEX_UI, ui, &ui->ex_data);
+    CRYPTO_THREAD_lock_free(ui->lock);
+    OPENSSL_free(ui);
+}
+
+static int allocate_string_stack(UI *ui)
+{
+    if (ui->strings == NULL) {
+        ui->strings = sk_UI_STRING_new_null();
+        if (ui->strings == NULL) {
+            return -1;
+        }
+    }
+    return 0;
+}
+
+static UI_STRING *general_allocate_prompt(UI *ui, const char *prompt,
+                                          int prompt_freeable,
+                                          enum UI_string_types type,
+                                          int input_flags, char *result_buf)
+{
+    UI_STRING *ret = NULL;
+
+    if (prompt == NULL) {
+        UIerr(UI_F_GENERAL_ALLOCATE_PROMPT, ERR_R_PASSED_NULL_PARAMETER);
+    } else if ((type == UIT_PROMPT || type == UIT_VERIFY
+                || type == UIT_BOOLEAN) && result_buf == NULL) {
+        UIerr(UI_F_GENERAL_ALLOCATE_PROMPT, UI_R_NO_RESULT_BUFFER);
+    } else if ((ret = OPENSSL_malloc(sizeof(*ret))) != NULL) {
+        ret->out_string = prompt;
+        ret->flags = prompt_freeable ? OUT_STRING_FREEABLE : 0;
+        ret->input_flags = input_flags;
+        ret->type = type;
+        ret->result_buf = result_buf;
+    }
+    return ret;
+}
+
+static int general_allocate_string(UI *ui, const char *prompt,
+                                   int prompt_freeable,
+                                   enum UI_string_types type, int input_flags,
+                                   char *result_buf, int minsize, int maxsize,
+                                   const char *test_buf)
+{
+    int ret = -1;
+    UI_STRING *s = general_allocate_prompt(ui, prompt, prompt_freeable,
+                                           type, input_flags, result_buf);
+
+    if (s != NULL) {
+        if (allocate_string_stack(ui) >= 0) {
+            s->_.string_data.result_minsize = minsize;
+            s->_.string_data.result_maxsize = maxsize;
+            s->_.string_data.test_buf = test_buf;
+            ret = sk_UI_STRING_push(ui->strings, s);
+            /* sk_push() returns 0 on error.  Let's adapt that */
+            if (ret <= 0) {
+                ret--;
+                free_string(s);
+            }
+        } else
+            free_string(s);
+    }
+    return ret;
+}
+
+static int general_allocate_boolean(UI *ui,
+                                    const char *prompt,
+                                    const char *action_desc,
+                                    const char *ok_chars,
+                                    const char *cancel_chars,
+                                    int prompt_freeable,
+                                    enum UI_string_types type,
+                                    int input_flags, char *result_buf)
+{
+    int ret = -1;
+    UI_STRING *s;
+    const char *p;
+
+    if (ok_chars == NULL) {
+        UIerr(UI_F_GENERAL_ALLOCATE_BOOLEAN, ERR_R_PASSED_NULL_PARAMETER);
+    } else if (cancel_chars == NULL) {
+        UIerr(UI_F_GENERAL_ALLOCATE_BOOLEAN, ERR_R_PASSED_NULL_PARAMETER);
+    } else {
+        for (p = ok_chars; *p != '\0'; p++) {
+            if (strchr(cancel_chars, *p) != NULL) {
+                UIerr(UI_F_GENERAL_ALLOCATE_BOOLEAN,
+                      UI_R_COMMON_OK_AND_CANCEL_CHARACTERS);
+            }
+        }
+
+        s = general_allocate_prompt(ui, prompt, prompt_freeable,
+                                    type, input_flags, result_buf);
+
+        if (s != NULL) {
+            if (allocate_string_stack(ui) >= 0) {
+                s->_.boolean_data.action_desc = action_desc;
+                s->_.boolean_data.ok_chars = ok_chars;
+                s->_.boolean_data.cancel_chars = cancel_chars;
+                ret = sk_UI_STRING_push(ui->strings, s);
+                /*
+                 * sk_push() returns 0 on error. Let's adapt that
+                 */
+                if (ret <= 0) {
+                    ret--;
+                    free_string(s);
+                }
+            } else
+                free_string(s);
+        }
+    }
+    return ret;
+}
+
+/*
+ * Returns the index to the place in the stack or -1 for error.  Uses a
+ * direct reference to the prompt.
+ */
+int UI_add_input_string(UI *ui, const char *prompt, int flags,
+                        char *result_buf, int minsize, int maxsize)
+{
+    return general_allocate_string(ui, prompt, 0,
+                                   UIT_PROMPT, flags, result_buf, minsize,
+                                   maxsize, NULL);
+}
+
+/* Same as UI_add_input_string(), excepts it takes a copy of the prompt */
+int UI_dup_input_string(UI *ui, const char *prompt, int flags,
+                        char *result_buf, int minsize, int maxsize)
+{
+    char *prompt_copy = NULL;
+
+    if (prompt != NULL) {
+        prompt_copy = OPENSSL_strdup(prompt);
+        if (prompt_copy == NULL) {
+            UIerr(UI_F_UI_DUP_INPUT_STRING, ERR_R_MALLOC_FAILURE);
+            return 0;
+        }
+    }
+
+    return general_allocate_string(ui, prompt_copy, 1,
+                                   UIT_PROMPT, flags, result_buf, minsize,
+                                   maxsize, NULL);
+}
+
+int UI_add_verify_string(UI *ui, const char *prompt, int flags,
+                         char *result_buf, int minsize, int maxsize,
+                         const char *test_buf)
+{
+    return general_allocate_string(ui, prompt, 0,
+                                   UIT_VERIFY, flags, result_buf, minsize,
+                                   maxsize, test_buf);
+}
+
+int UI_dup_verify_string(UI *ui, const char *prompt, int flags,
+                         char *result_buf, int minsize, int maxsize,
+                         const char *test_buf)
+{
+    char *prompt_copy = NULL;
+
+    if (prompt != NULL) {
+        prompt_copy = OPENSSL_strdup(prompt);
+        if (prompt_copy == NULL) {
+            UIerr(UI_F_UI_DUP_VERIFY_STRING, ERR_R_MALLOC_FAILURE);
+            return -1;
+        }
+    }
+
+    return general_allocate_string(ui, prompt_copy, 1,
+                                   UIT_VERIFY, flags, result_buf, minsize,
+                                   maxsize, test_buf);
+}
+
+int UI_add_input_boolean(UI *ui, const char *prompt, const char *action_desc,
+                         const char *ok_chars, const char *cancel_chars,
+                         int flags, char *result_buf)
+{
+    return general_allocate_boolean(ui, prompt, action_desc,
+                                    ok_chars, cancel_chars, 0, UIT_BOOLEAN,
+                                    flags, result_buf);
+}
+
+int UI_dup_input_boolean(UI *ui, const char *prompt, const char *action_desc,
+                         const char *ok_chars, const char *cancel_chars,
+                         int flags, char *result_buf)
+{
+    char *prompt_copy = NULL;
+    char *action_desc_copy = NULL;
+    char *ok_chars_copy = NULL;
+    char *cancel_chars_copy = NULL;
+
+    if (prompt != NULL) {
+        prompt_copy = OPENSSL_strdup(prompt);
+        if (prompt_copy == NULL) {
+            UIerr(UI_F_UI_DUP_INPUT_BOOLEAN, ERR_R_MALLOC_FAILURE);
+            goto err;
+        }
+    }
+
+    if (action_desc != NULL) {
+        action_desc_copy = OPENSSL_strdup(action_desc);
+        if (action_desc_copy == NULL) {
+            UIerr(UI_F_UI_DUP_INPUT_BOOLEAN, ERR_R_MALLOC_FAILURE);
+            goto err;
+        }
+    }
+
+    if (ok_chars != NULL) {
+        ok_chars_copy = OPENSSL_strdup(ok_chars);
+        if (ok_chars_copy == NULL) {
+            UIerr(UI_F_UI_DUP_INPUT_BOOLEAN, ERR_R_MALLOC_FAILURE);
+            goto err;
+        }
+    }
+
+    if (cancel_chars != NULL) {
+        cancel_chars_copy = OPENSSL_strdup(cancel_chars);
+        if (cancel_chars_copy == NULL) {
+            UIerr(UI_F_UI_DUP_INPUT_BOOLEAN, ERR_R_MALLOC_FAILURE);
+            goto err;
+        }
+    }
+
+    return general_allocate_boolean(ui, prompt_copy, action_desc_copy,
+                                    ok_chars_copy, cancel_chars_copy, 1,
+                                    UIT_BOOLEAN, flags, result_buf);
+ err:
+    OPENSSL_free(prompt_copy);
+    OPENSSL_free(action_desc_copy);
+    OPENSSL_free(ok_chars_copy);
+    OPENSSL_free(cancel_chars_copy);
+    return -1;
+}
+
+int UI_add_info_string(UI *ui, const char *text)
+{
+    return general_allocate_string(ui, text, 0, UIT_INFO, 0, NULL, 0, 0,
+                                   NULL);
+}
+
+int UI_dup_info_string(UI *ui, const char *text)
+{
+    char *text_copy = NULL;
+
+    if (text != NULL) {
+        text_copy = OPENSSL_strdup(text);
+        if (text_copy == NULL) {
+            UIerr(UI_F_UI_DUP_INFO_STRING, ERR_R_MALLOC_FAILURE);
+            return -1;
+        }
+    }
+
+    return general_allocate_string(ui, text_copy, 1, UIT_INFO, 0, NULL,
+                                   0, 0, NULL);
+}
+
+int UI_add_error_string(UI *ui, const char *text)
+{
+    return general_allocate_string(ui, text, 0, UIT_ERROR, 0, NULL, 0, 0,
+                                   NULL);
+}
+
+int UI_dup_error_string(UI *ui, const char *text)
+{
+    char *text_copy = NULL;
+
+    if (text != NULL) {
+        text_copy = OPENSSL_strdup(text);
+        if (text_copy == NULL) {
+            UIerr(UI_F_UI_DUP_ERROR_STRING, ERR_R_MALLOC_FAILURE);
+            return -1;
+        }
+    }
+    return general_allocate_string(ui, text_copy, 1, UIT_ERROR, 0, NULL,
+                                   0, 0, NULL);
+}
+
+char *UI_construct_prompt(UI *ui, const char *object_desc,
+                          const char *object_name)
+{
+    char *prompt = NULL;
+
+    if (ui->meth->ui_construct_prompt != NULL)
+        prompt = ui->meth->ui_construct_prompt(ui, object_desc, object_name);
+    else {
+        char prompt1[] = "Enter ";
+        char prompt2[] = " for ";
+        char prompt3[] = ":";
+        int len = 0;
+
+        if (object_desc == NULL)
+            return NULL;
+        len = sizeof(prompt1) - 1 + strlen(object_desc);
+        if (object_name != NULL)
+            len += sizeof(prompt2) - 1 + strlen(object_name);
+        len += sizeof(prompt3) - 1;
+
+        prompt = OPENSSL_malloc(len + 1);
+        if (prompt == NULL)
+            return NULL;
+        OPENSSL_strlcpy(prompt, prompt1, len + 1);
+        OPENSSL_strlcat(prompt, object_desc, len + 1);
+        if (object_name != NULL) {
+            OPENSSL_strlcat(prompt, prompt2, len + 1);
+            OPENSSL_strlcat(prompt, object_name, len + 1);
+        }
+        OPENSSL_strlcat(prompt, prompt3, len + 1);
+    }
+    return prompt;
+}
+
+void *UI_add_user_data(UI *ui, void *user_data)
+{
+    void *old_data = ui->user_data;
+    ui->user_data = user_data;
+    return old_data;
+}
+
+void *UI_get0_user_data(UI *ui)
+{
+    return ui->user_data;
+}
+
+const char *UI_get0_result(UI *ui, int i)
+{
+    if (i < 0) {
+        UIerr(UI_F_UI_GET0_RESULT, UI_R_INDEX_TOO_SMALL);
+        return NULL;
+    }
+    if (i >= sk_UI_STRING_num(ui->strings)) {
+        UIerr(UI_F_UI_GET0_RESULT, UI_R_INDEX_TOO_LARGE);
+        return NULL;
+    }
+    return UI_get0_result_string(sk_UI_STRING_value(ui->strings, i));
+}
+
+static int print_error(const char *str, size_t len, UI *ui)
+{
+    UI_STRING uis;
+
+    memset(&uis, 0, sizeof(uis));
+    uis.type = UIT_ERROR;
+    uis.out_string = str;
+
+    if (ui->meth->ui_write_string != NULL
+        && ui->meth->ui_write_string(ui, &uis) <= 0)
+        return -1;
+    return 0;
+}
+
+int UI_process(UI *ui)
+{
+    int i, ok = 0;
+    const char *state = "processing";
+
+    if (ui->meth->ui_open_session != NULL
+        && ui->meth->ui_open_session(ui) <= 0) {
+        state = "opening session";
+        ok = -1;
+        goto err;
+    }
+
+    if (ui->flags & UI_FLAG_PRINT_ERRORS)
+        ERR_print_errors_cb((int (*)(const char *, size_t, void *))
+                            print_error, (void *)ui);
+
+    for (i = 0; i < sk_UI_STRING_num(ui->strings); i++) {
+        if (ui->meth->ui_write_string != NULL
+            && (ui->meth->ui_write_string(ui,
+                                          sk_UI_STRING_value(ui->strings, i))
+                <= 0))
+        {
+            state = "writing strings";
+            ok = -1;
+            goto err;
+        }
+    }
+
+    if (ui->meth->ui_flush != NULL)
+        switch (ui->meth->ui_flush(ui)) {
+        case -1:               /* Interrupt/Cancel/something... */
+            ok = -2;
+            goto err;
+        case 0:                /* Errors */
+            state = "flushing";
+            ok = -1;
+            goto err;
+        default:               /* Success */
+            ok = 0;
+            break;
+        }
+
+    for (i = 0; i < sk_UI_STRING_num(ui->strings); i++) {
+        if (ui->meth->ui_read_string != NULL) {
+            switch (ui->meth->ui_read_string(ui,
+                                             sk_UI_STRING_value(ui->strings,
+                                                                i))) {
+            case -1:           /* Interrupt/Cancel/something... */
+                ok = -2;
+                goto err;
+            case 0:            /* Errors */
+                state = "reading strings";
+                ok = -1;
+                goto err;
+            default:           /* Success */
+                ok = 0;
+                break;
+            }
+        }
+    }
+ err:
+    if (ui->meth->ui_close_session != NULL
+        && ui->meth->ui_close_session(ui) <= 0) {
+        if (state == NULL)
+            state = "closing session";
+        ok = -1;
+    }
+
+    if (ok == -1) {
+        UIerr(UI_F_UI_PROCESS, UI_R_PROCESSING_ERROR);
+        ERR_add_error_data(2, "while ", state);
+    }
+    return ok;
+}
+
+int UI_ctrl(UI *ui, int cmd, long i, void *p, void (*f) (void))
+{
+    if (ui == NULL) {
+        UIerr(UI_F_UI_CTRL, ERR_R_PASSED_NULL_PARAMETER);
+        return -1;
+    }
+    switch (cmd) {
+    case UI_CTRL_PRINT_ERRORS:
+        {
+            int save_flag = ! !(ui->flags & UI_FLAG_PRINT_ERRORS);
+            if (i)
+                ui->flags |= UI_FLAG_PRINT_ERRORS;
+            else
+                ui->flags &= ~UI_FLAG_PRINT_ERRORS;
+            return save_flag;
+        }
+    case UI_CTRL_IS_REDOABLE:
+        return ! !(ui->flags & UI_FLAG_REDOABLE);
+    default:
+        break;
+    }
+    UIerr(UI_F_UI_CTRL, UI_R_UNKNOWN_CONTROL_COMMAND);
+    return -1;
+}
+
+int UI_set_ex_data(UI *r, int idx, void *arg)
+{
+    return (CRYPTO_set_ex_data(&r->ex_data, idx, arg));
+}
+
+void *UI_get_ex_data(UI *r, int idx)
+{
+    return (CRYPTO_get_ex_data(&r->ex_data, idx));
+}
+
+void UI_set_default_method(const UI_METHOD *meth)
+{
+    default_UI_meth = meth;
+}
+
+const UI_METHOD *UI_get_default_method(void)
+{
+    if (default_UI_meth == NULL) {
+        default_UI_meth = UI_OpenSSL();
+    }
+    return default_UI_meth;
+}
+
+const UI_METHOD *UI_get_method(UI *ui)
+{
+    return ui->meth;
+}
+
+const UI_METHOD *UI_set_method(UI *ui, const UI_METHOD *meth)
+{
+    ui->meth = meth;
+    return ui->meth;
+}
+
+UI_METHOD *UI_create_method(const char *name)
+{
+    UI_METHOD *ui_method = OPENSSL_zalloc(sizeof(*ui_method));
+
+    if (ui_method != NULL) {
+        ui_method->name = OPENSSL_strdup(name);
+        if (ui_method->name == NULL) {
+            OPENSSL_free(ui_method);
+            UIerr(UI_F_UI_CREATE_METHOD, ERR_R_MALLOC_FAILURE);
+            return NULL;
+        }
+    }
+    return ui_method;
+}
+
+/*
+ * BIG FSCKING WARNING!!!! If you use this on a statically allocated method
+ * (that is, it hasn't been allocated using UI_create_method(), you deserve
+ * anything Murphy can throw at you and more! You have been warned.
+ */
+void UI_destroy_method(UI_METHOD *ui_method)
+{
+    OPENSSL_free(ui_method->name);
+    ui_method->name = NULL;
+    OPENSSL_free(ui_method);
+}
+
+int UI_method_set_opener(UI_METHOD *method, int (*opener) (UI *ui))
+{
+    if (method != NULL) {
+        method->ui_open_session = opener;
+        return 0;
+    }
+    return -1;
+}
+
+int UI_method_set_writer(UI_METHOD *method,
+                         int (*writer) (UI *ui, UI_STRING *uis))
+{
+    if (method != NULL) {
+        method->ui_write_string = writer;
+        return 0;
+    }
+    return -1;
+}
+
+int UI_method_set_flusher(UI_METHOD *method, int (*flusher) (UI *ui))
+{
+    if (method != NULL) {
+        method->ui_flush = flusher;
+        return 0;
+    }
+    return -1;
+}
+
+int UI_method_set_reader(UI_METHOD *method,
+                         int (*reader) (UI *ui, UI_STRING *uis))
+{
+    if (method != NULL) {
+        method->ui_read_string = reader;
+        return 0;
+    }
+    return -1;
+}
+
+int UI_method_set_closer(UI_METHOD *method, int (*closer) (UI *ui))
+{
+    if (method != NULL) {
+        method->ui_close_session = closer;
+        return 0;
+    }
+    return -1;
+}
+
+int UI_method_set_prompt_constructor(UI_METHOD *method,
+                                     char *(*prompt_constructor) (UI *ui,
+                                                                  const char
+                                                                  *object_desc,
+                                                                  const char
+                                                                  *object_name))
+{
+    if (method != NULL) {
+        method->ui_construct_prompt = prompt_constructor;
+        return 0;
+    }
+    return -1;
+}
+
+int (*UI_method_get_opener(UI_METHOD *method)) (UI *)
+{
+    if (method != NULL)
+        return method->ui_open_session;
+    return NULL;
+}
+
+int (*UI_method_get_writer(UI_METHOD *method)) (UI *, UI_STRING *)
+{
+    if (method != NULL)
+        return method->ui_write_string;
+    return NULL;
+}
+
+int (*UI_method_get_flusher(UI_METHOD *method)) (UI *)
+{
+    if (method != NULL)
+        return method->ui_flush;
+    return NULL;
+}
+
+int (*UI_method_get_reader(UI_METHOD *method)) (UI *, UI_STRING *)
+{
+    if (method != NULL)
+        return method->ui_read_string;
+    return NULL;
+}
+
+int (*UI_method_get_closer(UI_METHOD *method)) (UI *)
+{
+    if (method != NULL)
+        return method->ui_close_session;
+    return NULL;
+}
+
+char *(*UI_method_get_prompt_constructor(UI_METHOD *method)) (UI *,
+                                                              const char *,
+                                                              const char *)
+{
+    if (method != NULL)
+        return method->ui_construct_prompt;
+    return NULL;
+}
+
+enum UI_string_types UI_get_string_type(UI_STRING *uis)
+{
+    return uis->type;
+}
+
+int UI_get_input_flags(UI_STRING *uis)
+{
+    return uis->input_flags;
+}
+
+const char *UI_get0_output_string(UI_STRING *uis)
+{
+    return uis->out_string;
+}
+
+const char *UI_get0_action_string(UI_STRING *uis)
+{
+    switch (uis->type) {
+    case UIT_PROMPT:
+    case UIT_BOOLEAN:
+        return uis->_.boolean_data.action_desc;
+    default:
+        return NULL;
+    }
+}
+
+const char *UI_get0_result_string(UI_STRING *uis)
+{
+    switch (uis->type) {
+    case UIT_PROMPT:
+    case UIT_VERIFY:
+        return uis->result_buf;
+    default:
+        return NULL;
+    }
+}
+
+const char *UI_get0_test_string(UI_STRING *uis)
+{
+    switch (uis->type) {
+    case UIT_VERIFY:
+        return uis->_.string_data.test_buf;
+    default:
+        return NULL;
+    }
+}
+
+int UI_get_result_minsize(UI_STRING *uis)
+{
+    switch (uis->type) {
+    case UIT_PROMPT:
+    case UIT_VERIFY:
+        return uis->_.string_data.result_minsize;
+    default:
+        return -1;
+    }
+}
+
+int UI_get_result_maxsize(UI_STRING *uis)
+{
+    switch (uis->type) {
+    case UIT_PROMPT:
+    case UIT_VERIFY:
+        return uis->_.string_data.result_maxsize;
+    default:
+        return -1;
+    }
+}
+
+int UI_set_result(UI *ui, UI_STRING *uis, const char *result)
+{
+    int l = strlen(result);
+
+    ui->flags &= ~UI_FLAG_REDOABLE;
+
+    switch (uis->type) {
+    case UIT_PROMPT:
+    case UIT_VERIFY:
+        {
+            char number1[DECIMAL_SIZE(uis->_.string_data.result_minsize) + 1];
+            char number2[DECIMAL_SIZE(uis->_.string_data.result_maxsize) + 1];
+
+            BIO_snprintf(number1, sizeof(number1), "%d",
+                         uis->_.string_data.result_minsize);
+            BIO_snprintf(number2, sizeof(number2), "%d",
+                         uis->_.string_data.result_maxsize);
+
+            if (l < uis->_.string_data.result_minsize) {
+                ui->flags |= UI_FLAG_REDOABLE;
+                UIerr(UI_F_UI_SET_RESULT, UI_R_RESULT_TOO_SMALL);
+                ERR_add_error_data(5, "You must type in ",
+                                   number1, " to ", number2, " characters");
+                return -1;
+            }
+            if (l > uis->_.string_data.result_maxsize) {
+                ui->flags |= UI_FLAG_REDOABLE;
+                UIerr(UI_F_UI_SET_RESULT, UI_R_RESULT_TOO_LARGE);
+                ERR_add_error_data(5, "You must type in ",
+                                   number1, " to ", number2, " characters");
+                return -1;
+            }
+        }
+
+        if (uis->result_buf == NULL) {
+            UIerr(UI_F_UI_SET_RESULT, UI_R_NO_RESULT_BUFFER);
+            return -1;
+        }
+
+        OPENSSL_strlcpy(uis->result_buf, result,
+                    uis->_.string_data.result_maxsize + 1);
+        break;
+    case UIT_BOOLEAN:
+        {
+            const char *p;
+
+            if (uis->result_buf == NULL) {
+                UIerr(UI_F_UI_SET_RESULT, UI_R_NO_RESULT_BUFFER);
+                return -1;
+            }
+
+            uis->result_buf[0] = '\0';
+            for (p = result; *p; p++) {
+                if (strchr(uis->_.boolean_data.ok_chars, *p)) {
+                    uis->result_buf[0] = uis->_.boolean_data.ok_chars[0];
+                    break;
+                }
+                if (strchr(uis->_.boolean_data.cancel_chars, *p)) {
+                    uis->result_buf[0] = uis->_.boolean_data.cancel_chars[0];
+                    break;
+                }
+            }
+        }
+    default:
+        break;
+    }
+    return 0;
+}
diff --git a/crypto/ui/ui_locl.h b/crypto/ui/ui_locl.h
new file mode 100644
index 000000000..2953739b7
--- /dev/null
+++ b/crypto/ui/ui_locl.h
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2001-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#ifndef HEADER_UI_LOCL_H
+# define HEADER_UI_LOCL_H
+
+# include 
+# include 
+
+# ifdef _
+#  undef _
+# endif
+
+struct ui_method_st {
+    char *name;
+    /*
+     * All the functions return 1 or non-NULL for success and 0 or NULL for
+     * failure
+     */
+    /*
+     * Open whatever channel for this, be it the console, an X window or
+     * whatever. This function should use the ex_data structure to save
+     * intermediate data.
+     */
+    int (*ui_open_session) (UI *ui);
+    int (*ui_write_string) (UI *ui, UI_STRING *uis);
+    /*
+     * Flush the output.  If a GUI dialog box is used, this function can be
+     * used to actually display it.
+     */
+    int (*ui_flush) (UI *ui);
+    int (*ui_read_string) (UI *ui, UI_STRING *uis);
+    int (*ui_close_session) (UI *ui);
+    /*
+     * Construct a prompt in a user-defined manner.  object_desc is a textual
+     * short description of the object, for example "pass phrase", and
+     * object_name is the name of the object (might be a card name or a file
+     * name. The returned string shall always be allocated on the heap with
+     * OPENSSL_malloc(), and need to be free'd with OPENSSL_free().
+     */
+    char *(*ui_construct_prompt) (UI *ui, const char *object_desc,
+                                  const char *object_name);
+};
+
+struct ui_string_st {
+    enum UI_string_types type;  /* Input */
+    const char *out_string;     /* Input */
+    int input_flags;            /* Flags from the user */
+    /*
+     * The following parameters are completely irrelevant for UIT_INFO, and
+     * can therefore be set to 0 or NULL
+     */
+    char *result_buf;           /* Input and Output: If not NULL,
+                                 * user-defined with size in result_maxsize.
+                                 * Otherwise, it may be allocated by the UI
+                                 * routine, meaning result_minsize is going
+                                 * to be overwritten. */
+    union {
+        struct {
+            int result_minsize; /* Input: minimum required size of the
+                                 * result. */
+            int result_maxsize; /* Input: maximum permitted size of the
+                                 * result */
+            const char *test_buf; /* Input: test string to verify against */
+        } string_data;
+        struct {
+            const char *action_desc; /* Input */
+            const char *ok_chars; /* Input */
+            const char *cancel_chars; /* Input */
+        } boolean_data;
+    } _;
+
+# define OUT_STRING_FREEABLE 0x01
+    int flags;                  /* flags for internal use */
+};
+
+struct ui_st {
+    const UI_METHOD *meth;
+    STACK_OF(UI_STRING) *strings; /* We might want to prompt for more than
+                                   * one thing at a time, and with different
+                                   * echoing status.  */
+    void *user_data;
+    CRYPTO_EX_DATA ex_data;
+# define UI_FLAG_REDOABLE        0x0001
+# define UI_FLAG_PRINT_ERRORS    0x0100
+    int flags;
+
+    CRYPTO_RWLOCK *lock;
+};
+
+#endif
diff --git a/crypto/ui/ui_openssl.c b/crypto/ui/ui_openssl.c
new file mode 100644
index 000000000..ed0bfa0b3
--- /dev/null
+++ b/crypto/ui/ui_openssl.c
@@ -0,0 +1,700 @@
+/*
+ * Copyright 2001-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+
+/*
+ * need for #define _POSIX_C_SOURCE arises whenever you pass -ansi to gcc
+ * [maybe others?], because it masks interfaces not discussed in standard,
+ * sigaction and fileno included. -pedantic would be more appropriate for the
+ * intended purposes, but we can't prevent users from adding -ansi.
+ */
+#if defined(OPENSSL_SYS_VXWORKS)
+# include 
+#endif
+
+#if !defined(_POSIX_C_SOURCE) && defined(OPENSSL_SYS_VMS)
+# ifndef _POSIX_C_SOURCE
+#  define _POSIX_C_SOURCE 2
+# endif
+#endif
+#include 
+#include 
+#include 
+#include 
+
+#if !defined(OPENSSL_SYS_MSDOS) && !defined(OPENSSL_SYS_VMS)
+# ifdef OPENSSL_UNISTD
+#  include OPENSSL_UNISTD
+# else
+#  include 
+# endif
+/*
+ * If unistd.h defines _POSIX_VERSION, we conclude that we are on a POSIX
+ * system and have sigaction and termios.
+ */
+# if defined(_POSIX_VERSION)
+
+#  define SIGACTION
+#  if !defined(TERMIOS) && !defined(TERMIO) && !defined(SGTTY)
+#   define TERMIOS
+#  endif
+
+# endif
+#endif
+
+/* 06-Apr-92 Luke Brennan    Support for VMS */
+#include "ui_locl.h"
+#include "internal/cryptlib.h"
+
+#ifdef OPENSSL_SYS_VMS          /* prototypes for sys$whatever */
+# include 
+# ifdef __DECC
+#  pragma message disable DOLLARID
+# endif
+#endif
+
+#ifdef WIN_CONSOLE_BUG
+# include 
+# ifndef OPENSSL_SYS_WINCE
+#  include 
+# endif
+#endif
+
+/*
+ * There are 6 types of terminal interface supported, TERMIO, TERMIOS, VMS,
+ * MSDOS, WIN32 Console and SGTTY.
+ *
+ * If someone defines one of the macros TERMIO, TERMIOS or SGTTY, it will
+ * remain respected.  Otherwise, we default to TERMIOS except for a few
+ * systems that require something different.
+ *
+ * Note: we do not use SGTTY unless it's defined by the configuration.  We
+ * may eventually opt to remove it's use entirely.
+ */
+
+#if !defined(TERMIOS) && !defined(TERMIO) && !defined(SGTTY)
+
+# if defined(_LIBC)
+#  undef  TERMIOS
+#  define TERMIO
+#  undef  SGTTY
+/*
+ * We know that VMS, MSDOS, VXWORKS, use entirely other mechanisms.
+ */
+# elif !defined(OPENSSL_SYS_VMS) \
+	&& !defined(OPENSSL_SYS_MSDOS) \
+	&& !defined(OPENSSL_SYS_VXWORKS)
+#  define TERMIOS
+#  undef  TERMIO
+#  undef  SGTTY
+# endif
+
+#endif
+
+#ifdef TERMIOS
+# include 
+# define TTY_STRUCT             struct termios
+# define TTY_FLAGS              c_lflag
+# define TTY_get(tty,data)      tcgetattr(tty,data)
+# define TTY_set(tty,data)      tcsetattr(tty,TCSANOW,data)
+#endif
+
+#ifdef TERMIO
+# include 
+# define TTY_STRUCT             struct termio
+# define TTY_FLAGS              c_lflag
+# define TTY_get(tty,data)      ioctl(tty,TCGETA,data)
+# define TTY_set(tty,data)      ioctl(tty,TCSETA,data)
+#endif
+
+#ifdef SGTTY
+# include 
+# define TTY_STRUCT             struct sgttyb
+# define TTY_FLAGS              sg_flags
+# define TTY_get(tty,data)      ioctl(tty,TIOCGETP,data)
+# define TTY_set(tty,data)      ioctl(tty,TIOCSETP,data)
+#endif
+
+#if !defined(_LIBC) && !defined(OPENSSL_SYS_MSDOS) && !defined(OPENSSL_SYS_VMS)
+# include 
+#endif
+
+#ifdef OPENSSL_SYS_MSDOS
+# include 
+#endif
+
+#ifdef OPENSSL_SYS_VMS
+# include 
+# include 
+# include 
+# include 
+struct IOSB {
+    short iosb$w_value;
+    short iosb$w_count;
+    long iosb$l_info;
+};
+#endif
+
+#ifndef NX509_SIG
+# define NX509_SIG 32
+#endif
+
+/* Define globals.  They are protected by a lock */
+#ifdef SIGACTION
+static struct sigaction savsig[NX509_SIG];
+#else
+static void (*savsig[NX509_SIG]) (int);
+#endif
+
+#ifdef OPENSSL_SYS_VMS
+static struct IOSB iosb;
+static $DESCRIPTOR(terminal, "TT");
+static long tty_orig[3], tty_new[3]; /* XXX Is there any guarantee that this
+                                      * will always suffice for the actual
+                                      * structures? */
+static long status;
+static unsigned short channel = 0;
+#elif defined(_WIN32) && !defined(_WIN32_WCE)
+static DWORD tty_orig, tty_new;
+#else
+# if !defined(OPENSSL_SYS_MSDOS) || defined(__DJGPP__)
+static TTY_STRUCT tty_orig, tty_new;
+# endif
+#endif
+static FILE *tty_in, *tty_out;
+static int is_a_tty;
+
+/* Declare static functions */
+#if !defined(OPENSSL_SYS_WINCE)
+static int read_till_nl(FILE *);
+static void recsig(int);
+static void pushsig(void);
+static void popsig(void);
+#endif
+#if defined(OPENSSL_SYS_MSDOS) && !defined(_WIN32)
+static int noecho_fgets(char *buf, int size, FILE *tty);
+#endif
+static int read_string_inner(UI *ui, UI_STRING *uis, int echo, int strip_nl);
+
+static int read_string(UI *ui, UI_STRING *uis);
+static int write_string(UI *ui, UI_STRING *uis);
+
+static int open_console(UI *ui);
+static int echo_console(UI *ui);
+static int noecho_console(UI *ui);
+static int close_console(UI *ui);
+
+static UI_METHOD ui_openssl = {
+    "OpenSSL default user interface",
+    open_console,
+    write_string,
+    NULL,                       /* No flusher is needed for command lines */
+    read_string,
+    close_console,
+    NULL
+};
+
+/* The method with all the built-in thingies */
+UI_METHOD *UI_OpenSSL(void)
+{
+    return &ui_openssl;
+}
+
+/*
+ * The following function makes sure that info and error strings are printed
+ * before any prompt.
+ */
+static int write_string(UI *ui, UI_STRING *uis)
+{
+    switch (UI_get_string_type(uis)) {
+    case UIT_ERROR:
+    case UIT_INFO:
+        fputs(UI_get0_output_string(uis), tty_out);
+        fflush(tty_out);
+        break;
+    default:
+        break;
+    }
+    return 1;
+}
+
+static int read_string(UI *ui, UI_STRING *uis)
+{
+    int ok = 0;
+
+    switch (UI_get_string_type(uis)) {
+    case UIT_BOOLEAN:
+        fputs(UI_get0_output_string(uis), tty_out);
+        fputs(UI_get0_action_string(uis), tty_out);
+        fflush(tty_out);
+        return read_string_inner(ui, uis,
+                                 UI_get_input_flags(uis) & UI_INPUT_FLAG_ECHO,
+                                 0);
+    case UIT_PROMPT:
+        fputs(UI_get0_output_string(uis), tty_out);
+        fflush(tty_out);
+        return read_string_inner(ui, uis,
+                                 UI_get_input_flags(uis) & UI_INPUT_FLAG_ECHO,
+                                 1);
+    case UIT_VERIFY:
+        fprintf(tty_out, "Verifying - %s", UI_get0_output_string(uis));
+        fflush(tty_out);
+        if ((ok = read_string_inner(ui, uis,
+                                    UI_get_input_flags(uis) &
+                                    UI_INPUT_FLAG_ECHO, 1)) <= 0)
+            return ok;
+        if (strcmp(UI_get0_result_string(uis), UI_get0_test_string(uis)) != 0) {
+            fprintf(tty_out, "Verify failure\n");
+            fflush(tty_out);
+            return 0;
+        }
+        break;
+    default:
+        break;
+    }
+    return 1;
+}
+
+#if !defined(OPENSSL_SYS_WINCE)
+/* Internal functions to read a string without echoing */
+static int read_till_nl(FILE *in)
+{
+# define SIZE 4
+    char buf[SIZE + 1];
+
+    do {
+        if (!fgets(buf, SIZE, in))
+            return 0;
+    } while (strchr(buf, '\n') == NULL);
+    return 1;
+}
+
+static volatile sig_atomic_t intr_signal;
+#endif
+
+static int read_string_inner(UI *ui, UI_STRING *uis, int echo, int strip_nl)
+{
+    static int ps;
+    int ok;
+    char result[BUFSIZ];
+    int maxsize = BUFSIZ - 1;
+#if !defined(OPENSSL_SYS_WINCE)
+    char *p = NULL;
+    int echo_eol = !echo;
+
+    intr_signal = 0;
+    ok = 0;
+    ps = 0;
+
+    pushsig();
+    ps = 1;
+
+    if (!echo && !noecho_console(ui))
+        goto error;
+    ps = 2;
+
+    result[0] = '\0';
+# if defined(_WIN32)
+    if (is_a_tty) {
+        DWORD numread;
+#  if defined(CP_UTF8)
+        if (GetEnvironmentVariableW(L"OPENSSL_WIN32_UTF8", NULL, 0) != 0) {
+            WCHAR wresult[BUFSIZ];
+
+            if (ReadConsoleW(GetStdHandle(STD_INPUT_HANDLE),
+                         wresult, maxsize, &numread, NULL)) {
+                if (numread >= 2 &&
+                    wresult[numread-2] == L'\r' &&
+                    wresult[numread-1] == L'\n') {
+                    wresult[numread-2] = L'\n';
+                    numread--;
+                }
+                wresult[numread] = '\0';
+                if (WideCharToMultiByte(CP_UTF8, 0, wresult, -1,
+                                        result, sizeof(result), NULL, 0) > 0)
+                    p = result;
+
+                OPENSSL_cleanse(wresult, sizeof(wresult));
+            }
+        } else
+#  endif
+        if (ReadConsoleA(GetStdHandle(STD_INPUT_HANDLE),
+                         result, maxsize, &numread, NULL)) {
+            if (numread >= 2 &&
+                result[numread-2] == '\r' && result[numread-1] == '\n') {
+                result[numread-2] = '\n';
+                numread--;
+            }
+            result[numread] = '\0';
+            p = result;
+        }
+    } else
+# elif defined(OPENSSL_SYS_MSDOS)
+    if (!echo) {
+        noecho_fgets(result, maxsize, tty_in);
+        p = result;             /* FIXME: noecho_fgets doesn't return errors */
+    } else
+# endif
+    p = fgets(result, maxsize, tty_in);
+    if (p == NULL)
+        goto error;
+    if (feof(tty_in))
+        goto error;
+    if (ferror(tty_in))
+        goto error;
+    if ((p = (char *)strchr(result, '\n')) != NULL) {
+        if (strip_nl)
+            *p = '\0';
+    } else if (!read_till_nl(tty_in))
+        goto error;
+    if (UI_set_result(ui, uis, result) >= 0)
+        ok = 1;
+
+ error:
+    if (intr_signal == SIGINT)
+        ok = -1;
+    if (echo_eol)
+        fprintf(tty_out, "\n");
+    if (ps >= 2 && !echo && !echo_console(ui))
+        ok = 0;
+
+    if (ps >= 1)
+        popsig();
+#else
+    ok = 1;
+#endif
+
+    OPENSSL_cleanse(result, BUFSIZ);
+    return ok;
+}
+
+/* Internal functions to open, handle and close a channel to the console.  */
+static int open_console(UI *ui)
+{
+    CRYPTO_THREAD_write_lock(ui->lock);
+    is_a_tty = 1;
+
+#if defined(OPENSSL_SYS_VXWORKS)
+    tty_in = stdin;
+    tty_out = stderr;
+#elif defined(_WIN32) && !defined(_WIN32_WCE)
+    if ((tty_out = fopen("conout$", "w")) == NULL)
+        tty_out = stderr;
+
+    if (GetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), &tty_orig)) {
+        tty_in = stdin;
+    } else {
+        is_a_tty = 0;
+        if ((tty_in = fopen("conin$", "r")) == NULL)
+            tty_in = stdin;
+    }
+#else
+# ifdef OPENSSL_SYS_MSDOS
+#  define DEV_TTY "con"
+# else
+#  define DEV_TTY "/dev/tty"
+# endif
+    if ((tty_in = fopen(DEV_TTY, "r")) == NULL)
+        tty_in = stdin;
+    if ((tty_out = fopen(DEV_TTY, "w")) == NULL)
+        tty_out = stderr;
+#endif
+
+#if defined(TTY_get) && !defined(OPENSSL_SYS_VMS)
+    if (TTY_get(fileno(tty_in), &tty_orig) == -1) {
+# ifdef ENOTTY
+        if (errno == ENOTTY)
+            is_a_tty = 0;
+        else
+# endif
+# ifdef EINVAL
+            /*
+             * Ariel Glenn ariel@columbia.edu reports that solaris can return
+             * EINVAL instead.  This should be ok
+             */
+        if (errno == EINVAL)
+            is_a_tty = 0;
+        else
+# endif
+# ifdef ENODEV
+            /*
+             * MacOS X returns ENODEV (Operation not supported by device),
+             * which seems appropriate.
+             */
+        if (errno == ENODEV)
+            is_a_tty = 0;
+        else
+# endif
+            {
+                char tmp_num[10];
+                BIO_snprintf(tmp_num, sizeof(tmp_num) - 1, "%d", errno);
+                UIerr(UI_F_OPEN_CONSOLE, UI_R_UNKNOWN_TTYGET_ERRNO_VALUE);
+                ERR_add_error_data(2, "errno=", tmp_num);
+
+                return 0;
+            }
+    }
+#endif
+#ifdef OPENSSL_SYS_VMS
+    status = sys$assign(&terminal, &channel, 0, 0);
+
+    /* if there isn't a TT device, something is very wrong */
+    if (status != SS$_NORMAL) {
+        char tmp_num[12];
+
+        BIO_snprintf(tmp_num, sizeof(tmp_num) - 1, "%%X%08X", status);
+        UIerr(UI_F_OPEN_CONSOLE, UI_R_SYSASSIGN_ERROR);
+        ERR_add_error_data(2, "status=", tmp_num);
+        return 0;
+    }
+
+    status = sys$qiow(0, channel, IO$_SENSEMODE, &iosb, 0, 0, tty_orig, 12,
+                      0, 0, 0, 0);
+
+    /* If IO$_SENSEMODE doesn't work, this is not a terminal device */
+    if ((status != SS$_NORMAL) || (iosb.iosb$w_value != SS$_NORMAL))
+        is_a_tty = 0;
+#endif
+    return 1;
+}
+
+static int noecho_console(UI *ui)
+{
+#ifdef TTY_FLAGS
+    memcpy(&(tty_new), &(tty_orig), sizeof(tty_orig));
+    tty_new.TTY_FLAGS &= ~ECHO;
+#endif
+
+#if defined(TTY_set) && !defined(OPENSSL_SYS_VMS)
+    if (is_a_tty && (TTY_set(fileno(tty_in), &tty_new) == -1))
+        return 0;
+#endif
+#ifdef OPENSSL_SYS_VMS
+    if (is_a_tty) {
+        tty_new[0] = tty_orig[0];
+        tty_new[1] = tty_orig[1] | TT$M_NOECHO;
+        tty_new[2] = tty_orig[2];
+        status = sys$qiow(0, channel, IO$_SETMODE, &iosb, 0, 0, tty_new, 12,
+                          0, 0, 0, 0);
+        if ((status != SS$_NORMAL) || (iosb.iosb$w_value != SS$_NORMAL)) {
+            char tmp_num[2][12];
+
+            BIO_snprintf(tmp_num[0], sizeof(tmp_num[0]) - 1, "%%X%08X",
+                         status);
+            BIO_snprintf(tmp_num[1], sizeof(tmp_num[1]) - 1, "%%X%08X",
+                         iosb.iosb$w_value);
+            UIerr(UI_F_NOECHO_CONSOLE, UI_R_SYSQIOW_ERROR);
+            ERR_add_error_data(5, "status=", tmp_num[0],
+                               ",", "iosb.iosb$w_value=", tmp_num[1]);
+            return 0;
+        }
+    }
+#endif
+#if defined(_WIN32) && !defined(_WIN32_WCE)
+    if (is_a_tty) {
+        tty_new = tty_orig;
+        tty_new &= ~ENABLE_ECHO_INPUT;
+        SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), tty_new);
+    }
+#endif
+    return 1;
+}
+
+static int echo_console(UI *ui)
+{
+#if defined(TTY_set) && !defined(OPENSSL_SYS_VMS)
+    memcpy(&(tty_new), &(tty_orig), sizeof(tty_orig));
+    tty_new.TTY_FLAGS |= ECHO;
+#endif
+
+#if defined(TTY_set) && !defined(OPENSSL_SYS_VMS)
+    if (is_a_tty && (TTY_set(fileno(tty_in), &tty_new) == -1))
+        return 0;
+#endif
+#ifdef OPENSSL_SYS_VMS
+    if (is_a_tty) {
+        tty_new[0] = tty_orig[0];
+        tty_new[1] = tty_orig[1] & ~TT$M_NOECHO;
+        tty_new[2] = tty_orig[2];
+        status = sys$qiow(0, channel, IO$_SETMODE, &iosb, 0, 0, tty_new, 12,
+                          0, 0, 0, 0);
+        if ((status != SS$_NORMAL) || (iosb.iosb$w_value != SS$_NORMAL)) {
+            char tmp_num[2][12];
+
+            BIO_snprintf(tmp_num[0], sizeof(tmp_num[0]) - 1, "%%X%08X",
+                         status);
+            BIO_snprintf(tmp_num[1], sizeof(tmp_num[1]) - 1, "%%X%08X",
+                         iosb.iosb$w_value);
+            UIerr(UI_F_ECHO_CONSOLE, UI_R_SYSQIOW_ERROR);
+            ERR_add_error_data(5, "status=", tmp_num[0],
+                               ",", "iosb.iosb$w_value=", tmp_num[1]);
+            return 0;
+        }
+    }
+#endif
+#if defined(_WIN32) && !defined(_WIN32_WCE)
+    if (is_a_tty) {
+        tty_new = tty_orig;
+        tty_new |= ENABLE_ECHO_INPUT;
+        SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), tty_new);
+    }
+#endif
+    return 1;
+}
+
+static int close_console(UI *ui)
+{
+    if (tty_in != stdin)
+        fclose(tty_in);
+    if (tty_out != stderr)
+        fclose(tty_out);
+#ifdef OPENSSL_SYS_VMS
+    status = sys$dassgn(channel);
+    if (status != SS$_NORMAL) {
+        char tmp_num[12];
+
+        BIO_snprintf(tmp_num, sizeof(tmp_num) - 1, "%%X%08X", status);
+        UIerr(UI_F_CLOSE_CONSOLE, UI_R_SYSDASSGN_ERROR);
+        ERR_add_error_data(2, "status=", tmp_num);
+        return 0;
+    }
+#endif
+    CRYPTO_THREAD_unlock(ui->lock);
+
+    return 1;
+}
+
+#if !defined(OPENSSL_SYS_WINCE)
+/* Internal functions to handle signals and act on them */
+static void pushsig(void)
+{
+# ifndef OPENSSL_SYS_WIN32
+    int i;
+# endif
+# ifdef SIGACTION
+    struct sigaction sa;
+
+    memset(&sa, 0, sizeof(sa));
+    sa.sa_handler = recsig;
+# endif
+
+# ifdef OPENSSL_SYS_WIN32
+    savsig[SIGABRT] = signal(SIGABRT, recsig);
+    savsig[SIGFPE] = signal(SIGFPE, recsig);
+    savsig[SIGILL] = signal(SIGILL, recsig);
+    savsig[SIGINT] = signal(SIGINT, recsig);
+    savsig[SIGSEGV] = signal(SIGSEGV, recsig);
+    savsig[SIGTERM] = signal(SIGTERM, recsig);
+# else
+    for (i = 1; i < NX509_SIG; i++) {
+#  ifdef SIGUSR1
+        if (i == SIGUSR1)
+            continue;
+#  endif
+#  ifdef SIGUSR2
+        if (i == SIGUSR2)
+            continue;
+#  endif
+#  ifdef SIGKILL
+        if (i == SIGKILL)       /* We can't make any action on that. */
+            continue;
+#  endif
+#  ifdef SIGACTION
+        sigaction(i, &sa, &savsig[i]);
+#  else
+        savsig[i] = signal(i, recsig);
+#  endif
+    }
+# endif
+
+# ifdef SIGWINCH
+    signal(SIGWINCH, SIG_DFL);
+# endif
+}
+
+static void popsig(void)
+{
+# ifdef OPENSSL_SYS_WIN32
+    signal(SIGABRT, savsig[SIGABRT]);
+    signal(SIGFPE, savsig[SIGFPE]);
+    signal(SIGILL, savsig[SIGILL]);
+    signal(SIGINT, savsig[SIGINT]);
+    signal(SIGSEGV, savsig[SIGSEGV]);
+    signal(SIGTERM, savsig[SIGTERM]);
+# else
+    int i;
+    for (i = 1; i < NX509_SIG; i++) {
+#  ifdef SIGUSR1
+        if (i == SIGUSR1)
+            continue;
+#  endif
+#  ifdef SIGUSR2
+        if (i == SIGUSR2)
+            continue;
+#  endif
+#  ifdef SIGACTION
+        sigaction(i, &savsig[i], NULL);
+#  else
+        signal(i, savsig[i]);
+#  endif
+    }
+# endif
+}
+
+static void recsig(int i)
+{
+    intr_signal = i;
+}
+#endif
+
+/* Internal functions specific for Windows */
+#if defined(OPENSSL_SYS_MSDOS) && !defined(_WIN32)
+static int noecho_fgets(char *buf, int size, FILE *tty)
+{
+    int i;
+    char *p;
+
+    p = buf;
+    for (;;) {
+        if (size == 0) {
+            *p = '\0';
+            break;
+        }
+        size--;
+# if defined(_WIN32)
+        i = _getch();
+# else
+        i = getch();
+# endif
+        if (i == '\r')
+            i = '\n';
+        *(p++) = i;
+        if (i == '\n') {
+            *p = '\0';
+            break;
+        }
+    }
+# ifdef WIN_CONSOLE_BUG
+    /*
+     * Win95 has several evil console bugs: one of these is that the last
+     * character read using getch() is passed to the next read: this is
+     * usually a CR so this can be trouble. No STDIO fix seems to work but
+     * flushing the console appears to do the trick.
+     */
+    {
+        HANDLE inh;
+        inh = GetStdHandle(STD_INPUT_HANDLE);
+        FlushConsoleInputBuffer(inh);
+    }
+# endif
+    return (strlen(buf));
+}
+#endif
diff --git a/crypto/ui/ui_util.c b/crypto/ui/ui_util.c
new file mode 100644
index 000000000..3b51db92c
--- /dev/null
+++ b/crypto/ui/ui_util.c
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2002-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include "ui_locl.h"
+
+#ifndef BUFSIZ
+#define BUFSIZ 256
+#endif
+
+int UI_UTIL_read_pw_string(char *buf, int length, const char *prompt,
+                           int verify)
+{
+    char buff[BUFSIZ];
+    int ret;
+
+    ret =
+        UI_UTIL_read_pw(buf, buff, (length > BUFSIZ) ? BUFSIZ : length,
+                        prompt, verify);
+    OPENSSL_cleanse(buff, BUFSIZ);
+    return (ret);
+}
+
+int UI_UTIL_read_pw(char *buf, char *buff, int size, const char *prompt,
+                    int verify)
+{
+    int ok = 0;
+    UI *ui;
+
+    if (size < 1)
+        return -1;
+
+    ui = UI_new();
+    if (ui != NULL) {
+        ok = UI_add_input_string(ui, prompt, 0, buf, 0, size - 1);
+        if (ok >= 0 && verify)
+            ok = UI_add_verify_string(ui, prompt, 0, buff, 0, size - 1, buf);
+        if (ok >= 0)
+            ok = UI_process(ui);
+        UI_free(ui);
+    }
+    if (ok > 0)
+        ok = 0;
+    return (ok);
+}
diff --git a/crypto/uid.c b/crypto/uid.c
new file mode 100644
index 000000000..12df8a4e8
--- /dev/null
+++ b/crypto/uid.c
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2001-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include 
+#include 
+
+#if defined(__OpenBSD__) || (defined(__FreeBSD__) && __FreeBSD__ > 2)
+
+# include OPENSSL_UNISTD
+
+int OPENSSL_issetugid(void)
+{
+    return issetugid();
+}
+
+#elif defined(OPENSSL_SYS_WIN32) || defined(OPENSSL_SYS_VXWORKS)
+
+int OPENSSL_issetugid(void)
+{
+    return 0;
+}
+
+#else
+
+# include OPENSSL_UNISTD
+# include 
+
+int OPENSSL_issetugid(void)
+{
+    if (getuid() != geteuid())
+        return 1;
+    if (getgid() != getegid())
+        return 1;
+    return 0;
+}
+#endif
diff --git a/crypto/vms_rms.h b/crypto/vms_rms.h
new file mode 100644
index 000000000..3b994a0ab
--- /dev/null
+++ b/crypto/vms_rms.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2011-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#ifdef NAML$C_MAXRSS
+
+# define CC_RMS_NAMX cc$rms_naml
+# define FAB_NAMX fab$l_naml
+# define FAB_OR_NAML( fab, naml) naml
+# define FAB_OR_NAML_DNA naml$l_long_defname
+# define FAB_OR_NAML_DNS naml$l_long_defname_size
+# define FAB_OR_NAML_FNA naml$l_long_filename
+# define FAB_OR_NAML_FNS naml$l_long_filename_size
+# define NAMX_ESA naml$l_long_expand
+# define NAMX_ESL naml$l_long_expand_size
+# define NAMX_ESS naml$l_long_expand_alloc
+# define NAMX_NOP naml$b_nop
+# define SET_NAMX_NO_SHORT_UPCASE( nam) nam.naml$v_no_short_upcase = 1
+
+# if __INITIAL_POINTER_SIZE == 64
+#  define NAMX_DNA_FNA_SET(fab) fab.fab$l_dna = (__char_ptr32) -1; \
+   fab.fab$l_fna = (__char_ptr32) -1;
+# else                          /* __INITIAL_POINTER_SIZE == 64 */
+#  define NAMX_DNA_FNA_SET(fab) fab.fab$l_dna = (char *) -1; \
+   fab.fab$l_fna = (char *) -1;
+# endif                         /* __INITIAL_POINTER_SIZE == 64 [else] */
+
+# define NAMX_MAXRSS NAML$C_MAXRSS
+# define NAMX_STRUCT NAML
+
+#else                           /* def NAML$C_MAXRSS */
+
+# define CC_RMS_NAMX cc$rms_nam
+# define FAB_NAMX fab$l_nam
+# define FAB_OR_NAML( fab, naml) fab
+# define FAB_OR_NAML_DNA fab$l_dna
+# define FAB_OR_NAML_DNS fab$b_dns
+# define FAB_OR_NAML_FNA fab$l_fna
+# define FAB_OR_NAML_FNS fab$b_fns
+# define NAMX_ESA nam$l_esa
+# define NAMX_ESL nam$b_esl
+# define NAMX_ESS nam$b_ess
+# define NAMX_NOP nam$b_nop
+# define NAMX_DNA_FNA_SET(fab)
+# define NAMX_MAXRSS NAM$C_MAXRSS
+# define NAMX_STRUCT NAM
+# ifdef NAM$M_NO_SHORT_UPCASE
+#  define SET_NAMX_NO_SHORT_UPCASE( nam) naml.naml$v_no_short_upcase = 1
+# else                          /* def NAM$M_NO_SHORT_UPCASE */
+#  define SET_NAMX_NO_SHORT_UPCASE( nam)
+# endif                         /* def NAM$M_NO_SHORT_UPCASE [else] */
+
+#endif                          /* def NAML$C_MAXRSS [else] */
diff --git a/crypto/whrlpool/asm/wp-mmx.pl b/crypto/whrlpool/asm/wp-mmx.pl
new file mode 100644
index 000000000..f63945c8b
--- /dev/null
+++ b/crypto/whrlpool/asm/wp-mmx.pl
@@ -0,0 +1,507 @@
+#! /usr/bin/env perl
+# Copyright 2005-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+#
+# ====================================================================
+# Written by Andy Polyakov  for the OpenSSL
+# project. Rights for redistribution and usage in source and binary
+# forms are granted according to the OpenSSL license.
+# ====================================================================
+#
+# whirlpool_block_mmx implementation.
+#
+*SCALE=\(2); # 2 or 8, that is the question:-) Value of 8 results
+# in 16KB large table, which is tough on L1 cache, but eliminates
+# unaligned references to it. Value of 2 results in 4KB table, but
+# 7/8 of references to it are unaligned. AMD cores seem to be
+# allergic to the latter, while Intel ones - to former [see the
+# table]. I stick to value of 2 for two reasons: 1. smaller table
+# minimizes cache trashing and thus mitigates the hazard of side-
+# channel leakage similar to AES cache-timing one; 2. performance
+# gap among different µ-archs is smaller.
+#
+# Performance table lists rounded amounts of CPU cycles spent by
+# whirlpool_block_mmx routine on single 64 byte input block, i.e.
+# smaller is better and asymptotic throughput can be estimated by
+# multiplying 64 by CPU clock frequency and dividing by relevant
+# value from the given table:
+#
+#		$SCALE=2/8	icc8	gcc3	
+# Intel P4	3200/4600	4600(*)	6400
+# Intel PIII	2900/3000	4900	5400
+# AMD K[78]	2500/1800	9900	8200(**)
+#
+# (*)	I've sketched even non-MMX assembler, but for the record
+#	I've failed to beat the Intel compiler on P4, without using
+#	MMX that is...
+# (**)	... on AMD on the other hand non-MMX assembler was observed
+#	to perform significantly better, but I figured this MMX
+#	implementation is even faster anyway, so why bother? As for
+#	pre-MMX AMD core[s], the improvement coefficient is more
+#	than likely to vary anyway and I don't know how. But the
+#	least I know is that gcc-generated code compiled with
+#	-DL_ENDIAN and -DOPENSSL_SMALL_FOOTPRINT [see C module for
+#	details] and optimized for Pentium was observed to perform
+#	*better* on Pentium 100 than unrolled non-MMX assembler
+#	loop... So we just say that I don't know if maintaining
+#	non-MMX implementation would actually pay off, but till
+#	opposite is proved "unlikely" is assumed.
+
+$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+push(@INC,"${dir}","${dir}../../perlasm");
+require "x86asm.pl";
+
+$output=pop;
+open STDOUT,">$output";
+
+&asm_init($ARGV[0],"wp-mmx.pl");
+
+sub L()  { &data_byte(@_); }
+sub LL()
+{	if	($SCALE==2)	{ &data_byte(@_); &data_byte(@_); }
+	elsif	($SCALE==8)	{ for ($i=0;$i<8;$i++) {
+					&data_byte(@_);
+					unshift(@_,pop(@_));
+				  }
+				}
+	else			{ die "unvalid SCALE value"; }
+}
+
+sub scale()
+{	if	($SCALE==2)	{ &lea(@_[0],&DWP(0,@_[1],@_[1])); }
+	elsif	($SCALE==8)	{ &lea(@_[0],&DWP(0,"",@_[1],8));  }
+	else			{ die "unvalid SCALE value";       }
+}
+
+sub row()
+{	if	($SCALE==2)	{ ((8-shift)&7); }
+	elsif	($SCALE==8)	{ (8*shift);     }
+	else			{ die "unvalid SCALE value"; }
+}
+
+$tbl="ebp";
+@mm=("mm0","mm1","mm2","mm3","mm4","mm5","mm6","mm7");
+
+&function_begin_B("whirlpool_block_mmx");
+	&push	("ebp");
+	&push	("ebx");
+	&push	("esi");
+	&push	("edi");
+
+	&mov	("esi",&wparam(0));		# hash value
+	&mov	("edi",&wparam(1));		# input data stream
+	&mov	("ebp",&wparam(2));		# number of chunks in input
+
+	&mov	("eax","esp");			# copy stack pointer
+	&sub	("esp",128+20);			# allocate frame
+	&and	("esp",-64);			# align for cache-line
+
+	&lea	("ebx",&DWP(128,"esp"));
+	&mov	(&DWP(0,"ebx"),"esi");		# save parameter block
+	&mov	(&DWP(4,"ebx"),"edi");
+	&mov	(&DWP(8,"ebx"),"ebp");
+	&mov	(&DWP(16,"ebx"),"eax");		# saved stack pointer
+
+	&call	(&label("pic_point"));
+&set_label("pic_point");
+	&blindpop($tbl);
+	&lea	($tbl,&DWP(&label("table")."-".&label("pic_point"),$tbl));
+
+	&xor	("ecx","ecx");
+	&xor	("edx","edx");
+
+	for($i=0;$i<8;$i++) { &movq(@mm[$i],&QWP($i*8,"esi")); }    # L=H
+&set_label("outerloop");
+	for($i=0;$i<8;$i++) { &movq(&QWP($i*8,"esp"),@mm[$i]); }    # K=L
+	for($i=0;$i<8;$i++) { &pxor(@mm[$i],&QWP($i*8,"edi")); }    # L^=inp
+	for($i=0;$i<8;$i++) { &movq(&QWP(64+$i*8,"esp"),@mm[$i]); } # S=L
+
+	&xor	("esi","esi");
+	&mov	(&DWP(12,"ebx"),"esi");		# zero round counter
+
+&set_label("round",16);
+	&movq	(@mm[0],&QWP(2048*$SCALE,$tbl,"esi",8));	# rc[r]
+	&mov	("eax",&DWP(0,"esp"));
+	&mov	("ebx",&DWP(4,"esp"));
+	&movz	("ecx",&LB("eax"));
+	&movz	("edx",&HB("eax"));
+for($i=0;$i<8;$i++) {
+    my $func = ($i==0)? \&movq : \&pxor;
+	&shr	("eax",16);
+	&scale	("esi","ecx");
+	&movz	("ecx",&LB("eax"));
+	&scale	("edi","edx");
+	&movz	("edx",&HB("eax"));
+	&pxor	(@mm[0],&QWP(&row(0),$tbl,"esi",8));
+	&$func	(@mm[1],&QWP(&row(1),$tbl,"edi",8));
+	&mov	("eax",&DWP(($i+1)*8,"esp"));
+	&scale	("esi","ecx");
+	&movz	("ecx",&LB("ebx"));
+	&scale	("edi","edx");
+	&movz	("edx",&HB("ebx"));
+	&$func	(@mm[2],&QWP(&row(2),$tbl,"esi",8));
+	&$func	(@mm[3],&QWP(&row(3),$tbl,"edi",8));
+	&shr	("ebx",16);
+	&scale	("esi","ecx");
+	&movz	("ecx",&LB("ebx"));
+	&scale	("edi","edx");
+	&movz	("edx",&HB("ebx"));
+	&$func	(@mm[4],&QWP(&row(4),$tbl,"esi",8));
+	&$func	(@mm[5],&QWP(&row(5),$tbl,"edi",8));
+	&mov	("ebx",&DWP(($i+1)*8+4,"esp"));
+	&scale	("esi","ecx");
+	&movz	("ecx",&LB("eax"));
+	&scale	("edi","edx");
+	&movz	("edx",&HB("eax"));
+	&$func	(@mm[6],&QWP(&row(6),$tbl,"esi",8));
+	&$func	(@mm[7],&QWP(&row(7),$tbl,"edi",8));
+    push(@mm,shift(@mm));
+}
+
+	for($i=0;$i<8;$i++) { &movq(&QWP($i*8,"esp"),@mm[$i]); }    # K=L
+
+for($i=0;$i<8;$i++) {
+	&shr	("eax",16);
+	&scale	("esi","ecx");
+	&movz	("ecx",&LB("eax"));
+	&scale	("edi","edx");
+	&movz	("edx",&HB("eax"));
+	&pxor	(@mm[0],&QWP(&row(0),$tbl,"esi",8));
+	&pxor	(@mm[1],&QWP(&row(1),$tbl,"edi",8));
+	&mov	("eax",&DWP(64+($i+1)*8,"esp"))		if ($i<7);
+	&scale	("esi","ecx");
+	&movz	("ecx",&LB("ebx"));
+	&scale	("edi","edx");
+	&movz	("edx",&HB("ebx"));
+	&pxor	(@mm[2],&QWP(&row(2),$tbl,"esi",8));
+	&pxor	(@mm[3],&QWP(&row(3),$tbl,"edi",8));
+	&shr	("ebx",16);
+	&scale	("esi","ecx");
+	&movz	("ecx",&LB("ebx"));
+	&scale	("edi","edx");
+	&movz	("edx",&HB("ebx"));
+	&pxor	(@mm[4],&QWP(&row(4),$tbl,"esi",8));
+	&pxor	(@mm[5],&QWP(&row(5),$tbl,"edi",8));
+	&mov	("ebx",&DWP(64+($i+1)*8+4,"esp"))	if ($i<7);
+	&scale	("esi","ecx");
+	&movz	("ecx",&LB("eax"));
+	&scale	("edi","edx");
+	&movz	("edx",&HB("eax"));
+	&pxor	(@mm[6],&QWP(&row(6),$tbl,"esi",8));
+	&pxor	(@mm[7],&QWP(&row(7),$tbl,"edi",8));
+    push(@mm,shift(@mm));
+}
+	&lea	("ebx",&DWP(128,"esp"));
+	&mov	("esi",&DWP(12,"ebx"));		# pull round counter
+	&add	("esi",1);
+	&cmp	("esi",10);
+	&je	(&label("roundsdone"));
+
+	&mov	(&DWP(12,"ebx"),"esi");		# update round counter
+	for($i=0;$i<8;$i++) { &movq(&QWP(64+$i*8,"esp"),@mm[$i]); } # S=L
+	&jmp	(&label("round"));
+
+&set_label("roundsdone",16);
+	&mov	("esi",&DWP(0,"ebx"));		# reload argument block
+	&mov	("edi",&DWP(4,"ebx"));
+	&mov	("eax",&DWP(8,"ebx"));
+
+	for($i=0;$i<8;$i++) { &pxor(@mm[$i],&QWP($i*8,"edi")); }    # L^=inp
+	for($i=0;$i<8;$i++) { &pxor(@mm[$i],&QWP($i*8,"esi")); }    # L^=H
+	for($i=0;$i<8;$i++) { &movq(&QWP($i*8,"esi"),@mm[$i]); }    # H=L
+
+	&lea	("edi",&DWP(64,"edi"));		# inp+=64
+	&sub	("eax",1);			# num--
+	&jz	(&label("alldone"));
+	&mov	(&DWP(4,"ebx"),"edi");		# update argument block
+	&mov	(&DWP(8,"ebx"),"eax");
+	&jmp	(&label("outerloop"));
+
+&set_label("alldone");
+	&emms	();
+	&mov	("esp",&DWP(16,"ebx"));		# restore saved stack pointer
+	&pop	("edi");
+	&pop	("esi");
+	&pop	("ebx");
+	&pop	("ebp");
+	&ret	();
+
+&align(64);
+&set_label("table");
+	&LL(0x18,0x18,0x60,0x18,0xc0,0x78,0x30,0xd8);
+	&LL(0x23,0x23,0x8c,0x23,0x05,0xaf,0x46,0x26);
+	&LL(0xc6,0xc6,0x3f,0xc6,0x7e,0xf9,0x91,0xb8);
+	&LL(0xe8,0xe8,0x87,0xe8,0x13,0x6f,0xcd,0xfb);
+	&LL(0x87,0x87,0x26,0x87,0x4c,0xa1,0x13,0xcb);
+	&LL(0xb8,0xb8,0xda,0xb8,0xa9,0x62,0x6d,0x11);
+	&LL(0x01,0x01,0x04,0x01,0x08,0x05,0x02,0x09);
+	&LL(0x4f,0x4f,0x21,0x4f,0x42,0x6e,0x9e,0x0d);
+	&LL(0x36,0x36,0xd8,0x36,0xad,0xee,0x6c,0x9b);
+	&LL(0xa6,0xa6,0xa2,0xa6,0x59,0x04,0x51,0xff);
+	&LL(0xd2,0xd2,0x6f,0xd2,0xde,0xbd,0xb9,0x0c);
+	&LL(0xf5,0xf5,0xf3,0xf5,0xfb,0x06,0xf7,0x0e);
+	&LL(0x79,0x79,0xf9,0x79,0xef,0x80,0xf2,0x96);
+	&LL(0x6f,0x6f,0xa1,0x6f,0x5f,0xce,0xde,0x30);
+	&LL(0x91,0x91,0x7e,0x91,0xfc,0xef,0x3f,0x6d);
+	&LL(0x52,0x52,0x55,0x52,0xaa,0x07,0xa4,0xf8);
+	&LL(0x60,0x60,0x9d,0x60,0x27,0xfd,0xc0,0x47);
+	&LL(0xbc,0xbc,0xca,0xbc,0x89,0x76,0x65,0x35);
+	&LL(0x9b,0x9b,0x56,0x9b,0xac,0xcd,0x2b,0x37);
+	&LL(0x8e,0x8e,0x02,0x8e,0x04,0x8c,0x01,0x8a);
+	&LL(0xa3,0xa3,0xb6,0xa3,0x71,0x15,0x5b,0xd2);
+	&LL(0x0c,0x0c,0x30,0x0c,0x60,0x3c,0x18,0x6c);
+	&LL(0x7b,0x7b,0xf1,0x7b,0xff,0x8a,0xf6,0x84);
+	&LL(0x35,0x35,0xd4,0x35,0xb5,0xe1,0x6a,0x80);
+	&LL(0x1d,0x1d,0x74,0x1d,0xe8,0x69,0x3a,0xf5);
+	&LL(0xe0,0xe0,0xa7,0xe0,0x53,0x47,0xdd,0xb3);
+	&LL(0xd7,0xd7,0x7b,0xd7,0xf6,0xac,0xb3,0x21);
+	&LL(0xc2,0xc2,0x2f,0xc2,0x5e,0xed,0x99,0x9c);
+	&LL(0x2e,0x2e,0xb8,0x2e,0x6d,0x96,0x5c,0x43);
+	&LL(0x4b,0x4b,0x31,0x4b,0x62,0x7a,0x96,0x29);
+	&LL(0xfe,0xfe,0xdf,0xfe,0xa3,0x21,0xe1,0x5d);
+	&LL(0x57,0x57,0x41,0x57,0x82,0x16,0xae,0xd5);
+	&LL(0x15,0x15,0x54,0x15,0xa8,0x41,0x2a,0xbd);
+	&LL(0x77,0x77,0xc1,0x77,0x9f,0xb6,0xee,0xe8);
+	&LL(0x37,0x37,0xdc,0x37,0xa5,0xeb,0x6e,0x92);
+	&LL(0xe5,0xe5,0xb3,0xe5,0x7b,0x56,0xd7,0x9e);
+	&LL(0x9f,0x9f,0x46,0x9f,0x8c,0xd9,0x23,0x13);
+	&LL(0xf0,0xf0,0xe7,0xf0,0xd3,0x17,0xfd,0x23);
+	&LL(0x4a,0x4a,0x35,0x4a,0x6a,0x7f,0x94,0x20);
+	&LL(0xda,0xda,0x4f,0xda,0x9e,0x95,0xa9,0x44);
+	&LL(0x58,0x58,0x7d,0x58,0xfa,0x25,0xb0,0xa2);
+	&LL(0xc9,0xc9,0x03,0xc9,0x06,0xca,0x8f,0xcf);
+	&LL(0x29,0x29,0xa4,0x29,0x55,0x8d,0x52,0x7c);
+	&LL(0x0a,0x0a,0x28,0x0a,0x50,0x22,0x14,0x5a);
+	&LL(0xb1,0xb1,0xfe,0xb1,0xe1,0x4f,0x7f,0x50);
+	&LL(0xa0,0xa0,0xba,0xa0,0x69,0x1a,0x5d,0xc9);
+	&LL(0x6b,0x6b,0xb1,0x6b,0x7f,0xda,0xd6,0x14);
+	&LL(0x85,0x85,0x2e,0x85,0x5c,0xab,0x17,0xd9);
+	&LL(0xbd,0xbd,0xce,0xbd,0x81,0x73,0x67,0x3c);
+	&LL(0x5d,0x5d,0x69,0x5d,0xd2,0x34,0xba,0x8f);
+	&LL(0x10,0x10,0x40,0x10,0x80,0x50,0x20,0x90);
+	&LL(0xf4,0xf4,0xf7,0xf4,0xf3,0x03,0xf5,0x07);
+	&LL(0xcb,0xcb,0x0b,0xcb,0x16,0xc0,0x8b,0xdd);
+	&LL(0x3e,0x3e,0xf8,0x3e,0xed,0xc6,0x7c,0xd3);
+	&LL(0x05,0x05,0x14,0x05,0x28,0x11,0x0a,0x2d);
+	&LL(0x67,0x67,0x81,0x67,0x1f,0xe6,0xce,0x78);
+	&LL(0xe4,0xe4,0xb7,0xe4,0x73,0x53,0xd5,0x97);
+	&LL(0x27,0x27,0x9c,0x27,0x25,0xbb,0x4e,0x02);
+	&LL(0x41,0x41,0x19,0x41,0x32,0x58,0x82,0x73);
+	&LL(0x8b,0x8b,0x16,0x8b,0x2c,0x9d,0x0b,0xa7);
+	&LL(0xa7,0xa7,0xa6,0xa7,0x51,0x01,0x53,0xf6);
+	&LL(0x7d,0x7d,0xe9,0x7d,0xcf,0x94,0xfa,0xb2);
+	&LL(0x95,0x95,0x6e,0x95,0xdc,0xfb,0x37,0x49);
+	&LL(0xd8,0xd8,0x47,0xd8,0x8e,0x9f,0xad,0x56);
+	&LL(0xfb,0xfb,0xcb,0xfb,0x8b,0x30,0xeb,0x70);
+	&LL(0xee,0xee,0x9f,0xee,0x23,0x71,0xc1,0xcd);
+	&LL(0x7c,0x7c,0xed,0x7c,0xc7,0x91,0xf8,0xbb);
+	&LL(0x66,0x66,0x85,0x66,0x17,0xe3,0xcc,0x71);
+	&LL(0xdd,0xdd,0x53,0xdd,0xa6,0x8e,0xa7,0x7b);
+	&LL(0x17,0x17,0x5c,0x17,0xb8,0x4b,0x2e,0xaf);
+	&LL(0x47,0x47,0x01,0x47,0x02,0x46,0x8e,0x45);
+	&LL(0x9e,0x9e,0x42,0x9e,0x84,0xdc,0x21,0x1a);
+	&LL(0xca,0xca,0x0f,0xca,0x1e,0xc5,0x89,0xd4);
+	&LL(0x2d,0x2d,0xb4,0x2d,0x75,0x99,0x5a,0x58);
+	&LL(0xbf,0xbf,0xc6,0xbf,0x91,0x79,0x63,0x2e);
+	&LL(0x07,0x07,0x1c,0x07,0x38,0x1b,0x0e,0x3f);
+	&LL(0xad,0xad,0x8e,0xad,0x01,0x23,0x47,0xac);
+	&LL(0x5a,0x5a,0x75,0x5a,0xea,0x2f,0xb4,0xb0);
+	&LL(0x83,0x83,0x36,0x83,0x6c,0xb5,0x1b,0xef);
+	&LL(0x33,0x33,0xcc,0x33,0x85,0xff,0x66,0xb6);
+	&LL(0x63,0x63,0x91,0x63,0x3f,0xf2,0xc6,0x5c);
+	&LL(0x02,0x02,0x08,0x02,0x10,0x0a,0x04,0x12);
+	&LL(0xaa,0xaa,0x92,0xaa,0x39,0x38,0x49,0x93);
+	&LL(0x71,0x71,0xd9,0x71,0xaf,0xa8,0xe2,0xde);
+	&LL(0xc8,0xc8,0x07,0xc8,0x0e,0xcf,0x8d,0xc6);
+	&LL(0x19,0x19,0x64,0x19,0xc8,0x7d,0x32,0xd1);
+	&LL(0x49,0x49,0x39,0x49,0x72,0x70,0x92,0x3b);
+	&LL(0xd9,0xd9,0x43,0xd9,0x86,0x9a,0xaf,0x5f);
+	&LL(0xf2,0xf2,0xef,0xf2,0xc3,0x1d,0xf9,0x31);
+	&LL(0xe3,0xe3,0xab,0xe3,0x4b,0x48,0xdb,0xa8);
+	&LL(0x5b,0x5b,0x71,0x5b,0xe2,0x2a,0xb6,0xb9);
+	&LL(0x88,0x88,0x1a,0x88,0x34,0x92,0x0d,0xbc);
+	&LL(0x9a,0x9a,0x52,0x9a,0xa4,0xc8,0x29,0x3e);
+	&LL(0x26,0x26,0x98,0x26,0x2d,0xbe,0x4c,0x0b);
+	&LL(0x32,0x32,0xc8,0x32,0x8d,0xfa,0x64,0xbf);
+	&LL(0xb0,0xb0,0xfa,0xb0,0xe9,0x4a,0x7d,0x59);
+	&LL(0xe9,0xe9,0x83,0xe9,0x1b,0x6a,0xcf,0xf2);
+	&LL(0x0f,0x0f,0x3c,0x0f,0x78,0x33,0x1e,0x77);
+	&LL(0xd5,0xd5,0x73,0xd5,0xe6,0xa6,0xb7,0x33);
+	&LL(0x80,0x80,0x3a,0x80,0x74,0xba,0x1d,0xf4);
+	&LL(0xbe,0xbe,0xc2,0xbe,0x99,0x7c,0x61,0x27);
+	&LL(0xcd,0xcd,0x13,0xcd,0x26,0xde,0x87,0xeb);
+	&LL(0x34,0x34,0xd0,0x34,0xbd,0xe4,0x68,0x89);
+	&LL(0x48,0x48,0x3d,0x48,0x7a,0x75,0x90,0x32);
+	&LL(0xff,0xff,0xdb,0xff,0xab,0x24,0xe3,0x54);
+	&LL(0x7a,0x7a,0xf5,0x7a,0xf7,0x8f,0xf4,0x8d);
+	&LL(0x90,0x90,0x7a,0x90,0xf4,0xea,0x3d,0x64);
+	&LL(0x5f,0x5f,0x61,0x5f,0xc2,0x3e,0xbe,0x9d);
+	&LL(0x20,0x20,0x80,0x20,0x1d,0xa0,0x40,0x3d);
+	&LL(0x68,0x68,0xbd,0x68,0x67,0xd5,0xd0,0x0f);
+	&LL(0x1a,0x1a,0x68,0x1a,0xd0,0x72,0x34,0xca);
+	&LL(0xae,0xae,0x82,0xae,0x19,0x2c,0x41,0xb7);
+	&LL(0xb4,0xb4,0xea,0xb4,0xc9,0x5e,0x75,0x7d);
+	&LL(0x54,0x54,0x4d,0x54,0x9a,0x19,0xa8,0xce);
+	&LL(0x93,0x93,0x76,0x93,0xec,0xe5,0x3b,0x7f);
+	&LL(0x22,0x22,0x88,0x22,0x0d,0xaa,0x44,0x2f);
+	&LL(0x64,0x64,0x8d,0x64,0x07,0xe9,0xc8,0x63);
+	&LL(0xf1,0xf1,0xe3,0xf1,0xdb,0x12,0xff,0x2a);
+	&LL(0x73,0x73,0xd1,0x73,0xbf,0xa2,0xe6,0xcc);
+	&LL(0x12,0x12,0x48,0x12,0x90,0x5a,0x24,0x82);
+	&LL(0x40,0x40,0x1d,0x40,0x3a,0x5d,0x80,0x7a);
+	&LL(0x08,0x08,0x20,0x08,0x40,0x28,0x10,0x48);
+	&LL(0xc3,0xc3,0x2b,0xc3,0x56,0xe8,0x9b,0x95);
+	&LL(0xec,0xec,0x97,0xec,0x33,0x7b,0xc5,0xdf);
+	&LL(0xdb,0xdb,0x4b,0xdb,0x96,0x90,0xab,0x4d);
+	&LL(0xa1,0xa1,0xbe,0xa1,0x61,0x1f,0x5f,0xc0);
+	&LL(0x8d,0x8d,0x0e,0x8d,0x1c,0x83,0x07,0x91);
+	&LL(0x3d,0x3d,0xf4,0x3d,0xf5,0xc9,0x7a,0xc8);
+	&LL(0x97,0x97,0x66,0x97,0xcc,0xf1,0x33,0x5b);
+	&LL(0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00);
+	&LL(0xcf,0xcf,0x1b,0xcf,0x36,0xd4,0x83,0xf9);
+	&LL(0x2b,0x2b,0xac,0x2b,0x45,0x87,0x56,0x6e);
+	&LL(0x76,0x76,0xc5,0x76,0x97,0xb3,0xec,0xe1);
+	&LL(0x82,0x82,0x32,0x82,0x64,0xb0,0x19,0xe6);
+	&LL(0xd6,0xd6,0x7f,0xd6,0xfe,0xa9,0xb1,0x28);
+	&LL(0x1b,0x1b,0x6c,0x1b,0xd8,0x77,0x36,0xc3);
+	&LL(0xb5,0xb5,0xee,0xb5,0xc1,0x5b,0x77,0x74);
+	&LL(0xaf,0xaf,0x86,0xaf,0x11,0x29,0x43,0xbe);
+	&LL(0x6a,0x6a,0xb5,0x6a,0x77,0xdf,0xd4,0x1d);
+	&LL(0x50,0x50,0x5d,0x50,0xba,0x0d,0xa0,0xea);
+	&LL(0x45,0x45,0x09,0x45,0x12,0x4c,0x8a,0x57);
+	&LL(0xf3,0xf3,0xeb,0xf3,0xcb,0x18,0xfb,0x38);
+	&LL(0x30,0x30,0xc0,0x30,0x9d,0xf0,0x60,0xad);
+	&LL(0xef,0xef,0x9b,0xef,0x2b,0x74,0xc3,0xc4);
+	&LL(0x3f,0x3f,0xfc,0x3f,0xe5,0xc3,0x7e,0xda);
+	&LL(0x55,0x55,0x49,0x55,0x92,0x1c,0xaa,0xc7);
+	&LL(0xa2,0xa2,0xb2,0xa2,0x79,0x10,0x59,0xdb);
+	&LL(0xea,0xea,0x8f,0xea,0x03,0x65,0xc9,0xe9);
+	&LL(0x65,0x65,0x89,0x65,0x0f,0xec,0xca,0x6a);
+	&LL(0xba,0xba,0xd2,0xba,0xb9,0x68,0x69,0x03);
+	&LL(0x2f,0x2f,0xbc,0x2f,0x65,0x93,0x5e,0x4a);
+	&LL(0xc0,0xc0,0x27,0xc0,0x4e,0xe7,0x9d,0x8e);
+	&LL(0xde,0xde,0x5f,0xde,0xbe,0x81,0xa1,0x60);
+	&LL(0x1c,0x1c,0x70,0x1c,0xe0,0x6c,0x38,0xfc);
+	&LL(0xfd,0xfd,0xd3,0xfd,0xbb,0x2e,0xe7,0x46);
+	&LL(0x4d,0x4d,0x29,0x4d,0x52,0x64,0x9a,0x1f);
+	&LL(0x92,0x92,0x72,0x92,0xe4,0xe0,0x39,0x76);
+	&LL(0x75,0x75,0xc9,0x75,0x8f,0xbc,0xea,0xfa);
+	&LL(0x06,0x06,0x18,0x06,0x30,0x1e,0x0c,0x36);
+	&LL(0x8a,0x8a,0x12,0x8a,0x24,0x98,0x09,0xae);
+	&LL(0xb2,0xb2,0xf2,0xb2,0xf9,0x40,0x79,0x4b);
+	&LL(0xe6,0xe6,0xbf,0xe6,0x63,0x59,0xd1,0x85);
+	&LL(0x0e,0x0e,0x38,0x0e,0x70,0x36,0x1c,0x7e);
+	&LL(0x1f,0x1f,0x7c,0x1f,0xf8,0x63,0x3e,0xe7);
+	&LL(0x62,0x62,0x95,0x62,0x37,0xf7,0xc4,0x55);
+	&LL(0xd4,0xd4,0x77,0xd4,0xee,0xa3,0xb5,0x3a);
+	&LL(0xa8,0xa8,0x9a,0xa8,0x29,0x32,0x4d,0x81);
+	&LL(0x96,0x96,0x62,0x96,0xc4,0xf4,0x31,0x52);
+	&LL(0xf9,0xf9,0xc3,0xf9,0x9b,0x3a,0xef,0x62);
+	&LL(0xc5,0xc5,0x33,0xc5,0x66,0xf6,0x97,0xa3);
+	&LL(0x25,0x25,0x94,0x25,0x35,0xb1,0x4a,0x10);
+	&LL(0x59,0x59,0x79,0x59,0xf2,0x20,0xb2,0xab);
+	&LL(0x84,0x84,0x2a,0x84,0x54,0xae,0x15,0xd0);
+	&LL(0x72,0x72,0xd5,0x72,0xb7,0xa7,0xe4,0xc5);
+	&LL(0x39,0x39,0xe4,0x39,0xd5,0xdd,0x72,0xec);
+	&LL(0x4c,0x4c,0x2d,0x4c,0x5a,0x61,0x98,0x16);
+	&LL(0x5e,0x5e,0x65,0x5e,0xca,0x3b,0xbc,0x94);
+	&LL(0x78,0x78,0xfd,0x78,0xe7,0x85,0xf0,0x9f);
+	&LL(0x38,0x38,0xe0,0x38,0xdd,0xd8,0x70,0xe5);
+	&LL(0x8c,0x8c,0x0a,0x8c,0x14,0x86,0x05,0x98);
+	&LL(0xd1,0xd1,0x63,0xd1,0xc6,0xb2,0xbf,0x17);
+	&LL(0xa5,0xa5,0xae,0xa5,0x41,0x0b,0x57,0xe4);
+	&LL(0xe2,0xe2,0xaf,0xe2,0x43,0x4d,0xd9,0xa1);
+	&LL(0x61,0x61,0x99,0x61,0x2f,0xf8,0xc2,0x4e);
+	&LL(0xb3,0xb3,0xf6,0xb3,0xf1,0x45,0x7b,0x42);
+	&LL(0x21,0x21,0x84,0x21,0x15,0xa5,0x42,0x34);
+	&LL(0x9c,0x9c,0x4a,0x9c,0x94,0xd6,0x25,0x08);
+	&LL(0x1e,0x1e,0x78,0x1e,0xf0,0x66,0x3c,0xee);
+	&LL(0x43,0x43,0x11,0x43,0x22,0x52,0x86,0x61);
+	&LL(0xc7,0xc7,0x3b,0xc7,0x76,0xfc,0x93,0xb1);
+	&LL(0xfc,0xfc,0xd7,0xfc,0xb3,0x2b,0xe5,0x4f);
+	&LL(0x04,0x04,0x10,0x04,0x20,0x14,0x08,0x24);
+	&LL(0x51,0x51,0x59,0x51,0xb2,0x08,0xa2,0xe3);
+	&LL(0x99,0x99,0x5e,0x99,0xbc,0xc7,0x2f,0x25);
+	&LL(0x6d,0x6d,0xa9,0x6d,0x4f,0xc4,0xda,0x22);
+	&LL(0x0d,0x0d,0x34,0x0d,0x68,0x39,0x1a,0x65);
+	&LL(0xfa,0xfa,0xcf,0xfa,0x83,0x35,0xe9,0x79);
+	&LL(0xdf,0xdf,0x5b,0xdf,0xb6,0x84,0xa3,0x69);
+	&LL(0x7e,0x7e,0xe5,0x7e,0xd7,0x9b,0xfc,0xa9);
+	&LL(0x24,0x24,0x90,0x24,0x3d,0xb4,0x48,0x19);
+	&LL(0x3b,0x3b,0xec,0x3b,0xc5,0xd7,0x76,0xfe);
+	&LL(0xab,0xab,0x96,0xab,0x31,0x3d,0x4b,0x9a);
+	&LL(0xce,0xce,0x1f,0xce,0x3e,0xd1,0x81,0xf0);
+	&LL(0x11,0x11,0x44,0x11,0x88,0x55,0x22,0x99);
+	&LL(0x8f,0x8f,0x06,0x8f,0x0c,0x89,0x03,0x83);
+	&LL(0x4e,0x4e,0x25,0x4e,0x4a,0x6b,0x9c,0x04);
+	&LL(0xb7,0xb7,0xe6,0xb7,0xd1,0x51,0x73,0x66);
+	&LL(0xeb,0xeb,0x8b,0xeb,0x0b,0x60,0xcb,0xe0);
+	&LL(0x3c,0x3c,0xf0,0x3c,0xfd,0xcc,0x78,0xc1);
+	&LL(0x81,0x81,0x3e,0x81,0x7c,0xbf,0x1f,0xfd);
+	&LL(0x94,0x94,0x6a,0x94,0xd4,0xfe,0x35,0x40);
+	&LL(0xf7,0xf7,0xfb,0xf7,0xeb,0x0c,0xf3,0x1c);
+	&LL(0xb9,0xb9,0xde,0xb9,0xa1,0x67,0x6f,0x18);
+	&LL(0x13,0x13,0x4c,0x13,0x98,0x5f,0x26,0x8b);
+	&LL(0x2c,0x2c,0xb0,0x2c,0x7d,0x9c,0x58,0x51);
+	&LL(0xd3,0xd3,0x6b,0xd3,0xd6,0xb8,0xbb,0x05);
+	&LL(0xe7,0xe7,0xbb,0xe7,0x6b,0x5c,0xd3,0x8c);
+	&LL(0x6e,0x6e,0xa5,0x6e,0x57,0xcb,0xdc,0x39);
+	&LL(0xc4,0xc4,0x37,0xc4,0x6e,0xf3,0x95,0xaa);
+	&LL(0x03,0x03,0x0c,0x03,0x18,0x0f,0x06,0x1b);
+	&LL(0x56,0x56,0x45,0x56,0x8a,0x13,0xac,0xdc);
+	&LL(0x44,0x44,0x0d,0x44,0x1a,0x49,0x88,0x5e);
+	&LL(0x7f,0x7f,0xe1,0x7f,0xdf,0x9e,0xfe,0xa0);
+	&LL(0xa9,0xa9,0x9e,0xa9,0x21,0x37,0x4f,0x88);
+	&LL(0x2a,0x2a,0xa8,0x2a,0x4d,0x82,0x54,0x67);
+	&LL(0xbb,0xbb,0xd6,0xbb,0xb1,0x6d,0x6b,0x0a);
+	&LL(0xc1,0xc1,0x23,0xc1,0x46,0xe2,0x9f,0x87);
+	&LL(0x53,0x53,0x51,0x53,0xa2,0x02,0xa6,0xf1);
+	&LL(0xdc,0xdc,0x57,0xdc,0xae,0x8b,0xa5,0x72);
+	&LL(0x0b,0x0b,0x2c,0x0b,0x58,0x27,0x16,0x53);
+	&LL(0x9d,0x9d,0x4e,0x9d,0x9c,0xd3,0x27,0x01);
+	&LL(0x6c,0x6c,0xad,0x6c,0x47,0xc1,0xd8,0x2b);
+	&LL(0x31,0x31,0xc4,0x31,0x95,0xf5,0x62,0xa4);
+	&LL(0x74,0x74,0xcd,0x74,0x87,0xb9,0xe8,0xf3);
+	&LL(0xf6,0xf6,0xff,0xf6,0xe3,0x09,0xf1,0x15);
+	&LL(0x46,0x46,0x05,0x46,0x0a,0x43,0x8c,0x4c);
+	&LL(0xac,0xac,0x8a,0xac,0x09,0x26,0x45,0xa5);
+	&LL(0x89,0x89,0x1e,0x89,0x3c,0x97,0x0f,0xb5);
+	&LL(0x14,0x14,0x50,0x14,0xa0,0x44,0x28,0xb4);
+	&LL(0xe1,0xe1,0xa3,0xe1,0x5b,0x42,0xdf,0xba);
+	&LL(0x16,0x16,0x58,0x16,0xb0,0x4e,0x2c,0xa6);
+	&LL(0x3a,0x3a,0xe8,0x3a,0xcd,0xd2,0x74,0xf7);
+	&LL(0x69,0x69,0xb9,0x69,0x6f,0xd0,0xd2,0x06);
+	&LL(0x09,0x09,0x24,0x09,0x48,0x2d,0x12,0x41);
+	&LL(0x70,0x70,0xdd,0x70,0xa7,0xad,0xe0,0xd7);
+	&LL(0xb6,0xb6,0xe2,0xb6,0xd9,0x54,0x71,0x6f);
+	&LL(0xd0,0xd0,0x67,0xd0,0xce,0xb7,0xbd,0x1e);
+	&LL(0xed,0xed,0x93,0xed,0x3b,0x7e,0xc7,0xd6);
+	&LL(0xcc,0xcc,0x17,0xcc,0x2e,0xdb,0x85,0xe2);
+	&LL(0x42,0x42,0x15,0x42,0x2a,0x57,0x84,0x68);
+	&LL(0x98,0x98,0x5a,0x98,0xb4,0xc2,0x2d,0x2c);
+	&LL(0xa4,0xa4,0xaa,0xa4,0x49,0x0e,0x55,0xed);
+	&LL(0x28,0x28,0xa0,0x28,0x5d,0x88,0x50,0x75);
+	&LL(0x5c,0x5c,0x6d,0x5c,0xda,0x31,0xb8,0x86);
+	&LL(0xf8,0xf8,0xc7,0xf8,0x93,0x3f,0xed,0x6b);
+	&LL(0x86,0x86,0x22,0x86,0x44,0xa4,0x11,0xc2);
+
+	&L(0x18,0x23,0xc6,0xe8,0x87,0xb8,0x01,0x4f);	# rc[ROUNDS]
+	&L(0x36,0xa6,0xd2,0xf5,0x79,0x6f,0x91,0x52);
+	&L(0x60,0xbc,0x9b,0x8e,0xa3,0x0c,0x7b,0x35);
+	&L(0x1d,0xe0,0xd7,0xc2,0x2e,0x4b,0xfe,0x57);
+	&L(0x15,0x77,0x37,0xe5,0x9f,0xf0,0x4a,0xda);
+	&L(0x58,0xc9,0x29,0x0a,0xb1,0xa0,0x6b,0x85);
+	&L(0xbd,0x5d,0x10,0xf4,0xcb,0x3e,0x05,0x67);
+	&L(0xe4,0x27,0x41,0x8b,0xa7,0x7d,0x95,0xd8);
+	&L(0xfb,0xee,0x7c,0x66,0xdd,0x17,0x47,0x9e);
+	&L(0xca,0x2d,0xbf,0x07,0xad,0x5a,0x83,0x33);
+
+&function_end_B("whirlpool_block_mmx");
+&asm_finish(); 
+
+close STDOUT;
diff --git a/crypto/whrlpool/asm/wp-x86_64.pl b/crypto/whrlpool/asm/wp-x86_64.pl
new file mode 100644
index 000000000..c0b21d13e
--- /dev/null
+++ b/crypto/whrlpool/asm/wp-x86_64.pl
@@ -0,0 +1,600 @@
+#! /usr/bin/env perl
+# Copyright 2005-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+#
+# ====================================================================
+# Written by Andy Polyakov  for the OpenSSL
+# project. Rights for redistribution and usage in source and binary
+# forms are granted according to the OpenSSL license.
+# ====================================================================
+#
+# whirlpool_block for x86_64.
+#
+# 2500 cycles per 64-byte input block on AMD64, which is *identical*
+# to 32-bit MMX version executed on same CPU. So why did I bother?
+# Well, it's faster than gcc 3.3.2 generated code by over 50%, and
+# over 80% faster than PathScale 1.4, an "ambitious" commercial
+# compiler. Furthermore it surpasses gcc 3.4.3 by 170% and Sun Studio
+# 10 - by 360%[!]... What is it with x86_64 compilers? It's not the
+# first example when they fail to generate more optimal code, when
+# I believe they had *all* chances to...
+#
+# Note that register and stack frame layout are virtually identical
+# to 32-bit MMX version, except that %r8-15 are used instead of
+# %mm0-8. You can even notice that K[i] and S[i] are loaded to
+# %eax:%ebx as pair of 32-bit values and not as single 64-bit one.
+# This is done in order to avoid 64-bit shift penalties on Intel
+# EM64T core. Speaking of which! I bet it's possible to improve
+# Opteron performance by compressing the table to 2KB and replacing
+# unaligned references with complementary rotations [which would
+# incidentally replace lea instructions], but it would definitely
+# just "kill" EM64T, because it has only 1 shifter/rotator [against
+# 3 on Opteron] and which is *unacceptably* slow with 64-bit
+# operand.
+
+$flavour = shift;
+$output  = shift;
+if ($flavour =~ /\./) { $output = $flavour; undef $flavour; }
+
+$win64=0; $win64=1 if ($flavour =~ /[nm]asm|mingw64/ || $output =~ /\.asm$/);
+
+$0 =~ m/(.*[\/\\])[^\/\\]+$/; my $dir=$1; my $xlate;
+( $xlate="${dir}x86_64-xlate.pl" and -f $xlate ) or
+( $xlate="${dir}../../perlasm/x86_64-xlate.pl" and -f $xlate) or
+die "can't locate x86_64-xlate.pl";
+
+open OUT,"| \"$^X\" \"$xlate\" $flavour \"$output\"";
+*STDOUT=*OUT;
+
+sub L() { $code.=".byte	".join(',',@_)."\n"; }
+sub LL(){ $code.=".byte	".join(',',@_).",".join(',',@_)."\n"; }
+
+@mm=("%r8","%r9","%r10","%r11","%r12","%r13","%r14","%r15");
+
+$func="whirlpool_block";
+$table=".Ltable";
+
+$code=<<___;
+.text
+
+.globl	$func
+.type	$func,\@function,3
+.align	16
+$func:
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+
+	mov	%rsp,%r11
+	sub	\$128+40,%rsp
+	and	\$-64,%rsp
+
+	lea	128(%rsp),%r10
+	mov	%rdi,0(%r10)		# save parameter block
+	mov	%rsi,8(%r10)
+	mov	%rdx,16(%r10)
+	mov	%r11,32(%r10)		# saved stack pointer
+.Lprologue:
+
+	mov	%r10,%rbx
+	lea	$table(%rip),%rbp
+
+	xor	%rcx,%rcx
+	xor	%rdx,%rdx
+___
+for($i=0;$i<8;$i++) { $code.="mov $i*8(%rdi),@mm[$i]\n"; }	# L=H
+$code.=".Louterloop:\n";
+for($i=0;$i<8;$i++) { $code.="mov @mm[$i],$i*8(%rsp)\n"; }	# K=L
+for($i=0;$i<8;$i++) { $code.="xor $i*8(%rsi),@mm[$i]\n"; }	# L^=inp
+for($i=0;$i<8;$i++) { $code.="mov @mm[$i],64+$i*8(%rsp)\n"; }	# S=L
+$code.=<<___;
+	xor	%rsi,%rsi
+	mov	%rsi,24(%rbx)		# zero round counter
+	jmp	.Lround
+.align	16
+.Lround:
+	mov	4096(%rbp,%rsi,8),@mm[0]	# rc[r]
+	mov	0(%rsp),%eax
+	mov	4(%rsp),%ebx
+	movz	%al,%ecx
+	movz	%ah,%edx
+___
+for($i=0;$i<8;$i++) {
+    my $func = ($i==0)? "mov" : "xor";
+    $code.=<<___;
+	shr	\$16,%eax
+	lea	(%rcx,%rcx),%rsi
+	movz	%al,%ecx
+	lea	(%rdx,%rdx),%rdi
+	movz	%ah,%edx
+	xor	0(%rbp,%rsi,8),@mm[0]
+	$func	7(%rbp,%rdi,8),@mm[1]
+	mov	$i*8+8(%rsp),%eax		# ($i+1)*8
+	lea	(%rcx,%rcx),%rsi
+	movz	%bl,%ecx
+	lea	(%rdx,%rdx),%rdi
+	movz	%bh,%edx
+	$func	6(%rbp,%rsi,8),@mm[2]
+	$func	5(%rbp,%rdi,8),@mm[3]
+	shr	\$16,%ebx
+	lea	(%rcx,%rcx),%rsi
+	movz	%bl,%ecx
+	lea	(%rdx,%rdx),%rdi
+	movz	%bh,%edx
+	$func	4(%rbp,%rsi,8),@mm[4]
+	$func	3(%rbp,%rdi,8),@mm[5]
+	mov	$i*8+8+4(%rsp),%ebx		# ($i+1)*8+4
+	lea	(%rcx,%rcx),%rsi
+	movz	%al,%ecx
+	lea	(%rdx,%rdx),%rdi
+	movz	%ah,%edx
+	$func	2(%rbp,%rsi,8),@mm[6]
+	$func	1(%rbp,%rdi,8),@mm[7]
+___
+    push(@mm,shift(@mm));
+}
+for($i=0;$i<8;$i++) { $code.="mov @mm[$i],$i*8(%rsp)\n"; }	# K=L
+for($i=0;$i<8;$i++) {
+    $code.=<<___;
+	shr	\$16,%eax
+	lea	(%rcx,%rcx),%rsi
+	movz	%al,%ecx
+	lea	(%rdx,%rdx),%rdi
+	movz	%ah,%edx
+	xor	0(%rbp,%rsi,8),@mm[0]
+	xor	7(%rbp,%rdi,8),@mm[1]
+	`"mov	64+$i*8+8(%rsp),%eax"	if($i<7);`	# 64+($i+1)*8
+	lea	(%rcx,%rcx),%rsi
+	movz	%bl,%ecx
+	lea	(%rdx,%rdx),%rdi
+	movz	%bh,%edx
+	xor	6(%rbp,%rsi,8),@mm[2]
+	xor	5(%rbp,%rdi,8),@mm[3]
+	shr	\$16,%ebx
+	lea	(%rcx,%rcx),%rsi
+	movz	%bl,%ecx
+	lea	(%rdx,%rdx),%rdi
+	movz	%bh,%edx
+	xor	4(%rbp,%rsi,8),@mm[4]
+	xor	3(%rbp,%rdi,8),@mm[5]
+	`"mov	64+$i*8+8+4(%rsp),%ebx"	if($i<7);`	# 64+($i+1)*8+4
+	lea	(%rcx,%rcx),%rsi
+	movz	%al,%ecx
+	lea	(%rdx,%rdx),%rdi
+	movz	%ah,%edx
+	xor	2(%rbp,%rsi,8),@mm[6]
+	xor	1(%rbp,%rdi,8),@mm[7]
+___
+    push(@mm,shift(@mm));
+}
+$code.=<<___;
+	lea	128(%rsp),%rbx
+	mov	24(%rbx),%rsi		# pull round counter
+	add	\$1,%rsi
+	cmp	\$10,%rsi
+	je	.Lroundsdone
+
+	mov	%rsi,24(%rbx)		# update round counter
+___
+for($i=0;$i<8;$i++) { $code.="mov @mm[$i],64+$i*8(%rsp)\n"; }	# S=L
+$code.=<<___;
+	jmp	.Lround
+.align	16
+.Lroundsdone:
+	mov	0(%rbx),%rdi		# reload argument block
+	mov	8(%rbx),%rsi
+	mov	16(%rbx),%rax
+___
+for($i=0;$i<8;$i++) { $code.="xor $i*8(%rsi),@mm[$i]\n"; }	# L^=inp
+for($i=0;$i<8;$i++) { $code.="xor $i*8(%rdi),@mm[$i]\n"; }	# L^=H
+for($i=0;$i<8;$i++) { $code.="mov @mm[$i],$i*8(%rdi)\n"; }	# H=L
+$code.=<<___;
+	lea	64(%rsi),%rsi		# inp+=64
+	sub	\$1,%rax		# num--
+	jz	.Lalldone
+	mov	%rsi,8(%rbx)		# update parameter block
+	mov	%rax,16(%rbx)
+	jmp	.Louterloop
+.Lalldone:
+	mov	32(%rbx),%rsi		# restore saved pointer
+	mov	(%rsi),%r15
+	mov	8(%rsi),%r14
+	mov	16(%rsi),%r13
+	mov	24(%rsi),%r12
+	mov	32(%rsi),%rbp
+	mov	40(%rsi),%rbx
+	lea	48(%rsi),%rsp
+.Lepilogue:
+	ret
+.size	$func,.-$func
+
+.align	64
+.type	$table,\@object
+$table:
+___
+	&LL(0x18,0x18,0x60,0x18,0xc0,0x78,0x30,0xd8);
+	&LL(0x23,0x23,0x8c,0x23,0x05,0xaf,0x46,0x26);
+	&LL(0xc6,0xc6,0x3f,0xc6,0x7e,0xf9,0x91,0xb8);
+	&LL(0xe8,0xe8,0x87,0xe8,0x13,0x6f,0xcd,0xfb);
+	&LL(0x87,0x87,0x26,0x87,0x4c,0xa1,0x13,0xcb);
+	&LL(0xb8,0xb8,0xda,0xb8,0xa9,0x62,0x6d,0x11);
+	&LL(0x01,0x01,0x04,0x01,0x08,0x05,0x02,0x09);
+	&LL(0x4f,0x4f,0x21,0x4f,0x42,0x6e,0x9e,0x0d);
+	&LL(0x36,0x36,0xd8,0x36,0xad,0xee,0x6c,0x9b);
+	&LL(0xa6,0xa6,0xa2,0xa6,0x59,0x04,0x51,0xff);
+	&LL(0xd2,0xd2,0x6f,0xd2,0xde,0xbd,0xb9,0x0c);
+	&LL(0xf5,0xf5,0xf3,0xf5,0xfb,0x06,0xf7,0x0e);
+	&LL(0x79,0x79,0xf9,0x79,0xef,0x80,0xf2,0x96);
+	&LL(0x6f,0x6f,0xa1,0x6f,0x5f,0xce,0xde,0x30);
+	&LL(0x91,0x91,0x7e,0x91,0xfc,0xef,0x3f,0x6d);
+	&LL(0x52,0x52,0x55,0x52,0xaa,0x07,0xa4,0xf8);
+	&LL(0x60,0x60,0x9d,0x60,0x27,0xfd,0xc0,0x47);
+	&LL(0xbc,0xbc,0xca,0xbc,0x89,0x76,0x65,0x35);
+	&LL(0x9b,0x9b,0x56,0x9b,0xac,0xcd,0x2b,0x37);
+	&LL(0x8e,0x8e,0x02,0x8e,0x04,0x8c,0x01,0x8a);
+	&LL(0xa3,0xa3,0xb6,0xa3,0x71,0x15,0x5b,0xd2);
+	&LL(0x0c,0x0c,0x30,0x0c,0x60,0x3c,0x18,0x6c);
+	&LL(0x7b,0x7b,0xf1,0x7b,0xff,0x8a,0xf6,0x84);
+	&LL(0x35,0x35,0xd4,0x35,0xb5,0xe1,0x6a,0x80);
+	&LL(0x1d,0x1d,0x74,0x1d,0xe8,0x69,0x3a,0xf5);
+	&LL(0xe0,0xe0,0xa7,0xe0,0x53,0x47,0xdd,0xb3);
+	&LL(0xd7,0xd7,0x7b,0xd7,0xf6,0xac,0xb3,0x21);
+	&LL(0xc2,0xc2,0x2f,0xc2,0x5e,0xed,0x99,0x9c);
+	&LL(0x2e,0x2e,0xb8,0x2e,0x6d,0x96,0x5c,0x43);
+	&LL(0x4b,0x4b,0x31,0x4b,0x62,0x7a,0x96,0x29);
+	&LL(0xfe,0xfe,0xdf,0xfe,0xa3,0x21,0xe1,0x5d);
+	&LL(0x57,0x57,0x41,0x57,0x82,0x16,0xae,0xd5);
+	&LL(0x15,0x15,0x54,0x15,0xa8,0x41,0x2a,0xbd);
+	&LL(0x77,0x77,0xc1,0x77,0x9f,0xb6,0xee,0xe8);
+	&LL(0x37,0x37,0xdc,0x37,0xa5,0xeb,0x6e,0x92);
+	&LL(0xe5,0xe5,0xb3,0xe5,0x7b,0x56,0xd7,0x9e);
+	&LL(0x9f,0x9f,0x46,0x9f,0x8c,0xd9,0x23,0x13);
+	&LL(0xf0,0xf0,0xe7,0xf0,0xd3,0x17,0xfd,0x23);
+	&LL(0x4a,0x4a,0x35,0x4a,0x6a,0x7f,0x94,0x20);
+	&LL(0xda,0xda,0x4f,0xda,0x9e,0x95,0xa9,0x44);
+	&LL(0x58,0x58,0x7d,0x58,0xfa,0x25,0xb0,0xa2);
+	&LL(0xc9,0xc9,0x03,0xc9,0x06,0xca,0x8f,0xcf);
+	&LL(0x29,0x29,0xa4,0x29,0x55,0x8d,0x52,0x7c);
+	&LL(0x0a,0x0a,0x28,0x0a,0x50,0x22,0x14,0x5a);
+	&LL(0xb1,0xb1,0xfe,0xb1,0xe1,0x4f,0x7f,0x50);
+	&LL(0xa0,0xa0,0xba,0xa0,0x69,0x1a,0x5d,0xc9);
+	&LL(0x6b,0x6b,0xb1,0x6b,0x7f,0xda,0xd6,0x14);
+	&LL(0x85,0x85,0x2e,0x85,0x5c,0xab,0x17,0xd9);
+	&LL(0xbd,0xbd,0xce,0xbd,0x81,0x73,0x67,0x3c);
+	&LL(0x5d,0x5d,0x69,0x5d,0xd2,0x34,0xba,0x8f);
+	&LL(0x10,0x10,0x40,0x10,0x80,0x50,0x20,0x90);
+	&LL(0xf4,0xf4,0xf7,0xf4,0xf3,0x03,0xf5,0x07);
+	&LL(0xcb,0xcb,0x0b,0xcb,0x16,0xc0,0x8b,0xdd);
+	&LL(0x3e,0x3e,0xf8,0x3e,0xed,0xc6,0x7c,0xd3);
+	&LL(0x05,0x05,0x14,0x05,0x28,0x11,0x0a,0x2d);
+	&LL(0x67,0x67,0x81,0x67,0x1f,0xe6,0xce,0x78);
+	&LL(0xe4,0xe4,0xb7,0xe4,0x73,0x53,0xd5,0x97);
+	&LL(0x27,0x27,0x9c,0x27,0x25,0xbb,0x4e,0x02);
+	&LL(0x41,0x41,0x19,0x41,0x32,0x58,0x82,0x73);
+	&LL(0x8b,0x8b,0x16,0x8b,0x2c,0x9d,0x0b,0xa7);
+	&LL(0xa7,0xa7,0xa6,0xa7,0x51,0x01,0x53,0xf6);
+	&LL(0x7d,0x7d,0xe9,0x7d,0xcf,0x94,0xfa,0xb2);
+	&LL(0x95,0x95,0x6e,0x95,0xdc,0xfb,0x37,0x49);
+	&LL(0xd8,0xd8,0x47,0xd8,0x8e,0x9f,0xad,0x56);
+	&LL(0xfb,0xfb,0xcb,0xfb,0x8b,0x30,0xeb,0x70);
+	&LL(0xee,0xee,0x9f,0xee,0x23,0x71,0xc1,0xcd);
+	&LL(0x7c,0x7c,0xed,0x7c,0xc7,0x91,0xf8,0xbb);
+	&LL(0x66,0x66,0x85,0x66,0x17,0xe3,0xcc,0x71);
+	&LL(0xdd,0xdd,0x53,0xdd,0xa6,0x8e,0xa7,0x7b);
+	&LL(0x17,0x17,0x5c,0x17,0xb8,0x4b,0x2e,0xaf);
+	&LL(0x47,0x47,0x01,0x47,0x02,0x46,0x8e,0x45);
+	&LL(0x9e,0x9e,0x42,0x9e,0x84,0xdc,0x21,0x1a);
+	&LL(0xca,0xca,0x0f,0xca,0x1e,0xc5,0x89,0xd4);
+	&LL(0x2d,0x2d,0xb4,0x2d,0x75,0x99,0x5a,0x58);
+	&LL(0xbf,0xbf,0xc6,0xbf,0x91,0x79,0x63,0x2e);
+	&LL(0x07,0x07,0x1c,0x07,0x38,0x1b,0x0e,0x3f);
+	&LL(0xad,0xad,0x8e,0xad,0x01,0x23,0x47,0xac);
+	&LL(0x5a,0x5a,0x75,0x5a,0xea,0x2f,0xb4,0xb0);
+	&LL(0x83,0x83,0x36,0x83,0x6c,0xb5,0x1b,0xef);
+	&LL(0x33,0x33,0xcc,0x33,0x85,0xff,0x66,0xb6);
+	&LL(0x63,0x63,0x91,0x63,0x3f,0xf2,0xc6,0x5c);
+	&LL(0x02,0x02,0x08,0x02,0x10,0x0a,0x04,0x12);
+	&LL(0xaa,0xaa,0x92,0xaa,0x39,0x38,0x49,0x93);
+	&LL(0x71,0x71,0xd9,0x71,0xaf,0xa8,0xe2,0xde);
+	&LL(0xc8,0xc8,0x07,0xc8,0x0e,0xcf,0x8d,0xc6);
+	&LL(0x19,0x19,0x64,0x19,0xc8,0x7d,0x32,0xd1);
+	&LL(0x49,0x49,0x39,0x49,0x72,0x70,0x92,0x3b);
+	&LL(0xd9,0xd9,0x43,0xd9,0x86,0x9a,0xaf,0x5f);
+	&LL(0xf2,0xf2,0xef,0xf2,0xc3,0x1d,0xf9,0x31);
+	&LL(0xe3,0xe3,0xab,0xe3,0x4b,0x48,0xdb,0xa8);
+	&LL(0x5b,0x5b,0x71,0x5b,0xe2,0x2a,0xb6,0xb9);
+	&LL(0x88,0x88,0x1a,0x88,0x34,0x92,0x0d,0xbc);
+	&LL(0x9a,0x9a,0x52,0x9a,0xa4,0xc8,0x29,0x3e);
+	&LL(0x26,0x26,0x98,0x26,0x2d,0xbe,0x4c,0x0b);
+	&LL(0x32,0x32,0xc8,0x32,0x8d,0xfa,0x64,0xbf);
+	&LL(0xb0,0xb0,0xfa,0xb0,0xe9,0x4a,0x7d,0x59);
+	&LL(0xe9,0xe9,0x83,0xe9,0x1b,0x6a,0xcf,0xf2);
+	&LL(0x0f,0x0f,0x3c,0x0f,0x78,0x33,0x1e,0x77);
+	&LL(0xd5,0xd5,0x73,0xd5,0xe6,0xa6,0xb7,0x33);
+	&LL(0x80,0x80,0x3a,0x80,0x74,0xba,0x1d,0xf4);
+	&LL(0xbe,0xbe,0xc2,0xbe,0x99,0x7c,0x61,0x27);
+	&LL(0xcd,0xcd,0x13,0xcd,0x26,0xde,0x87,0xeb);
+	&LL(0x34,0x34,0xd0,0x34,0xbd,0xe4,0x68,0x89);
+	&LL(0x48,0x48,0x3d,0x48,0x7a,0x75,0x90,0x32);
+	&LL(0xff,0xff,0xdb,0xff,0xab,0x24,0xe3,0x54);
+	&LL(0x7a,0x7a,0xf5,0x7a,0xf7,0x8f,0xf4,0x8d);
+	&LL(0x90,0x90,0x7a,0x90,0xf4,0xea,0x3d,0x64);
+	&LL(0x5f,0x5f,0x61,0x5f,0xc2,0x3e,0xbe,0x9d);
+	&LL(0x20,0x20,0x80,0x20,0x1d,0xa0,0x40,0x3d);
+	&LL(0x68,0x68,0xbd,0x68,0x67,0xd5,0xd0,0x0f);
+	&LL(0x1a,0x1a,0x68,0x1a,0xd0,0x72,0x34,0xca);
+	&LL(0xae,0xae,0x82,0xae,0x19,0x2c,0x41,0xb7);
+	&LL(0xb4,0xb4,0xea,0xb4,0xc9,0x5e,0x75,0x7d);
+	&LL(0x54,0x54,0x4d,0x54,0x9a,0x19,0xa8,0xce);
+	&LL(0x93,0x93,0x76,0x93,0xec,0xe5,0x3b,0x7f);
+	&LL(0x22,0x22,0x88,0x22,0x0d,0xaa,0x44,0x2f);
+	&LL(0x64,0x64,0x8d,0x64,0x07,0xe9,0xc8,0x63);
+	&LL(0xf1,0xf1,0xe3,0xf1,0xdb,0x12,0xff,0x2a);
+	&LL(0x73,0x73,0xd1,0x73,0xbf,0xa2,0xe6,0xcc);
+	&LL(0x12,0x12,0x48,0x12,0x90,0x5a,0x24,0x82);
+	&LL(0x40,0x40,0x1d,0x40,0x3a,0x5d,0x80,0x7a);
+	&LL(0x08,0x08,0x20,0x08,0x40,0x28,0x10,0x48);
+	&LL(0xc3,0xc3,0x2b,0xc3,0x56,0xe8,0x9b,0x95);
+	&LL(0xec,0xec,0x97,0xec,0x33,0x7b,0xc5,0xdf);
+	&LL(0xdb,0xdb,0x4b,0xdb,0x96,0x90,0xab,0x4d);
+	&LL(0xa1,0xa1,0xbe,0xa1,0x61,0x1f,0x5f,0xc0);
+	&LL(0x8d,0x8d,0x0e,0x8d,0x1c,0x83,0x07,0x91);
+	&LL(0x3d,0x3d,0xf4,0x3d,0xf5,0xc9,0x7a,0xc8);
+	&LL(0x97,0x97,0x66,0x97,0xcc,0xf1,0x33,0x5b);
+	&LL(0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00);
+	&LL(0xcf,0xcf,0x1b,0xcf,0x36,0xd4,0x83,0xf9);
+	&LL(0x2b,0x2b,0xac,0x2b,0x45,0x87,0x56,0x6e);
+	&LL(0x76,0x76,0xc5,0x76,0x97,0xb3,0xec,0xe1);
+	&LL(0x82,0x82,0x32,0x82,0x64,0xb0,0x19,0xe6);
+	&LL(0xd6,0xd6,0x7f,0xd6,0xfe,0xa9,0xb1,0x28);
+	&LL(0x1b,0x1b,0x6c,0x1b,0xd8,0x77,0x36,0xc3);
+	&LL(0xb5,0xb5,0xee,0xb5,0xc1,0x5b,0x77,0x74);
+	&LL(0xaf,0xaf,0x86,0xaf,0x11,0x29,0x43,0xbe);
+	&LL(0x6a,0x6a,0xb5,0x6a,0x77,0xdf,0xd4,0x1d);
+	&LL(0x50,0x50,0x5d,0x50,0xba,0x0d,0xa0,0xea);
+	&LL(0x45,0x45,0x09,0x45,0x12,0x4c,0x8a,0x57);
+	&LL(0xf3,0xf3,0xeb,0xf3,0xcb,0x18,0xfb,0x38);
+	&LL(0x30,0x30,0xc0,0x30,0x9d,0xf0,0x60,0xad);
+	&LL(0xef,0xef,0x9b,0xef,0x2b,0x74,0xc3,0xc4);
+	&LL(0x3f,0x3f,0xfc,0x3f,0xe5,0xc3,0x7e,0xda);
+	&LL(0x55,0x55,0x49,0x55,0x92,0x1c,0xaa,0xc7);
+	&LL(0xa2,0xa2,0xb2,0xa2,0x79,0x10,0x59,0xdb);
+	&LL(0xea,0xea,0x8f,0xea,0x03,0x65,0xc9,0xe9);
+	&LL(0x65,0x65,0x89,0x65,0x0f,0xec,0xca,0x6a);
+	&LL(0xba,0xba,0xd2,0xba,0xb9,0x68,0x69,0x03);
+	&LL(0x2f,0x2f,0xbc,0x2f,0x65,0x93,0x5e,0x4a);
+	&LL(0xc0,0xc0,0x27,0xc0,0x4e,0xe7,0x9d,0x8e);
+	&LL(0xde,0xde,0x5f,0xde,0xbe,0x81,0xa1,0x60);
+	&LL(0x1c,0x1c,0x70,0x1c,0xe0,0x6c,0x38,0xfc);
+	&LL(0xfd,0xfd,0xd3,0xfd,0xbb,0x2e,0xe7,0x46);
+	&LL(0x4d,0x4d,0x29,0x4d,0x52,0x64,0x9a,0x1f);
+	&LL(0x92,0x92,0x72,0x92,0xe4,0xe0,0x39,0x76);
+	&LL(0x75,0x75,0xc9,0x75,0x8f,0xbc,0xea,0xfa);
+	&LL(0x06,0x06,0x18,0x06,0x30,0x1e,0x0c,0x36);
+	&LL(0x8a,0x8a,0x12,0x8a,0x24,0x98,0x09,0xae);
+	&LL(0xb2,0xb2,0xf2,0xb2,0xf9,0x40,0x79,0x4b);
+	&LL(0xe6,0xe6,0xbf,0xe6,0x63,0x59,0xd1,0x85);
+	&LL(0x0e,0x0e,0x38,0x0e,0x70,0x36,0x1c,0x7e);
+	&LL(0x1f,0x1f,0x7c,0x1f,0xf8,0x63,0x3e,0xe7);
+	&LL(0x62,0x62,0x95,0x62,0x37,0xf7,0xc4,0x55);
+	&LL(0xd4,0xd4,0x77,0xd4,0xee,0xa3,0xb5,0x3a);
+	&LL(0xa8,0xa8,0x9a,0xa8,0x29,0x32,0x4d,0x81);
+	&LL(0x96,0x96,0x62,0x96,0xc4,0xf4,0x31,0x52);
+	&LL(0xf9,0xf9,0xc3,0xf9,0x9b,0x3a,0xef,0x62);
+	&LL(0xc5,0xc5,0x33,0xc5,0x66,0xf6,0x97,0xa3);
+	&LL(0x25,0x25,0x94,0x25,0x35,0xb1,0x4a,0x10);
+	&LL(0x59,0x59,0x79,0x59,0xf2,0x20,0xb2,0xab);
+	&LL(0x84,0x84,0x2a,0x84,0x54,0xae,0x15,0xd0);
+	&LL(0x72,0x72,0xd5,0x72,0xb7,0xa7,0xe4,0xc5);
+	&LL(0x39,0x39,0xe4,0x39,0xd5,0xdd,0x72,0xec);
+	&LL(0x4c,0x4c,0x2d,0x4c,0x5a,0x61,0x98,0x16);
+	&LL(0x5e,0x5e,0x65,0x5e,0xca,0x3b,0xbc,0x94);
+	&LL(0x78,0x78,0xfd,0x78,0xe7,0x85,0xf0,0x9f);
+	&LL(0x38,0x38,0xe0,0x38,0xdd,0xd8,0x70,0xe5);
+	&LL(0x8c,0x8c,0x0a,0x8c,0x14,0x86,0x05,0x98);
+	&LL(0xd1,0xd1,0x63,0xd1,0xc6,0xb2,0xbf,0x17);
+	&LL(0xa5,0xa5,0xae,0xa5,0x41,0x0b,0x57,0xe4);
+	&LL(0xe2,0xe2,0xaf,0xe2,0x43,0x4d,0xd9,0xa1);
+	&LL(0x61,0x61,0x99,0x61,0x2f,0xf8,0xc2,0x4e);
+	&LL(0xb3,0xb3,0xf6,0xb3,0xf1,0x45,0x7b,0x42);
+	&LL(0x21,0x21,0x84,0x21,0x15,0xa5,0x42,0x34);
+	&LL(0x9c,0x9c,0x4a,0x9c,0x94,0xd6,0x25,0x08);
+	&LL(0x1e,0x1e,0x78,0x1e,0xf0,0x66,0x3c,0xee);
+	&LL(0x43,0x43,0x11,0x43,0x22,0x52,0x86,0x61);
+	&LL(0xc7,0xc7,0x3b,0xc7,0x76,0xfc,0x93,0xb1);
+	&LL(0xfc,0xfc,0xd7,0xfc,0xb3,0x2b,0xe5,0x4f);
+	&LL(0x04,0x04,0x10,0x04,0x20,0x14,0x08,0x24);
+	&LL(0x51,0x51,0x59,0x51,0xb2,0x08,0xa2,0xe3);
+	&LL(0x99,0x99,0x5e,0x99,0xbc,0xc7,0x2f,0x25);
+	&LL(0x6d,0x6d,0xa9,0x6d,0x4f,0xc4,0xda,0x22);
+	&LL(0x0d,0x0d,0x34,0x0d,0x68,0x39,0x1a,0x65);
+	&LL(0xfa,0xfa,0xcf,0xfa,0x83,0x35,0xe9,0x79);
+	&LL(0xdf,0xdf,0x5b,0xdf,0xb6,0x84,0xa3,0x69);
+	&LL(0x7e,0x7e,0xe5,0x7e,0xd7,0x9b,0xfc,0xa9);
+	&LL(0x24,0x24,0x90,0x24,0x3d,0xb4,0x48,0x19);
+	&LL(0x3b,0x3b,0xec,0x3b,0xc5,0xd7,0x76,0xfe);
+	&LL(0xab,0xab,0x96,0xab,0x31,0x3d,0x4b,0x9a);
+	&LL(0xce,0xce,0x1f,0xce,0x3e,0xd1,0x81,0xf0);
+	&LL(0x11,0x11,0x44,0x11,0x88,0x55,0x22,0x99);
+	&LL(0x8f,0x8f,0x06,0x8f,0x0c,0x89,0x03,0x83);
+	&LL(0x4e,0x4e,0x25,0x4e,0x4a,0x6b,0x9c,0x04);
+	&LL(0xb7,0xb7,0xe6,0xb7,0xd1,0x51,0x73,0x66);
+	&LL(0xeb,0xeb,0x8b,0xeb,0x0b,0x60,0xcb,0xe0);
+	&LL(0x3c,0x3c,0xf0,0x3c,0xfd,0xcc,0x78,0xc1);
+	&LL(0x81,0x81,0x3e,0x81,0x7c,0xbf,0x1f,0xfd);
+	&LL(0x94,0x94,0x6a,0x94,0xd4,0xfe,0x35,0x40);
+	&LL(0xf7,0xf7,0xfb,0xf7,0xeb,0x0c,0xf3,0x1c);
+	&LL(0xb9,0xb9,0xde,0xb9,0xa1,0x67,0x6f,0x18);
+	&LL(0x13,0x13,0x4c,0x13,0x98,0x5f,0x26,0x8b);
+	&LL(0x2c,0x2c,0xb0,0x2c,0x7d,0x9c,0x58,0x51);
+	&LL(0xd3,0xd3,0x6b,0xd3,0xd6,0xb8,0xbb,0x05);
+	&LL(0xe7,0xe7,0xbb,0xe7,0x6b,0x5c,0xd3,0x8c);
+	&LL(0x6e,0x6e,0xa5,0x6e,0x57,0xcb,0xdc,0x39);
+	&LL(0xc4,0xc4,0x37,0xc4,0x6e,0xf3,0x95,0xaa);
+	&LL(0x03,0x03,0x0c,0x03,0x18,0x0f,0x06,0x1b);
+	&LL(0x56,0x56,0x45,0x56,0x8a,0x13,0xac,0xdc);
+	&LL(0x44,0x44,0x0d,0x44,0x1a,0x49,0x88,0x5e);
+	&LL(0x7f,0x7f,0xe1,0x7f,0xdf,0x9e,0xfe,0xa0);
+	&LL(0xa9,0xa9,0x9e,0xa9,0x21,0x37,0x4f,0x88);
+	&LL(0x2a,0x2a,0xa8,0x2a,0x4d,0x82,0x54,0x67);
+	&LL(0xbb,0xbb,0xd6,0xbb,0xb1,0x6d,0x6b,0x0a);
+	&LL(0xc1,0xc1,0x23,0xc1,0x46,0xe2,0x9f,0x87);
+	&LL(0x53,0x53,0x51,0x53,0xa2,0x02,0xa6,0xf1);
+	&LL(0xdc,0xdc,0x57,0xdc,0xae,0x8b,0xa5,0x72);
+	&LL(0x0b,0x0b,0x2c,0x0b,0x58,0x27,0x16,0x53);
+	&LL(0x9d,0x9d,0x4e,0x9d,0x9c,0xd3,0x27,0x01);
+	&LL(0x6c,0x6c,0xad,0x6c,0x47,0xc1,0xd8,0x2b);
+	&LL(0x31,0x31,0xc4,0x31,0x95,0xf5,0x62,0xa4);
+	&LL(0x74,0x74,0xcd,0x74,0x87,0xb9,0xe8,0xf3);
+	&LL(0xf6,0xf6,0xff,0xf6,0xe3,0x09,0xf1,0x15);
+	&LL(0x46,0x46,0x05,0x46,0x0a,0x43,0x8c,0x4c);
+	&LL(0xac,0xac,0x8a,0xac,0x09,0x26,0x45,0xa5);
+	&LL(0x89,0x89,0x1e,0x89,0x3c,0x97,0x0f,0xb5);
+	&LL(0x14,0x14,0x50,0x14,0xa0,0x44,0x28,0xb4);
+	&LL(0xe1,0xe1,0xa3,0xe1,0x5b,0x42,0xdf,0xba);
+	&LL(0x16,0x16,0x58,0x16,0xb0,0x4e,0x2c,0xa6);
+	&LL(0x3a,0x3a,0xe8,0x3a,0xcd,0xd2,0x74,0xf7);
+	&LL(0x69,0x69,0xb9,0x69,0x6f,0xd0,0xd2,0x06);
+	&LL(0x09,0x09,0x24,0x09,0x48,0x2d,0x12,0x41);
+	&LL(0x70,0x70,0xdd,0x70,0xa7,0xad,0xe0,0xd7);
+	&LL(0xb6,0xb6,0xe2,0xb6,0xd9,0x54,0x71,0x6f);
+	&LL(0xd0,0xd0,0x67,0xd0,0xce,0xb7,0xbd,0x1e);
+	&LL(0xed,0xed,0x93,0xed,0x3b,0x7e,0xc7,0xd6);
+	&LL(0xcc,0xcc,0x17,0xcc,0x2e,0xdb,0x85,0xe2);
+	&LL(0x42,0x42,0x15,0x42,0x2a,0x57,0x84,0x68);
+	&LL(0x98,0x98,0x5a,0x98,0xb4,0xc2,0x2d,0x2c);
+	&LL(0xa4,0xa4,0xaa,0xa4,0x49,0x0e,0x55,0xed);
+	&LL(0x28,0x28,0xa0,0x28,0x5d,0x88,0x50,0x75);
+	&LL(0x5c,0x5c,0x6d,0x5c,0xda,0x31,0xb8,0x86);
+	&LL(0xf8,0xf8,0xc7,0xf8,0x93,0x3f,0xed,0x6b);
+	&LL(0x86,0x86,0x22,0x86,0x44,0xa4,0x11,0xc2);
+
+	&L(0x18,0x23,0xc6,0xe8,0x87,0xb8,0x01,0x4f);	# rc[ROUNDS]
+	&L(0x36,0xa6,0xd2,0xf5,0x79,0x6f,0x91,0x52);
+	&L(0x60,0xbc,0x9b,0x8e,0xa3,0x0c,0x7b,0x35);
+	&L(0x1d,0xe0,0xd7,0xc2,0x2e,0x4b,0xfe,0x57);
+	&L(0x15,0x77,0x37,0xe5,0x9f,0xf0,0x4a,0xda);
+	&L(0x58,0xc9,0x29,0x0a,0xb1,0xa0,0x6b,0x85);
+	&L(0xbd,0x5d,0x10,0xf4,0xcb,0x3e,0x05,0x67);
+	&L(0xe4,0x27,0x41,0x8b,0xa7,0x7d,0x95,0xd8);
+	&L(0xfb,0xee,0x7c,0x66,0xdd,0x17,0x47,0x9e);
+	&L(0xca,0x2d,0xbf,0x07,0xad,0x5a,0x83,0x33);
+
+# EXCEPTION_DISPOSITION handler (EXCEPTION_RECORD *rec,ULONG64 frame,
+#		CONTEXT *context,DISPATCHER_CONTEXT *disp)
+if ($win64) {
+$rec="%rcx";
+$frame="%rdx";
+$context="%r8";
+$disp="%r9";
+
+$code.=<<___;
+.extern	__imp_RtlVirtualUnwind
+.type	se_handler,\@abi-omnipotent
+.align	16
+se_handler:
+	push	%rsi
+	push	%rdi
+	push	%rbx
+	push	%rbp
+	push	%r12
+	push	%r13
+	push	%r14
+	push	%r15
+	pushfq
+	sub	\$64,%rsp
+
+	mov	120($context),%rax	# pull context->Rax
+	mov	248($context),%rbx	# pull context->Rip
+
+	lea	.Lprologue(%rip),%r10
+	cmp	%r10,%rbx		# context->Rip<.Lprologue
+	jb	.Lin_prologue
+
+	mov	152($context),%rax	# pull context->Rsp
+
+	lea	.Lepilogue(%rip),%r10
+	cmp	%r10,%rbx		# context->Rip>=.Lepilogue
+	jae	.Lin_prologue
+
+	mov	128+32(%rax),%rax	# pull saved stack pointer
+	lea	48(%rax),%rax
+
+	mov	-8(%rax),%rbx
+	mov	-16(%rax),%rbp
+	mov	-24(%rax),%r12
+	mov	-32(%rax),%r13
+	mov	-40(%rax),%r14
+	mov	-48(%rax),%r15
+	mov	%rbx,144($context)	# restore context->Rbx
+	mov	%rbp,160($context)	# restore context->Rbp
+	mov	%r12,216($context)	# restore context->R12
+	mov	%r13,224($context)	# restore context->R13
+	mov	%r14,232($context)	# restore context->R14
+	mov	%r15,240($context)	# restore context->R15
+
+.Lin_prologue:
+	mov	8(%rax),%rdi
+	mov	16(%rax),%rsi
+	mov	%rax,152($context)	# restore context->Rsp
+	mov	%rsi,168($context)	# restore context->Rsi
+	mov	%rdi,176($context)	# restore context->Rdi
+
+	mov	40($disp),%rdi		# disp->ContextRecord
+	mov	$context,%rsi		# context
+	mov	\$154,%ecx		# sizeof(CONTEXT)
+	.long	0xa548f3fc		# cld; rep movsq
+
+	mov	$disp,%rsi
+	xor	%rcx,%rcx		# arg1, UNW_FLAG_NHANDLER
+	mov	8(%rsi),%rdx		# arg2, disp->ImageBase
+	mov	0(%rsi),%r8		# arg3, disp->ControlPc
+	mov	16(%rsi),%r9		# arg4, disp->FunctionEntry
+	mov	40(%rsi),%r10		# disp->ContextRecord
+	lea	56(%rsi),%r11		# &disp->HandlerData
+	lea	24(%rsi),%r12		# &disp->EstablisherFrame
+	mov	%r10,32(%rsp)		# arg5
+	mov	%r11,40(%rsp)		# arg6
+	mov	%r12,48(%rsp)		# arg7
+	mov	%rcx,56(%rsp)		# arg8, (NULL)
+	call	*__imp_RtlVirtualUnwind(%rip)
+
+	mov	\$1,%eax		# ExceptionContinueSearch
+	add	\$64,%rsp
+	popfq
+	pop	%r15
+	pop	%r14
+	pop	%r13
+	pop	%r12
+	pop	%rbp
+	pop	%rbx
+	pop	%rdi
+	pop	%rsi
+	ret
+.size	se_handler,.-se_handler
+
+.section	.pdata
+.align	4
+	.rva	.LSEH_begin_$func
+	.rva	.LSEH_end_$func
+	.rva	.LSEH_info_$func
+
+.section	.xdata
+.align	8
+.LSEH_info_$func:
+	.byte	9,0,0,0
+	.rva	se_handler
+___
+}
+
+$code =~ s/\`([^\`]*)\`/eval $1/gem;
+print $code;
+close STDOUT;
diff --git a/crypto/whrlpool/build.info b/crypto/whrlpool/build.info
new file mode 100644
index 000000000..7f3a19eaa
--- /dev/null
+++ b/crypto/whrlpool/build.info
@@ -0,0 +1,7 @@
+LIBS=../../libcrypto
+SOURCE[../../libcrypto]=wp_dgst.c {- $target{wp_asm_src} -}
+
+GENERATE[wp-mmx.s]=asm/wp-mmx.pl $(PERLASM_SCHEME) $(CFLAGS) $(LIB_CFLAGS) $(PROCESSOR)
+DEPEND[wp-mmx.s]=../perlasm/x86asm.pl
+
+GENERATE[wp-x86_64.s]=asm/wp-x86_64.pl $(PERLASM_SCHEME)
diff --git a/crypto/whrlpool/wp_block.c b/crypto/whrlpool/wp_block.c
new file mode 100644
index 000000000..b29f037bf
--- /dev/null
+++ b/crypto/whrlpool/wp_block.c
@@ -0,0 +1,792 @@
+/*
+ * Copyright 2005-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/**
+ * The Whirlpool hashing function.
+ *
+ * 

+ * References + * + *

+ * The Whirlpool algorithm was developed by + * Paulo S. L. M. Barreto and + * Vincent Rijmen. + * + * See + * P.S.L.M. Barreto, V. Rijmen, + * ``The Whirlpool hashing function,'' + * NESSIE submission, 2000 (tweaked version, 2001), + * + * + * Based on "@version 3.0 (2003.03.12)" by Paulo S.L.M. Barreto and + * Vincent Rijmen. Lookup "reference implementations" on + * + * + * ============================================================================= + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "wp_locl.h" +#include + +typedef unsigned char u8; +#if (defined(_WIN32) || defined(_WIN64)) && !defined(__MINGW32) +typedef unsigned __int64 u64; +#elif defined(__arch64__) +typedef unsigned long u64; +#else +typedef unsigned long long u64; +#endif + +#define ROUNDS 10 + +#define STRICT_ALIGNMENT +#if !defined(PEDANTIC) && (defined(__i386) || defined(__i386__) || \ + defined(__x86_64) || defined(__x86_64__) || \ + defined(_M_IX86) || defined(_M_AMD64) || \ + defined(_M_X64)) +/* + * Well, formally there're couple of other architectures, which permit + * unaligned loads, specifically those not crossing cache lines, IA-64 and + * PowerPC... + */ +# undef STRICT_ALIGNMENT +#endif + +#undef SMALL_REGISTER_BANK +#if defined(__i386) || defined(__i386__) || defined(_M_IX86) +# define SMALL_REGISTER_BANK +# if defined(WHIRLPOOL_ASM) +# ifndef OPENSSL_SMALL_FOOTPRINT +/* + * it appears that for elder non-MMX + * CPUs this is actually faster! + */ +# define OPENSSL_SMALL_FOOTPRINT +# endif +# define GO_FOR_MMX(ctx,inp,num) do { \ + extern unsigned long OPENSSL_ia32cap_P[]; \ + void whirlpool_block_mmx(void *,const void *,size_t); \ + if (!(OPENSSL_ia32cap_P[0] & (1<<23))) break; \ + whirlpool_block_mmx(ctx->H.c,inp,num); return; \ + } while (0) +# endif +#endif + +#undef ROTATE +#ifndef PEDANTIC +# if defined(_MSC_VER) +# if defined(_WIN64) /* applies to both IA-64 and AMD64 */ +# pragma intrinsic(_rotl64) +# define ROTATE(a,n) _rotl64((a),n) +# endif +# elif defined(__GNUC__) && __GNUC__>=2 +# if defined(__x86_64) || defined(__x86_64__) +# if defined(L_ENDIAN) +# define ROTATE(a,n) ({ u64 ret; asm ("rolq %1,%0" \ + : "=r"(ret) : "J"(n),"0"(a) : "cc"); ret; }) +# elif defined(B_ENDIAN) + /* + * Most will argue that x86_64 is always little-endian. Well, yes, but + * then we have stratus.com who has modified gcc to "emulate" + * big-endian on x86. Is there evidence that they [or somebody else] + * won't do same for x86_64? Naturally no. And this line is waiting + * ready for that brave soul:-) + */ +# define ROTATE(a,n) ({ u64 ret; asm ("rorq %1,%0" \ + : "=r"(ret) : "J"(n),"0"(a) : "cc"); ret; }) +# endif +# elif defined(__ia64) || defined(__ia64__) +# if defined(L_ENDIAN) +# define ROTATE(a,n) ({ u64 ret; asm ("shrp %0=%1,%1,%2" \ + : "=r"(ret) : "r"(a),"M"(64-(n))); ret; }) +# elif defined(B_ENDIAN) +# define ROTATE(a,n) ({ u64 ret; asm ("shrp %0=%1,%1,%2" \ + : "=r"(ret) : "r"(a),"M"(n)); ret; }) +# endif +# endif +# endif +#endif + +#if defined(OPENSSL_SMALL_FOOTPRINT) +# if !defined(ROTATE) +# if defined(L_ENDIAN) /* little-endians have to rotate left */ +# define ROTATE(i,n) ((i)<<(n) ^ (i)>>(64-n)) +# elif defined(B_ENDIAN) /* big-endians have to rotate right */ +# define ROTATE(i,n) ((i)>>(n) ^ (i)<<(64-n)) +# endif +# endif +# if defined(ROTATE) && !defined(STRICT_ALIGNMENT) +# define STRICT_ALIGNMENT /* ensure smallest table size */ +# endif +#endif + +/* + * Table size depends on STRICT_ALIGNMENT and whether or not endian- + * specific ROTATE macro is defined. If STRICT_ALIGNMENT is not + * defined, which is normally the case on x86[_64] CPUs, the table is + * 4KB large unconditionally. Otherwise if ROTATE is defined, the + * table is 2KB large, and otherwise - 16KB. 2KB table requires a + * whole bunch of additional rotations, but I'm willing to "trade," + * because 16KB table certainly trashes L1 cache. I wish all CPUs + * could handle unaligned load as 4KB table doesn't trash the cache, + * nor does it require additional rotations. + */ +/* + * Note that every Cn macro expands as two loads: one byte load and + * one quadword load. One can argue that that many single-byte loads + * is too excessive, as one could load a quadword and "milk" it for + * eight 8-bit values instead. Well, yes, but in order to do so *and* + * avoid excessive loads you have to accommodate a handful of 64-bit + * values in the register bank and issue a bunch of shifts and mask. + * It's a tradeoff: loads vs. shift and mask in big register bank[!]. + * On most CPUs eight single-byte loads are faster and I let other + * ones to depend on smart compiler to fold byte loads if beneficial. + * Hand-coded assembler would be another alternative:-) + */ +#ifdef STRICT_ALIGNMENT +# if defined(ROTATE) +# define N 1 +# define LL(c0,c1,c2,c3,c4,c5,c6,c7) c0,c1,c2,c3,c4,c5,c6,c7 +# define C0(K,i) (Cx.q[K.c[(i)*8+0]]) +# define C1(K,i) ROTATE(Cx.q[K.c[(i)*8+1]],8) +# define C2(K,i) ROTATE(Cx.q[K.c[(i)*8+2]],16) +# define C3(K,i) ROTATE(Cx.q[K.c[(i)*8+3]],24) +# define C4(K,i) ROTATE(Cx.q[K.c[(i)*8+4]],32) +# define C5(K,i) ROTATE(Cx.q[K.c[(i)*8+5]],40) +# define C6(K,i) ROTATE(Cx.q[K.c[(i)*8+6]],48) +# define C7(K,i) ROTATE(Cx.q[K.c[(i)*8+7]],56) +# else +# define N 8 +# define LL(c0,c1,c2,c3,c4,c5,c6,c7) c0,c1,c2,c3,c4,c5,c6,c7, \ + c7,c0,c1,c2,c3,c4,c5,c6, \ + c6,c7,c0,c1,c2,c3,c4,c5, \ + c5,c6,c7,c0,c1,c2,c3,c4, \ + c4,c5,c6,c7,c0,c1,c2,c3, \ + c3,c4,c5,c6,c7,c0,c1,c2, \ + c2,c3,c4,c5,c6,c7,c0,c1, \ + c1,c2,c3,c4,c5,c6,c7,c0 +# define C0(K,i) (Cx.q[0+8*K.c[(i)*8+0]]) +# define C1(K,i) (Cx.q[1+8*K.c[(i)*8+1]]) +# define C2(K,i) (Cx.q[2+8*K.c[(i)*8+2]]) +# define C3(K,i) (Cx.q[3+8*K.c[(i)*8+3]]) +# define C4(K,i) (Cx.q[4+8*K.c[(i)*8+4]]) +# define C5(K,i) (Cx.q[5+8*K.c[(i)*8+5]]) +# define C6(K,i) (Cx.q[6+8*K.c[(i)*8+6]]) +# define C7(K,i) (Cx.q[7+8*K.c[(i)*8+7]]) +# endif +#else +# define N 2 +# define LL(c0,c1,c2,c3,c4,c5,c6,c7) c0,c1,c2,c3,c4,c5,c6,c7, \ + c0,c1,c2,c3,c4,c5,c6,c7 +# define C0(K,i) (((u64*)(Cx.c+0))[2*K.c[(i)*8+0]]) +# define C1(K,i) (((u64*)(Cx.c+7))[2*K.c[(i)*8+1]]) +# define C2(K,i) (((u64*)(Cx.c+6))[2*K.c[(i)*8+2]]) +# define C3(K,i) (((u64*)(Cx.c+5))[2*K.c[(i)*8+3]]) +# define C4(K,i) (((u64*)(Cx.c+4))[2*K.c[(i)*8+4]]) +# define C5(K,i) (((u64*)(Cx.c+3))[2*K.c[(i)*8+5]]) +# define C6(K,i) (((u64*)(Cx.c+2))[2*K.c[(i)*8+6]]) +# define C7(K,i) (((u64*)(Cx.c+1))[2*K.c[(i)*8+7]]) +#endif + +static const + union { + u8 c[(256 * N + ROUNDS) * sizeof(u64)]; + u64 q[(256 * N + ROUNDS)]; +} Cx = { + { + /* Note endian-neutral representation:-) */ + LL(0x18, 0x18, 0x60, 0x18, 0xc0, 0x78, 0x30, 0xd8), + LL(0x23, 0x23, 0x8c, 0x23, 0x05, 0xaf, 0x46, 0x26), + LL(0xc6, 0xc6, 0x3f, 0xc6, 0x7e, 0xf9, 0x91, 0xb8), + LL(0xe8, 0xe8, 0x87, 0xe8, 0x13, 0x6f, 0xcd, 0xfb), + LL(0x87, 0x87, 0x26, 0x87, 0x4c, 0xa1, 0x13, 0xcb), + LL(0xb8, 0xb8, 0xda, 0xb8, 0xa9, 0x62, 0x6d, 0x11), + LL(0x01, 0x01, 0x04, 0x01, 0x08, 0x05, 0x02, 0x09), + LL(0x4f, 0x4f, 0x21, 0x4f, 0x42, 0x6e, 0x9e, 0x0d), + LL(0x36, 0x36, 0xd8, 0x36, 0xad, 0xee, 0x6c, 0x9b), + LL(0xa6, 0xa6, 0xa2, 0xa6, 0x59, 0x04, 0x51, 0xff), + LL(0xd2, 0xd2, 0x6f, 0xd2, 0xde, 0xbd, 0xb9, 0x0c), + LL(0xf5, 0xf5, 0xf3, 0xf5, 0xfb, 0x06, 0xf7, 0x0e), + LL(0x79, 0x79, 0xf9, 0x79, 0xef, 0x80, 0xf2, 0x96), + LL(0x6f, 0x6f, 0xa1, 0x6f, 0x5f, 0xce, 0xde, 0x30), + LL(0x91, 0x91, 0x7e, 0x91, 0xfc, 0xef, 0x3f, 0x6d), + LL(0x52, 0x52, 0x55, 0x52, 0xaa, 0x07, 0xa4, 0xf8), + LL(0x60, 0x60, 0x9d, 0x60, 0x27, 0xfd, 0xc0, 0x47), + LL(0xbc, 0xbc, 0xca, 0xbc, 0x89, 0x76, 0x65, 0x35), + LL(0x9b, 0x9b, 0x56, 0x9b, 0xac, 0xcd, 0x2b, 0x37), + LL(0x8e, 0x8e, 0x02, 0x8e, 0x04, 0x8c, 0x01, 0x8a), + LL(0xa3, 0xa3, 0xb6, 0xa3, 0x71, 0x15, 0x5b, 0xd2), + LL(0x0c, 0x0c, 0x30, 0x0c, 0x60, 0x3c, 0x18, 0x6c), + LL(0x7b, 0x7b, 0xf1, 0x7b, 0xff, 0x8a, 0xf6, 0x84), + LL(0x35, 0x35, 0xd4, 0x35, 0xb5, 0xe1, 0x6a, 0x80), + LL(0x1d, 0x1d, 0x74, 0x1d, 0xe8, 0x69, 0x3a, 0xf5), + LL(0xe0, 0xe0, 0xa7, 0xe0, 0x53, 0x47, 0xdd, 0xb3), + LL(0xd7, 0xd7, 0x7b, 0xd7, 0xf6, 0xac, 0xb3, 0x21), + LL(0xc2, 0xc2, 0x2f, 0xc2, 0x5e, 0xed, 0x99, 0x9c), + LL(0x2e, 0x2e, 0xb8, 0x2e, 0x6d, 0x96, 0x5c, 0x43), + LL(0x4b, 0x4b, 0x31, 0x4b, 0x62, 0x7a, 0x96, 0x29), + LL(0xfe, 0xfe, 0xdf, 0xfe, 0xa3, 0x21, 0xe1, 0x5d), + LL(0x57, 0x57, 0x41, 0x57, 0x82, 0x16, 0xae, 0xd5), + LL(0x15, 0x15, 0x54, 0x15, 0xa8, 0x41, 0x2a, 0xbd), + LL(0x77, 0x77, 0xc1, 0x77, 0x9f, 0xb6, 0xee, 0xe8), + LL(0x37, 0x37, 0xdc, 0x37, 0xa5, 0xeb, 0x6e, 0x92), + LL(0xe5, 0xe5, 0xb3, 0xe5, 0x7b, 0x56, 0xd7, 0x9e), + LL(0x9f, 0x9f, 0x46, 0x9f, 0x8c, 0xd9, 0x23, 0x13), + LL(0xf0, 0xf0, 0xe7, 0xf0, 0xd3, 0x17, 0xfd, 0x23), + LL(0x4a, 0x4a, 0x35, 0x4a, 0x6a, 0x7f, 0x94, 0x20), + LL(0xda, 0xda, 0x4f, 0xda, 0x9e, 0x95, 0xa9, 0x44), + LL(0x58, 0x58, 0x7d, 0x58, 0xfa, 0x25, 0xb0, 0xa2), + LL(0xc9, 0xc9, 0x03, 0xc9, 0x06, 0xca, 0x8f, 0xcf), + LL(0x29, 0x29, 0xa4, 0x29, 0x55, 0x8d, 0x52, 0x7c), + LL(0x0a, 0x0a, 0x28, 0x0a, 0x50, 0x22, 0x14, 0x5a), + LL(0xb1, 0xb1, 0xfe, 0xb1, 0xe1, 0x4f, 0x7f, 0x50), + LL(0xa0, 0xa0, 0xba, 0xa0, 0x69, 0x1a, 0x5d, 0xc9), + LL(0x6b, 0x6b, 0xb1, 0x6b, 0x7f, 0xda, 0xd6, 0x14), + LL(0x85, 0x85, 0x2e, 0x85, 0x5c, 0xab, 0x17, 0xd9), + LL(0xbd, 0xbd, 0xce, 0xbd, 0x81, 0x73, 0x67, 0x3c), + LL(0x5d, 0x5d, 0x69, 0x5d, 0xd2, 0x34, 0xba, 0x8f), + LL(0x10, 0x10, 0x40, 0x10, 0x80, 0x50, 0x20, 0x90), + LL(0xf4, 0xf4, 0xf7, 0xf4, 0xf3, 0x03, 0xf5, 0x07), + LL(0xcb, 0xcb, 0x0b, 0xcb, 0x16, 0xc0, 0x8b, 0xdd), + LL(0x3e, 0x3e, 0xf8, 0x3e, 0xed, 0xc6, 0x7c, 0xd3), + LL(0x05, 0x05, 0x14, 0x05, 0x28, 0x11, 0x0a, 0x2d), + LL(0x67, 0x67, 0x81, 0x67, 0x1f, 0xe6, 0xce, 0x78), + LL(0xe4, 0xe4, 0xb7, 0xe4, 0x73, 0x53, 0xd5, 0x97), + LL(0x27, 0x27, 0x9c, 0x27, 0x25, 0xbb, 0x4e, 0x02), + LL(0x41, 0x41, 0x19, 0x41, 0x32, 0x58, 0x82, 0x73), + LL(0x8b, 0x8b, 0x16, 0x8b, 0x2c, 0x9d, 0x0b, 0xa7), + LL(0xa7, 0xa7, 0xa6, 0xa7, 0x51, 0x01, 0x53, 0xf6), + LL(0x7d, 0x7d, 0xe9, 0x7d, 0xcf, 0x94, 0xfa, 0xb2), + LL(0x95, 0x95, 0x6e, 0x95, 0xdc, 0xfb, 0x37, 0x49), + LL(0xd8, 0xd8, 0x47, 0xd8, 0x8e, 0x9f, 0xad, 0x56), + LL(0xfb, 0xfb, 0xcb, 0xfb, 0x8b, 0x30, 0xeb, 0x70), + LL(0xee, 0xee, 0x9f, 0xee, 0x23, 0x71, 0xc1, 0xcd), + LL(0x7c, 0x7c, 0xed, 0x7c, 0xc7, 0x91, 0xf8, 0xbb), + LL(0x66, 0x66, 0x85, 0x66, 0x17, 0xe3, 0xcc, 0x71), + LL(0xdd, 0xdd, 0x53, 0xdd, 0xa6, 0x8e, 0xa7, 0x7b), + LL(0x17, 0x17, 0x5c, 0x17, 0xb8, 0x4b, 0x2e, 0xaf), + LL(0x47, 0x47, 0x01, 0x47, 0x02, 0x46, 0x8e, 0x45), + LL(0x9e, 0x9e, 0x42, 0x9e, 0x84, 0xdc, 0x21, 0x1a), + LL(0xca, 0xca, 0x0f, 0xca, 0x1e, 0xc5, 0x89, 0xd4), + LL(0x2d, 0x2d, 0xb4, 0x2d, 0x75, 0x99, 0x5a, 0x58), + LL(0xbf, 0xbf, 0xc6, 0xbf, 0x91, 0x79, 0x63, 0x2e), + LL(0x07, 0x07, 0x1c, 0x07, 0x38, 0x1b, 0x0e, 0x3f), + LL(0xad, 0xad, 0x8e, 0xad, 0x01, 0x23, 0x47, 0xac), + LL(0x5a, 0x5a, 0x75, 0x5a, 0xea, 0x2f, 0xb4, 0xb0), + LL(0x83, 0x83, 0x36, 0x83, 0x6c, 0xb5, 0x1b, 0xef), + LL(0x33, 0x33, 0xcc, 0x33, 0x85, 0xff, 0x66, 0xb6), + LL(0x63, 0x63, 0x91, 0x63, 0x3f, 0xf2, 0xc6, 0x5c), + LL(0x02, 0x02, 0x08, 0x02, 0x10, 0x0a, 0x04, 0x12), + LL(0xaa, 0xaa, 0x92, 0xaa, 0x39, 0x38, 0x49, 0x93), + LL(0x71, 0x71, 0xd9, 0x71, 0xaf, 0xa8, 0xe2, 0xde), + LL(0xc8, 0xc8, 0x07, 0xc8, 0x0e, 0xcf, 0x8d, 0xc6), + LL(0x19, 0x19, 0x64, 0x19, 0xc8, 0x7d, 0x32, 0xd1), + LL(0x49, 0x49, 0x39, 0x49, 0x72, 0x70, 0x92, 0x3b), + LL(0xd9, 0xd9, 0x43, 0xd9, 0x86, 0x9a, 0xaf, 0x5f), + LL(0xf2, 0xf2, 0xef, 0xf2, 0xc3, 0x1d, 0xf9, 0x31), + LL(0xe3, 0xe3, 0xab, 0xe3, 0x4b, 0x48, 0xdb, 0xa8), + LL(0x5b, 0x5b, 0x71, 0x5b, 0xe2, 0x2a, 0xb6, 0xb9), + LL(0x88, 0x88, 0x1a, 0x88, 0x34, 0x92, 0x0d, 0xbc), + LL(0x9a, 0x9a, 0x52, 0x9a, 0xa4, 0xc8, 0x29, 0x3e), + LL(0x26, 0x26, 0x98, 0x26, 0x2d, 0xbe, 0x4c, 0x0b), + LL(0x32, 0x32, 0xc8, 0x32, 0x8d, 0xfa, 0x64, 0xbf), + LL(0xb0, 0xb0, 0xfa, 0xb0, 0xe9, 0x4a, 0x7d, 0x59), + LL(0xe9, 0xe9, 0x83, 0xe9, 0x1b, 0x6a, 0xcf, 0xf2), + LL(0x0f, 0x0f, 0x3c, 0x0f, 0x78, 0x33, 0x1e, 0x77), + LL(0xd5, 0xd5, 0x73, 0xd5, 0xe6, 0xa6, 0xb7, 0x33), + LL(0x80, 0x80, 0x3a, 0x80, 0x74, 0xba, 0x1d, 0xf4), + LL(0xbe, 0xbe, 0xc2, 0xbe, 0x99, 0x7c, 0x61, 0x27), + LL(0xcd, 0xcd, 0x13, 0xcd, 0x26, 0xde, 0x87, 0xeb), + LL(0x34, 0x34, 0xd0, 0x34, 0xbd, 0xe4, 0x68, 0x89), + LL(0x48, 0x48, 0x3d, 0x48, 0x7a, 0x75, 0x90, 0x32), + LL(0xff, 0xff, 0xdb, 0xff, 0xab, 0x24, 0xe3, 0x54), + LL(0x7a, 0x7a, 0xf5, 0x7a, 0xf7, 0x8f, 0xf4, 0x8d), + LL(0x90, 0x90, 0x7a, 0x90, 0xf4, 0xea, 0x3d, 0x64), + LL(0x5f, 0x5f, 0x61, 0x5f, 0xc2, 0x3e, 0xbe, 0x9d), + LL(0x20, 0x20, 0x80, 0x20, 0x1d, 0xa0, 0x40, 0x3d), + LL(0x68, 0x68, 0xbd, 0x68, 0x67, 0xd5, 0xd0, 0x0f), + LL(0x1a, 0x1a, 0x68, 0x1a, 0xd0, 0x72, 0x34, 0xca), + LL(0xae, 0xae, 0x82, 0xae, 0x19, 0x2c, 0x41, 0xb7), + LL(0xb4, 0xb4, 0xea, 0xb4, 0xc9, 0x5e, 0x75, 0x7d), + LL(0x54, 0x54, 0x4d, 0x54, 0x9a, 0x19, 0xa8, 0xce), + LL(0x93, 0x93, 0x76, 0x93, 0xec, 0xe5, 0x3b, 0x7f), + LL(0x22, 0x22, 0x88, 0x22, 0x0d, 0xaa, 0x44, 0x2f), + LL(0x64, 0x64, 0x8d, 0x64, 0x07, 0xe9, 0xc8, 0x63), + LL(0xf1, 0xf1, 0xe3, 0xf1, 0xdb, 0x12, 0xff, 0x2a), + LL(0x73, 0x73, 0xd1, 0x73, 0xbf, 0xa2, 0xe6, 0xcc), + LL(0x12, 0x12, 0x48, 0x12, 0x90, 0x5a, 0x24, 0x82), + LL(0x40, 0x40, 0x1d, 0x40, 0x3a, 0x5d, 0x80, 0x7a), + LL(0x08, 0x08, 0x20, 0x08, 0x40, 0x28, 0x10, 0x48), + LL(0xc3, 0xc3, 0x2b, 0xc3, 0x56, 0xe8, 0x9b, 0x95), + LL(0xec, 0xec, 0x97, 0xec, 0x33, 0x7b, 0xc5, 0xdf), + LL(0xdb, 0xdb, 0x4b, 0xdb, 0x96, 0x90, 0xab, 0x4d), + LL(0xa1, 0xa1, 0xbe, 0xa1, 0x61, 0x1f, 0x5f, 0xc0), + LL(0x8d, 0x8d, 0x0e, 0x8d, 0x1c, 0x83, 0x07, 0x91), + LL(0x3d, 0x3d, 0xf4, 0x3d, 0xf5, 0xc9, 0x7a, 0xc8), + LL(0x97, 0x97, 0x66, 0x97, 0xcc, 0xf1, 0x33, 0x5b), + LL(0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00), + LL(0xcf, 0xcf, 0x1b, 0xcf, 0x36, 0xd4, 0x83, 0xf9), + LL(0x2b, 0x2b, 0xac, 0x2b, 0x45, 0x87, 0x56, 0x6e), + LL(0x76, 0x76, 0xc5, 0x76, 0x97, 0xb3, 0xec, 0xe1), + LL(0x82, 0x82, 0x32, 0x82, 0x64, 0xb0, 0x19, 0xe6), + LL(0xd6, 0xd6, 0x7f, 0xd6, 0xfe, 0xa9, 0xb1, 0x28), + LL(0x1b, 0x1b, 0x6c, 0x1b, 0xd8, 0x77, 0x36, 0xc3), + LL(0xb5, 0xb5, 0xee, 0xb5, 0xc1, 0x5b, 0x77, 0x74), + LL(0xaf, 0xaf, 0x86, 0xaf, 0x11, 0x29, 0x43, 0xbe), + LL(0x6a, 0x6a, 0xb5, 0x6a, 0x77, 0xdf, 0xd4, 0x1d), + LL(0x50, 0x50, 0x5d, 0x50, 0xba, 0x0d, 0xa0, 0xea), + LL(0x45, 0x45, 0x09, 0x45, 0x12, 0x4c, 0x8a, 0x57), + LL(0xf3, 0xf3, 0xeb, 0xf3, 0xcb, 0x18, 0xfb, 0x38), + LL(0x30, 0x30, 0xc0, 0x30, 0x9d, 0xf0, 0x60, 0xad), + LL(0xef, 0xef, 0x9b, 0xef, 0x2b, 0x74, 0xc3, 0xc4), + LL(0x3f, 0x3f, 0xfc, 0x3f, 0xe5, 0xc3, 0x7e, 0xda), + LL(0x55, 0x55, 0x49, 0x55, 0x92, 0x1c, 0xaa, 0xc7), + LL(0xa2, 0xa2, 0xb2, 0xa2, 0x79, 0x10, 0x59, 0xdb), + LL(0xea, 0xea, 0x8f, 0xea, 0x03, 0x65, 0xc9, 0xe9), + LL(0x65, 0x65, 0x89, 0x65, 0x0f, 0xec, 0xca, 0x6a), + LL(0xba, 0xba, 0xd2, 0xba, 0xb9, 0x68, 0x69, 0x03), + LL(0x2f, 0x2f, 0xbc, 0x2f, 0x65, 0x93, 0x5e, 0x4a), + LL(0xc0, 0xc0, 0x27, 0xc0, 0x4e, 0xe7, 0x9d, 0x8e), + LL(0xde, 0xde, 0x5f, 0xde, 0xbe, 0x81, 0xa1, 0x60), + LL(0x1c, 0x1c, 0x70, 0x1c, 0xe0, 0x6c, 0x38, 0xfc), + LL(0xfd, 0xfd, 0xd3, 0xfd, 0xbb, 0x2e, 0xe7, 0x46), + LL(0x4d, 0x4d, 0x29, 0x4d, 0x52, 0x64, 0x9a, 0x1f), + LL(0x92, 0x92, 0x72, 0x92, 0xe4, 0xe0, 0x39, 0x76), + LL(0x75, 0x75, 0xc9, 0x75, 0x8f, 0xbc, 0xea, 0xfa), + LL(0x06, 0x06, 0x18, 0x06, 0x30, 0x1e, 0x0c, 0x36), + LL(0x8a, 0x8a, 0x12, 0x8a, 0x24, 0x98, 0x09, 0xae), + LL(0xb2, 0xb2, 0xf2, 0xb2, 0xf9, 0x40, 0x79, 0x4b), + LL(0xe6, 0xe6, 0xbf, 0xe6, 0x63, 0x59, 0xd1, 0x85), + LL(0x0e, 0x0e, 0x38, 0x0e, 0x70, 0x36, 0x1c, 0x7e), + LL(0x1f, 0x1f, 0x7c, 0x1f, 0xf8, 0x63, 0x3e, 0xe7), + LL(0x62, 0x62, 0x95, 0x62, 0x37, 0xf7, 0xc4, 0x55), + LL(0xd4, 0xd4, 0x77, 0xd4, 0xee, 0xa3, 0xb5, 0x3a), + LL(0xa8, 0xa8, 0x9a, 0xa8, 0x29, 0x32, 0x4d, 0x81), + LL(0x96, 0x96, 0x62, 0x96, 0xc4, 0xf4, 0x31, 0x52), + LL(0xf9, 0xf9, 0xc3, 0xf9, 0x9b, 0x3a, 0xef, 0x62), + LL(0xc5, 0xc5, 0x33, 0xc5, 0x66, 0xf6, 0x97, 0xa3), + LL(0x25, 0x25, 0x94, 0x25, 0x35, 0xb1, 0x4a, 0x10), + LL(0x59, 0x59, 0x79, 0x59, 0xf2, 0x20, 0xb2, 0xab), + LL(0x84, 0x84, 0x2a, 0x84, 0x54, 0xae, 0x15, 0xd0), + LL(0x72, 0x72, 0xd5, 0x72, 0xb7, 0xa7, 0xe4, 0xc5), + LL(0x39, 0x39, 0xe4, 0x39, 0xd5, 0xdd, 0x72, 0xec), + LL(0x4c, 0x4c, 0x2d, 0x4c, 0x5a, 0x61, 0x98, 0x16), + LL(0x5e, 0x5e, 0x65, 0x5e, 0xca, 0x3b, 0xbc, 0x94), + LL(0x78, 0x78, 0xfd, 0x78, 0xe7, 0x85, 0xf0, 0x9f), + LL(0x38, 0x38, 0xe0, 0x38, 0xdd, 0xd8, 0x70, 0xe5), + LL(0x8c, 0x8c, 0x0a, 0x8c, 0x14, 0x86, 0x05, 0x98), + LL(0xd1, 0xd1, 0x63, 0xd1, 0xc6, 0xb2, 0xbf, 0x17), + LL(0xa5, 0xa5, 0xae, 0xa5, 0x41, 0x0b, 0x57, 0xe4), + LL(0xe2, 0xe2, 0xaf, 0xe2, 0x43, 0x4d, 0xd9, 0xa1), + LL(0x61, 0x61, 0x99, 0x61, 0x2f, 0xf8, 0xc2, 0x4e), + LL(0xb3, 0xb3, 0xf6, 0xb3, 0xf1, 0x45, 0x7b, 0x42), + LL(0x21, 0x21, 0x84, 0x21, 0x15, 0xa5, 0x42, 0x34), + LL(0x9c, 0x9c, 0x4a, 0x9c, 0x94, 0xd6, 0x25, 0x08), + LL(0x1e, 0x1e, 0x78, 0x1e, 0xf0, 0x66, 0x3c, 0xee), + LL(0x43, 0x43, 0x11, 0x43, 0x22, 0x52, 0x86, 0x61), + LL(0xc7, 0xc7, 0x3b, 0xc7, 0x76, 0xfc, 0x93, 0xb1), + LL(0xfc, 0xfc, 0xd7, 0xfc, 0xb3, 0x2b, 0xe5, 0x4f), + LL(0x04, 0x04, 0x10, 0x04, 0x20, 0x14, 0x08, 0x24), + LL(0x51, 0x51, 0x59, 0x51, 0xb2, 0x08, 0xa2, 0xe3), + LL(0x99, 0x99, 0x5e, 0x99, 0xbc, 0xc7, 0x2f, 0x25), + LL(0x6d, 0x6d, 0xa9, 0x6d, 0x4f, 0xc4, 0xda, 0x22), + LL(0x0d, 0x0d, 0x34, 0x0d, 0x68, 0x39, 0x1a, 0x65), + LL(0xfa, 0xfa, 0xcf, 0xfa, 0x83, 0x35, 0xe9, 0x79), + LL(0xdf, 0xdf, 0x5b, 0xdf, 0xb6, 0x84, 0xa3, 0x69), + LL(0x7e, 0x7e, 0xe5, 0x7e, 0xd7, 0x9b, 0xfc, 0xa9), + LL(0x24, 0x24, 0x90, 0x24, 0x3d, 0xb4, 0x48, 0x19), + LL(0x3b, 0x3b, 0xec, 0x3b, 0xc5, 0xd7, 0x76, 0xfe), + LL(0xab, 0xab, 0x96, 0xab, 0x31, 0x3d, 0x4b, 0x9a), + LL(0xce, 0xce, 0x1f, 0xce, 0x3e, 0xd1, 0x81, 0xf0), + LL(0x11, 0x11, 0x44, 0x11, 0x88, 0x55, 0x22, 0x99), + LL(0x8f, 0x8f, 0x06, 0x8f, 0x0c, 0x89, 0x03, 0x83), + LL(0x4e, 0x4e, 0x25, 0x4e, 0x4a, 0x6b, 0x9c, 0x04), + LL(0xb7, 0xb7, 0xe6, 0xb7, 0xd1, 0x51, 0x73, 0x66), + LL(0xeb, 0xeb, 0x8b, 0xeb, 0x0b, 0x60, 0xcb, 0xe0), + LL(0x3c, 0x3c, 0xf0, 0x3c, 0xfd, 0xcc, 0x78, 0xc1), + LL(0x81, 0x81, 0x3e, 0x81, 0x7c, 0xbf, 0x1f, 0xfd), + LL(0x94, 0x94, 0x6a, 0x94, 0xd4, 0xfe, 0x35, 0x40), + LL(0xf7, 0xf7, 0xfb, 0xf7, 0xeb, 0x0c, 0xf3, 0x1c), + LL(0xb9, 0xb9, 0xde, 0xb9, 0xa1, 0x67, 0x6f, 0x18), + LL(0x13, 0x13, 0x4c, 0x13, 0x98, 0x5f, 0x26, 0x8b), + LL(0x2c, 0x2c, 0xb0, 0x2c, 0x7d, 0x9c, 0x58, 0x51), + LL(0xd3, 0xd3, 0x6b, 0xd3, 0xd6, 0xb8, 0xbb, 0x05), + LL(0xe7, 0xe7, 0xbb, 0xe7, 0x6b, 0x5c, 0xd3, 0x8c), + LL(0x6e, 0x6e, 0xa5, 0x6e, 0x57, 0xcb, 0xdc, 0x39), + LL(0xc4, 0xc4, 0x37, 0xc4, 0x6e, 0xf3, 0x95, 0xaa), + LL(0x03, 0x03, 0x0c, 0x03, 0x18, 0x0f, 0x06, 0x1b), + LL(0x56, 0x56, 0x45, 0x56, 0x8a, 0x13, 0xac, 0xdc), + LL(0x44, 0x44, 0x0d, 0x44, 0x1a, 0x49, 0x88, 0x5e), + LL(0x7f, 0x7f, 0xe1, 0x7f, 0xdf, 0x9e, 0xfe, 0xa0), + LL(0xa9, 0xa9, 0x9e, 0xa9, 0x21, 0x37, 0x4f, 0x88), + LL(0x2a, 0x2a, 0xa8, 0x2a, 0x4d, 0x82, 0x54, 0x67), + LL(0xbb, 0xbb, 0xd6, 0xbb, 0xb1, 0x6d, 0x6b, 0x0a), + LL(0xc1, 0xc1, 0x23, 0xc1, 0x46, 0xe2, 0x9f, 0x87), + LL(0x53, 0x53, 0x51, 0x53, 0xa2, 0x02, 0xa6, 0xf1), + LL(0xdc, 0xdc, 0x57, 0xdc, 0xae, 0x8b, 0xa5, 0x72), + LL(0x0b, 0x0b, 0x2c, 0x0b, 0x58, 0x27, 0x16, 0x53), + LL(0x9d, 0x9d, 0x4e, 0x9d, 0x9c, 0xd3, 0x27, 0x01), + LL(0x6c, 0x6c, 0xad, 0x6c, 0x47, 0xc1, 0xd8, 0x2b), + LL(0x31, 0x31, 0xc4, 0x31, 0x95, 0xf5, 0x62, 0xa4), + LL(0x74, 0x74, 0xcd, 0x74, 0x87, 0xb9, 0xe8, 0xf3), + LL(0xf6, 0xf6, 0xff, 0xf6, 0xe3, 0x09, 0xf1, 0x15), + LL(0x46, 0x46, 0x05, 0x46, 0x0a, 0x43, 0x8c, 0x4c), + LL(0xac, 0xac, 0x8a, 0xac, 0x09, 0x26, 0x45, 0xa5), + LL(0x89, 0x89, 0x1e, 0x89, 0x3c, 0x97, 0x0f, 0xb5), + LL(0x14, 0x14, 0x50, 0x14, 0xa0, 0x44, 0x28, 0xb4), + LL(0xe1, 0xe1, 0xa3, 0xe1, 0x5b, 0x42, 0xdf, 0xba), + LL(0x16, 0x16, 0x58, 0x16, 0xb0, 0x4e, 0x2c, 0xa6), + LL(0x3a, 0x3a, 0xe8, 0x3a, 0xcd, 0xd2, 0x74, 0xf7), + LL(0x69, 0x69, 0xb9, 0x69, 0x6f, 0xd0, 0xd2, 0x06), + LL(0x09, 0x09, 0x24, 0x09, 0x48, 0x2d, 0x12, 0x41), + LL(0x70, 0x70, 0xdd, 0x70, 0xa7, 0xad, 0xe0, 0xd7), + LL(0xb6, 0xb6, 0xe2, 0xb6, 0xd9, 0x54, 0x71, 0x6f), + LL(0xd0, 0xd0, 0x67, 0xd0, 0xce, 0xb7, 0xbd, 0x1e), + LL(0xed, 0xed, 0x93, 0xed, 0x3b, 0x7e, 0xc7, 0xd6), + LL(0xcc, 0xcc, 0x17, 0xcc, 0x2e, 0xdb, 0x85, 0xe2), + LL(0x42, 0x42, 0x15, 0x42, 0x2a, 0x57, 0x84, 0x68), + LL(0x98, 0x98, 0x5a, 0x98, 0xb4, 0xc2, 0x2d, 0x2c), + LL(0xa4, 0xa4, 0xaa, 0xa4, 0x49, 0x0e, 0x55, 0xed), + LL(0x28, 0x28, 0xa0, 0x28, 0x5d, 0x88, 0x50, 0x75), + LL(0x5c, 0x5c, 0x6d, 0x5c, 0xda, 0x31, 0xb8, 0x86), + LL(0xf8, 0xf8, 0xc7, 0xf8, 0x93, 0x3f, 0xed, 0x6b), + LL(0x86, 0x86, 0x22, 0x86, 0x44, 0xa4, 0x11, 0xc2), +#define RC (&(Cx.q[256*N])) + 0x18, 0x23, 0xc6, 0xe8, 0x87, 0xb8, 0x01, 0x4f, + /* rc[ROUNDS] */ + 0x36, 0xa6, 0xd2, 0xf5, 0x79, 0x6f, 0x91, 0x52, 0x60, 0xbc, 0x9b, + 0x8e, 0xa3, 0x0c, 0x7b, 0x35, 0x1d, 0xe0, 0xd7, 0xc2, 0x2e, 0x4b, + 0xfe, 0x57, 0x15, 0x77, 0x37, 0xe5, 0x9f, 0xf0, 0x4a, 0xda, 0x58, + 0xc9, 0x29, 0x0a, 0xb1, 0xa0, 0x6b, 0x85, 0xbd, 0x5d, 0x10, 0xf4, + 0xcb, 0x3e, 0x05, 0x67, 0xe4, 0x27, 0x41, 0x8b, 0xa7, 0x7d, 0x95, + 0xd8, 0xfb, 0xee, 0x7c, 0x66, 0xdd, 0x17, 0x47, 0x9e, 0xca, 0x2d, + 0xbf, 0x07, 0xad, 0x5a, 0x83, 0x33 + } + }; + +void whirlpool_block(WHIRLPOOL_CTX *ctx, const void *inp, size_t n) +{ + int r; + const u8 *p = inp; + union { + u64 q[8]; + u8 c[64]; + } S, K, *H = (void *)ctx->H.q; + +#ifdef GO_FOR_MMX + GO_FOR_MMX(ctx, inp, n); +#endif + do { +#ifdef OPENSSL_SMALL_FOOTPRINT + u64 L[8]; + int i; + + for (i = 0; i < 64; i++) + S.c[i] = (K.c[i] = H->c[i]) ^ p[i]; + for (r = 0; r < ROUNDS; r++) { + for (i = 0; i < 8; i++) { + L[i] = i ? 0 : RC[r]; + L[i] ^= C0(K, i) ^ C1(K, (i - 1) & 7) ^ + C2(K, (i - 2) & 7) ^ C3(K, (i - 3) & 7) ^ + C4(K, (i - 4) & 7) ^ C5(K, (i - 5) & 7) ^ + C6(K, (i - 6) & 7) ^ C7(K, (i - 7) & 7); + } + memcpy(K.q, L, 64); + for (i = 0; i < 8; i++) { + L[i] ^= C0(S, i) ^ C1(S, (i - 1) & 7) ^ + C2(S, (i - 2) & 7) ^ C3(S, (i - 3) & 7) ^ + C4(S, (i - 4) & 7) ^ C5(S, (i - 5) & 7) ^ + C6(S, (i - 6) & 7) ^ C7(S, (i - 7) & 7); + } + memcpy(S.q, L, 64); + } + for (i = 0; i < 64; i++) + H->c[i] ^= S.c[i] ^ p[i]; +#else + u64 L0, L1, L2, L3, L4, L5, L6, L7; + +# ifdef STRICT_ALIGNMENT + if ((size_t)p & 7) { + memcpy(S.c, p, 64); + S.q[0] ^= (K.q[0] = H->q[0]); + S.q[1] ^= (K.q[1] = H->q[1]); + S.q[2] ^= (K.q[2] = H->q[2]); + S.q[3] ^= (K.q[3] = H->q[3]); + S.q[4] ^= (K.q[4] = H->q[4]); + S.q[5] ^= (K.q[5] = H->q[5]); + S.q[6] ^= (K.q[6] = H->q[6]); + S.q[7] ^= (K.q[7] = H->q[7]); + } else +# endif + { + const u64 *pa = (const u64 *)p; + S.q[0] = (K.q[0] = H->q[0]) ^ pa[0]; + S.q[1] = (K.q[1] = H->q[1]) ^ pa[1]; + S.q[2] = (K.q[2] = H->q[2]) ^ pa[2]; + S.q[3] = (K.q[3] = H->q[3]) ^ pa[3]; + S.q[4] = (K.q[4] = H->q[4]) ^ pa[4]; + S.q[5] = (K.q[5] = H->q[5]) ^ pa[5]; + S.q[6] = (K.q[6] = H->q[6]) ^ pa[6]; + S.q[7] = (K.q[7] = H->q[7]) ^ pa[7]; + } + + for (r = 0; r < ROUNDS; r++) { +# ifdef SMALL_REGISTER_BANK + L0 = C0(K, 0) ^ C1(K, 7) ^ C2(K, 6) ^ C3(K, 5) ^ + C4(K, 4) ^ C5(K, 3) ^ C6(K, 2) ^ C7(K, 1) ^ RC[r]; + L1 = C0(K, 1) ^ C1(K, 0) ^ C2(K, 7) ^ C3(K, 6) ^ + C4(K, 5) ^ C5(K, 4) ^ C6(K, 3) ^ C7(K, 2); + L2 = C0(K, 2) ^ C1(K, 1) ^ C2(K, 0) ^ C3(K, 7) ^ + C4(K, 6) ^ C5(K, 5) ^ C6(K, 4) ^ C7(K, 3); + L3 = C0(K, 3) ^ C1(K, 2) ^ C2(K, 1) ^ C3(K, 0) ^ + C4(K, 7) ^ C5(K, 6) ^ C6(K, 5) ^ C7(K, 4); + L4 = C0(K, 4) ^ C1(K, 3) ^ C2(K, 2) ^ C3(K, 1) ^ + C4(K, 0) ^ C5(K, 7) ^ C6(K, 6) ^ C7(K, 5); + L5 = C0(K, 5) ^ C1(K, 4) ^ C2(K, 3) ^ C3(K, 2) ^ + C4(K, 1) ^ C5(K, 0) ^ C6(K, 7) ^ C7(K, 6); + L6 = C0(K, 6) ^ C1(K, 5) ^ C2(K, 4) ^ C3(K, 3) ^ + C4(K, 2) ^ C5(K, 1) ^ C6(K, 0) ^ C7(K, 7); + L7 = C0(K, 7) ^ C1(K, 6) ^ C2(K, 5) ^ C3(K, 4) ^ + C4(K, 3) ^ C5(K, 2) ^ C6(K, 1) ^ C7(K, 0); + + K.q[0] = L0; + K.q[1] = L1; + K.q[2] = L2; + K.q[3] = L3; + K.q[4] = L4; + K.q[5] = L5; + K.q[6] = L6; + K.q[7] = L7; + + L0 ^= C0(S, 0) ^ C1(S, 7) ^ C2(S, 6) ^ C3(S, 5) ^ + C4(S, 4) ^ C5(S, 3) ^ C6(S, 2) ^ C7(S, 1); + L1 ^= C0(S, 1) ^ C1(S, 0) ^ C2(S, 7) ^ C3(S, 6) ^ + C4(S, 5) ^ C5(S, 4) ^ C6(S, 3) ^ C7(S, 2); + L2 ^= C0(S, 2) ^ C1(S, 1) ^ C2(S, 0) ^ C3(S, 7) ^ + C4(S, 6) ^ C5(S, 5) ^ C6(S, 4) ^ C7(S, 3); + L3 ^= C0(S, 3) ^ C1(S, 2) ^ C2(S, 1) ^ C3(S, 0) ^ + C4(S, 7) ^ C5(S, 6) ^ C6(S, 5) ^ C7(S, 4); + L4 ^= C0(S, 4) ^ C1(S, 3) ^ C2(S, 2) ^ C3(S, 1) ^ + C4(S, 0) ^ C5(S, 7) ^ C6(S, 6) ^ C7(S, 5); + L5 ^= C0(S, 5) ^ C1(S, 4) ^ C2(S, 3) ^ C3(S, 2) ^ + C4(S, 1) ^ C5(S, 0) ^ C6(S, 7) ^ C7(S, 6); + L6 ^= C0(S, 6) ^ C1(S, 5) ^ C2(S, 4) ^ C3(S, 3) ^ + C4(S, 2) ^ C5(S, 1) ^ C6(S, 0) ^ C7(S, 7); + L7 ^= C0(S, 7) ^ C1(S, 6) ^ C2(S, 5) ^ C3(S, 4) ^ + C4(S, 3) ^ C5(S, 2) ^ C6(S, 1) ^ C7(S, 0); + + S.q[0] = L0; + S.q[1] = L1; + S.q[2] = L2; + S.q[3] = L3; + S.q[4] = L4; + S.q[5] = L5; + S.q[6] = L6; + S.q[7] = L7; +# else + L0 = C0(K, 0); + L1 = C1(K, 0); + L2 = C2(K, 0); + L3 = C3(K, 0); + L4 = C4(K, 0); + L5 = C5(K, 0); + L6 = C6(K, 0); + L7 = C7(K, 0); + L0 ^= RC[r]; + + L1 ^= C0(K, 1); + L2 ^= C1(K, 1); + L3 ^= C2(K, 1); + L4 ^= C3(K, 1); + L5 ^= C4(K, 1); + L6 ^= C5(K, 1); + L7 ^= C6(K, 1); + L0 ^= C7(K, 1); + + L2 ^= C0(K, 2); + L3 ^= C1(K, 2); + L4 ^= C2(K, 2); + L5 ^= C3(K, 2); + L6 ^= C4(K, 2); + L7 ^= C5(K, 2); + L0 ^= C6(K, 2); + L1 ^= C7(K, 2); + + L3 ^= C0(K, 3); + L4 ^= C1(K, 3); + L5 ^= C2(K, 3); + L6 ^= C3(K, 3); + L7 ^= C4(K, 3); + L0 ^= C5(K, 3); + L1 ^= C6(K, 3); + L2 ^= C7(K, 3); + + L4 ^= C0(K, 4); + L5 ^= C1(K, 4); + L6 ^= C2(K, 4); + L7 ^= C3(K, 4); + L0 ^= C4(K, 4); + L1 ^= C5(K, 4); + L2 ^= C6(K, 4); + L3 ^= C7(K, 4); + + L5 ^= C0(K, 5); + L6 ^= C1(K, 5); + L7 ^= C2(K, 5); + L0 ^= C3(K, 5); + L1 ^= C4(K, 5); + L2 ^= C5(K, 5); + L3 ^= C6(K, 5); + L4 ^= C7(K, 5); + + L6 ^= C0(K, 6); + L7 ^= C1(K, 6); + L0 ^= C2(K, 6); + L1 ^= C3(K, 6); + L2 ^= C4(K, 6); + L3 ^= C5(K, 6); + L4 ^= C6(K, 6); + L5 ^= C7(K, 6); + + L7 ^= C0(K, 7); + L0 ^= C1(K, 7); + L1 ^= C2(K, 7); + L2 ^= C3(K, 7); + L3 ^= C4(K, 7); + L4 ^= C5(K, 7); + L5 ^= C6(K, 7); + L6 ^= C7(K, 7); + + K.q[0] = L0; + K.q[1] = L1; + K.q[2] = L2; + K.q[3] = L3; + K.q[4] = L4; + K.q[5] = L5; + K.q[6] = L6; + K.q[7] = L7; + + L0 ^= C0(S, 0); + L1 ^= C1(S, 0); + L2 ^= C2(S, 0); + L3 ^= C3(S, 0); + L4 ^= C4(S, 0); + L5 ^= C5(S, 0); + L6 ^= C6(S, 0); + L7 ^= C7(S, 0); + + L1 ^= C0(S, 1); + L2 ^= C1(S, 1); + L3 ^= C2(S, 1); + L4 ^= C3(S, 1); + L5 ^= C4(S, 1); + L6 ^= C5(S, 1); + L7 ^= C6(S, 1); + L0 ^= C7(S, 1); + + L2 ^= C0(S, 2); + L3 ^= C1(S, 2); + L4 ^= C2(S, 2); + L5 ^= C3(S, 2); + L6 ^= C4(S, 2); + L7 ^= C5(S, 2); + L0 ^= C6(S, 2); + L1 ^= C7(S, 2); + + L3 ^= C0(S, 3); + L4 ^= C1(S, 3); + L5 ^= C2(S, 3); + L6 ^= C3(S, 3); + L7 ^= C4(S, 3); + L0 ^= C5(S, 3); + L1 ^= C6(S, 3); + L2 ^= C7(S, 3); + + L4 ^= C0(S, 4); + L5 ^= C1(S, 4); + L6 ^= C2(S, 4); + L7 ^= C3(S, 4); + L0 ^= C4(S, 4); + L1 ^= C5(S, 4); + L2 ^= C6(S, 4); + L3 ^= C7(S, 4); + + L5 ^= C0(S, 5); + L6 ^= C1(S, 5); + L7 ^= C2(S, 5); + L0 ^= C3(S, 5); + L1 ^= C4(S, 5); + L2 ^= C5(S, 5); + L3 ^= C6(S, 5); + L4 ^= C7(S, 5); + + L6 ^= C0(S, 6); + L7 ^= C1(S, 6); + L0 ^= C2(S, 6); + L1 ^= C3(S, 6); + L2 ^= C4(S, 6); + L3 ^= C5(S, 6); + L4 ^= C6(S, 6); + L5 ^= C7(S, 6); + + L7 ^= C0(S, 7); + L0 ^= C1(S, 7); + L1 ^= C2(S, 7); + L2 ^= C3(S, 7); + L3 ^= C4(S, 7); + L4 ^= C5(S, 7); + L5 ^= C6(S, 7); + L6 ^= C7(S, 7); + + S.q[0] = L0; + S.q[1] = L1; + S.q[2] = L2; + S.q[3] = L3; + S.q[4] = L4; + S.q[5] = L5; + S.q[6] = L6; + S.q[7] = L7; +# endif + } + +# ifdef STRICT_ALIGNMENT + if ((size_t)p & 7) { + int i; + for (i = 0; i < 64; i++) + H->c[i] ^= S.c[i] ^ p[i]; + } else +# endif + { + const u64 *pa = (const u64 *)p; + H->q[0] ^= S.q[0] ^ pa[0]; + H->q[1] ^= S.q[1] ^ pa[1]; + H->q[2] ^= S.q[2] ^ pa[2]; + H->q[3] ^= S.q[3] ^ pa[3]; + H->q[4] ^= S.q[4] ^ pa[4]; + H->q[5] ^= S.q[5] ^ pa[5]; + H->q[6] ^= S.q[6] ^ pa[6]; + H->q[7] ^= S.q[7] ^ pa[7]; + } +#endif + p += 64; + } while (--n); +} diff --git a/crypto/whrlpool/wp_dgst.c b/crypto/whrlpool/wp_dgst.c new file mode 100644 index 000000000..ed064244f --- /dev/null +++ b/crypto/whrlpool/wp_dgst.c @@ -0,0 +1,266 @@ +/* + * Copyright 2005-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +/** + * The Whirlpool hashing function. + * + *

+ * References + * + *

+ * The Whirlpool algorithm was developed by + * Paulo S. L. M. Barreto and + * Vincent Rijmen. + * + * See + * P.S.L.M. Barreto, V. Rijmen, + * ``The Whirlpool hashing function,'' + * NESSIE submission, 2000 (tweaked version, 2001), + * + * + * Based on "@version 3.0 (2003.03.12)" by Paulo S.L.M. Barreto and + * Vincent Rijmen. Lookup "reference implementations" on + * + * + * ============================================================================= + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +/* + * OpenSSL-specific implementation notes. + * + * WHIRLPOOL_Update as well as one-stroke WHIRLPOOL both expect + * number of *bytes* as input length argument. Bit-oriented routine + * as specified by authors is called WHIRLPOOL_BitUpdate[!] and + * does not have one-stroke counterpart. + * + * WHIRLPOOL_BitUpdate implements byte-oriented loop, essentially + * to serve WHIRLPOOL_Update. This is done for performance. + * + * Unlike authors' reference implementation, block processing + * routine whirlpool_block is designed to operate on multi-block + * input. This is done for performance. + */ + +#include +#include "wp_locl.h" +#include + +int WHIRLPOOL_Init(WHIRLPOOL_CTX *c) +{ + memset(c, 0, sizeof(*c)); + return (1); +} + +int WHIRLPOOL_Update(WHIRLPOOL_CTX *c, const void *_inp, size_t bytes) +{ + /* + * Well, largest suitable chunk size actually is + * (1<<(sizeof(size_t)*8-3))-64, but below number is large enough for not + * to care about excessive calls to WHIRLPOOL_BitUpdate... + */ + size_t chunk = ((size_t)1) << (sizeof(size_t) * 8 - 4); + const unsigned char *inp = _inp; + + while (bytes >= chunk) { + WHIRLPOOL_BitUpdate(c, inp, chunk * 8); + bytes -= chunk; + inp += chunk; + } + if (bytes) + WHIRLPOOL_BitUpdate(c, inp, bytes * 8); + + return (1); +} + +void WHIRLPOOL_BitUpdate(WHIRLPOOL_CTX *c, const void *_inp, size_t bits) +{ + size_t n; + unsigned int bitoff = c->bitoff, + bitrem = bitoff % 8, inpgap = (8 - (unsigned int)bits % 8) & 7; + const unsigned char *inp = _inp; + + /* + * This 256-bit increment procedure relies on the size_t being natural + * size of CPU register, so that we don't have to mask the value in order + * to detect overflows. + */ + c->bitlen[0] += bits; + if (c->bitlen[0] < bits) { /* overflow */ + n = 1; + do { + c->bitlen[n]++; + } while (c->bitlen[n] == 0 + && ++n < (WHIRLPOOL_COUNTER / sizeof(size_t))); + } +#ifndef OPENSSL_SMALL_FOOTPRINT + reconsider: + if (inpgap == 0 && bitrem == 0) { /* byte-oriented loop */ + while (bits) { + if (bitoff == 0 && (n = bits / WHIRLPOOL_BBLOCK)) { + whirlpool_block(c, inp, n); + inp += n * WHIRLPOOL_BBLOCK / 8; + bits %= WHIRLPOOL_BBLOCK; + } else { + unsigned int byteoff = bitoff / 8; + + bitrem = WHIRLPOOL_BBLOCK - bitoff; /* re-use bitrem */ + if (bits >= bitrem) { + bits -= bitrem; + bitrem /= 8; + memcpy(c->data + byteoff, inp, bitrem); + inp += bitrem; + whirlpool_block(c, c->data, 1); + bitoff = 0; + } else { + memcpy(c->data + byteoff, inp, bits / 8); + bitoff += (unsigned int)bits; + bits = 0; + } + c->bitoff = bitoff; + } + } + } else /* bit-oriented loop */ +#endif + { + /*- + inp + | + +-------+-------+------- + ||||||||||||||||||||| + +-------+-------+------- + +-------+-------+-------+-------+------- + |||||||||||||| c->data + +-------+-------+-------+-------+------- + | + c->bitoff/8 + */ + while (bits) { + unsigned int byteoff = bitoff / 8; + unsigned char b; + +#ifndef OPENSSL_SMALL_FOOTPRINT + if (bitrem == inpgap) { + c->data[byteoff++] |= inp[0] & (0xff >> inpgap); + inpgap = 8 - inpgap; + bitoff += inpgap; + bitrem = 0; /* bitoff%8 */ + bits -= inpgap; + inpgap = 0; /* bits%8 */ + inp++; + if (bitoff == WHIRLPOOL_BBLOCK) { + whirlpool_block(c, c->data, 1); + bitoff = 0; + } + c->bitoff = bitoff; + goto reconsider; + } else +#endif + if (bits >= 8) { + b = ((inp[0] << inpgap) | (inp[1] >> (8 - inpgap))); + b &= 0xff; + if (bitrem) + c->data[byteoff++] |= b >> bitrem; + else + c->data[byteoff++] = b; + bitoff += 8; + bits -= 8; + inp++; + if (bitoff >= WHIRLPOOL_BBLOCK) { + whirlpool_block(c, c->data, 1); + byteoff = 0; + bitoff %= WHIRLPOOL_BBLOCK; + } + if (bitrem) + c->data[byteoff] = b << (8 - bitrem); + } else { /* remaining less than 8 bits */ + + b = (inp[0] << inpgap) & 0xff; + if (bitrem) + c->data[byteoff++] |= b >> bitrem; + else + c->data[byteoff++] = b; + bitoff += (unsigned int)bits; + if (bitoff == WHIRLPOOL_BBLOCK) { + whirlpool_block(c, c->data, 1); + byteoff = 0; + bitoff %= WHIRLPOOL_BBLOCK; + } + if (bitrem) + c->data[byteoff] = b << (8 - bitrem); + bits = 0; + } + c->bitoff = bitoff; + } + } +} + +int WHIRLPOOL_Final(unsigned char *md, WHIRLPOOL_CTX *c) +{ + unsigned int bitoff = c->bitoff, byteoff = bitoff / 8; + size_t i, j, v; + unsigned char *p; + + bitoff %= 8; + if (bitoff) + c->data[byteoff] |= 0x80 >> bitoff; + else + c->data[byteoff] = 0x80; + byteoff++; + + /* pad with zeros */ + if (byteoff > (WHIRLPOOL_BBLOCK / 8 - WHIRLPOOL_COUNTER)) { + if (byteoff < WHIRLPOOL_BBLOCK / 8) + memset(&c->data[byteoff], 0, WHIRLPOOL_BBLOCK / 8 - byteoff); + whirlpool_block(c, c->data, 1); + byteoff = 0; + } + if (byteoff < (WHIRLPOOL_BBLOCK / 8 - WHIRLPOOL_COUNTER)) + memset(&c->data[byteoff], 0, + (WHIRLPOOL_BBLOCK / 8 - WHIRLPOOL_COUNTER) - byteoff); + /* smash 256-bit c->bitlen in big-endian order */ + p = &c->data[WHIRLPOOL_BBLOCK / 8 - 1]; /* last byte in c->data */ + for (i = 0; i < WHIRLPOOL_COUNTER / sizeof(size_t); i++) + for (v = c->bitlen[i], j = 0; j < sizeof(size_t); j++, v >>= 8) + *p-- = (unsigned char)(v & 0xff); + + whirlpool_block(c, c->data, 1); + + if (md) { + memcpy(md, c->H.c, WHIRLPOOL_DIGEST_LENGTH); + OPENSSL_cleanse(c, sizeof(*c)); + return (1); + } + return (0); +} + +unsigned char *WHIRLPOOL(const void *inp, size_t bytes, unsigned char *md) +{ + WHIRLPOOL_CTX ctx; + static unsigned char m[WHIRLPOOL_DIGEST_LENGTH]; + + if (md == NULL) + md = m; + WHIRLPOOL_Init(&ctx); + WHIRLPOOL_Update(&ctx, inp, bytes); + WHIRLPOOL_Final(md, &ctx); + return (md); +} diff --git a/crypto/whrlpool/wp_locl.h b/crypto/whrlpool/wp_locl.h new file mode 100644 index 000000000..3a81cfd58 --- /dev/null +++ b/crypto/whrlpool/wp_locl.h @@ -0,0 +1,12 @@ +/* + * Copyright 2005-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include + +void whirlpool_block(WHIRLPOOL_CTX *, const void *, size_t); diff --git a/crypto/x509/build.info b/crypto/x509/build.info new file mode 100644 index 000000000..7fc4b4504 --- /dev/null +++ b/crypto/x509/build.info @@ -0,0 +1,10 @@ +LIBS=../../libcrypto +SOURCE[../../libcrypto]=\ + x509_def.c x509_d2.c x509_r2x.c x509_cmp.c \ + x509_obj.c x509_req.c x509spki.c x509_vfy.c \ + x509_set.c x509cset.c x509rset.c x509_err.c \ + x509name.c x509_v3.c x509_ext.c x509_att.c \ + x509type.c x509_lu.c x_all.c x509_txt.c \ + x509_trs.c by_file.c by_dir.c x509_vpm.c \ + x_crl.c t_crl.c x_req.c t_req.c x_x509.c t_x509.c \ + x_pubkey.c x_x509a.c x_attrib.c x_exten.c x_name.c diff --git a/crypto/x509/by_dir.c b/crypto/x509/by_dir.c new file mode 100644 index 000000000..f3a1f0542 --- /dev/null +++ b/crypto/x509/by_dir.c @@ -0,0 +1,388 @@ +/* + * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#include +#include + +#include "internal/cryptlib.h" + +#ifndef NO_SYS_TYPES_H +# include +#endif +#ifndef OPENSSL_NO_POSIX_IO +# include +#endif + + +#include +#include +#include "internal/x509_int.h" +#include "x509_lcl.h" + +struct lookup_dir_hashes_st { + unsigned long hash; + int suffix; +}; + +struct lookup_dir_entry_st { + char *dir; + int dir_type; + STACK_OF(BY_DIR_HASH) *hashes; +}; + +typedef struct lookup_dir_st { + BUF_MEM *buffer; + STACK_OF(BY_DIR_ENTRY) *dirs; + CRYPTO_RWLOCK *lock; +} BY_DIR; + +static int dir_ctrl(X509_LOOKUP *ctx, int cmd, const char *argp, long argl, + char **ret); +static int new_dir(X509_LOOKUP *lu); +static void free_dir(X509_LOOKUP *lu); +static int add_cert_dir(BY_DIR *ctx, const char *dir, int type); +static int get_cert_by_subject(X509_LOOKUP *xl, X509_LOOKUP_TYPE type, + X509_NAME *name, X509_OBJECT *ret); +static X509_LOOKUP_METHOD x509_dir_lookup = { + "Load certs from files in a directory", + new_dir, /* new */ + free_dir, /* free */ + NULL, /* init */ + NULL, /* shutdown */ + dir_ctrl, /* ctrl */ + get_cert_by_subject, /* get_by_subject */ + NULL, /* get_by_issuer_serial */ + NULL, /* get_by_fingerprint */ + NULL, /* get_by_alias */ +}; + +X509_LOOKUP_METHOD *X509_LOOKUP_hash_dir(void) +{ + return (&x509_dir_lookup); +} + +static int dir_ctrl(X509_LOOKUP *ctx, int cmd, const char *argp, long argl, + char **retp) +{ + int ret = 0; + BY_DIR *ld; + char *dir = NULL; + + ld = (BY_DIR *)ctx->method_data; + + switch (cmd) { + case X509_L_ADD_DIR: + if (argl == X509_FILETYPE_DEFAULT) { + dir = (char *)getenv(X509_get_default_cert_dir_env()); + if (dir) + ret = add_cert_dir(ld, dir, X509_FILETYPE_PEM); + else + ret = add_cert_dir(ld, X509_get_default_cert_dir(), + X509_FILETYPE_PEM); + if (!ret) { + X509err(X509_F_DIR_CTRL, X509_R_LOADING_CERT_DIR); + } + } else + ret = add_cert_dir(ld, argp, (int)argl); + break; + } + return (ret); +} + +static int new_dir(X509_LOOKUP *lu) +{ + BY_DIR *a; + + if ((a = OPENSSL_malloc(sizeof(*a))) == NULL) + return 0; + if ((a->buffer = BUF_MEM_new()) == NULL) { + OPENSSL_free(a); + return 0; + } + a->dirs = NULL; + a->lock = CRYPTO_THREAD_lock_new(); + if (a->lock == NULL) { + BUF_MEM_free(a->buffer); + OPENSSL_free(a); + return 0; + } + lu->method_data = (char *)a; + return 1; +} + +static void by_dir_hash_free(BY_DIR_HASH *hash) +{ + OPENSSL_free(hash); +} + +static int by_dir_hash_cmp(const BY_DIR_HASH *const *a, + const BY_DIR_HASH *const *b) +{ + if ((*a)->hash > (*b)->hash) + return 1; + if ((*a)->hash < (*b)->hash) + return -1; + return 0; +} + +static void by_dir_entry_free(BY_DIR_ENTRY *ent) +{ + OPENSSL_free(ent->dir); + sk_BY_DIR_HASH_pop_free(ent->hashes, by_dir_hash_free); + OPENSSL_free(ent); +} + +static void free_dir(X509_LOOKUP *lu) +{ + BY_DIR *a; + + a = (BY_DIR *)lu->method_data; + sk_BY_DIR_ENTRY_pop_free(a->dirs, by_dir_entry_free); + BUF_MEM_free(a->buffer); + CRYPTO_THREAD_lock_free(a->lock); + OPENSSL_free(a); +} + +static int add_cert_dir(BY_DIR *ctx, const char *dir, int type) +{ + const char *s, *p; + + if (dir == NULL || !*dir) { + X509err(X509_F_ADD_CERT_DIR, X509_R_INVALID_DIRECTORY); + return 0; + } + + s = dir; + p = s; + do { + if ((*p == LIST_SEPARATOR_CHAR) || (*p == '\0')) { + BY_DIR_ENTRY *ent; + int j; + size_t len; + const char *ss = s; + s = p + 1; + len = p - ss; + if (len == 0) + continue; + for (j = 0; j < sk_BY_DIR_ENTRY_num(ctx->dirs); j++) { + ent = sk_BY_DIR_ENTRY_value(ctx->dirs, j); + if (strlen(ent->dir) == len && + strncmp(ent->dir, ss, len) == 0) + break; + } + if (j < sk_BY_DIR_ENTRY_num(ctx->dirs)) + continue; + if (ctx->dirs == NULL) { + ctx->dirs = sk_BY_DIR_ENTRY_new_null(); + if (!ctx->dirs) { + X509err(X509_F_ADD_CERT_DIR, ERR_R_MALLOC_FAILURE); + return 0; + } + } + ent = OPENSSL_malloc(sizeof(*ent)); + if (ent == NULL) + return 0; + ent->dir_type = type; + ent->hashes = sk_BY_DIR_HASH_new(by_dir_hash_cmp); + ent->dir = OPENSSL_strndup(ss, len); + if (ent->dir == NULL || ent->hashes == NULL) { + by_dir_entry_free(ent); + return 0; + } + if (!sk_BY_DIR_ENTRY_push(ctx->dirs, ent)) { + by_dir_entry_free(ent); + return 0; + } + } + } while (*p++ != '\0'); + return 1; +} + +static int get_cert_by_subject(X509_LOOKUP *xl, X509_LOOKUP_TYPE type, + X509_NAME *name, X509_OBJECT *ret) +{ + BY_DIR *ctx; + union { + X509 st_x509; + X509_CRL crl; + } data; + int ok = 0; + int i, j, k; + unsigned long h; + BUF_MEM *b = NULL; + X509_OBJECT stmp, *tmp; + const char *postfix = ""; + + if (name == NULL) + return (0); + + stmp.type = type; + if (type == X509_LU_X509) { + data.st_x509.cert_info.subject = name; + stmp.data.x509 = &data.st_x509; + postfix = ""; + } else if (type == X509_LU_CRL) { + data.crl.crl.issuer = name; + stmp.data.crl = &data.crl; + postfix = "r"; + } else { + X509err(X509_F_GET_CERT_BY_SUBJECT, X509_R_WRONG_LOOKUP_TYPE); + goto finish; + } + + if ((b = BUF_MEM_new()) == NULL) { + X509err(X509_F_GET_CERT_BY_SUBJECT, ERR_R_BUF_LIB); + goto finish; + } + + ctx = (BY_DIR *)xl->method_data; + + h = X509_NAME_hash(name); + for (i = 0; i < sk_BY_DIR_ENTRY_num(ctx->dirs); i++) { + BY_DIR_ENTRY *ent; + int idx; + BY_DIR_HASH htmp, *hent; + ent = sk_BY_DIR_ENTRY_value(ctx->dirs, i); + j = strlen(ent->dir) + 1 + 8 + 6 + 1 + 1; + if (!BUF_MEM_grow(b, j)) { + X509err(X509_F_GET_CERT_BY_SUBJECT, ERR_R_MALLOC_FAILURE); + goto finish; + } + if (type == X509_LU_CRL && ent->hashes) { + htmp.hash = h; + CRYPTO_THREAD_read_lock(ctx->lock); + idx = sk_BY_DIR_HASH_find(ent->hashes, &htmp); + if (idx >= 0) { + hent = sk_BY_DIR_HASH_value(ent->hashes, idx); + k = hent->suffix; + } else { + hent = NULL; + k = 0; + } + CRYPTO_THREAD_unlock(ctx->lock); + } else { + k = 0; + hent = NULL; + } + for (;;) { + char c = '/'; +#ifdef OPENSSL_SYS_VMS + c = ent->dir[strlen(ent->dir) - 1]; + if (c != ':' && c != '>' && c != ']') { + /* + * If no separator is present, we assume the directory + * specifier is a logical name, and add a colon. We really + * should use better VMS routines for merging things like + * this, but this will do for now... -- Richard Levitte + */ + c = ':'; + } else { + c = '\0'; + } +#endif + if (c == '\0') { + /* + * This is special. When c == '\0', no directory separator + * should be added. + */ + BIO_snprintf(b->data, b->max, + "%s%08lx.%s%d", ent->dir, h, postfix, k); + } else { + BIO_snprintf(b->data, b->max, + "%s%c%08lx.%s%d", ent->dir, c, h, postfix, k); + } +#ifndef OPENSSL_NO_POSIX_IO +# ifdef _WIN32 +# define stat _stat +# endif + { + struct stat st; + if (stat(b->data, &st) < 0) + break; + } +#endif + /* found one. */ + if (type == X509_LU_X509) { + if ((X509_load_cert_file(xl, b->data, ent->dir_type)) == 0) + break; + } else if (type == X509_LU_CRL) { + if ((X509_load_crl_file(xl, b->data, ent->dir_type)) == 0) + break; + } + /* else case will caught higher up */ + k++; + } + + /* + * we have added it to the cache so now pull it out again + */ + CRYPTO_THREAD_write_lock(ctx->lock); + j = sk_X509_OBJECT_find(xl->store_ctx->objs, &stmp); + if (j != -1) + tmp = sk_X509_OBJECT_value(xl->store_ctx->objs, j); + else + tmp = NULL; + CRYPTO_THREAD_unlock(ctx->lock); + + /* If a CRL, update the last file suffix added for this */ + + if (type == X509_LU_CRL) { + CRYPTO_THREAD_write_lock(ctx->lock); + /* + * Look for entry again in case another thread added an entry + * first. + */ + if (!hent) { + htmp.hash = h; + idx = sk_BY_DIR_HASH_find(ent->hashes, &htmp); + if (idx >= 0) + hent = sk_BY_DIR_HASH_value(ent->hashes, idx); + } + if (!hent) { + hent = OPENSSL_malloc(sizeof(*hent)); + if (hent == NULL) { + CRYPTO_THREAD_unlock(ctx->lock); + X509err(X509_F_GET_CERT_BY_SUBJECT, ERR_R_MALLOC_FAILURE); + ok = 0; + goto finish; + } + hent->hash = h; + hent->suffix = k; + if (!sk_BY_DIR_HASH_push(ent->hashes, hent)) { + CRYPTO_THREAD_unlock(ctx->lock); + OPENSSL_free(hent); + ok = 0; + goto finish; + } + } else if (hent->suffix < k) { + hent->suffix = k; + } + + CRYPTO_THREAD_unlock(ctx->lock); + + } + + if (tmp != NULL) { + ok = 1; + ret->type = tmp->type; + memcpy(&ret->data, &tmp->data, sizeof(ret->data)); + /* + * If we were going to up the reference count, we would need to + * do it on a perl 'type' basis + */ + /*- CRYPTO_add(&tmp->data.x509->references,1, + CRYPTO_LOCK_X509);*/ + goto finish; + } + } + finish: + BUF_MEM_free(b); + return (ok); +} diff --git a/crypto/x509/by_file.c b/crypto/x509/by_file.c new file mode 100644 index 000000000..4376bed83 --- /dev/null +++ b/crypto/x509/by_file.c @@ -0,0 +1,221 @@ +/* + * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#include +#include + +#include "internal/cryptlib.h" +#include +#include +#include +#include +#include "x509_lcl.h" + +static int by_file_ctrl(X509_LOOKUP *ctx, int cmd, const char *argc, + long argl, char **ret); +static X509_LOOKUP_METHOD x509_file_lookup = { + "Load file into cache", + NULL, /* new */ + NULL, /* free */ + NULL, /* init */ + NULL, /* shutdown */ + by_file_ctrl, /* ctrl */ + NULL, /* get_by_subject */ + NULL, /* get_by_issuer_serial */ + NULL, /* get_by_fingerprint */ + NULL, /* get_by_alias */ +}; + +X509_LOOKUP_METHOD *X509_LOOKUP_file(void) +{ + return (&x509_file_lookup); +} + +static int by_file_ctrl(X509_LOOKUP *ctx, int cmd, const char *argp, + long argl, char **ret) +{ + int ok = 0; + char *file; + + switch (cmd) { + case X509_L_FILE_LOAD: + if (argl == X509_FILETYPE_DEFAULT) { + file = (char *)getenv(X509_get_default_cert_file_env()); + if (file) + ok = (X509_load_cert_crl_file(ctx, file, + X509_FILETYPE_PEM) != 0); + + else + ok = (X509_load_cert_crl_file + (ctx, X509_get_default_cert_file(), + X509_FILETYPE_PEM) != 0); + + if (!ok) { + X509err(X509_F_BY_FILE_CTRL, X509_R_LOADING_DEFAULTS); + } + } else { + if (argl == X509_FILETYPE_PEM) + ok = (X509_load_cert_crl_file(ctx, argp, + X509_FILETYPE_PEM) != 0); + else + ok = (X509_load_cert_file(ctx, argp, (int)argl) != 0); + } + break; + } + return (ok); +} + +int X509_load_cert_file(X509_LOOKUP *ctx, const char *file, int type) +{ + int ret = 0; + BIO *in = NULL; + int i, count = 0; + X509 *x = NULL; + + if (file == NULL) + return (1); + in = BIO_new(BIO_s_file()); + + if ((in == NULL) || (BIO_read_filename(in, file) <= 0)) { + X509err(X509_F_X509_LOAD_CERT_FILE, ERR_R_SYS_LIB); + goto err; + } + + if (type == X509_FILETYPE_PEM) { + for (;;) { + x = PEM_read_bio_X509_AUX(in, NULL, NULL, NULL); + if (x == NULL) { + if ((ERR_GET_REASON(ERR_peek_last_error()) == + PEM_R_NO_START_LINE) && (count > 0)) { + ERR_clear_error(); + break; + } else { + X509err(X509_F_X509_LOAD_CERT_FILE, ERR_R_PEM_LIB); + goto err; + } + } + i = X509_STORE_add_cert(ctx->store_ctx, x); + if (!i) + goto err; + count++; + X509_free(x); + x = NULL; + } + ret = count; + } else if (type == X509_FILETYPE_ASN1) { + x = d2i_X509_bio(in, NULL); + if (x == NULL) { + X509err(X509_F_X509_LOAD_CERT_FILE, ERR_R_ASN1_LIB); + goto err; + } + i = X509_STORE_add_cert(ctx->store_ctx, x); + if (!i) + goto err; + ret = i; + } else { + X509err(X509_F_X509_LOAD_CERT_FILE, X509_R_BAD_X509_FILETYPE); + goto err; + } + err: + X509_free(x); + BIO_free(in); + return (ret); +} + +int X509_load_crl_file(X509_LOOKUP *ctx, const char *file, int type) +{ + int ret = 0; + BIO *in = NULL; + int i, count = 0; + X509_CRL *x = NULL; + + if (file == NULL) + return (1); + in = BIO_new(BIO_s_file()); + + if ((in == NULL) || (BIO_read_filename(in, file) <= 0)) { + X509err(X509_F_X509_LOAD_CRL_FILE, ERR_R_SYS_LIB); + goto err; + } + + if (type == X509_FILETYPE_PEM) { + for (;;) { + x = PEM_read_bio_X509_CRL(in, NULL, NULL, NULL); + if (x == NULL) { + if ((ERR_GET_REASON(ERR_peek_last_error()) == + PEM_R_NO_START_LINE) && (count > 0)) { + ERR_clear_error(); + break; + } else { + X509err(X509_F_X509_LOAD_CRL_FILE, ERR_R_PEM_LIB); + goto err; + } + } + i = X509_STORE_add_crl(ctx->store_ctx, x); + if (!i) + goto err; + count++; + X509_CRL_free(x); + x = NULL; + } + ret = count; + } else if (type == X509_FILETYPE_ASN1) { + x = d2i_X509_CRL_bio(in, NULL); + if (x == NULL) { + X509err(X509_F_X509_LOAD_CRL_FILE, ERR_R_ASN1_LIB); + goto err; + } + i = X509_STORE_add_crl(ctx->store_ctx, x); + if (!i) + goto err; + ret = i; + } else { + X509err(X509_F_X509_LOAD_CRL_FILE, X509_R_BAD_X509_FILETYPE); + goto err; + } + err: + X509_CRL_free(x); + BIO_free(in); + return (ret); +} + +int X509_load_cert_crl_file(X509_LOOKUP *ctx, const char *file, int type) +{ + STACK_OF(X509_INFO) *inf; + X509_INFO *itmp; + BIO *in; + int i, count = 0; + if (type != X509_FILETYPE_PEM) + return X509_load_cert_file(ctx, file, type); + in = BIO_new_file(file, "r"); + if (!in) { + X509err(X509_F_X509_LOAD_CERT_CRL_FILE, ERR_R_SYS_LIB); + return 0; + } + inf = PEM_X509_INFO_read_bio(in, NULL, NULL, NULL); + BIO_free(in); + if (!inf) { + X509err(X509_F_X509_LOAD_CERT_CRL_FILE, ERR_R_PEM_LIB); + return 0; + } + for (i = 0; i < sk_X509_INFO_num(inf); i++) { + itmp = sk_X509_INFO_value(inf, i); + if (itmp->x509) { + X509_STORE_add_cert(ctx->store_ctx, itmp->x509); + count++; + } + if (itmp->crl) { + X509_STORE_add_crl(ctx->store_ctx, itmp->crl); + count++; + } + } + sk_X509_INFO_pop_free(inf, X509_INFO_free); + return count; +} diff --git a/crypto/x509/t_crl.c b/crypto/x509/t_crl.c new file mode 100644 index 000000000..f3ca6db8e --- /dev/null +++ b/crypto/x509/t_crl.c @@ -0,0 +1,89 @@ +/* + * Copyright 1999-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#include "internal/cryptlib.h" +#include +#include +#include +#include +#include + +#ifndef OPENSSL_NO_STDIO +int X509_CRL_print_fp(FILE *fp, X509_CRL *x) +{ + BIO *b; + int ret; + + if ((b = BIO_new(BIO_s_file())) == NULL) { + X509err(X509_F_X509_CRL_PRINT_FP, ERR_R_BUF_LIB); + return (0); + } + BIO_set_fp(b, fp, BIO_NOCLOSE); + ret = X509_CRL_print(b, x); + BIO_free(b); + return (ret); +} +#endif + +int X509_CRL_print(BIO *out, X509_CRL *x) +{ + STACK_OF(X509_REVOKED) *rev; + X509_REVOKED *r; + const X509_ALGOR *sig_alg; + const ASN1_BIT_STRING *sig; + long l; + int i; + char *p; + + BIO_printf(out, "Certificate Revocation List (CRL):\n"); + l = X509_CRL_get_version(x); + if (l >= 0 && l <= 1) + BIO_printf(out, "%8sVersion %ld (0x%lx)\n", "", l + 1, (unsigned long)l); + else + BIO_printf(out, "%8sVersion unknown (%ld)\n", "", l); + X509_CRL_get0_signature(x, &sig, &sig_alg); + X509_signature_print(out, sig_alg, NULL); + p = X509_NAME_oneline(X509_CRL_get_issuer(x), NULL, 0); + BIO_printf(out, "%8sIssuer: %s\n", "", p); + OPENSSL_free(p); + BIO_printf(out, "%8sLast Update: ", ""); + ASN1_TIME_print(out, X509_CRL_get0_lastUpdate(x)); + BIO_printf(out, "\n%8sNext Update: ", ""); + if (X509_CRL_get0_nextUpdate(x)) + ASN1_TIME_print(out, X509_CRL_get0_nextUpdate(x)); + else + BIO_printf(out, "NONE"); + BIO_printf(out, "\n"); + + X509V3_extensions_print(out, "CRL extensions", + X509_CRL_get0_extensions(x), 0, 8); + + rev = X509_CRL_get_REVOKED(x); + + if (sk_X509_REVOKED_num(rev) > 0) + BIO_printf(out, "Revoked Certificates:\n"); + else + BIO_printf(out, "No Revoked Certificates.\n"); + + for (i = 0; i < sk_X509_REVOKED_num(rev); i++) { + r = sk_X509_REVOKED_value(rev, i); + BIO_printf(out, " Serial Number: "); + i2a_ASN1_INTEGER(out, X509_REVOKED_get0_serialNumber(r)); + BIO_printf(out, "\n Revocation Date: "); + ASN1_TIME_print(out, X509_REVOKED_get0_revocationDate(r)); + BIO_printf(out, "\n"); + X509V3_extensions_print(out, "CRL entry extensions", + X509_REVOKED_get0_extensions(r), 0, 8); + } + X509_signature_print(out, sig_alg, sig); + + return 1; + +} diff --git a/crypto/x509/t_req.c b/crypto/x509/t_req.c new file mode 100644 index 000000000..a0e8b6deb --- /dev/null +++ b/crypto/x509/t_req.c @@ -0,0 +1,202 @@ +/* + * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#include "internal/cryptlib.h" +#include +#include +#include +#include +#include +#ifndef OPENSSL_NO_RSA +# include +#endif +#ifndef OPENSSL_NO_DSA +# include +#endif + +#ifndef OPENSSL_NO_STDIO +int X509_REQ_print_fp(FILE *fp, X509_REQ *x) +{ + BIO *b; + int ret; + + if ((b = BIO_new(BIO_s_file())) == NULL) { + X509err(X509_F_X509_REQ_PRINT_FP, ERR_R_BUF_LIB); + return (0); + } + BIO_set_fp(b, fp, BIO_NOCLOSE); + ret = X509_REQ_print(b, x); + BIO_free(b); + return (ret); +} +#endif + +int X509_REQ_print_ex(BIO *bp, X509_REQ *x, unsigned long nmflags, + unsigned long cflag) +{ + long l; + int i; + EVP_PKEY *pkey; + STACK_OF(X509_EXTENSION) *exts; + char mlch = ' '; + int nmindent = 0; + + if ((nmflags & XN_FLAG_SEP_MASK) == XN_FLAG_SEP_MULTILINE) { + mlch = '\n'; + nmindent = 12; + } + + if (nmflags == X509_FLAG_COMPAT) + nmindent = 16; + + if (!(cflag & X509_FLAG_NO_HEADER)) { + if (BIO_write(bp, "Certificate Request:\n", 21) <= 0) + goto err; + if (BIO_write(bp, " Data:\n", 10) <= 0) + goto err; + } + if (!(cflag & X509_FLAG_NO_VERSION)) { + l = X509_REQ_get_version(x); + if (l >= 0 && l <= 2) { + if (BIO_printf(bp, "%8sVersion: %ld (0x%lx)\n", "", l + 1, (unsigned long)l) <= 0) + goto err; + } else { + if (BIO_printf(bp, "%8sVersion: Unknown (%ld)\n", "", l) <= 0) + goto err; + } + } + if (!(cflag & X509_FLAG_NO_SUBJECT)) { + if (BIO_printf(bp, " Subject:%c", mlch) <= 0) + goto err; + if (X509_NAME_print_ex(bp, X509_REQ_get_subject_name(x), + nmindent, nmflags) < 0) + goto err; + if (BIO_write(bp, "\n", 1) <= 0) + goto err; + } + if (!(cflag & X509_FLAG_NO_PUBKEY)) { + X509_PUBKEY *xpkey; + ASN1_OBJECT *koid; + if (BIO_write(bp, " Subject Public Key Info:\n", 33) <= 0) + goto err; + if (BIO_printf(bp, "%12sPublic Key Algorithm: ", "") <= 0) + goto err; + xpkey = X509_REQ_get_X509_PUBKEY(x); + X509_PUBKEY_get0_param(&koid, NULL, NULL, NULL, xpkey); + if (i2a_ASN1_OBJECT(bp, koid) <= 0) + goto err; + if (BIO_puts(bp, "\n") <= 0) + goto err; + + pkey = X509_REQ_get0_pubkey(x); + if (pkey == NULL) { + BIO_printf(bp, "%12sUnable to load Public Key\n", ""); + ERR_print_errors(bp); + } else { + EVP_PKEY_print_public(bp, pkey, 16, NULL); + } + } + + if (!(cflag & X509_FLAG_NO_ATTRIBUTES)) { + /* may not be */ + if (BIO_printf(bp, "%8sAttributes:\n", "") <= 0) + goto err; + + if (X509_REQ_get_attr_count(x) == 0) { + if (BIO_printf(bp, "%12sa0:00\n", "") <= 0) + goto err; + } else { + for (i = 0; i < X509_REQ_get_attr_count(x); i++) { + ASN1_TYPE *at; + X509_ATTRIBUTE *a; + ASN1_BIT_STRING *bs = NULL; + ASN1_OBJECT *aobj; + int j, type = 0, count = 1, ii = 0; + + a = X509_REQ_get_attr(x, i); + aobj = X509_ATTRIBUTE_get0_object(a); + if (X509_REQ_extension_nid(OBJ_obj2nid(aobj))) + continue; + if (BIO_printf(bp, "%12s", "") <= 0) + goto err; + if ((j = i2a_ASN1_OBJECT(bp, aobj)) > 0) { + ii = 0; + count = X509_ATTRIBUTE_count(a); + get_next: + at = X509_ATTRIBUTE_get0_type(a, ii); + type = at->type; + bs = at->value.asn1_string; + } + for (j = 25 - j; j > 0; j--) + if (BIO_write(bp, " ", 1) != 1) + goto err; + if (BIO_puts(bp, ":") <= 0) + goto err; + if ((type == V_ASN1_PRINTABLESTRING) || + (type == V_ASN1_T61STRING) || + (type == V_ASN1_UTF8STRING) || + (type == V_ASN1_IA5STRING)) { + if (BIO_write(bp, (char *)bs->data, bs->length) + != bs->length) + goto err; + BIO_puts(bp, "\n"); + } else { + BIO_puts(bp, "unable to print attribute\n"); + } + if (++ii < count) + goto get_next; + } + } + } + if (!(cflag & X509_FLAG_NO_EXTENSIONS)) { + exts = X509_REQ_get_extensions(x); + if (exts) { + BIO_printf(bp, "%8sRequested Extensions:\n", ""); + for (i = 0; i < sk_X509_EXTENSION_num(exts); i++) { + ASN1_OBJECT *obj; + X509_EXTENSION *ex; + int critical; + ex = sk_X509_EXTENSION_value(exts, i); + if (BIO_printf(bp, "%12s", "") <= 0) + goto err; + obj = X509_EXTENSION_get_object(ex); + i2a_ASN1_OBJECT(bp, obj); + critical = X509_EXTENSION_get_critical(ex); + if (BIO_printf(bp, ": %s\n", critical ? "critical" : "") <= 0) + goto err; + if (!X509V3_EXT_print(bp, ex, cflag, 16)) { + BIO_printf(bp, "%16s", ""); + ASN1_STRING_print(bp, X509_EXTENSION_get_data(ex)); + } + if (BIO_write(bp, "\n", 1) <= 0) + goto err; + } + sk_X509_EXTENSION_pop_free(exts, X509_EXTENSION_free); + } + } + + if (!(cflag & X509_FLAG_NO_SIGDUMP)) { + const X509_ALGOR *sig_alg; + const ASN1_BIT_STRING *sig; + X509_REQ_get0_signature(x, &sig, &sig_alg); + if (!X509_signature_print(bp, sig_alg, sig)) + goto err; + } + + return (1); + err: + X509err(X509_F_X509_REQ_PRINT_EX, ERR_R_BUF_LIB); + return (0); +} + +int X509_REQ_print(BIO *bp, X509_REQ *x) +{ + return X509_REQ_print_ex(bp, x, XN_FLAG_COMPAT, X509_FLAG_COMPAT); +} diff --git a/crypto/x509/t_x509.c b/crypto/x509/t_x509.c new file mode 100644 index 000000000..c5fbad29a --- /dev/null +++ b/crypto/x509/t_x509.c @@ -0,0 +1,381 @@ +/* + * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#include "internal/cryptlib.h" +#include +#include +#include +#include +#include +#include "internal/asn1_int.h" + +#ifndef OPENSSL_NO_STDIO +int X509_print_fp(FILE *fp, X509 *x) +{ + return X509_print_ex_fp(fp, x, XN_FLAG_COMPAT, X509_FLAG_COMPAT); +} + +int X509_print_ex_fp(FILE *fp, X509 *x, unsigned long nmflag, + unsigned long cflag) +{ + BIO *b; + int ret; + + if ((b = BIO_new(BIO_s_file())) == NULL) { + X509err(X509_F_X509_PRINT_EX_FP, ERR_R_BUF_LIB); + return (0); + } + BIO_set_fp(b, fp, BIO_NOCLOSE); + ret = X509_print_ex(b, x, nmflag, cflag); + BIO_free(b); + return (ret); +} +#endif + +int X509_print(BIO *bp, X509 *x) +{ + return X509_print_ex(bp, x, XN_FLAG_COMPAT, X509_FLAG_COMPAT); +} + +int X509_print_ex(BIO *bp, X509 *x, unsigned long nmflags, + unsigned long cflag) +{ + long l; + int ret = 0, i; + char *m = NULL, mlch = ' '; + int nmindent = 0; + ASN1_INTEGER *bs; + EVP_PKEY *pkey = NULL; + const char *neg; + + if ((nmflags & XN_FLAG_SEP_MASK) == XN_FLAG_SEP_MULTILINE) { + mlch = '\n'; + nmindent = 12; + } + + if (nmflags == X509_FLAG_COMPAT) + nmindent = 16; + + if (!(cflag & X509_FLAG_NO_HEADER)) { + if (BIO_write(bp, "Certificate:\n", 13) <= 0) + goto err; + if (BIO_write(bp, " Data:\n", 10) <= 0) + goto err; + } + if (!(cflag & X509_FLAG_NO_VERSION)) { + l = X509_get_version(x); + if (l >= 0 && l <= 2) { + if (BIO_printf(bp, "%8sVersion: %ld (0x%lx)\n", "", l + 1, (unsigned long)l) <= 0) + goto err; + } else { + if (BIO_printf(bp, "%8sVersion: Unknown (%ld)\n", "", l) <= 0) + goto err; + } + } + if (!(cflag & X509_FLAG_NO_SERIAL)) { + + if (BIO_write(bp, " Serial Number:", 22) <= 0) + goto err; + + bs = X509_get_serialNumber(x); + if (bs->length <= (int)sizeof(long)) { + ERR_set_mark(); + l = ASN1_INTEGER_get(bs); + ERR_pop_to_mark(); + } else { + l = -1; + } + if (l != -1) { + unsigned long ul; + if (bs->type == V_ASN1_NEG_INTEGER) { + ul = 0 - (unsigned long)l; + neg = "-"; + } else { + ul = l; + neg = ""; + } + if (BIO_printf(bp, " %s%lu (%s0x%lx)\n", neg, ul, neg, ul) <= 0) + goto err; + } else { + neg = (bs->type == V_ASN1_NEG_INTEGER) ? " (Negative)" : ""; + if (BIO_printf(bp, "\n%12s%s", "", neg) <= 0) + goto err; + + for (i = 0; i < bs->length; i++) { + if (BIO_printf(bp, "%02x%c", bs->data[i], + ((i + 1 == bs->length) ? '\n' : ':')) <= 0) + goto err; + } + } + + } + + if (!(cflag & X509_FLAG_NO_SIGNAME)) { + const X509_ALGOR *tsig_alg = X509_get0_tbs_sigalg(x); + if (X509_signature_print(bp, tsig_alg, NULL) <= 0) + goto err; + } + + if (!(cflag & X509_FLAG_NO_ISSUER)) { + if (BIO_printf(bp, " Issuer:%c", mlch) <= 0) + goto err; + if (X509_NAME_print_ex(bp, X509_get_issuer_name(x), nmindent, nmflags) + < 0) + goto err; + if (BIO_write(bp, "\n", 1) <= 0) + goto err; + } + if (!(cflag & X509_FLAG_NO_VALIDITY)) { + if (BIO_write(bp, " Validity\n", 17) <= 0) + goto err; + if (BIO_write(bp, " Not Before: ", 24) <= 0) + goto err; + if (!ASN1_TIME_print(bp, X509_get0_notBefore(x))) + goto err; + if (BIO_write(bp, "\n Not After : ", 25) <= 0) + goto err; + if (!ASN1_TIME_print(bp, X509_get0_notAfter(x))) + goto err; + if (BIO_write(bp, "\n", 1) <= 0) + goto err; + } + if (!(cflag & X509_FLAG_NO_SUBJECT)) { + if (BIO_printf(bp, " Subject:%c", mlch) <= 0) + goto err; + if (X509_NAME_print_ex + (bp, X509_get_subject_name(x), nmindent, nmflags) < 0) + goto err; + if (BIO_write(bp, "\n", 1) <= 0) + goto err; + } + if (!(cflag & X509_FLAG_NO_PUBKEY)) { + X509_PUBKEY *xpkey = X509_get_X509_PUBKEY(x); + ASN1_OBJECT *xpoid; + X509_PUBKEY_get0_param(&xpoid, NULL, NULL, NULL, xpkey); + if (BIO_write(bp, " Subject Public Key Info:\n", 33) <= 0) + goto err; + if (BIO_printf(bp, "%12sPublic Key Algorithm: ", "") <= 0) + goto err; + if (i2a_ASN1_OBJECT(bp, xpoid) <= 0) + goto err; + if (BIO_puts(bp, "\n") <= 0) + goto err; + + pkey = X509_get0_pubkey(x); + if (pkey == NULL) { + BIO_printf(bp, "%12sUnable to load Public Key\n", ""); + ERR_print_errors(bp); + } else { + EVP_PKEY_print_public(bp, pkey, 16, NULL); + } + } + + if (!(cflag & X509_FLAG_NO_IDS)) { + const ASN1_BIT_STRING *iuid, *suid; + X509_get0_uids(x, &iuid, &suid); + if (iuid != NULL) { + if (BIO_printf(bp, "%8sIssuer Unique ID: ", "") <= 0) + goto err; + if (!X509_signature_dump(bp, iuid, 12)) + goto err; + } + if (suid != NULL) { + if (BIO_printf(bp, "%8sSubject Unique ID: ", "") <= 0) + goto err; + if (!X509_signature_dump(bp, suid, 12)) + goto err; + } + } + + if (!(cflag & X509_FLAG_NO_EXTENSIONS)) + X509V3_extensions_print(bp, "X509v3 extensions", + X509_get0_extensions(x), cflag, 8); + + if (!(cflag & X509_FLAG_NO_SIGDUMP)) { + const X509_ALGOR *sig_alg; + const ASN1_BIT_STRING *sig; + X509_get0_signature(&sig, &sig_alg, x); + if (X509_signature_print(bp, sig_alg, sig) <= 0) + goto err; + } + if (!(cflag & X509_FLAG_NO_AUX)) { + if (!X509_aux_print(bp, x, 0)) + goto err; + } + ret = 1; + err: + OPENSSL_free(m); + return (ret); +} + +int X509_ocspid_print(BIO *bp, X509 *x) +{ +#ifndef OPENSSL_NO_SHA + unsigned char *der = NULL; + unsigned char *dertmp; + int derlen; + int i; + unsigned char SHA1md[SHA_DIGEST_LENGTH]; + ASN1_BIT_STRING *keybstr; + X509_NAME *subj; + + /* + * display the hash of the subject as it would appear in OCSP requests + */ + if (BIO_printf(bp, " Subject OCSP hash: ") <= 0) + goto err; + subj = X509_get_subject_name(x); + derlen = i2d_X509_NAME(subj, NULL); + if ((der = dertmp = OPENSSL_malloc(derlen)) == NULL) + goto err; + i2d_X509_NAME(subj, &dertmp); + + if (!EVP_Digest(der, derlen, SHA1md, NULL, EVP_sha1(), NULL)) + goto err; + for (i = 0; i < SHA_DIGEST_LENGTH; i++) { + if (BIO_printf(bp, "%02X", SHA1md[i]) <= 0) + goto err; + } + OPENSSL_free(der); + der = NULL; + + /* + * display the hash of the public key as it would appear in OCSP requests + */ + if (BIO_printf(bp, "\n Public key OCSP hash: ") <= 0) + goto err; + + keybstr = X509_get0_pubkey_bitstr(x); + + if (keybstr == NULL) + goto err; + + if (!EVP_Digest(ASN1_STRING_get0_data(keybstr), + ASN1_STRING_length(keybstr), SHA1md, NULL, EVP_sha1(), + NULL)) + goto err; + for (i = 0; i < SHA_DIGEST_LENGTH; i++) { + if (BIO_printf(bp, "%02X", SHA1md[i]) <= 0) + goto err; + } + BIO_printf(bp, "\n"); + + return (1); + err: + OPENSSL_free(der); + return (0); +#else + BIO_printf(bp, "(SHA1 disabled)\n"); + return (1); +#endif +} + +int X509_signature_dump(BIO *bp, const ASN1_STRING *sig, int indent) +{ + const unsigned char *s; + int i, n; + + n = sig->length; + s = sig->data; + for (i = 0; i < n; i++) { + if ((i % 18) == 0) { + if (BIO_write(bp, "\n", 1) <= 0) + return 0; + if (BIO_indent(bp, indent, indent) <= 0) + return 0; + } + if (BIO_printf(bp, "%02x%s", s[i], ((i + 1) == n) ? "" : ":") <= 0) + return 0; + } + if (BIO_write(bp, "\n", 1) != 1) + return 0; + + return 1; +} + +int X509_signature_print(BIO *bp, const X509_ALGOR *sigalg, + const ASN1_STRING *sig) +{ + int sig_nid; + if (BIO_puts(bp, " Signature Algorithm: ") <= 0) + return 0; + if (i2a_ASN1_OBJECT(bp, sigalg->algorithm) <= 0) + return 0; + + sig_nid = OBJ_obj2nid(sigalg->algorithm); + if (sig_nid != NID_undef) { + int pkey_nid, dig_nid; + const EVP_PKEY_ASN1_METHOD *ameth; + if (OBJ_find_sigid_algs(sig_nid, &dig_nid, &pkey_nid)) { + ameth = EVP_PKEY_asn1_find(NULL, pkey_nid); + if (ameth && ameth->sig_print) + return ameth->sig_print(bp, sigalg, sig, 9, 0); + } + } + if (sig) + return X509_signature_dump(bp, sig, 9); + else if (BIO_puts(bp, "\n") <= 0) + return 0; + return 1; +} + +int X509_aux_print(BIO *out, X509 *x, int indent) +{ + char oidstr[80], first; + STACK_OF(ASN1_OBJECT) *trust, *reject; + const unsigned char *alias, *keyid; + int keyidlen; + int i; + if (X509_trusted(x) == 0) + return 1; + trust = X509_get0_trust_objects(x); + reject = X509_get0_reject_objects(x); + if (trust) { + first = 1; + BIO_printf(out, "%*sTrusted Uses:\n%*s", indent, "", indent + 2, ""); + for (i = 0; i < sk_ASN1_OBJECT_num(trust); i++) { + if (!first) + BIO_puts(out, ", "); + else + first = 0; + OBJ_obj2txt(oidstr, sizeof oidstr, + sk_ASN1_OBJECT_value(trust, i), 0); + BIO_puts(out, oidstr); + } + BIO_puts(out, "\n"); + } else + BIO_printf(out, "%*sNo Trusted Uses.\n", indent, ""); + if (reject) { + first = 1; + BIO_printf(out, "%*sRejected Uses:\n%*s", indent, "", indent + 2, ""); + for (i = 0; i < sk_ASN1_OBJECT_num(reject); i++) { + if (!first) + BIO_puts(out, ", "); + else + first = 0; + OBJ_obj2txt(oidstr, sizeof oidstr, + sk_ASN1_OBJECT_value(reject, i), 0); + BIO_puts(out, oidstr); + } + BIO_puts(out, "\n"); + } else + BIO_printf(out, "%*sNo Rejected Uses.\n", indent, ""); + alias = X509_alias_get0(x, NULL); + if (alias) + BIO_printf(out, "%*sAlias: %s\n", indent, "", alias); + keyid = X509_keyid_get0(x, &keyidlen); + if (keyid) { + BIO_printf(out, "%*sKey Id: ", indent, ""); + for (i = 0; i < keyidlen; i++) + BIO_printf(out, "%s%02X", i ? ":" : "", keyid[i]); + BIO_write(out, "\n", 1); + } + return 1; +} diff --git a/crypto/x509/x509_att.c b/crypto/x509/x509_att.c new file mode 100644 index 000000000..15f0e4fc8 --- /dev/null +++ b/crypto/x509/x509_att.c @@ -0,0 +1,329 @@ +/* + * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#include +#include "internal/cryptlib.h" +#include +#include +#include +#include +#include +#include "x509_lcl.h" + +int X509at_get_attr_count(const STACK_OF(X509_ATTRIBUTE) *x) +{ + return sk_X509_ATTRIBUTE_num(x); +} + +int X509at_get_attr_by_NID(const STACK_OF(X509_ATTRIBUTE) *x, int nid, + int lastpos) +{ + const ASN1_OBJECT *obj = OBJ_nid2obj(nid); + + if (obj == NULL) + return (-2); + return (X509at_get_attr_by_OBJ(x, obj, lastpos)); +} + +int X509at_get_attr_by_OBJ(const STACK_OF(X509_ATTRIBUTE) *sk, + const ASN1_OBJECT *obj, int lastpos) +{ + int n; + X509_ATTRIBUTE *ex; + + if (sk == NULL) + return (-1); + lastpos++; + if (lastpos < 0) + lastpos = 0; + n = sk_X509_ATTRIBUTE_num(sk); + for (; lastpos < n; lastpos++) { + ex = sk_X509_ATTRIBUTE_value(sk, lastpos); + if (OBJ_cmp(ex->object, obj) == 0) + return (lastpos); + } + return (-1); +} + +X509_ATTRIBUTE *X509at_get_attr(const STACK_OF(X509_ATTRIBUTE) *x, int loc) +{ + if (x == NULL || sk_X509_ATTRIBUTE_num(x) <= loc || loc < 0) + return NULL; + else + return sk_X509_ATTRIBUTE_value(x, loc); +} + +X509_ATTRIBUTE *X509at_delete_attr(STACK_OF(X509_ATTRIBUTE) *x, int loc) +{ + X509_ATTRIBUTE *ret; + + if (x == NULL || sk_X509_ATTRIBUTE_num(x) <= loc || loc < 0) + return (NULL); + ret = sk_X509_ATTRIBUTE_delete(x, loc); + return (ret); +} + +STACK_OF(X509_ATTRIBUTE) *X509at_add1_attr(STACK_OF(X509_ATTRIBUTE) **x, + X509_ATTRIBUTE *attr) +{ + X509_ATTRIBUTE *new_attr = NULL; + STACK_OF(X509_ATTRIBUTE) *sk = NULL; + + if (x == NULL) { + X509err(X509_F_X509AT_ADD1_ATTR, ERR_R_PASSED_NULL_PARAMETER); + goto err2; + } + + if (*x == NULL) { + if ((sk = sk_X509_ATTRIBUTE_new_null()) == NULL) + goto err; + } else + sk = *x; + + if ((new_attr = X509_ATTRIBUTE_dup(attr)) == NULL) + goto err2; + if (!sk_X509_ATTRIBUTE_push(sk, new_attr)) + goto err; + if (*x == NULL) + *x = sk; + return (sk); + err: + X509err(X509_F_X509AT_ADD1_ATTR, ERR_R_MALLOC_FAILURE); + err2: + X509_ATTRIBUTE_free(new_attr); + sk_X509_ATTRIBUTE_free(sk); + return (NULL); +} + +STACK_OF(X509_ATTRIBUTE) *X509at_add1_attr_by_OBJ(STACK_OF(X509_ATTRIBUTE) + **x, const ASN1_OBJECT *obj, + int type, + const unsigned char *bytes, + int len) +{ + X509_ATTRIBUTE *attr; + STACK_OF(X509_ATTRIBUTE) *ret; + attr = X509_ATTRIBUTE_create_by_OBJ(NULL, obj, type, bytes, len); + if (!attr) + return 0; + ret = X509at_add1_attr(x, attr); + X509_ATTRIBUTE_free(attr); + return ret; +} + +STACK_OF(X509_ATTRIBUTE) *X509at_add1_attr_by_NID(STACK_OF(X509_ATTRIBUTE) + **x, int nid, int type, + const unsigned char *bytes, + int len) +{ + X509_ATTRIBUTE *attr; + STACK_OF(X509_ATTRIBUTE) *ret; + attr = X509_ATTRIBUTE_create_by_NID(NULL, nid, type, bytes, len); + if (!attr) + return 0; + ret = X509at_add1_attr(x, attr); + X509_ATTRIBUTE_free(attr); + return ret; +} + +STACK_OF(X509_ATTRIBUTE) *X509at_add1_attr_by_txt(STACK_OF(X509_ATTRIBUTE) + **x, const char *attrname, + int type, + const unsigned char *bytes, + int len) +{ + X509_ATTRIBUTE *attr; + STACK_OF(X509_ATTRIBUTE) *ret; + attr = X509_ATTRIBUTE_create_by_txt(NULL, attrname, type, bytes, len); + if (!attr) + return 0; + ret = X509at_add1_attr(x, attr); + X509_ATTRIBUTE_free(attr); + return ret; +} + +void *X509at_get0_data_by_OBJ(STACK_OF(X509_ATTRIBUTE) *x, + const ASN1_OBJECT *obj, int lastpos, int type) +{ + int i; + X509_ATTRIBUTE *at; + i = X509at_get_attr_by_OBJ(x, obj, lastpos); + if (i == -1) + return NULL; + if ((lastpos <= -2) && (X509at_get_attr_by_OBJ(x, obj, i) != -1)) + return NULL; + at = X509at_get_attr(x, i); + if (lastpos <= -3 && (X509_ATTRIBUTE_count(at) != 1)) + return NULL; + return X509_ATTRIBUTE_get0_data(at, 0, type, NULL); +} + +X509_ATTRIBUTE *X509_ATTRIBUTE_create_by_NID(X509_ATTRIBUTE **attr, int nid, + int atrtype, const void *data, + int len) +{ + ASN1_OBJECT *obj; + X509_ATTRIBUTE *ret; + + obj = OBJ_nid2obj(nid); + if (obj == NULL) { + X509err(X509_F_X509_ATTRIBUTE_CREATE_BY_NID, X509_R_UNKNOWN_NID); + return (NULL); + } + ret = X509_ATTRIBUTE_create_by_OBJ(attr, obj, atrtype, data, len); + if (ret == NULL) + ASN1_OBJECT_free(obj); + return (ret); +} + +X509_ATTRIBUTE *X509_ATTRIBUTE_create_by_OBJ(X509_ATTRIBUTE **attr, + const ASN1_OBJECT *obj, + int atrtype, const void *data, + int len) +{ + X509_ATTRIBUTE *ret; + + if ((attr == NULL) || (*attr == NULL)) { + if ((ret = X509_ATTRIBUTE_new()) == NULL) { + X509err(X509_F_X509_ATTRIBUTE_CREATE_BY_OBJ, + ERR_R_MALLOC_FAILURE); + return (NULL); + } + } else + ret = *attr; + + if (!X509_ATTRIBUTE_set1_object(ret, obj)) + goto err; + if (!X509_ATTRIBUTE_set1_data(ret, atrtype, data, len)) + goto err; + + if ((attr != NULL) && (*attr == NULL)) + *attr = ret; + return (ret); + err: + if ((attr == NULL) || (ret != *attr)) + X509_ATTRIBUTE_free(ret); + return (NULL); +} + +X509_ATTRIBUTE *X509_ATTRIBUTE_create_by_txt(X509_ATTRIBUTE **attr, + const char *atrname, int type, + const unsigned char *bytes, + int len) +{ + ASN1_OBJECT *obj; + X509_ATTRIBUTE *nattr; + + obj = OBJ_txt2obj(atrname, 0); + if (obj == NULL) { + X509err(X509_F_X509_ATTRIBUTE_CREATE_BY_TXT, + X509_R_INVALID_FIELD_NAME); + ERR_add_error_data(2, "name=", atrname); + return (NULL); + } + nattr = X509_ATTRIBUTE_create_by_OBJ(attr, obj, type, bytes, len); + ASN1_OBJECT_free(obj); + return nattr; +} + +int X509_ATTRIBUTE_set1_object(X509_ATTRIBUTE *attr, const ASN1_OBJECT *obj) +{ + if ((attr == NULL) || (obj == NULL)) + return (0); + ASN1_OBJECT_free(attr->object); + attr->object = OBJ_dup(obj); + return attr->object != NULL; +} + +int X509_ATTRIBUTE_set1_data(X509_ATTRIBUTE *attr, int attrtype, + const void *data, int len) +{ + ASN1_TYPE *ttmp = NULL; + ASN1_STRING *stmp = NULL; + int atype = 0; + if (!attr) + return 0; + if (attrtype & MBSTRING_FLAG) { + stmp = ASN1_STRING_set_by_NID(NULL, data, len, attrtype, + OBJ_obj2nid(attr->object)); + if (!stmp) { + X509err(X509_F_X509_ATTRIBUTE_SET1_DATA, ERR_R_ASN1_LIB); + return 0; + } + atype = stmp->type; + } else if (len != -1) { + if ((stmp = ASN1_STRING_type_new(attrtype)) == NULL) + goto err; + if (!ASN1_STRING_set(stmp, data, len)) + goto err; + atype = attrtype; + } + /* + * This is a bit naughty because the attribute should really have at + * least one value but some types use and zero length SET and require + * this. + */ + if (attrtype == 0) { + ASN1_STRING_free(stmp); + return 1; + } + if ((ttmp = ASN1_TYPE_new()) == NULL) + goto err; + if ((len == -1) && !(attrtype & MBSTRING_FLAG)) { + if (!ASN1_TYPE_set1(ttmp, attrtype, data)) + goto err; + } else { + ASN1_TYPE_set(ttmp, atype, stmp); + stmp = NULL; + } + if (!sk_ASN1_TYPE_push(attr->set, ttmp)) + goto err; + return 1; + err: + X509err(X509_F_X509_ATTRIBUTE_SET1_DATA, ERR_R_MALLOC_FAILURE); + ASN1_TYPE_free(ttmp); + ASN1_STRING_free(stmp); + return 0; +} + +int X509_ATTRIBUTE_count(const X509_ATTRIBUTE *attr) +{ + if (attr == NULL) + return 0; + return sk_ASN1_TYPE_num(attr->set); +} + +ASN1_OBJECT *X509_ATTRIBUTE_get0_object(X509_ATTRIBUTE *attr) +{ + if (attr == NULL) + return (NULL); + return (attr->object); +} + +void *X509_ATTRIBUTE_get0_data(X509_ATTRIBUTE *attr, int idx, + int atrtype, void *data) +{ + ASN1_TYPE *ttmp; + ttmp = X509_ATTRIBUTE_get0_type(attr, idx); + if (!ttmp) + return NULL; + if (atrtype != ASN1_TYPE_get(ttmp)) { + X509err(X509_F_X509_ATTRIBUTE_GET0_DATA, X509_R_WRONG_TYPE); + return NULL; + } + return ttmp->value.ptr; +} + +ASN1_TYPE *X509_ATTRIBUTE_get0_type(X509_ATTRIBUTE *attr, int idx) +{ + if (attr == NULL) + return NULL; + return sk_ASN1_TYPE_value(attr->set, idx); +} diff --git a/crypto/x509/x509_cmp.c b/crypto/x509/x509_cmp.c new file mode 100644 index 000000000..651e3a7d6 --- /dev/null +++ b/crypto/x509/x509_cmp.c @@ -0,0 +1,452 @@ +/* + * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#include +#include "internal/cryptlib.h" +#include +#include +#include +#include +#include "internal/x509_int.h" + +int X509_issuer_and_serial_cmp(const X509 *a, const X509 *b) +{ + int i; + const X509_CINF *ai, *bi; + + ai = &a->cert_info; + bi = &b->cert_info; + i = ASN1_INTEGER_cmp(&ai->serialNumber, &bi->serialNumber); + if (i) + return (i); + return (X509_NAME_cmp(ai->issuer, bi->issuer)); +} + +unsigned long X509_issuer_and_serial_hash(X509 *a) +{ + unsigned long ret = 0; + EVP_MD_CTX *ctx = EVP_MD_CTX_new(); + unsigned char md[EVP_MAX_MD_SIZE]; + char *f; + + if (ctx == NULL) + goto err; + f = X509_NAME_oneline(a->cert_info.issuer, NULL, 0); + if (!EVP_DigestInit_ex(ctx, EVP_get_default_digest(), NULL)) + goto err; + if (!EVP_DigestUpdate(ctx, (unsigned char *)f, strlen(f))) + goto err; + OPENSSL_free(f); + if (!EVP_DigestUpdate + (ctx, (unsigned char *)a->cert_info.serialNumber.data, + (unsigned long)a->cert_info.serialNumber.length)) + goto err; + if (!EVP_DigestFinal_ex(ctx, &(md[0]), NULL)) + goto err; + ret = (((unsigned long)md[0]) | ((unsigned long)md[1] << 8L) | + ((unsigned long)md[2] << 16L) | ((unsigned long)md[3] << 24L) + ) & 0xffffffffL; + err: + EVP_MD_CTX_free(ctx); + return (ret); +} + +int X509_issuer_name_cmp(const X509 *a, const X509 *b) +{ + return (X509_NAME_cmp(a->cert_info.issuer, b->cert_info.issuer)); +} + +int X509_subject_name_cmp(const X509 *a, const X509 *b) +{ + return (X509_NAME_cmp(a->cert_info.subject, b->cert_info.subject)); +} + +int X509_CRL_cmp(const X509_CRL *a, const X509_CRL *b) +{ + return (X509_NAME_cmp(a->crl.issuer, b->crl.issuer)); +} + +int X509_CRL_match(const X509_CRL *a, const X509_CRL *b) +{ + return memcmp(a->sha1_hash, b->sha1_hash, sizeof(a->sha1_hash)); +} + +X509_NAME *X509_get_issuer_name(const X509 *a) +{ + return (a->cert_info.issuer); +} + +unsigned long X509_issuer_name_hash(X509 *x) +{ + return (X509_NAME_hash(x->cert_info.issuer)); +} + +unsigned long X509_issuer_name_hash_old(X509 *x) +{ + return (X509_NAME_hash_old(x->cert_info.issuer)); +} + +X509_NAME *X509_get_subject_name(const X509 *a) +{ + return (a->cert_info.subject); +} + +ASN1_INTEGER *X509_get_serialNumber(X509 *a) +{ + return &a->cert_info.serialNumber; +} + +const ASN1_INTEGER *X509_get0_serialNumber(const X509 *a) +{ + return &a->cert_info.serialNumber; +} + +unsigned long X509_subject_name_hash(X509 *x) +{ + return (X509_NAME_hash(x->cert_info.subject)); +} + +unsigned long X509_subject_name_hash_old(X509 *x) +{ + return (X509_NAME_hash_old(x->cert_info.subject)); +} + +/* + * Compare two certificates: they must be identical for this to work. NB: + * Although "cmp" operations are generally prototyped to take "const" + * arguments (eg. for use in STACKs), the way X509 handling is - these + * operations may involve ensuring the hashes are up-to-date and ensuring + * certain cert information is cached. So this is the point where the + * "depth-first" constification tree has to halt with an evil cast. + */ +int X509_cmp(const X509 *a, const X509 *b) +{ + int rv; + /* ensure hash is valid */ + X509_check_purpose((X509 *)a, -1, 0); + X509_check_purpose((X509 *)b, -1, 0); + + rv = memcmp(a->sha1_hash, b->sha1_hash, sizeof(a->sha1_hash)); + if (rv) + return rv; + /* Check for match against stored encoding too */ + if (!a->cert_info.enc.modified && !b->cert_info.enc.modified) { + if (a->cert_info.enc.len < b->cert_info.enc.len) + return -1; + if (a->cert_info.enc.len > b->cert_info.enc.len) + return 1; + return memcmp(a->cert_info.enc.enc, b->cert_info.enc.enc, + a->cert_info.enc.len); + } + return rv; +} + +int X509_NAME_cmp(const X509_NAME *a, const X509_NAME *b) +{ + int ret; + + /* Ensure canonical encoding is present and up to date */ + + if (!a->canon_enc || a->modified) { + ret = i2d_X509_NAME((X509_NAME *)a, NULL); + if (ret < 0) + return -2; + } + + if (!b->canon_enc || b->modified) { + ret = i2d_X509_NAME((X509_NAME *)b, NULL); + if (ret < 0) + return -2; + } + + ret = a->canon_enclen - b->canon_enclen; + + if (ret) + return ret; + + return memcmp(a->canon_enc, b->canon_enc, a->canon_enclen); + +} + +unsigned long X509_NAME_hash(X509_NAME *x) +{ + unsigned long ret = 0; + unsigned char md[EVP_MAX_MD_SIZE]; + + /* Make sure X509_NAME structure contains valid cached encoding */ + i2d_X509_NAME(x, NULL); + if (!EVP_Digest(x->canon_enc, x->canon_enclen, md, NULL, + EVP_get_default_digest(), NULL)) + return 0; + + ret = (((unsigned long)md[0]) | ((unsigned long)md[1] << 8L) | + ((unsigned long)md[2] << 16L) | ((unsigned long)md[3] << 24L) + ) & 0xffffffffL; + + return (ret); +} + +/* + * I now DER encode the name and hash it. Since I cache the DER encoding, + * this is reasonably efficient. + */ + +unsigned long X509_NAME_hash_old(X509_NAME *x) +{ + EVP_MD_CTX *md_ctx = EVP_MD_CTX_new(); + unsigned long ret = 0; + unsigned char md[EVP_MAX_MD_SIZE]; + + if (md_ctx == NULL) + return ret; + + /* Make sure X509_NAME structure contains valid cached encoding */ + i2d_X509_NAME(x, NULL); + EVP_MD_CTX_set_flags(md_ctx, EVP_MD_CTX_FLAG_NON_FIPS_ALLOW); + if (EVP_DigestInit_ex(md_ctx, EVP_get_default_digest(), NULL) + && EVP_DigestUpdate(md_ctx, x->bytes->data, x->bytes->length) + && EVP_DigestFinal_ex(md_ctx, md, NULL)) + ret = (((unsigned long)md[0]) | ((unsigned long)md[1] << 8L) | + ((unsigned long)md[2] << 16L) | ((unsigned long)md[3] << 24L) + ) & 0xffffffffL; + EVP_MD_CTX_free(md_ctx); + + return (ret); +} + +/* Search a stack of X509 for a match */ +X509 *X509_find_by_issuer_and_serial(STACK_OF(X509) *sk, X509_NAME *name, + ASN1_INTEGER *serial) +{ + int i; + X509 x, *x509 = NULL; + + if (!sk) + return NULL; + + x.cert_info.serialNumber = *serial; + x.cert_info.issuer = name; + + for (i = 0; i < sk_X509_num(sk); i++) { + x509 = sk_X509_value(sk, i); + if (X509_issuer_and_serial_cmp(x509, &x) == 0) + return (x509); + } + return (NULL); +} + +X509 *X509_find_by_subject(STACK_OF(X509) *sk, X509_NAME *name) +{ + X509 *x509; + int i; + + for (i = 0; i < sk_X509_num(sk); i++) { + x509 = sk_X509_value(sk, i); + if (X509_NAME_cmp(X509_get_subject_name(x509), name) == 0) + return (x509); + } + return (NULL); +} + +EVP_PKEY *X509_get0_pubkey(const X509 *x) +{ + if (x == NULL) + return NULL; + return X509_PUBKEY_get0(x->cert_info.key); +} + +EVP_PKEY *X509_get_pubkey(X509 *x) +{ + if (x == NULL) + return NULL; + return X509_PUBKEY_get(x->cert_info.key); +} + +int X509_check_private_key(const X509 *x, const EVP_PKEY *k) +{ + const EVP_PKEY *xk; + int ret; + + xk = X509_get0_pubkey(x); + + if (xk) + ret = EVP_PKEY_cmp(xk, k); + else + ret = -2; + + switch (ret) { + case 1: + break; + case 0: + X509err(X509_F_X509_CHECK_PRIVATE_KEY, X509_R_KEY_VALUES_MISMATCH); + break; + case -1: + X509err(X509_F_X509_CHECK_PRIVATE_KEY, X509_R_KEY_TYPE_MISMATCH); + break; + case -2: + X509err(X509_F_X509_CHECK_PRIVATE_KEY, X509_R_UNKNOWN_KEY_TYPE); + } + if (ret > 0) + return 1; + return 0; +} + +/* + * Check a suite B algorithm is permitted: pass in a public key and the NID + * of its signature (or 0 if no signature). The pflags is a pointer to a + * flags field which must contain the suite B verification flags. + */ + +#ifndef OPENSSL_NO_EC + +static int check_suite_b(EVP_PKEY *pkey, int sign_nid, unsigned long *pflags) +{ + const EC_GROUP *grp = NULL; + int curve_nid; + if (pkey && EVP_PKEY_id(pkey) == EVP_PKEY_EC) + grp = EC_KEY_get0_group(EVP_PKEY_get0_EC_KEY(pkey)); + if (!grp) + return X509_V_ERR_SUITE_B_INVALID_ALGORITHM; + curve_nid = EC_GROUP_get_curve_name(grp); + /* Check curve is consistent with LOS */ + if (curve_nid == NID_secp384r1) { /* P-384 */ + /* + * Check signature algorithm is consistent with curve. + */ + if (sign_nid != -1 && sign_nid != NID_ecdsa_with_SHA384) + return X509_V_ERR_SUITE_B_INVALID_SIGNATURE_ALGORITHM; + if (!(*pflags & X509_V_FLAG_SUITEB_192_LOS)) + return X509_V_ERR_SUITE_B_LOS_NOT_ALLOWED; + /* If we encounter P-384 we cannot use P-256 later */ + *pflags &= ~X509_V_FLAG_SUITEB_128_LOS_ONLY; + } else if (curve_nid == NID_X9_62_prime256v1) { /* P-256 */ + if (sign_nid != -1 && sign_nid != NID_ecdsa_with_SHA256) + return X509_V_ERR_SUITE_B_INVALID_SIGNATURE_ALGORITHM; + if (!(*pflags & X509_V_FLAG_SUITEB_128_LOS_ONLY)) + return X509_V_ERR_SUITE_B_LOS_NOT_ALLOWED; + } else + return X509_V_ERR_SUITE_B_INVALID_CURVE; + + return X509_V_OK; +} + +int X509_chain_check_suiteb(int *perror_depth, X509 *x, STACK_OF(X509) *chain, + unsigned long flags) +{ + int rv, i, sign_nid; + EVP_PKEY *pk; + unsigned long tflags = flags; + + if (!(flags & X509_V_FLAG_SUITEB_128_LOS)) + return X509_V_OK; + + /* If no EE certificate passed in must be first in chain */ + if (x == NULL) { + x = sk_X509_value(chain, 0); + i = 1; + } else + i = 0; + + pk = X509_get0_pubkey(x); + + /* + * With DANE-EE(3) success, or DANE-EE(3)/PKIX-EE(1) failure we don't build + * a chain all, just report trust success or failure, but must also report + * Suite-B errors if applicable. This is indicated via a NULL chain + * pointer. All we need to do is check the leaf key algorithm. + */ + if (chain == NULL) + return check_suite_b(pk, -1, &tflags); + + if (X509_get_version(x) != 2) { + rv = X509_V_ERR_SUITE_B_INVALID_VERSION; + /* Correct error depth */ + i = 0; + goto end; + } + + /* Check EE key only */ + rv = check_suite_b(pk, -1, &tflags); + if (rv != X509_V_OK) { + /* Correct error depth */ + i = 0; + goto end; + } + for (; i < sk_X509_num(chain); i++) { + sign_nid = X509_get_signature_nid(x); + x = sk_X509_value(chain, i); + if (X509_get_version(x) != 2) { + rv = X509_V_ERR_SUITE_B_INVALID_VERSION; + goto end; + } + pk = X509_get0_pubkey(x); + rv = check_suite_b(pk, sign_nid, &tflags); + if (rv != X509_V_OK) + goto end; + } + + /* Final check: root CA signature */ + rv = check_suite_b(pk, X509_get_signature_nid(x), &tflags); + end: + if (rv != X509_V_OK) { + /* Invalid signature or LOS errors are for previous cert */ + if ((rv == X509_V_ERR_SUITE_B_INVALID_SIGNATURE_ALGORITHM + || rv == X509_V_ERR_SUITE_B_LOS_NOT_ALLOWED) && i) + i--; + /* + * If we have LOS error and flags changed then we are signing P-384 + * with P-256. Use more meaningful error. + */ + if (rv == X509_V_ERR_SUITE_B_LOS_NOT_ALLOWED && flags != tflags) + rv = X509_V_ERR_SUITE_B_CANNOT_SIGN_P_384_WITH_P_256; + if (perror_depth) + *perror_depth = i; + } + return rv; +} + +int X509_CRL_check_suiteb(X509_CRL *crl, EVP_PKEY *pk, unsigned long flags) +{ + int sign_nid; + if (!(flags & X509_V_FLAG_SUITEB_128_LOS)) + return X509_V_OK; + sign_nid = OBJ_obj2nid(crl->crl.sig_alg.algorithm); + return check_suite_b(pk, sign_nid, &flags); +} + +#else +int X509_chain_check_suiteb(int *perror_depth, X509 *x, STACK_OF(X509) *chain, + unsigned long flags) +{ + return 0; +} + +int X509_CRL_check_suiteb(X509_CRL *crl, EVP_PKEY *pk, unsigned long flags) +{ + return 0; +} + +#endif +/* + * Not strictly speaking an "up_ref" as a STACK doesn't have a reference + * count but it has the same effect by duping the STACK and upping the ref of + * each X509 structure. + */ +STACK_OF(X509) *X509_chain_up_ref(STACK_OF(X509) *chain) +{ + STACK_OF(X509) *ret; + int i; + ret = sk_X509_dup(chain); + for (i = 0; i < sk_X509_num(ret); i++) { + X509 *x = sk_X509_value(ret, i); + X509_up_ref(x); + } + return ret; +} diff --git a/crypto/x509/x509_d2.c b/crypto/x509/x509_d2.c new file mode 100644 index 000000000..cb03dbfa6 --- /dev/null +++ b/crypto/x509/x509_d2.c @@ -0,0 +1,57 @@ +/* + * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#include "internal/cryptlib.h" +#include +#include + +int X509_STORE_set_default_paths(X509_STORE *ctx) +{ + X509_LOOKUP *lookup; + + lookup = X509_STORE_add_lookup(ctx, X509_LOOKUP_file()); + if (lookup == NULL) + return (0); + X509_LOOKUP_load_file(lookup, NULL, X509_FILETYPE_DEFAULT); + + lookup = X509_STORE_add_lookup(ctx, X509_LOOKUP_hash_dir()); + if (lookup == NULL) + return (0); + X509_LOOKUP_add_dir(lookup, NULL, X509_FILETYPE_DEFAULT); + + /* clear any errors */ + ERR_clear_error(); + + return (1); +} + +int X509_STORE_load_locations(X509_STORE *ctx, const char *file, + const char *path) +{ + X509_LOOKUP *lookup; + + if (file != NULL) { + lookup = X509_STORE_add_lookup(ctx, X509_LOOKUP_file()); + if (lookup == NULL) + return (0); + if (X509_LOOKUP_load_file(lookup, file, X509_FILETYPE_PEM) != 1) + return (0); + } + if (path != NULL) { + lookup = X509_STORE_add_lookup(ctx, X509_LOOKUP_hash_dir()); + if (lookup == NULL) + return (0); + if (X509_LOOKUP_add_dir(lookup, path, X509_FILETYPE_PEM) != 1) + return (0); + } + if ((path == NULL) && (file == NULL)) + return (0); + return (1); +} diff --git a/crypto/x509/x509_def.c b/crypto/x509/x509_def.c new file mode 100644 index 000000000..d11358e34 --- /dev/null +++ b/crypto/x509/x509_def.c @@ -0,0 +1,43 @@ +/* + * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#include "internal/cryptlib.h" +#include +#include + +const char *X509_get_default_private_dir(void) +{ + return (X509_PRIVATE_DIR); +} + +const char *X509_get_default_cert_area(void) +{ + return (X509_CERT_AREA); +} + +const char *X509_get_default_cert_dir(void) +{ + return (X509_CERT_DIR); +} + +const char *X509_get_default_cert_file(void) +{ + return (X509_CERT_FILE); +} + +const char *X509_get_default_cert_dir_env(void) +{ + return (X509_CERT_DIR_EVP); +} + +const char *X509_get_default_cert_file_env(void) +{ + return (X509_CERT_FILE_EVP); +} diff --git a/crypto/x509/x509_err.c b/crypto/x509/x509_err.c new file mode 100644 index 000000000..3f4b8ef0b --- /dev/null +++ b/crypto/x509/x509_err.c @@ -0,0 +1,142 @@ +/* + * Generated by util/mkerr.pl DO NOT EDIT + * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#include +#include + +/* BEGIN ERROR CODES */ +#ifndef OPENSSL_NO_ERR + +# define ERR_FUNC(func) ERR_PACK(ERR_LIB_X509,func,0) +# define ERR_REASON(reason) ERR_PACK(ERR_LIB_X509,0,reason) + +static ERR_STRING_DATA X509_str_functs[] = { + {ERR_FUNC(X509_F_ADD_CERT_DIR), "add_cert_dir"}, + {ERR_FUNC(X509_F_BUILD_CHAIN), "build_chain"}, + {ERR_FUNC(X509_F_BY_FILE_CTRL), "by_file_ctrl"}, + {ERR_FUNC(X509_F_CHECK_NAME_CONSTRAINTS), "check_name_constraints"}, + {ERR_FUNC(X509_F_CHECK_POLICY), "check_policy"}, + {ERR_FUNC(X509_F_DANE_I2D), "dane_i2d"}, + {ERR_FUNC(X509_F_DIR_CTRL), "dir_ctrl"}, + {ERR_FUNC(X509_F_GET_CERT_BY_SUBJECT), "get_cert_by_subject"}, + {ERR_FUNC(X509_F_NETSCAPE_SPKI_B64_DECODE), "NETSCAPE_SPKI_b64_decode"}, + {ERR_FUNC(X509_F_NETSCAPE_SPKI_B64_ENCODE), "NETSCAPE_SPKI_b64_encode"}, + {ERR_FUNC(X509_F_X509AT_ADD1_ATTR), "X509at_add1_attr"}, + {ERR_FUNC(X509_F_X509V3_ADD_EXT), "X509v3_add_ext"}, + {ERR_FUNC(X509_F_X509_ATTRIBUTE_CREATE_BY_NID), + "X509_ATTRIBUTE_create_by_NID"}, + {ERR_FUNC(X509_F_X509_ATTRIBUTE_CREATE_BY_OBJ), + "X509_ATTRIBUTE_create_by_OBJ"}, + {ERR_FUNC(X509_F_X509_ATTRIBUTE_CREATE_BY_TXT), + "X509_ATTRIBUTE_create_by_txt"}, + {ERR_FUNC(X509_F_X509_ATTRIBUTE_GET0_DATA), "X509_ATTRIBUTE_get0_data"}, + {ERR_FUNC(X509_F_X509_ATTRIBUTE_SET1_DATA), "X509_ATTRIBUTE_set1_data"}, + {ERR_FUNC(X509_F_X509_CHECK_PRIVATE_KEY), "X509_check_private_key"}, + {ERR_FUNC(X509_F_X509_CRL_DIFF), "X509_CRL_diff"}, + {ERR_FUNC(X509_F_X509_CRL_PRINT_FP), "X509_CRL_print_fp"}, + {ERR_FUNC(X509_F_X509_EXTENSION_CREATE_BY_NID), + "X509_EXTENSION_create_by_NID"}, + {ERR_FUNC(X509_F_X509_EXTENSION_CREATE_BY_OBJ), + "X509_EXTENSION_create_by_OBJ"}, + {ERR_FUNC(X509_F_X509_GET_PUBKEY_PARAMETERS), + "X509_get_pubkey_parameters"}, + {ERR_FUNC(X509_F_X509_LOAD_CERT_CRL_FILE), "X509_load_cert_crl_file"}, + {ERR_FUNC(X509_F_X509_LOAD_CERT_FILE), "X509_load_cert_file"}, + {ERR_FUNC(X509_F_X509_LOAD_CRL_FILE), "X509_load_crl_file"}, + {ERR_FUNC(X509_F_X509_NAME_ADD_ENTRY), "X509_NAME_add_entry"}, + {ERR_FUNC(X509_F_X509_NAME_ENTRY_CREATE_BY_NID), + "X509_NAME_ENTRY_create_by_NID"}, + {ERR_FUNC(X509_F_X509_NAME_ENTRY_CREATE_BY_TXT), + "X509_NAME_ENTRY_create_by_txt"}, + {ERR_FUNC(X509_F_X509_NAME_ENTRY_SET_OBJECT), + "X509_NAME_ENTRY_set_object"}, + {ERR_FUNC(X509_F_X509_NAME_ONELINE), "X509_NAME_oneline"}, + {ERR_FUNC(X509_F_X509_NAME_PRINT), "X509_NAME_print"}, + {ERR_FUNC(X509_F_X509_OBJECT_NEW), "X509_OBJECT_new"}, + {ERR_FUNC(X509_F_X509_PRINT_EX_FP), "X509_print_ex_fp"}, + {ERR_FUNC(X509_F_X509_PUBKEY_DECODE), "x509_pubkey_decode"}, + {ERR_FUNC(X509_F_X509_PUBKEY_GET0), "X509_PUBKEY_get0"}, + {ERR_FUNC(X509_F_X509_PUBKEY_SET), "X509_PUBKEY_set"}, + {ERR_FUNC(X509_F_X509_REQ_CHECK_PRIVATE_KEY), + "X509_REQ_check_private_key"}, + {ERR_FUNC(X509_F_X509_REQ_PRINT_EX), "X509_REQ_print_ex"}, + {ERR_FUNC(X509_F_X509_REQ_PRINT_FP), "X509_REQ_print_fp"}, + {ERR_FUNC(X509_F_X509_REQ_TO_X509), "X509_REQ_to_X509"}, + {ERR_FUNC(X509_F_X509_STORE_ADD_CERT), "X509_STORE_add_cert"}, + {ERR_FUNC(X509_F_X509_STORE_ADD_CRL), "X509_STORE_add_crl"}, + {ERR_FUNC(X509_F_X509_STORE_CTX_GET1_ISSUER), + "X509_STORE_CTX_get1_issuer"}, + {ERR_FUNC(X509_F_X509_STORE_CTX_INIT), "X509_STORE_CTX_init"}, + {ERR_FUNC(X509_F_X509_STORE_CTX_NEW), "X509_STORE_CTX_new"}, + {ERR_FUNC(X509_F_X509_STORE_CTX_PURPOSE_INHERIT), + "X509_STORE_CTX_purpose_inherit"}, + {ERR_FUNC(X509_F_X509_TO_X509_REQ), "X509_to_X509_REQ"}, + {ERR_FUNC(X509_F_X509_TRUST_ADD), "X509_TRUST_add"}, + {ERR_FUNC(X509_F_X509_TRUST_SET), "X509_TRUST_set"}, + {ERR_FUNC(X509_F_X509_VERIFY_CERT), "X509_verify_cert"}, + {0, NULL} +}; + +static ERR_STRING_DATA X509_str_reasons[] = { + {ERR_REASON(X509_R_AKID_MISMATCH), "akid mismatch"}, + {ERR_REASON(X509_R_BAD_SELECTOR), "bad selector"}, + {ERR_REASON(X509_R_BAD_X509_FILETYPE), "bad x509 filetype"}, + {ERR_REASON(X509_R_BASE64_DECODE_ERROR), "base64 decode error"}, + {ERR_REASON(X509_R_CANT_CHECK_DH_KEY), "cant check dh key"}, + {ERR_REASON(X509_R_CERT_ALREADY_IN_HASH_TABLE), + "cert already in hash table"}, + {ERR_REASON(X509_R_CRL_ALREADY_DELTA), "crl already delta"}, + {ERR_REASON(X509_R_CRL_VERIFY_FAILURE), "crl verify failure"}, + {ERR_REASON(X509_R_IDP_MISMATCH), "idp mismatch"}, + {ERR_REASON(X509_R_INVALID_DIRECTORY), "invalid directory"}, + {ERR_REASON(X509_R_INVALID_FIELD_NAME), "invalid field name"}, + {ERR_REASON(X509_R_INVALID_TRUST), "invalid trust"}, + {ERR_REASON(X509_R_ISSUER_MISMATCH), "issuer mismatch"}, + {ERR_REASON(X509_R_KEY_TYPE_MISMATCH), "key type mismatch"}, + {ERR_REASON(X509_R_KEY_VALUES_MISMATCH), "key values mismatch"}, + {ERR_REASON(X509_R_LOADING_CERT_DIR), "loading cert dir"}, + {ERR_REASON(X509_R_LOADING_DEFAULTS), "loading defaults"}, + {ERR_REASON(X509_R_METHOD_NOT_SUPPORTED), "method not supported"}, + {ERR_REASON(X509_R_NAME_TOO_LONG), "name too long"}, + {ERR_REASON(X509_R_NEWER_CRL_NOT_NEWER), "newer crl not newer"}, + {ERR_REASON(X509_R_NO_CERT_SET_FOR_US_TO_VERIFY), + "no cert set for us to verify"}, + {ERR_REASON(X509_R_NO_CRL_NUMBER), "no crl number"}, + {ERR_REASON(X509_R_PUBLIC_KEY_DECODE_ERROR), "public key decode error"}, + {ERR_REASON(X509_R_PUBLIC_KEY_ENCODE_ERROR), "public key encode error"}, + {ERR_REASON(X509_R_SHOULD_RETRY), "should retry"}, + {ERR_REASON(X509_R_UNABLE_TO_FIND_PARAMETERS_IN_CHAIN), + "unable to find parameters in chain"}, + {ERR_REASON(X509_R_UNABLE_TO_GET_CERTS_PUBLIC_KEY), + "unable to get certs public key"}, + {ERR_REASON(X509_R_UNKNOWN_KEY_TYPE), "unknown key type"}, + {ERR_REASON(X509_R_UNKNOWN_NID), "unknown nid"}, + {ERR_REASON(X509_R_UNKNOWN_PURPOSE_ID), "unknown purpose id"}, + {ERR_REASON(X509_R_UNKNOWN_TRUST_ID), "unknown trust id"}, + {ERR_REASON(X509_R_UNSUPPORTED_ALGORITHM), "unsupported algorithm"}, + {ERR_REASON(X509_R_WRONG_LOOKUP_TYPE), "wrong lookup type"}, + {ERR_REASON(X509_R_WRONG_TYPE), "wrong type"}, + {0, NULL} +}; + +#endif + +int ERR_load_X509_strings(void) +{ +#ifndef OPENSSL_NO_ERR + + if (ERR_func_error_string(X509_str_functs[0].error) == NULL) { + ERR_load_strings(0, X509_str_functs); + ERR_load_strings(0, X509_str_reasons); + } +#endif + return 1; +} diff --git a/crypto/x509/x509_ext.c b/crypto/x509/x509_ext.c new file mode 100644 index 000000000..3bbb0a6b6 --- /dev/null +++ b/crypto/x509/x509_ext.c @@ -0,0 +1,160 @@ +/* + * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#include +#include "internal/cryptlib.h" +#include +#include +#include +#include +#include "internal/x509_int.h" +#include + +int X509_CRL_get_ext_count(const X509_CRL *x) +{ + return (X509v3_get_ext_count(x->crl.extensions)); +} + +int X509_CRL_get_ext_by_NID(const X509_CRL *x, int nid, int lastpos) +{ + return (X509v3_get_ext_by_NID(x->crl.extensions, nid, lastpos)); +} + +int X509_CRL_get_ext_by_OBJ(const X509_CRL *x, const ASN1_OBJECT *obj, + int lastpos) +{ + return (X509v3_get_ext_by_OBJ(x->crl.extensions, obj, lastpos)); +} + +int X509_CRL_get_ext_by_critical(const X509_CRL *x, int crit, int lastpos) +{ + return (X509v3_get_ext_by_critical(x->crl.extensions, crit, lastpos)); +} + +X509_EXTENSION *X509_CRL_get_ext(const X509_CRL *x, int loc) +{ + return (X509v3_get_ext(x->crl.extensions, loc)); +} + +X509_EXTENSION *X509_CRL_delete_ext(X509_CRL *x, int loc) +{ + return (X509v3_delete_ext(x->crl.extensions, loc)); +} + +void *X509_CRL_get_ext_d2i(const X509_CRL *x, int nid, int *crit, int *idx) +{ + return X509V3_get_d2i(x->crl.extensions, nid, crit, idx); +} + +int X509_CRL_add1_ext_i2d(X509_CRL *x, int nid, void *value, int crit, + unsigned long flags) +{ + return X509V3_add1_i2d(&x->crl.extensions, nid, value, crit, flags); +} + +int X509_CRL_add_ext(X509_CRL *x, X509_EXTENSION *ex, int loc) +{ + return (X509v3_add_ext(&(x->crl.extensions), ex, loc) != NULL); +} + +int X509_get_ext_count(const X509 *x) +{ + return (X509v3_get_ext_count(x->cert_info.extensions)); +} + +int X509_get_ext_by_NID(const X509 *x, int nid, int lastpos) +{ + return (X509v3_get_ext_by_NID(x->cert_info.extensions, nid, lastpos)); +} + +int X509_get_ext_by_OBJ(const X509 *x, const ASN1_OBJECT *obj, int lastpos) +{ + return (X509v3_get_ext_by_OBJ(x->cert_info.extensions, obj, lastpos)); +} + +int X509_get_ext_by_critical(const X509 *x, int crit, int lastpos) +{ + return (X509v3_get_ext_by_critical + (x->cert_info.extensions, crit, lastpos)); +} + +X509_EXTENSION *X509_get_ext(const X509 *x, int loc) +{ + return (X509v3_get_ext(x->cert_info.extensions, loc)); +} + +X509_EXTENSION *X509_delete_ext(X509 *x, int loc) +{ + return (X509v3_delete_ext(x->cert_info.extensions, loc)); +} + +int X509_add_ext(X509 *x, X509_EXTENSION *ex, int loc) +{ + return (X509v3_add_ext(&(x->cert_info.extensions), ex, loc) != NULL); +} + +void *X509_get_ext_d2i(const X509 *x, int nid, int *crit, int *idx) +{ + return X509V3_get_d2i(x->cert_info.extensions, nid, crit, idx); +} + +int X509_add1_ext_i2d(X509 *x, int nid, void *value, int crit, + unsigned long flags) +{ + return X509V3_add1_i2d(&x->cert_info.extensions, nid, value, crit, + flags); +} + +int X509_REVOKED_get_ext_count(const X509_REVOKED *x) +{ + return (X509v3_get_ext_count(x->extensions)); +} + +int X509_REVOKED_get_ext_by_NID(const X509_REVOKED *x, int nid, int lastpos) +{ + return (X509v3_get_ext_by_NID(x->extensions, nid, lastpos)); +} + +int X509_REVOKED_get_ext_by_OBJ(const X509_REVOKED *x, const ASN1_OBJECT *obj, + int lastpos) +{ + return (X509v3_get_ext_by_OBJ(x->extensions, obj, lastpos)); +} + +int X509_REVOKED_get_ext_by_critical(const X509_REVOKED *x, int crit, int lastpos) +{ + return (X509v3_get_ext_by_critical(x->extensions, crit, lastpos)); +} + +X509_EXTENSION *X509_REVOKED_get_ext(const X509_REVOKED *x, int loc) +{ + return (X509v3_get_ext(x->extensions, loc)); +} + +X509_EXTENSION *X509_REVOKED_delete_ext(X509_REVOKED *x, int loc) +{ + return (X509v3_delete_ext(x->extensions, loc)); +} + +int X509_REVOKED_add_ext(X509_REVOKED *x, X509_EXTENSION *ex, int loc) +{ + return (X509v3_add_ext(&(x->extensions), ex, loc) != NULL); +} + +void *X509_REVOKED_get_ext_d2i(const X509_REVOKED *x, int nid, int *crit, int *idx) +{ + return X509V3_get_d2i(x->extensions, nid, crit, idx); +} + +int X509_REVOKED_add1_ext_i2d(X509_REVOKED *x, int nid, void *value, int crit, + unsigned long flags) +{ + return X509V3_add1_i2d(&x->extensions, nid, value, crit, flags); +} diff --git a/crypto/x509/x509_lcl.h b/crypto/x509/x509_lcl.h new file mode 100644 index 000000000..40bd102f7 --- /dev/null +++ b/crypto/x509/x509_lcl.h @@ -0,0 +1,142 @@ +/* + * Copyright 2014-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +/* + * This structure holds all parameters associated with a verify operation by + * including an X509_VERIFY_PARAM structure in related structures the + * parameters used can be customized + */ + +struct X509_VERIFY_PARAM_st { + char *name; + time_t check_time; /* Time to use */ + uint32_t inh_flags; /* Inheritance flags */ + unsigned long flags; /* Various verify flags */ + int purpose; /* purpose to check untrusted certificates */ + int trust; /* trust setting to check */ + int depth; /* Verify depth */ + int auth_level; /* Security level for chain verification */ + STACK_OF(ASN1_OBJECT) *policies; /* Permissible policies */ + /* Peer identity details */ + STACK_OF(OPENSSL_STRING) *hosts; /* Set of acceptable names */ + unsigned int hostflags; /* Flags to control matching features */ + char *peername; /* Matching hostname in peer certificate */ + char *email; /* If not NULL email address to match */ + size_t emaillen; + unsigned char *ip; /* If not NULL IP address to match */ + size_t iplen; /* Length of IP address */ +}; + +/* No error callback if depth < 0 */ +int x509_check_cert_time(X509_STORE_CTX *ctx, X509 *x, int depth); + +/* a sequence of these are used */ +struct x509_attributes_st { + ASN1_OBJECT *object; + STACK_OF(ASN1_TYPE) *set; +}; + +struct X509_extension_st { + ASN1_OBJECT *object; + ASN1_BOOLEAN critical; + ASN1_OCTET_STRING value; +}; + +/* + * Method to handle CRL access. In general a CRL could be very large (several + * Mb) and can consume large amounts of resources if stored in memory by + * multiple processes. This method allows general CRL operations to be + * redirected to more efficient callbacks: for example a CRL entry database. + */ + +#define X509_CRL_METHOD_DYNAMIC 1 + +struct x509_crl_method_st { + int flags; + int (*crl_init) (X509_CRL *crl); + int (*crl_free) (X509_CRL *crl); + int (*crl_lookup) (X509_CRL *crl, X509_REVOKED **ret, + ASN1_INTEGER *ser, X509_NAME *issuer); + int (*crl_verify) (X509_CRL *crl, EVP_PKEY *pk); +}; + +struct x509_lookup_method_st { + const char *name; + int (*new_item) (X509_LOOKUP *ctx); + void (*free) (X509_LOOKUP *ctx); + int (*init) (X509_LOOKUP *ctx); + int (*shutdown) (X509_LOOKUP *ctx); + int (*ctrl) (X509_LOOKUP *ctx, int cmd, const char *argc, long argl, + char **ret); + int (*get_by_subject) (X509_LOOKUP *ctx, X509_LOOKUP_TYPE type, + X509_NAME *name, X509_OBJECT *ret); + int (*get_by_issuer_serial) (X509_LOOKUP *ctx, X509_LOOKUP_TYPE type, + X509_NAME *name, ASN1_INTEGER *serial, + X509_OBJECT *ret); + int (*get_by_fingerprint) (X509_LOOKUP *ctx, X509_LOOKUP_TYPE type, + const unsigned char *bytes, int len, + X509_OBJECT *ret); + int (*get_by_alias) (X509_LOOKUP *ctx, X509_LOOKUP_TYPE type, + const char *str, int len, X509_OBJECT *ret); +}; + +/* This is the functions plus an instance of the local variables. */ +struct x509_lookup_st { + int init; /* have we been started */ + int skip; /* don't use us. */ + X509_LOOKUP_METHOD *method; /* the functions */ + char *method_data; /* method data */ + X509_STORE *store_ctx; /* who owns us */ +}; + +/* + * This is used to hold everything. It is used for all certificate + * validation. Once we have a certificate chain, the 'verify' function is + * then called to actually check the cert chain. + */ +struct x509_store_st { + /* The following is a cache of trusted certs */ + int cache; /* if true, stash any hits */ + STACK_OF(X509_OBJECT) *objs; /* Cache of all objects */ + /* These are external lookup methods */ + STACK_OF(X509_LOOKUP) *get_cert_methods; + X509_VERIFY_PARAM *param; + /* Callbacks for various operations */ + /* called to verify a certificate */ + int (*verify) (X509_STORE_CTX *ctx); + /* error callback */ + int (*verify_cb) (int ok, X509_STORE_CTX *ctx); + /* get issuers cert from ctx */ + int (*get_issuer) (X509 **issuer, X509_STORE_CTX *ctx, X509 *x); + /* check issued */ + int (*check_issued) (X509_STORE_CTX *ctx, X509 *x, X509 *issuer); + /* Check revocation status of chain */ + int (*check_revocation) (X509_STORE_CTX *ctx); + /* retrieve CRL */ + int (*get_crl) (X509_STORE_CTX *ctx, X509_CRL **crl, X509 *x); + /* Check CRL validity */ + int (*check_crl) (X509_STORE_CTX *ctx, X509_CRL *crl); + /* Check certificate against CRL */ + int (*cert_crl) (X509_STORE_CTX *ctx, X509_CRL *crl, X509 *x); + /* Check policy status of the chain */ + int (*check_policy) (X509_STORE_CTX *ctx); + STACK_OF(X509) *(*lookup_certs) (X509_STORE_CTX *ctx, X509_NAME *nm); + STACK_OF(X509_CRL) *(*lookup_crls) (X509_STORE_CTX *ctx, X509_NAME *nm); + int (*cleanup) (X509_STORE_CTX *ctx); + CRYPTO_EX_DATA ex_data; + int references; + CRYPTO_RWLOCK *lock; +}; + +typedef struct lookup_dir_hashes_st BY_DIR_HASH; +typedef struct lookup_dir_entry_st BY_DIR_ENTRY; +DEFINE_STACK_OF(BY_DIR_HASH) +DEFINE_STACK_OF(BY_DIR_ENTRY) +typedef STACK_OF(X509_NAME_ENTRY) STACK_OF_X509_NAME_ENTRY; +DEFINE_STACK_OF(STACK_OF_X509_NAME_ENTRY) diff --git a/crypto/x509/x509_lu.c b/crypto/x509/x509_lu.c new file mode 100644 index 000000000..952cbfb32 --- /dev/null +++ b/crypto/x509/x509_lu.c @@ -0,0 +1,861 @@ +/* + * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#include "internal/cryptlib.h" +#include +#include +#include "internal/x509_int.h" +#include +#include "x509_lcl.h" + +X509_LOOKUP *X509_LOOKUP_new(X509_LOOKUP_METHOD *method) +{ + X509_LOOKUP *ret; + + ret = OPENSSL_zalloc(sizeof(*ret)); + if (ret == NULL) + return NULL; + + ret->method = method; + if ((method->new_item != NULL) && !method->new_item(ret)) { + OPENSSL_free(ret); + return NULL; + } + return ret; +} + +void X509_LOOKUP_free(X509_LOOKUP *ctx) +{ + if (ctx == NULL) + return; + if ((ctx->method != NULL) && (ctx->method->free != NULL)) + (*ctx->method->free) (ctx); + OPENSSL_free(ctx); +} + +int X509_STORE_lock(X509_STORE *s) +{ + return CRYPTO_THREAD_write_lock(s->lock); +} + +int X509_STORE_unlock(X509_STORE *s) +{ + return CRYPTO_THREAD_unlock(s->lock); +} + +int X509_LOOKUP_init(X509_LOOKUP *ctx) +{ + if (ctx->method == NULL) + return 0; + if (ctx->method->init != NULL) + return ctx->method->init(ctx); + else + return 1; +} + +int X509_LOOKUP_shutdown(X509_LOOKUP *ctx) +{ + if (ctx->method == NULL) + return 0; + if (ctx->method->shutdown != NULL) + return ctx->method->shutdown(ctx); + else + return 1; +} + +int X509_LOOKUP_ctrl(X509_LOOKUP *ctx, int cmd, const char *argc, long argl, + char **ret) +{ + if (ctx->method == NULL) + return -1; + if (ctx->method->ctrl != NULL) + return ctx->method->ctrl(ctx, cmd, argc, argl, ret); + else + return 1; +} + +int X509_LOOKUP_by_subject(X509_LOOKUP *ctx, X509_LOOKUP_TYPE type, + X509_NAME *name, X509_OBJECT *ret) +{ + if ((ctx->method == NULL) || (ctx->method->get_by_subject == NULL)) + return 0; + if (ctx->skip) + return 0; + return ctx->method->get_by_subject(ctx, type, name, ret); +} + +int X509_LOOKUP_by_issuer_serial(X509_LOOKUP *ctx, X509_LOOKUP_TYPE type, + X509_NAME *name, ASN1_INTEGER *serial, + X509_OBJECT *ret) +{ + if ((ctx->method == NULL) || (ctx->method->get_by_issuer_serial == NULL)) + return 0; + return ctx->method->get_by_issuer_serial(ctx, type, name, serial, ret); +} + +int X509_LOOKUP_by_fingerprint(X509_LOOKUP *ctx, X509_LOOKUP_TYPE type, + const unsigned char *bytes, int len, + X509_OBJECT *ret) +{ + if ((ctx->method == NULL) || (ctx->method->get_by_fingerprint == NULL)) + return 0; + return ctx->method->get_by_fingerprint(ctx, type, bytes, len, ret); +} + +int X509_LOOKUP_by_alias(X509_LOOKUP *ctx, X509_LOOKUP_TYPE type, + const char *str, int len, X509_OBJECT *ret) +{ + if ((ctx->method == NULL) || (ctx->method->get_by_alias == NULL)) + return 0; + return ctx->method->get_by_alias(ctx, type, str, len, ret); +} + +static int x509_object_cmp(const X509_OBJECT *const *a, + const X509_OBJECT *const *b) +{ + int ret; + + ret = ((*a)->type - (*b)->type); + if (ret) + return ret; + switch ((*a)->type) { + case X509_LU_X509: + ret = X509_subject_name_cmp((*a)->data.x509, (*b)->data.x509); + break; + case X509_LU_CRL: + ret = X509_CRL_cmp((*a)->data.crl, (*b)->data.crl); + break; + default: + /* abort(); */ + return 0; + } + return ret; +} + +X509_STORE *X509_STORE_new(void) +{ + X509_STORE *ret; + + if ((ret = OPENSSL_zalloc(sizeof(*ret))) == NULL) + return NULL; + if ((ret->objs = sk_X509_OBJECT_new(x509_object_cmp)) == NULL) + goto err; + ret->cache = 1; + if ((ret->get_cert_methods = sk_X509_LOOKUP_new_null()) == NULL) + goto err; + + if ((ret->param = X509_VERIFY_PARAM_new()) == NULL) + goto err; + + if (!CRYPTO_new_ex_data(CRYPTO_EX_INDEX_X509_STORE, ret, &ret->ex_data)) + goto err; + + ret->lock = CRYPTO_THREAD_lock_new(); + if (ret->lock == NULL) + goto err; + + ret->references = 1; + return ret; + +err: + X509_VERIFY_PARAM_free(ret->param); + sk_X509_OBJECT_free(ret->objs); + sk_X509_LOOKUP_free(ret->get_cert_methods); + OPENSSL_free(ret); + return NULL; +} + +static void cleanup(X509_OBJECT *a) +{ + if (!a) + return; + if (a->type == X509_LU_X509) { + X509_free(a->data.x509); + } else if (a->type == X509_LU_CRL) { + X509_CRL_free(a->data.crl); + } else { + /* abort(); */ + } + + OPENSSL_free(a); +} + +void X509_STORE_free(X509_STORE *vfy) +{ + int i; + STACK_OF(X509_LOOKUP) *sk; + X509_LOOKUP *lu; + + if (vfy == NULL) + return; + + CRYPTO_atomic_add(&vfy->references, -1, &i, vfy->lock); + REF_PRINT_COUNT("X509_STORE", vfy); + if (i > 0) + return; + REF_ASSERT_ISNT(i < 0); + + sk = vfy->get_cert_methods; + for (i = 0; i < sk_X509_LOOKUP_num(sk); i++) { + lu = sk_X509_LOOKUP_value(sk, i); + X509_LOOKUP_shutdown(lu); + X509_LOOKUP_free(lu); + } + sk_X509_LOOKUP_free(sk); + sk_X509_OBJECT_pop_free(vfy->objs, cleanup); + + CRYPTO_free_ex_data(CRYPTO_EX_INDEX_X509_STORE, vfy, &vfy->ex_data); + X509_VERIFY_PARAM_free(vfy->param); + CRYPTO_THREAD_lock_free(vfy->lock); + OPENSSL_free(vfy); +} + +int X509_STORE_up_ref(X509_STORE *vfy) +{ + int i; + + if (CRYPTO_atomic_add(&vfy->references, 1, &i, vfy->lock) <= 0) + return 0; + + REF_PRINT_COUNT("X509_STORE", a); + REF_ASSERT_ISNT(i < 2); + return ((i > 1) ? 1 : 0); +} + +X509_LOOKUP *X509_STORE_add_lookup(X509_STORE *v, X509_LOOKUP_METHOD *m) +{ + int i; + STACK_OF(X509_LOOKUP) *sk; + X509_LOOKUP *lu; + + sk = v->get_cert_methods; + for (i = 0; i < sk_X509_LOOKUP_num(sk); i++) { + lu = sk_X509_LOOKUP_value(sk, i); + if (m == lu->method) { + return lu; + } + } + /* a new one */ + lu = X509_LOOKUP_new(m); + if (lu == NULL) + return NULL; + else { + lu->store_ctx = v; + if (sk_X509_LOOKUP_push(v->get_cert_methods, lu)) + return lu; + else { + X509_LOOKUP_free(lu); + return NULL; + } + } +} + +X509_OBJECT *X509_STORE_CTX_get_obj_by_subject(X509_STORE_CTX *vs, + X509_LOOKUP_TYPE type, + X509_NAME *name) +{ + X509_OBJECT *ret = X509_OBJECT_new(); + + if (ret == NULL) + return NULL; + if (!X509_STORE_CTX_get_by_subject(vs, type, name, ret)) { + X509_OBJECT_free(ret); + return NULL; + } + return ret; +} + +int X509_STORE_CTX_get_by_subject(X509_STORE_CTX *vs, X509_LOOKUP_TYPE type, + X509_NAME *name, X509_OBJECT *ret) +{ + X509_STORE *ctx = vs->ctx; + X509_LOOKUP *lu; + X509_OBJECT stmp, *tmp; + int i, j; + + CRYPTO_THREAD_write_lock(ctx->lock); + tmp = X509_OBJECT_retrieve_by_subject(ctx->objs, type, name); + CRYPTO_THREAD_unlock(ctx->lock); + + if (tmp == NULL || type == X509_LU_CRL) { + for (i = 0; i < sk_X509_LOOKUP_num(ctx->get_cert_methods); i++) { + lu = sk_X509_LOOKUP_value(ctx->get_cert_methods, i); + j = X509_LOOKUP_by_subject(lu, type, name, &stmp); + if (j) { + tmp = &stmp; + break; + } + } + if (tmp == NULL) + return 0; + } + + ret->type = tmp->type; + ret->data.ptr = tmp->data.ptr; + + X509_OBJECT_up_ref_count(ret); + + return 1; +} + +int X509_STORE_add_cert(X509_STORE *ctx, X509 *x) +{ + X509_OBJECT *obj; + int ret = 1, added = 1; + + if (x == NULL) + return 0; + obj = X509_OBJECT_new(); + if (obj == NULL) + return 0; + obj->type = X509_LU_X509; + obj->data.x509 = x; + X509_OBJECT_up_ref_count(obj); + + CRYPTO_THREAD_write_lock(ctx->lock); + + if (X509_OBJECT_retrieve_match(ctx->objs, obj)) { + X509err(X509_F_X509_STORE_ADD_CERT, + X509_R_CERT_ALREADY_IN_HASH_TABLE); + ret = 0; + } else { + added = sk_X509_OBJECT_push(ctx->objs, obj); + ret = added != 0; + } + + CRYPTO_THREAD_unlock(ctx->lock); + + if (!ret) /* obj not pushed */ + X509_OBJECT_free(obj); + if (!added) /* on push failure */ + X509err(X509_F_X509_STORE_ADD_CERT, ERR_R_MALLOC_FAILURE); + + return ret; +} + +int X509_STORE_add_crl(X509_STORE *ctx, X509_CRL *x) +{ + X509_OBJECT *obj; + int ret = 1, added = 1; + + if (x == NULL) + return 0; + obj = X509_OBJECT_new(); + if (obj == NULL) + return 0; + obj->type = X509_LU_CRL; + obj->data.crl = x; + X509_OBJECT_up_ref_count(obj); + + CRYPTO_THREAD_write_lock(ctx->lock); + + if (X509_OBJECT_retrieve_match(ctx->objs, obj)) { + X509err(X509_F_X509_STORE_ADD_CRL, X509_R_CERT_ALREADY_IN_HASH_TABLE); + ret = 0; + } else { + added = sk_X509_OBJECT_push(ctx->objs, obj); + ret = added != 0; + } + + CRYPTO_THREAD_unlock(ctx->lock); + + if (!ret) /* obj not pushed */ + X509_OBJECT_free(obj); + if (!added) /* on push failure */ + X509err(X509_F_X509_STORE_ADD_CRL, ERR_R_MALLOC_FAILURE); + + return ret; +} + +int X509_OBJECT_up_ref_count(X509_OBJECT *a) +{ + switch (a->type) { + default: + break; + case X509_LU_X509: + return X509_up_ref(a->data.x509); + case X509_LU_CRL: + return X509_CRL_up_ref(a->data.crl); + } + return 1; +} + +X509 *X509_OBJECT_get0_X509(const X509_OBJECT *a) +{ + if (a == NULL || a->type != X509_LU_X509) + return NULL; + return a->data.x509; +} + +X509_CRL *X509_OBJECT_get0_X509_CRL(X509_OBJECT *a) +{ + if (a == NULL || a->type != X509_LU_CRL) + return NULL; + return a->data.crl; +} + +X509_LOOKUP_TYPE X509_OBJECT_get_type(const X509_OBJECT *a) +{ + return a->type; +} + +X509_OBJECT *X509_OBJECT_new() +{ + X509_OBJECT *ret = OPENSSL_zalloc(sizeof(*ret)); + + if (ret == NULL) { + X509err(X509_F_X509_OBJECT_NEW, ERR_R_MALLOC_FAILURE); + return NULL; + } + ret->type = X509_LU_NONE; + return ret; +} + + +void X509_OBJECT_free(X509_OBJECT *a) +{ + if (a == NULL) + return; + switch (a->type) { + default: + break; + case X509_LU_X509: + X509_free(a->data.x509); + break; + case X509_LU_CRL: + X509_CRL_free(a->data.crl); + break; + } + OPENSSL_free(a); +} + +static int x509_object_idx_cnt(STACK_OF(X509_OBJECT) *h, X509_LOOKUP_TYPE type, + X509_NAME *name, int *pnmatch) +{ + X509_OBJECT stmp; + X509 x509_s; + X509_CRL crl_s; + int idx; + + stmp.type = type; + switch (type) { + case X509_LU_X509: + stmp.data.x509 = &x509_s; + x509_s.cert_info.subject = name; + break; + case X509_LU_CRL: + stmp.data.crl = &crl_s; + crl_s.crl.issuer = name; + break; + default: + /* abort(); */ + return -1; + } + + idx = sk_X509_OBJECT_find(h, &stmp); + if (idx >= 0 && pnmatch) { + int tidx; + const X509_OBJECT *tobj, *pstmp; + *pnmatch = 1; + pstmp = &stmp; + for (tidx = idx + 1; tidx < sk_X509_OBJECT_num(h); tidx++) { + tobj = sk_X509_OBJECT_value(h, tidx); + if (x509_object_cmp(&tobj, &pstmp)) + break; + (*pnmatch)++; + } + } + return idx; +} + +int X509_OBJECT_idx_by_subject(STACK_OF(X509_OBJECT) *h, X509_LOOKUP_TYPE type, + X509_NAME *name) +{ + return x509_object_idx_cnt(h, type, name, NULL); +} + +X509_OBJECT *X509_OBJECT_retrieve_by_subject(STACK_OF(X509_OBJECT) *h, + X509_LOOKUP_TYPE type, + X509_NAME *name) +{ + int idx; + idx = X509_OBJECT_idx_by_subject(h, type, name); + if (idx == -1) + return NULL; + return sk_X509_OBJECT_value(h, idx); +} + +STACK_OF(X509_OBJECT) *X509_STORE_get0_objects(X509_STORE *v) +{ + return v->objs; +} + +STACK_OF(X509) *X509_STORE_CTX_get1_certs(X509_STORE_CTX *ctx, X509_NAME *nm) +{ + int i, idx, cnt; + STACK_OF(X509) *sk = NULL; + X509 *x; + X509_OBJECT *obj; + + CRYPTO_THREAD_write_lock(ctx->ctx->lock); + idx = x509_object_idx_cnt(ctx->ctx->objs, X509_LU_X509, nm, &cnt); + if (idx < 0) { + /* + * Nothing found in cache: do lookup to possibly add new objects to + * cache + */ + X509_OBJECT *xobj = X509_OBJECT_new(); + + CRYPTO_THREAD_unlock(ctx->ctx->lock); + if (xobj == NULL) + return NULL; + if (!X509_STORE_CTX_get_by_subject(ctx, X509_LU_X509, nm, xobj)) { + X509_OBJECT_free(xobj); + return NULL; + } + X509_OBJECT_free(xobj); + CRYPTO_THREAD_write_lock(ctx->ctx->lock); + idx = x509_object_idx_cnt(ctx->ctx->objs, X509_LU_X509, nm, &cnt); + if (idx < 0) { + CRYPTO_THREAD_unlock(ctx->ctx->lock); + return NULL; + } + } + + sk = sk_X509_new_null(); + for (i = 0; i < cnt; i++, idx++) { + obj = sk_X509_OBJECT_value(ctx->ctx->objs, idx); + x = obj->data.x509; + X509_up_ref(x); + if (!sk_X509_push(sk, x)) { + CRYPTO_THREAD_unlock(ctx->ctx->lock); + X509_free(x); + sk_X509_pop_free(sk, X509_free); + return NULL; + } + } + CRYPTO_THREAD_unlock(ctx->ctx->lock); + return sk; +} + +STACK_OF(X509_CRL) *X509_STORE_CTX_get1_crls(X509_STORE_CTX *ctx, X509_NAME *nm) +{ + int i, idx, cnt; + STACK_OF(X509_CRL) *sk = sk_X509_CRL_new_null(); + X509_CRL *x; + X509_OBJECT *obj, *xobj = X509_OBJECT_new(); + + /* Always do lookup to possibly add new CRLs to cache */ + if (sk == NULL || xobj == NULL || + !X509_STORE_CTX_get_by_subject(ctx, X509_LU_CRL, nm, xobj)) { + X509_OBJECT_free(xobj); + sk_X509_CRL_free(sk); + return NULL; + } + X509_OBJECT_free(xobj); + CRYPTO_THREAD_write_lock(ctx->ctx->lock); + idx = x509_object_idx_cnt(ctx->ctx->objs, X509_LU_CRL, nm, &cnt); + if (idx < 0) { + CRYPTO_THREAD_unlock(ctx->ctx->lock); + sk_X509_CRL_free(sk); + return NULL; + } + + for (i = 0; i < cnt; i++, idx++) { + obj = sk_X509_OBJECT_value(ctx->ctx->objs, idx); + x = obj->data.crl; + X509_CRL_up_ref(x); + if (!sk_X509_CRL_push(sk, x)) { + CRYPTO_THREAD_unlock(ctx->ctx->lock); + X509_CRL_free(x); + sk_X509_CRL_pop_free(sk, X509_CRL_free); + return NULL; + } + } + CRYPTO_THREAD_unlock(ctx->ctx->lock); + return sk; +} + +X509_OBJECT *X509_OBJECT_retrieve_match(STACK_OF(X509_OBJECT) *h, + X509_OBJECT *x) +{ + int idx, i; + X509_OBJECT *obj; + idx = sk_X509_OBJECT_find(h, x); + if (idx == -1) + return NULL; + if ((x->type != X509_LU_X509) && (x->type != X509_LU_CRL)) + return sk_X509_OBJECT_value(h, idx); + for (i = idx; i < sk_X509_OBJECT_num(h); i++) { + obj = sk_X509_OBJECT_value(h, i); + if (x509_object_cmp + ((const X509_OBJECT **)&obj, (const X509_OBJECT **)&x)) + return NULL; + if (x->type == X509_LU_X509) { + if (!X509_cmp(obj->data.x509, x->data.x509)) + return obj; + } else if (x->type == X509_LU_CRL) { + if (!X509_CRL_match(obj->data.crl, x->data.crl)) + return obj; + } else + return obj; + } + return NULL; +} + +/*- + * Try to get issuer certificate from store. Due to limitations + * of the API this can only retrieve a single certificate matching + * a given subject name. However it will fill the cache with all + * matching certificates, so we can examine the cache for all + * matches. + * + * Return values are: + * 1 lookup successful. + * 0 certificate not found. + * -1 some other error. + */ +int X509_STORE_CTX_get1_issuer(X509 **issuer, X509_STORE_CTX *ctx, X509 *x) +{ + X509_NAME *xn; + X509_OBJECT *obj = X509_OBJECT_new(), *pobj = NULL; + int i, ok, idx, ret; + + if (obj == NULL) + return -1; + *issuer = NULL; + xn = X509_get_issuer_name(x); + ok = X509_STORE_CTX_get_by_subject(ctx, X509_LU_X509, xn, obj); + if (ok != 1) { + X509_OBJECT_free(obj); + return 0; + } + /* If certificate matches all OK */ + if (ctx->check_issued(ctx, x, obj->data.x509)) { + if (x509_check_cert_time(ctx, obj->data.x509, -1)) { + *issuer = obj->data.x509; + X509_up_ref(*issuer); + X509_OBJECT_free(obj); + return 1; + } + } + X509_OBJECT_free(obj); + + /* Else find index of first cert accepted by 'check_issued' */ + ret = 0; + CRYPTO_THREAD_write_lock(ctx->ctx->lock); + idx = X509_OBJECT_idx_by_subject(ctx->ctx->objs, X509_LU_X509, xn); + if (idx != -1) { /* should be true as we've had at least one + * match */ + /* Look through all matching certs for suitable issuer */ + for (i = idx; i < sk_X509_OBJECT_num(ctx->ctx->objs); i++) { + pobj = sk_X509_OBJECT_value(ctx->ctx->objs, i); + /* See if we've run past the matches */ + if (pobj->type != X509_LU_X509) + break; + if (X509_NAME_cmp(xn, X509_get_subject_name(pobj->data.x509))) + break; + if (ctx->check_issued(ctx, x, pobj->data.x509)) { + *issuer = pobj->data.x509; + ret = 1; + /* + * If times check, exit with match, + * otherwise keep looking. Leave last + * match in issuer so we return nearest + * match if no certificate time is OK. + */ + + if (x509_check_cert_time(ctx, *issuer, -1)) + break; + } + } + } + CRYPTO_THREAD_unlock(ctx->ctx->lock); + if (*issuer) + X509_up_ref(*issuer); + return ret; +} + +int X509_STORE_set_flags(X509_STORE *ctx, unsigned long flags) +{ + return X509_VERIFY_PARAM_set_flags(ctx->param, flags); +} + +int X509_STORE_set_depth(X509_STORE *ctx, int depth) +{ + X509_VERIFY_PARAM_set_depth(ctx->param, depth); + return 1; +} + +int X509_STORE_set_purpose(X509_STORE *ctx, int purpose) +{ + return X509_VERIFY_PARAM_set_purpose(ctx->param, purpose); +} + +int X509_STORE_set_trust(X509_STORE *ctx, int trust) +{ + return X509_VERIFY_PARAM_set_trust(ctx->param, trust); +} + +int X509_STORE_set1_param(X509_STORE *ctx, X509_VERIFY_PARAM *param) +{ + return X509_VERIFY_PARAM_set1(ctx->param, param); +} + +X509_VERIFY_PARAM *X509_STORE_get0_param(X509_STORE *ctx) +{ + return ctx->param; +} + +void X509_STORE_set_verify(X509_STORE *ctx, X509_STORE_CTX_verify_fn verify) +{ + ctx->verify = verify; +} + +X509_STORE_CTX_verify_fn X509_STORE_get_verify(X509_STORE *ctx) +{ + return ctx->verify; +} + +void X509_STORE_set_verify_cb(X509_STORE *ctx, + X509_STORE_CTX_verify_cb verify_cb) +{ + ctx->verify_cb = verify_cb; +} + +X509_STORE_CTX_verify_cb X509_STORE_get_verify_cb(X509_STORE *ctx) +{ + return ctx->verify_cb; +} + +void X509_STORE_set_get_issuer(X509_STORE *ctx, + X509_STORE_CTX_get_issuer_fn get_issuer) +{ + ctx->get_issuer = get_issuer; +} + +X509_STORE_CTX_get_issuer_fn X509_STORE_get_get_issuer(X509_STORE *ctx) +{ + return ctx->get_issuer; +} + +void X509_STORE_set_check_issued(X509_STORE *ctx, + X509_STORE_CTX_check_issued_fn check_issued) +{ + ctx->check_issued = check_issued; +} + +X509_STORE_CTX_check_issued_fn X509_STORE_get_check_issued(X509_STORE *ctx) +{ + return ctx->check_issued; +} + +void X509_STORE_set_check_revocation(X509_STORE *ctx, + X509_STORE_CTX_check_revocation_fn check_revocation) +{ + ctx->check_revocation = check_revocation; +} + +X509_STORE_CTX_check_revocation_fn X509_STORE_get_check_revocation(X509_STORE *ctx) +{ + return ctx->check_revocation; +} + +void X509_STORE_set_get_crl(X509_STORE *ctx, + X509_STORE_CTX_get_crl_fn get_crl) +{ + ctx->get_crl = get_crl; +} + +X509_STORE_CTX_get_crl_fn X509_STORE_get_get_crl(X509_STORE *ctx) +{ + return ctx->get_crl; +} + +void X509_STORE_set_check_crl(X509_STORE *ctx, + X509_STORE_CTX_check_crl_fn check_crl) +{ + ctx->check_crl = check_crl; +} + +X509_STORE_CTX_check_crl_fn X509_STORE_get_check_crl(X509_STORE *ctx) +{ + return ctx->check_crl; +} + +void X509_STORE_set_cert_crl(X509_STORE *ctx, + X509_STORE_CTX_cert_crl_fn cert_crl) +{ + ctx->cert_crl = cert_crl; +} + +X509_STORE_CTX_cert_crl_fn X509_STORE_get_cert_crl(X509_STORE *ctx) +{ + return ctx->cert_crl; +} + +void X509_STORE_set_check_policy(X509_STORE *ctx, + X509_STORE_CTX_check_policy_fn check_policy) +{ + ctx->check_policy = check_policy; +} + +X509_STORE_CTX_check_policy_fn X509_STORE_get_check_policy(X509_STORE *ctx) +{ + return ctx->check_policy; +} + +void X509_STORE_set_lookup_certs(X509_STORE *ctx, + X509_STORE_CTX_lookup_certs_fn lookup_certs) +{ + ctx->lookup_certs = lookup_certs; +} + +X509_STORE_CTX_lookup_certs_fn X509_STORE_get_lookup_certs(X509_STORE *ctx) +{ + return ctx->lookup_certs; +} + +void X509_STORE_set_lookup_crls(X509_STORE *ctx, + X509_STORE_CTX_lookup_crls_fn lookup_crls) +{ + ctx->lookup_crls = lookup_crls; +} + +X509_STORE_CTX_lookup_crls_fn X509_STORE_get_lookup_crls(X509_STORE *ctx) +{ + return ctx->lookup_crls; +} + +void X509_STORE_set_cleanup(X509_STORE *ctx, + X509_STORE_CTX_cleanup_fn ctx_cleanup) +{ + ctx->cleanup = ctx_cleanup; +} + +X509_STORE_CTX_cleanup_fn X509_STORE_get_cleanup(X509_STORE *ctx) +{ + return ctx->cleanup; +} + +int X509_STORE_set_ex_data(X509_STORE *ctx, int idx, void *data) +{ + return CRYPTO_set_ex_data(&ctx->ex_data, idx, data); +} + +void *X509_STORE_get_ex_data(X509_STORE *ctx, int idx) +{ + return CRYPTO_get_ex_data(&ctx->ex_data, idx); +} + +X509_STORE *X509_STORE_CTX_get0_store(X509_STORE_CTX *ctx) +{ + return ctx->ctx; +} diff --git a/crypto/x509/x509_obj.c b/crypto/x509/x509_obj.c new file mode 100644 index 000000000..55dc778bb --- /dev/null +++ b/crypto/x509/x509_obj.c @@ -0,0 +1,182 @@ +/* + * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#include "internal/cryptlib.h" +#include +#include +#include +#include +#include "internal/x509_int.h" + +/* + * Limit to ensure we don't overflow: much greater than + * anything encountered in practice. + */ + +#define NAME_ONELINE_MAX (1024 * 1024) + +char *X509_NAME_oneline(const X509_NAME *a, char *buf, int len) +{ + const X509_NAME_ENTRY *ne; + int i; + int n, lold, l, l1, l2, num, j, type; + const char *s; + char *p; + unsigned char *q; + BUF_MEM *b = NULL; + static const char hex[17] = "0123456789ABCDEF"; + int gs_doit[4]; + char tmp_buf[80]; +#ifdef CHARSET_EBCDIC + unsigned char ebcdic_buf[1024]; +#endif + + if (buf == NULL) { + if ((b = BUF_MEM_new()) == NULL) + goto err; + if (!BUF_MEM_grow(b, 200)) + goto err; + b->data[0] = '\0'; + len = 200; + } else if (len == 0) { + return NULL; + } + if (a == NULL) { + if (b) { + buf = b->data; + OPENSSL_free(b); + } + strncpy(buf, "NO X509_NAME", len); + buf[len - 1] = '\0'; + return buf; + } + + len--; /* space for '\0' */ + l = 0; + for (i = 0; i < sk_X509_NAME_ENTRY_num(a->entries); i++) { + ne = sk_X509_NAME_ENTRY_value(a->entries, i); + n = OBJ_obj2nid(ne->object); + if ((n == NID_undef) || ((s = OBJ_nid2sn(n)) == NULL)) { + i2t_ASN1_OBJECT(tmp_buf, sizeof(tmp_buf), ne->object); + s = tmp_buf; + } + l1 = strlen(s); + + type = ne->value->type; + num = ne->value->length; + if (num > NAME_ONELINE_MAX) { + X509err(X509_F_X509_NAME_ONELINE, X509_R_NAME_TOO_LONG); + goto end; + } + q = ne->value->data; +#ifdef CHARSET_EBCDIC + if (type == V_ASN1_GENERALSTRING || + type == V_ASN1_VISIBLESTRING || + type == V_ASN1_PRINTABLESTRING || + type == V_ASN1_TELETEXSTRING || + type == V_ASN1_IA5STRING) { + if (num > (int)sizeof(ebcdic_buf)) + num = sizeof(ebcdic_buf); + ascii2ebcdic(ebcdic_buf, q, num); + q = ebcdic_buf; + } +#endif + + if ((type == V_ASN1_GENERALSTRING) && ((num % 4) == 0)) { + gs_doit[0] = gs_doit[1] = gs_doit[2] = gs_doit[3] = 0; + for (j = 0; j < num; j++) + if (q[j] != 0) + gs_doit[j & 3] = 1; + + if (gs_doit[0] | gs_doit[1] | gs_doit[2]) + gs_doit[0] = gs_doit[1] = gs_doit[2] = gs_doit[3] = 1; + else { + gs_doit[0] = gs_doit[1] = gs_doit[2] = 0; + gs_doit[3] = 1; + } + } else + gs_doit[0] = gs_doit[1] = gs_doit[2] = gs_doit[3] = 1; + + for (l2 = j = 0; j < num; j++) { + if (!gs_doit[j & 3]) + continue; + l2++; +#ifndef CHARSET_EBCDIC + if ((q[j] < ' ') || (q[j] > '~')) + l2 += 3; +#else + if ((os_toascii[q[j]] < os_toascii[' ']) || + (os_toascii[q[j]] > os_toascii['~'])) + l2 += 3; +#endif + } + + lold = l; + l += 1 + l1 + 1 + l2; + if (l > NAME_ONELINE_MAX) { + X509err(X509_F_X509_NAME_ONELINE, X509_R_NAME_TOO_LONG); + goto end; + } + if (b != NULL) { + if (!BUF_MEM_grow(b, l + 1)) + goto err; + p = &(b->data[lold]); + } else if (l > len) { + break; + } else + p = &(buf[lold]); + *(p++) = '/'; + memcpy(p, s, (unsigned int)l1); + p += l1; + *(p++) = '='; + +#ifndef CHARSET_EBCDIC /* q was assigned above already. */ + q = ne->value->data; +#endif + + for (j = 0; j < num; j++) { + if (!gs_doit[j & 3]) + continue; +#ifndef CHARSET_EBCDIC + n = q[j]; + if ((n < ' ') || (n > '~')) { + *(p++) = '\\'; + *(p++) = 'x'; + *(p++) = hex[(n >> 4) & 0x0f]; + *(p++) = hex[n & 0x0f]; + } else + *(p++) = n; +#else + n = os_toascii[q[j]]; + if ((n < os_toascii[' ']) || (n > os_toascii['~'])) { + *(p++) = '\\'; + *(p++) = 'x'; + *(p++) = hex[(n >> 4) & 0x0f]; + *(p++) = hex[n & 0x0f]; + } else + *(p++) = q[j]; +#endif + } + *p = '\0'; + } + if (b != NULL) { + p = b->data; + OPENSSL_free(b); + } else + p = buf; + if (i == 0) + *p = '\0'; + return (p); + err: + X509err(X509_F_X509_NAME_ONELINE, ERR_R_MALLOC_FAILURE); + end: + BUF_MEM_free(b); + return (NULL); +} diff --git a/crypto/x509/x509_r2x.c b/crypto/x509/x509_r2x.c new file mode 100644 index 000000000..7429e9cc6 --- /dev/null +++ b/crypto/x509/x509_r2x.c @@ -0,0 +1,67 @@ +/* + * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#include "internal/cryptlib.h" +#include +#include +#include +#include +#include "internal/x509_int.h" +#include +#include + +X509 *X509_REQ_to_X509(X509_REQ *r, int days, EVP_PKEY *pkey) +{ + X509 *ret = NULL; + X509_CINF *xi = NULL; + X509_NAME *xn; + EVP_PKEY *pubkey = NULL; + + if ((ret = X509_new()) == NULL) { + X509err(X509_F_X509_REQ_TO_X509, ERR_R_MALLOC_FAILURE); + return NULL; + } + + /* duplicate the request */ + xi = &ret->cert_info; + + if (sk_X509_ATTRIBUTE_num(r->req_info.attributes) != 0) { + if ((xi->version = ASN1_INTEGER_new()) == NULL) + goto err; + if (!ASN1_INTEGER_set(xi->version, 2)) + goto err; +/*- xi->extensions=ri->attributes; <- bad, should not ever be done + ri->attributes=NULL; */ + } + + xn = X509_REQ_get_subject_name(r); + if (X509_set_subject_name(ret, xn) == 0) + goto err; + if (X509_set_issuer_name(ret, xn) == 0) + goto err; + + if (X509_gmtime_adj(xi->validity.notBefore, 0) == NULL) + goto err; + if (X509_gmtime_adj(xi->validity.notAfter, (long)60 * 60 * 24 * days) == + NULL) + goto err; + + pubkey = X509_REQ_get0_pubkey(r); + if (pubkey == NULL || !X509_set_pubkey(ret, pubkey)) + goto err; + + if (!X509_sign(ret, pkey, EVP_get_default_digest())) + goto err; + return ret; + + err: + X509_free(ret); + return NULL; +} diff --git a/crypto/x509/x509_req.c b/crypto/x509/x509_req.c new file mode 100644 index 000000000..7b88dbcd2 --- /dev/null +++ b/crypto/x509/x509_req.c @@ -0,0 +1,298 @@ +/* + * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#include "internal/cryptlib.h" +#include +#include +#include +#include +#include +#include "internal/x509_int.h" +#include +#include +#include + +X509_REQ *X509_to_X509_REQ(X509 *x, EVP_PKEY *pkey, const EVP_MD *md) +{ + X509_REQ *ret; + X509_REQ_INFO *ri; + int i; + EVP_PKEY *pktmp; + + ret = X509_REQ_new(); + if (ret == NULL) { + X509err(X509_F_X509_TO_X509_REQ, ERR_R_MALLOC_FAILURE); + goto err; + } + + ri = &ret->req_info; + + ri->version->length = 1; + ri->version->data = OPENSSL_malloc(1); + if (ri->version->data == NULL) + goto err; + ri->version->data[0] = 0; /* version == 0 */ + + if (!X509_REQ_set_subject_name(ret, X509_get_subject_name(x))) + goto err; + + pktmp = X509_get0_pubkey(x); + if (pktmp == NULL) + goto err; + i = X509_REQ_set_pubkey(ret, pktmp); + if (!i) + goto err; + + if (pkey != NULL) { + if (!X509_REQ_sign(ret, pkey, md)) + goto err; + } + return (ret); + err: + X509_REQ_free(ret); + return (NULL); +} + +EVP_PKEY *X509_REQ_get_pubkey(X509_REQ *req) +{ + if (req == NULL) + return (NULL); + return (X509_PUBKEY_get(req->req_info.pubkey)); +} + +EVP_PKEY *X509_REQ_get0_pubkey(X509_REQ *req) +{ + if (req == NULL) + return NULL; + return (X509_PUBKEY_get0(req->req_info.pubkey)); +} + +X509_PUBKEY *X509_REQ_get_X509_PUBKEY(X509_REQ *req) +{ + return req->req_info.pubkey; +} + +int X509_REQ_check_private_key(X509_REQ *x, EVP_PKEY *k) +{ + EVP_PKEY *xk = NULL; + int ok = 0; + + xk = X509_REQ_get_pubkey(x); + switch (EVP_PKEY_cmp(xk, k)) { + case 1: + ok = 1; + break; + case 0: + X509err(X509_F_X509_REQ_CHECK_PRIVATE_KEY, + X509_R_KEY_VALUES_MISMATCH); + break; + case -1: + X509err(X509_F_X509_REQ_CHECK_PRIVATE_KEY, X509_R_KEY_TYPE_MISMATCH); + break; + case -2: +#ifndef OPENSSL_NO_EC + if (EVP_PKEY_id(k) == EVP_PKEY_EC) { + X509err(X509_F_X509_REQ_CHECK_PRIVATE_KEY, ERR_R_EC_LIB); + break; + } +#endif +#ifndef OPENSSL_NO_DH + if (EVP_PKEY_id(k) == EVP_PKEY_DH) { + /* No idea */ + X509err(X509_F_X509_REQ_CHECK_PRIVATE_KEY, + X509_R_CANT_CHECK_DH_KEY); + break; + } +#endif + X509err(X509_F_X509_REQ_CHECK_PRIVATE_KEY, X509_R_UNKNOWN_KEY_TYPE); + } + + EVP_PKEY_free(xk); + return (ok); +} + +/* + * It seems several organisations had the same idea of including a list of + * extensions in a certificate request. There are at least two OIDs that are + * used and there may be more: so the list is configurable. + */ + +static int ext_nid_list[] = { NID_ext_req, NID_ms_ext_req, NID_undef }; + +static int *ext_nids = ext_nid_list; + +int X509_REQ_extension_nid(int req_nid) +{ + int i, nid; + for (i = 0;; i++) { + nid = ext_nids[i]; + if (nid == NID_undef) + return 0; + else if (req_nid == nid) + return 1; + } +} + +int *X509_REQ_get_extension_nids(void) +{ + return ext_nids; +} + +void X509_REQ_set_extension_nids(int *nids) +{ + ext_nids = nids; +} + +STACK_OF(X509_EXTENSION) *X509_REQ_get_extensions(X509_REQ *req) +{ + X509_ATTRIBUTE *attr; + ASN1_TYPE *ext = NULL; + int idx, *pnid; + const unsigned char *p; + + if ((req == NULL) || !ext_nids) + return (NULL); + for (pnid = ext_nids; *pnid != NID_undef; pnid++) { + idx = X509_REQ_get_attr_by_NID(req, *pnid, -1); + if (idx == -1) + continue; + attr = X509_REQ_get_attr(req, idx); + ext = X509_ATTRIBUTE_get0_type(attr, 0); + break; + } + if (!ext || (ext->type != V_ASN1_SEQUENCE)) + return NULL; + p = ext->value.sequence->data; + return (STACK_OF(X509_EXTENSION) *) + ASN1_item_d2i(NULL, &p, ext->value.sequence->length, + ASN1_ITEM_rptr(X509_EXTENSIONS)); +} + +/* + * Add a STACK_OF extensions to a certificate request: allow alternative OIDs + * in case we want to create a non standard one. + */ + +int X509_REQ_add_extensions_nid(X509_REQ *req, STACK_OF(X509_EXTENSION) *exts, + int nid) +{ + int extlen; + int rv = 0; + unsigned char *ext = NULL; + /* Generate encoding of extensions */ + extlen = ASN1_item_i2d((ASN1_VALUE *)exts, &ext, + ASN1_ITEM_rptr(X509_EXTENSIONS)); + if (extlen <= 0) + return 0; + rv = X509_REQ_add1_attr_by_NID(req, nid, V_ASN1_SEQUENCE, ext, extlen); + OPENSSL_free(ext); + return rv; +} + +/* This is the normal usage: use the "official" OID */ +int X509_REQ_add_extensions(X509_REQ *req, STACK_OF(X509_EXTENSION) *exts) +{ + return X509_REQ_add_extensions_nid(req, exts, NID_ext_req); +} + +/* Request attribute functions */ + +int X509_REQ_get_attr_count(const X509_REQ *req) +{ + return X509at_get_attr_count(req->req_info.attributes); +} + +int X509_REQ_get_attr_by_NID(const X509_REQ *req, int nid, int lastpos) +{ + return X509at_get_attr_by_NID(req->req_info.attributes, nid, lastpos); +} + +int X509_REQ_get_attr_by_OBJ(const X509_REQ *req, const ASN1_OBJECT *obj, + int lastpos) +{ + return X509at_get_attr_by_OBJ(req->req_info.attributes, obj, lastpos); +} + +X509_ATTRIBUTE *X509_REQ_get_attr(const X509_REQ *req, int loc) +{ + return X509at_get_attr(req->req_info.attributes, loc); +} + +X509_ATTRIBUTE *X509_REQ_delete_attr(X509_REQ *req, int loc) +{ + return X509at_delete_attr(req->req_info.attributes, loc); +} + +int X509_REQ_add1_attr(X509_REQ *req, X509_ATTRIBUTE *attr) +{ + if (X509at_add1_attr(&req->req_info.attributes, attr)) + return 1; + return 0; +} + +int X509_REQ_add1_attr_by_OBJ(X509_REQ *req, + const ASN1_OBJECT *obj, int type, + const unsigned char *bytes, int len) +{ + if (X509at_add1_attr_by_OBJ(&req->req_info.attributes, obj, + type, bytes, len)) + return 1; + return 0; +} + +int X509_REQ_add1_attr_by_NID(X509_REQ *req, + int nid, int type, + const unsigned char *bytes, int len) +{ + if (X509at_add1_attr_by_NID(&req->req_info.attributes, nid, + type, bytes, len)) + return 1; + return 0; +} + +int X509_REQ_add1_attr_by_txt(X509_REQ *req, + const char *attrname, int type, + const unsigned char *bytes, int len) +{ + if (X509at_add1_attr_by_txt(&req->req_info.attributes, attrname, + type, bytes, len)) + return 1; + return 0; +} + +long X509_REQ_get_version(const X509_REQ *req) +{ + return ASN1_INTEGER_get(req->req_info.version); +} + +X509_NAME *X509_REQ_get_subject_name(const X509_REQ *req) +{ + return req->req_info.subject; +} + +void X509_REQ_get0_signature(const X509_REQ *req, const ASN1_BIT_STRING **psig, + const X509_ALGOR **palg) +{ + if (psig != NULL) + *psig = req->signature; + if (palg != NULL) + *palg = &req->sig_alg; +} + +int X509_REQ_get_signature_nid(const X509_REQ *req) +{ + return OBJ_obj2nid(req->sig_alg.algorithm); +} + +int i2d_re_X509_REQ_tbs(X509_REQ *req, unsigned char **pp) +{ + req->req_info.enc.modified = 1; + return i2d_X509_REQ_INFO(&req->req_info, pp); +} diff --git a/crypto/x509/x509_set.c b/crypto/x509/x509_set.c new file mode 100644 index 000000000..c0ea41883 --- /dev/null +++ b/crypto/x509/x509_set.c @@ -0,0 +1,159 @@ +/* + * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#include "internal/cryptlib.h" +#include +#include +#include +#include +#include "internal/x509_int.h" + +int X509_set_version(X509 *x, long version) +{ + if (x == NULL) + return (0); + if (version == 0) { + ASN1_INTEGER_free(x->cert_info.version); + x->cert_info.version = NULL; + return (1); + } + if (x->cert_info.version == NULL) { + if ((x->cert_info.version = ASN1_INTEGER_new()) == NULL) + return (0); + } + return (ASN1_INTEGER_set(x->cert_info.version, version)); +} + +int X509_set_serialNumber(X509 *x, ASN1_INTEGER *serial) +{ + ASN1_INTEGER *in; + + if (x == NULL) + return 0; + in = &x->cert_info.serialNumber; + if (in != serial) + return ASN1_STRING_copy(in, serial); + return 1; +} + +int X509_set_issuer_name(X509 *x, X509_NAME *name) +{ + if (x == NULL) + return (0); + return (X509_NAME_set(&x->cert_info.issuer, name)); +} + +int X509_set_subject_name(X509 *x, X509_NAME *name) +{ + if (x == NULL) + return (0); + return (X509_NAME_set(&x->cert_info.subject, name)); +} + +int x509_set1_time(ASN1_TIME **ptm, const ASN1_TIME *tm) +{ + ASN1_TIME *in; + in = *ptm; + if (in != tm) { + in = ASN1_STRING_dup(tm); + if (in != NULL) { + ASN1_TIME_free(*ptm); + *ptm = in; + } + } + return (in != NULL); +} + +int X509_set1_notBefore(X509 *x, const ASN1_TIME *tm) +{ + if (x == NULL) + return 0; + return x509_set1_time(&x->cert_info.validity.notBefore, tm); +} + +int X509_set1_notAfter(X509 *x, const ASN1_TIME *tm) +{ + if (x == NULL) + return 0; + return x509_set1_time(&x->cert_info.validity.notAfter, tm); +} + +int X509_set_pubkey(X509 *x, EVP_PKEY *pkey) +{ + if (x == NULL) + return (0); + return (X509_PUBKEY_set(&(x->cert_info.key), pkey)); +} + +int X509_up_ref(X509 *x) +{ + int i; + + if (CRYPTO_atomic_add(&x->references, 1, &i, x->lock) <= 0) + return 0; + + REF_PRINT_COUNT("X509", x); + REF_ASSERT_ISNT(i < 2); + return ((i > 1) ? 1 : 0); +} + +long X509_get_version(const X509 *x) +{ + return ASN1_INTEGER_get(x->cert_info.version); +} + +const ASN1_TIME *X509_get0_notBefore(const X509 *x) +{ + return x->cert_info.validity.notBefore; +} + +const ASN1_TIME *X509_get0_notAfter(const X509 *x) +{ + return x->cert_info.validity.notAfter; +} + +ASN1_TIME *X509_getm_notBefore(const X509 *x) +{ + return x->cert_info.validity.notBefore; +} + +ASN1_TIME *X509_getm_notAfter(const X509 *x) +{ + return x->cert_info.validity.notAfter; +} + +int X509_get_signature_type(const X509 *x) +{ + return EVP_PKEY_type(OBJ_obj2nid(x->sig_alg.algorithm)); +} + +X509_PUBKEY *X509_get_X509_PUBKEY(const X509 *x) +{ + return x->cert_info.key; +} + +const STACK_OF(X509_EXTENSION) *X509_get0_extensions(const X509 *x) +{ + return x->cert_info.extensions; +} + +void X509_get0_uids(const X509 *x, const ASN1_BIT_STRING **piuid, + const ASN1_BIT_STRING **psuid) +{ + if (piuid != NULL) + *piuid = x->cert_info.issuerUID; + if (psuid != NULL) + *psuid = x->cert_info.subjectUID; +} + +const X509_ALGOR *X509_get0_tbs_sigalg(const X509 *x) +{ + return &x->cert_info.signature; +} diff --git a/crypto/x509/x509_trs.c b/crypto/x509/x509_trs.c new file mode 100644 index 000000000..a9bb88d1e --- /dev/null +++ b/crypto/x509/x509_trs.c @@ -0,0 +1,298 @@ +/* + * Copyright 1999-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#include "internal/cryptlib.h" +#include +#include "internal/x509_int.h" + +static int tr_cmp(const X509_TRUST *const *a, const X509_TRUST *const *b); +static void trtable_free(X509_TRUST *p); + +static int trust_1oidany(X509_TRUST *trust, X509 *x, int flags); +static int trust_1oid(X509_TRUST *trust, X509 *x, int flags); +static int trust_compat(X509_TRUST *trust, X509 *x, int flags); + +static int obj_trust(int id, X509 *x, int flags); +static int (*default_trust) (int id, X509 *x, int flags) = obj_trust; + +/* + * WARNING: the following table should be kept in order of trust and without + * any gaps so we can just subtract the minimum trust value to get an index + * into the table + */ + +static X509_TRUST trstandard[] = { + {X509_TRUST_COMPAT, 0, trust_compat, "compatible", 0, NULL}, + {X509_TRUST_SSL_CLIENT, 0, trust_1oidany, "SSL Client", NID_client_auth, + NULL}, + {X509_TRUST_SSL_SERVER, 0, trust_1oidany, "SSL Server", NID_server_auth, + NULL}, + {X509_TRUST_EMAIL, 0, trust_1oidany, "S/MIME email", NID_email_protect, + NULL}, + {X509_TRUST_OBJECT_SIGN, 0, trust_1oidany, "Object Signer", NID_code_sign, + NULL}, + {X509_TRUST_OCSP_SIGN, 0, trust_1oid, "OCSP responder", NID_OCSP_sign, + NULL}, + {X509_TRUST_OCSP_REQUEST, 0, trust_1oid, "OCSP request", NID_ad_OCSP, + NULL}, + {X509_TRUST_TSA, 0, trust_1oidany, "TSA server", NID_time_stamp, NULL} +}; + +#define X509_TRUST_COUNT OSSL_NELEM(trstandard) + +static STACK_OF(X509_TRUST) *trtable = NULL; + +static int tr_cmp(const X509_TRUST *const *a, const X509_TRUST *const *b) +{ + return (*a)->trust - (*b)->trust; +} + +int (*X509_TRUST_set_default(int (*trust) (int, X509 *, int))) (int, X509 *, + int) { + int (*oldtrust) (int, X509 *, int); + oldtrust = default_trust; + default_trust = trust; + return oldtrust; +} + +int X509_check_trust(X509 *x, int id, int flags) +{ + X509_TRUST *pt; + int idx; + + /* We get this as a default value */ + if (id == X509_TRUST_DEFAULT) + return obj_trust(NID_anyExtendedKeyUsage, x, + flags | X509_TRUST_DO_SS_COMPAT); + idx = X509_TRUST_get_by_id(id); + if (idx == -1) + return default_trust(id, x, flags); + pt = X509_TRUST_get0(idx); + return pt->check_trust(pt, x, flags); +} + +int X509_TRUST_get_count(void) +{ + if (!trtable) + return X509_TRUST_COUNT; + return sk_X509_TRUST_num(trtable) + X509_TRUST_COUNT; +} + +X509_TRUST *X509_TRUST_get0(int idx) +{ + if (idx < 0) + return NULL; + if (idx < (int)X509_TRUST_COUNT) + return trstandard + idx; + return sk_X509_TRUST_value(trtable, idx - X509_TRUST_COUNT); +} + +int X509_TRUST_get_by_id(int id) +{ + X509_TRUST tmp; + int idx; + if ((id >= X509_TRUST_MIN) && (id <= X509_TRUST_MAX)) + return id - X509_TRUST_MIN; + tmp.trust = id; + if (!trtable) + return -1; + idx = sk_X509_TRUST_find(trtable, &tmp); + if (idx == -1) + return -1; + return idx + X509_TRUST_COUNT; +} + +int X509_TRUST_set(int *t, int trust) +{ + if (X509_TRUST_get_by_id(trust) == -1) { + X509err(X509_F_X509_TRUST_SET, X509_R_INVALID_TRUST); + return 0; + } + *t = trust; + return 1; +} + +int X509_TRUST_add(int id, int flags, int (*ck) (X509_TRUST *, X509 *, int), + const char *name, int arg1, void *arg2) +{ + int idx; + X509_TRUST *trtmp; + /* + * This is set according to what we change: application can't set it + */ + flags &= ~X509_TRUST_DYNAMIC; + /* This will always be set for application modified trust entries */ + flags |= X509_TRUST_DYNAMIC_NAME; + /* Get existing entry if any */ + idx = X509_TRUST_get_by_id(id); + /* Need a new entry */ + if (idx == -1) { + if ((trtmp = OPENSSL_malloc(sizeof(*trtmp))) == NULL) { + X509err(X509_F_X509_TRUST_ADD, ERR_R_MALLOC_FAILURE); + return 0; + } + trtmp->flags = X509_TRUST_DYNAMIC; + } else + trtmp = X509_TRUST_get0(idx); + + /* OPENSSL_free existing name if dynamic */ + if (trtmp->flags & X509_TRUST_DYNAMIC_NAME) + OPENSSL_free(trtmp->name); + /* dup supplied name */ + if ((trtmp->name = OPENSSL_strdup(name)) == NULL) { + X509err(X509_F_X509_TRUST_ADD, ERR_R_MALLOC_FAILURE); + goto err; + } + /* Keep the dynamic flag of existing entry */ + trtmp->flags &= X509_TRUST_DYNAMIC; + /* Set all other flags */ + trtmp->flags |= flags; + + trtmp->trust = id; + trtmp->check_trust = ck; + trtmp->arg1 = arg1; + trtmp->arg2 = arg2; + + /* If its a new entry manage the dynamic table */ + if (idx == -1) { + if (trtable == NULL + && (trtable = sk_X509_TRUST_new(tr_cmp)) == NULL) { + X509err(X509_F_X509_TRUST_ADD, ERR_R_MALLOC_FAILURE); + goto err;; + } + if (!sk_X509_TRUST_push(trtable, trtmp)) { + X509err(X509_F_X509_TRUST_ADD, ERR_R_MALLOC_FAILURE); + goto err; + } + } + return 1; + err: + if (idx == -1) { + OPENSSL_free(trtmp->name); + OPENSSL_free(trtmp); + } + return 0; +} + +static void trtable_free(X509_TRUST *p) +{ + if (!p) + return; + if (p->flags & X509_TRUST_DYNAMIC) { + if (p->flags & X509_TRUST_DYNAMIC_NAME) + OPENSSL_free(p->name); + OPENSSL_free(p); + } +} + +void X509_TRUST_cleanup(void) +{ + sk_X509_TRUST_pop_free(trtable, trtable_free); + trtable = NULL; +} + +int X509_TRUST_get_flags(const X509_TRUST *xp) +{ + return xp->flags; +} + +char *X509_TRUST_get0_name(const X509_TRUST *xp) +{ + return xp->name; +} + +int X509_TRUST_get_trust(const X509_TRUST *xp) +{ + return xp->trust; +} + +static int trust_1oidany(X509_TRUST *trust, X509 *x, int flags) +{ + /* + * Declare the chain verified if the desired trust OID is not rejected in + * any auxiliary trust info for this certificate, and the OID is either + * expressly trusted, or else either "anyEKU" is trusted, or the + * certificate is self-signed. + */ + flags |= X509_TRUST_DO_SS_COMPAT | X509_TRUST_OK_ANY_EKU; + return obj_trust(trust->arg1, x, flags); +} + +static int trust_1oid(X509_TRUST *trust, X509 *x, int flags) +{ + /* + * Declare the chain verified only if the desired trust OID is not + * rejected and is expressly trusted. Neither "anyEKU" nor "compat" + * trust in self-signed certificates apply. + */ + flags &= ~(X509_TRUST_DO_SS_COMPAT | X509_TRUST_OK_ANY_EKU); + return obj_trust(trust->arg1, x, flags); +} + +static int trust_compat(X509_TRUST *trust, X509 *x, int flags) +{ + /* Call for side-effect of computing hash and caching extensions */ + X509_check_purpose(x, -1, 0); + if ((flags & X509_TRUST_NO_SS_COMPAT) == 0 && x->ex_flags & EXFLAG_SS) + return X509_TRUST_TRUSTED; + else + return X509_TRUST_UNTRUSTED; +} + +static int obj_trust(int id, X509 *x, int flags) +{ + X509_CERT_AUX *ax = x->aux; + int i; + + if (ax && ax->reject) { + for (i = 0; i < sk_ASN1_OBJECT_num(ax->reject); i++) { + ASN1_OBJECT *obj = sk_ASN1_OBJECT_value(ax->reject, i); + int nid = OBJ_obj2nid(obj); + + if (nid == id || (nid == NID_anyExtendedKeyUsage && + (flags & X509_TRUST_OK_ANY_EKU))) + return X509_TRUST_REJECTED; + } + } + + if (ax && ax->trust) { + for (i = 0; i < sk_ASN1_OBJECT_num(ax->trust); i++) { + ASN1_OBJECT *obj = sk_ASN1_OBJECT_value(ax->trust, i); + int nid = OBJ_obj2nid(obj); + + if (nid == id || (nid == NID_anyExtendedKeyUsage && + (flags & X509_TRUST_OK_ANY_EKU))) + return X509_TRUST_TRUSTED; + } + /* + * Reject when explicit trust EKU are set and none match. + * + * Returning untrusted is enough for for full chains that end in + * self-signed roots, because when explicit trust is specified it + * suppresses the default blanket trust of self-signed objects. + * + * But for partial chains, this is not enough, because absent a similar + * trust-self-signed policy, non matching EKUs are indistinguishable + * from lack of EKU constraints. + * + * Therefore, failure to match any trusted purpose must trigger an + * explicit reject. + */ + return X509_TRUST_REJECTED; + } + + if ((flags & X509_TRUST_DO_SS_COMPAT) == 0) + return X509_TRUST_UNTRUSTED; + + /* + * Not rejected, and there is no list of accepted uses, try compat. + */ + return trust_compat(NULL, x, flags); +} diff --git a/crypto/x509/x509_txt.c b/crypto/x509/x509_txt.c new file mode 100644 index 000000000..66e5fcd02 --- /dev/null +++ b/crypto/x509/x509_txt.c @@ -0,0 +1,177 @@ +/* + * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#include +#include + +#include "internal/cryptlib.h" +#include +#include +#include +#include +#include +#include + +const char *X509_verify_cert_error_string(long n) +{ + switch ((int)n) { + case X509_V_OK: + return ("ok"); + case X509_V_ERR_UNSPECIFIED: + return ("unspecified certificate verification error"); + case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT: + return ("unable to get issuer certificate"); + case X509_V_ERR_UNABLE_TO_GET_CRL: + return ("unable to get certificate CRL"); + case X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE: + return ("unable to decrypt certificate's signature"); + case X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE: + return ("unable to decrypt CRL's signature"); + case X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY: + return ("unable to decode issuer public key"); + case X509_V_ERR_CERT_SIGNATURE_FAILURE: + return ("certificate signature failure"); + case X509_V_ERR_CRL_SIGNATURE_FAILURE: + return ("CRL signature failure"); + case X509_V_ERR_CERT_NOT_YET_VALID: + return ("certificate is not yet valid"); + case X509_V_ERR_CERT_HAS_EXPIRED: + return ("certificate has expired"); + case X509_V_ERR_CRL_NOT_YET_VALID: + return ("CRL is not yet valid"); + case X509_V_ERR_CRL_HAS_EXPIRED: + return ("CRL has expired"); + case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD: + return ("format error in certificate's notBefore field"); + case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD: + return ("format error in certificate's notAfter field"); + case X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD: + return ("format error in CRL's lastUpdate field"); + case X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD: + return ("format error in CRL's nextUpdate field"); + case X509_V_ERR_OUT_OF_MEM: + return ("out of memory"); + case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT: + return ("self signed certificate"); + case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN: + return ("self signed certificate in certificate chain"); + case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY: + return ("unable to get local issuer certificate"); + case X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE: + return ("unable to verify the first certificate"); + case X509_V_ERR_CERT_CHAIN_TOO_LONG: + return ("certificate chain too long"); + case X509_V_ERR_CERT_REVOKED: + return ("certificate revoked"); + case X509_V_ERR_INVALID_CA: + return ("invalid CA certificate"); + case X509_V_ERR_PATH_LENGTH_EXCEEDED: + return ("path length constraint exceeded"); + case X509_V_ERR_INVALID_PURPOSE: + return ("unsupported certificate purpose"); + case X509_V_ERR_CERT_UNTRUSTED: + return ("certificate not trusted"); + case X509_V_ERR_CERT_REJECTED: + return ("certificate rejected"); + case X509_V_ERR_SUBJECT_ISSUER_MISMATCH: + return ("subject issuer mismatch"); + case X509_V_ERR_AKID_SKID_MISMATCH: + return ("authority and subject key identifier mismatch"); + case X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH: + return ("authority and issuer serial number mismatch"); + case X509_V_ERR_KEYUSAGE_NO_CERTSIGN: + return ("key usage does not include certificate signing"); + case X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER: + return ("unable to get CRL issuer certificate"); + case X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION: + return ("unhandled critical extension"); + case X509_V_ERR_KEYUSAGE_NO_CRL_SIGN: + return ("key usage does not include CRL signing"); + case X509_V_ERR_UNHANDLED_CRITICAL_CRL_EXTENSION: + return ("unhandled critical CRL extension"); + case X509_V_ERR_INVALID_NON_CA: + return ("invalid non-CA certificate (has CA markings)"); + case X509_V_ERR_PROXY_PATH_LENGTH_EXCEEDED: + return ("proxy path length constraint exceeded"); + case X509_V_ERR_KEYUSAGE_NO_DIGITAL_SIGNATURE: + return ("key usage does not include digital signature"); + case X509_V_ERR_PROXY_CERTIFICATES_NOT_ALLOWED: + return + ("proxy certificates not allowed, please set the appropriate flag"); + case X509_V_ERR_INVALID_EXTENSION: + return ("invalid or inconsistent certificate extension"); + case X509_V_ERR_INVALID_POLICY_EXTENSION: + return ("invalid or inconsistent certificate policy extension"); + case X509_V_ERR_NO_EXPLICIT_POLICY: + return ("no explicit policy"); + case X509_V_ERR_DIFFERENT_CRL_SCOPE: + return ("Different CRL scope"); + case X509_V_ERR_UNSUPPORTED_EXTENSION_FEATURE: + return ("Unsupported extension feature"); + case X509_V_ERR_UNNESTED_RESOURCE: + return ("RFC 3779 resource not subset of parent's resources"); + case X509_V_ERR_PERMITTED_VIOLATION: + return ("permitted subtree violation"); + case X509_V_ERR_EXCLUDED_VIOLATION: + return ("excluded subtree violation"); + case X509_V_ERR_SUBTREE_MINMAX: + return ("name constraints minimum and maximum not supported"); + case X509_V_ERR_APPLICATION_VERIFICATION: + return ("application verification failure"); + case X509_V_ERR_UNSUPPORTED_CONSTRAINT_TYPE: + return ("unsupported name constraint type"); + case X509_V_ERR_UNSUPPORTED_CONSTRAINT_SYNTAX: + return ("unsupported or invalid name constraint syntax"); + case X509_V_ERR_UNSUPPORTED_NAME_SYNTAX: + return ("unsupported or invalid name syntax"); + case X509_V_ERR_CRL_PATH_VALIDATION_ERROR: + return ("CRL path validation error"); + case X509_V_ERR_PATH_LOOP: + return ("Path Loop"); + case X509_V_ERR_SUITE_B_INVALID_VERSION: + return ("Suite B: certificate version invalid"); + case X509_V_ERR_SUITE_B_INVALID_ALGORITHM: + return ("Suite B: invalid public key algorithm"); + case X509_V_ERR_SUITE_B_INVALID_CURVE: + return ("Suite B: invalid ECC curve"); + case X509_V_ERR_SUITE_B_INVALID_SIGNATURE_ALGORITHM: + return ("Suite B: invalid signature algorithm"); + case X509_V_ERR_SUITE_B_LOS_NOT_ALLOWED: + return ("Suite B: curve not allowed for this LOS"); + case X509_V_ERR_SUITE_B_CANNOT_SIGN_P_384_WITH_P_256: + return ("Suite B: cannot sign P-384 with P-256"); + case X509_V_ERR_HOSTNAME_MISMATCH: + return ("Hostname mismatch"); + case X509_V_ERR_EMAIL_MISMATCH: + return ("Email address mismatch"); + case X509_V_ERR_IP_ADDRESS_MISMATCH: + return ("IP address mismatch"); + case X509_V_ERR_DANE_NO_MATCH: + return ("No matching DANE TLSA records"); + case X509_V_ERR_EE_KEY_TOO_SMALL: + return ("EE certificate key too weak"); + case X509_V_ERR_CA_KEY_TOO_SMALL: + return ("CA certificate key too weak"); + case X509_V_ERR_CA_MD_TOO_WEAK: + return ("CA signature digest algorithm too weak"); + case X509_V_ERR_INVALID_CALL: + return ("Invalid certificate verification context"); + case X509_V_ERR_STORE_LOOKUP: + return ("Issuer certificate lookup error"); + case X509_V_ERR_NO_VALID_SCTS: + return ("Certificate Transparency required, but no valid SCTs found"); + case X509_V_ERR_PROXY_SUBJECT_NAME_VIOLATION: + return ("proxy subject name violation"); + + default: + /* Printing an error number into a static buffer is not thread-safe */ + return ("unknown certificate verification error"); + } +} diff --git a/crypto/x509/x509_v3.c b/crypto/x509/x509_v3.c new file mode 100644 index 000000000..ad126efeb --- /dev/null +++ b/crypto/x509/x509_v3.c @@ -0,0 +1,234 @@ +/* + * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#include +#include "internal/cryptlib.h" +#include +#include +#include +#include +#include +#include "x509_lcl.h" + +int X509v3_get_ext_count(const STACK_OF(X509_EXTENSION) *x) +{ + if (x == NULL) + return (0); + return (sk_X509_EXTENSION_num(x)); +} + +int X509v3_get_ext_by_NID(const STACK_OF(X509_EXTENSION) *x, int nid, + int lastpos) +{ + ASN1_OBJECT *obj; + + obj = OBJ_nid2obj(nid); + if (obj == NULL) + return (-2); + return (X509v3_get_ext_by_OBJ(x, obj, lastpos)); +} + +int X509v3_get_ext_by_OBJ(const STACK_OF(X509_EXTENSION) *sk, + const ASN1_OBJECT *obj, int lastpos) +{ + int n; + X509_EXTENSION *ex; + + if (sk == NULL) + return (-1); + lastpos++; + if (lastpos < 0) + lastpos = 0; + n = sk_X509_EXTENSION_num(sk); + for (; lastpos < n; lastpos++) { + ex = sk_X509_EXTENSION_value(sk, lastpos); + if (OBJ_cmp(ex->object, obj) == 0) + return (lastpos); + } + return (-1); +} + +int X509v3_get_ext_by_critical(const STACK_OF(X509_EXTENSION) *sk, int crit, + int lastpos) +{ + int n; + X509_EXTENSION *ex; + + if (sk == NULL) + return (-1); + lastpos++; + if (lastpos < 0) + lastpos = 0; + n = sk_X509_EXTENSION_num(sk); + for (; lastpos < n; lastpos++) { + ex = sk_X509_EXTENSION_value(sk, lastpos); + if (((ex->critical > 0) && crit) || ((ex->critical <= 0) && !crit)) + return (lastpos); + } + return (-1); +} + +X509_EXTENSION *X509v3_get_ext(const STACK_OF(X509_EXTENSION) *x, int loc) +{ + if (x == NULL || sk_X509_EXTENSION_num(x) <= loc || loc < 0) + return NULL; + else + return sk_X509_EXTENSION_value(x, loc); +} + +X509_EXTENSION *X509v3_delete_ext(STACK_OF(X509_EXTENSION) *x, int loc) +{ + X509_EXTENSION *ret; + + if (x == NULL || sk_X509_EXTENSION_num(x) <= loc || loc < 0) + return (NULL); + ret = sk_X509_EXTENSION_delete(x, loc); + return (ret); +} + +STACK_OF(X509_EXTENSION) *X509v3_add_ext(STACK_OF(X509_EXTENSION) **x, + X509_EXTENSION *ex, int loc) +{ + X509_EXTENSION *new_ex = NULL; + int n; + STACK_OF(X509_EXTENSION) *sk = NULL; + + if (x == NULL) { + X509err(X509_F_X509V3_ADD_EXT, ERR_R_PASSED_NULL_PARAMETER); + goto err2; + } + + if (*x == NULL) { + if ((sk = sk_X509_EXTENSION_new_null()) == NULL) + goto err; + } else + sk = *x; + + n = sk_X509_EXTENSION_num(sk); + if (loc > n) + loc = n; + else if (loc < 0) + loc = n; + + if ((new_ex = X509_EXTENSION_dup(ex)) == NULL) + goto err2; + if (!sk_X509_EXTENSION_insert(sk, new_ex, loc)) + goto err; + if (*x == NULL) + *x = sk; + return (sk); + err: + X509err(X509_F_X509V3_ADD_EXT, ERR_R_MALLOC_FAILURE); + err2: + X509_EXTENSION_free(new_ex); + sk_X509_EXTENSION_free(sk); + return (NULL); +} + +X509_EXTENSION *X509_EXTENSION_create_by_NID(X509_EXTENSION **ex, int nid, + int crit, + ASN1_OCTET_STRING *data) +{ + ASN1_OBJECT *obj; + X509_EXTENSION *ret; + + obj = OBJ_nid2obj(nid); + if (obj == NULL) { + X509err(X509_F_X509_EXTENSION_CREATE_BY_NID, X509_R_UNKNOWN_NID); + return (NULL); + } + ret = X509_EXTENSION_create_by_OBJ(ex, obj, crit, data); + if (ret == NULL) + ASN1_OBJECT_free(obj); + return (ret); +} + +X509_EXTENSION *X509_EXTENSION_create_by_OBJ(X509_EXTENSION **ex, + const ASN1_OBJECT *obj, int crit, + ASN1_OCTET_STRING *data) +{ + X509_EXTENSION *ret; + + if ((ex == NULL) || (*ex == NULL)) { + if ((ret = X509_EXTENSION_new()) == NULL) { + X509err(X509_F_X509_EXTENSION_CREATE_BY_OBJ, + ERR_R_MALLOC_FAILURE); + return (NULL); + } + } else + ret = *ex; + + if (!X509_EXTENSION_set_object(ret, obj)) + goto err; + if (!X509_EXTENSION_set_critical(ret, crit)) + goto err; + if (!X509_EXTENSION_set_data(ret, data)) + goto err; + + if ((ex != NULL) && (*ex == NULL)) + *ex = ret; + return (ret); + err: + if ((ex == NULL) || (ret != *ex)) + X509_EXTENSION_free(ret); + return (NULL); +} + +int X509_EXTENSION_set_object(X509_EXTENSION *ex, const ASN1_OBJECT *obj) +{ + if ((ex == NULL) || (obj == NULL)) + return (0); + ASN1_OBJECT_free(ex->object); + ex->object = OBJ_dup(obj); + return ex->object != NULL; +} + +int X509_EXTENSION_set_critical(X509_EXTENSION *ex, int crit) +{ + if (ex == NULL) + return (0); + ex->critical = (crit) ? 0xFF : -1; + return (1); +} + +int X509_EXTENSION_set_data(X509_EXTENSION *ex, ASN1_OCTET_STRING *data) +{ + int i; + + if (ex == NULL) + return (0); + i = ASN1_OCTET_STRING_set(&ex->value, data->data, data->length); + if (!i) + return (0); + return (1); +} + +ASN1_OBJECT *X509_EXTENSION_get_object(X509_EXTENSION *ex) +{ + if (ex == NULL) + return (NULL); + return (ex->object); +} + +ASN1_OCTET_STRING *X509_EXTENSION_get_data(X509_EXTENSION *ex) +{ + if (ex == NULL) + return (NULL); + return &ex->value; +} + +int X509_EXTENSION_get_critical(const X509_EXTENSION *ex) +{ + if (ex == NULL) + return (0); + if (ex->critical > 0) + return 1; + return 0; +} diff --git a/crypto/x509/x509_vfy.c b/crypto/x509/x509_vfy.c new file mode 100644 index 000000000..ebc442400 --- /dev/null +++ b/crypto/x509/x509_vfy.c @@ -0,0 +1,3275 @@ +/* + * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#include +#include +#include + +#include "internal/cryptlib.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "x509_lcl.h" + +/* CRL score values */ + +/* No unhandled critical extensions */ + +#define CRL_SCORE_NOCRITICAL 0x100 + +/* certificate is within CRL scope */ + +#define CRL_SCORE_SCOPE 0x080 + +/* CRL times valid */ + +#define CRL_SCORE_TIME 0x040 + +/* Issuer name matches certificate */ + +#define CRL_SCORE_ISSUER_NAME 0x020 + +/* If this score or above CRL is probably valid */ + +#define CRL_SCORE_VALID (CRL_SCORE_NOCRITICAL|CRL_SCORE_TIME|CRL_SCORE_SCOPE) + +/* CRL issuer is certificate issuer */ + +#define CRL_SCORE_ISSUER_CERT 0x018 + +/* CRL issuer is on certificate path */ + +#define CRL_SCORE_SAME_PATH 0x008 + +/* CRL issuer matches CRL AKID */ + +#define CRL_SCORE_AKID 0x004 + +/* Have a delta CRL with valid times */ + +#define CRL_SCORE_TIME_DELTA 0x002 + +static int build_chain(X509_STORE_CTX *ctx); +static int verify_chain(X509_STORE_CTX *ctx); +static int dane_verify(X509_STORE_CTX *ctx); +static int null_callback(int ok, X509_STORE_CTX *e); +static int check_issued(X509_STORE_CTX *ctx, X509 *x, X509 *issuer); +static X509 *find_issuer(X509_STORE_CTX *ctx, STACK_OF(X509) *sk, X509 *x); +static int check_chain_extensions(X509_STORE_CTX *ctx); +static int check_name_constraints(X509_STORE_CTX *ctx); +static int check_id(X509_STORE_CTX *ctx); +static int check_trust(X509_STORE_CTX *ctx, int num_untrusted); +static int check_revocation(X509_STORE_CTX *ctx); +static int check_cert(X509_STORE_CTX *ctx); +static int check_policy(X509_STORE_CTX *ctx); +static int get_issuer_sk(X509 **issuer, X509_STORE_CTX *ctx, X509 *x); +static int check_dane_issuer(X509_STORE_CTX *ctx, int depth); +static int check_key_level(X509_STORE_CTX *ctx, X509 *cert); +static int check_sig_level(X509_STORE_CTX *ctx, X509 *cert); + +static int get_crl_score(X509_STORE_CTX *ctx, X509 **pissuer, + unsigned int *preasons, X509_CRL *crl, X509 *x); +static int get_crl_delta(X509_STORE_CTX *ctx, + X509_CRL **pcrl, X509_CRL **pdcrl, X509 *x); +static void get_delta_sk(X509_STORE_CTX *ctx, X509_CRL **dcrl, + int *pcrl_score, X509_CRL *base, + STACK_OF(X509_CRL) *crls); +static void crl_akid_check(X509_STORE_CTX *ctx, X509_CRL *crl, X509 **pissuer, + int *pcrl_score); +static int crl_crldp_check(X509 *x, X509_CRL *crl, int crl_score, + unsigned int *preasons); +static int check_crl_path(X509_STORE_CTX *ctx, X509 *x); +static int check_crl_chain(X509_STORE_CTX *ctx, + STACK_OF(X509) *cert_path, + STACK_OF(X509) *crl_path); + +static int internal_verify(X509_STORE_CTX *ctx); + +static int null_callback(int ok, X509_STORE_CTX *e) +{ + return ok; +} + +/* Return 1 is a certificate is self signed */ +static int cert_self_signed(X509 *x) +{ + /* + * FIXME: x509v3_cache_extensions() needs to detect more failures and not + * set EXFLAG_SET when that happens. Especially, if the failures are + * parse errors, rather than memory pressure! + */ + X509_check_purpose(x, -1, 0); + if (x->ex_flags & EXFLAG_SS) + return 1; + else + return 0; +} + +/* Given a certificate try and find an exact match in the store */ + +static X509 *lookup_cert_match(X509_STORE_CTX *ctx, X509 *x) +{ + STACK_OF(X509) *certs; + X509 *xtmp = NULL; + int i; + /* Lookup all certs with matching subject name */ + certs = ctx->lookup_certs(ctx, X509_get_subject_name(x)); + if (certs == NULL) + return NULL; + /* Look for exact match */ + for (i = 0; i < sk_X509_num(certs); i++) { + xtmp = sk_X509_value(certs, i); + if (!X509_cmp(xtmp, x)) + break; + } + if (i < sk_X509_num(certs)) + X509_up_ref(xtmp); + else + xtmp = NULL; + sk_X509_pop_free(certs, X509_free); + return xtmp; +} + +/*- + * Inform the verify callback of an error. + * If B is not NULL it is the error cert, otherwise use the chain cert at + * B. + * If B is not X509_V_OK, that's the error value, otherwise leave + * unchanged (presumably set by the caller). + * + * Returns 0 to abort verification with an error, non-zero to continue. + */ +static int verify_cb_cert(X509_STORE_CTX *ctx, X509 *x, int depth, int err) +{ + ctx->error_depth = depth; + ctx->current_cert = (x != NULL) ? x : sk_X509_value(ctx->chain, depth); + if (err != X509_V_OK) + ctx->error = err; + return ctx->verify_cb(0, ctx); +} + +/*- + * Inform the verify callback of an error, CRL-specific variant. Here, the + * error depth and certificate are already set, we just specify the error + * number. + * + * Returns 0 to abort verification with an error, non-zero to continue. + */ +static int verify_cb_crl(X509_STORE_CTX *ctx, int err) +{ + ctx->error = err; + return ctx->verify_cb(0, ctx); +} + +static int check_auth_level(X509_STORE_CTX *ctx) +{ + int i; + int num = sk_X509_num(ctx->chain); + + if (ctx->param->auth_level <= 0) + return 1; + + for (i = 0; i < num; ++i) { + X509 *cert = sk_X509_value(ctx->chain, i); + + /* + * We've already checked the security of the leaf key, so here we only + * check the security of issuer keys. + */ + if (i > 0 && !check_key_level(ctx, cert) && + verify_cb_cert(ctx, cert, i, X509_V_ERR_CA_KEY_TOO_SMALL) == 0) + return 0; + /* + * We also check the signature algorithm security of all certificates + * except those of the trust anchor at index num-1. + */ + if (i < num - 1 && !check_sig_level(ctx, cert) && + verify_cb_cert(ctx, cert, i, X509_V_ERR_CA_MD_TOO_WEAK) == 0) + return 0; + } + return 1; +} + +static int verify_chain(X509_STORE_CTX *ctx) +{ + int err; + int ok; + + /* + * Before either returning with an error, or continuing with CRL checks, + * instantiate chain public key parameters. + */ + if ((ok = build_chain(ctx)) == 0 || + (ok = check_chain_extensions(ctx)) == 0 || + (ok = check_auth_level(ctx)) == 0 || + (ok = check_name_constraints(ctx)) == 0 || + (ok = check_id(ctx)) == 0 || 1) + X509_get_pubkey_parameters(NULL, ctx->chain); + if (ok == 0 || (ok = ctx->check_revocation(ctx)) == 0) + return ok; + + err = X509_chain_check_suiteb(&ctx->error_depth, NULL, ctx->chain, + ctx->param->flags); + if (err != X509_V_OK) { + if ((ok = verify_cb_cert(ctx, NULL, ctx->error_depth, err)) == 0) + return ok; + } + + /* Verify chain signatures and expiration times */ + ok = (ctx->verify != NULL) ? ctx->verify(ctx) : internal_verify(ctx); + if (!ok) + return ok; + +#ifndef OPENSSL_NO_RFC3779 + /* RFC 3779 path validation, now that CRL check has been done */ + if ((ok = X509v3_asid_validate_path(ctx)) == 0) + return ok; + if ((ok = X509v3_addr_validate_path(ctx)) == 0) + return ok; +#endif + + /* If we get this far evaluate policies */ + if (ctx->param->flags & X509_V_FLAG_POLICY_CHECK) + ok = ctx->check_policy(ctx); + return ok; +} + +int X509_verify_cert(X509_STORE_CTX *ctx) +{ + SSL_DANE *dane = ctx->dane; + int ret; + + if (ctx->cert == NULL) { + X509err(X509_F_X509_VERIFY_CERT, X509_R_NO_CERT_SET_FOR_US_TO_VERIFY); + ctx->error = X509_V_ERR_INVALID_CALL; + return -1; + } + + if (ctx->chain != NULL) { + /* + * This X509_STORE_CTX has already been used to verify a cert. We + * cannot do another one. + */ + X509err(X509_F_X509_VERIFY_CERT, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED); + ctx->error = X509_V_ERR_INVALID_CALL; + return -1; + } + + /* + * first we make sure the chain we are going to build is present and that + * the first entry is in place + */ + if (((ctx->chain = sk_X509_new_null()) == NULL) || + (!sk_X509_push(ctx->chain, ctx->cert))) { + X509err(X509_F_X509_VERIFY_CERT, ERR_R_MALLOC_FAILURE); + ctx->error = X509_V_ERR_OUT_OF_MEM; + return -1; + } + X509_up_ref(ctx->cert); + ctx->num_untrusted = 1; + + /* If the peer's public key is too weak, we can stop early. */ + if (!check_key_level(ctx, ctx->cert) && + !verify_cb_cert(ctx, ctx->cert, 0, X509_V_ERR_EE_KEY_TOO_SMALL)) + return 0; + + if (DANETLS_ENABLED(dane)) + ret = dane_verify(ctx); + else + ret = verify_chain(ctx); + + /* + * Safety-net. If we are returning an error, we must also set ctx->error, + * so that the chain is not considered verified should the error be ignored + * (e.g. TLS with SSL_VERIFY_NONE). + */ + if (ret <= 0 && ctx->error == X509_V_OK) + ctx->error = X509_V_ERR_UNSPECIFIED; + return ret; +} + +/* + * Given a STACK_OF(X509) find the issuer of cert (if any) + */ +static X509 *find_issuer(X509_STORE_CTX *ctx, STACK_OF(X509) *sk, X509 *x) +{ + int i; + X509 *issuer, *rv = NULL; + + for (i = 0; i < sk_X509_num(sk); i++) { + issuer = sk_X509_value(sk, i); + if (ctx->check_issued(ctx, x, issuer)) { + rv = issuer; + if (x509_check_cert_time(ctx, rv, -1)) + break; + } + } + return rv; +} + +/* Given a possible certificate and issuer check them */ + +static int check_issued(X509_STORE_CTX *ctx, X509 *x, X509 *issuer) +{ + int ret; + if (x == issuer) + return cert_self_signed(x); + ret = X509_check_issued(issuer, x); + if (ret == X509_V_OK) { + int i; + X509 *ch; + /* Special case: single self signed certificate */ + if (cert_self_signed(x) && sk_X509_num(ctx->chain) == 1) + return 1; + for (i = 0; i < sk_X509_num(ctx->chain); i++) { + ch = sk_X509_value(ctx->chain, i); + if (ch == issuer || !X509_cmp(ch, issuer)) { + ret = X509_V_ERR_PATH_LOOP; + break; + } + } + } + + return (ret == X509_V_OK); +} + +/* Alternative lookup method: look from a STACK stored in other_ctx */ + +static int get_issuer_sk(X509 **issuer, X509_STORE_CTX *ctx, X509 *x) +{ + *issuer = find_issuer(ctx, ctx->other_ctx, x); + if (*issuer) { + X509_up_ref(*issuer); + return 1; + } else + return 0; +} + +static STACK_OF(X509) *lookup_certs_sk(X509_STORE_CTX *ctx, X509_NAME *nm) +{ + STACK_OF(X509) *sk = NULL; + X509 *x; + int i; + for (i = 0; i < sk_X509_num(ctx->other_ctx); i++) { + x = sk_X509_value(ctx->other_ctx, i); + if (X509_NAME_cmp(nm, X509_get_subject_name(x)) == 0) { + if (sk == NULL) + sk = sk_X509_new_null(); + if (sk == NULL || sk_X509_push(sk, x) == 0) { + sk_X509_pop_free(sk, X509_free); + return NULL; + } + X509_up_ref(x); + } + } + return sk; +} + +/* + * Check EE or CA certificate purpose. For trusted certificates explicit local + * auxiliary trust can be used to override EKU-restrictions. + */ +static int check_purpose(X509_STORE_CTX *ctx, X509 *x, int purpose, int depth, + int must_be_ca) +{ + int tr_ok = X509_TRUST_UNTRUSTED; + + /* + * For trusted certificates we want to see whether any auxiliary trust + * settings trump the purpose constraints. + * + * This is complicated by the fact that the trust ordinals in + * ctx->param->trust are entirely independent of the purpose ordinals in + * ctx->param->purpose! + * + * What connects them is their mutual initialization via calls from + * X509_STORE_CTX_set_default() into X509_VERIFY_PARAM_lookup() which sets + * related values of both param->trust and param->purpose. It is however + * typically possible to infer associated trust values from a purpose value + * via the X509_PURPOSE API. + * + * Therefore, we can only check for trust overrides when the purpose we're + * checking is the same as ctx->param->purpose and ctx->param->trust is + * also set. + */ + if (depth >= ctx->num_untrusted && purpose == ctx->param->purpose) + tr_ok = X509_check_trust(x, ctx->param->trust, X509_TRUST_NO_SS_COMPAT); + + switch (tr_ok) { + case X509_TRUST_TRUSTED: + return 1; + case X509_TRUST_REJECTED: + break; + default: + switch (X509_check_purpose(x, purpose, must_be_ca > 0)) { + case 1: + return 1; + case 0: + break; + default: + if ((ctx->param->flags & X509_V_FLAG_X509_STRICT) == 0) + return 1; + } + break; + } + + return verify_cb_cert(ctx, x, depth, X509_V_ERR_INVALID_PURPOSE); +} + +/* + * Check a certificate chains extensions for consistency with the supplied + * purpose + */ + +static int check_chain_extensions(X509_STORE_CTX *ctx) +{ + int i, must_be_ca, plen = 0; + X509 *x; + int proxy_path_length = 0; + int purpose; + int allow_proxy_certs; + int num = sk_X509_num(ctx->chain); + + /*- + * must_be_ca can have 1 of 3 values: + * -1: we accept both CA and non-CA certificates, to allow direct + * use of self-signed certificates (which are marked as CA). + * 0: we only accept non-CA certificates. This is currently not + * used, but the possibility is present for future extensions. + * 1: we only accept CA certificates. This is currently used for + * all certificates in the chain except the leaf certificate. + */ + must_be_ca = -1; + + /* CRL path validation */ + if (ctx->parent) { + allow_proxy_certs = 0; + purpose = X509_PURPOSE_CRL_SIGN; + } else { + allow_proxy_certs = + ! !(ctx->param->flags & X509_V_FLAG_ALLOW_PROXY_CERTS); + purpose = ctx->param->purpose; + } + + for (i = 0; i < num; i++) { + int ret; + x = sk_X509_value(ctx->chain, i); + if (!(ctx->param->flags & X509_V_FLAG_IGNORE_CRITICAL) + && (x->ex_flags & EXFLAG_CRITICAL)) { + if (!verify_cb_cert(ctx, x, i, + X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION)) + return 0; + } + if (!allow_proxy_certs && (x->ex_flags & EXFLAG_PROXY)) { + if (!verify_cb_cert(ctx, x, i, + X509_V_ERR_PROXY_CERTIFICATES_NOT_ALLOWED)) + return 0; + } + ret = X509_check_ca(x); + switch (must_be_ca) { + case -1: + if ((ctx->param->flags & X509_V_FLAG_X509_STRICT) + && (ret != 1) && (ret != 0)) { + ret = 0; + ctx->error = X509_V_ERR_INVALID_CA; + } else + ret = 1; + break; + case 0: + if (ret != 0) { + ret = 0; + ctx->error = X509_V_ERR_INVALID_NON_CA; + } else + ret = 1; + break; + default: + /* X509_V_FLAG_X509_STRICT is implicit for intermediate CAs */ + if ((ret == 0) + || ((i + 1 < num || ctx->param->flags & X509_V_FLAG_X509_STRICT) + && (ret != 1))) { + ret = 0; + ctx->error = X509_V_ERR_INVALID_CA; + } else + ret = 1; + break; + } + if (ret == 0 && !verify_cb_cert(ctx, x, i, X509_V_OK)) + return 0; + /* check_purpose() makes the callback as needed */ + if (purpose > 0 && !check_purpose(ctx, x, purpose, i, must_be_ca)) + return 0; + /* Check pathlen if not self issued */ + if ((i > 1) && !(x->ex_flags & EXFLAG_SI) + && (x->ex_pathlen != -1) + && (plen > (x->ex_pathlen + proxy_path_length + 1))) { + if (!verify_cb_cert(ctx, x, i, X509_V_ERR_PATH_LENGTH_EXCEEDED)) + return 0; + } + /* Increment path length if not self issued */ + if (!(x->ex_flags & EXFLAG_SI)) + plen++; + /* + * If this certificate is a proxy certificate, the next certificate + * must be another proxy certificate or a EE certificate. If not, + * the next certificate must be a CA certificate. + */ + if (x->ex_flags & EXFLAG_PROXY) { + /* + * RFC3820, 4.1.3 (b)(1) stipulates that if pCPathLengthConstraint + * is less than max_path_length, the former should be copied to + * the latter, and 4.1.4 (a) stipulates that max_path_length + * should be verified to be larger than zero and decrement it. + * + * Because we're checking the certs in the reverse order, we start + * with verifying that proxy_path_length isn't larger than pcPLC, + * and copy the latter to the former if it is, and finally, + * increment proxy_path_length. + */ + if (x->ex_pcpathlen != -1) { + if (proxy_path_length > x->ex_pcpathlen) { + if (!verify_cb_cert(ctx, x, i, + X509_V_ERR_PROXY_PATH_LENGTH_EXCEEDED)) + return 0; + } + proxy_path_length = x->ex_pcpathlen; + } + proxy_path_length++; + must_be_ca = 0; + } else + must_be_ca = 1; + } + return 1; +} + +static int check_name_constraints(X509_STORE_CTX *ctx) +{ + int i; + + /* Check name constraints for all certificates */ + for (i = sk_X509_num(ctx->chain) - 1; i >= 0; i--) { + X509 *x = sk_X509_value(ctx->chain, i); + int j; + + /* Ignore self issued certs unless last in chain */ + if (i && (x->ex_flags & EXFLAG_SI)) + continue; + + /* + * Proxy certificates policy has an extra constraint, where the + * certificate subject MUST be the issuer with a single CN entry + * added. + * (RFC 3820: 3.4, 4.1.3 (a)(4)) + */ + if (x->ex_flags & EXFLAG_PROXY) { + X509_NAME *tmpsubject = X509_get_subject_name(x); + X509_NAME *tmpissuer = X509_get_issuer_name(x); + X509_NAME_ENTRY *tmpentry = NULL; + int last_object_nid = 0; + int err = X509_V_OK; + int last_object_loc = X509_NAME_entry_count(tmpsubject) - 1; + + /* Check that there are at least two RDNs */ + if (last_object_loc < 1) { + err = X509_V_ERR_PROXY_SUBJECT_NAME_VIOLATION; + goto proxy_name_done; + } + + /* + * Check that there is exactly one more RDN in subject as + * there is in issuer. + */ + if (X509_NAME_entry_count(tmpsubject) + != X509_NAME_entry_count(tmpissuer) + 1) { + err = X509_V_ERR_PROXY_SUBJECT_NAME_VIOLATION; + goto proxy_name_done; + } + + /* + * Check that the last subject component isn't part of a + * multivalued RDN + */ + if (X509_NAME_ENTRY_set(X509_NAME_get_entry(tmpsubject, + last_object_loc)) + == X509_NAME_ENTRY_set(X509_NAME_get_entry(tmpsubject, + last_object_loc - 1))) { + err = X509_V_ERR_PROXY_SUBJECT_NAME_VIOLATION; + goto proxy_name_done; + } + + /* + * Check that the last subject RDN is a commonName, and that + * all the previous RDNs match the issuer exactly + */ + tmpsubject = X509_NAME_dup(tmpsubject); + if (tmpsubject == NULL) { + X509err(X509_F_CHECK_NAME_CONSTRAINTS, ERR_R_MALLOC_FAILURE); + ctx->error = X509_V_ERR_OUT_OF_MEM; + return 0; + } + + tmpentry = + X509_NAME_delete_entry(tmpsubject, last_object_loc); + last_object_nid = + OBJ_obj2nid(X509_NAME_ENTRY_get_object(tmpentry)); + + if (last_object_nid != NID_commonName + || X509_NAME_cmp(tmpsubject, tmpissuer) != 0) { + err = X509_V_ERR_PROXY_SUBJECT_NAME_VIOLATION; + } + + X509_NAME_ENTRY_free(tmpentry); + X509_NAME_free(tmpsubject); + + proxy_name_done: + if (err != X509_V_OK + && !verify_cb_cert(ctx, x, i, err)) + return 0; + } + + /* + * Check against constraints for all certificates higher in chain + * including trust anchor. Trust anchor not strictly speaking needed + * but if it includes constraints it is to be assumed it expects them + * to be obeyed. + */ + for (j = sk_X509_num(ctx->chain) - 1; j > i; j--) { + NAME_CONSTRAINTS *nc = sk_X509_value(ctx->chain, j)->nc; + + if (nc) { + int rv = NAME_CONSTRAINTS_check(x, nc); + + /* If EE certificate check commonName too */ + if (rv == X509_V_OK && i == 0) + rv = NAME_CONSTRAINTS_check_CN(x, nc); + + switch (rv) { + case X509_V_OK: + break; + case X509_V_ERR_OUT_OF_MEM: + return 0; + default: + if (!verify_cb_cert(ctx, x, i, rv)) + return 0; + break; + } + } + } + } + return 1; +} + +static int check_id_error(X509_STORE_CTX *ctx, int errcode) +{ + return verify_cb_cert(ctx, ctx->cert, 0, errcode); +} + +static int check_hosts(X509 *x, X509_VERIFY_PARAM *vpm) +{ + int i; + int n = sk_OPENSSL_STRING_num(vpm->hosts); + char *name; + + if (vpm->peername != NULL) { + OPENSSL_free(vpm->peername); + vpm->peername = NULL; + } + for (i = 0; i < n; ++i) { + name = sk_OPENSSL_STRING_value(vpm->hosts, i); + if (X509_check_host(x, name, 0, vpm->hostflags, &vpm->peername) > 0) + return 1; + } + return n == 0; +} + +static int check_id(X509_STORE_CTX *ctx) +{ + X509_VERIFY_PARAM *vpm = ctx->param; + X509 *x = ctx->cert; + if (vpm->hosts && check_hosts(x, vpm) <= 0) { + if (!check_id_error(ctx, X509_V_ERR_HOSTNAME_MISMATCH)) + return 0; + } + if (vpm->email && X509_check_email(x, vpm->email, vpm->emaillen, 0) <= 0) { + if (!check_id_error(ctx, X509_V_ERR_EMAIL_MISMATCH)) + return 0; + } + if (vpm->ip && X509_check_ip(x, vpm->ip, vpm->iplen, 0) <= 0) { + if (!check_id_error(ctx, X509_V_ERR_IP_ADDRESS_MISMATCH)) + return 0; + } + return 1; +} + +static int check_trust(X509_STORE_CTX *ctx, int num_untrusted) +{ + int i; + X509 *x = NULL; + X509 *mx; + SSL_DANE *dane = ctx->dane; + int num = sk_X509_num(ctx->chain); + int trust; + + /* + * Check for a DANE issuer at depth 1 or greater, if it is a DANE-TA(2) + * match, we're done, otherwise we'll merely record the match depth. + */ + if (DANETLS_HAS_TA(dane) && num_untrusted > 0 && num_untrusted < num) { + switch (trust = check_dane_issuer(ctx, num_untrusted)) { + case X509_TRUST_TRUSTED: + case X509_TRUST_REJECTED: + return trust; + } + } + + /* + * Check trusted certificates in chain at depth num_untrusted and up. + * Note, that depths 0..num_untrusted-1 may also contain trusted + * certificates, but the caller is expected to have already checked those, + * and wants to incrementally check just any added since. + */ + for (i = num_untrusted; i < num; i++) { + x = sk_X509_value(ctx->chain, i); + trust = X509_check_trust(x, ctx->param->trust, 0); + /* If explicitly trusted return trusted */ + if (trust == X509_TRUST_TRUSTED) + goto trusted; + if (trust == X509_TRUST_REJECTED) + goto rejected; + } + + /* + * If we are looking at a trusted certificate, and accept partial chains, + * the chain is PKIX trusted. + */ + if (num_untrusted < num) { + if (ctx->param->flags & X509_V_FLAG_PARTIAL_CHAIN) + goto trusted; + return X509_TRUST_UNTRUSTED; + } + + if (num_untrusted == num && ctx->param->flags & X509_V_FLAG_PARTIAL_CHAIN) { + /* + * Last-resort call with no new trusted certificates, check the leaf + * for a direct trust store match. + */ + i = 0; + x = sk_X509_value(ctx->chain, i); + mx = lookup_cert_match(ctx, x); + if (!mx) + return X509_TRUST_UNTRUSTED; + + /* + * Check explicit auxiliary trust/reject settings. If none are set, + * we'll accept X509_TRUST_UNTRUSTED when not self-signed. + */ + trust = X509_check_trust(mx, ctx->param->trust, 0); + if (trust == X509_TRUST_REJECTED) { + X509_free(mx); + goto rejected; + } + + /* Replace leaf with trusted match */ + (void) sk_X509_set(ctx->chain, 0, mx); + X509_free(x); + ctx->num_untrusted = 0; + goto trusted; + } + + /* + * If no trusted certs in chain at all return untrusted and allow + * standard (no issuer cert) etc errors to be indicated. + */ + return X509_TRUST_UNTRUSTED; + + rejected: + if (!verify_cb_cert(ctx, x, i, X509_V_ERR_CERT_REJECTED)) + return X509_TRUST_REJECTED; + return X509_TRUST_UNTRUSTED; + + trusted: + if (!DANETLS_ENABLED(dane)) + return X509_TRUST_TRUSTED; + if (dane->pdpth < 0) + dane->pdpth = num_untrusted; + /* With DANE, PKIX alone is not trusted until we have both */ + if (dane->mdpth >= 0) + return X509_TRUST_TRUSTED; + return X509_TRUST_UNTRUSTED; +} + +static int check_revocation(X509_STORE_CTX *ctx) +{ + int i = 0, last = 0, ok = 0; + if (!(ctx->param->flags & X509_V_FLAG_CRL_CHECK)) + return 1; + if (ctx->param->flags & X509_V_FLAG_CRL_CHECK_ALL) + last = sk_X509_num(ctx->chain) - 1; + else { + /* If checking CRL paths this isn't the EE certificate */ + if (ctx->parent) + return 1; + last = 0; + } + for (i = 0; i <= last; i++) { + ctx->error_depth = i; + ok = check_cert(ctx); + if (!ok) + return ok; + } + return 1; +} + +static int check_cert(X509_STORE_CTX *ctx) +{ + X509_CRL *crl = NULL, *dcrl = NULL; + int ok = 0; + int cnum = ctx->error_depth; + X509 *x = sk_X509_value(ctx->chain, cnum); + + ctx->current_cert = x; + ctx->current_issuer = NULL; + ctx->current_crl_score = 0; + ctx->current_reasons = 0; + + if (x->ex_flags & EXFLAG_PROXY) + return 1; + + while (ctx->current_reasons != CRLDP_ALL_REASONS) { + unsigned int last_reasons = ctx->current_reasons; + + /* Try to retrieve relevant CRL */ + if (ctx->get_crl) + ok = ctx->get_crl(ctx, &crl, x); + else + ok = get_crl_delta(ctx, &crl, &dcrl, x); + /* + * If error looking up CRL, nothing we can do except notify callback + */ + if (!ok) { + ok = verify_cb_crl(ctx, X509_V_ERR_UNABLE_TO_GET_CRL); + goto done; + } + ctx->current_crl = crl; + ok = ctx->check_crl(ctx, crl); + if (!ok) + goto done; + + if (dcrl) { + ok = ctx->check_crl(ctx, dcrl); + if (!ok) + goto done; + ok = ctx->cert_crl(ctx, dcrl, x); + if (!ok) + goto done; + } else + ok = 1; + + /* Don't look in full CRL if delta reason is removefromCRL */ + if (ok != 2) { + ok = ctx->cert_crl(ctx, crl, x); + if (!ok) + goto done; + } + + X509_CRL_free(crl); + X509_CRL_free(dcrl); + crl = NULL; + dcrl = NULL; + /* + * If reasons not updated we won't get anywhere by another iteration, + * so exit loop. + */ + if (last_reasons == ctx->current_reasons) { + ok = verify_cb_crl(ctx, X509_V_ERR_UNABLE_TO_GET_CRL); + goto done; + } + } + done: + X509_CRL_free(crl); + X509_CRL_free(dcrl); + + ctx->current_crl = NULL; + return ok; +} + +/* Check CRL times against values in X509_STORE_CTX */ + +static int check_crl_time(X509_STORE_CTX *ctx, X509_CRL *crl, int notify) +{ + time_t *ptime; + int i; + + if (notify) + ctx->current_crl = crl; + if (ctx->param->flags & X509_V_FLAG_USE_CHECK_TIME) + ptime = &ctx->param->check_time; + else if (ctx->param->flags & X509_V_FLAG_NO_CHECK_TIME) + return 1; + else + ptime = NULL; + + i = X509_cmp_time(X509_CRL_get0_lastUpdate(crl), ptime); + if (i == 0) { + if (!notify) + return 0; + if (!verify_cb_crl(ctx, X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD)) + return 0; + } + + if (i > 0) { + if (!notify) + return 0; + if (!verify_cb_crl(ctx, X509_V_ERR_CRL_NOT_YET_VALID)) + return 0; + } + + if (X509_CRL_get0_nextUpdate(crl)) { + i = X509_cmp_time(X509_CRL_get0_nextUpdate(crl), ptime); + + if (i == 0) { + if (!notify) + return 0; + if (!verify_cb_crl(ctx, X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD)) + return 0; + } + /* Ignore expiry of base CRL is delta is valid */ + if ((i < 0) && !(ctx->current_crl_score & CRL_SCORE_TIME_DELTA)) { + if (!notify) + return 0; + if (!verify_cb_crl(ctx, X509_V_ERR_CRL_HAS_EXPIRED)) + return 0; + } + } + + if (notify) + ctx->current_crl = NULL; + + return 1; +} + +static int get_crl_sk(X509_STORE_CTX *ctx, X509_CRL **pcrl, X509_CRL **pdcrl, + X509 **pissuer, int *pscore, unsigned int *preasons, + STACK_OF(X509_CRL) *crls) +{ + int i, crl_score, best_score = *pscore; + unsigned int reasons, best_reasons = 0; + X509 *x = ctx->current_cert; + X509_CRL *crl, *best_crl = NULL; + X509 *crl_issuer = NULL, *best_crl_issuer = NULL; + + for (i = 0; i < sk_X509_CRL_num(crls); i++) { + crl = sk_X509_CRL_value(crls, i); + reasons = *preasons; + crl_score = get_crl_score(ctx, &crl_issuer, &reasons, crl, x); + if (crl_score < best_score || crl_score == 0) + continue; + /* If current CRL is equivalent use it if it is newer */ + if (crl_score == best_score && best_crl != NULL) { + int day, sec; + if (ASN1_TIME_diff(&day, &sec, X509_CRL_get0_lastUpdate(best_crl), + X509_CRL_get0_lastUpdate(crl)) == 0) + continue; + /* + * ASN1_TIME_diff never returns inconsistent signs for |day| + * and |sec|. + */ + if (day <= 0 && sec <= 0) + continue; + } + best_crl = crl; + best_crl_issuer = crl_issuer; + best_score = crl_score; + best_reasons = reasons; + } + + if (best_crl) { + X509_CRL_free(*pcrl); + *pcrl = best_crl; + *pissuer = best_crl_issuer; + *pscore = best_score; + *preasons = best_reasons; + X509_CRL_up_ref(best_crl); + X509_CRL_free(*pdcrl); + *pdcrl = NULL; + get_delta_sk(ctx, pdcrl, pscore, best_crl, crls); + } + + if (best_score >= CRL_SCORE_VALID) + return 1; + + return 0; +} + +/* + * Compare two CRL extensions for delta checking purposes. They should be + * both present or both absent. If both present all fields must be identical. + */ + +static int crl_extension_match(X509_CRL *a, X509_CRL *b, int nid) +{ + ASN1_OCTET_STRING *exta, *extb; + int i; + i = X509_CRL_get_ext_by_NID(a, nid, -1); + if (i >= 0) { + /* Can't have multiple occurrences */ + if (X509_CRL_get_ext_by_NID(a, nid, i) != -1) + return 0; + exta = X509_EXTENSION_get_data(X509_CRL_get_ext(a, i)); + } else + exta = NULL; + + i = X509_CRL_get_ext_by_NID(b, nid, -1); + + if (i >= 0) { + + if (X509_CRL_get_ext_by_NID(b, nid, i) != -1) + return 0; + extb = X509_EXTENSION_get_data(X509_CRL_get_ext(b, i)); + } else + extb = NULL; + + if (!exta && !extb) + return 1; + + if (!exta || !extb) + return 0; + + if (ASN1_OCTET_STRING_cmp(exta, extb)) + return 0; + + return 1; +} + +/* See if a base and delta are compatible */ + +static int check_delta_base(X509_CRL *delta, X509_CRL *base) +{ + /* Delta CRL must be a delta */ + if (!delta->base_crl_number) + return 0; + /* Base must have a CRL number */ + if (!base->crl_number) + return 0; + /* Issuer names must match */ + if (X509_NAME_cmp(X509_CRL_get_issuer(base), X509_CRL_get_issuer(delta))) + return 0; + /* AKID and IDP must match */ + if (!crl_extension_match(delta, base, NID_authority_key_identifier)) + return 0; + if (!crl_extension_match(delta, base, NID_issuing_distribution_point)) + return 0; + /* Delta CRL base number must not exceed Full CRL number. */ + if (ASN1_INTEGER_cmp(delta->base_crl_number, base->crl_number) > 0) + return 0; + /* Delta CRL number must exceed full CRL number */ + if (ASN1_INTEGER_cmp(delta->crl_number, base->crl_number) > 0) + return 1; + return 0; +} + +/* + * For a given base CRL find a delta... maybe extend to delta scoring or + * retrieve a chain of deltas... + */ + +static void get_delta_sk(X509_STORE_CTX *ctx, X509_CRL **dcrl, int *pscore, + X509_CRL *base, STACK_OF(X509_CRL) *crls) +{ + X509_CRL *delta; + int i; + if (!(ctx->param->flags & X509_V_FLAG_USE_DELTAS)) + return; + if (!((ctx->current_cert->ex_flags | base->flags) & EXFLAG_FRESHEST)) + return; + for (i = 0; i < sk_X509_CRL_num(crls); i++) { + delta = sk_X509_CRL_value(crls, i); + if (check_delta_base(delta, base)) { + if (check_crl_time(ctx, delta, 0)) + *pscore |= CRL_SCORE_TIME_DELTA; + X509_CRL_up_ref(delta); + *dcrl = delta; + return; + } + } + *dcrl = NULL; +} + +/* + * For a given CRL return how suitable it is for the supplied certificate + * 'x'. The return value is a mask of several criteria. If the issuer is not + * the certificate issuer this is returned in *pissuer. The reasons mask is + * also used to determine if the CRL is suitable: if no new reasons the CRL + * is rejected, otherwise reasons is updated. + */ + +static int get_crl_score(X509_STORE_CTX *ctx, X509 **pissuer, + unsigned int *preasons, X509_CRL *crl, X509 *x) +{ + + int crl_score = 0; + unsigned int tmp_reasons = *preasons, crl_reasons; + + /* First see if we can reject CRL straight away */ + + /* Invalid IDP cannot be processed */ + if (crl->idp_flags & IDP_INVALID) + return 0; + /* Reason codes or indirect CRLs need extended CRL support */ + if (!(ctx->param->flags & X509_V_FLAG_EXTENDED_CRL_SUPPORT)) { + if (crl->idp_flags & (IDP_INDIRECT | IDP_REASONS)) + return 0; + } else if (crl->idp_flags & IDP_REASONS) { + /* If no new reasons reject */ + if (!(crl->idp_reasons & ~tmp_reasons)) + return 0; + } + /* Don't process deltas at this stage */ + else if (crl->base_crl_number) + return 0; + /* If issuer name doesn't match certificate need indirect CRL */ + if (X509_NAME_cmp(X509_get_issuer_name(x), X509_CRL_get_issuer(crl))) { + if (!(crl->idp_flags & IDP_INDIRECT)) + return 0; + } else + crl_score |= CRL_SCORE_ISSUER_NAME; + + if (!(crl->flags & EXFLAG_CRITICAL)) + crl_score |= CRL_SCORE_NOCRITICAL; + + /* Check expiry */ + if (check_crl_time(ctx, crl, 0)) + crl_score |= CRL_SCORE_TIME; + + /* Check authority key ID and locate certificate issuer */ + crl_akid_check(ctx, crl, pissuer, &crl_score); + + /* If we can't locate certificate issuer at this point forget it */ + + if (!(crl_score & CRL_SCORE_AKID)) + return 0; + + /* Check cert for matching CRL distribution points */ + + if (crl_crldp_check(x, crl, crl_score, &crl_reasons)) { + /* If no new reasons reject */ + if (!(crl_reasons & ~tmp_reasons)) + return 0; + tmp_reasons |= crl_reasons; + crl_score |= CRL_SCORE_SCOPE; + } + + *preasons = tmp_reasons; + + return crl_score; + +} + +static void crl_akid_check(X509_STORE_CTX *ctx, X509_CRL *crl, + X509 **pissuer, int *pcrl_score) +{ + X509 *crl_issuer = NULL; + X509_NAME *cnm = X509_CRL_get_issuer(crl); + int cidx = ctx->error_depth; + int i; + + if (cidx != sk_X509_num(ctx->chain) - 1) + cidx++; + + crl_issuer = sk_X509_value(ctx->chain, cidx); + + if (X509_check_akid(crl_issuer, crl->akid) == X509_V_OK) { + if (*pcrl_score & CRL_SCORE_ISSUER_NAME) { + *pcrl_score |= CRL_SCORE_AKID | CRL_SCORE_ISSUER_CERT; + *pissuer = crl_issuer; + return; + } + } + + for (cidx++; cidx < sk_X509_num(ctx->chain); cidx++) { + crl_issuer = sk_X509_value(ctx->chain, cidx); + if (X509_NAME_cmp(X509_get_subject_name(crl_issuer), cnm)) + continue; + if (X509_check_akid(crl_issuer, crl->akid) == X509_V_OK) { + *pcrl_score |= CRL_SCORE_AKID | CRL_SCORE_SAME_PATH; + *pissuer = crl_issuer; + return; + } + } + + /* Anything else needs extended CRL support */ + + if (!(ctx->param->flags & X509_V_FLAG_EXTENDED_CRL_SUPPORT)) + return; + + /* + * Otherwise the CRL issuer is not on the path. Look for it in the set of + * untrusted certificates. + */ + for (i = 0; i < sk_X509_num(ctx->untrusted); i++) { + crl_issuer = sk_X509_value(ctx->untrusted, i); + if (X509_NAME_cmp(X509_get_subject_name(crl_issuer), cnm)) + continue; + if (X509_check_akid(crl_issuer, crl->akid) == X509_V_OK) { + *pissuer = crl_issuer; + *pcrl_score |= CRL_SCORE_AKID; + return; + } + } +} + +/* + * Check the path of a CRL issuer certificate. This creates a new + * X509_STORE_CTX and populates it with most of the parameters from the + * parent. This could be optimised somewhat since a lot of path checking will + * be duplicated by the parent, but this will rarely be used in practice. + */ + +static int check_crl_path(X509_STORE_CTX *ctx, X509 *x) +{ + X509_STORE_CTX crl_ctx; + int ret; + + /* Don't allow recursive CRL path validation */ + if (ctx->parent) + return 0; + if (!X509_STORE_CTX_init(&crl_ctx, ctx->ctx, x, ctx->untrusted)) + return -1; + + crl_ctx.crls = ctx->crls; + /* Copy verify params across */ + X509_STORE_CTX_set0_param(&crl_ctx, ctx->param); + + crl_ctx.parent = ctx; + crl_ctx.verify_cb = ctx->verify_cb; + + /* Verify CRL issuer */ + ret = X509_verify_cert(&crl_ctx); + if (ret <= 0) + goto err; + + /* Check chain is acceptable */ + ret = check_crl_chain(ctx, ctx->chain, crl_ctx.chain); + err: + X509_STORE_CTX_cleanup(&crl_ctx); + return ret; +} + +/* + * RFC3280 says nothing about the relationship between CRL path and + * certificate path, which could lead to situations where a certificate could + * be revoked or validated by a CA not authorised to do so. RFC5280 is more + * strict and states that the two paths must end in the same trust anchor, + * though some discussions remain... until this is resolved we use the + * RFC5280 version + */ + +static int check_crl_chain(X509_STORE_CTX *ctx, + STACK_OF(X509) *cert_path, + STACK_OF(X509) *crl_path) +{ + X509 *cert_ta, *crl_ta; + cert_ta = sk_X509_value(cert_path, sk_X509_num(cert_path) - 1); + crl_ta = sk_X509_value(crl_path, sk_X509_num(crl_path) - 1); + if (!X509_cmp(cert_ta, crl_ta)) + return 1; + return 0; +} + +/*- + * Check for match between two dist point names: three separate cases. + * 1. Both are relative names and compare X509_NAME types. + * 2. One full, one relative. Compare X509_NAME to GENERAL_NAMES. + * 3. Both are full names and compare two GENERAL_NAMES. + * 4. One is NULL: automatic match. + */ + +static int idp_check_dp(DIST_POINT_NAME *a, DIST_POINT_NAME *b) +{ + X509_NAME *nm = NULL; + GENERAL_NAMES *gens = NULL; + GENERAL_NAME *gena, *genb; + int i, j; + if (!a || !b) + return 1; + if (a->type == 1) { + if (!a->dpname) + return 0; + /* Case 1: two X509_NAME */ + if (b->type == 1) { + if (!b->dpname) + return 0; + if (!X509_NAME_cmp(a->dpname, b->dpname)) + return 1; + else + return 0; + } + /* Case 2: set name and GENERAL_NAMES appropriately */ + nm = a->dpname; + gens = b->name.fullname; + } else if (b->type == 1) { + if (!b->dpname) + return 0; + /* Case 2: set name and GENERAL_NAMES appropriately */ + gens = a->name.fullname; + nm = b->dpname; + } + + /* Handle case 2 with one GENERAL_NAMES and one X509_NAME */ + if (nm) { + for (i = 0; i < sk_GENERAL_NAME_num(gens); i++) { + gena = sk_GENERAL_NAME_value(gens, i); + if (gena->type != GEN_DIRNAME) + continue; + if (!X509_NAME_cmp(nm, gena->d.directoryName)) + return 1; + } + return 0; + } + + /* Else case 3: two GENERAL_NAMES */ + + for (i = 0; i < sk_GENERAL_NAME_num(a->name.fullname); i++) { + gena = sk_GENERAL_NAME_value(a->name.fullname, i); + for (j = 0; j < sk_GENERAL_NAME_num(b->name.fullname); j++) { + genb = sk_GENERAL_NAME_value(b->name.fullname, j); + if (!GENERAL_NAME_cmp(gena, genb)) + return 1; + } + } + + return 0; + +} + +static int crldp_check_crlissuer(DIST_POINT *dp, X509_CRL *crl, int crl_score) +{ + int i; + X509_NAME *nm = X509_CRL_get_issuer(crl); + /* If no CRLissuer return is successful iff don't need a match */ + if (!dp->CRLissuer) + return ! !(crl_score & CRL_SCORE_ISSUER_NAME); + for (i = 0; i < sk_GENERAL_NAME_num(dp->CRLissuer); i++) { + GENERAL_NAME *gen = sk_GENERAL_NAME_value(dp->CRLissuer, i); + if (gen->type != GEN_DIRNAME) + continue; + if (!X509_NAME_cmp(gen->d.directoryName, nm)) + return 1; + } + return 0; +} + +/* Check CRLDP and IDP */ + +static int crl_crldp_check(X509 *x, X509_CRL *crl, int crl_score, + unsigned int *preasons) +{ + int i; + if (crl->idp_flags & IDP_ONLYATTR) + return 0; + if (x->ex_flags & EXFLAG_CA) { + if (crl->idp_flags & IDP_ONLYUSER) + return 0; + } else { + if (crl->idp_flags & IDP_ONLYCA) + return 0; + } + *preasons = crl->idp_reasons; + for (i = 0; i < sk_DIST_POINT_num(x->crldp); i++) { + DIST_POINT *dp = sk_DIST_POINT_value(x->crldp, i); + if (crldp_check_crlissuer(dp, crl, crl_score)) { + if (!crl->idp || idp_check_dp(dp->distpoint, crl->idp->distpoint)) { + *preasons &= dp->dp_reasons; + return 1; + } + } + } + if ((!crl->idp || !crl->idp->distpoint) + && (crl_score & CRL_SCORE_ISSUER_NAME)) + return 1; + return 0; +} + +/* + * Retrieve CRL corresponding to current certificate. If deltas enabled try + * to find a delta CRL too + */ + +static int get_crl_delta(X509_STORE_CTX *ctx, + X509_CRL **pcrl, X509_CRL **pdcrl, X509 *x) +{ + int ok; + X509 *issuer = NULL; + int crl_score = 0; + unsigned int reasons; + X509_CRL *crl = NULL, *dcrl = NULL; + STACK_OF(X509_CRL) *skcrl; + X509_NAME *nm = X509_get_issuer_name(x); + + reasons = ctx->current_reasons; + ok = get_crl_sk(ctx, &crl, &dcrl, + &issuer, &crl_score, &reasons, ctx->crls); + if (ok) + goto done; + + /* Lookup CRLs from store */ + + skcrl = ctx->lookup_crls(ctx, nm); + + /* If no CRLs found and a near match from get_crl_sk use that */ + if (!skcrl && crl) + goto done; + + get_crl_sk(ctx, &crl, &dcrl, &issuer, &crl_score, &reasons, skcrl); + + sk_X509_CRL_pop_free(skcrl, X509_CRL_free); + + done: + /* If we got any kind of CRL use it and return success */ + if (crl) { + ctx->current_issuer = issuer; + ctx->current_crl_score = crl_score; + ctx->current_reasons = reasons; + *pcrl = crl; + *pdcrl = dcrl; + return 1; + } + return 0; +} + +/* Check CRL validity */ +static int check_crl(X509_STORE_CTX *ctx, X509_CRL *crl) +{ + X509 *issuer = NULL; + EVP_PKEY *ikey = NULL; + int cnum = ctx->error_depth; + int chnum = sk_X509_num(ctx->chain) - 1; + + /* if we have an alternative CRL issuer cert use that */ + if (ctx->current_issuer) + issuer = ctx->current_issuer; + /* + * Else find CRL issuer: if not last certificate then issuer is next + * certificate in chain. + */ + else if (cnum < chnum) + issuer = sk_X509_value(ctx->chain, cnum + 1); + else { + issuer = sk_X509_value(ctx->chain, chnum); + /* If not self signed, can't check signature */ + if (!ctx->check_issued(ctx, issuer, issuer) && + !verify_cb_crl(ctx, X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER)) + return 0; + } + + if (issuer == NULL) + return 1; + + /* + * Skip most tests for deltas because they have already been done + */ + if (!crl->base_crl_number) { + /* Check for cRLSign bit if keyUsage present */ + if ((issuer->ex_flags & EXFLAG_KUSAGE) && + !(issuer->ex_kusage & KU_CRL_SIGN) && + !verify_cb_crl(ctx, X509_V_ERR_KEYUSAGE_NO_CRL_SIGN)) + return 0; + + if (!(ctx->current_crl_score & CRL_SCORE_SCOPE) && + !verify_cb_crl(ctx, X509_V_ERR_DIFFERENT_CRL_SCOPE)) + return 0; + + if (!(ctx->current_crl_score & CRL_SCORE_SAME_PATH) && + check_crl_path(ctx, ctx->current_issuer) <= 0 && + !verify_cb_crl(ctx, X509_V_ERR_CRL_PATH_VALIDATION_ERROR)) + return 0; + + if ((crl->idp_flags & IDP_INVALID) && + !verify_cb_crl(ctx, X509_V_ERR_INVALID_EXTENSION)) + return 0; + } + + if (!(ctx->current_crl_score & CRL_SCORE_TIME) && + !check_crl_time(ctx, crl, 1)) + return 0; + + /* Attempt to get issuer certificate public key */ + ikey = X509_get0_pubkey(issuer); + + if (!ikey && + !verify_cb_crl(ctx, X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY)) + return 0; + + if (ikey) { + int rv = X509_CRL_check_suiteb(crl, ikey, ctx->param->flags); + + if (rv != X509_V_OK && !verify_cb_crl(ctx, rv)) + return 0; + /* Verify CRL signature */ + if (X509_CRL_verify(crl, ikey) <= 0 && + !verify_cb_crl(ctx, X509_V_ERR_CRL_SIGNATURE_FAILURE)) + return 0; + } + return 1; +} + +/* Check certificate against CRL */ +static int cert_crl(X509_STORE_CTX *ctx, X509_CRL *crl, X509 *x) +{ + X509_REVOKED *rev; + + /* + * The rules changed for this... previously if a CRL contained unhandled + * critical extensions it could still be used to indicate a certificate + * was revoked. This has since been changed since critical extensions can + * change the meaning of CRL entries. + */ + if (!(ctx->param->flags & X509_V_FLAG_IGNORE_CRITICAL) + && (crl->flags & EXFLAG_CRITICAL) && + !verify_cb_crl(ctx, X509_V_ERR_UNHANDLED_CRITICAL_CRL_EXTENSION)) + return 0; + /* + * Look for serial number of certificate in CRL. If found, make sure + * reason is not removeFromCRL. + */ + if (X509_CRL_get0_by_cert(crl, &rev, x)) { + if (rev->reason == CRL_REASON_REMOVE_FROM_CRL) + return 2; + if (!verify_cb_crl(ctx, X509_V_ERR_CERT_REVOKED)) + return 0; + } + + return 1; +} + +static int check_policy(X509_STORE_CTX *ctx) +{ + int ret; + + if (ctx->parent) + return 1; + /* + * With DANE, the trust anchor might be a bare public key, not a + * certificate! In that case our chain does not have the trust anchor + * certificate as a top-most element. This comports well with RFC5280 + * chain verification, since there too, the trust anchor is not part of the + * chain to be verified. In particular, X509_policy_check() does not look + * at the TA cert, but assumes that it is present as the top-most chain + * element. We therefore temporarily push a NULL cert onto the chain if it + * was verified via a bare public key, and pop it off right after the + * X509_policy_check() call. + */ + if (ctx->bare_ta_signed && !sk_X509_push(ctx->chain, NULL)) { + X509err(X509_F_CHECK_POLICY, ERR_R_MALLOC_FAILURE); + ctx->error = X509_V_ERR_OUT_OF_MEM; + return 0; + } + ret = X509_policy_check(&ctx->tree, &ctx->explicit_policy, ctx->chain, + ctx->param->policies, ctx->param->flags); + if (ctx->bare_ta_signed) + sk_X509_pop(ctx->chain); + + if (ret == X509_PCY_TREE_INTERNAL) { + X509err(X509_F_CHECK_POLICY, ERR_R_MALLOC_FAILURE); + ctx->error = X509_V_ERR_OUT_OF_MEM; + return 0; + } + /* Invalid or inconsistent extensions */ + if (ret == X509_PCY_TREE_INVALID) { + int i; + + /* Locate certificates with bad extensions and notify callback. */ + for (i = 1; i < sk_X509_num(ctx->chain); i++) { + X509 *x = sk_X509_value(ctx->chain, i); + + if (!(x->ex_flags & EXFLAG_INVALID_POLICY)) + continue; + if (!verify_cb_cert(ctx, x, i, + X509_V_ERR_INVALID_POLICY_EXTENSION)) + return 0; + } + return 1; + } + if (ret == X509_PCY_TREE_FAILURE) { + ctx->current_cert = NULL; + ctx->error = X509_V_ERR_NO_EXPLICIT_POLICY; + return ctx->verify_cb(0, ctx); + } + if (ret != X509_PCY_TREE_VALID) { + X509err(X509_F_CHECK_POLICY, ERR_R_INTERNAL_ERROR); + return 0; + } + + if (ctx->param->flags & X509_V_FLAG_NOTIFY_POLICY) { + ctx->current_cert = NULL; + /* + * Verification errors need to be "sticky", a callback may have allowed + * an SSL handshake to continue despite an error, and we must then + * remain in an error state. Therefore, we MUST NOT clear earlier + * verification errors by setting the error to X509_V_OK. + */ + if (!ctx->verify_cb(2, ctx)) + return 0; + } + + return 1; +} + +/*- + * Check certificate validity times. + * If depth >= 0, invoke verification callbacks on error, otherwise just return + * the validation status. + * + * Return 1 on success, 0 otherwise. + */ +int x509_check_cert_time(X509_STORE_CTX *ctx, X509 *x, int depth) +{ + time_t *ptime; + int i; + + if (ctx->param->flags & X509_V_FLAG_USE_CHECK_TIME) + ptime = &ctx->param->check_time; + else if (ctx->param->flags & X509_V_FLAG_NO_CHECK_TIME) + return 1; + else + ptime = NULL; + + i = X509_cmp_time(X509_get0_notBefore(x), ptime); + if (i >= 0 && depth < 0) + return 0; + if (i == 0 && !verify_cb_cert(ctx, x, depth, + X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD)) + return 0; + if (i > 0 && !verify_cb_cert(ctx, x, depth, X509_V_ERR_CERT_NOT_YET_VALID)) + return 0; + + i = X509_cmp_time(X509_get0_notAfter(x), ptime); + if (i <= 0 && depth < 0) + return 0; + if (i == 0 && !verify_cb_cert(ctx, x, depth, + X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD)) + return 0; + if (i < 0 && !verify_cb_cert(ctx, x, depth, X509_V_ERR_CERT_HAS_EXPIRED)) + return 0; + return 1; +} + +static int internal_verify(X509_STORE_CTX *ctx) +{ + int n = sk_X509_num(ctx->chain) - 1; + X509 *xi = sk_X509_value(ctx->chain, n); + X509 *xs; + + /* + * With DANE-verified bare public key TA signatures, it remains only to + * check the timestamps of the top certificate. We report the issuer as + * NULL, since all we have is a bare key. + */ + if (ctx->bare_ta_signed) { + xs = xi; + xi = NULL; + goto check_cert; + } + + if (ctx->check_issued(ctx, xi, xi)) + xs = xi; + else { + if (ctx->param->flags & X509_V_FLAG_PARTIAL_CHAIN) { + xs = xi; + goto check_cert; + } + if (n <= 0) + return verify_cb_cert(ctx, xi, 0, + X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE); + n--; + ctx->error_depth = n; + xs = sk_X509_value(ctx->chain, n); + } + + /* + * Do not clear ctx->error=0, it must be "sticky", only the user's callback + * is allowed to reset errors (at its own peril). + */ + while (n >= 0) { + EVP_PKEY *pkey; + + /* + * Skip signature check for self signed certificates unless explicitly + * asked for. It doesn't add any security and just wastes time. If + * the issuer's public key is unusable, report the issuer certificate + * and its depth (rather than the depth of the subject). + */ + if (xs != xi || (ctx->param->flags & X509_V_FLAG_CHECK_SS_SIGNATURE)) { + if ((pkey = X509_get0_pubkey(xi)) == NULL) { + if (!verify_cb_cert(ctx, xi, xi != xs ? n+1 : n, + X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY)) + return 0; + } else if (X509_verify(xs, pkey) <= 0) { + if (!verify_cb_cert(ctx, xs, n, + X509_V_ERR_CERT_SIGNATURE_FAILURE)) + return 0; + } + } + + check_cert: + /* Calls verify callback as needed */ + if (!x509_check_cert_time(ctx, xs, n)) + return 0; + + /* + * Signal success at this depth. However, the previous error (if any) + * is retained. + */ + ctx->current_issuer = xi; + ctx->current_cert = xs; + ctx->error_depth = n; + if (!ctx->verify_cb(1, ctx)) + return 0; + + if (--n >= 0) { + xi = xs; + xs = sk_X509_value(ctx->chain, n); + } + } + return 1; +} + +int X509_cmp_current_time(const ASN1_TIME *ctm) +{ + return X509_cmp_time(ctm, NULL); +} + +int X509_cmp_time(const ASN1_TIME *ctm, time_t *cmp_time) +{ + char *str; + ASN1_TIME atm; + long offset; + char buff1[24], buff2[24], *p; + int i, j, remaining; + + p = buff1; + remaining = ctm->length; + str = (char *)ctm->data; + /* + * Note that the following (historical) code allows much more slack in the + * time format than RFC5280. In RFC5280, the representation is fixed: + * UTCTime: YYMMDDHHMMSSZ + * GeneralizedTime: YYYYMMDDHHMMSSZ + */ + if (ctm->type == V_ASN1_UTCTIME) { + /* YYMMDDHHMM[SS]Z or YYMMDDHHMM[SS](+-)hhmm */ + int min_length = sizeof("YYMMDDHHMMZ") - 1; + int max_length = sizeof("YYMMDDHHMMSS+hhmm") - 1; + if (remaining < min_length || remaining > max_length) + return 0; + memcpy(p, str, 10); + p += 10; + str += 10; + remaining -= 10; + } else { + /* YYYYMMDDHHMM[SS[.fff]]Z or YYYYMMDDHHMM[SS[.f[f[f]]]](+-)hhmm */ + int min_length = sizeof("YYYYMMDDHHMMZ") - 1; + int max_length = sizeof("YYYYMMDDHHMMSS.fff+hhmm") - 1; + if (remaining < min_length || remaining > max_length) + return 0; + memcpy(p, str, 12); + p += 12; + str += 12; + remaining -= 12; + } + + if ((*str == 'Z') || (*str == '-') || (*str == '+')) { + *(p++) = '0'; + *(p++) = '0'; + } else { + /* SS (seconds) */ + if (remaining < 2) + return 0; + *(p++) = *(str++); + *(p++) = *(str++); + remaining -= 2; + /* + * Skip any (up to three) fractional seconds... + * TODO(emilia): in RFC5280, fractional seconds are forbidden. + * Can we just kill them altogether? + */ + if (remaining && *str == '.') { + str++; + remaining--; + for (i = 0; i < 3 && remaining; i++, str++, remaining--) { + if (*str < '0' || *str > '9') + break; + } + } + + } + *(p++) = 'Z'; + *(p++) = '\0'; + + /* We now need either a terminating 'Z' or an offset. */ + if (!remaining) + return 0; + if (*str == 'Z') { + if (remaining != 1) + return 0; + offset = 0; + } else { + /* (+-)HHMM */ + if ((*str != '+') && (*str != '-')) + return 0; + /* Historical behaviour: the (+-)hhmm offset is forbidden in RFC5280. */ + if (remaining != 5) + return 0; + if (str[1] < '0' || str[1] > '9' || str[2] < '0' || str[2] > '9' || + str[3] < '0' || str[3] > '9' || str[4] < '0' || str[4] > '9') + return 0; + offset = ((str[1] - '0') * 10 + (str[2] - '0')) * 60; + offset += (str[3] - '0') * 10 + (str[4] - '0'); + if (*str == '-') + offset = -offset; + } + atm.type = ctm->type; + atm.flags = 0; + atm.length = sizeof(buff2); + atm.data = (unsigned char *)buff2; + + if (X509_time_adj(&atm, offset * 60, cmp_time) == NULL) + return 0; + + if (ctm->type == V_ASN1_UTCTIME) { + i = (buff1[0] - '0') * 10 + (buff1[1] - '0'); + if (i < 50) + i += 100; /* cf. RFC 2459 */ + j = (buff2[0] - '0') * 10 + (buff2[1] - '0'); + if (j < 50) + j += 100; + + if (i < j) + return -1; + if (i > j) + return 1; + } + i = strcmp(buff1, buff2); + if (i == 0) /* wait a second then return younger :-) */ + return -1; + else + return i; +} + +ASN1_TIME *X509_gmtime_adj(ASN1_TIME *s, long adj) +{ + return X509_time_adj(s, adj, NULL); +} + +ASN1_TIME *X509_time_adj(ASN1_TIME *s, long offset_sec, time_t *in_tm) +{ + return X509_time_adj_ex(s, 0, offset_sec, in_tm); +} + +ASN1_TIME *X509_time_adj_ex(ASN1_TIME *s, + int offset_day, long offset_sec, time_t *in_tm) +{ + time_t t; + + if (in_tm) + t = *in_tm; + else + time(&t); + + if (s && !(s->flags & ASN1_STRING_FLAG_MSTRING)) { + if (s->type == V_ASN1_UTCTIME) + return ASN1_UTCTIME_adj(s, t, offset_day, offset_sec); + if (s->type == V_ASN1_GENERALIZEDTIME) + return ASN1_GENERALIZEDTIME_adj(s, t, offset_day, offset_sec); + } + return ASN1_TIME_adj(s, t, offset_day, offset_sec); +} + +int X509_get_pubkey_parameters(EVP_PKEY *pkey, STACK_OF(X509) *chain) +{ + EVP_PKEY *ktmp = NULL, *ktmp2; + int i, j; + + if ((pkey != NULL) && !EVP_PKEY_missing_parameters(pkey)) + return 1; + + for (i = 0; i < sk_X509_num(chain); i++) { + ktmp = X509_get0_pubkey(sk_X509_value(chain, i)); + if (ktmp == NULL) { + X509err(X509_F_X509_GET_PUBKEY_PARAMETERS, + X509_R_UNABLE_TO_GET_CERTS_PUBLIC_KEY); + return 0; + } + if (!EVP_PKEY_missing_parameters(ktmp)) + break; + } + if (ktmp == NULL) { + X509err(X509_F_X509_GET_PUBKEY_PARAMETERS, + X509_R_UNABLE_TO_FIND_PARAMETERS_IN_CHAIN); + return 0; + } + + /* first, populate the other certs */ + for (j = i - 1; j >= 0; j--) { + ktmp2 = X509_get0_pubkey(sk_X509_value(chain, j)); + EVP_PKEY_copy_parameters(ktmp2, ktmp); + } + + if (pkey != NULL) + EVP_PKEY_copy_parameters(pkey, ktmp); + return 1; +} + +/* Make a delta CRL as the diff between two full CRLs */ + +X509_CRL *X509_CRL_diff(X509_CRL *base, X509_CRL *newer, + EVP_PKEY *skey, const EVP_MD *md, unsigned int flags) +{ + X509_CRL *crl = NULL; + int i; + STACK_OF(X509_REVOKED) *revs = NULL; + /* CRLs can't be delta already */ + if (base->base_crl_number || newer->base_crl_number) { + X509err(X509_F_X509_CRL_DIFF, X509_R_CRL_ALREADY_DELTA); + return NULL; + } + /* Base and new CRL must have a CRL number */ + if (!base->crl_number || !newer->crl_number) { + X509err(X509_F_X509_CRL_DIFF, X509_R_NO_CRL_NUMBER); + return NULL; + } + /* Issuer names must match */ + if (X509_NAME_cmp(X509_CRL_get_issuer(base), X509_CRL_get_issuer(newer))) { + X509err(X509_F_X509_CRL_DIFF, X509_R_ISSUER_MISMATCH); + return NULL; + } + /* AKID and IDP must match */ + if (!crl_extension_match(base, newer, NID_authority_key_identifier)) { + X509err(X509_F_X509_CRL_DIFF, X509_R_AKID_MISMATCH); + return NULL; + } + if (!crl_extension_match(base, newer, NID_issuing_distribution_point)) { + X509err(X509_F_X509_CRL_DIFF, X509_R_IDP_MISMATCH); + return NULL; + } + /* Newer CRL number must exceed full CRL number */ + if (ASN1_INTEGER_cmp(newer->crl_number, base->crl_number) <= 0) { + X509err(X509_F_X509_CRL_DIFF, X509_R_NEWER_CRL_NOT_NEWER); + return NULL; + } + /* CRLs must verify */ + if (skey && (X509_CRL_verify(base, skey) <= 0 || + X509_CRL_verify(newer, skey) <= 0)) { + X509err(X509_F_X509_CRL_DIFF, X509_R_CRL_VERIFY_FAILURE); + return NULL; + } + /* Create new CRL */ + crl = X509_CRL_new(); + if (crl == NULL || !X509_CRL_set_version(crl, 1)) + goto memerr; + /* Set issuer name */ + if (!X509_CRL_set_issuer_name(crl, X509_CRL_get_issuer(newer))) + goto memerr; + + if (!X509_CRL_set1_lastUpdate(crl, X509_CRL_get0_lastUpdate(newer))) + goto memerr; + if (!X509_CRL_set1_nextUpdate(crl, X509_CRL_get0_nextUpdate(newer))) + goto memerr; + + /* Set base CRL number: must be critical */ + + if (!X509_CRL_add1_ext_i2d(crl, NID_delta_crl, base->crl_number, 1, 0)) + goto memerr; + + /* + * Copy extensions across from newest CRL to delta: this will set CRL + * number to correct value too. + */ + + for (i = 0; i < X509_CRL_get_ext_count(newer); i++) { + X509_EXTENSION *ext; + ext = X509_CRL_get_ext(newer, i); + if (!X509_CRL_add_ext(crl, ext, -1)) + goto memerr; + } + + /* Go through revoked entries, copying as needed */ + + revs = X509_CRL_get_REVOKED(newer); + + for (i = 0; i < sk_X509_REVOKED_num(revs); i++) { + X509_REVOKED *rvn, *rvtmp; + rvn = sk_X509_REVOKED_value(revs, i); + /* + * Add only if not also in base. TODO: need something cleverer here + * for some more complex CRLs covering multiple CAs. + */ + if (!X509_CRL_get0_by_serial(base, &rvtmp, &rvn->serialNumber)) { + rvtmp = X509_REVOKED_dup(rvn); + if (!rvtmp) + goto memerr; + if (!X509_CRL_add0_revoked(crl, rvtmp)) { + X509_REVOKED_free(rvtmp); + goto memerr; + } + } + } + /* TODO: optionally prune deleted entries */ + + if (skey && md && !X509_CRL_sign(crl, skey, md)) + goto memerr; + + return crl; + + memerr: + X509err(X509_F_X509_CRL_DIFF, ERR_R_MALLOC_FAILURE); + X509_CRL_free(crl); + return NULL; +} + +int X509_STORE_CTX_set_ex_data(X509_STORE_CTX *ctx, int idx, void *data) +{ + return CRYPTO_set_ex_data(&ctx->ex_data, idx, data); +} + +void *X509_STORE_CTX_get_ex_data(X509_STORE_CTX *ctx, int idx) +{ + return CRYPTO_get_ex_data(&ctx->ex_data, idx); +} + +int X509_STORE_CTX_get_error(X509_STORE_CTX *ctx) +{ + return ctx->error; +} + +void X509_STORE_CTX_set_error(X509_STORE_CTX *ctx, int err) +{ + ctx->error = err; +} + +int X509_STORE_CTX_get_error_depth(X509_STORE_CTX *ctx) +{ + return ctx->error_depth; +} + +void X509_STORE_CTX_set_error_depth(X509_STORE_CTX *ctx, int depth) +{ + ctx->error_depth = depth; +} + +X509 *X509_STORE_CTX_get_current_cert(X509_STORE_CTX *ctx) +{ + return ctx->current_cert; +} + +void X509_STORE_CTX_set_current_cert(X509_STORE_CTX *ctx, X509 *x) +{ + ctx->current_cert = x; +} + +STACK_OF(X509) *X509_STORE_CTX_get0_chain(X509_STORE_CTX *ctx) +{ + return ctx->chain; +} + +STACK_OF(X509) *X509_STORE_CTX_get1_chain(X509_STORE_CTX *ctx) +{ + if (!ctx->chain) + return NULL; + return X509_chain_up_ref(ctx->chain); +} + +X509 *X509_STORE_CTX_get0_current_issuer(X509_STORE_CTX *ctx) +{ + return ctx->current_issuer; +} + +X509_CRL *X509_STORE_CTX_get0_current_crl(X509_STORE_CTX *ctx) +{ + return ctx->current_crl; +} + +X509_STORE_CTX *X509_STORE_CTX_get0_parent_ctx(X509_STORE_CTX *ctx) +{ + return ctx->parent; +} + +void X509_STORE_CTX_set_cert(X509_STORE_CTX *ctx, X509 *x) +{ + ctx->cert = x; +} + +void X509_STORE_CTX_set0_crls(X509_STORE_CTX *ctx, STACK_OF(X509_CRL) *sk) +{ + ctx->crls = sk; +} + +int X509_STORE_CTX_set_purpose(X509_STORE_CTX *ctx, int purpose) +{ + /* + * XXX: Why isn't this function always used to set the associated trust? + * Should there even be a VPM->trust field at all? Or should the trust + * always be inferred from the purpose by X509_STORE_CTX_init(). + */ + return X509_STORE_CTX_purpose_inherit(ctx, 0, purpose, 0); +} + +int X509_STORE_CTX_set_trust(X509_STORE_CTX *ctx, int trust) +{ + /* + * XXX: See above, this function would only be needed when the default + * trust for the purpose needs an override in a corner case. + */ + return X509_STORE_CTX_purpose_inherit(ctx, 0, 0, trust); +} + +/* + * This function is used to set the X509_STORE_CTX purpose and trust values. + * This is intended to be used when another structure has its own trust and + * purpose values which (if set) will be inherited by the ctx. If they aren't + * set then we will usually have a default purpose in mind which should then + * be used to set the trust value. An example of this is SSL use: an SSL + * structure will have its own purpose and trust settings which the + * application can set: if they aren't set then we use the default of SSL + * client/server. + */ + +int X509_STORE_CTX_purpose_inherit(X509_STORE_CTX *ctx, int def_purpose, + int purpose, int trust) +{ + int idx; + /* If purpose not set use default */ + if (!purpose) + purpose = def_purpose; + /* If we have a purpose then check it is valid */ + if (purpose) { + X509_PURPOSE *ptmp; + idx = X509_PURPOSE_get_by_id(purpose); + if (idx == -1) { + X509err(X509_F_X509_STORE_CTX_PURPOSE_INHERIT, + X509_R_UNKNOWN_PURPOSE_ID); + return 0; + } + ptmp = X509_PURPOSE_get0(idx); + if (ptmp->trust == X509_TRUST_DEFAULT) { + idx = X509_PURPOSE_get_by_id(def_purpose); + /* + * XXX: In the two callers above def_purpose is always 0, which is + * not a known value, so idx will always be -1. How is the + * X509_TRUST_DEFAULT case actually supposed to be handled? + */ + if (idx == -1) { + X509err(X509_F_X509_STORE_CTX_PURPOSE_INHERIT, + X509_R_UNKNOWN_PURPOSE_ID); + return 0; + } + ptmp = X509_PURPOSE_get0(idx); + } + /* If trust not set then get from purpose default */ + if (!trust) + trust = ptmp->trust; + } + if (trust) { + idx = X509_TRUST_get_by_id(trust); + if (idx == -1) { + X509err(X509_F_X509_STORE_CTX_PURPOSE_INHERIT, + X509_R_UNKNOWN_TRUST_ID); + return 0; + } + } + + if (purpose && !ctx->param->purpose) + ctx->param->purpose = purpose; + if (trust && !ctx->param->trust) + ctx->param->trust = trust; + return 1; +} + +X509_STORE_CTX *X509_STORE_CTX_new(void) +{ + X509_STORE_CTX *ctx = OPENSSL_zalloc(sizeof(*ctx)); + + if (ctx == NULL) { + X509err(X509_F_X509_STORE_CTX_NEW, ERR_R_MALLOC_FAILURE); + return NULL; + } + return ctx; +} + +void X509_STORE_CTX_free(X509_STORE_CTX *ctx) +{ + if (ctx == NULL) + return; + + X509_STORE_CTX_cleanup(ctx); + OPENSSL_free(ctx); +} + +int X509_STORE_CTX_init(X509_STORE_CTX *ctx, X509_STORE *store, X509 *x509, + STACK_OF(X509) *chain) +{ + int ret = 1; + + ctx->ctx = store; + ctx->cert = x509; + ctx->untrusted = chain; + ctx->crls = NULL; + ctx->num_untrusted = 0; + ctx->other_ctx = NULL; + ctx->valid = 0; + ctx->chain = NULL; + ctx->error = 0; + ctx->explicit_policy = 0; + ctx->error_depth = 0; + ctx->current_cert = NULL; + ctx->current_issuer = NULL; + ctx->current_crl = NULL; + ctx->current_crl_score = 0; + ctx->current_reasons = 0; + ctx->tree = NULL; + ctx->parent = NULL; + ctx->dane = NULL; + ctx->bare_ta_signed = 0; + /* Zero ex_data to make sure we're cleanup-safe */ + memset(&ctx->ex_data, 0, sizeof(ctx->ex_data)); + + /* store->cleanup is always 0 in OpenSSL, if set must be idempotent */ + if (store) + ctx->cleanup = store->cleanup; + else + ctx->cleanup = 0; + + if (store && store->check_issued) + ctx->check_issued = store->check_issued; + else + ctx->check_issued = check_issued; + + if (store && store->get_issuer) + ctx->get_issuer = store->get_issuer; + else + ctx->get_issuer = X509_STORE_CTX_get1_issuer; + + if (store && store->verify_cb) + ctx->verify_cb = store->verify_cb; + else + ctx->verify_cb = null_callback; + + if (store && store->verify) + ctx->verify = store->verify; + else + ctx->verify = internal_verify; + + if (store && store->check_revocation) + ctx->check_revocation = store->check_revocation; + else + ctx->check_revocation = check_revocation; + + if (store && store->get_crl) + ctx->get_crl = store->get_crl; + else + ctx->get_crl = NULL; + + if (store && store->check_crl) + ctx->check_crl = store->check_crl; + else + ctx->check_crl = check_crl; + + if (store && store->cert_crl) + ctx->cert_crl = store->cert_crl; + else + ctx->cert_crl = cert_crl; + + if (store && store->check_policy) + ctx->check_policy = store->check_policy; + else + ctx->check_policy = check_policy; + + if (store && store->lookup_certs) + ctx->lookup_certs = store->lookup_certs; + else + ctx->lookup_certs = X509_STORE_CTX_get1_certs; + + if (store && store->lookup_crls) + ctx->lookup_crls = store->lookup_crls; + else + ctx->lookup_crls = X509_STORE_CTX_get1_crls; + + ctx->param = X509_VERIFY_PARAM_new(); + if (ctx->param == NULL) { + X509err(X509_F_X509_STORE_CTX_INIT, ERR_R_MALLOC_FAILURE); + goto err; + } + + /* + * Inherit callbacks and flags from X509_STORE if not set use defaults. + */ + if (store) + ret = X509_VERIFY_PARAM_inherit(ctx->param, store->param); + else + ctx->param->inh_flags |= X509_VP_FLAG_DEFAULT | X509_VP_FLAG_ONCE; + + if (ret) + ret = X509_VERIFY_PARAM_inherit(ctx->param, + X509_VERIFY_PARAM_lookup("default")); + + if (ret == 0) { + X509err(X509_F_X509_STORE_CTX_INIT, ERR_R_MALLOC_FAILURE); + goto err; + } + + /* + * XXX: For now, continue to inherit trust from VPM, but infer from the + * purpose if this still yields the default value. + */ + if (ctx->param->trust == X509_TRUST_DEFAULT) { + int idx = X509_PURPOSE_get_by_id(ctx->param->purpose); + X509_PURPOSE *xp = X509_PURPOSE_get0(idx); + + if (xp != NULL) + ctx->param->trust = X509_PURPOSE_get_trust(xp); + } + + if (CRYPTO_new_ex_data(CRYPTO_EX_INDEX_X509_STORE_CTX, ctx, + &ctx->ex_data)) + return 1; + X509err(X509_F_X509_STORE_CTX_INIT, ERR_R_MALLOC_FAILURE); + + err: + /* + * On error clean up allocated storage, if the store context was not + * allocated with X509_STORE_CTX_new() this is our last chance to do so. + */ + X509_STORE_CTX_cleanup(ctx); + return 0; +} + +/* + * Set alternative lookup method: just a STACK of trusted certificates. This + * avoids X509_STORE nastiness where it isn't needed. + */ +void X509_STORE_CTX_set0_trusted_stack(X509_STORE_CTX *ctx, STACK_OF(X509) *sk) +{ + ctx->other_ctx = sk; + ctx->get_issuer = get_issuer_sk; + ctx->lookup_certs = lookup_certs_sk; +} + +void X509_STORE_CTX_cleanup(X509_STORE_CTX *ctx) +{ + /* + * We need to be idempotent because, unfortunately, free() also calls + * cleanup(), so the natural call sequence new(), init(), cleanup(), free() + * calls cleanup() for the same object twice! Thus we must zero the + * pointers below after they're freed! + */ + /* Seems to always be 0 in OpenSSL, do this at most once. */ + if (ctx->cleanup != NULL) { + ctx->cleanup(ctx); + ctx->cleanup = NULL; + } + if (ctx->param != NULL) { + if (ctx->parent == NULL) + X509_VERIFY_PARAM_free(ctx->param); + ctx->param = NULL; + } + X509_policy_tree_free(ctx->tree); + ctx->tree = NULL; + sk_X509_pop_free(ctx->chain, X509_free); + ctx->chain = NULL; + CRYPTO_free_ex_data(CRYPTO_EX_INDEX_X509_STORE_CTX, ctx, &(ctx->ex_data)); + memset(&ctx->ex_data, 0, sizeof(ctx->ex_data)); +} + +void X509_STORE_CTX_set_depth(X509_STORE_CTX *ctx, int depth) +{ + X509_VERIFY_PARAM_set_depth(ctx->param, depth); +} + +void X509_STORE_CTX_set_flags(X509_STORE_CTX *ctx, unsigned long flags) +{ + X509_VERIFY_PARAM_set_flags(ctx->param, flags); +} + +void X509_STORE_CTX_set_time(X509_STORE_CTX *ctx, unsigned long flags, + time_t t) +{ + X509_VERIFY_PARAM_set_time(ctx->param, t); +} + +X509 *X509_STORE_CTX_get0_cert(X509_STORE_CTX *ctx) +{ + return ctx->cert; +} + +STACK_OF(X509) *X509_STORE_CTX_get0_untrusted(X509_STORE_CTX *ctx) +{ + return ctx->untrusted; +} + +void X509_STORE_CTX_set0_untrusted(X509_STORE_CTX *ctx, STACK_OF(X509) *sk) +{ + ctx->untrusted = sk; +} + +void X509_STORE_CTX_set0_verified_chain(X509_STORE_CTX *ctx, STACK_OF(X509) *sk) +{ + sk_X509_pop_free(ctx->chain, X509_free); + ctx->chain = sk; +} + +void X509_STORE_CTX_set_verify_cb(X509_STORE_CTX *ctx, + X509_STORE_CTX_verify_cb verify_cb) +{ + ctx->verify_cb = verify_cb; +} + +X509_STORE_CTX_verify_cb X509_STORE_CTX_get_verify_cb(X509_STORE_CTX *ctx) +{ + return ctx->verify_cb; +} + +void X509_STORE_CTX_set_verify(X509_STORE_CTX *ctx, + X509_STORE_CTX_verify_fn verify) +{ + ctx->verify = verify; +} + +X509_STORE_CTX_verify_fn X509_STORE_CTX_get_verify(X509_STORE_CTX *ctx) +{ + return ctx->verify; +} + +X509_STORE_CTX_get_issuer_fn X509_STORE_CTX_get_get_issuer(X509_STORE_CTX *ctx) +{ + return ctx->get_issuer; +} + +X509_STORE_CTX_check_issued_fn X509_STORE_CTX_get_check_issued(X509_STORE_CTX *ctx) +{ + return ctx->check_issued; +} + +X509_STORE_CTX_check_revocation_fn X509_STORE_CTX_get_check_revocation(X509_STORE_CTX *ctx) +{ + return ctx->check_revocation; +} + +X509_STORE_CTX_get_crl_fn X509_STORE_CTX_get_get_crl(X509_STORE_CTX *ctx) +{ + return ctx->get_crl; +} + +X509_STORE_CTX_check_crl_fn X509_STORE_CTX_get_check_crl(X509_STORE_CTX *ctx) +{ + return ctx->check_crl; +} + +X509_STORE_CTX_cert_crl_fn X509_STORE_CTX_get_cert_crl(X509_STORE_CTX *ctx) +{ + return ctx->cert_crl; +} + +X509_STORE_CTX_check_policy_fn X509_STORE_CTX_get_check_policy(X509_STORE_CTX *ctx) +{ + return ctx->check_policy; +} + +X509_STORE_CTX_lookup_certs_fn X509_STORE_CTX_get_lookup_certs(X509_STORE_CTX *ctx) +{ + return ctx->lookup_certs; +} + +X509_STORE_CTX_lookup_crls_fn X509_STORE_CTX_get_lookup_crls(X509_STORE_CTX *ctx) +{ + return ctx->lookup_crls; +} + +X509_STORE_CTX_cleanup_fn X509_STORE_CTX_get_cleanup(X509_STORE_CTX *ctx) +{ + return ctx->cleanup; +} + +X509_POLICY_TREE *X509_STORE_CTX_get0_policy_tree(X509_STORE_CTX *ctx) +{ + return ctx->tree; +} + +int X509_STORE_CTX_get_explicit_policy(X509_STORE_CTX *ctx) +{ + return ctx->explicit_policy; +} + +int X509_STORE_CTX_get_num_untrusted(X509_STORE_CTX *ctx) +{ + return ctx->num_untrusted; +} + +int X509_STORE_CTX_set_default(X509_STORE_CTX *ctx, const char *name) +{ + const X509_VERIFY_PARAM *param; + param = X509_VERIFY_PARAM_lookup(name); + if (!param) + return 0; + return X509_VERIFY_PARAM_inherit(ctx->param, param); +} + +X509_VERIFY_PARAM *X509_STORE_CTX_get0_param(X509_STORE_CTX *ctx) +{ + return ctx->param; +} + +void X509_STORE_CTX_set0_param(X509_STORE_CTX *ctx, X509_VERIFY_PARAM *param) +{ + X509_VERIFY_PARAM_free(ctx->param); + ctx->param = param; +} + +void X509_STORE_CTX_set0_dane(X509_STORE_CTX *ctx, SSL_DANE *dane) +{ + ctx->dane = dane; +} + +static unsigned char *dane_i2d( + X509 *cert, + uint8_t selector, + unsigned int *i2dlen) +{ + unsigned char *buf = NULL; + int len; + + /* + * Extract ASN.1 DER form of certificate or public key. + */ + switch (selector) { + case DANETLS_SELECTOR_CERT: + len = i2d_X509(cert, &buf); + break; + case DANETLS_SELECTOR_SPKI: + len = i2d_X509_PUBKEY(X509_get_X509_PUBKEY(cert), &buf); + break; + default: + X509err(X509_F_DANE_I2D, X509_R_BAD_SELECTOR); + return NULL; + } + + if (len < 0 || buf == NULL) { + X509err(X509_F_DANE_I2D, ERR_R_MALLOC_FAILURE); + return NULL; + } + + *i2dlen = (unsigned int)len; + return buf; +} + +#define DANETLS_NONE 256 /* impossible uint8_t */ + +static int dane_match(X509_STORE_CTX *ctx, X509 *cert, int depth) +{ + SSL_DANE *dane = ctx->dane; + unsigned usage = DANETLS_NONE; + unsigned selector = DANETLS_NONE; + unsigned ordinal = DANETLS_NONE; + unsigned mtype = DANETLS_NONE; + unsigned char *i2dbuf = NULL; + unsigned int i2dlen = 0; + unsigned char mdbuf[EVP_MAX_MD_SIZE]; + unsigned char *cmpbuf = NULL; + unsigned int cmplen = 0; + int i; + int recnum; + int matched = 0; + danetls_record *t = NULL; + uint32_t mask; + + mask = (depth == 0) ? DANETLS_EE_MASK : DANETLS_TA_MASK; + + /* + * The trust store is not applicable with DANE-TA(2) + */ + if (depth >= ctx->num_untrusted) + mask &= DANETLS_PKIX_MASK; + + /* + * If we've previously matched a PKIX-?? record, no need to test any + * further PKIX-?? records, it remains to just build the PKIX chain. + * Had the match been a DANE-?? record, we'd be done already. + */ + if (dane->mdpth >= 0) + mask &= ~DANETLS_PKIX_MASK; + + /*- + * https://tools.ietf.org/html/rfc7671#section-5.1 + * https://tools.ietf.org/html/rfc7671#section-5.2 + * https://tools.ietf.org/html/rfc7671#section-5.3 + * https://tools.ietf.org/html/rfc7671#section-5.4 + * + * We handle DANE-EE(3) records first as they require no chain building + * and no expiration or hostname checks. We also process digests with + * higher ordinals first and ignore lower priorities except Full(0) which + * is always processed (last). If none match, we then process PKIX-EE(1). + * + * NOTE: This relies on DANE usages sorting before the corresponding PKIX + * usages in SSL_dane_tlsa_add(), and also on descending sorting of digest + * priorities. See twin comment in ssl/ssl_lib.c. + * + * We expect that most TLSA RRsets will have just a single usage, so we + * don't go out of our way to cache multiple selector-specific i2d buffers + * across usages, but if the selector happens to remain the same as switch + * usages, that's OK. Thus, a set of "3 1 1", "3 0 1", "1 1 1", "1 0 1", + * records would result in us generating each of the certificate and public + * key DER forms twice, but more typically we'd just see multiple "3 1 1" + * or multiple "3 0 1" records. + * + * As soon as we find a match at any given depth, we stop, because either + * we've matched a DANE-?? record and the peer is authenticated, or, after + * exhausting all DANE-?? records, we've matched a PKIX-?? record, which is + * sufficient for DANE, and what remains to do is ordinary PKIX validation. + */ + recnum = (dane->umask & mask) ? sk_danetls_record_num(dane->trecs) : 0; + for (i = 0; matched == 0 && i < recnum; ++i) { + t = sk_danetls_record_value(dane->trecs, i); + if ((DANETLS_USAGE_BIT(t->usage) & mask) == 0) + continue; + if (t->usage != usage) { + usage = t->usage; + + /* Reset digest agility for each usage/selector pair */ + mtype = DANETLS_NONE; + ordinal = dane->dctx->mdord[t->mtype]; + } + if (t->selector != selector) { + selector = t->selector; + + /* Update per-selector state */ + OPENSSL_free(i2dbuf); + i2dbuf = dane_i2d(cert, selector, &i2dlen); + if (i2dbuf == NULL) + return -1; + + /* Reset digest agility for each usage/selector pair */ + mtype = DANETLS_NONE; + ordinal = dane->dctx->mdord[t->mtype]; + } else if (t->mtype != DANETLS_MATCHING_FULL) { + /*- + * Digest agility: + * + * + * + * For a fixed selector, after processing all records with the + * highest mtype ordinal, ignore all mtypes with lower ordinals + * other than "Full". + */ + if (dane->dctx->mdord[t->mtype] < ordinal) + continue; + } + + /* + * Each time we hit a (new selector or) mtype, re-compute the relevant + * digest, more complex caching is not worth the code space. + */ + if (t->mtype != mtype) { + const EVP_MD *md = dane->dctx->mdevp[mtype = t->mtype]; + cmpbuf = i2dbuf; + cmplen = i2dlen; + + if (md != NULL) { + cmpbuf = mdbuf; + if (!EVP_Digest(i2dbuf, i2dlen, cmpbuf, &cmplen, md, 0)) { + matched = -1; + break; + } + } + } + + /* + * Squirrel away the certificate and depth if we have a match. Any + * DANE match is dispositive, but with PKIX we still need to build a + * full chain. + */ + if (cmplen == t->dlen && + memcmp(cmpbuf, t->data, cmplen) == 0) { + if (DANETLS_USAGE_BIT(usage) & DANETLS_DANE_MASK) + matched = 1; + if (matched || dane->mdpth < 0) { + dane->mdpth = depth; + dane->mtlsa = t; + OPENSSL_free(dane->mcert); + dane->mcert = cert; + X509_up_ref(cert); + } + break; + } + } + + /* Clear the one-element DER cache */ + OPENSSL_free(i2dbuf); + return matched; +} + +static int check_dane_issuer(X509_STORE_CTX *ctx, int depth) +{ + SSL_DANE *dane = ctx->dane; + int matched = 0; + X509 *cert; + + if (!DANETLS_HAS_TA(dane) || depth == 0) + return X509_TRUST_UNTRUSTED; + + /* + * Record any DANE trust-anchor matches, for the first depth to test, if + * there's one at that depth. (This'll be false for length 1 chains looking + * for an exact match for the leaf certificate). + */ + cert = sk_X509_value(ctx->chain, depth); + if (cert != NULL && (matched = dane_match(ctx, cert, depth)) < 0) + return X509_TRUST_REJECTED; + if (matched > 0) { + ctx->num_untrusted = depth - 1; + return X509_TRUST_TRUSTED; + } + + return X509_TRUST_UNTRUSTED; +} + +static int check_dane_pkeys(X509_STORE_CTX *ctx) +{ + SSL_DANE *dane = ctx->dane; + danetls_record *t; + int num = ctx->num_untrusted; + X509 *cert = sk_X509_value(ctx->chain, num - 1); + int recnum = sk_danetls_record_num(dane->trecs); + int i; + + for (i = 0; i < recnum; ++i) { + t = sk_danetls_record_value(dane->trecs, i); + if (t->usage != DANETLS_USAGE_DANE_TA || + t->selector != DANETLS_SELECTOR_SPKI || + t->mtype != DANETLS_MATCHING_FULL || + X509_verify(cert, t->spki) <= 0) + continue; + + /* Clear any PKIX-?? matches that failed to extend to a full chain */ + X509_free(dane->mcert); + dane->mcert = NULL; + + /* Record match via a bare TA public key */ + ctx->bare_ta_signed = 1; + dane->mdpth = num - 1; + dane->mtlsa = t; + + /* Prune any excess chain certificates */ + num = sk_X509_num(ctx->chain); + for (; num > ctx->num_untrusted; --num) + X509_free(sk_X509_pop(ctx->chain)); + + return X509_TRUST_TRUSTED; + } + + return X509_TRUST_UNTRUSTED; +} + +static void dane_reset(SSL_DANE *dane) +{ + /* + * Reset state to verify another chain, or clear after failure. + */ + X509_free(dane->mcert); + dane->mcert = NULL; + dane->mtlsa = NULL; + dane->mdpth = -1; + dane->pdpth = -1; +} + +static int check_leaf_suiteb(X509_STORE_CTX *ctx, X509 *cert) +{ + int err = X509_chain_check_suiteb(NULL, cert, NULL, ctx->param->flags); + + if (err == X509_V_OK) + return 1; + return verify_cb_cert(ctx, cert, 0, err); +} + +static int dane_verify(X509_STORE_CTX *ctx) +{ + X509 *cert = ctx->cert; + SSL_DANE *dane = ctx->dane; + int matched; + int done; + + dane_reset(dane); + + /*- + * When testing the leaf certificate, if we match a DANE-EE(3) record, + * dane_match() returns 1 and we're done. If however we match a PKIX-EE(1) + * record, the match depth and matching TLSA record are recorded, but the + * return value is 0, because we still need to find a PKIX trust-anchor. + * Therefore, when DANE authentication is enabled (required), we're done + * if: + * + matched < 0, internal error. + * + matched == 1, we matched a DANE-EE(3) record + * + matched == 0, mdepth < 0 (no PKIX-EE match) and there are no + * DANE-TA(2) or PKIX-TA(0) to test. + */ + matched = dane_match(ctx, ctx->cert, 0); + done = matched != 0 || (!DANETLS_HAS_TA(dane) && dane->mdpth < 0); + + if (done) + X509_get_pubkey_parameters(NULL, ctx->chain); + + if (matched > 0) { + /* Callback invoked as needed */ + if (!check_leaf_suiteb(ctx, cert)) + return 0; + /* Callback invoked as needed */ + if ((dane->flags & DANE_FLAG_NO_DANE_EE_NAMECHECKS) == 0 && + !check_id(ctx)) + return 0; + /* Bypass internal_verify(), issue depth 0 success callback */ + ctx->error_depth = 0; + ctx->current_cert = cert; + return ctx->verify_cb(1, ctx); + } + + if (matched < 0) { + ctx->error_depth = 0; + ctx->current_cert = cert; + ctx->error = X509_V_ERR_OUT_OF_MEM; + return -1; + } + + if (done) { + /* Fail early, TA-based success is not possible */ + if (!check_leaf_suiteb(ctx, cert)) + return 0; + return verify_cb_cert(ctx, cert, 0, X509_V_ERR_DANE_NO_MATCH); + } + + /* + * Chain verification for usages 0/1/2. TLSA record matching of depth > 0 + * certificates happens in-line with building the rest of the chain. + */ + return verify_chain(ctx); +} + +/* Get issuer, without duplicate suppression */ +static int get_issuer(X509 **issuer, X509_STORE_CTX *ctx, X509 *cert) +{ + STACK_OF(X509) *saved_chain = ctx->chain; + int ok; + + ctx->chain = NULL; + ok = ctx->get_issuer(issuer, ctx, cert); + ctx->chain = saved_chain; + + return ok; +} + +static int build_chain(X509_STORE_CTX *ctx) +{ + SSL_DANE *dane = ctx->dane; + int num = sk_X509_num(ctx->chain); + X509 *cert = sk_X509_value(ctx->chain, num - 1); + int ss = cert_self_signed(cert); + STACK_OF(X509) *sktmp = NULL; + unsigned int search; + int may_trusted = 0; + int may_alternate = 0; + int trust = X509_TRUST_UNTRUSTED; + int alt_untrusted = 0; + int depth; + int ok = 0; + int i; + + /* Our chain starts with a single untrusted element. */ + OPENSSL_assert(num == 1 && ctx->num_untrusted == num); + +#define S_DOUNTRUSTED (1 << 0) /* Search untrusted chain */ +#define S_DOTRUSTED (1 << 1) /* Search trusted store */ +#define S_DOALTERNATE (1 << 2) /* Retry with pruned alternate chain */ + /* + * Set up search policy, untrusted if possible, trusted-first if enabled. + * If we're doing DANE and not doing PKIX-TA/PKIX-EE, we never look in the + * trust_store, otherwise we might look there first. If not trusted-first, + * and alternate chains are not disabled, try building an alternate chain + * if no luck with untrusted first. + */ + search = (ctx->untrusted != NULL) ? S_DOUNTRUSTED : 0; + if (DANETLS_HAS_PKIX(dane) || !DANETLS_HAS_DANE(dane)) { + if (search == 0 || ctx->param->flags & X509_V_FLAG_TRUSTED_FIRST) + search |= S_DOTRUSTED; + else if (!(ctx->param->flags & X509_V_FLAG_NO_ALT_CHAINS)) + may_alternate = 1; + may_trusted = 1; + } + + /* + * Shallow-copy the stack of untrusted certificates (with TLS, this is + * typically the content of the peer's certificate message) so can make + * multiple passes over it, while free to remove elements as we go. + */ + if (ctx->untrusted && (sktmp = sk_X509_dup(ctx->untrusted)) == NULL) { + X509err(X509_F_BUILD_CHAIN, ERR_R_MALLOC_FAILURE); + ctx->error = X509_V_ERR_OUT_OF_MEM; + return 0; + } + + /* + * If we got any "DANE-TA(2) Cert(0) Full(0)" trust-anchors from DNS, add + * them to our working copy of the untrusted certificate stack. Since the + * caller of X509_STORE_CTX_init() may have provided only a leaf cert with + * no corresponding stack of untrusted certificates, we may need to create + * an empty stack first. [ At present only the ssl library provides DANE + * support, and ssl_verify_cert_chain() always provides a non-null stack + * containing at least the leaf certificate, but we must be prepared for + * this to change. ] + */ + if (DANETLS_ENABLED(dane) && dane->certs != NULL) { + if (sktmp == NULL && (sktmp = sk_X509_new_null()) == NULL) { + X509err(X509_F_BUILD_CHAIN, ERR_R_MALLOC_FAILURE); + ctx->error = X509_V_ERR_OUT_OF_MEM; + return 0; + } + for (i = 0; i < sk_X509_num(dane->certs); ++i) { + if (!sk_X509_push(sktmp, sk_X509_value(dane->certs, i))) { + sk_X509_free(sktmp); + X509err(X509_F_BUILD_CHAIN, ERR_R_MALLOC_FAILURE); + ctx->error = X509_V_ERR_OUT_OF_MEM; + return 0; + } + } + } + + /* + * Still absurdly large, but arithmetically safe, a lower hard upper bound + * might be reasonable. + */ + if (ctx->param->depth > INT_MAX/2) + ctx->param->depth = INT_MAX/2; + + /* + * Try to Extend the chain until we reach an ultimately trusted issuer. + * Build chains up to one longer the limit, later fail if we hit the limit, + * with an X509_V_ERR_CERT_CHAIN_TOO_LONG error code. + */ + depth = ctx->param->depth + 1; + + while (search != 0) { + X509 *x; + X509 *xtmp = NULL; + + /* + * Look in the trust store if enabled for first lookup, or we've run + * out of untrusted issuers and search here is not disabled. When we + * reach the depth limit, we stop extending the chain, if by that point + * we've not found a trust-anchor, any trusted chain would be too long. + * + * The error reported to the application verify callback is at the + * maximal valid depth with the current certificate equal to the last + * not ultimately-trusted issuer. For example, with verify_depth = 0, + * the callback will report errors at depth=1 when the immediate issuer + * of the leaf certificate is not a trust anchor. No attempt will be + * made to locate an issuer for that certificate, since such a chain + * would be a-priori too long. + */ + if ((search & S_DOTRUSTED) != 0) { + i = num = sk_X509_num(ctx->chain); + if ((search & S_DOALTERNATE) != 0) { + /* + * As high up the chain as we can, look for an alternative + * trusted issuer of an untrusted certificate that currently + * has an untrusted issuer. We use the alt_untrusted variable + * to track how far up the chain we find the first match. It + * is only if and when we find a match, that we prune the chain + * and reset ctx->num_untrusted to the reduced count of + * untrusted certificates. While we're searching for such a + * match (which may never be found), it is neither safe nor + * wise to preemptively modify either the chain or + * ctx->num_untrusted. + * + * Note, like ctx->num_untrusted, alt_untrusted is a count of + * untrusted certificates, not a "depth". + */ + i = alt_untrusted; + } + x = sk_X509_value(ctx->chain, i-1); + + ok = (depth < num) ? 0 : get_issuer(&xtmp, ctx, x); + + if (ok < 0) { + trust = X509_TRUST_REJECTED; + ctx->error = X509_V_ERR_STORE_LOOKUP; + search = 0; + continue; + } + + if (ok > 0) { + /* + * Alternative trusted issuer for a mid-chain untrusted cert? + * Pop the untrusted cert's successors and retry. We might now + * be able to complete a valid chain via the trust store. Note + * that despite the current trust-store match we might still + * fail complete the chain to a suitable trust-anchor, in which + * case we may prune some more untrusted certificates and try + * again. Thus the S_DOALTERNATE bit may yet be turned on + * again with an even shorter untrusted chain! + * + * If in the process we threw away our matching PKIX-TA trust + * anchor, reset DANE trust. We might find a suitable trusted + * certificate among the ones from the trust store. + */ + if ((search & S_DOALTERNATE) != 0) { + OPENSSL_assert(num > i && i > 0 && ss == 0); + search &= ~S_DOALTERNATE; + for (; num > i; --num) + X509_free(sk_X509_pop(ctx->chain)); + ctx->num_untrusted = num; + + if (DANETLS_ENABLED(dane) && + dane->mdpth >= ctx->num_untrusted) { + dane->mdpth = -1; + X509_free(dane->mcert); + dane->mcert = NULL; + } + if (DANETLS_ENABLED(dane) && + dane->pdpth >= ctx->num_untrusted) + dane->pdpth = -1; + } + + /* + * Self-signed untrusted certificates get replaced by their + * trusted matching issuer. Otherwise, grow the chain. + */ + if (ss == 0) { + if (!sk_X509_push(ctx->chain, x = xtmp)) { + X509_free(xtmp); + X509err(X509_F_BUILD_CHAIN, ERR_R_MALLOC_FAILURE); + trust = X509_TRUST_REJECTED; + ctx->error = X509_V_ERR_OUT_OF_MEM; + search = 0; + continue; + } + ss = cert_self_signed(x); + } else if (num == ctx->num_untrusted) { + /* + * We have a self-signed certificate that has the same + * subject name (and perhaps keyid and/or serial number) as + * a trust-anchor. We must have an exact match to avoid + * possible impersonation via key substitution etc. + */ + if (X509_cmp(x, xtmp) != 0) { + /* Self-signed untrusted mimic. */ + X509_free(xtmp); + ok = 0; + } else { + X509_free(x); + ctx->num_untrusted = --num; + (void) sk_X509_set(ctx->chain, num, x = xtmp); + } + } + + /* + * We've added a new trusted certificate to the chain, recheck + * trust. If not done, and not self-signed look deeper. + * Whether or not we're doing "trusted first", we no longer + * look for untrusted certificates from the peer's chain. + * + * At this point ctx->num_trusted and num must reflect the + * correct number of untrusted certificates, since the DANE + * logic in check_trust() depends on distinguishing CAs from + * "the wire" from CAs from the trust store. In particular, the + * certificate at depth "num" should be the new trusted + * certificate with ctx->num_untrusted <= num. + */ + if (ok) { + OPENSSL_assert(ctx->num_untrusted <= num); + search &= ~S_DOUNTRUSTED; + switch (trust = check_trust(ctx, num)) { + case X509_TRUST_TRUSTED: + case X509_TRUST_REJECTED: + search = 0; + continue; + } + if (ss == 0) + continue; + } + } + + /* + * No dispositive decision, and either self-signed or no match, if + * we were doing untrusted-first, and alt-chains are not disabled, + * do that, by repeatedly losing one untrusted element at a time, + * and trying to extend the shorted chain. + */ + if ((search & S_DOUNTRUSTED) == 0) { + /* Continue search for a trusted issuer of a shorter chain? */ + if ((search & S_DOALTERNATE) != 0 && --alt_untrusted > 0) + continue; + /* Still no luck and no fallbacks left? */ + if (!may_alternate || (search & S_DOALTERNATE) != 0 || + ctx->num_untrusted < 2) + break; + /* Search for a trusted issuer of a shorter chain */ + search |= S_DOALTERNATE; + alt_untrusted = ctx->num_untrusted - 1; + ss = 0; + } + } + + /* + * Extend chain with peer-provided certificates + */ + if ((search & S_DOUNTRUSTED) != 0) { + num = sk_X509_num(ctx->chain); + OPENSSL_assert(num == ctx->num_untrusted); + x = sk_X509_value(ctx->chain, num-1); + + /* + * Once we run out of untrusted issuers, we stop looking for more + * and start looking only in the trust store if enabled. + */ + xtmp = (ss || depth < num) ? NULL : find_issuer(ctx, sktmp, x); + if (xtmp == NULL) { + search &= ~S_DOUNTRUSTED; + if (may_trusted) + search |= S_DOTRUSTED; + continue; + } + + /* Drop this issuer from future consideration */ + (void) sk_X509_delete_ptr(sktmp, xtmp); + + if (!sk_X509_push(ctx->chain, xtmp)) { + X509err(X509_F_BUILD_CHAIN, ERR_R_MALLOC_FAILURE); + trust = X509_TRUST_REJECTED; + ctx->error = X509_V_ERR_OUT_OF_MEM; + search = 0; + continue; + } + + X509_up_ref(x = xtmp); + ++ctx->num_untrusted; + ss = cert_self_signed(xtmp); + + /* + * Check for DANE-TA trust of the topmost untrusted certificate. + */ + switch (trust = check_dane_issuer(ctx, ctx->num_untrusted - 1)) { + case X509_TRUST_TRUSTED: + case X509_TRUST_REJECTED: + search = 0; + continue; + } + } + } + sk_X509_free(sktmp); + + /* + * Last chance to make a trusted chain, either bare DANE-TA public-key + * signers, or else direct leaf PKIX trust. + */ + num = sk_X509_num(ctx->chain); + if (num <= depth) { + if (trust == X509_TRUST_UNTRUSTED && DANETLS_HAS_DANE_TA(dane)) + trust = check_dane_pkeys(ctx); + if (trust == X509_TRUST_UNTRUSTED && num == ctx->num_untrusted) + trust = check_trust(ctx, num); + } + + switch (trust) { + case X509_TRUST_TRUSTED: + return 1; + case X509_TRUST_REJECTED: + /* Callback already issued */ + return 0; + case X509_TRUST_UNTRUSTED: + default: + num = sk_X509_num(ctx->chain); + if (num > depth) + return verify_cb_cert(ctx, NULL, num-1, + X509_V_ERR_CERT_CHAIN_TOO_LONG); + if (DANETLS_ENABLED(dane) && + (!DANETLS_HAS_PKIX(dane) || dane->pdpth >= 0)) + return verify_cb_cert(ctx, NULL, num-1, X509_V_ERR_DANE_NO_MATCH); + if (ss && sk_X509_num(ctx->chain) == 1) + return verify_cb_cert(ctx, NULL, num-1, + X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT); + if (ss) + return verify_cb_cert(ctx, NULL, num-1, + X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN); + if (ctx->num_untrusted < num) + return verify_cb_cert(ctx, NULL, num-1, + X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT); + return verify_cb_cert(ctx, NULL, num-1, + X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY); + } +} + +static const int minbits_table[] = { 80, 112, 128, 192, 256 }; +static const int NUM_AUTH_LEVELS = OSSL_NELEM(minbits_table); + +/* + * Check whether the public key of ``cert`` meets the security level of + * ``ctx``. + * + * Returns 1 on success, 0 otherwise. + */ +static int check_key_level(X509_STORE_CTX *ctx, X509 *cert) +{ + EVP_PKEY *pkey = X509_get0_pubkey(cert); + int level = ctx->param->auth_level; + + /* Unsupported or malformed keys are not secure */ + if (pkey == NULL) + return 0; + + if (level <= 0) + return 1; + if (level > NUM_AUTH_LEVELS) + level = NUM_AUTH_LEVELS; + + return EVP_PKEY_security_bits(pkey) >= minbits_table[level - 1]; +} + +/* + * Check whether the signature digest algorithm of ``cert`` meets the security + * level of ``ctx``. Should not be checked for trust anchors (whether + * self-signed or otherwise). + * + * Returns 1 on success, 0 otherwise. + */ +static int check_sig_level(X509_STORE_CTX *ctx, X509 *cert) +{ + int nid = X509_get_signature_nid(cert); + int mdnid = NID_undef; + int secbits = -1; + int level = ctx->param->auth_level; + + if (level <= 0) + return 1; + if (level > NUM_AUTH_LEVELS) + level = NUM_AUTH_LEVELS; + + /* Lookup signature algorithm digest */ + if (nid && OBJ_find_sigid_algs(nid, &mdnid, NULL)) { + const EVP_MD *md; + + /* Assume 4 bits of collision resistance for each hash octet */ + if (mdnid != NID_undef && (md = EVP_get_digestbynid(mdnid)) != NULL) + secbits = EVP_MD_size(md) * 4; + } + + return secbits >= minbits_table[level - 1]; +} diff --git a/crypto/x509/x509_vpm.c b/crypto/x509/x509_vpm.c new file mode 100644 index 000000000..b5067220a --- /dev/null +++ b/crypto/x509/x509_vpm.c @@ -0,0 +1,617 @@ +/* + * Copyright 2004-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include + +#include "internal/cryptlib.h" +#include +#include +#include +#include +#include +#include "internal/x509_int.h" + +#include "x509_lcl.h" + +/* X509_VERIFY_PARAM functions */ + +#define SET_HOST 0 +#define ADD_HOST 1 + +static char *str_copy(const char *s) +{ + return OPENSSL_strdup(s); +} + +static void str_free(char *s) +{ + OPENSSL_free(s); +} + +static int int_x509_param_set_hosts(X509_VERIFY_PARAM *vpm, int mode, + const char *name, size_t namelen) +{ + char *copy; + + /* + * Refuse names with embedded NUL bytes, except perhaps as final byte. + * XXX: Do we need to push an error onto the error stack? + */ + if (namelen == 0 || name == NULL) + namelen = name ? strlen(name) : 0; + else if (name && memchr(name, '\0', namelen > 1 ? namelen - 1 : namelen)) + return 0; + if (namelen > 0 && name[namelen - 1] == '\0') + --namelen; + + if (mode == SET_HOST) { + sk_OPENSSL_STRING_pop_free(vpm->hosts, str_free); + vpm->hosts = NULL; + } + if (name == NULL || namelen == 0) + return 1; + + copy = OPENSSL_strndup(name, namelen); + if (copy == NULL) + return 0; + + if (vpm->hosts == NULL && + (vpm->hosts = sk_OPENSSL_STRING_new_null()) == NULL) { + OPENSSL_free(copy); + return 0; + } + + if (!sk_OPENSSL_STRING_push(vpm->hosts, copy)) { + OPENSSL_free(copy); + if (sk_OPENSSL_STRING_num(vpm->hosts) == 0) { + sk_OPENSSL_STRING_free(vpm->hosts); + vpm->hosts = NULL; + } + return 0; + } + + return 1; +} + +static void x509_verify_param_zero(X509_VERIFY_PARAM *param) +{ + if (!param) + return; + param->name = NULL; + param->purpose = 0; + param->trust = X509_TRUST_DEFAULT; + /* + * param->inh_flags = X509_VP_FLAG_DEFAULT; + */ + param->inh_flags = 0; + param->flags = 0; + param->depth = -1; + param->auth_level = -1; /* -1 means unset, 0 is explicit */ + sk_ASN1_OBJECT_pop_free(param->policies, ASN1_OBJECT_free); + param->policies = NULL; + sk_OPENSSL_STRING_pop_free(param->hosts, str_free); + param->hosts = NULL; + OPENSSL_free(param->peername); + param->peername = NULL; + OPENSSL_free(param->email); + param->email = NULL; + param->emaillen = 0; + OPENSSL_free(param->ip); + param->ip = NULL; + param->iplen = 0; +} + +X509_VERIFY_PARAM *X509_VERIFY_PARAM_new(void) +{ + X509_VERIFY_PARAM *param; + + param = OPENSSL_zalloc(sizeof(*param)); + if (param == NULL) + return NULL; + x509_verify_param_zero(param); + return param; +} + +void X509_VERIFY_PARAM_free(X509_VERIFY_PARAM *param) +{ + if (!param) + return; + x509_verify_param_zero(param); + OPENSSL_free(param); +} + +/*- + * This function determines how parameters are "inherited" from one structure + * to another. There are several different ways this can happen. + * + * 1. If a child structure needs to have its values initialized from a parent + * they are simply copied across. For example SSL_CTX copied to SSL. + * 2. If the structure should take on values only if they are currently unset. + * For example the values in an SSL structure will take appropriate value + * for SSL servers or clients but only if the application has not set new + * ones. + * + * The "inh_flags" field determines how this function behaves. + * + * Normally any values which are set in the default are not copied from the + * destination and verify flags are ORed together. + * + * If X509_VP_FLAG_DEFAULT is set then anything set in the source is copied + * to the destination. Effectively the values in "to" become default values + * which will be used only if nothing new is set in "from". + * + * If X509_VP_FLAG_OVERWRITE is set then all value are copied across whether + * they are set or not. Flags is still Ored though. + * + * If X509_VP_FLAG_RESET_FLAGS is set then the flags value is copied instead + * of ORed. + * + * If X509_VP_FLAG_LOCKED is set then no values are copied. + * + * If X509_VP_FLAG_ONCE is set then the current inh_flags setting is zeroed + * after the next call. + */ + +/* Macro to test if a field should be copied from src to dest */ + +#define test_x509_verify_param_copy(field, def) \ + (to_overwrite || \ + ((src->field != def) && (to_default || (dest->field == def)))) + +/* Macro to test and copy a field if necessary */ + +#define x509_verify_param_copy(field, def) \ + if (test_x509_verify_param_copy(field, def)) \ + dest->field = src->field + +int X509_VERIFY_PARAM_inherit(X509_VERIFY_PARAM *dest, + const X509_VERIFY_PARAM *src) +{ + unsigned long inh_flags; + int to_default, to_overwrite; + if (!src) + return 1; + inh_flags = dest->inh_flags | src->inh_flags; + + if (inh_flags & X509_VP_FLAG_ONCE) + dest->inh_flags = 0; + + if (inh_flags & X509_VP_FLAG_LOCKED) + return 1; + + if (inh_flags & X509_VP_FLAG_DEFAULT) + to_default = 1; + else + to_default = 0; + + if (inh_flags & X509_VP_FLAG_OVERWRITE) + to_overwrite = 1; + else + to_overwrite = 0; + + x509_verify_param_copy(purpose, 0); + x509_verify_param_copy(trust, X509_TRUST_DEFAULT); + x509_verify_param_copy(depth, -1); + x509_verify_param_copy(auth_level, -1); + + /* If overwrite or check time not set, copy across */ + + if (to_overwrite || !(dest->flags & X509_V_FLAG_USE_CHECK_TIME)) { + dest->check_time = src->check_time; + dest->flags &= ~X509_V_FLAG_USE_CHECK_TIME; + /* Don't need to copy flag: that is done below */ + } + + if (inh_flags & X509_VP_FLAG_RESET_FLAGS) + dest->flags = 0; + + dest->flags |= src->flags; + + if (test_x509_verify_param_copy(policies, NULL)) { + if (!X509_VERIFY_PARAM_set1_policies(dest, src->policies)) + return 0; + } + + /* Copy the host flags if and only if we're copying the host list */ + if (test_x509_verify_param_copy(hosts, NULL)) { + sk_OPENSSL_STRING_pop_free(dest->hosts, str_free); + dest->hosts = NULL; + if (src->hosts) { + dest->hosts = + sk_OPENSSL_STRING_deep_copy(src->hosts, str_copy, str_free); + if (dest->hosts == NULL) + return 0; + dest->hostflags = src->hostflags; + } + } + + if (test_x509_verify_param_copy(email, NULL)) { + if (!X509_VERIFY_PARAM_set1_email(dest, src->email, src->emaillen)) + return 0; + } + + if (test_x509_verify_param_copy(ip, NULL)) { + if (!X509_VERIFY_PARAM_set1_ip(dest, src->ip, src->iplen)) + return 0; + } + + return 1; +} + +int X509_VERIFY_PARAM_set1(X509_VERIFY_PARAM *to, + const X509_VERIFY_PARAM *from) +{ + unsigned long save_flags = to->inh_flags; + int ret; + to->inh_flags |= X509_VP_FLAG_DEFAULT; + ret = X509_VERIFY_PARAM_inherit(to, from); + to->inh_flags = save_flags; + return ret; +} + +static int int_x509_param_set1(char **pdest, size_t *pdestlen, + const char *src, size_t srclen) +{ + void *tmp; + if (src) { + if (srclen == 0) + srclen = strlen(src); + + tmp = OPENSSL_memdup(src, srclen); + if (tmp == NULL) + return 0; + } else { + tmp = NULL; + srclen = 0; + } + OPENSSL_free(*pdest); + *pdest = tmp; + if (pdestlen != NULL) + *pdestlen = srclen; + return 1; +} + +int X509_VERIFY_PARAM_set1_name(X509_VERIFY_PARAM *param, const char *name) +{ + OPENSSL_free(param->name); + param->name = OPENSSL_strdup(name); + if (param->name) + return 1; + return 0; +} + +int X509_VERIFY_PARAM_set_flags(X509_VERIFY_PARAM *param, unsigned long flags) +{ + param->flags |= flags; + if (flags & X509_V_FLAG_POLICY_MASK) + param->flags |= X509_V_FLAG_POLICY_CHECK; + return 1; +} + +int X509_VERIFY_PARAM_clear_flags(X509_VERIFY_PARAM *param, + unsigned long flags) +{ + param->flags &= ~flags; + return 1; +} + +unsigned long X509_VERIFY_PARAM_get_flags(X509_VERIFY_PARAM *param) +{ + return param->flags; +} + +uint32_t X509_VERIFY_PARAM_get_inh_flags(const X509_VERIFY_PARAM *param) +{ + return param->inh_flags; +} + +int X509_VERIFY_PARAM_set_inh_flags(X509_VERIFY_PARAM *param, uint32_t flags) +{ + param->inh_flags = flags; + return 1; +} + +int X509_VERIFY_PARAM_set_purpose(X509_VERIFY_PARAM *param, int purpose) +{ + return X509_PURPOSE_set(¶m->purpose, purpose); +} + +int X509_VERIFY_PARAM_set_trust(X509_VERIFY_PARAM *param, int trust) +{ + return X509_TRUST_set(¶m->trust, trust); +} + +void X509_VERIFY_PARAM_set_depth(X509_VERIFY_PARAM *param, int depth) +{ + param->depth = depth; +} + +void X509_VERIFY_PARAM_set_auth_level(X509_VERIFY_PARAM *param, int auth_level) +{ + param->auth_level = auth_level; +} + +time_t X509_VERIFY_PARAM_get_time(const X509_VERIFY_PARAM *param) +{ + return param->check_time; +} + +void X509_VERIFY_PARAM_set_time(X509_VERIFY_PARAM *param, time_t t) +{ + param->check_time = t; + param->flags |= X509_V_FLAG_USE_CHECK_TIME; +} + +int X509_VERIFY_PARAM_add0_policy(X509_VERIFY_PARAM *param, + ASN1_OBJECT *policy) +{ + if (!param->policies) { + param->policies = sk_ASN1_OBJECT_new_null(); + if (!param->policies) + return 0; + } + if (!sk_ASN1_OBJECT_push(param->policies, policy)) + return 0; + return 1; +} + +int X509_VERIFY_PARAM_set1_policies(X509_VERIFY_PARAM *param, + STACK_OF(ASN1_OBJECT) *policies) +{ + int i; + ASN1_OBJECT *oid, *doid; + + if (!param) + return 0; + sk_ASN1_OBJECT_pop_free(param->policies, ASN1_OBJECT_free); + + if (!policies) { + param->policies = NULL; + return 1; + } + + param->policies = sk_ASN1_OBJECT_new_null(); + if (!param->policies) + return 0; + + for (i = 0; i < sk_ASN1_OBJECT_num(policies); i++) { + oid = sk_ASN1_OBJECT_value(policies, i); + doid = OBJ_dup(oid); + if (!doid) + return 0; + if (!sk_ASN1_OBJECT_push(param->policies, doid)) { + ASN1_OBJECT_free(doid); + return 0; + } + } + param->flags |= X509_V_FLAG_POLICY_CHECK; + return 1; +} + +int X509_VERIFY_PARAM_set1_host(X509_VERIFY_PARAM *param, + const char *name, size_t namelen) +{ + return int_x509_param_set_hosts(param, SET_HOST, name, namelen); +} + +int X509_VERIFY_PARAM_add1_host(X509_VERIFY_PARAM *param, + const char *name, size_t namelen) +{ + return int_x509_param_set_hosts(param, ADD_HOST, name, namelen); +} + +void X509_VERIFY_PARAM_set_hostflags(X509_VERIFY_PARAM *param, + unsigned int flags) +{ + param->hostflags = flags; +} + +char *X509_VERIFY_PARAM_get0_peername(X509_VERIFY_PARAM *param) +{ + return param->peername; +} + +/* + * Move peername from one param structure to another, freeing any name present + * at the target. If the source is a NULL parameter structure, free and zero + * the target peername. + */ +void X509_VERIFY_PARAM_move_peername(X509_VERIFY_PARAM *to, + X509_VERIFY_PARAM *from) +{ + char *peername = (from != NULL) ? from->peername : NULL; + + if (to->peername != peername) { + OPENSSL_free(to->peername); + to->peername = peername; + } + if (from) + from->peername = NULL; +} + +int X509_VERIFY_PARAM_set1_email(X509_VERIFY_PARAM *param, + const char *email, size_t emaillen) +{ + return int_x509_param_set1(¶m->email, ¶m->emaillen, + email, emaillen); +} + +int X509_VERIFY_PARAM_set1_ip(X509_VERIFY_PARAM *param, + const unsigned char *ip, size_t iplen) +{ + if (iplen != 0 && iplen != 4 && iplen != 16) + return 0; + return int_x509_param_set1((char **)¶m->ip, ¶m->iplen, + (char *)ip, iplen); +} + +int X509_VERIFY_PARAM_set1_ip_asc(X509_VERIFY_PARAM *param, const char *ipasc) +{ + unsigned char ipout[16]; + size_t iplen; + + iplen = (size_t)a2i_ipadd(ipout, ipasc); + if (iplen == 0) + return 0; + return X509_VERIFY_PARAM_set1_ip(param, ipout, iplen); +} + +int X509_VERIFY_PARAM_get_depth(const X509_VERIFY_PARAM *param) +{ + return param->depth; +} + +int X509_VERIFY_PARAM_get_auth_level(const X509_VERIFY_PARAM *param) +{ + return param->auth_level; +} + +const char *X509_VERIFY_PARAM_get0_name(const X509_VERIFY_PARAM *param) +{ + return param->name; +} + +#define vpm_empty_id NULL, 0U, NULL, NULL, 0, NULL, 0 + +/* + * Default verify parameters: these are used for various applications and can + * be overridden by the user specified table. NB: the 'name' field *must* be + * in alphabetical order because it will be searched using OBJ_search. + */ + +static const X509_VERIFY_PARAM default_table[] = { + { + "default", /* X509 default parameters */ + 0, /* Check time */ + 0, /* internal flags */ + X509_V_FLAG_TRUSTED_FIRST, /* flags */ + 0, /* purpose */ + 0, /* trust */ + 100, /* depth */ + -1, /* auth_level */ + NULL, /* policies */ + vpm_empty_id}, + { + "pkcs7", /* S/MIME sign parameters */ + 0, /* Check time */ + 0, /* internal flags */ + 0, /* flags */ + X509_PURPOSE_SMIME_SIGN, /* purpose */ + X509_TRUST_EMAIL, /* trust */ + -1, /* depth */ + -1, /* auth_level */ + NULL, /* policies */ + vpm_empty_id}, + { + "smime_sign", /* S/MIME sign parameters */ + 0, /* Check time */ + 0, /* internal flags */ + 0, /* flags */ + X509_PURPOSE_SMIME_SIGN, /* purpose */ + X509_TRUST_EMAIL, /* trust */ + -1, /* depth */ + -1, /* auth_level */ + NULL, /* policies */ + vpm_empty_id}, + { + "ssl_client", /* SSL/TLS client parameters */ + 0, /* Check time */ + 0, /* internal flags */ + 0, /* flags */ + X509_PURPOSE_SSL_CLIENT, /* purpose */ + X509_TRUST_SSL_CLIENT, /* trust */ + -1, /* depth */ + -1, /* auth_level */ + NULL, /* policies */ + vpm_empty_id}, + { + "ssl_server", /* SSL/TLS server parameters */ + 0, /* Check time */ + 0, /* internal flags */ + 0, /* flags */ + X509_PURPOSE_SSL_SERVER, /* purpose */ + X509_TRUST_SSL_SERVER, /* trust */ + -1, /* depth */ + -1, /* auth_level */ + NULL, /* policies */ + vpm_empty_id} +}; + +static STACK_OF(X509_VERIFY_PARAM) *param_table = NULL; + +static int table_cmp(const X509_VERIFY_PARAM *a, const X509_VERIFY_PARAM *b) +{ + return strcmp(a->name, b->name); +} + +DECLARE_OBJ_BSEARCH_CMP_FN(X509_VERIFY_PARAM, X509_VERIFY_PARAM, table); +IMPLEMENT_OBJ_BSEARCH_CMP_FN(X509_VERIFY_PARAM, X509_VERIFY_PARAM, table); + +static int param_cmp(const X509_VERIFY_PARAM *const *a, + const X509_VERIFY_PARAM *const *b) +{ + return strcmp((*a)->name, (*b)->name); +} + +int X509_VERIFY_PARAM_add0_table(X509_VERIFY_PARAM *param) +{ + int idx; + X509_VERIFY_PARAM *ptmp; + if (param_table == NULL) { + param_table = sk_X509_VERIFY_PARAM_new(param_cmp); + if (param_table == NULL) + return 0; + } else { + idx = sk_X509_VERIFY_PARAM_find(param_table, param); + if (idx != -1) { + ptmp = sk_X509_VERIFY_PARAM_value(param_table, idx); + X509_VERIFY_PARAM_free(ptmp); + (void)sk_X509_VERIFY_PARAM_delete(param_table, idx); + } + } + if (!sk_X509_VERIFY_PARAM_push(param_table, param)) + return 0; + return 1; +} + +int X509_VERIFY_PARAM_get_count(void) +{ + int num = OSSL_NELEM(default_table); + if (param_table) + num += sk_X509_VERIFY_PARAM_num(param_table); + return num; +} + +const X509_VERIFY_PARAM *X509_VERIFY_PARAM_get0(int id) +{ + int num = OSSL_NELEM(default_table); + if (id < num) + return default_table + id; + return sk_X509_VERIFY_PARAM_value(param_table, id - num); +} + +const X509_VERIFY_PARAM *X509_VERIFY_PARAM_lookup(const char *name) +{ + int idx; + X509_VERIFY_PARAM pm; + + pm.name = (char *)name; + if (param_table) { + idx = sk_X509_VERIFY_PARAM_find(param_table, &pm); + if (idx != -1) + return sk_X509_VERIFY_PARAM_value(param_table, idx); + } + return OBJ_bsearch_table(&pm, default_table, OSSL_NELEM(default_table)); +} + +void X509_VERIFY_PARAM_table_cleanup(void) +{ + sk_X509_VERIFY_PARAM_pop_free(param_table, X509_VERIFY_PARAM_free); + param_table = NULL; +} diff --git a/crypto/x509/x509cset.c b/crypto/x509/x509cset.c new file mode 100644 index 000000000..205785961 --- /dev/null +++ b/crypto/x509/x509cset.c @@ -0,0 +1,182 @@ +/* + * Copyright 2001-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#include "internal/cryptlib.h" +#include +#include +#include +#include +#include "internal/x509_int.h" + +int X509_CRL_set_version(X509_CRL *x, long version) +{ + if (x == NULL) + return (0); + if (x->crl.version == NULL) { + if ((x->crl.version = ASN1_INTEGER_new()) == NULL) + return (0); + } + return (ASN1_INTEGER_set(x->crl.version, version)); +} + +int X509_CRL_set_issuer_name(X509_CRL *x, X509_NAME *name) +{ + if (x == NULL) + return (0); + return (X509_NAME_set(&x->crl.issuer, name)); +} + +int X509_CRL_set1_lastUpdate(X509_CRL *x, const ASN1_TIME *tm) +{ + if (x == NULL) + return 0; + return x509_set1_time(&x->crl.lastUpdate, tm); +} + +int X509_CRL_set1_nextUpdate(X509_CRL *x, const ASN1_TIME *tm) +{ + if (x == NULL) + return 0; + return x509_set1_time(&x->crl.nextUpdate, tm); +} + +int X509_CRL_sort(X509_CRL *c) +{ + int i; + X509_REVOKED *r; + /* + * sort the data so it will be written in serial number order + */ + sk_X509_REVOKED_sort(c->crl.revoked); + for (i = 0; i < sk_X509_REVOKED_num(c->crl.revoked); i++) { + r = sk_X509_REVOKED_value(c->crl.revoked, i); + r->sequence = i; + } + c->crl.enc.modified = 1; + return 1; +} + +int X509_CRL_up_ref(X509_CRL *crl) +{ + int i; + + if (CRYPTO_atomic_add(&crl->references, 1, &i, crl->lock) <= 0) + return 0; + + REF_PRINT_COUNT("X509_CRL", crl); + REF_ASSERT_ISNT(i < 2); + return ((i > 1) ? 1 : 0); +} + +long X509_CRL_get_version(const X509_CRL *crl) +{ + return ASN1_INTEGER_get(crl->crl.version); +} + +const ASN1_TIME *X509_CRL_get0_lastUpdate(const X509_CRL *crl) +{ + return crl->crl.lastUpdate; +} + +const ASN1_TIME *X509_CRL_get0_nextUpdate(const X509_CRL *crl) +{ + return crl->crl.nextUpdate; +} + +#if OPENSSL_API_COMPAT < 0x10100000L +ASN1_TIME *X509_CRL_get_lastUpdate(X509_CRL *crl) +{ + return crl->crl.lastUpdate; +} + +ASN1_TIME *X509_CRL_get_nextUpdate(X509_CRL *crl) +{ + return crl->crl.nextUpdate; +} +#endif + +X509_NAME *X509_CRL_get_issuer(const X509_CRL *crl) +{ + return crl->crl.issuer; +} + +const STACK_OF(X509_EXTENSION) *X509_CRL_get0_extensions(const X509_CRL *crl) +{ + return crl->crl.extensions; +} + +STACK_OF(X509_REVOKED) *X509_CRL_get_REVOKED(X509_CRL *crl) +{ + return crl->crl.revoked; +} + +void X509_CRL_get0_signature(const X509_CRL *crl, const ASN1_BIT_STRING **psig, + const X509_ALGOR **palg) +{ + if (psig != NULL) + *psig = &crl->signature; + if (palg != NULL) + *palg = &crl->sig_alg; +} + +int X509_CRL_get_signature_nid(const X509_CRL *crl) +{ + return OBJ_obj2nid(crl->sig_alg.algorithm); +} + +const ASN1_TIME *X509_REVOKED_get0_revocationDate(const X509_REVOKED *x) +{ + return x->revocationDate; +} + +int X509_REVOKED_set_revocationDate(X509_REVOKED *x, ASN1_TIME *tm) +{ + ASN1_TIME *in; + + if (x == NULL) + return (0); + in = x->revocationDate; + if (in != tm) { + in = ASN1_STRING_dup(tm); + if (in != NULL) { + ASN1_TIME_free(x->revocationDate); + x->revocationDate = in; + } + } + return (in != NULL); +} + +const ASN1_INTEGER *X509_REVOKED_get0_serialNumber(const X509_REVOKED *x) +{ + return &x->serialNumber; +} + +int X509_REVOKED_set_serialNumber(X509_REVOKED *x, ASN1_INTEGER *serial) +{ + ASN1_INTEGER *in; + + if (x == NULL) + return (0); + in = &x->serialNumber; + if (in != serial) + return ASN1_STRING_copy(in, serial); + return 1; +} + +const STACK_OF(X509_EXTENSION) *X509_REVOKED_get0_extensions(const X509_REVOKED *r) +{ + return r->extensions; +} + +int i2d_re_X509_CRL_tbs(X509_CRL *crl, unsigned char **pp) +{ + crl->crl.enc.modified = 1; + return i2d_X509_CRL_INFO(&crl->crl, pp); +} diff --git a/crypto/x509/x509name.c b/crypto/x509/x509name.c new file mode 100644 index 000000000..919d8c1e7 --- /dev/null +++ b/crypto/x509/x509name.c @@ -0,0 +1,358 @@ +/* + * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#include +#include "internal/cryptlib.h" +#include +#include +#include +#include +#include "internal/x509_int.h" + +int X509_NAME_get_text_by_NID(X509_NAME *name, int nid, char *buf, int len) +{ + ASN1_OBJECT *obj; + + obj = OBJ_nid2obj(nid); + if (obj == NULL) + return (-1); + return (X509_NAME_get_text_by_OBJ(name, obj, buf, len)); +} + +int X509_NAME_get_text_by_OBJ(X509_NAME *name, const ASN1_OBJECT *obj, char *buf, + int len) +{ + int i; + const ASN1_STRING *data; + + i = X509_NAME_get_index_by_OBJ(name, obj, -1); + if (i < 0) + return (-1); + data = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(name, i)); + i = (data->length > (len - 1)) ? (len - 1) : data->length; + if (buf == NULL) + return (data->length); + memcpy(buf, data->data, i); + buf[i] = '\0'; + return (i); +} + +int X509_NAME_entry_count(const X509_NAME *name) +{ + if (name == NULL) + return (0); + return (sk_X509_NAME_ENTRY_num(name->entries)); +} + +int X509_NAME_get_index_by_NID(X509_NAME *name, int nid, int lastpos) +{ + ASN1_OBJECT *obj; + + obj = OBJ_nid2obj(nid); + if (obj == NULL) + return (-2); + return (X509_NAME_get_index_by_OBJ(name, obj, lastpos)); +} + +/* NOTE: you should be passing -1, not 0 as lastpos */ +int X509_NAME_get_index_by_OBJ(X509_NAME *name, const ASN1_OBJECT *obj, int lastpos) +{ + int n; + X509_NAME_ENTRY *ne; + STACK_OF(X509_NAME_ENTRY) *sk; + + if (name == NULL) + return (-1); + if (lastpos < 0) + lastpos = -1; + sk = name->entries; + n = sk_X509_NAME_ENTRY_num(sk); + for (lastpos++; lastpos < n; lastpos++) { + ne = sk_X509_NAME_ENTRY_value(sk, lastpos); + if (OBJ_cmp(ne->object, obj) == 0) + return (lastpos); + } + return (-1); +} + +X509_NAME_ENTRY *X509_NAME_get_entry(const X509_NAME *name, int loc) +{ + if (name == NULL || sk_X509_NAME_ENTRY_num(name->entries) <= loc + || loc < 0) + return (NULL); + else + return (sk_X509_NAME_ENTRY_value(name->entries, loc)); +} + +X509_NAME_ENTRY *X509_NAME_delete_entry(X509_NAME *name, int loc) +{ + X509_NAME_ENTRY *ret; + int i, n, set_prev, set_next; + STACK_OF(X509_NAME_ENTRY) *sk; + + if (name == NULL || sk_X509_NAME_ENTRY_num(name->entries) <= loc + || loc < 0) + return (NULL); + sk = name->entries; + ret = sk_X509_NAME_ENTRY_delete(sk, loc); + n = sk_X509_NAME_ENTRY_num(sk); + name->modified = 1; + if (loc == n) + return (ret); + + /* else we need to fixup the set field */ + if (loc != 0) + set_prev = (sk_X509_NAME_ENTRY_value(sk, loc - 1))->set; + else + set_prev = ret->set - 1; + set_next = sk_X509_NAME_ENTRY_value(sk, loc)->set; + + /*- + * set_prev is the previous set + * set is the current set + * set_next is the following + * prev 1 1 1 1 1 1 1 1 + * set 1 1 2 2 + * next 1 1 2 2 2 2 3 2 + * so basically only if prev and next differ by 2, then + * re-number down by 1 + */ + if (set_prev + 1 < set_next) + for (i = loc; i < n; i++) + sk_X509_NAME_ENTRY_value(sk, i)->set--; + return (ret); +} + +int X509_NAME_add_entry_by_OBJ(X509_NAME *name, const ASN1_OBJECT *obj, int type, + const unsigned char *bytes, int len, int loc, + int set) +{ + X509_NAME_ENTRY *ne; + int ret; + ne = X509_NAME_ENTRY_create_by_OBJ(NULL, obj, type, bytes, len); + if (!ne) + return 0; + ret = X509_NAME_add_entry(name, ne, loc, set); + X509_NAME_ENTRY_free(ne); + return ret; +} + +int X509_NAME_add_entry_by_NID(X509_NAME *name, int nid, int type, + const unsigned char *bytes, int len, int loc, + int set) +{ + X509_NAME_ENTRY *ne; + int ret; + ne = X509_NAME_ENTRY_create_by_NID(NULL, nid, type, bytes, len); + if (!ne) + return 0; + ret = X509_NAME_add_entry(name, ne, loc, set); + X509_NAME_ENTRY_free(ne); + return ret; +} + +int X509_NAME_add_entry_by_txt(X509_NAME *name, const char *field, int type, + const unsigned char *bytes, int len, int loc, + int set) +{ + X509_NAME_ENTRY *ne; + int ret; + ne = X509_NAME_ENTRY_create_by_txt(NULL, field, type, bytes, len); + if (!ne) + return 0; + ret = X509_NAME_add_entry(name, ne, loc, set); + X509_NAME_ENTRY_free(ne); + return ret; +} + +/* + * if set is -1, append to previous set, 0 'a new one', and 1, prepend to the + * guy we are about to stomp on. + */ +int X509_NAME_add_entry(X509_NAME *name, const X509_NAME_ENTRY *ne, int loc, + int set) +{ + X509_NAME_ENTRY *new_name = NULL; + int n, i, inc; + STACK_OF(X509_NAME_ENTRY) *sk; + + if (name == NULL) + return (0); + sk = name->entries; + n = sk_X509_NAME_ENTRY_num(sk); + if (loc > n) + loc = n; + else if (loc < 0) + loc = n; + + name->modified = 1; + + if (set == -1) { + if (loc == 0) { + set = 0; + inc = 1; + } else { + set = sk_X509_NAME_ENTRY_value(sk, loc - 1)->set; + inc = 0; + } + } else { /* if (set >= 0) */ + + if (loc >= n) { + if (loc != 0) + set = sk_X509_NAME_ENTRY_value(sk, loc - 1)->set + 1; + else + set = 0; + } else + set = sk_X509_NAME_ENTRY_value(sk, loc)->set; + inc = (set == 0) ? 1 : 0; + } + + /* + * X509_NAME_ENTRY_dup is ASN1 generated code, that can't be easily + * const'ified; harmless cast as dup() don't modify its input. + */ + if ((new_name = X509_NAME_ENTRY_dup((X509_NAME_ENTRY *)ne)) == NULL) + goto err; + new_name->set = set; + if (!sk_X509_NAME_ENTRY_insert(sk, new_name, loc)) { + X509err(X509_F_X509_NAME_ADD_ENTRY, ERR_R_MALLOC_FAILURE); + goto err; + } + if (inc) { + n = sk_X509_NAME_ENTRY_num(sk); + for (i = loc + 1; i < n; i++) + sk_X509_NAME_ENTRY_value(sk, i - 1)->set += 1; + } + return (1); + err: + X509_NAME_ENTRY_free(new_name); + return (0); +} + +X509_NAME_ENTRY *X509_NAME_ENTRY_create_by_txt(X509_NAME_ENTRY **ne, + const char *field, int type, + const unsigned char *bytes, + int len) +{ + ASN1_OBJECT *obj; + X509_NAME_ENTRY *nentry; + + obj = OBJ_txt2obj(field, 0); + if (obj == NULL) { + X509err(X509_F_X509_NAME_ENTRY_CREATE_BY_TXT, + X509_R_INVALID_FIELD_NAME); + ERR_add_error_data(2, "name=", field); + return (NULL); + } + nentry = X509_NAME_ENTRY_create_by_OBJ(ne, obj, type, bytes, len); + ASN1_OBJECT_free(obj); + return nentry; +} + +X509_NAME_ENTRY *X509_NAME_ENTRY_create_by_NID(X509_NAME_ENTRY **ne, int nid, + int type, + const unsigned char *bytes, + int len) +{ + ASN1_OBJECT *obj; + X509_NAME_ENTRY *nentry; + + obj = OBJ_nid2obj(nid); + if (obj == NULL) { + X509err(X509_F_X509_NAME_ENTRY_CREATE_BY_NID, X509_R_UNKNOWN_NID); + return (NULL); + } + nentry = X509_NAME_ENTRY_create_by_OBJ(ne, obj, type, bytes, len); + ASN1_OBJECT_free(obj); + return nentry; +} + +X509_NAME_ENTRY *X509_NAME_ENTRY_create_by_OBJ(X509_NAME_ENTRY **ne, + const ASN1_OBJECT *obj, int type, + const unsigned char *bytes, + int len) +{ + X509_NAME_ENTRY *ret; + + if ((ne == NULL) || (*ne == NULL)) { + if ((ret = X509_NAME_ENTRY_new()) == NULL) + return (NULL); + } else + ret = *ne; + + if (!X509_NAME_ENTRY_set_object(ret, obj)) + goto err; + if (!X509_NAME_ENTRY_set_data(ret, type, bytes, len)) + goto err; + + if ((ne != NULL) && (*ne == NULL)) + *ne = ret; + return (ret); + err: + if ((ne == NULL) || (ret != *ne)) + X509_NAME_ENTRY_free(ret); + return (NULL); +} + +int X509_NAME_ENTRY_set_object(X509_NAME_ENTRY *ne, const ASN1_OBJECT *obj) +{ + if ((ne == NULL) || (obj == NULL)) { + X509err(X509_F_X509_NAME_ENTRY_SET_OBJECT, + ERR_R_PASSED_NULL_PARAMETER); + return (0); + } + ASN1_OBJECT_free(ne->object); + ne->object = OBJ_dup(obj); + return ((ne->object == NULL) ? 0 : 1); +} + +int X509_NAME_ENTRY_set_data(X509_NAME_ENTRY *ne, int type, + const unsigned char *bytes, int len) +{ + int i; + + if ((ne == NULL) || ((bytes == NULL) && (len != 0))) + return (0); + if ((type > 0) && (type & MBSTRING_FLAG)) + return ASN1_STRING_set_by_NID(&ne->value, bytes, + len, type, + OBJ_obj2nid(ne->object)) ? 1 : 0; + if (len < 0) + len = strlen((const char *)bytes); + i = ASN1_STRING_set(ne->value, bytes, len); + if (!i) + return (0); + if (type != V_ASN1_UNDEF) { + if (type == V_ASN1_APP_CHOOSE) + ne->value->type = ASN1_PRINTABLE_type(bytes, len); + else + ne->value->type = type; + } + return (1); +} + +ASN1_OBJECT *X509_NAME_ENTRY_get_object(const X509_NAME_ENTRY *ne) +{ + if (ne == NULL) + return (NULL); + return (ne->object); +} + +ASN1_STRING *X509_NAME_ENTRY_get_data(const X509_NAME_ENTRY *ne) +{ + if (ne == NULL) + return (NULL); + return (ne->value); +} + +int X509_NAME_ENTRY_set(const X509_NAME_ENTRY *ne) +{ + return ne->set; +} diff --git a/crypto/x509/x509rset.c b/crypto/x509/x509rset.c new file mode 100644 index 000000000..6dee297a1 --- /dev/null +++ b/crypto/x509/x509rset.c @@ -0,0 +1,40 @@ +/* + * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#include "internal/cryptlib.h" +#include +#include +#include +#include +#include "internal/x509_int.h" + +int X509_REQ_set_version(X509_REQ *x, long version) +{ + if (x == NULL) + return (0); + x->req_info.enc.modified = 1; + return (ASN1_INTEGER_set(x->req_info.version, version)); +} + +int X509_REQ_set_subject_name(X509_REQ *x, X509_NAME *name) +{ + if (x == NULL) + return (0); + x->req_info.enc.modified = 1; + return (X509_NAME_set(&x->req_info.subject, name)); +} + +int X509_REQ_set_pubkey(X509_REQ *x, EVP_PKEY *pkey) +{ + if (x == NULL) + return (0); + x->req_info.enc.modified = 1; + return (X509_PUBKEY_set(&x->req_info.pubkey, pkey)); +} diff --git a/crypto/x509/x509spki.c b/crypto/x509/x509spki.c new file mode 100644 index 000000000..b142485db --- /dev/null +++ b/crypto/x509/x509spki.c @@ -0,0 +1,75 @@ +/* + * Copyright 1999-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#include "internal/cryptlib.h" +#include + +int NETSCAPE_SPKI_set_pubkey(NETSCAPE_SPKI *x, EVP_PKEY *pkey) +{ + if ((x == NULL) || (x->spkac == NULL)) + return (0); + return (X509_PUBKEY_set(&(x->spkac->pubkey), pkey)); +} + +EVP_PKEY *NETSCAPE_SPKI_get_pubkey(NETSCAPE_SPKI *x) +{ + if ((x == NULL) || (x->spkac == NULL)) + return (NULL); + return (X509_PUBKEY_get(x->spkac->pubkey)); +} + +/* Load a Netscape SPKI from a base64 encoded string */ + +NETSCAPE_SPKI *NETSCAPE_SPKI_b64_decode(const char *str, int len) +{ + unsigned char *spki_der; + const unsigned char *p; + int spki_len; + NETSCAPE_SPKI *spki; + if (len <= 0) + len = strlen(str); + if ((spki_der = OPENSSL_malloc(len + 1)) == NULL) { + X509err(X509_F_NETSCAPE_SPKI_B64_DECODE, ERR_R_MALLOC_FAILURE); + return NULL; + } + spki_len = EVP_DecodeBlock(spki_der, (const unsigned char *)str, len); + if (spki_len < 0) { + X509err(X509_F_NETSCAPE_SPKI_B64_DECODE, X509_R_BASE64_DECODE_ERROR); + OPENSSL_free(spki_der); + return NULL; + } + p = spki_der; + spki = d2i_NETSCAPE_SPKI(NULL, &p, spki_len); + OPENSSL_free(spki_der); + return spki; +} + +/* Generate a base64 encoded string from an SPKI */ + +char *NETSCAPE_SPKI_b64_encode(NETSCAPE_SPKI *spki) +{ + unsigned char *der_spki, *p; + char *b64_str; + int der_len; + der_len = i2d_NETSCAPE_SPKI(spki, NULL); + der_spki = OPENSSL_malloc(der_len); + b64_str = OPENSSL_malloc(der_len * 2); + if (der_spki == NULL || b64_str == NULL) { + X509err(X509_F_NETSCAPE_SPKI_B64_ENCODE, ERR_R_MALLOC_FAILURE); + OPENSSL_free(der_spki); + OPENSSL_free(b64_str); + return NULL; + } + p = der_spki; + i2d_NETSCAPE_SPKI(spki, &p); + EVP_EncodeBlock((unsigned char *)b64_str, der_spki, der_len); + OPENSSL_free(der_spki); + return b64_str; +} diff --git a/crypto/x509/x509type.c b/crypto/x509/x509type.c new file mode 100644 index 000000000..aca835527 --- /dev/null +++ b/crypto/x509/x509type.c @@ -0,0 +1,77 @@ +/* + * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#include "internal/cryptlib.h" +#include +#include +#include + +int X509_certificate_type(const X509 *x, const EVP_PKEY *pkey) +{ + const EVP_PKEY *pk; + int ret = 0, i; + + if (x == NULL) + return (0); + + if (pkey == NULL) + pk = X509_get0_pubkey(x); + else + pk = pkey; + + if (pk == NULL) + return (0); + + switch (EVP_PKEY_id(pk)) { + case EVP_PKEY_RSA: + ret = EVP_PK_RSA | EVP_PKT_SIGN; +/* if (!sign only extension) */ + ret |= EVP_PKT_ENC; + break; + case EVP_PKEY_DSA: + ret = EVP_PK_DSA | EVP_PKT_SIGN; + break; + case EVP_PKEY_EC: + ret = EVP_PK_EC | EVP_PKT_SIGN | EVP_PKT_EXCH; + break; + case EVP_PKEY_DH: + ret = EVP_PK_DH | EVP_PKT_EXCH; + break; + case NID_id_GostR3410_2001: + case NID_id_GostR3410_2012_256: + case NID_id_GostR3410_2012_512: + ret = EVP_PKT_EXCH | EVP_PKT_SIGN; + break; + default: + break; + } + + i = X509_get_signature_nid(x); + if (i && OBJ_find_sigid_algs(i, NULL, &i)) { + + switch (i) { + case NID_rsaEncryption: + case NID_rsa: + ret |= EVP_PKS_RSA; + break; + case NID_dsa: + case NID_dsa_2: + ret |= EVP_PKS_DSA; + break; + case NID_X9_62_id_ecPublicKey: + ret |= EVP_PKS_EC; + break; + default: + break; + } + } + + return (ret); +} diff --git a/crypto/x509/x_all.c b/crypto/x509/x_all.c new file mode 100644 index 000000000..962942f99 --- /dev/null +++ b/crypto/x509/x_all.c @@ -0,0 +1,534 @@ +/* + * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#include +#include "internal/cryptlib.h" +#include +#include +#include +#include +#include "internal/x509_int.h" +#include +#ifndef OPENSSL_NO_RSA +# include +#endif +#ifndef OPENSSL_NO_DSA +#include +#endif +#include + +int X509_verify(X509 *a, EVP_PKEY *r) +{ + if (X509_ALGOR_cmp(&a->sig_alg, &a->cert_info.signature)) + return 0; + return (ASN1_item_verify(ASN1_ITEM_rptr(X509_CINF), &a->sig_alg, + &a->signature, &a->cert_info, r)); +} + +int X509_REQ_verify(X509_REQ *a, EVP_PKEY *r) +{ + return (ASN1_item_verify(ASN1_ITEM_rptr(X509_REQ_INFO), + &a->sig_alg, a->signature, &a->req_info, r)); +} + +int NETSCAPE_SPKI_verify(NETSCAPE_SPKI *a, EVP_PKEY *r) +{ + return (ASN1_item_verify(ASN1_ITEM_rptr(NETSCAPE_SPKAC), + &a->sig_algor, a->signature, a->spkac, r)); +} + +int X509_sign(X509 *x, EVP_PKEY *pkey, const EVP_MD *md) +{ + x->cert_info.enc.modified = 1; + return (ASN1_item_sign(ASN1_ITEM_rptr(X509_CINF), &x->cert_info.signature, + &x->sig_alg, &x->signature, &x->cert_info, pkey, + md)); +} + +int X509_sign_ctx(X509 *x, EVP_MD_CTX *ctx) +{ + x->cert_info.enc.modified = 1; + return ASN1_item_sign_ctx(ASN1_ITEM_rptr(X509_CINF), + &x->cert_info.signature, + &x->sig_alg, &x->signature, &x->cert_info, ctx); +} + +#ifndef OPENSSL_NO_OCSP +int X509_http_nbio(OCSP_REQ_CTX *rctx, X509 **pcert) +{ + return OCSP_REQ_CTX_nbio_d2i(rctx, + (ASN1_VALUE **)pcert, ASN1_ITEM_rptr(X509)); +} +#endif + +int X509_REQ_sign(X509_REQ *x, EVP_PKEY *pkey, const EVP_MD *md) +{ + return (ASN1_item_sign(ASN1_ITEM_rptr(X509_REQ_INFO), &x->sig_alg, NULL, + x->signature, &x->req_info, pkey, md)); +} + +int X509_REQ_sign_ctx(X509_REQ *x, EVP_MD_CTX *ctx) +{ + return ASN1_item_sign_ctx(ASN1_ITEM_rptr(X509_REQ_INFO), + &x->sig_alg, NULL, x->signature, &x->req_info, + ctx); +} + +int X509_CRL_sign(X509_CRL *x, EVP_PKEY *pkey, const EVP_MD *md) +{ + x->crl.enc.modified = 1; + return (ASN1_item_sign(ASN1_ITEM_rptr(X509_CRL_INFO), &x->crl.sig_alg, + &x->sig_alg, &x->signature, &x->crl, pkey, md)); +} + +int X509_CRL_sign_ctx(X509_CRL *x, EVP_MD_CTX *ctx) +{ + x->crl.enc.modified = 1; + return ASN1_item_sign_ctx(ASN1_ITEM_rptr(X509_CRL_INFO), + &x->crl.sig_alg, &x->sig_alg, &x->signature, + &x->crl, ctx); +} + +#ifndef OPENSSL_NO_OCSP +int X509_CRL_http_nbio(OCSP_REQ_CTX *rctx, X509_CRL **pcrl) +{ + return OCSP_REQ_CTX_nbio_d2i(rctx, + (ASN1_VALUE **)pcrl, + ASN1_ITEM_rptr(X509_CRL)); +} +#endif + +int NETSCAPE_SPKI_sign(NETSCAPE_SPKI *x, EVP_PKEY *pkey, const EVP_MD *md) +{ + return (ASN1_item_sign(ASN1_ITEM_rptr(NETSCAPE_SPKAC), &x->sig_algor, NULL, + x->signature, x->spkac, pkey, md)); +} + +#ifndef OPENSSL_NO_STDIO +X509 *d2i_X509_fp(FILE *fp, X509 **x509) +{ + return ASN1_item_d2i_fp(ASN1_ITEM_rptr(X509), fp, x509); +} + +int i2d_X509_fp(FILE *fp, X509 *x509) +{ + return ASN1_item_i2d_fp(ASN1_ITEM_rptr(X509), fp, x509); +} +#endif + +X509 *d2i_X509_bio(BIO *bp, X509 **x509) +{ + return ASN1_item_d2i_bio(ASN1_ITEM_rptr(X509), bp, x509); +} + +int i2d_X509_bio(BIO *bp, X509 *x509) +{ + return ASN1_item_i2d_bio(ASN1_ITEM_rptr(X509), bp, x509); +} + +#ifndef OPENSSL_NO_STDIO +X509_CRL *d2i_X509_CRL_fp(FILE *fp, X509_CRL **crl) +{ + return ASN1_item_d2i_fp(ASN1_ITEM_rptr(X509_CRL), fp, crl); +} + +int i2d_X509_CRL_fp(FILE *fp, X509_CRL *crl) +{ + return ASN1_item_i2d_fp(ASN1_ITEM_rptr(X509_CRL), fp, crl); +} +#endif + +X509_CRL *d2i_X509_CRL_bio(BIO *bp, X509_CRL **crl) +{ + return ASN1_item_d2i_bio(ASN1_ITEM_rptr(X509_CRL), bp, crl); +} + +int i2d_X509_CRL_bio(BIO *bp, X509_CRL *crl) +{ + return ASN1_item_i2d_bio(ASN1_ITEM_rptr(X509_CRL), bp, crl); +} + +#ifndef OPENSSL_NO_STDIO +PKCS7 *d2i_PKCS7_fp(FILE *fp, PKCS7 **p7) +{ + return ASN1_item_d2i_fp(ASN1_ITEM_rptr(PKCS7), fp, p7); +} + +int i2d_PKCS7_fp(FILE *fp, PKCS7 *p7) +{ + return ASN1_item_i2d_fp(ASN1_ITEM_rptr(PKCS7), fp, p7); +} +#endif + +PKCS7 *d2i_PKCS7_bio(BIO *bp, PKCS7 **p7) +{ + return ASN1_item_d2i_bio(ASN1_ITEM_rptr(PKCS7), bp, p7); +} + +int i2d_PKCS7_bio(BIO *bp, PKCS7 *p7) +{ + return ASN1_item_i2d_bio(ASN1_ITEM_rptr(PKCS7), bp, p7); +} + +#ifndef OPENSSL_NO_STDIO +X509_REQ *d2i_X509_REQ_fp(FILE *fp, X509_REQ **req) +{ + return ASN1_item_d2i_fp(ASN1_ITEM_rptr(X509_REQ), fp, req); +} + +int i2d_X509_REQ_fp(FILE *fp, X509_REQ *req) +{ + return ASN1_item_i2d_fp(ASN1_ITEM_rptr(X509_REQ), fp, req); +} +#endif + +X509_REQ *d2i_X509_REQ_bio(BIO *bp, X509_REQ **req) +{ + return ASN1_item_d2i_bio(ASN1_ITEM_rptr(X509_REQ), bp, req); +} + +int i2d_X509_REQ_bio(BIO *bp, X509_REQ *req) +{ + return ASN1_item_i2d_bio(ASN1_ITEM_rptr(X509_REQ), bp, req); +} + +#ifndef OPENSSL_NO_RSA + +# ifndef OPENSSL_NO_STDIO +RSA *d2i_RSAPrivateKey_fp(FILE *fp, RSA **rsa) +{ + return ASN1_item_d2i_fp(ASN1_ITEM_rptr(RSAPrivateKey), fp, rsa); +} + +int i2d_RSAPrivateKey_fp(FILE *fp, RSA *rsa) +{ + return ASN1_item_i2d_fp(ASN1_ITEM_rptr(RSAPrivateKey), fp, rsa); +} + +RSA *d2i_RSAPublicKey_fp(FILE *fp, RSA **rsa) +{ + return ASN1_item_d2i_fp(ASN1_ITEM_rptr(RSAPublicKey), fp, rsa); +} + +RSA *d2i_RSA_PUBKEY_fp(FILE *fp, RSA **rsa) +{ + return ASN1_d2i_fp((void *(*)(void)) + RSA_new, (D2I_OF(void)) d2i_RSA_PUBKEY, fp, + (void **)rsa); +} + +int i2d_RSAPublicKey_fp(FILE *fp, RSA *rsa) +{ + return ASN1_item_i2d_fp(ASN1_ITEM_rptr(RSAPublicKey), fp, rsa); +} + +int i2d_RSA_PUBKEY_fp(FILE *fp, RSA *rsa) +{ + return ASN1_i2d_fp((I2D_OF(void))i2d_RSA_PUBKEY, fp, rsa); +} +# endif + +RSA *d2i_RSAPrivateKey_bio(BIO *bp, RSA **rsa) +{ + return ASN1_item_d2i_bio(ASN1_ITEM_rptr(RSAPrivateKey), bp, rsa); +} + +int i2d_RSAPrivateKey_bio(BIO *bp, RSA *rsa) +{ + return ASN1_item_i2d_bio(ASN1_ITEM_rptr(RSAPrivateKey), bp, rsa); +} + +RSA *d2i_RSAPublicKey_bio(BIO *bp, RSA **rsa) +{ + return ASN1_item_d2i_bio(ASN1_ITEM_rptr(RSAPublicKey), bp, rsa); +} + +RSA *d2i_RSA_PUBKEY_bio(BIO *bp, RSA **rsa) +{ + return ASN1_d2i_bio_of(RSA, RSA_new, d2i_RSA_PUBKEY, bp, rsa); +} + +int i2d_RSAPublicKey_bio(BIO *bp, RSA *rsa) +{ + return ASN1_item_i2d_bio(ASN1_ITEM_rptr(RSAPublicKey), bp, rsa); +} + +int i2d_RSA_PUBKEY_bio(BIO *bp, RSA *rsa) +{ + return ASN1_i2d_bio_of(RSA, i2d_RSA_PUBKEY, bp, rsa); +} +#endif + +#ifndef OPENSSL_NO_DSA +# ifndef OPENSSL_NO_STDIO +DSA *d2i_DSAPrivateKey_fp(FILE *fp, DSA **dsa) +{ + return ASN1_d2i_fp_of(DSA, DSA_new, d2i_DSAPrivateKey, fp, dsa); +} + +int i2d_DSAPrivateKey_fp(FILE *fp, DSA *dsa) +{ + return ASN1_i2d_fp_of_const(DSA, i2d_DSAPrivateKey, fp, dsa); +} + +DSA *d2i_DSA_PUBKEY_fp(FILE *fp, DSA **dsa) +{ + return ASN1_d2i_fp_of(DSA, DSA_new, d2i_DSA_PUBKEY, fp, dsa); +} + +int i2d_DSA_PUBKEY_fp(FILE *fp, DSA *dsa) +{ + return ASN1_i2d_fp_of(DSA, i2d_DSA_PUBKEY, fp, dsa); +} +# endif + +DSA *d2i_DSAPrivateKey_bio(BIO *bp, DSA **dsa) +{ + return ASN1_d2i_bio_of(DSA, DSA_new, d2i_DSAPrivateKey, bp, dsa); +} + +int i2d_DSAPrivateKey_bio(BIO *bp, DSA *dsa) +{ + return ASN1_i2d_bio_of_const(DSA, i2d_DSAPrivateKey, bp, dsa); +} + +DSA *d2i_DSA_PUBKEY_bio(BIO *bp, DSA **dsa) +{ + return ASN1_d2i_bio_of(DSA, DSA_new, d2i_DSA_PUBKEY, bp, dsa); +} + +int i2d_DSA_PUBKEY_bio(BIO *bp, DSA *dsa) +{ + return ASN1_i2d_bio_of(DSA, i2d_DSA_PUBKEY, bp, dsa); +} + +#endif + +#ifndef OPENSSL_NO_EC +# ifndef OPENSSL_NO_STDIO +EC_KEY *d2i_EC_PUBKEY_fp(FILE *fp, EC_KEY **eckey) +{ + return ASN1_d2i_fp_of(EC_KEY, EC_KEY_new, d2i_EC_PUBKEY, fp, eckey); +} + +int i2d_EC_PUBKEY_fp(FILE *fp, EC_KEY *eckey) +{ + return ASN1_i2d_fp_of(EC_KEY, i2d_EC_PUBKEY, fp, eckey); +} + +EC_KEY *d2i_ECPrivateKey_fp(FILE *fp, EC_KEY **eckey) +{ + return ASN1_d2i_fp_of(EC_KEY, EC_KEY_new, d2i_ECPrivateKey, fp, eckey); +} + +int i2d_ECPrivateKey_fp(FILE *fp, EC_KEY *eckey) +{ + return ASN1_i2d_fp_of(EC_KEY, i2d_ECPrivateKey, fp, eckey); +} +# endif +EC_KEY *d2i_EC_PUBKEY_bio(BIO *bp, EC_KEY **eckey) +{ + return ASN1_d2i_bio_of(EC_KEY, EC_KEY_new, d2i_EC_PUBKEY, bp, eckey); +} + +int i2d_EC_PUBKEY_bio(BIO *bp, EC_KEY *ecdsa) +{ + return ASN1_i2d_bio_of(EC_KEY, i2d_EC_PUBKEY, bp, ecdsa); +} + +EC_KEY *d2i_ECPrivateKey_bio(BIO *bp, EC_KEY **eckey) +{ + return ASN1_d2i_bio_of(EC_KEY, EC_KEY_new, d2i_ECPrivateKey, bp, eckey); +} + +int i2d_ECPrivateKey_bio(BIO *bp, EC_KEY *eckey) +{ + return ASN1_i2d_bio_of(EC_KEY, i2d_ECPrivateKey, bp, eckey); +} +#endif + +int X509_pubkey_digest(const X509 *data, const EVP_MD *type, + unsigned char *md, unsigned int *len) +{ + ASN1_BIT_STRING *key; + key = X509_get0_pubkey_bitstr(data); + if (!key) + return 0; + return EVP_Digest(key->data, key->length, md, len, type, NULL); +} + +int X509_digest(const X509 *data, const EVP_MD *type, unsigned char *md, + unsigned int *len) +{ +#ifndef OPENSSL_NO_SHA + if (type == EVP_sha1() && (data->ex_flags & EXFLAG_SET) != 0) { + /* Asking for SHA1 and we already computed it. */ + if (len != NULL) + *len = sizeof(data->sha1_hash); + memcpy(md, data->sha1_hash, sizeof(data->sha1_hash)); + return 1; + } +#endif + return (ASN1_item_digest + (ASN1_ITEM_rptr(X509), type, (char *)data, md, len)); +} + +int X509_CRL_digest(const X509_CRL *data, const EVP_MD *type, + unsigned char *md, unsigned int *len) +{ +#ifndef OPENSSL_NO_SHA + if (type == EVP_sha1()) { + /* Asking for SHA1; always computed in CRL d2i. */ + if (len != NULL) + *len = sizeof(data->sha1_hash); + memcpy(md, data->sha1_hash, sizeof(data->sha1_hash)); + return 1; + } +#endif + return (ASN1_item_digest + (ASN1_ITEM_rptr(X509_CRL), type, (char *)data, md, len)); +} + +int X509_REQ_digest(const X509_REQ *data, const EVP_MD *type, + unsigned char *md, unsigned int *len) +{ + return (ASN1_item_digest + (ASN1_ITEM_rptr(X509_REQ), type, (char *)data, md, len)); +} + +int X509_NAME_digest(const X509_NAME *data, const EVP_MD *type, + unsigned char *md, unsigned int *len) +{ + return (ASN1_item_digest + (ASN1_ITEM_rptr(X509_NAME), type, (char *)data, md, len)); +} + +int PKCS7_ISSUER_AND_SERIAL_digest(PKCS7_ISSUER_AND_SERIAL *data, + const EVP_MD *type, unsigned char *md, + unsigned int *len) +{ + return (ASN1_item_digest(ASN1_ITEM_rptr(PKCS7_ISSUER_AND_SERIAL), type, + (char *)data, md, len)); +} + +#ifndef OPENSSL_NO_STDIO +X509_SIG *d2i_PKCS8_fp(FILE *fp, X509_SIG **p8) +{ + return ASN1_d2i_fp_of(X509_SIG, X509_SIG_new, d2i_X509_SIG, fp, p8); +} + +int i2d_PKCS8_fp(FILE *fp, X509_SIG *p8) +{ + return ASN1_i2d_fp_of(X509_SIG, i2d_X509_SIG, fp, p8); +} +#endif + +X509_SIG *d2i_PKCS8_bio(BIO *bp, X509_SIG **p8) +{ + return ASN1_d2i_bio_of(X509_SIG, X509_SIG_new, d2i_X509_SIG, bp, p8); +} + +int i2d_PKCS8_bio(BIO *bp, X509_SIG *p8) +{ + return ASN1_i2d_bio_of(X509_SIG, i2d_X509_SIG, bp, p8); +} + +#ifndef OPENSSL_NO_STDIO +PKCS8_PRIV_KEY_INFO *d2i_PKCS8_PRIV_KEY_INFO_fp(FILE *fp, + PKCS8_PRIV_KEY_INFO **p8inf) +{ + return ASN1_d2i_fp_of(PKCS8_PRIV_KEY_INFO, PKCS8_PRIV_KEY_INFO_new, + d2i_PKCS8_PRIV_KEY_INFO, fp, p8inf); +} + +int i2d_PKCS8_PRIV_KEY_INFO_fp(FILE *fp, PKCS8_PRIV_KEY_INFO *p8inf) +{ + return ASN1_i2d_fp_of(PKCS8_PRIV_KEY_INFO, i2d_PKCS8_PRIV_KEY_INFO, fp, + p8inf); +} + +int i2d_PKCS8PrivateKeyInfo_fp(FILE *fp, EVP_PKEY *key) +{ + PKCS8_PRIV_KEY_INFO *p8inf; + int ret; + p8inf = EVP_PKEY2PKCS8(key); + if (!p8inf) + return 0; + ret = i2d_PKCS8_PRIV_KEY_INFO_fp(fp, p8inf); + PKCS8_PRIV_KEY_INFO_free(p8inf); + return ret; +} + +int i2d_PrivateKey_fp(FILE *fp, EVP_PKEY *pkey) +{ + return ASN1_i2d_fp_of(EVP_PKEY, i2d_PrivateKey, fp, pkey); +} + +EVP_PKEY *d2i_PrivateKey_fp(FILE *fp, EVP_PKEY **a) +{ + return ASN1_d2i_fp_of(EVP_PKEY, EVP_PKEY_new, d2i_AutoPrivateKey, fp, a); +} + +int i2d_PUBKEY_fp(FILE *fp, EVP_PKEY *pkey) +{ + return ASN1_i2d_fp_of(EVP_PKEY, i2d_PUBKEY, fp, pkey); +} + +EVP_PKEY *d2i_PUBKEY_fp(FILE *fp, EVP_PKEY **a) +{ + return ASN1_d2i_fp_of(EVP_PKEY, EVP_PKEY_new, d2i_PUBKEY, fp, a); +} + +#endif + +PKCS8_PRIV_KEY_INFO *d2i_PKCS8_PRIV_KEY_INFO_bio(BIO *bp, + PKCS8_PRIV_KEY_INFO **p8inf) +{ + return ASN1_d2i_bio_of(PKCS8_PRIV_KEY_INFO, PKCS8_PRIV_KEY_INFO_new, + d2i_PKCS8_PRIV_KEY_INFO, bp, p8inf); +} + +int i2d_PKCS8_PRIV_KEY_INFO_bio(BIO *bp, PKCS8_PRIV_KEY_INFO *p8inf) +{ + return ASN1_i2d_bio_of(PKCS8_PRIV_KEY_INFO, i2d_PKCS8_PRIV_KEY_INFO, bp, + p8inf); +} + +int i2d_PKCS8PrivateKeyInfo_bio(BIO *bp, EVP_PKEY *key) +{ + PKCS8_PRIV_KEY_INFO *p8inf; + int ret; + p8inf = EVP_PKEY2PKCS8(key); + if (!p8inf) + return 0; + ret = i2d_PKCS8_PRIV_KEY_INFO_bio(bp, p8inf); + PKCS8_PRIV_KEY_INFO_free(p8inf); + return ret; +} + +int i2d_PrivateKey_bio(BIO *bp, EVP_PKEY *pkey) +{ + return ASN1_i2d_bio_of(EVP_PKEY, i2d_PrivateKey, bp, pkey); +} + +EVP_PKEY *d2i_PrivateKey_bio(BIO *bp, EVP_PKEY **a) +{ + return ASN1_d2i_bio_of(EVP_PKEY, EVP_PKEY_new, d2i_AutoPrivateKey, bp, a); +} + +int i2d_PUBKEY_bio(BIO *bp, EVP_PKEY *pkey) +{ + return ASN1_i2d_bio_of(EVP_PKEY, i2d_PUBKEY, bp, pkey); +} + +EVP_PKEY *d2i_PUBKEY_bio(BIO *bp, EVP_PKEY **a) +{ + return ASN1_d2i_bio_of(EVP_PKEY, EVP_PKEY_new, d2i_PUBKEY, bp, a); +} diff --git a/crypto/x509/x_attrib.c b/crypto/x509/x_attrib.c new file mode 100644 index 000000000..35f4aeef2 --- /dev/null +++ b/crypto/x509/x_attrib.c @@ -0,0 +1,55 @@ +/* + * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#include "internal/cryptlib.h" +#include +#include +#include +#include "x509_lcl.h" + +/*- + * X509_ATTRIBUTE: this has the following form: + * + * typedef struct x509_attributes_st + * { + * ASN1_OBJECT *object; + * STACK_OF(ASN1_TYPE) *set; + * } X509_ATTRIBUTE; + * + */ + +ASN1_SEQUENCE(X509_ATTRIBUTE) = { + ASN1_SIMPLE(X509_ATTRIBUTE, object, ASN1_OBJECT), + ASN1_SET_OF(X509_ATTRIBUTE, set, ASN1_ANY) +} ASN1_SEQUENCE_END(X509_ATTRIBUTE) + +IMPLEMENT_ASN1_FUNCTIONS(X509_ATTRIBUTE) +IMPLEMENT_ASN1_DUP_FUNCTION(X509_ATTRIBUTE) + +X509_ATTRIBUTE *X509_ATTRIBUTE_create(int nid, int atrtype, void *value) +{ + X509_ATTRIBUTE *ret = NULL; + ASN1_TYPE *val = NULL; + + if ((ret = X509_ATTRIBUTE_new()) == NULL) + return (NULL); + ret->object = OBJ_nid2obj(nid); + if ((val = ASN1_TYPE_new()) == NULL) + goto err; + if (!sk_ASN1_TYPE_push(ret->set, val)) + goto err; + + ASN1_TYPE_set(val, atrtype, value); + return (ret); + err: + X509_ATTRIBUTE_free(ret); + ASN1_TYPE_free(val); + return (NULL); +} diff --git a/crypto/x509/x_crl.c b/crypto/x509/x_crl.c new file mode 100644 index 000000000..ce362f866 --- /dev/null +++ b/crypto/x509/x_crl.c @@ -0,0 +1,465 @@ +/* + * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#include "internal/cryptlib.h" +#include +#include +#include "internal/x509_int.h" +#include +#include "x509_lcl.h" + +static int X509_REVOKED_cmp(const X509_REVOKED *const *a, + const X509_REVOKED *const *b); +static void setup_idp(X509_CRL *crl, ISSUING_DIST_POINT *idp); + +ASN1_SEQUENCE(X509_REVOKED) = { + ASN1_EMBED(X509_REVOKED,serialNumber, ASN1_INTEGER), + ASN1_SIMPLE(X509_REVOKED,revocationDate, ASN1_TIME), + ASN1_SEQUENCE_OF_OPT(X509_REVOKED,extensions, X509_EXTENSION) +} ASN1_SEQUENCE_END(X509_REVOKED) + +static int def_crl_verify(X509_CRL *crl, EVP_PKEY *r); +static int def_crl_lookup(X509_CRL *crl, + X509_REVOKED **ret, ASN1_INTEGER *serial, + X509_NAME *issuer); + +static X509_CRL_METHOD int_crl_meth = { + 0, + 0, 0, + def_crl_lookup, + def_crl_verify +}; + +static const X509_CRL_METHOD *default_crl_method = &int_crl_meth; + +/* + * The X509_CRL_INFO structure needs a bit of customisation. Since we cache + * the original encoding the signature won't be affected by reordering of the + * revoked field. + */ +static int crl_inf_cb(int operation, ASN1_VALUE **pval, const ASN1_ITEM *it, + void *exarg) +{ + X509_CRL_INFO *a = (X509_CRL_INFO *)*pval; + + if (!a || !a->revoked) + return 1; + switch (operation) { + /* + * Just set cmp function here. We don't sort because that would + * affect the output of X509_CRL_print(). + */ + case ASN1_OP_D2I_POST: + (void)sk_X509_REVOKED_set_cmp_func(a->revoked, X509_REVOKED_cmp); + break; + } + return 1; +} + + +ASN1_SEQUENCE_enc(X509_CRL_INFO, enc, crl_inf_cb) = { + ASN1_OPT(X509_CRL_INFO, version, ASN1_INTEGER), + ASN1_EMBED(X509_CRL_INFO, sig_alg, X509_ALGOR), + ASN1_SIMPLE(X509_CRL_INFO, issuer, X509_NAME), + ASN1_SIMPLE(X509_CRL_INFO, lastUpdate, ASN1_TIME), + ASN1_OPT(X509_CRL_INFO, nextUpdate, ASN1_TIME), + ASN1_SEQUENCE_OF_OPT(X509_CRL_INFO, revoked, X509_REVOKED), + ASN1_EXP_SEQUENCE_OF_OPT(X509_CRL_INFO, extensions, X509_EXTENSION, 0) +} ASN1_SEQUENCE_END_enc(X509_CRL_INFO, X509_CRL_INFO) + +/* + * Set CRL entry issuer according to CRL certificate issuer extension. Check + * for unhandled critical CRL entry extensions. + */ + +static int crl_set_issuers(X509_CRL *crl) +{ + + int i, j; + GENERAL_NAMES *gens, *gtmp; + STACK_OF(X509_REVOKED) *revoked; + + revoked = X509_CRL_get_REVOKED(crl); + + gens = NULL; + for (i = 0; i < sk_X509_REVOKED_num(revoked); i++) { + X509_REVOKED *rev = sk_X509_REVOKED_value(revoked, i); + STACK_OF(X509_EXTENSION) *exts; + ASN1_ENUMERATED *reason; + X509_EXTENSION *ext; + gtmp = X509_REVOKED_get_ext_d2i(rev, + NID_certificate_issuer, &j, NULL); + if (!gtmp && (j != -1)) { + crl->flags |= EXFLAG_INVALID; + return 1; + } + + if (gtmp) { + gens = gtmp; + if (!crl->issuers) { + crl->issuers = sk_GENERAL_NAMES_new_null(); + if (!crl->issuers) + return 0; + } + if (!sk_GENERAL_NAMES_push(crl->issuers, gtmp)) + return 0; + } + rev->issuer = gens; + + reason = X509_REVOKED_get_ext_d2i(rev, NID_crl_reason, &j, NULL); + if (!reason && (j != -1)) { + crl->flags |= EXFLAG_INVALID; + return 1; + } + + if (reason) { + rev->reason = ASN1_ENUMERATED_get(reason); + ASN1_ENUMERATED_free(reason); + } else + rev->reason = CRL_REASON_NONE; + + /* Check for critical CRL entry extensions */ + + exts = rev->extensions; + + for (j = 0; j < sk_X509_EXTENSION_num(exts); j++) { + ext = sk_X509_EXTENSION_value(exts, j); + if (X509_EXTENSION_get_critical(ext)) { + if (OBJ_obj2nid(X509_EXTENSION_get_object(ext)) == NID_certificate_issuer) + continue; + crl->flags |= EXFLAG_CRITICAL; + break; + } + } + + } + + return 1; + +} + +/* + * The X509_CRL structure needs a bit of customisation. Cache some extensions + * and hash of the whole CRL. + */ +static int crl_cb(int operation, ASN1_VALUE **pval, const ASN1_ITEM *it, + void *exarg) +{ + X509_CRL *crl = (X509_CRL *)*pval; + STACK_OF(X509_EXTENSION) *exts; + X509_EXTENSION *ext; + int idx; + const EVP_MD *md; +#ifndef OPENSSL_NO_SHA + md = EVP_sha1(); +#elif !defined(OPENSSL_NO_SM3) + md = EVP_sm3(); +#else + return 0; +#endif + + switch (operation) { + case ASN1_OP_NEW_POST: + crl->idp = NULL; + crl->akid = NULL; + crl->flags = 0; + crl->idp_flags = 0; + crl->idp_reasons = CRLDP_ALL_REASONS; + crl->meth = default_crl_method; + crl->meth_data = NULL; + crl->issuers = NULL; + crl->crl_number = NULL; + crl->base_crl_number = NULL; + break; + + case ASN1_OP_D2I_POST: + X509_CRL_digest(crl, md, crl->sha1_hash, NULL); + crl->idp = X509_CRL_get_ext_d2i(crl, + NID_issuing_distribution_point, NULL, + NULL); + if (crl->idp) + setup_idp(crl, crl->idp); + + crl->akid = X509_CRL_get_ext_d2i(crl, + NID_authority_key_identifier, NULL, + NULL); + + crl->crl_number = X509_CRL_get_ext_d2i(crl, + NID_crl_number, NULL, NULL); + + crl->base_crl_number = X509_CRL_get_ext_d2i(crl, + NID_delta_crl, NULL, + NULL); + /* Delta CRLs must have CRL number */ + if (crl->base_crl_number && !crl->crl_number) + crl->flags |= EXFLAG_INVALID; + + /* + * See if we have any unhandled critical CRL extensions and indicate + * this in a flag. We only currently handle IDP so anything else + * critical sets the flag. This code accesses the X509_CRL structure + * directly: applications shouldn't do this. + */ + + exts = crl->crl.extensions; + + for (idx = 0; idx < sk_X509_EXTENSION_num(exts); idx++) { + int nid; + ext = sk_X509_EXTENSION_value(exts, idx); + nid = OBJ_obj2nid(X509_EXTENSION_get_object(ext)); + if (nid == NID_freshest_crl) + crl->flags |= EXFLAG_FRESHEST; + if (X509_EXTENSION_get_critical(ext)) { + /* We handle IDP and deltas */ + if ((nid == NID_issuing_distribution_point) + || (nid == NID_authority_key_identifier) + || (nid == NID_delta_crl)) + continue; + crl->flags |= EXFLAG_CRITICAL; + break; + } + } + + if (!crl_set_issuers(crl)) + return 0; + + if (crl->meth->crl_init) { + if (crl->meth->crl_init(crl) == 0) + return 0; + } + break; + + case ASN1_OP_FREE_POST: + if (crl->meth->crl_free) { + if (!crl->meth->crl_free(crl)) + return 0; + } + AUTHORITY_KEYID_free(crl->akid); + ISSUING_DIST_POINT_free(crl->idp); + ASN1_INTEGER_free(crl->crl_number); + ASN1_INTEGER_free(crl->base_crl_number); + sk_GENERAL_NAMES_pop_free(crl->issuers, GENERAL_NAMES_free); + break; + } + return 1; +} + +/* Convert IDP into a more convenient form */ + +static void setup_idp(X509_CRL *crl, ISSUING_DIST_POINT *idp) +{ + int idp_only = 0; + /* Set various flags according to IDP */ + crl->idp_flags |= IDP_PRESENT; + if (idp->onlyuser > 0) { + idp_only++; + crl->idp_flags |= IDP_ONLYUSER; + } + if (idp->onlyCA > 0) { + idp_only++; + crl->idp_flags |= IDP_ONLYCA; + } + if (idp->onlyattr > 0) { + idp_only++; + crl->idp_flags |= IDP_ONLYATTR; + } + + if (idp_only > 1) + crl->idp_flags |= IDP_INVALID; + + if (idp->indirectCRL > 0) + crl->idp_flags |= IDP_INDIRECT; + + if (idp->onlysomereasons) { + crl->idp_flags |= IDP_REASONS; + if (idp->onlysomereasons->length > 0) + crl->idp_reasons = idp->onlysomereasons->data[0]; + if (idp->onlysomereasons->length > 1) + crl->idp_reasons |= (idp->onlysomereasons->data[1] << 8); + crl->idp_reasons &= CRLDP_ALL_REASONS; + } + + DIST_POINT_set_dpname(idp->distpoint, X509_CRL_get_issuer(crl)); +} + +ASN1_SEQUENCE_ref(X509_CRL, crl_cb) = { + ASN1_EMBED(X509_CRL, crl, X509_CRL_INFO), + ASN1_EMBED(X509_CRL, sig_alg, X509_ALGOR), + ASN1_EMBED(X509_CRL, signature, ASN1_BIT_STRING) +} ASN1_SEQUENCE_END_ref(X509_CRL, X509_CRL) + +IMPLEMENT_ASN1_FUNCTIONS(X509_REVOKED) + +IMPLEMENT_ASN1_DUP_FUNCTION(X509_REVOKED) + +IMPLEMENT_ASN1_FUNCTIONS(X509_CRL_INFO) + +IMPLEMENT_ASN1_FUNCTIONS(X509_CRL) + +IMPLEMENT_ASN1_DUP_FUNCTION(X509_CRL) + +static int X509_REVOKED_cmp(const X509_REVOKED *const *a, + const X509_REVOKED *const *b) +{ + return (ASN1_STRING_cmp((ASN1_STRING *)&(*a)->serialNumber, + (ASN1_STRING *)&(*b)->serialNumber)); +} + +int X509_CRL_add0_revoked(X509_CRL *crl, X509_REVOKED *rev) +{ + X509_CRL_INFO *inf; + inf = &crl->crl; + if (inf->revoked == NULL) + inf->revoked = sk_X509_REVOKED_new(X509_REVOKED_cmp); + if (inf->revoked == NULL || !sk_X509_REVOKED_push(inf->revoked, rev)) { + ASN1err(ASN1_F_X509_CRL_ADD0_REVOKED, ERR_R_MALLOC_FAILURE); + return 0; + } + inf->enc.modified = 1; + return 1; +} + +int X509_CRL_verify(X509_CRL *crl, EVP_PKEY *r) +{ + if (crl->meth->crl_verify) + return crl->meth->crl_verify(crl, r); + return 0; +} + +int X509_CRL_get0_by_serial(X509_CRL *crl, + X509_REVOKED **ret, ASN1_INTEGER *serial) +{ + if (crl->meth->crl_lookup) + return crl->meth->crl_lookup(crl, ret, serial, NULL); + return 0; +} + +int X509_CRL_get0_by_cert(X509_CRL *crl, X509_REVOKED **ret, X509 *x) +{ + if (crl->meth->crl_lookup) + return crl->meth->crl_lookup(crl, ret, + X509_get_serialNumber(x), + X509_get_issuer_name(x)); + return 0; +} + +static int def_crl_verify(X509_CRL *crl, EVP_PKEY *r) +{ + return (ASN1_item_verify(ASN1_ITEM_rptr(X509_CRL_INFO), + &crl->sig_alg, &crl->signature, &crl->crl, r)); +} + +static int crl_revoked_issuer_match(X509_CRL *crl, X509_NAME *nm, + X509_REVOKED *rev) +{ + int i; + + if (!rev->issuer) { + if (!nm) + return 1; + if (!X509_NAME_cmp(nm, X509_CRL_get_issuer(crl))) + return 1; + return 0; + } + + if (!nm) + nm = X509_CRL_get_issuer(crl); + + for (i = 0; i < sk_GENERAL_NAME_num(rev->issuer); i++) { + GENERAL_NAME *gen = sk_GENERAL_NAME_value(rev->issuer, i); + if (gen->type != GEN_DIRNAME) + continue; + if (!X509_NAME_cmp(nm, gen->d.directoryName)) + return 1; + } + return 0; + +} + +static int def_crl_lookup(X509_CRL *crl, + X509_REVOKED **ret, ASN1_INTEGER *serial, + X509_NAME *issuer) +{ + X509_REVOKED rtmp, *rev; + int idx; + rtmp.serialNumber = *serial; + /* + * Sort revoked into serial number order if not already sorted. Do this + * under a lock to avoid race condition. + */ + if (!sk_X509_REVOKED_is_sorted(crl->crl.revoked)) { + CRYPTO_THREAD_write_lock(crl->lock); + sk_X509_REVOKED_sort(crl->crl.revoked); + CRYPTO_THREAD_unlock(crl->lock); + } + idx = sk_X509_REVOKED_find(crl->crl.revoked, &rtmp); + if (idx < 0) + return 0; + /* Need to look for matching name */ + for (; idx < sk_X509_REVOKED_num(crl->crl.revoked); idx++) { + rev = sk_X509_REVOKED_value(crl->crl.revoked, idx); + if (ASN1_INTEGER_cmp(&rev->serialNumber, serial)) + return 0; + if (crl_revoked_issuer_match(crl, issuer, rev)) { + if (ret) + *ret = rev; + if (rev->reason == CRL_REASON_REMOVE_FROM_CRL) + return 2; + return 1; + } + } + return 0; +} + +void X509_CRL_set_default_method(const X509_CRL_METHOD *meth) +{ + if (meth == NULL) + default_crl_method = &int_crl_meth; + else + default_crl_method = meth; +} + +X509_CRL_METHOD *X509_CRL_METHOD_new(int (*crl_init) (X509_CRL *crl), + int (*crl_free) (X509_CRL *crl), + int (*crl_lookup) (X509_CRL *crl, + X509_REVOKED **ret, + ASN1_INTEGER *ser, + X509_NAME *issuer), + int (*crl_verify) (X509_CRL *crl, + EVP_PKEY *pk)) +{ + X509_CRL_METHOD *m; + m = OPENSSL_malloc(sizeof(*m)); + if (m == NULL) + return NULL; + m->crl_init = crl_init; + m->crl_free = crl_free; + m->crl_lookup = crl_lookup; + m->crl_verify = crl_verify; + m->flags = X509_CRL_METHOD_DYNAMIC; + return m; +} + +void X509_CRL_METHOD_free(X509_CRL_METHOD *m) +{ + if (m == NULL || !(m->flags & X509_CRL_METHOD_DYNAMIC)) + return; + OPENSSL_free(m); +} + +void X509_CRL_set_meth_data(X509_CRL *crl, void *dat) +{ + crl->meth_data = dat; +} + +void *X509_CRL_get_meth_data(X509_CRL *crl) +{ + return crl->meth_data; +} diff --git a/crypto/x509/x_exten.c b/crypto/x509/x_exten.c new file mode 100644 index 000000000..f10f4a4d8 --- /dev/null +++ b/crypto/x509/x_exten.c @@ -0,0 +1,28 @@ +/* + * Copyright 2000-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#include +#include +#include +#include "x509_lcl.h" + +ASN1_SEQUENCE(X509_EXTENSION) = { + ASN1_SIMPLE(X509_EXTENSION, object, ASN1_OBJECT), + ASN1_OPT(X509_EXTENSION, critical, ASN1_BOOLEAN), + ASN1_EMBED(X509_EXTENSION, value, ASN1_OCTET_STRING) +} ASN1_SEQUENCE_END(X509_EXTENSION) + +ASN1_ITEM_TEMPLATE(X509_EXTENSIONS) = + ASN1_EX_TEMPLATE_TYPE(ASN1_TFLG_SEQUENCE_OF, 0, Extension, X509_EXTENSION) +ASN1_ITEM_TEMPLATE_END(X509_EXTENSIONS) + +IMPLEMENT_ASN1_FUNCTIONS(X509_EXTENSION) +IMPLEMENT_ASN1_ENCODE_FUNCTIONS_fname(X509_EXTENSIONS, X509_EXTENSIONS, X509_EXTENSIONS) +IMPLEMENT_ASN1_DUP_FUNCTION(X509_EXTENSION) diff --git a/crypto/x509/x_name.c b/crypto/x509/x_name.c new file mode 100644 index 000000000..c863c6921 --- /dev/null +++ b/crypto/x509/x_name.c @@ -0,0 +1,567 @@ +/* + * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#include +#include "internal/cryptlib.h" +#include +#include +#include "internal/x509_int.h" +#include "internal/asn1_int.h" +#include "x509_lcl.h" + +/* + * Maximum length of X509_NAME: much larger than anything we should + * ever see in practice. + */ + +#define X509_NAME_MAX (1024 * 1024) + +static int x509_name_ex_d2i(ASN1_VALUE **val, + const unsigned char **in, long len, + const ASN1_ITEM *it, + int tag, int aclass, char opt, ASN1_TLC *ctx); + +static int x509_name_ex_i2d(ASN1_VALUE **val, unsigned char **out, + const ASN1_ITEM *it, int tag, int aclass); +static int x509_name_ex_new(ASN1_VALUE **val, const ASN1_ITEM *it); +static void x509_name_ex_free(ASN1_VALUE **val, const ASN1_ITEM *it); + +static int x509_name_encode(X509_NAME *a); +static int x509_name_canon(X509_NAME *a); +static int asn1_string_canon(ASN1_STRING *out, const ASN1_STRING *in); +static int i2d_name_canon(STACK_OF(STACK_OF_X509_NAME_ENTRY) * intname, + unsigned char **in); + +static int x509_name_ex_print(BIO *out, ASN1_VALUE **pval, + int indent, + const char *fname, const ASN1_PCTX *pctx); + +ASN1_SEQUENCE(X509_NAME_ENTRY) = { + ASN1_SIMPLE(X509_NAME_ENTRY, object, ASN1_OBJECT), + ASN1_SIMPLE(X509_NAME_ENTRY, value, ASN1_PRINTABLE) +} ASN1_SEQUENCE_END(X509_NAME_ENTRY) + +IMPLEMENT_ASN1_FUNCTIONS(X509_NAME_ENTRY) +IMPLEMENT_ASN1_DUP_FUNCTION(X509_NAME_ENTRY) + +/* + * For the "Name" type we need a SEQUENCE OF { SET OF X509_NAME_ENTRY } so + * declare two template wrappers for this + */ + +ASN1_ITEM_TEMPLATE(X509_NAME_ENTRIES) = + ASN1_EX_TEMPLATE_TYPE(ASN1_TFLG_SET_OF, 0, RDNS, X509_NAME_ENTRY) +static_ASN1_ITEM_TEMPLATE_END(X509_NAME_ENTRIES) + +ASN1_ITEM_TEMPLATE(X509_NAME_INTERNAL) = + ASN1_EX_TEMPLATE_TYPE(ASN1_TFLG_SEQUENCE_OF, 0, Name, X509_NAME_ENTRIES) +static_ASN1_ITEM_TEMPLATE_END(X509_NAME_INTERNAL) + +/* + * Normally that's where it would end: we'd have two nested STACK structures + * representing the ASN1. Unfortunately X509_NAME uses a completely different + * form and caches encodings so we have to process the internal form and + * convert to the external form. + */ + +static const ASN1_EXTERN_FUNCS x509_name_ff = { + NULL, + x509_name_ex_new, + x509_name_ex_free, + 0, /* Default clear behaviour is OK */ + x509_name_ex_d2i, + x509_name_ex_i2d, + x509_name_ex_print +}; + +IMPLEMENT_EXTERN_ASN1(X509_NAME, V_ASN1_SEQUENCE, x509_name_ff) + +IMPLEMENT_ASN1_FUNCTIONS(X509_NAME) + +IMPLEMENT_ASN1_DUP_FUNCTION(X509_NAME) + +static int x509_name_ex_new(ASN1_VALUE **val, const ASN1_ITEM *it) +{ + X509_NAME *ret = OPENSSL_zalloc(sizeof(*ret)); + + if (ret == NULL) + goto memerr; + if ((ret->entries = sk_X509_NAME_ENTRY_new_null()) == NULL) + goto memerr; + if ((ret->bytes = BUF_MEM_new()) == NULL) + goto memerr; + ret->modified = 1; + *val = (ASN1_VALUE *)ret; + return 1; + + memerr: + ASN1err(ASN1_F_X509_NAME_EX_NEW, ERR_R_MALLOC_FAILURE); + if (ret) { + sk_X509_NAME_ENTRY_free(ret->entries); + OPENSSL_free(ret); + } + return 0; +} + +static void x509_name_ex_free(ASN1_VALUE **pval, const ASN1_ITEM *it) +{ + X509_NAME *a; + + if (!pval || !*pval) + return; + a = (X509_NAME *)*pval; + + BUF_MEM_free(a->bytes); + sk_X509_NAME_ENTRY_pop_free(a->entries, X509_NAME_ENTRY_free); + OPENSSL_free(a->canon_enc); + OPENSSL_free(a); + *pval = NULL; +} + +static void name_entry_stack_free(STACK_OF(X509_NAME_ENTRY) *ents) +{ + sk_X509_NAME_ENTRY_pop_free(ents, X509_NAME_ENTRY_free); +} + +static int x509_name_ex_d2i(ASN1_VALUE **val, + const unsigned char **in, long len, + const ASN1_ITEM *it, int tag, int aclass, + char opt, ASN1_TLC *ctx) +{ + const unsigned char *p = *in, *q; + union { + STACK_OF(STACK_OF_X509_NAME_ENTRY) *s; + ASN1_VALUE *a; + } intname = { + NULL + }; + union { + X509_NAME *x; + ASN1_VALUE *a; + } nm = { + NULL + }; + int i, j, ret; + STACK_OF(X509_NAME_ENTRY) *entries; + X509_NAME_ENTRY *entry; + if (len > X509_NAME_MAX) + len = X509_NAME_MAX; + q = p; + + /* Get internal representation of Name */ + ret = ASN1_item_ex_d2i(&intname.a, + &p, len, ASN1_ITEM_rptr(X509_NAME_INTERNAL), + tag, aclass, opt, ctx); + + if (ret <= 0) + return ret; + + if (*val) + x509_name_ex_free(val, NULL); + if (!x509_name_ex_new(&nm.a, NULL)) + goto err; + /* We've decoded it: now cache encoding */ + if (!BUF_MEM_grow(nm.x->bytes, p - q)) + goto err; + memcpy(nm.x->bytes->data, q, p - q); + + /* Convert internal representation to X509_NAME structure */ + for (i = 0; i < sk_STACK_OF_X509_NAME_ENTRY_num(intname.s); i++) { + entries = sk_STACK_OF_X509_NAME_ENTRY_value(intname.s, i); + for (j = 0; j < sk_X509_NAME_ENTRY_num(entries); j++) { + entry = sk_X509_NAME_ENTRY_value(entries, j); + entry->set = i; + if (!sk_X509_NAME_ENTRY_push(nm.x->entries, entry)) + goto err; + } + } + /* + * All entries have now been pushed to nm->x.entries + * free up the stacks in intname.s but not the entries + * themselves. + */ + sk_STACK_OF_X509_NAME_ENTRY_pop_free(intname.s, sk_X509_NAME_ENTRY_free); + intname.s = NULL; + ret = x509_name_canon(nm.x); + if (!ret) + goto err; + nm.x->modified = 0; + *val = nm.a; + *in = p; + return ret; + + err: + /* If intname.s is not NULL only some entries exist in nm->x.entries: + * zero references in nm->x.entries list. Since all entries exist + * in intname.s we can free them all there + */ + if (intname.s != NULL) { + sk_X509_NAME_ENTRY_zero(nm.x->entries); + sk_STACK_OF_X509_NAME_ENTRY_pop_free(intname.s, name_entry_stack_free); + } + X509_NAME_free(nm.x); + ASN1err(ASN1_F_X509_NAME_EX_D2I, ERR_R_NESTED_ASN1_ERROR); + return 0; +} + +static int x509_name_ex_i2d(ASN1_VALUE **val, unsigned char **out, + const ASN1_ITEM *it, int tag, int aclass) +{ + int ret; + X509_NAME *a = (X509_NAME *)*val; + if (a->modified) { + ret = x509_name_encode(a); + if (ret < 0) + return ret; + ret = x509_name_canon(a); + if (ret < 0) + return ret; + } + ret = a->bytes->length; + if (out != NULL) { + memcpy(*out, a->bytes->data, ret); + *out += ret; + } + return ret; +} + +static void local_sk_X509_NAME_ENTRY_free(STACK_OF(X509_NAME_ENTRY) *ne) +{ + sk_X509_NAME_ENTRY_free(ne); +} + +static void local_sk_X509_NAME_ENTRY_pop_free(STACK_OF(X509_NAME_ENTRY) *ne) +{ + sk_X509_NAME_ENTRY_pop_free(ne, X509_NAME_ENTRY_free); +} + +static int x509_name_encode(X509_NAME *a) +{ + union { + STACK_OF(STACK_OF_X509_NAME_ENTRY) *s; + ASN1_VALUE *a; + } intname = { + NULL + }; + int len; + unsigned char *p; + STACK_OF(X509_NAME_ENTRY) *entries = NULL; + X509_NAME_ENTRY *entry; + int i, set = -1; + intname.s = sk_STACK_OF_X509_NAME_ENTRY_new_null(); + if (!intname.s) + goto memerr; + for (i = 0; i < sk_X509_NAME_ENTRY_num(a->entries); i++) { + entry = sk_X509_NAME_ENTRY_value(a->entries, i); + if (entry->set != set) { + entries = sk_X509_NAME_ENTRY_new_null(); + if (!entries) + goto memerr; + if (!sk_STACK_OF_X509_NAME_ENTRY_push(intname.s, entries)) + goto memerr; + set = entry->set; + } + if (!sk_X509_NAME_ENTRY_push(entries, entry)) + goto memerr; + } + len = ASN1_item_ex_i2d(&intname.a, NULL, + ASN1_ITEM_rptr(X509_NAME_INTERNAL), -1, -1); + if (!BUF_MEM_grow(a->bytes, len)) + goto memerr; + p = (unsigned char *)a->bytes->data; + ASN1_item_ex_i2d(&intname.a, + &p, ASN1_ITEM_rptr(X509_NAME_INTERNAL), -1, -1); + sk_STACK_OF_X509_NAME_ENTRY_pop_free(intname.s, + local_sk_X509_NAME_ENTRY_free); + a->modified = 0; + return len; + memerr: + sk_STACK_OF_X509_NAME_ENTRY_pop_free(intname.s, + local_sk_X509_NAME_ENTRY_free); + ASN1err(ASN1_F_X509_NAME_ENCODE, ERR_R_MALLOC_FAILURE); + return -1; +} + +static int x509_name_ex_print(BIO *out, ASN1_VALUE **pval, + int indent, + const char *fname, const ASN1_PCTX *pctx) +{ + if (X509_NAME_print_ex(out, (const X509_NAME *)*pval, + indent, pctx->nm_flags) <= 0) + return 0; + return 2; +} + +/* + * This function generates the canonical encoding of the Name structure. In + * it all strings are converted to UTF8, leading, trailing and multiple + * spaces collapsed, converted to lower case and the leading SEQUENCE header + * removed. In future we could also normalize the UTF8 too. By doing this + * comparison of Name structures can be rapidly performed by just using + * memcmp() of the canonical encoding. By omitting the leading SEQUENCE name + * constraints of type dirName can also be checked with a simple memcmp(). + */ + +static int x509_name_canon(X509_NAME *a) +{ + unsigned char *p; + STACK_OF(STACK_OF_X509_NAME_ENTRY) *intname = NULL; + STACK_OF(X509_NAME_ENTRY) *entries = NULL; + X509_NAME_ENTRY *entry, *tmpentry = NULL; + int i, set = -1, ret = 0, len; + + OPENSSL_free(a->canon_enc); + a->canon_enc = NULL; + /* Special case: empty X509_NAME => null encoding */ + if (sk_X509_NAME_ENTRY_num(a->entries) == 0) { + a->canon_enclen = 0; + return 1; + } + intname = sk_STACK_OF_X509_NAME_ENTRY_new_null(); + if (!intname) + goto err; + for (i = 0; i < sk_X509_NAME_ENTRY_num(a->entries); i++) { + entry = sk_X509_NAME_ENTRY_value(a->entries, i); + if (entry->set != set) { + entries = sk_X509_NAME_ENTRY_new_null(); + if (!entries) + goto err; + if (!sk_STACK_OF_X509_NAME_ENTRY_push(intname, entries)) + goto err; + set = entry->set; + } + tmpentry = X509_NAME_ENTRY_new(); + if (tmpentry == NULL) + goto err; + tmpentry->object = OBJ_dup(entry->object); + if (tmpentry->object == NULL) + goto err; + if (!asn1_string_canon(tmpentry->value, entry->value)) + goto err; + if (!sk_X509_NAME_ENTRY_push(entries, tmpentry)) + goto err; + tmpentry = NULL; + } + + /* Finally generate encoding */ + + len = i2d_name_canon(intname, NULL); + if (len < 0) + goto err; + a->canon_enclen = len; + + p = OPENSSL_malloc(a->canon_enclen); + + if (p == NULL) + goto err; + + a->canon_enc = p; + + i2d_name_canon(intname, &p); + + ret = 1; + + err: + + X509_NAME_ENTRY_free(tmpentry); + sk_STACK_OF_X509_NAME_ENTRY_pop_free(intname, + local_sk_X509_NAME_ENTRY_pop_free); + return ret; +} + +/* Bitmap of all the types of string that will be canonicalized. */ + +#define ASN1_MASK_CANON \ + (B_ASN1_UTF8STRING | B_ASN1_BMPSTRING | B_ASN1_UNIVERSALSTRING \ + | B_ASN1_PRINTABLESTRING | B_ASN1_T61STRING | B_ASN1_IA5STRING \ + | B_ASN1_VISIBLESTRING) + +static int asn1_string_canon(ASN1_STRING *out, const ASN1_STRING *in) +{ + unsigned char *to, *from; + int len, i; + + /* If type not in bitmask just copy string across */ + if (!(ASN1_tag2bit(in->type) & ASN1_MASK_CANON)) { + if (!ASN1_STRING_copy(out, in)) + return 0; + return 1; + } + + out->type = V_ASN1_UTF8STRING; + out->length = ASN1_STRING_to_UTF8(&out->data, in); + if (out->length == -1) + return 0; + + to = out->data; + from = to; + + len = out->length; + + /* + * Convert string in place to canonical form. Ultimately we may need to + * handle a wider range of characters but for now ignore anything with + * MSB set and rely on the isspace() and tolower() functions. + */ + + /* Ignore leading spaces */ + while ((len > 0) && !(*from & 0x80) && isspace(*from)) { + from++; + len--; + } + + to = from + len; + + /* Ignore trailing spaces */ + while ((len > 0) && !(to[-1] & 0x80) && isspace(to[-1])) { + to--; + len--; + } + + to = out->data; + + i = 0; + while (i < len) { + /* If MSB set just copy across */ + if (*from & 0x80) { + *to++ = *from++; + i++; + } + /* Collapse multiple spaces */ + else if (isspace(*from)) { + /* Copy one space across */ + *to++ = ' '; + /* + * Ignore subsequent spaces. Note: don't need to check len here + * because we know the last character is a non-space so we can't + * overflow. + */ + do { + from++; + i++; + } + while (!(*from & 0x80) && isspace(*from)); + } else { + *to++ = tolower(*from); + from++; + i++; + } + } + + out->length = to - out->data; + + return 1; + +} + +static int i2d_name_canon(STACK_OF(STACK_OF_X509_NAME_ENTRY) * _intname, + unsigned char **in) +{ + int i, len, ltmp; + ASN1_VALUE *v; + STACK_OF(ASN1_VALUE) *intname = (STACK_OF(ASN1_VALUE) *)_intname; + + len = 0; + for (i = 0; i < sk_ASN1_VALUE_num(intname); i++) { + v = sk_ASN1_VALUE_value(intname, i); + ltmp = ASN1_item_ex_i2d(&v, in, + ASN1_ITEM_rptr(X509_NAME_ENTRIES), -1, -1); + if (ltmp < 0) + return ltmp; + len += ltmp; + } + return len; +} + +int X509_NAME_set(X509_NAME **xn, X509_NAME *name) +{ + X509_NAME *in; + + if (!xn || !name) + return (0); + + if (*xn != name) { + in = X509_NAME_dup(name); + if (in != NULL) { + X509_NAME_free(*xn); + *xn = in; + } + } + return (*xn != NULL); +} + +int X509_NAME_print(BIO *bp, const X509_NAME *name, int obase) +{ + char *s, *c, *b; + int l, i; + + l = 80 - 2 - obase; + + b = X509_NAME_oneline(name, NULL, 0); + if (!b) + return 0; + if (!*b) { + OPENSSL_free(b); + return 1; + } + s = b + 1; /* skip the first slash */ + + c = s; + for (;;) { +#ifndef CHARSET_EBCDIC + if (((*s == '/') && + ((s[1] >= 'A') && (s[1] <= 'Z') && ((s[2] == '=') || + ((s[2] >= 'A') + && (s[2] <= 'Z') + && (s[3] == '=')) + ))) || (*s == '\0')) +#else + if (((*s == '/') && + (isupper(s[1]) && ((s[2] == '=') || + (isupper(s[2]) && (s[3] == '=')) + ))) || (*s == '\0')) +#endif + { + i = s - c; + if (BIO_write(bp, c, i) != i) + goto err; + c = s + 1; /* skip following slash */ + if (*s != '\0') { + if (BIO_write(bp, ", ", 2) != 2) + goto err; + } + l--; + } + if (*s == '\0') + break; + s++; + l--; + } + + OPENSSL_free(b); + return 1; + err: + X509err(X509_F_X509_NAME_PRINT, ERR_R_BUF_LIB); + OPENSSL_free(b); + return 0; +} + +int X509_NAME_get0_der(X509_NAME *nm, const unsigned char **pder, + size_t *pderlen) +{ + /* Make sure encoding is valid */ + if (i2d_X509_NAME(nm, NULL) <= 0) + return 0; + if (pder != NULL) + *pder = (unsigned char *)nm->bytes->data; + if (pderlen != NULL) + *pderlen = nm->bytes->length; + return 1; +} diff --git a/crypto/x509/x_pubkey.c b/crypto/x509/x_pubkey.c new file mode 100644 index 000000000..75dbafb36 --- /dev/null +++ b/crypto/x509/x_pubkey.c @@ -0,0 +1,499 @@ +/* + * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#include "internal/cryptlib.h" +#include +#include +#include "internal/asn1_int.h" +#include "internal/evp_int.h" +#include "internal/x509_int.h" +#ifndef OPENSSL_NO_RSA +# include +#endif +#ifndef OPENSSL_NO_DSA +# include +#endif +#ifndef OPENSSL_NO_PAILLIER +# include +#endif + +struct X509_pubkey_st { + X509_ALGOR *algor; + ASN1_BIT_STRING *public_key; + EVP_PKEY *pkey; +}; + +static int x509_pubkey_decode(EVP_PKEY **pk, X509_PUBKEY *key); + +/* Minor tweak to operation: free up EVP_PKEY */ +static int pubkey_cb(int operation, ASN1_VALUE **pval, const ASN1_ITEM *it, + void *exarg) +{ + if (operation == ASN1_OP_FREE_POST) { + X509_PUBKEY *pubkey = (X509_PUBKEY *)*pval; + EVP_PKEY_free(pubkey->pkey); + } else if (operation == ASN1_OP_D2I_POST) { + /* Attempt to decode public key and cache in pubkey structure. */ + X509_PUBKEY *pubkey = (X509_PUBKEY *)*pval; + EVP_PKEY_free(pubkey->pkey); + /* + * Opportunistically decode the key but remove any non fatal errors + * from the queue. Subsequent explicit attempts to decode/use the key + * will return an appropriate error. + */ + ERR_set_mark(); + if (x509_pubkey_decode(&pubkey->pkey, pubkey) == -1) + return 0; + ERR_pop_to_mark(); + } + return 1; +} + +ASN1_SEQUENCE_cb(X509_PUBKEY, pubkey_cb) = { + ASN1_SIMPLE(X509_PUBKEY, algor, X509_ALGOR), + ASN1_SIMPLE(X509_PUBKEY, public_key, ASN1_BIT_STRING) +} ASN1_SEQUENCE_END_cb(X509_PUBKEY, X509_PUBKEY) + +IMPLEMENT_ASN1_FUNCTIONS(X509_PUBKEY) + +int X509_PUBKEY_set(X509_PUBKEY **x, EVP_PKEY *pkey) +{ + X509_PUBKEY *pk = NULL; + + if (x == NULL) + return (0); + + if ((pk = X509_PUBKEY_new()) == NULL) + goto error; + + if (pkey->ameth) { + if (pkey->ameth->pub_encode) { + if (!pkey->ameth->pub_encode(pk, pkey)) { + X509err(X509_F_X509_PUBKEY_SET, + X509_R_PUBLIC_KEY_ENCODE_ERROR); + goto error; + } + } else { + X509err(X509_F_X509_PUBKEY_SET, X509_R_METHOD_NOT_SUPPORTED); + goto error; + } + } else { + X509err(X509_F_X509_PUBKEY_SET, X509_R_UNSUPPORTED_ALGORITHM); + goto error; + } + + X509_PUBKEY_free(*x); + *x = pk; + pk->pkey = pkey; + EVP_PKEY_up_ref(pkey); + return 1; + + error: + X509_PUBKEY_free(pk); + return 0; +} + +/* + * Attempt to decode a public key. + * Returns 1 on success, 0 for a decode failure and -1 for a fatal + * error e.g. malloc failure. + */ + + +static int x509_pubkey_decode(EVP_PKEY **ppkey, X509_PUBKEY *key) + { + EVP_PKEY *pkey = EVP_PKEY_new(); + + if (pkey == NULL) { + X509err(X509_F_X509_PUBKEY_DECODE, ERR_R_MALLOC_FAILURE); + return -1; + } + + if (!EVP_PKEY_set_type(pkey, OBJ_obj2nid(key->algor->algorithm))) { + X509err(X509_F_X509_PUBKEY_DECODE, X509_R_UNSUPPORTED_ALGORITHM); + goto error; + } + + if (pkey->ameth->pub_decode) { + /* + * Treat any failure of pub_decode as a decode error. In + * future we could have different return codes for decode + * errors and fatal errors such as malloc failure. + */ + if (!pkey->ameth->pub_decode(pkey, key)) { + X509err(X509_F_X509_PUBKEY_DECODE, X509_R_PUBLIC_KEY_DECODE_ERROR); + goto error; + } + } else { + X509err(X509_F_X509_PUBKEY_DECODE, X509_R_METHOD_NOT_SUPPORTED); + goto error; + } + + *ppkey = pkey; + return 1; + + error: + EVP_PKEY_free(pkey); + return 0; +} + +EVP_PKEY *X509_PUBKEY_get0(X509_PUBKEY *key) +{ + EVP_PKEY *ret = NULL; + + if (key == NULL || key->public_key == NULL) + return NULL; + + if (key->pkey != NULL) + return key->pkey; + + /* + * When the key ASN.1 is initially parsed an attempt is made to + * decode the public key and cache the EVP_PKEY structure. If this + * operation fails the cached value will be NULL. Parsing continues + * to allow parsing of unknown key types or unsupported forms. + * We repeat the decode operation so the appropriate errors are left + * in the queue. + */ + x509_pubkey_decode(&ret, key); + /* If decode doesn't fail something bad happened */ + if (ret != NULL) { + X509err(X509_F_X509_PUBKEY_GET0, ERR_R_INTERNAL_ERROR); + EVP_PKEY_free(ret); + } + + return NULL; +} + +EVP_PKEY *X509_PUBKEY_get(X509_PUBKEY *key) +{ + EVP_PKEY *ret = X509_PUBKEY_get0(key); + if (ret != NULL) + EVP_PKEY_up_ref(ret); + return ret; +} + +/* + * Now two pseudo ASN1 routines that take an EVP_PKEY structure and encode or + * decode as X509_PUBKEY + */ + +EVP_PKEY *d2i_PUBKEY(EVP_PKEY **a, const unsigned char **pp, long length) +{ + X509_PUBKEY *xpk; + EVP_PKEY *pktmp; + const unsigned char *q; + q = *pp; + xpk = d2i_X509_PUBKEY(NULL, &q, length); + if (!xpk) + return NULL; + pktmp = X509_PUBKEY_get(xpk); + X509_PUBKEY_free(xpk); + if (!pktmp) + return NULL; + *pp = q; + if (a) { + EVP_PKEY_free(*a); + *a = pktmp; + } + return pktmp; +} + +int i2d_PUBKEY(EVP_PKEY *a, unsigned char **pp) +{ + X509_PUBKEY *xpk = NULL; + int ret; + if (!a) + return 0; + if (!X509_PUBKEY_set(&xpk, a)) + return 0; + ret = i2d_X509_PUBKEY(xpk, pp); + X509_PUBKEY_free(xpk); + return ret; +} + +/* + * The following are equivalents but which return RSA and DSA keys + */ +#ifndef OPENSSL_NO_RSA +RSA *d2i_RSA_PUBKEY(RSA **a, const unsigned char **pp, long length) +{ + EVP_PKEY *pkey; + RSA *key; + const unsigned char *q; + q = *pp; + pkey = d2i_PUBKEY(NULL, &q, length); + if (!pkey) + return NULL; + key = EVP_PKEY_get1_RSA(pkey); + EVP_PKEY_free(pkey); + if (!key) + return NULL; + *pp = q; + if (a) { + RSA_free(*a); + *a = key; + } + return key; +} + +int i2d_RSA_PUBKEY(RSA *a, unsigned char **pp) +{ + EVP_PKEY *pktmp; + int ret; + if (!a) + return 0; + pktmp = EVP_PKEY_new(); + if (pktmp == NULL) { + ASN1err(ASN1_F_I2D_RSA_PUBKEY, ERR_R_MALLOC_FAILURE); + return 0; + } + EVP_PKEY_set1_RSA(pktmp, a); + ret = i2d_PUBKEY(pktmp, pp); + EVP_PKEY_free(pktmp); + return ret; +} +#endif + +#ifndef OPENSSL_NO_PAILLIER +PAILLIER *d2i_PAILLIER_PUBKEY(PAILLIER **a, const unsigned char **pp, long length) +{ + EVP_PKEY *pkey; + PAILLIER *key; + const unsigned char *q; + q = *pp; + pkey = d2i_PUBKEY(NULL, &q, length); + if (!pkey) + return NULL; + key = EVP_PKEY_get1_PAILLIER(pkey); + EVP_PKEY_free(pkey); + if (!key) + return NULL; + *pp = q; + if (a) { + PAILLIER_free(*a); + *a = key; + } + return key; +} + +int i2d_PAILLIER_PUBKEY(PAILLIER *a, unsigned char **pp) +{ + EVP_PKEY *pktmp; + int ret; + if (!a) + return 0; + pktmp = EVP_PKEY_new(); + if (pktmp == NULL) { + ASN1err(ASN1_F_I2D_PAILLIER_PUBKEY, ERR_R_MALLOC_FAILURE); + return 0; + } + EVP_PKEY_set1_PAILLIER(pktmp, a); + ret = i2d_PUBKEY(pktmp, pp); + EVP_PKEY_free(pktmp); + return ret; +} +#endif + +#ifndef OPENSSL_NO_SM9 +SM9_MASTER_KEY *d2i_SM9_MASTER_PUBKEY(SM9_MASTER_KEY **a, const unsigned char **pp, long length) +{ + EVP_PKEY *pkey; + SM9_MASTER_KEY *key; + const unsigned char *q; + q = *pp; + pkey = d2i_PUBKEY(NULL, &q, length); + if (!pkey) + return NULL; + key = EVP_PKEY_get1_SM9_MASTER(pkey); + EVP_PKEY_free(pkey); + if (!key) + return NULL; + *pp = q; + if (a) { + SM9_MASTER_KEY_free(*a); + *a = key; + } + return key; +} + +int i2d_SM9_MASTER_PUBKEY(SM9_MASTER_KEY *a, unsigned char **pp) +{ + EVP_PKEY *pktmp; + int ret; + if (!a) + return 0; + pktmp = EVP_PKEY_new(); + if (pktmp == NULL) { + ASN1err(ASN1_F_I2D_SM9_MASTER_PUBKEY, ERR_R_MALLOC_FAILURE); + return 0; + } + EVP_PKEY_set1_SM9_MASTER(pktmp, a); + ret = i2d_PUBKEY(pktmp, pp); + EVP_PKEY_free(pktmp); + return ret; +} + +SM9_KEY *d2i_SM9_PUBKEY(SM9_KEY **a, const unsigned char **pp, long length) +{ + EVP_PKEY *pkey; + SM9_KEY *key; + const unsigned char *q; + q = *pp; + pkey = d2i_PUBKEY(NULL, &q, length); + if (!pkey) + return NULL; + key = EVP_PKEY_get1_SM9(pkey); + EVP_PKEY_free(pkey); + if (!key) + return NULL; + *pp = q; + if (a) { + SM9_KEY_free(*a); + *a = key; + } + return key; +} + +int i2d_SM9_PUBKEY(SM9_KEY *a, unsigned char **pp) +{ + EVP_PKEY *pktmp; + int ret; + if (!a) + return 0; + pktmp = EVP_PKEY_new(); + if (pktmp == NULL) { + //ASN1err(ASN1_F_I2D_SM9_PUBKEY, ERR_R_MALLOC_FAILURE); + return 0; + } + EVP_PKEY_set1_SM9(pktmp, a); + ret = i2d_PUBKEY(pktmp, pp); + EVP_PKEY_free(pktmp); + return ret; +} +#endif + +#ifndef OPENSSL_NO_DSA +DSA *d2i_DSA_PUBKEY(DSA **a, const unsigned char **pp, long length) +{ + EVP_PKEY *pkey; + DSA *key; + const unsigned char *q; + q = *pp; + pkey = d2i_PUBKEY(NULL, &q, length); + if (!pkey) + return NULL; + key = EVP_PKEY_get1_DSA(pkey); + EVP_PKEY_free(pkey); + if (!key) + return NULL; + *pp = q; + if (a) { + DSA_free(*a); + *a = key; + } + return key; +} + +int i2d_DSA_PUBKEY(DSA *a, unsigned char **pp) +{ + EVP_PKEY *pktmp; + int ret; + if (!a) + return 0; + pktmp = EVP_PKEY_new(); + if (pktmp == NULL) { + ASN1err(ASN1_F_I2D_DSA_PUBKEY, ERR_R_MALLOC_FAILURE); + return 0; + } + EVP_PKEY_set1_DSA(pktmp, a); + ret = i2d_PUBKEY(pktmp, pp); + EVP_PKEY_free(pktmp); + return ret; +} +#endif + +#ifndef OPENSSL_NO_EC +EC_KEY *d2i_EC_PUBKEY(EC_KEY **a, const unsigned char **pp, long length) +{ + EVP_PKEY *pkey; + EC_KEY *key; + const unsigned char *q; + q = *pp; + pkey = d2i_PUBKEY(NULL, &q, length); + if (!pkey) + return (NULL); + key = EVP_PKEY_get1_EC_KEY(pkey); + EVP_PKEY_free(pkey); + if (!key) + return (NULL); + *pp = q; + if (a) { + EC_KEY_free(*a); + *a = key; + } + return (key); +} + +int i2d_EC_PUBKEY(EC_KEY *a, unsigned char **pp) +{ + EVP_PKEY *pktmp; + int ret; + if (!a) + return (0); + if ((pktmp = EVP_PKEY_new()) == NULL) { + ASN1err(ASN1_F_I2D_EC_PUBKEY, ERR_R_MALLOC_FAILURE); + return (0); + } + EVP_PKEY_set1_EC_KEY(pktmp, a); + ret = i2d_PUBKEY(pktmp, pp); + EVP_PKEY_free(pktmp); + return (ret); +} +#endif + +int X509_PUBKEY_set0_param(X509_PUBKEY *pub, ASN1_OBJECT *aobj, + int ptype, void *pval, + unsigned char *penc, int penclen) +{ + if (!X509_ALGOR_set0(pub->algor, aobj, ptype, pval)) + return 0; + if (penc) { + OPENSSL_free(pub->public_key->data); + pub->public_key->data = penc; + pub->public_key->length = penclen; + /* Set number of unused bits to zero */ + pub->public_key->flags &= ~(ASN1_STRING_FLAG_BITS_LEFT | 0x07); + pub->public_key->flags |= ASN1_STRING_FLAG_BITS_LEFT; + } + return 1; +} + +int X509_PUBKEY_get0_param(ASN1_OBJECT **ppkalg, + const unsigned char **pk, int *ppklen, + X509_ALGOR **pa, X509_PUBKEY *pub) +{ + if (ppkalg) + *ppkalg = pub->algor->algorithm; + if (pk) { + *pk = pub->public_key->data; + *ppklen = pub->public_key->length; + } + if (pa) + *pa = pub->algor; + return 1; +} + +ASN1_BIT_STRING *X509_get0_pubkey_bitstr(const X509 *x) +{ + if (x == NULL) + return NULL; + return x->cert_info.key->public_key; +} diff --git a/crypto/x509/x_req.c b/crypto/x509/x_req.c new file mode 100644 index 000000000..c2da95a73 --- /dev/null +++ b/crypto/x509/x_req.c @@ -0,0 +1,68 @@ +/* + * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#include "internal/cryptlib.h" +#include +#include +#include "internal/x509_int.h" + +/*- + * X509_REQ_INFO is handled in an unusual way to get round + * invalid encodings. Some broken certificate requests don't + * encode the attributes field if it is empty. This is in + * violation of PKCS#10 but we need to tolerate it. We do + * this by making the attributes field OPTIONAL then using + * the callback to initialise it to an empty STACK. + * + * This means that the field will be correctly encoded unless + * we NULL out the field. + * + * As a result we no longer need the req_kludge field because + * the information is now contained in the attributes field: + * 1. If it is NULL then it's the invalid omission. + * 2. If it is empty it is the correct encoding. + * 3. If it is not empty then some attributes are present. + * + */ + +static int rinf_cb(int operation, ASN1_VALUE **pval, const ASN1_ITEM *it, + void *exarg) +{ + X509_REQ_INFO *rinf = (X509_REQ_INFO *)*pval; + + if (operation == ASN1_OP_NEW_POST) { + rinf->attributes = sk_X509_ATTRIBUTE_new_null(); + if (!rinf->attributes) + return 0; + } + return 1; +} + +ASN1_SEQUENCE_enc(X509_REQ_INFO, enc, rinf_cb) = { + ASN1_SIMPLE(X509_REQ_INFO, version, ASN1_INTEGER), + ASN1_SIMPLE(X509_REQ_INFO, subject, X509_NAME), + ASN1_SIMPLE(X509_REQ_INFO, pubkey, X509_PUBKEY), + /* This isn't really OPTIONAL but it gets round invalid + * encodings + */ + ASN1_IMP_SET_OF_OPT(X509_REQ_INFO, attributes, X509_ATTRIBUTE, 0) +} ASN1_SEQUENCE_END_enc(X509_REQ_INFO, X509_REQ_INFO) + +IMPLEMENT_ASN1_FUNCTIONS(X509_REQ_INFO) + +ASN1_SEQUENCE_ref(X509_REQ, 0) = { + ASN1_EMBED(X509_REQ, req_info, X509_REQ_INFO), + ASN1_EMBED(X509_REQ, sig_alg, X509_ALGOR), + ASN1_SIMPLE(X509_REQ, signature, ASN1_BIT_STRING) +} ASN1_SEQUENCE_END_ref(X509_REQ, X509_REQ) + +IMPLEMENT_ASN1_FUNCTIONS(X509_REQ) + +IMPLEMENT_ASN1_DUP_FUNCTION(X509_REQ) diff --git a/crypto/x509/x_x509.c b/crypto/x509/x_x509.c new file mode 100644 index 000000000..6783fd872 --- /dev/null +++ b/crypto/x509/x_x509.c @@ -0,0 +1,224 @@ +/* + * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#include "internal/cryptlib.h" +#include +#include +#include +#include +#include "internal/x509_int.h" + +ASN1_SEQUENCE_enc(X509_CINF, enc, 0) = { + ASN1_EXP_OPT(X509_CINF, version, ASN1_INTEGER, 0), + ASN1_EMBED(X509_CINF, serialNumber, ASN1_INTEGER), + ASN1_EMBED(X509_CINF, signature, X509_ALGOR), + ASN1_SIMPLE(X509_CINF, issuer, X509_NAME), + ASN1_EMBED(X509_CINF, validity, X509_VAL), + ASN1_SIMPLE(X509_CINF, subject, X509_NAME), + ASN1_SIMPLE(X509_CINF, key, X509_PUBKEY), + ASN1_IMP_OPT(X509_CINF, issuerUID, ASN1_BIT_STRING, 1), + ASN1_IMP_OPT(X509_CINF, subjectUID, ASN1_BIT_STRING, 2), + ASN1_EXP_SEQUENCE_OF_OPT(X509_CINF, extensions, X509_EXTENSION, 3) +} ASN1_SEQUENCE_END_enc(X509_CINF, X509_CINF) + +IMPLEMENT_ASN1_FUNCTIONS(X509_CINF) +/* X509 top level structure needs a bit of customisation */ + +extern void policy_cache_free(X509_POLICY_CACHE *cache); + +static int x509_cb(int operation, ASN1_VALUE **pval, const ASN1_ITEM *it, + void *exarg) +{ + X509 *ret = (X509 *)*pval; + + switch (operation) { + + case ASN1_OP_NEW_POST: + ret->ex_flags = 0; + ret->ex_pathlen = -1; + ret->ex_pcpathlen = -1; + ret->skid = NULL; + ret->akid = NULL; +#ifndef OPENSSL_NO_RFC3779 + ret->rfc3779_addr = NULL; + ret->rfc3779_asid = NULL; +#endif + ret->aux = NULL; + ret->crldp = NULL; + if (!CRYPTO_new_ex_data(CRYPTO_EX_INDEX_X509, ret, &ret->ex_data)) + return 0; + break; + + case ASN1_OP_FREE_POST: + CRYPTO_free_ex_data(CRYPTO_EX_INDEX_X509, ret, &ret->ex_data); + X509_CERT_AUX_free(ret->aux); + ASN1_OCTET_STRING_free(ret->skid); + AUTHORITY_KEYID_free(ret->akid); + CRL_DIST_POINTS_free(ret->crldp); + policy_cache_free(ret->policy_cache); + GENERAL_NAMES_free(ret->altname); + NAME_CONSTRAINTS_free(ret->nc); +#ifndef OPENSSL_NO_RFC3779 + sk_IPAddressFamily_pop_free(ret->rfc3779_addr, IPAddressFamily_free); + ASIdentifiers_free(ret->rfc3779_asid); +#endif + break; + + } + + return 1; + +} + +ASN1_SEQUENCE_ref(X509, x509_cb) = { + ASN1_EMBED(X509, cert_info, X509_CINF), + ASN1_EMBED(X509, sig_alg, X509_ALGOR), + ASN1_EMBED(X509, signature, ASN1_BIT_STRING) +} ASN1_SEQUENCE_END_ref(X509, X509) + +IMPLEMENT_ASN1_FUNCTIONS(X509) + +IMPLEMENT_ASN1_DUP_FUNCTION(X509) + +int X509_set_ex_data(X509 *r, int idx, void *arg) +{ + return (CRYPTO_set_ex_data(&r->ex_data, idx, arg)); +} + +void *X509_get_ex_data(X509 *r, int idx) +{ + return (CRYPTO_get_ex_data(&r->ex_data, idx)); +} + +/* + * X509_AUX ASN1 routines. X509_AUX is the name given to a certificate with + * extra info tagged on the end. Since these functions set how a certificate + * is trusted they should only be used when the certificate comes from a + * reliable source such as local storage. + */ + +X509 *d2i_X509_AUX(X509 **a, const unsigned char **pp, long length) +{ + const unsigned char *q; + X509 *ret; + int freeret = 0; + + /* Save start position */ + q = *pp; + + if (a == NULL || *a == NULL) + freeret = 1; + ret = d2i_X509(a, &q, length); + /* If certificate unreadable then forget it */ + if (ret == NULL) + return NULL; + /* update length */ + length -= q - *pp; + if (length > 0 && !d2i_X509_CERT_AUX(&ret->aux, &q, length)) + goto err; + *pp = q; + return ret; + err: + if (freeret) { + X509_free(ret); + if (a) + *a = NULL; + } + return NULL; +} + +/* + * Serialize trusted certificate to *pp or just return the required buffer + * length if pp == NULL. We ultimately want to avoid modifying *pp in the + * error path, but that depends on similar hygiene in lower-level functions. + * Here we avoid compounding the problem. + */ +static int i2d_x509_aux_internal(X509 *a, unsigned char **pp) +{ + int length, tmplen; + unsigned char *start = pp != NULL ? *pp : NULL; + + OPENSSL_assert(pp == NULL || *pp != NULL); + + /* + * This might perturb *pp on error, but fixing that belongs in i2d_X509() + * not here. It should be that if a == NULL length is zero, but we check + * both just in case. + */ + length = i2d_X509(a, pp); + if (length <= 0 || a == NULL) + return length; + + tmplen = i2d_X509_CERT_AUX(a->aux, pp); + if (tmplen < 0) { + if (start != NULL) + *pp = start; + return tmplen; + } + length += tmplen; + + return length; +} + +/* + * Serialize trusted certificate to *pp, or just return the required buffer + * length if pp == NULL. + * + * When pp is not NULL, but *pp == NULL, we allocate the buffer, but since + * we're writing two ASN.1 objects back to back, we can't have i2d_X509() do + * the allocation, nor can we allow i2d_X509_CERT_AUX() to increment the + * allocated buffer. + */ +int i2d_X509_AUX(X509 *a, unsigned char **pp) +{ + int length; + unsigned char *tmp; + + /* Buffer provided by caller */ + if (pp == NULL || *pp != NULL) + return i2d_x509_aux_internal(a, pp); + + /* Obtain the combined length */ + if ((length = i2d_x509_aux_internal(a, NULL)) <= 0) + return length; + + /* Allocate requisite combined storage */ + *pp = tmp = OPENSSL_malloc(length); + if (tmp == NULL) + return -1; /* Push error onto error stack? */ + + /* Encode, but keep *pp at the originally malloced pointer */ + length = i2d_x509_aux_internal(a, &tmp); + if (length <= 0) { + OPENSSL_free(*pp); + *pp = NULL; + } + return length; +} + +int i2d_re_X509_tbs(X509 *x, unsigned char **pp) +{ + x->cert_info.enc.modified = 1; + return i2d_X509_CINF(&x->cert_info, pp); +} + +void X509_get0_signature(const ASN1_BIT_STRING **psig, + const X509_ALGOR **palg, const X509 *x) +{ + if (psig) + *psig = &x->signature; + if (palg) + *palg = &x->sig_alg; +} + +int X509_get_signature_nid(const X509 *x) +{ + return OBJ_obj2nid(x->sig_alg.algorithm); +} diff --git a/crypto/x509/x_x509a.c b/crypto/x509/x_x509a.c new file mode 100644 index 000000000..8c9ad71d1 --- /dev/null +++ b/crypto/x509/x_x509a.c @@ -0,0 +1,169 @@ +/* + * Copyright 1999-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#include "internal/cryptlib.h" +#include +#include +#include +#include "internal/x509_int.h" + +/* + * X509_CERT_AUX routines. These are used to encode additional user + * modifiable data about a certificate. This data is appended to the X509 + * encoding when the *_X509_AUX routines are used. This means that the + * "traditional" X509 routines will simply ignore the extra data. + */ + +static X509_CERT_AUX *aux_get(X509 *x); + +ASN1_SEQUENCE(X509_CERT_AUX) = { + ASN1_SEQUENCE_OF_OPT(X509_CERT_AUX, trust, ASN1_OBJECT), + ASN1_IMP_SEQUENCE_OF_OPT(X509_CERT_AUX, reject, ASN1_OBJECT, 0), + ASN1_OPT(X509_CERT_AUX, alias, ASN1_UTF8STRING), + ASN1_OPT(X509_CERT_AUX, keyid, ASN1_OCTET_STRING), + ASN1_IMP_SEQUENCE_OF_OPT(X509_CERT_AUX, other, X509_ALGOR, 1) +} ASN1_SEQUENCE_END(X509_CERT_AUX) + +IMPLEMENT_ASN1_FUNCTIONS(X509_CERT_AUX) + +int X509_trusted(const X509 *x) +{ + return x->aux ? 1 : 0; +} + +static X509_CERT_AUX *aux_get(X509 *x) +{ + if (x == NULL) + return NULL; + if (x->aux == NULL && (x->aux = X509_CERT_AUX_new()) == NULL) + return NULL; + return x->aux; +} + +int X509_alias_set1(X509 *x, const unsigned char *name, int len) +{ + X509_CERT_AUX *aux; + if (!name) { + if (!x || !x->aux || !x->aux->alias) + return 1; + ASN1_UTF8STRING_free(x->aux->alias); + x->aux->alias = NULL; + return 1; + } + if ((aux = aux_get(x)) == NULL) + return 0; + if (aux->alias == NULL && (aux->alias = ASN1_UTF8STRING_new()) == NULL) + return 0; + return ASN1_STRING_set(aux->alias, name, len); +} + +int X509_keyid_set1(X509 *x, const unsigned char *id, int len) +{ + X509_CERT_AUX *aux; + if (!id) { + if (!x || !x->aux || !x->aux->keyid) + return 1; + ASN1_OCTET_STRING_free(x->aux->keyid); + x->aux->keyid = NULL; + return 1; + } + if ((aux = aux_get(x)) == NULL) + return 0; + if (aux->keyid == NULL + && (aux->keyid = ASN1_OCTET_STRING_new()) == NULL) + return 0; + return ASN1_STRING_set(aux->keyid, id, len); +} + +unsigned char *X509_alias_get0(X509 *x, int *len) +{ + if (!x->aux || !x->aux->alias) + return NULL; + if (len) + *len = x->aux->alias->length; + return x->aux->alias->data; +} + +unsigned char *X509_keyid_get0(X509 *x, int *len) +{ + if (!x->aux || !x->aux->keyid) + return NULL; + if (len) + *len = x->aux->keyid->length; + return x->aux->keyid->data; +} + +int X509_add1_trust_object(X509 *x, const ASN1_OBJECT *obj) +{ + X509_CERT_AUX *aux; + ASN1_OBJECT *objtmp = NULL; + if (obj) { + objtmp = OBJ_dup(obj); + if (!objtmp) + return 0; + } + if ((aux = aux_get(x)) == NULL) + goto err; + if (aux->trust == NULL + && (aux->trust = sk_ASN1_OBJECT_new_null()) == NULL) + goto err; + if (!objtmp || sk_ASN1_OBJECT_push(aux->trust, objtmp)) + return 1; + err: + ASN1_OBJECT_free(objtmp); + return 0; +} + +int X509_add1_reject_object(X509 *x, const ASN1_OBJECT *obj) +{ + X509_CERT_AUX *aux; + ASN1_OBJECT *objtmp; + if ((objtmp = OBJ_dup(obj)) == NULL) + return 0; + if ((aux = aux_get(x)) == NULL) + goto err; + if (aux->reject == NULL + && (aux->reject = sk_ASN1_OBJECT_new_null()) == NULL) + goto err; + return sk_ASN1_OBJECT_push(aux->reject, objtmp); + err: + ASN1_OBJECT_free(objtmp); + return 0; +} + +void X509_trust_clear(X509 *x) +{ + if (x->aux) { + sk_ASN1_OBJECT_pop_free(x->aux->trust, ASN1_OBJECT_free); + x->aux->trust = NULL; + } +} + +void X509_reject_clear(X509 *x) +{ + if (x->aux) { + sk_ASN1_OBJECT_pop_free(x->aux->reject, ASN1_OBJECT_free); + x->aux->reject = NULL; + } +} + +STACK_OF(ASN1_OBJECT) *X509_get0_trust_objects(X509 *x) +{ + if (x->aux != NULL) + return x->aux->trust; + return NULL; +} + +STACK_OF(ASN1_OBJECT) *X509_get0_reject_objects(X509 *x) +{ + if (x->aux != NULL) + return x->aux->reject; + return NULL; +} diff --git a/crypto/x509v3/build.info b/crypto/x509v3/build.info new file mode 100644 index 000000000..452a8b03c --- /dev/null +++ b/crypto/x509v3/build.info @@ -0,0 +1,8 @@ +LIBS=../../libcrypto +SOURCE[../../libcrypto]=\ + v3_bcons.c v3_bitst.c v3_conf.c v3_extku.c v3_ia5.c v3_lib.c \ + v3_prn.c v3_utl.c v3err.c v3_genn.c v3_alt.c v3_skey.c v3_akey.c v3_pku.c \ + v3_int.c v3_enum.c v3_sxnet.c v3_cpols.c v3_crld.c v3_purp.c v3_info.c \ + v3_akeya.c v3_pmaps.c v3_pcons.c v3_ncons.c v3_pcia.c v3_pci.c \ + pcy_cache.c pcy_node.c pcy_data.c pcy_map.c pcy_tree.c pcy_lib.c \ + v3_asid.c v3_addr.c v3_tlsf.c diff --git a/crypto/x509v3/ext_dat.h b/crypto/x509v3/ext_dat.h new file mode 100644 index 000000000..c9ede960e --- /dev/null +++ b/crypto/x509v3/ext_dat.h @@ -0,0 +1,24 @@ +/* + * Copyright 1999-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +int name_cmp(const char *name, const char *cmp); + +extern const X509V3_EXT_METHOD v3_bcons, v3_nscert, v3_key_usage, v3_ext_ku; +extern const X509V3_EXT_METHOD v3_pkey_usage_period, v3_sxnet, v3_info, v3_sinfo; +extern const X509V3_EXT_METHOD v3_ns_ia5_list[8], v3_alt[3], v3_skey_id, v3_akey_id; +extern const X509V3_EXT_METHOD v3_crl_num, v3_crl_reason, v3_crl_invdate; +extern const X509V3_EXT_METHOD v3_delta_crl, v3_cpols, v3_crld, v3_freshest_crl; +extern const X509V3_EXT_METHOD v3_ocsp_nonce, v3_ocsp_accresp, v3_ocsp_acutoff; +extern const X509V3_EXT_METHOD v3_ocsp_crlid, v3_ocsp_nocheck, v3_ocsp_serviceloc; +extern const X509V3_EXT_METHOD v3_crl_hold, v3_pci; +extern const X509V3_EXT_METHOD v3_policy_mappings, v3_policy_constraints; +extern const X509V3_EXT_METHOD v3_name_constraints, v3_inhibit_anyp, v3_idp; +extern const X509V3_EXT_METHOD v3_addr, v3_asid; +extern const X509V3_EXT_METHOD v3_ct_scts[3]; +extern const X509V3_EXT_METHOD v3_tls_feature; diff --git a/crypto/x509v3/pcy_cache.c b/crypto/x509v3/pcy_cache.c new file mode 100644 index 000000000..a9ee30a8d --- /dev/null +++ b/crypto/x509v3/pcy_cache.c @@ -0,0 +1,216 @@ +/* + * Copyright 2004-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include "internal/cryptlib.h" +#include +#include +#include "internal/x509_int.h" + +#include "pcy_int.h" + +static int policy_data_cmp(const X509_POLICY_DATA *const *a, + const X509_POLICY_DATA *const *b); +static int policy_cache_set_int(long *out, ASN1_INTEGER *value); + +/* + * Set cache entry according to CertificatePolicies extension. Note: this + * destroys the passed CERTIFICATEPOLICIES structure. + */ + +static int policy_cache_create(X509 *x, + CERTIFICATEPOLICIES *policies, int crit) +{ + int i; + int ret = 0; + X509_POLICY_CACHE *cache = x->policy_cache; + X509_POLICY_DATA *data = NULL; + POLICYINFO *policy; + if (sk_POLICYINFO_num(policies) == 0) + goto bad_policy; + cache->data = sk_X509_POLICY_DATA_new(policy_data_cmp); + if (cache->data == NULL) + goto bad_policy; + for (i = 0; i < sk_POLICYINFO_num(policies); i++) { + policy = sk_POLICYINFO_value(policies, i); + data = policy_data_new(policy, NULL, crit); + if (data == NULL) + goto bad_policy; + /* + * Duplicate policy OIDs are illegal: reject if matches found. + */ + if (OBJ_obj2nid(data->valid_policy) == NID_any_policy) { + if (cache->anyPolicy) { + ret = -1; + goto bad_policy; + } + cache->anyPolicy = data; + } else if (sk_X509_POLICY_DATA_find(cache->data, data) != -1) { + ret = -1; + goto bad_policy; + } else if (!sk_X509_POLICY_DATA_push(cache->data, data)) + goto bad_policy; + data = NULL; + } + ret = 1; + bad_policy: + if (ret == -1) + x->ex_flags |= EXFLAG_INVALID_POLICY; + policy_data_free(data); + sk_POLICYINFO_pop_free(policies, POLICYINFO_free); + if (ret <= 0) { + sk_X509_POLICY_DATA_pop_free(cache->data, policy_data_free); + cache->data = NULL; + } + return ret; +} + +static int policy_cache_new(X509 *x) +{ + X509_POLICY_CACHE *cache; + ASN1_INTEGER *ext_any = NULL; + POLICY_CONSTRAINTS *ext_pcons = NULL; + CERTIFICATEPOLICIES *ext_cpols = NULL; + POLICY_MAPPINGS *ext_pmaps = NULL; + int i; + + if (x->policy_cache != NULL) + return 1; + cache = OPENSSL_malloc(sizeof(*cache)); + if (cache == NULL) + return 0; + cache->anyPolicy = NULL; + cache->data = NULL; + cache->any_skip = -1; + cache->explicit_skip = -1; + cache->map_skip = -1; + + x->policy_cache = cache; + + /* + * Handle requireExplicitPolicy *first*. Need to process this even if we + * don't have any policies. + */ + ext_pcons = X509_get_ext_d2i(x, NID_policy_constraints, &i, NULL); + + if (!ext_pcons) { + if (i != -1) + goto bad_cache; + } else { + if (!ext_pcons->requireExplicitPolicy + && !ext_pcons->inhibitPolicyMapping) + goto bad_cache; + if (!policy_cache_set_int(&cache->explicit_skip, + ext_pcons->requireExplicitPolicy)) + goto bad_cache; + if (!policy_cache_set_int(&cache->map_skip, + ext_pcons->inhibitPolicyMapping)) + goto bad_cache; + } + + /* Process CertificatePolicies */ + + ext_cpols = X509_get_ext_d2i(x, NID_certificate_policies, &i, NULL); + /* + * If no CertificatePolicies extension or problem decoding then there is + * no point continuing because the valid policies will be NULL. + */ + if (!ext_cpols) { + /* If not absent some problem with extension */ + if (i != -1) + goto bad_cache; + return 1; + } + + i = policy_cache_create(x, ext_cpols, i); + + /* NB: ext_cpols freed by policy_cache_set_policies */ + + if (i <= 0) + return i; + + ext_pmaps = X509_get_ext_d2i(x, NID_policy_mappings, &i, NULL); + + if (!ext_pmaps) { + /* If not absent some problem with extension */ + if (i != -1) + goto bad_cache; + } else { + i = policy_cache_set_mapping(x, ext_pmaps); + if (i <= 0) + goto bad_cache; + } + + ext_any = X509_get_ext_d2i(x, NID_inhibit_any_policy, &i, NULL); + + if (!ext_any) { + if (i != -1) + goto bad_cache; + } else if (!policy_cache_set_int(&cache->any_skip, ext_any)) + goto bad_cache; + goto just_cleanup; + + bad_cache: + x->ex_flags |= EXFLAG_INVALID_POLICY; + + just_cleanup: + POLICY_CONSTRAINTS_free(ext_pcons); + ASN1_INTEGER_free(ext_any); + return 1; + +} + +void policy_cache_free(X509_POLICY_CACHE *cache) +{ + if (!cache) + return; + policy_data_free(cache->anyPolicy); + sk_X509_POLICY_DATA_pop_free(cache->data, policy_data_free); + OPENSSL_free(cache); +} + +const X509_POLICY_CACHE *policy_cache_set(X509 *x) +{ + + if (x->policy_cache == NULL) { + CRYPTO_THREAD_write_lock(x->lock); + policy_cache_new(x); + CRYPTO_THREAD_unlock(x->lock); + } + + return x->policy_cache; + +} + +X509_POLICY_DATA *policy_cache_find_data(const X509_POLICY_CACHE *cache, + const ASN1_OBJECT *id) +{ + int idx; + X509_POLICY_DATA tmp; + tmp.valid_policy = (ASN1_OBJECT *)id; + idx = sk_X509_POLICY_DATA_find(cache->data, &tmp); + if (idx == -1) + return NULL; + return sk_X509_POLICY_DATA_value(cache->data, idx); +} + +static int policy_data_cmp(const X509_POLICY_DATA *const *a, + const X509_POLICY_DATA *const *b) +{ + return OBJ_cmp((*a)->valid_policy, (*b)->valid_policy); +} + +static int policy_cache_set_int(long *out, ASN1_INTEGER *value) +{ + if (value == NULL) + return 1; + if (value->type == V_ASN1_NEG_INTEGER) + return 0; + *out = ASN1_INTEGER_get(value); + return 1; +} diff --git a/crypto/x509v3/pcy_data.c b/crypto/x509v3/pcy_data.c new file mode 100644 index 000000000..cf1d635ec --- /dev/null +++ b/crypto/x509v3/pcy_data.c @@ -0,0 +1,77 @@ +/* + * Copyright 2004-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include "internal/cryptlib.h" +#include +#include + +#include "pcy_int.h" + +/* Policy Node routines */ + +void policy_data_free(X509_POLICY_DATA *data) +{ + if (!data) + return; + ASN1_OBJECT_free(data->valid_policy); + /* Don't free qualifiers if shared */ + if (!(data->flags & POLICY_DATA_FLAG_SHARED_QUALIFIERS)) + sk_POLICYQUALINFO_pop_free(data->qualifier_set, POLICYQUALINFO_free); + sk_ASN1_OBJECT_pop_free(data->expected_policy_set, ASN1_OBJECT_free); + OPENSSL_free(data); +} + +/* + * Create a data based on an existing policy. If 'id' is NULL use the OID in + * the policy, otherwise use 'id'. This behaviour covers the two types of + * data in RFC3280: data with from a CertificatePolicies extension and + * additional data with just the qualifiers of anyPolicy and ID from another + * source. + */ + +X509_POLICY_DATA *policy_data_new(POLICYINFO *policy, + const ASN1_OBJECT *cid, int crit) +{ + X509_POLICY_DATA *ret; + ASN1_OBJECT *id; + if (!policy && !cid) + return NULL; + if (cid) { + id = OBJ_dup(cid); + if (!id) + return NULL; + } else + id = NULL; + ret = OPENSSL_zalloc(sizeof(*ret)); + if (ret == NULL) + return NULL; + ret->expected_policy_set = sk_ASN1_OBJECT_new_null(); + if (ret->expected_policy_set == NULL) { + OPENSSL_free(ret); + ASN1_OBJECT_free(id); + return NULL; + } + + if (crit) + ret->flags = POLICY_DATA_FLAG_CRITICAL; + + if (id) + ret->valid_policy = id; + else { + ret->valid_policy = policy->policyid; + policy->policyid = NULL; + } + + if (policy) { + ret->qualifier_set = policy->qualifiers; + policy->qualifiers = NULL; + } + + return ret; +} diff --git a/crypto/x509v3/pcy_int.h b/crypto/x509v3/pcy_int.h new file mode 100644 index 000000000..5daf78de4 --- /dev/null +++ b/crypto/x509v3/pcy_int.h @@ -0,0 +1,167 @@ +/* + * Copyright 2004-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +typedef struct X509_POLICY_DATA_st X509_POLICY_DATA; + +DEFINE_STACK_OF(X509_POLICY_DATA) + +/* Internal structures */ + +/* + * This structure and the field names correspond to the Policy 'node' of + * RFC3280. NB this structure contains no pointers to parent or child data: + * X509_POLICY_NODE contains that. This means that the main policy data can + * be kept static and cached with the certificate. + */ + +struct X509_POLICY_DATA_st { + unsigned int flags; + /* Policy OID and qualifiers for this data */ + ASN1_OBJECT *valid_policy; + STACK_OF(POLICYQUALINFO) *qualifier_set; + STACK_OF(ASN1_OBJECT) *expected_policy_set; +}; + +/* X509_POLICY_DATA flags values */ + +/* + * This flag indicates the structure has been mapped using a policy mapping + * extension. If policy mapping is not active its references get deleted. + */ + +#define POLICY_DATA_FLAG_MAPPED 0x1 + +/* + * This flag indicates the data doesn't correspond to a policy in Certificate + * Policies: it has been mapped to any policy. + */ + +#define POLICY_DATA_FLAG_MAPPED_ANY 0x2 + +/* AND with flags to see if any mapping has occurred */ + +#define POLICY_DATA_FLAG_MAP_MASK 0x3 + +/* qualifiers are shared and shouldn't be freed */ + +#define POLICY_DATA_FLAG_SHARED_QUALIFIERS 0x4 + +/* Parent node is an extra node and should be freed */ + +#define POLICY_DATA_FLAG_EXTRA_NODE 0x8 + +/* Corresponding CertificatePolicies is critical */ + +#define POLICY_DATA_FLAG_CRITICAL 0x10 + +/* This structure is cached with a certificate */ + +struct X509_POLICY_CACHE_st { + /* anyPolicy data or NULL if no anyPolicy */ + X509_POLICY_DATA *anyPolicy; + /* other policy data */ + STACK_OF(X509_POLICY_DATA) *data; + /* If InhibitAnyPolicy present this is its value or -1 if absent. */ + long any_skip; + /* + * If policyConstraints and requireExplicitPolicy present this is its + * value or -1 if absent. + */ + long explicit_skip; + /* + * If policyConstraints and policyMapping present this is its value or -1 + * if absent. + */ + long map_skip; +}; + +/* + * #define POLICY_CACHE_FLAG_CRITICAL POLICY_DATA_FLAG_CRITICAL + */ + +/* This structure represents the relationship between nodes */ + +struct X509_POLICY_NODE_st { + /* node data this refers to */ + const X509_POLICY_DATA *data; + /* Parent node */ + X509_POLICY_NODE *parent; + /* Number of child nodes */ + int nchild; +}; + +struct X509_POLICY_LEVEL_st { + /* Cert for this level */ + X509 *cert; + /* nodes at this level */ + STACK_OF(X509_POLICY_NODE) *nodes; + /* anyPolicy node */ + X509_POLICY_NODE *anyPolicy; + /* Extra data */ + /* + * STACK_OF(X509_POLICY_DATA) *extra_data; + */ + unsigned int flags; +}; + +struct X509_POLICY_TREE_st { + /* This is the tree 'level' data */ + X509_POLICY_LEVEL *levels; + int nlevel; + /* + * Extra policy data when additional nodes (not from the certificate) are + * required. + */ + STACK_OF(X509_POLICY_DATA) *extra_data; + /* This is the authority constrained policy set */ + STACK_OF(X509_POLICY_NODE) *auth_policies; + STACK_OF(X509_POLICY_NODE) *user_policies; + unsigned int flags; +}; + +/* Set if anyPolicy present in user policies */ +#define POLICY_FLAG_ANY_POLICY 0x2 + +/* Useful macros */ + +#define node_data_critical(data) (data->flags & POLICY_DATA_FLAG_CRITICAL) +#define node_critical(node) node_data_critical(node->data) + +/* Internal functions */ + +X509_POLICY_DATA *policy_data_new(POLICYINFO *policy, const ASN1_OBJECT *id, + int crit); +void policy_data_free(X509_POLICY_DATA *data); + +X509_POLICY_DATA *policy_cache_find_data(const X509_POLICY_CACHE *cache, + const ASN1_OBJECT *id); +int policy_cache_set_mapping(X509 *x, POLICY_MAPPINGS *maps); + +STACK_OF(X509_POLICY_NODE) *policy_node_cmp_new(void); + +void policy_cache_init(void); + +void policy_cache_free(X509_POLICY_CACHE *cache); + +X509_POLICY_NODE *level_find_node(const X509_POLICY_LEVEL *level, + const X509_POLICY_NODE *parent, + const ASN1_OBJECT *id); + +X509_POLICY_NODE *tree_find_sk(STACK_OF(X509_POLICY_NODE) *sk, + const ASN1_OBJECT *id); + +X509_POLICY_NODE *level_add_node(X509_POLICY_LEVEL *level, + X509_POLICY_DATA *data, + X509_POLICY_NODE *parent, + X509_POLICY_TREE *tree); +void policy_node_free(X509_POLICY_NODE *node); +int policy_node_match(const X509_POLICY_LEVEL *lvl, + const X509_POLICY_NODE *node, const ASN1_OBJECT *oid); + +const X509_POLICY_CACHE *policy_cache_set(X509 *x); diff --git a/crypto/x509v3/pcy_lib.c b/crypto/x509v3/pcy_lib.c new file mode 100644 index 000000000..67f7eafc6 --- /dev/null +++ b/crypto/x509v3/pcy_lib.c @@ -0,0 +1,108 @@ +/* + * Copyright 2004-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include "internal/cryptlib.h" +#include +#include + +#include "pcy_int.h" + +/* accessor functions */ + +/* X509_POLICY_TREE stuff */ + +int X509_policy_tree_level_count(const X509_POLICY_TREE *tree) +{ + if (!tree) + return 0; + return tree->nlevel; +} + +X509_POLICY_LEVEL *X509_policy_tree_get0_level(const X509_POLICY_TREE *tree, + int i) +{ + if (!tree || (i < 0) || (i >= tree->nlevel)) + return NULL; + return tree->levels + i; +} + +STACK_OF(X509_POLICY_NODE) *X509_policy_tree_get0_policies(const + X509_POLICY_TREE + *tree) +{ + if (!tree) + return NULL; + return tree->auth_policies; +} + +STACK_OF(X509_POLICY_NODE) *X509_policy_tree_get0_user_policies(const + X509_POLICY_TREE + *tree) +{ + if (!tree) + return NULL; + if (tree->flags & POLICY_FLAG_ANY_POLICY) + return tree->auth_policies; + else + return tree->user_policies; +} + +/* X509_POLICY_LEVEL stuff */ + +int X509_policy_level_node_count(X509_POLICY_LEVEL *level) +{ + int n; + if (!level) + return 0; + if (level->anyPolicy) + n = 1; + else + n = 0; + if (level->nodes) + n += sk_X509_POLICY_NODE_num(level->nodes); + return n; +} + +X509_POLICY_NODE *X509_policy_level_get0_node(X509_POLICY_LEVEL *level, int i) +{ + if (!level) + return NULL; + if (level->anyPolicy) { + if (i == 0) + return level->anyPolicy; + i--; + } + return sk_X509_POLICY_NODE_value(level->nodes, i); +} + +/* X509_POLICY_NODE stuff */ + +const ASN1_OBJECT *X509_policy_node_get0_policy(const X509_POLICY_NODE *node) +{ + if (!node) + return NULL; + return node->data->valid_policy; +} + +STACK_OF(POLICYQUALINFO) *X509_policy_node_get0_qualifiers(const + X509_POLICY_NODE + *node) +{ + if (!node) + return NULL; + return node->data->qualifier_set; +} + +const X509_POLICY_NODE *X509_policy_node_get0_parent(const X509_POLICY_NODE + *node) +{ + if (!node) + return NULL; + return node->parent; +} diff --git a/crypto/x509v3/pcy_map.c b/crypto/x509v3/pcy_map.c new file mode 100644 index 000000000..ab9dd21b7 --- /dev/null +++ b/crypto/x509v3/pcy_map.c @@ -0,0 +1,81 @@ +/* + * Copyright 2004-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include "internal/cryptlib.h" +#include +#include +#include "internal/x509_int.h" + +#include "pcy_int.h" + +/* + * Set policy mapping entries in cache. Note: this modifies the passed + * POLICY_MAPPINGS structure + */ + +int policy_cache_set_mapping(X509 *x, POLICY_MAPPINGS *maps) +{ + POLICY_MAPPING *map; + X509_POLICY_DATA *data; + X509_POLICY_CACHE *cache = x->policy_cache; + int i; + int ret = 0; + if (sk_POLICY_MAPPING_num(maps) == 0) { + ret = -1; + goto bad_mapping; + } + for (i = 0; i < sk_POLICY_MAPPING_num(maps); i++) { + map = sk_POLICY_MAPPING_value(maps, i); + /* Reject if map to or from anyPolicy */ + if ((OBJ_obj2nid(map->subjectDomainPolicy) == NID_any_policy) + || (OBJ_obj2nid(map->issuerDomainPolicy) == NID_any_policy)) { + ret = -1; + goto bad_mapping; + } + + /* Attempt to find matching policy data */ + data = policy_cache_find_data(cache, map->issuerDomainPolicy); + /* If we don't have anyPolicy can't map */ + if (data == NULL && !cache->anyPolicy) + continue; + + /* Create a NODE from anyPolicy */ + if (data == NULL) { + data = policy_data_new(NULL, map->issuerDomainPolicy, + cache->anyPolicy->flags + & POLICY_DATA_FLAG_CRITICAL); + if (data == NULL) + goto bad_mapping; + data->qualifier_set = cache->anyPolicy->qualifier_set; + /* + * map->issuerDomainPolicy = NULL; + */ + data->flags |= POLICY_DATA_FLAG_MAPPED_ANY; + data->flags |= POLICY_DATA_FLAG_SHARED_QUALIFIERS; + if (!sk_X509_POLICY_DATA_push(cache->data, data)) { + policy_data_free(data); + goto bad_mapping; + } + } else + data->flags |= POLICY_DATA_FLAG_MAPPED; + if (!sk_ASN1_OBJECT_push(data->expected_policy_set, + map->subjectDomainPolicy)) + goto bad_mapping; + map->subjectDomainPolicy = NULL; + + } + + ret = 1; + bad_mapping: + if (ret == -1) + x->ex_flags |= EXFLAG_INVALID_POLICY; + sk_POLICY_MAPPING_pop_free(maps, POLICY_MAPPING_free); + return ret; + +} diff --git a/crypto/x509v3/pcy_node.c b/crypto/x509v3/pcy_node.c new file mode 100644 index 000000000..80443bff9 --- /dev/null +++ b/crypto/x509v3/pcy_node.c @@ -0,0 +1,139 @@ +/* + * Copyright 2004-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#include +#include + +#include "pcy_int.h" + +static int node_cmp(const X509_POLICY_NODE *const *a, + const X509_POLICY_NODE *const *b) +{ + return OBJ_cmp((*a)->data->valid_policy, (*b)->data->valid_policy); +} + +STACK_OF(X509_POLICY_NODE) *policy_node_cmp_new(void) +{ + return sk_X509_POLICY_NODE_new(node_cmp); +} + +X509_POLICY_NODE *tree_find_sk(STACK_OF(X509_POLICY_NODE) *nodes, + const ASN1_OBJECT *id) +{ + X509_POLICY_DATA n; + X509_POLICY_NODE l; + int idx; + + n.valid_policy = (ASN1_OBJECT *)id; + l.data = &n; + + idx = sk_X509_POLICY_NODE_find(nodes, &l); + if (idx == -1) + return NULL; + + return sk_X509_POLICY_NODE_value(nodes, idx); + +} + +X509_POLICY_NODE *level_find_node(const X509_POLICY_LEVEL *level, + const X509_POLICY_NODE *parent, + const ASN1_OBJECT *id) +{ + X509_POLICY_NODE *node; + int i; + for (i = 0; i < sk_X509_POLICY_NODE_num(level->nodes); i++) { + node = sk_X509_POLICY_NODE_value(level->nodes, i); + if (node->parent == parent) { + if (!OBJ_cmp(node->data->valid_policy, id)) + return node; + } + } + return NULL; +} + +X509_POLICY_NODE *level_add_node(X509_POLICY_LEVEL *level, + X509_POLICY_DATA *data, + X509_POLICY_NODE *parent, + X509_POLICY_TREE *tree) +{ + X509_POLICY_NODE *node; + + node = OPENSSL_zalloc(sizeof(*node)); + if (node == NULL) + return NULL; + node->data = data; + node->parent = parent; + if (level) { + if (OBJ_obj2nid(data->valid_policy) == NID_any_policy) { + if (level->anyPolicy) + goto node_error; + level->anyPolicy = node; + } else { + + if (level->nodes == NULL) + level->nodes = policy_node_cmp_new(); + if (level->nodes == NULL) + goto node_error; + if (!sk_X509_POLICY_NODE_push(level->nodes, node)) + goto node_error; + } + } + + if (tree) { + if (tree->extra_data == NULL) + tree->extra_data = sk_X509_POLICY_DATA_new_null(); + if (tree->extra_data == NULL) + goto node_error; + if (!sk_X509_POLICY_DATA_push(tree->extra_data, data)) + goto node_error; + } + + if (parent) + parent->nchild++; + + return node; + + node_error: + policy_node_free(node); + return NULL; +} + +void policy_node_free(X509_POLICY_NODE *node) +{ + OPENSSL_free(node); +} + +/* + * See if a policy node matches a policy OID. If mapping enabled look through + * expected policy set otherwise just valid policy. + */ + +int policy_node_match(const X509_POLICY_LEVEL *lvl, + const X509_POLICY_NODE *node, const ASN1_OBJECT *oid) +{ + int i; + ASN1_OBJECT *policy_oid; + const X509_POLICY_DATA *x = node->data; + + if ((lvl->flags & X509_V_FLAG_INHIBIT_MAP) + || !(x->flags & POLICY_DATA_FLAG_MAP_MASK)) { + if (!OBJ_cmp(x->valid_policy, oid)) + return 1; + return 0; + } + + for (i = 0; i < sk_ASN1_OBJECT_num(x->expected_policy_set); i++) { + policy_oid = sk_ASN1_OBJECT_value(x->expected_policy_set, i); + if (!OBJ_cmp(policy_oid, oid)) + return 1; + } + return 0; + +} diff --git a/crypto/x509v3/pcy_tree.c b/crypto/x509v3/pcy_tree.c new file mode 100644 index 000000000..9f9246bea --- /dev/null +++ b/crypto/x509v3/pcy_tree.c @@ -0,0 +1,696 @@ +/* + * Copyright 2004-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include "internal/cryptlib.h" +#include +#include + +#include "pcy_int.h" + +/* + * Enable this to print out the complete policy tree at various point during + * evaluation. + */ + +/* + * #define OPENSSL_POLICY_DEBUG + */ + +#ifdef OPENSSL_POLICY_DEBUG + +static void expected_print(BIO *err, X509_POLICY_LEVEL *lev, + X509_POLICY_NODE *node, int indent) +{ + if ((lev->flags & X509_V_FLAG_INHIBIT_MAP) + || !(node->data->flags & POLICY_DATA_FLAG_MAP_MASK)) + BIO_puts(err, " Not Mapped\n"); + else { + int i; + STACK_OF(ASN1_OBJECT) *pset = node->data->expected_policy_set; + ASN1_OBJECT *oid; + BIO_puts(err, " Expected: "); + for (i = 0; i < sk_ASN1_OBJECT_num(pset); i++) { + oid = sk_ASN1_OBJECT_value(pset, i); + if (i) + BIO_puts(err, ", "); + i2a_ASN1_OBJECT(err, oid); + } + BIO_puts(err, "\n"); + } +} + +static void tree_print(char *str, X509_POLICY_TREE *tree, + X509_POLICY_LEVEL *curr) +{ + BIO *err = BIO_new_fp(stderr, BIO_NOCLOSE); + X509_POLICY_LEVEL *plev; + + if (err == NULL) + return; + if (!curr) + curr = tree->levels + tree->nlevel; + else + curr++; + + BIO_printf(err, "Level print after %s\n", str); + BIO_printf(err, "Printing Up to Level %ld\n", curr - tree->levels); + for (plev = tree->levels; plev != curr; plev++) { + int i; + + BIO_printf(err, "Level %ld, flags = %x\n", + (long)(plev - tree->levels), plev->flags); + for (i = 0; i < sk_X509_POLICY_NODE_num(plev->nodes); i++) { + X509_POLICY_NODE *node = sk_X509_POLICY_NODE_value(plev->nodes, i); + + X509_POLICY_NODE_print(err, node, 2); + expected_print(err, plev, node, 2); + BIO_printf(err, " Flags: %x\n", node->data->flags); + } + if (plev->anyPolicy) + X509_POLICY_NODE_print(err, plev->anyPolicy, 2); + } + BIO_free(err); +} +#endif + +/*- + * Return value: <= 0 on error, or positive bit mask: + * + * X509_PCY_TREE_VALID: valid tree + * X509_PCY_TREE_EMPTY: empty tree (including bare TA case) + * X509_PCY_TREE_EXPLICIT: explicit policy required + */ +static int tree_init(X509_POLICY_TREE **ptree, STACK_OF(X509) *certs, + unsigned int flags) +{ + X509_POLICY_TREE *tree; + X509_POLICY_LEVEL *level; + const X509_POLICY_CACHE *cache; + X509_POLICY_DATA *data = NULL; + int ret = X509_PCY_TREE_VALID; + int n = sk_X509_num(certs) - 1; /* RFC5280 paths omit the TA */ + int explicit_policy = (flags & X509_V_FLAG_EXPLICIT_POLICY) ? 0 : n+1; + int any_skip = (flags & X509_V_FLAG_INHIBIT_ANY) ? 0 : n+1; + int map_skip = (flags & X509_V_FLAG_INHIBIT_MAP) ? 0 : n+1; + int i; + + *ptree = NULL; + + /* Can't do anything with just a trust anchor */ + if (n == 0) + return X509_PCY_TREE_EMPTY; + + /* + * First setup the policy cache in all n non-TA certificates, this will be + * used in X509_verify_cert() which will invoke the verify callback for all + * certificates with invalid policy extensions. + */ + for (i = n - 1; i >= 0; i--) { + X509 *x = sk_X509_value(certs, i); + + /* Call for side-effect of computing hash and caching extensions */ + X509_check_purpose(x, -1, 0); + + /* If cache is NULL, likely ENOMEM: return immediately */ + if (policy_cache_set(x) == NULL) + return X509_PCY_TREE_INTERNAL; + } + + /* + * At this point check for invalid policies and required explicit policy. + * Note that the explicit_policy counter is a count-down to zero, with the + * requirement kicking in if and once it does that. The counter is + * decremented for every non-self-issued certificate in the path, but may + * be further reduced by policy constraints in a non-leaf certificate. + * + * The ultimate policy set is the intersection of all the policies along + * the path, if we hit a certificate with an empty policy set, and explicit + * policy is required we're done. + */ + for (i = n - 1; + i >= 0 && (explicit_policy > 0 || (ret & X509_PCY_TREE_EMPTY) == 0); + i--) { + X509 *x = sk_X509_value(certs, i); + uint32_t ex_flags = X509_get_extension_flags(x); + + /* All the policies are already cached, we can return early */ + if (ex_flags & EXFLAG_INVALID_POLICY) + return X509_PCY_TREE_INVALID; + + /* Access the cache which we now know exists */ + cache = policy_cache_set(x); + + if ((ret & X509_PCY_TREE_VALID) && cache->data == NULL) + ret = X509_PCY_TREE_EMPTY; + if (explicit_policy > 0) { + if (!(ex_flags & EXFLAG_SI)) + explicit_policy--; + if ((cache->explicit_skip >= 0) + && (cache->explicit_skip < explicit_policy)) + explicit_policy = cache->explicit_skip; + } + } + + if (explicit_policy == 0) + ret |= X509_PCY_TREE_EXPLICIT; + if ((ret & X509_PCY_TREE_VALID) == 0) + return ret; + + /* If we get this far initialize the tree */ + if ((tree = OPENSSL_zalloc(sizeof(*tree))) == NULL) + return X509_PCY_TREE_INTERNAL; + + /* + * http://tools.ietf.org/html/rfc5280#section-6.1.2, figure 3. + * + * The top level is implicitly for the trust anchor with valid expected + * policies of anyPolicy. (RFC 5280 has the TA at depth 0 and the leaf at + * depth n, we have the leaf at depth 0 and the TA at depth n). + */ + if ((tree->levels = OPENSSL_zalloc(sizeof(*tree->levels)*(n+1))) == NULL) { + OPENSSL_free(tree); + return X509_PCY_TREE_INTERNAL; + } + tree->nlevel = n+1; + level = tree->levels; + if ((data = policy_data_new(NULL, OBJ_nid2obj(NID_any_policy), 0)) == NULL) + goto bad_tree; + if (level_add_node(level, data, NULL, tree) == NULL) { + policy_data_free(data); + goto bad_tree; + } + + /* + * In this pass initialize all the tree levels and whether anyPolicy and + * policy mapping are inhibited at each level. + */ + for (i = n - 1; i >= 0; i--) { + X509 *x = sk_X509_value(certs, i); + uint32_t ex_flags = X509_get_extension_flags(x); + + /* Access the cache which we now know exists */ + cache = policy_cache_set(x); + + X509_up_ref(x); + (++level)->cert = x; + + if (!cache->anyPolicy) + level->flags |= X509_V_FLAG_INHIBIT_ANY; + + /* Determine inhibit any and inhibit map flags */ + if (any_skip == 0) { + /* + * Any matching allowed only if certificate is self issued and not + * the last in the chain. + */ + if (!(ex_flags & EXFLAG_SI) || (i == 0)) + level->flags |= X509_V_FLAG_INHIBIT_ANY; + } else { + if (!(ex_flags & EXFLAG_SI)) + any_skip--; + if ((cache->any_skip >= 0) && (cache->any_skip < any_skip)) + any_skip = cache->any_skip; + } + + if (map_skip == 0) + level->flags |= X509_V_FLAG_INHIBIT_MAP; + else { + if (!(ex_flags & EXFLAG_SI)) + map_skip--; + if ((cache->map_skip >= 0) && (cache->map_skip < map_skip)) + map_skip = cache->map_skip; + } + } + + *ptree = tree; + return ret; + + bad_tree: + X509_policy_tree_free(tree); + return X509_PCY_TREE_INTERNAL; +} + +/* + * Return value: 1 on success, 0 otherwise + */ +static int tree_link_matching_nodes(X509_POLICY_LEVEL *curr, + X509_POLICY_DATA *data) +{ + X509_POLICY_LEVEL *last = curr - 1; + int i, matched = 0; + + /* Iterate through all in nodes linking matches */ + for (i = 0; i < sk_X509_POLICY_NODE_num(last->nodes); i++) { + X509_POLICY_NODE *node = sk_X509_POLICY_NODE_value(last->nodes, i); + + if (policy_node_match(last, node, data->valid_policy)) { + if (level_add_node(curr, data, node, NULL) == NULL) + return 0; + matched = 1; + } + } + if (!matched && last->anyPolicy) { + if (level_add_node(curr, data, last->anyPolicy, NULL) == NULL) + return 0; + } + return 1; +} + +/* + * This corresponds to RFC3280 6.1.3(d)(1): link any data from + * CertificatePolicies onto matching parent or anyPolicy if no match. + * + * Return value: 1 on success, 0 otherwise. + */ +static int tree_link_nodes(X509_POLICY_LEVEL *curr, + const X509_POLICY_CACHE *cache) +{ + int i; + + for (i = 0; i < sk_X509_POLICY_DATA_num(cache->data); i++) { + X509_POLICY_DATA *data = sk_X509_POLICY_DATA_value(cache->data, i); + + /* Look for matching nodes in previous level */ + if (!tree_link_matching_nodes(curr, data)) + return 0; + } + return 1; +} + +/* + * This corresponds to RFC3280 6.1.3(d)(2): Create new data for any unmatched + * policies in the parent and link to anyPolicy. + * + * Return value: 1 on success, 0 otherwise. + */ +static int tree_add_unmatched(X509_POLICY_LEVEL *curr, + const X509_POLICY_CACHE *cache, + const ASN1_OBJECT *id, + X509_POLICY_NODE *node, X509_POLICY_TREE *tree) +{ + X509_POLICY_DATA *data; + + if (id == NULL) + id = node->data->valid_policy; + /* + * Create a new node with qualifiers from anyPolicy and id from unmatched + * node. + */ + if ((data = policy_data_new(NULL, id, node_critical(node))) == NULL) + return 0; + + /* Curr may not have anyPolicy */ + data->qualifier_set = cache->anyPolicy->qualifier_set; + data->flags |= POLICY_DATA_FLAG_SHARED_QUALIFIERS; + if (level_add_node(curr, data, node, tree) == NULL) { + policy_data_free(data); + return 0; + } + return 1; +} + +/* + * Return value: 1 on success, 0 otherwise. + */ +static int tree_link_unmatched(X509_POLICY_LEVEL *curr, + const X509_POLICY_CACHE *cache, + X509_POLICY_NODE *node, X509_POLICY_TREE *tree) +{ + const X509_POLICY_LEVEL *last = curr - 1; + int i; + + if ((last->flags & X509_V_FLAG_INHIBIT_MAP) + || !(node->data->flags & POLICY_DATA_FLAG_MAPPED)) { + /* If no policy mapping: matched if one child present */ + if (node->nchild) + return 1; + if (!tree_add_unmatched(curr, cache, NULL, node, tree)) + return 0; + /* Add it */ + } else { + /* If mapping: matched if one child per expected policy set */ + STACK_OF(ASN1_OBJECT) *expset = node->data->expected_policy_set; + if (node->nchild == sk_ASN1_OBJECT_num(expset)) + return 1; + /* Locate unmatched nodes */ + for (i = 0; i < sk_ASN1_OBJECT_num(expset); i++) { + ASN1_OBJECT *oid = sk_ASN1_OBJECT_value(expset, i); + if (level_find_node(curr, node, oid)) + continue; + if (!tree_add_unmatched(curr, cache, oid, node, tree)) + return 0; + } + + } + return 1; +} + +/* + * Return value: 1 on success, 0 otherwise + */ +static int tree_link_any(X509_POLICY_LEVEL *curr, + const X509_POLICY_CACHE *cache, + X509_POLICY_TREE *tree) +{ + int i; + X509_POLICY_NODE *node; + X509_POLICY_LEVEL *last = curr - 1; + + for (i = 0; i < sk_X509_POLICY_NODE_num(last->nodes); i++) { + node = sk_X509_POLICY_NODE_value(last->nodes, i); + + if (!tree_link_unmatched(curr, cache, node, tree)) + return 0; + } + /* Finally add link to anyPolicy */ + if (last->anyPolicy && + level_add_node(curr, cache->anyPolicy, last->anyPolicy, NULL) == NULL) + return 0; + return 1; +} + +/*- + * Prune the tree: delete any child mapped child data on the current level then + * proceed up the tree deleting any data with no children. If we ever have no + * data on a level we can halt because the tree will be empty. + * + * Return value: <= 0 error, otherwise one of: + * + * X509_PCY_TREE_VALID: valid tree + * X509_PCY_TREE_EMPTY: empty tree + */ +static int tree_prune(X509_POLICY_TREE *tree, X509_POLICY_LEVEL *curr) +{ + STACK_OF(X509_POLICY_NODE) *nodes; + X509_POLICY_NODE *node; + int i; + nodes = curr->nodes; + if (curr->flags & X509_V_FLAG_INHIBIT_MAP) { + for (i = sk_X509_POLICY_NODE_num(nodes) - 1; i >= 0; i--) { + node = sk_X509_POLICY_NODE_value(nodes, i); + /* Delete any mapped data: see RFC3280 XXXX */ + if (node->data->flags & POLICY_DATA_FLAG_MAP_MASK) { + node->parent->nchild--; + OPENSSL_free(node); + (void)sk_X509_POLICY_NODE_delete(nodes, i); + } + } + } + + for (;;) { + --curr; + nodes = curr->nodes; + for (i = sk_X509_POLICY_NODE_num(nodes) - 1; i >= 0; i--) { + node = sk_X509_POLICY_NODE_value(nodes, i); + if (node->nchild == 0) { + node->parent->nchild--; + OPENSSL_free(node); + (void)sk_X509_POLICY_NODE_delete(nodes, i); + } + } + if (curr->anyPolicy && !curr->anyPolicy->nchild) { + if (curr->anyPolicy->parent) + curr->anyPolicy->parent->nchild--; + OPENSSL_free(curr->anyPolicy); + curr->anyPolicy = NULL; + } + if (curr == tree->levels) { + /* If we zapped anyPolicy at top then tree is empty */ + if (!curr->anyPolicy) + return X509_PCY_TREE_EMPTY; + break; + } + } + return X509_PCY_TREE_VALID; +} + +/* + * Return value: 1 on success, 0 otherwise. + */ +static int tree_add_auth_node(STACK_OF(X509_POLICY_NODE) **pnodes, + X509_POLICY_NODE *pcy) +{ + if (*pnodes == NULL && + (*pnodes = policy_node_cmp_new()) == NULL) + return 0; + if (sk_X509_POLICY_NODE_find(*pnodes, pcy) != -1) + return 1; + return sk_X509_POLICY_NODE_push(*pnodes, pcy) != 0; +} + +#define TREE_CALC_FAILURE 0 +#define TREE_CALC_OK_NOFREE 1 +#define TREE_CALC_OK_DOFREE 2 + +/*- + * Calculate the authority set based on policy tree. The 'pnodes' parameter is + * used as a store for the set of policy nodes used to calculate the user set. + * If the authority set is not anyPolicy then pnodes will just point to the + * authority set. If however the authority set is anyPolicy then the set of + * valid policies (other than anyPolicy) is store in pnodes. + * + * Return value: + * TREE_CALC_FAILURE on failure, + * TREE_CALC_OK_NOFREE on success and pnodes need not be freed, + * TREE_CALC_OK_DOFREE on success and pnodes needs to be freed + */ +static int tree_calculate_authority_set(X509_POLICY_TREE *tree, + STACK_OF(X509_POLICY_NODE) **pnodes) +{ + X509_POLICY_LEVEL *curr; + X509_POLICY_NODE *node, *anyptr; + STACK_OF(X509_POLICY_NODE) **addnodes; + int i, j; + curr = tree->levels + tree->nlevel - 1; + + /* If last level contains anyPolicy set is anyPolicy */ + if (curr->anyPolicy) { + if (!tree_add_auth_node(&tree->auth_policies, curr->anyPolicy)) + return TREE_CALC_FAILURE; + addnodes = pnodes; + } else + /* Add policies to authority set */ + addnodes = &tree->auth_policies; + + curr = tree->levels; + for (i = 1; i < tree->nlevel; i++) { + /* + * If no anyPolicy node on this this level it can't appear on lower + * levels so end search. + */ + if ((anyptr = curr->anyPolicy) == NULL) + break; + curr++; + for (j = 0; j < sk_X509_POLICY_NODE_num(curr->nodes); j++) { + node = sk_X509_POLICY_NODE_value(curr->nodes, j); + if ((node->parent == anyptr) + && !tree_add_auth_node(addnodes, node)) { + if (addnodes == pnodes) { + sk_X509_POLICY_NODE_free(*pnodes); + *pnodes = NULL; + } + return TREE_CALC_FAILURE; + } + } + } + if (addnodes == pnodes) + return TREE_CALC_OK_DOFREE; + + *pnodes = tree->auth_policies; + return TREE_CALC_OK_NOFREE; +} + +/* + * Return value: 1 on success, 0 otherwise. + */ +static int tree_calculate_user_set(X509_POLICY_TREE *tree, + STACK_OF(ASN1_OBJECT) *policy_oids, + STACK_OF(X509_POLICY_NODE) *auth_nodes) +{ + int i; + X509_POLICY_NODE *node; + ASN1_OBJECT *oid; + X509_POLICY_NODE *anyPolicy; + X509_POLICY_DATA *extra; + + /* + * Check if anyPolicy present in authority constrained policy set: this + * will happen if it is a leaf node. + */ + if (sk_ASN1_OBJECT_num(policy_oids) <= 0) + return 1; + + anyPolicy = tree->levels[tree->nlevel - 1].anyPolicy; + + for (i = 0; i < sk_ASN1_OBJECT_num(policy_oids); i++) { + oid = sk_ASN1_OBJECT_value(policy_oids, i); + if (OBJ_obj2nid(oid) == NID_any_policy) { + tree->flags |= POLICY_FLAG_ANY_POLICY; + return 1; + } + } + + for (i = 0; i < sk_ASN1_OBJECT_num(policy_oids); i++) { + oid = sk_ASN1_OBJECT_value(policy_oids, i); + node = tree_find_sk(auth_nodes, oid); + if (!node) { + if (!anyPolicy) + continue; + /* + * Create a new node with policy ID from user set and qualifiers + * from anyPolicy. + */ + extra = policy_data_new(NULL, oid, node_critical(anyPolicy)); + if (extra == NULL) + return 0; + extra->qualifier_set = anyPolicy->data->qualifier_set; + extra->flags = POLICY_DATA_FLAG_SHARED_QUALIFIERS + | POLICY_DATA_FLAG_EXTRA_NODE; + node = level_add_node(NULL, extra, anyPolicy->parent, tree); + } + if (!tree->user_policies) { + tree->user_policies = sk_X509_POLICY_NODE_new_null(); + if (!tree->user_policies) + return 1; + } + if (!sk_X509_POLICY_NODE_push(tree->user_policies, node)) + return 0; + } + return 1; +} + +/*- + * Return value: <= 0 error, otherwise one of: + * X509_PCY_TREE_VALID: valid tree + * X509_PCY_TREE_EMPTY: empty tree + * (see tree_prune()). + */ +static int tree_evaluate(X509_POLICY_TREE *tree) +{ + int ret, i; + X509_POLICY_LEVEL *curr = tree->levels + 1; + const X509_POLICY_CACHE *cache; + + for (i = 1; i < tree->nlevel; i++, curr++) { + cache = policy_cache_set(curr->cert); + if (!tree_link_nodes(curr, cache)) + return X509_PCY_TREE_INTERNAL; + + if (!(curr->flags & X509_V_FLAG_INHIBIT_ANY) + && !tree_link_any(curr, cache, tree)) + return X509_PCY_TREE_INTERNAL; +#ifdef OPENSSL_POLICY_DEBUG + tree_print("before tree_prune()", tree, curr); +#endif + ret = tree_prune(tree, curr); + if (ret != X509_PCY_TREE_VALID) + return ret; + } + return X509_PCY_TREE_VALID; +} + +static void exnode_free(X509_POLICY_NODE *node) +{ + if (node->data && (node->data->flags & POLICY_DATA_FLAG_EXTRA_NODE)) + OPENSSL_free(node); +} + +void X509_policy_tree_free(X509_POLICY_TREE *tree) +{ + X509_POLICY_LEVEL *curr; + int i; + + if (!tree) + return; + + sk_X509_POLICY_NODE_free(tree->auth_policies); + sk_X509_POLICY_NODE_pop_free(tree->user_policies, exnode_free); + + for (i = 0, curr = tree->levels; i < tree->nlevel; i++, curr++) { + X509_free(curr->cert); + sk_X509_POLICY_NODE_pop_free(curr->nodes, policy_node_free); + policy_node_free(curr->anyPolicy); + } + + sk_X509_POLICY_DATA_pop_free(tree->extra_data, policy_data_free); + OPENSSL_free(tree->levels); + OPENSSL_free(tree); + +} + +/*- + * Application policy checking function. + * Return codes: + * X509_PCY_TREE_FAILURE: Failure to satisfy explicit policy + * X509_PCY_TREE_INVALID: Inconsistent or invalid extensions + * X509_PCY_TREE_INTERNAL: Internal error, most likely malloc + * X509_PCY_TREE_VALID: Success (null tree if empty or bare TA) + */ +int X509_policy_check(X509_POLICY_TREE **ptree, int *pexplicit_policy, + STACK_OF(X509) *certs, + STACK_OF(ASN1_OBJECT) *policy_oids, unsigned int flags) +{ + int init_ret; + int ret; + X509_POLICY_TREE *tree = NULL; + STACK_OF(X509_POLICY_NODE) *nodes, *auth_nodes = NULL; + + *ptree = NULL; + *pexplicit_policy = 0; + init_ret = tree_init(&tree, certs, flags); + + if (init_ret <= 0) + return init_ret; + + if ((init_ret & X509_PCY_TREE_EXPLICIT) == 0) { + if (init_ret & X509_PCY_TREE_EMPTY) { + X509_policy_tree_free(tree); + return X509_PCY_TREE_VALID; + } + } else { + *pexplicit_policy = 1; + /* Tree empty and requireExplicit True: Error */ + if (init_ret & X509_PCY_TREE_EMPTY) + return X509_PCY_TREE_FAILURE; + } + + ret = tree_evaluate(tree); +#ifdef OPENSSL_POLICY_DEBUG + tree_print("tree_evaluate()", tree, NULL); +#endif + if (ret <= 0) + goto error; + + if (ret == X509_PCY_TREE_EMPTY) { + X509_policy_tree_free(tree); + if (init_ret & X509_PCY_TREE_EXPLICIT) + return X509_PCY_TREE_FAILURE; + return X509_PCY_TREE_VALID; + } + + /* Tree is not empty: continue */ + if ((ret = tree_calculate_authority_set(tree, &auth_nodes)) == 0 || + !tree_calculate_user_set(tree, policy_oids, auth_nodes)) + goto error; + if (ret == TREE_CALC_OK_DOFREE) + sk_X509_POLICY_NODE_free(auth_nodes); + + *ptree = tree; + + if (init_ret & X509_PCY_TREE_EXPLICIT) { + nodes = X509_policy_tree_get0_user_policies(tree); + if (sk_X509_POLICY_NODE_num(nodes) <= 0) + return X509_PCY_TREE_FAILURE; + } + return X509_PCY_TREE_VALID; + + error: + X509_policy_tree_free(tree); + return X509_PCY_TREE_INTERNAL; +} diff --git a/crypto/x509v3/tabtest.c b/crypto/x509v3/tabtest.c new file mode 100644 index 000000000..a33a63a79 --- /dev/null +++ b/crypto/x509v3/tabtest.c @@ -0,0 +1,42 @@ +/* + * Copyright 1999-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +/* + * Simple program to check the ext_dat.h is correct and print out problems if + * it is not. + */ + +#include + +#include + +#include "ext_dat.h" + +main() +{ + int i, prev = -1, bad = 0; + X509V3_EXT_METHOD **tmp; + i = OSSL_NELEM(standard_exts); + if (i != STANDARD_EXTENSION_COUNT) + fprintf(stderr, "Extension number invalid expecting %d\n", i); + tmp = standard_exts; + for (i = 0; i < STANDARD_EXTENSION_COUNT; i++, tmp++) { + if ((*tmp)->ext_nid < prev) + bad = 1; + prev = (*tmp)->ext_nid; + + } + if (bad) { + tmp = standard_exts; + fprintf(stderr, "Extensions out of order!\n"); + for (i = 0; i < STANDARD_EXTENSION_COUNT; i++, tmp++) + printf("%d : %s\n", (*tmp)->ext_nid, OBJ_nid2sn((*tmp)->ext_nid)); + } else + fprintf(stderr, "Order OK\n"); +} diff --git a/crypto/x509v3/v3_addr.c b/crypto/x509v3/v3_addr.c new file mode 100644 index 000000000..ef1d775ac --- /dev/null +++ b/crypto/x509v3/v3_addr.c @@ -0,0 +1,1305 @@ +/* + * Copyright 2006-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +/* + * Implementation of RFC 3779 section 2.2. + */ + +#include +#include + +#include "internal/cryptlib.h" +#include +#include +#include +#include +#include +#include "internal/x509_int.h" +#include "ext_dat.h" + +#ifndef OPENSSL_NO_RFC3779 + +/* + * OpenSSL ASN.1 template translation of RFC 3779 2.2.3. + */ + +ASN1_SEQUENCE(IPAddressRange) = { + ASN1_SIMPLE(IPAddressRange, min, ASN1_BIT_STRING), + ASN1_SIMPLE(IPAddressRange, max, ASN1_BIT_STRING) +} ASN1_SEQUENCE_END(IPAddressRange) + +ASN1_CHOICE(IPAddressOrRange) = { + ASN1_SIMPLE(IPAddressOrRange, u.addressPrefix, ASN1_BIT_STRING), + ASN1_SIMPLE(IPAddressOrRange, u.addressRange, IPAddressRange) +} ASN1_CHOICE_END(IPAddressOrRange) + +ASN1_CHOICE(IPAddressChoice) = { + ASN1_SIMPLE(IPAddressChoice, u.inherit, ASN1_NULL), + ASN1_SEQUENCE_OF(IPAddressChoice, u.addressesOrRanges, IPAddressOrRange) +} ASN1_CHOICE_END(IPAddressChoice) + +ASN1_SEQUENCE(IPAddressFamily) = { + ASN1_SIMPLE(IPAddressFamily, addressFamily, ASN1_OCTET_STRING), + ASN1_SIMPLE(IPAddressFamily, ipAddressChoice, IPAddressChoice) +} ASN1_SEQUENCE_END(IPAddressFamily) + +ASN1_ITEM_TEMPLATE(IPAddrBlocks) = + ASN1_EX_TEMPLATE_TYPE(ASN1_TFLG_SEQUENCE_OF, 0, + IPAddrBlocks, IPAddressFamily) +static_ASN1_ITEM_TEMPLATE_END(IPAddrBlocks) + +IMPLEMENT_ASN1_FUNCTIONS(IPAddressRange) +IMPLEMENT_ASN1_FUNCTIONS(IPAddressOrRange) +IMPLEMENT_ASN1_FUNCTIONS(IPAddressChoice) +IMPLEMENT_ASN1_FUNCTIONS(IPAddressFamily) + +/* + * How much buffer space do we need for a raw address? + */ +#define ADDR_RAW_BUF_LEN 16 + +/* + * What's the address length associated with this AFI? + */ +static int length_from_afi(const unsigned afi) +{ + switch (afi) { + case IANA_AFI_IPV4: + return 4; + case IANA_AFI_IPV6: + return 16; + default: + return 0; + } +} + +/* + * Extract the AFI from an IPAddressFamily. + */ +unsigned int X509v3_addr_get_afi(const IPAddressFamily *f) +{ + return ((f != NULL && + f->addressFamily != NULL && f->addressFamily->data != NULL) + ? ((f->addressFamily->data[0] << 8) | (f->addressFamily->data[1])) + : 0); +} + +/* + * Expand the bitstring form of an address into a raw byte array. + * At the moment this is coded for simplicity, not speed. + */ +static int addr_expand(unsigned char *addr, + const ASN1_BIT_STRING *bs, + const int length, const unsigned char fill) +{ + if (bs->length < 0 || bs->length > length) + return 0; + if (bs->length > 0) { + memcpy(addr, bs->data, bs->length); + if ((bs->flags & 7) != 0) { + unsigned char mask = 0xFF >> (8 - (bs->flags & 7)); + if (fill == 0) + addr[bs->length - 1] &= ~mask; + else + addr[bs->length - 1] |= mask; + } + } + memset(addr + bs->length, fill, length - bs->length); + return 1; +} + +/* + * Extract the prefix length from a bitstring. + */ +#define addr_prefixlen(bs) ((int) ((bs)->length * 8 - ((bs)->flags & 7))) + +/* + * i2r handler for one address bitstring. + */ +static int i2r_address(BIO *out, + const unsigned afi, + const unsigned char fill, const ASN1_BIT_STRING *bs) +{ + unsigned char addr[ADDR_RAW_BUF_LEN]; + int i, n; + + if (bs->length < 0) + return 0; + switch (afi) { + case IANA_AFI_IPV4: + if (!addr_expand(addr, bs, 4, fill)) + return 0; + BIO_printf(out, "%d.%d.%d.%d", addr[0], addr[1], addr[2], addr[3]); + break; + case IANA_AFI_IPV6: + if (!addr_expand(addr, bs, 16, fill)) + return 0; + for (n = 16; n > 1 && addr[n - 1] == 0x00 && addr[n - 2] == 0x00; + n -= 2) ; + for (i = 0; i < n; i += 2) + BIO_printf(out, "%x%s", (addr[i] << 8) | addr[i + 1], + (i < 14 ? ":" : "")); + if (i < 16) + BIO_puts(out, ":"); + if (i == 0) + BIO_puts(out, ":"); + break; + default: + for (i = 0; i < bs->length; i++) + BIO_printf(out, "%s%02x", (i > 0 ? ":" : ""), bs->data[i]); + BIO_printf(out, "[%d]", (int)(bs->flags & 7)); + break; + } + return 1; +} + +/* + * i2r handler for a sequence of addresses and ranges. + */ +static int i2r_IPAddressOrRanges(BIO *out, + const int indent, + const IPAddressOrRanges *aors, + const unsigned afi) +{ + int i; + for (i = 0; i < sk_IPAddressOrRange_num(aors); i++) { + const IPAddressOrRange *aor = sk_IPAddressOrRange_value(aors, i); + BIO_printf(out, "%*s", indent, ""); + switch (aor->type) { + case IPAddressOrRange_addressPrefix: + if (!i2r_address(out, afi, 0x00, aor->u.addressPrefix)) + return 0; + BIO_printf(out, "/%d\n", addr_prefixlen(aor->u.addressPrefix)); + continue; + case IPAddressOrRange_addressRange: + if (!i2r_address(out, afi, 0x00, aor->u.addressRange->min)) + return 0; + BIO_puts(out, "-"); + if (!i2r_address(out, afi, 0xFF, aor->u.addressRange->max)) + return 0; + BIO_puts(out, "\n"); + continue; + } + } + return 1; +} + +/* + * i2r handler for an IPAddrBlocks extension. + */ +static int i2r_IPAddrBlocks(const X509V3_EXT_METHOD *method, + void *ext, BIO *out, int indent) +{ + const IPAddrBlocks *addr = ext; + int i; + for (i = 0; i < sk_IPAddressFamily_num(addr); i++) { + IPAddressFamily *f = sk_IPAddressFamily_value(addr, i); + const unsigned int afi = X509v3_addr_get_afi(f); + switch (afi) { + case IANA_AFI_IPV4: + BIO_printf(out, "%*sIPv4", indent, ""); + break; + case IANA_AFI_IPV6: + BIO_printf(out, "%*sIPv6", indent, ""); + break; + default: + BIO_printf(out, "%*sUnknown AFI %u", indent, "", afi); + break; + } + if (f->addressFamily->length > 2) { + switch (f->addressFamily->data[2]) { + case 1: + BIO_puts(out, " (Unicast)"); + break; + case 2: + BIO_puts(out, " (Multicast)"); + break; + case 3: + BIO_puts(out, " (Unicast/Multicast)"); + break; + case 4: + BIO_puts(out, " (MPLS)"); + break; + case 64: + BIO_puts(out, " (Tunnel)"); + break; + case 65: + BIO_puts(out, " (VPLS)"); + break; + case 66: + BIO_puts(out, " (BGP MDT)"); + break; + case 128: + BIO_puts(out, " (MPLS-labeled VPN)"); + break; + default: + BIO_printf(out, " (Unknown SAFI %u)", + (unsigned)f->addressFamily->data[2]); + break; + } + } + switch (f->ipAddressChoice->type) { + case IPAddressChoice_inherit: + BIO_puts(out, ": inherit\n"); + break; + case IPAddressChoice_addressesOrRanges: + BIO_puts(out, ":\n"); + if (!i2r_IPAddressOrRanges(out, + indent + 2, + f->ipAddressChoice-> + u.addressesOrRanges, afi)) + return 0; + break; + } + } + return 1; +} + +/* + * Sort comparison function for a sequence of IPAddressOrRange + * elements. + * + * There's no sane answer we can give if addr_expand() fails, and an + * assertion failure on externally supplied data is seriously uncool, + * so we just arbitrarily declare that if given invalid inputs this + * function returns -1. If this messes up your preferred sort order + * for garbage input, tough noogies. + */ +static int IPAddressOrRange_cmp(const IPAddressOrRange *a, + const IPAddressOrRange *b, const int length) +{ + unsigned char addr_a[ADDR_RAW_BUF_LEN], addr_b[ADDR_RAW_BUF_LEN]; + int prefixlen_a = 0, prefixlen_b = 0; + int r; + + switch (a->type) { + case IPAddressOrRange_addressPrefix: + if (!addr_expand(addr_a, a->u.addressPrefix, length, 0x00)) + return -1; + prefixlen_a = addr_prefixlen(a->u.addressPrefix); + break; + case IPAddressOrRange_addressRange: + if (!addr_expand(addr_a, a->u.addressRange->min, length, 0x00)) + return -1; + prefixlen_a = length * 8; + break; + } + + switch (b->type) { + case IPAddressOrRange_addressPrefix: + if (!addr_expand(addr_b, b->u.addressPrefix, length, 0x00)) + return -1; + prefixlen_b = addr_prefixlen(b->u.addressPrefix); + break; + case IPAddressOrRange_addressRange: + if (!addr_expand(addr_b, b->u.addressRange->min, length, 0x00)) + return -1; + prefixlen_b = length * 8; + break; + } + + if ((r = memcmp(addr_a, addr_b, length)) != 0) + return r; + else + return prefixlen_a - prefixlen_b; +} + +/* + * IPv4-specific closure over IPAddressOrRange_cmp, since sk_sort() + * comparison routines are only allowed two arguments. + */ +static int v4IPAddressOrRange_cmp(const IPAddressOrRange *const *a, + const IPAddressOrRange *const *b) +{ + return IPAddressOrRange_cmp(*a, *b, 4); +} + +/* + * IPv6-specific closure over IPAddressOrRange_cmp, since sk_sort() + * comparison routines are only allowed two arguments. + */ +static int v6IPAddressOrRange_cmp(const IPAddressOrRange *const *a, + const IPAddressOrRange *const *b) +{ + return IPAddressOrRange_cmp(*a, *b, 16); +} + +/* + * Calculate whether a range collapses to a prefix. + * See last paragraph of RFC 3779 2.2.3.7. + */ +static int range_should_be_prefix(const unsigned char *min, + const unsigned char *max, const int length) +{ + unsigned char mask; + int i, j; + + OPENSSL_assert(memcmp(min, max, length) <= 0); + for (i = 0; i < length && min[i] == max[i]; i++) ; + for (j = length - 1; j >= 0 && min[j] == 0x00 && max[j] == 0xFF; j--) ; + if (i < j) + return -1; + if (i > j) + return i * 8; + mask = min[i] ^ max[i]; + switch (mask) { + case 0x01: + j = 7; + break; + case 0x03: + j = 6; + break; + case 0x07: + j = 5; + break; + case 0x0F: + j = 4; + break; + case 0x1F: + j = 3; + break; + case 0x3F: + j = 2; + break; + case 0x7F: + j = 1; + break; + default: + return -1; + } + if ((min[i] & mask) != 0 || (max[i] & mask) != mask) + return -1; + else + return i * 8 + j; +} + +/* + * Construct a prefix. + */ +static int make_addressPrefix(IPAddressOrRange **result, + unsigned char *addr, const int prefixlen) +{ + int bytelen = (prefixlen + 7) / 8, bitlen = prefixlen % 8; + IPAddressOrRange *aor = IPAddressOrRange_new(); + + if (aor == NULL) + return 0; + aor->type = IPAddressOrRange_addressPrefix; + if (aor->u.addressPrefix == NULL && + (aor->u.addressPrefix = ASN1_BIT_STRING_new()) == NULL) + goto err; + if (!ASN1_BIT_STRING_set(aor->u.addressPrefix, addr, bytelen)) + goto err; + aor->u.addressPrefix->flags &= ~7; + aor->u.addressPrefix->flags |= ASN1_STRING_FLAG_BITS_LEFT; + if (bitlen > 0) { + aor->u.addressPrefix->data[bytelen - 1] &= ~(0xFF >> bitlen); + aor->u.addressPrefix->flags |= 8 - bitlen; + } + + *result = aor; + return 1; + + err: + IPAddressOrRange_free(aor); + return 0; +} + +/* + * Construct a range. If it can be expressed as a prefix, + * return a prefix instead. Doing this here simplifies + * the rest of the code considerably. + */ +static int make_addressRange(IPAddressOrRange **result, + unsigned char *min, + unsigned char *max, const int length) +{ + IPAddressOrRange *aor; + int i, prefixlen; + + if ((prefixlen = range_should_be_prefix(min, max, length)) >= 0) + return make_addressPrefix(result, min, prefixlen); + + if ((aor = IPAddressOrRange_new()) == NULL) + return 0; + aor->type = IPAddressOrRange_addressRange; + OPENSSL_assert(aor->u.addressRange == NULL); + if ((aor->u.addressRange = IPAddressRange_new()) == NULL) + goto err; + if (aor->u.addressRange->min == NULL && + (aor->u.addressRange->min = ASN1_BIT_STRING_new()) == NULL) + goto err; + if (aor->u.addressRange->max == NULL && + (aor->u.addressRange->max = ASN1_BIT_STRING_new()) == NULL) + goto err; + + for (i = length; i > 0 && min[i - 1] == 0x00; --i) ; + if (!ASN1_BIT_STRING_set(aor->u.addressRange->min, min, i)) + goto err; + aor->u.addressRange->min->flags &= ~7; + aor->u.addressRange->min->flags |= ASN1_STRING_FLAG_BITS_LEFT; + if (i > 0) { + unsigned char b = min[i - 1]; + int j = 1; + while ((b & (0xFFU >> j)) != 0) + ++j; + aor->u.addressRange->min->flags |= 8 - j; + } + + for (i = length; i > 0 && max[i - 1] == 0xFF; --i) ; + if (!ASN1_BIT_STRING_set(aor->u.addressRange->max, max, i)) + goto err; + aor->u.addressRange->max->flags &= ~7; + aor->u.addressRange->max->flags |= ASN1_STRING_FLAG_BITS_LEFT; + if (i > 0) { + unsigned char b = max[i - 1]; + int j = 1; + while ((b & (0xFFU >> j)) != (0xFFU >> j)) + ++j; + aor->u.addressRange->max->flags |= 8 - j; + } + + *result = aor; + return 1; + + err: + IPAddressOrRange_free(aor); + return 0; +} + +/* + * Construct a new address family or find an existing one. + */ +static IPAddressFamily *make_IPAddressFamily(IPAddrBlocks *addr, + const unsigned afi, + const unsigned *safi) +{ + IPAddressFamily *f; + unsigned char key[3]; + int keylen; + int i; + + key[0] = (afi >> 8) & 0xFF; + key[1] = afi & 0xFF; + if (safi != NULL) { + key[2] = *safi & 0xFF; + keylen = 3; + } else { + keylen = 2; + } + + for (i = 0; i < sk_IPAddressFamily_num(addr); i++) { + f = sk_IPAddressFamily_value(addr, i); + OPENSSL_assert(f->addressFamily->data != NULL); + if (f->addressFamily->length == keylen && + !memcmp(f->addressFamily->data, key, keylen)) + return f; + } + + if ((f = IPAddressFamily_new()) == NULL) + goto err; + if (f->ipAddressChoice == NULL && + (f->ipAddressChoice = IPAddressChoice_new()) == NULL) + goto err; + if (f->addressFamily == NULL && + (f->addressFamily = ASN1_OCTET_STRING_new()) == NULL) + goto err; + if (!ASN1_OCTET_STRING_set(f->addressFamily, key, keylen)) + goto err; + if (!sk_IPAddressFamily_push(addr, f)) + goto err; + + return f; + + err: + IPAddressFamily_free(f); + return NULL; +} + +/* + * Add an inheritance element. + */ +int X509v3_addr_add_inherit(IPAddrBlocks *addr, + const unsigned afi, const unsigned *safi) +{ + IPAddressFamily *f = make_IPAddressFamily(addr, afi, safi); + if (f == NULL || + f->ipAddressChoice == NULL || + (f->ipAddressChoice->type == IPAddressChoice_addressesOrRanges && + f->ipAddressChoice->u.addressesOrRanges != NULL)) + return 0; + if (f->ipAddressChoice->type == IPAddressChoice_inherit && + f->ipAddressChoice->u.inherit != NULL) + return 1; + if (f->ipAddressChoice->u.inherit == NULL && + (f->ipAddressChoice->u.inherit = ASN1_NULL_new()) == NULL) + return 0; + f->ipAddressChoice->type = IPAddressChoice_inherit; + return 1; +} + +/* + * Construct an IPAddressOrRange sequence, or return an existing one. + */ +static IPAddressOrRanges *make_prefix_or_range(IPAddrBlocks *addr, + const unsigned afi, + const unsigned *safi) +{ + IPAddressFamily *f = make_IPAddressFamily(addr, afi, safi); + IPAddressOrRanges *aors = NULL; + + if (f == NULL || + f->ipAddressChoice == NULL || + (f->ipAddressChoice->type == IPAddressChoice_inherit && + f->ipAddressChoice->u.inherit != NULL)) + return NULL; + if (f->ipAddressChoice->type == IPAddressChoice_addressesOrRanges) + aors = f->ipAddressChoice->u.addressesOrRanges; + if (aors != NULL) + return aors; + if ((aors = sk_IPAddressOrRange_new_null()) == NULL) + return NULL; + switch (afi) { + case IANA_AFI_IPV4: + (void)sk_IPAddressOrRange_set_cmp_func(aors, v4IPAddressOrRange_cmp); + break; + case IANA_AFI_IPV6: + (void)sk_IPAddressOrRange_set_cmp_func(aors, v6IPAddressOrRange_cmp); + break; + } + f->ipAddressChoice->type = IPAddressChoice_addressesOrRanges; + f->ipAddressChoice->u.addressesOrRanges = aors; + return aors; +} + +/* + * Add a prefix. + */ +int X509v3_addr_add_prefix(IPAddrBlocks *addr, + const unsigned afi, + const unsigned *safi, + unsigned char *a, const int prefixlen) +{ + IPAddressOrRanges *aors = make_prefix_or_range(addr, afi, safi); + IPAddressOrRange *aor; + if (aors == NULL || !make_addressPrefix(&aor, a, prefixlen)) + return 0; + if (sk_IPAddressOrRange_push(aors, aor)) + return 1; + IPAddressOrRange_free(aor); + return 0; +} + +/* + * Add a range. + */ +int X509v3_addr_add_range(IPAddrBlocks *addr, + const unsigned afi, + const unsigned *safi, + unsigned char *min, unsigned char *max) +{ + IPAddressOrRanges *aors = make_prefix_or_range(addr, afi, safi); + IPAddressOrRange *aor; + int length = length_from_afi(afi); + if (aors == NULL) + return 0; + if (!make_addressRange(&aor, min, max, length)) + return 0; + if (sk_IPAddressOrRange_push(aors, aor)) + return 1; + IPAddressOrRange_free(aor); + return 0; +} + +/* + * Extract min and max values from an IPAddressOrRange. + */ +static int extract_min_max(IPAddressOrRange *aor, + unsigned char *min, unsigned char *max, int length) +{ + if (aor == NULL || min == NULL || max == NULL) + return 0; + switch (aor->type) { + case IPAddressOrRange_addressPrefix: + return (addr_expand(min, aor->u.addressPrefix, length, 0x00) && + addr_expand(max, aor->u.addressPrefix, length, 0xFF)); + case IPAddressOrRange_addressRange: + return (addr_expand(min, aor->u.addressRange->min, length, 0x00) && + addr_expand(max, aor->u.addressRange->max, length, 0xFF)); + } + return 0; +} + +/* + * Public wrapper for extract_min_max(). + */ +int X509v3_addr_get_range(IPAddressOrRange *aor, + const unsigned afi, + unsigned char *min, + unsigned char *max, const int length) +{ + int afi_length = length_from_afi(afi); + if (aor == NULL || min == NULL || max == NULL || + afi_length == 0 || length < afi_length || + (aor->type != IPAddressOrRange_addressPrefix && + aor->type != IPAddressOrRange_addressRange) || + !extract_min_max(aor, min, max, afi_length)) + return 0; + + return afi_length; +} + +/* + * Sort comparison function for a sequence of IPAddressFamily. + * + * The last paragraph of RFC 3779 2.2.3.3 is slightly ambiguous about + * the ordering: I can read it as meaning that IPv6 without a SAFI + * comes before IPv4 with a SAFI, which seems pretty weird. The + * examples in appendix B suggest that the author intended the + * null-SAFI rule to apply only within a single AFI, which is what I + * would have expected and is what the following code implements. + */ +static int IPAddressFamily_cmp(const IPAddressFamily *const *a_, + const IPAddressFamily *const *b_) +{ + const ASN1_OCTET_STRING *a = (*a_)->addressFamily; + const ASN1_OCTET_STRING *b = (*b_)->addressFamily; + int len = ((a->length <= b->length) ? a->length : b->length); + int cmp = memcmp(a->data, b->data, len); + return cmp ? cmp : a->length - b->length; +} + +/* + * Check whether an IPAddrBLocks is in canonical form. + */ +int X509v3_addr_is_canonical(IPAddrBlocks *addr) +{ + unsigned char a_min[ADDR_RAW_BUF_LEN], a_max[ADDR_RAW_BUF_LEN]; + unsigned char b_min[ADDR_RAW_BUF_LEN], b_max[ADDR_RAW_BUF_LEN]; + IPAddressOrRanges *aors; + int i, j, k; + + /* + * Empty extension is canonical. + */ + if (addr == NULL) + return 1; + + /* + * Check whether the top-level list is in order. + */ + for (i = 0; i < sk_IPAddressFamily_num(addr) - 1; i++) { + const IPAddressFamily *a = sk_IPAddressFamily_value(addr, i); + const IPAddressFamily *b = sk_IPAddressFamily_value(addr, i + 1); + if (IPAddressFamily_cmp(&a, &b) >= 0) + return 0; + } + + /* + * Top level's ok, now check each address family. + */ + for (i = 0; i < sk_IPAddressFamily_num(addr); i++) { + IPAddressFamily *f = sk_IPAddressFamily_value(addr, i); + int length = length_from_afi(X509v3_addr_get_afi(f)); + + /* + * Inheritance is canonical. Anything other than inheritance or + * a SEQUENCE OF IPAddressOrRange is an ASN.1 error or something. + */ + if (f == NULL || f->ipAddressChoice == NULL) + return 0; + switch (f->ipAddressChoice->type) { + case IPAddressChoice_inherit: + continue; + case IPAddressChoice_addressesOrRanges: + break; + default: + return 0; + } + + /* + * It's an IPAddressOrRanges sequence, check it. + */ + aors = f->ipAddressChoice->u.addressesOrRanges; + if (sk_IPAddressOrRange_num(aors) == 0) + return 0; + for (j = 0; j < sk_IPAddressOrRange_num(aors) - 1; j++) { + IPAddressOrRange *a = sk_IPAddressOrRange_value(aors, j); + IPAddressOrRange *b = sk_IPAddressOrRange_value(aors, j + 1); + + if (!extract_min_max(a, a_min, a_max, length) || + !extract_min_max(b, b_min, b_max, length)) + return 0; + + /* + * Punt misordered list, overlapping start, or inverted range. + */ + if (memcmp(a_min, b_min, length) >= 0 || + memcmp(a_min, a_max, length) > 0 || + memcmp(b_min, b_max, length) > 0) + return 0; + + /* + * Punt if adjacent or overlapping. Check for adjacency by + * subtracting one from b_min first. + */ + for (k = length - 1; k >= 0 && b_min[k]-- == 0x00; k--) ; + if (memcmp(a_max, b_min, length) >= 0) + return 0; + + /* + * Check for range that should be expressed as a prefix. + */ + if (a->type == IPAddressOrRange_addressRange && + range_should_be_prefix(a_min, a_max, length) >= 0) + return 0; + } + + /* + * Check range to see if it's inverted or should be a + * prefix. + */ + j = sk_IPAddressOrRange_num(aors) - 1; + { + IPAddressOrRange *a = sk_IPAddressOrRange_value(aors, j); + if (a != NULL && a->type == IPAddressOrRange_addressRange) { + if (!extract_min_max(a, a_min, a_max, length)) + return 0; + if (memcmp(a_min, a_max, length) > 0 || + range_should_be_prefix(a_min, a_max, length) >= 0) + return 0; + } + } + } + + /* + * If we made it through all that, we're happy. + */ + return 1; +} + +/* + * Whack an IPAddressOrRanges into canonical form. + */ +static int IPAddressOrRanges_canonize(IPAddressOrRanges *aors, + const unsigned afi) +{ + int i, j, length = length_from_afi(afi); + + /* + * Sort the IPAddressOrRanges sequence. + */ + sk_IPAddressOrRange_sort(aors); + + /* + * Clean up representation issues, punt on duplicates or overlaps. + */ + for (i = 0; i < sk_IPAddressOrRange_num(aors) - 1; i++) { + IPAddressOrRange *a = sk_IPAddressOrRange_value(aors, i); + IPAddressOrRange *b = sk_IPAddressOrRange_value(aors, i + 1); + unsigned char a_min[ADDR_RAW_BUF_LEN], a_max[ADDR_RAW_BUF_LEN]; + unsigned char b_min[ADDR_RAW_BUF_LEN], b_max[ADDR_RAW_BUF_LEN]; + + if (!extract_min_max(a, a_min, a_max, length) || + !extract_min_max(b, b_min, b_max, length)) + return 0; + + /* + * Punt inverted ranges. + */ + if (memcmp(a_min, a_max, length) > 0 || + memcmp(b_min, b_max, length) > 0) + return 0; + + /* + * Punt overlaps. + */ + if (memcmp(a_max, b_min, length) >= 0) + return 0; + + /* + * Merge if a and b are adjacent. We check for + * adjacency by subtracting one from b_min first. + */ + for (j = length - 1; j >= 0 && b_min[j]-- == 0x00; j--) ; + if (memcmp(a_max, b_min, length) == 0) { + IPAddressOrRange *merged; + if (!make_addressRange(&merged, a_min, b_max, length)) + return 0; + (void)sk_IPAddressOrRange_set(aors, i, merged); + (void)sk_IPAddressOrRange_delete(aors, i + 1); + IPAddressOrRange_free(a); + IPAddressOrRange_free(b); + --i; + continue; + } + } + + /* + * Check for inverted final range. + */ + j = sk_IPAddressOrRange_num(aors) - 1; + { + IPAddressOrRange *a = sk_IPAddressOrRange_value(aors, j); + if (a != NULL && a->type == IPAddressOrRange_addressRange) { + unsigned char a_min[ADDR_RAW_BUF_LEN], a_max[ADDR_RAW_BUF_LEN]; + if (!extract_min_max(a, a_min, a_max, length)) + return 0; + if (memcmp(a_min, a_max, length) > 0) + return 0; + } + } + + return 1; +} + +/* + * Whack an IPAddrBlocks extension into canonical form. + */ +int X509v3_addr_canonize(IPAddrBlocks *addr) +{ + int i; + for (i = 0; i < sk_IPAddressFamily_num(addr); i++) { + IPAddressFamily *f = sk_IPAddressFamily_value(addr, i); + if (f->ipAddressChoice->type == IPAddressChoice_addressesOrRanges && + !IPAddressOrRanges_canonize(f->ipAddressChoice-> + u.addressesOrRanges, + X509v3_addr_get_afi(f))) + return 0; + } + (void)sk_IPAddressFamily_set_cmp_func(addr, IPAddressFamily_cmp); + sk_IPAddressFamily_sort(addr); + OPENSSL_assert(X509v3_addr_is_canonical(addr)); + return 1; +} + +/* + * v2i handler for the IPAddrBlocks extension. + */ +static void *v2i_IPAddrBlocks(const struct v3_ext_method *method, + struct v3_ext_ctx *ctx, + STACK_OF(CONF_VALUE) *values) +{ + static const char v4addr_chars[] = "0123456789."; + static const char v6addr_chars[] = "0123456789.:abcdefABCDEF"; + IPAddrBlocks *addr = NULL; + char *s = NULL, *t; + int i; + + if ((addr = sk_IPAddressFamily_new(IPAddressFamily_cmp)) == NULL) { + X509V3err(X509V3_F_V2I_IPADDRBLOCKS, ERR_R_MALLOC_FAILURE); + return NULL; + } + + for (i = 0; i < sk_CONF_VALUE_num(values); i++) { + CONF_VALUE *val = sk_CONF_VALUE_value(values, i); + unsigned char min[ADDR_RAW_BUF_LEN], max[ADDR_RAW_BUF_LEN]; + unsigned afi, *safi = NULL, safi_; + const char *addr_chars = NULL; + int prefixlen, i1, i2, delim, length; + + if (!name_cmp(val->name, "IPv4")) { + afi = IANA_AFI_IPV4; + } else if (!name_cmp(val->name, "IPv6")) { + afi = IANA_AFI_IPV6; + } else if (!name_cmp(val->name, "IPv4-SAFI")) { + afi = IANA_AFI_IPV4; + safi = &safi_; + } else if (!name_cmp(val->name, "IPv6-SAFI")) { + afi = IANA_AFI_IPV6; + safi = &safi_; + } else { + X509V3err(X509V3_F_V2I_IPADDRBLOCKS, + X509V3_R_EXTENSION_NAME_ERROR); + X509V3_conf_err(val); + goto err; + } + + switch (afi) { + case IANA_AFI_IPV4: + addr_chars = v4addr_chars; + break; + case IANA_AFI_IPV6: + addr_chars = v6addr_chars; + break; + } + + length = length_from_afi(afi); + + /* + * Handle SAFI, if any, and OPENSSL_strdup() so we can null-terminate + * the other input values. + */ + if (safi != NULL) { + *safi = strtoul(val->value, &t, 0); + t += strspn(t, " \t"); + if (*safi > 0xFF || *t++ != ':') { + X509V3err(X509V3_F_V2I_IPADDRBLOCKS, X509V3_R_INVALID_SAFI); + X509V3_conf_err(val); + goto err; + } + t += strspn(t, " \t"); + s = OPENSSL_strdup(t); + } else { + s = OPENSSL_strdup(val->value); + } + if (s == NULL) { + X509V3err(X509V3_F_V2I_IPADDRBLOCKS, ERR_R_MALLOC_FAILURE); + goto err; + } + + /* + * Check for inheritance. Not worth additional complexity to + * optimize this (seldom-used) case. + */ + if (strcmp(s, "inherit") == 0) { + if (!X509v3_addr_add_inherit(addr, afi, safi)) { + X509V3err(X509V3_F_V2I_IPADDRBLOCKS, + X509V3_R_INVALID_INHERITANCE); + X509V3_conf_err(val); + goto err; + } + OPENSSL_free(s); + s = NULL; + continue; + } + + i1 = strspn(s, addr_chars); + i2 = i1 + strspn(s + i1, " \t"); + delim = s[i2++]; + s[i1] = '\0'; + + if (a2i_ipadd(min, s) != length) { + X509V3err(X509V3_F_V2I_IPADDRBLOCKS, X509V3_R_INVALID_IPADDRESS); + X509V3_conf_err(val); + goto err; + } + + switch (delim) { + case '/': + prefixlen = (int)strtoul(s + i2, &t, 10); + if (t == s + i2 || *t != '\0') { + X509V3err(X509V3_F_V2I_IPADDRBLOCKS, + X509V3_R_EXTENSION_VALUE_ERROR); + X509V3_conf_err(val); + goto err; + } + if (!X509v3_addr_add_prefix(addr, afi, safi, min, prefixlen)) { + X509V3err(X509V3_F_V2I_IPADDRBLOCKS, ERR_R_MALLOC_FAILURE); + goto err; + } + break; + case '-': + i1 = i2 + strspn(s + i2, " \t"); + i2 = i1 + strspn(s + i1, addr_chars); + if (i1 == i2 || s[i2] != '\0') { + X509V3err(X509V3_F_V2I_IPADDRBLOCKS, + X509V3_R_EXTENSION_VALUE_ERROR); + X509V3_conf_err(val); + goto err; + } + if (a2i_ipadd(max, s + i1) != length) { + X509V3err(X509V3_F_V2I_IPADDRBLOCKS, + X509V3_R_INVALID_IPADDRESS); + X509V3_conf_err(val); + goto err; + } + if (memcmp(min, max, length_from_afi(afi)) > 0) { + X509V3err(X509V3_F_V2I_IPADDRBLOCKS, + X509V3_R_EXTENSION_VALUE_ERROR); + X509V3_conf_err(val); + goto err; + } + if (!X509v3_addr_add_range(addr, afi, safi, min, max)) { + X509V3err(X509V3_F_V2I_IPADDRBLOCKS, ERR_R_MALLOC_FAILURE); + goto err; + } + break; + case '\0': + if (!X509v3_addr_add_prefix(addr, afi, safi, min, length * 8)) { + X509V3err(X509V3_F_V2I_IPADDRBLOCKS, ERR_R_MALLOC_FAILURE); + goto err; + } + break; + default: + X509V3err(X509V3_F_V2I_IPADDRBLOCKS, + X509V3_R_EXTENSION_VALUE_ERROR); + X509V3_conf_err(val); + goto err; + } + + OPENSSL_free(s); + s = NULL; + } + + /* + * Canonize the result, then we're done. + */ + if (!X509v3_addr_canonize(addr)) + goto err; + return addr; + + err: + OPENSSL_free(s); + sk_IPAddressFamily_pop_free(addr, IPAddressFamily_free); + return NULL; +} + +/* + * OpenSSL dispatch + */ +const X509V3_EXT_METHOD v3_addr = { + NID_sbgp_ipAddrBlock, /* nid */ + 0, /* flags */ + ASN1_ITEM_ref(IPAddrBlocks), /* template */ + 0, 0, 0, 0, /* old functions, ignored */ + 0, /* i2s */ + 0, /* s2i */ + 0, /* i2v */ + v2i_IPAddrBlocks, /* v2i */ + i2r_IPAddrBlocks, /* i2r */ + 0, /* r2i */ + NULL /* extension-specific data */ +}; + +/* + * Figure out whether extension sues inheritance. + */ +int X509v3_addr_inherits(IPAddrBlocks *addr) +{ + int i; + if (addr == NULL) + return 0; + for (i = 0; i < sk_IPAddressFamily_num(addr); i++) { + IPAddressFamily *f = sk_IPAddressFamily_value(addr, i); + if (f->ipAddressChoice->type == IPAddressChoice_inherit) + return 1; + } + return 0; +} + +/* + * Figure out whether parent contains child. + */ +static int addr_contains(IPAddressOrRanges *parent, + IPAddressOrRanges *child, int length) +{ + unsigned char p_min[ADDR_RAW_BUF_LEN], p_max[ADDR_RAW_BUF_LEN]; + unsigned char c_min[ADDR_RAW_BUF_LEN], c_max[ADDR_RAW_BUF_LEN]; + int p, c; + + if (child == NULL || parent == child) + return 1; + if (parent == NULL) + return 0; + + p = 0; + for (c = 0; c < sk_IPAddressOrRange_num(child); c++) { + if (!extract_min_max(sk_IPAddressOrRange_value(child, c), + c_min, c_max, length)) + return -1; + for (;; p++) { + if (p >= sk_IPAddressOrRange_num(parent)) + return 0; + if (!extract_min_max(sk_IPAddressOrRange_value(parent, p), + p_min, p_max, length)) + return 0; + if (memcmp(p_max, c_max, length) < 0) + continue; + if (memcmp(p_min, c_min, length) > 0) + return 0; + break; + } + } + + return 1; +} + +/* + * Test whether a is a subset of b. + */ +int X509v3_addr_subset(IPAddrBlocks *a, IPAddrBlocks *b) +{ + int i; + if (a == NULL || a == b) + return 1; + if (b == NULL || X509v3_addr_inherits(a) || X509v3_addr_inherits(b)) + return 0; + (void)sk_IPAddressFamily_set_cmp_func(b, IPAddressFamily_cmp); + for (i = 0; i < sk_IPAddressFamily_num(a); i++) { + IPAddressFamily *fa = sk_IPAddressFamily_value(a, i); + int j = sk_IPAddressFamily_find(b, fa); + IPAddressFamily *fb; + fb = sk_IPAddressFamily_value(b, j); + if (fb == NULL) + return 0; + if (!addr_contains(fb->ipAddressChoice->u.addressesOrRanges, + fa->ipAddressChoice->u.addressesOrRanges, + length_from_afi(X509v3_addr_get_afi(fb)))) + return 0; + } + return 1; +} + +/* + * Validation error handling via callback. + */ +#define validation_err(_err_) \ + do { \ + if (ctx != NULL) { \ + ctx->error = _err_; \ + ctx->error_depth = i; \ + ctx->current_cert = x; \ + ret = ctx->verify_cb(0, ctx); \ + } else { \ + ret = 0; \ + } \ + if (!ret) \ + goto done; \ + } while (0) + +/* + * Core code for RFC 3779 2.3 path validation. + * + * Returns 1 for success, 0 on error. + * + * When returning 0, ctx->error MUST be set to an appropriate value other than + * X509_V_OK. + */ +static int addr_validate_path_internal(X509_STORE_CTX *ctx, + STACK_OF(X509) *chain, + IPAddrBlocks *ext) +{ + IPAddrBlocks *child = NULL; + int i, j, ret = 1; + X509 *x; + + OPENSSL_assert(chain != NULL && sk_X509_num(chain) > 0); + OPENSSL_assert(ctx != NULL || ext != NULL); + OPENSSL_assert(ctx == NULL || ctx->verify_cb != NULL); + + /* + * Figure out where to start. If we don't have an extension to + * check, we're done. Otherwise, check canonical form and + * set up for walking up the chain. + */ + if (ext != NULL) { + i = -1; + x = NULL; + } else { + i = 0; + x = sk_X509_value(chain, i); + OPENSSL_assert(x != NULL); + if ((ext = x->rfc3779_addr) == NULL) + goto done; + } + if (!X509v3_addr_is_canonical(ext)) + validation_err(X509_V_ERR_INVALID_EXTENSION); + (void)sk_IPAddressFamily_set_cmp_func(ext, IPAddressFamily_cmp); + if ((child = sk_IPAddressFamily_dup(ext)) == NULL) { + X509V3err(X509V3_F_ADDR_VALIDATE_PATH_INTERNAL, + ERR_R_MALLOC_FAILURE); + ctx->error = X509_V_ERR_OUT_OF_MEM; + ret = 0; + goto done; + } + + /* + * Now walk up the chain. No cert may list resources that its + * parent doesn't list. + */ + for (i++; i < sk_X509_num(chain); i++) { + x = sk_X509_value(chain, i); + OPENSSL_assert(x != NULL); + if (!X509v3_addr_is_canonical(x->rfc3779_addr)) + validation_err(X509_V_ERR_INVALID_EXTENSION); + if (x->rfc3779_addr == NULL) { + for (j = 0; j < sk_IPAddressFamily_num(child); j++) { + IPAddressFamily *fc = sk_IPAddressFamily_value(child, j); + if (fc->ipAddressChoice->type != IPAddressChoice_inherit) { + validation_err(X509_V_ERR_UNNESTED_RESOURCE); + break; + } + } + continue; + } + (void)sk_IPAddressFamily_set_cmp_func(x->rfc3779_addr, + IPAddressFamily_cmp); + for (j = 0; j < sk_IPAddressFamily_num(child); j++) { + IPAddressFamily *fc = sk_IPAddressFamily_value(child, j); + int k = sk_IPAddressFamily_find(x->rfc3779_addr, fc); + IPAddressFamily *fp = + sk_IPAddressFamily_value(x->rfc3779_addr, k); + if (fp == NULL) { + if (fc->ipAddressChoice->type == + IPAddressChoice_addressesOrRanges) { + validation_err(X509_V_ERR_UNNESTED_RESOURCE); + break; + } + continue; + } + if (fp->ipAddressChoice->type == + IPAddressChoice_addressesOrRanges) { + if (fc->ipAddressChoice->type == IPAddressChoice_inherit + || addr_contains(fp->ipAddressChoice->u.addressesOrRanges, + fc->ipAddressChoice->u.addressesOrRanges, + length_from_afi(X509v3_addr_get_afi(fc)))) + sk_IPAddressFamily_set(child, j, fp); + else + validation_err(X509_V_ERR_UNNESTED_RESOURCE); + } + } + } + + /* + * Trust anchor can't inherit. + */ + OPENSSL_assert(x != NULL); + if (x->rfc3779_addr != NULL) { + for (j = 0; j < sk_IPAddressFamily_num(x->rfc3779_addr); j++) { + IPAddressFamily *fp = + sk_IPAddressFamily_value(x->rfc3779_addr, j); + if (fp->ipAddressChoice->type == IPAddressChoice_inherit + && sk_IPAddressFamily_find(child, fp) >= 0) + validation_err(X509_V_ERR_UNNESTED_RESOURCE); + } + } + + done: + sk_IPAddressFamily_free(child); + return ret; +} + +#undef validation_err + +/* + * RFC 3779 2.3 path validation -- called from X509_verify_cert(). + */ +int X509v3_addr_validate_path(X509_STORE_CTX *ctx) +{ + return addr_validate_path_internal(ctx, ctx->chain, NULL); +} + +/* + * RFC 3779 2.3 path validation of an extension. + * Test whether chain covers extension. + */ +int X509v3_addr_validate_resource_set(STACK_OF(X509) *chain, + IPAddrBlocks *ext, int allow_inheritance) +{ + if (ext == NULL) + return 1; + if (chain == NULL || sk_X509_num(chain) == 0) + return 0; + if (!allow_inheritance && X509v3_addr_inherits(ext)) + return 0; + return addr_validate_path_internal(NULL, chain, ext); +} + +#endif /* OPENSSL_NO_RFC3779 */ diff --git a/crypto/x509v3/v3_akey.c b/crypto/x509v3/v3_akey.c new file mode 100644 index 000000000..d9f770433 --- /dev/null +++ b/crypto/x509v3/v3_akey.c @@ -0,0 +1,160 @@ +/* + * Copyright 1999-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#include "internal/cryptlib.h" +#include +#include +#include +#include +#include "ext_dat.h" + +static STACK_OF(CONF_VALUE) *i2v_AUTHORITY_KEYID(X509V3_EXT_METHOD *method, + AUTHORITY_KEYID *akeyid, + STACK_OF(CONF_VALUE) + *extlist); +static AUTHORITY_KEYID *v2i_AUTHORITY_KEYID(X509V3_EXT_METHOD *method, + X509V3_CTX *ctx, + STACK_OF(CONF_VALUE) *values); + +const X509V3_EXT_METHOD v3_akey_id = { + NID_authority_key_identifier, + X509V3_EXT_MULTILINE, ASN1_ITEM_ref(AUTHORITY_KEYID), + 0, 0, 0, 0, + 0, 0, + (X509V3_EXT_I2V) i2v_AUTHORITY_KEYID, + (X509V3_EXT_V2I)v2i_AUTHORITY_KEYID, + 0, 0, + NULL +}; + +static STACK_OF(CONF_VALUE) *i2v_AUTHORITY_KEYID(X509V3_EXT_METHOD *method, + AUTHORITY_KEYID *akeyid, + STACK_OF(CONF_VALUE) + *extlist) +{ + char *tmp; + if (akeyid->keyid) { + tmp = OPENSSL_buf2hexstr(akeyid->keyid->data, akeyid->keyid->length); + X509V3_add_value("keyid", tmp, &extlist); + OPENSSL_free(tmp); + } + if (akeyid->issuer) + extlist = i2v_GENERAL_NAMES(NULL, akeyid->issuer, extlist); + if (akeyid->serial) { + tmp = OPENSSL_buf2hexstr(akeyid->serial->data, akeyid->serial->length); + X509V3_add_value("serial", tmp, &extlist); + OPENSSL_free(tmp); + } + return extlist; +} + +/*- + * Currently two options: + * keyid: use the issuers subject keyid, the value 'always' means its is + * an error if the issuer certificate doesn't have a key id. + * issuer: use the issuers cert issuer and serial number. The default is + * to only use this if keyid is not present. With the option 'always' + * this is always included. + */ + +static AUTHORITY_KEYID *v2i_AUTHORITY_KEYID(X509V3_EXT_METHOD *method, + X509V3_CTX *ctx, + STACK_OF(CONF_VALUE) *values) +{ + char keyid = 0, issuer = 0; + int i; + CONF_VALUE *cnf; + ASN1_OCTET_STRING *ikeyid = NULL; + X509_NAME *isname = NULL; + GENERAL_NAMES *gens = NULL; + GENERAL_NAME *gen = NULL; + ASN1_INTEGER *serial = NULL; + X509_EXTENSION *ext; + X509 *cert; + AUTHORITY_KEYID *akeyid; + + for (i = 0; i < sk_CONF_VALUE_num(values); i++) { + cnf = sk_CONF_VALUE_value(values, i); + if (strcmp(cnf->name, "keyid") == 0) { + keyid = 1; + if (cnf->value && strcmp(cnf->value, "always") == 0) + keyid = 2; + } else if (strcmp(cnf->name, "issuer") == 0) { + issuer = 1; + if (cnf->value && strcmp(cnf->value, "always") == 0) + issuer = 2; + } else { + X509V3err(X509V3_F_V2I_AUTHORITY_KEYID, X509V3_R_UNKNOWN_OPTION); + ERR_add_error_data(2, "name=", cnf->name); + return NULL; + } + } + + if (!ctx || !ctx->issuer_cert) { + if (ctx && (ctx->flags == CTX_TEST)) + return AUTHORITY_KEYID_new(); + X509V3err(X509V3_F_V2I_AUTHORITY_KEYID, + X509V3_R_NO_ISSUER_CERTIFICATE); + return NULL; + } + + cert = ctx->issuer_cert; + + if (keyid) { + i = X509_get_ext_by_NID(cert, NID_subject_key_identifier, -1); + if ((i >= 0) && (ext = X509_get_ext(cert, i))) + ikeyid = X509V3_EXT_d2i(ext); + if (keyid == 2 && !ikeyid) { + X509V3err(X509V3_F_V2I_AUTHORITY_KEYID, + X509V3_R_UNABLE_TO_GET_ISSUER_KEYID); + return NULL; + } + } + + if ((issuer && !ikeyid) || (issuer == 2)) { + isname = X509_NAME_dup(X509_get_issuer_name(cert)); + serial = ASN1_INTEGER_dup(X509_get_serialNumber(cert)); + if (!isname || !serial) { + X509V3err(X509V3_F_V2I_AUTHORITY_KEYID, + X509V3_R_UNABLE_TO_GET_ISSUER_DETAILS); + goto err; + } + } + + if ((akeyid = AUTHORITY_KEYID_new()) == NULL) + goto err; + + if (isname) { + if ((gens = sk_GENERAL_NAME_new_null()) == NULL + || (gen = GENERAL_NAME_new()) == NULL + || !sk_GENERAL_NAME_push(gens, gen)) { + X509V3err(X509V3_F_V2I_AUTHORITY_KEYID, ERR_R_MALLOC_FAILURE); + goto err; + } + gen->type = GEN_DIRNAME; + gen->d.dirn = isname; + } + + akeyid->issuer = gens; + gen = NULL; + gens = NULL; + akeyid->serial = serial; + akeyid->keyid = ikeyid; + + return akeyid; + + err: + sk_GENERAL_NAME_free(gens); + GENERAL_NAME_free(gen); + X509_NAME_free(isname); + ASN1_INTEGER_free(serial); + ASN1_OCTET_STRING_free(ikeyid); + return NULL; +} diff --git a/crypto/x509v3/v3_akeya.c b/crypto/x509v3/v3_akeya.c new file mode 100644 index 000000000..d6dd6bcb9 --- /dev/null +++ b/crypto/x509v3/v3_akeya.c @@ -0,0 +1,23 @@ +/* + * Copyright 2001-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#include "internal/cryptlib.h" +#include +#include +#include +#include + +ASN1_SEQUENCE(AUTHORITY_KEYID) = { + ASN1_IMP_OPT(AUTHORITY_KEYID, keyid, ASN1_OCTET_STRING, 0), + ASN1_IMP_SEQUENCE_OF_OPT(AUTHORITY_KEYID, issuer, GENERAL_NAME, 1), + ASN1_IMP_OPT(AUTHORITY_KEYID, serial, ASN1_INTEGER, 2) +} ASN1_SEQUENCE_END(AUTHORITY_KEYID) + +IMPLEMENT_ASN1_FUNCTIONS(AUTHORITY_KEYID) diff --git a/crypto/x509v3/v3_alt.c b/crypto/x509v3/v3_alt.c new file mode 100644 index 000000000..0364e336a --- /dev/null +++ b/crypto/x509v3/v3_alt.c @@ -0,0 +1,566 @@ +/* + * Copyright 1999-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#include "internal/cryptlib.h" +#include +#include +#include "ext_dat.h" + +static GENERAL_NAMES *v2i_subject_alt(X509V3_EXT_METHOD *method, + X509V3_CTX *ctx, + STACK_OF(CONF_VALUE) *nval); +static GENERAL_NAMES *v2i_issuer_alt(X509V3_EXT_METHOD *method, + X509V3_CTX *ctx, + STACK_OF(CONF_VALUE) *nval); +static int copy_email(X509V3_CTX *ctx, GENERAL_NAMES *gens, int move_p); +static int copy_issuer(X509V3_CTX *ctx, GENERAL_NAMES *gens); +static int do_othername(GENERAL_NAME *gen, const char *value, X509V3_CTX *ctx); +static int do_dirname(GENERAL_NAME *gen, const char *value, X509V3_CTX *ctx); + +const X509V3_EXT_METHOD v3_alt[3] = { + {NID_subject_alt_name, 0, ASN1_ITEM_ref(GENERAL_NAMES), + 0, 0, 0, 0, + 0, 0, + (X509V3_EXT_I2V) i2v_GENERAL_NAMES, + (X509V3_EXT_V2I)v2i_subject_alt, + NULL, NULL, NULL}, + + {NID_issuer_alt_name, 0, ASN1_ITEM_ref(GENERAL_NAMES), + 0, 0, 0, 0, + 0, 0, + (X509V3_EXT_I2V) i2v_GENERAL_NAMES, + (X509V3_EXT_V2I)v2i_issuer_alt, + NULL, NULL, NULL}, + + {NID_certificate_issuer, 0, ASN1_ITEM_ref(GENERAL_NAMES), + 0, 0, 0, 0, + 0, 0, + (X509V3_EXT_I2V) i2v_GENERAL_NAMES, + NULL, NULL, NULL, NULL}, +}; + +STACK_OF(CONF_VALUE) *i2v_GENERAL_NAMES(X509V3_EXT_METHOD *method, + GENERAL_NAMES *gens, + STACK_OF(CONF_VALUE) *ret) +{ + int i; + GENERAL_NAME *gen; + for (i = 0; i < sk_GENERAL_NAME_num(gens); i++) { + gen = sk_GENERAL_NAME_value(gens, i); + ret = i2v_GENERAL_NAME(method, gen, ret); + } + if (!ret) + return sk_CONF_VALUE_new_null(); + return ret; +} + +STACK_OF(CONF_VALUE) *i2v_GENERAL_NAME(X509V3_EXT_METHOD *method, + GENERAL_NAME *gen, + STACK_OF(CONF_VALUE) *ret) +{ + unsigned char *p; + char oline[256], htmp[5]; + int i; + switch (gen->type) { + case GEN_OTHERNAME: + X509V3_add_value("othername", "", &ret); + break; + + case GEN_X400: + X509V3_add_value("X400Name", "", &ret); + break; + + case GEN_EDIPARTY: + X509V3_add_value("EdiPartyName", "", &ret); + break; + + case GEN_EMAIL: + X509V3_add_value_uchar("email", gen->d.ia5->data, &ret); + break; + + case GEN_DNS: + X509V3_add_value_uchar("DNS", gen->d.ia5->data, &ret); + break; + + case GEN_URI: + X509V3_add_value_uchar("URI", gen->d.ia5->data, &ret); + break; + + case GEN_DIRNAME: + X509_NAME_oneline(gen->d.dirn, oline, 256); + X509V3_add_value("DirName", oline, &ret); + break; + + case GEN_IPADD: + p = gen->d.ip->data; + if (gen->d.ip->length == 4) + BIO_snprintf(oline, sizeof oline, + "%d.%d.%d.%d", p[0], p[1], p[2], p[3]); + else if (gen->d.ip->length == 16) { + oline[0] = 0; + for (i = 0; i < 8; i++) { + BIO_snprintf(htmp, sizeof htmp, "%X", p[0] << 8 | p[1]); + p += 2; + strcat(oline, htmp); + if (i != 7) + strcat(oline, ":"); + } + } else { + X509V3_add_value("IP Address", "", &ret); + break; + } + X509V3_add_value("IP Address", oline, &ret); + break; + + case GEN_RID: + i2t_ASN1_OBJECT(oline, 256, gen->d.rid); + X509V3_add_value("Registered ID", oline, &ret); + break; + } + return ret; +} + +int GENERAL_NAME_print(BIO *out, GENERAL_NAME *gen) +{ + unsigned char *p; + int i; + switch (gen->type) { + case GEN_OTHERNAME: + BIO_printf(out, "othername:"); + break; + + case GEN_X400: + BIO_printf(out, "X400Name:"); + break; + + case GEN_EDIPARTY: + /* Maybe fix this: it is supported now */ + BIO_printf(out, "EdiPartyName:"); + break; + + case GEN_EMAIL: + BIO_printf(out, "email:%s", gen->d.ia5->data); + break; + + case GEN_DNS: + BIO_printf(out, "DNS:%s", gen->d.ia5->data); + break; + + case GEN_URI: + BIO_printf(out, "URI:%s", gen->d.ia5->data); + break; + + case GEN_DIRNAME: + BIO_printf(out, "DirName:"); + X509_NAME_print_ex(out, gen->d.dirn, 0, XN_FLAG_ONELINE); + break; + + case GEN_IPADD: + p = gen->d.ip->data; + if (gen->d.ip->length == 4) + BIO_printf(out, "IP Address:%d.%d.%d.%d", p[0], p[1], p[2], p[3]); + else if (gen->d.ip->length == 16) { + BIO_printf(out, "IP Address"); + for (i = 0; i < 8; i++) { + BIO_printf(out, ":%X", p[0] << 8 | p[1]); + p += 2; + } + BIO_puts(out, "\n"); + } else { + BIO_printf(out, "IP Address:"); + break; + } + break; + + case GEN_RID: + BIO_printf(out, "Registered ID:"); + i2a_ASN1_OBJECT(out, gen->d.rid); + break; + } + return 1; +} + +static GENERAL_NAMES *v2i_issuer_alt(X509V3_EXT_METHOD *method, + X509V3_CTX *ctx, + STACK_OF(CONF_VALUE) *nval) +{ + GENERAL_NAMES *gens = NULL; + CONF_VALUE *cnf; + int i; + + if ((gens = sk_GENERAL_NAME_new_null()) == NULL) { + X509V3err(X509V3_F_V2I_ISSUER_ALT, ERR_R_MALLOC_FAILURE); + return NULL; + } + for (i = 0; i < sk_CONF_VALUE_num(nval); i++) { + cnf = sk_CONF_VALUE_value(nval, i); + if (!name_cmp(cnf->name, "issuer") + && cnf->value && strcmp(cnf->value, "copy") == 0) { + if (!copy_issuer(ctx, gens)) + goto err; + } else { + GENERAL_NAME *gen; + if ((gen = v2i_GENERAL_NAME(method, ctx, cnf)) == NULL) + goto err; + sk_GENERAL_NAME_push(gens, gen); + } + } + return gens; + err: + sk_GENERAL_NAME_pop_free(gens, GENERAL_NAME_free); + return NULL; +} + +/* Append subject altname of issuer to issuer alt name of subject */ + +static int copy_issuer(X509V3_CTX *ctx, GENERAL_NAMES *gens) +{ + GENERAL_NAMES *ialt; + GENERAL_NAME *gen; + X509_EXTENSION *ext; + int i; + + if (ctx && (ctx->flags == CTX_TEST)) + return 1; + if (!ctx || !ctx->issuer_cert) { + X509V3err(X509V3_F_COPY_ISSUER, X509V3_R_NO_ISSUER_DETAILS); + goto err; + } + i = X509_get_ext_by_NID(ctx->issuer_cert, NID_subject_alt_name, -1); + if (i < 0) + return 1; + if ((ext = X509_get_ext(ctx->issuer_cert, i)) == NULL + || (ialt = X509V3_EXT_d2i(ext)) == NULL) { + X509V3err(X509V3_F_COPY_ISSUER, X509V3_R_ISSUER_DECODE_ERROR); + goto err; + } + + for (i = 0; i < sk_GENERAL_NAME_num(ialt); i++) { + gen = sk_GENERAL_NAME_value(ialt, i); + if (!sk_GENERAL_NAME_push(gens, gen)) { + X509V3err(X509V3_F_COPY_ISSUER, ERR_R_MALLOC_FAILURE); + goto err; + } + } + sk_GENERAL_NAME_free(ialt); + + return 1; + + err: + return 0; + +} + +static GENERAL_NAMES *v2i_subject_alt(X509V3_EXT_METHOD *method, + X509V3_CTX *ctx, + STACK_OF(CONF_VALUE) *nval) +{ + GENERAL_NAMES *gens = NULL; + CONF_VALUE *cnf; + int i; + + if ((gens = sk_GENERAL_NAME_new_null()) == NULL) { + X509V3err(X509V3_F_V2I_SUBJECT_ALT, ERR_R_MALLOC_FAILURE); + return NULL; + } + for (i = 0; i < sk_CONF_VALUE_num(nval); i++) { + cnf = sk_CONF_VALUE_value(nval, i); + if (!name_cmp(cnf->name, "email") + && cnf->value && strcmp(cnf->value, "copy") == 0) { + if (!copy_email(ctx, gens, 0)) + goto err; + } else if (!name_cmp(cnf->name, "email") + && cnf->value && strcmp(cnf->value, "move") == 0) { + if (!copy_email(ctx, gens, 1)) + goto err; + } else { + GENERAL_NAME *gen; + if ((gen = v2i_GENERAL_NAME(method, ctx, cnf)) == NULL) + goto err; + sk_GENERAL_NAME_push(gens, gen); + } + } + return gens; + err: + sk_GENERAL_NAME_pop_free(gens, GENERAL_NAME_free); + return NULL; +} + +/* + * Copy any email addresses in a certificate or request to GENERAL_NAMES + */ + +static int copy_email(X509V3_CTX *ctx, GENERAL_NAMES *gens, int move_p) +{ + X509_NAME *nm; + ASN1_IA5STRING *email = NULL; + X509_NAME_ENTRY *ne; + GENERAL_NAME *gen = NULL; + int i; + if (ctx != NULL && ctx->flags == CTX_TEST) + return 1; + if (!ctx || (!ctx->subject_cert && !ctx->subject_req)) { + X509V3err(X509V3_F_COPY_EMAIL, X509V3_R_NO_SUBJECT_DETAILS); + goto err; + } + /* Find the subject name */ + if (ctx->subject_cert) + nm = X509_get_subject_name(ctx->subject_cert); + else + nm = X509_REQ_get_subject_name(ctx->subject_req); + + /* Now add any email address(es) to STACK */ + i = -1; + while ((i = X509_NAME_get_index_by_NID(nm, + NID_pkcs9_emailAddress, i)) >= 0) { + ne = X509_NAME_get_entry(nm, i); + email = ASN1_STRING_dup(X509_NAME_ENTRY_get_data(ne)); + if (move_p) { + X509_NAME_delete_entry(nm, i); + X509_NAME_ENTRY_free(ne); + i--; + } + if (email == NULL || (gen = GENERAL_NAME_new()) == NULL) { + X509V3err(X509V3_F_COPY_EMAIL, ERR_R_MALLOC_FAILURE); + goto err; + } + gen->d.ia5 = email; + email = NULL; + gen->type = GEN_EMAIL; + if (!sk_GENERAL_NAME_push(gens, gen)) { + X509V3err(X509V3_F_COPY_EMAIL, ERR_R_MALLOC_FAILURE); + goto err; + } + gen = NULL; + } + + return 1; + + err: + GENERAL_NAME_free(gen); + ASN1_IA5STRING_free(email); + return 0; + +} + +GENERAL_NAMES *v2i_GENERAL_NAMES(const X509V3_EXT_METHOD *method, + X509V3_CTX *ctx, STACK_OF(CONF_VALUE) *nval) +{ + GENERAL_NAME *gen; + GENERAL_NAMES *gens = NULL; + CONF_VALUE *cnf; + int i; + + if ((gens = sk_GENERAL_NAME_new_null()) == NULL) { + X509V3err(X509V3_F_V2I_GENERAL_NAMES, ERR_R_MALLOC_FAILURE); + return NULL; + } + for (i = 0; i < sk_CONF_VALUE_num(nval); i++) { + cnf = sk_CONF_VALUE_value(nval, i); + if ((gen = v2i_GENERAL_NAME(method, ctx, cnf)) == NULL) + goto err; + sk_GENERAL_NAME_push(gens, gen); + } + return gens; + err: + sk_GENERAL_NAME_pop_free(gens, GENERAL_NAME_free); + return NULL; +} + +GENERAL_NAME *v2i_GENERAL_NAME(const X509V3_EXT_METHOD *method, + X509V3_CTX *ctx, CONF_VALUE *cnf) +{ + return v2i_GENERAL_NAME_ex(NULL, method, ctx, cnf, 0); +} + +GENERAL_NAME *a2i_GENERAL_NAME(GENERAL_NAME *out, + const X509V3_EXT_METHOD *method, + X509V3_CTX *ctx, int gen_type, const char *value, + int is_nc) +{ + char is_string = 0; + GENERAL_NAME *gen = NULL; + + if (!value) { + X509V3err(X509V3_F_A2I_GENERAL_NAME, X509V3_R_MISSING_VALUE); + return NULL; + } + + if (out) + gen = out; + else { + gen = GENERAL_NAME_new(); + if (gen == NULL) { + X509V3err(X509V3_F_A2I_GENERAL_NAME, ERR_R_MALLOC_FAILURE); + return NULL; + } + } + + switch (gen_type) { + case GEN_URI: + case GEN_EMAIL: + case GEN_DNS: + is_string = 1; + break; + + case GEN_RID: + { + ASN1_OBJECT *obj; + if ((obj = OBJ_txt2obj(value, 0)) == NULL) { + X509V3err(X509V3_F_A2I_GENERAL_NAME, X509V3_R_BAD_OBJECT); + ERR_add_error_data(2, "value=", value); + goto err; + } + gen->d.rid = obj; + } + break; + + case GEN_IPADD: + if (is_nc) + gen->d.ip = a2i_IPADDRESS_NC(value); + else + gen->d.ip = a2i_IPADDRESS(value); + if (gen->d.ip == NULL) { + X509V3err(X509V3_F_A2I_GENERAL_NAME, X509V3_R_BAD_IP_ADDRESS); + ERR_add_error_data(2, "value=", value); + goto err; + } + break; + + case GEN_DIRNAME: + if (!do_dirname(gen, value, ctx)) { + X509V3err(X509V3_F_A2I_GENERAL_NAME, X509V3_R_DIRNAME_ERROR); + goto err; + } + break; + + case GEN_OTHERNAME: + if (!do_othername(gen, value, ctx)) { + X509V3err(X509V3_F_A2I_GENERAL_NAME, X509V3_R_OTHERNAME_ERROR); + goto err; + } + break; + default: + X509V3err(X509V3_F_A2I_GENERAL_NAME, X509V3_R_UNSUPPORTED_TYPE); + goto err; + } + + if (is_string) { + if ((gen->d.ia5 = ASN1_IA5STRING_new()) == NULL || + !ASN1_STRING_set(gen->d.ia5, (unsigned char *)value, + strlen(value))) { + X509V3err(X509V3_F_A2I_GENERAL_NAME, ERR_R_MALLOC_FAILURE); + goto err; + } + } + + gen->type = gen_type; + + return gen; + + err: + if (!out) + GENERAL_NAME_free(gen); + return NULL; +} + +GENERAL_NAME *v2i_GENERAL_NAME_ex(GENERAL_NAME *out, + const X509V3_EXT_METHOD *method, + X509V3_CTX *ctx, CONF_VALUE *cnf, int is_nc) +{ + int type; + + char *name, *value; + + name = cnf->name; + value = cnf->value; + + if (!value) { + X509V3err(X509V3_F_V2I_GENERAL_NAME_EX, X509V3_R_MISSING_VALUE); + return NULL; + } + + if (!name_cmp(name, "email")) + type = GEN_EMAIL; + else if (!name_cmp(name, "URI")) + type = GEN_URI; + else if (!name_cmp(name, "DNS")) + type = GEN_DNS; + else if (!name_cmp(name, "RID")) + type = GEN_RID; + else if (!name_cmp(name, "IP")) + type = GEN_IPADD; + else if (!name_cmp(name, "dirName")) + type = GEN_DIRNAME; + else if (!name_cmp(name, "otherName")) + type = GEN_OTHERNAME; + else { + X509V3err(X509V3_F_V2I_GENERAL_NAME_EX, X509V3_R_UNSUPPORTED_OPTION); + ERR_add_error_data(2, "name=", name); + return NULL; + } + + return a2i_GENERAL_NAME(out, method, ctx, type, value, is_nc); + +} + +static int do_othername(GENERAL_NAME *gen, const char *value, X509V3_CTX *ctx) +{ + char *objtmp = NULL, *p; + int objlen; + + if ((p = strchr(value, ';')) == NULL) + return 0; + if ((gen->d.otherName = OTHERNAME_new()) == NULL) + return 0; + /* + * Free this up because we will overwrite it. no need to free type_id + * because it is static + */ + ASN1_TYPE_free(gen->d.otherName->value); + if ((gen->d.otherName->value = ASN1_generate_v3(p + 1, ctx)) == NULL) + return 0; + objlen = p - value; + objtmp = OPENSSL_strndup(value, objlen); + if (objtmp == NULL) + return 0; + gen->d.otherName->type_id = OBJ_txt2obj(objtmp, 0); + OPENSSL_free(objtmp); + if (!gen->d.otherName->type_id) + return 0; + return 1; +} + +static int do_dirname(GENERAL_NAME *gen, const char *value, X509V3_CTX *ctx) +{ + int ret = 0; + STACK_OF(CONF_VALUE) *sk = NULL; + X509_NAME *nm; + + if ((nm = X509_NAME_new()) == NULL) + goto err; + sk = X509V3_get_section(ctx, value); + if (!sk) { + X509V3err(X509V3_F_DO_DIRNAME, X509V3_R_SECTION_NOT_FOUND); + ERR_add_error_data(2, "section=", value); + goto err; + } + /* FIXME: should allow other character types... */ + ret = X509V3_NAME_from_section(nm, sk, MBSTRING_ASC); + if (!ret) + goto err; + gen->d.dirn = nm; + +err: + if (ret == 0) + X509_NAME_free(nm); + X509V3_section_free(ctx, sk); + return ret; +} diff --git a/crypto/x509v3/v3_asid.c b/crypto/x509v3/v3_asid.c new file mode 100644 index 000000000..af4fcf4cd --- /dev/null +++ b/crypto/x509v3/v3_asid.c @@ -0,0 +1,852 @@ +/* + * Copyright 2006-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +/* + * Implementation of RFC 3779 section 3.2. + */ + +#include +#include +#include "internal/cryptlib.h" +#include +#include +#include +#include +#include +#include "internal/x509_int.h" +#include +#include "ext_dat.h" + +#ifndef OPENSSL_NO_RFC3779 + +/* + * OpenSSL ASN.1 template translation of RFC 3779 3.2.3. + */ + +ASN1_SEQUENCE(ASRange) = { + ASN1_SIMPLE(ASRange, min, ASN1_INTEGER), + ASN1_SIMPLE(ASRange, max, ASN1_INTEGER) +} ASN1_SEQUENCE_END(ASRange) + +ASN1_CHOICE(ASIdOrRange) = { + ASN1_SIMPLE(ASIdOrRange, u.id, ASN1_INTEGER), + ASN1_SIMPLE(ASIdOrRange, u.range, ASRange) +} ASN1_CHOICE_END(ASIdOrRange) + +ASN1_CHOICE(ASIdentifierChoice) = { + ASN1_SIMPLE(ASIdentifierChoice, u.inherit, ASN1_NULL), + ASN1_SEQUENCE_OF(ASIdentifierChoice, u.asIdsOrRanges, ASIdOrRange) +} ASN1_CHOICE_END(ASIdentifierChoice) + +ASN1_SEQUENCE(ASIdentifiers) = { + ASN1_EXP_OPT(ASIdentifiers, asnum, ASIdentifierChoice, 0), + ASN1_EXP_OPT(ASIdentifiers, rdi, ASIdentifierChoice, 1) +} ASN1_SEQUENCE_END(ASIdentifiers) + +IMPLEMENT_ASN1_FUNCTIONS(ASRange) +IMPLEMENT_ASN1_FUNCTIONS(ASIdOrRange) +IMPLEMENT_ASN1_FUNCTIONS(ASIdentifierChoice) +IMPLEMENT_ASN1_FUNCTIONS(ASIdentifiers) + +/* + * i2r method for an ASIdentifierChoice. + */ +static int i2r_ASIdentifierChoice(BIO *out, + ASIdentifierChoice *choice, + int indent, const char *msg) +{ + int i; + char *s; + if (choice == NULL) + return 1; + BIO_printf(out, "%*s%s:\n", indent, "", msg); + switch (choice->type) { + case ASIdentifierChoice_inherit: + BIO_printf(out, "%*sinherit\n", indent + 2, ""); + break; + case ASIdentifierChoice_asIdsOrRanges: + for (i = 0; i < sk_ASIdOrRange_num(choice->u.asIdsOrRanges); i++) { + ASIdOrRange *aor = + sk_ASIdOrRange_value(choice->u.asIdsOrRanges, i); + switch (aor->type) { + case ASIdOrRange_id: + if ((s = i2s_ASN1_INTEGER(NULL, aor->u.id)) == NULL) + return 0; + BIO_printf(out, "%*s%s\n", indent + 2, "", s); + OPENSSL_free(s); + break; + case ASIdOrRange_range: + if ((s = i2s_ASN1_INTEGER(NULL, aor->u.range->min)) == NULL) + return 0; + BIO_printf(out, "%*s%s-", indent + 2, "", s); + OPENSSL_free(s); + if ((s = i2s_ASN1_INTEGER(NULL, aor->u.range->max)) == NULL) + return 0; + BIO_printf(out, "%s\n", s); + OPENSSL_free(s); + break; + default: + return 0; + } + } + break; + default: + return 0; + } + return 1; +} + +/* + * i2r method for an ASIdentifier extension. + */ +static int i2r_ASIdentifiers(const X509V3_EXT_METHOD *method, + void *ext, BIO *out, int indent) +{ + ASIdentifiers *asid = ext; + return (i2r_ASIdentifierChoice(out, asid->asnum, indent, + "Autonomous System Numbers") && + i2r_ASIdentifierChoice(out, asid->rdi, indent, + "Routing Domain Identifiers")); +} + +/* + * Sort comparison function for a sequence of ASIdOrRange elements. + */ +static int ASIdOrRange_cmp(const ASIdOrRange *const *a_, + const ASIdOrRange *const *b_) +{ + const ASIdOrRange *a = *a_, *b = *b_; + + OPENSSL_assert((a->type == ASIdOrRange_id && a->u.id != NULL) || + (a->type == ASIdOrRange_range && a->u.range != NULL && + a->u.range->min != NULL && a->u.range->max != NULL)); + + OPENSSL_assert((b->type == ASIdOrRange_id && b->u.id != NULL) || + (b->type == ASIdOrRange_range && b->u.range != NULL && + b->u.range->min != NULL && b->u.range->max != NULL)); + + if (a->type == ASIdOrRange_id && b->type == ASIdOrRange_id) + return ASN1_INTEGER_cmp(a->u.id, b->u.id); + + if (a->type == ASIdOrRange_range && b->type == ASIdOrRange_range) { + int r = ASN1_INTEGER_cmp(a->u.range->min, b->u.range->min); + return r != 0 ? r : ASN1_INTEGER_cmp(a->u.range->max, + b->u.range->max); + } + + if (a->type == ASIdOrRange_id) + return ASN1_INTEGER_cmp(a->u.id, b->u.range->min); + else + return ASN1_INTEGER_cmp(a->u.range->min, b->u.id); +} + +/* + * Add an inherit element. + */ +int X509v3_asid_add_inherit(ASIdentifiers *asid, int which) +{ + ASIdentifierChoice **choice; + if (asid == NULL) + return 0; + switch (which) { + case V3_ASID_ASNUM: + choice = &asid->asnum; + break; + case V3_ASID_RDI: + choice = &asid->rdi; + break; + default: + return 0; + } + if (*choice == NULL) { + if ((*choice = ASIdentifierChoice_new()) == NULL) + return 0; + OPENSSL_assert((*choice)->u.inherit == NULL); + if (((*choice)->u.inherit = ASN1_NULL_new()) == NULL) + return 0; + (*choice)->type = ASIdentifierChoice_inherit; + } + return (*choice)->type == ASIdentifierChoice_inherit; +} + +/* + * Add an ID or range to an ASIdentifierChoice. + */ +int X509v3_asid_add_id_or_range(ASIdentifiers *asid, + int which, ASN1_INTEGER *min, ASN1_INTEGER *max) +{ + ASIdentifierChoice **choice; + ASIdOrRange *aor; + if (asid == NULL) + return 0; + switch (which) { + case V3_ASID_ASNUM: + choice = &asid->asnum; + break; + case V3_ASID_RDI: + choice = &asid->rdi; + break; + default: + return 0; + } + if (*choice != NULL && (*choice)->type == ASIdentifierChoice_inherit) + return 0; + if (*choice == NULL) { + if ((*choice = ASIdentifierChoice_new()) == NULL) + return 0; + OPENSSL_assert((*choice)->u.asIdsOrRanges == NULL); + (*choice)->u.asIdsOrRanges = sk_ASIdOrRange_new(ASIdOrRange_cmp); + if ((*choice)->u.asIdsOrRanges == NULL) + return 0; + (*choice)->type = ASIdentifierChoice_asIdsOrRanges; + } + if ((aor = ASIdOrRange_new()) == NULL) + return 0; + if (max == NULL) { + aor->type = ASIdOrRange_id; + aor->u.id = min; + } else { + aor->type = ASIdOrRange_range; + if ((aor->u.range = ASRange_new()) == NULL) + goto err; + ASN1_INTEGER_free(aor->u.range->min); + aor->u.range->min = min; + ASN1_INTEGER_free(aor->u.range->max); + aor->u.range->max = max; + } + if (!(sk_ASIdOrRange_push((*choice)->u.asIdsOrRanges, aor))) + goto err; + return 1; + + err: + ASIdOrRange_free(aor); + return 0; +} + +/* + * Extract min and max values from an ASIdOrRange. + */ +static void extract_min_max(ASIdOrRange *aor, + ASN1_INTEGER **min, ASN1_INTEGER **max) +{ + OPENSSL_assert(aor != NULL && min != NULL && max != NULL); + switch (aor->type) { + case ASIdOrRange_id: + *min = aor->u.id; + *max = aor->u.id; + return; + case ASIdOrRange_range: + *min = aor->u.range->min; + *max = aor->u.range->max; + return; + } +} + +/* + * Check whether an ASIdentifierChoice is in canonical form. + */ +static int ASIdentifierChoice_is_canonical(ASIdentifierChoice *choice) +{ + ASN1_INTEGER *a_max_plus_one = NULL; + BIGNUM *bn = NULL; + int i, ret = 0; + + /* + * Empty element or inheritance is canonical. + */ + if (choice == NULL || choice->type == ASIdentifierChoice_inherit) + return 1; + + /* + * If not a list, or if empty list, it's broken. + */ + if (choice->type != ASIdentifierChoice_asIdsOrRanges || + sk_ASIdOrRange_num(choice->u.asIdsOrRanges) == 0) + return 0; + + /* + * It's a list, check it. + */ + for (i = 0; i < sk_ASIdOrRange_num(choice->u.asIdsOrRanges) - 1; i++) { + ASIdOrRange *a = sk_ASIdOrRange_value(choice->u.asIdsOrRanges, i); + ASIdOrRange *b = sk_ASIdOrRange_value(choice->u.asIdsOrRanges, i + 1); + ASN1_INTEGER *a_min = NULL, *a_max = NULL, *b_min = NULL, *b_max = + NULL; + + extract_min_max(a, &a_min, &a_max); + extract_min_max(b, &b_min, &b_max); + + /* + * Punt misordered list, overlapping start, or inverted range. + */ + if (ASN1_INTEGER_cmp(a_min, b_min) >= 0 || + ASN1_INTEGER_cmp(a_min, a_max) > 0 || + ASN1_INTEGER_cmp(b_min, b_max) > 0) + goto done; + + /* + * Calculate a_max + 1 to check for adjacency. + */ + if ((bn == NULL && (bn = BN_new()) == NULL) || + ASN1_INTEGER_to_BN(a_max, bn) == NULL || + !BN_add_word(bn, 1) || + (a_max_plus_one = + BN_to_ASN1_INTEGER(bn, a_max_plus_one)) == NULL) { + X509V3err(X509V3_F_ASIDENTIFIERCHOICE_IS_CANONICAL, + ERR_R_MALLOC_FAILURE); + goto done; + } + + /* + * Punt if adjacent or overlapping. + */ + if (ASN1_INTEGER_cmp(a_max_plus_one, b_min) >= 0) + goto done; + } + + /* + * Check for inverted range. + */ + i = sk_ASIdOrRange_num(choice->u.asIdsOrRanges) - 1; + { + ASIdOrRange *a = sk_ASIdOrRange_value(choice->u.asIdsOrRanges, i); + ASN1_INTEGER *a_min, *a_max; + if (a != NULL && a->type == ASIdOrRange_range) { + extract_min_max(a, &a_min, &a_max); + if (ASN1_INTEGER_cmp(a_min, a_max) > 0) + goto done; + } + } + + ret = 1; + + done: + ASN1_INTEGER_free(a_max_plus_one); + BN_free(bn); + return ret; +} + +/* + * Check whether an ASIdentifier extension is in canonical form. + */ +int X509v3_asid_is_canonical(ASIdentifiers *asid) +{ + return (asid == NULL || + (ASIdentifierChoice_is_canonical(asid->asnum) && + ASIdentifierChoice_is_canonical(asid->rdi))); +} + +/* + * Whack an ASIdentifierChoice into canonical form. + */ +static int ASIdentifierChoice_canonize(ASIdentifierChoice *choice) +{ + ASN1_INTEGER *a_max_plus_one = NULL; + BIGNUM *bn = NULL; + int i, ret = 0; + + /* + * Nothing to do for empty element or inheritance. + */ + if (choice == NULL || choice->type == ASIdentifierChoice_inherit) + return 1; + + /* + * If not a list, or if empty list, it's broken. + */ + if (choice->type != ASIdentifierChoice_asIdsOrRanges || + sk_ASIdOrRange_num(choice->u.asIdsOrRanges) == 0) { + X509V3err(X509V3_F_ASIDENTIFIERCHOICE_CANONIZE, + X509V3_R_EXTENSION_VALUE_ERROR); + return 0; + } + + /* + * We have a non-empty list. Sort it. + */ + sk_ASIdOrRange_sort(choice->u.asIdsOrRanges); + + /* + * Now check for errors and suboptimal encoding, rejecting the + * former and fixing the latter. + */ + for (i = 0; i < sk_ASIdOrRange_num(choice->u.asIdsOrRanges) - 1; i++) { + ASIdOrRange *a = sk_ASIdOrRange_value(choice->u.asIdsOrRanges, i); + ASIdOrRange *b = sk_ASIdOrRange_value(choice->u.asIdsOrRanges, i + 1); + ASN1_INTEGER *a_min = NULL, *a_max = NULL, *b_min = NULL, *b_max = + NULL; + + extract_min_max(a, &a_min, &a_max); + extract_min_max(b, &b_min, &b_max); + + /* + * Make sure we're properly sorted (paranoia). + */ + OPENSSL_assert(ASN1_INTEGER_cmp(a_min, b_min) <= 0); + + /* + * Punt inverted ranges. + */ + if (ASN1_INTEGER_cmp(a_min, a_max) > 0 || + ASN1_INTEGER_cmp(b_min, b_max) > 0) + goto done; + + /* + * Check for overlaps. + */ + if (ASN1_INTEGER_cmp(a_max, b_min) >= 0) { + X509V3err(X509V3_F_ASIDENTIFIERCHOICE_CANONIZE, + X509V3_R_EXTENSION_VALUE_ERROR); + goto done; + } + + /* + * Calculate a_max + 1 to check for adjacency. + */ + if ((bn == NULL && (bn = BN_new()) == NULL) || + ASN1_INTEGER_to_BN(a_max, bn) == NULL || + !BN_add_word(bn, 1) || + (a_max_plus_one = + BN_to_ASN1_INTEGER(bn, a_max_plus_one)) == NULL) { + X509V3err(X509V3_F_ASIDENTIFIERCHOICE_CANONIZE, + ERR_R_MALLOC_FAILURE); + goto done; + } + + /* + * If a and b are adjacent, merge them. + */ + if (ASN1_INTEGER_cmp(a_max_plus_one, b_min) == 0) { + ASRange *r; + switch (a->type) { + case ASIdOrRange_id: + if ((r = OPENSSL_malloc(sizeof(*r))) == NULL) { + X509V3err(X509V3_F_ASIDENTIFIERCHOICE_CANONIZE, + ERR_R_MALLOC_FAILURE); + goto done; + } + r->min = a_min; + r->max = b_max; + a->type = ASIdOrRange_range; + a->u.range = r; + break; + case ASIdOrRange_range: + ASN1_INTEGER_free(a->u.range->max); + a->u.range->max = b_max; + break; + } + switch (b->type) { + case ASIdOrRange_id: + b->u.id = NULL; + break; + case ASIdOrRange_range: + b->u.range->max = NULL; + break; + } + ASIdOrRange_free(b); + (void)sk_ASIdOrRange_delete(choice->u.asIdsOrRanges, i + 1); + i--; + continue; + } + } + + /* + * Check for final inverted range. + */ + i = sk_ASIdOrRange_num(choice->u.asIdsOrRanges) - 1; + { + ASIdOrRange *a = sk_ASIdOrRange_value(choice->u.asIdsOrRanges, i); + ASN1_INTEGER *a_min, *a_max; + if (a != NULL && a->type == ASIdOrRange_range) { + extract_min_max(a, &a_min, &a_max); + if (ASN1_INTEGER_cmp(a_min, a_max) > 0) + goto done; + } + } + + OPENSSL_assert(ASIdentifierChoice_is_canonical(choice)); /* Paranoia */ + + ret = 1; + + done: + ASN1_INTEGER_free(a_max_plus_one); + BN_free(bn); + return ret; +} + +/* + * Whack an ASIdentifier extension into canonical form. + */ +int X509v3_asid_canonize(ASIdentifiers *asid) +{ + return (asid == NULL || + (ASIdentifierChoice_canonize(asid->asnum) && + ASIdentifierChoice_canonize(asid->rdi))); +} + +/* + * v2i method for an ASIdentifier extension. + */ +static void *v2i_ASIdentifiers(const struct v3_ext_method *method, + struct v3_ext_ctx *ctx, + STACK_OF(CONF_VALUE) *values) +{ + ASN1_INTEGER *min = NULL, *max = NULL; + ASIdentifiers *asid = NULL; + int i; + + if ((asid = ASIdentifiers_new()) == NULL) { + X509V3err(X509V3_F_V2I_ASIDENTIFIERS, ERR_R_MALLOC_FAILURE); + return NULL; + } + + for (i = 0; i < sk_CONF_VALUE_num(values); i++) { + CONF_VALUE *val = sk_CONF_VALUE_value(values, i); + int i1 = 0, i2 = 0, i3 = 0, is_range = 0, which = 0; + + /* + * Figure out whether this is an AS or an RDI. + */ + if (!name_cmp(val->name, "AS")) { + which = V3_ASID_ASNUM; + } else if (!name_cmp(val->name, "RDI")) { + which = V3_ASID_RDI; + } else { + X509V3err(X509V3_F_V2I_ASIDENTIFIERS, + X509V3_R_EXTENSION_NAME_ERROR); + X509V3_conf_err(val); + goto err; + } + + /* + * Handle inheritance. + */ + if (strcmp(val->value, "inherit") == 0) { + if (X509v3_asid_add_inherit(asid, which)) + continue; + X509V3err(X509V3_F_V2I_ASIDENTIFIERS, + X509V3_R_INVALID_INHERITANCE); + X509V3_conf_err(val); + goto err; + } + + /* + * Number, range, or mistake, pick it apart and figure out which. + */ + i1 = strspn(val->value, "0123456789"); + if (val->value[i1] == '\0') { + is_range = 0; + } else { + is_range = 1; + i2 = i1 + strspn(val->value + i1, " \t"); + if (val->value[i2] != '-') { + X509V3err(X509V3_F_V2I_ASIDENTIFIERS, + X509V3_R_INVALID_ASNUMBER); + X509V3_conf_err(val); + goto err; + } + i2++; + i2 = i2 + strspn(val->value + i2, " \t"); + i3 = i2 + strspn(val->value + i2, "0123456789"); + if (val->value[i3] != '\0') { + X509V3err(X509V3_F_V2I_ASIDENTIFIERS, + X509V3_R_INVALID_ASRANGE); + X509V3_conf_err(val); + goto err; + } + } + + /* + * Syntax is ok, read and add it. + */ + if (!is_range) { + if (!X509V3_get_value_int(val, &min)) { + X509V3err(X509V3_F_V2I_ASIDENTIFIERS, ERR_R_MALLOC_FAILURE); + goto err; + } + } else { + char *s = OPENSSL_strdup(val->value); + if (s == NULL) { + X509V3err(X509V3_F_V2I_ASIDENTIFIERS, ERR_R_MALLOC_FAILURE); + goto err; + } + s[i1] = '\0'; + min = s2i_ASN1_INTEGER(NULL, s); + max = s2i_ASN1_INTEGER(NULL, s + i2); + OPENSSL_free(s); + if (min == NULL || max == NULL) { + X509V3err(X509V3_F_V2I_ASIDENTIFIERS, ERR_R_MALLOC_FAILURE); + goto err; + } + if (ASN1_INTEGER_cmp(min, max) > 0) { + X509V3err(X509V3_F_V2I_ASIDENTIFIERS, + X509V3_R_EXTENSION_VALUE_ERROR); + goto err; + } + } + if (!X509v3_asid_add_id_or_range(asid, which, min, max)) { + X509V3err(X509V3_F_V2I_ASIDENTIFIERS, ERR_R_MALLOC_FAILURE); + goto err; + } + min = max = NULL; + } + + /* + * Canonize the result, then we're done. + */ + if (!X509v3_asid_canonize(asid)) + goto err; + return asid; + + err: + ASIdentifiers_free(asid); + ASN1_INTEGER_free(min); + ASN1_INTEGER_free(max); + return NULL; +} + +/* + * OpenSSL dispatch. + */ +const X509V3_EXT_METHOD v3_asid = { + NID_sbgp_autonomousSysNum, /* nid */ + 0, /* flags */ + ASN1_ITEM_ref(ASIdentifiers), /* template */ + 0, 0, 0, 0, /* old functions, ignored */ + 0, /* i2s */ + 0, /* s2i */ + 0, /* i2v */ + v2i_ASIdentifiers, /* v2i */ + i2r_ASIdentifiers, /* i2r */ + 0, /* r2i */ + NULL /* extension-specific data */ +}; + +/* + * Figure out whether extension uses inheritance. + */ +int X509v3_asid_inherits(ASIdentifiers *asid) +{ + return (asid != NULL && + ((asid->asnum != NULL && + asid->asnum->type == ASIdentifierChoice_inherit) || + (asid->rdi != NULL && + asid->rdi->type == ASIdentifierChoice_inherit))); +} + +/* + * Figure out whether parent contains child. + */ +static int asid_contains(ASIdOrRanges *parent, ASIdOrRanges *child) +{ + ASN1_INTEGER *p_min = NULL, *p_max = NULL, *c_min = NULL, *c_max = NULL; + int p, c; + + if (child == NULL || parent == child) + return 1; + if (parent == NULL) + return 0; + + p = 0; + for (c = 0; c < sk_ASIdOrRange_num(child); c++) { + extract_min_max(sk_ASIdOrRange_value(child, c), &c_min, &c_max); + for (;; p++) { + if (p >= sk_ASIdOrRange_num(parent)) + return 0; + extract_min_max(sk_ASIdOrRange_value(parent, p), &p_min, &p_max); + if (ASN1_INTEGER_cmp(p_max, c_max) < 0) + continue; + if (ASN1_INTEGER_cmp(p_min, c_min) > 0) + return 0; + break; + } + } + + return 1; +} + +/* + * Test whether a is a subset of b. + */ +int X509v3_asid_subset(ASIdentifiers *a, ASIdentifiers *b) +{ + return (a == NULL || + a == b || + (b != NULL && + !X509v3_asid_inherits(a) && + !X509v3_asid_inherits(b) && + asid_contains(b->asnum->u.asIdsOrRanges, + a->asnum->u.asIdsOrRanges) && + asid_contains(b->rdi->u.asIdsOrRanges, + a->rdi->u.asIdsOrRanges))); +} + +/* + * Validation error handling via callback. + */ +#define validation_err(_err_) \ + do { \ + if (ctx != NULL) { \ + ctx->error = _err_; \ + ctx->error_depth = i; \ + ctx->current_cert = x; \ + ret = ctx->verify_cb(0, ctx); \ + } else { \ + ret = 0; \ + } \ + if (!ret) \ + goto done; \ + } while (0) + +/* + * Core code for RFC 3779 3.3 path validation. + */ +static int asid_validate_path_internal(X509_STORE_CTX *ctx, + STACK_OF(X509) *chain, + ASIdentifiers *ext) +{ + ASIdOrRanges *child_as = NULL, *child_rdi = NULL; + int i, ret = 1, inherit_as = 0, inherit_rdi = 0; + X509 *x; + + OPENSSL_assert(chain != NULL && sk_X509_num(chain) > 0); + OPENSSL_assert(ctx != NULL || ext != NULL); + OPENSSL_assert(ctx == NULL || ctx->verify_cb != NULL); + + /* + * Figure out where to start. If we don't have an extension to + * check, we're done. Otherwise, check canonical form and + * set up for walking up the chain. + */ + if (ext != NULL) { + i = -1; + x = NULL; + } else { + i = 0; + x = sk_X509_value(chain, i); + OPENSSL_assert(x != NULL); + if ((ext = x->rfc3779_asid) == NULL) + goto done; + } + if (!X509v3_asid_is_canonical(ext)) + validation_err(X509_V_ERR_INVALID_EXTENSION); + if (ext->asnum != NULL) { + switch (ext->asnum->type) { + case ASIdentifierChoice_inherit: + inherit_as = 1; + break; + case ASIdentifierChoice_asIdsOrRanges: + child_as = ext->asnum->u.asIdsOrRanges; + break; + } + } + if (ext->rdi != NULL) { + switch (ext->rdi->type) { + case ASIdentifierChoice_inherit: + inherit_rdi = 1; + break; + case ASIdentifierChoice_asIdsOrRanges: + child_rdi = ext->rdi->u.asIdsOrRanges; + break; + } + } + + /* + * Now walk up the chain. Extensions must be in canonical form, no + * cert may list resources that its parent doesn't list. + */ + for (i++; i < sk_X509_num(chain); i++) { + x = sk_X509_value(chain, i); + OPENSSL_assert(x != NULL); + if (x->rfc3779_asid == NULL) { + if (child_as != NULL || child_rdi != NULL) + validation_err(X509_V_ERR_UNNESTED_RESOURCE); + continue; + } + if (!X509v3_asid_is_canonical(x->rfc3779_asid)) + validation_err(X509_V_ERR_INVALID_EXTENSION); + if (x->rfc3779_asid->asnum == NULL && child_as != NULL) { + validation_err(X509_V_ERR_UNNESTED_RESOURCE); + child_as = NULL; + inherit_as = 0; + } + if (x->rfc3779_asid->asnum != NULL && + x->rfc3779_asid->asnum->type == + ASIdentifierChoice_asIdsOrRanges) { + if (inherit_as + || asid_contains(x->rfc3779_asid->asnum->u.asIdsOrRanges, + child_as)) { + child_as = x->rfc3779_asid->asnum->u.asIdsOrRanges; + inherit_as = 0; + } else { + validation_err(X509_V_ERR_UNNESTED_RESOURCE); + } + } + if (x->rfc3779_asid->rdi == NULL && child_rdi != NULL) { + validation_err(X509_V_ERR_UNNESTED_RESOURCE); + child_rdi = NULL; + inherit_rdi = 0; + } + if (x->rfc3779_asid->rdi != NULL && + x->rfc3779_asid->rdi->type == ASIdentifierChoice_asIdsOrRanges) { + if (inherit_rdi || + asid_contains(x->rfc3779_asid->rdi->u.asIdsOrRanges, + child_rdi)) { + child_rdi = x->rfc3779_asid->rdi->u.asIdsOrRanges; + inherit_rdi = 0; + } else { + validation_err(X509_V_ERR_UNNESTED_RESOURCE); + } + } + } + + /* + * Trust anchor can't inherit. + */ + OPENSSL_assert(x != NULL); + if (x->rfc3779_asid != NULL) { + if (x->rfc3779_asid->asnum != NULL && + x->rfc3779_asid->asnum->type == ASIdentifierChoice_inherit) + validation_err(X509_V_ERR_UNNESTED_RESOURCE); + if (x->rfc3779_asid->rdi != NULL && + x->rfc3779_asid->rdi->type == ASIdentifierChoice_inherit) + validation_err(X509_V_ERR_UNNESTED_RESOURCE); + } + + done: + return ret; +} + +#undef validation_err + +/* + * RFC 3779 3.3 path validation -- called from X509_verify_cert(). + */ +int X509v3_asid_validate_path(X509_STORE_CTX *ctx) +{ + return asid_validate_path_internal(ctx, ctx->chain, NULL); +} + +/* + * RFC 3779 3.3 path validation of an extension. + * Test whether chain covers extension. + */ +int X509v3_asid_validate_resource_set(STACK_OF(X509) *chain, + ASIdentifiers *ext, int allow_inheritance) +{ + if (ext == NULL) + return 1; + if (chain == NULL || sk_X509_num(chain) == 0) + return 0; + if (!allow_inheritance && X509v3_asid_inherits(ext)) + return 0; + return asid_validate_path_internal(NULL, chain, ext); +} + +#endif /* OPENSSL_NO_RFC3779 */ diff --git a/crypto/x509v3/v3_bcons.c b/crypto/x509v3/v3_bcons.c new file mode 100644 index 000000000..3bbf15550 --- /dev/null +++ b/crypto/x509v3/v3_bcons.c @@ -0,0 +1,84 @@ +/* + * Copyright 1999-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#include "internal/cryptlib.h" +#include +#include +#include +#include +#include "ext_dat.h" + +static STACK_OF(CONF_VALUE) *i2v_BASIC_CONSTRAINTS(X509V3_EXT_METHOD *method, + BASIC_CONSTRAINTS *bcons, + STACK_OF(CONF_VALUE) + *extlist); +static BASIC_CONSTRAINTS *v2i_BASIC_CONSTRAINTS(X509V3_EXT_METHOD *method, + X509V3_CTX *ctx, + STACK_OF(CONF_VALUE) *values); + +const X509V3_EXT_METHOD v3_bcons = { + NID_basic_constraints, 0, + ASN1_ITEM_ref(BASIC_CONSTRAINTS), + 0, 0, 0, 0, + 0, 0, + (X509V3_EXT_I2V) i2v_BASIC_CONSTRAINTS, + (X509V3_EXT_V2I)v2i_BASIC_CONSTRAINTS, + NULL, NULL, + NULL +}; + +ASN1_SEQUENCE(BASIC_CONSTRAINTS) = { + ASN1_OPT(BASIC_CONSTRAINTS, ca, ASN1_FBOOLEAN), + ASN1_OPT(BASIC_CONSTRAINTS, pathlen, ASN1_INTEGER) +} ASN1_SEQUENCE_END(BASIC_CONSTRAINTS) + +IMPLEMENT_ASN1_FUNCTIONS(BASIC_CONSTRAINTS) + +static STACK_OF(CONF_VALUE) *i2v_BASIC_CONSTRAINTS(X509V3_EXT_METHOD *method, + BASIC_CONSTRAINTS *bcons, + STACK_OF(CONF_VALUE) + *extlist) +{ + X509V3_add_value_bool("CA", bcons->ca, &extlist); + X509V3_add_value_int("pathlen", bcons->pathlen, &extlist); + return extlist; +} + +static BASIC_CONSTRAINTS *v2i_BASIC_CONSTRAINTS(X509V3_EXT_METHOD *method, + X509V3_CTX *ctx, + STACK_OF(CONF_VALUE) *values) +{ + BASIC_CONSTRAINTS *bcons = NULL; + CONF_VALUE *val; + int i; + + if ((bcons = BASIC_CONSTRAINTS_new()) == NULL) { + X509V3err(X509V3_F_V2I_BASIC_CONSTRAINTS, ERR_R_MALLOC_FAILURE); + return NULL; + } + for (i = 0; i < sk_CONF_VALUE_num(values); i++) { + val = sk_CONF_VALUE_value(values, i); + if (strcmp(val->name, "CA") == 0) { + if (!X509V3_get_value_bool(val, &bcons->ca)) + goto err; + } else if (strcmp(val->name, "pathlen") == 0) { + if (!X509V3_get_value_int(val, &bcons->pathlen)) + goto err; + } else { + X509V3err(X509V3_F_V2I_BASIC_CONSTRAINTS, X509V3_R_INVALID_NAME); + X509V3_conf_err(val); + goto err; + } + } + return bcons; + err: + BASIC_CONSTRAINTS_free(bcons); + return NULL; +} diff --git a/crypto/x509v3/v3_bitst.c b/crypto/x509v3/v3_bitst.c new file mode 100644 index 000000000..4802116ba --- /dev/null +++ b/crypto/x509v3/v3_bitst.c @@ -0,0 +1,93 @@ +/* + * Copyright 1999-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#include "internal/cryptlib.h" +#include +#include +#include "ext_dat.h" + +static BIT_STRING_BITNAME ns_cert_type_table[] = { + {0, "SSL Client", "client"}, + {1, "SSL Server", "server"}, + {2, "S/MIME", "email"}, + {3, "Object Signing", "objsign"}, + {4, "Unused", "reserved"}, + {5, "SSL CA", "sslCA"}, + {6, "S/MIME CA", "emailCA"}, + {7, "Object Signing CA", "objCA"}, + {-1, NULL, NULL} +}; + +static BIT_STRING_BITNAME key_usage_type_table[] = { + {0, "Digital Signature", "digitalSignature"}, + {1, "Non Repudiation", "nonRepudiation"}, + {2, "Key Encipherment", "keyEncipherment"}, + {3, "Data Encipherment", "dataEncipherment"}, + {4, "Key Agreement", "keyAgreement"}, + {5, "Certificate Sign", "keyCertSign"}, + {6, "CRL Sign", "cRLSign"}, + {7, "Encipher Only", "encipherOnly"}, + {8, "Decipher Only", "decipherOnly"}, + {-1, NULL, NULL} +}; + +const X509V3_EXT_METHOD v3_nscert = +EXT_BITSTRING(NID_netscape_cert_type, ns_cert_type_table); +const X509V3_EXT_METHOD v3_key_usage = +EXT_BITSTRING(NID_key_usage, key_usage_type_table); + +STACK_OF(CONF_VALUE) *i2v_ASN1_BIT_STRING(X509V3_EXT_METHOD *method, + ASN1_BIT_STRING *bits, + STACK_OF(CONF_VALUE) *ret) +{ + BIT_STRING_BITNAME *bnam; + for (bnam = method->usr_data; bnam->lname; bnam++) { + if (ASN1_BIT_STRING_get_bit(bits, bnam->bitnum)) + X509V3_add_value(bnam->lname, NULL, &ret); + } + return ret; +} + +ASN1_BIT_STRING *v2i_ASN1_BIT_STRING(X509V3_EXT_METHOD *method, + X509V3_CTX *ctx, + STACK_OF(CONF_VALUE) *nval) +{ + CONF_VALUE *val; + ASN1_BIT_STRING *bs; + int i; + BIT_STRING_BITNAME *bnam; + if ((bs = ASN1_BIT_STRING_new()) == NULL) { + X509V3err(X509V3_F_V2I_ASN1_BIT_STRING, ERR_R_MALLOC_FAILURE); + return NULL; + } + for (i = 0; i < sk_CONF_VALUE_num(nval); i++) { + val = sk_CONF_VALUE_value(nval, i); + for (bnam = method->usr_data; bnam->lname; bnam++) { + if (strcmp(bnam->sname, val->name) == 0 + || strcmp(bnam->lname, val->name) == 0) { + if (!ASN1_BIT_STRING_set_bit(bs, bnam->bitnum, 1)) { + X509V3err(X509V3_F_V2I_ASN1_BIT_STRING, + ERR_R_MALLOC_FAILURE); + ASN1_BIT_STRING_free(bs); + return NULL; + } + break; + } + } + if (!bnam->lname) { + X509V3err(X509V3_F_V2I_ASN1_BIT_STRING, + X509V3_R_UNKNOWN_BIT_STRING_ARGUMENT); + X509V3_conf_err(val); + ASN1_BIT_STRING_free(bs); + return NULL; + } + } + return bs; +} diff --git a/crypto/x509v3/v3_conf.c b/crypto/x509v3/v3_conf.c new file mode 100644 index 000000000..f625ff542 --- /dev/null +++ b/crypto/x509v3/v3_conf.c @@ -0,0 +1,507 @@ +/* + * Copyright 1999-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +/* extension creation utilities */ + +#include +#include +#include "internal/cryptlib.h" +#include +#include +#include "internal/x509_int.h" +#include + +static int v3_check_critical(const char **value); +static int v3_check_generic(const char **value); +static X509_EXTENSION *do_ext_nconf(CONF *conf, X509V3_CTX *ctx, int ext_nid, + int crit, const char *value); +static X509_EXTENSION *v3_generic_extension(const char *ext, const char *value, + int crit, int type, + X509V3_CTX *ctx); +static char *conf_lhash_get_string(void *db, const char *section, const char *value); +static STACK_OF(CONF_VALUE) *conf_lhash_get_section(void *db, const char *section); +static X509_EXTENSION *do_ext_i2d(const X509V3_EXT_METHOD *method, + int ext_nid, int crit, void *ext_struc); +static unsigned char *generic_asn1(const char *value, X509V3_CTX *ctx, + long *ext_len); +/* CONF *conf: Config file */ +/* char *name: Name */ +/* char *value: Value */ +X509_EXTENSION *X509V3_EXT_nconf(CONF *conf, X509V3_CTX *ctx, const char *name, + const char *value) +{ + int crit; + int ext_type; + X509_EXTENSION *ret; + crit = v3_check_critical(&value); + if ((ext_type = v3_check_generic(&value))) + return v3_generic_extension(name, value, crit, ext_type, ctx); + ret = do_ext_nconf(conf, ctx, OBJ_sn2nid(name), crit, value); + if (!ret) { + X509V3err(X509V3_F_X509V3_EXT_NCONF, X509V3_R_ERROR_IN_EXTENSION); + ERR_add_error_data(4, "name=", name, ", value=", value); + } + return ret; +} + +/* CONF *conf: Config file */ +/* char *value: Value */ +X509_EXTENSION *X509V3_EXT_nconf_nid(CONF *conf, X509V3_CTX *ctx, int ext_nid, + const char *value) +{ + int crit; + int ext_type; + crit = v3_check_critical(&value); + if ((ext_type = v3_check_generic(&value))) + return v3_generic_extension(OBJ_nid2sn(ext_nid), + value, crit, ext_type, ctx); + return do_ext_nconf(conf, ctx, ext_nid, crit, value); +} + +/* CONF *conf: Config file */ +/* char *value: Value */ +static X509_EXTENSION *do_ext_nconf(CONF *conf, X509V3_CTX *ctx, int ext_nid, + int crit, const char *value) +{ + const X509V3_EXT_METHOD *method; + X509_EXTENSION *ext; + STACK_OF(CONF_VALUE) *nval; + void *ext_struc; + + if (ext_nid == NID_undef) { + X509V3err(X509V3_F_DO_EXT_NCONF, X509V3_R_UNKNOWN_EXTENSION_NAME); + return NULL; + } + if ((method = X509V3_EXT_get_nid(ext_nid)) == NULL) { + X509V3err(X509V3_F_DO_EXT_NCONF, X509V3_R_UNKNOWN_EXTENSION); + return NULL; + } + /* Now get internal extension representation based on type */ + if (method->v2i) { + if (*value == '@') + nval = NCONF_get_section(conf, value + 1); + else + nval = X509V3_parse_list(value); + if (nval == NULL || sk_CONF_VALUE_num(nval) <= 0) { + X509V3err(X509V3_F_DO_EXT_NCONF, + X509V3_R_INVALID_EXTENSION_STRING); + ERR_add_error_data(4, "name=", OBJ_nid2sn(ext_nid), ",section=", + value); + if (*value != '@') + sk_CONF_VALUE_pop_free(nval, X509V3_conf_free); + return NULL; + } + ext_struc = method->v2i(method, ctx, nval); + if (*value != '@') + sk_CONF_VALUE_pop_free(nval, X509V3_conf_free); + if (!ext_struc) + return NULL; + } else if (method->s2i) { + if ((ext_struc = method->s2i(method, ctx, value)) == NULL) + return NULL; + } else if (method->r2i) { + if (!ctx->db || !ctx->db_meth) { + X509V3err(X509V3_F_DO_EXT_NCONF, X509V3_R_NO_CONFIG_DATABASE); + return NULL; + } + if ((ext_struc = method->r2i(method, ctx, value)) == NULL) + return NULL; + } else { + X509V3err(X509V3_F_DO_EXT_NCONF, + X509V3_R_EXTENSION_SETTING_NOT_SUPPORTED); + ERR_add_error_data(2, "name=", OBJ_nid2sn(ext_nid)); + return NULL; + } + + ext = do_ext_i2d(method, ext_nid, crit, ext_struc); + if (method->it) + ASN1_item_free(ext_struc, ASN1_ITEM_ptr(method->it)); + else + method->ext_free(ext_struc); + return ext; + +} + +static X509_EXTENSION *do_ext_i2d(const X509V3_EXT_METHOD *method, + int ext_nid, int crit, void *ext_struc) +{ + unsigned char *ext_der = NULL; + int ext_len; + ASN1_OCTET_STRING *ext_oct = NULL; + X509_EXTENSION *ext; + /* Convert internal representation to DER */ + if (method->it) { + ext_der = NULL; + ext_len = + ASN1_item_i2d(ext_struc, &ext_der, ASN1_ITEM_ptr(method->it)); + if (ext_len < 0) + goto merr; + } else { + unsigned char *p; + + ext_len = method->i2d(ext_struc, NULL); + if ((ext_der = OPENSSL_malloc(ext_len)) == NULL) + goto merr; + p = ext_der; + method->i2d(ext_struc, &p); + } + if ((ext_oct = ASN1_OCTET_STRING_new()) == NULL) + goto merr; + ext_oct->data = ext_der; + ext_der = NULL; + ext_oct->length = ext_len; + + ext = X509_EXTENSION_create_by_NID(NULL, ext_nid, crit, ext_oct); + if (!ext) + goto merr; + ASN1_OCTET_STRING_free(ext_oct); + + return ext; + + merr: + X509V3err(X509V3_F_DO_EXT_I2D, ERR_R_MALLOC_FAILURE); + OPENSSL_free(ext_der); + ASN1_OCTET_STRING_free(ext_oct); + return NULL; + +} + +/* Given an internal structure, nid and critical flag create an extension */ + +X509_EXTENSION *X509V3_EXT_i2d(int ext_nid, int crit, void *ext_struc) +{ + const X509V3_EXT_METHOD *method; + + if ((method = X509V3_EXT_get_nid(ext_nid)) == NULL) { + X509V3err(X509V3_F_X509V3_EXT_I2D, X509V3_R_UNKNOWN_EXTENSION); + return NULL; + } + return do_ext_i2d(method, ext_nid, crit, ext_struc); +} + +/* Check the extension string for critical flag */ +static int v3_check_critical(const char **value) +{ + const char *p = *value; + if ((strlen(p) < 9) || strncmp(p, "critical,", 9)) + return 0; + p += 9; + while (isspace((unsigned char)*p)) + p++; + *value = p; + return 1; +} + +/* Check extension string for generic extension and return the type */ +static int v3_check_generic(const char **value) +{ + int gen_type = 0; + const char *p = *value; + if ((strlen(p) >= 4) && strncmp(p, "DER:", 4) == 0) { + p += 4; + gen_type = 1; + } else if ((strlen(p) >= 5) && strncmp(p, "ASN1:", 5) == 0) { + p += 5; + gen_type = 2; + } else + return 0; + + while (isspace((unsigned char)*p)) + p++; + *value = p; + return gen_type; +} + +/* Create a generic extension: for now just handle DER type */ +static X509_EXTENSION *v3_generic_extension(const char *ext, const char *value, + int crit, int gen_type, + X509V3_CTX *ctx) +{ + unsigned char *ext_der = NULL; + long ext_len = 0; + ASN1_OBJECT *obj = NULL; + ASN1_OCTET_STRING *oct = NULL; + X509_EXTENSION *extension = NULL; + + if ((obj = OBJ_txt2obj(ext, 0)) == NULL) { + X509V3err(X509V3_F_V3_GENERIC_EXTENSION, + X509V3_R_EXTENSION_NAME_ERROR); + ERR_add_error_data(2, "name=", ext); + goto err; + } + + if (gen_type == 1) + ext_der = OPENSSL_hexstr2buf(value, &ext_len); + else if (gen_type == 2) + ext_der = generic_asn1(value, ctx, &ext_len); + + if (ext_der == NULL) { + X509V3err(X509V3_F_V3_GENERIC_EXTENSION, + X509V3_R_EXTENSION_VALUE_ERROR); + ERR_add_error_data(2, "value=", value); + goto err; + } + + if ((oct = ASN1_OCTET_STRING_new()) == NULL) { + X509V3err(X509V3_F_V3_GENERIC_EXTENSION, ERR_R_MALLOC_FAILURE); + goto err; + } + + oct->data = ext_der; + oct->length = ext_len; + ext_der = NULL; + + extension = X509_EXTENSION_create_by_OBJ(NULL, obj, crit, oct); + + err: + ASN1_OBJECT_free(obj); + ASN1_OCTET_STRING_free(oct); + OPENSSL_free(ext_der); + return extension; + +} + +static unsigned char *generic_asn1(const char *value, X509V3_CTX *ctx, + long *ext_len) +{ + ASN1_TYPE *typ; + unsigned char *ext_der = NULL; + typ = ASN1_generate_v3(value, ctx); + if (typ == NULL) + return NULL; + *ext_len = i2d_ASN1_TYPE(typ, &ext_der); + ASN1_TYPE_free(typ); + return ext_der; +} + +static void delete_ext(STACK_OF(X509_EXTENSION) *sk, X509_EXTENSION *dext) +{ + int idx; + ASN1_OBJECT *obj; + obj = X509_EXTENSION_get_object(dext); + while ((idx = X509v3_get_ext_by_OBJ(sk, obj, -1)) >= 0) { + X509_EXTENSION *tmpext = X509v3_get_ext(sk, idx); + X509v3_delete_ext(sk, idx); + X509_EXTENSION_free(tmpext); + } +} + +/* + * This is the main function: add a bunch of extensions based on a config + * file section to an extension STACK. + */ + +int X509V3_EXT_add_nconf_sk(CONF *conf, X509V3_CTX *ctx, const char *section, + STACK_OF(X509_EXTENSION) **sk) +{ + X509_EXTENSION *ext; + STACK_OF(CONF_VALUE) *nval; + CONF_VALUE *val; + int i; + + if ((nval = NCONF_get_section(conf, section)) == NULL) + return 0; + for (i = 0; i < sk_CONF_VALUE_num(nval); i++) { + val = sk_CONF_VALUE_value(nval, i); + if ((ext = X509V3_EXT_nconf(conf, ctx, val->name, val->value)) == NULL) + return 0; + if (ctx->flags == X509V3_CTX_REPLACE) + delete_ext(*sk, ext); + if (sk) + X509v3_add_ext(sk, ext, -1); + X509_EXTENSION_free(ext); + } + return 1; +} + +/* + * Convenience functions to add extensions to a certificate, CRL and request + */ + +int X509V3_EXT_add_nconf(CONF *conf, X509V3_CTX *ctx, const char *section, + X509 *cert) +{ + STACK_OF(X509_EXTENSION) **sk = NULL; + if (cert) + sk = &cert->cert_info.extensions; + return X509V3_EXT_add_nconf_sk(conf, ctx, section, sk); +} + +/* Same as above but for a CRL */ + +int X509V3_EXT_CRL_add_nconf(CONF *conf, X509V3_CTX *ctx, const char *section, + X509_CRL *crl) +{ + STACK_OF(X509_EXTENSION) **sk = NULL; + if (crl) + sk = &crl->crl.extensions; + return X509V3_EXT_add_nconf_sk(conf, ctx, section, sk); +} + +/* Add extensions to certificate request */ + +int X509V3_EXT_REQ_add_nconf(CONF *conf, X509V3_CTX *ctx, const char *section, + X509_REQ *req) +{ + STACK_OF(X509_EXTENSION) *extlist = NULL, **sk = NULL; + int i; + if (req) + sk = &extlist; + i = X509V3_EXT_add_nconf_sk(conf, ctx, section, sk); + if (!i || !sk) + return i; + i = X509_REQ_add_extensions(req, extlist); + sk_X509_EXTENSION_pop_free(extlist, X509_EXTENSION_free); + return i; +} + +/* Config database functions */ + +char *X509V3_get_string(X509V3_CTX *ctx, const char *name, const char *section) +{ + if (!ctx->db || !ctx->db_meth || !ctx->db_meth->get_string) { + X509V3err(X509V3_F_X509V3_GET_STRING, X509V3_R_OPERATION_NOT_DEFINED); + return NULL; + } + if (ctx->db_meth->get_string) + return ctx->db_meth->get_string(ctx->db, name, section); + return NULL; +} + +STACK_OF(CONF_VALUE) *X509V3_get_section(X509V3_CTX *ctx, const char *section) +{ + if (!ctx->db || !ctx->db_meth || !ctx->db_meth->get_section) { + X509V3err(X509V3_F_X509V3_GET_SECTION, + X509V3_R_OPERATION_NOT_DEFINED); + return NULL; + } + if (ctx->db_meth->get_section) + return ctx->db_meth->get_section(ctx->db, section); + return NULL; +} + +void X509V3_string_free(X509V3_CTX *ctx, char *str) +{ + if (!str) + return; + if (ctx->db_meth->free_string) + ctx->db_meth->free_string(ctx->db, str); +} + +void X509V3_section_free(X509V3_CTX *ctx, STACK_OF(CONF_VALUE) *section) +{ + if (!section) + return; + if (ctx->db_meth->free_section) + ctx->db_meth->free_section(ctx->db, section); +} + +static char *nconf_get_string(void *db, const char *section, const char *value) +{ + return NCONF_get_string(db, section, value); +} + +static STACK_OF(CONF_VALUE) *nconf_get_section(void *db, const char *section) +{ + return NCONF_get_section(db, section); +} + +static X509V3_CONF_METHOD nconf_method = { + nconf_get_string, + nconf_get_section, + NULL, + NULL +}; + +void X509V3_set_nconf(X509V3_CTX *ctx, CONF *conf) +{ + ctx->db_meth = &nconf_method; + ctx->db = conf; +} + +void X509V3_set_ctx(X509V3_CTX *ctx, X509 *issuer, X509 *subj, X509_REQ *req, + X509_CRL *crl, int flags) +{ + ctx->issuer_cert = issuer; + ctx->subject_cert = subj; + ctx->crl = crl; + ctx->subject_req = req; + ctx->flags = flags; +} + +/* Old conf compatibility functions */ + +X509_EXTENSION *X509V3_EXT_conf(LHASH_OF(CONF_VALUE) *conf, X509V3_CTX *ctx, + const char *name, const char *value) +{ + CONF ctmp; + CONF_set_nconf(&ctmp, conf); + return X509V3_EXT_nconf(&ctmp, ctx, name, value); +} + +/* LHASH *conf: Config file */ +/* char *value: Value */ +X509_EXTENSION *X509V3_EXT_conf_nid(LHASH_OF(CONF_VALUE) *conf, + X509V3_CTX *ctx, int ext_nid, const char *value) +{ + CONF ctmp; + CONF_set_nconf(&ctmp, conf); + return X509V3_EXT_nconf_nid(&ctmp, ctx, ext_nid, value); +} + +static char *conf_lhash_get_string(void *db, const char *section, const char *value) +{ + return CONF_get_string(db, section, value); +} + +static STACK_OF(CONF_VALUE) *conf_lhash_get_section(void *db, const char *section) +{ + return CONF_get_section(db, section); +} + +static X509V3_CONF_METHOD conf_lhash_method = { + conf_lhash_get_string, + conf_lhash_get_section, + NULL, + NULL +}; + +void X509V3_set_conf_lhash(X509V3_CTX *ctx, LHASH_OF(CONF_VALUE) *lhash) +{ + ctx->db_meth = &conf_lhash_method; + ctx->db = lhash; +} + +int X509V3_EXT_add_conf(LHASH_OF(CONF_VALUE) *conf, X509V3_CTX *ctx, + const char *section, X509 *cert) +{ + CONF ctmp; + CONF_set_nconf(&ctmp, conf); + return X509V3_EXT_add_nconf(&ctmp, ctx, section, cert); +} + +/* Same as above but for a CRL */ + +int X509V3_EXT_CRL_add_conf(LHASH_OF(CONF_VALUE) *conf, X509V3_CTX *ctx, + const char *section, X509_CRL *crl) +{ + CONF ctmp; + CONF_set_nconf(&ctmp, conf); + return X509V3_EXT_CRL_add_nconf(&ctmp, ctx, section, crl); +} + +/* Add extensions to certificate request */ + +int X509V3_EXT_REQ_add_conf(LHASH_OF(CONF_VALUE) *conf, X509V3_CTX *ctx, + const char *section, X509_REQ *req) +{ + CONF ctmp; + CONF_set_nconf(&ctmp, conf); + return X509V3_EXT_REQ_add_nconf(&ctmp, ctx, section, req); +} diff --git a/crypto/x509v3/v3_cpols.c b/crypto/x509v3/v3_cpols.c new file mode 100644 index 000000000..a69733848 --- /dev/null +++ b/crypto/x509v3/v3_cpols.c @@ -0,0 +1,441 @@ +/* + * Copyright 1999-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#include "internal/cryptlib.h" +#include +#include +#include +#include + +#include "pcy_int.h" +#include "ext_dat.h" + +/* Certificate policies extension support: this one is a bit complex... */ + +static int i2r_certpol(X509V3_EXT_METHOD *method, STACK_OF(POLICYINFO) *pol, + BIO *out, int indent); +static STACK_OF(POLICYINFO) *r2i_certpol(X509V3_EXT_METHOD *method, + X509V3_CTX *ctx, const char *value); +static void print_qualifiers(BIO *out, STACK_OF(POLICYQUALINFO) *quals, + int indent); +static void print_notice(BIO *out, USERNOTICE *notice, int indent); +static POLICYINFO *policy_section(X509V3_CTX *ctx, + STACK_OF(CONF_VALUE) *polstrs, int ia5org); +static POLICYQUALINFO *notice_section(X509V3_CTX *ctx, + STACK_OF(CONF_VALUE) *unot, int ia5org); +static int nref_nos(STACK_OF(ASN1_INTEGER) *nnums, STACK_OF(CONF_VALUE) *nos); + +const X509V3_EXT_METHOD v3_cpols = { + NID_certificate_policies, 0, ASN1_ITEM_ref(CERTIFICATEPOLICIES), + 0, 0, 0, 0, + 0, 0, + 0, 0, + (X509V3_EXT_I2R)i2r_certpol, + (X509V3_EXT_R2I)r2i_certpol, + NULL +}; + +ASN1_ITEM_TEMPLATE(CERTIFICATEPOLICIES) = + ASN1_EX_TEMPLATE_TYPE(ASN1_TFLG_SEQUENCE_OF, 0, CERTIFICATEPOLICIES, POLICYINFO) +ASN1_ITEM_TEMPLATE_END(CERTIFICATEPOLICIES) + +IMPLEMENT_ASN1_FUNCTIONS(CERTIFICATEPOLICIES) + +ASN1_SEQUENCE(POLICYINFO) = { + ASN1_SIMPLE(POLICYINFO, policyid, ASN1_OBJECT), + ASN1_SEQUENCE_OF_OPT(POLICYINFO, qualifiers, POLICYQUALINFO) +} ASN1_SEQUENCE_END(POLICYINFO) + +IMPLEMENT_ASN1_FUNCTIONS(POLICYINFO) + +ASN1_ADB_TEMPLATE(policydefault) = ASN1_SIMPLE(POLICYQUALINFO, d.other, ASN1_ANY); + +ASN1_ADB(POLICYQUALINFO) = { + ADB_ENTRY(NID_id_qt_cps, ASN1_SIMPLE(POLICYQUALINFO, d.cpsuri, ASN1_IA5STRING)), + ADB_ENTRY(NID_id_qt_unotice, ASN1_SIMPLE(POLICYQUALINFO, d.usernotice, USERNOTICE)) +} ASN1_ADB_END(POLICYQUALINFO, 0, pqualid, 0, &policydefault_tt, NULL); + +ASN1_SEQUENCE(POLICYQUALINFO) = { + ASN1_SIMPLE(POLICYQUALINFO, pqualid, ASN1_OBJECT), + ASN1_ADB_OBJECT(POLICYQUALINFO) +} ASN1_SEQUENCE_END(POLICYQUALINFO) + +IMPLEMENT_ASN1_FUNCTIONS(POLICYQUALINFO) + +ASN1_SEQUENCE(USERNOTICE) = { + ASN1_OPT(USERNOTICE, noticeref, NOTICEREF), + ASN1_OPT(USERNOTICE, exptext, DISPLAYTEXT) +} ASN1_SEQUENCE_END(USERNOTICE) + +IMPLEMENT_ASN1_FUNCTIONS(USERNOTICE) + +ASN1_SEQUENCE(NOTICEREF) = { + ASN1_SIMPLE(NOTICEREF, organization, DISPLAYTEXT), + ASN1_SEQUENCE_OF(NOTICEREF, noticenos, ASN1_INTEGER) +} ASN1_SEQUENCE_END(NOTICEREF) + +IMPLEMENT_ASN1_FUNCTIONS(NOTICEREF) + +static STACK_OF(POLICYINFO) *r2i_certpol(X509V3_EXT_METHOD *method, + X509V3_CTX *ctx, const char *value) +{ + STACK_OF(POLICYINFO) *pols = NULL; + char *pstr; + POLICYINFO *pol; + ASN1_OBJECT *pobj; + STACK_OF(CONF_VALUE) *vals; + CONF_VALUE *cnf; + int i, ia5org; + pols = sk_POLICYINFO_new_null(); + if (pols == NULL) { + X509V3err(X509V3_F_R2I_CERTPOL, ERR_R_MALLOC_FAILURE); + return NULL; + } + vals = X509V3_parse_list(value); + if (vals == NULL) { + X509V3err(X509V3_F_R2I_CERTPOL, ERR_R_X509V3_LIB); + goto err; + } + ia5org = 0; + for (i = 0; i < sk_CONF_VALUE_num(vals); i++) { + cnf = sk_CONF_VALUE_value(vals, i); + if (cnf->value || !cnf->name) { + X509V3err(X509V3_F_R2I_CERTPOL, + X509V3_R_INVALID_POLICY_IDENTIFIER); + X509V3_conf_err(cnf); + goto err; + } + pstr = cnf->name; + if (strcmp(pstr, "ia5org") == 0) { + ia5org = 1; + continue; + } else if (*pstr == '@') { + STACK_OF(CONF_VALUE) *polsect; + polsect = X509V3_get_section(ctx, pstr + 1); + if (!polsect) { + X509V3err(X509V3_F_R2I_CERTPOL, X509V3_R_INVALID_SECTION); + + X509V3_conf_err(cnf); + goto err; + } + pol = policy_section(ctx, polsect, ia5org); + X509V3_section_free(ctx, polsect); + if (pol == NULL) + goto err; + } else { + if ((pobj = OBJ_txt2obj(cnf->name, 0)) == NULL) { + X509V3err(X509V3_F_R2I_CERTPOL, + X509V3_R_INVALID_OBJECT_IDENTIFIER); + X509V3_conf_err(cnf); + goto err; + } + pol = POLICYINFO_new(); + if (pol == NULL) { + X509V3err(X509V3_F_R2I_CERTPOL, ERR_R_MALLOC_FAILURE); + ASN1_OBJECT_free(pobj); + goto err; + } + pol->policyid = pobj; + } + if (!sk_POLICYINFO_push(pols, pol)) { + POLICYINFO_free(pol); + X509V3err(X509V3_F_R2I_CERTPOL, ERR_R_MALLOC_FAILURE); + goto err; + } + } + sk_CONF_VALUE_pop_free(vals, X509V3_conf_free); + return pols; + err: + sk_CONF_VALUE_pop_free(vals, X509V3_conf_free); + sk_POLICYINFO_pop_free(pols, POLICYINFO_free); + return NULL; +} + +static POLICYINFO *policy_section(X509V3_CTX *ctx, + STACK_OF(CONF_VALUE) *polstrs, int ia5org) +{ + int i; + CONF_VALUE *cnf; + POLICYINFO *pol; + POLICYQUALINFO *qual; + + if ((pol = POLICYINFO_new()) == NULL) + goto merr; + for (i = 0; i < sk_CONF_VALUE_num(polstrs); i++) { + cnf = sk_CONF_VALUE_value(polstrs, i); + if (strcmp(cnf->name, "policyIdentifier") == 0) { + ASN1_OBJECT *pobj; + if ((pobj = OBJ_txt2obj(cnf->value, 0)) == NULL) { + X509V3err(X509V3_F_POLICY_SECTION, + X509V3_R_INVALID_OBJECT_IDENTIFIER); + X509V3_conf_err(cnf); + goto err; + } + pol->policyid = pobj; + + } else if (!name_cmp(cnf->name, "CPS")) { + if (pol->qualifiers == NULL) + pol->qualifiers = sk_POLICYQUALINFO_new_null(); + if ((qual = POLICYQUALINFO_new()) == NULL) + goto merr; + if (!sk_POLICYQUALINFO_push(pol->qualifiers, qual)) + goto merr; + if ((qual->pqualid = OBJ_nid2obj(NID_id_qt_cps)) == NULL) { + X509V3err(X509V3_F_POLICY_SECTION, ERR_R_INTERNAL_ERROR); + goto err; + } + if ((qual->d.cpsuri = ASN1_IA5STRING_new()) == NULL) + goto merr; + if (!ASN1_STRING_set(qual->d.cpsuri, cnf->value, + strlen(cnf->value))) + goto merr; + } else if (!name_cmp(cnf->name, "userNotice")) { + STACK_OF(CONF_VALUE) *unot; + if (*cnf->value != '@') { + X509V3err(X509V3_F_POLICY_SECTION, + X509V3_R_EXPECTED_A_SECTION_NAME); + X509V3_conf_err(cnf); + goto err; + } + unot = X509V3_get_section(ctx, cnf->value + 1); + if (!unot) { + X509V3err(X509V3_F_POLICY_SECTION, X509V3_R_INVALID_SECTION); + + X509V3_conf_err(cnf); + goto err; + } + qual = notice_section(ctx, unot, ia5org); + X509V3_section_free(ctx, unot); + if (!qual) + goto err; + if (!pol->qualifiers) + pol->qualifiers = sk_POLICYQUALINFO_new_null(); + if (!sk_POLICYQUALINFO_push(pol->qualifiers, qual)) + goto merr; + } else { + X509V3err(X509V3_F_POLICY_SECTION, X509V3_R_INVALID_OPTION); + + X509V3_conf_err(cnf); + goto err; + } + } + if (!pol->policyid) { + X509V3err(X509V3_F_POLICY_SECTION, X509V3_R_NO_POLICY_IDENTIFIER); + goto err; + } + + return pol; + + merr: + X509V3err(X509V3_F_POLICY_SECTION, ERR_R_MALLOC_FAILURE); + + err: + POLICYINFO_free(pol); + return NULL; + +} + +static POLICYQUALINFO *notice_section(X509V3_CTX *ctx, + STACK_OF(CONF_VALUE) *unot, int ia5org) +{ + int i, ret; + CONF_VALUE *cnf; + USERNOTICE *not; + POLICYQUALINFO *qual; + + if ((qual = POLICYQUALINFO_new()) == NULL) + goto merr; + if ((qual->pqualid = OBJ_nid2obj(NID_id_qt_unotice)) == NULL) { + X509V3err(X509V3_F_NOTICE_SECTION, ERR_R_INTERNAL_ERROR); + goto err; + } + if ((not = USERNOTICE_new()) == NULL) + goto merr; + qual->d.usernotice = not; + for (i = 0; i < sk_CONF_VALUE_num(unot); i++) { + cnf = sk_CONF_VALUE_value(unot, i); + if (strcmp(cnf->name, "explicitText") == 0) { + if ((not->exptext = ASN1_VISIBLESTRING_new()) == NULL) + goto merr; + if (!ASN1_STRING_set(not->exptext, cnf->value, + strlen(cnf->value))) + goto merr; + } else if (strcmp(cnf->name, "organization") == 0) { + NOTICEREF *nref; + if (!not->noticeref) { + if ((nref = NOTICEREF_new()) == NULL) + goto merr; + not->noticeref = nref; + } else + nref = not->noticeref; + if (ia5org) + nref->organization->type = V_ASN1_IA5STRING; + else + nref->organization->type = V_ASN1_VISIBLESTRING; + if (!ASN1_STRING_set(nref->organization, cnf->value, + strlen(cnf->value))) + goto merr; + } else if (strcmp(cnf->name, "noticeNumbers") == 0) { + NOTICEREF *nref; + STACK_OF(CONF_VALUE) *nos; + if (!not->noticeref) { + if ((nref = NOTICEREF_new()) == NULL) + goto merr; + not->noticeref = nref; + } else + nref = not->noticeref; + nos = X509V3_parse_list(cnf->value); + if (!nos || !sk_CONF_VALUE_num(nos)) { + X509V3err(X509V3_F_NOTICE_SECTION, X509V3_R_INVALID_NUMBERS); + X509V3_conf_err(cnf); + sk_CONF_VALUE_pop_free(nos, X509V3_conf_free); + goto err; + } + ret = nref_nos(nref->noticenos, nos); + sk_CONF_VALUE_pop_free(nos, X509V3_conf_free); + if (!ret) + goto err; + } else { + X509V3err(X509V3_F_NOTICE_SECTION, X509V3_R_INVALID_OPTION); + X509V3_conf_err(cnf); + goto err; + } + } + + if (not->noticeref && + (!not->noticeref->noticenos || !not->noticeref->organization)) { + X509V3err(X509V3_F_NOTICE_SECTION, + X509V3_R_NEED_ORGANIZATION_AND_NUMBERS); + goto err; + } + + return qual; + + merr: + X509V3err(X509V3_F_NOTICE_SECTION, ERR_R_MALLOC_FAILURE); + + err: + POLICYQUALINFO_free(qual); + return NULL; +} + +static int nref_nos(STACK_OF(ASN1_INTEGER) *nnums, STACK_OF(CONF_VALUE) *nos) +{ + CONF_VALUE *cnf; + ASN1_INTEGER *aint; + + int i; + + for (i = 0; i < sk_CONF_VALUE_num(nos); i++) { + cnf = sk_CONF_VALUE_value(nos, i); + if ((aint = s2i_ASN1_INTEGER(NULL, cnf->name)) == NULL) { + X509V3err(X509V3_F_NREF_NOS, X509V3_R_INVALID_NUMBER); + goto err; + } + if (!sk_ASN1_INTEGER_push(nnums, aint)) + goto merr; + } + return 1; + + merr: + X509V3err(X509V3_F_NREF_NOS, ERR_R_MALLOC_FAILURE); + + err: + sk_ASN1_INTEGER_pop_free(nnums, ASN1_STRING_free); + return 0; +} + +static int i2r_certpol(X509V3_EXT_METHOD *method, STACK_OF(POLICYINFO) *pol, + BIO *out, int indent) +{ + int i; + POLICYINFO *pinfo; + /* First print out the policy OIDs */ + for (i = 0; i < sk_POLICYINFO_num(pol); i++) { + pinfo = sk_POLICYINFO_value(pol, i); + BIO_printf(out, "%*sPolicy: ", indent, ""); + i2a_ASN1_OBJECT(out, pinfo->policyid); + BIO_puts(out, "\n"); + if (pinfo->qualifiers) + print_qualifiers(out, pinfo->qualifiers, indent + 2); + } + return 1; +} + +static void print_qualifiers(BIO *out, STACK_OF(POLICYQUALINFO) *quals, + int indent) +{ + POLICYQUALINFO *qualinfo; + int i; + for (i = 0; i < sk_POLICYQUALINFO_num(quals); i++) { + qualinfo = sk_POLICYQUALINFO_value(quals, i); + switch (OBJ_obj2nid(qualinfo->pqualid)) { + case NID_id_qt_cps: + BIO_printf(out, "%*sCPS: %s\n", indent, "", + qualinfo->d.cpsuri->data); + break; + + case NID_id_qt_unotice: + BIO_printf(out, "%*sUser Notice:\n", indent, ""); + print_notice(out, qualinfo->d.usernotice, indent + 2); + break; + + default: + BIO_printf(out, "%*sUnknown Qualifier: ", indent + 2, ""); + + i2a_ASN1_OBJECT(out, qualinfo->pqualid); + BIO_puts(out, "\n"); + break; + } + } +} + +static void print_notice(BIO *out, USERNOTICE *notice, int indent) +{ + int i; + if (notice->noticeref) { + NOTICEREF *ref; + ref = notice->noticeref; + BIO_printf(out, "%*sOrganization: %s\n", indent, "", + ref->organization->data); + BIO_printf(out, "%*sNumber%s: ", indent, "", + sk_ASN1_INTEGER_num(ref->noticenos) > 1 ? "s" : ""); + for (i = 0; i < sk_ASN1_INTEGER_num(ref->noticenos); i++) { + ASN1_INTEGER *num; + char *tmp; + num = sk_ASN1_INTEGER_value(ref->noticenos, i); + if (i) + BIO_puts(out, ", "); + tmp = i2s_ASN1_INTEGER(NULL, num); + BIO_puts(out, tmp); + OPENSSL_free(tmp); + } + BIO_puts(out, "\n"); + } + if (notice->exptext) + BIO_printf(out, "%*sExplicit Text: %s\n", indent, "", + notice->exptext->data); +} + +void X509_POLICY_NODE_print(BIO *out, X509_POLICY_NODE *node, int indent) +{ + const X509_POLICY_DATA *dat = node->data; + + BIO_printf(out, "%*sPolicy: ", indent, ""); + + i2a_ASN1_OBJECT(out, dat->valid_policy); + BIO_puts(out, "\n"); + BIO_printf(out, "%*s%s\n", indent + 2, "", + node_data_critical(dat) ? "Critical" : "Non Critical"); + if (dat->qualifier_set) + print_qualifiers(out, dat->qualifier_set, indent + 2); + else + BIO_printf(out, "%*sNo Qualifiers\n", indent + 2, ""); +} diff --git a/crypto/x509v3/v3_crld.c b/crypto/x509v3/v3_crld.c new file mode 100644 index 000000000..c4c77f185 --- /dev/null +++ b/crypto/x509v3/v3_crld.c @@ -0,0 +1,509 @@ +/* + * Copyright 1999-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#include "internal/cryptlib.h" +#include +#include +#include +#include + +#include "internal/x509_int.h" +#include "ext_dat.h" + +static void *v2i_crld(const X509V3_EXT_METHOD *method, + X509V3_CTX *ctx, STACK_OF(CONF_VALUE) *nval); +static int i2r_crldp(const X509V3_EXT_METHOD *method, void *pcrldp, BIO *out, + int indent); + +const X509V3_EXT_METHOD v3_crld = { + NID_crl_distribution_points, 0, ASN1_ITEM_ref(CRL_DIST_POINTS), + 0, 0, 0, 0, + 0, 0, + 0, + v2i_crld, + i2r_crldp, 0, + NULL +}; + +const X509V3_EXT_METHOD v3_freshest_crl = { + NID_freshest_crl, 0, ASN1_ITEM_ref(CRL_DIST_POINTS), + 0, 0, 0, 0, + 0, 0, + 0, + v2i_crld, + i2r_crldp, 0, + NULL +}; + +static STACK_OF(GENERAL_NAME) *gnames_from_sectname(X509V3_CTX *ctx, + char *sect) +{ + STACK_OF(CONF_VALUE) *gnsect; + STACK_OF(GENERAL_NAME) *gens; + if (*sect == '@') + gnsect = X509V3_get_section(ctx, sect + 1); + else + gnsect = X509V3_parse_list(sect); + if (!gnsect) { + X509V3err(X509V3_F_GNAMES_FROM_SECTNAME, X509V3_R_SECTION_NOT_FOUND); + return NULL; + } + gens = v2i_GENERAL_NAMES(NULL, ctx, gnsect); + if (*sect == '@') + X509V3_section_free(ctx, gnsect); + else + sk_CONF_VALUE_pop_free(gnsect, X509V3_conf_free); + return gens; +} + +static int set_dist_point_name(DIST_POINT_NAME **pdp, X509V3_CTX *ctx, + CONF_VALUE *cnf) +{ + STACK_OF(GENERAL_NAME) *fnm = NULL; + STACK_OF(X509_NAME_ENTRY) *rnm = NULL; + + if (strncmp(cnf->name, "fullname", 9) == 0) { + fnm = gnames_from_sectname(ctx, cnf->value); + if (!fnm) + goto err; + } else if (strcmp(cnf->name, "relativename") == 0) { + int ret; + STACK_OF(CONF_VALUE) *dnsect; + X509_NAME *nm; + nm = X509_NAME_new(); + if (nm == NULL) + return -1; + dnsect = X509V3_get_section(ctx, cnf->value); + if (!dnsect) { + X509V3err(X509V3_F_SET_DIST_POINT_NAME, + X509V3_R_SECTION_NOT_FOUND); + return -1; + } + ret = X509V3_NAME_from_section(nm, dnsect, MBSTRING_ASC); + X509V3_section_free(ctx, dnsect); + rnm = nm->entries; + nm->entries = NULL; + X509_NAME_free(nm); + if (!ret || sk_X509_NAME_ENTRY_num(rnm) <= 0) + goto err; + /* + * Since its a name fragment can't have more than one RDNSequence + */ + if (sk_X509_NAME_ENTRY_value(rnm, + sk_X509_NAME_ENTRY_num(rnm) - 1)->set) { + X509V3err(X509V3_F_SET_DIST_POINT_NAME, + X509V3_R_INVALID_MULTIPLE_RDNS); + goto err; + } + } else + return 0; + + if (*pdp) { + X509V3err(X509V3_F_SET_DIST_POINT_NAME, + X509V3_R_DISTPOINT_ALREADY_SET); + goto err; + } + + *pdp = DIST_POINT_NAME_new(); + if (*pdp == NULL) + goto err; + if (fnm) { + (*pdp)->type = 0; + (*pdp)->name.fullname = fnm; + } else { + (*pdp)->type = 1; + (*pdp)->name.relativename = rnm; + } + + return 1; + + err: + sk_GENERAL_NAME_pop_free(fnm, GENERAL_NAME_free); + sk_X509_NAME_ENTRY_pop_free(rnm, X509_NAME_ENTRY_free); + return -1; +} + +static const BIT_STRING_BITNAME reason_flags[] = { + {0, "Unused", "unused"}, + {1, "Key Compromise", "keyCompromise"}, + {2, "CA Compromise", "CACompromise"}, + {3, "Affiliation Changed", "affiliationChanged"}, + {4, "Superseded", "superseded"}, + {5, "Cessation Of Operation", "cessationOfOperation"}, + {6, "Certificate Hold", "certificateHold"}, + {7, "Privilege Withdrawn", "privilegeWithdrawn"}, + {8, "AA Compromise", "AACompromise"}, + {-1, NULL, NULL} +}; + +static int set_reasons(ASN1_BIT_STRING **preas, char *value) +{ + STACK_OF(CONF_VALUE) *rsk = NULL; + const BIT_STRING_BITNAME *pbn; + const char *bnam; + int i, ret = 0; + rsk = X509V3_parse_list(value); + if (rsk == NULL) + return 0; + if (*preas != NULL) + goto err; + for (i = 0; i < sk_CONF_VALUE_num(rsk); i++) { + bnam = sk_CONF_VALUE_value(rsk, i)->name; + if (*preas == NULL) { + *preas = ASN1_BIT_STRING_new(); + if (*preas == NULL) + goto err; + } + for (pbn = reason_flags; pbn->lname; pbn++) { + if (strcmp(pbn->sname, bnam) == 0) { + if (!ASN1_BIT_STRING_set_bit(*preas, pbn->bitnum, 1)) + goto err; + break; + } + } + if (!pbn->lname) + goto err; + } + ret = 1; + + err: + sk_CONF_VALUE_pop_free(rsk, X509V3_conf_free); + return ret; +} + +static int print_reasons(BIO *out, const char *rname, + ASN1_BIT_STRING *rflags, int indent) +{ + int first = 1; + const BIT_STRING_BITNAME *pbn; + BIO_printf(out, "%*s%s:\n%*s", indent, "", rname, indent + 2, ""); + for (pbn = reason_flags; pbn->lname; pbn++) { + if (ASN1_BIT_STRING_get_bit(rflags, pbn->bitnum)) { + if (first) + first = 0; + else + BIO_puts(out, ", "); + BIO_puts(out, pbn->lname); + } + } + if (first) + BIO_puts(out, "\n"); + else + BIO_puts(out, "\n"); + return 1; +} + +static DIST_POINT *crldp_from_section(X509V3_CTX *ctx, + STACK_OF(CONF_VALUE) *nval) +{ + int i; + CONF_VALUE *cnf; + DIST_POINT *point = NULL; + point = DIST_POINT_new(); + if (point == NULL) + goto err; + for (i = 0; i < sk_CONF_VALUE_num(nval); i++) { + int ret; + cnf = sk_CONF_VALUE_value(nval, i); + ret = set_dist_point_name(&point->distpoint, ctx, cnf); + if (ret > 0) + continue; + if (ret < 0) + goto err; + if (strcmp(cnf->name, "reasons") == 0) { + if (!set_reasons(&point->reasons, cnf->value)) + goto err; + } else if (strcmp(cnf->name, "CRLissuer") == 0) { + point->CRLissuer = gnames_from_sectname(ctx, cnf->value); + if (!point->CRLissuer) + goto err; + } + } + + return point; + + err: + DIST_POINT_free(point); + return NULL; +} + +static void *v2i_crld(const X509V3_EXT_METHOD *method, + X509V3_CTX *ctx, STACK_OF(CONF_VALUE) *nval) +{ + STACK_OF(DIST_POINT) *crld = NULL; + GENERAL_NAMES *gens = NULL; + GENERAL_NAME *gen = NULL; + CONF_VALUE *cnf; + int i; + + if ((crld = sk_DIST_POINT_new_null()) == NULL) + goto merr; + for (i = 0; i < sk_CONF_VALUE_num(nval); i++) { + DIST_POINT *point; + cnf = sk_CONF_VALUE_value(nval, i); + if (!cnf->value) { + STACK_OF(CONF_VALUE) *dpsect; + dpsect = X509V3_get_section(ctx, cnf->name); + if (!dpsect) + goto err; + point = crldp_from_section(ctx, dpsect); + X509V3_section_free(ctx, dpsect); + if (!point) + goto err; + if (!sk_DIST_POINT_push(crld, point)) { + DIST_POINT_free(point); + goto merr; + } + } else { + if ((gen = v2i_GENERAL_NAME(method, ctx, cnf)) == NULL) + goto err; + if ((gens = GENERAL_NAMES_new()) == NULL) + goto merr; + if (!sk_GENERAL_NAME_push(gens, gen)) + goto merr; + gen = NULL; + if ((point = DIST_POINT_new()) == NULL) + goto merr; + if (!sk_DIST_POINT_push(crld, point)) { + DIST_POINT_free(point); + goto merr; + } + if ((point->distpoint = DIST_POINT_NAME_new()) == NULL) + goto merr; + point->distpoint->name.fullname = gens; + point->distpoint->type = 0; + gens = NULL; + } + } + return crld; + + merr: + X509V3err(X509V3_F_V2I_CRLD, ERR_R_MALLOC_FAILURE); + err: + GENERAL_NAME_free(gen); + GENERAL_NAMES_free(gens); + sk_DIST_POINT_pop_free(crld, DIST_POINT_free); + return NULL; +} + +static int dpn_cb(int operation, ASN1_VALUE **pval, const ASN1_ITEM *it, + void *exarg) +{ + DIST_POINT_NAME *dpn = (DIST_POINT_NAME *)*pval; + + switch (operation) { + case ASN1_OP_NEW_POST: + dpn->dpname = NULL; + break; + + case ASN1_OP_FREE_POST: + X509_NAME_free(dpn->dpname); + break; + } + return 1; +} + + +ASN1_CHOICE_cb(DIST_POINT_NAME, dpn_cb) = { + ASN1_IMP_SEQUENCE_OF(DIST_POINT_NAME, name.fullname, GENERAL_NAME, 0), + ASN1_IMP_SET_OF(DIST_POINT_NAME, name.relativename, X509_NAME_ENTRY, 1) +} ASN1_CHOICE_END_cb(DIST_POINT_NAME, DIST_POINT_NAME, type) + + +IMPLEMENT_ASN1_FUNCTIONS(DIST_POINT_NAME) + +ASN1_SEQUENCE(DIST_POINT) = { + ASN1_EXP_OPT(DIST_POINT, distpoint, DIST_POINT_NAME, 0), + ASN1_IMP_OPT(DIST_POINT, reasons, ASN1_BIT_STRING, 1), + ASN1_IMP_SEQUENCE_OF_OPT(DIST_POINT, CRLissuer, GENERAL_NAME, 2) +} ASN1_SEQUENCE_END(DIST_POINT) + +IMPLEMENT_ASN1_FUNCTIONS(DIST_POINT) + +ASN1_ITEM_TEMPLATE(CRL_DIST_POINTS) = + ASN1_EX_TEMPLATE_TYPE(ASN1_TFLG_SEQUENCE_OF, 0, CRLDistributionPoints, DIST_POINT) +ASN1_ITEM_TEMPLATE_END(CRL_DIST_POINTS) + +IMPLEMENT_ASN1_FUNCTIONS(CRL_DIST_POINTS) + +ASN1_SEQUENCE(ISSUING_DIST_POINT) = { + ASN1_EXP_OPT(ISSUING_DIST_POINT, distpoint, DIST_POINT_NAME, 0), + ASN1_IMP_OPT(ISSUING_DIST_POINT, onlyuser, ASN1_FBOOLEAN, 1), + ASN1_IMP_OPT(ISSUING_DIST_POINT, onlyCA, ASN1_FBOOLEAN, 2), + ASN1_IMP_OPT(ISSUING_DIST_POINT, onlysomereasons, ASN1_BIT_STRING, 3), + ASN1_IMP_OPT(ISSUING_DIST_POINT, indirectCRL, ASN1_FBOOLEAN, 4), + ASN1_IMP_OPT(ISSUING_DIST_POINT, onlyattr, ASN1_FBOOLEAN, 5) +} ASN1_SEQUENCE_END(ISSUING_DIST_POINT) + +IMPLEMENT_ASN1_FUNCTIONS(ISSUING_DIST_POINT) + +static int i2r_idp(const X509V3_EXT_METHOD *method, void *pidp, BIO *out, + int indent); +static void *v2i_idp(const X509V3_EXT_METHOD *method, X509V3_CTX *ctx, + STACK_OF(CONF_VALUE) *nval); + +const X509V3_EXT_METHOD v3_idp = { + NID_issuing_distribution_point, X509V3_EXT_MULTILINE, + ASN1_ITEM_ref(ISSUING_DIST_POINT), + 0, 0, 0, 0, + 0, 0, + 0, + v2i_idp, + i2r_idp, 0, + NULL +}; + +static void *v2i_idp(const X509V3_EXT_METHOD *method, X509V3_CTX *ctx, + STACK_OF(CONF_VALUE) *nval) +{ + ISSUING_DIST_POINT *idp = NULL; + CONF_VALUE *cnf; + char *name, *val; + int i, ret; + idp = ISSUING_DIST_POINT_new(); + if (idp == NULL) + goto merr; + for (i = 0; i < sk_CONF_VALUE_num(nval); i++) { + cnf = sk_CONF_VALUE_value(nval, i); + name = cnf->name; + val = cnf->value; + ret = set_dist_point_name(&idp->distpoint, ctx, cnf); + if (ret > 0) + continue; + if (ret < 0) + goto err; + if (strcmp(name, "onlyuser") == 0) { + if (!X509V3_get_value_bool(cnf, &idp->onlyuser)) + goto err; + } else if (strcmp(name, "onlyCA") == 0) { + if (!X509V3_get_value_bool(cnf, &idp->onlyCA)) + goto err; + } else if (strcmp(name, "onlyAA") == 0) { + if (!X509V3_get_value_bool(cnf, &idp->onlyattr)) + goto err; + } else if (strcmp(name, "indirectCRL") == 0) { + if (!X509V3_get_value_bool(cnf, &idp->indirectCRL)) + goto err; + } else if (strcmp(name, "onlysomereasons") == 0) { + if (!set_reasons(&idp->onlysomereasons, val)) + goto err; + } else { + X509V3err(X509V3_F_V2I_IDP, X509V3_R_INVALID_NAME); + X509V3_conf_err(cnf); + goto err; + } + } + return idp; + + merr: + X509V3err(X509V3_F_V2I_IDP, ERR_R_MALLOC_FAILURE); + err: + ISSUING_DIST_POINT_free(idp); + return NULL; +} + +static int print_gens(BIO *out, STACK_OF(GENERAL_NAME) *gens, int indent) +{ + int i; + for (i = 0; i < sk_GENERAL_NAME_num(gens); i++) { + BIO_printf(out, "%*s", indent + 2, ""); + GENERAL_NAME_print(out, sk_GENERAL_NAME_value(gens, i)); + BIO_puts(out, "\n"); + } + return 1; +} + +static int print_distpoint(BIO *out, DIST_POINT_NAME *dpn, int indent) +{ + if (dpn->type == 0) { + BIO_printf(out, "%*sFull Name:\n", indent, ""); + print_gens(out, dpn->name.fullname, indent); + } else { + X509_NAME ntmp; + ntmp.entries = dpn->name.relativename; + BIO_printf(out, "%*sRelative Name:\n%*s", indent, "", indent + 2, ""); + X509_NAME_print_ex(out, &ntmp, 0, XN_FLAG_ONELINE); + BIO_puts(out, "\n"); + } + return 1; +} + +static int i2r_idp(const X509V3_EXT_METHOD *method, void *pidp, BIO *out, + int indent) +{ + ISSUING_DIST_POINT *idp = pidp; + if (idp->distpoint) + print_distpoint(out, idp->distpoint, indent); + if (idp->onlyuser > 0) + BIO_printf(out, "%*sOnly User Certificates\n", indent, ""); + if (idp->onlyCA > 0) + BIO_printf(out, "%*sOnly CA Certificates\n", indent, ""); + if (idp->indirectCRL > 0) + BIO_printf(out, "%*sIndirect CRL\n", indent, ""); + if (idp->onlysomereasons) + print_reasons(out, "Only Some Reasons", idp->onlysomereasons, indent); + if (idp->onlyattr > 0) + BIO_printf(out, "%*sOnly Attribute Certificates\n", indent, ""); + if (!idp->distpoint && (idp->onlyuser <= 0) && (idp->onlyCA <= 0) + && (idp->indirectCRL <= 0) && !idp->onlysomereasons + && (idp->onlyattr <= 0)) + BIO_printf(out, "%*s\n", indent, ""); + + return 1; +} + +static int i2r_crldp(const X509V3_EXT_METHOD *method, void *pcrldp, BIO *out, + int indent) +{ + STACK_OF(DIST_POINT) *crld = pcrldp; + DIST_POINT *point; + int i; + for (i = 0; i < sk_DIST_POINT_num(crld); i++) { + BIO_puts(out, "\n"); + point = sk_DIST_POINT_value(crld, i); + if (point->distpoint) + print_distpoint(out, point->distpoint, indent); + if (point->reasons) + print_reasons(out, "Reasons", point->reasons, indent); + if (point->CRLissuer) { + BIO_printf(out, "%*sCRL Issuer:\n", indent, ""); + print_gens(out, point->CRLissuer, indent); + } + } + return 1; +} + +int DIST_POINT_set_dpname(DIST_POINT_NAME *dpn, X509_NAME *iname) +{ + int i; + STACK_OF(X509_NAME_ENTRY) *frag; + X509_NAME_ENTRY *ne; + if (!dpn || (dpn->type != 1)) + return 1; + frag = dpn->name.relativename; + dpn->dpname = X509_NAME_dup(iname); + if (!dpn->dpname) + return 0; + for (i = 0; i < sk_X509_NAME_ENTRY_num(frag); i++) { + ne = sk_X509_NAME_ENTRY_value(frag, i); + if (!X509_NAME_add_entry(dpn->dpname, ne, -1, i ? 0 : 1)) { + X509_NAME_free(dpn->dpname); + dpn->dpname = NULL; + return 0; + } + } + /* generate cached encoding of name */ + if (i2d_X509_NAME(dpn->dpname, NULL) < 0) { + X509_NAME_free(dpn->dpname); + dpn->dpname = NULL; + return 0; + } + return 1; +} diff --git a/crypto/x509v3/v3_enum.c b/crypto/x509v3/v3_enum.c new file mode 100644 index 000000000..f39cb5ac2 --- /dev/null +++ b/crypto/x509v3/v3_enum.c @@ -0,0 +1,53 @@ +/* + * Copyright 1999-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#include "internal/cryptlib.h" +#include +#include "ext_dat.h" + +static ENUMERATED_NAMES crl_reasons[] = { + {CRL_REASON_UNSPECIFIED, "Unspecified", "unspecified"}, + {CRL_REASON_KEY_COMPROMISE, "Key Compromise", "keyCompromise"}, + {CRL_REASON_CA_COMPROMISE, "CA Compromise", "CACompromise"}, + {CRL_REASON_AFFILIATION_CHANGED, "Affiliation Changed", + "affiliationChanged"}, + {CRL_REASON_SUPERSEDED, "Superseded", "superseded"}, + {CRL_REASON_CESSATION_OF_OPERATION, + "Cessation Of Operation", "cessationOfOperation"}, + {CRL_REASON_CERTIFICATE_HOLD, "Certificate Hold", "certificateHold"}, + {CRL_REASON_REMOVE_FROM_CRL, "Remove From CRL", "removeFromCRL"}, + {CRL_REASON_PRIVILEGE_WITHDRAWN, "Privilege Withdrawn", + "privilegeWithdrawn"}, + {CRL_REASON_AA_COMPROMISE, "AA Compromise", "AACompromise"}, + {-1, NULL, NULL} +}; + +const X509V3_EXT_METHOD v3_crl_reason = { + NID_crl_reason, 0, ASN1_ITEM_ref(ASN1_ENUMERATED), + 0, 0, 0, 0, + (X509V3_EXT_I2S)i2s_ASN1_ENUMERATED_TABLE, + 0, + 0, 0, 0, 0, + crl_reasons +}; + +char *i2s_ASN1_ENUMERATED_TABLE(X509V3_EXT_METHOD *method, + const ASN1_ENUMERATED *e) +{ + ENUMERATED_NAMES *enam; + long strval; + + strval = ASN1_ENUMERATED_get(e); + for (enam = method->usr_data; enam->lname; enam++) { + if (strval == enam->bitnum) + return OPENSSL_strdup(enam->lname); + } + return i2s_ASN1_ENUMERATED(method, e); +} diff --git a/crypto/x509v3/v3_extku.c b/crypto/x509v3/v3_extku.c new file mode 100644 index 000000000..bae755e3f --- /dev/null +++ b/crypto/x509v3/v3_extku.c @@ -0,0 +1,100 @@ +/* + * Copyright 1999-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#include "internal/cryptlib.h" +#include +#include +#include +#include "ext_dat.h" + +static void *v2i_EXTENDED_KEY_USAGE(const X509V3_EXT_METHOD *method, + X509V3_CTX *ctx, + STACK_OF(CONF_VALUE) *nval); +static STACK_OF(CONF_VALUE) *i2v_EXTENDED_KEY_USAGE(const X509V3_EXT_METHOD + *method, void *eku, STACK_OF(CONF_VALUE) + *extlist); + +const X509V3_EXT_METHOD v3_ext_ku = { + NID_ext_key_usage, 0, + ASN1_ITEM_ref(EXTENDED_KEY_USAGE), + 0, 0, 0, 0, + 0, 0, + i2v_EXTENDED_KEY_USAGE, + v2i_EXTENDED_KEY_USAGE, + 0, 0, + NULL +}; + +/* NB OCSP acceptable responses also is a SEQUENCE OF OBJECT */ +const X509V3_EXT_METHOD v3_ocsp_accresp = { + NID_id_pkix_OCSP_acceptableResponses, 0, + ASN1_ITEM_ref(EXTENDED_KEY_USAGE), + 0, 0, 0, 0, + 0, 0, + i2v_EXTENDED_KEY_USAGE, + v2i_EXTENDED_KEY_USAGE, + 0, 0, + NULL +}; + +ASN1_ITEM_TEMPLATE(EXTENDED_KEY_USAGE) = + ASN1_EX_TEMPLATE_TYPE(ASN1_TFLG_SEQUENCE_OF, 0, EXTENDED_KEY_USAGE, ASN1_OBJECT) +ASN1_ITEM_TEMPLATE_END(EXTENDED_KEY_USAGE) + +IMPLEMENT_ASN1_FUNCTIONS(EXTENDED_KEY_USAGE) + +static STACK_OF(CONF_VALUE) *i2v_EXTENDED_KEY_USAGE(const X509V3_EXT_METHOD + *method, void *a, STACK_OF(CONF_VALUE) + *ext_list) +{ + EXTENDED_KEY_USAGE *eku = a; + int i; + ASN1_OBJECT *obj; + char obj_tmp[80]; + for (i = 0; i < sk_ASN1_OBJECT_num(eku); i++) { + obj = sk_ASN1_OBJECT_value(eku, i); + i2t_ASN1_OBJECT(obj_tmp, 80, obj); + X509V3_add_value(NULL, obj_tmp, &ext_list); + } + return ext_list; +} + +static void *v2i_EXTENDED_KEY_USAGE(const X509V3_EXT_METHOD *method, + X509V3_CTX *ctx, + STACK_OF(CONF_VALUE) *nval) +{ + EXTENDED_KEY_USAGE *extku; + char *extval; + ASN1_OBJECT *objtmp; + CONF_VALUE *val; + int i; + + if ((extku = sk_ASN1_OBJECT_new_null()) == NULL) { + X509V3err(X509V3_F_V2I_EXTENDED_KEY_USAGE, ERR_R_MALLOC_FAILURE); + return NULL; + } + + for (i = 0; i < sk_CONF_VALUE_num(nval); i++) { + val = sk_CONF_VALUE_value(nval, i); + if (val->value) + extval = val->value; + else + extval = val->name; + if ((objtmp = OBJ_txt2obj(extval, 0)) == NULL) { + sk_ASN1_OBJECT_pop_free(extku, ASN1_OBJECT_free); + X509V3err(X509V3_F_V2I_EXTENDED_KEY_USAGE, + X509V3_R_INVALID_OBJECT_IDENTIFIER); + X509V3_conf_err(val); + return NULL; + } + sk_ASN1_OBJECT_push(extku, objtmp); + } + return extku; +} diff --git a/crypto/x509v3/v3_genn.c b/crypto/x509v3/v3_genn.c new file mode 100644 index 000000000..8d1199748 --- /dev/null +++ b/crypto/x509v3/v3_genn.c @@ -0,0 +1,200 @@ +/* + * Copyright 1999-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#include "internal/cryptlib.h" +#include +#include +#include + +ASN1_SEQUENCE(OTHERNAME) = { + ASN1_SIMPLE(OTHERNAME, type_id, ASN1_OBJECT), + /* Maybe have a true ANY DEFINED BY later */ + ASN1_EXP(OTHERNAME, value, ASN1_ANY, 0) +} ASN1_SEQUENCE_END(OTHERNAME) + +IMPLEMENT_ASN1_FUNCTIONS(OTHERNAME) + +ASN1_SEQUENCE(EDIPARTYNAME) = { + ASN1_IMP_OPT(EDIPARTYNAME, nameAssigner, DIRECTORYSTRING, 0), + ASN1_IMP_OPT(EDIPARTYNAME, partyName, DIRECTORYSTRING, 1) +} ASN1_SEQUENCE_END(EDIPARTYNAME) + +IMPLEMENT_ASN1_FUNCTIONS(EDIPARTYNAME) + +ASN1_CHOICE(GENERAL_NAME) = { + ASN1_IMP(GENERAL_NAME, d.otherName, OTHERNAME, GEN_OTHERNAME), + ASN1_IMP(GENERAL_NAME, d.rfc822Name, ASN1_IA5STRING, GEN_EMAIL), + ASN1_IMP(GENERAL_NAME, d.dNSName, ASN1_IA5STRING, GEN_DNS), + /* Don't decode this */ + ASN1_IMP(GENERAL_NAME, d.x400Address, ASN1_SEQUENCE, GEN_X400), + /* X509_NAME is a CHOICE type so use EXPLICIT */ + ASN1_EXP(GENERAL_NAME, d.directoryName, X509_NAME, GEN_DIRNAME), + ASN1_IMP(GENERAL_NAME, d.ediPartyName, EDIPARTYNAME, GEN_EDIPARTY), + ASN1_IMP(GENERAL_NAME, d.uniformResourceIdentifier, ASN1_IA5STRING, GEN_URI), + ASN1_IMP(GENERAL_NAME, d.iPAddress, ASN1_OCTET_STRING, GEN_IPADD), + ASN1_IMP(GENERAL_NAME, d.registeredID, ASN1_OBJECT, GEN_RID) +} ASN1_CHOICE_END(GENERAL_NAME) + +IMPLEMENT_ASN1_FUNCTIONS(GENERAL_NAME) + +ASN1_ITEM_TEMPLATE(GENERAL_NAMES) = + ASN1_EX_TEMPLATE_TYPE(ASN1_TFLG_SEQUENCE_OF, 0, GeneralNames, GENERAL_NAME) +ASN1_ITEM_TEMPLATE_END(GENERAL_NAMES) + +IMPLEMENT_ASN1_FUNCTIONS(GENERAL_NAMES) + +GENERAL_NAME *GENERAL_NAME_dup(GENERAL_NAME *a) +{ + return (GENERAL_NAME *)ASN1_dup((i2d_of_void *)i2d_GENERAL_NAME, + (d2i_of_void *)d2i_GENERAL_NAME, + (char *)a); +} + +/* Returns 0 if they are equal, != 0 otherwise. */ +int GENERAL_NAME_cmp(GENERAL_NAME *a, GENERAL_NAME *b) +{ + int result = -1; + + if (!a || !b || a->type != b->type) + return -1; + switch (a->type) { + case GEN_X400: + case GEN_EDIPARTY: + result = ASN1_TYPE_cmp(a->d.other, b->d.other); + break; + + case GEN_OTHERNAME: + result = OTHERNAME_cmp(a->d.otherName, b->d.otherName); + break; + + case GEN_EMAIL: + case GEN_DNS: + case GEN_URI: + result = ASN1_STRING_cmp(a->d.ia5, b->d.ia5); + break; + + case GEN_DIRNAME: + result = X509_NAME_cmp(a->d.dirn, b->d.dirn); + break; + + case GEN_IPADD: + result = ASN1_OCTET_STRING_cmp(a->d.ip, b->d.ip); + break; + + case GEN_RID: + result = OBJ_cmp(a->d.rid, b->d.rid); + break; + } + return result; +} + +/* Returns 0 if they are equal, != 0 otherwise. */ +int OTHERNAME_cmp(OTHERNAME *a, OTHERNAME *b) +{ + int result = -1; + + if (!a || !b) + return -1; + /* Check their type first. */ + if ((result = OBJ_cmp(a->type_id, b->type_id)) != 0) + return result; + /* Check the value. */ + result = ASN1_TYPE_cmp(a->value, b->value); + return result; +} + +void GENERAL_NAME_set0_value(GENERAL_NAME *a, int type, void *value) +{ + switch (type) { + case GEN_X400: + case GEN_EDIPARTY: + a->d.other = value; + break; + + case GEN_OTHERNAME: + a->d.otherName = value; + break; + + case GEN_EMAIL: + case GEN_DNS: + case GEN_URI: + a->d.ia5 = value; + break; + + case GEN_DIRNAME: + a->d.dirn = value; + break; + + case GEN_IPADD: + a->d.ip = value; + break; + + case GEN_RID: + a->d.rid = value; + break; + } + a->type = type; +} + +void *GENERAL_NAME_get0_value(GENERAL_NAME *a, int *ptype) +{ + if (ptype) + *ptype = a->type; + switch (a->type) { + case GEN_X400: + case GEN_EDIPARTY: + return a->d.other; + + case GEN_OTHERNAME: + return a->d.otherName; + + case GEN_EMAIL: + case GEN_DNS: + case GEN_URI: + return a->d.ia5; + + case GEN_DIRNAME: + return a->d.dirn; + + case GEN_IPADD: + return a->d.ip; + + case GEN_RID: + return a->d.rid; + + default: + return NULL; + } +} + +int GENERAL_NAME_set0_othername(GENERAL_NAME *gen, + ASN1_OBJECT *oid, ASN1_TYPE *value) +{ + OTHERNAME *oth; + oth = OTHERNAME_new(); + if (oth == NULL) + return 0; + oth->type_id = oid; + oth->value = value; + GENERAL_NAME_set0_value(gen, GEN_OTHERNAME, oth); + return 1; +} + +int GENERAL_NAME_get0_otherName(GENERAL_NAME *gen, + ASN1_OBJECT **poid, ASN1_TYPE **pvalue) +{ + if (gen->type != GEN_OTHERNAME) + return 0; + if (poid) + *poid = gen->d.otherName->type_id; + if (pvalue) + *pvalue = gen->d.otherName->value; + return 1; +} diff --git a/crypto/x509v3/v3_ia5.c b/crypto/x509v3/v3_ia5.c new file mode 100644 index 000000000..c1170d461 --- /dev/null +++ b/crypto/x509v3/v3_ia5.c @@ -0,0 +1,65 @@ +/* + * Copyright 1999-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#include "internal/cryptlib.h" +#include +#include +#include +#include "ext_dat.h" + +const X509V3_EXT_METHOD v3_ns_ia5_list[8] = { + EXT_IA5STRING(NID_netscape_base_url), + EXT_IA5STRING(NID_netscape_revocation_url), + EXT_IA5STRING(NID_netscape_ca_revocation_url), + EXT_IA5STRING(NID_netscape_renewal_url), + EXT_IA5STRING(NID_netscape_ca_policy_url), + EXT_IA5STRING(NID_netscape_ssl_server_name), + EXT_IA5STRING(NID_netscape_comment), + EXT_END +}; + +char *i2s_ASN1_IA5STRING(X509V3_EXT_METHOD *method, ASN1_IA5STRING *ia5) +{ + char *tmp; + + if (!ia5 || !ia5->length) + return NULL; + if ((tmp = OPENSSL_malloc(ia5->length + 1)) == NULL) { + X509V3err(X509V3_F_I2S_ASN1_IA5STRING, ERR_R_MALLOC_FAILURE); + return NULL; + } + memcpy(tmp, ia5->data, ia5->length); + tmp[ia5->length] = 0; + return tmp; +} + +ASN1_IA5STRING *s2i_ASN1_IA5STRING(X509V3_EXT_METHOD *method, + X509V3_CTX *ctx, const char *str) +{ + ASN1_IA5STRING *ia5; + if (!str) { + X509V3err(X509V3_F_S2I_ASN1_IA5STRING, + X509V3_R_INVALID_NULL_ARGUMENT); + return NULL; + } + if ((ia5 = ASN1_IA5STRING_new()) == NULL) + goto err; + if (!ASN1_STRING_set((ASN1_STRING *)ia5, str, strlen(str))) { + ASN1_IA5STRING_free(ia5); + return NULL; + } +#ifdef CHARSET_EBCDIC + ebcdic2ascii(ia5->data, ia5->data, ia5->length); +#endif /* CHARSET_EBCDIC */ + return ia5; + err: + X509V3err(X509V3_F_S2I_ASN1_IA5STRING, ERR_R_MALLOC_FAILURE); + return NULL; +} diff --git a/crypto/x509v3/v3_info.c b/crypto/x509v3/v3_info.c new file mode 100644 index 000000000..61ef2138b --- /dev/null +++ b/crypto/x509v3/v3_info.c @@ -0,0 +1,157 @@ +/* + * Copyright 1999-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#include "internal/cryptlib.h" +#include +#include +#include +#include +#include "ext_dat.h" + +static STACK_OF(CONF_VALUE) *i2v_AUTHORITY_INFO_ACCESS(X509V3_EXT_METHOD + *method, AUTHORITY_INFO_ACCESS + *ainfo, STACK_OF(CONF_VALUE) + *ret); +static AUTHORITY_INFO_ACCESS *v2i_AUTHORITY_INFO_ACCESS(X509V3_EXT_METHOD + *method, + X509V3_CTX *ctx, + STACK_OF(CONF_VALUE) + *nval); + +const X509V3_EXT_METHOD v3_info = { NID_info_access, X509V3_EXT_MULTILINE, + ASN1_ITEM_ref(AUTHORITY_INFO_ACCESS), + 0, 0, 0, 0, + 0, 0, + (X509V3_EXT_I2V) i2v_AUTHORITY_INFO_ACCESS, + (X509V3_EXT_V2I)v2i_AUTHORITY_INFO_ACCESS, + 0, 0, + NULL +}; + +const X509V3_EXT_METHOD v3_sinfo = { NID_sinfo_access, X509V3_EXT_MULTILINE, + ASN1_ITEM_ref(AUTHORITY_INFO_ACCESS), + 0, 0, 0, 0, + 0, 0, + (X509V3_EXT_I2V) i2v_AUTHORITY_INFO_ACCESS, + (X509V3_EXT_V2I)v2i_AUTHORITY_INFO_ACCESS, + 0, 0, + NULL +}; + +ASN1_SEQUENCE(ACCESS_DESCRIPTION) = { + ASN1_SIMPLE(ACCESS_DESCRIPTION, method, ASN1_OBJECT), + ASN1_SIMPLE(ACCESS_DESCRIPTION, location, GENERAL_NAME) +} ASN1_SEQUENCE_END(ACCESS_DESCRIPTION) + +IMPLEMENT_ASN1_FUNCTIONS(ACCESS_DESCRIPTION) + +ASN1_ITEM_TEMPLATE(AUTHORITY_INFO_ACCESS) = + ASN1_EX_TEMPLATE_TYPE(ASN1_TFLG_SEQUENCE_OF, 0, GeneralNames, ACCESS_DESCRIPTION) +ASN1_ITEM_TEMPLATE_END(AUTHORITY_INFO_ACCESS) + +IMPLEMENT_ASN1_FUNCTIONS(AUTHORITY_INFO_ACCESS) + +static STACK_OF(CONF_VALUE) *i2v_AUTHORITY_INFO_ACCESS(X509V3_EXT_METHOD + *method, AUTHORITY_INFO_ACCESS + *ainfo, STACK_OF(CONF_VALUE) + *ret) +{ + ACCESS_DESCRIPTION *desc; + int i, nlen; + char objtmp[80], *ntmp; + CONF_VALUE *vtmp; + for (i = 0; i < sk_ACCESS_DESCRIPTION_num(ainfo); i++) { + desc = sk_ACCESS_DESCRIPTION_value(ainfo, i); + ret = i2v_GENERAL_NAME(method, desc->location, ret); + if (!ret) + break; + vtmp = sk_CONF_VALUE_value(ret, i); + i2t_ASN1_OBJECT(objtmp, sizeof objtmp, desc->method); + nlen = strlen(objtmp) + strlen(vtmp->name) + 5; + ntmp = OPENSSL_malloc(nlen); + if (ntmp == NULL) { + X509V3err(X509V3_F_I2V_AUTHORITY_INFO_ACCESS, + ERR_R_MALLOC_FAILURE); + return NULL; + } + OPENSSL_strlcpy(ntmp, objtmp, nlen); + OPENSSL_strlcat(ntmp, " - ", nlen); + OPENSSL_strlcat(ntmp, vtmp->name, nlen); + OPENSSL_free(vtmp->name); + vtmp->name = ntmp; + + } + if (!ret) + return sk_CONF_VALUE_new_null(); + return ret; +} + +static AUTHORITY_INFO_ACCESS *v2i_AUTHORITY_INFO_ACCESS(X509V3_EXT_METHOD + *method, + X509V3_CTX *ctx, + STACK_OF(CONF_VALUE) + *nval) +{ + AUTHORITY_INFO_ACCESS *ainfo = NULL; + CONF_VALUE *cnf, ctmp; + ACCESS_DESCRIPTION *acc; + int i, objlen; + char *objtmp, *ptmp; + + if ((ainfo = sk_ACCESS_DESCRIPTION_new_null()) == NULL) { + X509V3err(X509V3_F_V2I_AUTHORITY_INFO_ACCESS, ERR_R_MALLOC_FAILURE); + return NULL; + } + for (i = 0; i < sk_CONF_VALUE_num(nval); i++) { + cnf = sk_CONF_VALUE_value(nval, i); + if ((acc = ACCESS_DESCRIPTION_new()) == NULL + || !sk_ACCESS_DESCRIPTION_push(ainfo, acc)) { + X509V3err(X509V3_F_V2I_AUTHORITY_INFO_ACCESS, + ERR_R_MALLOC_FAILURE); + goto err; + } + ptmp = strchr(cnf->name, ';'); + if (!ptmp) { + X509V3err(X509V3_F_V2I_AUTHORITY_INFO_ACCESS, + X509V3_R_INVALID_SYNTAX); + goto err; + } + objlen = ptmp - cnf->name; + ctmp.name = ptmp + 1; + ctmp.value = cnf->value; + if (!v2i_GENERAL_NAME_ex(acc->location, method, ctx, &ctmp, 0)) + goto err; + if ((objtmp = OPENSSL_strndup(cnf->name, objlen)) == NULL) { + X509V3err(X509V3_F_V2I_AUTHORITY_INFO_ACCESS, + ERR_R_MALLOC_FAILURE); + goto err; + } + acc->method = OBJ_txt2obj(objtmp, 0); + if (!acc->method) { + X509V3err(X509V3_F_V2I_AUTHORITY_INFO_ACCESS, + X509V3_R_BAD_OBJECT); + ERR_add_error_data(2, "value=", objtmp); + OPENSSL_free(objtmp); + goto err; + } + OPENSSL_free(objtmp); + + } + return ainfo; + err: + sk_ACCESS_DESCRIPTION_pop_free(ainfo, ACCESS_DESCRIPTION_free); + return NULL; +} + +int i2a_ACCESS_DESCRIPTION(BIO *bp, const ACCESS_DESCRIPTION *a) +{ + i2a_ASN1_OBJECT(bp, a->method); + return 2; +} diff --git a/crypto/x509v3/v3_int.c b/crypto/x509v3/v3_int.c new file mode 100644 index 000000000..690c90e8f --- /dev/null +++ b/crypto/x509v3/v3_int.c @@ -0,0 +1,43 @@ +/* + * Copyright 1999-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#include "internal/cryptlib.h" +#include +#include "ext_dat.h" + +const X509V3_EXT_METHOD v3_crl_num = { + NID_crl_number, 0, ASN1_ITEM_ref(ASN1_INTEGER), + 0, 0, 0, 0, + (X509V3_EXT_I2S)i2s_ASN1_INTEGER, + 0, + 0, 0, 0, 0, NULL +}; + +const X509V3_EXT_METHOD v3_delta_crl = { + NID_delta_crl, 0, ASN1_ITEM_ref(ASN1_INTEGER), + 0, 0, 0, 0, + (X509V3_EXT_I2S)i2s_ASN1_INTEGER, + 0, + 0, 0, 0, 0, NULL +}; + +static void *s2i_asn1_int(X509V3_EXT_METHOD *meth, X509V3_CTX *ctx, + const char *value) +{ + return s2i_ASN1_INTEGER(meth, value); +} + +const X509V3_EXT_METHOD v3_inhibit_anyp = { + NID_inhibit_any_policy, 0, ASN1_ITEM_ref(ASN1_INTEGER), + 0, 0, 0, 0, + (X509V3_EXT_I2S)i2s_ASN1_INTEGER, + (X509V3_EXT_S2I)s2i_asn1_int, + 0, 0, 0, 0, NULL +}; diff --git a/crypto/x509v3/v3_lib.c b/crypto/x509v3/v3_lib.c new file mode 100644 index 000000000..a3ca720fe --- /dev/null +++ b/crypto/x509v3/v3_lib.c @@ -0,0 +1,360 @@ +/* + * Copyright 1999-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +/* X509 v3 extension utilities */ + +#include +#include "internal/cryptlib.h" +#include +#include + +#include "ext_dat.h" + +static STACK_OF(X509V3_EXT_METHOD) *ext_list = NULL; + +static int ext_cmp(const X509V3_EXT_METHOD *const *a, + const X509V3_EXT_METHOD *const *b); +static void ext_list_free(X509V3_EXT_METHOD *ext); + +int X509V3_EXT_add(X509V3_EXT_METHOD *ext) +{ + if (ext_list == NULL + && (ext_list = sk_X509V3_EXT_METHOD_new(ext_cmp)) == NULL) { + X509V3err(X509V3_F_X509V3_EXT_ADD, ERR_R_MALLOC_FAILURE); + return 0; + } + if (!sk_X509V3_EXT_METHOD_push(ext_list, ext)) { + X509V3err(X509V3_F_X509V3_EXT_ADD, ERR_R_MALLOC_FAILURE); + return 0; + } + return 1; +} + +static int ext_cmp(const X509V3_EXT_METHOD *const *a, + const X509V3_EXT_METHOD *const *b) +{ + return ((*a)->ext_nid - (*b)->ext_nid); +} + +DECLARE_OBJ_BSEARCH_CMP_FN(const X509V3_EXT_METHOD *, + const X509V3_EXT_METHOD *, ext); +IMPLEMENT_OBJ_BSEARCH_CMP_FN(const X509V3_EXT_METHOD *, + const X509V3_EXT_METHOD *, ext); + +/* + * This table will be searched using OBJ_bsearch so it *must* kept in order + * of the ext_nid values. + */ + +static const X509V3_EXT_METHOD *standard_exts[] = { + &v3_nscert, + &v3_ns_ia5_list[0], + &v3_ns_ia5_list[1], + &v3_ns_ia5_list[2], + &v3_ns_ia5_list[3], + &v3_ns_ia5_list[4], + &v3_ns_ia5_list[5], + &v3_ns_ia5_list[6], + &v3_skey_id, + &v3_key_usage, + &v3_pkey_usage_period, + &v3_alt[0], + &v3_alt[1], + &v3_bcons, + &v3_crl_num, + &v3_cpols, + &v3_akey_id, + &v3_crld, + &v3_ext_ku, + &v3_delta_crl, + &v3_crl_reason, +#ifndef OPENSSL_NO_OCSP + &v3_crl_invdate, +#endif + &v3_sxnet, + &v3_info, +#ifndef OPENSSL_NO_RFC3779 + &v3_addr, + &v3_asid, +#endif +#ifndef OPENSSL_NO_OCSP + &v3_ocsp_nonce, + &v3_ocsp_crlid, + &v3_ocsp_accresp, + &v3_ocsp_nocheck, + &v3_ocsp_acutoff, + &v3_ocsp_serviceloc, +#endif + &v3_sinfo, + &v3_policy_constraints, +#ifndef OPENSSL_NO_OCSP + &v3_crl_hold, +#endif + &v3_pci, + &v3_name_constraints, + &v3_policy_mappings, + &v3_inhibit_anyp, + &v3_idp, + &v3_alt[2], + &v3_freshest_crl, +#ifndef OPENSSL_NO_CT + &v3_ct_scts[0], + &v3_ct_scts[1], + &v3_ct_scts[2], +#endif + &v3_tls_feature, +}; + +/* Number of standard extensions */ + +#define STANDARD_EXTENSION_COUNT OSSL_NELEM(standard_exts) + +const X509V3_EXT_METHOD *X509V3_EXT_get_nid(int nid) +{ + X509V3_EXT_METHOD tmp; + const X509V3_EXT_METHOD *t = &tmp, *const *ret; + int idx; + if (nid < 0) + return NULL; + tmp.ext_nid = nid; + ret = OBJ_bsearch_ext(&t, standard_exts, STANDARD_EXTENSION_COUNT); + if (ret) + return *ret; + if (!ext_list) + return NULL; + idx = sk_X509V3_EXT_METHOD_find(ext_list, &tmp); + if (idx == -1) + return NULL; + return sk_X509V3_EXT_METHOD_value(ext_list, idx); +} + +const X509V3_EXT_METHOD *X509V3_EXT_get(X509_EXTENSION *ext) +{ + int nid; + if ((nid = OBJ_obj2nid(X509_EXTENSION_get_object(ext))) == NID_undef) + return NULL; + return X509V3_EXT_get_nid(nid); +} + +int X509V3_EXT_add_list(X509V3_EXT_METHOD *extlist) +{ + for (; extlist->ext_nid != -1; extlist++) + if (!X509V3_EXT_add(extlist)) + return 0; + return 1; +} + +int X509V3_EXT_add_alias(int nid_to, int nid_from) +{ + const X509V3_EXT_METHOD *ext; + X509V3_EXT_METHOD *tmpext; + + if ((ext = X509V3_EXT_get_nid(nid_from)) == NULL) { + X509V3err(X509V3_F_X509V3_EXT_ADD_ALIAS, X509V3_R_EXTENSION_NOT_FOUND); + return 0; + } + if ((tmpext = OPENSSL_malloc(sizeof(*tmpext))) == NULL) { + X509V3err(X509V3_F_X509V3_EXT_ADD_ALIAS, ERR_R_MALLOC_FAILURE); + return 0; + } + *tmpext = *ext; + tmpext->ext_nid = nid_to; + tmpext->ext_flags |= X509V3_EXT_DYNAMIC; + return X509V3_EXT_add(tmpext); +} + +void X509V3_EXT_cleanup(void) +{ + sk_X509V3_EXT_METHOD_pop_free(ext_list, ext_list_free); + ext_list = NULL; +} + +static void ext_list_free(X509V3_EXT_METHOD *ext) +{ + if (ext->ext_flags & X509V3_EXT_DYNAMIC) + OPENSSL_free(ext); +} + +/* + * Legacy function: we don't need to add standard extensions any more because + * they are now kept in ext_dat.h. + */ + +int X509V3_add_standard_extensions(void) +{ + return 1; +} + +/* Return an extension internal structure */ + +void *X509V3_EXT_d2i(X509_EXTENSION *ext) +{ + const X509V3_EXT_METHOD *method; + const unsigned char *p; + ASN1_STRING *extvalue; + int extlen; + + if ((method = X509V3_EXT_get(ext)) == NULL) + return NULL; + extvalue = X509_EXTENSION_get_data(ext); + p = ASN1_STRING_get0_data(extvalue); + extlen = ASN1_STRING_length(extvalue); + if (method->it) + return ASN1_item_d2i(NULL, &p, extlen, ASN1_ITEM_ptr(method->it)); + return method->d2i(NULL, &p, extlen); +} + +/*- + * Get critical flag and decoded version of extension from a NID. + * The "idx" variable returns the last found extension and can + * be used to retrieve multiple extensions of the same NID. + * However multiple extensions with the same NID is usually + * due to a badly encoded certificate so if idx is NULL we + * choke if multiple extensions exist. + * The "crit" variable is set to the critical value. + * The return value is the decoded extension or NULL on + * error. The actual error can have several different causes, + * the value of *crit reflects the cause: + * >= 0, extension found but not decoded (reflects critical value). + * -1 extension not found. + * -2 extension occurs more than once. + */ + +void *X509V3_get_d2i(const STACK_OF(X509_EXTENSION) *x, int nid, int *crit, + int *idx) +{ + int lastpos, i; + X509_EXTENSION *ex, *found_ex = NULL; + if (!x) { + if (idx) + *idx = -1; + if (crit) + *crit = -1; + return NULL; + } + if (idx) + lastpos = *idx + 1; + else + lastpos = 0; + if (lastpos < 0) + lastpos = 0; + for (i = lastpos; i < sk_X509_EXTENSION_num(x); i++) { + ex = sk_X509_EXTENSION_value(x, i); + if (OBJ_obj2nid(X509_EXTENSION_get_object(ex)) == nid) { + if (idx) { + *idx = i; + found_ex = ex; + break; + } else if (found_ex) { + /* Found more than one */ + if (crit) + *crit = -2; + return NULL; + } + found_ex = ex; + } + } + if (found_ex) { + /* Found it */ + if (crit) + *crit = X509_EXTENSION_get_critical(found_ex); + return X509V3_EXT_d2i(found_ex); + } + + /* Extension not found */ + if (idx) + *idx = -1; + if (crit) + *crit = -1; + return NULL; +} + +/* + * This function is a general extension append, replace and delete utility. + * The precise operation is governed by the 'flags' value. The 'crit' and + * 'value' arguments (if relevant) are the extensions internal structure. + */ + +int X509V3_add1_i2d(STACK_OF(X509_EXTENSION) **x, int nid, void *value, + int crit, unsigned long flags) +{ + int extidx = -1; + int errcode; + X509_EXTENSION *ext, *extmp; + unsigned long ext_op = flags & X509V3_ADD_OP_MASK; + + /* + * If appending we don't care if it exists, otherwise look for existing + * extension. + */ + if (ext_op != X509V3_ADD_APPEND) + extidx = X509v3_get_ext_by_NID(*x, nid, -1); + + /* See if extension exists */ + if (extidx >= 0) { + /* If keep existing, nothing to do */ + if (ext_op == X509V3_ADD_KEEP_EXISTING) + return 1; + /* If default then its an error */ + if (ext_op == X509V3_ADD_DEFAULT) { + errcode = X509V3_R_EXTENSION_EXISTS; + goto err; + } + /* If delete, just delete it */ + if (ext_op == X509V3_ADD_DELETE) { + if (!sk_X509_EXTENSION_delete(*x, extidx)) + return -1; + return 1; + } + } else { + /* + * If replace existing or delete, error since extension must exist + */ + if ((ext_op == X509V3_ADD_REPLACE_EXISTING) || + (ext_op == X509V3_ADD_DELETE)) { + errcode = X509V3_R_EXTENSION_NOT_FOUND; + goto err; + } + } + + /* + * If we get this far then we have to create an extension: could have + * some flags for alternative encoding schemes... + */ + + ext = X509V3_EXT_i2d(nid, crit, value); + + if (!ext) { + X509V3err(X509V3_F_X509V3_ADD1_I2D, + X509V3_R_ERROR_CREATING_EXTENSION); + return 0; + } + + /* If extension exists replace it.. */ + if (extidx >= 0) { + extmp = sk_X509_EXTENSION_value(*x, extidx); + X509_EXTENSION_free(extmp); + if (!sk_X509_EXTENSION_set(*x, extidx, ext)) + return -1; + return 1; + } + + if (*x == NULL + && (*x = sk_X509_EXTENSION_new_null()) == NULL) + return -1; + if (!sk_X509_EXTENSION_push(*x, ext)) + return -1; + + return 1; + + err: + if (!(flags & X509V3_ADD_SILENT)) + X509V3err(X509V3_F_X509V3_ADD1_I2D, errcode); + return 0; +} diff --git a/crypto/x509v3/v3_ncons.c b/crypto/x509v3/v3_ncons.c new file mode 100644 index 000000000..9b3bb128e --- /dev/null +++ b/crypto/x509v3/v3_ncons.c @@ -0,0 +1,513 @@ +/* + * Copyright 2003-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#include "internal/cryptlib.h" +#include "internal/asn1_int.h" +#include +#include +#include + +#include "internal/x509_int.h" +#include "ext_dat.h" + +static void *v2i_NAME_CONSTRAINTS(const X509V3_EXT_METHOD *method, + X509V3_CTX *ctx, + STACK_OF(CONF_VALUE) *nval); +static int i2r_NAME_CONSTRAINTS(const X509V3_EXT_METHOD *method, void *a, + BIO *bp, int ind); +static int do_i2r_name_constraints(const X509V3_EXT_METHOD *method, + STACK_OF(GENERAL_SUBTREE) *trees, BIO *bp, + int ind, const char *name); +static int print_nc_ipadd(BIO *bp, ASN1_OCTET_STRING *ip); + +static int nc_match(GENERAL_NAME *gen, NAME_CONSTRAINTS *nc); +static int nc_match_single(GENERAL_NAME *sub, GENERAL_NAME *gen); +static int nc_dn(X509_NAME *sub, X509_NAME *nm); +static int nc_dns(ASN1_IA5STRING *sub, ASN1_IA5STRING *dns); +static int nc_email(ASN1_IA5STRING *sub, ASN1_IA5STRING *eml); +static int nc_uri(ASN1_IA5STRING *uri, ASN1_IA5STRING *base); +static int nc_ip(ASN1_OCTET_STRING *ip, ASN1_OCTET_STRING *base); + +const X509V3_EXT_METHOD v3_name_constraints = { + NID_name_constraints, 0, + ASN1_ITEM_ref(NAME_CONSTRAINTS), + 0, 0, 0, 0, + 0, 0, + 0, v2i_NAME_CONSTRAINTS, + i2r_NAME_CONSTRAINTS, 0, + NULL +}; + +ASN1_SEQUENCE(GENERAL_SUBTREE) = { + ASN1_SIMPLE(GENERAL_SUBTREE, base, GENERAL_NAME), + ASN1_IMP_OPT(GENERAL_SUBTREE, minimum, ASN1_INTEGER, 0), + ASN1_IMP_OPT(GENERAL_SUBTREE, maximum, ASN1_INTEGER, 1) +} ASN1_SEQUENCE_END(GENERAL_SUBTREE) + +ASN1_SEQUENCE(NAME_CONSTRAINTS) = { + ASN1_IMP_SEQUENCE_OF_OPT(NAME_CONSTRAINTS, permittedSubtrees, + GENERAL_SUBTREE, 0), + ASN1_IMP_SEQUENCE_OF_OPT(NAME_CONSTRAINTS, excludedSubtrees, + GENERAL_SUBTREE, 1), +} ASN1_SEQUENCE_END(NAME_CONSTRAINTS) + + +IMPLEMENT_ASN1_ALLOC_FUNCTIONS(GENERAL_SUBTREE) +IMPLEMENT_ASN1_ALLOC_FUNCTIONS(NAME_CONSTRAINTS) + +static void *v2i_NAME_CONSTRAINTS(const X509V3_EXT_METHOD *method, + X509V3_CTX *ctx, STACK_OF(CONF_VALUE) *nval) +{ + int i; + CONF_VALUE tval, *val; + STACK_OF(GENERAL_SUBTREE) **ptree = NULL; + NAME_CONSTRAINTS *ncons = NULL; + GENERAL_SUBTREE *sub = NULL; + + ncons = NAME_CONSTRAINTS_new(); + if (ncons == NULL) + goto memerr; + for (i = 0; i < sk_CONF_VALUE_num(nval); i++) { + val = sk_CONF_VALUE_value(nval, i); + if (strncmp(val->name, "permitted", 9) == 0 && val->name[9]) { + ptree = &ncons->permittedSubtrees; + tval.name = val->name + 10; + } else if (strncmp(val->name, "excluded", 8) == 0 && val->name[8]) { + ptree = &ncons->excludedSubtrees; + tval.name = val->name + 9; + } else { + X509V3err(X509V3_F_V2I_NAME_CONSTRAINTS, X509V3_R_INVALID_SYNTAX); + goto err; + } + tval.value = val->value; + sub = GENERAL_SUBTREE_new(); + if (sub == NULL) + goto memerr; + if (!v2i_GENERAL_NAME_ex(sub->base, method, ctx, &tval, 1)) + goto err; + if (*ptree == NULL) + *ptree = sk_GENERAL_SUBTREE_new_null(); + if (*ptree == NULL || !sk_GENERAL_SUBTREE_push(*ptree, sub)) + goto memerr; + sub = NULL; + } + + return ncons; + + memerr: + X509V3err(X509V3_F_V2I_NAME_CONSTRAINTS, ERR_R_MALLOC_FAILURE); + err: + NAME_CONSTRAINTS_free(ncons); + GENERAL_SUBTREE_free(sub); + + return NULL; +} + +static int i2r_NAME_CONSTRAINTS(const X509V3_EXT_METHOD *method, void *a, + BIO *bp, int ind) +{ + NAME_CONSTRAINTS *ncons = a; + do_i2r_name_constraints(method, ncons->permittedSubtrees, + bp, ind, "Permitted"); + do_i2r_name_constraints(method, ncons->excludedSubtrees, + bp, ind, "Excluded"); + return 1; +} + +static int do_i2r_name_constraints(const X509V3_EXT_METHOD *method, + STACK_OF(GENERAL_SUBTREE) *trees, + BIO *bp, int ind, const char *name) +{ + GENERAL_SUBTREE *tree; + int i; + if (sk_GENERAL_SUBTREE_num(trees) > 0) + BIO_printf(bp, "%*s%s:\n", ind, "", name); + for (i = 0; i < sk_GENERAL_SUBTREE_num(trees); i++) { + tree = sk_GENERAL_SUBTREE_value(trees, i); + BIO_printf(bp, "%*s", ind + 2, ""); + if (tree->base->type == GEN_IPADD) + print_nc_ipadd(bp, tree->base->d.ip); + else + GENERAL_NAME_print(bp, tree->base); + BIO_puts(bp, "\n"); + } + return 1; +} + +static int print_nc_ipadd(BIO *bp, ASN1_OCTET_STRING *ip) +{ + int i, len; + unsigned char *p; + p = ip->data; + len = ip->length; + BIO_puts(bp, "IP:"); + if (len == 8) { + BIO_printf(bp, "%d.%d.%d.%d/%d.%d.%d.%d", + p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]); + } else if (len == 32) { + for (i = 0; i < 16; i++) { + BIO_printf(bp, "%X", p[0] << 8 | p[1]); + p += 2; + if (i == 7) + BIO_puts(bp, "/"); + else if (i != 15) + BIO_puts(bp, ":"); + } + } else + BIO_printf(bp, "IP Address:"); + return 1; +} + +/*- + * Check a certificate conforms to a specified set of constraints. + * Return values: + * X509_V_OK: All constraints obeyed. + * X509_V_ERR_PERMITTED_VIOLATION: Permitted subtree violation. + * X509_V_ERR_EXCLUDED_VIOLATION: Excluded subtree violation. + * X509_V_ERR_SUBTREE_MINMAX: Min or max values present and matching type. + * X509_V_ERR_UNSUPPORTED_CONSTRAINT_TYPE: Unsupported constraint type. + * X509_V_ERR_UNSUPPORTED_CONSTRAINT_SYNTAX: bad unsupported constraint syntax. + * X509_V_ERR_UNSUPPORTED_NAME_SYNTAX: bad or unsupported syntax of name + */ + +int NAME_CONSTRAINTS_check(X509 *x, NAME_CONSTRAINTS *nc) +{ + int r, i; + X509_NAME *nm; + + nm = X509_get_subject_name(x); + + if (X509_NAME_entry_count(nm) > 0) { + GENERAL_NAME gntmp; + gntmp.type = GEN_DIRNAME; + gntmp.d.directoryName = nm; + + r = nc_match(&gntmp, nc); + + if (r != X509_V_OK) + return r; + + gntmp.type = GEN_EMAIL; + + /* Process any email address attributes in subject name */ + + for (i = -1;;) { + const X509_NAME_ENTRY *ne; + + i = X509_NAME_get_index_by_NID(nm, NID_pkcs9_emailAddress, i); + if (i == -1) + break; + ne = X509_NAME_get_entry(nm, i); + gntmp.d.rfc822Name = X509_NAME_ENTRY_get_data(ne); + if (gntmp.d.rfc822Name->type != V_ASN1_IA5STRING) + return X509_V_ERR_UNSUPPORTED_NAME_SYNTAX; + + r = nc_match(&gntmp, nc); + + if (r != X509_V_OK) + return r; + } + + } + + for (i = 0; i < sk_GENERAL_NAME_num(x->altname); i++) { + GENERAL_NAME *gen = sk_GENERAL_NAME_value(x->altname, i); + r = nc_match(gen, nc); + if (r != X509_V_OK) + return r; + } + + return X509_V_OK; + +} + +int NAME_CONSTRAINTS_check_CN(X509 *x, NAME_CONSTRAINTS *nc) +{ + int r, i; + X509_NAME *nm; + + ASN1_STRING stmp; + GENERAL_NAME gntmp; + stmp.flags = 0; + stmp.type = V_ASN1_IA5STRING; + gntmp.type = GEN_DNS; + gntmp.d.dNSName = &stmp; + + nm = X509_get_subject_name(x); + + /* Process any commonName attributes in subject name */ + + for (i = -1;;) { + X509_NAME_ENTRY *ne; + ASN1_STRING *hn; + i = X509_NAME_get_index_by_NID(nm, NID_commonName, i); + if (i == -1) + break; + ne = X509_NAME_get_entry(nm, i); + hn = X509_NAME_ENTRY_get_data(ne); + /* Only process attributes that look like host names */ + if (asn1_valid_host(hn)) { + unsigned char *h; + int hlen = ASN1_STRING_to_UTF8(&h, hn); + if (hlen <= 0) + return X509_V_ERR_OUT_OF_MEM; + + stmp.length = hlen; + stmp.data = h; + + r = nc_match(&gntmp, nc); + + OPENSSL_free(h); + + if (r != X509_V_OK) + return r; + } + } + return X509_V_OK; +} + +static int nc_match(GENERAL_NAME *gen, NAME_CONSTRAINTS *nc) +{ + GENERAL_SUBTREE *sub; + int i, r, match = 0; + + /* + * Permitted subtrees: if any subtrees exist of matching the type at + * least one subtree must match. + */ + + for (i = 0; i < sk_GENERAL_SUBTREE_num(nc->permittedSubtrees); i++) { + sub = sk_GENERAL_SUBTREE_value(nc->permittedSubtrees, i); + if (gen->type != sub->base->type) + continue; + if (sub->minimum || sub->maximum) + return X509_V_ERR_SUBTREE_MINMAX; + /* If we already have a match don't bother trying any more */ + if (match == 2) + continue; + if (match == 0) + match = 1; + r = nc_match_single(gen, sub->base); + if (r == X509_V_OK) + match = 2; + else if (r != X509_V_ERR_PERMITTED_VIOLATION) + return r; + } + + if (match == 1) + return X509_V_ERR_PERMITTED_VIOLATION; + + /* Excluded subtrees: must not match any of these */ + + for (i = 0; i < sk_GENERAL_SUBTREE_num(nc->excludedSubtrees); i++) { + sub = sk_GENERAL_SUBTREE_value(nc->excludedSubtrees, i); + if (gen->type != sub->base->type) + continue; + if (sub->minimum || sub->maximum) + return X509_V_ERR_SUBTREE_MINMAX; + + r = nc_match_single(gen, sub->base); + if (r == X509_V_OK) + return X509_V_ERR_EXCLUDED_VIOLATION; + else if (r != X509_V_ERR_PERMITTED_VIOLATION) + return r; + + } + + return X509_V_OK; + +} + +static int nc_match_single(GENERAL_NAME *gen, GENERAL_NAME *base) +{ + switch (base->type) { + case GEN_DIRNAME: + return nc_dn(gen->d.directoryName, base->d.directoryName); + + case GEN_DNS: + return nc_dns(gen->d.dNSName, base->d.dNSName); + + case GEN_EMAIL: + return nc_email(gen->d.rfc822Name, base->d.rfc822Name); + + case GEN_URI: + return nc_uri(gen->d.uniformResourceIdentifier, + base->d.uniformResourceIdentifier); + + case GEN_IPADD: + return nc_ip(gen->d.iPAddress, base->d.iPAddress); + + default: + return X509_V_ERR_UNSUPPORTED_CONSTRAINT_TYPE; + } + +} + +/* + * directoryName name constraint matching. The canonical encoding of + * X509_NAME makes this comparison easy. It is matched if the subtree is a + * subset of the name. + */ + +static int nc_dn(X509_NAME *nm, X509_NAME *base) +{ + /* Ensure canonical encodings are up to date. */ + if (nm->modified && i2d_X509_NAME(nm, NULL) < 0) + return X509_V_ERR_OUT_OF_MEM; + if (base->modified && i2d_X509_NAME(base, NULL) < 0) + return X509_V_ERR_OUT_OF_MEM; + if (base->canon_enclen > nm->canon_enclen) + return X509_V_ERR_PERMITTED_VIOLATION; + if (memcmp(base->canon_enc, nm->canon_enc, base->canon_enclen)) + return X509_V_ERR_PERMITTED_VIOLATION; + return X509_V_OK; +} + +static int nc_dns(ASN1_IA5STRING *dns, ASN1_IA5STRING *base) +{ + char *baseptr = (char *)base->data; + char *dnsptr = (char *)dns->data; + /* Empty matches everything */ + if (!*baseptr) + return X509_V_OK; + /* + * Otherwise can add zero or more components on the left so compare RHS + * and if dns is longer and expect '.' as preceding character. + */ + if (dns->length > base->length) { + dnsptr += dns->length - base->length; + if (*baseptr != '.' && dnsptr[-1] != '.') + return X509_V_ERR_PERMITTED_VIOLATION; + } + + if (strcasecmp(baseptr, dnsptr)) + return X509_V_ERR_PERMITTED_VIOLATION; + + return X509_V_OK; + +} + +static int nc_email(ASN1_IA5STRING *eml, ASN1_IA5STRING *base) +{ + const char *baseptr = (char *)base->data; + const char *emlptr = (char *)eml->data; + + const char *baseat = strchr(baseptr, '@'); + const char *emlat = strchr(emlptr, '@'); + if (!emlat) + return X509_V_ERR_UNSUPPORTED_NAME_SYNTAX; + /* Special case: initial '.' is RHS match */ + if (!baseat && (*baseptr == '.')) { + if (eml->length > base->length) { + emlptr += eml->length - base->length; + if (strcasecmp(baseptr, emlptr) == 0) + return X509_V_OK; + } + return X509_V_ERR_PERMITTED_VIOLATION; + } + + /* If we have anything before '@' match local part */ + + if (baseat) { + if (baseat != baseptr) { + if ((baseat - baseptr) != (emlat - emlptr)) + return X509_V_ERR_PERMITTED_VIOLATION; + /* Case sensitive match of local part */ + if (strncmp(baseptr, emlptr, emlat - emlptr)) + return X509_V_ERR_PERMITTED_VIOLATION; + } + /* Position base after '@' */ + baseptr = baseat + 1; + } + emlptr = emlat + 1; + /* Just have hostname left to match: case insensitive */ + if (strcasecmp(baseptr, emlptr)) + return X509_V_ERR_PERMITTED_VIOLATION; + + return X509_V_OK; + +} + +static int nc_uri(ASN1_IA5STRING *uri, ASN1_IA5STRING *base) +{ + const char *baseptr = (char *)base->data; + const char *hostptr = (char *)uri->data; + const char *p = strchr(hostptr, ':'); + int hostlen; + /* Check for foo:// and skip past it */ + if (!p || (p[1] != '/') || (p[2] != '/')) + return X509_V_ERR_UNSUPPORTED_NAME_SYNTAX; + hostptr = p + 3; + + /* Determine length of hostname part of URI */ + + /* Look for a port indicator as end of hostname first */ + + p = strchr(hostptr, ':'); + /* Otherwise look for trailing slash */ + if (!p) + p = strchr(hostptr, '/'); + + if (!p) + hostlen = strlen(hostptr); + else + hostlen = p - hostptr; + + if (hostlen == 0) + return X509_V_ERR_UNSUPPORTED_NAME_SYNTAX; + + /* Special case: initial '.' is RHS match */ + if (*baseptr == '.') { + if (hostlen > base->length) { + p = hostptr + hostlen - base->length; + if (strncasecmp(p, baseptr, base->length) == 0) + return X509_V_OK; + } + return X509_V_ERR_PERMITTED_VIOLATION; + } + + if ((base->length != (int)hostlen) + || strncasecmp(hostptr, baseptr, hostlen)) + return X509_V_ERR_PERMITTED_VIOLATION; + + return X509_V_OK; + +} + +static int nc_ip(ASN1_OCTET_STRING *ip, ASN1_OCTET_STRING *base) +{ + int hostlen, baselen, i; + unsigned char *hostptr, *baseptr, *maskptr; + hostptr = ip->data; + hostlen = ip->length; + baseptr = base->data; + baselen = base->length; + + /* Invalid if not IPv4 or IPv6 */ + if (!((hostlen == 4) || (hostlen == 16))) + return X509_V_ERR_UNSUPPORTED_NAME_SYNTAX; + if (!((baselen == 8) || (baselen == 32))) + return X509_V_ERR_UNSUPPORTED_NAME_SYNTAX; + + /* Do not match IPv4 with IPv6 */ + if (hostlen * 2 != baselen) + return X509_V_ERR_PERMITTED_VIOLATION; + + maskptr = base->data + hostlen; + + /* Considering possible not aligned base ipAddress */ + /* Not checking for wrong mask definition: i.e.: 255.0.255.0 */ + for (i = 0; i < hostlen; i++) + if ((hostptr[i] & maskptr[i]) != (baseptr[i] & maskptr[i])) + return X509_V_ERR_PERMITTED_VIOLATION; + + return X509_V_OK; + +} diff --git a/crypto/x509v3/v3_pci.c b/crypto/x509v3/v3_pci.c new file mode 100644 index 000000000..2c05edb82 --- /dev/null +++ b/crypto/x509v3/v3_pci.c @@ -0,0 +1,321 @@ +/* + * Copyright 2004-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +/* Copyright (c) 2004 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include "internal/cryptlib.h" +#include +#include +#include "ext_dat.h" + +static int i2r_pci(X509V3_EXT_METHOD *method, PROXY_CERT_INFO_EXTENSION *ext, + BIO *out, int indent); +static PROXY_CERT_INFO_EXTENSION *r2i_pci(X509V3_EXT_METHOD *method, + X509V3_CTX *ctx, char *str); + +const X509V3_EXT_METHOD v3_pci = + { NID_proxyCertInfo, 0, ASN1_ITEM_ref(PROXY_CERT_INFO_EXTENSION), + 0, 0, 0, 0, + 0, 0, + NULL, NULL, + (X509V3_EXT_I2R)i2r_pci, + (X509V3_EXT_R2I)r2i_pci, + NULL, +}; + +static int i2r_pci(X509V3_EXT_METHOD *method, PROXY_CERT_INFO_EXTENSION *pci, + BIO *out, int indent) +{ + BIO_printf(out, "%*sPath Length Constraint: ", indent, ""); + if (pci->pcPathLengthConstraint) + i2a_ASN1_INTEGER(out, pci->pcPathLengthConstraint); + else + BIO_printf(out, "infinite"); + BIO_puts(out, "\n"); + BIO_printf(out, "%*sPolicy Language: ", indent, ""); + i2a_ASN1_OBJECT(out, pci->proxyPolicy->policyLanguage); + BIO_puts(out, "\n"); + if (pci->proxyPolicy->policy && pci->proxyPolicy->policy->data) + BIO_printf(out, "%*sPolicy Text: %s\n", indent, "", + pci->proxyPolicy->policy->data); + return 1; +} + +static int process_pci_value(CONF_VALUE *val, + ASN1_OBJECT **language, ASN1_INTEGER **pathlen, + ASN1_OCTET_STRING **policy) +{ + int free_policy = 0; + + if (strcmp(val->name, "language") == 0) { + if (*language) { + X509V3err(X509V3_F_PROCESS_PCI_VALUE, + X509V3_R_POLICY_LANGUAGE_ALREADY_DEFINED); + X509V3_conf_err(val); + return 0; + } + if ((*language = OBJ_txt2obj(val->value, 0)) == NULL) { + X509V3err(X509V3_F_PROCESS_PCI_VALUE, + X509V3_R_INVALID_OBJECT_IDENTIFIER); + X509V3_conf_err(val); + return 0; + } + } else if (strcmp(val->name, "pathlen") == 0) { + if (*pathlen) { + X509V3err(X509V3_F_PROCESS_PCI_VALUE, + X509V3_R_POLICY_PATH_LENGTH_ALREADY_DEFINED); + X509V3_conf_err(val); + return 0; + } + if (!X509V3_get_value_int(val, pathlen)) { + X509V3err(X509V3_F_PROCESS_PCI_VALUE, + X509V3_R_POLICY_PATH_LENGTH); + X509V3_conf_err(val); + return 0; + } + } else if (strcmp(val->name, "policy") == 0) { + unsigned char *tmp_data = NULL; + long val_len; + if (!*policy) { + *policy = ASN1_OCTET_STRING_new(); + if (*policy == NULL) { + X509V3err(X509V3_F_PROCESS_PCI_VALUE, ERR_R_MALLOC_FAILURE); + X509V3_conf_err(val); + return 0; + } + free_policy = 1; + } + if (strncmp(val->value, "hex:", 4) == 0) { + unsigned char *tmp_data2 = + OPENSSL_hexstr2buf(val->value + 4, &val_len); + + if (!tmp_data2) { + X509V3_conf_err(val); + goto err; + } + + tmp_data = OPENSSL_realloc((*policy)->data, + (*policy)->length + val_len + 1); + if (tmp_data) { + (*policy)->data = tmp_data; + memcpy(&(*policy)->data[(*policy)->length], + tmp_data2, val_len); + (*policy)->length += val_len; + (*policy)->data[(*policy)->length] = '\0'; + } else { + OPENSSL_free(tmp_data2); + /* + * realloc failure implies the original data space is b0rked + * too! + */ + OPENSSL_free((*policy)->data); + (*policy)->data = NULL; + (*policy)->length = 0; + X509V3err(X509V3_F_PROCESS_PCI_VALUE, ERR_R_MALLOC_FAILURE); + X509V3_conf_err(val); + goto err; + } + OPENSSL_free(tmp_data2); + } else if (strncmp(val->value, "file:", 5) == 0) { + unsigned char buf[2048]; + int n; + BIO *b = BIO_new_file(val->value + 5, "r"); + if (!b) { + X509V3err(X509V3_F_PROCESS_PCI_VALUE, ERR_R_BIO_LIB); + X509V3_conf_err(val); + goto err; + } + while ((n = BIO_read(b, buf, sizeof(buf))) > 0 + || (n == 0 && BIO_should_retry(b))) { + if (!n) + continue; + + tmp_data = OPENSSL_realloc((*policy)->data, + (*policy)->length + n + 1); + + if (!tmp_data) { + OPENSSL_free((*policy)->data); + (*policy)->data = NULL; + (*policy)->length = 0; + X509V3err(X509V3_F_PROCESS_PCI_VALUE, + ERR_R_MALLOC_FAILURE); + X509V3_conf_err(val); + BIO_free_all(b); + goto err; + } + + (*policy)->data = tmp_data; + memcpy(&(*policy)->data[(*policy)->length], buf, n); + (*policy)->length += n; + (*policy)->data[(*policy)->length] = '\0'; + } + BIO_free_all(b); + + if (n < 0) { + X509V3err(X509V3_F_PROCESS_PCI_VALUE, ERR_R_BIO_LIB); + X509V3_conf_err(val); + goto err; + } + } else if (strncmp(val->value, "text:", 5) == 0) { + val_len = strlen(val->value + 5); + tmp_data = OPENSSL_realloc((*policy)->data, + (*policy)->length + val_len + 1); + if (tmp_data) { + (*policy)->data = tmp_data; + memcpy(&(*policy)->data[(*policy)->length], + val->value + 5, val_len); + (*policy)->length += val_len; + (*policy)->data[(*policy)->length] = '\0'; + } else { + /* + * realloc failure implies the original data space is b0rked + * too! + */ + OPENSSL_free((*policy)->data); + (*policy)->data = NULL; + (*policy)->length = 0; + X509V3err(X509V3_F_PROCESS_PCI_VALUE, ERR_R_MALLOC_FAILURE); + X509V3_conf_err(val); + goto err; + } + } else { + X509V3err(X509V3_F_PROCESS_PCI_VALUE, + X509V3_R_INCORRECT_POLICY_SYNTAX_TAG); + X509V3_conf_err(val); + goto err; + } + if (!tmp_data) { + X509V3err(X509V3_F_PROCESS_PCI_VALUE, ERR_R_MALLOC_FAILURE); + X509V3_conf_err(val); + goto err; + } + } + return 1; + err: + if (free_policy) { + ASN1_OCTET_STRING_free(*policy); + *policy = NULL; + } + return 0; +} + +static PROXY_CERT_INFO_EXTENSION *r2i_pci(X509V3_EXT_METHOD *method, + X509V3_CTX *ctx, char *value) +{ + PROXY_CERT_INFO_EXTENSION *pci = NULL; + STACK_OF(CONF_VALUE) *vals; + ASN1_OBJECT *language = NULL; + ASN1_INTEGER *pathlen = NULL; + ASN1_OCTET_STRING *policy = NULL; + int i, j; + + vals = X509V3_parse_list(value); + for (i = 0; i < sk_CONF_VALUE_num(vals); i++) { + CONF_VALUE *cnf = sk_CONF_VALUE_value(vals, i); + if (!cnf->name || (*cnf->name != '@' && !cnf->value)) { + X509V3err(X509V3_F_R2I_PCI, + X509V3_R_INVALID_PROXY_POLICY_SETTING); + X509V3_conf_err(cnf); + goto err; + } + if (*cnf->name == '@') { + STACK_OF(CONF_VALUE) *sect; + int success_p = 1; + + sect = X509V3_get_section(ctx, cnf->name + 1); + if (!sect) { + X509V3err(X509V3_F_R2I_PCI, X509V3_R_INVALID_SECTION); + X509V3_conf_err(cnf); + goto err; + } + for (j = 0; success_p && j < sk_CONF_VALUE_num(sect); j++) { + success_p = + process_pci_value(sk_CONF_VALUE_value(sect, j), + &language, &pathlen, &policy); + } + X509V3_section_free(ctx, sect); + if (!success_p) + goto err; + } else { + if (!process_pci_value(cnf, &language, &pathlen, &policy)) { + X509V3_conf_err(cnf); + goto err; + } + } + } + + /* Language is mandatory */ + if (!language) { + X509V3err(X509V3_F_R2I_PCI, + X509V3_R_NO_PROXY_CERT_POLICY_LANGUAGE_DEFINED); + goto err; + } + i = OBJ_obj2nid(language); + if ((i == NID_Independent || i == NID_id_ppl_inheritAll) && policy) { + X509V3err(X509V3_F_R2I_PCI, + X509V3_R_POLICY_WHEN_PROXY_LANGUAGE_REQUIRES_NO_POLICY); + goto err; + } + + pci = PROXY_CERT_INFO_EXTENSION_new(); + if (pci == NULL) { + X509V3err(X509V3_F_R2I_PCI, ERR_R_MALLOC_FAILURE); + goto err; + } + + pci->proxyPolicy->policyLanguage = language; + language = NULL; + pci->proxyPolicy->policy = policy; + policy = NULL; + pci->pcPathLengthConstraint = pathlen; + pathlen = NULL; + goto end; + err: + ASN1_OBJECT_free(language); + ASN1_INTEGER_free(pathlen); + pathlen = NULL; + ASN1_OCTET_STRING_free(policy); + policy = NULL; + PROXY_CERT_INFO_EXTENSION_free(pci); + pci = NULL; + end: + sk_CONF_VALUE_pop_free(vals, X509V3_conf_free); + return pci; +} diff --git a/crypto/x509v3/v3_pcia.c b/crypto/x509v3/v3_pcia.c new file mode 100644 index 000000000..e6f7a9179 --- /dev/null +++ b/crypto/x509v3/v3_pcia.c @@ -0,0 +1,60 @@ +/* + * Copyright 2004-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +/* Copyright (c) 2004 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include + +ASN1_SEQUENCE(PROXY_POLICY) = + { + ASN1_SIMPLE(PROXY_POLICY,policyLanguage,ASN1_OBJECT), + ASN1_OPT(PROXY_POLICY,policy,ASN1_OCTET_STRING) +} ASN1_SEQUENCE_END(PROXY_POLICY) + +IMPLEMENT_ASN1_FUNCTIONS(PROXY_POLICY) + +ASN1_SEQUENCE(PROXY_CERT_INFO_EXTENSION) = + { + ASN1_OPT(PROXY_CERT_INFO_EXTENSION,pcPathLengthConstraint,ASN1_INTEGER), + ASN1_SIMPLE(PROXY_CERT_INFO_EXTENSION,proxyPolicy,PROXY_POLICY) +} ASN1_SEQUENCE_END(PROXY_CERT_INFO_EXTENSION) + +IMPLEMENT_ASN1_FUNCTIONS(PROXY_CERT_INFO_EXTENSION) diff --git a/crypto/x509v3/v3_pcons.c b/crypto/x509v3/v3_pcons.c new file mode 100644 index 000000000..24f7ff49e --- /dev/null +++ b/crypto/x509v3/v3_pcons.c @@ -0,0 +1,91 @@ +/* + * Copyright 2003-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#include "internal/cryptlib.h" +#include +#include +#include +#include +#include "ext_dat.h" + +static STACK_OF(CONF_VALUE) *i2v_POLICY_CONSTRAINTS(const X509V3_EXT_METHOD + *method, void *bcons, STACK_OF(CONF_VALUE) + *extlist); +static void *v2i_POLICY_CONSTRAINTS(const X509V3_EXT_METHOD *method, + X509V3_CTX *ctx, + STACK_OF(CONF_VALUE) *values); + +const X509V3_EXT_METHOD v3_policy_constraints = { + NID_policy_constraints, 0, + ASN1_ITEM_ref(POLICY_CONSTRAINTS), + 0, 0, 0, 0, + 0, 0, + i2v_POLICY_CONSTRAINTS, + v2i_POLICY_CONSTRAINTS, + NULL, NULL, + NULL +}; + +ASN1_SEQUENCE(POLICY_CONSTRAINTS) = { + ASN1_IMP_OPT(POLICY_CONSTRAINTS, requireExplicitPolicy, ASN1_INTEGER,0), + ASN1_IMP_OPT(POLICY_CONSTRAINTS, inhibitPolicyMapping, ASN1_INTEGER,1) +} ASN1_SEQUENCE_END(POLICY_CONSTRAINTS) + +IMPLEMENT_ASN1_ALLOC_FUNCTIONS(POLICY_CONSTRAINTS) + +static STACK_OF(CONF_VALUE) *i2v_POLICY_CONSTRAINTS(const X509V3_EXT_METHOD + *method, void *a, STACK_OF(CONF_VALUE) + *extlist) +{ + POLICY_CONSTRAINTS *pcons = a; + X509V3_add_value_int("Require Explicit Policy", + pcons->requireExplicitPolicy, &extlist); + X509V3_add_value_int("Inhibit Policy Mapping", + pcons->inhibitPolicyMapping, &extlist); + return extlist; +} + +static void *v2i_POLICY_CONSTRAINTS(const X509V3_EXT_METHOD *method, + X509V3_CTX *ctx, + STACK_OF(CONF_VALUE) *values) +{ + POLICY_CONSTRAINTS *pcons = NULL; + CONF_VALUE *val; + int i; + + if ((pcons = POLICY_CONSTRAINTS_new()) == NULL) { + X509V3err(X509V3_F_V2I_POLICY_CONSTRAINTS, ERR_R_MALLOC_FAILURE); + return NULL; + } + for (i = 0; i < sk_CONF_VALUE_num(values); i++) { + val = sk_CONF_VALUE_value(values, i); + if (strcmp(val->name, "requireExplicitPolicy") == 0) { + if (!X509V3_get_value_int(val, &pcons->requireExplicitPolicy)) + goto err; + } else if (strcmp(val->name, "inhibitPolicyMapping") == 0) { + if (!X509V3_get_value_int(val, &pcons->inhibitPolicyMapping)) + goto err; + } else { + X509V3err(X509V3_F_V2I_POLICY_CONSTRAINTS, X509V3_R_INVALID_NAME); + X509V3_conf_err(val); + goto err; + } + } + if (!pcons->inhibitPolicyMapping && !pcons->requireExplicitPolicy) { + X509V3err(X509V3_F_V2I_POLICY_CONSTRAINTS, + X509V3_R_ILLEGAL_EMPTY_EXTENSION); + goto err; + } + + return pcons; + err: + POLICY_CONSTRAINTS_free(pcons); + return NULL; +} diff --git a/crypto/x509v3/v3_pku.c b/crypto/x509v3/v3_pku.c new file mode 100644 index 000000000..ed82bca8b --- /dev/null +++ b/crypto/x509v3/v3_pku.c @@ -0,0 +1,65 @@ +/* + * Copyright 1999-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#include "internal/cryptlib.h" +#include +#include +#include +#include "ext_dat.h" + +static int i2r_PKEY_USAGE_PERIOD(X509V3_EXT_METHOD *method, + PKEY_USAGE_PERIOD *usage, BIO *out, + int indent); +/* + * static PKEY_USAGE_PERIOD *v2i_PKEY_USAGE_PERIOD(X509V3_EXT_METHOD *method, + * X509V3_CTX *ctx, STACK_OF(CONF_VALUE) *values); + */ +const X509V3_EXT_METHOD v3_pkey_usage_period = { + NID_private_key_usage_period, 0, ASN1_ITEM_ref(PKEY_USAGE_PERIOD), + 0, 0, 0, 0, + 0, 0, 0, 0, + (X509V3_EXT_I2R)i2r_PKEY_USAGE_PERIOD, NULL, + NULL +}; + +ASN1_SEQUENCE(PKEY_USAGE_PERIOD) = { + ASN1_IMP_OPT(PKEY_USAGE_PERIOD, notBefore, ASN1_GENERALIZEDTIME, 0), + ASN1_IMP_OPT(PKEY_USAGE_PERIOD, notAfter, ASN1_GENERALIZEDTIME, 1) +} ASN1_SEQUENCE_END(PKEY_USAGE_PERIOD) + +IMPLEMENT_ASN1_FUNCTIONS(PKEY_USAGE_PERIOD) + +static int i2r_PKEY_USAGE_PERIOD(X509V3_EXT_METHOD *method, + PKEY_USAGE_PERIOD *usage, BIO *out, + int indent) +{ + BIO_printf(out, "%*s", indent, ""); + if (usage->notBefore) { + BIO_write(out, "Not Before: ", 12); + ASN1_GENERALIZEDTIME_print(out, usage->notBefore); + if (usage->notAfter) + BIO_write(out, ", ", 2); + } + if (usage->notAfter) { + BIO_write(out, "Not After: ", 11); + ASN1_GENERALIZEDTIME_print(out, usage->notAfter); + } + return 1; +} + +/*- +static PKEY_USAGE_PERIOD *v2i_PKEY_USAGE_PERIOD(method, ctx, values) +X509V3_EXT_METHOD *method; +X509V3_CTX *ctx; +STACK_OF(CONF_VALUE) *values; +{ +return NULL; +} +*/ diff --git a/crypto/x509v3/v3_pmaps.c b/crypto/x509v3/v3_pmaps.c new file mode 100644 index 000000000..73f4ec246 --- /dev/null +++ b/crypto/x509v3/v3_pmaps.c @@ -0,0 +1,110 @@ +/* + * Copyright 2003-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#include "internal/cryptlib.h" +#include +#include +#include +#include "ext_dat.h" + +static void *v2i_POLICY_MAPPINGS(const X509V3_EXT_METHOD *method, + X509V3_CTX *ctx, STACK_OF(CONF_VALUE) *nval); +static STACK_OF(CONF_VALUE) *i2v_POLICY_MAPPINGS(const X509V3_EXT_METHOD + *method, void *pmps, STACK_OF(CONF_VALUE) + *extlist); + +const X509V3_EXT_METHOD v3_policy_mappings = { + NID_policy_mappings, 0, + ASN1_ITEM_ref(POLICY_MAPPINGS), + 0, 0, 0, 0, + 0, 0, + i2v_POLICY_MAPPINGS, + v2i_POLICY_MAPPINGS, + 0, 0, + NULL +}; + +ASN1_SEQUENCE(POLICY_MAPPING) = { + ASN1_SIMPLE(POLICY_MAPPING, issuerDomainPolicy, ASN1_OBJECT), + ASN1_SIMPLE(POLICY_MAPPING, subjectDomainPolicy, ASN1_OBJECT) +} ASN1_SEQUENCE_END(POLICY_MAPPING) + +ASN1_ITEM_TEMPLATE(POLICY_MAPPINGS) = + ASN1_EX_TEMPLATE_TYPE(ASN1_TFLG_SEQUENCE_OF, 0, POLICY_MAPPINGS, + POLICY_MAPPING) +ASN1_ITEM_TEMPLATE_END(POLICY_MAPPINGS) + +IMPLEMENT_ASN1_ALLOC_FUNCTIONS(POLICY_MAPPING) + +static STACK_OF(CONF_VALUE) *i2v_POLICY_MAPPINGS(const X509V3_EXT_METHOD + *method, void *a, STACK_OF(CONF_VALUE) + *ext_list) +{ + POLICY_MAPPINGS *pmaps = a; + POLICY_MAPPING *pmap; + int i; + char obj_tmp1[80]; + char obj_tmp2[80]; + for (i = 0; i < sk_POLICY_MAPPING_num(pmaps); i++) { + pmap = sk_POLICY_MAPPING_value(pmaps, i); + i2t_ASN1_OBJECT(obj_tmp1, 80, pmap->issuerDomainPolicy); + i2t_ASN1_OBJECT(obj_tmp2, 80, pmap->subjectDomainPolicy); + X509V3_add_value(obj_tmp1, obj_tmp2, &ext_list); + } + return ext_list; +} + +static void *v2i_POLICY_MAPPINGS(const X509V3_EXT_METHOD *method, + X509V3_CTX *ctx, STACK_OF(CONF_VALUE) *nval) +{ + POLICY_MAPPINGS *pmaps = NULL; + POLICY_MAPPING *pmap = NULL; + ASN1_OBJECT *obj1 = NULL, *obj2 = NULL; + CONF_VALUE *val; + int i; + + if ((pmaps = sk_POLICY_MAPPING_new_null()) == NULL) { + X509V3err(X509V3_F_V2I_POLICY_MAPPINGS, ERR_R_MALLOC_FAILURE); + return NULL; + } + + for (i = 0; i < sk_CONF_VALUE_num(nval); i++) { + val = sk_CONF_VALUE_value(nval, i); + if (!val->value || !val->name) { + X509V3err(X509V3_F_V2I_POLICY_MAPPINGS, + X509V3_R_INVALID_OBJECT_IDENTIFIER); + X509V3_conf_err(val); + goto err; + } + obj1 = OBJ_txt2obj(val->name, 0); + obj2 = OBJ_txt2obj(val->value, 0); + if (!obj1 || !obj2) { + X509V3err(X509V3_F_V2I_POLICY_MAPPINGS, + X509V3_R_INVALID_OBJECT_IDENTIFIER); + X509V3_conf_err(val); + goto err; + } + pmap = POLICY_MAPPING_new(); + if (pmap == NULL) { + X509V3err(X509V3_F_V2I_POLICY_MAPPINGS, ERR_R_MALLOC_FAILURE); + goto err; + } + pmap->issuerDomainPolicy = obj1; + pmap->subjectDomainPolicy = obj2; + obj1 = obj2 = NULL; + sk_POLICY_MAPPING_push(pmaps, pmap); + } + return pmaps; + err: + ASN1_OBJECT_free(obj1); + ASN1_OBJECT_free(obj2); + sk_POLICY_MAPPING_pop_free(pmaps, POLICY_MAPPING_free); + return NULL; +} diff --git a/crypto/x509v3/v3_prn.c b/crypto/x509v3/v3_prn.c new file mode 100644 index 000000000..f384c342a --- /dev/null +++ b/crypto/x509v3/v3_prn.c @@ -0,0 +1,210 @@ +/* + * Copyright 1999-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +/* X509 v3 extension utilities */ + +#include +#include "internal/cryptlib.h" +#include +#include + +/* Extension printing routines */ + +static int unknown_ext_print(BIO *out, const unsigned char *ext, int extlen, + unsigned long flag, int indent, int supported); + +/* Print out a name+value stack */ + +void X509V3_EXT_val_prn(BIO *out, STACK_OF(CONF_VALUE) *val, int indent, + int ml) +{ + int i; + CONF_VALUE *nval; + if (!val) + return; + if (!ml || !sk_CONF_VALUE_num(val)) { + BIO_printf(out, "%*s", indent, ""); + if (!sk_CONF_VALUE_num(val)) + BIO_puts(out, "\n"); + } + for (i = 0; i < sk_CONF_VALUE_num(val); i++) { + if (ml) + BIO_printf(out, "%*s", indent, ""); + else if (i > 0) + BIO_printf(out, ", "); + nval = sk_CONF_VALUE_value(val, i); + if (!nval->name) + BIO_puts(out, nval->value); + else if (!nval->value) + BIO_puts(out, nval->name); +#ifndef CHARSET_EBCDIC + else + BIO_printf(out, "%s:%s", nval->name, nval->value); +#else + else { + int len; + char *tmp; + len = strlen(nval->value) + 1; + tmp = OPENSSL_malloc(len); + if (tmp != NULL) { + ascii2ebcdic(tmp, nval->value, len); + BIO_printf(out, "%s:%s", nval->name, tmp); + OPENSSL_free(tmp); + } + } +#endif + if (ml) + BIO_puts(out, "\n"); + } +} + +/* Main routine: print out a general extension */ + +int X509V3_EXT_print(BIO *out, X509_EXTENSION *ext, unsigned long flag, + int indent) +{ + void *ext_str = NULL; + char *value = NULL; + ASN1_OCTET_STRING *extoct; + const unsigned char *p; + int extlen; + const X509V3_EXT_METHOD *method; + STACK_OF(CONF_VALUE) *nval = NULL; + int ok = 1; + + extoct = X509_EXTENSION_get_data(ext); + p = ASN1_STRING_get0_data(extoct); + extlen = ASN1_STRING_length(extoct); + + if ((method = X509V3_EXT_get(ext)) == NULL) + return unknown_ext_print(out, p, extlen, flag, indent, 0); + if (method->it) + ext_str = ASN1_item_d2i(NULL, &p, extlen, ASN1_ITEM_ptr(method->it)); + else + ext_str = method->d2i(NULL, &p, extlen); + + if (!ext_str) + return unknown_ext_print(out, p, extlen, flag, indent, 1); + + if (method->i2s) { + if ((value = method->i2s(method, ext_str)) == NULL) { + ok = 0; + goto err; + } +#ifndef CHARSET_EBCDIC + BIO_printf(out, "%*s%s", indent, "", value); +#else + { + int len; + char *tmp; + len = strlen(value) + 1; + tmp = OPENSSL_malloc(len); + if (tmp != NULL) { + ascii2ebcdic(tmp, value, len); + BIO_printf(out, "%*s%s", indent, "", tmp); + OPENSSL_free(tmp); + } + } +#endif + } else if (method->i2v) { + if ((nval = method->i2v(method, ext_str, NULL)) == NULL) { + ok = 0; + goto err; + } + X509V3_EXT_val_prn(out, nval, indent, + method->ext_flags & X509V3_EXT_MULTILINE); + } else if (method->i2r) { + if (!method->i2r(method, ext_str, out, indent)) + ok = 0; + } else + ok = 0; + + err: + sk_CONF_VALUE_pop_free(nval, X509V3_conf_free); + OPENSSL_free(value); + if (method->it) + ASN1_item_free(ext_str, ASN1_ITEM_ptr(method->it)); + else + method->ext_free(ext_str); + return ok; +} + +int X509V3_extensions_print(BIO *bp, const char *title, + const STACK_OF(X509_EXTENSION) *exts, + unsigned long flag, int indent) +{ + int i, j; + + if (sk_X509_EXTENSION_num(exts) <= 0) + return 1; + + if (title) { + BIO_printf(bp, "%*s%s:\n", indent, "", title); + indent += 4; + } + + for (i = 0; i < sk_X509_EXTENSION_num(exts); i++) { + ASN1_OBJECT *obj; + X509_EXTENSION *ex; + ex = sk_X509_EXTENSION_value(exts, i); + if (indent && BIO_printf(bp, "%*s", indent, "") <= 0) + return 0; + obj = X509_EXTENSION_get_object(ex); + i2a_ASN1_OBJECT(bp, obj); + j = X509_EXTENSION_get_critical(ex); + if (BIO_printf(bp, ": %s\n", j ? "critical" : "") <= 0) + return 0; + if (!X509V3_EXT_print(bp, ex, flag, indent + 4)) { + BIO_printf(bp, "%*s", indent + 4, ""); + ASN1_STRING_print(bp, X509_EXTENSION_get_data(ex)); + } + if (BIO_write(bp, "\n", 1) <= 0) + return 0; + } + return 1; +} + +static int unknown_ext_print(BIO *out, const unsigned char *ext, int extlen, + unsigned long flag, int indent, int supported) +{ + switch (flag & X509V3_EXT_UNKNOWN_MASK) { + + case X509V3_EXT_DEFAULT: + return 0; + + case X509V3_EXT_ERROR_UNKNOWN: + if (supported) + BIO_printf(out, "%*s", indent, ""); + else + BIO_printf(out, "%*s", indent, ""); + return 1; + + case X509V3_EXT_PARSE_UNKNOWN: + return ASN1_parse_dump(out, ext, extlen, indent, -1); + case X509V3_EXT_DUMP_UNKNOWN: + return BIO_dump_indent(out, (const char *)ext, extlen, indent); + + default: + return 1; + } +} + +#ifndef OPENSSL_NO_STDIO +int X509V3_EXT_print_fp(FILE *fp, X509_EXTENSION *ext, int flag, int indent) +{ + BIO *bio_tmp; + int ret; + + if ((bio_tmp = BIO_new_fp(fp, BIO_NOCLOSE)) == NULL) + return 0; + ret = X509V3_EXT_print(bio_tmp, ext, flag, indent); + BIO_free(bio_tmp); + return ret; +} +#endif diff --git a/crypto/x509v3/v3_purp.c b/crypto/x509v3/v3_purp.c new file mode 100644 index 000000000..29021ea9f --- /dev/null +++ b/crypto/x509v3/v3_purp.c @@ -0,0 +1,873 @@ +/* + * Copyright 1999-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#include "internal/cryptlib.h" +#include "internal/numbers.h" +#include +#include +#include "internal/x509_int.h" + +static void x509v3_cache_extensions(X509 *x); + +static int check_ssl_ca(const X509 *x); +static int check_purpose_ssl_client(const X509_PURPOSE *xp, const X509 *x, + int ca); +static int check_purpose_ssl_server(const X509_PURPOSE *xp, const X509 *x, + int ca); +static int check_purpose_ns_ssl_server(const X509_PURPOSE *xp, const X509 *x, + int ca); +static int purpose_smime(const X509 *x, int ca); +static int check_purpose_smime_sign(const X509_PURPOSE *xp, const X509 *x, + int ca); +static int check_purpose_smime_encrypt(const X509_PURPOSE *xp, const X509 *x, + int ca); +static int check_purpose_crl_sign(const X509_PURPOSE *xp, const X509 *x, + int ca); +static int check_purpose_timestamp_sign(const X509_PURPOSE *xp, const X509 *x, + int ca); +static int no_check(const X509_PURPOSE *xp, const X509 *x, int ca); +static int ocsp_helper(const X509_PURPOSE *xp, const X509 *x, int ca); + +static int xp_cmp(const X509_PURPOSE *const *a, const X509_PURPOSE *const *b); +static void xptable_free(X509_PURPOSE *p); + +static X509_PURPOSE xstandard[] = { + {X509_PURPOSE_SSL_CLIENT, X509_TRUST_SSL_CLIENT, 0, + check_purpose_ssl_client, "SSL client", "sslclient", NULL}, + {X509_PURPOSE_SSL_SERVER, X509_TRUST_SSL_SERVER, 0, + check_purpose_ssl_server, "SSL server", "sslserver", NULL}, + {X509_PURPOSE_NS_SSL_SERVER, X509_TRUST_SSL_SERVER, 0, + check_purpose_ns_ssl_server, "Netscape SSL server", "nssslserver", NULL}, + {X509_PURPOSE_SMIME_SIGN, X509_TRUST_EMAIL, 0, check_purpose_smime_sign, + "S/MIME signing", "smimesign", NULL}, + {X509_PURPOSE_SMIME_ENCRYPT, X509_TRUST_EMAIL, 0, + check_purpose_smime_encrypt, "S/MIME encryption", "smimeencrypt", NULL}, + {X509_PURPOSE_CRL_SIGN, X509_TRUST_COMPAT, 0, check_purpose_crl_sign, + "CRL signing", "crlsign", NULL}, + {X509_PURPOSE_ANY, X509_TRUST_DEFAULT, 0, no_check, "Any Purpose", "any", + NULL}, + {X509_PURPOSE_OCSP_HELPER, X509_TRUST_COMPAT, 0, ocsp_helper, + "OCSP helper", "ocsphelper", NULL}, + {X509_PURPOSE_TIMESTAMP_SIGN, X509_TRUST_TSA, 0, + check_purpose_timestamp_sign, "Time Stamp signing", "timestampsign", + NULL}, +}; + +#define X509_PURPOSE_COUNT OSSL_NELEM(xstandard) + +static STACK_OF(X509_PURPOSE) *xptable = NULL; + +static int xp_cmp(const X509_PURPOSE *const *a, const X509_PURPOSE *const *b) +{ + return (*a)->purpose - (*b)->purpose; +} + +/* + * As much as I'd like to make X509_check_purpose use a "const" X509* I + * really can't because it does recalculate hashes and do other non-const + * things. + */ +int X509_check_purpose(X509 *x, int id, int ca) +{ + int idx; + const X509_PURPOSE *pt; + if (!(x->ex_flags & EXFLAG_SET)) { + CRYPTO_THREAD_write_lock(x->lock); + x509v3_cache_extensions(x); + CRYPTO_THREAD_unlock(x->lock); + } + /* Return if side-effect only call */ + if (id == -1) + return 1; + idx = X509_PURPOSE_get_by_id(id); + if (idx == -1) + return -1; + pt = X509_PURPOSE_get0(idx); + return pt->check_purpose(pt, x, ca); +} + +int X509_PURPOSE_set(int *p, int purpose) +{ + if (X509_PURPOSE_get_by_id(purpose) == -1) { + X509V3err(X509V3_F_X509_PURPOSE_SET, X509V3_R_INVALID_PURPOSE); + return 0; + } + *p = purpose; + return 1; +} + +int X509_PURPOSE_get_count(void) +{ + if (!xptable) + return X509_PURPOSE_COUNT; + return sk_X509_PURPOSE_num(xptable) + X509_PURPOSE_COUNT; +} + +X509_PURPOSE *X509_PURPOSE_get0(int idx) +{ + if (idx < 0) + return NULL; + if (idx < (int)X509_PURPOSE_COUNT) + return xstandard + idx; + return sk_X509_PURPOSE_value(xptable, idx - X509_PURPOSE_COUNT); +} + +int X509_PURPOSE_get_by_sname(const char *sname) +{ + int i; + X509_PURPOSE *xptmp; + for (i = 0; i < X509_PURPOSE_get_count(); i++) { + xptmp = X509_PURPOSE_get0(i); + if (strcmp(xptmp->sname, sname) == 0) + return i; + } + return -1; +} + +int X509_PURPOSE_get_by_id(int purpose) +{ + X509_PURPOSE tmp; + int idx; + if ((purpose >= X509_PURPOSE_MIN) && (purpose <= X509_PURPOSE_MAX)) + return purpose - X509_PURPOSE_MIN; + tmp.purpose = purpose; + if (!xptable) + return -1; + idx = sk_X509_PURPOSE_find(xptable, &tmp); + if (idx == -1) + return -1; + return idx + X509_PURPOSE_COUNT; +} + +int X509_PURPOSE_add(int id, int trust, int flags, + int (*ck) (const X509_PURPOSE *, const X509 *, int), + const char *name, const char *sname, void *arg) +{ + int idx; + X509_PURPOSE *ptmp; + /* + * This is set according to what we change: application can't set it + */ + flags &= ~X509_PURPOSE_DYNAMIC; + /* This will always be set for application modified trust entries */ + flags |= X509_PURPOSE_DYNAMIC_NAME; + /* Get existing entry if any */ + idx = X509_PURPOSE_get_by_id(id); + /* Need a new entry */ + if (idx == -1) { + if ((ptmp = OPENSSL_malloc(sizeof(*ptmp))) == NULL) { + X509V3err(X509V3_F_X509_PURPOSE_ADD, ERR_R_MALLOC_FAILURE); + return 0; + } + ptmp->flags = X509_PURPOSE_DYNAMIC; + } else + ptmp = X509_PURPOSE_get0(idx); + + /* OPENSSL_free existing name if dynamic */ + if (ptmp->flags & X509_PURPOSE_DYNAMIC_NAME) { + OPENSSL_free(ptmp->name); + OPENSSL_free(ptmp->sname); + } + /* dup supplied name */ + ptmp->name = OPENSSL_strdup(name); + ptmp->sname = OPENSSL_strdup(sname); + if (!ptmp->name || !ptmp->sname) { + X509V3err(X509V3_F_X509_PURPOSE_ADD, ERR_R_MALLOC_FAILURE); + goto err; + } + /* Keep the dynamic flag of existing entry */ + ptmp->flags &= X509_PURPOSE_DYNAMIC; + /* Set all other flags */ + ptmp->flags |= flags; + + ptmp->purpose = id; + ptmp->trust = trust; + ptmp->check_purpose = ck; + ptmp->usr_data = arg; + + /* If its a new entry manage the dynamic table */ + if (idx == -1) { + if (xptable == NULL + && (xptable = sk_X509_PURPOSE_new(xp_cmp)) == NULL) { + X509V3err(X509V3_F_X509_PURPOSE_ADD, ERR_R_MALLOC_FAILURE); + goto err; + } + if (!sk_X509_PURPOSE_push(xptable, ptmp)) { + X509V3err(X509V3_F_X509_PURPOSE_ADD, ERR_R_MALLOC_FAILURE); + goto err; + } + } + return 1; + err: + if (idx == -1) { + OPENSSL_free(ptmp->name); + OPENSSL_free(ptmp->sname); + OPENSSL_free(ptmp); + } + return 0; +} + +static void xptable_free(X509_PURPOSE *p) +{ + if (!p) + return; + if (p->flags & X509_PURPOSE_DYNAMIC) { + if (p->flags & X509_PURPOSE_DYNAMIC_NAME) { + OPENSSL_free(p->name); + OPENSSL_free(p->sname); + } + OPENSSL_free(p); + } +} + +void X509_PURPOSE_cleanup(void) +{ + sk_X509_PURPOSE_pop_free(xptable, xptable_free); + xptable = NULL; +} + +int X509_PURPOSE_get_id(const X509_PURPOSE *xp) +{ + return xp->purpose; +} + +char *X509_PURPOSE_get0_name(const X509_PURPOSE *xp) +{ + return xp->name; +} + +char *X509_PURPOSE_get0_sname(const X509_PURPOSE *xp) +{ + return xp->sname; +} + +int X509_PURPOSE_get_trust(const X509_PURPOSE *xp) +{ + return xp->trust; +} + +static int nid_cmp(const int *a, const int *b) +{ + return *a - *b; +} + +DECLARE_OBJ_BSEARCH_CMP_FN(int, int, nid); +IMPLEMENT_OBJ_BSEARCH_CMP_FN(int, int, nid); + +int X509_supported_extension(X509_EXTENSION *ex) +{ + /* + * This table is a list of the NIDs of supported extensions: that is + * those which are used by the verify process. If an extension is + * critical and doesn't appear in this list then the verify process will + * normally reject the certificate. The list must be kept in numerical + * order because it will be searched using bsearch. + */ + + static const int supported_nids[] = { + NID_netscape_cert_type, /* 71 */ + NID_key_usage, /* 83 */ + NID_subject_alt_name, /* 85 */ + NID_basic_constraints, /* 87 */ + NID_certificate_policies, /* 89 */ + NID_ext_key_usage, /* 126 */ +#ifndef OPENSSL_NO_RFC3779 + NID_sbgp_ipAddrBlock, /* 290 */ + NID_sbgp_autonomousSysNum, /* 291 */ +#endif + NID_policy_constraints, /* 401 */ + NID_proxyCertInfo, /* 663 */ + NID_name_constraints, /* 666 */ + NID_policy_mappings, /* 747 */ + NID_inhibit_any_policy /* 748 */ + }; + + int ex_nid = OBJ_obj2nid(X509_EXTENSION_get_object(ex)); + + if (ex_nid == NID_undef) + return 0; + + if (OBJ_bsearch_nid(&ex_nid, supported_nids, OSSL_NELEM(supported_nids))) + return 1; + return 0; +} + +static void setup_dp(X509 *x, DIST_POINT *dp) +{ + X509_NAME *iname = NULL; + int i; + if (dp->reasons) { + if (dp->reasons->length > 0) + dp->dp_reasons = dp->reasons->data[0]; + if (dp->reasons->length > 1) + dp->dp_reasons |= (dp->reasons->data[1] << 8); + dp->dp_reasons &= CRLDP_ALL_REASONS; + } else + dp->dp_reasons = CRLDP_ALL_REASONS; + if (!dp->distpoint || (dp->distpoint->type != 1)) + return; + for (i = 0; i < sk_GENERAL_NAME_num(dp->CRLissuer); i++) { + GENERAL_NAME *gen = sk_GENERAL_NAME_value(dp->CRLissuer, i); + if (gen->type == GEN_DIRNAME) { + iname = gen->d.directoryName; + break; + } + } + if (!iname) + iname = X509_get_issuer_name(x); + + DIST_POINT_set_dpname(dp->distpoint, iname); + +} + +static void setup_crldp(X509 *x) +{ + int i; + x->crldp = X509_get_ext_d2i(x, NID_crl_distribution_points, NULL, NULL); + for (i = 0; i < sk_DIST_POINT_num(x->crldp); i++) + setup_dp(x, sk_DIST_POINT_value(x->crldp, i)); +} + +#define V1_ROOT (EXFLAG_V1|EXFLAG_SS) +#define ku_reject(x, usage) \ + (((x)->ex_flags & EXFLAG_KUSAGE) && !((x)->ex_kusage & (usage))) +#define xku_reject(x, usage) \ + (((x)->ex_flags & EXFLAG_XKUSAGE) && !((x)->ex_xkusage & (usage))) +#define ns_reject(x, usage) \ + (((x)->ex_flags & EXFLAG_NSCERT) && !((x)->ex_nscert & (usage))) + +static void x509v3_cache_extensions(X509 *x) +{ + BASIC_CONSTRAINTS *bs; + PROXY_CERT_INFO_EXTENSION *pci; + ASN1_BIT_STRING *usage; + ASN1_BIT_STRING *ns; + EXTENDED_KEY_USAGE *extusage; + X509_EXTENSION *ex; + const EVP_MD *md; + int i; +#ifndef OPENSSL_NO_SHA + md = EVP_sha1(); +#elif !defined(OPENSSL_NO_SM3) + md = EVP_sm3(); +#else + return; +#endif + + if (x->ex_flags & EXFLAG_SET) + return; + X509_digest(x, md, x->sha1_hash, NULL); + /* V1 should mean no extensions ... */ + if (!X509_get_version(x)) + x->ex_flags |= EXFLAG_V1; + /* Handle basic constraints */ + if ((bs = X509_get_ext_d2i(x, NID_basic_constraints, NULL, NULL))) { + if (bs->ca) + x->ex_flags |= EXFLAG_CA; + if (bs->pathlen) { + if ((bs->pathlen->type == V_ASN1_NEG_INTEGER) + || !bs->ca) { + x->ex_flags |= EXFLAG_INVALID; + x->ex_pathlen = 0; + } else + x->ex_pathlen = ASN1_INTEGER_get(bs->pathlen); + } else + x->ex_pathlen = -1; + BASIC_CONSTRAINTS_free(bs); + x->ex_flags |= EXFLAG_BCONS; + } + /* Handle proxy certificates */ + if ((pci = X509_get_ext_d2i(x, NID_proxyCertInfo, NULL, NULL))) { + if (x->ex_flags & EXFLAG_CA + || X509_get_ext_by_NID(x, NID_subject_alt_name, -1) >= 0 + || X509_get_ext_by_NID(x, NID_issuer_alt_name, -1) >= 0) { + x->ex_flags |= EXFLAG_INVALID; + } + if (pci->pcPathLengthConstraint) { + x->ex_pcpathlen = ASN1_INTEGER_get(pci->pcPathLengthConstraint); + } else + x->ex_pcpathlen = -1; + PROXY_CERT_INFO_EXTENSION_free(pci); + x->ex_flags |= EXFLAG_PROXY; + } + /* Handle key usage */ + if ((usage = X509_get_ext_d2i(x, NID_key_usage, NULL, NULL))) { + if (usage->length > 0) { + x->ex_kusage = usage->data[0]; + if (usage->length > 1) + x->ex_kusage |= usage->data[1] << 8; + } else + x->ex_kusage = 0; + x->ex_flags |= EXFLAG_KUSAGE; + ASN1_BIT_STRING_free(usage); + } + x->ex_xkusage = 0; + if ((extusage = X509_get_ext_d2i(x, NID_ext_key_usage, NULL, NULL))) { + x->ex_flags |= EXFLAG_XKUSAGE; + for (i = 0; i < sk_ASN1_OBJECT_num(extusage); i++) { + switch (OBJ_obj2nid(sk_ASN1_OBJECT_value(extusage, i))) { + case NID_server_auth: + x->ex_xkusage |= XKU_SSL_SERVER; + break; + + case NID_client_auth: + x->ex_xkusage |= XKU_SSL_CLIENT; + break; + + case NID_email_protect: + x->ex_xkusage |= XKU_SMIME; + break; + + case NID_code_sign: + x->ex_xkusage |= XKU_CODE_SIGN; + break; + + case NID_ms_sgc: + case NID_ns_sgc: + x->ex_xkusage |= XKU_SGC; + break; + + case NID_OCSP_sign: + x->ex_xkusage |= XKU_OCSP_SIGN; + break; + + case NID_time_stamp: + x->ex_xkusage |= XKU_TIMESTAMP; + break; + + case NID_dvcs: + x->ex_xkusage |= XKU_DVCS; + break; + + case NID_anyExtendedKeyUsage: + x->ex_xkusage |= XKU_ANYEKU; + break; + } + } + sk_ASN1_OBJECT_pop_free(extusage, ASN1_OBJECT_free); + } + + if ((ns = X509_get_ext_d2i(x, NID_netscape_cert_type, NULL, NULL))) { + if (ns->length > 0) + x->ex_nscert = ns->data[0]; + else + x->ex_nscert = 0; + x->ex_flags |= EXFLAG_NSCERT; + ASN1_BIT_STRING_free(ns); + } + x->skid = X509_get_ext_d2i(x, NID_subject_key_identifier, NULL, NULL); + x->akid = X509_get_ext_d2i(x, NID_authority_key_identifier, NULL, NULL); + /* Does subject name match issuer ? */ + if (!X509_NAME_cmp(X509_get_subject_name(x), X509_get_issuer_name(x))) { + x->ex_flags |= EXFLAG_SI; + /* If SKID matches AKID also indicate self signed */ + if (X509_check_akid(x, x->akid) == X509_V_OK && + !ku_reject(x, KU_KEY_CERT_SIGN)) + x->ex_flags |= EXFLAG_SS; + } + x->altname = X509_get_ext_d2i(x, NID_subject_alt_name, NULL, NULL); + x->nc = X509_get_ext_d2i(x, NID_name_constraints, &i, NULL); + if (!x->nc && (i != -1)) + x->ex_flags |= EXFLAG_INVALID; + setup_crldp(x); + +#ifndef OPENSSL_NO_RFC3779 + x->rfc3779_addr = X509_get_ext_d2i(x, NID_sbgp_ipAddrBlock, NULL, NULL); + x->rfc3779_asid = X509_get_ext_d2i(x, NID_sbgp_autonomousSysNum, + NULL, NULL); +#endif + for (i = 0; i < X509_get_ext_count(x); i++) { + ex = X509_get_ext(x, i); + if (OBJ_obj2nid(X509_EXTENSION_get_object(ex)) + == NID_freshest_crl) + x->ex_flags |= EXFLAG_FRESHEST; + if (!X509_EXTENSION_get_critical(ex)) + continue; + if (!X509_supported_extension(ex)) { + x->ex_flags |= EXFLAG_CRITICAL; + break; + } + } + x->ex_flags |= EXFLAG_SET; +} + +/*- + * CA checks common to all purposes + * return codes: + * 0 not a CA + * 1 is a CA + * 2 basicConstraints absent so "maybe" a CA + * 3 basicConstraints absent but self signed V1. + * 4 basicConstraints absent but keyUsage present and keyCertSign asserted. + */ + +static int check_ca(const X509 *x) +{ + /* keyUsage if present should allow cert signing */ + if (ku_reject(x, KU_KEY_CERT_SIGN)) + return 0; + if (x->ex_flags & EXFLAG_BCONS) { + if (x->ex_flags & EXFLAG_CA) + return 1; + /* If basicConstraints says not a CA then say so */ + else + return 0; + } else { + /* we support V1 roots for... uh, I don't really know why. */ + if ((x->ex_flags & V1_ROOT) == V1_ROOT) + return 3; + /* + * If key usage present it must have certSign so tolerate it + */ + else if (x->ex_flags & EXFLAG_KUSAGE) + return 4; + /* Older certificates could have Netscape-specific CA types */ + else if (x->ex_flags & EXFLAG_NSCERT && x->ex_nscert & NS_ANY_CA) + return 5; + /* can this still be regarded a CA certificate? I doubt it */ + return 0; + } +} + +void X509_set_proxy_flag(X509 *x) +{ + x->ex_flags |= EXFLAG_PROXY; +} + +void X509_set_proxy_pathlen(X509 *x, long l) +{ + x->ex_pcpathlen = l; +} + +int X509_check_ca(X509 *x) +{ + if (!(x->ex_flags & EXFLAG_SET)) { + CRYPTO_THREAD_write_lock(x->lock); + x509v3_cache_extensions(x); + CRYPTO_THREAD_unlock(x->lock); + } + + return check_ca(x); +} + +/* Check SSL CA: common checks for SSL client and server */ +static int check_ssl_ca(const X509 *x) +{ + int ca_ret; + ca_ret = check_ca(x); + if (!ca_ret) + return 0; + /* check nsCertType if present */ + if (ca_ret != 5 || x->ex_nscert & NS_SSL_CA) + return ca_ret; + else + return 0; +} + +static int check_purpose_ssl_client(const X509_PURPOSE *xp, const X509 *x, + int ca) +{ + if (xku_reject(x, XKU_SSL_CLIENT)) + return 0; + if (ca) + return check_ssl_ca(x); + /* We need to do digital signatures or key agreement */ + if (ku_reject(x, KU_DIGITAL_SIGNATURE | KU_KEY_AGREEMENT)) + return 0; + /* nsCertType if present should allow SSL client use */ + if (ns_reject(x, NS_SSL_CLIENT)) + return 0; + return 1; +} + +/* + * Key usage needed for TLS/SSL server: digital signature, encipherment or + * key agreement. The ssl code can check this more thoroughly for individual + * key types. + */ +#define KU_TLS \ + KU_DIGITAL_SIGNATURE|KU_KEY_ENCIPHERMENT|KU_KEY_AGREEMENT + +static int check_purpose_ssl_server(const X509_PURPOSE *xp, const X509 *x, + int ca) +{ + if (xku_reject(x, XKU_SSL_SERVER | XKU_SGC)) + return 0; + if (ca) + return check_ssl_ca(x); + + if (ns_reject(x, NS_SSL_SERVER)) + return 0; + if (ku_reject(x, KU_TLS)) + return 0; + + return 1; + +} + +static int check_purpose_ns_ssl_server(const X509_PURPOSE *xp, const X509 *x, + int ca) +{ + int ret; + ret = check_purpose_ssl_server(xp, x, ca); + if (!ret || ca) + return ret; + /* We need to encipher or Netscape complains */ + if (ku_reject(x, KU_KEY_ENCIPHERMENT)) + return 0; + return ret; +} + +/* common S/MIME checks */ +static int purpose_smime(const X509 *x, int ca) +{ + if (xku_reject(x, XKU_SMIME)) + return 0; + if (ca) { + int ca_ret; + ca_ret = check_ca(x); + if (!ca_ret) + return 0; + /* check nsCertType if present */ + if (ca_ret != 5 || x->ex_nscert & NS_SMIME_CA) + return ca_ret; + else + return 0; + } + if (x->ex_flags & EXFLAG_NSCERT) { + if (x->ex_nscert & NS_SMIME) + return 1; + /* Workaround for some buggy certificates */ + if (x->ex_nscert & NS_SSL_CLIENT) + return 2; + return 0; + } + return 1; +} + +static int check_purpose_smime_sign(const X509_PURPOSE *xp, const X509 *x, + int ca) +{ + int ret; + ret = purpose_smime(x, ca); + if (!ret || ca) + return ret; + if (ku_reject(x, KU_DIGITAL_SIGNATURE | KU_NON_REPUDIATION)) + return 0; + return ret; +} + +static int check_purpose_smime_encrypt(const X509_PURPOSE *xp, const X509 *x, + int ca) +{ + int ret; + ret = purpose_smime(x, ca); + if (!ret || ca) + return ret; + if (ku_reject(x, KU_KEY_ENCIPHERMENT)) + return 0; + return ret; +} + +static int check_purpose_crl_sign(const X509_PURPOSE *xp, const X509 *x, + int ca) +{ + if (ca) { + int ca_ret; + if ((ca_ret = check_ca(x)) != 2) + return ca_ret; + else + return 0; + } + if (ku_reject(x, KU_CRL_SIGN)) + return 0; + return 1; +} + +/* + * OCSP helper: this is *not* a full OCSP check. It just checks that each CA + * is valid. Additional checks must be made on the chain. + */ + +static int ocsp_helper(const X509_PURPOSE *xp, const X509 *x, int ca) +{ + /* + * Must be a valid CA. Should we really support the "I don't know" value + * (2)? + */ + if (ca) + return check_ca(x); + /* leaf certificate is checked in OCSP_verify() */ + return 1; +} + +static int check_purpose_timestamp_sign(const X509_PURPOSE *xp, const X509 *x, + int ca) +{ + int i_ext; + + /* If ca is true we must return if this is a valid CA certificate. */ + if (ca) + return check_ca(x); + + /* + * Check the optional key usage field: + * if Key Usage is present, it must be one of digitalSignature + * and/or nonRepudiation (other values are not consistent and shall + * be rejected). + */ + if ((x->ex_flags & EXFLAG_KUSAGE) + && ((x->ex_kusage & ~(KU_NON_REPUDIATION | KU_DIGITAL_SIGNATURE)) || + !(x->ex_kusage & (KU_NON_REPUDIATION | KU_DIGITAL_SIGNATURE)))) + return 0; + + /* Only time stamp key usage is permitted and it's required. */ + if (!(x->ex_flags & EXFLAG_XKUSAGE) || x->ex_xkusage != XKU_TIMESTAMP) + return 0; + + /* Extended Key Usage MUST be critical */ + i_ext = X509_get_ext_by_NID(x, NID_ext_key_usage, -1); + if (i_ext >= 0) { + X509_EXTENSION *ext = X509_get_ext((X509 *)x, i_ext); + if (!X509_EXTENSION_get_critical(ext)) + return 0; + } + + return 1; +} + +static int no_check(const X509_PURPOSE *xp, const X509 *x, int ca) +{ + return 1; +} + +/*- + * Various checks to see if one certificate issued the second. + * This can be used to prune a set of possible issuer certificates + * which have been looked up using some simple method such as by + * subject name. + * These are: + * 1. Check issuer_name(subject) == subject_name(issuer) + * 2. If akid(subject) exists check it matches issuer + * 3. If key_usage(issuer) exists check it supports certificate signing + * returns 0 for OK, positive for reason for mismatch, reasons match + * codes for X509_verify_cert() + */ + +int X509_check_issued(X509 *issuer, X509 *subject) +{ + if (X509_NAME_cmp(X509_get_subject_name(issuer), + X509_get_issuer_name(subject))) + return X509_V_ERR_SUBJECT_ISSUER_MISMATCH; + x509v3_cache_extensions(issuer); + x509v3_cache_extensions(subject); + + if (subject->akid) { + int ret = X509_check_akid(issuer, subject->akid); + if (ret != X509_V_OK) + return ret; + } + + if (subject->ex_flags & EXFLAG_PROXY) { + if (ku_reject(issuer, KU_DIGITAL_SIGNATURE)) + return X509_V_ERR_KEYUSAGE_NO_DIGITAL_SIGNATURE; + } else if (ku_reject(issuer, KU_KEY_CERT_SIGN)) + return X509_V_ERR_KEYUSAGE_NO_CERTSIGN; + return X509_V_OK; +} + +int X509_check_akid(X509 *issuer, AUTHORITY_KEYID *akid) +{ + + if (!akid) + return X509_V_OK; + + /* Check key ids (if present) */ + if (akid->keyid && issuer->skid && + ASN1_OCTET_STRING_cmp(akid->keyid, issuer->skid)) + return X509_V_ERR_AKID_SKID_MISMATCH; + /* Check serial number */ + if (akid->serial && + ASN1_INTEGER_cmp(X509_get_serialNumber(issuer), akid->serial)) + return X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH; + /* Check issuer name */ + if (akid->issuer) { + /* + * Ugh, for some peculiar reason AKID includes SEQUENCE OF + * GeneralName. So look for a DirName. There may be more than one but + * we only take any notice of the first. + */ + GENERAL_NAMES *gens; + GENERAL_NAME *gen; + X509_NAME *nm = NULL; + int i; + gens = akid->issuer; + for (i = 0; i < sk_GENERAL_NAME_num(gens); i++) { + gen = sk_GENERAL_NAME_value(gens, i); + if (gen->type == GEN_DIRNAME) { + nm = gen->d.dirn; + break; + } + } + if (nm && X509_NAME_cmp(nm, X509_get_issuer_name(issuer))) + return X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH; + } + return X509_V_OK; +} + +uint32_t X509_get_extension_flags(X509 *x) +{ + /* Call for side-effect of computing hash and caching extensions */ + X509_check_purpose(x, -1, -1); + return x->ex_flags; +} + +uint32_t X509_get_key_usage(X509 *x) +{ + /* Call for side-effect of computing hash and caching extensions */ + X509_check_purpose(x, -1, -1); + if (x->ex_flags & EXFLAG_KUSAGE) + return x->ex_kusage; + return UINT32_MAX; +} + +uint32_t X509_get_extended_key_usage(X509 *x) +{ + /* Call for side-effect of computing hash and caching extensions */ + X509_check_purpose(x, -1, -1); + if (x->ex_flags & EXFLAG_XKUSAGE) + return x->ex_xkusage; + return UINT32_MAX; +} + +const ASN1_OCTET_STRING *X509_get0_subject_key_id(X509 *x) +{ + /* Call for side-effect of computing hash and caching extensions */ + X509_check_purpose(x, -1, -1); + return x->skid; +} + +long X509_get_pathlen(X509 *x) +{ + /* Called for side effect of caching extensions */ + if (X509_check_purpose(x, -1, -1) != 1 + || (x->ex_flags & EXFLAG_BCONS) == 0) + return -1; + return x->ex_pathlen; +} + +long X509_get_proxy_pathlen(X509 *x) +{ + /* Called for side effect of caching extensions */ + if (X509_check_purpose(x, -1, -1) != 1 + || (x->ex_flags & EXFLAG_PROXY) == 0) + return -1; + return x->ex_pcpathlen; +} diff --git a/crypto/x509v3/v3_skey.c b/crypto/x509v3/v3_skey.c new file mode 100644 index 000000000..e06d0486e --- /dev/null +++ b/crypto/x509v3/v3_skey.c @@ -0,0 +1,114 @@ +/* + * Copyright 1999-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#include "internal/cryptlib.h" +#include +#include "internal/x509_int.h" +#include "ext_dat.h" + +static ASN1_OCTET_STRING *s2i_skey_id(X509V3_EXT_METHOD *method, + X509V3_CTX *ctx, char *str); +const X509V3_EXT_METHOD v3_skey_id = { + NID_subject_key_identifier, 0, ASN1_ITEM_ref(ASN1_OCTET_STRING), + 0, 0, 0, 0, + (X509V3_EXT_I2S)i2s_ASN1_OCTET_STRING, + (X509V3_EXT_S2I)s2i_skey_id, + 0, 0, 0, 0, + NULL +}; + +char *i2s_ASN1_OCTET_STRING(X509V3_EXT_METHOD *method, + const ASN1_OCTET_STRING *oct) +{ + return OPENSSL_buf2hexstr(oct->data, oct->length); +} + +ASN1_OCTET_STRING *s2i_ASN1_OCTET_STRING(X509V3_EXT_METHOD *method, + X509V3_CTX *ctx, const char *str) +{ + ASN1_OCTET_STRING *oct; + long length; + + if ((oct = ASN1_OCTET_STRING_new()) == NULL) { + X509V3err(X509V3_F_S2I_ASN1_OCTET_STRING, ERR_R_MALLOC_FAILURE); + return NULL; + } + + if ((oct->data = OPENSSL_hexstr2buf(str, &length)) == NULL) { + ASN1_OCTET_STRING_free(oct); + return NULL; + } + + oct->length = length; + + return oct; + +} + +static ASN1_OCTET_STRING *s2i_skey_id(X509V3_EXT_METHOD *method, + X509V3_CTX *ctx, char *str) +{ + ASN1_OCTET_STRING *oct; + X509_PUBKEY *pubkey; + const unsigned char *pk; + int pklen; + unsigned char pkey_dig[EVP_MAX_MD_SIZE]; + unsigned int diglen; + const EVP_MD *md; +#ifndef OPENSSL_NO_SHA + md = EVP_sha1(); +#elif !defined(OPENSSL_NO_SM3) + md = EVP_sm3(); +#else + return NULL; +#endif + + if (strcmp(str, "hash")) + return s2i_ASN1_OCTET_STRING(method, ctx, str); + + if ((oct = ASN1_OCTET_STRING_new()) == NULL) { + X509V3err(X509V3_F_S2I_SKEY_ID, ERR_R_MALLOC_FAILURE); + return NULL; + } + + if (ctx && (ctx->flags == CTX_TEST)) + return oct; + + if (!ctx || (!ctx->subject_req && !ctx->subject_cert)) { + X509V3err(X509V3_F_S2I_SKEY_ID, X509V3_R_NO_PUBLIC_KEY); + goto err; + } + + if (ctx->subject_req) + pubkey = ctx->subject_req->req_info.pubkey; + else + pubkey = ctx->subject_cert->cert_info.key; + + if (pubkey == NULL) { + X509V3err(X509V3_F_S2I_SKEY_ID, X509V3_R_NO_PUBLIC_KEY); + goto err; + } + + X509_PUBKEY_get0_param(NULL, &pk, &pklen, NULL, pubkey); + + if (!EVP_Digest(pk, pklen, pkey_dig, &diglen, md, NULL)) + goto err; + + if (!ASN1_OCTET_STRING_set(oct, pkey_dig, diglen)) { + X509V3err(X509V3_F_S2I_SKEY_ID, ERR_R_MALLOC_FAILURE); + goto err; + } + + return oct; + + err: + ASN1_OCTET_STRING_free(oct); + return NULL; +} diff --git a/crypto/x509v3/v3_sxnet.c b/crypto/x509v3/v3_sxnet.c new file mode 100644 index 000000000..89cda01be --- /dev/null +++ b/crypto/x509v3/v3_sxnet.c @@ -0,0 +1,226 @@ +/* + * Copyright 1999-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#include "internal/cryptlib.h" +#include +#include +#include +#include +#include "ext_dat.h" + +/* Support for Thawte strong extranet extension */ + +#define SXNET_TEST + +static int sxnet_i2r(X509V3_EXT_METHOD *method, SXNET *sx, BIO *out, + int indent); +#ifdef SXNET_TEST +static SXNET *sxnet_v2i(X509V3_EXT_METHOD *method, X509V3_CTX *ctx, + STACK_OF(CONF_VALUE) *nval); +#endif +const X509V3_EXT_METHOD v3_sxnet = { + NID_sxnet, X509V3_EXT_MULTILINE, ASN1_ITEM_ref(SXNET), + 0, 0, 0, 0, + 0, 0, + 0, +#ifdef SXNET_TEST + (X509V3_EXT_V2I)sxnet_v2i, +#else + 0, +#endif + (X509V3_EXT_I2R)sxnet_i2r, + 0, + NULL +}; + +ASN1_SEQUENCE(SXNETID) = { + ASN1_SIMPLE(SXNETID, zone, ASN1_INTEGER), + ASN1_SIMPLE(SXNETID, user, ASN1_OCTET_STRING) +} ASN1_SEQUENCE_END(SXNETID) + +IMPLEMENT_ASN1_FUNCTIONS(SXNETID) + +ASN1_SEQUENCE(SXNET) = { + ASN1_SIMPLE(SXNET, version, ASN1_INTEGER), + ASN1_SEQUENCE_OF(SXNET, ids, SXNETID) +} ASN1_SEQUENCE_END(SXNET) + +IMPLEMENT_ASN1_FUNCTIONS(SXNET) + +static int sxnet_i2r(X509V3_EXT_METHOD *method, SXNET *sx, BIO *out, + int indent) +{ + long v; + char *tmp; + SXNETID *id; + int i; + v = ASN1_INTEGER_get(sx->version); + BIO_printf(out, "%*sVersion: %ld (0x%lX)", indent, "", v + 1, v); + for (i = 0; i < sk_SXNETID_num(sx->ids); i++) { + id = sk_SXNETID_value(sx->ids, i); + tmp = i2s_ASN1_INTEGER(NULL, id->zone); + BIO_printf(out, "\n%*sZone: %s, User: ", indent, "", tmp); + OPENSSL_free(tmp); + ASN1_STRING_print(out, id->user); + } + return 1; +} + +#ifdef SXNET_TEST + +/* + * NBB: this is used for testing only. It should *not* be used for anything + * else because it will just take static IDs from the configuration file and + * they should really be separate values for each user. + */ + +static SXNET *sxnet_v2i(X509V3_EXT_METHOD *method, X509V3_CTX *ctx, + STACK_OF(CONF_VALUE) *nval) +{ + CONF_VALUE *cnf; + SXNET *sx = NULL; + int i; + for (i = 0; i < sk_CONF_VALUE_num(nval); i++) { + cnf = sk_CONF_VALUE_value(nval, i); + if (!SXNET_add_id_asc(&sx, cnf->name, cnf->value, -1)) + return NULL; + } + return sx; +} + +#endif + +/* Strong Extranet utility functions */ + +/* Add an id given the zone as an ASCII number */ + +int SXNET_add_id_asc(SXNET **psx, const char *zone, const char *user, int userlen) +{ + ASN1_INTEGER *izone; + + if ((izone = s2i_ASN1_INTEGER(NULL, zone)) == NULL) { + X509V3err(X509V3_F_SXNET_ADD_ID_ASC, X509V3_R_ERROR_CONVERTING_ZONE); + return 0; + } + return SXNET_add_id_INTEGER(psx, izone, user, userlen); +} + +/* Add an id given the zone as an unsigned long */ + +int SXNET_add_id_ulong(SXNET **psx, unsigned long lzone, const char *user, + int userlen) +{ + ASN1_INTEGER *izone; + + if ((izone = ASN1_INTEGER_new()) == NULL + || !ASN1_INTEGER_set(izone, lzone)) { + X509V3err(X509V3_F_SXNET_ADD_ID_ULONG, ERR_R_MALLOC_FAILURE); + ASN1_INTEGER_free(izone); + return 0; + } + return SXNET_add_id_INTEGER(psx, izone, user, userlen); + +} + +/* + * Add an id given the zone as an ASN1_INTEGER. Note this version uses the + * passed integer and doesn't make a copy so don't free it up afterwards. + */ + +int SXNET_add_id_INTEGER(SXNET **psx, ASN1_INTEGER *zone, const char *user, + int userlen) +{ + SXNET *sx = NULL; + SXNETID *id = NULL; + if (!psx || !zone || !user) { + X509V3err(X509V3_F_SXNET_ADD_ID_INTEGER, + X509V3_R_INVALID_NULL_ARGUMENT); + return 0; + } + if (userlen == -1) + userlen = strlen(user); + if (userlen > 64) { + X509V3err(X509V3_F_SXNET_ADD_ID_INTEGER, X509V3_R_USER_TOO_LONG); + return 0; + } + if (*psx == NULL) { + if ((sx = SXNET_new()) == NULL) + goto err; + if (!ASN1_INTEGER_set(sx->version, 0)) + goto err; + *psx = sx; + } else + sx = *psx; + if (SXNET_get_id_INTEGER(sx, zone)) { + X509V3err(X509V3_F_SXNET_ADD_ID_INTEGER, X509V3_R_DUPLICATE_ZONE_ID); + return 0; + } + + if ((id = SXNETID_new()) == NULL) + goto err; + if (userlen == -1) + userlen = strlen(user); + + if (!ASN1_OCTET_STRING_set(id->user, (const unsigned char *)user, userlen)) + goto err; + if (!sk_SXNETID_push(sx->ids, id)) + goto err; + id->zone = zone; + return 1; + + err: + X509V3err(X509V3_F_SXNET_ADD_ID_INTEGER, ERR_R_MALLOC_FAILURE); + SXNETID_free(id); + SXNET_free(sx); + *psx = NULL; + return 0; +} + +ASN1_OCTET_STRING *SXNET_get_id_asc(SXNET *sx, const char *zone) +{ + ASN1_INTEGER *izone; + ASN1_OCTET_STRING *oct; + + if ((izone = s2i_ASN1_INTEGER(NULL, zone)) == NULL) { + X509V3err(X509V3_F_SXNET_GET_ID_ASC, X509V3_R_ERROR_CONVERTING_ZONE); + return NULL; + } + oct = SXNET_get_id_INTEGER(sx, izone); + ASN1_INTEGER_free(izone); + return oct; +} + +ASN1_OCTET_STRING *SXNET_get_id_ulong(SXNET *sx, unsigned long lzone) +{ + ASN1_INTEGER *izone; + ASN1_OCTET_STRING *oct; + + if ((izone = ASN1_INTEGER_new()) == NULL + || !ASN1_INTEGER_set(izone, lzone)) { + X509V3err(X509V3_F_SXNET_GET_ID_ULONG, ERR_R_MALLOC_FAILURE); + ASN1_INTEGER_free(izone); + return NULL; + } + oct = SXNET_get_id_INTEGER(sx, izone); + ASN1_INTEGER_free(izone); + return oct; +} + +ASN1_OCTET_STRING *SXNET_get_id_INTEGER(SXNET *sx, ASN1_INTEGER *zone) +{ + SXNETID *id; + int i; + for (i = 0; i < sk_SXNETID_num(sx->ids); i++) { + id = sk_SXNETID_value(sx->ids, i); + if (!ASN1_INTEGER_cmp(id->zone, zone)) + return id->user; + } + return NULL; +} diff --git a/crypto/x509v3/v3_tlsf.c b/crypto/x509v3/v3_tlsf.c new file mode 100644 index 000000000..fec67243f --- /dev/null +++ b/crypto/x509v3/v3_tlsf.c @@ -0,0 +1,137 @@ +/* + * Copyright 2015-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#include "internal/cryptlib.h" +#include "internal/o_str.h" +#include +#include +#include +#include "ext_dat.h" + +static STACK_OF(CONF_VALUE) *i2v_TLS_FEATURE(const X509V3_EXT_METHOD *method, + TLS_FEATURE *tls_feature, + STACK_OF(CONF_VALUE) *ext_list); +static TLS_FEATURE *v2i_TLS_FEATURE(const X509V3_EXT_METHOD *method, + X509V3_CTX *ctx, + STACK_OF(CONF_VALUE) *nval); + +ASN1_ITEM_TEMPLATE(TLS_FEATURE) = + ASN1_EX_TEMPLATE_TYPE(ASN1_TFLG_SEQUENCE_OF, 0, TLS_FEATURE, ASN1_INTEGER) +static_ASN1_ITEM_TEMPLATE_END(TLS_FEATURE) + +IMPLEMENT_ASN1_ALLOC_FUNCTIONS(TLS_FEATURE) + +const X509V3_EXT_METHOD v3_tls_feature = { + NID_tlsfeature, 0, + ASN1_ITEM_ref(TLS_FEATURE), + 0, 0, 0, 0, + 0, 0, + (X509V3_EXT_I2V)i2v_TLS_FEATURE, + (X509V3_EXT_V2I)v2i_TLS_FEATURE, + 0, 0, + NULL +}; + + +typedef struct { + long num; + const char *name; +} TLS_FEATURE_NAME; + +static TLS_FEATURE_NAME tls_feature_tbl[] = { + { 5, "status_request" }, + { 17, "status_request_v2" } +}; + +/* + * i2v_TLS_FEATURE converts the TLS_FEATURE structure tls_feature into the + * STACK_OF(CONF_VALUE) structure ext_list. STACK_OF(CONF_VALUE) is the format + * used by the CONF library to represent a multi-valued extension. ext_list is + * returned. + */ +static STACK_OF(CONF_VALUE) *i2v_TLS_FEATURE(const X509V3_EXT_METHOD *method, + TLS_FEATURE *tls_feature, + STACK_OF(CONF_VALUE) *ext_list) +{ + int i; + size_t j; + ASN1_INTEGER *ai; + long tlsextid; + for (i = 0; i < sk_ASN1_INTEGER_num(tls_feature); i++) { + ai = sk_ASN1_INTEGER_value(tls_feature, i); + tlsextid = ASN1_INTEGER_get(ai); + for (j = 0; j < OSSL_NELEM(tls_feature_tbl); j++) + if (tlsextid == tls_feature_tbl[j].num) + break; + if (j < OSSL_NELEM(tls_feature_tbl)) + X509V3_add_value(NULL, tls_feature_tbl[j].name, &ext_list); + else + X509V3_add_value_int(NULL, ai, &ext_list); + } + return ext_list; +} + +/* + * v2i_TLS_FEATURE converts the multi-valued extension nval into a TLS_FEATURE + * structure, which is returned if the conversion is successful. In case of + * error, NULL is returned. + */ +static TLS_FEATURE *v2i_TLS_FEATURE(const X509V3_EXT_METHOD *method, + X509V3_CTX *ctx, STACK_OF(CONF_VALUE) *nval) +{ + TLS_FEATURE *tlsf; + char *extval, *endptr; + ASN1_INTEGER *ai; + CONF_VALUE *val; + int i; + size_t j; + long tlsextid; + + if ((tlsf = sk_ASN1_INTEGER_new_null()) == NULL) { + X509V3err(X509V3_F_V2I_TLS_FEATURE, ERR_R_MALLOC_FAILURE); + return NULL; + } + + for (i = 0; i < sk_CONF_VALUE_num(nval); i++) { + val = sk_CONF_VALUE_value(nval, i); + if (val->value) + extval = val->value; + else + extval = val->name; + + for (j = 0; j < OSSL_NELEM(tls_feature_tbl); j++) + if (strcasecmp(extval, tls_feature_tbl[j].name) == 0) + break; + if (j < OSSL_NELEM(tls_feature_tbl)) + tlsextid = tls_feature_tbl[j].num; + else { + tlsextid = strtol(extval, &endptr, 10); + if (((*endptr) != '\0') || (extval == endptr) || (tlsextid < 0) || + (tlsextid > 65535)) { + X509V3err(X509V3_F_V2I_TLS_FEATURE, X509V3_R_INVALID_SYNTAX); + X509V3_conf_err(val); + goto err; + } + } + + ai = ASN1_INTEGER_new(); + if (ai == NULL) { + X509V3err(X509V3_F_V2I_TLS_FEATURE, ERR_R_MALLOC_FAILURE); + goto err; + } + ASN1_INTEGER_set(ai, tlsextid); + sk_ASN1_INTEGER_push(tlsf, ai); + } + return tlsf; + + err: + sk_ASN1_INTEGER_pop_free(tlsf, ASN1_INTEGER_free); + return NULL; +} diff --git a/crypto/x509v3/v3_utl.c b/crypto/x509v3/v3_utl.c new file mode 100644 index 000000000..7dc9a4533 --- /dev/null +++ b/crypto/x509v3/v3_utl.c @@ -0,0 +1,1195 @@ +/* + * Copyright 1999-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +/* X509 v3 extension utilities */ + +#include +#include +#include "internal/cryptlib.h" +#include +#include +#include "internal/x509_int.h" +#include +#include "ext_dat.h" + +static char *strip_spaces(char *name); +static int sk_strcmp(const char *const *a, const char *const *b); +static STACK_OF(OPENSSL_STRING) *get_email(X509_NAME *name, + GENERAL_NAMES *gens); +static void str_free(OPENSSL_STRING str); +static int append_ia5(STACK_OF(OPENSSL_STRING) **sk, const ASN1_IA5STRING *email); + +static int ipv4_from_asc(unsigned char *v4, const char *in); +static int ipv6_from_asc(unsigned char *v6, const char *in); +static int ipv6_cb(const char *elem, int len, void *usr); +static int ipv6_hex(unsigned char *out, const char *in, int inlen); + +/* Add a CONF_VALUE name value pair to stack */ + +int X509V3_add_value(const char *name, const char *value, + STACK_OF(CONF_VALUE) **extlist) +{ + CONF_VALUE *vtmp = NULL; + char *tname = NULL, *tvalue = NULL; + + if (name && (tname = OPENSSL_strdup(name)) == NULL) + goto err; + if (value && (tvalue = OPENSSL_strdup(value)) == NULL) + goto err; + if ((vtmp = OPENSSL_malloc(sizeof(*vtmp))) == NULL) + goto err; + if (*extlist == NULL && (*extlist = sk_CONF_VALUE_new_null()) == NULL) + goto err; + vtmp->section = NULL; + vtmp->name = tname; + vtmp->value = tvalue; + if (!sk_CONF_VALUE_push(*extlist, vtmp)) + goto err; + return 1; + err: + X509V3err(X509V3_F_X509V3_ADD_VALUE, ERR_R_MALLOC_FAILURE); + OPENSSL_free(vtmp); + OPENSSL_free(tname); + OPENSSL_free(tvalue); + return 0; +} + +int X509V3_add_value_uchar(const char *name, const unsigned char *value, + STACK_OF(CONF_VALUE) **extlist) +{ + return X509V3_add_value(name, (const char *)value, extlist); +} + +/* Free function for STACK_OF(CONF_VALUE) */ + +void X509V3_conf_free(CONF_VALUE *conf) +{ + if (!conf) + return; + OPENSSL_free(conf->name); + OPENSSL_free(conf->value); + OPENSSL_free(conf->section); + OPENSSL_free(conf); +} + +int X509V3_add_value_bool(const char *name, int asn1_bool, + STACK_OF(CONF_VALUE) **extlist) +{ + if (asn1_bool) + return X509V3_add_value(name, "TRUE", extlist); + return X509V3_add_value(name, "FALSE", extlist); +} + +int X509V3_add_value_bool_nf(const char *name, int asn1_bool, + STACK_OF(CONF_VALUE) **extlist) +{ + if (asn1_bool) + return X509V3_add_value(name, "TRUE", extlist); + return 1; +} + +char *i2s_ASN1_ENUMERATED(X509V3_EXT_METHOD *method, const ASN1_ENUMERATED *a) +{ + BIGNUM *bntmp = NULL; + char *strtmp = NULL; + + if (!a) + return NULL; + if ((bntmp = ASN1_ENUMERATED_to_BN(a, NULL)) == NULL + || (strtmp = BN_bn2dec(bntmp)) == NULL) + X509V3err(X509V3_F_I2S_ASN1_ENUMERATED, ERR_R_MALLOC_FAILURE); + BN_free(bntmp); + return strtmp; +} + +char *i2s_ASN1_INTEGER(X509V3_EXT_METHOD *method, const ASN1_INTEGER *a) +{ + BIGNUM *bntmp = NULL; + char *strtmp = NULL; + + if (!a) + return NULL; + if ((bntmp = ASN1_INTEGER_to_BN(a, NULL)) == NULL + || (strtmp = BN_bn2dec(bntmp)) == NULL) + X509V3err(X509V3_F_I2S_ASN1_INTEGER, ERR_R_MALLOC_FAILURE); + BN_free(bntmp); + return strtmp; +} + +ASN1_INTEGER *s2i_ASN1_INTEGER(X509V3_EXT_METHOD *method, const char *value) +{ + BIGNUM *bn = NULL; + ASN1_INTEGER *aint; + int isneg, ishex; + int ret; + if (value == NULL) { + X509V3err(X509V3_F_S2I_ASN1_INTEGER, X509V3_R_INVALID_NULL_VALUE); + return NULL; + } + bn = BN_new(); + if (bn == NULL) { + X509V3err(X509V3_F_S2I_ASN1_INTEGER, ERR_R_MALLOC_FAILURE); + return NULL; + } + if (value[0] == '-') { + value++; + isneg = 1; + } else + isneg = 0; + + if (value[0] == '0' && ((value[1] == 'x') || (value[1] == 'X'))) { + value += 2; + ishex = 1; + } else + ishex = 0; + + if (ishex) + ret = BN_hex2bn(&bn, value); + else + ret = BN_dec2bn(&bn, value); + + if (!ret || value[ret]) { + BN_free(bn); + X509V3err(X509V3_F_S2I_ASN1_INTEGER, X509V3_R_BN_DEC2BN_ERROR); + return NULL; + } + + if (isneg && BN_is_zero(bn)) + isneg = 0; + + aint = BN_to_ASN1_INTEGER(bn, NULL); + BN_free(bn); + if (!aint) { + X509V3err(X509V3_F_S2I_ASN1_INTEGER, + X509V3_R_BN_TO_ASN1_INTEGER_ERROR); + return NULL; + } + if (isneg) + aint->type |= V_ASN1_NEG; + return aint; +} + +int X509V3_add_value_int(const char *name, const ASN1_INTEGER *aint, + STACK_OF(CONF_VALUE) **extlist) +{ + char *strtmp; + int ret; + + if (!aint) + return 1; + if ((strtmp = i2s_ASN1_INTEGER(NULL, aint)) == NULL) + return 0; + ret = X509V3_add_value(name, strtmp, extlist); + OPENSSL_free(strtmp); + return ret; +} + +int X509V3_get_value_bool(const CONF_VALUE *value, int *asn1_bool) +{ + const char *btmp; + + if ((btmp = value->value) == NULL) + goto err; + if (strcmp(btmp, "TRUE") == 0 + || strcmp(btmp, "true") == 0 + || strcmp(btmp, "Y") == 0 + || strcmp(btmp, "y") == 0 + || strcmp(btmp, "YES") == 0 + || strcmp(btmp, "yes") == 0) { + *asn1_bool = 0xff; + return 1; + } + if (strcmp(btmp, "FALSE") == 0 + || strcmp(btmp, "false") == 0 + || strcmp(btmp, "N") == 0 + || strcmp(btmp, "n") == 0 + || strcmp(btmp, "NO") == 0 + || strcmp(btmp, "no") == 0) { + *asn1_bool = 0; + return 1; + } + err: + X509V3err(X509V3_F_X509V3_GET_VALUE_BOOL, + X509V3_R_INVALID_BOOLEAN_STRING); + X509V3_conf_err(value); + return 0; +} + +int X509V3_get_value_int(const CONF_VALUE *value, ASN1_INTEGER **aint) +{ + ASN1_INTEGER *itmp; + + if ((itmp = s2i_ASN1_INTEGER(NULL, value->value)) == NULL) { + X509V3_conf_err(value); + return 0; + } + *aint = itmp; + return 1; +} + +#define HDR_NAME 1 +#define HDR_VALUE 2 + +/* + * #define DEBUG + */ + +STACK_OF(CONF_VALUE) *X509V3_parse_list(const char *line) +{ + char *p, *q, c; + char *ntmp, *vtmp; + STACK_OF(CONF_VALUE) *values = NULL; + char *linebuf; + int state; + /* We are going to modify the line so copy it first */ + linebuf = OPENSSL_strdup(line); + if (linebuf == NULL) { + X509V3err(X509V3_F_X509V3_PARSE_LIST, ERR_R_MALLOC_FAILURE); + goto err; + } + state = HDR_NAME; + ntmp = NULL; + /* Go through all characters */ + for (p = linebuf, q = linebuf; (c = *p) && (c != '\r') && (c != '\n'); + p++) { + + switch (state) { + case HDR_NAME: + if (c == ':') { + state = HDR_VALUE; + *p = 0; + ntmp = strip_spaces(q); + if (!ntmp) { + X509V3err(X509V3_F_X509V3_PARSE_LIST, + X509V3_R_INVALID_NULL_NAME); + goto err; + } + q = p + 1; + } else if (c == ',') { + *p = 0; + ntmp = strip_spaces(q); + q = p + 1; + if (!ntmp) { + X509V3err(X509V3_F_X509V3_PARSE_LIST, + X509V3_R_INVALID_NULL_NAME); + goto err; + } + X509V3_add_value(ntmp, NULL, &values); + } + break; + + case HDR_VALUE: + if (c == ',') { + state = HDR_NAME; + *p = 0; + vtmp = strip_spaces(q); + if (!vtmp) { + X509V3err(X509V3_F_X509V3_PARSE_LIST, + X509V3_R_INVALID_NULL_VALUE); + goto err; + } + X509V3_add_value(ntmp, vtmp, &values); + ntmp = NULL; + q = p + 1; + } + + } + } + + if (state == HDR_VALUE) { + vtmp = strip_spaces(q); + if (!vtmp) { + X509V3err(X509V3_F_X509V3_PARSE_LIST, + X509V3_R_INVALID_NULL_VALUE); + goto err; + } + X509V3_add_value(ntmp, vtmp, &values); + } else { + ntmp = strip_spaces(q); + if (!ntmp) { + X509V3err(X509V3_F_X509V3_PARSE_LIST, X509V3_R_INVALID_NULL_NAME); + goto err; + } + X509V3_add_value(ntmp, NULL, &values); + } + OPENSSL_free(linebuf); + return values; + + err: + OPENSSL_free(linebuf); + sk_CONF_VALUE_pop_free(values, X509V3_conf_free); + return NULL; + +} + +/* Delete leading and trailing spaces from a string */ +static char *strip_spaces(char *name) +{ + char *p, *q; + /* Skip over leading spaces */ + p = name; + while (*p && isspace((unsigned char)*p)) + p++; + if (!*p) + return NULL; + q = p + strlen(p) - 1; + while ((q != p) && isspace((unsigned char)*q)) + q--; + if (p != q) + q[1] = 0; + if (!*p) + return NULL; + return p; +} + + +/* + * V2I name comparison function: returns zero if 'name' matches cmp or cmp.* + */ + +int name_cmp(const char *name, const char *cmp) +{ + int len, ret; + char c; + len = strlen(cmp); + if ((ret = strncmp(name, cmp, len))) + return ret; + c = name[len]; + if (!c || (c == '.')) + return 0; + return 1; +} + +static int sk_strcmp(const char *const *a, const char *const *b) +{ + return strcmp(*a, *b); +} + +STACK_OF(OPENSSL_STRING) *X509_get1_email(X509 *x) +{ + GENERAL_NAMES *gens; + STACK_OF(OPENSSL_STRING) *ret; + + gens = X509_get_ext_d2i(x, NID_subject_alt_name, NULL, NULL); + ret = get_email(X509_get_subject_name(x), gens); + sk_GENERAL_NAME_pop_free(gens, GENERAL_NAME_free); + return ret; +} + +STACK_OF(OPENSSL_STRING) *X509_get1_ocsp(X509 *x) +{ + AUTHORITY_INFO_ACCESS *info; + STACK_OF(OPENSSL_STRING) *ret = NULL; + int i; + + info = X509_get_ext_d2i(x, NID_info_access, NULL, NULL); + if (!info) + return NULL; + for (i = 0; i < sk_ACCESS_DESCRIPTION_num(info); i++) { + ACCESS_DESCRIPTION *ad = sk_ACCESS_DESCRIPTION_value(info, i); + if (OBJ_obj2nid(ad->method) == NID_ad_OCSP) { + if (ad->location->type == GEN_URI) { + if (!append_ia5 + (&ret, ad->location->d.uniformResourceIdentifier)) + break; + } + } + } + AUTHORITY_INFO_ACCESS_free(info); + return ret; +} + +STACK_OF(OPENSSL_STRING) *X509_REQ_get1_email(X509_REQ *x) +{ + GENERAL_NAMES *gens; + STACK_OF(X509_EXTENSION) *exts; + STACK_OF(OPENSSL_STRING) *ret; + + exts = X509_REQ_get_extensions(x); + gens = X509V3_get_d2i(exts, NID_subject_alt_name, NULL, NULL); + ret = get_email(X509_REQ_get_subject_name(x), gens); + sk_GENERAL_NAME_pop_free(gens, GENERAL_NAME_free); + sk_X509_EXTENSION_pop_free(exts, X509_EXTENSION_free); + return ret; +} + +static STACK_OF(OPENSSL_STRING) *get_email(X509_NAME *name, + GENERAL_NAMES *gens) +{ + STACK_OF(OPENSSL_STRING) *ret = NULL; + X509_NAME_ENTRY *ne; + ASN1_IA5STRING *email; + GENERAL_NAME *gen; + int i; + /* Now add any email address(es) to STACK */ + i = -1; + /* First supplied X509_NAME */ + while ((i = X509_NAME_get_index_by_NID(name, + NID_pkcs9_emailAddress, i)) >= 0) { + ne = X509_NAME_get_entry(name, i); + email = X509_NAME_ENTRY_get_data(ne); + if (!append_ia5(&ret, email)) + return NULL; + } + for (i = 0; i < sk_GENERAL_NAME_num(gens); i++) { + gen = sk_GENERAL_NAME_value(gens, i); + if (gen->type != GEN_EMAIL) + continue; + if (!append_ia5(&ret, gen->d.ia5)) + return NULL; + } + return ret; +} + +static void str_free(OPENSSL_STRING str) +{ + OPENSSL_free(str); +} + +static int append_ia5(STACK_OF(OPENSSL_STRING) **sk, const ASN1_IA5STRING *email) +{ + char *emtmp; + /* First some sanity checks */ + if (email->type != V_ASN1_IA5STRING) + return 1; + if (!email->data || !email->length) + return 1; + if (*sk == NULL) + *sk = sk_OPENSSL_STRING_new(sk_strcmp); + if (*sk == NULL) + return 0; + /* Don't add duplicates */ + if (sk_OPENSSL_STRING_find(*sk, (char *)email->data) != -1) + return 1; + emtmp = OPENSSL_strdup((char *)email->data); + if (emtmp == NULL || !sk_OPENSSL_STRING_push(*sk, emtmp)) { + OPENSSL_free(emtmp); /* free on push failure */ + X509_email_free(*sk); + *sk = NULL; + return 0; + } + return 1; +} + +void X509_email_free(STACK_OF(OPENSSL_STRING) *sk) +{ + sk_OPENSSL_STRING_pop_free(sk, str_free); +} + +typedef int (*equal_fn) (const unsigned char *pattern, size_t pattern_len, + const unsigned char *subject, size_t subject_len, + unsigned int flags); + +/* Skip pattern prefix to match "wildcard" subject */ +static void skip_prefix(const unsigned char **p, size_t *plen, + size_t subject_len, + unsigned int flags) +{ + const unsigned char *pattern = *p; + size_t pattern_len = *plen; + + /* + * If subject starts with a leading '.' followed by more octets, and + * pattern is longer, compare just an equal-length suffix with the + * full subject (starting at the '.'), provided the prefix contains + * no NULs. + */ + if ((flags & _X509_CHECK_FLAG_DOT_SUBDOMAINS) == 0) + return; + + while (pattern_len > subject_len && *pattern) { + if ((flags & X509_CHECK_FLAG_SINGLE_LABEL_SUBDOMAINS) && + *pattern == '.') + break; + ++pattern; + --pattern_len; + } + + /* Skip if entire prefix acceptable */ + if (pattern_len == subject_len) { + *p = pattern; + *plen = pattern_len; + } +} + +/* Compare while ASCII ignoring case. */ +static int equal_nocase(const unsigned char *pattern, size_t pattern_len, + const unsigned char *subject, size_t subject_len, + unsigned int flags) +{ + skip_prefix(&pattern, &pattern_len, subject_len, flags); + if (pattern_len != subject_len) + return 0; + while (pattern_len) { + unsigned char l = *pattern; + unsigned char r = *subject; + /* The pattern must not contain NUL characters. */ + if (l == 0) + return 0; + if (l != r) { + if ('A' <= l && l <= 'Z') + l = (l - 'A') + 'a'; + if ('A' <= r && r <= 'Z') + r = (r - 'A') + 'a'; + if (l != r) + return 0; + } + ++pattern; + ++subject; + --pattern_len; + } + return 1; +} + +/* Compare using memcmp. */ +static int equal_case(const unsigned char *pattern, size_t pattern_len, + const unsigned char *subject, size_t subject_len, + unsigned int flags) +{ + skip_prefix(&pattern, &pattern_len, subject_len, flags); + if (pattern_len != subject_len) + return 0; + return !memcmp(pattern, subject, pattern_len); +} + +/* + * RFC 5280, section 7.5, requires that only the domain is compared in a + * case-insensitive manner. + */ +static int equal_email(const unsigned char *a, size_t a_len, + const unsigned char *b, size_t b_len, + unsigned int unused_flags) +{ + size_t i = a_len; + if (a_len != b_len) + return 0; + /* + * We search backwards for the '@' character, so that we do not have to + * deal with quoted local-parts. The domain part is compared in a + * case-insensitive manner. + */ + while (i > 0) { + --i; + if (a[i] == '@' || b[i] == '@') { + if (!equal_nocase(a + i, a_len - i, b + i, a_len - i, 0)) + return 0; + break; + } + } + if (i == 0) + i = a_len; + return equal_case(a, i, b, i, 0); +} + +/* + * Compare the prefix and suffix with the subject, and check that the + * characters in-between are valid. + */ +static int wildcard_match(const unsigned char *prefix, size_t prefix_len, + const unsigned char *suffix, size_t suffix_len, + const unsigned char *subject, size_t subject_len, + unsigned int flags) +{ + const unsigned char *wildcard_start; + const unsigned char *wildcard_end; + const unsigned char *p; + int allow_multi = 0; + int allow_idna = 0; + + if (subject_len < prefix_len + suffix_len) + return 0; + if (!equal_nocase(prefix, prefix_len, subject, prefix_len, flags)) + return 0; + wildcard_start = subject + prefix_len; + wildcard_end = subject + (subject_len - suffix_len); + if (!equal_nocase(wildcard_end, suffix_len, suffix, suffix_len, flags)) + return 0; + /* + * If the wildcard makes up the entire first label, it must match at + * least one character. + */ + if (prefix_len == 0 && *suffix == '.') { + if (wildcard_start == wildcard_end) + return 0; + allow_idna = 1; + if (flags & X509_CHECK_FLAG_MULTI_LABEL_WILDCARDS) + allow_multi = 1; + } + /* IDNA labels cannot match partial wildcards */ + if (!allow_idna && + subject_len >= 4 && strncasecmp((char *)subject, "xn--", 4) == 0) + return 0; + /* The wildcard may match a literal '*' */ + if (wildcard_end == wildcard_start + 1 && *wildcard_start == '*') + return 1; + /* + * Check that the part matched by the wildcard contains only + * permitted characters and only matches a single label unless + * allow_multi is set. + */ + for (p = wildcard_start; p != wildcard_end; ++p) + if (!(('0' <= *p && *p <= '9') || + ('A' <= *p && *p <= 'Z') || + ('a' <= *p && *p <= 'z') || + *p == '-' || (allow_multi && *p == '.'))) + return 0; + return 1; +} + +#define LABEL_START (1 << 0) +#define LABEL_END (1 << 1) +#define LABEL_HYPHEN (1 << 2) +#define LABEL_IDNA (1 << 3) + +static const unsigned char *valid_star(const unsigned char *p, size_t len, + unsigned int flags) +{ + const unsigned char *star = 0; + size_t i; + int state = LABEL_START; + int dots = 0; + for (i = 0; i < len; ++i) { + /* + * Locate first and only legal wildcard, either at the start + * or end of a non-IDNA first and not final label. + */ + if (p[i] == '*') { + int atstart = (state & LABEL_START); + int atend = (i == len - 1 || p[i + 1] == '.'); + /*- + * At most one wildcard per pattern. + * No wildcards in IDNA labels. + * No wildcards after the first label. + */ + if (star != NULL || (state & LABEL_IDNA) != 0 || dots) + return NULL; + /* Only full-label '*.example.com' wildcards? */ + if ((flags & X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS) + && (!atstart || !atend)) + return NULL; + /* No 'foo*bar' wildcards */ + if (!atstart && !atend) + return NULL; + star = &p[i]; + state &= ~LABEL_START; + } else if (('a' <= p[i] && p[i] <= 'z') + || ('A' <= p[i] && p[i] <= 'Z') + || ('0' <= p[i] && p[i] <= '9')) { + if ((state & LABEL_START) != 0 + && len - i >= 4 && strncasecmp((char *)&p[i], "xn--", 4) == 0) + state |= LABEL_IDNA; + state &= ~(LABEL_HYPHEN | LABEL_START); + } else if (p[i] == '.') { + if ((state & (LABEL_HYPHEN | LABEL_START)) != 0) + return NULL; + state = LABEL_START; + ++dots; + } else if (p[i] == '-') { + /* no domain/subdomain starts with '-' */ + if ((state & LABEL_START) != 0) + return NULL; + state |= LABEL_HYPHEN; + } else + return NULL; + } + + /* + * The final label must not end in a hyphen or ".", and + * there must be at least two dots after the star. + */ + if ((state & (LABEL_START | LABEL_HYPHEN)) != 0 || dots < 2) + return NULL; + return star; +} + +/* Compare using wildcards. */ +static int equal_wildcard(const unsigned char *pattern, size_t pattern_len, + const unsigned char *subject, size_t subject_len, + unsigned int flags) +{ + const unsigned char *star = NULL; + + /* + * Subject names starting with '.' can only match a wildcard pattern + * via a subject sub-domain pattern suffix match. + */ + if (!(subject_len > 1 && subject[0] == '.')) + star = valid_star(pattern, pattern_len, flags); + if (star == NULL) + return equal_nocase(pattern, pattern_len, + subject, subject_len, flags); + return wildcard_match(pattern, star - pattern, + star + 1, (pattern + pattern_len) - star - 1, + subject, subject_len, flags); +} + +/* + * Compare an ASN1_STRING to a supplied string. If they match return 1. If + * cmp_type > 0 only compare if string matches the type, otherwise convert it + * to UTF8. + */ + +static int do_check_string(const ASN1_STRING *a, int cmp_type, equal_fn equal, + unsigned int flags, const char *b, size_t blen, + char **peername) +{ + int rv = 0; + + if (!a->data || !a->length) + return 0; + if (cmp_type > 0) { + if (cmp_type != a->type) + return 0; + if (cmp_type == V_ASN1_IA5STRING) + rv = equal(a->data, a->length, (unsigned char *)b, blen, flags); + else if (a->length == (int)blen && !memcmp(a->data, b, blen)) + rv = 1; + if (rv > 0 && peername) + *peername = OPENSSL_strndup((char *)a->data, a->length); + } else { + int astrlen; + unsigned char *astr; + astrlen = ASN1_STRING_to_UTF8(&astr, a); + if (astrlen < 0) { + /* + * -1 could be an internal malloc failure or a decoding error from + * malformed input; we can't distinguish. + */ + return -1; + } + rv = equal(astr, astrlen, (unsigned char *)b, blen, flags); + if (rv > 0 && peername) + *peername = OPENSSL_strndup((char *)astr, astrlen); + OPENSSL_free(astr); + } + return rv; +} + +static int do_x509_check(X509 *x, const char *chk, size_t chklen, + unsigned int flags, int check_type, char **peername) +{ + GENERAL_NAMES *gens = NULL; + X509_NAME *name = NULL; + int i; + int cnid = NID_undef; + int alt_type; + int san_present = 0; + int rv = 0; + equal_fn equal; + + /* See below, this flag is internal-only */ + flags &= ~_X509_CHECK_FLAG_DOT_SUBDOMAINS; + if (check_type == GEN_EMAIL) { + cnid = NID_pkcs9_emailAddress; + alt_type = V_ASN1_IA5STRING; + equal = equal_email; + } else if (check_type == GEN_DNS) { + cnid = NID_commonName; + /* Implicit client-side DNS sub-domain pattern */ + if (chklen > 1 && chk[0] == '.') + flags |= _X509_CHECK_FLAG_DOT_SUBDOMAINS; + alt_type = V_ASN1_IA5STRING; + if (flags & X509_CHECK_FLAG_NO_WILDCARDS) + equal = equal_nocase; + else + equal = equal_wildcard; + } else { + alt_type = V_ASN1_OCTET_STRING; + equal = equal_case; + } + + if (chklen == 0) + chklen = strlen(chk); + + gens = X509_get_ext_d2i(x, NID_subject_alt_name, NULL, NULL); + if (gens) { + for (i = 0; i < sk_GENERAL_NAME_num(gens); i++) { + GENERAL_NAME *gen; + ASN1_STRING *cstr; + gen = sk_GENERAL_NAME_value(gens, i); + if (gen->type != check_type) + continue; + san_present = 1; + if (check_type == GEN_EMAIL) + cstr = gen->d.rfc822Name; + else if (check_type == GEN_DNS) + cstr = gen->d.dNSName; + else + cstr = gen->d.iPAddress; + /* Positive on success, negative on error! */ + if ((rv = do_check_string(cstr, alt_type, equal, flags, + chk, chklen, peername)) != 0) + break; + } + GENERAL_NAMES_free(gens); + if (rv != 0) + return rv; + if (san_present && !(flags & X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT)) + return 0; + } + + /* We're done if CN-ID is not pertinent */ + if (cnid == NID_undef || (flags & X509_CHECK_FLAG_NEVER_CHECK_SUBJECT)) + return 0; + + i = -1; + name = X509_get_subject_name(x); + while ((i = X509_NAME_get_index_by_NID(name, cnid, i)) >= 0) { + const X509_NAME_ENTRY *ne = X509_NAME_get_entry(name, i); + const ASN1_STRING *str = X509_NAME_ENTRY_get_data(ne); + + /* Positive on success, negative on error! */ + if ((rv = do_check_string(str, -1, equal, flags, + chk, chklen, peername)) != 0) + return rv; + } + return 0; +} + +int X509_check_host(X509 *x, const char *chk, size_t chklen, + unsigned int flags, char **peername) +{ + if (chk == NULL) + return -2; + /* + * Embedded NULs are disallowed, except as the last character of a + * string of length 2 or more (tolerate caller including terminating + * NUL in string length). + */ + if (chklen == 0) + chklen = strlen(chk); + else if (memchr(chk, '\0', chklen > 1 ? chklen - 1 : chklen)) + return -2; + if (chklen > 1 && chk[chklen - 1] == '\0') + --chklen; + return do_x509_check(x, chk, chklen, flags, GEN_DNS, peername); +} + +int X509_check_email(X509 *x, const char *chk, size_t chklen, + unsigned int flags) +{ + if (chk == NULL) + return -2; + /* + * Embedded NULs are disallowed, except as the last character of a + * string of length 2 or more (tolerate caller including terminating + * NUL in string length). + */ + if (chklen == 0) + chklen = strlen((char *)chk); + else if (memchr(chk, '\0', chklen > 1 ? chklen - 1 : chklen)) + return -2; + if (chklen > 1 && chk[chklen - 1] == '\0') + --chklen; + return do_x509_check(x, chk, chklen, flags, GEN_EMAIL, NULL); +} + +int X509_check_ip(X509 *x, const unsigned char *chk, size_t chklen, + unsigned int flags) +{ + if (chk == NULL) + return -2; + return do_x509_check(x, (char *)chk, chklen, flags, GEN_IPADD, NULL); +} + +int X509_check_ip_asc(X509 *x, const char *ipasc, unsigned int flags) +{ + unsigned char ipout[16]; + size_t iplen; + + if (ipasc == NULL) + return -2; + iplen = (size_t)a2i_ipadd(ipout, ipasc); + if (iplen == 0) + return -2; + return do_x509_check(x, (char *)ipout, iplen, flags, GEN_IPADD, NULL); +} + +/* + * Convert IP addresses both IPv4 and IPv6 into an OCTET STRING compatible + * with RFC3280. + */ + +ASN1_OCTET_STRING *a2i_IPADDRESS(const char *ipasc) +{ + unsigned char ipout[16]; + ASN1_OCTET_STRING *ret; + int iplen; + + /* If string contains a ':' assume IPv6 */ + + iplen = a2i_ipadd(ipout, ipasc); + + if (!iplen) + return NULL; + + ret = ASN1_OCTET_STRING_new(); + if (ret == NULL) + return NULL; + if (!ASN1_OCTET_STRING_set(ret, ipout, iplen)) { + ASN1_OCTET_STRING_free(ret); + return NULL; + } + return ret; +} + +ASN1_OCTET_STRING *a2i_IPADDRESS_NC(const char *ipasc) +{ + ASN1_OCTET_STRING *ret = NULL; + unsigned char ipout[32]; + char *iptmp = NULL, *p; + int iplen1, iplen2; + p = strchr(ipasc, '/'); + if (!p) + return NULL; + iptmp = OPENSSL_strdup(ipasc); + if (!iptmp) + return NULL; + p = iptmp + (p - ipasc); + *p++ = 0; + + iplen1 = a2i_ipadd(ipout, iptmp); + + if (!iplen1) + goto err; + + iplen2 = a2i_ipadd(ipout + iplen1, p); + + OPENSSL_free(iptmp); + iptmp = NULL; + + if (!iplen2 || (iplen1 != iplen2)) + goto err; + + ret = ASN1_OCTET_STRING_new(); + if (ret == NULL) + goto err; + if (!ASN1_OCTET_STRING_set(ret, ipout, iplen1 + iplen2)) + goto err; + + return ret; + + err: + OPENSSL_free(iptmp); + ASN1_OCTET_STRING_free(ret); + return NULL; +} + +int a2i_ipadd(unsigned char *ipout, const char *ipasc) +{ + /* If string contains a ':' assume IPv6 */ + + if (strchr(ipasc, ':')) { + if (!ipv6_from_asc(ipout, ipasc)) + return 0; + return 16; + } else { + if (!ipv4_from_asc(ipout, ipasc)) + return 0; + return 4; + } +} + +static int ipv4_from_asc(unsigned char *v4, const char *in) +{ + int a0, a1, a2, a3; + if (sscanf(in, "%d.%d.%d.%d", &a0, &a1, &a2, &a3) != 4) + return 0; + if ((a0 < 0) || (a0 > 255) || (a1 < 0) || (a1 > 255) + || (a2 < 0) || (a2 > 255) || (a3 < 0) || (a3 > 255)) + return 0; + v4[0] = a0; + v4[1] = a1; + v4[2] = a2; + v4[3] = a3; + return 1; +} + +typedef struct { + /* Temporary store for IPV6 output */ + unsigned char tmp[16]; + /* Total number of bytes in tmp */ + int total; + /* The position of a zero (corresponding to '::') */ + int zero_pos; + /* Number of zeroes */ + int zero_cnt; +} IPV6_STAT; + +static int ipv6_from_asc(unsigned char *v6, const char *in) +{ + IPV6_STAT v6stat; + v6stat.total = 0; + v6stat.zero_pos = -1; + v6stat.zero_cnt = 0; + /* + * Treat the IPv6 representation as a list of values separated by ':'. + * The presence of a '::' will parse as one, two or three zero length + * elements. + */ + if (!CONF_parse_list(in, ':', 0, ipv6_cb, &v6stat)) + return 0; + + /* Now for some sanity checks */ + + if (v6stat.zero_pos == -1) { + /* If no '::' must have exactly 16 bytes */ + if (v6stat.total != 16) + return 0; + } else { + /* If '::' must have less than 16 bytes */ + if (v6stat.total == 16) + return 0; + /* More than three zeroes is an error */ + if (v6stat.zero_cnt > 3) + return 0; + /* Can only have three zeroes if nothing else present */ + else if (v6stat.zero_cnt == 3) { + if (v6stat.total > 0) + return 0; + } + /* Can only have two zeroes if at start or end */ + else if (v6stat.zero_cnt == 2) { + if ((v6stat.zero_pos != 0) + && (v6stat.zero_pos != v6stat.total)) + return 0; + } else + /* Can only have one zero if *not* start or end */ + { + if ((v6stat.zero_pos == 0) + || (v6stat.zero_pos == v6stat.total)) + return 0; + } + } + + /* Format result */ + + if (v6stat.zero_pos >= 0) { + /* Copy initial part */ + memcpy(v6, v6stat.tmp, v6stat.zero_pos); + /* Zero middle */ + memset(v6 + v6stat.zero_pos, 0, 16 - v6stat.total); + /* Copy final part */ + if (v6stat.total != v6stat.zero_pos) + memcpy(v6 + v6stat.zero_pos + 16 - v6stat.total, + v6stat.tmp + v6stat.zero_pos, + v6stat.total - v6stat.zero_pos); + } else + memcpy(v6, v6stat.tmp, 16); + + return 1; +} + +static int ipv6_cb(const char *elem, int len, void *usr) +{ + IPV6_STAT *s = usr; + /* Error if 16 bytes written */ + if (s->total == 16) + return 0; + if (len == 0) { + /* Zero length element, corresponds to '::' */ + if (s->zero_pos == -1) + s->zero_pos = s->total; + /* If we've already got a :: its an error */ + else if (s->zero_pos != s->total) + return 0; + s->zero_cnt++; + } else { + /* If more than 4 characters could be final a.b.c.d form */ + if (len > 4) { + /* Need at least 4 bytes left */ + if (s->total > 12) + return 0; + /* Must be end of string */ + if (elem[len]) + return 0; + if (!ipv4_from_asc(s->tmp + s->total, elem)) + return 0; + s->total += 4; + } else { + if (!ipv6_hex(s->tmp + s->total, elem, len)) + return 0; + s->total += 2; + } + } + return 1; +} + +/* + * Convert a string of up to 4 hex digits into the corresponding IPv6 form. + */ + +static int ipv6_hex(unsigned char *out, const char *in, int inlen) +{ + unsigned char c; + unsigned int num = 0; + int x; + + if (inlen > 4) + return 0; + while (inlen--) { + c = *in++; + num <<= 4; + x = OPENSSL_hexchar2int(c); + if (x < 0) + return 0; + num |= (char)x; + } + out[0] = num >> 8; + out[1] = num & 0xff; + return 1; +} + +int X509V3_NAME_from_section(X509_NAME *nm, STACK_OF(CONF_VALUE) *dn_sk, + unsigned long chtype) +{ + CONF_VALUE *v; + int i, mval, spec_char, plus_char; + char *p, *type; + if (!nm) + return 0; + + for (i = 0; i < sk_CONF_VALUE_num(dn_sk); i++) { + v = sk_CONF_VALUE_value(dn_sk, i); + type = v->name; + /* + * Skip past any leading X. X: X, etc to allow for multiple instances + */ + for (p = type; *p; p++) { +#ifndef CHARSET_EBCDIC + spec_char = ((*p == ':') || (*p == ',') || (*p == '.')); +#else + spec_char = ((*p == os_toascii[':']) || (*p == os_toascii[',']) + || (*p == os_toascii['.'])); +#endif + if (spec_char) { + p++; + if (*p) + type = p; + break; + } + } +#ifndef CHARSET_EBCDIC + plus_char = (*type == '+'); +#else + plus_char = (*type == os_toascii['+']); +#endif + if (plus_char) { + mval = -1; + type++; + } else + mval = 0; + if (!X509_NAME_add_entry_by_txt(nm, type, chtype, + (unsigned char *)v->value, -1, -1, + mval)) + return 0; + + } + return 1; +} diff --git a/crypto/x509v3/v3conf.c b/crypto/x509v3/v3conf.c new file mode 100644 index 000000000..966ab90bc --- /dev/null +++ b/crypto/x509v3/v3conf.c @@ -0,0 +1,79 @@ +/* + * Copyright 1999-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#include "internal/cryptlib.h" +#include +#include +#include +#include + +/* Test application to add extensions from a config file */ + +int main(int argc, char **argv) +{ + LHASH *conf; + X509 *cert; + FILE *inf; + char *conf_file; + int i; + int count; + X509_EXTENSION *ext; + X509V3_add_standard_extensions(); + ERR_load_crypto_strings(); + if (!argv[1]) { + fprintf(stderr, "Usage: v3conf cert.pem [file.cnf]\n"); + exit(1); + } + conf_file = argv[2]; + if (!conf_file) + conf_file = "test.cnf"; + conf = CONF_load(NULL, "test.cnf", NULL); + if (!conf) { + fprintf(stderr, "Error opening Config file %s\n", conf_file); + ERR_print_errors_fp(stderr); + exit(1); + } + + inf = fopen(argv[1], "r"); + if (!inf) { + fprintf(stderr, "Can't open certificate file %s\n", argv[1]); + exit(1); + } + cert = PEM_read_X509(inf, NULL, NULL); + if (!cert) { + fprintf(stderr, "Error reading certificate file %s\n", argv[1]); + exit(1); + } + fclose(inf); + + sk_pop_free(cert->cert_info->extensions, X509_EXTENSION_free); + cert->cert_info->extensions = NULL; + + if (!X509V3_EXT_add_conf(conf, NULL, "test_section", cert)) { + fprintf(stderr, "Error adding extensions\n"); + ERR_print_errors_fp(stderr); + exit(1); + } + + count = X509_get_ext_count(cert); + printf("%d extensions\n", count); + for (i = 0; i < count; i++) { + ext = X509_get_ext(cert, i); + printf("%s", OBJ_nid2ln(OBJ_obj2nid(ext->object))); + if (ext->critical) + printf(",critical:\n"); + else + printf(":\n"); + X509V3_EXT_print_fp(stdout, ext, 0, 0); + printf("\n"); + + } + return 0; +} diff --git a/crypto/x509v3/v3err.c b/crypto/x509v3/v3err.c new file mode 100644 index 000000000..5d79c8c6c --- /dev/null +++ b/crypto/x509v3/v3err.c @@ -0,0 +1,187 @@ +/* + * Generated by util/mkerr.pl DO NOT EDIT + * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#include +#include + +/* BEGIN ERROR CODES */ +#ifndef OPENSSL_NO_ERR + +# define ERR_FUNC(func) ERR_PACK(ERR_LIB_X509V3,func,0) +# define ERR_REASON(reason) ERR_PACK(ERR_LIB_X509V3,0,reason) + +static ERR_STRING_DATA X509V3_str_functs[] = { + {ERR_FUNC(X509V3_F_A2I_GENERAL_NAME), "a2i_GENERAL_NAME"}, + {ERR_FUNC(X509V3_F_ADDR_VALIDATE_PATH_INTERNAL), + "addr_validate_path_internal"}, + {ERR_FUNC(X509V3_F_ASIDENTIFIERCHOICE_CANONIZE), + "ASIdentifierChoice_canonize"}, + {ERR_FUNC(X509V3_F_ASIDENTIFIERCHOICE_IS_CANONICAL), + "ASIdentifierChoice_is_canonical"}, + {ERR_FUNC(X509V3_F_COPY_EMAIL), "copy_email"}, + {ERR_FUNC(X509V3_F_COPY_ISSUER), "copy_issuer"}, + {ERR_FUNC(X509V3_F_DO_DIRNAME), "do_dirname"}, + {ERR_FUNC(X509V3_F_DO_EXT_I2D), "do_ext_i2d"}, + {ERR_FUNC(X509V3_F_DO_EXT_NCONF), "do_ext_nconf"}, + {ERR_FUNC(X509V3_F_GNAMES_FROM_SECTNAME), "gnames_from_sectname"}, + {ERR_FUNC(X509V3_F_I2S_ASN1_ENUMERATED), "i2s_ASN1_ENUMERATED"}, + {ERR_FUNC(X509V3_F_I2S_ASN1_IA5STRING), "i2s_ASN1_IA5STRING"}, + {ERR_FUNC(X509V3_F_I2S_ASN1_INTEGER), "i2s_ASN1_INTEGER"}, + {ERR_FUNC(X509V3_F_I2V_AUTHORITY_INFO_ACCESS), + "i2v_AUTHORITY_INFO_ACCESS"}, + {ERR_FUNC(X509V3_F_NOTICE_SECTION), "notice_section"}, + {ERR_FUNC(X509V3_F_NREF_NOS), "nref_nos"}, + {ERR_FUNC(X509V3_F_POLICY_SECTION), "policy_section"}, + {ERR_FUNC(X509V3_F_PROCESS_PCI_VALUE), "process_pci_value"}, + {ERR_FUNC(X509V3_F_R2I_CERTPOL), "r2i_certpol"}, + {ERR_FUNC(X509V3_F_R2I_PCI), "r2i_pci"}, + {ERR_FUNC(X509V3_F_S2I_ASN1_IA5STRING), "s2i_ASN1_IA5STRING"}, + {ERR_FUNC(X509V3_F_S2I_ASN1_INTEGER), "s2i_ASN1_INTEGER"}, + {ERR_FUNC(X509V3_F_S2I_ASN1_OCTET_STRING), "s2i_ASN1_OCTET_STRING"}, + {ERR_FUNC(X509V3_F_S2I_SKEY_ID), "s2i_skey_id"}, + {ERR_FUNC(X509V3_F_SET_DIST_POINT_NAME), "set_dist_point_name"}, + {ERR_FUNC(X509V3_F_SXNET_ADD_ID_ASC), "SXNET_add_id_asc"}, + {ERR_FUNC(X509V3_F_SXNET_ADD_ID_INTEGER), "SXNET_add_id_INTEGER"}, + {ERR_FUNC(X509V3_F_SXNET_ADD_ID_ULONG), "SXNET_add_id_ulong"}, + {ERR_FUNC(X509V3_F_SXNET_GET_ID_ASC), "SXNET_get_id_asc"}, + {ERR_FUNC(X509V3_F_SXNET_GET_ID_ULONG), "SXNET_get_id_ulong"}, + {ERR_FUNC(X509V3_F_V2I_ASIDENTIFIERS), "v2i_ASIdentifiers"}, + {ERR_FUNC(X509V3_F_V2I_ASN1_BIT_STRING), "v2i_ASN1_BIT_STRING"}, + {ERR_FUNC(X509V3_F_V2I_AUTHORITY_INFO_ACCESS), + "v2i_AUTHORITY_INFO_ACCESS"}, + {ERR_FUNC(X509V3_F_V2I_AUTHORITY_KEYID), "v2i_AUTHORITY_KEYID"}, + {ERR_FUNC(X509V3_F_V2I_BASIC_CONSTRAINTS), "v2i_BASIC_CONSTRAINTS"}, + {ERR_FUNC(X509V3_F_V2I_CRLD), "v2i_crld"}, + {ERR_FUNC(X509V3_F_V2I_EXTENDED_KEY_USAGE), "v2i_EXTENDED_KEY_USAGE"}, + {ERR_FUNC(X509V3_F_V2I_GENERAL_NAMES), "v2i_GENERAL_NAMES"}, + {ERR_FUNC(X509V3_F_V2I_GENERAL_NAME_EX), "v2i_GENERAL_NAME_ex"}, + {ERR_FUNC(X509V3_F_V2I_IDP), "v2i_idp"}, + {ERR_FUNC(X509V3_F_V2I_IPADDRBLOCKS), "v2i_IPAddrBlocks"}, + {ERR_FUNC(X509V3_F_V2I_ISSUER_ALT), "v2i_issuer_alt"}, + {ERR_FUNC(X509V3_F_V2I_NAME_CONSTRAINTS), "v2i_NAME_CONSTRAINTS"}, + {ERR_FUNC(X509V3_F_V2I_POLICY_CONSTRAINTS), "v2i_POLICY_CONSTRAINTS"}, + {ERR_FUNC(X509V3_F_V2I_POLICY_MAPPINGS), "v2i_POLICY_MAPPINGS"}, + {ERR_FUNC(X509V3_F_V2I_SUBJECT_ALT), "v2i_subject_alt"}, + {ERR_FUNC(X509V3_F_V2I_TLS_FEATURE), "v2i_TLS_FEATURE"}, + {ERR_FUNC(X509V3_F_V3_GENERIC_EXTENSION), "v3_generic_extension"}, + {ERR_FUNC(X509V3_F_X509V3_ADD1_I2D), "X509V3_add1_i2d"}, + {ERR_FUNC(X509V3_F_X509V3_ADD_VALUE), "X509V3_add_value"}, + {ERR_FUNC(X509V3_F_X509V3_EXT_ADD), "X509V3_EXT_add"}, + {ERR_FUNC(X509V3_F_X509V3_EXT_ADD_ALIAS), "X509V3_EXT_add_alias"}, + {ERR_FUNC(X509V3_F_X509V3_EXT_I2D), "X509V3_EXT_i2d"}, + {ERR_FUNC(X509V3_F_X509V3_EXT_NCONF), "X509V3_EXT_nconf"}, + {ERR_FUNC(X509V3_F_X509V3_GET_SECTION), "X509V3_get_section"}, + {ERR_FUNC(X509V3_F_X509V3_GET_STRING), "X509V3_get_string"}, + {ERR_FUNC(X509V3_F_X509V3_GET_VALUE_BOOL), "X509V3_get_value_bool"}, + {ERR_FUNC(X509V3_F_X509V3_PARSE_LIST), "X509V3_parse_list"}, + {ERR_FUNC(X509V3_F_X509_PURPOSE_ADD), "X509_PURPOSE_add"}, + {ERR_FUNC(X509V3_F_X509_PURPOSE_SET), "X509_PURPOSE_set"}, + {0, NULL} +}; + +static ERR_STRING_DATA X509V3_str_reasons[] = { + {ERR_REASON(X509V3_R_BAD_IP_ADDRESS), "bad ip address"}, + {ERR_REASON(X509V3_R_BAD_OBJECT), "bad object"}, + {ERR_REASON(X509V3_R_BN_DEC2BN_ERROR), "bn dec2bn error"}, + {ERR_REASON(X509V3_R_BN_TO_ASN1_INTEGER_ERROR), + "bn to asn1 integer error"}, + {ERR_REASON(X509V3_R_DIRNAME_ERROR), "dirname error"}, + {ERR_REASON(X509V3_R_DISTPOINT_ALREADY_SET), "distpoint already set"}, + {ERR_REASON(X509V3_R_DUPLICATE_ZONE_ID), "duplicate zone id"}, + {ERR_REASON(X509V3_R_ERROR_CONVERTING_ZONE), "error converting zone"}, + {ERR_REASON(X509V3_R_ERROR_CREATING_EXTENSION), + "error creating extension"}, + {ERR_REASON(X509V3_R_ERROR_IN_EXTENSION), "error in extension"}, + {ERR_REASON(X509V3_R_EXPECTED_A_SECTION_NAME), "expected a section name"}, + {ERR_REASON(X509V3_R_EXTENSION_EXISTS), "extension exists"}, + {ERR_REASON(X509V3_R_EXTENSION_NAME_ERROR), "extension name error"}, + {ERR_REASON(X509V3_R_EXTENSION_NOT_FOUND), "extension not found"}, + {ERR_REASON(X509V3_R_EXTENSION_SETTING_NOT_SUPPORTED), + "extension setting not supported"}, + {ERR_REASON(X509V3_R_EXTENSION_VALUE_ERROR), "extension value error"}, + {ERR_REASON(X509V3_R_ILLEGAL_EMPTY_EXTENSION), "illegal empty extension"}, + {ERR_REASON(X509V3_R_INCORRECT_POLICY_SYNTAX_TAG), + "incorrect policy syntax tag"}, + {ERR_REASON(X509V3_R_INVALID_ASNUMBER), "invalid asnumber"}, + {ERR_REASON(X509V3_R_INVALID_ASRANGE), "invalid asrange"}, + {ERR_REASON(X509V3_R_INVALID_BOOLEAN_STRING), "invalid boolean string"}, + {ERR_REASON(X509V3_R_INVALID_EXTENSION_STRING), + "invalid extension string"}, + {ERR_REASON(X509V3_R_INVALID_INHERITANCE), "invalid inheritance"}, + {ERR_REASON(X509V3_R_INVALID_IPADDRESS), "invalid ipaddress"}, + {ERR_REASON(X509V3_R_INVALID_MULTIPLE_RDNS), "invalid multiple rdns"}, + {ERR_REASON(X509V3_R_INVALID_NAME), "invalid name"}, + {ERR_REASON(X509V3_R_INVALID_NULL_ARGUMENT), "invalid null argument"}, + {ERR_REASON(X509V3_R_INVALID_NULL_NAME), "invalid null name"}, + {ERR_REASON(X509V3_R_INVALID_NULL_VALUE), "invalid null value"}, + {ERR_REASON(X509V3_R_INVALID_NUMBER), "invalid number"}, + {ERR_REASON(X509V3_R_INVALID_NUMBERS), "invalid numbers"}, + {ERR_REASON(X509V3_R_INVALID_OBJECT_IDENTIFIER), + "invalid object identifier"}, + {ERR_REASON(X509V3_R_INVALID_OPTION), "invalid option"}, + {ERR_REASON(X509V3_R_INVALID_POLICY_IDENTIFIER), + "invalid policy identifier"}, + {ERR_REASON(X509V3_R_INVALID_PROXY_POLICY_SETTING), + "invalid proxy policy setting"}, + {ERR_REASON(X509V3_R_INVALID_PURPOSE), "invalid purpose"}, + {ERR_REASON(X509V3_R_INVALID_SAFI), "invalid safi"}, + {ERR_REASON(X509V3_R_INVALID_SECTION), "invalid section"}, + {ERR_REASON(X509V3_R_INVALID_SYNTAX), "invalid syntax"}, + {ERR_REASON(X509V3_R_ISSUER_DECODE_ERROR), "issuer decode error"}, + {ERR_REASON(X509V3_R_MISSING_VALUE), "missing value"}, + {ERR_REASON(X509V3_R_NEED_ORGANIZATION_AND_NUMBERS), + "need organization and numbers"}, + {ERR_REASON(X509V3_R_NO_CONFIG_DATABASE), "no config database"}, + {ERR_REASON(X509V3_R_NO_ISSUER_CERTIFICATE), "no issuer certificate"}, + {ERR_REASON(X509V3_R_NO_ISSUER_DETAILS), "no issuer details"}, + {ERR_REASON(X509V3_R_NO_POLICY_IDENTIFIER), "no policy identifier"}, + {ERR_REASON(X509V3_R_NO_PROXY_CERT_POLICY_LANGUAGE_DEFINED), + "no proxy cert policy language defined"}, + {ERR_REASON(X509V3_R_NO_PUBLIC_KEY), "no public key"}, + {ERR_REASON(X509V3_R_NO_SUBJECT_DETAILS), "no subject details"}, + {ERR_REASON(X509V3_R_OPERATION_NOT_DEFINED), "operation not defined"}, + {ERR_REASON(X509V3_R_OTHERNAME_ERROR), "othername error"}, + {ERR_REASON(X509V3_R_POLICY_LANGUAGE_ALREADY_DEFINED), + "policy language already defined"}, + {ERR_REASON(X509V3_R_POLICY_PATH_LENGTH), "policy path length"}, + {ERR_REASON(X509V3_R_POLICY_PATH_LENGTH_ALREADY_DEFINED), + "policy path length already defined"}, + {ERR_REASON(X509V3_R_POLICY_WHEN_PROXY_LANGUAGE_REQUIRES_NO_POLICY), + "policy when proxy language requires no policy"}, + {ERR_REASON(X509V3_R_SECTION_NOT_FOUND), "section not found"}, + {ERR_REASON(X509V3_R_UNABLE_TO_GET_ISSUER_DETAILS), + "unable to get issuer details"}, + {ERR_REASON(X509V3_R_UNABLE_TO_GET_ISSUER_KEYID), + "unable to get issuer keyid"}, + {ERR_REASON(X509V3_R_UNKNOWN_BIT_STRING_ARGUMENT), + "unknown bit string argument"}, + {ERR_REASON(X509V3_R_UNKNOWN_EXTENSION), "unknown extension"}, + {ERR_REASON(X509V3_R_UNKNOWN_EXTENSION_NAME), "unknown extension name"}, + {ERR_REASON(X509V3_R_UNKNOWN_OPTION), "unknown option"}, + {ERR_REASON(X509V3_R_UNSUPPORTED_OPTION), "unsupported option"}, + {ERR_REASON(X509V3_R_UNSUPPORTED_TYPE), "unsupported type"}, + {ERR_REASON(X509V3_R_USER_TOO_LONG), "user too long"}, + {0, NULL} +}; + +#endif + +int ERR_load_X509V3_strings(void) +{ +#ifndef OPENSSL_NO_ERR + + if (ERR_func_error_string(X509V3_str_functs[0].error) == NULL) { + ERR_load_strings(0, X509V3_str_functs); + ERR_load_strings(0, X509V3_str_reasons); + } +#endif + return 1; +} diff --git a/crypto/x509v3/v3prin.c b/crypto/x509v3/v3prin.c new file mode 100644 index 000000000..7431a4ea6 --- /dev/null +++ b/crypto/x509v3/v3prin.c @@ -0,0 +1,50 @@ +/* + * Copyright 1999-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#include +#include +#include +#include + +int main(int argc, char **argv) +{ + X509 *cert; + FILE *inf; + int i, count; + X509_EXTENSION *ext; + + X509V3_add_standard_extensions(); + ERR_load_crypto_strings(); + if (!argv[1]) { + fprintf(stderr, "Usage v3prin cert.pem\n"); + exit(1); + } + if ((inf = fopen(argv[1], "r")) == NULL) { + fprintf(stderr, "Can't open %s\n", argv[1]); + exit(1); + } + if ((cert = PEM_read_X509(inf, NULL, NULL)) == NULL) { + fprintf(stderr, "Can't read certificate %s\n", argv[1]); + ERR_print_errors_fp(stderr); + exit(1); + } + fclose(inf); + count = X509_get_ext_count(cert); + printf("%d extensions\n", count); + for (i = 0; i < count; i++) { + ext = X509_get_ext(cert, i); + printf("%s\n", OBJ_nid2ln(OBJ_obj2nid(ext->object))); + if (!X509V3_EXT_print_fp(stdout, ext, 0, 0)) + ERR_print_errors_fp(stderr); + printf("\n"); + + } + return 0; +} diff --git a/crypto/x86_64cpuid.pl b/crypto/x86_64cpuid.pl new file mode 100644 index 000000000..6cb152148 --- /dev/null +++ b/crypto/x86_64cpuid.pl @@ -0,0 +1,459 @@ +#! /usr/bin/env perl +# Copyright 2005-2016 The OpenSSL Project Authors. All Rights Reserved. +# +# Licensed under the OpenSSL license (the "License"). You may not use +# this file except in compliance with the License. You can obtain a copy +# in the file LICENSE in the source distribution or at +# https://www.openssl.org/source/license.html + + +$flavour = shift; +$output = shift; +if ($flavour =~ /\./) { $output = $flavour; undef $flavour; } + +$win64=0; $win64=1 if ($flavour =~ /[nm]asm|mingw64/ || $output =~ /\.asm$/); + +$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1; +( $xlate="${dir}x86_64-xlate.pl" and -f $xlate ) or +( $xlate="${dir}perlasm/x86_64-xlate.pl" and -f $xlate) or +die "can't locate x86_64-xlate.pl"; + +open OUT,"| \"$^X\" \"$xlate\" $flavour \"$output\""; +*STDOUT=*OUT; + +($arg1,$arg2,$arg3,$arg4)=$win64?("%rcx","%rdx","%r8", "%r9") : # Win64 order + ("%rdi","%rsi","%rdx","%rcx"); # Unix order + +print<<___; +.extern OPENSSL_cpuid_setup +.hidden OPENSSL_cpuid_setup +.section .init + call OPENSSL_cpuid_setup + +.hidden OPENSSL_ia32cap_P +.comm OPENSSL_ia32cap_P,16,4 + +.text + +.globl OPENSSL_atomic_add +.type OPENSSL_atomic_add,\@abi-omnipotent +.align 16 +OPENSSL_atomic_add: + movl ($arg1),%eax +.Lspin: leaq ($arg2,%rax),%r8 + .byte 0xf0 # lock + cmpxchgl %r8d,($arg1) + jne .Lspin + movl %r8d,%eax + .byte 0x48,0x98 # cltq/cdqe + ret +.size OPENSSL_atomic_add,.-OPENSSL_atomic_add + +.globl OPENSSL_rdtsc +.type OPENSSL_rdtsc,\@abi-omnipotent +.align 16 +OPENSSL_rdtsc: + rdtsc + shl \$32,%rdx + or %rdx,%rax + ret +.size OPENSSL_rdtsc,.-OPENSSL_rdtsc + +.globl OPENSSL_ia32_cpuid +.type OPENSSL_ia32_cpuid,\@function,1 +.align 16 +OPENSSL_ia32_cpuid: + mov %rbx,%r8 # save %rbx + + xor %eax,%eax + mov %eax,8(%rdi) # clear 3rd word + cpuid + mov %eax,%r11d # max value for standard query level + + xor %eax,%eax + cmp \$0x756e6547,%ebx # "Genu" + setne %al + mov %eax,%r9d + cmp \$0x49656e69,%edx # "ineI" + setne %al + or %eax,%r9d + cmp \$0x6c65746e,%ecx # "ntel" + setne %al + or %eax,%r9d # 0 indicates Intel CPU + jz .Lintel + + cmp \$0x68747541,%ebx # "Auth" + setne %al + mov %eax,%r10d + cmp \$0x69746E65,%edx # "enti" + setne %al + or %eax,%r10d + cmp \$0x444D4163,%ecx # "cAMD" + setne %al + or %eax,%r10d # 0 indicates AMD CPU + jnz .Lintel + + # AMD specific + mov \$0x80000000,%eax + cpuid + cmp \$0x80000001,%eax + jb .Lintel + mov %eax,%r10d + mov \$0x80000001,%eax + cpuid + or %ecx,%r9d + and \$0x00000801,%r9d # isolate AMD XOP bit, 1<<11 + + cmp \$0x80000008,%r10d + jb .Lintel + + mov \$0x80000008,%eax + cpuid + movzb %cl,%r10 # number of cores - 1 + inc %r10 # number of cores + + mov \$1,%eax + cpuid + bt \$28,%edx # test hyper-threading bit + jnc .Lgeneric + shr \$16,%ebx # number of logical processors + cmp %r10b,%bl + ja .Lgeneric + and \$0xefffffff,%edx # ~(1<<28) + jmp .Lgeneric + +.Lintel: + cmp \$4,%r11d + mov \$-1,%r10d + jb .Lnocacheinfo + + mov \$4,%eax + mov \$0,%ecx # query L1D + cpuid + mov %eax,%r10d + shr \$14,%r10d + and \$0xfff,%r10d # number of cores -1 per L1D + + cmp \$7,%r11d + jb .Lnocacheinfo + + mov \$7,%eax + xor %ecx,%ecx + cpuid + mov %ebx,8(%rdi) + +.Lnocacheinfo: + mov \$1,%eax + cpuid + and \$0xbfefffff,%edx # force reserved bits to 0 + cmp \$0,%r9d + jne .Lnotintel + or \$0x40000000,%edx # set reserved bit#30 on Intel CPUs + and \$15,%ah + cmp \$15,%ah # examine Family ID + jne .Lnotintel + or \$0x00100000,%edx # set reserved bit#20 to engage RC4_CHAR +.Lnotintel: + bt \$28,%edx # test hyper-threading bit + jnc .Lgeneric + and \$0xefffffff,%edx # ~(1<<28) + cmp \$0,%r10d + je .Lgeneric + + or \$0x10000000,%edx # 1<<28 + shr \$16,%ebx + cmp \$1,%bl # see if cache is shared + ja .Lgeneric + and \$0xefffffff,%edx # ~(1<<28) +.Lgeneric: + and \$0x00000800,%r9d # isolate AMD XOP flag + and \$0xfffff7ff,%ecx + or %ecx,%r9d # merge AMD XOP flag + + mov %edx,%r10d # %r9d:%r10d is copy of %ecx:%edx + bt \$27,%r9d # check OSXSAVE bit + jnc .Lclear_avx + xor %ecx,%ecx # XCR0 + .byte 0x0f,0x01,0xd0 # xgetbv + and \$6,%eax # isolate XMM and YMM state support + cmp \$6,%eax + je .Ldone +.Lclear_avx: + mov \$0xefffe7ff,%eax # ~(1<<28|1<<12|1<<11) + and %eax,%r9d # clear AVX, FMA and AMD XOP bits + andl \$0xffffffdf,8(%rdi) # cleax AVX2, ~(1<<5) +.Ldone: + shl \$32,%r9 + mov %r10d,%eax + mov %r8,%rbx # restore %rbx + or %r9,%rax + ret +.size OPENSSL_ia32_cpuid,.-OPENSSL_ia32_cpuid + +.globl OPENSSL_cleanse +.type OPENSSL_cleanse,\@abi-omnipotent +.align 16 +OPENSSL_cleanse: + xor %rax,%rax + cmp \$15,$arg2 + jae .Lot + cmp \$0,$arg2 + je .Lret +.Little: + mov %al,($arg1) + sub \$1,$arg2 + lea 1($arg1),$arg1 + jnz .Little +.Lret: + ret +.align 16 +.Lot: + test \$7,$arg1 + jz .Laligned + mov %al,($arg1) + lea -1($arg2),$arg2 + lea 1($arg1),$arg1 + jmp .Lot +.Laligned: + mov %rax,($arg1) + lea -8($arg2),$arg2 + test \$-8,$arg2 + lea 8($arg1),$arg1 + jnz .Laligned + cmp \$0,$arg2 + jne .Little + ret +.size OPENSSL_cleanse,.-OPENSSL_cleanse + +.globl CRYPTO_memcmp +.type CRYPTO_memcmp,\@abi-omnipotent +.align 16 +CRYPTO_memcmp: + xor %rax,%rax + xor %r10,%r10 + cmp \$0,$arg3 + je .Lno_data +.Loop_cmp: + mov ($arg1),%r10b + lea 1($arg1),$arg1 + xor ($arg2),%r10b + lea 1($arg2),$arg2 + or %r10b,%al + dec $arg3 + jnz .Loop_cmp + neg %rax + shr \$63,%rax +.Lno_data: + ret +.size CRYPTO_memcmp,.-CRYPTO_memcmp +___ + +print<<___ if (!$win64); +.globl OPENSSL_wipe_cpu +.type OPENSSL_wipe_cpu,\@abi-omnipotent +.align 16 +OPENSSL_wipe_cpu: + pxor %xmm0,%xmm0 + pxor %xmm1,%xmm1 + pxor %xmm2,%xmm2 + pxor %xmm3,%xmm3 + pxor %xmm4,%xmm4 + pxor %xmm5,%xmm5 + pxor %xmm6,%xmm6 + pxor %xmm7,%xmm7 + pxor %xmm8,%xmm8 + pxor %xmm9,%xmm9 + pxor %xmm10,%xmm10 + pxor %xmm11,%xmm11 + pxor %xmm12,%xmm12 + pxor %xmm13,%xmm13 + pxor %xmm14,%xmm14 + pxor %xmm15,%xmm15 + xorq %rcx,%rcx + xorq %rdx,%rdx + xorq %rsi,%rsi + xorq %rdi,%rdi + xorq %r8,%r8 + xorq %r9,%r9 + xorq %r10,%r10 + xorq %r11,%r11 + leaq 8(%rsp),%rax + ret +.size OPENSSL_wipe_cpu,.-OPENSSL_wipe_cpu +___ +print<<___ if ($win64); +.globl OPENSSL_wipe_cpu +.type OPENSSL_wipe_cpu,\@abi-omnipotent +.align 16 +OPENSSL_wipe_cpu: + pxor %xmm0,%xmm0 + pxor %xmm1,%xmm1 + pxor %xmm2,%xmm2 + pxor %xmm3,%xmm3 + pxor %xmm4,%xmm4 + pxor %xmm5,%xmm5 + xorq %rcx,%rcx + xorq %rdx,%rdx + xorq %r8,%r8 + xorq %r9,%r9 + xorq %r10,%r10 + xorq %r11,%r11 + leaq 8(%rsp),%rax + ret +.size OPENSSL_wipe_cpu,.-OPENSSL_wipe_cpu +___ +{ +my $out="%r10"; +my $cnt="%rcx"; +my $max="%r11"; +my $lasttick="%r8d"; +my $lastdiff="%r9d"; +my $redzone=win64?8:-8; + +print<<___; +.globl OPENSSL_instrument_bus +.type OPENSSL_instrument_bus,\@abi-omnipotent +.align 16 +OPENSSL_instrument_bus: + mov $arg1,$out # tribute to Win64 + mov $arg2,$cnt + mov $arg2,$max + + rdtsc # collect 1st tick + mov %eax,$lasttick # lasttick = tick + mov \$0,$lastdiff # lastdiff = 0 + clflush ($out) + .byte 0xf0 # lock + add $lastdiff,($out) + jmp .Loop +.align 16 +.Loop: rdtsc + mov %eax,%edx + sub $lasttick,%eax + mov %edx,$lasttick + mov %eax,$lastdiff + clflush ($out) + .byte 0xf0 # lock + add %eax,($out) + lea 4($out),$out + sub \$1,$cnt + jnz .Loop + + mov $max,%rax + ret +.size OPENSSL_instrument_bus,.-OPENSSL_instrument_bus + +.globl OPENSSL_instrument_bus2 +.type OPENSSL_instrument_bus2,\@abi-omnipotent +.align 16 +OPENSSL_instrument_bus2: + mov $arg1,$out # tribute to Win64 + mov $arg2,$cnt + mov $arg3,$max + mov $cnt,$redzone(%rsp) + + rdtsc # collect 1st tick + mov %eax,$lasttick # lasttick = tick + mov \$0,$lastdiff # lastdiff = 0 + + clflush ($out) + .byte 0xf0 # lock + add $lastdiff,($out) + + rdtsc # collect 1st diff + mov %eax,%edx + sub $lasttick,%eax # diff + mov %edx,$lasttick # lasttick = tick + mov %eax,$lastdiff # lastdiff = diff +.Loop2: + clflush ($out) + .byte 0xf0 # lock + add %eax,($out) # accumulate diff + + sub \$1,$max + jz .Ldone2 + + rdtsc + mov %eax,%edx + sub $lasttick,%eax # diff + mov %edx,$lasttick # lasttick = tick + cmp $lastdiff,%eax + mov %eax,$lastdiff # lastdiff = diff + mov \$0,%edx + setne %dl + sub %rdx,$cnt # conditional --$cnt + lea ($out,%rdx,4),$out # conditional ++$out + jnz .Loop2 + +.Ldone2: + mov $redzone(%rsp),%rax + sub $cnt,%rax + ret +.size OPENSSL_instrument_bus2,.-OPENSSL_instrument_bus2 +___ +} + +sub gen_random { +my $rdop = shift; +print<<___; +.globl OPENSSL_ia32_${rdop} +.type OPENSSL_ia32_${rdop},\@abi-omnipotent +.align 16 +OPENSSL_ia32_${rdop}: + mov \$8,%ecx +.Loop_${rdop}: + ${rdop} %rax + jc .Lbreak_${rdop} + loop .Loop_${rdop} +.Lbreak_${rdop}: + cmp \$0,%rax + cmove %rcx,%rax + ret +.size OPENSSL_ia32_${rdop},.-OPENSSL_ia32_${rdop} + +.globl OPENSSL_ia32_${rdop}_bytes +.type OPENSSL_ia32_${rdop}_bytes,\@abi-omnipotent +.align 16 +OPENSSL_ia32_${rdop}_bytes: + xor %rax, %rax # return value + cmp \$0,$arg2 + je .Ldone_${rdop}_bytes + + mov \$8,%r11 +.Loop_${rdop}_bytes: + ${rdop} %r10 + jc .Lbreak_${rdop}_bytes + dec %r11 + jnz .Loop_${rdop}_bytes + jmp .Ldone_${rdop}_bytes + +.align 16 +.Lbreak_${rdop}_bytes: + cmp \$8,$arg2 + jb .Ltail_${rdop}_bytes + mov %r10,($arg1) + lea 8($arg1),$arg1 + add \$8,%rax + sub \$8,$arg2 + jz .Ldone_${rdop}_bytes + mov \$8,%r11 + jmp .Loop_${rdop}_bytes + +.align 16 +.Ltail_${rdop}_bytes: + mov %r10b,($arg1) + lea 1($arg1),$arg1 + inc %rax + shr \$8,%r8 + dec $arg2 + jnz .Ltail_${rdop}_bytes + +.Ldone_${rdop}_bytes: + ret +.size OPENSSL_ia32_${rdop}_bytes,.-OPENSSL_ia32_${rdop}_bytes +___ +} +gen_random("rdrand"); +gen_random("rdseed"); + +close STDOUT; # flush diff --git a/crypto/x86cpuid.pl b/crypto/x86cpuid.pl new file mode 100644 index 000000000..c45b18356 --- /dev/null +++ b/crypto/x86cpuid.pl @@ -0,0 +1,561 @@ +#! /usr/bin/env perl +# Copyright 2004-2016 The OpenSSL Project Authors. All Rights Reserved. +# +# Licensed under the OpenSSL license (the "License"). You may not use +# this file except in compliance with the License. You can obtain a copy +# in the file LICENSE in the source distribution or at +# https://www.openssl.org/source/license.html + +$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1; +push(@INC, "${dir}perlasm", "perlasm"); +require "x86asm.pl"; + +$output = pop; +open OUT,">$output"; +*STDOUT=*OUT; + +&asm_init($ARGV[0],"x86cpuid"); + +for (@ARGV) { $sse2=1 if (/-DOPENSSL_IA32_SSE2/); } + +&function_begin("OPENSSL_ia32_cpuid"); + &xor ("edx","edx"); + &pushf (); + &pop ("eax"); + &mov ("ecx","eax"); + &xor ("eax",1<<21); + &push ("eax"); + &popf (); + &pushf (); + &pop ("eax"); + &xor ("ecx","eax"); + &xor ("eax","eax"); + &bt ("ecx",21); + &jnc (&label("nocpuid")); + &mov ("esi",&wparam(0)); + &mov (&DWP(8,"esi"),"eax"); # clear 3rd word + &cpuid (); + &mov ("edi","eax"); # max value for standard query level + + &xor ("eax","eax"); + &cmp ("ebx",0x756e6547); # "Genu" + &setne (&LB("eax")); + &mov ("ebp","eax"); + &cmp ("edx",0x49656e69); # "ineI" + &setne (&LB("eax")); + &or ("ebp","eax"); + &cmp ("ecx",0x6c65746e); # "ntel" + &setne (&LB("eax")); + &or ("ebp","eax"); # 0 indicates Intel CPU + &jz (&label("intel")); + + &cmp ("ebx",0x68747541); # "Auth" + &setne (&LB("eax")); + &mov ("esi","eax"); + &cmp ("edx",0x69746E65); # "enti" + &setne (&LB("eax")); + &or ("esi","eax"); + &cmp ("ecx",0x444D4163); # "cAMD" + &setne (&LB("eax")); + &or ("esi","eax"); # 0 indicates AMD CPU + &jnz (&label("intel")); + + # AMD specific + &mov ("eax",0x80000000); + &cpuid (); + &cmp ("eax",0x80000001); + &jb (&label("intel")); + &mov ("esi","eax"); + &mov ("eax",0x80000001); + &cpuid (); + &or ("ebp","ecx"); + &and ("ebp",1<<11|1); # isolate XOP bit + &cmp ("esi",0x80000008); + &jb (&label("intel")); + + &mov ("eax",0x80000008); + &cpuid (); + &movz ("esi",&LB("ecx")); # number of cores - 1 + &inc ("esi"); # number of cores + + &mov ("eax",1); + &xor ("ecx","ecx"); + &cpuid (); + &bt ("edx",28); + &jnc (&label("generic")); + &shr ("ebx",16); + &and ("ebx",0xff); + &cmp ("ebx","esi"); + &ja (&label("generic")); + &and ("edx",0xefffffff); # clear hyper-threading bit + &jmp (&label("generic")); + +&set_label("intel"); + &cmp ("edi",7); + &jb (&label("cacheinfo")); + + &mov ("esi",&wparam(0)); + &mov ("eax",7); + &xor ("ecx","ecx"); + &cpuid (); + &mov (&DWP(8,"esi"),"ebx"); + +&set_label("cacheinfo"); + &cmp ("edi",4); + &mov ("edi",-1); + &jb (&label("nocacheinfo")); + + &mov ("eax",4); + &mov ("ecx",0); # query L1D + &cpuid (); + &mov ("edi","eax"); + &shr ("edi",14); + &and ("edi",0xfff); # number of cores -1 per L1D + +&set_label("nocacheinfo"); + &mov ("eax",1); + &xor ("ecx","ecx"); + &cpuid (); + &and ("edx",0xbfefffff); # force reserved bits #20, #30 to 0 + &cmp ("ebp",0); + &jne (&label("notintel")); + &or ("edx",1<<30); # set reserved bit#30 on Intel CPUs + &and (&HB("eax"),15); # familiy ID + &cmp (&HB("eax"),15); # P4? + &jne (&label("notintel")); + &or ("edx",1<<20); # set reserved bit#20 to engage RC4_CHAR +&set_label("notintel"); + &bt ("edx",28); # test hyper-threading bit + &jnc (&label("generic")); + &and ("edx",0xefffffff); + &cmp ("edi",0); + &je (&label("generic")); + + &or ("edx",0x10000000); + &shr ("ebx",16); + &cmp (&LB("ebx"),1); + &ja (&label("generic")); + &and ("edx",0xefffffff); # clear hyper-threading bit if not + +&set_label("generic"); + &and ("ebp",1<<11); # isolate AMD XOP flag + &and ("ecx",0xfffff7ff); # force 11th bit to 0 + &mov ("esi","edx"); + &or ("ebp","ecx"); # merge AMD XOP flag + + &bt ("ecx",27); # check OSXSAVE bit + &jnc (&label("clear_avx")); + &xor ("ecx","ecx"); + &data_byte(0x0f,0x01,0xd0); # xgetbv + &and ("eax",6); + &cmp ("eax",6); + &je (&label("done")); + &cmp ("eax",2); + &je (&label("clear_avx")); +&set_label("clear_xmm"); + &and ("ebp",0xfdfffffd); # clear AESNI and PCLMULQDQ bits + &and ("esi",0xfeffffff); # clear FXSR +&set_label("clear_avx"); + &and ("ebp",0xefffe7ff); # clear AVX, FMA and AMD XOP bits + &mov ("edi",&wparam(0)); + &and (&DWP(8,"edi"),0xffffffdf); # clear AVX2 +&set_label("done"); + &mov ("eax","esi"); + &mov ("edx","ebp"); +&set_label("nocpuid"); +&function_end("OPENSSL_ia32_cpuid"); + +&external_label("OPENSSL_ia32cap_P"); + +&function_begin_B("OPENSSL_rdtsc","EXTRN\t_OPENSSL_ia32cap_P:DWORD"); + &xor ("eax","eax"); + &xor ("edx","edx"); + &picmeup("ecx","OPENSSL_ia32cap_P"); + &bt (&DWP(0,"ecx"),4); + &jnc (&label("notsc")); + &rdtsc (); +&set_label("notsc"); + &ret (); +&function_end_B("OPENSSL_rdtsc"); + +# This works in Ring 0 only [read DJGPP+MS-DOS+privileged DPMI host], +# but it's safe to call it on any [supported] 32-bit platform... +# Just check for [non-]zero return value... +&function_begin_B("OPENSSL_instrument_halt","EXTRN\t_OPENSSL_ia32cap_P:DWORD"); + &picmeup("ecx","OPENSSL_ia32cap_P"); + &bt (&DWP(0,"ecx"),4); + &jnc (&label("nohalt")); # no TSC + + &data_word(0x9058900e); # push %cs; pop %eax + &and ("eax",3); + &jnz (&label("nohalt")); # not enough privileges + + &pushf (); + &pop ("eax"); + &bt ("eax",9); + &jnc (&label("nohalt")); # interrupts are disabled + + &rdtsc (); + &push ("edx"); + &push ("eax"); + &halt (); + &rdtsc (); + + &sub ("eax",&DWP(0,"esp")); + &sbb ("edx",&DWP(4,"esp")); + &add ("esp",8); + &ret (); + +&set_label("nohalt"); + &xor ("eax","eax"); + &xor ("edx","edx"); + &ret (); +&function_end_B("OPENSSL_instrument_halt"); + +# Essentially there is only one use for this function. Under DJGPP: +# +# #include +# ... +# i=OPENSSL_far_spin(_dos_ds,0x46c); +# ... +# to obtain the number of spins till closest timer interrupt. + +&function_begin_B("OPENSSL_far_spin"); + &pushf (); + &pop ("eax"); + &bt ("eax",9); + &jnc (&label("nospin")); # interrupts are disabled + + &mov ("eax",&DWP(4,"esp")); + &mov ("ecx",&DWP(8,"esp")); + &data_word (0x90d88e1e); # push %ds, mov %eax,%ds + &xor ("eax","eax"); + &mov ("edx",&DWP(0,"ecx")); + &jmp (&label("spin")); + + &align (16); +&set_label("spin"); + &inc ("eax"); + &cmp ("edx",&DWP(0,"ecx")); + &je (&label("spin")); + + &data_word (0x1f909090); # pop %ds + &ret (); + +&set_label("nospin"); + &xor ("eax","eax"); + &xor ("edx","edx"); + &ret (); +&function_end_B("OPENSSL_far_spin"); + +&function_begin_B("OPENSSL_wipe_cpu","EXTRN\t_OPENSSL_ia32cap_P:DWORD"); + &xor ("eax","eax"); + &xor ("edx","edx"); + &picmeup("ecx","OPENSSL_ia32cap_P"); + &mov ("ecx",&DWP(0,"ecx")); + &bt (&DWP(0,"ecx"),1); + &jnc (&label("no_x87")); + if ($sse2) { + &and ("ecx",1<<26|1<<24); # check SSE2 and FXSR bits + &cmp ("ecx",1<<26|1<<24); + &jne (&label("no_sse2")); + &pxor ("xmm0","xmm0"); + &pxor ("xmm1","xmm1"); + &pxor ("xmm2","xmm2"); + &pxor ("xmm3","xmm3"); + &pxor ("xmm4","xmm4"); + &pxor ("xmm5","xmm5"); + &pxor ("xmm6","xmm6"); + &pxor ("xmm7","xmm7"); + &set_label("no_sse2"); + } + # just a bunch of fldz to zap the fp/mm bank followed by finit... + &data_word(0xeed9eed9,0xeed9eed9,0xeed9eed9,0xeed9eed9,0x90e3db9b); +&set_label("no_x87"); + &lea ("eax",&DWP(4,"esp")); + &ret (); +&function_end_B("OPENSSL_wipe_cpu"); + +&function_begin_B("OPENSSL_atomic_add"); + &mov ("edx",&DWP(4,"esp")); # fetch the pointer, 1st arg + &mov ("ecx",&DWP(8,"esp")); # fetch the increment, 2nd arg + &push ("ebx"); + &nop (); + &mov ("eax",&DWP(0,"edx")); +&set_label("spin"); + &lea ("ebx",&DWP(0,"eax","ecx")); + &nop (); + &data_word(0x1ab10ff0); # lock; cmpxchg %ebx,(%edx) # %eax is envolved and is always reloaded + &jne (&label("spin")); + &mov ("eax","ebx"); # OpenSSL expects the new value + &pop ("ebx"); + &ret (); +&function_end_B("OPENSSL_atomic_add"); + +# This function can become handy under Win32 in situations when +# we don't know which calling convention, __stdcall or __cdecl(*), +# indirect callee is using. In C it can be deployed as +# +#ifdef OPENSSL_CPUID_OBJ +# type OPENSSL_indirect_call(void *f,...); +# ... +# OPENSSL_indirect_call(func,[up to $max arguments]); +#endif +# +# (*) it's designed to work even for __fastcall if number of +# arguments is 1 or 2! +&function_begin_B("OPENSSL_indirect_call"); + { + my ($max,$i)=(7,); # $max has to be chosen as 4*n-1 + # in order to preserve eventual + # stack alignment + &push ("ebp"); + &mov ("ebp","esp"); + &sub ("esp",$max*4); + &mov ("ecx",&DWP(12,"ebp")); + &mov (&DWP(0,"esp"),"ecx"); + &mov ("edx",&DWP(16,"ebp")); + &mov (&DWP(4,"esp"),"edx"); + for($i=2;$i<$max;$i++) + { + # Some copies will be redundant/bogus... + &mov ("eax",&DWP(12+$i*4,"ebp")); + &mov (&DWP(0+$i*4,"esp"),"eax"); + } + &call_ptr (&DWP(8,"ebp"));# make the call... + &mov ("esp","ebp"); # ... and just restore the stack pointer + # without paying attention to what we called, + # (__cdecl *func) or (__stdcall *one). + &pop ("ebp"); + &ret (); + } +&function_end_B("OPENSSL_indirect_call"); + +&function_begin_B("OPENSSL_cleanse"); + &mov ("edx",&wparam(0)); + &mov ("ecx",&wparam(1)); + &xor ("eax","eax"); + &cmp ("ecx",7); + &jae (&label("lot")); + &cmp ("ecx",0); + &je (&label("ret")); +&set_label("little"); + &mov (&BP(0,"edx"),"al"); + &sub ("ecx",1); + &lea ("edx",&DWP(1,"edx")); + &jnz (&label("little")); +&set_label("ret"); + &ret (); + +&set_label("lot",16); + &test ("edx",3); + &jz (&label("aligned")); + &mov (&BP(0,"edx"),"al"); + &lea ("ecx",&DWP(-1,"ecx")); + &lea ("edx",&DWP(1,"edx")); + &jmp (&label("lot")); +&set_label("aligned"); + &mov (&DWP(0,"edx"),"eax"); + &lea ("ecx",&DWP(-4,"ecx")); + &test ("ecx",-4); + &lea ("edx",&DWP(4,"edx")); + &jnz (&label("aligned")); + &cmp ("ecx",0); + &jne (&label("little")); + &ret (); +&function_end_B("OPENSSL_cleanse"); + +&function_begin_B("CRYPTO_memcmp"); + &push ("esi"); + &push ("edi"); + &mov ("esi",&wparam(0)); + &mov ("edi",&wparam(1)); + &mov ("ecx",&wparam(2)); + &xor ("eax","eax"); + &xor ("edx","edx"); + &cmp ("ecx",0); + &je (&label("no_data")); +&set_label("loop"); + &mov ("dl",&BP(0,"esi")); + &lea ("esi",&DWP(1,"esi")); + &xor ("dl",&BP(0,"edi")); + &lea ("edi",&DWP(1,"edi")); + &or ("al","dl"); + &dec ("ecx"); + &jnz (&label("loop")); + &neg ("eax"); + &shr ("eax",31); +&set_label("no_data"); + &pop ("edi"); + &pop ("esi"); + &ret (); +&function_end_B("CRYPTO_memcmp"); +{ +my $lasttick = "esi"; +my $lastdiff = "ebx"; +my $out = "edi"; +my $cnt = "ecx"; +my $max = "ebp"; + +&function_begin("OPENSSL_instrument_bus"); + &mov ("eax",0); + if ($sse2) { + &picmeup("edx","OPENSSL_ia32cap_P"); + &bt (&DWP(0,"edx"),4); + &jnc (&label("nogo")); # no TSC + &bt (&DWP(0,"edx"),19); + &jnc (&label("nogo")); # no CLFLUSH + + &mov ($out,&wparam(0)); # load arguments + &mov ($cnt,&wparam(1)); + + # collect 1st tick + &rdtsc (); + &mov ($lasttick,"eax"); # lasttick = tick + &mov ($lastdiff,0); # lastdiff = 0 + &clflush(&DWP(0,$out)); + &data_byte(0xf0); # lock + &add (&DWP(0,$out),$lastdiff); + &jmp (&label("loop")); + +&set_label("loop",16); + &rdtsc (); + &mov ("edx","eax"); # put aside tick (yes, I neglect edx) + &sub ("eax",$lasttick); # diff + &mov ($lasttick,"edx"); # lasttick = tick + &mov ($lastdiff,"eax"); # lastdiff = diff + &clflush(&DWP(0,$out)); + &data_byte(0xf0); # lock + &add (&DWP(0,$out),"eax"); # accumulate diff + &lea ($out,&DWP(4,$out)); # ++$out + &sub ($cnt,1); # --$cnt + &jnz (&label("loop")); + + &mov ("eax",&wparam(1)); +&set_label("nogo"); + } +&function_end("OPENSSL_instrument_bus"); + +&function_begin("OPENSSL_instrument_bus2"); + &mov ("eax",0); + if ($sse2) { + &picmeup("edx","OPENSSL_ia32cap_P"); + &bt (&DWP(0,"edx"),4); + &jnc (&label("nogo")); # no TSC + &bt (&DWP(0,"edx"),19); + &jnc (&label("nogo")); # no CLFLUSH + + &mov ($out,&wparam(0)); # load arguments + &mov ($cnt,&wparam(1)); + &mov ($max,&wparam(2)); + + &rdtsc (); # collect 1st tick + &mov ($lasttick,"eax"); # lasttick = tick + &mov ($lastdiff,0); # lastdiff = 0 + + &clflush(&DWP(0,$out)); + &data_byte(0xf0); # lock + &add (&DWP(0,$out),$lastdiff); + + &rdtsc (); # collect 1st diff + &mov ("edx","eax"); # put aside tick (yes, I neglect edx) + &sub ("eax",$lasttick); # diff + &mov ($lasttick,"edx"); # lasttick = tick + &mov ($lastdiff,"eax"); # lastdiff = diff + &jmp (&label("loop2")); + +&set_label("loop2",16); + &clflush(&DWP(0,$out)); + &data_byte(0xf0); # lock + &add (&DWP(0,$out),"eax"); # accumulate diff + + &sub ($max,1); + &jz (&label("done2")); + + &rdtsc (); + &mov ("edx","eax"); # put aside tick (yes, I neglect edx) + &sub ("eax",$lasttick); # diff + &mov ($lasttick,"edx"); # lasttick = tick + &cmp ("eax",$lastdiff); + &mov ($lastdiff,"eax"); # lastdiff = diff + &mov ("edx",0); + &setne ("dl"); + &sub ($cnt,"edx"); # conditional --$cnt + &lea ($out,&DWP(0,$out,"edx",4)); # conditional ++$out + &jnz (&label("loop2")); + +&set_label("done2"); + &mov ("eax",&wparam(1)); + &sub ("eax",$cnt); +&set_label("nogo"); + } +&function_end("OPENSSL_instrument_bus2"); +} + +sub gen_random { +my $rdop = shift; +&function_begin_B("OPENSSL_ia32_${rdop}"); + &mov ("ecx",8); +&set_label("loop"); + &${rdop}("eax"); + &jc (&label("break")); + &loop (&label("loop")); +&set_label("break"); + &cmp ("eax",0); + &cmove ("eax","ecx"); + &ret (); +&function_end_B("OPENSSL_ia32_${rdop}"); + +&function_begin_B("OPENSSL_ia32_${rdop}_bytes"); + &push ("edi"); + &push ("ebx"); + &xor ("eax","eax"); # return value + &mov ("edi",&wparam(0)); + &mov ("ebx",&wparam(1)); + + &cmp ("ebx",0); + &je (&label("done")); + + &mov ("ecx",8); +&set_label("loop"); + &${rdop}("edx"); + &jc (&label("break")); + &loop (&label("loop")); + &jmp (&label("done")); + +&set_label("break",16); + &cmp ("ebx",4); + &jb (&label("tail")); + &mov (&DWP(0,"edi"),"edx"); + &lea ("edi",&DWP(4,"edi")); + &add ("eax",4); + &sub ("ebx",4); + &jz (&label("done")); + &mov ("ecx",8); + &jmp (&label("loop")); + +&set_label("tail",16); + &mov (&BP(0,"edi"),"dl"); + &lea ("edi",&DWP(1,"edi")); + &inc ("eax"); + &shr ("edx",8); + &dec ("ebx"); + &jnz (&label("tail")); + +&set_label("done"); + &pop ("ebx"); + &pop ("edi"); + &ret (); +&function_end_B("OPENSSL_ia32_${rdop}_bytes"); +} +&gen_random("rdrand"); +&gen_random("rdseed"); + +&initseg("OPENSSL_cpuid_setup"); + +&hidden("OPENSSL_cpuid_setup"); +&hidden("OPENSSL_ia32cap_P"); + +&asm_finish(); + +close STDOUT; diff --git a/crypto/zuc/build.info b/crypto/zuc/build.info new file mode 100644 index 000000000..70472c015 --- /dev/null +++ b/crypto/zuc/build.info @@ -0,0 +1,4 @@ +LIBS=../../libcrypto +SOURCE[../../libcrypto]=zuc_core.c zuc_eea.c zuc_eia.c +INCLUDE[zuc_core.o]=../modes +INCLUDE[zuc_eia.o]=../modes diff --git a/crypto/zuc/zuc_core.c b/crypto/zuc/zuc_core.c new file mode 100644 index 000000000..084d1e599 --- /dev/null +++ b/crypto/zuc/zuc_core.c @@ -0,0 +1,602 @@ +/* ==================================================================== + * Copyright (c) 2015 - 2019 The GmSSL Project. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the GmSSL Project. + * (http://gmssl.org/)" + * + * 4. The name "GmSSL Project" must not be used to endorse or promote + * products derived from this software without prior written + * permission. For written permission, please contact + * guanzhi1980@gmail.com. + * + * 5. Products derived from this software may not be called "GmSSL" + * nor may "GmSSL" appear in their names without prior written + * permission of the GmSSL Project. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the GmSSL Project + * (http://gmssl.org/)" + * + * THIS SOFTWARE IS PROVIDED BY THE GmSSL PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE GmSSL PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + */ + +#include +#include +#include +#include "modes_lcl.h" + +static const ZUC_UINT15 KD[16] = { + 0x44D7,0x26BC,0x626B,0x135E,0x5789,0x35E2,0x7135,0x09AF, + 0x4D78,0x2F13,0x6BC4,0x1AF1,0x5E26,0x3C4D,0x789A,0x47AC, +}; + +static const uint8_t S0[256] = { + 0x3e,0x72,0x5b,0x47,0xca,0xe0,0x00,0x33,0x04,0xd1,0x54,0x98,0x09,0xb9,0x6d,0xcb, + 0x7b,0x1b,0xf9,0x32,0xaf,0x9d,0x6a,0xa5,0xb8,0x2d,0xfc,0x1d,0x08,0x53,0x03,0x90, + 0x4d,0x4e,0x84,0x99,0xe4,0xce,0xd9,0x91,0xdd,0xb6,0x85,0x48,0x8b,0x29,0x6e,0xac, + 0xcd,0xc1,0xf8,0x1e,0x73,0x43,0x69,0xc6,0xb5,0xbd,0xfd,0x39,0x63,0x20,0xd4,0x38, + 0x76,0x7d,0xb2,0xa7,0xcf,0xed,0x57,0xc5,0xf3,0x2c,0xbb,0x14,0x21,0x06,0x55,0x9b, + 0xe3,0xef,0x5e,0x31,0x4f,0x7f,0x5a,0xa4,0x0d,0x82,0x51,0x49,0x5f,0xba,0x58,0x1c, + 0x4a,0x16,0xd5,0x17,0xa8,0x92,0x24,0x1f,0x8c,0xff,0xd8,0xae,0x2e,0x01,0xd3,0xad, + 0x3b,0x4b,0xda,0x46,0xeb,0xc9,0xde,0x9a,0x8f,0x87,0xd7,0x3a,0x80,0x6f,0x2f,0xc8, + 0xb1,0xb4,0x37,0xf7,0x0a,0x22,0x13,0x28,0x7c,0xcc,0x3c,0x89,0xc7,0xc3,0x96,0x56, + 0x07,0xbf,0x7e,0xf0,0x0b,0x2b,0x97,0x52,0x35,0x41,0x79,0x61,0xa6,0x4c,0x10,0xfe, + 0xbc,0x26,0x95,0x88,0x8a,0xb0,0xa3,0xfb,0xc0,0x18,0x94,0xf2,0xe1,0xe5,0xe9,0x5d, + 0xd0,0xdc,0x11,0x66,0x64,0x5c,0xec,0x59,0x42,0x75,0x12,0xf5,0x74,0x9c,0xaa,0x23, + 0x0e,0x86,0xab,0xbe,0x2a,0x02,0xe7,0x67,0xe6,0x44,0xa2,0x6c,0xc2,0x93,0x9f,0xf1, + 0xf6,0xfa,0x36,0xd2,0x50,0x68,0x9e,0x62,0x71,0x15,0x3d,0xd6,0x40,0xc4,0xe2,0x0f, + 0x8e,0x83,0x77,0x6b,0x25,0x05,0x3f,0x0c,0x30,0xea,0x70,0xb7,0xa1,0xe8,0xa9,0x65, + 0x8d,0x27,0x1a,0xdb,0x81,0xb3,0xa0,0xf4,0x45,0x7a,0x19,0xdf,0xee,0x78,0x34,0x60, +}; + +static const uint8_t S1[256] = { + 0x55,0xc2,0x63,0x71,0x3b,0xc8,0x47,0x86,0x9f,0x3c,0xda,0x5b,0x29,0xaa,0xfd,0x77, + 0x8c,0xc5,0x94,0x0c,0xa6,0x1a,0x13,0x00,0xe3,0xa8,0x16,0x72,0x40,0xf9,0xf8,0x42, + 0x44,0x26,0x68,0x96,0x81,0xd9,0x45,0x3e,0x10,0x76,0xc6,0xa7,0x8b,0x39,0x43,0xe1, + 0x3a,0xb5,0x56,0x2a,0xc0,0x6d,0xb3,0x05,0x22,0x66,0xbf,0xdc,0x0b,0xfa,0x62,0x48, + 0xdd,0x20,0x11,0x06,0x36,0xc9,0xc1,0xcf,0xf6,0x27,0x52,0xbb,0x69,0xf5,0xd4,0x87, + 0x7f,0x84,0x4c,0xd2,0x9c,0x57,0xa4,0xbc,0x4f,0x9a,0xdf,0xfe,0xd6,0x8d,0x7a,0xeb, + 0x2b,0x53,0xd8,0x5c,0xa1,0x14,0x17,0xfb,0x23,0xd5,0x7d,0x30,0x67,0x73,0x08,0x09, + 0xee,0xb7,0x70,0x3f,0x61,0xb2,0x19,0x8e,0x4e,0xe5,0x4b,0x93,0x8f,0x5d,0xdb,0xa9, + 0xad,0xf1,0xae,0x2e,0xcb,0x0d,0xfc,0xf4,0x2d,0x46,0x6e,0x1d,0x97,0xe8,0xd1,0xe9, + 0x4d,0x37,0xa5,0x75,0x5e,0x83,0x9e,0xab,0x82,0x9d,0xb9,0x1c,0xe0,0xcd,0x49,0x89, + 0x01,0xb6,0xbd,0x58,0x24,0xa2,0x5f,0x38,0x78,0x99,0x15,0x90,0x50,0xb8,0x95,0xe4, + 0xd0,0x91,0xc7,0xce,0xed,0x0f,0xb4,0x6f,0xa0,0xcc,0xf0,0x02,0x4a,0x79,0xc3,0xde, + 0xa3,0xef,0xea,0x51,0xe6,0x6b,0x18,0xec,0x1b,0x2c,0x80,0xf7,0x74,0xe7,0xff,0x21, + 0x5a,0x6a,0x54,0x1e,0x41,0x31,0x92,0x35,0xc4,0x33,0x07,0x0a,0xba,0x7e,0x0e,0x34, + 0x88,0xb1,0x98,0x7c,0xf3,0x3d,0x60,0x6c,0x7b,0xca,0xd3,0x1f,0x32,0x65,0x04,0x28, + 0x64,0xbe,0x85,0x9b,0x2f,0x59,0x8a,0xd7,0xb0,0x25,0xac,0xaf,0x12,0x03,0xe2,0xf2, +}; + + +#define ADD31(a,b) a += (b); a = (a & 0x7fffffff) + (a >> 31) +#define ROT31(a,k) ((((a) << (k)) | ((a) >> (31 - (k)))) & 0x7FFFFFFF) +#define ROT32(a,k) (((a) << (k)) | ((a) >> (32 - (k)))) + +#define L1(X) \ + ((X) ^ \ + ROT32((X), 2) ^ \ + ROT32((X), 10) ^ \ + ROT32((X), 18) ^ \ + ROT32((X), 24)) + +#define L2(X) \ + ((X) ^ \ + ROT32((X), 8) ^ \ + ROT32((X), 14) ^ \ + ROT32((X), 22) ^ \ + ROT32((X), 30)) + +#define LFSRWithInitialisationMode(u) \ + V = LFSR[0]; \ + ADD31(V, ROT31(LFSR[0], 8)); \ + ADD31(V, ROT31(LFSR[4], 20)); \ + ADD31(V, ROT31(LFSR[10], 21)); \ + ADD31(V, ROT31(LFSR[13], 17)); \ + ADD31(V, ROT31(LFSR[15], 15)); \ + ADD31(V, (u)); \ + {int j; for (j=0; j<15;j++) LFSR[j]=LFSR[j+1];} \ + LFSR[15] = V + +#define LFSRWithWorkMode() \ + { \ + int j; \ + uint64_t a = LFSR[0]; \ + a += ((uint64_t)LFSR[0]) << 8; \ + a += ((uint64_t)LFSR[4]) << 20; \ + a += ((uint64_t)LFSR[10]) << 21; \ + a += ((uint64_t)LFSR[13]) << 17; \ + a += ((uint64_t)LFSR[15]) << 15; \ + a = (a & 0x7fffffff) + (a >> 31); \ + V = (a & 0x7fffffff) + (a >> 31); \ + for (j = 0; j < 15; j++) \ + LFSR[j] = LFSR[j+1]; \ + LFSR[15] = V; \ + } + +#define BitReconstruction2(X1,X2) \ + X1 = ((LFSR[11] & 0xFFFF) << 16) | (LFSR[9] >> 15); \ + X2 = ((LFSR[7] & 0xFFFF) << 16) | (LFSR[5] >> 15) + +#define BitReconstruction3(X0,X1,X2) \ + X0 = ((LFSR[15] & 0x7FFF8000) << 1) | (LFSR[14] & 0xFFFF); \ + BitReconstruction2(X1,X2) + +#define BitReconstruction4(X0,X1,X2,X3) \ + BitReconstruction3(X0,X1,X2); \ + X3 = ((LFSR[2] & 0xFFFF) << 16) | (LFSR[0] >> 15) + + +#define MAKEU31(k,d,iv) \ + (((uint32_t)(k) << 23) | \ + ((uint32_t)(d) << 8) | \ + (uint32_t)(iv)) + +#define MAKEU32(a, b, c, d) \ + (((uint32_t)(a) << 24) | \ + ((uint32_t)(b) << 16) | \ + ((uint32_t)(c) << 8) | \ + ((uint32_t)(d))) + +#define F_(X1,X2) \ + W1 = R1 + X1; \ + W2 = R2 ^ X2; \ + U = L1((W1 << 16) | (W2 >> 16)); \ + V = L2((W2 << 16) | (W1 >> 16)); \ + R1 = MAKEU32( S0[U >> 24], \ + S1[(U >> 16) & 0xFF], \ + S0[(U >> 8) & 0xFF], \ + S1[U & 0xFF]); \ + R2 = MAKEU32( S0[V >> 24], \ + S1[(V >> 16) & 0xFF], \ + S0[(V >> 8) & 0xFF], \ + S1[V & 0xFF]) + +#define F(X0,X1,X2) \ + (X0 ^ R1) + R2; \ + F_(X1, X2) + +void ZUC_set_key(ZUC_KEY *key, const unsigned char *user_key, const unsigned char *iv) +{ + ZUC_UINT31 *LFSR = key->LFSR; + uint32_t R1, R2; + uint32_t X0, X1, X2; + uint32_t W, W1, W2, U, V; + int i; + + for (i = 0; i < 16; i++) { + LFSR[i] = MAKEU31(user_key[i], KD[i], iv[i]); + } + + R1 = 0; + R2 = 0; + + for (i = 0; i < 32; i++) { + BitReconstruction3(X0, X1, X2); + W = F(X0, X1, X2); + LFSRWithInitialisationMode(W >> 1); + } + + BitReconstruction2(X1, X2); + F_(X1, X2); + LFSRWithWorkMode(); + + key->R1 = R1; + key->R2 = R2; +} + +uint32_t ZUC_generate_keyword(ZUC_KEY *key) +{ + ZUC_UINT31 *LFSR = key->LFSR; + uint32_t R1 = key->R1; + uint32_t R2 = key->R2; + uint32_t X0, X1, X2, X3; + uint32_t W1, W2, U, V; + uint32_t Z; + + BitReconstruction4(X0, X1, X2, X3); + Z = X3 ^ F(X0, X1, X2); + LFSRWithWorkMode(); + + key->R1 = R1; + key->R2 = R2; + + return Z; +} + +void ZUC_generate_keystream(ZUC_KEY *key, size_t nwords, uint32_t *keystream) +{ + ZUC_UINT31 *LFSR = key->LFSR; + uint32_t R1 = key->R1; + uint32_t R2 = key->R2; + uint32_t X0, X1, X2, X3; + uint32_t W1, W2, U, V; + size_t i; + + for (i = 0; i < nwords; i ++) { + BitReconstruction4(X0, X1, X2, X3); + keystream[i] = X3 ^ F(X0, X1, X2); + LFSRWithWorkMode(); + } + + key->R1 = R1; + key->R2 = R2; +} + +void ZUC_MAC_init(ZUC_MAC_CTX *ctx, const unsigned char key[16], const unsigned char iv[16]) +{ + memset(ctx, 0, sizeof(*ctx)); + ZUC_set_key((ZUC_KEY *)ctx, key, iv); + ctx->K0 = ZUC_generate_keyword((ZUC_KEY *)ctx); +} + +void ZUC_MAC_update(ZUC_MAC_CTX *ctx, const unsigned char *data, size_t len) +{ + ZUC_UINT32 T = ctx->T; + ZUC_UINT32 K0 = ctx->K0; + ZUC_UINT32 K1, M; + ZUC_UINT31 *LFSR = ctx->LFSR; + ZUC_UINT32 R1 = ctx->R1; + ZUC_UINT32 R2 = ctx->R2; + ZUC_UINT32 X0, X1, X2, X3; + ZUC_UINT32 W1, W2, U, V; + size_t i; + + if (!data || !len) { + return; + } + + if (ctx->buflen) { + size_t num = sizeof(ctx->buf) - ctx->buflen; + if (len < num) { + memcpy(ctx->buf + ctx->buflen, data, len); + ctx->buflen += len; + return; + } + + memcpy(ctx->buf + ctx->buflen, data, num); + M = GETU32(ctx->buf); + ctx->buflen = 0; + + BitReconstruction4(X0, X1, X2, X3); + K1 = X3 ^ F(X0, X1, X2); + LFSRWithWorkMode(); + + for (i = 0; i < 32; i++) { + if (M & 0x80000000) { + T ^= K0; + } + M <<= 1; + K0 = (K0 << 1) | (K1 >> 31); + K1 <<= 1; + } + + data += num; + len -= num; + } + + while (len >= 4) { + M = GETU32(data); + + BitReconstruction4(X0, X1, X2, X3); + K1 = X3 ^ F(X0, X1, X2); + LFSRWithWorkMode(); + + for (i = 0; i < 32; i++) { + if (M & 0x80000000) { + T ^= K0; + } + M <<= 1; + K0 = (K0 << 1) | (K1 >> 31); + K1 <<= 1; + } + + data += 4; + len -= 4; + } + + if (len) { + memcpy(ctx->buf, data, len); + ctx->buflen = len; + } + ctx->R1 = R1; + ctx->R2 = R2; + ctx->K0 = K0; + ctx->T = T; +} + +void ZUC_MAC_final(ZUC_MAC_CTX *ctx, const unsigned char *data, size_t nbits, unsigned char mac[4]) +{ + ZUC_UINT32 T = ctx->T; + ZUC_UINT32 K0 = ctx->K0; + ZUC_UINT32 K1, M; + ZUC_UINT31 *LFSR = ctx->LFSR; + ZUC_UINT32 R1 = ctx->R1; + ZUC_UINT32 R2 = ctx->R2; + ZUC_UINT32 X0, X1, X2, X3; + ZUC_UINT32 W1, W2, U, V; + size_t i; + + + if (!data) + nbits = 0; + + if (nbits >= 8) { + ZUC_MAC_update(ctx, data, nbits/8); + data += nbits/8; + nbits %= 8; + } + + T = ctx->T; + K0 = ctx->K0; + LFSR = ctx->LFSR; + R1 = ctx->R1; + R2 = ctx->R2; + + + if (nbits) + ctx->buf[ctx->buflen] = *data; + + if (ctx->buflen || nbits) { + M = GETU32(ctx->buf); + BitReconstruction4(X0, X1, X2, X3); + K1 = X3 ^ F(X0, X1, X2); + LFSRWithWorkMode(); + + for (i = 0; i < ctx->buflen * 8 + nbits; i++) { + if (M & 0x80000000) { + T ^= K0; + } + M <<= 1; + K0 = (K0 << 1) | (K1 >> 31); + K1 <<= 1; + } + } + + T ^= K0; + + BitReconstruction4(X0, X1, X2, X3); + K1 = X3 ^ F(X0, X1, X2); + LFSRWithWorkMode(); + T ^= K1; + + ctx->T = T; + PUTU32(mac, T); +} + +typedef unsigned char ZUC_UINT7; + +static const ZUC_UINT7 ZUC256_D[][16] = { + {0x22,0x2F,0x24,0x2A,0x6D,0x40,0x40,0x40, + 0x40,0x40,0x40,0x40,0x40,0x52,0x10,0x30}, + {0x22,0x2F,0x25,0x2A,0x6D,0x40,0x40,0x40, + 0x40,0x40,0x40,0x40,0x40,0x52,0x10,0x30}, + {0x23,0x2F,0x24,0x2A,0x6D,0x40,0x40,0x40, + 0x40,0x40,0x40,0x40,0x40,0x52,0x10,0x30}, + {0x23,0x2F,0x25,0x2A,0x6D,0x40,0x40,0x40, + 0x40,0x40,0x40,0x40,0x40,0x52,0x10,0x30}, +}; + +#define ZUC256_MAKEU31(a,b,c,d) \ + (((uint32_t)(a) << 23) | \ + ((uint32_t)(b) << 16) | \ + ((uint32_t)(c) << 8) | \ + (uint32_t)(d)) + + +static void zuc256_set_mac_key(ZUC_KEY *key, const unsigned char K[32], + const unsigned char IV[23], int macbits) +{ + ZUC_UINT31 *LFSR = key->LFSR; + uint32_t R1, R2; + uint32_t X0, X1, X2; + uint32_t W, W1, W2, U, V; + const ZUC_UINT7 *D; + int i; + + ZUC_UINT6 IV17 = IV[17] >> 2; + ZUC_UINT6 IV18 = ((IV[17] & 0x3) << 4) | (IV[18] >> 4); + ZUC_UINT6 IV19 = ((IV[18] & 0xf) << 2) | (IV[19] >> 6); + ZUC_UINT6 IV20 = IV[19] & 0x3f; + ZUC_UINT6 IV21 = IV[20] >> 2; + ZUC_UINT6 IV22 = ((IV[20] & 0x3) << 4) | (IV[21] >> 4); + ZUC_UINT6 IV23 = ((IV[21] & 0xf) << 2) | (IV[22] >> 6); + ZUC_UINT6 IV24 = IV[22] & 0x3f; + + D = macbits/32 < 3 ? ZUC256_D[macbits/32] : ZUC256_D[3]; + LFSR[0] = ZUC256_MAKEU31(K[0], D[0], K[21], K[16]); + LFSR[1] = ZUC256_MAKEU31(K[1], D[1], K[22], K[17]); + LFSR[2] = ZUC256_MAKEU31(K[2], D[2], K[23], K[18]); + LFSR[3] = ZUC256_MAKEU31(K[3], D[3], K[24], K[19]); + LFSR[4] = ZUC256_MAKEU31(K[4], D[4], K[25], K[20]); + LFSR[5] = ZUC256_MAKEU31(IV[0], (D[5] | IV17), K[5], K[26]); + LFSR[6] = ZUC256_MAKEU31(IV[1], (D[6] | IV18), K[6], K[27]); + LFSR[7] = ZUC256_MAKEU31(IV[10], (D[7] | IV19), K[7], IV[2]); + LFSR[8] = ZUC256_MAKEU31(K[8], (D[8] | IV20), IV[3], IV[11]); + LFSR[9] = ZUC256_MAKEU31(K[9], (D[9] | IV21), IV[12], IV[4]); + LFSR[10] = ZUC256_MAKEU31(IV[5], (D[10] | IV22), K[10], K[28]); + LFSR[11] = ZUC256_MAKEU31(K[11], (D[11] | IV23), IV[6], IV[13]); + LFSR[12] = ZUC256_MAKEU31(K[12], (D[12] | IV24), IV[7], IV[14]); + LFSR[13] = ZUC256_MAKEU31(K[13], D[13], IV[15], IV[8]); + LFSR[14] = ZUC256_MAKEU31(K[14], (D[14] | (K[31] >> 4)), IV[16], IV[9]); + LFSR[15] = ZUC256_MAKEU31(K[15], (D[15] | (K[31] & 0x0F)), K[30], K[29]); + + R1 = 0; + R2 = 0; + + for (i = 0; i < 32; i++) { + BitReconstruction3(X0, X1, X2); + W = F(X0, X1, X2); + LFSRWithInitialisationMode(W >> 1); + } + + BitReconstruction2(X1, X2); + F_(X1, X2); + LFSRWithWorkMode(); + + key->R1 = R1; + key->R2 = R2; +} + +void ZUC256_set_key(ZUC_KEY *key, const unsigned char K[32], + const unsigned char IV[23]) +{ + zuc256_set_mac_key(key, K, IV, 0); +} + +void ZUC256_MAC_init(ZUC256_MAC_CTX *ctx, const unsigned char key[32], + const unsigned char iv[23], int macbits) +{ + if (macbits < 32) + macbits = 32; + else if (macbits > 64) + macbits = 128; + memset(ctx, 0, sizeof(*ctx)); + zuc256_set_mac_key((ZUC256_KEY *)ctx, key, iv, macbits); + ZUC256_generate_keystream((ZUC256_KEY *)ctx, macbits/32, ctx->T); + ZUC256_generate_keystream((ZUC256_KEY *)ctx, macbits/32, ctx->K0); + ctx->macbits = (macbits/32) * 32; +} + +void ZUC256_MAC_update(ZUC256_MAC_CTX *ctx, const unsigned char *data, size_t len) +{ + ZUC_UINT32 K1, M; + size_t n = ctx->macbits / 32; + size_t i, j; + + if (!data || !len) { + return; + } + + if (ctx->buflen) { + size_t num = sizeof(ctx->buf) - ctx->buflen; + if (len < num) { + memcpy(ctx->buf + ctx->buflen, data, len); + ctx->buflen += len; + return; + } + + memcpy(ctx->buf + ctx->buflen, data, num); + M = GETU32(ctx->buf); + ctx->buflen = 0; + + K1 = ZUC256_generate_keyword((ZUC256_KEY *)ctx); + + for (i = 0; i < 32; i++) { + if (M & 0x80000000) { + for (j = 0; j < n; j++) { + ctx->T[j] ^= ctx->K0[j]; + } + } + M <<= 1; + for (j = 0; j < n - 1; j++) { + ctx->K0[j] = (ctx->K0[j] << 1) | (ctx->K0[j + 1] >> 31); + } + ctx->K0[j] = (ctx->K0[j] << 1) | (K1 >> 31); + K1 <<= 1; + } + + data += num; + len -= num; + } + + while (len >= 4) { + M = GETU32(data); + K1 = ZUC256_generate_keyword((ZUC256_KEY *)ctx); + + for (i = 0; i < 32; i++) { + if (M & 0x80000000) { + for (j = 0; j < n; j++) { + ctx->T[j] ^= ctx->K0[j]; + } + } + M <<= 1; + for (j = 0; j < n - 1; j++) { + ctx->K0[j] = (ctx->K0[j] << 1) | (ctx->K0[j + 1] >> 31); + } + ctx->K0[j] = (ctx->K0[j] << 1) | (K1 >> 31); + K1 <<= 1; + } + + data += 4; + len -= 4; + } + + if (len) { + memcpy(ctx->buf, data, len); + ctx->buflen = len; + } +} + +void ZUC256_MAC_final(ZUC256_MAC_CTX *ctx, const unsigned char *data, size_t nbits, unsigned char *mac) +{ + ZUC_UINT32 K1, M; + size_t n = ctx->macbits/32; + size_t i, j; + + + if (!data) + nbits = 0; + + if (nbits >= 8) { + ZUC256_MAC_update(ctx, data, nbits/8); + data += nbits/8; + nbits %= 8; + } + + if (nbits) + ctx->buf[ctx->buflen] = *data; + + if (ctx->buflen || nbits) { + M = GETU32(ctx->buf); + K1 = ZUC256_generate_keyword((ZUC256_KEY *)ctx); + + + for (i = 0; i < ctx->buflen * 8 + nbits; i++) { + if (M & 0x80000000) { + for (j = 0; j < n; j++) { + ctx->T[j] ^= ctx->K0[j]; + } + } + M <<= 1; + for (j = 0; j < n - 1; j++) { + ctx->K0[j] = (ctx->K0[j] << 1) | (ctx->K0[j + 1] >> 31); + } + ctx->K0[j] = (ctx->K0[j] << 1) | (K1 >> 31); + K1 <<= 1; + } + } + + for (j = 0; j < n; j++) { + ctx->T[j] ^= ctx->K0[j]; + PUTU32(mac, ctx->T[j]); + mac += 4; + } +} diff --git a/crypto/zuc/zuc_eea.c b/crypto/zuc/zuc_eea.c new file mode 100644 index 000000000..ccd71ee81 --- /dev/null +++ b/crypto/zuc/zuc_eea.c @@ -0,0 +1,82 @@ +/* ==================================================================== + * Copyright (c) 2015 - 2019 The GmSSL Project. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the GmSSL Project. + * (http://gmssl.org/)" + * + * 4. The name "GmSSL Project" must not be used to endorse or promote + * products derived from this software without prior written + * permission. For written permission, please contact + * guanzhi1980@gmail.com. + * + * 5. Products derived from this software may not be called "GmSSL" + * nor may "GmSSL" appear in their names without prior written + * permission of the GmSSL Project. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the GmSSL Project + * (http://gmssl.org/)" + * + * THIS SOFTWARE IS PROVIDED BY THE GmSSL PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE GmSSL PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + */ + +#include +#include + +static void ZUC_set_eea_key(ZUC_KEY *key, const unsigned char user_key[16], + ZUC_UINT32 count, ZUC_UINT5 bearer, ZUC_BIT direction) +{ + unsigned char iv[16] = {0}; + iv[0] = iv[8] = count >> 24; + iv[1] = iv[9] = count >> 16; + iv[2] = iv[10] = count >> 8; + iv[3] = iv[11] = count; + iv[4] = iv[12] = ((bearer << 1) | (direction & 1)) << 2; + ZUC_set_key(key, user_key, iv); +} + +void ZUC_eea_encrypt(const ZUC_UINT32 *in, ZUC_UINT32 *out, size_t nbits, + const unsigned char key[16], ZUC_UINT32 count, ZUC_UINT5 bearer, + ZUC_BIT direction) +{ + ZUC_KEY zuc_key; + size_t nwords = (nbits + 31)/32; + size_t i; + + ZUC_set_eea_key(&zuc_key, key, count, bearer, direction); + ZUC_generate_keystream(&zuc_key, nwords, out); + for (i = 0; i < nwords; i++) { + out[i] ^= in[i]; + } + + if (nbits % 32 != 0) { + out[nwords - 1] |= (0xffffffff << (32 - (nbits%32))); + } +} diff --git a/crypto/zuc/zuc_eia.c b/crypto/zuc/zuc_eia.c new file mode 100644 index 000000000..a3645c533 --- /dev/null +++ b/crypto/zuc/zuc_eia.c @@ -0,0 +1,160 @@ +/* ==================================================================== + * Copyright (c) 2015 - 2019 The GmSSL Project. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the GmSSL Project. + * (http://gmssl.org/)" + * + * 4. The name "GmSSL Project" must not be used to endorse or promote + * products derived from this software without prior written + * permission. For written permission, please contact + * guanzhi1980@gmail.com. + * + * 5. Products derived from this software may not be called "GmSSL" + * nor may "GmSSL" appear in their names without prior written + * permission of the GmSSL Project. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the GmSSL Project + * (http://gmssl.org/)" + * + * THIS SOFTWARE IS PROVIDED BY THE GmSSL PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE GmSSL PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + */ + +#include +#include +#include +#include +#include "modes_lcl.h" + +static void zuc_set_eia_iv(unsigned char iv[16], ZUC_UINT32 count, ZUC_UINT5 bearer, + ZUC_BIT direction) +{ + memset(iv, 0, 16); + iv[0] = count >> 24; + iv[1] = iv[9] = count >> 16; + iv[2] = iv[10] = count >> 8; + iv[3] = iv[11] = count; + iv[4] = iv[12] = bearer << 3; + iv[8] = iv[0] ^ (direction << 7); + iv[14] = (direction << 7); +} + +#if 1 +ZUC_UINT32 ZUC_eia_generate_mac(const ZUC_UINT32 *data, size_t nbits, + const unsigned char key[16], ZUC_UINT32 count, ZUC_UINT5 bearer, + ZUC_BIT direction) +{ + ZUC_MAC_CTX ctx; + unsigned char iv[16]; + unsigned char mac[4]; + zuc_set_eia_iv(iv, count, bearer, direction); + ZUC_MAC_init(&ctx, key, iv); + ZUC_MAC_final(&ctx, (unsigned char *)data, nbits, mac); + return GETU32(mac); +} +#else + +#define ZUC_MAC_BUF_WORDS 64 + +#define GET_WORD(p, i) ((i) % 32) \ + ? ((*((ZUC_UINT32 *)(p) + (i)/32) << ((i) % 32)) \ + | (*((ZUC_UINT32 *)(p) + (i)/32 + 1) >> (32 - ((i) % 32)))) \ + : *((ZUC_UINT32 *)(p) + (i)/32) + +#define GET_BIT(p, i) \ + (((*((ZUC_UINT32 *)(p) + (i)/32)) & (1 << (31 - ((i) % 32)))) ? 1 : 0) + +ZUC_UINT32 ZUC_eia_generate_mac(const ZUC_UINT32 *data, size_t nbits, + const unsigned char user_key[16], ZUC_UINT32 count, ZUC_UINT5 bearer, + ZUC_BIT direction) +{ + ZUC_UINT32 T = 0; + ZUC_KEY key; + unsigned char iv[16]; + ZUC_UINT32 buf[ZUC_MAC_BUF_WORDS + 2]; + size_t nwords = (nbits + 31)/32; + size_t i; + size_t num = ZUC_MAC_BUF_WORDS; + + + ZUC_set_eia_iv(iv, count, bearer, direction); + ZUC_set_key(&key, user_key, iv); + + if (nwords <= ZUC_MAC_BUF_WORDS) { + ZUC_generate_keystream(&key, nwords + 2, buf); + for (i = 0; i < nbits; i++) { + if (GET_BIT(data, i)) { + T ^= GET_WORD(buf, i); + } + } + T ^= GET_WORD(buf, i); + T ^= buf[nwords + 1]; + return T; + + } else { + + ZUC_generate_keystream(&key, ZUC_MAC_BUF_WORDS + 1, buf); + for (i = 0; i < ZUC_MAC_BUF_WORDS * 32; i++) { + if (GET_BIT(data, i)) { + T ^= GET_WORD(buf, i); + } + } + data += ZUC_MAC_BUF_WORDS; + nwords -= ZUC_MAC_BUF_WORDS; + nbits -= ZUC_MAC_BUF_WORDS * 32; + } + + while (nwords > ZUC_MAC_BUF_WORDS) { + buf[0] = buf[ZUC_MAC_BUF_WORDS]; + ZUC_generate_keystream(&key, ZUC_MAC_BUF_WORDS, buf + 1); + for (i = 0; i < ZUC_MAC_BUF_WORDS * 32; i ++) { + if (GET_BIT(data, i)) { + T ^= GET_WORD(buf, i); + } + } + data += num; + nwords -= num; + nbits -= ZUC_MAC_BUF_WORDS * 32; + } + + buf[0] = buf[ZUC_MAC_BUF_WORDS]; + ZUC_generate_keystream(&key, nwords + 1, buf + 1); + for (i = 0; i < nbits; i++) { + if (GET_BIT(data, i)) { + T ^= GET_WORD(buf, i); + } + } + + T ^= GET_WORD(buf, i); + T ^= buf[nwords + 1]; + + return T; +} +#endif diff --git a/demos/README b/demos/README new file mode 100644 index 000000000..724227990 --- /dev/null +++ b/demos/README @@ -0,0 +1,3 @@ +# GmSSL Demos + + diff --git a/demos/bio/Makefile b/demos/bio/Makefile new file mode 100644 index 000000000..493e8a58a --- /dev/null +++ b/demos/bio/Makefile @@ -0,0 +1,30 @@ +# Quick instruction: +# To build against an OpenSSL built in the source tree, do this: +# +# make OPENSSL_INCS_LOCATION=-I../../include OPENSSL_LIBS_LOCATION=-L../.. +# +# To run the demos when linked with a shared library (default): +# +# LD_LIBRARY_PATH=../.. ./server-arg +# LD_LIBRARY_PATH=../.. ./server-cmod +# LD_LIBRARY_PATH=../.. ./server-conf +# LD_LIBRARY_PATH=../.. ./client-arg +# LD_LIBRARY_PATH=../.. ./client-conf +# LD_LIBRARY_PATH=../.. ./saccept +# LD_LIBRARY_PATH=../.. ./sconnect + +CFLAGS = $(OPENSSL_INCS_LOCATION) +LDFLAGS = $(OPENSSL_LIBS_LOCATION) -lssl -lcrypto $(EX_LIBS) + +all: client-arg client-conf saccept sconnect server-arg server-cmod server-conf + +client-arg: client-arg.o +client-conf: client-conf.o +saccept: saccept.o +sconnect: sconnect.o +server-arg: server-arg.o +server-cmod: server-cmod.o +server-conf: server-conf.o + +client-arg client-conf saccept sconnect server-arg server-cmod server-conf: + $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< diff --git a/demos/bio/README b/demos/bio/README new file mode 100644 index 000000000..a36bb48a5 --- /dev/null +++ b/demos/bio/README @@ -0,0 +1,7 @@ +This directory contains some simple examples of the use of BIO's +to simplify socket programming. + +The client-conf, server-conf, client-arg and client-conf include examples +of how to use the SSL_CONF API for configuration file or command line +processing. + diff --git a/demos/bio/accept.cnf b/demos/bio/accept.cnf new file mode 100644 index 000000000..eb6965832 --- /dev/null +++ b/demos/bio/accept.cnf @@ -0,0 +1,17 @@ +# Example configuration file +# Port to listen on +Port = 4433 +# Disable TLS v1.2 for test. +# Protocol = ALL, -TLSv1.2 +# Only support 3 curves +Curves = P-521:P-384:P-256 +# Restricted signature algorithms +SignatureAlgorithms = RSA+SHA512:ECDSA+SHA512 +Certificate=server.pem +PrivateKey=server.pem +ChainCAFile=root.pem +VerifyCAFile=root.pem + +# Request certificate +VerifyMode=Request +ClientCAFile=root.pem diff --git a/demos/bio/client-arg.c b/demos/bio/client-arg.c new file mode 100644 index 000000000..e8d5e46ab --- /dev/null +++ b/demos/bio/client-arg.c @@ -0,0 +1,117 @@ +/* + * Copyright 2013-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#include +#include + +int main(int argc, char **argv) +{ + BIO *sbio = NULL, *out = NULL; + int len; + char tmpbuf[1024]; + SSL_CTX *ctx; + SSL_CONF_CTX *cctx; + SSL *ssl; + char **args = argv + 1; + const char *connect_str = "localhost:4433"; + int nargs = argc - 1; + + ctx = SSL_CTX_new(TLS_client_method()); + cctx = SSL_CONF_CTX_new(); + SSL_CONF_CTX_set_flags(cctx, SSL_CONF_FLAG_CLIENT); + SSL_CONF_CTX_set_ssl_ctx(cctx, ctx); + while (*args && **args == '-') { + int rv; + /* Parse standard arguments */ + rv = SSL_CONF_cmd_argv(cctx, &nargs, &args); + if (rv == -3) { + fprintf(stderr, "Missing argument for %s\n", *args); + goto end; + } + if (rv < 0) { + fprintf(stderr, "Error in command %s\n", *args); + ERR_print_errors_fp(stderr); + goto end; + } + /* If rv > 0 we processed something so proceed to next arg */ + if (rv > 0) + continue; + /* Otherwise application specific argument processing */ + if (strcmp(*args, "-connect") == 0) { + connect_str = args[1]; + if (connect_str == NULL) { + fprintf(stderr, "Missing -connect argument\n"); + goto end; + } + args += 2; + nargs -= 2; + continue; + } else { + fprintf(stderr, "Unknown argument %s\n", *args); + goto end; + } + } + + if (!SSL_CONF_CTX_finish(cctx)) { + fprintf(stderr, "Finish error\n"); + ERR_print_errors_fp(stderr); + goto end; + } + + /* + * We'd normally set some stuff like the verify paths and * mode here + * because as things stand this will connect to * any server whose + * certificate is signed by any CA. + */ + + sbio = BIO_new_ssl_connect(ctx); + + BIO_get_ssl(sbio, &ssl); + + if (!ssl) { + fprintf(stderr, "Can't locate SSL pointer\n"); + goto end; + } + + /* Don't want any retries */ + SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY); + + /* We might want to do other things with ssl here */ + + BIO_set_conn_hostname(sbio, connect_str); + + out = BIO_new_fp(stdout, BIO_NOCLOSE); + if (BIO_do_connect(sbio) <= 0) { + fprintf(stderr, "Error connecting to server\n"); + ERR_print_errors_fp(stderr); + goto end; + } + + if (BIO_do_handshake(sbio) <= 0) { + fprintf(stderr, "Error establishing SSL connection\n"); + ERR_print_errors_fp(stderr); + goto end; + } + + /* Could examine ssl here to get connection info */ + + BIO_puts(sbio, "GET / HTTP/1.0\n\n"); + for (;;) { + len = BIO_read(sbio, tmpbuf, 1024); + if (len <= 0) + break; + BIO_write(out, tmpbuf, len); + } + end: + SSL_CONF_CTX_free(cctx); + BIO_free_all(sbio); + BIO_free(out); + return 0; +} diff --git a/demos/bio/client-conf.c b/demos/bio/client-conf.c new file mode 100644 index 000000000..e819030ee --- /dev/null +++ b/demos/bio/client-conf.c @@ -0,0 +1,126 @@ +/* + * Copyright 2013-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#include +#include +#include + +int main(int argc, char **argv) +{ + BIO *sbio = NULL, *out = NULL; + int i, len, rv; + char tmpbuf[1024]; + SSL_CTX *ctx = NULL; + SSL_CONF_CTX *cctx = NULL; + SSL *ssl = NULL; + CONF *conf = NULL; + STACK_OF(CONF_VALUE) *sect = NULL; + CONF_VALUE *cnf; + const char *connect_str = "localhost:4433"; + long errline = -1; + + conf = NCONF_new(NULL); + + if (NCONF_load(conf, "connect.cnf", &errline) <= 0) { + if (errline <= 0) + fprintf(stderr, "Error processing config file\n"); + else + fprintf(stderr, "Error on line %ld\n", errline); + goto end; + } + + sect = NCONF_get_section(conf, "default"); + + if (sect == NULL) { + fprintf(stderr, "Error retrieving default section\n"); + goto end; + } + + ctx = SSL_CTX_new(TLS_client_method()); + cctx = SSL_CONF_CTX_new(); + SSL_CONF_CTX_set_flags(cctx, SSL_CONF_FLAG_CLIENT); + SSL_CONF_CTX_set_flags(cctx, SSL_CONF_FLAG_FILE); + SSL_CONF_CTX_set_ssl_ctx(cctx, ctx); + for (i = 0; i < sk_CONF_VALUE_num(sect); i++) { + cnf = sk_CONF_VALUE_value(sect, i); + rv = SSL_CONF_cmd(cctx, cnf->name, cnf->value); + if (rv > 0) + continue; + if (rv != -2) { + fprintf(stderr, "Error processing %s = %s\n", + cnf->name, cnf->value); + ERR_print_errors_fp(stderr); + goto end; + } + if (strcmp(cnf->name, "Connect") == 0) { + connect_str = cnf->value; + } else { + fprintf(stderr, "Unknown configuration option %s\n", cnf->name); + goto end; + } + } + + if (!SSL_CONF_CTX_finish(cctx)) { + fprintf(stderr, "Finish error\n"); + ERR_print_errors_fp(stderr); + goto end; + } + + /* + * We'd normally set some stuff like the verify paths and * mode here + * because as things stand this will connect to * any server whose + * certificate is signed by any CA. + */ + + sbio = BIO_new_ssl_connect(ctx); + + BIO_get_ssl(sbio, &ssl); + + if (!ssl) { + fprintf(stderr, "Can't locate SSL pointer\n"); + goto end; + } + + /* Don't want any retries */ + SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY); + + /* We might want to do other things with ssl here */ + + BIO_set_conn_hostname(sbio, connect_str); + + out = BIO_new_fp(stdout, BIO_NOCLOSE); + if (BIO_do_connect(sbio) <= 0) { + fprintf(stderr, "Error connecting to server\n"); + ERR_print_errors_fp(stderr); + goto end; + } + + if (BIO_do_handshake(sbio) <= 0) { + fprintf(stderr, "Error establishing SSL connection\n"); + ERR_print_errors_fp(stderr); + goto end; + } + + /* Could examine ssl here to get connection info */ + + BIO_puts(sbio, "GET / HTTP/1.0\n\n"); + for (;;) { + len = BIO_read(sbio, tmpbuf, 1024); + if (len <= 0) + break; + BIO_write(out, tmpbuf, len); + } + end: + SSL_CONF_CTX_free(cctx); + BIO_free_all(sbio); + BIO_free(out); + NCONF_free(conf); + return 0; +} diff --git a/demos/bio/cmod.cnf b/demos/bio/cmod.cnf new file mode 100644 index 000000000..4c45dfb78 --- /dev/null +++ b/demos/bio/cmod.cnf @@ -0,0 +1,24 @@ +# Example config module configuration + +# Name supplied by application to CONF_modules_load_file +# and section containing configuration +testapp = test_sect + +[test_sect] +# list of confuration modules + +# SSL configuration module +ssl_conf = ssl_sect + +[ssl_sect] +# list of SSL configurations +server = server_sect + +[server_sect] +# Only support 3 curves +Curves = P-521:P-384:P-256 +# Restricted signature algorithms +SignatureAlgorithms = RSA+SHA512:ECDSA+SHA512 +# Certificates and keys +RSA.Certificate=server.pem +ECDSA.Certificate=server-ec.pem diff --git a/demos/bio/connect.cnf b/demos/bio/connect.cnf new file mode 100644 index 000000000..4dee03c37 --- /dev/null +++ b/demos/bio/connect.cnf @@ -0,0 +1,9 @@ +# Example configuration file +# Connects to the default port of s_server +Connect = localhost:4433 +# Disable TLS v1.2 for test. +# Protocol = ALL, -TLSv1.2 +# Only support 3 curves +Curves = P-521:P-384:P-256 +# Restricted signature algorithms +SignatureAlgorithms = RSA+SHA512:ECDSA+SHA512 diff --git a/demos/bio/descrip.mms b/demos/bio/descrip.mms new file mode 100644 index 000000000..8e127b079 --- /dev/null +++ b/demos/bio/descrip.mms @@ -0,0 +1,47 @@ +# This build description trusts that the following logical names are defined: +# +# For compilation: OPENSSL +# For linking with shared libraries: OSSL$LIBCRYPTO_SHR and OSSL$LIBSSL_SHR +# For linking with static libraries: OSSL$LIBCRYPTO and OSSL$LIBSSL +# +# These are normally defined with the OpenSSL startup procedure + +# By default, we link with the shared libraries +SHARED = TRUE + +# Alternative, for linking with static libraries +#SHARED = FALSE + +.FIRST : + IF "$(SHARED)" .EQS. "TRUE" THEN DEFINE OPT []shared.opt + IF "$(SHARED)" .NES. "TRUE" THEN DEFINE OPT []static.opt + +.LAST : + DEASSIGN OPT + +.DEFAULT : + @ ! + +# Because we use an option file, we need to redefine this +.obj.exe : + $(LINK) $(LINKFLAGS) $<,OPT:/OPT + +all : client-arg.exe client-conf.exe saccept.exe sconnect.exe - + server-arg.exe server-cmod.exe server-conf.exe + +client-arg.exe : client-arg.obj +client-conf.exe : client-conf.obj +saccept.exe : saccept.obj +sconnect.exe : sconnect.obj +server-arg.exe : server-arg.obj +server-cmod.exe : server-cmod.obj +server-conf.exe : server-conf.obj + +# Stoopid MMS doesn't infer this automatically... +client-arg.obj : client-arg.c +client-conf.obj : client-conf.c +saccept.obj : saccept.c +sconnect.obj : sconnect.c +server-arg.obj : server-arg.c +server-cmod.obj : server-cmod.c +server-conf.obj : server-conf.c diff --git a/demos/bio/intca.pem b/demos/bio/intca.pem new file mode 100644 index 000000000..3551ea93d --- /dev/null +++ b/demos/bio/intca.pem @@ -0,0 +1,23 @@ +-----BEGIN CERTIFICATE----- +MIIDvjCCAqagAwIBAgIJAPzCy4CUW9/qMA0GCSqGSIb3DQEBCwUAMGgxCzAJBgNV +BAYTAlVLMRYwFAYDVQQKDA1PcGVuU1NMIEdyb3VwMSIwIAYDVQQLDBlGT1IgVEVT +VElORyBQVVJQT1NFUyBPTkxZMR0wGwYDVQQDDBRPcGVuU1NMIFRlc3QgUm9vdCBD +QTAeFw0xNTA3MTQxMzIyMDVaFw0yNTA2MjExMzIyMDVaMHAxCzAJBgNVBAYTAlVL +MRYwFAYDVQQKDA1PcGVuU1NMIEdyb3VwMSIwIAYDVQQLDBlGT1IgVEVTVElORyBQ +VVJQT1NFUyBPTkxZMSUwIwYDVQQDDBxPcGVuU1NMIFRlc3QgSW50ZXJtZWRpYXRl +IENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsErw75CmLYD6pkrG +W/YhAl/K8L5wJYxDjqu2FghxjD8K308W3EHq4uBxEwR1OHXaM1+6ZZw7/r2I37VL +IdurBEAIEUdbzx0so74FPawgz5EW2CTqoJnK8F71/vo5Kj1VPwW46CxwxUR3cfvJ +GNXND2ip0TcyTSPLROXOyQakcVfIGJmdSa1wHKi+c2gMA4emADudZUOYLrg80gr2 +ldePm07ynbVsKKzCcStw8MdmoW9Qt3fLnPJn2TFUUBNWj+4kvL+88edWCVQXKNds +ysD/CDrH4W/hjyPDStVsM6XpiNU0+L2ZY6fcj3OP8d0goOx45xotMn9m8hNkCGsr +VXx9IwIDAQABo2MwYTAdBgNVHQ4EFgQUNsNsiOeV/rC97M4+PYarIYGH2towHwYD +VR0jBBgwFoAUjBkP10IxdwUG4dOxn+s5+3hxOkUwDwYDVR0TAQH/BAUwAwEB/zAO +BgNVHQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQELBQADggEBAANQT0pDWBQoT/RY76xz +audadGz/dfYnwvSwT0RMFcXLcMVVRNqP0HeR8OP8qLaP7onRbNnEXNfos9pxXYlg +j+/WjWTBLVcr3pX2Xtmcaqw3CGN9qbQI8B3JkYeijZmc5+3r5MzK/9R0w8Y/T9Xt +CXEiQhtWHpPrFEfrExeVy2kjJNRctEfq3OTd1bjgX64zvTU7eR+MHFYKPoyMqwIR +gjoVKinvovEwWoZe5kfMQwJNA3IgoJexX9BXbS8efAYF/ku3tS0laoZS/q6V/o5I +RvG0OqnNgxhul+96PE5ujSaprsyvBswIUKt+e/BCxGaS6f2AJ8RmtoPOSfT4b9qN +thI= +-----END CERTIFICATE----- diff --git a/demos/bio/root.pem b/demos/bio/root.pem new file mode 100644 index 000000000..3bd0e9b3e --- /dev/null +++ b/demos/bio/root.pem @@ -0,0 +1,22 @@ +-----BEGIN CERTIFICATE----- +MIIDtjCCAp6gAwIBAgIJAKkg71CjIAovMA0GCSqGSIb3DQEBBQUAMGgxCzAJBgNV +BAYTAlVLMRYwFAYDVQQKDA1PcGVuU1NMIEdyb3VwMSIwIAYDVQQLDBlGT1IgVEVT +VElORyBQVVJQT1NFUyBPTkxZMR0wGwYDVQQDDBRPcGVuU1NMIFRlc3QgUm9vdCBD +QTAeFw0xNDAyMjMxMzA1MTNaFw0yNDAyMjExMzA1MTNaMGgxCzAJBgNVBAYTAlVL +MRYwFAYDVQQKDA1PcGVuU1NMIEdyb3VwMSIwIAYDVQQLDBlGT1IgVEVTVElORyBQ +VVJQT1NFUyBPTkxZMR0wGwYDVQQDDBRPcGVuU1NMIFRlc3QgUm9vdCBDQTCCASIw +DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANMaarigKGOra5Mc/LrhOkcmHzDs +vkYL7dfaaht8fLBKRTYwzSBvO9x54koTWjq7HkbaxkYAg3HnDTkNCyzkGKNdM89H +q/PtGIFFlceQIOat3Kjd05Iw3PtLEWTDjT6FMA9Mkjk/XbpmycqRIwNKtgICoFsG +juIpc4P31kxK7i3ri+JnlyvVmRZjJxrheJB0qHGXilrOVDPOliDn//jXbcyzXemu +R8KgAeQM4IIs9jYHJOgHrTItIpwa9wNTEp9KCGkO6xr20NkKyDp6XRyd+hmnUB7r +77WTptvKPFFTjTDFqEtcif9U2kVkCfn2mSRO8noCbVH++fuR8LMWlD99gt8CAwEA +AaNjMGEwHQYDVR0OBBYEFIwZD9dCMXcFBuHTsZ/rOft4cTpFMB8GA1UdIwQYMBaA +FIwZD9dCMXcFBuHTsZ/rOft4cTpFMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/ +BAQDAgEGMA0GCSqGSIb3DQEBBQUAA4IBAQCsoxVi49anYZ1aI/2rVJ5bvEd3ZvGn +wx1Y+l75SQVYU2qX9CHNBVg1t8reIBN8yPEfBM1WcFPEg7Vy3zFaklMPm/oYXwVI +/lX/LsfPUxdnQmONxLw4x/0booN1LV/dtRcebewUSqog6W9Z2fbTEe6srIBE4M5G +Wa943lthlmQM6HzlU4D606PQ3zQbX08mue4eqQB813r4uSoI1MpGLqxkziBRFGGN +T4VNYp8DeSVr3jHjNBmKCAPZxJIYElnLEK027OG00RH7sF7SGFDNsCjN1NmCvuRz +9AHnjVIBNzIvI3uiOn9tngRDXBRIcUBsdYG19tal8yWBgrr9SdlqFy/Y +-----END CERTIFICATE----- diff --git a/demos/bio/saccept.c b/demos/bio/saccept.c new file mode 100644 index 000000000..66c5c6175 --- /dev/null +++ b/demos/bio/saccept.c @@ -0,0 +1,122 @@ +/* + * Copyright 1998-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +/*- + * A minimal program to serve an SSL connection. + * It uses blocking. + * saccept host:port + * host is the interface IP to use. If any interface, use *:port + * The default it *:4433 + * + * cc -I../../include saccept.c -L../.. -lssl -lcrypto -ldl + */ + +#include +#include +#include +#include + +#define CERT_FILE "server.pem" + +static int done = 0; + +void interrupt(int sig) +{ + done = 1; +} + +void sigsetup(void) +{ + struct sigaction sa; + + /* + * Catch at most once, and don't restart the accept system call. + */ + sa.sa_flags = SA_RESETHAND; + sa.sa_handler = interrupt; + sigemptyset(&sa.sa_mask); + sigaction(SIGINT, &sa, NULL); +} + +int main(int argc, char *argv[]) +{ + char *port = NULL; + BIO *in = NULL; + BIO *ssl_bio, *tmp; + SSL_CTX *ctx; + char buf[512]; + int ret = 1, i; + + if (argc <= 1) + port = "*:4433"; + else + port = argv[1]; + + ctx = SSL_CTX_new(TLS_server_method()); + if (!SSL_CTX_use_certificate_chain_file(ctx, CERT_FILE)) + goto err; + if (!SSL_CTX_use_PrivateKey_file(ctx, CERT_FILE, SSL_FILETYPE_PEM)) + goto err; + if (!SSL_CTX_check_private_key(ctx)) + goto err; + + /* Setup server side SSL bio */ + ssl_bio = BIO_new_ssl(ctx, 0); + + if ((in = BIO_new_accept(port)) == NULL) + goto err; + + /* + * This means that when a new connection is accepted on 'in', The ssl_bio + * will be 'duplicated' and have the new socket BIO push into it. + * Basically it means the SSL BIO will be automatically setup + */ + BIO_set_accept_bios(in, ssl_bio); + + /* Arrange to leave server loop on interrupt */ + sigsetup(); + + again: + /* + * The first call will setup the accept socket, and the second will get a + * socket. In this loop, the first actual accept will occur in the + * BIO_read() function. + */ + + if (BIO_do_accept(in) <= 0) + goto err; + + while (!done) { + i = BIO_read(in, buf, 512); + if (i == 0) { + /* + * If we have finished, remove the underlying BIO stack so the + * next time we call any function for this BIO, it will attempt + * to do an accept + */ + printf("Done\n"); + tmp = BIO_pop(in); + BIO_free_all(tmp); + goto again; + } + if (i < 0) + goto err; + fwrite(buf, 1, i, stdout); + fflush(stdout); + } + + ret = 0; + err: + if (ret) { + ERR_print_errors_fp(stderr); + } + BIO_free(in); + exit(ret); + return (!ret); +} diff --git a/demos/bio/sconnect.c b/demos/bio/sconnect.c new file mode 100644 index 000000000..664a1e038 --- /dev/null +++ b/demos/bio/sconnect.c @@ -0,0 +1,131 @@ +/* + * Copyright 1998-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +/*- + * A minimal program to do SSL to a passed host and port. + * It is actually using non-blocking IO but in a very simple manner + * sconnect host:port - it does a 'GET / HTTP/1.0' + * + * cc -I../../include sconnect.c -L../.. -lssl -lcrypto + */ +#include +#include +#include +#include +#include +#include + +#define HOSTPORT "localhost:4433" +#define CAFILE "root.pem" + +extern int errno; + +int main(argc, argv) +int argc; +char *argv[]; +{ + const char *hostport = HOSTPORT; + const char *CAfile = CAFILE; + char *hostname; + char *cp; + BIO *out = NULL; + char buf[1024 * 10], *p; + SSL_CTX *ssl_ctx = NULL; + SSL *ssl; + BIO *ssl_bio; + int i, len, off, ret = 1; + + if (argc > 1) + hostport = argv[1]; + if (argc > 2) + CAfile = argv[2]; + + hostname = OPENSSL_strdup(hostport); + if ((cp = strchr(hostname, ':')) != NULL) + *cp = 0; + +#ifdef WATT32 + dbug_init(); + sock_init(); +#endif + + ssl_ctx = SSL_CTX_new(TLS_client_method()); + + /* Enable trust chain verification */ + SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, NULL); + SSL_CTX_load_verify_locations(ssl_ctx, CAfile, NULL); + + /* Lets make a SSL structure */ + ssl = SSL_new(ssl_ctx); + SSL_set_connect_state(ssl); + + /* Enable peername verification */ + if (SSL_set1_host(ssl, hostname) <= 0) + goto err; + + /* Use it inside an SSL BIO */ + ssl_bio = BIO_new(BIO_f_ssl()); + BIO_set_ssl(ssl_bio, ssl, BIO_CLOSE); + + /* Lets use a connect BIO under the SSL BIO */ + out = BIO_new(BIO_s_connect()); + BIO_set_conn_hostname(out, hostport); + BIO_set_nbio(out, 1); + out = BIO_push(ssl_bio, out); + + p = "GET / HTTP/1.0\r\n\r\n"; + len = strlen(p); + + off = 0; + for (;;) { + i = BIO_write(out, &(p[off]), len); + if (i <= 0) { + if (BIO_should_retry(out)) { + fprintf(stderr, "write DELAY\n"); + sleep(1); + continue; + } else { + goto err; + } + } + off += i; + len -= i; + if (len <= 0) + break; + } + + for (;;) { + i = BIO_read(out, buf, sizeof(buf)); + if (i == 0) + break; + if (i < 0) { + if (BIO_should_retry(out)) { + fprintf(stderr, "read DELAY\n"); + sleep(1); + continue; + } + goto err; + } + fwrite(buf, 1, i, stdout); + } + + ret = 1; + goto done; + + err: + if (ERR_peek_error() == 0) { /* system call error */ + fprintf(stderr, "errno=%d ", errno); + perror("error"); + } else + ERR_print_errors_fp(stderr); + done: + BIO_free_all(out); + SSL_CTX_free(ssl_ctx); + return (ret == 1); +} diff --git a/demos/bio/server-arg.c b/demos/bio/server-arg.c new file mode 100644 index 000000000..6056969fe --- /dev/null +++ b/demos/bio/server-arg.c @@ -0,0 +1,145 @@ +/* + * Copyright 2013-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +/* + * A minimal program to serve an SSL connection. It uses blocking. It use the + * SSL_CONF API with the command line. cc -I../../include server-arg.c + * -L../.. -lssl -lcrypto -ldl + */ + +#include +#include +#include +#include +#include + +int main(int argc, char *argv[]) +{ + char *port = "*:4433"; + BIO *ssl_bio, *tmp; + SSL_CTX *ctx; + SSL_CONF_CTX *cctx; + char buf[512]; + BIO *in = NULL; + int ret = 1, i; + char **args = argv + 1; + int nargs = argc - 1; + + ctx = SSL_CTX_new(TLS_server_method()); + + cctx = SSL_CONF_CTX_new(); + SSL_CONF_CTX_set_flags(cctx, SSL_CONF_FLAG_SERVER); + SSL_CONF_CTX_set_flags(cctx, SSL_CONF_FLAG_CERTIFICATE); + SSL_CONF_CTX_set_ssl_ctx(cctx, ctx); + while (*args && **args == '-') { + int rv; + /* Parse standard arguments */ + rv = SSL_CONF_cmd_argv(cctx, &nargs, &args); + if (rv == -3) { + fprintf(stderr, "Missing argument for %s\n", *args); + goto err; + } + if (rv < 0) { + fprintf(stderr, "Error in command %s\n", *args); + ERR_print_errors_fp(stderr); + goto err; + } + /* If rv > 0 we processed something so proceed to next arg */ + if (rv > 0) + continue; + /* Otherwise application specific argument processing */ + if (strcmp(*args, "-port") == 0) { + port = args[1]; + if (port == NULL) { + fprintf(stderr, "Missing -port argument\n"); + goto err; + } + args += 2; + nargs -= 2; + continue; + } else { + fprintf(stderr, "Unknown argument %s\n", *args); + goto err; + } + } + + if (!SSL_CONF_CTX_finish(cctx)) { + fprintf(stderr, "Finish error\n"); + ERR_print_errors_fp(stderr); + goto err; + } +#ifdef ITERATE_CERTS + /* + * Demo of how to iterate over all certificates in an SSL_CTX structure. + */ + { + X509 *x; + int rv; + rv = SSL_CTX_set_current_cert(ctx, SSL_CERT_SET_FIRST); + while (rv) { + X509 *x = SSL_CTX_get0_certificate(ctx); + X509_NAME_print_ex_fp(stdout, X509_get_subject_name(x), 0, + XN_FLAG_ONELINE); + printf("\n"); + rv = SSL_CTX_set_current_cert(ctx, SSL_CERT_SET_NEXT); + } + fflush(stdout); + } +#endif + /* Setup server side SSL bio */ + ssl_bio = BIO_new_ssl(ctx, 0); + + if ((in = BIO_new_accept(port)) == NULL) + goto err; + + /* + * This means that when a new connection is accepted on 'in', The ssl_bio + * will be 'duplicated' and have the new socket BIO push into it. + * Basically it means the SSL BIO will be automatically setup + */ + BIO_set_accept_bios(in, ssl_bio); + + again: + /* + * The first call will setup the accept socket, and the second will get a + * socket. In this loop, the first actual accept will occur in the + * BIO_read() function. + */ + + if (BIO_do_accept(in) <= 0) + goto err; + + for (;;) { + i = BIO_read(in, buf, 512); + if (i == 0) { + /* + * If we have finished, remove the underlying BIO stack so the + * next time we call any function for this BIO, it will attempt + * to do an accept + */ + printf("Done\n"); + tmp = BIO_pop(in); + BIO_free_all(tmp); + goto again; + } + if (i < 0) + goto err; + fwrite(buf, 1, i, stdout); + fflush(stdout); + } + + ret = 0; + err: + if (ret) { + ERR_print_errors_fp(stderr); + } + BIO_free(in); + exit(ret); + return (!ret); +} diff --git a/demos/bio/server-cmod.c b/demos/bio/server-cmod.c new file mode 100644 index 000000000..9cb246375 --- /dev/null +++ b/demos/bio/server-cmod.c @@ -0,0 +1,95 @@ +/* + * Copyright 2015-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +/* + * A minimal TLS server it ses SSL_CTX_config and a configuration file to + * set most server parameters. + */ + +#include +#include +#include +#include +#include + +int main(int argc, char *argv[]) +{ + unsigned char buf[512]; + char *port = "*:4433"; + BIO *in = NULL; + BIO *ssl_bio, *tmp; + SSL_CTX *ctx; + int ret = 1, i; + + ctx = SSL_CTX_new(TLS_server_method()); + + if (CONF_modules_load_file("cmod.cnf", "testapp", 0) <= 0) { + fprintf(stderr, "Error processing config file\n"); + goto err; + } + + if (SSL_CTX_config(ctx, "server") == 0) { + fprintf(stderr, "Error configuring server.\n"); + goto err; + } + + /* Setup server side SSL bio */ + ssl_bio = BIO_new_ssl(ctx, 0); + + if ((in = BIO_new_accept(port)) == NULL) + goto err; + + /* + * This means that when a new connection is accepted on 'in', The ssl_bio + * will be 'duplicated' and have the new socket BIO push into it. + * Basically it means the SSL BIO will be automatically setup + */ + BIO_set_accept_bios(in, ssl_bio); + + again: + /* + * The first call will setup the accept socket, and the second will get a + * socket. In this loop, the first actual accept will occur in the + * BIO_read() function. + */ + + if (BIO_do_accept(in) <= 0) + goto err; + + for (;;) { + i = BIO_read(in, buf, sizeof(buf)); + if (i == 0) { + /* + * If we have finished, remove the underlying BIO stack so the + * next time we call any function for this BIO, it will attempt + * to do an accept + */ + printf("Done\n"); + tmp = BIO_pop(in); + BIO_free_all(tmp); + goto again; + } + if (i < 0) { + if (BIO_should_retry(in)) + continue; + goto err; + } + fwrite(buf, 1, i, stdout); + fflush(stdout); + } + + ret = 0; + err: + if (ret) { + ERR_print_errors_fp(stderr); + } + BIO_free(in); + exit(ret); + return (!ret); +} diff --git a/demos/bio/server-conf.c b/demos/bio/server-conf.c new file mode 100644 index 000000000..41b13089c --- /dev/null +++ b/demos/bio/server-conf.c @@ -0,0 +1,140 @@ +/* + * Copyright 2013-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +/* + * A minimal program to serve an SSL connection. It uses blocking. It uses + * the SSL_CONF API with a configuration file. cc -I../../include saccept.c + * -L../.. -lssl -lcrypto -ldl + */ + +#include +#include +#include +#include +#include +#include + +int main(int argc, char *argv[]) +{ + char *port = "*:4433"; + BIO *in = NULL; + BIO *ssl_bio, *tmp; + SSL_CTX *ctx; + SSL_CONF_CTX *cctx = NULL; + CONF *conf = NULL; + STACK_OF(CONF_VALUE) *sect = NULL; + CONF_VALUE *cnf; + long errline = -1; + char buf[512]; + int ret = 1, i; + + ctx = SSL_CTX_new(TLS_server_method()); + + conf = NCONF_new(NULL); + + if (NCONF_load(conf, "accept.cnf", &errline) <= 0) { + if (errline <= 0) + fprintf(stderr, "Error processing config file\n"); + else + fprintf(stderr, "Error on line %ld\n", errline); + goto err; + } + + sect = NCONF_get_section(conf, "default"); + + if (sect == NULL) { + fprintf(stderr, "Error retrieving default section\n"); + goto err; + } + + cctx = SSL_CONF_CTX_new(); + SSL_CONF_CTX_set_flags(cctx, SSL_CONF_FLAG_SERVER); + SSL_CONF_CTX_set_flags(cctx, SSL_CONF_FLAG_CERTIFICATE); + SSL_CONF_CTX_set_flags(cctx, SSL_CONF_FLAG_FILE); + SSL_CONF_CTX_set_ssl_ctx(cctx, ctx); + for (i = 0; i < sk_CONF_VALUE_num(sect); i++) { + int rv; + cnf = sk_CONF_VALUE_value(sect, i); + rv = SSL_CONF_cmd(cctx, cnf->name, cnf->value); + if (rv > 0) + continue; + if (rv != -2) { + fprintf(stderr, "Error processing %s = %s\n", + cnf->name, cnf->value); + ERR_print_errors_fp(stderr); + goto err; + } + if (strcmp(cnf->name, "Port") == 0) { + port = cnf->value; + } else { + fprintf(stderr, "Unknown configuration option %s\n", cnf->name); + goto err; + } + } + + if (!SSL_CONF_CTX_finish(cctx)) { + fprintf(stderr, "Finish error\n"); + ERR_print_errors_fp(stderr); + goto err; + } + + /* Setup server side SSL bio */ + ssl_bio = BIO_new_ssl(ctx, 0); + + if ((in = BIO_new_accept(port)) == NULL) + goto err; + + /* + * This means that when a new connection is accepted on 'in', The ssl_bio + * will be 'duplicated' and have the new socket BIO push into it. + * Basically it means the SSL BIO will be automatically setup + */ + BIO_set_accept_bios(in, ssl_bio); + + again: + /* + * The first call will setup the accept socket, and the second will get a + * socket. In this loop, the first actual accept will occur in the + * BIO_read() function. + */ + + if (BIO_do_accept(in) <= 0) + goto err; + + for (;;) { + i = BIO_read(in, buf, 512); + if (i == 0) { + /* + * If we have finished, remove the underlying BIO stack so the + * next time we call any function for this BIO, it will attempt + * to do an accept + */ + printf("Done\n"); + tmp = BIO_pop(in); + BIO_free_all(tmp); + goto again; + } + if (i < 0) { + if (BIO_should_retry(in)) + continue; + goto err; + } + fwrite(buf, 1, i, stdout); + fflush(stdout); + } + + ret = 0; + err: + if (ret) { + ERR_print_errors_fp(stderr); + } + BIO_free(in); + exit(ret); + return (!ret); +} diff --git a/demos/bio/server-ec.pem b/demos/bio/server-ec.pem new file mode 100644 index 000000000..a13fdc7e2 --- /dev/null +++ b/demos/bio/server-ec.pem @@ -0,0 +1,17 @@ +-----BEGIN PRIVATE KEY----- +MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg/5kYU3PUlHwfdjEN +lC1xTZEx3o55RgtSOuOCTryDfomhRANCAARW/qUFg+qZzjcFWrST4bmkRCFu8/rn +KTHjW2vpBXYGXKDn4AbAfYXYhM9J7v1HkkrZBPPGx53eVzs61/Pgr6Rc +-----END PRIVATE KEY----- +-----BEGIN CERTIFICATE----- +MIIBsTCCAVegAwIBAgIJALChLe0vZzgoMAoGCCqGSM49BAMCMDUxHzAdBgNVBAsM +FlRlc3QgRUNEU0EgQ2VydGlmaWNhdGUxEjAQBgNVBAMMCWxvY2FsaG9zdDAeFw0x +NTEyMjIxNDUxMDRaFw00NDAxMDQxNDUxMDRaMDUxHzAdBgNVBAsMFlRlc3QgRUNE +U0EgQ2VydGlmaWNhdGUxEjAQBgNVBAMMCWxvY2FsaG9zdDBZMBMGByqGSM49AgEG +CCqGSM49AwEHA0IABFb+pQWD6pnONwVatJPhuaREIW7z+ucpMeNba+kFdgZcoOfg +BsB9hdiEz0nu/UeSStkE88bHnd5XOzrX8+CvpFyjUDBOMB0GA1UdDgQWBBROhkTJ +lsm8Qd8pEgrrapccfFY5gjAfBgNVHSMEGDAWgBROhkTJlsm8Qd8pEgrrapccfFY5 +gjAMBgNVHRMEBTADAQH/MAoGCCqGSM49BAMCA0gAMEUCIFhyU/WZRcihilTpwFVm +fly1JhwisouiZjLnPkRYZVzHAiEAgqxXfRQl1/phnEgO9gRcv2nFp9xvJiDgKPse +VktDYjE= +-----END CERTIFICATE----- diff --git a/demos/bio/server.pem b/demos/bio/server.pem new file mode 100644 index 000000000..8a4a51f9f --- /dev/null +++ b/demos/bio/server.pem @@ -0,0 +1,77 @@ +subject= C = UK, O = OpenSSL Group, OU = FOR TESTING PURPOSES ONLY, CN = Test Server Cert +issuer= C = UK, O = OpenSSL Group, OU = FOR TESTING PURPOSES ONLY, CN = OpenSSL Test Intermediate CA +-----BEGIN CERTIFICATE----- +MIIDyTCCArGgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBwMQswCQYDVQQGEwJVSzEW +MBQGA1UECgwNT3BlblNTTCBHcm91cDEiMCAGA1UECwwZRk9SIFRFU1RJTkcgUFVS +UE9TRVMgT05MWTElMCMGA1UEAwwcT3BlblNTTCBUZXN0IEludGVybWVkaWF0ZSBD +QTAgFw0xNjAxMDQwODU0NDZaGA8yMTE2MDEwNTA4NTQ0NlowZDELMAkGA1UEBhMC +VUsxFjAUBgNVBAoMDU9wZW5TU0wgR3JvdXAxIjAgBgNVBAsMGUZPUiBURVNUSU5H +IFBVUlBPU0VTIE9OTFkxGTAXBgNVBAMMEFRlc3QgU2VydmVyIENlcnQwggEiMA0G +CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDzhPOSNtyyRspmeuUpxfNJKCLTuf7g +3uQ4zu4iHOmRO5TQci+HhVlLZrHF9XqFXcIP0y4pWDbMSGuiorUmzmfiR7bfSdI/ ++qIQt8KXRH6HNG1t8ou0VSvWId5TS5Dq/er5ODUr9OaaDva7EquHIcMvvPQGuI+O +EAcnleVCy9HVEIySrO4P3CNIicnGkwwiAud05yUAq/gPXBC1hTtmlPD7TVcGVSEi +Jdvzqqlgv02qedGrkki6GY4S7GjZxrrf7Foc2EP+51LJzwLQx3/JfrCU41NEWAsu +/Sl0tQabXESN+zJ1pDqoZ3uHMgpQjeGiE0olr+YcsSW/tJmiU9OiAr8RAgMBAAGj +eDB2MB0GA1UdDgQWBBSCvM8AABPR9zklmifnr9LvIBturDAfBgNVHSMEGDAWgBQ2 +w2yI55X+sL3szj49hqshgYfa2jAJBgNVHRMEAjAAMBMGA1UdJQQMMAoGCCsGAQUF +BwMBMBQGA1UdEQQNMAuCCWxvY2FsaG9zdDANBgkqhkiG9w0BAQsFAAOCAQEAC78R +sAr4uvkYOu/pSwQ3MYOFqZ0BnPuP0/AZW2zF7TLNy8g36GyH9rKxz2ffQEHRmPQN +Z11Ohg3z03jw/sVzkgt2U5Ipv923sSeCZcu0nuNex3v9/x72ldYikZNhQOsw+2kr +hx3OvE9R7xl9eyjz7BknsbY7PC3kiUY8SDdc5Fr/XMkHm3ge65oWYOHBjC5tAr5K +FGCEjM3syxS+Li5X6yfDGiVSjOU4gJuZDCYbl7cEQexU2deds8EmpJJrrI7s4JcQ +rraHI8+Hu8X9VLpZE1jl/fKJw3D0i53PoN2WhukIOg1Zv+ajMKQ4ubVfISH2ebox ++ybAZO8hxL6/I08/GQ== +-----END CERTIFICATE----- +subject= C = UK, O = OpenSSL Group, OU = FOR TESTING PURPOSES ONLY, CN = OpenSSL Test Intermediate CA +issuer= C = UK, O = OpenSSL Group, OU = FOR TESTING PURPOSES ONLY, CN = OpenSSL Test Root CA +-----BEGIN CERTIFICATE----- +MIIDvjCCAqagAwIBAgIJAPzCy4CUW9/qMA0GCSqGSIb3DQEBCwUAMGgxCzAJBgNV +BAYTAlVLMRYwFAYDVQQKDA1PcGVuU1NMIEdyb3VwMSIwIAYDVQQLDBlGT1IgVEVT +VElORyBQVVJQT1NFUyBPTkxZMR0wGwYDVQQDDBRPcGVuU1NMIFRlc3QgUm9vdCBD +QTAeFw0xNTA3MTQxMzIyMDVaFw0yNTA2MjExMzIyMDVaMHAxCzAJBgNVBAYTAlVL +MRYwFAYDVQQKDA1PcGVuU1NMIEdyb3VwMSIwIAYDVQQLDBlGT1IgVEVTVElORyBQ +VVJQT1NFUyBPTkxZMSUwIwYDVQQDDBxPcGVuU1NMIFRlc3QgSW50ZXJtZWRpYXRl +IENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsErw75CmLYD6pkrG +W/YhAl/K8L5wJYxDjqu2FghxjD8K308W3EHq4uBxEwR1OHXaM1+6ZZw7/r2I37VL +IdurBEAIEUdbzx0so74FPawgz5EW2CTqoJnK8F71/vo5Kj1VPwW46CxwxUR3cfvJ +GNXND2ip0TcyTSPLROXOyQakcVfIGJmdSa1wHKi+c2gMA4emADudZUOYLrg80gr2 +ldePm07ynbVsKKzCcStw8MdmoW9Qt3fLnPJn2TFUUBNWj+4kvL+88edWCVQXKNds +ysD/CDrH4W/hjyPDStVsM6XpiNU0+L2ZY6fcj3OP8d0goOx45xotMn9m8hNkCGsr +VXx9IwIDAQABo2MwYTAdBgNVHQ4EFgQUNsNsiOeV/rC97M4+PYarIYGH2towHwYD +VR0jBBgwFoAUjBkP10IxdwUG4dOxn+s5+3hxOkUwDwYDVR0TAQH/BAUwAwEB/zAO +BgNVHQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQELBQADggEBAANQT0pDWBQoT/RY76xz +audadGz/dfYnwvSwT0RMFcXLcMVVRNqP0HeR8OP8qLaP7onRbNnEXNfos9pxXYlg +j+/WjWTBLVcr3pX2Xtmcaqw3CGN9qbQI8B3JkYeijZmc5+3r5MzK/9R0w8Y/T9Xt +CXEiQhtWHpPrFEfrExeVy2kjJNRctEfq3OTd1bjgX64zvTU7eR+MHFYKPoyMqwIR +gjoVKinvovEwWoZe5kfMQwJNA3IgoJexX9BXbS8efAYF/ku3tS0laoZS/q6V/o5I +RvG0OqnNgxhul+96PE5ujSaprsyvBswIUKt+e/BCxGaS6f2AJ8RmtoPOSfT4b9qN +thI= +-----END CERTIFICATE----- +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEA84TzkjbcskbKZnrlKcXzSSgi07n+4N7kOM7uIhzpkTuU0HIv +h4VZS2axxfV6hV3CD9MuKVg2zEhroqK1Js5n4ke230nSP/qiELfCl0R+hzRtbfKL +tFUr1iHeU0uQ6v3q+Tg1K/Tmmg72uxKrhyHDL7z0BriPjhAHJ5XlQsvR1RCMkqzu +D9wjSInJxpMMIgLndOclAKv4D1wQtYU7ZpTw+01XBlUhIiXb86qpYL9NqnnRq5JI +uhmOEuxo2ca63+xaHNhD/udSyc8C0Md/yX6wlONTRFgLLv0pdLUGm1xEjfsydaQ6 +qGd7hzIKUI3hohNKJa/mHLElv7SZolPTogK/EQIDAQABAoIBAADq9FwNtuE5IRQn +zGtO4q7Y5uCzZ8GDNYr9RKp+P2cbuWDbvVAecYq2NV9QoIiWJOAYZKklOvekIju3 +r0UZLA0PRiIrTg6NrESx3JrjWDK8QNlUO7CPTZ39/K+FrmMkV9lem9yxjJjyC34D +AQB+YRTx+l14HppjdxNwHjAVQpIx/uO2F5xAMuk32+3K+pq9CZUtrofe1q4Agj9R +5s8mSy9pbRo9kW9wl5xdEotz1LivFOEiqPUJTUq5J5PeMKao3vdK726XI4Z455Nm +W2/MA0YV0ug2FYinHcZdvKM6dimH8GLfa3X8xKRfzjGjTiMSwsdjgMa4awY3tEHH +674jhAECgYEA/zqMrc0zsbNk83sjgaYIug5kzEpN4ic020rSZsmQxSCerJTgNhmg +utKSCt0Re09Jt3LqG48msahX8ycqDsHNvlEGPQSbMu9IYeO3Wr3fAm75GEtFWePY +BhM73I7gkRt4s8bUiUepMG/wY45c5tRF23xi8foReHFFe9MDzh8fJFECgYEA9EFX +4qAik1pOJGNei9BMwmx0I0gfVEIgu0tzeVqT45vcxbxr7RkTEaDoAG6PlbWP6D9a +WQNLp4gsgRM90ZXOJ4up5DsAWDluvaF4/omabMA+MJJ5kGZ0gCj5rbZbKqUws7x8 +bp+6iBfUPJUbcqNqFmi/08Yt7vrDnMnyMw2A/sECgYEAiiuRMxnuzVm34hQcsbhH +6ymVqf7j0PW2qK0F4H1ocT9qhzWFd+RB3kHWrCjnqODQoI6GbGr/4JepHUpre1ex +4UEN5oSS3G0ru0rC3U4C59dZ5KwDHFm7ffZ1pr52ljfQDUsrjjIMRtuiwNK2OoRa +WSsqiaL+SDzSB+nBmpnAizECgYBdt/y6rerWUx4MhDwwtTnel7JwHyo2MDFS6/5g +n8qC2Lj6/fMDRE22w+CA2esp7EJNQJGv+b27iFpbJEDh+/Lf5YzIT4MwVskQ5bYB +JFcmRxUVmf4e09D7o705U/DjCgMH09iCsbLmqQ38ONIRSHZaJtMDtNTHD1yi+jF+ +OT43gQKBgQC/2OHZoko6iRlNOAQ/tMVFNq7fL81GivoQ9F1U0Qr+DH3ZfaH8eIkX +xT0ToMPJUzWAn8pZv0snA0um6SIgvkCuxO84OkANCVbttzXImIsL7pFzfcwV/ERK +UM6j0ZuSMFOCr/lGPAoOQU0fskidGEHi1/kW+suSr28TqsyYZpwBDQ== +-----END RSA PRIVATE KEY----- diff --git a/demos/bio/shared.opt b/demos/bio/shared.opt new file mode 100644 index 000000000..4141b93a5 --- /dev/null +++ b/demos/bio/shared.opt @@ -0,0 +1,2 @@ +OSSL$LIBSSL_SHR/SHARE +OSSL$LIBCRYPTO_SHR/SHARE diff --git a/demos/bio/static.opt b/demos/bio/static.opt new file mode 100644 index 000000000..9ca1588f7 --- /dev/null +++ b/demos/bio/static.opt @@ -0,0 +1,2 @@ +OSSL$LIBSSL/LIB +OSSL$LIBCRYPTO/LIB diff --git a/demos/certs/README b/demos/certs/README new file mode 100644 index 000000000..126663a1d --- /dev/null +++ b/demos/certs/README @@ -0,0 +1,21 @@ +There is often a need to generate test certificates automatically using +a script. This is often a cause for confusion which can result in incorrect +CA certificates, obsolete V1 certificates or duplicate serial numbers. +The range of command line options can be daunting for a beginner. + +The mkcerts.sh script is an example of how to generate certificates +automatically using scripts. Example creates a root CA, an intermediate CA +signed by the root and several certificates signed by the intermediate CA. + +The script then creates an empty index.txt file and adds entries for the +certificates and generates a CRL. Then one certificate is revoked and a +second CRL generated. + +The script ocsprun.sh runs the test responder on port 8888 covering the +client certificates. + +The script ocspquery.sh queries the status of the certificates using the +test responder. + + + diff --git a/demos/certs/apps/apps.cnf b/demos/certs/apps/apps.cnf new file mode 100644 index 000000000..531afe64b --- /dev/null +++ b/demos/certs/apps/apps.cnf @@ -0,0 +1,69 @@ +# +# OpenSSL configuration file to create apps directory certificates +# + +# This definition stops the following lines choking if HOME or CN +# is undefined. +HOME = . +RANDFILE = $ENV::HOME/.rnd +CN = "Not Defined" + +#################################################################### +[ req ] +default_bits = 2048 +default_keyfile = privkey.pem +# Don't prompt for fields: use those in section directly +prompt = no +distinguished_name = req_distinguished_name +x509_extensions = v3_ca # The extensions to add to the self signed cert +string_mask = utf8only + +# req_extensions = v3_req # The extensions to add to a certificate request + +[ req_distinguished_name ] +countryName = UK + +organizationName = OpenSSL Group +organizationalUnitName = FOR TESTING PURPOSES ONLY +# Take CN from environment so it can come from a script. +commonName = $ENV::CN + +[ usr_cert ] + +# These extensions are added when 'ca' signs a request for an end entity +# certificate + +basicConstraints=critical, CA:FALSE +keyUsage=critical, nonRepudiation, digitalSignature, keyEncipherment + +# This will be displayed in Netscape's comment listbox. +nsComment = "OpenSSL Generated Certificate" + +[ ec_cert ] + +# These extensions are added when 'ca' signs a request for an end entity +# certificate + +basicConstraints=critical, CA:FALSE +keyUsage=critical, nonRepudiation, digitalSignature, keyAgreement + +# This will be displayed in Netscape's comment listbox. +nsComment = "OpenSSL Generated Certificate" + +# PKIX recommendations harmless if included in all certificates. +subjectKeyIdentifier=hash +authorityKeyIdentifier=keyid + +[ v3_ca ] + + +# Extensions for a typical CA + +# PKIX recommendation. + +subjectKeyIdentifier=hash +authorityKeyIdentifier=keyid:always +basicConstraints = critical,CA:true +keyUsage = critical, cRLSign, keyCertSign + + diff --git a/demos/certs/apps/ckey.pem b/demos/certs/apps/ckey.pem new file mode 100644 index 000000000..8e9054d29 --- /dev/null +++ b/demos/certs/apps/ckey.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpQIBAAKCAQEAtK2p2x0S3C1ajftAc3GaWPsji6scw1k9Sw/XltbLQuDc11/f +wwrUiFcje2CB3Ri6yD6+uCA3V12jEc4GdqzirJZhwgIhaTv42vfYBgiUcR9McEGr +agFC3yVR3lIbOzhBjmXNp1on46irxnzU4pT+w58IuvYqUBavaEtfRZocFR5NsIOy +mRhyNag8htOFK3wmTEYrb0vflFYT6SD47ogYtsd/xWSKS+YFyb7xSusR2Ot6Ktmr +MswQE57QYJz+KiRVlnL0cduMBdT52Wm8blaC9mz50PyrzjQ68NyHapCoWDU7pe4x +HLtzpXGSDMPuw4miiSwMym/2wReYJv6cFugLPQIDAQABAoIBAAZOyc9MhIwLSU4L +p4RgQvM4UVVe8/Id+3XTZ8NsXExJbWxXfIhiqGjaIfL8u4vsgRjcl+v1s/jo2/iT +KMab4o4D8gXD7UavQVDjtjb/ta79WL3SjRl2Uc9YjjMkyq6WmDNQeo2NKDdafCTB +1uzSJtLNipB8Z53ELPuHJhxX9QMHrMnuha49riQgXZ7buP9iQrHJFhImBjSzbxJx +L+TI6rkyLSf9Wi0Pd3L27Ob3QWNfNRYNSeTE+08eSRChkur5W0RuXAcuAICdQlCl +LBvWO/LmmvbzCqiDcgy/TliSb6CGGwgiNG7LJZmlkYNj8laGwalNlYZs3UrVv6NO +Br2loAECgYEA2kvCvPGj0Dg/6g7WhXDvAkEbcaL1tSeCxBbNH+6HS2UWMWvyTtCn +/bbD519QIdkvayy1QjEf32GV/UjUVmlULMLBcDy0DGjtL3+XpIhLKWDNxN1v1/ai +1oz23ZJCOgnk6K4qtFtlRS1XtynjA+rBetvYvLP9SKeFrnpzCgaA2r0CgYEA0+KX +1ACXDTNH5ySX3kMjSS9xdINf+OOw4CvPHFwbtc9aqk2HePlEsBTz5I/W3rKwXva3 +NqZ/bRqVVeZB/hHKFywgdUQk2Uc5z/S7Lw70/w1HubNTXGU06Ngb6zOFAo/o/TwZ +zTP1BMIKSOB6PAZPS3l+aLO4FRIRotfFhgRHOoECgYEAmiZbqt8cJaJDB/5YYDzC +mp3tSk6gIb936Q6M5VqkMYp9pIKsxhk0N8aDCnTU+kIK6SzWBpr3/d9Ecmqmfyq7 +5SvWO3KyVf0WWK9KH0abhOm2BKm2HBQvI0DB5u8sUx2/hsvOnjPYDISbZ11t0MtK +u35Zy89yMYcSsIYJjG/ROCUCgYEAgI2P9G5PNxEP5OtMwOsW84Y3Xat/hPAQFlI+ +HES+AzbFGWJkeT8zL2nm95tVkFP1sggZ7Kxjz3w7cpx7GX0NkbWSE9O+T51pNASV +tN1sQ3p5M+/a+cnlqgfEGJVvc7iAcXQPa3LEi5h2yPR49QYXAgG6cifn3dDSpmwn +SUI7PQECgYEApGCIIpSRPLAEHTGmP87RBL1smurhwmy2s/pghkvUkWehtxg0sGHh +kuaqDWcskogv+QC0sVdytiLSz8G0DwcEcsHK1Fkyb8A+ayiw6jWJDo2m9+IF4Fww +1Te6jFPYDESnbhq7+TLGgHGhtwcu5cnb4vSuYXGXKupZGzoLOBbv1Zw= +-----END RSA PRIVATE KEY----- diff --git a/demos/certs/apps/intkey.pem b/demos/certs/apps/intkey.pem new file mode 100644 index 000000000..d586cb7c6 --- /dev/null +++ b/demos/certs/apps/intkey.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEAsErw75CmLYD6pkrGW/YhAl/K8L5wJYxDjqu2FghxjD8K308W +3EHq4uBxEwR1OHXaM1+6ZZw7/r2I37VLIdurBEAIEUdbzx0so74FPawgz5EW2CTq +oJnK8F71/vo5Kj1VPwW46CxwxUR3cfvJGNXND2ip0TcyTSPLROXOyQakcVfIGJmd +Sa1wHKi+c2gMA4emADudZUOYLrg80gr2ldePm07ynbVsKKzCcStw8MdmoW9Qt3fL +nPJn2TFUUBNWj+4kvL+88edWCVQXKNdsysD/CDrH4W/hjyPDStVsM6XpiNU0+L2Z +Y6fcj3OP8d0goOx45xotMn9m8hNkCGsrVXx9IwIDAQABAoIBACg3wIV2o2KIJSZg +sqXyHY+0GNEZMO5v9E2NAMo//N941lshaN6wrww5FbK39qH9yNylfxmFLe6sgJhA +fLZprbcXgH+onto+Fpv4UqvCI+4WdHa03U3sJ+70SvxzSy1Gtrbc8FUPJl7qgrFf +Nn5S8CgOwYb4J6KPguTh5G3Z9RPiCKObwOwEM34hrZUlgPS88wmzu9H6L2GM8A1v +YBtEr0msBnlJBJOgStyUEfHW2KspNQ+VllQ6c0cedgFXUpl9EoKTLxP+WXwFI1sx +jFCFzSrMqPcPz1PxU6bXoZE0WH6r+3c8WAW4xR/HVu04BrBDu0CGwn6zAXDy6wCU +pWogDlkCgYEA4o+nIu2CTzqUlgc22pj+hjenfS5lnCtJfAdrXOJHmnuL+J9h8Nzz +9kkL+/Y0Xg9bOM6xXPm+81UNpDvOLbUahSSQsfB+LNVEkthJIL4XIk083LsHjFaJ +9SiCFRbf2OgWrEhe/c1drySwz9u/0f4Q7B6VGqxMnTDjzS5JacZ1pE8CgYEAxzMn +/n/Dpdn+c4rf14BRNKCv1qBXngPNylKJCmiRpKRJAn+B+Msdwtggk/1Ihju21wSo +IGy0Gw7WQd1Iq7V85cB2G5PAFY6ybpSV6G3QrzmzuvjHmKvXgUAuuaN+7Pp1YkMY +rLVjUOcdP5JbXG6XnaCkHYJR8uapPwWPkDt+oO0CgYBI4yZGGlr92j7LNW70TJw1 +2dnMcAzIfTSa7lgf/bxDetPBHKWJs8vYxA9S9BZM3Gvgjr6IxuAjsI0+9O6TzdvG +UckrNc+h5Mq241ZDbmRK6MZXzOPUxlKDyJBw8Hb7dU82BeJpjJRDMG6hsHS5vh77 +l6sodZ4ARCZFcEq1+N8ICQKBgDeBHJLAXO6YmFrvhkGQ4o+senJuSRuhabUHXGIH +ExXyJNnKV5fQWOGSwTkbKRsmBmNRS9uFDoY/kxnVI8ucjUmjYAV9HNek5DkFs+OI +vc4lYNwnN85li23bSWm2kcZMX2ra0URGYn8HdtHg4Q4XTq3ANhp21oi9FsmVrhP9 +T+JdAoGBAK2ebwZ7CXFavDFo4mzLKkGitBjrSi/udFhZECXZWEbNzWlVc3Y3q0cU +drDqUtbVm+/Xb5CMU044Gqq6SKdObAb3JElKmFylFL9fp2rfL/foUr2sdb87Vqdp +2j5jZyvt1DKnNaJ7JaFbUdRxlvHQRiqKlZpafN/SMQ0jCs1bSgCg +-----END RSA PRIVATE KEY----- diff --git a/demos/certs/apps/mkacerts.sh b/demos/certs/apps/mkacerts.sh new file mode 100644 index 000000000..70984969f --- /dev/null +++ b/demos/certs/apps/mkacerts.sh @@ -0,0 +1,45 @@ +#!/bin/sh + +# Recreate the demo certificates in the apps directory. + +OPENSSL=openssl + +# Root CA: create certificate directly +CN="OpenSSL Test Root CA" $OPENSSL req -config apps.cnf -x509 -nodes \ + -keyout root.pem -out root.pem -key rootkey.pem -new -days 3650 +# Intermediate CA: request first +CN="OpenSSL Test Intermediate CA" $OPENSSL req -config apps.cnf -nodes \ + -key intkey.pem -out intreq.pem -new +# Sign request: CA extensions +$OPENSSL x509 -req -in intreq.pem -CA root.pem -CAkey rootkey.pem -days 3630 \ + -extfile apps.cnf -extensions v3_ca -CAcreateserial -out intca.pem +# Client certificate: request first +CN="Test Client Cert" $OPENSSL req -config apps.cnf -nodes \ + -key ckey.pem -out creq.pem -new +# Sign using intermediate CA +$OPENSSL x509 -req -in creq.pem -CA intca.pem -CAkey intkey.pem -days 3600 \ + -extfile apps.cnf -extensions usr_cert -CAcreateserial | \ + $OPENSSL x509 -nameopt oneline -subject -issuer >client.pem +# Server certificate: request first +CN="Test Server Cert" $OPENSSL req -config apps.cnf -nodes \ + -key skey.pem -out sreq.pem -new +# Sign using intermediate CA +$OPENSSL x509 -req -in sreq.pem -CA intca.pem -CAkey intkey.pem -days 3600 \ + -extfile apps.cnf -extensions usr_cert -CAcreateserial | \ + $OPENSSL x509 -nameopt oneline -subject -issuer >server.pem +# Server certificate #2: request first +CN="Test Server Cert #2" $OPENSSL req -config apps.cnf -nodes \ + -key skey2.pem -out sreq2.pem -new +# Sign using intermediate CA +$OPENSSL x509 -req -in sreq2.pem -CA intca.pem -CAkey intkey.pem -days 3600 \ + -extfile apps.cnf -extensions usr_cert -CAcreateserial | \ + $OPENSSL x509 -nameopt oneline -subject -issuer >server2.pem + +# Append keys to file. + +cat skey.pem >>server.pem +cat skey2.pem >>server2.pem +cat ckey.pem >>client.pem + +$OPENSSL verify -CAfile root.pem -untrusted intca.pem \ + server2.pem server.pem client.pem diff --git a/demos/certs/apps/mkxcerts.sh b/demos/certs/apps/mkxcerts.sh new file mode 100644 index 000000000..0f88a48fb --- /dev/null +++ b/demos/certs/apps/mkxcerts.sh @@ -0,0 +1,29 @@ + +# Create certificates using various algorithms to test multi-certificate +# functionality. + +OPENSSL=../../../apps/openssl +CN="OpenSSL Test RSA SHA-1 cert" $OPENSSL req \ + -config apps.cnf -extensions usr_cert -x509 -nodes \ + -keyout tsha1.pem -out tsha1.pem -new -days 3650 -sha1 +CN="OpenSSL Test RSA SHA-256 cert" $OPENSSL req \ + -config apps.cnf -extensions usr_cert -x509 -nodes \ + -keyout tsha256.pem -out tsha256.pem -new -days 3650 -sha256 +CN="OpenSSL Test RSA SHA-512 cert" $OPENSSL req \ + -config apps.cnf -extensions usr_cert -x509 -nodes \ + -keyout tsha512.pem -out tsha512.pem -new -days 3650 -sha512 + +# Create EC parameters + +$OPENSSL ecparam -name P-256 -out ecp256.pem +$OPENSSL ecparam -name P-384 -out ecp384.pem + +CN="OpenSSL Test P-256 SHA-256 cert" $OPENSSL req \ + -config apps.cnf -extensions ec_cert -x509 -nodes \ + -nodes -keyout tecp256.pem -out tecp256.pem -newkey ec:ecp256.pem \ + -days 3650 -sha256 + +CN="OpenSSL Test P-384 SHA-384 cert" $OPENSSL req \ + -config apps.cnf -extensions ec_cert -x509 -nodes \ + -nodes -keyout tecp384.pem -out tecp384.pem -newkey ec:ecp384.pem \ + -days 3650 -sha384 diff --git a/demos/certs/apps/rootkey.pem b/demos/certs/apps/rootkey.pem new file mode 100644 index 000000000..2600aab8e --- /dev/null +++ b/demos/certs/apps/rootkey.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpgIBAAKCAQEA0xpquKAoY6trkxz8uuE6RyYfMOy+Rgvt19pqG3x8sEpFNjDN +IG873HniShNaOrseRtrGRgCDcecNOQ0LLOQYo10zz0er8+0YgUWVx5Ag5q3cqN3T +kjDc+0sRZMONPoUwD0ySOT9dumbJypEjA0q2AgKgWwaO4ilzg/fWTEruLeuL4meX +K9WZFmMnGuF4kHSocZeKWs5UM86WIOf/+NdtzLNd6a5HwqAB5Azggiz2Ngck6Aet +Mi0inBr3A1MSn0oIaQ7rGvbQ2QrIOnpdHJ36GadQHuvvtZOm28o8UVONMMWoS1yJ +/1TaRWQJ+faZJE7yegJtUf75+5HwsxaUP32C3wIDAQABAoIBAQCEybEnwVamm0Vn +nGw9AT+vUYN9Ou3VEdviUzk7YOrt2Un/9GKTbGSzItf80H+JQfqhhywBDIGiPDxN +Dq9g5Xm6CP51/BdlsFYhuqukhDyt3d9XOXHEG4hlaarfP0KxeQXqGbhA2mMSxWVZ +TkI/59blHNHRcCagjIJlGJhsFRYNO1/ApfA5zN7fWCFvH1XWZhuvsPDgUXKm4BS0 +p3ol67MVJHRfYcLb/txBO5rBhSXinK0jEBiljRcE0rWzRycSedmDgG3SNV17wvA0 +UWgMNpPcJ1b7Satr0nM7A8+siV8FRcfvPqCuGPKCYTrNn71hGJEhKXKwlURj9+95 +O5yzRxjBAoGBAPtTRYN40/piRB0XLpi+zNh+4Ba4TGfXSymbaozgC/pI5wfgGXrz +IpT9ujjV42r8TABHvXa6uiGm0cbxcUgq2n6Y8rf6iHxmn23ezCEBUs7rd6jtt11b +m58T8o0XWyOgAovaH0UgzMtrlsZYR2fli5254oRkTWwaUTuO38z6CVddAoGBANcH +nvdu3RniIYStsr5/deu7l81ZQ9rSiR1m3H6Wy8ryMIfkYfa0WqXhwNHrLrhvhLIQ +7mGnJ+jAkJyVQULE6UdbmVW8tC58Dfrgz/1s7RMeUYPnOmRpx79c/LqZ2IunfFWx +IvBvFu7vidEHA+1tU2N+oXNsU+B9XpfsJ+/d2QtrAoGBAJTuP58tFtClMp/agO5b +AqC4bqqIBB704cGCK53XlsF2OhHcprzJH5ES2iub8+wOHit8V7Xn6SzP4jf2E58k +Zd3nXM3RVNgDKC6/fE+CrUOZHYupcqOMCag29eDOGl/+DgQ5+ZXJXhKdaveWkJns +2NNat/SkS4zn+4NDozOgZ7CxAoGBAIuXjfJRTUXNUDci0APtGO9U1AJiLbOzs4Gb +0g539IqmWS0O7S3L/YDsolFkXOsssjcq2KYabsUhpX+RQVGIJWzGoS9QlqQKssSo +Bz4c5Xbg2shHZtfi9+JaClNVJofazdOPcAAoDfpFFPHWnQ0YSOcxQLx+maEFok/7 +5h1IputLAoGBAKGBWDPwskgRRfCAIFpCJLOu/9D30M/akMtO0kJYQpBjOaKuigUy +ic7pthFVse/pMUljXHAd1hs2CTjMW1ukEusU3x1Ei6wvnHHqn0Hs+6D5NQFQkcMn +7rejJ+bpJPRAn40AAV5hGBYI12XycB8ZgyPC4hTUK6unGVK06DC4qvdv +-----END RSA PRIVATE KEY----- diff --git a/demos/certs/apps/skey.pem b/demos/certs/apps/skey.pem new file mode 100644 index 000000000..dbd403d19 --- /dev/null +++ b/demos/certs/apps/skey.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEA84TzkjbcskbKZnrlKcXzSSgi07n+4N7kOM7uIhzpkTuU0HIv +h4VZS2axxfV6hV3CD9MuKVg2zEhroqK1Js5n4ke230nSP/qiELfCl0R+hzRtbfKL +tFUr1iHeU0uQ6v3q+Tg1K/Tmmg72uxKrhyHDL7z0BriPjhAHJ5XlQsvR1RCMkqzu +D9wjSInJxpMMIgLndOclAKv4D1wQtYU7ZpTw+01XBlUhIiXb86qpYL9NqnnRq5JI +uhmOEuxo2ca63+xaHNhD/udSyc8C0Md/yX6wlONTRFgLLv0pdLUGm1xEjfsydaQ6 +qGd7hzIKUI3hohNKJa/mHLElv7SZolPTogK/EQIDAQABAoIBAADq9FwNtuE5IRQn +zGtO4q7Y5uCzZ8GDNYr9RKp+P2cbuWDbvVAecYq2NV9QoIiWJOAYZKklOvekIju3 +r0UZLA0PRiIrTg6NrESx3JrjWDK8QNlUO7CPTZ39/K+FrmMkV9lem9yxjJjyC34D +AQB+YRTx+l14HppjdxNwHjAVQpIx/uO2F5xAMuk32+3K+pq9CZUtrofe1q4Agj9R +5s8mSy9pbRo9kW9wl5xdEotz1LivFOEiqPUJTUq5J5PeMKao3vdK726XI4Z455Nm +W2/MA0YV0ug2FYinHcZdvKM6dimH8GLfa3X8xKRfzjGjTiMSwsdjgMa4awY3tEHH +674jhAECgYEA/zqMrc0zsbNk83sjgaYIug5kzEpN4ic020rSZsmQxSCerJTgNhmg +utKSCt0Re09Jt3LqG48msahX8ycqDsHNvlEGPQSbMu9IYeO3Wr3fAm75GEtFWePY +BhM73I7gkRt4s8bUiUepMG/wY45c5tRF23xi8foReHFFe9MDzh8fJFECgYEA9EFX +4qAik1pOJGNei9BMwmx0I0gfVEIgu0tzeVqT45vcxbxr7RkTEaDoAG6PlbWP6D9a +WQNLp4gsgRM90ZXOJ4up5DsAWDluvaF4/omabMA+MJJ5kGZ0gCj5rbZbKqUws7x8 +bp+6iBfUPJUbcqNqFmi/08Yt7vrDnMnyMw2A/sECgYEAiiuRMxnuzVm34hQcsbhH +6ymVqf7j0PW2qK0F4H1ocT9qhzWFd+RB3kHWrCjnqODQoI6GbGr/4JepHUpre1ex +4UEN5oSS3G0ru0rC3U4C59dZ5KwDHFm7ffZ1pr52ljfQDUsrjjIMRtuiwNK2OoRa +WSsqiaL+SDzSB+nBmpnAizECgYBdt/y6rerWUx4MhDwwtTnel7JwHyo2MDFS6/5g +n8qC2Lj6/fMDRE22w+CA2esp7EJNQJGv+b27iFpbJEDh+/Lf5YzIT4MwVskQ5bYB +JFcmRxUVmf4e09D7o705U/DjCgMH09iCsbLmqQ38ONIRSHZaJtMDtNTHD1yi+jF+ +OT43gQKBgQC/2OHZoko6iRlNOAQ/tMVFNq7fL81GivoQ9F1U0Qr+DH3ZfaH8eIkX +xT0ToMPJUzWAn8pZv0snA0um6SIgvkCuxO84OkANCVbttzXImIsL7pFzfcwV/ERK +UM6j0ZuSMFOCr/lGPAoOQU0fskidGEHi1/kW+suSr28TqsyYZpwBDQ== +-----END RSA PRIVATE KEY----- diff --git a/demos/certs/apps/skey2.pem b/demos/certs/apps/skey2.pem new file mode 100644 index 000000000..785382285 --- /dev/null +++ b/demos/certs/apps/skey2.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEA63Yu4/cnLRvi+BIwcoIz5hKmcziREG2tujKEBs4JVO3uV3+f +UW/4YFULigKImXu/0fKyuMyeFu4l3V8NC6gachvAeWhiniN9sPgPU3AQKaF1y9gq +2EBEI2cFCKS5WASItjZCY951ZKuXYJdYDgC4kPlvI4N5M4ORHPa4pqfa/dzfMLEi +92sLGn7q5mArzn+5Xh2jD9Vif8w0RlDRxv1rQ413PGVBtfuhF1PSXNhbPtjpn+33 +DdJdNACv8D4PDmjUtKyshqvSXSE/RURldW13v68efBWhOQiLXcAkmISbxfzveS1k +KMSV8nuWwhS5rw0xMlavRTEgqbX7Jm14xGRrFwIDAQABAoIBAHLsTPihIfLnYIE5 +x4GsQQ5zXeBw5ITDM37ktwHnQDC+rIzyUl1aLD1AZRBoKinXd4lOTqLZ4/NHKx4A +DYr58mZtWyUmqLOMmQVuHXTZBlp7XtYuXMMNovQwjQlp9LicBeoBU6gQ5PVMtubD +F4xGF89Sn0cTHW3iMkqTtQ5KcR1j57OcJO0FEb1vPvk2MXI5ZyAatUYE7YacbEzd +rg02uIwx3FqNSkuSI79uz4hMdV5TPtuhxx9nTwj9aLUhXFeZ0mn2PVgVzEnnMoJb ++znlsZDgzDlJqdaD744YGWh8Z3OEssB35KfzFcdOeO6yH8lmv2Zfznk7pNPT7LTb +Lae9VgkCgYEA92p1qnAB3NtJtNcaW53i0S5WJgS1hxWKvUDx3lTB9s8X9fHpqL1a +E94fDfWzp/hax6FefUKIvBOukPLQ6bYjTMiFoOHzVirghAIuIUoMI5VtLhwD1hKs +Lr7l/dptMgKb1nZHyXoKHRBthsy3K4+udsPi8TzMvYElgEqyQIe/Rk0CgYEA86GL +8HC6zLszzKERDPBxrboRmoFvVUCTQDhsfj1M8aR3nQ8V5LkdIJc7Wqm/Ggfk9QRf +rJ8M2WUMlU5CNnCn/KCrKzCNZIReze3fV+HnKdbcXGLvgbHPrhnz8yYehUFG+RGq +bVyDWRU94T38izy2s5qMYrMJWZEYyXncSPbfcPMCgYAtaXfxcZ+V5xYPQFARMtiX +5nZfggvDoJuXgx0h3tK/N2HBfcaSdzbaYLG4gTmZggc/jwnl2dl5E++9oSPhUdIG +3ONSFUbxsOsGr9PBvnKd8WZZyUCXAVRjPBzAzF+whzQNWCZy/5htnz9LN7YDI9s0 +5113Q96cheDZPFydZY0hHQKBgQDVbEhNukM5xCiNcu+f2SaMnLp9EjQ4h5g3IvaP +5B16daw/Dw8LzcohWboqIxeAsze0GD/D1ZUJAEd0qBjC3g+a9BjefervCjKOzXng +38mEUm+6EwVjJSQcjSmycEs+Sr/kwr/8i5WYvU32+jk4tFgMoC+o6tQe/Uesf68k +z/dPVwKBgGbF7Vv1/3SmhlOy+zYyvJ0CrWtKxH9QP6tLIEgEpd8x7YTSuCH94yok +kToMXYA3sWNPt22GbRDZ+rcp4c7HkDx6I6vpdP9aQEwJTp0EPy0sgWr2XwYmreIQ +NFmkk8Itn9EY2R9VBaP7GLv5kvwxDdLAnmwGmzVtbmaVdxCaBwUk +-----END RSA PRIVATE KEY----- diff --git a/demos/certs/ca.cnf b/demos/certs/ca.cnf new file mode 100644 index 000000000..5a8a5f29e --- /dev/null +++ b/demos/certs/ca.cnf @@ -0,0 +1,86 @@ +# +# OpenSSL example configuration file for automated certificate creation. +# + +# This definition stops the following lines choking if HOME or CN +# is undefined. +HOME = . +RANDFILE = $ENV::HOME/.rnd +CN = "Not Defined" +default_ca = ca + +#################################################################### +[ req ] +default_bits = 1024 +default_keyfile = privkey.pem +# Don't prompt for fields: use those in section directly +prompt = no +distinguished_name = req_distinguished_name +x509_extensions = v3_ca # The extensions to add to the self signed cert +string_mask = utf8only + +# req_extensions = v3_req # The extensions to add to a certificate request + +[ req_distinguished_name ] +countryName = UK + +organizationName = OpenSSL Group +# Take CN from environment so it can come from a script. +commonName = $ENV::CN + +[ usr_cert ] + +# These extensions are added when 'ca' signs a request for an end entity +# certificate + +basicConstraints=critical, CA:FALSE +keyUsage=critical, nonRepudiation, digitalSignature, keyEncipherment + +# This will be displayed in Netscape's comment listbox. +nsComment = "OpenSSL Generated Certificate" + +# PKIX recommendations harmless if included in all certificates. +subjectKeyIdentifier=hash +authorityKeyIdentifier=keyid +# OCSP responder certificate +[ ocsp_cert ] + +basicConstraints=critical, CA:FALSE +keyUsage=critical, nonRepudiation, digitalSignature, keyEncipherment + +# This will be displayed in Netscape's comment listbox. +nsComment = "OpenSSL Generated Certificate" + +# PKIX recommendations harmless if included in all certificates. +subjectKeyIdentifier=hash +authorityKeyIdentifier=keyid +extendedKeyUsage=OCSPSigning + +[ dh_cert ] + +# These extensions are added when 'ca' signs a request for an end entity +# DH certificate + +basicConstraints=critical, CA:FALSE +keyUsage=critical, keyAgreement + +# PKIX recommendations harmless if included in all certificates. +subjectKeyIdentifier=hash +authorityKeyIdentifier=keyid + +[ v3_ca ] + + +# Extensions for a typical CA + +# PKIX recommendation. + +subjectKeyIdentifier=hash +authorityKeyIdentifier=keyid:always +basicConstraints = critical,CA:true +keyUsage = critical, cRLSign, keyCertSign + +# Minimal CA entry to allow generation of CRLs. +[ca] +database=index.txt +crlnumber=crlnum.txt diff --git a/demos/certs/mkcerts.sh b/demos/certs/mkcerts.sh new file mode 100644 index 000000000..18daa6bcf --- /dev/null +++ b/demos/certs/mkcerts.sh @@ -0,0 +1,96 @@ +#!/bin/sh + +OPENSSL=../../apps/openssl +OPENSSL_CONF=../../apps/openssl.cnf +export OPENSSL_CONF + +# Root CA: create certificate directly +CN="Test Root CA" $OPENSSL req -config ca.cnf -x509 -nodes \ + -keyout root.pem -out root.pem -newkey rsa:2048 -days 3650 +# Intermediate CA: request first +CN="Test Intermediate CA" $OPENSSL req -config ca.cnf -nodes \ + -keyout intkey.pem -out intreq.pem -newkey rsa:2048 +# Sign request: CA extensions +$OPENSSL x509 -req -in intreq.pem -CA root.pem -days 3600 \ + -extfile ca.cnf -extensions v3_ca -CAcreateserial -out intca.pem + +# Server certificate: create request first +CN="Test Server Cert" $OPENSSL req -config ca.cnf -nodes \ + -keyout skey.pem -out req.pem -newkey rsa:1024 +# Sign request: end entity extensions +$OPENSSL x509 -req -in req.pem -CA intca.pem -CAkey intkey.pem -days 3600 \ + -extfile ca.cnf -extensions usr_cert -CAcreateserial -out server.pem + +# Client certificate: request first +CN="Test Client Cert" $OPENSSL req -config ca.cnf -nodes \ + -keyout ckey.pem -out creq.pem -newkey rsa:1024 +# Sign using intermediate CA +$OPENSSL x509 -req -in creq.pem -CA intca.pem -CAkey intkey.pem -days 3600 \ + -extfile ca.cnf -extensions usr_cert -CAcreateserial -out client.pem + +# Revoked certificate: request first +CN="Test Revoked Cert" $OPENSSL req -config ca.cnf -nodes \ + -keyout revkey.pem -out rreq.pem -newkey rsa:1024 +# Sign using intermediate CA +$OPENSSL x509 -req -in rreq.pem -CA intca.pem -CAkey intkey.pem -days 3600 \ + -extfile ca.cnf -extensions usr_cert -CAcreateserial -out rev.pem + +# OCSP responder certificate: request first +CN="Test OCSP Responder Cert" $OPENSSL req -config ca.cnf -nodes \ + -keyout respkey.pem -out respreq.pem -newkey rsa:1024 +# Sign using intermediate CA and responder extensions +$OPENSSL x509 -req -in respreq.pem -CA intca.pem -CAkey intkey.pem -days 3600 \ + -extfile ca.cnf -extensions ocsp_cert -CAcreateserial -out resp.pem + +# Example creating a PKCS#3 DH certificate. + +# First DH parameters + +[ -f dhp.pem ] || $OPENSSL genpkey -genparam -algorithm DH -pkeyopt dh_paramgen_prime_len:1024 -out dhp.pem + +# Now a DH private key +$OPENSSL genpkey -paramfile dhp.pem -out dhskey.pem +# Create DH public key file +$OPENSSL pkey -in dhskey.pem -pubout -out dhspub.pem +# Certificate request, key just reuses old one as it is ignored when the +# request is signed. +CN="Test Server DH Cert" $OPENSSL req -config ca.cnf -new \ + -key skey.pem -out dhsreq.pem +# Sign request: end entity DH extensions +$OPENSSL x509 -req -in dhsreq.pem -CA root.pem -days 3600 \ + -force_pubkey dhspub.pem \ + -extfile ca.cnf -extensions dh_cert -CAcreateserial -out dhserver.pem + +# DH client certificate + +$OPENSSL genpkey -paramfile dhp.pem -out dhckey.pem +$OPENSSL pkey -in dhckey.pem -pubout -out dhcpub.pem +CN="Test Client DH Cert" $OPENSSL req -config ca.cnf -new \ + -key skey.pem -out dhcreq.pem +$OPENSSL x509 -req -in dhcreq.pem -CA root.pem -days 3600 \ + -force_pubkey dhcpub.pem \ + -extfile ca.cnf -extensions dh_cert -CAcreateserial -out dhclient.pem + +# Examples of CRL generation without the need to use 'ca' to issue +# certificates. +# Create zero length index file +>index.txt +# Create initial crl number file +echo 01 >crlnum.txt +# Add entries for server and client certs +$OPENSSL ca -valid server.pem -keyfile root.pem -cert root.pem \ + -config ca.cnf -md sha1 +$OPENSSL ca -valid client.pem -keyfile root.pem -cert root.pem \ + -config ca.cnf -md sha1 +$OPENSSL ca -valid rev.pem -keyfile root.pem -cert root.pem \ + -config ca.cnf -md sha1 +# Generate a CRL. +$OPENSSL ca -gencrl -keyfile root.pem -cert root.pem -config ca.cnf \ + -md sha1 -crldays 1 -out crl1.pem +# Revoke a certificate +openssl ca -revoke rev.pem -crl_reason superseded \ + -keyfile root.pem -cert root.pem -config ca.cnf -md sha1 +# Generate another CRL +$OPENSSL ca -gencrl -keyfile root.pem -cert root.pem -config ca.cnf \ + -md sha1 -crldays 1 -out crl2.pem + diff --git a/demos/certs/ocspquery.sh b/demos/certs/ocspquery.sh new file mode 100644 index 000000000..f66411330 --- /dev/null +++ b/demos/certs/ocspquery.sh @@ -0,0 +1,21 @@ +# Example querying OpenSSL test responder. Assumes ocsprun.sh has been +# called. + +OPENSSL=../../apps/openssl +OPENSSL_CONF=../../apps/openssl.cnf +export OPENSSL_CONF + +# Send responder queries for each certificate. + +echo "Requesting OCSP status for each certificate" +$OPENSSL ocsp -issuer intca.pem -cert client.pem -CAfile root.pem \ + -url http://127.0.0.1:8888/ +$OPENSSL ocsp -issuer intca.pem -cert server.pem -CAfile root.pem \ + -url http://127.0.0.1:8888/ +$OPENSSL ocsp -issuer intca.pem -cert rev.pem -CAfile root.pem \ + -url http://127.0.0.1:8888/ +# One query for all three certificates. +echo "Requesting OCSP status for three certificates in one request" +$OPENSSL ocsp -issuer intca.pem \ + -cert client.pem -cert server.pem -cert rev.pem \ + -CAfile root.pem -url http://127.0.0.1:8888/ diff --git a/demos/certs/ocsprun.sh b/demos/certs/ocsprun.sh new file mode 100644 index 000000000..a65e5f2fd --- /dev/null +++ b/demos/certs/ocsprun.sh @@ -0,0 +1,14 @@ +# Example of running an querying OpenSSL test OCSP responder. +# This assumes "mkcerts.sh" or similar has been run to set up the +# necessary file structure. + +OPENSSL=../../apps/openssl +OPENSSL_CONF=../../apps/openssl.cnf +export OPENSSL_CONF + +# Run OCSP responder. + +PORT=8888 + +$OPENSSL ocsp -port $PORT -index index.txt -CA intca.pem \ + -rsigner resp.pem -rkey respkey.pem -rother intca.pem $* diff --git a/demos/cms/cacert.pem b/demos/cms/cacert.pem new file mode 100644 index 000000000..75cbb347a --- /dev/null +++ b/demos/cms/cacert.pem @@ -0,0 +1,18 @@ +-----BEGIN CERTIFICATE----- +MIIC6DCCAlGgAwIBAgIJAMfGO3rdo2uUMA0GCSqGSIb3DQEBBAUAMFcxCzAJBgNV +BAYTAlVLMRIwEAYDVQQHEwlUZXN0IENpdHkxFjAUBgNVBAoTDU9wZW5TU0wgR3Jv +dXAxHDAaBgNVBAMTE1Rlc3QgUy9NSU1FIFJvb3QgQ0EwHhcNMDcwNDEzMTc0MzE3 +WhcNMTcwNDEwMTc0MzE3WjBXMQswCQYDVQQGEwJVSzESMBAGA1UEBxMJVGVzdCBD +aXR5MRYwFAYDVQQKEw1PcGVuU1NMIEdyb3VwMRwwGgYDVQQDExNUZXN0IFMvTUlN +RSBSb290IENBMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCqJMal1uC1/1wz +i5+dE4EZF2im3BgROm5PVMbwPY9V1t+KYvtdc3rMcRgJaMbP+qaEcDXoIsZfYXGR +ielgfDNZmZcj1y/FOum+Jc2OZMs3ggPmjIQ3dbBECq0hZKcbz7wfr+2OeNWm46iT +jcSIXpGIRhUYEzOgv7zb8oOU70IbbwIDAQABo4G7MIG4MB0GA1UdDgQWBBRHUypx +CXFQYqewhGo72lWPQUsjoDCBiAYDVR0jBIGAMH6AFEdTKnEJcVBip7CEajvaVY9B +SyOgoVukWTBXMQswCQYDVQQGEwJVSzESMBAGA1UEBxMJVGVzdCBDaXR5MRYwFAYD +VQQKEw1PcGVuU1NMIEdyb3VwMRwwGgYDVQQDExNUZXN0IFMvTUlNRSBSb290IENB +ggkAx8Y7et2ja5QwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQQFAAOBgQANI+Yc +G/YDM1WMUGEzEkU9UhsIUqdyBebnK3+OyxZSouDcE/M10jFJzBf/F5b0uUGAKWwo +u0dzmILfKjdfWe8EyCRafZcm00rVcO09i/63FBYzlHbmfUATIqZdhKzxxQMPs5mF +1je+pHUpzIY8TSXyh/uD9IkAy04IHwGZQf9akw== +-----END CERTIFICATE----- diff --git a/demos/cms/cakey.pem b/demos/cms/cakey.pem new file mode 100644 index 000000000..3b53c5e81 --- /dev/null +++ b/demos/cms/cakey.pem @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICXgIBAAKBgQCqJMal1uC1/1wzi5+dE4EZF2im3BgROm5PVMbwPY9V1t+KYvtd +c3rMcRgJaMbP+qaEcDXoIsZfYXGRielgfDNZmZcj1y/FOum+Jc2OZMs3ggPmjIQ3 +dbBECq0hZKcbz7wfr+2OeNWm46iTjcSIXpGIRhUYEzOgv7zb8oOU70IbbwIDAQAB +AoGBAKWOZ2UTc1BkjDjz0XoscmAR8Rj77MdGzfOPkIxPultSW+3yZpkGNyUbnsH5 +HAtf4Avai/m3bMN+s91kDpx9/g/I9ZEHPQLcDICETvwt/EHT7+hwvaQgsM+TgpMs +tjlGZOWent6wVIuvwwzqOMXZLgK9FvY7upwgtrys4G3Kab5hAkEA2QzFflWyEvKS +rMSaVtn/IjFilwa7H0IdakkjM34z4peerFTPBr4J47YD4RCR/dAvxyNy3zUxtH18 +9R6dUixI6QJBAMitJD0xOkbGWBX8KVJvRiKOIdf/95ZUAgN/h3bWKy57EB9NYj3u +jbxXcvdjfSqiITykkjAg7SG7nrlzJsu6CpcCQG6gVsy0auXDY0TRlASuaZ6I40Is +uRUOgqWYj2uAaHuWYdZeB4LdO3cnX0TISFDAWom6JKNlnmbrCtR4fSDT13kCQQCU ++VQJyV3F5MDHsWbLt6eNR46AV5lpk/vatPXPlrZ/zwPs+PmRmGLICvNiDA2DdNDP +wCx2Zjsj67CtY3rNitMJAkEAm09BQnjnbBXUb1rd2SjNDWTsu80Z+zLu8pAwXNhW +8nsvMYqlYMIxuMPwu/QuTnMRhMZ08uhqoD3ukZnBeoMEVg== +-----END RSA PRIVATE KEY----- diff --git a/demos/cms/cms_comp.c b/demos/cms/cms_comp.c new file mode 100644 index 000000000..0d548f93c --- /dev/null +++ b/demos/cms/cms_comp.c @@ -0,0 +1,64 @@ +/* + * Copyright 2008-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +/* Simple S/MIME compress example */ +#include +#include +#include + +int main(int argc, char **argv) +{ + BIO *in = NULL, *out = NULL; + CMS_ContentInfo *cms = NULL; + int ret = 1; + + /* + * On OpenSSL 1.0.0+ only: + * for streaming set CMS_STREAM + */ + int flags = CMS_STREAM; + + OpenSSL_add_all_algorithms(); + ERR_load_crypto_strings(); + + /* Open content being compressed */ + + in = BIO_new_file("comp.txt", "r"); + + if (!in) + goto err; + + /* compress content */ + cms = CMS_compress(in, NID_zlib_compression, flags); + + if (!cms) + goto err; + + out = BIO_new_file("smcomp.txt", "w"); + if (!out) + goto err; + + /* Write out S/MIME message */ + if (!SMIME_write_CMS(out, cms, in, flags)) + goto err; + + ret = 0; + + err: + + if (ret) { + fprintf(stderr, "Error Compressing Data\n"); + ERR_print_errors_fp(stderr); + } + + CMS_ContentInfo_free(cms); + BIO_free(in); + BIO_free(out); + return ret; +} diff --git a/demos/cms/cms_ddec.c b/demos/cms/cms_ddec.c new file mode 100644 index 000000000..8f2e9aecb --- /dev/null +++ b/demos/cms/cms_ddec.c @@ -0,0 +1,88 @@ +/* + * Copyright 2008-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +/* + * S/MIME detached data decrypt example: rarely done but should the need + * arise this is an example.... + */ +#include +#include +#include + +int main(int argc, char **argv) +{ + BIO *in = NULL, *out = NULL, *tbio = NULL, *dcont = NULL; + X509 *rcert = NULL; + EVP_PKEY *rkey = NULL; + CMS_ContentInfo *cms = NULL; + int ret = 1; + + OpenSSL_add_all_algorithms(); + ERR_load_crypto_strings(); + + /* Read in recipient certificate and private key */ + tbio = BIO_new_file("signer.pem", "r"); + + if (!tbio) + goto err; + + rcert = PEM_read_bio_X509(tbio, NULL, 0, NULL); + + BIO_reset(tbio); + + rkey = PEM_read_bio_PrivateKey(tbio, NULL, 0, NULL); + + if (!rcert || !rkey) + goto err; + + /* Open PEM file containing enveloped data */ + + in = BIO_new_file("smencr.pem", "r"); + + if (!in) + goto err; + + /* Parse PEM content */ + cms = PEM_read_bio_CMS(in, NULL, 0, NULL); + + if (!cms) + goto err; + + /* Open file containing detached content */ + dcont = BIO_new_file("smencr.out", "rb"); + + if (!in) + goto err; + + out = BIO_new_file("encrout.txt", "w"); + if (!out) + goto err; + + /* Decrypt S/MIME message */ + if (!CMS_decrypt(cms, rkey, rcert, dcont, out, 0)) + goto err; + + ret = 0; + + err: + + if (ret) { + fprintf(stderr, "Error Decrypting Data\n"); + ERR_print_errors_fp(stderr); + } + + CMS_ContentInfo_free(cms); + X509_free(rcert); + EVP_PKEY_free(rkey); + BIO_free(in); + BIO_free(out); + BIO_free(tbio); + BIO_free(dcont); + return ret; +} diff --git a/demos/cms/cms_dec.c b/demos/cms/cms_dec.c new file mode 100644 index 000000000..4f9428b4e --- /dev/null +++ b/demos/cms/cms_dec.c @@ -0,0 +1,78 @@ +/* + * Copyright 2008-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +/* Simple S/MIME decryption example */ +#include +#include +#include + +int main(int argc, char **argv) +{ + BIO *in = NULL, *out = NULL, *tbio = NULL; + X509 *rcert = NULL; + EVP_PKEY *rkey = NULL; + CMS_ContentInfo *cms = NULL; + int ret = 1; + + OpenSSL_add_all_algorithms(); + ERR_load_crypto_strings(); + + /* Read in recipient certificate and private key */ + tbio = BIO_new_file("signer.pem", "r"); + + if (!tbio) + goto err; + + rcert = PEM_read_bio_X509(tbio, NULL, 0, NULL); + + BIO_reset(tbio); + + rkey = PEM_read_bio_PrivateKey(tbio, NULL, 0, NULL); + + if (!rcert || !rkey) + goto err; + + /* Open S/MIME message to decrypt */ + + in = BIO_new_file("smencr.txt", "r"); + + if (!in) + goto err; + + /* Parse message */ + cms = SMIME_read_CMS(in, NULL); + + if (!cms) + goto err; + + out = BIO_new_file("decout.txt", "w"); + if (!out) + goto err; + + /* Decrypt S/MIME message */ + if (!CMS_decrypt(cms, rkey, rcert, NULL, out, 0)) + goto err; + + ret = 0; + + err: + + if (ret) { + fprintf(stderr, "Error Decrypting Data\n"); + ERR_print_errors_fp(stderr); + } + + CMS_ContentInfo_free(cms); + X509_free(rcert); + EVP_PKEY_free(rkey); + BIO_free(in); + BIO_free(out); + BIO_free(tbio); + return ret; +} diff --git a/demos/cms/cms_denc.c b/demos/cms/cms_denc.c new file mode 100644 index 000000000..adba69b96 --- /dev/null +++ b/demos/cms/cms_denc.c @@ -0,0 +1,97 @@ +/* + * Copyright 2008-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +/* + * S/MIME detached data encrypt example: rarely done but should the need + * arise this is an example.... + */ +#include +#include +#include + +int main(int argc, char **argv) +{ + BIO *in = NULL, *out = NULL, *tbio = NULL, *dout = NULL; + X509 *rcert = NULL; + STACK_OF(X509) *recips = NULL; + CMS_ContentInfo *cms = NULL; + int ret = 1; + + int flags = CMS_STREAM | CMS_DETACHED; + + OpenSSL_add_all_algorithms(); + ERR_load_crypto_strings(); + + /* Read in recipient certificate */ + tbio = BIO_new_file("signer.pem", "r"); + + if (!tbio) + goto err; + + rcert = PEM_read_bio_X509(tbio, NULL, 0, NULL); + + if (!rcert) + goto err; + + /* Create recipient STACK and add recipient cert to it */ + recips = sk_X509_new_null(); + + if (!recips || !sk_X509_push(recips, rcert)) + goto err; + + /* + * sk_X509_pop_free will free up recipient STACK and its contents so set + * rcert to NULL so it isn't freed up twice. + */ + rcert = NULL; + + /* Open content being encrypted */ + + in = BIO_new_file("encr.txt", "r"); + + dout = BIO_new_file("smencr.out", "wb"); + + if (!in) + goto err; + + /* encrypt content */ + cms = CMS_encrypt(recips, in, EVP_des_ede3_cbc(), flags); + + if (!cms) + goto err; + + out = BIO_new_file("smencr.pem", "w"); + if (!out) + goto err; + + if (!CMS_final(cms, in, dout, flags)) + goto err; + + /* Write out CMS structure without content */ + if (!PEM_write_bio_CMS(out, cms)) + goto err; + + ret = 0; + + err: + + if (ret) { + fprintf(stderr, "Error Encrypting Data\n"); + ERR_print_errors_fp(stderr); + } + + CMS_ContentInfo_free(cms); + X509_free(rcert); + sk_X509_pop_free(recips, X509_free); + BIO_free(in); + BIO_free(out); + BIO_free(dout); + BIO_free(tbio); + return ret; +} diff --git a/demos/cms/cms_enc.c b/demos/cms/cms_enc.c new file mode 100644 index 000000000..4d17d720c --- /dev/null +++ b/demos/cms/cms_enc.c @@ -0,0 +1,92 @@ +/* + * Copyright 2008-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +/* Simple S/MIME encrypt example */ +#include +#include +#include + +int main(int argc, char **argv) +{ + BIO *in = NULL, *out = NULL, *tbio = NULL; + X509 *rcert = NULL; + STACK_OF(X509) *recips = NULL; + CMS_ContentInfo *cms = NULL; + int ret = 1; + + /* + * On OpenSSL 1.0.0 and later only: + * for streaming set CMS_STREAM + */ + int flags = CMS_STREAM; + + OpenSSL_add_all_algorithms(); + ERR_load_crypto_strings(); + + /* Read in recipient certificate */ + tbio = BIO_new_file("signer.pem", "r"); + + if (!tbio) + goto err; + + rcert = PEM_read_bio_X509(tbio, NULL, 0, NULL); + + if (!rcert) + goto err; + + /* Create recipient STACK and add recipient cert to it */ + recips = sk_X509_new_null(); + + if (!recips || !sk_X509_push(recips, rcert)) + goto err; + + /* + * sk_X509_pop_free will free up recipient STACK and its contents so set + * rcert to NULL so it isn't freed up twice. + */ + rcert = NULL; + + /* Open content being encrypted */ + + in = BIO_new_file("encr.txt", "r"); + + if (!in) + goto err; + + /* encrypt content */ + cms = CMS_encrypt(recips, in, EVP_des_ede3_cbc(), flags); + + if (!cms) + goto err; + + out = BIO_new_file("smencr.txt", "w"); + if (!out) + goto err; + + /* Write out S/MIME message */ + if (!SMIME_write_CMS(out, cms, in, flags)) + goto err; + + ret = 0; + + err: + + if (ret) { + fprintf(stderr, "Error Encrypting Data\n"); + ERR_print_errors_fp(stderr); + } + + CMS_ContentInfo_free(cms); + X509_free(rcert); + sk_X509_pop_free(recips, X509_free); + BIO_free(in); + BIO_free(out); + BIO_free(tbio); + return ret; +} diff --git a/demos/cms/cms_sign.c b/demos/cms/cms_sign.c new file mode 100644 index 000000000..15bd5b8dd --- /dev/null +++ b/demos/cms/cms_sign.c @@ -0,0 +1,88 @@ +/* + * Copyright 2008-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +/* Simple S/MIME signing example */ +#include +#include +#include + +int main(int argc, char **argv) +{ + BIO *in = NULL, *out = NULL, *tbio = NULL; + X509 *scert = NULL; + EVP_PKEY *skey = NULL; + CMS_ContentInfo *cms = NULL; + int ret = 1; + + /* + * For simple S/MIME signing use CMS_DETACHED. On OpenSSL 1.0.0 only: for + * streaming detached set CMS_DETACHED|CMS_STREAM for streaming + * non-detached set CMS_STREAM + */ + int flags = CMS_DETACHED | CMS_STREAM; + + OpenSSL_add_all_algorithms(); + ERR_load_crypto_strings(); + + /* Read in signer certificate and private key */ + tbio = BIO_new_file("signer.pem", "r"); + + if (!tbio) + goto err; + + scert = PEM_read_bio_X509(tbio, NULL, 0, NULL); + + BIO_reset(tbio); + + skey = PEM_read_bio_PrivateKey(tbio, NULL, 0, NULL); + + if (!scert || !skey) + goto err; + + /* Open content being signed */ + + in = BIO_new_file("sign.txt", "r"); + + if (!in) + goto err; + + /* Sign content */ + cms = CMS_sign(scert, skey, NULL, in, flags); + + if (!cms) + goto err; + + out = BIO_new_file("smout.txt", "w"); + if (!out) + goto err; + + if (!(flags & CMS_STREAM)) + BIO_reset(in); + + /* Write out S/MIME message */ + if (!SMIME_write_CMS(out, cms, in, flags)) + goto err; + + ret = 0; + + err: + + if (ret) { + fprintf(stderr, "Error Signing Data\n"); + ERR_print_errors_fp(stderr); + } + + CMS_ContentInfo_free(cms); + X509_free(scert); + EVP_PKEY_free(skey); + BIO_free(in); + BIO_free(out); + BIO_free(tbio); + return ret; +} diff --git a/demos/cms/cms_sign2.c b/demos/cms/cms_sign2.c new file mode 100644 index 000000000..14ebf2777 --- /dev/null +++ b/demos/cms/cms_sign2.c @@ -0,0 +1,98 @@ +/* + * Copyright 2008-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +/* S/MIME signing example: 2 signers */ +#include +#include +#include + +int main(int argc, char **argv) +{ + BIO *in = NULL, *out = NULL, *tbio = NULL; + X509 *scert = NULL, *scert2 = NULL; + EVP_PKEY *skey = NULL, *skey2 = NULL; + CMS_ContentInfo *cms = NULL; + int ret = 1; + + OpenSSL_add_all_algorithms(); + ERR_load_crypto_strings(); + + tbio = BIO_new_file("signer.pem", "r"); + + if (!tbio) + goto err; + + scert = PEM_read_bio_X509(tbio, NULL, 0, NULL); + + BIO_reset(tbio); + + skey = PEM_read_bio_PrivateKey(tbio, NULL, 0, NULL); + + BIO_free(tbio); + + tbio = BIO_new_file("signer2.pem", "r"); + + if (!tbio) + goto err; + + scert2 = PEM_read_bio_X509(tbio, NULL, 0, NULL); + + BIO_reset(tbio); + + skey2 = PEM_read_bio_PrivateKey(tbio, NULL, 0, NULL); + + if (!scert2 || !skey2) + goto err; + + in = BIO_new_file("sign.txt", "r"); + + if (!in) + goto err; + + cms = CMS_sign(NULL, NULL, NULL, in, CMS_STREAM | CMS_PARTIAL); + + if (!cms) + goto err; + + /* Add each signer in turn */ + + if (!CMS_add1_signer(cms, scert, skey, NULL, 0)) + goto err; + + if (!CMS_add1_signer(cms, scert2, skey2, NULL, 0)) + goto err; + + out = BIO_new_file("smout.txt", "w"); + if (!out) + goto err; + + /* NB: content included and finalized by SMIME_write_CMS */ + + if (!SMIME_write_CMS(out, cms, in, CMS_STREAM)) + goto err; + + ret = 0; + + err: + + if (ret) { + fprintf(stderr, "Error Signing Data\n"); + ERR_print_errors_fp(stderr); + } + + CMS_ContentInfo_free(cms); + X509_free(scert); + EVP_PKEY_free(skey); + X509_free(scert2); + EVP_PKEY_free(skey2); + BIO_free(in); + BIO_free(out); + BIO_free(tbio); + return ret; +} diff --git a/demos/cms/cms_uncomp.c b/demos/cms/cms_uncomp.c new file mode 100644 index 000000000..3e3b4c4c6 --- /dev/null +++ b/demos/cms/cms_uncomp.c @@ -0,0 +1,58 @@ +/* + * Copyright 2008-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +/* Simple S/MIME uncompression example */ +#include +#include +#include + +int main(int argc, char **argv) +{ + BIO *in = NULL, *out = NULL; + CMS_ContentInfo *cms = NULL; + int ret = 1; + + OpenSSL_add_all_algorithms(); + ERR_load_crypto_strings(); + + /* Open compressed content */ + + in = BIO_new_file("smcomp.txt", "r"); + + if (!in) + goto err; + + /* Sign content */ + cms = SMIME_read_CMS(in, NULL); + + if (!cms) + goto err; + + out = BIO_new_file("smuncomp.txt", "w"); + if (!out) + goto err; + + /* Uncompress S/MIME message */ + if (!CMS_uncompress(cms, out, NULL, 0)) + goto err; + + ret = 0; + + err: + + if (ret) { + fprintf(stderr, "Error Uncompressing Data\n"); + ERR_print_errors_fp(stderr); + } + + CMS_ContentInfo_free(cms); + BIO_free(in); + BIO_free(out); + return ret; +} diff --git a/demos/cms/cms_ver.c b/demos/cms/cms_ver.c new file mode 100644 index 000000000..43c10e251 --- /dev/null +++ b/demos/cms/cms_ver.c @@ -0,0 +1,85 @@ +/* + * Copyright 2008-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +/* Simple S/MIME verification example */ +#include +#include +#include + +int main(int argc, char **argv) +{ + BIO *in = NULL, *out = NULL, *tbio = NULL, *cont = NULL; + X509_STORE *st = NULL; + X509 *cacert = NULL; + CMS_ContentInfo *cms = NULL; + + int ret = 1; + + OpenSSL_add_all_algorithms(); + ERR_load_crypto_strings(); + + /* Set up trusted CA certificate store */ + + st = X509_STORE_new(); + + /* Read in CA certificate */ + tbio = BIO_new_file("cacert.pem", "r"); + + if (!tbio) + goto err; + + cacert = PEM_read_bio_X509(tbio, NULL, 0, NULL); + + if (!cacert) + goto err; + + if (!X509_STORE_add_cert(st, cacert)) + goto err; + + /* Open message being verified */ + + in = BIO_new_file("smout.txt", "r"); + + if (!in) + goto err; + + /* parse message */ + cms = SMIME_read_CMS(in, &cont); + + if (!cms) + goto err; + + /* File to output verified content to */ + out = BIO_new_file("smver.txt", "w"); + if (!out) + goto err; + + if (!CMS_verify(cms, NULL, st, cont, out, 0)) { + fprintf(stderr, "Verification Failure\n"); + goto err; + } + + fprintf(stderr, "Verification Successful\n"); + + ret = 0; + + err: + + if (ret) { + fprintf(stderr, "Error Verifying Data\n"); + ERR_print_errors_fp(stderr); + } + + CMS_ContentInfo_free(cms); + X509_free(cacert); + BIO_free(in); + BIO_free(out); + BIO_free(tbio); + return ret; +} diff --git a/demos/cms/comp.txt b/demos/cms/comp.txt new file mode 100644 index 000000000..1672328e7 --- /dev/null +++ b/demos/cms/comp.txt @@ -0,0 +1,22 @@ +Content-type: text/plain + +Some Text To be Compressed +Some Text To be Compressed +Some Text To be Compressed +Some Text To be Compressed +Some Text To be Compressed +Some Text To be Compressed +Some Text To be Compressed +Some Text To be Compressed +Some Text To be Compressed +Some Text To be Compressed +Some Text To be Compressed +Some Text To be Compressed +Some Text To be Compressed +Some Text To be Compressed +Some Text To be Compressed +Some Text To be Compressed +Some Text To be Compressed +Some Text To be Compressed +Some Text To be Compressed +Some Text To be Compressed diff --git a/demos/cms/encr.txt b/demos/cms/encr.txt new file mode 100644 index 000000000..0eceb407b --- /dev/null +++ b/demos/cms/encr.txt @@ -0,0 +1,3 @@ +Content-type: text/plain + +Sample OpenSSL Data for CMS encryption diff --git a/demos/cms/sign.txt b/demos/cms/sign.txt new file mode 100644 index 000000000..c3f9d73d6 --- /dev/null +++ b/demos/cms/sign.txt @@ -0,0 +1,3 @@ +Content-type: text/plain + +Test OpenSSL CMS Signed Content diff --git a/demos/cms/signer.pem b/demos/cms/signer.pem new file mode 100644 index 000000000..bac16ba96 --- /dev/null +++ b/demos/cms/signer.pem @@ -0,0 +1,32 @@ +-----BEGIN CERTIFICATE----- +MIICpjCCAg+gAwIBAgIJAJ+rfmEoLQRhMA0GCSqGSIb3DQEBBAUAMFcxCzAJBgNV +BAYTAlVLMRIwEAYDVQQHEwlUZXN0IENpdHkxFjAUBgNVBAoTDU9wZW5TU0wgR3Jv +dXAxHDAaBgNVBAMTE1Rlc3QgUy9NSU1FIFJvb3QgQ0EwHhcNMDcwNDEzMTgyOTI3 +WhcNMTcwNDA5MTgyOTI3WjBWMQswCQYDVQQGEwJVSzElMCMGA1UEAxMcT3BlblNT +TCB0ZXN0IFMvTUlNRSBzaWduZXIgMTEgMB4GCSqGSIb3DQEJARYRdGVzdDFAb3Bl +bnNzbC5vcmcwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAL1ocAQ7ON2pIUXz +jwKPzpPB9ozB6PFG6F6kARO+i0DiT6Qn8abUjwpHPU+lGys83QlpbkQVUD6Fv/4L +ytihk6N9Pr/feECVcSZ20dI43WXjfYak14dSVrZkGNMMXqKmnnqtkAdD0oJN7A7y +gcf8RuViV0kvk9/36eCMwMHrImfhAgMBAAGjezB5MAkGA1UdEwQCMAAwLAYJYIZI +AYb4QgENBB8WHU9wZW5TU0wgR2VuZXJhdGVkIENlcnRpZmljYXRlMB0GA1UdDgQW +BBSyKqjvctIsFNBHULBTqr8SHtSxpDAfBgNVHSMEGDAWgBRHUypxCXFQYqewhGo7 +2lWPQUsjoDANBgkqhkiG9w0BAQQFAAOBgQBvdYVoBfd4RV/xWSMXIcgw/i5OiwyX +MsenQePll51MpglfArd7pUipUalCqlJt/Gs8kD16Ih1z1yuWYVTMlnDZ0PwbIOYn ++Jr8XLF9b1SMJt6PwckZZ0LZdIi2KwGAxVsIW1kjJAqu9o4YH37XW37yYdQRxfvv +lDiQlgX0JtmLgA== +-----END CERTIFICATE----- +-----BEGIN RSA PRIVATE KEY----- +MIICXAIBAAKBgQC9aHAEOzjdqSFF848Cj86TwfaMwejxRuhepAETvotA4k+kJ/Gm +1I8KRz1PpRsrPN0JaW5EFVA+hb/+C8rYoZOjfT6/33hAlXEmdtHSON1l432GpNeH +Ula2ZBjTDF6ipp56rZAHQ9KCTewO8oHH/EblYldJL5Pf9+ngjMDB6yJn4QIDAQAB +AoGACCuYIWaYll80UzslYRvo8lC8nOfEb5v6bBKxBTQD98GLY+5hKywiG3RlPalG +mb/fXQeSPReaRYgpdwD1OBEIOEMW9kLyqpzokC0xjpZ+MwsuJTlxCesk5GEsMa3o +wC3QMmiRA7qrZ/SzTtwrs++9mZ/pxp8JZ6pKYUj8SE7/vV0CQQDz8Ix2t40E16hx +04+XhClnGqydZJyLLSxcTU3ZVhYxL+efo/5hZ8tKpkcDi8wq6T03BOKrKxrlIW55 +qDRNM24rAkEAxsWzu/rJhIouQyNoYygEIEYzFRlTQyZSg59u6dNiewMn27dOAbyc +YT7B6da7e74QttTXo0lIllsX2S38+XsIIwJBANSRuIU3G66tkr5l4gnhhAaxqtuY +sgVhvvdL8dvC9aG1Ifzt9hzBSthpHxbK+oYmK07HdhI8hLpIMLHYzoK7n3MCQEy4 +4rccBcxyyYiAkjozp+QNNIpgTBMPJ6pGT7lRLiHtBeV4y1NASdv/LTnk+Fi69Bid +7t3H24ytfHcHmS1yn6ECQF6Jmh4C7dlvp59zXp+t+VsXxa/8sq41vKNIj0Rx9vh5 +xp9XL0C5ZpgmBnsTydP9pmkiL4ltLbMX0wJU6N2cmFw= +-----END RSA PRIVATE KEY----- diff --git a/demos/cms/signer2.pem b/demos/cms/signer2.pem new file mode 100644 index 000000000..25e23d131 --- /dev/null +++ b/demos/cms/signer2.pem @@ -0,0 +1,32 @@ +-----BEGIN CERTIFICATE----- +MIICpjCCAg+gAwIBAgIJAJ+rfmEoLQRiMA0GCSqGSIb3DQEBBAUAMFcxCzAJBgNV +BAYTAlVLMRIwEAYDVQQHEwlUZXN0IENpdHkxFjAUBgNVBAoTDU9wZW5TU0wgR3Jv +dXAxHDAaBgNVBAMTE1Rlc3QgUy9NSU1FIFJvb3QgQ0EwHhcNMDcwNDEzMTgyOTQ0 +WhcNMTcwNDA5MTgyOTQ0WjBWMQswCQYDVQQGEwJVSzElMCMGA1UEAxMcT3BlblNT +TCB0ZXN0IFMvTUlNRSBzaWduZXIgMjEgMB4GCSqGSIb3DQEJARYRdGVzdDJAb3Bl +bnNzbC5vcmcwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANco7VPgX9vcGwmZ +jYqjq1JiR7M38dsMNhuJyLRVjJ5/cpFluQydQuG1PhzOJ8zfYVFicOXKvbYuKuXW +ozZIwzqEqWsNf36KHTLS6yOMG8I13cRInh+fAIKq9Z8Eh65I7FJzVsNsfEQrGfEW +GMA8us24IaSvP3QkbfHJn/4RaKznAgMBAAGjezB5MAkGA1UdEwQCMAAwLAYJYIZI +AYb4QgENBB8WHU9wZW5TU0wgR2VuZXJhdGVkIENlcnRpZmljYXRlMB0GA1UdDgQW +BBRlrLQJUB8uAa4q8B2OqvvTXonF5zAfBgNVHSMEGDAWgBRHUypxCXFQYqewhGo7 +2lWPQUsjoDANBgkqhkiG9w0BAQQFAAOBgQBQbi2juGALg2k9m1hKpzR2lCGmGO3X +h3Jh/l0vIxDr0RTgP2vBrtITlx655P/o1snoeTIpYG8uUnFnTE/6YakdayAIlxV4 +aZl63AivZMpQB5SPaPH/jEsGJ8UQMfdiy4ORWIULupuPKlKwODNw7tVhQIACS/DR +2aX6rl2JEuJ5Yg== +-----END CERTIFICATE----- +-----BEGIN RSA PRIVATE KEY----- +MIICXAIBAAKBgQDXKO1T4F/b3BsJmY2Ko6tSYkezN/HbDDYbici0VYyef3KRZbkM +nULhtT4czifM32FRYnDlyr22Lirl1qM2SMM6hKlrDX9+ih0y0usjjBvCNd3ESJ4f +nwCCqvWfBIeuSOxSc1bDbHxEKxnxFhjAPLrNuCGkrz90JG3xyZ/+EWis5wIDAQAB +AoGAUTB2bcIrKfGimjrBOGGOUmYXnD8uGnQ/LqENhU8K4vxApTD3ZRUqmbUknQYF +6r8YH/e/llasw8QkF9qod+F5GTgsnyh/aMidFHKrXXbf1662scz9+S6crSXq9Eb2 +CL57f6Kw61k6edrz8zHdA+rnTK00hzgzKCP4ZL5k8/55ueECQQD+BK+nsKi6CcKf +m3Mh61Sf2Icm5JlMCKaihlbnh78lBN1imYUAfHJEnQ1ujxXB94R+6o9S+XrWTnTX +2m/JNIfpAkEA2NaidX7Sv5jnRPkwJ02Srl0urxINLmg4bU0zmM3VoMklYBHWnMyr +upPZGPh5TzCa+g6FTBmU8XK61wvnEKNcTwJBAM24VdnlBIDGbsx8RJ3vzLU30xz4 +ff5J80okqjUQhwkgC3tTAZgHMTPITZyAXQqdvrxakoCMc6MkHxTBX08AMCECQHHL +SdyxXrYv7waSY0PtANJCkpJLveEhzqMFxdMmCjtj9BpTojYNbv3uQxtIopj9YAdk +gW2ray++zvC2DV/86x8CQH4UJwgO6JqU4bSgi6HiRNjDg26tJ0Beu8jjl1vrkIVX +pHFwSUeLZUsT2/iTUSgYH4uYiZPgYNcKTCT9W6se30A= +-----END RSA PRIVATE KEY----- diff --git a/demos/evp/aesccm.c b/demos/evp/aesccm.c new file mode 100644 index 000000000..cc4d0b5ee --- /dev/null +++ b/demos/evp/aesccm.c @@ -0,0 +1,125 @@ +/* + * Copyright 2013-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +/* + * Simple AES CCM test program, uses the same NIST data used for the FIPS + * self test but uses the application level EVP APIs. + */ +#include +#include +#include + +/* AES-CCM test data from NIST public test vectors */ + +static const unsigned char ccm_key[] = { + 0xce, 0xb0, 0x09, 0xae, 0xa4, 0x45, 0x44, 0x51, 0xfe, 0xad, 0xf0, 0xe6, + 0xb3, 0x6f, 0x45, 0x55, 0x5d, 0xd0, 0x47, 0x23, 0xba, 0xa4, 0x48, 0xe8 +}; + +static const unsigned char ccm_nonce[] = { + 0x76, 0x40, 0x43, 0xc4, 0x94, 0x60, 0xb7 +}; + +static const unsigned char ccm_adata[] = { + 0x6e, 0x80, 0xdd, 0x7f, 0x1b, 0xad, 0xf3, 0xa1, 0xc9, 0xab, 0x25, 0xc7, + 0x5f, 0x10, 0xbd, 0xe7, 0x8c, 0x23, 0xfa, 0x0e, 0xb8, 0xf9, 0xaa, 0xa5, + 0x3a, 0xde, 0xfb, 0xf4, 0xcb, 0xf7, 0x8f, 0xe4 +}; + +static const unsigned char ccm_pt[] = { + 0xc8, 0xd2, 0x75, 0xf9, 0x19, 0xe1, 0x7d, 0x7f, 0xe6, 0x9c, 0x2a, 0x1f, + 0x58, 0x93, 0x9d, 0xfe, 0x4d, 0x40, 0x37, 0x91, 0xb5, 0xdf, 0x13, 0x10 +}; + +static const unsigned char ccm_ct[] = { + 0x8a, 0x0f, 0x3d, 0x82, 0x29, 0xe4, 0x8e, 0x74, 0x87, 0xfd, 0x95, 0xa2, + 0x8a, 0xd3, 0x92, 0xc8, 0x0b, 0x36, 0x81, 0xd4, 0xfb, 0xc7, 0xbb, 0xfd +}; + +static const unsigned char ccm_tag[] = { + 0x2d, 0xd6, 0xef, 0x1c, 0x45, 0xd4, 0xcc, 0xb7, 0x23, 0xdc, 0x07, 0x44, + 0x14, 0xdb, 0x50, 0x6d +}; + +void aes_ccm_encrypt(void) +{ + EVP_CIPHER_CTX *ctx; + int outlen, tmplen; + unsigned char outbuf[1024]; + printf("AES CCM Encrypt:\n"); + printf("Plaintext:\n"); + BIO_dump_fp(stdout, ccm_pt, sizeof(ccm_pt)); + ctx = EVP_CIPHER_CTX_new(); + /* Set cipher type and mode */ + EVP_EncryptInit_ex(ctx, EVP_aes_192_ccm(), NULL, NULL, NULL); + /* Set nonce length if default 96 bits is not appropriate */ + EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_IVLEN, sizeof(ccm_nonce), + NULL); + /* Set tag length */ + EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, sizeof(ccm_tag), NULL); + /* Initialise key and IV */ + EVP_EncryptInit_ex(ctx, NULL, NULL, ccm_key, ccm_nonce); + /* Set plaintext length: only needed if AAD is used */ + EVP_EncryptUpdate(ctx, NULL, &outlen, NULL, sizeof(ccm_pt)); + /* Zero or one call to specify any AAD */ + EVP_EncryptUpdate(ctx, NULL, &outlen, ccm_adata, sizeof(ccm_adata)); + /* Encrypt plaintext: can only be called once */ + EVP_EncryptUpdate(ctx, outbuf, &outlen, ccm_pt, sizeof(ccm_pt)); + /* Output encrypted block */ + printf("Ciphertext:\n"); + BIO_dump_fp(stdout, outbuf, outlen); + /* Finalise: note get no output for CCM */ + EVP_EncryptFinal_ex(ctx, outbuf, &outlen); + /* Get tag */ + EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_GET_TAG, 16, outbuf); + /* Output tag */ + printf("Tag:\n"); + BIO_dump_fp(stdout, outbuf, 16); + EVP_CIPHER_CTX_free(ctx); +} + +void aes_ccm_decrypt(void) +{ + EVP_CIPHER_CTX *ctx; + int outlen, tmplen, rv; + unsigned char outbuf[1024]; + printf("AES CCM Derypt:\n"); + printf("Ciphertext:\n"); + BIO_dump_fp(stdout, ccm_ct, sizeof(ccm_ct)); + ctx = EVP_CIPHER_CTX_new(); + /* Select cipher */ + EVP_DecryptInit_ex(ctx, EVP_aes_192_ccm(), NULL, NULL, NULL); + /* Set nonce length, omit for 96 bits */ + EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_IVLEN, sizeof(ccm_nonce), + NULL); + /* Set expected tag value */ + EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, + sizeof(ccm_tag), (void *)ccm_tag); + /* Specify key and IV */ + EVP_DecryptInit_ex(ctx, NULL, NULL, ccm_key, ccm_nonce); + /* Set ciphertext length: only needed if we have AAD */ + EVP_DecryptUpdate(ctx, NULL, &outlen, NULL, sizeof(ccm_ct)); + /* Zero or one call to specify any AAD */ + EVP_DecryptUpdate(ctx, NULL, &outlen, ccm_adata, sizeof(ccm_adata)); + /* Decrypt plaintext, verify tag: can only be called once */ + rv = EVP_DecryptUpdate(ctx, outbuf, &outlen, ccm_ct, sizeof(ccm_ct)); + /* Output decrypted block: if tag verify failed we get nothing */ + if (rv > 0) { + printf("Plaintext:\n"); + BIO_dump_fp(stdout, outbuf, outlen); + } else + printf("Plaintext not available: tag verify failed.\n"); + EVP_CIPHER_CTX_free(ctx); +} + +int main(int argc, char **argv) +{ + aes_ccm_encrypt(); + aes_ccm_decrypt(); +} diff --git a/demos/evp/aesgcm.c b/demos/evp/aesgcm.c new file mode 100644 index 000000000..17b0ef474 --- /dev/null +++ b/demos/evp/aesgcm.c @@ -0,0 +1,120 @@ +/* + * Copyright 2012-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +/* + * Simple AES GCM test program, uses the same NIST data used for the FIPS + * self test but uses the application level EVP APIs. + */ +#include +#include +#include + +/* AES-GCM test data from NIST public test vectors */ + +static const unsigned char gcm_key[] = { + 0xee, 0xbc, 0x1f, 0x57, 0x48, 0x7f, 0x51, 0x92, 0x1c, 0x04, 0x65, 0x66, + 0x5f, 0x8a, 0xe6, 0xd1, 0x65, 0x8b, 0xb2, 0x6d, 0xe6, 0xf8, 0xa0, 0x69, + 0xa3, 0x52, 0x02, 0x93, 0xa5, 0x72, 0x07, 0x8f +}; + +static const unsigned char gcm_iv[] = { + 0x99, 0xaa, 0x3e, 0x68, 0xed, 0x81, 0x73, 0xa0, 0xee, 0xd0, 0x66, 0x84 +}; + +static const unsigned char gcm_pt[] = { + 0xf5, 0x6e, 0x87, 0x05, 0x5b, 0xc3, 0x2d, 0x0e, 0xeb, 0x31, 0xb2, 0xea, + 0xcc, 0x2b, 0xf2, 0xa5 +}; + +static const unsigned char gcm_aad[] = { + 0x4d, 0x23, 0xc3, 0xce, 0xc3, 0x34, 0xb4, 0x9b, 0xdb, 0x37, 0x0c, 0x43, + 0x7f, 0xec, 0x78, 0xde +}; + +static const unsigned char gcm_ct[] = { + 0xf7, 0x26, 0x44, 0x13, 0xa8, 0x4c, 0x0e, 0x7c, 0xd5, 0x36, 0x86, 0x7e, + 0xb9, 0xf2, 0x17, 0x36 +}; + +static const unsigned char gcm_tag[] = { + 0x67, 0xba, 0x05, 0x10, 0x26, 0x2a, 0xe4, 0x87, 0xd7, 0x37, 0xee, 0x62, + 0x98, 0xf7, 0x7e, 0x0c +}; + +void aes_gcm_encrypt(void) +{ + EVP_CIPHER_CTX *ctx; + int outlen, tmplen; + unsigned char outbuf[1024]; + printf("AES GCM Encrypt:\n"); + printf("Plaintext:\n"); + BIO_dump_fp(stdout, gcm_pt, sizeof(gcm_pt)); + ctx = EVP_CIPHER_CTX_new(); + /* Set cipher type and mode */ + EVP_EncryptInit_ex(ctx, EVP_aes_256_gcm(), NULL, NULL, NULL); + /* Set IV length if default 96 bits is not appropriate */ + EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_IVLEN, sizeof(gcm_iv), NULL); + /* Initialise key and IV */ + EVP_EncryptInit_ex(ctx, NULL, NULL, gcm_key, gcm_iv); + /* Zero or more calls to specify any AAD */ + EVP_EncryptUpdate(ctx, NULL, &outlen, gcm_aad, sizeof(gcm_aad)); + /* Encrypt plaintext */ + EVP_EncryptUpdate(ctx, outbuf, &outlen, gcm_pt, sizeof(gcm_pt)); + /* Output encrypted block */ + printf("Ciphertext:\n"); + BIO_dump_fp(stdout, outbuf, outlen); + /* Finalise: note get no output for GCM */ + EVP_EncryptFinal_ex(ctx, outbuf, &outlen); + /* Get tag */ + EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_GET_TAG, 16, outbuf); + /* Output tag */ + printf("Tag:\n"); + BIO_dump_fp(stdout, outbuf, 16); + EVP_CIPHER_CTX_free(ctx); +} + +void aes_gcm_decrypt(void) +{ + EVP_CIPHER_CTX *ctx; + int outlen, tmplen, rv; + unsigned char outbuf[1024]; + printf("AES GCM Derypt:\n"); + printf("Ciphertext:\n"); + BIO_dump_fp(stdout, gcm_ct, sizeof(gcm_ct)); + ctx = EVP_CIPHER_CTX_new(); + /* Select cipher */ + EVP_DecryptInit_ex(ctx, EVP_aes_256_gcm(), NULL, NULL, NULL); + /* Set IV length, omit for 96 bits */ + EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_IVLEN, sizeof(gcm_iv), NULL); + /* Specify key and IV */ + EVP_DecryptInit_ex(ctx, NULL, NULL, gcm_key, gcm_iv); + /* Zero or more calls to specify any AAD */ + EVP_DecryptUpdate(ctx, NULL, &outlen, gcm_aad, sizeof(gcm_aad)); + /* Decrypt plaintext */ + EVP_DecryptUpdate(ctx, outbuf, &outlen, gcm_ct, sizeof(gcm_ct)); + /* Output decrypted block */ + printf("Plaintext:\n"); + BIO_dump_fp(stdout, outbuf, outlen); + /* Set expected tag value. */ + EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, sizeof(gcm_tag), gcm_tag); + /* Finalise: note get no output for GCM */ + rv = EVP_DecryptFinal_ex(ctx, outbuf, &outlen); + /* + * Print out return value. If this is not successful authentication + * failed and plaintext is not trustworthy. + */ + printf("Tag Verify %s\n", rv > 0 ? "Successful!" : "Failed!"); + EVP_CIPHER_CTX_free(ctx); +} + +int main(int argc, char **argv) +{ + aes_gcm_encrypt(); + aes_gcm_decrypt(); +} diff --git a/demos/pkcs12/README b/demos/pkcs12/README new file mode 100644 index 000000000..c87434b04 --- /dev/null +++ b/demos/pkcs12/README @@ -0,0 +1,3 @@ +PKCS#12 demo applications + +Written by Steve Henson. diff --git a/demos/pkcs12/pkread.c b/demos/pkcs12/pkread.c new file mode 100644 index 000000000..3b87d7a4a --- /dev/null +++ b/demos/pkcs12/pkread.c @@ -0,0 +1,68 @@ +/* + * Copyright 2000-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#include +#include +#include +#include + +/* Simple PKCS#12 file reader */ + +int main(int argc, char **argv) +{ + FILE *fp; + EVP_PKEY *pkey; + X509 *cert; + STACK_OF(X509) *ca = NULL; + PKCS12 *p12; + int i; + if (argc != 4) { + fprintf(stderr, "Usage: pkread p12file password opfile\n"); + exit(1); + } + OpenSSL_add_all_algorithms(); + ERR_load_crypto_strings(); + if ((fp = fopen(argv[1], "rb")) == NULL) { + fprintf(stderr, "Error opening file %s\n", argv[1]); + exit(1); + } + p12 = d2i_PKCS12_fp(fp, NULL); + fclose(fp); + if (!p12) { + fprintf(stderr, "Error reading PKCS#12 file\n"); + ERR_print_errors_fp(stderr); + exit(1); + } + if (!PKCS12_parse(p12, argv[2], &pkey, &cert, &ca)) { + fprintf(stderr, "Error parsing PKCS#12 file\n"); + ERR_print_errors_fp(stderr); + exit(1); + } + PKCS12_free(p12); + if ((fp = fopen(argv[3], "w")) == NULL) { + fprintf(stderr, "Error opening file %s\n", argv[1]); + exit(1); + } + if (pkey) { + fprintf(fp, "***Private Key***\n"); + PEM_write_PrivateKey(fp, pkey, NULL, NULL, 0, NULL, NULL); + } + if (cert) { + fprintf(fp, "***User Certificate***\n"); + PEM_write_X509_AUX(fp, cert); + } + if (ca && sk_X509_num(ca)) { + fprintf(fp, "***Other Certificates***\n"); + for (i = 0; i < sk_X509_num(ca); i++) + PEM_write_X509_AUX(fp, sk_X509_value(ca, i)); + } + fclose(fp); + return 0; +} diff --git a/demos/pkcs12/pkwrite.c b/demos/pkcs12/pkwrite.c new file mode 100644 index 000000000..e14cf8327 --- /dev/null +++ b/demos/pkcs12/pkwrite.c @@ -0,0 +1,53 @@ +/* + * Copyright 2000-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#include +#include +#include +#include + +/* Simple PKCS#12 file creator */ + +int main(int argc, char **argv) +{ + FILE *fp; + EVP_PKEY *pkey; + X509 *cert; + PKCS12 *p12; + if (argc != 5) { + fprintf(stderr, "Usage: pkwrite infile password name p12file\n"); + exit(1); + } + OpenSSL_add_all_algorithms(); + ERR_load_crypto_strings(); + if ((fp = fopen(argv[1], "r")) == NULL) { + fprintf(stderr, "Error opening file %s\n", argv[1]); + exit(1); + } + cert = PEM_read_X509(fp, NULL, NULL, NULL); + rewind(fp); + pkey = PEM_read_PrivateKey(fp, NULL, NULL, NULL); + fclose(fp); + p12 = PKCS12_create(argv[2], argv[3], pkey, cert, NULL, 0, 0, 0, 0, 0); + if (!p12) { + fprintf(stderr, "Error creating PKCS#12 structure\n"); + ERR_print_errors_fp(stderr); + exit(1); + } + if ((fp = fopen(argv[4], "wb")) == NULL) { + fprintf(stderr, "Error opening file %s\n", argv[1]); + ERR_print_errors_fp(stderr); + exit(1); + } + i2d_PKCS12_fp(fp, p12); + PKCS12_free(p12); + fclose(fp); + return 0; +} diff --git a/demos/smime/cacert.pem b/demos/smime/cacert.pem new file mode 100644 index 000000000..75cbb347a --- /dev/null +++ b/demos/smime/cacert.pem @@ -0,0 +1,18 @@ +-----BEGIN CERTIFICATE----- +MIIC6DCCAlGgAwIBAgIJAMfGO3rdo2uUMA0GCSqGSIb3DQEBBAUAMFcxCzAJBgNV +BAYTAlVLMRIwEAYDVQQHEwlUZXN0IENpdHkxFjAUBgNVBAoTDU9wZW5TU0wgR3Jv +dXAxHDAaBgNVBAMTE1Rlc3QgUy9NSU1FIFJvb3QgQ0EwHhcNMDcwNDEzMTc0MzE3 +WhcNMTcwNDEwMTc0MzE3WjBXMQswCQYDVQQGEwJVSzESMBAGA1UEBxMJVGVzdCBD +aXR5MRYwFAYDVQQKEw1PcGVuU1NMIEdyb3VwMRwwGgYDVQQDExNUZXN0IFMvTUlN +RSBSb290IENBMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCqJMal1uC1/1wz +i5+dE4EZF2im3BgROm5PVMbwPY9V1t+KYvtdc3rMcRgJaMbP+qaEcDXoIsZfYXGR +ielgfDNZmZcj1y/FOum+Jc2OZMs3ggPmjIQ3dbBECq0hZKcbz7wfr+2OeNWm46iT +jcSIXpGIRhUYEzOgv7zb8oOU70IbbwIDAQABo4G7MIG4MB0GA1UdDgQWBBRHUypx +CXFQYqewhGo72lWPQUsjoDCBiAYDVR0jBIGAMH6AFEdTKnEJcVBip7CEajvaVY9B +SyOgoVukWTBXMQswCQYDVQQGEwJVSzESMBAGA1UEBxMJVGVzdCBDaXR5MRYwFAYD +VQQKEw1PcGVuU1NMIEdyb3VwMRwwGgYDVQQDExNUZXN0IFMvTUlNRSBSb290IENB +ggkAx8Y7et2ja5QwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQQFAAOBgQANI+Yc +G/YDM1WMUGEzEkU9UhsIUqdyBebnK3+OyxZSouDcE/M10jFJzBf/F5b0uUGAKWwo +u0dzmILfKjdfWe8EyCRafZcm00rVcO09i/63FBYzlHbmfUATIqZdhKzxxQMPs5mF +1je+pHUpzIY8TSXyh/uD9IkAy04IHwGZQf9akw== +-----END CERTIFICATE----- diff --git a/demos/smime/cakey.pem b/demos/smime/cakey.pem new file mode 100644 index 000000000..3b53c5e81 --- /dev/null +++ b/demos/smime/cakey.pem @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICXgIBAAKBgQCqJMal1uC1/1wzi5+dE4EZF2im3BgROm5PVMbwPY9V1t+KYvtd +c3rMcRgJaMbP+qaEcDXoIsZfYXGRielgfDNZmZcj1y/FOum+Jc2OZMs3ggPmjIQ3 +dbBECq0hZKcbz7wfr+2OeNWm46iTjcSIXpGIRhUYEzOgv7zb8oOU70IbbwIDAQAB +AoGBAKWOZ2UTc1BkjDjz0XoscmAR8Rj77MdGzfOPkIxPultSW+3yZpkGNyUbnsH5 +HAtf4Avai/m3bMN+s91kDpx9/g/I9ZEHPQLcDICETvwt/EHT7+hwvaQgsM+TgpMs +tjlGZOWent6wVIuvwwzqOMXZLgK9FvY7upwgtrys4G3Kab5hAkEA2QzFflWyEvKS +rMSaVtn/IjFilwa7H0IdakkjM34z4peerFTPBr4J47YD4RCR/dAvxyNy3zUxtH18 +9R6dUixI6QJBAMitJD0xOkbGWBX8KVJvRiKOIdf/95ZUAgN/h3bWKy57EB9NYj3u +jbxXcvdjfSqiITykkjAg7SG7nrlzJsu6CpcCQG6gVsy0auXDY0TRlASuaZ6I40Is +uRUOgqWYj2uAaHuWYdZeB4LdO3cnX0TISFDAWom6JKNlnmbrCtR4fSDT13kCQQCU ++VQJyV3F5MDHsWbLt6eNR46AV5lpk/vatPXPlrZ/zwPs+PmRmGLICvNiDA2DdNDP +wCx2Zjsj67CtY3rNitMJAkEAm09BQnjnbBXUb1rd2SjNDWTsu80Z+zLu8pAwXNhW +8nsvMYqlYMIxuMPwu/QuTnMRhMZ08uhqoD3ukZnBeoMEVg== +-----END RSA PRIVATE KEY----- diff --git a/demos/smime/encr.txt b/demos/smime/encr.txt new file mode 100644 index 000000000..f163a326e --- /dev/null +++ b/demos/smime/encr.txt @@ -0,0 +1,3 @@ +Content-type: text/plain + +Sample OpenSSL Data for PKCS#7 encryption diff --git a/demos/smime/sign.txt b/demos/smime/sign.txt new file mode 100644 index 000000000..af1341d0a --- /dev/null +++ b/demos/smime/sign.txt @@ -0,0 +1,3 @@ +Content-type: text/plain + +Test OpenSSL Signed Content diff --git a/demos/smime/signer.pem b/demos/smime/signer.pem new file mode 100644 index 000000000..bac16ba96 --- /dev/null +++ b/demos/smime/signer.pem @@ -0,0 +1,32 @@ +-----BEGIN CERTIFICATE----- +MIICpjCCAg+gAwIBAgIJAJ+rfmEoLQRhMA0GCSqGSIb3DQEBBAUAMFcxCzAJBgNV +BAYTAlVLMRIwEAYDVQQHEwlUZXN0IENpdHkxFjAUBgNVBAoTDU9wZW5TU0wgR3Jv +dXAxHDAaBgNVBAMTE1Rlc3QgUy9NSU1FIFJvb3QgQ0EwHhcNMDcwNDEzMTgyOTI3 +WhcNMTcwNDA5MTgyOTI3WjBWMQswCQYDVQQGEwJVSzElMCMGA1UEAxMcT3BlblNT +TCB0ZXN0IFMvTUlNRSBzaWduZXIgMTEgMB4GCSqGSIb3DQEJARYRdGVzdDFAb3Bl +bnNzbC5vcmcwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAL1ocAQ7ON2pIUXz +jwKPzpPB9ozB6PFG6F6kARO+i0DiT6Qn8abUjwpHPU+lGys83QlpbkQVUD6Fv/4L +ytihk6N9Pr/feECVcSZ20dI43WXjfYak14dSVrZkGNMMXqKmnnqtkAdD0oJN7A7y +gcf8RuViV0kvk9/36eCMwMHrImfhAgMBAAGjezB5MAkGA1UdEwQCMAAwLAYJYIZI +AYb4QgENBB8WHU9wZW5TU0wgR2VuZXJhdGVkIENlcnRpZmljYXRlMB0GA1UdDgQW +BBSyKqjvctIsFNBHULBTqr8SHtSxpDAfBgNVHSMEGDAWgBRHUypxCXFQYqewhGo7 +2lWPQUsjoDANBgkqhkiG9w0BAQQFAAOBgQBvdYVoBfd4RV/xWSMXIcgw/i5OiwyX +MsenQePll51MpglfArd7pUipUalCqlJt/Gs8kD16Ih1z1yuWYVTMlnDZ0PwbIOYn ++Jr8XLF9b1SMJt6PwckZZ0LZdIi2KwGAxVsIW1kjJAqu9o4YH37XW37yYdQRxfvv +lDiQlgX0JtmLgA== +-----END CERTIFICATE----- +-----BEGIN RSA PRIVATE KEY----- +MIICXAIBAAKBgQC9aHAEOzjdqSFF848Cj86TwfaMwejxRuhepAETvotA4k+kJ/Gm +1I8KRz1PpRsrPN0JaW5EFVA+hb/+C8rYoZOjfT6/33hAlXEmdtHSON1l432GpNeH +Ula2ZBjTDF6ipp56rZAHQ9KCTewO8oHH/EblYldJL5Pf9+ngjMDB6yJn4QIDAQAB +AoGACCuYIWaYll80UzslYRvo8lC8nOfEb5v6bBKxBTQD98GLY+5hKywiG3RlPalG +mb/fXQeSPReaRYgpdwD1OBEIOEMW9kLyqpzokC0xjpZ+MwsuJTlxCesk5GEsMa3o +wC3QMmiRA7qrZ/SzTtwrs++9mZ/pxp8JZ6pKYUj8SE7/vV0CQQDz8Ix2t40E16hx +04+XhClnGqydZJyLLSxcTU3ZVhYxL+efo/5hZ8tKpkcDi8wq6T03BOKrKxrlIW55 +qDRNM24rAkEAxsWzu/rJhIouQyNoYygEIEYzFRlTQyZSg59u6dNiewMn27dOAbyc +YT7B6da7e74QttTXo0lIllsX2S38+XsIIwJBANSRuIU3G66tkr5l4gnhhAaxqtuY +sgVhvvdL8dvC9aG1Ifzt9hzBSthpHxbK+oYmK07HdhI8hLpIMLHYzoK7n3MCQEy4 +4rccBcxyyYiAkjozp+QNNIpgTBMPJ6pGT7lRLiHtBeV4y1NASdv/LTnk+Fi69Bid +7t3H24ytfHcHmS1yn6ECQF6Jmh4C7dlvp59zXp+t+VsXxa/8sq41vKNIj0Rx9vh5 +xp9XL0C5ZpgmBnsTydP9pmkiL4ltLbMX0wJU6N2cmFw= +-----END RSA PRIVATE KEY----- diff --git a/demos/smime/signer2.pem b/demos/smime/signer2.pem new file mode 100644 index 000000000..25e23d131 --- /dev/null +++ b/demos/smime/signer2.pem @@ -0,0 +1,32 @@ +-----BEGIN CERTIFICATE----- +MIICpjCCAg+gAwIBAgIJAJ+rfmEoLQRiMA0GCSqGSIb3DQEBBAUAMFcxCzAJBgNV +BAYTAlVLMRIwEAYDVQQHEwlUZXN0IENpdHkxFjAUBgNVBAoTDU9wZW5TU0wgR3Jv +dXAxHDAaBgNVBAMTE1Rlc3QgUy9NSU1FIFJvb3QgQ0EwHhcNMDcwNDEzMTgyOTQ0 +WhcNMTcwNDA5MTgyOTQ0WjBWMQswCQYDVQQGEwJVSzElMCMGA1UEAxMcT3BlblNT +TCB0ZXN0IFMvTUlNRSBzaWduZXIgMjEgMB4GCSqGSIb3DQEJARYRdGVzdDJAb3Bl +bnNzbC5vcmcwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANco7VPgX9vcGwmZ +jYqjq1JiR7M38dsMNhuJyLRVjJ5/cpFluQydQuG1PhzOJ8zfYVFicOXKvbYuKuXW +ozZIwzqEqWsNf36KHTLS6yOMG8I13cRInh+fAIKq9Z8Eh65I7FJzVsNsfEQrGfEW +GMA8us24IaSvP3QkbfHJn/4RaKznAgMBAAGjezB5MAkGA1UdEwQCMAAwLAYJYIZI +AYb4QgENBB8WHU9wZW5TU0wgR2VuZXJhdGVkIENlcnRpZmljYXRlMB0GA1UdDgQW +BBRlrLQJUB8uAa4q8B2OqvvTXonF5zAfBgNVHSMEGDAWgBRHUypxCXFQYqewhGo7 +2lWPQUsjoDANBgkqhkiG9w0BAQQFAAOBgQBQbi2juGALg2k9m1hKpzR2lCGmGO3X +h3Jh/l0vIxDr0RTgP2vBrtITlx655P/o1snoeTIpYG8uUnFnTE/6YakdayAIlxV4 +aZl63AivZMpQB5SPaPH/jEsGJ8UQMfdiy4ORWIULupuPKlKwODNw7tVhQIACS/DR +2aX6rl2JEuJ5Yg== +-----END CERTIFICATE----- +-----BEGIN RSA PRIVATE KEY----- +MIICXAIBAAKBgQDXKO1T4F/b3BsJmY2Ko6tSYkezN/HbDDYbici0VYyef3KRZbkM +nULhtT4czifM32FRYnDlyr22Lirl1qM2SMM6hKlrDX9+ih0y0usjjBvCNd3ESJ4f +nwCCqvWfBIeuSOxSc1bDbHxEKxnxFhjAPLrNuCGkrz90JG3xyZ/+EWis5wIDAQAB +AoGAUTB2bcIrKfGimjrBOGGOUmYXnD8uGnQ/LqENhU8K4vxApTD3ZRUqmbUknQYF +6r8YH/e/llasw8QkF9qod+F5GTgsnyh/aMidFHKrXXbf1662scz9+S6crSXq9Eb2 +CL57f6Kw61k6edrz8zHdA+rnTK00hzgzKCP4ZL5k8/55ueECQQD+BK+nsKi6CcKf +m3Mh61Sf2Icm5JlMCKaihlbnh78lBN1imYUAfHJEnQ1ujxXB94R+6o9S+XrWTnTX +2m/JNIfpAkEA2NaidX7Sv5jnRPkwJ02Srl0urxINLmg4bU0zmM3VoMklYBHWnMyr +upPZGPh5TzCa+g6FTBmU8XK61wvnEKNcTwJBAM24VdnlBIDGbsx8RJ3vzLU30xz4 +ff5J80okqjUQhwkgC3tTAZgHMTPITZyAXQqdvrxakoCMc6MkHxTBX08AMCECQHHL +SdyxXrYv7waSY0PtANJCkpJLveEhzqMFxdMmCjtj9BpTojYNbv3uQxtIopj9YAdk +gW2ray++zvC2DV/86x8CQH4UJwgO6JqU4bSgi6HiRNjDg26tJ0Beu8jjl1vrkIVX +pHFwSUeLZUsT2/iTUSgYH4uYiZPgYNcKTCT9W6se30A= +-----END RSA PRIVATE KEY----- diff --git a/demos/smime/smdec.c b/demos/smime/smdec.c new file mode 100644 index 000000000..c4d1b090d --- /dev/null +++ b/demos/smime/smdec.c @@ -0,0 +1,78 @@ +/* + * Copyright 2007-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +/* Simple S/MIME signing example */ +#include +#include +#include + +int main(int argc, char **argv) +{ + BIO *in = NULL, *out = NULL, *tbio = NULL; + X509 *rcert = NULL; + EVP_PKEY *rkey = NULL; + PKCS7 *p7 = NULL; + int ret = 1; + + OpenSSL_add_all_algorithms(); + ERR_load_crypto_strings(); + + /* Read in recipient certificate and private key */ + tbio = BIO_new_file("signer.pem", "r"); + + if (!tbio) + goto err; + + rcert = PEM_read_bio_X509(tbio, NULL, 0, NULL); + + BIO_reset(tbio); + + rkey = PEM_read_bio_PrivateKey(tbio, NULL, 0, NULL); + + if (!rcert || !rkey) + goto err; + + /* Open content being signed */ + + in = BIO_new_file("smencr.txt", "r"); + + if (!in) + goto err; + + /* Sign content */ + p7 = SMIME_read_PKCS7(in, NULL); + + if (!p7) + goto err; + + out = BIO_new_file("encrout.txt", "w"); + if (!out) + goto err; + + /* Decrypt S/MIME message */ + if (!PKCS7_decrypt(p7, rkey, rcert, out, 0)) + goto err; + + ret = 0; + + err: + if (ret) { + fprintf(stderr, "Error Signing Data\n"); + ERR_print_errors_fp(stderr); + } + PKCS7_free(p7); + X509_free(rcert); + EVP_PKEY_free(rkey); + BIO_free(in); + BIO_free(out); + BIO_free(tbio); + + return ret; + +} diff --git a/demos/smime/smenc.c b/demos/smime/smenc.c new file mode 100644 index 000000000..5d36e9a49 --- /dev/null +++ b/demos/smime/smenc.c @@ -0,0 +1,91 @@ +/* + * Copyright 2007-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +/* Simple S/MIME encrypt example */ +#include +#include +#include + +int main(int argc, char **argv) +{ + BIO *in = NULL, *out = NULL, *tbio = NULL; + X509 *rcert = NULL; + STACK_OF(X509) *recips = NULL; + PKCS7 *p7 = NULL; + int ret = 1; + + /* + * On OpenSSL 0.9.9 only: + * for streaming set PKCS7_STREAM + */ + int flags = PKCS7_STREAM; + + OpenSSL_add_all_algorithms(); + ERR_load_crypto_strings(); + + /* Read in recipient certificate */ + tbio = BIO_new_file("signer.pem", "r"); + + if (!tbio) + goto err; + + rcert = PEM_read_bio_X509(tbio, NULL, 0, NULL); + + if (!rcert) + goto err; + + /* Create recipient STACK and add recipient cert to it */ + recips = sk_X509_new_null(); + + if (!recips || !sk_X509_push(recips, rcert)) + goto err; + + /* + * sk_X509_pop_free will free up recipient STACK and its contents so set + * rcert to NULL so it isn't freed up twice. + */ + rcert = NULL; + + /* Open content being encrypted */ + + in = BIO_new_file("encr.txt", "r"); + + if (!in) + goto err; + + /* encrypt content */ + p7 = PKCS7_encrypt(recips, in, EVP_des_ede3_cbc(), flags); + + if (!p7) + goto err; + + out = BIO_new_file("smencr.txt", "w"); + if (!out) + goto err; + + /* Write out S/MIME message */ + if (!SMIME_write_PKCS7(out, p7, in, flags)) + goto err; + + ret = 0; + + err: + if (ret) { + fprintf(stderr, "Error Encrypting Data\n"); + ERR_print_errors_fp(stderr); + } + PKCS7_free(p7); + X509_free(rcert); + sk_X509_pop_free(recips, X509_free); + BIO_free(in); + BIO_free(out); + BIO_free(tbio); + return ret; + +} diff --git a/demos/smime/smsign.c b/demos/smime/smsign.c new file mode 100644 index 000000000..ba0adb397 --- /dev/null +++ b/demos/smime/smsign.c @@ -0,0 +1,88 @@ +/* + * Copyright 2007-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +/* Simple S/MIME signing example */ +#include +#include +#include + +int main(int argc, char **argv) +{ + BIO *in = NULL, *out = NULL, *tbio = NULL; + X509 *scert = NULL; + EVP_PKEY *skey = NULL; + PKCS7 *p7 = NULL; + int ret = 1; + + /* + * For simple S/MIME signing use PKCS7_DETACHED. On OpenSSL 0.9.9 only: + * for streaming detached set PKCS7_DETACHED|PKCS7_STREAM for streaming + * non-detached set PKCS7_STREAM + */ + int flags = PKCS7_DETACHED | PKCS7_STREAM; + + OpenSSL_add_all_algorithms(); + ERR_load_crypto_strings(); + + /* Read in signer certificate and private key */ + tbio = BIO_new_file("signer.pem", "r"); + + if (!tbio) + goto err; + + scert = PEM_read_bio_X509(tbio, NULL, 0, NULL); + + BIO_reset(tbio); + + skey = PEM_read_bio_PrivateKey(tbio, NULL, 0, NULL); + + if (!scert || !skey) + goto err; + + /* Open content being signed */ + + in = BIO_new_file("sign.txt", "r"); + + if (!in) + goto err; + + /* Sign content */ + p7 = PKCS7_sign(scert, skey, NULL, in, flags); + + if (!p7) + goto err; + + out = BIO_new_file("smout.txt", "w"); + if (!out) + goto err; + + if (!(flags & PKCS7_STREAM)) + BIO_reset(in); + + /* Write out S/MIME message */ + if (!SMIME_write_PKCS7(out, p7, in, flags)) + goto err; + + ret = 0; + + err: + if (ret) { + fprintf(stderr, "Error Signing Data\n"); + ERR_print_errors_fp(stderr); + } + PKCS7_free(p7); + X509_free(scert); + EVP_PKEY_free(skey); + BIO_free(in); + BIO_free(out); + BIO_free(tbio); + + return ret; + +} diff --git a/demos/smime/smsign2.c b/demos/smime/smsign2.c new file mode 100644 index 000000000..2b7f45b29 --- /dev/null +++ b/demos/smime/smsign2.c @@ -0,0 +1,96 @@ +/* + * Copyright 2007-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +/* S/MIME signing example: 2 signers. OpenSSL 0.9.9 only */ +#include +#include +#include + +int main(int argc, char **argv) +{ + BIO *in = NULL, *out = NULL, *tbio = NULL; + X509 *scert = NULL, *scert2 = NULL; + EVP_PKEY *skey = NULL, *skey2 = NULL; + PKCS7 *p7 = NULL; + int ret = 1; + + OpenSSL_add_all_algorithms(); + ERR_load_crypto_strings(); + + tbio = BIO_new_file("signer.pem", "r"); + + if (!tbio) + goto err; + + scert = PEM_read_bio_X509(tbio, NULL, 0, NULL); + + BIO_reset(tbio); + + skey = PEM_read_bio_PrivateKey(tbio, NULL, 0, NULL); + + BIO_free(tbio); + + tbio = BIO_new_file("signer2.pem", "r"); + + if (!tbio) + goto err; + + scert2 = PEM_read_bio_X509(tbio, NULL, 0, NULL); + + BIO_reset(tbio); + + skey2 = PEM_read_bio_PrivateKey(tbio, NULL, 0, NULL); + + if (!scert2 || !skey2) + goto err; + + in = BIO_new_file("sign.txt", "r"); + + if (!in) + goto err; + + p7 = PKCS7_sign(NULL, NULL, NULL, in, PKCS7_STREAM | PKCS7_PARTIAL); + + if (!p7) + goto err; + + /* Add each signer in turn */ + + if (!PKCS7_sign_add_signer(p7, scert, skey, NULL, 0)) + goto err; + + if (!PKCS7_sign_add_signer(p7, scert2, skey2, NULL, 0)) + goto err; + + out = BIO_new_file("smout.txt", "w"); + if (!out) + goto err; + + /* NB: content included and finalized by SMIME_write_PKCS7 */ + + if (!SMIME_write_PKCS7(out, p7, in, PKCS7_STREAM)) + goto err; + + ret = 0; + + err: + if (ret) { + fprintf(stderr, "Error Signing Data\n"); + ERR_print_errors_fp(stderr); + } + PKCS7_free(p7); + X509_free(scert); + EVP_PKEY_free(skey); + X509_free(scert2); + EVP_PKEY_free(skey2); + BIO_free(in); + BIO_free(out); + BIO_free(tbio); + return ret; +} diff --git a/demos/smime/smver.c b/demos/smime/smver.c new file mode 100644 index 000000000..75411c40d --- /dev/null +++ b/demos/smime/smver.c @@ -0,0 +1,83 @@ +/* + * Copyright 2007-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +/* Simple S/MIME verification example */ +#include +#include +#include + +int main(int argc, char **argv) +{ + BIO *in = NULL, *out = NULL, *tbio = NULL, *cont = NULL; + X509_STORE *st = NULL; + X509 *cacert = NULL; + PKCS7 *p7 = NULL; + + int ret = 1; + + OpenSSL_add_all_algorithms(); + ERR_load_crypto_strings(); + + /* Set up trusted CA certificate store */ + + st = X509_STORE_new(); + + /* Read in signer certificate and private key */ + tbio = BIO_new_file("cacert.pem", "r"); + + if (!tbio) + goto err; + + cacert = PEM_read_bio_X509(tbio, NULL, 0, NULL); + + if (!cacert) + goto err; + + if (!X509_STORE_add_cert(st, cacert)) + goto err; + + /* Open content being signed */ + + in = BIO_new_file("smout.txt", "r"); + + if (!in) + goto err; + + /* Sign content */ + p7 = SMIME_read_PKCS7(in, &cont); + + if (!p7) + goto err; + + /* File to output verified content to */ + out = BIO_new_file("smver.txt", "w"); + if (!out) + goto err; + + if (!PKCS7_verify(p7, NULL, st, cont, out, 0)) { + fprintf(stderr, "Verification Failure\n"); + goto err; + } + + fprintf(stderr, "Verification Successful\n"); + + ret = 0; + + err: + if (ret) { + fprintf(stderr, "Error Verifying Data\n"); + ERR_print_errors_fp(stderr); + } + PKCS7_free(p7); + X509_free(cacert); + BIO_free(in); + BIO_free(out); + BIO_free(tbio); + return ret; +} diff --git a/doc/CT_POLICY_EVAL_CTX_new.pod b/doc/CT_POLICY_EVAL_CTX_new.pod new file mode 100644 index 000000000..01f042b1d --- /dev/null +++ b/doc/CT_POLICY_EVAL_CTX_new.pod @@ -0,0 +1,113 @@ +=pod + +=encoding utf8 + +=head1 NAME + +CT_POLICY_EVAL_CTX_new, CT_POLICY_EVAL_CTX_free, +CT_POLICY_EVAL_CTX_get0_cert, CT_POLICY_EVAL_CTX_set1_cert, +CT_POLICY_EVAL_CTX_get0_issuer, CT_POLICY_EVAL_CTX_set1_issuer, +CT_POLICY_EVAL_CTX_get0_log_store, CT_POLICY_EVAL_CTX_set_shared_CTLOG_STORE, +CT_POLICY_EVAL_CTX_get_time, CT_POLICY_EVAL_CTX_set_time - +Encapsulates the data required to evaluate whether SCTs meet a Certificate Transparency policy + +=head1 SYNOPSIS + + #include + + CT_POLICY_EVAL_CTX *CT_POLICY_EVAL_CTX_new(void); + void CT_POLICY_EVAL_CTX_free(CT_POLICY_EVAL_CTX *ctx); + X509* CT_POLICY_EVAL_CTX_get0_cert(const CT_POLICY_EVAL_CTX *ctx); + int CT_POLICY_EVAL_CTX_set1_cert(CT_POLICY_EVAL_CTX *ctx, X509 *cert); + X509* CT_POLICY_EVAL_CTX_get0_issuer(const CT_POLICY_EVAL_CTX *ctx); + int CT_POLICY_EVAL_CTX_set1_issuer(CT_POLICY_EVAL_CTX *ctx, X509 *issuer); + const CTLOG_STORE *CT_POLICY_EVAL_CTX_get0_log_store(const CT_POLICY_EVAL_CTX *ctx); + void CT_POLICY_EVAL_CTX_set_shared_CTLOG_STORE(CT_POLICY_EVAL_CTX *ctx, CTLOG_STORE *log_store); + uint64_t CT_POLICY_EVAL_CTX_get_time(const CT_POLICY_EVAL_CTX *ctx); + void CT_POLICY_EVAL_CTX_set_time(CT_POLICY_EVAL_CTX *ctx, uint64_t time_in_ms); + +=head1 DESCRIPTION + +A B is used by functions that evaluate whether Signed +Certificate Timestamps (SCTs) fulfil a Certificate Transparency (CT) policy. +This policy may be, for example, that at least one valid SCT is available. To +determine this, an SCT's timestamp and signature must be verified. +This requires: + +=over + +=item * the public key of the log that issued the SCT + +=item * the certificate that the SCT was issued for + +=item * the issuer certificate (if the SCT was issued for a pre-certificate) + +=item * the current time + +=back + +The above requirements are met using the setters described below. + +CT_POLICY_EVAL_CTX_new() creates an empty policy evaluation context. This +should then be populated using: + +=over + +=item * CT_POLICY_EVAL_CTX_set1_cert() to provide the certificate the SCTs were issued for + +Increments the reference count of the certificate. + +=item * CT_POLICY_EVAL_CTX_set1_issuer() to provide the issuer certificate + +Increments the reference count of the certificate. + +=item * CT_POLICY_EVAL_CTX_set_shared_CTLOG_STORE() to provide a list of logs that are trusted as sources of SCTs + +Holds a pointer to the CTLOG_STORE, so the CTLOG_STORE must outlive the +CT_POLICY_EVAL_CTX. + +=item * CT_POLICY_EVAL_CTX_set_time() to set the time SCTs should be compared with to determine if they are valid + +The SCT timestamp will be compared to this time to check whether the SCT was +issued in the future. RFC6962 states that "TLS clients MUST reject SCTs whose +timestamp is in the future". By default, this will be set to 5 minutes in the +future (e.g. (time() + 300) * 1000), to allow for clock drift. + +The time should be in milliseconds since the Unix epoch. + +=back + +Each setter has a matching getter for accessing the current value. + +When no longer required, the B should be passed to +CT_POLICY_EVAL_CTX_free() to delete it. + +=head1 NOTES + +The issuer certificate only needs to be provided if at least one of the SCTs +was issued for a pre-certificate. This will be the case for SCTs embedded in a +certificate (i.e. those in an X.509 extension), but may not be the case for SCTs +found in the TLS SCT extension or OCSP response. + +=head1 RETURN VALUES + +CT_POLICY_EVAL_CTX_new() will return NULL if malloc fails. + +=head1 SEE ALSO + +L + +=head1 HISTORY + +These functions were added in OpenSSL 1.1.0. + +=head1 COPYRIGHT + +Copyright 2016 The OpenSSL Project Authors. All Rights Reserved. + +Licensed under the OpenSSL license (the "License"). You may not use +this file except in compliance with the License. You can obtain a copy +in the file LICENSE in the source distribution or at +L. + +=cut diff --git a/doc/HOWTO/certificates.txt b/doc/HOWTO/certificates.txt new file mode 100644 index 000000000..65f8fc829 --- /dev/null +++ b/doc/HOWTO/certificates.txt @@ -0,0 +1,110 @@ + + HOWTO certificates + +1. Introduction + +How you handle certificates depends a great deal on what your role is. +Your role can be one or several of: + + - User of some client application + - User of some server application + - Certificate authority + +This file is for users who wish to get a certificate of their own. +Certificate authorities should read https://www.openssl.org/docs/apps/ca.html. + +In all the cases shown below, the standard configuration file, as +compiled into openssl, will be used. You may find it in /etc/, +/usr/local/ssl/ or somewhere else. By default the file is named +openssl.cnf and is described at https://www.openssl.org/docs/apps/config.html. +You can specify a different configuration file using the +'-config {file}' argument with the commands shown below. + + +2. Relationship with keys + +Certificates are related to public key cryptography by containing a +public key. To be useful, there must be a corresponding private key +somewhere. With OpenSSL, public keys are easily derived from private +keys, so before you create a certificate or a certificate request, you +need to create a private key. + +Private keys are generated with 'openssl genrsa -out privkey.pem' if +you want a RSA private key, or if you want a DSA private key: +'openssl dsaparam -out dsaparam.pem 2048; openssl gendsa -out privkey.pem dsaparam.pem'. + +The private keys created by these commands are not passphrase protected; +it might or might not be the desirable thing. Further information on how to +create private keys can be found at https://www.openssl.org/docs/HOWTO/keys.txt. +The rest of this text assumes you have a private key in the file privkey.pem. + + +3. Creating a certificate request + +To create a certificate, you need to start with a certificate request +(or, as some certificate authorities like to put it, "certificate +signing request", since that's exactly what they do, they sign it and +give you the result back, thus making it authentic according to their +policies). A certificate request is sent to a certificate authority +to get it signed into a certificate. You can also sign the certificate +yourself if you have your own certificate authority or create a +self-signed certificate (typically for testing purpose). + +The certificate request is created like this: + + openssl req -new -key privkey.pem -out cert.csr + +Now, cert.csr can be sent to the certificate authority, if they can +handle files in PEM format. If not, use the extra argument '-outform' +followed by the keyword for the format to use (see another HOWTO +). In some cases, -outform does not let you output the +certificate request in the right format and you will have to use one +of the various other commands that are exposed by openssl (or get +creative and use a combination of tools). + +The certificate authority performs various checks (according to their +policies) and usually waits for payment from you. Once that is +complete, they send you your new certificate. + +Section 5 will tell you more on how to handle the certificate you +received. + + +4. Creating a self-signed test certificate + +You can create a self-signed certificate if you don't want to deal +with a certificate authority, or if you just want to create a test +certificate for yourself. This is similar to creating a certificate +request, but creates a certificate instead of a certificate request. +This is NOT the recommended way to create a CA certificate, see +https://www.openssl.org/docs/apps/ca.html. + + openssl req -new -x509 -key privkey.pem -out cacert.pem -days 1095 + + +5. What to do with the certificate + +If you created everything yourself, or if the certificate authority +was kind enough, your certificate is a raw DER thing in PEM format. +Your key most definitely is if you have followed the examples above. +However, some (most?) certificate authorities will encode them with +things like PKCS7 or PKCS12, or something else. Depending on your +applications, this may be perfectly OK, it all depends on what they +know how to decode. If not, There are a number of OpenSSL tools to +convert between some (most?) formats. + +So, depending on your application, you may have to convert your +certificate and your key to various formats, most often also putting +them together into one file. The ways to do this is described in +another HOWTO , I will just mention the simplest case. +In the case of a raw DER thing in PEM format, and assuming that's all +right for your applications, simply concatenating the certificate and +the key into a new file and using that one should be enough. With +some applications, you don't even have to do that. + + +By now, you have your certificate and your private key and can start +using applications that depend on it. + +-- +Richard Levitte diff --git a/doc/HOWTO/keys.txt b/doc/HOWTO/keys.txt new file mode 100644 index 000000000..ba0314faf --- /dev/null +++ b/doc/HOWTO/keys.txt @@ -0,0 +1,72 @@ + + HOWTO keys + +1. Introduction + +Keys are the basis of public key algorithms and PKI. Keys usually +come in pairs, with one half being the public key and the other half +being the private key. With OpenSSL, the private key contains the +public key information as well, so a public key doesn't need to be +generated separately. + +Public keys come in several flavors, using different cryptographic +algorithms. The most popular ones associated with certificates are +RSA and DSA, and this HOWTO will show how to generate each of them. + + +2. To generate a RSA key + +A RSA key can be used both for encryption and for signing. + +Generating a key for the RSA algorithm is quite easy, all you have to +do is the following: + + openssl genrsa -des3 -out privkey.pem 2048 + +With this variant, you will be prompted for a protecting password. If +you don't want your key to be protected by a password, remove the flag +'-des3' from the command line above. + + NOTE: if you intend to use the key together with a server + certificate, it may be a good thing to avoid protecting it + with a password, since that would mean someone would have to + type in the password every time the server needs to access + the key. + +The number 2048 is the size of the key, in bits. Today, 2048 or +higher is recommended for RSA keys, as fewer amount of bits is +consider insecure or to be insecure pretty soon. + + +3. To generate a DSA key + +A DSA key can be used for signing only. It is important to +know what a certificate request with a DSA key can really be used for. + +Generating a key for the DSA algorithm is a two-step process. First, +you have to generate parameters from which to generate the key: + + openssl dsaparam -out dsaparam.pem 2048 + +The number 2048 is the size of the key, in bits. Today, 2048 or +higher is recommended for DSA keys, as fewer amount of bits is +consider insecure or to be insecure pretty soon. + +When that is done, you can generate a key using the parameters in +question (actually, several keys can be generated from the same +parameters): + + openssl gendsa -des3 -out privkey.pem dsaparam.pem + +With this variant, you will be prompted for a protecting password. If +you don't want your key to be protected by a password, remove the flag +'-des3' from the command line above. + + NOTE: if you intend to use the key together with a server + certificate, it may be a good thing to avoid protecting it + with a password, since that would mean someone would have to + type in the password every time the server needs to access + the key. + +-- +Richard Levitte diff --git a/doc/HOWTO/proxy_certificates.txt b/doc/HOWTO/proxy_certificates.txt new file mode 100644 index 000000000..642bec928 --- /dev/null +++ b/doc/HOWTO/proxy_certificates.txt @@ -0,0 +1,319 @@ + HOWTO proxy certificates + +0. WARNING + +NONE OF THE CODE PRESENTED HERE HAS BEEN CHECKED! The code is just examples to +show you how things could be done. There might be typos or type conflicts, and +you will have to resolve them. + +1. Introduction + +Proxy certificates are defined in RFC 3820. They are really usual certificates +with the mandatory extension proxyCertInfo. + +Proxy certificates are issued by an End Entity (typically a user), either +directly with the EE certificate as issuing certificate, or by extension through +an already issued proxy certificate. Proxy certificates are used to extend +rights to some other entity (a computer process, typically, or sometimes to the +user itself). This allows the entity to perform operations on behalf of the +owner of the EE certificate. + +See http://www.ietf.org/rfc/rfc3820.txt for more information. + + +2. A warning about proxy certificates + +No one seems to have tested proxy certificates with security in mind. To this +date, it seems that proxy certificates have only been used in a context highly +aware of them. + +Existing applications might misbehave when trying to validate a chain of +certificates which use a proxy certificate. They might incorrectly consider the +leaf to be the certificate to check for authorisation data, which is controlled +by the EE certificate owner. + +subjectAltName and issuerAltName are forbidden in proxy certificates, and this +is enforced in OpenSSL. The subject must be the same as the issuer, with one +commonName added on. + +Possible threats we can think of at this time include: + + - impersonation through commonName (think server certificates). + - use of additional extensions, possibly non-standard ones used in certain + environments, that would grant extra or different authorisation rights. + +For these reasons, OpenSSL requires that the use of proxy certificates be +explicitly allowed. Currently, this can be done using the following methods: + + - if the application directly calls X509_verify_cert(), it can first call: + + X509_STORE_CTX_set_flags(ctx, X509_V_FLAG_ALLOW_PROXY_CERTS); + + Where ctx is the pointer which then gets passed to X509_verify_cert(). + + - proxy certificate validation can be enabled before starting the application + by setting the environment variable OPENSSL_ALLOW_PROXY_CERTS. + +In the future, it might be possible to enable proxy certificates by editing +openssl.cnf. + + +3. How to create proxy certificates + +Creating proxy certificates is quite easy, by taking advantage of a lack of +checks in the 'openssl x509' application (*ahem*). You must first create a +configuration section that contains a definition of the proxyCertInfo extension, +for example: + + [ v3_proxy ] + # A proxy certificate MUST NEVER be a CA certificate. + basicConstraints=CA:FALSE + + # Usual authority key ID + authorityKeyIdentifier=keyid,issuer:always + + # The extension which marks this certificate as a proxy + proxyCertInfo=critical,language:id-ppl-anyLanguage,pathlen:1,policy:text:AB + +It's also possible to specify the proxy extension in a separate section: + + proxyCertInfo=critical,@proxy_ext + + [ proxy_ext ] + language=id-ppl-anyLanguage + pathlen=0 + policy=text:BC + +The policy value has a specific syntax, {syntag}:{string}, where the syntag +determines what will be done with the string. The following syntags are +recognised: + + text indicates that the string is simply bytes, without any encoding: + + policy=text:räksmörgÃ¥s + + Previous versions of this design had a specific tag for UTF-8 text. + However, since the bytes are copied as-is anyway, there is no need for + such a specific tag. + + hex indicates the string is encoded in hex, with colons between each byte + (every second hex digit): + + policy=hex:72:E4:6B:73:6D:F6:72:67:E5:73 + + Previous versions of this design had a tag to insert a complete DER + blob. However, the only legal use for this would be to surround the + bytes that would go with the hex: tag with whatever is needed to + construct a correct OCTET STRING. The DER tag therefore felt + superfluous, and was removed. + + file indicates that the text of the policy should really be taken from a + file. The string is then really a file name. This is useful for + policies that are large (more than a few lines, e.g. XML documents). + +The 'policy' setting can be split up in multiple lines like this: + + 0.policy=This is + 1.policy= a multi- + 2.policy=line policy. + +NOTE: the proxy policy value is the part which determines the rights granted to +the process using the proxy certificate. The value is completely dependent on +the application reading and interpreting it! + +Now that you have created an extension section for your proxy certificate, you +can easily create a proxy certificate by doing: + + openssl req -new -config openssl.cnf -out proxy.req -keyout proxy.key + openssl x509 -req -CAcreateserial -in proxy.req -days 7 -out proxy.crt \ + -CA user.crt -CAkey user.key -extfile openssl.cnf -extensions v3_proxy + +You can also create a proxy certificate using another proxy certificate as +issuer (note: I'm using a different configuration section for it): + + openssl req -new -config openssl.cnf -out proxy2.req -keyout proxy2.key + openssl x509 -req -CAcreateserial -in proxy2.req -days 7 -out proxy2.crt \ + -CA proxy.crt -CAkey proxy.key -extfile openssl.cnf -extensions v3_proxy2 + + +4. How to have your application interpret the policy? + +The basic way to interpret proxy policies is to start with some default rights, +then compute the resulting rights by checking the proxy certificate against +the chain of proxy certificates, user certificate and CA certificates. You then +use the final computed rights. Sounds easy, huh? It almost is. + +The slightly complicated part is figuring out how to pass data between your +application and the certificate validation procedure. + +You need the following ingredients: + + - a callback function that will be called for every certificate being + validated. The callback be called several times for each certificate, + so you must be careful to do the proxy policy interpretation at the right + time. You also need to fill in the defaults when the EE certificate is + checked. + + - a data structure that is shared between your application code and the + callback. + + - a wrapper function that sets it all up. + + - an ex_data index function that creates an index into the generic ex_data + store that is attached to an X509 validation context. + +Here is some skeleton code you can fill in: + + #include + #include + #include + #include + + #define total_rights 25 + + /* + * In this example, I will use a view of granted rights as a bit + * array, one bit for each possible right. + */ + typedef struct your_rights { + unsigned char rights[(total_rights + 7) / 8]; + } YOUR_RIGHTS; + + /* + * The following procedure will create an index for the ex_data + * store in the X509 validation context the first time it's called. + * Subsequent calls will return the same index. */ + static int get_proxy_auth_ex_data_idx(X509_STORE_CTX *ctx) + { + static volatile int idx = -1; + if (idx < 0) { + X509_STORE_lock(X509_STORE_CTX_get0_store(ctx)); + if (idx < 0) { + idx = X509_STORE_CTX_get_ex_new_index(0, + "for verify callback", + NULL,NULL,NULL); + } + X509_STORE_unlock(X509_STORE_CTX_get0_store(ctx)); + } + return idx; + } + + /* Callback to be given to the X509 validation procedure. */ + static int verify_callback(int ok, X509_STORE_CTX *ctx) + { + if (ok == 1) { + /* + * It's REALLY important you keep the proxy policy + * check within this section. It's important to know + * that when ok is 1, the certificates are checked + * from top to bottom. You get the CA root first, + * followed by the possible chain of intermediate + * CAs, followed by the EE certificate, followed by + * the possible proxy certificates. + */ + X509 *xs = X509_STORE_CTX_get_current_cert(ctx); + + if (X509_get_extension_flags(xs) & EXFLAG_PROXY) { + YOUR_RIGHTS *rights = + (YOUR_RIGHTS *)X509_STORE_CTX_get_ex_data(ctx, + get_proxy_auth_ex_data_idx(ctx)); + PROXY_CERT_INFO_EXTENSION *pci = + X509_get_ext_d2i(xs, NID_proxyCertInfo, NULL, NULL); + + switch (OBJ_obj2nid(pci->proxyPolicy->policyLanguage)) { + case NID_Independent: + /* + * Do whatever you need to grant explicit rights to + * this particular proxy certificate, usually by + * pulling them from some database. If there are none + * to be found, clear all rights (making this and any + * subsequent proxy certificate void of any rights). + */ + memset(rights->rights, 0, sizeof(rights->rights)); + break; + case NID_id_ppl_inheritAll: + /* + * This is basically a NOP, we simply let the current + * rights stand as they are. + */ + break; + default: + /* This is usually the most complex section of code. + * You really do whatever you want as long as you + * follow RFC 3820. In the example we use here, the + * simplest thing to do is to build another, temporary + * bit array and fill it with the rights granted by + * the current proxy certificate, then use it as a + * mask on the accumulated rights bit array, and + * voilà, you now have a new accumulated rights bit + * array. + */ + { + int i; + YOUR_RIGHTS tmp_rights; + memset(tmp_rights.rights, 0, sizeof(tmp_rights.rights)); + + /* + * process_rights() is supposed to be a procedure + * that takes a string and it's length, interprets + * it and sets the bits in the YOUR_RIGHTS pointed + * at by the third argument. + */ + process_rights((char *) pci->proxyPolicy->policy->data, + pci->proxyPolicy->policy->length, + &tmp_rights); + + for(i = 0; i < total_rights / 8; i++) + rights->rights[i] &= tmp_rights.rights[i]; + } + break; + } + PROXY_CERT_INFO_EXTENSION_free(pci); + } else if (!(X509_get_extension_flags(xs) & EXFLAG_CA)) { + /* We have an EE certificate, let's use it to set default! */ + YOUR_RIGHTS *rights = + (YOUR_RIGHTS *)X509_STORE_CTX_get_ex_data(ctx, + get_proxy_auth_ex_data_idx(ctx)); + + /* The following procedure finds out what rights the owner + * of the current certificate has, and sets them in the + * YOUR_RIGHTS structure pointed at by the second + * argument. + */ + set_default_rights(xs, rights); + } + } + return ok; + } + + static int my_X509_verify_cert(X509_STORE_CTX *ctx, + YOUR_RIGHTS *needed_rights) + { + int ok; + int (*save_verify_cb)(int ok,X509_STORE_CTX *ctx) = + X509_STORE_CTX_get_verify_cb(ctx); + YOUR_RIGHTS rights; + + X509_STORE_CTX_set_verify_cb(ctx, verify_callback); + X509_STORE_CTX_set_ex_data(ctx, get_proxy_auth_ex_data_idx(ctx), &rights); + X509_STORE_CTX_set_flags(ctx, X509_V_FLAG_ALLOW_PROXY_CERTS); + ok = X509_verify_cert(ctx); + + if (ok == 1) { + ok = check_needed_rights(rights, needed_rights); + } + + X509_STORE_CTX_set_verify_cb(ctx, save_verify_cb); + + return ok; + } + + +If you use SSL or TLS, you can easily set up a callback to have the +certificates checked properly, using the code above: + + SSL_CTX_set_cert_verify_callback(s_ctx, my_X509_verify_cert, &needed_rights); + + +-- +Richard Levitte diff --git a/doc/README b/doc/README new file mode 100644 index 000000000..593129023 --- /dev/null +++ b/doc/README @@ -0,0 +1,20 @@ + +README This file + +fingerprints.txt + PGP fingerprints of authoried release signers + +standards.txt + Moved to the web, https://www.openssl.org/docs/standards.html + +HOWTO/ + A few how-to documents; not necessarily up-to-date +apps/ + The openssl command-line tools; start with openssl.pod +ssl/ + The SSL library; start with ssl.pod +crypto/ + The cryptographic library; start with crypto.pod + +Formatted versions of the manpages (apps,ssl,crypto) can be found at + https://www.openssl.org/docs/manpages.html diff --git a/doc/SCT_validate.pod b/doc/SCT_validate.pod new file mode 100644 index 000000000..a4fbde6b3 --- /dev/null +++ b/doc/SCT_validate.pod @@ -0,0 +1,100 @@ +=pod + +=encoding utf8 + +=head1 NAME + +SCT_validate, SCT_LIST_validate, SCT_get_validation_status - +checks Signed Certificate Timestamps (SCTs) are valid + +=head1 SYNOPSIS + + #include + + typedef enum { + SCT_VALIDATION_STATUS_NOT_SET, + SCT_VALIDATION_STATUS_UNKNOWN_LOG, + SCT_VALIDATION_STATUS_VALID, + SCT_VALIDATION_STATUS_INVALID, + SCT_VALIDATION_STATUS_UNVERIFIED, + SCT_VALIDATION_STATUS_UNKNOWN_VERSION + } sct_validation_status_t; + + int SCT_validate(SCT *sct, const CT_POLICY_EVAL_CTX *ctx); + int SCT_LIST_validate(const STACK_OF(SCT) *scts, CT_POLICY_EVAL_CTX *ctx); + sct_validation_status_t SCT_get_validation_status(const SCT *sct); + +=head1 DESCRIPTION + +SCT_validate() will check that an SCT is valid and verify its signature. +SCT_LIST_validate() performs the same checks on an entire stack of SCTs. +The result of the validation checks can be obtained by passing the SCT to +SCT_get_validation_status(). + +A CT_POLICY_EVAL_CTX must be provided that specifies: + +=over + +=item * The certificate the SCT was issued for. + +Failure to provide the certificate will result in the validation status being +SCT_VALIDATION_STATUS_UNVERIFIED. + +=item * The issuer of that certificate. + +This is only required if the SCT was issued for a pre-certificate +(see RFC 6962). If it is required but not provided, the validation status will +be SCT_VALIDATION_STATUS_UNVERIFIED. + +=item * A CTLOG_STORE that contains the CT log that issued this SCT. + +If the SCT was issued by a log that is not in this CTLOG_STORE, the validation +status will be SCT_VALIDATION_STATUS_UNKNOWN_LOG. + +=back + +If the SCT is of an unsupported version (only v1 is currently supported), the +validation status will be SCT_VALIDATION_STATUS_UNKNOWN_VERSION. + +If the SCT's signature is incorrect, its timestamp is in the future (relative to +the time in CT_POLICY_EVAL_CTX), or if it is otherwise invalid, the validation +status will be SCT_VALIDATION_STATUS_INVALID. + +If all checks pass, the validation status will be SCT_VALIDATION_STATUS_VALID. + +=head1 NOTES + +A return value of 0 from SCT_LIST_validate() should not be interpreted as a +failure. At a minimum, only one valid SCT may provide sufficient confidence +that a certificate has been publicly logged. + +=head1 RETURN VALUES + +SCT_validate() returns a negative integer if an internal error occurs, 0 if the +SCT fails validation, or 1 if the SCT passes validation. + +SCT_LIST_validate() returns a negative integer if an internal error occurs, 0 +if any of SCTs fails validation, or 1 if they all pass validation. + +SCT_get_validation_status() returns the validation status of the SCT. +If SCT_validate() or SCT_LIST_validate() have not been passed that SCT, the +returned value will be SCT_VALIDATION_STATUS_NOT_SET. + +=head1 SEE ALSO + +L + +=head1 HISTORY + +These functions were added in OpenSSL 1.1.0. + +=head1 COPYRIGHT + +Copyright 2016 The OpenSSL Project Authors. All Rights Reserved. + +Licensed under the OpenSSL license (the "License"). You may not use +this file except in compliance with the License. You can obtain a copy +in the file LICENSE in the source distribution or at +L. + +=cut diff --git a/doc/SSL_CTX_set_ct_validation_callback.pod b/doc/SSL_CTX_set_ct_validation_callback.pod new file mode 100644 index 000000000..80e3fec91 --- /dev/null +++ b/doc/SSL_CTX_set_ct_validation_callback.pod @@ -0,0 +1,144 @@ +=pod + +=encoding utf8 + +=head1 NAME + +SSL_enable_ct, SSL_CTX_enable_ct, SSL_disable_ct, SSL_CTX_disable_ct, +SSL_set_ct_validation_callback, SSL_CTX_set_ct_validation_callback, +SSL_ct_is_enabled, SSL_CTX_ct_is_enabled - +control Certificate Transparency policy + +=head1 SYNOPSIS + + #include + + int SSL_enable_ct(SSL *s, int validation_mode); + int SSL_CTX_enable_ct(SSL_CTX *ctx, int validation_mode); + int SSL_set_ct_validation_callback(SSL *s, ssl_ct_validation_cb callback, + void *arg); + int SSL_CTX_set_ct_validation_callback(SSL_CTX *ctx, + ssl_ct_validation_cb callback, + void *arg); + void SSL_disable_ct(SSL *s); + void SSL_CTX_disable_ct(SSL_CTX *ctx); + int SSL_ct_is_enabled(const SSL *s); + int SSL_CTX_ct_is_enabled(const SSL_CTX *ctx); + +=head1 DESCRIPTION + +SSL_enable_ct() and SSL_CTX_enable_ct() enable the processing of signed +certificate timestamps (SCTs) either for a given SSL connection or for all +connections that share the given SSL context, respectively. +This is accomplished by setting a built-in CT validation callback. +The behaviour of the callback is determined by the B argument, +which can be either of B or +B as described below. + +If B is equal to B, then in a full +TLS handshake with the verification mode set to B, if the peer +presents no valid SCTs the handshake will be aborted. +If the verification mode is B, the handshake will continue +despite lack of valid SCTs. +However, in that case if the verification status before the built-in callback +was B it will be set to B after the +callback. +Applications can call L to check the status at +handshake completion, even after session resumption since the verification +status is part of the saved session state. +See L, , L. + +If B is equal to B, then the +handshake continues, and the verification status is not modified, regardless of +the validation status of any SCTs. +The application can still inspect the validation status of the SCTs at +handshake completion. +Note that with session resumption there will not be any SCTs presented during +the handshake. +Therefore, in applications that delay SCT policy enforcement until after +handshake completion, such delayed SCT checks should only be performed when the +session is not resumed. + +SSL_set_ct_validation_callback() and SSL_CTX_set_ct_validation_callback() +register a custom callback that may implement a different policy than either of +the above. +This callback can examine the peer's SCTs and determine whether they are +sufficient to allow the connection to continue. +The TLS handshake is aborted if the verification mode is not B +and the callback returns a non-positive result. + +An arbitrary callback context argument, B, can be passed in when setting +the callback. +This will be passed to the callback whenever it is invoked. +Ownership of this context remains with the caller. + +If no callback is set, SCTs will not be requested and Certificate Transparency +validation will not occur. + +No callback will be invoked when the peer presents no certificate, e.g. by +employing an anonymous (aNULL) ciphersuite. +In that case the handshake continues as it would had no callback been +requested. +Callbacks are also not invoked when the peer certificate chain is invalid or +validated via DANE-TA(2) or DANE-EE(3) TLSA records which use a private X.509 +PKI, or no X.509 PKI at all, respectively. +Clients that require SCTs are expected to not have enabled any aNULL ciphers +nor to have specified server verification via DANE-TA(2) or DANE-EE(3) TLSA +records. + +SSL_disable_ct() and SSL_CTX_disable_ct() turn off CT processing, whether +enabled via the built-in or the custom callbacks, by setting a NULL callback. +These may be implemented as macros. + +SSL_ct_is_enabled() and SSL_CTX_ct_is_enabled() return 1 if CT processing is +enabled via either SSL_enable_ct() or a non-null custom callback, and 0 +otherwise. + +=head1 NOTES + +When SCT processing is enabled, OCSP stapling will be enabled. This is because +one possible source of SCTs is the OCSP response from a server. + +The time returned by SSL_SESSION_get_time() will be used to evaluate whether any +presented SCTs have timestamps that are in the future (and therefore invalid). + +=head1 RESTRICTIONS + +Certificate Transparency validation cannot be enabled and so a callback cannot +be set if a custom client extension handler has been registered to handle SCT +extensions (B). + +=head1 RETURN VALUES + +SSL_enable_ct(), SSL_CTX_enable_ct(), SSL_CTX_set_ct_validation_callback() and +SSL_set_ct_validation_callback() return 1 if the B is successfully +set. +They return 0 if an error occurs, e.g. a custom client extension handler has +been setup to handle SCTs. + +SSL_disable_ct() and SSL_CTX_disable_ct() do not return a result. + +SSL_CTX_ct_is_enabled() and SSL_ct_is_enabled() return a 1 if a non-null CT +validation callback is set, or 0 if no callback (or equivalently a NULL +callback) is set. + +=head1 SEE ALSO + +L, +, +L, +L, +L, +L, +L + +=head1 COPYRIGHT + +Copyright 2016 The OpenSSL Project Authors. All Rights Reserved. + +Licensed under the OpenSSL license (the "License"). You may not use +this file except in compliance with the License. You can obtain a copy +in the file LICENSE in the source distribution or at +L. + +=cut diff --git a/doc/apps/CA.pl.pod b/doc/apps/CA.pl.pod new file mode 100644 index 000000000..c86b7ee43 --- /dev/null +++ b/doc/apps/CA.pl.pod @@ -0,0 +1,222 @@ +=pod + +=encoding utf8 + +=head1 NAME + +CA.pl - friendlier interface for GmSSL certificate programs + +=head1 SYNOPSIS + +B +B<-?> | +B<-h> | +B<-help> + +B +B<-newcert> | +B<-newreq> | +B<-newreq-nodes> | +B<-xsign> | +B<-sign> | +B<-signCA> | +B<-signcert> | +B<-crl> | +B<-newca> +[B<-extra-cmd> extra-params] + +B B<-pkcs12> [B<-extra-pkcs12> extra-params] [B] + +B B<-verify> [B<-extra-verify> extra-params] B... + +B B<-revoke> [B<-extra-ca> extra-params] B [B] + +=head1 DESCRIPTION + +The B script is a perl script that supplies the relevant command line +arguments to the B command for some common certificate operations. +It is intended to simplify the process of certificate creation and management +by the use of some simple options. + +=head1 OPTIONS + +=over 4 + +=item B, B<-h>, B<-help> + +prints a usage message. + +=item B<-newcert> + +creates a new self signed certificate. The private key is written to the file +"newkey.pem" and the request written to the file "newreq.pem". +This argument invokes B command. + +=item B<-newreq> + +creates a new certificate request. The private key is written to the file +"newkey.pem" and the request written to the file "newreq.pem". +Executes B command below the hood. + +=item B<-newreq-nodes> + +is like B<-newreq> except that the private key will not be encrypted. +Uses B command. + +=item B<-newca> + +creates a new CA hierarchy for use with the B program (or the B<-signcert> +and B<-xsign> options). The user is prompted to enter the filename of the CA +certificates (which should also contain the private key) or by hitting ENTER +details of the CA will be prompted for. The relevant files and directories +are created in a directory called "demoCA" in the current directory. +B and B commands are get invoked. + +=item B<-pkcs12> + +create a PKCS#12 file containing the user certificate, private key and CA +certificate. It expects the user certificate and private key to be in the +file "newcert.pem" and the CA certificate to be in the file demoCA/cacert.pem, +it creates a file "newcert.p12". This command can thus be called after the +B<-sign> option. The PKCS#12 file can be imported directly into a browser. +If there is an additional argument on the command line it will be used as the +"friendly name" for the certificate (which is typically displayed in the browser +list box), otherwise the name "My Certificate" is used. +Delegates work to B command. + +=item B<-sign>, B<-signcert>, B<-xsign> + +calls the B program to sign a certificate request. It expects the request +to be in the file "newreq.pem". The new certificate is written to the file +"newcert.pem" except in the case of the B<-xsign> option when it is written +to standard output. Leverages B command. + +=item B<-signCA> + +this option is the same as the B<-signreq> option except it uses the configuration +file section B and so makes the signed request a valid CA certificate. This +is useful when creating intermediate CA from a root CA. +Extra params are passed on to B command. + +=item B<-signcert> + +this option is the same as B<-sign> except it expects a self signed certificate +to be present in the file "newreq.pem". +Extra params are passed on to B and B commands. + +=item B<-crl> + +generate a CRL. Executes B command. + +=item B<-revoke certfile [reason]> + +revoke the certificate contained in the specified B. An optional +reason may be specified, and must be one of: B, +B, B, B, B, +B, B, or B. +Leverages B command. + +=item B<-verify> + +verifies certificates against the CA certificate for "demoCA". If no certificates +are specified on the command line it tries to verify the file "newcert.pem". +Invokes B command. + +=item B<-extra-req> | B<-extra-ca> | B<-extra-pkcs12> | B<-extra-x509> | B<-extra-verify> + +The purpose of these parameters is to allow optional parameters to be supplied +to B that this command executes. The B<-extra-cmd> are specific to the +option being used and the B command getting invoked. For example +when this command invokes B extra parameters can be passed on +with the B<-extra-req> parameter. The +B commands being invoked per option are documented below. +Users should consult B command documentation for more information. + +=back + +=head1 EXAMPLES + +Create a CA hierarchy: + + CA.pl -newca + +Complete certificate creation example: create a CA, create a request, sign +the request and finally create a PKCS#12 file containing it. + + CA.pl -newca + CA.pl -newreq + CA.pl -signreq + CA.pl -pkcs12 "My Test Certificate" + +=head1 DSA CERTIFICATES + +Although the B creates RSA CAs and requests it is still possible to +use it with DSA certificates and requests using the L command +directly. The following example shows the steps that would typically be taken. + +Create some DSA parameters: + + gmssl dsaparam -out dsap.pem 1024 + +Create a DSA CA certificate and private key: + + gmssl req -x509 -newkey dsa:dsap.pem -keyout cacert.pem -out cacert.pem + +Create the CA directories and files: + + CA.pl -newca + +enter cacert.pem when prompted for the CA file name. + +Create a DSA certificate request and private key (a different set of parameters +can optionally be created first): + + gmssl req -out newreq.pem -newkey dsa:dsap.pem + +Sign the request: + + CA.pl -signreq + +=head1 NOTES + +Most of the filenames mentioned can be modified by editing the B script. + +If the demoCA directory already exists then the B<-newca> command will not +overwrite it and will do nothing. This can happen if a previous call using +the B<-newca> option terminated abnormally. To get the correct behaviour +delete the demoCA directory if it already exists. + +Under some environments it may not be possible to run the B script +directly (for example Win32) and the default configuration file location may +be wrong. In this case the command: + + perl -S CA.pl + +can be used and the B environment variable changed to point to +the correct path of the configuration file "openssl.cnf". + +The script is intended as a simple front end for the B program for use +by a beginner. Its behaviour isn't always what is wanted. For more control over the +behaviour of the certificate commands call the B command directly. + +=head1 ENVIRONMENT VARIABLES + +The variable B if defined allows an alternative configuration +file location to be specified, it should contain the full path to the +configuration file, not just its directory. + +=head1 SEE ALSO + +L, L, L, L, +L + +=head1 COPYRIGHT + +Copyright 2000-2016 The OpenSSL Project Authors. All Rights Reserved. + +Licensed under the GmSSL license (the "License"). You may not use +this file except in compliance with the License. You can obtain a copy +in the file LICENSE in the source distribution or at +L. + +=cut diff --git a/doc/apps/asn1parse.pod b/doc/apps/asn1parse.pod new file mode 100644 index 000000000..52d4903f6 --- /dev/null +++ b/doc/apps/asn1parse.pod @@ -0,0 +1,210 @@ +=pod + +=encoding utf8 + +=head1 NAME + +asn1parse - ASN.1 parsing tool + +=head1 SYNOPSIS + +B B +[B<-help>] +[B<-inform PEM|DER>] +[B<-in filename>] +[B<-out filename>] +[B<-noout>] +[B<-offset number>] +[B<-length number>] +[B<-i>] +[B<-oid filename>] +[B<-dump>] +[B<-dlimit num>] +[B<-strparse offset>] +[B<-genstr string>] +[B<-genconf file>] +[B<-strictpem>] + +=head1 DESCRIPTION + +The B command is a diagnostic utility that can parse ASN.1 +structures. It can also be used to extract data from ASN.1 formatted data. + +=head1 OPTIONS + +=over 4 + +=item B<-help> + +Print out a usage message. + +=item B<-inform> B + +the input format. B is binary format and B (the default) is base64 +encoded. + +=item B<-in filename> + +the input file, default is standard input + +=item B<-out filename> + +output file to place the DER encoded data into. If this +option is not present then no data will be output. This is most useful when +combined with the B<-strparse> option. + +=item B<-noout> + +don't output the parsed version of the input file. + +=item B<-offset number> + +starting offset to begin parsing, default is start of file. + +=item B<-length number> + +number of bytes to parse, default is until end of file. + +=item B<-i> + +indents the output according to the "depth" of the structures. + +=item B<-oid filename> + +a file containing additional OBJECT IDENTIFIERs (OIDs). The format of this +file is described in the NOTES section below. + +=item B<-dump> + +dump unknown data in hex format. + +=item B<-dlimit num> + +like B<-dump>, but only the first B bytes are output. + +=item B<-strparse offset> + +parse the contents octets of the ASN.1 object starting at B. This +option can be used multiple times to "drill down" into a nested structure. + +=item B<-genstr string>, B<-genconf file> + +generate encoded data based on B, B or both using +L format. If B only is +present then the string is obtained from the default section using the name +B. The encoded data is passed through the ASN1 parser and printed out as +though it came from a file, the contents can thus be examined and written to a +file using the B option. + +=item B<-strictpem> + +If this option is used then B<-inform> will be ignored. Without this option any +data in a PEM format input file will be treated as being base64 encoded and +processed whether it has the normal PEM BEGIN and END markers or not. This +option will ignore any data prior to the start of the BEGIN marker, or after an +END marker in a PEM file. + +=back + +=head2 Output + +The output will typically contain lines like this: + + 0:d=0 hl=4 l= 681 cons: SEQUENCE + +..... + + 229:d=3 hl=3 l= 141 prim: BIT STRING + 373:d=2 hl=3 l= 162 cons: cont [ 3 ] + 376:d=3 hl=3 l= 159 cons: SEQUENCE + 379:d=4 hl=2 l= 29 cons: SEQUENCE + 381:d=5 hl=2 l= 3 prim: OBJECT :X509v3 Subject Key Identifier + 386:d=5 hl=2 l= 22 prim: OCTET STRING + 410:d=4 hl=2 l= 112 cons: SEQUENCE + 412:d=5 hl=2 l= 3 prim: OBJECT :X509v3 Authority Key Identifier + 417:d=5 hl=2 l= 105 prim: OCTET STRING + 524:d=4 hl=2 l= 12 cons: SEQUENCE + +..... + +This example is part of a self-signed certificate. Each line starts with the +offset in decimal. B specifies the current depth. The depth is increased +within the scope of any SET or SEQUENCE. B gives the header length +(tag and length octets) of the current type. B gives the length of +the contents octets. + +The B<-i> option can be used to make the output more readable. + +Some knowledge of the ASN.1 structure is needed to interpret the output. + +In this example the BIT STRING at offset 229 is the certificate public key. +The contents octets of this will contain the public key information. This can +be examined using the option B<-strparse 229> to yield: + + 0:d=0 hl=3 l= 137 cons: SEQUENCE + 3:d=1 hl=3 l= 129 prim: INTEGER :E5D21E1F5C8D208EA7A2166C7FAF9F6BDF2059669C60876DDB70840F1A5AAFA59699FE471F379F1DD6A487E7D5409AB6A88D4A9746E24B91D8CF55DB3521015460C8EDE44EE8A4189F7A7BE77D6CD3A9AF2696F486855CF58BF0EDF2B4068058C7A947F52548DDF7E15E96B385F86422BEA9064A3EE9E1158A56E4A6F47E5897 + 135:d=1 hl=2 l= 3 prim: INTEGER :010001 + +=head1 NOTES + +If an OID is not part of GmSSL's internal table it will be represented in +numerical form (for example 1.2.3.4). The file passed to the B<-oid> option +allows additional OIDs to be included. Each line consists of three columns, +the first column is the OID in numerical format and should be followed by white +space. The second column is the "short name" which is a single word followed +by white space. The final column is the rest of the line and is the +"long name". B displays the long name. Example: + +C<1.2.3.4 shortName A long name> + +=head1 EXAMPLES + +Parse a file: + + gmssl asn1parse -in file.pem + +Parse a DER file: + + gmssl asn1parse -inform DER -in file.der + +Generate a simple UTF8String: + + gmssl asn1parse -genstr 'UTF8:Hello World' + +Generate and write out a UTF8String, don't print parsed output: + + gmssl asn1parse -genstr 'UTF8:Hello World' -noout -out utf8.der + +Generate using a config file: + + gmssl asn1parse -genconf asn1.cnf -noout -out asn1.der + +Example config file: + + asn1=SEQUENCE:seq_sect + + [seq_sect] + + field1=BOOL:TRUE + field2=EXP:0, UTF8:some random string + + +=head1 BUGS + +There should be options to change the format of output lines. The output of some +ASN.1 types is not well handled (if at all). + +=head1 SEE ALSO + +L + +=head1 COPYRIGHT + +Copyright 2000-2016 The OpenSSL Project Authors. All Rights Reserved. + +Licensed under the GmSSL license (the "License"). You may not use +this file except in compliance with the License. You can obtain a copy +in the file LICENSE in the source distribution or at +L. + +=cut diff --git a/doc/apps/ca.pod b/doc/apps/ca.pod new file mode 100644 index 000000000..85ef87612 --- /dev/null +++ b/doc/apps/ca.pod @@ -0,0 +1,896 @@ +=pod + +=encoding utf8 + +=head1 NAME + +ca - sample minimal CA application + +=head1 SYNOPSIS + +B B +[B<-help>] +[B<-verbose>] +[B<-config filename>] +[B<-name section>] +[B<-gencrl>] +[B<-revoke file>] +[B<-valid file>] +[B<-status serial>] +[B<-updatedb>] +[B<-crl_reason reason>] +[B<-crl_hold instruction>] +[B<-crl_compromise time>] +[B<-crl_CA_compromise time>] +[B<-crldays days>] +[B<-crlhours hours>] +[B<-crlexts section>] +[B<-startdate date>] +[B<-enddate date>] +[B<-days arg>] +[B<-md arg>] +[B<-policy arg>] +[B<-keyfile arg>] +[B<-keyform PEM|DER>] +[B<-key arg>] +[B<-passin arg>] +[B<-cert file>] +[B<-selfsign>] +[B<-in file>] +[B<-out file>] +[B<-notext>] +[B<-outdir dir>] +[B<-infiles>] +[B<-spkac file>] +[B<-ss_cert file>] +[B<-preserveDN>] +[B<-noemailDN>] +[B<-batch>] +[B<-msie_hack>] +[B<-extensions section>] +[B<-extfile section>] +[B<-engine id>] +[B<-subj arg>] +[B<-utf8>] +[B<-create_serial>] +[B<-multivalue-rdn>] + +=head1 DESCRIPTION + +The B command is a minimal CA application. It can be used +to sign certificate requests in a variety of forms and generate +CRLs it also maintains a text database of issued certificates +and their status. + +The options descriptions will be divided into each purpose. + +ca指令是CA中很å°çš„应用。它å¯ä»¥ç”¨æ¥ç­¾å‘å„ç§å½¢å¼çš„用户è¯ä¹¦å¹¶äº§ç”ŸCRL。它还å¯ä»¥ç”¨æ¥æ›´æ–°è¯ä¹¦åº“。 +在这些指令的介ç»ä¸­ï¼Œå°†å°½å¯èƒ½åœ°å¯¹å®ƒä»¬è¿›è¡Œåˆ†ç±»ä»‹ç»ã€‚ + +=head1 OPTIONS + +=over 4 + +=item B<-help> + +Print out a usage message. + +显示用法信æ¯ã€‚ + +=item B<-verbose> + +this prints extra details about the operations being performed. + +输出更详细的一些æ“作过程信æ¯ã€‚ + +=item B<-config filename> + +specifies the configuration file to use. + +指定将è¦ä½¿ç”¨çš„é…置文件。 + +=item B<-name section> + +specifies the configuration file section to use (overrides +B in the B section). + +指定将è¦ä½¿ç”¨çš„é…置文件部分(覆盖ca部分中的default_ca部分) + +=item B<-in filename> + +an input filename containing a single certificate request to be +signed by the CA. + +一个输入文件å包å«äº†ä¸€ä¸ªè¦ç”±CAç­¾åçš„å•独è¯ä¹¦è¯·æ±‚。 + +=item B<-ss_cert filename> + +a single self-signed certificate to be signed by the CA. + +一个è¦ç”±CAç­¾å的自签åè¯ä¹¦ã€‚ + +=item B<-spkac filename> + +a file containing a single Netscape signed public key and challenge +and additional field values to be signed by the CA. See the B +section for information on the required input and output format. + +一个包å«äº†ä¸€ä¸ªå•独的Netscapeç­¾å的公钥和其他附加用户信æ¯ã€‚ +关于输入输出格å¼çš„ä¿¡æ¯å…·ä½“å¯ä»¥å‚考SPKAC部分。 + +=item B<-infiles> + +if present this should be the last option, all subsequent arguments +are taken as the names of files containing certificate requests. + +该选项总是作为指令的最åŽä¸€ä¸ªé€‰é¡¹ï¼Œå…¶åŽé¢æ‰€æœ‰çš„傿•°éƒ½è¢«è®¤ä¸ºæ˜¯è¯ä¹¦è¯·æ±‚文件。 + +=item B<-out filename> + +the output file to output certificates to. The default is standard +output. The certificate details will also be printed out to this +file in PEM format (except that B<-spkac> outputs DER format). + +输出文件输出签å‘好的è¯ä¹¦ã€‚默认值为标准输出。输出的è¯ä¹¦éƒ½æ˜¯PEMç¼–ç çš„(除了spkac输出DERç¼–ç ï¼‰ + +=item B<-outdir directory> + +the directory to output certificates to. The certificate will be +written to a filename consisting of the serial number in hex with +".pem" appended. + +将新生æˆçš„è¯ä¹¦è¾“出到目录。新生æˆè¯ä¹¦å°†ä¼šåºåˆ—å·åŠ â€œpemâ€åŽç¼€æˆä¸ºä¸€ä¸ªå®Œæ•´çš„è¯ä¹¦æ–‡ä»¶å。 + +=item B<-cert> + +the CA certificate file. + +CAè¯ä¹¦æ–‡ä»¶ã€‚ + +=item B<-keyfile filename> + +the private key to sign requests with. + +用于签署请求的ç§é’¥ã€‚ + +=item B<-keyform PEM|DER> + +the format of the data in the private key file. +The default is PEM. + +ç§é’¥æ–‡ä»¶ä¸­æ•°æ®çš„æ ¼å¼ã€‚默认为PEM。 + +=item B<-key password> + +the password used to encrypt the private key. Since on some +systems the command line arguments are visible (e.g. Unix with +the 'ps' utility) this option should be used with caution. + +用于加密ç§é’¥çš„密ç ã€‚因为在æŸäº›ç³»ç»Ÿä¸Šå‘½ä»¤è¡Œå‚数是å¯è§çš„(例如使用“psâ€å®žç”¨ç¨‹åºçš„Unix),应谨慎使用此指令。 + +=item B<-selfsign> + +indicates the issued certificates are to be signed with the key +the certificate requests were signed with (given with B<-keyfile>). +Certificate requests signed with a different key are ignored. If +B<-spkac>, B<-ss_cert> or B<-gencrl> are given, B<-selfsign> is +ignored. + +A consequence of using B<-selfsign> is that the self-signed +certificate appears among the entries in the certificate database +(see the configuration option B), and uses the same +serial number counter as all other certificates sign with the +self-signed certificate. + +表示å‘出的è¯ä¹¦å°†ä½¿ç”¨è¯ä¹¦è¯·æ±‚ç­¾å的密钥(以-keyfile命å)进行签å。 +使用ä¸åŒå¯†é’¥ç­¾åçš„è¯ä¹¦è¯·æ±‚将被忽略。如果给出-spkac,-ss_cert或-gencrl,则忽略-selfsign + +使用-selfsign的结果是自签åè¯ä¹¦å‡ºçŽ°åœ¨è¯ä¹¦æ•°æ®åº“çš„æ¡ç›®ä¸­ï¼Œå¹¶ä½¿ç”¨ä¸Žå…¶ä»–è¯ä¹¦ç›¸åŒçš„åºåˆ—å·è®¡æ•°å™¨ã€‚ + +=item B<-passin arg> + +the key password source. For more information about the format of B +see the B section in L. + +给定了读å–ç§é’¥æ–‡ä»¶çš„æ—¶å€™éœ€è¦æä¾›çš„å£ä»¤ã€‚ + +=item B<-notext> + +don't output the text form of a certificate to the output file. + +ä¸æŠŠè¯ä¹¦çš„æ–‡æœ¬å½¢å¼è¾“出到输出文件。 + +=item B<-startdate date> + +this allows the start date to be explicitly set. The format of the +date is YYMMDDHHMMSSZ (the same as an ASN1 UTCTime structure). + +设置è¯ä¹¦çš„ç”Ÿæ•ˆæ—¶é—´ï¼Œå…¶å‚æ•°æ ¼å¼æ˜¯â€œYYMMDDHHMMSSZâ€ã€‚ + +=item B<-enddate date> + +this allows the expiry date to be explicitly set. The format of the +date is YYMMDDHHMMSSZ (the same as an ASN1 UTCTime structure). + +设置è¯ä¹¦çš„åˆ°æœŸæ—¶é—´ï¼Œå…¶å‚æ•°æ ¼å¼æ˜¯â€œYYMMDDHHMMSSZâ€ã€‚ + +=item B<-days arg> + +the number of days to certify the certificate for. + +设置è¯ä¹¦çš„æœ‰æ•ˆå¤©æ•°ã€‚ + +=item B<-md alg> + +the message digest to use. +Any digest supported by the GmSSL B command can be used. +This option also applies to CRLs. + +æ¶ˆæ¯æ‘˜è¦ä½¿ç”¨ã€‚ +å¯ä»¥ä½¿ç”¨GmSSL dgst命令支æŒçš„任何摘è¦ã€‚ +该选项也适用于CRLs。 + +=item B<-policy arg> + +this option defines the CA "policy" to use. This is a section in +the configuration file which decides which fields should be mandatory +or match the CA certificate. Check out the B section +for more information. + +该选项定义了CA的匹é…策略。这是é…置文件中的一部分,它决定了哪些字段应该是必须的 +或与CAè¯ä¹¦åŒ¹é…。查看policy format部分æ¥äº†è§£æ›´å¤šä¿¡æ¯ã€‚ + +=item B<-msie_hack> + +this is a legacy option to make B work with very old versions of +the IE certificate enrollment control "certenr3". It used UniversalStrings +for almost everything. Since the old control has various security bugs +its use is strongly discouraged. The newer control "Xenroll" does not +need this option. + +这是一个é—留的项目,它å¯ä»¥ä½¿caå¯ä»¥ä½¿ç”¨éžå¸¸è€çš„ieè¯ä¹¦æ³¨å†ŒæŽ§ä»¶centenr3.它几乎所有东西 +都使用了UniversalStrings。我们éžå¸¸ä¸æŽ¨èä½¿ç”¨æ—§ç‰ˆæŽ§ä»¶åº”ä¸ºå®ƒæœ‰å¾ˆå¤šçš„å®‰å…¨æ¼æ´žã€‚新的控件 +Xenrollä¸éœ€è¦è¿™ä¸€é¡¹ã€‚ + +=item B<-preserveDN> + +Normally the DN order of a certificate is the same as the order of the +fields in the relevant policy section. When this option is set the order +is the same as the request. This is largely for compatibility with the +older IE enrollment control which would only accept certificates if their +DNs match the order of the request. This is not needed for Xenroll. + +使指令在签å‘è¯ä¹¦çš„æ—¶å€™è®©è¯ä¹¦ä¸»ä½“å称内的å„项内容顺åºè·Ÿè¯ä¹¦è¯·æ±‚中的顺åºä¿æŒä¸€è‡´ã€‚ +而在默认情况下,è¯ä¹¦ä¸»é¢˜å称内的å„ä¸ªé€‰é¡¹é¡ºåºæ˜¯æŒ‰ç…§é…置文件中的è¯ä¹¦åŒ¹é…ç­–ç•¥å­æ®µçš„ +选项顺åºè¿›è¡ŒæŽ’列的。 + +=item B<-noemailDN> + +The DN of a certificate can contain the EMAIL field if present in the +request DN, however it is good policy just having the e-mail set into +the altName extension of the certificate. When this option is set the +EMAIL field is removed from the certificate' subject and set only in +the, eventually present, extensions. The B keyword can be +used in the configuration file to enable this behaviour. + +一个è¯ä¹¦çš„主体åç§°å¯ä»¥åŒ…å«E-mail项目,但是将电å­é‚®ä»¶æ”¾åœ¨ä¸»ä½“别å中会更好一点。 +当你å¯ç”¨è¿™ä¸ªé€‰é¡¹åŽe-mail会从è¯ä¹¦ä¸»ä½“å称移除并最终设在主体别å中。 +å¯ä»¥åœ¨é…置文件中使用email_in_dnæ¥å¯ç”¨è¿™ä¸ªè¡Œä¸ºã€‚ + +=item B<-batch> + +this sets the batch mode. In this mode no questions will be asked +and all certificates will be certified automatically. + +该选项设定batch模å¼ã€‚在这一模å¼ä¸­caæŒ‡ä»¤ä¸æç¤ºç”¨æˆ·è¾“å…¥ä»»ä½•ä¿¡æ¯è€Œç›´æŽ¥ç­¾å‘所有输入的è¯ä¹¦è¯·æ±‚。 + +=item B<-extensions section> + +the section of the configuration file containing certificate extensions +to be added when a certificate is issued (defaults to B +unless the B<-extfile> option is used). If no extension section is +present then, a V1 certificate is created. If the extension section +is present (even if it is empty), then a V3 certificate is created. See the:w +L manual page for details of the +extension section format. + +é…置文件部分包å«äº†é¢å‘è¯ä¹¦æ—¶è¦æ·»åŠ çš„è¯ä¹¦æ‰©å±•å(默认为x509_extensions,除éžä½¿ç”¨-extfile选项)。 +如果没有扩展部分,则创建V1è¯ä¹¦ã€‚如果存在扩展部分(å³ä½¿è¯¥éƒ¨åˆ†ä¸ºç©ºï¼‰ï¼Œåˆ™åˆ›å»ºV3è¯ä¹¦ã€‚ +有关扩展部分格å¼çš„详细信æ¯ï¼Œè¯·å‚阅x509v3_config(5)手册页。 + +=item B<-extfile file> + +an additional configuration file to read certificate extensions from +(using the default section unless the B<-extensions> option is also +used). + +一个专门用æ¥ä¿å­˜X.509 v3扩展项信æ¯çš„æ–‡ä»¶ã€‚ + +=item B<-engine id> + +specifying an engine (by its unique B string) will cause B +to attempt to obtain a functional reference to the specified engine, +thus initialising it if needed. The engine will then be set as the default +for all available algorithms. + +指定一个引擎(通过其唯一的id字符串)会导致caå°è¯•获å–对指定engine设备的功能引用 +并进行åˆå§‹åŒ–如果需è¦ã€‚这个engine将被设置为所有å¯ç”¨ç®—法的默认。 + +=item B<-subj arg> + +supersedes subject name given in the request. +The arg must be formatted as I, +characters may be escaped by \ (backslash), no spaces are skipped. + +釿–°å¡«å†™ç”¨æˆ·çš„è¯ä¹¦ä¸»ä½“å称。subjé€‰é¡¹çš„å‚æ•°æ ¼å¼ä¸º/type0=value0/type1=value1/type2...,字符å¯èƒ½ä¼šè¢«\转义,空格ä¸è¢«è·³è¿‡ã€‚ + +=item B<-utf8> + +this option causes field values to be interpreted as UTF8 strings, by +default they are interpreted as ASCII. This means that the field +values, whether prompted from a terminal or obtained from a +configuration file, must be valid UTF8 strings. + +这一选项字段值转为UTF8字符串,默认情况下为ASCII。这æ„味ç€å­—段值(无论从终端æç¤ºè¿˜æ˜¯ä»Žé…置文件获å–)都必须是有效的UTF8字符串。 + +=item B<-create_serial> + +if reading serial from the text file as specified in the configuration +fails, specifying this option creates a new random serial to be used as next +serial number. + +如果从é…置中指定的文本文件读å–åºåˆ—失败,该选项å¯ä»¥åˆ›é€ ä¸€ä¸ªæ–°çš„éšæœºåºåˆ—作为下一个åºåˆ—å·ã€‚ + +=item B<-multivalue-rdn> + +This option causes the -subj argument to be interpreted with full +support for multivalued RDNs. Example: + +该选项å¯ä»¥è§£é‡Š-subj傿•°ï¼Œå¹¶å®Œå…¨æ”¯æŒå¤šRND。 + +I + +If -multi-rdn is not used then the UID value is I<123456+CN=John Doe>. + +=back + +=head1 CRL OPTIONS + +=over 4 + +=item B<-gencrl> + +this option generates a CRL based on information in the index file. + +该选项用于生æˆä¸€ä¸ªåŸºäºŽç´¢å¼•文件信æ¯çš„CFL, + +=item B<-crldays num> + +the number of days before the next CRL is due. That is the days from +now to place in the CRL nextUpdate field. + +以“天â€ä¸ºå•ä½è®¾ç½®CRL的有效期。 + +=item B<-crlhours num> + +the number of hours before the next CRL is due. + +以尿—¶ä¸ºå•ä½è®¾ç½®CRL有效期。 + +=item B<-revoke filename> + +a filename containing a certificate to revoke. + +一个包å«è¦æ’¤é”€çš„è¯ä¹¦çš„æ–‡ä»¶å + +=item B<-valid filename> + +a filename containing a certificate to add a Valid certificate entry. + +ä¸€ä¸ªåŒ…å«æ·»åŠ æœ‰æ•ˆè¯ä¹¦æ¡ç›®çš„è¯ä¹¦çš„æ–‡ä»¶å。 + +=item B<-status serial> + +displays the revocation status of the certificate with the specified +serial number and exits. + +显示具有指定åºåˆ—å·çš„è¯ä¹¦çš„æ’¤é”€çжæ€å¹¶é€€å‡ºã€‚ + +=item B<-updatedb> + +Updates the database index to purge expired certificates. + +æ›´æ–°æ•°æ®åº“索引清除以过期的è¯ä¹¦ã€‚ + +=item B<-crl_reason reason> + +revocation reason, where B is one of: B, B, +B, B, B, B, +B or B. The matching of B is case +insensitive. Setting any revocation reason will make the CRL v2. + +In practice B is not particularly useful because it is only used +in delta CRLs which are not currently implemented. + +撤销原因,其中的原因有:未指定,key妥å,CA妥å,è”系改å˜ï¼Œsuperseded,cessationofoperation,insensitive。设置任何撤销原因将使CRLå˜æˆv2. + +实际上,removeFromCRL䏿˜¯ç‰¹åˆ«æœ‰ç”¨å› ä¸ºï¼Œå› ä¸ºå®ƒä»…ç”¨äºŽå½“å‰æœªå®žçŽ°çš„deltaCRL。 + +=item B<-crl_hold instruction> + +This sets the CRL revocation reason code to B and the hold +instruction to B which must be an OID. Although any OID can be +used only B (the use of which is discouraged by RFC2459) +B or B will normally be used. + +这会将CRL撤销原因代ç è®¾ç½®ä¸ºcertificateholdï¼Œå¹¶å°†æŒ‡ä»¤çš„ä¿æŒæŒ‡ä»¤è®¾ç½®ä¸ºå¿…须是OID的指令。 +虽然任何OIDåªèƒ½ä½¿ç”¨holdInstructionNone*RFC2459ä¸é¼“励使用它),但通常会使用holdInstructionCallIssuer或holdInstruvtionReject。 + +=item B<-crl_compromise time> + +This sets the revocation reason to B and the compromise time to +B